muffin-6.4.1/0000775000175000017500000000000014723361714011742 5ustar fabiofabiomuffin-6.4.1/debian/0000775000175000017500000000000014723361714013164 5ustar fabiofabiomuffin-6.4.1/meson.build0000664000175000017500000003516014723361714014111 0ustar fabiofabioproject('muffin', 'c', version : '6.4.1', meson_version : '>=0.56.0', license : 'GPLv2+') mutter_plugin_api_version = '3' split_version = meson.project_version().split('.') # Automatically increase API version each development cycle, # starting with 0 in 3.23.x api_version = 0 libmutter_api_version = '@0@'.format(api_version) # generic version requirements fribidi_req = '>= 1.0.0' glib_req = '>= 2.67.3' gi_req = '>= 0.9.5' graphene_req = '>= 1.9.3' gtk3_req = '>= 3.19.8' gdk_pixbuf_req = '>= 2.0' uprof_req = '>= 0.3' pango_req = '>= 1.2.0' cairo_req = '>= 1.10.0' pangocairo_req = '>= 1.20' json_glib_req = '>= 0.12.0' upower_glib_req = '>= 0.99.0' xcomposite_req = '>= 0.4' xkbcommon_req = '>= 0.4.3' xfixes_req = '>= 3' xi_req = '>= 1.7.4' xrandr_req = '>= 1.5.0' libstartup_notification_req = '>= 0.7' libcanberra_req = '>= 0.26' libwacom_req = '>= 0.13' atk_req = '>= 2.5.3' # optional version requirements udev_req = '>= 228' gudev_req = '>= 232' # wayland version requirements wayland_server_req = '>= 1.13.0' wayland_protocols_req = '>= 1.19' # native backend version requirements libinput_req = '>= 1.7' gbm_req = '>= 10.3' # screen cast version requirements libpipewire_req = '>= 0.3.0' # profiler requirements sysprof_req = '>= 3.35.2' gnome = import('gnome') pkg = import('pkgconfig') i18n = import('i18n') cc = meson.get_compiler('c') prefix = get_option('prefix') bindir = join_paths(prefix, get_option('bindir')) datadir = join_paths(prefix, get_option('datadir')) libdir = join_paths(prefix, get_option('libdir')) libexecdir = join_paths(prefix, get_option('libexecdir')) includedir = join_paths(prefix, get_option('includedir')) sysconfdir = get_option('sysconfdir') pkgname = '@0@'.format(meson.project_name()) pkgdatadir = join_paths(datadir, pkgname) pkglibdir = join_paths(libdir, pkgname) pkgincludedir = join_paths(includedir, pkgname) pcdir = join_paths(libdir, 'pkgconfig') gettext_package = meson.project_name() localedir = join_paths(datadir, 'locale') libmutter_name = 'muffin' mutter_installed_tests_datadir = join_paths( datadir, 'installed-tests', libmutter_name) mutter_installed_tests_libexecdir = join_paths( libexecdir, 'installed-tests', libmutter_name) m_dep = cc.find_library('m', required: true) x11_dep = dependency('x11') graphene_dep = dependency('graphene-gobject-1.0', version: graphene_req) gtk3_dep = dependency('gtk+-3.0', version: gtk3_req) gdk_pixbuf_dep = dependency('gdk-pixbuf-2.0') pango_dep = dependency('pango', version: pango_req) cairo_dep = dependency('cairo', version: cairo_req) cairo_gobject_dep = dependency('cairo-gobject', version: cairo_req) pangocairo_dep = dependency('pangocairo', version: pangocairo_req) fribidi_dep = dependency('fribidi', version: fribidi_req) glib_dep = dependency('glib-2.0', version: glib_req) gio_dep = dependency('gio-unix-2.0', version: glib_req) gio_unix_dep = dependency('gio-unix-2.0', version: glib_req) gobject_dep = dependency('gobject-2.0', version: glib_req) gthread_dep = dependency('gobject-2.0', version: glib_req) gmodule_no_export_dep = dependency('gmodule-no-export-2.0', version: glib_req) json_glib_dep = dependency('json-glib-1.0', version: json_glib_req) cinnamon_desktop_dep = dependency('cinnamon-desktop', version: '>= 5.3') xcomposite_dep = dependency('xcomposite', version: xcomposite_req) xcursor_dep = dependency('xcursor') xdamage_dep = dependency('xdamage') xext_dep = dependency('xext') xfixes_dep = dependency('xfixes', version: xfixes_req) xi_dep = dependency('xi', version: xi_req) xtst_dep = dependency('xtst') xkbfile_dep = dependency('xkbfile') xkeyboard_config_dep = dependency('xkeyboard-config') xkbcommon_dep = dependency('xkbcommon', version: xkbcommon_req) xkbcommon_x11_dep = dependency('xkbcommon-x11') xrender_dep = dependency('xrender') x11_xcb_dep = dependency('x11-xcb') xrandr_dep = dependency('xrandr', version: xrandr_req) xcb_randr_dep = dependency('xcb-randr') xcb_res_dep = dependency('xcb-res') xinerama_dep = dependency('xinerama') xau_dep = dependency('xau') ice_dep = dependency('ice') atk_dep = dependency('atk', version: atk_req) libcanberra_dep = dependency('libcanberra', version: libcanberra_req) dbus_dep = dependency('dbus-1') # For now always require X11 support have_x11 = true have_gl = get_option('opengl') if have_gl gl_dep = dependency('gl') gl_libname = get_option('opengl_libname') endif have_egl = get_option('egl') if have_egl egl_dep = dependency('egl') endif have_glx = get_option('glx') if have_glx if not have_gl error('GLX support requires OpenGL to be enabled') endif endif have_egl_xlib = have_egl and have_x11 have_gles2 = get_option('gles2') if have_gles2 gles2_dep = dependency('glesv2') gles2_libname = get_option('gles2_libname') if not have_egl error('GLESv2 support requires EGL to be enabled') endif endif have_wayland = get_option('wayland') if have_wayland wayland_server_dep = dependency('wayland-server', version: wayland_server_req) wayland_client_dep = dependency('wayland-client', version: wayland_server_req) wayland_protocols_dep = dependency('wayland-protocols', version: wayland_protocols_req) wayland_egl_dep = dependency('wayland-egl') if not have_egl error('Wayland support requires EGL to be enabled') endif endif have_libgudev = get_option('udev') if have_libgudev libudev_dep = dependency('libudev', version: udev_req) gudev_dep = dependency('gudev-1.0', version: gudev_req) endif have_native_backend = get_option('native_backend') if have_native_backend libdrm_dep = dependency('libdrm') libgbm_dep = dependency('gbm', version: gbm_req) libinput_dep = dependency('libinput', version: libinput_req) libsystemd_dep = dependency('libsystemd', required: false) if libsystemd_dep.found() logind_provider_dep = libsystemd_dep else logind_provider_dep = dependency('libelogind') endif if not have_egl error('The native backend requires EGL to be enabled') endif if not have_gles2 error('The native backend requires GLESv2 to be enabled') endif if not have_libgudev error('The native backend requires udev to be enabled') endif endif have_egl_device = get_option('egl_device') have_wayland_eglstream = get_option('wayland_eglstream') if have_wayland_eglstream wayland_eglstream_protocols_dep = dependency('wayland-eglstream-protocols') dl_dep = cc.find_library('dl', required: true) if not have_wayland error('Wayland EGLStream support requires Wayland to be enabled') endif endif default_driver = get_option('default_driver') have_sm = get_option('sm') if have_sm sm_dep = dependency('sm') endif have_libwacom = get_option('libwacom') if have_libwacom libwacom_dep = dependency('libwacom', version: libwacom_req) endif have_pango_ft2 = get_option('pango_ft2') if have_pango_ft2 pangoft2_dep = dependency('pangoft2') endif have_startup_notification = get_option('startup_notification') if have_startup_notification libstartup_notification_dep = dependency('libstartup-notification-1.0', version: libstartup_notification_req) endif have_remote_desktop = get_option('remote_desktop') if have_remote_desktop libpipewire_dep = dependency('libpipewire-0.3', version: libpipewire_req) endif have_introspection = get_option('introspection') if have_introspection gobject_introspection_dep = dependency('gobject-introspection-1.0') introspection_args = [ '--quiet', '-U_GNU_SOURCE', ] endif have_tests = get_option('tests') have_core_tests = false have_cogl_tests = false have_clutter_tests = false have_installed_tests = false if have_tests have_core_tests = get_option('core_tests') if have_core_tests if not have_wayland error('Tests require Wayland to be enabled') endif endif have_cogl_tests = get_option('cogl_tests') have_clutter_tests = get_option('clutter_tests') have_installed_tests = get_option('installed_tests') endif have_profiler = get_option('profiler') if have_profiler sysprof_dep = dependency('sysprof-capture-3', version: sysprof_req) endif required_functions = [ 'ffs', 'clz', 'memmem', ] foreach function : required_functions if not cc.has_function(function) error('Required function ' + function + ' missing') endif endforeach if host_machine.cpu_family() == 'x86' add_project_arguments('-ffloat-store', language: 'c') endif add_project_arguments('-D_GNU_SOURCE', language: 'c') buildtype = get_option('buildtype') if buildtype != 'plain' all_warnings = [ '-fno-strict-aliasing', '-Wpointer-arith', '-Wmissing-declarations', '-Wimplicit-function-declaration', '-Wformat=2', '-Wformat-nonliteral', '-Wformat-security', '-Wstrict-prototypes', '-Wmissing-prototypes', '-Wnested-externs', '-Wold-style-definition', '-Wundef', '-Wunused', '-Wcast-align', '-Wmissing-noreturn', '-Wmissing-format-attribute', '-Wmissing-include-dirs', '-Wlogical-op', '-Wignored-qualifiers', '-Werror=redundant-decls', '-Werror=implicit', '-Werror=nonnull', '-Werror=init-self', '-Werror=main', '-Werror=missing-braces', '-Werror=sequence-point', '-Werror=return-type', '-Werror=trigraphs', '-Werror=array-bounds', '-Werror=write-strings', '-Werror=address', '-Werror=int-to-pointer-cast', '-Werror=pointer-to-int-cast', '-Werror=empty-body', '-Werror=write-strings', ] supported_warnings = cc.get_supported_arguments(all_warnings) add_project_arguments(supported_warnings, language: 'c') endif if get_option('debug') debug_c_args = [ '-DG_ENABLE_DEBUG', '-fno-omit-frame-pointer' ] supported_debug_c_args = cc.get_supported_arguments(debug_c_args) add_project_arguments(supported_debug_c_args, language: 'c') endif cc.compiles('void main (void) { __builtin_ffsl (0); __builtin_popcountl (0); }') cdata = configuration_data() cdata.set_quoted('GETTEXT_PACKAGE', gettext_package) cdata.set_quoted('VERSION', meson.project_version()) cdata.set_quoted('PACKAGE_VERSION', meson.project_version()) cdata.set('HAVE_EGL', have_egl) cdata.set('HAVE_WAYLAND', have_wayland) cdata.set('HAVE_NATIVE_BACKEND', have_native_backend) cdata.set('HAVE_REMOTE_DESKTOP', have_remote_desktop) cdata.set('HAVE_EGL_DEVICE', have_egl_device) cdata.set('HAVE_WAYLAND_EGLSTREAM', have_wayland_eglstream) cdata.set('HAVE_LIBGUDEV', have_libgudev) cdata.set('HAVE_LIBWACOM', have_libwacom) cdata.set('HAVE_SM', have_sm) cdata.set('HAVE_STARTUP_NOTIFICATION', have_startup_notification) cdata.set('HAVE_INTROSPECTION', have_introspection) cdata.set('HAVE_PROFILER', have_profiler) xkb_base = xkeyboard_config_dep.get_variable(pkgconfig: 'xkb_base') cdata.set_quoted('XKB_BASE', xkb_base) if cc.has_header_symbol('sys/prctl.h', 'prctl') cdata.set('HAVE_SYS_PRCTL', 1) endif have_xwayland_initfd = false if have_wayland xwayland_dep = dependency('xwayland', required: false) xwayland_path = get_option('xwayland_path') if xwayland_path == '' if xwayland_dep.found() xwayland_path = xwayland_dep.get_pkgconfig_variable('xwayland') else xwayland_path = find_program('Xwayland').full_path() endif endif cdata.set_quoted('XWAYLAND_PATH', xwayland_path) # For Xwayland authority file generation. if cc.has_header_symbol('sys/random.h', 'getrandom') cdata.set('HAVE_SYS_RANDOM', 1) elif cc.has_header_symbol('linux/random.h', 'getrandom') cdata.set('HAVE_LINUX_RANDOM', 1) else error('Required function getrandom not found') endif # For Xwayland -initfd usage use_initfd = get_option('xwayland_initfd') if xwayland_dep.found() xwayland_supports_initfd = xwayland_dep.get_pkgconfig_variable('have_initfd') == 'true' else xwayland_options = run_command(xwayland_path, '-help') xwayland_supports_initfd = xwayland_options.stderr().contains('-initfd') endif if use_initfd.auto() have_xwayland_initfd = xwayland_supports_initfd else have_xwayland_initfd = use_initfd.enabled() endif if (have_xwayland_initfd) cdata.set('HAVE_XWAYLAND_INITFD', 1) endif endif #xwayland_grab_default_access_rules = get_option('xwayland_grab_default_access_rules') xwayland_grab_default_access_rules = '' cdata.set_quoted('XWAYLAND_GRAB_DEFAULT_ACCESS_RULES', xwayland_grab_default_access_rules) cdata.set_quoted('MUTTER_PLUGIN_DIR', join_paths(pkglibdir, 'plugins')) cdata.set_quoted('MUTTER_LOCALEDIR', localedir) cdata.set_quoted('MUTTER_LIBEXECDIR', libexecdir) cdata.set_quoted('MUTTER_PKGDATADIR', pkgdatadir) config_h = configure_file( input: 'config.h.meson', output: 'config.h', configuration: cdata ) top_includepath = include_directories('.') subdir('cogl') subdir('clutter') subdir('data') subdir('src') subdir('po') subdir('doc/man') meson.add_install_script('meson/meson-postinstall.sh') output = [ '', '', ' Mutter ' + meson.project_version(), ' ===============', '', ' Prefix....................... ' + prefix, ' libexecdir................... ' + libexecdir, ' pkgdatadir................... ' + pkgdatadir, '', ' Rendering APIs:', '', ' OpenGL................... ' + have_gl.to_string(), ' GLES2.................... ' + have_gles2.to_string(), ' EGL...................... ' + have_egl.to_string(), ' GLX...................... ' + have_glx.to_string(), '', ' Options:', '', ' Wayland.................. ' + have_wayland.to_string(), ' Wayland EGLStream........ ' + have_wayland_eglstream.to_string(), ' Native Backend........... ' + have_native_backend.to_string(), ' EGL Device............... ' + have_egl_device.to_string(), ' Default driver........... ' + default_driver, ' Remote desktop........... ' + have_remote_desktop.to_string(), ' gudev.................... ' + have_libgudev.to_string(), ' Wacom.................... ' + have_libwacom.to_string(), ' SM....................... ' + have_sm.to_string(), ' Startup notification..... ' + have_startup_notification.to_string(), ' Introspection............ ' + have_introspection.to_string(), ' Profiler................. ' + have_profiler.to_string(), ' Xwayland initfd.......... ' + have_xwayland_initfd.to_string(), '', ' Tests:', '', ' Enabled.................. ' + have_tests.to_string(), ' Core tests............... ' + have_core_tests.to_string(), ' Cogl tests............... ' + have_cogl_tests.to_string(), ' Clutter tests............ ' + have_clutter_tests.to_string(), ' Installed tests.......... ' + have_installed_tests.to_string(), '', ' Now type \'ninja -C ' + meson.project_build_root() + '\' to build ' + meson.project_name(), '', '', ] message('\n'.join(output)) muffin-6.4.1/.gitignore0000664000175000017500000000036314723361714013734 0ustar fabiofabiodebian/*.debhelper.log debian/*.substvars debian/.debhelper/ debian/debhelper-build-stamp debian/files debian/gir1.2-meta-muffin-0.0/ debian/libmuffin-dev/ debian/libmuffin0/ debian/muffin-common/ debian/muffin-dbg/ debian/muffin/ debian/tmp/ muffin-6.4.1/mutter.doap0000664000175000017500000000526514723361714014137 0ustar fabiofabio mutter Window and compositing manager based on Clutter Mutter is a window and compositing manager that displays and manages your desktop via OpenGL. Mutter combines a sophisticated display engine using the Clutter toolkit with solid window-management logic inherited from the Metacity window manager. While Mutter can be used stand-alone, it is primarily intended to be used as the display core of a larger system such as GNOME Shell. For this reason, Mutter is very extensible via plugins, which are used both to add fancy visual effects and to rework the window management behaviors to meet the needs of the environment. C Tomas Frydrych tomasf Owen Taylor otaylor Jonas Ådahl jadahl Carlos Garnacho carlosg Georges Basile Stavracas Neto gbsneto Florian Müllner fmuellner muffin-6.4.1/data/0000775000175000017500000000000014723361714012653 5ustar fabiofabiomuffin-6.4.1/data/meson.build0000664000175000017500000000237114723361714015020 0ustar fabiofabiomsgfmt = find_program('msgfmt') custom_target('muffin.desktop', input: 'muffin.desktop.in', output: 'muffin.desktop', command: [ msgfmt, '--desktop', '--template', '@INPUT@', '-d', join_paths(meson.project_source_root(), 'po'), '-o', '@OUTPUT@' ], install: true, install_dir: join_paths(datadir, 'applications'), ) #xwayland_grab_default_access_rules = get_option('xwayland_grab_default_access_rules') xwayland_grab_default_access_rules = '' gschema_config = configuration_data() gschema_config.set('GETTEXT_DOMAIN', meson.project_name()) gschema_config.set('XWAYLAND_GRAB_DEFAULT_ACCESS_RULES', xwayland_grab_default_access_rules) schemadir = join_paths(datadir, 'glib-2.0', 'schemas') configure_file( input: 'org.cinnamon.muffin.gschema.xml.in', output: 'org.cinnamon.muffin.gschema.xml', configuration: gschema_config, install_dir: schemadir ) configure_file( input: 'org.cinnamon.muffin.wayland.gschema.xml.in', output: 'org.cinnamon.muffin.wayland.gschema.xml', configuration: gschema_config, install_dir: schemadir ) configure_file( input: 'org.cinnamon.muffin.x11.gschema.xml.in', output: 'org.cinnamon.muffin.x11.gschema.xml', configuration: gschema_config, install_dir: schemadir ) muffin-6.4.1/data/org.cinnamon.muffin.wayland.gschema.xml.in0000664000175000017500000001031114723361714022715 0ustar fabiofabio F1']]]> Switch to VT 1 F2']]]> Switch to VT 2 F3']]]> Switch to VT 3 F4']]]> Switch to VT 4 F5']]]> Switch to VT 5 F6']]]> Switch to VT 6 F7']]]> Switch to VT 7 F8']]]> Switch to VT 8 F9']]]> Switch to VT 9 F10']]]> Switch to VT 10 F11']]]> Switch to VT 11 F12']]]> Switch to VT 12 Escape']]]> Re-enable shortcuts false Allow X11 grabs to lock keyboard focus with Xwayland Allow all keyboard events to be routed to X11 “override redirect” windows with a grab when running in Xwayland. This option is to support X11 clients which map an “override redirect” window (which do not receive keyboard focus) and issue a keyboard grab to force all keyboard events to that window. This option is seldom used and has no effect on regular X11 windows which can receive keyboard focus under normal circumstances. For a X11 grab to be taken into account under Wayland, the client must also either send a specific X11 ClientMessage to the root window or be among the applications white-listed in key “xwayland-grab-access-rules”. [] Xwayland applications allowed to issue keyboard grabs List the resource names or resource class of X11 windows either allowed or not allowed to issue X11 keyboard grabs under Xwayland. The resource name or resource class of a given X11 window can be obtained using the command “xprop WM_CLASS”. Wildcards “*” and jokers “?” in the values are supported. Values starting with “!” are blacklisted, which has precedence over the whitelist, to revoke applications from the default system list. The default system list includes the following applications: “@XWAYLAND_GRAB_DEFAULT_ACCESS_RULES@” Users can break an existing grab by using the specific keyboard shortcut defined by the keybinding key “restore-shortcuts”. muffin-6.4.1/data/default_icon.png0000664000175000017500000000224014723361714016013 0ustar fabiofabioPNG  IHDR00WbKGD pHYs  tIME -IDATxYKnA }Ĭ؀}`p'! $!!H1*lw0 R[QS>feYnR8r]6ߴH;k&8;<}|紾8ǓǏZ^oӗ2ꛞ^/]S~ "p[3ڄBrvv>H HԿvlc:Nר(uAUx|xDP)fT uq3͠4 1I"p{]66 <"Ƃ2jH/ Vi5=0&i*'IJ 0JVD<HCBLe'nw`95NOV=2v;{'H'bQ{Z} 8,\#VR0{"!w4Dbx-#;Q9^9JTF&>vt䏛w6Bؗ4:-_ lQqA38js=S:merbpQ+$QgawJ {l: >|5&p/!OX^qeYf7n>Ec6IENDB`muffin-6.4.1/data/muffin.desktop.in0000664000175000017500000000071714723361714016144 0ustar fabiofabio[Desktop Entry] Type=Application Name=Muffin Exec=muffin NoDisplay=true # name of loadable control center module X-GNOME-WMSettingsModule=metacity # name we put on the WM spec check window X-GNOME-WMName=Muffin # back compat only X-GnomeWMSettingsLibrary=metacity X-GNOME-Bugzilla-Bugzilla=GNOME X-GNOME-Bugzilla-Product=muffin X-GNOME-Bugzilla-Component=general X-GNOME-Autostart-Phase=WindowManager X-GNOME-Provides=windowmanager X-GNOME-Autostart-Notify=true muffin-6.4.1/data/org.cinnamon.muffin.x11.gschema.xml.in0000664000175000017500000000216314723361714021675 0ustar fabiofabio "scale-ui-down" Choose the scaling mode to be used under X11 via Randr extension. Supported methods are: • “scale-up” — Scale everything up to the requested scale, shrinking the UI. The applications will look blurry when scaling at higher values and the resolution will be lowered. • “scale-ui-down — Scale up the UI toolkits to the closest integer scaling value upwards, while scale down the display to match the requested scaling level. It increases the resolution of the logical display. muffin-6.4.1/data/org.cinnamon.muffin.gschema.xml.in0000664000175000017500000002034514723361714021267 0ustar fabiofabio '' DO NOT USE Use keybinding mechanisms in cinnamon instead. false Attach modal dialogs When true, instead of having independent titlebars, modal dialogs appear attached to the titlebar of the parent window and are moved together with the parent window. true Enable edge tiling when dropping windows on screen edges If enabled, dropping windows on vertical screen edges maximizes them vertically and resizes them horizontally to cover half of the available area. Dropping windows on the top screen edge maximizes them completely. false Sets maximize as the tile action for the top edge of the screen Makes tiling to the top maximize the window false Inverts the direction the left and right arrows take you when you switch workspaces during a window drag Changes left-right arrow keys to window-centric directions rather than workspace-centric false Workspaces are managed dynamically Determines whether workspaces are managed dynamically or whether there’s a static number of workspaces (determined by the num-workspaces key in org.cinnamon.desktop.wm.preferences). false Allow cycling through workspaces Allows cycling through workspaces, going to the workspace at the other end if you are at the left-most or right-most one. false Fullscreen windows are unredirected (i.e. they bypass compositing) Determines whether fullscreen windows bypass compositing. False is better for vsync/screen-tearing, True gives games and apps i false Workspaces only on primary Determines whether workspace switching should happen for windows on all monitors or only for windows on the primary monitor. false No tab popup Determines whether the use of popup and highlight frame should be disabled for window cycling. false Delay focus changes until the pointer stops moving If set to true, and the focus mode is either “sloppy” or “mouse” then the focus will not be changed immediately when entering a window, but only after the pointer stops moving. 10 Draggable border width The amount of total draggable borders. If the theme’s visible borders are not enough, invisible borders will be added to meet this value. false Auto maximize nearly monitor sized windows If enabled, new windows that are initially the size of the monitor automatically get maximized. false Obsolete — not used 'automatic' Window placement mode The window placement mode indicates how new windows are positioned. • “automatic” — the system chooses a location automatically based on the space available on the desktop, or by a simple cascade if there is no space • “pointer” — new windows are placed according to the mouse pointer position • “manual” — the user must manually place the new window with the mouse or keyboard. • “center” — new windows are always put in the center of the active screen of the monitor 'blend' Background transition The type of animation performed when changing background. "none" means no animation at all. "fade-in" means the old background switches to black and the new background appears with a fade-in effect. "blend" means the old background disappears as the new background appears with a fade-in effect. [] Enable experimental features Currently only 'x11-randr-fractional-scaling' is useful in Cinnamon • “x11-randr-fractional-scaling” — enable fractional scaling under X11 using xrandr scaling. It might reduce performances. Does not require a restart. ['Control_L'] Modifier to use to locate the pointer This key will initiate the “locate pointer” action. 5000 Timeout for check-alive ping Number of milliseconds a client has to respond to a ping request in order to not be detected as frozen. Using 0 will disable the alive check completely. false Brings windows requiring attention to the current workspace When enabled, if a window requires attention, it is brought to the current workspace. [] Select window from tab popup [] Cancel tab popup muffin-6.4.1/COPYING0000664000175000017500000004325414723361714013005 0ustar fabiofabio GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Preamble The licenses for most software are designed to take away your freedom to share and change it. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change free software--to make sure the software is free for all its users. This General Public License applies to most of the Free Software Foundation's software and to any other program whose authors commit to using it. (Some other Free Software Foundation software is covered by the GNU Lesser General Public License instead.) You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for this service if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs; and that you know you can do these things. To protect your rights, we need to make restrictions that forbid anyone to deny you these rights or to ask you to surrender the rights. These restrictions translate to certain responsibilities for you if you distribute copies of the software, or if you modify it. For example, if you distribute copies of such a program, whether gratis or for a fee, you must give the recipients all the rights that you have. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. We protect your rights with two steps: (1) copyright the software, and (2) offer you this license which gives you legal permission to copy, distribute and/or modify the software. Also, for each author's protection and ours, we want to make certain that everyone understands that there is no warranty for this free software. If the software is modified by someone else and passed on, we want its recipients to know that what they have is not the original, so that any problems introduced by others will not reflect on the original authors' reputations. Finally, any free program is threatened constantly by software patents. We wish to avoid the danger that redistributors of a free program will individually obtain patent licenses, in effect making the program proprietary. To prevent this, we have made it clear that any patent must be licensed for everyone's free use or not licensed at all. The precise terms and conditions for copying, distribution and modification follow. GNU GENERAL PUBLIC LICENSE TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION 0. This License applies to any program or other work which contains a notice placed by the copyright holder saying it may be distributed under the terms of this General Public License. The "Program", below, refers to any such program or work, and a "work based on the Program" means either the Program or any derivative work under copyright law: that is to say, a work containing the Program or a portion of it, either verbatim or with modifications and/or translated into another language. (Hereinafter, translation is included without limitation in the term "modification".) Each licensee is addressed as "you". Activities other than copying, distribution and modification are not covered by this License; they are outside its scope. The act of running the Program is not restricted, and the output from the Program is covered only if its contents constitute a work based on the Program (independent of having been made by running the Program). Whether that is true depends on what the Program does. 1. You may copy and distribute verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice and disclaimer of warranty; keep intact all the notices that refer to this License and to the absence of any warranty; and give any other recipients of the Program a copy of this License along with the Program. You may charge a fee for the physical act of transferring a copy, and you may at your option offer warranty protection in exchange for a fee. 2. You may modify your copy or copies of the Program or any portion of it, thus forming a work based on the Program, and copy and distribute such modifications or work under the terms of Section 1 above, provided that you also meet all of these conditions: a) You must cause the modified files to carry prominent notices stating that you changed the files and the date of any change. b) You must cause any work that you distribute or publish, that in whole or in part contains or is derived from the Program or any part thereof, to be licensed as a whole at no charge to all third parties under the terms of this License. c) If the modified program normally reads commands interactively when run, you must cause it, when started running for such interactive use in the most ordinary way, to print or display an announcement including an appropriate copyright notice and a notice that there is no warranty (or else, saying that you provide a warranty) and that users may redistribute the program under these conditions, and telling the user how to view a copy of this License. (Exception: if the Program itself is interactive but does not normally print such an announcement, your work based on the Program is not required to print an announcement.) These requirements apply to the modified work as a whole. If identifiable sections of that work are not derived from the Program, and can be reasonably considered independent and separate works in themselves, then this License, and its terms, do not apply to those sections when you distribute them as separate works. But when you distribute the same sections as part of a whole which is a work based on the Program, the distribution of the whole must be on the terms of this License, whose permissions for other licensees extend to the entire whole, and thus to each and every part regardless of who wrote it. Thus, it is not the intent of this section to claim rights or contest your rights to work written entirely by you; rather, the intent is to exercise the right to control the distribution of derivative or collective works based on the Program. In addition, mere aggregation of another work not based on the Program with the Program (or with a work based on the Program) on a volume of a storage or distribution medium does not bring the other work under the scope of this License. 3. You may copy and distribute the Program (or a work based on it, under Section 2) in object code or executable form under the terms of Sections 1 and 2 above provided that you also do one of the following: a) Accompany it with the complete corresponding machine-readable source code, which must be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, b) Accompany it with a written offer, valid for at least three years, to give any third party, for a charge no more than your cost of physically performing source distribution, a complete machine-readable copy of the corresponding source code, to be distributed under the terms of Sections 1 and 2 above on a medium customarily used for software interchange; or, c) Accompany it with the information you received as to the offer to distribute corresponding source code. (This alternative is allowed only for noncommercial distribution and only if you received the program in object code or executable form with such an offer, in accord with Subsection b above.) The source code for a work means the preferred form of the work for making modifications to it. For an executable work, complete source code means all the source code for all modules it contains, plus any associated interface definition files, plus the scripts used to control compilation and installation of the executable. However, as a special exception, the source code distributed need not include anything that is normally distributed (in either source or binary form) with the major components (compiler, kernel, and so on) of the operating system on which the executable runs, unless that component itself accompanies the executable. If distribution of executable or object code is made by offering access to copy from a designated place, then offering equivalent access to copy the source code from the same place counts as distribution of the source code, even though third parties are not compelled to copy the source along with the object code. 4. You may not copy, modify, sublicense, or distribute the Program except as expressly provided under this License. Any attempt otherwise to copy, modify, sublicense or distribute the Program is void, and will automatically terminate your rights under this License. However, parties who have received copies, or rights, from you under this License will not have their licenses terminated so long as such parties remain in full compliance. 5. You are not required to accept this License, since you have not signed it. However, nothing else grants you permission to modify or distribute the Program or its derivative works. These actions are prohibited by law if you do not accept this License. Therefore, by modifying or distributing the Program (or any work based on the Program), you indicate your acceptance of this License to do so, and all its terms and conditions for copying, distributing or modifying the Program or works based on it. 6. Each time you redistribute the Program (or any work based on the Program), the recipient automatically receives a license from the original licensor to copy, distribute or modify the Program subject to these terms and conditions. You may not impose any further restrictions on the recipients' exercise of the rights granted herein. You are not responsible for enforcing compliance by third parties to this License. 7. If, as a consequence of a court judgment or allegation of patent infringement or for any other reason (not limited to patent issues), conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot distribute so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not distribute the Program at all. For example, if a patent license would not permit royalty-free redistribution of the Program by all those who receive copies directly or indirectly through you, then the only way you could satisfy both it and this License would be to refrain entirely from distribution of the Program. If any portion of this section is held invalid or unenforceable under any particular circumstance, the balance of the section is intended to apply and the section as a whole is intended to apply in other circumstances. It is not the purpose of this section to induce you to infringe any patents or other property right claims or to contest validity of any such claims; this section has the sole purpose of protecting the integrity of the free software distribution system, which is implemented by public license practices. Many people have made generous contributions to the wide range of software distributed through that system in reliance on consistent application of that system; it is up to the author/donor to decide if he or she is willing to distribute software through any other system and a licensee cannot impose that choice. This section is intended to make thoroughly clear what is believed to be a consequence of the rest of this License. 8. If the distribution and/or use of the Program is restricted in certain countries either by patents or by copyrighted interfaces, the original copyright holder who places the Program under this License may add an explicit geographical distribution limitation excluding those countries, so that distribution is permitted only in or among countries not thus excluded. In such case, this License incorporates the limitation as if written in the body of this License. 9. The Free Software Foundation may publish revised and/or new versions of the General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies a version number of this License which applies to it and "any later version", you have the option of following the terms and conditions either of that version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of this License, you may choose any version ever published by the Free Software Foundation. 10. If you wish to incorporate parts of the Program into other free programs whose distribution conditions are different, write to the author to ask for permission. For software which is copyrighted by the Free Software Foundation, write to the Free Software Foundation; we sometimes make exceptions for this. Our decision will be guided by the two goals of preserving the free status of all derivatives of our free software and of promoting the sharing and reuse of software generally. NO WARRANTY 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. muffin-6.4.1/config.h.meson0000664000175000017500000000333614723361714014505 0ustar fabiofabio/* The prefix for our gettext translation domains. */ #mesondefine GETTEXT_PACKAGE /* Version number of package */ #mesondefine VERSION /* Version number of package */ #mesondefine PACKAGE_VERSION /* Search path for plugins */ #mesondefine MUTTER_PLUGIN_DIR /* */ #mesondefine MUTTER_LOCALEDIR /* */ #mesondefine MUTTER_LIBEXECDIR /* */ #mesondefine MUTTER_PKGDATADIR /* Defined if EGL support is enabled */ #mesondefine HAVE_EGL /* Defined if EGLDevice support is enabled */ #mesondefine HAVE_EGL_DEVICE /* Defined if EGLStream support is enabled */ #mesondefine HAVE_WAYLAND_EGLSTREAM /* Building with gudev for device type detection */ #mesondefine HAVE_LIBGUDEV /* Building with libwacom for advanced tablet management */ #mesondefine HAVE_LIBWACOM /* Define if you want to enable the native (KMS) backend based on systemd */ #mesondefine HAVE_NATIVE_BACKEND /* Define if you want to enable Wayland support */ #mesondefine HAVE_WAYLAND /* Defined if screen cast and remote desktop support is enabled */ #mesondefine HAVE_REMOTE_DESKTOP /* Building with SM support */ #mesondefine HAVE_SM /* Building with startup notification support */ #mesondefine HAVE_STARTUP_NOTIFICATION /* Building with Sysprof profiling suport */ #mesondefine HAVE_PROFILER /* Path to Xwayland executable */ #mesondefine XWAYLAND_PATH /* Xwayland applications allowed to issue keyboard grabs */ #mesondefine XWAYLAND_GRAB_DEFAULT_ACCESS_RULES /* XKB base prefix */ #mesondefine XKB_BASE /* Whether exists and it defines prctl() */ #mesondefine HAVE_SYS_PRCTL /* Either or */ #mesondefine HAVE_SYS_RANDOM #mesondefine HAVE_LINUX_RANDOM /* Whether Xwayland has -initfd option */ #mesondefine HAVE_XWAYLAND_INITFD muffin-6.4.1/clutter/0000775000175000017500000000000014723361714013424 5ustar fabiofabiomuffin-6.4.1/clutter/meson.build0000664000175000017500000000315214723361714015567 0ustar fabiofabioclutter_includesubdir = join_paths(pkgname, 'clutter') clutter_includedir = join_paths(includedir, clutter_includesubdir) clutter_includepath = include_directories('.', 'clutter') clutter_includes = [clutter_includepath, cogl_includepath] clutter_c_args = [ '-DCLUTTER_SYSCONFDIR="@0@"'.format(join_paths(prefix, sysconfdir)), '-DCLUTTER_COMPILATION=1', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DG_LOG_DOMAIN="Clutter"', ] clutter_debug_c_args = [] if get_option('debug') clutter_debug_c_args += [ '-DCLUTTER_ENABLE_DEBUG', '-fno-omit-frame-pointer' ] elif buildtype != 'plain' clutter_debug_c_args += [ '-DG_DISABLE_ASSERT', '-DG_DISABLE_CHECKS', '-DG_DISABLE_CAST_CHECKS', ] endif supported_clutter_debug_c_args = cc.get_supported_arguments(clutter_debug_c_args) clutter_c_args += clutter_debug_c_args clutter_pkg_deps = [ atk_dep, cairo_gobject_dep, glib_dep, gobject_dep, gio_dep, json_glib_dep, pango_dep, ] clutter_pkg_private_deps = [ fribidi_dep, gdk_pixbuf_dep, gthread_dep, gmodule_no_export_dep, pangocairo_dep, ] if have_pango_ft2 clutter_pkg_private_deps += [ pangoft2_dep, ] endif if have_wayland clutter_pkg_private_deps += [ wayland_egl_dep, wayland_server_dep, ] endif if have_x11 clutter_pkg_deps += [ x11_dep, ] clutter_pkg_private_deps += [ xext_dep, xdamage_dep, xcomposite_dep, xtst_dep, xi_dep, ] endif if have_libwacom clutter_pkg_private_deps += [ libwacom_dep, ] endif clutter_deps = [ clutter_pkg_deps, clutter_pkg_private_deps, libmutter_cogl_dep, m_dep ] subdir('clutter') muffin-6.4.1/clutter/.gitignore0000664000175000017500000000307414723361714015420 0ustar fabiofabioABOUT-NLS INSTALL Makefile Makefile.in aclocal.m4 autom4te.cache compile *.pc .deps .libs *.o *.lo *.la *.gcov .dirstamp README stamp-enum-types stamp-marshal tags /ChangeLog* clutter-build-config.h clutter-build-config.h.in clutter-config.h clutter-enum-types.[ch] clutter-marshal.[ch] gcov-report.txt clutter-json.h clutter-lcov.info clutter-lcov !/build/autotools/introspection.m4 !/build/autotools/as-linguas.m4 !/build/autotools/as-compiler-flag.m4 !/build/autotools/glibtests.m4 /build/autotools/*.m4 /build/test-driver *.gir *.typelib *.gcda *.gcno config.* configure depcomp /doc/cookbook/*.pdf /doc/cookbook/html /doc/cookbook/*.stamp /doc/cookbook/clutter-cookbook.xml /doc/cookbook/clutter-cookbook.html doc/reference/clutter-*.txt !/doc/reference/clutter-sections.txt doc/reference/html doc/reference/tmpl doc/reference/xml doc/reference/clutter.args doc/reference/clutter.hierarchy doc/reference/clutter.interfaces doc/reference/clutter.prerequisites doc/reference/clutter.signals doc/reference/clutter-docs.xml doc/reference/*.stamp doc/reference/*.bak doc/reference/*.log doc/reference/gtkdoc-check.* gtk-doc.make install-sh libtool ltmain.sh missing mkinstalldirs stamp-h1 TAGS /tests/tools/disable-npots.sh /tests/conform/test-launcher.sh /tests/interactive/wrapper.sh /po/POTFILES /po/clutter-1.0.pot /po/*.gmo /po/Makefile.in.in /po/Makevars.template /po/Rules-quot /po/boldquot.sed /po/en@boldquot.header /po/en@quot.header /po/insert-header.sin /po/quot.sed /po/remove-potcdate.sin /po/remove-potcdate.sed /po/stamp-po *.swn *.swo *.swp *~ *.orig *.rej .DS_Store .testlogs-* muffin-6.4.1/clutter/clutter/0000775000175000017500000000000014723361714015106 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/clutter-path.c0000664000175000017500000012047614723361714017700 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2008 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-path * @short_description: An object describing a path with straight lines * and bezier curves. * * A #ClutterPath contains a description of a path consisting of * straight lines and bezier curves. * * The path consists of a series of nodes. Each node is one of the * following four types: * * - %CLUTTER_PATH_MOVE_TO, changes the position of the path to the * given pair of coordinates. This is usually used as the first node * of a path to mark the start position. If it is used in the middle * of a path then the path will be disjoint and the actor will appear * to jump to the new position when animated. * - %CLUTTER_PATH_LINE_TO, creates a straight line from the previous * point to the given point. * - %CLUTTER_PATH_CURVE_TO, creates a bezier curve. The end of the * last node is used as the first control point and the three * subsequent coordinates given in the node as used as the other three. * -%CLUTTER_PATH_CLOSE, creates a straight line from the last node to * the last %CLUTTER_PATH_MOVE_TO node. This can be used to close a * path so that it will appear as a loop when animated. * * The first three types have the corresponding relative versions * %CLUTTER_PATH_REL_MOVE_TO, %CLUTTER_PATH_REL_LINE_TO and * %CLUTTER_PATH_REL_CURVE_TO. These are exactly the same except the * coordinates are given relative to the previous node instead of as * direct screen positions. * * You can build a path using the node adding functions such as * clutter_path_add_line_to(). Alternatively the path can be described * in a string using a subset of the SVG path syntax. See * clutter_path_add_string() for details. * * #ClutterPath is available since Clutter 1.0 */ #include "clutter-build-config.h" #include #include #include #include #include "clutter-path.h" #include "clutter-types.h" #include "clutter-bezier.h" #include "clutter-private.h" #define CLUTTER_PATH_NODE_TYPE_IS_VALID(t) \ ((((t) & ~CLUTTER_PATH_RELATIVE) >= CLUTTER_PATH_MOVE_TO \ && ((t) & ~CLUTTER_PATH_RELATIVE) <= CLUTTER_PATH_CURVE_TO) \ || (t) == CLUTTER_PATH_CLOSE) enum { PROP_0, PROP_DESCRIPTION, PROP_LENGTH, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; typedef struct _ClutterPathNodeFull ClutterPathNodeFull; struct _ClutterPathNodeFull { ClutterPathNode k; ClutterBezier *bezier; guint length; }; struct _ClutterPathPrivate { GSList *nodes, *nodes_tail; gboolean nodes_dirty; guint total_length; }; /* Character tests that don't pay attention to the locale */ #define clutter_path_isspace(ch) memchr (" \f\n\r\t\v", (ch), 6) #define clutter_path_isdigit(ch) ((ch) >= '0' && (ch) <= '9') static ClutterPathNodeFull *clutter_path_node_full_new (void); static void clutter_path_node_full_free (ClutterPathNodeFull *node); static void clutter_path_finalize (GObject *object); static void clutter_value_transform_path_string (const GValue *src, GValue *dest); static void clutter_value_transform_string_path (const GValue *src, GValue *dest); G_DEFINE_BOXED_TYPE (ClutterPathNode, clutter_path_node, clutter_path_node_copy, clutter_path_node_free); G_DEFINE_TYPE_WITH_CODE (ClutterPath, clutter_path, G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (ClutterPath) CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_path_string) CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_path)); static void clutter_path_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterPath *path = CLUTTER_PATH (gobject); switch (prop_id) { case PROP_DESCRIPTION: g_value_take_string (value, clutter_path_get_description (path)); break; case PROP_LENGTH: g_value_set_uint (value, clutter_path_get_length (path)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_path_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterPath *path = CLUTTER_PATH (gobject); switch (prop_id) { case PROP_DESCRIPTION: if (!clutter_path_set_description (path, g_value_get_string (value))) g_warning ("Invalid path description"); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_path_class_init (ClutterPathClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; GParamSpec *pspec; gobject_class->get_property = clutter_path_get_property; gobject_class->set_property = clutter_path_set_property; gobject_class->finalize = clutter_path_finalize; pspec = g_param_spec_string ("description", "Description", "SVG-style description of the path", "", CLUTTER_PARAM_READWRITE); obj_props[PROP_DESCRIPTION] = pspec; g_object_class_install_property (gobject_class, PROP_DESCRIPTION, pspec); pspec = g_param_spec_uint ("length", "Length", "An approximation of the total length " "of the path.", 0, G_MAXUINT, 0, CLUTTER_PARAM_READABLE); obj_props[PROP_LENGTH] = pspec; g_object_class_install_property (gobject_class, PROP_LENGTH, pspec); } static void clutter_path_init (ClutterPath *self) { self->priv = clutter_path_get_instance_private (self); } static void clutter_value_transform_path_string (const GValue *src, GValue *dest) { gchar *string = clutter_path_get_description (src->data[0].v_pointer); g_value_take_string (dest, string); } static void clutter_value_transform_string_path (const GValue *src, GValue *dest) { ClutterPath *new_path; new_path = clutter_path_new_with_description (g_value_get_string (src)); g_value_take_object (dest, new_path); } static void clutter_path_finalize (GObject *object) { ClutterPath *self = (ClutterPath *) object; clutter_path_clear (self); G_OBJECT_CLASS (clutter_path_parent_class)->finalize (object); } /** * clutter_path_new: * * Creates a new #ClutterPath instance with no nodes. * * Return value: the newly created #ClutterPath * * Since: 1.0 */ ClutterPath * clutter_path_new (void) { ClutterPath *self = g_object_new (CLUTTER_TYPE_PATH, NULL); return self; } /** * clutter_path_new_with_description: * @desc: a string describing the path * * Creates a new #ClutterPath instance with the nodes described in * @desc. See clutter_path_add_string() for details of the format of * the string. * * Return value: the newly created #ClutterPath * * Since: 1.0 */ ClutterPath * clutter_path_new_with_description (const gchar *desc) { return g_object_new (CLUTTER_TYPE_PATH, "description", desc, NULL); } /** * clutter_path_clear: * @path: a #ClutterPath * * Removes all nodes from the path. * * Since: 1.0 */ void clutter_path_clear (ClutterPath *path) { ClutterPathPrivate *priv = path->priv; g_slist_free_full (priv->nodes, (GDestroyNotify) clutter_path_node_full_free); priv->nodes = priv->nodes_tail = NULL; priv->nodes_dirty = TRUE; } /* Takes ownership of the node */ static void clutter_path_add_node_full (ClutterPath *path, ClutterPathNodeFull *node) { ClutterPathPrivate *priv = path->priv; GSList *new_node; new_node = g_slist_prepend (NULL, node); if (priv->nodes_tail == NULL) priv->nodes = new_node; else priv->nodes_tail->next = new_node; priv->nodes_tail = new_node; priv->nodes_dirty = TRUE; } /* Helper function to make the rest of teh add_* functions shorter */ static void clutter_path_add_node_helper (ClutterPath *path, ClutterPathNodeType type, int num_coords, ...) { ClutterPathNodeFull *node; int i; va_list ap; node = clutter_path_node_full_new (); node->k.type = type; va_start (ap, num_coords); for (i = 0; i < num_coords; i++) { node->k.points[i].x = va_arg (ap, gint); node->k.points[i].y = va_arg (ap, gint); } va_end (ap); clutter_path_add_node_full (path, node); } /** * clutter_path_add_move_to: * @path: a #ClutterPath * @x: the x coordinate * @y: the y coordinate * * Adds a %CLUTTER_PATH_MOVE_TO type node to the path. This is usually * used as the first node in a path. It can also be used in the middle * of the path to cause the actor to jump to the new coordinate. * * Since: 1.0 */ void clutter_path_add_move_to (ClutterPath *path, gint x, gint y) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_MOVE_TO, 1, x, y); } /** * clutter_path_add_rel_move_to: * @path: a #ClutterPath * @x: the x coordinate * @y: the y coordinate * * Same as clutter_path_add_move_to() except the coordinates are * relative to the previous node. * * Since: 1.0 */ void clutter_path_add_rel_move_to (ClutterPath *path, gint x, gint y) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_REL_MOVE_TO, 1, x, y); } /** * clutter_path_add_line_to: * @path: a #ClutterPath * @x: the x coordinate * @y: the y coordinate * * Adds a %CLUTTER_PATH_LINE_TO type node to the path. This causes the * actor to move to the new coordinates in a straight line. * * Since: 1.0 */ void clutter_path_add_line_to (ClutterPath *path, gint x, gint y) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_LINE_TO, 1, x, y); } /** * clutter_path_add_rel_line_to: * @path: a #ClutterPath * @x: the x coordinate * @y: the y coordinate * * Same as clutter_path_add_line_to() except the coordinates are * relative to the previous node. * * Since: 1.0 */ void clutter_path_add_rel_line_to (ClutterPath *path, gint x, gint y) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_REL_LINE_TO, 1, x, y); } /** * clutter_path_add_curve_to: * @path: a #ClutterPath * @x_1: the x coordinate of the first control point * @y_1: the y coordinate of the first control point * @x_2: the x coordinate of the second control point * @y_2: the y coordinate of the second control point * @x_3: the x coordinate of the third control point * @y_3: the y coordinate of the third control point * * Adds a %CLUTTER_PATH_CURVE_TO type node to the path. This causes * the actor to follow a bezier from the last node to (@x_3, @y_3) using * (@x_1, @y_1) and (@x_2,@y_2) as control points. * * Since: 1.0 */ void clutter_path_add_curve_to (ClutterPath *path, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_CURVE_TO, 3, x_1, y_1, x_2, y_2, x_3, y_3); } /** * clutter_path_add_rel_curve_to: * @path: a #ClutterPath * @x_1: the x coordinate of the first control point * @y_1: the y coordinate of the first control point * @x_2: the x coordinate of the second control point * @y_2: the y coordinate of the second control point * @x_3: the x coordinate of the third control point * @y_3: the y coordinate of the third control point * * Same as clutter_path_add_curve_to() except the coordinates are * relative to the previous node. * * Since: 1.0 */ void clutter_path_add_rel_curve_to (ClutterPath *path, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_REL_CURVE_TO, 3, x_1, y_1, x_2, y_2, x_3, y_3); } /** * clutter_path_add_close: * @path: a #ClutterPath * * Adds a %CLUTTER_PATH_CLOSE type node to the path. This creates a * straight line from the last node to the last %CLUTTER_PATH_MOVE_TO * type node. * * Since: 1.0 */ void clutter_path_add_close (ClutterPath *path) { g_return_if_fail (CLUTTER_IS_PATH (path)); clutter_path_add_node_helper (path, CLUTTER_PATH_CLOSE, 0); } static gboolean clutter_path_parse_number (const gchar **pin, gboolean allow_comma, gint *ret) { gint val = 0; gboolean negative = FALSE; gint digit_count = 0; const gchar *p = *pin; /* Skip leading spaces */ while (clutter_path_isspace (*p)) p++; /* Optional comma */ if (allow_comma && *p == ',') { p++; while (clutter_path_isspace (*p)) p++; } /* Optional sign */ if (*p == '+') p++; else if (*p == '-') { negative = TRUE; p++; } /* Some digits */ while (clutter_path_isdigit (*p)) { val = val * 10 + *p - '0'; digit_count++; p++; } /* We need at least one digit */ if (digit_count < 1) return FALSE; /* Optional fractional part which we ignore */ if (*p == '.') { p++; digit_count = 0; while (clutter_path_isdigit (*p)) { digit_count++; p++; } /* If there is a fractional part then it also needs at least one digit */ if (digit_count < 1) return FALSE; } *pin = p; *ret = negative ? -val : val; return TRUE; } static gboolean clutter_path_parse_description (const gchar *p, GSList **ret) { ClutterPathNodeFull *node; GSList *nodes = NULL; if (p == NULL || *p == '\0') return FALSE; while (TRUE) { /* Skip leading whitespace */ while (clutter_path_isspace (*p)) p++; /* It is not an error to end now */ if (*p == '\0') break; switch (*p) { case 'M': case 'm': case 'L': case 'l': node = clutter_path_node_full_new (); nodes = g_slist_prepend (nodes, node); node->k.type = (*p == 'M' ? CLUTTER_PATH_MOVE_TO : *p == 'm' ? CLUTTER_PATH_REL_MOVE_TO : *p == 'L' ? CLUTTER_PATH_LINE_TO : CLUTTER_PATH_REL_LINE_TO); p++; if (!clutter_path_parse_number (&p, FALSE, &node->k.points[0].x) || !clutter_path_parse_number (&p, TRUE, &node->k.points[0].y)) goto fail; break; case 'C': case 'c': node = clutter_path_node_full_new (); nodes = g_slist_prepend (nodes, node); node->k.type = (*p == 'C' ? CLUTTER_PATH_CURVE_TO : CLUTTER_PATH_REL_CURVE_TO); p++; if (!clutter_path_parse_number (&p, FALSE, &node->k.points[0].x) || !clutter_path_parse_number (&p, TRUE, &node->k.points[0].y) || !clutter_path_parse_number (&p, TRUE, &node->k.points[1].x) || !clutter_path_parse_number (&p, TRUE, &node->k.points[1].y) || !clutter_path_parse_number (&p, TRUE, &node->k.points[2].x) || !clutter_path_parse_number (&p, TRUE, &node->k.points[2].y)) goto fail; break; case 'Z': case 'z': node = clutter_path_node_full_new (); nodes = g_slist_prepend (nodes, node); p++; node->k.type = CLUTTER_PATH_CLOSE; break; default: goto fail; } } *ret = g_slist_reverse (nodes); return TRUE; fail: g_slist_free_full (nodes, (GDestroyNotify) clutter_path_node_full_free); return FALSE; } /* Takes ownership of the node list */ static void clutter_path_add_nodes (ClutterPath *path, GSList *nodes) { ClutterPathPrivate *priv = path->priv; if (priv->nodes_tail == NULL) priv->nodes = nodes; else priv->nodes_tail->next = nodes; while (nodes) { priv->nodes_tail = nodes; nodes = nodes->next; } priv->nodes_dirty = TRUE; } /** * clutter_path_add_string: * @path: a #ClutterPath * @str: a string describing the new nodes * * Adds new nodes to the end of the path as described in @str. The * format is a subset of the SVG path format. Each node is represented * by a letter and is followed by zero, one or three pairs of * coordinates. The coordinates can be separated by spaces or a * comma. The types are: * * - `M`: Adds a %CLUTTER_PATH_MOVE_TO node. Takes one pair of coordinates. * - `L`: Adds a %CLUTTER_PATH_LINE_TO node. Takes one pair of coordinates. * - `C`: Adds a %CLUTTER_PATH_CURVE_TO node. Takes three pairs of coordinates. * - `z`: Adds a %CLUTTER_PATH_CLOSE node. No coordinates are needed. * * The M, L and C commands can also be specified in lower case which * means the coordinates are relative to the previous node. * * For example, to move an actor in a 100 by 100 pixel square centered * on the point 300,300 you could use the following path: * * |[ * M 250,350 l 0 -100 L 350,250 l 0 100 z * ]| * * If the path description isn't valid %FALSE will be returned and no * nodes will be added. * * Return value: %TRUE is the path description was valid or %FALSE * otherwise. * * Since: 1.0 */ gboolean clutter_path_add_string (ClutterPath *path, const gchar *str) { GSList *nodes; g_return_val_if_fail (CLUTTER_IS_PATH (path), FALSE); g_return_val_if_fail (str != NULL, FALSE); if (clutter_path_parse_description (str, &nodes)) { clutter_path_add_nodes (path, nodes); return TRUE; } else return FALSE; } /** * clutter_path_add_node: * @path: a #ClutterPath * @node: a #ClutterPathNode * * Adds @node to the end of the path. * * Since: 1.0 */ void clutter_path_add_node (ClutterPath *path, const ClutterPathNode *node) { ClutterPathNodeFull *node_full; g_return_if_fail (CLUTTER_IS_PATH (path)); g_return_if_fail (node != NULL); g_return_if_fail (CLUTTER_PATH_NODE_TYPE_IS_VALID (node->type)); node_full = clutter_path_node_full_new (); node_full->k = *node; clutter_path_add_node_full (path, node_full); } /** * clutter_path_add_cairo_path: * @path: a #ClutterPath * @cpath: a Cairo path * * Add the nodes of the Cairo path to the end of @path. * * Since: 1.0 */ void clutter_path_add_cairo_path (ClutterPath *path, const cairo_path_t *cpath) { int num_data; const cairo_path_data_t *p; g_return_if_fail (CLUTTER_IS_PATH (path)); g_return_if_fail (cpath != NULL); /* Iterate over each command in the cairo path */ for (num_data = cpath->num_data, p = cpath->data; num_data > 0; num_data -= p->header.length, p += p->header.length) { switch (p->header.type) { case CAIRO_PATH_MOVE_TO: g_assert (p->header.length >= 2); clutter_path_add_move_to (path, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_LINE_TO: g_assert (p->header.length >= 2); clutter_path_add_line_to (path, p[1].point.x, p[1].point.y); break; case CAIRO_PATH_CURVE_TO: g_assert (p->header.length >= 4); clutter_path_add_curve_to (path, p[1].point.x, p[1].point.y, p[2].point.x, p[2].point.y, p[3].point.x, p[3].point.y); break; case CAIRO_PATH_CLOSE_PATH: clutter_path_add_close (path); } } } static void clutter_path_add_node_to_cairo_path (const ClutterPathNode *node, gpointer data) { cairo_t *cr = data; switch (node->type) { case CLUTTER_PATH_MOVE_TO: cairo_move_to (cr, node->points[0].x, node->points[0].y); break; case CLUTTER_PATH_LINE_TO: cairo_line_to (cr, node->points[0].x, node->points[0].y); break; case CLUTTER_PATH_CURVE_TO: cairo_curve_to (cr, node->points[0].x, node->points[0].y, node->points[1].x, node->points[1].y, node->points[2].x, node->points[2].y); break; case CLUTTER_PATH_REL_MOVE_TO: cairo_rel_move_to (cr, node->points[0].x, node->points[0].y); break; case CLUTTER_PATH_REL_LINE_TO: cairo_rel_line_to (cr, node->points[0].x, node->points[0].y); break; case CLUTTER_PATH_REL_CURVE_TO: cairo_rel_curve_to (cr, node->points[0].x, node->points[0].y, node->points[1].x, node->points[1].y, node->points[2].x, node->points[2].y); break; case CLUTTER_PATH_CLOSE: cairo_close_path (cr); } } /** * clutter_path_to_cairo_path: * @path: a #ClutterPath * @cr: a Cairo context * * Add the nodes of the ClutterPath to the path in the Cairo context. * * Since: 1.0 */ void clutter_path_to_cairo_path (ClutterPath *path, cairo_t *cr) { g_return_if_fail (CLUTTER_IS_PATH (path)); g_return_if_fail (cr != NULL); clutter_path_foreach (path, clutter_path_add_node_to_cairo_path, cr); } /** * clutter_path_get_n_nodes: * @path: a #ClutterPath * * Retrieves the number of nodes in the path. * * Return value: the number of nodes. * * Since: 1.0 */ guint clutter_path_get_n_nodes (ClutterPath *path) { ClutterPathPrivate *priv; g_return_val_if_fail (CLUTTER_IS_PATH (path), 0); priv = path->priv; return g_slist_length (priv->nodes); } /** * clutter_path_get_node: * @path: a #ClutterPath * @index_: the node number to retrieve * @node: (out): a location to store a copy of the node * * Retrieves the node of the path indexed by @index. * * Since: 1.0 */ void clutter_path_get_node (ClutterPath *path, guint index_, ClutterPathNode *node) { ClutterPathNodeFull *node_full; ClutterPathPrivate *priv; g_return_if_fail (CLUTTER_IS_PATH (path)); priv = path->priv; node_full = g_slist_nth_data (priv->nodes, index_); g_return_if_fail (node_full != NULL); *node = node_full->k; } /** * clutter_path_get_nodes: * @path: a #ClutterPath * * Returns a #GSList of #ClutterPathNodes. The list should be * freed with g_slist_free(). The nodes are owned by the path and * should not be freed. Altering the path may cause the nodes in the * list to become invalid so you should copy them if you want to keep * the list. * * Return value: (transfer container) (element-type Clutter.PathNode): a * list of nodes in the path. * * Since: 1.0 */ GSList * clutter_path_get_nodes (ClutterPath *path) { ClutterPathPrivate *priv; g_return_val_if_fail (CLUTTER_IS_PATH (path), NULL); priv = path->priv; return g_slist_copy (priv->nodes); } /** * clutter_path_foreach: * @path: a #ClutterPath * @callback: (scope call): the function to call with each node * @user_data: user data to pass to the function * * Calls a function for each node of the path. * * Since: 1.0 */ void clutter_path_foreach (ClutterPath *path, ClutterPathCallback callback, gpointer user_data) { ClutterPathPrivate *priv; g_return_if_fail (CLUTTER_IS_PATH (path)); priv = path->priv; g_slist_foreach (priv->nodes, (GFunc) callback, user_data); } /** * clutter_path_insert_node: * @path: a #ClutterPath * @index_: offset of where to insert the node * @node: the node to insert * * Inserts @node into the path before the node at the given offset. If * @index_ is negative it will append the node to the end of the path. * * Since: 1.0 */ void clutter_path_insert_node (ClutterPath *path, gint index_, const ClutterPathNode *node) { ClutterPathPrivate *priv; ClutterPathNodeFull *node_full; g_return_if_fail (CLUTTER_IS_PATH (path)); g_return_if_fail (node != NULL); g_return_if_fail (CLUTTER_PATH_NODE_TYPE_IS_VALID (node->type)); priv = path->priv; node_full = clutter_path_node_full_new (); node_full->k = *node; priv->nodes = g_slist_insert (priv->nodes, node_full, index_); if (priv->nodes_tail == NULL) priv->nodes_tail = priv->nodes; else if (priv->nodes_tail->next) priv->nodes_tail = priv->nodes_tail->next; priv->nodes_dirty = TRUE; } /** * clutter_path_remove_node: * @path: a #ClutterPath * @index_: index of the node to remove * * Removes the node at the given offset from the path. * * Since: 1.0 */ void clutter_path_remove_node (ClutterPath *path, guint index_) { ClutterPathPrivate *priv; GSList *node, *prev = NULL; g_return_if_fail (CLUTTER_IS_PATH (path)); priv = path->priv; for (node = priv->nodes; node && index_--; node = node->next) prev = node; if (node) { clutter_path_node_full_free (node->data); if (prev) prev->next = node->next; else priv->nodes = node->next; if (node == priv->nodes_tail) priv->nodes_tail = prev; g_slist_free_1 (node); priv->nodes_dirty = TRUE; } } /** * clutter_path_replace_node: * @path: a #ClutterPath * @index_: index to the existing node * @node: the replacement node * * Replaces the node at offset @index_ with @node. * * Since: 1.0 */ void clutter_path_replace_node (ClutterPath *path, guint index_, const ClutterPathNode *node) { ClutterPathPrivate *priv; ClutterPathNodeFull *node_full; g_return_if_fail (CLUTTER_IS_PATH (path)); g_return_if_fail (node != NULL); g_return_if_fail (CLUTTER_PATH_NODE_TYPE_IS_VALID (node->type)); priv = path->priv; if ((node_full = g_slist_nth_data (priv->nodes, index_))) { node_full->k = *node; priv->nodes_dirty = TRUE; } } /** * clutter_path_set_description: * @path: a #ClutterPath * @str: a string describing the path * * Replaces all of the nodes in the path with nodes described by * @str. See clutter_path_add_string() for details of the format. * * If the string is invalid then %FALSE is returned and the path is * unaltered. * * Return value: %TRUE is the path was valid, %FALSE otherwise. * * Since: 1.0 */ gboolean clutter_path_set_description (ClutterPath *path, const gchar *str) { GSList *nodes; g_return_val_if_fail (CLUTTER_IS_PATH (path), FALSE); g_return_val_if_fail (str != NULL, FALSE); if (clutter_path_parse_description (str, &nodes)) { clutter_path_clear (path); clutter_path_add_nodes (path, nodes); return TRUE; } else return FALSE; } /** * clutter_path_get_description: * @path: a #ClutterPath * * Returns a newly allocated string describing the path in the same * format as used by clutter_path_add_string(). * * Return value: a string description of the path. Free with g_free(). * * Since: 1.0 */ gchar * clutter_path_get_description (ClutterPath *path) { ClutterPathPrivate *priv; GString *str; GSList *l; g_return_val_if_fail (CLUTTER_IS_PATH (path), NULL); priv = path->priv; str = g_string_new (""); for (l = priv->nodes; l; l = l->next) { ClutterPathNodeFull *node = l->data; gchar letter = '?'; gint params = 0; gint i; switch (node->k.type) { case CLUTTER_PATH_MOVE_TO: letter = 'M'; params = 1; break; case CLUTTER_PATH_REL_MOVE_TO: letter = 'm'; params = 1; break; case CLUTTER_PATH_LINE_TO: letter = 'L'; params = 1; break; case CLUTTER_PATH_REL_LINE_TO: letter = 'l'; params = 1; break; case CLUTTER_PATH_CURVE_TO: letter = 'C'; params = 3; break; case CLUTTER_PATH_REL_CURVE_TO: letter = 'c'; params = 3; break; case CLUTTER_PATH_CLOSE: letter = 'z'; params = 0; break; } if (str->len > 0) g_string_append_c (str, ' '); g_string_append_c (str, letter); for (i = 0; i < params; i++) g_string_append_printf (str, " %i %i", node->k.points[i].x, node->k.points[i].y); } return g_string_free (str, FALSE); } static guint clutter_path_node_distance (const ClutterKnot *start, const ClutterKnot *end) { gint64 x_d, y_d; float t; g_return_val_if_fail (start != NULL, 0); g_return_val_if_fail (end != NULL, 0); if (clutter_knot_equal (start, end)) return 0; x_d = end->x - start->x; y_d = end->y - start->y; t = floorf (sqrtf ((x_d * x_d) + (y_d * y_d))); return (guint) t; } static void clutter_path_ensure_node_data (ClutterPath *path) { ClutterPathPrivate *priv = path->priv; /* Recalculate the nodes data if has changed */ if (priv->nodes_dirty) { GSList *l; ClutterKnot last_position = { 0, 0 }; ClutterKnot loop_start = { 0, 0 }; ClutterKnot points[3]; priv->total_length = 0; for (l = priv->nodes; l; l = l->next) { ClutterPathNodeFull *node = l->data; gboolean relative = (node->k.type & CLUTTER_PATH_RELATIVE) != 0; switch (node->k.type & ~CLUTTER_PATH_RELATIVE) { case CLUTTER_PATH_MOVE_TO: node->length = 0; /* Store the actual position in point[1] */ if (relative) { node->k.points[1].x = last_position.x + node->k.points[0].x; node->k.points[1].y = last_position.y + node->k.points[0].y; } else node->k.points[1] = node->k.points[0]; last_position = node->k.points[1]; loop_start = node->k.points[1]; break; case CLUTTER_PATH_LINE_TO: /* Use point[1] as the start point and point[2] as the end point */ node->k.points[1] = last_position; if (relative) { node->k.points[2].x = (node->k.points[1].x + node->k.points[0].x); node->k.points[2].y = (node->k.points[1].y + node->k.points[0].y); } else node->k.points[2] = node->k.points[0]; last_position = node->k.points[2]; node->length = clutter_path_node_distance (node->k.points + 1, node->k.points + 2); break; case CLUTTER_PATH_CURVE_TO: /* Convert to a bezier curve */ if (node->bezier == NULL) node->bezier = _clutter_bezier_new (); if (relative) { int i; for (i = 0; i < 3; i++) { points[i].x = last_position.x + node->k.points[i].x; points[i].y = last_position.y + node->k.points[i].y; } } else memcpy (points, node->k.points, sizeof (ClutterKnot) * 3); _clutter_bezier_init (node->bezier, last_position.x, last_position.y, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y); last_position = points[2]; node->length = _clutter_bezier_get_length (node->bezier); break; case CLUTTER_PATH_CLOSE: /* Convert to a line to from last_point to loop_start */ node->k.points[1] = last_position; node->k.points[2] = loop_start; last_position = node->k.points[2]; node->length = clutter_path_node_distance (node->k.points + 1, node->k.points + 2); break; } priv->total_length += node->length; } priv->nodes_dirty = FALSE; } } /** * clutter_path_get_position: * @path: a #ClutterPath * @progress: a position along the path as a fraction of its length * @position: (out): location to store the position * * The value in @progress represents a position along the path where * 0.0 is the beginning and 1.0 is the end of the path. An * interpolated position is then stored in @position. * * Return value: index of the node used to calculate the position. * * Since: 1.0 */ guint clutter_path_get_position (ClutterPath *path, gdouble progress, ClutterKnot *position) { ClutterPathPrivate *priv; GSList *l; guint point_distance, length = 0, node_num = 0; ClutterPathNodeFull *node; g_return_val_if_fail (CLUTTER_IS_PATH (path), 0); g_return_val_if_fail (progress >= 0.0 && progress <= 1.0, 0); priv = path->priv; clutter_path_ensure_node_data (path); /* Special case if the path is empty, just return 0,0 for want of something better */ if (priv->nodes == NULL) { memset (position, 0, sizeof (ClutterKnot)); return 0; } /* Convert the progress to a length along the path */ point_distance = progress * priv->total_length; /* Find the node that covers this point */ for (l = priv->nodes; l->next && point_distance >= (((ClutterPathNodeFull *) l->data)->length + length); l = l->next) { length += ((ClutterPathNodeFull *) l->data)->length; node_num++; } node = l->data; /* Convert the point distance to a distance along the node */ point_distance -= length; if (point_distance > node->length) point_distance = node->length; switch (node->k.type & ~CLUTTER_PATH_RELATIVE) { case CLUTTER_PATH_MOVE_TO: *position = node->k.points[1]; break; case CLUTTER_PATH_LINE_TO: case CLUTTER_PATH_CLOSE: if (node->length == 0) *position = node->k.points[1]; else { position->x = (node->k.points[1].x + ((node->k.points[2].x - node->k.points[1].x) * (gint) point_distance / (gint) node->length)); position->y = (node->k.points[1].y + ((node->k.points[2].y - node->k.points[1].y) * (gint) point_distance / (gint) node->length)); } break; case CLUTTER_PATH_CURVE_TO: if (node->length == 0) *position = node->k.points[2]; else { _clutter_bezier_advance (node->bezier, point_distance * CLUTTER_BEZIER_MAX_LENGTH / node->length, position); } break; } return node_num; } /** * clutter_path_get_length: * @path: a #ClutterPath * * Retrieves an approximation of the total length of the path. * * Return value: the length of the path. * * Since: 1.0 */ guint clutter_path_get_length (ClutterPath *path) { g_return_val_if_fail (CLUTTER_IS_PATH (path), 0); clutter_path_ensure_node_data (path); return path->priv->total_length; } static ClutterPathNodeFull * clutter_path_node_full_new (void) { return g_slice_new0 (ClutterPathNodeFull); } static void clutter_path_node_full_free (ClutterPathNodeFull *node) { if (node->bezier) _clutter_bezier_free (node->bezier); g_slice_free (ClutterPathNodeFull, node); } /** * clutter_path_node_copy: * @node: a #ClutterPathNode * * Makes an allocated copy of a node. * * Return value: the copied node. * * Since: 1.0 */ ClutterPathNode * clutter_path_node_copy (const ClutterPathNode *node) { return g_slice_dup (ClutterPathNode, node); } /** * clutter_path_node_free: * @node: a #ClutterPathNode * * Frees the memory of an allocated node. * * Since: 1.0 */ void clutter_path_node_free (ClutterPathNode *node) { if (G_LIKELY (node)) g_slice_free (ClutterPathNode, node); } /** * clutter_path_node_equal: * @node_a: First node * @node_b: Second node * * Compares two nodes and checks if they are the same type with the * same coordinates. * * Return value: %TRUE if the nodes are the same. * * Since: 1.0 */ gboolean clutter_path_node_equal (const ClutterPathNode *node_a, const ClutterPathNode *node_b) { guint n_points, i; g_return_val_if_fail (node_a != NULL, FALSE); g_return_val_if_fail (node_b != NULL, FALSE); if (node_a->type != node_b->type) return FALSE; switch (node_a->type & ~CLUTTER_PATH_RELATIVE) { case CLUTTER_PATH_MOVE_TO: n_points = 1; break; case CLUTTER_PATH_LINE_TO: n_points = 1; break; case CLUTTER_PATH_CURVE_TO: n_points = 3; break; case CLUTTER_PATH_CLOSE: n_points = 0; break; default: return FALSE; } for (i = 0; i < n_points; i++) if (node_a->points[i].x != node_b->points[i].x || node_a->points[i].y != node_b->points[i].y) return FALSE; return TRUE; } G_DEFINE_BOXED_TYPE (ClutterKnot, clutter_knot, clutter_knot_copy, clutter_knot_free); /** * clutter_knot_copy: * @knot: a #ClutterKnot * * Makes an allocated copy of a knot. * * Return value: the copied knot. * * Since: 0.2 */ ClutterKnot * clutter_knot_copy (const ClutterKnot *knot) { if (G_UNLIKELY (knot == NULL)) return NULL; return g_slice_dup (ClutterKnot, knot); } /** * clutter_knot_free: * @knot: a #ClutterKnot * * Frees the memory of an allocated knot. * * Since: 0.2 */ void clutter_knot_free (ClutterKnot *knot) { if (G_LIKELY (knot != NULL)) g_slice_free (ClutterKnot, knot); } /** * clutter_knot_equal: * @knot_a: First knot * @knot_b: Second knot * * Compares to knot and checks if the point to the same location. * * Return value: %TRUE if the knots point to the same location. * * Since: 0.2 */ gboolean clutter_knot_equal (const ClutterKnot *knot_a, const ClutterKnot *knot_b) { g_return_val_if_fail (knot_a != NULL, FALSE); g_return_val_if_fail (knot_b != NULL, FALSE); if (knot_a == knot_b) return TRUE; return knot_a->x == knot_b->x && knot_a->y == knot_b->y; } muffin-6.4.1/clutter/clutter/clutter-pan-action.c0000664000175000017500000007312214723361714020770 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * Copyright (C) 2012, 2014 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emanuele Aina * * Based on ClutterDragAction, ClutterSwipeAction, and MxKineticScrollView, * written by: * Emmanuele Bassi * Tomeu Vizoso * Chris Lord */ /** * SECTION:clutter-pan-action * @Title: ClutterPanAction * @Short_Description: Action for pan gestures * * #ClutterPanAction is a sub-class of #ClutterGestureAction that implements * the logic for recognizing pan gestures. * * The simplest usage of #ClutterPanAction consists in adding it to * a #ClutterActor with a child and setting it as reactive; for instance, * the following code: * * |[ * clutter_actor_add_action (actor, clutter_pan_action_new ()); * clutter_actor_set_reactive (actor, TRUE); * ]| * * will automatically result in the actor children to be moved * when dragging. * * Since: 1.12 */ #include "clutter-build-config.h" #include "clutter-pan-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-gesture-action-private.h" #include "clutter-marshal.h" #include "clutter-private.h" #include #define FLOAT_EPSILON (1e-15) static const gfloat min_velocity = 0.1f; // measured in px/ms static const gfloat reference_fps = 60.0f; // the fps assumed for the deceleration rate static const gfloat default_deceleration_rate = 0.95f; static const gfloat default_acceleration_factor = 1.0f; typedef enum { PAN_STATE_INACTIVE, PAN_STATE_PANNING, PAN_STATE_INTERPOLATING } PanState; typedef enum { SCROLL_PINNED_UNKNOWN, SCROLL_PINNED_NONE, SCROLL_PINNED_HORIZONTAL, SCROLL_PINNED_VERTICAL } PinState; struct _ClutterPanActionPrivate { ClutterPanAxis pan_axis; PanState state; /* Variables for storing acceleration information */ ClutterTimeline *deceleration_timeline; gfloat target_x; gfloat target_y; gfloat dx; gfloat dy; gdouble deceleration_rate; gdouble acceleration_factor; /* Inertial motion tracking */ gfloat interpolated_x; gfloat interpolated_y; gfloat release_x; gfloat release_y; guint should_interpolate : 1; PinState pin_state; }; enum { PROP_0, PROP_PAN_AXIS, PROP_INTERPOLATE, PROP_DECELERATION, PROP_ACCELERATION_FACTOR, PROP_LAST }; static GParamSpec *pan_props[PROP_LAST] = { NULL, }; enum { PAN, PAN_STOPPED, LAST_SIGNAL }; static guint pan_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterPanAction, clutter_pan_action, CLUTTER_TYPE_GESTURE_ACTION) static void emit_pan (ClutterPanAction *self, ClutterActor *actor, gboolean is_interpolated) { ClutterPanActionPrivate *priv = self->priv; gboolean retval; if (priv->pin_state == SCROLL_PINNED_UNKNOWN) { priv->pin_state = SCROLL_PINNED_NONE; if (priv->pan_axis == CLUTTER_PAN_AXIS_AUTO) { gfloat delta_x; gfloat delta_y; gfloat scroll_threshold = G_PI_4/2; gfloat drag_angle; clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self), 0, &delta_x, &delta_y); if (delta_x != 0.0f) drag_angle = atanf (delta_y / delta_x); else drag_angle = G_PI_2; if ((drag_angle > -scroll_threshold) && (drag_angle < scroll_threshold)) priv->pin_state = SCROLL_PINNED_HORIZONTAL; else if ((drag_angle > (G_PI_2 - scroll_threshold)) || (drag_angle < -(G_PI_2 - scroll_threshold))) priv->pin_state = SCROLL_PINNED_VERTICAL; } } g_signal_emit (self, pan_signals[PAN], 0, actor, is_interpolated, &retval); } static void emit_pan_stopped (ClutterPanAction *self, ClutterActor *actor) { ClutterPanActionPrivate *priv = self->priv; g_signal_emit (self, pan_signals[PAN_STOPPED], 0, actor); priv->state = PAN_STATE_INACTIVE; } static void on_deceleration_stopped (ClutterTimeline *timeline, gboolean is_finished, ClutterPanAction *self) { ClutterPanActionPrivate *priv = self->priv; ClutterActor *actor; g_object_unref (timeline); priv->deceleration_timeline = NULL; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)); emit_pan_stopped (self, actor); } static void on_deceleration_new_frame (ClutterTimeline *timeline, gint elapsed_time, ClutterPanAction *self) { ClutterPanActionPrivate *priv = self->priv; ClutterActor *actor; gdouble progress; gfloat interpolated_x, interpolated_y; progress = clutter_timeline_get_progress (timeline); interpolated_x = priv->target_x * progress; interpolated_y = priv->target_y * progress; priv->dx = interpolated_x - priv->interpolated_x; priv->dy = interpolated_y - priv->interpolated_y; priv->interpolated_x = interpolated_x; priv->interpolated_y = interpolated_y; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)); emit_pan (self, actor, TRUE); } static gboolean gesture_prepare (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); ClutterPanActionPrivate *priv = self->priv; if (priv->state == PAN_STATE_INTERPOLATING && priv->deceleration_timeline) clutter_timeline_stop (priv->deceleration_timeline); return TRUE; } static gboolean gesture_begin (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); ClutterPanActionPrivate *priv = self->priv; priv->pin_state = SCROLL_PINNED_UNKNOWN; priv->state = PAN_STATE_PANNING; priv->interpolated_x = priv->interpolated_y = 0.0f; priv->dx = priv->dy = 0.0f; return TRUE; } static gboolean gesture_progress (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); emit_pan (self, actor, FALSE); return TRUE; } static void gesture_cancel (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); ClutterPanActionPrivate *priv = self->priv; priv->state = PAN_STATE_INACTIVE; } static void gesture_end (ClutterGestureAction *gesture, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gesture); ClutterPanActionPrivate *priv = self->priv; gfloat velocity, velocity_x, velocity_y; gfloat delta_x, delta_y; gfloat tau; gint duration; clutter_gesture_action_get_release_coords (CLUTTER_GESTURE_ACTION (self), 0, &priv->release_x, &priv->release_y); if (!priv->should_interpolate) { priv->state = PAN_STATE_INACTIVE; return; } priv->state = PAN_STATE_INTERPOLATING; clutter_gesture_action_get_motion_delta (gesture, 0, &delta_x, &delta_y); velocity = clutter_gesture_action_get_velocity (gesture, 0, &velocity_x, &velocity_y); /* Exponential timing constant v(t) = v(0) * exp(-t/tau) * tau = 1000ms / (frame_per_second * - ln(decay_per_frame)) * with frame_per_second = 60 and decay_per_frame = 0.95, tau ~= 325ms * see http://ariya.ofilabs.com/2011/10/flick-list-with-its-momentum-scrolling-and-deceleration.html */ tau = 1000.0f / (reference_fps * - logf (priv->deceleration_rate)); /* See where the decreasing velocity reaches $min_velocity px/ms * v(t) = v(0) * exp(-t/tau) = min_velocity * t = - tau * ln( min_velocity / |v(0)|) */ duration = - tau * logf (min_velocity / (ABS (velocity) * priv->acceleration_factor)); /* Target point: x(t) = v(0) * tau * [1 - exp(-t/tau)] */ priv->target_x = velocity_x * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau)); priv->target_y = velocity_y * priv->acceleration_factor * tau * (1 - exp ((float)-duration / tau)); if (ABS (velocity) * priv->acceleration_factor > min_velocity && duration > FLOAT_EPSILON) { priv->interpolated_x = priv->interpolated_y = 0.0f; priv->deceleration_timeline = clutter_timeline_new (duration); clutter_timeline_set_progress_mode (priv->deceleration_timeline, CLUTTER_EASE_OUT_EXPO); g_signal_connect (priv->deceleration_timeline, "new_frame", G_CALLBACK (on_deceleration_new_frame), self); g_signal_connect (priv->deceleration_timeline, "stopped", G_CALLBACK (on_deceleration_stopped), self); clutter_timeline_start (priv->deceleration_timeline); } else { emit_pan_stopped (self, actor); } } static gboolean clutter_pan_action_real_pan (ClutterPanAction *self, ClutterActor *actor, gboolean is_interpolated) { gfloat dx, dy; ClutterMatrix transform; clutter_pan_action_get_constrained_motion_delta (self, 0, &dx, &dy); clutter_actor_get_child_transform (actor, &transform); cogl_matrix_translate (&transform, dx, dy, 0.0f); clutter_actor_set_child_transform (actor, &transform); return TRUE; } static void clutter_pan_action_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gobject); switch (prop_id) { case PROP_PAN_AXIS: clutter_pan_action_set_pan_axis (self, g_value_get_enum (value)); break; case PROP_INTERPOLATE : clutter_pan_action_set_interpolate (self, g_value_get_boolean (value)); break; case PROP_DECELERATION : clutter_pan_action_set_deceleration (self, g_value_get_double (value)); break; case PROP_ACCELERATION_FACTOR : clutter_pan_action_set_acceleration_factor (self, g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_pan_action_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterPanAction *self = CLUTTER_PAN_ACTION (gobject); ClutterPanActionPrivate *priv = self->priv; switch (prop_id) { case PROP_PAN_AXIS: g_value_set_enum (value, priv->pan_axis); break; case PROP_INTERPOLATE : g_value_set_boolean (value, priv->should_interpolate); break; case PROP_DECELERATION : g_value_set_double (value, priv->deceleration_rate); break; case PROP_ACCELERATION_FACTOR : g_value_set_double (value, priv->acceleration_factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_pan_action_constructed (GObject *gobject) { ClutterGestureAction *gesture; gesture = CLUTTER_GESTURE_ACTION (gobject); clutter_gesture_action_set_threshold_trigger_edge (gesture, CLUTTER_GESTURE_TRIGGER_EDGE_AFTER); } static void clutter_pan_action_dispose (GObject *gobject) { ClutterPanActionPrivate *priv = CLUTTER_PAN_ACTION (gobject)->priv; g_clear_object (&priv->deceleration_timeline); G_OBJECT_CLASS (clutter_pan_action_parent_class)->dispose (gobject); } static void clutter_pan_action_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterPanAction *self = CLUTTER_PAN_ACTION (meta); ClutterPanActionPrivate *priv = self->priv; ClutterActor *old_actor; old_actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (self)); if (old_actor != actor) { /* make sure we reset the state */ if (priv->state == PAN_STATE_INTERPOLATING) g_clear_object (&priv->deceleration_timeline); } CLUTTER_ACTOR_META_CLASS (clutter_pan_action_parent_class)->set_actor (meta, actor); } static void clutter_pan_action_class_init (ClutterPanActionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterGestureActionClass *gesture_class = CLUTTER_GESTURE_ACTION_CLASS (klass); klass->pan = clutter_pan_action_real_pan; gesture_class->gesture_prepare = gesture_prepare; gesture_class->gesture_begin = gesture_begin; gesture_class->gesture_progress = gesture_progress; gesture_class->gesture_cancel = gesture_cancel; gesture_class->gesture_end = gesture_end; meta_class->set_actor = clutter_pan_action_set_actor; /** * ClutterPanAction:pan-axis: * * Constraints the panning action to the specified axis * * Since: 1.12 */ pan_props[PROP_PAN_AXIS] = g_param_spec_enum ("pan-axis", P_("Pan Axis"), P_("Constraints the panning to an axis"), CLUTTER_TYPE_PAN_AXIS, CLUTTER_PAN_AXIS_NONE, CLUTTER_PARAM_READWRITE); /** * ClutterPanAction:interpolate: * * Whether interpolated events emission is enabled. * * Since: 1.12 */ pan_props[PROP_INTERPOLATE] = g_param_spec_boolean ("interpolate", P_("Interpolate"), P_("Whether interpolated events emission is enabled."), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterPanAction:deceleration: * * The rate at which the interpolated panning will decelerate in * * #ClutterPanAction will emit interpolated ::pan events with decreasing * scroll deltas, using the rate specified by this property. * * Since: 1.12 */ pan_props[PROP_DECELERATION] = g_param_spec_double ("deceleration", P_("Deceleration"), P_("Rate at which the interpolated panning will decelerate in"), FLOAT_EPSILON, 1.0, default_deceleration_rate, CLUTTER_PARAM_READWRITE); /** * ClutterPanAction:acceleration-factor: * * The initial acceleration factor * * The kinetic momentum measured at the time of releasing the pointer will * be multiplied by the factor specified by this property before being used * to generate interpolated ::pan events. * * Since: 1.12 */ pan_props[PROP_ACCELERATION_FACTOR] = g_param_spec_double ("acceleration-factor", P_("Initial acceleration factor"), P_("Factor applied to the momentum when starting the interpolated phase"), 1.0, G_MAXDOUBLE, default_acceleration_factor, CLUTTER_PARAM_READWRITE); gobject_class->constructed = clutter_pan_action_constructed; gobject_class->set_property = clutter_pan_action_set_property; gobject_class->get_property = clutter_pan_action_get_property; gobject_class->dispose = clutter_pan_action_dispose; g_object_class_install_properties (gobject_class, PROP_LAST, pan_props); /** * ClutterPanAction::pan: * @action: the #ClutterPanAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @is_interpolated: if the event is the result of interpolating * the motion velocity at the end of the drag * * The ::pan signal is emitted to keep track of the motion during * a pan gesture. @is_interpolated is set to %TRUE during the * interpolation phase of the pan, after the drag has ended and * the :interpolate property was set to %TRUE. * * Return value: %TRUE if the pan should continue, and %FALSE if * the pan should be cancelled. * * Since: 1.12 */ pan_signals[PAN] = g_signal_new (I_("pan"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterPanActionClass, pan), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_BOOLEAN, G_TYPE_BOOLEAN, 2, CLUTTER_TYPE_ACTOR, G_TYPE_BOOLEAN); /** * ClutterPanAction::pan-stopped: * @action: the #ClutterPanAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::pan-stopped signal is emitted at the end of the interpolation * phase of the pan action, only when :interpolate is set to %TRUE. * * Since: 1.12 */ pan_signals[PAN_STOPPED] = g_signal_new (I_("pan-stopped"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterPanActionClass, pan_stopped), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); } static void clutter_pan_action_init (ClutterPanAction *self) { self->priv = clutter_pan_action_get_instance_private (self); self->priv->deceleration_rate = default_deceleration_rate; self->priv->acceleration_factor = default_acceleration_factor; self->priv->state = PAN_STATE_INACTIVE; } /** * clutter_pan_action_new: * * Creates a new #ClutterPanAction instance * * Return value: the newly created #ClutterPanAction * * Since: 1.12 */ ClutterAction * clutter_pan_action_new (void) { return g_object_new (CLUTTER_TYPE_PAN_ACTION, NULL); } /** * clutter_pan_action_set_pan_axis: * @self: a #ClutterPanAction * @axis: the axis to constraint the panning to * * Restricts the panning action to a specific axis * * Since: 1.12 */ void clutter_pan_action_set_pan_axis (ClutterPanAction *self, ClutterPanAxis axis) { ClutterPanActionPrivate *priv; g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); g_return_if_fail (axis >= CLUTTER_PAN_AXIS_NONE && axis <= CLUTTER_PAN_AXIS_AUTO); priv = self->priv; if (priv->pan_axis == axis) return; priv->pan_axis = axis; g_object_notify_by_pspec (G_OBJECT (self), pan_props[PROP_PAN_AXIS]); } /** * clutter_pan_action_get_pan_axis: * @self: a #ClutterPanAction * * Retrieves the axis constraint set by clutter_pan_action_set_pan_axis() * * Return value: the axis constraint * * Since: 1.12 */ ClutterPanAxis clutter_pan_action_get_pan_axis (ClutterPanAction *self) { g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), CLUTTER_PAN_AXIS_NONE); return self->priv->pan_axis; } /** * clutter_pan_action_set_interpolate: * @self: a #ClutterPanAction * @should_interpolate: whether to enable interpolated pan events * * Sets whether the action should emit interpolated ::pan events * after the drag has ended, to emulate the gesture kinetic inertia. * * Since: 1.12 */ void clutter_pan_action_set_interpolate (ClutterPanAction *self, gboolean should_interpolate) { ClutterPanActionPrivate *priv; g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); priv = self->priv; should_interpolate = !!should_interpolate; if (priv->should_interpolate == should_interpolate) return; priv->should_interpolate = should_interpolate; g_object_notify_by_pspec (G_OBJECT (self), pan_props[PROP_INTERPOLATE]); } /** * clutter_pan_action_get_interpolate: * @self: a #ClutterPanAction * * Checks if the action should emit ::pan events even after releasing * the pointer during a panning gesture, to emulate some kind of * kinetic inertia. * * Return value: %TRUE if interpolated events emission is active. * * Since: 1.12 */ gboolean clutter_pan_action_get_interpolate (ClutterPanAction *self) { g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), FALSE); return self->priv->should_interpolate; } /** * clutter_pan_action_set_deceleration: * @self: A #ClutterPanAction * @rate: The deceleration rate * * Sets the deceleration rate of the interpolated ::pan events generated * after a pan gesture. This is approximately the value that the momentum * at the time of releasing the pointer is divided by every 60th of a second. * * Since: 1.12 */ void clutter_pan_action_set_deceleration (ClutterPanAction *self, gdouble rate) { g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); g_return_if_fail (rate <= 1.0); g_return_if_fail (rate > 0.0); self->priv->deceleration_rate = rate; g_object_notify_by_pspec (G_OBJECT (self), pan_props[PROP_DECELERATION]); } /** * clutter_pan_action_get_deceleration: * @self: A #ClutterPanAction * * Retrieves the deceleration rate of interpolated ::pan events. * * Return value: The deceleration rate of the interpolated events. * * Since: 1.12 */ gdouble clutter_pan_action_get_deceleration (ClutterPanAction *self) { g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.95); return self->priv->deceleration_rate; } /** * clutter_pan_action_set_acceleration_factor: * @self: A #ClutterPanAction * @factor: The acceleration factor * * Factor applied to the momentum velocity at the time of releasing the * pointer when generating interpolated ::pan events. * * Since: 1.12 */ void clutter_pan_action_set_acceleration_factor (ClutterPanAction *self, gdouble factor) { g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); g_return_if_fail (factor >= 0.0); self->priv->acceleration_factor = factor; g_object_notify_by_pspec (G_OBJECT (self), pan_props[PROP_ACCELERATION_FACTOR]); } /** * clutter_pan_action_get_acceleration_factor: * @self: A #ClutterPanAction * * Retrieves the initial acceleration factor for interpolated ::pan events. * * Return value: The initial acceleration factor for interpolated events. * * Since: 1.12 */ gdouble clutter_pan_action_get_acceleration_factor (ClutterPanAction *self) { g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 1.0); return self->priv->acceleration_factor; } /** * clutter_pan_action_get_interpolated_coords: * @self: A #ClutterPanAction * @interpolated_x: (out) (allow-none): return location for the latest * interpolated event's X coordinate * @interpolated_y: (out) (allow-none): return location for the latest * interpolated event's Y coordinate * * Retrieves the coordinates, in stage space, of the latest interpolated * event, analogous to clutter_gesture_action_get_motion_coords(). * * Since: 1.12 */ void clutter_pan_action_get_interpolated_coords (ClutterPanAction *self, gfloat *interpolated_x, gfloat *interpolated_y) { ClutterPanActionPrivate *priv; g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); priv = self->priv; if (interpolated_x) *interpolated_x = priv->release_x + priv->interpolated_x; if (interpolated_y) *interpolated_y = priv->release_y + priv->interpolated_y; } /** * clutter_pan_action_get_interpolated_delta: * @self: A #ClutterPanAction * @delta_x: (out) (allow-none): return location for the X delta since * the latest interpolated event * @delta_y: (out) (allow-none): return location for the Y delta since * the latest interpolated event * * Retrieves the delta, in stage space, since the latest interpolated * event, analogous to clutter_gesture_action_get_motion_delta(). * * Return value: the distance since the latest interpolated event * * Since: 1.12 */ gfloat clutter_pan_action_get_interpolated_delta (ClutterPanAction *self, gfloat *delta_x, gfloat *delta_y) { ClutterPanActionPrivate *priv; g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.0f); priv = self->priv; if (delta_x) *delta_x = priv->dx; if (delta_y) *delta_y = priv->dy; return sqrt ((priv->dx * priv->dx) + (priv->dy * priv->dy)); } /** * clutter_pan_action_get_constrained_motion_delta: * @self: A #ClutterPanAction * @point: the touch point index, with 0 being the first touch * point received by the action * @delta_x: (out) (optional): return location for the X delta * @delta_y: (out) (optional): return location for the Y delta * * Retrieves the delta, in stage space, dependent on the current state * of the #ClutterPanAction, and respecting the constraint specified by the * #ClutterPanAction:pan-axis property. * * Return value: the distance since last motion event * * Since: 1.24 */ gfloat clutter_pan_action_get_constrained_motion_delta (ClutterPanAction *self, guint point, gfloat *out_delta_x, gfloat *out_delta_y) { ClutterPanActionPrivate *priv; gfloat delta_x = 0.f, delta_y = 0.f, distance; g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.0f); priv = self->priv; distance = clutter_pan_action_get_motion_delta (self, point, &delta_x, &delta_y); switch (priv->pan_axis) { case CLUTTER_PAN_AXIS_NONE: break; case CLUTTER_PAN_AXIS_AUTO: if (priv->pin_state == SCROLL_PINNED_VERTICAL) delta_x = 0.0f; else if (priv->pin_state == SCROLL_PINNED_HORIZONTAL) delta_y = 0.0f; break; case CLUTTER_PAN_X_AXIS: delta_y = 0.0f; break; case CLUTTER_PAN_Y_AXIS: delta_x = 0.0f; break; } if (out_delta_x) *out_delta_x = delta_x; if (out_delta_y) *out_delta_y = delta_y; return distance; } /** * clutter_pan_action_get_motion_delta: * @self: A #ClutterPanAction * @point: the touch point index, with 0 being the first touch * point received by the action * @delta_x: (out) (allow-none): return location for the X delta * @delta_y: (out) (allow-none): return location for the Y delta * * Retrieves the delta, in stage space, dependent on the current state * of the #ClutterPanAction. If it is inactive, both fields will be * set to 0. If it is panning by user action, the values will be equivalent * to those returned by clutter_gesture_action_get_motion_delta(). * If it is interpolating with some form of kinetic scrolling, the values * will be equivalent to those returned by * clutter_pan_action_get_interpolated_delta(). This is a convenience * method designed to be used in replacement "pan" signal handlers. * * Since: 1.14 */ gfloat clutter_pan_action_get_motion_delta (ClutterPanAction *self, guint point, gfloat *delta_x, gfloat *delta_y) { ClutterPanActionPrivate *priv; g_return_val_if_fail (CLUTTER_IS_PAN_ACTION (self), 0.0f); priv = self->priv; switch (priv->state) { case PAN_STATE_INACTIVE: if (delta_x) *delta_x = 0; if (delta_y) *delta_y = 0; return 0; case PAN_STATE_PANNING: return clutter_gesture_action_get_motion_delta (CLUTTER_GESTURE_ACTION (self), point, delta_x, delta_y); case PAN_STATE_INTERPOLATING: return clutter_pan_action_get_interpolated_delta (self, delta_x, delta_y); default: g_assert_not_reached (); return 0.0f; } } /** * clutter_pan_action_get_motion_coords: * @self: A #ClutterPanAction * @point: the touch point index, with 0 being the first touch * point received by the action * @motion_x: (out) (allow-none): return location for the X coordinate * @motion_y: (out) (allow-none): return location for the Y coordinate * * Retrieves the coordinates, in stage space, dependent on the current state * of the #ClutterPanAction. If it is inactive, both fields will be * set to 0. If it is panning by user action, the values will be equivalent * to those returned by clutter_gesture_action_get_motion_coords(). * If it is interpolating with some form of kinetic scrolling, the values * will be equivalent to those returned by * clutter_pan_action_get_interpolated_coords(). This is a convenience * method designed to be used in replacement "pan" signal handlers. * * Since: 1.14 */ void clutter_pan_action_get_motion_coords (ClutterPanAction *self, guint point, gfloat *motion_x, gfloat *motion_y) { ClutterPanActionPrivate *priv; g_return_if_fail (CLUTTER_IS_PAN_ACTION (self)); priv = self->priv; switch (priv->state) { case PAN_STATE_INACTIVE: if (motion_x) *motion_x = 0; if (motion_y) *motion_y = 0; break; case PAN_STATE_PANNING: clutter_gesture_action_get_motion_coords (CLUTTER_GESTURE_ACTION (self), point, motion_x, motion_y); break; case PAN_STATE_INTERPOLATING: clutter_pan_action_get_interpolated_coords (self, motion_x, motion_y); break; default: g_assert_not_reached (); } } muffin-6.4.1/clutter/clutter/clutter-blur-effect.h0000664000175000017500000000347614723361714021147 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_BLUR_EFFECT_H__ #define __CLUTTER_BLUR_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_BLUR_EFFECT (clutter_blur_effect_get_type ()) #define CLUTTER_BLUR_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffect)) #define CLUTTER_IS_BLUR_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BLUR_EFFECT)) /** * ClutterBlurEffect: * * #ClutterBlurEffect is an opaque structure * whose members cannot be accessed directly * * Since: 1.4 */ typedef struct _ClutterBlurEffect ClutterBlurEffect; typedef struct _ClutterBlurEffectClass ClutterBlurEffectClass; CLUTTER_EXPORT GType clutter_blur_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect *clutter_blur_effect_new (void); G_END_DECLS #endif /* __CLUTTER_BLUR_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-paint-context-private.h0000664000175000017500000000312614723361714023206 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef CLUTTER_PAINT_CONTEXT_PRIVATE_H #define CLUTTER_PAINT_CONTEXT_PRIVATE_H #include "clutter-paint-context.h" typedef enum _ClutterPaintFlag { CLUTTER_PAINT_FLAG_NONE = 0, CLUTTER_PAINT_FLAG_NO_CURSORS = 1 << 0, CLUTTER_PAINT_FLAG_NO_PAINT_SIGNAL = 1 << 1, } ClutterPaintFlag; ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view, const cairo_region_t *redraw_clip, ClutterPaintFlag paint_flags); gboolean clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context); CoglFramebuffer * clutter_paint_context_get_base_framebuffer (ClutterPaintContext *paint_context); CLUTTER_EXPORT ClutterPaintFlag clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context); #endif /* CLUTTER_PAINT_CONTEXT_PRIVATE_H */ muffin-6.4.1/clutter/clutter/clutter-deprecated.h0000664000175000017500000000110414723361714021033 0ustar fabiofabio#ifndef __CLUTTER_DEPRECATED_H__ #define __CLUTTER_DEPRECATED_H__ #define __CLUTTER_DEPRECATED_H_INSIDE__ #include "deprecated/clutter-actor.h" #include "deprecated/clutter-alpha.h" #include "deprecated/clutter-animation.h" #include "deprecated/clutter-box.h" #include "deprecated/clutter-container.h" #include "deprecated/clutter-group.h" #include "deprecated/clutter-rectangle.h" #include "deprecated/clutter-stage.h" #include "deprecated/clutter-state.h" #include "deprecated/clutter-timeline.h" #undef __CLUTTER_DEPRECATED_H_INSIDE__ #endif /* __CLUTTER_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/clutter-click-action.h0000664000175000017500000001020114723361714021271 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Inspired by the StClickable class in GNOME Shell, written by: * Colin Walters */ #ifndef __CLUTTER_CLICK_ACTION_H__ #define __CLUTTER_CLICK_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_CLICK_ACTION (clutter_click_action_get_type ()) #define CLUTTER_CLICK_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CLICK_ACTION, ClutterClickAction)) #define CLUTTER_IS_CLICK_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CLICK_ACTION)) #define CLUTTER_CLICK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CLICK_ACTION, ClutterClickActionClass)) #define CLUTTER_IS_CLICK_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CLICK_ACTION)) #define CLUTTER_CLICK_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CLICK_ACTION, ClutterClickActionClass)) typedef struct _ClutterClickAction ClutterClickAction; typedef struct _ClutterClickActionPrivate ClutterClickActionPrivate; typedef struct _ClutterClickActionClass ClutterClickActionClass; /** * ClutterClickAction: * * The #ClutterClickAction structure contains * only private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterClickAction { /*< private >*/ ClutterAction parent_instance; ClutterClickActionPrivate *priv; }; /** * ClutterClickActionClass: * @clicked: class handler for the #ClutterClickAction::clicked signal * @long_press: class handler for the #ClutterClickAction::long-press signal * * The #ClutterClickActionClass structure * contains only private data * * Since: 1.4 */ struct _ClutterClickActionClass { /*< private >*/ ClutterActionClass parent_class; /*< public >*/ void (* clicked) (ClutterClickAction *action, ClutterActor *actor); gboolean (* long_press) (ClutterClickAction *action, ClutterActor *actor, ClutterLongPressState state); /*< private >*/ void (* _clutter_click_action1) (void); void (* _clutter_click_action2) (void); void (* _clutter_click_action3) (void); void (* _clutter_click_action4) (void); void (* _clutter_click_action5) (void); void (* _clutter_click_action6) (void); void (* _clutter_click_action7) (void); }; CLUTTER_EXPORT GType clutter_click_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_click_action_new (void); CLUTTER_EXPORT guint clutter_click_action_get_button (ClutterClickAction *action); CLUTTER_EXPORT ClutterModifierType clutter_click_action_get_state (ClutterClickAction *action); CLUTTER_EXPORT void clutter_click_action_get_coords (ClutterClickAction *action, gfloat *press_x, gfloat *press_y); CLUTTER_EXPORT void clutter_click_action_release (ClutterClickAction *action); G_END_DECLS #endif /* __CLUTTER_CLICK_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-config.h.in0000664000175000017500000000045614723361714020616 0ustar fabiofabio#if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_CONFIG_H__ #define __CLUTTER_CONFIG_H__ #include G_BEGIN_DECLS @CLUTTER_CONFIG_DEFINES@ G_END_DECLS #endif /* __CLUTTER_CONFIG_H__ */ muffin-6.4.1/clutter/clutter/clutter-stage.h0000664000175000017500000002363114723361714020047 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_STAGE_H__ #define __CLUTTER_STAGE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE (clutter_stage_get_type()) #define CLUTTER_STAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE, ClutterStage)) #define CLUTTER_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE, ClutterStageClass)) #define CLUTTER_IS_STAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE)) #define CLUTTER_IS_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE)) #define CLUTTER_STAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE, ClutterStageClass)) typedef struct _ClutterStageClass ClutterStageClass; typedef struct _ClutterStagePrivate ClutterStagePrivate; /** * ClutterStage: * * The #ClutterStage structure contains only private data * and should be accessed using the provided API * * Since: 0.2 */ struct _ClutterStage { /*< private >*/ ClutterGroup parent_instance; ClutterStagePrivate *priv; }; /** * ClutterStageClass: * @activate: handler for the #ClutterStage::activate signal * @deactivate: handler for the #ClutterStage::deactivate signal * @delete_event: handler for the #ClutterStage::delete-event signal * * The #ClutterStageClass structure contains only private data * * Since: 0.2 */ struct _ClutterStageClass { /*< private >*/ ClutterGroupClass parent_class; /*< public >*/ /* signals */ void (* activate) (ClutterStage *stage); void (* deactivate) (ClutterStage *stage); gboolean (* delete_event) (ClutterStage *stage, ClutterEvent *event); void (* paint_view) (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip); /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[30]; }; /** * ClutterPerspective: * @fovy: the field of view angle, in degrees, in the y direction * @aspect: the aspect ratio that determines the field of view in the x * direction. The aspect ratio is the ratio of x (width) to y (height) * @z_near: the distance from the viewer to the near clipping * plane (always positive) * @z_far: the distance from the viewer to the far clipping * plane (always positive) * * Stage perspective definition. #ClutterPerspective is only used by * the fixed point version of clutter_stage_set_perspective(). * * Since: 0.4 */ struct _ClutterPerspective { gfloat fovy; gfloat aspect; gfloat z_near; gfloat z_far; }; /** * ClutterFrameInfo: (skip) */ struct _ClutterFrameInfo { int64_t frame_counter; int64_t presentation_time; float refresh_rate; }; typedef struct _ClutterCapture { cairo_surface_t *image; cairo_rectangle_int_t rect; } ClutterCapture; CLUTTER_EXPORT GType clutter_perspective_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GType clutter_stage_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_stage_new (void); CLUTTER_EXPORT void clutter_stage_set_perspective (ClutterStage *stage, ClutterPerspective *perspective); CLUTTER_EXPORT void clutter_stage_get_perspective (ClutterStage *stage, ClutterPerspective *perspective); CLUTTER_EXPORT void clutter_stage_show_cursor (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_hide_cursor (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_title (ClutterStage *stage, const gchar *title); CLUTTER_EXPORT const gchar * clutter_stage_get_title (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_minimum_size (ClutterStage *stage, guint width, guint height); CLUTTER_EXPORT void clutter_stage_get_minimum_size (ClutterStage *stage, guint *width, guint *height); CLUTTER_EXPORT void clutter_stage_set_use_alpha (ClutterStage *stage, gboolean use_alpha); CLUTTER_EXPORT gboolean clutter_stage_get_use_alpha (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_key_focus (ClutterStage *stage, ClutterActor *actor); CLUTTER_EXPORT ClutterActor * clutter_stage_get_key_focus (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_throttle_motion_events (ClutterStage *stage, gboolean throttle); CLUTTER_EXPORT gboolean clutter_stage_get_throttle_motion_events (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_motion_events_enabled (ClutterStage *stage, gboolean enabled); CLUTTER_EXPORT gboolean clutter_stage_get_motion_events_enabled (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_set_accept_focus (ClutterStage *stage, gboolean accept_focus); CLUTTER_EXPORT gboolean clutter_stage_get_accept_focus (ClutterStage *stage); CLUTTER_EXPORT gboolean clutter_stage_event (ClutterStage *stage, ClutterEvent *event); CLUTTER_EXPORT ClutterActor * clutter_stage_get_actor_at_pos (ClutterStage *stage, ClutterPickMode pick_mode, float x, float y); CLUTTER_EXPORT guchar * clutter_stage_read_pixels (ClutterStage *stage, gint x, gint y, gint width, gint height); CLUTTER_EXPORT void clutter_stage_ensure_viewport (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_ensure_redraw (ClutterStage *stage); CLUTTER_EXPORT gboolean clutter_stage_is_redraw_queued (ClutterStage *stage); #ifdef CLUTTER_ENABLE_EXPERIMENTAL_API CLUTTER_EXPORT void clutter_stage_set_sync_delay (ClutterStage *stage, gint sync_delay); CLUTTER_EXPORT void clutter_stage_skip_sync_delay (ClutterStage *stage); #endif CLUTTER_EXPORT void clutter_stage_schedule_update (ClutterStage *stage); CLUTTER_EXPORT gboolean clutter_stage_get_capture_final_size (ClutterStage *stage, cairo_rectangle_int_t *rect, int *width, int *height, float *scale); CLUTTER_EXPORT gboolean clutter_stage_capture (ClutterStage *stage, gboolean paint, cairo_rectangle_int_t *rect, ClutterCapture **out_captures, int *out_n_captures); CLUTTER_EXPORT ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage, float x, float y); G_END_DECLS #endif /* __CLUTTER_STAGE_H__ */ muffin-6.4.1/clutter/clutter/clutter-drag-action.h0000664000175000017500000001464314723361714021137 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_DRAG_ACTION_H__ #define __CLUTTER_DRAG_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_DRAG_ACTION (clutter_drag_action_get_type ()) #define CLUTTER_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragAction)) #define CLUTTER_IS_DRAG_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DRAG_ACTION)) #define CLUTTER_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass)) #define CLUTTER_IS_DRAG_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DRAG_ACTION)) #define CLUTTER_DRAG_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DRAG_ACTION, ClutterDragActionClass)) typedef struct _ClutterDragAction ClutterDragAction; typedef struct _ClutterDragActionPrivate ClutterDragActionPrivate; typedef struct _ClutterDragActionClass ClutterDragActionClass; /** * ClutterDragAction: * * The #ClutterDragAction structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterDragAction { /*< private >*/ ClutterAction parent_instance; ClutterDragActionPrivate *priv; }; /** * ClutterDragActionClass: * @drag_begin: class handler of the #ClutterDragAction::drag-begin signal * @drag_motion: class handler of the #ClutterDragAction::drag-motion signal * @drag_end: class handler of the #ClutterDragAction::drag-end signal * @drag_progress: class handler of the #ClutterDragAction::drag-progress signal * * The #ClutterDragActionClass structure contains * only private data * * Since: 1.4 */ struct _ClutterDragActionClass { /*< private >*/ ClutterActionClass parent_class; /*< public >*/ void (* drag_begin) (ClutterDragAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y, ClutterModifierType modifiers); void (* drag_motion) (ClutterDragAction *action, ClutterActor *actor, gfloat delta_x, gfloat delta_y); void (* drag_end) (ClutterDragAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y, ClutterModifierType modifiers); gboolean (* drag_progress) (ClutterDragAction *action, ClutterActor *actor, gfloat delta_x, gfloat delta_y); /*< private >*/ void (* _clutter_drag_action1) (void); void (* _clutter_drag_action2) (void); void (* _clutter_drag_action3) (void); void (* _clutter_drag_action4) (void); }; CLUTTER_EXPORT GType clutter_drag_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_drag_action_new (void); CLUTTER_EXPORT void clutter_drag_action_set_drag_threshold (ClutterDragAction *action, gint x_threshold, gint y_threshold); CLUTTER_EXPORT void clutter_drag_action_get_drag_threshold (ClutterDragAction *action, guint *x_threshold, guint *y_threshold); CLUTTER_EXPORT void clutter_drag_action_set_drag_handle (ClutterDragAction *action, ClutterActor *handle); CLUTTER_EXPORT ClutterActor * clutter_drag_action_get_drag_handle (ClutterDragAction *action); CLUTTER_EXPORT void clutter_drag_action_set_drag_axis (ClutterDragAction *action, ClutterDragAxis axis); CLUTTER_EXPORT ClutterDragAxis clutter_drag_action_get_drag_axis (ClutterDragAction *action); CLUTTER_EXPORT void clutter_drag_action_get_press_coords (ClutterDragAction *action, gfloat *press_x, gfloat *press_y); CLUTTER_EXPORT void clutter_drag_action_get_motion_coords (ClutterDragAction *action, gfloat *motion_x, gfloat *motion_y); CLUTTER_EXPORT gboolean clutter_drag_action_get_drag_area (ClutterDragAction *action, graphene_rect_t *drag_area); CLUTTER_EXPORT void clutter_drag_action_set_drag_area (ClutterDragAction *action, const graphene_rect_t *drag_area); G_END_DECLS #endif /* __CLUTTER_DRAG_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-actor.c0000664000175000017500000240216414723361714020053 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010, 2011, 2012 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-actor * @short_description: The basic element of the scene graph * * The ClutterActor class is the basic element of the scene graph in Clutter, * and it encapsulates the position, size, and transformations of a node in * the graph. * * ## Actor transformations ## {#clutter-actor-transformations} * * Each actor can be transformed using methods like clutter_actor_set_scale() * or clutter_actor_set_rotation(). The order in which the transformations are * applied is decided by Clutter and it is the following: * * 1. translation by the origin of the #ClutterActor:allocation property * 2. translation by the actor's #ClutterActor:z-position property * 3. translation by the actor's #ClutterActor:pivot-point property * 4. scaling by the #ClutterActor:scale-x and #ClutterActor:scale-y factors * 5. rotation around the #ClutterActor:rotation-angle-x and #ClutterActor:rotation-center-x * 6. rotation around the #ClutterActor:rotation-angle-y and #ClutterActor:rotation-center-y * 7. rotation around the #ClutterActor:rotation-angle-z and #ClutterActor:rotation-center-z * 8. negative translation by the #ClutterActor:anchor-x and #ClutterActor:anchor-y point. * 9. negative translation by the actor's #ClutterActor:pivot-point * * ## Modifying an actor's geometry ## {#clutter-actor-geometry} * * Each actor has a bounding box, called #ClutterActor:allocation * which is either set by its parent or explicitly through the * clutter_actor_set_position() and clutter_actor_set_size() methods. * Each actor also has an implicit preferred size. * * An actor’s preferred size can be defined by any subclass by * overriding the #ClutterActorClass.get_preferred_width() and the * #ClutterActorClass.get_preferred_height() virtual functions, or it can * be explicitly set by using clutter_actor_set_width() and * clutter_actor_set_height(). * * An actor’s position can be set explicitly by using * clutter_actor_set_x() and clutter_actor_set_y(); the coordinates are * relative to the origin of the actor’s parent. * * ## Managing actor children ## {#clutter-actor-children} * * Each actor can have multiple children, by calling * clutter_actor_add_child() to add a new child actor, and * clutter_actor_remove_child() to remove an existing child. #ClutterActor * will hold a reference on each child actor, which will be released when * the child is removed from its parent, or destroyed using * clutter_actor_destroy(). * * |[ * ClutterActor *actor = clutter_actor_new (); * * // set the bounding box of the actor * clutter_actor_set_position (actor, 0, 0); * clutter_actor_set_size (actor, 480, 640); * * // set the background color of the actor * clutter_actor_set_background_color (actor, CLUTTER_COLOR_Orange); * * // set the bounding box of the child, relative to the parent * ClutterActor *child = clutter_actor_new (); * clutter_actor_set_position (child, 20, 20); * clutter_actor_set_size (child, 80, 240); * * // set the background color of the child * clutter_actor_set_background_color (child, CLUTTER_COLOR_Blue); * * // add the child to the actor * clutter_actor_add_child (actor, child); * ]| * * Children can be inserted at a given index, or above and below * another child actor. The order of insertion determines the order of the * children when iterating over them. Iterating over children is performed * by using clutter_actor_get_first_child(), clutter_actor_get_previous_sibling(), * clutter_actor_get_next_sibling(), and clutter_actor_get_last_child(). It is * also possible to retrieve a list of children by using * clutter_actor_get_children(), as well as retrieving a specific child at a * given index by using clutter_actor_get_child_at_index(). * * If you need to track additions of children to a #ClutterActor, use * the #ClutterContainer::actor-added signal; similarly, to track removals * of children from a ClutterActor, use the #ClutterContainer::actor-removed * signal. * * See [basic-actor.c](https://git.gnome.org/browse/clutter/tree/examples/basic-actor.c?h=clutter-1.18). * * ## Painting an actor ## {#clutter-actor-painting} * * There are three ways to paint an actor: * * - set a delegate #ClutterContent as the value for the #ClutterActor:content property of the actor * - subclass #ClutterActor and override the #ClutterActorClass.paint_node() virtual function * - subclass #ClutterActor and override the #ClutterActorClass.paint() virtual function. * * A #ClutterContent is a delegate object that takes over the painting * operations of one, or more actors. The #ClutterContent painting will * be performed on top of the #ClutterActor:background-color of the actor, * and before calling the actor's own implementation of the * #ClutterActorClass.paint_node() virtual function. * * |[ * ClutterActor *actor = clutter_actor_new (); * * // set the bounding box * clutter_actor_set_position (actor, 50, 50); * clutter_actor_set_size (actor, 100, 100); * * // set the content; the image_content variable is set elsewhere * clutter_actor_set_content (actor, image_content); * ]| * * The #ClutterActorClass.paint_node() virtual function is invoked whenever * an actor needs to be painted. The implementation of the virtual function * must only paint the contents of the actor itself, and not the contents of * its children, if the actor has any. * * The #ClutterPaintNode passed to the virtual function is the local root of * the render tree; any node added to it will be rendered at the correct * position, as defined by the actor's #ClutterActor:allocation. * * |[ * static void * my_actor_paint_node (ClutterActor *actor, * ClutterPaintNode *root) * { * ClutterPaintNode *node; * ClutterActorBox box; * * // where the content of the actor should be painted * clutter_actor_get_allocation_box (actor, &box); * * // the cogl_texture variable is set elsewhere * node = clutter_texture_node_new (cogl_texture, CLUTTER_COLOR_White, * CLUTTER_SCALING_FILTER_TRILINEAR, * CLUTTER_SCALING_FILTER_LINEAR); * * // paint the content of the node using the allocation * clutter_paint_node_add_rectangle (node, &box); * * // add the node, and transfer ownership * clutter_paint_node_add_child (root, node); * clutter_paint_node_unref (node); * } * * The #ClutterActorClass.paint() virtual function is invoked when the * #ClutterActor::paint signal is emitted, and after the other signal * handlers have been invoked. Overriding the paint virtual function * gives total control to the paint sequence of the actor itself, * including the children of the actor, if any. * * It is strongly discouraged to override the #ClutterActorClass.paint() * virtual function, as well as connecting to the #ClutterActor::paint * signal. These hooks into the paint sequence are considered legacy, and * will be removed when the Clutter API changes. * * ## Handling events on an actor ## {#clutter-actor-event-handling} * * A #ClutterActor can receive and handle input device events, for * instance pointer events and key events, as long as its * #ClutterActor:reactive property is set to %TRUE. * * Once an actor has been determined to be the source of an event, * Clutter will traverse the scene graph from the top-level actor towards the * event source, emitting the #ClutterActor::captured-event signal on each * ancestor until it reaches the source; this phase is also called * the "capture" phase. If the event propagation was not stopped, the graph * is walked backwards, from the source actor to the top-level, and the * #ClutterActor::event signal is emitted, alongside eventual event-specific * signals like #ClutterActor::button-press-event or #ClutterActor::motion-event; * this phase is also called the "bubble" phase. * * At any point of the signal emission, signal handlers can stop the propagation * through the scene graph by returning %CLUTTER_EVENT_STOP; otherwise, they can * continue the propagation by returning %CLUTTER_EVENT_PROPAGATE. * * ## Animation ## {#clutter-actor-animation} * * Animation is a core concept of modern user interfaces; Clutter provides a * complete and powerful animation framework that automatically tweens the * actor's state without requiring direct, frame by frame manipulation from * your application code. You have two models at your disposal: * * - an implicit animation model * - an explicit animation model * * The implicit animation model of Clutter assumes that all the * changes in an actor state should be gradual and asynchronous; Clutter * will automatically transition an actor's property change between the * current state and the desired one without manual intervention, if the * property is defined to be animatable in its documentation. * * By default, in the 1.0 API series, the transition happens with a duration * of zero milliseconds, and the implicit animation is an opt in feature to * retain backwards compatibility. * * Implicit animations depend on the current easing state; in order to use * the default easing state for an actor you should call the * clutter_actor_save_easing_state() function: * * |[ * // assume that the actor is currently positioned at (100, 100) * * // store the current easing state and reset the new easing state to * // its default values * clutter_actor_save_easing_state (actor); * * // change the actor's position * clutter_actor_set_position (actor, 500, 500); * * // restore the previously saved easing state * clutter_actor_restore_easing_state (actor); * ]| * * The example above will trigger an implicit animation of the * actor between its current position to a new position. * * Implicit animations use a default duration of 250 milliseconds, * and a default easing mode of %CLUTTER_EASE_OUT_CUBIC, unless you call * clutter_actor_set_easing_mode() and clutter_actor_set_easing_duration() * after changing the easing state of the actor. * * It is possible to animate multiple properties of an actor * at the same time, and you can animate multiple actors at the same * time as well, for instance: * * |[ * clutter_actor_save_easing_state (actor); * * // animate the actor's opacity and depth * clutter_actor_set_opacity (actor, 0); * clutter_actor_set_depth (actor, -100); * * clutter_actor_restore_easing_state (actor); * * clutter_actor_save_easing_state (another_actor); * * // animate another actor's opacity * clutter_actor_set_opacity (another_actor, 255); * clutter_actor_set_depth (another_actor, 100); * * clutter_actor_restore_easing_state (another_actor); * ]| * * Changing the easing state will affect all the following property * transitions, but will not affect existing transitions. * * It is important to note that if you modify the state on an * animatable property while a transition is in flight, the transition's * final value will be updated, as well as its duration and progress * mode by using the current easing state; for instance, in the following * example: * * |[ * clutter_actor_save_easing_state (actor); * clutter_actor_set_easing_duration (actor, 1000); * clutter_actor_set_x (actor, 200); * clutter_actor_restore_easing_state (actor); * * clutter_actor_save_easing_state (actor); * clutter_actor_set_easing_duration (actor, 500); * clutter_actor_set_x (actor, 100); * clutter_actor_restore_easing_state (actor); * ]| * * the first call to clutter_actor_set_x() will begin a transition * of the #ClutterActor:x property from the current value to the value of * 200 over a duration of one second; the second call to clutter_actor_set_x() * will change the transition's final value to 100 and the duration to 500 * milliseconds. * * It is possible to receive a notification of the completion of an * implicit transition by using the #ClutterActor::transition-stopped * signal, decorated with the name of the property. In case you want to * know when all the currently in flight transitions are complete, use * the #ClutterActor::transitions-completed signal instead. * * It is possible to retrieve the #ClutterTransition used by the * animatable properties by using clutter_actor_get_transition() and using * the property name as the transition name. * * The explicit animation model supported by Clutter requires that * you create a #ClutterTransition object, and optionally set the initial * and final values. The transition will not start unless you add it to the * #ClutterActor. * * |[ * ClutterTransition *transition; * * transition = clutter_property_transition_new ("opacity"); * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 3000); * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2); * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE); * clutter_transition_set_from (transition, G_TYPE_UINT, 255); * clutter_transition_set_to (transition, G_TYPE_UINT, 0); * * clutter_actor_add_transition (actor, "animate-opacity", transition); * ]| * * The example above will animate the #ClutterActor:opacity property * of an actor between fully opaque and fully transparent, and back, over * a span of 3 seconds. The animation does not begin until it is added to * the actor. * * The explicit animation API applies to all #GObject properties, * as well as the custom properties defined through the #ClutterAnimatable * interface, regardless of whether they are defined as implicitly * animatable or not. * * The explicit animation API should also be used when using custom * animatable properties for #ClutterAction, #ClutterConstraint, and * #ClutterEffect instances associated to an actor; see the section on * custom animatable properties below for an example. * * Finally, explicit animations are useful for creating animations * that run continuously, for instance: * * |[ * // this animation will pulse the actor's opacity continuously * ClutterTransition *transition; * ClutterInterval *interval; * * transition = clutter_property_transition_new ("opacity"); * * // we want to animate the opacity between 0 and 255 * clutter_transition_set_from (transition, G_TYPE_UINT, 0); * clutter_transition_set_to (transition, G_TYPE_UINT, 255); * * // over a one second duration, running an infinite amount of times * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 1000); * clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), -1); * * // we want to fade in and out, so we need to auto-reverse the transition * clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE); * * // and we want to use an easing function that eases both in and out * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), * CLUTTER_EASE_IN_OUT_CUBIC); * * // add the transition to the desired actor to start it * clutter_actor_add_transition (actor, "opacityAnimation", transition); * ]| * * ## Implementing an actor ## {#clutter-actor-implementing} * * Careful consideration should be given when deciding to implement * a #ClutterActor sub-class. It is generally recommended to implement a * sub-class of #ClutterActor only for actors that should be used as leaf * nodes of a scene graph. * * If your actor should be painted in a custom way, you should * override the #ClutterActor::paint signal class handler. You can either * opt to chain up to the parent class implementation or decide to fully * override the default paint implementation; Clutter will set up the * transformations and clip regions prior to emitting the #ClutterActor::paint * signal. * * By overriding the #ClutterActorClass.get_preferred_width() and * #ClutterActorClass.get_preferred_height() virtual functions it is * possible to change or provide the preferred size of an actor; similarly, * by overriding the #ClutterActorClass.allocate() virtual function it is * possible to control the layout of the children of an actor. Make sure to * always chain up to the parent implementation of the * #ClutterActorClass.allocate() virtual function. * * In general, it is strongly encouraged to use delegation and composition * instead of direct subclassing. * * ## ClutterActor custom properties for ClutterScript ## {#clutter-actor-custom-script} * * #ClutterActor defines a custom "rotation" property which allows a short-hand * description of the rotations to be applied to an actor. * * The syntax of the "rotation" property is the following: * * |[ * "rotation" : [ { "" : [ , [ ] ] } ] * ]| * * where: * * - axis is the name of an enumeration value of type #ClutterRotateAxis * - angle is a floating point value representing the rotation angle on the given axis in degrees * - center-point is an optional array, and if present it must contain the center of rotation as described by two coordinates: * - Y and Z for "x-axis" * - X and Z for "y-axis" * - X and Y for "z-axis". * * #ClutterActor also defines a scriptable "margin" property which follows the CSS "margin" shorthand. * * |[ * // 4 values * "margin" : [ top, right, bottom, left ] * // 3 values * "margin" : [ top, left/right, bottom ] * // 2 values * "margin" : [ top/bottom, left/right ] * // 1 value * "margin" : [ top/right/bottom/left ] * ]| * * #ClutterActor will also parse every positional and dimensional * property defined as a string through clutter_units_from_string(); you * should read the documentation for the #ClutterUnits parser format for * the valid units and syntax. * * ## Custom animatable properties * * #ClutterActor allows accessing properties of #ClutterAction, * #ClutterEffect, and #ClutterConstraint instances associated to an actor * instance for animation purposes, as well as its #ClutterLayoutManager. * * In order to access a specific #ClutterAction or a #ClutterConstraint * property it is necessary to set the #ClutterActorMeta:name property on the * given action or constraint. * * The property can be accessed using the following syntax: * * |[ * @
.. * ]| * * - the initial `@` is mandatory * - the `section` fragment can be one between "actions", "constraints" and "effects" * - the `meta-name` fragment is the name of the action, effect, or constraint, as * specified by the #ClutterActorMeta:name property of #ClutterActorMeta * - the `property-name` fragment is the name of the action, effect, or constraint * property to be animated. * * The example below animates a #ClutterBindConstraint applied to an actor * using an explicit transition. The `rect` actor has a binding constraint * on the `origin` actor, and in its initial state is overlapping the actor * to which is bound to. * * As the actor has only one #ClutterLayoutManager, the syntax for accessing its * properties is simpler: * * |[ * @layout. * ]| * * |[ * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_X, 0.0); * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-x"); * clutter_actor_add_constraint (rect, constraint); * * constraint = clutter_bind_constraint_new (origin, CLUTTER_BIND_Y, 0.0); * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), "bind-y"); * clutter_actor_add_constraint (rect, constraint); * * clutter_actor_set_reactive (origin, TRUE); * * g_signal_connect (origin, "button-press-event", * G_CALLBACK (on_button_press), * rect); * ]| * * On button press, the rectangle "slides" from behind the actor to * which is bound to, using the #ClutterBindConstraint:offset property to * achieve the effect: * * |[ * gboolean * on_button_press (ClutterActor *origin, * ClutterEvent *event, * ClutterActor *rect) * { * ClutterTransition *transition; * * // the offset that we want to apply; this will make the actor * // slide in from behind the origin and rest at the right of * // the origin, plus a padding value * float new_offset = clutter_actor_get_width (origin) + h_padding; * * // the property we wish to animate; the "@constraints" section * // tells Clutter to check inside the constraints associated * // with the actor; the "bind-x" section is the name of the * // constraint; and the "offset" is the name of the property * // on the constraint * const char *prop = "@constraints.bind-x.offset"; * * // create a new transition for the given property * transition = clutter_property_transition_new (prop); * * // set the easing mode and duration * clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), * CLUTTER_EASE_OUT_CUBIC); * clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), 500); * * // create the interval with the initial and final values * clutter_transition_set_from (transition, G_TYPE_FLOAT, 0.f); * clutter_transition_set_to (transition, G_TYPE_FLOAT, new_offset); * * // add the transition to the actor; this causes the animation * // to start. the name "offsetAnimation" can be used to retrieve * // the transition later * clutter_actor_add_transition (rect, "offsetAnimation", transition); * * // we handled the event * return CLUTTER_EVENT_STOP; * } * ]| */ /** * CLUTTER_ACTOR_IS_MAPPED: * @a: a #ClutterActor * * Evaluates to %TRUE if the %CLUTTER_ACTOR_MAPPED flag is set. * * The mapped state is set when the actor is visible and all its parents up * to a top-level (e.g. a #ClutterStage) are visible, realized, and mapped. * * This check can be used to see if an actor is going to be painted, as only * actors with the %CLUTTER_ACTOR_MAPPED flag set are going to be painted. * * The %CLUTTER_ACTOR_MAPPED flag is managed by Clutter itself, and it should * not be checked directly; instead, the recommended usage is to connect a * handler on the #GObject::notify signal for the #ClutterActor:mapped * property of #ClutterActor, and check the presence of * the %CLUTTER_ACTOR_MAPPED flag on state changes. * * It is also important to note that Clutter may delay the changes of * the %CLUTTER_ACTOR_MAPPED flag on top-levels due to backend-specific * limitations, or during the reparenting of an actor, to optimize * unnecessary (and potentially expensive) state changes. * * Since: 0.2 * * Deprecated: 1.24: Use clutter_actor_is_mapped() or the #ClutterActor:mapped * property instead of this macro. */ /** * CLUTTER_ACTOR_IS_REALIZED: * @a: a #ClutterActor * * Evaluates to %TRUE if the %CLUTTER_ACTOR_REALIZED flag is set. * * The realized state has an actor-dependant interpretation. If an * actor wants to delay allocating resources until it is attached to a * stage, it may use the realize state to do so. However it is * perfectly acceptable for an actor to allocate Cogl resources before * being realized because there is only one drawing context used by Clutter * so any resources will work on any stage. If an actor is mapped it * must also be realized, but an actor can be realized and unmapped * (this is so hiding an actor temporarily doesn't do an expensive * unrealize/realize). * * To be realized an actor must be inside a stage, and all its parents * must be realized. * * Since: 0.2 * * Deprecated: 1.24: Use clutter_actor_is_realized() or the #ClutterActor:realized * property instead of this macro. */ /** * CLUTTER_ACTOR_IS_VISIBLE: * @a: a #ClutterActor * * Evaluates to %TRUE if the actor has been shown, %FALSE if it's hidden. * Equivalent to the ClutterActor::visible object property. * * Note that an actor is only painted onscreen if it's mapped, which * means it's visible, and all its parents are visible, and one of the * parents is a toplevel stage; see also %CLUTTER_ACTOR_IS_MAPPED. * * Since: 0.2 * * Deprecated: 1.24: Use clutter_actor_is_visible() or the #ClutterActor:visible * property instead of this macro. */ /** * CLUTTER_ACTOR_IS_REACTIVE: * @a: a #ClutterActor * * Evaluates to %TRUE if the %CLUTTER_ACTOR_REACTIVE flag is set. * * Only reactive actors will receive event-related signals. * * Since: 0.6 * * Deprecated: 1.24: Use clutter_actor_get_reactive() or the * #ClutterActor:reactive property instead of this macro. */ #include "clutter-build-config.h" #include #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-actor-private.h" #include "clutter-action.h" #include "clutter-actor-meta-private.h" #include "clutter-animatable.h" #include "clutter-color-static.h" #include "clutter-color.h" #include "clutter-constraint-private.h" #include "clutter-container-private.h" #include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-easing.h" #include "clutter-effect-private.h" #include "clutter-enum-types.h" #include "clutter-fixed-layout.h" #include "clutter-flatten-effect.h" #include "clutter-interval.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-mutter.h" #include "clutter-paint-context-private.h" #include "clutter-paint-nodes.h" #include "clutter-paint-node-private.h" #include "clutter-paint-volume-private.h" #include "clutter-private.h" #include "clutter-property-transition.h" #include "clutter-scriptable.h" #include "clutter-script-private.h" #include "clutter-stage-private.h" #include "clutter-timeline.h" #include "clutter-transition.h" #include "clutter-units.h" #include "deprecated/clutter-actor.h" #include "deprecated/clutter-container.h" /* Internal enum used to control mapped state update. This is a hint * which indicates when to do something other than just enforce * invariants. */ typedef enum { MAP_STATE_CHECK, /* just enforce invariants. */ MAP_STATE_MAKE_UNREALIZED, /* force unrealize, ignoring invariants, * used when about to unparent. */ MAP_STATE_MAKE_MAPPED, /* set mapped, error if invariants not met; * used to set mapped on toplevels. */ MAP_STATE_MAKE_UNMAPPED /* set unmapped, even if parent is mapped, * used just before unmapping parent. */ } MapStateChange; /* 3 entries should be a good compromise, few layout managers * will ask for 3 different preferred size in each allocation cycle */ #define N_CACHED_SIZE_REQUESTS 3 struct _ClutterActorPrivate { /* request mode */ ClutterRequestMode request_mode; /* our cached size requests for different width / height */ SizeRequest width_requests[N_CACHED_SIZE_REQUESTS]; SizeRequest height_requests[N_CACHED_SIZE_REQUESTS]; /* An age of 0 means the entry is not set */ guint cached_height_age; guint cached_width_age; /* the bounding box of the actor, relative to the parent's * allocation */ ClutterActorBox allocation; ClutterAllocationFlags allocation_flags; /* clip, in actor coordinates */ graphene_rect_t clip; /* the cached transformation matrix; see apply_transform() */ CoglMatrix transform; float resource_scale; guint8 opacity; gint opacity_override; unsigned int inhibit_culling_counter; ClutterOffscreenRedirect offscreen_redirect; /* This is an internal effect used to implement the offscreen-redirect property */ ClutterEffect *flatten_effect; /* scene graph */ ClutterActor *parent; ClutterActor *prev_sibling; ClutterActor *next_sibling; ClutterActor *first_child; ClutterActor *last_child; gint n_children; /* tracks whenever the children of an actor are changed; the * age is incremented by 1 whenever an actor is added or * removed. the age is not incremented when the first or the * last child pointers are changed, or when grandchildren of * an actor are changed. */ gint age; gchar *name; /* a non-unique name, used for debugging */ /* a back-pointer to the Pango context that we can use * to create pre-configured PangoLayout */ PangoContext *pango_context; /* the text direction configured for this child - either by * application code, or by the actor's parent */ ClutterTextDirection text_direction; /* meta classes */ ClutterMetaGroup *actions; ClutterMetaGroup *constraints; ClutterMetaGroup *effects; /* delegate object used to allocate the children of this actor */ ClutterLayoutManager *layout_manager; /* delegate object used to paint the contents of this actor */ ClutterContent *content; ClutterActorBox content_box; ClutterContentGravity content_gravity; ClutterScalingFilter min_filter; ClutterScalingFilter mag_filter; ClutterContentRepeat content_repeat; /* used when painting, to update the paint volume */ ClutterEffect *current_effect; /* This is used to store an effect which needs to be redrawn. A redraw can be queued to start from a particular effect. This is used by parametrised effects that can cache an image of the actor. If a parameter of the effect changes then it only needs to redraw the cached image, not the actual actor. The pointer is only valid if is_dirty == TRUE. If the pointer is NULL then the whole actor is dirty. */ ClutterEffect *effect_to_redraw; /* This is used when painting effects to implement the clutter_actor_continue_paint() function. It points to the node in the list of effects that is next in the chain */ const GList *next_effect_to_paint; ClutterPaintVolume paint_volume; /* NB: This volume isn't relative to this actor, it is in eye * coordinates so that it can remain valid after the actor changes. */ ClutterPaintVolume last_paint_volume; ClutterStageQueueRedrawEntry *queue_redraw_entry; ClutterColor bg_color; #ifdef CLUTTER_ENABLE_DEBUG /* a string used for debugging messages */ gchar *debug_name; #endif /* a set of clones of the actor */ GHashTable *clones; /* whether the actor is inside a cloned branch; this * value is propagated to all the actor's children */ gulong in_cloned_branch; GListModel *child_model; ClutterActorCreateChildFunc create_child_func; gpointer create_child_data; GDestroyNotify create_child_notify; gulong resolution_changed_id; gulong font_changed_id; /* bitfields: KEEP AT THE END */ /* fixed position and sizes */ guint position_set : 1; guint min_width_set : 1; guint min_height_set : 1; guint natural_width_set : 1; guint natural_height_set : 1; /* cached request is invalid (implies allocation is too) */ guint needs_width_request : 1; /* cached request is invalid (implies allocation is too) */ guint needs_height_request : 1; /* cached allocation is invalid (request has changed, probably) */ guint needs_allocation : 1; guint show_on_set_parent : 1; guint has_clip : 1; guint clip_to_allocation : 1; guint enable_model_view_transform : 1; guint enable_paint_unmapped : 1; guint has_pointer : 1; guint has_key_focus : 1; guint propagated_one_redraw : 1; guint paint_volume_valid : 1; guint last_paint_volume_valid : 1; guint in_clone_paint : 1; guint transform_valid : 1; /* This is TRUE if anything has queued a redraw since we were last painted. In this case effect_to_redraw will point to an effect the redraw was queued from or it will be NULL if the redraw was queued without an effect. */ guint is_dirty : 1; guint bg_color_set : 1; guint content_box_valid : 1; guint x_expand_set : 1; guint y_expand_set : 1; guint needs_compute_expand : 1; guint needs_x_expand : 1; guint needs_y_expand : 1; guint needs_paint_volume_update : 1; guint had_effects_on_last_paint_volume_update : 1; guint needs_compute_resource_scale : 1; }; enum { PROP_0, PROP_NAME, /* X, Y, WIDTH, HEIGHT are "do what I mean" properties; * when set they force a size request, when gotten they * get the allocation if the allocation is valid, and the * request otherwise */ PROP_X, PROP_Y, PROP_WIDTH, PROP_HEIGHT, PROP_POSITION, PROP_SIZE, /* Then the rest of these size-related properties are the "actual" * underlying properties set or gotten by X, Y, WIDTH, HEIGHT */ PROP_FIXED_X, PROP_FIXED_Y, PROP_FIXED_POSITION_SET, PROP_MIN_WIDTH, PROP_MIN_WIDTH_SET, PROP_MIN_HEIGHT, PROP_MIN_HEIGHT_SET, PROP_NATURAL_WIDTH, PROP_NATURAL_WIDTH_SET, PROP_NATURAL_HEIGHT, PROP_NATURAL_HEIGHT_SET, PROP_REQUEST_MODE, /* Allocation properties are read-only */ PROP_ALLOCATION, PROP_DEPTH, /* XXX:2.0 remove */ PROP_Z_POSITION, PROP_CLIP_RECT, PROP_HAS_CLIP, PROP_CLIP_TO_ALLOCATION, PROP_OPACITY, PROP_OFFSCREEN_REDIRECT, PROP_VISIBLE, PROP_MAPPED, PROP_REALIZED, PROP_REACTIVE, PROP_PIVOT_POINT, PROP_PIVOT_POINT_Z, PROP_SCALE_X, PROP_SCALE_Y, PROP_SCALE_Z, PROP_SCALE_CENTER_X, /* XXX:2.0 remove */ PROP_SCALE_CENTER_Y, /* XXX:2.0 remove */ PROP_SCALE_GRAVITY, /* XXX:2.0 remove */ PROP_RESOURCE_SCALE, PROP_ROTATION_ANGLE_X, /* XXX:2.0 rename to rotation-x */ PROP_ROTATION_ANGLE_Y, /* XXX:2.0 rename to rotation-y */ PROP_ROTATION_ANGLE_Z, /* XXX:2.0 rename to rotation-z */ PROP_ROTATION_CENTER_X, /* XXX:2.0 remove */ PROP_ROTATION_CENTER_Y, /* XXX:2.0 remove */ PROP_ROTATION_CENTER_Z, /* XXX:2.0 remove */ /* This property only makes sense for the z rotation because the others would depend on the actor having a size along the z-axis */ PROP_ROTATION_CENTER_Z_GRAVITY, /* XXX:2.0 remove */ PROP_ANCHOR_X, /* XXX:2.0 remove */ PROP_ANCHOR_Y, /* XXX:2.0 remove */ PROP_ANCHOR_GRAVITY, /*XXX:2.0 remove */ PROP_TRANSLATION_X, PROP_TRANSLATION_Y, PROP_TRANSLATION_Z, PROP_TRANSFORM, PROP_TRANSFORM_SET, PROP_CHILD_TRANSFORM, PROP_CHILD_TRANSFORM_SET, PROP_SHOW_ON_SET_PARENT, /*XXX:2.0 remove */ PROP_TEXT_DIRECTION, PROP_HAS_POINTER, PROP_ACTIONS, PROP_CONSTRAINTS, PROP_EFFECT, PROP_LAYOUT_MANAGER, PROP_X_EXPAND, PROP_Y_EXPAND, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_MARGIN_TOP, PROP_MARGIN_BOTTOM, PROP_MARGIN_LEFT, PROP_MARGIN_RIGHT, PROP_BACKGROUND_COLOR, PROP_BACKGROUND_COLOR_SET, PROP_FIRST_CHILD, PROP_LAST_CHILD, PROP_CONTENT, PROP_CONTENT_GRAVITY, PROP_CONTENT_BOX, PROP_MINIFICATION_FILTER, PROP_MAGNIFICATION_FILTER, PROP_CONTENT_REPEAT, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { SHOW, HIDE, DESTROY, PARENT_SET, KEY_FOCUS_IN, KEY_FOCUS_OUT, PAINT, PICK, REALIZE, UNREALIZE, QUEUE_REDRAW, QUEUE_RELAYOUT, EVENT, CAPTURED_EVENT, BUTTON_PRESS_EVENT, BUTTON_RELEASE_EVENT, SCROLL_EVENT, KEY_PRESS_EVENT, KEY_RELEASE_EVENT, MOTION_EVENT, ENTER_EVENT, LEAVE_EVENT, ALLOCATION_CHANGED, TRANSITIONS_COMPLETED, TOUCH_EVENT, TRANSITION_STOPPED, LAST_SIGNAL }; static guint actor_signals[LAST_SIGNAL] = { 0, }; typedef struct _TransitionClosure { ClutterActor *actor; ClutterTransition *transition; gchar *name; gulong completed_id; } TransitionClosure; static void clutter_container_iface_init (ClutterContainerIface *iface); static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface); static void atk_implementor_iface_init (AtkImplementorIface *iface); /* These setters are all static for now, maybe they should be in the * public API, but they are perhaps obscure enough to leave only as * properties */ static void clutter_actor_set_min_width (ClutterActor *self, gfloat min_width); static void clutter_actor_set_min_height (ClutterActor *self, gfloat min_height); static void clutter_actor_set_natural_width (ClutterActor *self, gfloat natural_width); static void clutter_actor_set_natural_height (ClutterActor *self, gfloat natural_height); static void clutter_actor_set_min_width_set (ClutterActor *self, gboolean use_min_width); static void clutter_actor_set_min_height_set (ClutterActor *self, gboolean use_min_height); static void clutter_actor_set_natural_width_set (ClutterActor *self, gboolean use_natural_width); static void clutter_actor_set_natural_height_set (ClutterActor *self, gboolean use_natural_height); static void clutter_actor_update_map_state (ClutterActor *self, MapStateChange change); static void clutter_actor_unrealize_not_hiding (ClutterActor *self); /* Helper routines for managing anchor coords */ static void clutter_anchor_coord_get_units (ClutterActor *self, const AnchorCoord *coord, gfloat *x, gfloat *y, gfloat *z); static void clutter_anchor_coord_set_units (AnchorCoord *coord, gfloat x, gfloat y, gfloat z); static ClutterGravity clutter_anchor_coord_get_gravity (const AnchorCoord *coord); static void clutter_anchor_coord_set_gravity (AnchorCoord *coord, ClutterGravity gravity); static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord); static void _clutter_actor_get_relative_transformation_matrix (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix); static ClutterPaintVolume *_clutter_actor_get_paint_volume_mutable (ClutterActor *self); static guint8 clutter_actor_get_paint_opacity_internal (ClutterActor *self); static inline void clutter_actor_set_background_color_internal (ClutterActor *self, const ClutterColor *color); static void on_layout_manager_changed (ClutterLayoutManager *manager, ClutterActor *self); static inline void clutter_actor_queue_compute_expand (ClutterActor *self); static inline void clutter_actor_set_margin_internal (ClutterActor *self, gfloat margin, GParamSpec *pspec); static void clutter_actor_set_transform_internal (ClutterActor *self, const ClutterMatrix *transform); static void clutter_actor_set_child_transform_internal (ClutterActor *self, const ClutterMatrix *transform); static void clutter_actor_realize_internal (ClutterActor *self); static void clutter_actor_unrealize_internal (ClutterActor *self); static gboolean clutter_actor_update_resource_scale (ClutterActor *self); static void clutter_actor_ensure_resource_scale (ClutterActor *self); static void clutter_actor_push_in_cloned_branch (ClutterActor *self, gulong count); static void clutter_actor_pop_in_cloned_branch (ClutterActor *self, gulong count); /* Helper macro which translates by the anchor coord, applies the given transformation and then translates back */ #define TRANSFORM_ABOUT_ANCHOR_COORD(a,m,c,_transform) G_STMT_START { \ gfloat _tx, _ty, _tz; \ clutter_anchor_coord_get_units ((a), (c), &_tx, &_ty, &_tz); \ cogl_matrix_translate ((m), _tx, _ty, _tz); \ { _transform; } \ cogl_matrix_translate ((m), -_tx, -_ty, -_tz); } G_STMT_END static GQuark quark_actor_layout_info = 0; static GQuark quark_actor_transform_info = 0; static GQuark quark_actor_animation_info = 0; static GQuark quark_key = 0; static GQuark quark_motion = 0; static GQuark quark_pointer_focus = 0; static GQuark quark_button = 0; static GQuark quark_scroll = 0; static GQuark quark_stage = 0; static GQuark quark_destroy = 0; static GQuark quark_client = 0; static GQuark quark_delete = 0; static GQuark quark_touch = 0; static GQuark quark_touchpad = 0; static GQuark quark_proximity = 0; static GQuark quark_pad = 0; static GQuark quark_im = 0; G_DEFINE_TYPE_WITH_CODE (ClutterActor, clutter_actor, G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (ClutterActor) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE, clutter_animatable_iface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_IMPLEMENTOR, atk_implementor_iface_init)); /*< private > * clutter_actor_get_debug_name: * @actor: a #ClutterActor * * Retrieves a printable name of @actor for debugging messages * * Return value: a string with a printable name */ const gchar * _clutter_actor_get_debug_name (ClutterActor *actor) { ClutterActorPrivate *priv = actor->priv; const gchar *retval; #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (priv->debug_name == NULL)) { priv->debug_name = g_strdup_printf ("<%s>[<%s>:%p]", priv->name != NULL ? priv->name : "unnamed", G_OBJECT_TYPE_NAME (actor), actor); } retval = priv->debug_name; #else retval = priv->name != NULL ? priv->name : G_OBJECT_TYPE_NAME (actor); #endif return retval; } #ifdef CLUTTER_ENABLE_DEBUG /* XXX - this is for debugging only, remove once working (or leave * in only in some debug mode). Should leave it for a little while * until we're confident in the new map/realize/visible handling. */ static inline void clutter_actor_verify_map_state (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (CLUTTER_ACTOR_IS_REALIZED (self)) { /* all bets are off during reparent when we're potentially realized, * but should not be according to invariants */ if (!CLUTTER_ACTOR_IN_REPARENT (self)) { if (priv->parent == NULL) { if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) { } else g_warning ("Realized non-toplevel actor '%s' should " "have a parent", _clutter_actor_get_debug_name (self)); } else if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent)) { g_warning ("Realized actor %s has an unrealized parent %s", _clutter_actor_get_debug_name (self), _clutter_actor_get_debug_name (priv->parent)); } } } if (CLUTTER_ACTOR_IS_MAPPED (self)) { if (!CLUTTER_ACTOR_IS_REALIZED (self)) g_warning ("Actor '%s' is mapped but not realized", _clutter_actor_get_debug_name (self)); /* remaining bets are off during reparent when we're potentially * mapped, but should not be according to invariants */ if (!CLUTTER_ACTOR_IN_REPARENT (self)) { if (priv->parent == NULL) { if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) { if (!CLUTTER_ACTOR_IS_VISIBLE (self) && !CLUTTER_ACTOR_IN_DESTRUCTION (self)) { g_warning ("Toplevel actor '%s' is mapped " "but not visible", _clutter_actor_get_debug_name (self)); } } else { g_warning ("Mapped actor '%s' should have a parent", _clutter_actor_get_debug_name (self)); } } else { ClutterActor *iter = self; /* check for the enable_paint_unmapped flag on the actor * and parents; if the flag is enabled at any point of this * branch of the scene graph then all the later checks * become pointless */ while (iter != NULL) { if (iter->priv->enable_paint_unmapped) return; iter = iter->priv->parent; } if (!CLUTTER_ACTOR_IS_VISIBLE (priv->parent)) { g_warning ("Actor '%s' should not be mapped if parent '%s'" "is not visible", _clutter_actor_get_debug_name (self), _clutter_actor_get_debug_name (priv->parent)); } if (!CLUTTER_ACTOR_IS_REALIZED (priv->parent)) { g_warning ("Actor '%s' should not be mapped if parent '%s'" "is not realized", _clutter_actor_get_debug_name (self), _clutter_actor_get_debug_name (priv->parent)); } if (!CLUTTER_ACTOR_IS_TOPLEVEL (priv->parent)) { if (!CLUTTER_ACTOR_IS_MAPPED (priv->parent)) g_warning ("Actor '%s' is mapped but its non-toplevel " "parent '%s' is not mapped", _clutter_actor_get_debug_name (self), _clutter_actor_get_debug_name (priv->parent)); } } } } } #endif /* CLUTTER_ENABLE_DEBUG */ static gboolean _clutter_actor_transform_local_box_to_stage (ClutterActor *self, ClutterStage *stage, ClutterPickContext *pick_context, const ClutterActorBox *box, graphene_point_t vertices[4]) { CoglFramebuffer *fb = clutter_pick_context_get_framebuffer (pick_context); CoglMatrix stage_transform, inv_stage_transform; CoglMatrix modelview, transform_to_stage; int v; clutter_actor_get_transform (CLUTTER_ACTOR (stage), &stage_transform); if (!cogl_matrix_get_inverse (&stage_transform, &inv_stage_transform)) return FALSE; cogl_framebuffer_get_modelview_matrix (fb, &modelview); cogl_matrix_multiply (&transform_to_stage, &inv_stage_transform, &modelview); vertices[0].x = box->x1; vertices[0].y = box->y1; vertices[1].x = box->x2; vertices[1].y = box->y1; vertices[2].x = box->x2; vertices[2].y = box->y2; vertices[3].x = box->x1; vertices[3].y = box->y2; for (v = 0; v < 4; v++) { float z = 0.f; float w = 1.f; cogl_matrix_transform_point (&transform_to_stage, &vertices[v].x, &vertices[v].y, &z, &w); } return TRUE; } /** * clutter_actor_pick_box: * @self: The #ClutterActor being "pick" painted. * @pick_context: The #ClutterPickContext * @box: A rectangle in the actor's own local coordinates. * * Logs (does a virtual paint of) a rectangle for picking. Note that @box is * in the actor's own local coordinates, so is usually {0,0,width,height} * to include the whole actor. That is unless the actor has a shaped input * region in which case you may wish to log the (multiple) smaller rectangles * that make up the input region. */ void clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box) { ClutterStage *stage; graphene_point_t vertices[4]; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); if (box->x1 >= box->x2 || box->y1 >= box->y2) return; stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); if (_clutter_actor_transform_local_box_to_stage (self, stage, pick_context, box, vertices)) clutter_stage_log_pick (stage, vertices, self); } static gboolean _clutter_actor_push_pick_clip (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *clip) { ClutterStage *stage; graphene_point_t vertices[4]; stage = CLUTTER_STAGE (_clutter_actor_get_stage_internal (self)); if (!_clutter_actor_transform_local_box_to_stage (self, stage, pick_context, clip, vertices)) return FALSE; clutter_stage_push_pick_clip (stage, vertices); return TRUE; } static void _clutter_actor_pop_pick_clip (ClutterActor *self) { ClutterActor *stage; stage = _clutter_actor_get_stage_internal (self); clutter_stage_pop_pick_clip (CLUTTER_STAGE (stage)); } static void clutter_actor_set_mapped (ClutterActor *self, gboolean mapped) { if (CLUTTER_ACTOR_IS_MAPPED (self) == mapped) return; g_return_if_fail (!CLUTTER_ACTOR_IN_MAP_UNMAP (self)); CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_MAP_UNMAP); if (mapped) { CLUTTER_ACTOR_GET_CLASS (self)->map (self); g_assert (CLUTTER_ACTOR_IS_MAPPED (self)); } else { CLUTTER_ACTOR_GET_CLASS (self)->unmap (self); g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); } CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_MAP_UNMAP); } /* this function updates the mapped and realized states according to * invariants, in the appropriate order. */ static void clutter_actor_update_map_state (ClutterActor *self, MapStateChange change) { gboolean was_mapped; was_mapped = CLUTTER_ACTOR_IS_MAPPED (self); if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) { /* the mapped flag on top-level actors must be set by the * per-backend implementation because it might be asynchronous. * * That is, the MAPPED flag on toplevels currently tracks the X * server mapped-ness of the window, while the expected behavior * (if used to GTK) may be to track WM_STATE!=WithdrawnState. * This creates some weird complexity by breaking the invariant * that if we're visible and all ancestors shown then we are * also mapped - instead, we are mapped if all ancestors * _possibly excepting_ the stage are mapped. The stage * will map/unmap for example when it is minimized or * moved to another workspace. * * So, the only invariant on the stage is that if visible it * should be realized, and that it has to be visible to be * mapped. */ if (CLUTTER_ACTOR_IS_VISIBLE (self)) clutter_actor_realize (self); switch (change) { case MAP_STATE_CHECK: break; case MAP_STATE_MAKE_MAPPED: g_assert (!was_mapped); clutter_actor_set_mapped (self, TRUE); break; case MAP_STATE_MAKE_UNMAPPED: g_assert (was_mapped); clutter_actor_set_mapped (self, FALSE); break; case MAP_STATE_MAKE_UNREALIZED: /* we only use MAKE_UNREALIZED in unparent, * and unparenting a stage isn't possible. * If someone wants to just unrealize a stage * then clutter_actor_unrealize() doesn't * go through this codepath. */ g_warning ("Trying to force unrealize stage is not allowed"); break; } if (CLUTTER_ACTOR_IS_MAPPED (self) && !CLUTTER_ACTOR_IS_VISIBLE (self) && !CLUTTER_ACTOR_IN_DESTRUCTION (self)) { g_warning ("Clutter toplevel of type '%s' is not visible, but " "it is somehow still mapped", _clutter_actor_get_debug_name (self)); } } else { ClutterActorPrivate *priv = self->priv; ClutterActor *parent = priv->parent; gboolean should_be_mapped; gboolean may_be_realized; gboolean must_be_realized; should_be_mapped = FALSE; may_be_realized = TRUE; must_be_realized = FALSE; if (parent == NULL || change == MAP_STATE_MAKE_UNREALIZED) { may_be_realized = FALSE; } else { /* Maintain invariant that if parent is mapped, and we are * visible, then we are mapped ... unless parent is a * stage, in which case we map regardless of parent's map * state but do require stage to be visible and realized. * * If parent is realized, that does not force us to be * realized; but if parent is unrealized, that does force * us to be unrealized. * * The reason we don't force children to realize with * parents is _clutter_actor_rerealize(); if we require that * a realized parent means children are realized, then to * unrealize an actor we would have to unrealize its * parents, which would end up meaning unrealizing and * hiding the entire stage. So we allow unrealizing a * child (as long as that child is not mapped) while that * child still has a realized parent. * * Also, if we unrealize from leaf nodes to root, and * realize from root to leaf, the invariants are never * violated if we allow children to be unrealized * while parents are realized. * * When unmapping, MAP_STATE_MAKE_UNMAPPED is specified * to force us to unmap, even though parent is still * mapped. This is because we're unmapping from leaf nodes * up to root nodes. */ if (CLUTTER_ACTOR_IS_VISIBLE (self) && change != MAP_STATE_MAKE_UNMAPPED) { gboolean parent_is_visible_realized_toplevel; parent_is_visible_realized_toplevel = (CLUTTER_ACTOR_IS_TOPLEVEL (parent) && CLUTTER_ACTOR_IS_VISIBLE (parent) && CLUTTER_ACTOR_IS_REALIZED (parent)); if (CLUTTER_ACTOR_IS_MAPPED (parent) || parent_is_visible_realized_toplevel) { must_be_realized = TRUE; should_be_mapped = TRUE; } } /* if the actor has been set to be painted even if unmapped * then we should map it and check for realization as well; * this is an override for the branch of the scene graph * which begins with this node */ if (priv->enable_paint_unmapped) { should_be_mapped = TRUE; must_be_realized = TRUE; } if (!CLUTTER_ACTOR_IS_REALIZED (parent)) may_be_realized = FALSE; } if (change == MAP_STATE_MAKE_MAPPED && !should_be_mapped) { if (parent == NULL) g_warning ("Attempting to map a child that does not " "meet the necessary invariants: the actor '%s' " "has no parent", _clutter_actor_get_debug_name (self)); else g_warning ("Attempting to map a child that does not " "meet the necessary invariants: the actor '%s' " "is parented to an unmapped actor '%s'", _clutter_actor_get_debug_name (self), _clutter_actor_get_debug_name (priv->parent)); } /* If in reparent, we temporarily suspend unmap and unrealize. * * We want to go in the order "realize, map" and "unmap, unrealize" */ /* Unmap */ if (!should_be_mapped && !CLUTTER_ACTOR_IN_REPARENT (self)) clutter_actor_set_mapped (self, FALSE); /* Realize */ if (must_be_realized) clutter_actor_realize (self); /* if we must be realized then we may be, presumably */ g_assert (!(must_be_realized && !may_be_realized)); /* Unrealize */ if (!may_be_realized && !CLUTTER_ACTOR_IN_REPARENT (self)) clutter_actor_unrealize_not_hiding (self); /* Map */ if (should_be_mapped) { if (!must_be_realized) g_warning ("Somehow we think actor '%s' should be mapped but " "not realized, which isn't allowed", _clutter_actor_get_debug_name (self)); /* realization is allowed to fail (though I don't know what * an app is supposed to do about that - shouldn't it just * be a g_error? anyway, we have to avoid mapping if this * happens) */ if (CLUTTER_ACTOR_IS_REALIZED (self)) clutter_actor_set_mapped (self, TRUE); } } #ifdef CLUTTER_ENABLE_DEBUG /* check all invariants were kept */ clutter_actor_verify_map_state (self); #endif } static void clutter_actor_real_map (ClutterActor *self) { ClutterActor *iter; g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); CLUTTER_NOTE (ACTOR, "Mapping actor '%s'", _clutter_actor_get_debug_name (self)); CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_MAPPED); self->priv->needs_paint_volume_update = TRUE; clutter_actor_ensure_resource_scale (self); /* notify on parent mapped before potentially mapping * children, so apps see a top-down notification. */ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]); for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { clutter_actor_map (iter); } } /** * clutter_actor_map: * @self: A #ClutterActor * * Sets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly maps * and realizes its children if they are visible. Does nothing if the * actor is not visible. * * Calling this function is strongly disencouraged: the default * implementation of #ClutterActorClass.map() will map all the children * of an actor when mapping its parent. * * When overriding map, it is mandatory to chain up to the parent * implementation. * * Since: 1.0 */ void clutter_actor_map (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (CLUTTER_ACTOR_IS_MAPPED (self)) return; if (!CLUTTER_ACTOR_IS_VISIBLE (self)) return; clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED); } /** * clutter_actor_is_mapped: * @self: a #ClutterActor * * Checks whether a #ClutterActor has been set as mapped. * * See also %CLUTTER_ACTOR_IS_MAPPED and #ClutterActor:mapped * * Returns: %TRUE if the actor is mapped * * Since: 1.24 */ gboolean clutter_actor_is_mapped (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return CLUTTER_ACTOR_IS_MAPPED (self); } static void maybe_unset_key_focus (ClutterActor *self) { ClutterActor *stage; if (!self->priv->has_key_focus) return; stage = _clutter_actor_get_stage_internal (self); if (stage) clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL); } static void clutter_actor_real_unmap (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; ClutterActor *iter; g_assert (CLUTTER_ACTOR_IS_MAPPED (self)); CLUTTER_NOTE (ACTOR, "Unmapping actor '%s'", _clutter_actor_get_debug_name (self)); for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { clutter_actor_unmap (iter); } CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_MAPPED); /* clear the contents of the last paint volume, so that hiding + moving + * showing will not result in the wrong area being repainted */ _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL); priv->last_paint_volume_valid = TRUE; /* notify on parent mapped after potentially unmapping * children, so apps see a bottom-up notification. */ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAPPED]); /* relinquish keyboard focus if we were unmapped while owning it */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) maybe_unset_key_focus (self); } /** * clutter_actor_unmap: * @self: A #ClutterActor * * Unsets the %CLUTTER_ACTOR_MAPPED flag on the actor and possibly * unmaps its children if they were mapped. * * Calling this function is not encouraged: the default #ClutterActor * implementation of #ClutterActorClass.unmap() will also unmap any * eventual children by default when their parent is unmapped. * * When overriding #ClutterActorClass.unmap(), it is mandatory to * chain up to the parent implementation. * * It is important to note that the implementation of the * #ClutterActorClass.unmap() virtual function may be called after * the #ClutterActorClass.destroy() or the #GObjectClass.dispose() * implementation, but it is guaranteed to be called before the * #GObjectClass.finalize() implementation. * * Since: 1.0 */ void clutter_actor_unmap (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (!CLUTTER_ACTOR_IS_MAPPED (self)) return; clutter_actor_update_map_state (self, MAP_STATE_MAKE_UNMAPPED); } static void clutter_actor_queue_shallow_relayout (ClutterActor *self) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); if (stage != NULL) clutter_stage_queue_actor_relayout (CLUTTER_STAGE (stage), self); } static void clutter_actor_real_show (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (CLUTTER_ACTOR_IS_VISIBLE (self)) return; CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); /* we notify on the "visible" flag in the clutter_actor_show() * wrapper so the entire show signal emission completes first, * and the branch of the scene graph is in a stable state */ clutter_actor_update_map_state (self, MAP_STATE_CHECK); /* we queue a relayout unless the actor is inside a * container that explicitly told us not to */ if (priv->parent != NULL && (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT))) { /* While an actor is hidden the parent may not have * allocated/requested so we need to start from scratch * and avoid the short-circuiting in * clutter_actor_queue_relayout(). */ priv->needs_width_request = FALSE; priv->needs_height_request = FALSE; priv->needs_allocation = FALSE; clutter_actor_queue_relayout (self); } else /* but still don't leave the actor un-allocated before showing it */ { clutter_actor_queue_shallow_relayout (self); clutter_actor_queue_redraw (self); } } static inline void set_show_on_set_parent (ClutterActor *self, gboolean set_show) { ClutterActorPrivate *priv = self->priv; set_show = !!set_show; if (priv->show_on_set_parent == set_show) return; if (priv->parent == NULL) { priv->show_on_set_parent = set_show; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SHOW_ON_SET_PARENT]); } } static void clutter_actor_queue_redraw_on_parent (ClutterActor *self) { const ClutterPaintVolume *pv; if (!self->priv->parent) return; /* A relayout/redraw is underway */ if (self->priv->needs_allocation) return; pv = clutter_actor_get_transformed_paint_volume (self, self->priv->parent); _clutter_actor_queue_redraw_with_clip (self->priv->parent, 0, pv); } /** * clutter_actor_show: * @self: A #ClutterActor * * Flags an actor to be displayed. An actor that isn't shown will not * be rendered on the stage. * * Actors are visible by default. * * If this function is called on an actor without a parent, the * #ClutterActor:show-on-set-parent will be set to %TRUE as a side * effect. */ void clutter_actor_show (ClutterActor *self) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* simple optimization */ if (CLUTTER_ACTOR_IS_VISIBLE (self)) { /* we still need to set the :show-on-set-parent property, in * case show() is called on an unparented actor */ set_show_on_set_parent (self, TRUE); return; } #ifdef CLUTTER_ENABLE_DEBUG clutter_actor_verify_map_state (self); #endif priv = self->priv; g_object_freeze_notify (G_OBJECT (self)); set_show_on_set_parent (self, TRUE); /* if we're showing a child that needs to expand, or may * expand, then we need to recompute the expand flags for * its parent as well */ if (priv->needs_compute_expand || priv->needs_x_expand || priv->needs_y_expand) { clutter_actor_queue_compute_expand (self); } g_signal_emit (self, actor_signals[SHOW], 0); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]); if (priv->parent != NULL) clutter_actor_queue_redraw (self); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_is_visible: * @self: a #ClutterActor * * Checks whether an actor is marked as visible. * * See also %CLUTTER_ACTOR_IS_VISIBLE and #ClutterActor:visible. * * Returns: %TRUE if the actor visible * * Since: 1.24 */ gboolean clutter_actor_is_visible (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return CLUTTER_ACTOR_IS_VISIBLE (self); } /** * clutter_actor_show_all: * @self: a #ClutterActor * * Calls clutter_actor_show() on all children of an actor (if any). * * Since: 0.2 * * Deprecated: 1.10: Actors are visible by default */ void clutter_actor_show_all (ClutterActor *self) { ClutterActorClass *klass; g_return_if_fail (CLUTTER_IS_ACTOR (self)); klass = CLUTTER_ACTOR_GET_CLASS (self); if (klass->show_all) klass->show_all (self); } static void clutter_actor_real_hide (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (!CLUTTER_ACTOR_IS_VISIBLE (self)) return; CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_VISIBLE); /* we notify on the "visible" flag in the clutter_actor_hide() * wrapper so the entire hide signal emission completes first, * and the branch of the scene graph is in a stable state */ clutter_actor_update_map_state (self, MAP_STATE_CHECK); /* we queue a relayout unless the actor is inside a * container that explicitly told us not to */ if (priv->parent != NULL && (!(priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT))) clutter_actor_queue_relayout (priv->parent); } /** * clutter_actor_hide: * @self: A #ClutterActor * * Flags an actor to be hidden. A hidden actor will not be * rendered on the stage. * * Actors are visible by default. * * If this function is called on an actor without a parent, the * #ClutterActor:show-on-set-parent property will be set to %FALSE * as a side-effect. */ void clutter_actor_hide (ClutterActor *self) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* simple optimization */ if (!CLUTTER_ACTOR_IS_VISIBLE (self)) { /* we still need to set the :show-on-set-parent property, in * case hide() is called on an unparented actor */ set_show_on_set_parent (self, FALSE); return; } #ifdef CLUTTER_ENABLE_DEBUG clutter_actor_verify_map_state (self); #endif priv = self->priv; g_object_freeze_notify (G_OBJECT (self)); set_show_on_set_parent (self, FALSE); /* if we're hiding a child that needs to expand, or may * expand, then we need to recompute the expand flags for * its parent as well */ if (priv->needs_compute_expand || priv->needs_x_expand || priv->needs_y_expand) { clutter_actor_queue_compute_expand (self); } g_signal_emit (self, actor_signals[HIDE], 0); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_VISIBLE]); if (priv->parent != NULL && priv->needs_allocation) clutter_actor_queue_redraw (priv->parent); else clutter_actor_queue_redraw_on_parent (self); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_realize: * @self: A #ClutterActor * * Realization informs the actor that it is attached to a stage. It * can use this to allocate resources if it wanted to delay allocation * until it would be rendered. However it is perfectly acceptable for * an actor to create resources before being realized because Clutter * only ever has a single rendering context so that actor is free to * be moved from one stage to another. * * This function does nothing if the actor is already realized. * * Because a realized actor must have realized parent actors, calling * clutter_actor_realize() will also realize all parents of the actor. * * This function does not realize child actors, except in the special * case that realizing the stage, when the stage is visible, will * suddenly map (and thus realize) the children of the stage. * * Deprecated: 1.16: Actors are automatically realized, and nothing * requires explicit realization. */ void clutter_actor_realize (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); clutter_actor_realize_internal (self); } /** * clutter_actor_is_realized: * @self: a #ClutterActor * * Checks whether a #ClutterActor is realized. * * See also %CLUTTER_ACTOR_IS_REALIZED and #ClutterActor:realized. * * Returns: %TRUE if the actor is realized * * Since: 1.24 */ gboolean clutter_actor_is_realized (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return CLUTTER_ACTOR_IS_REALIZED (self); } static void clutter_actor_realize_internal (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; #ifdef CLUTTER_ENABLE_DEBUG clutter_actor_verify_map_state (self); #endif if (CLUTTER_ACTOR_IS_REALIZED (self)) return; /* To be realized, our parent actors must be realized first. * This will only succeed if we're inside a toplevel. */ if (priv->parent != NULL) clutter_actor_realize (priv->parent); if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) { /* toplevels can be realized at any time */ } else { /* "Fail" the realization if parent is missing or unrealized; * this should really be a g_warning() not some kind of runtime * failure; how can an app possibly recover? Instead it's a bug * in the app and the app should get an explanatory warning so * someone can fix it. But for now it's too hard to fix this * because e.g. ClutterTexture needs reworking. */ if (priv->parent == NULL || !CLUTTER_ACTOR_IS_REALIZED (priv->parent)) return; } CLUTTER_NOTE (ACTOR, "Realizing actor '%s'", _clutter_actor_get_debug_name (self)); CLUTTER_ACTOR_SET_FLAGS (self, CLUTTER_ACTOR_REALIZED); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]); g_signal_emit (self, actor_signals[REALIZE], 0); /* Stage actor is allowed to unset the realized flag again in its * default signal handler, though that is a pathological situation. */ /* If realization "failed" we'll have to update child state. */ clutter_actor_update_map_state (self, MAP_STATE_CHECK); } static void clutter_actor_real_unrealize (ClutterActor *self) { /* we must be unmapped (implying our children are also unmapped) */ g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); } /** * clutter_actor_unrealize: * @self: A #ClutterActor * * Unrealization informs the actor that it may be being destroyed or * moved to another stage. The actor may want to destroy any * underlying graphics resources at this point. However it is * perfectly acceptable for it to retain the resources until the actor * is destroyed because Clutter only ever uses a single rendering * context and all of the graphics resources are valid on any stage. * * Because mapped actors must be realized, actors may not be * unrealized if they are mapped. This function hides the actor to be * sure it isn't mapped, an application-visible side effect that you * may not be expecting. * * This function should not be called by application code. * * This function should not really be in the public API, because * there isn't a good reason to call it. ClutterActor will already * unrealize things for you when it's important to do so. * * If you were using clutter_actor_unrealize() in a dispose * implementation, then don't, just chain up to ClutterActor's * dispose. * * If you were using clutter_actor_unrealize() to implement * unrealizing children of your container, then don't, ClutterActor * will already take care of that. * * Deprecated: 1.16: Actors are automatically unrealized, and nothing * requires explicit realization. */ void clutter_actor_unrealize (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (!CLUTTER_ACTOR_IS_MAPPED (self)); clutter_actor_unrealize_internal (self); } /* If you were using clutter_actor_unrealize() to re-realize to * create your resources in a different way, then use * _clutter_actor_rerealize() (inside Clutter) or just call your * code that recreates your resources directly (outside Clutter). */ static void clutter_actor_unrealize_internal (ClutterActor *self) { #ifdef CLUTTER_ENABLE_DEBUG clutter_actor_verify_map_state (self); #endif clutter_actor_hide (self); clutter_actor_unrealize_not_hiding (self); } static ClutterActorTraverseVisitFlags unrealize_actor_before_children_cb (ClutterActor *self, int depth, void *user_data) { /* If an actor is already unrealized we know its children have also * already been unrealized... */ if (!CLUTTER_ACTOR_IS_REALIZED (self)) return CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN; g_signal_emit (self, actor_signals[UNREALIZE], 0); return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } static ClutterActorTraverseVisitFlags unrealize_actor_after_children_cb (ClutterActor *self, int depth, void *user_data) { /* We want to unset the realized flag only _after_ * child actors are unrealized, to maintain invariants. */ CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REALIZED]); return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } /* * clutter_actor_unrealize_not_hiding: * @self: A #ClutterActor * * Unrealization informs the actor that it may be being destroyed or * moved to another stage. The actor may want to destroy any * underlying graphics resources at this point. However it is * perfectly acceptable for it to retain the resources until the actor * is destroyed because Clutter only ever uses a single rendering * context and all of the graphics resources are valid on any stage. * * Because mapped actors must be realized, actors may not be * unrealized if they are mapped. You must hide the actor or one of * its parents before attempting to unrealize. * * This function is separate from clutter_actor_unrealize() because it * does not automatically hide the actor. * Actors need not be hidden to be unrealized, they just need to * be unmapped. In fact we don't want to mess up the application's * setting of the "visible" flag, so hiding is very undesirable. * * clutter_actor_unrealize() does a clutter_actor_hide() just for * backward compatibility. */ static void clutter_actor_unrealize_not_hiding (ClutterActor *self) { _clutter_actor_traverse (self, CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, unrealize_actor_before_children_cb, unrealize_actor_after_children_cb, NULL); } /* * _clutter_actor_rerealize: * @self: A #ClutterActor * @callback: Function to call while unrealized * @data: data for callback * * If an actor is already unrealized, this just calls the callback. * * If it is realized, it unrealizes temporarily, calls the callback, * and then re-realizes the actor. * * As a side effect, leaves all children of the actor unrealized if * the actor was realized but not showing. This is because when we * unrealize the actor temporarily we must unrealize its children * (e.g. children of a stage can't be realized if stage window is * gone). And we aren't clever enough to save the realization state of * all children. In most cases this should not matter, because * the children will automatically realize when they next become mapped. */ void _clutter_actor_rerealize (ClutterActor *self, ClutterCallback callback, void *data) { gboolean was_mapped; gboolean was_showing; gboolean was_realized; g_return_if_fail (CLUTTER_IS_ACTOR (self)); #ifdef CLUTTER_ENABLE_DEBUG clutter_actor_verify_map_state (self); #endif was_realized = CLUTTER_ACTOR_IS_REALIZED (self); was_mapped = CLUTTER_ACTOR_IS_MAPPED (self); was_showing = CLUTTER_ACTOR_IS_VISIBLE (self); /* Must be unmapped to unrealize. Note we only have to hide this * actor if it was mapped (if all parents were showing). If actor * is merely visible (but not mapped), then that's fine, we can * leave it visible. */ if (was_mapped) clutter_actor_hide (self); g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); /* unrealize self and all children */ clutter_actor_unrealize_not_hiding (self); if (callback != NULL) { (* callback) (self, data); } if (was_showing) clutter_actor_show (self); /* will realize only if mapping implies it */ else if (was_realized) clutter_actor_realize (self); /* realize self and all parents */ } static void clutter_actor_real_pick (ClutterActor *self, ClutterPickContext *pick_context) { if (clutter_actor_should_pick_paint (self)) { ClutterActorBox box = { .x1 = 0, .y1 = 0, .x2 = clutter_actor_get_width (self), .y2 = clutter_actor_get_height (self), }; clutter_actor_pick_box (self, pick_context, &box); } /* XXX - this thoroughly sucks, but we need to maintain compatibility * with existing container classes that override the pick() virtual * and chain up to the default implementation - otherwise we'll end up * painting our children twice. * * this has to go away for 2.0; hopefully along the pick() itself. */ if (CLUTTER_ACTOR_GET_CLASS (self)->pick == clutter_actor_real_pick) { ClutterActor *iter; for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) clutter_actor_pick (iter, pick_context); } } /** * clutter_actor_should_pick_paint: * @self: A #ClutterActor * * Should be called inside the implementation of the * #ClutterActor::pick virtual function in order to check whether * the actor should paint itself in pick mode or not. * * This function should never be called directly by applications. * * Return value: %TRUE if the actor should paint its silhouette, * %FALSE otherwise */ gboolean clutter_actor_should_pick_paint (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (CLUTTER_ACTOR_IS_MAPPED (self) && clutter_actor_has_allocation (self) && (_clutter_context_get_pick_mode () == CLUTTER_PICK_ALL || CLUTTER_ACTOR_IS_REACTIVE (self))) return TRUE; return FALSE; } static void clutter_actor_real_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterActorPrivate *priv = self->priv; if (priv->n_children != 0 && priv->layout_manager != NULL) { ClutterContainer *container = CLUTTER_CONTAINER (self); CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] " "for the preferred width", G_OBJECT_TYPE_NAME (priv->layout_manager), priv->layout_manager); clutter_layout_manager_get_preferred_width (priv->layout_manager, container, for_height, min_width_p, natural_width_p); return; } /* Default implementation is always 0x0, usually an actor * using this default is relying on someone to set the * request manually */ CLUTTER_NOTE (LAYOUT, "Default preferred width: 0, 0"); if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; } static void clutter_actor_real_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterActorPrivate *priv = self->priv; if (priv->n_children != 0 && priv->layout_manager != NULL) { ClutterContainer *container = CLUTTER_CONTAINER (self); CLUTTER_NOTE (LAYOUT, "Querying the layout manager '%s'[%p] " "for the preferred height", G_OBJECT_TYPE_NAME (priv->layout_manager), priv->layout_manager); clutter_layout_manager_get_preferred_height (priv->layout_manager, container, for_width, min_height_p, natural_height_p); return; } /* Default implementation is always 0x0, usually an actor * using this default is relying on someone to set the * request manually */ CLUTTER_NOTE (LAYOUT, "Default preferred height: 0, 0"); if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } static void clutter_actor_store_old_geometry (ClutterActor *self, ClutterActorBox *box) { *box = self->priv->allocation; } static inline void clutter_actor_notify_if_geometry_changed (ClutterActor *self, const ClutterActorBox *old) { ClutterActorPrivate *priv = self->priv; GObject *obj = G_OBJECT (self); g_object_freeze_notify (obj); /* to avoid excessive requisition or allocation cycles we * use the cached values. * * - if we don't have an allocation we assume that we need * to notify anyway * - if we don't have a width or a height request we notify * width and height * - if we have a valid allocation then we check the old * bounding box with the current allocation and we notify * the changes */ if (priv->needs_allocation) { g_object_notify_by_pspec (obj, obj_props[PROP_X]); g_object_notify_by_pspec (obj, obj_props[PROP_Y]); g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]); g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]); g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]); g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]); } else if (priv->needs_width_request || priv->needs_height_request) { g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]); g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]); g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]); } else { gfloat x, y; gfloat width, height; x = priv->allocation.x1; y = priv->allocation.y1; width = priv->allocation.x2 - priv->allocation.x1; height = priv->allocation.y2 - priv->allocation.y1; if (x != old->x1) { g_object_notify_by_pspec (obj, obj_props[PROP_X]); g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]); } if (y != old->y1) { g_object_notify_by_pspec (obj, obj_props[PROP_Y]); g_object_notify_by_pspec (obj, obj_props[PROP_POSITION]); } if (width != (old->x2 - old->x1)) { g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]); g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]); } if (height != (old->y2 - old->y1)) { g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]); g_object_notify_by_pspec (obj, obj_props[PROP_SIZE]); } } g_object_thaw_notify (obj); } /*< private > * clutter_actor_set_allocation_internal: * @self: a #ClutterActor * @box: a #ClutterActorBox * @flags: allocation flags * * Stores the allocation of @self. * * This function only performs basic storage and property notification. * * This function should be called by clutter_actor_set_allocation() * and by the default implementation of #ClutterActorClass.allocate(). * * Return value: %TRUE if the allocation of the #ClutterActor has been * changed, and %FALSE otherwise */ static inline gboolean clutter_actor_set_allocation_internal (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorPrivate *priv = self->priv; GObject *obj; gboolean x1_changed, y1_changed, x2_changed, y2_changed; gboolean retval; ClutterActorBox old_alloc = { 0, }; g_return_val_if_fail (!isnan (box->x1) && !isnan (box->x2) && !isnan (box->y1) && !isnan (box->y2), FALSE); obj = G_OBJECT (self); g_object_freeze_notify (obj); clutter_actor_store_old_geometry (self, &old_alloc); x1_changed = priv->allocation.x1 != box->x1; y1_changed = priv->allocation.y1 != box->y1; x2_changed = priv->allocation.x2 != box->x2; y2_changed = priv->allocation.y2 != box->y2; priv->allocation = *box; priv->allocation_flags = flags; /* allocation is authoritative */ priv->needs_width_request = FALSE; priv->needs_height_request = FALSE; priv->needs_allocation = FALSE; if (x1_changed || y1_changed || x2_changed || y2_changed) { CLUTTER_NOTE (LAYOUT, "Allocation for '%s' changed", _clutter_actor_get_debug_name (self)); priv->transform_valid = FALSE; g_object_notify_by_pspec (obj, obj_props[PROP_ALLOCATION]); /* if the allocation changes, so does the content box */ if (priv->content != NULL) { priv->content_box_valid = FALSE; g_object_notify_by_pspec (obj, obj_props[PROP_CONTENT_BOX]); } retval = TRUE; } else retval = FALSE; clutter_actor_notify_if_geometry_changed (self, &old_alloc); g_object_thaw_notify (obj); return retval; } static void clutter_actor_real_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags); static inline void clutter_actor_maybe_layout_children (ClutterActor *self, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterActorPrivate *priv = self->priv; /* this is going to be a bit hard to follow, so let's put an explanation * here. * * we want ClutterActor to have a default layout manager if the actor was * created using "g_object_new (CLUTTER_TYPE_ACTOR, NULL)". * * we also want any subclass of ClutterActor that does not override the * ::allocate() virtual function to delegate to a layout manager. * * finally, we want to allow people subclassing ClutterActor and overriding * the ::allocate() vfunc to let Clutter delegate to the layout manager. * * on the other hand, we want existing actor subclasses overriding the * ::allocate() virtual function and chaining up to the parent's * implementation to continue working without allocating their children * twice, or without entering an allocation loop. * * for the first two points, we check if the class of the actor is * overridding the ::allocate() virtual function; if it isn't, then we * follow through with checking whether we have children and a layout * manager, and eventually calling clutter_layout_manager_allocate(). * * for the third point, we check the CLUTTER_DELEGATE_LAYOUT flag in the * allocation flags that we got passed, and if it is present, we continue * with the check above. * * if neither of these two checks yields a positive result, we just * assume that the ::allocate() virtual function that resulted in this * function being called will also allocate the children of the actor. */ if (CLUTTER_ACTOR_GET_CLASS (self)->allocate == clutter_actor_real_allocate) goto check_layout; if ((flags & CLUTTER_DELEGATE_LAYOUT) != 0) goto check_layout; return; check_layout: if (priv->n_children != 0 && priv->layout_manager != NULL) { ClutterContainer *container = CLUTTER_CONTAINER (self); ClutterAllocationFlags children_flags; ClutterActorBox children_box; /* normalize the box passed to the layout manager */ children_box.x1 = children_box.y1 = 0.f; children_box.x2 = (allocation->x2 - allocation->x1); children_box.y2 = (allocation->y2 - allocation->y1); /* remove the DELEGATE_LAYOUT flag; this won't be passed to * the actor's children, since it refers only to the current * actor's allocation. */ children_flags = flags; children_flags &= ~CLUTTER_DELEGATE_LAYOUT; CLUTTER_NOTE (LAYOUT, "Allocating %d children of %s " "at { %.2f, %.2f - %.2f x %.2f } " "using %s", priv->n_children, _clutter_actor_get_debug_name (self), allocation->x1, allocation->y1, (allocation->x2 - allocation->x1), (allocation->y2 - allocation->y1), G_OBJECT_TYPE_NAME (priv->layout_manager)); clutter_layout_manager_allocate (priv->layout_manager, container, &children_box, children_flags); } } static void clutter_actor_real_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorPrivate *priv = self->priv; gboolean changed; g_object_freeze_notify (G_OBJECT (self)); changed = clutter_actor_set_allocation_internal (self, box, flags); /* we allocate our children before we notify changes in our geometry, * so that people connecting to properties will be able to get valid * data out of the sub-tree of the scene graph that has this actor at * the root. */ clutter_actor_maybe_layout_children (self, box, flags); if (changed) { ClutterActorBox signal_box = priv->allocation; ClutterAllocationFlags signal_flags = priv->allocation_flags; g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0, &signal_box, signal_flags); } g_object_thaw_notify (G_OBJECT (self)); } static void _clutter_actor_propagate_queue_redraw (ClutterActor *self, ClutterActor *origin, ClutterPaintVolume *pv) { gboolean stop = FALSE; /* no point in queuing a redraw on a destroyed actor */ if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; /* NB: We can't bail out early here if the actor is hidden in case * the actor bas been cloned. In this case the clone will need to * receive the signal so it can queue its own redraw. */ while (self) { _clutter_actor_queue_redraw_on_clones (self); /* calls klass->queue_redraw in default handler */ if (g_signal_has_handler_pending (self, actor_signals[QUEUE_REDRAW], 0, TRUE)) { g_signal_emit (self, actor_signals[QUEUE_REDRAW], 0, origin, pv, &stop); } else { stop = CLUTTER_ACTOR_GET_CLASS (self)->queue_redraw (self, origin, pv); } if (stop) break; self = clutter_actor_get_parent (self); } } static gboolean clutter_actor_real_queue_redraw (ClutterActor *self, ClutterActor *origin, ClutterPaintVolume *paint_volume) { CLUTTER_NOTE (PAINT, "Redraw queued on '%s' (from: '%s')", _clutter_actor_get_debug_name (self), origin != NULL ? _clutter_actor_get_debug_name (origin) : "same actor"); /* no point in queuing a redraw on a destroyed actor */ if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return TRUE; /* If the queue redraw is coming from a child then the actor has become dirty and any queued effect is no longer valid */ if (self != origin) { self->priv->is_dirty = TRUE; self->priv->effect_to_redraw = NULL; } /* If the actor isn't visible, we still had to emit the signal * to allow for a ClutterClone, but the appearance of the parent * won't change so we don't have to propagate up the hierarchy. */ if (!CLUTTER_ACTOR_IS_VISIBLE (self)) return TRUE; /* Although we could determine here that a full stage redraw * has already been queued and immediately bail out, we actually * guarantee that we will propagate a queue-redraw signal to our * parent at least once so that it's possible to implement a * container that tracks which of its children have queued a * redraw. */ if (self->priv->propagated_one_redraw) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); if (stage != NULL && _clutter_stage_has_full_redraw_queued (CLUTTER_STAGE (stage))) return TRUE; } self->priv->propagated_one_redraw = TRUE; /* notify parents, if they are all visible eventually we'll * queue redraw on the stage, which queues the redraw idle. */ return FALSE; } static inline gboolean clutter_actor_needs_relayout (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; return (priv->needs_width_request || priv->needs_height_request || priv->needs_allocation); } static void clutter_actor_real_queue_relayout (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; /* no point in queueing a redraw on a destroyed actor */ if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; priv->needs_width_request = TRUE; priv->needs_height_request = TRUE; priv->needs_allocation = TRUE; priv->needs_paint_volume_update = TRUE; /* reset the cached size requests */ memset (priv->width_requests, 0, N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest)); memset (priv->height_requests, 0, N_CACHED_SIZE_REQUESTS * sizeof (SizeRequest)); /* We may need to go all the way up the hierarchy */ if (priv->parent != NULL) { if (priv->parent->flags & CLUTTER_ACTOR_NO_LAYOUT) { clutter_actor_queue_shallow_relayout (self); /* The above might have invalidated the parent's paint volume if self * has moved or resized. DnD seems to require this... */ priv->parent->priv->needs_paint_volume_update = TRUE; } else { _clutter_actor_queue_only_relayout (priv->parent); } } } /** * clutter_actor_apply_relative_transform_to_point: * @self: A #ClutterActor * @ancestor: (allow-none): A #ClutterActor ancestor, or %NULL to use the * default #ClutterStage * @point: A point as #graphene_point3d_t * @vertex: (out caller-allocates): The translated #graphene_point3d_t * * Transforms @point in coordinates relative to the actor into * ancestor-relative coordinates using the relevant transform * stack (i.e. scale, rotation, etc). * * If @ancestor is %NULL the ancestor will be the #ClutterStage. In * this case, the coordinates returned will be the coordinates on * the stage before the projection is applied. This is different from * the behaviour of clutter_actor_apply_transform_to_point(). * * Since: 0.6 */ void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, ClutterActor *ancestor, const graphene_point3d_t *point, graphene_point3d_t *vertex) { gfloat w; CoglMatrix matrix; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); *vertex = *point; w = 1.0; if (ancestor == NULL) ancestor = _clutter_actor_get_stage_internal (self); if (ancestor == NULL) { *vertex = *point; return; } _clutter_actor_get_relative_transformation_matrix (self, ancestor, &matrix); cogl_matrix_transform_point (&matrix, &vertex->x, &vertex->y, &vertex->z, &w); } static gboolean _clutter_actor_fully_transform_vertices (ClutterActor *self, const graphene_point3d_t *vertices_in, graphene_point3d_t *vertices_out, int n_vertices) { ClutterActor *stage; CoglMatrix modelview; CoglMatrix projection; float viewport[4]; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); stage = _clutter_actor_get_stage_internal (self); /* We really can't do anything meaningful in this case so don't try * to do any transform */ if (stage == NULL) return FALSE; /* Note: we pass NULL as the ancestor because we don't just want the modelview * that gets us to stage coordinates, we want to go all the way to eye * coordinates */ _clutter_actor_get_relative_transformation_matrix (self, NULL, &modelview); /* Fetch the projection and viewport */ _clutter_stage_get_projection_matrix (CLUTTER_STAGE (stage), &projection); _clutter_stage_get_viewport (CLUTTER_STAGE (stage), &viewport[0], &viewport[1], &viewport[2], &viewport[3]); _clutter_util_fully_transform_vertices (&modelview, &projection, viewport, vertices_in, vertices_out, n_vertices); return TRUE; } /** * clutter_actor_apply_transform_to_point: * @self: A #ClutterActor * @point: A point as #graphene_point3d_t * @vertex: (out caller-allocates): The translated #graphene_point3d_t * * Transforms @point in coordinates relative to the actor * into screen-relative coordinates with the current actor * transformation (i.e. scale, rotation, etc) * * Since: 0.4 **/ void clutter_actor_apply_transform_to_point (ClutterActor *self, const graphene_point3d_t *point, graphene_point3d_t *vertex) { g_return_if_fail (point != NULL); g_return_if_fail (vertex != NULL); _clutter_actor_fully_transform_vertices (self, point, vertex, 1); } /* * _clutter_actor_get_relative_transformation_matrix: * @self: The actor whose coordinate space you want to transform from. * @ancestor: The ancestor actor whose coordinate space you want to transform too * or %NULL if you want to transform all the way to eye coordinates. * @matrix: A #CoglMatrix to store the transformation * * This gets a transformation @matrix that will transform coordinates from the * coordinate space of @self into the coordinate space of @ancestor. * * For example if you need a matrix that can transform the local actor * coordinates of @self into stage coordinates you would pass the actor's stage * pointer as the @ancestor. * * If you pass %NULL then the transformation will take you all the way through * to eye coordinates. This can be useful if you want to extract the entire * modelview transform that Clutter applies before applying the projection * transformation. If you want to explicitly set a modelview on a CoglFramebuffer * using cogl_set_modelview_matrix() for example then you would want a matrix * that transforms into eye coordinates. * * Note: This function explicitly initializes the given @matrix. If you just * want clutter to multiply a relative transformation with an existing matrix * you can use clutter_actor_apply_relative_transformation_matrix() * instead. * */ /* XXX: We should consider caching the stage relative modelview along with * the actor itself */ static void _clutter_actor_get_relative_transformation_matrix (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix) { cogl_matrix_init_identity (matrix); _clutter_actor_apply_relative_transformation_matrix (self, ancestor, matrix); } /* Project the given @box into stage window coordinates, writing the * transformed vertices to @verts[]. */ static gboolean _clutter_actor_transform_and_project_box (ClutterActor *self, const ClutterActorBox *box, graphene_point3d_t *verts) { graphene_point3d_t box_vertices[4]; box_vertices[0].x = box->x1; box_vertices[0].y = box->y1; box_vertices[0].z = 0; box_vertices[1].x = box->x2; box_vertices[1].y = box->y1; box_vertices[1].z = 0; box_vertices[2].x = box->x1; box_vertices[2].y = box->y2; box_vertices[2].z = 0; box_vertices[3].x = box->x2; box_vertices[3].y = box->y2; box_vertices[3].z = 0; return _clutter_actor_fully_transform_vertices (self, box_vertices, verts, 4); } /** * clutter_actor_get_allocation_vertices: * @self: A #ClutterActor * @ancestor: (allow-none): A #ClutterActor to calculate the vertices * against, or %NULL to use the #ClutterStage * @verts: (out) (array fixed-size=4): return * location for an array of 4 #graphene_point3d_t in which to store the result * * Calculates the transformed coordinates of the four corners of the * actor in the plane of @ancestor. The returned vertices relate to * the #ClutterActorBox coordinates as follows: * * - @verts[0] contains (x1, y1) * - @verts[1] contains (x2, y1) * - @verts[2] contains (x1, y2) * - @verts[3] contains (x2, y2) * * If @ancestor is %NULL the ancestor will be the #ClutterStage. In * this case, the coordinates returned will be the coordinates on * the stage before the projection is applied. This is different from * the behaviour of clutter_actor_get_abs_allocation_vertices(). * * Since: 0.6 */ void clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterActor *ancestor, graphene_point3d_t *verts) { ClutterActorPrivate *priv; ClutterActorBox box; graphene_point3d_t vertices[4]; CoglMatrix modelview; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (ancestor == NULL || CLUTTER_IS_ACTOR (ancestor)); if (ancestor == NULL) ancestor = _clutter_actor_get_stage_internal (self); /* Fallback to a NOP transform if the actor isn't parented under a * stage. */ if (ancestor == NULL) ancestor = self; priv = self->priv; /* if the actor needs to be allocated we force a relayout, so that * we will have valid values to use in the transformations */ if (priv->needs_allocation) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); if (stage) _clutter_stage_maybe_relayout (stage); else { box.x1 = box.y1 = 0; /* The result isn't really meaningful in this case but at * least try to do something *vaguely* reasonable... */ clutter_actor_get_size (self, &box.x2, &box.y2); } } clutter_actor_get_allocation_box (self, &box); vertices[0].x = box.x1; vertices[0].y = box.y1; vertices[0].z = 0; vertices[1].x = box.x2; vertices[1].y = box.y1; vertices[1].z = 0; vertices[2].x = box.x1; vertices[2].y = box.y2; vertices[2].z = 0; vertices[3].x = box.x2; vertices[3].y = box.y2; vertices[3].z = 0; _clutter_actor_get_relative_transformation_matrix (self, ancestor, &modelview); cogl_matrix_transform_points (&modelview, 3, sizeof (graphene_point3d_t), vertices, sizeof (graphene_point3d_t), vertices, 4); } /** * clutter_actor_get_abs_allocation_vertices: * @self: A #ClutterActor * @verts: (out) (array fixed-size=4): Pointer to a location of an array * of 4 #graphene_point3d_t where to store the result. * * Calculates the transformed screen coordinates of the four corners of * the actor; the returned vertices relate to the #ClutterActorBox * coordinates as follows: * * - v[0] contains (x1, y1) * - v[1] contains (x2, y1) * - v[2] contains (x1, y2) * - v[3] contains (x2, y2) * * Since: 0.4 */ void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, graphene_point3d_t *verts) { ClutterActorPrivate *priv; ClutterActorBox actor_space_allocation; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; /* if the actor needs to be allocated we force a relayout, so that * the actor allocation box will be valid for * _clutter_actor_transform_and_project_box() */ if (priv->needs_allocation) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); /* There's nothing meaningful we can do now */ if (!stage) return; _clutter_stage_maybe_relayout (stage); } /* NB: _clutter_actor_transform_and_project_box expects a box in the actor's * own coordinate space... */ actor_space_allocation.x1 = 0; actor_space_allocation.y1 = 0; actor_space_allocation.x2 = priv->allocation.x2 - priv->allocation.x1; actor_space_allocation.y2 = priv->allocation.y2 - priv->allocation.y1; _clutter_actor_transform_and_project_box (self, &actor_space_allocation, verts); } static void clutter_actor_real_apply_transform (ClutterActor *self, ClutterMatrix *matrix) { ClutterActorPrivate *priv = self->priv; CoglMatrix *transform = &priv->transform; const ClutterTransformInfo *info; float pivot_x = 0.f, pivot_y = 0.f; /* we already have a cached transformation */ if (priv->transform_valid) goto multiply_and_return; info = _clutter_actor_get_transform_info_or_defaults (self); /* compute the pivot point given the allocated size */ pivot_x = (priv->allocation.x2 - priv->allocation.x1) * info->pivot.x; pivot_y = (priv->allocation.y2 - priv->allocation.y1) * info->pivot.y; CLUTTER_NOTE (PAINT, "Allocation: (%.2f, %2.f), " "pivot: (%.2f, %.2f), " "translation: (%.2f, %.2f) -> " "new origin: (%.2f, %.2f)", priv->allocation.x1, priv->allocation.y1, info->pivot.x, info->pivot.y, info->translation.x, info->translation.y, priv->allocation.x1 + pivot_x + info->translation.x, priv->allocation.y1 + pivot_y + info->translation.y); /* we apply the :child-transform from the parent actor, if we have one */ if (priv->parent != NULL) { const ClutterTransformInfo *parent_info; parent_info = _clutter_actor_get_transform_info_or_defaults (priv->parent); clutter_matrix_init_from_matrix (transform, &(parent_info->child_transform)); } else clutter_matrix_init_identity (transform); /* if we have an overriding transformation, we use that, and get out */ if (info->transform_set) { /* we still need to apply the :allocation's origin and :pivot-point * translations, since :transform is relative to the actor's coordinate * space, and to the pivot point */ cogl_matrix_translate (transform, priv->allocation.x1 + pivot_x, priv->allocation.y1 + pivot_y, info->pivot_z); cogl_matrix_multiply (transform, transform, &info->transform); goto roll_back_pivot; } /* basic translation: :allocation's origin and :z-position; instead * of decomposing the pivot and translation info separate operations, * we just compose everything into a single translation */ cogl_matrix_translate (transform, priv->allocation.x1 + pivot_x + info->translation.x, priv->allocation.y1 + pivot_y + info->translation.y, info->z_position + info->pivot_z + info->translation.z); /* because the rotation involves translations, we must scale * before applying the rotations (if we apply the scale after * the rotations, the translations included in the rotation are * not scaled and so the entire object will move on the screen * as a result of rotating it). * * XXX:2.0 the comment has to be reworded once we remove the * per-transformation centers; we also may want to apply rotation * first and scaling after, to match the matrix decomposition * code we use when interpolating transformations */ if (info->scale_x != 1.0 || info->scale_y != 1.0 || info->scale_z != 1.0) { /* XXX:2.0 remove anchor coord */ TRANSFORM_ABOUT_ANCHOR_COORD (self, transform, &info->scale_center, cogl_matrix_scale (transform, info->scale_x, info->scale_y, info->scale_z)); } if (info->rz_angle) { /* XXX:2.0 remove anchor coord */ TRANSFORM_ABOUT_ANCHOR_COORD (self, transform, &info->rz_center, cogl_matrix_rotate (transform, info->rz_angle, 0, 0, 1.0)); } if (info->ry_angle) { /* XXX:2.0 remove anchor coord */ TRANSFORM_ABOUT_ANCHOR_COORD (self, transform, &info->ry_center, cogl_matrix_rotate (transform, info->ry_angle, 0, 1.0, 0)); } if (info->rx_angle) { /* XXX:2.0 remove anchor coord */ TRANSFORM_ABOUT_ANCHOR_COORD (self, transform, &info->rx_center, cogl_matrix_rotate (transform, info->rx_angle, 1.0, 0, 0)); } /* XXX:2.0 remove anchor point translation */ if (!clutter_anchor_coord_is_zero (&info->anchor)) { gfloat x, y, z; clutter_anchor_coord_get_units (self, &info->anchor, &x, &y, &z); cogl_matrix_translate (transform, -x, -y, -z); } roll_back_pivot: /* roll back the pivot translation */ if (pivot_x != 0.f || pivot_y != 0.f || info->pivot_z != 0.f) cogl_matrix_translate (transform, -pivot_x, -pivot_y, -info->pivot_z); /* we have a valid modelview */ priv->transform_valid = TRUE; multiply_and_return: cogl_matrix_multiply (matrix, matrix, &priv->transform); } /* Applies the transforms associated with this actor to the given * matrix. */ void _clutter_actor_apply_modelview_transform (ClutterActor *self, ClutterMatrix *matrix) { CLUTTER_ACTOR_GET_CLASS (self)->apply_transform (self, matrix); } /* * clutter_actor_apply_relative_transformation_matrix: * @self: The actor whose coordinate space you want to transform from. * @ancestor: The ancestor actor whose coordinate space you want to transform too * or %NULL if you want to transform all the way to eye coordinates. * @matrix: A #ClutterMatrix to apply the transformation too. * * This multiplies a transform with @matrix that will transform coordinates * from the coordinate space of @self into the coordinate space of @ancestor. * * For example if you need a matrix that can transform the local actor * coordinates of @self into stage coordinates you would pass the actor's stage * pointer as the @ancestor. * * If you pass %NULL then the transformation will take you all the way through * to eye coordinates. This can be useful if you want to extract the entire * modelview transform that Clutter applies before applying the projection * transformation. If you want to explicitly set a modelview on a CoglFramebuffer * using cogl_set_modelview_matrix() for example then you would want a matrix * that transforms into eye coordinates. * * This function doesn't initialize the given @matrix, it simply * multiplies the requested transformation matrix with the existing contents of * @matrix. You can use cogl_matrix_init_identity() to initialize the @matrix * before calling this function, or you can use * clutter_actor_get_relative_transformation_matrix() instead. */ void _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix) { ClutterActor *parent; /* Note we terminate before ever calling stage->apply_transform() * since that would conceptually be relative to the underlying * window OpenGL coordinates so we'd need a special @ancestor * value to represent the fake parent of the stage. */ if (self == ancestor) return; parent = clutter_actor_get_parent (self); if (parent != NULL) _clutter_actor_apply_relative_transformation_matrix (parent, ancestor, matrix); _clutter_actor_apply_modelview_transform (self, matrix); } static void _clutter_actor_draw_paint_volume_full (ClutterActor *self, ClutterPaintVolume *pv, const char *label, const ClutterColor *color, ClutterPaintNode *node) { g_autoptr (ClutterPaintNode) pipeline_node = NULL; static CoglPipeline *outline = NULL; CoglPrimitive *prim; graphene_point3d_t line_ends[12 * 2]; int n_vertices; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglColor cogl_color; if (outline == NULL) outline = cogl_pipeline_new (ctx); _clutter_paint_volume_complete (pv); n_vertices = pv->is_2d ? 4 * 2 : 12 * 2; /* Front face */ line_ends[0] = pv->vertices[0]; line_ends[1] = pv->vertices[1]; line_ends[2] = pv->vertices[1]; line_ends[3] = pv->vertices[2]; line_ends[4] = pv->vertices[2]; line_ends[5] = pv->vertices[3]; line_ends[6] = pv->vertices[3]; line_ends[7] = pv->vertices[0]; if (!pv->is_2d) { /* Back face */ line_ends[8] = pv->vertices[4]; line_ends[9] = pv->vertices[5]; line_ends[10] = pv->vertices[5]; line_ends[11] = pv->vertices[6]; line_ends[12] = pv->vertices[6]; line_ends[13] = pv->vertices[7]; line_ends[14] = pv->vertices[7]; line_ends[15] = pv->vertices[4]; /* Lines connecting front face to back face */ line_ends[16] = pv->vertices[0]; line_ends[17] = pv->vertices[4]; line_ends[18] = pv->vertices[1]; line_ends[19] = pv->vertices[5]; line_ends[20] = pv->vertices[2]; line_ends[21] = pv->vertices[6]; line_ends[22] = pv->vertices[3]; line_ends[23] = pv->vertices[7]; } prim = cogl_primitive_new_p3 (ctx, COGL_VERTICES_MODE_LINES, n_vertices, (CoglVertexP3 *)line_ends); cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); cogl_pipeline_set_color (outline, &cogl_color); pipeline_node = clutter_pipeline_node_new (outline); clutter_paint_node_set_static_name (pipeline_node, "ClutterActor (paint volume outline)"); clutter_paint_node_add_primitive (pipeline_node, prim); clutter_paint_node_add_child (node, pipeline_node); cogl_object_unref (prim); if (label) { g_autoptr (ClutterPaintNode) text_node = NULL; PangoLayout *layout; layout = pango_layout_new (clutter_actor_get_pango_context (self)); pango_layout_set_text (layout, label, -1); text_node = clutter_text_node_new (layout, color); clutter_paint_node_set_static_name (text_node, "ClutterActor (paint volume label)"); clutter_paint_node_add_rectangle (text_node, &(ClutterActorBox) { .x1 = pv->vertices[0].x, .y1 = pv->vertices[0].y, .x2 = pv->vertices[2].x, .y2 = pv->vertices[2].y, }); clutter_paint_node_add_child (node, text_node); g_object_unref (layout); } } static void _clutter_actor_draw_paint_volume (ClutterActor *self, ClutterPaintNode *node) { ClutterPaintVolume *pv; ClutterColor color; pv = _clutter_actor_get_paint_volume_mutable (self); if (!pv) { gfloat width, height; ClutterPaintVolume fake_pv; ClutterActor *stage = _clutter_actor_get_stage_internal (self); _clutter_paint_volume_init_static (&fake_pv, stage); clutter_actor_get_size (self, &width, &height); clutter_paint_volume_set_width (&fake_pv, width); clutter_paint_volume_set_height (&fake_pv, height); clutter_color_init (&color, 0, 0, 255, 255); _clutter_actor_draw_paint_volume_full (self, &fake_pv, _clutter_actor_get_debug_name (self), &color, node); clutter_paint_volume_free (&fake_pv); } else { clutter_color_init (&color, 0, 255, 0, 255); _clutter_actor_draw_paint_volume_full (self, pv, _clutter_actor_get_debug_name (self), &color, node); } } static void _clutter_actor_paint_cull_result (ClutterActor *self, gboolean success, ClutterCullResult result, ClutterPaintNode *node) { ClutterActorPrivate *priv = self->priv; ClutterPaintVolume *pv; ClutterColor color; if (success) { if (result == CLUTTER_CULL_RESULT_IN) clutter_color_init (&color, 0, 255, 0, 255); else if (result == CLUTTER_CULL_RESULT_OUT) clutter_color_init (&color, 0, 0, 255, 255); else clutter_color_init (&color, 0, 255, 255, 255); } else clutter_color_init (&color, 255, 255, 255, 255); if (success && (pv = _clutter_actor_get_paint_volume_mutable (self))) _clutter_actor_draw_paint_volume_full (self, pv, _clutter_actor_get_debug_name (self), &color, node); else { g_autoptr (ClutterPaintNode) text_node = NULL; PangoLayout *layout; float width; float height; char *label = g_strdup_printf ("CULL FAILURE: %s", _clutter_actor_get_debug_name (self)); clutter_color_init (&color, 255, 255, 255, 255); width = clutter_actor_box_get_width (&priv->allocation); height = clutter_actor_box_get_height (&priv->allocation); layout = pango_layout_new (clutter_actor_get_pango_context (self)); pango_layout_set_text (layout, label, -1); text_node = clutter_text_node_new (layout, &color); clutter_paint_node_set_static_name (text_node, "ClutterActor (paint volume text)"); clutter_paint_node_add_rectangle (text_node, &(ClutterActorBox) { .x1 = 0.f, .y1 = 0.f, .x2 = width, .y2 = height, }); clutter_paint_node_add_child (node, text_node); g_free (label); g_object_unref (layout); } } static int clone_paint_level = 0; void _clutter_actor_push_clone_paint (void) { clone_paint_level++; } void _clutter_actor_pop_clone_paint (void) { clone_paint_level--; } static gboolean in_clone_paint (void) { return clone_paint_level > 0; } /* Returns TRUE if the actor can be ignored */ /* FIXME: we should return a ClutterCullResult, and * clutter_actor_paint should understand that a CLUTTER_CULL_RESULT_IN * means there's no point in trying to cull descendants of the current * node. */ static gboolean cull_actor (ClutterActor *self, ClutterPaintContext *paint_context, ClutterCullResult *result_out) { ClutterActorPrivate *priv = self->priv; ClutterStage *stage; const ClutterPlane *stage_clip; if (!priv->last_paint_volume_valid) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " "->last_paint_volume_valid == FALSE", _clutter_actor_get_debug_name (self)); return FALSE; } if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CULLING)) return FALSE; stage = (ClutterStage *) _clutter_actor_get_stage_internal (self); stage_clip = _clutter_stage_get_clip (stage); if (G_UNLIKELY (!stage_clip)) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " "No stage clip set", _clutter_actor_get_debug_name (self)); return FALSE; } if (clutter_paint_context_is_drawing_off_stage (paint_context)) { CLUTTER_NOTE (CLIPPING, "Bail from cull_actor without culling (%s): " "Drawing off stage", _clutter_actor_get_debug_name (self)); return FALSE; } *result_out = _clutter_paint_volume_cull (&priv->last_paint_volume, stage_clip); return TRUE; } static void _clutter_actor_update_last_paint_volume (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; const ClutterPaintVolume *pv; if (priv->last_paint_volume_valid) { clutter_paint_volume_free (&priv->last_paint_volume); priv->last_paint_volume_valid = FALSE; } pv = clutter_actor_get_paint_volume (self); if (!pv) { CLUTTER_NOTE (CLIPPING, "Bail from update_last_paint_volume (%s): " "Actor failed to report a paint volume", _clutter_actor_get_debug_name (self)); return; } _clutter_paint_volume_copy_static (pv, &priv->last_paint_volume); _clutter_paint_volume_transform_relative (&priv->last_paint_volume, NULL); /* eye coordinates */ priv->last_paint_volume_valid = TRUE; } /* This is the same as clutter_actor_add_effect except that it doesn't queue a redraw and it doesn't notify on the effect property */ static void _clutter_actor_add_effect_internal (ClutterActor *self, ClutterEffect *effect) { ClutterActorPrivate *priv = self->priv; if (priv->effects == NULL) { priv->effects = g_object_new (CLUTTER_TYPE_META_GROUP, NULL); priv->effects->actor = self; } _clutter_meta_group_add_meta (priv->effects, CLUTTER_ACTOR_META (effect)); } /* This is the same as clutter_actor_remove_effect except that it doesn't queue a redraw and it doesn't notify on the effect property */ static void _clutter_actor_remove_effect_internal (ClutterActor *self, ClutterEffect *effect) { ClutterActorPrivate *priv = self->priv; if (priv->effects == NULL) return; _clutter_meta_group_remove_meta (priv->effects, CLUTTER_ACTOR_META (effect)); if (_clutter_meta_group_peek_metas (priv->effects) == NULL) g_clear_object (&priv->effects); } static gboolean needs_flatten_effect (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT)) return FALSE; /* We need to enable the effect immediately even in ON_IDLE because that can * only be implemented efficiently within the effect itself. * If it was implemented here using just priv->is_dirty then we would lose * the ability to animate opacity without repaints. */ if ((priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ALWAYS) || (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE)) return TRUE; else if (priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY) { if (clutter_actor_get_paint_opacity (self) < 255 && clutter_actor_has_overlaps (self)) return TRUE; } return FALSE; } static void add_or_remove_flatten_effect (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; /* Add or remove the flatten effect depending on the offscreen-redirect property. */ if (needs_flatten_effect (self)) { if (priv->flatten_effect == NULL) { ClutterActorMeta *actor_meta; gint priority; priv->flatten_effect = _clutter_flatten_effect_new (); /* Keep a reference to the effect so that we can queue redraws from it */ g_object_ref_sink (priv->flatten_effect); /* Set the priority of the effect to high so that it will always be applied to the actor first. It uses an internal priority so that it won't be visible to applications */ actor_meta = CLUTTER_ACTOR_META (priv->flatten_effect); priority = CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH; _clutter_actor_meta_set_priority (actor_meta, priority); /* This will add the effect without queueing a redraw */ _clutter_actor_add_effect_internal (self, priv->flatten_effect); } } else { if (priv->flatten_effect != NULL) { /* Destroy the effect so that it will lose its fbo cache of the actor */ _clutter_actor_remove_effect_internal (self, priv->flatten_effect); g_clear_object (&priv->flatten_effect); } } } static void clutter_actor_real_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { ClutterActorPrivate *priv = actor->priv; ClutterActor *iter; for (iter = priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { CLUTTER_NOTE (PAINT, "Painting %s, child of %s, at { %.2f, %.2f - %.2f x %.2f }", _clutter_actor_get_debug_name (iter), _clutter_actor_get_debug_name (actor), iter->priv->allocation.x1, iter->priv->allocation.y1, iter->priv->allocation.x2 - iter->priv->allocation.x1, iter->priv->allocation.y2 - iter->priv->allocation.y1); clutter_actor_paint (iter, paint_context); } } static gboolean clutter_actor_paint_node (ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ClutterActorPrivate *priv = actor->priv; ClutterActorBox box; ClutterColor bg_color; box.x1 = 0.f; box.y1 = 0.f; box.x2 = clutter_actor_box_get_width (&priv->allocation); box.y2 = clutter_actor_box_get_height (&priv->allocation); bg_color = priv->bg_color; if (CLUTTER_ACTOR_IS_TOPLEVEL (actor)) { ClutterPaintNode *node; CoglFramebuffer *fb; CoglBufferBit clear_flags; fb = clutter_paint_context_get_base_framebuffer (paint_context); if (clutter_stage_get_use_alpha (CLUTTER_STAGE (actor))) { bg_color.alpha = priv->opacity * priv->bg_color.alpha / 255; } else bg_color.alpha = 255; CLUTTER_NOTE (PAINT, "Stage clear color: (%d, %d, %d, %d)", bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha); clear_flags = COGL_BUFFER_BIT_DEPTH; node = clutter_root_node_new (fb, &bg_color, clear_flags); clutter_paint_node_set_static_name (node, "stageClear"); clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); } else if (priv->bg_color_set && !clutter_color_equal (&priv->bg_color, CLUTTER_COLOR_Transparent)) { ClutterPaintNode *node; bg_color.alpha = clutter_actor_get_paint_opacity_internal (actor) * priv->bg_color.alpha / 255; node = clutter_color_node_new (&bg_color); clutter_paint_node_set_static_name (node, "backgroundColor"); clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); } if (priv->content != NULL) _clutter_content_paint_content (priv->content, actor, root, paint_context); if (CLUTTER_ACTOR_GET_CLASS (actor)->paint_node != NULL) CLUTTER_ACTOR_GET_CLASS (actor)->paint_node (actor, root); if (clutter_paint_node_get_n_children (root) == 0) return FALSE; #ifdef CLUTTER_ENABLE_DEBUG if (CLUTTER_HAS_DEBUG (PAINT)) { /* dump the tree only if we have one */ _clutter_paint_node_dump_tree (root); } #endif /* CLUTTER_ENABLE_DEBUG */ clutter_paint_node_paint (root, paint_context); return TRUE; } /** * clutter_actor_paint: * @self: A #ClutterActor * * Renders the actor to display. * * This function should not be called directly by applications. * Call clutter_actor_queue_redraw() to queue paints, instead. * * This function is context-aware, and will either cause a * regular paint or a pick paint. * * This function will emit the #ClutterActor::paint signal or * the #ClutterActor::pick signal, depending on the context. * * This function does not paint the actor if the actor is set to 0, * unless it is performing a pick paint. */ void clutter_actor_paint (ClutterActor *self, ClutterPaintContext *paint_context) { g_autoptr (ClutterPaintNode) actor_node = NULL; g_autoptr (ClutterPaintNode) root_node = NULL; ClutterActorPrivate *priv; ClutterActorBox clip; gboolean culling_inhibited; gboolean clip_set = FALSE; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; priv = self->priv; priv->propagated_one_redraw = FALSE; /* It's an important optimization that we consider painting of * actors with 0 opacity to be a NOP... */ if (/* ignore top-levels, since they might be transparent */ !CLUTTER_ACTOR_IS_TOPLEVEL (self) && /* Use the override opacity if its been set */ ((priv->opacity_override >= 0) ? priv->opacity_override : priv->opacity) == 0) return; /* if we aren't paintable (not in a toplevel with all * parents paintable) then do nothing. */ if (!CLUTTER_ACTOR_IS_MAPPED (self)) return; clutter_actor_ensure_resource_scale (self); actor_node = clutter_actor_node_new (self); root_node = clutter_paint_node_ref (actor_node); if (priv->has_clip) { clip.x1 = priv->clip.origin.x; clip.y1 = priv->clip.origin.y; clip.x2 = priv->clip.origin.x + priv->clip.size.width; clip.y2 = priv->clip.origin.y + priv->clip.size.height; clip_set = TRUE; } else if (priv->clip_to_allocation) { clip.x1 = 0.f; clip.y1 = 0.f; clip.x2 = priv->allocation.x2 - priv->allocation.x1; clip.y2 = priv->allocation.y2 - priv->allocation.y1; clip_set = TRUE; } if (clip_set) { ClutterPaintNode *clip_node; clip_node = clutter_clip_node_new (); clutter_paint_node_add_rectangle (clip_node, &clip); clutter_paint_node_add_child (clip_node, root_node); clutter_paint_node_unref (root_node); root_node = g_steal_pointer (&clip_node); } if (priv->enable_model_view_transform) { ClutterPaintNode *transform_node; CoglMatrix transform; clutter_actor_get_transform (self, &transform); if (!cogl_matrix_is_identity (&transform)) { transform_node = clutter_transform_node_new (&transform); clutter_paint_node_add_child (transform_node, root_node); clutter_paint_node_unref (root_node); root_node = g_steal_pointer (&transform_node); } #ifdef CLUTTER_ENABLE_DEBUG /* Catch when out-of-band transforms have been made by actors not as part * of an apply_transform vfunc... */ if (G_UNLIKELY (clutter_debug_flags & CLUTTER_DEBUG_OOB_TRANSFORMS)) { CoglMatrix expected_matrix; _clutter_actor_get_relative_transformation_matrix (self, NULL, &expected_matrix); if (!cogl_matrix_equal (&transform, &expected_matrix)) { GString *buf = g_string_sized_new (1024); ClutterActor *parent; parent = self; while (parent != NULL) { g_string_append (buf, _clutter_actor_get_debug_name (parent)); if (parent->priv->parent != NULL) g_string_append (buf, "->"); parent = parent->priv->parent; } g_warning ("Unexpected transform found when painting actor " "\"%s\". This will be caused by one of the actor's " "ancestors (%s) using the Cogl API directly to transform " "children instead of using ::apply_transform().", _clutter_actor_get_debug_name (self), buf->str); g_string_free (buf, TRUE); } } #endif /* CLUTTER_ENABLE_DEBUG */ } /* We check whether we need to add the flatten effect before * each paint so that we can avoid having a mechanism for * applications to notify when the value of the * has_overlaps virtual changes. */ add_or_remove_flatten_effect (self); /* We save the current paint volume so that the next time the * actor queues a redraw we can constrain the redraw to just * cover the union of the new bounding box and the old. * * We also fetch the current paint volume to perform culling so * we can avoid painting actors outside the current clip region. * * If we are painting inside a clone, we should neither update * the paint volume or use it to cull painting, since the paint * box represents the location of the source actor on the * screen. * * XXX: We are starting to do a lot of vertex transforms on * the CPU in a typical paint, so at some point we should * audit these and consider caching some things. * * NB: We don't perform culling while picking at this point because * clutter-stage.c doesn't setup the clipping planes appropriately. * * NB: We don't want to update the last-paint-volume during picking * because the last-paint-volume is used to determine the old screen * space location of an actor that has moved so we can know the * minimal region to redraw to clear an old view of the actor. If we * update this during picking then by the time we come around to * paint then the last-paint-volume would likely represent the new * actor position not the old. */ culling_inhibited = priv->inhibit_culling_counter > 0; if (!culling_inhibited && !in_clone_paint ()) { gboolean success; /* annoyingly gcc warns if uninitialized even though * the initialization is redundant :-( */ ClutterCullResult result = CLUTTER_CULL_RESULT_IN; if (G_LIKELY ((clutter_paint_debug_flags & (CLUTTER_DEBUG_DISABLE_CULLING | CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS)) != (CLUTTER_DEBUG_DISABLE_CULLING | CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))) _clutter_actor_update_last_paint_volume (self); success = cull_actor (self, paint_context, &result); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS)) _clutter_actor_paint_cull_result (self, success, result, actor_node); else if (result == CLUTTER_CULL_RESULT_OUT && success) return; } if (priv->effects == NULL) priv->next_effect_to_paint = NULL; else priv->next_effect_to_paint = _clutter_meta_group_peek_metas (priv->effects); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES)) _clutter_actor_draw_paint_volume (self, actor_node); clutter_paint_node_paint (root_node, paint_context); /* If we make it here then the actor has run through a complete paint run including all the effects so it's no longer dirty */ priv->is_dirty = FALSE; } /** * clutter_actor_continue_paint: * @self: A #ClutterActor * * Run the next stage of the paint sequence. This function should only * be called within the implementation of the ‘run’ virtual of a * #ClutterEffect. It will cause the run method of the next effect to * be applied, or it will paint the actual actor if the current effect * is the last effect in the chain. * * Since: 1.8 */ void clutter_actor_continue_paint (ClutterActor *self, ClutterPaintContext *paint_context) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* This should only be called from with in the ‘run’ implementation of a ClutterEffect */ g_return_if_fail (CLUTTER_ACTOR_IN_PAINT (self)); priv = self->priv; /* Skip any effects that are disabled */ while (priv->next_effect_to_paint && !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data)) priv->next_effect_to_paint = priv->next_effect_to_paint->next; /* If this has come from the last effect then we'll just paint the actual actor */ if (priv->next_effect_to_paint == NULL) { CoglFramebuffer *framebuffer; ClutterPaintNode *dummy; /* XXX - this will go away in 2.0, when we can get rid of this * stuff and switch to a pure retained render tree of PaintNodes * for the entire frame, starting from the Stage; the paint() * virtual function can then be called directly. */ framebuffer = clutter_paint_context_get_base_framebuffer (paint_context); dummy = _clutter_dummy_node_new (self, framebuffer); clutter_paint_node_set_static_name (dummy, "Root"); /* XXX - for 1.12, we use the return value of paint_node() to * decide whether we should emit the ::paint signal. */ clutter_actor_paint_node (self, dummy, paint_context); clutter_paint_node_unref (dummy); /* XXX:2.0 - Call the paint() virtual directly */ if (!(clutter_paint_context_get_paint_flags (paint_context) & CLUTTER_PAINT_FLAG_NO_PAINT_SIGNAL) && g_signal_has_handler_pending (self, actor_signals[PAINT], 0, TRUE)) g_signal_emit (self, actor_signals[PAINT], 0, paint_context); else CLUTTER_ACTOR_GET_CLASS (self)->paint (self, paint_context); } else { ClutterEffect *old_current_effect; ClutterEffectPaintFlags run_flags = 0; /* Cache the current effect so that we can put it back before returning */ old_current_effect = priv->current_effect; priv->current_effect = priv->next_effect_to_paint->data; priv->next_effect_to_paint = priv->next_effect_to_paint->next; if (priv->is_dirty) { /* If there's an effect queued with this redraw then all * effects up to that one will be considered dirty. It * is expected the queued effect will paint the cached * image and not call clutter_actor_continue_paint again * (although it should work ok if it does) */ if (priv->effect_to_redraw == NULL || priv->current_effect != priv->effect_to_redraw) run_flags |= CLUTTER_EFFECT_PAINT_ACTOR_DIRTY; } if (priv->current_effect == priv->flatten_effect && priv->offscreen_redirect & CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE && run_flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY) run_flags |= CLUTTER_EFFECT_PAINT_BYPASS_EFFECT; _clutter_effect_paint (priv->current_effect, paint_context, run_flags); priv->current_effect = old_current_effect; } } /** * clutter_actor_pick: * @actor: A #ClutterActor * * Asks @actor to perform a pick. */ void clutter_actor_pick (ClutterActor *actor, ClutterPickContext *pick_context) { ClutterActorPrivate *priv; CoglFramebuffer *framebuffer; ClutterActorBox clip; gboolean clip_set = FALSE; if (CLUTTER_ACTOR_IN_DESTRUCTION (actor)) return; priv = actor->priv; /* if we aren't paintable (not in a toplevel with all * parents paintable) then do nothing. */ if (!CLUTTER_ACTOR_IS_MAPPED (actor)) return; clutter_actor_ensure_resource_scale (actor); /* mark that we are in the paint process */ CLUTTER_SET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK); framebuffer = clutter_pick_context_get_framebuffer (pick_context); cogl_framebuffer_push_matrix (framebuffer); if (priv->enable_model_view_transform) { CoglMatrix matrix; cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix); _clutter_actor_apply_modelview_transform (actor, &matrix); cogl_framebuffer_set_modelview_matrix (framebuffer, &matrix); } if (priv->has_clip) { clip.x1 = priv->clip.origin.x; clip.y1 = priv->clip.origin.y; clip.x2 = priv->clip.origin.x + priv->clip.size.width; clip.y2 = priv->clip.origin.y + priv->clip.size.height; clip_set = TRUE; } else if (priv->clip_to_allocation) { clip.x1 = 0.f; clip.y1 = 0.f; clip.x2 = priv->allocation.x2 - priv->allocation.x1; clip.y2 = priv->allocation.y2 - priv->allocation.y1; clip_set = TRUE; } if (clip_set) clip_set = _clutter_actor_push_pick_clip (actor, pick_context, &clip); priv->next_effect_to_paint = NULL; if (priv->effects) { priv->next_effect_to_paint = _clutter_meta_group_peek_metas (priv->effects); } clutter_actor_continue_pick (actor, pick_context); if (clip_set) _clutter_actor_pop_pick_clip (actor); cogl_framebuffer_pop_matrix (framebuffer); /* paint sequence complete */ CLUTTER_UNSET_PRIVATE_FLAGS (actor, CLUTTER_IN_PICK); } /** * clutter_actor_continue_pick: * @actor: A #ClutterActor * * Run the next stage of the pick sequence. This function should only * be called within the implementation of the ‘pick’ virtual of a * #ClutterEffect. It will cause the run method of the next effect to * be applied, or it will pick the actual actor if the current effect * is the last effect in the chain. */ void clutter_actor_continue_pick (ClutterActor *actor, ClutterPickContext *pick_context) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (CLUTTER_ACTOR_IN_PICK (actor)); priv = actor->priv; /* Skip any effects that are disabled */ while (priv->next_effect_to_paint && !clutter_actor_meta_get_enabled (priv->next_effect_to_paint->data)) priv->next_effect_to_paint = priv->next_effect_to_paint->next; /* If this has come from the last effect then we'll just pick the * actual actor. */ if (priv->next_effect_to_paint == NULL) { /* The actor will log a silhouette of itself to the stage pick log. * * XXX:2.0 - Call the pick() virtual directly */ if (g_signal_has_handler_pending (actor, actor_signals[PICK], 0, TRUE)) g_signal_emit (actor, actor_signals[PICK], 0, pick_context); else CLUTTER_ACTOR_GET_CLASS (actor)->pick (actor, pick_context); } else { ClutterEffect *old_current_effect; /* Cache the current effect so that we can put it back before * returning. */ old_current_effect = priv->current_effect; priv->current_effect = priv->next_effect_to_paint->data; priv->next_effect_to_paint = priv->next_effect_to_paint->next; _clutter_effect_pick (priv->current_effect, pick_context); priv->current_effect = old_current_effect; } } static void _clutter_actor_stop_transitions (ClutterActor *self) { const ClutterAnimationInfo *info; GHashTableIter iter; gpointer value; info = _clutter_actor_get_animation_info_or_defaults (self); if (info->transitions == NULL) return; g_hash_table_iter_init (&iter, info->transitions); while (g_hash_table_iter_next (&iter, NULL, &value)) { TransitionClosure *closure = value; if (clutter_transition_get_remove_on_complete (closure->transition)) { g_hash_table_iter_remove (&iter); } else { /* otherwise we stop it, and the transition will be removed * later, either by the actor's destruction or by explicit * removal */ clutter_timeline_stop (CLUTTER_TIMELINE (closure->transition)); } } } static ClutterActorTraverseVisitFlags invalidate_queue_redraw_entry (ClutterActor *self, int depth, gpointer user_data) { ClutterActorPrivate *priv = self->priv; if (priv->queue_redraw_entry != NULL) { _clutter_stage_queue_redraw_entry_invalidate (priv->queue_redraw_entry); priv->queue_redraw_entry = NULL; } return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } static inline void remove_child (ClutterActor *self, ClutterActor *child) { ClutterActor *prev_sibling, *next_sibling; prev_sibling = child->priv->prev_sibling; next_sibling = child->priv->next_sibling; if (prev_sibling != NULL) prev_sibling->priv->next_sibling = next_sibling; if (next_sibling != NULL) next_sibling->priv->prev_sibling = prev_sibling; if (self->priv->first_child == child) self->priv->first_child = next_sibling; if (self->priv->last_child == child) self->priv->last_child = prev_sibling; child->priv->parent = NULL; child->priv->prev_sibling = NULL; child->priv->next_sibling = NULL; } typedef enum { REMOVE_CHILD_DESTROY_META = 1 << 0, REMOVE_CHILD_EMIT_PARENT_SET = 1 << 1, REMOVE_CHILD_EMIT_ACTOR_REMOVED = 1 << 2, REMOVE_CHILD_CHECK_STATE = 1 << 3, REMOVE_CHILD_FLUSH_QUEUE = 1 << 4, REMOVE_CHILD_NOTIFY_FIRST_LAST = 1 << 5, REMOVE_CHILD_STOP_TRANSITIONS = 1 << 6, /* default flags for public API */ REMOVE_CHILD_DEFAULT_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_DESTROY_META | REMOVE_CHILD_EMIT_PARENT_SET | REMOVE_CHILD_EMIT_ACTOR_REMOVED | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_NOTIFY_FIRST_LAST, /* flags for legacy/deprecated API */ REMOVE_CHILD_LEGACY_FLAGS = REMOVE_CHILD_STOP_TRANSITIONS | REMOVE_CHILD_CHECK_STATE | REMOVE_CHILD_FLUSH_QUEUE | REMOVE_CHILD_EMIT_PARENT_SET | REMOVE_CHILD_NOTIFY_FIRST_LAST } ClutterActorRemoveChildFlags; /*< private > * clutter_actor_remove_child_internal: * @self: a #ClutterActor * @child: the child of @self that has to be removed * @flags: control the removal operations * * Removes @child from the list of children of @self. */ static void clutter_actor_remove_child_internal (ClutterActor *self, ClutterActor *child, ClutterActorRemoveChildFlags flags) { ClutterActor *old_first, *old_last; gboolean destroy_meta, emit_parent_set, emit_actor_removed, check_state; gboolean flush_queue; gboolean notify_first_last; gboolean was_mapped; gboolean stop_transitions; GObject *obj; if (self == child) { g_warning ("Cannot remove actor '%s' from itself.", _clutter_actor_get_debug_name (self)); return; } destroy_meta = (flags & REMOVE_CHILD_DESTROY_META) != 0; emit_parent_set = (flags & REMOVE_CHILD_EMIT_PARENT_SET) != 0; emit_actor_removed = (flags & REMOVE_CHILD_EMIT_ACTOR_REMOVED) != 0; check_state = (flags & REMOVE_CHILD_CHECK_STATE) != 0; flush_queue = (flags & REMOVE_CHILD_FLUSH_QUEUE) != 0; notify_first_last = (flags & REMOVE_CHILD_NOTIFY_FIRST_LAST) != 0; stop_transitions = (flags & REMOVE_CHILD_STOP_TRANSITIONS) != 0; obj = G_OBJECT (self); g_object_freeze_notify (obj); if (stop_transitions) _clutter_actor_stop_transitions (child); if (destroy_meta) clutter_container_destroy_child_meta (CLUTTER_CONTAINER (self), child); if (check_state) { was_mapped = CLUTTER_ACTOR_IS_MAPPED (child); /* we need to unrealize *before* we set parent_actor to NULL, * because in an unrealize method actors are dissociating from the * stage, which means they need to be able to * clutter_actor_get_stage(). * * yhis should unmap and unrealize, unless we're reparenting. */ clutter_actor_update_map_state (child, MAP_STATE_MAKE_UNREALIZED); } else was_mapped = FALSE; if (flush_queue) { /* We take this opportunity to invalidate any queue redraw entry * associated with the actor and descendants since we won't be able to * determine the appropriate stage after this. * * we do this after we updated the mapped state because actors might * end up queueing redraws inside their mapped/unmapped virtual * functions, and if we invalidate the redraw entry we could end up * with an inconsistent state and weird memory corruption. see * bugs: * * http://bugzilla.clutter-project.org/show_bug.cgi?id=2621 * https://bugzilla.gnome.org/show_bug.cgi?id=652036 */ _clutter_actor_traverse (child, 0, invalidate_queue_redraw_entry, NULL, NULL); } old_first = self->priv->first_child; old_last = self->priv->last_child; remove_child (self, child); self->priv->n_children -= 1; self->priv->age += 1; if (self->priv->in_cloned_branch) clutter_actor_pop_in_cloned_branch (child, self->priv->in_cloned_branch); /* if the child that got removed was visible and set to * expand then we want to reset the parent's state in * case the child was the only thing that was making it * expand. */ if (CLUTTER_ACTOR_IS_VISIBLE (child) && (child->priv->needs_compute_expand || child->priv->needs_x_expand || child->priv->needs_y_expand)) { clutter_actor_queue_compute_expand (self); } if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child) && !CLUTTER_ACTOR_IN_DESTRUCTION (child)) { child->priv->needs_compute_resource_scale = TRUE; g_signal_emit (child, actor_signals[PARENT_SET], 0, self); } /* if the child was mapped then we need to relayout ourselves to account * for the removed child */ if (was_mapped) clutter_actor_queue_relayout (self); /* we need to emit the signal before dropping the reference */ if (emit_actor_removed) _clutter_container_emit_actor_removed (CLUTTER_CONTAINER (self), child); if (notify_first_last) { if (old_first != self->priv->first_child) g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]); if (old_last != self->priv->last_child) g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]); } g_object_thaw_notify (obj); /* remove the reference we acquired in clutter_actor_add_child() */ g_object_unref (child); } static const ClutterTransformInfo default_transform_info = { 0.0, { 0, }, /* rotation-x */ 0.0, { 0, }, /* rotation-y */ 0.0, { 0, }, /* rotation-z */ 1.0, 1.0, 1.0, { 0, }, /* scale */ { 0, }, /* anchor XXX:2.0 - remove*/ GRAPHENE_POINT3D_INIT_ZERO, /* translation */ 0.f, /* z-position */ GRAPHENE_POINT_INIT_ZERO, /* pivot */ 0.f, /* pivot-z */ CLUTTER_MATRIX_INIT_IDENTITY, FALSE, /* transform */ CLUTTER_MATRIX_INIT_IDENTITY, FALSE, /* child-transform */ }; /*< private > * _clutter_actor_get_transform_info_or_defaults: * @self: a #ClutterActor * * Retrieves the ClutterTransformInfo structure associated to an actor. * * If the actor does not have a ClutterTransformInfo structure associated * to it, then the default structure will be returned. * * This function should only be used for getters. * * Return value: a const pointer to the ClutterTransformInfo structure */ const ClutterTransformInfo * _clutter_actor_get_transform_info_or_defaults (ClutterActor *self) { ClutterTransformInfo *info; info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info); if (info != NULL) return info; return &default_transform_info; } static void clutter_transform_info_free (gpointer data) { if (data != NULL) g_slice_free (ClutterTransformInfo, data); } /*< private > * _clutter_actor_get_transform_info: * @self: a #ClutterActor * * Retrieves a pointer to the ClutterTransformInfo structure. * * If the actor does not have a ClutterTransformInfo associated to it, one * will be created and initialized to the default values. * * This function should be used for setters. * * For getters, you should use _clutter_actor_get_transform_info_or_defaults() * instead. * * Return value: (transfer none): a pointer to the ClutterTransformInfo * structure */ ClutterTransformInfo * _clutter_actor_get_transform_info (ClutterActor *self) { ClutterTransformInfo *info; info = g_object_get_qdata (G_OBJECT (self), quark_actor_transform_info); if (info == NULL) { info = g_slice_new (ClutterTransformInfo); *info = default_transform_info; g_object_set_qdata_full (G_OBJECT (self), quark_actor_transform_info, info, clutter_transform_info_free); } return info; } static inline void clutter_actor_set_pivot_point_internal (ClutterActor *self, const graphene_point_t *pivot) { ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); info->pivot = *pivot; self->priv->transform_valid = FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT]); clutter_actor_queue_redraw (self); } static inline void clutter_actor_set_pivot_point_z_internal (ClutterActor *self, float pivot_z) { ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); info->pivot_z = pivot_z; self->priv->transform_valid = FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PIVOT_POINT_Z]); clutter_actor_queue_redraw (self); } /*< private > * clutter_actor_set_translation_internal: * @self: a #ClutterActor * @axis: the axis of the translation to change * @angle: the translation as a value along @axis * * Sets the translation on the given @axis */ static void clutter_actor_set_translation_internal (ClutterActor *self, gfloat value, GParamSpec *pspec) { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (pspec == obj_props[PROP_TRANSLATION_X]) info->translation.x = value; else if (pspec == obj_props[PROP_TRANSLATION_Y]) info->translation.y = value; else if (pspec == obj_props[PROP_TRANSLATION_Z]) info->translation.z = value; else g_assert_not_reached (); self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, pspec); } static inline void clutter_actor_set_translation_factor (ClutterActor *self, ClutterRotateAxis axis, gdouble value) { const ClutterTransformInfo *info; const float *translate_p = NULL; GParamSpec *pspec = NULL; info = _clutter_actor_get_transform_info_or_defaults (self); switch (axis) { case CLUTTER_X_AXIS: pspec = obj_props[PROP_TRANSLATION_X]; translate_p = &info->translation.x; break; case CLUTTER_Y_AXIS: pspec = obj_props[PROP_TRANSLATION_Y]; translate_p = &info->translation.y; break; case CLUTTER_Z_AXIS: pspec = obj_props[PROP_TRANSLATION_Z]; translate_p = &info->translation.z; break; } g_assert (pspec != NULL); g_assert (translate_p != NULL); _clutter_actor_create_transition (self, pspec, *translate_p, value); } /** * clutter_actor_set_translation: * @self: a #ClutterActor * @translate_x: the translation along the X axis * @translate_y: the translation along the Y axis * @translate_z: the translation along the Z axis * * Sets an additional translation transformation on a #ClutterActor, * relative to the #ClutterActor:pivot-point. * * Since: 1.12 */ void clutter_actor_set_translation (ClutterActor *self, gfloat translate_x, gfloat translate_y, gfloat translate_z) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_translation_factor (self, CLUTTER_X_AXIS, translate_x); clutter_actor_set_translation_factor (self, CLUTTER_Y_AXIS, translate_y); clutter_actor_set_translation_factor (self, CLUTTER_Z_AXIS, translate_z); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_get_translation: * @self: a #ClutterActor * @translate_x: (out) (allow-none): return location for the X component * of the translation, or %NULL * @translate_y: (out) (allow-none): return location for the Y component * of the translation, or %NULL * @translate_z: (out) (allow-none): return location for the Z component * of the translation, or %NULL * * Retrieves the translation set using clutter_actor_set_translation(). * * Since: 1.12 */ void clutter_actor_get_translation (ClutterActor *self, gfloat *translate_x, gfloat *translate_y, gfloat *translate_z) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); if (translate_x != NULL) *translate_x = info->translation.x; if (translate_y != NULL) *translate_y = info->translation.y; if (translate_z != NULL) *translate_z = info->translation.z; } /*< private > * clutter_actor_set_rotation_angle_internal: * @self: a #ClutterActor * @angle: the angle of rotation * @pspec: the #GParamSpec of the property * * Sets the rotation angle on the given axis without affecting the * rotation center point. */ static inline void clutter_actor_set_rotation_angle_internal (ClutterActor *self, gdouble angle, GParamSpec *pspec) { ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (pspec == obj_props[PROP_ROTATION_ANGLE_X]) info->rx_angle = angle; else if (pspec == obj_props[PROP_ROTATION_ANGLE_Y]) info->ry_angle = angle; else if (pspec == obj_props[PROP_ROTATION_ANGLE_Z]) info->rz_angle = angle; else g_assert_not_reached (); self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), pspec); } /** * clutter_actor_set_rotation_angle: * @self: a #ClutterActor * @axis: the axis to set the angle one * @angle: the angle of rotation, in degrees * * Sets the @angle of rotation of a #ClutterActor on the given @axis. * * This function is a convenience for setting the rotation properties * #ClutterActor:rotation-angle-x, #ClutterActor:rotation-angle-y, * and #ClutterActor:rotation-angle-z. * * The center of rotation is established by the #ClutterActor:pivot-point * property. * * Since: 1.12 */ void clutter_actor_set_rotation_angle (ClutterActor *self, ClutterRotateAxis axis, gdouble angle) { const ClutterTransformInfo *info; const double *cur_angle_p = NULL; GParamSpec *pspec = NULL; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); switch (axis) { case CLUTTER_X_AXIS: cur_angle_p = &info->rx_angle; pspec = obj_props[PROP_ROTATION_ANGLE_X]; break; case CLUTTER_Y_AXIS: cur_angle_p = &info->ry_angle; pspec = obj_props[PROP_ROTATION_ANGLE_Y]; break; case CLUTTER_Z_AXIS: cur_angle_p = &info->rz_angle; pspec = obj_props[PROP_ROTATION_ANGLE_Z]; break; } g_assert (pspec != NULL); g_assert (cur_angle_p != NULL); _clutter_actor_create_transition (self, pspec, *cur_angle_p, angle); } /** * clutter_actor_get_rotation_angle: * @self: a #ClutterActor * @axis: the axis of the rotation * * Retrieves the angle of rotation set by clutter_actor_set_rotation_angle(). * * Return value: the angle of rotation, in degrees * * Since: 1.12 */ gdouble clutter_actor_get_rotation_angle (ClutterActor *self, ClutterRotateAxis axis) { const ClutterTransformInfo *info; gdouble retval; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0); info = _clutter_actor_get_transform_info_or_defaults (self); switch (axis) { case CLUTTER_X_AXIS: retval = info->rx_angle; break; case CLUTTER_Y_AXIS: retval = info->ry_angle; break; case CLUTTER_Z_AXIS: retval = info->rz_angle; break; default: g_warn_if_reached (); retval = 0.0; break; } return retval; } /*< private > * clutter_actor_set_rotation_center_internal: * @self: a #ClutterActor * @axis: the axis of the center to change * @center: the coordinates of the rotation center * * Sets the rotation center on the given axis without affecting the * rotation angle. */ static inline void clutter_actor_set_rotation_center_internal (ClutterActor *self, ClutterRotateAxis axis, const graphene_point3d_t *center) { graphene_point3d_t v = GRAPHENE_POINT3D_INIT_ZERO; GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (center != NULL) v = *center; g_object_freeze_notify (obj); switch (axis) { case CLUTTER_X_AXIS: clutter_anchor_coord_set_units (&info->rx_center, v.x, v.y, v.z); g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_X]); break; case CLUTTER_Y_AXIS: clutter_anchor_coord_set_units (&info->ry_center, v.x, v.y, v.z); g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Y]); break; case CLUTTER_Z_AXIS: /* if the previously set rotation center was fractional, then * setting explicit coordinates will have to notify the * :rotation-center-z-gravity property as well */ if (info->rz_center.is_fractional) g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]); clutter_anchor_coord_set_units (&info->rz_center, v.x, v.y, v.z); g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]); break; } self->priv->transform_valid = FALSE; g_object_thaw_notify (obj); clutter_actor_queue_redraw (self); } static void clutter_actor_set_scale_factor_internal (ClutterActor *self, double factor, GParamSpec *pspec) { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (pspec == obj_props[PROP_SCALE_X]) info->scale_x = factor; else if (pspec == obj_props[PROP_SCALE_Y]) info->scale_y = factor; else if (pspec == obj_props[PROP_SCALE_Z]) info->scale_z = factor; else g_assert_not_reached (); self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, pspec); } static inline void clutter_actor_set_scale_factor (ClutterActor *self, ClutterRotateAxis axis, gdouble factor) { const ClutterTransformInfo *info; const double *scale_p = NULL; GParamSpec *pspec = NULL; info = _clutter_actor_get_transform_info_or_defaults (self); switch (axis) { case CLUTTER_X_AXIS: pspec = obj_props[PROP_SCALE_X]; scale_p = &info->scale_x; break; case CLUTTER_Y_AXIS: pspec = obj_props[PROP_SCALE_Y]; scale_p = &info->scale_y; break; case CLUTTER_Z_AXIS: pspec = obj_props[PROP_SCALE_Z]; scale_p = &info->scale_z; break; } g_assert (pspec != NULL); g_assert (scale_p != NULL); if (*scale_p != factor) _clutter_actor_create_transition (self, pspec, *scale_p, factor); } static inline void clutter_actor_set_scale_center (ClutterActor *self, ClutterRotateAxis axis, gfloat coord) { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; gfloat center_x, center_y; info = _clutter_actor_get_transform_info (self); g_object_freeze_notify (obj); /* get the current scale center coordinates */ clutter_anchor_coord_get_units (self, &info->scale_center, ¢er_x, ¢er_y, NULL); /* we need to notify this too, because setting explicit coordinates will * change the gravity as a side effect */ if (info->scale_center.is_fractional) g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]); switch (axis) { case CLUTTER_X_AXIS: clutter_anchor_coord_set_units (&info->scale_center, coord, center_y, 0); g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]); break; case CLUTTER_Y_AXIS: clutter_anchor_coord_set_units (&info->scale_center, center_x, coord, 0); g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]); break; default: g_assert_not_reached (); } self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_thaw_notify (obj); } static inline void clutter_actor_set_scale_gravity (ClutterActor *self, ClutterGravity gravity) { ClutterTransformInfo *info; GObject *obj; info = _clutter_actor_get_transform_info (self); obj = G_OBJECT (self); if (gravity == CLUTTER_GRAVITY_NONE) clutter_anchor_coord_set_units (&info->scale_center, 0, 0, 0); else clutter_anchor_coord_set_gravity (&info->scale_center, gravity); self->priv->transform_valid = FALSE; g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_X]); g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_CENTER_Y]); g_object_notify_by_pspec (obj, obj_props[PROP_SCALE_GRAVITY]); clutter_actor_queue_redraw (self); } /* XXX:2.0 - remove */ static inline void clutter_actor_set_anchor_coord (ClutterActor *self, ClutterRotateAxis axis, gfloat coord) { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; gfloat anchor_x, anchor_y; info = _clutter_actor_get_transform_info (self); g_object_freeze_notify (obj); clutter_anchor_coord_get_units (self, &info->anchor, &anchor_x, &anchor_y, NULL); if (info->anchor.is_fractional) g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]); switch (axis) { case CLUTTER_X_AXIS: clutter_anchor_coord_set_units (&info->anchor, coord, anchor_y, 0.0); g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]); break; case CLUTTER_Y_AXIS: clutter_anchor_coord_set_units (&info->anchor, anchor_x, coord, 0.0); g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]); break; default: g_assert_not_reached (); } self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_thaw_notify (obj); } static void clutter_actor_set_clip_rect (ClutterActor *self, const graphene_rect_t *clip) { ClutterActorPrivate *priv = self->priv; GObject *obj = G_OBJECT (self); if (clip != NULL) { priv->clip = *clip; priv->has_clip = TRUE; } else priv->has_clip = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]); g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]); } static void clutter_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterActor *actor = CLUTTER_ACTOR (object); ClutterActorPrivate *priv = actor->priv; switch (prop_id) { case PROP_X: clutter_actor_set_x (actor, g_value_get_float (value)); break; case PROP_Y: clutter_actor_set_y (actor, g_value_get_float (value)); break; case PROP_POSITION: { const graphene_point_t *pos = g_value_get_boxed (value); if (pos != NULL) clutter_actor_set_position (actor, pos->x, pos->y); else clutter_actor_set_fixed_position_set (actor, FALSE); } break; case PROP_WIDTH: clutter_actor_set_width (actor, g_value_get_float (value)); break; case PROP_HEIGHT: clutter_actor_set_height (actor, g_value_get_float (value)); break; case PROP_SIZE: { const graphene_size_t *size = g_value_get_boxed (value); if (size != NULL) clutter_actor_set_size (actor, size->width, size->height); else clutter_actor_set_size (actor, -1, -1); } break; case PROP_FIXED_X: clutter_actor_set_x (actor, g_value_get_float (value)); break; case PROP_FIXED_Y: clutter_actor_set_y (actor, g_value_get_float (value)); break; case PROP_FIXED_POSITION_SET: clutter_actor_set_fixed_position_set (actor, g_value_get_boolean (value)); break; case PROP_MIN_WIDTH: clutter_actor_set_min_width (actor, g_value_get_float (value)); break; case PROP_MIN_HEIGHT: clutter_actor_set_min_height (actor, g_value_get_float (value)); break; case PROP_NATURAL_WIDTH: clutter_actor_set_natural_width (actor, g_value_get_float (value)); break; case PROP_NATURAL_HEIGHT: clutter_actor_set_natural_height (actor, g_value_get_float (value)); break; case PROP_MIN_WIDTH_SET: clutter_actor_set_min_width_set (actor, g_value_get_boolean (value)); break; case PROP_MIN_HEIGHT_SET: clutter_actor_set_min_height_set (actor, g_value_get_boolean (value)); break; case PROP_NATURAL_WIDTH_SET: clutter_actor_set_natural_width_set (actor, g_value_get_boolean (value)); break; case PROP_NATURAL_HEIGHT_SET: clutter_actor_set_natural_height_set (actor, g_value_get_boolean (value)); break; case PROP_REQUEST_MODE: clutter_actor_set_request_mode (actor, g_value_get_enum (value)); break; case PROP_DEPTH: /* XXX:2.0 - remove */ clutter_actor_set_depth (actor, g_value_get_float (value)); break; case PROP_Z_POSITION: clutter_actor_set_z_position (actor, g_value_get_float (value)); break; case PROP_OPACITY: clutter_actor_set_opacity (actor, g_value_get_uint (value)); break; case PROP_OFFSCREEN_REDIRECT: clutter_actor_set_offscreen_redirect (actor, g_value_get_enum (value)); break; case PROP_NAME: clutter_actor_set_name (actor, g_value_get_string (value)); break; case PROP_VISIBLE: if (g_value_get_boolean (value) == TRUE) clutter_actor_show (actor); else clutter_actor_hide (actor); break; case PROP_PIVOT_POINT: { const graphene_point_t *pivot = g_value_get_boxed (value); if (pivot == NULL) pivot = graphene_point_zero (); clutter_actor_set_pivot_point (actor, pivot->x, pivot->y); } break; case PROP_PIVOT_POINT_Z: clutter_actor_set_pivot_point_z (actor, g_value_get_float (value)); break; case PROP_TRANSLATION_X: clutter_actor_set_translation_factor (actor, CLUTTER_X_AXIS, g_value_get_float (value)); break; case PROP_TRANSLATION_Y: clutter_actor_set_translation_factor (actor, CLUTTER_Y_AXIS, g_value_get_float (value)); break; case PROP_TRANSLATION_Z: clutter_actor_set_translation_factor (actor, CLUTTER_Z_AXIS, g_value_get_float (value)); break; case PROP_SCALE_X: clutter_actor_set_scale_factor (actor, CLUTTER_X_AXIS, g_value_get_double (value)); break; case PROP_SCALE_Y: clutter_actor_set_scale_factor (actor, CLUTTER_Y_AXIS, g_value_get_double (value)); break; case PROP_SCALE_Z: clutter_actor_set_scale_factor (actor, CLUTTER_Z_AXIS, g_value_get_double (value)); break; case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */ clutter_actor_set_scale_center (actor, CLUTTER_X_AXIS, g_value_get_float (value)); break; case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */ clutter_actor_set_scale_center (actor, CLUTTER_Y_AXIS, g_value_get_float (value)); break; case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */ clutter_actor_set_scale_gravity (actor, g_value_get_enum (value)); break; case PROP_CLIP_RECT: clutter_actor_set_clip_rect (actor, g_value_get_boxed (value)); break; case PROP_CLIP_TO_ALLOCATION: clutter_actor_set_clip_to_allocation (actor, g_value_get_boolean (value)); break; case PROP_REACTIVE: clutter_actor_set_reactive (actor, g_value_get_boolean (value)); break; case PROP_ROTATION_ANGLE_X: clutter_actor_set_rotation_angle (actor, CLUTTER_X_AXIS, g_value_get_double (value)); break; case PROP_ROTATION_ANGLE_Y: clutter_actor_set_rotation_angle (actor, CLUTTER_Y_AXIS, g_value_get_double (value)); break; case PROP_ROTATION_ANGLE_Z: clutter_actor_set_rotation_angle (actor, CLUTTER_Z_AXIS, g_value_get_double (value)); break; case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */ clutter_actor_set_rotation_center_internal (actor, CLUTTER_X_AXIS, g_value_get_boxed (value)); break; case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */ clutter_actor_set_rotation_center_internal (actor, CLUTTER_Y_AXIS, g_value_get_boxed (value)); break; case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */ clutter_actor_set_rotation_center_internal (actor, CLUTTER_Z_AXIS, g_value_get_boxed (value)); break; case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */ { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); clutter_actor_set_z_rotation_from_gravity (actor, info->rz_angle, g_value_get_enum (value)); } break; case PROP_ANCHOR_X: /* XXX:2.0 - remove */ clutter_actor_set_anchor_coord (actor, CLUTTER_X_AXIS, g_value_get_float (value)); break; case PROP_ANCHOR_Y: /* XXX:2.0 - remove */ clutter_actor_set_anchor_coord (actor, CLUTTER_Y_AXIS, g_value_get_float (value)); break; case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */ clutter_actor_set_anchor_point_from_gravity (actor, g_value_get_enum (value)); break; case PROP_TRANSFORM: clutter_actor_set_transform (actor, g_value_get_boxed (value)); break; case PROP_CHILD_TRANSFORM: clutter_actor_set_child_transform (actor, g_value_get_boxed (value)); break; case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */ priv->show_on_set_parent = g_value_get_boolean (value); break; case PROP_TEXT_DIRECTION: clutter_actor_set_text_direction (actor, g_value_get_enum (value)); break; case PROP_ACTIONS: clutter_actor_add_action (actor, g_value_get_object (value)); break; case PROP_CONSTRAINTS: clutter_actor_add_constraint (actor, g_value_get_object (value)); break; case PROP_EFFECT: clutter_actor_add_effect (actor, g_value_get_object (value)); break; case PROP_LAYOUT_MANAGER: clutter_actor_set_layout_manager (actor, g_value_get_object (value)); break; case PROP_X_EXPAND: clutter_actor_set_x_expand (actor, g_value_get_boolean (value)); break; case PROP_Y_EXPAND: clutter_actor_set_y_expand (actor, g_value_get_boolean (value)); break; case PROP_X_ALIGN: clutter_actor_set_x_align (actor, g_value_get_enum (value)); break; case PROP_Y_ALIGN: clutter_actor_set_y_align (actor, g_value_get_enum (value)); break; case PROP_MARGIN_TOP: clutter_actor_set_margin_top (actor, g_value_get_float (value)); break; case PROP_MARGIN_BOTTOM: clutter_actor_set_margin_bottom (actor, g_value_get_float (value)); break; case PROP_MARGIN_LEFT: clutter_actor_set_margin_left (actor, g_value_get_float (value)); break; case PROP_MARGIN_RIGHT: clutter_actor_set_margin_right (actor, g_value_get_float (value)); break; case PROP_BACKGROUND_COLOR: clutter_actor_set_background_color (actor, g_value_get_boxed (value)); break; case PROP_CONTENT: clutter_actor_set_content (actor, g_value_get_object (value)); break; case PROP_CONTENT_GRAVITY: clutter_actor_set_content_gravity (actor, g_value_get_enum (value)); break; case PROP_MINIFICATION_FILTER: clutter_actor_set_content_scaling_filters (actor, g_value_get_enum (value), actor->priv->mag_filter); break; case PROP_MAGNIFICATION_FILTER: clutter_actor_set_content_scaling_filters (actor, actor->priv->min_filter, g_value_get_enum (value)); break; case PROP_CONTENT_REPEAT: clutter_actor_set_content_repeat (actor, g_value_get_flags (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterActor *actor = CLUTTER_ACTOR (object); ClutterActorPrivate *priv = actor->priv; switch (prop_id) { case PROP_X: g_value_set_float (value, clutter_actor_get_x (actor)); break; case PROP_Y: g_value_set_float (value, clutter_actor_get_y (actor)); break; case PROP_POSITION: { graphene_point_t position; graphene_point_init (&position, clutter_actor_get_x (actor), clutter_actor_get_y (actor)); g_value_set_boxed (value, &position); } break; case PROP_WIDTH: g_value_set_float (value, clutter_actor_get_width (actor)); break; case PROP_HEIGHT: g_value_set_float (value, clutter_actor_get_height (actor)); break; case PROP_SIZE: { graphene_size_t size; graphene_size_init (&size, clutter_actor_get_width (actor), clutter_actor_get_height (actor)); g_value_set_boxed (value, &size); } break; case PROP_FIXED_X: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->fixed_pos.x); } break; case PROP_FIXED_Y: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->fixed_pos.y); } break; case PROP_FIXED_POSITION_SET: g_value_set_boolean (value, priv->position_set); break; case PROP_MIN_WIDTH: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->minimum.width); } break; case PROP_MIN_HEIGHT: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->minimum.height); } break; case PROP_NATURAL_WIDTH: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->natural.width); } break; case PROP_NATURAL_HEIGHT: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->natural.height); } break; case PROP_MIN_WIDTH_SET: g_value_set_boolean (value, priv->min_width_set); break; case PROP_MIN_HEIGHT_SET: g_value_set_boolean (value, priv->min_height_set); break; case PROP_NATURAL_WIDTH_SET: g_value_set_boolean (value, priv->natural_width_set); break; case PROP_NATURAL_HEIGHT_SET: g_value_set_boolean (value, priv->natural_height_set); break; case PROP_REQUEST_MODE: g_value_set_enum (value, priv->request_mode); break; case PROP_ALLOCATION: g_value_set_boxed (value, &priv->allocation); break; case PROP_DEPTH: /* XXX:2.0 - remove */ g_value_set_float (value, clutter_actor_get_depth (actor)); break; case PROP_Z_POSITION: g_value_set_float (value, clutter_actor_get_z_position (actor)); break; case PROP_OPACITY: g_value_set_uint (value, priv->opacity); break; case PROP_OFFSCREEN_REDIRECT: g_value_set_flags (value, priv->offscreen_redirect); break; case PROP_NAME: g_value_set_string (value, priv->name); break; case PROP_VISIBLE: g_value_set_boolean (value, CLUTTER_ACTOR_IS_VISIBLE (actor)); break; case PROP_MAPPED: g_value_set_boolean (value, CLUTTER_ACTOR_IS_MAPPED (actor)); break; case PROP_REALIZED: g_value_set_boolean (value, CLUTTER_ACTOR_IS_REALIZED (actor)); break; case PROP_HAS_CLIP: g_value_set_boolean (value, priv->has_clip); break; case PROP_CLIP_RECT: g_value_set_boxed (value, &priv->clip); break; case PROP_CLIP_TO_ALLOCATION: g_value_set_boolean (value, priv->clip_to_allocation); break; case PROP_PIVOT_POINT: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_boxed (value, &info->pivot); } break; case PROP_PIVOT_POINT_Z: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_float (value, info->pivot_z); } break; case PROP_TRANSLATION_X: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_float (value, info->translation.x); } break; case PROP_TRANSLATION_Y: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_float (value, info->translation.y); } break; case PROP_TRANSLATION_Z: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_float (value, info->translation.z); } break; case PROP_SCALE_X: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->scale_x); } break; case PROP_SCALE_Y: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->scale_y); } break; case PROP_SCALE_Z: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->scale_z); } break; case PROP_SCALE_CENTER_X: /* XXX:2.0 - remove */ { gfloat center; clutter_actor_get_scale_center (actor, ¢er, NULL); g_value_set_float (value, center); } break; case PROP_SCALE_CENTER_Y: /* XXX:2.0 - remove */ { gfloat center; clutter_actor_get_scale_center (actor, NULL, ¢er); g_value_set_float (value, center); } break; case PROP_SCALE_GRAVITY: /* XXX:2.0 - remove */ g_value_set_enum (value, clutter_actor_get_scale_gravity (actor)); break; case PROP_RESOURCE_SCALE: if (priv->needs_compute_resource_scale) { if (!clutter_actor_update_resource_scale (actor)) g_warning ("Getting invalid resource scale property"); } g_value_set_float (value, priv->resource_scale); break; case PROP_REACTIVE: g_value_set_boolean (value, clutter_actor_get_reactive (actor)); break; case PROP_ROTATION_ANGLE_X: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->rx_angle); } break; case PROP_ROTATION_ANGLE_Y: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->ry_angle); } break; case PROP_ROTATION_ANGLE_Z: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_double (value, info->rz_angle); } break; case PROP_ROTATION_CENTER_X: /* XXX:2.0 - remove */ { graphene_point3d_t center; clutter_actor_get_rotation (actor, CLUTTER_X_AXIS, ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; case PROP_ROTATION_CENTER_Y: /* XXX:2.0 - remove */ { graphene_point3d_t center; clutter_actor_get_rotation (actor, CLUTTER_Y_AXIS, ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; case PROP_ROTATION_CENTER_Z: /* XXX:2.0 - remove */ { graphene_point3d_t center; clutter_actor_get_rotation (actor, CLUTTER_Z_AXIS, ¢er.x, ¢er.y, ¢er.z); g_value_set_boxed (value, ¢er); } break; case PROP_ROTATION_CENTER_Z_GRAVITY: /* XXX:2.0 - remove */ g_value_set_enum (value, clutter_actor_get_z_rotation_gravity (actor)); break; case PROP_ANCHOR_X: /* XXX:2.0 - remove */ { const ClutterTransformInfo *info; gfloat anchor_x; info = _clutter_actor_get_transform_info_or_defaults (actor); clutter_anchor_coord_get_units (actor, &info->anchor, &anchor_x, NULL, NULL); g_value_set_float (value, anchor_x); } break; case PROP_ANCHOR_Y: /* XXX:2.0 - remove */ { const ClutterTransformInfo *info; gfloat anchor_y; info = _clutter_actor_get_transform_info_or_defaults (actor); clutter_anchor_coord_get_units (actor, &info->anchor, NULL, &anchor_y, NULL); g_value_set_float (value, anchor_y); } break; case PROP_ANCHOR_GRAVITY: /* XXX:2.0 - remove */ g_value_set_enum (value, clutter_actor_get_anchor_point_gravity (actor)); break; case PROP_TRANSFORM: { ClutterMatrix m; clutter_actor_get_transform (actor, &m); g_value_set_boxed (value, &m); } break; case PROP_TRANSFORM_SET: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_boolean (value, info->transform_set); } break; case PROP_CHILD_TRANSFORM: { ClutterMatrix m; clutter_actor_get_child_transform (actor, &m); g_value_set_boxed (value, &m); } break; case PROP_CHILD_TRANSFORM_SET: { const ClutterTransformInfo *info; info = _clutter_actor_get_transform_info_or_defaults (actor); g_value_set_boolean (value, info->child_transform_set); } break; case PROP_SHOW_ON_SET_PARENT: /* XXX:2.0 - remove */ g_value_set_boolean (value, priv->show_on_set_parent); break; case PROP_TEXT_DIRECTION: g_value_set_enum (value, priv->text_direction); break; case PROP_HAS_POINTER: g_value_set_boolean (value, priv->has_pointer); break; case PROP_LAYOUT_MANAGER: g_value_set_object (value, priv->layout_manager); break; case PROP_X_EXPAND: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_boolean (value, info->x_expand); } break; case PROP_Y_EXPAND: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_boolean (value, info->y_expand); } break; case PROP_X_ALIGN: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_enum (value, info->x_align); } break; case PROP_Y_ALIGN: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_enum (value, info->y_align); } break; case PROP_MARGIN_TOP: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->margin.top); } break; case PROP_MARGIN_BOTTOM: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->margin.bottom); } break; case PROP_MARGIN_LEFT: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->margin.left); } break; case PROP_MARGIN_RIGHT: { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (actor); g_value_set_float (value, info->margin.right); } break; case PROP_BACKGROUND_COLOR_SET: g_value_set_boolean (value, priv->bg_color_set); break; case PROP_BACKGROUND_COLOR: g_value_set_boxed (value, &priv->bg_color); break; case PROP_FIRST_CHILD: g_value_set_object (value, priv->first_child); break; case PROP_LAST_CHILD: g_value_set_object (value, priv->last_child); break; case PROP_CONTENT: g_value_set_object (value, priv->content); break; case PROP_CONTENT_GRAVITY: g_value_set_enum (value, priv->content_gravity); break; case PROP_CONTENT_BOX: { ClutterActorBox box = { 0, }; clutter_actor_get_content_box (actor, &box); g_value_set_boxed (value, &box); } break; case PROP_MINIFICATION_FILTER: g_value_set_enum (value, priv->min_filter); break; case PROP_MAGNIFICATION_FILTER: g_value_set_enum (value, priv->mag_filter); break; case PROP_CONTENT_REPEAT: g_value_set_flags (value, priv->content_repeat); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_actor_dispose (GObject *object) { ClutterActor *self = CLUTTER_ACTOR (object); ClutterActorPrivate *priv = self->priv; ClutterBackend *backend = clutter_get_default_backend (); CLUTTER_NOTE (MISC, "Dispose actor (name='%s', ref_count:%d) of type '%s'", _clutter_actor_get_debug_name (self), object->ref_count, g_type_name (G_OBJECT_TYPE (self))); maybe_unset_key_focus (self); /* Stop the emission of any property change */ g_object_freeze_notify (object); g_signal_emit (self, actor_signals[DESTROY], 0); /* avoid recursing when called from clutter_actor_destroy() */ if (priv->parent != NULL) { ClutterActor *parent = priv->parent; clutter_container_remove_actor (CLUTTER_CONTAINER (parent), self); } /* parent must be gone at this point */ g_assert (priv->parent == NULL); if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) { /* can't be mapped or realized with no parent */ g_assert (!CLUTTER_ACTOR_IS_MAPPED (self)); g_assert (!CLUTTER_ACTOR_IS_REALIZED (self)); } g_clear_signal_handler (&priv->resolution_changed_id, backend); g_clear_signal_handler (&priv->font_changed_id, backend); g_clear_object (&priv->pango_context); g_clear_object (&priv->actions); g_clear_object (&priv->constraints); g_clear_object (&priv->effects); g_clear_object (&priv->flatten_effect); if (priv->child_model != NULL) { if (priv->create_child_notify != NULL) priv->create_child_notify (priv->create_child_data); priv->create_child_func = NULL; priv->create_child_data = NULL; priv->create_child_notify = NULL; g_clear_object (&priv->child_model); } if (priv->layout_manager != NULL) { clutter_layout_manager_set_container (priv->layout_manager, NULL); g_clear_object (&priv->layout_manager); } if (priv->content != NULL) { _clutter_content_detached (priv->content, self); g_clear_object (&priv->content); } if (priv->clones != NULL) { g_hash_table_unref (priv->clones); priv->clones = NULL; } G_OBJECT_CLASS (clutter_actor_parent_class)->dispose (object); } static void clutter_actor_finalize (GObject *object) { ClutterActorPrivate *priv = CLUTTER_ACTOR (object)->priv; CLUTTER_NOTE (MISC, "Finalize actor (name='%s') of type '%s'", _clutter_actor_get_debug_name ((ClutterActor *) object), g_type_name (G_OBJECT_TYPE (object))); g_free (priv->name); #ifdef CLUTTER_ENABLE_DEBUG g_free (priv->debug_name); #endif G_OBJECT_CLASS (clutter_actor_parent_class)->finalize (object); } /** * clutter_actor_get_accessible: * @self: a #ClutterActor * * Returns the accessible object that describes the actor to an * assistive technology. * * If no class-specific #AtkObject implementation is available for the * actor instance in question, it will inherit an #AtkObject * implementation from the first ancestor class for which such an * implementation is defined. * * The documentation of the ATK * library contains more information about accessible objects and * their uses. * * Returns: (transfer none): the #AtkObject associated with @actor */ AtkObject * clutter_actor_get_accessible (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return CLUTTER_ACTOR_GET_CLASS (self)->get_accessible (self); } static AtkObject * clutter_actor_real_get_accessible (ClutterActor *actor) { return atk_gobject_accessible_for_object (G_OBJECT (actor)); } static AtkObject * _clutter_actor_ref_accessible (AtkImplementor *implementor) { AtkObject *accessible; accessible = clutter_actor_get_accessible (CLUTTER_ACTOR (implementor)); if (accessible != NULL) g_object_ref (accessible); return accessible; } static void atk_implementor_iface_init (AtkImplementorIface *iface) { iface->ref_accessible = _clutter_actor_ref_accessible; } static gboolean clutter_actor_update_default_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterActorPrivate *priv = self->priv; gboolean res = TRUE; /* this should be checked before we call this function, but it's a * good idea to be explicit when it costs us nothing */ if (priv->needs_allocation) return FALSE; /* we start from the allocation */ clutter_paint_volume_set_width (volume, priv->allocation.x2 - priv->allocation.x1); clutter_paint_volume_set_height (volume, priv->allocation.y2 - priv->allocation.y1); /* if the actor has a clip set then we have a pretty definite * size for the paint volume: the actor cannot possibly paint * outside the clip region. */ if (priv->clip_to_allocation) { /* the allocation has already been set, so we just flip the * return value */ res = TRUE; } else { ClutterActor *child; if (priv->has_clip && priv->clip.size.width >= 0 && priv->clip.size.height >= 0) { graphene_point3d_t origin; origin.x = priv->clip.origin.x; origin.y = priv->clip.origin.y; origin.z = 0; clutter_paint_volume_set_origin (volume, &origin); clutter_paint_volume_set_width (volume, priv->clip.size.width); clutter_paint_volume_set_height (volume, priv->clip.size.height); res = TRUE; } /* if we don't have children we just bail out here... */ if (priv->n_children == 0) return res; /* ...but if we have children then we ask for their paint volume in * our coordinates. if any of our children replies that it doesn't * have a paint volume, we bail out */ for (child = priv->first_child; child != NULL; child = child->priv->next_sibling) { const ClutterPaintVolume *child_volume; /* we ignore unmapped children, since they won't be painted. * * XXX: we also have to ignore mapped children without a valid * allocation, because apparently some code above Clutter allows * them. */ if (!CLUTTER_ACTOR_IS_MAPPED (child) || !clutter_actor_has_allocation (child)) continue; child_volume = clutter_actor_get_transformed_paint_volume (child, self); if (child_volume == NULL) { res = FALSE; break; } clutter_paint_volume_union (volume, child_volume); res = TRUE; } } return res; } static gboolean clutter_actor_real_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterActorClass *klass; gboolean res; klass = CLUTTER_ACTOR_GET_CLASS (self); /* XXX - this thoroughly sucks, but we don't want to penalize users * who use ClutterActor as a "new ClutterGroup" by forcing a full-stage * redraw. This should go away in 2.0. */ if (klass->paint == clutter_actor_real_paint && klass->get_paint_volume == clutter_actor_real_get_paint_volume) { res = TRUE; } else { /* this is the default return value: we cannot know if a class * is going to paint outside its allocation, so we take the * conservative approach. */ res = FALSE; } /* update_default_paint_volume() should only fail if one of the children * reported an invalid, or no, paint volume */ if (!clutter_actor_update_default_paint_volume (self, volume)) return FALSE; return res; } /** * clutter_actor_get_default_paint_volume: * @self: a #ClutterActor * * Retrieves the default paint volume for @self. * * This function provides the same #ClutterPaintVolume that would be * computed by the default implementation inside #ClutterActor of the * #ClutterActorClass.get_paint_volume() virtual function. * * This function should only be used by #ClutterActor subclasses that * cannot chain up to the parent implementation when computing their * paint volume. * * Return value: (transfer none): a pointer to the default * #ClutterPaintVolume, relative to the #ClutterActor, or %NULL if * the actor could not compute a valid paint volume. The returned value * is not guaranteed to be stable across multiple frames, so if you * want to retain it, you will need to copy it using * clutter_paint_volume_copy(). * * Since: 1.10 */ const ClutterPaintVolume * clutter_actor_get_default_paint_volume (ClutterActor *self) { ClutterPaintVolume volume; ClutterPaintVolume *res; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); res = NULL; _clutter_paint_volume_init_static (&volume, self); if (clutter_actor_update_default_paint_volume (self, &volume)) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); if (stage != NULL) { res = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage)); _clutter_paint_volume_copy_static (&volume, res); } } clutter_paint_volume_free (&volume); return res; } static gboolean clutter_actor_real_has_overlaps (ClutterActor *self) { /* By default we'll assume that all actors need an offscreen redirect to get * the correct opacity. Actors such as ClutterTexture that would never need * an offscreen redirect can override this to return FALSE. */ return TRUE; } static void clutter_actor_real_destroy (ClutterActor *actor) { ClutterActorIter iter; g_object_freeze_notify (G_OBJECT (actor)); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, NULL)) clutter_actor_iter_destroy (&iter); g_object_thaw_notify (G_OBJECT (actor)); } static GObject * clutter_actor_constructor (GType gtype, guint n_props, GObjectConstructParam *props) { GObjectClass *gobject_class; ClutterActor *self; GObject *retval; gobject_class = G_OBJECT_CLASS (clutter_actor_parent_class); retval = gobject_class->constructor (gtype, n_props, props); self = CLUTTER_ACTOR (retval); if (self->priv->layout_manager == NULL) { ClutterLayoutManager *default_layout; CLUTTER_NOTE (LAYOUT, "Creating default layout manager"); default_layout = clutter_fixed_layout_new (); clutter_actor_set_layout_manager (self, default_layout); } return retval; } static void clutter_actor_class_init (ClutterActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); quark_actor_layout_info = g_quark_from_static_string ("-clutter-actor-layout-info"); quark_actor_transform_info = g_quark_from_static_string ("-clutter-actor-transform-info"); quark_actor_animation_info = g_quark_from_static_string ("-clutter-actor-animation-info"); quark_key = g_quark_from_static_string ("key"); quark_motion = g_quark_from_static_string ("motion"); quark_pointer_focus = g_quark_from_static_string ("pointer-focus"); quark_button = g_quark_from_static_string ("button"); quark_scroll = g_quark_from_static_string ("scroll"); quark_stage = g_quark_from_static_string ("stage"); quark_destroy = g_quark_from_static_string ("destroy"); quark_client = g_quark_from_static_string ("client"); quark_delete = g_quark_from_static_string ("delete"); quark_touch = g_quark_from_static_string ("touch"); quark_touchpad = g_quark_from_static_string ("touchpad"); quark_proximity = g_quark_from_static_string ("proximity"); quark_pad = g_quark_from_static_string ("pad"); quark_im = g_quark_from_static_string ("im"); object_class->constructor = clutter_actor_constructor; object_class->set_property = clutter_actor_set_property; object_class->get_property = clutter_actor_get_property; object_class->dispose = clutter_actor_dispose; object_class->finalize = clutter_actor_finalize; klass->show = clutter_actor_real_show; klass->show_all = clutter_actor_show; klass->hide = clutter_actor_real_hide; klass->hide_all = clutter_actor_hide; klass->map = clutter_actor_real_map; klass->unmap = clutter_actor_real_unmap; klass->unrealize = clutter_actor_real_unrealize; klass->pick = clutter_actor_real_pick; klass->get_preferred_width = clutter_actor_real_get_preferred_width; klass->get_preferred_height = clutter_actor_real_get_preferred_height; klass->allocate = clutter_actor_real_allocate; klass->queue_redraw = clutter_actor_real_queue_redraw; klass->queue_relayout = clutter_actor_real_queue_relayout; klass->apply_transform = clutter_actor_real_apply_transform; klass->get_accessible = clutter_actor_real_get_accessible; klass->get_paint_volume = clutter_actor_real_get_paint_volume; klass->has_overlaps = clutter_actor_real_has_overlaps; klass->paint = clutter_actor_real_paint; klass->destroy = clutter_actor_real_destroy; /** * ClutterActor:x: * * X coordinate of the actor in pixels. If written, forces a fixed * position for the actor. If read, returns the fixed position if any, * otherwise the allocation if available, otherwise 0. * * The #ClutterActor:x property is animatable. */ obj_props[PROP_X] = g_param_spec_float ("x", P_("X coordinate"), P_("X coordinate of the actor"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:y: * * Y coordinate of the actor in pixels. If written, forces a fixed * position for the actor. If read, returns the fixed position if * any, otherwise the allocation if available, otherwise 0. * * The #ClutterActor:y property is animatable. */ obj_props[PROP_Y] = g_param_spec_float ("y", P_("Y coordinate"), P_("Y coordinate of the actor"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:position: * * The position of the origin of the actor. * * This property is a shorthand for setting and getting the * #ClutterActor:x and #ClutterActor:y properties at the same * time. * * The #ClutterActor:position property is animatable. * * Since: 1.12 */ obj_props[PROP_POSITION] = g_param_spec_boxed ("position", P_("Position"), P_("The position of the origin of the actor"), GRAPHENE_TYPE_POINT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:width: * * Width of the actor (in pixels). If written, forces the minimum and * natural size request of the actor to the given width. If read, returns * the allocated width if available, otherwise the width request. * * The #ClutterActor:width property is animatable. */ obj_props[PROP_WIDTH] = g_param_spec_float ("width", P_("Width"), P_("Width of the actor"), -1.0f, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:height: * * Height of the actor (in pixels). If written, forces the minimum and * natural size request of the actor to the given height. If read, returns * the allocated height if available, otherwise the height request. * * The #ClutterActor:height property is animatable. */ obj_props[PROP_HEIGHT] = g_param_spec_float ("height", P_("Height"), P_("Height of the actor"), -1.0f, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:size: * * The size of the actor. * * This property is a shorthand for setting and getting the * #ClutterActor:width and #ClutterActor:height at the same time. * * The #ClutterActor:size property is animatable. * * Since: 1.12 */ obj_props[PROP_SIZE] = g_param_spec_boxed ("size", P_("Size"), P_("The size of the actor"), GRAPHENE_TYPE_SIZE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:fixed-x: * * The fixed X position of the actor in pixels. * * Writing this property sets #ClutterActor:fixed-position-set * property as well, as a side effect * * Since: 0.8 */ obj_props[PROP_FIXED_X] = g_param_spec_float ("fixed-x", P_("Fixed X"), P_("Forced X position of the actor"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:fixed-y: * * The fixed Y position of the actor in pixels. * * Writing this property sets the #ClutterActor:fixed-position-set * property as well, as a side effect * * Since: 0.8 */ obj_props[PROP_FIXED_Y] = g_param_spec_float ("fixed-y", P_("Fixed Y"), P_("Forced Y position of the actor"), -G_MAXFLOAT, G_MAXFLOAT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:fixed-position-set: * * This flag controls whether the #ClutterActor:fixed-x and * #ClutterActor:fixed-y properties are used * * Since: 0.8 */ obj_props[PROP_FIXED_POSITION_SET] = g_param_spec_boolean ("fixed-position-set", P_("Fixed position set"), P_("Whether to use fixed positioning for the actor"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:min-width: * * A forced minimum width request for the actor, in pixels * * Writing this property sets the #ClutterActor:min-width-set property * as well, as a side effect. * *This property overrides the usual width request of the actor. * * Since: 0.8 */ obj_props[PROP_MIN_WIDTH] = g_param_spec_float ("min-width", P_("Min Width"), P_("Forced minimum width request for the actor"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:min-height: * * A forced minimum height request for the actor, in pixels * * Writing this property sets the #ClutterActor:min-height-set property * as well, as a side effect. This property overrides the usual height * request of the actor. * * Since: 0.8 */ obj_props[PROP_MIN_HEIGHT] = g_param_spec_float ("min-height", P_("Min Height"), P_("Forced minimum height request for the actor"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:natural-width: * * A forced natural width request for the actor, in pixels * * Writing this property sets the #ClutterActor:natural-width-set * property as well, as a side effect. This property overrides the * usual width request of the actor * * Since: 0.8 */ obj_props[PROP_NATURAL_WIDTH] = g_param_spec_float ("natural-width", P_("Natural Width"), P_("Forced natural width request for the actor"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:natural-height: * * A forced natural height request for the actor, in pixels * * Writing this property sets the #ClutterActor:natural-height-set * property as well, as a side effect. This property overrides the * usual height request of the actor * * Since: 0.8 */ obj_props[PROP_NATURAL_HEIGHT] = g_param_spec_float ("natural-height", P_("Natural Height"), P_("Forced natural height request for the actor"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:min-width-set: * * This flag controls whether the #ClutterActor:min-width property * is used * * Since: 0.8 */ obj_props[PROP_MIN_WIDTH_SET] = g_param_spec_boolean ("min-width-set", P_("Minimum width set"), P_("Whether to use the min-width property"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:min-height-set: * * This flag controls whether the #ClutterActor:min-height property * is used * * Since: 0.8 */ obj_props[PROP_MIN_HEIGHT_SET] = g_param_spec_boolean ("min-height-set", P_("Minimum height set"), P_("Whether to use the min-height property"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:natural-width-set: * * This flag controls whether the #ClutterActor:natural-width property * is used * * Since: 0.8 */ obj_props[PROP_NATURAL_WIDTH_SET] = g_param_spec_boolean ("natural-width-set", P_("Natural width set"), P_("Whether to use the natural-width property"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:natural-height-set: * * This flag controls whether the #ClutterActor:natural-height property * is used * * Since: 0.8 */ obj_props[PROP_NATURAL_HEIGHT_SET] = g_param_spec_boolean ("natural-height-set", P_("Natural height set"), P_("Whether to use the natural-height property"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:allocation: * * The allocation for the actor, in pixels * * This is property is read-only, but you might monitor it to know when an * actor moves or resizes * * Since: 0.8 */ obj_props[PROP_ALLOCATION] = g_param_spec_boxed ("allocation", P_("Allocation"), P_("The actor's allocation"), CLUTTER_TYPE_ACTOR_BOX, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:request-mode: * * Request mode for the #ClutterActor. The request mode determines the * type of geometry management used by the actor, either height for width * (the default) or width for height. * * For actors implementing height for width, the parent container should get * the preferred width first, and then the preferred height for that width. * * For actors implementing width for height, the parent container should get * the preferred height first, and then the preferred width for that height. * * For instance: * * |[ * ClutterRequestMode mode; * gfloat natural_width, min_width; * gfloat natural_height, min_height; * * mode = clutter_actor_get_request_mode (child); * if (mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) * { * clutter_actor_get_preferred_width (child, -1, * &min_width, * &natural_width); * clutter_actor_get_preferred_height (child, natural_width, * &min_height, * &natural_height); * } * else if (mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) * { * clutter_actor_get_preferred_height (child, -1, * &min_height, * &natural_height); * clutter_actor_get_preferred_width (child, natural_height, * &min_width, * &natural_width); * } * else if (mode == CLUTTER_REQUEST_CONTENT_SIZE) * { * ClutterContent *content = clutter_actor_get_content (child); * * min_width, min_height = 0; * natural_width = natural_height = 0; * * if (content != NULL) * clutter_content_get_preferred_size (content, &natural_width, &natural_height); * } * ]| * * will retrieve the minimum and natural width and height depending on the * preferred request mode of the #ClutterActor "child". * * The clutter_actor_get_preferred_size() function will implement this * check for you. * * Since: 0.8 */ obj_props[PROP_REQUEST_MODE] = g_param_spec_enum ("request-mode", P_("Request Mode"), P_("The actor's request mode"), CLUTTER_TYPE_REQUEST_MODE, CLUTTER_REQUEST_HEIGHT_FOR_WIDTH, CLUTTER_PARAM_READWRITE); /** * ClutterActor:depth: * * The position of the actor on the Z axis. * * The #ClutterActor:depth property is relative to the parent's * modelview matrix. * * Setting this property will call #ClutterContainerIface.sort_depth_order() * which is usually a no-op, and it's most likely not what you want. * * The #ClutterActor:depth property is animatable. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:z-position instead. */ obj_props[PROP_DEPTH] = g_param_spec_float ("depth", P_("Depth"), P_("Position on the Z axis"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:z-position: * * The actor's position on the Z axis, relative to the parent's * transformations. * * Positive values will bring the actor's position nearer to the user, * whereas negative values will bring the actor's position farther from * the user. * * The #ClutterActor:z-position does not affect the paint or allocation * order. * * The #ClutterActor:z-position property is animatable. * * Since: 1.12 */ obj_props[PROP_Z_POSITION] = g_param_spec_float ("z-position", P_("Z Position"), P_("The actor's position on the Z axis"), -G_MAXFLOAT, G_MAXFLOAT, 0.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:opacity: * * Opacity of an actor, between 0 (fully transparent) and * 255 (fully opaque) * * The #ClutterActor:opacity property is animatable. */ obj_props[PROP_OPACITY] = g_param_spec_uint ("opacity", P_("Opacity"), P_("Opacity of an actor"), 0, 255, 255, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:offscreen-redirect: * * Determines the conditions in which the actor will be redirected * to an offscreen framebuffer while being painted. For example this * can be used to cache an actor in a framebuffer or for improved * handling of transparent actors. See * clutter_actor_set_offscreen_redirect() for details. * * Since: 1.8 */ obj_props[PROP_OFFSCREEN_REDIRECT] = g_param_spec_flags ("offscreen-redirect", P_("Offscreen redirect"), P_("Flags controlling when to flatten the actor into a single image"), CLUTTER_TYPE_OFFSCREEN_REDIRECT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterActor:visible: * * Whether the actor is set to be visible or not * * See also #ClutterActor:mapped */ obj_props[PROP_VISIBLE] = g_param_spec_boolean ("visible", P_("Visible"), P_("Whether the actor is visible or not"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:mapped: * * Whether the actor is mapped (will be painted when the stage * to which it belongs is mapped) * * Since: 1.0 */ obj_props[PROP_MAPPED] = g_param_spec_boolean ("mapped", P_("Mapped"), P_("Whether the actor will be painted"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterActor:realized: * * Whether the actor has been realized * * Since: 1.0 */ obj_props[PROP_REALIZED] = g_param_spec_boolean ("realized", P_("Realized"), P_("Whether the actor has been realized"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterActor:reactive: * * Whether the actor is reactive to events or not * * Only reactive actors will emit event-related signals * * Since: 0.6 */ obj_props[PROP_REACTIVE] = g_param_spec_boolean ("reactive", P_("Reactive"), P_("Whether the actor is reactive to events"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:has-clip: * * Whether the actor has the #ClutterActor:clip property set or not */ obj_props[PROP_HAS_CLIP] = g_param_spec_boolean ("has-clip", P_("Has Clip"), P_("Whether the actor has a clip set"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterActor:clip-rect: * * The visible region of the actor, in actor-relative coordinates, * expressed as a #graphene_rect_t. * * Setting this property to %NULL will unset the existing clip. * * Setting this property will change the #ClutterActor:has-clip * property as a side effect. * * Since: 1.12 */ obj_props[PROP_CLIP_RECT] = g_param_spec_boxed ("clip-rect", P_("Clip Rectangle"), P_("The visible region of the actor"), GRAPHENE_TYPE_RECT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterActor:name: * * The name of the actor * * Since: 0.2 */ obj_props[PROP_NAME] = g_param_spec_string ("name", P_("Name"), P_("Name of the actor"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterActor:pivot-point: * * The point around which the scaling and rotation transformations occur. * * The pivot point is expressed in normalized coordinates space, with (0, 0) * being the top left corner of the actor and (1, 1) the bottom right corner * of the actor. * * The default pivot point is located at (0, 0). * * The #ClutterActor:pivot-point property is animatable. * * Since: 1.12 */ obj_props[PROP_PIVOT_POINT] = g_param_spec_boxed ("pivot-point", P_("Pivot Point"), P_("The point around which the scaling and rotation occur"), GRAPHENE_TYPE_POINT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:pivot-point-z: * * The Z component of the #ClutterActor:pivot-point, expressed as a value * along the Z axis. * * The #ClutterActor:pivot-point-z property is animatable. * * Since: 1.12 */ obj_props[PROP_PIVOT_POINT_Z] = g_param_spec_float ("pivot-point-z", P_("Pivot Point Z"), P_("Z component of the pivot point"), -G_MAXFLOAT, G_MAXFLOAT, 0.f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:scale-x: * * The horizontal scale of the actor. * * The #ClutterActor:scale-x property is animatable. * * Since: 0.6 */ obj_props[PROP_SCALE_X] = g_param_spec_double ("scale-x", P_("Scale X"), P_("Scale factor on the X axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:scale-y: * * The vertical scale of the actor. * * The #ClutterActor:scale-y property is animatable. * * Since: 0.6 */ obj_props[PROP_SCALE_Y] = g_param_spec_double ("scale-y", P_("Scale Y"), P_("Scale factor on the Y axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:scale-z: * * The scale factor of the actor along the Z axis. * * The #ClutterActor:scale-y property is animatable. * * Since: 1.12 */ obj_props[PROP_SCALE_Z] = g_param_spec_double ("scale-z", P_("Scale Z"), P_("Scale factor on the Z axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 1.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:scale-center-x: * * The horizontal center point for scaling * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_SCALE_CENTER_X] = /* XXX:2.0 - remove */ g_param_spec_float ("scale-center-x", P_("Scale Center X"), P_("Horizontal scale center"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:scale-center-y: * * The vertical center point for scaling * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_SCALE_CENTER_Y] = /* XXX:2.0 - remove */ g_param_spec_float ("scale-center-y", P_("Scale Center Y"), P_("Vertical scale center"), -G_MAXFLOAT, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:scale-gravity: * * The center point for scaling expressed as a #ClutterGravity * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_SCALE_GRAVITY] = /* XXX:2.0 - remove */ g_param_spec_enum ("scale-gravity", P_("Scale Gravity"), P_("The center of scaling"), CLUTTER_TYPE_GRAVITY, CLUTTER_GRAVITY_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:resource-scale: * * The resource-scale of the #ClutterActor if any or -1 if not available */ obj_props[PROP_RESOURCE_SCALE] = g_param_spec_float ("resource-scale", P_("Resource Scale"), P_("The Scaling factor for resources painting"), -1.0f, G_MAXFLOAT, 1.0f, CLUTTER_PARAM_READABLE); /** * ClutterActor:rotation-angle-x: * * The rotation angle on the X axis. * * The #ClutterActor:rotation-angle-x property is animatable. * * Since: 0.6 */ obj_props[PROP_ROTATION_ANGLE_X] = g_param_spec_double ("rotation-angle-x", P_("Rotation Angle X"), P_("The rotation angle on the X axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:rotation-angle-y: * * The rotation angle on the Y axis * * The #ClutterActor:rotation-angle-y property is animatable. * * Since: 0.6 */ obj_props[PROP_ROTATION_ANGLE_Y] = g_param_spec_double ("rotation-angle-y", P_("Rotation Angle Y"), P_("The rotation angle on the Y axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:rotation-angle-z: * * The rotation angle on the Z axis * * The #ClutterActor:rotation-angle-z property is animatable. * * Since: 0.6 */ obj_props[PROP_ROTATION_ANGLE_Z] = g_param_spec_double ("rotation-angle-z", P_("Rotation Angle Z"), P_("The rotation angle on the Z axis"), -G_MAXDOUBLE, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:rotation-center-x: * * The rotation center on the X axis. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ROTATION_CENTER_X] = /* XXX:2.0 - remove */ g_param_spec_boxed ("rotation-center-x", P_("Rotation Center X"), P_("The rotation center on the X axis"), GRAPHENE_TYPE_POINT3D, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:rotation-center-y: * * The rotation center on the Y axis. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ROTATION_CENTER_Y] = /* XXX:2.0 - remove */ g_param_spec_boxed ("rotation-center-y", P_("Rotation Center Y"), P_("The rotation center on the Y axis"), GRAPHENE_TYPE_POINT3D, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:rotation-center-z: * * The rotation center on the Z axis. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ROTATION_CENTER_Z] = /* XXX:2.0 - remove */ g_param_spec_boxed ("rotation-center-z", P_("Rotation Center Z"), P_("The rotation center on the Z axis"), GRAPHENE_TYPE_POINT3D, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:rotation-center-z-gravity: * * The rotation center on the Z axis expressed as a #ClutterGravity. * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ROTATION_CENTER_Z_GRAVITY] = /* XXX:2.0 - remove */ g_param_spec_enum ("rotation-center-z-gravity", P_("Rotation Center Z Gravity"), P_("Center point for rotation around the Z axis"), CLUTTER_TYPE_GRAVITY, CLUTTER_GRAVITY_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:anchor-x: * * The X coordinate of an actor's anchor point, relative to * the actor coordinate space, in pixels. * * It is highly recommended not to use #ClutterActor:anchor-x, * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly * written code; the anchor point adds an additional translation that * will affect the actor's relative position with regards to its * parent, as well as the position of its children. This change needs * to always be taken into account when positioning the actor. It is * recommended to use the #ClutterActor:pivot-point property instead, * as it will affect only the transformations. * * Since: 0.8 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ANCHOR_X] = /* XXX:2.0 - remove */ g_param_spec_float ("anchor-x", P_("Anchor X"), P_("X coordinate of the anchor point"), -G_MAXFLOAT, G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:anchor-y: * * The Y coordinate of an actor's anchor point, relative to * the actor coordinate space, in pixels * * It is highly recommended not to use #ClutterActor:anchor-x, * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly * written code; the anchor point adds an additional translation that * will affect the actor's relative position with regards to its * parent, as well as the position of its children. This change needs * to always be taken into account when positioning the actor. It is * recommended to use the #ClutterActor:pivot-point property instead, * as it will affect only the transformations. * * Since: 0.8 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ANCHOR_Y] = /* XXX:2.0 - remove */ g_param_spec_float ("anchor-y", P_("Anchor Y"), P_("Y coordinate of the anchor point"), -G_MAXFLOAT, G_MAXFLOAT, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:anchor-gravity: * * The anchor point expressed as a #ClutterGravity * * It is highly recommended not to use #ClutterActor:anchor-x, * #ClutterActor:anchor-y, and #ClutterActor:anchor-gravity in newly * written code; the anchor point adds an additional translation that * will affect the actor's relative position with regards to its * parent, as well as the position of its children. This change needs * to always be taken into account when positioning the actor. It is * recommended to use the #ClutterActor:pivot-point property instead, * as it will affect only the transformations. * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead */ obj_props[PROP_ANCHOR_GRAVITY] = /* XXX:2.0 - remove */ g_param_spec_enum ("anchor-gravity", P_("Anchor Gravity"), P_("The anchor point as a ClutterGravity"), CLUTTER_TYPE_GRAVITY, CLUTTER_GRAVITY_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterActor:translation-x: * * An additional translation applied along the X axis, relative * to the actor's #ClutterActor:pivot-point. * * The #ClutterActor:translation-x property is animatable. * * Since: 1.12 */ obj_props[PROP_TRANSLATION_X] = g_param_spec_float ("translation-x", P_("Translation X"), P_("Translation along the X axis"), -G_MAXFLOAT, G_MAXFLOAT, 0.f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:translation-y: * * An additional translation applied along the Y axis, relative * to the actor's #ClutterActor:pivot-point. * * The #ClutterActor:translation-y property is animatable. * * Since: 1.12 */ obj_props[PROP_TRANSLATION_Y] = g_param_spec_float ("translation-y", P_("Translation Y"), P_("Translation along the Y axis"), -G_MAXFLOAT, G_MAXFLOAT, 0.f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:translation-z: * * An additional translation applied along the Z axis, relative * to the actor's #ClutterActor:pivot-point. * * The #ClutterActor:translation-z property is animatable. * * Since: 1.12 */ obj_props[PROP_TRANSLATION_Z] = g_param_spec_float ("translation-z", P_("Translation Z"), P_("Translation along the Z axis"), -G_MAXFLOAT, G_MAXFLOAT, 0.f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:transform: * * Overrides the transformations of a #ClutterActor with a custom * matrix. * * The matrix specified by the #ClutterActor:transform property is * applied to the actor and its children relative to the actor's * #ClutterActor:allocation and #ClutterActor:pivot-point. * * Application code should rarely need to use this function directly. * * Setting this property with a #ClutterMatrix will set the * #ClutterActor:transform-set property to %TRUE as a side effect; * setting this property with %NULL will set the * #ClutterActor:transform-set property to %FALSE. * * The #ClutterActor:transform property is animatable. * * Since: 1.12 */ obj_props[PROP_TRANSFORM] = g_param_spec_boxed ("transform", P_("Transform"), P_("Transformation matrix"), CLUTTER_TYPE_MATRIX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:transform-set: * * Whether the #ClutterActor:transform property is set. * * Since: 1.12 */ obj_props[PROP_TRANSFORM_SET] = g_param_spec_boolean ("transform-set", P_("Transform Set"), P_("Whether the transform property is set"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * ClutterActor:child-transform: * * Applies a transformation matrix on each child of an actor. * * Setting this property with a #ClutterMatrix will set the * #ClutterActor:child-transform-set property to %TRUE as a side effect; * setting this property with %NULL will set the * #ClutterActor:child-transform-set property to %FALSE. * * The #ClutterActor:child-transform property is animatable. * * Since: 1.12 */ obj_props[PROP_CHILD_TRANSFORM] = g_param_spec_boxed ("child-transform", P_("Child Transform"), P_("Children transformation matrix"), CLUTTER_TYPE_MATRIX, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:child-transform-set: * * Whether the #ClutterActor:child-transform property is set. * * Since: 1.12 */ obj_props[PROP_CHILD_TRANSFORM_SET] = g_param_spec_boolean ("child-transform-set", P_("Child Transform Set"), P_("Whether the child-transform property is set"), FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * ClutterActor:show-on-set-parent: * * If %TRUE, the actor is automatically shown when parented. * * Calling clutter_actor_hide() on an actor which has not been * parented will set this property to %FALSE as a side effect. * * Since: 0.8 */ obj_props[PROP_SHOW_ON_SET_PARENT] = /* XXX:2.0 - remove */ g_param_spec_boolean ("show-on-set-parent", P_("Show on set parent"), P_("Whether the actor is shown when parented"), TRUE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:clip-to-allocation: * * Whether the clip region should track the allocated area * of the actor. * * This property is ignored if a clip area has been explicitly * set using clutter_actor_set_clip(). * * Since: 1.0 */ obj_props[PROP_CLIP_TO_ALLOCATION] = g_param_spec_boolean ("clip-to-allocation", P_("Clip to Allocation"), P_("Sets the clip region to track the actor's allocation"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterActor:text-direction: * * The direction of the text inside a #ClutterActor. * * Since: 1.0 */ obj_props[PROP_TEXT_DIRECTION] = g_param_spec_enum ("text-direction", P_("Text Direction"), P_("Direction of the text"), CLUTTER_TYPE_TEXT_DIRECTION, CLUTTER_TEXT_DIRECTION_LTR, CLUTTER_PARAM_READWRITE); /** * ClutterActor:has-pointer: * * Whether the actor contains the pointer of a #ClutterInputDevice * or not. * * Since: 1.2 */ obj_props[PROP_HAS_POINTER] = g_param_spec_boolean ("has-pointer", P_("Has Pointer"), P_("Whether the actor contains the pointer of an input device"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterActor:actions: * * Adds a #ClutterAction to the actor * * Since: 1.4 */ obj_props[PROP_ACTIONS] = g_param_spec_object ("actions", P_("Actions"), P_("Adds an action to the actor"), CLUTTER_TYPE_ACTION, CLUTTER_PARAM_WRITABLE); /** * ClutterActor:constraints: * * Adds a #ClutterConstraint to the actor * * Since: 1.4 */ obj_props[PROP_CONSTRAINTS] = g_param_spec_object ("constraints", P_("Constraints"), P_("Adds a constraint to the actor"), CLUTTER_TYPE_CONSTRAINT, CLUTTER_PARAM_WRITABLE); /** * ClutterActor:effect: * * Adds #ClutterEffect to the list of effects be applied on a #ClutterActor * * Since: 1.4 */ obj_props[PROP_EFFECT] = g_param_spec_object ("effect", P_("Effect"), P_("Add an effect to be applied on the actor"), CLUTTER_TYPE_EFFECT, CLUTTER_PARAM_WRITABLE); /** * ClutterActor:layout-manager: * * A delegate object for controlling the layout of the children of * an actor. * * Since: 1.10 */ obj_props[PROP_LAYOUT_MANAGER] = g_param_spec_object ("layout-manager", P_("Layout Manager"), P_("The object controlling the layout of an actor's children"), CLUTTER_TYPE_LAYOUT_MANAGER, CLUTTER_PARAM_READWRITE); /** * ClutterActor:x-expand: * * Whether a layout manager should assign more space to the actor on * the X axis. * * Since: 1.12 */ obj_props[PROP_X_EXPAND] = g_param_spec_boolean ("x-expand", P_("X Expand"), P_("Whether extra horizontal space should be assigned to the actor"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterActor:y-expand: * * Whether a layout manager should assign more space to the actor on * the Y axis. * * Since: 1.12 */ obj_props[PROP_Y_EXPAND] = g_param_spec_boolean ("y-expand", P_("Y Expand"), P_("Whether extra vertical space should be assigned to the actor"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterActor:x-align: * * The alignment of an actor on the X axis, if the actor has been given * extra space for its allocation. See also the #ClutterActor:x-expand * property. * * Since: 1.10 */ obj_props[PROP_X_ALIGN] = g_param_spec_enum ("x-align", P_("X Alignment"), P_("The alignment of the actor on the X axis within its allocation"), CLUTTER_TYPE_ACTOR_ALIGN, CLUTTER_ACTOR_ALIGN_FILL, CLUTTER_PARAM_READWRITE); /** * ClutterActor:y-align: * * The alignment of an actor on the Y axis, if the actor has been given * extra space for its allocation. * * Since: 1.10 */ obj_props[PROP_Y_ALIGN] = g_param_spec_enum ("y-align", P_("Y Alignment"), P_("The alignment of the actor on the Y axis within its allocation"), CLUTTER_TYPE_ACTOR_ALIGN, CLUTTER_ACTOR_ALIGN_FILL, CLUTTER_PARAM_READWRITE); /** * ClutterActor:margin-top: * * The margin (in pixels) from the top of the actor. * * This property adds a margin to the actor's preferred size; the margin * will be automatically taken into account when allocating the actor. * * The #ClutterActor:margin-top property is animatable. * * Since: 1.10 */ obj_props[PROP_MARGIN_TOP] = g_param_spec_float ("margin-top", P_("Margin Top"), P_("Extra space at the top"), 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:margin-bottom: * * The margin (in pixels) from the bottom of the actor. * * This property adds a margin to the actor's preferred size; the margin * will be automatically taken into account when allocating the actor. * * The #ClutterActor:margin-bottom property is animatable. * * Since: 1.10 */ obj_props[PROP_MARGIN_BOTTOM] = g_param_spec_float ("margin-bottom", P_("Margin Bottom"), P_("Extra space at the bottom"), 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:margin-left: * * The margin (in pixels) from the left of the actor. * * This property adds a margin to the actor's preferred size; the margin * will be automatically taken into account when allocating the actor. * * The #ClutterActor:margin-left property is animatable. * * Since: 1.10 */ obj_props[PROP_MARGIN_LEFT] = g_param_spec_float ("margin-left", P_("Margin Left"), P_("Extra space at the left"), 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:margin-right: * * The margin (in pixels) from the right of the actor. * * This property adds a margin to the actor's preferred size; the margin * will be automatically taken into account when allocating the actor. * * The #ClutterActor:margin-right property is animatable. * * Since: 1.10 */ obj_props[PROP_MARGIN_RIGHT] = g_param_spec_float ("margin-right", P_("Margin Right"), P_("Extra space at the right"), 0.0, G_MAXFLOAT, 0.0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:background-color-set: * * Whether the #ClutterActor:background-color property has been set. * * Since: 1.10 */ obj_props[PROP_BACKGROUND_COLOR_SET] = g_param_spec_boolean ("background-color-set", P_("Background Color Set"), P_("Whether the background color is set"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterActor:background-color: * * Paints a solid fill of the actor's allocation using the specified * color. * * The #ClutterActor:background-color property is animatable. * * Since: 1.10 */ obj_props[PROP_BACKGROUND_COLOR] = clutter_param_spec_color ("background-color", P_("Background color"), P_("The actor's background color"), CLUTTER_COLOR_Transparent, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); /** * ClutterActor:first-child: * * The actor's first child. * * Since: 1.10 */ obj_props[PROP_FIRST_CHILD] = g_param_spec_object ("first-child", P_("First Child"), P_("The actor's first child"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READABLE); /** * ClutterActor:last-child: * * The actor's last child. * * Since: 1.10 */ obj_props[PROP_LAST_CHILD] = g_param_spec_object ("last-child", P_("Last Child"), P_("The actor's last child"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READABLE); /** * ClutterActor:content: * * The #ClutterContent implementation that controls the content * of the actor. * * Since: 1.10 */ obj_props[PROP_CONTENT] = g_param_spec_object ("content", P_("Content"), P_("Delegate object for painting the actor's content"), CLUTTER_TYPE_CONTENT, CLUTTER_PARAM_READWRITE); /** * ClutterActor:content-gravity: * * The alignment that should be honoured by the #ClutterContent * set with the #ClutterActor:content property. * * Changing the value of this property will change the bounding box of * the content; you can use the #ClutterActor:content-box property to * get the position and size of the content within the actor's * allocation. * * This property is meaningful only for #ClutterContent implementations * that have a preferred size, and if the preferred size is smaller than * the actor's allocation. * * The #ClutterActor:content-gravity property is animatable. * * Since: 1.10 */ obj_props[PROP_CONTENT_GRAVITY] = g_param_spec_enum ("content-gravity", P_("Content Gravity"), P_("Alignment of the actor's content"), CLUTTER_TYPE_CONTENT_GRAVITY, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL, CLUTTER_PARAM_READWRITE); /** * ClutterActor:content-box: * * The bounding box for the #ClutterContent used by the actor. * * The value of this property is controlled by the #ClutterActor:allocation * and #ClutterActor:content-gravity properties of #ClutterActor. * * The bounding box for the content is guaranteed to never exceed the * allocation's of the actor. * * Since: 1.10 */ obj_props[PROP_CONTENT_BOX] = g_param_spec_boxed ("content-box", P_("Content Box"), P_("The bounding box of the actor's content"), CLUTTER_TYPE_ACTOR_BOX, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); obj_props[PROP_MINIFICATION_FILTER] = g_param_spec_enum ("minification-filter", P_("Minification Filter"), P_("The filter used when reducing the size of the content"), CLUTTER_TYPE_SCALING_FILTER, CLUTTER_SCALING_FILTER_LINEAR, CLUTTER_PARAM_READWRITE); obj_props[PROP_MAGNIFICATION_FILTER] = g_param_spec_enum ("magnification-filter", P_("Magnification Filter"), P_("The filter used when increasing the size of the content"), CLUTTER_TYPE_SCALING_FILTER, CLUTTER_SCALING_FILTER_LINEAR, CLUTTER_PARAM_READWRITE); /** * ClutterActor:content-repeat: * * The repeat policy for the actor's #ClutterActor:content. * * Since: 1.12 */ obj_props[PROP_CONTENT_REPEAT] = g_param_spec_flags ("content-repeat", P_("Content Repeat"), P_("The repeat policy for the actor's content"), CLUTTER_TYPE_CONTENT_REPEAT, CLUTTER_REPEAT_NONE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); /** * ClutterActor::destroy: * @actor: the #ClutterActor which emitted the signal * * The ::destroy signal notifies that all references held on the * actor which emitted it should be released. * * The ::destroy signal should be used by all holders of a reference * on @actor. * * This signal might result in the finalization of the #ClutterActor * if all references are released. * * Composite actors and actors implementing the #ClutterContainer * interface should override the default implementation of the * class handler of this signal and call clutter_actor_destroy() on * their children. When overriding the default class handler, it is * required to chain up to the parent's implementation. * * Since: 0.2 */ actor_signals[DESTROY] = g_signal_new (I_("destroy"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_CLEANUP | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (ClutterActorClass, destroy), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::show: * @actor: the object which received the signal * * The ::show signal is emitted when an actor is visible and * rendered on the stage. * * Since: 0.2 */ actor_signals[SHOW] = g_signal_new (I_("show"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterActorClass, show), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::hide: * @actor: the object which received the signal * * The ::hide signal is emitted when an actor is no longer rendered * on the stage. * * Since: 0.2 */ actor_signals[HIDE] = g_signal_new (I_("hide"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterActorClass, hide), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::parent-set: * @actor: the object which received the signal * @old_parent: (allow-none): the previous parent of the actor, or %NULL * * This signal is emitted when the parent of the actor changes. * * Since: 0.2 */ actor_signals[PARENT_SET] = g_signal_new (I_("parent-set"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, parent_set), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterActor::queue-redraw: * @actor: the actor we're bubbling the redraw request through * @origin: the actor which initiated the redraw request * @volume: paint volume to redraw * * The ::queue_redraw signal is emitted when clutter_actor_queue_redraw() * is called on @origin. * * The default implementation for #ClutterActor chains up to the * parent actor and queues a redraw on the parent, thus "bubbling" * the redraw queue up through the actor graph. The default * implementation for #ClutterStage queues a clutter_stage_ensure_redraw() * in a main loop idle handler. * * Note that the @origin actor may be the stage, or a container; it * does not have to be a leaf node in the actor graph. * * Toolkits embedding a #ClutterStage which require a redraw and * relayout cycle can stop the emission of this signal using the * GSignal API, redraw the UI and then call clutter_stage_ensure_redraw() * themselves, like: * * |[ * static void * on_redraw_complete (gpointer data) * { * ClutterStage *stage = data; * * // execute the Clutter drawing pipeline * clutter_stage_ensure_redraw (stage); * } * * static void * on_stage_queue_redraw (ClutterStage *stage) * { * // this prevents the default handler to run * g_signal_stop_emission_by_name (stage, "queue-redraw"); * * // queue a redraw with the host toolkit and call * // a function when the redraw has been completed * queue_a_redraw (G_CALLBACK (on_redraw_complete), stage); * } * ]| * * Note: This signal is emitted before the Clutter paint * pipeline is executed. If you want to know when the pipeline has * been completed you should use clutter_threads_add_repaint_func() * or clutter_threads_add_repaint_func_full(). * * Since: 1.0 */ actor_signals[QUEUE_REDRAW] = g_signal_new (I_("queue-redraw"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (ClutterActorClass, queue_redraw), g_signal_accumulator_true_handled, NULL, _clutter_marshal_BOOLEAN__OBJECT_BOXED, G_TYPE_BOOLEAN, 2, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_PAINT_VOLUME); g_signal_set_va_marshaller (actor_signals[QUEUE_REDRAW], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__OBJECT_BOXEDv); /** * ClutterActor::queue-relayout: * @actor: the actor being queued for relayout * * The ::queue_layout signal is emitted when clutter_actor_queue_relayout() * is called on an actor. * * The default implementation for #ClutterActor chains up to the * parent actor and queues a relayout on the parent, thus "bubbling" * the relayout queue up through the actor graph. * * The main purpose of this signal is to allow relayout to be propagated * properly in the procense of #ClutterClone actors. Applications will * not normally need to connect to this signal. * * Since: 1.2 */ actor_signals[QUEUE_RELAYOUT] = g_signal_new (I_("queue-relayout"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (ClutterActorClass, queue_relayout), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::event: * @actor: the actor which received the event * @event: a #ClutterEvent * * The ::event signal is emitted each time an event is received * by the @actor. This signal will be emitted on every actor, * following the hierarchy chain, until it reaches the top-level * container (the #ClutterStage). * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[EVENT] = g_signal_new (I_("event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::button-press-event: * @actor: the actor which received the event * @event: (type ClutterButtonEvent): a #ClutterButtonEvent * * The ::button-press-event signal is emitted each time a mouse button * is pressed on @actor. * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[BUTTON_PRESS_EVENT] = g_signal_new (I_("button-press-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, button_press_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[BUTTON_PRESS_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::button-release-event: * @actor: the actor which received the event * @event: (type ClutterButtonEvent): a #ClutterButtonEvent * * The ::button-release-event signal is emitted each time a mouse button * is released on @actor. * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[BUTTON_RELEASE_EVENT] = g_signal_new (I_("button-release-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, button_release_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[BUTTON_RELEASE_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::scroll-event: * @actor: the actor which received the event * @event: (type ClutterScrollEvent): a #ClutterScrollEvent * * The ::scroll-event signal is emitted each time the mouse is * scrolled on @actor * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[SCROLL_EVENT] = g_signal_new (I_("scroll-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, scroll_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[SCROLL_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::key-press-event: * @actor: the actor which received the event * @event: (type ClutterKeyEvent): a #ClutterKeyEvent * * The ::key-press-event signal is emitted each time a keyboard button * is pressed while @actor has key focus (see clutter_stage_set_key_focus()). * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[KEY_PRESS_EVENT] = g_signal_new (I_("key-press-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, key_press_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[KEY_PRESS_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::key-release-event: * @actor: the actor which received the event * @event: (type ClutterKeyEvent): a #ClutterKeyEvent * * The ::key-release-event signal is emitted each time a keyboard button * is released while @actor has key focus (see * clutter_stage_set_key_focus()). * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[KEY_RELEASE_EVENT] = g_signal_new (I_("key-release-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, key_release_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[KEY_RELEASE_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::motion-event: * @actor: the actor which received the event * @event: (type ClutterMotionEvent): a #ClutterMotionEvent * * The ::motion-event signal is emitted each time the mouse pointer is * moved over @actor. * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[MOTION_EVENT] = g_signal_new (I_("motion-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, motion_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[MOTION_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::key-focus-in: * @actor: the actor which now has key focus * * The ::key-focus-in signal is emitted when @actor receives key focus. * * Since: 0.6 */ actor_signals[KEY_FOCUS_IN] = g_signal_new (I_("key-focus-in"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, key_focus_in), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::key-focus-out: * @actor: the actor which now has key focus * * The ::key-focus-out signal is emitted when @actor loses key focus. * * Since: 0.6 */ actor_signals[KEY_FOCUS_OUT] = g_signal_new (I_("key-focus-out"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, key_focus_out), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::enter-event: * @actor: the actor which the pointer has entered. * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent * * The ::enter-event signal is emitted when the pointer enters the @actor * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[ENTER_EVENT] = g_signal_new (I_("enter-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, enter_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[ENTER_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::leave-event: * @actor: the actor which the pointer has left * @event: (type ClutterCrossingEvent): a #ClutterCrossingEvent * * The ::leave-event signal is emitted when the pointer leaves the @actor. * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[LEAVE_EVENT] = g_signal_new (I_("leave-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, leave_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[LEAVE_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::captured-event: * @actor: the actor which received the signal * @event: a #ClutterEvent * * The ::captured-event signal is emitted when an event is captured * by Clutter. This signal will be emitted starting from the top-level * container (the #ClutterStage) to the actor which received the event * going down the hierarchy. This signal can be used to intercept every * event before the specialized events (like * ClutterActor::button-press-event or ::key-released-event) are * emitted. * * Return value: %TRUE if the event has been handled by the actor, * or %FALSE to continue the emission. * * Since: 0.6 */ actor_signals[CAPTURED_EVENT] = g_signal_new (I_("captured-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, G_STRUCT_OFFSET (ClutterActorClass, captured_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[CAPTURED_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); /** * ClutterActor::paint: * @actor: the #ClutterActor that received the signal * @paint_context: a #ClutterPaintContext * * The ::paint signal is emitted each time an actor is being painted. * * Subclasses of #ClutterActor should override the #ClutterActorClass.paint * virtual function paint themselves in that function. * * It is strongly discouraged to connect a signal handler to * the #ClutterActor::paint signal; if you want to change the paint * sequence of an existing #ClutterActor instance, either create a new * #ClutterActor class and override the #ClutterActorClass.paint virtual * function, or use a #ClutterEffect. The #ClutterActor::paint signal * will be removed in a future version of Clutter. * * Since: 0.8 * * Deprecated: 1.12: Override the #ClutterActorClass.paint virtual * function, use a #ClutterContent implementation, or a #ClutterEffect * instead of connecting to this signal. */ actor_signals[PAINT] = g_signal_new (I_("paint"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_HOOKS | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterActorClass, paint), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_PAINT_CONTEXT); /** * ClutterActor::realize: * @actor: the #ClutterActor that received the signal * * The ::realize signal is emitted each time an actor is being * realized. * * Since: 0.8 * * Deprecated: 1.16: The signal should not be used in newly * written code */ actor_signals[REALIZE] = g_signal_new (I_("realize"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterActorClass, realize), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::unrealize: * @actor: the #ClutterActor that received the signal * * The ::unrealize signal is emitted each time an actor is being * unrealized. * * Since: 0.8 * * Deprecated: 1.16: The signal should not be used in newly * written code */ actor_signals[UNREALIZE] = g_signal_new (I_("unrealize"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterActorClass, unrealize), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::pick: * @actor: the #ClutterActor that received the signal * @pick_context: a #ClutterPickContext * * The ::pick signal is emitted each time an actor is being painted * in "pick mode". The pick mode is used to identify the actor during * the event handling phase, or by clutter_stage_get_actor_at_pos(). * * Subclasses of #ClutterActor should override the class signal handler * and paint themselves in that function. * * It is possible to connect a handler to the ::pick signal in order * to set up some custom aspect of a paint in pick mode. * * Since: 1.0 * Deprecated: 1.12: Override the #ClutterActorClass.pick virtual function * instead. */ actor_signals[PICK] = g_signal_new (I_("pick"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterActorClass, pick), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_PICK_CONTEXT); /** * ClutterActor::allocation-changed: * @actor: the #ClutterActor that emitted the signal * @box: a #ClutterActorBox with the new allocation * @flags: #ClutterAllocationFlags for the allocation * * The ::allocation-changed signal is emitted when the * #ClutterActor:allocation property changes. Usually, application * code should just use the notifications for the :allocation property * but if you want to track the allocation flags as well, for instance * to know whether the absolute origin of @actor changed, then you might * want use this signal instead. * * Since: 1.0 */ actor_signals[ALLOCATION_CHANGED] = g_signal_new (I_("allocation-changed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__BOXED_FLAGS, G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR_BOX | G_SIGNAL_TYPE_STATIC_SCOPE, CLUTTER_TYPE_ALLOCATION_FLAGS); g_signal_set_va_marshaller (actor_signals[ALLOCATION_CHANGED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__BOXED_FLAGSv); /** * ClutterActor::transitions-completed: * @actor: a #ClutterActor * * The ::transitions-completed signal is emitted once all transitions * involving @actor are complete. * * Since: 1.10 */ actor_signals[TRANSITIONS_COMPLETED] = g_signal_new (I_("transitions-completed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterActor::transition-stopped: * @actor: a #ClutterActor * @name: the name of the transition * @is_finished: whether the transition was finished, or stopped * * The ::transition-stopped signal is emitted once a transition * is stopped; a transition is stopped once it reached its total * duration (including eventual repeats), it has been stopped * using clutter_timeline_stop(), or it has been removed from the * transitions applied on @actor, using clutter_actor_remove_transition(). * * Since: 1.12 */ actor_signals[TRANSITION_STOPPED] = g_signal_new (I_("transition-stopped"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS | G_SIGNAL_DETAILED, 0, NULL, NULL, _clutter_marshal_VOID__STRING_BOOLEAN, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_BOOLEAN); g_signal_set_va_marshaller (actor_signals[TRANSITION_STOPPED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__STRING_BOOLEANv); /** * ClutterActor::touch-event: * @actor: a #ClutterActor * @event: a #ClutterEvent * * The ::touch-event signal is emitted each time a touch * begin/end/update/cancel event. * * Return value: %CLUTTER_EVENT_STOP if the event has been handled by * the actor, or %CLUTTER_EVENT_PROPAGATE to continue the emission. * * Since: 1.12 */ actor_signals[TOUCH_EVENT] = g_signal_new (I_("touch-event"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterActorClass, touch_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); g_signal_set_va_marshaller (actor_signals[TOUCH_EVENT], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_BOOLEAN__BOXEDv); } static void clutter_actor_init (ClutterActor *self) { ClutterActorPrivate *priv; self->priv = priv = clutter_actor_get_instance_private (self); priv->opacity = 0xff; priv->show_on_set_parent = TRUE; priv->resource_scale = -1.0f; priv->needs_width_request = TRUE; priv->needs_height_request = TRUE; priv->needs_allocation = TRUE; priv->needs_paint_volume_update = TRUE; priv->needs_compute_resource_scale = TRUE; priv->cached_width_age = 1; priv->cached_height_age = 1; priv->opacity_override = -1; priv->enable_model_view_transform = TRUE; /* Initialize an empty paint volume to start with */ _clutter_paint_volume_init_static (&priv->last_paint_volume, NULL); priv->last_paint_volume_valid = TRUE; priv->transform_valid = FALSE; /* the default is to stretch the content, to match the * current behaviour of basically all actors. also, it's * the easiest thing to compute. */ priv->content_gravity = CLUTTER_CONTENT_GRAVITY_RESIZE_FILL; priv->min_filter = CLUTTER_SCALING_FILTER_LINEAR; priv->mag_filter = CLUTTER_SCALING_FILTER_LINEAR; /* this flag will be set to TRUE if the actor gets a child * or if the [xy]-expand flags are explicitly set; until * then, the actor does not need to expand. * * this also allows us to avoid computing the expand flag * when building up a scene. */ priv->needs_compute_expand = FALSE; /* we start with an easing state with duration forcibly set * to 0, for backward compatibility. */ clutter_actor_save_easing_state (self); clutter_actor_set_easing_duration (self, 0); } /** * clutter_actor_new: * * Creates a new #ClutterActor. * * A newly created actor has a floating reference, which will be sunk * when it is added to another actor. * * Return value: the newly created #ClutterActor * * Since: 1.10 */ ClutterActor * clutter_actor_new (void) { return g_object_new (CLUTTER_TYPE_ACTOR, NULL); } /** * clutter_actor_destroy: * @self: a #ClutterActor * * Destroys an actor. When an actor is destroyed, it will break any * references it holds to other objects. If the actor is inside a * container, the actor will be removed. * * When you destroy a container, its children will be destroyed as well. * * Note: you cannot destroy the #ClutterStage returned by * clutter_stage_get_default(). */ void clutter_actor_destroy (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_object_ref (self); /* avoid recursion while destroying */ if (!CLUTTER_ACTOR_IN_DESTRUCTION (self)) { CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION); g_object_run_dispose (G_OBJECT (self)); CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_DESTRUCTION); } g_object_unref (self); } void _clutter_actor_finish_queue_redraw (ClutterActor *self, ClutterPaintVolume *clip) { ClutterActorPrivate *priv = self->priv; ClutterPaintVolume *pv = NULL; /* Remove queue entry early in the process, otherwise a new queue_redraw() during signal handling could put back this object in the stage redraw list (but the entry is freed as soon as we return from this function, causing a segfault later) */ priv->queue_redraw_entry = NULL; /* If we've been explicitly passed a clip volume then there's * nothing more to calculate, but otherwise the only thing we know * is that the change is constrained to the given actor. * * The idea is that if we know the paint volume for where the actor * was last drawn (in eye coordinates) and we also have the paint * volume for where it will be drawn next (in actor coordinates) * then if we queue a redraw for both these volumes that will cover * everything that needs to be redrawn to clear the old view and * show the latest view of the actor. * * Don't clip this redraw if we don't know what position we had for * the previous redraw since we don't know where to set the clip so * it will clear the actor as it is currently. */ if (clip) { pv = clip; } else if (G_LIKELY (priv->last_paint_volume_valid)) { pv = _clutter_actor_get_paint_volume_mutable (self); if (pv) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); /* make sure we redraw the actors old position... */ _clutter_actor_propagate_queue_redraw (stage, stage, &priv->last_paint_volume); } } _clutter_actor_propagate_queue_redraw (self, self, pv); } static void _clutter_actor_get_allocation_clip (ClutterActor *self, ClutterActorBox *clip) { ClutterActorBox allocation; /* XXX: we don't care if we get an out of date allocation here * because clutter_actor_queue_redraw_with_clip knows to ignore * the clip if the actor's allocation is invalid. * * This is noted because clutter_actor_get_allocation_box does some * unnecessary work to support buggy code with a comment suggesting * that it could be changed later which would be good for this use * case! */ clutter_actor_get_allocation_box (self, &allocation); /* NB: clutter_actor_queue_redraw_with_clip expects a box in the * actor's own coordinate space but the allocation is in parent * coordinates */ clip->x1 = 0; clip->y1 = 0; clip->x2 = allocation.x2 - allocation.x1; clip->y2 = allocation.y2 - allocation.y1; } void _clutter_actor_queue_redraw_full (ClutterActor *self, ClutterRedrawFlags flags, const ClutterPaintVolume *volume, ClutterEffect *effect) { ClutterActorPrivate *priv = self->priv; ClutterPaintVolume allocation_pv; ClutterPaintVolume *pv = NULL; ClutterActor *stage; /* Here's an outline of the actor queue redraw mechanism: * * The process starts in one of the following two functions which * are wrappers for this function: * * clutter_actor_queue_redraw() * _clutter_actor_queue_redraw_with_clip() * * additionally, an effect can queue a redraw by wrapping this * function in clutter_effect_queue_repaint(). * * This functions queues an entry in a list associated with the * stage which is a list of actors that queued a redraw while * updating the timelines, performing layouting and processing other * mainloop sources before the next paint starts. * * We aim to minimize the processing done at this point because * there is a good chance other events will happen while updating * the scenegraph that would invalidate any expensive work we might * otherwise try to do here. For example we don't try and resolve * the screen space bounding box of an actor at this stage so as to * minimize how much of the screen redraw because it's possible * something else will happen which will force a full redraw anyway. * * When all updates are complete and we come to paint the stage then * we iterate this list and actually emit the "queue-redraw" signals * for each of the listed actors which will bubble up to the stage * for each actor and at that point we will transform the actors * paint volume into screen coordinates to determine the clip region * for what needs to be redrawn in the next paint. * * Besides minimizing redundant work another reason for this * deferred design is that it's more likely we will be able to * determine the paint volume of an actor once we've finished * updating the scenegraph because its allocation should be up to * date. NB: If we can't determine an actors paint volume then we * can't automatically queue a clipped redraw which can make a big * difference to performance. * * So the control flow goes like this: * One of clutter_actor_queue_redraw(), * _clutter_actor_queue_redraw_with_clip(), * or clutter_effect_queue_repaint() * * then control moves to: * _clutter_stage_queue_actor_redraw() * * later during _clutter_stage_do_update(), once relayouting is done * and the scenegraph has been updated we will call: * clutter_stage_maybe_finish_queue_redraws(). * * clutter_stage_maybe_finish_queue_redraws() will call * _clutter_actor_finish_queue_redraw() for each listed actor. * * Note: actors *are* allowed to queue further redraws during this * process (considering clone actors or texture_new_from_actor which * respond to their source queueing a redraw by queuing a redraw * themselves). We repeat the process until the list is empty. * * This will result in the "queue-redraw" signal being fired for * each actor which will pass control to the default signal handler: * clutter_actor_real_queue_redraw() * * This will bubble up to the stages handler: * clutter_stage_real_queue_redraw() * * clutter_stage_real_queue_redraw() will transform the actors paint * volume into screen space and add it as a clip region for the next * paint. */ /* ignore queueing a redraw for actors being destroyed */ if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; /* we can ignore unmapped actors, unless they have at least one * mapped clone or they are inside a cloned branch of the scene * graph, as unmapped actors will simply be left unpainted. * * this allows us to ignore redraws queued on leaf nodes when one * of their parents has been hidden */ if (!CLUTTER_ACTOR_IS_MAPPED (self) && self->priv->in_cloned_branch == 0 && !clutter_actor_has_mapped_clones (self)) { CLUTTER_NOTE (PAINT, "Skipping queue_redraw('%s'): mapped=%s, " "mapped_clones=%s, " "in_cloned_branch=%s", _clutter_actor_get_debug_name (self), CLUTTER_ACTOR_IS_MAPPED (self) ? "yes" : "no", clutter_actor_has_mapped_clones (self) ? "yes" : "no", self->priv->in_cloned_branch != 0 ? "yes" : "no"); return; } /* given the check above we could end up queueing a redraw on an * unmapped actor with mapped clones, so we cannot assume that * get_stage() will return a Stage */ stage = _clutter_actor_get_stage_internal (self); if (stage == NULL) return; /* ignore queueing a redraw on stages that are being destroyed */ if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return; if (flags & CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION) { ClutterActorBox allocation_clip; graphene_point3d_t origin; /* If the actor doesn't have a valid allocation then we will * queue a full stage redraw. */ if (priv->needs_allocation) { /* NB: NULL denotes an undefined clip which will result in a * full redraw... */ _clutter_actor_propagate_queue_redraw (self, self, NULL); return; } _clutter_paint_volume_init_static (&allocation_pv, self); pv = &allocation_pv; _clutter_actor_get_allocation_clip (self, &allocation_clip); origin.x = allocation_clip.x1; origin.y = allocation_clip.y1; origin.z = 0; clutter_paint_volume_set_origin (pv, &origin); clutter_paint_volume_set_width (pv, allocation_clip.x2 - allocation_clip.x1); clutter_paint_volume_set_height (pv, allocation_clip.y2 - allocation_clip.y1); } self->priv->queue_redraw_entry = _clutter_stage_queue_actor_redraw (CLUTTER_STAGE (stage), priv->queue_redraw_entry, self, pv ? pv : volume); if (pv) clutter_paint_volume_free (pv); /* If this is the first redraw queued then we can directly use the effect parameter */ if (!priv->is_dirty) priv->effect_to_redraw = effect; /* Otherwise we need to merge it with the existing effect parameter */ else if (effect != NULL) { /* If there's already an effect then we need to use whichever is later in the chain of actors. Otherwise a full redraw has already been queued on the actor so we need to ignore the effect parameter */ if (priv->effect_to_redraw != NULL) { if (priv->effects == NULL) g_warning ("Redraw queued with an effect that is " "not applied to the actor"); else { const GList *l; for (l = _clutter_meta_group_peek_metas (priv->effects); l != NULL; l = l->next) { if (l->data == priv->effect_to_redraw || l->data == effect) priv->effect_to_redraw = l->data; } } } } else { /* If no effect is specified then we need to redraw the whole actor */ priv->effect_to_redraw = NULL; } priv->is_dirty = TRUE; } /** * clutter_actor_queue_redraw: * @self: A #ClutterActor * * Queues up a redraw of an actor and any children. The redraw occurs * once the main loop becomes idle (after the current batch of events * has been processed, roughly). * * Applications rarely need to call this, as redraws are handled * automatically by modification functions. * * This function will not do anything if @self is not visible, or * if the actor is inside an invisible part of the scenegraph. * * Also be aware that painting is a NOP for actors with an opacity of * 0 * * When you are implementing a custom actor you must queue a redraw * whenever some private state changes that will affect painting or * picking of your actor. */ void clutter_actor_queue_redraw (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); _clutter_actor_queue_redraw_full (self, 0, /* flags */ NULL, /* clip volume */ NULL /* effect */); } /*< private > * _clutter_actor_queue_redraw_with_clip: * @self: A #ClutterActor * @flags: A mask of #ClutterRedrawFlags controlling the behaviour of * this queue redraw. * @volume: A #ClutterPaintVolume describing the bounds of what needs to be * redrawn or %NULL if you are just using a @flag to state your * desired clipping. * * Queues up a clipped redraw of an actor and any children. The redraw * occurs once the main loop becomes idle (after the current batch of * events has been processed, roughly). * * If no flags are given the clip volume is defined by @volume * specified in actor coordinates and tells Clutter that only content * within this volume has been changed so Clutter can optionally * optimize the redraw. * * If the %CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION @flag is used, @volume * should be %NULL and this tells Clutter to use the actor's current * allocation as a clip box. This flag can only be used for 2D actors, * because any actor with depth may be projected outside its * allocation. * * Applications rarely need to call this, as redraws are handled * automatically by modification functions. * * This function will not do anything if @self is not visible, or if * the actor is inside an invisible part of the scenegraph. * * Also be aware that painting is a NOP for actors with an opacity of * 0 * * When you are implementing a custom actor you must queue a redraw * whenever some private state changes that will affect painting or * picking of your actor. */ void _clutter_actor_queue_redraw_with_clip (ClutterActor *self, ClutterRedrawFlags flags, const ClutterPaintVolume *volume) { _clutter_actor_queue_redraw_full (self, flags, /* flags */ volume, /* clip volume */ NULL /* effect */); } void _clutter_actor_queue_only_relayout (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return; if (priv->needs_width_request && priv->needs_height_request && priv->needs_allocation) return; /* save some cpu cycles */ #ifdef CLUTTER_ENABLE_DEBUG if (!CLUTTER_ACTOR_IS_TOPLEVEL (self) && CLUTTER_ACTOR_IN_RELAYOUT (self)) { g_warning ("The actor '%s' is currently inside an allocation " "cycle; calling clutter_actor_queue_relayout() is " "not recommended", _clutter_actor_get_debug_name (self)); } #endif /* CLUTTER_ENABLE_DEBUG */ _clutter_actor_queue_relayout_on_clones (self); g_signal_emit (self, actor_signals[QUEUE_RELAYOUT], 0); } /** * clutter_actor_queue_redraw_with_clip: * @self: a #ClutterActor * @clip: (allow-none): a rectangular clip region, or %NULL * * Queues a redraw on @self limited to a specific, actor-relative * rectangular area. * * If @clip is %NULL this function is equivalent to * clutter_actor_queue_redraw(). * * Since: 1.10 */ void clutter_actor_queue_redraw_with_clip (ClutterActor *self, const cairo_rectangle_int_t *clip) { ClutterPaintVolume volume; graphene_point3d_t origin; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (clip == NULL) { clutter_actor_queue_redraw (self); return; } _clutter_paint_volume_init_static (&volume, self); origin.x = clip->x; origin.y = clip->y; origin.z = 0.0f; clutter_paint_volume_set_origin (&volume, &origin); clutter_paint_volume_set_width (&volume, clip->width); clutter_paint_volume_set_height (&volume, clip->height); _clutter_actor_queue_redraw_full (self, 0, &volume, NULL); clutter_paint_volume_free (&volume); } /** * clutter_actor_queue_relayout: * @self: A #ClutterActor * * Indicates that the actor's size request or other layout-affecting * properties may have changed. This function is used inside #ClutterActor * subclass implementations, not by applications directly. * * Queueing a new layout automatically queues a redraw as well. * * Since: 0.8 */ void clutter_actor_queue_relayout (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); _clutter_actor_queue_only_relayout (self); clutter_actor_queue_redraw (self); } /** * clutter_actor_get_preferred_size: * @self: a #ClutterActor * @min_width_p: (out) (allow-none): return location for the minimum * width, or %NULL * @min_height_p: (out) (allow-none): return location for the minimum * height, or %NULL * @natural_width_p: (out) (allow-none): return location for the natural * width, or %NULL * @natural_height_p: (out) (allow-none): return location for the natural * height, or %NULL * * Computes the preferred minimum and natural size of an actor, taking into * account the actor's geometry management (either height-for-width * or width-for-height). * * The width and height used to compute the preferred height and preferred * width are the actor's natural ones. * * If you need to control the height for the preferred width, or the width for * the preferred height, you should use clutter_actor_get_preferred_width() * and clutter_actor_get_preferred_height(), and check the actor's preferred * geometry management using the #ClutterActor:request-mode property. * * Since: 0.8 */ void clutter_actor_get_preferred_size (ClutterActor *self, gfloat *min_width_p, gfloat *min_height_p, gfloat *natural_width_p, gfloat *natural_height_p) { ClutterActorPrivate *priv; gfloat min_width, min_height; gfloat natural_width, natural_height; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; min_width = min_height = 0; natural_width = natural_height = 0; if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { CLUTTER_NOTE (LAYOUT, "Preferred size (height-for-width)"); clutter_actor_get_preferred_width (self, -1, &min_width, &natural_width); clutter_actor_get_preferred_height (self, natural_width, &min_height, &natural_height); } else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { CLUTTER_NOTE (LAYOUT, "Preferred size (width-for-height)"); clutter_actor_get_preferred_height (self, -1, &min_height, &natural_height); clutter_actor_get_preferred_width (self, natural_height, &min_width, &natural_width); } else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE) { CLUTTER_NOTE (LAYOUT, "Preferred size (content-size)"); if (priv->content != NULL) clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height); } else { CLUTTER_NOTE (LAYOUT, "Unknown request mode"); } if (min_width_p) *min_width_p = min_width; if (min_height_p) *min_height_p = min_height; if (natural_width_p) *natural_width_p = natural_width; if (natural_height_p) *natural_height_p = natural_height; } /*< private > * effective_align: * @align: a #ClutterActorAlign * @direction: a #ClutterTextDirection * * Retrieves the correct alignment depending on the text direction * * Return value: the effective alignment */ static ClutterActorAlign effective_align (ClutterActorAlign align, ClutterTextDirection direction) { ClutterActorAlign res; switch (align) { case CLUTTER_ACTOR_ALIGN_START: res = (direction == CLUTTER_TEXT_DIRECTION_RTL) ? CLUTTER_ACTOR_ALIGN_END : CLUTTER_ACTOR_ALIGN_START; break; case CLUTTER_ACTOR_ALIGN_END: res = (direction == CLUTTER_TEXT_DIRECTION_RTL) ? CLUTTER_ACTOR_ALIGN_START : CLUTTER_ACTOR_ALIGN_END; break; default: res = align; break; } return res; } /*< private > * _clutter_actor_get_effective_x_align: * @self: a #ClutterActor * * Retrieves the effective horizontal alignment, taking into * consideration the text direction of @self. * * Return value: the effective horizontal alignment */ ClutterActorAlign _clutter_actor_get_effective_x_align (ClutterActor *self) { return effective_align (clutter_actor_get_x_align (self), clutter_actor_get_text_direction (self)); } static inline void adjust_for_margin (float margin_start, float margin_end, float *minimum_size, float *natural_size, float *allocated_start, float *allocated_end) { float min_size = *minimum_size; float nat_size = *natural_size; float start = *allocated_start; float end = *allocated_end; min_size = MAX (min_size - (margin_start + margin_end), 0); nat_size = MAX (nat_size - (margin_start + margin_end), 0); *minimum_size = min_size; *natural_size = nat_size; start += margin_start; end -= margin_end; if (end - start >= 0) { *allocated_start = start; *allocated_end = end; } } static inline void adjust_for_alignment (ClutterActorAlign alignment, float natural_size, float *allocated_start, float *allocated_end) { float allocated_size = *allocated_end - *allocated_start; if (allocated_size <= 0.f) return; switch (alignment) { case CLUTTER_ACTOR_ALIGN_FILL: /* do nothing */ break; case CLUTTER_ACTOR_ALIGN_START: /* keep start */ *allocated_end = *allocated_start + MIN (natural_size, allocated_size); break; case CLUTTER_ACTOR_ALIGN_END: if (allocated_size > natural_size) { *allocated_start += (allocated_size - natural_size); *allocated_end = *allocated_start + natural_size; } break; case CLUTTER_ACTOR_ALIGN_CENTER: if (allocated_size > natural_size) { *allocated_start += floorf ((allocated_size - natural_size) / 2); *allocated_end = *allocated_start + MIN (allocated_size, natural_size); } break; } } /*< private > * clutter_actor_adjust_width: * @self: a #ClutterActor * @minimum_width: (inout): the actor's preferred minimum width, which * will be adjusted depending on the margin * @natural_width: (inout): the actor's preferred natural width, which * will be adjusted depending on the margin * @adjusted_x1: (out): the adjusted x1 for the actor's bounding box * @adjusted_x2: (out): the adjusted x2 for the actor's bounding box * * Adjusts the preferred and allocated position and size of an actor, * depending on the margin and alignment properties. */ static void clutter_actor_adjust_width (ClutterActor *self, gfloat *minimum_width, gfloat *natural_width, gfloat *adjusted_x1, gfloat *adjusted_x2) { ClutterTextDirection text_dir; const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (self); text_dir = clutter_actor_get_text_direction (self); CLUTTER_NOTE (LAYOUT, "Adjusting allocated X and width"); /* this will tweak natural_width to remove the margin, so that * adjust_for_alignment() will use the correct size */ adjust_for_margin (info->margin.left, info->margin.right, minimum_width, natural_width, adjusted_x1, adjusted_x2); adjust_for_alignment (effective_align (info->x_align, text_dir), *natural_width, adjusted_x1, adjusted_x2); } /*< private > * clutter_actor_adjust_height: * @self: a #ClutterActor * @minimum_height: (inout): the actor's preferred minimum height, which * will be adjusted depending on the margin * @natural_height: (inout): the actor's preferred natural height, which * will be adjusted depending on the margin * @adjusted_y1: (out): the adjusted y1 for the actor's bounding box * @adjusted_y2: (out): the adjusted y2 for the actor's bounding box * * Adjusts the preferred and allocated position and size of an actor, * depending on the margin and alignment properties. */ static void clutter_actor_adjust_height (ClutterActor *self, gfloat *minimum_height, gfloat *natural_height, gfloat *adjusted_y1, gfloat *adjusted_y2) { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (self); CLUTTER_NOTE (LAYOUT, "Adjusting allocated Y and height"); /* this will tweak natural_height to remove the margin, so that * adjust_for_alignment() will use the correct size */ adjust_for_margin (info->margin.top, info->margin.bottom, minimum_height, natural_height, adjusted_y1, adjusted_y2); /* we don't use effective_align() here, because text direction * only affects the horizontal axis */ adjust_for_alignment (info->y_align, *natural_height, adjusted_y1, adjusted_y2); } /* looks for a cached size request for this for_size. If not * found, returns the oldest entry so it can be overwritten */ static gboolean _clutter_actor_get_cached_size_request (gfloat for_size, SizeRequest *cached_size_requests, SizeRequest **result) { guint i; *result = &cached_size_requests[0]; for (i = 0; i < N_CACHED_SIZE_REQUESTS; i++) { SizeRequest *sr; sr = &cached_size_requests[i]; if (sr->age > 0 && sr->for_size == for_size) { CLUTTER_NOTE (LAYOUT, "Size cache hit for size: %.2f", for_size); *result = sr; return TRUE; } else if (sr->age < (*result)->age) { *result = sr; } } CLUTTER_NOTE (LAYOUT, "Size cache miss for size: %.2f", for_size); return FALSE; } static void clutter_actor_update_preferred_size_for_constraints (ClutterActor *self, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size) { ClutterActorPrivate *priv = self->priv; const GList *constraints, *l; if (priv->constraints == NULL) return; constraints = _clutter_meta_group_peek_metas (priv->constraints); for (l = constraints; l != NULL; l = l->next) { ClutterConstraint *constraint = l->data; ClutterActorMeta *meta = l->data; if (!clutter_actor_meta_get_enabled (meta)) continue; clutter_constraint_update_preferred_size (constraint, self, direction, for_size, minimum_size, natural_size); CLUTTER_NOTE (LAYOUT, "Preferred %s of '%s' after constraint '%s': " "{ min:%.2f, nat:%.2f }", direction == CLUTTER_ORIENTATION_HORIZONTAL ? "width" : "height", _clutter_actor_get_debug_name (self), _clutter_actor_meta_get_debug_name (meta), *minimum_size, *natural_size); } } /** * clutter_actor_get_preferred_width: * @self: A #ClutterActor * @for_height: available height when computing the preferred width, * or a negative value to indicate that no height is defined * @min_width_p: (out) (allow-none): return location for minimum width, * or %NULL * @natural_width_p: (out) (allow-none): return location for the natural * width, or %NULL * * Computes the requested minimum and natural widths for an actor, * optionally depending on the specified height, or if they are * already computed, returns the cached values. * * An actor may not get its request - depending on the layout * manager that's in effect. * * A request should not incorporate the actor's scale or anchor point; * those transformations do not affect layout, only rendering. * * Since: 0.8 */ void clutter_actor_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { float request_min_width, request_natural_width; SizeRequest *cached_size_request; const ClutterLayoutInfo *info; ClutterActorPrivate *priv; gboolean found_in_cache; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; info = _clutter_actor_get_layout_info_or_defaults (self); /* we shortcircuit the case of a fixed size set using set_width() */ if (priv->min_width_set && priv->natural_width_set) { if (min_width_p != NULL) *min_width_p = info->minimum.width + (info->margin.left + info->margin.right); if (natural_width_p != NULL) *natural_width_p = info->natural.width + (info->margin.left + info->margin.right); return; } /* if the request mode is CONTENT_SIZE we simply return the content width */ if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE) { float content_width = 0.f; if (priv->content != NULL) clutter_content_get_preferred_size (priv->content, &content_width, NULL); if (min_width_p != NULL) *min_width_p = content_width; if (natural_width_p != NULL) *natural_width_p = content_width; return; } CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH); /* the remaining cases are: * * - either min_width or natural_width have been set * - neither min_width or natural_width have been set * * in both cases, we go through the cache (and through the actor in case * of cache misses) and determine the authoritative value depending on * the *_set flags. */ if (!priv->needs_width_request) { found_in_cache = _clutter_actor_get_cached_size_request (for_height, priv->width_requests, &cached_size_request); } else { /* if the actor needs a width request we use the first slot */ found_in_cache = FALSE; cached_size_request = &priv->width_requests[0]; } if (!found_in_cache) { gfloat minimum_width, natural_width; ClutterActorClass *klass; minimum_width = natural_width = 0; /* adjust for the margin */ if (for_height >= 0) { for_height -= (info->margin.top + info->margin.bottom); if (for_height < 0) for_height = 0; } CLUTTER_NOTE (LAYOUT, "Width request for %.2f px", for_height); klass = CLUTTER_ACTOR_GET_CLASS (self); klass->get_preferred_width (self, for_height, &minimum_width, &natural_width); /* adjust for constraints */ clutter_actor_update_preferred_size_for_constraints (self, CLUTTER_ORIENTATION_HORIZONTAL, for_height, &minimum_width, &natural_width); /* adjust for the margin */ minimum_width += (info->margin.left + info->margin.right); natural_width += (info->margin.left + info->margin.right); /* Due to accumulated float errors, it's better not to warn * on this, but just fix it. */ if (natural_width < minimum_width) natural_width = minimum_width; cached_size_request->min_size = minimum_width; cached_size_request->natural_size = natural_width; cached_size_request->for_size = for_height; cached_size_request->age = priv->cached_width_age; priv->cached_width_age += 1; priv->needs_width_request = FALSE; } if (!priv->min_width_set) request_min_width = cached_size_request->min_size; else request_min_width = info->margin.left + info->minimum.width + info->margin.right; if (!priv->natural_width_set) request_natural_width = cached_size_request->natural_size; else request_natural_width = info->margin.left + info->natural.width + info->margin.right; if (min_width_p) *min_width_p = request_min_width; if (natural_width_p) *natural_width_p = request_natural_width; CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_WIDTH); } /** * clutter_actor_get_preferred_height: * @self: A #ClutterActor * @for_width: available width to assume in computing desired height, * or a negative value to indicate that no width is defined * @min_height_p: (out) (allow-none): return location for minimum height, * or %NULL * @natural_height_p: (out) (allow-none): return location for natural * height, or %NULL * * Computes the requested minimum and natural heights for an actor, * or if they are already computed, returns the cached values. * * An actor may not get its request - depending on the layout * manager that's in effect. * * A request should not incorporate the actor's scale or anchor point; * those transformations do not affect layout, only rendering. * * Since: 0.8 */ void clutter_actor_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { float request_min_height, request_natural_height; SizeRequest *cached_size_request; const ClutterLayoutInfo *info; ClutterActorPrivate *priv; gboolean found_in_cache; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; info = _clutter_actor_get_layout_info_or_defaults (self); /* we shortcircuit the case of a fixed size set using set_height() */ if (priv->min_height_set && priv->natural_height_set) { if (min_height_p != NULL) *min_height_p = info->minimum.height + (info->margin.top + info->margin.bottom); if (natural_height_p != NULL) *natural_height_p = info->natural.height + (info->margin.top + info->margin.bottom); return; } /* if the request mode is CONTENT_SIZE we simply return the content height */ if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE) { float content_height = 0.f; if (priv->content != NULL) clutter_content_get_preferred_size (priv->content, NULL, &content_height); if (min_height_p != NULL) *min_height_p = content_height; if (natural_height_p != NULL) *natural_height_p = content_height; return; } CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT); /* the remaining cases are: * * - either min_height or natural_height have been set * - neither min_height or natural_height have been set * * in both cases, we go through the cache (and through the actor in case * of cache misses) and determine the authoritative value depending on * the *_set flags. */ if (!priv->needs_height_request) { found_in_cache = _clutter_actor_get_cached_size_request (for_width, priv->height_requests, &cached_size_request); } else { found_in_cache = FALSE; cached_size_request = &priv->height_requests[0]; } if (!found_in_cache) { gfloat minimum_height, natural_height; ClutterActorClass *klass; minimum_height = natural_height = 0; CLUTTER_NOTE (LAYOUT, "Height request for %.2f px", for_width); /* adjust for margin */ if (for_width >= 0) { for_width -= (info->margin.left + info->margin.right); if (for_width < 0) for_width = 0; } klass = CLUTTER_ACTOR_GET_CLASS (self); klass->get_preferred_height (self, for_width, &minimum_height, &natural_height); /* adjust for constraints */ clutter_actor_update_preferred_size_for_constraints (self, CLUTTER_ORIENTATION_VERTICAL, for_width, &minimum_height, &natural_height); /* adjust for margin */ minimum_height += (info->margin.top + info->margin.bottom); natural_height += (info->margin.top + info->margin.bottom); /* Due to accumulated float errors, it's better not to warn * on this, but just fix it. */ if (natural_height < minimum_height) natural_height = minimum_height; cached_size_request->min_size = minimum_height; cached_size_request->natural_size = natural_height; cached_size_request->for_size = for_width; cached_size_request->age = priv->cached_height_age; priv->cached_height_age += 1; priv->needs_height_request = FALSE; } if (!priv->min_height_set) request_min_height = cached_size_request->min_size; else request_min_height = info->margin.top + info->minimum.height + info->margin.bottom; if (!priv->natural_height_set) request_natural_height = cached_size_request->natural_size; else request_natural_height = info->margin.top + info->natural.height + info->margin.bottom; if (min_height_p) *min_height_p = request_min_height; if (natural_height_p) *natural_height_p = request_natural_height; CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_PREF_HEIGHT); } /** * clutter_actor_get_allocation_box: * @self: A #ClutterActor * @box: (out): the function fills this in with the actor's allocation * * Gets the layout box an actor has been assigned. The allocation can * only be assumed valid inside a paint() method; anywhere else, it * may be out-of-date. * * An allocation does not incorporate the actor's scale or anchor point; * those transformations do not affect layout, only rendering. * * Do not call any of the clutter_actor_get_allocation_*() family * of functions inside the implementation of the get_preferred_width() * or get_preferred_height() virtual functions. * * Since: 0.8 */ void clutter_actor_get_allocation_box (ClutterActor *self, ClutterActorBox *box) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* XXX - if needs_allocation=TRUE, we can either 1) g_return_if_fail, * which limits calling get_allocation to inside paint() basically; or * we can 2) force a layout, which could be expensive if someone calls * get_allocation somewhere silly; or we can 3) just return the latest * value, allowing it to be out-of-date, and assume people know what * they are doing. * * The least-surprises approach that keeps existing code working is * likely to be 2). People can end up doing some inefficient things, * though, and in general code that requires 2) is probably broken. */ /* this implements 2) */ if (G_UNLIKELY (self->priv->needs_allocation)) { ClutterActor *stage = _clutter_actor_get_stage_internal (self); /* do not queue a relayout on an unparented actor */ if (stage) _clutter_stage_maybe_relayout (stage); } /* commenting out the code above and just keeping this assigment * implements 3) */ *box = self->priv->allocation; } static void clutter_actor_update_constraints (ClutterActor *self, ClutterActorBox *allocation) { ClutterActorPrivate *priv = self->priv; const GList *constraints, *l; if (priv->constraints == NULL) return; constraints = _clutter_meta_group_peek_metas (priv->constraints); for (l = constraints; l != NULL; l = l->next) { ClutterConstraint *constraint = l->data; ClutterActorMeta *meta = l->data; gboolean changed = FALSE; if (clutter_actor_meta_get_enabled (meta)) { changed |= clutter_constraint_update_allocation (constraint, self, allocation); CLUTTER_NOTE (LAYOUT, "Allocation of '%s' after constraint '%s': " "{ %.2f, %.2f, %.2f, %.2f } (changed:%s)", _clutter_actor_get_debug_name (self), _clutter_actor_meta_get_debug_name (meta), allocation->x1, allocation->y1, allocation->x2, allocation->y2, changed ? "yes" : "no"); } } } /*< private > * clutter_actor_adjust_allocation: * @self: a #ClutterActor * @allocation: (inout): the allocation to adjust * * Adjusts the passed allocation box taking into account the actor's * layout information, like alignment, expansion, and margin. */ static void clutter_actor_adjust_allocation (ClutterActor *self, ClutterActorBox *allocation) { ClutterActorBox adj_allocation; float alloc_width, alloc_height; float min_width, min_height; float nat_width, nat_height; ClutterRequestMode req_mode; adj_allocation = *allocation; clutter_actor_box_get_size (allocation, &alloc_width, &alloc_height); /* There's no point in trying to adjust a zero-sized actor */ if (alloc_width == 0.f && alloc_height == 0.f) return; /* we want to hit the cache, so we use the public API */ req_mode = clutter_actor_get_request_mode (self); if (req_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (self, -1, &min_width, &nat_width); clutter_actor_get_preferred_height (self, alloc_width, &min_height, &nat_height); } else if (req_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { clutter_actor_get_preferred_height (self, -1, &min_height, &nat_height); clutter_actor_get_preferred_width (self, alloc_height, &min_width, &nat_width); } else if (req_mode == CLUTTER_REQUEST_CONTENT_SIZE) { min_width = min_height = 0; nat_width = nat_height = 0; if (self->priv->content != NULL) clutter_content_get_preferred_size (self->priv->content, &nat_width, &nat_height); } #ifdef CLUTTER_ENABLE_DEBUG /* warn about underallocations */ if (_clutter_diagnostic_enabled () && (floorf (min_width - alloc_width) > 0 || floorf (min_height - alloc_height) > 0)) { ClutterActor *parent = clutter_actor_get_parent (self); /* the only actors that are allowed to be underallocated are the Stage, * as it doesn't have an implicit size, and Actors that specifically * told us that they want to opt-out from layout control mechanisms * through the NO_LAYOUT escape hatch. */ if (parent != NULL && !(self->flags & CLUTTER_ACTOR_NO_LAYOUT) != 0) { g_warning (G_STRLOC ": The actor '%s' is getting an allocation " "of %.2f x %.2f from its parent actor '%s', but its " "requested minimum size is of %.2f x %.2f", _clutter_actor_get_debug_name (self), alloc_width, alloc_height, _clutter_actor_get_debug_name (parent), min_width, min_height); } } #endif clutter_actor_adjust_width (self, &min_width, &nat_width, &adj_allocation.x1, &adj_allocation.x2); clutter_actor_adjust_height (self, &min_height, &nat_height, &adj_allocation.y1, &adj_allocation.y2); /* we maintain the invariant that an allocation cannot be adjusted * to be outside the parent-given box */ if (adj_allocation.x1 < allocation->x1 || adj_allocation.y1 < allocation->y1 || adj_allocation.x2 > allocation->x2 || adj_allocation.y2 > allocation->y2) { g_warning (G_STRLOC ": The actor '%s' tried to adjust its allocation " "to { %.2f, %.2f, %.2f, %.2f }, which is outside of its " "original allocation of { %.2f, %.2f, %.2f, %.2f }", _clutter_actor_get_debug_name (self), adj_allocation.x1, adj_allocation.y1, adj_allocation.x2 - adj_allocation.x1, adj_allocation.y2 - adj_allocation.y1, allocation->x1, allocation->y1, allocation->x2 - allocation->x1, allocation->y2 - allocation->y1); return; } *allocation = adj_allocation; } static void clutter_actor_allocate_internal (ClutterActor *self, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterActorClass *klass; CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT); CLUTTER_NOTE (LAYOUT, "Calling %s::allocate()", _clutter_actor_get_debug_name (self)); klass = CLUTTER_ACTOR_GET_CLASS (self); klass->allocate (self, allocation, flags); CLUTTER_UNSET_PRIVATE_FLAGS (self, CLUTTER_IN_RELAYOUT); /* Caller should call clutter_actor_queue_redraw() if needed * for that particular case. */ } /** * clutter_actor_allocate: * @self: A #ClutterActor * @box: new allocation of the actor, in parent-relative coordinates * @flags: flags that control the allocation * * Assigns the size of a #ClutterActor from the given @box. * * This function should only be called on the children of an actor when * overriding the #ClutterActorClass.allocate() virtual function. * * This function will adjust the stored allocation to take into account * the alignment flags set in the #ClutterActor:x-align and * #ClutterActor:y-align properties, as well as the margin values set in * the #ClutterActor:margin-top, #ClutterActor:margin-right, * #ClutterActor:margin-bottom, and #ClutterActor:margin-left properties. * * This function will respect the easing state of the #ClutterActor and * interpolate between the current allocation and the new one if the * easing state duration is a positive value. * * Actors can know from their allocation box whether they have moved * with respect to their parent actor. The @flags parameter describes * additional information about the allocation, for instance whether * the parent has moved with respect to the stage, for example because * a grandparent's origin has moved. * * Since: 0.8 */ void clutter_actor_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorBox old_allocation, real_allocation; gboolean origin_changed, child_moved, size_changed; gboolean stage_allocation_changed; ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (G_UNLIKELY (_clutter_actor_get_stage_internal (self) == NULL)) { g_warning ("Spurious clutter_actor_allocate called for actor %p/%s " "which isn't a descendent of the stage!\n", self, _clutter_actor_get_debug_name (self)); return; } if (!clutter_actor_is_visible (self)) return; priv = self->priv; old_allocation = priv->allocation; real_allocation = *box; g_return_if_fail (!isnan (real_allocation.x1) && !isnan (real_allocation.x2) && !isnan (real_allocation.y1) && !isnan (real_allocation.y2)); /* constraints are allowed to modify the allocation only here; we do * this prior to all the other checks so that we can bail out if the * allocation did not change */ clutter_actor_update_constraints (self, &real_allocation); /* adjust the allocation depending on the align/margin properties */ clutter_actor_adjust_allocation (self, &real_allocation); if (real_allocation.x2 < real_allocation.x1 || real_allocation.y2 < real_allocation.y1) { g_warning (G_STRLOC ": Actor '%s' tried to allocate a size of %.2f x %.2f", _clutter_actor_get_debug_name (self), real_allocation.x2 - real_allocation.x1, real_allocation.y2 - real_allocation.y1); } /* we allow 0-sized actors, but not negative-sized ones */ real_allocation.x2 = MAX (real_allocation.x2, real_allocation.x1); real_allocation.y2 = MAX (real_allocation.y2, real_allocation.y1); origin_changed = (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED); child_moved = (real_allocation.x1 != old_allocation.x1 || real_allocation.y1 != old_allocation.y1); size_changed = (real_allocation.x2 != old_allocation.x2 || real_allocation.y2 != old_allocation.y2); if (origin_changed || child_moved || size_changed) stage_allocation_changed = TRUE; else stage_allocation_changed = FALSE; /* If we get an allocation "out of the blue" * (we did not queue relayout), then we want to * ignore it. But if we have needs_allocation set, * we want to guarantee that allocate() virtual * method is always called, i.e. that queue_relayout() * always results in an allocate() invocation on * an actor. * * The optimization here is to avoid re-allocating * actors that did not queue relayout and were * not moved. */ if (!priv->needs_allocation && !stage_allocation_changed) { CLUTTER_NOTE (LAYOUT, "No allocation needed"); return; } if (CLUTTER_ACTOR_IS_MAPPED (self)) self->priv->needs_paint_volume_update = TRUE; if (stage_allocation_changed) priv->needs_compute_resource_scale = TRUE; if (!stage_allocation_changed) { /* If the actor didn't move but needs_allocation is set, we just * need to allocate the children */ clutter_actor_allocate_internal (self, &real_allocation, flags); return; } /* When ABSOLUTE_ORIGIN_CHANGED is passed in to * clutter_actor_allocate(), it indicates whether the parent has its * absolute origin moved; when passed in to ClutterActor::allocate() * virtual method though, it indicates whether the child has its * absolute origin moved. So we set it when child_moved is TRUE */ if (child_moved) flags |= CLUTTER_ABSOLUTE_ORIGIN_CHANGED; /* store the flags here, so that they can be propagated by the * transition code */ self->priv->allocation_flags = flags; _clutter_actor_create_transition (self, obj_props[PROP_ALLOCATION], &priv->allocation, &real_allocation); } /** * clutter_actor_set_allocation: * @self: a #ClutterActor * @box: a #ClutterActorBox * @flags: allocation flags * * Stores the allocation of @self as defined by @box. * * This function can only be called from within the implementation of * the #ClutterActorClass.allocate() virtual function. * * The allocation should have been adjusted to take into account constraints, * alignment, and margin properties. If you are implementing a #ClutterActor * subclass that provides its own layout management policy for its children * instead of using a #ClutterLayoutManager delegate, you should not call * this function on the children of @self; instead, you should call * clutter_actor_allocate(), which will adjust the allocation box for * you. * * This function should only be used by subclasses of #ClutterActor * that wish to store their allocation but cannot chain up to the * parent's implementation; the default implementation of the * #ClutterActorClass.allocate() virtual function will call this * function. * * It is important to note that, while chaining up was the recommended * behaviour for #ClutterActor subclasses prior to the introduction of * this function, it is recommended to call clutter_actor_set_allocation() * instead. * * If the #ClutterActor is using a #ClutterLayoutManager delegate object * to handle the allocation of its children, this function will call * the clutter_layout_manager_allocate() function only if the * %CLUTTER_DELEGATE_LAYOUT flag is set on @flags, otherwise it is * expected that the subclass will call clutter_layout_manager_allocate() * by itself. For instance, the following code: * * |[ * static void * my_actor_allocate (ClutterActor *actor, * const ClutterActorBox *allocation, * ClutterAllocationFlags flags) * { * ClutterActorBox new_alloc; * ClutterAllocationFlags new_flags; * * adjust_allocation (allocation, &new_alloc); * * new_flags = flags | CLUTTER_DELEGATE_LAYOUT; * * // this will use the layout manager set on the actor * clutter_actor_set_allocation (actor, &new_alloc, new_flags); * } * ]| * * is equivalent to this: * * |[ * static void * my_actor_allocate (ClutterActor *actor, * const ClutterActorBox *allocation, * ClutterAllocationFlags flags) * { * ClutterLayoutManager *layout; * ClutterActorBox new_alloc; * * adjust_allocation (allocation, &new_alloc); * * clutter_actor_set_allocation (actor, &new_alloc, flags); * * layout = clutter_actor_get_layout_manager (actor); * clutter_layout_manager_allocate (layout, * CLUTTER_CONTAINER (actor), * &new_alloc, * flags); * } * ]| * * Since: 1.10 */ void clutter_actor_set_allocation (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterActorPrivate *priv; gboolean changed; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); if (G_UNLIKELY (!CLUTTER_ACTOR_IN_RELAYOUT (self))) { g_critical (G_STRLOC ": The clutter_actor_set_allocation() function " "can only be called from within the implementation of " "the ClutterActor::allocate() virtual function."); return; } priv = self->priv; g_object_freeze_notify (G_OBJECT (self)); changed = clutter_actor_set_allocation_internal (self, box, flags); /* we allocate our children before we notify changes in our geometry, * so that people connecting to properties will be able to get valid * data out of the sub-tree of the scene graph that has this actor at * the root. */ clutter_actor_maybe_layout_children (self, box, flags); if (changed) { ClutterActorBox signal_box = priv->allocation; ClutterAllocationFlags signal_flags = priv->allocation_flags; g_signal_emit (self, actor_signals[ALLOCATION_CHANGED], 0, &signal_box, signal_flags); } g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_set_position: * @self: A #ClutterActor * @x: New left position of actor in pixels. * @y: New top position of actor in pixels. * * Sets the actor's fixed position in pixels relative to any parent * actor. * * If a layout manager is in use, this position will override the * layout manager and force a fixed position. */ void clutter_actor_set_position (ClutterActor *self, gfloat x, gfloat y) { graphene_point_t new_position; graphene_point_t cur_position; g_return_if_fail (CLUTTER_IS_ACTOR (self)); graphene_point_init (&new_position, x, y); cur_position.x = clutter_actor_get_x (self); cur_position.y = clutter_actor_get_y (self); if (!graphene_point_equal (&cur_position, &new_position)) _clutter_actor_create_transition (self, obj_props[PROP_POSITION], &cur_position, &new_position); } /** * clutter_actor_get_fixed_position_set: * @self: A #ClutterActor * * Checks whether an actor has a fixed position set (and will thus be * unaffected by any layout manager). * * Return value: %TRUE if the fixed position is set on the actor * * Since: 0.8 */ gboolean clutter_actor_get_fixed_position_set (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return self->priv->position_set; } /** * clutter_actor_set_fixed_position_set: * @self: A #ClutterActor * @is_set: whether to use fixed position * * Sets whether an actor has a fixed position set (and will thus be * unaffected by any layout manager). * * Since: 0.8 */ void clutter_actor_set_fixed_position_set (ClutterActor *self, gboolean is_set) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->position_set == (is_set != FALSE)) return; if (!is_set) { ClutterLayoutInfo *info; /* Ensure we set back the default fixed position of 0,0 so that setting just one of x/y always atomically gets 0 for the other */ info = _clutter_actor_peek_layout_info (self); if (info != NULL) { info->fixed_pos.x = 0; info->fixed_pos.y = 0; } } self->priv->position_set = is_set != FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FIXED_POSITION_SET]); clutter_actor_queue_relayout (self); } /** * clutter_actor_move_by: * @self: A #ClutterActor * @dx: Distance to move Actor on X axis. * @dy: Distance to move Actor on Y axis. * * Moves an actor by the specified distance relative to its current * position in pixels. * * This function modifies the fixed position of an actor and thus removes * it from any layout management. Another way to move an actor is with an * anchor point, see clutter_actor_set_anchor_point(), or with an additional * translation, using clutter_actor_set_translation(). * * Since: 0.2 */ void clutter_actor_move_by (ClutterActor *self, gfloat dx, gfloat dy) { const ClutterLayoutInfo *info; gfloat x, y; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_layout_info_or_defaults (self); x = info->fixed_pos.x; y = info->fixed_pos.y; clutter_actor_set_position (self, x + dx, y + dy); } static void clutter_actor_set_min_width (ClutterActor *self, gfloat min_width) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; ClutterLayoutInfo *info; /* if we are setting the size on a top-level actor and the * backend only supports static top-levels (e.g. framebuffers) * then we ignore the passed value and we override it with * the stage implementation's preferred size. */ if (CLUTTER_ACTOR_IS_TOPLEVEL (self) && clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) return; info = _clutter_actor_get_layout_info (self); if (priv->min_width_set && min_width == info->minimum.width) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_store_old_geometry (self, &old); info->minimum.width = min_width; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH]); clutter_actor_set_min_width_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); g_object_thaw_notify (G_OBJECT (self)); clutter_actor_queue_relayout (self); } static void clutter_actor_set_min_height (ClutterActor *self, gfloat min_height) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; ClutterLayoutInfo *info; /* if we are setting the size on a top-level actor and the * backend only supports static top-levels (e.g. framebuffers) * then we ignore the passed value and we override it with * the stage implementation's preferred size. */ if (CLUTTER_ACTOR_IS_TOPLEVEL (self) && clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) return; info = _clutter_actor_get_layout_info (self); if (priv->min_height_set && min_height == info->minimum.height) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_store_old_geometry (self, &old); info->minimum.height = min_height; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT]); clutter_actor_set_min_height_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); g_object_thaw_notify (G_OBJECT (self)); clutter_actor_queue_relayout (self); } static void clutter_actor_set_natural_width (ClutterActor *self, gfloat natural_width) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; ClutterLayoutInfo *info; /* if we are setting the size on a top-level actor and the * backend only supports static top-levels (e.g. framebuffers) * then we ignore the passed value and we override it with * the stage implementation's preferred size. */ if (CLUTTER_ACTOR_IS_TOPLEVEL (self) && clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) return; info = _clutter_actor_get_layout_info (self); if (priv->natural_width_set && natural_width == info->natural.width) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_store_old_geometry (self, &old); info->natural.width = natural_width; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH]); clutter_actor_set_natural_width_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); g_object_thaw_notify (G_OBJECT (self)); clutter_actor_queue_relayout (self); } static void clutter_actor_set_natural_height (ClutterActor *self, gfloat natural_height) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; ClutterLayoutInfo *info; /* if we are setting the size on a top-level actor and the * backend only supports static top-levels (e.g. framebuffers) * then we ignore the passed value and we override it with * the stage implementation's preferred size. */ if (CLUTTER_ACTOR_IS_TOPLEVEL (self) && clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) return; info = _clutter_actor_get_layout_info (self); if (priv->natural_height_set && natural_height == info->natural.height) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_store_old_geometry (self, &old); info->natural.height = natural_height; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT]); clutter_actor_set_natural_height_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); g_object_thaw_notify (G_OBJECT (self)); clutter_actor_queue_relayout (self); } static void clutter_actor_set_min_width_set (ClutterActor *self, gboolean use_min_width) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; if (priv->min_width_set == (use_min_width != FALSE)) return; clutter_actor_store_old_geometry (self, &old); priv->min_width_set = use_min_width != FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_WIDTH_SET]); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } static void clutter_actor_set_min_height_set (ClutterActor *self, gboolean use_min_height) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; if (priv->min_height_set == (use_min_height != FALSE)) return; clutter_actor_store_old_geometry (self, &old); priv->min_height_set = use_min_height != FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MIN_HEIGHT_SET]); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } static void clutter_actor_set_natural_width_set (ClutterActor *self, gboolean use_natural_width) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; if (priv->natural_width_set == (use_natural_width != FALSE)) return; clutter_actor_store_old_geometry (self, &old); priv->natural_width_set = use_natural_width != FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_WIDTH_SET]); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } static void clutter_actor_set_natural_height_set (ClutterActor *self, gboolean use_natural_height) { ClutterActorPrivate *priv = self->priv; ClutterActorBox old = { 0, }; if (priv->natural_height_set == (use_natural_height != FALSE)) return; clutter_actor_store_old_geometry (self, &old); priv->natural_height_set = use_natural_height != FALSE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NATURAL_HEIGHT_SET]); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } /** * clutter_actor_set_request_mode: * @self: a #ClutterActor * @mode: the request mode * * Sets the geometry request mode of @self. * * The @mode determines the order for invoking * clutter_actor_get_preferred_width() and * clutter_actor_get_preferred_height() * * Since: 1.2 */ void clutter_actor_set_request_mode (ClutterActor *self, ClutterRequestMode mode) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (priv->request_mode == mode) return; priv->request_mode = mode; priv->needs_width_request = TRUE; priv->needs_height_request = TRUE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_REQUEST_MODE]); clutter_actor_queue_relayout (self); } /** * clutter_actor_get_request_mode: * @self: a #ClutterActor * * Retrieves the geometry request mode of @self * * Return value: the request mode for the actor * * Since: 1.2 */ ClutterRequestMode clutter_actor_get_request_mode (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REQUEST_HEIGHT_FOR_WIDTH); return self->priv->request_mode; } /* variant of set_width() without checks and without notification * freeze+thaw, for internal usage only */ static inline void clutter_actor_set_width_internal (ClutterActor *self, gfloat width) { if (width >= 0) { /* the Stage will use the :min-width to control the minimum * width to be resized to, so we should not be setting it * along with the :natural-width */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) clutter_actor_set_min_width (self, width); clutter_actor_set_natural_width (self, width); } else { /* we only unset the :natural-width for the Stage */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) clutter_actor_set_min_width_set (self, FALSE); clutter_actor_set_natural_width_set (self, FALSE); } } /* variant of set_height() without checks and without notification * freeze+thaw, for internal usage only */ static inline void clutter_actor_set_height_internal (ClutterActor *self, gfloat height) { if (height >= 0) { /* see the comment above in set_width_internal() */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) clutter_actor_set_min_height (self, height); clutter_actor_set_natural_height (self, height); } else { /* see the comment above in set_width_internal() */ if (!CLUTTER_ACTOR_IS_TOPLEVEL (self)) clutter_actor_set_min_height_set (self, FALSE); clutter_actor_set_natural_height_set (self, FALSE); } } static void clutter_actor_set_size_internal (ClutterActor *self, const graphene_size_t *size) { if (size != NULL) { clutter_actor_set_width_internal (self, size->width); clutter_actor_set_height_internal (self, size->height); } else { clutter_actor_set_width_internal (self, -1); clutter_actor_set_height_internal (self, -1); } } /** * clutter_actor_set_size: * @self: A #ClutterActor * @width: New width of actor in pixels, or -1 * @height: New height of actor in pixels, or -1 * * Sets the actor's size request in pixels. This overrides any * "normal" size request the actor would have. For example * a text actor might normally request the size of the text; * this function would force a specific size instead. * * If @width and/or @height are -1 the actor will use its * "normal" size request instead of overriding it, i.e. * you can "unset" the size with -1. * * This function sets or unsets both the minimum and natural size. */ void clutter_actor_set_size (ClutterActor *self, gfloat width, gfloat height) { graphene_size_t new_size; g_return_if_fail (CLUTTER_IS_ACTOR (self)); graphene_size_init (&new_size, width, height); /* minor optimization: if we don't have a duration then we can * skip the get_size() below, to avoid the chance of going through * get_preferred_width() and get_preferred_height() just to jump to * a new desired size */ if (clutter_actor_get_easing_duration (self) == 0) { g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_size_internal (self, &new_size); g_object_thaw_notify (G_OBJECT (self)); return; } else { graphene_size_t cur_size; graphene_size_init (&cur_size, clutter_actor_get_width (self), clutter_actor_get_height (self)); _clutter_actor_create_transition (self, obj_props[PROP_SIZE], &cur_size, &new_size); } } /** * clutter_actor_get_size: * @self: A #ClutterActor * @width: (out) (allow-none): return location for the width, or %NULL. * @height: (out) (allow-none): return location for the height, or %NULL. * * This function tries to "do what you mean" and return * the size an actor will have. If the actor has a valid * allocation, the allocation will be returned; otherwise, * the actors natural size request will be returned. * * If you care whether you get the request vs. the allocation, you * should probably call a different function like * clutter_actor_get_allocation_box() or * clutter_actor_get_preferred_width(). * * Since: 0.2 */ void clutter_actor_get_size (ClutterActor *self, gfloat *width, gfloat *height) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (width) *width = clutter_actor_get_width (self); if (height) *height = clutter_actor_get_height (self); } /** * clutter_actor_get_position: * @self: a #ClutterActor * @x: (out) (allow-none): return location for the X coordinate, or %NULL * @y: (out) (allow-none): return location for the Y coordinate, or %NULL * * This function tries to "do what you mean" and tell you where the * actor is, prior to any transformations. Retrieves the fixed * position of an actor in pixels, if one has been set; otherwise, if * the allocation is valid, returns the actor's allocated position; * otherwise, returns 0,0. * * The returned position is in pixels. * * Since: 0.6 */ void clutter_actor_get_position (ClutterActor *self, gfloat *x, gfloat *y) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (x) *x = clutter_actor_get_x (self); if (y) *y = clutter_actor_get_y (self); } /** * clutter_actor_get_transformed_position: * @self: A #ClutterActor * @x: (out) (allow-none): return location for the X coordinate, or %NULL * @y: (out) (allow-none): return location for the Y coordinate, or %NULL * * Gets the absolute position of an actor, in pixels relative to the stage. * * Since: 0.8 */ void clutter_actor_get_transformed_position (ClutterActor *self, gfloat *x, gfloat *y) { graphene_point3d_t v1; graphene_point3d_t v2; v1.x = v1.y = v1.z = 0; clutter_actor_apply_transform_to_point (self, &v1, &v2); if (x) *x = v2.x; if (y) *y = v2.y; } /** * clutter_actor_get_transformed_size: * @self: A #ClutterActor * @width: (out) (allow-none): return location for the width, or %NULL * @height: (out) (allow-none): return location for the height, or %NULL * * Gets the absolute size of an actor in pixels, taking into account the * scaling factors. * * If the actor has a valid allocation, the allocated size will be used. * If the actor has not a valid allocation then the preferred size will * be transformed and returned. * * If you want the transformed allocation, see * clutter_actor_get_abs_allocation_vertices() instead. * * When the actor (or one of its ancestors) is rotated around the * X or Y axis, it no longer appears as on the stage as a rectangle, but * as a generic quadrangle; in that case this function returns the size * of the smallest rectangle that encapsulates the entire quad. Please * note that in this case no assumptions can be made about the relative * position of this envelope to the absolute position of the actor, as * returned by clutter_actor_get_transformed_position(); if you need this * information, you need to use clutter_actor_get_abs_allocation_vertices() * to get the coords of the actual quadrangle. * * Since: 0.8 */ void clutter_actor_get_transformed_size (ClutterActor *self, gfloat *width, gfloat *height) { ClutterActorPrivate *priv; graphene_point3d_t v[4]; gfloat x_min, x_max, y_min, y_max; gint i; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; /* if the actor hasn't been allocated yet, get the preferred * size and transform that */ if (priv->needs_allocation) { gfloat natural_width, natural_height; ClutterActorBox box; /* Make a fake allocation to transform. * * NB: _clutter_actor_transform_and_project_box expects a box in * the actor's coordinate space... */ box.x1 = 0; box.y1 = 0; natural_width = natural_height = 0; clutter_actor_get_preferred_size (self, NULL, NULL, &natural_width, &natural_height); box.x2 = natural_width; box.y2 = natural_height; _clutter_actor_transform_and_project_box (self, &box, v); } else clutter_actor_get_abs_allocation_vertices (self, v); x_min = x_max = v[0].x; y_min = y_max = v[0].y; for (i = 1; i < G_N_ELEMENTS (v); ++i) { if (v[i].x < x_min) x_min = v[i].x; if (v[i].x > x_max) x_max = v[i].x; if (v[i].y < y_min) y_min = v[i].y; if (v[i].y > y_max) y_max = v[i].y; } if (width) *width = x_max - x_min; if (height) *height = y_max - y_min; } /** * clutter_actor_get_width: * @self: A #ClutterActor * * Retrieves the width of a #ClutterActor. * * If the actor has a valid allocation, this function will return the * width of the allocated area given to the actor. * * If the actor does not have a valid allocation, this function will * return the actor's natural width, that is the preferred width of * the actor. * * If you care whether you get the preferred width or the width that * has been assigned to the actor, you should probably call a different * function like clutter_actor_get_allocation_box() to retrieve the * allocated size or clutter_actor_get_preferred_width() to retrieve the * preferred width. * * If an actor has a fixed width, for instance a width that has been * assigned using clutter_actor_set_width(), the width returned will * be the same value. * * Return value: the width of the actor, in pixels */ gfloat clutter_actor_get_width (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); priv = self->priv; if (priv->needs_allocation) { gfloat natural_width = 0; if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { clutter_actor_get_preferred_width (self, -1, NULL, &natural_width); } else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { gfloat natural_height = 0; clutter_actor_get_preferred_height (self, -1, NULL, &natural_height); clutter_actor_get_preferred_width (self, natural_height, NULL, &natural_width); } else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL) { clutter_content_get_preferred_size (priv->content, &natural_width, NULL); } return natural_width; } else return priv->allocation.x2 - priv->allocation.x1; } /** * clutter_actor_get_height: * @self: A #ClutterActor * * Retrieves the height of a #ClutterActor. * * If the actor has a valid allocation, this function will return the * height of the allocated area given to the actor. * * If the actor does not have a valid allocation, this function will * return the actor's natural height, that is the preferred height of * the actor. * * If you care whether you get the preferred height or the height that * has been assigned to the actor, you should probably call a different * function like clutter_actor_get_allocation_box() to retrieve the * allocated size or clutter_actor_get_preferred_height() to retrieve the * preferred height. * * If an actor has a fixed height, for instance a height that has been * assigned using clutter_actor_set_height(), the height returned will * be the same value. * * Return value: the height of the actor, in pixels */ gfloat clutter_actor_get_height (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); priv = self->priv; if (priv->needs_allocation) { gfloat natural_height = 0; if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { gfloat natural_width = 0; clutter_actor_get_preferred_width (self, -1, NULL, &natural_width); clutter_actor_get_preferred_height (self, natural_width, NULL, &natural_height); } else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { clutter_actor_get_preferred_height (self, -1, NULL, &natural_height); } else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL) { clutter_content_get_preferred_size (priv->content, NULL, &natural_height); } return natural_height; } else return priv->allocation.y2 - priv->allocation.y1; } /** * clutter_actor_set_width: * @self: A #ClutterActor * @width: Requested new width for the actor, in pixels, or -1 * * Forces a width on an actor, causing the actor's preferred width * and height (if any) to be ignored. * * If @width is -1 the actor will use its preferred width request * instead of overriding it, i.e. you can "unset" the width with -1. * * This function sets both the minimum and natural size of the actor. * * since: 0.2 */ void clutter_actor_set_width (ClutterActor *self, gfloat width) { float cur_size; g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* minor optimization: if we don't have a duration * then we can skip the get_width() below, to avoid * the chance of going through get_preferred_width() * just to jump to a new desired width. */ if (clutter_actor_get_easing_duration (self) == 0) { g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_width_internal (self, width); g_object_thaw_notify (G_OBJECT (self)); return; } else cur_size = clutter_actor_get_width (self); _clutter_actor_create_transition (self, obj_props[PROP_WIDTH], cur_size, width); } /** * clutter_actor_set_height: * @self: A #ClutterActor * @height: Requested new height for the actor, in pixels, or -1 * * Forces a height on an actor, causing the actor's preferred width * and height (if any) to be ignored. * * If @height is -1 the actor will use its preferred height instead of * overriding it, i.e. you can "unset" the height with -1. * * This function sets both the minimum and natural size of the actor. * * since: 0.2 */ void clutter_actor_set_height (ClutterActor *self, gfloat height) { float cur_size; g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* see the comment in clutter_actor_set_width() above */ if (clutter_actor_get_easing_duration (self) == 0) { g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_height_internal (self, height); g_object_thaw_notify (G_OBJECT (self)); return; } else cur_size = clutter_actor_get_height (self); _clutter_actor_create_transition (self, obj_props[PROP_HEIGHT], cur_size, height); } static inline void clutter_actor_set_x_internal (ClutterActor *self, float x) { ClutterActorPrivate *priv = self->priv; ClutterLayoutInfo *linfo; ClutterActorBox old = { 0, }; linfo = _clutter_actor_get_layout_info (self); if (priv->position_set && linfo->fixed_pos.x == x) return; clutter_actor_store_old_geometry (self, &old); linfo->fixed_pos.x = x; clutter_actor_set_fixed_position_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } static inline void clutter_actor_set_y_internal (ClutterActor *self, float y) { ClutterActorPrivate *priv = self->priv; ClutterLayoutInfo *linfo; ClutterActorBox old = { 0, }; linfo = _clutter_actor_get_layout_info (self); if (priv->position_set && linfo->fixed_pos.y == y) return; clutter_actor_store_old_geometry (self, &old); linfo->fixed_pos.y = y; clutter_actor_set_fixed_position_set (self, TRUE); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } static void clutter_actor_set_position_internal (ClutterActor *self, const graphene_point_t *position) { ClutterActorPrivate *priv = self->priv; ClutterLayoutInfo *linfo; ClutterActorBox old = { 0, }; linfo = _clutter_actor_get_layout_info (self); if (priv->position_set && graphene_point_equal (position, &linfo->fixed_pos)) return; clutter_actor_store_old_geometry (self, &old); if (position != NULL) { linfo->fixed_pos = *position; clutter_actor_set_fixed_position_set (self, TRUE); } else clutter_actor_set_fixed_position_set (self, FALSE); clutter_actor_notify_if_geometry_changed (self, &old); clutter_actor_queue_relayout (self); } /** * clutter_actor_set_x: * @self: a #ClutterActor * @x: the actor's position on the X axis * * Sets the actor's X coordinate, relative to its parent, in pixels. * * Overrides any layout manager and forces a fixed position for * the actor. * * The #ClutterActor:x property is animatable. * * Since: 0.6 */ void clutter_actor_set_x (ClutterActor *self, gfloat x) { float cur_position = clutter_actor_get_x (self); g_return_if_fail (CLUTTER_IS_ACTOR (self)); _clutter_actor_create_transition (self, obj_props[PROP_X], cur_position, x); } /** * clutter_actor_set_y: * @self: a #ClutterActor * @y: the actor's position on the Y axis * * Sets the actor's Y coordinate, relative to its parent, in pixels.# * * Overrides any layout manager and forces a fixed position for * the actor. * * The #ClutterActor:y property is animatable. * * Since: 0.6 */ void clutter_actor_set_y (ClutterActor *self, gfloat y) { float cur_position = clutter_actor_get_y (self); g_return_if_fail (CLUTTER_IS_ACTOR (self)); _clutter_actor_create_transition (self, obj_props[PROP_Y], cur_position, y); } /** * clutter_actor_get_x: * @self: A #ClutterActor * * Retrieves the X coordinate of a #ClutterActor. * * This function tries to "do what you mean", by returning the * correct value depending on the actor's state. * * If the actor has a valid allocation, this function will return * the X coordinate of the origin of the allocation box. * * If the actor has any fixed coordinate set using clutter_actor_set_x(), * clutter_actor_set_position(), this function will return that coordinate. * * If both the allocation and a fixed position are missing, this function * will return 0. * * Return value: the X coordinate, in pixels, ignoring any * transformation (i.e. scaling, rotation) */ gfloat clutter_actor_get_x (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); priv = self->priv; if (priv->needs_allocation) { if (priv->position_set) { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (self); return info->fixed_pos.x; } else return 0; } else return priv->allocation.x1; } /** * clutter_actor_get_y: * @self: A #ClutterActor * * Retrieves the Y coordinate of a #ClutterActor. * * This function tries to "do what you mean", by returning the * correct value depending on the actor's state. * * If the actor has a valid allocation, this function will return * the Y coordinate of the origin of the allocation box. * * If the actor has any fixed coordinate set using clutter_actor_set_y(), * clutter_actor_set_position(), this function will return that coordinate. * * If both the allocation and a fixed position are missing, this function * will return 0. * * Return value: the Y coordinate, in pixels, ignoring any * transformation (i.e. scaling, rotation) */ gfloat clutter_actor_get_y (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); priv = self->priv; if (priv->needs_allocation) { if (priv->position_set) { const ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info_or_defaults (self); return info->fixed_pos.y; } else return 0; } else return priv->allocation.y1; } /** * clutter_actor_set_scale: * @self: A #ClutterActor * @scale_x: double factor to scale actor by horizontally. * @scale_y: double factor to scale actor by vertically. * * Scales an actor with the given factors. * * The scale transformation is relative the the #ClutterActor:pivot-point. * * The #ClutterActor:scale-x and #ClutterActor:scale-y properties are * animatable. * * Since: 0.2 */ void clutter_actor_set_scale (ClutterActor *self, gdouble scale_x, gdouble scale_y) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x); clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_set_scale_z: * @self: a #ClutterActor * @scale_z: the scaling factor along the Z axis * * Scales an actor on the Z axis by the given @scale_z factor. * * The scale transformation is relative the the #ClutterActor:pivot-point. * * The #ClutterActor:scale-z property is animatable. * * Since: 1.12 */ void clutter_actor_set_scale_z (ClutterActor *self, gdouble scale_z) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); clutter_actor_set_scale_factor (self, CLUTTER_Z_AXIS, scale_z); } /** * clutter_actor_set_scale_full: * @self: A #ClutterActor * @scale_x: double factor to scale actor by horizontally. * @scale_y: double factor to scale actor by vertically. * @center_x: X coordinate of the center of the scaling * @center_y: Y coordinate of the center of the scaling * * Scales an actor with the given factors around the given center * point. The center point is specified in pixels relative to the * anchor point (usually the top left corner of the actor). * * The #ClutterActor:scale-x and #ClutterActor:scale-y properties * are animatable. * * Since: 1.0 * * Deprecated: 1.12: Use clutter_actor_set_pivot_point() to control * the scale center */ void clutter_actor_set_scale_full (ClutterActor *self, gdouble scale_x, gdouble scale_y, gfloat center_x, gfloat center_y) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_scale_factor (self, CLUTTER_X_AXIS, scale_x); clutter_actor_set_scale_factor (self, CLUTTER_Y_AXIS, scale_y); clutter_actor_set_scale_center (self, CLUTTER_X_AXIS, center_x); clutter_actor_set_scale_center (self, CLUTTER_Y_AXIS, center_y); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_get_scale: * @self: A #ClutterActor * @scale_x: (out) (allow-none): Location to store horizonal * scale factor, or %NULL. * @scale_y: (out) (allow-none): Location to store vertical * scale factor, or %NULL. * * Retrieves an actors scale factors. * * Since: 0.2 */ void clutter_actor_get_scale (ClutterActor *self, gdouble *scale_x, gdouble *scale_y) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); if (scale_x) *scale_x = info->scale_x; if (scale_y) *scale_y = info->scale_y; } /** * clutter_actor_get_scale_z: * @self: A #ClutterActor * * Retrieves the scaling factor along the Z axis, as set using * clutter_actor_set_scale_z(). * * Return value: the scaling factor along the Z axis * * Since: 1.12 */ gdouble clutter_actor_get_scale_z (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 1.0); return _clutter_actor_get_transform_info_or_defaults (self)->scale_z; } /** * clutter_actor_get_scale_center: * @self: A #ClutterActor * @center_x: (out) (allow-none): Location to store the X position * of the scale center, or %NULL. * @center_y: (out) (allow-none): Location to store the Y position * of the scale center, or %NULL. * * Retrieves the scale center coordinate in pixels relative to the top * left corner of the actor. If the scale center was specified using a * #ClutterGravity this will calculate the pixel offset using the * current size of the actor. * * Since: 1.0 * * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead. */ void clutter_actor_get_scale_center (ClutterActor *self, gfloat *center_x, gfloat *center_y) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); clutter_anchor_coord_get_units (self, &info->scale_center, center_x, center_y, NULL); } /** * clutter_actor_get_scale_gravity: * @self: A #ClutterActor * * Retrieves the scale center as a compass direction. If the scale * center was specified in pixels or units this will return * %CLUTTER_GRAVITY_NONE. * * Return value: the scale gravity * * Since: 1.0 * * Deprecated: 1.12: Use clutter_actor_get_pivot_point() instead. */ ClutterGravity clutter_actor_get_scale_gravity (ClutterActor *self) { const ClutterTransformInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE); info = _clutter_actor_get_transform_info_or_defaults (self); return clutter_anchor_coord_get_gravity (&info->scale_center); } static inline void clutter_actor_set_opacity_internal (ClutterActor *self, guint8 opacity) { ClutterActorPrivate *priv = self->priv; if (priv->opacity != opacity) { priv->opacity = opacity; /* Queue a redraw from the flatten effect so that it can use its cached image if available instead of having to redraw the actual actor. If it doesn't end up using the FBO then the effect is still able to continue the paint anyway. If there is no flatten effect yet then this is equivalent to queueing a full redraw */ _clutter_actor_queue_redraw_full (self, 0, /* flags */ NULL, /* clip */ priv->flatten_effect); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OPACITY]); } } /** * clutter_actor_set_opacity: * @self: A #ClutterActor * @opacity: New opacity value for the actor. * * Sets the actor's opacity, with zero being completely transparent and * 255 (0xff) being fully opaque. * * The #ClutterActor:opacity property is animatable. */ void clutter_actor_set_opacity (ClutterActor *self, guint8 opacity) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); _clutter_actor_create_transition (self, obj_props[PROP_OPACITY], self->priv->opacity, opacity); } /* * clutter_actor_get_paint_opacity_internal: * @self: a #ClutterActor * * Retrieves the absolute opacity of the actor, as it appears on the stage * * This function does not do type checks * * Return value: the absolute opacity of the actor */ static guint8 clutter_actor_get_paint_opacity_internal (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; ClutterActor *parent; /* override the top-level opacity to always be 255; even in * case of ClutterStage:use-alpha being TRUE we want the rest * of the scene to be painted */ if (CLUTTER_ACTOR_IS_TOPLEVEL (self)) return 255; if (priv->opacity_override >= 0) return priv->opacity_override; parent = priv->parent; /* Factor in the actual actors opacity with parents */ if (parent != NULL) { guint8 opacity = clutter_actor_get_paint_opacity_internal (parent); if (opacity != 0xff) return (opacity * priv->opacity) / 0xff; } return priv->opacity; } /** * clutter_actor_get_paint_opacity: * @self: A #ClutterActor * * Retrieves the absolute opacity of the actor, as it appears on the stage. * * This function traverses the hierarchy chain and composites the opacity of * the actor with that of its parents. * * This function is intended for subclasses to use in the paint virtual * function, to paint themselves with the correct opacity. * * Return value: The actor opacity value. * * Since: 0.8 */ guint8 clutter_actor_get_paint_opacity (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); return clutter_actor_get_paint_opacity_internal (self); } /** * clutter_actor_get_opacity: * @self: a #ClutterActor * * Retrieves the opacity value of an actor, as set by * clutter_actor_set_opacity(). * * For retrieving the absolute opacity of the actor inside a paint * virtual function, see clutter_actor_get_paint_opacity(). * * Return value: the opacity of the actor */ guint8 clutter_actor_get_opacity (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); return self->priv->opacity; } /** * clutter_actor_set_offscreen_redirect: * @self: A #ClutterActor * @redirect: New offscreen redirect flags for the actor. * * Defines the circumstances where the actor should be redirected into * an offscreen image. The offscreen image is used to flatten the * actor into a single image while painting for two main reasons. * Firstly, when the actor is painted a second time without any of its * contents changing it can simply repaint the cached image without * descending further down the actor hierarchy. Secondly, it will make * the opacity look correct even if there are overlapping primitives * in the actor. * * Caching the actor could in some cases be a performance win and in * some cases be a performance lose so it is important to determine * which value is right for an actor before modifying this value. For * example, there is never any reason to flatten an actor that is just * a single texture (such as a #ClutterTexture) because it is * effectively already cached in an image so the offscreen would be * redundant. Also if the actor contains primitives that are far apart * with a large transparent area in the middle (such as a large * CluterGroup with a small actor in the top left and a small actor in * the bottom right) then the cached image will contain the entire * image of the large area and the paint will waste time blending all * of the transparent pixels in the middle. * * The default method of implementing opacity on a container simply * forwards on the opacity to all of the children. If the children are * overlapping then it will appear as if they are two separate glassy * objects and there will be a break in the color where they * overlap. By redirecting to an offscreen buffer it will be as if the * two opaque objects are combined into one and then made transparent * which is usually what is expected. * * The image below demonstrates the difference between redirecting and * not. The image shows two Clutter groups, each containing a red and * a green rectangle which overlap. The opacity on the group is set to * 128 (which is 50%). When the offscreen redirect is not used, the * red rectangle can be seen through the blue rectangle as if the two * rectangles were separately transparent. When the redirect is used * the group as a whole is transparent instead so the red rectangle is * not visible where they overlap. * *
* Sample of using an offscreen redirect for transparency * *
* * The default value for this property is 0, so we effectively will * never redirect an actor offscreen by default. This means that there * are times that transparent actors may look glassy as described * above. The reason this is the default is because there is a * performance trade off between quality and performance here. In many * cases the default form of glassy opacity looks good enough, but if * it's not you will need to set the * %CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY flag to enable * redirection for opacity. * * Custom actors that don't contain any overlapping primitives are * recommended to override the has_overlaps() virtual to return %FALSE * for maximum efficiency. * * Since: 1.8 */ void clutter_actor_set_offscreen_redirect (ClutterActor *self, ClutterOffscreenRedirect redirect) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (priv->offscreen_redirect != redirect) { priv->offscreen_redirect = redirect; /* Queue a redraw from the effect so that it can use its cached image if available instead of having to redraw the actual actor. If it doesn't end up using the FBO then the effect is still able to continue the paint anyway. If there is no effect then this is equivalent to queuing a full redraw */ _clutter_actor_queue_redraw_full (self, 0, /* flags */ NULL, /* clip */ priv->flatten_effect); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_OFFSCREEN_REDIRECT]); } } /** * clutter_actor_get_offscreen_redirect: * @self: a #ClutterActor * * Retrieves whether to redirect the actor to an offscreen buffer, as * set by clutter_actor_set_offscreen_redirect(). * * Return value: the value of the offscreen-redirect property of the actor * * Since: 1.8 */ ClutterOffscreenRedirect clutter_actor_get_offscreen_redirect (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); return self->priv->offscreen_redirect; } /** * clutter_actor_set_name: * @self: A #ClutterActor * @name: Textual tag to apply to actor * * Sets the given name to @self. The name can be used to identify * a #ClutterActor. */ void clutter_actor_set_name (ClutterActor *self, const gchar *name) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_free (self->priv->name); self->priv->name = g_strdup (name); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_NAME]); } /** * clutter_actor_get_name: * @self: A #ClutterActor * * Retrieves the name of @self. * * Return value: the name of the actor, or %NULL. The returned string is * owned by the actor and should not be modified or freed. */ const gchar * clutter_actor_get_name (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->name; } static inline void clutter_actor_set_depth_internal (ClutterActor *self, float depth) { ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (info->z_position != depth) { /* Sets Z value - XXX 2.0: should we invert? */ info->z_position = depth; self->priv->transform_valid = FALSE; /* FIXME - remove this crap; sadly, there are still containers * in Clutter that depend on this utter brain damage */ clutter_container_sort_depth_order (CLUTTER_CONTAINER (self)); clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DEPTH]); } } static inline void clutter_actor_set_z_position_internal (ClutterActor *self, float z_position) { ClutterTransformInfo *info; info = _clutter_actor_get_transform_info (self); if (memcmp (&info->z_position, &z_position, sizeof (float)) != 0) { info->z_position = z_position; self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Z_POSITION]); } } /** * clutter_actor_set_z_position: * @self: a #ClutterActor * @z_position: the position on the Z axis * * Sets the actor's position on the Z axis. * * See #ClutterActor:z-position. * * Since: 1.12 */ void clutter_actor_set_z_position (ClutterActor *self, gfloat z_position) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); _clutter_actor_create_transition (self, obj_props[PROP_Z_POSITION], info->z_position, z_position); } /** * clutter_actor_get_z_position: * @self: a #ClutterActor * * Retrieves the actor's position on the Z axis. * * Return value: the position on the Z axis. * * Since: 1.12 */ gfloat clutter_actor_get_z_position (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_transform_info_or_defaults (self)->z_position; } /** * clutter_actor_set_pivot_point: * @self: a #ClutterActor * @pivot_x: the normalized X coordinate of the pivot point * @pivot_y: the normalized Y coordinate of the pivot point * * Sets the position of the #ClutterActor:pivot-point around which the * scaling and rotation transformations occur. * * The pivot point's coordinates are in normalized space, with the (0, 0) * point being the top left corner of the actor, and the (1, 1) point being * the bottom right corner. * * Since: 1.12 */ void clutter_actor_set_pivot_point (ClutterActor *self, gfloat pivot_x, gfloat pivot_y) { graphene_point_t pivot = GRAPHENE_POINT_INIT (pivot_x, pivot_y); const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT], &info->pivot, &pivot); } /** * clutter_actor_get_pivot_point: * @self: a #ClutterActor * @pivot_x: (out) (allow-none): return location for the normalized X * coordinate of the pivot point, or %NULL * @pivot_y: (out) (allow-none): return location for the normalized Y * coordinate of the pivot point, or %NULL * * Retrieves the coordinates of the #ClutterActor:pivot-point. * * Since: 1.12 */ void clutter_actor_get_pivot_point (ClutterActor *self, gfloat *pivot_x, gfloat *pivot_y) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); if (pivot_x != NULL) *pivot_x = info->pivot.x; if (pivot_y != NULL) *pivot_y = info->pivot.y; } /** * clutter_actor_set_pivot_point_z: * @self: a #ClutterActor * @pivot_z: the Z coordinate of the actor's pivot point * * Sets the component on the Z axis of the #ClutterActor:pivot-point around * which the scaling and rotation transformations occur. * * The @pivot_z value is expressed as a distance along the Z axis. * * Since: 1.12 */ void clutter_actor_set_pivot_point_z (ClutterActor *self, gfloat pivot_z) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); _clutter_actor_create_transition (self, obj_props[PROP_PIVOT_POINT_Z], info->pivot_z, pivot_z); } /** * clutter_actor_get_pivot_point_z: * @self: a #ClutterActor * * Retrieves the Z component of the #ClutterActor:pivot-point. * * Since: 1.12 */ gfloat clutter_actor_get_pivot_point_z (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_transform_info_or_defaults (self)->pivot_z; } /** * clutter_actor_set_depth: * @self: a #ClutterActor * @depth: Z co-ord * * Sets the Z coordinate of @self to @depth. * * The unit used by @depth is dependant on the perspective setup. See * also clutter_stage_set_perspective(). * * Deprecated: 1.12: Use clutter_actor_set_z_position() instead. */ void clutter_actor_set_depth (ClutterActor *self, gfloat depth) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); _clutter_actor_create_transition (self, obj_props[PROP_DEPTH], info->z_position, depth); } /** * clutter_actor_get_depth: * @self: a #ClutterActor * * Retrieves the depth of @self. * * Return value: the depth of the actor * * Deprecated: 1.12: Use clutter_actor_get_z_position() instead. */ gfloat clutter_actor_get_depth (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.0); return _clutter_actor_get_transform_info_or_defaults (self)->z_position; } /** * clutter_actor_set_rotation: * @self: a #ClutterActor * @axis: the axis of rotation * @angle: the angle of rotation * @x: X coordinate of the rotation center * @y: Y coordinate of the rotation center * @z: Z coordinate of the rotation center * * Sets the rotation angle of @self around the given axis. * * The rotation center coordinates used depend on the value of @axis: * * - %CLUTTER_X_AXIS requires @y and @z * - %CLUTTER_Y_AXIS requires @x and @z * - %CLUTTER_Z_AXIS requires @x and @y * * The rotation coordinates are relative to the anchor point of the * actor, set using clutter_actor_set_anchor_point(). If no anchor * point is set, the upper left corner is assumed as the origin. * * Since: 0.8 * * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and * clutter_actor_set_pivot_point() instead. */ void clutter_actor_set_rotation (ClutterActor *self, ClutterRotateAxis axis, gdouble angle, gfloat x, gfloat y, gfloat z) { graphene_point3d_t v; g_return_if_fail (CLUTTER_IS_ACTOR (self)); v.x = x; v.y = y; v.z = z; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_rotation_angle (self, axis, angle); clutter_actor_set_rotation_center_internal (self, axis, &v); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_set_z_rotation_from_gravity: * @self: a #ClutterActor * @angle: the angle of rotation * @gravity: the center point of the rotation * * Sets the rotation angle of @self around the Z axis using the center * point specified as a compass point. For example to rotate such that * the center of the actor remains static you can use * %CLUTTER_GRAVITY_CENTER. If the actor changes size the center point * will move accordingly. * * Since: 1.0 * * Deprecated: 1.12: Use clutter_actor_set_rotation_angle() and * clutter_actor_set_pivot_point() instead. */ void clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, gdouble angle, ClutterGravity gravity) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (gravity == CLUTTER_GRAVITY_NONE) clutter_actor_set_rotation (self, CLUTTER_Z_AXIS, angle, 0, 0, 0); else { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; GParamSpec *pspec; pspec = obj_props[PROP_ROTATION_ANGLE_Z]; info = _clutter_actor_get_transform_info (self); g_object_freeze_notify (obj); clutter_actor_set_rotation_angle_internal (self, angle, pspec); clutter_anchor_coord_set_gravity (&info->rz_center, gravity); g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z_GRAVITY]); g_object_notify_by_pspec (obj, obj_props[PROP_ROTATION_CENTER_Z]); g_object_thaw_notify (obj); } } /** * clutter_actor_get_rotation: * @self: a #ClutterActor * @axis: the axis of rotation * @x: (out): return value for the X coordinate of the center of rotation * @y: (out): return value for the Y coordinate of the center of rotation * @z: (out): return value for the Z coordinate of the center of rotation * * Retrieves the angle and center of rotation on the given axis, * set using clutter_actor_set_rotation(). * * Return value: the angle of rotation * * Since: 0.8 * * Deprecated: 1.12: Use clutter_actor_get_rotation_angle() and * clutter_actor_get_pivot_point() instead. */ gdouble clutter_actor_get_rotation (ClutterActor *self, ClutterRotateAxis axis, gfloat *x, gfloat *y, gfloat *z) { const ClutterTransformInfo *info; const AnchorCoord *anchor_coord; gdouble retval = 0; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); info = _clutter_actor_get_transform_info_or_defaults (self); switch (axis) { case CLUTTER_X_AXIS: anchor_coord = &info->rx_center; retval = info->rx_angle; break; case CLUTTER_Y_AXIS: anchor_coord = &info->ry_center; retval = info->ry_angle; break; case CLUTTER_Z_AXIS: anchor_coord = &info->rz_center; retval = info->rz_angle; break; default: anchor_coord = NULL; retval = 0.0; break; } clutter_anchor_coord_get_units (self, anchor_coord, x, y, z); return retval; } /** * clutter_actor_get_z_rotation_gravity: * @self: A #ClutterActor * * Retrieves the center for the rotation around the Z axis as a * compass direction. If the center was specified in pixels or units * this will return %CLUTTER_GRAVITY_NONE. * * Return value: the Z rotation center * * Since: 1.0 * * Deprecated: 1.12: Use the #ClutterActor:pivot-point instead of * a #ClutterGravity */ ClutterGravity clutter_actor_get_z_rotation_gravity (ClutterActor *self) { const ClutterTransformInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE); info = _clutter_actor_get_transform_info_or_defaults (self); return clutter_anchor_coord_get_gravity (&info->rz_center); } /** * clutter_actor_set_clip: * @self: A #ClutterActor * @xoff: X offset of the clip rectangle * @yoff: Y offset of the clip rectangle * @width: Width of the clip rectangle * @height: Height of the clip rectangle * * Sets clip area for @self. The clip area is always computed from the * upper left corner of the actor, even if the anchor point is set * otherwise. * * Since: 0.6 */ void clutter_actor_set_clip (ClutterActor *self, gfloat xoff, gfloat yoff, gfloat width, gfloat height) { ClutterActorPrivate *priv; GObject *obj; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (priv->has_clip && priv->clip.origin.x == xoff && priv->clip.origin.y == yoff && priv->clip.size.width == width && priv->clip.size.height == height) return; obj = G_OBJECT (self); priv->clip.origin.x = xoff; priv->clip.origin.y = yoff; priv->clip.size.width = width; priv->clip.size.height = height; priv->has_clip = TRUE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, obj_props[PROP_CLIP_RECT]); g_object_notify_by_pspec (obj, obj_props[PROP_HAS_CLIP]); } /** * clutter_actor_remove_clip: * @self: A #ClutterActor * * Removes clip area from @self. */ void clutter_actor_remove_clip (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (!self->priv->has_clip) return; self->priv->has_clip = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]); } /** * clutter_actor_has_clip: * @self: a #ClutterActor * * Determines whether the actor has a clip area set or not. * * Return value: %TRUE if the actor has a clip area set. * * Since: 0.2 */ gboolean clutter_actor_has_clip (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return self->priv->has_clip; } /** * clutter_actor_get_clip: * @self: a #ClutterActor * @xoff: (out) (allow-none): return location for the X offset of * the clip rectangle, or %NULL * @yoff: (out) (allow-none): return location for the Y offset of * the clip rectangle, or %NULL * @width: (out) (allow-none): return location for the width of * the clip rectangle, or %NULL * @height: (out) (allow-none): return location for the height of * the clip rectangle, or %NULL * * Gets the clip area for @self, if any is set. * * Since: 0.6 */ void clutter_actor_get_clip (ClutterActor *self, gfloat *xoff, gfloat *yoff, gfloat *width, gfloat *height) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (!priv->has_clip) return; if (xoff != NULL) *xoff = priv->clip.origin.x; if (yoff != NULL) *yoff = priv->clip.origin.y; if (width != NULL) *width = priv->clip.size.width; if (height != NULL) *height = priv->clip.size.height; } /** * clutter_actor_get_children: * @self: a #ClutterActor * * Retrieves the list of children of @self. * * Return value: (transfer container) (element-type ClutterActor): A newly * allocated #GList of #ClutterActors. Use g_list_free() when * done. * * Since: 1.10 */ GList * clutter_actor_get_children (ClutterActor *self) { ClutterActor *iter; GList *res; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); /* we walk the list backward so that we can use prepend(), * which is O(1) */ for (iter = self->priv->last_child, res = NULL; iter != NULL; iter = iter->priv->prev_sibling) { res = g_list_prepend (res, iter); } return res; } /*< private > * insert_child_at_depth: * @self: a #ClutterActor * @child: a #ClutterActor * * Inserts @child inside the list of children held by @self, using * the depth as the insertion criteria. * * This sadly makes the insertion not O(1), but we can keep the * list sorted so that the painters algorithm we use for painting * the children will work correctly. */ static void insert_child_at_depth (ClutterActor *self, ClutterActor *child, gpointer dummy G_GNUC_UNUSED) { ClutterActor *iter; float child_depth; child->priv->parent = self; child_depth = _clutter_actor_get_transform_info_or_defaults (child)->z_position; /* special-case the first child */ if (self->priv->n_children == 0) { self->priv->first_child = child; self->priv->last_child = child; child->priv->next_sibling = NULL; child->priv->prev_sibling = NULL; return; } /* Find the right place to insert the child so that it will still be sorted and the child will be after all of the actors at the same dept */ for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { float iter_depth; iter_depth = _clutter_actor_get_transform_info_or_defaults (iter)->z_position; if (iter_depth > child_depth) break; } if (iter != NULL) { ClutterActor *tmp = iter->priv->prev_sibling; if (tmp != NULL) tmp->priv->next_sibling = child; /* Insert the node before the found one */ child->priv->prev_sibling = iter->priv->prev_sibling; child->priv->next_sibling = iter; iter->priv->prev_sibling = child; } else { ClutterActor *tmp = self->priv->last_child; if (tmp != NULL) tmp->priv->next_sibling = child; /* insert the node at the end of the list */ child->priv->prev_sibling = self->priv->last_child; child->priv->next_sibling = NULL; } if (child->priv->prev_sibling == NULL) self->priv->first_child = child; if (child->priv->next_sibling == NULL) self->priv->last_child = child; } static void insert_child_at_index (ClutterActor *self, ClutterActor *child, gpointer data_) { gint index_ = GPOINTER_TO_INT (data_); child->priv->parent = self; if (index_ == 0) { ClutterActor *tmp = self->priv->first_child; if (tmp != NULL) tmp->priv->prev_sibling = child; child->priv->prev_sibling = NULL; child->priv->next_sibling = tmp; } else if (index_ < 0 || index_ >= self->priv->n_children) { ClutterActor *tmp = self->priv->last_child; if (tmp != NULL) tmp->priv->next_sibling = child; child->priv->prev_sibling = tmp; child->priv->next_sibling = NULL; } else { ClutterActor *iter; int i; for (iter = self->priv->first_child, i = 0; iter != NULL; iter = iter->priv->next_sibling, i += 1) { if (index_ == i) { ClutterActor *tmp = iter->priv->prev_sibling; child->priv->prev_sibling = tmp; child->priv->next_sibling = iter; iter->priv->prev_sibling = child; if (tmp != NULL) tmp->priv->next_sibling = child; break; } } } if (child->priv->prev_sibling == NULL) self->priv->first_child = child; if (child->priv->next_sibling == NULL) self->priv->last_child = child; } static void insert_child_above (ClutterActor *self, ClutterActor *child, gpointer data) { ClutterActor *sibling = data; child->priv->parent = self; if (sibling == NULL) sibling = self->priv->last_child; child->priv->prev_sibling = sibling; if (sibling != NULL) { ClutterActor *tmp = sibling->priv->next_sibling; child->priv->next_sibling = tmp; if (tmp != NULL) tmp->priv->prev_sibling = child; sibling->priv->next_sibling = child; } else child->priv->next_sibling = NULL; if (child->priv->prev_sibling == NULL) self->priv->first_child = child; if (child->priv->next_sibling == NULL) self->priv->last_child = child; } static void insert_child_below (ClutterActor *self, ClutterActor *child, gpointer data) { ClutterActor *sibling = data; child->priv->parent = self; if (sibling == NULL) sibling = self->priv->first_child; child->priv->next_sibling = sibling; if (sibling != NULL) { ClutterActor *tmp = sibling->priv->prev_sibling; child->priv->prev_sibling = tmp; if (tmp != NULL) tmp->priv->next_sibling = child; sibling->priv->prev_sibling = child; } else child->priv->prev_sibling = NULL; if (child->priv->prev_sibling == NULL) self->priv->first_child = child; if (child->priv->next_sibling == NULL) self->priv->last_child = child; } typedef void (* ClutterActorAddChildFunc) (ClutterActor *parent, ClutterActor *child, gpointer data); typedef enum { ADD_CHILD_CREATE_META = 1 << 0, ADD_CHILD_EMIT_PARENT_SET = 1 << 1, ADD_CHILD_EMIT_ACTOR_ADDED = 1 << 2, ADD_CHILD_CHECK_STATE = 1 << 3, ADD_CHILD_NOTIFY_FIRST_LAST = 1 << 4, ADD_CHILD_SHOW_ON_SET_PARENT = 1 << 5, /* default flags for public API */ ADD_CHILD_DEFAULT_FLAGS = ADD_CHILD_CREATE_META | ADD_CHILD_EMIT_PARENT_SET | ADD_CHILD_EMIT_ACTOR_ADDED | ADD_CHILD_CHECK_STATE | ADD_CHILD_NOTIFY_FIRST_LAST | ADD_CHILD_SHOW_ON_SET_PARENT, /* flags for legacy/deprecated API */ ADD_CHILD_LEGACY_FLAGS = ADD_CHILD_EMIT_PARENT_SET | ADD_CHILD_CHECK_STATE | ADD_CHILD_NOTIFY_FIRST_LAST | ADD_CHILD_SHOW_ON_SET_PARENT } ClutterActorAddChildFlags; /*< private > * clutter_actor_add_child_internal: * @self: a #ClutterActor * @child: a #ClutterActor * @flags: control flags for actions * @add_func (closure data): delegate function * @data: data to pass to @add_func * * Adds @child to the list of children of @self. * * The actual insertion inside the list is delegated to @add_func: this * function will just set up the state, perform basic checks, and emit * signals. * * The @flags argument is used to perform additional operations. */ static inline void clutter_actor_add_child_internal (ClutterActor *self, ClutterActor *child, ClutterActorAddChildFlags flags, ClutterActorAddChildFunc add_func, gpointer data) { ClutterTextDirection text_dir; gboolean create_meta; gboolean emit_parent_set, emit_actor_added; gboolean check_state; gboolean notify_first_last; gboolean show_on_set_parent; ClutterActor *old_first_child, *old_last_child; GObject *obj; if (self == child) { g_warning ("Cannot add the actor '%s' to itself.", _clutter_actor_get_debug_name (self)); return; } if (child->priv->parent != NULL) { g_warning ("The actor '%s' already has a parent, '%s'. You must " "use clutter_actor_remove_child() first.", _clutter_actor_get_debug_name (child), _clutter_actor_get_debug_name (child->priv->parent)); return; } if (CLUTTER_ACTOR_IS_TOPLEVEL (child)) { g_warning ("The actor '%s' is a top-level actor, and cannot be " "a child of another actor.", _clutter_actor_get_debug_name (child)); return; } /* the following check disallows calling methods that change the stacking * order within the destruction sequence, by triggering a critical * warning first, and leaving the actor in an undefined state, which * then ends up being caught by an assertion. * * the reproducible sequence is: * * - actor gets destroyed; * - another actor, linked to the first, will try to change the * stacking order of the first actor; * - changing the stacking order is a composite operation composed * by the following steps: * 1. ref() the child; * 2. remove_child_internal(), which removes the reference; * 3. add_child_internal(), which adds a reference; * - the state of the actor is not changed between (2) and (3), as * it could be an expensive recomputation; * - if (3) bails out, then the actor is in an undefined state, but * still alive; * - the destruction sequence terminates, but the actor is unparented * while its state indicates being parented instead. * - assertion failure. * * the obvious fix would be to decompose each set_child_*_sibling() * method into proper remove_child()/add_child(), with state validation; * this may cause excessive work, though, and trigger a cascade of other * bugs in code that assumes that a change in the stacking order is an * atomic operation. * * another potential fix is to just remove this check here, and let * code doing stacking order changes inside the destruction sequence * of an actor continue doing the stacking changes as before; this * option still performs more work than necessary. * * the third fix is to silently bail out early from every * set_child_*_sibling() and set_child_at_index() method, and avoid * doing stack changes altogether; Clutter implements this last option. * * see bug: https://bugzilla.gnome.org/show_bug.cgi?id=670647 */ if (CLUTTER_ACTOR_IN_DESTRUCTION (child)) { g_warning ("The actor '%s' is currently being destroyed, and " "cannot be added as a child of another actor.", _clutter_actor_get_debug_name (child)); return; } create_meta = (flags & ADD_CHILD_CREATE_META) != 0; emit_parent_set = (flags & ADD_CHILD_EMIT_PARENT_SET) != 0; emit_actor_added = (flags & ADD_CHILD_EMIT_ACTOR_ADDED) != 0; check_state = (flags & ADD_CHILD_CHECK_STATE) != 0; notify_first_last = (flags & ADD_CHILD_NOTIFY_FIRST_LAST) != 0; show_on_set_parent = (flags & ADD_CHILD_SHOW_ON_SET_PARENT) != 0; old_first_child = self->priv->first_child; old_last_child = self->priv->last_child; obj = G_OBJECT (self); g_object_freeze_notify (obj); if (create_meta) clutter_container_create_child_meta (CLUTTER_CONTAINER (self), child); g_object_ref_sink (child); child->priv->parent = NULL; child->priv->next_sibling = NULL; child->priv->prev_sibling = NULL; /* delegate the actual insertion */ add_func (self, child, data); g_assert (child->priv->parent == self); self->priv->n_children += 1; self->priv->age += 1; if (self->priv->in_cloned_branch) clutter_actor_push_in_cloned_branch (child, self->priv->in_cloned_branch); /* children may cause their parent to expand, if they are set * to expand; if a child is not expanded then it cannot change * its parent's state. any further change later on will queue * an expand state check. * * this check, with the initial state of the needs_compute_expand * flag set to FALSE, should avoid recomputing the expand flags * state while building the actor tree. */ if (CLUTTER_ACTOR_IS_VISIBLE (child) && (child->priv->needs_compute_expand || child->priv->needs_x_expand || child->priv->needs_y_expand)) { clutter_actor_queue_compute_expand (self); } if (emit_parent_set && !CLUTTER_ACTOR_IN_REPARENT (child)) { child->priv->needs_compute_resource_scale = TRUE; g_signal_emit (child, actor_signals[PARENT_SET], 0, NULL); } if (check_state) { /* If parent is mapped or realized, we need to also be mapped or * realized once we're inside the parent. */ clutter_actor_update_map_state (child, MAP_STATE_CHECK); /* propagate the parent's text direction to the child */ text_dir = clutter_actor_get_text_direction (self); clutter_actor_set_text_direction (child, text_dir); } /* this may end up queueing a redraw, in case the actor is * not visible but the show-on-set-parent property is still * set. * * XXX:2.0 - remove this check and unconditionally show() the * actor once we remove the show-on-set-parent property */ if (show_on_set_parent && child->priv->show_on_set_parent) clutter_actor_show (child); /* on the other hand, this will catch any other case where * the actor is supposed to be visible when it's added */ if (CLUTTER_ACTOR_IS_MAPPED (child)) clutter_actor_queue_redraw (child); /* maintain the invariant that if an actor needs layout, * its parents do as well */ if (clutter_actor_needs_relayout (child)) { /* we work around the short-circuiting we do * in clutter_actor_queue_relayout() since we * want to force a relayout */ child->priv->needs_width_request = TRUE; child->priv->needs_height_request = TRUE; child->priv->needs_allocation = TRUE; if (CLUTTER_ACTOR_IS_MAPPED (child)) child->priv->needs_paint_volume_update = TRUE; /* we only queue a relayout here, because any possible * redraw has already been queued either by show() or * by our call to queue_redraw() above */ _clutter_actor_queue_only_relayout (child->priv->parent); } if (emit_actor_added) _clutter_container_emit_actor_added (CLUTTER_CONTAINER (self), child); if (notify_first_last) { if (old_first_child != self->priv->first_child) g_object_notify_by_pspec (obj, obj_props[PROP_FIRST_CHILD]); if (old_last_child != self->priv->last_child) g_object_notify_by_pspec (obj, obj_props[PROP_LAST_CHILD]); } g_object_thaw_notify (obj); } /** * clutter_actor_add_child: * @self: a #ClutterActor * @child: a #ClutterActor * * Adds @child to the children of @self. * * This function will acquire a reference on @child that will only * be released when calling clutter_actor_remove_child(). * * This function will take into consideration the #ClutterActor:depth * of @child, and will keep the list of children sorted. * * This function will emit the #ClutterContainer::actor-added signal * on @self. * * Since: 1.10 */ void clutter_actor_add_child (ClutterActor *self, ClutterActor *child) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (self != child); g_return_if_fail (child->priv->parent == NULL); clutter_actor_add_child_internal (self, child, ADD_CHILD_DEFAULT_FLAGS, insert_child_at_depth, NULL); } /** * clutter_actor_insert_child_at_index: * @self: a #ClutterActor * @child: a #ClutterActor * @index_: the index * * Inserts @child into the list of children of @self, using the * given @index_. If @index_ is greater than the number of children * in @self, or is less than 0, then the new child is added at the end. * * This function will acquire a reference on @child that will only * be released when calling clutter_actor_remove_child(). * * This function will not take into consideration the #ClutterActor:depth * of @child. * * This function will emit the #ClutterContainer::actor-added signal * on @self. * * Since: 1.10 */ void clutter_actor_insert_child_at_index (ClutterActor *self, ClutterActor *child, gint index_) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (self != child); g_return_if_fail (child->priv->parent == NULL); clutter_actor_add_child_internal (self, child, ADD_CHILD_DEFAULT_FLAGS, insert_child_at_index, GINT_TO_POINTER (index_)); } /** * clutter_actor_insert_child_above: * @self: a #ClutterActor * @child: a #ClutterActor * @sibling: (allow-none): a child of @self, or %NULL * * Inserts @child into the list of children of @self, above another * child of @self or, if @sibling is %NULL, above all the children * of @self. * * This function will acquire a reference on @child that will only * be released when calling clutter_actor_remove_child(). * * This function will not take into consideration the #ClutterActor:depth * of @child. * * This function will emit the #ClutterContainer::actor-added signal * on @self. * * Since: 1.10 */ void clutter_actor_insert_child_above (ClutterActor *self, ClutterActor *child, ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (self != child); g_return_if_fail (child != sibling); g_return_if_fail (child->priv->parent == NULL); g_return_if_fail (sibling == NULL || (CLUTTER_IS_ACTOR (sibling) && sibling->priv->parent == self)); clutter_actor_add_child_internal (self, child, ADD_CHILD_DEFAULT_FLAGS, insert_child_above, sibling); } /** * clutter_actor_insert_child_below: * @self: a #ClutterActor * @child: a #ClutterActor * @sibling: (allow-none): a child of @self, or %NULL * * Inserts @child into the list of children of @self, below another * child of @self or, if @sibling is %NULL, below all the children * of @self. * * This function will acquire a reference on @child that will only * be released when calling clutter_actor_remove_child(). * * This function will not take into consideration the #ClutterActor:depth * of @child. * * This function will emit the #ClutterContainer::actor-added signal * on @self. * * Since: 1.10 */ void clutter_actor_insert_child_below (ClutterActor *self, ClutterActor *child, ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (self != child); g_return_if_fail (child != sibling); g_return_if_fail (child->priv->parent == NULL); g_return_if_fail (sibling == NULL || (CLUTTER_IS_ACTOR (sibling) && sibling->priv->parent == self)); clutter_actor_add_child_internal (self, child, ADD_CHILD_DEFAULT_FLAGS, insert_child_below, sibling); } /** * clutter_actor_set_parent: * @self: A #ClutterActor * @parent: A new #ClutterActor parent * * Sets the parent of @self to @parent. * * This function will result in @parent acquiring a reference on @self, * eventually by sinking its floating reference first. The reference * will be released by clutter_actor_unparent(). * * This function should only be called by legacy #ClutterActors * implementing the #ClutterContainer interface. * * Deprecated: 1.10: Use clutter_actor_add_child() instead. */ void clutter_actor_set_parent (ClutterActor *self, ClutterActor *parent) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (parent)); g_return_if_fail (self != parent); g_return_if_fail (self->priv->parent == NULL); /* as this function will be called inside ClutterContainer::add * implementations or when building up a composite actor, we have * to preserve the old behaviour, and not create child meta or * emit the ::actor-added signal, to avoid recursion or double * emissions */ clutter_actor_add_child_internal (parent, self, ADD_CHILD_LEGACY_FLAGS, insert_child_at_depth, NULL); } /** * clutter_actor_get_parent: * @self: A #ClutterActor * * Retrieves the parent of @self. * * Return Value: (transfer none): The #ClutterActor parent, or %NULL * if no parent is set */ ClutterActor * clutter_actor_get_parent (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->parent; } /** * clutter_actor_get_paint_visibility: * @self: A #ClutterActor * * Retrieves the 'paint' visibility of an actor recursively checking for non * visible parents. * * This is by definition the same as %CLUTTER_ACTOR_IS_MAPPED. * * Return Value: %TRUE if the actor is visibile and will be painted. * * Since: 0.8 */ gboolean clutter_actor_get_paint_visibility (ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); return CLUTTER_ACTOR_IS_MAPPED (actor); } /** * clutter_actor_remove_child: * @self: a #ClutterActor * @child: a #ClutterActor * * Removes @child from the children of @self. * * This function will release the reference added by * clutter_actor_add_child(), so if you want to keep using @child * you will have to acquire a referenced on it before calling this * function. * * This function will emit the #ClutterContainer::actor-removed * signal on @self. * * Since: 1.10 */ void clutter_actor_remove_child (ClutterActor *self, ClutterActor *child) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (self != child); g_return_if_fail (child->priv->parent != NULL); g_return_if_fail (child->priv->parent == self); clutter_actor_remove_child_internal (self, child, REMOVE_CHILD_DEFAULT_FLAGS); } /** * clutter_actor_remove_all_children: * @self: a #ClutterActor * * Removes all children of @self. * * This function releases the reference added by inserting a child actor * in the list of children of @self. * * If the reference count of a child drops to zero, the child will be * destroyed. If you want to ensure the destruction of all the children * of @self, use clutter_actor_destroy_all_children(). * * Since: 1.10 */ void clutter_actor_remove_all_children (ClutterActor *self) { ClutterActorIter iter; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->n_children == 0) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, NULL)) clutter_actor_iter_remove (&iter); g_object_thaw_notify (G_OBJECT (self)); /* sanity check */ g_assert (self->priv->first_child == NULL); g_assert (self->priv->last_child == NULL); g_assert (self->priv->n_children == 0); } /** * clutter_actor_destroy_all_children: * @self: a #ClutterActor * * Destroys all children of @self. * * This function releases the reference added by inserting a child * actor in the list of children of @self, and ensures that the * #ClutterActor::destroy signal is emitted on each child of the * actor. * * By default, #ClutterActor will emit the #ClutterActor::destroy signal * when its reference count drops to 0; the default handler of the * #ClutterActor::destroy signal will destroy all the children of an * actor. This function ensures that all children are destroyed, instead * of just removed from @self, unlike clutter_actor_remove_all_children() * which will merely release the reference and remove each child. * * Unless you acquired an additional reference on each child of @self * prior to calling clutter_actor_remove_all_children() and want to reuse * the actors, you should use clutter_actor_destroy_all_children() in * order to make sure that children are destroyed and signal handlers * are disconnected even in cases where circular references prevent this * from automatically happening through reference counting alone. * * Since: 1.10 */ void clutter_actor_destroy_all_children (ClutterActor *self) { ClutterActorIter iter; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->n_children == 0) return; g_object_freeze_notify (G_OBJECT (self)); clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, NULL)) clutter_actor_iter_destroy (&iter); g_object_thaw_notify (G_OBJECT (self)); /* sanity check */ g_assert (self->priv->first_child == NULL); g_assert (self->priv->last_child == NULL); g_assert (self->priv->n_children == 0); } typedef struct _InsertBetweenData { ClutterActor *prev_sibling; ClutterActor *next_sibling; } InsertBetweenData; static void insert_child_between (ClutterActor *self, ClutterActor *child, gpointer data_) { InsertBetweenData *data = data_; ClutterActor *prev_sibling = data->prev_sibling; ClutterActor *next_sibling = data->next_sibling; child->priv->parent = self; child->priv->prev_sibling = prev_sibling; child->priv->next_sibling = next_sibling; if (prev_sibling != NULL) prev_sibling->priv->next_sibling = child; if (next_sibling != NULL) next_sibling->priv->prev_sibling = child; if (child->priv->prev_sibling == NULL) self->priv->first_child = child; if (child->priv->next_sibling == NULL) self->priv->last_child = child; } /** * clutter_actor_replace_child: * @self: a #ClutterActor * @old_child: the child of @self to replace * @new_child: the #ClutterActor to replace @old_child * * Replaces @old_child with @new_child in the list of children of @self. * * Since: 1.10 */ void clutter_actor_replace_child (ClutterActor *self, ClutterActor *old_child, ClutterActor *new_child) { ClutterActor *prev_sibling, *next_sibling; InsertBetweenData clos; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (old_child)); g_return_if_fail (old_child->priv->parent == self); g_return_if_fail (CLUTTER_IS_ACTOR (new_child)); g_return_if_fail (old_child != new_child); g_return_if_fail (new_child != self); g_return_if_fail (new_child->priv->parent == NULL); prev_sibling = old_child->priv->prev_sibling; next_sibling = old_child->priv->next_sibling; clutter_actor_remove_child_internal (self, old_child, REMOVE_CHILD_DEFAULT_FLAGS); clos.prev_sibling = prev_sibling; clos.next_sibling = next_sibling; clutter_actor_add_child_internal (self, new_child, ADD_CHILD_DEFAULT_FLAGS, insert_child_between, &clos); } /** * clutter_actor_unparent: * @self: a #ClutterActor * * Removes the parent of @self. * * This will cause the parent of @self to release the reference * acquired when calling clutter_actor_set_parent(), so if you * want to keep @self you will have to acquire a reference of * your own, through g_object_ref(). * * This function should only be called by legacy #ClutterActors * implementing the #ClutterContainer interface. * * Since: 0.2 * * Deprecated: 1.10: Use clutter_actor_remove_child() instead. */ void clutter_actor_unparent (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->parent == NULL) return; clutter_actor_remove_child_internal (self->priv->parent, self, REMOVE_CHILD_LEGACY_FLAGS); } /** * clutter_actor_contains: * @self: A #ClutterActor * @descendant: A #ClutterActor, possibly contained in @self * * Determines if @descendant is contained inside @self (either as an * immediate child, or as a deeper descendant). If @self and * @descendant point to the same actor then it will also return %TRUE. * * Return value: whether @descendent is contained within @self * * Since: 1.4 */ gboolean clutter_actor_contains (ClutterActor *self, ClutterActor *descendant) { ClutterActor *actor; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (descendant), FALSE); for (actor = descendant; actor; actor = actor->priv->parent) if (actor == self) return TRUE; return FALSE; } /** * clutter_actor_set_child_above_sibling: * @self: a #ClutterActor * @child: a #ClutterActor child of @self * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL * * Sets @child to be above @sibling in the list of children of @self. * * If @sibling is %NULL, @child will be the new last child of @self. * * This function is logically equivalent to removing @child and using * clutter_actor_insert_child_above(), but it will not emit signals * or change state on @child. * * Since: 1.10 */ void clutter_actor_set_child_above_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (child->priv->parent == self); g_return_if_fail (child != sibling); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); if (sibling != NULL) g_return_if_fail (sibling->priv->parent == self); if (CLUTTER_ACTOR_IN_DESTRUCTION (self) || CLUTTER_ACTOR_IN_DESTRUCTION (child) || (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling))) return; /* we don't want to change the state of child, or emit signals, or * regenerate ChildMeta instances here, but we still want to follow * the correct sequence of steps encoded in remove_child() and * add_child(), so that correctness is ensured, and we only go * through one known code path. */ g_object_ref (child); clutter_actor_remove_child_internal (self, child, 0); clutter_actor_add_child_internal (self, child, ADD_CHILD_NOTIFY_FIRST_LAST, insert_child_above, sibling); g_object_unref(child); clutter_actor_queue_redraw_on_parent (child); } /** * clutter_actor_set_child_below_sibling: * @self: a #ClutterActor * @child: a #ClutterActor child of @self * @sibling: (allow-none): a #ClutterActor child of @self, or %NULL * * Sets @child to be below @sibling in the list of children of @self. * * If @sibling is %NULL, @child will be the new first child of @self. * * This function is logically equivalent to removing @self and using * clutter_actor_insert_child_below(), but it will not emit signals * or change state on @child. * * Since: 1.10 */ void clutter_actor_set_child_below_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (child->priv->parent == self); g_return_if_fail (child != sibling); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); if (sibling != NULL) g_return_if_fail (sibling->priv->parent == self); if (CLUTTER_ACTOR_IN_DESTRUCTION (self) || CLUTTER_ACTOR_IN_DESTRUCTION (child) || (sibling != NULL && CLUTTER_ACTOR_IN_DESTRUCTION (sibling))) return; /* see the comment in set_child_above_sibling() */ g_object_ref (child); clutter_actor_remove_child_internal (self, child, 0); clutter_actor_add_child_internal (self, child, ADD_CHILD_NOTIFY_FIRST_LAST, insert_child_below, sibling); g_object_unref(child); clutter_actor_queue_redraw_on_parent (child); } /** * clutter_actor_set_child_at_index: * @self: a #ClutterActor * @child: a #ClutterActor child of @self * @index_: the new index for @child * * Changes the index of @child in the list of children of @self. * * This function is logically equivalent to removing @child and * calling clutter_actor_insert_child_at_index(), but it will not * emit signals or change state on @child. * * Since: 1.10 */ void clutter_actor_set_child_at_index (ClutterActor *self, ClutterActor *child, gint index_) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (child->priv->parent == self); g_return_if_fail (index_ <= self->priv->n_children); if (CLUTTER_ACTOR_IN_DESTRUCTION (self) || CLUTTER_ACTOR_IN_DESTRUCTION (child)) return; g_object_ref (child); clutter_actor_remove_child_internal (self, child, 0); clutter_actor_add_child_internal (self, child, ADD_CHILD_NOTIFY_FIRST_LAST, insert_child_at_index, GINT_TO_POINTER (index_)); g_object_unref (child); clutter_actor_queue_relayout (self); } /* * Event handling */ /** * clutter_actor_event: * @actor: a #ClutterActor * @event: a #ClutterEvent * @capture: %TRUE if event in in capture phase, %FALSE otherwise. * * This function is used to emit an event on the main stage. * You should rarely need to use this function, except for * synthetising events. * * Return value: the return value from the signal emission: %TRUE * if the actor handled the event, or %FALSE if the event was * not handled * * Since: 0.6 */ gboolean clutter_actor_event (ClutterActor *actor, const ClutterEvent *event, gboolean capture) { gboolean retval = FALSE; gint signal_num = -1; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); g_return_val_if_fail (event != NULL, FALSE); g_object_ref (actor); if (capture) { GQuark detail = 0; switch (event->type) { case CLUTTER_NOTHING: break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: detail = quark_key; break; case CLUTTER_MOTION: detail = quark_motion; break; case CLUTTER_ENTER: case CLUTTER_LEAVE: detail = quark_pointer_focus; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: detail = quark_button; break; case CLUTTER_SCROLL: detail = quark_scroll; break; case CLUTTER_STAGE_STATE: detail = quark_stage; break; case CLUTTER_DESTROY_NOTIFY: detail = quark_destroy; break; case CLUTTER_CLIENT_MESSAGE: detail = quark_client; break; case CLUTTER_DELETE: detail = quark_delete; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: detail = quark_touch; break; case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: detail = quark_touchpad; break; case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: detail = quark_proximity; break; case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: detail = quark_pad; break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: detail = quark_im; break; case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: break; case CLUTTER_EVENT_LAST: /* Just keep compiler warnings quiet */ break; } g_signal_emit (actor, actor_signals[CAPTURED_EVENT], detail, event, &retval); goto out; } g_signal_emit (actor, actor_signals[EVENT], 0, event, &retval); if (!retval) { switch (event->type) { case CLUTTER_NOTHING: break; case CLUTTER_BUTTON_PRESS: signal_num = BUTTON_PRESS_EVENT; break; case CLUTTER_BUTTON_RELEASE: signal_num = BUTTON_RELEASE_EVENT; break; case CLUTTER_SCROLL: signal_num = SCROLL_EVENT; break; case CLUTTER_KEY_PRESS: signal_num = KEY_PRESS_EVENT; break; case CLUTTER_KEY_RELEASE: signal_num = KEY_RELEASE_EVENT; break; case CLUTTER_MOTION: signal_num = MOTION_EVENT; break; case CLUTTER_ENTER: signal_num = ENTER_EVENT; break; case CLUTTER_LEAVE: signal_num = LEAVE_EVENT; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_CANCEL: signal_num = TOUCH_EVENT; break; case CLUTTER_DELETE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: default: signal_num = -1; break; } if (signal_num != -1) g_signal_emit (actor, actor_signals[signal_num], 0, event, &retval); } out: g_object_unref (actor); return retval; } /** * clutter_actor_set_reactive: * @actor: a #ClutterActor * @reactive: whether the actor should be reactive to events * * Sets @actor as reactive. Reactive actors will receive events. * * Since: 0.6 */ void clutter_actor_set_reactive (ClutterActor *actor, gboolean reactive) { g_return_if_fail (CLUTTER_IS_ACTOR (actor)); if (reactive == CLUTTER_ACTOR_IS_REACTIVE (actor)) return; if (reactive) CLUTTER_ACTOR_SET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE); else CLUTTER_ACTOR_UNSET_FLAGS (actor, CLUTTER_ACTOR_REACTIVE); g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_REACTIVE]); } /** * clutter_actor_get_reactive: * @actor: a #ClutterActor * * Checks whether @actor is marked as reactive. * * Return value: %TRUE if the actor is reactive * * Since: 0.6 */ gboolean clutter_actor_get_reactive (ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); return CLUTTER_ACTOR_IS_REACTIVE (actor) ? TRUE : FALSE; } /** * clutter_actor_set_anchor_point: * @self: a #ClutterActor * @anchor_x: X coordinate of the anchor point * @anchor_y: Y coordinate of the anchor point * * Sets an anchor point for @self. The anchor point is a point in the * coordinate space of an actor to which the actor position within its * parent is relative; the default is (0, 0), i.e. the top-left corner * of the actor. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead. */ void clutter_actor_set_anchor_point (ClutterActor *self, gfloat anchor_x, gfloat anchor_y) { ClutterTransformInfo *info; ClutterActorPrivate *priv; gboolean changed = FALSE; gfloat old_anchor_x, old_anchor_y; GObject *obj; g_return_if_fail (CLUTTER_IS_ACTOR (self)); obj = G_OBJECT (self); priv = self->priv; info = _clutter_actor_get_transform_info (self); g_object_freeze_notify (obj); clutter_anchor_coord_get_units (self, &info->anchor, &old_anchor_x, &old_anchor_y, NULL); if (info->anchor.is_fractional) g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]); if (old_anchor_x != anchor_x) { g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]); changed = TRUE; } if (old_anchor_y != anchor_y) { g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]); changed = TRUE; } clutter_anchor_coord_set_units (&info->anchor, anchor_x, anchor_y, 0); if (changed) { priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); } g_object_thaw_notify (obj); } /** * clutter_actor_get_anchor_point_gravity: * @self: a #ClutterActor * * Retrieves the anchor position expressed as a #ClutterGravity. If * the anchor point was specified using pixels or units this will * return %CLUTTER_GRAVITY_NONE. * * Return value: the #ClutterGravity used by the anchor point * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterActor:pivot-point instead. */ ClutterGravity clutter_actor_get_anchor_point_gravity (ClutterActor *self) { const ClutterTransformInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_GRAVITY_NONE); info = _clutter_actor_get_transform_info_or_defaults (self); return clutter_anchor_coord_get_gravity (&info->anchor); } /** * clutter_actor_move_anchor_point: * @self: a #ClutterActor * @anchor_x: X coordinate of the anchor point * @anchor_y: Y coordinate of the anchor point * * Sets an anchor point for the actor, and adjusts the actor postion so that * the relative position of the actor toward its parent remains the same. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point and * clutter_actor_set_translation() instead. */ void clutter_actor_move_anchor_point (ClutterActor *self, gfloat anchor_x, gfloat anchor_y) { gfloat old_anchor_x, old_anchor_y; const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info (self); clutter_anchor_coord_get_units (self, &info->anchor, &old_anchor_x, &old_anchor_y, NULL); g_object_freeze_notify (G_OBJECT (self)); clutter_actor_set_anchor_point (self, anchor_x, anchor_y); if (self->priv->position_set) clutter_actor_move_by (self, anchor_x - old_anchor_x, anchor_y - old_anchor_y); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_move_anchor_point_from_gravity: * @self: a #ClutterActor * @gravity: #ClutterGravity. * * Sets an anchor point on the actor based on the given gravity, adjusting the * actor postion so that its relative position within its parent remains * unchanged. * * Since version 1.0 the anchor point will be stored as a gravity so * that if the actor changes size then the anchor point will move. For * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST * and later double the size of the actor, the anchor point will move * to the bottom right. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point and * clutter_actor_set_translation() instead. */ void clutter_actor_move_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity) { gfloat old_anchor_x, old_anchor_y, new_anchor_x, new_anchor_y; const ClutterTransformInfo *info; ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; info = _clutter_actor_get_transform_info (self); g_object_freeze_notify (G_OBJECT (self)); clutter_anchor_coord_get_units (self, &info->anchor, &old_anchor_x, &old_anchor_y, NULL); clutter_actor_set_anchor_point_from_gravity (self, gravity); clutter_anchor_coord_get_units (self, &info->anchor, &new_anchor_x, &new_anchor_y, NULL); if (priv->position_set) clutter_actor_move_by (self, new_anchor_x - old_anchor_x, new_anchor_y - old_anchor_y); g_object_thaw_notify (G_OBJECT (self)); } /** * clutter_actor_set_anchor_point_from_gravity: * @self: a #ClutterActor * @gravity: #ClutterGravity. * * Sets an anchor point on the actor, based on the given gravity (this is a * convenience function wrapping clutter_actor_set_anchor_point()). * * Since version 1.0 the anchor point will be stored as a gravity so * that if the actor changes size then the anchor point will move. For * example, if you set the anchor point to %CLUTTER_GRAVITY_SOUTH_EAST * and later double the size of the actor, the anchor point will move * to the bottom right. * * Since: 0.6 * * Deprecated: 1.12: Use #ClutterActor:pivot-point and * clutter_actor_set_translation() instead. E.g. For %CLUTTER_GRAVITY_CENTER set * pivot_point to (0.5,0.5) and the translation to (width/2,height/2). */ void clutter_actor_set_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (gravity == CLUTTER_GRAVITY_NONE) clutter_actor_set_anchor_point (self, 0, 0); else { GObject *obj = G_OBJECT (self); ClutterTransformInfo *info; g_object_freeze_notify (obj); info = _clutter_actor_get_transform_info (self); clutter_anchor_coord_set_gravity (&info->anchor, gravity); g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_GRAVITY]); g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_X]); g_object_notify_by_pspec (obj, obj_props[PROP_ANCHOR_Y]); self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_thaw_notify (obj); } } static void clutter_actor_store_content_box (ClutterActor *self, const ClutterActorBox *box) { if (box != NULL) { self->priv->content_box = *box; self->priv->content_box_valid = TRUE; } else self->priv->content_box_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]); } static void clutter_container_iface_init (ClutterContainerIface *iface) { /* we don't override anything, as ClutterContainer already has a default * implementation that we can use, and which calls into our own API. */ } typedef enum { PARSE_X, PARSE_Y, PARSE_WIDTH, PARSE_HEIGHT, PARSE_ANCHOR_X, PARSE_ANCHOR_Y } ParseDimension; static gfloat parse_units (ClutterActor *self, ParseDimension dimension, JsonNode *node) { GValue value = G_VALUE_INIT; gfloat retval = 0; if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE) return 0; json_node_get_value (node, &value); if (G_VALUE_HOLDS (&value, G_TYPE_INT64)) { retval = (gfloat) g_value_get_int64 (&value); } else if (G_VALUE_HOLDS (&value, G_TYPE_DOUBLE)) { retval = g_value_get_double (&value); } else if (G_VALUE_HOLDS (&value, G_TYPE_STRING)) { ClutterUnits units; gboolean res; res = clutter_units_from_string (&units, g_value_get_string (&value)); if (res) retval = clutter_units_to_pixels (&units); else { g_warning ("Invalid value '%s': integers, strings or floating point " "values can be used for the x, y, width and height " "properties. Valid modifiers for strings are 'px', 'mm', " "'pt' and 'em'.", g_value_get_string (&value)); retval = 0; } } else { g_warning ("Invalid value of type '%s': integers, strings of floating " "point values can be used for the x, y, width, height " "anchor-x and anchor-y properties.", g_type_name (G_VALUE_TYPE (&value))); } g_value_unset (&value); return retval; } typedef struct { ClutterRotateAxis axis; gdouble angle; gfloat center_x; gfloat center_y; gfloat center_z; } RotationInfo; static inline gboolean parse_rotation_array (ClutterActor *actor, JsonArray *array, RotationInfo *info) { JsonNode *element; if (json_array_get_length (array) != 2) return FALSE; /* angle */ element = json_array_get_element (array, 0); if (JSON_NODE_TYPE (element) == JSON_NODE_VALUE) info->angle = json_node_get_double (element); else return FALSE; /* center */ element = json_array_get_element (array, 1); if (JSON_NODE_TYPE (element) == JSON_NODE_ARRAY) { JsonArray *center = json_node_get_array (element); if (json_array_get_length (center) != 2) return FALSE; switch (info->axis) { case CLUTTER_X_AXIS: info->center_y = parse_units (actor, PARSE_Y, json_array_get_element (center, 0)); info->center_z = parse_units (actor, PARSE_Y, json_array_get_element (center, 1)); return TRUE; case CLUTTER_Y_AXIS: info->center_x = parse_units (actor, PARSE_X, json_array_get_element (center, 0)); info->center_z = parse_units (actor, PARSE_X, json_array_get_element (center, 1)); return TRUE; case CLUTTER_Z_AXIS: info->center_x = parse_units (actor, PARSE_X, json_array_get_element (center, 0)); info->center_y = parse_units (actor, PARSE_Y, json_array_get_element (center, 1)); return TRUE; } } return FALSE; } static gboolean parse_rotation (ClutterActor *actor, JsonNode *node, RotationInfo *info) { JsonArray *array; guint len, i; gboolean retval = FALSE; if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) { g_warning ("Invalid node of type '%s' found, expecting an array", json_node_type_name (node)); return FALSE; } array = json_node_get_array (node); len = json_array_get_length (array); for (i = 0; i < len; i++) { JsonNode *element = json_array_get_element (array, i); JsonObject *object; JsonNode *member; if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT) { g_warning ("Invalid node of type '%s' found, expecting an object", json_node_type_name (element)); return FALSE; } object = json_node_get_object (element); if (json_object_has_member (object, "x-axis")) { member = json_object_get_member (object, "x-axis"); info->axis = CLUTTER_X_AXIS; if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE) { info->angle = json_node_get_double (member); retval = TRUE; } else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY) retval = parse_rotation_array (actor, json_node_get_array (member), info); else retval = FALSE; } else if (json_object_has_member (object, "y-axis")) { member = json_object_get_member (object, "y-axis"); info->axis = CLUTTER_Y_AXIS; if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE) { info->angle = json_node_get_double (member); retval = TRUE; } else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY) retval = parse_rotation_array (actor, json_node_get_array (member), info); else retval = FALSE; } else if (json_object_has_member (object, "z-axis")) { member = json_object_get_member (object, "z-axis"); info->axis = CLUTTER_Z_AXIS; if (JSON_NODE_TYPE (member) == JSON_NODE_VALUE) { info->angle = json_node_get_double (member); retval = TRUE; } else if (JSON_NODE_TYPE (member) == JSON_NODE_ARRAY) retval = parse_rotation_array (actor, json_node_get_array (member), info); else retval = FALSE; } } return retval; } static GSList * parse_actor_metas (ClutterScript *script, ClutterActor *actor, JsonNode *node) { GList *elements, *l; GSList *retval = NULL; if (!JSON_NODE_HOLDS_ARRAY (node)) return NULL; elements = json_array_get_elements (json_node_get_array (node)); for (l = elements; l != NULL; l = l->next) { JsonNode *element = l->data; const gchar *id_ = _clutter_script_get_id_from_node (element); GObject *meta; if (id_ == NULL || *id_ == '\0') continue; meta = clutter_script_get_object (script, id_); if (meta == NULL) continue; retval = g_slist_prepend (retval, meta); } g_list_free (elements); return g_slist_reverse (retval); } static ClutterMargin * parse_margin (ClutterActor *self, JsonNode *node) { ClutterMargin *margin; JsonArray *array; if (!JSON_NODE_HOLDS_ARRAY (node)) { g_warning ("The margin property must be an array of 1 to 4 elements"); return NULL; } margin = clutter_margin_new (); array = json_node_get_array (node); switch (json_array_get_length (array)) { case 1: margin->top = margin->right = margin->bottom = margin->left = parse_units (self, 0, json_array_get_element (array, 0)); break; case 2: margin->top = margin->bottom = parse_units (self, 0, json_array_get_element (array, 0)); margin->right = margin->left = parse_units (self, 0, json_array_get_element (array, 1)); break; case 3: margin->top = parse_units (self, 0, json_array_get_element (array, 0)); margin->right = margin->left = parse_units (self, 0, json_array_get_element (array, 1)); margin->bottom = parse_units (self, 0, json_array_get_element (array, 2)); break; case 4: margin->top = parse_units (self, 0, json_array_get_element (array, 0)); margin->right = parse_units (self, 0, json_array_get_element (array, 1)); margin->bottom = parse_units (self, 0, json_array_get_element (array, 2)); margin->left = parse_units (self, 0, json_array_get_element (array, 3)); break; default: g_warning ("The margin property must be an array of 1 to 4 elements"); clutter_margin_free (margin); return NULL; } return margin; } static gboolean clutter_actor_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { ClutterActor *actor = CLUTTER_ACTOR (scriptable); gboolean retval = FALSE; if ((name[0] == 'x' && name[1] == '\0') || (name[0] == 'y' && name[1] == '\0') || (strcmp (name, "width") == 0) || (strcmp (name, "height") == 0) || (strcmp (name, "anchor_x") == 0) || (strcmp (name, "anchor_y") == 0)) { ParseDimension dimension; gfloat units; if (name[0] == 'x') dimension = PARSE_X; else if (name[0] == 'y') dimension = PARSE_Y; else if (name[0] == 'w') dimension = PARSE_WIDTH; else if (name[0] == 'h') dimension = PARSE_HEIGHT; else if (name[0] == 'a' && name[7] == 'x') dimension = PARSE_ANCHOR_X; else if (name[0] == 'a' && name[7] == 'y') dimension = PARSE_ANCHOR_Y; else return FALSE; units = parse_units (actor, dimension, node); /* convert back to pixels: all properties are pixel-based */ g_value_init (value, G_TYPE_FLOAT); g_value_set_float (value, units); retval = TRUE; } else if (strcmp (name, "rotation") == 0) { RotationInfo *info; info = g_slice_new0 (RotationInfo); retval = parse_rotation (actor, node, info); if (retval) { g_value_init (value, G_TYPE_POINTER); g_value_set_pointer (value, info); } else g_slice_free (RotationInfo, info); } else if (strcmp (name, "actions") == 0 || strcmp (name, "constraints") == 0 || strcmp (name, "effects") == 0) { GSList *l; l = parse_actor_metas (script, actor, node); g_value_init (value, G_TYPE_POINTER); g_value_set_pointer (value, l); retval = TRUE; } else if (strcmp (name, "margin") == 0) { ClutterMargin *margin = parse_margin (actor, node); if (margin) { g_value_init (value, CLUTTER_TYPE_MARGIN); g_value_set_boxed (value, margin); retval = TRUE; } } return retval; } static void clutter_actor_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { ClutterActor *actor = CLUTTER_ACTOR (scriptable); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (CLUTTER_HAS_DEBUG (SCRIPT))) { gchar *tmp = g_strdup_value_contents (value); CLUTTER_NOTE (SCRIPT, "in ClutterActor::set_custom_property('%s') = %s", name, tmp); g_free (tmp); } #endif /* CLUTTER_ENABLE_DEBUG */ if (strcmp (name, "rotation") == 0) { RotationInfo *info; if (!G_VALUE_HOLDS (value, G_TYPE_POINTER)) return; info = g_value_get_pointer (value); clutter_actor_set_rotation (actor, info->axis, info->angle, info->center_x, info->center_y, info->center_z); g_slice_free (RotationInfo, info); return; } if (strcmp (name, "actions") == 0 || strcmp (name, "constraints") == 0 || strcmp (name, "effects") == 0) { GSList *metas, *l; if (!G_VALUE_HOLDS (value, G_TYPE_POINTER)) return; metas = g_value_get_pointer (value); for (l = metas; l != NULL; l = l->next) { if (name[0] == 'a') clutter_actor_add_action (actor, l->data); if (name[0] == 'c') clutter_actor_add_constraint (actor, l->data); if (name[0] == 'e') clutter_actor_add_effect (actor, l->data); } g_slist_free (metas); return; } if (strcmp (name, "margin") == 0) { clutter_actor_set_margin (actor, g_value_get_boxed (value)); return; } g_object_set_property (G_OBJECT (scriptable), name, value); } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_actor_parse_custom_node; iface->set_custom_property = clutter_actor_set_custom_property; } static gboolean get_layout_from_animation_property (ClutterActor *actor, const gchar *name, gchar **name_p) { g_auto (GStrv) tokens = NULL; if (!g_str_has_prefix (name, "@layout")) return FALSE; tokens = g_strsplit (name, ".", -1); if (tokens == NULL || g_strv_length (tokens) != 2) { CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'", name + 1); return FALSE; } if (name_p != NULL) *name_p = g_strdup (tokens[1]); return TRUE; } static ClutterActorMeta * get_meta_from_animation_property (ClutterActor *actor, const gchar *name, gchar **name_p) { ClutterActorPrivate *priv = actor->priv; ClutterActorMeta *meta = NULL; gchar **tokens; /* if this is not a special property, fall through */ if (name[0] != '@') return NULL; /* detect the properties named using the following spec: * * @
.. * * where
can be one of the following: * * - actions * - constraints * - effects * * and is the name set on a specific ActorMeta */ tokens = g_strsplit (name + 1, ".", -1); if (tokens == NULL || g_strv_length (tokens) != 3) { CLUTTER_NOTE (ANIMATION, "Invalid property name '%s'", name + 1); g_strfreev (tokens); return NULL; } if (strcmp (tokens[0], "actions") == 0) meta = _clutter_meta_group_get_meta (priv->actions, tokens[1]); if (strcmp (tokens[0], "constraints") == 0) meta = _clutter_meta_group_get_meta (priv->constraints, tokens[1]); if (strcmp (tokens[0], "effects") == 0) meta = _clutter_meta_group_get_meta (priv->effects, tokens[1]); if (name_p != NULL) *name_p = g_strdup (tokens[2]); CLUTTER_NOTE (ANIMATION, "Looking for property '%s' of object '%s' in section '%s'", tokens[2], tokens[1], tokens[0]); g_strfreev (tokens); return meta; } static GParamSpec * clutter_actor_find_property (ClutterAnimatable *animatable, const gchar *property_name) { ClutterActor *actor = CLUTTER_ACTOR (animatable); ClutterActorMeta *meta = NULL; GObjectClass *klass = NULL; GParamSpec *pspec = NULL; gchar *p_name = NULL; gboolean use_layout; use_layout = get_layout_from_animation_property (actor, property_name, &p_name); if (!use_layout) meta = get_meta_from_animation_property (actor, property_name, &p_name); if (meta != NULL) { klass = G_OBJECT_GET_CLASS (meta); pspec = g_object_class_find_property (klass, p_name); } else if (use_layout) { klass = G_OBJECT_GET_CLASS (actor->priv->layout_manager); pspec = g_object_class_find_property (klass, p_name); } else { klass = G_OBJECT_GET_CLASS (animatable); pspec = g_object_class_find_property (klass, property_name); } g_free (p_name); return pspec; } static void clutter_actor_get_initial_state (ClutterAnimatable *animatable, const gchar *property_name, GValue *initial) { ClutterActor *actor = CLUTTER_ACTOR (animatable); ClutterActorMeta *meta = NULL; gchar *p_name = NULL; gboolean use_layout; use_layout = get_layout_from_animation_property (actor, property_name, &p_name); if (!use_layout) meta = get_meta_from_animation_property (actor, property_name, &p_name); if (meta != NULL) g_object_get_property (G_OBJECT (meta), p_name, initial); else if (use_layout) g_object_get_property (G_OBJECT (actor->priv->layout_manager), p_name, initial); else g_object_get_property (G_OBJECT (animatable), property_name, initial); g_free (p_name); } /* * clutter_actor_set_animatable_property: * @actor: a #ClutterActor * @prop_id: the paramspec id * @value: the value to set * @pspec: the paramspec * * Sets values of animatable properties. * * This is a variant of clutter_actor_set_property() that gets called * by the #ClutterAnimatable implementation of #ClutterActor for the * properties with the %CLUTTER_PARAM_ANIMATABLE flag set on their * #GParamSpec. * * Unlike the implementation of #GObjectClass.set_property(), this * function will not update the interval if a transition involving an * animatable property is in progress - this avoids cycles with the * transition API calling the public API. */ static void clutter_actor_set_animatable_property (ClutterActor *actor, guint prop_id, const GValue *value, GParamSpec *pspec) { GObject *obj = G_OBJECT (actor); g_object_freeze_notify (obj); switch (prop_id) { case PROP_X: clutter_actor_set_x_internal (actor, g_value_get_float (value)); break; case PROP_Y: clutter_actor_set_y_internal (actor, g_value_get_float (value)); break; case PROP_POSITION: clutter_actor_set_position_internal (actor, g_value_get_boxed (value)); break; case PROP_WIDTH: clutter_actor_set_width_internal (actor, g_value_get_float (value)); break; case PROP_HEIGHT: clutter_actor_set_height_internal (actor, g_value_get_float (value)); break; case PROP_SIZE: clutter_actor_set_size_internal (actor, g_value_get_boxed (value)); break; case PROP_ALLOCATION: clutter_actor_allocate_internal (actor, g_value_get_boxed (value), actor->priv->allocation_flags); clutter_actor_queue_redraw (actor); break; case PROP_DEPTH: clutter_actor_set_depth_internal (actor, g_value_get_float (value)); break; case PROP_Z_POSITION: clutter_actor_set_z_position_internal (actor, g_value_get_float (value)); break; case PROP_OPACITY: clutter_actor_set_opacity_internal (actor, g_value_get_uint (value)); break; case PROP_BACKGROUND_COLOR: clutter_actor_set_background_color_internal (actor, clutter_value_get_color (value)); break; case PROP_PIVOT_POINT: clutter_actor_set_pivot_point_internal (actor, g_value_get_boxed (value)); break; case PROP_PIVOT_POINT_Z: clutter_actor_set_pivot_point_z_internal (actor, g_value_get_float (value)); break; case PROP_TRANSLATION_X: case PROP_TRANSLATION_Y: case PROP_TRANSLATION_Z: clutter_actor_set_translation_internal (actor, g_value_get_float (value), pspec); break; case PROP_SCALE_X: case PROP_SCALE_Y: case PROP_SCALE_Z: clutter_actor_set_scale_factor_internal (actor, g_value_get_double (value), pspec); break; case PROP_ROTATION_ANGLE_X: case PROP_ROTATION_ANGLE_Y: case PROP_ROTATION_ANGLE_Z: clutter_actor_set_rotation_angle_internal (actor, g_value_get_double (value), pspec); break; case PROP_CONTENT_BOX: clutter_actor_store_content_box (actor, g_value_get_boxed (value)); break; case PROP_MARGIN_TOP: case PROP_MARGIN_BOTTOM: case PROP_MARGIN_LEFT: case PROP_MARGIN_RIGHT: clutter_actor_set_margin_internal (actor, g_value_get_float (value), pspec); break; case PROP_TRANSFORM: clutter_actor_set_transform_internal (actor, g_value_get_boxed (value)); break; case PROP_CHILD_TRANSFORM: clutter_actor_set_child_transform_internal (actor, g_value_get_boxed (value)); break; default: g_object_set_property (obj, pspec->name, value); break; } g_object_thaw_notify (obj); } static void clutter_actor_set_final_state (ClutterAnimatable *animatable, const gchar *property_name, const GValue *final) { ClutterActor *actor = CLUTTER_ACTOR (animatable); ClutterActorMeta *meta = NULL; gchar *p_name = NULL; gboolean use_layout; use_layout = get_layout_from_animation_property (actor, property_name, &p_name); if (!use_layout) meta = get_meta_from_animation_property (actor, property_name, &p_name); if (meta != NULL) g_object_set_property (G_OBJECT (meta), p_name, final); else if (use_layout) g_object_set_property (G_OBJECT (actor->priv->layout_manager), p_name, final); else { GObjectClass *obj_class = G_OBJECT_GET_CLASS (animatable); GParamSpec *pspec; pspec = g_object_class_find_property (obj_class, property_name); if (pspec != NULL) { if ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0) { /* XXX - I'm going to the special hell for this */ clutter_actor_set_animatable_property (actor, pspec->param_id, final, pspec); } else g_object_set_property (G_OBJECT (animatable), pspec->name, final); } } g_free (p_name); } static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface) { iface->find_property = clutter_actor_find_property; iface->get_initial_state = clutter_actor_get_initial_state; iface->set_final_state = clutter_actor_set_final_state; } /** * clutter_actor_transform_stage_point: * @self: A #ClutterActor * @x: (in): x screen coordinate of the point to unproject * @y: (in): y screen coordinate of the point to unproject * @x_out: (out): return location for the unprojected x coordinance * @y_out: (out): return location for the unprojected y coordinance * * This function translates screen coordinates (@x, @y) to * coordinates relative to the actor. For example, it can be used to translate * screen events from global screen coordinates into actor-local coordinates. * * The conversion can fail, notably if the transform stack results in the * actor being projected on the screen as a mere line. * * The conversion should not be expected to be pixel-perfect due to the * nature of the operation. In general the error grows when the skewing * of the actor rectangle on screen increases. * * This function can be computationally intensive. * * This function only works when the allocation is up-to-date, i.e. inside of * the #ClutterActorClass.paint() implementation * * Return value: %TRUE if conversion was successful. * * Since: 0.6 */ gboolean clutter_actor_transform_stage_point (ClutterActor *self, gfloat x, gfloat y, gfloat *x_out, gfloat *y_out) { graphene_point3d_t v[4]; double ST[3][3]; double RQ[3][3]; int du, dv; double px, py; double det; float xf, yf, wf; ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); priv = self->priv; /* This implementation is based on the quad -> quad projection algorithm * described by Paul Heckbert in: * * http://www.cs.cmu.edu/~ph/texfund/texfund.pdf * * and the sample implementation at: * * http://www.cs.cmu.edu/~ph/src/texfund/ * * Our texture is a rectangle with origin [0, 0], so we are mapping from * quad to rectangle only, which significantly simplifies things. * Function calls have been unrolled. */ clutter_actor_get_abs_allocation_vertices (self, v); /* Keeping these as ints simplifies the multiplication (no significant * loss of precision here). */ du = ceilf (priv->allocation.x2 - priv->allocation.x1); dv = ceilf (priv->allocation.y2 - priv->allocation.y1); if (du == 0 || dv == 0) return FALSE; #define DET(a,b,c,d) (((a) * (d)) - ((b) * (c))) /* First, find mapping from unit uv square to xy quadrilateral; this * equivalent to the pmap_square_quad() functions in the sample * implementation, which we can simplify, since our target is always * a rectangle. */ px = v[0].x - v[1].x + v[3].x - v[2].x; py = v[0].y - v[1].y + v[3].y - v[2].y; if ((int) px == 0 && (int) py == 0) { /* affine transform */ RQ[0][0] = v[1].x - v[0].x; RQ[1][0] = v[3].x - v[1].x; RQ[2][0] = v[0].x; RQ[0][1] = v[1].y - v[0].y; RQ[1][1] = v[3].y - v[1].y; RQ[2][1] = v[0].y; RQ[0][2] = 0.0; RQ[1][2] = 0.0; RQ[2][2] = 1.0; } else { /* projective transform */ double dx1, dx2, dy1, dy2; dx1 = v[1].x - v[3].x; dx2 = v[2].x - v[3].x; dy1 = v[1].y - v[3].y; dy2 = v[2].y - v[3].y; det = DET (dx1, dx2, dy1, dy2); if (fabs (det) <= DBL_EPSILON) return FALSE; RQ[0][2] = DET (px, dx2, py, dy2) / det; RQ[1][2] = DET (dx1, px, dy1, py) / det; RQ[1][2] = DET (dx1, px, dy1, py) / det; RQ[2][2] = 1.0; RQ[0][0] = v[1].x - v[0].x + (RQ[0][2] * v[1].x); RQ[1][0] = v[2].x - v[0].x + (RQ[1][2] * v[2].x); RQ[2][0] = v[0].x; RQ[0][1] = v[1].y - v[0].y + (RQ[0][2] * v[1].y); RQ[1][1] = v[2].y - v[0].y + (RQ[1][2] * v[2].y); RQ[2][1] = v[0].y; } /* * Now combine with transform from our rectangle (u0,v0,u1,v1) to unit * square. Since our rectangle is based at 0,0 we only need to scale. */ RQ[0][0] /= du; RQ[1][0] /= dv; RQ[0][1] /= du; RQ[1][1] /= dv; RQ[0][2] /= du; RQ[1][2] /= dv; /* * Now RQ is transform from uv rectangle to xy quadrilateral; we need an * inverse of that. */ ST[0][0] = DET (RQ[1][1], RQ[1][2], RQ[2][1], RQ[2][2]); ST[1][0] = DET (RQ[1][2], RQ[1][0], RQ[2][2], RQ[2][0]); ST[2][0] = DET (RQ[1][0], RQ[1][1], RQ[2][0], RQ[2][1]); ST[0][1] = DET (RQ[2][1], RQ[2][2], RQ[0][1], RQ[0][2]); ST[1][1] = DET (RQ[2][2], RQ[2][0], RQ[0][2], RQ[0][0]); ST[2][1] = DET (RQ[2][0], RQ[2][1], RQ[0][0], RQ[0][1]); ST[0][2] = DET (RQ[0][1], RQ[0][2], RQ[1][1], RQ[1][2]); ST[1][2] = DET (RQ[0][2], RQ[0][0], RQ[1][2], RQ[1][0]); ST[2][2] = DET (RQ[0][0], RQ[0][1], RQ[1][0], RQ[1][1]); /* * Check the resulting matrix is OK. */ det = (RQ[0][0] * ST[0][0]) + (RQ[0][1] * ST[0][1]) + (RQ[0][2] * ST[0][2]); if (fabs (det) <= DBL_EPSILON) return FALSE; /* * Now transform our point with the ST matrix; the notional w * coordinate is 1, hence the last part is simply added. */ xf = x * ST[0][0] + y * ST[1][0] + ST[2][0]; yf = x * ST[0][1] + y * ST[1][1] + ST[2][1]; wf = x * ST[0][2] + y * ST[1][2] + ST[2][2]; if (x_out) *x_out = xf / wf; if (y_out) *y_out = yf / wf; #undef DET return TRUE; } /** * clutter_actor_is_rotated: * @self: a #ClutterActor * * Checks whether any rotation is applied to the actor. * * Return value: %TRUE if the actor is rotated. * * Since: 0.6 */ gboolean clutter_actor_is_rotated (ClutterActor *self) { const ClutterTransformInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); info = _clutter_actor_get_transform_info_or_defaults (self); if (info->rx_angle || info->ry_angle || info->rz_angle) return TRUE; return FALSE; } /** * clutter_actor_is_scaled: * @self: a #ClutterActor * * Checks whether the actor is scaled in either dimension. * * Return value: %TRUE if the actor is scaled. * * Since: 0.6 */ gboolean clutter_actor_is_scaled (ClutterActor *self) { const ClutterTransformInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); info = _clutter_actor_get_transform_info_or_defaults (self); if (info->scale_x != 1.0 || info->scale_y != 1.0) return TRUE; return FALSE; } ClutterActor * _clutter_actor_get_stage_internal (ClutterActor *actor) { while (actor && !CLUTTER_ACTOR_IS_TOPLEVEL (actor)) actor = actor->priv->parent; return actor; } /** * clutter_actor_get_stage: * @actor: a #ClutterActor * * Retrieves the #ClutterStage where @actor is contained. * * Return value: (transfer none) (type Clutter.Stage): the stage * containing the actor, or %NULL * * Since: 0.8 */ ClutterActor * clutter_actor_get_stage (ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); return _clutter_actor_get_stage_internal (actor); } /** * clutter_actor_allocate_available_size: * @self: a #ClutterActor * @x: the actor's X coordinate * @y: the actor's Y coordinate * @available_width: the maximum available width, or -1 to use the * actor's natural width * @available_height: the maximum available height, or -1 to use the * actor's natural height * @flags: flags controlling the allocation * * Allocates @self taking into account the #ClutterActor's * preferred size, but limiting it to the maximum available width * and height provided. * * This function will do the right thing when dealing with the * actor's request mode. * * The implementation of this function is equivalent to: * * |[ * if (request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) * { * clutter_actor_get_preferred_width (self, available_height, * &min_width, * &natural_width); * width = CLAMP (natural_width, min_width, available_width); * * clutter_actor_get_preferred_height (self, width, * &min_height, * &natural_height); * height = CLAMP (natural_height, min_height, available_height); * } * else if (request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) * { * clutter_actor_get_preferred_height (self, available_width, * &min_height, * &natural_height); * height = CLAMP (natural_height, min_height, available_height); * * clutter_actor_get_preferred_width (self, height, * &min_width, * &natural_width); * width = CLAMP (natural_width, min_width, available_width); * } * else if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE) * { * clutter_content_get_preferred_size (content, &natural_width, &natural_height); * * width = CLAMP (natural_width, 0, available_width); * height = CLAMP (natural_height, 0, available_height); * } * * box.x1 = x; box.y1 = y; * box.x2 = box.x1 + available_width; * box.y2 = box.y1 + available_height; * clutter_actor_allocate (self, &box, flags); * ]| * * This function can be used by fluid layout managers to allocate * an actor's preferred size without making it bigger than the area * available for the container. * * Since: 1.0 */ void clutter_actor_allocate_available_size (ClutterActor *self, gfloat x, gfloat y, gfloat available_width, gfloat available_height, ClutterAllocationFlags flags) { ClutterActorPrivate *priv; gfloat width, height; gfloat min_width, min_height; gfloat natural_width, natural_height; ClutterActorBox box; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; width = height = 0.0; switch (priv->request_mode) { case CLUTTER_REQUEST_HEIGHT_FOR_WIDTH: clutter_actor_get_preferred_width (self, available_height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); clutter_actor_get_preferred_height (self, width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); break; case CLUTTER_REQUEST_WIDTH_FOR_HEIGHT: clutter_actor_get_preferred_height (self, available_width, &min_height, &natural_height); height = CLAMP (natural_height, min_height, available_height); clutter_actor_get_preferred_width (self, height, &min_width, &natural_width); width = CLAMP (natural_width, min_width, available_width); break; case CLUTTER_REQUEST_CONTENT_SIZE: if (priv->content != NULL) { clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height); width = CLAMP (natural_width, 0, available_width); height = CLAMP (natural_height, 0, available_height); } break; } box.x1 = x; box.y1 = y; box.x2 = box.x1 + width; box.y2 = box.y1 + height; clutter_actor_allocate (self, &box, flags); } /** * clutter_actor_allocate_preferred_size: * @self: a #ClutterActor * @flags: flags controlling the allocation * * Allocates the natural size of @self. * * This function is a utility call for #ClutterActor implementations * that allocates the actor's preferred natural size. It can be used * by fixed layout managers (like #ClutterGroup or so called * 'composite actors') inside the ClutterActor::allocate * implementation to give each child exactly how much space it * requires, regardless of the size of the parent. * * This function is not meant to be used by applications. It is also * not meant to be used outside the implementation of the * #ClutterActorClass.allocate virtual function. * * Since: 0.8 */ void clutter_actor_allocate_preferred_size (ClutterActor *self, ClutterAllocationFlags flags) { gfloat actor_x, actor_y; gfloat natural_width, natural_height; ClutterActorBox actor_box; ClutterActorPrivate *priv; const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (priv->position_set) { info = _clutter_actor_get_layout_info_or_defaults (self); actor_x = info->fixed_pos.x; actor_y = info->fixed_pos.y; } else { actor_x = 0; actor_y = 0; } clutter_actor_get_preferred_size (self, NULL, NULL, &natural_width, &natural_height); actor_box.x1 = actor_x; actor_box.y1 = actor_y; actor_box.x2 = actor_box.x1 + natural_width; actor_box.y2 = actor_box.y1 + natural_height; clutter_actor_allocate (self, &actor_box, flags); } /** * clutter_actor_allocate_align_fill: * @self: a #ClutterActor * @box: a #ClutterActorBox, containing the available width and height * @x_align: the horizontal alignment, between 0 and 1 * @y_align: the vertical alignment, between 0 and 1 * @x_fill: whether the actor should fill horizontally * @y_fill: whether the actor should fill vertically * @flags: allocation flags to be passed to clutter_actor_allocate() * * Allocates @self by taking into consideration the available allocation * area; an alignment factor on either axis; and whether the actor should * fill the allocation on either axis. * * The @box should contain the available allocation width and height; * if the x1 and y1 members of #ClutterActorBox are not set to 0, the * allocation will be offset by their value. * * This function takes into consideration the geometry request specified by * the #ClutterActor:request-mode property, and the text direction. * * This function is useful for fluid layout managers using legacy alignment * flags. Newly written layout managers should use the #ClutterActor:x-align * and #ClutterActor:y-align properties, instead, and just call * clutter_actor_allocate() inside their #ClutterActorClass.allocate() * implementation. * * Since: 1.4 */ void clutter_actor_allocate_align_fill (ClutterActor *self, const ClutterActorBox *box, gdouble x_align, gdouble y_align, gboolean x_fill, gboolean y_fill, ClutterAllocationFlags flags) { ClutterActorPrivate *priv; ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT_ZERO; gfloat x_offset, y_offset; gfloat available_width, available_height; gfloat child_width = 0.f, child_height = 0.f; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); g_return_if_fail (x_align >= 0.0 && x_align <= 1.0); g_return_if_fail (y_align >= 0.0 && y_align <= 1.0); priv = self->priv; clutter_actor_box_get_origin (box, &x_offset, &y_offset); clutter_actor_box_get_size (box, &available_width, &available_height); if (available_width <= 0) available_width = 0.f; if (available_height <= 0) available_height = 0.f; allocation.x1 = x_offset; allocation.y1 = y_offset; if (available_width == 0.f && available_height == 0.f) goto out; if (x_fill) child_width = available_width; if (y_fill) child_height = available_height; /* if we are filling horizontally and vertically then we're done */ if (x_fill && y_fill) goto out; if (priv->request_mode == CLUTTER_REQUEST_HEIGHT_FOR_WIDTH) { gfloat min_width, natural_width; gfloat min_height, natural_height; if (!x_fill) { clutter_actor_get_preferred_width (self, available_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } if (!y_fill) { clutter_actor_get_preferred_height (self, child_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } } else if (priv->request_mode == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) { gfloat min_width, natural_width; gfloat min_height, natural_height; if (!y_fill) { clutter_actor_get_preferred_height (self, available_width, &min_height, &natural_height); child_height = CLAMP (natural_height, min_height, available_height); } if (!x_fill) { clutter_actor_get_preferred_width (self, child_height, &min_width, &natural_width); child_width = CLAMP (natural_width, min_width, available_width); } } else if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE && priv->content != NULL) { gfloat natural_width, natural_height; clutter_content_get_preferred_size (priv->content, &natural_width, &natural_height); if (!x_fill) child_width = CLAMP (natural_width, 0, available_width); if (!y_fill) child_height = CLAMP (natural_height, 0, available_height); } /* invert the horizontal alignment for RTL languages */ if (priv->text_direction == CLUTTER_TEXT_DIRECTION_RTL) x_align = 1.0 - x_align; if (!x_fill) allocation.x1 += ((available_width - child_width) * x_align); if (!y_fill) allocation.y1 += ((available_height - child_height) * y_align); out: allocation.x1 = floorf (allocation.x1); allocation.y1 = floorf (allocation.y1); allocation.x2 = ceilf (allocation.x1 + MAX (child_width, 0)); allocation.y2 = ceilf (allocation.y1 + MAX (child_height, 0)); clutter_actor_allocate (self, &allocation, flags); } /** * clutter_actor_grab_key_focus: * @self: a #ClutterActor * * Sets the key focus of the #ClutterStage including @self * to this #ClutterActor. * * Since: 1.0 */ void clutter_actor_grab_key_focus (ClutterActor *self) { ClutterActor *stage; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->has_key_focus) return; stage = _clutter_actor_get_stage_internal (self); if (stage != NULL) clutter_stage_set_key_focus (CLUTTER_STAGE (stage), self); } static void update_pango_context (ClutterBackend *backend, PangoContext *context) { ClutterSettings *settings; PangoFontDescription *font_desc; const cairo_font_options_t *font_options; gchar *font_name; PangoDirection pango_dir; gdouble resolution; settings = clutter_settings_get_default (); /* update the text direction */ if (clutter_get_default_text_direction () == CLUTTER_TEXT_DIRECTION_RTL) pango_dir = PANGO_DIRECTION_RTL; else pango_dir = PANGO_DIRECTION_LTR; pango_context_set_base_dir (context, pango_dir); g_object_get (settings, "font-name", &font_name, NULL); /* get the configuration for the PangoContext from the backend */ font_options = clutter_backend_get_font_options (backend); resolution = clutter_backend_get_resolution (backend); font_desc = pango_font_description_from_string (font_name); if (resolution < 0) resolution = 96.0; /* fall back */ pango_context_set_font_description (context, font_desc); pango_cairo_context_set_font_options (context, font_options); pango_cairo_context_set_resolution (context, resolution); pango_font_description_free (font_desc); g_free (font_name); } /** * clutter_actor_get_pango_context: * @self: a #ClutterActor * * Retrieves the #PangoContext for @self. The actor's #PangoContext * is already configured using the appropriate font map, resolution * and font options. * * Unlike clutter_actor_create_pango_context(), this context is owend * by the #ClutterActor and it will be updated each time the options * stored by the #ClutterBackend change. * * You can use the returned #PangoContext to create a #PangoLayout * and render text using cogl_pango_show_layout() to reuse the * glyphs cache also used by Clutter. * * Return value: (transfer none): the #PangoContext for a #ClutterActor. * The returned #PangoContext is owned by the actor and should not be * unreferenced by the application code * * Since: 1.0 */ PangoContext * clutter_actor_get_pango_context (ClutterActor *self) { ClutterActorPrivate *priv; ClutterBackend *backend = clutter_get_default_backend (); g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); priv = self->priv; if (G_UNLIKELY (priv->pango_context == NULL)) { priv->pango_context = clutter_actor_create_pango_context (self); priv->resolution_changed_id = g_signal_connect_object (backend, "resolution-changed", G_CALLBACK (update_pango_context), priv->pango_context, 0); priv->font_changed_id = g_signal_connect_object (backend, "font-changed", G_CALLBACK (update_pango_context), priv->pango_context, 0); } else update_pango_context (backend, priv->pango_context); return priv->pango_context; } /** * clutter_actor_create_pango_context: * @self: a #ClutterActor * * Creates a #PangoContext for the given actor. The #PangoContext * is already configured using the appropriate font map, resolution * and font options. * * See also clutter_actor_get_pango_context(). * * Return value: (transfer full): the newly created #PangoContext. * Use g_object_unref() on the returned value to deallocate its * resources * * Since: 1.0 */ PangoContext * clutter_actor_create_pango_context (ClutterActor *self) { CoglPangoFontMap *font_map; PangoContext *context; font_map = COGL_PANGO_FONT_MAP (clutter_get_font_map ()); context = cogl_pango_font_map_create_context (font_map); update_pango_context (clutter_get_default_backend (), context); pango_context_set_language (context, pango_language_get_default ()); return context; } /** * clutter_actor_create_pango_layout: * @self: a #ClutterActor * @text: (allow-none): the text to set on the #PangoLayout, or %NULL * * Creates a new #PangoLayout from the same #PangoContext used * by the #ClutterActor. The #PangoLayout is already configured * with the font map, resolution and font options, and the * given @text. * * If you want to keep around a #PangoLayout created by this * function you will have to connect to the #ClutterBackend::font-changed * and #ClutterBackend::resolution-changed signals, and call * pango_layout_context_changed() in response to them. * * Return value: (transfer full): the newly created #PangoLayout. * Use g_object_unref() when done * * Since: 1.0 */ PangoLayout * clutter_actor_create_pango_layout (ClutterActor *self, const gchar *text) { PangoContext *context; PangoLayout *layout; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); context = clutter_actor_get_pango_context (self); layout = pango_layout_new (context); if (text) pango_layout_set_text (layout, text, -1); return layout; } /** * clutter_actor_set_opacity_override: * @self: a #ClutterActor * @opacity: the override opacity value, or -1 to reset * * Allows overriding the calculated paint opacity (as returned by * clutter_actor_get_paint_opacity()). This is used internally by * ClutterClone and ClutterOffscreenEffect, and should be used by * actors that need to mimick those. * * In almost all cases this should not used by applications. * * Stability: unstable */ void clutter_actor_set_opacity_override (ClutterActor *self, gint opacity) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); /* ensure bounds */ if (opacity >= 0) opacity = CLAMP (opacity, 0, 255); else opacity = -1; self->priv->opacity_override = opacity; } /** * clutter_actor_get_opacity_override: * @self: a #ClutterActor * * See clutter_actor_set_opacity_override() * * Returns: the override value for the actor's opacity, or -1 if no override * is set. * * Since: 1.22 * * Stability: unstable */ gint clutter_actor_get_opacity_override (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), -1); return self->priv->opacity_override; } /** * clutter_actor_inhibit_culling: * @actor: a #ClutterActor * * Increases the culling inhibitor counter. Inhibiting culling * forces the actor to be painted even when outside the visible * bounds of the stage view. * * This is usually necessary when an actor is being painted on * another paint context. * * Pair with clutter_actor_uninhibit_culling() when the actor doesn't * need to be painted anymore. */ void clutter_actor_inhibit_culling (ClutterActor *actor) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = actor->priv; priv->inhibit_culling_counter++; _clutter_actor_set_enable_paint_unmapped (actor, TRUE); } /** * clutter_actor_uninhibit_culling: * @actor: a #ClutterActor * * Decreases the culling inhibitor counter. See clutter_actor_inhibit_culling() * for when inhibit culling is necessary. * * Calling this function without a matching call to * clutter_actor_inhibit_culling() is a programming error. */ void clutter_actor_uninhibit_culling (ClutterActor *actor) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = actor->priv; if (priv->inhibit_culling_counter == 0) { g_critical ("Unpaired call to clutter_actor_uninhibit_culling"); return; } priv->inhibit_culling_counter--; if (priv->inhibit_culling_counter == 0) _clutter_actor_set_enable_paint_unmapped (actor, FALSE); } /* Allows you to disable applying the actors model view transform during * a paint. Used by ClutterClone. */ void _clutter_actor_set_enable_model_view_transform (ClutterActor *self, gboolean enable) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); self->priv->enable_model_view_transform = enable; } void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, gboolean enable) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; priv->enable_paint_unmapped = enable; if (priv->enable_paint_unmapped) { /* Make sure that the parents of the widget are realized first; * otherwise checks in clutter_actor_update_map_state() will * fail. */ clutter_actor_realize (self); /* If the actor isn't ultimately connected to a toplevel, it can't be * realized or painted. */ if (CLUTTER_ACTOR_IS_REALIZED (self)) clutter_actor_update_map_state (self, MAP_STATE_MAKE_MAPPED); } else { clutter_actor_update_map_state (self, MAP_STATE_CHECK); } } static void clutter_anchor_coord_get_units (ClutterActor *self, const AnchorCoord *coord, gfloat *x, gfloat *y, gfloat *z) { if (coord->is_fractional) { gfloat actor_width, actor_height; clutter_actor_get_size (self, &actor_width, &actor_height); if (x) *x = actor_width * coord->v.fraction.x; if (y) *y = actor_height * coord->v.fraction.y; if (z) *z = 0; } else { if (x) *x = coord->v.units.x; if (y) *y = coord->v.units.y; if (z) *z = coord->v.units.z; } } static void clutter_anchor_coord_set_units (AnchorCoord *coord, gfloat x, gfloat y, gfloat z) { coord->is_fractional = FALSE; coord->v.units.x = x; coord->v.units.y = y; coord->v.units.z = z; } static ClutterGravity clutter_anchor_coord_get_gravity (const AnchorCoord *coord) { if (coord->is_fractional) { if (coord->v.fraction.x == 0.0) { if (coord->v.fraction.y == 0.0) return CLUTTER_GRAVITY_NORTH_WEST; else if (coord->v.fraction.y == 0.5) return CLUTTER_GRAVITY_WEST; else if (coord->v.fraction.y == 1.0) return CLUTTER_GRAVITY_SOUTH_WEST; else return CLUTTER_GRAVITY_NONE; } else if (coord->v.fraction.x == 0.5) { if (coord->v.fraction.y == 0.0) return CLUTTER_GRAVITY_NORTH; else if (coord->v.fraction.y == 0.5) return CLUTTER_GRAVITY_CENTER; else if (coord->v.fraction.y == 1.0) return CLUTTER_GRAVITY_SOUTH; else return CLUTTER_GRAVITY_NONE; } else if (coord->v.fraction.x == 1.0) { if (coord->v.fraction.y == 0.0) return CLUTTER_GRAVITY_NORTH_EAST; else if (coord->v.fraction.y == 0.5) return CLUTTER_GRAVITY_EAST; else if (coord->v.fraction.y == 1.0) return CLUTTER_GRAVITY_SOUTH_EAST; else return CLUTTER_GRAVITY_NONE; } else return CLUTTER_GRAVITY_NONE; } else return CLUTTER_GRAVITY_NONE; } static void clutter_anchor_coord_set_gravity (AnchorCoord *coord, ClutterGravity gravity) { switch (gravity) { case CLUTTER_GRAVITY_NORTH: coord->v.fraction.x = 0.5; coord->v.fraction.y = 0.0; break; case CLUTTER_GRAVITY_NORTH_EAST: coord->v.fraction.x = 1.0; coord->v.fraction.y = 0.0; break; case CLUTTER_GRAVITY_EAST: coord->v.fraction.x = 1.0; coord->v.fraction.y = 0.5; break; case CLUTTER_GRAVITY_SOUTH_EAST: coord->v.fraction.x = 1.0; coord->v.fraction.y = 1.0; break; case CLUTTER_GRAVITY_SOUTH: coord->v.fraction.x = 0.5; coord->v.fraction.y = 1.0; break; case CLUTTER_GRAVITY_SOUTH_WEST: coord->v.fraction.x = 0.0; coord->v.fraction.y = 1.0; break; case CLUTTER_GRAVITY_WEST: coord->v.fraction.x = 0.0; coord->v.fraction.y = 0.5; break; case CLUTTER_GRAVITY_NORTH_WEST: coord->v.fraction.x = 0.0; coord->v.fraction.y = 0.0; break; case CLUTTER_GRAVITY_CENTER: coord->v.fraction.x = 0.5; coord->v.fraction.y = 0.5; break; default: coord->v.fraction.x = 0.0; coord->v.fraction.y = 0.0; break; } coord->is_fractional = TRUE; } static gboolean clutter_anchor_coord_is_zero (const AnchorCoord *coord) { if (coord->is_fractional) return coord->v.fraction.x == 0.0 && coord->v.fraction.y == 0.0; else return (coord->v.units.x == 0.0 && coord->v.units.y == 0.0 && coord->v.units.z == 0.0); } /** * clutter_actor_get_flags: * @self: a #ClutterActor * * Retrieves the flags set on @self * * Return value: a bitwise or of #ClutterActorFlags or 0 * * Since: 1.0 */ ClutterActorFlags clutter_actor_get_flags (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); return self->flags; } /** * clutter_actor_set_flags: * @self: a #ClutterActor * @flags: the flags to set * * Sets @flags on @self * * This function will emit notifications for the changed properties * * Since: 1.0 */ void clutter_actor_set_flags (ClutterActor *self, ClutterActorFlags flags) { ClutterActorFlags old_flags; GObject *obj; gboolean was_reactive_set, reactive_set; gboolean was_realized_set, realized_set; gboolean was_mapped_set, mapped_set; gboolean was_visible_set, visible_set; g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->flags == flags) return; obj = G_OBJECT (self); g_object_ref (obj); g_object_freeze_notify (obj); old_flags = self->flags; was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0); was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0); was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0); was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0); self->flags |= flags; reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0); realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0); mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0); visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0); if (reactive_set != was_reactive_set) g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]); if (realized_set != was_realized_set) g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]); if (mapped_set != was_mapped_set) g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]); if (visible_set != was_visible_set) g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]); g_object_thaw_notify (obj); g_object_unref (obj); } /** * clutter_actor_unset_flags: * @self: a #ClutterActor * @flags: the flags to unset * * Unsets @flags on @self * * This function will emit notifications for the changed properties * * Since: 1.0 */ void clutter_actor_unset_flags (ClutterActor *self, ClutterActorFlags flags) { ClutterActorFlags old_flags; GObject *obj; gboolean was_reactive_set, reactive_set; gboolean was_realized_set, realized_set; gboolean was_mapped_set, mapped_set; gboolean was_visible_set, visible_set; g_return_if_fail (CLUTTER_IS_ACTOR (self)); obj = G_OBJECT (self); g_object_freeze_notify (obj); old_flags = self->flags; was_reactive_set = ((old_flags & CLUTTER_ACTOR_REACTIVE) != 0); was_realized_set = ((old_flags & CLUTTER_ACTOR_REALIZED) != 0); was_mapped_set = ((old_flags & CLUTTER_ACTOR_MAPPED) != 0); was_visible_set = ((old_flags & CLUTTER_ACTOR_VISIBLE) != 0); self->flags &= ~flags; if (self->flags == old_flags) return; reactive_set = ((self->flags & CLUTTER_ACTOR_REACTIVE) != 0); realized_set = ((self->flags & CLUTTER_ACTOR_REALIZED) != 0); mapped_set = ((self->flags & CLUTTER_ACTOR_MAPPED) != 0); visible_set = ((self->flags & CLUTTER_ACTOR_VISIBLE) != 0); if (reactive_set != was_reactive_set) g_object_notify_by_pspec (obj, obj_props[PROP_REACTIVE]); if (realized_set != was_realized_set) g_object_notify_by_pspec (obj, obj_props[PROP_REALIZED]); if (mapped_set != was_mapped_set) g_object_notify_by_pspec (obj, obj_props[PROP_MAPPED]); if (visible_set != was_visible_set) g_object_notify_by_pspec (obj, obj_props[PROP_VISIBLE]); g_object_thaw_notify (obj); } static void clutter_actor_set_transform_internal (ClutterActor *self, const ClutterMatrix *transform) { ClutterTransformInfo *info; gboolean was_set; GObject *obj; obj = G_OBJECT (self); info = _clutter_actor_get_transform_info (self); was_set = info->transform_set; info->transform = *transform; info->transform_set = !cogl_matrix_is_identity (&info->transform); self->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM]); if (was_set != info->transform_set) g_object_notify_by_pspec (obj, obj_props[PROP_TRANSFORM_SET]); } /** * clutter_actor_set_transform: * @self: a #ClutterActor * @transform: (allow-none): a #ClutterMatrix, or %NULL to * unset a custom transformation * * Overrides the transformations of a #ClutterActor with a custom * matrix, which will be applied relative to the origin of the * actor's allocation and to the actor's pivot point. * * The #ClutterActor:transform property is animatable. * * Since: 1.12 */ void clutter_actor_set_transform (ClutterActor *self, const ClutterMatrix *transform) { const ClutterTransformInfo *info; ClutterMatrix new_transform; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); if (transform != NULL) clutter_matrix_init_from_matrix (&new_transform, transform); else clutter_matrix_init_identity (&new_transform); _clutter_actor_create_transition (self, obj_props[PROP_TRANSFORM], &info->transform, &new_transform); } /** * clutter_actor_get_transform: * @self: a #ClutterActor * @transform: (out caller-allocates): a #ClutterMatrix * * Retrieves the current transformation matrix of a #ClutterActor. * * Since: 1.12 */ void clutter_actor_get_transform (ClutterActor *self, ClutterMatrix *transform) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (transform != NULL); cogl_matrix_init_identity (transform); _clutter_actor_apply_modelview_transform (self, transform); } void _clutter_actor_set_in_clone_paint (ClutterActor *self, gboolean is_in_clone_paint) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); self->priv->in_clone_paint = is_in_clone_paint; } /** * clutter_actor_is_in_clone_paint: * @self: a #ClutterActor * * Checks whether @self is being currently painted by a #ClutterClone * * This function is useful only inside the ::paint virtual function * implementations or within handlers for the #ClutterActor::paint * signal * * This function should not be used by applications * * Return value: %TRUE if the #ClutterActor is currently being painted * by a #ClutterClone, and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_actor_is_in_clone_paint (ClutterActor *self) { ClutterActor *parent; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (self->priv->in_clone_paint) return TRUE; if (self->priv->in_cloned_branch == 0) return FALSE; parent = self->priv->parent; while (parent != NULL) { if (parent->priv->in_cloned_branch == 0) break; if (parent->priv->in_clone_paint) return TRUE; parent = parent->priv->parent; } return FALSE; } gboolean clutter_actor_has_damage (ClutterActor *actor) { return actor->priv->is_dirty; } static gboolean set_direction_recursive (ClutterActor *actor, gpointer user_data) { ClutterTextDirection text_dir = GPOINTER_TO_INT (user_data); clutter_actor_set_text_direction (actor, text_dir); return TRUE; } /** * clutter_actor_set_text_direction: * @self: a #ClutterActor * @text_dir: the text direction for @self * * Sets the #ClutterTextDirection for an actor * * The passed text direction must not be %CLUTTER_TEXT_DIRECTION_DEFAULT * * If @self implements #ClutterContainer then this function will recurse * inside all the children of @self (including the internal ones). * * Composite actors not implementing #ClutterContainer, or actors requiring * special handling when the text direction changes, should connect to * the #GObject::notify signal for the #ClutterActor:text-direction property * * Since: 1.2 */ void clutter_actor_set_text_direction (ClutterActor *self, ClutterTextDirection text_dir) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (text_dir != CLUTTER_TEXT_DIRECTION_DEFAULT); priv = self->priv; if (priv->text_direction != text_dir) { priv->text_direction = text_dir; /* we need to emit the notify::text-direction first, so that * the sub-classes can catch that and do specific handling of * the text direction; see clutter_text_direction_changed_cb() * inside clutter-text.c */ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT_DIRECTION]); _clutter_actor_foreach_child (self, set_direction_recursive, GINT_TO_POINTER (text_dir)); clutter_actor_queue_relayout (self); } } void _clutter_actor_set_has_pointer (ClutterActor *self, gboolean has_pointer) { ClutterActorPrivate *priv = self->priv; if (priv->has_pointer != has_pointer) { priv->has_pointer = has_pointer; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_POINTER]); } } void _clutter_actor_set_has_key_focus (ClutterActor *self, gboolean has_key_focus) { ClutterActorPrivate *priv = self->priv; if (priv->has_key_focus != has_key_focus) { priv->has_key_focus = has_key_focus; if (has_key_focus) g_signal_emit (self, actor_signals[KEY_FOCUS_IN], 0); else g_signal_emit (self, actor_signals[KEY_FOCUS_OUT], 0); } } /** * clutter_actor_get_text_direction: * @self: a #ClutterActor * * Retrieves the value set using clutter_actor_set_text_direction() * * If no text direction has been previously set, the default text * direction, as returned by clutter_get_default_text_direction(), will * be returned instead * * Return value: the #ClutterTextDirection for the actor * * Since: 1.2 */ ClutterTextDirection clutter_actor_get_text_direction (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_TEXT_DIRECTION_LTR); priv = self->priv; /* if no direction has been set yet use the default */ if (priv->text_direction == CLUTTER_TEXT_DIRECTION_DEFAULT) priv->text_direction = clutter_get_default_text_direction (); return priv->text_direction; } /** * clutter_actor_has_pointer: * @self: a #ClutterActor * * Checks whether an actor contains the pointer of a * #ClutterInputDevice * * Return value: %TRUE if the actor contains the pointer, and * %FALSE otherwise * * Since: 1.2 */ gboolean clutter_actor_has_pointer (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return self->priv->has_pointer; } /** * clutter_actor_has_allocation: * @self: a #ClutterActor * * Checks if the actor has an up-to-date allocation assigned to * it. This means that the actor should have an allocation: it's * visible and has a parent. It also means that there is no * outstanding relayout request in progress for the actor or its * children (There might be other outstanding layout requests in * progress that will cause the actor to get a new allocation * when the stage is laid out, however). * * If this function returns %FALSE, then the actor will normally * be allocated before it is next drawn on the screen. * * Return value: %TRUE if the actor has an up-to-date allocation * * Since: 1.4 */ gboolean clutter_actor_has_allocation (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); priv = self->priv; return priv->parent != NULL && CLUTTER_ACTOR_IS_VISIBLE (self) && !priv->needs_allocation; } /** * clutter_actor_add_action: * @self: a #ClutterActor * @action: a #ClutterAction * * Adds @action to the list of actions applied to @self * * A #ClutterAction can only belong to one actor at a time * * The #ClutterActor will hold a reference on @action until either * clutter_actor_remove_action() or clutter_actor_clear_actions() * is called * * Since: 1.4 */ void clutter_actor_add_action (ClutterActor *self, ClutterAction *action) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTION (action)); priv = self->priv; if (priv->actions == NULL) { priv->actions = g_object_new (CLUTTER_TYPE_META_GROUP, NULL); priv->actions->actor = self; } _clutter_meta_group_add_meta (priv->actions, CLUTTER_ACTOR_META (action)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]); } /** * clutter_actor_add_action_with_name: * @self: a #ClutterActor * @name: the name to set on the action * @action: a #ClutterAction * * A convenience function for setting the name of a #ClutterAction * while adding it to the list of actions applied to @self * * This function is the logical equivalent of: * * |[ * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name); * clutter_actor_add_action (self, action); * ]| * * Since: 1.4 */ void clutter_actor_add_action_with_name (ClutterActor *self, const gchar *name, ClutterAction *action) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); g_return_if_fail (CLUTTER_IS_ACTION (action)); clutter_actor_meta_set_name (CLUTTER_ACTOR_META (action), name); clutter_actor_add_action (self, action); } /** * clutter_actor_remove_action: * @self: a #ClutterActor * @action: a #ClutterAction * * Removes @action from the list of actions applied to @self * * The reference held by @self on the #ClutterAction will be released * * Since: 1.4 */ void clutter_actor_remove_action (ClutterActor *self, ClutterAction *action) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_ACTION (action)); priv = self->priv; if (priv->actions == NULL) return; _clutter_meta_group_remove_meta (priv->actions, CLUTTER_ACTOR_META (action)); if (_clutter_meta_group_peek_metas (priv->actions) == NULL) g_clear_object (&priv->actions); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]); } /** * clutter_actor_remove_action_by_name: * @self: a #ClutterActor * @name: the name of the action to remove * * Removes the #ClutterAction with the given name from the list * of actions applied to @self * * Since: 1.4 */ void clutter_actor_remove_action_by_name (ClutterActor *self, const gchar *name) { ClutterActorPrivate *priv; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); priv = self->priv; if (priv->actions == NULL) return; meta = _clutter_meta_group_get_meta (priv->actions, name); if (meta == NULL) return; _clutter_meta_group_remove_meta (priv->actions, meta); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIONS]); } /** * clutter_actor_get_actions: * @self: a #ClutterActor * * Retrieves the list of actions applied to @self * * Return value: (transfer container) (element-type Clutter.Action): a copy * of the list of #ClutterActions. The contents of the list are * owned by the #ClutterActor. Use g_list_free() to free the resources * allocated by the returned #GList * * Since: 1.4 */ GList * clutter_actor_get_actions (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); if (self->priv->actions == NULL) return NULL; return _clutter_meta_group_get_metas_no_internal (self->priv->actions); } /** * clutter_actor_get_action: * @self: a #ClutterActor * @name: the name of the action to retrieve * * Retrieves the #ClutterAction with the given name in the list * of actions applied to @self * * Return value: (transfer none): a #ClutterAction for the given * name, or %NULL. The returned #ClutterAction is owned by the * actor and it should not be unreferenced directly * * Since: 1.4 */ ClutterAction * clutter_actor_get_action (ClutterActor *self, const gchar *name) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (name != NULL, NULL); if (self->priv->actions == NULL) return NULL; return CLUTTER_ACTION (_clutter_meta_group_get_meta (self->priv->actions, name)); } /** * clutter_actor_clear_actions: * @self: a #ClutterActor * * Clears the list of actions applied to @self * * Since: 1.4 */ void clutter_actor_clear_actions (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->actions == NULL) return; _clutter_meta_group_clear_metas_no_internal (self->priv->actions); } /** * clutter_actor_add_constraint: * @self: a #ClutterActor * @constraint: a #ClutterConstraint * * Adds @constraint to the list of #ClutterConstraints applied * to @self * * The #ClutterActor will hold a reference on the @constraint until * either clutter_actor_remove_constraint() or * clutter_actor_clear_constraints() is called. * * Since: 1.4 */ void clutter_actor_add_constraint (ClutterActor *self, ClutterConstraint *constraint) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint)); priv = self->priv; if (priv->constraints == NULL) { priv->constraints = g_object_new (CLUTTER_TYPE_META_GROUP, NULL); priv->constraints->actor = self; } _clutter_meta_group_add_meta (priv->constraints, CLUTTER_ACTOR_META (constraint)); clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]); } /** * clutter_actor_add_constraint_with_name: * @self: a #ClutterActor * @name: the name to set on the constraint * @constraint: a #ClutterConstraint * * A convenience function for setting the name of a #ClutterConstraint * while adding it to the list of constraints applied to @self * * This function is the logical equivalent of: * * |[ * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name); * clutter_actor_add_constraint (self, constraint); * ]| * * Since: 1.4 */ void clutter_actor_add_constraint_with_name (ClutterActor *self, const gchar *name, ClutterConstraint *constraint) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint)); clutter_actor_meta_set_name (CLUTTER_ACTOR_META (constraint), name); clutter_actor_add_constraint (self, constraint); } /** * clutter_actor_remove_constraint: * @self: a #ClutterActor * @constraint: a #ClutterConstraint * * Removes @constraint from the list of constraints applied to @self * * The reference held by @self on the #ClutterConstraint will be released * * Since: 1.4 */ void clutter_actor_remove_constraint (ClutterActor *self, ClutterConstraint *constraint) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint)); priv = self->priv; if (priv->constraints == NULL) return; _clutter_meta_group_remove_meta (priv->constraints, CLUTTER_ACTOR_META (constraint)); if (_clutter_meta_group_peek_metas (priv->constraints) == NULL) g_clear_object (&priv->constraints); clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONSTRAINTS]); } /** * clutter_actor_remove_constraint_by_name: * @self: a #ClutterActor * @name: the name of the constraint to remove * * Removes the #ClutterConstraint with the given name from the list * of constraints applied to @self * * Since: 1.4 */ void clutter_actor_remove_constraint_by_name (ClutterActor *self, const gchar *name) { ClutterActorPrivate *priv; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); priv = self->priv; if (priv->constraints == NULL) return; meta = _clutter_meta_group_get_meta (priv->constraints, name); if (meta == NULL) return; _clutter_meta_group_remove_meta (priv->constraints, meta); clutter_actor_queue_relayout (self); } /** * clutter_actor_get_constraints: * @self: a #ClutterActor * * Retrieves the list of constraints applied to @self * * Return value: (transfer container) (element-type Clutter.Constraint): a copy * of the list of #ClutterConstraints. The contents of the list are * owned by the #ClutterActor. Use g_list_free() to free the resources * allocated by the returned #GList * * Since: 1.4 */ GList * clutter_actor_get_constraints (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); if (self->priv->constraints == NULL) return NULL; return _clutter_meta_group_get_metas_no_internal (self->priv->constraints); } /** * clutter_actor_get_constraint: * @self: a #ClutterActor * @name: the name of the constraint to retrieve * * Retrieves the #ClutterConstraint with the given name in the list * of constraints applied to @self * * Return value: (transfer none): a #ClutterConstraint for the given * name, or %NULL. The returned #ClutterConstraint is owned by the * actor and it should not be unreferenced directly * * Since: 1.4 */ ClutterConstraint * clutter_actor_get_constraint (ClutterActor *self, const gchar *name) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (name != NULL, NULL); if (self->priv->constraints == NULL) return NULL; return CLUTTER_CONSTRAINT (_clutter_meta_group_get_meta (self->priv->constraints, name)); } /** * clutter_actor_clear_constraints: * @self: a #ClutterActor * * Clears the list of constraints applied to @self * * Since: 1.4 */ void clutter_actor_clear_constraints (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->constraints == NULL) return; _clutter_meta_group_clear_metas_no_internal (self->priv->constraints); clutter_actor_queue_relayout (self); } /** * clutter_actor_set_clip_to_allocation: * @self: a #ClutterActor * @clip_set: %TRUE to apply a clip tracking the allocation * * Sets whether @self should be clipped to the same size as its * allocation * * Since: 1.4 */ void clutter_actor_set_clip_to_allocation (ClutterActor *self, gboolean clip_set) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); clip_set = !!clip_set; priv = self->priv; if (priv->clip_to_allocation != clip_set) { priv->clip_to_allocation = clip_set; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CLIP_TO_ALLOCATION]); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_HAS_CLIP]); } } /** * clutter_actor_get_clip_to_allocation: * @self: a #ClutterActor * * Retrieves the value set using clutter_actor_set_clip_to_allocation() * * Return value: %TRUE if the #ClutterActor is clipped to its allocation * * Since: 1.4 */ gboolean clutter_actor_get_clip_to_allocation (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return self->priv->clip_to_allocation; } /** * clutter_actor_add_effect: * @self: a #ClutterActor * @effect: a #ClutterEffect * * Adds @effect to the list of #ClutterEffects applied to @self * * The #ClutterActor will hold a reference on the @effect until either * clutter_actor_remove_effect() or clutter_actor_clear_effects() is * called. * * Since: 1.4 */ void clutter_actor_add_effect (ClutterActor *self, ClutterEffect *effect) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_EFFECT (effect)); _clutter_actor_add_effect_internal (self, effect); clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]); } /** * clutter_actor_add_effect_with_name: * @self: a #ClutterActor * @name: the name to set on the effect * @effect: a #ClutterEffect * * A convenience function for setting the name of a #ClutterEffect * while adding it to the list of effectss applied to @self * * This function is the logical equivalent of: * * |[ * clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name); * clutter_actor_add_effect (self, effect); * ]| * * Since: 1.4 */ void clutter_actor_add_effect_with_name (ClutterActor *self, const gchar *name, ClutterEffect *effect) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); g_return_if_fail (CLUTTER_IS_EFFECT (effect)); clutter_actor_meta_set_name (CLUTTER_ACTOR_META (effect), name); clutter_actor_add_effect (self, effect); } /** * clutter_actor_remove_effect: * @self: a #ClutterActor * @effect: a #ClutterEffect * * Removes @effect from the list of effects applied to @self * * The reference held by @self on the #ClutterEffect will be released * * Since: 1.4 */ void clutter_actor_remove_effect (ClutterActor *self, ClutterEffect *effect) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (CLUTTER_IS_EFFECT (effect)); _clutter_actor_remove_effect_internal (self, effect); clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EFFECT]); } /** * clutter_actor_remove_effect_by_name: * @self: a #ClutterActor * @name: the name of the effect to remove * * Removes the #ClutterEffect with the given name from the list * of effects applied to @self * * Since: 1.4 */ void clutter_actor_remove_effect_by_name (ClutterActor *self, const gchar *name) { ClutterActorPrivate *priv; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); priv = self->priv; if (priv->effects == NULL) return; meta = _clutter_meta_group_get_meta (priv->effects, name); if (meta == NULL) return; clutter_actor_remove_effect (self, CLUTTER_EFFECT (meta)); } /** * clutter_actor_get_effects: * @self: a #ClutterActor * * Retrieves the #ClutterEffects applied on @self, if any * * Return value: (transfer container) (element-type Clutter.Effect): a list * of #ClutterEffects, or %NULL. The elements of the returned * list are owned by Clutter and they should not be freed. You should * free the returned list using g_list_free() when done * * Since: 1.4 */ GList * clutter_actor_get_effects (ClutterActor *self) { ClutterActorPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); priv = self->priv; if (priv->effects == NULL) return NULL; return _clutter_meta_group_get_metas_no_internal (priv->effects); } /** * clutter_actor_get_effect: * @self: a #ClutterActor * @name: the name of the effect to retrieve * * Retrieves the #ClutterEffect with the given name in the list * of effects applied to @self * * Return value: (transfer none): a #ClutterEffect for the given * name, or %NULL. The returned #ClutterEffect is owned by the * actor and it should not be unreferenced directly * * Since: 1.4 */ ClutterEffect * clutter_actor_get_effect (ClutterActor *self, const gchar *name) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (name != NULL, NULL); if (self->priv->effects == NULL) return NULL; return CLUTTER_EFFECT (_clutter_meta_group_get_meta (self->priv->effects, name)); } /** * clutter_actor_clear_effects: * @self: a #ClutterActor * * Clears the list of effects applied to @self * * Since: 1.4 */ void clutter_actor_clear_effects (ClutterActor *self) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->effects == NULL) return; _clutter_meta_group_clear_metas_no_internal (self->priv->effects); clutter_actor_queue_redraw (self); } /** * clutter_actor_has_key_focus: * @self: a #ClutterActor * * Checks whether @self is the #ClutterActor that has key focus * * Return value: %TRUE if the actor has key focus, and %FALSE otherwise * * Since: 1.4 */ gboolean clutter_actor_has_key_focus (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return self->priv->has_key_focus; } static gboolean _clutter_actor_get_paint_volume_real (ClutterActor *self, ClutterPaintVolume *pv) { ClutterActorPrivate *priv = self->priv; /* Actors are only expected to report a valid paint volume * while they have a valid allocation. */ if (G_UNLIKELY (priv->needs_allocation)) { CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): " "Actor needs allocation", _clutter_actor_get_debug_name (self)); return FALSE; } /* Check if there are any handlers connected to the paint * signal. If there are then all bets are off for what the paint * volume for this actor might possibly be! * * XXX: It's expected that this is going to end up being quite a * costly check to have to do here, but we haven't come up with * another solution that can reliably catch paint signal handlers at * the right time to either avoid artefacts due to invalid stage * clipping or due to incorrect culling. * * Previously we checked in clutter_actor_paint(), but at that time * we may already be using a stage clip that could be derived from * an invalid paint-volume. We used to try and handle that by * queuing a follow up, unclipped, redraw but still the previous * checking wasn't enough to catch invalid volumes involved in * culling (considering that containers may derive their volume from * children that haven't yet been painted) * * Longer term, improved solutions could be: * - Disallow painting in the paint signal, only allow using it * for tracking when paints happen. We can add another API that * allows monkey patching the paint of arbitrary actors but in a * more controlled way and that also supports modifying the * paint-volume. * - If we could be notified somehow when signal handlers are * connected we wouldn't have to poll for handlers like this. * * XXX:2.0 - Remove when we remove the paint signal */ if (g_signal_has_handler_pending (self, actor_signals[PAINT], 0, TRUE)) { CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): " "Actor has \"paint\" signal handlers", _clutter_actor_get_debug_name (self)); return FALSE; } _clutter_paint_volume_init_static (pv, self); if (!CLUTTER_ACTOR_GET_CLASS (self)->get_paint_volume (self, pv)) { clutter_paint_volume_free (pv); CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): " "Actor failed to report a volume", _clutter_actor_get_debug_name (self)); return FALSE; } /* since effects can modify the paint volume, we allow them to actually * do this by making get_paint_volume() "context sensitive" */ if (priv->effects != NULL) { if (priv->current_effect != NULL) { const GList *effects, *l; /* if we are being called from within the paint sequence of * an actor, get the paint volume up to the current effect */ effects = _clutter_meta_group_peek_metas (priv->effects); for (l = effects; l != NULL && l->data != priv->current_effect; l = l->next) { if (!_clutter_effect_modify_paint_volume (l->data, pv)) { clutter_paint_volume_free (pv); CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): " "Effect (%s) failed to report a volume", _clutter_actor_get_debug_name (self), _clutter_actor_meta_get_debug_name (l->data)); return FALSE; } } } else { const GList *effects, *l; /* otherwise, get the cumulative volume */ effects = _clutter_meta_group_peek_metas (priv->effects); for (l = effects; l != NULL; l = l->next) if (!_clutter_effect_modify_paint_volume (l->data, pv)) { clutter_paint_volume_free (pv); CLUTTER_NOTE (CLIPPING, "Bail from get_paint_volume (%s): " "Effect (%s) failed to report a volume", _clutter_actor_get_debug_name (self), _clutter_actor_meta_get_debug_name (l->data)); return FALSE; } } } return TRUE; } static gboolean _clutter_actor_has_active_paint_volume_override_effects (ClutterActor *self) { const GList *l; if (self->priv->effects == NULL) return FALSE; /* We just need to all effects current effect to see * if anyone wants to override the paint volume. If so, then * we need to recompute, since the paint volume returned can * change from call to call. */ for (l = _clutter_meta_group_peek_metas (self->priv->effects); l != NULL; l = l->next) { ClutterEffect *effect = l->data; if (clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect)) && _clutter_effect_has_custom_paint_volume (effect)) return TRUE; } return FALSE; } /* The public clutter_actor_get_paint_volume API returns a const * pointer since we return a pointer directly to the cached * PaintVolume associated with the actor and don't want the user to * inadvertently modify it, but for internal uses we sometimes need * access to the same PaintVolume but need to apply some book-keeping * modifications to it so we don't want a const pointer. */ static ClutterPaintVolume * _clutter_actor_get_paint_volume_mutable (ClutterActor *self) { gboolean has_paint_volume_override_effects; ClutterActorPrivate *priv; priv = self->priv; has_paint_volume_override_effects = _clutter_actor_has_active_paint_volume_override_effects (self); if (priv->paint_volume_valid) { /* If effects are applied, the actor paint volume * needs to be recomputed on each paint, since those * paint volumes could change over the duration of the * effect. * * We also need to update the paint volume if we went * from having effects to not having effects on the last * paint volume update. */ if (!priv->needs_paint_volume_update && priv->current_effect == NULL && !has_paint_volume_override_effects && !priv->had_effects_on_last_paint_volume_update) return &priv->paint_volume; clutter_paint_volume_free (&priv->paint_volume); } priv->had_effects_on_last_paint_volume_update = has_paint_volume_override_effects; if (_clutter_actor_get_paint_volume_real (self, &priv->paint_volume)) { priv->paint_volume_valid = TRUE; priv->needs_paint_volume_update = FALSE; return &priv->paint_volume; } else { priv->paint_volume_valid = FALSE; return NULL; } } /** * clutter_actor_get_paint_volume: * @self: a #ClutterActor * * Retrieves the paint volume of the passed #ClutterActor, or %NULL * when a paint volume can't be determined. * * The paint volume is defined as the 3D space occupied by an actor * when being painted. * * This function will call the #ClutterActorClass.get_paint_volume() * virtual function of the #ClutterActor class. Sub-classes of #ClutterActor * should not usually care about overriding the default implementation, * unless they are, for instance: painting outside their allocation, or * actors with a depth factor (not in terms of #ClutterActor:depth but real * 3D depth). * * Note: 2D actors overriding #ClutterActorClass.get_paint_volume() * should ensure that their volume has a depth of 0. (This will be true * as long as you don't call clutter_paint_volume_set_depth().) * * Return value: (transfer none): a pointer to a #ClutterPaintVolume, * or %NULL if no volume could be determined. The returned pointer * is not guaranteed to be valid across multiple frames; if you want * to keep it, you will need to copy it using clutter_paint_volume_copy(). * * Since: 1.6 */ const ClutterPaintVolume * clutter_actor_get_paint_volume (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return _clutter_actor_get_paint_volume_mutable (self); } /** * clutter_actor_get_transformed_paint_volume: * @self: a #ClutterActor * @relative_to_ancestor: A #ClutterActor that is an ancestor of @self * (or %NULL for the stage) * * Retrieves the 3D paint volume of an actor like * clutter_actor_get_paint_volume() does (Please refer to the * documentation of clutter_actor_get_paint_volume() for more * details.) and it additionally transforms the paint volume into the * coordinate space of @relative_to_ancestor. (Or the stage if %NULL * is passed for @relative_to_ancestor) * * This can be used by containers that base their paint volume on * the volume of their children. Such containers can query the * transformed paint volume of all of its children and union them * together using clutter_paint_volume_union(). * * Return value: (transfer none): a pointer to a #ClutterPaintVolume, * or %NULL if no volume could be determined. The returned pointer is * not guaranteed to be valid across multiple frames; if you wish to * keep it, you will have to copy it using clutter_paint_volume_copy(). * * Since: 1.6 */ const ClutterPaintVolume * clutter_actor_get_transformed_paint_volume (ClutterActor *self, ClutterActor *relative_to_ancestor) { const ClutterPaintVolume *volume; ClutterActor *stage; ClutterPaintVolume *transformed_volume; stage = _clutter_actor_get_stage_internal (self); if (G_UNLIKELY (stage == NULL)) return NULL; if (relative_to_ancestor == NULL) relative_to_ancestor = stage; volume = clutter_actor_get_paint_volume (self); if (volume == NULL) return NULL; transformed_volume = _clutter_stage_paint_volume_stack_allocate (CLUTTER_STAGE (stage)); _clutter_paint_volume_copy_static (volume, transformed_volume); _clutter_paint_volume_transform_relative (transformed_volume, relative_to_ancestor); return transformed_volume; } /** * clutter_actor_get_paint_box: * @self: a #ClutterActor * @box: (out): return location for a #ClutterActorBox * * Retrieves the paint volume of the passed #ClutterActor, and * transforms it into a 2D bounding box in stage coordinates. * * This function is useful to determine the on screen area occupied by * the actor. The box is only an approximation and may often be * considerably larger due to the optimizations used to calculate the * box. The box is never smaller though, so it can reliably be used * for culling. * * There are times when a 2D paint box can't be determined, e.g. * because the actor isn't yet parented under a stage or because * the actor is unable to determine a paint volume. * * Return value: %TRUE if a 2D paint box could be determined, else * %FALSE. * * Since: 1.6 */ gboolean clutter_actor_get_paint_box (ClutterActor *self, ClutterActorBox *box) { ClutterActor *stage; ClutterPaintVolume *pv; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (box != NULL, FALSE); stage = _clutter_actor_get_stage_internal (self); if (G_UNLIKELY (!stage)) return FALSE; pv = _clutter_actor_get_paint_volume_mutable (self); if (G_UNLIKELY (!pv)) return FALSE; _clutter_paint_volume_get_stage_paint_box (pv, CLUTTER_STAGE (stage), box); return TRUE; } static gboolean _clutter_actor_get_resource_scale_for_rect (ClutterActor *self, graphene_rect_t *bounding_rect, float *resource_scale) { ClutterActor *stage; float max_scale = 0; stage = _clutter_actor_get_stage_internal (self); if (!stage) return FALSE; if (!_clutter_stage_get_max_view_scale_factor_for_rect (CLUTTER_STAGE (stage), bounding_rect, &max_scale)) return FALSE; *resource_scale = max_scale; return TRUE; } static gboolean _clutter_actor_compute_resource_scale (ClutterActor *self, float *resource_scale) { graphene_rect_t bounding_rect; ClutterActorPrivate *priv = self->priv; if (CLUTTER_ACTOR_IN_DESTRUCTION (self) || CLUTTER_ACTOR_IN_PREF_SIZE (self) || !clutter_actor_is_mapped (self)) { return FALSE; } clutter_actor_get_transformed_position (self, &bounding_rect.origin.x, &bounding_rect.origin.y); clutter_actor_get_transformed_size (self, &bounding_rect.size.width, &bounding_rect.size.height); if (bounding_rect.size.width == 0.0 || bounding_rect.size.height == 0.0 || !_clutter_actor_get_resource_scale_for_rect (self, &bounding_rect, resource_scale)) { if (priv->parent) { gboolean in_clone_paint; gboolean was_parent_in_clone_paint; gboolean was_parent_unmapped; gboolean was_parent_paint_unmapped; gboolean ret; in_clone_paint = clutter_actor_is_in_clone_paint (self); was_parent_unmapped = !clutter_actor_is_mapped (priv->parent); was_parent_in_clone_paint = clutter_actor_is_in_clone_paint (priv->parent); was_parent_paint_unmapped = priv->parent->priv->enable_paint_unmapped; if (in_clone_paint && was_parent_unmapped) { _clutter_actor_set_in_clone_paint (priv->parent, TRUE); _clutter_actor_set_enable_paint_unmapped (priv->parent, TRUE); } ret = _clutter_actor_compute_resource_scale (priv->parent, resource_scale); if (in_clone_paint && was_parent_unmapped) { _clutter_actor_set_in_clone_paint (priv->parent, was_parent_in_clone_paint); _clutter_actor_set_enable_paint_unmapped (priv->parent, was_parent_paint_unmapped); } return ret; } else { return FALSE; } } return TRUE; } static ClutterActorTraverseVisitFlags queue_update_resource_scale_cb (ClutterActor *actor, int depth, void *user_data) { actor->priv->needs_compute_resource_scale = TRUE; return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } void _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *self) { _clutter_actor_traverse (self, CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST, queue_update_resource_scale_cb, NULL, NULL); } static gboolean clutter_actor_update_resource_scale (ClutterActor *self) { ClutterActorPrivate *priv; float resource_scale; float old_resource_scale; priv = self->priv; g_return_val_if_fail (priv->needs_compute_resource_scale, FALSE); old_resource_scale = priv->resource_scale; priv->resource_scale = -1.0f; if (_clutter_actor_compute_resource_scale (self, &resource_scale)) { priv->resource_scale = resource_scale; priv->needs_compute_resource_scale = FALSE; return fabsf (old_resource_scale - resource_scale) > FLT_EPSILON; } return FALSE; } static void clutter_actor_ensure_resource_scale (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; if (!priv->needs_compute_resource_scale) return; if (clutter_actor_update_resource_scale (self)) g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_RESOURCE_SCALE]); } gboolean _clutter_actor_get_real_resource_scale (ClutterActor *self, gfloat *resource_scale) { ClutterActorPrivate *priv = self->priv; clutter_actor_ensure_resource_scale (self); if (!priv->needs_compute_resource_scale) { *resource_scale = priv->resource_scale; return TRUE; } *resource_scale = -1.0f; return FALSE; } /** * clutter_actor_get_resource_scale: * @self: A #ClutterActor * @resource_scale: (out): return location for the resource scale * * Retrieves the resource scale for this actor, if available. * * The resource scale refers to the scale the actor should use for its resources. * For example if an actor draws a a picture of size 100 x 100 in the stage * coordinate space, it should use a texture of twice the size (i.e. 200 x 200) * if the resource scale is 2. * * The resource scale is determined by calculating the highest #ClutterStageView * scale the actor will get painted on. * * Returns: TRUE if resource scale is set for the actor, otherwise FALSE */ gboolean clutter_actor_get_resource_scale (ClutterActor *self, gfloat *resource_scale) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); g_return_val_if_fail (resource_scale != NULL, FALSE); if (_clutter_actor_get_real_resource_scale (self, resource_scale)) { *resource_scale = ceilf (*resource_scale); return TRUE; } return FALSE; } /** * clutter_actor_has_overlaps: * @self: A #ClutterActor * * Asks the actor's implementation whether it may contain overlapping * primitives. * * For example; Clutter may use this to determine whether the painting * should be redirected to an offscreen buffer to correctly implement * the opacity property. * * Custom actors can override the default response by implementing the * #ClutterActorClass.has_overlaps() virtual function. See * clutter_actor_set_offscreen_redirect() for more information. * * Return value: %TRUE if the actor may have overlapping primitives, and * %FALSE otherwise * * Since: 1.8 */ gboolean clutter_actor_has_overlaps (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), TRUE); return CLUTTER_ACTOR_GET_CLASS (self)->has_overlaps (self); } /** * clutter_actor_has_effects: * @self: A #ClutterActor * * Returns whether the actor has any effects applied. * * Return value: %TRUE if the actor has any effects, * %FALSE otherwise * * Since: 1.10 */ gboolean clutter_actor_has_effects (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (self->priv->effects == NULL) return FALSE; return _clutter_meta_group_has_metas_no_internal (self->priv->effects); } /** * clutter_actor_has_constraints: * @self: A #ClutterActor * * Returns whether the actor has any constraints applied. * * Return value: %TRUE if the actor has any constraints, * %FALSE otherwise * * Since: 1.10 */ gboolean clutter_actor_has_constraints (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (self->priv->constraints == NULL) return FALSE; return _clutter_meta_group_has_metas_no_internal (self->priv->constraints); } /** * clutter_actor_has_actions: * @self: A #ClutterActor * * Returns whether the actor has any actions applied. * * Return value: %TRUE if the actor has any actions, * %FALSE otherwise * * Since: 1.10 */ gboolean clutter_actor_has_actions (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (self->priv->actions == NULL) return FALSE; return _clutter_meta_group_has_metas_no_internal (self->priv->actions); } /** * clutter_actor_get_n_children: * @self: a #ClutterActor * * Retrieves the number of children of @self. * * Return value: the number of children of an actor * * Since: 1.10 */ gint clutter_actor_get_n_children (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); return self->priv->n_children; } /** * clutter_actor_get_child_at_index: * @self: a #ClutterActor * @index_: the position in the list of children * * Retrieves the actor at the given @index_ inside the list of * children of @self. * * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_actor_get_child_at_index (ClutterActor *self, gint index_) { ClutterActor *iter; int i; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (index_ <= self->priv->n_children, NULL); for (iter = self->priv->first_child, i = 0; iter != NULL && i < index_; iter = iter->priv->next_sibling, i += 1) ; return iter; } /*< private > * _clutter_actor_foreach_child: * @actor: The actor whos children you want to iterate * @callback: The function to call for each child * @user_data: Private data to pass to @callback * * Calls a given @callback once for each child of the specified @actor and * passing the @user_data pointer each time. * * Return value: returns %TRUE if all children were iterated, else * %FALSE if a callback broke out of iteration early. */ gboolean _clutter_actor_foreach_child (ClutterActor *self, ClutterForeachCallback callback, gpointer user_data) { ClutterActor *iter; gboolean cont; if (self->priv->first_child == NULL) return TRUE; cont = TRUE; iter = self->priv->first_child; /* we use this form so that it's safe to change the children * list while iterating it */ while (cont && iter != NULL) { ClutterActor *next = iter->priv->next_sibling; cont = callback (iter, user_data); iter = next; } return cont; } #if 0 /* For debugging purposes this gives us a simple way to print out * the scenegraph e.g in gdb using: * [| * _clutter_actor_traverse (stage, * 0, * clutter_debug_print_actor_cb, * NULL, * NULL); * |] */ static ClutterActorTraverseVisitFlags clutter_debug_print_actor_cb (ClutterActor *actor, int depth, void *user_data) { g_print ("%*s%s:%p\n", depth * 2, "", _clutter_actor_get_debug_name (actor), actor); return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } #endif static void _clutter_actor_traverse_breadth (ClutterActor *actor, ClutterTraverseCallback callback, gpointer user_data) { GQueue *queue = g_queue_new (); ClutterActor dummy; int current_depth = 0; g_queue_push_tail (queue, actor); g_queue_push_tail (queue, &dummy); /* use to delimit depth changes */ while ((actor = g_queue_pop_head (queue))) { ClutterActorTraverseVisitFlags flags; if (actor == &dummy) { current_depth++; g_queue_push_tail (queue, &dummy); continue; } flags = callback (actor, current_depth, user_data); if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK) break; else if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN)) { ClutterActor *iter; for (iter = actor->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { g_queue_push_tail (queue, iter); } } } g_queue_free (queue); } static ClutterActorTraverseVisitFlags _clutter_actor_traverse_depth (ClutterActor *actor, ClutterTraverseCallback before_children_callback, ClutterTraverseCallback after_children_callback, int current_depth, gpointer user_data) { ClutterActorTraverseVisitFlags flags; flags = before_children_callback (actor, current_depth, user_data); if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK) return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK; if (!(flags & CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN)) { ClutterActor *iter; for (iter = actor->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) { flags = _clutter_actor_traverse_depth (iter, before_children_callback, after_children_callback, current_depth + 1, user_data); if (flags & CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK) return CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK; } } if (after_children_callback) return after_children_callback (actor, current_depth, user_data); else return CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE; } /* _clutter_actor_traverse: * @actor: The actor to start traversing the graph from * @flags: These flags may affect how the traversal is done * @before_children_callback: A function to call before visiting the * children of the current actor. * @after_children_callback: A function to call after visiting the * children of the current actor. (Ignored if * %CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST is passed to @flags.) * @user_data: The private data to pass to the callbacks * * Traverses the scenegraph starting at the specified @actor and * descending through all its children and its children's children. * For each actor traversed @before_children_callback and * @after_children_callback are called with the specified * @user_data, before and after visiting that actor's children. * * The callbacks can return flags that affect the ongoing traversal * such as by skipping over an actors children or bailing out of * any further traversing. */ void _clutter_actor_traverse (ClutterActor *actor, ClutterActorTraverseFlags flags, ClutterTraverseCallback before_children_callback, ClutterTraverseCallback after_children_callback, gpointer user_data) { if (flags & CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST) _clutter_actor_traverse_breadth (actor, before_children_callback, user_data); else /* DEPTH_FIRST */ _clutter_actor_traverse_depth (actor, before_children_callback, after_children_callback, 0, /* start depth */ user_data); } static void on_layout_manager_changed (ClutterLayoutManager *manager, ClutterActor *self) { clutter_actor_queue_relayout (self); } /** * clutter_actor_set_layout_manager: * @self: a #ClutterActor * @manager: (allow-none): a #ClutterLayoutManager, or %NULL to unset it * * Sets the #ClutterLayoutManager delegate object that will be used to * lay out the children of @self. * * The #ClutterActor will take a reference on the passed @manager which * will be released either when the layout manager is removed, or when * the actor is destroyed. * * Since: 1.10 */ void clutter_actor_set_layout_manager (ClutterActor *self, ClutterLayoutManager *manager) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (manager == NULL || CLUTTER_IS_LAYOUT_MANAGER (manager)); priv = self->priv; if (priv->layout_manager != NULL) { g_signal_handlers_disconnect_by_func (priv->layout_manager, G_CALLBACK (on_layout_manager_changed), self); clutter_layout_manager_set_container (priv->layout_manager, NULL); g_clear_object (&priv->layout_manager); } priv->layout_manager = manager; if (priv->layout_manager != NULL) { g_object_ref_sink (priv->layout_manager); clutter_layout_manager_set_container (priv->layout_manager, CLUTTER_CONTAINER (self)); g_signal_connect (priv->layout_manager, "layout-changed", G_CALLBACK (on_layout_manager_changed), self); } clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LAYOUT_MANAGER]); } /** * clutter_actor_get_layout_manager: * @self: a #ClutterActor * * Retrieves the #ClutterLayoutManager used by @self. * * Return value: (transfer none): a pointer to the #ClutterLayoutManager, * or %NULL * * Since: 1.10 */ ClutterLayoutManager * clutter_actor_get_layout_manager (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->layout_manager; } static const ClutterLayoutInfo default_layout_info = { GRAPHENE_POINT_INIT_ZERO, /* fixed-pos */ { 0, 0, 0, 0 }, /* margin */ CLUTTER_ACTOR_ALIGN_FILL, /* x-align */ CLUTTER_ACTOR_ALIGN_FILL, /* y-align */ FALSE, FALSE, /* expand */ GRAPHENE_SIZE_INIT_ZERO, /* minimum */ GRAPHENE_SIZE_INIT_ZERO, /* natural */ }; static void layout_info_free (gpointer data) { if (G_LIKELY (data != NULL)) g_slice_free (ClutterLayoutInfo, data); } /*< private > * _clutter_actor_peek_layout_info: * @self: a #ClutterActor * * Retrieves a pointer to the ClutterLayoutInfo structure. * * If the actor does not have a ClutterLayoutInfo associated to it, %NULL is returned. * * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure */ ClutterLayoutInfo * _clutter_actor_peek_layout_info (ClutterActor *self) { return g_object_get_qdata (G_OBJECT (self), quark_actor_layout_info); } /*< private > * _clutter_actor_get_layout_info: * @self: a #ClutterActor * * Retrieves a pointer to the ClutterLayoutInfo structure. * * If the actor does not have a ClutterLayoutInfo associated to it, one * will be created and initialized to the default values. * * This function should be used for setters. * * For getters, you should use _clutter_actor_get_layout_info_or_defaults() * instead. * * Return value: (transfer none): a pointer to the ClutterLayoutInfo structure */ ClutterLayoutInfo * _clutter_actor_get_layout_info (ClutterActor *self) { ClutterLayoutInfo *retval; retval = _clutter_actor_peek_layout_info (self); if (retval == NULL) { retval = g_slice_new (ClutterLayoutInfo); *retval = default_layout_info; g_object_set_qdata_full (G_OBJECT (self), quark_actor_layout_info, retval, layout_info_free); } return retval; } /*< private > * _clutter_actor_get_layout_info_or_defaults: * @self: a #ClutterActor * * Retrieves the ClutterLayoutInfo structure associated to an actor. * * If the actor does not have a ClutterLayoutInfo structure associated to it, * then the default structure will be returned. * * This function should only be used for getters. * * Return value: a const pointer to the ClutterLayoutInfo structure */ const ClutterLayoutInfo * _clutter_actor_get_layout_info_or_defaults (ClutterActor *self) { const ClutterLayoutInfo *info; info = _clutter_actor_peek_layout_info (self); if (info == NULL) return &default_layout_info; return info; } /** * clutter_actor_set_x_align: * @self: a #ClutterActor * @x_align: the horizontal alignment policy * * Sets the horizontal alignment policy of a #ClutterActor, in case the * actor received extra horizontal space. * * See also the #ClutterActor:x-align property. * * Since: 1.10 */ void clutter_actor_set_x_align (ClutterActor *self, ClutterActorAlign x_align) { ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_layout_info (self); if (info->x_align != x_align) { info->x_align = x_align; clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_ALIGN]); } } /** * clutter_actor_get_x_align: * @self: a #ClutterActor * * Retrieves the horizontal alignment policy set using * clutter_actor_set_x_align(). * * Return value: the horizontal alignment policy. * * Since: 1.10 */ ClutterActorAlign clutter_actor_get_x_align (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL); return _clutter_actor_get_layout_info_or_defaults (self)->x_align; } /** * clutter_actor_set_y_align: * @self: a #ClutterActor * @y_align: the vertical alignment policy * * Sets the vertical alignment policy of a #ClutterActor, in case the * actor received extra vertical space. * * See also the #ClutterActor:y-align property. * * Since: 1.10 */ void clutter_actor_set_y_align (ClutterActor *self, ClutterActorAlign y_align) { ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_layout_info (self); if (info->y_align != y_align) { info->y_align = y_align; clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_ALIGN]); } } /** * clutter_actor_get_y_align: * @self: a #ClutterActor * * Retrieves the vertical alignment policy set using * clutter_actor_set_y_align(). * * Return value: the vertical alignment policy. * * Since: 1.10 */ ClutterActorAlign clutter_actor_get_y_align (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_ACTOR_ALIGN_FILL); return _clutter_actor_get_layout_info_or_defaults (self)->y_align; } static inline void clutter_actor_set_margin_internal (ClutterActor *self, gfloat margin, GParamSpec *pspec) { ClutterLayoutInfo *info; info = _clutter_actor_get_layout_info (self); if (pspec == obj_props[PROP_MARGIN_TOP]) info->margin.top = margin; else if (pspec == obj_props[PROP_MARGIN_RIGHT]) info->margin.right = margin; else if (pspec == obj_props[PROP_MARGIN_BOTTOM]) info->margin.bottom = margin; else info->margin.left = margin; clutter_actor_queue_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), pspec); } /** * clutter_actor_set_margin: * @self: a #ClutterActor * @margin: a #ClutterMargin * * Sets all the components of the margin of a #ClutterActor. * * Since: 1.10 */ void clutter_actor_set_margin (ClutterActor *self, const ClutterMargin *margin) { ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin != NULL); info = _clutter_actor_get_layout_info (self); if (info->margin.top != margin->top) clutter_actor_set_margin_top (self, margin->top); if (info->margin.right != margin->right) clutter_actor_set_margin_right (self, margin->right); if (info->margin.bottom != margin->bottom) clutter_actor_set_margin_bottom (self, margin->bottom); if (info->margin.left != margin->left) clutter_actor_set_margin_left (self, margin->left); } /** * clutter_actor_get_margin: * @self: a #ClutterActor * @margin: (out caller-allocates): return location for a #ClutterMargin * * Retrieves all the components of the margin of a #ClutterActor. * * Since: 1.10 */ void clutter_actor_get_margin (ClutterActor *self, ClutterMargin *margin) { const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin != NULL); info = _clutter_actor_get_layout_info_or_defaults (self); *margin = info->margin; } /** * clutter_actor_set_margin_top: * @self: a #ClutterActor * @margin: the top margin * * Sets the margin from the top of a #ClutterActor. * * The #ClutterActor:margin-top property is animatable. * * Since: 1.10 */ void clutter_actor_set_margin_top (ClutterActor *self, gfloat margin) { const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin >= 0.f); info = _clutter_actor_get_layout_info_or_defaults (self); if (info->margin.top == margin) return; _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_TOP], info->margin.top, margin); } /** * clutter_actor_get_margin_top: * @self: a #ClutterActor * * Retrieves the top margin of a #ClutterActor. * * Return value: the top margin * * Since: 1.10 */ gfloat clutter_actor_get_margin_top (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_layout_info_or_defaults (self)->margin.top; } /** * clutter_actor_set_margin_bottom: * @self: a #ClutterActor * @margin: the bottom margin * * Sets the margin from the bottom of a #ClutterActor. * * The #ClutterActor:margin-bottom property is animatable. * * Since: 1.10 */ void clutter_actor_set_margin_bottom (ClutterActor *self, gfloat margin) { const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin >= 0.f); info = _clutter_actor_get_layout_info_or_defaults (self); if (info->margin.bottom == margin) return; _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_BOTTOM], info->margin.bottom, margin); } /** * clutter_actor_get_margin_bottom: * @self: a #ClutterActor * * Retrieves the bottom margin of a #ClutterActor. * * Return value: the bottom margin * * Since: 1.10 */ gfloat clutter_actor_get_margin_bottom (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_layout_info_or_defaults (self)->margin.bottom; } /** * clutter_actor_set_margin_left: * @self: a #ClutterActor * @margin: the left margin * * Sets the margin from the left of a #ClutterActor. * * The #ClutterActor:margin-left property is animatable. * * Since: 1.10 */ void clutter_actor_set_margin_left (ClutterActor *self, gfloat margin) { const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin >= 0.f); info = _clutter_actor_get_layout_info_or_defaults (self); if (info->margin.left == margin) return; _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_LEFT], info->margin.left, margin); } /** * clutter_actor_get_margin_left: * @self: a #ClutterActor * * Retrieves the left margin of a #ClutterActor. * * Return value: the left margin * * Since: 1.10 */ gfloat clutter_actor_get_margin_left (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_layout_info_or_defaults (self)->margin.left; } /** * clutter_actor_set_margin_right: * @self: a #ClutterActor * @margin: the right margin * * Sets the margin from the right of a #ClutterActor. * * The #ClutterActor:margin-right property is animatable. * * Since: 1.10 */ void clutter_actor_set_margin_right (ClutterActor *self, gfloat margin) { const ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (margin >= 0.f); info = _clutter_actor_get_layout_info_or_defaults (self); if (info->margin.right == margin) return; _clutter_actor_create_transition (self, obj_props[PROP_MARGIN_RIGHT], info->margin.right, margin); } /** * clutter_actor_get_margin_right: * @self: a #ClutterActor * * Retrieves the right margin of a #ClutterActor. * * Return value: the right margin * * Since: 1.10 */ gfloat clutter_actor_get_margin_right (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0.f); return _clutter_actor_get_layout_info_or_defaults (self)->margin.right; } static inline void clutter_actor_set_background_color_internal (ClutterActor *self, const ClutterColor *color) { ClutterActorPrivate *priv = self->priv; GObject *obj; if (priv->bg_color_set && clutter_color_equal (color, &priv->bg_color)) return; obj = G_OBJECT (self); priv->bg_color = *color; priv->bg_color_set = TRUE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]); g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR]); } /** * clutter_actor_set_background_color: * @self: a #ClutterActor * @color: (allow-none): a #ClutterColor, or %NULL to unset a previously * set color * * Sets the background color of a #ClutterActor. * * The background color will be used to cover the whole allocation of the * actor. The default background color of an actor is transparent. * * To check whether an actor has a background color, you can use the * #ClutterActor:background-color-set actor property. * * The #ClutterActor:background-color property is animatable. * * Since: 1.10 */ void clutter_actor_set_background_color (ClutterActor *self, const ClutterColor *color) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (color == NULL) { GObject *obj = G_OBJECT (self); priv->bg_color_set = FALSE; clutter_actor_queue_redraw (self); g_object_notify_by_pspec (obj, obj_props[PROP_BACKGROUND_COLOR_SET]); } else _clutter_actor_create_transition (self, obj_props[PROP_BACKGROUND_COLOR], &priv->bg_color, color); } /** * clutter_actor_get_background_color: * @self: a #ClutterActor * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the color set using clutter_actor_set_background_color(). * * Since: 1.10 */ void clutter_actor_get_background_color (ClutterActor *self, ClutterColor *color) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (color != NULL); *color = self->priv->bg_color; } /** * clutter_actor_get_previous_sibling: * @self: a #ClutterActor * * Retrieves the sibling of @self that comes before it in the list * of children of @self's parent. * * The returned pointer is only valid until the scene graph changes; it * is not safe to modify the list of children of @self while iterating * it. * * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_actor_get_previous_sibling (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->prev_sibling; } /** * clutter_actor_get_next_sibling: * @self: a #ClutterActor * * Retrieves the sibling of @self that comes after it in the list * of children of @self's parent. * * The returned pointer is only valid until the scene graph changes; it * is not safe to modify the list of children of @self while iterating * it. * * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_actor_get_next_sibling (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->next_sibling; } /** * clutter_actor_get_first_child: * @self: a #ClutterActor * * Retrieves the first child of @self. * * The returned pointer is only valid until the scene graph changes; it * is not safe to modify the list of children of @self while iterating * it. * * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_actor_get_first_child (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->first_child; } /** * clutter_actor_get_last_child: * @self: a #ClutterActor * * Retrieves the last child of @self. * * The returned pointer is only valid until the scene graph changes; it * is not safe to modify the list of children of @self while iterating * it. * * Return value: (transfer none): a pointer to a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_actor_get_last_child (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->last_child; } /* easy way to have properly named fields instead of the dummy ones * we use in the public structure */ typedef struct _RealActorIter { ClutterActor *root; /* dummy1 */ ClutterActor *current; /* dummy2 */ gpointer padding_1; /* dummy3 */ gint age; /* dummy4 */ gpointer padding_2; /* dummy5 */ } RealActorIter; /** * clutter_actor_iter_init: * @iter: a #ClutterActorIter * @root: a #ClutterActor * * Initializes a #ClutterActorIter, which can then be used to iterate * efficiently over a section of the scene graph, and associates it * with @root. * * Modifying the scene graph section that contains @root will invalidate * the iterator. * * |[ * ClutterActorIter iter; * ClutterActor *child; * * clutter_actor_iter_init (&iter, container); * while (clutter_actor_iter_next (&iter, &child)) * { * // do something with child * } * ]| * * Since: 1.10 */ void clutter_actor_iter_init (ClutterActorIter *iter, ClutterActor *root) { RealActorIter *ri = (RealActorIter *) iter; g_return_if_fail (iter != NULL); g_return_if_fail (CLUTTER_IS_ACTOR (root)); ri->root = root; ri->current = NULL; ri->age = root->priv->age; } /** * clutter_actor_iter_is_valid: * @iter: a #ClutterActorIter * * Checks whether a #ClutterActorIter is still valid. * * An iterator is considered valid if it has been initialized, and * if the #ClutterActor that it refers to hasn't been modified after * the initialization. * * Return value: %TRUE if the iterator is valid, and %FALSE otherwise * * Since: 1.12 */ gboolean clutter_actor_iter_is_valid (const ClutterActorIter *iter) { RealActorIter *ri = (RealActorIter *) iter; g_return_val_if_fail (iter != NULL, FALSE); if (ri->root == NULL) return FALSE; return ri->root->priv->age == ri->age; } /** * clutter_actor_iter_next: * @iter: a #ClutterActorIter * @child: (out) (transfer none): return location for a #ClutterActor * * Advances the @iter and retrieves the next child of the root #ClutterActor * that was used to initialize the #ClutterActorIterator. * * If the iterator can advance, this function returns %TRUE and sets the * @child argument. * * If the iterator cannot advance, this function returns %FALSE, and * the contents of @child are undefined. * * Return value: %TRUE if the iterator could advance, and %FALSE otherwise. * * Since: 1.10 */ gboolean clutter_actor_iter_next (ClutterActorIter *iter, ClutterActor **child) { RealActorIter *ri = (RealActorIter *) iter; g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (ri->root != NULL, FALSE); #ifndef G_DISABLE_ASSERT g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE); #endif if (ri->current == NULL) ri->current = ri->root->priv->first_child; else ri->current = ri->current->priv->next_sibling; if (child != NULL) *child = ri->current; return ri->current != NULL; } /** * clutter_actor_iter_prev: * @iter: a #ClutterActorIter * @child: (out) (transfer none): return location for a #ClutterActor * * Advances the @iter and retrieves the previous child of the root * #ClutterActor that was used to initialize the #ClutterActorIterator. * * If the iterator can advance, this function returns %TRUE and sets the * @child argument. * * If the iterator cannot advance, this function returns %FALSE, and * the contents of @child are undefined. * * Return value: %TRUE if the iterator could advance, and %FALSE otherwise. * * Since: 1.10 */ gboolean clutter_actor_iter_prev (ClutterActorIter *iter, ClutterActor **child) { RealActorIter *ri = (RealActorIter *) iter; g_return_val_if_fail (iter != NULL, FALSE); g_return_val_if_fail (ri->root != NULL, FALSE); #ifndef G_DISABLE_ASSERT g_return_val_if_fail (ri->age == ri->root->priv->age, FALSE); #endif if (ri->current == NULL) ri->current = ri->root->priv->last_child; else ri->current = ri->current->priv->prev_sibling; if (child != NULL) *child = ri->current; return ri->current != NULL; } /** * clutter_actor_iter_remove: * @iter: a #ClutterActorIter * * Safely removes the #ClutterActor currently pointer to by the iterator * from its parent. * * This function can only be called after clutter_actor_iter_next() or * clutter_actor_iter_prev() returned %TRUE, and cannot be called more * than once for the same actor. * * This function will call clutter_actor_remove_child() internally. * * Since: 1.10 */ void clutter_actor_iter_remove (ClutterActorIter *iter) { RealActorIter *ri = (RealActorIter *) iter; ClutterActor *cur; g_return_if_fail (iter != NULL); g_return_if_fail (ri->root != NULL); #ifndef G_DISABLE_ASSERT g_return_if_fail (ri->age == ri->root->priv->age); #endif g_return_if_fail (ri->current != NULL); cur = ri->current; if (cur != NULL) { ri->current = cur->priv->prev_sibling; clutter_actor_remove_child_internal (ri->root, cur, REMOVE_CHILD_DEFAULT_FLAGS); ri->age += 1; } } /** * clutter_actor_iter_destroy: * @iter: a #ClutterActorIter * * Safely destroys the #ClutterActor currently pointer to by the iterator * from its parent. * * This function can only be called after clutter_actor_iter_next() or * clutter_actor_iter_prev() returned %TRUE, and cannot be called more * than once for the same actor. * * This function will call clutter_actor_destroy() internally. * * Since: 1.10 */ void clutter_actor_iter_destroy (ClutterActorIter *iter) { RealActorIter *ri = (RealActorIter *) iter; ClutterActor *cur; g_return_if_fail (iter != NULL); g_return_if_fail (ri->root != NULL); #ifndef G_DISABLE_ASSERT g_return_if_fail (ri->age == ri->root->priv->age); #endif g_return_if_fail (ri->current != NULL); cur = ri->current; if (cur != NULL) { ri->current = cur->priv->prev_sibling; clutter_actor_destroy (cur); ri->age += 1; } } static const ClutterAnimationInfo default_animation_info = { NULL, /* states */ NULL, /* cur_state */ NULL, /* transitions */ }; static void clutter_animation_info_free (gpointer data) { if (data != NULL) { ClutterAnimationInfo *info = data; if (info->transitions != NULL) g_hash_table_unref (info->transitions); if (info->states != NULL) g_array_unref (info->states); g_slice_free (ClutterAnimationInfo, info); } } const ClutterAnimationInfo * _clutter_actor_get_animation_info_or_defaults (ClutterActor *self) { const ClutterAnimationInfo *res; GObject *obj = G_OBJECT (self); res = g_object_get_qdata (obj, quark_actor_animation_info); if (res != NULL) return res; return &default_animation_info; } ClutterAnimationInfo * _clutter_actor_get_animation_info (ClutterActor *self) { GObject *obj = G_OBJECT (self); ClutterAnimationInfo *res; res = g_object_get_qdata (obj, quark_actor_animation_info); if (res == NULL) { res = g_slice_new (ClutterAnimationInfo); *res = default_animation_info; g_object_set_qdata_full (obj, quark_actor_animation_info, res, clutter_animation_info_free); } return res; } static void transition_closure_free (gpointer data) { if (G_LIKELY (data != NULL)) { TransitionClosure *clos = data; ClutterTimeline *timeline; timeline = CLUTTER_TIMELINE (clos->transition); /* we disconnect the signal handler before stopping the timeline, * so that we don't end up inside on_transition_stopped() from * a call to g_hash_table_remove(). */ g_clear_signal_handler (&clos->completed_id, clos->transition); if (clutter_timeline_is_playing (timeline)) clutter_timeline_stop (timeline); else if (clutter_timeline_get_delay (timeline) > 0) clutter_timeline_cancel_delay (timeline); /* remove the reference added in add_transition_internal() */ g_object_unref (clos->transition); g_free (clos->name); g_slice_free (TransitionClosure, clos); } } static void on_transition_stopped (ClutterTransition *transition, gboolean is_finished, TransitionClosure *clos) { ClutterActor *actor = clos->actor; ClutterAnimationInfo *info; GQuark t_quark; gchar *t_name; if (clos->name == NULL) return; /* reset the caches used by animations */ clutter_actor_store_content_box (actor, NULL); info = _clutter_actor_get_animation_info (actor); /* we need copies because we emit the signal after the * TransitionClosure data structure has been freed */ t_quark = g_quark_from_string (clos->name); t_name = g_strdup (clos->name); if (clutter_transition_get_remove_on_complete (transition)) { /* this is safe, because the timeline has now stopped, * so we won't recurse; the reference on the Animatable * will be dropped by the ::stopped signal closure in * ClutterTransition, which is RUN_LAST, and thus will * be called after this handler */ g_hash_table_remove (info->transitions, clos->name); } /* we emit the ::transition-stopped after removing the * transition, so that we can chain up new transitions * without interfering with the one that just finished */ g_signal_emit (actor, actor_signals[TRANSITION_STOPPED], t_quark, t_name, is_finished); g_free (t_name); /* if it's the last transition then we clean up */ if (g_hash_table_size (info->transitions) == 0) { g_hash_table_unref (info->transitions); info->transitions = NULL; CLUTTER_NOTE (ANIMATION, "Transitions for '%s' completed", _clutter_actor_get_debug_name (actor)); g_signal_emit (actor, actor_signals[TRANSITIONS_COMPLETED], 0); } } static void clutter_actor_add_transition_internal (ClutterActor *self, const gchar *name, ClutterTransition *transition) { ClutterTimeline *timeline; TransitionClosure *clos; ClutterAnimationInfo *info; info = _clutter_actor_get_animation_info (self); if (info->transitions == NULL) info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, transition_closure_free); if (g_hash_table_lookup (info->transitions, name) != NULL) { g_warning ("A transition with name '%s' already exists for " "the actor '%s'", name, _clutter_actor_get_debug_name (self)); return; } clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self)); timeline = CLUTTER_TIMELINE (transition); clos = g_slice_new (TransitionClosure); clos->actor = self; clos->transition = g_object_ref (transition); clos->name = g_strdup (name); clos->completed_id = g_signal_connect (timeline, "stopped", G_CALLBACK (on_transition_stopped), clos); CLUTTER_NOTE (ANIMATION, "Adding transition '%s' [%p] to actor '%s'", clos->name, clos->transition, _clutter_actor_get_debug_name (self)); g_hash_table_insert (info->transitions, clos->name, clos); clutter_timeline_start (timeline); } static gboolean should_skip_implicit_transition (ClutterActor *self, GParamSpec *pspec) { ClutterActorPrivate *priv = self->priv; const ClutterAnimationInfo *info; /* this function is called from _clutter_actor_create_transition() which * calls _clutter_actor_get_animation_info() first, so we're guaranteed * to have the correct ClutterAnimationInfo pointer */ info = _clutter_actor_get_animation_info_or_defaults (self); /* if the easing state has a non-zero duration we always want an * implicit transition to occur */ if (info->cur_state->easing_duration == 0) return TRUE; /* on the other hand, if the actor hasn't been allocated yet, we want to * skip all transitions on the :allocation, to avoid actors "flying in" * into their new position and size */ if (pspec == obj_props[PROP_ALLOCATION] && priv->needs_allocation) return TRUE; /* if the actor is not mapped and is not part of a branch of the scene * graph that is being cloned, then we always skip implicit transitions * on the account of the fact that the actor is not going to be visible * when those transitions happen */ if (!CLUTTER_ACTOR_IS_MAPPED (self) && priv->in_cloned_branch == 0 && !clutter_actor_has_mapped_clones (self)) return TRUE; return FALSE; } /*< private >* * _clutter_actor_create_transition: * @actor: a #ClutterActor * @pspec: the property used for the transition * @...: initial and final state * * Creates a #ClutterTransition for the property represented by @pspec. * * Return value: a #ClutterTransition */ ClutterTransition * _clutter_actor_create_transition (ClutterActor *actor, GParamSpec *pspec, ...) { ClutterTimeline *timeline; ClutterInterval *interval; ClutterAnimationInfo *info; ClutterTransition *res = NULL; gboolean call_restore = FALSE; TransitionClosure *clos; va_list var_args; g_auto (GValue) initial = G_VALUE_INIT; g_auto (GValue) final = G_VALUE_INIT; GType ptype; char *error; g_assert (pspec != NULL); g_assert ((pspec->flags & CLUTTER_PARAM_ANIMATABLE) != 0); info = _clutter_actor_get_animation_info (actor); /* XXX - this will go away in 2.0 * * if no state has been pushed, we assume that the easing state is * in "compatibility mode": all transitions have a duration of 0 * msecs, which means that they happen immediately. in Clutter 2.0 * this will turn into a g_assert(info->states != NULL), as every * actor will start with a predefined easing state */ if (info->states == NULL) { clutter_actor_save_easing_state (actor); clutter_actor_set_easing_duration (actor, 0); call_restore = TRUE; } if (info->transitions == NULL) info->transitions = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, transition_closure_free); va_start (var_args, pspec); ptype = G_PARAM_SPEC_VALUE_TYPE (pspec); G_VALUE_COLLECT_INIT (&initial, ptype, var_args, 0, &error); if (error != NULL) { g_critical ("%s: %s", G_STRLOC, error); g_free (error); goto out; } G_VALUE_COLLECT_INIT (&final, ptype, var_args, 0, &error); if (error != NULL) { g_critical ("%s: %s", G_STRLOC, error); g_free (error); goto out; } if (should_skip_implicit_transition (actor, pspec)) { CLUTTER_NOTE (ANIMATION, "Skipping implicit transition for '%s::%s'", _clutter_actor_get_debug_name (actor), pspec->name); /* remove a transition, if one exists */ clutter_actor_remove_transition (actor, pspec->name); /* we don't go through the Animatable interface because we * already know we got here through an animatable property. */ clutter_actor_set_animatable_property (actor, pspec->param_id, &final, pspec); goto out; } clos = g_hash_table_lookup (info->transitions, pspec->name); if (clos == NULL) { res = clutter_property_transition_new (pspec->name); clutter_transition_set_remove_on_complete (res, TRUE); interval = clutter_interval_new_with_values (ptype, &initial, &final); clutter_transition_set_interval (res, interval); timeline = CLUTTER_TIMELINE (res); clutter_timeline_set_delay (timeline, info->cur_state->easing_delay); clutter_timeline_set_duration (timeline, info->cur_state->easing_duration); clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode); #ifdef CLUTTER_ENABLE_DEBUG if (CLUTTER_HAS_DEBUG (ANIMATION)) { gchar *initial_v, *final_v; initial_v = g_strdup_value_contents (&initial); final_v = g_strdup_value_contents (&final); CLUTTER_NOTE (ANIMATION, "Created transition for %s:%s " "(len:%u, mode:%s, delay:%u) " "initial:%s, final:%s", _clutter_actor_get_debug_name (actor), pspec->name, info->cur_state->easing_duration, clutter_get_easing_name_for_mode (info->cur_state->easing_mode), info->cur_state->easing_delay, initial_v, final_v); g_free (initial_v); g_free (final_v); } #endif /* CLUTTER_ENABLE_DEBUG */ /* this will start the transition as well */ clutter_actor_add_transition_internal (actor, pspec->name, res); /* the actor now owns the transition */ g_object_unref (res); } else { ClutterAnimationMode cur_mode; guint cur_duration; CLUTTER_NOTE (ANIMATION, "Existing transition for %s:%s", _clutter_actor_get_debug_name (actor), pspec->name); timeline = CLUTTER_TIMELINE (clos->transition); cur_duration = clutter_timeline_get_duration (timeline); if (cur_duration != info->cur_state->easing_duration) clutter_timeline_set_duration (timeline, info->cur_state->easing_duration); cur_mode = clutter_timeline_get_progress_mode (timeline); if (cur_mode != info->cur_state->easing_mode) clutter_timeline_set_progress_mode (timeline, info->cur_state->easing_mode); clutter_timeline_rewind (timeline); interval = clutter_transition_get_interval (clos->transition); clutter_interval_set_initial_value (interval, &initial); clutter_interval_set_final_value (interval, &final); res = clos->transition; } out: if (call_restore) clutter_actor_restore_easing_state (actor); va_end (var_args); return res; } /** * clutter_actor_add_transition: * @self: a #ClutterActor * @name: the name of the transition to add * @transition: the #ClutterTransition to add * * Adds a @transition to the #ClutterActor's list of animations. * * The @name string is a per-actor unique identifier of the @transition: only * one #ClutterTransition can be associated to the specified @name. * * The @transition will be started once added. * * This function will take a reference on the @transition. * * This function is usually called implicitly when modifying an animatable * property. * * Since: 1.10 */ void clutter_actor_add_transition (ClutterActor *self, const char *name, ClutterTransition *transition) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); clutter_actor_add_transition_internal (self, name, transition); } /** * clutter_actor_remove_transition: * @self: a #ClutterActor * @name: the name of the transition to remove * * Removes the transition stored inside a #ClutterActor using @name * identifier. * * If the transition is currently in progress, it will be stopped. * * This function releases the reference acquired when the transition * was added to the #ClutterActor. * * Since: 1.10 */ void clutter_actor_remove_transition (ClutterActor *self, const char *name) { const ClutterAnimationInfo *info; TransitionClosure *clos; gboolean was_playing; GQuark t_quark; gchar *t_name; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (name != NULL); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->transitions == NULL) return; clos = g_hash_table_lookup (info->transitions, name); if (clos == NULL) return; was_playing = clutter_timeline_is_playing (CLUTTER_TIMELINE (clos->transition)); t_quark = g_quark_from_string (clos->name); t_name = g_strdup (clos->name); g_hash_table_remove (info->transitions, name); /* we want to maintain the invariant that ::transition-stopped is * emitted after the transition has been removed, to allow replacing * or chaining; removing the transition from the hash table will * stop it, but transition_closure_free() will disconnect the signal * handler we install in add_transition_internal(), to avoid loops * or segfaults. * * since we know already that a transition will stop once it's removed * from an actor, we can simply emit the ::transition-stopped here * ourselves, if the timeline was playing (if it wasn't, then the * signal was already emitted at least once). */ if (was_playing) { g_signal_emit (self, actor_signals[TRANSITION_STOPPED], t_quark, t_name, FALSE); } g_free (t_name); } /** * clutter_actor_remove_all_transitions: * @self: a #ClutterActor * * Removes all transitions associated to @self. * * Since: 1.10 */ void clutter_actor_remove_all_transitions (ClutterActor *self) { const ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->transitions == NULL) return; g_hash_table_remove_all (info->transitions); } /** * clutter_actor_set_easing_duration: * @self: a #ClutterActor * @msecs: the duration of the easing, or %NULL * * Sets the duration of the tweening for animatable properties * of @self for the current easing state. * * Since: 1.10 */ void clutter_actor_set_easing_duration (ClutterActor *self, guint msecs) { ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_animation_info (self); if (info->cur_state == NULL) { g_warning ("You must call clutter_actor_save_easing_state() prior " "to calling clutter_actor_set_easing_duration()."); return; } if (info->cur_state->easing_duration != msecs) info->cur_state->easing_duration = msecs; } /** * clutter_actor_get_easing_duration: * @self: a #ClutterActor * * Retrieves the duration of the tweening for animatable * properties of @self for the current easing state. * * Return value: the duration of the tweening, in milliseconds * * Since: 1.10 */ guint clutter_actor_get_easing_duration (ClutterActor *self) { const ClutterAnimationInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->cur_state != NULL) return info->cur_state->easing_duration; return 0; } /** * clutter_actor_set_easing_mode: * @self: a #ClutterActor * @mode: an easing mode, excluding %CLUTTER_CUSTOM_MODE * * Sets the easing mode for the tweening of animatable properties * of @self. * * Since: 1.10 */ void clutter_actor_set_easing_mode (ClutterActor *self, ClutterAnimationMode mode) { ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (mode != CLUTTER_CUSTOM_MODE); g_return_if_fail (mode < CLUTTER_ANIMATION_LAST); info = _clutter_actor_get_animation_info (self); if (info->cur_state == NULL) { g_warning ("You must call clutter_actor_save_easing_state() prior " "to calling clutter_actor_set_easing_mode()."); return; } if (info->cur_state->easing_mode != mode) info->cur_state->easing_mode = mode; } /** * clutter_actor_get_easing_mode: * @self: a #ClutterActor * * Retrieves the easing mode for the tweening of animatable properties * of @self for the current easing state. * * Return value: an easing mode * * Since: 1.10 */ ClutterAnimationMode clutter_actor_get_easing_mode (ClutterActor *self) { const ClutterAnimationInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_EASE_OUT_CUBIC); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->cur_state != NULL) return info->cur_state->easing_mode; return CLUTTER_EASE_OUT_CUBIC; } /** * clutter_actor_set_easing_delay: * @self: a #ClutterActor * @msecs: the delay before the start of the tweening, in milliseconds * * Sets the delay that should be applied before tweening animatable * properties. * * Since: 1.10 */ void clutter_actor_set_easing_delay (ClutterActor *self, guint msecs) { ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_animation_info (self); if (info->cur_state == NULL) { g_warning ("You must call clutter_actor_save_easing_state() prior " "to calling clutter_actor_set_easing_delay()."); return; } if (info->cur_state->easing_delay != msecs) info->cur_state->easing_delay = msecs; } /** * clutter_actor_get_easing_delay: * @self: a #ClutterActor * * Retrieves the delay that should be applied when tweening animatable * properties. * * Return value: a delay, in milliseconds * * Since: 1.10 */ guint clutter_actor_get_easing_delay (ClutterActor *self) { const ClutterAnimationInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), 0); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->cur_state != NULL) return info->cur_state->easing_delay; return 0; } /** * clutter_actor_get_transition: * @self: a #ClutterActor * @name: the name of the transition * * Retrieves the #ClutterTransition of a #ClutterActor by using the * transition @name. * * Transitions created for animatable properties use the name of the * property itself, for instance the code below: * * |[ * clutter_actor_set_easing_duration (actor, 1000); * clutter_actor_set_rotation (actor, CLUTTER_Y_AXIS, 360.0, x, y, z); * * transition = clutter_actor_get_transition (actor, "rotation-angle-y"); * g_signal_connect (transition, "stopped", * G_CALLBACK (on_transition_stopped), * actor); * ]| * * will call the `on_transition_stopped` callback when the transition * is finished. * * If you just want to get notifications of the completion of a transition, * you should use the #ClutterActor::transition-stopped signal, using the * transition name as the signal detail. * * Return value: (transfer none): a #ClutterTransition, or %NULL is none * was found to match the passed name; the returned instance is owned * by Clutter and it should not be freed * * Since: 1.10 */ ClutterTransition * clutter_actor_get_transition (ClutterActor *self, const char *name) { TransitionClosure *clos; const ClutterAnimationInfo *info; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (name != NULL, NULL); info = _clutter_actor_get_animation_info_or_defaults (self); if (info->transitions == NULL) return NULL; clos = g_hash_table_lookup (info->transitions, name); if (clos == NULL) return NULL; return clos->transition; } /** * clutter_actor_save_easing_state: * @self: a #ClutterActor * * Saves the current easing state for animatable properties, and creates * a new state with the default values for easing mode and duration. * * New transitions created after calling this function will inherit the * duration, easing mode, and delay of the new easing state; this also * applies to transitions modified in flight. * * Since: 1.10 */ void clutter_actor_save_easing_state (ClutterActor *self) { ClutterAnimationInfo *info; AState new_state; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_animation_info (self); if (info->states == NULL) info->states = g_array_new (FALSE, FALSE, sizeof (AState)); new_state.easing_mode = CLUTTER_EASE_OUT_CUBIC; new_state.easing_duration = 250; new_state.easing_delay = 0; g_array_append_val (info->states, new_state); info->cur_state = &g_array_index (info->states, AState, info->states->len - 1); } /** * clutter_actor_restore_easing_state: * @self: a #ClutterActor * * Restores the easing state as it was prior to a call to * clutter_actor_save_easing_state(). * * Since: 1.10 */ void clutter_actor_restore_easing_state (ClutterActor *self) { ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_animation_info (self); if (info->states == NULL) { g_critical ("The function clutter_actor_restore_easing_state() has " "been called without a previous call to " "clutter_actor_save_easing_state()."); return; } g_array_remove_index (info->states, info->states->len - 1); if (info->states->len > 0) info->cur_state = &g_array_index (info->states, AState, info->states->len - 1); else { g_array_unref (info->states); info->states = NULL; info->cur_state = NULL; } } /** * clutter_actor_set_content: * @self: a #ClutterActor * @content: (allow-none): a #ClutterContent, or %NULL * * Sets the contents of a #ClutterActor. * * Since: 1.10 */ void clutter_actor_set_content (ClutterActor *self, ClutterContent *content) { ClutterActorPrivate *priv; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (content == NULL || CLUTTER_IS_CONTENT (content)); priv = self->priv; if (priv->content == content) return; if (priv->content != NULL) { _clutter_content_detached (priv->content, self); g_clear_object (&priv->content); } priv->content = content; if (priv->content != NULL) { g_object_ref (priv->content); _clutter_content_attached (priv->content, self); } /* if the actor's preferred size is the content's preferred size, * then we need to conditionally queue a relayout here... */ if (priv->request_mode == CLUTTER_REQUEST_CONTENT_SIZE) _clutter_actor_queue_only_relayout (self); clutter_actor_queue_redraw (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT]); /* if the content gravity is not resize-fill, and the new content has a * different preferred size than the previous one, then the content box * may have been changed. since we compute that lazily, we just notify * here, and let whomever watches :content-box do whatever they need to * do. */ if (priv->content_gravity != CLUTTER_CONTENT_GRAVITY_RESIZE_FILL) { if (priv->content_box_valid) { ClutterActorBox from_box, to_box; clutter_actor_get_content_box (self, &from_box); /* invalidate the cached content box */ priv->content_box_valid = FALSE; clutter_actor_get_content_box (self, &to_box); if (!clutter_actor_box_equal (&from_box, &to_box)) _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX], &from_box, &to_box); } g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_BOX]); } } /** * clutter_actor_get_content: * @self: a #ClutterActor * * Retrieves the contents of @self. * * Return value: (transfer none): a pointer to the #ClutterContent instance, * or %NULL if none was set * * Since: 1.10 */ ClutterContent * clutter_actor_get_content (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); return self->priv->content; } /** * clutter_actor_set_content_gravity: * @self: a #ClutterActor * @gravity: the #ClutterContentGravity * * Sets the gravity of the #ClutterContent used by @self. * * See the description of the #ClutterActor:content-gravity property for * more information. * * The #ClutterActor:content-gravity property is animatable. * * Since: 1.10 */ void clutter_actor_set_content_gravity (ClutterActor *self, ClutterContentGravity gravity) { ClutterActorPrivate *priv; ClutterActorBox from_box, to_box; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; if (priv->content_gravity == gravity) return; priv->content_box_valid = FALSE; clutter_actor_get_content_box (self, &from_box); priv->content_gravity = gravity; clutter_actor_get_content_box (self, &to_box); _clutter_actor_create_transition (self, obj_props[PROP_CONTENT_BOX], &from_box, &to_box); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CONTENT_GRAVITY]); } /** * clutter_actor_get_content_gravity: * @self: a #ClutterActor * * Retrieves the content gravity as set using * clutter_actor_set_content_gravity(). * * Return value: the content gravity * * Since: 1.10 */ ClutterContentGravity clutter_actor_get_content_gravity (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_CONTENT_GRAVITY_RESIZE_FILL); return self->priv->content_gravity; } /** * clutter_actor_get_content_box: * @self: a #ClutterActor * @box: (out caller-allocates): the return location for the bounding * box for the #ClutterContent * * Retrieves the bounding box for the #ClutterContent of @self. * * The bounding box is relative to the actor's allocation. * * If no #ClutterContent is set for @self, or if @self has not been * allocated yet, then the result is undefined. * * The content box is guaranteed to be, at most, as big as the allocation * of the #ClutterActor. * * If the #ClutterContent used by the actor has a preferred size, then * it is possible to modify the content box by using the * #ClutterActor:content-gravity property. * * Since: 1.10 */ void clutter_actor_get_content_box (ClutterActor *self, ClutterActorBox *box) { ClutterActorPrivate *priv; gfloat content_w, content_h; gfloat alloc_w, alloc_h; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (box != NULL); priv = self->priv; box->x1 = 0.f; box->y1 = 0.f; box->x2 = priv->allocation.x2 - priv->allocation.x1; box->y2 = priv->allocation.y2 - priv->allocation.y1; if (priv->content_box_valid) { *box = priv->content_box; return; } /* no need to do any more work */ if (priv->content_gravity == CLUTTER_CONTENT_GRAVITY_RESIZE_FILL) return; if (priv->content == NULL) return; /* if the content does not have a preferred size then there is * no point in computing the content box */ if (!clutter_content_get_preferred_size (priv->content, &content_w, &content_h)) return; alloc_w = box->x2; alloc_h = box->y2; switch (priv->content_gravity) { case CLUTTER_CONTENT_GRAVITY_TOP_LEFT: box->x2 = box->x1 + MIN (content_w, alloc_w); box->y2 = box->y1 + MIN (content_h, alloc_h); break; case CLUTTER_CONTENT_GRAVITY_TOP: if (alloc_w > content_w) { box->x1 += ceilf ((alloc_w - content_w) / 2.0); box->x2 = box->x1 + content_w; } box->y2 = box->y1 + MIN (content_h, alloc_h); break; case CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: if (alloc_w > content_w) { box->x1 += (alloc_w - content_w); box->x2 = box->x1 + content_w; } box->y2 = box->y1 + MIN (content_h, alloc_h); break; case CLUTTER_CONTENT_GRAVITY_LEFT: box->x2 = box->x1 + MIN (content_w, alloc_w); if (alloc_h > content_h) { box->y1 += ceilf ((alloc_h - content_h) / 2.0); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_CENTER: if (alloc_w > content_w) { box->x1 += ceilf ((alloc_w - content_w) / 2.0); box->x2 = box->x1 + content_w; } if (alloc_h > content_h) { box->y1 += ceilf ((alloc_h - content_h) / 2.0); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_RIGHT: if (alloc_w > content_w) { box->x1 += (alloc_w - content_w); box->x2 = box->x1 + content_w; } if (alloc_h > content_h) { box->y1 += ceilf ((alloc_h - content_h) / 2.0); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: box->x2 = box->x1 + MIN (content_w, alloc_w); if (alloc_h > content_h) { box->y1 += (alloc_h - content_h); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_BOTTOM: if (alloc_w > content_w) { box->x1 += ceilf ((alloc_w - content_w) / 2.0); box->x2 = box->x1 + content_w; } if (alloc_h > content_h) { box->y1 += (alloc_h - content_h); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: if (alloc_w > content_w) { box->x1 += (alloc_w - content_w); box->x2 = box->x1 + content_w; } if (alloc_h > content_h) { box->y1 += (alloc_h - content_h); box->y2 = box->y1 + content_h; } break; case CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: g_assert_not_reached (); break; case CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: { double r_c = content_w / content_h; if ((alloc_w / r_c) > alloc_h) { box->y1 = 0.f; box->y2 = alloc_h; box->x1 = (alloc_w - (alloc_h * r_c)) / 2.0f; box->x2 = box->x1 + (alloc_h * r_c); } else { box->x1 = 0.f; box->x2 = alloc_w; box->y1 = (alloc_h - (alloc_w / r_c)) / 2.0f; box->y2 = box->y1 + (alloc_w / r_c); } CLUTTER_NOTE (LAYOUT, "r_c: %.3f, r_a: %.3f\t" "a: [%.2fx%.2f], c: [%.2fx%.2f]\t" "b: [%.2f, %.2f, %.2f, %.2f]", r_c, alloc_w / alloc_h, alloc_w, alloc_h, content_w, content_h, box->x1, box->y1, box->x2, box->y2); } break; } } /** * clutter_actor_set_content_scaling_filters: * @self: a #ClutterActor * @min_filter: the minification filter for the content * @mag_filter: the magnification filter for the content * * Sets the minification and magnification filter to be applied when * scaling the #ClutterActor:content of a #ClutterActor. * * The #ClutterActor:minification-filter will be used when reducing * the size of the content; the #ClutterActor:magnification-filter * will be used when increasing the size of the content. * * Since: 1.10 */ void clutter_actor_set_content_scaling_filters (ClutterActor *self, ClutterScalingFilter min_filter, ClutterScalingFilter mag_filter) { ClutterActorPrivate *priv; gboolean changed; GObject *obj; g_return_if_fail (CLUTTER_IS_ACTOR (self)); priv = self->priv; obj = G_OBJECT (self); g_object_freeze_notify (obj); changed = FALSE; if (priv->min_filter != min_filter) { priv->min_filter = min_filter; changed = TRUE; g_object_notify_by_pspec (obj, obj_props[PROP_MINIFICATION_FILTER]); } if (priv->mag_filter != mag_filter) { priv->mag_filter = mag_filter; changed = TRUE; g_object_notify_by_pspec (obj, obj_props[PROP_MAGNIFICATION_FILTER]); } if (changed) clutter_actor_queue_redraw (self); g_object_thaw_notify (obj); } /** * clutter_actor_get_content_scaling_filters: * @self: a #ClutterActor * @min_filter: (out) (allow-none): return location for the minification * filter, or %NULL * @mag_filter: (out) (allow-none): return location for the magnification * filter, or %NULL * * Retrieves the values set using clutter_actor_set_content_scaling_filters(). * * Since: 1.10 */ void clutter_actor_get_content_scaling_filters (ClutterActor *self, ClutterScalingFilter *min_filter, ClutterScalingFilter *mag_filter) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (min_filter != NULL) *min_filter = self->priv->min_filter; if (mag_filter != NULL) *mag_filter = self->priv->mag_filter; } /* * clutter_actor_queue_compute_expand: * @self: a #ClutterActor * * Invalidates the needs_x_expand and needs_y_expand flags on @self * and its parents up to the top-level actor. * * This function also queues a relayout if anything changed. */ static inline void clutter_actor_queue_compute_expand (ClutterActor *self) { ClutterActor *parent; gboolean changed; if (self->priv->needs_compute_expand) return; changed = FALSE; parent = self; while (parent != NULL) { if (!parent->priv->needs_compute_expand) { parent->priv->needs_compute_expand = TRUE; changed = TRUE; } parent = parent->priv->parent; } if (changed) clutter_actor_queue_relayout (self); } /** * clutter_actor_set_x_expand: * @self: a #ClutterActor * @expand: whether the actor should expand horizontally * * Sets whether a #ClutterActor should expand horizontally; this means * that layout manager should allocate extra space for the actor, if * possible. * * Setting an actor to expand will also make all its parent expand, so * that it's possible to build an actor tree and only set this flag on * its leaves and not on every single actor. * * Since: 1.12 */ void clutter_actor_set_x_expand (ClutterActor *self, gboolean expand) { ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); expand = !!expand; info = _clutter_actor_get_layout_info (self); if (info->x_expand != expand) { info->x_expand = expand; self->priv->x_expand_set = TRUE; clutter_actor_queue_compute_expand (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_X_EXPAND]); } } /** * clutter_actor_get_x_expand: * @self: a #ClutterActor * * Retrieves the value set with clutter_actor_set_x_expand(). * * See also: clutter_actor_needs_expand() * * Return value: %TRUE if the actor has been set to expand * * Since: 1.12 */ gboolean clutter_actor_get_x_expand (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return _clutter_actor_get_layout_info_or_defaults (self)->x_expand; } /** * clutter_actor_set_y_expand: * @self: a #ClutterActor * @expand: whether the actor should expand vertically * * Sets whether a #ClutterActor should expand horizontally; this means * that layout manager should allocate extra space for the actor, if * possible. * * Setting an actor to expand will also make all its parent expand, so * that it's possible to build an actor tree and only set this flag on * its leaves and not on every single actor. * * Since: 1.12 */ void clutter_actor_set_y_expand (ClutterActor *self, gboolean expand) { ClutterLayoutInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); expand = !!expand; info = _clutter_actor_get_layout_info (self); if (info->y_expand != expand) { info->y_expand = expand; self->priv->y_expand_set = TRUE; clutter_actor_queue_compute_expand (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_Y_EXPAND]); } } /** * clutter_actor_get_y_expand: * @self: a #ClutterActor * * Retrieves the value set with clutter_actor_set_y_expand(). * * See also: clutter_actor_needs_expand() * * Return value: %TRUE if the actor has been set to expand * * Since: 1.12 */ gboolean clutter_actor_get_y_expand (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); return _clutter_actor_get_layout_info_or_defaults (self)->y_expand; } static void clutter_actor_compute_expand_recursive (ClutterActor *self, gboolean *x_expand_p, gboolean *y_expand_p) { ClutterActorIter iter; ClutterActor *child; gboolean x_expand, y_expand; x_expand = y_expand = FALSE; /* note that we don't recurse into children if we're already set to expand; * this avoids traversing the whole actor tree, even if it may lead to some * child left with the needs_compute_expand flag set. */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { x_expand = x_expand || clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL); y_expand = y_expand || clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL); } *x_expand_p = x_expand; *y_expand_p = y_expand; } static void clutter_actor_compute_expand (ClutterActor *self) { if (self->priv->needs_compute_expand) { const ClutterLayoutInfo *info; gboolean x_expand, y_expand; info = _clutter_actor_get_layout_info_or_defaults (self); if (self->priv->x_expand_set) x_expand = info->x_expand; else x_expand = FALSE; if (self->priv->y_expand_set) y_expand = info->y_expand; else y_expand = FALSE; /* we don't need to recurse down to the children if the * actor has been forcibly set to expand */ if (!(self->priv->x_expand_set && self->priv->y_expand_set)) { if (self->priv->n_children != 0) { gboolean *x_expand_p, *y_expand_p; gboolean ignored = FALSE; x_expand_p = self->priv->x_expand_set ? &ignored : &x_expand; y_expand_p = self->priv->y_expand_set ? &ignored : &y_expand; clutter_actor_compute_expand_recursive (self, x_expand_p, y_expand_p); } } self->priv->needs_compute_expand = FALSE; self->priv->needs_x_expand = (x_expand != FALSE); self->priv->needs_y_expand = (y_expand != FALSE); } } /** * clutter_actor_needs_expand: * @self: a #ClutterActor * @orientation: the direction of expansion * * Checks whether an actor, or any of its children, is set to expand * horizontally or vertically. * * This function should only be called by layout managers that can * assign extra space to their children. * * If you want to know whether the actor was explicitly set to expand, * use clutter_actor_get_x_expand() or clutter_actor_get_y_expand(). * * Return value: %TRUE if the actor should expand * * Since: 1.12 */ gboolean clutter_actor_needs_expand (ClutterActor *self, ClutterOrientation orientation) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); if (!CLUTTER_ACTOR_IS_VISIBLE (self)) return FALSE; if (CLUTTER_ACTOR_IN_DESTRUCTION (self)) return FALSE; clutter_actor_compute_expand (self); switch (orientation) { case CLUTTER_ORIENTATION_HORIZONTAL: return self->priv->needs_x_expand; case CLUTTER_ORIENTATION_VERTICAL: return self->priv->needs_y_expand; } return FALSE; } /** * clutter_actor_set_content_repeat: * @self: a #ClutterActor * @repeat: the repeat policy * * Sets the policy for repeating the #ClutterActor:content of a * #ClutterActor. The behaviour is deferred to the #ClutterContent * implementation. * * Since: 1.12 */ void clutter_actor_set_content_repeat (ClutterActor *self, ClutterContentRepeat repeat) { g_return_if_fail (CLUTTER_IS_ACTOR (self)); if (self->priv->content_repeat == repeat) return; self->priv->content_repeat = repeat; clutter_actor_queue_redraw (self); } /** * clutter_actor_get_content_repeat: * @self: a #ClutterActor * * Retrieves the repeat policy for a #ClutterActor set by * clutter_actor_set_content_repeat(). * * Return value: the content repeat policy * * Since: 1.12 */ ClutterContentRepeat clutter_actor_get_content_repeat (ClutterActor *self) { g_return_val_if_fail (CLUTTER_IS_ACTOR (self), CLUTTER_REPEAT_NONE); return self->priv->content_repeat; } void _clutter_actor_handle_event (ClutterActor *self, const ClutterEvent *event) { GPtrArray *event_tree; ClutterActor *iter; gboolean is_key_event; gint i = 0; /* XXX - for historical reasons that are now lost in the mists of time, * key events are delivered regardless of whether an actor is set as * reactive; this should be changed for 2.0. */ is_key_event = event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE; event_tree = g_ptr_array_sized_new (64); g_ptr_array_set_free_func (event_tree, (GDestroyNotify) g_object_unref); /* build the list of of emitters for the event */ iter = self; while (iter != NULL) { ClutterActor *parent = iter->priv->parent; if (CLUTTER_ACTOR_IS_REACTIVE (iter) || /* an actor must be reactive */ parent == NULL || /* unless it's the stage */ is_key_event) /* or this is a key event */ { /* keep a reference on the actor, so that it remains valid * for the duration of the signal emission */ g_ptr_array_add (event_tree, g_object_ref (iter)); } iter = parent; } /* Capture: from top-level downwards */ for (i = event_tree->len - 1; i >= 0; i--) if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, TRUE)) goto done; /* Bubble: from source upwards */ for (i = 0; i < event_tree->len; i++) if (clutter_actor_event (g_ptr_array_index (event_tree, i), event, FALSE)) goto done; done: g_ptr_array_free (event_tree, TRUE); } static void clutter_actor_set_child_transform_internal (ClutterActor *self, const ClutterMatrix *transform) { ClutterTransformInfo *info = _clutter_actor_get_transform_info (self); ClutterActorIter iter; ClutterActor *child; GObject *obj; gboolean was_set = info->child_transform_set; clutter_matrix_init_from_matrix (&info->child_transform, transform); /* if it's the identity matrix, we need to toggle the boolean flag */ info->child_transform_set = !cogl_matrix_is_identity (transform); /* we need to reset the transform_valid flag on each child */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) child->priv->transform_valid = FALSE; clutter_actor_queue_redraw (self); obj = G_OBJECT (self); g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM]); if (was_set != info->child_transform_set) g_object_notify_by_pspec (obj, obj_props[PROP_CHILD_TRANSFORM_SET]); } /** * clutter_actor_set_child_transform: * @self: a #ClutterActor * @transform: (allow-none): a #ClutterMatrix, or %NULL * * Sets the transformation matrix to be applied to all the children * of @self prior to their own transformations. The default child * transformation is the identity matrix. * * If @transform is %NULL, the child transform will be unset. * * The #ClutterActor:child-transform property is animatable. * * Since: 1.12 */ void clutter_actor_set_child_transform (ClutterActor *self, const ClutterMatrix *transform) { const ClutterTransformInfo *info; ClutterMatrix new_transform; g_return_if_fail (CLUTTER_IS_ACTOR (self)); info = _clutter_actor_get_transform_info_or_defaults (self); if (transform != NULL) clutter_matrix_init_from_matrix (&new_transform, transform); else clutter_matrix_init_identity (&new_transform); _clutter_actor_create_transition (self, obj_props[PROP_CHILD_TRANSFORM], &info->child_transform, &new_transform); } /** * clutter_actor_get_child_transform: * @self: a #ClutterActor * @transform: (out caller-allocates): a #ClutterMatrix * * Retrieves the child transformation matrix set using * clutter_actor_set_child_transform(); if none is currently set, * the @transform matrix will be initialized to the identity matrix. * * Since: 1.12 */ void clutter_actor_get_child_transform (ClutterActor *self, ClutterMatrix *transform) { const ClutterTransformInfo *info; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (transform != NULL); info = _clutter_actor_get_transform_info_or_defaults (self); if (info->child_transform_set) clutter_matrix_init_from_matrix (transform, &info->child_transform); else clutter_matrix_init_identity (transform); } static void clutter_actor_push_in_cloned_branch (ClutterActor *self, gulong count) { ClutterActor *iter; for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) clutter_actor_push_in_cloned_branch (iter, count); self->priv->in_cloned_branch += count; } static void clutter_actor_pop_in_cloned_branch (ClutterActor *self, gulong count) { ClutterActor *iter; self->priv->in_cloned_branch -= count; for (iter = self->priv->first_child; iter != NULL; iter = iter->priv->next_sibling) clutter_actor_pop_in_cloned_branch (iter, count); } void _clutter_actor_attach_clone (ClutterActor *actor, ClutterActor *clone) { ClutterActorPrivate *priv = actor->priv; g_assert (clone != NULL); if (priv->clones == NULL) priv->clones = g_hash_table_new (NULL, NULL); g_hash_table_add (priv->clones, clone); clutter_actor_push_in_cloned_branch (actor, 1); } void _clutter_actor_detach_clone (ClutterActor *actor, ClutterActor *clone) { ClutterActorPrivate *priv = actor->priv; g_assert (clone != NULL); if (priv->clones == NULL || g_hash_table_lookup (priv->clones, clone) == NULL) return; clutter_actor_pop_in_cloned_branch (actor, 1); g_hash_table_remove (priv->clones, clone); if (g_hash_table_size (priv->clones) == 0) { g_hash_table_unref (priv->clones); priv->clones = NULL; } } void _clutter_actor_queue_redraw_on_clones (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; GHashTableIter iter; gpointer key; if (priv->clones == NULL) return; g_hash_table_iter_init (&iter, priv->clones); while (g_hash_table_iter_next (&iter, &key, NULL)) clutter_actor_queue_redraw (key); } void _clutter_actor_queue_relayout_on_clones (ClutterActor *self) { ClutterActorPrivate *priv = self->priv; GHashTableIter iter; gpointer key; if (priv->clones == NULL) return; g_hash_table_iter_init (&iter, priv->clones); while (g_hash_table_iter_next (&iter, &key, NULL)) clutter_actor_queue_relayout (key); } /** * clutter_actor_has_mapped_clones: * @self: a #ClutterActor * * Returns whether a #ClutterActor has any mapped clones. * * Return: %TRUE if the actor has mapped clones, and %FALSE otherwise * * Since: 1.16 */ gboolean clutter_actor_has_mapped_clones (ClutterActor *self) { ClutterActorPrivate *priv; GHashTableIter iter; gpointer key; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), FALSE); priv = self->priv; if (priv->clones == NULL) return FALSE; g_hash_table_iter_init (&iter, priv->clones); while (g_hash_table_iter_next (&iter, &key, NULL)) { if (CLUTTER_ACTOR_IS_MAPPED (key)) return TRUE; } return FALSE; } static void clutter_actor_child_model__items_changed (GListModel *model, guint position, guint removed, guint added, gpointer user_data) { ClutterActor *parent = user_data; ClutterActorPrivate *priv = parent->priv; guint i; while (removed--) { ClutterActor *child = clutter_actor_get_child_at_index (parent, position); clutter_actor_destroy (child); } for (i = 0; i < added; i++) { GObject *item = g_list_model_get_item (model, position + i); ClutterActor *child = priv->create_child_func (item, priv->create_child_data); /* The actor returned by the function can have a floating reference, * if the implementation is in pure C, or have a full reference, usually * the case for language bindings. To avoid leaking references, we * try to assume ownership of the instance, and release the reference * at the end unconditionally, leaving the only reference to the actor * itself. */ if (g_object_is_floating (child)) g_object_ref_sink (child); clutter_actor_insert_child_at_index (parent, child, position + i); g_object_unref (child); g_object_unref (item); } } /** * clutter_actor_bind_model: * @self: a #ClutterActor * @model: (nullable): a #GListModel * @create_child_func: a function that creates #ClutterActor instances * from the contents of the @model * @user_data: user data passed to @create_child_func * @notify: function called when unsetting the @model * * Binds a #GListModel to a #ClutterActor. * * If the #ClutterActor was already bound to a #GListModel, the previous * binding is destroyed. * * The existing children of #ClutterActor are destroyed when setting a * model, and new children are created and added, representing the contents * of the @model. The #ClutterActor is updated whenever the @model changes. * If @model is %NULL, the #ClutterActor is left empty. * * When a #ClutterActor is bound to a model, adding and removing children * directly is undefined behaviour. * * Since: 1.24 */ void clutter_actor_bind_model (ClutterActor *self, GListModel *model, ClutterActorCreateChildFunc create_child_func, gpointer user_data, GDestroyNotify notify) { ClutterActorPrivate *priv = clutter_actor_get_instance_private (self); g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (model == NULL || G_IS_LIST_MODEL (model)); g_return_if_fail (model == NULL || create_child_func != NULL); if (priv->child_model != NULL) { if (priv->create_child_notify != NULL) priv->create_child_notify (priv->create_child_data); g_signal_handlers_disconnect_by_func (priv->child_model, clutter_actor_child_model__items_changed, self); g_clear_object (&priv->child_model); priv->create_child_func = NULL; priv->create_child_data = NULL; priv->create_child_notify = NULL; } clutter_actor_destroy_all_children (self); if (model == NULL) return; priv->child_model = g_object_ref (model); priv->create_child_func = create_child_func; priv->create_child_data = user_data; priv->create_child_notify = notify; g_signal_connect (priv->child_model, "items-changed", G_CALLBACK (clutter_actor_child_model__items_changed), self); clutter_actor_child_model__items_changed (priv->child_model, 0, 0, g_list_model_get_n_items (priv->child_model), self); } typedef struct { GType child_type; GArray *props; } BindClosure; typedef struct { const char *model_property; const char *child_property; GBindingFlags flags; } BindProperty; static void bind_closure_free (gpointer data_) { BindClosure *data = data_; if (data == NULL) return; g_array_unref (data->props); g_slice_free (BindClosure, data); } static ClutterActor * bind_child_with_properties (gpointer item, gpointer data_) { BindClosure *data = data_; ClutterActor *res; guint i; res = g_object_new (data->child_type, NULL); for (i = 0; i < data->props->len; i++) { const BindProperty *prop = &g_array_index (data->props, BindProperty, i); g_object_bind_property (item, prop->model_property, res, prop->child_property, prop->flags); } return res; } /** * clutter_actor_bind_model_with_properties: * @self: a #ClutterActor * @model: a #GListModel * @child_type: the type of #ClutterActor to use when creating * children mapping to items inside the @model * @first_model_property: the first property of @model to bind * @...: tuples of property names on the @model, on the child, and the * #GBindingFlags used to bind them, terminated by %NULL * * Binds a #GListModel to a #ClutterActor. * * Unlike clutter_actor_bind_model(), this function automatically creates * a child #ClutterActor of type @child_type, and binds properties on the * items inside the @model to the corresponding properties on the child, * for instance: * * |[ * clutter_actor_bind_model_with_properties (actor, model, * MY_TYPE_CHILD_VIEW, * "label", "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE, * "icon", "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE, * "selected", "selected", G_BINDING_BIDIRECTIONAL, * "active", "active", G_BINDING_BIDIRECTIONAL, * NULL); * ]| * * is the equivalent of calling clutter_actor_bind_model() with a * #ClutterActorCreateChildFunc of: * * |[ * ClutterActor *res = g_object_new (MY_TYPE_CHILD_VIEW, NULL); * * g_object_bind_property (item, "label", res, "text", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); * g_object_bind_property (item, "icon", res, "image", G_BINDING_DEFAULT | G_BINDING_SYNC_CREATE); * g_object_bind_property (item, "selected", res, "selected", G_BINDING_BIDIRECTIONAL); * g_object_bind_property (item, "active", res, "active", G_BINDING_BIDIRECTIONAL); * * return res; * ]| * * If the #ClutterActor was already bound to a #GListModel, the previous * binding is destroyed. * * When a #ClutterActor is bound to a model, adding and removing children * directly is undefined behaviour. * * See also: clutter_actor_bind_model() * * Since: 1.24 */ void clutter_actor_bind_model_with_properties (ClutterActor *self, GListModel *model, GType child_type, const char *first_model_property, ...) { va_list args; BindClosure *clos; const char *model_property; g_return_if_fail (CLUTTER_IS_ACTOR (self)); g_return_if_fail (G_IS_LIST_MODEL (model)); g_return_if_fail (g_type_is_a (child_type, CLUTTER_TYPE_ACTOR)); clos = g_slice_new0 (BindClosure); clos->child_type = child_type; clos->props = g_array_new (FALSE, FALSE, sizeof (BindProperty)); va_start (args, first_model_property); model_property = first_model_property; while (model_property != NULL) { const char *child_property = va_arg (args, char *); GBindingFlags binding_flags = va_arg (args, guint); BindProperty bind; bind.model_property = g_intern_string (model_property); bind.child_property = g_intern_string (child_property); bind.flags = binding_flags; g_array_append_val (clos->props, bind); model_property = va_arg (args, char *); } va_end (args); clutter_actor_bind_model (self, model, bind_child_with_properties, clos, bind_closure_free); } /*< private > * clutter_actor_create_texture_paint_node: * @self: a #ClutterActor * @texture: a #CoglTexture * * Creates a #ClutterPaintNode initialized using the state of the * given #ClutterActor, ready to be used inside the implementation * of the #ClutterActorClass.paint_node virtual function. * * The returned paint node has the geometry set to the size of the * #ClutterActor:content-box property; it uses the filters specified * in the #ClutterActor:minification-filter and #ClutterActor:magnification-filter * properties; and respects the #ClutterActor:content-repeat property. * * Returns: (transfer full): The newly created #ClutterPaintNode * * Since: 1.24 */ ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture) { ClutterActorPrivate *priv = clutter_actor_get_instance_private (self); ClutterPaintNode *node; ClutterActorBox box; ClutterColor color; g_return_val_if_fail (CLUTTER_IS_ACTOR (self), NULL); g_return_val_if_fail (texture != NULL, NULL); clutter_actor_get_content_box (self, &box); /* ClutterTextureNode will premultiply the blend color, so we * want it to be white with the paint opacity */ color.red = 255; color.green = 255; color.blue = 255; color.alpha = clutter_actor_get_paint_opacity_internal (self); node = clutter_texture_node_new (texture, &color, priv->min_filter, priv->mag_filter); clutter_paint_node_set_static_name (node, "Texture"); if (priv->content_repeat == CLUTTER_REPEAT_NONE) clutter_paint_node_add_rectangle (node, &box); else { float t_w = 1.f, t_h = 1.f; if ((priv->content_repeat & CLUTTER_REPEAT_X_AXIS) != FALSE) t_w = (box.x2 - box.x1) / cogl_texture_get_width (texture); if ((priv->content_repeat & CLUTTER_REPEAT_Y_AXIS) != FALSE) t_h = (box.y2 - box.y1) / cogl_texture_get_height (texture); clutter_paint_node_add_texture_rectangle (node, &box, 0.f, 0.f, t_w, t_h); } return node; } gboolean clutter_actor_has_accessible (ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); if (CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible) return CLUTTER_ACTOR_GET_CLASS (actor)->has_accessible (actor); return TRUE; } muffin-6.4.1/clutter/clutter/clutter-swipe-action.h0000664000175000017500000000713414723361714021346 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Tomeu Vizoso * * Based on ClutterDragAction, written by: * Emmanuele Bassi */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_SWIPE_ACTION_H__ #define __CLUTTER_SWIPE_ACTION_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_SWIPE_ACTION (clutter_swipe_action_get_type ()) #define CLUTTER_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeAction)) #define CLUTTER_IS_SWIPE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SWIPE_ACTION)) #define CLUTTER_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass)) #define CLUTTER_IS_SWIPE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SWIPE_ACTION)) #define CLUTTER_SWIPE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SWIPE_ACTION, ClutterSwipeActionClass)) typedef struct _ClutterSwipeAction ClutterSwipeAction; typedef struct _ClutterSwipeActionPrivate ClutterSwipeActionPrivate; typedef struct _ClutterSwipeActionClass ClutterSwipeActionClass; /** * ClutterSwipeAction: * * The #ClutterSwipeAction structure contains * only private data and should be accessed using the provided API * * Since: 1.8 */ struct _ClutterSwipeAction { /*< private >*/ ClutterGestureAction parent_instance; ClutterSwipeActionPrivate *priv; }; /** * ClutterSwipeActionClass: * @swept: class handler for the #ClutterSwipeAction::swept signal; * deprecated since 1.14 * @swipe: class handler for the #ClutterSwipeAction::swipe signal * * The #ClutterSwipeActionClass structure contains * only private data. * * Since: 1.8 */ struct _ClutterSwipeActionClass { /*< private >*/ ClutterGestureActionClass parent_class; /*< public >*/ void (* swept) (ClutterSwipeAction *action, ClutterActor *actor, ClutterSwipeDirection direction); gboolean (* swipe) (ClutterSwipeAction *action, ClutterActor *actor, ClutterSwipeDirection direction); /*< private >*/ void (* _clutter_swipe_action1) (void); void (* _clutter_swipe_action2) (void); void (* _clutter_swipe_action3) (void); void (* _clutter_swipe_action4) (void); void (* _clutter_swipe_action5) (void); void (* _clutter_swipe_action6) (void); }; CLUTTER_EXPORT GType clutter_swipe_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_swipe_action_new (void); G_END_DECLS #endif /* __CLUTTER_SWIPE_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-transition-group.c0000664000175000017500000001603714723361714022265 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ /** * SECTION:clutter-transition-group * @Title: ClutterTransitionGroup * @Short_Description: Group transitions together * * The #ClutterTransitionGroup allows running multiple #ClutterTransition * instances concurrently. * * The transitions inside a group will run within the boundaries of the * group; for instance, if a transition has a duration of 10 seconds, and * the group that contains it has a duration of 5 seconds, only the first * 5 seconds of the transition will be played. * * #ClutterTransitionGroup is available since Clutter 1.12 */ #include "clutter-build-config.h" #include "clutter-transition-group.h" #include "clutter-debug.h" #include "clutter-private.h" struct _ClutterTransitionGroupPrivate { GHashTable *transitions; }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterTransitionGroup, clutter_transition_group, CLUTTER_TYPE_TRANSITION) static void clutter_transition_group_new_frame (ClutterTimeline *timeline, gint elapsed) { ClutterTransitionGroupPrivate *priv; GHashTableIter iter; gpointer element; gint64 msecs; priv = CLUTTER_TRANSITION_GROUP (timeline)->priv; /* get the time elapsed since the last ::new-frame... */ msecs = clutter_timeline_get_delta (timeline); g_hash_table_iter_init (&iter, priv->transitions); while (g_hash_table_iter_next (&iter, &element, NULL)) { ClutterTimeline *t = element; /* ... and advance every timeline */ clutter_timeline_set_direction (t, clutter_timeline_get_direction (timeline)); clutter_timeline_set_duration (t, clutter_timeline_get_duration (timeline)); _clutter_timeline_advance (t, msecs); } } static void clutter_transition_group_attached (ClutterTransition *transition, ClutterAnimatable *animatable) { ClutterTransitionGroupPrivate *priv; GHashTableIter iter; gpointer element; priv = CLUTTER_TRANSITION_GROUP (transition)->priv; g_hash_table_iter_init (&iter, priv->transitions); while (g_hash_table_iter_next (&iter, &element, NULL)) { ClutterTransition *t = element; clutter_transition_set_animatable (t, animatable); } } static void clutter_transition_group_detached (ClutterTransition *transition, ClutterAnimatable *animatable) { ClutterTransitionGroupPrivate *priv; GHashTableIter iter; gpointer element; priv = CLUTTER_TRANSITION_GROUP (transition)->priv; g_hash_table_iter_init (&iter, priv->transitions); while (g_hash_table_iter_next (&iter, &element, NULL)) { ClutterTransition *t = element; clutter_transition_set_animatable (t, NULL); } } static void clutter_transition_group_started (ClutterTimeline *timeline) { ClutterTransitionGroupPrivate *priv; GHashTableIter iter; gpointer element; priv = CLUTTER_TRANSITION_GROUP (timeline)->priv; g_hash_table_iter_init (&iter, priv->transitions); while (g_hash_table_iter_next (&iter, &element, NULL)) { ClutterTransition *t = element; g_signal_emit_by_name (t, "started"); } } static void clutter_transition_group_finalize (GObject *gobject) { ClutterTransitionGroupPrivate *priv; priv = CLUTTER_TRANSITION_GROUP (gobject)->priv; g_hash_table_unref (priv->transitions); G_OBJECT_CLASS (clutter_transition_group_parent_class)->finalize (gobject); } static void clutter_transition_group_class_init (ClutterTransitionGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass); ClutterTransitionClass *transition_class = CLUTTER_TRANSITION_CLASS (klass); gobject_class->finalize = clutter_transition_group_finalize; timeline_class->started = clutter_transition_group_started; timeline_class->new_frame = clutter_transition_group_new_frame; transition_class->attached = clutter_transition_group_attached; transition_class->detached = clutter_transition_group_detached; } static void clutter_transition_group_init (ClutterTransitionGroup *self) { self->priv = clutter_transition_group_get_instance_private (self); self->priv->transitions = g_hash_table_new_full (NULL, NULL, (GDestroyNotify) g_object_unref, NULL); } /** * clutter_transition_group_new: * * Creates a new #ClutterTransitionGroup instance. * * Return value: the newly created #ClutterTransitionGroup. Use * g_object_unref() when done to deallocate the resources it * uses * * Since: 1.12 */ ClutterTransition * clutter_transition_group_new (void) { return g_object_new (CLUTTER_TYPE_TRANSITION_GROUP, NULL); } /** * clutter_transition_group_add_transition: * @group: a #ClutterTransitionGroup * @transition: a #ClutterTransition * * Adds @transition to @group. * * This function acquires a reference on @transition that will be released * when calling clutter_transition_group_remove_transition(). * * Since: 1.12 */ void clutter_transition_group_add_transition (ClutterTransitionGroup *group, ClutterTransition *transition) { g_return_if_fail (CLUTTER_IS_TRANSITION_GROUP (group)); g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_hash_table_add (group->priv->transitions, g_object_ref (transition)); } /** * clutter_transition_group_remove_transition: * @group: a #ClutterTransitionGroup * @transition: a #ClutterTransition * * Removes @transition from @group. * * This function releases the reference acquired on @transition when * calling clutter_transition_group_add_transition(). * * Since: 1.12 */ void clutter_transition_group_remove_transition (ClutterTransitionGroup *group, ClutterTransition *transition) { g_return_if_fail (CLUTTER_IS_TRANSITION_GROUP (group)); g_hash_table_remove (group->priv->transitions, transition); } /** * clutter_transition_group_remove_all: * @group: a #ClutterTransitionGroup * * Removes all transitions from @group. * * This function releases the reference acquired when calling * clutter_transition_group_add_transition(). * * Since: 1.12 */ void clutter_transition_group_remove_all (ClutterTransitionGroup *group) { g_return_if_fail (CLUTTER_IS_TRANSITION_GROUP (group)); g_hash_table_remove_all (group->priv->transitions); } muffin-6.4.1/clutter/clutter/clutter-stage-window.h0000664000175000017500000001255014723361714021352 0ustar fabiofabio#ifndef __CLUTTER_STAGE_WINDOW_H__ #define __CLUTTER_STAGE_WINDOW_H__ #include #include #include "clutter/clutter-stage-view.h" G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE_WINDOW (clutter_stage_window_get_type ()) CLUTTER_EXPORT G_DECLARE_INTERFACE (ClutterStageWindow, clutter_stage_window, CLUTTER, STAGE_WINDOW, GObject) /* * ClutterStageWindowInterface: (skip) * * The interface implemented by backends for stage windows * * Since: 0.8 */ struct _ClutterStageWindowInterface { /*< private >*/ GTypeInterface parent_iface; ClutterActor *(* get_wrapper) (ClutterStageWindow *stage_window); void (* set_title) (ClutterStageWindow *stage_window, const gchar *title); void (* set_cursor_visible) (ClutterStageWindow *stage_window, gboolean cursor_visible); gboolean (* realize) (ClutterStageWindow *stage_window); void (* unrealize) (ClutterStageWindow *stage_window); void (* show) (ClutterStageWindow *stage_window, gboolean do_raise); void (* hide) (ClutterStageWindow *stage_window); void (* resize) (ClutterStageWindow *stage_window, gint width, gint height); void (* get_geometry) (ClutterStageWindow *stage_window, cairo_rectangle_int_t *geometry); void (* schedule_update) (ClutterStageWindow *stage_window, int sync_delay); gint64 (* get_update_time) (ClutterStageWindow *stage_window); void (* clear_update_time) (ClutterStageWindow *stage_window); void (* set_accept_focus) (ClutterStageWindow *stage_window, gboolean accept_focus); void (* redraw) (ClutterStageWindow *stage_window); gboolean (* can_clip_redraws) (ClutterStageWindow *stage_window); GList *(* get_views) (ClutterStageWindow *stage_window); int64_t (* get_frame_counter) (ClutterStageWindow *stage_window); void (* finish_frame) (ClutterStageWindow *stage_window); int64_t (* get_next_presentation_time) (ClutterStageWindow *stage_window); }; ClutterActor * _clutter_stage_window_get_wrapper (ClutterStageWindow *window); void _clutter_stage_window_set_title (ClutterStageWindow *window, const gchar *title); void _clutter_stage_window_set_cursor_visible (ClutterStageWindow *window, gboolean is_visible); gboolean _clutter_stage_window_realize (ClutterStageWindow *window); void _clutter_stage_window_unrealize (ClutterStageWindow *window); void _clutter_stage_window_show (ClutterStageWindow *window, gboolean do_raise); void _clutter_stage_window_hide (ClutterStageWindow *window); void _clutter_stage_window_resize (ClutterStageWindow *window, gint width, gint height); CLUTTER_EXPORT void _clutter_stage_window_get_geometry (ClutterStageWindow *window, cairo_rectangle_int_t *geometry); void _clutter_stage_window_schedule_update (ClutterStageWindow *window, int sync_delay); gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window); void _clutter_stage_window_clear_update_time (ClutterStageWindow *window); void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window, gboolean accept_focus); void _clutter_stage_window_redraw (ClutterStageWindow *window); gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window); GList * _clutter_stage_window_get_views (ClutterStageWindow *window); void _clutter_stage_window_finish_frame (ClutterStageWindow *window); int64_t _clutter_stage_window_get_frame_counter (ClutterStageWindow *window); int64_t _clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window); G_END_DECLS #endif /* __CLUTTER_STAGE_WINDOW_H__ */ muffin-6.4.1/clutter/clutter/meson.build0000664000175000017500000003544614723361714017264 0ustar fabiofabioclutter_clutter_includesubdir = join_paths(clutter_includesubdir, 'clutter') clutter_clutter_includedir = join_paths(clutter_includedir, 'clutter') clutter_headers = [ 'clutter.h', 'clutter-action.h', 'clutter-actor-meta.h', 'clutter-actor.h', 'clutter-align-constraint.h', 'clutter-animatable.h', 'clutter-autocleanups.h', 'clutter-backend.h', 'clutter-bind-constraint.h', 'clutter-binding-pool.h', 'clutter-bin-layout.h', 'clutter-blur-effect.h', 'clutter-box-layout.h', 'clutter-brightness-contrast-effect.h', 'clutter-cairo.h', 'clutter-canvas.h', 'clutter-child-meta.h', 'clutter-click-action.h', 'clutter-clone.h', 'clutter-color-static.h', 'clutter-color.h', 'clutter-colorize-effect.h', 'clutter-constraint.h', 'clutter-container.h', 'clutter-content.h', 'clutter-deform-effect.h', 'clutter-deprecated.h', 'clutter-desaturate-effect.h', 'clutter-drag-action.h', 'clutter-drop-action.h', 'clutter-effect.h', 'clutter-enums.h', 'clutter-event.h', 'clutter-feature.h', 'clutter-fixed-layout.h', 'clutter-flow-layout.h', 'clutter-gesture-action.h', 'clutter-grid-layout.h', 'clutter-group.h', 'clutter-image.h', 'clutter-input-device.h', 'clutter-input-device-tool.h', 'clutter-input-focus.h', 'clutter-input-method.h', 'clutter-interval.h', 'clutter-keyframe-transition.h', 'clutter-keymap.h', 'clutter-keysyms.h', 'clutter-layout-manager.h', 'clutter-layout-meta.h', 'clutter-macros.h', 'clutter-main.h', 'clutter-mutter.h', 'clutter-offscreen-effect.h', 'clutter-page-turn-effect.h', 'clutter-paint-context.h', 'clutter-paint-nodes.h', 'clutter-paint-node.h', 'clutter-pan-action.h', 'clutter-path-constraint.h', 'clutter-path.h', 'clutter-pick-context.h', 'clutter-property-transition.h', 'clutter-rotate-action.h', 'clutter-script.h', 'clutter-scriptable.h', 'clutter-scroll-actor.h', 'clutter-seat.h', 'clutter-settings.h', 'clutter-shader-effect.h', 'clutter-shader-types.h', 'clutter-swipe-action.h', 'clutter-snap-constraint.h', 'clutter-stage.h', 'clutter-stage-manager.h', 'clutter-stage-view.h', 'clutter-tap-action.h', 'clutter-text.h', 'clutter-text-buffer.h', 'clutter-texture-content.h', 'clutter-timeline.h', 'clutter-transition-group.h', 'clutter-transition.h', 'clutter-types.h', 'clutter-units.h', 'clutter-virtual-input-device.h', 'clutter-zoom-action.h', ] clutter_sources = [ 'clutter-action.c', 'clutter-actor-box.c', 'clutter-actor-meta.c', 'clutter-actor.c', 'clutter-align-constraint.c', 'clutter-animatable.c', 'clutter-backend.c', 'clutter-base-types.c', 'clutter-bezier.c', 'clutter-bind-constraint.c', 'clutter-binding-pool.c', 'clutter-bin-layout.c', 'clutter-blur-effect.c', 'clutter-box-layout.c', 'clutter-brightness-contrast-effect.c', 'clutter-cairo.c', 'clutter-canvas.c', 'clutter-child-meta.c', 'clutter-click-action.c', 'clutter-clone.c', 'clutter-color.c', 'clutter-colorize-effect.c', 'clutter-constraint.c', 'clutter-container.c', 'clutter-content.c', 'clutter-deform-effect.c', 'clutter-desaturate-effect.c', 'clutter-drag-action.c', 'clutter-drop-action.c', 'clutter-effect.c', 'clutter-event.c', 'clutter-feature.c', 'clutter-fixed-layout.c', 'clutter-flatten-effect.c', 'clutter-flow-layout.c', 'clutter-gesture-action.c', 'clutter-graphene.c', 'clutter-grid-layout.c', 'clutter-image.c', 'clutter-input-device.c', 'clutter-input-device-tool.c', 'clutter-input-focus.c', 'clutter-input-method.c', 'clutter-input-pointer-a11y.c', 'clutter-virtual-input-device.c', 'clutter-interval.c', 'clutter-keyframe-transition.c', 'clutter-keymap.c', 'clutter-keysyms-table.c', 'clutter-layout-manager.c', 'clutter-layout-meta.c', 'clutter-main.c', 'clutter-master-clock.c', 'clutter-master-clock-default.c', 'clutter-offscreen-effect.c', 'clutter-page-turn-effect.c', 'clutter-paint-context.c', 'clutter-paint-nodes.c', 'clutter-paint-node.c', 'clutter-pan-action.c', 'clutter-path-constraint.c', 'clutter-path.c', 'clutter-pick-context.c', 'clutter-property-transition.c', 'clutter-rotate-action.c', 'clutter-script.c', 'clutter-script-parser.c', 'clutter-scriptable.c', 'clutter-scroll-actor.c', 'clutter-seat.c', 'clutter-settings.c', 'clutter-shader-effect.c', 'clutter-shader-types.c', 'clutter-swipe-action.c', 'clutter-snap-constraint.c', 'clutter-stage.c', 'clutter-stage-manager.c', 'clutter-stage-view.c', 'clutter-stage-window.c', 'clutter-tap-action.c', 'clutter-text.c', 'clutter-text-buffer.c', 'clutter-texture-content.c', 'clutter-transition-group.c', 'clutter-transition.c', 'clutter-timeline.c', 'clutter-units.c', 'clutter-util.c', 'clutter-paint-volume.c', 'clutter-zoom-action.c', ] clutter_private_headers = [ 'clutter-actor-meta-private.h', 'clutter-actor-private.h', 'clutter-backend-private.h', 'clutter-bezier.h', 'clutter-constraint-private.h', 'clutter-content-private.h', 'clutter-debug.h', 'clutter-easing.h', 'clutter-effect-private.h', 'clutter-event-private.h', 'clutter-flatten-effect.h', 'clutter-graphene.h', 'clutter-gesture-action-private.h', 'clutter-id-pool.h', 'clutter-input-device-private.h', 'clutter-input-focus-private.h', 'clutter-input-method-private.h', 'clutter-input-pointer-a11y-private.h', 'clutter-master-clock.h', 'clutter-master-clock-default.h', 'clutter-offscreen-effect-private.h', 'clutter-paint-context-private.h', 'clutter-paint-node-private.h', 'clutter-paint-volume-private.h', 'clutter-private.h', 'clutter-script-private.h', 'clutter-settings-private.h', 'clutter-stage-manager-private.h', 'clutter-stage-private.h', 'clutter-stage-view-private.h', 'clutter-stage-window.h', ] clutter_nonintrospected_sources = [ 'clutter-easing.c', 'clutter-id-pool.c', ] clutter_deprecated_headers = [ 'deprecated/clutter-actor.h', 'deprecated/clutter-alpha.h', 'deprecated/clutter-animation.h', 'deprecated/clutter-box.h', 'deprecated/clutter-container.h', 'deprecated/clutter-group.h', 'deprecated/clutter-rectangle.h', 'deprecated/clutter-stage.h', 'deprecated/clutter-state.h', 'deprecated/clutter-timeline.h', ] clutter_deprecated_sources = [ 'deprecated/clutter-alpha.c', 'deprecated/clutter-animation.c', 'deprecated/clutter-box.c', 'deprecated/clutter-group.c', 'deprecated/clutter-rectangle.c', 'deprecated/clutter-state.c', ] clutter_backend_sources = [] clutter_backend_nonintrospected_sources = [ 'cogl/clutter-stage-cogl.c', ] clutter_backend_headers = [] clutter_backend_private_headers = [ 'cogl/clutter-stage-cogl.h', ] if have_x11 clutter_x11_sources = [ 'x11/clutter-backend-x11.c', ] clutter_backend_sources += clutter_x11_sources clutter_x11_headers = [ 'x11/clutter-x11.h', ] clutter_backend_headers += clutter_x11_headers clutter_x11_private_headers = [ 'x11/clutter-backend-x11.h', 'x11/clutter-settings-x11.h', ] clutter_backend_private_headers += clutter_x11_private_headers clutter_x11_nonintrospected_sources = [ 'x11/xsettings/xsettings-client.c', 'x11/xsettings/xsettings-client.h', 'x11/xsettings/xsettings-common.c', 'x11/xsettings/xsettings-common.h', ] clutter_backend_nonintrospected_sources += clutter_x11_nonintrospected_sources endif if have_native_backend clutter_native_nonintrospected_sources = [ 'egl/clutter-backend-eglnative.c', ] clutter_backend_nonintrospected_sources += clutter_native_nonintrospected_sources endif if have_wayland clutter_wayland_private_headers = [ 'wayland/clutter-wayland-compositor.h', ] clutter_backend_private_headers += clutter_wayland_private_headers endif cally_headers = [ 'cally/cally-actor.h', 'cally/cally-clone.h', 'cally/cally-factory.h', 'cally/cally-group.h', 'cally/cally.h', 'cally/cally-main.h', 'cally/cally-rectangle.h', 'cally/cally-root.h', 'cally/cally-stage.h', 'cally/cally-text.h', 'cally/cally-util.h', ] cally_sources = [ 'cally/cally-actor.c', 'cally/cally.c', 'cally/cally-clone.c', 'cally/cally-group.c', 'cally/cally-rectangle.c', 'cally/cally-root.c', 'cally/cally-stage.c', 'cally/cally-text.c', 'cally/cally-util.c', ] cally_private_headers = [ 'cally/cally-actor-private.h', ] clutter_built_sources = [] clutter_built_headers = [] clutter_built_private_headers = [] cdata = configuration_data() cdata.set_quoted('MUTTER_VERSION', meson.project_version()) cdata.set('CLUTTER_DRIVERS', '"*"') cdata.set('HAVE_EVDEV', have_native_backend) cdata.set('HAVE_LIBWACOM', have_libwacom) cdata.set('HAVE_PANGO_FT2', have_pango_ft2) clutter_build_config_h = configure_file( input: 'clutter-build-config.h.meson', output: 'clutter-build-config.h', configuration: cdata, install: false, ) clutter_built_private_headers += clutter_build_config_h clutter_config_defines = [] if have_wayland clutter_config_defines += [ '#define CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT 1', ] endif if have_x11 clutter_config_defines += [ '#define CLUTTER_WINDOWING_X11 "x11"', '#define CLUTTER_INPUT_X11 "x11"', '#define CLUTTER_WINDOWING_GLX "glx"', ] endif if have_native_backend clutter_config_defines += [ '#define CLUTTER_WINDOWING_EGL "eglnative"', '#define CLUTTER_INPUT_EVDEV "evdev"', ] endif clutter_config_defines += [ '#define CLUTTER_INPUT_NULL "null"', ] clutter_config_defines_string = '' foreach clutter_config_define : clutter_config_defines clutter_config_defines_string += clutter_config_define + '\n' endforeach cdata = configuration_data() cdata.set('CLUTTER_CONFIG_DEFINES', clutter_config_defines_string) clutter_config_h = configure_file( input: 'clutter-config.h.in', output: 'clutter-config.h', configuration: cdata, install: true, install_dir: clutter_clutter_includedir, ) clutter_built_headers += clutter_config_h clutter_enum_types = gnome.mkenums('clutter-enum-types', sources: [clutter_headers, clutter_deprecated_headers], c_template: 'clutter-enum-types.c.in', h_template: 'clutter-enum-types.h.in', install_dir: clutter_clutter_includedir, install_header: true, ) clutter_built_sources += clutter_enum_types[0] clutter_built_headers += clutter_enum_types[1] clutter_marshal = gnome.genmarshal('clutter-marshal', prefix: '_clutter_marshal', sources: 'clutter-marshal.list', valist_marshallers: true, extra_args: ['--quiet'], install_dir: clutter_clutter_includedir, install_header: true, ) clutter_built_sources += clutter_marshal[0] clutter_built_headers += clutter_marshal[1] libmutter_clutter_name = 'muffin-clutter-' + libmutter_api_version libmutter_clutter = shared_library(libmutter_clutter_name, sources: [ clutter_sources, clutter_headers, clutter_private_headers, clutter_nonintrospected_sources, clutter_deprecated_sources, clutter_deprecated_headers, clutter_backend_sources, clutter_backend_nonintrospected_sources, clutter_backend_headers, clutter_backend_private_headers, clutter_built_sources, clutter_built_headers, cally_sources, cally_headers, cally_private_headers, ], version: '0.0.0', soversion: 0, c_args: clutter_c_args, include_directories: clutter_includes, dependencies: [clutter_deps], gnu_symbol_visibility: 'hidden', link_with: [ libmutter_cogl, libmutter_cogl_pango, libmutter_cogl_path, ], install_rpath: pkglibdir, install_dir: pkglibdir, install: true, ) libmutter_clutter_dep = declare_dependency( sources: [clutter_enum_types[1]], link_with: libmutter_clutter, dependencies: clutter_deps, ) if have_introspection clutter_introspection_args = introspection_args + [ '-DCLUTTER_SYSCONFDIR="@0@"'.format(join_paths(prefix, sysconfdir)), '-DCLUTTER_COMPILATION=1', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DG_LOG_DOMAIN="Clutter"' ] libmutter_clutter_gir = gnome.generate_gir(libmutter_clutter, sources: [ clutter_built_sources, clutter_built_headers, clutter_sources, clutter_headers, clutter_deprecated_sources, clutter_deprecated_headers, ], nsversion: libmutter_api_version, namespace: 'Clutter', export_packages: [libmutter_clutter_name], includes: [ libmutter_cogl_gir[0], libmutter_cogl_pango_gir[0], 'GL-1.0', 'GObject-2.0', 'cairo-1.0', 'Atk-1.0', 'Json-1.0', ], dependencies: [cogl_deps], extra_args: clutter_introspection_args + ['--c-include=clutter/clutter.h'], install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true, ) libmutter_cally_gir = gnome.generate_gir(libmutter_clutter, sources: [ cally_sources, cally_headers, ], nsversion: libmutter_api_version, namespace: 'Cally', includes: [ libmutter_cogl_gir[0], libmutter_cogl_pango_gir[0], libmutter_clutter_gir[0], ], dependencies: [cogl_deps], extra_args: clutter_introspection_args, install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true ) if have_x11 libmutter_clutter_x11_gir = gnome.generate_gir(libmutter_clutter, sources: [ clutter_x11_sources, clutter_x11_headers, ], nsversion: libmutter_api_version, namespace: 'ClutterX11', export_packages: ['muffin-clutter-x11-' + libmutter_api_version], includes: [ libmutter_cogl_gir[0], libmutter_cogl_pango_gir[0], libmutter_clutter_gir[0], 'xlib-2.0', ], dependencies: [], extra_args: clutter_introspection_args, install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true ) endif endif install_headers(clutter_headers, subdir: clutter_clutter_includesubdir) install_headers(cally_headers, subdir: join_paths(clutter_includesubdir, 'cally')) install_headers(clutter_deprecated_headers, subdir: join_paths(clutter_clutter_includesubdir, 'deprecated')) install_headers(clutter_x11_headers, subdir: join_paths(clutter_clutter_includesubdir, 'x11')) pkg.generate(libmutter_clutter, name: 'Muffins Clutter', filebase: libmutter_clutter_name, description: 'Muffinss Clutter Private Library', libraries: [m_dep], subdirs: join_paths(pkgname, 'clutter'), requires: [clutter_pkg_deps, libmutter_cogl_name], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, ], install_dir: pcdir, ) pkg.generate(libmutter_clutter, name: 'Muffins ClutterX11', filebase: 'muffin-clutter-x11-' + libmutter_api_version, description: 'Muffin ClutterX11 Private Library', libraries: [m_dep], subdirs: join_paths(pkgname, 'clutter'), requires: [clutter_pkg_deps, libmutter_cogl_name], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, ], install_dir: pcdir, ) muffin-6.4.1/clutter/clutter/clutter-action.c0000664000175000017500000000355714723361714020221 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-action * @Title: ClutterAction * @Short_Description: Abstract class for event-related logic * @See_Also: #ClutterConstraint * * #ClutterAction is an abstract base class for event-related actions that * modify the user interaction of a #ClutterActor, just like * #ClutterConstraint is an abstract class for modifiers of an actor's * position or size. * * Implementations of #ClutterAction are associated to an actor and can * provide behavioral changes when dealing with user input - for instance * drag and drop capabilities, or scrolling, or panning - by using the * various event-related signals provided by #ClutterActor itself. * * #ClutterAction is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-action.h" #include "clutter-debug.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterAction, clutter_action, CLUTTER_TYPE_ACTOR_META); static void clutter_action_class_init (ClutterActionClass *klass) { } static void clutter_action_init (ClutterAction *self) { } muffin-6.4.1/clutter/clutter/clutter-macros.h0000664000175000017500000001035214723361714020224 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_MACROS_H__ #define __CLUTTER_MACROS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif /** * CLUTTER_FLAVOUR: * * GL Windowing system used * * Since: 0.4 * * Deprecated: 1.10: The macro evaluates to "deprecated" as Clutter can be * compiled with multiple windowing system backends. Use the various * CLUTTER_WINDOWING_* macros to detect the windowing system that Clutter * is being compiled against, and the type check macros for the * #ClutterBackend for a run-time check. */ #define CLUTTER_FLAVOUR "deprecated" /** * CLUTTER_COGL: * * Cogl (internal GL abstraction utility library) backend. Can be "gl" or * "gles" currently * * Since: 0.4 * * Deprecated: 1.10: The macro evaluates to "deprecated" as Cogl can be * compiled against multiple GL implementations. */ #define CLUTTER_COGL "deprecated" /** * CLUTTER_STAGE_TYPE: * * The default GObject type for the Clutter stage. * * Since: 0.8 * * Deprecated: 1.10: The macro evaluates to "deprecated" as Clutter can * be compiled against multiple windowing systems. You can use the * CLUTTER_WINDOWING_* macros for compile-time checks, and the type * check macros for run-time checks. */ #define CLUTTER_STAGE_TYPE "deprecated" /** * CLUTTER_NO_FPU: * * Set to 1 if Clutter was built without FPU (i.e fixed math), 0 otherwise * * Deprecated: 0.6: This macro is no longer defined (identical code is used * regardless the presence of FPU). */ #define CLUTTER_NO_FPU (0) /* some structures are meant to be opaque and still be allocated on the stack; * in order to avoid people poking at their internals, we use this macro to * ensure that users don't accidentally access a struct private members. * * we use the CLUTTER_COMPILATION define to allow us easier access, though. */ #ifdef CLUTTER_COMPILATION #define CLUTTER_PRIVATE_FIELD(x) x #else #define CLUTTER_PRIVATE_FIELD(x) clutter_private_ ## x #endif #define _CLUTTER_EXTERN __attribute__((visibility("default"))) extern #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || \ __clang_major__ > 3 || (__clang_major__ == 3 && __clang_minor__ >= 4) #define _CLUTTER_GNUC_DO_PRAGMA(x) _Pragma(G_STRINGIFY (x)) #define _CLUTTER_DEPRECATED_MACRO _CLUTTER_GNUC_DO_PRAGMA(GCC warning "Deprecated macro") #define _CLUTTER_DEPRECATED_MACRO_FOR(f) _CLUTTER_GNUC_DO_PRAGMA(GCC warning #f) #else #define _CLUTTER_DEPRECATED_MACRO #define _CLUTTER_DEPRECATED_MACRO_FOR(f) #endif /* these macros are used to mark deprecated functions, and thus have to be * exposed in a public header. * * do *not* use them in other libraries depending on Clutter: use G_DEPRECATED * and G_DEPRECATED_FOR, or use your own wrappers around them. */ #ifdef CLUTTER_DISABLE_DEPRECATION_WARNINGS #define CLUTTER_DEPRECATED _CLUTTER_EXTERN #define CLUTTER_DEPRECATED_FOR(f) _CLUTTER_EXTERN #define CLUTTER_DEPRECATED_MACRO #define CLUTTER_DEPRECATED_MACRO_FOR(f) #else #define CLUTTER_DEPRECATED G_DEPRECATED _CLUTTER_EXTERN #define CLUTTER_DEPRECATED_FOR(f) G_DEPRECATED_FOR(f) _CLUTTER_EXTERN #define CLUTTER_DEPRECATED_MACRO _CLUTTER_DEPRECATED_MACRO #define CLUTTER_DEPRECATED_MACRO_FOR(f) _CLUTTER_DEPRECATED_MACRO_FOR(f) #endif #define CLUTTER_MACRO_DEPRECATED CLUTTER_DEPRECATED_MACRO #define CLUTTER_MACRO_DEPRECATED_FOR(f) CLUTTER_DEPRECATED_MACRO_FOR(f) #define CLUTTER_EXPORT _CLUTTER_EXTERN #endif /* __CLUTTER_MACROS_H__ */ muffin-6.4.1/clutter/clutter/clutter-private.h0000664000175000017500000003044414723361714020416 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_PRIVATE_H__ #define __CLUTTER_PRIVATE_H__ #include #include #include #include "clutter-backend.h" #include "clutter-effect.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-id-pool.h" #include "clutter-layout-manager.h" #include "clutter-master-clock.h" #include "clutter-settings.h" #include "clutter-stage-manager.h" #include "clutter-stage.h" G_BEGIN_DECLS typedef struct _ClutterMainContext ClutterMainContext; typedef struct _ClutterVertex4 ClutterVertex4; #define CLUTTER_REGISTER_VALUE_TRANSFORM_TO(TYPE_TO,func) { \ g_value_register_transform_func (g_define_type_id, TYPE_TO, func); \ } #define CLUTTER_REGISTER_VALUE_TRANSFORM_FROM(TYPE_FROM,func) { \ g_value_register_transform_func (TYPE_FROM, g_define_type_id, func); \ } #define CLUTTER_REGISTER_INTERVAL_PROGRESS(func) { \ clutter_interval_register_progress_func (g_define_type_id, func); \ } #define CLUTTER_PRIVATE_FLAGS(a) (((ClutterActor *) (a))->private_flags) #define CLUTTER_SET_PRIVATE_FLAGS(a,f) (CLUTTER_PRIVATE_FLAGS (a) |= (f)) #define CLUTTER_UNSET_PRIVATE_FLAGS(a,f) (CLUTTER_PRIVATE_FLAGS (a) &= ~(f)) #define CLUTTER_ACTOR_IS_TOPLEVEL(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IS_TOPLEVEL) != FALSE) #define CLUTTER_ACTOR_IN_DESTRUCTION(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_DESTRUCTION) != FALSE) #define CLUTTER_ACTOR_IN_REPARENT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_REPARENT) != FALSE) #define CLUTTER_ACTOR_IN_PAINT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PAINT) != FALSE) #define CLUTTER_ACTOR_IN_PICK(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PICK) != FALSE) #define CLUTTER_ACTOR_IN_RELAYOUT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_RELAYOUT) != FALSE) #define CLUTTER_ACTOR_IN_PREF_WIDTH(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PREF_WIDTH) != FALSE) #define CLUTTER_ACTOR_IN_PREF_HEIGHT(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_PREF_HEIGHT) != FALSE) #define CLUTTER_ACTOR_IN_PREF_SIZE(a) ((CLUTTER_PRIVATE_FLAGS (a) & (CLUTTER_IN_PREF_HEIGHT|CLUTTER_IN_PREF_WIDTH)) != FALSE) #define CLUTTER_ACTOR_IN_MAP_UNMAP(a) ((CLUTTER_PRIVATE_FLAGS (a) & CLUTTER_IN_MAP_UNMAP) != FALSE) #define CLUTTER_PARAM_READABLE (G_PARAM_READABLE | G_PARAM_STATIC_STRINGS) #define CLUTTER_PARAM_WRITABLE (G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS) #define CLUTTER_PARAM_READWRITE (G_PARAM_READABLE | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS) #define CLUTTER_PARAM_ANIMATABLE (1 << G_PARAM_USER_SHIFT) /* automagic interning of a static string */ #define I_(str) (g_intern_static_string ((str))) /* keep this for source compatibility with clutter */ #define P_(String) (String) #define N_(String) (String) /* This is a replacement for the nearbyint function which always rounds to the * nearest integer. nearbyint is apparently a C99 function so it might not * always be available but also it seems in glibc it is defined as a function * call so this macro could end up faster anyway. We can't just add 0.5f * because it will break for negative numbers. */ #define CLUTTER_NEARBYINT(x) ((int) ((x) < 0.0f ? (x) - 0.5f : (x) + 0.5f)) typedef enum { CLUTTER_ACTOR_UNUSED_FLAG = 0, CLUTTER_IN_DESTRUCTION = 1 << 0, CLUTTER_IS_TOPLEVEL = 1 << 1, CLUTTER_IN_REPARENT = 1 << 2, CLUTTER_IN_PREF_WIDTH = 1 << 3, CLUTTER_IN_PREF_HEIGHT = 1 << 4, /* Used to avoid recursion */ CLUTTER_IN_PAINT = 1 << 5, CLUTTER_IN_PICK = 1 << 6, /* Used to avoid recursion */ CLUTTER_IN_RELAYOUT = 1 << 7, CLUTTER_IN_MAP_UNMAP = 1 << 8, } ClutterPrivateFlags; /* * ClutterMainContext: * * The shared state of Clutter */ struct _ClutterMainContext { /* the main windowing system backend */ ClutterBackend *backend; /* the object holding all the stage instances */ ClutterStageManager *stage_manager; /* the clock driving all the frame operations */ ClutterMasterClock *master_clock; /* the main event queue */ GQueue *events_queue; /* the event filters added via clutter_event_add_filter. these are * ordered from least recently added to most recently added */ GList *event_filters; ClutterPickMode pick_mode; /* default FPS; this is only used if we cannot sync to vblank */ guint frame_rate; /* fb bit masks for col<->id mapping in picking */ gint fb_r_mask; gint fb_g_mask; gint fb_b_mask; gint fb_r_mask_used; gint fb_g_mask_used; gint fb_b_mask_used; CoglPangoFontMap *font_map; /* Global font map */ /* stack of #ClutterEvent */ GSList *current_event; /* list of repaint functions installed through * clutter_threads_add_repaint_func() */ GList *repaint_funcs; guint last_repaint_id; /* main settings singleton */ ClutterSettings *settings; /* boolean flags */ guint is_initialized : 1; guint defer_display_setup : 1; guint options_parsed : 1; guint show_fps : 1; }; /* shared between clutter-main.c and clutter-frame-source.c */ typedef struct { GSourceFunc func; gpointer data; GDestroyNotify notify; } ClutterThreadsDispatch; gboolean _clutter_threads_dispatch (gpointer data); void _clutter_threads_dispatch_free (gpointer data); CLUTTER_EXPORT void _clutter_threads_acquire_lock (void); CLUTTER_EXPORT void _clutter_threads_release_lock (void); ClutterMainContext * _clutter_context_get_default (void); void _clutter_context_lock (void); void _clutter_context_unlock (void); gboolean _clutter_context_is_initialized (void); ClutterPickMode _clutter_context_get_pick_mode (void); gboolean _clutter_context_get_show_fps (void); gboolean _clutter_feature_init (GError **error); /* Diagnostic mode */ gboolean _clutter_diagnostic_enabled (void); void _clutter_diagnostic_message (const char *fmt, ...) G_GNUC_PRINTF (1, 2); CLUTTER_EXPORT void _clutter_set_sync_to_vblank (gboolean sync_to_vblank); /* use this function as the accumulator if you have a signal with * a G_TYPE_BOOLEAN return value; this will stop the emission as * soon as one handler returns TRUE */ gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy); /* use this function as the accumulator if you have a signal with * a G_TYPE_BOOLEAN return value; this will stop the emission as * soon as one handler returns FALSE */ gboolean _clutter_boolean_continue_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy); void _clutter_run_repaint_functions (ClutterRepaintFlags flags); GType _clutter_layout_manager_get_child_meta_type (ClutterLayoutManager *manager); void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, const CoglMatrix *projection, const float *viewport, const graphene_point3d_t *vertices_in, graphene_point3d_t *vertices_out, int n_vertices); void _clutter_util_rect_from_rectangle (const cairo_rectangle_int_t *src, graphene_rect_t *dest); void _clutter_util_rectangle_int_extents (const graphene_rect_t *src, cairo_rectangle_int_t *dest); void _clutter_util_rectangle_offset (const cairo_rectangle_int_t *src, int x, int y, cairo_rectangle_int_t *dest); void _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2, cairo_rectangle_int_t *dest); gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2, cairo_rectangle_int_t *dest); gboolean clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2); struct _ClutterVertex4 { float x; float y; float z; float w; }; void _clutter_util_vertex4_interpolate (const ClutterVertex4 *a, const ClutterVertex4 *b, double progress, ClutterVertex4 *res); #define CLUTTER_MATRIX_INIT_IDENTITY { \ 1.0f, 0.0f, 0.0f, 0.0f, \ 0.0f, 1.0f, 0.0f, 0.0f, \ 0.0f, 0.0f, 1.0f, 0.0f, \ 0.0f, 0.0f, 0.0f, 1.0f, \ } float _clutter_util_matrix_determinant (const ClutterMatrix *matrix); void _clutter_util_matrix_skew_xy (ClutterMatrix *matrix, float factor); void _clutter_util_matrix_skew_xz (ClutterMatrix *matrix, float factor); void _clutter_util_matrix_skew_yz (ClutterMatrix *matrix, float factor); gboolean _clutter_util_matrix_decompose (const ClutterMatrix *src, graphene_point3d_t *scale_p, float shear_p[3], graphene_point3d_t *rotate_p, graphene_point3d_t *translate_p, ClutterVertex4 *perspective_p); CLUTTER_EXPORT PangoDirection _clutter_pango_unichar_direction (gunichar ch); PangoDirection _clutter_pango_find_base_dir (const gchar *text, gint length); typedef struct _ClutterPlane { graphene_vec3_t v0; graphene_vec3_t n; } ClutterPlane; typedef enum _ClutterCullResult { CLUTTER_CULL_RESULT_UNKNOWN, CLUTTER_CULL_RESULT_IN, CLUTTER_CULL_RESULT_OUT, CLUTTER_CULL_RESULT_PARTIAL } ClutterCullResult; gboolean _clutter_has_progress_function (GType gtype); gboolean _clutter_run_progress_function (GType gtype, const GValue *initial, const GValue *final, gdouble progress, GValue *retval); void clutter_timeline_cancel_delay (ClutterTimeline *timeline); G_END_DECLS #endif /* __CLUTTER_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-transition.c0000664000175000017500000004754014723361714021136 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ /** * SECTION:clutter-transition * @Title: ClutterTransition * @Short_Description: Transition between two values * * #ClutterTransition is an abstract subclass of #ClutterTimeline that * computes the interpolation between two values, stored by a #ClutterInterval. */ #include "clutter-build-config.h" #include "clutter-transition.h" #include "clutter-animatable.h" #include "clutter-debug.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-timeline.h" #include struct _ClutterTransitionPrivate { ClutterInterval *interval; ClutterAnimatable *animatable; guint remove_on_complete : 1; }; enum { PROP_0, PROP_INTERVAL, PROP_ANIMATABLE, PROP_REMOVE_ON_COMPLETE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; static GQuark quark_animatable_set = 0; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterTransition, clutter_transition, CLUTTER_TYPE_TIMELINE) static void clutter_transition_attach (ClutterTransition *transition, ClutterAnimatable *animatable) { CLUTTER_TRANSITION_GET_CLASS (transition)->attached (transition, animatable); } static void clutter_transition_detach (ClutterTransition *transition, ClutterAnimatable *animatable) { CLUTTER_TRANSITION_GET_CLASS (transition)->detached (transition, animatable); } static void clutter_transition_real_compute_value (ClutterTransition *transition, ClutterAnimatable *animatable, ClutterInterval *interval, gdouble progress) { } static void clutter_transition_real_attached (ClutterTransition *transition, ClutterAnimatable *animatable) { } static void clutter_transition_real_detached (ClutterTransition *transition, ClutterAnimatable *animatable) { } static void clutter_transition_new_frame (ClutterTimeline *timeline, gint elapsed G_GNUC_UNUSED) { ClutterTransition *transition = CLUTTER_TRANSITION (timeline); ClutterTransitionPrivate *priv = transition->priv; gdouble progress; if (priv->interval == NULL || priv->animatable == NULL) return; progress = clutter_timeline_get_progress (timeline); CLUTTER_TRANSITION_GET_CLASS (timeline)->compute_value (transition, priv->animatable, priv->interval, progress); } static void clutter_transition_stopped (ClutterTimeline *timeline, gboolean is_finished) { ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (timeline)->priv; if (is_finished && priv->animatable != NULL && priv->remove_on_complete) { clutter_transition_detach (CLUTTER_TRANSITION (timeline), priv->animatable); g_clear_object (&priv->animatable); } } static void clutter_transition_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterTransition *transition = CLUTTER_TRANSITION (gobject); switch (prop_id) { case PROP_INTERVAL: clutter_transition_set_interval (transition, g_value_get_object (value)); break; case PROP_ANIMATABLE: clutter_transition_set_animatable (transition, g_value_get_object (value)); break; case PROP_REMOVE_ON_COMPLETE: clutter_transition_set_remove_on_complete (transition, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_transition_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv; switch (prop_id) { case PROP_INTERVAL: g_value_set_object (value, priv->interval); break; case PROP_ANIMATABLE: g_value_set_object (value, priv->animatable); break; case PROP_REMOVE_ON_COMPLETE: g_value_set_boolean (value, priv->remove_on_complete); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_transition_dispose (GObject *gobject) { ClutterTransitionPrivate *priv = CLUTTER_TRANSITION (gobject)->priv; if (priv->animatable != NULL) clutter_transition_detach (CLUTTER_TRANSITION (gobject), priv->animatable); g_clear_object (&priv->interval); g_clear_object (&priv->animatable); G_OBJECT_CLASS (clutter_transition_parent_class)->dispose (gobject); } static void clutter_transition_class_init (ClutterTransitionClass *klass) { ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); quark_animatable_set = g_quark_from_static_string ("-clutter-transition-animatable-set"); klass->compute_value = clutter_transition_real_compute_value; klass->attached = clutter_transition_real_attached; klass->detached = clutter_transition_real_detached; timeline_class->new_frame = clutter_transition_new_frame; timeline_class->stopped = clutter_transition_stopped; gobject_class->set_property = clutter_transition_set_property; gobject_class->get_property = clutter_transition_get_property; gobject_class->dispose = clutter_transition_dispose; /** * ClutterTransition:interval: * * The #ClutterInterval used to describe the initial and final states * of the transition. * * Since: 1.10 */ obj_props[PROP_INTERVAL] = g_param_spec_object ("interval", P_("Interval"), P_("The interval of values to transition"), CLUTTER_TYPE_INTERVAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterTransition:animatable: * * The #ClutterAnimatable instance currently being animated. * * Since: 1.10 */ obj_props[PROP_ANIMATABLE] = g_param_spec_object ("animatable", P_("Animatable"), P_("The animatable object"), CLUTTER_TYPE_ANIMATABLE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterTransition:remove-on-complete: * * Whether the #ClutterTransition should be automatically detached * from the #ClutterTransition:animatable instance whenever the * #ClutterTimeline::stopped signal is emitted. * * The #ClutterTransition:remove-on-complete property takes into * account the value of the #ClutterTimeline:repeat-count property, * and it only detaches the transition if the transition is not * repeating. * * Since: 1.10 */ obj_props[PROP_REMOVE_ON_COMPLETE] = g_param_spec_boolean ("remove-on-complete", P_("Remove on Complete"), P_("Detach the transition when completed"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_transition_init (ClutterTransition *self) { self->priv = clutter_transition_get_instance_private (self); } /** * clutter_transition_set_interval: * @transition: a #ClutterTransition * @interval: (allow-none): a #ClutterInterval, or %NULL * * Sets the #ClutterTransition:interval property using @interval. * * The @transition will acquire a reference on the @interval, sinking * the floating flag on it if necessary. * * Since: 1.10 */ void clutter_transition_set_interval (ClutterTransition *transition, ClutterInterval *interval) { ClutterTransitionPrivate *priv; g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (interval == NULL || CLUTTER_IS_INTERVAL (interval)); priv = transition->priv; if (priv->interval == interval) return; g_clear_object (&priv->interval); if (interval != NULL) priv->interval = g_object_ref_sink (interval); g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_INTERVAL]); } /** * clutter_transition_get_interval: * @transition: a #ClutterTransition * * Retrieves the interval set using clutter_transition_set_interval() * * Return value: (transfer none): a #ClutterInterval, or %NULL; the returned * interval is owned by the #ClutterTransition and it should not be freed * directly * * Since: 1.10 */ ClutterInterval * clutter_transition_get_interval (ClutterTransition *transition) { g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL); return transition->priv->interval; } /** * clutter_transition_set_animatable: * @transition: a #ClutterTransition * @animatable: (allow-none): a #ClutterAnimatable, or %NULL * * Sets the #ClutterTransition:animatable property. * * The @transition will acquire a reference to the @animatable instance, * and will call the #ClutterTransitionClass.attached() virtual function. * * If an existing #ClutterAnimatable is attached to @transition, the * reference will be released, and the #ClutterTransitionClass.detached() * virtual function will be called. * * Since: 1.10 */ void clutter_transition_set_animatable (ClutterTransition *transition, ClutterAnimatable *animatable) { ClutterTransitionPrivate *priv; g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (animatable == NULL || CLUTTER_IS_ANIMATABLE (animatable)); priv = transition->priv; if (priv->animatable == animatable) return; if (priv->animatable != NULL) clutter_transition_detach (transition, priv->animatable); g_clear_object (&priv->animatable); if (animatable != NULL) { priv->animatable = g_object_ref (animatable); clutter_transition_attach (transition, priv->animatable); } } /** * clutter_transition_get_animatable: * @transition: a #ClutterTransition * * Retrieves the #ClutterAnimatable set using clutter_transition_set_animatable(). * * Return value: (transfer none): a #ClutterAnimatable, or %NULL; the returned * animatable is owned by the #ClutterTransition, and it should not be freed * directly. * * Since: 1.10 */ ClutterAnimatable * clutter_transition_get_animatable (ClutterTransition *transition) { g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), NULL); return transition->priv->animatable; } /** * clutter_transition_set_remove_on_complete: * @transition: a #ClutterTransition * @remove_complete: whether to detach @transition when complete * * Sets whether @transition should be detached from the #ClutterAnimatable * set using clutter_transition_set_animatable() when the * #ClutterTimeline::completed signal is emitted. * * Since: 1.10 */ void clutter_transition_set_remove_on_complete (ClutterTransition *transition, gboolean remove_complete) { g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); remove_complete = !!remove_complete; if (transition->priv->remove_on_complete == remove_complete) return; transition->priv->remove_on_complete = remove_complete; g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_REMOVE_ON_COMPLETE]); } /** * clutter_transition_get_remove_on_complete: * @transition: a #ClutterTransition * * Retrieves the value of the #ClutterTransition:remove-on-complete property. * * Return value: %TRUE if the @transition should be detached when complete, * and %FALSE otherwise * * Since: 1.10 */ gboolean clutter_transition_get_remove_on_complete (ClutterTransition *transition) { g_return_val_if_fail (CLUTTER_IS_TRANSITION (transition), FALSE); return transition->priv->remove_on_complete; } typedef void (* IntervalSetFunc) (ClutterInterval *interval, const GValue *value); static inline void clutter_transition_set_value (ClutterTransition *transition, IntervalSetFunc interval_set_func, const GValue *value) { ClutterTransitionPrivate *priv = transition->priv; GType interval_type; if (priv->interval == NULL) { priv->interval = clutter_interval_new_with_values (G_VALUE_TYPE (value), NULL, NULL); g_object_ref_sink (priv->interval); } interval_type = clutter_interval_get_value_type (priv->interval); if (!g_type_is_a (G_VALUE_TYPE (value), interval_type)) { if (g_value_type_compatible (G_VALUE_TYPE (value), interval_type)) { interval_set_func (priv->interval, value); return; } if (g_value_type_transformable (G_VALUE_TYPE (value), interval_type)) { GValue transform = G_VALUE_INIT; g_value_init (&transform, interval_type); if (g_value_transform (value, &transform)) interval_set_func (priv->interval, &transform); else { g_warning ("%s: Unable to convert a value of type '%s' into " "the value type '%s' of the interval used by the " "transition.", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (interval_type)); } g_value_unset (&transform); } } else interval_set_func (priv->interval, value); } /** * clutter_transition_set_from_value: (rename-to clutter_transition_set_from) * @transition: a #ClutterTransition * @value: a #GValue with the initial value of the transition * * Sets the initial value of the transition. * * This is a convenience function that will either create the * #ClutterInterval used by @transition, or will update it if * the #ClutterTransition:interval is already set. * * This function will copy the contents of @value, so it is * safe to call g_value_unset() after it returns. * * If @transition already has a #ClutterTransition:interval set, * then @value must hold the same type, or a transformable type, * as the interval's #ClutterInterval:value-type property. * * This function is meant to be used by language bindings. * * Since: 1.12 */ void clutter_transition_set_from_value (ClutterTransition *transition, const GValue *value) { g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (G_IS_VALUE (value)); clutter_transition_set_value (transition, clutter_interval_set_initial_value, value); } /** * clutter_transition_set_to_value: (rename-to clutter_transition_set_to) * @transition: a #ClutterTransition * @value: a #GValue with the final value of the transition * * Sets the final value of the transition. * * This is a convenience function that will either create the * #ClutterInterval used by @transition, or will update it if * the #ClutterTransition:interval is already set. * * This function will copy the contents of @value, so it is * safe to call g_value_unset() after it returns. * * If @transition already has a #ClutterTransition:interval set, * then @value must hold the same type, or a transformable type, * as the interval's #ClutterInterval:value-type property. * * This function is meant to be used by language bindings. * * Since: 1.12 */ void clutter_transition_set_to_value (ClutterTransition *transition, const GValue *value) { g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (G_IS_VALUE (value)); clutter_transition_set_value (transition, clutter_interval_set_final_value, value); } /** * clutter_transition_set_from: (skip) * @transition: a #ClutterTransition * @value_type: the type of the value to set * @...: the initial value * * Sets the initial value of the transition. * * This is a convenience function that will either create the * #ClutterInterval used by @transition, or will update it if * the #ClutterTransition:interval is already set. * * If @transition already has a #ClutterTransition:interval set, * then @value must hold the same type, or a transformable type, * as the interval's #ClutterInterval:value-type property. * * This is a convenience function for the C API; language bindings * should use clutter_transition_set_from_value() instead. * * Since: 1.12 */ void clutter_transition_set_from (ClutterTransition *transition, GType value_type, ...) { GValue value = G_VALUE_INIT; gchar *error = NULL; va_list args; g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (value_type != G_TYPE_INVALID); va_start (args, value_type); G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error); va_end (args); if (error != NULL) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); return; } clutter_transition_set_value (transition, clutter_interval_set_initial_value, &value); g_value_unset (&value); } /** * clutter_transition_set_to: (skip) * @transition: a #ClutterTransition * @value_type: the type of the value to set * @...: the final value * * Sets the final value of the transition. * * This is a convenience function that will either create the * #ClutterInterval used by @transition, or will update it if * the #ClutterTransition:interval is already set. * * If @transition already has a #ClutterTransition:interval set, * then @value must hold the same type, or a transformable type, * as the interval's #ClutterInterval:value-type property. * * This is a convenience function for the C API; language bindings * should use clutter_transition_set_to_value() instead. * * Since: 1.12 */ void clutter_transition_set_to (ClutterTransition *transition, GType value_type, ...) { GValue value = G_VALUE_INIT; gchar *error = NULL; va_list args; g_return_if_fail (CLUTTER_IS_TRANSITION (transition)); g_return_if_fail (value_type != G_TYPE_INVALID); va_start (args, value_type); G_VALUE_COLLECT_INIT (&value, value_type, args, 0, &error); va_end (args); if (error != NULL) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); return; } clutter_transition_set_value (transition, clutter_interval_set_final_value, &value); g_value_unset (&value); } muffin-6.4.1/clutter/clutter/clutter-enum-types.h.in0000664000175000017500000000126214723361714021453 0ustar fabiofabio/*** BEGIN file-header ***/ #ifndef __CLUTTER_ENUM_TYPES_H__ #define __CLUTTER_ENUM_TYPES_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN value-header ***/ CLUTTER_EXPORT GType @enum_name@_get_type (void) G_GNUC_CONST; #define CLUTTER_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__CLUTTER_ENUM_TYPES_H__ */ /*** END file-tail ***/ muffin-6.4.1/clutter/clutter/clutter-rotate-action.c0000664000175000017500000001645714723361714021520 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Lionel Landwerlin */ /** * SECTION:clutter-rotate-action * @Title: ClutterRotateAction * @Short_Description: Action to rotate an actor * * #ClutterRotateAction is a sub-class of #ClutterGestureAction that implements * the logic for recognizing rotate gestures using two touch points. * * Since: 1.12 */ #include "clutter-build-config.h" #include #include "clutter-rotate-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-gesture-action-private.h" #include "clutter-marshal.h" #include "clutter-private.h" struct _ClutterRotateActionPrivate { gfloat initial_vector[2]; gdouble initial_vector_norm; gdouble initial_rotation; }; enum { ROTATE, LAST_SIGNAL }; static guint rotate_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterRotateAction, clutter_rotate_action, CLUTTER_TYPE_GESTURE_ACTION) static gboolean clutter_rotate_action_real_rotate (ClutterRotateAction *action, ClutterActor *actor, gdouble angle) { clutter_actor_set_rotation_angle (actor, CLUTTER_Z_AXIS, action->priv->initial_rotation + angle); return TRUE; } static gboolean clutter_rotate_action_gesture_begin (ClutterGestureAction *action, ClutterActor *actor) { ClutterRotateActionPrivate *priv = CLUTTER_ROTATE_ACTION (action)->priv; gfloat p1[2], p2[2]; /* capture initial vector */ clutter_gesture_action_get_motion_coords (action, 0, &p1[0], &p1[1]); clutter_gesture_action_get_motion_coords (action, 1, &p2[0], &p2[1]); priv->initial_vector[0] = p2[0] - p1[0]; priv->initial_vector[1] = p2[1] - p1[1]; priv->initial_vector_norm = sqrt (priv->initial_vector[0] * priv->initial_vector[0] + priv->initial_vector[1] * priv->initial_vector[1]); priv->initial_rotation = clutter_actor_get_rotation_angle (actor, CLUTTER_Z_AXIS); return TRUE; } static gboolean clutter_rotate_action_gesture_progress (ClutterGestureAction *action, ClutterActor *actor) { ClutterRotateActionPrivate *priv = CLUTTER_ROTATE_ACTION (action)->priv; gfloat p1[2], p2[2]; gfloat vector[2]; gboolean retval; /* capture current vector */ clutter_gesture_action_get_motion_coords (action, 0, &p1[0], &p1[1]); clutter_gesture_action_get_motion_coords (action, 1, &p2[0], &p2[1]); vector[0] = p2[0] - p1[0]; vector[1] = p2[1] - p1[1]; if ((vector[0] == priv->initial_vector[0]) && (vector[1] == priv->initial_vector[1])) { g_signal_emit (action, rotate_signals[ROTATE], 0, actor, (gdouble) 0.0, &retval); } else { gfloat mult[2]; gfloat norm; gdouble angle; /* Computes angle between the 2 initial touch points and the current position of the 2 touch points. */ norm = sqrt (vector[0] * vector[0] + vector[1] * vector[1]); norm = (priv->initial_vector[0] * vector[0] + priv->initial_vector[1] * vector[1]) / (priv->initial_vector_norm * norm); if ((norm >= -1.0) && (norm <= 1.0)) angle = acos (norm); else angle = 0; /* The angle given is comprise between 0 and 180 degrees, we need some logic on top to get a value between 0 and 360. */ mult[0] = priv->initial_vector[0] * vector[1] - priv->initial_vector[1] * vector[0]; mult[1] = priv->initial_vector[1] * vector[0] - priv->initial_vector[0] * vector[1]; if (mult[0] < 0) angle = -angle; /* Convert radians to degrees */ angle = angle * 180.0 / G_PI; g_signal_emit (action, rotate_signals[ROTATE], 0, actor, angle, &retval); } return TRUE; } static void clutter_rotate_action_gesture_cancel (ClutterGestureAction *action, ClutterActor *actor) { gboolean retval; g_signal_emit (action, rotate_signals[ROTATE], 0, actor, (gdouble) 0.0, &retval); } static void clutter_rotate_action_constructed (GObject *gobject) { ClutterGestureAction *gesture; gesture = CLUTTER_GESTURE_ACTION (gobject); clutter_gesture_action_set_threshold_trigger_edge (gesture, CLUTTER_GESTURE_TRIGGER_EDGE_NONE); } static void clutter_rotate_action_class_init (ClutterRotateActionClass *klass) { ClutterGestureActionClass *gesture_class = CLUTTER_GESTURE_ACTION_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); klass->rotate = clutter_rotate_action_real_rotate; object_class->constructed = clutter_rotate_action_constructed; gesture_class->gesture_begin = clutter_rotate_action_gesture_begin; gesture_class->gesture_progress = clutter_rotate_action_gesture_progress; gesture_class->gesture_cancel = clutter_rotate_action_gesture_cancel; /** * ClutterRotateAction::rotate: * @action: the #ClutterRotateAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @angle: the difference of angle of rotation between the initial * rotation and the current rotation * * The ::rotate signal is emitted when a rotate gesture is * recognized on the attached actor and when the gesture is * cancelled (in this case with an angle value of 0). * * Return value: %TRUE if the rotation should continue, and %FALSE if * the rotation should be cancelled. * * Since: 1.12 */ rotate_signals[ROTATE] = g_signal_new (I_("rotate"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterRotateActionClass, rotate), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_DOUBLE, G_TYPE_BOOLEAN, 2, CLUTTER_TYPE_ACTOR, G_TYPE_DOUBLE); } static void clutter_rotate_action_init (ClutterRotateAction *self) { ClutterGestureAction *gesture; self->priv = clutter_rotate_action_get_instance_private (self); gesture = CLUTTER_GESTURE_ACTION (self); clutter_gesture_action_set_n_touch_points (gesture, 2); } /** * clutter_rotate_action_new: * * Creates a new #ClutterRotateAction instance * * Return value: the newly created #ClutterRotateAction * * Since: 1.12 */ ClutterAction * clutter_rotate_action_new (void) { return g_object_new (CLUTTER_TYPE_ROTATE_ACTION, NULL); } muffin-6.4.1/clutter/clutter/clutter-enums.h0000664000175000017500000015242514723361714020077 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_ENUMS_H__ #define __CLUTTER_ENUMS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS /** * ClutterGravity: * @CLUTTER_GRAVITY_NONE: Do not apply any gravity * @CLUTTER_GRAVITY_NORTH: Scale from topmost downwards * @CLUTTER_GRAVITY_NORTH_EAST: Scale from the top right corner * @CLUTTER_GRAVITY_EAST: Scale from the right side * @CLUTTER_GRAVITY_SOUTH_EAST: Scale from the bottom right corner * @CLUTTER_GRAVITY_SOUTH: Scale from the bottom upwards * @CLUTTER_GRAVITY_SOUTH_WEST: Scale from the bottom left corner * @CLUTTER_GRAVITY_WEST: Scale from the left side * @CLUTTER_GRAVITY_NORTH_WEST: Scale from the top left corner * @CLUTTER_GRAVITY_CENTER: Scale from the center. * * Gravity of the scaling operations. When a gravity different than * %CLUTTER_GRAVITY_NONE is used, an actor is scaled keeping the position * of the specified portion at the same coordinates. * * Since: 0.2 * * Deprecated: 1.22: Use the normalized #ClutterActor pivot point instead */ typedef enum /*< prefix=CLUTTER_GRAVITY >*/ { CLUTTER_GRAVITY_NONE = 0, CLUTTER_GRAVITY_NORTH, CLUTTER_GRAVITY_NORTH_EAST, CLUTTER_GRAVITY_EAST, CLUTTER_GRAVITY_SOUTH_EAST, CLUTTER_GRAVITY_SOUTH, CLUTTER_GRAVITY_SOUTH_WEST, CLUTTER_GRAVITY_WEST, CLUTTER_GRAVITY_NORTH_WEST, CLUTTER_GRAVITY_CENTER } ClutterGravity; /** * ClutterRotateAxis: * @CLUTTER_X_AXIS: Rotate around the X axis * @CLUTTER_Y_AXIS: Rotate around the Y axis * @CLUTTER_Z_AXIS: Rotate around the Z axis * * Axis of a rotation. * * Since: 0.4 */ typedef enum /*< prefix=CLUTTER >*/ { CLUTTER_X_AXIS, CLUTTER_Y_AXIS, CLUTTER_Z_AXIS } ClutterRotateAxis; /** * ClutterRotateDirection: * @CLUTTER_ROTATE_CW: Clockwise rotation * @CLUTTER_ROTATE_CCW: Counter-clockwise rotation * * Direction of a rotation. * * Since: 0.4 * * Deprecated: 1.22 */ typedef enum /*< prefix=CLUTTER_ROTATE >*/ { CLUTTER_ROTATE_CW, CLUTTER_ROTATE_CCW } ClutterRotateDirection; /** * ClutterRequestMode: * @CLUTTER_REQUEST_HEIGHT_FOR_WIDTH: Height for width requests * @CLUTTER_REQUEST_WIDTH_FOR_HEIGHT: Width for height requests * @CLUTTER_REQUEST_CONTENT_SIZE: Use the preferred size of the * #ClutterContent, if it has any (available since 1.22) * * Specifies the type of requests for a #ClutterActor. * * Since: 0.8 */ typedef enum /*< prefix=CLUTTER_REQUEST >*/ { CLUTTER_REQUEST_HEIGHT_FOR_WIDTH, CLUTTER_REQUEST_WIDTH_FOR_HEIGHT, CLUTTER_REQUEST_CONTENT_SIZE } ClutterRequestMode; /** * ClutterAnimationMode: * @CLUTTER_CUSTOM_MODE: custom progress function * @CLUTTER_LINEAR: linear tweening * @CLUTTER_EASE_IN_QUAD: quadratic tweening * @CLUTTER_EASE_OUT_QUAD: quadratic tweening, inverse of * %CLUTTER_EASE_IN_QUAD * @CLUTTER_EASE_IN_OUT_QUAD: quadratic tweening, combininig * %CLUTTER_EASE_IN_QUAD and %CLUTTER_EASE_OUT_QUAD * @CLUTTER_EASE_IN_CUBIC: cubic tweening * @CLUTTER_EASE_OUT_CUBIC: cubic tweening, invers of * %CLUTTER_EASE_IN_CUBIC * @CLUTTER_EASE_IN_OUT_CUBIC: cubic tweening, combining * %CLUTTER_EASE_IN_CUBIC and %CLUTTER_EASE_OUT_CUBIC * @CLUTTER_EASE_IN_QUART: quartic tweening * @CLUTTER_EASE_OUT_QUART: quartic tweening, inverse of * %CLUTTER_EASE_IN_QUART * @CLUTTER_EASE_IN_OUT_QUART: quartic tweening, combining * %CLUTTER_EASE_IN_QUART and %CLUTTER_EASE_OUT_QUART * @CLUTTER_EASE_IN_QUINT: quintic tweening * @CLUTTER_EASE_OUT_QUINT: quintic tweening, inverse of * %CLUTTER_EASE_IN_QUINT * @CLUTTER_EASE_IN_OUT_QUINT: fifth power tweening, combining * %CLUTTER_EASE_IN_QUINT and %CLUTTER_EASE_OUT_QUINT * @CLUTTER_EASE_IN_SINE: sinusoidal tweening * @CLUTTER_EASE_OUT_SINE: sinusoidal tweening, inverse of * %CLUTTER_EASE_IN_SINE * @CLUTTER_EASE_IN_OUT_SINE: sine wave tweening, combining * %CLUTTER_EASE_IN_SINE and %CLUTTER_EASE_OUT_SINE * @CLUTTER_EASE_IN_EXPO: exponential tweening * @CLUTTER_EASE_OUT_EXPO: exponential tweening, inverse of * %CLUTTER_EASE_IN_EXPO * @CLUTTER_EASE_IN_OUT_EXPO: exponential tweening, combining * %CLUTTER_EASE_IN_EXPO and %CLUTTER_EASE_OUT_EXPO * @CLUTTER_EASE_IN_CIRC: circular tweening * @CLUTTER_EASE_OUT_CIRC: circular tweening, inverse of * %CLUTTER_EASE_IN_CIRC * @CLUTTER_EASE_IN_OUT_CIRC: circular tweening, combining * %CLUTTER_EASE_IN_CIRC and %CLUTTER_EASE_OUT_CIRC * @CLUTTER_EASE_IN_ELASTIC: elastic tweening, with offshoot on start * @CLUTTER_EASE_OUT_ELASTIC: elastic tweening, with offshoot on end * @CLUTTER_EASE_IN_OUT_ELASTIC: elastic tweening with offshoot on both ends * @CLUTTER_EASE_IN_BACK: overshooting cubic tweening, with * backtracking on start * @CLUTTER_EASE_OUT_BACK: overshooting cubic tweening, with * backtracking on end * @CLUTTER_EASE_IN_OUT_BACK: overshooting cubic tweening, with * backtracking on both ends * @CLUTTER_EASE_IN_BOUNCE: exponentially decaying parabolic (bounce) * tweening, with bounce on start * @CLUTTER_EASE_OUT_BOUNCE: exponentially decaying parabolic (bounce) * tweening, with bounce on end * @CLUTTER_EASE_IN_OUT_BOUNCE: exponentially decaying parabolic (bounce) * tweening, with bounce on both ends * @CLUTTER_STEPS: parametrized step function; see clutter_timeline_set_step_progress() * for further details. (Since 1.12) * @CLUTTER_STEP_START: equivalent to %CLUTTER_STEPS with a number of steps * equal to 1, and a step mode of %CLUTTER_STEP_MODE_START. (Since 1.12) * @CLUTTER_STEP_END: equivalent to %CLUTTER_STEPS with a number of steps * equal to 1, and a step mode of %CLUTTER_STEP_MODE_END. (Since 1.12) * @CLUTTER_CUBIC_BEZIER: cubic bezier between (0, 0) and (1, 1) with two * control points; see clutter_timeline_set_cubic_bezier_progress(). (Since 1.12) * @CLUTTER_EASE: equivalent to %CLUTTER_CUBIC_BEZIER with control points * in (0.25, 0.1) and (0.25, 1.0). (Since 1.12) * @CLUTTER_EASE_IN: equivalent to %CLUTTER_CUBIC_BEZIER with control points * in (0.42, 0) and (1.0, 1.0). (Since 1.12) * @CLUTTER_EASE_OUT: equivalent to %CLUTTER_CUBIC_BEZIER with control points * in (0, 0) and (0.58, 1.0). (Since 1.12) * @CLUTTER_EASE_IN_OUT: equivalent to %CLUTTER_CUBIC_BEZIER with control points * in (0.42, 0) and (0.58, 1.0). (Since 1.12) * @CLUTTER_ANIMATION_LAST: last animation mode, used as a guard for * registered global alpha functions * * The animation modes used by #ClutterAlpha and #ClutterAnimation. This * enumeration can be expanded in later versions of Clutter. * *
* Easing modes provided by Clutter * *
* * Every global alpha function registered using clutter_alpha_register_func() * or clutter_alpha_register_closure() will have a logical id greater than * %CLUTTER_ANIMATION_LAST. * * Since: 1.0 */ typedef enum { CLUTTER_CUSTOM_MODE = 0, /* linear */ CLUTTER_LINEAR, /* quadratic */ CLUTTER_EASE_IN_QUAD, CLUTTER_EASE_OUT_QUAD, CLUTTER_EASE_IN_OUT_QUAD, /* cubic */ CLUTTER_EASE_IN_CUBIC, CLUTTER_EASE_OUT_CUBIC, CLUTTER_EASE_IN_OUT_CUBIC, /* quartic */ CLUTTER_EASE_IN_QUART, CLUTTER_EASE_OUT_QUART, CLUTTER_EASE_IN_OUT_QUART, /* quintic */ CLUTTER_EASE_IN_QUINT, CLUTTER_EASE_OUT_QUINT, CLUTTER_EASE_IN_OUT_QUINT, /* sinusoidal */ CLUTTER_EASE_IN_SINE, CLUTTER_EASE_OUT_SINE, CLUTTER_EASE_IN_OUT_SINE, /* exponential */ CLUTTER_EASE_IN_EXPO, CLUTTER_EASE_OUT_EXPO, CLUTTER_EASE_IN_OUT_EXPO, /* circular */ CLUTTER_EASE_IN_CIRC, CLUTTER_EASE_OUT_CIRC, CLUTTER_EASE_IN_OUT_CIRC, /* elastic */ CLUTTER_EASE_IN_ELASTIC, CLUTTER_EASE_OUT_ELASTIC, CLUTTER_EASE_IN_OUT_ELASTIC, /* overshooting cubic */ CLUTTER_EASE_IN_BACK, CLUTTER_EASE_OUT_BACK, CLUTTER_EASE_IN_OUT_BACK, /* exponentially decaying parabolic */ CLUTTER_EASE_IN_BOUNCE, CLUTTER_EASE_OUT_BOUNCE, CLUTTER_EASE_IN_OUT_BOUNCE, /* step functions (see css3-transitions) */ CLUTTER_STEPS, CLUTTER_STEP_START, /* steps(1, start) */ CLUTTER_STEP_END, /* steps(1, end) */ /* cubic bezier (see css3-transitions) */ CLUTTER_CUBIC_BEZIER, CLUTTER_EASE, CLUTTER_EASE_IN, CLUTTER_EASE_OUT, CLUTTER_EASE_IN_OUT, /* guard, before registered alpha functions */ CLUTTER_ANIMATION_LAST } ClutterAnimationMode; /** * ClutterTextDirection: * @CLUTTER_TEXT_DIRECTION_DEFAULT: Use the default setting, as returned * by clutter_get_default_text_direction() * @CLUTTER_TEXT_DIRECTION_LTR: Use left-to-right text direction * @CLUTTER_TEXT_DIRECTION_RTL: Use right-to-left text direction * * The text direction to be used by #ClutterActors * * Since: 1.2 */ typedef enum { CLUTTER_TEXT_DIRECTION_DEFAULT, CLUTTER_TEXT_DIRECTION_LTR, CLUTTER_TEXT_DIRECTION_RTL } ClutterTextDirection; /** * ClutterShaderType: * @CLUTTER_VERTEX_SHADER: a vertex shader * @CLUTTER_FRAGMENT_SHADER: a fragment shader * * The type of GLSL shader program * * Since: 1.4 */ typedef enum { CLUTTER_VERTEX_SHADER, CLUTTER_FRAGMENT_SHADER } ClutterShaderType; /** * ClutterModifierType: * @CLUTTER_SHIFT_MASK: Mask applied by the Shift key * @CLUTTER_LOCK_MASK: Mask applied by the Caps Lock key * @CLUTTER_CONTROL_MASK: Mask applied by the Control key * @CLUTTER_MOD1_MASK: Mask applied by the first Mod key * @CLUTTER_MOD2_MASK: Mask applied by the second Mod key * @CLUTTER_MOD3_MASK: Mask applied by the third Mod key * @CLUTTER_MOD4_MASK: Mask applied by the fourth Mod key * @CLUTTER_MOD5_MASK: Mask applied by the fifth Mod key * @CLUTTER_BUTTON1_MASK: Mask applied by the first pointer button * @CLUTTER_BUTTON2_MASK: Mask applied by the second pointer button * @CLUTTER_BUTTON3_MASK: Mask applied by the third pointer button * @CLUTTER_BUTTON4_MASK: Mask applied by the fourth pointer button * @CLUTTER_BUTTON5_MASK: Mask applied by the fifth pointer button * @CLUTTER_SUPER_MASK: Mask applied by the Super key * @CLUTTER_HYPER_MASK: Mask applied by the Hyper key * @CLUTTER_META_MASK: Mask applied by the Meta key * @CLUTTER_RELEASE_MASK: Mask applied during release * @CLUTTER_MODIFIER_MASK: A mask covering all modifier types * * Masks applied to a #ClutterEvent by modifiers. * * Note that Clutter may add internal values to events which include * reserved values such as %CLUTTER_MODIFIER_RESERVED_13_MASK. Your code * should preserve and ignore them. You can use %CLUTTER_MODIFIER_MASK to * remove all reserved values. * * Since: 0.4 */ typedef enum { CLUTTER_SHIFT_MASK = 1 << 0, CLUTTER_LOCK_MASK = 1 << 1, CLUTTER_CONTROL_MASK = 1 << 2, CLUTTER_MOD1_MASK = 1 << 3, CLUTTER_MOD2_MASK = 1 << 4, CLUTTER_MOD3_MASK = 1 << 5, CLUTTER_MOD4_MASK = 1 << 6, CLUTTER_MOD5_MASK = 1 << 7, CLUTTER_BUTTON1_MASK = 1 << 8, CLUTTER_BUTTON2_MASK = 1 << 9, CLUTTER_BUTTON3_MASK = 1 << 10, CLUTTER_BUTTON4_MASK = 1 << 11, CLUTTER_BUTTON5_MASK = 1 << 12, #ifndef __GTK_DOC_IGNORE__ CLUTTER_MODIFIER_RESERVED_13_MASK = 1 << 13, CLUTTER_MODIFIER_RESERVED_14_MASK = 1 << 14, CLUTTER_MODIFIER_RESERVED_15_MASK = 1 << 15, CLUTTER_MODIFIER_RESERVED_16_MASK = 1 << 16, CLUTTER_MODIFIER_RESERVED_17_MASK = 1 << 17, CLUTTER_MODIFIER_RESERVED_18_MASK = 1 << 18, CLUTTER_MODIFIER_RESERVED_19_MASK = 1 << 19, CLUTTER_MODIFIER_RESERVED_20_MASK = 1 << 20, CLUTTER_MODIFIER_RESERVED_21_MASK = 1 << 21, CLUTTER_MODIFIER_RESERVED_22_MASK = 1 << 22, CLUTTER_MODIFIER_RESERVED_23_MASK = 1 << 23, CLUTTER_MODIFIER_RESERVED_24_MASK = 1 << 24, CLUTTER_MODIFIER_RESERVED_25_MASK = 1 << 25, #endif CLUTTER_SUPER_MASK = 1 << 26, CLUTTER_HYPER_MASK = 1 << 27, CLUTTER_META_MASK = 1 << 28, #ifndef __GTK_DOC_IGNORE__ CLUTTER_MODIFIER_RESERVED_29_MASK = 1 << 29, #endif CLUTTER_RELEASE_MASK = 1 << 30, /* Combination of CLUTTER_SHIFT_MASK..CLUTTER_BUTTON5_MASK + CLUTTER_SUPER_MASK + CLUTTER_HYPER_MASK + CLUTTER_META_MASK + CLUTTER_RELEASE_MASK */ CLUTTER_MODIFIER_MASK = 0x5c001fff } ClutterModifierType; /** * ClutterKeyboardA11yFlags: * @CLUTTER_A11Y_KEYBOARD_ENABLED: * @CLUTTER_A11Y_TIMEOUT_ENABLED: * @CLUTTER_A11Y_MOUSE_KEYS_ENABLED: * @CLUTTER_A11Y_SLOW_KEYS_ENABLED: * @CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS: * @CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT: * @CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT: * @CLUTTER_A11Y_BOUNCE_KEYS_ENABLED: * @CLUTTER_A11Y_BOUNCE_KEYS_BEEP_REJECT: * @CLUTTER_A11Y_TOGGLE_KEYS_ENABLED: * @CLUTTER_A11Y_STICKY_KEYS_ENABLED: * @CLUTTER_A11Y_STICKY_KEYS_TWO_KEY_OFF: * @CLUTTER_A11Y_STICKY_KEYS_BEEP: * @CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP: * * Keyboard accessibility features applied to a ClutterInputDevice keyboard. * */ typedef enum { CLUTTER_A11Y_KEYBOARD_ENABLED = 1 << 0, CLUTTER_A11Y_TIMEOUT_ENABLED = 1 << 1, CLUTTER_A11Y_MOUSE_KEYS_ENABLED = 1 << 2, CLUTTER_A11Y_SLOW_KEYS_ENABLED = 1 << 3, CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS = 1 << 4, CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT = 1 << 5, CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT = 1 << 6, CLUTTER_A11Y_BOUNCE_KEYS_ENABLED = 1 << 7, CLUTTER_A11Y_BOUNCE_KEYS_BEEP_REJECT = 1 << 8, CLUTTER_A11Y_TOGGLE_KEYS_ENABLED = 1 << 9, CLUTTER_A11Y_STICKY_KEYS_ENABLED = 1 << 10, CLUTTER_A11Y_STICKY_KEYS_TWO_KEY_OFF = 1 << 11, CLUTTER_A11Y_STICKY_KEYS_BEEP = 1 << 12, CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP = 1 << 13, } ClutterKeyboardA11yFlags; /** * ClutterPointerA11yFlags: * @CLUTTER_A11Y_POINTER_ENABLED: * @CLUTTER_A11Y_SECONDARY_CLICK_ENABLED: * @CLUTTER_A11Y_DWELL_ENABLED: * * Pointer accessibility features applied to a ClutterInputDevice pointer. * */ typedef enum { CLUTTER_A11Y_SECONDARY_CLICK_ENABLED = 1 << 0, CLUTTER_A11Y_DWELL_ENABLED = 1 << 1, } ClutterPointerA11yFlags; /** * ClutterPointerA11yDwellClickType: * @CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE: Internal use only * @CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY: * @CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY: * @CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE: * @CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE: * @CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG: * * Dwell click types. * */ typedef enum { CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE, CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY, CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY, CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE, CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG, } ClutterPointerA11yDwellClickType; /** * ClutterPointerA11yDwellDirection: * @CLUTTER_A11Y_DWELL_DIRECTION_NONE: * @CLUTTER_A11Y_DWELL_DIRECTION_LEFT: * @CLUTTER_A11Y_DWELL_DIRECTION_RIGHT: * @CLUTTER_A11Y_DWELL_DIRECTION_UP: * @CLUTTER_A11Y_DWELL_DIRECTION_DOWN: * * Dwell gesture directions. * */ typedef enum { CLUTTER_A11Y_DWELL_DIRECTION_NONE, CLUTTER_A11Y_DWELL_DIRECTION_LEFT, CLUTTER_A11Y_DWELL_DIRECTION_RIGHT, CLUTTER_A11Y_DWELL_DIRECTION_UP, CLUTTER_A11Y_DWELL_DIRECTION_DOWN, } ClutterPointerA11yDwellDirection; /** * ClutterPointerA11yDwellMode: * @CLUTTER_A11Y_DWELL_MODE_WINDOW: * @CLUTTER_A11Y_DWELL_MODE_GESTURE: * * Dwell mode. * */ typedef enum { CLUTTER_A11Y_DWELL_MODE_WINDOW, CLUTTER_A11Y_DWELL_MODE_GESTURE, } ClutterPointerA11yDwellMode; /** * ClutterPointerA11yTimeoutType: * @CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK: * @CLUTTER_A11Y_TIMEOUT_TYPE_DWELL: * @CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE: * * Pointer accessibility timeout type. * */ typedef enum { CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK, CLUTTER_A11Y_TIMEOUT_TYPE_DWELL, CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE, } ClutterPointerA11yTimeoutType; /** * ClutterActorFlags: * @CLUTTER_ACTOR_MAPPED: the actor will be painted (is visible, and inside * a toplevel, and all parents visible) * @CLUTTER_ACTOR_REALIZED: the resources associated to the actor have been * allocated * @CLUTTER_ACTOR_REACTIVE: the actor 'reacts' to mouse events emmitting event * signals * @CLUTTER_ACTOR_VISIBLE: the actor has been shown by the application program * @CLUTTER_ACTOR_NO_LAYOUT: the actor provides an explicit layout management * policy for its children; this flag will prevent Clutter from automatic * queueing of relayout and will defer all layouting to the actor itself * * Flags used to signal the state of an actor. */ typedef enum /*< prefix=CLUTTER_ACTOR >*/ { CLUTTER_ACTOR_MAPPED = 1 << 1, CLUTTER_ACTOR_REALIZED = 1 << 2, CLUTTER_ACTOR_REACTIVE = 1 << 3, CLUTTER_ACTOR_VISIBLE = 1 << 4, CLUTTER_ACTOR_NO_LAYOUT = 1 << 5 } ClutterActorFlags; /** * ClutterOffscreenRedirect: * @CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY: Only redirect * the actor if it is semi-transparent and its has_overlaps() * virtual returns %TRUE. * @CLUTTER_OFFSCREEN_REDIRECT_ALWAYS: Always redirect the actor to an * offscreen buffer even if it is fully opaque. * @CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE: Only redirect the actor if it is the * most efficient thing to do based on its recent repaint behaviour. That * means when its contents are changing less frequently than it's being used * on stage. * * Possible flags to pass to clutter_actor_set_offscreen_redirect(). * * Since: 1.8 */ typedef enum /*< prefix=CLUTTER_OFFSCREEN_REDIRECT >*/ { CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY = 1 << 0, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS = 1 << 1, CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE = 1 << 2 } ClutterOffscreenRedirect; /** * ClutterAllocationFlags: * @CLUTTER_ALLOCATION_NONE: No flag set * @CLUTTER_ABSOLUTE_ORIGIN_CHANGED: Whether the absolute origin of the * actor has changed; this implies that any ancestor of the actor has * been moved. * @CLUTTER_DELEGATE_LAYOUT: Whether the allocation should be delegated * to the #ClutterLayoutManager instance stored inside the * #ClutterActor:layout-manager property of #ClutterActor. This flag * should only be used if you are subclassing #ClutterActor and * overriding the #ClutterActorClass.allocate() virtual function, but * you wish to use the default implementation of the virtual function * inside #ClutterActor. Added in Clutter 1.10. * * Flags passed to the #ClutterActorClass.allocate() virtual function * and to the clutter_actor_allocate() function. * * Since: 1.0 */ typedef enum { CLUTTER_ALLOCATION_NONE = 0, CLUTTER_ABSOLUTE_ORIGIN_CHANGED = 1 << 1, CLUTTER_DELEGATE_LAYOUT = 1 << 2 } ClutterAllocationFlags; /** * ClutterAlignAxis: * @CLUTTER_ALIGN_X_AXIS: Maintain the alignment on the X axis * @CLUTTER_ALIGN_Y_AXIS: Maintain the alignment on the Y axis * @CLUTTER_ALIGN_BOTH: Maintain the alignment on both the X and Y axis * * Specifies the axis on which #ClutterAlignConstraint should maintain * the alignment. * * Since: 1.4 */ typedef enum /*< prefix=CLUTTER_ALIGN >*/ { CLUTTER_ALIGN_X_AXIS, CLUTTER_ALIGN_Y_AXIS, CLUTTER_ALIGN_BOTH } ClutterAlignAxis; /** * ClutterInterpolation: * @CLUTTER_INTERPOLATION_LINEAR: linear interpolation * @CLUTTER_INTERPOLATION_CUBIC: cubic interpolation * * The mode of interpolation between key frames * * Since: 1.2 * * Deprecated: 1.22 */ typedef enum { CLUTTER_INTERPOLATION_LINEAR, CLUTTER_INTERPOLATION_CUBIC } ClutterInterpolation; /** * ClutterBinAlignment: * @CLUTTER_BIN_ALIGNMENT_FIXED: Fixed position alignment; the * #ClutterBinLayout will honour the fixed position provided * by the actors themselves when allocating them * @CLUTTER_BIN_ALIGNMENT_FILL: Fill the allocation size * @CLUTTER_BIN_ALIGNMENT_START: Position the actors at the top * or left side of the container, depending on the axis * @CLUTTER_BIN_ALIGNMENT_END: Position the actors at the bottom * or right side of the container, depending on the axis * @CLUTTER_BIN_ALIGNMENT_CENTER: Position the actors at the * center of the container, depending on the axis * * The alignment policies available on each axis for #ClutterBinLayout * * Since: 1.2 * * Deprecated: 1.12: Use #ClutterActorAlign and the #ClutterActor * API instead */ typedef enum { CLUTTER_BIN_ALIGNMENT_FIXED, CLUTTER_BIN_ALIGNMENT_FILL, CLUTTER_BIN_ALIGNMENT_START, CLUTTER_BIN_ALIGNMENT_END, CLUTTER_BIN_ALIGNMENT_CENTER } ClutterBinAlignment; /** * ClutterBindCoordinate: * @CLUTTER_BIND_X: Bind the X coordinate * @CLUTTER_BIND_Y: Bind the Y coordinate * @CLUTTER_BIND_WIDTH: Bind the width * @CLUTTER_BIND_HEIGHT: Bind the height * @CLUTTER_BIND_POSITION: Equivalent to to %CLUTTER_BIND_X and * %CLUTTER_BIND_Y (added in Clutter 1.6) * @CLUTTER_BIND_SIZE: Equivalent to %CLUTTER_BIND_WIDTH and * %CLUTTER_BIND_HEIGHT (added in Clutter 1.6) * @CLUTTER_BIND_ALL: Equivalent to %CLUTTER_BIND_POSITION and * %CLUTTER_BIND_SIZE (added in Clutter 1.10) * * Specifies which property should be used in a binding * * Since: 1.4 */ typedef enum /*< prefix=CLUTTER_BIND >*/ { CLUTTER_BIND_X, CLUTTER_BIND_Y, CLUTTER_BIND_WIDTH, CLUTTER_BIND_HEIGHT, CLUTTER_BIND_POSITION, CLUTTER_BIND_SIZE, CLUTTER_BIND_ALL } ClutterBindCoordinate; /** * ClutterEffectPaintFlags: * @CLUTTER_EFFECT_PAINT_ACTOR_DIRTY: The actor or one of its children * has queued a redraw before this paint. This implies that the effect * should call clutter_actor_continue_paint() to chain to the next * effect and can not cache any results from a previous paint. * @CLUTTER_EFFECT_PAINT_BYPASS_EFFECT: The effect should not be used * on this frame, but it will be asked to paint the actor still. * * Flags passed to the ‘paint’ or ‘pick’ method of #ClutterEffect. */ typedef enum /*< prefix=CLUTTER_EFFECT_PAINT >*/ { CLUTTER_EFFECT_PAINT_ACTOR_DIRTY = (1 << 0), CLUTTER_EFFECT_PAINT_BYPASS_EFFECT = (1 << 1) } ClutterEffectPaintFlags; /** * ClutterBoxAlignment: * @CLUTTER_BOX_ALIGNMENT_START: Align the child to the top or to * to the left, depending on the used axis * @CLUTTER_BOX_ALIGNMENT_CENTER: Align the child to the center * @CLUTTER_BOX_ALIGNMENT_END: Align the child to the bottom or to * the right, depending on the used axis * * The alignment policies available on each axis of the #ClutterBoxLayout * * Since: 1.2 */ typedef enum { CLUTTER_BOX_ALIGNMENT_START, CLUTTER_BOX_ALIGNMENT_END, CLUTTER_BOX_ALIGNMENT_CENTER } ClutterBoxAlignment; /** * ClutterLongPressState: * @CLUTTER_LONG_PRESS_QUERY: Queries the action whether it supports * long presses * @CLUTTER_LONG_PRESS_ACTIVATE: Activates the action on a long press * @CLUTTER_LONG_PRESS_CANCEL: The long press was cancelled * * The states for the #ClutterClickAction::long-press signal. * * Since: 1.8 */ typedef enum /*< prefix=CLUTTER_LONG_PRESS >*/ { CLUTTER_LONG_PRESS_QUERY, CLUTTER_LONG_PRESS_ACTIVATE, CLUTTER_LONG_PRESS_CANCEL } ClutterLongPressState; /** * ClutterStaticColor: * @CLUTTER_COLOR_WHITE: White color (ffffffff) * @CLUTTER_COLOR_BLACK: Black color (000000ff) * @CLUTTER_COLOR_RED: Red color (ff0000ff) * @CLUTTER_COLOR_DARK_RED: Dark red color (800000ff) * @CLUTTER_COLOR_GREEN: Green color (00ff00ff) * @CLUTTER_COLOR_DARK_GREEN: Dark green color (008000ff) * @CLUTTER_COLOR_BLUE: Blue color (0000ffff) * @CLUTTER_COLOR_DARK_BLUE: Dark blue color (000080ff) * @CLUTTER_COLOR_CYAN: Cyan color (00ffffff) * @CLUTTER_COLOR_DARK_CYAN: Dark cyan color (008080ff) * @CLUTTER_COLOR_MAGENTA: Magenta color (ff00ffff) * @CLUTTER_COLOR_DARK_MAGENTA: Dark magenta color (800080ff) * @CLUTTER_COLOR_YELLOW: Yellow color (ffff00ff) * @CLUTTER_COLOR_DARK_YELLOW: Dark yellow color (808000ff) * @CLUTTER_COLOR_GRAY: Gray color (a0a0a4ff) * @CLUTTER_COLOR_DARK_GRAY: Dark Gray color (808080ff) * @CLUTTER_COLOR_LIGHT_GRAY: Light gray color (c0c0c0ff) * @CLUTTER_COLOR_BUTTER: Butter color (edd400ff) * @CLUTTER_COLOR_BUTTER_LIGHT: Light butter color (fce94fff) * @CLUTTER_COLOR_BUTTER_DARK: Dark butter color (c4a000ff) * @CLUTTER_COLOR_ORANGE: Orange color (f57900ff) * @CLUTTER_COLOR_ORANGE_LIGHT: Light orange color (fcaf3fff) * @CLUTTER_COLOR_ORANGE_DARK: Dark orange color (ce5c00ff) * @CLUTTER_COLOR_CHOCOLATE: Chocolate color (c17d11ff) * @CLUTTER_COLOR_CHOCOLATE_LIGHT: Light chocolate color (e9b96eff) * @CLUTTER_COLOR_CHOCOLATE_DARK: Dark chocolate color (8f5902ff) * @CLUTTER_COLOR_CHAMELEON: Chameleon color (73d216ff) * @CLUTTER_COLOR_CHAMELEON_LIGHT: Light chameleon color (8ae234ff) * @CLUTTER_COLOR_CHAMELEON_DARK: Dark chameleon color (4e9a06ff) * @CLUTTER_COLOR_SKY_BLUE: Sky color (3465a4ff) * @CLUTTER_COLOR_SKY_BLUE_LIGHT: Light sky color (729fcfff) * @CLUTTER_COLOR_SKY_BLUE_DARK: Dark sky color (204a87ff) * @CLUTTER_COLOR_PLUM: Plum color (75507bff) * @CLUTTER_COLOR_PLUM_LIGHT: Light plum color (ad7fa8ff) * @CLUTTER_COLOR_PLUM_DARK: Dark plum color (5c3566ff) * @CLUTTER_COLOR_SCARLET_RED: Scarlet red color (cc0000ff) * @CLUTTER_COLOR_SCARLET_RED_LIGHT: Light scarlet red color (ef2929ff) * @CLUTTER_COLOR_SCARLET_RED_DARK: Dark scarlet red color (a40000ff) * @CLUTTER_COLOR_ALUMINIUM_1: Aluminium, first variant (eeeeecff) * @CLUTTER_COLOR_ALUMINIUM_2: Aluminium, second variant (d3d7cfff) * @CLUTTER_COLOR_ALUMINIUM_3: Aluminium, third variant (babdb6ff) * @CLUTTER_COLOR_ALUMINIUM_4: Aluminium, fourth variant (888a85ff) * @CLUTTER_COLOR_ALUMINIUM_5: Aluminium, fifth variant (555753ff) * @CLUTTER_COLOR_ALUMINIUM_6: Aluminium, sixth variant (2e3436ff) * @CLUTTER_COLOR_TRANSPARENT: Transparent color (00000000) * * Named colors, for accessing global colors defined by Clutter * * Since: 1.6 */ typedef enum /*< prefix=CLUTTER_COLOR >*/ { /* CGA/EGA-like palette */ CLUTTER_COLOR_WHITE = 0, CLUTTER_COLOR_BLACK, CLUTTER_COLOR_RED, CLUTTER_COLOR_DARK_RED, CLUTTER_COLOR_GREEN, CLUTTER_COLOR_DARK_GREEN, CLUTTER_COLOR_BLUE, CLUTTER_COLOR_DARK_BLUE, CLUTTER_COLOR_CYAN, CLUTTER_COLOR_DARK_CYAN, CLUTTER_COLOR_MAGENTA, CLUTTER_COLOR_DARK_MAGENTA, CLUTTER_COLOR_YELLOW, CLUTTER_COLOR_DARK_YELLOW, CLUTTER_COLOR_GRAY, CLUTTER_COLOR_DARK_GRAY, CLUTTER_COLOR_LIGHT_GRAY, /* Tango icon palette */ CLUTTER_COLOR_BUTTER, CLUTTER_COLOR_BUTTER_LIGHT, CLUTTER_COLOR_BUTTER_DARK, CLUTTER_COLOR_ORANGE, CLUTTER_COLOR_ORANGE_LIGHT, CLUTTER_COLOR_ORANGE_DARK, CLUTTER_COLOR_CHOCOLATE, CLUTTER_COLOR_CHOCOLATE_LIGHT, CLUTTER_COLOR_CHOCOLATE_DARK, CLUTTER_COLOR_CHAMELEON, CLUTTER_COLOR_CHAMELEON_LIGHT, CLUTTER_COLOR_CHAMELEON_DARK, CLUTTER_COLOR_SKY_BLUE, CLUTTER_COLOR_SKY_BLUE_LIGHT, CLUTTER_COLOR_SKY_BLUE_DARK, CLUTTER_COLOR_PLUM, CLUTTER_COLOR_PLUM_LIGHT, CLUTTER_COLOR_PLUM_DARK, CLUTTER_COLOR_SCARLET_RED, CLUTTER_COLOR_SCARLET_RED_LIGHT, CLUTTER_COLOR_SCARLET_RED_DARK, CLUTTER_COLOR_ALUMINIUM_1, CLUTTER_COLOR_ALUMINIUM_2, CLUTTER_COLOR_ALUMINIUM_3, CLUTTER_COLOR_ALUMINIUM_4, CLUTTER_COLOR_ALUMINIUM_5, CLUTTER_COLOR_ALUMINIUM_6, /* Fully transparent black */ CLUTTER_COLOR_TRANSPARENT } ClutterStaticColor; /** * ClutterDragAxis: * @CLUTTER_DRAG_AXIS_NONE: No constraint * @CLUTTER_DRAG_X_AXIS: Set a constraint on the X axis * @CLUTTER_DRAG_Y_AXIS: Set a constraint on the Y axis * * The axis of the constraint that should be applied on the * dragging action * * Since: 1.4 */ typedef enum /*< prefix=CLUTTER_DRAG >*/ { CLUTTER_DRAG_AXIS_NONE = 0, CLUTTER_DRAG_X_AXIS, CLUTTER_DRAG_Y_AXIS } ClutterDragAxis; /** * ClutterEventFlags: * @CLUTTER_EVENT_NONE: No flag set * @CLUTTER_EVENT_FLAG_SYNTHETIC: Synthetic event * @CLUTTER_EVENT_FLAG_REPEATED: Auto-repeated event * * Flags for the #ClutterEvent * * Since: 0.6 */ typedef enum /*< flags prefix=CLUTTER_EVENT >*/ { CLUTTER_EVENT_NONE = 0, CLUTTER_EVENT_FLAG_SYNTHETIC = 1 << 0, CLUTTER_EVENT_FLAG_INPUT_METHOD = 1 << 1, CLUTTER_EVENT_FLAG_REPEATED = 1 << 2 } ClutterEventFlags; /** * ClutterEventType: * @CLUTTER_NOTHING: Empty event * @CLUTTER_KEY_PRESS: Key press event * @CLUTTER_KEY_RELEASE: Key release event * @CLUTTER_MOTION: Pointer motion event * @CLUTTER_ENTER: Actor enter event * @CLUTTER_LEAVE: Actor leave event * @CLUTTER_BUTTON_PRESS: Pointer button press event * @CLUTTER_BUTTON_RELEASE: Pointer button release event * @CLUTTER_SCROLL: Pointer scroll event * @CLUTTER_STAGE_STATE: Stage state change event * @CLUTTER_DESTROY_NOTIFY: Destroy notification event * @CLUTTER_CLIENT_MESSAGE: Client message event * @CLUTTER_DELETE: Stage delete event * @CLUTTER_TOUCH_BEGIN: A new touch event sequence has started; * event added in 1.10 * @CLUTTER_TOUCH_UPDATE: A touch event sequence has been updated; * event added in 1.10 * @CLUTTER_TOUCH_END: A touch event sequence has finished; * event added in 1.10 * @CLUTTER_TOUCH_CANCEL: A touch event sequence has been canceled; * event added in 1.10 * @CLUTTER_TOUCHPAD_PINCH: A pinch gesture event, the current state is * determined by its phase field; event added in 1.24 * @CLUTTER_TOUCHPAD_SWIPE: A swipe gesture event, the current state is * determined by its phase field; event added in 1.24 * @CLUTTER_PROXIMITY_IN: A tool entered in proximity to a tablet; * event added in 1.28 * @CLUTTER_PROXIMITY_OUT: A tool left from the proximity area of a tablet; * event added in 1.28 * @CLUTTER_EVENT_LAST: Marks the end of the #ClutterEventType enumeration; * added in 1.10 * * Types of events. * * Since: 0.4 */ typedef enum /*< prefix=CLUTTER >*/ { CLUTTER_NOTHING = 0, CLUTTER_KEY_PRESS, CLUTTER_KEY_RELEASE, CLUTTER_MOTION, CLUTTER_ENTER, CLUTTER_LEAVE, CLUTTER_BUTTON_PRESS, CLUTTER_BUTTON_RELEASE, CLUTTER_SCROLL, CLUTTER_STAGE_STATE, CLUTTER_DESTROY_NOTIFY, CLUTTER_CLIENT_MESSAGE, CLUTTER_DELETE, CLUTTER_TOUCH_BEGIN, CLUTTER_TOUCH_UPDATE, CLUTTER_TOUCH_END, CLUTTER_TOUCH_CANCEL, CLUTTER_TOUCHPAD_PINCH, CLUTTER_TOUCHPAD_SWIPE, CLUTTER_PROXIMITY_IN, CLUTTER_PROXIMITY_OUT, CLUTTER_PAD_BUTTON_PRESS, CLUTTER_PAD_BUTTON_RELEASE, CLUTTER_PAD_STRIP, CLUTTER_PAD_RING, CLUTTER_IM_COMMIT, CLUTTER_IM_DELETE, CLUTTER_IM_PREEDIT, CLUTTER_DEVICE_ADDED, CLUTTER_DEVICE_REMOVED, CLUTTER_EVENT_LAST /* helper */ } ClutterEventType; /** * ClutterScrollDirection: * @CLUTTER_SCROLL_UP: Scroll up * @CLUTTER_SCROLL_DOWN: Scroll down * @CLUTTER_SCROLL_LEFT: Scroll left * @CLUTTER_SCROLL_RIGHT: Scroll right * @CLUTTER_SCROLL_SMOOTH: Precise scrolling delta (available in 1.10) * * Direction of a pointer scroll event. * * The %CLUTTER_SCROLL_SMOOTH value implies that the #ClutterScrollEvent * has precise scrolling delta information. * * Since: 0.4 */ typedef enum /*< prefix=CLUTTER_SCROLL >*/ { CLUTTER_SCROLL_UP, CLUTTER_SCROLL_DOWN, CLUTTER_SCROLL_LEFT, CLUTTER_SCROLL_RIGHT, CLUTTER_SCROLL_SMOOTH } ClutterScrollDirection; /** * ClutterStageState: * @CLUTTER_STAGE_STATE_ACTIVATED: Activated mask * * Stage state masks, used by the #ClutterEvent of type %CLUTTER_STAGE_STATE. * * Since: 0.4 */ typedef enum { CLUTTER_STAGE_STATE_ACTIVATED = (1 << 3) } ClutterStageState; /** * ClutterFeatureFlags: * @CLUTTER_FEATURE_SWAP_THROTTLE: Set if backend throttles buffer swaps. * @CLUTTER_FEATURE_STAGE_STATIC: Set if stage size if fixed (i.e framebuffer) * @CLUTTER_FEATURE_STAGE_CURSOR: Set if stage has a graphical cursor. * @CLUTTER_FEATURE_SHADERS_GLSL: Set if the backend supports GLSL shaders. * @CLUTTER_FEATURE_OFFSCREEN: Set if the backend supports offscreen rendering. * @CLUTTER_FEATURE_STAGE_MULTIPLE: Set if multiple stages are supported. * @CLUTTER_FEATURE_SWAP_EVENTS: Set if the GLX_INTEL_swap_event is supported. * * Runtime flags indicating specific features available via Clutter window * system and graphics backend. * * Since: 0.4 */ typedef enum { CLUTTER_FEATURE_SWAP_THROTTLE = (1 << 3), CLUTTER_FEATURE_STAGE_STATIC = (1 << 6), CLUTTER_FEATURE_STAGE_CURSOR = (1 << 8), CLUTTER_FEATURE_SHADERS_GLSL = (1 << 9), CLUTTER_FEATURE_OFFSCREEN = (1 << 10), CLUTTER_FEATURE_STAGE_MULTIPLE = (1 << 11), CLUTTER_FEATURE_SWAP_EVENTS = (1 << 12) } ClutterFeatureFlags; /** * ClutterFlowOrientation: * @CLUTTER_FLOW_HORIZONTAL: Arrange the children of the flow layout * horizontally first * @CLUTTER_FLOW_VERTICAL: Arrange the children of the flow layout * vertically first * * The direction of the arrangement of the children inside * a #ClutterFlowLayout * * Since: 1.2 */ typedef enum /*< prefix=CLUTTER_FLOW >*/ { CLUTTER_FLOW_HORIZONTAL, CLUTTER_FLOW_VERTICAL } ClutterFlowOrientation; /** * ClutterInputDeviceType: * @CLUTTER_POINTER_DEVICE: A pointer device * @CLUTTER_KEYBOARD_DEVICE: A keyboard device * @CLUTTER_EXTENSION_DEVICE: A generic extension device * @CLUTTER_JOYSTICK_DEVICE: A joystick device * @CLUTTER_TABLET_DEVICE: A tablet device * @CLUTTER_TOUCHPAD_DEVICE: A touchpad device * @CLUTTER_TOUCHSCREEN_DEVICE: A touch screen device * @CLUTTER_PEN_DEVICE: A pen device * @CLUTTER_ERASER_DEVICE: An eraser device * @CLUTTER_CURSOR_DEVICE: A cursor device * @CLUTTER_PAD_DEVICE: A tablet pad * @CLUTTER_N_DEVICE_TYPES: The number of device types * * The types of input devices available. * * The #ClutterInputDeviceType enumeration can be extended at later * date; not every platform supports every input device type. * * Since: 1.0 */ typedef enum { CLUTTER_POINTER_DEVICE, CLUTTER_KEYBOARD_DEVICE, CLUTTER_EXTENSION_DEVICE, CLUTTER_JOYSTICK_DEVICE, CLUTTER_TABLET_DEVICE, CLUTTER_TOUCHPAD_DEVICE, CLUTTER_TOUCHSCREEN_DEVICE, CLUTTER_PEN_DEVICE, CLUTTER_ERASER_DEVICE, CLUTTER_CURSOR_DEVICE, CLUTTER_PAD_DEVICE, CLUTTER_N_DEVICE_TYPES } ClutterInputDeviceType; /** * ClutterInputMode: * @CLUTTER_INPUT_MODE_MASTER: A master, virtual device * @CLUTTER_INPUT_MODE_SLAVE: A slave, physical device, attached to * a master device * @CLUTTER_INPUT_MODE_FLOATING: A slave, physical device, not attached * to a master device * * The mode for input devices available. * * Since: 1.6 */ typedef enum { CLUTTER_INPUT_MODE_MASTER, CLUTTER_INPUT_MODE_SLAVE, CLUTTER_INPUT_MODE_FLOATING } ClutterInputMode; /** * ClutterInputAxis: * @CLUTTER_INPUT_AXIS_IGNORE: Unused axis * @CLUTTER_INPUT_AXIS_X: The position on the X axis * @CLUTTER_INPUT_AXIS_Y: The position of the Y axis * @CLUTTER_INPUT_AXIS_PRESSURE: The pressure information * @CLUTTER_INPUT_AXIS_XTILT: The tilt on the X axis * @CLUTTER_INPUT_AXIS_YTILT: The tile on the Y axis * @CLUTTER_INPUT_AXIS_WHEEL: A wheel * @CLUTTER_INPUT_AXIS_DISTANCE: Distance (Since 1.12) * @CLUTTER_INPUT_AXIS_ROTATION: Rotation along the z-axis (Since 1.28) * @CLUTTER_INPUT_AXIS_SLIDER: A slider (Since 1.28) * @CLUTTER_INPUT_AXIS_LAST: Last value of the enumeration; this value is * useful when iterating over the enumeration values (Since 1.12) * * The type of axes Clutter recognizes on a #ClutterInputDevice * * Since: 1.6 */ typedef enum { CLUTTER_INPUT_AXIS_IGNORE, CLUTTER_INPUT_AXIS_X, CLUTTER_INPUT_AXIS_Y, CLUTTER_INPUT_AXIS_PRESSURE, CLUTTER_INPUT_AXIS_XTILT, CLUTTER_INPUT_AXIS_YTILT, CLUTTER_INPUT_AXIS_WHEEL, CLUTTER_INPUT_AXIS_DISTANCE, CLUTTER_INPUT_AXIS_ROTATION, CLUTTER_INPUT_AXIS_SLIDER, CLUTTER_INPUT_AXIS_LAST } ClutterInputAxis; /** * ClutterSnapEdge: * @CLUTTER_SNAP_EDGE_TOP: the top edge * @CLUTTER_SNAP_EDGE_RIGHT: the right edge * @CLUTTER_SNAP_EDGE_BOTTOM: the bottom edge * @CLUTTER_SNAP_EDGE_LEFT: the left edge * * The edge to snap * * Since: 1.6 */ typedef enum { CLUTTER_SNAP_EDGE_TOP, CLUTTER_SNAP_EDGE_RIGHT, CLUTTER_SNAP_EDGE_BOTTOM, CLUTTER_SNAP_EDGE_LEFT } ClutterSnapEdge; /** * ClutterPickMode: * @CLUTTER_PICK_NONE: Do not paint any actor * @CLUTTER_PICK_REACTIVE: Paint only the reactive actors * @CLUTTER_PICK_ALL: Paint all actors * * Controls the paint cycle of the scene graph when in pick mode * * Since: 1.0 */ typedef enum { CLUTTER_PICK_NONE = 0, CLUTTER_PICK_REACTIVE, CLUTTER_PICK_ALL } ClutterPickMode; /** * ClutterSwipeDirection: * @CLUTTER_SWIPE_DIRECTION_UP: Upwards swipe gesture * @CLUTTER_SWIPE_DIRECTION_DOWN: Downwards swipe gesture * @CLUTTER_SWIPE_DIRECTION_LEFT: Leftwards swipe gesture * @CLUTTER_SWIPE_DIRECTION_RIGHT: Rightwards swipe gesture * * The main direction of the swipe gesture * * Since: 1.8 */ typedef enum /*< prefix=CLUTTER_SWIPE_DIRECTION >*/ { CLUTTER_SWIPE_DIRECTION_UP = 1 << 0, CLUTTER_SWIPE_DIRECTION_DOWN = 1 << 1, CLUTTER_SWIPE_DIRECTION_LEFT = 1 << 2, CLUTTER_SWIPE_DIRECTION_RIGHT = 1 << 3 } ClutterSwipeDirection; /** * ClutterPanAxis: * @CLUTTER_PAN_AXIS_NONE: No constraint * @CLUTTER_PAN_X_AXIS: Set a constraint on the X axis * @CLUTTER_PAN_Y_AXIS: Set a constraint on the Y axis * @CLUTTER_PAN_AXIS_AUTO: Constrain panning automatically based on initial * movement (available since 1.24) * * The axis of the constraint that should be applied on the * panning action * * Since: 1.12 */ typedef enum /*< prefix=CLUTTER_PAN >*/ { CLUTTER_PAN_AXIS_NONE = 0, CLUTTER_PAN_X_AXIS, CLUTTER_PAN_Y_AXIS, CLUTTER_PAN_AXIS_AUTO } ClutterPanAxis; /** * ClutterTextureFlags: * @CLUTTER_TEXTURE_NONE: No flags * @CLUTTER_TEXTURE_RGB_FLAG_BGR: Unused flag * @CLUTTER_TEXTURE_RGB_FLAG_PREMULT: Unused flag * @CLUTTER_TEXTURE_YUV_FLAG_YUV2: Unused flag * * Flags for clutter_texture_set_from_rgb_data(). * * Since: 0.4 * * Deprecated: 1.22: The #ClutterTexture class was the only user of * this API */ typedef enum /*< prefix=CLUTTER_TEXTURE >*/ { CLUTTER_TEXTURE_NONE = 0, CLUTTER_TEXTURE_RGB_FLAG_BGR = 1 << 1, CLUTTER_TEXTURE_RGB_FLAG_PREMULT = 1 << 2, /* FIXME: not handled */ CLUTTER_TEXTURE_YUV_FLAG_YUV2 = 1 << 3 } ClutterTextureFlags; /** * ClutterTextureQuality: * @CLUTTER_TEXTURE_QUALITY_LOW: fastest rendering will use nearest neighbour * interpolation when rendering. good setting. * @CLUTTER_TEXTURE_QUALITY_MEDIUM: higher quality rendering without using * extra resources. * @CLUTTER_TEXTURE_QUALITY_HIGH: render the texture with the best quality * available using extra memory. * * Enumaration controlling the texture quality. * * Since: 0.8 * * Deprecated: 1.22: The #ClutterTexture class was the only used ot * this API; use #ClutterImage and clutter_actor_set_content_scaling_filters() * instead. */ typedef enum /*< prefix=CLUTTER_TEXTURE_QUALITY >*/ { CLUTTER_TEXTURE_QUALITY_LOW, CLUTTER_TEXTURE_QUALITY_MEDIUM, CLUTTER_TEXTURE_QUALITY_HIGH } ClutterTextureQuality; /** * ClutterTimelineDirection: * @CLUTTER_TIMELINE_FORWARD: forward direction for a timeline * @CLUTTER_TIMELINE_BACKWARD: backward direction for a timeline * * The direction of a #ClutterTimeline * * Since: 0.6 */ typedef enum { CLUTTER_TIMELINE_FORWARD, CLUTTER_TIMELINE_BACKWARD } ClutterTimelineDirection; /** * ClutterUnitType: * @CLUTTER_UNIT_PIXEL: Unit expressed in pixels (with subpixel precision) * @CLUTTER_UNIT_EM: Unit expressed in em * @CLUTTER_UNIT_MM: Unit expressed in millimeters * @CLUTTER_UNIT_POINT: Unit expressed in points * @CLUTTER_UNIT_CM: Unit expressed in centimeters * * The type of unit in which a value is expressed * * This enumeration might be expanded at later date * * Since: 1.0 */ typedef enum /*< prefix=CLUTTER_UNIT >*/ { CLUTTER_UNIT_PIXEL, CLUTTER_UNIT_EM, CLUTTER_UNIT_MM, CLUTTER_UNIT_POINT, CLUTTER_UNIT_CM } ClutterUnitType; #define CLUTTER_PATH_RELATIVE (32) /** * ClutterPathNodeType: * @CLUTTER_PATH_MOVE_TO: jump to the given position * @CLUTTER_PATH_LINE_TO: create a line from the last node to the * given position * @CLUTTER_PATH_CURVE_TO: bezier curve using the last position and * three control points. * @CLUTTER_PATH_CLOSE: create a line from the last node to the last * %CLUTTER_PATH_MOVE_TO node. * @CLUTTER_PATH_REL_MOVE_TO: same as %CLUTTER_PATH_MOVE_TO but with * coordinates relative to the last node. * @CLUTTER_PATH_REL_LINE_TO: same as %CLUTTER_PATH_LINE_TO but with * coordinates relative to the last node. * @CLUTTER_PATH_REL_CURVE_TO: same as %CLUTTER_PATH_CURVE_TO but with * coordinates relative to the last node. * * Types of nodes in a #ClutterPath. * * Since: 1.0 */ typedef enum { CLUTTER_PATH_MOVE_TO = 0, CLUTTER_PATH_LINE_TO = 1, CLUTTER_PATH_CURVE_TO = 2, CLUTTER_PATH_CLOSE = 3, CLUTTER_PATH_REL_MOVE_TO = CLUTTER_PATH_MOVE_TO | CLUTTER_PATH_RELATIVE, CLUTTER_PATH_REL_LINE_TO = CLUTTER_PATH_LINE_TO | CLUTTER_PATH_RELATIVE, CLUTTER_PATH_REL_CURVE_TO = CLUTTER_PATH_CURVE_TO | CLUTTER_PATH_RELATIVE } ClutterPathNodeType; /** * ClutterActorAlign: * @CLUTTER_ACTOR_ALIGN_FILL: Stretch to cover the whole allocated space * @CLUTTER_ACTOR_ALIGN_START: Snap to left or top side, leaving space * to the right or bottom. For horizontal layouts, in right-to-left * locales this should be reversed. * @CLUTTER_ACTOR_ALIGN_CENTER: Center the actor inside the allocation * @CLUTTER_ACTOR_ALIGN_END: Snap to right or bottom side, leaving space * to the left or top. For horizontal layouts, in right-to-left locales * this should be reversed. * * Controls how a #ClutterActor should align itself inside the extra space * assigned to it during the allocation. * * Alignment only matters if the allocated space given to an actor is * bigger than its natural size; for example, when the #ClutterActor:x-expand * or the #ClutterActor:y-expand properties of #ClutterActor are set to %TRUE. * * Since: 1.10 */ typedef enum { CLUTTER_ACTOR_ALIGN_FILL, CLUTTER_ACTOR_ALIGN_START, CLUTTER_ACTOR_ALIGN_CENTER, CLUTTER_ACTOR_ALIGN_END } ClutterActorAlign; /** * ClutterRepaintFlags: * @CLUTTER_REPAINT_FLAGS_PRE_PAINT: Run the repaint function prior to * painting the stages * @CLUTTER_REPAINT_FLAGS_POST_PAINT: Run the repaint function after * painting the stages * @CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD: Ensure that a new frame * is queued after adding the repaint function * * Flags to pass to clutter_threads_add_repaint_func_full(). * * Since: 1.10 */ typedef enum { CLUTTER_REPAINT_FLAGS_PRE_PAINT = 1 << 0, CLUTTER_REPAINT_FLAGS_POST_PAINT = 1 << 1, CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD = 1 << 2 } ClutterRepaintFlags; /** * ClutterContentGravity: * @CLUTTER_CONTENT_GRAVITY_TOP_LEFT: Align the content to the top left corner * @CLUTTER_CONTENT_GRAVITY_TOP: Align the content to the top edge * @CLUTTER_CONTENT_GRAVITY_TOP_RIGHT: Align the content to the top right corner * @CLUTTER_CONTENT_GRAVITY_LEFT: Align the content to the left edge * @CLUTTER_CONTENT_GRAVITY_CENTER: Align the content to the center * @CLUTTER_CONTENT_GRAVITY_RIGHT: Align the content to the right edge * @CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT: Align the content to the bottom left corner * @CLUTTER_CONTENT_GRAVITY_BOTTOM: Align the content to the bottom edge * @CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT: Align the content to the bottom right corner * @CLUTTER_CONTENT_GRAVITY_RESIZE_FILL: Resize the content to fill the allocation * @CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT: Resize the content to remain within the * allocation, while maintaining the aspect ratio * * Controls the alignment of the #ClutterContent inside a #ClutterActor. * * Since: 1.10 */ typedef enum { CLUTTER_CONTENT_GRAVITY_TOP_LEFT, CLUTTER_CONTENT_GRAVITY_TOP, CLUTTER_CONTENT_GRAVITY_TOP_RIGHT, CLUTTER_CONTENT_GRAVITY_LEFT, CLUTTER_CONTENT_GRAVITY_CENTER, CLUTTER_CONTENT_GRAVITY_RIGHT, CLUTTER_CONTENT_GRAVITY_BOTTOM_LEFT, CLUTTER_CONTENT_GRAVITY_BOTTOM, CLUTTER_CONTENT_GRAVITY_BOTTOM_RIGHT, CLUTTER_CONTENT_GRAVITY_RESIZE_FILL, CLUTTER_CONTENT_GRAVITY_RESIZE_ASPECT } ClutterContentGravity; /** * ClutterScalingFilter: * @CLUTTER_SCALING_FILTER_LINEAR: Linear interpolation filter * @CLUTTER_SCALING_FILTER_NEAREST: Nearest neighbor interpolation filter * @CLUTTER_SCALING_FILTER_TRILINEAR: Trilinear minification filter, with * mipmap generation; this filter linearly interpolates on every axis, * as well as between mipmap levels. * * The scaling filters to be used with the #ClutterActor:minification-filter * and #ClutterActor:magnification-filter properties. * * Since: 1.10 */ typedef enum { CLUTTER_SCALING_FILTER_LINEAR, CLUTTER_SCALING_FILTER_NEAREST, CLUTTER_SCALING_FILTER_TRILINEAR } ClutterScalingFilter; /** * ClutterOrientation: * @CLUTTER_ORIENTATION_HORIZONTAL: An horizontal orientation * @CLUTTER_ORIENTATION_VERTICAL: A vertical orientation * * Represents the orientation of actors or layout managers. * * Since: 1.12 */ typedef enum { CLUTTER_ORIENTATION_HORIZONTAL, CLUTTER_ORIENTATION_VERTICAL } ClutterOrientation; /** * ClutterScrollMode: * @CLUTTER_SCROLL_NONE: Ignore scrolling * @CLUTTER_SCROLL_HORIZONTALLY: Scroll only horizontally * @CLUTTER_SCROLL_VERTICALLY: Scroll only vertically * @CLUTTER_SCROLL_BOTH: Scroll in both directions * * Scroll modes. * * Since: 1.12 */ typedef enum /*< prefix=CLUTTER_SCROLL >*/ { CLUTTER_SCROLL_NONE = 0, CLUTTER_SCROLL_HORIZONTALLY = 1 << 0, CLUTTER_SCROLL_VERTICALLY = 1 << 1, CLUTTER_SCROLL_BOTH = CLUTTER_SCROLL_HORIZONTALLY | CLUTTER_SCROLL_VERTICALLY } ClutterScrollMode; /** * ClutterGridPosition: * @CLUTTER_GRID_POSITION_LEFT: left position * @CLUTTER_GRID_POSITION_RIGHT: right position * @CLUTTER_GRID_POSITION_TOP: top position * @CLUTTER_GRID_POSITION_BOTTOM: bottom position * * Grid position modes. * * Since: 1.12 */ typedef enum { CLUTTER_GRID_POSITION_LEFT, CLUTTER_GRID_POSITION_RIGHT, CLUTTER_GRID_POSITION_TOP, CLUTTER_GRID_POSITION_BOTTOM } ClutterGridPosition; /** * ClutterContentRepeat: * @CLUTTER_REPEAT_NONE: No repeat * @CLUTTER_REPEAT_X_AXIS: Repeat the content on the X axis * @CLUTTER_REPEAT_Y_AXIS: Repeat the content on the Y axis * @CLUTTER_REPEAT_BOTH: Repeat the content on both axis * * Content repeat modes. * * Since: 1.12 */ typedef enum { CLUTTER_REPEAT_NONE = 0, CLUTTER_REPEAT_X_AXIS = 1 << 0, CLUTTER_REPEAT_Y_AXIS = 1 << 1, CLUTTER_REPEAT_BOTH = CLUTTER_REPEAT_X_AXIS | CLUTTER_REPEAT_Y_AXIS } ClutterContentRepeat; /** * ClutterStepMode: * @CLUTTER_STEP_MODE_START: The change in the value of a * %CLUTTER_STEP progress mode should occur at the start of * the transition * @CLUTTER_STEP_MODE_END: The change in the value of a * %CLUTTER_STEP progress mode should occur at the end of * the transition * * Change the value transition of a step function. * * See clutter_timeline_set_step_progress(). * * Since: 1.12 */ typedef enum { CLUTTER_STEP_MODE_START, CLUTTER_STEP_MODE_END } ClutterStepMode; /** * ClutterZoomAxis: * @CLUTTER_ZOOM_X_AXIS: Scale only on the X axis * @CLUTTER_ZOOM_Y_AXIS: Scale only on the Y axis * @CLUTTER_ZOOM_BOTH: Scale on both axis * * The axis of the constraint that should be applied by the * zooming action. * * Since: 1.12 */ typedef enum /*< prefix=CLUTTER_ZOOM >*/ { CLUTTER_ZOOM_X_AXIS, CLUTTER_ZOOM_Y_AXIS, CLUTTER_ZOOM_BOTH } ClutterZoomAxis; /** * ClutterGestureTriggerEdge: * @CLUTTER_GESTURE_TRIGGER_EDGE_NONE: Tell #ClutterGestureAction that * the gesture must begin immediately and there's no drag limit that * will cause its cancellation; * @CLUTTER_GESTURE_TRIGGER_EDGE_AFTER: Tell #ClutterGestureAction that * it needs to wait until the drag threshold has been exceeded before * considering that the gesture has begun; * @CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE: Tell #ClutterGestureAction that * the gesture must begin immediately and that it must be cancelled * once the drag exceed the configured threshold. * * Enum passed to the clutter_gesture_action_set_threshold_trigger_edge() * function. * * Since: 1.18 */ typedef enum { CLUTTER_GESTURE_TRIGGER_EDGE_NONE = 0, CLUTTER_GESTURE_TRIGGER_EDGE_AFTER, CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE } ClutterGestureTriggerEdge; /** * ClutterTouchpadGesturePhase: * @CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN: The gesture has begun. * @CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE: The gesture has been updated. * @CLUTTER_TOUCHPAD_GESTURE_PHASE_END: The gesture was finished, changes * should be permanently applied. * @CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL: The gesture was cancelled, all * changes should be undone. * * The phase of a touchpad gesture event. All gestures are guaranteed to * begin with an event of type %CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN, * followed by a number of %CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE (possibly 0). * * A finished gesture may have 2 possible outcomes, an event with phase * %CLUTTER_TOUCHPAD_GESTURE_PHASE_END will be emitted when the gesture is * considered successful, this should be used as the hint to perform any * permanent changes. * Cancelled gestures may be so for a variety of reasons, due to hardware, * or due to the gesture recognition layers hinting the gesture did not * finish resolutely (eg. a 3rd finger being added during a pinch gesture). * In these cases, the last event with report the phase * %CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL, this should be used as a hint * to undo any visible/permanent changes that were done throughout the * progress of the gesture. * * See also #ClutterTouchpadPinchEvent and #ClutterTouchpadPinchEvent. * * Since: 1.24 */ typedef enum { CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN, CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, CLUTTER_TOUCHPAD_GESTURE_PHASE_END, CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL } ClutterTouchpadGesturePhase; /** * ClutterScrollSource: * @CLUTTER_SCROLL_SOURCE_UNKNOWN: Source of scroll events is unknown. * @CLUTTER_SCROLL_SOURCE_WHEEL: The scroll event is originated by a mouse wheel. * @CLUTTER_SCROLL_SOURCE_FINGER: The scroll event is originated by one or more * fingers on the device (eg. touchpads). * @CLUTTER_SCROLL_SOURCE_CONTINUOUS: The scroll event is originated by the * motion of some device (eg. a scroll button is set). * * The scroll source determines the source of the scroll event. Keep in mind * that the source device #ClutterInputDeviceType is not enough to infer * the scroll source. * * Since: 1.26 */ typedef enum { CLUTTER_SCROLL_SOURCE_UNKNOWN, CLUTTER_SCROLL_SOURCE_WHEEL, CLUTTER_SCROLL_SOURCE_FINGER, CLUTTER_SCROLL_SOURCE_CONTINUOUS } ClutterScrollSource; /** * ClutterScrollFinishFlags: * @CLUTTER_SCROLL_FINISHED_NONE: no axis was stopped. * @CLUTTER_SCROLL_FINISHED_HORIZONTAL: The horizontal axis stopped. * @CLUTTER_SCROLL_FINISHED_VERTICAL: The vertical axis stopped. * * Flags used to notify the axes that were stopped in a #ClutterScrollEvent. * These can be used to trigger post-scroll effects like kinetic scrolling. * * Since: 1.26 */ typedef enum { CLUTTER_SCROLL_FINISHED_NONE = 0, CLUTTER_SCROLL_FINISHED_HORIZONTAL = 1 << 0, CLUTTER_SCROLL_FINISHED_VERTICAL = 1 << 1 } ClutterScrollFinishFlags; /** * ClutterInputDeviceToolType: * @CLUTTER_INPUT_DEVICE_TOOL_NONE: No tool * @CLUTTER_INPUT_DEVICE_TOOL_PEN: The tool is a pen * @CLUTTER_INPUT_DEVICE_TOOL_ERASER: The tool is an eraser * @CLUTTER_INPUT_DEVICE_TOOL_BRUSH: The tool is a brush * @CLUTTER_INPUT_DEVICE_TOOL_PENCIL: The tool is a pencil * @CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH: The tool is an airbrush * @CLUTTER_INPUT_DEVICE_TOOL_MOUSE: The tool is a mouse * @CLUTTER_INPUT_DEVICE_TOOL_LENS: The tool is a lens * * Defines the type of tool that a #ClutterInputDeviceTool represents. * * Since: 1.28 */ typedef enum { CLUTTER_INPUT_DEVICE_TOOL_NONE, CLUTTER_INPUT_DEVICE_TOOL_PEN, CLUTTER_INPUT_DEVICE_TOOL_ERASER, CLUTTER_INPUT_DEVICE_TOOL_BRUSH, CLUTTER_INPUT_DEVICE_TOOL_PENCIL, CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH, CLUTTER_INPUT_DEVICE_TOOL_MOUSE, CLUTTER_INPUT_DEVICE_TOOL_LENS } ClutterInputDeviceToolType; typedef enum { CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN, CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER, } ClutterInputDevicePadSource; typedef enum { CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE, CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE, } ClutterInputDeviceMapping; typedef enum { CLUTTER_INPUT_CONTENT_HINT_COMPLETION = 1 << 0, CLUTTER_INPUT_CONTENT_HINT_SPELLCHECK = 1 << 1, CLUTTER_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION = 1 << 2, CLUTTER_INPUT_CONTENT_HINT_LOWERCASE = 1 << 3, CLUTTER_INPUT_CONTENT_HINT_UPPERCASE = 1 << 4, CLUTTER_INPUT_CONTENT_HINT_TITLECASE = 1 << 5, CLUTTER_INPUT_CONTENT_HINT_HIDDEN_TEXT = 1 << 6, CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA = 1 << 7, CLUTTER_INPUT_CONTENT_HINT_LATIN = 1 << 8, CLUTTER_INPUT_CONTENT_HINT_MULTILINE = 1 << 9, } ClutterInputContentHintFlags; typedef enum { CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL, CLUTTER_INPUT_CONTENT_PURPOSE_ALPHA, CLUTTER_INPUT_CONTENT_PURPOSE_DIGITS, CLUTTER_INPUT_CONTENT_PURPOSE_NUMBER, CLUTTER_INPUT_CONTENT_PURPOSE_PHONE, CLUTTER_INPUT_CONTENT_PURPOSE_URL, CLUTTER_INPUT_CONTENT_PURPOSE_EMAIL, CLUTTER_INPUT_CONTENT_PURPOSE_NAME, CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD, CLUTTER_INPUT_CONTENT_PURPOSE_DATE, CLUTTER_INPUT_CONTENT_PURPOSE_TIME, CLUTTER_INPUT_CONTENT_PURPOSE_DATETIME, CLUTTER_INPUT_CONTENT_PURPOSE_TERMINAL, } ClutterInputContentPurpose; typedef enum { CLUTTER_INPUT_PANEL_STATE_OFF, CLUTTER_INPUT_PANEL_STATE_ON, CLUTTER_INPUT_PANEL_STATE_TOGGLE, } ClutterInputPanelState; G_END_DECLS #endif /* __CLUTTER_ENUMS_H__ */ muffin-6.4.1/clutter/clutter/clutter-path-constraint.c0000664000175000017500000002557614723361714022067 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-path-constraint * @Title: ClutterPathConstraint * @Short_Description: A constraint that follows a path * * #ClutterPathConstraint is a simple constraint that modifies the allocation * of the #ClutterActor to which it has been applied using a #ClutterPath. * * By setting the #ClutterPathConstraint:offset property it is possible to * control how far along the path the #ClutterActor should be. * * ClutterPathConstraint is available since Clutter 1.6. */ #include "clutter-build-config.h" #include "clutter-path-constraint.h" #include "clutter-debug.h" #include "clutter-marshal.h" #include "clutter-private.h" #define CLUTTER_PATH_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PATH_CONSTRAINT, ClutterPathConstraintClass)) #define CLUTTER_IS_PATH_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PATH_CONSTRAINT)) #define CLUTTER_PATH_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PATH_CONSTRAINT, ClutterPathConstraintClass)) struct _ClutterPathConstraint { ClutterConstraint parent_instance; ClutterPath *path; gfloat offset; ClutterActor *actor; guint current_node; }; struct _ClutterPathConstraintClass { ClutterConstraintClass parent_class; }; enum { PROP_0, PROP_PATH, PROP_OFFSET, LAST_PROPERTY }; enum { NODE_REACHED, LAST_SIGNAL }; G_DEFINE_TYPE (ClutterPathConstraint, clutter_path_constraint, CLUTTER_TYPE_CONSTRAINT); static GParamSpec *path_properties[LAST_PROPERTY] = { NULL, }; static guint path_signals[LAST_SIGNAL] = { 0, }; static void clutter_path_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { ClutterPathConstraint *self = CLUTTER_PATH_CONSTRAINT (constraint); gfloat width, height; ClutterKnot position; guint knot_id; if (self->path == NULL) return; knot_id = clutter_path_get_position (self->path, self->offset, &position); clutter_actor_box_get_size (allocation, &width, &height); allocation->x1 = position.x; allocation->y1 = position.y; allocation->x2 = allocation->x1 + width; allocation->y2 = allocation->y1 + height; if (knot_id != self->current_node) { self->current_node = knot_id; g_signal_emit (self, path_signals[NODE_REACHED], 0, self->actor, self->current_node); } } static void clutter_path_constraint_set_actor (ClutterActorMeta *meta, ClutterActor *new_actor) { ClutterPathConstraint *path = CLUTTER_PATH_CONSTRAINT (meta); ClutterActorMetaClass *parent; /* store the pointer to the actor, for later use */ path->actor = new_actor; parent = CLUTTER_ACTOR_META_CLASS (clutter_path_constraint_parent_class); parent->set_actor (meta, new_actor); } static void clutter_path_constraint_dispose (GObject *gobject) { ClutterPathConstraint *self = CLUTTER_PATH_CONSTRAINT (gobject); if (self->path != NULL) { g_object_unref (self->path); self->path = NULL; } G_OBJECT_CLASS (clutter_path_constraint_parent_class)->dispose (gobject); } static void clutter_path_constraint_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterPathConstraint *self = CLUTTER_PATH_CONSTRAINT (gobject); switch (prop_id) { case PROP_PATH: clutter_path_constraint_set_path (self, g_value_get_object (value)); break; case PROP_OFFSET: clutter_path_constraint_set_offset (self, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_path_constraint_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterPathConstraint *self = CLUTTER_PATH_CONSTRAINT (gobject); switch (prop_id) { case PROP_PATH: g_value_set_object (value, self->path); break; case PROP_OFFSET: g_value_set_float (value, self->offset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_path_constraint_class_init (ClutterPathConstraintClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass); /** * ClutterPathConstraint:path: * * The #ClutterPath used to constrain the position of an actor. * * Since: 1.6 */ path_properties[PROP_PATH] = g_param_spec_object ("path", P_("Path"), P_("The path used to constrain an actor"), CLUTTER_TYPE_PATH, CLUTTER_PARAM_READWRITE); /** * ClutterPathConstraint:offset: * * The offset along the #ClutterPathConstraint:path, between -1.0 and 2.0. * * Since: 1.6 */ path_properties[PROP_OFFSET] = g_param_spec_float ("offset", P_("Offset"), P_("The offset along the path, between -1.0 and 2.0"), -1.0, 2.0, 0.0, CLUTTER_PARAM_READWRITE); gobject_class->set_property = clutter_path_constraint_set_property; gobject_class->get_property = clutter_path_constraint_get_property; gobject_class->dispose = clutter_path_constraint_dispose; g_object_class_install_properties (gobject_class, LAST_PROPERTY, path_properties); meta_class->set_actor = clutter_path_constraint_set_actor; constraint_class->update_allocation = clutter_path_constraint_update_allocation; /** * ClutterPathConstraint::node-reached: * @constraint: the #ClutterPathConstraint that emitted the signal * @actor: the #ClutterActor using the @constraint * @index: the index of the node that has been reached * * The ::node-reached signal is emitted each time a * #ClutterPathConstraint:offset value results in the actor * passing a #ClutterPathNode * * Since: 1.6 */ path_signals[NODE_REACHED] = g_signal_new (I_("node-reached"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__OBJECT_UINT, G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR, G_TYPE_UINT); } static void clutter_path_constraint_init (ClutterPathConstraint *self) { self->offset = 0.0f; self->current_node = G_MAXUINT; } /** * clutter_path_constraint_new: * @path: (allow-none): a #ClutterPath, or %NULL * @offset: the offset along the #ClutterPath * * Creates a new #ClutterPathConstraint with the given @path and @offset * * Return value: the newly created #ClutterPathConstraint * * Since: 1.6 */ ClutterConstraint * clutter_path_constraint_new (ClutterPath *path, gfloat offset) { g_return_val_if_fail (path == NULL || CLUTTER_IS_PATH (path), NULL); return g_object_new (CLUTTER_TYPE_PATH_CONSTRAINT, "path", path, "offset", offset, NULL); } /** * clutter_path_constraint_set_path: * @constraint: a #ClutterPathConstraint * @path: (allow-none): a #ClutterPath * * Sets the @path to be followed by the #ClutterPathConstraint. * * The @constraint will take ownership of the #ClutterPath passed to this * function. * * Since: 1.6 */ void clutter_path_constraint_set_path (ClutterPathConstraint *constraint, ClutterPath *path) { g_return_if_fail (CLUTTER_IS_PATH_CONSTRAINT (constraint)); g_return_if_fail (path == NULL || CLUTTER_IS_PATH (path)); if (constraint->path == path) return; if (constraint->path != NULL) { g_object_unref (constraint->path); constraint->path = NULL; } if (path != NULL) constraint->path = g_object_ref_sink (path); if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); g_object_notify_by_pspec (G_OBJECT (constraint), path_properties[PROP_PATH]); } /** * clutter_path_constraint_get_path: * @constraint: a #ClutterPathConstraint * * Retrieves a pointer to the #ClutterPath used by @constraint. * * Return value: (transfer none): the #ClutterPath used by the * #ClutterPathConstraint, or %NULL. The returned #ClutterPath is owned * by the constraint and it should not be unreferenced * * Since: 1.6 */ ClutterPath * clutter_path_constraint_get_path (ClutterPathConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_PATH_CONSTRAINT (constraint), NULL); return constraint->path; } /** * clutter_path_constraint_set_offset: * @constraint: a #ClutterPathConstraint * @offset: the offset along the path * * Sets the offset along the #ClutterPath used by @constraint. * * Since: 1.6 */ void clutter_path_constraint_set_offset (ClutterPathConstraint *constraint, gfloat offset) { g_return_if_fail (CLUTTER_IS_PATH_CONSTRAINT (constraint)); if (constraint->offset == offset) return; constraint->offset = offset; if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); g_object_notify_by_pspec (G_OBJECT (constraint), path_properties[PROP_OFFSET]); } /** * clutter_path_constraint_get_offset: * @constraint: a #ClutterPathConstraint * * Retrieves the offset along the #ClutterPath used by @constraint. * * Return value: the offset * * Since: 1.6 */ gfloat clutter_path_constraint_get_offset (ClutterPathConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_PATH_CONSTRAINT (constraint), 0.0); return constraint->offset; } muffin-6.4.1/clutter/clutter/clutter-swipe-action.c0000664000175000017500000002023214723361714021333 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Tomeu Vizoso * * Based on ClutterDragAction, written by: * Emmanuele Bassi */ /** * SECTION:clutter-swipe-action * @Title: ClutterSwipeAction * @Short_Description: Action for swipe gestures * * #ClutterSwipeAction is a sub-class of #ClutterGestureAction that implements * the logic for recognizing swipe gestures. * * Since: 1.8 */ #include "clutter-build-config.h" #include "clutter-swipe-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-gesture-action-private.h" #include "clutter-marshal.h" #include "clutter-private.h" struct _ClutterSwipeActionPrivate { ClutterSwipeDirection h_direction; ClutterSwipeDirection v_direction; float distance_x, distance_y; }; enum { SWEPT, SWIPE, LAST_SIGNAL }; static guint swipe_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterSwipeAction, clutter_swipe_action, CLUTTER_TYPE_GESTURE_ACTION) static gboolean gesture_begin (ClutterGestureAction *action, ClutterActor *actor) { ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; /* reset the state at the beginning of a new gesture */ priv->h_direction = 0; priv->v_direction = 0; g_object_get (action, "threshold-trigger-distance-x", &priv->distance_x, "threshold-trigger-distance-y", &priv->distance_y, NULL); return TRUE; } static gboolean gesture_progress (ClutterGestureAction *action, ClutterActor *actor) { ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; gfloat press_x, press_y; gfloat motion_x, motion_y; gfloat delta_x, delta_y; ClutterSwipeDirection h_direction = 0, v_direction = 0; clutter_gesture_action_get_press_coords (action, 0, &press_x, &press_y); clutter_gesture_action_get_motion_coords (action, 0, &motion_x, &motion_y); delta_x = press_x - motion_x; delta_y = press_y - motion_y; if (delta_x >= priv->distance_x) h_direction = CLUTTER_SWIPE_DIRECTION_RIGHT; else if (delta_x < -priv->distance_x) h_direction = CLUTTER_SWIPE_DIRECTION_LEFT; if (delta_y >= priv->distance_y) v_direction = CLUTTER_SWIPE_DIRECTION_DOWN; else if (delta_y < -priv->distance_y) v_direction = CLUTTER_SWIPE_DIRECTION_UP; /* cancel gesture on direction reversal */ if (priv->h_direction == 0) priv->h_direction = h_direction; if (priv->v_direction == 0) priv->v_direction = v_direction; if (priv->h_direction != h_direction) return FALSE; if (priv->v_direction != v_direction) return FALSE; return TRUE; } static void gesture_end (ClutterGestureAction *action, ClutterActor *actor) { ClutterSwipeActionPrivate *priv = CLUTTER_SWIPE_ACTION (action)->priv; gfloat press_x, press_y; gfloat release_x, release_y; ClutterSwipeDirection direction = 0; gboolean can_emit_swipe; const ClutterEvent *last_event; clutter_gesture_action_get_press_coords (action, 0, &press_x, &press_y); /* Check the last event instead of get_release_coords(), this * might not be the sequence that finished on multi-finger swipes. */ last_event = clutter_gesture_action_get_last_event (action, 0); clutter_event_get_coords (last_event, &release_x, &release_y); if (release_x - press_x > priv->distance_x) direction |= CLUTTER_SWIPE_DIRECTION_RIGHT; else if (press_x - release_x > priv->distance_x) direction |= CLUTTER_SWIPE_DIRECTION_LEFT; if (release_y - press_y > priv->distance_y) direction |= CLUTTER_SWIPE_DIRECTION_DOWN; else if (press_y - release_y > priv->distance_y) direction |= CLUTTER_SWIPE_DIRECTION_UP; /* XXX:2.0 remove */ g_signal_emit (action, swipe_signals[SWIPE], 0, actor, direction, &can_emit_swipe); if (can_emit_swipe) g_signal_emit (action, swipe_signals[SWEPT], 0, actor, direction); } /* XXX:2.0 remove */ static gboolean clutter_swipe_action_real_swipe (ClutterSwipeAction *action, ClutterActor *actor, ClutterSwipeDirection direction) { return TRUE; } static void clutter_swipe_action_constructed (GObject *object) { clutter_gesture_action_set_threshold_trigger_edge (CLUTTER_GESTURE_ACTION (object), CLUTTER_GESTURE_TRIGGER_EDGE_AFTER); } static void clutter_swipe_action_class_init (ClutterSwipeActionClass *klass) { ClutterGestureActionClass *gesture_class = CLUTTER_GESTURE_ACTION_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = clutter_swipe_action_constructed; gesture_class->gesture_begin = gesture_begin; gesture_class->gesture_progress = gesture_progress; gesture_class->gesture_end = gesture_end; /* XXX:2.0 remove */ klass->swipe = clutter_swipe_action_real_swipe; /** * ClutterSwipeAction::swept: * @action: the #ClutterSwipeAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @direction: the main direction of the swipe gesture * * The ::swept signal is emitted when a swipe gesture is recognized on the * attached actor. * * Deprecated: 1.14: Use the ::swipe signal instead. * * Since: 1.8 */ swipe_signals[SWEPT] = g_signal_new (I_("swept"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterSwipeActionClass, swept), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLAGS, G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_SWIPE_DIRECTION); /** * ClutterSwipeAction::swipe: * @action: the #ClutterSwipeAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @direction: the main direction of the swipe gesture * * The ::swipe signal is emitted when a swipe gesture is recognized on the * attached actor. * * Return value: %TRUE if the swipe should continue, and %FALSE if * the swipe should be cancelled. * * Since: 1.14 */ swipe_signals[SWIPE] = g_signal_new (I_("swipe"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterSwipeActionClass, swipe), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_FLAGS, G_TYPE_BOOLEAN, 2, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_SWIPE_DIRECTION); } static void clutter_swipe_action_init (ClutterSwipeAction *self) { self->priv = clutter_swipe_action_get_instance_private (self); } /** * clutter_swipe_action_new: * * Creates a new #ClutterSwipeAction instance * * Return value: the newly created #ClutterSwipeAction * * Since: 1.8 */ ClutterAction * clutter_swipe_action_new (void) { return g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL); } muffin-6.4.1/clutter/clutter/clutter-bind-constraint.h0000664000175000017500000000606114723361714022040 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_BIND_CONSTRAINT_H__ #define __CLUTTER_BIND_CONSTRAINT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_BIND_CONSTRAINT (clutter_bind_constraint_get_type ()) #define CLUTTER_BIND_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIND_CONSTRAINT, ClutterBindConstraint)) #define CLUTTER_IS_BIND_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIND_CONSTRAINT)) /** * ClutterBindConstraint: * * #ClutterBindConstraint is an opaque structure * whose members cannot be directly accessed * * Since: 1.4 */ typedef struct _ClutterBindConstraint ClutterBindConstraint; typedef struct _ClutterBindConstraintClass ClutterBindConstraintClass; CLUTTER_EXPORT GType clutter_bind_constraint_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterConstraint * clutter_bind_constraint_new (ClutterActor *source, ClutterBindCoordinate coordinate, gfloat offset); CLUTTER_EXPORT void clutter_bind_constraint_set_source (ClutterBindConstraint *constraint, ClutterActor *source); CLUTTER_EXPORT ClutterActor * clutter_bind_constraint_get_source (ClutterBindConstraint *constraint); CLUTTER_EXPORT void clutter_bind_constraint_set_coordinate (ClutterBindConstraint *constraint, ClutterBindCoordinate coordinate); CLUTTER_EXPORT ClutterBindCoordinate clutter_bind_constraint_get_coordinate (ClutterBindConstraint *constraint); CLUTTER_EXPORT void clutter_bind_constraint_set_offset (ClutterBindConstraint *constraint, gfloat offset); CLUTTER_EXPORT gfloat clutter_bind_constraint_get_offset (ClutterBindConstraint *constraint); G_END_DECLS #endif /* __CLUTTER_BIND_CONSTRAINT_H__ */ muffin-6.4.1/clutter/clutter/clutter-keysyms-update.pl0000775000175000017500000001205714723361714022117 0ustar fabiofabio#!/usr/bin/env perl # Author : Simos Xenitellis . # Authos : Bastien Nocera # Version : 1.2 # # Notes : It downloads keysymdef.h from the Internet, if not found locally, # Notes : and creates an updated clutter-keysyms.h use strict; my $update_url = 'http://git.clutter-project.org/clutter/plain/clutter/clutter-keysyms-update.pl'; # Used for reading the keysymdef symbols. my @keysymelements; my $keysymdef_url = 'http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h'; if ( ! -f "keysymdef.h" ) { print "Trying to download keysymdef.h from\n", $keysymdef_url, "\n"; die "Unable to download keysymdef.h: $!" unless system("wget -c -O keysymdef.h \"$keysymdef_url\"") == 0; print " done.\n\n"; } else { print "We are using existing keysymdef.h found in this directory.\n"; print "It is assumed that you took care and it is a recent version\n"; } my $XF86keysym_url = 'http://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h'; if ( ! -f "XF86keysym.h" ) { print "Trying to download XF86keysym.h from\n", $XF86keysym_url, "\n"; die "Unable to download keysymdef.h: $!\n" unless system("wget -c -O XF86keysym.h \"$XF86keysym_url\"") == 0; print " done.\n\n"; } else { print "We are using existing XF86keysym.h found in this directory.\n"; print "It is assumed that you took care and it is a recent version\n"; } if ( -f "clutter-keysyms.h" ) { print "There is already a clutter-keysyms.h file in this directory. We are not overwriting it.\n"; print "Please move it somewhere else in order to run this script.\n"; die "Exiting...\n\n"; } die "Could not open file keysymdef.h: $!\n" unless open(IN_KEYSYMDEF, "<:utf8", "keysymdef.h"); # Output: clutter/clutter/clutter-keysyms.h die "Could not open file clutter-keysyms.h: $!\n" unless open(OUT_KEYSYMS, ">:utf8", "clutter-keysyms.h"); my $LICENSE_HEADER= <. */ EOF print OUT_KEYSYMS $LICENSE_HEADER; print OUT_KEYSYMS<) { next if ( ! /^#define / ); @keysymelements = split(/\s+/); die "Internal error, no \@keysymelements: $_\n" unless @keysymelements; $_ = $keysymelements[1]; die "Internal error, was expecting \"XC_*\", found: $_\n" if ( ! /^XK_/ ); $_ = $keysymelements[2]; die "Internal error, was expecting \"0x*\", found: $_\n" if ( ! /^0x/ ); my $element = $keysymelements[1]; my $binding = $element; $binding =~ s/^XK_/CLUTTER_KEY_/g; printf OUT_KEYSYMS "#define %s 0x%03x\n", $binding, hex($keysymelements[2]); } close IN_KEYSYMDEF; #$cluttersyms{"0"} = "0000"; # Source: http://gitweb.freedesktop.org/?p=xorg/proto/x11proto.git;a=blob;f=XF86keysym.h die "Could not open file XF86keysym.h: $!\n" unless open(IN_XF86KEYSYM, "<:utf8", "XF86keysym.h"); while () { next if ( ! /^#define / ); @keysymelements = split(/\s+/); die "Internal error, no \@keysymelements: $_\n" unless @keysymelements; $_ = $keysymelements[1]; die "Internal error, was expecting \"XF86XK_*\", found: $_\n" if ( ! /^XF86XK_/ ); # Work-around https://bugs.freedesktop.org/show_bug.cgi?id=11193 if ($_ eq "XF86XK_XF86BackForward") { $keysymelements[1] = "XF86XK_AudioForward"; } # XF86XK_Clear could end up a dupe of XK_Clear # XF86XK_Select could end up a dupe of XK_Select if ($_ eq "XF86XK_Clear") { $keysymelements[1] = "XF86XK_WindowClear"; } if ($_ eq "XF86XK_Select") { $keysymelements[1] = "XF86XK_SelectButton"; } # Ignore XF86XK_Q next if ( $_ eq "XF86XK_Q"); # XF86XK_Calculater is misspelled, and a dupe next if ( $_ eq "XF86XK_Calculater"); $_ = $keysymelements[2]; die "Internal error, was expecting \"0x*\", found: $_\n" if ( ! /^0x/ ); my $element = $keysymelements[1]; my $binding = $element; $binding =~ s/^XF86XK_/CLUTTER_KEY_/g; printf OUT_KEYSYMS "#define %s 0x%03x\n", $binding, hex($keysymelements[2]); } close IN_XF86KEYSYM; print OUT_KEYSYMS< * Emmanuele Bassi * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_SCRIPTABLE_H__ #define __CLUTTER_SCRIPTABLE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_SCRIPTABLE (clutter_scriptable_get_type ()) #define CLUTTER_SCRIPTABLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCRIPTABLE, ClutterScriptable)) #define CLUTTER_IS_SCRIPTABLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCRIPTABLE)) #define CLUTTER_SCRIPTABLE_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_SCRIPTABLE, ClutterScriptableIface)) typedef struct _ClutterScriptable ClutterScriptable; typedef struct _ClutterScriptableIface ClutterScriptableIface; /** * ClutterScriptable: * * #ClutterScriptable is an opaque structure whose members cannot be directly * accessed * * Since: 0.6 */ /** * ClutterScriptableIface: * @set_id: virtual function for setting the id of a scriptable object * @get_id: virtual function for getting the id of a scriptable object * @parse_custom_node: virtual function for parsing complex data containers * into GObject properties * @set_custom_property: virtual function for setting a custom property * * Interface for implementing "scriptable" objects. An object implementing * this interface can override the parsing and properties setting sequence * when loading a UI definition data with #ClutterScript * * Since: 0.6 */ struct _ClutterScriptableIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ void (* set_id) (ClutterScriptable *scriptable, const gchar *id_); const gchar *(* get_id) (ClutterScriptable *scriptable); gboolean (* parse_custom_node) (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node); void (* set_custom_property) (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value); }; CLUTTER_EXPORT GType clutter_scriptable_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_scriptable_set_id (ClutterScriptable *scriptable, const gchar *id_); CLUTTER_EXPORT const gchar * clutter_scriptable_get_id (ClutterScriptable *scriptable); CLUTTER_EXPORT gboolean clutter_scriptable_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node); CLUTTER_EXPORT void clutter_scriptable_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value); G_END_DECLS #endif /* __CLUTTER_SCRIPTABLE_H__ */ muffin-6.4.1/clutter/clutter/clutter-deform-effect.c0000664000175000017500000006053614723361714021452 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on the MxDeformTexture class, written by: * Chris Lord */ /** * SECTION:clutter-deform-effect * @Title: ClutterDeformEffect * @Short_Description: A base class for effects deforming the geometry * of an actor * * #ClutterDeformEffect is an abstract class providing all the plumbing * for creating effects that result in the deformation of an actor's * geometry. * * #ClutterDeformEffect uses offscreen buffers to render the contents of * a #ClutterActor and then the Cogl vertex buffers API to submit the * geometry to the GPU. * * #ClutterDeformEffect is available since Clutter 1.4 * * ## Implementing ClutterDeformEffect * * Sub-classes of #ClutterDeformEffect should override the * #ClutterDeformEffectClass.deform_vertex() virtual function; this function * is called on every vertex that needs to be deformed by the effect. * Each passed vertex is an in-out parameter that initially contains the * position of the vertex and should be modified according to a specific * deformation algorithm. */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-deform-effect.h" #include #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-offscreen-effect-private.h" #include "clutter-private.h" #define DEFAULT_N_TILES 32 struct _ClutterDeformEffectPrivate { CoglPipeline *back_pipeline; gint x_tiles; gint y_tiles; CoglAttributeBuffer *buffer; CoglPrimitive *primitive; CoglPrimitive *lines_primitive; gint n_vertices; gulong allocation_id; guint is_dirty : 1; }; enum { PROP_0, PROP_X_TILES, PROP_Y_TILES, PROP_BACK_MATERIAL, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterDeformEffect, clutter_deform_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT) static void clutter_deform_effect_real_deform_vertex (ClutterDeformEffect *effect, gfloat width, gfloat height, CoglTextureVertex *vertex) { g_warning ("%s: Deformation effect of type '%s' does not implement " "the required ClutterDeformEffect::deform_vertex virtual " "function.", G_STRLOC, G_OBJECT_TYPE_NAME (effect)); } static void clutter_deform_effect_deform_vertex (ClutterDeformEffect *effect, gfloat width, gfloat height, CoglTextureVertex *vertex) { CLUTTER_DEFORM_EFFECT_GET_CLASS (effect)->deform_vertex (effect, width, height, vertex); } static void vbo_invalidate (ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags, ClutterDeformEffect *effect) { effect->priv->is_dirty = TRUE; } static void clutter_deform_effect_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterDeformEffectPrivate *priv = CLUTTER_DEFORM_EFFECT (meta)->priv; if (priv->allocation_id != 0) { ClutterActor *old_actor = clutter_actor_meta_get_actor (meta); if (old_actor != NULL) g_clear_signal_handler (&priv->allocation_id, old_actor); priv->allocation_id = 0; } /* we need to invalidate the VBO whenever the allocation of the actor * changes */ if (actor != NULL) priv->allocation_id = g_signal_connect (actor, "allocation-changed", G_CALLBACK (vbo_invalidate), meta); priv->is_dirty = TRUE; CLUTTER_ACTOR_META_CLASS (clutter_deform_effect_parent_class)->set_actor (meta, actor); } static void clutter_deform_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterDeformEffect *self= CLUTTER_DEFORM_EFFECT (effect); ClutterDeformEffectPrivate *priv = self->priv; CoglHandle material; CoglPipeline *pipeline; CoglDepthState depth_state; CoglFramebuffer *fb = clutter_paint_context_get_framebuffer (paint_context); if (priv->is_dirty) { graphene_rect_t rect; gboolean mapped_buffer; CoglVertexP3T2C4 *verts; ClutterActor *actor; gfloat width, height; guint opacity; gint i, j; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); opacity = clutter_actor_get_paint_opacity (actor); /* if we don't have a target size, fall back to the actor's * allocation, though wrong it might be */ if (clutter_offscreen_effect_get_target_rect (effect, &rect)) { width = graphene_rect_get_width (&rect); height = graphene_rect_get_height (&rect); } else clutter_actor_get_size (actor, &width, &height); /* XXX ideally, the sub-classes should tell us what they * changed in the texture vertices; we then would be able to * avoid resubmitting the same data, if it did not change. for * the time being, we resubmit everything */ verts = cogl_buffer_map (COGL_BUFFER (priv->buffer), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); /* If the map failed then we'll resort to allocating a temporary buffer */ if (verts == NULL) { mapped_buffer = FALSE; verts = g_malloc (sizeof (*verts) * priv->n_vertices); } else mapped_buffer = TRUE; for (i = 0; i < priv->y_tiles + 1; i++) { for (j = 0; j < priv->x_tiles + 1; j++) { CoglVertexP3T2C4 *vertex_out; CoglTextureVertex vertex; /* CoglTextureVertex isn't an ideal structure to use for this because it contains a CoglColor. The internal layout of CoglColor is mean to be private so Clutter can not pass a pointer to it as a vertex attribute. Also it contains padding so we end up storing more data in the vertex buffer than we need to. Instead we let the application modify a dummy vertex and then copy the details back out to a more well-defined struct */ vertex.tx = (float) j / priv->x_tiles; vertex.ty = (float) i / priv->y_tiles; vertex.x = width * vertex.tx; vertex.y = height * vertex.ty; vertex.z = 0.0f; cogl_color_init_from_4ub (&vertex.color, 255, 255, 255, opacity); clutter_deform_effect_deform_vertex (self, width, height, &vertex); vertex_out = verts + i * (priv->x_tiles + 1) + j; vertex_out->x = vertex.x; vertex_out->y = vertex.y; vertex_out->z = vertex.z; vertex_out->s = vertex.tx; vertex_out->t = vertex.ty; vertex_out->r = cogl_color_get_red_byte (&vertex.color); vertex_out->g = cogl_color_get_green_byte (&vertex.color); vertex_out->b = cogl_color_get_blue_byte (&vertex.color); vertex_out->a = cogl_color_get_alpha_byte (&vertex.color); } } if (mapped_buffer) cogl_buffer_unmap (COGL_BUFFER (priv->buffer)); else { cogl_buffer_set_data (COGL_BUFFER (priv->buffer), 0, /* offset */ verts, sizeof (*verts) * priv->n_vertices); g_free (verts); } priv->is_dirty = FALSE; } material = clutter_offscreen_effect_get_target (effect); pipeline = COGL_PIPELINE (material); /* enable depth testing */ cogl_depth_state_init (&depth_state); cogl_depth_state_set_test_enabled (&depth_state, TRUE); cogl_depth_state_set_test_function (&depth_state, COGL_DEPTH_TEST_FUNCTION_LEQUAL); cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL); /* enable backface culling if we have a back material */ if (priv->back_pipeline != NULL) cogl_pipeline_set_cull_face_mode (pipeline, COGL_PIPELINE_CULL_FACE_MODE_BACK); /* draw the front */ if (material != NULL) cogl_framebuffer_draw_primitive (fb, pipeline, priv->primitive); /* draw the back */ if (priv->back_pipeline != NULL) { CoglPipeline *back_pipeline; /* We probably shouldn't be modifying the user's material so instead we make a temporary copy */ back_pipeline = cogl_pipeline_copy (priv->back_pipeline); cogl_pipeline_set_depth_state (back_pipeline, &depth_state, NULL); cogl_pipeline_set_cull_face_mode (back_pipeline, COGL_PIPELINE_CULL_FACE_MODE_FRONT); cogl_framebuffer_draw_primitive (fb, back_pipeline, priv->primitive); cogl_object_unref (back_pipeline); } if (G_UNLIKELY (priv->lines_primitive != NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglPipeline *lines_pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4f (lines_pipeline, 1.0, 0, 0, 1.0); cogl_framebuffer_draw_primitive (fb, lines_pipeline, priv->lines_primitive); cogl_object_unref (lines_pipeline); } } static inline void clutter_deform_effect_free_arrays (ClutterDeformEffect *self) { ClutterDeformEffectPrivate *priv = self->priv; if (priv->buffer) { cogl_object_unref (priv->buffer); priv->buffer = NULL; } if (priv->primitive) { cogl_object_unref (priv->primitive); priv->primitive = NULL; } if (priv->lines_primitive) { cogl_object_unref (priv->lines_primitive); priv->lines_primitive = NULL; } } static void clutter_deform_effect_init_arrays (ClutterDeformEffect *self) { ClutterDeformEffectPrivate *priv = self->priv; gint x, y, direction, n_indices; CoglAttribute *attributes[3]; guint16 *static_indices; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglIndices *indices; guint16 *idx; int i; clutter_deform_effect_free_arrays (self); n_indices = ((2 + 2 * priv->x_tiles) * priv->y_tiles + (priv->y_tiles - 1)); static_indices = g_new (guint16, n_indices); #define MESH_INDEX(x,y) ((y) * (priv->x_tiles + 1) + (x)) /* compute all the triangles from the various tiles */ direction = 1; idx = static_indices; idx[0] = MESH_INDEX (0, 0); idx[1] = MESH_INDEX (0, 1); idx += 2; for (y = 0; y < priv->y_tiles; y++) { for (x = 0; x < priv->x_tiles; x++) { if (direction) { idx[0] = MESH_INDEX (x + 1, y); idx[1] = MESH_INDEX (x + 1, y + 1); } else { idx[0] = MESH_INDEX (priv->x_tiles - x - 1, y); idx[1] = MESH_INDEX (priv->x_tiles - x - 1, y + 1); } idx += 2; } if (y == (priv->y_tiles - 1)) break; if (direction) { idx[0] = MESH_INDEX (priv->x_tiles, y + 1); idx[1] = MESH_INDEX (priv->x_tiles, y + 1); idx[2] = MESH_INDEX (priv->x_tiles, y + 2); } else { idx[0] = MESH_INDEX (0, y + 1); idx[1] = MESH_INDEX (0, y + 1); idx[2] = MESH_INDEX (0, y + 2); } idx += 3; direction = !direction; } #undef MESH_INDEX indices = cogl_indices_new (ctx, COGL_INDICES_TYPE_UNSIGNED_SHORT, static_indices, n_indices); g_free (static_indices); priv->n_vertices = (priv->x_tiles + 1) * (priv->y_tiles + 1); priv->buffer = cogl_attribute_buffer_new (ctx, sizeof (CoglVertexP3T2C4) * priv->n_vertices, NULL); /* The application is expected to continuously modify the vertices so we should give a hint to Cogl about that */ cogl_buffer_set_update_hint (COGL_BUFFER (priv->buffer), COGL_BUFFER_UPDATE_HINT_DYNAMIC); attributes[0] = cogl_attribute_new (priv->buffer, "cogl_position_in", sizeof (CoglVertexP3T2C4), G_STRUCT_OFFSET (CoglVertexP3T2C4, x), 3, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (priv->buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP3T2C4), G_STRUCT_OFFSET (CoglVertexP3T2C4, s), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); attributes[2] = cogl_attribute_new (priv->buffer, "cogl_color_in", sizeof (CoglVertexP3T2C4), G_STRUCT_OFFSET (CoglVertexP3T2C4, r), 4, /* n_components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); priv->primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLE_STRIP, priv->n_vertices, attributes, 3 /* n_attributes */); cogl_primitive_set_indices (priv->primitive, indices, n_indices); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DEFORM_TILES)) { priv->lines_primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP, priv->n_vertices, attributes, 2 /* n_attributes */); cogl_primitive_set_indices (priv->lines_primitive, indices, n_indices); } cogl_object_unref (indices); for (i = 0; i < 3; i++) cogl_object_unref (attributes[i]); priv->is_dirty = TRUE; } static inline void clutter_deform_effect_free_back_pipeline (ClutterDeformEffect *self) { ClutterDeformEffectPrivate *priv = self->priv; if (priv->back_pipeline != NULL) { cogl_object_unref (priv->back_pipeline); priv->back_pipeline = NULL; } } static void clutter_deform_effect_finalize (GObject *gobject) { ClutterDeformEffect *self = CLUTTER_DEFORM_EFFECT (gobject); clutter_deform_effect_free_arrays (self); clutter_deform_effect_free_back_pipeline (self); G_OBJECT_CLASS (clutter_deform_effect_parent_class)->finalize (gobject); } static void clutter_deform_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterDeformEffect *self = CLUTTER_DEFORM_EFFECT (gobject); switch (prop_id) { case PROP_X_TILES: clutter_deform_effect_set_n_tiles (self, g_value_get_uint (value), self->priv->y_tiles); break; case PROP_Y_TILES: clutter_deform_effect_set_n_tiles (self, self->priv->x_tiles, g_value_get_uint (value)); break; case PROP_BACK_MATERIAL: clutter_deform_effect_set_back_material (self, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_deform_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterDeformEffectPrivate *priv = CLUTTER_DEFORM_EFFECT (gobject)->priv; switch (prop_id) { case PROP_X_TILES: g_value_set_uint (value, priv->x_tiles); break; case PROP_Y_TILES: g_value_set_uint (value, priv->y_tiles); break; case PROP_BACK_MATERIAL: g_value_set_boxed (value, priv->back_pipeline); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_deform_effect_class_init (ClutterDeformEffectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); klass->deform_vertex = clutter_deform_effect_real_deform_vertex; /** * ClutterDeformEffect:x-tiles: * * The number of horizontal tiles. The bigger the number, the * smaller the tiles * * Since: 1.4 */ obj_props[PROP_X_TILES] = g_param_spec_uint ("x-tiles", P_("Horizontal Tiles"), P_("The number of horizontal tiles"), 1, G_MAXUINT, DEFAULT_N_TILES, CLUTTER_PARAM_READWRITE); /** * ClutterDeformEffect:y-tiles: * * The number of vertical tiles. The bigger the number, the * smaller the tiles * * Since: 1.4 */ obj_props[PROP_Y_TILES] = g_param_spec_uint ("y-tiles", P_("Vertical Tiles"), P_("The number of vertical tiles"), 1, G_MAXUINT, DEFAULT_N_TILES, CLUTTER_PARAM_READWRITE); /** * ClutterDeformEffect:back-material: * * A material to be used when painting the back of the actor * to which this effect has been applied * * By default, no material will be used * * Since: 1.4 */ obj_props[PROP_BACK_MATERIAL] = g_param_spec_boxed ("back-material", P_("Back Material"), P_("The material to be used when painting the back of the actor"), COGL_TYPE_HANDLE, CLUTTER_PARAM_READWRITE); gobject_class->finalize = clutter_deform_effect_finalize; gobject_class->set_property = clutter_deform_effect_set_property; gobject_class->get_property = clutter_deform_effect_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); meta_class->set_actor = clutter_deform_effect_set_actor; offscreen_class->paint_target = clutter_deform_effect_paint_target; } static void clutter_deform_effect_init (ClutterDeformEffect *self) { self->priv = clutter_deform_effect_get_instance_private (self); self->priv->x_tiles = self->priv->y_tiles = DEFAULT_N_TILES; self->priv->back_pipeline = NULL; clutter_deform_effect_init_arrays (self); } /** * clutter_deform_effect_set_back_material: * @effect: a #ClutterDeformEffect * @material: (allow-none): a handle to a Cogl material * * Sets the material that should be used when drawing the back face * of the actor during a deformation * * The #ClutterDeformEffect will take a reference on the material's * handle * * Since: 1.4 */ void clutter_deform_effect_set_back_material (ClutterDeformEffect *effect, CoglHandle material) { ClutterDeformEffectPrivate *priv; CoglPipeline *pipeline = COGL_PIPELINE (material); g_return_if_fail (CLUTTER_IS_DEFORM_EFFECT (effect)); g_return_if_fail (pipeline == NULL || cogl_is_pipeline (pipeline)); priv = effect->priv; clutter_deform_effect_free_back_pipeline (effect); priv->back_pipeline = material; if (priv->back_pipeline != NULL) cogl_object_ref (priv->back_pipeline); clutter_deform_effect_invalidate (effect); } /** * clutter_deform_effect_get_back_material: * @effect: a #ClutterDeformEffect * * Retrieves the handle to the back face material used by @effect * * Return value: (transfer none): a handle for the material, or %NULL. * The returned material is owned by the #ClutterDeformEffect and it * should not be freed directly * * Since: 1.4 */ CoglHandle clutter_deform_effect_get_back_material (ClutterDeformEffect *effect) { g_return_val_if_fail (CLUTTER_IS_DEFORM_EFFECT (effect), NULL); return effect->priv->back_pipeline; } /** * clutter_deform_effect_set_n_tiles: * @effect: a #ClutterDeformEffect * @x_tiles: number of horizontal tiles * @y_tiles: number of vertical tiles * * Sets the number of horizontal and vertical tiles to be used * when applying the effect * * More tiles allow a finer grained deformation at the expenses * of computation * * Since: 1.4 */ void clutter_deform_effect_set_n_tiles (ClutterDeformEffect *effect, guint x_tiles, guint y_tiles) { ClutterDeformEffectPrivate *priv; gboolean tiles_changed = FALSE; g_return_if_fail (CLUTTER_IS_DEFORM_EFFECT (effect)); g_return_if_fail (x_tiles > 0 && y_tiles > 0); priv = effect->priv; g_object_freeze_notify (G_OBJECT (effect)); if (priv->x_tiles != x_tiles) { priv->x_tiles = x_tiles; g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_X_TILES]); tiles_changed = TRUE; } if (priv->y_tiles != y_tiles) { priv->y_tiles = y_tiles; g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_Y_TILES]); tiles_changed = TRUE; } if (tiles_changed) { clutter_deform_effect_init_arrays (effect); clutter_deform_effect_invalidate (effect); } g_object_thaw_notify (G_OBJECT (effect)); } /** * clutter_deform_effect_get_n_tiles: * @effect: a #ClutterDeformEffect * @x_tiles: (out): return location for the number of horizontal tiles, * or %NULL * @y_tiles: (out): return location for the number of vertical tiles, * or %NULL * * Retrieves the number of horizontal and vertical tiles used to sub-divide * the actor's geometry during the effect * * Since: 1.4 */ void clutter_deform_effect_get_n_tiles (ClutterDeformEffect *effect, guint *x_tiles, guint *y_tiles) { g_return_if_fail (CLUTTER_IS_DEFORM_EFFECT (effect)); if (x_tiles != NULL) *x_tiles = effect->priv->x_tiles; if (y_tiles != NULL) *y_tiles = effect->priv->y_tiles; } /** * clutter_deform_effect_invalidate: * @effect: a #ClutterDeformEffect * * Invalidates the @effect's vertices and, if it is associated * to an actor, it will queue a redraw * * Since: 1.4 */ void clutter_deform_effect_invalidate (ClutterDeformEffect *effect) { ClutterActor *actor; g_return_if_fail (CLUTTER_IS_DEFORM_EFFECT (effect)); if (effect->priv->is_dirty) return; effect->priv->is_dirty = TRUE; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); if (actor != NULL) clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); } muffin-6.4.1/clutter/clutter/clutter-scriptable.c0000664000175000017500000001307214723361714021065 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Emmanuele Bassi * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-scriptable * @short_description: Override the UI definition parsing * * The #ClutterScriptableIface interface exposes the UI definition parsing * process to external classes. By implementing this interface, a class can * override the UI definition parsing and transform complex data types into * GObject properties, or allow custom properties. * * #ClutterScriptable is available since Clutter 0.6 */ #include "clutter-build-config.h" #include #include #include #include "clutter-scriptable.h" #include "clutter-script-private.h" #include "clutter-private.h" #include "clutter-debug.h" typedef ClutterScriptableIface ClutterScriptableInterface; G_DEFINE_INTERFACE (ClutterScriptable, clutter_scriptable, G_TYPE_OBJECT); static void clutter_scriptable_default_init (ClutterScriptableInterface *iface) { } /** * clutter_scriptable_set_id: * @scriptable: a #ClutterScriptable * @id_: the #ClutterScript id of the object * * Sets @id_ as the unique Clutter script it for this instance of * #ClutterScriptableIface. * * This name can be used by user interface designer applications to * define a unique name for an object constructable using the UI * definition language parsed by #ClutterScript. * * Since: 0.6 */ void clutter_scriptable_set_id (ClutterScriptable *scriptable, const gchar *id_) { ClutterScriptableIface *iface; g_return_if_fail (CLUTTER_IS_SCRIPTABLE (scriptable)); g_return_if_fail (id_ != NULL); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->set_id) iface->set_id (scriptable, id_); else g_object_set_data_full (G_OBJECT (scriptable), "clutter-script-id", g_strdup (id_), g_free); } /** * clutter_scriptable_get_id: * @scriptable: a #ClutterScriptable * * Retrieves the id of @scriptable set using clutter_scriptable_set_id(). * * Return value: the id of the object. The returned string is owned by * the scriptable object and should never be modified of freed * * Since: 0.6 */ const gchar * clutter_scriptable_get_id (ClutterScriptable *scriptable) { ClutterScriptableIface *iface; g_return_val_if_fail (CLUTTER_IS_SCRIPTABLE (scriptable), NULL); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->get_id) return iface->get_id (scriptable); else return g_object_get_data (G_OBJECT (scriptable), "clutter-script-id"); } /** * clutter_scriptable_parse_custom_node: * @scriptable: a #ClutterScriptable * @script: the #ClutterScript creating the scriptable instance * @value: the generic value to be set * @name: the name of the node * @node: the JSON node to be parsed * * Parses the passed JSON node. The implementation must set the type * of the passed #GValue pointer using g_value_init(). * * Return value: %TRUE if the node was successfully parsed, %FALSE otherwise. * * Since: 0.6 */ gboolean clutter_scriptable_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { ClutterScriptableIface *iface; g_return_val_if_fail (CLUTTER_IS_SCRIPTABLE (scriptable), FALSE); g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (node != NULL, FALSE); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->parse_custom_node) return iface->parse_custom_node (scriptable, script, value, name, node); return FALSE; } /** * clutter_scriptable_set_custom_property: * @scriptable: a #ClutterScriptable * @script: the #ClutterScript creating the scriptable instance * @name: the name of the property * @value: the value of the property * * Overrides the common properties setting. The underlying virtual * function should be used when implementing custom properties. * * Since: 0.6 */ void clutter_scriptable_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { ClutterScriptableIface *iface; g_return_if_fail (CLUTTER_IS_SCRIPTABLE (scriptable)); g_return_if_fail (CLUTTER_IS_SCRIPT (script)); g_return_if_fail (name != NULL); g_return_if_fail (value != NULL); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->set_custom_property) iface->set_custom_property (scriptable, script, name, value); } muffin-6.4.1/clutter/clutter/clutter-grid-layout.h0000664000175000017500000001516714723361714021211 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Red Hat, Inc. * Copyright (C) 2012 Bastian Winkler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Bastian Winkler * * Based on GtkGrid widget by: * Matthias Clasen */ #ifndef __CLUTTER_GRID_LAYOUT_H__ #define __CLUTTER_GRID_LAYOUT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_GRID_LAYOUT (clutter_grid_layout_get_type ()) #define CLUTTER_GRID_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayout)) #define CLUTTER_IS_GRID_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_LAYOUT)) #define CLUTTER_GRID_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass)) #define CLUTTER_IS_GRID_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GRID_LAYOUT)) #define CLUTTER_GRID_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GRID_LAYOUT, ClutterGridLayoutClass)) typedef struct _ClutterGridLayout ClutterGridLayout; typedef struct _ClutterGridLayoutPrivate ClutterGridLayoutPrivate; typedef struct _ClutterGridLayoutClass ClutterGridLayoutClass; /** * ClutterGridLayout: * * The #ClutterGridLayout structure contains only private data * and should be accessed using the provided API * * Since: 1.12 */ struct _ClutterGridLayout { /*< private >*/ ClutterLayoutManager parent_instance; ClutterGridLayoutPrivate *priv; }; /** * ClutterGridLayoutClass: * * The #ClutterGridLayoutClass structure contains only private * data and should be accessed using the provided API * * Since: 1.12 */ struct _ClutterGridLayoutClass { /*< private >*/ ClutterLayoutManagerClass parent_class; gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_grid_layout_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager * clutter_grid_layout_new (void); CLUTTER_EXPORT void clutter_grid_layout_attach (ClutterGridLayout *layout, ClutterActor *child, gint left, gint top, gint width, gint height); CLUTTER_EXPORT void clutter_grid_layout_attach_next_to (ClutterGridLayout *layout, ClutterActor *child, ClutterActor *sibling, ClutterGridPosition side, gint width, gint height); CLUTTER_EXPORT ClutterActor * clutter_grid_layout_get_child_at (ClutterGridLayout *layout, gint left, gint top); CLUTTER_EXPORT void clutter_grid_layout_insert_row (ClutterGridLayout *layout, gint position); CLUTTER_EXPORT void clutter_grid_layout_insert_column (ClutterGridLayout *layout, gint position); CLUTTER_EXPORT void clutter_grid_layout_insert_next_to (ClutterGridLayout *layout, ClutterActor *sibling, ClutterGridPosition side); CLUTTER_EXPORT void clutter_grid_layout_set_orientation (ClutterGridLayout *layout, ClutterOrientation orientation); CLUTTER_EXPORT ClutterOrientation clutter_grid_layout_get_orientation (ClutterGridLayout *layout); CLUTTER_EXPORT void clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout, guint spacing); CLUTTER_EXPORT guint clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout); CLUTTER_EXPORT void clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout, guint spacing); CLUTTER_EXPORT guint clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout); CLUTTER_EXPORT void clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout, gboolean homogeneous); CLUTTER_EXPORT gboolean clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout); CLUTTER_EXPORT void clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout, gboolean homogeneous); CLUTTER_EXPORT gboolean clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout); G_END_DECLS #endif /* __CLUTTER_GRID_LAYOUT_H__ */ muffin-6.4.1/clutter/clutter/clutter-paint-node.h0000664000175000017500000001277514723361714021011 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_PAINT_NODE_H__ #define __CLUTTER_PAINT_NODE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_PAINT_NODE (clutter_paint_node_get_type ()) #define CLUTTER_PAINT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNode)) #define CLUTTER_IS_PAINT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PAINT_NODE)) typedef struct _ClutterPaintNodePrivate ClutterPaintNodePrivate; typedef struct _ClutterPaintNodeClass ClutterPaintNodeClass; CLUTTER_EXPORT GType clutter_paint_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_paint_node_ref (ClutterPaintNode *node); CLUTTER_EXPORT void clutter_paint_node_unref (ClutterPaintNode *node); CLUTTER_EXPORT void clutter_paint_node_paint (ClutterPaintNode *node, ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_paint_node_set_name (ClutterPaintNode *node, const char *name); CLUTTER_EXPORT void clutter_paint_node_set_static_name (ClutterPaintNode *node, const char *name); CLUTTER_EXPORT CoglFramebuffer * clutter_paint_node_get_framebuffer (ClutterPaintNode *node); CLUTTER_EXPORT void clutter_paint_node_add_child (ClutterPaintNode *node, ClutterPaintNode *child); CLUTTER_EXPORT void clutter_paint_node_add_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect); CLUTTER_EXPORT void clutter_paint_node_add_texture_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect, float x_1, float y_1, float x_2, float y_2); CLUTTER_EXPORT void clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect, const float *text_coords, unsigned int text_coords_len); CLUTTER_EXPORT void clutter_paint_node_add_path (ClutterPaintNode *node, CoglPath *path); CLUTTER_EXPORT void clutter_paint_node_add_primitive (ClutterPaintNode *node, CoglPrimitive *primitive); /** * CLUTTER_VALUE_HOLDS_PAINT_NODE: * @value: a #GValue * * Evaluates to %TRUE if the @value has been initialized to hold * a #ClutterPaintNode. * * Since: 1.10 */ #define CLUTTER_VALUE_HOLDS_PAINT_NODE(value) (G_VALUE_HOLDS (value, CLUTTER_TYPE_PAINT_NODE)) CLUTTER_EXPORT void clutter_value_set_paint_node (GValue *value, gpointer node); CLUTTER_EXPORT void clutter_value_take_paint_node (GValue *value, gpointer node); CLUTTER_EXPORT gpointer clutter_value_get_paint_node (const GValue *value); CLUTTER_EXPORT gpointer clutter_value_dup_paint_node (const GValue *value); G_END_DECLS #endif /* __CLUTTER_PAINT_NODE_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-device-private.h0000664000175000017500000001775514723361714023022 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef CLUTTER_INPUT_DEVICE_PRIVATE_H #define CLUTTER_INPUT_DEVICE_PRIVATE_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS typedef struct _ClutterAxisInfo { ClutterInputAxis axis; double min_axis; double max_axis; double min_value; double max_value; double resolution; } ClutterAxisInfo; typedef struct _ClutterKeyInfo { guint keyval; ClutterModifierType modifiers; } ClutterKeyInfo; typedef struct _ClutterScrollInfo { guint axis_id; ClutterScrollDirection direction; double increment; double last_value; guint last_value_valid : 1; } ClutterScrollInfo; typedef struct _ClutterTouchInfo { ClutterEventSequence *sequence; ClutterActor *actor; float current_x; float current_y; } ClutterTouchInfo; typedef struct _ClutterPtrA11yData { int n_btn_pressed; float current_x; float current_y; float dwell_x; float dwell_y; gboolean dwell_drag_started; gboolean dwell_gesture_started; guint dwell_timer; guint dwell_position_timer; guint secondary_click_timer; gboolean secondary_click_triggered; } ClutterPtrA11yData; struct _ClutterInputDevice { GObject parent_instance; int id; ClutterInputDeviceType device_type; ClutterInputMode device_mode; char *device_name; ClutterSeat *seat; ClutterBackend *backend; /* the associated device */ ClutterInputDevice *associated; GList *slaves; /* the actor underneath the pointer */ ClutterActor *cursor_actor; GHashTable *inv_touch_sequence_actors; /* the actor that has a grab in place for the device */ ClutterActor *pointer_grab_actor; ClutterActor *keyboard_grab_actor; GHashTable *sequence_grab_actors; GHashTable *inv_sequence_grab_actors; /* the current click count */ int click_count; /* the stage the device is on */ ClutterStage *stage; /* the current state */ float current_x; float current_y; uint32_t current_time; int current_button_number; ClutterModifierType current_state; /* the current touch points states */ GHashTable *touch_sequences_info; /* the previous state, used for click count generation */ int previous_x; int previous_y; uint32_t previous_time; int previous_button_number; ClutterModifierType previous_state; GArray *axes; guint n_keys; GArray *keys; GArray *scroll_info; char *vendor_id; char *product_id; char *node_path; GPtrArray *tools; int n_rings; int n_strips; int n_mode_groups; ClutterInputDeviceMapping mapping_mode; guint has_cursor : 1; guint is_enabled : 1; /* Accessiblity */ ClutterVirtualInputDevice *accessibility_virtual_device; ClutterPtrA11yData *ptr_a11y_data; }; CLUTTER_EXPORT void _clutter_input_device_set_associated_device (ClutterInputDevice *device, ClutterInputDevice *associated); CLUTTER_EXPORT void _clutter_input_device_add_slave (ClutterInputDevice *master, ClutterInputDevice *slave); CLUTTER_EXPORT void _clutter_input_device_remove_slave (ClutterInputDevice *master, ClutterInputDevice *slave); CLUTTER_EXPORT void clutter_input_device_update_from_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool); CLUTTER_EXPORT ClutterStage * _clutter_input_device_get_stage (ClutterInputDevice *device); CLUTTER_EXPORT void _clutter_input_device_set_stage (ClutterInputDevice *device, ClutterStage *stage); CLUTTER_EXPORT void _clutter_input_device_set_coords (ClutterInputDevice *device, ClutterEventSequence *sequence, gfloat x, gfloat y, ClutterStage *stage); CLUTTER_EXPORT void _clutter_input_device_set_state (ClutterInputDevice *device, ClutterModifierType state); CLUTTER_EXPORT void _clutter_input_device_set_time (ClutterInputDevice *device, guint32 time_); void _clutter_input_device_set_actor (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterActor *actor, gboolean emit_crossing); CLUTTER_EXPORT ClutterActor * clutter_input_device_update (ClutterInputDevice *device, ClutterEventSequence *sequence, gboolean emit_crossing); CLUTTER_EXPORT void _clutter_input_device_add_event_sequence (ClutterInputDevice *device, ClutterEvent *event); CLUTTER_EXPORT void _clutter_input_device_remove_event_sequence (ClutterInputDevice *device, ClutterEvent *event); CLUTTER_EXPORT void _clutter_input_device_set_n_keys (ClutterInputDevice *device, guint n_keys); CLUTTER_EXPORT gboolean _clutter_input_device_translate_axis (ClutterInputDevice *device, guint index_, gdouble value, gdouble *axis_value); CLUTTER_EXPORT guint _clutter_input_device_add_axis (ClutterInputDevice *device, ClutterInputAxis axis, gdouble minimum, gdouble maximum, gdouble resolution); CLUTTER_EXPORT void _clutter_input_device_reset_axes (ClutterInputDevice *device); CLUTTER_EXPORT void _clutter_input_device_add_scroll_info (ClutterInputDevice *device, guint index_, ClutterScrollDirection direction, gdouble increment); CLUTTER_EXPORT gboolean _clutter_input_device_get_scroll_delta (ClutterInputDevice *device, guint index_, gdouble value, ClutterScrollDirection *direction_p, gdouble *delta_p); CLUTTER_EXPORT void _clutter_input_device_reset_scroll_info (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_add_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool); CLUTTER_EXPORT ClutterInputDeviceTool * clutter_input_device_lookup_tool (ClutterInputDevice *device, guint64 serial, ClutterInputDeviceToolType type); #endif /* CLUTTER_INPUT_DEVICE_PRIVATE_H */ muffin-6.4.1/clutter/clutter/clutter-backend.h0000664000175000017500000000605114723361714020330 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_BACKEND_H__ #define __CLUTTER_BACKEND_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_BACKEND (clutter_backend_get_type ()) #define CLUTTER_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND, ClutterBackend)) #define CLUTTER_IS_BACKEND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND)) /** * ClutterBackend: * * #ClutterBackend is an opaque structure whose * members cannot be directly accessed. * * Since: 0.4 */ typedef struct _ClutterBackend ClutterBackend; typedef struct _ClutterBackendClass ClutterBackendClass; CLUTTER_EXPORT GType clutter_backend_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterBackend * clutter_get_default_backend (void); CLUTTER_EXPORT gdouble clutter_backend_get_resolution (ClutterBackend *backend); CLUTTER_EXPORT void clutter_backend_set_font_options (ClutterBackend *backend, const cairo_font_options_t *options); CLUTTER_EXPORT const cairo_font_options_t * clutter_backend_get_font_options (ClutterBackend *backend); CLUTTER_EXPORT CoglContext * clutter_backend_get_cogl_context (ClutterBackend *backend); CLUTTER_EXPORT ClutterInputMethod * clutter_backend_get_input_method (ClutterBackend *backend); CLUTTER_EXPORT void clutter_backend_set_input_method (ClutterBackend *backend, ClutterInputMethod *method); CLUTTER_EXPORT ClutterSeat * clutter_backend_get_default_seat (ClutterBackend *backend); G_END_DECLS #endif /* __CLUTTER_BACKEND_H__ */ muffin-6.4.1/clutter/clutter/clutter-color.c0000664000175000017500000007346014723361714020062 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-color * @short_description: Color management and manipulation. * * #ClutterColor is a simple type for representing colors in Clutter. * * A #ClutterColor is expressed as a 4-tuple of values ranging from * zero to 255, one for each color channel plus one for the alpha. * * The alpha channel is fully opaque at 255 and fully transparent at 0. */ #include "clutter-build-config.h" #include #include #include "clutter-interval.h" #include "clutter-main.h" #include "clutter-color.h" #include "clutter-private.h" #include "clutter-debug.h" /* XXX - keep in sync with the ClutterStaticColor enumeration order */ static const ClutterColor static_colors[] = { /* CGA/EGA color palette */ { 0xff, 0xff, 0xff, 0xff }, /* white */ { 0x00, 0x00, 0x00, 0xff }, /* black */ { 0xff, 0x00, 0x00, 0xff }, /* red */ { 0x80, 0x00, 0x00, 0xff }, /* dark red */ { 0x00, 0xff, 0x00, 0xff }, /* green */ { 0x00, 0x80, 0x00, 0xff }, /* dark green */ { 0x00, 0x00, 0xff, 0xff }, /* blue */ { 0x00, 0x00, 0x80, 0xff }, /* dark blue */ { 0x00, 0xff, 0xff, 0xff }, /* cyan */ { 0x00, 0x80, 0x80, 0xff }, /* dark cyan */ { 0xff, 0x00, 0xff, 0xff }, /* magenta */ { 0x80, 0x00, 0x80, 0xff }, /* dark magenta */ { 0xff, 0xff, 0x00, 0xff }, /* yellow */ { 0x80, 0x80, 0x00, 0xff }, /* dark yellow */ { 0xa0, 0xa0, 0xa4, 0xff }, /* gray */ { 0x80, 0x80, 0x80, 0xff }, /* dark gray */ { 0xc0, 0xc0, 0xc0, 0xff }, /* light gray */ /* Tango Icon color palette */ { 0xed, 0xd4, 0x00, 0xff }, /* butter */ { 0xfc, 0xe9, 0x4f, 0xff }, /* butter light */ { 0xc4, 0xa0, 0x00, 0xff }, /* butter dark */ { 0xf5, 0x79, 0x00, 0xff }, /* orange */ { 0xfc, 0xaf, 0x3e, 0xff }, /* orange light */ { 0xce, 0x5c, 0x00, 0xff }, /* orange dark */ { 0xc1, 0x7d, 0x11, 0xff }, /* chocolate */ { 0xe9, 0xb9, 0x6e, 0xff }, /* chocolate light */ { 0x8f, 0x59, 0x02, 0xff }, /* chocolate dark */ { 0x73, 0xd2, 0x16, 0xff }, /* chameleon */ { 0x8a, 0xe2, 0x34, 0xff }, /* chameleon light */ { 0x4e, 0x9a, 0x06, 0xff }, /* chameleon dark */ { 0x34, 0x65, 0xa4, 0xff }, /* sky blue */ { 0x72, 0x9f, 0xcf, 0xff }, /* sky blue light */ { 0x20, 0x4a, 0x87, 0xff }, /* sky blue dark */ { 0x75, 0x50, 0x7b, 0xff }, /* plum */ { 0xad, 0x7f, 0xa8, 0xff }, /* plum light */ { 0x5c, 0x35, 0x66, 0xff }, /* plum dark */ { 0xcc, 0x00, 0x00, 0xff }, /* scarlet red */ { 0xef, 0x29, 0x29, 0xff }, /* scarlet red light */ { 0xa4, 0x00, 0x00, 0xff }, /* scarlet red dark */ { 0xee, 0xee, 0xec, 0xff }, /* aluminium 1 */ { 0xd3, 0xd7, 0xcf, 0xff }, /* aluminium 2 */ { 0xba, 0xbd, 0xb6, 0xff }, /* aluminium 3 */ { 0x88, 0x8a, 0x85, 0xff }, /* aluminium 4 */ { 0x55, 0x57, 0x53, 0xff }, /* aluminium 5 */ { 0x2e, 0x34, 0x36, 0xff }, /* aluminium 6 */ /* last color */ { 0x00, 0x00, 0x00, 0x00 } /* transparent */ }; /** * clutter_color_get_static: * @color: the named global color * * Retrieves a static color for the given @color name * * Static colors are created by Clutter and are guaranteed to always be * available and valid * * Return value: a pointer to a static color; the returned pointer * is owned by Clutter and it should never be modified or freed * * Since: 1.6 */ const ClutterColor * clutter_color_get_static (ClutterStaticColor color) { g_return_val_if_fail (color >= CLUTTER_COLOR_WHITE && color <= CLUTTER_COLOR_TRANSPARENT, NULL); return &static_colors[color]; } /** * clutter_color_add: * @a: a #ClutterColor * @b: a #ClutterColor * @result: (out caller-allocates): return location for the result * * Adds @a to @b and saves the resulting color inside @result. * * The alpha channel of @result is set as as the maximum value * between the alpha channels of @a and @b. */ void clutter_color_add (const ClutterColor *a, const ClutterColor *b, ClutterColor *result) { g_return_if_fail (a != NULL); g_return_if_fail (b != NULL); g_return_if_fail (result != NULL); result->red = CLAMP (a->red + b->red, 0, 255); result->green = CLAMP (a->green + b->green, 0, 255); result->blue = CLAMP (a->blue + b->blue, 0, 255); result->alpha = MAX (a->alpha, b->alpha); } /** * clutter_color_subtract: * @a: a #ClutterColor * @b: a #ClutterColor * @result: (out caller-allocates): return location for the result * * Subtracts @b from @a and saves the resulting color inside @result. * * This function assumes that the components of @a are greater than the * components of @b; the result is, otherwise, undefined. * * The alpha channel of @result is set as the minimum value * between the alpha channels of @a and @b. */ void clutter_color_subtract (const ClutterColor *a, const ClutterColor *b, ClutterColor *result) { g_return_if_fail (a != NULL); g_return_if_fail (b != NULL); g_return_if_fail (result != NULL); result->red = CLAMP (a->red - b->red, 0, 255); result->green = CLAMP (a->green - b->green, 0, 255); result->blue = CLAMP (a->blue - b->blue, 0, 255); result->alpha = MIN (a->alpha, b->alpha); } /** * clutter_color_lighten: * @color: a #ClutterColor * @result: (out caller-allocates): return location for the lighter color * * Lightens @color by a fixed amount, and saves the changed color * in @result. */ void clutter_color_lighten (const ClutterColor *color, ClutterColor *result) { clutter_color_shade (color, 1.3, result); } /** * clutter_color_darken: * @color: a #ClutterColor * @result: (out caller-allocates): return location for the darker color * * Darkens @color by a fixed amount, and saves the changed color * in @result. */ void clutter_color_darken (const ClutterColor *color, ClutterColor *result) { clutter_color_shade (color, 0.7, result); } /** * clutter_color_to_hls: * @color: a #ClutterColor * @hue: (out): return location for the hue value or %NULL * @luminance: (out): return location for the luminance value or %NULL * @saturation: (out): return location for the saturation value or %NULL * * Converts @color to the HLS format. * * The @hue value is in the 0 .. 360 range. The @luminance and * @saturation values are in the 0 .. 1 range. */ void clutter_color_to_hls (const ClutterColor *color, float *hue, float *luminance, float *saturation) { float red, green, blue; float min, max, delta; float h, l, s; g_return_if_fail (color != NULL); red = color->red / 255.0; green = color->green / 255.0; blue = color->blue / 255.0; if (red > green) { if (red > blue) max = red; else max = blue; if (green < blue) min = green; else min = blue; } else { if (green > blue) max = green; else max = blue; if (red < blue) min = red; else min = blue; } l = (max + min) / 2; s = 0; h = 0; if (max != min) { if (l <= 0.5) s = (max - min) / (max + min); else s = (max - min) / (2.0 - max - min); delta = max - min; if (red == max) h = (green - blue) / delta; else if (green == max) h = 2.0 + (blue - red) / delta; else if (blue == max) h = 4.0 + (red - green) / delta; h *= 60; if (h < 0) h += 360.0; } if (hue) *hue = h; if (luminance) *luminance = l; if (saturation) *saturation = s; } /** * clutter_color_from_hls: * @color: (out): return location for a #ClutterColor * @hue: hue value, in the 0 .. 360 range * @luminance: luminance value, in the 0 .. 1 range * @saturation: saturation value, in the 0 .. 1 range * * Converts a color expressed in HLS (hue, luminance and saturation) * values into a #ClutterColor. */ void clutter_color_from_hls (ClutterColor *color, float hue, float luminance, float saturation) { float tmp1, tmp2; float tmp3[3]; float clr[3]; int i; hue /= 360.0; if (saturation == 0) { color->red = color->green = color->blue = (luminance * 255); return; } if (luminance <= 0.5) tmp2 = luminance * (1.0 + saturation); else tmp2 = luminance + saturation - (luminance * saturation); tmp1 = 2.0 * luminance - tmp2; tmp3[0] = hue + 1.0 / 3.0; tmp3[1] = hue; tmp3[2] = hue - 1.0 / 3.0; for (i = 0; i < 3; i++) { if (tmp3[i] < 0) tmp3[i] += 1.0; if (tmp3[i] > 1) tmp3[i] -= 1.0; if (6.0 * tmp3[i] < 1.0) clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0; else if (2.0 * tmp3[i] < 1.0) clr[i] = tmp2; else if (3.0 * tmp3[i] < 2.0) clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0); else clr[i] = tmp1; } color->red = floorf (clr[0] * 255.0 + 0.5); color->green = floorf (clr[1] * 255.0 + 0.5); color->blue = floorf (clr[2] * 255.0 + 0.5); } /** * clutter_color_shade: * @color: a #ClutterColor * @factor: the shade factor to apply * @result: (out caller-allocates): return location for the shaded color * * Shades @color by @factor and saves the modified color into @result. */ void clutter_color_shade (const ClutterColor *color, gdouble factor, ClutterColor *result) { float h, l, s; g_return_if_fail (color != NULL); g_return_if_fail (result != NULL); clutter_color_to_hls (color, &h, &l, &s); l = CLAMP (l * factor, 0.0, 1.0); s = CLAMP (s * factor, 0.0, 1.0); clutter_color_from_hls (result, h, l, s); result->alpha = color->alpha; } /** * clutter_color_to_pixel: * @color: a #ClutterColor * * Converts @color into a packed 32 bit integer, containing * all the four 8 bit channels used by #ClutterColor. * * Return value: a packed color */ guint32 clutter_color_to_pixel (const ClutterColor *color) { g_return_val_if_fail (color != NULL, 0); return (color->alpha | color->blue << 8 | color->green << 16 | color->red << 24); } /** * clutter_color_from_pixel: * @color: (out caller-allocates): return location for a #ClutterColor * @pixel: a 32 bit packed integer containing a color * * Converts @pixel from the packed representation of a four 8 bit channel * color to a #ClutterColor. */ void clutter_color_from_pixel (ClutterColor *color, guint32 pixel) { g_return_if_fail (color != NULL); color->red = pixel >> 24; color->green = (pixel >> 16) & 0xff; color->blue = (pixel >> 8) & 0xff; color->alpha = pixel & 0xff; } static inline void skip_whitespace (gchar **str) { while (g_ascii_isspace (**str)) *str += 1; } static inline void parse_rgb_value (gchar *str, guint8 *color, gchar **endp) { gdouble number; gchar *p; skip_whitespace (&str); number = g_ascii_strtod (str, endp); p = *endp; skip_whitespace (&p); if (*p == '%') { *endp = (gchar *) (p + 1); *color = CLAMP (number / 100.0, 0.0, 1.0) * 255; } else *color = CLAMP (number, 0, 255); } static gboolean parse_rgba (ClutterColor *color, gchar *str, gboolean has_alpha) { skip_whitespace (&str); if (*str != '(') return FALSE; str += 1; /* red */ parse_rgb_value (str, &color->red, &str); skip_whitespace (&str); if (*str != ',') return FALSE; str += 1; /* green */ parse_rgb_value (str, &color->green, &str); skip_whitespace (&str); if (*str != ',') return FALSE; str += 1; /* blue */ parse_rgb_value (str, &color->blue, &str); skip_whitespace (&str); /* alpha (optional); since the alpha channel value can only * be between 0 and 1 we don't use the parse_rgb_value() * function */ if (has_alpha) { gdouble number; if (*str != ',') return FALSE; str += 1; skip_whitespace (&str); number = g_ascii_strtod (str, &str); color->alpha = CLAMP (number * 255.0, 0, 255); } else color->alpha = 255; skip_whitespace (&str); if (*str != ')') return FALSE; return TRUE; } static gboolean parse_hsla (ClutterColor *color, gchar *str, gboolean has_alpha) { gdouble number; gdouble h, l, s; skip_whitespace (&str); if (*str != '(') return FALSE; str += 1; /* hue */ skip_whitespace (&str); /* we don't do any angle normalization here because * clutter_color_from_hls() will do it for us */ number = g_ascii_strtod (str, &str); skip_whitespace (&str); if (*str != ',') return FALSE; h = number; str += 1; /* saturation */ skip_whitespace (&str); number = g_ascii_strtod (str, &str); skip_whitespace (&str); if (*str != '%') return FALSE; str += 1; s = CLAMP (number / 100.0, 0.0, 1.0); skip_whitespace (&str); if (*str != ',') return FALSE; str += 1; /* luminance */ skip_whitespace (&str); number = g_ascii_strtod (str, &str); skip_whitespace (&str); if (*str != '%') return FALSE; str += 1; l = CLAMP (number / 100.0, 0.0, 1.0); skip_whitespace (&str); /* alpha (optional); since the alpha channel value can only * be between 0 and 1 we don't use the parse_rgb_value() * function */ if (has_alpha) { if (*str != ',') return FALSE; str += 1; skip_whitespace (&str); number = g_ascii_strtod (str, &str); color->alpha = CLAMP (number * 255.0, 0, 255); } else color->alpha = 255; skip_whitespace (&str); if (*str != ')') return FALSE; clutter_color_from_hls (color, h, l, s); return TRUE; } /** * clutter_color_from_string: * @color: (out caller-allocates): return location for a #ClutterColor * @str: a string specifiying a color * * Parses a string definition of a color, filling the #ClutterColor.red, * #ClutterColor.green, #ClutterColor.blue and #ClutterColor.alpha fields * of @color. * * The @color is not allocated. * * The format of @str can be either one of: * * - a standard name (as taken from the X11 rgb.txt file) * - an hexadecimal value in the form: `#rgb`, `#rrggbb`, `#rgba`, or `#rrggbbaa` * - a RGB color in the form: `rgb(r, g, b)` * - a RGB color in the form: `rgba(r, g, b, a)` * - a HSL color in the form: `hsl(h, s, l)` * -a HSL color in the form: `hsla(h, s, l, a)` * * where 'r', 'g', 'b' and 'a' are (respectively) the red, green, blue color * intensities and the opacity. The 'h', 's' and 'l' are (respectively) the * hue, saturation and luminance values. * * In the rgb() and rgba() formats, the 'r', 'g', and 'b' values are either * integers between 0 and 255, or percentage values in the range between 0% * and 100%; the percentages require the '%' character. The 'a' value, if * specified, can only be a floating point value between 0.0 and 1.0. * * In the hls() and hlsa() formats, the 'h' value (hue) is an angle between * 0 and 360.0 degrees; the 'l' and 's' values (luminance and saturation) are * percentage values in the range between 0% and 100%. The 'a' value, if specified, * can only be a floating point value between 0.0 and 1.0. * * Whitespace inside the definitions is ignored; no leading whitespace * is allowed. * * If the alpha component is not specified then it is assumed to be set to * be fully opaque. * * Return value: %TRUE if parsing succeeded, and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_color_from_string (ClutterColor *color, const gchar *str) { PangoColor pango_color = { 0, }; g_return_val_if_fail (color != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); if (strncmp (str, "rgb", 3) == 0) { gchar *s = (gchar *) str; gboolean res; if (strncmp (str, "rgba", 4) == 0) res = parse_rgba (color, s + 4, TRUE); else res = parse_rgba (color, s + 3, FALSE); return res; } if (strncmp (str, "hsl", 3) == 0) { gchar *s = (gchar *) str; gboolean res; if (strncmp (str, "hsla", 4) == 0) res = parse_hsla (color, s + 4, TRUE); else res = parse_hsla (color, s + 3, FALSE); return res; } /* if the string contains a color encoded using the hexadecimal * notations (#rrggbbaa or #rgba) we attempt a rough pass at * parsing the color ourselves, as we need the alpha channel that * Pango can't retrieve. */ if (str[0] == '#' && str[1] != '\0') { gsize length = strlen (str + 1); gint32 result; if (sscanf (str + 1, "%x", &result) == 1) { switch (length) { case 8: /* rrggbbaa */ color->red = (result >> 24) & 0xff; color->green = (result >> 16) & 0xff; color->blue = (result >> 8) & 0xff; color->alpha = result & 0xff; return TRUE; case 6: /* #rrggbb */ color->red = (result >> 16) & 0xff; color->green = (result >> 8) & 0xff; color->blue = result & 0xff; color->alpha = 0xff; return TRUE; case 4: /* #rgba */ color->red = ((result >> 12) & 0xf); color->green = ((result >> 8) & 0xf); color->blue = ((result >> 4) & 0xf); color->alpha = result & 0xf; color->red = (color->red << 4) | color->red; color->green = (color->green << 4) | color->green; color->blue = (color->blue << 4) | color->blue; color->alpha = (color->alpha << 4) | color->alpha; return TRUE; case 3: /* #rgb */ color->red = ((result >> 8) & 0xf); color->green = ((result >> 4) & 0xf); color->blue = result & 0xf; color->red = (color->red << 4) | color->red; color->green = (color->green << 4) | color->green; color->blue = (color->blue << 4) | color->blue; color->alpha = 0xff; return TRUE; default: return FALSE; } } } /* fall back to pango for X11-style named colors; see: * * http://en.wikipedia.org/wiki/X11_color_names * * for a list. at some point we might even ship with our own list generated * from X11/rgb.txt, like we generate the key symbols. */ if (pango_color_parse (&pango_color, str)) { color->red = pango_color.red; color->green = pango_color.green; color->blue = pango_color.blue; color->alpha = 0xff; return TRUE; } return FALSE; } /** * clutter_color_to_string: * @color: a #ClutterColor * * Returns a textual specification of @color in the hexadecimal form * #rrggbbaa, where r, * g, b and a are * hexadecimal digits representing the red, green, blue and alpha components * respectively. * * Return value: (transfer full): a newly-allocated text string * * Since: 0.2 */ gchar * clutter_color_to_string (const ClutterColor *color) { g_return_val_if_fail (color != NULL, NULL); return g_strdup_printf ("#%02x%02x%02x%02x", color->red, color->green, color->blue, color->alpha); } /** * clutter_color_equal: * @v1: (type Clutter.Color): a #ClutterColor * @v2: (type Clutter.Color): a #ClutterColor * * Compares two #ClutterColors and checks if they are the same. * * This function can be passed to g_hash_table_new() as the @key_equal_func * parameter, when using #ClutterColors as keys in a #GHashTable. * * Return value: %TRUE if the two colors are the same. * * Since: 0.2 */ gboolean clutter_color_equal (gconstpointer v1, gconstpointer v2) { const ClutterColor *a, *b; g_return_val_if_fail (v1 != NULL, FALSE); g_return_val_if_fail (v2 != NULL, FALSE); if (v1 == v2) return TRUE; a = v1; b = v2; return (a->red == b->red && a->green == b->green && a->blue == b->blue && a->alpha == b->alpha); } /** * clutter_color_hash: * @v: (type Clutter.Color): a #ClutterColor * * Converts a #ClutterColor to a hash value. * * This function can be passed to g_hash_table_new() as the @hash_func * parameter, when using #ClutterColors as keys in a #GHashTable. * * Return value: a hash value corresponding to the color * * Since: 1.0 */ guint clutter_color_hash (gconstpointer v) { return clutter_color_to_pixel ((const ClutterColor *) v); } /** * clutter_color_interpolate: * @initial: the initial #ClutterColor * @final: the final #ClutterColor * @progress: the interpolation progress * @result: (out): return location for the interpolation * * Interpolates between @initial and @final #ClutterColors * using @progress * * Since: 1.6 */ void clutter_color_interpolate (const ClutterColor *initial, const ClutterColor *final, gdouble progress, ClutterColor *result) { g_return_if_fail (initial != NULL); g_return_if_fail (final != NULL); g_return_if_fail (result != NULL); result->red = initial->red + (final->red - initial->red) * progress; result->green = initial->green + (final->green - initial->green) * progress; result->blue = initial->blue + (final->blue - initial->blue) * progress; result->alpha = initial->alpha + (final->alpha - initial->alpha) * progress; } static gboolean clutter_color_progress (const GValue *a, const GValue *b, gdouble progress, GValue *retval) { const ClutterColor *a_color = clutter_value_get_color (a); const ClutterColor *b_color = clutter_value_get_color (b); ClutterColor res = { 0, }; clutter_color_interpolate (a_color, b_color, progress, &res); clutter_value_set_color (retval, &res); return TRUE; } /** * clutter_color_copy: * @color: a #ClutterColor * * Makes a copy of the color structure. The result must be * freed using clutter_color_free(). * * Return value: (transfer full): an allocated copy of @color. * * Since: 0.2 */ ClutterColor * clutter_color_copy (const ClutterColor *color) { if (G_LIKELY (color != NULL)) return g_slice_dup (ClutterColor, color); return NULL; } /** * clutter_color_free: * @color: a #ClutterColor * * Frees a color structure created with clutter_color_copy(). * * Since: 0.2 */ void clutter_color_free (ClutterColor *color) { if (G_LIKELY (color != NULL)) g_slice_free (ClutterColor, color); } /** * clutter_color_new: * @red: red component of the color, between 0 and 255 * @green: green component of the color, between 0 and 255 * @blue: blue component of the color, between 0 and 255 * @alpha: alpha component of the color, between 0 and 255 * * Creates a new #ClutterColor with the given values. * * This function is the equivalent of: * * |[ * clutter_color_init (clutter_color_alloc (), red, green, blue, alpha); * ]| * * Return value: (transfer full): the newly allocated color. * Use clutter_color_free() when done * * Since: 0.8 */ ClutterColor * clutter_color_new (guint8 red, guint8 green, guint8 blue, guint8 alpha) { return clutter_color_init (clutter_color_alloc (), red, green, blue, alpha); } /** * clutter_color_alloc: (constructor) * * Allocates a new, transparent black #ClutterColor. * * Return value: (transfer full): the newly allocated #ClutterColor; use * clutter_color_free() to free its resources * * Since: 1.12 */ ClutterColor * clutter_color_alloc (void) { return g_slice_new0 (ClutterColor); } /** * clutter_color_init: * @color: a #ClutterColor * @red: red component of the color, between 0 and 255 * @green: green component of the color, between 0 and 255 * @blue: blue component of the color, between 0 and 255 * @alpha: alpha component of the color, between 0 and 255 * * Initializes @color with the given values. * * Return value: (transfer none): the initialized #ClutterColor * * Since: 1.12 */ ClutterColor * clutter_color_init (ClutterColor *color, guint8 red, guint8 green, guint8 blue, guint8 alpha) { g_return_val_if_fail (color != NULL, NULL); color->red = red; color->green = green; color->blue = blue; color->alpha = alpha; return color; } static void clutter_value_transform_color_string (const GValue *src, GValue *dest) { const ClutterColor *color = g_value_get_boxed (src); if (color) { gchar *string = clutter_color_to_string (color); g_value_take_string (dest, string); } else g_value_set_string (dest, NULL); } static void clutter_value_transform_string_color (const GValue *src, GValue *dest) { const char *str = g_value_get_string (src); if (str) { ClutterColor color = { 0, }; clutter_color_from_string (&color, str); clutter_value_set_color (dest, &color); } else clutter_value_set_color (dest, NULL); } G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterColor, clutter_color, clutter_color_copy, clutter_color_free, CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_color_string) CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_color) CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_color_progress)); /** * clutter_value_set_color: * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR * @color: the color to set * * Sets @value to @color. * * Since: 0.8 */ void clutter_value_set_color (GValue *value, const ClutterColor *color) { g_return_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value)); g_value_set_boxed (value, color); } /** * clutter_value_get_color: * @value: a #GValue initialized to #CLUTTER_TYPE_COLOR * * Gets the #ClutterColor contained in @value. * * Return value: (transfer none): the color inside the passed #GValue * * Since: 0.8 */ const ClutterColor * clutter_value_get_color (const GValue *value) { g_return_val_if_fail (CLUTTER_VALUE_HOLDS_COLOR (value), NULL); return g_value_get_boxed (value); } static void param_color_init (GParamSpec *pspec) { ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec); cspec->default_value = NULL; } static void param_color_finalize (GParamSpec *pspec) { ClutterParamSpecColor *cspec = CLUTTER_PARAM_SPEC_COLOR (pspec); clutter_color_free (cspec->default_value); } static void param_color_set_default (GParamSpec *pspec, GValue *value) { const ClutterColor *default_value = CLUTTER_PARAM_SPEC_COLOR (pspec)->default_value; clutter_value_set_color (value, default_value); } static gint param_color_values_cmp (GParamSpec *pspec, const GValue *value1, const GValue *value2) { const ClutterColor *color1 = g_value_get_boxed (value1); const ClutterColor *color2 = g_value_get_boxed (value2); int pixel1, pixel2; if (color1 == NULL) return color2 == NULL ? 0 : -1; pixel1 = clutter_color_to_pixel (color1); pixel2 = clutter_color_to_pixel (color2); if (pixel1 < pixel2) return -1; else if (pixel1 == pixel2) return 0; else return 1; } GType clutter_param_color_get_type (void) { static GType pspec_type = 0; if (G_UNLIKELY (pspec_type == 0)) { const GParamSpecTypeInfo pspec_info = { sizeof (ClutterParamSpecColor), 16, param_color_init, CLUTTER_TYPE_COLOR, param_color_finalize, param_color_set_default, NULL, param_color_values_cmp, }; pspec_type = g_param_type_register_static (I_("ClutterParamSpecColor"), &pspec_info); } return pspec_type; } /** * clutter_param_spec_color: (skip) * @name: name of the property * @nick: short name * @blurb: description (can be translatable) * @default_value: default value * @flags: flags for the param spec * * Creates a #GParamSpec for properties using #ClutterColor. * * Return value: the newly created #GParamSpec * * Since: 0.8 */ GParamSpec * clutter_param_spec_color (const gchar *name, const gchar *nick, const gchar *blurb, const ClutterColor *default_value, GParamFlags flags) { ClutterParamSpecColor *cspec; cspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_COLOR, name, nick, blurb, flags); cspec->default_value = clutter_color_copy (default_value); return G_PARAM_SPEC (cspec); } muffin-6.4.1/clutter/clutter/clutter-layout-manager.h0000664000175000017500000002673514723361714021701 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_LAYOUT_MANAGER_H__ #define __CLUTTER_LAYOUT_MANAGER_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_LAYOUT_MANAGER (clutter_layout_manager_get_type ()) #define CLUTTER_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManager)) #define CLUTTER_IS_LAYOUT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_MANAGER)) #define CLUTTER_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass)) #define CLUTTER_IS_LAYOUT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_MANAGER)) #define CLUTTER_LAYOUT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_MANAGER, ClutterLayoutManagerClass)) typedef struct _ClutterLayoutManagerClass ClutterLayoutManagerClass; /** * ClutterLayoutManager: * * The #ClutterLayoutManager structure contains only private data * and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterLayoutManager { /*< private >*/ GInitiallyUnowned parent_instance; gpointer CLUTTER_PRIVATE_FIELD (dummy); }; /** * ClutterLayoutManagerClass: * @get_preferred_width: virtual function; override to provide a preferred * width for the layout manager. See also the get_preferred_width() * virtual function in #ClutterActor * @get_preferred_height: virtual function; override to provide a preferred * height for the layout manager. See also the get_preferred_height() * virtual function in #ClutterActor * @allocate: virtual function; override to allocate the children of the * layout manager. See also the allocate() virtual function in * #ClutterActor * @set_container: virtual function; override to set a back pointer * on the #ClutterContainer using the layout manager. The implementation * should not take a reference on the container, but just take a weak * reference, to avoid potential leaks due to reference cycles * @get_child_meta_type: virtual function; override to return the #GType * of the #ClutterLayoutMeta sub-class used by the #ClutterLayoutManager * @create_child_meta: virtual function; override to create a * #ClutterLayoutMeta instance associated to a #ClutterContainer and a * child #ClutterActor, used to maintain layout manager specific properties * @begin_animation: virtual function; override to control the animation * of a #ClutterLayoutManager with the given duration and easing mode. * This virtual function is deprecated, and it should not be overridden * in newly written code. * @end_animation: virtual function; override to end an animation started * by clutter_layout_manager_begin_animation(). This virtual function is * deprecated, and it should not be overriden in newly written code. * @get_animation_progress: virtual function; override to control the * progress of the animation of a #ClutterLayoutManager. This virtual * function is deprecated, and it should not be overridden in newly written * code. * @layout_changed: class handler for the #ClutterLayoutManager::layout-changed * signal * * The #ClutterLayoutManagerClass structure contains only private * data and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterLayoutManagerClass { /*< private >*/ GInitiallyUnownedClass parent_class; /*< public >*/ void (* get_preferred_width) (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p); void (* get_preferred_height) (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p); void (* allocate) (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags); void (* set_container) (ClutterLayoutManager *manager, ClutterContainer *container); GType (* get_child_meta_type) (ClutterLayoutManager *manager); ClutterLayoutMeta *(* create_child_meta) (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor); /* deprecated */ ClutterAlpha * (* begin_animation) (ClutterLayoutManager *manager, guint duration, gulong mode); /* deprecated */ gdouble (* get_animation_progress) (ClutterLayoutManager *manager); /* deprecated */ void (* end_animation) (ClutterLayoutManager *manager); void (* layout_changed) (ClutterLayoutManager *manager); /*< private >*/ /* padding for future expansion */ void (* _clutter_padding_1) (void); void (* _clutter_padding_2) (void); void (* _clutter_padding_3) (void); void (* _clutter_padding_4) (void); void (* _clutter_padding_5) (void); void (* _clutter_padding_6) (void); void (* _clutter_padding_7) (void); void (* _clutter_padding_8) (void); }; CLUTTER_EXPORT GType clutter_layout_manager_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p); CLUTTER_EXPORT void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p); CLUTTER_EXPORT void clutter_layout_manager_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_layout_manager_set_container (ClutterLayoutManager *manager, ClutterContainer *container); CLUTTER_EXPORT void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager); CLUTTER_EXPORT GParamSpec * clutter_layout_manager_find_child_property (ClutterLayoutManager *manager, const gchar *name); CLUTTER_EXPORT GParamSpec ** clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, guint *n_pspecs); CLUTTER_EXPORT ClutterLayoutMeta *clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor); CLUTTER_EXPORT void clutter_layout_manager_child_set (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) G_GNUC_NULL_TERMINATED; CLUTTER_EXPORT void clutter_layout_manager_child_get (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) G_GNUC_NULL_TERMINATED; CLUTTER_EXPORT void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, const GValue *value); CLUTTER_EXPORT void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, GValue *value); G_END_DECLS #endif /* __CLUTTER_LAYOUT_MANAGER_H__ */ muffin-6.4.1/clutter/clutter/clutter-settings-private.h0000664000175000017500000000135714723361714022255 0ustar fabiofabio#ifndef __CLUTTER_SETTINGS_PRIVATE_H__ #define __CLUTTER_SETTINGS_PRIVATE_H__ #include #include G_BEGIN_DECLS void _clutter_settings_set_backend (ClutterSettings *settings, ClutterBackend *backend); void _clutter_settings_read_from_key_file (ClutterSettings *settings, GKeyFile *key_file); void clutter_settings_set_property_internal (ClutterSettings *settings, const char *property, GValue *value); G_END_DECLS #endif /* __CLUTTER_SETTINGS_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-image.h0000664000175000017500000001201614723361714020021 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive image' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_IMAGE_H__ #define __CLUTTER_IMAGE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_IMAGE (clutter_image_get_type ()) #define CLUTTER_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_IMAGE, ClutterImage)) #define CLUTTER_IS_IMAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_IMAGE)) #define CLUTTER_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_IMAGE, ClutterImageClass)) #define CLUTTER_IS_IMAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_IMAGE)) #define CLUTTER_IMAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_IMAGE, ClutterImageClass)) /** * CLUTTER_IMAGE_ERROR: * * Error domain for the #ClutterImageError enumeration. * * Since: 1.10 */ #define CLUTTER_IMAGE_ERROR (clutter_image_error_quark ()) typedef struct _ClutterImage ClutterImage; typedef struct _ClutterImagePrivate ClutterImagePrivate; typedef struct _ClutterImageClass ClutterImageClass; /** * ClutterImageError: * @CLUTTER_IMAGE_ERROR_INVALID_DATA: Invalid data passed to the * clutter_image_set_data() function. * * Error enumeration for #ClutterImage. * * Since: 1.10 */ typedef enum { CLUTTER_IMAGE_ERROR_INVALID_DATA } ClutterImageError; /** * ClutterImage: * * The #ClutterImage structure contains * private data and should only be accessed using the provided * API. * * Since: 1.10 */ struct _ClutterImage { /*< private >*/ GObject parent_instance; ClutterImagePrivate *priv; }; /** * ClutterImageClass: * * The #ClutterImageClass structure contains * private data. * * Since: 1.10 */ struct _ClutterImageClass { /*< private >*/ GObjectClass parent_class; gpointer _padding[16]; }; CLUTTER_EXPORT GQuark clutter_image_error_quark (void); CLUTTER_EXPORT GType clutter_image_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterContent * clutter_image_new (void); CLUTTER_EXPORT gboolean clutter_image_set_data (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error); CLUTTER_EXPORT gboolean clutter_image_set_area (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, const cairo_rectangle_int_t *rect, guint row_stride, GError **error); CLUTTER_EXPORT gboolean clutter_image_set_bytes (ClutterImage *image, GBytes *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error); CLUTTER_EXPORT CoglTexture * clutter_image_get_texture (ClutterImage *image); G_END_DECLS #endif /* __CLUTTER_IMAGE_H__ */ muffin-6.4.1/clutter/clutter/clutter-main.h0000664000175000017500000002052414723361714017666 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_MAIN_H__ #define __CLUTTER_MAIN_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS typedef enum { CLUTTER_DEBUG_MISC = 1 << 0, CLUTTER_DEBUG_ACTOR = 1 << 1, CLUTTER_DEBUG_TEXTURE = 1 << 2, CLUTTER_DEBUG_EVENT = 1 << 3, CLUTTER_DEBUG_PAINT = 1 << 4, CLUTTER_DEBUG_PANGO = 1 << 5, CLUTTER_DEBUG_BACKEND = 1 << 6, CLUTTER_DEBUG_SCHEDULER = 1 << 7, CLUTTER_DEBUG_SCRIPT = 1 << 8, CLUTTER_DEBUG_SHADER = 1 << 9, CLUTTER_DEBUG_MULTISTAGE = 1 << 10, CLUTTER_DEBUG_ANIMATION = 1 << 11, CLUTTER_DEBUG_LAYOUT = 1 << 12, CLUTTER_DEBUG_PICK = 1 << 13, CLUTTER_DEBUG_EVENTLOOP = 1 << 14, CLUTTER_DEBUG_CLIPPING = 1 << 15, CLUTTER_DEBUG_OOB_TRANSFORMS = 1 << 16, } ClutterDebugFlag; typedef enum { CLUTTER_DEBUG_NOP_PICKING = 1 << 0, } ClutterPickDebugFlag; typedef enum { CLUTTER_DEBUG_DISABLE_SWAP_EVENTS = 1 << 0, CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS = 1 << 1, CLUTTER_DEBUG_REDRAWS = 1 << 2, CLUTTER_DEBUG_PAINT_VOLUMES = 1 << 3, CLUTTER_DEBUG_DISABLE_CULLING = 1 << 4, CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT = 1 << 5, CLUTTER_DEBUG_CONTINUOUS_REDRAW = 1 << 6, CLUTTER_DEBUG_PAINT_DEFORM_TILES = 1 << 7, CLUTTER_DEBUG_PAINT_DAMAGE_REGION = 1 << 8, } ClutterDrawDebugFlag; /** * CLUTTER_INIT_ERROR: * * #GError domain for #ClutterInitError */ #define CLUTTER_INIT_ERROR (clutter_init_error_quark ()) /** * ClutterInitError: * @CLUTTER_INIT_SUCCESS: Initialisation successful * @CLUTTER_INIT_ERROR_UNKNOWN: Unknown error * @CLUTTER_INIT_ERROR_THREADS: Thread initialisation failed * @CLUTTER_INIT_ERROR_BACKEND: Backend initialisation failed * @CLUTTER_INIT_ERROR_INTERNAL: Internal error * * Error conditions returned by clutter_init() and clutter_init_with_args(). * * Since: 0.2 */ typedef enum { CLUTTER_INIT_SUCCESS = 1, CLUTTER_INIT_ERROR_UNKNOWN = 0, CLUTTER_INIT_ERROR_THREADS = -1, CLUTTER_INIT_ERROR_BACKEND = -2, CLUTTER_INIT_ERROR_INTERNAL = -3 } ClutterInitError; CLUTTER_EXPORT GQuark clutter_init_error_quark (void); /** * CLUTTER_PRIORITY_REDRAW: * * Priority of the redraws. This is chosen to be lower than the GTK+ * redraw and resize priorities, because in application with both * GTK+ and Clutter it's more likely that the Clutter part will be * continually animating (and thus able to starve GTK+) than * vice-versa. * * Since: 0.8 */ #define CLUTTER_PRIORITY_REDRAW (G_PRIORITY_HIGH_IDLE + 50) /* Initialisation */ CLUTTER_EXPORT void clutter_base_init (void); CLUTTER_EXPORT ClutterInitError clutter_init (int *argc, char ***argv) G_GNUC_WARN_UNUSED_RESULT; CLUTTER_EXPORT ClutterInitError clutter_init_with_args (int *argc, char ***argv, const char *parameter_string, GOptionEntry *entries, const char *translation_domain, GError **error) G_GNUC_WARN_UNUSED_RESULT; CLUTTER_EXPORT GOptionGroup * clutter_get_option_group (void); CLUTTER_EXPORT GOptionGroup * clutter_get_option_group_without_init (void); /* Mainloop */ CLUTTER_EXPORT void clutter_main (void); CLUTTER_EXPORT void clutter_main_quit (void); CLUTTER_EXPORT gint clutter_main_level (void); CLUTTER_EXPORT void clutter_do_event (ClutterEvent *event); /* Debug utility functions */ CLUTTER_EXPORT gboolean clutter_get_accessibility_enabled (void); CLUTTER_EXPORT void clutter_disable_accessibility (void); /* Threading functions */ CLUTTER_EXPORT guint clutter_threads_add_idle (GSourceFunc func, gpointer data); CLUTTER_EXPORT guint clutter_threads_add_idle_full (gint priority, GSourceFunc func, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT guint clutter_threads_add_timeout (guint interval, GSourceFunc func, gpointer data); CLUTTER_EXPORT guint clutter_threads_add_timeout_full (gint priority, guint interval, GSourceFunc func, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT guint clutter_threads_add_repaint_func (GSourceFunc func, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT guint clutter_threads_add_repaint_func_full (ClutterRepaintFlags flags, GSourceFunc func, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT void clutter_threads_remove_repaint_func (guint handle_id); CLUTTER_EXPORT PangoFontMap * clutter_get_font_map (void); CLUTTER_EXPORT ClutterTextDirection clutter_get_default_text_direction (void); CLUTTER_EXPORT guint clutter_get_default_frame_rate (void); CLUTTER_EXPORT gboolean clutter_check_windowing_backend (const char *backend_type); CLUTTER_EXPORT void clutter_add_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags); CLUTTER_EXPORT void clutter_remove_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags); G_END_DECLS #endif /* _CLUTTER_MAIN_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-method.h0000664000175000017500000000754214723361714021364 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef __CLUTTER_INPUT_METHOD_H__ #define __CLUTTER_INPUT_METHOD_H__ #include #define CLUTTER_TYPE_INPUT_METHOD (clutter_input_method_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterInputMethod, clutter_input_method, CLUTTER, INPUT_METHOD, GObject) typedef struct _ClutterInputMethodClass ClutterInputMethodClass; struct _ClutterInputMethodClass { GObjectClass parent_class; void (* focus_in) (ClutterInputMethod *im, ClutterInputFocus *actor); void (* focus_out) (ClutterInputMethod *im); void (* reset) (ClutterInputMethod *im); void (* set_cursor_location) (ClutterInputMethod *im, const graphene_rect_t *rect); void (* set_surrounding) (ClutterInputMethod *im, const gchar *text, guint cursor, guint anchor); void (* update_content_hints) (ClutterInputMethod *im, ClutterInputContentHintFlags hint); void (* update_content_purpose) (ClutterInputMethod *im, ClutterInputContentPurpose purpose); gboolean (* filter_key_event) (ClutterInputMethod *im, const ClutterEvent *key); }; CLUTTER_EXPORT void clutter_input_method_focus_in (ClutterInputMethod *im, ClutterInputFocus *focus); CLUTTER_EXPORT void clutter_input_method_focus_out (ClutterInputMethod *im); CLUTTER_EXPORT void clutter_input_method_commit (ClutterInputMethod *im, const gchar *text); CLUTTER_EXPORT void clutter_input_method_delete_surrounding (ClutterInputMethod *im, int offset, guint len); CLUTTER_EXPORT void clutter_input_method_request_surrounding (ClutterInputMethod *im); CLUTTER_EXPORT void clutter_input_method_set_preedit_text (ClutterInputMethod *im, const gchar *preedit, guint cursor); CLUTTER_EXPORT void clutter_input_method_notify_key_event (ClutterInputMethod *im, const ClutterEvent *event, gboolean filtered); CLUTTER_EXPORT void clutter_input_method_set_input_panel_state (ClutterInputMethod *im, ClutterInputPanelState state); CLUTTER_EXPORT void clutter_input_method_forward_key (ClutterInputMethod *im, uint32_t keyval, uint32_t keycode, uint32_t state, uint64_t time_, gboolean press); #endif /* __CLUTTER_INPUT_METHOD_H__ */ muffin-6.4.1/clutter/clutter/clutter-path-constraint.h0000664000175000017500000000516314723361714022062 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_PATH_CONSTRAINT_H__ #define __CLUTTER_PATH_CONSTRAINT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_PATH_CONSTRAINT (clutter_path_constraint_get_type ()) #define CLUTTER_PATH_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PATH_CONSTRAINT, ClutterPathConstraint)) #define CLUTTER_IS_PATH_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PATH_CONSTRAINT)) /** * ClutterPathConstraint: * * #ClutterPathConstraint is an opaque structure * whose members cannot be directly accessed * * Since: 1.6 */ typedef struct _ClutterPathConstraint ClutterPathConstraint; typedef struct _ClutterPathConstraintClass ClutterPathConstraintClass; CLUTTER_EXPORT GType clutter_path_constraint_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterConstraint *clutter_path_constraint_new (ClutterPath *path, gfloat offset); CLUTTER_EXPORT void clutter_path_constraint_set_path (ClutterPathConstraint *constraint, ClutterPath *path); CLUTTER_EXPORT ClutterPath * clutter_path_constraint_get_path (ClutterPathConstraint *constraint); CLUTTER_EXPORT void clutter_path_constraint_set_offset (ClutterPathConstraint *constraint, gfloat offset); CLUTTER_EXPORT gfloat clutter_path_constraint_get_offset (ClutterPathConstraint *constraint); G_END_DECLS #endif /* __CLUTTER_PATH_CONSTRAINT_H__ */ muffin-6.4.1/clutter/clutter/clutter-id-pool.h0000664000175000017500000000320714723361714020304 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * ClutterIDPool: pool of reusable integer ids associated with pointers. * * Author: Øyvind Kolås */ #ifndef __CLUTTER_ID_POOL_H__ #define __CLUTTER_ID_POOL_H__ #include G_BEGIN_DECLS typedef struct _ClutterIDPool ClutterIDPool; ClutterIDPool * _clutter_id_pool_new (guint initial_size); void _clutter_id_pool_free (ClutterIDPool *id_pool); guint32 _clutter_id_pool_add (ClutterIDPool *id_pool, gpointer ptr); void _clutter_id_pool_remove (ClutterIDPool *id_pool, guint32 id_); gpointer _clutter_id_pool_lookup (ClutterIDPool *id_pool, guint32 id_); G_END_DECLS #endif /* __CLUTTER_ID_POOL_H__ */ muffin-6.4.1/clutter/clutter/clutter-tap-action.h0000664000175000017500000000657714723361714021015 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emanuele Aina * * Based on ClutterPanAction * Based on ClutterDragAction, ClutterSwipeAction, and MxKineticScrollView, * written by: * Emmanuele Bassi * Tomeu Vizoso * Chris Lord */ #ifndef __CLUTTER_TAP_ACTION_H__ #define __CLUTTER_TAP_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_TAP_ACTION (clutter_tap_action_get_type ()) #define CLUTTER_TAP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TAP_ACTION, ClutterTapAction)) #define CLUTTER_IS_TAP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TAP_ACTION)) #define CLUTTER_TAP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TAP_ACTION, ClutterTapActionClass)) #define CLUTTER_IS_TAP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TAP_ACTION)) #define CLUTTER_TAP_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TAP_ACTION, ClutterTapActionClass)) typedef struct _ClutterTapAction ClutterTapAction; typedef struct _ClutterTapActionPrivate ClutterTapActionPrivate; typedef struct _ClutterTapActionClass ClutterTapActionClass; /** * ClutterTapAction: * * The #ClutterTapAction structure contains * only private data and should be accessed using the provided API * * Since: 1.14 */ struct _ClutterTapAction { /*< private >*/ ClutterGestureAction parent_instance; }; /** * ClutterTapActionClass: * @tap: class handler for the #ClutterTapAction::tap signal * * The #ClutterTapActionClass structure contains * only private data. */ struct _ClutterTapActionClass { /*< private >*/ ClutterGestureActionClass parent_class; /*< public >*/ gboolean (* tap) (ClutterTapAction *action, ClutterActor *actor); /*< private >*/ void (* _clutter_tap_action1) (void); void (* _clutter_tap_action2) (void); void (* _clutter_tap_action3) (void); void (* _clutter_tap_action4) (void); void (* _clutter_tap_action5) (void); void (* _clutter_tap_action6) (void); }; CLUTTER_EXPORT GType clutter_tap_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_tap_action_new (void); G_END_DECLS #endif /* __CLUTTER_TAP_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-graphene.c0000664000175000017500000000645514723361714020535 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Georges Basile Stavracas Neto * * Copyright (C) 2019 Endless, Inc * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "clutter-build-config.h" #include "clutter-graphene.h" #include "clutter-private.h" #include "clutter-types.h" static gboolean graphene_point_progress (const GValue *a, const GValue *b, double progress, GValue *retval) { const graphene_point_t *ap = g_value_get_boxed (a); const graphene_point_t *bp = g_value_get_boxed (b); graphene_point_t res; graphene_point_interpolate (ap, bp, progress, &res); g_value_set_boxed (retval, &res); return TRUE; } static gboolean graphene_point3d_progress (const GValue *a, const GValue *b, double progress, GValue *retval) { const graphene_point3d_t *av = g_value_get_boxed (a); const graphene_point3d_t *bv = g_value_get_boxed (b); graphene_point3d_t res; graphene_point3d_interpolate (av, bv, progress, &res); g_value_set_boxed (retval, &res); return TRUE; } static gboolean graphene_rect_progress (const GValue *a, const GValue *b, double progress, GValue *retval) { const graphene_rect_t *rect_a = g_value_get_boxed (a); const graphene_rect_t *rect_b = g_value_get_boxed (b); graphene_rect_t res; graphene_rect_interpolate (rect_a, rect_b, progress, &res); g_value_set_boxed (retval, &res); return TRUE; } static gboolean graphene_size_progress (const GValue *a, const GValue *b, double progress, GValue *retval) { const graphene_size_t *as = g_value_get_boxed (a); const graphene_size_t *bs = g_value_get_boxed (b); graphene_size_t res; graphene_size_interpolate (as, bs, progress, &res); g_value_set_boxed (retval, &res); return TRUE; } void clutter_graphene_init (void) { clutter_interval_register_progress_func (GRAPHENE_TYPE_POINT, graphene_point_progress); clutter_interval_register_progress_func (GRAPHENE_TYPE_POINT3D, graphene_point3d_progress); clutter_interval_register_progress_func (GRAPHENE_TYPE_RECT, graphene_rect_progress); clutter_interval_register_progress_func (GRAPHENE_TYPE_SIZE, graphene_size_progress); } muffin-6.4.1/clutter/clutter/clutter-constraint.h0000664000175000017500000001255614723361714021134 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_CONSTRAINT_H__ #define __CLUTTER_CONSTRAINT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_CONSTRAINT (clutter_constraint_get_type ()) #define CLUTTER_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CONSTRAINT, ClutterConstraint)) #define CLUTTER_IS_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONSTRAINT)) #define CLUTTER_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CONSTRAINT, ClutterConstraintClass)) #define CLUTTER_IS_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CONSTRAINT)) #define CLUTTER_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CONSTRAINT, ClutterConstraintClass)) typedef struct _ClutterConstraintClass ClutterConstraintClass; /** * ClutterConstraint: * * The #ClutterConstraint structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterConstraint { /*< private >*/ ClutterActorMeta parent_instance; }; /** * ClutterConstraintClass: * @update_allocation: virtual function used to update the allocation * of the #ClutterActor using the #ClutterConstraint * @update_preferred_size: virtual function used to update the preferred * size of the #ClutterActor using the #ClutterConstraint; optional, * since 1.22 * * The #ClutterConstraintClass structure contains * only private data * * Since: 1.4 */ struct _ClutterConstraintClass { /*< private >*/ ClutterActorMetaClass parent_class; /*< public >*/ void (* update_allocation) (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation); void (* update_preferred_size) (ClutterConstraint *constraint, ClutterActor *actor, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size); /*< private >*/ void (* _clutter_constraint1) (void); void (* _clutter_constraint2) (void); void (* _clutter_constraint3) (void); void (* _clutter_constraint4) (void); void (* _clutter_constraint5) (void); void (* _clutter_constraint6) (void); void (* _clutter_constraint7) (void); }; CLUTTER_EXPORT GType clutter_constraint_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_constraint_update_preferred_size (ClutterConstraint *constraint, ClutterActor *actor, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size); /* ClutterActor API */ CLUTTER_EXPORT void clutter_actor_add_constraint (ClutterActor *self, ClutterConstraint *constraint); CLUTTER_EXPORT void clutter_actor_add_constraint_with_name (ClutterActor *self, const gchar *name, ClutterConstraint *constraint); CLUTTER_EXPORT void clutter_actor_remove_constraint (ClutterActor *self, ClutterConstraint *constraint); CLUTTER_EXPORT void clutter_actor_remove_constraint_by_name (ClutterActor *self, const gchar *name); CLUTTER_EXPORT GList * clutter_actor_get_constraints (ClutterActor *self); CLUTTER_EXPORT ClutterConstraint *clutter_actor_get_constraint (ClutterActor *self, const gchar *name); CLUTTER_EXPORT void clutter_actor_clear_constraints (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_constraints (ClutterActor *self); G_END_DECLS #endif /* __CLUTTER_CONSTRAINT_H__ */ muffin-6.4.1/clutter/clutter/clutter-shader-effect.c0000664000175000017500000006532014723361714021440 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-shader-effect * @short_description: Base class for shader effects * @See_Also: #ClutterEffect, #ClutterOffscreenEffect * * #ClutterShaderEffect is a class that implements all the plumbing for * creating #ClutterEffects using GLSL shaders. * * #ClutterShaderEffect creates an offscreen buffer and then applies the * GLSL shader (after checking whether the compilation and linking were * successfull) to the buffer before painting it on screen. * * #ClutterShaderEffect is available since Clutter 1.4 * * ## Implementing a ClutterShaderEffect * * Creating a sub-class of #ClutterShaderEffect requires the * overriding of the #ClutterOffscreenEffectClass.paint_target() virtual * function from the #ClutterOffscreenEffect class. It is also convenient * to implement the #ClutterShaderEffectClass.get_static_shader_source() * virtual function in case you are planning to create more than one * instance of the effect. * * The #ClutterShaderEffectClass.get_static_shader_source() * function should return a copy of the shader source to use. This * function is only called once per subclass of #ClutterShaderEffect * regardless of how many instances of the effect are created. The * source for the shader is typically stored in a static const * string which is returned from this function via * g_strdup(). * * The #ClutterOffscreenEffectClass.paint_target() should set the * shader's uniforms if any. This is done by calling * clutter_shader_effect_set_uniform_value() or * clutter_shader_effect_set_uniform(). The sub-class should then * chain up to the #ClutterShaderEffect implementation. * * ## Setting uniforms on a ClutterShaderEffect * * The example below shows a typical implementation of the * #ClutterShaderEffectClass.get_static_shader_source() and * #ClutterOffscreenEffectClass.paint_target() virtual functions * for a #ClutterShaderEffect subclass. * * |[ * static gchar * * my_effect_get_static_shader_source (ClutterShaderEffect *effect) * { * // shader_source is set elsewhere * return g_strdup (shader_source); * } * * static gboolean * my_effect_paint_target (ClutterOffscreenEffect *effect) * { * MyEffect *self = MY_EFFECT (effect); * ClutterShaderEffect *shader = CLUTTER_SHADER_EFFECT (effect); * ClutterEffectClass *parent_class; * gfloat component_r, component_g, component_b; * * // the "tex" uniform is declared in the shader as: * // * // uniform int tex; * // * // and it is passed a constant value of 0 * clutter_shader_effect_set_uniform (shader, "tex", G_TYPE_INT, 1, 0); * * // the "component" uniform is declared in the shader as: * // * // uniform vec3 component; * // * // and it's defined to contain the normalized components * // of a #ClutterColor * component_r = self->color.red / 255.0f; * component_g = self->color.green / 255.0f; * component_b = self->color.blue / 255.0f; * clutter_shader_effect_set_uniform (shader, "component", * G_TYPE_FLOAT, 3, * component_r, * component_g, * component_b); * * // chain up to the parent's implementation * parent_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (my_effect_parent_class); * return parent_class->paint_target (effect); * } * ]| */ #include "clutter-build-config.h" #include "cogl/cogl.h" #include "clutter-shader-effect.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-feature.h" #include "clutter-private.h" #include "clutter-shader-types.h" typedef struct _ShaderUniform { gchar *name; GType type; GValue value; int location; } ShaderUniform; struct _ClutterShaderEffectPrivate { ClutterActor *actor; ClutterShaderType shader_type; CoglHandle program; CoglHandle shader; GHashTable *uniforms; }; typedef struct _ClutterShaderEffectClassPrivate { /* These are the per-class pre-compiled shader and program which is used when the class implements get_static_shader_source without calling set_shader_source. They will be shared by all instances of this class */ CoglHandle program; CoglHandle shader; } ClutterShaderEffectClassPrivate; enum { PROP_0, PROP_SHADER_TYPE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE_WITH_CODE (ClutterShaderEffect, clutter_shader_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT, G_ADD_PRIVATE (ClutterShaderEffect) g_type_add_class_private (g_define_type_id, sizeof (ClutterShaderEffectClassPrivate))) static inline void clutter_shader_effect_clear (ClutterShaderEffect *self, gboolean reset_uniforms) { ClutterShaderEffectPrivate *priv = self->priv; if (priv->shader != NULL) { cogl_object_unref (priv->shader); priv->shader = NULL; } if (priv->program != NULL) { cogl_object_unref (priv->program); priv->program = NULL; } if (reset_uniforms && priv->uniforms != NULL) { g_hash_table_destroy (priv->uniforms); priv->uniforms = NULL; } priv->actor = NULL; } static void clutter_shader_effect_update_uniforms (ClutterShaderEffect *effect) { ClutterShaderEffectPrivate *priv = effect->priv; GHashTableIter iter; gpointer key, value; gsize size; if (priv->program == NULL) return; if (priv->uniforms == NULL) return; key = value = NULL; g_hash_table_iter_init (&iter, priv->uniforms); while (g_hash_table_iter_next (&iter, &key, &value)) { ShaderUniform *uniform = value; if (uniform->location == -1) uniform->location = cogl_program_get_uniform_location (priv->program, uniform->name); if (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (&uniform->value)) { const float *floats; floats = clutter_value_get_shader_float (&uniform->value, &size); cogl_program_set_uniform_float (priv->program, uniform->location, size, 1, floats); } else if (CLUTTER_VALUE_HOLDS_SHADER_INT (&uniform->value)) { const int *ints; ints = clutter_value_get_shader_int (&uniform->value, &size); cogl_program_set_uniform_int (priv->program, uniform->location, size, 1, ints); } else if (CLUTTER_VALUE_HOLDS_SHADER_MATRIX (&uniform->value)) { const float *matrix; matrix = clutter_value_get_shader_matrix (&uniform->value, &size); cogl_program_set_uniform_matrix (priv->program, uniform->location, size, 1, FALSE, matrix); } else if (G_VALUE_HOLDS_FLOAT (&uniform->value)) { const float float_val = g_value_get_float (&uniform->value); cogl_program_set_uniform_float (priv->program, uniform->location, 1, 1, &float_val); } else if (G_VALUE_HOLDS_DOUBLE (&uniform->value)) { const float float_val = (float) g_value_get_double (&uniform->value); cogl_program_set_uniform_float (priv->program, uniform->location, 1, 1, &float_val); } else if (G_VALUE_HOLDS_INT (&uniform->value)) { const int int_val = g_value_get_int (&uniform->value); cogl_program_set_uniform_int (priv->program, uniform->location, 1, 1, &int_val); } else g_warning ("Invalid uniform of type '%s' for name '%s'", g_type_name (G_VALUE_TYPE (&uniform->value)), uniform->name); } } static void clutter_shader_effect_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (meta); ClutterShaderEffectPrivate *priv = self->priv; ClutterActorMetaClass *parent; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ShaderEffect: the graphics hardware " "or the current GL driver does not implement support " "for the GLSL shading language."); clutter_actor_meta_set_enabled (meta, FALSE); return; } parent = CLUTTER_ACTOR_META_CLASS (clutter_shader_effect_parent_class); parent->set_actor (meta, actor); /* we keep a back pointer here */ priv->actor = clutter_actor_meta_get_actor (meta); if (priv->actor == NULL) return; CLUTTER_NOTE (SHADER, "Preparing shader effect of type '%s'", G_OBJECT_TYPE_NAME (meta)); } static CoglHandle clutter_shader_effect_create_shader (ClutterShaderEffect *self) { ClutterShaderEffectPrivate *priv = self->priv; switch (priv->shader_type) { case CLUTTER_FRAGMENT_SHADER: return cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); break; case CLUTTER_VERTEX_SHADER: return cogl_create_shader (COGL_SHADER_TYPE_VERTEX); break; default: g_assert_not_reached (); return NULL; } } static void clutter_shader_effect_try_static_source (ClutterShaderEffect *self) { ClutterShaderEffectPrivate *priv = self->priv; ClutterShaderEffectClass *shader_effect_class = CLUTTER_SHADER_EFFECT_GET_CLASS (self); if (shader_effect_class->get_static_shader_source != NULL) { ClutterShaderEffectClassPrivate *class_priv; class_priv = G_TYPE_CLASS_GET_PRIVATE (shader_effect_class, CLUTTER_TYPE_SHADER_EFFECT, ClutterShaderEffectClassPrivate); if (class_priv->shader == NULL) { gchar *source; class_priv->shader = clutter_shader_effect_create_shader (self); source = shader_effect_class->get_static_shader_source (self); cogl_shader_source (class_priv->shader, source); g_free (source); CLUTTER_NOTE (SHADER, "Compiling shader effect"); class_priv->program = cogl_create_program (); cogl_program_attach_shader (class_priv->program, class_priv->shader); cogl_program_link (class_priv->program); } priv->shader = cogl_object_ref (class_priv->shader); if (class_priv->program != NULL) priv->program = cogl_object_ref (class_priv->program); } } static void clutter_shader_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterShaderEffect *self = CLUTTER_SHADER_EFFECT (effect); ClutterShaderEffectPrivate *priv = self->priv; ClutterOffscreenEffectClass *parent; CoglHandle material; /* If the source hasn't been set then we'll try to get it from the static source instead */ if (priv->shader == NULL) clutter_shader_effect_try_static_source (self); /* we haven't been prepared or we don't have support for * GLSL shaders in Clutter */ if (priv->program == NULL) goto out; CLUTTER_NOTE (SHADER, "Applying the shader effect of type '%s'", G_OBJECT_TYPE_NAME (effect)); clutter_shader_effect_update_uniforms (CLUTTER_SHADER_EFFECT (effect)); /* associate the program to the offscreen target material */ material = clutter_offscreen_effect_get_target (effect); cogl_pipeline_set_user_program (material, priv->program); out: /* paint the offscreen buffer */ parent = CLUTTER_OFFSCREEN_EFFECT_CLASS (clutter_shader_effect_parent_class); parent->paint_target (effect, paint_context); } static void clutter_shader_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterShaderEffectPrivate *priv = CLUTTER_SHADER_EFFECT (gobject)->priv; switch (prop_id) { case PROP_SHADER_TYPE: priv->shader_type = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_shader_effect_finalize (GObject *gobject) { ClutterShaderEffect *effect = CLUTTER_SHADER_EFFECT (gobject); clutter_shader_effect_clear (effect, TRUE); G_OBJECT_CLASS (clutter_shader_effect_parent_class)->finalize (gobject); } static void clutter_shader_effect_class_init (ClutterShaderEffectClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); /** * ClutterShaderEffect:shader-type: * * The type of shader that is used by the effect. This property * should be set by the constructor of #ClutterShaderEffect * sub-classes. * * Since: 1.4 */ obj_props[PROP_SHADER_TYPE] = g_param_spec_enum ("shader-type", P_("Shader Type"), P_("The type of shader used"), CLUTTER_TYPE_SHADER_TYPE, CLUTTER_FRAGMENT_SHADER, CLUTTER_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); gobject_class->set_property = clutter_shader_effect_set_property; gobject_class->finalize = clutter_shader_effect_finalize; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); meta_class->set_actor = clutter_shader_effect_set_actor; offscreen_class->paint_target = clutter_shader_effect_paint_target; } static void clutter_shader_effect_init (ClutterShaderEffect *effect) { effect->priv = clutter_shader_effect_get_instance_private (effect); effect->priv->shader_type = CLUTTER_FRAGMENT_SHADER; } /** * clutter_shader_effect_new: * @shader_type: the type of the shader, either %CLUTTER_FRAGMENT_SHADER, * or %CLUTTER_VERTEX_SHADER * * Creates a new #ClutterShaderEffect, to be applied to an actor using * clutter_actor_add_effect(). * * The effect will be empty until clutter_shader_effect_set_shader_source() * is called. * * Return value: the newly created #ClutterShaderEffect. * Use g_object_unref() when done. * * Since: 1.8 */ ClutterEffect * clutter_shader_effect_new (ClutterShaderType shader_type) { return g_object_new (CLUTTER_TYPE_SHADER_EFFECT, "shader-type", shader_type, NULL); } /** * clutter_shader_effect_get_shader: * @effect: a #ClutterShaderEffect * * Retrieves a pointer to the shader's handle * * Return value: (transfer none): a pointer to the shader's handle, * or %NULL * * Since: 1.4 */ CoglHandle clutter_shader_effect_get_shader (ClutterShaderEffect *effect) { g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect), NULL); return effect->priv->shader; } /** * clutter_shader_effect_get_program: * @effect: a #ClutterShaderEffect * * Retrieves a pointer to the program's handle * * Return value: (transfer none): a pointer to the program's handle, * or %NULL * * Since: 1.4 */ CoglHandle clutter_shader_effect_get_program (ClutterShaderEffect *effect) { g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect), NULL); return effect->priv->program; } static void shader_uniform_free (gpointer data) { if (data != NULL) { ShaderUniform *uniform = data; g_value_unset (&uniform->value); g_free (uniform->name); g_slice_free (ShaderUniform, uniform); } } static ShaderUniform * shader_uniform_new (const gchar *name, const GValue *value) { ShaderUniform *retval; retval = g_slice_new0 (ShaderUniform); retval->name = g_strdup (name); retval->type = G_VALUE_TYPE (value); retval->location = -1; g_value_init (&retval->value, retval->type); g_value_copy (value, &retval->value); return retval; } static void shader_uniform_update (ShaderUniform *uniform, const GValue *value) { g_value_unset (&uniform->value); g_value_init (&uniform->value, G_VALUE_TYPE (value)); g_value_copy (value, &uniform->value); } static inline void clutter_shader_effect_add_uniform (ClutterShaderEffect *effect, const gchar *name, const GValue *value) { ClutterShaderEffectPrivate *priv = effect->priv; ShaderUniform *uniform; if (priv->uniforms == NULL) { priv->uniforms = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, shader_uniform_free); } uniform = g_hash_table_lookup (priv->uniforms, name); if (uniform == NULL) { uniform = shader_uniform_new (name, value); g_hash_table_insert (priv->uniforms, uniform->name, uniform); } else shader_uniform_update (uniform, value); if (priv->actor != NULL && !CLUTTER_ACTOR_IN_PAINT (priv->actor)) clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); } /** * clutter_shader_effect_set_uniform_value: * @effect: a #ClutterShaderEffect * @name: the name of the uniform to set * @value: a #GValue with the value of the uniform to set * * Sets @value as the payload for the uniform @name inside the shader * effect * * The #GType of the @value must be one of: %G_TYPE_INT, for a single * integer value; %G_TYPE_FLOAT, for a single floating point value; * %CLUTTER_TYPE_SHADER_INT, for an array of integer values; * %CLUTTER_TYPE_SHADER_FLOAT, for an array of floating point values; * and %CLUTTER_TYPE_SHADER_MATRIX, for a matrix of floating point * values. It also accepts %G_TYPE_DOUBLE for compatibility with other * languages than C. * * Since: 1.4 */ void clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect, const gchar *name, const GValue *value) { g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect)); g_return_if_fail (name != NULL); g_return_if_fail (value != NULL); clutter_shader_effect_add_uniform (effect, name, value); } static void clutter_shader_effect_set_uniform_valist (ClutterShaderEffect *effect, const gchar *name, GType value_type, gsize n_values, va_list *args) { GValue value = G_VALUE_INIT; if (value_type == CLUTTER_TYPE_SHADER_INT) { gint *int_values = va_arg (*args, gint*); g_value_init (&value, CLUTTER_TYPE_SHADER_INT); clutter_value_set_shader_int (&value, n_values, int_values); goto add_uniform; } if (value_type == CLUTTER_TYPE_SHADER_FLOAT) { gfloat *float_values = va_arg (*args, gfloat*); g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT); clutter_value_set_shader_float (&value, n_values, float_values); goto add_uniform; } if (value_type == CLUTTER_TYPE_SHADER_MATRIX) { gfloat *float_values = va_arg (*args, gfloat*); g_value_init (&value, CLUTTER_TYPE_SHADER_MATRIX); clutter_value_set_shader_matrix (&value, n_values, float_values); goto add_uniform; } if (value_type == G_TYPE_INT) { g_return_if_fail (n_values <= 4); /* if we only have one value we can go through the fast path * of using G_TYPE_INT, otherwise we create a vector of integers * from the passed values */ if (n_values == 1) { gint int_val = va_arg (*args, gint); g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, int_val); } else { gint *int_values = g_new (gint, n_values); gint i; for (i = 0; i < n_values; i++) int_values[i] = va_arg (*args, gint); g_value_init (&value, CLUTTER_TYPE_SHADER_INT); clutter_value_set_shader_int (&value, n_values, int_values); g_free (int_values); } goto add_uniform; } if (value_type == G_TYPE_FLOAT) { g_return_if_fail (n_values <= 4); /* if we only have one value we can go through the fast path * of using G_TYPE_FLOAT, otherwise we create a vector of floats * from the passed values */ if (n_values == 1) { gfloat float_val = (gfloat) va_arg (*args, gdouble); g_value_init (&value, G_TYPE_FLOAT); g_value_set_float (&value, float_val); } else { gfloat *float_values = g_new (gfloat, n_values); gint i; for (i = 0; i < n_values; i++) float_values[i] = (gfloat) va_arg (*args, double); g_value_init (&value, CLUTTER_TYPE_SHADER_FLOAT); clutter_value_set_shader_float (&value, n_values, float_values); g_free (float_values); } goto add_uniform; } g_warning ("Unrecognized type '%s' (values: %d) for uniform name '%s'", g_type_name (value_type), (int) n_values, name); return; add_uniform: clutter_shader_effect_add_uniform (effect, name, &value); g_value_unset (&value); } /** * clutter_shader_effect_set_uniform: * @effect: a #ClutterShaderEffect * @name: the name of the uniform to set * @gtype: the type of the uniform to set * @n_values: the number of values * @...: a list of values * * Sets a list of values as the payload for the uniform @name inside * the shader effect * * The @gtype must be one of: %G_TYPE_INT, for 1 or more integer values; * %G_TYPE_FLOAT, for 1 or more floating point values; * %CLUTTER_TYPE_SHADER_INT, for a pointer to an array of integer values; * %CLUTTER_TYPE_SHADER_FLOAT, for a pointer to an array of floating point * values; and %CLUTTER_TYPE_SHADER_MATRIX, for a pointer to an array of * floating point values mapping a matrix * * The number of values interepreted is defined by the @n_value * argument, and by the @gtype argument. For instance, a uniform named * "sampler0" and containing a single integer value is set using: * * |[ * clutter_shader_effect_set_uniform (effect, "sampler0", * G_TYPE_INT, 1, * 0); * ]| * * While a uniform named "components" and containing a 3-elements vector * of floating point values (a "vec3") can be set using: * * |[ * gfloat component_r, component_g, component_b; * * clutter_shader_effect_set_uniform (effect, "components", * G_TYPE_FLOAT, 3, * component_r, * component_g, * component_b); * ]| * * or can be set using: * * |[ * gfloat component_vec[3]; * * clutter_shader_effect_set_uniform (effect, "components", * CLUTTER_TYPE_SHADER_FLOAT, 3, * component_vec); * ]| * * Finally, a uniform named "map" and containing a matrix can be set using: * * |[ * clutter_shader_effect_set_uniform (effect, "map", * CLUTTER_TYPE_SHADER_MATRIX, 1, * cogl_matrix_get_array (&matrix)); * ]| * * Since: 1.4 */ void clutter_shader_effect_set_uniform (ClutterShaderEffect *effect, const gchar *name, GType gtype, gsize n_values, ...) { va_list args; g_return_if_fail (CLUTTER_IS_SHADER_EFFECT (effect)); g_return_if_fail (name != NULL); g_return_if_fail (gtype != G_TYPE_INVALID); g_return_if_fail (n_values > 0); va_start (args, n_values); clutter_shader_effect_set_uniform_valist (effect, name, gtype, n_values, &args); va_end (args); } /** * clutter_shader_effect_set_shader_source: * @effect: a #ClutterShaderEffect * @source: the source of a GLSL shader * * Sets the source of the GLSL shader used by @effect * * This function should only be called by implementations of * the #ClutterShaderEffect class, and not by application code. * * This function can only be called once; subsequent calls will * yield no result. * * Return value: %TRUE if the source was set * * Since: 1.4 */ gboolean clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect, const gchar *source) { ClutterShaderEffectPrivate *priv; g_return_val_if_fail (CLUTTER_IS_SHADER_EFFECT (effect), FALSE); g_return_val_if_fail (source != NULL && *source != '\0', FALSE); priv = effect->priv; if (priv->shader != NULL) return TRUE; priv->shader = clutter_shader_effect_create_shader (effect); cogl_shader_source (priv->shader, source); CLUTTER_NOTE (SHADER, "Compiling shader effect"); priv->program = cogl_create_program (); cogl_program_attach_shader (priv->program, priv->shader); cogl_program_link (priv->program); return TRUE; } muffin-6.4.1/clutter/clutter/clutter-animatable.h0000664000175000017500000001076114723361714021041 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_ANIMATABLE_H__ #define __CLUTTER_ANIMATABLE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_ANIMATABLE (clutter_animatable_get_type ()) CLUTTER_EXPORT G_DECLARE_INTERFACE (ClutterAnimatable, clutter_animatable, CLUTTER, ANIMATABLE, GObject) /** * ClutterAnimatableInterface: * @animate_property: virtual function for custom interpolation of a * property. This virtual function is deprecated * @find_property: virtual function for retrieving the #GParamSpec of * an animatable property * @get_initial_state: virtual function for retrieving the initial * state of an animatable property * @set_final_state: virtual function for setting the state of an * animatable property * @interpolate_value: virtual function for interpolating the progress * of a property * * Base interface for #GObjects that can be animated by a * a #ClutterAnimation. * * Since: 1.0 */ struct _ClutterAnimatableInterface { /*< private >*/ GTypeInterface parent_iface; /*< public >*/ gboolean (* animate_property) (ClutterAnimatable *animatable, ClutterAnimation *animation, const gchar *property_name, const GValue *initial_value, const GValue *final_value, gdouble progress, GValue *value); GParamSpec *(* find_property) (ClutterAnimatable *animatable, const gchar *property_name); void (* get_initial_state) (ClutterAnimatable *animatable, const gchar *property_name, GValue *value); void (* set_final_state) (ClutterAnimatable *animatable, const gchar *property_name, const GValue *value); gboolean (* interpolate_value) (ClutterAnimatable *animatable, const gchar *property_name, ClutterInterval *interval, gdouble progress, GValue *value); }; CLUTTER_EXPORT GParamSpec *clutter_animatable_find_property (ClutterAnimatable *animatable, const gchar *property_name); CLUTTER_EXPORT void clutter_animatable_get_initial_state (ClutterAnimatable *animatable, const gchar *property_name, GValue *value); CLUTTER_EXPORT void clutter_animatable_set_final_state (ClutterAnimatable *animatable, const gchar *property_name, const GValue *value); CLUTTER_EXPORT gboolean clutter_animatable_interpolate_value (ClutterAnimatable *animatable, const gchar *property_name, ClutterInterval *interval, gdouble progress, GValue *value); G_END_DECLS #endif /* __CLUTTER_ANIMATABLE_H__ */ muffin-6.4.1/clutter/clutter/clutter-effect-private.h0000664000175000017500000000241714723361714021647 0ustar fabiofabio#ifndef __CLUTTER_EFFECT_PRIVATE_H__ #define __CLUTTER_EFFECT_PRIVATE_H__ #include G_BEGIN_DECLS gboolean _clutter_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context); void _clutter_effect_post_paint (ClutterEffect *effect, ClutterPaintContext *paint_context); gboolean _clutter_effect_modify_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume); gboolean _clutter_effect_has_custom_paint_volume (ClutterEffect *effect); void _clutter_effect_paint (ClutterEffect *effect, ClutterPaintContext *paint_context, ClutterEffectPaintFlags flags); void _clutter_effect_pick (ClutterEffect *effect, ClutterPickContext *pick_context); G_END_DECLS #endif /* __CLUTTER_EFFECT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-keyframe-transition.h0000664000175000017500000001337714723361714022745 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_KEYFRAME_TRANSITION_H__ #define __CLUTTER_KEYFRAME_TRANSITION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_KEYFRAME_TRANSITION (clutter_keyframe_transition_get_type ()) #define CLUTTER_KEYFRAME_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransition)) #define CLUTTER_IS_KEYFRAME_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION)) #define CLUTTER_KEYFRAME_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransitionClass)) #define CLUTTER_IS_KEYFRAME_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_KEYFRAME_TRANSITION)) #define CLUTTER_KEYFRAME_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_KEYFRAME_TRANSITION, ClutterKeyframeTransitionClass)) typedef struct _ClutterKeyframeTransitionPrivate ClutterKeyframeTransitionPrivate; typedef struct _ClutterKeyframeTransitionClass ClutterKeyframeTransitionClass; /** * ClutterKeyframeTransition: * * The `ClutterKeyframeTransition` structure contains only private * data and should be accessed using the provided API. * * Since: 1.12 */ struct _ClutterKeyframeTransition { /*< private >*/ ClutterPropertyTransition parent_instance; ClutterKeyframeTransitionPrivate *priv; }; /** * ClutterKeyframeTransitionClass: * * The `ClutterKeyframeTransitionClass` structure contains only * private data. * * Since: 1.12 */ struct _ClutterKeyframeTransitionClass { /*< private >*/ ClutterPropertyTransitionClass parent_class; gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_keyframe_transition_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterTransition * clutter_keyframe_transition_new (const char *property_name); CLUTTER_EXPORT void clutter_keyframe_transition_set_key_frames (ClutterKeyframeTransition *transition, guint n_key_frames, const double *key_frames); CLUTTER_EXPORT void clutter_keyframe_transition_set_values (ClutterKeyframeTransition *transition, guint n_values, const GValue *values); CLUTTER_EXPORT void clutter_keyframe_transition_set_modes (ClutterKeyframeTransition *transition, guint n_modes, const ClutterAnimationMode *modes); CLUTTER_EXPORT void clutter_keyframe_transition_set (ClutterKeyframeTransition *transition, GType gtype, guint n_key_frames, ...); CLUTTER_EXPORT void clutter_keyframe_transition_set_key_frame (ClutterKeyframeTransition *transition, guint index_, double key, ClutterAnimationMode mode, const GValue *value); CLUTTER_EXPORT void clutter_keyframe_transition_get_key_frame (ClutterKeyframeTransition *transition, guint index_, double *key, ClutterAnimationMode *mode, GValue *value); CLUTTER_EXPORT guint clutter_keyframe_transition_get_n_key_frames (ClutterKeyframeTransition *transition); CLUTTER_EXPORT void clutter_keyframe_transition_clear (ClutterKeyframeTransition *transition); G_END_DECLS #endif /* __CLUTTER_KEYFRAME_TRANSITION_H__ */ muffin-6.4.1/clutter/clutter/clutter.h0000664000175000017500000000712314723361714016744 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_H__ #define __CLUTTER_H__ #define __CLUTTER_H_INSIDE__ #include "clutter-config.h" #include "clutter-types.h" #include "clutter-action.h" #include "clutter-actor.h" #include "clutter-actor-meta.h" #include "clutter-align-constraint.h" #include "clutter-animatable.h" #include "clutter-backend.h" #include "clutter-bind-constraint.h" #include "clutter-binding-pool.h" #include "clutter-bin-layout.h" #include "clutter-blur-effect.h" #include "clutter-box-layout.h" #include "clutter-brightness-contrast-effect.h" #include "clutter-cairo.h" #include "clutter-canvas.h" #include "clutter-child-meta.h" #include "clutter-click-action.h" #include "clutter-clone.h" #include "clutter-color.h" #include "clutter-color-static.h" #include "clutter-colorize-effect.h" #include "clutter-constraint.h" #include "clutter-container.h" #include "clutter-content.h" #include "clutter-deform-effect.h" #include "clutter-desaturate-effect.h" #include "clutter-drag-action.h" #include "clutter-drop-action.h" #include "clutter-effect.h" #include "clutter-enums.h" #include "clutter-enum-types.h" #include "clutter-event.h" #include "clutter-feature.h" #include "clutter-fixed-layout.h" #include "clutter-flow-layout.h" #include "clutter-gesture-action.h" #include "clutter-grid-layout.h" #include "clutter-group.h" #include "clutter-image.h" #include "clutter-input-device.h" #include "clutter-input-device-tool.h" #include "clutter-input-method.h" #include "clutter-input-focus.h" #include "clutter-interval.h" #include "clutter-keyframe-transition.h" #include "clutter-keymap.h" #include "clutter-keysyms.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" #include "clutter-macros.h" #include "clutter-main.h" #include "clutter-offscreen-effect.h" #include "clutter-page-turn-effect.h" #include "clutter-paint-nodes.h" #include "clutter-paint-node.h" #include "clutter-pan-action.h" #include "clutter-path-constraint.h" #include "clutter-path.h" #include "clutter-property-transition.h" #include "clutter-rotate-action.h" #include "clutter-scriptable.h" #include "clutter-script.h" #include "clutter-scroll-actor.h" #include "clutter-settings.h" #include "clutter-shader-effect.h" #include "clutter-shader-types.h" #include "clutter-swipe-action.h" #include "clutter-snap-constraint.h" #include "clutter-stage.h" #include "clutter-stage-manager.h" #include "clutter-stage-view.h" #include "clutter-tap-action.h" #include "clutter-text.h" #include "clutter-texture-content.h" #include "clutter-timeline.h" #include "clutter-transition-group.h" #include "clutter-transition.h" #include "clutter-units.h" #include "clutter-virtual-input-device.h" #include "clutter-zoom-action.h" #include "clutter-deprecated.h" #include "clutter-autocleanups.h" #undef __CLUTTER_H_INSIDE__ #endif /* __CLUTTER_H__ */ muffin-6.4.1/clutter/clutter/clutter-color-static.h0000664000175000017500000001060314723361714021342 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_COLOR_STATIC_H__ #define __CLUTTER_COLOR_STATIC_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #define __CLUTTER_COLOR_SYM(x) (clutter_color_get_static (CLUTTER_COLOR_##x)) #define CLUTTER_COLOR_White (__CLUTTER_COLOR_SYM (WHITE)) #define CLUTTER_COLOR_Black (__CLUTTER_COLOR_SYM (BLACK)) #define CLUTTER_COLOR_Red (__CLUTTER_COLOR_SYM (RED)) #define CLUTTER_COLOR_DarkRed (__CLUTTER_COLOR_SYM (DARK_RED)) #define CLUTTER_COLOR_Green (__CLUTTER_COLOR_SYM (GREEN)) #define CLUTTER_COLOR_DarkGreen (__CLUTTER_COLOR_SYM (DARK_GREEN)) #define CLUTTER_COLOR_Blue (__CLUTTER_COLOR_SYM (BLUE)) #define CLUTTER_COLOR_DarkBlue (__CLUTTER_COLOR_SYM (DARK_BLUE)) #define CLUTTER_COLOR_Cyan (__CLUTTER_COLOR_SYM (CYAN)) #define CLUTTER_COLOR_DarkCyan (__CLUTTER_COLOR_SYM (DARK_CYAN)) #define CLUTTER_COLOR_Magenta (__CLUTTER_COLOR_SYM (MAGENTA)) #define CLUTTER_COLOR_DarkMagenta (__CLUTTER_COLOR_SYM (DARK_MAGENTA)) #define CLUTTER_COLOR_Yellow (__CLUTTER_COLOR_SYM (YELLOW)) #define CLUTTER_COLOR_DarkYellow (__CLUTTER_COLOR_SYM (DARK_YELLOW)) #define CLUTTER_COLOR_Gray (__CLUTTER_COLOR_SYM (GRAY)) #define CLUTTER_COLOR_DarkGray (__CLUTTER_COLOR_SYM (DARK_GRAY)) #define CLUTTER_COLOR_LightGray (__CLUTTER_COLOR_SYM (LIGHT_GRAY)) #define CLUTTER_COLOR_Butter (__CLUTTER_COLOR_SYM (BUTTER)) #define CLUTTER_COLOR_LightButter (__CLUTTER_COLOR_SYM (BUTTER_LIGHT)) #define CLUTTER_COLOR_DarkButter (__CLUTTER_COLOR_SYM (BUTTER_DARK)) #define CLUTTER_COLOR_Orange (__CLUTTER_COLOR_SYM (ORANGE)) #define CLUTTER_COLOR_LightOrange (__CLUTTER_COLOR_SYM (ORANGE_LIGHT)) #define CLUTTER_COLOR_DarkOrange (__CLUTTER_COLOR_SYM (ORANGE_DARK)) #define CLUTTER_COLOR_Chocolate (__CLUTTER_COLOR_SYM (CHOCOLATE)) #define CLUTTER_COLOR_LightChocolate (__CLUTTER_COLOR_SYM (CHOCOLATE_LIGHT)) #define CLUTTER_COLOR_DarkChocolate (__CLUTTER_COLOR_SYM (CHOCOLATE_DARK)) #define CLUTTER_COLOR_Chameleon (__CLUTTER_COLOR_SYM (CHAMELEON)) #define CLUTTER_COLOR_LightChameleon (__CLUTTER_COLOR_SYM (CHAMELEON_LIGHT)) #define CLUTTER_COLOR_DarkChameleon (__CLUTTER_COLOR_SYM (CHAMELEON_DARK)) #define CLUTTER_COLOR_SkyBlue (__CLUTTER_COLOR_SYM (SKY_BLUE)) #define CLUTTER_COLOR_LightSkyBlue (__CLUTTER_COLOR_SYM (SKY_BLUE_LIGHT)) #define CLUTTER_COLOR_DarkSkyBlue (__CLUTTER_COLOR_SYM (SKY_BLUE_DARK)) #define CLUTTER_COLOR_Plum (__CLUTTER_COLOR_SYM (PLUM)) #define CLUTTER_COLOR_LightPlum (__CLUTTER_COLOR_SYM (PLUM_LIGHT)) #define CLUTTER_COLOR_DarkPlum (__CLUTTER_COLOR_SYM (PLUM_DARK)) #define CLUTTER_COLOR_ScarletRed (__CLUTTER_COLOR_SYM (SCARLET_RED)) #define CLUTTER_COLOR_LightScarletRed (__CLUTTER_COLOR_SYM (SCARLET_RED_LIGHT)) #define CLUTTER_COLOR_DarkScarletRed (__CLUTTER_COLOR_SYM (SCARLET_RED_DARK)) #define CLUTTER_COLOR_Aluminium1 (__CLUTTER_COLOR_SYM (ALUMINIUM_1)) #define CLUTTER_COLOR_Aluminium2 (__CLUTTER_COLOR_SYM (ALUMINIUM_2)) #define CLUTTER_COLOR_Aluminium3 (__CLUTTER_COLOR_SYM (ALUMINIUM_3)) #define CLUTTER_COLOR_Aluminium4 (__CLUTTER_COLOR_SYM (ALUMINIUM_4)) #define CLUTTER_COLOR_Aluminium5 (__CLUTTER_COLOR_SYM (ALUMINIUM_5)) #define CLUTTER_COLOR_Aluminium6 (__CLUTTER_COLOR_SYM (ALUMINIUM_6)) #define CLUTTER_COLOR_Transparent (__CLUTTER_COLOR_SYM (TRANSPARENT)) #endif /* __CLUTTER_COLOR_STATIC_H__ */ muffin-6.4.1/clutter/clutter/clutter-text.h0000664000175000017500000003713414723361714017733 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Øyvind Kolås * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_TEXT_H__ #define __CLUTTER_TEXT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_TEXT (clutter_text_get_type ()) #define CLUTTER_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT, ClutterText)) #define CLUTTER_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TEXT, ClutterTextClass)) #define CLUTTER_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT)) #define CLUTTER_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXT)) #define CLUTTER_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXT, ClutterTextClass)) typedef struct _ClutterText ClutterText; typedef struct _ClutterTextPrivate ClutterTextPrivate; typedef struct _ClutterTextClass ClutterTextClass; /** * ClutterText: * * The #ClutterText struct contains only private data. * * Since: 1.0 */ struct _ClutterText { /*< private >*/ ClutterActor parent_instance; ClutterTextPrivate *priv; }; /** * ClutterTextClass: * @text_changed: class handler for the #ClutterText::text-changed signal * @activate: class handler for the #ClutterText::activate signal * @cursor_event: class handler for the #ClutterText::cursor-event signal * @cursor_changed: class handler for the #ClutterText::cursor-changed signal * * The #ClutterTextClass struct contains only private data. * * Since: 1.0 */ struct _ClutterTextClass { /*< private >*/ ClutterActorClass parent_class; /*< public >*/ /* signals, not vfuncs */ void (* text_changed) (ClutterText *self); void (* activate) (ClutterText *self); void (* cursor_event) (ClutterText *self, const graphene_rect_t *rect); void (* cursor_changed) (ClutterText *self); /*< private >*/ /* padding for future expansion */ void (* _clutter_reserved1) (void); void (* _clutter_reserved2) (void); void (* _clutter_reserved3) (void); void (* _clutter_reserved4) (void); void (* _clutter_reserved5) (void); void (* _clutter_reserved6) (void); void (* _clutter_reserved7) (void); }; CLUTTER_EXPORT GType clutter_text_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_text_new (void); CLUTTER_EXPORT ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, const ClutterColor *color); CLUTTER_EXPORT ClutterActor * clutter_text_new_with_text (const gchar *font_name, const gchar *text); CLUTTER_EXPORT ClutterActor * clutter_text_new_with_buffer (ClutterTextBuffer *buffer); CLUTTER_EXPORT ClutterTextBuffer * clutter_text_get_buffer (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_buffer (ClutterText *self, ClutterTextBuffer *buffer); CLUTTER_EXPORT const gchar * clutter_text_get_text (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_text (ClutterText *self, const gchar *text); CLUTTER_EXPORT void clutter_text_set_markup (ClutterText *self, const gchar *markup); CLUTTER_EXPORT void clutter_text_set_color (ClutterText *self, const ClutterColor *color); CLUTTER_EXPORT void clutter_text_get_color (ClutterText *self, ClutterColor *color); CLUTTER_EXPORT void clutter_text_set_font_name (ClutterText *self, const gchar *font_name); CLUTTER_EXPORT const gchar * clutter_text_get_font_name (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_font_description (ClutterText *self, PangoFontDescription *font_desc); CLUTTER_EXPORT PangoFontDescription *clutter_text_get_font_description (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_ellipsize (ClutterText *self, PangoEllipsizeMode mode); CLUTTER_EXPORT PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap); CLUTTER_EXPORT gboolean clutter_text_get_line_wrap (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_line_wrap_mode (ClutterText *self, PangoWrapMode wrap_mode); CLUTTER_EXPORT PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self); CLUTTER_EXPORT PangoLayout * clutter_text_get_layout (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_attributes (ClutterText *self, PangoAttrList *attrs); CLUTTER_EXPORT PangoAttrList * clutter_text_get_attributes (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_use_markup (ClutterText *self, gboolean setting); CLUTTER_EXPORT gboolean clutter_text_get_use_markup (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_line_alignment (ClutterText *self, PangoAlignment alignment); CLUTTER_EXPORT PangoAlignment clutter_text_get_line_alignment (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_justify (ClutterText *self, gboolean justify); CLUTTER_EXPORT gboolean clutter_text_get_justify (ClutterText *self); CLUTTER_EXPORT void clutter_text_insert_unichar (ClutterText *self, gunichar wc); CLUTTER_EXPORT void clutter_text_delete_chars (ClutterText *self, guint n_chars); CLUTTER_EXPORT void clutter_text_insert_text (ClutterText *self, const gchar *text, gssize position); CLUTTER_EXPORT void clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos); CLUTTER_EXPORT gchar * clutter_text_get_chars (ClutterText *self, gssize start_pos, gssize end_pos); CLUTTER_EXPORT void clutter_text_set_editable (ClutterText *self, gboolean editable); CLUTTER_EXPORT gboolean clutter_text_get_editable (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_activatable (ClutterText *self, gboolean activatable); CLUTTER_EXPORT gboolean clutter_text_get_activatable (ClutterText *self); CLUTTER_EXPORT gint clutter_text_get_cursor_position (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_cursor_position (ClutterText *self, gint position); CLUTTER_EXPORT void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible); CLUTTER_EXPORT gboolean clutter_text_get_cursor_visible (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_cursor_color (ClutterText *self, const ClutterColor *color); CLUTTER_EXPORT void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color); CLUTTER_EXPORT void clutter_text_set_cursor_size (ClutterText *self, gint size); CLUTTER_EXPORT guint clutter_text_get_cursor_size (ClutterText *self); CLUTTER_EXPORT void clutter_text_get_cursor_rect (ClutterText *self, graphene_rect_t *rect); CLUTTER_EXPORT void clutter_text_set_selectable (ClutterText *self, gboolean selectable); CLUTTER_EXPORT gboolean clutter_text_get_selectable (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound); CLUTTER_EXPORT gint clutter_text_get_selection_bound (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_selection (ClutterText *self, gssize start_pos, gssize end_pos); CLUTTER_EXPORT gchar * clutter_text_get_selection (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_selection_color (ClutterText *self, const ClutterColor *color); CLUTTER_EXPORT void clutter_text_get_selection_color (ClutterText *self, ClutterColor *color); CLUTTER_EXPORT gboolean clutter_text_delete_selection (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_password_char (ClutterText *self, gunichar wc); CLUTTER_EXPORT gunichar clutter_text_get_password_char (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_max_length (ClutterText *self, gint max); CLUTTER_EXPORT gint clutter_text_get_max_length (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_single_line_mode (ClutterText *self, gboolean single_line); CLUTTER_EXPORT gboolean clutter_text_get_single_line_mode (ClutterText *self); CLUTTER_EXPORT void clutter_text_set_selected_text_color (ClutterText *self, const ClutterColor *color); CLUTTER_EXPORT void clutter_text_get_selected_text_color (ClutterText *self, ClutterColor *color); CLUTTER_EXPORT gboolean clutter_text_activate (ClutterText *self); CLUTTER_EXPORT gint clutter_text_coords_to_position (ClutterText *self, gfloat x, gfloat y); CLUTTER_EXPORT gboolean clutter_text_position_to_coords (ClutterText *self, gint position, gfloat *x, gfloat *y, gfloat *line_height); CLUTTER_EXPORT void clutter_text_set_preedit_string (ClutterText *self, const gchar *preedit_str, PangoAttrList *preedit_attrs, guint cursor_pos); CLUTTER_EXPORT void clutter_text_get_layout_offsets (ClutterText *self, gint *x, gint *y); CLUTTER_EXPORT void clutter_text_set_input_hints (ClutterText *self, ClutterInputContentHintFlags hints); CLUTTER_EXPORT void clutter_text_set_input_purpose (ClutterText *self, ClutterInputContentPurpose purpose); CLUTTER_EXPORT ClutterInputContentHintFlags clutter_text_get_input_hints (ClutterText *self); CLUTTER_EXPORT ClutterInputContentPurpose clutter_text_get_input_purpose (ClutterText *self); CLUTTER_EXPORT gboolean clutter_text_has_preedit (ClutterText *self); G_END_DECLS #endif /* __CLUTTER_TEXT_H__ */ muffin-6.4.1/clutter/clutter/clutter-content-private.h0000664000175000017500000000326614723361714022070 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_CONTENT_PRIVATE_H__ #define __CLUTTER_CONTENT_PRIVATE_H__ #include G_BEGIN_DECLS void _clutter_content_attached (ClutterContent *content, ClutterActor *actor); void _clutter_content_detached (ClutterContent *content, ClutterActor *actor); void _clutter_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *node, ClutterPaintContext *paint_context); G_END_DECLS #endif /* __CLUTTER_CONTENT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-gesture-action.h0000664000175000017500000002125214723361714021672 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Tomeu Vizoso */ #ifndef __CLUTTER_GESTURE_ACTION_H__ #define __CLUTTER_GESTURE_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_GESTURE_ACTION (clutter_gesture_action_get_type ()) #define CLUTTER_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureAction)) #define CLUTTER_IS_GESTURE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GESTURE_ACTION)) #define CLUTTER_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass)) #define CLUTTER_IS_GESTURE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GESTURE_ACTION)) #define CLUTTER_GESTURE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GESTURE_ACTION, ClutterGestureActionClass)) typedef struct _ClutterGestureAction ClutterGestureAction; typedef struct _ClutterGestureActionPrivate ClutterGestureActionPrivate; typedef struct _ClutterGestureActionClass ClutterGestureActionClass; /** * ClutterGestureAction: * * The #ClutterGestureAction structure contains * only private data and should be accessed using the provided API * * Since: 1.8 */ struct _ClutterGestureAction { /*< private >*/ ClutterAction parent_instance; ClutterGestureActionPrivate *priv; }; /** * ClutterGestureActionClass: * @gesture_begin: class handler for the #ClutterGestureAction::gesture-begin signal * @gesture_progress: class handler for the #ClutterGestureAction::gesture-progress signal * @gesture_end: class handler for the #ClutterGestureAction::gesture-end signal * @gesture_cancel: class handler for the #ClutterGestureAction::gesture-cancel signal * @gesture_prepare: virtual function called before emitting the * #ClutterGestureAction::gesture-cancel signal * * The #ClutterGestureClass structure contains only * private data. * * Since: 1.8 */ struct _ClutterGestureActionClass { /*< private >*/ ClutterActionClass parent_class; /*< public >*/ gboolean (* gesture_begin) (ClutterGestureAction *action, ClutterActor *actor); gboolean (* gesture_progress) (ClutterGestureAction *action, ClutterActor *actor); void (* gesture_end) (ClutterGestureAction *action, ClutterActor *actor); void (* gesture_cancel) (ClutterGestureAction *action, ClutterActor *actor); gboolean (* gesture_prepare) (ClutterGestureAction *action, ClutterActor *actor); /*< private >*/ void (* _clutter_gesture_action1) (void); void (* _clutter_gesture_action2) (void); void (* _clutter_gesture_action3) (void); void (* _clutter_gesture_action4) (void); void (* _clutter_gesture_action5) (void); void (* _clutter_gesture_action6) (void); }; CLUTTER_EXPORT GType clutter_gesture_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_gesture_action_new (void); CLUTTER_EXPORT gint clutter_gesture_action_get_n_touch_points (ClutterGestureAction *action); CLUTTER_EXPORT void clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action, gint nb_points); CLUTTER_EXPORT void clutter_gesture_action_get_press_coords (ClutterGestureAction *action, guint point, gfloat *press_x, gfloat *press_y); CLUTTER_EXPORT void clutter_gesture_action_get_motion_coords (ClutterGestureAction *action, guint point, gfloat *motion_x, gfloat *motion_y); CLUTTER_EXPORT gfloat clutter_gesture_action_get_motion_delta (ClutterGestureAction *action, guint point, gfloat *delta_x, gfloat *delta_y); CLUTTER_EXPORT void clutter_gesture_action_get_release_coords (ClutterGestureAction *action, guint point, gfloat *release_x, gfloat *release_y); CLUTTER_EXPORT gfloat clutter_gesture_action_get_velocity (ClutterGestureAction *action, guint point, gfloat *velocity_x, gfloat *velocity_y); CLUTTER_EXPORT guint clutter_gesture_action_get_n_current_points (ClutterGestureAction *action); CLUTTER_EXPORT ClutterEventSequence * clutter_gesture_action_get_sequence (ClutterGestureAction *action, guint point); CLUTTER_EXPORT ClutterInputDevice * clutter_gesture_action_get_device (ClutterGestureAction *action, guint point); CLUTTER_EXPORT const ClutterEvent * clutter_gesture_action_get_last_event (ClutterGestureAction *action, guint point); CLUTTER_EXPORT void clutter_gesture_action_cancel (ClutterGestureAction *action); CLUTTER_EXPORT void clutter_gesture_action_set_threshold_trigger_edge (ClutterGestureAction *action, ClutterGestureTriggerEdge edge); CLUTTER_DEPRECATED_FOR(clutter_gesture_action_get_threshold_trigger_edge) ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action); CLUTTER_EXPORT ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_edge (ClutterGestureAction *action); CLUTTER_EXPORT void clutter_gesture_action_set_threshold_trigger_distance (ClutterGestureAction *action, float x, float y); CLUTTER_EXPORT void clutter_gesture_action_get_threshold_trigger_distance (ClutterGestureAction *action, float *x, float *y); G_END_DECLS #endif /* __CLUTTER_GESTURE_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-pick-context.h0000664000175000017500000000307314723361714021352 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef CLUTTER_PICK_CONTEXT_H #define CLUTTER_PICK_CONTEXT_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include "clutter-macros.h" #include "clutter-stage-view.h" typedef struct _ClutterPickContext ClutterPickContext; #define CLUTTER_TYPE_PICK_CONTEXT (clutter_pick_context_get_type ()) CLUTTER_EXPORT GType clutter_pick_context_get_type (void); CLUTTER_EXPORT ClutterPickContext * clutter_pick_context_ref (ClutterPickContext *pick_context); CLUTTER_EXPORT void clutter_pick_context_unref (ClutterPickContext *pick_context); CLUTTER_EXPORT void clutter_pick_context_destroy (ClutterPickContext *pick_context); CLUTTER_EXPORT CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context); #endif /* CLUTTER_PICK_CONTEXT_H */ muffin-6.4.1/clutter/clutter/clutter-bin-layout.c0000664000175000017500000004674014723361714021030 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-bin-layout * @short_description: A simple layout manager * * #ClutterBinLayout is a layout manager which implements the following * policy: * * - the preferred size is the maximum preferred size * between all the children of the container using the * layout; * - each child is allocated in "layers", on on top * of the other; * - for each layer there are horizontal and vertical * alignment policies. * * The [bin-layout example](https://git.gnome.org/browse/clutter/tree/examples/bin-layout.c?h=clutter-1.18) * shows how to pack actors inside a #ClutterBinLayout. * * #ClutterBinLayout is available since Clutter 1.2 */ #include "clutter-build-config.h" #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "clutter-actor-private.h" #include "clutter-animatable.h" #include "clutter-bin-layout.h" #include "clutter-child-meta.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-layout-meta.h" #include "clutter-private.h" #define CLUTTER_TYPE_BIN_LAYER (clutter_bin_layer_get_type ()) #define CLUTTER_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYER, ClutterBinLayer)) #define CLUTTER_IS_BIN_LAYER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYER)) typedef struct _ClutterBinLayer ClutterBinLayer; typedef struct _ClutterLayoutMetaClass ClutterBinLayerClass; struct _ClutterBinLayoutPrivate { ClutterBinAlignment x_align; ClutterBinAlignment y_align; ClutterContainer *container; }; struct _ClutterBinLayer { ClutterLayoutMeta parent_instance; ClutterBinAlignment x_align; ClutterBinAlignment y_align; }; enum { PROP_LAYER_0, PROP_LAYER_X_ALIGN, PROP_LAYER_Y_ALIGN, PROP_LAYER_LAST }; enum { PROP_0, PROP_X_ALIGN, PROP_Y_ALIGN, PROP_LAST }; static GParamSpec *layer_props[PROP_LAYER_LAST] = { NULL, }; static GParamSpec *bin_props[PROP_LAST] = { NULL, }; GType clutter_bin_layer_get_type (void); G_DEFINE_TYPE (ClutterBinLayer, clutter_bin_layer, CLUTTER_TYPE_LAYOUT_META) G_DEFINE_TYPE_WITH_PRIVATE (ClutterBinLayout, clutter_bin_layout, CLUTTER_TYPE_LAYOUT_MANAGER) /* * ClutterBinLayer */ static void set_layer_x_align (ClutterBinLayer *self, ClutterBinAlignment alignment) { ClutterLayoutManager *manager; ClutterLayoutMeta *meta; if (self->x_align == alignment) return; self->x_align = alignment; meta = CLUTTER_LAYOUT_META (self); manager = clutter_layout_meta_get_manager (meta); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (self), layer_props[PROP_LAYER_X_ALIGN]); } static void set_layer_y_align (ClutterBinLayer *self, ClutterBinAlignment alignment) { ClutterLayoutManager *manager; ClutterLayoutMeta *meta; if (self->y_align == alignment) return; self->y_align = alignment; meta = CLUTTER_LAYOUT_META (self); manager = clutter_layout_meta_get_manager (meta); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (self), layer_props[PROP_LAYER_Y_ALIGN]); } static void clutter_bin_layer_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject); switch (prop_id) { case PROP_LAYER_X_ALIGN: set_layer_x_align (layer, g_value_get_enum (value)); break; case PROP_LAYER_Y_ALIGN: set_layer_y_align (layer, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bin_layer_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBinLayer *layer = CLUTTER_BIN_LAYER (gobject); switch (prop_id) { case PROP_LAYER_X_ALIGN: g_value_set_enum (value, layer->x_align); break; case PROP_LAYER_Y_ALIGN: g_value_set_enum (value, layer->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bin_layer_class_init (ClutterBinLayerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_bin_layer_set_property; gobject_class->get_property = clutter_bin_layer_get_property; layer_props[PROP_LAYER_X_ALIGN] = g_param_spec_enum ("x-align", P_("Horizontal Alignment"), P_("Horizontal alignment for the actor " "inside the layout manager"), CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); layer_props[PROP_LAYER_Y_ALIGN] = g_param_spec_enum ("y-align", P_("Vertical Alignment"), P_("Vertical alignment for the actor " "inside the layout manager"), CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAYER_LAST, layer_props); } static void clutter_bin_layer_init (ClutterBinLayer *layer) { layer->x_align = CLUTTER_BIN_ALIGNMENT_CENTER; layer->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; } /* * ClutterBinLayout */ static void set_x_align (ClutterBinLayout *self, ClutterBinAlignment alignment) { ClutterBinLayoutPrivate *priv = self->priv; if (priv->x_align != alignment) { ClutterLayoutManager *manager; priv->x_align = alignment; manager = CLUTTER_LAYOUT_MANAGER (self); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (self), bin_props[PROP_X_ALIGN]); } } static void set_y_align (ClutterBinLayout *self, ClutterBinAlignment alignment) { ClutterBinLayoutPrivate *priv = self->priv; if (priv->y_align != alignment) { ClutterLayoutManager *manager; priv->y_align = alignment; manager = CLUTTER_LAYOUT_MANAGER (self); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (self), bin_props[PROP_Y_ALIGN]); } } static void clutter_bin_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterActor *actor = CLUTTER_ACTOR (container); ClutterActorIter iter; ClutterActor *child; gfloat min_width, nat_width; min_width = nat_width = 0.0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat minimum, natural; if (!clutter_actor_is_visible (child)) continue; clutter_actor_get_preferred_width (child, for_height, &minimum, &natural); min_width = MAX (min_width, minimum); nat_width = MAX (nat_width, natural); } if (min_width_p) *min_width_p = min_width; if (nat_width_p) *nat_width_p = nat_width; } static void clutter_bin_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterActor *actor = CLUTTER_ACTOR (container); ClutterActorIter iter; ClutterActor *child; gfloat min_height, nat_height; min_height = nat_height = 0.0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat minimum, natural; if (!clutter_actor_is_visible (child)) continue; clutter_actor_get_preferred_height (child, for_width, &minimum, &natural); min_height = MAX (min_height, minimum); nat_height = MAX (nat_height, natural); } if (min_height_p) *min_height_p = min_height; if (nat_height_p) *nat_height_p = nat_height; } static gdouble get_bin_alignment_factor (ClutterBinAlignment alignment, ClutterTextDirection text_dir) { switch (alignment) { case CLUTTER_BIN_ALIGNMENT_CENTER: return 0.5; case CLUTTER_BIN_ALIGNMENT_START: return text_dir == CLUTTER_TEXT_DIRECTION_LTR ? 0.0 : 1.0; case CLUTTER_BIN_ALIGNMENT_END: return text_dir == CLUTTER_TEXT_DIRECTION_LTR ? 1.0 : 0.0; case CLUTTER_BIN_ALIGNMENT_FIXED: case CLUTTER_BIN_ALIGNMENT_FILL: return 0.0; } return 0.0; } static gdouble get_actor_align_factor (ClutterActorAlign alignment) { switch (alignment) { case CLUTTER_ACTOR_ALIGN_CENTER: return 0.5; case CLUTTER_ACTOR_ALIGN_START: return 0.0; case CLUTTER_ACTOR_ALIGN_END: return 1.0; case CLUTTER_ACTOR_ALIGN_FILL: return 0.0; } return 0.0; } static void clutter_bin_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { gfloat allocation_x, allocation_y; gfloat available_w, available_h; ClutterActor *actor, *child; ClutterActorIter iter; clutter_actor_box_get_origin (allocation, &allocation_x, &allocation_y); clutter_actor_box_get_size (allocation, &available_w, &available_h); actor = CLUTTER_ACTOR (container); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterLayoutMeta *meta; ClutterBinLayer *layer; ClutterActorBox child_alloc = { 0, }; gdouble x_align, y_align; gboolean x_fill, y_fill, is_fixed_position_set; float fixed_x, fixed_y; if (!clutter_actor_is_visible (child)) continue; meta = clutter_layout_manager_get_child_meta (manager, container, child); layer = CLUTTER_BIN_LAYER (meta); fixed_x = fixed_y = 0.f; g_object_get (child, "fixed-position-set", &is_fixed_position_set, "fixed-x", &fixed_x, "fixed-y", &fixed_y, NULL); /* XXX:2.0 - remove the FIXED alignment, and just use the fixed position * of the actor if one is set */ if (is_fixed_position_set || layer->x_align == CLUTTER_BIN_ALIGNMENT_FIXED) { if (is_fixed_position_set) child_alloc.x1 = fixed_x; else child_alloc.x1 = clutter_actor_get_x (child); } else child_alloc.x1 = allocation_x; if (is_fixed_position_set || layer->y_align == CLUTTER_BIN_ALIGNMENT_FIXED) { if (is_fixed_position_set) child_alloc.y1 = fixed_y; else child_alloc.y1 = clutter_actor_get_y (child); } else child_alloc.y1 = allocation_y; child_alloc.x2 = allocation_x + available_w; child_alloc.y2 = allocation_y + available_h; if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL)) { ClutterActorAlign align; align = clutter_actor_get_x_align (child); x_fill = align == CLUTTER_ACTOR_ALIGN_FILL; x_align = get_actor_align_factor (align); } else { ClutterTextDirection text_dir; x_fill = (layer->x_align == CLUTTER_BIN_ALIGNMENT_FILL); text_dir = clutter_actor_get_text_direction (child); if (!is_fixed_position_set) x_align = get_bin_alignment_factor (layer->x_align, text_dir); else x_align = 0.0; } if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) { ClutterActorAlign align; align = clutter_actor_get_y_align (child); y_fill = align == CLUTTER_ACTOR_ALIGN_FILL; y_align = get_actor_align_factor (align); } else { y_fill = (layer->y_align == CLUTTER_BIN_ALIGNMENT_FILL); if (!is_fixed_position_set) y_align = get_bin_alignment_factor (layer->y_align, CLUTTER_TEXT_DIRECTION_LTR); else y_align = 0.0; } clutter_actor_allocate_align_fill (child, &child_alloc, x_align, y_align, x_fill, y_fill, flags); } } static GType clutter_bin_layout_get_child_meta_type (ClutterLayoutManager *manager) { return CLUTTER_TYPE_BIN_LAYER; } static ClutterLayoutMeta * clutter_bin_layout_create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterBinLayoutPrivate *priv; priv = CLUTTER_BIN_LAYOUT (manager)->priv; return g_object_new (CLUTTER_TYPE_BIN_LAYER, "container", container, "actor", actor, "manager", manager, "x-align", priv->x_align, "y_align", priv->y_align, NULL); } static void clutter_bin_layout_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { ClutterBinLayoutPrivate *priv; ClutterLayoutManagerClass *parent_class; priv = CLUTTER_BIN_LAYOUT (manager)->priv; priv->container = container; parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_bin_layout_parent_class); parent_class->set_container (manager, container); } static void clutter_bin_layout_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBinLayout *layout = CLUTTER_BIN_LAYOUT (gobject); switch (prop_id) { case PROP_X_ALIGN: set_x_align (layout, g_value_get_enum (value)); break; case PROP_Y_ALIGN: set_y_align (layout, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bin_layout_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBinLayoutPrivate *priv; priv = CLUTTER_BIN_LAYOUT (gobject)->priv; switch (prop_id) { case PROP_X_ALIGN: g_value_set_enum (value, priv->x_align); break; case PROP_Y_ALIGN: g_value_set_enum (value, priv->y_align); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bin_layout_class_init (ClutterBinLayoutClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterLayoutManagerClass *layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); /** * ClutterBinLayout:x-align: * * The default horizontal alignment policy for actors managed * by the #ClutterBinLayout * * Since: 1.2 * * Deprecated: 1.12: Use the #ClutterActor:x-expand and the * #ClutterActor:x-align properties on #ClutterActor instead. */ bin_props[PROP_X_ALIGN] = g_param_spec_enum ("x-align", P_("Horizontal Alignment"), P_("Default horizontal alignment for the actors " "inside the layout manager"), CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); /** * ClutterBinLayout:y-align: * * The default vertical alignment policy for actors managed * by the #ClutterBinLayout * * Since: 1.2 * * Deprecated: 1.12: Use the #ClutterActor:y-expand and the * #ClutterActor:y-align properties on #ClutterActor instead. */ bin_props[PROP_Y_ALIGN] = g_param_spec_enum ("y-align", P_("Vertical Alignment"), P_("Default vertical alignment for the actors " "inside the layout manager"), CLUTTER_TYPE_BIN_ALIGNMENT, CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); gobject_class->set_property = clutter_bin_layout_set_property; gobject_class->get_property = clutter_bin_layout_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, bin_props); layout_class->get_preferred_width = clutter_bin_layout_get_preferred_width; layout_class->get_preferred_height = clutter_bin_layout_get_preferred_height; layout_class->allocate = clutter_bin_layout_allocate; layout_class->create_child_meta = clutter_bin_layout_create_child_meta; layout_class->get_child_meta_type = clutter_bin_layout_get_child_meta_type; layout_class->set_container = clutter_bin_layout_set_container; } static void clutter_bin_layout_init (ClutterBinLayout *self) { self->priv = clutter_bin_layout_get_instance_private (self); self->priv->x_align = CLUTTER_BIN_ALIGNMENT_CENTER; self->priv->y_align = CLUTTER_BIN_ALIGNMENT_CENTER; } /** * clutter_bin_layout_new: * @x_align: the default alignment policy to be used on the * horizontal axis * @y_align: the default alignment policy to be used on the * vertical axis * * Creates a new #ClutterBinLayout layout manager * * Return value: the newly created layout manager * * Since: 1.2 */ ClutterLayoutManager * clutter_bin_layout_new (ClutterBinAlignment x_align, ClutterBinAlignment y_align) { return g_object_new (CLUTTER_TYPE_BIN_LAYOUT, "x-align", x_align, "y-align", y_align, NULL); } muffin-6.4.1/clutter/clutter/clutter-graphene.h0000664000175000017500000000174214723361714020534 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Georges Basile Stavracas Neto * * Copyright (C) 2019 Endless, Inc * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef CLUTTER_GRAPHENE_H #define CLUTTER_GRAPHENE_H void clutter_graphene_init (void); #endif muffin-6.4.1/clutter/clutter/clutter-input-device-tool.h0000664000175000017500000000507014723361714022310 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef __CLUTTER_INPUT_DEVICE_TOOL_H__ #define __CLUTTER_INPUT_DEVICE_TOOL_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include "clutter-enum-types.h" G_BEGIN_DECLS #define CLUTTER_TYPE_INPUT_DEVICE_TOOL (clutter_input_device_tool_get_type ()) #define CLUTTER_INPUT_DEVICE_TOOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE_TOOL, ClutterInputDeviceTool)) #define CLUTTER_IS_INPUT_DEVICE_TOOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE_TOOL)) #define CLUTTER_INPUT_DEVICE_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INPUT_DEVICE_TOOL, ClutterInputDeviceToolClass)) #define CLUTTER_IS_INPUT_DEVICE_TOOL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INPUT_DEVICE_TOOL)) #define CLUTTER_INPUT_DEVICE_TOOL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INPUT_DEVICE_TOOL, ClutterInputDeviceToolClass)) typedef struct _ClutterInputDeviceToolClass ClutterInputDeviceToolClass; struct _ClutterInputDeviceTool { GObject parent_instance; }; struct _ClutterInputDeviceToolClass { GObjectClass parent_class; }; CLUTTER_EXPORT GType clutter_input_device_tool_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT guint64 clutter_input_device_tool_get_serial (ClutterInputDeviceTool *tool); CLUTTER_EXPORT ClutterInputDeviceToolType clutter_input_device_tool_get_tool_type (ClutterInputDeviceTool *tool); CLUTTER_EXPORT guint64 clutter_input_device_tool_get_id (ClutterInputDeviceTool *tool); G_END_DECLS #endif /* __CLUTTER_INPUT_DEVICE_TOOL_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-device.h0000664000175000017500000002443114723361714021337 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_INPUT_DEVICE_H__ #define __CLUTTER_INPUT_DEVICE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS typedef void (*ClutterEmitInputDeviceEvent) (ClutterEvent *event, ClutterInputDevice *device); struct _ClutterInputDeviceClass { GObjectClass parent_class; gboolean (* keycode_to_evdev) (ClutterInputDevice *device, guint hardware_keycode, guint *evdev_keycode); void (* update_from_tool) (ClutterInputDevice *device, ClutterInputDeviceTool *tool); gboolean (* is_mode_switch_button) (ClutterInputDevice *device, guint group, guint button); gint (* get_group_n_modes) (ClutterInputDevice *device, gint group); gboolean (* is_grouped) (ClutterInputDevice *device, ClutterInputDevice *other_device); /* Keyboard accessbility */ void (* process_kbd_a11y_event) (ClutterEvent *event, ClutterInputDevice *device, ClutterEmitInputDeviceEvent emit_event_func); }; #define CLUTTER_TYPE_INPUT_DEVICE (clutter_input_device_get_type ()) #define CLUTTER_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDevice)) #define CLUTTER_IS_INPUT_DEVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INPUT_DEVICE)) #define CLUTTER_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass)) #define CLUTTER_IS_INPUT_DEVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INPUT_DEVICE)) #define CLUTTER_INPUT_DEVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INPUT_DEVICE, ClutterInputDeviceClass)) /** * ClutterInputDevice: * * Generic representation of an input device. The actual contents of this * structure depend on the backend used. */ typedef struct _ClutterInputDeviceClass ClutterInputDeviceClass; CLUTTER_EXPORT GType clutter_input_device_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device); CLUTTER_EXPORT gint clutter_input_device_get_device_id (ClutterInputDevice *device); CLUTTER_EXPORT gboolean clutter_input_device_get_coords (ClutterInputDevice *device, ClutterEventSequence *sequence, graphene_point_t *point); CLUTTER_EXPORT ClutterModifierType clutter_input_device_get_modifier_state (ClutterInputDevice *device); CLUTTER_EXPORT ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device); CLUTTER_EXPORT ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDevice *device); CLUTTER_EXPORT const gchar * clutter_input_device_get_device_name (ClutterInputDevice *device); CLUTTER_EXPORT ClutterInputMode clutter_input_device_get_device_mode (ClutterInputDevice *device); CLUTTER_EXPORT gboolean clutter_input_device_get_has_cursor (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_set_enabled (ClutterInputDevice *device, gboolean enabled); CLUTTER_EXPORT gboolean clutter_input_device_get_enabled (ClutterInputDevice *device); CLUTTER_EXPORT guint clutter_input_device_get_n_axes (ClutterInputDevice *device); CLUTTER_EXPORT ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, guint index_); CLUTTER_EXPORT gboolean clutter_input_device_get_axis_value (ClutterInputDevice *device, gdouble *axes, ClutterInputAxis axis, gdouble *value); CLUTTER_EXPORT guint clutter_input_device_get_n_keys (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_set_key (ClutterInputDevice *device, guint index_, guint keyval, ClutterModifierType modifiers); CLUTTER_EXPORT gboolean clutter_input_device_get_key (ClutterInputDevice *device, guint index_, guint *keyval, ClutterModifierType *modifiers); CLUTTER_EXPORT ClutterInputDevice * clutter_input_device_get_associated_device (ClutterInputDevice *device); CLUTTER_EXPORT GList * clutter_input_device_get_slave_devices (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_update_from_event (ClutterInputDevice *device, ClutterEvent *event, gboolean update_stage); CLUTTER_EXPORT void clutter_input_device_grab (ClutterInputDevice *device, ClutterActor *actor); CLUTTER_EXPORT void clutter_input_device_ungrab (ClutterInputDevice *device); CLUTTER_EXPORT ClutterActor * clutter_input_device_get_grabbed_actor (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_sequence_grab (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterActor *actor); CLUTTER_EXPORT void clutter_input_device_sequence_ungrab (ClutterInputDevice *device, ClutterEventSequence *sequence); CLUTTER_EXPORT ClutterActor * clutter_input_device_sequence_get_grabbed_actor (ClutterInputDevice *device, ClutterEventSequence *sequence); CLUTTER_EXPORT gboolean clutter_input_device_keycode_to_evdev (ClutterInputDevice *device, guint hardware_keycode, guint *evdev_keycode); CLUTTER_EXPORT const gchar * clutter_input_device_get_vendor_id (ClutterInputDevice *device); CLUTTER_EXPORT const gchar * clutter_input_device_get_product_id (ClutterInputDevice *device); CLUTTER_EXPORT gint clutter_input_device_get_n_rings (ClutterInputDevice *device); CLUTTER_EXPORT gint clutter_input_device_get_n_strips (ClutterInputDevice *device); CLUTTER_EXPORT gint clutter_input_device_get_n_mode_groups (ClutterInputDevice *device); CLUTTER_EXPORT gint clutter_input_device_get_group_n_modes (ClutterInputDevice *device, gint group); CLUTTER_EXPORT gboolean clutter_input_device_is_mode_switch_button (ClutterInputDevice *device, guint group, guint button); CLUTTER_EXPORT gint clutter_input_device_get_mode_switch_button_group (ClutterInputDevice *device, guint button); CLUTTER_EXPORT const gchar * clutter_input_device_get_device_node (ClutterInputDevice *device); CLUTTER_EXPORT ClutterInputDeviceMapping clutter_input_device_get_mapping_mode (ClutterInputDevice *device); CLUTTER_EXPORT void clutter_input_device_set_mapping_mode (ClutterInputDevice *device, ClutterInputDeviceMapping mapping); CLUTTER_EXPORT gboolean clutter_input_device_is_grouped (ClutterInputDevice *device, ClutterInputDevice *other_device); CLUTTER_EXPORT ClutterSeat * clutter_input_device_get_seat (ClutterInputDevice *device); G_END_DECLS #endif /* __CLUTTER_INPUT_DEVICE_H__ */ muffin-6.4.1/clutter/clutter/clutter-align-constraint.h0000664000175000017500000000603714723361714022221 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_ALIGN_CONSTRAINT_H__ #define __CLUTTER_ALIGN_CONSTRAINT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_ALIGN_CONSTRAINT (clutter_align_constraint_get_type ()) #define CLUTTER_ALIGN_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ALIGN_CONSTRAINT, ClutterAlignConstraint)) #define CLUTTER_IS_ALIGN_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ALIGN_CONSTRAINT)) /** * ClutterAlignConstraint: * * #ClutterAlignConstraint is an opaque structure * whose members cannot be directly accesses * * Since: 1.4 */ typedef struct _ClutterAlignConstraint ClutterAlignConstraint; typedef struct _ClutterAlignConstraintClass ClutterAlignConstraintClass; CLUTTER_EXPORT GType clutter_align_constraint_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterConstraint *clutter_align_constraint_new (ClutterActor *source, ClutterAlignAxis axis, gfloat factor); CLUTTER_EXPORT void clutter_align_constraint_set_source (ClutterAlignConstraint *align, ClutterActor *source); CLUTTER_EXPORT ClutterActor * clutter_align_constraint_get_source (ClutterAlignConstraint *align); CLUTTER_EXPORT void clutter_align_constraint_set_align_axis (ClutterAlignConstraint *align, ClutterAlignAxis axis); CLUTTER_EXPORT ClutterAlignAxis clutter_align_constraint_get_align_axis (ClutterAlignConstraint *align); CLUTTER_EXPORT void clutter_align_constraint_set_factor (ClutterAlignConstraint *align, gfloat factor); CLUTTER_EXPORT gfloat clutter_align_constraint_get_factor (ClutterAlignConstraint *align); G_END_DECLS #endif /* __CLUTTER_ALIGN_CONSTRAINT_H__ */ muffin-6.4.1/clutter/clutter/clutter-paint-context.h0000664000175000017500000000426314723361714021541 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef CLUTTER_PAINT_CONTEXT_H #define CLUTTER_PAINT_CONTEXT_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include "clutter-macros.h" #include "clutter-stage-view.h" typedef struct _ClutterPaintContext ClutterPaintContext; #define CLUTTER_TYPE_PAINT_CONTEXT (clutter_paint_context_get_type ()) CLUTTER_EXPORT GType clutter_paint_context_get_type (void); CLUTTER_EXPORT ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer); CLUTTER_EXPORT ClutterPaintContext * clutter_paint_context_ref (ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_paint_context_unref (ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_paint_context_destroy (ClutterPaintContext *paint_context); CLUTTER_EXPORT CoglFramebuffer * clutter_paint_context_get_framebuffer (ClutterPaintContext *paint_context); CLUTTER_EXPORT ClutterStageView * clutter_paint_context_get_stage_view (ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_paint_context_push_framebuffer (ClutterPaintContext *paint_context, CoglFramebuffer *framebuffer); CLUTTER_EXPORT void clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context); CLUTTER_EXPORT const cairo_region_t * clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context); #endif /* CLUTTER_PAINT_CONTEXT_H */ muffin-6.4.1/clutter/clutter/clutter-paint-context.c0000664000175000017500000001231314723361714021527 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "clutter-build-config.h" #include "clutter-paint-context-private.h" struct _ClutterPaintContext { grefcount ref_count; ClutterPaintFlag paint_flags; GList *framebuffers; ClutterStageView *view; cairo_region_t *redraw_clip; }; G_DEFINE_BOXED_TYPE (ClutterPaintContext, clutter_paint_context, clutter_paint_context_ref, clutter_paint_context_unref) ClutterPaintContext * clutter_paint_context_new_for_view (ClutterStageView *view, const cairo_region_t *redraw_clip, ClutterPaintFlag paint_flags) { ClutterPaintContext *paint_context; CoglFramebuffer *framebuffer; paint_context = g_new0 (ClutterPaintContext, 1); g_ref_count_init (&paint_context->ref_count); paint_context->view = view; paint_context->redraw_clip = cairo_region_copy (redraw_clip); paint_context->paint_flags = paint_flags; framebuffer = clutter_stage_view_get_framebuffer (view); clutter_paint_context_push_framebuffer (paint_context, framebuffer); return paint_context; } /** * clutter_paint_context_new_for_framebuffer: (skip) */ ClutterPaintContext * clutter_paint_context_new_for_framebuffer (CoglFramebuffer *framebuffer) { ClutterPaintContext *paint_context; paint_context = g_new0 (ClutterPaintContext, 1); g_ref_count_init (&paint_context->ref_count); paint_context->paint_flags = (CLUTTER_PAINT_FLAG_NO_CURSORS | CLUTTER_PAINT_FLAG_NO_PAINT_SIGNAL); clutter_paint_context_push_framebuffer (paint_context, framebuffer); return paint_context; } ClutterPaintContext * clutter_paint_context_ref (ClutterPaintContext *paint_context) { g_ref_count_inc (&paint_context->ref_count); return paint_context; } static void clutter_paint_context_dispose (ClutterPaintContext *paint_context) { g_list_free_full (paint_context->framebuffers, cogl_object_unref); paint_context->framebuffers = NULL; g_clear_pointer (&paint_context->redraw_clip, cairo_region_destroy); } void clutter_paint_context_unref (ClutterPaintContext *paint_context) { if (g_ref_count_dec (&paint_context->ref_count)) { clutter_paint_context_dispose (paint_context); g_free (paint_context); } } void clutter_paint_context_destroy (ClutterPaintContext *paint_context) { clutter_paint_context_dispose (paint_context); clutter_paint_context_unref (paint_context); } void clutter_paint_context_push_framebuffer (ClutterPaintContext *paint_context, CoglFramebuffer *framebuffer) { paint_context->framebuffers = g_list_prepend (paint_context->framebuffers, cogl_object_ref (framebuffer)); } void clutter_paint_context_pop_framebuffer (ClutterPaintContext *paint_context) { g_return_if_fail (paint_context->framebuffers); cogl_object_unref (paint_context->framebuffers->data); paint_context->framebuffers = g_list_delete_link (paint_context->framebuffers, paint_context->framebuffers); } const cairo_region_t * clutter_paint_context_get_redraw_clip (ClutterPaintContext *paint_context) { return paint_context->redraw_clip; } /** * clutter_paint_context_get_framebuffer: * @paint_context: The #ClutterPaintContext * * Returns: (transfer none): The #CoglFramebuffer used for drawing */ CoglFramebuffer * clutter_paint_context_get_framebuffer (ClutterPaintContext *paint_context) { g_return_val_if_fail (paint_context->framebuffers, NULL); return paint_context->framebuffers->data; } CoglFramebuffer * clutter_paint_context_get_base_framebuffer (ClutterPaintContext *paint_context) { return g_list_last (paint_context->framebuffers)->data; } /** * clutter_paint_context_get_stage_view: (skip) */ ClutterStageView * clutter_paint_context_get_stage_view (ClutterPaintContext *paint_context) { return paint_context->view; } /** * clutter_paint_context_is_drawing_off_stage: (skip) * * Return %TRUE if the paint context is currently drawing off stage. * This happens if there are any framebuffers pushed, and the base framebuffer * comes from the stage view. */ gboolean clutter_paint_context_is_drawing_off_stage (ClutterPaintContext *paint_context) { if (g_list_length (paint_context->framebuffers) > 1) return TRUE; return !paint_context->view; } /** * clutter_paint_context_get_paint_flags: (skip) */ ClutterPaintFlag clutter_paint_context_get_paint_flags (ClutterPaintContext *paint_context) { return paint_context->paint_flags; } muffin-6.4.1/clutter/clutter/clutter-enum-types.c.in0000664000175000017500000000153314723361714021447 0ustar fabiofabio/*** BEGIN file-header ***/ #include "clutter-build-config.h" #include "clutter-enum-types.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static gsize g_enum_type_id = 0; if (g_once_init_enter (&g_enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType id; id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_enum_type_id, id); } return g_enum_type_id; } /*** END value-tail ***/ muffin-6.4.1/clutter/clutter/clutter-snap-constraint.h0000664000175000017500000000673114723361714022071 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_SNAP_CONSTRAINT_H__ #define __CLUTTER_SNAP_CONSTRAINT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_SNAP_CONSTRAINT (clutter_snap_constraint_get_type ()) #define CLUTTER_SNAP_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SNAP_CONSTRAINT, ClutterSnapConstraint)) #define CLUTTER_IS_SNAP_CONSTRAINT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SNAP_CONSTRAINT)) /** * ClutterSnapConstraint: * * #ClutterSnapConstraint is an opaque structure * whose members cannot be directly accesses * * Since: 1.6 */ typedef struct _ClutterSnapConstraint ClutterSnapConstraint; typedef struct _ClutterSnapConstraintClass ClutterSnapConstraintClass; CLUTTER_EXPORT GType clutter_snap_constraint_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterConstraint * clutter_snap_constraint_new (ClutterActor *source, ClutterSnapEdge from_edge, ClutterSnapEdge to_edge, gfloat offset); CLUTTER_EXPORT void clutter_snap_constraint_set_source (ClutterSnapConstraint *constraint, ClutterActor *source); CLUTTER_EXPORT ClutterActor * clutter_snap_constraint_get_source (ClutterSnapConstraint *constraint); CLUTTER_EXPORT void clutter_snap_constraint_set_edges (ClutterSnapConstraint *constraint, ClutterSnapEdge from_edge, ClutterSnapEdge to_edge); CLUTTER_EXPORT void clutter_snap_constraint_get_edges (ClutterSnapConstraint *constraint, ClutterSnapEdge *from_edge, ClutterSnapEdge *to_edge); CLUTTER_EXPORT void clutter_snap_constraint_set_offset (ClutterSnapConstraint *constraint, gfloat offset); CLUTTER_EXPORT gfloat clutter_snap_constraint_get_offset (ClutterSnapConstraint *constraint); G_END_DECLS #endif /* __CLUTTER_SNAP_CONSTRAINT_H__ */ muffin-6.4.1/clutter/clutter/clutter-drop-action.c0000664000175000017500000003701514723361714021157 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-drop-action * @Title: ClutterDropAction * @short_description: An action for drop targets * * #ClutterDropAction is a #ClutterAction that allows a #ClutterActor * implementation to control what happens when an actor dragged using * a #ClutterDragAction crosses the target area or when a dragged actor * is released (or "dropped") on the target area. * * A trivial use of #ClutterDropAction consists in connecting to the * #ClutterDropAction::drop signal and handling the drop from there, * for instance: * * |[ * ClutterAction *action = clutter_drop_action (); * * g_signal_connect (action, "drop", G_CALLBACK (on_drop), NULL); * clutter_actor_add_action (an_actor, action); * ]| * * The #ClutterDropAction::can-drop can be used to control whether the * #ClutterDropAction::drop signal is going to be emitted; returning %FALSE * from a handler connected to the #ClutterDropAction::can-drop signal will * cause the #ClutterDropAction::drop signal to be skipped when the input * device button is released. * * It's important to note that #ClutterDropAction will only work with * actors dragged using #ClutterDragAction. * * See [drop-action.c](https://git.gnome.org/browse/clutter/tree/examples/drop-action.c?h=clutter-1.18) * for an example of how to use #ClutterDropAction. * * #ClutterDropAction is available since Clutter 1.8 */ #include "clutter-build-config.h" #include "clutter-drop-action.h" #include "clutter-actor-meta-private.h" #include "clutter-actor-private.h" #include "clutter-drag-action.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-stage-private.h" struct _ClutterDropActionPrivate { ClutterActor *actor; ClutterActor *stage; gulong mapped_id; }; typedef struct _DropTarget { ClutterActor *stage; gulong capture_id; GHashTable *actions; ClutterDropAction *last_action; } DropTarget; enum { CAN_DROP, OVER_IN, OVER_OUT, DROP, DROP_CANCEL, LAST_SIGNAL }; static guint drop_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterDropAction, clutter_drop_action, CLUTTER_TYPE_ACTION) static void drop_target_free (gpointer _data) { DropTarget *data = _data; g_clear_signal_handler (&data->capture_id, data->stage); g_hash_table_destroy (data->actions); g_free (data); } static gboolean on_stage_capture (ClutterStage *stage, ClutterEvent *event, gpointer user_data) { DropTarget *data = user_data; gfloat event_x, event_y; ClutterActor *actor, *drag_actor; ClutterDropAction *drop_action; ClutterInputDevice *device; gboolean was_reactive; switch (clutter_event_type (event)) { case CLUTTER_MOTION: case CLUTTER_BUTTON_RELEASE: if (clutter_event_type (event) == CLUTTER_MOTION && !(clutter_event_get_state (event) & CLUTTER_BUTTON1_MASK)) return CLUTTER_EVENT_PROPAGATE; if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && clutter_event_get_button (event) != CLUTTER_BUTTON_PRIMARY) return CLUTTER_EVENT_PROPAGATE; device = clutter_event_get_device (event); drag_actor = _clutter_stage_get_pointer_drag_actor (stage, device); if (drag_actor == NULL) return CLUTTER_EVENT_PROPAGATE; break; case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: drag_actor = _clutter_stage_get_touch_drag_actor (stage, clutter_event_get_event_sequence (event)); if (drag_actor == NULL) return CLUTTER_EVENT_PROPAGATE; break; default: return CLUTTER_EVENT_PROPAGATE; } clutter_event_get_coords (event, &event_x, &event_y); /* get the actor under the cursor, excluding the dragged actor; we * use reactivity because it won't cause any scene invalidation */ was_reactive = clutter_actor_get_reactive (drag_actor); clutter_actor_set_reactive (drag_actor, FALSE); actor = clutter_stage_get_actor_at_pos (stage, CLUTTER_PICK_REACTIVE, event_x, event_y); if (actor == NULL || actor == CLUTTER_ACTOR (stage)) { if (data->last_action != NULL) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action); g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0, clutter_actor_meta_get_actor (meta)); data->last_action = NULL; } goto out; } drop_action = g_hash_table_lookup (data->actions, actor); if (drop_action == NULL) { if (data->last_action != NULL) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action); g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0, clutter_actor_meta_get_actor (meta)); data->last_action = NULL; } goto out; } else { if (data->last_action != drop_action) { ClutterActorMeta *meta; if (data->last_action != NULL) { meta = CLUTTER_ACTOR_META (data->last_action); g_signal_emit (data->last_action, drop_signals[OVER_OUT], 0, clutter_actor_meta_get_actor (meta)); } meta = CLUTTER_ACTOR_META (drop_action); g_signal_emit (drop_action, drop_signals[OVER_IN], 0, clutter_actor_meta_get_actor (meta)); } data->last_action = drop_action; } out: if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE || clutter_event_type (event) == CLUTTER_TOUCH_END) { if (data->last_action != NULL) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (data->last_action); gboolean can_drop = FALSE; g_signal_emit (data->last_action, drop_signals[CAN_DROP], 0, clutter_actor_meta_get_actor (meta), event_x, event_y, &can_drop); if (can_drop) { g_signal_emit (data->last_action, drop_signals[DROP], 0, clutter_actor_meta_get_actor (meta), event_x, event_y); } else { g_signal_emit (data->last_action, drop_signals[DROP_CANCEL], 0, clutter_actor_meta_get_actor (meta), event_x, event_y); } } data->last_action = NULL; } if (drag_actor != NULL) clutter_actor_set_reactive (drag_actor, was_reactive); return CLUTTER_EVENT_PROPAGATE; } static void drop_action_register (ClutterDropAction *self) { ClutterDropActionPrivate *priv = self->priv; DropTarget *data; g_assert (priv->stage != NULL); data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets"); if (data == NULL) { data = g_new0 (DropTarget, 1); data->stage = priv->stage; data->actions = g_hash_table_new (NULL, NULL); data->capture_id = g_signal_connect (priv->stage, "captured-event", G_CALLBACK (on_stage_capture), data); g_object_set_data_full (G_OBJECT (priv->stage), "__clutter_drop_targets", data, drop_target_free); } g_hash_table_replace (data->actions, priv->actor, self); } static void drop_action_unregister (ClutterDropAction *self) { ClutterDropActionPrivate *priv = self->priv; DropTarget *data = NULL; if (priv->stage != NULL) data = g_object_get_data (G_OBJECT (priv->stage), "__clutter_drop_targets"); if (data == NULL) return; g_hash_table_remove (data->actions, priv->actor); if (g_hash_table_size (data->actions) == 0) g_object_set_data (G_OBJECT (data->stage), "__clutter_drop_targets", NULL); } static void on_actor_mapped (ClutterActor *actor, GParamSpec *pspec, ClutterDropAction *self) { if (clutter_actor_is_mapped (actor)) { if (self->priv->stage == NULL) self->priv->stage = clutter_actor_get_stage (actor); drop_action_register (self); } else drop_action_unregister (self); } static void clutter_drop_action_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterDropActionPrivate *priv = CLUTTER_DROP_ACTION (meta)->priv; if (priv->actor != NULL) { drop_action_unregister (CLUTTER_DROP_ACTION (meta)); g_clear_signal_handler (&priv->mapped_id, priv->actor); priv->stage = NULL; priv->actor = NULL; } priv->actor = actor; if (priv->actor != NULL) { priv->stage = clutter_actor_get_stage (actor); priv->mapped_id = g_signal_connect (actor, "notify::mapped", G_CALLBACK (on_actor_mapped), meta); if (priv->stage != NULL) drop_action_register (CLUTTER_DROP_ACTION (meta)); } CLUTTER_ACTOR_META_CLASS (clutter_drop_action_parent_class)->set_actor (meta, actor); } static gboolean signal_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer user_data) { gboolean continue_emission; continue_emission = g_value_get_boolean (handler_return); g_value_set_boolean (return_accu, continue_emission); return continue_emission; } static gboolean clutter_drop_action_real_can_drop (ClutterDropAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y) { return TRUE; } static void clutter_drop_action_class_init (ClutterDropActionClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); meta_class->set_actor = clutter_drop_action_set_actor; klass->can_drop = clutter_drop_action_real_can_drop; /** * ClutterDropAction::can-drop: * @action: the #ClutterDropAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @event_x: the X coordinate (in stage space) of the drop event * @event_y: the Y coordinate (in stage space) of the drop event * * The ::can-drop signal is emitted when the dragged actor is dropped * on @actor. The return value of the ::can-drop signal will determine * whether or not the #ClutterDropAction::drop signal is going to be * emitted on @action. * * The default implementation of #ClutterDropAction returns %TRUE for * this signal. * * Return value: %TRUE if the drop is accepted, and %FALSE otherwise * * Since: 1.8 */ drop_signals[CAN_DROP] = g_signal_new (I_("can-drop"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDropActionClass, can_drop), signal_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_FLOAT_FLOAT, G_TYPE_BOOLEAN, 3, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT); /** * ClutterDropAction::over-in: * @action: the #ClutterDropAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::over-in signal is emitted when the dragged actor crosses * into @actor. * * Since: 1.8 */ drop_signals[OVER_IN] = g_signal_new (I_("over-in"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDropActionClass, over_in), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterDropAction::over-out: * @action: the #ClutterDropAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::over-out signal is emitted when the dragged actor crosses * outside @actor. * * Since: 1.8 */ drop_signals[OVER_OUT] = g_signal_new (I_("over-out"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDropActionClass, over_out), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterDropAction::drop: * @action: the #ClutterDropAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @event_x: the X coordinate (in stage space) of the drop event * @event_y: the Y coordinate (in stage space) of the drop event * * The ::drop signal is emitted when the dragged actor is dropped * on @actor. This signal is only emitted if at least an handler of * #ClutterDropAction::can-drop returns %TRUE. * * Since: 1.8 */ drop_signals[DROP] = g_signal_new (I_("drop"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDropActionClass, drop), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT, G_TYPE_NONE, 3, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT); /** * ClutterDropAction::drop-cancel: * @action: the #ClutterDropAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @event_x: the X coordinate (in stage space) of the drop event * @event_y: the Y coordinate (in stage space) of the drop event * * The ::drop-cancel signal is emitted when the drop is refused * by an emission of the #ClutterDropAction::can-drop signal. * * After the ::drop-cancel signal is fired the active drag is * terminated. * * Since: 1.12 */ drop_signals[DROP_CANCEL] = g_signal_new (I_("drop-cancel"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDropActionClass, drop), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT, G_TYPE_NONE, 3, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT); } static void clutter_drop_action_init (ClutterDropAction *self) { self->priv = clutter_drop_action_get_instance_private (self); } /** * clutter_drop_action_new: * * Creates a new #ClutterDropAction. * * Use clutter_actor_add_action() to add the action to a #ClutterActor. * * Return value: the newly created #ClutterDropAction * * Since: 1.8 */ ClutterAction * clutter_drop_action_new (void) { return g_object_new (CLUTTER_TYPE_DROP_ACTION, NULL); } muffin-6.4.1/clutter/clutter/clutter-input-device-tool.c0000664000175000017500000001320414723361714022301 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "clutter-build-config.h" #include "clutter-input-device-tool.h" #include "clutter-private.h" typedef struct _ClutterInputDeviceToolPrivate ClutterInputDeviceToolPrivate; struct _ClutterInputDeviceToolPrivate { ClutterInputDeviceToolType type; guint64 serial; guint64 id; }; enum { PROP_0, PROP_TYPE, PROP_SERIAL, PROP_ID, PROP_LAST }; static GParamSpec *props[PROP_LAST] = { NULL, }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterInputDeviceTool, clutter_input_device_tool, G_TYPE_OBJECT) static void clutter_input_device_tool_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterInputDeviceTool *tool = CLUTTER_INPUT_DEVICE_TOOL (object); ClutterInputDeviceToolPrivate *priv; priv = clutter_input_device_tool_get_instance_private (tool); switch (prop_id) { case PROP_TYPE: priv->type = g_value_get_enum (value); break; case PROP_SERIAL: priv->serial = g_value_get_uint64 (value); break; case PROP_ID: priv->id = g_value_get_uint64 (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_input_device_tool_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterInputDeviceTool *tool = CLUTTER_INPUT_DEVICE_TOOL (object); ClutterInputDeviceToolPrivate *priv; priv = clutter_input_device_tool_get_instance_private (tool); switch (prop_id) { case PROP_TYPE: g_value_set_enum (value, priv->type); break; case PROP_SERIAL: g_value_set_uint64 (value, priv->serial); break; case PROP_ID: g_value_set_uint64 (value, priv->id); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_input_device_tool_class_init (ClutterInputDeviceToolClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_input_device_tool_set_property; gobject_class->get_property = clutter_input_device_tool_get_property; props[PROP_TYPE] = g_param_spec_enum ("type", P_("Tool type"), P_("Tool type"), CLUTTER_TYPE_INPUT_DEVICE_TOOL_TYPE, CLUTTER_INPUT_DEVICE_TOOL_NONE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); props[PROP_SERIAL] = g_param_spec_uint64 ("serial", P_("Tool serial"), P_("Tool serial"), 0, G_MAXUINT64, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); props[PROP_ID] = g_param_spec_uint64 ("id", P_("Tool ID"), P_("Tool ID"), 0, G_MAXUINT64, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (gobject_class, PROP_LAST, props); } static void clutter_input_device_tool_init (ClutterInputDeviceTool *tool) { } /** * clutter_input_device_tool_get_serial: * @tool: a #ClutterInputDeviceTool * * Gets the serial of this tool, this value can be used to identify a * physical tool (eg. a tablet pen) across program executions. * * Returns: The serial ID for this tool * * Since: 1.28 **/ guint64 clutter_input_device_tool_get_serial (ClutterInputDeviceTool *tool) { ClutterInputDeviceToolPrivate *priv; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_TOOL (tool), 0); priv = clutter_input_device_tool_get_instance_private (tool); return priv->serial; } /** * clutter_input_device_tool_get_tool_type: * @tool: a #ClutterInputDeviceTool * * Gets the tool type of this tool. * * Returns: The tool type of this tool * * Since: 1.28 **/ ClutterInputDeviceToolType clutter_input_device_tool_get_tool_type (ClutterInputDeviceTool *tool) { ClutterInputDeviceToolPrivate *priv; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_TOOL (tool), 0); priv = clutter_input_device_tool_get_instance_private (tool); return priv->type; } /** * clutter_input_device_tool_get_id: * @tool: a #ClutterInputDeviceTool * * Gets the ID of this tool, this value can be used to identify a * physical tool (eg. a tablet pen) across program executions. * * Returns: The tool ID for this tool **/ guint64 clutter_input_device_tool_get_id (ClutterInputDeviceTool *tool) { ClutterInputDeviceToolPrivate *priv; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE_TOOL (tool), 0); priv = clutter_input_device_tool_get_instance_private (tool); return priv->id; } muffin-6.4.1/clutter/clutter/clutter-backend-private.h0000664000175000017500000001520514723361714022001 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_BACKEND_PRIVATE_H__ #define __CLUTTER_BACKEND_PRIVATE_H__ #include #include #include #define CLUTTER_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND, ClutterBackendClass)) #define CLUTTER_IS_BACKEND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND)) #define CLUTTER_BACKEND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND, ClutterBackendClass)) G_BEGIN_DECLS typedef struct _ClutterBackendPrivate ClutterBackendPrivate; struct _ClutterBackend { /*< private >*/ GObject parent_instance; CoglRenderer *cogl_renderer; CoglDisplay *cogl_display; CoglContext *cogl_context; GSource *cogl_source; CoglOnscreen *dummy_onscreen; cairo_font_options_t *font_options; gchar *font_name; gfloat units_per_em; gint32 units_serial; ClutterStageWindow *stage_window; ClutterInputMethod *input_method; }; struct _ClutterBackendClass { /*< private >*/ GObjectClass parent_class; /* vfuncs */ gboolean (* pre_parse) (ClutterBackend *backend, GError **error); gboolean (* post_parse) (ClutterBackend *backend, GError **error); ClutterStageWindow * (* create_stage) (ClutterBackend *backend, ClutterStage *wrapper, GError **error); void (* init_events) (ClutterBackend *backend); void (* init_features) (ClutterBackend *backend); void (* add_options) (ClutterBackend *backend, GOptionGroup *group); ClutterFeatureFlags (* get_features) (ClutterBackend *backend); CoglRenderer * (* get_renderer) (ClutterBackend *backend, GError **error); CoglDisplay * (* get_display) (ClutterBackend *backend, CoglRenderer *renderer, CoglSwapChain *swap_chain, GError **error); gboolean (* create_context) (ClutterBackend *backend, GError **error); gboolean (* translate_event) (ClutterBackend *backend, gpointer native, ClutterEvent *event); ClutterSeat * (* get_default_seat) (ClutterBackend *backend); /* signals */ void (* resolution_changed) (ClutterBackend *backend); void (* font_changed) (ClutterBackend *backend); void (* settings_changed) (ClutterBackend *backend); }; ClutterBackend * _clutter_create_backend (void); ClutterStageWindow * _clutter_backend_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error); gboolean _clutter_backend_create_context (ClutterBackend *backend, GError **error); void _clutter_backend_add_options (ClutterBackend *backend, GOptionGroup *group); gboolean _clutter_backend_pre_parse (ClutterBackend *backend, GError **error); gboolean _clutter_backend_post_parse (ClutterBackend *backend, GError **error); void _clutter_backend_init_events (ClutterBackend *backend); void _clutter_backend_copy_event_data (ClutterBackend *backend, const ClutterEvent *src, ClutterEvent *dest); void _clutter_backend_free_event_data (ClutterBackend *backend, ClutterEvent *event); CLUTTER_EXPORT gboolean _clutter_backend_translate_event (ClutterBackend *backend, gpointer native, ClutterEvent *event); ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend); gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend, PangoFontDescription *font_desc); gint32 _clutter_backend_get_units_serial (ClutterBackend *backend); void clutter_set_allowed_drivers (const char *drivers); CLUTTER_EXPORT ClutterStageWindow * clutter_backend_get_stage_window (ClutterBackend *backend); G_END_DECLS #endif /* __CLUTTER_BACKEND_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-seat.c0000664000175000017500000005314414723361714017675 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "clutter-build-config.h" #include "clutter-backend-private.h" #include "clutter-input-device-tool.h" #include "clutter-input-pointer-a11y-private.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-seat.h" #include "clutter-virtual-input-device.h" enum { DEVICE_ADDED, DEVICE_REMOVED, TOOL_CHANGED, KBD_A11Y_MASK_CHANGED, KBD_A11Y_FLAGS_CHANGED, PTR_A11Y_DWELL_CLICK_TYPE_CHANGED, PTR_A11Y_TIMEOUT_STARTED, PTR_A11Y_TIMEOUT_STOPPED, IS_UNFOCUS_INHIBITED_CHANGED, N_SIGNALS, }; static guint signals[N_SIGNALS] = { 0 }; enum { PROP_0, PROP_BACKEND, PROP_TOUCH_MODE, N_PROPS }; static GParamSpec *props[N_PROPS]; typedef struct _ClutterSeatPrivate ClutterSeatPrivate; struct _ClutterSeatPrivate { ClutterBackend *backend; unsigned int inhibit_unfocus_count; /* Keyboard a11y */ ClutterKbdA11ySettings kbd_a11y_settings; /* Pointer a11y */ ClutterPointerA11ySettings pointer_a11y_settings; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterSeat, clutter_seat, G_TYPE_OBJECT) static void clutter_seat_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterSeat *seat = CLUTTER_SEAT (object); ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); switch (prop_id) { case PROP_BACKEND: priv->backend = g_value_get_object (value); break; case PROP_TOUCH_MODE: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_seat_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterSeat *seat = CLUTTER_SEAT (object); ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); switch (prop_id) { case PROP_BACKEND: g_value_set_object (value, priv->backend); break; case PROP_TOUCH_MODE: g_value_set_boolean (value, FALSE); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_seat_finalize (GObject *object) { ClutterSeat *seat = CLUTTER_SEAT (object); ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_clear_object (&priv->backend); G_OBJECT_CLASS (clutter_seat_parent_class)->finalize (object); } static void clutter_seat_class_init (ClutterSeatClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = clutter_seat_set_property; object_class->get_property = clutter_seat_get_property; object_class->finalize = clutter_seat_finalize; signals[DEVICE_ADDED] = g_signal_new (I_("device-added"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_INPUT_DEVICE); signals[DEVICE_REMOVED] = g_signal_new (I_("device-removed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_INPUT_DEVICE); signals[TOOL_CHANGED] = g_signal_new (I_("tool-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__OBJECT_OBJECT, G_TYPE_NONE, 2, CLUTTER_TYPE_INPUT_DEVICE, CLUTTER_TYPE_INPUT_DEVICE_TOOL); g_signal_set_va_marshaller (signals[TOOL_CHANGED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__OBJECT_OBJECTv); /** * ClutterSeat::kbd-a11y-mods-state-changed: * @seat: the #ClutterSeat that emitted the signal * @latched_mask: the latched modifier mask from stickykeys * @locked_mask: the locked modifier mask from stickykeys * * The ::kbd-a11y-mods-state-changed signal is emitted each time either the * latched modifiers mask or locked modifiers mask are changed as the * result of keyboard accessibilty's sticky keys operations. */ signals[KBD_A11Y_MASK_CHANGED] = g_signal_new (I_("kbd-a11y-mods-state-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); g_signal_set_va_marshaller (signals[KBD_A11Y_MASK_CHANGED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__UINT_UINTv); /** * ClutterSeat::kbd-a11y-flags-changed: * @seat: the #ClutterSeat that emitted the signal * @settings_flags: the new ClutterKeyboardA11yFlags configuration * @changed_mask: the ClutterKeyboardA11yFlags changed * * The ::kbd-a11y-flags-changed signal is emitted each time the * ClutterKeyboardA11yFlags configuration is changed as the result of * keyboard accessibility operations. */ signals[KBD_A11Y_FLAGS_CHANGED] = g_signal_new (I_("kbd-a11y-flags-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); g_signal_set_va_marshaller (signals[KBD_A11Y_FLAGS_CHANGED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__UINT_UINTv); /** * ClutterSeat::ptr-a11y-dwell-click-type-changed: * @seat: the #ClutterSeat that emitted the signal * @click_type: the new #ClutterPointerA11yDwellClickType mode * * The ::ptr-a11y-dwell-click-type-changed signal is emitted each time * the ClutterPointerA11yDwellClickType mode is changed as the result * of pointer accessibility operations. */ signals[PTR_A11Y_DWELL_CLICK_TYPE_CHANGED] = g_signal_new (I_("ptr-a11y-dwell-click-type-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_POINTER_A11Y_DWELL_CLICK_TYPE); /** * ClutterSeat::ptr-a11y-timeout-started: * @seat: the #ClutterSeat that emitted the signal * @device: the core pointer #ClutterInputDevice * @timeout_type: the type of timeout #ClutterPointerA11yTimeoutType * @delay: the delay in ms before secondary-click is triggered. * * The ::ptr-a11y-timeout-started signal is emitted when a * pointer accessibility timeout delay is started, so that upper * layers can notify the user with some visual feedback. */ signals[PTR_A11Y_TIMEOUT_STARTED] = g_signal_new (I_("ptr-a11y-timeout-started"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__OBJECT_FLAGS_UINT, G_TYPE_NONE, 3, CLUTTER_TYPE_INPUT_DEVICE, CLUTTER_TYPE_POINTER_A11Y_TIMEOUT_TYPE, G_TYPE_UINT); g_signal_set_va_marshaller (signals[PTR_A11Y_TIMEOUT_STARTED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__OBJECT_FLAGS_UINTv); /** * ClutterSeat::ptr-a11y-timeout-stopped: * @seat: the #ClutterSeat that emitted the signal * @device: the core pointer #ClutterInputDevice * @timeout_type: the type of timeout #ClutterPointerA11yTimeoutType * @clicked: %TRUE if the timeout finished and triggered a click * * The ::ptr-a11y-timeout-stopped signal is emitted when a running * pointer accessibility timeout delay is stopped, either because * it's triggered at the end of the delay or cancelled, so that * upper layers can notify the user with some visual feedback. */ signals[PTR_A11Y_TIMEOUT_STOPPED] = g_signal_new (I_("ptr-a11y-timeout-stopped"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__OBJECT_FLAGS_BOOLEAN, G_TYPE_NONE, 3, CLUTTER_TYPE_INPUT_DEVICE, CLUTTER_TYPE_POINTER_A11Y_TIMEOUT_TYPE, G_TYPE_BOOLEAN); g_signal_set_va_marshaller (signals[PTR_A11Y_TIMEOUT_STOPPED], G_TYPE_FROM_CLASS (object_class), _clutter_marshal_VOID__OBJECT_FLAGS_BOOLEANv); /** * ClutterSeat::is-unfocus-inhibited-changed: * @seat: the #ClutterSeat that emitted the signal * * The ::is-unfocus-inhibited-changed signal is emitted when the * property to inhibit the unsetting of the focus-surface of the * #ClutterSeat changed. To get the current state of this property, * use clutter_seat_is_unfocus_inhibited(). */ signals[IS_UNFOCUS_INHIBITED_CHANGED] = g_signal_new (I_("is-unfocus-inhibited-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); props[PROP_BACKEND] = g_param_spec_object ("backend", P_("Backend"), P_("Backend"), CLUTTER_TYPE_BACKEND, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterSeat:touch-mode: * * The current touch-mode of the #ClutterSeat, it is set to %TRUE if the * requirements documented in clutter_seat_get_touch_mode() are fulfilled. **/ props[PROP_TOUCH_MODE] = g_param_spec_boolean ("touch-mode", P_("Touch mode"), P_("Touch mode"), FALSE, CLUTTER_PARAM_READABLE); g_object_class_install_properties (object_class, N_PROPS, props); } static void clutter_seat_init (ClutterSeat *seat) { } /** * clutter_seat_get_pointer: * @seat: a #ClutterSeat * * Returns the master pointer * * Returns: (transfer none): the master pointer **/ ClutterInputDevice * clutter_seat_get_pointer (ClutterSeat *seat) { g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL); return CLUTTER_SEAT_GET_CLASS (seat)->get_pointer (seat); } /** * clutter_seat_get_keyboard: * @seat: a #ClutterSeat * * Returns the master keyboard * * Returns: (transfer none): the master keyboard **/ ClutterInputDevice * clutter_seat_get_keyboard (ClutterSeat *seat) { g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL); return CLUTTER_SEAT_GET_CLASS (seat)->get_keyboard (seat); } /** * clutter_seat_list_devices: * @seat: a #ClutterSeat * * Returns the list of HW devices * * Returns: (transfer container) (element-type Clutter.InputDevice): A list * of #ClutterInputDevice. The elements of the returned list are owned by * Clutter and may not be freed, the returned list should be freed using * g_list_free() when done. **/ GList * clutter_seat_list_devices (ClutterSeat *seat) { g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL); return CLUTTER_SEAT_GET_CLASS (seat)->list_devices (seat); } void clutter_seat_bell_notify (ClutterSeat *seat) { CLUTTER_SEAT_GET_CLASS (seat)->bell_notify (seat); } /** * clutter_seat_get_keymap: * @seat: a #ClutterSeat * * Returns the seat keymap * * Returns: (transfer none): the seat keymap **/ ClutterKeymap * clutter_seat_get_keymap (ClutterSeat *seat) { return CLUTTER_SEAT_GET_CLASS (seat)->get_keymap (seat); } static gboolean are_kbd_a11y_settings_equal (ClutterKbdA11ySettings *a, ClutterKbdA11ySettings *b) { return (memcmp (a, b, sizeof (ClutterKbdA11ySettings)) == 0); } void clutter_seat_set_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *settings) { ClutterSeatClass *seat_class; ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_return_if_fail (CLUTTER_IS_SEAT (seat)); if (are_kbd_a11y_settings_equal (&priv->kbd_a11y_settings, settings)) return; priv->kbd_a11y_settings = *settings; seat_class = CLUTTER_SEAT_GET_CLASS (seat); if (seat_class->apply_kbd_a11y_settings) seat_class->apply_kbd_a11y_settings (seat, settings); } void clutter_seat_get_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *settings) { ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_return_if_fail (CLUTTER_IS_SEAT (seat)); *settings = priv->kbd_a11y_settings; } void clutter_seat_ensure_a11y_state (ClutterSeat *seat) { ClutterInputDevice *core_pointer; core_pointer = clutter_seat_get_pointer (seat); if (core_pointer) { if (_clutter_is_input_pointer_a11y_enabled (core_pointer)) _clutter_input_pointer_a11y_add_device (core_pointer); } } static gboolean are_pointer_a11y_settings_equal (ClutterPointerA11ySettings *a, ClutterPointerA11ySettings *b) { return (memcmp (a, b, sizeof (ClutterPointerA11ySettings)) == 0); } static void clutter_seat_enable_pointer_a11y (ClutterSeat *seat) { ClutterInputDevice *core_pointer; core_pointer = clutter_seat_get_pointer (seat); _clutter_input_pointer_a11y_add_device (core_pointer); } static void clutter_seat_disable_pointer_a11y (ClutterSeat *seat) { ClutterInputDevice *core_pointer; core_pointer = clutter_seat_get_pointer (seat); _clutter_input_pointer_a11y_remove_device (core_pointer); } /** * clutter_seat_set_pointer_a11y_settings: * @seat: a #ClutterSeat * @settings: a pointer to a #ClutterPointerA11ySettings * * Sets the pointer accessibility settings **/ void clutter_seat_set_pointer_a11y_settings (ClutterSeat *seat, ClutterPointerA11ySettings *settings) { ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_return_if_fail (CLUTTER_IS_SEAT (seat)); if (are_pointer_a11y_settings_equal (&priv->pointer_a11y_settings, settings)) return; if (priv->pointer_a11y_settings.controls == 0 && settings->controls != 0) clutter_seat_enable_pointer_a11y (seat); else if (priv->pointer_a11y_settings.controls != 0 && settings->controls == 0) clutter_seat_disable_pointer_a11y (seat); priv->pointer_a11y_settings = *settings; } /** * clutter_seat_get_pointer_a11y_settings: * @seat: a #ClutterSeat * @settings: a pointer to a #ClutterPointerA11ySettings * * Gets the current pointer accessibility settings **/ void clutter_seat_get_pointer_a11y_settings (ClutterSeat *seat, ClutterPointerA11ySettings *settings) { ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_return_if_fail (CLUTTER_IS_SEAT (seat)); *settings = priv->pointer_a11y_settings; } /** * clutter_seat_set_pointer_a11y_dwell_click_type: * @seat: a #ClutterSeat * @click_type: type of click as #ClutterPointerA11yDwellClickType * * Sets the dwell click type **/ void clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat *seat, ClutterPointerA11yDwellClickType click_type) { ClutterSeatPrivate *priv = clutter_seat_get_instance_private (seat); g_return_if_fail (CLUTTER_IS_SEAT (seat)); priv->pointer_a11y_settings.dwell_click_type = click_type; } /** * clutter_seat_inhibit_unfocus: * @seat: a #ClutterSeat * * Inhibits unsetting of the pointer focus-surface for the #ClutterSeat @seat, * this allows to keep using the pointer even when it's hidden. * * This property is refcounted, so clutter_seat_uninhibit_unfocus() must be * called the exact same number of times as clutter_seat_inhibit_unfocus() * was called before. **/ void clutter_seat_inhibit_unfocus (ClutterSeat *seat) { ClutterSeatPrivate *priv; g_return_if_fail (CLUTTER_IS_SEAT (seat)); priv = clutter_seat_get_instance_private (seat); priv->inhibit_unfocus_count++; if (priv->inhibit_unfocus_count == 1) g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0); } /** * clutter_seat_uninhibit_unfocus: * @seat: a #ClutterSeat * * Disables the inhibiting of unsetting of the pointer focus-surface * previously enabled by calling clutter_seat_inhibit_unfocus(). * * This property is refcounted, so clutter_seat_uninhibit_unfocus() must be * called the exact same number of times as clutter_seat_inhibit_unfocus() * was called before. **/ void clutter_seat_uninhibit_unfocus (ClutterSeat *seat) { ClutterSeatPrivate *priv; g_return_if_fail (CLUTTER_IS_SEAT (seat)); priv = clutter_seat_get_instance_private (seat); if (priv->inhibit_unfocus_count == 0) { g_warning ("Called clutter_seat_uninhibit_unfocus without inhibiting before"); return; } priv->inhibit_unfocus_count--; if (priv->inhibit_unfocus_count == 0) g_signal_emit (G_OBJECT (seat), signals[IS_UNFOCUS_INHIBITED_CHANGED], 0); } /** * clutter_seat_is_unfocus_inhibited: * @seat: a #ClutterSeat * * Gets whether unsetting of the pointer focus-surface is inhibited * for the #ClutterSeat @seat. * * Returns: %TRUE if unsetting is inhibited, %FALSE otherwise **/ gboolean clutter_seat_is_unfocus_inhibited (ClutterSeat *seat) { ClutterSeatPrivate *priv; g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE); priv = clutter_seat_get_instance_private (seat); return priv->inhibit_unfocus_count > 0; } /** * clutter_seat_create_virtual_device: * @seat: a #ClutterSeat * @device_type: the type of the virtual device * * Creates a virtual input device. * * Returns: (transfer full): a newly created virtual device **/ ClutterVirtualInputDevice * clutter_seat_create_virtual_device (ClutterSeat *seat, ClutterInputDeviceType device_type) { ClutterSeatClass *seat_class; g_return_val_if_fail (CLUTTER_IS_SEAT (seat), NULL); seat_class = CLUTTER_SEAT_GET_CLASS (seat); return seat_class->create_virtual_device (seat, device_type); } /** * clutter_seat_supported_virtual_device_types: (skip) */ ClutterVirtualDeviceType clutter_seat_get_supported_virtual_device_types (ClutterSeat *seat) { ClutterSeatClass *seat_class; g_return_val_if_fail (CLUTTER_IS_SEAT (seat), CLUTTER_VIRTUAL_DEVICE_TYPE_NONE); seat_class = CLUTTER_SEAT_GET_CLASS (seat); return seat_class->get_supported_virtual_device_types (seat); } void clutter_seat_compress_motion (ClutterSeat *seat, ClutterEvent *event, const ClutterEvent *to_discard) { ClutterSeatClass *seat_class; g_return_if_fail (CLUTTER_IS_SEAT (seat)); seat_class = CLUTTER_SEAT_GET_CLASS (seat); if (seat_class->compress_motion) seat_class->compress_motion (seat, event, to_discard); } gboolean clutter_seat_handle_device_event (ClutterSeat *seat, ClutterEvent *event) { ClutterSeatClass *seat_class; ClutterInputDevice *device; g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE); g_return_val_if_fail (event, FALSE); g_assert (event->type == CLUTTER_DEVICE_ADDED || event->type == CLUTTER_DEVICE_REMOVED); seat_class = CLUTTER_SEAT_GET_CLASS (seat); if (seat_class->handle_device_event) { if (!seat_class->handle_device_event (seat, event)) return FALSE; } device = clutter_event_get_source_device (event); g_assert_true (CLUTTER_IS_INPUT_DEVICE (device)); switch (event->type) { case CLUTTER_DEVICE_ADDED: g_signal_emit (seat, signals[DEVICE_ADDED], 0, device); break; case CLUTTER_DEVICE_REMOVED: g_signal_emit (seat, signals[DEVICE_REMOVED], 0, device); g_object_run_dispose (G_OBJECT (device)); break; default: break; } return TRUE; } void clutter_seat_warp_pointer (ClutterSeat *seat, int x, int y) { g_return_if_fail (CLUTTER_IS_SEAT (seat)); CLUTTER_SEAT_GET_CLASS (seat)->warp_pointer (seat, x, y); } /** * clutter_seat_get_touch_mode: * @seat: a #ClutterSeat * * Gets the current touch-mode state of the #ClutterSeat @seat. * The #ClutterSeat:touch-mode property is set to %TRUE if the following * requirements are fulfilled: * * - A touchscreen is available * - A tablet mode switch, if present, is enabled * * Returns: %TRUE if the device is a tablet that doesn't have an external * keyboard attached, %FALSE otherwise. **/ gboolean clutter_seat_get_touch_mode (ClutterSeat *seat) { gboolean touch_mode; g_return_val_if_fail (CLUTTER_IS_SEAT (seat), FALSE); g_object_get (G_OBJECT (seat), "touch-mode", &touch_mode, NULL); return touch_mode; } muffin-6.4.1/clutter/clutter/clutter-paint-nodes.h0000664000175000017500000002153014723361714021161 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_PAINT_NODES_H__ #define __CLUTTER_PAINT_NODES_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_COLOR_NODE (clutter_color_node_get_type ()) #define CLUTTER_COLOR_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_COLOR_NODE, ClutterColorNode)) #define CLUTTER_IS_COLOR_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_COLOR_NODE)) /** * ClutterColorNode: * * The #ClutterTextNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterColorNode ClutterColorNode; typedef struct _ClutterColorNodeClass ClutterColorNodeClass; CLUTTER_EXPORT GType clutter_color_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_color_node_new (const ClutterColor *color); #define CLUTTER_TYPE_TEXTURE_NODE (clutter_texture_node_get_type ()) #define CLUTTER_TEXTURE_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXTURE_NODE, ClutterTextureNode)) #define CLUTTER_IS_TEXTURE_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXTURE_NODE)) /** * ClutterTextureNode: * * The #ClutterTextNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterTextureNode ClutterTextureNode; typedef struct _ClutterTextureNodeClass ClutterTextureNodeClass; CLUTTER_EXPORT GType clutter_texture_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_texture_node_new (CoglTexture *texture, const ClutterColor *color, ClutterScalingFilter min_filter, ClutterScalingFilter mag_filter); #define CLUTTER_TYPE_CLIP_NODE (clutter_clip_node_get_type ()) #define CLUTTER_CLIP_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CLIP_NODE, ClutterClipNode)) #define CLUTTER_IS_CLIP_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CLIP_NODE)) /** * ClutterClipNode: * * The #ClutterTextNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterClipNode ClutterClipNode; typedef struct _ClutterClipNodeClass ClutterClipNodeClass; CLUTTER_EXPORT GType clutter_clip_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_clip_node_new (void); #define CLUTTER_TYPE_PIPELINE_NODE (clutter_pipeline_node_get_type ()) #define CLUTTER_PIPELINE_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PIPELINE_NODE, ClutterPipelineNode)) #define CLUTTER_IS_PIPELINE_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PIPELINE_NODE)) /** * ClutterPipelineNode: * * The #ClutterTextNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterPipelineNode ClutterPipelineNode; typedef struct _ClutterPipelineNodeClass ClutterPipelineNodeClass; CLUTTER_EXPORT GType clutter_pipeline_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_pipeline_node_new (CoglPipeline *pipeline); #define CLUTTER_TYPE_TEXT_NODE (clutter_text_node_get_type ()) #define CLUTTER_TEXT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT_NODE, ClutterTextNode)) #define CLUTTER_IS_TEXT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT_NODE)) /** * ClutterTextNode: * * The #ClutterTextNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterTextNode ClutterTextNode; typedef struct _ClutterTextNodeClass ClutterTextNodeClass; CLUTTER_EXPORT GType clutter_text_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_text_node_new (PangoLayout *layout, const ClutterColor *color); #define CLUTTER_TYPE_ACTOR_NODE (clutter_actor_node_get_type ()) #define CLUTTER_ACTOR_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ACTOR_NODE, ClutterActorNode)) #define CLUTTER_IS_ACTOR_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ACTOR_NODE)) /** * ClutterActorNode: * * The #ClutterActorNode structure is an opaque * type whose members cannot be directly accessed. */ typedef struct _ClutterActorNode ClutterActorNode; typedef struct _ClutterActorNode ClutterActorNodeClass; CLUTTER_EXPORT GType clutter_actor_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_actor_node_new (ClutterActor *actor); #define CLUTTER_TYPE_ROOT_NODE (clutter_root_node_get_type ()) #define CLUTTER_ROOT_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ROOT_NODE, ClutterRootNode)) #define CLUTTER_IS_ROOT_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ROOT_NODE)) /** * ClutterRootNode: * * The #ClutterRootNode structure is an opaque * type whose members cannot be directly accessed. */ typedef struct _ClutterRootNode ClutterRootNode; typedef struct _ClutterPaintNodeClass ClutterRootNodeClass; CLUTTER_EXPORT GType clutter_root_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_root_node_new (CoglFramebuffer *framebuffer, const ClutterColor *clear_color, CoglBufferBit clear_flags); #define CLUTTER_TYPE_LAYER_NODE (clutter_layer_node_get_type ()) #define CLUTTER_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYER_NODE, ClutterLayerNode)) #define CLUTTER_IS_LAYER_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYER_NODE)) /* * ClutterLayerNode: * * The #ClutterLayerNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterLayerNode ClutterLayerNode; typedef struct _ClutterLayerNodeClass ClutterLayerNodeClass; CLUTTER_EXPORT GType clutter_layer_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_layer_node_new (const CoglMatrix *projection, const cairo_rectangle_t *viewport, float width, float height, guint8 opacity); #define CLUTTER_TYPE_TRANSFORM_NODE (clutter_transform_node_get_type ()) #define CLUTTER_TRANSFORM_NODE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TRANSFORM_NODE, ClutterTransformNode)) #define CLUTTER_IS_TRANSFORM_NODE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TRANSFORM_NODE)) /* * ClutterTransformNode: * * The #ClutterLayerNode structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ typedef struct _ClutterTransformNode ClutterTransformNode; typedef struct _ClutterPaintNodeClass ClutterTransformNodeClass; CLUTTER_EXPORT GType clutter_transform_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintNode * clutter_transform_node_new (const CoglMatrix *projection); G_END_DECLS #endif /* __CLUTTER_PAINT_NODES_H__ */ muffin-6.4.1/clutter/clutter/clutter-feature.c0000664000175000017500000000676414723361714020402 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-feature * @short_description: Run-time detection of Clutter features * * Parts of Clutter depend on the underlying platform, including the * capabilities of the backend used and the OpenGL features exposed through the * Clutter and COGL API. * * It is possible to ask whether Clutter has support for specific features at * run-time. */ #include "clutter-build-config.h" #include #include #include #include "clutter-backend-private.h" #include "clutter-feature.h" #include "clutter-main.h" #include "clutter-private.h" #include "clutter-debug.h" #include "cogl/cogl.h" typedef struct ClutterFeatures { ClutterFeatureFlags flags; guint features_set : 1; } ClutterFeatures; static ClutterFeatures* __features = NULL; static ClutterFeatureFlags clutter_features_from_cogl (void) { ClutterFeatureFlags clutter_flags = 0; clutter_flags |= CLUTTER_FEATURE_SHADERS_GLSL; clutter_flags |= CLUTTER_FEATURE_OFFSCREEN; return clutter_flags; } gboolean _clutter_feature_init (GError **error) { ClutterMainContext *context; CLUTTER_NOTE (MISC, "checking features"); if (!__features) { CLUTTER_NOTE (MISC, "allocating features data"); __features = g_new0 (ClutterFeatures, 1); __features->features_set = FALSE; /* don't rely on zero-ing */ } if (__features->features_set) return TRUE; context = _clutter_context_get_default (); /* makes sure we have a GL context; if we have, this is a no-op */ if (!_clutter_backend_create_context (context->backend, error)) return FALSE; __features->flags = (clutter_features_from_cogl () | _clutter_backend_get_features (context->backend)); __features->features_set = TRUE; CLUTTER_NOTE (MISC, "features checked"); return TRUE; } /** * clutter_feature_available: * @feature: a #ClutterFeatureFlags * * Checks whether @feature is available. @feature can be a logical * OR of #ClutterFeatureFlags. * * Return value: %TRUE if a feature is available * * Since: 0.2 */ gboolean clutter_feature_available (ClutterFeatureFlags feature) { if (G_UNLIKELY (!__features)) { g_critical ("Unable to check features. Have you initialized Clutter?"); return FALSE; } return (__features->flags & feature); } /** * clutter_feature_get_all: * * Returns all the supported features. * * Return value: a logical OR of all the supported features. * * Since: 0.2 */ ClutterFeatureFlags clutter_feature_get_all (void) { if (G_UNLIKELY (!__features)) { g_critical ("Unable to check features. Have you initialized Clutter?"); return FALSE; } return __features->flags; } muffin-6.4.1/clutter/clutter/clutter-mutter.h0000664000175000017500000000652314723361714020265 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #ifndef __CLUTTER_MUTTER_H__ #define __CLUTTER_MUTTER_H__ #define __CLUTTER_H_INSIDE__ #include "clutter-backend.h" #include "clutter-event-private.h" #include "clutter-input-device-private.h" #include "clutter-input-pointer-a11y-private.h" #include "clutter-macros.h" #include "clutter-paint-context-private.h" #include "clutter-private.h" #include "clutter-stage-private.h" #include "clutter-stage-view.h" #include "cogl/clutter-stage-cogl.h" #include "clutter/x11/clutter-backend-x11.h" CLUTTER_EXPORT void clutter_set_custom_backend_func (ClutterBackend *(* func) (void)); CLUTTER_EXPORT int64_t clutter_stage_get_frame_counter (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_capture_into (ClutterStage *stage, gboolean paint, cairo_rectangle_int_t *rect, uint8_t *data); CLUTTER_EXPORT void clutter_stage_paint_to_framebuffer (ClutterStage *stage, CoglFramebuffer *framebuffer, const cairo_rectangle_int_t *rect, float scale, ClutterPaintFlag paint_flags); CLUTTER_EXPORT gboolean clutter_stage_paint_to_buffer (ClutterStage *stage, const cairo_rectangle_int_t *rect, float scale, uint8_t *data, int stride, CoglPixelFormat format, ClutterPaintFlag paint_flags, GError **error); CLUTTER_EXPORT void clutter_stage_freeze_updates (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_thaw_updates (ClutterStage *stage); CLUTTER_EXPORT void clutter_stage_update_resource_scales (ClutterStage *stage); CLUTTER_EXPORT gboolean clutter_actor_has_damage (ClutterActor *actor); CLUTTER_EXPORT void clutter_stage_get_device_coords (ClutterStage *stage, ClutterInputDevice *device, ClutterEventSequence *sequence, graphene_point_t *coords); #undef __CLUTTER_H_INSIDE__ #endif /* __CLUTTER_MUTTER_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-focus-private.h0000664000175000017500000000333314723361714022665 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef __CLUTTER_INPUT_FOCUS_PRIVATE_H__ #define __CLUTTER_INPUT_FOCUS_PRIVATE_H__ void clutter_input_focus_focus_in (ClutterInputFocus *focus, ClutterInputMethod *method); void clutter_input_focus_focus_out (ClutterInputFocus *focus); void clutter_input_focus_commit (ClutterInputFocus *focus, const gchar *text); void clutter_input_focus_delete_surrounding (ClutterInputFocus *focus, int offset, guint len); void clutter_input_focus_request_surrounding (ClutterInputFocus *focus); void clutter_input_focus_set_preedit_text (ClutterInputFocus *focus, const gchar *preedit, guint cursor); #endif /* __CLUTTER_INPUT_FOCUS_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-text-buffer.h0000664000175000017500000001601514723361714021175 0ustar fabiofabio/* clutter-text-buffer.h * Copyright (C) 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Stef Walter */ #ifndef __CLUTTER_TEXT_BUFFER_H__ #define __CLUTTER_TEXT_BUFFER_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_TEXT_BUFFER (clutter_text_buffer_get_type ()) #define CLUTTER_TEXT_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TEXT_BUFFER, ClutterTextBuffer)) #define CLUTTER_TEXT_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TEXT_BUFFER, ClutterTextBufferClass)) #define CLUTTER_IS_TEXT_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TEXT_BUFFER)) #define CLUTTER_IS_TEXT_BUFFER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TEXT_BUFFER)) #define CLUTTER_TEXT_BUFFER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TEXT_BUFFER, ClutterTextBufferClass)) /** * CLUTTER_TEXT_BUFFER_MAX_SIZE: * * Maximum size of text buffer, in bytes. * * Since: 1.10 */ #define CLUTTER_TEXT_BUFFER_MAX_SIZE G_MAXUSHORT typedef struct _ClutterTextBuffer ClutterTextBuffer; typedef struct _ClutterTextBufferClass ClutterTextBufferClass; typedef struct _ClutterTextBufferPrivate ClutterTextBufferPrivate; /** * ClutterTextBuffer: * * The #ClutterTextBuffer structure contains private * data and it should only be accessed using the provided API. * * Since: 1.10 */ struct _ClutterTextBuffer { /*< private >*/ GObject parent_instance; ClutterTextBufferPrivate *priv; }; /** * ClutterTextBufferClass: * @inserted_text: default handler for the #ClutterTextBuffer::inserted-text signal * @deleted_text: default hanlder for the #ClutterTextBuffer::deleted-text signal * @get_text: virtual function * @get_length: virtual function * @insert_text: virtual function * @delete_text: virtual function * * The #ClutterTextBufferClass structure contains * only private data. * * Since: 1.10 */ struct _ClutterTextBufferClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ /* Signals */ void (*inserted_text) (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars); void (*deleted_text) (ClutterTextBuffer *buffer, guint position, guint n_chars); /* Virtual Methods */ const gchar* (*get_text) (ClutterTextBuffer *buffer, gsize *n_bytes); guint (*get_length) (ClutterTextBuffer *buffer); guint (*insert_text) (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars); guint (*delete_text) (ClutterTextBuffer *buffer, guint position, guint n_chars); /*< private >*/ /* Padding for future expansion */ void (*_clutter_reserved1) (void); void (*_clutter_reserved2) (void); void (*_clutter_reserved3) (void); void (*_clutter_reserved4) (void); void (*_clutter_reserved5) (void); void (*_clutter_reserved6) (void); void (*_clutter_reserved7) (void); void (*_clutter_reserved8) (void); }; CLUTTER_EXPORT GType clutter_text_buffer_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterTextBuffer* clutter_text_buffer_new (void); CLUTTER_EXPORT ClutterTextBuffer* clutter_text_buffer_new_with_text (const gchar *text, gssize text_len); CLUTTER_EXPORT gsize clutter_text_buffer_get_bytes (ClutterTextBuffer *buffer); CLUTTER_EXPORT guint clutter_text_buffer_get_length (ClutterTextBuffer *buffer); CLUTTER_EXPORT const gchar* clutter_text_buffer_get_text (ClutterTextBuffer *buffer); CLUTTER_EXPORT void clutter_text_buffer_set_text (ClutterTextBuffer *buffer, const gchar *chars, gint n_chars); CLUTTER_EXPORT void clutter_text_buffer_set_max_length (ClutterTextBuffer *buffer, gint max_length); CLUTTER_EXPORT gint clutter_text_buffer_get_max_length (ClutterTextBuffer *buffer); CLUTTER_EXPORT guint clutter_text_buffer_insert_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, gint n_chars); CLUTTER_EXPORT guint clutter_text_buffer_delete_text (ClutterTextBuffer *buffer, guint position, gint n_chars); CLUTTER_EXPORT void clutter_text_buffer_emit_inserted_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars); CLUTTER_EXPORT void clutter_text_buffer_emit_deleted_text (ClutterTextBuffer *buffer, guint position, guint n_chars); G_END_DECLS #endif /* __CLUTTER_TEXT_BUFFER_H__ */ muffin-6.4.1/clutter/clutter/clutter-drop-action.h0000664000175000017500000000762114723361714021164 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_DROP_ACTION_H__ #define __CLUTTER_DROP_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be directly included." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_DROP_ACTION (clutter_drop_action_get_type ()) #define CLUTTER_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropAction)) #define CLUTTER_IS_DROP_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DROP_ACTION)) #define CLUTTER_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass)) #define CLUTTER_IS_DROP_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DROP_ACTION)) #define CLUTTER_DROP_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DROP_ACTION, ClutterDropActionClass)) typedef struct _ClutterDropAction ClutterDropAction; typedef struct _ClutterDropActionPrivate ClutterDropActionPrivate; typedef struct _ClutterDropActionClass ClutterDropActionClass; /** * ClutterDropAction: * * The #ClutterDropAction structure contains only * private data and should be accessed using the provided API. * * Since: 1.8 */ struct _ClutterDropAction { /*< private >*/ ClutterAction parent_instance; ClutterDropActionPrivate *priv; }; /** * ClutterDropActionClass: * @can_drop: class handler for the #ClutterDropAction::can-drop signal * @over_in: class handler for the #ClutterDropAction::over-in signal * @over_out: class handler for the #ClutterDropAction::over-out signal * @drop: class handler for the #ClutterDropAction::drop signal * * The #ClutterDropActionClass structure contains * only private data. * * Since: 1.8 */ struct _ClutterDropActionClass { /*< private >*/ ClutterActionClass parent_class; /*< public >*/ gboolean (* can_drop) (ClutterDropAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y); void (* over_in) (ClutterDropAction *action, ClutterActor *actor); void (* over_out) (ClutterDropAction *action, ClutterActor *actor); void (* drop) (ClutterDropAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y); /*< private >*/ void (*_clutter_drop_action1) (void); void (*_clutter_drop_action2) (void); void (*_clutter_drop_action3) (void); void (*_clutter_drop_action4) (void); void (*_clutter_drop_action5) (void); void (*_clutter_drop_action6) (void); void (*_clutter_drop_action7) (void); void (*_clutter_drop_action8) (void); }; CLUTTER_EXPORT GType clutter_drop_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_drop_action_new (void); G_END_DECLS #endif /* __CLUTTER_DROP_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-master-clock-default.h0000664000175000017500000000354114723361714022750 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Lionel Landwerlin * * Copyright (C) 2015 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_MASTER_CLOCK_DEFAULT_H__ #define __CLUTTER_MASTER_CLOCK_DEFAULT_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_MASTER_CLOCK_DEFAULT (_clutter_master_clock_default_get_type ()) #define CLUTTER_MASTER_CLOCK_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, ClutterMasterClockDefault)) #define CLUTTER_IS_MASTER_CLOCK_DEFAULT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT)) #define CLUTTER_MASTER_CLOCK_DEFAULT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, ClutterMasterClockDefaultClass)) typedef struct _ClutterMasterClockDefault ClutterMasterClockDefault; typedef struct _ClutterMasterClockDefaultClass ClutterMasterClockDefaultClass; struct _ClutterMasterClockDefaultClass { GObjectClass parent_class; }; GType _clutter_master_clock_default_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __CLUTTER_MASTER_CLOCK_DEFAULT_H__ */ muffin-6.4.1/clutter/clutter/clutter-actor-meta.h0000664000175000017500000000746614723361714021010 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_ACTOR_META_H__ #define __CLUTTER_ACTOR_META_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_ACTOR_META (clutter_actor_meta_get_type ()) #define CLUTTER_ACTOR_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ACTOR_META, ClutterActorMeta)) #define CLUTTER_IS_ACTOR_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ACTOR_META)) #define CLUTTER_ACTOR_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ACTOR_META, ClutterActorMetaClass)) #define CLUTTER_IS_ACTOR_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ACTOR_META)) #define CLUTTER_ACTOR_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ACTOR_META, ClutterActorMetaClass)) typedef struct _ClutterActorMetaPrivate ClutterActorMetaPrivate; typedef struct _ClutterActorMetaClass ClutterActorMetaClass; /** * ClutterActorMeta: * * The #ClutterActorMeta structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterActorMeta { /*< private >*/ GInitiallyUnowned parent_instance; ClutterActorMetaPrivate *priv; }; /** * ClutterActorMetaClass: * @set_actor: virtual function, invoked when attaching and detaching * a #ClutterActorMeta instance to a #ClutterActor * * The #ClutterActorMetaClass structure contains * only private data * * Since: 1.4 */ struct _ClutterActorMetaClass { /*< private >*/ GInitiallyUnownedClass parent_class; /*< public >*/ /** * ClutterActorMetaClass::set_actor: * @meta: a #ClutterActorMeta * @actor: (allow-none): the actor attached to @meta, or %NULL * * Virtual function, called when @meta is attached or detached * from a #ClutterActor. */ void (* set_actor) (ClutterActorMeta *meta, ClutterActor *actor); /*< private >*/ void (* _clutter_meta1) (void); void (* _clutter_meta2) (void); void (* _clutter_meta3) (void); void (* _clutter_meta4) (void); void (* _clutter_meta5) (void); void (* _clutter_meta6) (void); void (* _clutter_meta7) (void); }; CLUTTER_EXPORT GType clutter_actor_meta_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_actor_meta_set_name (ClutterActorMeta *meta, const gchar *name); CLUTTER_EXPORT const gchar * clutter_actor_meta_get_name (ClutterActorMeta *meta); CLUTTER_EXPORT void clutter_actor_meta_set_enabled (ClutterActorMeta *meta, gboolean is_enabled); CLUTTER_EXPORT gboolean clutter_actor_meta_get_enabled (ClutterActorMeta *meta); CLUTTER_EXPORT ClutterActor * clutter_actor_meta_get_actor (ClutterActorMeta *meta); G_END_DECLS #endif /* __CLUTTER_ACTOR_META_H__ */ muffin-6.4.1/clutter/clutter/clutter-base-types.c0000664000175000017500000001742014723361714021012 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-geometric-types * @Title: Base geometric types * @Short_Description: Common geometric data types used by Clutter * * Clutter defines a set of geometric data structures that are commonly used * across the whole API. */ #include "clutter-build-config.h" #include "clutter-types.h" #include "clutter-private.h" #include #define FLOAT_EPSILON (1e-15) /* * ClutterMargin */ /** * clutter_margin_new: * * Creates a new #ClutterMargin. * * Return value: (transfer full): a newly allocated #ClutterMargin. Use * clutter_margin_free() to free the resources associated with it when * done. * * Since: 1.10 */ ClutterMargin * clutter_margin_new (void) { return g_slice_new0 (ClutterMargin); } /** * clutter_margin_copy: * @margin_: a #ClutterMargin * * Creates a new #ClutterMargin and copies the contents of @margin_ into * the newly created structure. * * Return value: (transfer full): a copy of the #ClutterMargin. * * Since: 1.10 */ ClutterMargin * clutter_margin_copy (const ClutterMargin *margin_) { if (G_LIKELY (margin_ != NULL)) return g_slice_dup (ClutterMargin, margin_); return NULL; } /** * clutter_margin_free: * @margin_: a #ClutterMargin * * Frees the resources allocated by clutter_margin_new() and * clutter_margin_copy(). * * Since: 1.10 */ void clutter_margin_free (ClutterMargin *margin_) { if (G_LIKELY (margin_ != NULL)) g_slice_free (ClutterMargin, margin_); } G_DEFINE_BOXED_TYPE (ClutterMargin, clutter_margin, clutter_margin_copy, clutter_margin_free) /** * ClutterMatrix: * * A type representing a 4x4 matrix. * * It is identicaly to #CoglMatrix. * * Since: 1.12 */ static gpointer clutter_matrix_copy (gpointer data) { return cogl_matrix_copy (data); } static gboolean clutter_matrix_progress (const GValue *a, const GValue *b, gdouble progress, GValue *retval) { const ClutterMatrix *matrix1 = g_value_get_boxed (a); const ClutterMatrix *matrix2 = g_value_get_boxed (b); graphene_point3d_t scale1 = GRAPHENE_POINT3D_INIT (1.f, 1.f, 1.f); float shear1[3] = { 0.f, 0.f, 0.f }; graphene_point3d_t rotate1 = GRAPHENE_POINT3D_INIT_ZERO; graphene_point3d_t translate1 = GRAPHENE_POINT3D_INIT_ZERO; ClutterVertex4 perspective1 = { 0.f, 0.f, 0.f, 0.f }; graphene_point3d_t scale2 = GRAPHENE_POINT3D_INIT (1.f, 1.f, 1.f); float shear2[3] = { 0.f, 0.f, 0.f }; graphene_point3d_t rotate2 = GRAPHENE_POINT3D_INIT_ZERO; graphene_point3d_t translate2 = GRAPHENE_POINT3D_INIT_ZERO; ClutterVertex4 perspective2 = { 0.f, 0.f, 0.f, 0.f }; graphene_point3d_t scale_res = GRAPHENE_POINT3D_INIT (1.f, 1.f, 1.f); float shear_res = 0.f; graphene_point3d_t rotate_res = GRAPHENE_POINT3D_INIT_ZERO; graphene_point3d_t translate_res = GRAPHENE_POINT3D_INIT_ZERO; ClutterVertex4 perspective_res = { 0.f, 0.f, 0.f, 0.f }; ClutterMatrix res; clutter_matrix_init_identity (&res); _clutter_util_matrix_decompose (matrix1, &scale1, shear1, &rotate1, &translate1, &perspective1); _clutter_util_matrix_decompose (matrix2, &scale2, shear2, &rotate2, &translate2, &perspective2); /* perspective */ _clutter_util_vertex4_interpolate (&perspective1, &perspective2, progress, &perspective_res); res.wx = perspective_res.x; res.wy = perspective_res.y; res.wz = perspective_res.z; res.ww = perspective_res.w; /* translation */ graphene_point3d_interpolate (&translate1, &translate2, progress, &translate_res); cogl_matrix_translate (&res, translate_res.x, translate_res.y, translate_res.z); /* rotation */ graphene_point3d_interpolate (&rotate1, &rotate2, progress, &rotate_res); cogl_matrix_rotate (&res, rotate_res.x, 1.0f, 0.0f, 0.0f); cogl_matrix_rotate (&res, rotate_res.y, 0.0f, 1.0f, 0.0f); cogl_matrix_rotate (&res, rotate_res.z, 0.0f, 0.0f, 1.0f); /* skew */ shear_res = shear1[2] + (shear2[2] - shear1[2]) * progress; /* YZ */ if (shear_res != 0.f) _clutter_util_matrix_skew_yz (&res, shear_res); shear_res = shear1[1] + (shear2[1] - shear1[1]) * progress; /* XZ */ if (shear_res != 0.f) _clutter_util_matrix_skew_xz (&res, shear_res); shear_res = shear1[0] + (shear2[0] - shear1[0]) * progress; /* XY */ if (shear_res != 0.f) _clutter_util_matrix_skew_xy (&res, shear_res); /* scale */ graphene_point3d_interpolate (&scale1, &scale2, progress, &scale_res); cogl_matrix_scale (&res, scale_res.x, scale_res.y, scale_res.z); g_value_set_boxed (retval, &res); return TRUE; } G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterMatrix, clutter_matrix, clutter_matrix_copy, clutter_matrix_free, CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_matrix_progress)) /** * clutter_matrix_alloc: * * Allocates enough memory to hold a #ClutterMatrix. * * Return value: (transfer full): the newly allocated #ClutterMatrix * * Since: 1.12 */ ClutterMatrix * clutter_matrix_alloc (void) { return g_new0 (ClutterMatrix, 1); } /** * clutter_matrix_free: * @matrix: (allow-none): a #ClutterMatrix * * Frees the memory allocated by clutter_matrix_alloc(). * * Since: 1.12 */ void clutter_matrix_free (ClutterMatrix *matrix) { cogl_matrix_free (matrix); } /** * clutter_matrix_init_identity: * @matrix: a #ClutterMatrix * * Initializes @matrix with the identity matrix, i.e.: * * |[ * .xx = 1.0, .xy = 0.0, .xz = 0.0, .xw = 0.0 * .yx = 0.0, .yy = 1.0, .yz = 0.0, .yw = 0.0 * .zx = 0.0, .zy = 0.0, .zz = 1.0, .zw = 0.0 * .wx = 0.0, .wy = 0.0, .wz = 0.0, .ww = 1.0 * ]| * * Return value: (transfer none): the initialized #ClutterMatrix * * Since: 1.12 */ ClutterMatrix * clutter_matrix_init_identity (ClutterMatrix *matrix) { cogl_matrix_init_identity (matrix); return matrix; } /** * clutter_matrix_init_from_array: * @matrix: a #ClutterMatrix * @values: (array fixed-size=16): a C array of 16 floating point values, * representing a 4x4 matrix, with column-major order * * Initializes @matrix with the contents of a C array of floating point * values. * * Return value: (transfer none): the initialzed #ClutterMatrix * * Since: 1.12 */ ClutterMatrix * clutter_matrix_init_from_array (ClutterMatrix *matrix, const float values[16]) { cogl_matrix_init_from_array (matrix, values); return matrix; } /** * clutter_matrix_init_from_matrix: * @a: the #ClutterMatrix to initialize * @b: the #ClutterMatrix to copy * * Initializes the #ClutterMatrix @a with the contents of the * #ClutterMatrix @b. * * Return value: (transfer none): the initialized #ClutterMatrix * * Since: 1.12 */ ClutterMatrix * clutter_matrix_init_from_matrix (ClutterMatrix *a, const ClutterMatrix *b) { return memcpy (a, b, sizeof (ClutterMatrix)); } muffin-6.4.1/clutter/clutter/clutter-texture-content.h0000664000175000017500000000336214723361714022113 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive image' library. * * Copyright (C) 2012 Intel Corporation. * Copyright (C) 2021 Robert Mader. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * Robert Mader */ #ifndef CLUTTER_TEXTURE_CONTENT_H #define CLUTTER_TEXTURE_CONTENT_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_TEXTURE_CONTENT (clutter_texture_content_get_type ()) CLUTTER_EXPORT G_DECLARE_FINAL_TYPE (ClutterTextureContent, clutter_texture_content, CLUTTER, TEXTURE_CONTENT, GObject) CLUTTER_EXPORT ClutterContent * clutter_texture_content_new_from_texture (CoglTexture *texture, cairo_rectangle_int_t *clip); CLUTTER_EXPORT CoglTexture * clutter_texture_content_get_texture (ClutterTextureContent *texture_content); G_END_DECLS #endif /* CLUTTER_TEXTURE_CONTENT_H */ muffin-6.4.1/clutter/clutter/clutter-clone.h0000664000175000017500000000561714723361714020050 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Robert Bragg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_CLONE_H__ #define __CLUTTER_CLONE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_CLONE (clutter_clone_get_type()) #define CLUTTER_CLONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CLONE, ClutterClone)) #define CLUTTER_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CLONE, ClutterCloneClass)) #define CLUTTER_IS_CLONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CLONE)) #define CLUTTER_IS_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CLONE)) #define CLUTTER_CLONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CLONE, ClutterCloneClass)) typedef struct _ClutterClone ClutterClone; typedef struct _ClutterCloneClass ClutterCloneClass; typedef struct _ClutterClonePrivate ClutterClonePrivate; /** * ClutterClone: * * The #ClutterClone structure contains only private data * and should be accessed using the provided API * * Since: 1.0 */ struct _ClutterClone { /*< private >*/ ClutterActor parent_instance; ClutterClonePrivate *priv; }; /** * ClutterCloneClass: * * The #ClutterCloneClass structure contains only private data * * Since: 1.0 */ struct _ClutterCloneClass { /*< private >*/ ClutterActorClass parent_class; /* padding for future expansion */ void (*_clutter_actor_clone1) (void); void (*_clutter_actor_clone2) (void); void (*_clutter_actor_clone3) (void); void (*_clutter_actor_clone4) (void); }; CLUTTER_EXPORT GType clutter_clone_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_clone_new (ClutterActor *source); CLUTTER_EXPORT void clutter_clone_set_source (ClutterClone *self, ClutterActor *source); CLUTTER_EXPORT ClutterActor * clutter_clone_get_source (ClutterClone *self); G_END_DECLS #endif /* __CLUTTER_CLONE_H__ */ muffin-6.4.1/clutter/clutter/clutter-layout-meta.h0000664000175000017500000000620214723361714021200 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_LAYOUT_META_H__ #define __CLUTTER_LAYOUT_META_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_LAYOUT_META (clutter_layout_meta_get_type ()) #define CLUTTER_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMeta)) #define CLUTTER_IS_LAYOUT_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_LAYOUT_META)) #define CLUTTER_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass)) #define CLUTTER_IS_LAYOUT_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_LAYOUT_META)) #define CLUTTER_LAYOUT_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_LAYOUT_META, ClutterLayoutMetaClass)) /* ClutterLayoutMeta is defined in clutter-types.h */ typedef struct _ClutterLayoutMetaClass ClutterLayoutMetaClass; /** * ClutterLayoutMeta: * @manager: the layout manager handling this data * * Sub-class of #ClutterChildMeta specific for layout managers * * A #ClutterLayoutManager sub-class should create a #ClutterLayoutMeta * instance by overriding the #ClutterLayoutManager::create_child_meta() * virtual function * * Since: 1.2 */ struct _ClutterLayoutMeta { /*< private >*/ ClutterChildMeta parent_instance; /*< public >*/ ClutterLayoutManager *manager; /*< private >*/ /* padding */ gint32 dummy0; gpointer dummy1; }; /** * ClutterLayoutMetaClass: * * The #ClutterLayoutMetaClass contains only private data and * should never be accessed directly * * Since: 1.2 */ struct _ClutterLayoutMetaClass { /*< private >*/ ClutterChildMetaClass parent_class; /* padding, for expansion */ void (*_clutter_padding1) (void); void (*_clutter_padding2) (void); void (*_clutter_padding3) (void); void (*_clutter_padding4) (void); }; CLUTTER_EXPORT GType clutter_layout_meta_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager *clutter_layout_meta_get_manager (ClutterLayoutMeta *data); G_END_DECLS #endif /* __CLUTTER_LAYOUT_META_H__ */ muffin-6.4.1/clutter/clutter/clutter-canvas.h0000664000175000017500000000652314723361714020220 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_CANVAS_H__ #define __CLUTTER_CANVAS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_CANVAS (clutter_canvas_get_type ()) #define CLUTTER_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CANVAS, ClutterCanvas)) #define CLUTTER_IS_CANVAS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CANVAS)) #define CLUTTER_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CANVAS, ClutterCanvasClass)) #define CLUTTER_IS_CANVAS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CANVAS)) #define CLUTTER_CANVAS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CANVAS, ClutterCanvasClass)) typedef struct _ClutterCanvas ClutterCanvas; typedef struct _ClutterCanvasPrivate ClutterCanvasPrivate; typedef struct _ClutterCanvasClass ClutterCanvasClass; /** * ClutterCanvas: * * The #ClutterCanvas structure contains * private data and should only be accessed using the provided * API. * * Since: 1.10 */ struct _ClutterCanvas { /*< private >*/ GObject parent_instance; ClutterCanvasPrivate *priv; }; /** * ClutterCanvasClass: * @draw: class handler for the #ClutterCanvas::draw signal * * The #ClutterCanvasClass structure contains * private data. * * Since: 1.10 */ struct _ClutterCanvasClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ gboolean (* draw) (ClutterCanvas *canvas, cairo_t *cr, int width, int height); /*< private >*/ gpointer _padding[16]; }; CLUTTER_EXPORT GType clutter_canvas_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterContent * clutter_canvas_new (void); CLUTTER_EXPORT gboolean clutter_canvas_set_size (ClutterCanvas *canvas, int width, int height); CLUTTER_EXPORT void clutter_canvas_set_scale_factor (ClutterCanvas *canvas, float scale); CLUTTER_EXPORT float clutter_canvas_get_scale_factor (ClutterCanvas *canvas); G_END_DECLS #endif /* __CLUTTER_CANVAS_H__ */ muffin-6.4.1/clutter/clutter/cally/0000775000175000017500000000000014723361714016212 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/cally/cally-actor-private.h0000664000175000017500000000303214723361714022243 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Some parts are based on GailWidget from GAIL * GAIL - The GNOME Accessibility Implementation Library * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_ACTOR_PRIVATE_H__ #define __CALLY_ACTOR_PRIVATE_H__ #include "cally-actor.h" /* * Auxiliar define, in order to get the clutter actor from the AtkObject using * AtkGObject methods * */ #define CALLY_GET_CLUTTER_ACTOR(cally_object) \ (CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (cally_object)))) void _cally_actor_get_top_level_origin (ClutterActor *actor, gint *x, gint *y); #endif /* __CALLY_ACTOR_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-clone.c0000664000175000017500000001036414723361714020564 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2010 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-clone * @Title: CallyClone * @short_description: Implementation of the ATK interfaces for a #ClutterClone * @see_also: #ClutterClone * * #CallyClone implements the required ATK interfaces of #ClutterClone * * In particular it sets a proper role for the clone, as just a image, * as it is the sanest and simplest approach. */ /* Design rationale for CallyClone: * * In the old times, it was just ClutterCloneTexture. So, from a a11y POV * CallyCloneTexture was just another image, like ClutterTexture, and if * it was a clone was irrevelant. So on cally-0.8, CallyCloneTexture * expose a object with role ATK_ROLE_IMAGE. But now, ClutterClone is more * general. You can clone any object, including groups, and made things * like have one text entry, and a clone with different properties in the * same window, updated both at once. * * The question is if the idea is have a ClutterClone as a "first-class" * citizen inside the stage hierarchy (full clone), or it is just supposed * to be a mirror image of the original object. * * In the case of the a11y POV this would mean that if the text changes on * the source, the clone should emit as well the text-changing signals. * * As ClutterClone smartly just paint the same object with different * parameters, this would mean that it should be the cally object the one * that should replicate the source clutter hierarchy to do that, * something that just sound crazy. * * Taking into account that: * * - ClutterClone doesn't re-emit mirrored signals from the source * I think that likely the answer would be "yes, it is just a * mirrored image, not a real full clone". * * - You can't interact directly with the clone (ie: focus, and so on). * Its basic usage (right now) is clone textures. * * Any other solution could be overwhelming. * * I think that the final solution would be that ClutterClone from the * a11y POV should still be managed as a image (with the proper properties, * position, size, etc.). */ #include "clutter-build-config.h" #include "cally-clone.h" #include "cally-actor-private.h" /* AtkObject */ static void cally_clone_real_initialize (AtkObject *obj, gpointer data); G_DEFINE_TYPE (CallyClone, cally_clone, CALLY_TYPE_ACTOR) static void cally_clone_class_init (CallyCloneClass *klass) { /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ AtkObjectClass *class = ATK_OBJECT_CLASS (klass); class->initialize = cally_clone_real_initialize; } static void cally_clone_init (CallyClone *clone) { /* nothing to do yet */ } /** * cally_clone_new: * @actor: a #ClutterActor * * Creates a new #CallyClone for the given @actor. @actor must be a * #ClutterClone. * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject* cally_clone_new (ClutterActor *actor) { GObject *object = NULL; AtkObject *accessible = NULL; g_return_val_if_fail (CLUTTER_IS_CLONE (actor), NULL); object = g_object_new (CALLY_TYPE_CLONE, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, actor); return accessible; } static void cally_clone_real_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (cally_clone_parent_class)->initialize (obj, data); obj->role = ATK_ROLE_IMAGE; } muffin-6.4.1/clutter/clutter/cally/cally-actor.c0000664000175000017500000010502114723361714020567 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Some parts are based on GailWidget from GAIL * GAIL - The GNOME Accessibility Implementation Library * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:cally-actor * @Title: CallyActor * @short_description: Implementation of the ATK interfaces for #ClutterActor * @see_also: #ClutterActor * * #CallyActor implements the required ATK interfaces of #ClutterActor * exposing the common elements on each actor (position, extents, etc). */ /* * * IMPLEMENTATION NOTES: * * #### * * Focus: clutter hasn't got the focus concept in the same way that GTK, but it * has a key focus managed by the stage. Basically any actor can be focused using * clutter_stage_set_key_focus. So, we will use this approach: all actors are * focusable, and we get the currently focused using clutter_stage_get_key_focus * This affects focus related stateset and some atk_componenet focus methods (like * grab focus). * * In the same way, we will manage the focus state change management * on the cally-stage object. The reason is avoid missing a focus * state change event if the object is focused just before the * accessibility object being created. * * #AtkAction implementation: on previous releases ClutterActor added * the actions "press", "release" and "click", as at that time some * general-purpose actors like textures were directly used as buttons. * * But now, new toolkits appeared, providing high-level widgets, like * buttons. So in this environment, it doesn't make sense to keep * adding them as default. * * Anyway, current implementation of AtkAction is done at CallyActor * providing methods to add and remove actions. This is based on the * one used at gailcell, and proposed as a change on #AtkAction * interface: * * https://bugzilla.gnome.org/show_bug.cgi?id=649804 * */ #include "clutter-build-config.h" #include #include #ifdef CLUTTER_WINDOWING_X11 #include #endif #include #include "cally-actor.h" #include "cally-actor-private.h" typedef struct _CallyActorActionInfo CallyActorActionInfo; /*< private > * CallyActorActionInfo: * @name: name of the action * @description: description of the action * @keybinding: keybinding related to the action * @do_action_func: callback * @user_data: data to be passed to @do_action_func * @notify: function to be called when removing the action * * Utility structure to maintain the different actions added to the * #CallyActor */ struct _CallyActorActionInfo { gchar *name; gchar *description; gchar *keybinding; CallyActionCallback do_action_func; gpointer user_data; GDestroyNotify notify; }; static void cally_actor_initialize (AtkObject *obj, gpointer data); static void cally_actor_finalize (GObject *obj); /* AtkObject.h */ static AtkObject* cally_actor_get_parent (AtkObject *obj); static gint cally_actor_get_index_in_parent (AtkObject *obj); static AtkStateSet* cally_actor_ref_state_set (AtkObject *obj); static gint cally_actor_get_n_children (AtkObject *obj); static AtkObject* cally_actor_ref_child (AtkObject *obj, gint i); static AtkAttributeSet * cally_actor_get_attributes (AtkObject *obj); /* ClutterContainer */ static gint cally_actor_add_actor (ClutterActor *container, ClutterActor *actor, gpointer data); static gint cally_actor_remove_actor (ClutterActor *container, ClutterActor *actor, gpointer data); static gint cally_actor_real_add_actor (ClutterActor *container, ClutterActor *actor, gpointer data); static gint cally_actor_real_remove_actor (ClutterActor *container, ClutterActor *actor, gpointer data); /* AtkComponent.h */ static void cally_actor_component_interface_init (AtkComponentIface *iface); static void cally_actor_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type); static gint cally_actor_get_mdi_zorder (AtkComponent *component); static gboolean cally_actor_grab_focus (AtkComponent *component); /* AtkAction.h */ static void cally_actor_action_interface_init (AtkActionIface *iface); static gboolean cally_actor_action_do_action (AtkAction *action, gint i); static gboolean idle_do_action (gpointer data); static gint cally_actor_action_get_n_actions (AtkAction *action); static const gchar* cally_actor_action_get_description (AtkAction *action, gint i); static const gchar* cally_actor_action_get_keybinding (AtkAction *action, gint i); static const gchar* cally_actor_action_get_name (AtkAction *action, gint i); static gboolean cally_actor_action_set_description (AtkAction *action, gint i, const gchar *desc); static void _cally_actor_destroy_action_info (gpointer action_info, gpointer user_data); static void _cally_actor_clean_action_list (CallyActor *cally_actor); static CallyActorActionInfo* _cally_actor_get_action_info (CallyActor *cally_actor, gint index); /* Misc functions */ static void cally_actor_notify_clutter (GObject *obj, GParamSpec *pspec); static void cally_actor_real_notify_clutter (GObject *obj, GParamSpec *pspec); struct _CallyActorPrivate { GQueue *action_queue; guint action_idle_handler; GList *action_list; GList *children; }; G_DEFINE_TYPE_WITH_CODE (CallyActor, cally_actor, ATK_TYPE_GOBJECT_ACCESSIBLE, G_ADD_PRIVATE (CallyActor) G_IMPLEMENT_INTERFACE (ATK_TYPE_COMPONENT, cally_actor_component_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_ACTION, cally_actor_action_interface_init)); /** * cally_actor_new: * @actor: a #ClutterActor * * Creates a new #CallyActor for the given @actor * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject * cally_actor_new (ClutterActor *actor) { gpointer object; AtkObject *atk_object; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); object = g_object_new (CALLY_TYPE_ACTOR, NULL); atk_object = ATK_OBJECT (object); atk_object_initialize (atk_object, actor); return atk_object; } static void cally_actor_initialize (AtkObject *obj, gpointer data) { CallyActor *self = NULL; CallyActorPrivate *priv = NULL; ClutterActor *actor = NULL; guint handler_id; ATK_OBJECT_CLASS (cally_actor_parent_class)->initialize (obj, data); self = CALLY_ACTOR(obj); priv = self->priv; actor = CLUTTER_ACTOR (data); g_signal_connect (actor, "notify", G_CALLBACK (cally_actor_notify_clutter), NULL); g_object_set_data (G_OBJECT (obj), "atk-component-layer", GINT_TO_POINTER (ATK_LAYER_MDI)); priv->children = clutter_actor_get_children (actor); /* * We store the handler ids for these signals in case some objects * need to remove these handlers. */ handler_id = g_signal_connect (actor, "actor-added", G_CALLBACK (cally_actor_add_actor), obj); g_object_set_data (G_OBJECT (obj), "cally-add-handler-id", GUINT_TO_POINTER (handler_id)); handler_id = g_signal_connect (actor, "actor-removed", G_CALLBACK (cally_actor_remove_actor), obj); g_object_set_data (G_OBJECT (obj), "cally-remove-handler-id", GUINT_TO_POINTER (handler_id)); obj->role = ATK_ROLE_PANEL; /* typically objects implementing ClutterContainer interface would be a panel */ } static void cally_actor_class_init (CallyActorClass *klass) { AtkObjectClass *class = ATK_OBJECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->notify_clutter = cally_actor_real_notify_clutter; klass->add_actor = cally_actor_real_add_actor; klass->remove_actor = cally_actor_real_remove_actor; /* GObject */ gobject_class->finalize = cally_actor_finalize; /* AtkObject */ class->get_parent = cally_actor_get_parent; class->get_index_in_parent = cally_actor_get_index_in_parent; class->ref_state_set = cally_actor_ref_state_set; class->initialize = cally_actor_initialize; class->get_n_children = cally_actor_get_n_children; class->ref_child = cally_actor_ref_child; class->get_attributes = cally_actor_get_attributes; } static void cally_actor_init (CallyActor *cally_actor) { CallyActorPrivate *priv = cally_actor_get_instance_private (cally_actor); cally_actor->priv = priv; priv->action_queue = NULL; priv->action_idle_handler = 0; priv->action_list = NULL; priv->children = NULL; } static void cally_actor_finalize (GObject *obj) { CallyActor *cally_actor = NULL; CallyActorPrivate *priv = NULL; cally_actor = CALLY_ACTOR (obj); priv = cally_actor->priv; _cally_actor_clean_action_list (cally_actor); g_clear_handle_id (&priv->action_idle_handler, g_source_remove); if (priv->action_queue) { g_queue_free (priv->action_queue); } if (priv->children) { g_list_free (priv->children); priv->children = NULL; } G_OBJECT_CLASS (cally_actor_parent_class)->finalize (obj); } /* AtkObject */ static AtkObject * cally_actor_get_parent (AtkObject *obj) { ClutterActor *parent_actor = NULL; AtkObject *parent = NULL; ClutterActor *actor = NULL; CallyActor *cally_actor = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); /* Check if we have and assigned parent */ if (obj->accessible_parent) return obj->accessible_parent; /* Try to get it from the clutter parent */ cally_actor = CALLY_ACTOR (obj); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); if (actor == NULL) /* Object is defunct */ return NULL; parent_actor = clutter_actor_get_parent (actor); if (parent_actor == NULL) return NULL; parent = clutter_actor_get_accessible (parent_actor); /* FIXME: I need to review the clutter-embed, to check if in this case I * should get the widget accessible */ return parent; } static gint cally_actor_get_index_in_parent (AtkObject *obj) { CallyActor *cally_actor = NULL; ClutterActor *actor = NULL; ClutterActor *parent_actor = NULL; ClutterActor *iter; gint index = -1; g_return_val_if_fail (CALLY_IS_ACTOR (obj), -1); if (obj->accessible_parent) { gint n_children, i; gboolean found = FALSE; n_children = atk_object_get_n_accessible_children (obj->accessible_parent); for (i = 0; i < n_children; i++) { AtkObject *child; child = atk_object_ref_accessible_child (obj->accessible_parent, i); if (child == obj) found = TRUE; g_object_unref (child); if (found) return i; } return -1; } cally_actor = CALLY_ACTOR (obj); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); if (actor == NULL) /* Object is defunct */ return -1; index = 0; parent_actor = clutter_actor_get_parent (actor); if (parent_actor == NULL) return -1; for (iter = clutter_actor_get_first_child (parent_actor); iter != NULL && iter != actor; iter = clutter_actor_get_next_sibling (iter)) { index += 1; } return index; } static AtkStateSet* cally_actor_ref_state_set (AtkObject *obj) { ClutterActor *actor = NULL; AtkStateSet *state_set = NULL; ClutterStage *stage = NULL; ClutterActor *focus_actor = NULL; CallyActor *cally_actor = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); cally_actor = CALLY_ACTOR (obj); state_set = ATK_OBJECT_CLASS (cally_actor_parent_class)->ref_state_set (obj); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); if (actor == NULL) /* Object is defunct */ { atk_state_set_add_state (state_set, ATK_STATE_DEFUNCT); } else { if (clutter_actor_get_reactive (actor)) { atk_state_set_add_state (state_set, ATK_STATE_SENSITIVE); atk_state_set_add_state (state_set, ATK_STATE_ENABLED); } if (clutter_actor_is_visible (actor)) { atk_state_set_add_state (state_set, ATK_STATE_VISIBLE); /* It would be good to also check if the actor is on screen, like the old and removed clutter_actor_is_on_stage*/ if (clutter_actor_get_paint_visibility (actor)) atk_state_set_add_state (state_set, ATK_STATE_SHOWING); } /* See focus section on implementation notes */ atk_state_set_add_state (state_set, ATK_STATE_FOCUSABLE); stage = CLUTTER_STAGE (clutter_actor_get_stage (actor)); if (stage != NULL) { focus_actor = clutter_stage_get_key_focus (stage); if (focus_actor == actor) atk_state_set_add_state (state_set, ATK_STATE_FOCUSED); } } return state_set; } static gint cally_actor_get_n_children (AtkObject *obj) { ClutterActor *actor = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (obj), 0); actor = CALLY_GET_CLUTTER_ACTOR (obj); if (actor == NULL) /* State is defunct */ return 0; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); return clutter_actor_get_n_children (actor); } static AtkObject* cally_actor_ref_child (AtkObject *obj, gint i) { ClutterActor *actor = NULL; ClutterActor *child = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (obj), NULL); actor = CALLY_GET_CLUTTER_ACTOR (obj); if (actor == NULL) /* State is defunct */ return NULL; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); if (i >= clutter_actor_get_n_children (actor)) return NULL; child = clutter_actor_get_child_at_index (actor, i); if (child == NULL) return NULL; return g_object_ref (clutter_actor_get_accessible (child)); } static AtkAttributeSet * cally_actor_get_attributes (AtkObject *obj) { AtkAttributeSet *attributes; AtkAttribute *toolkit; toolkit = g_new (AtkAttribute, 1); toolkit->name = g_strdup ("toolkit"); toolkit->value = g_strdup ("clutter"); attributes = g_slist_append (NULL, toolkit); return attributes; } /* ClutterContainer */ static gint cally_actor_add_actor (ClutterActor *container, ClutterActor *actor, gpointer data) { CallyActor *cally_actor = CALLY_ACTOR (data); CallyActorClass *klass = NULL; klass = CALLY_ACTOR_GET_CLASS (cally_actor); if (klass->add_actor) return klass->add_actor (container, actor, data); else return 1; } static gint cally_actor_remove_actor (ClutterActor *container, ClutterActor *actor, gpointer data) { CallyActor *cally_actor = CALLY_ACTOR (data); CallyActorClass *klass = NULL; klass = CALLY_ACTOR_GET_CLASS (cally_actor); if (klass->remove_actor) return klass->remove_actor (container, actor, data); else return 1; } static gint cally_actor_real_add_actor (ClutterActor *container, ClutterActor *actor, gpointer data) { AtkObject *atk_parent = ATK_OBJECT (data); AtkObject *atk_child = clutter_actor_get_accessible (actor); CallyActor *cally_actor = CALLY_ACTOR (atk_parent); CallyActorPrivate *priv = cally_actor->priv; gint index; g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); g_object_notify (G_OBJECT (atk_child), "accessible_parent"); g_list_free (priv->children); priv->children = clutter_actor_get_children (CLUTTER_ACTOR (container)); index = g_list_index (priv->children, actor); g_signal_emit_by_name (atk_parent, "children_changed::add", index, atk_child, NULL); return 1; } static gint cally_actor_real_remove_actor (ClutterActor *container, ClutterActor *actor, gpointer data) { AtkPropertyValues values = { NULL }; AtkObject* atk_parent = NULL; AtkObject *atk_child = NULL; CallyActorPrivate *priv = NULL; gint index; g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), 0); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), 0); atk_parent = ATK_OBJECT (data); if (clutter_actor_has_accessible (actor)) { atk_child = clutter_actor_get_accessible (actor); g_value_init (&values.old_value, G_TYPE_POINTER); g_value_set_pointer (&values.old_value, atk_parent); values.property_name = "accessible-parent"; g_object_ref (atk_child); g_signal_emit_by_name (atk_child, "property_change::accessible-parent", &values, NULL); g_object_unref (atk_child); } priv = CALLY_ACTOR (atk_parent)->priv; index = g_list_index (priv->children, actor); g_list_free (priv->children); priv->children = clutter_actor_get_children (CLUTTER_ACTOR (container)); if (index >= 0 && index <= g_list_length (priv->children)) g_signal_emit_by_name (atk_parent, "children_changed::remove", index, atk_child, NULL); return 1; } /* AtkComponent implementation */ static void cally_actor_component_interface_init (AtkComponentIface *iface) { g_return_if_fail (iface != NULL); iface->get_extents = cally_actor_get_extents; iface->get_mdi_zorder = cally_actor_get_mdi_zorder; /* focus management */ iface->grab_focus = cally_actor_grab_focus; } static void cally_actor_get_extents (AtkComponent *component, gint *x, gint *y, gint *width, gint *height, AtkCoordType coord_type) { CallyActor *cally_actor = NULL; ClutterActor *actor = NULL; gint top_level_x, top_level_y; gfloat f_width, f_height; graphene_point3d_t verts[4]; ClutterActor *stage = NULL; g_return_if_fail (CALLY_IS_ACTOR (component)); cally_actor = CALLY_ACTOR (component); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); if (actor == NULL) /* actor is defunct */ return; /* If the actor is not placed in any stage, we can't compute the * extents */ stage = clutter_actor_get_stage (actor); if (stage == NULL) return; clutter_actor_get_abs_allocation_vertices (actor, verts); clutter_actor_get_transformed_size (actor, &f_width, &f_height); *x = verts[0].x; *y = verts[0].y; *width = ceilf (f_width); *height = ceilf (f_height); /* In the ATK_XY_WINDOW case, we consider the stage as the * "top-level-window" * * http://library.gnome.org/devel/atk/stable/AtkUtil.html#AtkCoordType */ if (coord_type == ATK_XY_SCREEN) { _cally_actor_get_top_level_origin (actor, &top_level_x, &top_level_y); *x += top_level_x; *y += top_level_y; } return; } static gint cally_actor_get_mdi_zorder (AtkComponent *component) { CallyActor *cally_actor = NULL; ClutterActor *actor = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (component), G_MININT); cally_actor = CALLY_ACTOR(component); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); return clutter_actor_get_z_position (actor); } static gboolean cally_actor_grab_focus (AtkComponent *component) { ClutterActor *actor = NULL; ClutterActor *stage = NULL; CallyActor *cally_actor = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (component), FALSE); /* See focus section on implementation notes */ cally_actor = CALLY_ACTOR(component); actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); stage = clutter_actor_get_stage (actor); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); return TRUE; } /* * * This gets the top level origin, it is, the position of the stage in * the global screen. You can see it as the absolute display position * of the stage. This is 0,0 for a compositor. */ void _cally_actor_get_top_level_origin (ClutterActor *actor, gint *xp, gint *yp) { /* default values */ if (xp) *xp = 0; if (yp) *yp = 0; } /* AtkAction implementation */ static void cally_actor_action_interface_init (AtkActionIface *iface) { g_return_if_fail (iface != NULL); iface->do_action = cally_actor_action_do_action; iface->get_n_actions = cally_actor_action_get_n_actions; iface->get_description = cally_actor_action_get_description; iface->get_keybinding = cally_actor_action_get_keybinding; iface->get_name = cally_actor_action_get_name; iface->set_description = cally_actor_action_set_description; } static gboolean cally_actor_action_do_action (AtkAction *action, gint index) { CallyActor *cally_actor = NULL; AtkStateSet *set = NULL; CallyActorPrivate *priv = NULL; CallyActorActionInfo *info = NULL; gboolean did_action = FALSE; cally_actor = CALLY_ACTOR (action); priv = cally_actor->priv; set = atk_object_ref_state_set (ATK_OBJECT (cally_actor)); if (atk_state_set_contains_state (set, ATK_STATE_DEFUNCT)) goto out; if (!atk_state_set_contains_state (set, ATK_STATE_SENSITIVE) || !atk_state_set_contains_state (set, ATK_STATE_SHOWING)) goto out; info = _cally_actor_get_action_info (cally_actor, index); if (info == NULL) goto out; if (info->do_action_func == NULL) goto out; if (!priv->action_queue) priv->action_queue = g_queue_new (); g_queue_push_head (priv->action_queue, info); if (!priv->action_idle_handler) priv->action_idle_handler = g_idle_add (idle_do_action, cally_actor); did_action = TRUE; out: g_clear_object (&set); return did_action; } static gboolean idle_do_action (gpointer data) { CallyActor *cally_actor = NULL; CallyActorPrivate *priv = NULL; ClutterActor *actor = NULL; cally_actor = CALLY_ACTOR (data); priv = cally_actor->priv; actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); priv->action_idle_handler = 0; if (actor == NULL) /* state is defunct*/ return FALSE; while (!g_queue_is_empty (priv->action_queue)) { CallyActorActionInfo *info = NULL; info = (CallyActorActionInfo *) g_queue_pop_head (priv->action_queue); info->do_action_func (cally_actor, info->user_data); } return FALSE; } static gint cally_actor_action_get_n_actions (AtkAction *action) { CallyActor *cally_actor = NULL; CallyActorPrivate *priv = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (action), 0); cally_actor = CALLY_ACTOR (action); priv = cally_actor->priv; return g_list_length (priv->action_list); } static const gchar* cally_actor_action_get_name (AtkAction *action, gint i) { CallyActor *cally_actor = NULL; CallyActorActionInfo *info = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); cally_actor = CALLY_ACTOR (action); info = _cally_actor_get_action_info (cally_actor, i); if (info == NULL) return NULL; return info->name; } static const gchar* cally_actor_action_get_description (AtkAction *action, gint i) { CallyActor *cally_actor = NULL; CallyActorActionInfo *info = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); cally_actor = CALLY_ACTOR (action); info = _cally_actor_get_action_info (cally_actor, i); if (info == NULL) return NULL; return info->description; } static gboolean cally_actor_action_set_description (AtkAction *action, gint i, const gchar *desc) { CallyActor *cally_actor = NULL; CallyActorActionInfo *info = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (action), FALSE); cally_actor = CALLY_ACTOR (action); info = _cally_actor_get_action_info (cally_actor, i); if (info == NULL) return FALSE; g_free (info->description); info->description = g_strdup (desc); return TRUE; } static const gchar* cally_actor_action_get_keybinding (AtkAction *action, gint i) { CallyActor *cally_actor = NULL; CallyActorActionInfo *info = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (action), NULL); cally_actor = CALLY_ACTOR (action); info = _cally_actor_get_action_info (cally_actor, i); if (info == NULL) return NULL; return info->keybinding; } /* Misc functions */ /* * This function is a signal handler for notify signal which gets emitted * when a property changes value on the ClutterActor associated with the object. * * It calls a function for the CallyActor type */ static void cally_actor_notify_clutter (GObject *obj, GParamSpec *pspec) { CallyActor *cally_actor = NULL; CallyActorClass *klass = NULL; cally_actor = CALLY_ACTOR (clutter_actor_get_accessible (CLUTTER_ACTOR (obj))); klass = CALLY_ACTOR_GET_CLASS (cally_actor); if (klass->notify_clutter) klass->notify_clutter (obj, pspec); } /* * This function is a signal handler for notify signal which gets emitted * when a property changes value on the ClutterActor associated with a CallyActor * * It constructs an AtkPropertyValues structure and emits a "property_changed" * signal which causes the user specified AtkPropertyChangeHandler * to be called. */ static void cally_actor_real_notify_clutter (GObject *obj, GParamSpec *pspec) { ClutterActor* actor = CLUTTER_ACTOR (obj); AtkObject* atk_obj = clutter_actor_get_accessible (CLUTTER_ACTOR(obj)); AtkState state; gboolean value; if (g_strcmp0 (pspec->name, "visible") == 0) { state = ATK_STATE_VISIBLE; value = clutter_actor_is_visible (actor); } else if (g_strcmp0 (pspec->name, "mapped") == 0) { /* Clones may temporarily map an actor in order to * paint it; we don't want this to generate an ATK * state change */ if (clutter_actor_is_in_clone_paint (actor)) return; state = ATK_STATE_SHOWING; value = clutter_actor_is_mapped (actor); } else if (g_strcmp0 (pspec->name, "reactive") == 0) { state = ATK_STATE_SENSITIVE; value = clutter_actor_get_reactive (actor); } else return; atk_object_notify_state_change (atk_obj, state, value); } static void _cally_actor_clean_action_list (CallyActor *cally_actor) { CallyActorPrivate *priv = NULL; priv = cally_actor->priv; if (priv->action_list) { g_list_free_full (priv->action_list, (GDestroyNotify) _cally_actor_destroy_action_info); priv->action_list = NULL; } } static CallyActorActionInfo * _cally_actor_get_action_info (CallyActor *cally_actor, gint index) { CallyActorPrivate *priv = NULL; GList *node = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), NULL); priv = cally_actor->priv; if (priv->action_list == NULL) return NULL; node = g_list_nth (priv->action_list, index); if (node == NULL) return NULL; return (CallyActorActionInfo *)(node->data); } /** * cally_actor_add_action: (skip) * @cally_actor: a #CallyActor * @action_name: the action name * @action_description: the action description * @action_keybinding: the action keybinding * @action_func: the callback of the action, to be executed with do_action * * Adds a new action to be accessed with the #AtkAction interface. * * Return value: added action id, or -1 if failure * * Since: 1.4 */ guint cally_actor_add_action (CallyActor *cally_actor, const gchar *action_name, const gchar *action_description, const gchar *action_keybinding, CallyActionFunc action_func) { return cally_actor_add_action_full (cally_actor, action_name, action_description, action_keybinding, (CallyActionCallback) action_func, NULL, NULL); } /** * cally_actor_add_action_full: (rename-to cally_actor_add_action) * @cally_actor: a #CallyActor * @action_name: the action name * @action_description: the action description * @action_keybinding: the action keybinding * @callback: (scope notified): the callback of the action * @user_data: (closure): data to be passed to @callback * @notify: function to be called when removing the action * * Adds a new action to be accessed with the #AtkAction interface. * * Return value: added action id, or -1 if failure * * Since: 1.6 */ guint cally_actor_add_action_full (CallyActor *cally_actor, const gchar *action_name, const gchar *action_description, const gchar *action_keybinding, CallyActionCallback callback, gpointer user_data, GDestroyNotify notify) { CallyActorActionInfo *info = NULL; CallyActorPrivate *priv = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), -1); g_return_val_if_fail (callback != NULL, -1); priv = cally_actor->priv; info = g_slice_new (CallyActorActionInfo); info->name = g_strdup (action_name); info->description = g_strdup (action_description); info->keybinding = g_strdup (action_keybinding); info->do_action_func = callback; info->user_data = user_data; info->notify = notify; priv->action_list = g_list_append (priv->action_list, info); return g_list_length (priv->action_list); } /** * cally_actor_remove_action: * @cally_actor: a #CallyActor * @action_id: the action id * * Removes a action, using the @action_id returned by cally_actor_add_action() * * Return value: %TRUE if the operation was succesful, %FALSE otherwise * * Since: 1.4 */ gboolean cally_actor_remove_action (CallyActor *cally_actor, gint action_id) { GList *list_node = NULL; CallyActorPrivate *priv = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), FALSE); priv = cally_actor->priv; list_node = g_list_nth (priv->action_list, action_id - 1); if (!list_node) return FALSE; _cally_actor_destroy_action_info (list_node->data, NULL); priv->action_list = g_list_remove_link (priv->action_list, list_node); return TRUE; } /** * cally_actor_remove_action_by_name: * @cally_actor: a #CallyActor * @action_name: the name of the action to remove * * Removes an action, using the @action_name used when the action was added * with cally_actor_add_action() * * Return value: %TRUE if the operation was succesful, %FALSE otherwise * * Since: 1.4 */ gboolean cally_actor_remove_action_by_name (CallyActor *cally_actor, const gchar *action_name) { GList *node = NULL; gboolean action_found = FALSE; CallyActorPrivate *priv = NULL; g_return_val_if_fail (CALLY_IS_ACTOR (cally_actor), FALSE); priv = CALLY_ACTOR (cally_actor)->priv; for (node = priv->action_list; node && !action_found; node = node->next) { CallyActorActionInfo *ainfo = node->data; if (!g_ascii_strcasecmp (ainfo->name, action_name)) { action_found = TRUE; break; } } if (!action_found) return FALSE; _cally_actor_destroy_action_info (node->data, NULL); priv->action_list = g_list_remove_link (priv->action_list, node); return TRUE; } static void _cally_actor_destroy_action_info (gpointer action_info, gpointer user_data) { CallyActorActionInfo *info = action_info; g_assert (info != NULL); g_free (info->name); g_free (info->description); g_free (info->keybinding); if (info->notify) info->notify (info->user_data); g_slice_free (CallyActorActionInfo, info); } muffin-6.4.1/clutter/clutter/cally/cally-main.h0000664000175000017500000000256514723361714020421 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Some parts are based on GailWidget from GAIL * GAIL - The GNOME Accessibility Implementation Library * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_MAIN_H__ #define __CALLY_MAIN_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS CLUTTER_EXPORT gboolean cally_get_cally_initialized (void); CLUTTER_EXPORT gboolean cally_accessibility_init (void); G_END_DECLS #endif /* __CALLY_MAIN_H__ */ muffin-6.4.1/clutter/clutter/cally/ChangeLog.pre-cally-merge0000664000175000017500000007414314723361714022761 0ustar fabiofabio# DO NOT MODIFY THIS FILE # # Clutter uses the Git commit log to generate the ChangeLog files when # creating the tarball for releases and snapshots. This file is maintained # only for historical reasons. 2010-07-05 Alejandro Pieiro Cleaning ClutterText * Removing superfluous g_return_if_fail * Removing unused ClutterText::text-changed callback 2010-07-05 Alejandro Pieiro Refactoring "window:create" and "window:destroy" emission code Previously "window:create" and "window:destroy" were emitted on CallyUtil. Although it works, and CallyUtil already have callbacks to stage_added/removed signals, I think that it is more tidy/clear to do that on CallyRoot: * CallyRoot already has code to manage ClutterStage addition/removal * In fact, we can see CallyRoot as the object exposing the a11y information from ClutterStageManager, so it fits better here. * CallyUtil callbacks these signals are related to key event listeners (key snooper simulation). One of the main CallyUtil responsabilities is managing event (connecting, emitting), so I would prefer to not start to add/mix more functionalities here. Ideally it would be better to emit all CallyStage methods from CallyStage, but it is clear that "create" and "destroy" are more easy to emit from a external object. 2010-06-25 Alejandro Pieiro Cleaning clutter-actor Some cleaning changes: * Using CallyActionFunc instead of ACTION_FUNC * Removing a extra * on cally-actor-private macro documentation, to avoid gtk-doc warnings * Using g_strcmp0 instead of strcmp Changes to be applied on clutter (see CB#2097 and CB#2098), applied also here to maintain the sync. My intention is keep this developing line until the real integration, in order to make a final independent cally release. 2010-06-14 Alejandro Pieiro Adding -Wshadow option and solving warnings related 2010-06-14 Alejandro Pieiro Added dummy padding for future vt expasion Added dummy padding on the different classes structures, to allow future expansion of virtual methods. I decided to add this on all the classes, although it would be really unlikely in some cases (ie, CallyGroup) 2010-06-10 Alejandro Pieiro Adding and emitting "window:xxx" methods on CallyStage Added some window related signals on CallyStage: * window:activate and window:deactivate emitted from CallyStage * window:create and window:destroy emitted from CallyUtil ClutterStage doesn't fulfill 100% the window concept, but some of these signals are important in order to identify the object which could emit global/key events. The current implementation is equivalent to GailWindow one, supposing CallyStage as the only window related window. This likely would change in any clutter-based toolkit implement a real Window object, so a more flexible procedure would be required. But we would solve problems step by step. BTW: as I explain here [1] I really think that the current way to implement "window:xxx" signals (not defined in ATK but expected from the a11y implementation toolkit) somewhat hacky and undocumented (you need to check at-spi2 idls to know that you require to emit this events) Related to bug CB#2147 (Orca doesn't speech out properly non printable chars on some environments), as solves this problem in a specific case. [1] https://bugzilla.gnome.org/show_bug.cgi?id=620977#c1 2010-06-04 Alejandro Pieiro Avoiding clutter_stage_get_key_focus warning For any reason, in some cases, a clutter actor doesn't have a stage associated. We use the default one as fallback. 2010-06-02 Alejandro Pieiro Added a defunct check on cally_group_get_n_children Some warnings appeared when we tried to get the number of children of a defunct object. 2010-06-02 Alejandro Pieiro Update TODO file Use Bugzilla to setting missing features. 2010-06-01 Alejandro Pieiro Removing heuristics to decide CallyRectable/CallyTexture role Previously CallyRectangle and CallyTexture used some heuristics in order to decide the default role: ATK_ROLE_IMAGE or ATK_PUSH_BUTTON, as in practice most applications using these objects as buttons were not applying the proper role. As this is a hack, and a application responsibility, finally we have decided to remove this, so the default role is ATK_ROLE_IMAGE. Fixes CB#1732 (CallyTexture and CallyRectangle uses some heuristics to decide the role) 2010-05-28 Alejandro Pieiro Post-release version bump, after release 1.2.0 I wrongly added the last commit on the 1.1 branch, when in fact it requires clutter 1.3.3, and on the README it is explained that cally versioning is tied to clutter versioning. In order to solve that a clutter-1.2 release branch is created, and bumped the version. This versioning tyding will be obsolete when the integration with clutter become a reality, but in the same way, this is the way to tidy this thinking in this integration. 2010-04-13 Alejandro Pieiro Use clutter_actor_get_accessible The method clutter_actor_get_accessible was added due work on bug 2070, and should be used to get the accessibility object, instead of atk_gobject_accessible_for_object This would allow to implement a11y support directly on any clutter based toolkit object (ie StLabel). 2010-05-13 Alejandro Pieiro Added CallyClone example 2010-05-13 Alejandro Pieiro Added a11y support for ClutterClone Resolved in the most simplified way, just as a image and a default description to identify cloned objects. More information: http://lists.o-hand.com/clutter/3797.html 2010-04-14 Alejandro Pieiro Remove gail dependency Removed to avoid gdk/gtk dependency on cally. Part of bug CB#2072 solution 2010-04-14 Alejandro Pieiro Avoid gdk functions filling AtkKeyEventStruct Now when AtkKeyEventStruct is filled in order to emit any key event signal, it is not used any gdk function on the keyval or the string fields. event_string is filled with the printable character if possible, if not (Ctrl, Alt, etc) it is set as NULL. Now the AT should take care of that, at least until we define atk key event struct in a more agnostic way (not tied to gdk/gtk). See orca bug bgo#616206 as a example. Part of bug CB#2072 solution. 2010-04-15 Alejandro Pieiro Added gail_misc_layout_get_run_attributes implementation Part of bug CB#2072 solution 2010-04-14 Alejandro Pieiro Remove gailutil/gailmisc functions calls This is because gailutil/gailmisc added a gdk/gtk dependency, and this dependency is being removed. New cally-specific implementation are required. Related to bug CB#1733 Part of bug CB#2072 solution 2010-04-13 Alejandro Pieiro Fixing the libdir directory in some examples 2010-03-26 Alejandro Pieiro Previous cally.pc.in update was incomplete The previous commit was not tested properly, and it was missing one detail. Sorry for the noise. 2010-03-26 Alejandro Pieiro Update cally.pc.in after module relocation Previous commit places cally module in a different directory. It also corrects where the include directory is placed. 2010-03-15 Alejandro Pieiro Use a proper clutter module directory Use a proper clutter module directory, instead of keep being installed on a gtk directory. Improve the cally-examples-util, in order to keep using hardcoded values. Fixes CB#1737 (Wrong cally module directory) 2010-03-15 Alejandro Pieiro Proper UTF-8 headers 2010-02-25 Alejandro Pieiro Change "--with-dbus" option for "atk-bridge-dir" on examples The atk-adaptor in the dbus at-spi was renamed to atk-bridge due some apps hardcoding the name. So right now the only difference is the final directory. So the option was removed, and atk-bridge-dir added. This also allows to use the system atk-bridge or the compiled in any developing environment, so it is really more flexible. See the README (updated with this commit) for more information. 2010-02-19 Alejandro Pieiro Added .gitignore file 2010-02-19 Alejandro Pieiro Release 1.1.1 2010-02-19 Alejandro Pieiro Using clutter_threads_idle_add instead of the gdk one The idea is being as less gdk dependent as possible. Right now it is inviable to remove the dependency (gailutil and so on) but hypothetically, the ideal is remove this dependency in the future, and being "clutter pure". 2010-02-15 Alejandro Pieiro Check if the state is defunct on cally_text_get_name Check if the state is defunct on cally_text_get_name, in order to avoid warnings cally clutter_text_get_text when the clutter object is NULL 2010-01-26 Alejandro Pieiro Update on configure.ac after autoupdate call 2010-02-02 Alejandro Pieiro Try to apply the key modifiers to event->keyval like GDK does ClutterKeyEvent defines the keyval without taking into account the modifiers. GDK defines this keyval taking into account the modifiers. AtkKeyEventStruct expects the keyval in a GDK fashion, so a translation is required. This patch tries to do that using using gdk_keymap_translate_keyboard_state. This functions only works correctly if gtk has been initialized, so the fallback is create the AtkKeyEventStruct with the keyval provided by Clutter. More information: http://library.gnome.org/devel/atk/stable/AtkUtil.html#AtkKeyEventStruct http://bugzilla.openedhand.com/show_bug.cgi?id=1961 2010-02-02 Alejandro Pieiro Filling AtkKeyEventStruct->string used on the atk key event listeners Finally we use directly gdk_keyval_name. Not the ideal solution, but works, and more important, it avoids to reimplement this issue on clutter or cally. More information on Bug 1952 Fixes http://bugzilla.openedhand.com/show_bug.cgi?id=1952 2010-01-22 Alejandro Pieiro Added AM_PROG_CC_C_O option to avoid a warning running configure 2010-01-22 Alejandro Pieiro Fix clutter version required on the pc files 2010-01-22 Alejandro Pieiro Check on configure time if any x11 clutter backend is in use It uses AC_CHECK_LIB in order to check if x11 backend is in use. It also modifies cally-actor in order to use the information retrieved. So now cally has a minimum multi-backend support. It only manages a x11 (glx or eglx) backend, but at least, it checks it, so you can compile cally without this backend. It probably will not work properly, but at least you can compile and execute it. Solves http://bugzilla.openedhand.com/show_bug.cgi?id=1736 2010-01-21 Alejandro Pieiro Fix the perspective problems computing the on-screen extensions Use clutter_actor_get_abs_allocation_vertices and clutter_actor_get_transformed_size to get the real on-screen position and size, instead of compute that using the geometry and the anchor point. It also update cally-atkcomponent-example, adding a actor inside a nested ClutterGroup hierarchy. Fixes: http://bugzilla.openedhand.com/show_bug.cgi?id=1731 2010-01-13 Alejandro Pieiro Added extra button on cally-atkeditabletext-example Added a button to print the current cursor position, and also extend the size of the buttons 2010-01-12 Alejandro Pieiro Remove superfluous g_print on CallyStage 2009-12-03 Alejandro Pieiro Use clutter_stage_manager_peek_stages to avoid a leak 2009-12-03 Alejandro Pieiro Added ATK_STATE_SELECTABLE_TEXT management 2009-11-26 Alejandro Pieiro Manage properly ATK_STATE_ACTIVE on CallyStage * cally/cally-stage.c Added private struct (cally_stage_class_init),(cally_stage_init),(cally_stage_real_initalize): Initialization stuff (cally_stage_activate_cb) (cally_stage_deactivate_cb): new ClutterStage signal callbacks, change the internal value of active, and notify the atk state change (cally_stage_ref_state_set): manage ATK_STATE_ACTIVATE * examples/cally-atktext-example2.c If possible, creates two stage, in order to test ATK_STATE_ACTIVATE 2009-11-24 Alejandro Pieiro Focused state change and focused object notification * cally/cally-actor.h (focus_clutter): added virtual method for the focus management * cally/cally-actor.c (cally_actor_component_interface_init) (cally_actor_add_focus_handler) (cally_actor_remove_focus_handler): Implementation of the AtkComponent methods add_focus_handler and remove_focus_handler (cally_actor_focus_event): CallyActor specific focus handler, notify the state focused change (cally_actor_focus_clutter) (cally_actor_real_focus_clutter): Handlers for the ClutterActor "key-focus-in" and "key-focus-out" signals. Emit the signal AtkObject "focus_event" and set the focus object with atk_focus_tracker_notify. (cally_actor_initialize): Connect to the signals "key-focus-in" and "key-focus-out", use atk_component_add_focus_handler to add cally_actor_focus_event Note: The focus management is more simplified that the gail one. The main reason is that the focus management in GTK is really more complex that the Clutter one. 2009-11-24 Alejandro Pieiro Modify cally-atkeditabletext-example.c to manage "activatable" status 2009-11-24 Alejandro Pieiro Added "activate" action in ClutterText * cally/cally-actor.h * cally/cally-actor.c cally_actor_add_action now returns the action id added. Documentation added in order to explain the return values and others. * cally/cally-text.c Added action "activate". This action is only available if the ClutterText is activatable, so the "activatable" property is tracked in the notify 2009-11-20 Alejandro Pieiro Signal event emission Emits the signals "text_selection_changed", "text_caret_moved", "text_changed::insert", "text_changed::delete", and notify the ATK_STATE_EDITABLE state change. It also adds the ATK_STATE_EDITABLE in the ref_state_set, includes a finalize to clean the new private data used, and move part of the initialization from the _init to the _real_initialization. 2009-12-03 Alejandro Pieiro Remove the ATK_STATE_DEFUNCT emission Remove the ATK_STATE_DEFUNCT emission, as this is already made by AtkGObjectAccessible. It also removes the clutter actor from the private structure, as we can use the AtkGObjectAccessible API to obtain it. This makes the code more coherent, with respect of the rest of the Cally classes implementation. 2009-11-26 Alejandro Pieiro Remove ; from the CALLY_GET_CLUTTER_ACTOR macro 2009-11-25 Alejandro Pieiro TODO cleanup and more implementation notes * TODO: removed the data that we have in the bugzilla or in the implementation notes on the cally source * cally/cally-actor.c: complete implementations notes * cally/Makefile.am: add a comment related to the public headers, and include Makefile.in in the MAINTAINERCLEANFILES 2009-11-13 Alejandro Pieiro Adding new tips on CODING_STYLE 2009-11-09 Alejandro Pieiro AtkEditableText implementation on CallyText * examples/Makefile.am * examples/cally-atkeditabletext-example.c: New example added * cally/cally-text.c Interface AtkEditableText implemented, except some methods: * Missing ClipBoard feature on Clutter: paste_text copy_text cut_text * Missing a equivalent GtkTextTag on Clutter (so the possibility to set run attributes in a range): set_run_attributes Fixes bug CB#1734 2009-11-03 Alejandro Pieiro Removed DG_DISABLE_CHECKS and DG_DISABLE_CAST_CHECKS from CFLAGS * configure.ac: Removed DG_DISABLE_CHECKS and DG_DISABLE_CAST_CHECKS from the common CFLAGS options * cally/cally-actor.c: fixed cast errors on some return values, not detected previously because of the use of relaxed compilation options Problem detected by Mario Sánchez Prada 2009-10-28 Alejandro Pieiro Support for multiple stages * cally-root.c * cally-stage.c * cally-util.c Implemented the support for multiple stages, by tracking the signals stage-added and stage-removed of the ClutterStageManager. In the same way CallyRoot has implement properly the atk_object_initialize, and in general now is more tied to ClutterStageManager (CallyRoot is now the a11y object of ClutterStageManager), but factory not required anyway, as it is instanced on the CallyUtil atk_get_root Fixes: CB#1754 (Missing multi-stage support) 2009-10-27 Alejandro Pieiro Implemented atk_[add/remove]_key_event_listener on CallyUtil * cally/cally-util.c: Implemented atk_[add/remove]_key_event_listener * examples/cally-atktext-example2.c: Modified in order to install and remove key event listeners, for testing purposes Fixes CB#1852 (AtkUtil implementation misses atk_[add/remove]_key_event_listener) 2009-10-21 Alejandro Pieiro Implemented atk-[add/remove]-global-event-listener on CallyUtil * cally/cally-util.c: Implemented atk-[add/remove]-global-event-listener on CallyUtil * examples/Makefile.am * examples/cally-atktext-example2.c New example in order to test easier the event emission on focus change (not working right now) 2009-10-12 Alejandro Pieiro Add --with-dbus option executing the examples The replacement for atk-bridge on at-spi-dbus has a different name (atk-adaptor), and it has not defined the method gnome_accessibility_init. The --with-dbus option allow to load the correct library and use the correct hook method if you are using at-spi-dbus. Anyway, take into account that this is just an example, and in a final environment, this should be made in a more general way. More information: CB#1738, CB#1737 2009-09-25 Alejandro Pieiro Symplifying shave support. 2009-09-25 Alejandro Pieiro Cleanup on the compilation and installation process * cally/Makefile.am: Added libcallydir and libcally_HEADERS in order to publish all cally headers, as the current policy is use the cally headers as public. * configure.ac: Change API_VERSION_MAJOR for CALLY_API_VERSION, as was the real meaning, and define CALLY_VERSION. Change CALLY_OBJ_CFLAGS and CALLY_OBJ_LIBS, used to compile the tests, as was not required to compile against the cally module (the example only required to compile against Clutter, as the cally module was just a module loaded by GModule). Support for Shave. 2009-07-31 Alejandro Pieiro Be able to run the examples without installing Cally Before that, the examples searched the cally module from the final installed directory. This means that you should install the library to use the examples. On development this is not desirable. Now it is loaded from ../cally/.libs This is a little hackish, but more useful, and in the end, it is just a example. Probably a best option could be configure that on the command line. $ ./example --cally-dir="mydir" But just a nitpick. 2009-07-29 Alejandro Pieiro Upgrade to cally-1.0, using clutter-1.0 * NEWS * TODO: removed several items, waiting to be moved to the bugzilla * configure.ac * examples/cally-examples-util.c 2009-07-27 Alejandro Pieiro Fixed return value of cally_actor_get_index_in_parent Bug and solutiond pointed by Gerd Kohlberger 2009-06-30 Alejandro Pieiro Added the implementation of most AtkText methods for CluttetText (CallyText) It remains some methods: get_default_attributes get_character_extents get_offset_at_point The current gail implementation delegate on gailmisc, but this is tied to GtkWidget so an equivalent functionality would be implemented (something like callymisc), and in the case of get_character_extents, not sure about the layout position (see gtk_entry_get_layout_offsets). I think that worth manage this in a different commit. In the same way is still missing AtkEditableText support. 2009-07-07 Alejandro Pieiro Added CALLY_GET_CLUTTER_ACTOR macro This macro was created to simplify how do you get the clutter actor object related to the cally object. On CallyActor a private attributte maintains it (for convenience, as it is heavily used) but outside, atkgobject methods can be used. Note that there is a possibility on the future to change it. Two options: * Add a public method to get the clutter object * Use this method on CallyActor too This macro simplifies this: CLUTTER_ACTOR (atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (cally_object))) 2009-06-24 Alejandro Pieiro Renamed examples/cally-util.[ch] to examples/cally-examples-util.[ch] Renamed examples/cally-util.[ch] to examples/cally-examples-util.[ch] to avoid confusion with cally/cally-util.[ch], implementation of the AtkUtil interface 2009-06-23 Alejandro Pieiro Adding examples directory * NEWS: Updates * configure.ac * Makefile.am * cally/Makefile.am * examples/Makefile.am: New * examples/cally-util.[ch]: New * examples/example1.c: New Added a directory in order to put examples. In this way we don't require any external clutter app to make the basic a11y functionality checks. At this moment only an example was added, but all the compiling structure is working. By default the examples are not compiled, use "--enable-examples" on configure time in order to enable their compilation. This basic example basically shows several objects, with different depth, in order to check that AtkComponent returns the correct screen position. Other minor changes done on the building infrastructure. 2009-06-23 Alejandro Pieiro Fix clutter version required 2009-06-23 Alejandro Pieiro Solve a problem calling clutter_actor_get_anchor_point * cally/cally-actor.c: (_get_actor_extents): use gfloat instead of gint, as now this clutter_actor_get_anchor_point use floats 2009-06-11 Alejandro Pieiro Minor fixes * Update TODO * Fix .pc files, to use clutter-0.9 version 2009-05-20 Alejandro Pieiro Library renamed from cail to cally 2009-05-08 Alejandro Pieiro Removed cail-clone-texture.h from cail.h * cail/cail.h: Removed reference to cail-clone-texture.h 2009-05-08 Alejandro Pieiro Upgrade to cail-0.9, using clutter-0.9, first compilable version * NEWS: new file with the information of the releases * TODO: updated * configure.ac: updated clutter version to compile against * cail/cail-clone-texture.[ch]: Removed as ClutterCloneTexture was removed on Clutter 0.9.0 * cail/cail-label.[ch]: Removed as ClutterLabel was removed on Clutter 0.9.0 * cail/Makefile.am: updated due the source files removed * cail/cail-actor.c: removed include to * cail/cail.c: removed the factories for CailLabel and CailCloneTexture 2009-05-07 Alejandro Pieiro Reflect change on the version number policy * README: correct some typos and explain that the cail version number is tied to the clutter version number and how * configure.ac Set the version number to 0.8.0 2009-05-07 Alejandro Pieiro Edit the ChangeLog file, to show that now we are using git * ChangeLog.SVN: new file, with the ChangeLog used while cail was using a Subversion repository * ChangeLog: now is empty, and only maintains a reference to use git log 2009-04-29 Alejandro Pieiro Coding style review * CODING_STYLE * cail/Makefile.am * cail/cail-actor-private.[ch] * cail/cail-actor.h * cail/cail-clone-texture.[ch] * cail/cail-group.[ch] * cail/cail-label.[ch] * cail/cail-rectangle.[ch] * cail/cail-root.[ch] * cail/cail-stage.[ch] * cail/cail-texture.[ch] * cail/cail-util.[ch] * cail/cail.c 2009-04-28 Alejandro Pieiro Coding style review: cail-actor.c 2009-04-21 Alejandro Pieiro 2009-04-21 Alejandro Pinheiro * TODO: updated TODO file 2009-04-21 Alejandro Pieiro 2009-03-06 Alejandro Pinheiro * AUTHORS: update authors file to public release 2009-03-06 Alejandro Pieiro 2009-03-06 Alejandro Pinheiro * debian/control Added cdbs dependency, renamed debugging package * debian/libcail-common-dbg.dirs: new file * debian/libcail-common.dirs * debian/libcail-common.install Minor changes 2009-03-05 Alejandro Pieiro 2009-03-05 Alejandro Pinheiro * TODO Added TODO file, in order to list the remaining tasks. 2009-03-05 Alejandro Pieiro 2009-03-05 Alejandro Pinheiro * configure.ac * cail/cail.c * cail/cail-util.c * Makefile.am Removed all the missing gtk related stuff 2009-03-05 Alejandro Pieiro 2009-03-05 Alejandro Pinheiro * cail/cail-actor.c (_get_actor_extents): managing too the anchor point to compute the position (_get_top_level_origin): reimplemented using x11 functions, removed gtk/gdk related functions, and taking into account the relative position inside the parent (previous position calculation was wrong if a child was not a direct stage child) * cail/clutter-gtk/cail-clutter-embed.[ch] * cail/clutter-gtk/cail-gtk-factory.h Removed, in order to remove any gtk dependency * cail/debian/control: removed gtk dependency 2009-03-03 Alejandro Pieiro 2009-03-03 Alejandro Pinheiro * cail/cail-actor-private.[ch]: new files to private utility functions (_cail_actor_pushable): new function, that checks if a cail actor is pushable by checking if the clutter actor related has a handler for a release event * cail/cail-texture.c * cail/cail-clone-texture.c * cail/cail-rectangle.c Use of new function _cail_actor_pushable * cail-actor.c: Added some documentation related to current implementation * cail-util.c: Code style review 2009-03-02 Alejandro Pieiro 2009-03-02 Alejandro Pinheiro * cail/cail-label.[ch]: new * cail/cail.[ch] (cail_accessibility_module_init) * cail/Makefile.am Added CailLabel, a11y object for ClutterLabel 2009-02-27 Alejandro Pieiro 2009-02-27 Alejandro Pinheiro * cail/cail-actor.c (cail_actor_real_remove_actor) Fixed a typo that causes a crash while removing the actor from a container 2009-02-26 Alejandro Pieiro 2009-02-26 Alejandro Pinheiro * cail/cail-actor.c (cail_actor_remove_actor) (cail_actor_add_actor) Fixed a typo calling klass->add_actor and klass->remove_actor that causes a crash in some (container,actor) combinations (cail_actor_real_add_actor) Additional parameter check 2009-02-25 Alejandro Pieiro Missing cail-rectangle.[ch] files, according 2009-02-23 entry at Changelog 2009-02-23 Alejandro Pieiro 2009-02-23 Alejandro Pinheiro * cail/cail-rectangle.[ch] * cail/cail.[ch] * cail/Makefile.am Added CailRectangle, a11y object for ClutterRectangle * cail/cail-group.c * cail/cail-texture.c * cail/cail-stage.c Avoid to add a empty private structure, to avoid the glib warning. Anyway the pointer to the private structure is still on the .h, to allow future add-on. 2009-02-20 Alejandro Pieiro 2009-02-20 Alejandro Pinheiro * cail-actor.[ch] * cail-group.[ch] Moved most of the ClutterContainer a11y support from cail-group to cail-actor, in order to generalize this support. * cail-stage.[ch] * cail-util.[ch] Normalize the private structure to avoid future problems with missing gaps 2009-02-20 Alejandro Pieiro 2009-02-20 Alejandro Pinheiro * cail/cail-actor.c (cail_actor_connect_actor_destroyed): connects to the clutter actor destroy signal (cail_actor_clutter_actor_destroyed): handler to the clutter actor destroy signal, update the priv->actor pointer and notify a state change This change allows to be sure about the priv->actor correct value, so we can use directly priv->actor instead of atk_gobject_accessible_get_object in the next functions: (cail_actor_get_parent) (cail_actor_get_index_in_parent) (cail_actor_ref_state_set) (cail_actor_get_extents) 2009-02-19 Alejandro Pieiro 2009-02-19 Alejandro Pinheiro * cail/cail-texture.[ch] * cail/cail-clone-texture.[ch] * cail/cail.[ch] * cail/Makefile.am Added CailTexture and CailCloneTexture a11y objects for ClutterTexture and ClutterCloneTexture * cail/cail-util.c Added private structure 2009-02-18 Alejandro Pieiro 2009-02-18 Alejandro Pinheiro * cail/cail-actor.c: (cail_actor_get_parent) Return the accessible object of the clutter actor if accessible_parent is not available. Previously it only took into account the these object as a possible parent to return (you can set it with atk_object_set_parent) 2009-02-18 Alejandro Pieiro 2009-02-18 Alejandro Pinheiro * cail/cail-group.[ch]: code style review * cail/cail-actor.[ch]: implemented basic support for ClutterContainer 2009-02-18 Alejandro Pieiro 2009-02-18 Alejandro Pinheiro * debian/control: updating dependencies 2009-02-18 Alejandro Pieiro 2009-02-18 Alejandro Pinheiro * configure.ac: added aditional compile flags * cail/cail-actor.[ch]: Reimplemented support for AtkAction interface * cail/cail-root.[ch]: code style review 2009-02-16 Alejandro Pieiro 2009-02-16 Alejandro Pinheiro * First release. muffin-6.4.1/clutter/clutter/cally/cally-stage.h0000664000175000017500000000475414723361714020602 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_STAGE_H__ #define __CALLY_STAGE_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_STAGE (cally_stage_get_type ()) #define CALLY_STAGE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_STAGE, CallyStage)) #define CALLY_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_STAGE, CallyStageClass)) #define CALLY_IS_STAGE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_STAGE)) #define CALLY_IS_STAGE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_STAGE)) #define CALLY_STAGE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_STAGE, CallyStageClass)) typedef struct _CallyStage CallyStage; typedef struct _CallyStageClass CallyStageClass; typedef struct _CallyStagePrivate CallyStagePrivate; /** * CallyStage: * * The CallyStage structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyStage { /*< private >*/ CallyGroup parent; CallyStagePrivate *priv; }; /** * CallyStageClass: * * The CallyStageClass structure contains only * private data * * Since: 1.4 */ struct _CallyStageClass { /*< private >*/ CallyGroupClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[16]; }; CLUTTER_EXPORT GType cally_stage_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject *cally_stage_new (ClutterActor *actor); G_END_DECLS #endif /* __CALLY_STAGE_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-group.h0000664000175000017500000000522414723361714020624 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Based on GailContainer from GAIL * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_GROUP_H__ #define __CALLY_GROUP_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_GROUP (cally_group_get_type ()) #define CALLY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_GROUP, CallyGroup)) #define CALLY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_GROUP, CallyGroupClass)) #define CALLY_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_GROUP)) #define CALLY_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_GROUP)) #define CALLY_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_GROUP, CallyGroupClass)) typedef struct _CallyGroup CallyGroup; typedef struct _CallyGroupClass CallyGroupClass; typedef struct _CallyGroupPrivate CallyGroupPrivate; /** * CallyGroup: * * The CallyGroup structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyGroup { /*< private >*/ CallyActor parent; CallyGroupPrivate *priv; }; /** * CallyGroupClass: * * The CallyGroupClass structure contains only * private data * * Since: 1.4 */ struct _CallyGroupClass { /*< private >*/ CallyActorClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_EXPORT GType cally_group_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject* cally_group_new (ClutterActor *actor); G_END_DECLS #endif /* __CALLY_GROUP_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-util.c0000664000175000017500000003525114723361714020443 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Based on GailUtil from GAIL * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-util * @Title: CallyUtil * @short_description: #AtkUtil implementation * @see_also: #ClutterActor * * #CallyUtil implements #AtkUtil abstract methods. Although it * includes the name "Util" it is in fact one of the most important * interfaces to be implemented in any ATK toolkit implementation. * For instance, it defines atk_get_root(), the method that returns * the root object in the hierarchy. Without it, you don't have * available any accessible object. */ #include "clutter-build-config.h" #include #include #include #include "cally-util.h" #include "cally-root.h" #include "cally-stage.h" #define DEFAULT_PASSWORD_CHAR '*' /* atkutil.h */ static guint cally_util_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data); static void cally_util_remove_key_event_listener (guint remove_listener); static AtkObject* cally_util_get_root (void); static const gchar * cally_util_get_toolkit_name (void); static const gchar * cally_util_get_toolkit_version (void); /* private */ static void cally_util_simulate_snooper_install (void); static void cally_util_simulate_snooper_remove (void); static gboolean cally_key_snooper (ClutterActor *actor, ClutterEvent *event, gpointer user_data); static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data); static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data); static gboolean notify_hf (gpointer key, gpointer value, gpointer data); static void insert_hf (gpointer key, gpointer value, gpointer data); /* This is just a copy of the Gail one, a shared library or place to define it could be a good idea. */ typedef struct _CallyKeyEventInfo CallyKeyEventInfo; struct _CallyKeyEventInfo { AtkKeySnoopFunc listener; gpointer func_data; }; static AtkObject* root = NULL; static GHashTable *key_listener_list = NULL; G_DEFINE_TYPE (CallyUtil, cally_util, ATK_TYPE_UTIL); static void cally_util_class_init (CallyUtilClass *klass) { AtkUtilClass *atk_class; gpointer data; data = g_type_class_peek (ATK_TYPE_UTIL); atk_class = ATK_UTIL_CLASS (data); atk_class->add_key_event_listener = cally_util_add_key_event_listener; atk_class->remove_key_event_listener = cally_util_remove_key_event_listener; atk_class->get_root = cally_util_get_root; atk_class->get_toolkit_name = cally_util_get_toolkit_name; atk_class->get_toolkit_version = cally_util_get_toolkit_version; /* FIXME: Instead of create this on the class, I think that would worth to implement CallyUtil as a singleton instance, so the class methods will access this instance. This will be a good future enhancement, meanwhile, just using the same *working* implementation used on GailUtil */ } static void cally_util_init (CallyUtil *cally_util) { /* instance init: usually not required */ } /* ------------------------------ ATK UTIL METHODS -------------------------- */ static AtkObject* cally_util_get_root (void) { if (!root) root = cally_root_new (); return root; } static const gchar * cally_util_get_toolkit_name (void) { return "clutter"; } static const gchar * cally_util_get_toolkit_version (void) { return MUTTER_VERSION; } static guint cally_util_add_key_event_listener (AtkKeySnoopFunc listener, gpointer data) { static guint key = 1; CallyKeyEventInfo *event_info = NULL; if (!key_listener_list) { key_listener_list = g_hash_table_new_full (NULL, NULL, NULL, g_free); cally_util_simulate_snooper_install (); } event_info = g_new (CallyKeyEventInfo, 1); event_info->listener = listener; event_info->func_data = data; g_hash_table_insert (key_listener_list, GUINT_TO_POINTER (key++), event_info); /* XXX: we don't check to see if n_listeners > MAXUINT */ return key - 1; } static void cally_util_remove_key_event_listener (guint remove_listener) { if (!g_hash_table_remove (key_listener_list, GUINT_TO_POINTER (remove_listener))) { g_warning ("Not able to remove listener with id %i", remove_listener); } if (g_hash_table_size (key_listener_list) == 0) { g_hash_table_destroy (key_listener_list); key_listener_list = NULL; cally_util_simulate_snooper_remove (); } } /* ------------------------------ PRIVATE FUNCTIONS ------------------------- */ /* Trying to emulate gtk_key_snooper install (a kind of wrapper). This could be implemented without it, but I will maintain it in this way, so if in the future clutter implements it natively it would be easier the transition */ static void cally_util_simulate_snooper_install (void) { ClutterStageManager *stage_manager = NULL; ClutterStage *stage = NULL; GSList *stage_list = NULL; GSList *iter = NULL; stage_manager = clutter_stage_manager_get_default (); stage_list = clutter_stage_manager_list_stages (stage_manager); for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) { stage = CLUTTER_STAGE (iter->data); g_signal_connect (G_OBJECT (stage), "captured-event", G_CALLBACK (cally_key_snooper), NULL); } g_signal_connect (G_OBJECT (stage_manager), "stage-added", G_CALLBACK (cally_util_stage_added_cb), cally_key_snooper); g_signal_connect (G_OBJECT (stage_manager), "stage-removed", G_CALLBACK (cally_util_stage_removed_cb), cally_key_snooper); g_slist_free (stage_list); } static void cally_util_simulate_snooper_remove (void) { ClutterStageManager *stage_manager = NULL; ClutterStage *stage = NULL; GSList *stage_list = NULL; GSList *iter = NULL; gint num = 0; stage_manager = clutter_stage_manager_get_default (); stage_list = clutter_stage_manager_list_stages (stage_manager); for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) { stage = CLUTTER_STAGE (iter->data); num += g_signal_handlers_disconnect_by_func (stage, cally_key_snooper, NULL); } g_signal_handlers_disconnect_by_func (G_OBJECT (stage_manager), G_CALLBACK (cally_util_stage_added_cb), cally_key_snooper); g_signal_handlers_disconnect_by_func (G_OBJECT (stage_manager), G_CALLBACK (cally_util_stage_removed_cb), cally_key_snooper); #ifdef CALLY_DEBUG g_print ("Number of snooper callbacks disconnected: %i\n", num); #endif } static AtkKeyEventStruct * atk_key_event_from_clutter_event_key (ClutterKeyEvent *clutter_event, gunichar password_char) { AtkKeyEventStruct *atk_event = g_new0 (AtkKeyEventStruct, 1); gunichar key_unichar; switch (clutter_event->type) { case CLUTTER_KEY_PRESS: atk_event->type = ATK_KEY_EVENT_PRESS; break; case CLUTTER_KEY_RELEASE: atk_event->type = ATK_KEY_EVENT_RELEASE; break; default: g_assert_not_reached (); return NULL; } if (password_char) atk_event->state = 0; else atk_event->state = clutter_event->modifier_state; /* We emit the clutter keyval. This is not exactly the one expected by AtkKeyEventStruct, as it expects a Gdk-like event, with the modifiers applied. But to avoid a dependency to gdk, we delegate that on the AT application. More information: Bug 1952 and bug 2072 */ if (password_char) atk_event->keyval = clutter_unicode_to_keysym (password_char); else atk_event->keyval = clutter_event->keyval; /* It is expected to store a key defining string here (ie "Space" in case you press a space). Anyway, there are no function on clutter to obtain that, and we want to avoid a gdk dependency here, so we delegate on the AT application to obtain that string using the rest of the data on the ATK event struct. More information: Bug 1952 and 2072 */ if (password_char) key_unichar = password_char; else key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) clutter_event); if (g_unichar_validate (key_unichar) && !g_unichar_iscntrl (key_unichar)) { GString *new = NULL; new = g_string_new (""); new = g_string_insert_unichar (new, 0, key_unichar); atk_event->string = new->str; g_string_free (new, FALSE); } else atk_event->string = NULL; atk_event->length = 0; /* Computing the hardware keycode from the password-char is difficult. But we are in a password situation. We are already a unichar that it is not the original one. Providing a "almost real" keycode is irrelevant */ if (password_char) atk_event->keycode = 0; else atk_event->keycode = clutter_event->hardware_keycode; atk_event->timestamp = clutter_event->time; #ifdef CALLY_DEBUG g_debug ("CallyKeyEvent:\tsym 0x%x\n\t\tmods %x\n\t\tcode %u\n\t\ttime %lx \n\t\tstring %s\n", (unsigned int) atk_event->keyval, (unsigned int) atk_event->state, (unsigned int) atk_event->keycode, (unsigned long int) atk_event->timestamp, atk_event->string); #endif return atk_event; } static gboolean notify_hf (gpointer key, gpointer value, gpointer data) { CallyKeyEventInfo *info = (CallyKeyEventInfo *) value; AtkKeyEventStruct *key_event = (AtkKeyEventStruct *)data; return (*(AtkKeySnoopFunc) info->listener) (key_event, info->func_data) ? TRUE : FALSE; } static void insert_hf (gpointer key, gpointer value, gpointer data) { GHashTable *new_table = (GHashTable *) data; g_hash_table_insert (new_table, key, value); } /* * 0 if the key of that event is visible, in other case the password * char */ static gunichar check_key_visibility (ClutterEvent *event) { ClutterKeyEvent *key_event = (ClutterKeyEvent *)event; AtkObject *accessible = clutter_actor_get_accessible (key_event->source); g_return_val_if_fail (accessible != NULL, 0); if (atk_object_get_role (accessible) != ATK_ROLE_PASSWORD_TEXT) return 0; /* If it is a clutter text, we use his password char. Note that although at Clutter toolkit itself, only ClutterText exposes a password role, nothing prevents on any derived toolkit (like st) to create a new actor that can behave like a password entry. And the key event will still be emitted here. Although in that case we would lose any password char from the derived toolkit, it is still better fill this with a default unichar that the original one */ if (CLUTTER_IS_TEXT (key_event->source)) return clutter_text_get_password_char (CLUTTER_TEXT (key_event->source)); else return DEFAULT_PASSWORD_CHAR; } static gboolean cally_key_snooper (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { AtkKeyEventStruct *key_event = NULL; gint consumed = 0; gunichar password_char = 0; /* filter key events */ if ((event->type != CLUTTER_KEY_PRESS) && (event->type != CLUTTER_KEY_RELEASE)) { return FALSE; } password_char = check_key_visibility (event); if (key_listener_list) { GHashTable *new_hash = g_hash_table_new (NULL, NULL); g_hash_table_foreach (key_listener_list, insert_hf, new_hash); key_event = atk_key_event_from_clutter_event_key ((ClutterKeyEvent *)event, password_char); /* func data is inside the hash table */ consumed = g_hash_table_foreach_steal (new_hash, notify_hf, key_event); g_hash_table_destroy (new_hash); g_free (key_event->string); g_free (key_event); } return (consumed ? 1 : 0); } static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data) { GCallback cally_key_snooper_cb = G_CALLBACK (data); g_signal_connect (G_OBJECT (stage), "captured-event", cally_key_snooper_cb, NULL); } static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data) { GCallback cally_key_snooper_cb = G_CALLBACK (data); g_signal_handlers_disconnect_by_func (stage, cally_key_snooper_cb, NULL); } void _cally_util_override_atk_util (void) { AtkUtilClass *atk_class = ATK_UTIL_CLASS (g_type_class_ref (ATK_TYPE_UTIL)); atk_class->add_key_event_listener = cally_util_add_key_event_listener; atk_class->remove_key_event_listener = cally_util_remove_key_event_listener; atk_class->get_root = cally_util_get_root; atk_class->get_toolkit_name = cally_util_get_toolkit_name; atk_class->get_toolkit_version = cally_util_get_toolkit_version; } muffin-6.4.1/clutter/clutter/cally/cally-clone.h0000664000175000017500000000475314723361714020576 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2010 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_CLONE_H__ #define __CALLY_CLONE_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_CLONE (cally_clone_get_type ()) #define CALLY_CLONE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_CLONE, CallyClone)) #define CALLY_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_CLONE, CallyCloneClass)) #define CALLY_IS_CLONE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_CLONE)) #define CALLY_IS_CLONE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_CLONE)) #define CALLY_CLONE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_CLONE, CallyCloneClass)) typedef struct _CallyClone CallyClone; typedef struct _CallyCloneClass CallyCloneClass; typedef struct _CallyClonePrivate CallyClonePrivate; /** * CallyClone: * * The CallyClone structure contains only private * data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyClone { /*< private >*/ CallyActor parent; CallyClonePrivate *priv; }; /** * CallyCloneClass: * * The CallyCloneClass structure contains only * private data * * Since: 1.4 */ struct _CallyCloneClass { /*< private >*/ CallyActorClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_EXPORT GType cally_clone_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject *cally_clone_new (ClutterActor *actor); G_END_DECLS #endif /* __CALLY_CLONE_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-stage.c0000664000175000017500000001703314723361714020567 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-stage * @Title: CallyStage * @short_description: Implementation of the ATK interfaces for a #ClutterStage * @see_also: #ClutterStage * * #CallyStage implements the required ATK interfaces for #ClutterStage * * Some implementation details: at this moment #CallyStage is used as * the most similar Window object in this toolkit (ie: emitting window * related signals), although the real purpose of #ClutterStage is * being a canvas. Anyway, this is required for applications using * just clutter, or directly #ClutterStage */ #include "clutter-build-config.h" #include "cally-stage.h" #include "cally-actor-private.h" /* AtkObject.h */ static void cally_stage_real_initialize (AtkObject *obj, gpointer data); static AtkStateSet* cally_stage_ref_state_set (AtkObject *obj); /* AtkWindow */ static void cally_stage_window_interface_init (AtkWindowIface *iface); /* Auxiliar */ static void cally_stage_activate_cb (ClutterStage *stage, gpointer data); static void cally_stage_deactivate_cb (ClutterStage *stage, gpointer data); struct _CallyStagePrivate { /* NULL means that the stage will receive the focus */ ClutterActor *key_focus; gboolean active; }; G_DEFINE_TYPE_WITH_CODE (CallyStage, cally_stage, CALLY_TYPE_GROUP, G_ADD_PRIVATE (CallyStage) G_IMPLEMENT_INTERFACE (ATK_TYPE_WINDOW, cally_stage_window_interface_init)); static void cally_stage_class_init (CallyStageClass *klass) { AtkObjectClass *class = ATK_OBJECT_CLASS (klass); /* AtkObject */ class->initialize = cally_stage_real_initialize; class->ref_state_set = cally_stage_ref_state_set; } static void cally_stage_init (CallyStage *cally_stage) { CallyStagePrivate *priv = cally_stage_get_instance_private (cally_stage); cally_stage->priv = priv; priv->active = FALSE; } /** * cally_stage_new: * @actor: a #ClutterActor * * Creates a new #CallyStage for the given @actor. @actor should be a * #ClutterStage. * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject* cally_stage_new (ClutterActor *actor) { GObject *object = NULL; AtkObject *accessible = NULL; g_return_val_if_fail (CLUTTER_IS_STAGE (actor), NULL); object = g_object_new (CALLY_TYPE_STAGE, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, actor); return accessible; } static void cally_stage_notify_key_focus_cb (ClutterStage *stage, GParamSpec *pspec, CallyStage *self) { ClutterActor *key_focus = NULL; AtkObject *new = NULL; if (self->priv->active == FALSE) return; key_focus = clutter_stage_get_key_focus (stage); if (key_focus != self->priv->key_focus) { AtkObject *old = NULL; if (self->priv->key_focus != NULL) { g_object_remove_weak_pointer (G_OBJECT (self->priv->key_focus), (gpointer *) &self->priv->key_focus); old = clutter_actor_get_accessible (self->priv->key_focus); } else old = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); atk_object_notify_state_change (old, ATK_STATE_FOCUSED, FALSE); } /* we keep notifying the focus gain without checking previous * key-focus to avoid some missing events due timing */ self->priv->key_focus = key_focus; if (key_focus != NULL) { /* ensure that if the key focus goes away, the field inside * CallyStage is reset. see bug: * * https://bugzilla.gnome.org/show_bug.cgi?id=692706 * * we remove the weak pointer above. */ g_object_add_weak_pointer (G_OBJECT (self->priv->key_focus), (gpointer *) &self->priv->key_focus); new = clutter_actor_get_accessible (key_focus); } else new = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); atk_object_notify_state_change (new, ATK_STATE_FOCUSED, TRUE); } static void cally_stage_real_initialize (AtkObject *obj, gpointer data) { ClutterStage *stage = NULL; g_return_if_fail (CALLY_IS_STAGE (obj)); ATK_OBJECT_CLASS (cally_stage_parent_class)->initialize (obj, data); stage = CLUTTER_STAGE (CALLY_GET_CLUTTER_ACTOR (obj)); g_signal_connect (stage, "activate", G_CALLBACK (cally_stage_activate_cb), obj); g_signal_connect (stage, "deactivate", G_CALLBACK (cally_stage_deactivate_cb), obj); g_signal_connect (stage, "notify::key-focus", G_CALLBACK (cally_stage_notify_key_focus_cb), obj); atk_object_set_role (obj, ATK_ROLE_WINDOW); } static AtkStateSet* cally_stage_ref_state_set (AtkObject *obj) { CallyStage *cally_stage = NULL; AtkStateSet *state_set = NULL; ClutterStage *stage = NULL; g_return_val_if_fail (CALLY_IS_STAGE (obj), NULL); cally_stage = CALLY_STAGE (obj); state_set = ATK_OBJECT_CLASS (cally_stage_parent_class)->ref_state_set (obj); stage = CLUTTER_STAGE (CALLY_GET_CLUTTER_ACTOR (cally_stage)); if (stage == NULL) return state_set; if (cally_stage->priv->active) atk_state_set_add_state (state_set, ATK_STATE_ACTIVE); return state_set; } /* AtkWindow */ static void cally_stage_window_interface_init (AtkWindowIface *iface) { /* At this moment AtkWindow is just about signals */ } /* Auxiliar */ static void cally_stage_activate_cb (ClutterStage *stage, gpointer data) { CallyStage *cally_stage = NULL; g_return_if_fail (CALLY_IS_STAGE (data)); cally_stage = CALLY_STAGE (data); cally_stage->priv->active = TRUE; atk_object_notify_state_change (ATK_OBJECT (cally_stage), ATK_STATE_ACTIVE, TRUE); g_signal_emit_by_name (cally_stage, "activate", 0); } static void cally_stage_deactivate_cb (ClutterStage *stage, gpointer data) { CallyStage *cally_stage = NULL; g_return_if_fail (CALLY_IS_STAGE (data)); cally_stage = CALLY_STAGE (data); cally_stage->priv->active = FALSE; atk_object_notify_state_change (ATK_OBJECT (cally_stage), ATK_STATE_ACTIVE, FALSE); g_signal_emit_by_name (cally_stage, "deactivate", 0); } muffin-6.4.1/clutter/clutter/cally/cally-factory.h0000664000175000017500000000760614723361714021145 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Based on gailfactory.h from GAIL * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #ifndef _CALLY_FACTORY_H__ #define _CALLY_FACTORY_H__ #include #include /** * CALLY_ACCESSIBLE_FACTORY: * @type: GType of the accessible which is created by the factory * @type_as_function: prefix of the accessible object methods * @opt_create_accessible: method to instantiate the accessibility object * * Defines a new #AtkObjectFactory factory to create accessible * objects of a specific GType. It defines the factory GType and also * overrides the proper #AtkObjectFactory methods. * * It assumes that the accessibility object provides a * @opt_create_accessible method in order to create the accessibility * object. It returns a @type GType object. * * Since: 1.4 */ #define CALLY_ACCESSIBLE_FACTORY(type, type_as_function, opt_create_accessible) \ \ static GType \ type_as_function ## _factory_get_accessible_type (void) \ { \ return type; \ } \ \ static AtkObject* \ type_as_function ## _factory_create_accessible (GObject *obj) \ { \ ClutterActor *actor; \ AtkObject *accessible; \ \ g_return_val_if_fail (CLUTTER_ACTOR (obj), NULL); \ \ actor = CLUTTER_ACTOR (obj); \ \ accessible = opt_create_accessible (actor); \ \ return accessible; \ } \ \ static void \ type_as_function ## _factory_class_init (AtkObjectFactoryClass *klass) \ { \ klass->create_accessible = type_as_function ## _factory_create_accessible; \ klass->get_accessible_type = type_as_function ## _factory_get_accessible_type;\ } \ \ static GType \ type_as_function ## _factory_get_type (void) \ { \ static GType t = 0; \ \ if (!t) \ { \ char *name; \ static const GTypeInfo tinfo = \ { \ sizeof (AtkObjectFactoryClass), \ NULL, NULL, (GClassInitFunc) type_as_function ## _factory_class_init, \ NULL, NULL, sizeof (AtkObjectFactory), 0, NULL, NULL \ }; \ \ name = g_strconcat (g_type_name (type), "Factory", NULL); \ t = g_type_register_static ( \ ATK_TYPE_OBJECT_FACTORY, name, &tinfo, 0); \ g_free (name); \ } \ \ return t; \ } /** * CALLY_ACTOR_SET_FACTORY: * @widget_type: GType of the clutter actor * @type_as_function: prefix of the accessible object methods * * Sets the #AtkObjectFactory to be used in order to instantiate * accessibility objects for the actor which GType is @widget_type. * * Since: 1.4 */ #define CALLY_ACTOR_SET_FACTORY(widget_type, type_as_function) \ atk_registry_set_factory_type (atk_get_default_registry (), \ widget_type, \ type_as_function ## _factory_get_type ()) #endif /* _CALLY_FACTORY_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-root.c0000664000175000017500000002120314723361714020441 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-root * @short_description: Root object for the Cally toolkit * @see_also: #ClutterStage * * #CallyRoot is the root object of the accessibility tree-like * hierarchy, exposing the application level. * * Somewhat equivalent to #GailTopLevel. We consider that this class * expose the a11y information of the #ClutterStageManager, as the * children of this object are the different ClutterStage managed (so * the #GObject used in the atk_object_initialize() is the * #ClutterStageManager). */ #include "clutter-build-config.h" #include "cally-root.h" #include "clutter-actor.h" #include "clutter-stage-private.h" #include "clutter-stage-manager.h" /* GObject */ static void cally_root_finalize (GObject *object); /* AtkObject.h */ static void cally_root_initialize (AtkObject *accessible, gpointer data); static gint cally_root_get_n_children (AtkObject *obj); static AtkObject * cally_root_ref_child (AtkObject *obj, gint i); static AtkObject * cally_root_get_parent (AtkObject *obj); static const char * cally_root_get_name (AtkObject *obj); /* Private */ static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data); static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data); struct _CallyRootPrivate { /* We save the CallyStage objects. Other option could save the stage * list, and then just get the a11y object on the ref_child, etc. But * the ref_child is more common that the init and the stage-add, * stage-remove, so we avoid getting the accessible object * constantly */ GSList *stage_list; /* signals id */ gulong stage_added_id; gulong stage_removed_id; }; G_DEFINE_TYPE_WITH_PRIVATE (CallyRoot, cally_root, ATK_TYPE_GOBJECT_ACCESSIBLE) static void cally_root_class_init (CallyRootClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); AtkObjectClass *class = ATK_OBJECT_CLASS (klass); gobject_class->finalize = cally_root_finalize; /* AtkObject */ class->get_n_children = cally_root_get_n_children; class->ref_child = cally_root_ref_child; class->get_parent = cally_root_get_parent; class->initialize = cally_root_initialize; class->get_name = cally_root_get_name; } static void cally_root_init (CallyRoot *root) { root->priv = cally_root_get_instance_private (root); root->priv->stage_list = NULL; root->priv->stage_added_id = 0; root->priv->stage_removed_id = 0; } /** * cally_root_new: * * Creates a new #CallyRoot object. * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject* cally_root_new (void) { GObject *object = NULL; AtkObject *accessible = NULL; ClutterStageManager *stage_manager = NULL; object = g_object_new (CALLY_TYPE_ROOT, NULL); accessible = ATK_OBJECT (object); stage_manager = clutter_stage_manager_get_default (); atk_object_initialize (accessible, stage_manager); return accessible; } static void cally_root_finalize (GObject *object) { CallyRoot *root = CALLY_ROOT (object); GObject *stage_manager = NULL; g_return_if_fail (CALLY_IS_ROOT (object)); if (root->priv->stage_list) { g_slist_free (root->priv->stage_list); root->priv->stage_list = NULL; } stage_manager = atk_gobject_accessible_get_object (ATK_GOBJECT_ACCESSIBLE (root)); g_clear_signal_handler (&root->priv->stage_added_id, stage_manager); g_clear_signal_handler (&root->priv->stage_removed_id, stage_manager); G_OBJECT_CLASS (cally_root_parent_class)->finalize (object); } /* AtkObject.h */ static void cally_root_initialize (AtkObject *accessible, gpointer data) { ClutterStageManager *stage_manager = NULL; const GSList *iter = NULL; const GSList *stage_list = NULL; ClutterStage *clutter_stage = NULL; AtkObject *cally_stage = NULL; CallyRoot *root = NULL; accessible->role = ATK_ROLE_APPLICATION; accessible->accessible_parent = NULL; /* children initialization */ root = CALLY_ROOT (accessible); stage_manager = CLUTTER_STAGE_MANAGER (data); stage_list = clutter_stage_manager_peek_stages (stage_manager); for (iter = stage_list; iter != NULL; iter = g_slist_next (iter)) { clutter_stage = CLUTTER_STAGE (iter->data); cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (clutter_stage)); atk_object_set_parent (cally_stage, ATK_OBJECT (root)); root->priv->stage_list = g_slist_append (root->priv->stage_list, cally_stage); } root->priv->stage_added_id = g_signal_connect (G_OBJECT (stage_manager), "stage-added", G_CALLBACK (cally_util_stage_added_cb), root); root->priv->stage_removed_id = g_signal_connect (G_OBJECT (stage_manager), "stage-removed", G_CALLBACK (cally_util_stage_removed_cb), root); ATK_OBJECT_CLASS (cally_root_parent_class)->initialize (accessible, data); } static gint cally_root_get_n_children (AtkObject *obj) { CallyRoot *root = CALLY_ROOT (obj); return g_slist_length (root->priv->stage_list); } static AtkObject* cally_root_ref_child (AtkObject *obj, gint i) { CallyRoot *cally_root = NULL; GSList *stage_list = NULL; gint num = 0; AtkObject *item = NULL; cally_root = CALLY_ROOT (obj); stage_list = cally_root->priv->stage_list; num = g_slist_length (stage_list); g_return_val_if_fail ((i < num)&&(i >= 0), NULL); item = g_slist_nth_data (stage_list, i); if (!item) { return NULL; } g_object_ref (item); return item; } static AtkObject* cally_root_get_parent (AtkObject *obj) { return NULL; } static const char * cally_root_get_name (AtkObject *obj) { return g_get_prgname (); } /* -------------------------------- PRIVATE --------------------------------- */ static void cally_util_stage_added_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data) { CallyRoot *root = CALLY_ROOT (data); AtkObject *cally_stage = NULL; gint index = -1; cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); atk_object_set_parent (cally_stage, ATK_OBJECT (root)); root->priv->stage_list = g_slist_append (root->priv->stage_list, cally_stage); index = g_slist_index (root->priv->stage_list, cally_stage); g_signal_emit_by_name (root, "children_changed::add", index, cally_stage, NULL); g_signal_emit_by_name (cally_stage, "create", 0); } static void cally_util_stage_removed_cb (ClutterStageManager *stage_manager, ClutterStage *stage, gpointer data) { CallyRoot *root = CALLY_ROOT (data); AtkObject *cally_stage = NULL; gint index = -1; cally_stage = clutter_actor_get_accessible (CLUTTER_ACTOR (stage)); index = g_slist_index (root->priv->stage_list, cally_stage); root->priv->stage_list = g_slist_remove (root->priv->stage_list, cally_stage); index = g_slist_index (root->priv->stage_list, cally_stage); g_signal_emit_by_name (root, "children_changed::remove", index, cally_stage, NULL); g_signal_emit_by_name (cally_stage, "destroy", 0); } muffin-6.4.1/clutter/clutter/cally/cally-rectangle.h0000664000175000017500000000516214723361714021435 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_RECTANGLE_H__ #define __CALLY_RECTANGLE_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_RECTANGLE (cally_rectangle_get_type ()) #define CALLY_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_RECTANGLE, CallyRectangle)) #define CALLY_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_RECTANGLE, CallyRectangleClass)) #define CALLY_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_RECTANGLE)) #define CALLY_IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_RECTANGLE)) #define CALLY_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_RECTANGLE, CallyRectangleClass)) typedef struct _CallyRectangle CallyRectangle; typedef struct _CallyRectangleClass CallyRectangleClass; typedef struct _CallyRectanglePrivate CallyRectanglePrivate; /** * CallyRectangle: * * The CallyRectangle structure contains only private * data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyRectangle { /*< private >*/ CallyActor parent; CallyRectanglePrivate *priv; }; /** * CallyRectangleClass: * * The CallyRectangleClass structure contains * only private data * * Since: 1.4 */ struct _CallyRectangleClass { /*< private >*/ CallyActorClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_EXPORT GType cally_rectangle_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject* cally_rectangle_new (ClutterActor *actor); G_END_DECLS #endif /* __CALLY_RECTANGLE_H__ */ muffin-6.4.1/clutter/clutter/cally/cally.c0000664000175000017500000000571314723361714017470 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally * @Title: Cally * @short_description: Cally initialization methods. * * Cally initialization methods. * */ #include "clutter-build-config.h" #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "cally.h" #include "cally-actor.h" #include "cally-group.h" #include "cally-stage.h" #include "cally-text.h" #include "cally-rectangle.h" #include "cally-clone.h" #include "cally-factory.h" #include "cally-util.h" #include "clutter.h" #include "clutter-debug.h" #include "clutter-private.h" /* factories initialization*/ CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_ACTOR, cally_actor, cally_actor_new) CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_GROUP, cally_group, cally_group_new) CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_STAGE, cally_stage, cally_stage_new) CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_TEXT, cally_text, cally_text_new) CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_RECTANGLE, cally_rectangle, cally_rectangle_new) CALLY_ACCESSIBLE_FACTORY (CALLY_TYPE_CLONE, cally_clone, cally_clone_new) /** * cally_accessibility_init: * * Initializes the accessibility support. * * Return value: %TRUE if accessibility support has been correctly * initialized. * * Since: 1.4 */ gboolean cally_accessibility_init (void) { /* setting the factories */ CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_ACTOR, cally_actor); CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_GROUP, cally_group); CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_STAGE, cally_stage); CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_TEXT, cally_text); CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_RECTANGLE, cally_rectangle); CALLY_ACTOR_SET_FACTORY (CLUTTER_TYPE_CLONE, cally_clone); /* Initialize the CallyUtility class */ _cally_util_override_atk_util (); CLUTTER_NOTE (MISC, "Clutter Accessibility initialized"); return TRUE; } /** * cally_get_cally_initialized: * * Returns if the accessibility support using cally is enabled. * * Return value: %TRUE if accessibility support has been correctly * initialized. * * Since: 1.4 */ gboolean cally_get_cally_initialized (void) { return !g_strcmp0 (atk_get_toolkit_name (), "clutter"); } muffin-6.4.1/clutter/clutter/cally/cally-group.c0000664000175000017500000000753114723361714020622 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Based on GailContainer from GAIL * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-group * @Title: CallyGroup * @short_description: Implementation of the ATK interfaces for a #ClutterGroup * @see_also: #ClutterGroup * * #CallyGroup implements the required ATK interfaces of #ClutterGroup * In particular it exposes each of the Clutter actors contained in the * group. */ #include "clutter-build-config.h" #include "cally-group.h" #include "cally-actor-private.h" static gint cally_group_get_n_children (AtkObject *obj); static AtkObject* cally_group_ref_child (AtkObject *obj, gint i); static void cally_group_real_initialize (AtkObject *obj, gpointer data); G_DEFINE_TYPE (CallyGroup, cally_group, CALLY_TYPE_ACTOR) static void cally_group_class_init (CallyGroupClass *klass) { /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ AtkObjectClass *class = ATK_OBJECT_CLASS (klass); class->get_n_children = cally_group_get_n_children; class->ref_child = cally_group_ref_child; class->initialize = cally_group_real_initialize; } static void cally_group_init (CallyGroup *group) { /* nothing to do yet */ } /** * cally_group_new: * @actor: a #ClutterGroup * * Creates a #CallyGroup for @actor * * Return value: the newly created #CallyGroup * * Since: 1.4 */ AtkObject * cally_group_new (ClutterActor *actor) { GObject *object = NULL; AtkObject *accessible = NULL; g_return_val_if_fail (CLUTTER_IS_GROUP (actor), NULL); object = g_object_new (CALLY_TYPE_GROUP, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, actor); return accessible; } static gint cally_group_get_n_children (AtkObject *obj) { ClutterActor *actor = NULL; gint count = 0; g_return_val_if_fail (CALLY_IS_GROUP (obj), count); actor = CALLY_GET_CLUTTER_ACTOR (obj); if (actor == NULL) /* defunct */ return 0; g_return_val_if_fail (CLUTTER_IS_GROUP(actor), count); count = clutter_actor_get_n_children (actor); return count; } static AtkObject* cally_group_ref_child (AtkObject *obj, gint i) { AtkObject *accessible = NULL; ClutterActor *actor = NULL; ClutterActor *child = NULL; g_return_val_if_fail (CALLY_IS_GROUP (obj), NULL); g_return_val_if_fail ((i >= 0), NULL); actor = CALLY_GET_CLUTTER_ACTOR (obj); g_return_val_if_fail (CLUTTER_IS_GROUP(actor), NULL); child = clutter_actor_get_child_at_index (actor, i); if (!child) return NULL; accessible = clutter_actor_get_accessible (child); if (accessible != NULL) g_object_ref (accessible); return accessible; } static void cally_group_real_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (cally_group_parent_class)->initialize (obj, data); obj->role = ATK_ROLE_PANEL; } muffin-6.4.1/clutter/clutter/cally/cally-util.h0000664000175000017500000000463414723361714020451 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_UTIL_H__ #define __CALLY_UTIL_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_UTIL (cally_util_get_type ()) #define CALLY_UTIL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_UTIL, CallyUtil)) #define CALLY_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_UTIL, CallyUtilClass)) #define CALLY_IS_UTIL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_UTIL)) #define CALLY_IS_UTIL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_UTIL)) #define CALLY_UTIL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_UTIL, CallyUtilClass)) typedef struct _CallyUtil CallyUtil; typedef struct _CallyUtilClass CallyUtilClass; typedef struct _CallyUtilPrivate CallyUtilPrivate; /** * CallyUtil: * * The CallyUtil structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyUtil { /*< private >*/ AtkUtil parent; CallyUtilPrivate *priv; }; /** * CallyUtilClass: * * The CallyUtilClass structure contains only * private data * * Since: 1.4 */ struct _CallyUtilClass { /*< private >*/ AtkUtilClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_EXPORT GType cally_util_get_type (void) G_GNUC_CONST; void _cally_util_override_atk_util (void); G_END_DECLS #endif /* __CALLY_UTIL_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-text.c0000664000175000017500000023040214723361714020445 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Some parts are based on GailLabel, GailEntry, GailTextView from GAIL * GAIL - The GNOME Accessibility Implementation Library * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * Implementation of atk_text_get_text_[before/at/after]_offset * copied from gtkpango.c, part of GTK+ project * Copyright (c) 2010 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-text * @short_description: Implementation of the ATK interfaces for a #ClutterText * @see_also: #ClutterText * * #CallyText implements the required ATK interfaces of * #ClutterText, #AtkText and #AtkEditableText * * */ #include "clutter-build-config.h" #include "cally-text.h" #include "cally-actor-private.h" #include "clutter-color.h" #include "clutter-main.h" #include "clutter-text.h" static void cally_text_finalize (GObject *obj); /* AtkObject */ static void cally_text_real_initialize (AtkObject *obj, gpointer data); static AtkStateSet* cally_text_ref_state_set (AtkObject *obj); /* atkaction */ static void _cally_text_activate_action (CallyActor *cally_actor); static void _check_activate_action (CallyText *cally_text, ClutterText *clutter_text); /* AtkText */ static void cally_text_text_interface_init (AtkTextIface *iface); static gchar* cally_text_get_text (AtkText *text, gint start_offset, gint end_offset); static gunichar cally_text_get_character_at_offset (AtkText *text, gint offset); static gchar* cally_text_get_text_before_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gchar* cally_text_get_text_at_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gchar* cally_text_get_text_after_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset); static gint cally_text_get_caret_offset (AtkText *text); static gboolean cally_text_set_caret_offset (AtkText *text, gint offset); static gint cally_text_get_character_count (AtkText *text); static gint cally_text_get_n_selections (AtkText *text); static gchar* cally_text_get_selection (AtkText *text, gint selection_num, gint *start_offset, gint *end_offset); static gboolean cally_text_add_selection (AtkText *text, gint start_offset, gint end_offset); static gboolean cally_text_remove_selection (AtkText *text, gint selection_num); static gboolean cally_text_set_selection (AtkText *text, gint selection_num, gint start_offset, gint end_offset); static AtkAttributeSet* cally_text_get_run_attributes (AtkText *text, gint offset, gint *start_offset, gint *end_offset); static AtkAttributeSet* cally_text_get_default_attributes (AtkText *text); static void cally_text_get_character_extents (AtkText *text, gint offset, gint *x, gint *y, gint *width, gint *height, AtkCoordType coords); static gint cally_text_get_offset_at_point (AtkText *text, gint x, gint y, AtkCoordType coords); static void _cally_text_get_selection_bounds (ClutterText *clutter_text, gint *start_offset, gint *end_offset); static void _cally_text_insert_text_cb (ClutterText *clutter_text, gchar *new_text, gint new_text_length, gint *position, gpointer data); static void _cally_text_delete_text_cb (ClutterText *clutter_text, gint start_pos, gint end_pos, gpointer data); static gboolean _idle_notify_insert (gpointer data); static void _notify_insert (CallyText *cally_text); static void _notify_delete (CallyText *cally_text); /* AtkEditableText */ static void cally_text_editable_text_interface_init (AtkEditableTextIface *iface); static void cally_text_set_text_contents (AtkEditableText *text, const gchar *string); static void cally_text_insert_text (AtkEditableText *text, const gchar *string, gint length, gint *position); static void cally_text_delete_text (AtkEditableText *text, gint start_pos, gint end_pos); /* CallyActor */ static void cally_text_notify_clutter (GObject *obj, GParamSpec *pspec); static gboolean _check_for_selection_change (CallyText *cally_text, ClutterText *clutter_text); /* Misc functions */ static AtkAttributeSet* _cally_misc_add_attribute (AtkAttributeSet *attrib_set, AtkTextAttribute attr, gchar *value); static AtkAttributeSet* _cally_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, ClutterText *clutter_text, gint offset, gint *start_offset, gint *end_offset); static AtkAttributeSet* _cally_misc_layout_get_default_attributes (AtkAttributeSet *attrib_set, ClutterText *text); static int _cally_misc_get_index_at_point (ClutterText *clutter_text, gint x, gint y, AtkCoordType coords); struct _CallyTextPrivate { /* Cached ClutterText values*/ gint cursor_position; gint selection_bound; /* text_changed::insert stuff */ const gchar *signal_name_insert; gint position_insert; gint length_insert; guint insert_idle_handler; /* text_changed::delete stuff */ const gchar *signal_name_delete; gint position_delete; gint length_delete; /* action */ guint activate_action_id; }; G_DEFINE_TYPE_WITH_CODE (CallyText, cally_text, CALLY_TYPE_ACTOR, G_ADD_PRIVATE (CallyText) G_IMPLEMENT_INTERFACE (ATK_TYPE_TEXT, cally_text_text_interface_init) G_IMPLEMENT_INTERFACE (ATK_TYPE_EDITABLE_TEXT, cally_text_editable_text_interface_init)); static void cally_text_class_init (CallyTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); AtkObjectClass *class = ATK_OBJECT_CLASS (klass); CallyActorClass *cally_class = CALLY_ACTOR_CLASS (klass); gobject_class->finalize = cally_text_finalize; class->initialize = cally_text_real_initialize; class->ref_state_set = cally_text_ref_state_set; cally_class->notify_clutter = cally_text_notify_clutter; } static void cally_text_init (CallyText *cally_text) { CallyTextPrivate *priv = cally_text_get_instance_private (cally_text); cally_text->priv = priv; priv->cursor_position = 0; priv->selection_bound = 0; priv->signal_name_insert = NULL; priv->position_insert = -1; priv->length_insert = -1; priv->insert_idle_handler = 0; priv->signal_name_delete = NULL; priv->position_delete = -1; priv->length_delete = -1; priv->activate_action_id = 0; } static void cally_text_finalize (GObject *obj) { CallyText *cally_text = CALLY_TEXT (obj); /* g_object_unref (cally_text->priv->textutil); */ /* cally_text->priv->textutil = NULL; */ g_clear_handle_id (&cally_text->priv->insert_idle_handler, g_source_remove); G_OBJECT_CLASS (cally_text_parent_class)->finalize (obj); } /** * cally_text_new: * @actor: a #ClutterActor * * Creates a new #CallyText for the given @actor. @actor must be a * #ClutterText. * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject* cally_text_new (ClutterActor *actor) { GObject *object = NULL; AtkObject *accessible = NULL; g_return_val_if_fail (CLUTTER_IS_TEXT (actor), NULL); object = g_object_new (CALLY_TYPE_TEXT, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, actor); return accessible; } /* atkobject.h */ static void cally_text_real_initialize(AtkObject *obj, gpointer data) { ClutterText *clutter_text = NULL; CallyText *cally_text = NULL; ATK_OBJECT_CLASS (cally_text_parent_class)->initialize (obj, data); g_return_if_fail (CLUTTER_TEXT (data)); cally_text = CALLY_TEXT (obj); clutter_text = CLUTTER_TEXT (data); cally_text->priv->cursor_position = clutter_text_get_cursor_position (clutter_text); cally_text->priv->selection_bound = clutter_text_get_selection_bound (clutter_text); g_signal_connect (clutter_text, "insert-text", G_CALLBACK (_cally_text_insert_text_cb), cally_text); g_signal_connect (clutter_text, "delete-text", G_CALLBACK (_cally_text_delete_text_cb), cally_text); _check_activate_action (cally_text, clutter_text); if (clutter_text_get_password_char (clutter_text) != 0) atk_object_set_role (obj, ATK_ROLE_PASSWORD_TEXT); else atk_object_set_role (obj, ATK_ROLE_TEXT); } static AtkStateSet* cally_text_ref_state_set (AtkObject *obj) { AtkStateSet *result = NULL; ClutterActor *actor = NULL; result = ATK_OBJECT_CLASS (cally_text_parent_class)->ref_state_set (obj); actor = CALLY_GET_CLUTTER_ACTOR (obj); if (actor == NULL) return result; if (clutter_text_get_editable (CLUTTER_TEXT (actor))) atk_state_set_add_state (result, ATK_STATE_EDITABLE); if (clutter_text_get_selectable (CLUTTER_TEXT (actor))) atk_state_set_add_state (result, ATK_STATE_SELECTABLE_TEXT); return result; } /***** pango stuff **** * * FIXME: all this pango related code used to implement * atk_text_get_text_[before/at/after]_offset was copied from GTK, and * should be on a common library (like pango itself). * *********************/ /* * _gtk_pango_move_chars: * @layout: a #PangoLayout * @offset: a character offset in @layout * @count: the number of characters to move from @offset * * Returns the position that is @count characters from the * given @offset. @count may be positive or negative. * * For the purpose of this function, characters are defined * by what Pango considers cursor positions. * * Returns: the new position */ static gint _gtk_pango_move_chars (PangoLayout *layout, gint offset, gint count) { const PangoLogAttr *attrs; gint n_attrs; attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); while (count > 0 && offset < n_attrs - 1) { do offset++; while (offset < n_attrs - 1 && !attrs[offset].is_cursor_position); count--; } while (count < 0 && offset > 0) { do offset--; while (offset > 0 && !attrs[offset].is_cursor_position); count++; } return offset; } /* * _gtk_pango_move_words: * @layout: a #PangoLayout * @offset: a character offset in @layout * @count: the number of words to move from @offset * * Returns the position that is @count words from the * given @offset. @count may be positive or negative. * * If @count is positive, the returned position will * be a word end, otherwise it will be a word start. * See the Pango documentation for details on how * word starts and ends are defined. * * Returns: the new position */ static gint _gtk_pango_move_words (PangoLayout *layout, gint offset, gint count) { const PangoLogAttr *attrs; gint n_attrs; attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); while (count > 0 && offset < n_attrs - 1) { do offset++; while (offset < n_attrs - 1 && !attrs[offset].is_word_end); count--; } while (count < 0 && offset > 0) { do offset--; while (offset > 0 && !attrs[offset].is_word_start); count++; } return offset; } /* * _gtk_pango_move_sentences: * @layout: a #PangoLayout * @offset: a character offset in @layout * @count: the number of sentences to move from @offset * * Returns the position that is @count sentences from the * given @offset. @count may be positive or negative. * * If @count is positive, the returned position will * be a sentence end, otherwise it will be a sentence start. * See the Pango documentation for details on how * sentence starts and ends are defined. * * Returns: the new position */ static gint _gtk_pango_move_sentences (PangoLayout *layout, gint offset, gint count) { const PangoLogAttr *attrs; gint n_attrs; attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); while (count > 0 && offset < n_attrs - 1) { do offset++; while (offset < n_attrs - 1 && !attrs[offset].is_sentence_end); count--; } while (count < 0 && offset > 0) { do offset--; while (offset > 0 && !attrs[offset].is_sentence_start); count++; } return offset; } /* * _gtk_pango_is_inside_word: * @layout: a #PangoLayout * @offset: a character offset in @layout * * Returns whether the given position is inside * a word. * * Returns: %TRUE if @offset is inside a word */ static gboolean _gtk_pango_is_inside_word (PangoLayout *layout, gint offset) { const PangoLogAttr *attrs; gint n_attrs; attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); while (offset >= 0 && !(attrs[offset].is_word_start || attrs[offset].is_word_end)) offset--; if (offset >= 0) return attrs[offset].is_word_start; return FALSE; } /* * _gtk_pango_is_inside_sentence: * @layout: a #PangoLayout * @offset: a character offset in @layout * * Returns whether the given position is inside * a sentence. * * Returns: %TRUE if @offset is inside a sentence */ static gboolean _gtk_pango_is_inside_sentence (PangoLayout *layout, gint offset) { const PangoLogAttr *attrs; gint n_attrs; attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); while (offset >= 0 && !(attrs[offset].is_sentence_start || attrs[offset].is_sentence_end)) offset--; if (offset >= 0) return attrs[offset].is_sentence_start; return FALSE; } static void pango_layout_get_line_before (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { PangoLayoutIter *iter; PangoLayoutLine *line, *prev_line = NULL, *prev_prev_line = NULL; gint index, start_index, end_index; const gchar *text; gboolean found = FALSE; text = pango_layout_get_text (layout); index = g_utf8_offset_to_pointer (text, offset) - text; iter = pango_layout_get_iter (layout); do { line = pango_layout_iter_get_line (iter); start_index = line->start_index; end_index = start_index + line->length; if (index >= start_index && index <= end_index) { /* Found line for offset */ if (prev_line) { switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: end_index = start_index; start_index = prev_line->start_index; break; case ATK_TEXT_BOUNDARY_LINE_END: if (prev_prev_line) start_index = prev_prev_line->start_index + prev_prev_line->length; else start_index = 0; end_index = prev_line->start_index + prev_line->length; break; default: g_assert_not_reached(); } } else start_index = end_index = 0; found = TRUE; break; } prev_prev_line = prev_line; prev_line = line; } while (pango_layout_iter_next_line (iter)); if (!found) { start_index = prev_line->start_index + prev_line->length; end_index = start_index; } pango_layout_iter_free (iter); *start_offset = g_utf8_pointer_to_offset (text, text + start_index); *end_offset = g_utf8_pointer_to_offset (text, text + end_index); } static void pango_layout_get_line_at (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { PangoLayoutIter *iter; PangoLayoutLine *line, *prev_line = NULL; gint index, start_index, end_index; const gchar *text; gboolean found = FALSE; text = pango_layout_get_text (layout); index = g_utf8_offset_to_pointer (text, offset) - text; iter = pango_layout_get_iter (layout); do { line = pango_layout_iter_get_line (iter); start_index = line->start_index; end_index = start_index + line->length; if (index >= start_index && index <= end_index) { /* Found line for offset */ switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: if (pango_layout_iter_next_line (iter)) end_index = pango_layout_iter_get_line (iter)->start_index; break; case ATK_TEXT_BOUNDARY_LINE_END: if (prev_line) start_index = prev_line->start_index + prev_line->length; break; default: g_assert_not_reached(); } found = TRUE; break; } prev_line = line; } while (pango_layout_iter_next_line (iter)); if (!found) { start_index = prev_line->start_index + prev_line->length; end_index = start_index; } pango_layout_iter_free (iter); *start_offset = g_utf8_pointer_to_offset (text, text + start_index); *end_offset = g_utf8_pointer_to_offset (text, text + end_index); } static void pango_layout_get_line_after (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { PangoLayoutIter *iter; PangoLayoutLine *line, *prev_line = NULL; gint index, start_index, end_index; const gchar *text; gboolean found = FALSE; text = pango_layout_get_text (layout); index = g_utf8_offset_to_pointer (text, offset) - text; iter = pango_layout_get_iter (layout); do { line = pango_layout_iter_get_line (iter); start_index = line->start_index; end_index = start_index + line->length; if (index >= start_index && index <= end_index) { /* Found line for offset */ if (pango_layout_iter_next_line (iter)) { line = pango_layout_iter_get_line (iter); switch (boundary_type) { case ATK_TEXT_BOUNDARY_LINE_START: start_index = line->start_index; if (pango_layout_iter_next_line (iter)) end_index = pango_layout_iter_get_line (iter)->start_index; else end_index = start_index + line->length; break; case ATK_TEXT_BOUNDARY_LINE_END: start_index = end_index; end_index = line->start_index + line->length; break; default: g_assert_not_reached(); } } else start_index = end_index; found = TRUE; break; } prev_line = line; } while (pango_layout_iter_next_line (iter)); if (!found) { start_index = prev_line->start_index + prev_line->length; end_index = start_index; } pango_layout_iter_free (iter); *start_offset = g_utf8_pointer_to_offset (text, text + start_index); *end_offset = g_utf8_pointer_to_offset (text, text + end_index); } /* * _gtk_pango_get_text_at: * @layout: a #PangoLayout * @boundary_type: a #AtkTextBoundary * @offset: a character offset in @layout * @start_offset: return location for the start of the returned text * @end_offset: return location for the end of the return text * * Gets a slice of the text from @layout at @offset. * * The @boundary_type determines the size of the returned slice of * text. For the exact semantics of this function, see * atk_text_get_text_after_offset(). * * Returns: a newly allocated string containing a slice of text * from layout. Free with g_free(). */ static gchar * _gtk_pango_get_text_at (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { const gchar *text; gint start, end; const PangoLogAttr *attrs; gint n_attrs; text = pango_layout_get_text (layout); if (text[0] == 0) { *start_offset = 0; *end_offset = 0; return g_strdup (""); } attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); start = offset; end = start; switch (boundary_type) { case ATK_TEXT_BOUNDARY_CHAR: end = _gtk_pango_move_chars (layout, end, 1); break; case ATK_TEXT_BOUNDARY_WORD_START: if (!attrs[start].is_word_start) start = _gtk_pango_move_words (layout, start, -1); if (_gtk_pango_is_inside_word (layout, end)) end = _gtk_pango_move_words (layout, end, 1); while (!attrs[end].is_word_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); break; case ATK_TEXT_BOUNDARY_WORD_END: if (_gtk_pango_is_inside_word (layout, start) && !attrs[start].is_word_start) start = _gtk_pango_move_words (layout, start, -1); while (!attrs[start].is_word_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); end = _gtk_pango_move_words (layout, end, 1); break; case ATK_TEXT_BOUNDARY_SENTENCE_START: if (!attrs[start].is_sentence_start) start = _gtk_pango_move_sentences (layout, start, -1); if (_gtk_pango_is_inside_sentence (layout, end)) end = _gtk_pango_move_sentences (layout, end, 1); while (!attrs[end].is_sentence_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); break; case ATK_TEXT_BOUNDARY_SENTENCE_END: if (_gtk_pango_is_inside_sentence (layout, start) && !attrs[start].is_sentence_start) start = _gtk_pango_move_sentences (layout, start, -1); while (!attrs[start].is_sentence_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); end = _gtk_pango_move_sentences (layout, end, 1); break; case ATK_TEXT_BOUNDARY_LINE_START: case ATK_TEXT_BOUNDARY_LINE_END: pango_layout_get_line_at (layout, boundary_type, offset, &start, &end); break; } *start_offset = start; *end_offset = end; g_assert (start <= end); return g_utf8_substring (text, start, end); } /* * _gtk_pango_get_text_before: * @layout: a #PangoLayout * @boundary_type: a #AtkTextBoundary * @offset: a character offset in @layout * @start_offset: return location for the start of the returned text * @end_offset: return location for the end of the return text * * Gets a slice of the text from @layout before @offset. * * The @boundary_type determines the size of the returned slice of * text. For the exact semantics of this function, see * atk_text_get_text_before_offset(). * * Returns: a newly allocated string containing a slice of text * from layout. Free with g_free(). */ static gchar * _gtk_pango_get_text_before (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { const gchar *text; gint start, end; const PangoLogAttr *attrs; gint n_attrs; text = pango_layout_get_text (layout); if (text[0] == 0) { *start_offset = 0; *end_offset = 0; return g_strdup (""); } attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); start = offset; end = start; switch (boundary_type) { case ATK_TEXT_BOUNDARY_CHAR: start = _gtk_pango_move_chars (layout, start, -1); break; case ATK_TEXT_BOUNDARY_WORD_START: if (!attrs[start].is_word_start) start = _gtk_pango_move_words (layout, start, -1); end = start; start = _gtk_pango_move_words (layout, start, -1); break; case ATK_TEXT_BOUNDARY_WORD_END: if (_gtk_pango_is_inside_word (layout, start) && !attrs[start].is_word_start) start = _gtk_pango_move_words (layout, start, -1); while (!attrs[start].is_word_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); end = start; start = _gtk_pango_move_words (layout, start, -1); while (!attrs[start].is_word_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); break; case ATK_TEXT_BOUNDARY_SENTENCE_START: if (!attrs[start].is_sentence_start) start = _gtk_pango_move_sentences (layout, start, -1); end = start; start = _gtk_pango_move_sentences (layout, start, -1); break; case ATK_TEXT_BOUNDARY_SENTENCE_END: if (_gtk_pango_is_inside_sentence (layout, start) && !attrs[start].is_sentence_start) start = _gtk_pango_move_sentences (layout, start, -1); while (!attrs[start].is_sentence_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); end = start; start = _gtk_pango_move_sentences (layout, start, -1); while (!attrs[start].is_sentence_end && start > 0) start = _gtk_pango_move_chars (layout, start, -1); break; case ATK_TEXT_BOUNDARY_LINE_START: case ATK_TEXT_BOUNDARY_LINE_END: pango_layout_get_line_before (layout, boundary_type, offset, &start, &end); break; } *start_offset = start; *end_offset = end; g_assert (start <= end); return g_utf8_substring (text, start, end); } /* * _gtk_pango_get_text_after: * @layout: a #PangoLayout * @boundary_type: a #AtkTextBoundary * @offset: a character offset in @layout * @start_offset: return location for the start of the returned text * @end_offset: return location for the end of the return text * * Gets a slice of the text from @layout after @offset. * * The @boundary_type determines the size of the returned slice of * text. For the exact semantics of this function, see * atk_text_get_text_after_offset(). * * Returns: a newly allocated string containing a slice of text * from layout. Free with g_free(). */ static gchar * _gtk_pango_get_text_after (PangoLayout *layout, AtkTextBoundary boundary_type, gint offset, gint *start_offset, gint *end_offset) { const gchar *text; gint start, end; const PangoLogAttr *attrs; gint n_attrs; text = pango_layout_get_text (layout); if (text[0] == 0) { *start_offset = 0; *end_offset = 0; return g_strdup (""); } attrs = pango_layout_get_log_attrs_readonly (layout, &n_attrs); start = offset; end = start; switch (boundary_type) { case ATK_TEXT_BOUNDARY_CHAR: start = _gtk_pango_move_chars (layout, start, 1); end = start; end = _gtk_pango_move_chars (layout, end, 1); break; case ATK_TEXT_BOUNDARY_WORD_START: if (_gtk_pango_is_inside_word (layout, end)) end = _gtk_pango_move_words (layout, end, 1); while (!attrs[end].is_word_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); start = end; if (end < n_attrs - 1) { end = _gtk_pango_move_words (layout, end, 1); while (!attrs[end].is_word_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); } break; case ATK_TEXT_BOUNDARY_WORD_END: end = _gtk_pango_move_words (layout, end, 1); start = end; if (end < n_attrs - 1) end = _gtk_pango_move_words (layout, end, 1); break; case ATK_TEXT_BOUNDARY_SENTENCE_START: if (_gtk_pango_is_inside_sentence (layout, end)) end = _gtk_pango_move_sentences (layout, end, 1); while (!attrs[end].is_sentence_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); start = end; if (end < n_attrs - 1) { end = _gtk_pango_move_sentences (layout, end, 1); while (!attrs[end].is_sentence_start && end < n_attrs - 1) end = _gtk_pango_move_chars (layout, end, 1); } break; case ATK_TEXT_BOUNDARY_SENTENCE_END: end = _gtk_pango_move_sentences (layout, end, 1); start = end; if (end < n_attrs - 1) end = _gtk_pango_move_sentences (layout, end, 1); break; case ATK_TEXT_BOUNDARY_LINE_START: case ATK_TEXT_BOUNDARY_LINE_END: pango_layout_get_line_after (layout, boundary_type, offset, &start, &end); break; } *start_offset = start; *end_offset = end; g_assert (start <= end); return g_utf8_substring (text, start, end); } /***** atktext.h ******/ static void cally_text_text_interface_init (AtkTextIface *iface) { g_return_if_fail (iface != NULL); iface->get_text = cally_text_get_text; iface->get_character_at_offset = cally_text_get_character_at_offset; iface->get_text_before_offset = cally_text_get_text_before_offset; iface->get_text_at_offset = cally_text_get_text_at_offset; iface->get_text_after_offset = cally_text_get_text_after_offset; iface->get_character_count = cally_text_get_character_count; iface->get_caret_offset = cally_text_get_caret_offset; iface->set_caret_offset = cally_text_set_caret_offset; iface->get_n_selections = cally_text_get_n_selections; iface->get_selection = cally_text_get_selection; iface->add_selection = cally_text_add_selection; iface->remove_selection = cally_text_remove_selection; iface->set_selection = cally_text_set_selection; iface->get_run_attributes = cally_text_get_run_attributes; iface->get_default_attributes = cally_text_get_default_attributes; iface->get_character_extents = cally_text_get_character_extents; iface->get_offset_at_point = cally_text_get_offset_at_point; } static gchar* cally_text_get_text (AtkText *text, gint start_offset, gint end_offset) { ClutterActor *actor = NULL; PangoLayout *layout = NULL; const gchar *string = NULL; gint character_count = 0; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* Object is defunct */ return NULL; /* we use the pango layout instead of clutter_text_get_chars because it take into account password-char */ layout = clutter_text_get_layout (CLUTTER_TEXT (actor)); string = pango_layout_get_text (layout); character_count = pango_layout_get_character_count (layout); if (end_offset == -1 || end_offset > character_count) end_offset = character_count; if (string[0] == 0) return g_strdup(""); else return g_utf8_substring (string, start_offset, end_offset); } static gunichar cally_text_get_character_at_offset (AtkText *text, gint offset) { ClutterActor *actor = NULL; const gchar *string = NULL; gchar *index = NULL; gunichar unichar; PangoLayout *layout = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return '\0'; /* we use the pango layout instead of clutter_text_get_chars because it take into account password-char */ layout = clutter_text_get_layout (CLUTTER_TEXT (actor)); string = pango_layout_get_text (layout); if (offset >= g_utf8_strlen (string, -1)) { unichar = '\0'; } else { index = g_utf8_offset_to_pointer (string, offset); unichar = g_utf8_get_char (index); } return unichar; } static gchar* cally_text_get_text_before_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; return _gtk_pango_get_text_before (clutter_text_get_layout (CLUTTER_TEXT (actor)), boundary_type, offset, start_offset, end_offset); } static gchar* cally_text_get_text_at_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; return _gtk_pango_get_text_at (clutter_text_get_layout (CLUTTER_TEXT (actor)), boundary_type, offset, start_offset, end_offset); } static gchar* cally_text_get_text_after_offset (AtkText *text, gint offset, AtkTextBoundary boundary_type, gint *start_offset, gint *end_offset) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; return _gtk_pango_get_text_after (clutter_text_get_layout (CLUTTER_TEXT (actor)), boundary_type, offset, start_offset, end_offset); } static gint cally_text_get_caret_offset (AtkText *text) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return -1; return clutter_text_get_cursor_position (CLUTTER_TEXT (actor)); } static gboolean cally_text_set_caret_offset (AtkText *text, gint offset) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return FALSE; clutter_text_set_cursor_position (CLUTTER_TEXT (actor), offset); /* like in gailentry, we suppose that this always works, as clutter text doesn't return anything */ return TRUE; } static gint cally_text_get_character_count (AtkText *text) { ClutterActor *actor = NULL; ClutterText *clutter_text = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return 0; clutter_text = CLUTTER_TEXT (actor); return g_utf8_strlen (clutter_text_get_text (clutter_text), -1); } static gint cally_text_get_n_selections (AtkText *text) { ClutterActor *actor = NULL; gint selection_bound = -1; gint cursor_position = -1; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return 0; if (!clutter_text_get_selectable (CLUTTER_TEXT (actor))) return 0; selection_bound = clutter_text_get_selection_bound (CLUTTER_TEXT (actor)); cursor_position = clutter_text_get_cursor_position (CLUTTER_TEXT (actor)); if (selection_bound == cursor_position) return 0; else return 1; } static gchar* cally_text_get_selection (AtkText *text, gint selection_num, gint *start_offset, gint *end_offset) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; /* As in gailentry, only let the user get the selection if one is set, and if * the selection_num is 0. */ if (selection_num != 0) return NULL; _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), start_offset, end_offset); if (*start_offset != *end_offset) return clutter_text_get_selection (CLUTTER_TEXT (actor)); else return NULL; } /* ClutterText only allows one selection. So this method will set the selection if no selection exists, but as in gailentry, it will not change the current selection */ static gboolean cally_text_add_selection (AtkText *text, gint start_offset, gint end_offset) { ClutterActor *actor; gint select_start, select_end; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return FALSE; _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), &select_start, &select_end); /* Like in gailentry, if there is already a selection, then don't allow another * to be added, since ClutterText only supports one selected region. */ if (select_start == select_end) { clutter_text_set_selection (CLUTTER_TEXT (actor), start_offset, end_offset); return TRUE; } else return FALSE; } static gboolean cally_text_remove_selection (AtkText *text, gint selection_num) { ClutterActor *actor = NULL; gint caret_pos = -1; gint select_start = -1; gint select_end = -1; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return FALSE; /* only one selection is allowed */ if (selection_num != 0) return FALSE; _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), &select_start, &select_end); if (select_start != select_end) { /* Setting the start & end of the selected region to the caret position * turns off the selection. */ caret_pos = clutter_text_get_cursor_position (CLUTTER_TEXT (actor)); clutter_text_set_selection (CLUTTER_TEXT (actor), caret_pos, caret_pos); return TRUE; } else return FALSE; } static gboolean cally_text_set_selection (AtkText *text, gint selection_num, gint start_offset, gint end_offset) { ClutterActor *actor = NULL; gint select_start = -1; gint select_end = -1; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return FALSE; /* Like in gailentry, only let the user move the selection if one is set, * and if the selection_num is 0 */ if (selection_num != 0) return FALSE; _cally_text_get_selection_bounds (CLUTTER_TEXT (actor), &select_start, &select_end); if (select_start != select_end) { clutter_text_set_selection (CLUTTER_TEXT (actor), start_offset, end_offset); return TRUE; } else return FALSE; } static AtkAttributeSet* cally_text_get_run_attributes (AtkText *text, gint offset, gint *start_offset, gint *end_offset) { ClutterActor *actor = NULL; ClutterText *clutter_text = NULL; AtkAttributeSet *at_set = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; /* Clutter don't have any reference to the direction*/ clutter_text = CLUTTER_TEXT (actor); at_set = _cally_misc_layout_get_run_attributes (at_set, clutter_text, offset, start_offset, end_offset); return at_set; } static AtkAttributeSet* cally_text_get_default_attributes (AtkText *text) { ClutterActor *actor = NULL; ClutterText *clutter_text = NULL; AtkAttributeSet *at_set = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return NULL; clutter_text = CLUTTER_TEXT (actor); at_set = _cally_misc_layout_get_default_attributes (at_set, clutter_text); return at_set; } static void cally_text_get_character_extents (AtkText *text, gint offset, gint *xp, gint *yp, gint *widthp, gint *heightp, AtkCoordType coords) { ClutterActor *actor = NULL; ClutterText *clutter_text = NULL; gint x = 0, y = 0, width = 0, height = 0; gint index, x_window, y_window, x_toplevel, y_toplevel; gint x_layout, y_layout; PangoLayout *layout; PangoRectangle extents; const gchar *text_value; graphene_point3d_t verts[4]; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ goto done; clutter_text = CLUTTER_TEXT (actor); text_value = clutter_text_get_text (clutter_text); index = g_utf8_offset_to_pointer (text_value, offset) - text_value; layout = clutter_text_get_layout (clutter_text); pango_layout_index_to_pos (layout, index, &extents); /* handle RTL text layout */ if (extents.width < 0) { extents.x += extents.width; extents.width = -extents.width; } clutter_actor_get_abs_allocation_vertices (actor, verts); x_window = verts[0].x; y_window = verts[0].y; clutter_text_get_layout_offsets (clutter_text, &x_layout, &y_layout); x = (extents.x / PANGO_SCALE) + x_layout + x_window; y = (extents.y / PANGO_SCALE) + y_layout + y_window; width = extents.width / PANGO_SCALE; height = extents.height / PANGO_SCALE; if (coords == ATK_XY_SCREEN) { _cally_actor_get_top_level_origin (actor, &x_toplevel, &y_toplevel); x += x_toplevel; y += y_toplevel; } done: if (widthp) *widthp = width; if (heightp) *heightp = height; if (xp) *xp = x; if (yp) *yp = y; } static gint cally_text_get_offset_at_point (AtkText *text, gint x, gint y, AtkCoordType coords) { ClutterActor *actor = NULL; ClutterText *clutter_text = NULL; const gchar *text_value; gint index; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) /* State is defunct */ return -1; clutter_text = CLUTTER_TEXT (actor); index = _cally_misc_get_index_at_point (clutter_text, x, y, coords); text_value = clutter_text_get_text (clutter_text); if (index == -1) return g_utf8_strlen (text_value, -1); else return g_utf8_pointer_to_offset (text_value, text_value + index); } /******** Auxiliar private methods ******/ /* ClutterText only maintains the current cursor position and a extra selection bound, but this could be before or after the cursor. This method returns the start and end positions in a proper order (so start<=end). This is similar to the function gtk_editable_get_selection_bounds */ static void _cally_text_get_selection_bounds (ClutterText *clutter_text, gint *start_offset, gint *end_offset) { gint pos = -1; gint selection_bound = -1; pos = clutter_text_get_cursor_position (clutter_text); selection_bound = clutter_text_get_selection_bound (clutter_text); if (pos < selection_bound) { *start_offset = pos; *end_offset = selection_bound; } else { *start_offset = selection_bound; *end_offset = pos; } } static void _cally_text_delete_text_cb (ClutterText *clutter_text, gint start_pos, gint end_pos, gpointer data) { CallyText *cally_text = NULL; g_return_if_fail (CALLY_IS_TEXT (data)); /* Ignore zero lengh deletions */ if (end_pos - start_pos == 0) return; cally_text = CALLY_TEXT (data); if (!cally_text->priv->signal_name_delete) { cally_text->priv->signal_name_delete = "text_changed::delete"; cally_text->priv->position_delete = start_pos; cally_text->priv->length_delete = end_pos - start_pos; } _notify_delete (cally_text); } static void _cally_text_insert_text_cb (ClutterText *clutter_text, gchar *new_text, gint new_text_length, gint *position, gpointer data) { CallyText *cally_text = NULL; g_return_if_fail (CALLY_IS_TEXT (data)); cally_text = CALLY_TEXT (data); if (!cally_text->priv->signal_name_insert) { cally_text->priv->signal_name_insert = "text_changed::insert"; cally_text->priv->position_insert = *position; cally_text->priv->length_insert = g_utf8_strlen (new_text, new_text_length); } /* * The signal will be emitted when the cursor position is updated, * or in an idle handler if it not updated. */ if (cally_text->priv->insert_idle_handler == 0) cally_text->priv->insert_idle_handler = clutter_threads_add_idle (_idle_notify_insert, cally_text); } /***** atkeditabletext.h ******/ static void cally_text_editable_text_interface_init (AtkEditableTextIface *iface) { g_return_if_fail (iface != NULL); iface->set_text_contents = cally_text_set_text_contents; iface->insert_text = cally_text_insert_text; iface->delete_text = cally_text_delete_text; iface->set_run_attributes = NULL; iface->copy_text = NULL; iface->cut_text = NULL; iface->paste_text = NULL; } static void cally_text_set_text_contents (AtkEditableText *text, const gchar *string) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) return; if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) return; clutter_text_set_text (CLUTTER_TEXT (actor), string); } static void cally_text_insert_text (AtkEditableText *text, const gchar *string, gint length, gint *position) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) return; if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) return; if (length < 0) length = g_utf8_strlen (string, -1); clutter_text_insert_text (CLUTTER_TEXT (actor), string, *position); /* we suppose that the text insertion will be succesful, clutter-text doesn't warn about it. A option would be search for the text, but it seems not really required */ *position += length; } static void cally_text_delete_text (AtkEditableText *text, gint start_pos, gint end_pos) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (text); if (actor == NULL) return; if (!clutter_text_get_editable (CLUTTER_TEXT (actor))) return; clutter_text_delete_text (CLUTTER_TEXT (actor), start_pos, end_pos); } /* CallyActor */ static void cally_text_notify_clutter (GObject *obj, GParamSpec *pspec) { ClutterText *clutter_text = NULL; CallyText *cally_text = NULL; AtkObject *atk_obj = NULL; clutter_text = CLUTTER_TEXT (obj); atk_obj = clutter_actor_get_accessible (CLUTTER_ACTOR (obj)); cally_text = CALLY_TEXT (atk_obj); if (g_strcmp0 (pspec->name, "position") == 0) { /* the selection can change also for the cursor position */ if (_check_for_selection_change (cally_text, clutter_text)) g_signal_emit_by_name (atk_obj, "text_selection_changed"); g_signal_emit_by_name (atk_obj, "text_caret_moved", clutter_text_get_cursor_position (clutter_text)); } else if (g_strcmp0 (pspec->name, "selection-bound") == 0) { if (_check_for_selection_change (cally_text, clutter_text)) g_signal_emit_by_name (atk_obj, "text_selection_changed"); } else if (g_strcmp0 (pspec->name, "editable") == 0) { atk_object_notify_state_change (atk_obj, ATK_STATE_EDITABLE, clutter_text_get_editable (clutter_text)); } else if (g_strcmp0 (pspec->name, "activatable") == 0) { _check_activate_action (cally_text, clutter_text); } else if (g_strcmp0 (pspec->name, "password-char") == 0) { if (clutter_text_get_password_char (clutter_text) != 0) atk_object_set_role (atk_obj, ATK_ROLE_PASSWORD_TEXT); else atk_object_set_role (atk_obj, ATK_ROLE_TEXT); } else { CALLY_ACTOR_CLASS (cally_text_parent_class)->notify_clutter (obj, pspec); } } static gboolean _check_for_selection_change (CallyText *cally_text, ClutterText *clutter_text) { gboolean ret_val = FALSE; gint clutter_pos = -1; gint clutter_bound = -1; clutter_pos = clutter_text_get_cursor_position (clutter_text); clutter_bound = clutter_text_get_selection_bound (clutter_text); if (clutter_pos != clutter_bound) { if (clutter_pos != cally_text->priv->cursor_position || clutter_bound != cally_text->priv->selection_bound) /* * This check is here as this function can be called for * notification of selection_bound and current_pos. The * values of current_pos and selection_bound may be the same * for both notifications and we only want to generate one * text_selection_changed signal. */ ret_val = TRUE; } else { /* We had a selection */ ret_val = (cally_text->priv->cursor_position != cally_text->priv->selection_bound); } cally_text->priv->cursor_position = clutter_pos; cally_text->priv->selection_bound = clutter_bound; return ret_val; } static gboolean _idle_notify_insert (gpointer data) { CallyText *cally_text = NULL; cally_text = CALLY_TEXT (data); cally_text->priv->insert_idle_handler = 0; _notify_insert (cally_text); return FALSE; } static void _notify_insert (CallyText *cally_text) { if (cally_text->priv->signal_name_insert) { g_signal_emit_by_name (cally_text, cally_text->priv->signal_name_insert, cally_text->priv->position_insert, cally_text->priv->length_insert); cally_text->priv->signal_name_insert = NULL; } } static void _notify_delete (CallyText *cally_text) { if (cally_text->priv->signal_name_delete) { g_signal_emit_by_name (cally_text, cally_text->priv->signal_name_delete, cally_text->priv->position_delete, cally_text->priv->length_delete); cally_text->priv->signal_name_delete = NULL; } } /* atkaction */ static void _cally_text_activate_action (CallyActor *cally_actor) { ClutterActor *actor = NULL; actor = CALLY_GET_CLUTTER_ACTOR (cally_actor); clutter_text_activate (CLUTTER_TEXT (actor)); } static void _check_activate_action (CallyText *cally_text, ClutterText *clutter_text) { if (clutter_text_get_activatable (clutter_text)) { if (cally_text->priv->activate_action_id != 0) return; cally_text->priv->activate_action_id = cally_actor_add_action (CALLY_ACTOR (cally_text), "activate", NULL, NULL, _cally_text_activate_action); } else { if (cally_text->priv->activate_action_id == 0) return; if (cally_actor_remove_action (CALLY_ACTOR (cally_text), cally_text->priv->activate_action_id)) { cally_text->priv->activate_action_id = 0; } } } /* GailTextUtil/GailMisc reimplementation methods */ /** * _cally_misc_add_attribute: * * Reimplementation of gail_misc_layout_get_run_attributes (check this * function for more documentation). * * Returns: A pointer to the new #AtkAttributeSet. **/ static AtkAttributeSet* _cally_misc_add_attribute (AtkAttributeSet *attrib_set, AtkTextAttribute attr, gchar *value) { AtkAttributeSet *return_set; AtkAttribute *at = g_malloc (sizeof (AtkAttribute)); at->name = g_strdup (atk_text_attribute_get_name (attr)); at->value = value; return_set = g_slist_prepend(attrib_set, at); return return_set; } static gint _cally_atk_attribute_lookup_func (gconstpointer data, gconstpointer user_data) { AtkTextAttribute attr = (AtkTextAttribute) user_data; AtkAttribute *at = (AtkAttribute *) data; if (!g_strcmp0 (at->name, atk_text_attribute_get_name (attr))) return 0; return -1; } static gboolean _cally_misc_find_atk_attribute (AtkAttributeSet *attrib_set, AtkTextAttribute attr) { GSList* result = g_slist_find_custom ((GSList*) attrib_set, (gconstpointer) attr, _cally_atk_attribute_lookup_func); return (result != NULL); } /** * _cally_misc_layout_atk_attributes_from_pango: * * Store the pango attributes as their ATK equivalent in an existing * #AtkAttributeSet. * * Returns: A pointer to the updated #AtkAttributeSet. **/ static AtkAttributeSet* _cally_misc_layout_atk_attributes_from_pango (AtkAttributeSet *attrib_set, PangoAttrIterator *iter) { PangoAttrString *pango_string; PangoAttrInt *pango_int; PangoAttrColor *pango_color; PangoAttrLanguage *pango_lang; PangoAttrFloat *pango_float; gchar *value = NULL; if ((pango_string = (PangoAttrString*) pango_attr_iterator_get (iter, PANGO_ATTR_FAMILY)) != NULL) { value = g_strdup_printf("%s", pango_string->value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STYLE)) != NULL) { attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STYLE, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_WEIGHT)) != NULL) { value = g_strdup_printf("%i", pango_int->value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_VARIANT)) != NULL) { attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_VARIANT, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRETCH)) != NULL) { attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STRETCH, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_SIZE)) != NULL) { value = g_strdup_printf("%i", pango_int->value / PANGO_SCALE); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_UNDERLINE)) != NULL) { attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_UNDERLINE, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_UNDERLINE, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_STRIKETHROUGH)) != NULL) { attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STRIKETHROUGH, g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRIKETHROUGH, pango_int->value))); } if ((pango_int = (PangoAttrInt*) pango_attr_iterator_get (iter, PANGO_ATTR_RISE)) != NULL) { value = g_strdup_printf("%i", pango_int->value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_RISE, value); } if ((pango_lang = (PangoAttrLanguage*) pango_attr_iterator_get (iter, PANGO_ATTR_LANGUAGE)) != NULL) { value = g_strdup( pango_language_to_string( pango_lang->value)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value); } if ((pango_float = (PangoAttrFloat*) pango_attr_iterator_get (iter, PANGO_ATTR_SCALE)) != NULL) { value = g_strdup_printf("%g", pango_float->value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SCALE, value); } if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_FOREGROUND)) != NULL) { value = g_strdup_printf ("%u,%u,%u", pango_color->color.red, pango_color->color.green, pango_color->color.blue); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value); } if ((pango_color = (PangoAttrColor*) pango_attr_iterator_get (iter, PANGO_ATTR_BACKGROUND)) != NULL) { value = g_strdup_printf ("%u,%u,%u", pango_color->color.red, pango_color->color.green, pango_color->color.blue); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_BG_COLOR, value); } return attrib_set; } static AtkAttributeSet* _cally_misc_add_actor_color_to_attribute_set (AtkAttributeSet *attrib_set, ClutterText *clutter_text) { ClutterColor color; gchar *value; clutter_text_get_color (clutter_text, &color); value = g_strdup_printf ("%u,%u,%u", (guint) (color.red * 65535 / 255), (guint) (color.green * 65535 / 255), (guint) (color.blue * 65535 / 255)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR, value); return attrib_set; } /** * _cally_misc_layout_get_run_attributes: * * Reimplementation of gail_misc_layout_get_run_attributes (check this * function for more documentation). * * Returns: A pointer to the #AtkAttributeSet. **/ static AtkAttributeSet* _cally_misc_layout_get_run_attributes (AtkAttributeSet *attrib_set, ClutterText *clutter_text, gint offset, gint *start_offset, gint *end_offset) { PangoAttrIterator *iter; PangoAttrList *attr; gint index, start_index, end_index; gboolean is_next = TRUE; glong len; PangoLayout *layout = clutter_text_get_layout (clutter_text); gchar *text = (gchar*) clutter_text_get_text (clutter_text); len = g_utf8_strlen (text, -1); /* Grab the attributes of the PangoLayout, if any */ if ((attr = pango_layout_get_attributes (layout)) == NULL) { *start_offset = 0; *end_offset = len; _cally_misc_add_actor_color_to_attribute_set (attrib_set, clutter_text); } else { iter = pango_attr_list_get_iterator (attr); /* Get invariant range offsets */ /* If offset out of range, set offset in range */ if (offset > len) offset = len; else if (offset < 0) offset = 0; index = g_utf8_offset_to_pointer (text, offset) - text; pango_attr_iterator_range (iter, &start_index, &end_index); while (is_next) { if (index >= start_index && index < end_index) { *start_offset = g_utf8_pointer_to_offset (text, text + start_index); if (end_index == G_MAXINT) /* Last iterator */ end_index = len; *end_offset = g_utf8_pointer_to_offset (text, text + end_index); break; } is_next = pango_attr_iterator_next (iter); pango_attr_iterator_range (iter, &start_index, &end_index); } /* Get attributes */ attrib_set = _cally_misc_layout_atk_attributes_from_pango (attrib_set, iter); pango_attr_iterator_destroy (iter); } if (!_cally_misc_find_atk_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR)) attrib_set = _cally_misc_add_actor_color_to_attribute_set (attrib_set, clutter_text); return attrib_set; } /** * _cally_misc_layout_get_default_attributes: * * Reimplementation of gail_misc_layout_get_default_attributes (check this * function for more documentation). * * Returns: A pointer to the #AtkAttributeSet. **/ static AtkAttributeSet* _cally_misc_layout_get_default_attributes (AtkAttributeSet *attrib_set, ClutterText *clutter_text) { PangoLayout *layout; PangoContext *context; PangoLanguage* language; PangoFontDescription* font; PangoWrapMode mode; gchar *value = NULL; gint int_value; ClutterTextDirection text_direction; PangoAttrIterator *iter; PangoAttrList *attr; text_direction = clutter_actor_get_text_direction (CLUTTER_ACTOR (clutter_text)); switch (text_direction) { case CLUTTER_TEXT_DIRECTION_DEFAULT: value = g_strdup ("none"); break; case CLUTTER_TEXT_DIRECTION_LTR: value = g_strdup ("ltr"); break; case CLUTTER_TEXT_DIRECTION_RTL: value = g_strdup ("rtl"); break; default: value = g_strdup ("none"); break; } attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_DIRECTION, value); layout = clutter_text_get_layout (clutter_text); context = pango_layout_get_context (layout); if (context) { if ((language = pango_context_get_language (context))) { value = g_strdup (pango_language_to_string (language)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LANGUAGE, value); } if ((font = pango_context_get_font_description (context))) { value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STYLE, pango_font_description_get_style (font))); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STYLE, value); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_VARIANT, pango_font_description_get_variant (font))); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_VARIANT, value); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_STRETCH, pango_font_description_get_stretch (font))); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_STRETCH, value); value = g_strdup (pango_font_description_get_family (font)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FAMILY_NAME, value); value = g_strdup_printf ("%d", pango_font_description_get_weight (font)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_WEIGHT, value); value = g_strdup_printf ("%i", pango_font_description_get_size (font) / PANGO_SCALE); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_SIZE, value); } } if (pango_layout_get_justify (layout)) int_value = 3; else { PangoAlignment align; align = pango_layout_get_alignment (layout); if (align == PANGO_ALIGN_LEFT) int_value = 0; else if (align == PANGO_ALIGN_CENTER) int_value = 2; else /* if (align == PANGO_ALIGN_RIGHT) */ int_value = 1; } value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_JUSTIFICATION, int_value)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_JUSTIFICATION, value); mode = pango_layout_get_wrap (layout); if (mode == PANGO_WRAP_WORD) int_value = 2; else /* if (mode == PANGO_WRAP_CHAR) */ int_value = 1; value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_WRAP_MODE, int_value)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_WRAP_MODE, value); if ((attr = clutter_text_get_attributes (clutter_text))) { iter = pango_attr_list_get_iterator (attr); /* Get attributes */ attrib_set = _cally_misc_layout_atk_attributes_from_pango (attrib_set, iter); pango_attr_iterator_destroy (iter); } if (!_cally_misc_find_atk_attribute (attrib_set, ATK_TEXT_ATTR_FG_COLOR)) attrib_set = _cally_misc_add_actor_color_to_attribute_set (attrib_set, clutter_text); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_FG_STIPPLE, 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_FG_STIPPLE, value); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_BG_STIPPLE, 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_BG_STIPPLE, value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_BG_FULL_HEIGHT, g_strdup_printf ("%i", 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_INSIDE_WRAP, g_strdup_printf ("%i", 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_BELOW_LINES, g_strdup_printf ("%i", 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_PIXELS_ABOVE_LINES, g_strdup_printf ("%i", 0)); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_EDITABLE, clutter_text_get_editable (clutter_text))); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_EDITABLE, value); value = g_strdup (atk_text_attribute_get_value (ATK_TEXT_ATTR_INVISIBLE, !clutter_actor_is_visible (CLUTTER_ACTOR (clutter_text)))); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_INVISIBLE, value); value = g_strdup_printf ("%i", pango_layout_get_indent (layout)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_INDENT, value); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_RIGHT_MARGIN, g_strdup_printf ("%i", 0)); attrib_set = _cally_misc_add_attribute (attrib_set, ATK_TEXT_ATTR_LEFT_MARGIN, g_strdup_printf ("%i", 0)); return attrib_set; } static int _cally_misc_get_index_at_point (ClutterText *clutter_text, gint x, gint y, AtkCoordType coords) { gint index, x_window, y_window, x_toplevel, y_toplevel; gint x_temp, y_temp; gboolean ret; graphene_point3d_t verts[4]; PangoLayout *layout; gint x_layout, y_layout; clutter_text_get_layout_offsets (clutter_text, &x_layout, &y_layout); clutter_actor_get_abs_allocation_vertices (CLUTTER_ACTOR (clutter_text), verts); x_window = verts[0].x; y_window = verts[0].y; x_temp = x - x_layout - x_window; y_temp = y - y_layout - y_window; if (coords == ATK_XY_SCREEN) { _cally_actor_get_top_level_origin (CLUTTER_ACTOR (clutter_text), &x_toplevel, &y_toplevel); x_temp -= x_toplevel; y_temp -= y_toplevel; } layout = clutter_text_get_layout (clutter_text); ret = pango_layout_xy_to_index (layout, x_temp * PANGO_SCALE, y_temp * PANGO_SCALE, &index, NULL); if (!ret) { if (x_temp < 0 || y_temp < 0) index = 0; else index = -1; } return index; } muffin-6.4.1/clutter/clutter/cally/cally-actor.h0000664000175000017500000001315414723361714020601 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * Some parts are based on GailWidget from GAIL * GAIL - The GNOME Accessibility Implementation Library * Copyright 2001, 2002, 2003 Sun Microsystems Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_ACTOR_H__ #define __CALLY_ACTOR_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_ACTOR (cally_actor_get_type ()) #define CALLY_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_ACTOR, CallyActor)) #define CALLY_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_ACTOR, CallyActorClass)) #define CALLY_IS_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_ACTOR)) #define CALLY_IS_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_ACTOR)) #define CALLY_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_ACTOR, CallyActorClass)) typedef struct _CallyActor CallyActor; typedef struct _CallyActorClass CallyActorClass; typedef struct _CallyActorPrivate CallyActorPrivate; /** * CallyActionFunc: * @cally_actor: a #CallyActor * * Action function, to be used on #AtkAction implementations as a individual * action * * Since: 1.4 */ typedef void (* CallyActionFunc) (CallyActor *cally_actor); /** * CallyActionCallback: * @cally_actor: a #CallyActor * @user_data: user data passed to the function * * Action function, to be used on #AtkAction implementations as * an individual action. Unlike #CallyActionFunc, this function * uses the @user_data argument passed to cally_actor_add_action_full(). * * Since: 1.6 */ typedef void (* CallyActionCallback) (CallyActor *cally_actor, gpointer user_data); /** * CallyActor: * * The CallyActor structure contains only private * data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyActor { /*< private >*/ AtkGObjectAccessible parent; CallyActorPrivate *priv; }; /** * CallyActorClass: * @notify_clutter: Signal handler for notify signal on Clutter actor * @focus_clutter: Signal handler for key-focus-in and key-focus-out * signal on Clutter actor. This virtual functions is deprecated. * @add_actor: Signal handler for actor-added signal on * ClutterContainer interface * @remove_actor: Signal handler for actor-added signal on * ClutterContainer interface * * The CallyActorClass structure contains * only private data * * Since: 1.4 */ struct _CallyActorClass { /*< private >*/ AtkGObjectAccessibleClass parent_class; /*< public >*/ void (*notify_clutter) (GObject *object, GParamSpec *pspec); gboolean (*focus_clutter) (ClutterActor *actor, gpointer data); gint (*add_actor) (ClutterActor *container, ClutterActor *actor, gpointer data); gint (*remove_actor) (ClutterActor *container, ClutterActor *actor, gpointer data); /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[32]; }; CLUTTER_EXPORT GType cally_actor_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject* cally_actor_new (ClutterActor *actor); CLUTTER_EXPORT guint cally_actor_add_action (CallyActor *cally_actor, const gchar *action_name, const gchar *action_description, const gchar *action_keybinding, CallyActionFunc action_func); CLUTTER_EXPORT guint cally_actor_add_action_full (CallyActor *cally_actor, const gchar *action_name, const gchar *action_description, const gchar *action_keybinding, CallyActionCallback callback, gpointer user_data, GDestroyNotify notify); CLUTTER_EXPORT gboolean cally_actor_remove_action (CallyActor *cally_actor, gint action_id); CLUTTER_EXPORT gboolean cally_actor_remove_action_by_name (CallyActor *cally_actor, const gchar *action_name); G_END_DECLS #endif /* __CALLY_ACTOR_H__ */ muffin-6.4.1/clutter/clutter/cally/cally.h0000664000175000017500000000227314723361714017473 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2008 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_H__ #define __CALLY_H__ #define __CALLY_H_INSIDE__ #include "cally-actor.h" #include "cally-clone.h" #include "cally-factory.h" #include "cally-group.h" #include "cally-main.h" #include "cally-rectangle.h" #include "cally-root.h" #include "cally-stage.h" #include "cally-text.h" #include "cally-util.h" #undef __CALLY_H_INSIDE__ #endif /* __CALLY_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-root.h0000664000175000017500000000470614723361714020457 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_ROOT_H__ #define __CALLY_ROOT_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_ROOT (cally_root_get_type ()) #define CALLY_ROOT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_ROOT, CallyRoot)) #define CALLY_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_ROOT, CallyRootClass)) #define CALLY_IS_ROOT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_ROOT)) #define CALLY_IS_ROOT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_ROOT)) #define CALLY_ROOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_ROOT, CallyRootClass)) typedef struct _CallyRoot CallyRoot; typedef struct _CallyRootClass CallyRootClass; typedef struct _CallyRootPrivate CallyRootPrivate; /** * CallyRoot: * * The CallyRoot structure contains only private * data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyRoot { /*< private >*/ AtkGObjectAccessible parent; CallyRootPrivate *priv; }; /** * CallyRootClass: * * The CallyRootClass structure contains only * private data * * Since: 1.4 */ struct _CallyRootClass { /*< private >*/ AtkGObjectAccessibleClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[16]; }; CLUTTER_EXPORT GType cally_root_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject *cally_root_new (void); G_END_DECLS #endif /* __CALLY_ROOT_H__ */ muffin-6.4.1/clutter/clutter/cally/cally-rectangle.c0000664000175000017500000000535414723361714021433 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /** * SECTION:cally-rectangle * @short_description: Implementation of the ATK interfaces for a #ClutterRectangle * @see_also: #ClutterRectangle * * #CallyRectangle implements the required ATK interfaces of #ClutterRectangle * * In particular it sets a proper role for the rectangle. */ #include "clutter-build-config.h" #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "cally-rectangle.h" #include "cally-actor-private.h" #include "clutter-color.h" #include "deprecated/clutter-rectangle.h" /* AtkObject */ static void cally_rectangle_real_initialize (AtkObject *obj, gpointer data); G_DEFINE_TYPE (CallyRectangle, cally_rectangle, CALLY_TYPE_ACTOR) static void cally_rectangle_class_init (CallyRectangleClass *klass) { /* GObjectClass *gobject_class = G_OBJECT_CLASS (klass); */ AtkObjectClass *class = ATK_OBJECT_CLASS (klass); class->initialize = cally_rectangle_real_initialize; } static void cally_rectangle_init (CallyRectangle *rectangle) { /* nothing to do yet */ } /** * cally_rectangle_new: * @actor: a #ClutterActor * * Creates a new #CallyRectangle for the given @actor. @actor must be * a #ClutterRectangle. * * Return value: the newly created #AtkObject * * Since: 1.4 */ AtkObject* cally_rectangle_new (ClutterActor *actor) { GObject *object = NULL; AtkObject *accessible = NULL; g_return_val_if_fail (CLUTTER_IS_RECTANGLE (actor), NULL); object = g_object_new (CALLY_TYPE_RECTANGLE, NULL); accessible = ATK_OBJECT (object); atk_object_initialize (accessible, actor); return accessible; } static void cally_rectangle_real_initialize (AtkObject *obj, gpointer data) { ATK_OBJECT_CLASS (cally_rectangle_parent_class)->initialize (obj, data); obj->role = ATK_ROLE_IMAGE; } muffin-6.4.1/clutter/clutter/cally/cally-text.h0000664000175000017500000000477514723361714020466 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CALLY_TEXT_H__ #define __CALLY_TEXT_H__ #if !defined(__CALLY_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CALLY_TYPE_TEXT (cally_text_get_type ()) #define CALLY_TEXT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CALLY_TYPE_TEXT, CallyText)) #define CALLY_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CALLY_TYPE_TEXT, CallyTextClass)) #define CALLY_IS_TEXT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CALLY_TYPE_TEXT)) #define CALLY_IS_TEXT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CALLY_TYPE_TEXT)) #define CALLY_TEXT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CALLY_TYPE_TEXT, CallyTextClass)) typedef struct _CallyText CallyText; typedef struct _CallyTextClass CallyTextClass; typedef struct _CallyTextPrivate CallyTextPrivate; /** * CallyText: * * The CallyText structure contains only private * data and should be accessed using the provided API * * Since: 1.4 */ struct _CallyText { /*< private >*/ CallyActor parent; CallyTextPrivate *priv; }; /** * CallyTextClass: * * The CallyTextClass structure contains only * private data * * Since: 1.4 */ struct _CallyTextClass { /*< private >*/ CallyActorClass parent_class; /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_EXPORT GType cally_text_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT AtkObject* cally_text_new (ClutterActor *actor); G_END_DECLS #endif /* __CALLY_TEXT_H__ */ muffin-6.4.1/clutter/clutter/clutter-grid-layout.c0000664000175000017500000017654014723361714021207 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Red Hat, Inc. * Copyright (C) 2012 Bastian Winkler * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Bastian Winkler * * Based on GtkGrid widget by: * Matthias Clasen */ #include "clutter-build-config.h" #include #include #include "clutter-grid-layout.h" #include "clutter-actor-private.h" #include "clutter-container.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-layout-meta.h" #include "clutter-private.h" /** * SECTION:clutter-grid-layout * @Short_description: A layout manager for a grid of actors * @Title: ClutterGridLayout * @See_also: #ClutterBoxLayout * * #ClutterGridLayout is a layout manager which arranges its child widgets in * rows and columns. It is a very similar to #ClutterBoxLayout, but it * consistently uses #ClutterActor's alignment and expansion flags instead of * custom child properties. * * Children are added using clutter_grid_layout_attach(). They can span * multiple rows or columns. It is also possible to add a child next to an * existing child, using clutter_grid_layout_attach_next_to(). The behaviour of * #ClutterGridLayout when several children occupy the same grid cell is undefined. * * #ClutterGridLayout can be used like a #ClutterBoxLayout by just using * clutter_actor_add_child(), which will place children next to each other in * the direction determined by the #ClutterGridLayout:orientation property. */ #define CLUTTER_TYPE_GRID_CHILD (clutter_grid_child_get_type ()) #define CLUTTER_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GRID_CHILD, ClutterGridChild)) #define CLUTTER_IS_GRID_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GRID_CHILD)) typedef struct _ClutterGridChild ClutterGridChild; typedef struct _ClutterLayoutMetaClass ClutterGridChildClass; typedef struct _ClutterGridAttach ClutterGridAttach; typedef struct _ClutterGridLine ClutterGridLine; typedef struct _ClutterGridLines ClutterGridLines; typedef struct _ClutterGridLineData ClutterGridLineData; typedef struct _ClutterGridRequest ClutterGridRequest; struct _ClutterGridAttach { gint pos; gint span; }; struct _ClutterGridChild { ClutterLayoutMeta parent_instance; ClutterGridAttach attach[2]; }; #define CHILD_LEFT(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].pos) #define CHILD_WIDTH(child) ((child)->attach[CLUTTER_ORIENTATION_HORIZONTAL].span) #define CHILD_TOP(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].pos) #define CHILD_HEIGHT(child) ((child)->attach[CLUTTER_ORIENTATION_VERTICAL].span) /* A ClutterGridLineData struct contains row/column specific parts * of the grid. */ struct _ClutterGridLineData { gfloat spacing; guint homogeneous : 1; }; struct _ClutterGridLayoutPrivate { ClutterContainer *container; ClutterOrientation orientation; ClutterGridLineData linedata[2]; }; #define ROWS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_HORIZONTAL]) #define COLUMNS(priv) (&(priv)->linedata[CLUTTER_ORIENTATION_VERTICAL]) /* A ClutterGridLine struct represents a single row or column * during size requests */ struct _ClutterGridLine { gfloat minimum; gfloat natural; gfloat position; gfloat allocation; guint need_expand : 1; guint expand : 1; guint empty : 1; }; struct _ClutterGridLines { ClutterGridLine *lines; gint min, max; }; struct _ClutterGridRequest { ClutterGridLayout *grid; ClutterGridLines lines[2]; }; enum { PROP_0, PROP_ORIENTATION, PROP_ROW_SPACING, PROP_COLUMN_SPACING, PROP_ROW_HOMOGENEOUS, PROP_COLUMN_HOMOGENEOUS, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { PROP_CHILD_0, PROP_CHILD_LEFT_ATTACH, PROP_CHILD_TOP_ATTACH, PROP_CHILD_WIDTH, PROP_CHILD_HEIGHT, PROP_CHILD_LAST }; static GParamSpec *child_props[PROP_CHILD_LAST]; GType clutter_grid_child_get_type (void); G_DEFINE_TYPE (ClutterGridChild, clutter_grid_child, CLUTTER_TYPE_LAYOUT_META) G_DEFINE_TYPE_WITH_PRIVATE (ClutterGridLayout, clutter_grid_layout, CLUTTER_TYPE_LAYOUT_MANAGER) #define GET_GRID_CHILD(grid, child) \ (CLUTTER_GRID_CHILD(clutter_layout_manager_get_child_meta \ (CLUTTER_LAYOUT_MANAGER((grid)),\ CLUTTER_GRID_LAYOUT((grid))->priv->container,(child)))) static void grid_attach (ClutterGridLayout *self, ClutterActor *actor, gint left, gint top, gint width, gint height) { ClutterGridChild *grid_child; grid_child = GET_GRID_CHILD (self, actor); CHILD_LEFT (grid_child) = left; CHILD_TOP (grid_child) = top; CHILD_WIDTH (grid_child) = width; CHILD_HEIGHT (grid_child) = height; } /* Find the position 'touching' existing * children. @orientation and @max determine * from which direction to approach (horizontal * + max = right, vertical + !max = top, etc). * @op_pos, @op_span determine the rows/columns * in which the touching has to happen. */ static gint find_attach_position (ClutterGridLayout *self, ClutterOrientation orientation, gint op_pos, gint op_span, gboolean max) { ClutterGridLayoutPrivate *priv = self->priv; ClutterGridChild *grid_child; ClutterGridAttach *attach; ClutterGridAttach *opposite; ClutterActorIter iter; ClutterActor *child; gint pos; gboolean hit; if (max) pos = -G_MAXINT; else pos = G_MAXINT; hit = FALSE; if (!priv->container) return -1; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (self, child); attach = &grid_child->attach[orientation]; opposite = &grid_child->attach[1 - orientation]; /* check if the ranges overlap */ if (opposite->pos <= op_pos + op_span && op_pos <= opposite->pos + opposite->span) { hit = TRUE; if (max) pos = MAX (pos, attach->pos + attach->span); else pos = MIN (pos, attach->pos); } } if (!hit) pos = 0; return pos; } static void grid_attach_next_to (ClutterGridLayout *layout, ClutterActor *child, ClutterActor *sibling, ClutterGridPosition side, gint width, gint height) { ClutterGridChild *grid_sibling; gint left, top; if (sibling) { grid_sibling = GET_GRID_CHILD (layout, sibling); switch (side) { case CLUTTER_GRID_POSITION_LEFT: left = CHILD_LEFT (grid_sibling) - width; top = CHILD_TOP (grid_sibling); break; case CLUTTER_GRID_POSITION_RIGHT: left = CHILD_LEFT (grid_sibling) + CHILD_WIDTH (grid_sibling); top = CHILD_TOP (grid_sibling); break; case CLUTTER_GRID_POSITION_TOP: left = CHILD_LEFT (grid_sibling); top = CHILD_TOP (grid_sibling) - height; break; case CLUTTER_GRID_POSITION_BOTTOM: left = CHILD_LEFT (grid_sibling); top = CHILD_TOP (grid_sibling) + CHILD_HEIGHT (grid_sibling); break; default: g_assert_not_reached (); } } else { switch (side) { case CLUTTER_GRID_POSITION_LEFT: left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL, 0, height, FALSE); left -= width; top = 0; break; case CLUTTER_GRID_POSITION_RIGHT: left = find_attach_position (layout, CLUTTER_ORIENTATION_HORIZONTAL, 0, height, TRUE); top = 0; break; case CLUTTER_GRID_POSITION_TOP: left = 0; top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL, 0, width, FALSE); top -= height; break; case CLUTTER_GRID_POSITION_BOTTOM: left = 0; top = find_attach_position (layout, CLUTTER_ORIENTATION_VERTICAL, 0, width, TRUE); break; default: g_assert_not_reached (); } } grid_attach (layout, child, left, top, width, height); } static void clutter_grid_request_update_child_attach (ClutterGridRequest *request, ClutterActor *actor) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; grid_child = GET_GRID_CHILD (request->grid, actor); if (CHILD_LEFT (grid_child) == -1 || CHILD_TOP (grid_child) == -1) { ClutterGridPosition side; ClutterActor *sibling; if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL) { ClutterTextDirection td; gboolean rtl; ClutterActor *container = CLUTTER_ACTOR (priv->container); td = clutter_actor_get_text_direction (container); rtl = (td == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE; side = rtl ? CLUTTER_GRID_POSITION_LEFT : CLUTTER_GRID_POSITION_RIGHT; } else { /* XXX: maybe we should also add a :pack-start property to modify * this */ side = CLUTTER_GRID_POSITION_BOTTOM; } sibling = clutter_actor_get_previous_sibling (actor); if (sibling) clutter_grid_layout_insert_next_to (request->grid, sibling, side); grid_attach_next_to (request->grid, actor, sibling, side, CHILD_WIDTH (grid_child), CHILD_HEIGHT (grid_child)); } } static void clutter_grid_request_update_attach (ClutterGridRequest *request) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterActorIter iter; ClutterActor *child; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) clutter_grid_request_update_child_attach (request, child); } /* Calculates the min and max numbers for both orientations. */ static void clutter_grid_request_count_lines (ClutterGridRequest *request) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterGridAttach *attach; ClutterActorIter iter; ClutterActor *child; gint min[2]; gint max[2]; min[0] = min[1] = G_MAXINT; max[0] = max[1] = G_MININT; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (request->grid, child); attach = grid_child->attach; min[0] = MIN (min[0], attach[0].pos); max[0] = MAX (max[0], attach[0].pos + attach[0].span); min[1] = MIN (min[1], attach[1].pos); max[1] = MAX (max[1], attach[1].pos + attach[1].span); } request->lines[0].min = min[0]; request->lines[0].max = max[0]; request->lines[1].min = min[1]; request->lines[1].max = max[1]; } /* Sets line sizes to 0 and marks lines as expand * if they have a non-spanning expanding child. */ static void clutter_grid_request_init (ClutterGridRequest *request, ClutterOrientation orientation) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterGridAttach *attach; ClutterGridLines *lines; ClutterActorIter iter; ClutterActor *child; gint i; lines = &request->lines[orientation]; for (i = 0; i < lines->max - lines->min; i++) { lines->lines[i].minimum = 0; lines->lines[i].natural = 0; lines->lines[i].expand = FALSE; } clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (request->grid, child); attach = &grid_child->attach[orientation]; if (attach->span == 1 && clutter_actor_needs_expand (child, orientation)) lines->lines[attach->pos - lines->min].expand = TRUE; } } /* Sums allocations for lines spanned by child and their spacing. */ static gfloat compute_allocation_for_child (ClutterGridRequest *request, ClutterActor *child, ClutterOrientation orientation) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterGridLineData *linedata; ClutterGridLines *lines; ClutterGridLine *line; ClutterGridAttach *attach; gfloat size; gint i; grid_child = GET_GRID_CHILD (request->grid, child); linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; attach = &grid_child->attach[orientation]; size = (attach->span - 1) * linedata->spacing; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; size += line->allocation; } return size; } static void compute_request_for_child (ClutterGridRequest *request, ClutterActor *child, ClutterOrientation orientation, gboolean contextual, gfloat *minimum, gfloat *natural) { if (contextual) { gfloat size; size = compute_allocation_for_child (request, child, 1 - orientation); if (orientation == CLUTTER_ORIENTATION_HORIZONTAL) clutter_actor_get_preferred_width (child, size, minimum, natural); else clutter_actor_get_preferred_height (child, size, minimum, natural); } else { if (orientation == CLUTTER_ORIENTATION_HORIZONTAL) clutter_actor_get_preferred_width (child, -1, minimum, natural); else clutter_actor_get_preferred_height (child, -1, minimum, natural); } } /* Sets requisition to max. of non-spanning children. * If contextual is TRUE, requires allocations of * lines in the opposite orientation to be set. */ static void clutter_grid_request_non_spanning (ClutterGridRequest *request, ClutterOrientation orientation, gboolean contextual) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterGridAttach *attach; ClutterGridLines *lines; ClutterGridLine *line; ClutterActorIter iter; ClutterActor *child; gfloat minimum; gfloat natural; lines = &request->lines[orientation]; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; grid_child = GET_GRID_CHILD (request->grid, child); attach = &grid_child->attach[orientation]; if (attach->span != 1) continue; compute_request_for_child (request, child, orientation, contextual, &minimum, &natural); line = &lines->lines[attach->pos - lines->min]; line->minimum = MAX (line->minimum, minimum); line->natural = MAX (line->natural, natural); } } /* Enforce homogeneous sizes. */ static void clutter_grid_request_homogeneous (ClutterGridRequest *request, ClutterOrientation orientation) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridLineData *linedata; ClutterGridLines *lines; gfloat minimum, natural; gint i; linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; if (!linedata->homogeneous) return; minimum = 0.0f; natural = 0.0f; for (i = 0; i < lines->max - lines->min; i++) { minimum = MAX (minimum, lines->lines[i].minimum); natural = MAX (natural, lines->lines[i].natural); } for (i = 0; i < lines->max - lines->min; i++) { lines->lines[i].minimum = minimum; lines->lines[i].natural = natural; } } /* Deals with spanning children. * Requires expand fields of lines to be set for * non-spanning children. */ static void clutter_grid_request_spanning (ClutterGridRequest *request, ClutterOrientation orientation, gboolean contextual) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterActor *child; ClutterActorIter iter; ClutterGridAttach *attach; ClutterGridLineData *linedata; ClutterGridLines *lines; ClutterGridLine *line; gfloat minimum; gfloat natural; gint span_minimum; gint span_natural; gint span_expand; gboolean force_expand; gint extra; gint expand; gint line_extra; gint i; linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; grid_child = GET_GRID_CHILD (request->grid, child); attach = &grid_child->attach[orientation]; if (attach->span == 1) continue; compute_request_for_child (request, child, orientation, contextual, &minimum, &natural); span_minimum = (attach->span - 1) * linedata->spacing; span_natural = (attach->span - 1) * linedata->spacing; span_expand = 0; force_expand = FALSE; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; span_minimum += line->minimum; span_natural += line->natural; if (line->expand) span_expand += 1; } if (span_expand == 0) { span_expand = attach->span; force_expand = TRUE; } /* If we need to request more space for this child to fill * its requisition, then divide up the needed space amongst the * lines it spans, favoring expandable lines if any. * * When doing homogeneous allocation though, try to keep the * line allocations even, since we're going to force them to * be the same anyway, and we don't want to introduce unnecessary * extra space. */ if (span_minimum < minimum) { if (linedata->homogeneous) { gint total, m; total = minimum - (attach->span - 1) * linedata->spacing; m = total / attach->span + (total % attach->span ? 1 : 0); for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; line->minimum = MAX(line->minimum, m); } } else { extra = minimum - span_minimum; expand = span_expand; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; if (force_expand || line->expand) { line_extra = extra / expand; line->minimum += line_extra; extra -= line_extra; expand -= 1; } } } } if (span_natural < natural) { if (linedata->homogeneous) { gint total, n; total = natural - (attach->span - 1) * linedata->spacing; n = total / attach->span + (total % attach->span ? 1 : 0); for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; line->natural = MAX(line->natural, n); } } else { extra = natural - span_natural; expand = span_expand; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; if (force_expand || line->expand) { line_extra = extra / expand; line->natural += line_extra; extra -= line_extra; expand -= 1; } } } } } } /* Marks empty and expanding lines and counts them. */ static void clutter_grid_request_compute_expand (ClutterGridRequest *request, ClutterOrientation orientation, gint *nonempty_lines, gint *expand_lines) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridChild *grid_child; ClutterGridAttach *attach; ClutterActorIter iter; ClutterActor *child; gint i; ClutterGridLines *lines; ClutterGridLine *line; gboolean has_expand; gint expand; gint empty; lines = &request->lines[orientation]; for (i = 0; i < lines->max - lines->min; i++) { lines->lines[i].need_expand = FALSE; lines->lines[i].expand = FALSE; lines->lines[i].empty = TRUE; } clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; grid_child = GET_GRID_CHILD (request->grid, child); attach = &grid_child->attach[orientation]; if (attach->span != 1) continue; line = &lines->lines[attach->pos - lines->min]; line->empty = FALSE; if (clutter_actor_needs_expand (child, orientation)) line->expand = TRUE; } clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; grid_child = GET_GRID_CHILD (request->grid, child); attach = &grid_child->attach[orientation]; if (attach->span == 1) continue; has_expand = FALSE; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; line->empty = FALSE; if (line->expand) has_expand = TRUE; } if (!has_expand && clutter_actor_needs_expand (child, orientation)) { for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; line->need_expand = TRUE; } } } empty = 0; expand = 0; for (i = 0; i < lines->max - lines->min; i++) { line = &lines->lines[i]; if (line->need_expand) line->expand = TRUE; if (line->empty) empty += 1; if (line->expand) expand += 1; } if (nonempty_lines) *nonempty_lines = lines->max - lines->min - empty; if (expand_lines) *expand_lines = expand; } /* Sums the minimum and natural fields of lines and their spacing. */ static void clutter_grid_request_sum (ClutterGridRequest *request, ClutterOrientation orientation, gfloat *minimum, gfloat *natural) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridLineData *linedata; ClutterGridLines *lines; gint i; gfloat min, nat; gint nonempty; clutter_grid_request_compute_expand (request, orientation, &nonempty, NULL); linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; min = 0; nat = 0; if (nonempty > 0) { min = (nonempty - 1) * linedata->spacing; nat = (nonempty - 1) * linedata->spacing; } for (i = 0; i < lines->max - lines->min; i++) { min += lines->lines[i].minimum; nat += lines->lines[i].natural; } if (minimum) *minimum = min; if (natural) *natural = nat; } /* Computes minimum and natural fields of lines. * When contextual is TRUE, requires allocation of * lines in the opposite orientation to be set. */ static void clutter_grid_request_run (ClutterGridRequest *request, ClutterOrientation orientation, gboolean contextual) { clutter_grid_request_init (request, orientation); clutter_grid_request_non_spanning (request, orientation, contextual); clutter_grid_request_homogeneous (request, orientation); clutter_grid_request_spanning (request, orientation, contextual); clutter_grid_request_homogeneous (request, orientation); } typedef struct _RequestedSize { gpointer data; gfloat minimum_size; gfloat natural_size; } RequestedSize; /* Pulled from gtksizerequest.c from Gtk+ */ static gint compare_gap (gconstpointer p1, gconstpointer p2, gpointer data) { RequestedSize *sizes = data; const guint *c1 = p1; const guint *c2 = p2; const gint d1 = MAX (sizes[*c1].natural_size - sizes[*c1].minimum_size, 0); const gint d2 = MAX (sizes[*c2].natural_size - sizes[*c2].minimum_size, 0); gint delta = (d2 - d1); if (0 == delta) delta = (*c2 - *c1); return delta; } /* * distribute_natural_allocation: * @extra_space: Extra space to redistribute among children after subtracting * minimum sizes and any child padding from the overall allocation * @n_requested_sizes: Number of requests to fit into the allocation * @sizes: An array of structs with a client pointer and a minimum/natural size * in the orientation of the allocation. * * Distributes @extra_space to child @sizes by bringing smaller * children up to natural size first. * * The remaining space will be added to the @minimum_size member of the * RequestedSize struct. If all sizes reach their natural size then * the remaining space is returned. * * Returns: The remainder of @extra_space after redistributing space * to @sizes. * * Pulled from gtksizerequest.c from Gtk+ */ static gint distribute_natural_allocation (gint extra_space, guint n_requested_sizes, RequestedSize *sizes) { guint *spreading; gint i; g_return_val_if_fail (extra_space >= 0, 0); spreading = g_newa (guint, n_requested_sizes); for (i = 0; i < n_requested_sizes; i++) spreading[i] = i; /* Distribute the container's extra space c_gap. We want to assign * this space such that the sum of extra space assigned to children * (c^i_gap) is equal to c_cap. The case that there's not enough * space for all children to take their natural size needs some * attention. The goals we want to achieve are: * * a) Maximize number of children taking their natural size. * b) The allocated size of children should be a continuous * function of c_gap. That is, increasing the container size by * one pixel should never make drastic changes in the distribution. * c) If child i takes its natural size and child j doesn't, * child j should have received at least as much gap as child i. * * The following code distributes the additional space by following * these rules. */ /* Sort descending by gap and position. */ g_qsort_with_data (spreading, n_requested_sizes, sizeof (guint), compare_gap, sizes); /* Distribute available space. * This master piece of a loop was conceived by Behdad Esfahbod. */ for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i) { /* Divide remaining space by number of remaining children. * Sort order and reducing remaining space by assigned space * ensures that space is distributed equally. */ gint glue = (extra_space + i) / (i + 1); gint gap = sizes[(spreading[i])].natural_size - sizes[(spreading[i])].minimum_size; gint extra = MIN (glue, gap); sizes[spreading[i]].minimum_size += extra; extra_space -= extra; } return extra_space; } /* Requires that the minimum and natural fields of lines * have been set, computes the allocation field of lines * by distributing total_size among lines. */ static void clutter_grid_request_allocate (ClutterGridRequest *request, ClutterOrientation orientation, gfloat total_size) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridLineData *linedata; ClutterGridLines *lines; ClutterGridLine *line; gint nonempty; gint expand; gint i, j; RequestedSize *sizes; gint extra; gint rest; gint size; clutter_grid_request_compute_expand (request, orientation, &nonempty, &expand); if (nonempty == 0) return; linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; size = total_size - (nonempty - 1) * linedata->spacing; if (linedata->homogeneous) { extra = size / nonempty; rest = size % nonempty; for (i = 0; i < lines->max - lines->min; i++) { line = &lines->lines[i]; if (line->empty) continue; line->allocation = extra; if (rest > 0) { line->allocation += 1; rest -= 1; } } } else { sizes = g_newa (RequestedSize, nonempty); j = 0; for (i = 0; i < lines->max - lines->min; i++) { line = &lines->lines[i]; if (line->empty) continue; size -= line->minimum; sizes[j].minimum_size = line->minimum; sizes[j].natural_size = line->natural; sizes[j].data = line; j++; } size = distribute_natural_allocation (MAX (0, size), nonempty, sizes); if (expand > 0) { extra = size / expand; rest = size % expand; } else { extra = 0; rest = 0; } j = 0; for (i = 0; i < lines->max - lines->min; i++) { line = &lines->lines[i]; if (line->empty) continue; g_assert (line == sizes[j].data); line->allocation = sizes[j].minimum_size; if (line->expand) { line->allocation += extra; if (rest > 0) { line->allocation += 1; rest -= 1; } } j++; } } } /* Computes the position fields from allocation and spacing. */ static void clutter_grid_request_position (ClutterGridRequest *request, ClutterOrientation orientation) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridLineData *linedata; ClutterGridLines *lines; ClutterGridLine *line; gfloat position; gint i; linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; position = 0.f; for (i = 0; i < lines->max - lines->min; i++) { line = &lines->lines[i]; if (!line->empty) { line->position = position; position += line->allocation + linedata->spacing; } } } static void clutter_grid_child_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject); ClutterLayoutManager *manager; manager = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (gobject)); switch (prop_id) { case PROP_CHILD_LEFT_ATTACH: CHILD_LEFT (grid_child) = g_value_get_int (value); clutter_layout_manager_layout_changed (manager); break; case PROP_CHILD_TOP_ATTACH: CHILD_TOP (grid_child) = g_value_get_int (value); clutter_layout_manager_layout_changed (manager); break; case PROP_CHILD_WIDTH: CHILD_WIDTH (grid_child) = g_value_get_int (value); clutter_layout_manager_layout_changed (manager); break; case PROP_CHILD_HEIGHT: CHILD_HEIGHT (grid_child) = g_value_get_int (value); clutter_layout_manager_layout_changed (manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_grid_child_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterGridChild *grid_child = CLUTTER_GRID_CHILD (gobject); switch (prop_id) { case PROP_CHILD_LEFT_ATTACH: g_value_set_int (value, CHILD_LEFT (grid_child)); break; case PROP_CHILD_TOP_ATTACH: g_value_set_int (value, CHILD_TOP (grid_child)); break; case PROP_CHILD_WIDTH: g_value_set_int (value, CHILD_WIDTH (grid_child)); break; case PROP_CHILD_HEIGHT: g_value_set_int (value, CHILD_HEIGHT (grid_child)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_grid_child_class_init (ClutterGridChildClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_grid_child_set_property; gobject_class->get_property = clutter_grid_child_get_property; child_props[PROP_CHILD_LEFT_ATTACH] = g_param_spec_int ("left-attach", P_("Left attachment"), P_("The column number to attach the left side of the " "child to"), -G_MAXINT, G_MAXINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); child_props[PROP_CHILD_TOP_ATTACH] = g_param_spec_int ("top-attach", P_("Top attachment"), P_("The row number to attach the top side of a child " "widget to"), -G_MAXINT, G_MAXINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); child_props[PROP_CHILD_WIDTH] = g_param_spec_int ("width", P_("Width"), P_("The number of columns that a child spans"), -G_MAXINT, G_MAXINT, 1, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); child_props[PROP_CHILD_HEIGHT] = g_param_spec_int ("height", P_("Height"), P_("The number of rows that a child spans"), -G_MAXINT, G_MAXINT, 1, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_CHILD_LAST, child_props); } static void clutter_grid_child_init (ClutterGridChild *self) { CHILD_LEFT (self) = -1; CHILD_TOP (self) = -1; CHILD_WIDTH (self) = 1; CHILD_HEIGHT (self) = 1; } static void clutter_grid_layout_set_container (ClutterLayoutManager *self, ClutterContainer *container) { ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (self)->priv; ClutterLayoutManagerClass *parent_class; priv->container = container; if (priv->container != NULL) { ClutterRequestMode request_mode; /* we need to change the :request-mode of the container * to match the orientation */ request_mode = priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), request_mode); } parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_grid_layout_parent_class); parent_class->set_container (self, container); } static void clutter_grid_layout_get_size_for_size (ClutterGridLayout *self, ClutterOrientation orientation, float size, float *minimum, float *natural) { ClutterGridRequest request; ClutterGridLines *lines; float min_size, nat_size; request.grid = self; clutter_grid_request_update_attach (&request); clutter_grid_request_count_lines (&request); lines = &request.lines[0]; lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); lines = &request.lines[1]; lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); clutter_grid_request_run (&request, 1 - orientation, FALSE); clutter_grid_request_sum (&request, 1 - orientation, &min_size, &nat_size); clutter_grid_request_allocate (&request, 1 - orientation, MAX (size, nat_size)); clutter_grid_request_run (&request, orientation, TRUE); clutter_grid_request_sum (&request, orientation, minimum, natural); } static void clutter_grid_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (manager); if (min_width_p) *min_width_p = 0.0f; if (nat_width_p) *nat_width_p = 0.0f; clutter_grid_layout_get_size_for_size (self, CLUTTER_ORIENTATION_HORIZONTAL, for_height, min_width_p, nat_width_p); } static void clutter_grid_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (manager); if (min_height_p) *min_height_p = 0.0f; if (nat_height_p) *nat_height_p = 0.0f; clutter_grid_layout_get_size_for_size (self, CLUTTER_ORIENTATION_VERTICAL, for_width, min_height_p, nat_height_p); } static void allocate_child (ClutterGridRequest *request, ClutterOrientation orientation, ClutterGridChild *child, gfloat *position, gfloat *size) { ClutterGridLayoutPrivate *priv = request->grid->priv; ClutterGridLineData *linedata; ClutterGridLines *lines; ClutterGridLine *line; ClutterGridAttach *attach; gint i; linedata = &priv->linedata[orientation]; lines = &request->lines[orientation]; attach = &child->attach[orientation]; *position = lines->lines[attach->pos - lines->min].position; *size = (attach->span - 1) * linedata->spacing; for (i = 0; i < attach->span; i++) { line = &lines->lines[attach->pos - lines->min + i]; *size += line->allocation; } } #define GET_SIZE(allocation, orientation) \ (orientation == CLUTTER_ORIENTATION_HORIZONTAL \ ? clutter_actor_box_get_width ((allocation)) \ : clutter_actor_box_get_height ((allocation))) static void clutter_grid_layout_allocate (ClutterLayoutManager *layout, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (layout); ClutterOrientation orientation; ClutterGridRequest request; ClutterGridLines *lines; ClutterActorIter iter; ClutterActor *child; request.grid = self; clutter_grid_request_update_attach (&request); clutter_grid_request_count_lines (&request); lines = &request.lines[0]; lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); lines = &request.lines[1]; lines->lines = g_newa (ClutterGridLine, lines->max - lines->min); memset (lines->lines, 0, (lines->max - lines->min) * sizeof (ClutterGridLine)); if (clutter_actor_get_request_mode (CLUTTER_ACTOR (container)) == CLUTTER_REQUEST_WIDTH_FOR_HEIGHT) orientation = CLUTTER_ORIENTATION_HORIZONTAL; else orientation = CLUTTER_ORIENTATION_VERTICAL; clutter_grid_request_run (&request, 1 - orientation, FALSE); clutter_grid_request_allocate (&request, 1 - orientation, GET_SIZE (allocation, 1 - orientation)); clutter_grid_request_run (&request, orientation, TRUE); clutter_grid_request_allocate (&request, orientation, GET_SIZE (allocation, orientation)); clutter_grid_request_position (&request, 0); clutter_grid_request_position (&request, 1); clutter_actor_iter_init (&iter, CLUTTER_ACTOR (container)); while (clutter_actor_iter_next (&iter, &child)) { ClutterActorBox child_allocation; gfloat x, y, width, height; ClutterGridChild *grid_child; if (!clutter_actor_is_visible (child)) continue; grid_child = GET_GRID_CHILD (self, child); allocate_child (&request, CLUTTER_ORIENTATION_HORIZONTAL, grid_child, &x, &width); allocate_child (&request, CLUTTER_ORIENTATION_VERTICAL, grid_child, &y, &height); x += allocation->x1; y += allocation->y1; CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f - %.2f x %.2f }", _clutter_actor_get_debug_name (child), x, y, width, height); child_allocation.x1 = x; child_allocation.y1 = y; child_allocation.x2 = child_allocation.x1 + width; child_allocation.y2 = child_allocation.y1 + height; clutter_actor_allocate (child, &child_allocation, flags); } } static GType clutter_grid_layout_get_child_meta_type (ClutterLayoutManager *self) { return CLUTTER_TYPE_GRID_CHILD; } static void clutter_grid_layout_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterGridLayout *self = CLUTTER_GRID_LAYOUT (gobject); switch (prop_id) { case PROP_ORIENTATION: clutter_grid_layout_set_orientation (self, g_value_get_enum (value)); break; case PROP_ROW_SPACING: clutter_grid_layout_set_row_spacing (self, g_value_get_uint (value)); break; case PROP_COLUMN_SPACING: clutter_grid_layout_set_column_spacing (self, g_value_get_uint (value)); break; case PROP_ROW_HOMOGENEOUS: clutter_grid_layout_set_row_homogeneous (self, g_value_get_boolean (value)); break; case PROP_COLUMN_HOMOGENEOUS: clutter_grid_layout_set_column_homogeneous (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_grid_layout_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterGridLayoutPrivate *priv = CLUTTER_GRID_LAYOUT (gobject)->priv; switch (prop_id) { case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; case PROP_ROW_SPACING: g_value_set_uint (value, COLUMNS (priv)->spacing); break; case PROP_COLUMN_SPACING: g_value_set_uint (value, ROWS (priv)->spacing); break; case PROP_ROW_HOMOGENEOUS: g_value_set_boolean (value, COLUMNS (priv)->homogeneous); break; case PROP_COLUMN_HOMOGENEOUS: g_value_set_boolean (value, ROWS (priv)->homogeneous); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_grid_layout_class_init (ClutterGridLayoutClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterLayoutManagerClass *layout_class; layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); object_class->set_property = clutter_grid_layout_set_property; object_class->get_property = clutter_grid_layout_get_property; layout_class->set_container = clutter_grid_layout_set_container; layout_class->get_preferred_width = clutter_grid_layout_get_preferred_width; layout_class->get_preferred_height = clutter_grid_layout_get_preferred_height; layout_class->allocate = clutter_grid_layout_allocate; layout_class->get_child_meta_type = clutter_grid_layout_get_child_meta_type; /** * ClutterGridLayout:orientation: * * The orientation of the layout, either horizontal or vertical * * Since: 1.12 */ obj_props[PROP_ORIENTATION] = g_param_spec_enum ("orientation", P_("Orientation"), P_("The orientation of the layout"), CLUTTER_TYPE_ORIENTATION, CLUTTER_ORIENTATION_HORIZONTAL, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); /** * ClutterGridLayout:row-spacing: * * The amount of space in pixels between two consecutive rows * * Since: 1.12 */ obj_props[PROP_ROW_SPACING] = g_param_spec_uint ("row-spacing", P_("Row spacing"), P_("The amount of space between two consecutive rows"), 0, G_MAXUINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); /** * ClutterGridLayout:column-spacing: * * The amount of space in pixels between two consecutive columns * * Since: 1.12 */ obj_props[PROP_COLUMN_SPACING] = g_param_spec_uint ("column-spacing", P_("Column spacing"), P_("The amount of space between two consecutive " "columns"), 0, G_MAXUINT, 0, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); /** * ClutterGridLayout:row-homogeneous: * * Whether all rows of the layout should have the same height * * Since: 1.12 */ obj_props[PROP_ROW_HOMOGENEOUS] = g_param_spec_boolean ("row-homogeneous", P_("Row Homogeneous"), P_("If TRUE, the rows are all the same height"), FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); /** * ClutterGridLayout:column-homogeneous: * * Whether all columns of the layout should have the same width * * Since: 1.12 */ obj_props[PROP_COLUMN_HOMOGENEOUS] = g_param_spec_boolean ("column-homogeneous", P_("Column Homogeneous"), P_("If TRUE, the columns are all the same width"), FALSE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } static void clutter_grid_layout_init (ClutterGridLayout *self) { self->priv = clutter_grid_layout_get_instance_private (self); self->priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL; self->priv->linedata[0].spacing = 0; self->priv->linedata[1].spacing = 0; self->priv->linedata[0].homogeneous = FALSE; self->priv->linedata[1].homogeneous = FALSE; } /** * clutter_grid_layout_new: * * Creates a new #ClutterGridLayout * * Return value: the new #ClutterGridLayout */ ClutterLayoutManager * clutter_grid_layout_new (void) { return g_object_new (CLUTTER_TYPE_GRID_LAYOUT, NULL); } /** * clutter_grid_layout_attach: * @layout: a #ClutterGridLayout * @child: the #ClutterActor to add * @left: the column number to attach the left side of @child to * @top: the row number to attach the top side of @child to * @width: the number of columns that @child will span * @height: the number of rows that @child will span * * Adds a widget to the grid. * * The position of @child is determined by @left and @top. The * number of 'cells' that @child will occupy is determined by * @width and @height. * * Since: 1.12 */ void clutter_grid_layout_attach (ClutterGridLayout *layout, ClutterActor *child, gint left, gint top, gint width, gint height) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (!priv->container) return; grid_attach (layout, child, left, top, width, height); clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child); } /** * clutter_grid_layout_attach_next_to: * @layout: a #ClutterGridLayout * @child: the actor to add * @sibling: (allow-none): the child of @layout that @child will be placed * next to, or %NULL to place @child at the beginning or end * @side: the side of @sibling that @child is positioned next to * @width: the number of columns that @child will span * @height: the number of rows that @child will span * * Adds a actor to the grid. * * The actor is placed next to @sibling, on the side determined by * @side. When @sibling is %NULL, the actor is placed in row (for * left or right placement) or column 0 (for top or bottom placement), * at the end indicated by @side. * * Attaching widgets labeled [1], [2], [3] with @sibling == %NULL and * @side == %CLUTTER_GRID_POSITION_LEFT yields a layout of [3][2][1]. * * Since: 1.12 */ void clutter_grid_layout_attach_next_to (ClutterGridLayout *layout, ClutterActor *child, ClutterActor *sibling, ClutterGridPosition side, gint width, gint height) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (clutter_actor_get_parent (child) == NULL); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); g_return_if_fail (width > 0); g_return_if_fail (height > 0); priv = layout->priv; if (!priv->container) return; grid_attach_next_to (layout, child, sibling, side, width, height); clutter_actor_add_child (CLUTTER_ACTOR (priv->container), child); } /** * clutter_grid_layout_set_orientation: * @layout: a #ClutterGridLayout * @orientation: the orientation of the #ClutterGridLayout * * Sets the orientation of the @layout. * * #ClutterGridLayout uses the orientation as a hint when adding * children to the #ClutterActor using it as a layout manager via * clutter_actor_add_child(); changing this value will not have * any effect on children that are already part of the layout. * * Since: 1.12 */ void clutter_grid_layout_set_orientation (ClutterGridLayout *layout, ClutterOrientation orientation) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (priv->orientation != orientation) { priv->orientation = orientation; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]); } } /** * clutter_grid_layout_get_child_at: * @layout: a #ClutterGridLayout * @left: the left edge of the cell * @top: the top edge of the cell * * Gets the child of @layout whose area covers the grid * cell whose upper left corner is at @left, @top. * * Returns: (transfer none): the child at the given position, or %NULL * * Since: 1.12 */ ClutterActor * clutter_grid_layout_get_child_at (ClutterGridLayout *layout, gint left, gint top) { ClutterGridLayoutPrivate *priv; ClutterGridChild *grid_child; ClutterActorIter iter; ClutterActor *child; g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), NULL); priv = layout->priv; if (!priv->container) return NULL; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (layout, child); if (CHILD_LEFT (grid_child) <= left && CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child) > left && CHILD_TOP (grid_child) <= top && CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child) > top) return child; } return NULL; } /** * clutter_grid_layout_insert_row: * @layout: a #ClutterGridLayout * @position: the position to insert the row at * * Inserts a row at the specified position. * * Children which are attached at or below this position * are moved one row down. Children which span across this * position are grown to span the new row. * * Since: 1.12 */ void clutter_grid_layout_insert_row (ClutterGridLayout *layout, gint position) { ClutterGridLayoutPrivate *priv; ClutterGridChild *grid_child; ClutterActorIter iter; ClutterActor *child; gint top, height; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (!priv->container) return; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (layout, child); top = CHILD_TOP (grid_child); height = CHILD_HEIGHT (grid_child); if (top >= position) { CHILD_TOP (grid_child) = top + 1; g_object_notify_by_pspec (G_OBJECT (grid_child), child_props[PROP_CHILD_TOP_ATTACH]); } else if (top + height > position) { CHILD_HEIGHT (grid_child) = height + 1; g_object_notify_by_pspec (G_OBJECT (grid_child), child_props[PROP_CHILD_HEIGHT]); } } clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); } /** * clutter_grid_layout_insert_column: * @layout: a #ClutterGridLayout * @position: the position to insert the column at * * Inserts a column at the specified position. * * Children which are attached at or to the right of this position * are moved one column to the right. Children which span across this * position are grown to span the new column. * * Since: 1.12 */ void clutter_grid_layout_insert_column (ClutterGridLayout *layout, gint position) { ClutterGridLayoutPrivate *priv; ClutterGridChild *grid_child; ClutterActorIter iter; ClutterActor *child; gint left, width; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (!priv->container) return; clutter_actor_iter_init (&iter, CLUTTER_ACTOR (priv->container)); while (clutter_actor_iter_next (&iter, &child)) { grid_child = GET_GRID_CHILD (layout, child); left = CHILD_LEFT (grid_child); width = CHILD_WIDTH (grid_child); if (left >= position) { CHILD_LEFT (grid_child) = left + 1; g_object_notify_by_pspec (G_OBJECT (grid_child), child_props[PROP_CHILD_LEFT_ATTACH]); } else if (left + width > position) { CHILD_WIDTH (grid_child) = width + 1; g_object_notify_by_pspec (G_OBJECT (grid_child), child_props[PROP_CHILD_WIDTH]); } } clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); } /** * clutter_grid_layout_insert_next_to: * @layout: a #ClutterGridLayout * @sibling: the child of @layout that the new row or column will be * placed next to * @side: the side of @sibling that @child is positioned next to * * Inserts a row or column at the specified position. * * The new row or column is placed next to @sibling, on the side * determined by @side. If @side is %CLUTTER_GRID_POSITION_LEFT or * %CLUTTER_GRID_POSITION_BOTTOM, a row is inserted. If @side is * %CLUTTER_GRID_POSITION_LEFT of %CLUTTER_GRID_POSITION_RIGHT, * a column is inserted. * * Since: 1.12 */ void clutter_grid_layout_insert_next_to (ClutterGridLayout *layout, ClutterActor *sibling, ClutterGridPosition side) { ClutterGridChild *grid_child; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (sibling)); grid_child = GET_GRID_CHILD (layout, sibling); switch (side) { case CLUTTER_GRID_POSITION_LEFT: clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child)); break; case CLUTTER_GRID_POSITION_RIGHT: clutter_grid_layout_insert_column (layout, CHILD_LEFT (grid_child) + CHILD_WIDTH (grid_child)); break; case CLUTTER_GRID_POSITION_TOP: clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child)); break; case CLUTTER_GRID_POSITION_BOTTOM: clutter_grid_layout_insert_row (layout, CHILD_TOP (grid_child) + CHILD_HEIGHT (grid_child)); break; default: g_assert_not_reached (); } } /** * clutter_grid_layout_get_orientation: * @layout: a #ClutterGridLayout * * Retrieves the orientation of the @layout. * * Return value: the orientation of the layout * * Since: 1.12 */ ClutterOrientation clutter_grid_layout_get_orientation (ClutterGridLayout *layout) { g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), CLUTTER_ORIENTATION_HORIZONTAL); return layout->priv->orientation; } /** * clutter_grid_layout_set_row_spacing: * @layout: a #ClutterGridLayout * @spacing: the spacing between rows of the layout, in pixels * * Sets the spacing between rows of @layout * * Since: 1.12 */ void clutter_grid_layout_set_row_spacing (ClutterGridLayout *layout, guint spacing) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (COLUMNS (priv)->spacing != spacing) { COLUMNS (priv)->spacing = spacing; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ROW_SPACING]); } } /** * clutter_grid_layout_get_row_spacing: * @layout: a #ClutterGridLayout * * Retrieves the spacing set using clutter_grid_layout_set_row_spacing() * * Return value: the spacing between rows of @layout * * Since: 1.12 */ guint clutter_grid_layout_get_row_spacing (ClutterGridLayout *layout) { ClutterGridLayoutPrivate *priv; g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0); priv = layout->priv; return COLUMNS (priv)->spacing; } /** * clutter_grid_layout_set_column_spacing: * @layout: a #ClutterGridLayout * @spacing: the spacing between columns of the layout, in pixels * * Sets the spacing between columns of @layout * * Since: 1.12 */ void clutter_grid_layout_set_column_spacing (ClutterGridLayout *layout, guint spacing) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (ROWS (priv)->spacing != spacing) { ROWS (priv)->spacing = spacing; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_COLUMN_SPACING]); } } /** * clutter_grid_layout_get_column_spacing: * @layout: a #ClutterGridLayout * * Retrieves the spacing set using clutter_grid_layout_set_column_spacing() * * Return value: the spacing between coluns of @layout * * Since: 1.12 */ guint clutter_grid_layout_get_column_spacing (ClutterGridLayout *layout) { ClutterGridLayoutPrivate *priv; g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), 0); priv = layout->priv; return ROWS (priv)->spacing; } /** * clutter_grid_layout_set_column_homogeneous: * @layout: a #ClutterGridLayout * @homogeneous: %TRUE to make columns homogeneous * * Sets whether all columns of @layout will have the same width. * * Since: 1.12 */ void clutter_grid_layout_set_column_homogeneous (ClutterGridLayout *layout, gboolean homogeneous) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (ROWS (priv)->homogeneous != homogeneous) { ROWS (priv)->homogeneous = homogeneous; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_COLUMN_HOMOGENEOUS]); } } /** * clutter_grid_layout_get_column_homogeneous: * @layout: a #ClutterGridLayout * * Returns whether all columns of @layout have the same width. * * Returns: whether all columns of @layout have the same width. */ gboolean clutter_grid_layout_get_column_homogeneous (ClutterGridLayout *layout) { ClutterGridLayoutPrivate *priv; g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE); priv = layout->priv; return ROWS (priv)->homogeneous; } /** * clutter_grid_layout_set_row_homogeneous: * @layout: a #ClutterGridLayout * @homogeneous: %TRUE to make rows homogeneous * * Sets whether all rows of @layout will have the same height. * * Since: 1.12 */ void clutter_grid_layout_set_row_homogeneous (ClutterGridLayout *layout, gboolean homogeneous) { ClutterGridLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_GRID_LAYOUT (layout)); priv = layout->priv; if (COLUMNS (priv)->homogeneous != homogeneous) { COLUMNS (priv)->homogeneous = homogeneous; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ROW_HOMOGENEOUS]); } } /** * clutter_grid_layout_get_row_homogeneous: * @layout: a #ClutterGridLayout * * Returns whether all rows of @layout have the same height. * * Returns: whether all rows of @layout have the same height. * * Since: 1.12 */ gboolean clutter_grid_layout_get_row_homogeneous (ClutterGridLayout *layout) { ClutterGridLayoutPrivate *priv; g_return_val_if_fail (CLUTTER_IS_GRID_LAYOUT (layout), FALSE); priv = layout->priv; return COLUMNS (priv)->homogeneous; } muffin-6.4.1/clutter/clutter/clutter-transition.h0000664000175000017500000001257614723361714021144 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_TRANSITION_H__ #define __CLUTTER_TRANSITION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_TRANSITION (clutter_transition_get_type ()) #define CLUTTER_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TRANSITION, ClutterTransition)) #define CLUTTER_IS_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TRANSITION)) #define CLUTTER_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TRANSITION, ClutterTransitionClass)) #define CLUTTER_IS_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TRANSITION)) #define CLUTTER_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TRANSITION, ClutterTransitionClass)) typedef struct _ClutterTransitionPrivate ClutterTransitionPrivate; typedef struct _ClutterTransitionClass ClutterTransitionClass; /** * ClutterTransition: * * The #ClutterTransition structure contains private * data and should only be accessed using the provided API. * * Since: 1.10 */ struct _ClutterTransition { /*< private >*/ ClutterTimeline parent_instance; ClutterTransitionPrivate *priv; }; /** * ClutterTransitionClass: * @attached: virtual function; called when a transition is attached to * a #ClutterAnimatable instance * @detached: virtual function; called when a transition is detached from * a #ClutterAnimatable instance * @compute_value: virtual function; called each frame to compute and apply * the interpolation of the interval * * The #ClutterTransitionClass structure contains * private data. * * Since: 1.10 */ struct _ClutterTransitionClass { /*< private >*/ ClutterTimelineClass parent_class; /*< public >*/ void (* attached) (ClutterTransition *transition, ClutterAnimatable *animatable); void (* detached) (ClutterTransition *transition, ClutterAnimatable *animatable); void (* compute_value) (ClutterTransition *transition, ClutterAnimatable *animatable, ClutterInterval *interval, gdouble progress); /*< private >*/ gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_transition_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_transition_set_interval (ClutterTransition *transition, ClutterInterval *interval); CLUTTER_EXPORT ClutterInterval * clutter_transition_get_interval (ClutterTransition *transition); CLUTTER_EXPORT void clutter_transition_set_from_value (ClutterTransition *transition, const GValue *value); CLUTTER_EXPORT void clutter_transition_set_to_value (ClutterTransition *transition, const GValue *value); CLUTTER_EXPORT void clutter_transition_set_from (ClutterTransition *transition, GType value_type, ...); CLUTTER_EXPORT void clutter_transition_set_to (ClutterTransition *transition, GType value_type, ...); CLUTTER_EXPORT void clutter_transition_set_animatable (ClutterTransition *transition, ClutterAnimatable *animatable); CLUTTER_EXPORT ClutterAnimatable * clutter_transition_get_animatable (ClutterTransition *transition); CLUTTER_EXPORT void clutter_transition_set_remove_on_complete (ClutterTransition *transition, gboolean remove_complete); CLUTTER_EXPORT gboolean clutter_transition_get_remove_on_complete (ClutterTransition *transition); G_END_DECLS #endif /* __CLUTTER_TRANSITION_H__ */ muffin-6.4.1/clutter/clutter/clutter-stage-manager.c0000664000175000017500000002054514723361714021453 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ /** * SECTION:clutter-stage-manager * @short_description: Maintains the list of stages * * #ClutterStageManager is a singleton object, owned by Clutter, which * maintains the list of currently active stages * * Every newly-created #ClutterStage will cause the emission of the * #ClutterStageManager::stage-added signal; once a #ClutterStage has * been destroyed, the #ClutterStageManager::stage-removed signal will * be emitted * * #ClutterStageManager is available since Clutter 0.8 */ #include "clutter-build-config.h" #include "clutter-stage-manager-private.h" #include "clutter-marshal.h" #include "clutter-debug.h" #include "clutter-private.h" enum { PROP_0, PROP_DEFAULT_STAGE }; enum { STAGE_ADDED, STAGE_REMOVED, LAST_SIGNAL }; static guint manager_signals[LAST_SIGNAL] = { 0, }; static ClutterStage *default_stage = NULL; G_DEFINE_TYPE (ClutterStageManager, clutter_stage_manager, G_TYPE_OBJECT); static void clutter_stage_manager_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_DEFAULT_STAGE: g_value_set_object (value, default_stage); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_stage_manager_dispose (GObject *gobject) { ClutterStageManager *stage_manager; stage_manager = CLUTTER_STAGE_MANAGER (gobject); g_slist_free_full (stage_manager->stages, (GDestroyNotify) clutter_actor_destroy); stage_manager->stages = NULL; G_OBJECT_CLASS (clutter_stage_manager_parent_class)->dispose (gobject); } static void clutter_stage_manager_class_init (ClutterStageManagerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = clutter_stage_manager_dispose; gobject_class->get_property = clutter_stage_manager_get_property; /** * ClutterStageManager:default-stage: * * The default stage used by Clutter. * * Since: 0.8 */ g_object_class_install_property (gobject_class, PROP_DEFAULT_STAGE, g_param_spec_object ("default-stage", "Default Stage", "The default stage", CLUTTER_TYPE_STAGE, CLUTTER_PARAM_READABLE)); /** * ClutterStageManager::stage-added: * @stage_manager: the object which received the signal * @stage: the added stage * * The ::stage-added signal is emitted each time a new #ClutterStage * has been added to the stage manager. * * Since: 0.8 */ manager_signals[STAGE_ADDED] = g_signal_new ("stage-added", G_OBJECT_CLASS_TYPE (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageManagerClass, stage_added), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_STAGE); /** * ClutterStageManager::stage-removed: * @stage_manager: the object which received the signal * @stage: the removed stage * * The ::stage-removed signal is emitted each time a #ClutterStage * has been removed from the stage manager. * * Since: 0.8 */ manager_signals[STAGE_REMOVED] = g_signal_new ("stage-removed", G_OBJECT_CLASS_TYPE (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageManagerClass, stage_removed), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_STAGE); } static void clutter_stage_manager_init (ClutterStageManager *stage_manager) { } /** * clutter_stage_manager_get_default: * * Returns the default #ClutterStageManager. * * Return value: (transfer none): the default stage manager instance. The returned * object is owned by Clutter and you should not reference or unreference it. * * Since: 0.8 */ ClutterStageManager * clutter_stage_manager_get_default (void) { ClutterMainContext *context = _clutter_context_get_default (); if (G_UNLIKELY (context->stage_manager == NULL)) context->stage_manager = g_object_new (CLUTTER_TYPE_STAGE_MANAGER, NULL); return context->stage_manager; } /*< private > * _clutter_stage_manager_set_default_stage: * @stage_manager: a #ClutterStageManager * @stage: a #ClutterStage * * Sets @stage as the default stage * * A no-op if there already is a default stage */ void _clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manager, ClutterStage *stage) { if (G_UNLIKELY (default_stage == NULL)) { default_stage = stage; /* the default stage is immediately realized */ clutter_actor_realize (CLUTTER_ACTOR (stage)); g_object_notify (G_OBJECT (stage_manager), "default-stage"); } } /** * clutter_stage_manager_get_default_stage: * @stage_manager: a #ClutterStageManager * * Returns the default #ClutterStage. * * Return value: (transfer none): the default stage. The returned object * is owned by Clutter and you should never reference or unreference it * * Since: 0.8 */ ClutterStage * clutter_stage_manager_get_default_stage (ClutterStageManager *stage_manager) { return default_stage; } /** * clutter_stage_manager_list_stages: * @stage_manager: a #ClutterStageManager * * Lists all currently used stages. * * Return value: (transfer container) (element-type Clutter.Stage): a newly * allocated list of #ClutterStage objects. Use g_slist_free() to * deallocate it when done. * * Since: 0.8 */ GSList * clutter_stage_manager_list_stages (ClutterStageManager *stage_manager) { return g_slist_copy (stage_manager->stages); } /** * clutter_stage_manager_peek_stages: * @stage_manager: a #ClutterStageManager * * Lists all currently used stages. * * Return value: (transfer none) (element-type Clutter.Stage): a pointer * to the internal list of #ClutterStage objects. The returned list * is owned by the #ClutterStageManager and should never be modified * or freed * * Since: 1.0 */ const GSList * clutter_stage_manager_peek_stages (ClutterStageManager *stage_manager) { return stage_manager->stages; } void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, ClutterStage *stage) { if (g_slist_find (stage_manager->stages, stage)) { g_warning ("Trying to add a stage to the list of managed stages, " "but it is already in it, aborting."); return; } g_object_ref_sink (stage); stage_manager->stages = g_slist_append (stage_manager->stages, stage); g_signal_emit (stage_manager, manager_signals[STAGE_ADDED], 0, stage); } void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, ClutterStage *stage) { /* this might be called multiple times from a ::dispose, so it * needs to just return without warning */ if (!g_slist_find (stage_manager->stages, stage)) return; stage_manager->stages = g_slist_remove (stage_manager->stages, stage); /* if the default stage is being destroyed then we unset the pointer */ if (default_stage == stage) default_stage = NULL; g_signal_emit (stage_manager, manager_signals[STAGE_REMOVED], 0, stage); g_object_unref (stage); } muffin-6.4.1/clutter/clutter/clutter-input-pointer-a11y-private.h0000664000175000017500000000344514723361714024003 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Olivier Fourdan */ #ifndef __CLUTTER_INPUT_POINTER_A11Y_H__ #define __CLUTTER_INPUT_POINTER_A11Y_H__ #include #include "clutter-enum-types.h" G_BEGIN_DECLS CLUTTER_EXPORT void _clutter_input_pointer_a11y_add_device (ClutterInputDevice *device); CLUTTER_EXPORT void _clutter_input_pointer_a11y_remove_device (ClutterInputDevice *device); CLUTTER_EXPORT void _clutter_input_pointer_a11y_on_motion_event (ClutterInputDevice *device, float x, float y); CLUTTER_EXPORT void _clutter_input_pointer_a11y_on_button_event (ClutterInputDevice *device, int button, gboolean pressed); CLUTTER_EXPORT gboolean _clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device); G_END_DECLS #endif /* __CLUTTER_INPUT_POINTER_A11Y_H__ */ muffin-6.4.1/clutter/clutter/clutter-align-constraint.c0000664000175000017500000003777114723361714022225 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-align-constraint * @Title: ClutterAlignConstraint * @Short_Description: A constraint aligning the position of an actor * * #ClutterAlignConstraint is a #ClutterConstraint that aligns the position * of the #ClutterActor to which it is applied to the size of another * #ClutterActor using an alignment factor * * #ClutterAlignConstraint is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-align-constraint.h" #include "clutter-actor-meta-private.h" #include "clutter-actor-private.h" #include "clutter-constraint.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-private.h" #include #define CLUTTER_ALIGN_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ALIGN_CONSTRAINT, ClutterAlignConstraintClass)) #define CLUTTER_IS_ALIGN_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ALIGN_CONSTRAINT)) #define CLUTTER_ALIGN_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ALIGN_CONSTRAINT, ClutterAlignConstraintClass)) struct _ClutterAlignConstraint { ClutterConstraint parent_instance; ClutterActor *actor; ClutterActor *source; ClutterAlignAxis align_axis; gfloat factor; }; struct _ClutterAlignConstraintClass { ClutterConstraintClass parent_class; }; enum { PROP_0, PROP_SOURCE, PROP_ALIGN_AXIS, PROP_FACTOR, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterAlignConstraint, clutter_align_constraint, CLUTTER_TYPE_CONSTRAINT); static void source_position_changed (ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags, ClutterAlignConstraint *align) { if (align->actor != NULL) clutter_actor_queue_relayout (align->actor); } static void source_destroyed (ClutterActor *actor, ClutterAlignConstraint *align) { align->source = NULL; } static void clutter_align_constraint_set_actor (ClutterActorMeta *meta, ClutterActor *new_actor) { ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (meta); ClutterActorMetaClass *parent; if (new_actor != NULL && align->source != NULL && clutter_actor_contains (new_actor, align->source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (align->source), _clutter_actor_get_debug_name (new_actor), _clutter_actor_meta_get_debug_name (meta)); return; } /* store the pointer to the actor, for later use */ align->actor = new_actor; parent = CLUTTER_ACTOR_META_CLASS (clutter_align_constraint_parent_class); parent->set_actor (meta, new_actor); } static void clutter_align_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (constraint); gfloat source_width, source_height; gfloat actor_width, actor_height; gfloat source_x, source_y; if (align->source == NULL) return; clutter_actor_box_get_size (allocation, &actor_width, &actor_height); clutter_actor_get_position (align->source, &source_x, &source_y); clutter_actor_get_size (align->source, &source_width, &source_height); switch (align->align_axis) { case CLUTTER_ALIGN_X_AXIS: allocation->x1 = ((source_width - actor_width) * align->factor) + source_x; allocation->x2 = allocation->x1 + actor_width; break; case CLUTTER_ALIGN_Y_AXIS: allocation->y1 = ((source_height - actor_height) * align->factor) + source_y; allocation->y2 = allocation->y1 + actor_height; break; case CLUTTER_ALIGN_BOTH: allocation->x1 = ((source_width - actor_width) * align->factor) + source_x; allocation->y1 = ((source_height - actor_height) * align->factor) + source_y; allocation->x2 = allocation->x1 + actor_width; allocation->y2 = allocation->y1 + actor_height; break; default: g_assert_not_reached (); break; } clutter_actor_box_clamp_to_pixel (allocation); } static void clutter_align_constraint_dispose (GObject *gobject) { ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject); if (align->source != NULL) { g_signal_handlers_disconnect_by_func (align->source, G_CALLBACK (source_destroyed), align); g_signal_handlers_disconnect_by_func (align->source, G_CALLBACK (source_position_changed), align); align->source = NULL; } G_OBJECT_CLASS (clutter_align_constraint_parent_class)->dispose (gobject); } static void clutter_align_constraint_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: clutter_align_constraint_set_source (align, g_value_get_object (value)); break; case PROP_ALIGN_AXIS: clutter_align_constraint_set_align_axis (align, g_value_get_enum (value)); break; case PROP_FACTOR: clutter_align_constraint_set_factor (align, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_align_constraint_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterAlignConstraint *align = CLUTTER_ALIGN_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: g_value_set_object (value, align->source); break; case PROP_ALIGN_AXIS: g_value_set_enum (value, align->align_axis); break; case PROP_FACTOR: g_value_set_float (value, align->factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_align_constraint_class_init (ClutterAlignConstraintClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass); meta_class->set_actor = clutter_align_constraint_set_actor; constraint_class->update_allocation = clutter_align_constraint_update_allocation; /** * ClutterAlignConstraint:source: * * The #ClutterActor used as the source for the alignment. * * The #ClutterActor must not be a child or a grandchild of the actor * using the constraint. * * Since: 1.4 */ obj_props[PROP_SOURCE] = g_param_spec_object ("source", P_("Source"), P_("The source of the alignment"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterAlignConstraint:align-axis: * * The axis to be used to compute the alignment * * Since: 1.4 */ obj_props[PROP_ALIGN_AXIS] = g_param_spec_enum ("align-axis", P_("Align Axis"), P_("The axis to align the position to"), CLUTTER_TYPE_ALIGN_AXIS, CLUTTER_ALIGN_X_AXIS, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterAlignConstraint:factor: * * The alignment factor, as a normalized value between 0.0 and 1.0 * * The factor depends on the #ClutterAlignConstraint:align-axis property: * with an align-axis value of %CLUTTER_ALIGN_X_AXIS, 0.0 means left and * 1.0 means right; with a value of %CLUTTER_ALIGN_Y_AXIS, 0.0 means top * and 1.0 means bottom. * * Since: 1.4 */ obj_props[PROP_FACTOR] = g_param_spec_float ("factor", P_("Factor"), P_("The alignment factor, between 0.0 and 1.0"), 0.0, 1.0, 0.0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); gobject_class->dispose = clutter_align_constraint_dispose; gobject_class->set_property = clutter_align_constraint_set_property; gobject_class->get_property = clutter_align_constraint_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_align_constraint_init (ClutterAlignConstraint *self) { self->actor = NULL; self->source = NULL; self->align_axis = CLUTTER_ALIGN_X_AXIS; self->factor = 0.0f; } /** * clutter_align_constraint_new: * @source: (allow-none): the #ClutterActor to use as the source of the * alignment, or %NULL * @axis: the axis to be used to compute the alignment * @factor: the alignment factor, between 0.0 and 1.0 * * Creates a new constraint, aligning a #ClutterActor's position with * regards of the size of the actor to @source, with the given * alignment @factor * * Return value: the newly created #ClutterAlignConstraint * * Since: 1.4 */ ClutterConstraint * clutter_align_constraint_new (ClutterActor *source, ClutterAlignAxis axis, gfloat factor) { g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL); return g_object_new (CLUTTER_TYPE_ALIGN_CONSTRAINT, "source", source, "align-axis", axis, "factor", factor, NULL); } /** * clutter_align_constraint_set_source: * @align: a #ClutterAlignConstraint * @source: (allow-none): a #ClutterActor, or %NULL to unset the source * * Sets the source of the alignment constraint * * Since: 1.4 */ void clutter_align_constraint_set_source (ClutterAlignConstraint *align, ClutterActor *source) { ClutterActor *old_source, *actor; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); if (align->source == source) return; meta = CLUTTER_ACTOR_META (align); actor = clutter_actor_meta_get_actor (meta); if (actor != NULL && source != NULL) { if (clutter_actor_contains (actor, source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (source), _clutter_actor_get_debug_name (actor), _clutter_actor_meta_get_debug_name (meta)); return; } } old_source = align->source; if (old_source != NULL) { g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_destroyed), align); g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_position_changed), align); } align->source = source; if (align->source != NULL) { g_signal_connect (align->source, "allocation-changed", G_CALLBACK (source_position_changed), align); g_signal_connect (align->source, "destroy", G_CALLBACK (source_destroyed), align); if (align->actor != NULL) clutter_actor_queue_relayout (align->actor); } g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_SOURCE]); } /** * clutter_align_constraint_get_source: * @align: a #ClutterAlignConstraint * * Retrieves the source of the alignment * * Return value: (transfer none): the #ClutterActor used as the source * of the alignment * * Since: 1.4 */ ClutterActor * clutter_align_constraint_get_source (ClutterAlignConstraint *align) { g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), NULL); return align->source; } /** * clutter_align_constraint_set_align_axis: * @align: a #ClutterAlignConstraint * @axis: the axis to which the alignment refers to * * Sets the axis to which the alignment refers to * * Since: 1.4 */ void clutter_align_constraint_set_align_axis (ClutterAlignConstraint *align, ClutterAlignAxis axis) { g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align)); if (align->align_axis == axis) return; align->align_axis = axis; if (align->actor != NULL) clutter_actor_queue_relayout (align->actor); g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_ALIGN_AXIS]); } /** * clutter_align_constraint_get_align_axis: * @align: a #ClutterAlignConstraint * * Retrieves the value set using clutter_align_constraint_set_align_axis() * * Return value: the alignment axis * * Since: 1.4 */ ClutterAlignAxis clutter_align_constraint_get_align_axis (ClutterAlignConstraint *align) { g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), CLUTTER_ALIGN_X_AXIS); return align->align_axis; } /** * clutter_align_constraint_set_factor: * @align: a #ClutterAlignConstraint * @factor: the alignment factor, between 0.0 and 1.0 * * Sets the alignment factor of the constraint * * The factor depends on the #ClutterAlignConstraint:align-axis property * and it is a value between 0.0 (meaning left, when * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or * meaning top, when #ClutterAlignConstraint:align-axis is set to * %CLUTTER_ALIGN_Y_AXIS) and 1.0 (meaning right, when * #ClutterAlignConstraint:align-axis is set to %CLUTTER_ALIGN_X_AXIS; or * meaning bottom, when #ClutterAlignConstraint:align-axis is set to * %CLUTTER_ALIGN_Y_AXIS). A value of 0.5 aligns in the middle in either * cases * * Since: 1.4 */ void clutter_align_constraint_set_factor (ClutterAlignConstraint *align, gfloat factor) { g_return_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align)); align->factor = CLAMP (factor, 0.0, 1.0); if (align->actor != NULL) clutter_actor_queue_relayout (align->actor); g_object_notify_by_pspec (G_OBJECT (align), obj_props[PROP_FACTOR]); } /** * clutter_align_constraint_get_factor: * @align: a #ClutterAlignConstraint * * Retrieves the factor set using clutter_align_constraint_set_factor() * * Return value: the alignment factor * * Since: 1.4 */ gfloat clutter_align_constraint_get_factor (ClutterAlignConstraint *align) { g_return_val_if_fail (CLUTTER_IS_ALIGN_CONSTRAINT (align), 0.0); return align->factor; } muffin-6.4.1/clutter/clutter/clutter-tap-action.c0000664000175000017500000000774614723361714021007 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emanuele Aina * * Based on ClutterPanAction * Based on ClutterDragAction, ClutterSwipeAction, and MxKineticScrollView, * written by: * Emmanuele Bassi * Tomeu Vizoso * Chris Lord */ /** * SECTION:clutter-tap-action * @Title: ClutterTapAction * @Short_Description: Action for tap gestures * * #ClutterTapAction is a sub-class of #ClutterGestureAction that implements * the logic for recognizing mouse clicks and touch tap gestures. * * The simplest usage of #ClutterTapAction consists in adding it to * a #ClutterActor, setting it as reactive and connecting a * callback for the #ClutterTapAction::tap signal, along the lines of the * following code: * * |[ * clutter_actor_add_action (actor, clutter_tap_action_new ()); * clutter_actor_set_reactive (actor, TRUE); * g_signal_connect (action, "tap", G_CALLBACK (on_tap_callback), NULL); * ]| * * Since: 1.14 */ #include "clutter-build-config.h" #include "clutter-tap-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-gesture-action-private.h" #include "clutter-marshal.h" #include "clutter-private.h" enum { TAP, LAST_SIGNAL }; static guint tap_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE (ClutterTapAction, clutter_tap_action, CLUTTER_TYPE_GESTURE_ACTION); static void emit_tap (ClutterTapAction *self, ClutterActor *actor) { g_signal_emit (self, tap_signals[TAP], 0, actor); } static void gesture_end (ClutterGestureAction *gesture, ClutterActor *actor) { emit_tap (CLUTTER_TAP_ACTION (gesture), actor); } static void clutter_tap_action_constructed (GObject *object) { clutter_gesture_action_set_threshold_trigger_edge (CLUTTER_GESTURE_ACTION (object), CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE); } static void clutter_tap_action_class_init (ClutterTapActionClass *klass) { ClutterGestureActionClass *gesture_class = CLUTTER_GESTURE_ACTION_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = clutter_tap_action_constructed; gesture_class->gesture_end = gesture_end; /** * ClutterTapAction::tap: * @action: the #ClutterTapAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::tap signal is emitted when the tap gesture is complete. * * Since: 1.14 */ tap_signals[TAP] = g_signal_new (I_("tap"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTapActionClass, tap), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); } static void clutter_tap_action_init (ClutterTapAction *self) { } /** * clutter_tap_action_new: * * Creates a new #ClutterTapAction instance * * Return value: the newly created #ClutterTapAction * * Since: 1.14 */ ClutterAction * clutter_tap_action_new (void) { return g_object_new (CLUTTER_TYPE_TAP_ACTION, NULL); } muffin-6.4.1/clutter/clutter/clutter-bin-layout.h0000664000175000017500000000545114723361714021027 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_BIN_LAYOUT_H__ #define __CLUTTER_BIN_LAYOUT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_BIN_LAYOUT (clutter_bin_layout_get_type ()) #define CLUTTER_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayout)) #define CLUTTER_IS_BIN_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BIN_LAYOUT)) #define CLUTTER_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) #define CLUTTER_IS_BIN_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIN_LAYOUT)) #define CLUTTER_BIN_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIN_LAYOUT, ClutterBinLayoutClass)) typedef struct _ClutterBinLayout ClutterBinLayout; typedef struct _ClutterBinLayoutPrivate ClutterBinLayoutPrivate; typedef struct _ClutterBinLayoutClass ClutterBinLayoutClass; /** * ClutterBinLayout: * * The #ClutterBinLayout structure contains only private data * and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterBinLayout { /*< private >*/ ClutterLayoutManager parent_instance; ClutterBinLayoutPrivate *priv; }; /** * ClutterBinLayoutClass: * * The #ClutterBinLayoutClass structure contains only private * data and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterBinLayoutClass { /*< private >*/ ClutterLayoutManagerClass parent_class; }; CLUTTER_EXPORT GType clutter_bin_layout_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager * clutter_bin_layout_new (ClutterBinAlignment x_align, ClutterBinAlignment y_align); G_END_DECLS #endif /* __CLUTTER_BIN_LAYOUT_H__ */ muffin-6.4.1/clutter/clutter/clutter-scroll-actor.h0000664000175000017500000000651314723361714021350 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_SCROLL_ACTOR_H__ #define __CLUTTER_SCROLL_ACTOR_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_SCROLL_ACTOR (clutter_scroll_actor_get_type ()) #define CLUTTER_SCROLL_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActor)) #define CLUTTER_IS_SCROLL_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCROLL_ACTOR)) #define CLUTTER_SCROLL_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActorClass)) #define CLUTTER_IS_SCROLL_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SCROLL_ACTOR)) #define CLUTTER_SCROLL_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SCROLL_ACTOR, ClutterScrollActorClass)) typedef struct _ClutterScrollActorPrivate ClutterScrollActorPrivate; typedef struct _ClutterScrollActorClass ClutterScrollActorClass; /** * ClutterScrollActor: * * The #ClutterScrollActor structure contains only * private data, and should be accessed using the provided API. * * Since: 1.12 */ struct _ClutterScrollActor { /*< private >*/ ClutterActor parent_instance; ClutterScrollActorPrivate *priv; }; /** * ClutterScrollActorClass: * * The #ClutterScrollActor structure contains only * private data. * * Since: 1.12 */ struct _ClutterScrollActorClass { /*< private >*/ ClutterActorClass parent_instance; gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_scroll_actor_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_scroll_actor_new (void); CLUTTER_EXPORT void clutter_scroll_actor_set_scroll_mode (ClutterScrollActor *actor, ClutterScrollMode mode); CLUTTER_EXPORT ClutterScrollMode clutter_scroll_actor_get_scroll_mode (ClutterScrollActor *actor); CLUTTER_EXPORT void clutter_scroll_actor_scroll_to_point (ClutterScrollActor *actor, const graphene_point_t *point); CLUTTER_EXPORT void clutter_scroll_actor_scroll_to_rect (ClutterScrollActor *actor, const graphene_rect_t *rect); G_END_DECLS #endif /* __CLUTTER_SCROLL_ACTOR_H__ */ muffin-6.4.1/clutter/clutter/clutter-path.h0000664000175000017500000001730114723361714017675 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2008 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_PATH_H__ #define __CLUTTER_PATH_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_PATH (clutter_path_get_type ()) #define CLUTTER_TYPE_PATH_NODE (clutter_path_node_get_type ()) #define CLUTTER_PATH(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PATH, ClutterPath)) #define CLUTTER_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PATH, ClutterPathClass)) #define CLUTTER_IS_PATH(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PATH)) #define CLUTTER_IS_PATH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PATH)) #define CLUTTER_PATH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PATH, ClutterPathClass)) typedef struct _ClutterPathClass ClutterPathClass; typedef struct _ClutterPathPrivate ClutterPathPrivate; /** * ClutterPathCallback: * @node: the node * @data: (closure): optional data passed to the function * * This function is passed to clutter_path_foreach() and will be * called for each node contained in the path. * * Since: 1.0 */ typedef void (* ClutterPathCallback) (const ClutterPathNode *node, gpointer data); /** * ClutterPath: * * The #ClutterPath struct contains only private data and should * be accessed with the functions below. * * Since: 1.0 */ struct _ClutterPath { /*< private >*/ GInitiallyUnowned parent; ClutterPathPrivate *priv; }; /** * ClutterPathClass: * * The #ClutterPathClass struct contains only private data. * * Since: 1.0 */ struct _ClutterPathClass { /*< private >*/ GInitiallyUnownedClass parent_class; }; CLUTTER_EXPORT GType clutter_path_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPath *clutter_path_new (void); CLUTTER_EXPORT ClutterPath *clutter_path_new_with_description (const gchar *desc); CLUTTER_EXPORT void clutter_path_add_move_to (ClutterPath *path, gint x, gint y); CLUTTER_EXPORT void clutter_path_add_rel_move_to (ClutterPath *path, gint x, gint y); CLUTTER_EXPORT void clutter_path_add_line_to (ClutterPath *path, gint x, gint y); CLUTTER_EXPORT void clutter_path_add_rel_line_to (ClutterPath *path, gint x, gint y); CLUTTER_EXPORT void clutter_path_add_curve_to (ClutterPath *path, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3); CLUTTER_EXPORT void clutter_path_add_rel_curve_to (ClutterPath *path, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3); CLUTTER_EXPORT void clutter_path_add_close (ClutterPath *path); CLUTTER_EXPORT gboolean clutter_path_add_string (ClutterPath *path, const gchar *str); CLUTTER_EXPORT void clutter_path_add_node (ClutterPath *path, const ClutterPathNode *node); CLUTTER_EXPORT void clutter_path_add_cairo_path (ClutterPath *path, const cairo_path_t *cpath); CLUTTER_EXPORT guint clutter_path_get_n_nodes (ClutterPath *path); CLUTTER_EXPORT void clutter_path_get_node (ClutterPath *path, guint index_, ClutterPathNode *node); CLUTTER_EXPORT GSList * clutter_path_get_nodes (ClutterPath *path); CLUTTER_EXPORT void clutter_path_foreach (ClutterPath *path, ClutterPathCallback callback, gpointer user_data); CLUTTER_EXPORT void clutter_path_insert_node (ClutterPath *path, gint index_, const ClutterPathNode *node); CLUTTER_EXPORT void clutter_path_remove_node (ClutterPath *path, guint index_); CLUTTER_EXPORT void clutter_path_replace_node (ClutterPath *path, guint index_, const ClutterPathNode *node); CLUTTER_EXPORT gchar * clutter_path_get_description (ClutterPath *path); CLUTTER_EXPORT gboolean clutter_path_set_description (ClutterPath *path, const gchar *str); CLUTTER_EXPORT void clutter_path_clear (ClutterPath *path); CLUTTER_EXPORT void clutter_path_to_cairo_path (ClutterPath *path, cairo_t *cr); CLUTTER_EXPORT guint clutter_path_get_position (ClutterPath *path, gdouble progress, ClutterKnot *position); CLUTTER_EXPORT guint clutter_path_get_length (ClutterPath *path); G_END_DECLS #endif /* __CLUTTER_PATH_H__ */ muffin-6.4.1/clutter/clutter/clutter-desaturate-effect.h0000664000175000017500000000437114723361714022337 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_DESATURATE_EFFECT_H__ #define __CLUTTER_DESATURATE_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_DESATURATE_EFFECT (clutter_desaturate_effect_get_type ()) #define CLUTTER_DESATURATE_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DESATURATE_EFFECT, ClutterDesaturateEffect)) #define CLUTTER_IS_DESATURATE_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DESATURATE_EFFECT)) /** * ClutterDesaturateEffect: * * #ClutterDesaturateEffect is an opaque structure * whose members cannot be directly accessed * * Since: 1.4 */ typedef struct _ClutterDesaturateEffect ClutterDesaturateEffect; typedef struct _ClutterDesaturateEffectClass ClutterDesaturateEffectClass; CLUTTER_EXPORT GType clutter_desaturate_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect *clutter_desaturate_effect_new (gdouble factor); CLUTTER_EXPORT void clutter_desaturate_effect_set_factor (ClutterDesaturateEffect *effect, gdouble factor); CLUTTER_EXPORT gdouble clutter_desaturate_effect_get_factor (ClutterDesaturateEffect *effect); G_END_DECLS #endif /* __CLUTTER_DESATURATE_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-master-clock.c0000664000175000017500000001002114723361714021310 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Emmanuele Bassi * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * SECTION:clutter-master-clock * @short_description: The master clock for all animations * * The #ClutterMasterClock class is responsible for advancing all * #ClutterTimelines when a stage is being redrawn. The master clock * makes sure that the scenegraph is always integrally updated before * painting it. */ #include "clutter-build-config.h" #include "clutter-master-clock.h" #include "clutter-master-clock-default.h" #include "clutter-private.h" G_DEFINE_INTERFACE (ClutterMasterClock, clutter_master_clock, G_TYPE_OBJECT) static void clutter_master_clock_default_init (ClutterMasterClockInterface *iface) { } ClutterMasterClock * _clutter_master_clock_get_default (void) { ClutterMainContext *context = _clutter_context_get_default (); if (G_UNLIKELY (context->master_clock == NULL)) context->master_clock = g_object_new (CLUTTER_TYPE_MASTER_CLOCK_DEFAULT, NULL); return context->master_clock; } /* * _clutter_master_clock_add_timeline: * @master_clock: a #ClutterMasterClock * @timeline: a #ClutterTimeline * * Adds @timeline to the list of playing timelines held by the master * clock. */ void _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock, ClutterTimeline *timeline) { g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->add_timeline (master_clock, timeline); } /* * _clutter_master_clock_remove_timeline: * @master_clock: a #ClutterMasterClock * @timeline: a #ClutterTimeline * * Removes @timeline from the list of playing timelines held by the * master clock. */ void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock, ClutterTimeline *timeline) { g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->remove_timeline (master_clock, timeline); } /* * _clutter_master_clock_start_running: * @master_clock: a #ClutterMasterClock * * Called when we have events or redraws to process; if the clock * is stopped, does the processing necessary to wake it up again. */ void _clutter_master_clock_start_running (ClutterMasterClock *master_clock) { g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->start_running (master_clock); } /** * _clutter_master_clock_ensure_next_iteration: * @master_clock: a #ClutterMasterClock * * Ensures that the master clock will run at least one iteration */ void _clutter_master_clock_ensure_next_iteration (ClutterMasterClock *master_clock) { g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->ensure_next_iteration (master_clock); } void _clutter_master_clock_set_paused (ClutterMasterClock *master_clock, gboolean paused) { g_return_if_fail (CLUTTER_IS_MASTER_CLOCK (master_clock)); CLUTTER_MASTER_CLOCK_GET_IFACE (master_clock)->set_paused (master_clock, !!paused); } muffin-6.4.1/clutter/clutter/deprecated/0000775000175000017500000000000014723361714017206 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/deprecated/clutter-stage.h0000664000175000017500000000606114723361714022145 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_STAGE_DEPRECATED_H__ #define __CLUTTER_STAGE_DEPRECATED_H__ #include G_BEGIN_DECLS #ifndef CLUTTER_DISABLE_DEPRECATED /** * CLUTTER_STAGE_WIDTH: * * Macro that evaluates to the width of the default stage * * Since: 0.2 * * Deprecated: 1.2: Use clutter_actor_get_width() instead */ #define CLUTTER_STAGE_WIDTH() (clutter_actor_get_width (clutter_stage_get_default ())) /** * CLUTTER_STAGE_HEIGHT: * * Macro that evaluates to the height of the default stage * * Since: 0.2 * * Deprecated: 1.2: use clutter_actor_get_height() instead */ #define CLUTTER_STAGE_HEIGHT() (clutter_actor_get_height (clutter_stage_get_default ())) /* Commodity macro, for mallum only */ #define clutter_stage_add(stage,actor) G_STMT_START { \ if (CLUTTER_IS_STAGE ((stage)) && CLUTTER_IS_ACTOR ((actor))) \ { \ ClutterContainer *_container = (ClutterContainer *) (stage); \ ClutterActor *_actor = (ClutterActor *) (actor); \ clutter_container_add_actor (_container, _actor); \ } } G_STMT_END #endif /* CLUTTER_DISABLE_DEPRECATED */ CLUTTER_DEPRECATED_FOR(clutter_stage_new) ClutterActor * clutter_stage_get_default (void); CLUTTER_DEPRECATED gboolean clutter_stage_is_default (ClutterStage *stage); CLUTTER_DEPRECATED_FOR(clutter_actor_queue_redraw) void clutter_stage_queue_redraw (ClutterStage *stage); CLUTTER_DEPRECATED_FOR(clutter_actor_set_background_color) void clutter_stage_set_color (ClutterStage *stage, const ClutterColor *color); CLUTTER_DEPRECATED_FOR(clutter_actor_get_background_color) void clutter_stage_get_color (ClutterStage *stage, ClutterColor *color); CLUTTER_DEPRECATED void clutter_stage_ensure_current (ClutterStage *stage); G_END_DECLS #endif /* __CLUTTER_STAGE_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-state.h0000664000175000017500000001247514723361714022170 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Øyvind Kolås * * Copyright (C) 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_STATE_H__ #define __CLUTTER_STATE_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_STATE_KEY (clutter_state_key_get_type ()) #define CLUTTER_TYPE_STATE (clutter_state_get_type ()) #define CLUTTER_STATE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STATE, ClutterState)) #define CLUTTER_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STATE, ClutterStateClass)) #define CLUTTER_IS_STATE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STATE)) #define CLUTTER_IS_STATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STATE)) #define CLUTTER_STATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STATE, ClutterStateClass)) typedef struct _ClutterStatePrivate ClutterStatePrivate; typedef struct _ClutterStateClass ClutterStateClass; /** * ClutterStateKey: * * #ClutterStateKey is an opaque structure whose * members cannot be accessed directly * * Since: 1.4 */ typedef struct _ClutterStateKey ClutterStateKey; /** * ClutterState: * * The #ClutterState structure contains only * private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterState { /*< private >*/ GObject parent; ClutterStatePrivate *priv; }; /** * ClutterStateClass: * @completed: class handler for the #ClutterState::completed signal * * The #ClutterStateClass structure contains * only private data * * Since: 1.4 * * Deprecated: 1.12 */ struct _ClutterStateClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ void (* completed) (ClutterState *state); /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[8]; }; CLUTTER_DEPRECATED GType clutter_state_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED ClutterState *clutter_state_new (void); CLUTTER_DEPRECATED ClutterTimeline * clutter_state_set_state (ClutterState *state, const gchar *target_state_name); CLUTTER_DEPRECATED ClutterTimeline * clutter_state_warp_to_state (ClutterState *state, const gchar *target_state_name); CLUTTER_DEPRECATED ClutterState * clutter_state_set_key (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, GObject *object, const gchar *property_name, guint mode, const GValue *value, gdouble pre_delay, gdouble post_delay); CLUTTER_DEPRECATED void clutter_state_set_duration (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, guint duration); CLUTTER_DEPRECATED guint clutter_state_get_duration (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name); CLUTTER_DEPRECATED void clutter_state_set (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, gpointer first_object, const gchar *first_property_name, gulong first_mode, ...) G_GNUC_NULL_TERMINATED; CLUTTER_DEPRECATED GList * clutter_state_get_states (ClutterState *state); CLUTTER_DEPRECATED const gchar * clutter_state_get_state (ClutterState *state); /* * ClutterStateKey */ CLUTTER_DEPRECATED GType clutter_state_key_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED GType clutter_state_key_get_property_type (const ClutterStateKey *key); G_END_DECLS #endif /* __CLUTTER_STATE_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-alpha.h0000664000175000017500000001121314723361714022122 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Jorn Baayen * Emmanuele Bassi * Tomas Frydrych * * Copyright (C) 2006, 2007, 2008 OpenedHand * Copyright (C) 2009 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_ALPHA_H__ #define __CLUTTER_ALPHA_H__ #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_ALPHA (clutter_alpha_get_type ()) #define CLUTTER_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ALPHA, ClutterAlpha)) #define CLUTTER_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) #define CLUTTER_IS_ALPHA(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ALPHA)) #define CLUTTER_IS_ALPHA_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ALPHA)) #define CLUTTER_ALPHA_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ALPHA, ClutterAlphaClass)) typedef struct _ClutterAlphaClass ClutterAlphaClass; typedef struct _ClutterAlphaPrivate ClutterAlphaPrivate; /** * ClutterAlphaFunc: * @alpha: a #ClutterAlpha * @user_data: user data passed to the function * * A function returning a value depending on the position of * the #ClutterTimeline bound to @alpha. * * Return value: a floating point value * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimelineProgressFunc instead. */ typedef gdouble (*ClutterAlphaFunc) (ClutterAlpha *alpha, gpointer user_data); /** * ClutterAlpha: * * #ClutterAlpha combines a #ClutterTimeline and a function. * The contents of the #ClutterAlpha structure are private and should * only be accessed using the provided API. * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimeline instead */ struct _ClutterAlpha { /*< private >*/ GInitiallyUnowned parent; ClutterAlphaPrivate *priv; }; /** * ClutterAlphaClass: * * Base class for #ClutterAlpha * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimeline instead */ struct _ClutterAlphaClass { /*< private >*/ GInitiallyUnownedClass parent_class; void (*_clutter_alpha_1) (void); void (*_clutter_alpha_2) (void); void (*_clutter_alpha_3) (void); void (*_clutter_alpha_4) (void); void (*_clutter_alpha_5) (void); }; CLUTTER_DEPRECATED GType clutter_alpha_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED ClutterAlpha * clutter_alpha_new (void); CLUTTER_DEPRECATED ClutterAlpha * clutter_alpha_new_full (ClutterTimeline *timeline, gulong mode); CLUTTER_DEPRECATED gdouble clutter_alpha_get_alpha (ClutterAlpha *alpha); CLUTTER_DEPRECATED void clutter_alpha_set_func (ClutterAlpha *alpha, ClutterAlphaFunc func, gpointer data, GDestroyNotify destroy); CLUTTER_DEPRECATED void clutter_alpha_set_closure (ClutterAlpha *alpha, GClosure *closure); CLUTTER_DEPRECATED void clutter_alpha_set_timeline (ClutterAlpha *alpha, ClutterTimeline *timeline); CLUTTER_DEPRECATED ClutterTimeline *clutter_alpha_get_timeline (ClutterAlpha *alpha); CLUTTER_DEPRECATED void clutter_alpha_set_mode (ClutterAlpha *alpha, gulong mode); CLUTTER_DEPRECATED gulong clutter_alpha_get_mode (ClutterAlpha *alpha); G_END_DECLS #endif /* __CLUTTER_ALPHA_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-alpha.c0000664000175000017500000005215114723361714022123 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Jorn Baayen * Emmanuele Bassi * Tomas Frydrych * * Copyright (C) 2006, 2007, 2008 OpenedHand * Copyright (C) 2009, 2010 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-alpha * @short_description: A class for calculating a value as a function of time * * #ClutterAlpha is a class for calculating an floating point value * dependent only on the position of a #ClutterTimeline. * * For newly written code, it is recommended to use the * #ClutterTimeline:progress-mode property of #ClutterTimeline, or the * clutter_timeline_set_progress_func() function instead of #ClutterAlpha. * The #ClutterAlpha class will be deprecated in the future, and will not * be available any more in the next major version of Clutter. * * A #ClutterAlpha binds a #ClutterTimeline to a progress function which * translates the time T into an adimensional factor alpha. * * You should provide a #ClutterTimeline and bind it to the #ClutterAlpha * instance using clutter_alpha_set_timeline(). You should also set an * "animation mode", by using the #ClutterAnimationMode values that * Clutter provides. * * Instead of a #ClutterAnimationMode you may provide a function returning * the alpha value depending on the progress of the timeline, using * clutter_alpha_set_func() or clutter_alpha_set_closure(). The alpha * function will be executed each time a new frame in the #ClutterTimeline * is reached. * * Since the alpha function is controlled by the timeline instance, you can * pause, stop or resume the #ClutterAlpha from calling the alpha function by * using the appropriate functions of the #ClutterTimeline object. * * #ClutterAlpha is available since Clutter 0.2. * * #ClutterAlpha is deprecated since Clutter 1.12. #ClutterTimeline and * the #ClutterTimeline:progress-mode property replace this whole class. * * ## ClutterAlpha custom properties for #ClutterScript * * #ClutterAlpha defines a custom `function` property for * #ClutterScript which allows to reference a custom alpha function * available in the source code. Setting the `function` property * is equivalent to calling clutter_alpha_set_func() with the * specified function name. No user data or #GDestroyNotify is * available to be passed. * * The following JSON fragment defines a #ClutterAlpha * using a #ClutterTimeline with id "sine-timeline" and an alpha * function called `my_sine_alpha`. * * |[ * { * "id" : "sine-alpha", * "timeline" : { * "id" : "sine-timeline", * "duration" : 500, * "loop" : true * }, * "function" : "my_sine_alpha" * } * ]| */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-alpha.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-easing.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-scriptable.h" #include "clutter-script-private.h" struct _ClutterAlphaPrivate { ClutterTimeline *timeline; guint timeline_new_frame_id; gdouble alpha; GClosure *closure; ClutterAlphaFunc func; gpointer user_data; GDestroyNotify notify; gulong mode; }; enum { PROP_0, PROP_TIMELINE, PROP_ALPHA, PROP_MODE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterAlpha, clutter_alpha, G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (ClutterAlpha) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init)); static void timeline_new_frame_cb (ClutterTimeline *timeline, guint msecs, ClutterAlpha *alpha) { ClutterAlphaPrivate *priv = alpha->priv; /* Update alpha value and notify */ priv->alpha = clutter_alpha_get_alpha (alpha); g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_ALPHA]); } static void clutter_alpha_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterAlpha *alpha = CLUTTER_ALPHA (object); switch (prop_id) { case PROP_TIMELINE: clutter_alpha_set_timeline (alpha, g_value_get_object (value)); break; case PROP_MODE: clutter_alpha_set_mode (alpha, g_value_get_ulong (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_alpha_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv; switch (prop_id) { case PROP_TIMELINE: g_value_set_object (value, priv->timeline); break; case PROP_ALPHA: g_value_set_double (value, priv->alpha); break; case PROP_MODE: g_value_set_ulong (value, priv->mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_alpha_finalize (GObject *object) { ClutterAlphaPrivate *priv = CLUTTER_ALPHA (object)->priv; if (priv->notify != NULL) priv->notify (priv->user_data); else if (priv->closure != NULL) g_closure_unref (priv->closure); G_OBJECT_CLASS (clutter_alpha_parent_class)->finalize (object); } static void clutter_alpha_dispose (GObject *object) { ClutterAlpha *self = CLUTTER_ALPHA(object); clutter_alpha_set_timeline (self, NULL); G_OBJECT_CLASS (clutter_alpha_parent_class)->dispose (object); } static ClutterAlphaFunc resolve_alpha_func (const gchar *name) { static GModule *module = NULL; ClutterAlphaFunc func; CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name); if (G_UNLIKELY (module == NULL)) module = g_module_open (NULL, 0); if (g_module_symbol (module, name, (gpointer) &func)) { CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table", name); return func; } return NULL; } static void clutter_alpha_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { if (strncmp (name, "function", 8) == 0) { g_assert (G_VALUE_HOLDS (value, G_TYPE_POINTER)); if (g_value_get_pointer (value) != NULL) { clutter_alpha_set_func (CLUTTER_ALPHA (scriptable), g_value_get_pointer (value), NULL, NULL); } } else g_object_set_property (G_OBJECT (scriptable), name, value); } static gboolean clutter_alpha_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { if (strncmp (name, "function", 8) == 0) { const gchar *func_name = json_node_get_string (node); g_value_init (value, G_TYPE_POINTER); g_value_set_pointer (value, resolve_alpha_func (func_name)); return TRUE; } /* we need to do this because we use gulong in place * of ClutterAnimationMode for ClutterAlpha:mode */ if (strncmp (name, "mode", 4) == 0) { gulong mode; mode = _clutter_script_resolve_animation_mode (node); g_value_init (value, G_TYPE_ULONG); g_value_set_ulong (value, mode); return TRUE; } return FALSE; } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_alpha_parse_custom_node; iface->set_custom_property = clutter_alpha_set_custom_property; } static void clutter_alpha_class_init (ClutterAlphaClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = clutter_alpha_set_property; object_class->get_property = clutter_alpha_get_property; object_class->finalize = clutter_alpha_finalize; object_class->dispose = clutter_alpha_dispose; /** * ClutterAlpha:timeline: * * A #ClutterTimeline instance used to drive the alpha function. * * Since: 0.2 * * Deprecated: 1.12 */ obj_props[PROP_TIMELINE] = g_param_spec_object ("timeline", P_("Timeline"), P_("Timeline used by the alpha"), CLUTTER_TYPE_TIMELINE, CLUTTER_PARAM_READWRITE); /** * ClutterAlpha:alpha: * * The alpha value as computed by the alpha function. The linear * interval is 0.0 to 1.0, but the Alpha allows overshooting by * one unit in each direction, so the valid interval is -1.0 to 2.0. * * Since: 0.2 * Deprecated: 1.12: Use #ClutterTimeline::new-frame and * clutter_timeline_get_progress() instead */ obj_props[PROP_ALPHA] = g_param_spec_double ("alpha", P_("Alpha value"), P_("Alpha value as computed by the alpha"), -1.0, 2.0, 0.0, CLUTTER_PARAM_READABLE); /** * ClutterAlpha:mode: * * The progress function logical id - a value from the * #ClutterAnimationMode enumeration. * * If %CLUTTER_CUSTOM_MODE is used then the function set using * clutter_alpha_set_closure() or clutter_alpha_set_func() * will be used. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterTimeline:progress-mode */ obj_props[PROP_MODE] = g_param_spec_ulong ("mode", P_("Mode"), P_("Progress mode"), 0, G_MAXULONG, CLUTTER_CUSTOM_MODE, G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } static void clutter_alpha_init (ClutterAlpha *self) { self->priv = clutter_alpha_get_instance_private (self); self->priv->mode = CLUTTER_CUSTOM_MODE; self->priv->alpha = 0.0; } /** * clutter_alpha_get_alpha: * @alpha: A #ClutterAlpha * * Query the current alpha value. * * Return Value: The current alpha value for the alpha * * Since: 0.2 * * Deprecated: 1.12: Use clutter_timeline_get_progress() */ gdouble clutter_alpha_get_alpha (ClutterAlpha *alpha) { ClutterAlphaPrivate *priv; gdouble retval = 0; g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), 0); priv = alpha->priv; if (G_LIKELY (priv->func)) { return priv->func (alpha, priv->user_data); } else if (priv->closure) { GValue params = G_VALUE_INIT; GValue result_value = G_VALUE_INIT; g_object_ref (alpha); g_value_init (&result_value, G_TYPE_DOUBLE); g_value_init (¶ms, CLUTTER_TYPE_ALPHA); g_value_set_object (¶ms, alpha); g_closure_invoke (priv->closure, &result_value, 1, ¶ms, NULL); retval = g_value_get_double (&result_value); g_value_unset (&result_value); g_value_unset (¶ms); g_object_unref (alpha); } return retval; } /* * clutter_alpha_set_closure_internal: * @alpha: a #ClutterAlpha * @closure: a #GClosure * * Sets the @closure for @alpha. This function does not * set the #ClutterAlpha:mode property and does not emit * the #GObject::notify signal for it. */ static inline void clutter_alpha_set_closure_internal (ClutterAlpha *alpha, GClosure *closure) { ClutterAlphaPrivate *priv = alpha->priv; if (priv->notify != NULL) priv->notify (priv->user_data); else if (priv->closure != NULL) g_closure_unref (priv->closure); priv->func = NULL; priv->user_data = NULL; priv->notify = NULL; if (closure == NULL) return; /* need to take ownership of the closure before sinking it */ priv->closure = g_closure_ref (closure); g_closure_sink (closure); /* set the marshaller */ if (G_CLOSURE_NEEDS_MARSHAL (closure)) { GClosureMarshal marshal = _clutter_marshal_DOUBLE__VOID; g_closure_set_marshal (priv->closure, marshal); } } /** * clutter_alpha_set_closure: * @alpha: A #ClutterAlpha * @closure: A #GClosure * * Sets the #GClosure used to compute the alpha value at each * frame of the #ClutterTimeline bound to @alpha. * * Since: 0.8 * * Deprecated: 1.12: Use clutter_timeline_set_progress_func() */ void clutter_alpha_set_closure (ClutterAlpha *alpha, GClosure *closure) { ClutterAlphaPrivate *priv; g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); g_return_if_fail (closure != NULL); priv = alpha->priv; clutter_alpha_set_closure_internal (alpha, closure); priv->mode = CLUTTER_CUSTOM_MODE; g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]); } /** * clutter_alpha_set_func: * @alpha: A #ClutterAlpha * @func: A #ClutterAlphaFunc * @data: user data to be passed to the alpha function, or %NULL * @destroy: notify function used when disposing the alpha function * * Sets the #ClutterAlphaFunc function used to compute * the alpha value at each frame of the #ClutterTimeline * bound to @alpha. * * This function will not register @func as a global alpha function. * * Since: 0.2 * * Deprecated: 1.12: Use clutter_timeline_set_progress_func() */ void clutter_alpha_set_func (ClutterAlpha *alpha, ClutterAlphaFunc func, gpointer data, GDestroyNotify destroy) { ClutterAlphaPrivate *priv; g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); g_return_if_fail (func != NULL); priv = alpha->priv; if (priv->notify != NULL) { priv->notify (priv->user_data); } else if (priv->closure != NULL) { g_closure_unref (priv->closure); priv->closure = NULL; } priv->func = func; priv->user_data = data; priv->notify = destroy; priv->mode = CLUTTER_CUSTOM_MODE; g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]); } /** * clutter_alpha_set_timeline: * @alpha: A #ClutterAlpha * @timeline: A #ClutterTimeline * * Binds @alpha to @timeline. * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimeline directly */ void clutter_alpha_set_timeline (ClutterAlpha *alpha, ClutterTimeline *timeline) { ClutterAlphaPrivate *priv; g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline)); priv = alpha->priv; if (priv->timeline == timeline) return; if (priv->timeline) { g_signal_handlers_disconnect_by_func (priv->timeline, timeline_new_frame_cb, alpha); g_object_unref (priv->timeline); priv->timeline = NULL; } if (timeline) { priv->timeline = g_object_ref (timeline); g_signal_connect (priv->timeline, "new-frame", G_CALLBACK (timeline_new_frame_cb), alpha); } g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_TIMELINE]); } /** * clutter_alpha_get_timeline: * @alpha: A #ClutterAlpha * * Gets the #ClutterTimeline bound to @alpha. * * Return value: (transfer none): a #ClutterTimeline instance * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimeline directlry */ ClutterTimeline * clutter_alpha_get_timeline (ClutterAlpha *alpha) { g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), NULL); return alpha->priv->timeline; } /** * clutter_alpha_new: * * Creates a new #ClutterAlpha instance. You must set a function * to compute the alpha value using clutter_alpha_set_func() and * bind a #ClutterTimeline object to the #ClutterAlpha instance * using clutter_alpha_set_timeline(). * * Return value: the newly created empty #ClutterAlpha instance. * * Since: 0.2 * * Deprecated: 1.12: Use #ClutterTimeline instead */ ClutterAlpha * clutter_alpha_new (void) { return g_object_new (CLUTTER_TYPE_ALPHA, NULL); } /** * clutter_alpha_new_full: * @timeline: #ClutterTimeline timeline * @mode: animation mode * * Creates a new #ClutterAlpha instance and sets the timeline * and animation mode. * * See also clutter_alpha_set_timeline() and clutter_alpha_set_mode(). * * Return Value: the newly created #ClutterAlpha * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterTimeline instead */ ClutterAlpha * clutter_alpha_new_full (ClutterTimeline *timeline, gulong mode) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); g_return_val_if_fail (mode != CLUTTER_ANIMATION_LAST, NULL); return g_object_new (CLUTTER_TYPE_ALPHA, "timeline", timeline, "mode", mode, NULL); } /** * clutter_alpha_get_mode: * @alpha: a #ClutterAlpha * * Retrieves the #ClutterAnimationMode used by @alpha. * * Return value: the animation mode * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterTimeline instead */ gulong clutter_alpha_get_mode (ClutterAlpha *alpha) { g_return_val_if_fail (CLUTTER_IS_ALPHA (alpha), CLUTTER_CUSTOM_MODE); return alpha->priv->mode; } typedef struct _AlphaData { guint closure_set : 1; ClutterAlphaFunc func; gpointer data; GClosure *closure; } AlphaData; static GPtrArray *clutter_alphas = NULL; static gdouble clutter_alpha_easing_func (ClutterAlpha *alpha, gpointer data G_GNUC_UNUSED) { ClutterAlphaPrivate *priv = alpha->priv; ClutterTimeline *timeline = priv->timeline; gdouble t, d; if (G_UNLIKELY (priv->timeline == NULL)) return 0.0; t = clutter_timeline_get_elapsed_time (timeline); d = clutter_timeline_get_duration (timeline); return clutter_easing_for_mode (priv->mode, t, d); } /** * clutter_alpha_set_mode: * @alpha: a #ClutterAlpha * @mode: a #ClutterAnimationMode * * Sets the progress function of @alpha using the symbolic value * of @mode, as taken by the #ClutterAnimationMode enumeration. * * Since: 1.0 * * Deprecated: 1.12: Use #ClutterTimeline and * clutter_timeline_set_progress_mode() instead */ void clutter_alpha_set_mode (ClutterAlpha *alpha, gulong mode) { ClutterAlphaPrivate *priv; g_return_if_fail (CLUTTER_IS_ALPHA (alpha)); g_return_if_fail (mode != CLUTTER_ANIMATION_LAST); priv = alpha->priv; if (mode == CLUTTER_CUSTOM_MODE) { priv->mode = mode; } else if (mode < CLUTTER_ANIMATION_LAST) { if (priv->mode == mode) return; /* sanity check to avoid getting an out of sync * enum/function mapping */ g_assert (clutter_get_easing_func_for_mode (mode) != NULL); clutter_alpha_set_closure_internal (alpha, NULL); priv->mode = mode; CLUTTER_NOTE (ANIMATION, "New easing mode '%s'[%lu]\n", clutter_get_easing_name_for_mode (priv->mode), priv->mode); priv->func = clutter_alpha_easing_func; priv->user_data = NULL; priv->notify = NULL; } else if (mode > CLUTTER_ANIMATION_LAST) { AlphaData *alpha_data = NULL; gulong real_index = 0; if (priv->mode == mode) return; if (G_UNLIKELY (clutter_alphas == NULL)) { g_warning ("No alpha functions defined for ClutterAlpha to use. "); return; } real_index = mode - CLUTTER_ANIMATION_LAST - 1; alpha_data = g_ptr_array_index (clutter_alphas, real_index); if (G_UNLIKELY (alpha_data == NULL)) { g_warning ("No alpha function registered for mode %lu.", mode); return; } if (alpha_data->closure_set) clutter_alpha_set_closure (alpha, alpha_data->closure); else { clutter_alpha_set_closure_internal (alpha, NULL); priv->func = alpha_data->func; priv->user_data = alpha_data->data; priv->notify = NULL; } priv->mode = mode; } else g_assert_not_reached (); g_object_notify_by_pspec (G_OBJECT (alpha), obj_props[PROP_MODE]); } muffin-6.4.1/clutter/clutter/deprecated/clutter-box.c0000664000175000017500000005303514723361714021630 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009,2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-box * @short_description: A Generic layout container * * #ClutterBox is a #ClutterActor sub-class implementing the #ClutterContainer * interface. A Box delegates the whole size requisition and size allocation to * a #ClutterLayoutManager instance. * * #ClutterBox is available since Clutter 1.2 * * #ClutterBox is deprecated since Clutter 1.10; all its relevant API is provided * by #ClutterActor, via the #ClutterActor:layout-manager property. * * ## Using ClutterBox * * The following code shows how to create a #ClutterBox with * a #ClutterLayoutManager sub-class, and how to add children to * it via clutter_box_pack(). * * |[ * ClutterActor *box; * ClutterLayoutManager *layout; * * // Create the layout manager first * layout = clutter_box_layout_new (); * clutter_box_layout_set_homogeneous (CLUTTER_BOX_LAYOUT (layout), TRUE); * clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout), 12); * * // Then create the ClutterBox actor. The Box will take * // ownership of the ClutterLayoutManager instance by sinking * // its floating reference * box = clutter_box_new (layout); * * // Now add children to the Box using the variadic arguments * // function clutter_box_pack() to set layout properties * clutter_box_pack (CLUTTER_BOX (box), actor, * "x-align", CLUTTER_BOX_ALIGNMENT_CENTER, * "y-align", CLUTTER_BOX_ALIGNMENT_END, * "expand", TRUE, * NULL); * ]| * * #ClutterBox's clutter_box_pack() wraps the generic * clutter_container_add_actor() function, but it also allows setting * layout properties while adding the new child to the box. */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "clutter-box.h" #include "clutter-actor-private.h" #include "clutter-color.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" struct _ClutterBoxPrivate { ClutterLayoutManager *manager; guint changed_id; }; enum { PROP_0, PROP_COLOR, PROP_COLOR_SET, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; static const ClutterColor default_box_color = { 255, 255, 255, 255 }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterBox, clutter_box, CLUTTER_TYPE_ACTOR) static inline void clutter_box_set_color_internal (ClutterBox *box, const ClutterColor *color) { clutter_actor_set_background_color (CLUTTER_ACTOR (box), color); g_object_notify_by_pspec (G_OBJECT (box), obj_props[PROP_COLOR_SET]); g_object_notify_by_pspec (G_OBJECT (box), obj_props[PROP_COLOR]); } static gboolean clutter_box_real_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { gboolean retval = FALSE; ClutterActorIter iter; ClutterActor *child; /* if we have a background color, and an allocation, then we need to * set it as the base of our paint volume */ retval = clutter_paint_volume_set_from_allocation (volume, actor); /* bail out early if we don't have any child */ if (clutter_actor_get_n_children (actor) == 0) return retval; retval = TRUE; /* otherwise, union the paint volumes of our children, in case * any one of them decides to paint outside the parent's allocation */ clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { const ClutterPaintVolume *child_volume; /* This gets the paint volume of the child transformed into the * group's coordinate space... */ child_volume = clutter_actor_get_transformed_paint_volume (child, actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } return retval; } static void clutter_box_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBox *self = CLUTTER_BOX (gobject); switch (prop_id) { case PROP_COLOR: clutter_box_set_color_internal (self, clutter_value_get_color (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_COLOR: { ClutterColor color; clutter_actor_get_background_color (CLUTTER_ACTOR (gobject), &color); clutter_value_set_color (value, &color); } break; case PROP_COLOR_SET: { gboolean color_set; g_object_get (gobject, "background-color-set", &color_set, NULL); g_value_set_boolean (value, color_set); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_real_destroy (ClutterActor *actor) { ClutterActor *iter; iter = clutter_actor_get_first_child (actor); while (iter != NULL) { ClutterActor *next = clutter_actor_get_next_sibling (iter); clutter_actor_destroy (iter); iter = next; } } static void clutter_box_class_init (ClutterBoxClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->destroy = clutter_box_real_destroy; actor_class->get_paint_volume = clutter_box_real_get_paint_volume; gobject_class->set_property = clutter_box_set_property; gobject_class->get_property = clutter_box_get_property; /** * ClutterBox:color: * * The color to be used to paint the background of the * #ClutterBox. Setting this property will set the * #ClutterBox:color-set property as a side effect * * This property sets the #ClutterActor:background-color property * internally. * * Since: 1.2 * * Deprecated: 1.10: Use the #ClutterActor:background-color property */ obj_props[PROP_COLOR] = clutter_param_spec_color ("color", P_("Color"), P_("The background color of the box"), &default_box_color, CLUTTER_PARAM_READWRITE); /** * ClutterBox:color-set: * * Whether the #ClutterBox:color property has been set. * * This property reads the #ClutterActor:background-color-set property * internally. * * Since: 1.2 * * Deprecated: 1.10: Use the #ClutterActor:background-color-set property */ obj_props[PROP_COLOR_SET] = g_param_spec_boolean ("color-set", P_("Color Set"), P_("Whether the background color is set"), FALSE, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_box_init (ClutterBox *self) { self->priv = clutter_box_get_instance_private (self); } /** * clutter_box_new: * @manager: a #ClutterLayoutManager * * Creates a new #ClutterBox. The children of the box will be layed * out by the passed @manager * * Return value: the newly created #ClutterBox actor * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_new() instead. */ ClutterActor * clutter_box_new (ClutterLayoutManager *manager) { g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); return g_object_new (CLUTTER_TYPE_BOX, "layout-manager", manager, NULL); } /** * clutter_box_set_layout_manager: * @box: a #ClutterBox * @manager: a #ClutterLayoutManager * * Sets the #ClutterLayoutManager for @box * * A #ClutterLayoutManager is a delegate object that controls the * layout of the children of @box * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_set_layout_manager() instead. */ void clutter_box_set_layout_manager (ClutterBox *box, ClutterLayoutManager *manager) { clutter_actor_set_layout_manager (CLUTTER_ACTOR (box), manager); } /** * clutter_box_get_layout_manager: * @box: a #ClutterBox * * Retrieves the #ClutterLayoutManager instance used by @box * * Return value: (transfer none): a #ClutterLayoutManager. The returned * #ClutterLayoutManager is owned by the #ClutterBox and it should not * be unreferenced * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_get_layout_manager() instead. */ ClutterLayoutManager * clutter_box_get_layout_manager (ClutterBox *box) { return clutter_actor_get_layout_manager (CLUTTER_ACTOR (box)); } /** * clutter_box_packv: * @box: a #ClutterBox * @actor: a #ClutterActor * @n_properties: the number of properties to set * @properties: (array length=n_properties) (element-type utf8): a vector * containing the property names to set * @values: (array length=n_properties): a vector containing the property * values to set * * Vector-based variant of clutter_box_pack(), intended for language * bindings to use * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_add_child() instead. To set * specific layout properties, use clutter_layout_manager_child_set() */ void clutter_box_packv (ClutterBox *box, ClutterActor *actor, guint n_properties, const gchar * const properties[], const GValue *values) { ClutterLayoutManager *manager; ClutterContainer *container; ClutterLayoutMeta *meta; GObjectClass *klass; gint i; g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); container = CLUTTER_CONTAINER (box); clutter_container_add_actor (container, actor); manager = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box)); if (manager == NULL) return; meta = clutter_layout_manager_get_child_meta (manager, container, actor); if (meta == NULL) return; klass = G_OBJECT_GET_CLASS (meta); for (i = 0; i < n_properties; i++) { const gchar *pname = properties[i]; GParamSpec *pspec; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: the layout property '%s' for managers " "of type '%s' (meta type '%s') does not exist", G_STRLOC, pname, G_OBJECT_TYPE_NAME (manager), G_OBJECT_TYPE_NAME (meta)); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: the layout property '%s' for managers " "of type '%s' (meta type '%s') is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager), G_OBJECT_TYPE_NAME (meta)); break; } clutter_layout_manager_child_set_property (manager, container, actor, pname, &values[i]); } } static inline void clutter_box_set_property_valist (ClutterBox *box, ClutterActor *actor, const gchar *first_property, va_list var_args) { ClutterContainer *container = CLUTTER_CONTAINER (box); ClutterLayoutManager *manager; ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; manager = clutter_actor_get_layout_manager (CLUTTER_ACTOR (box)); if (manager == NULL) return; meta = clutter_layout_manager_get_child_meta (manager, container, actor); if (meta == NULL) return; klass = G_OBJECT_GET_CLASS (meta); pname = first_property; while (pname) { GValue value = { 0, }; GParamSpec *pspec; gchar *error; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: the layout property '%s' for managers " "of type '%s' (meta type '%s') does not exist", G_STRLOC, pname, G_OBJECT_TYPE_NAME (manager), G_OBJECT_TYPE_NAME (meta)); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: the layout property '%s' for managers " "of type '%s' (meta type '%s') is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager), G_OBJECT_TYPE_NAME (meta)); break; } G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } clutter_layout_manager_child_set_property (manager, container, actor, pspec->name, &value); g_value_unset (&value); pname = va_arg (var_args, gchar*); } } /** * clutter_box_pack: * @box: a #ClutterBox * @actor: a #ClutterActor * @first_property: the name of the first property to set, or %NULL * @...: a list of property name and value pairs, terminated by %NULL * * Adds @actor to @box and sets layout properties at the same time, * if the #ClutterLayoutManager used by @box has them * * This function is a wrapper around clutter_container_add_actor() * and clutter_layout_manager_child_set() * * Language bindings should use the vector-based clutter_box_packv() * variant instead * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_add_child() instead. To set * specific layout properties, use clutter_layout_manager_child_set() */ void clutter_box_pack (ClutterBox *box, ClutterActor *actor, const gchar *first_property, ...) { va_list var_args; g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); if (first_property == NULL || *first_property == '\0') return; va_start (var_args, first_property); clutter_box_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } /** * clutter_box_pack_after: * @box: a #ClutterBox * @actor: a #ClutterActor * @sibling: (allow-none): a #ClutterActor or %NULL * @first_property: the name of the first property to set, or %NULL * @...: a list of property name and value pairs, terminated by %NULL * * Adds @actor to @box, placing it after @sibling, and sets layout * properties at the same time, if the #ClutterLayoutManager used by * @box supports them * * If @sibling is %NULL then @actor is placed at the end of the * list of children, to be allocated and painted after every other child * * This function is a wrapper around clutter_container_add_actor(), * clutter_container_raise_child() and clutter_layout_manager_child_set() * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_insert_child_above() instead. * To set specific layout properties, use clutter_layout_manager_child_set() */ void clutter_box_pack_after (ClutterBox *box, ClutterActor *actor, ClutterActor *sibling, const gchar *first_property, ...) { va_list var_args; g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); clutter_container_raise_child (CLUTTER_CONTAINER (box), actor, sibling); if (first_property == NULL || *first_property == '\0') return; va_start (var_args, first_property); clutter_box_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } /** * clutter_box_pack_before: * @box: a #ClutterBox * @actor: a #ClutterActor * @sibling: (allow-none): a #ClutterActor or %NULL * @first_property: the name of the first property to set, or %NULL * @...: a list of property name and value pairs, terminated by %NULL * * Adds @actor to @box, placing it before @sibling, and sets layout * properties at the same time, if the #ClutterLayoutManager used by * @box supports them * * If @sibling is %NULL then @actor is placed at the beginning of the * list of children, to be allocated and painted below every other child * * This function is a wrapper around clutter_container_add_actor(), * clutter_container_lower_child() and clutter_layout_manager_child_set() * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_insert_child_below() instead. * To set specific layout properties, use clutter_layout_manager_child_set() */ void clutter_box_pack_before (ClutterBox *box, ClutterActor *actor, ClutterActor *sibling, const gchar *first_property, ...) { va_list var_args; g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); clutter_container_add_actor (CLUTTER_CONTAINER (box), actor); clutter_container_lower_child (CLUTTER_CONTAINER (box), actor, sibling); if (first_property == NULL || *first_property == '\0') return; va_start (var_args, first_property); clutter_box_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } /** * clutter_box_pack_at: * @box: a #ClutterBox * @actor: a #ClutterActor * @position: the position to insert the @actor at * @first_property: the name of the first property to set, or %NULL * @...: a list of property name and value pairs, terminated by %NULL * * Adds @actor to @box, placing it at @position, and sets layout * properties at the same time, if the #ClutterLayoutManager used by * @box supports them * * If @position is a negative number, or is larger than the number of * children of @box, the new child is added at the end of the list of * children * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_insert_child_at_index() instead. * To set specific layout properties, use clutter_layout_manager_child_set() */ void clutter_box_pack_at (ClutterBox *box, ClutterActor *actor, gint position, const gchar *first_property, ...) { va_list var_args; g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); clutter_actor_insert_child_at_index (CLUTTER_ACTOR (box), actor, position); /* we need to explicitly call this, because we're not going through * the default code paths provided by clutter_container_add() */ clutter_container_create_child_meta (CLUTTER_CONTAINER (box), actor); g_signal_emit_by_name (box, "actor-added", actor); if (first_property == NULL || *first_property == '\0') return; va_start (var_args, first_property); clutter_box_set_property_valist (box, actor, first_property, var_args); va_end (var_args); } /** * clutter_box_set_color: * @box: a #ClutterBox * @color: (allow-none): the background color, or %NULL to unset * * Sets (or unsets) the background color for @box * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_set_background_color() instead. */ void clutter_box_set_color (ClutterBox *box, const ClutterColor *color) { g_return_if_fail (CLUTTER_IS_BOX (box)); clutter_box_set_color_internal (box, color); } /** * clutter_box_get_color: * @box: a #ClutterBox * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the background color of @box * * If the #ClutterBox:color-set property is set to %FALSE the * returned #ClutterColor is undefined * * Since: 1.2 * * Deprecated: 1.10: Use clutter_actor_get_background_color() instead. */ void clutter_box_get_color (ClutterBox *box, ClutterColor *color) { g_return_if_fail (CLUTTER_IS_BOX (box)); g_return_if_fail (color != NULL); clutter_actor_get_background_color (CLUTTER_ACTOR (box), color); } muffin-6.4.1/clutter/clutter/deprecated/clutter-state.c0000664000175000017500000015327314723361714022165 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Øyvind Kolås * * Copyright (C) 2009 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-state * @short_description: State machine with animated transitions * * #ClutterState is an object controlling the tweening of properties on * multiple actors between a set of named states. #ClutterStateKeys * define how the properties are animated. If the source_state_name for a key * is NULL it is used for transition to the target state unless a specific key * exists for transitioning from the current state to the requested state. * * #ClutterState is available since Clutter 1.4. * * #ClutterState has been deprecated in Clutter 1.12. There is no direct * replacement for this API, but it's highly suggested you use a combination * of [implicit transitions][clutter-actor-animation] and explicit transitions * using #ClutterTransition and its subclasses. * * ## Using ClutterState * * The following example defines a "base" and a "hover" state in a * #ClutterState instance. * * |[ * ClutterState *state = clutter_state_new (); * ClutterColor color = { 0, }; * * // transition from any state to the "base" state * clutter_color_from_string (&color, "rgb(255, 0, 0)"); * clutter_state_set (state, NULL, "base", * actor, "color", CLUTTER_LINEAR, &color, * actor, "scale-x", CLUTTER_EASE_IN_BOUNCE, 1.0, * actor, "scale-y", CLUTTER_EASE_IN_BOUNCE, 1.0, * NULL); * * // transition from the "base" state to the "hover" state * clutter_color_from_string (&color, "rgb(0, 0, 255)"); * clutter_state_set (state, "base", "hover", * actor, "color", CLUTTER_LINEAR, &color, * actor, "scale-x", CLUTTER_EASE_OUT_BOUNCE, 1.7, * actor, "scale-y", CLUTTER_EASE_OUT_BOUNCE, 1.7, * NULL); * * // the default duration of any transition * clutter_state_set_duration (state, NULL, NULL, 500); * * // set "base" as the initial state * clutter_state_warp_to_state (state, "base"); * ]| * * The actor then uses the #ClutterState to animate through the * two states using callbacks for the #ClutterActor::enter-event and * #ClutterActor::leave-event signals. * * |[ * static gboolean * on_enter (ClutterActor *actor, * ClutterEvent *event, * ClutterState *state) * { * clutter_state_set_state (state, "hover"); * * return CLUTTER_EVENT_STOP; * } * * static gboolean * on_leave (ClutterActor *actor, * ClutterEvent *event, * ClutterState *state) * { * clutter_state_set_state (state, "base"); * * return CLUTTER_EVENT_STOP; * } * * ## ClutterState description for ClutterScript * * #ClutterState defines a custom `transitions` JSON object member which * allows describing the states. * * The `transitions` property has the following syntax: * * |[ * { * "transitions" : [ * { * "source" : "source-state", * "target" : "target-state", * "duration" : milliseconds, * "keys" : [ * [ * "object-id", * "property-name", * "easing-mode", * "final-value", * ], * [ * "object-id", * "property-name", * "easing-mode", * "final-value", * pre-delay, * post-delay; * ], * ... * ] * }, * { * "source" : "source-state", * "target" : "target-state", * "duration" : milliseconds, * }, * ... * ] * } * ]| * * Each element of the transitions array follows the same rules and order * as clutter_state_set_key() function arguments. * * The source and target values control the source and target state of the * transition. * * The pre-delay and post-delay values are optional. * * The example below is a translation into a #ClutterScript definition of * the code in the #ClutterState example above. * * |[ * { * "id" : "button-state", * "type" : "ClutterState", * "duration" : 500, * "transitions" : [ * { * "source" : "*", * "target" : "base", * "keys" : [ * [ "button", "color", "linear", "rgb(255, 0, 0)" ], * [ "button", "scale-x", "easeInBounce", 1.0 ], * [ "button", "scale-y", "easeInBounce", 1.0 ] * ] * }, * { * "source" : "base", * "target" : "hover", * "keys" : [ * [ "button", "color", "linear", "rgb(0, 0, 255)" ], * [ "button", "scale-x", "easeOutBounce", 1.7 ], * [ "button", "scale-y", "easeOutBounce", 1.7 ] * ] * } * ] * } * ]| */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-state.h" #include "clutter-alpha.h" #include "clutter-animatable.h" #include "clutter-enum-types.h" #include "clutter-interval.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-scriptable.h" #include "clutter-script-private.h" typedef struct State { const gchar *name; /* interned string for this state name */ GHashTable *durations; /* durations for transitions from various state names */ GList *keys; /* list of all keys pertaining to transitions from other states to this one */ ClutterState *clutter_state; /* the ClutterState object this state belongs to */ } State; struct _ClutterStatePrivate { GHashTable *states; /* contains state objects */ guint duration; /* global fallback duration */ ClutterTimeline *timeline; /* The timeline used for doing the progress */ ClutterTimeline *slave_timeline; /* a slave timeline used to compute alphas */ const gchar *source_state_name; /* current source state */ State *source_state; /* current source_state */ const gchar *target_state_name; /* current target state */ State *target_state; /* target state name */ }; #define SLAVE_TIMELINE_LENGTH 10000 /* * ClutterStateKey: * * An opaque data structure with accessor functions. * */ typedef struct _ClutterStateKey { GObject *object; /* an Gobject */ const gchar *property_name;/* the name of a property */ gulong mode; /* alpha to use */ GValue value; /* target value */ gdouble pre_delay; /* fraction of duration to delay before starting */ gdouble pre_pre_delay;/* fraction of duration to add to pre_delay. This is used to set keys during transitions. */ gdouble post_delay; /* fraction of duration to be done in */ State *source_state; /* source state */ State *target_state; /* target state */ ClutterAlpha *alpha; /* The alpha this key uses for interpolation */ ClutterInterval *interval; /* The interval this key uses for interpolation */ guint is_animatable : 1; guint is_inert : 1; /* set if the key is being destroyed due to weak reference */ gint ref_count; /* reference count for boxed life time */ } _ClutterStateKey; enum { PROP_0, PROP_DURATION, PROP_STATE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { COMPLETED, LAST_SIGNAL }; static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); static guint state_signals[LAST_SIGNAL] = {0, }; G_DEFINE_TYPE_WITH_CODE (ClutterState, clutter_state, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterState) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init)) /** * clutter_state_new: * * Creates a new #ClutterState * * Return value: the newly create #ClutterState instance * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ ClutterState * clutter_state_new (void) { return g_object_new (CLUTTER_TYPE_STATE, NULL); } static gint sort_props_func (gconstpointer a, gconstpointer b) { const ClutterStateKey *pa = a; const ClutterStateKey *pb = b; if (pa->object == pb->object) { gint propnamediff = pa->property_name-pb->property_name; if (propnamediff == 0) return pb->source_state - pa->source_state; return propnamediff; } return pa->object - pb->object; } static State * clutter_state_fetch_state (ClutterState *state, const gchar *state_name, gboolean force_creation); static void object_disappeared (gpointer data, GObject *where_the_object_was); static ClutterStateKey * clutter_state_key_new (State *state, GObject *object, const gchar *property_name, GParamSpec *pspec, guint mode) { ClutterStatePrivate *priv = state->clutter_state->priv; ClutterStateKey *state_key; GValue value = G_VALUE_INIT; state_key = g_slice_new0 (ClutterStateKey); state_key->target_state = state; state_key->object = object; state_key->property_name = g_intern_string (property_name); state_key->mode = mode; state_key->is_animatable = CLUTTER_IS_ANIMATABLE (object); state_key->alpha = clutter_alpha_new (); g_object_ref_sink (state_key->alpha); clutter_alpha_set_mode (state_key->alpha, mode); clutter_alpha_set_timeline (state_key->alpha, priv->slave_timeline); state_key->interval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", G_PARAM_SPEC_VALUE_TYPE (pspec), NULL); g_object_ref_sink (state_key->interval); g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); clutter_interval_set_initial_value (state_key->interval, &value); clutter_interval_set_final_value (state_key->interval, &value); g_value_unset (&value); g_object_weak_ref (object, object_disappeared, state_key->target_state->clutter_state); return state_key; } static void clutter_state_key_free (gpointer clutter_state_key) { ClutterStateKey *key = clutter_state_key; if (key == NULL) return; key->ref_count -= 1; if (key->ref_count > 0) return; if (!key->is_inert) { g_object_weak_unref (key->object, object_disappeared, key->target_state->clutter_state); } g_value_unset (&key->value); g_object_unref (key->alpha); g_object_unref (key->interval); g_slice_free (ClutterStateKey, key); } static inline void clutter_state_remove_key_internal (ClutterState *this, const gchar *source_state_name, const gchar *target_state_name, GObject *object, const gchar *property_name, gboolean is_inert) { GList *s, *state_list; State *source_state = NULL; source_state_name = g_intern_string (source_state_name); target_state_name = g_intern_string (target_state_name); property_name = g_intern_string (property_name); if (source_state_name) source_state = clutter_state_fetch_state (this, source_state_name, FALSE); again_from_start: if (target_state_name != NULL) state_list = g_list_append (NULL, (gpointer) target_state_name); else state_list = clutter_state_get_states (this); for (s = state_list; s != NULL; s = s->next) { State *target_state; target_state = clutter_state_fetch_state (this, s->data, FALSE); /* Go through each TargetState */ if (target_state) { GList *k = target_state->keys; /* Note the safe while() loop, because we modify the list inline */ while (k != NULL) { ClutterStateKey *key = k->data; k = k->next; /* Check if each key matches query */ if ( (object == NULL || (object == key->object)) && (source_state == NULL || (source_state == key->source_state)) && (property_name == NULL || ((property_name == key->property_name)))) { /* Remove matching key */ target_state->keys = g_list_remove (target_state->keys, key); key->is_inert = is_inert; clutter_state_key_free (key); /* no more keys with transitions to this target_state*/ if (target_state->keys == NULL) { /* If this state is the current state, unset the state */ if (target_state == this->priv->target_state) clutter_state_set_state (this, NULL); /* remove any keys that exist that uses this state as a source */ clutter_state_remove_key_internal (this, s->data, NULL, NULL, NULL, is_inert); g_hash_table_remove (this->priv->states, s->data); goto again_from_start; /* we have just freed State *target_state, so need to restart removal */ } } } } } g_list_free (state_list); } static void object_disappeared (gpointer data, GObject *where_the_object_was) { clutter_state_remove_key_internal (data, NULL, NULL, (gpointer) where_the_object_was, NULL, TRUE); } static void state_free (gpointer data) { State *state = data; for (; state->keys; state->keys = g_list_remove (state->keys, state->keys->data)) clutter_state_key_free (state->keys->data); g_hash_table_destroy (state->durations); g_free (state); } static State * state_new (ClutterState *clutter_state, const gchar *name) { State *state; state = g_new0 (State, 1); state->clutter_state = clutter_state; state->name = name; state->durations = g_hash_table_new (g_direct_hash, g_direct_equal); return state; } static void clutter_state_finalize (GObject *object) { ClutterStatePrivate *priv = CLUTTER_STATE (object)->priv; g_hash_table_destroy (priv->states); g_object_unref (priv->timeline); g_object_unref (priv->slave_timeline); G_OBJECT_CLASS (clutter_state_parent_class)->finalize (object); } static void clutter_state_completed (ClutterTimeline *timeline, ClutterState *state) { g_signal_emit (state, state_signals[COMPLETED], 0); } static void clutter_state_new_frame (ClutterTimeline *timeline, gint msecs, ClutterState *state) { ClutterStatePrivate *priv = state->priv; GList *k; gdouble progress; const gchar *curprop = NULL; GObject *curobj = NULL; gboolean found_specific = FALSE; progress = clutter_timeline_get_progress (timeline); for (k = priv->target_state->keys; k; k = k->next) { ClutterStateKey *key = k->data; gdouble sub_progress; if ((curprop && !(curprop == key->property_name)) || key->object != curobj) { curprop = key->property_name; curobj = key->object; found_specific = FALSE; } if (!found_specific) { if (key->source_state != NULL && key->source_state->name != NULL && priv->source_state_name != NULL && g_str_equal (priv->source_state_name, key->source_state->name)) { found_specific = TRUE; } if (found_specific || key->source_state == NULL) { gdouble pre_delay = key->pre_delay + key->pre_pre_delay; sub_progress = (progress - pre_delay) / (1.0 - (pre_delay + key->post_delay)); if (sub_progress >= 0.0) { if (sub_progress >= 1.0) sub_progress = 1.0; clutter_timeline_advance (priv->slave_timeline, sub_progress * SLAVE_TIMELINE_LENGTH); sub_progress = clutter_alpha_get_alpha (key->alpha); if (key->is_animatable) { ClutterAnimatable *animatable; GValue value = G_VALUE_INIT; gboolean res; animatable = CLUTTER_ANIMATABLE (key->object); g_value_init (&value, clutter_state_key_get_property_type (key)); res = clutter_animatable_interpolate_value (animatable, key->property_name, key->interval, sub_progress, &value); if (res) clutter_animatable_set_final_state (animatable, key->property_name, &value); g_value_unset (&value); } else { const GValue *value; value = clutter_interval_compute (key->interval, sub_progress); if (value != NULL) g_object_set_property (key->object, key->property_name, value); } } /* XXX: should the target value of the default destination be * used even when found a specific source_state key? */ } } } } static ClutterTimeline * clutter_state_change (ClutterState *state, const gchar *target_state_name, gboolean animate) { ClutterStatePrivate *priv; State *new_state; guint duration; GList *k; g_return_val_if_fail (CLUTTER_IS_STATE (state), NULL); priv = state->priv; /* If we've been asked to change state to NULL, reset the * ClutterState to its initial state, but leave the keys * alone. */ if (!target_state_name) { if (!priv->target_state) return NULL; priv->source_state_name = priv->target_state_name = NULL; priv->source_state = priv->target_state = NULL; clutter_timeline_stop (priv->timeline); clutter_timeline_rewind (priv->timeline); return NULL; } target_state_name = g_intern_string (target_state_name); if (target_state_name == priv->target_state_name) { /* Avoid transitioning if the desired state is already current, * unless we're warping to it and the state transition is in * progress (in that case, immediately warp to the state). */ if (!clutter_timeline_is_playing (priv->timeline) || animate) return priv->timeline; } priv->source_state_name = priv->target_state_name; priv->target_state_name = target_state_name; g_object_notify_by_pspec (G_OBJECT (state), obj_props[PROP_STATE]); duration = clutter_state_get_duration (state, priv->source_state_name, priv->target_state_name); clutter_timeline_set_duration (priv->timeline, duration); new_state = clutter_state_fetch_state (state, target_state_name, FALSE); if (new_state == NULL) { g_warning ("State '%s' not found", target_state_name); return NULL; } for (k = new_state->keys; k != NULL; k = k->next) { ClutterStateKey *key = k->data; GValue initial = G_VALUE_INIT; /* Reset the pre-pre-delay - this is only used for setting keys * during transitions. */ key->pre_pre_delay = 0; g_value_init (&initial, clutter_interval_get_value_type (key->interval)); if (key->is_animatable) { ClutterAnimatable *animatable; animatable = CLUTTER_ANIMATABLE (key->object); clutter_animatable_get_initial_state (animatable, key->property_name, &initial); } else g_object_get_property (key->object, key->property_name, &initial); if (clutter_alpha_get_mode (key->alpha) != key->mode) clutter_alpha_set_mode (key->alpha, key->mode); clutter_interval_set_initial_value (key->interval, &initial); clutter_interval_set_final_value (key->interval, &key->value); g_value_unset (&initial); } if (!animate) { clutter_timeline_stop (priv->timeline); clutter_timeline_advance (priv->timeline, duration); /* emit signals, to change properties, and indicate that the * state change is complete */ g_signal_emit_by_name (priv->timeline, "new-frame", GINT_TO_POINTER (duration), NULL); g_signal_emit_by_name (priv->timeline, "completed", NULL); } else { clutter_timeline_stop (priv->timeline); clutter_timeline_rewind (priv->timeline); clutter_timeline_start (priv->timeline); } return priv->timeline; } /** * clutter_state_set_state: * @state: a #ClutterState * @target_state_name: the state to transition to * * Change the current state of #ClutterState to @target_state_name. * * The state will animate during its transition, see * #clutter_state_warp_to_state for animation-free state switching. * * Setting a %NULL state will stop the current animation and unset * the current state, but keys will be left intact. * * Return value: (transfer none): the #ClutterTimeline that drives the * state transition. The returned timeline is owned by the #ClutterState * and it should not be unreferenced * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ ClutterTimeline * clutter_state_set_state (ClutterState *state, const gchar *target_state_name) { return clutter_state_change (state, target_state_name, TRUE); } /** * clutter_state_warp_to_state: * @state: a #ClutterState * @target_state_name: the state to transition to * * Change to the specified target state immediately with no animation. * * See clutter_state_set_state(). * * Return value: (transfer none): the #ClutterTimeline that drives the * state transition. The returned timeline is owned by the #ClutterState * and it should not be unreferenced * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ ClutterTimeline * clutter_state_warp_to_state (ClutterState *state, const gchar *target_state_name) { return clutter_state_change (state, target_state_name, FALSE); } static GParamSpec * get_property_from_object (GObject *gobject, const gchar *property_name) { GParamSpec *pspec; if (CLUTTER_IS_ANIMATABLE (gobject)) { ClutterAnimatable *animatable = CLUTTER_ANIMATABLE (gobject); pspec = clutter_animatable_find_property (animatable, property_name); } else { GObjectClass *klass = G_OBJECT_GET_CLASS (gobject); pspec = g_object_class_find_property (klass, property_name); } if (pspec == NULL) { g_warning ("Cannot bind property '%s': objects of type '%s' " "do not have this property", property_name, G_OBJECT_TYPE_NAME (gobject)); return NULL; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("Cannot bind property '%s' of object of type '%s': " "the property is not writable", property_name, G_OBJECT_TYPE_NAME (gobject)); return NULL; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("Cannot bind property '%s' of object of type '%s': " "the property is not readable", property_name, G_OBJECT_TYPE_NAME (gobject)); return NULL; } if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { g_warning ("Cannot bind property '%s' of object of type '%s': " "the property is set as constructor-only", property_name, G_OBJECT_TYPE_NAME (gobject)); return NULL; } return pspec; } /** * clutter_state_set: * @state: a #ClutterState instance. * @source_state_name: (allow-none): the name of the source state keys are being added for * @target_state_name: the name of the target state keys are being added for * @first_object: a #GObject * @first_property_name: a property of @first_object to specify a key for * @first_mode: the id of the alpha function to use * @...: the value @first_property_name should have in @target_state_name, * followed by object, property name, mode, value tuples, terminated * by %NULL * * Adds multiple keys to a named state of a #ClutterState instance, specifying * the easing mode and value a given property of an object should have at a * given progress of the animation. * * The mode specified is the easing mode used when going to from the previous * key to the specified key. * * For instance, the code below: * * |[ * clutter_state_set (state, NULL, "hover", * button, "opacity", CLUTTER_LINEAR, 255, * button, "scale-x", CLUTTER_EASE_OUT_CUBIC, 1.2, * button, "scale-y", CLUTTER_EASE_OUT_CUBIC, 1.2, * NULL); * ]| * * will create a transition from any state (a @source_state_name or NULL is * treated as a wildcard) and a state named "hover"; the * button object will have the #ClutterActor:opacity * property animated to a value of 255 using %CLUTTER_LINEAR as the animation * mode, and the #ClutterActor:scale-x and #ClutterActor:scale-y properties * animated to a value of 1.2 using %CLUTTER_EASE_OUT_CUBIC as the animation * mode. To change the state (and start the transition) you can use the * clutter_state_set_state() function: * * |[ * clutter_state_set_state (state, "hover"); * ]| * * If a given object, state_name, property tuple already exist in the * #ClutterState instance, then the mode and value will be replaced with * the new specified values. * * If a property name is prefixed with "delayed::" two additional * arguments per key are expected: a value relative to the full state time * to pause before transitioning and a similar value to pause after * transitioning, e.g.: * * |[ * clutter_state_set (state, "hover", "toggled", * button, "delayed::scale-x", CLUTTER_LINEAR, 1.0, 0.2, 0.2, * button, "delayed::scale-y", CLUTTER_LINEAR, 1.0, 0.2, 0.2, * NULL); * ]| * * will pause for 20% of the duration of the transition before animating, * and 20% of the duration after animating. * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ void clutter_state_set (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, gpointer first_object, const gchar *first_property_name, gulong first_mode, ...) { gpointer object; const gchar *property_name; gulong mode; va_list args; g_return_if_fail (CLUTTER_IS_STATE (state)); object = first_object; property_name = first_property_name; mode = first_mode; g_return_if_fail (G_IS_OBJECT (first_object)); g_return_if_fail (first_property_name); va_start (args, first_mode); while (object != NULL) { GParamSpec *pspec; GValue value = G_VALUE_INIT; gchar *error = NULL; gboolean is_delayed = FALSE; if (g_str_has_prefix (property_name, "delayed::")) { property_name = strstr (property_name, "::") + 2; is_delayed = TRUE; } pspec = get_property_from_object (object, property_name); if (pspec == NULL) break; G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), args, 0, &error); if (error != NULL) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } if (is_delayed) { gdouble pre_delay = va_arg (args, gdouble); gdouble post_delay = va_arg (args, gdouble); clutter_state_set_key (state, source_state_name, target_state_name, object, property_name, mode, &value, pre_delay, post_delay); } else { clutter_state_set_key (state, source_state_name, target_state_name, object, property_name, mode, &value, 0.0, 0.0); } g_value_unset (&value); object = va_arg (args, gpointer); if (object != NULL) { property_name = va_arg (args, gchar*); mode = va_arg (args, gulong); } } va_end (args); } static void clutter_state_set_key_internal (ClutterState *state, ClutterStateKey *key) { ClutterStatePrivate *priv = state->priv; State *target_state = key->target_state; GList *old_item = NULL; if ((old_item = g_list_find_custom (target_state->keys, key, sort_props_func))) { ClutterStateKey *old_key = old_item->data; target_state->keys = g_list_remove (target_state->keys, old_key); clutter_state_key_free (old_key); } target_state->keys = g_list_insert_sorted (target_state->keys, key, sort_props_func); /* If the current target state is modified, we have some work to do. * * If the animation is running, we add a key to the current animation * with a delay of the current duration so that the new animation will * animate into place. * * If the animation isn't running, but the state is set, we immediately * warp to that state. */ if (key->target_state == priv->target_state) { if (!clutter_timeline_is_playing (priv->timeline)) { /* We can warp to the state by setting a NULL state, then setting * the target state again. */ clutter_state_change (state, NULL, FALSE); clutter_state_change (state, target_state->name, FALSE); } else { /* Set the ClutterInterval associated with the state */ GValue initial = G_VALUE_INIT; gdouble progress = clutter_timeline_get_progress (priv->timeline); g_value_init (&initial, clutter_interval_get_value_type (key->interval)); if (key->is_animatable) { ClutterAnimatable *animatable; animatable = CLUTTER_ANIMATABLE (key->object); clutter_animatable_get_initial_state (animatable, key->property_name, &initial); } else g_object_get_property (key->object, key->property_name, &initial); if (clutter_alpha_get_mode (key->alpha) != key->mode) clutter_alpha_set_mode (key->alpha, key->mode); clutter_interval_set_initial_value (key->interval, &initial); clutter_interval_set_final_value (key->interval, &key->value); g_value_unset (&initial); /* Set the delay as if the interval had just begun */ if (progress > key->pre_delay) key->pre_pre_delay = MIN (progress - key->pre_delay, 1.0 - key->post_delay); } } } /* * clutter_state_fetch_state: * @state: a #ClutterState * @state_name: the name of the state to be retrieved * @create: %TRUE if the state should be instantiated if not found * * Retrieves the #State structure for @state_name inside the given * #ClutterState instance * * If @state_name is %NULL and @create is %TRUE then NULL will * be returned. * * Return value: a #State structure for the given name, or %NULL */ static State * clutter_state_fetch_state (ClutterState *state, const gchar *state_name, gboolean create) { ClutterStatePrivate *priv = state->priv; State *retval; if (state_name == NULL) { return NULL; } else state_name = g_intern_string (state_name); retval = g_hash_table_lookup (priv->states, state_name); if (retval == NULL && create) { retval = state_new (state, state_name); g_hash_table_insert (priv->states, (gpointer) state_name, retval); } return retval; } /** * clutter_state_set_key: * @state: a #ClutterState instance. * @source_state_name: (allow-none): the source transition to specify * transition for, or %NULL to specify the default fallback when a * more specific source state doesn't exist. * @target_state_name: the name of the transition to set a key value for. * @object: the #GObject to set a key for * @property_name: the property to set a key for * @mode: the id of the alpha function to use * @value: the value for property_name of object in state_name * @pre_delay: relative time of the transition to be idle in the beginning * of the transition * @post_delay: relative time of the transition to be idle in the end of * the transition * * Sets one specific end key for a state name, @object, @property_name * combination. * * Return value: (transfer none): the #ClutterState instance, allowing * chaining of multiple calls * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ ClutterState * clutter_state_set_key (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, GObject *object, const gchar *property_name, guint mode, const GValue *value, gdouble pre_delay, gdouble post_delay) { GParamSpec *pspec; ClutterStateKey *state_key; State *source_state = NULL; State *target_state; g_return_val_if_fail (CLUTTER_IS_STATE (state), NULL); g_return_val_if_fail (G_IS_OBJECT (object), NULL); g_return_val_if_fail (property_name, NULL); g_return_val_if_fail (value, NULL); pspec = get_property_from_object (object, property_name); if (pspec == NULL) return state; source_state = clutter_state_fetch_state (state, source_state_name, TRUE); target_state = clutter_state_fetch_state (state, target_state_name, TRUE); property_name = g_intern_string (property_name); state_key = clutter_state_key_new (target_state, object, property_name, pspec, mode); state_key->source_state = source_state; state_key->pre_delay = pre_delay; state_key->post_delay = post_delay; g_value_init (&state_key->value, G_VALUE_TYPE (value)); g_value_copy (value, &state_key->value); clutter_state_set_key_internal (state, state_key); return state; } /** * clutter_state_get_states: * @state: a #ClutterState instance. * * Gets a list of all the state names managed by this #ClutterState. * * Return value: (transfer container) (element-type utf8): a newly allocated * #GList of state names. The contents of the returned #GList are owned * by the #ClutterState and should not be modified or freed. Use * g_list_free() to free the resources allocated by the returned list when * done using it * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ GList * clutter_state_get_states (ClutterState *state) { g_return_val_if_fail (CLUTTER_IS_STATE (state), NULL); return g_hash_table_get_keys (state->priv->states); } static void clutter_state_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterState *state = CLUTTER_STATE (object); switch (prop_id) { case PROP_STATE: clutter_state_set_state (state, g_value_get_string (value)); break; case PROP_DURATION: state->priv->duration = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_state_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterState *state = CLUTTER_STATE (object); switch (prop_id) { case PROP_STATE: g_value_set_string (value, clutter_state_get_state (state)); break; case PROP_DURATION: g_value_set_uint (value, state->priv->duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_state_class_init (ClutterStateClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->finalize = clutter_state_finalize; gobject_class->set_property = clutter_state_set_property; gobject_class->get_property = clutter_state_get_property; /** * ClutterState::completed: * @state: the #ClutterState that emitted the signal * * The ::completed signal is emitted when a #ClutterState reaches * the target state specified by clutter_state_set_state() or * clutter_state_warp_to_state(). * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ state_signals[COMPLETED] = g_signal_new (I_("completed"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStateClass, completed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterState:state: * * The currently set target state, setting it causes the * state machine to transition to the new state, use * clutter_state_warp_to_state() to change state without * a transition. * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ pspec = g_param_spec_string ("state", P_("State"), P_("Currently set state, (transition to this state might not be complete)"), NULL, CLUTTER_PARAM_READWRITE); obj_props[PROP_STATE] = pspec; g_object_class_install_property (gobject_class, PROP_STATE, pspec); /** * ClutterState:duration: * * Default duration used if an duration has not been specified for a specific * source/target state pair. The values is in milliseconds. * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ pspec = g_param_spec_uint ("duration", P_("Duration"), P_("Default transition duration"), 0, 86400000, 1000, CLUTTER_PARAM_READWRITE); obj_props[PROP_DURATION] = pspec; g_object_class_install_property (gobject_class, PROP_DURATION, pspec); } static void clutter_state_init (ClutterState *self) { ClutterStatePrivate *priv; priv = self->priv = clutter_state_get_instance_private (self); priv->states = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, state_free); self->priv->source_state_name = NULL; self->priv->target_state_name = NULL; self->priv->duration = 1000; priv->timeline = clutter_timeline_new (1000); g_signal_connect (priv->timeline, "new-frame", G_CALLBACK (clutter_state_new_frame), self); g_signal_connect (priv->timeline, "completed", G_CALLBACK (clutter_state_completed), self); priv->slave_timeline = clutter_timeline_new (SLAVE_TIMELINE_LENGTH); } static gpointer clutter_state_key_copy (gpointer boxed) { if (boxed != NULL) { ClutterStateKey *key = boxed; key->ref_count += 1; } return boxed; } G_DEFINE_BOXED_TYPE (ClutterStateKey, clutter_state_key, clutter_state_key_copy, clutter_state_key_free); /** * clutter_state_key_get_property_type: * @key: a #ClutterStateKey * * Retrieves the #GType of the property a key applies to * * Return value: the #GType of the property * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ GType clutter_state_key_get_property_type (const ClutterStateKey *key) { g_return_val_if_fail (key != NULL, G_TYPE_INVALID); return G_VALUE_TYPE (&key->value); } /** * clutter_state_set_duration: * @state: a #ClutterState * @source_state_name: (allow-none): the name of the source state, or %NULL * @target_state_name: (allow-none): the name of the target state, or %NULL * @duration: the duration of the transition, in milliseconds * * Sets the duration of a transition. * * If both state names are %NULL the default duration for @state is set. * * If only @target_state_name is specified, the passed @duration becomes * the default duration for transitions to the target state. * * If both states names are specified, the passed @duration only applies * to the specified transition. * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ void clutter_state_set_duration (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name, guint duration) { State *target_state; g_return_if_fail (CLUTTER_IS_STATE (state)); source_state_name = g_intern_string (source_state_name); if (source_state_name == g_intern_static_string ("")) source_state_name = NULL; target_state_name = g_intern_string (target_state_name); if (target_state_name == g_intern_static_string ("")) target_state_name = NULL; if (target_state_name == NULL) { state->priv->duration = duration; return; } target_state = clutter_state_fetch_state (state, target_state_name, FALSE); if (target_state != NULL) { if (source_state_name != NULL) g_hash_table_insert (target_state->durations, (gpointer) source_state_name, GINT_TO_POINTER (duration)); else g_hash_table_insert (target_state->durations, NULL, GINT_TO_POINTER (duration)); } } /** * clutter_state_get_duration: * @state: a #ClutterState * @source_state_name: (allow-none): the name of the source state to * get the duration of, or %NULL * @target_state_name: (allow-none): the name of the source state to * get the duration of, or %NULL * * Queries the duration used for transitions between a source and * target state pair * * The semantics for the query are the same as the semantics used for * setting the duration with clutter_state_set_duration() * * Return value: the duration, in milliseconds * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ guint clutter_state_get_duration (ClutterState *state, const gchar *source_state_name, const gchar *target_state_name) { State *target_state; guint ret = 0; g_return_val_if_fail (CLUTTER_IS_STATE (state), 0); source_state_name = g_intern_string (source_state_name); if (source_state_name == g_intern_static_string ("")) source_state_name = NULL; target_state_name = g_intern_string (target_state_name); if (target_state_name == g_intern_static_string ("")) target_state_name = NULL; if (target_state_name == NULL) return state->priv->duration; target_state = clutter_state_fetch_state (state, target_state_name, FALSE); if (target_state != NULL) { if (source_state_name) { ret = GPOINTER_TO_INT (g_hash_table_lookup (target_state->durations, source_state_name)); if(!ret) ret = GPOINTER_TO_INT (g_hash_table_lookup (target_state->durations, NULL)); } else ret = GPOINTER_TO_INT (g_hash_table_lookup (target_state->durations, NULL)); } if (!ret) ret = state->priv->duration; return ret; } /** * clutter_state_get_state: * @state: a #ClutterState * * Queries the currently set target state. * * During a transition this function will return the target of the transition. * * This function is useful when called from handlers of the * #ClutterState::completed signal. * * Return value: a string containing the target state. The returned string * is owned by the #ClutterState and should not be modified or freed * * Since: 1.4 * Deprecated: 1.12: Use #ClutterKeyframeTransition and * #ClutterTransitionGroup instead */ const gchar * clutter_state_get_state (ClutterState *state) { g_return_val_if_fail (CLUTTER_IS_STATE (state), NULL); return state->priv->target_state_name; } typedef struct _ParseClosure { ClutterState *state; ClutterScript *script; GValue *value; gboolean result; } ParseClosure; static void parse_state_transition (JsonArray *array, guint index_, JsonNode *element, gpointer data) { ParseClosure *clos = data; JsonObject *object; const gchar *source_name, *target_name; State *source_state, *target_state; JsonArray *keys; GSList *valid_keys = NULL; GList *array_keys, *k; if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT) { g_warning ("The 'transitions' member of a ClutterState description " "should be an array of objects, but the element %d of the " "array is of type '%s'. The element will be ignored.", index_, json_node_type_name (element)); return; } object = json_node_get_object (element); if (!json_object_has_member (object, "source") || !json_object_has_member (object, "target") || !(json_object_has_member (object, "keys"))) { g_warning ("The transition description at index %d is missing one " "of the mandatory members: source, target and keys", index_); return; } source_name = json_object_get_string_member (object, "source"); source_state = clutter_state_fetch_state (clos->state, source_name, TRUE); target_name = json_object_get_string_member (object, "target"); target_state = clutter_state_fetch_state (clos->state, target_name, TRUE); if (json_object_has_member (object, "duration")) { guint duration = json_object_get_int_member (object, "duration"); clutter_state_set_duration (clos->state, source_name, target_name, duration); } if (!json_object_has_member (object, "keys")) return; keys = json_object_get_array_member (object, "keys"); if (keys == NULL) { g_warning ("The transition description at index %d has an invalid " "key member of type '%s' when an array was expected.", index_, json_node_type_name (json_object_get_member (object, "keys"))); return; } if (G_IS_VALUE (clos->value)) valid_keys = g_slist_reverse (g_value_get_pointer (clos->value)); else g_value_init (clos->value, G_TYPE_POINTER); array_keys = json_array_get_elements (keys); for (k = array_keys; k != NULL; k = k->next) { JsonNode *node = k->data; JsonArray *key = json_node_get_array (node); ClutterStateKey *state_key; GObject *gobject; GParamSpec *pspec; const gchar *id_; const gchar *property; gulong mode; gboolean res; id_ = json_array_get_string_element (key, 0); gobject = clutter_script_get_object (clos->script, id_); if (gobject == NULL) { g_warning ("No object with id '%s' has been defined.", id_); continue; } property = json_array_get_string_element (key, 1); pspec = get_property_from_object (gobject, property); if (pspec == NULL) { g_warning ("The object of type '%s' and name '%s' has no " "property named '%s'.", G_OBJECT_TYPE_NAME (gobject), id_, property); continue; } mode = _clutter_script_resolve_animation_mode (json_array_get_element (key, 2)); state_key = clutter_state_key_new (target_state, gobject, property, pspec, mode); res = _clutter_script_parse_node (clos->script, &(state_key->value), property, json_array_get_element (key, 3), pspec); if (!res) { g_warning ("Unable to parse the key value for the " "property '%s' of object '%s' at index %d", property, id_, index_); clutter_state_key_free (state_key); continue; } switch (json_array_get_length (key)) { case 5: state_key->pre_delay = json_array_get_double_element (key, 4); state_key->post_delay = 0.0; break; case 6: state_key->pre_delay = json_array_get_double_element (key, 4); state_key->post_delay = json_array_get_double_element (key, 5); break; default: state_key->pre_delay = 0.0; state_key->post_delay = 0.0; break; } state_key->source_state = source_state; valid_keys = g_slist_prepend (valid_keys, state_key); } g_list_free (array_keys); g_value_set_pointer (clos->value, g_slist_reverse (valid_keys)); clos->result = TRUE; } static gboolean clutter_state_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { ParseClosure clos; if (strcmp (name, "transitions") != 0) return FALSE; if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) return FALSE; clos.state = CLUTTER_STATE (scriptable); clos.script = script; clos.value = value; clos.result = FALSE; json_array_foreach_element (json_node_get_array (node), parse_state_transition, &clos); return clos.result; } static void clutter_state_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { if (strcmp (name, "transitions") == 0) { ClutterState *state = CLUTTER_STATE (scriptable); GSList *keys = g_value_get_pointer (value); GSList *k; for (k = keys; k != NULL; k = k->next) clutter_state_set_key_internal (state, k->data); g_slist_free (keys); } else g_object_set_property (G_OBJECT (scriptable), name, value); } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_state_parse_custom_node; iface->set_custom_property = clutter_state_set_custom_property; } muffin-6.4.1/clutter/clutter/deprecated/clutter-group.h0000664000175000017500000000437714723361714022206 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_GROUP_DEPRECATED_H__ #define __CLUTTER_GROUP_DEPRECATED_H__ #include #include G_BEGIN_DECLS CLUTTER_DEPRECATED_FOR(clutter_actor_new) ClutterActor * clutter_group_new (void); CLUTTER_DEPRECATED_FOR(clutter_actor_get_child_at_index) ClutterActor * clutter_group_get_nth_child (ClutterGroup *self, gint index_); CLUTTER_DEPRECATED_FOR(clutter_actor_get_n_children) gint clutter_group_get_n_children (ClutterGroup *self); CLUTTER_DEPRECATED_FOR(clutter_actor_remove_all_children) void clutter_group_remove_all (ClutterGroup *self); #ifndef CLUTTER_DISABLE_DEPRECATED /* for Mr. Mallum only */ #define clutter_group_add(group,actor) G_STMT_START { \ ClutterActor *_actor = (ClutterActor *) (actor); \ if (CLUTTER_IS_GROUP ((group)) && CLUTTER_IS_ACTOR ((_actor))) \ { \ ClutterContainer *_container = (ClutterContainer *) (group); \ clutter_container_add_actor (_container, _actor); \ } } G_STMT_END #endif /* CLUTTER_DISABLE_DEPRECATED */ G_END_DECLS #endif /* __CLUTTER_GROUP_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-actor.h0000664000175000017500000001423014723361714022147 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_ACTOR_DEPRECATED_H__ #define __CLUTTER_ACTOR_DEPRECATED_H__ #include G_BEGIN_DECLS CLUTTER_DEPRECATED ClutterActor * clutter_get_actor_by_gid (guint32 id_); CLUTTER_DEPRECATED_FOR(clutter_actor_add_child) void clutter_actor_set_parent (ClutterActor *self, ClutterActor *parent); CLUTTER_DEPRECATED_FOR(clutter_actor_remove_child) void clutter_actor_unparent (ClutterActor *self); CLUTTER_DEPRECATED void clutter_actor_push_internal (ClutterActor *self); CLUTTER_DEPRECATED void clutter_actor_pop_internal (ClutterActor *self); CLUTTER_DEPRECATED void clutter_actor_show_all (ClutterActor *self); CLUTTER_DEPRECATED_FOR(clutter_actor_set_z_position) void clutter_actor_set_depth (ClutterActor *self, gfloat depth); CLUTTER_DEPRECATED_FOR(clutter_actor_get_z_position) gfloat clutter_actor_get_depth (ClutterActor *self); CLUTTER_DEPRECATED_FOR(clutter_actor_set_rotation_angle) void clutter_actor_set_rotation (ClutterActor *self, ClutterRotateAxis axis, gdouble angle, gfloat x, gfloat y, gfloat z); CLUTTER_DEPRECATED_FOR(clutter_actor_set_rotation_angle and clutter_actor_set_pivot_point) void clutter_actor_set_z_rotation_from_gravity (ClutterActor *self, gdouble angle, ClutterGravity gravity); CLUTTER_DEPRECATED_FOR(clutter_actor_get_rotation_angle) gdouble clutter_actor_get_rotation (ClutterActor *self, ClutterRotateAxis axis, gfloat *x, gfloat *y, gfloat *z); CLUTTER_DEPRECATED ClutterGravity clutter_actor_get_z_rotation_gravity (ClutterActor *self); CLUTTER_DEPRECATED_FOR(clutter_actor_set_scale and clutter_actor_set_pivot_point) void clutter_actor_set_scale_full (ClutterActor *self, gdouble scale_x, gdouble scale_y, gfloat center_x, gfloat center_y); CLUTTER_DEPRECATED_FOR(clutter_actor_get_pivot_point) void clutter_actor_get_scale_center (ClutterActor *self, gfloat *center_x, gfloat *center_y); CLUTTER_DEPRECATED_FOR(clutter_actor_get_pivot_point) ClutterGravity clutter_actor_get_scale_gravity (ClutterActor *self); CLUTTER_DEPRECATED void clutter_actor_set_anchor_point (ClutterActor *self, gfloat anchor_x, gfloat anchor_y); CLUTTER_DEPRECATED void clutter_actor_move_anchor_point (ClutterActor *self, gfloat anchor_x, gfloat anchor_y); CLUTTER_DEPRECATED ClutterGravity clutter_actor_get_anchor_point_gravity (ClutterActor *self); CLUTTER_DEPRECATED void clutter_actor_set_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity); CLUTTER_DEPRECATED void clutter_actor_move_anchor_point_from_gravity (ClutterActor *self, ClutterGravity gravity); G_END_DECLS #endif /* __CLUTTER_ACTOR_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-animation.h0000664000175000017500000001515414723361714023024 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_ANIMATION_H__ #define __CLUTTER_ANIMATION_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_ANIMATION (clutter_animation_get_type ()) #define CLUTTER_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimation)) #define CLUTTER_IS_ANIMATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ANIMATION)) #define CLUTTER_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ANIMATION, ClutterAnimationClass)) #define CLUTTER_IS_ANIMATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ANIMATION)) #define CLUTTER_ANIMATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ANIMATION, ClutterAnimationClass)) typedef struct _ClutterAnimationPrivate ClutterAnimationPrivate; typedef struct _ClutterAnimationClass ClutterAnimationClass; /** * ClutterAnimation: * * The #ClutterAnimation structure contains only private data and should * be accessed using the provided functions. * * Since: 1.0 * * Deprecated: 1.12: Use the implicit animation on #ClutterActor */ struct _ClutterAnimation { /*< private >*/ GObject parent_instance; ClutterAnimationPrivate *priv; }; /** * ClutterAnimationClass: * @started: class handler for the #ClutterAnimation::started signal * @completed: class handler for the #ClutterAnimation::completed signal * * The #ClutterAnimationClass structure contains only private data and * should be accessed using the provided functions. * * Since: 1.0 * * Deprecated: 1.12: Use the implicit animation on #ClutterActor */ struct _ClutterAnimationClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ void (* started) (ClutterAnimation *animation); void (* completed) (ClutterAnimation *animation); /*< private >*/ /* padding for future expansion */ void (*_clutter_reserved1) (void); void (*_clutter_reserved2) (void); void (*_clutter_reserved3) (void); void (*_clutter_reserved4) (void); void (*_clutter_reserved5) (void); void (*_clutter_reserved6) (void); void (*_clutter_reserved7) (void); void (*_clutter_reserved8) (void); }; CLUTTER_DEPRECATED GType clutter_animation_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED_FOR(clutter_property_transition_new) ClutterAnimation * clutter_animation_new (void); CLUTTER_DEPRECATED_FOR(clutter_transition_set_animatable) void clutter_animation_set_object (ClutterAnimation *animation, GObject *object); CLUTTER_DEPRECATED_FOR(clutter_timeline_set_progress_mode) void clutter_animation_set_mode (ClutterAnimation *animation, gulong mode); CLUTTER_DEPRECATED_FOR(clutter_timeline_get_progress_mode) gulong clutter_animation_get_mode (ClutterAnimation *animation); CLUTTER_DEPRECATED_FOR(clutter_timeline_set_duration) void clutter_animation_set_duration (ClutterAnimation *animation, guint msecs); CLUTTER_DEPRECATED_FOR(clutter_timeline_get_duration) guint clutter_animation_get_duration (ClutterAnimation *animation); CLUTTER_DEPRECATED_FOR(clutter_timeline_set_repeat_count) void clutter_animation_set_loop (ClutterAnimation *animation, gboolean loop); CLUTTER_DEPRECATED_FOR(clutter_timeline_get_repeat_count) gboolean clutter_animation_get_loop (ClutterAnimation *animation); CLUTTER_DEPRECATED void clutter_animation_set_timeline (ClutterAnimation *animation, ClutterTimeline *timeline); CLUTTER_DEPRECATED ClutterTimeline * clutter_animation_get_timeline (ClutterAnimation *animation); CLUTTER_DEPRECATED gboolean clutter_animation_has_property (ClutterAnimation *animation, const gchar *property_name); CLUTTER_DEPRECATED ClutterInterval * clutter_animation_get_interval (ClutterAnimation *animation, const gchar *property_name); /* * ClutterActor API */ CLUTTER_DEPRECATED ClutterAnimation * clutter_actor_animate (ClutterActor *actor, gulong mode, guint duration, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; CLUTTER_DEPRECATED ClutterAnimation * clutter_actor_animate_with_timeline (ClutterActor *actor, gulong mode, ClutterTimeline *timeline, const gchar *first_property_name, ...) G_GNUC_NULL_TERMINATED; G_END_DECLS #endif /* __CLUTTER_ANIMATION_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-container.h0000664000175000017500000000636714723361714023035 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * ClutterContainer: Generic actor container interface. * * Author: Emmanuele Bassi */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_CONTAINER_DEPRECATED_H__ #define __CLUTTER_CONTAINER_DEPRECATED_H__ #include G_BEGIN_DECLS CLUTTER_DEPRECATED_FOR(clutter_actor_add_child) void clutter_container_add (ClutterContainer *container, ClutterActor *first_actor, ...) G_GNUC_NULL_TERMINATED; CLUTTER_DEPRECATED_FOR(clutter_actor_add_child) void clutter_container_add_actor (ClutterContainer *container, ClutterActor *actor); CLUTTER_DEPRECATED_FOR(clutter_actor_remove_child) void clutter_container_remove (ClutterContainer *container, ClutterActor *first_actor, ...) G_GNUC_NULL_TERMINATED; CLUTTER_DEPRECATED_FOR(clutter_actor_remove_child) void clutter_container_remove_actor (ClutterContainer *container, ClutterActor *actor); CLUTTER_DEPRECATED_FOR(clutter_actor_get_children) GList * clutter_container_get_children (ClutterContainer *container); CLUTTER_DEPRECATED_FOR(clutter_actor_set_child_above_sibling) void clutter_container_raise_child (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); CLUTTER_DEPRECATED_FOR(clutter_actor_set_child_below_sibling) void clutter_container_lower_child (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); CLUTTER_DEPRECATED void clutter_container_sort_depth_order (ClutterContainer *container); G_END_DECLS #endif /* __CLUTTER_CONTAINER_DEPRECATED_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-box.h0000664000175000017500000001342414723361714021633 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009,2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_BOX_H__ #define __CLUTTER_BOX_H__ #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_BOX (clutter_box_get_type ()) #define CLUTTER_BOX(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX, ClutterBox)) #define CLUTTER_IS_BOX(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX)) #define CLUTTER_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX, ClutterBoxClass)) #define CLUTTER_IS_BOX_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX)) #define CLUTTER_BOX_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX, ClutterBoxClass)) typedef struct _ClutterBox ClutterBox; typedef struct _ClutterBoxPrivate ClutterBoxPrivate; typedef struct _ClutterBoxClass ClutterBoxClass; /** * ClutterBox: * * The #ClutterBox structure contains only private data and should * be accessed using the provided API * * Since: 1.2 */ struct _ClutterBox { /*< private >*/ ClutterActor parent_instance; ClutterBoxPrivate *priv; }; /** * ClutterBoxClass: * * The #ClutterBoxClass structure contains only private data * * Since: 1.2 */ struct _ClutterBoxClass { /*< private >*/ ClutterActorClass parent_class; /* padding, for future expansion */ void (*clutter_padding_1) (void); void (*clutter_padding_2) (void); void (*clutter_padding_3) (void); void (*clutter_padding_4) (void); void (*clutter_padding_5) (void); void (*clutter_padding_6) (void); }; CLUTTER_DEPRECATED GType clutter_box_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED_FOR(clutter_actor_new) ClutterActor * clutter_box_new (ClutterLayoutManager *manager); CLUTTER_DEPRECATED_FOR(clutter_actor_set_layout_manager) void clutter_box_set_layout_manager (ClutterBox *box, ClutterLayoutManager *manager); CLUTTER_DEPRECATED_FOR(clutter_actor_get_layout_manager) ClutterLayoutManager *clutter_box_get_layout_manager (ClutterBox *box); CLUTTER_DEPRECATED_FOR(clutter_actor_set_background_color) void clutter_box_set_color (ClutterBox *box, const ClutterColor *color); CLUTTER_DEPRECATED_FOR(clutter_actor_get_background_color) void clutter_box_get_color (ClutterBox *box, ClutterColor *color); CLUTTER_DEPRECATED_FOR(clutter_actor_add_child) void clutter_box_pack (ClutterBox *box, ClutterActor *actor, const gchar *first_property, ...); CLUTTER_DEPRECATED_FOR(clutter_actor_add_child) void clutter_box_packv (ClutterBox *box, ClutterActor *actor, guint n_properties, const gchar * const properties[], const GValue *values); CLUTTER_DEPRECATED_FOR(clutter_actor_insert_child_above) void clutter_box_pack_after (ClutterBox *box, ClutterActor *actor, ClutterActor *sibling, const gchar *first_property, ...); CLUTTER_DEPRECATED_FOR(clutter_actor_insert_child_below) void clutter_box_pack_before (ClutterBox *box, ClutterActor *actor, ClutterActor *sibling, const gchar *first_property, ...); CLUTTER_DEPRECATED_FOR(clutter_actor_insert_child_at_index) void clutter_box_pack_at (ClutterBox *box, ClutterActor *actor, gint position, const gchar *first_property, ...); G_END_DECLS #endif /* __CLUTTER_BOX_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-animation.c0000664000175000017500000015624214723361714023023 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-animation * @short_description: Simple implicit animations * @See_Also: #ClutterAnimatable, #ClutterInterval, #ClutterAlpha, * #ClutterTimeline * * #ClutterAnimation is an object providing simple, implicit animations * for #GObjects. * * #ClutterAnimation instances will bind one or more #GObject properties * belonging to a #GObject to a #ClutterInterval, and will then use a * #ClutterAlpha to interpolate the property between the initial and final * values of the interval. * * The duration of the animation is set using clutter_animation_set_duration(). * The easing mode of the animation is set using clutter_animation_set_mode(). * * If you want to control the animation you should retrieve the * #ClutterTimeline using clutter_animation_get_timeline() and then * use #ClutterTimeline functions like clutter_timeline_start(), * clutter_timeline_pause() or clutter_timeline_stop(). * * A #ClutterAnimation will emit the #ClutterAnimation::completed signal * when the #ClutterTimeline used by the animation is completed; unlike * #ClutterTimeline, though, the #ClutterAnimation::completed will not be * emitted if #ClutterAnimation:loop is set to %TRUE - that is, a looping * animation never completes. * * If the #GObject instance bound to a #ClutterAnimation implements the * #ClutterAnimatable interface it is possible for that instance to * control the way the initial and final states are interpolated. * * For convenience, it is possible to use the clutter_actor_animate() * function call which will take care of setting up and tearing down * a #ClutterAnimation instance and animate an actor between its current * state and the specified final state. * * #ClutterAnimation is available since Clutter 1.0. * * #ClutterAnimation has been deprecated in Clutter 1.12. You should use * the [implicit animation API][clutter-actor-animation] available inside * #ClutterActor instead. If you require to define explicit transitions for * one or more properties in order to reuse them, see #ClutterTransition * instead. * * ## Defining ClutterAnimationMode inside ClutterScript * * When defining a #ClutterAnimation inside a ClutterScript * file or string the #ClutterAnimation:mode can be defined either * using the #ClutterAnimationMode enumeration values through their * "nick" (the short string used inside #GEnumValue), their numeric * id, or using the following strings: * * - easeInQuad, easeOutQuad, easeInOutQuad * - easeInCubic, easeOutCubic, easeInOutCubic * - easeInQuart, easeOutQuart, easeInOutQuart * - easeInQuint, easeOutQuint, easeInOutQuint * - easeInSine, easeOutSine, easeInOutSine * - easeInExpo, easeOutExpo, easeInOutExpo * - easeInCirc, easeOutCirc, easeInOutCirc * - easeInElastic, easeOutElastic, easeInOutElastic * - easeInBack, easeOutBack, easeInOutBack * - easeInBounce, easeOutBounce, easeInOutBounce */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-alpha.h" #include "clutter-animatable.h" #include "clutter-animation.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-interval.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-scriptable.h" #include "clutter-script-private.h" #include "deprecated/clutter-animation.h" enum { PROP_0, PROP_OBJECT, PROP_MODE, PROP_DURATION, PROP_LOOP, PROP_TIMELINE, PROP_ALPHA, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { STARTED, COMPLETED, LAST_SIGNAL }; struct _ClutterAnimationPrivate { GObject *object; GHashTable *properties; ClutterAlpha *alpha; ClutterTimeline *timeline; guint timeline_started_id; guint timeline_completed_id; guint timeline_frame_id; }; static guint animation_signals[LAST_SIGNAL] = { 0, }; static GQuark quark_object_animation = 0; static void clutter_scriptable_init (ClutterScriptableIface *iface); static void clutter_animation_set_alpha_internal (ClutterAnimation *animation, ClutterAlpha *alpha); static ClutterAlpha * clutter_animation_get_alpha_internal (ClutterAnimation *animation); static ClutterTimeline * clutter_animation_get_timeline_internal (ClutterAnimation *animation); G_DEFINE_TYPE_WITH_CODE (ClutterAnimation, clutter_animation, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterAnimation) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_init)); static ClutterAlpha * clutter_animation_get_alpha_internal (ClutterAnimation *animation) { ClutterAnimationPrivate *priv = animation->priv; if (priv->alpha == NULL) { ClutterAlpha *alpha; alpha = clutter_alpha_new (); clutter_alpha_set_mode (alpha, CLUTTER_LINEAR); priv->alpha = g_object_ref_sink (alpha); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_ALPHA]); } return priv->alpha; } static void on_actor_destroy (ClutterActor *actor, ClutterAnimation *animation) { ClutterAnimationPrivate *priv = animation->priv; GObject *obj = G_OBJECT (actor); if (obj == priv->object) { g_object_set_qdata (priv->object, quark_object_animation, NULL); g_signal_handlers_disconnect_by_func (priv->object, G_CALLBACK (on_actor_destroy), animation); g_object_unref (animation); } } static void clutter_animation_real_completed (ClutterAnimation *self) { ClutterAnimationPrivate *priv = self->priv; ClutterAnimatable *animatable = NULL; ClutterAnimation *animation; ClutterTimeline *timeline; ClutterTimelineDirection direction; gpointer key, value; GHashTableIter iter; timeline = clutter_animation_get_timeline (self); direction = clutter_timeline_get_direction (timeline); if (CLUTTER_IS_ANIMATABLE (priv->object)) animatable = CLUTTER_ANIMATABLE (priv->object); /* explicitly set the final state of the animation */ CLUTTER_NOTE (ANIMATION, "Set final state on object [%p]", priv->object); g_hash_table_iter_init (&iter, priv->properties); while (g_hash_table_iter_next (&iter, &key, &value)) { const gchar *p_name = key; ClutterInterval *interval = value; GValue *p_value; if (direction == CLUTTER_TIMELINE_FORWARD) p_value = clutter_interval_peek_final_value (interval); else p_value = clutter_interval_peek_initial_value (interval); if (animatable != NULL) clutter_animatable_set_final_state (animatable, p_name, p_value); else g_object_set_property (priv->object, p_name, p_value); } /* at this point, if this animation was created by clutter_actor_animate() * and friends, the animation will be attached to the object's data; since * we want to allow developers to use g_signal_connect_after("completed") * to concatenate a new animation, we need to remove the animation back * pointer here, and unref() the animation. FIXME - we might want to * provide a clutter_animation_attach()/clutter_animation_detach() pair * to let the user reattach an animation */ animation = g_object_get_qdata (priv->object, quark_object_animation); if (animation == self) { CLUTTER_NOTE (ANIMATION, "Unsetting animation for actor [%p]", priv->object); g_object_set_qdata (priv->object, quark_object_animation, NULL); g_signal_handlers_disconnect_by_func (priv->object, G_CALLBACK (on_actor_destroy), animation); CLUTTER_NOTE (ANIMATION, "Releasing the reference Animation [%p]", animation); g_object_unref (animation); } } static void clutter_animation_finalize (GObject *gobject) { ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv; CLUTTER_NOTE (ANIMATION, "Destroying properties table for Animation [%p]", gobject); g_hash_table_destroy (priv->properties); G_OBJECT_CLASS (clutter_animation_parent_class)->finalize (gobject); } static void clutter_animation_dispose (GObject *gobject) { ClutterAnimationPrivate *priv = CLUTTER_ANIMATION (gobject)->priv; ClutterTimeline *timeline; if (priv->alpha != NULL) timeline = clutter_alpha_get_timeline (priv->alpha); else timeline = priv->timeline; if (timeline != NULL && priv->timeline_started_id != 0) g_signal_handler_disconnect (timeline, priv->timeline_started_id); if (timeline != NULL && priv->timeline_completed_id != 0) g_signal_handler_disconnect (timeline, priv->timeline_completed_id); if (timeline != NULL && priv->timeline_frame_id != 0) g_signal_handler_disconnect (timeline, priv->timeline_frame_id); priv->timeline_started_id = 0; priv->timeline_completed_id = 0; priv->timeline_frame_id = 0; if (priv->timeline != NULL) { g_object_unref (priv->timeline); priv->timeline = NULL; } if (priv->alpha != NULL) { g_object_unref (priv->alpha); priv->alpha = NULL; } if (priv->object != NULL) { g_object_unref (priv->object); priv->object = NULL; } G_OBJECT_CLASS (clutter_animation_parent_class)->dispose (gobject); } static void clutter_animation_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterAnimation *animation = CLUTTER_ANIMATION (gobject); switch (prop_id) { case PROP_OBJECT: clutter_animation_set_object (animation, g_value_get_object (value)); break; case PROP_MODE: clutter_animation_set_mode (animation, g_value_get_ulong (value)); break; case PROP_DURATION: clutter_animation_set_duration (animation, g_value_get_uint (value)); break; case PROP_LOOP: clutter_animation_set_loop (animation, g_value_get_boolean (value)); break; case PROP_TIMELINE: clutter_animation_set_timeline (animation, g_value_get_object (value)); break; case PROP_ALPHA: clutter_animation_set_alpha_internal (animation, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_animation_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterAnimation *animation = CLUTTER_ANIMATION (gobject); ClutterAnimationPrivate *priv = animation->priv; switch (prop_id) { case PROP_OBJECT: g_value_set_object (value, priv->object); break; case PROP_MODE: g_value_set_ulong (value, clutter_animation_get_mode (animation)); break; case PROP_DURATION: g_value_set_uint (value, clutter_animation_get_duration (animation)); break; case PROP_LOOP: g_value_set_boolean (value, clutter_animation_get_loop (animation)); break; case PROP_TIMELINE: g_value_set_object (value, clutter_animation_get_timeline (animation)); break; case PROP_ALPHA: g_value_set_object (value, clutter_animation_get_alpha_internal (animation)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static gboolean clutter_animation_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { if (strncmp (name, "mode", 4) == 0) { gulong mode; mode = _clutter_script_resolve_animation_mode (node); g_value_init (value, G_TYPE_ULONG); g_value_set_ulong (value, mode); return TRUE; } return FALSE; } static void clutter_scriptable_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_animation_parse_custom_node; } static void clutter_animation_class_init (ClutterAnimationClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); quark_object_animation = g_quark_from_static_string ("clutter-actor-animation"); klass->completed = clutter_animation_real_completed; gobject_class->set_property = clutter_animation_set_property; gobject_class->get_property = clutter_animation_get_property; gobject_class->dispose = clutter_animation_dispose; gobject_class->finalize = clutter_animation_finalize; /** * ClutterAnimation:object: * * The #GObject to which the animation applies. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ obj_props[PROP_OBJECT] = g_param_spec_object ("object", P_("Object"), P_("Object to which the animation applies"), G_TYPE_OBJECT, CLUTTER_PARAM_READWRITE); /** * ClutterAnimation:mode: * * The animation mode, either a value from #ClutterAnimationMode * or a value returned by clutter_alpha_register_func(). The * default value is %CLUTTER_LINEAR. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ obj_props[PROP_MODE] = g_param_spec_ulong ("mode", P_("Mode"), P_("The mode of the animation"), 0, G_MAXULONG, CLUTTER_LINEAR, CLUTTER_PARAM_READWRITE); /** * ClutterAnimation:duration: * * The duration of the animation, expressed in milliseconds. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ obj_props[PROP_DURATION] = g_param_spec_uint ("duration", P_("Duration"), P_("Duration of the animation, in milliseconds"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterAnimation:loop: * * Whether the animation should loop. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ obj_props[PROP_LOOP] = g_param_spec_boolean ("loop", P_("Loop"), P_("Whether the animation should loop"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterAnimation:timeline: * * The #ClutterTimeline used by the animation. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ obj_props[PROP_TIMELINE] = g_param_spec_object ("timeline", P_("Timeline"), P_("The timeline used by the animation"), CLUTTER_TYPE_TIMELINE, CLUTTER_PARAM_READWRITE); /** * ClutterAnimation:alpha: * * The #ClutterAlpha used by the animation. * * Since: 1.0 * * Deprecated: 1.10: Use the #ClutterAnimation:timeline property and * the #ClutterTimeline:progress-mode property instead. */ obj_props[PROP_ALPHA] = g_param_spec_object ("alpha", P_("Alpha"), P_("The alpha used by the animation"), CLUTTER_TYPE_ALPHA, CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); /** * ClutterAnimation::started: * @animation: the animation that emitted the signal * * The ::started signal is emitted once the animation has been * started * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ animation_signals[STARTED] = g_signal_new (I_("started"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterAnimationClass, started), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterAnimation::completed: * @animation: the animation that emitted the signal * * The ::completed signal is emitted once the animation has * been completed. * * The @animation instance is guaranteed to be valid for the entire * duration of the signal emission chain. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ animation_signals[COMPLETED] = g_signal_new (I_("completed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterAnimationClass, completed), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void clutter_animation_init (ClutterAnimation *self) { self->priv = clutter_animation_get_instance_private (self); self->priv->properties = g_hash_table_new_full (g_str_hash, g_str_equal, (GDestroyNotify) g_free, (GDestroyNotify) g_object_unref); } static inline void clutter_animation_bind_property_internal (ClutterAnimation *animation, const gchar *property_name, GParamSpec *pspec, ClutterInterval *interval) { ClutterAnimationPrivate *priv = animation->priv; if (!clutter_interval_validate (interval, pspec)) { g_warning ("Cannot bind property '%s': the interval is out " "of bounds", property_name); return; } g_hash_table_insert (priv->properties, g_strdup (property_name), g_object_ref_sink (interval)); } static inline void clutter_animation_update_property_internal (ClutterAnimation *animation, const gchar *property_name, GParamSpec *pspec, ClutterInterval *interval) { ClutterAnimationPrivate *priv = animation->priv; if (!clutter_interval_validate (interval, pspec)) { g_warning ("Cannot bind property '%s': the interval is out " "of bounds", property_name); return; } g_hash_table_replace (priv->properties, g_strdup (property_name), g_object_ref_sink (interval)); } /** * clutter_animation_has_property: * @animation: a #ClutterAnimation * @property_name: name of the property * * Checks whether @animation is controlling @property_name. * * Return value: %TRUE if the property is animated by the * #ClutterAnimation, %FALSE otherwise * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ gboolean clutter_animation_has_property (ClutterAnimation *animation, const gchar *property_name) { ClutterAnimationPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE); g_return_val_if_fail (property_name != NULL, FALSE); priv = animation->priv; return g_hash_table_lookup (priv->properties, property_name) != NULL; } /** * clutter_animation_get_interval: * @animation: a #ClutterAnimation * @property_name: name of the property * * Retrieves the #ClutterInterval associated to @property_name * inside @animation. * * Return value: (transfer none): a #ClutterInterval or %NULL if no * property with the same name was found. The returned interval is * owned by the #ClutterAnimation and should not be unreferenced * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ ClutterInterval * clutter_animation_get_interval (ClutterAnimation *animation, const gchar *property_name) { ClutterAnimationPrivate *priv; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); g_return_val_if_fail (property_name != NULL, NULL); priv = animation->priv; return g_hash_table_lookup (priv->properties, property_name); } static void on_timeline_started (ClutterTimeline *timeline, ClutterAnimation *animation) { g_signal_emit (animation, animation_signals[STARTED], 0); } static void on_timeline_completed (ClutterTimeline *timeline, ClutterAnimation *animation) { CLUTTER_NOTE (ANIMATION, "Timeline [%p] complete", timeline); if (!clutter_animation_get_loop (animation)) g_signal_emit (animation, animation_signals[COMPLETED], 0); } static void on_timeline_frame (ClutterTimeline *timeline, gint elapsed, ClutterAnimation *animation) { ClutterAnimationPrivate *priv; GList *properties, *p; gdouble alpha_value; gboolean is_animatable = FALSE; ClutterAnimatable *animatable = NULL; /* make sure the animation survives the notification */ g_object_ref (animation); priv = animation->priv; if (priv->alpha != NULL) alpha_value = clutter_alpha_get_alpha (priv->alpha); else alpha_value = clutter_timeline_get_progress (priv->timeline); if (CLUTTER_IS_ANIMATABLE (priv->object)) { animatable = CLUTTER_ANIMATABLE (priv->object); is_animatable = TRUE; } g_object_freeze_notify (priv->object); properties = g_hash_table_get_keys (priv->properties); for (p = properties; p != NULL; p = p->next) { const gchar *p_name = p->data; ClutterInterval *interval; GValue value = G_VALUE_INIT; gboolean apply; interval = g_hash_table_lookup (priv->properties, p_name); g_assert (CLUTTER_IS_INTERVAL (interval)); g_value_init (&value, clutter_interval_get_value_type (interval)); if (is_animatable) { apply = clutter_animatable_interpolate_value (animatable, p_name, interval, alpha_value, &value); } else { apply = clutter_interval_compute_value (interval, alpha_value, &value); } if (apply) { if (is_animatable) clutter_animatable_set_final_state (animatable, p_name, &value); else g_object_set_property (priv->object, p_name, &value); } g_value_unset (&value); } g_list_free (properties); g_object_thaw_notify (priv->object); g_object_unref (animation); } static ClutterTimeline * clutter_animation_get_timeline_internal (ClutterAnimation *animation) { ClutterAnimationPrivate *priv = animation->priv; ClutterTimeline *timeline; if (priv->timeline != NULL) return priv->timeline; if (priv->alpha != NULL) { timeline = clutter_alpha_get_timeline (priv->alpha); if (timeline != NULL) return timeline; } timeline = g_object_new (CLUTTER_TYPE_TIMELINE, NULL); priv->timeline_started_id = g_signal_connect (timeline, "started", G_CALLBACK (on_timeline_started), animation); priv->timeline_completed_id = g_signal_connect (timeline, "completed", G_CALLBACK (on_timeline_completed), animation); priv->timeline_frame_id = g_signal_connect (timeline, "new-frame", G_CALLBACK (on_timeline_frame), animation); if (priv->alpha != NULL) { clutter_alpha_set_timeline (priv->alpha, timeline); /* the alpha owns the timeline now */ g_object_unref (timeline); } priv->timeline = timeline; g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]); return priv->timeline; } static void clutter_animation_set_alpha_internal (ClutterAnimation *animation, ClutterAlpha *alpha) { ClutterAnimationPrivate *priv; ClutterTimeline *timeline; priv = animation->priv; if (priv->alpha == alpha) return; g_object_freeze_notify (G_OBJECT (animation)); if (priv->alpha != NULL) timeline = clutter_alpha_get_timeline (priv->alpha); else timeline = NULL; /* disconnect the old timeline first */ if (timeline != NULL && priv->timeline_started_id != 0) { g_signal_handler_disconnect (timeline, priv->timeline_started_id); priv->timeline_started_id = 0; } if (timeline != NULL && priv->timeline_completed_id != 0) { g_signal_handler_disconnect (timeline, priv->timeline_completed_id); priv->timeline_completed_id = 0; } /* then we need to disconnect the signal handler from the old alpha */ if (timeline != NULL && priv->timeline_frame_id != 0) { g_signal_handler_disconnect (timeline, priv->timeline_frame_id); priv->timeline_frame_id = 0; } if (priv->alpha != NULL) { /* this will take care of any reference we hold on the timeline */ g_object_unref (priv->alpha); priv->alpha = NULL; } if (alpha == NULL) goto out; priv->alpha = g_object_ref_sink (alpha); /* if the alpha has a timeline then we use it, otherwise we create one */ timeline = clutter_alpha_get_timeline (priv->alpha); if (timeline != NULL) { priv->timeline_started_id = g_signal_connect (timeline, "started", G_CALLBACK (on_timeline_started), animation); priv->timeline_completed_id = g_signal_connect (timeline, "completed", G_CALLBACK (on_timeline_completed), animation); priv->timeline_frame_id = g_signal_connect (timeline, "new-frame", G_CALLBACK (on_timeline_frame), animation); } else { /* FIXME - add a create_timeline_internal() because this does * not look very good */ (void) clutter_animation_get_timeline_internal (animation); } out: /* emit all relevant notifications */ g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_MODE]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_ALPHA]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]); g_object_thaw_notify (G_OBJECT (animation)); } /** * clutter_animation_new: * * Creates a new #ClutterAnimation instance. You should set the * #GObject to be animated using clutter_animation_set_object(), * set the duration with clutter_animation_set_duration() and the * easing mode using clutter_animation_set_mode(). * * The clutter_actor_animate() and relative family of functions provide * an easy way to animate a #ClutterActor and automatically manage the * lifetime of a #ClutterAnimation instance, so you should consider using * those functions instead of manually creating an animation. * * Return value: the newly created #ClutterAnimation. Use g_object_unref() * to release the associated resources * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ ClutterAnimation * clutter_animation_new (void) { return g_object_new (CLUTTER_TYPE_ANIMATION, NULL); } /** * clutter_animation_set_object: * @animation: a #ClutterAnimation * @object: a #GObject * * Attaches @animation to @object. The #ClutterAnimation will take a * reference on @object. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ void clutter_animation_set_object (ClutterAnimation *animation, GObject *object) { ClutterAnimationPrivate *priv; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_return_if_fail (object == NULL || G_IS_OBJECT (object)); priv = animation->priv; if (priv->object != NULL) { g_object_set_qdata (priv->object, quark_object_animation, NULL); g_object_unref (priv->object); priv->object = NULL; } if (object != NULL) priv->object = g_object_ref (object); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_OBJECT]); } /** * clutter_animation_set_mode: * @animation: a #ClutterAnimation * @mode: an animation mode logical id * * Sets the animation @mode of @animation. The animation @mode is * a logical id, either coming from the #ClutterAnimationMode enumeration * or the return value of clutter_alpha_register_func(). * * This function will also set #ClutterAnimation:alpha if needed. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ void clutter_animation_set_mode (ClutterAnimation *animation, gulong mode) { g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_object_freeze_notify (G_OBJECT (animation)); if (animation->priv->alpha != NULL || mode > CLUTTER_ANIMATION_LAST) { ClutterAlpha *alpha; if (animation->priv->alpha == NULL) alpha = clutter_animation_get_alpha_internal (animation); else alpha = animation->priv->alpha; clutter_alpha_set_mode (alpha, mode); } else { ClutterTimeline *timeline; timeline = clutter_animation_get_timeline_internal (animation); clutter_timeline_set_progress_mode (timeline, mode); } g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_MODE]); g_object_thaw_notify (G_OBJECT (animation)); } /** * clutter_animation_get_mode: * @animation: a #ClutterAnimation * * Retrieves the animation mode of @animation, as set by * clutter_animation_set_mode(). * * Return value: the mode for the animation * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ gulong clutter_animation_get_mode (ClutterAnimation *animation) { ClutterTimeline *timeline; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), CLUTTER_LINEAR); if (animation->priv->alpha != NULL) return clutter_alpha_get_mode (animation->priv->alpha); timeline = clutter_animation_get_timeline_internal (animation); return clutter_timeline_get_progress_mode (timeline); } /** * clutter_animation_set_duration: * @animation: a #ClutterAnimation * @msecs: the duration in milliseconds * * Sets the duration of @animation in milliseconds. * * This function will set #ClutterAnimation:alpha and * #ClutterAnimation:timeline if needed. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ void clutter_animation_set_duration (ClutterAnimation *animation, guint msecs) { ClutterTimeline *timeline; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_object_freeze_notify (G_OBJECT (animation)); timeline = clutter_animation_get_timeline_internal (animation); clutter_timeline_set_duration (timeline, msecs); clutter_timeline_rewind (timeline); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]); g_object_thaw_notify (G_OBJECT (animation)); } /** * clutter_animation_set_loop: * @animation: a #ClutterAnimation * @loop: %TRUE if the animation should loop * * Sets whether @animation should loop over itself once finished. * * A looping #ClutterAnimation will not emit the #ClutterAnimation::completed * signal when finished. * * This function will set #ClutterAnimation:alpha and * #ClutterAnimation:timeline if needed. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ void clutter_animation_set_loop (ClutterAnimation *animation, gboolean loop) { ClutterTimeline *timeline; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_object_freeze_notify (G_OBJECT (animation)); timeline = clutter_animation_get_timeline_internal (animation); clutter_timeline_set_repeat_count (timeline, loop ? -1 : 0); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]); g_object_thaw_notify (G_OBJECT (animation)); } /** * clutter_animation_get_loop: * @animation: a #ClutterAnimation * * Retrieves whether @animation is looping. * * Return value: %TRUE if the animation is looping * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ gboolean clutter_animation_get_loop (ClutterAnimation *animation) { ClutterTimeline *timeline; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), FALSE); timeline = clutter_animation_get_timeline_internal (animation); return clutter_timeline_get_repeat_count (timeline) != 0; } /** * clutter_animation_get_duration: * @animation: a #ClutterAnimation * * Retrieves the duration of @animation, in milliseconds. * * Return value: the duration of the animation * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ guint clutter_animation_get_duration (ClutterAnimation *animation) { ClutterTimeline *timeline; g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), 0); timeline = clutter_animation_get_timeline_internal (animation); return clutter_timeline_get_duration (timeline); } /** * clutter_animation_set_timeline: * @animation: a #ClutterAnimation * @timeline: (allow-none): a #ClutterTimeline, or %NULL to unset the * current #ClutterTimeline * * Sets the #ClutterTimeline used by @animation. * * This function will take a reference on the passed @timeline. * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ void clutter_animation_set_timeline (ClutterAnimation *animation, ClutterTimeline *timeline) { ClutterAnimationPrivate *priv; ClutterTimeline *cur_timeline; g_return_if_fail (CLUTTER_IS_ANIMATION (animation)); g_return_if_fail (timeline == NULL || CLUTTER_IS_TIMELINE (timeline)); priv = animation->priv; if (priv->alpha != NULL) cur_timeline = clutter_alpha_get_timeline (priv->alpha); else cur_timeline = priv->timeline; if (cur_timeline == timeline) return; g_object_freeze_notify (G_OBJECT (animation)); if (cur_timeline != NULL && priv->timeline_started_id != 0) g_signal_handler_disconnect (cur_timeline, priv->timeline_started_id); if (cur_timeline != NULL && priv->timeline_completed_id != 0) g_signal_handler_disconnect (cur_timeline, priv->timeline_completed_id); if (cur_timeline != NULL && priv->timeline_frame_id != 0) g_signal_handler_disconnect (cur_timeline, priv->timeline_frame_id); priv->timeline_started_id = 0; priv->timeline_completed_id = 0; priv->timeline_frame_id = 0; /* Release previously set timeline if any */ g_clear_object (&priv->timeline); if (priv->alpha != NULL) clutter_alpha_set_timeline (priv->alpha, timeline); else { /* Hold a reference to the timeline if it's not reffed by the priv->alpha */ priv->timeline = timeline; if (priv->timeline) g_object_ref (priv->timeline); } g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_TIMELINE]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_DURATION]); g_object_notify_by_pspec (G_OBJECT (animation), obj_props[PROP_LOOP]); if (timeline != NULL) { priv->timeline_started_id = g_signal_connect (timeline, "started", G_CALLBACK (on_timeline_started), animation); priv->timeline_completed_id = g_signal_connect (timeline, "completed", G_CALLBACK (on_timeline_completed), animation); priv->timeline_frame_id = g_signal_connect (timeline, "new-frame", G_CALLBACK (on_timeline_frame), animation); } g_object_thaw_notify (G_OBJECT (animation)); } /** * clutter_animation_get_timeline: * @animation: a #ClutterAnimation * * Retrieves the #ClutterTimeline used by @animation * * Return value: (transfer none): the timeline used by the animation * * Since: 1.0 * Deprecated: 1.12: Use #ClutterPropertyTransition instead */ ClutterTimeline * clutter_animation_get_timeline (ClutterAnimation *animation) { g_return_val_if_fail (CLUTTER_IS_ANIMATION (animation), NULL); return clutter_animation_get_timeline_internal (animation); } /* * starts the timeline */ static void clutter_animation_start (ClutterAnimation *animation) { ClutterTimeline *timeline; timeline = clutter_animation_get_timeline_internal (animation); if (G_LIKELY (timeline != NULL)) clutter_timeline_start (timeline); else { /* sanity check */ g_warning (G_STRLOC ": no timeline found, unable to start the animation"); } } static void clutter_animation_setup_property (ClutterAnimation *animation, const gchar *property_name, const GValue *value, GParamSpec *pspec, gboolean is_fixed) { ClutterAnimationPrivate *priv = animation->priv; GValue real_value = G_VALUE_INIT; if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { g_warning ("Cannot bind property '%s': the property is " "construct-only", property_name); return; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("Cannot bind property '%s': the property is " "not writable", property_name); return; } /* initialize the real value that will be used to store the * final state of the animation */ g_value_init (&real_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); /* if it's not the same type of the GParamSpec value, try to * convert it using the GValue transformation API, otherwise * just copy it */ if (!g_type_is_a (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value))) { /* are these two types compatible (can be directly copied)? */ if (g_value_type_compatible (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value))) { g_value_copy (value, &real_value); goto done; } /* are these two type transformable? */ if (g_value_type_transformable (G_VALUE_TYPE (value), G_VALUE_TYPE (&real_value))) { if (g_value_transform (value, &real_value)) goto done; } /* if not compatible and not transformable then we can't do much */ g_warning ("%s: Unable to convert from %s to %s for " "the property '%s' of object %s", G_STRLOC, g_type_name (G_VALUE_TYPE (value)), g_type_name (G_VALUE_TYPE (&real_value)), property_name, G_OBJECT_TYPE_NAME (priv->object)); g_value_unset (&real_value); return; } else g_value_copy (value, &real_value); done: /* create an interval and bind it to the property, in case * it's not a fixed property, otherwise just set it */ if (G_LIKELY (!is_fixed)) { ClutterInterval *interval; GValue cur_value = G_VALUE_INIT; g_value_init (&cur_value, G_PARAM_SPEC_VALUE_TYPE (pspec)); if (CLUTTER_IS_ANIMATABLE (priv->object)) clutter_animatable_get_initial_state (CLUTTER_ANIMATABLE (priv->object), property_name, &cur_value); else g_object_get_property (priv->object, property_name, &cur_value); interval = clutter_interval_new_with_values (G_PARAM_SPEC_VALUE_TYPE (pspec), &cur_value, &real_value); if (!clutter_animation_has_property (animation, property_name)) clutter_animation_bind_property_internal (animation, property_name, pspec, interval); else clutter_animation_update_property_internal (animation, property_name, pspec, interval); g_value_unset (&cur_value); } else { if (CLUTTER_IS_ANIMATABLE (priv->object)) clutter_animatable_set_final_state (CLUTTER_ANIMATABLE (priv->object), property_name, &real_value); else g_object_set_property (priv->object, property_name, &real_value); } g_value_unset (&real_value); } static const struct { const gchar *name; GConnectFlags flags; } signal_prefixes[] = { { "::", 0 }, { "-swapped::", G_CONNECT_SWAPPED }, { "-after::", G_CONNECT_AFTER }, { "-swapped-after::", G_CONNECT_SWAPPED | G_CONNECT_AFTER } }; static gboolean clutter_animation_has_signal_prefix (const gchar *property_name, GConnectFlags *flags, int *offset) { int i; if (!g_str_has_prefix (property_name, "signal")) return FALSE; for (i = 0; i < G_N_ELEMENTS (signal_prefixes); i++) if (g_str_has_prefix (property_name + 6, signal_prefixes[i].name)) { *offset = strlen (signal_prefixes[i].name) + 6; *flags = signal_prefixes[i].flags; return TRUE; } return FALSE; } static void clutter_animation_setup_valist (ClutterAnimation *animation, const gchar *first_property_name, va_list var_args) { ClutterAnimationPrivate *priv = animation->priv; ClutterAnimatable *animatable = NULL; GObjectClass *klass = NULL; const gchar *property_name; if (CLUTTER_IS_ANIMATABLE (priv->object)) animatable = CLUTTER_ANIMATABLE (priv->object); else klass = G_OBJECT_GET_CLASS (priv->object); property_name = first_property_name; while (property_name != NULL) { GParamSpec *pspec; GValue final = G_VALUE_INIT; gchar *error = NULL; gboolean is_fixed = FALSE; GConnectFlags flags; int offset; if (clutter_animation_has_signal_prefix (property_name, &flags, &offset)) { const gchar *signal_name = property_name + offset; GCallback callback = va_arg (var_args, GCallback); gpointer userdata = va_arg (var_args, gpointer); g_signal_connect_data (animation, signal_name, callback, userdata, NULL, flags); } else { if (g_str_has_prefix (property_name, "fixed::")) { property_name += 7; /* strlen("fixed::") */ is_fixed = TRUE; } if (animatable != NULL) pspec = clutter_animatable_find_property (animatable, property_name); else pspec = g_object_class_find_property (klass, property_name); if (pspec == NULL) { g_warning ("Cannot bind property '%s': objects of type '%s' do " "not have this property", property_name, g_type_name (G_OBJECT_TYPE (priv->object))); break; } G_VALUE_COLLECT_INIT (&final, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } clutter_animation_setup_property (animation, property_name, &final, pspec, is_fixed); g_value_unset (&final); } property_name = va_arg (var_args, gchar*); } } static ClutterAnimation * animation_create_for_actor (ClutterActor *actor) { ClutterAnimation *animation; GObject *object = G_OBJECT (actor); animation = g_object_get_qdata (object, quark_object_animation); if (animation == NULL) { animation = clutter_animation_new (); clutter_animation_set_object (animation, object); g_object_set_qdata (object, quark_object_animation, animation); /* use the ::destroy signal to get a notification * that the actor went away mid-animation */ g_signal_connect (object, "destroy", G_CALLBACK (on_actor_destroy), animation); CLUTTER_NOTE (ANIMATION, "Created new Animation [%p] for actor [%p]", animation, actor); } else { CLUTTER_NOTE (ANIMATION, "Reusing Animation [%p] for actor [%p]", animation, actor); } return animation; } /** * clutter_actor_animate_with_timeline: * @actor: a #ClutterActor * @mode: an animation mode logical id * @timeline: a #ClutterTimeline * @first_property_name: the name of a property * @...: a %NULL terminated list of property names and * property values * * Animates the given list of properties of @actor between the current * value for each property and a new final value. The animation has a * definite duration given by @timeline and a speed given by the @mode. * * See clutter_actor_animate() for further details. * * This function is useful if you want to use an existing timeline * to animate @actor. * * Return value: (transfer none): a #ClutterAnimation object. The object is * owned by the #ClutterActor and should not be unreferenced with * g_object_unref() * * Since: 1.0 * Deprecated: 1.12: Use the implicit transition for animatable properties * in #ClutterActor instead. See clutter_actor_save_easing_state(), * clutter_actor_set_easing_mode(), clutter_actor_set_easing_duration(), * clutter_actor_set_easing_delay(), and clutter_actor_restore_easing_state(). */ ClutterAnimation * clutter_actor_animate_with_timeline (ClutterActor *actor, gulong mode, ClutterTimeline *timeline, const gchar *first_property_name, ...) { ClutterAnimation *animation; va_list args; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); g_return_val_if_fail (first_property_name != NULL, NULL); animation = animation_create_for_actor (actor); clutter_animation_set_mode (animation, mode); clutter_animation_set_timeline (animation, timeline); va_start (args, first_property_name); clutter_animation_setup_valist (animation, first_property_name, args); va_end (args); clutter_animation_start (animation); return animation; } /** * clutter_actor_animate: * @actor: a #ClutterActor * @mode: an animation mode logical id * @duration: duration of the animation, in milliseconds * @first_property_name: the name of a property * @...: a %NULL terminated list of property names and * property values * * Animates the given list of properties of @actor between the current * value for each property and a new final value. The animation has a * definite duration and a speed given by the @mode. * * For example, this: * * |[ * clutter_actor_animate (rectangle, CLUTTER_LINEAR, 250, * "width", 100.0, * "height", 100.0, * NULL); * ]| * * will make width and height properties of the #ClutterActor "rectangle" * grow linearly between the current value and 100 pixels, in 250 milliseconds. * * The animation @mode is a logical id, either from the #ClutterAnimationMode * enumeration of from clutter_alpha_register_func(). * * All the properties specified will be animated between the current value * and the final value. If a property should be set at the beginning of * the animation but not updated during the animation, it should be prefixed * by the "fixed::" string, for instance: * * |[ * clutter_actor_animate (actor, CLUTTER_EASE_IN_SINE, 100, * "rotation-angle-z", 360.0, * "fixed::rotation-center-z", ¢er, * NULL); * ]| * * Will animate the "rotation-angle-z" property between the current value * and 360 degrees, and set the "rotation-center-z" property to the fixed * value of the #graphene_point3d_t "center". * * This function will implicitly create a #ClutterAnimation object which * will be assigned to the @actor and will be returned to the developer * to control the animation or to know when the animation has been * completed. * * If a name argument starts with "signal::", "signal-after::", * "signal-swapped::" or "signal-swapped-after::" the two following arguments * are used as callback function and data for a signal handler installed on * the #ClutterAnimation object for the specified signal name, for instance: * * |[ * static void * on_animation_completed (ClutterAnimation *animation, * ClutterActor *actor) * { * clutter_actor_hide (actor); * } * * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 100, * "opacity", 0, * "signal::completed", on_animation_completed, actor, * NULL); * ]| * * or, to automatically destroy an actor at the end of the animation: * * |[ * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 100, * "opacity", 0, * "signal-swapped-after::completed", * clutter_actor_destroy, * actor, * NULL); * ]| * * The "signal::" modifier is the equivalent of using g_signal_connect(); * the "signal-after::" modifier is the equivalent of using * g_signal_connect_after() or g_signal_connect_data() with the * %G_CONNECT_AFTER; the "signal-swapped::" modifier is the equivalent * of using g_signal_connect_swapped() or g_signal_connect_data() with the * %G_CONNECT_SWAPPED flah; finally, the "signal-swapped-after::" modifier * is the equivalent of using g_signal_connect_data() with both the * %G_CONNECT_AFTER and %G_CONNECT_SWAPPED flags. The clutter_actor_animate() * function will not keep track of multiple connections to the same signal, * so it is your responsability to avoid them when calling * clutter_actor_animate() multiple times on the same actor. * * Calling this function on an actor that is already being animated * will cause the current animation to change with the new final values, * the new easing mode and the new duration - that is, this code: * * |[ * clutter_actor_animate (actor, CLUTTER_LINEAR, 250, * "width", 100.0, * "height", 100.0, * NULL); * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 500, * "x", 100.0, * "y", 100.0, * "width", 200.0, * NULL); * ]| * * is the equivalent of: * * |[ * clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 500, * "x", 100.0, * "y", 100.0, * "width", 200.0, * "height", 100.0, * NULL); * ]| * * Unless the animation is looping, the #ClutterAnimation created by * clutter_actor_animate() will become invalid as soon as it is * complete. * * Since the created #ClutterAnimation instance attached to @actor * is guaranteed to be valid throughout the #ClutterAnimation::completed * signal emission chain, you will not be able to create a new animation * using clutter_actor_animate() on the same @actor from within the * #ClutterAnimation::completed signal handler unless you use * g_signal_connect_after() to connect the callback function, for instance: * * |[ * static void * on_animation_completed (ClutterAnimation *animation, * ClutterActor *actor) * { * clutter_actor_animate (actor, CLUTTER_EASE_OUT_CUBIC, 250, * "x", 500.0, * "y", 500.0, * NULL); * } * * ... * animation = clutter_actor_animate (actor, CLUTTER_EASE_IN_CUBIC, 250, * "x", 100.0, * "y", 100.0, * NULL); * g_signal_connect (animation, "completed", * G_CALLBACK (on_animation_completed), * actor); * ... * ]| * * Return value: (transfer none): a #ClutterAnimation object. The object is * owned by the #ClutterActor and should not be unreferenced with * g_object_unref() * * Since: 1.0 * Deprecated: 1.12: Use the implicit transition for animatable properties * in #ClutterActor instead. See clutter_actor_save_easing_state(), * clutter_actor_set_easing_mode(), clutter_actor_set_easing_duration(), * clutter_actor_set_easing_delay(), and clutter_actor_restore_easing_state(). */ ClutterAnimation * clutter_actor_animate (ClutterActor *actor, gulong mode, guint duration, const gchar *first_property_name, ...) { ClutterAnimation *animation; va_list args; g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); g_return_val_if_fail (mode != CLUTTER_CUSTOM_MODE, NULL); g_return_val_if_fail (duration > 0, NULL); g_return_val_if_fail (first_property_name != NULL, NULL); animation = animation_create_for_actor (actor); clutter_animation_set_mode (animation, mode); clutter_animation_set_duration (animation, duration); va_start (args, first_property_name); clutter_animation_setup_valist (animation, first_property_name, args); va_end (args); clutter_animation_start (animation); return animation; } muffin-6.4.1/clutter/clutter/deprecated/clutter-rectangle.c0000664000175000017500000004603614723361714023007 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-rectangle * @short_description: An actor that displays a simple rectangle. * * #ClutterRectangle is a #ClutterActor which draws a simple filled rectangle. * * #ClutterRectangle is deprecated since Clutter 1.10. If you want an actor * painting a solid color, you can replace it with #ClutterActor and set the * #ClutterActor:background-color property to the desired #ClutterColor. If * you are drawing more complex shapes, use #ClutterCanvas to draw using the * Cairo 2D API instead. */ #include "clutter-build-config.h" #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-rectangle.h" #include "deprecated/clutter-actor.h" #include "clutter-actor-private.h" #include "clutter-color.h" #include "clutter-debug.h" #include "clutter-main.h" #include "clutter-private.h" #include "cogl/cogl.h" struct _ClutterRectanglePrivate { ClutterColor color; ClutterColor border_color; guint border_width; guint has_border : 1; }; enum { PROP_0, PROP_COLOR, PROP_BORDER_COLOR, PROP_BORDER_WIDTH, PROP_HAS_BORDER /* FIXME: Add gradient, rounded corner props etc */ }; static const ClutterColor default_color = { 255, 255, 255, 255 }; static const ClutterColor default_border_color = { 0, 0, 0, 255 }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterRectangle, clutter_rectangle, CLUTTER_TYPE_ACTOR) static void clutter_rectangle_paint (ClutterActor *self, ClutterPaintContext *paint_context) { ClutterRectanglePrivate *priv = CLUTTER_RECTANGLE (self)->priv; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); static CoglPipeline *default_color_pipeline = NULL; CoglPipeline *content_pipeline; ClutterActorBox alloc; CoglColor color; guint8 tmp_alpha; CLUTTER_NOTE (PAINT, "painting rect '%s'", clutter_actor_get_name (self) ? clutter_actor_get_name (self) : "unknown"); clutter_actor_get_allocation_box (self, &alloc); if (G_UNLIKELY (default_color_pipeline == NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); default_color_pipeline = cogl_pipeline_new (ctx); } g_assert (default_color_pipeline != NULL); content_pipeline = cogl_pipeline_copy (default_color_pipeline); /* compute the composited opacity of the actor taking into * account the opacity of the color set by the user */ tmp_alpha = clutter_actor_get_paint_opacity (self) * priv->color.alpha / 255; cogl_color_init_from_4ub (&color, priv->color.red, priv->color.green, priv->color.blue, tmp_alpha); cogl_color_premultiply (&color); cogl_pipeline_set_color (content_pipeline, &color); if (priv->has_border) { CoglPipeline *border_pipeline; border_pipeline = cogl_pipeline_copy (default_color_pipeline); tmp_alpha = clutter_actor_get_paint_opacity (self) * priv->border_color.alpha / 255; cogl_color_init_from_4ub (&color, priv->border_color.red, priv->border_color.green, priv->border_color.blue, tmp_alpha); cogl_color_premultiply (&color); cogl_pipeline_set_color (border_pipeline, &color); /* We paint the border and the content only if the rectangle * is big enough to show them */ if ((priv->border_width * 2) < clutter_actor_box_get_width (&alloc) && (priv->border_width * 2) < clutter_actor_box_get_height (&alloc)) { /* paint the border. this sucks, but it's the only way to make a border */ cogl_framebuffer_draw_rectangle (framebuffer, border_pipeline, priv->border_width, 0, clutter_actor_box_get_width (&alloc), priv->border_width); cogl_framebuffer_draw_rectangle (framebuffer, border_pipeline, clutter_actor_box_get_width (&alloc) - priv->border_width, priv->border_width, clutter_actor_box_get_width (&alloc), clutter_actor_box_get_height (&alloc)); cogl_framebuffer_draw_rectangle (framebuffer, border_pipeline, 0, clutter_actor_box_get_height (&alloc) - priv->border_width, clutter_actor_box_get_width (&alloc) - priv->border_width, clutter_actor_box_get_height (&alloc)); cogl_framebuffer_draw_rectangle (framebuffer, border_pipeline, 0, 0, priv->border_width, clutter_actor_box_get_height (&alloc) - priv->border_width); /* now paint the rectangle */ cogl_framebuffer_draw_rectangle (framebuffer, content_pipeline, priv->border_width, priv->border_width, clutter_actor_box_get_width (&alloc) - priv->border_width, clutter_actor_box_get_height (&alloc) - priv->border_width); } else { /* Otherwise, we draw a rectangle with the same color * as the border, since we can only fit that into the * allocation. */ cogl_framebuffer_draw_rectangle (framebuffer, border_pipeline, 0, 0, clutter_actor_box_get_width (&alloc), clutter_actor_box_get_height (&alloc)); } cogl_object_unref (border_pipeline); } else { cogl_framebuffer_draw_rectangle (framebuffer, content_pipeline, 0, 0, clutter_actor_box_get_width (&alloc), clutter_actor_box_get_height (&alloc)); } cogl_object_unref (content_pipeline); } static gboolean clutter_rectangle_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { return _clutter_actor_set_default_paint_volume (self, CLUTTER_TYPE_RECTANGLE, volume); } static gboolean clutter_rectangle_has_overlaps (ClutterActor *self) { /* Rectangles never need an offscreen redirect because there are never any overlapping primitives */ return FALSE; } static void clutter_rectangle_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterRectangle *rectangle = CLUTTER_RECTANGLE(object); switch (prop_id) { case PROP_COLOR: clutter_rectangle_set_color (rectangle, clutter_value_get_color (value)); break; case PROP_BORDER_COLOR: clutter_rectangle_set_border_color (rectangle, clutter_value_get_color (value)); break; case PROP_BORDER_WIDTH: clutter_rectangle_set_border_width (rectangle, g_value_get_uint (value)); break; case PROP_HAS_BORDER: rectangle->priv->has_border = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_rectangle_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterRectanglePrivate *priv = CLUTTER_RECTANGLE(object)->priv; switch (prop_id) { case PROP_COLOR: clutter_value_set_color (value, &priv->color); break; case PROP_BORDER_COLOR: clutter_value_set_color (value, &priv->border_color); break; case PROP_BORDER_WIDTH: g_value_set_uint (value, priv->border_width); break; case PROP_HAS_BORDER: g_value_set_boolean (value, priv->has_border); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_rectangle_finalize (GObject *object) { G_OBJECT_CLASS (clutter_rectangle_parent_class)->finalize (object); } static void clutter_rectangle_dispose (GObject *object) { G_OBJECT_CLASS (clutter_rectangle_parent_class)->dispose (object); } static void clutter_rectangle_class_init (ClutterRectangleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; actor_class->paint = clutter_rectangle_paint; actor_class->get_paint_volume = clutter_rectangle_get_paint_volume; actor_class->has_overlaps = clutter_rectangle_has_overlaps; gobject_class->finalize = clutter_rectangle_finalize; gobject_class->dispose = clutter_rectangle_dispose; gobject_class->set_property = clutter_rectangle_set_property; gobject_class->get_property = clutter_rectangle_get_property; /** * ClutterRectangle:color: * * The color of the rectangle. */ pspec = clutter_param_spec_color ("color", P_("Color"), P_("The color of the rectangle"), &default_color, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_COLOR, pspec); /** * ClutterRectangle:border-color: * * The color of the border of the rectangle. * * Since: 0.2 */ pspec = clutter_param_spec_color ("border-color", P_("Border Color"), P_("The color of the border of the rectangle"), &default_border_color, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_BORDER_COLOR, pspec); /** * ClutterRectangle:border-width: * * The width of the border of the rectangle, in pixels. * * Since: 0.2 */ g_object_class_install_property (gobject_class, PROP_BORDER_WIDTH, g_param_spec_uint ("border-width", P_("Border Width"), P_("The width of the border of the rectangle"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE)); /** * ClutterRectangle:has-border: * * Whether the #ClutterRectangle should be displayed with a border. * * Since: 0.2 */ g_object_class_install_property (gobject_class, PROP_HAS_BORDER, g_param_spec_boolean ("has-border", P_("Has Border"), P_("Whether the rectangle should have a border"), FALSE, CLUTTER_PARAM_READWRITE)); } static void clutter_rectangle_init (ClutterRectangle *self) { ClutterRectanglePrivate *priv; self->priv = priv = clutter_rectangle_get_instance_private (self); priv->color = default_color; priv->border_color = default_border_color; priv->border_width = 0; priv->has_border = FALSE; } /** * clutter_rectangle_new: * * Creates a new #ClutterActor with a rectangular shape. * * Return value: a new #ClutterRectangle * * Deprecated: 1.10: Use clutter_actor_new() instead */ ClutterActor* clutter_rectangle_new (void) { return g_object_new (CLUTTER_TYPE_RECTANGLE, NULL); } /** * clutter_rectangle_new_with_color: * @color: a #ClutterColor * * Creates a new #ClutterActor with a rectangular shape * and of the given @color. * * Return value: a new #ClutterRectangle * * Deprecated: 1.10: Use clutter_actor_new() and * clutter_actor_set_background_color() instead */ ClutterActor * clutter_rectangle_new_with_color (const ClutterColor *color) { return g_object_new (CLUTTER_TYPE_RECTANGLE, "color", color, NULL); } /** * clutter_rectangle_get_color: * @rectangle: a #ClutterRectangle * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the color of @rectangle. * * Deprecated: 1.10: Use #ClutterActor and clutter_actor_get_background_color() * instead */ void clutter_rectangle_get_color (ClutterRectangle *rectangle, ClutterColor *color) { ClutterRectanglePrivate *priv; g_return_if_fail (CLUTTER_IS_RECTANGLE (rectangle)); g_return_if_fail (color != NULL); priv = rectangle->priv; color->red = priv->color.red; color->green = priv->color.green; color->blue = priv->color.blue; color->alpha = priv->color.alpha; } /** * clutter_rectangle_set_color: * @rectangle: a #ClutterRectangle * @color: a #ClutterColor * * Sets the color of @rectangle. * * Deprecated: 1.10: Use #ClutterActor and clutter_actor_set_background_color() * instead */ void clutter_rectangle_set_color (ClutterRectangle *rectangle, const ClutterColor *color) { ClutterRectanglePrivate *priv; g_return_if_fail (CLUTTER_IS_RECTANGLE (rectangle)); g_return_if_fail (color != NULL); g_object_ref (rectangle); priv = rectangle->priv; priv->color.red = color->red; priv->color.green = color->green; priv->color.blue = color->blue; priv->color.alpha = color->alpha; #if 0 /* FIXME - appears to be causing border to always get drawn */ if (clutter_color_equal (&priv->color, &priv->border_color)) priv->has_border = FALSE; else priv->has_border = TRUE; #endif clutter_actor_queue_redraw (CLUTTER_ACTOR (rectangle)); g_object_notify (G_OBJECT (rectangle), "color"); g_object_notify (G_OBJECT (rectangle), "has-border"); g_object_unref (rectangle); } /** * clutter_rectangle_get_border_width: * @rectangle: a #ClutterRectangle * * Gets the width (in pixels) of the border used by @rectangle * * Return value: the border's width * * Since: 0.2 * * Deprecated: 1.10: Use #ClutterActor and a #ClutterCanvas content * to draw the border using Cairo */ guint clutter_rectangle_get_border_width (ClutterRectangle *rectangle) { g_return_val_if_fail (CLUTTER_IS_RECTANGLE (rectangle), 0); return rectangle->priv->border_width; } /** * clutter_rectangle_set_border_width: * @rectangle: a #ClutterRectangle * @width: the width of the border * * Sets the width (in pixel) of the border used by @rectangle. * A @width of 0 will unset the border. * * Since: 0.2 * * Deprecated: 1.10: Use #ClutterActor and a #ClutterCanvas content * to draw the border using Cairo */ void clutter_rectangle_set_border_width (ClutterRectangle *rectangle, guint width) { ClutterRectanglePrivate *priv; g_return_if_fail (CLUTTER_IS_RECTANGLE (rectangle)); priv = rectangle->priv; if (priv->border_width != width) { g_object_ref (rectangle); priv->border_width = width; if (priv->border_width != 0) priv->has_border = TRUE; else priv->has_border = FALSE; clutter_actor_queue_redraw (CLUTTER_ACTOR (rectangle)); g_object_notify (G_OBJECT (rectangle), "border-width"); g_object_notify (G_OBJECT (rectangle), "has-border"); g_object_unref (rectangle); } } /** * clutter_rectangle_get_border_color: * @rectangle: a #ClutterRectangle * @color: (out caller-allocates): return location for a #ClutterColor * * Gets the color of the border used by @rectangle and places * it into @color. * * Since: 0.2 * * Deprecated: 1.10: Use #ClutterActor and a #ClutterCanvas to draw * the border with Cairo */ void clutter_rectangle_get_border_color (ClutterRectangle *rectangle, ClutterColor *color) { ClutterRectanglePrivate *priv; g_return_if_fail (CLUTTER_IS_RECTANGLE (rectangle)); g_return_if_fail (color != NULL); priv = rectangle->priv; color->red = priv->border_color.red; color->green = priv->border_color.green; color->blue = priv->border_color.blue; color->alpha = priv->border_color.alpha; } /** * clutter_rectangle_set_border_color: * @rectangle: a #ClutterRectangle * @color: the color of the border * * Sets the color of the border used by @rectangle using @color * * Deprecated: 1.10: Use #ClutterActor and a #ClutterCanvas to draw * the border with Cairo */ void clutter_rectangle_set_border_color (ClutterRectangle *rectangle, const ClutterColor *color) { ClutterRectanglePrivate *priv; g_return_if_fail (CLUTTER_IS_RECTANGLE (rectangle)); g_return_if_fail (color != NULL); priv = rectangle->priv; if (priv->border_color.red != color->red || priv->border_color.green != color->green || priv->border_color.blue != color->blue || priv->border_color.alpha != color->alpha) { g_object_ref (rectangle); priv->border_color.red = color->red; priv->border_color.green = color->green; priv->border_color.blue = color->blue; priv->border_color.alpha = color->alpha; if (clutter_color_equal (&priv->color, &priv->border_color)) priv->has_border = FALSE; else priv->has_border = TRUE; clutter_actor_queue_redraw (CLUTTER_ACTOR (rectangle)); g_object_notify (G_OBJECT (rectangle), "border-color"); g_object_notify (G_OBJECT (rectangle), "has-border"); g_object_unref (rectangle); } } muffin-6.4.1/clutter/clutter/deprecated/clutter-group.c0000664000175000017500000004024714723361714022175 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-group * @short_description: A fixed layout container * * A #ClutterGroup is an Actor which contains multiple child actors positioned * relative to the #ClutterGroup position. Other operations such as scaling, * rotating and clipping of the group will apply to the child actors. * * A #ClutterGroup's size is defined by the size and position of its children; * it will be the smallest non-negative size that covers the right and bottom * edges of all of its children. * * Setting the size on a Group using #ClutterActor methods like * clutter_actor_set_size() will override the natural size of the Group, * however this will not affect the size of the children and they may still * be painted outside of the allocation of the group. One way to constrain * the visible area of a #ClutterGroup to a specified allocation is to * explicitly set the size of the #ClutterGroup and then use the * #ClutterActor:clip-to-allocation property. * * #ClutterGroup as a concrete class has been superceded by #ClutterActor * since Clutter 1.10. The type itself is not deprecated as it is used by * #ClutterStage. You should instantiate #ClutterActor and use its API to * manage child actors. */ #include "clutter-build-config.h" #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-group.h" #include "clutter-actor.h" #include "clutter-actor-private.h" #include "clutter-container.h" #include "clutter-fixed-layout.h" #include "clutter-main.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "cogl/cogl.h" struct _ClutterGroupPrivate { GList *children; ClutterLayoutManager *layout; }; static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterGroup, clutter_group, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (ClutterGroup) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)); static gint sort_by_depth (gconstpointer a, gconstpointer b) { gfloat depth_a = clutter_actor_get_depth (CLUTTER_ACTOR(a)); gfloat depth_b = clutter_actor_get_depth (CLUTTER_ACTOR(b)); if (depth_a < depth_b) return -1; if (depth_a > depth_b) return 1; return 0; } static void clutter_group_real_add (ClutterContainer *container, ClutterActor *actor) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; g_object_ref (actor); priv->children = g_list_append (priv->children, actor); clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-added", actor); clutter_container_sort_depth_order (container); g_object_unref (actor); } static void clutter_group_real_actor_added (ClutterContainer *container, ClutterActor *actor) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; /* XXX - children added using clutter_actor_add_child() will * cause actor-added to be emitted without going through the * add() virtual function. * * if we get an actor-added for a child that is not in our * list of children already, then we go in compatibility * mode. */ if (g_list_find (priv->children, actor) != NULL) return; priv->children = g_list_append (priv->children, actor); clutter_container_sort_depth_order (container); } static void clutter_group_real_remove (ClutterContainer *container, ClutterActor *actor) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; g_object_ref (actor); priv->children = g_list_remove (priv->children, actor); clutter_actor_unparent (actor); clutter_actor_queue_relayout (CLUTTER_ACTOR (container)); g_signal_emit_by_name (container, "actor-removed", actor); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); g_object_unref (actor); } static void clutter_group_real_actor_removed (ClutterContainer *container, ClutterActor *actor) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; /* XXX - same compatibility mode of the ::actor-added implementation */ if (g_list_find (priv->children, actor) == NULL) return; priv->children = g_list_remove (priv->children, actor); } static void clutter_group_real_raise (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; priv->children = g_list_remove (priv->children, actor); /* Raise at the top */ if (!sibling) { GList *last_item; last_item = g_list_last (priv->children); if (last_item) sibling = last_item->data; priv->children = g_list_append (priv->children, actor); } else { gint index_ = g_list_index (priv->children, sibling) + 1; priv->children = g_list_insert (priv->children, actor, index_); } /* set Z ordering a value below, this will then call sort * as values are equal ordering shouldn't change but Z * values will be correct. * * FIXME: get rid of this crap; this is so utterly broken and wrong on * so many levels it's not even funny. sadly, we get to keep this until * we can break API and remove Group for good. */ if (sibling && clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) { clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); } clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void clutter_group_real_lower (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { ClutterGroup *self = CLUTTER_GROUP (container); ClutterGroupPrivate *priv = self->priv; priv->children = g_list_remove (priv->children, actor); /* Push to bottom */ if (!sibling) { GList *last_item; last_item = g_list_first (priv->children); if (last_item) sibling = last_item->data; priv->children = g_list_prepend (priv->children, actor); } else { gint index_ = g_list_index (priv->children, sibling); priv->children = g_list_insert (priv->children, actor, index_); } /* See comment in group_raise for this */ if (sibling && clutter_actor_get_depth (sibling) != clutter_actor_get_depth (actor)) { clutter_actor_set_depth (actor, clutter_actor_get_depth (sibling)); } clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void clutter_group_real_sort_depth_order (ClutterContainer *container) { ClutterGroupPrivate *priv = CLUTTER_GROUP (container)->priv; priv->children = g_list_sort (priv->children, sort_by_depth); clutter_actor_queue_redraw (CLUTTER_ACTOR (container)); } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = clutter_group_real_add; iface->actor_added = clutter_group_real_actor_added; iface->remove = clutter_group_real_remove; iface->actor_removed = clutter_group_real_actor_removed; iface->raise = clutter_group_real_raise; iface->lower = clutter_group_real_lower; iface->sort_depth_order = clutter_group_real_sort_depth_order; } static void clutter_group_real_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; CLUTTER_NOTE (PAINT, "ClutterGroup paint enter '%s'", _clutter_actor_get_debug_name (actor)); g_list_foreach (priv->children, (GFunc) clutter_actor_paint, paint_context); CLUTTER_NOTE (PAINT, "ClutterGroup paint leave '%s'", _clutter_actor_get_debug_name (actor)); } static void clutter_group_real_pick (ClutterActor *actor, ClutterPickContext *pick_context) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; /* Chain up so we get a bounding box pained (if we are reactive) */ CLUTTER_ACTOR_CLASS (clutter_group_parent_class)->pick (actor, pick_context); g_list_foreach (priv->children, (GFunc) clutter_actor_pick, pick_context); } static void clutter_group_real_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *natural_width) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; clutter_layout_manager_get_preferred_width (priv->layout, CLUTTER_CONTAINER (actor), for_height, min_width, natural_width); } static void clutter_group_real_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *natural_height) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; clutter_layout_manager_get_preferred_height (priv->layout, CLUTTER_CONTAINER (actor), for_width, min_height, natural_height); } static void clutter_group_real_allocate (ClutterActor *actor, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; ClutterActorClass *klass; klass = CLUTTER_ACTOR_CLASS (clutter_group_parent_class); klass->allocate (actor, allocation, flags); if (priv->children == NULL) return; clutter_layout_manager_allocate (priv->layout, CLUTTER_CONTAINER (actor), allocation, flags); } static void clutter_group_dispose (GObject *object) { ClutterGroup *self = CLUTTER_GROUP (object); ClutterGroupPrivate *priv = self->priv; /* Note: we are careful to consider that destroying children could * have the side-effect of destroying other children so * priv->children may be modified during clutter_actor_destroy. */ while (priv->children != NULL) { ClutterActor *child = priv->children->data; priv->children = g_list_delete_link (priv->children, priv->children); clutter_actor_destroy (child); } if (priv->layout) { clutter_layout_manager_set_container (priv->layout, NULL); g_object_unref (priv->layout); priv->layout = NULL; } G_OBJECT_CLASS (clutter_group_parent_class)->dispose (object); } static void clutter_group_real_show_all (ClutterActor *self) { ClutterActorIter iter; ClutterActor *actor; clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &actor)) clutter_actor_show (actor); clutter_actor_show (self); } static void clutter_group_real_hide_all (ClutterActor *actor) { ClutterActorIter iter; clutter_actor_hide (actor); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &actor)) clutter_actor_hide (actor); } static gboolean clutter_group_real_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { ClutterGroupPrivate *priv = CLUTTER_GROUP (actor)->priv; GList *l; if (priv->children == NULL) return TRUE; for (l = priv->children; l != NULL; l = l->next) { ClutterActor *child = l->data; const ClutterPaintVolume *child_volume; /* This gets the paint volume of the child transformed into the * group's coordinate space... */ child_volume = clutter_actor_get_transformed_paint_volume (child, actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } return TRUE; } static void clutter_group_class_init (ClutterGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_width = clutter_group_real_get_preferred_width; actor_class->get_preferred_height = clutter_group_real_get_preferred_height; actor_class->allocate = clutter_group_real_allocate; actor_class->paint = clutter_group_real_paint; actor_class->pick = clutter_group_real_pick; actor_class->show_all = clutter_group_real_show_all; actor_class->hide_all = clutter_group_real_hide_all; actor_class->get_paint_volume = clutter_group_real_get_paint_volume; gobject_class->dispose = clutter_group_dispose; } static void clutter_group_init (ClutterGroup *self) { ClutterActor *actor = CLUTTER_ACTOR (self); self->priv = clutter_group_get_instance_private (self); /* turn on some optimization * * XXX - these so-called "optimizations" are insane and should have never * been used. they introduce some weird behaviour that breaks invariants * and have to be explicitly worked around. * * this flag was set by the ClutterFixedLayout, but since that layout * manager is now the default for ClutterActor, we set the flag explicitly * here, to avoid breaking perfectly working actors overriding the * allocate() virtual function. * * also, we keep this flag here so that it can die once we get rid of * ClutterGroup. */ clutter_actor_set_flags (actor, CLUTTER_ACTOR_NO_LAYOUT); self->priv->layout = clutter_fixed_layout_new (); g_object_ref_sink (self->priv->layout); clutter_actor_set_layout_manager (actor, self->priv->layout); } /** * clutter_group_new: * * Create a new #ClutterGroup. * * Return value: the newly created #ClutterGroup actor * * Deprecated: 1.10: Use clutter_actor_new() instead. */ ClutterActor * clutter_group_new (void) { return g_object_new (CLUTTER_TYPE_GROUP, NULL); } /** * clutter_group_remove_all: * @self: A #ClutterGroup * * Removes all children actors from the #ClutterGroup. * * Deprecated: 1.10: Use clutter_actor_remove_all_children() instead. */ void clutter_group_remove_all (ClutterGroup *self) { g_return_if_fail (CLUTTER_IS_GROUP (self)); clutter_actor_remove_all_children (CLUTTER_ACTOR (self)); } /** * clutter_group_get_n_children: * @self: A #ClutterGroup * * Gets the number of actors held in the group. * * Return value: The number of child actors held in the group. * * Since: 0.2 * * Deprecated: 1.10: Use clutter_actor_get_n_children() instead. */ gint clutter_group_get_n_children (ClutterGroup *self) { g_return_val_if_fail (CLUTTER_IS_GROUP (self), 0); return clutter_actor_get_n_children (CLUTTER_ACTOR (self)); } /** * clutter_group_get_nth_child: * @self: A #ClutterGroup * @index_: the position of the requested actor. * * Gets a groups child held at @index_ in stack. * * Return value: (transfer none): A Clutter actor, or %NULL if * @index_ is invalid. * * Since: 0.2 * * Deprecated: 1.10: Use clutter_actor_get_child_at_index() instead. */ ClutterActor * clutter_group_get_nth_child (ClutterGroup *self, gint index_) { ClutterActor *actor; g_return_val_if_fail (CLUTTER_IS_GROUP (self), NULL); actor = CLUTTER_ACTOR (self); g_return_val_if_fail (index_ <= clutter_actor_get_n_children (actor), NULL); return clutter_actor_get_child_at_index (actor, index_); } muffin-6.4.1/clutter/clutter/deprecated/clutter-rectangle.h0000664000175000017500000000775414723361714023020 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_RECTANGLE_H__ #define __CLUTTER_RECTANGLE_H__ #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_RECTANGLE (clutter_rectangle_get_type()) #define CLUTTER_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_RECTANGLE, ClutterRectangle)) #define CLUTTER_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_RECTANGLE, ClutterRectangleClass)) #define CLUTTER_IS_RECTANGLE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_RECTANGLE)) #define CLUTTER_IS_RECTANGLE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_RECTANGLE)) #define CLUTTER_RECTANGLE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_RECTANGLE, ClutterRectangleClass)) typedef struct _ClutterRectangle ClutterRectangle; typedef struct _ClutterRectangleClass ClutterRectangleClass; typedef struct _ClutterRectanglePrivate ClutterRectanglePrivate; /** * ClutterRectangle: * * The #ClutterRectangle structure contains only private data * and should be accessed using the provided API * * Since: 0.2 */ struct _ClutterRectangle { /*< private >*/ ClutterActor parent; ClutterRectanglePrivate *priv; }; /** * ClutterRectangleClass: * * The #ClutterRectangleClass structure contains only private data * * Since: 0.2 */ struct _ClutterRectangleClass { /*< private >*/ ClutterActorClass parent_class; /* padding for future expansion */ void (*_clutter_rectangle1) (void); void (*_clutter_rectangle2) (void); void (*_clutter_rectangle3) (void); void (*_clutter_rectangle4) (void); }; CLUTTER_DEPRECATED GType clutter_rectangle_get_type (void) G_GNUC_CONST; CLUTTER_DEPRECATED_FOR(clutter_actor_new) ClutterActor *clutter_rectangle_new (void); CLUTTER_DEPRECATED_FOR(clutter_actor_new) ClutterActor *clutter_rectangle_new_with_color (const ClutterColor *color); CLUTTER_DEPRECATED_FOR(clutter_actor_get_background_color) void clutter_rectangle_get_color (ClutterRectangle *rectangle, ClutterColor *color); CLUTTER_DEPRECATED_FOR(clutter_actor_set_background_color) void clutter_rectangle_set_color (ClutterRectangle *rectangle, const ClutterColor *color); CLUTTER_DEPRECATED guint clutter_rectangle_get_border_width (ClutterRectangle *rectangle); CLUTTER_DEPRECATED void clutter_rectangle_set_border_width (ClutterRectangle *rectangle, guint width); CLUTTER_DEPRECATED void clutter_rectangle_get_border_color (ClutterRectangle *rectangle, ClutterColor *color); CLUTTER_DEPRECATED void clutter_rectangle_set_border_color (ClutterRectangle *rectangle, const ClutterColor *color); G_END_DECLS #endif /* __CLUTTER_RECTANGLE_H__ */ muffin-6.4.1/clutter/clutter/deprecated/clutter-timeline.h0000664000175000017500000000303614723361714022647 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_TIMELINE_PRIVATE_H__ #define __CLUTTER_TIMELINE_PRIVATE_H__ #include G_BEGIN_DECLS CLUTTER_DEPRECATED_FOR(clutter_timeline_new) ClutterTimeline * clutter_timeline_clone (ClutterTimeline *timeline); CLUTTER_DEPRECATED_FOR(clutter_timeline_set_repeat_count) void clutter_timeline_set_loop (ClutterTimeline *timeline, gboolean loop); CLUTTER_DEPRECATED_FOR(clutter_timeline_get_repeat_count) gboolean clutter_timeline_get_loop (ClutterTimeline *timeline); G_END_DECLS #endif /* __CLUTTER_TIMELINE_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-interval.c0000664000175000017500000010030414723361714020554 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-interval * @short_description: An object holding an interval of two values * * #ClutterInterval is a simple object that can hold two values * defining an interval. #ClutterInterval can hold any value that * can be enclosed inside a #GValue. * * Once a #ClutterInterval for a specific #GType has been instantiated * the #ClutterInterval:value-type property cannot be changed anymore. * * #ClutterInterval starts with a floating reference; this means that * any object taking a reference on a #ClutterInterval instance should * also take ownership of the interval by using g_object_ref_sink(). * * #ClutterInterval is used by #ClutterAnimation to define the * interval of values that an implicit animation should tween over. * * #ClutterInterval can be subclassed to override the validation * and value computation. * * #ClutterInterval is available since Clutter 1.0 */ #include "clutter-build-config.h" #include #include #include #include #include #include "clutter-color.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-units.h" #include "clutter-scriptable.h" #include "clutter-script-private.h" enum { PROP_0, PROP_VALUE_TYPE, PROP_INITIAL, PROP_FINAL, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { INITIAL = 0, FINAL, RESULT, N_VALUES }; struct _ClutterIntervalPrivate { GType value_type; GValue *values; }; static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterInterval, clutter_interval, G_TYPE_INITIALLY_UNOWNED, G_ADD_PRIVATE (ClutterInterval) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init)); static gboolean clutter_interval_real_validate (ClutterInterval *interval, GParamSpec *pspec) { GType pspec_gtype = G_PARAM_SPEC_VALUE_TYPE (pspec); /* then check the fundamental types */ switch (G_TYPE_FUNDAMENTAL (pspec_gtype)) { case G_TYPE_INT: { GParamSpecInt *pspec_int = G_PARAM_SPEC_INT (pspec); gint a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_int->minimum && a <= pspec_int->maximum) && (b >= pspec_int->minimum && b <= pspec_int->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_INT64: { GParamSpecInt64 *pspec_int = G_PARAM_SPEC_INT64 (pspec); gint64 a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_int->minimum && a <= pspec_int->maximum) && (b >= pspec_int->minimum && b <= pspec_int->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_UINT: { GParamSpecUInt *pspec_uint = G_PARAM_SPEC_UINT (pspec); guint a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_uint->minimum && a <= pspec_uint->maximum) && (b >= pspec_uint->minimum && b <= pspec_uint->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_UINT64: { GParamSpecUInt64 *pspec_int = G_PARAM_SPEC_UINT64 (pspec); guint64 a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_int->minimum && a <= pspec_int->maximum) && (b >= pspec_int->minimum && b <= pspec_int->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_CHAR: { GParamSpecChar *pspec_char = G_PARAM_SPEC_CHAR (pspec); guchar a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_char->minimum && a <= pspec_char->maximum) && (b >= pspec_char->minimum && b <= pspec_char->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_UCHAR: { GParamSpecUChar *pspec_uchar = G_PARAM_SPEC_UCHAR (pspec); guchar a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_uchar->minimum && a <= pspec_uchar->maximum) && (b >= pspec_uchar->minimum && b <= pspec_uchar->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_FLOAT: { GParamSpecFloat *pspec_flt = G_PARAM_SPEC_FLOAT (pspec); float a, b; a = b = 0.f; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_flt->minimum && a <= pspec_flt->maximum) && (b >= pspec_flt->minimum && b <= pspec_flt->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_DOUBLE: { GParamSpecDouble *pspec_flt = G_PARAM_SPEC_DOUBLE (pspec); double a, b; a = b = 0; clutter_interval_get_interval (interval, &a, &b); if ((a >= pspec_flt->minimum && a <= pspec_flt->maximum) && (b >= pspec_flt->minimum && b <= pspec_flt->maximum)) return TRUE; else return FALSE; } break; case G_TYPE_BOOLEAN: return TRUE; default: break; } return TRUE; } static gboolean clutter_interval_real_compute_value (ClutterInterval *interval, gdouble factor, GValue *value) { GValue *initial, *final; GType value_type; gboolean retval = FALSE; initial = clutter_interval_peek_initial_value (interval); final = clutter_interval_peek_final_value (interval); value_type = clutter_interval_get_value_type (interval); if (_clutter_has_progress_function (value_type)) { retval = _clutter_run_progress_function (value_type, initial, final, factor, value); if (retval) return TRUE; } switch (G_TYPE_FUNDAMENTAL (value_type)) { case G_TYPE_INT: { gint ia, ib, res; ia = g_value_get_int (initial); ib = g_value_get_int (final); res = (factor * (ib - ia)) + ia; g_value_set_int (value, res); retval = TRUE; } break; case G_TYPE_CHAR: { gchar ia, ib, res; ia = g_value_get_schar (initial); ib = g_value_get_schar (final); res = (factor * (ib - (gdouble) ia)) + ia; g_value_set_schar (value, res); retval = TRUE; } break; case G_TYPE_UINT: { guint ia, ib, res; ia = g_value_get_uint (initial); ib = g_value_get_uint (final); res = (factor * (ib - (gdouble) ia)) + ia; g_value_set_uint (value, res); retval = TRUE; } break; case G_TYPE_UCHAR: { guchar ia, ib, res; ia = g_value_get_uchar (initial); ib = g_value_get_uchar (final); res = (factor * (ib - (gdouble) ia)) + ia; g_value_set_uchar (value, res); retval = TRUE; } break; case G_TYPE_FLOAT: case G_TYPE_DOUBLE: { gdouble ia, ib, res; if (value_type == G_TYPE_DOUBLE) { ia = g_value_get_double (initial); ib = g_value_get_double (final); } else { ia = g_value_get_float (initial); ib = g_value_get_float (final); } res = (factor * (ib - ia)) + ia; if (value_type == G_TYPE_DOUBLE) g_value_set_double (value, res); else g_value_set_float (value, res); retval = TRUE; } break; case G_TYPE_BOOLEAN: if (factor > 0.5) g_value_set_boolean (value, TRUE); else g_value_set_boolean (value, FALSE); retval = TRUE; break; case G_TYPE_BOXED: break; default: break; } /* We're trying to animate a property without knowing how to do that. Issue * a warning with a hint to what could be done to fix that */ if (G_UNLIKELY (retval == FALSE)) { g_warning ("%s: Could not compute progress between two %s. You can " "register a progress function to instruct ClutterInterval " "how to deal with this GType", G_STRLOC, g_type_name (value_type)); } return retval; } static void clutter_interval_finalize (GObject *gobject) { ClutterIntervalPrivate *priv = CLUTTER_INTERVAL (gobject)->priv; if (G_IS_VALUE (&priv->values[INITIAL])) g_value_unset (&priv->values[INITIAL]); if (G_IS_VALUE (&priv->values[FINAL])) g_value_unset (&priv->values[FINAL]); if (G_IS_VALUE (&priv->values[RESULT])) g_value_unset (&priv->values[RESULT]); g_free (priv->values); G_OBJECT_CLASS (clutter_interval_parent_class)->finalize (gobject); } static void clutter_interval_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterInterval *self = CLUTTER_INTERVAL (gobject); ClutterIntervalPrivate *priv = clutter_interval_get_instance_private (self); switch (prop_id) { case PROP_VALUE_TYPE: priv->value_type = g_value_get_gtype (value); break; case PROP_INITIAL: if (g_value_get_boxed (value) != NULL) clutter_interval_set_initial_value (self, g_value_get_boxed (value)); else if (G_IS_VALUE (&priv->values[INITIAL])) g_value_unset (&priv->values[INITIAL]); break; case PROP_FINAL: if (g_value_get_boxed (value) != NULL) clutter_interval_set_final_value (self, g_value_get_boxed (value)); else if (G_IS_VALUE (&priv->values[FINAL])) g_value_unset (&priv->values[FINAL]); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_interval_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterIntervalPrivate *priv; priv = clutter_interval_get_instance_private (CLUTTER_INTERVAL (gobject)); switch (prop_id) { case PROP_VALUE_TYPE: g_value_set_gtype (value, priv->value_type); break; case PROP_INITIAL: if (G_IS_VALUE (&priv->values[INITIAL])) g_value_set_boxed (value, &priv->values[INITIAL]); break; case PROP_FINAL: if (G_IS_VALUE (&priv->values[FINAL])) g_value_set_boxed (value, &priv->values[FINAL]); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static gboolean clutter_interval_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { ClutterIntervalPrivate *priv = CLUTTER_INTERVAL (scriptable)->priv; if ((strcmp (name, "initial") == 0) || (strcmp (name, "final") == 0)) { g_value_init (value, priv->value_type); return _clutter_script_parse_node (script, value, name, node, NULL); } return FALSE; } static void clutter_interval_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { ClutterInterval *self = CLUTTER_INTERVAL (scriptable); if (strcmp (name, "initial") == 0) clutter_interval_set_initial_value (self, value); else if (strcmp (name, "final") == 0) clutter_interval_set_final_value (self, value); else g_object_set_property (G_OBJECT (scriptable), name, value); } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_interval_parse_custom_node; iface->set_custom_property = clutter_interval_set_custom_property; } static void clutter_interval_class_init (ClutterIntervalClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->validate = clutter_interval_real_validate; klass->compute_value = clutter_interval_real_compute_value; gobject_class->set_property = clutter_interval_set_property, gobject_class->get_property = clutter_interval_get_property; gobject_class->finalize = clutter_interval_finalize; /** * ClutterInterval:value-type: * * The type of the values in the interval. * * Since: 1.0 */ obj_props[PROP_VALUE_TYPE] = g_param_spec_gtype ("value-type", P_("Value Type"), P_("The type of the values in the interval"), G_TYPE_NONE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); /** * ClutterInterval:initial: * * The initial value of the interval. * * Since: 1.12 */ obj_props[PROP_INITIAL] = g_param_spec_boxed ("initial", P_("Initial Value"), P_("Initial value of the interval"), G_TYPE_VALUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterInterval:final: * * The final value of the interval. * * Since: 1.12 */ obj_props[PROP_FINAL] = g_param_spec_boxed ("final", P_("Final Value"), P_("Final value of the interval"), G_TYPE_VALUE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_interval_init (ClutterInterval *self) { self->priv = clutter_interval_get_instance_private (self); self->priv->value_type = G_TYPE_INVALID; self->priv->values = g_malloc0 (sizeof (GValue) * N_VALUES); } static inline void clutter_interval_set_value_internal (ClutterInterval *interval, gint index_, const GValue *value) { ClutterIntervalPrivate *priv = interval->priv; GType value_type; g_assert (index_ >= INITIAL && index_ <= RESULT); if (G_IS_VALUE (&priv->values[index_])) g_value_unset (&priv->values[index_]); g_value_init (&priv->values[index_], priv->value_type); value_type = G_VALUE_TYPE (value); if (value_type != priv->value_type || !g_type_is_a (value_type, priv->value_type)) { if (g_value_type_compatible (value_type, priv->value_type)) { g_value_copy (value, &priv->values[index_]); return; } if (g_value_type_transformable (value_type, priv->value_type)) { GValue transform = G_VALUE_INIT; g_value_init (&transform, priv->value_type); if (g_value_transform (value, &transform)) g_value_copy (&transform, &priv->values[index_]); else { g_warning ("%s: Unable to convert a value of type '%s' into " "the value type '%s' of the interval.", G_STRLOC, g_type_name (value_type), g_type_name (priv->value_type)); } g_value_unset (&transform); } } else g_value_copy (value, &priv->values[index_]); } static inline void clutter_interval_get_value_internal (ClutterInterval *interval, gint index_, GValue *value) { ClutterIntervalPrivate *priv = interval->priv; g_assert (index_ >= INITIAL && index_ <= RESULT); g_value_copy (&priv->values[index_], value); } static gboolean clutter_interval_set_initial_internal (ClutterInterval *interval, va_list *args) { GType gtype = interval->priv->value_type; GValue value = G_VALUE_INIT; gchar *error; /* initial value */ G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); /* we leak the value here as it might not be in a valid state * given the error and calling g_value_unset() might lead to * undefined behaviour */ g_free (error); return FALSE; } clutter_interval_set_value_internal (interval, INITIAL, &value); g_value_unset (&value); return TRUE; } static gboolean clutter_interval_set_final_internal (ClutterInterval *interval, va_list *args) { GType gtype = interval->priv->value_type; GValue value = G_VALUE_INIT; gchar *error; /* initial value */ G_VALUE_COLLECT_INIT (&value, gtype, *args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); /* we leak the value here as it might not be in a valid state * given the error and calling g_value_unset() might lead to * undefined behaviour */ g_free (error); return FALSE; } clutter_interval_set_value_internal (interval, FINAL, &value); g_value_unset (&value); return TRUE; } static void clutter_interval_get_interval_valist (ClutterInterval *interval, va_list var_args) { GType gtype = interval->priv->value_type; GValue value = G_VALUE_INIT; gchar *error; /* initial value */ g_value_init (&value, gtype); clutter_interval_get_initial_value (interval, &value); G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); return; } g_value_unset (&value); /* final value */ g_value_init (&value, gtype); clutter_interval_get_final_value (interval, &value); G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); return; } g_value_unset (&value); } /** * clutter_interval_new: * @gtype: the type of the values in the interval * @...: the initial value and the final value of the interval * * Creates a new #ClutterInterval holding values of type @gtype. * * This function avoids using a #GValue for the initial and final values * of the interval: * * |[ * interval = clutter_interval_new (G_TYPE_FLOAT, 0.0, 1.0); * interval = clutter_interval_new (G_TYPE_BOOLEAN, FALSE, TRUE); * interval = clutter_interval_new (G_TYPE_INT, 0, 360); * ]| * * Return value: the newly created #ClutterInterval * * Since: 1.0 */ ClutterInterval * clutter_interval_new (GType gtype, ...) { ClutterInterval *retval; va_list args; g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); va_start (args, gtype); if (!clutter_interval_set_initial_internal (retval, &args)) goto out; clutter_interval_set_final_internal (retval, &args); out: va_end (args); return retval; } /** * clutter_interval_new_with_values: * @gtype: the type of the values in the interval * @initial: (allow-none): a #GValue holding the initial value of the interval * @final: (allow-none): a #GValue holding the final value of the interval * * Creates a new #ClutterInterval of type @gtype, between @initial * and @final. * * This function is useful for language bindings. * * Return value: the newly created #ClutterInterval * * Since: 1.0 */ ClutterInterval * clutter_interval_new_with_values (GType gtype, const GValue *initial, const GValue *final) { g_return_val_if_fail (gtype != G_TYPE_INVALID, NULL); g_return_val_if_fail (initial == NULL || G_VALUE_TYPE (initial) == gtype, NULL); g_return_val_if_fail (final == NULL || G_VALUE_TYPE (final) == gtype, NULL); return g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, "initial", initial, "final", final, NULL); } /** * clutter_interval_clone: * @interval: a #ClutterInterval * * Creates a copy of @interval. * * Return value: (transfer full): the newly created #ClutterInterval * * Since: 1.0 */ ClutterInterval * clutter_interval_clone (ClutterInterval *interval) { ClutterInterval *retval; GType gtype; GValue *tmp; g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); g_return_val_if_fail (interval->priv->value_type != G_TYPE_INVALID, NULL); gtype = interval->priv->value_type; retval = g_object_new (CLUTTER_TYPE_INTERVAL, "value-type", gtype, NULL); tmp = clutter_interval_peek_initial_value (interval); clutter_interval_set_initial_value (retval, tmp); tmp = clutter_interval_peek_final_value (interval); clutter_interval_set_final_value (retval, tmp); return retval; } /** * clutter_interval_get_value_type: * @interval: a #ClutterInterval * * Retrieves the #GType of the values inside @interval. * * Return value: the type of the value, or G_TYPE_INVALID * * Since: 1.0 */ GType clutter_interval_get_value_type (ClutterInterval *interval) { g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), G_TYPE_INVALID); return interval->priv->value_type; } /** * clutter_interval_set_initial_value: (rename-to clutter_interval_set_initial) * @interval: a #ClutterInterval * @value: a #GValue * * Sets the initial value of @interval to @value. The value is copied * inside the #ClutterInterval. * * Since: 1.0 */ void clutter_interval_set_initial_value (ClutterInterval *interval, const GValue *value) { g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (value != NULL); clutter_interval_set_value_internal (interval, INITIAL, value); } /** * clutter_interval_set_initial: (skip) * @interval: a #ClutterInterval * @...: the initial value of the interval. * * Variadic arguments version of clutter_interval_set_initial_value(). * * This function is meant as a convenience for the C API. * * Language bindings should use clutter_interval_set_initial_value() * instead. * * Since: 1.10 */ void clutter_interval_set_initial (ClutterInterval *interval, ...) { va_list args; g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); va_start (args, interval); clutter_interval_set_initial_internal (interval, &args); va_end (args); } /** * clutter_interval_get_initial_value: * @interval: a #ClutterInterval * @value: (out caller-allocates): a #GValue * * Retrieves the initial value of @interval and copies * it into @value. * * The passed #GValue must be initialized to the value held by * the #ClutterInterval. * * Since: 1.0 */ void clutter_interval_get_initial_value (ClutterInterval *interval, GValue *value) { g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (value != NULL); clutter_interval_get_value_internal (interval, INITIAL, value); } /** * clutter_interval_peek_initial_value: * @interval: a #ClutterInterval * * Gets the pointer to the initial value of @interval * * Return value: (transfer none): the initial value of the interval. * The value is owned by the #ClutterInterval and it should not be * modified or freed * * Since: 1.0 */ GValue * clutter_interval_peek_initial_value (ClutterInterval *interval) { g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); return interval->priv->values + INITIAL; } /** * clutter_interval_set_final_value: (rename-to clutter_interval_set_final) * @interval: a #ClutterInterval * @value: a #GValue * * Sets the final value of @interval to @value. The value is * copied inside the #ClutterInterval. * * Since: 1.0 */ void clutter_interval_set_final_value (ClutterInterval *interval, const GValue *value) { g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (value != NULL); clutter_interval_set_value_internal (interval, FINAL, value); } /** * clutter_interval_get_final_value: * @interval: a #ClutterInterval * @value: (out caller-allocates): a #GValue * * Retrieves the final value of @interval and copies * it into @value. * * The passed #GValue must be initialized to the value held by * the #ClutterInterval. * * Since: 1.0 */ void clutter_interval_get_final_value (ClutterInterval *interval, GValue *value) { g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (value != NULL); clutter_interval_get_value_internal (interval, FINAL, value); } /** * clutter_interval_set_final: (skip) * @interval: a #ClutterInterval * @...: the final value of the interval * * Variadic arguments version of clutter_interval_set_final_value(). * * This function is meant as a convenience for the C API. * * Language bindings should use clutter_interval_set_final_value() instead. * * Since: 1.10 */ void clutter_interval_set_final (ClutterInterval *interval, ...) { va_list args; g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); va_start (args, interval); clutter_interval_set_final_internal (interval, &args); va_end (args); } /** * clutter_interval_peek_final_value: * @interval: a #ClutterInterval * * Gets the pointer to the final value of @interval * * Return value: (transfer none): the final value of the interval. * The value is owned by the #ClutterInterval and it should not be * modified or freed * * Since: 1.0 */ GValue * clutter_interval_peek_final_value (ClutterInterval *interval) { g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); return interval->priv->values + FINAL; } /** * clutter_interval_set_interval: * @interval: a #ClutterInterval * @...: the initial and final values of the interval * * Variable arguments wrapper for clutter_interval_set_initial_value() * and clutter_interval_set_final_value() that avoids using the * #GValue arguments: * * |[ * clutter_interval_set_interval (interval, 0, 50); * clutter_interval_set_interval (interval, 1.0, 0.0); * clutter_interval_set_interval (interval, FALSE, TRUE); * ]| * * This function is meant for the convenience of the C API; bindings * should reimplement this function using the #GValue-based API. * * Since: 1.0 */ void clutter_interval_set_interval (ClutterInterval *interval, ...) { va_list args; g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID); va_start (args, interval); if (!clutter_interval_set_initial_internal (interval, &args)) goto out; clutter_interval_set_final_internal (interval, &args); out: va_end (args); } /** * clutter_interval_get_interval: * @interval: a #ClutterInterval * @...: return locations for the initial and final values of * the interval * * Variable arguments wrapper for clutter_interval_get_initial_value() * and clutter_interval_get_final_value() that avoids using the * #GValue arguments: * * |[ * gint a = 0, b = 0; * clutter_interval_get_interval (interval, &a, &b); * ]| * * This function is meant for the convenience of the C API; bindings * should reimplement this function using the #GValue-based API. * * Since: 1.0 */ void clutter_interval_get_interval (ClutterInterval *interval, ...) { va_list args; g_return_if_fail (CLUTTER_IS_INTERVAL (interval)); g_return_if_fail (interval->priv->value_type != G_TYPE_INVALID); va_start (args, interval); clutter_interval_get_interval_valist (interval, args); va_end (args); } /** * clutter_interval_validate: * @interval: a #ClutterInterval * @pspec: a #GParamSpec * * Validates the initial and final values of @interval against * a #GParamSpec. * * Return value: %TRUE if the #ClutterInterval is valid, %FALSE otherwise * * Since: 1.0 */ gboolean clutter_interval_validate (ClutterInterval *interval, GParamSpec *pspec) { g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); g_return_val_if_fail (G_IS_PARAM_SPEC (pspec), FALSE); return CLUTTER_INTERVAL_GET_CLASS (interval)->validate (interval, pspec); } /** * clutter_interval_compute_value: * @interval: a #ClutterInterval * @factor: the progress factor, between 0 and 1 * @value: (out caller-allocates): return location for an initialized #GValue * * Computes the value between the @interval boundaries given the * progress @factor and copies it into @value. * * Return value: %TRUE if the operation was successful * * Since: 1.0 */ gboolean clutter_interval_compute_value (ClutterInterval *interval, gdouble factor, GValue *value) { g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); g_return_val_if_fail (value != NULL, FALSE); return CLUTTER_INTERVAL_GET_CLASS (interval)->compute_value (interval, factor, value); } /** * clutter_interval_compute: * @interval: a #ClutterInterval * @factor: the progress factor, between 0 and 1 * * Computes the value between the @interval boundaries given the * progress @factor * * Unlike clutter_interval_compute_value(), this function will * return a const pointer to the computed value * * You should use this function if you immediately pass the computed * value to another function that makes a copy of it, like * g_object_set_property() * * Return value: (transfer none): a pointer to the computed value, * or %NULL if the computation was not successfull * * Since: 1.4 */ const GValue * clutter_interval_compute (ClutterInterval *interval, gdouble factor) { GValue *value; gboolean res; g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), NULL); value = &(interval->priv->values[RESULT]); if (G_VALUE_TYPE (value) == G_TYPE_INVALID) g_value_init (value, interval->priv->value_type); res = CLUTTER_INTERVAL_GET_CLASS (interval)->compute_value (interval, factor, value); if (res) return interval->priv->values + RESULT; return NULL; } /** * clutter_interval_is_valid: * @interval: a #ClutterInterval * * Checks if the @interval has a valid initial and final values. * * Return value: %TRUE if the #ClutterInterval has an initial and * final values, and %FALSE otherwise * * Since: 1.12 */ gboolean clutter_interval_is_valid (ClutterInterval *interval) { ClutterIntervalPrivate *priv; g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); priv = interval->priv; return G_IS_VALUE (&priv->values[INITIAL]) && G_IS_VALUE (&priv->values[FINAL]); } muffin-6.4.1/clutter/clutter/clutter-blur-effect.c0000664000175000017500000002070414723361714021133 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-blur-effect * @short_description: A blur effect * @see_also: #ClutterEffect, #ClutterOffscreenEffect * * #ClutterBlurEffect is a sub-class of #ClutterEffect that allows blurring a * actor and its contents. * * #ClutterBlurEffect is available since Clutter 1.4 */ #define CLUTTER_BLUR_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass)) #define CLUTTER_IS_BLUR_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BLUR_EFFECT)) #define CLUTTER_BLUR_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BLUR_EFFECT, ClutterBlurEffectClass)) #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-blur-effect.h" #include "cogl/cogl.h" #include "clutter-debug.h" #include "clutter-offscreen-effect.h" #include "clutter-private.h" #define BLUR_PADDING 2 /* FIXME - lame shader; we should really have a decoupled * horizontal/vertical two pass shader for the gaussian blur */ static const gchar *box_blur_glsl_declarations = "uniform vec2 pixel_step;\n"; #define SAMPLE(offx, offy) \ "cogl_texel += texture2D (cogl_sampler, cogl_tex_coord.st + pixel_step * " \ "vec2 (" G_STRINGIFY (offx) ", " G_STRINGIFY (offy) "));\n" static const gchar *box_blur_glsl_shader = " cogl_texel = texture2D (cogl_sampler, cogl_tex_coord.st);\n" SAMPLE (-1.0, -1.0) SAMPLE ( 0.0, -1.0) SAMPLE (+1.0, -1.0) SAMPLE (-1.0, 0.0) SAMPLE (+1.0, 0.0) SAMPLE (-1.0, +1.0) SAMPLE ( 0.0, +1.0) SAMPLE (+1.0, +1.0) " cogl_texel /= 9.0;\n"; #undef SAMPLE struct _ClutterBlurEffect { ClutterOffscreenEffect parent_instance; /* a back pointer to our actor, so that we can query it */ ClutterActor *actor; gint pixel_step_uniform; gint tex_width; gint tex_height; CoglPipeline *pipeline; }; struct _ClutterBlurEffectClass { ClutterOffscreenEffectClass parent_class; CoglPipeline *base_pipeline; }; G_DEFINE_TYPE (ClutterBlurEffect, clutter_blur_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); static gboolean clutter_blur_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect); ClutterEffectClass *parent_class; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; self->actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); if (self->actor == NULL) return FALSE; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ShaderEffect: the graphics hardware " "or the current GL driver does not implement support " "for the GLSL shading language."); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); return FALSE; } parent_class = CLUTTER_EFFECT_CLASS (clutter_blur_effect_parent_class); if (parent_class->pre_paint (effect, paint_context)) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect); CoglHandle texture; texture = clutter_offscreen_effect_get_texture (offscreen_effect); self->tex_width = cogl_texture_get_width (texture); self->tex_height = cogl_texture_get_height (texture); if (self->pixel_step_uniform > -1) { gfloat pixel_step[2]; pixel_step[0] = 1.0f / self->tex_width; pixel_step[1] = 1.0f / self->tex_height; cogl_pipeline_set_uniform_float (self->pipeline, self->pixel_step_uniform, 2, /* n_components */ 1, /* count */ pixel_step); } cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); return TRUE; } else return FALSE; } static void clutter_blur_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (effect); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); guint8 paint_opacity; paint_opacity = clutter_actor_get_paint_opacity (self->actor); cogl_pipeline_set_color4ub (self->pipeline, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_framebuffer_draw_rectangle (framebuffer, self->pipeline, 0, 0, self->tex_width, self->tex_height); } static gboolean clutter_blur_effect_modify_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume) { gfloat cur_width, cur_height; graphene_point3d_t origin; clutter_paint_volume_get_origin (volume, &origin); cur_width = clutter_paint_volume_get_width (volume); cur_height = clutter_paint_volume_get_height (volume); origin.x -= BLUR_PADDING; origin.y -= BLUR_PADDING; cur_width += 2 * BLUR_PADDING; cur_height += 2 * BLUR_PADDING; clutter_paint_volume_set_origin (volume, &origin); clutter_paint_volume_set_width (volume, cur_width); clutter_paint_volume_set_height (volume, cur_height); return TRUE; } static void clutter_blur_effect_dispose (GObject *gobject) { ClutterBlurEffect *self = CLUTTER_BLUR_EFFECT (gobject); if (self->pipeline != NULL) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } G_OBJECT_CLASS (clutter_blur_effect_parent_class)->dispose (gobject); } static void clutter_blur_effect_class_init (ClutterBlurEffectClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; gobject_class->dispose = clutter_blur_effect_dispose; effect_class->pre_paint = clutter_blur_effect_pre_paint; effect_class->modify_paint_volume = clutter_blur_effect_modify_paint_volume; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->paint_target = clutter_blur_effect_paint_target; } static void clutter_blur_effect_init (ClutterBlurEffect *self) { ClutterBlurEffectClass *klass = CLUTTER_BLUR_EFFECT_GET_CLASS (self); if (G_UNLIKELY (klass->base_pipeline == NULL)) { CoglSnippet *snippet; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); klass->base_pipeline = cogl_pipeline_new (ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, box_blur_glsl_declarations, NULL); cogl_snippet_set_replace (snippet, box_blur_glsl_shader); cogl_pipeline_add_layer_snippet (klass->base_pipeline, 0, snippet); cogl_object_unref (snippet); cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0); } self->pipeline = cogl_pipeline_copy (klass->base_pipeline); self->pixel_step_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "pixel_step"); } /** * clutter_blur_effect_new: * * Creates a new #ClutterBlurEffect to be used with * clutter_actor_add_effect() * * Return value: the newly created #ClutterBlurEffect or %NULL * * Since: 1.4 */ ClutterEffect * clutter_blur_effect_new (void) { return g_object_new (CLUTTER_TYPE_BLUR_EFFECT, NULL); } muffin-6.4.1/clutter/clutter/clutter-drag-action.c0000664000175000017500000011710014723361714021122 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-drag-action * @Title: ClutterDragAction * @Short_Description: Action enabling dragging on actors * * #ClutterDragAction is a sub-class of #ClutterAction that implements * all the necessary logic for dragging actors. * * The simplest usage of #ClutterDragAction consists in adding it to * a #ClutterActor and setting it as reactive; for instance, the following * code: * * |[ * clutter_actor_add_action (actor, clutter_drag_action_new ()); * clutter_actor_set_reactive (actor, TRUE); * ]| * * will automatically result in the actor moving to follow the pointer * whenever the pointer's button is pressed over the actor and moved * across the stage. * * The #ClutterDragAction will signal the begin and the end of a dragging * through the #ClutterDragAction::drag-begin and #ClutterDragAction::drag-end * signals, respectively. Each pointer motion during a drag will also result * in the #ClutterDragAction::drag-motion signal to be emitted. * * It is also possible to set another #ClutterActor as the dragged actor * by calling clutter_drag_action_set_drag_handle() from within a handle * of the #ClutterDragAction::drag-begin signal. The drag handle must be * parented and exist between the emission of #ClutterDragAction::drag-begin * and #ClutterDragAction::drag-end. * * The [drag-action example](https://git.gnome.org/browse/clutter/tree/examples/drag-action.c?h=clutter-1.18) * allows dragging the rectangle around the stage using a #ClutterDragAction. * When pressing the `Shift` key the actor that is being dragged will be a * separate rectangle, and when the drag ends, the original rectangle will be * animated to the final drop coordinates. * * #ClutterDragAction is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-drag-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-stage-private.h" struct _ClutterDragActionPrivate { ClutterStage *stage; gint x_drag_threshold; gint y_drag_threshold; ClutterActor *drag_handle; ClutterDragAxis drag_axis; graphene_rect_t drag_area; ClutterInputDevice *device; ClutterEventSequence *sequence; gulong button_press_id; gulong touch_begin_id; gulong capture_id; gfloat press_x; gfloat press_y; ClutterModifierType press_state; gfloat last_motion_x; gfloat last_motion_y; ClutterModifierType last_motion_state; ClutterInputDevice *last_motion_device; gfloat transformed_press_x; gfloat transformed_press_y; guint emit_delayed_press : 1; guint in_drag : 1; guint motion_events_enabled : 1; guint drag_area_set : 1; }; enum { PROP_0, PROP_X_DRAG_THRESHOLD, PROP_Y_DRAG_THRESHOLD, PROP_DRAG_HANDLE, PROP_DRAG_AXIS, PROP_DRAG_AREA, PROP_DRAG_AREA_SET, PROP_LAST }; static GParamSpec *drag_props[PROP_LAST] = { NULL, }; enum { DRAG_BEGIN, DRAG_PROGRESS, DRAG_MOTION, DRAG_END, LAST_SIGNAL }; static guint drag_signals[LAST_SIGNAL] = { 0, }; /* forward declaration */ static gboolean on_captured_event (ClutterActor *stage, ClutterEvent *event, ClutterDragAction *action); G_DEFINE_TYPE_WITH_PRIVATE (ClutterDragAction, clutter_drag_action, CLUTTER_TYPE_ACTION) static void get_drag_threshold (ClutterDragAction *action, gint *x_threshold, gint *y_threshold) { ClutterDragActionPrivate *priv = action->priv; ClutterSettings *settings = clutter_settings_get_default (); gint x_res, y_res, default_threshold; g_object_get (settings, "dnd-drag-threshold", &default_threshold, NULL); if (priv->x_drag_threshold < 0) x_res = default_threshold; else x_res = priv->x_drag_threshold; if (priv->y_drag_threshold < 0) y_res = default_threshold; else y_res = priv->y_drag_threshold; if (x_threshold != NULL) *x_threshold = x_res; if (y_threshold != NULL) *y_threshold = y_res; } static void emit_drag_begin (ClutterDragAction *action, ClutterActor *actor, ClutterEvent *event) { ClutterDragActionPrivate *priv = action->priv; if (priv->stage != NULL) { clutter_stage_set_motion_events_enabled (priv->stage, FALSE); if (clutter_event_type (event) == CLUTTER_TOUCH_BEGIN) _clutter_stage_add_touch_drag_actor (priv->stage, clutter_event_get_event_sequence (event), priv->drag_handle != NULL ? priv->drag_handle : actor); else _clutter_stage_add_pointer_drag_actor (priv->stage, clutter_event_get_device (event), priv->drag_handle != NULL ? priv->drag_handle : actor); } g_signal_emit (action, drag_signals[DRAG_BEGIN], 0, actor, priv->press_x, priv->press_y, priv->press_state); } static void emit_drag_motion (ClutterDragAction *action, ClutterActor *actor, ClutterEvent *event) { ClutterDragActionPrivate *priv = action->priv; ClutterActor *drag_handle = NULL; gfloat delta_x, delta_y; gfloat motion_x, motion_y; gboolean can_emit_drag_motion = TRUE; clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y); priv->last_motion_state = clutter_event_get_state (event); priv->last_motion_device = clutter_event_get_device (event); if (priv->drag_handle != NULL && !priv->emit_delayed_press) drag_handle = priv->drag_handle; else drag_handle = actor; motion_x = motion_y = 0.0f; clutter_actor_transform_stage_point (drag_handle, priv->last_motion_x, priv->last_motion_y, &motion_x, &motion_y); delta_x = delta_y = 0.0f; switch (priv->drag_axis) { case CLUTTER_DRAG_AXIS_NONE: delta_x = motion_x - priv->transformed_press_x; delta_y = motion_y - priv->transformed_press_y; break; case CLUTTER_DRAG_X_AXIS: delta_x = motion_x - priv->transformed_press_x; break; case CLUTTER_DRAG_Y_AXIS: delta_y = motion_y - priv->transformed_press_y; break; default: g_assert_not_reached (); return; } if (priv->emit_delayed_press) { gint x_drag_threshold, y_drag_threshold; get_drag_threshold (action, &x_drag_threshold, &y_drag_threshold); if (ABS (delta_x) >= x_drag_threshold || ABS (delta_y) >= y_drag_threshold) { priv->emit_delayed_press = FALSE; emit_drag_begin (action, actor, event); } else return; } g_signal_emit (action, drag_signals[DRAG_PROGRESS], 0, actor, delta_x, delta_y, &can_emit_drag_motion); if (can_emit_drag_motion) { g_signal_emit (action, drag_signals[DRAG_MOTION], 0, actor, delta_x, delta_y); } } static void emit_drag_end (ClutterDragAction *action, ClutterActor *actor, ClutterEvent *event) { ClutterDragActionPrivate *priv = action->priv; /* ::drag-end may result in the destruction of the actor, which in turn * will lead to the removal and finalization of the action, so we need * to keep the action alive for the entire emission sequence */ g_object_ref (action); /* if we have an event, update our own state, otherwise we'll * just use the currently stored state when emitting the ::drag-end * signal */ if (event != NULL) { clutter_event_get_coords (event, &priv->last_motion_x, &priv->last_motion_y); priv->last_motion_state = clutter_event_get_state (event); priv->last_motion_device = clutter_event_get_device (event); } priv->in_drag = FALSE; /* we might not have emitted ::drag-begin yet */ if (!priv->emit_delayed_press) g_signal_emit (action, drag_signals[DRAG_END], 0, actor, priv->last_motion_x, priv->last_motion_y, priv->last_motion_state); if (priv->stage == NULL) goto out; /* disconnect the capture */ g_clear_signal_handler (&priv->capture_id, priv->stage); clutter_stage_set_motion_events_enabled (priv->stage, priv->motion_events_enabled); if (priv->last_motion_device != NULL && event != NULL) { if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE) _clutter_stage_remove_pointer_drag_actor (priv->stage, priv->last_motion_device); else _clutter_stage_remove_touch_drag_actor (priv->stage, priv->sequence); } out: priv->last_motion_device = NULL; priv->sequence = NULL; g_object_unref (action); } static gboolean on_captured_event (ClutterActor *stage, ClutterEvent *event, ClutterDragAction *action) { ClutterDragActionPrivate *priv = action->priv; ClutterActor *actor; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); if (!priv->in_drag) return CLUTTER_EVENT_PROPAGATE; if (clutter_event_get_device (event) != priv->device || clutter_event_get_event_sequence (event) != priv->sequence) return CLUTTER_EVENT_PROPAGATE; switch (clutter_event_type (event)) { case CLUTTER_TOUCH_UPDATE: emit_drag_motion (action, actor, event); break; case CLUTTER_MOTION: { ClutterModifierType mods = clutter_event_get_state (event); /* we might miss a button-release event in case of grabs, * so we need to check whether the button is still down * during a motion event */ if (mods & CLUTTER_BUTTON1_MASK) emit_drag_motion (action, actor, event); else emit_drag_end (action, actor, event); } break; case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: emit_drag_end (action, actor, event); break; case CLUTTER_BUTTON_RELEASE: if (priv->in_drag) emit_drag_end (action, actor, event); break; case CLUTTER_ENTER: case CLUTTER_LEAVE: if (priv->in_drag) return CLUTTER_EVENT_STOP; break; default: break; } return CLUTTER_EVENT_PROPAGATE; } static gboolean on_drag_begin (ClutterActor *actor, ClutterEvent *event, ClutterDragAction *action) { ClutterDragActionPrivate *priv = action->priv; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) return CLUTTER_EVENT_PROPAGATE; /* dragging is only performed using the primary button */ switch (clutter_event_type (event)) { case CLUTTER_BUTTON_PRESS: if (priv->sequence != NULL) return CLUTTER_EVENT_PROPAGATE; if (clutter_event_get_button (event) != CLUTTER_BUTTON_PRIMARY) return CLUTTER_EVENT_PROPAGATE; break; case CLUTTER_TOUCH_BEGIN: if (priv->sequence != NULL) return CLUTTER_EVENT_PROPAGATE; priv->sequence = clutter_event_get_event_sequence (event); break; default: return CLUTTER_EVENT_PROPAGATE; } if (priv->stage == NULL) priv->stage = CLUTTER_STAGE (clutter_actor_get_stage (actor)); clutter_event_get_coords (event, &priv->press_x, &priv->press_y); priv->press_state = clutter_event_get_state (event); priv->device = clutter_event_get_device (event); priv->last_motion_x = priv->press_x; priv->last_motion_y = priv->press_y; priv->transformed_press_x = priv->press_x; priv->transformed_press_y = priv->press_y; clutter_actor_transform_stage_point (actor, priv->press_x, priv->press_y, &priv->transformed_press_x, &priv->transformed_press_y); priv->motion_events_enabled = clutter_stage_get_motion_events_enabled (priv->stage); if (priv->x_drag_threshold == 0 || priv->y_drag_threshold == 0) emit_drag_begin (action, actor, event); else priv->emit_delayed_press = TRUE; priv->in_drag = TRUE; priv->capture_id = g_signal_connect_after (priv->stage, "captured-event", G_CALLBACK (on_captured_event), action); return CLUTTER_EVENT_PROPAGATE; } static void clutter_drag_action_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (meta)->priv; if (priv->button_press_id != 0) { ClutterActor *old_actor; old_actor = clutter_actor_meta_get_actor (meta); if (old_actor != NULL) { g_clear_signal_handler (&priv->button_press_id, old_actor); g_clear_signal_handler (&priv->touch_begin_id, old_actor); } priv->button_press_id = 0; priv->touch_begin_id = 0; } if (priv->capture_id != 0) { if (priv->stage != NULL) g_clear_signal_handler (&priv->capture_id, priv->stage); priv->capture_id = 0; priv->stage = NULL; } clutter_drag_action_set_drag_handle (CLUTTER_DRAG_ACTION (meta), NULL); priv->in_drag = FALSE; if (actor != NULL) { priv->button_press_id = g_signal_connect (actor, "button-press-event", G_CALLBACK (on_drag_begin), meta); priv->touch_begin_id = g_signal_connect (actor, "touch-event", G_CALLBACK (on_drag_begin), meta); } CLUTTER_ACTOR_META_CLASS (clutter_drag_action_parent_class)->set_actor (meta, actor); } static gboolean clutter_drag_action_real_drag_progress (ClutterDragAction *action, ClutterActor *actor, gfloat delta_x, gfloat delta_y) { return TRUE; } static void clutter_drag_action_real_drag_motion (ClutterDragAction *action, ClutterActor *actor, gfloat delta_x, gfloat delta_y) { ClutterActor *drag_handle; gfloat x, y; if (action->priv->drag_handle != NULL) drag_handle = action->priv->drag_handle; else drag_handle = actor; clutter_actor_get_position (drag_handle, &x, &y); x += delta_x; y += delta_y; if (action->priv->drag_area_set) { graphene_rect_t *drag_area = &action->priv->drag_area; x = CLAMP (x, drag_area->origin.x, drag_area->origin.x + drag_area->size.width); y = CLAMP (y, drag_area->origin.y, drag_area->origin.y + drag_area->size.height); } clutter_actor_set_position (drag_handle, x, y); } static void clutter_drag_action_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterDragAction *action = CLUTTER_DRAG_ACTION (gobject); ClutterDragActionPrivate *priv = action->priv; switch (prop_id) { case PROP_X_DRAG_THRESHOLD: clutter_drag_action_set_drag_threshold (action, g_value_get_int (value), priv->y_drag_threshold); break; case PROP_Y_DRAG_THRESHOLD: clutter_drag_action_set_drag_threshold (action, priv->x_drag_threshold, g_value_get_int (value)); break; case PROP_DRAG_HANDLE: clutter_drag_action_set_drag_handle (action, g_value_get_object (value)); break; case PROP_DRAG_AXIS: clutter_drag_action_set_drag_axis (action, g_value_get_enum (value)); break; case PROP_DRAG_AREA: clutter_drag_action_set_drag_area (action, g_value_get_boxed (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_drag_action_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv; switch (prop_id) { case PROP_X_DRAG_THRESHOLD: { gint threshold; get_drag_threshold (CLUTTER_DRAG_ACTION (gobject), &threshold, NULL); g_value_set_int (value, threshold); } break; case PROP_Y_DRAG_THRESHOLD: { gint threshold; get_drag_threshold (CLUTTER_DRAG_ACTION (gobject), NULL, &threshold); g_value_set_int (value, threshold); } break; case PROP_DRAG_HANDLE: g_value_set_object (value, priv->drag_handle); break; case PROP_DRAG_AXIS: g_value_set_enum (value, priv->drag_axis); break; case PROP_DRAG_AREA: g_value_set_boxed (value, &priv->drag_area); break; case PROP_DRAG_AREA_SET: g_value_set_boolean (value, priv->drag_area_set); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_drag_action_dispose (GObject *gobject) { ClutterDragActionPrivate *priv = CLUTTER_DRAG_ACTION (gobject)->priv; /* if we're being disposed while a capture is still present, we * need to reset the state we are currently holding */ if (priv->last_motion_device != NULL) { _clutter_stage_remove_pointer_drag_actor (priv->stage, priv->last_motion_device); priv->last_motion_device = NULL; } if (priv->sequence != NULL) { _clutter_stage_remove_touch_drag_actor (priv->stage, priv->sequence); priv->sequence = NULL; } if (priv->capture_id != 0) { clutter_stage_set_motion_events_enabled (priv->stage, priv->motion_events_enabled); if (priv->stage != NULL) g_clear_signal_handler (&priv->capture_id, priv->stage); priv->capture_id = 0; priv->stage = NULL; } if (priv->button_press_id != 0) { ClutterActor *actor; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (gobject)); if (actor != NULL) { g_clear_signal_handler (&priv->button_press_id, actor); g_clear_signal_handler (&priv->touch_begin_id, actor); } priv->button_press_id = 0; priv->touch_begin_id = 0; } clutter_drag_action_set_drag_handle (CLUTTER_DRAG_ACTION (gobject), NULL); clutter_drag_action_set_drag_area (CLUTTER_DRAG_ACTION (gobject), NULL); G_OBJECT_CLASS (clutter_drag_action_parent_class)->dispose (gobject); } static void clutter_drag_action_class_init (ClutterDragActionClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); meta_class->set_actor = clutter_drag_action_set_actor; klass->drag_progress = clutter_drag_action_real_drag_progress; klass->drag_motion = clutter_drag_action_real_drag_motion; /** * ClutterDragAction:x-drag-threshold: * * The horizontal threshold, in pixels, that the cursor must travel * in order to begin a drag action. * * When set to a positive value, #ClutterDragAction will only emit * #ClutterDragAction::drag-begin if the pointer has moved * horizontally at least of the given amount of pixels since * the button press event. * * When set to -1, #ClutterDragAction will use the default threshold * stored in the #ClutterSettings:dnd-drag-threshold property of * #ClutterSettings. * * When read, this property will always return a valid drag * threshold, either as set or the default one. * * Since: 1.4 */ drag_props[PROP_X_DRAG_THRESHOLD] = g_param_spec_int ("x-drag-threshold", P_("Horizontal Drag Threshold"), P_("The horizontal amount of pixels required to start dragging"), -1, G_MAXINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterDragAction:y-drag-threshold: * * The vertical threshold, in pixels, that the cursor must travel * in order to begin a drag action. * * When set to a positive value, #ClutterDragAction will only emit * #ClutterDragAction::drag-begin if the pointer has moved * vertically at least of the given amount of pixels since * the button press event. * * When set to -1, #ClutterDragAction will use the value stored * in the #ClutterSettings:dnd-drag-threshold property of * #ClutterSettings. * * When read, this property will always return a valid drag * threshold, either as set or the default one. * * Since: 1.4 */ drag_props[PROP_Y_DRAG_THRESHOLD] = g_param_spec_int ("y-drag-threshold", P_("Vertical Drag Threshold"), P_("The vertical amount of pixels required to start dragging"), -1, G_MAXINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterDragAction:drag-handle: * * The #ClutterActor that is effectively being dragged * * A #ClutterDragAction will, be default, use the #ClutterActor that * has been attached to the action; it is possible to create a * separate #ClutterActor and use it instead. * * Setting this property has no effect on the #ClutterActor argument * passed to the #ClutterDragAction signals * * Since: 1.4 */ drag_props[PROP_DRAG_HANDLE] = g_param_spec_object ("drag-handle", P_("Drag Handle"), P_("The actor that is being dragged"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READWRITE); /** * ClutterDragAction:drag-axis: * * Constraints the dragging action to the specified axis * * Since: 1.4 */ drag_props[PROP_DRAG_AXIS] = g_param_spec_enum ("drag-axis", P_("Drag Axis"), P_("Constraints the dragging to an axis"), CLUTTER_TYPE_DRAG_AXIS, CLUTTER_DRAG_AXIS_NONE, CLUTTER_PARAM_READWRITE); /** * ClutterDragAction:drag-area: * * Constains the dragging action (or in particular, the resulting * actor position) to the specified #ClutterRect, in parent's * coordinates. * * Since: 1.12 */ drag_props[PROP_DRAG_AREA] = g_param_spec_boxed ("drag-area", P_("Drag Area"), P_("Constrains the dragging to a rectangle"), GRAPHENE_TYPE_RECT, CLUTTER_PARAM_READWRITE); /** * ClutterDragAction:drag-area-set: * * Whether the #ClutterDragAction:drag-area property has been set. * * Since: 1.12 */ drag_props[PROP_DRAG_AREA_SET] = g_param_spec_boolean ("drag-area-set", P_("Drag Area Set"), P_("Whether the drag area is set"), FALSE, CLUTTER_PARAM_READABLE); gobject_class->set_property = clutter_drag_action_set_property; gobject_class->get_property = clutter_drag_action_get_property; gobject_class->dispose = clutter_drag_action_dispose; g_object_class_install_properties (gobject_class, PROP_LAST, drag_props); /** * ClutterDragAction::drag-begin: * @action: the #ClutterDragAction that emitted the signal * @actor: the #ClutterActor attached to the action * @event_x: the X coordinate (in stage space) of the press event * @event_y: the Y coordinate (in stage space) of the press event * @modifiers: the modifiers of the press event * * The ::drag-begin signal is emitted when the #ClutterDragAction * starts the dragging * * The emission of this signal can be delayed by using the * #ClutterDragAction:x-drag-threshold and * #ClutterDragAction:y-drag-threshold properties * * Since: 1.4 */ drag_signals[DRAG_BEGIN] = g_signal_new (I_("drag-begin"), CLUTTER_TYPE_DRAG_ACTION, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDragActionClass, drag_begin), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS, G_TYPE_NONE, 4, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT, CLUTTER_TYPE_MODIFIER_TYPE); /** * ClutterDragAction::drag-progress: * @action: the #ClutterDragAction that emitted the signal * @actor: the #ClutterActor attached to the action * @delta_x: the X component of the distance between the press event * that began the dragging and the current position of the pointer, * as of the latest motion event * @delta_y: the Y component of the distance between the press event * that began the dragging and the current position of the pointer, * as of the latest motion event * * The ::drag-progress signal is emitted for each motion event after * the #ClutterDragAction::drag-begin signal has been emitted. * * The components of the distance between the press event and the * latest motion event are computed in the actor's coordinate space, * to take into account eventual transformations. If you want the * stage coordinates of the latest motion event you can use * clutter_drag_action_get_motion_coords(). * * The default handler will emit #ClutterDragAction::drag-motion, * if #ClutterDragAction::drag-progress emission returns %TRUE. * * Return value: %TRUE if the drag should continue, and %FALSE * if it should be stopped. * * Since: 1.12 */ drag_signals[DRAG_PROGRESS] = g_signal_new (I_("drag-progress"), CLUTTER_TYPE_DRAG_ACTION, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDragActionClass, drag_progress), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_FLOAT_FLOAT, G_TYPE_BOOLEAN, 3, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT); /** * ClutterDragAction::drag-motion: * @action: the #ClutterDragAction that emitted the signal * @actor: the #ClutterActor attached to the action * @delta_x: the X component of the distance between the press event * that began the dragging and the current position of the pointer, * as of the latest motion event * @delta_y: the Y component of the distance between the press event * that began the dragging and the current position of the pointer, * as of the latest motion event * * The ::drag-motion signal is emitted for each motion event after * the #ClutterDragAction::drag-begin signal has been emitted. * * The components of the distance between the press event and the * latest motion event are computed in the actor's coordinate space, * to take into account eventual transformations. If you want the * stage coordinates of the latest motion event you can use * clutter_drag_action_get_motion_coords(). * * The default handler of the signal will call clutter_actor_move_by() * either on @actor or, if set, of #ClutterDragAction:drag-handle using * the @delta_x and @delta_y components of the dragging motion. If you * want to override the default behaviour, you can connect to the * #ClutterDragAction::drag-progress signal and return %FALSE from the * handler. * * Since: 1.4 */ drag_signals[DRAG_MOTION] = g_signal_new (I_("drag-motion"), CLUTTER_TYPE_DRAG_ACTION, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDragActionClass, drag_motion), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT, G_TYPE_NONE, 3, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT); /** * ClutterDragAction::drag-end: * @action: the #ClutterDragAction that emitted the signal * @actor: the #ClutterActor attached to the action * @event_x: the X coordinate (in stage space) of the release event * @event_y: the Y coordinate (in stage space) of the release event * @modifiers: the modifiers of the release event * * The ::drag-end signal is emitted at the end of the dragging, * when the pointer button's is released * * This signal is emitted if and only if the #ClutterDragAction::drag-begin * signal has been emitted first * * Since: 1.4 */ drag_signals[DRAG_END] = g_signal_new (I_("drag-end"), CLUTTER_TYPE_DRAG_ACTION, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterDragActionClass, drag_end), NULL, NULL, _clutter_marshal_VOID__OBJECT_FLOAT_FLOAT_FLAGS, G_TYPE_NONE, 4, CLUTTER_TYPE_ACTOR, G_TYPE_FLOAT, G_TYPE_FLOAT, CLUTTER_TYPE_MODIFIER_TYPE); } static void clutter_drag_action_init (ClutterDragAction *self) { self->priv = clutter_drag_action_get_instance_private (self); } /** * clutter_drag_action_new: * * Creates a new #ClutterDragAction instance * * Return value: the newly created #ClutterDragAction * * Since: 1.4 */ ClutterAction * clutter_drag_action_new (void) { return g_object_new (CLUTTER_TYPE_DRAG_ACTION, NULL); } /** * clutter_drag_action_set_drag_threshold: * @action: a #ClutterDragAction * @x_threshold: a distance on the horizontal axis, in pixels, or * -1 to use the default drag threshold from #ClutterSettings * @y_threshold: a distance on the vertical axis, in pixels, or * -1 to use the default drag threshold from #ClutterSettings * * Sets the horizontal and vertical drag thresholds that must be * cleared by the pointer before @action can begin the dragging. * * If @x_threshold or @y_threshold are set to -1 then the default * drag threshold stored in the #ClutterSettings:dnd-drag-threshold * property of #ClutterSettings will be used. * * Since: 1.4 */ void clutter_drag_action_set_drag_threshold (ClutterDragAction *action, gint x_threshold, gint y_threshold) { ClutterDragActionPrivate *priv; GObject *self; g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); self = G_OBJECT (action); priv = action->priv; g_object_freeze_notify (self); if (priv->x_drag_threshold != x_threshold) { priv->x_drag_threshold = x_threshold; g_object_notify_by_pspec (self, drag_props[PROP_X_DRAG_THRESHOLD]); } if (priv->y_drag_threshold != y_threshold) { priv->y_drag_threshold = y_threshold; g_object_notify_by_pspec (self, drag_props[PROP_Y_DRAG_THRESHOLD]); } g_object_thaw_notify (self); } /** * clutter_drag_action_get_drag_threshold: * @action: a #ClutterDragAction * @x_threshold: (out): return location for the horizontal drag * threshold value, in pixels * @y_threshold: (out): return location for the vertical drag * threshold value, in pixels * * Retrieves the values set by clutter_drag_action_set_drag_threshold(). * * If the #ClutterDragAction:x-drag-threshold property or the * #ClutterDragAction:y-drag-threshold property have been set to -1 then * this function will return the default drag threshold value as stored * by the #ClutterSettings:dnd-drag-threshold property of #ClutterSettings. * * Since: 1.4 */ void clutter_drag_action_get_drag_threshold (ClutterDragAction *action, guint *x_threshold, guint *y_threshold) { gint x_res, y_res; g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); get_drag_threshold (action, &x_res, &y_res); if (x_threshold != NULL) *x_threshold = x_res; if (y_threshold != NULL) *y_threshold = y_res; } static void on_drag_handle_destroy (ClutterActor *handle, ClutterDragAction *action) { ClutterDragActionPrivate *priv = action->priv; ClutterActor *actor; /* make sure we reset the state */ actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); if (priv->in_drag) emit_drag_end (action, actor, NULL); priv->drag_handle = NULL; } /** * clutter_drag_action_set_drag_handle: * @action: a #ClutterDragAction * @handle: (allow-none): a #ClutterActor, or %NULL to unset * * Sets the actor to be used as the drag handle. * * Since: 1.4 */ void clutter_drag_action_set_drag_handle (ClutterDragAction *action, ClutterActor *handle) { ClutterDragActionPrivate *priv; g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); g_return_if_fail (handle == NULL || CLUTTER_IS_ACTOR (handle)); priv = action->priv; if (priv->drag_handle == handle) return; if (priv->drag_handle != NULL) g_signal_handlers_disconnect_by_func (priv->drag_handle, G_CALLBACK (on_drag_handle_destroy), action); priv->drag_handle = handle; priv->transformed_press_x = priv->press_x; priv->transformed_press_y = priv->press_y; if (priv->drag_handle != NULL) { clutter_actor_transform_stage_point (priv->drag_handle, priv->press_x, priv->press_y, &priv->transformed_press_x, &priv->transformed_press_y); g_signal_connect (priv->drag_handle, "destroy", G_CALLBACK (on_drag_handle_destroy), action); } g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_HANDLE]); } /** * clutter_drag_action_get_drag_handle: * @action: a #ClutterDragAction * * Retrieves the drag handle set by clutter_drag_action_set_drag_handle() * * Return value: (transfer none): a #ClutterActor, used as the drag * handle, or %NULL if none was set * * Since: 1.4 */ ClutterActor * clutter_drag_action_get_drag_handle (ClutterDragAction *action) { g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action), NULL); return action->priv->drag_handle; } /** * clutter_drag_action_set_drag_axis: * @action: a #ClutterDragAction * @axis: the axis to constraint the dragging to * * Restricts the dragging action to a specific axis * * Since: 1.4 */ void clutter_drag_action_set_drag_axis (ClutterDragAction *action, ClutterDragAxis axis) { ClutterDragActionPrivate *priv; g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); g_return_if_fail (axis >= CLUTTER_DRAG_AXIS_NONE && axis <= CLUTTER_DRAG_Y_AXIS); priv = action->priv; if (priv->drag_axis == axis) return; priv->drag_axis = axis; g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_AXIS]); } /** * clutter_drag_action_get_drag_axis: * @action: a #ClutterDragAction * * Retrieves the axis constraint set by clutter_drag_action_set_drag_axis() * * Return value: the axis constraint * * Since: 1.4 */ ClutterDragAxis clutter_drag_action_get_drag_axis (ClutterDragAction *action) { g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action), CLUTTER_DRAG_AXIS_NONE); return action->priv->drag_axis; } /** * clutter_drag_action_get_press_coords: * @action: a #ClutterDragAction * @press_x: (out): return location for the press event's X coordinate * @press_y: (out): return location for the press event's Y coordinate * * Retrieves the coordinates, in stage space, of the press event * that started the dragging * * Since: 1.4 */ void clutter_drag_action_get_press_coords (ClutterDragAction *action, gfloat *press_x, gfloat *press_y) { g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); if (press_x) *press_x = action->priv->press_x; if (press_y) *press_y = action->priv->press_y; } /** * clutter_drag_action_get_motion_coords: * @action: a #ClutterDragAction * @motion_x: (out): return location for the latest motion * event's X coordinate * @motion_y: (out): return location for the latest motion * event's Y coordinate * * Retrieves the coordinates, in stage space, of the latest motion * event during the dragging * * Since: 1.4 */ void clutter_drag_action_get_motion_coords (ClutterDragAction *action, gfloat *motion_x, gfloat *motion_y) { g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); if (motion_x) *motion_x = action->priv->last_motion_x; if (motion_y) *motion_y = action->priv->last_motion_y; } /** * clutter_drag_action_get_drag_area: * @action: a #ClutterDragAction * @drag_area: (out caller-allocates): a #graphene_rect_t to be filled * * Retrieves the "drag area" associated with @action, that * is a #graphene_rect_t that constrains the actor movements, * in parents coordinates. * * Returns: %TRUE if the actor is actually constrained (and thus * @drag_area is valid), %FALSE otherwise */ gboolean clutter_drag_action_get_drag_area (ClutterDragAction *action, graphene_rect_t *drag_area) { g_return_val_if_fail (CLUTTER_IS_DRAG_ACTION (action), FALSE); if (drag_area != NULL) *drag_area = action->priv->drag_area; return action->priv->drag_area_set; } /** * clutter_drag_action_set_drag_area: * @action: a #ClutterDragAction * @drag_area: (allow-none): a #ClutterRect * * Sets @drag_area to constrain the dragging of the actor associated * with @action, so that it position is always within @drag_area, expressed * in parent's coordinates. * If @drag_area is %NULL, the actor is not constrained. */ void clutter_drag_action_set_drag_area (ClutterDragAction *action, const graphene_rect_t *drag_area) { ClutterDragActionPrivate *priv; g_return_if_fail (CLUTTER_IS_DRAG_ACTION (action)); priv = action->priv; if (drag_area != NULL) { priv->drag_area = *drag_area; priv->drag_area_set = TRUE; } else priv->drag_area_set = FALSE; g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_AREA_SET]); g_object_notify_by_pspec (G_OBJECT (action), drag_props[PROP_DRAG_AREA]); } muffin-6.4.1/clutter/clutter/clutter-input-focus.c0000664000175000017500000001723214723361714021213 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "clutter-build-config.h" #include "clutter/clutter-input-focus.h" #include "clutter/clutter-input-focus-private.h" #include "clutter/clutter-input-method-private.h" typedef struct _ClutterInputFocusPrivate ClutterInputFocusPrivate; struct _ClutterInputFocusPrivate { ClutterInputMethod *im; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterInputFocus, clutter_input_focus, G_TYPE_OBJECT) static void clutter_input_focus_real_focus_in (ClutterInputFocus *focus, ClutterInputMethod *im) { ClutterInputFocusPrivate *priv; priv = clutter_input_focus_get_instance_private (focus); priv->im = im; } static void clutter_input_focus_real_focus_out (ClutterInputFocus *focus) { ClutterInputFocusPrivate *priv; priv = clutter_input_focus_get_instance_private (focus); priv->im = NULL; } static void clutter_input_focus_class_init (ClutterInputFocusClass *klass) { klass->focus_in = clutter_input_focus_real_focus_in; klass->focus_out = clutter_input_focus_real_focus_out; } static void clutter_input_focus_init (ClutterInputFocus *focus) { } gboolean clutter_input_focus_is_focused (ClutterInputFocus *focus) { ClutterInputFocusPrivate *priv; priv = clutter_input_focus_get_instance_private (focus); return !!priv->im; } void clutter_input_focus_reset (ClutterInputFocus *focus) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_reset (priv->im); } void clutter_input_focus_set_cursor_location (ClutterInputFocus *focus, const graphene_rect_t *rect) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_cursor_location (priv->im, rect); } void clutter_input_focus_set_surrounding (ClutterInputFocus *focus, const gchar *text, guint cursor, guint anchor) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_surrounding (priv->im, text, cursor, anchor); } void clutter_input_focus_set_content_hints (ClutterInputFocus *focus, ClutterInputContentHintFlags hints) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_content_hints (priv->im, hints); } void clutter_input_focus_set_content_purpose (ClutterInputFocus *focus, ClutterInputContentPurpose purpose) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_content_purpose (priv->im, purpose); } gboolean clutter_input_focus_filter_event (ClutterInputFocus *focus, const ClutterEvent *event) { ClutterInputFocusPrivate *priv; g_return_val_if_fail (CLUTTER_IS_INPUT_FOCUS (focus), FALSE); g_return_val_if_fail (clutter_input_focus_is_focused (focus), FALSE); priv = clutter_input_focus_get_instance_private (focus); if (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE) { return clutter_input_method_filter_key_event (priv->im, &event->key); } else if (event->type == CLUTTER_IM_COMMIT) { clutter_input_focus_commit (focus, event->im.text); return TRUE; } else if (event->type == CLUTTER_IM_DELETE) { clutter_input_focus_delete_surrounding (focus, event->im.offset, event->im.len); return TRUE; } else if (event->type == CLUTTER_IM_PREEDIT) { clutter_input_focus_set_preedit_text (focus, event->im.text, event->im.offset); return TRUE; } return FALSE; } void clutter_input_focus_set_can_show_preedit (ClutterInputFocus *focus, gboolean can_show_preedit) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_can_show_preedit (priv->im, can_show_preedit); } void clutter_input_focus_set_input_panel_state (ClutterInputFocus *focus, ClutterInputPanelState state) { ClutterInputFocusPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (clutter_input_focus_is_focused (focus)); priv = clutter_input_focus_get_instance_private (focus); clutter_input_method_set_input_panel_state (priv->im, state); } void clutter_input_focus_focus_in (ClutterInputFocus *focus, ClutterInputMethod *im) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->focus_in (focus, im); } void clutter_input_focus_focus_out (ClutterInputFocus *focus) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->focus_out (focus); } void clutter_input_focus_commit (ClutterInputFocus *focus, const gchar *text) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->commit_text (focus, text); } void clutter_input_focus_delete_surrounding (ClutterInputFocus *focus, int offset, guint len) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->delete_surrounding (focus, offset, len); } void clutter_input_focus_request_surrounding (ClutterInputFocus *focus) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->request_surrounding (focus); } void clutter_input_focus_set_preedit_text (ClutterInputFocus *focus, const gchar *preedit, guint cursor) { g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); CLUTTER_INPUT_FOCUS_GET_CLASS (focus)->set_preedit_text (focus, preedit, cursor); } muffin-6.4.1/clutter/clutter/clutter-script-private.h0000664000175000017500000001362714723361714021724 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_SCRIPT_PRIVATE_H__ #define __CLUTTER_SCRIPT_PRIVATE_H__ #include #include #include "clutter-color.h" #include "clutter-types.h" #include "clutter-script.h" G_BEGIN_DECLS #define CLUTTER_TYPE_SCRIPT_PARSER (_clutter_script_parser_get_type ()) #define CLUTTER_SCRIPT_PARSER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCRIPT_PARSER, ClutterScriptParser)) #define CLUTTER_IS_SCRIPT_PARSER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCRIPT_PARSER)) typedef struct _ClutterScriptParser ClutterScriptParser; typedef struct _JsonParserClass ClutterScriptParserClass; struct _ClutterScriptParser { JsonParser parent_instance; /* back reference */ ClutterScript *script; }; typedef GType (* GTypeGetFunc) (void); typedef struct { gchar *id; gchar *class_name; gchar *type_func; GList *properties; GList *children; GList *signals; GType gtype; GObject *object; guint merge_id; guint is_actor : 1; guint is_stage : 1; guint is_stage_default : 1; guint has_unresolved : 1; guint is_unmerged : 1; } ObjectInfo; void object_info_free (gpointer data); typedef struct { gchar *name; JsonNode *node; GParamSpec *pspec; guint is_child : 1; guint is_layout : 1; } PropertyInfo; typedef struct { gchar *name; gchar *handler; gchar *object; gchar *state; gchar *target; GConnectFlags flags; guint is_handler : 1; guint warp_to : 1; } SignalInfo; void property_info_free (gpointer data); GType _clutter_script_parser_get_type (void) G_GNUC_CONST; gboolean _clutter_script_parse_node (ClutterScript *script, GValue *value, const gchar *name, JsonNode *node, GParamSpec *pspec); GType _clutter_script_get_type_from_symbol (const gchar *symbol); GType _clutter_script_get_type_from_class (const gchar *name); gulong _clutter_script_resolve_animation_mode (JsonNode *node); gboolean _clutter_script_enum_from_string (GType gtype, const gchar *string, gint *enum_value); gboolean _clutter_script_flags_from_string (GType gtype, const gchar *string, gint *flags_value); gboolean _clutter_script_parse_knot (ClutterScript *script, JsonNode *node, ClutterKnot *knot); gboolean _clutter_script_parse_rect (ClutterScript *script, JsonNode *node, graphene_rect_t *rect); gboolean _clutter_script_parse_color (ClutterScript *script, JsonNode *node, ClutterColor *color); GObject *_clutter_script_parse_alpha (ClutterScript *script, JsonNode *node); gboolean _clutter_script_parse_point (ClutterScript *script, JsonNode *node, graphene_point_t *point); gboolean _clutter_script_parse_size (ClutterScript *script, JsonNode *node, graphene_size_t *size); gboolean _clutter_script_parse_translatable_string (ClutterScript *script, JsonNode *node, char **str); void _clutter_script_construct_object (ClutterScript *script, ObjectInfo *oinfo); void _clutter_script_apply_properties (ClutterScript *script, ObjectInfo *oinfo); gchar *_clutter_script_generate_fake_id (ClutterScript *script); void _clutter_script_warn_missing_attribute (ClutterScript *script, const gchar *id, const gchar *attribute); void _clutter_script_warn_invalid_value (ClutterScript *script, const gchar *attribute, const gchar *expected, JsonNode *node); ObjectInfo *_clutter_script_get_object_info (ClutterScript *script, const gchar *script_id); guint _clutter_script_get_last_merge_id (ClutterScript *script); void _clutter_script_add_object_info (ClutterScript *script, ObjectInfo *oinfo); const gchar *_clutter_script_get_id_from_node (JsonNode *node); G_END_DECLS #endif /* __CLUTTER_SCRIPT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-cairo.h0000664000175000017500000000362314723361714020040 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_CAIRO_H__ #define __CLUTTER_CAIRO_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS /** * CLUTTER_CAIRO_FORMAT_ARGB32: * * The #CoglPixelFormat to be used when uploading image data from * and to a Cairo image surface using %CAIRO_FORMAT_ARGB32 and * %CAIRO_FORMAT_RGB24 as #cairo_format_t. * * Since: 1.8 */ /* Cairo stores the data in native byte order as ARGB but Cogl's pixel * formats specify the actual byte order. Therefore we need to use a * different format depending on the architecture */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define CLUTTER_CAIRO_FORMAT_ARGB32 (COGL_PIXEL_FORMAT_BGRA_8888_PRE) #else #define CLUTTER_CAIRO_FORMAT_ARGB32 (COGL_PIXEL_FORMAT_ARGB_8888_PRE) #endif CLUTTER_EXPORT void clutter_cairo_clear (cairo_t *cr); CLUTTER_EXPORT void clutter_cairo_set_source_color (cairo_t *cr, const ClutterColor *color); G_END_DECLS #endif /* __CLUTTER_CAIRO_H__ */ muffin-6.4.1/clutter/clutter/clutter-zoom-action.h0000664000175000017500000000772014723361714021204 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Lionel Landwerlin */ #ifndef __CLUTTER_ZOOM_ACTION_H__ #define __CLUTTER_ZOOM_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_ZOOM_ACTION (clutter_zoom_action_get_type ()) #define CLUTTER_ZOOM_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ZOOM_ACTION, ClutterZoomAction)) #define CLUTTER_IS_ZOOM_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ZOOM_ACTION)) #define CLUTTER_ZOOM_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ZOOM_ACTION, ClutterZoomActionClass)) #define CLUTTER_IS_ZOOM_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ZOOM_ACTION)) #define CLUTTER_ZOOM_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ZOOM_ACTION, ClutterZoomActionClass)) typedef struct _ClutterZoomAction ClutterZoomAction; typedef struct _ClutterZoomActionPrivate ClutterZoomActionPrivate; typedef struct _ClutterZoomActionClass ClutterZoomActionClass; /** * ClutterZoomAction: * * The #ClutterZoomAction structure contains only * private data and should be accessed using the provided API * * Since: 1.12 */ struct _ClutterZoomAction { /*< private >*/ ClutterGestureAction parent_instance; ClutterZoomActionPrivate *priv; }; /** * ClutterZoomActionClass: * @zoom: class handler of the #ClutterZoomAction::zoom signal * * The #ClutterZoomActionClass structure contains * only private data * * Since: 1.12 */ struct _ClutterZoomActionClass { /*< private >*/ ClutterGestureActionClass parent_class; /*< public >*/ gboolean (* zoom) (ClutterZoomAction *action, ClutterActor *actor, graphene_point_t *focal_point, gdouble factor); /*< private >*/ void (* _clutter_zoom_action1) (void); void (* _clutter_zoom_action2) (void); void (* _clutter_zoom_action3) (void); void (* _clutter_zoom_action4) (void); void (* _clutter_zoom_action5) (void); }; CLUTTER_EXPORT GType clutter_zoom_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_zoom_action_new (void); CLUTTER_EXPORT void clutter_zoom_action_set_zoom_axis (ClutterZoomAction *action, ClutterZoomAxis axis); CLUTTER_EXPORT ClutterZoomAxis clutter_zoom_action_get_zoom_axis (ClutterZoomAction *action); CLUTTER_EXPORT void clutter_zoom_action_get_focal_point (ClutterZoomAction *action, graphene_point_t *point); CLUTTER_EXPORT void clutter_zoom_action_get_transformed_focal_point (ClutterZoomAction *action, graphene_point_t *point); G_END_DECLS #endif /* __CLUTTER_ZOOM_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-device.c0000664000175000017500000021101014723361714021321 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ /** * SECTION:clutter-input-device * @short_description: An input device managed by Clutter * * #ClutterInputDevice represents an input device known to Clutter. * * The #ClutterInputDevice class holds the state of the device, but * its contents are usually defined by the Clutter backend in use. */ #include "clutter-build-config.h" #include "clutter-input-device.h" #include "clutter-actor-private.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-event-private.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-stage-private.h" #include "clutter-input-device-private.h" #include "clutter-input-device-tool.h" #include enum { PROP_0, PROP_BACKEND, PROP_ID, PROP_NAME, PROP_DEVICE_TYPE, PROP_SEAT, PROP_DEVICE_MODE, PROP_HAS_CURSOR, PROP_ENABLED, PROP_N_AXES, PROP_VENDOR_ID, PROP_PRODUCT_ID, PROP_N_STRIPS, PROP_N_RINGS, PROP_N_MODE_GROUPS, PROP_DEVICE_NODE, PROP_MAPPING_MODE, PROP_LAST }; static void _clutter_input_device_free_touch_info (gpointer data); static void on_cursor_actor_destroy (ClutterActor *actor, ClutterInputDevice *device); static void on_cursor_actor_reactive_changed (ClutterActor *actor, GParamSpec *pspec, ClutterInputDevice *device); static GParamSpec *obj_props[PROP_LAST] = { NULL, }; G_DEFINE_TYPE (ClutterInputDevice, clutter_input_device, G_TYPE_OBJECT); static void clutter_input_device_dispose (GObject *gobject) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (gobject); g_clear_pointer (&device->device_name, g_free); g_clear_pointer (&device->vendor_id, g_free); g_clear_pointer (&device->product_id, g_free); g_clear_pointer (&device->node_path, g_free); if (device->associated != NULL) { if (device->device_mode == CLUTTER_INPUT_MODE_SLAVE) _clutter_input_device_remove_slave (device->associated, device); _clutter_input_device_set_associated_device (device->associated, NULL); g_object_unref (device->associated); device->associated = NULL; } if (device->accessibility_virtual_device) g_clear_object (&device->accessibility_virtual_device); g_clear_pointer (&device->axes, g_array_unref); g_clear_pointer (&device->keys, g_array_unref); g_clear_pointer (&device->scroll_info, g_array_unref); g_clear_pointer (&device->touch_sequences_info, g_hash_table_unref); if (device->cursor_actor) { g_signal_handlers_disconnect_by_func (device->cursor_actor, G_CALLBACK (on_cursor_actor_destroy), device); g_signal_handlers_disconnect_by_func (device->cursor_actor, G_CALLBACK (on_cursor_actor_reactive_changed), device); _clutter_actor_set_has_pointer (device->cursor_actor, FALSE); device->cursor_actor = NULL; } if (device->inv_touch_sequence_actors) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, device->inv_touch_sequence_actors); while (g_hash_table_iter_next (&iter, &key, &value)) { g_signal_handlers_disconnect_by_func (key, G_CALLBACK (on_cursor_actor_destroy), device); g_signal_handlers_disconnect_by_func (device->cursor_actor, G_CALLBACK (on_cursor_actor_reactive_changed), device); _clutter_actor_set_has_pointer (key, FALSE); g_list_free (value); } g_hash_table_unref (device->inv_touch_sequence_actors); device->inv_touch_sequence_actors = NULL; } G_OBJECT_CLASS (clutter_input_device_parent_class)->dispose (gobject); } static void clutter_input_device_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject); switch (prop_id) { case PROP_ID: self->id = g_value_get_int (value); break; case PROP_DEVICE_TYPE: self->device_type = g_value_get_enum (value); break; case PROP_SEAT: self->seat = g_value_get_object (value); break; case PROP_DEVICE_MODE: self->device_mode = g_value_get_enum (value); break; case PROP_BACKEND: self->backend = g_value_get_object (value); break; case PROP_NAME: self->device_name = g_value_dup_string (value); break; case PROP_HAS_CURSOR: self->has_cursor = g_value_get_boolean (value); break; case PROP_ENABLED: clutter_input_device_set_enabled (self, g_value_get_boolean (value)); break; case PROP_VENDOR_ID: self->vendor_id = g_value_dup_string (value); break; case PROP_PRODUCT_ID: self->product_id = g_value_dup_string (value); break; case PROP_N_RINGS: self->n_rings = g_value_get_int (value); break; case PROP_N_STRIPS: self->n_strips = g_value_get_int (value); break; case PROP_N_MODE_GROUPS: self->n_mode_groups = g_value_get_int (value); break; case PROP_DEVICE_NODE: self->node_path = g_value_dup_string (value); break; case PROP_MAPPING_MODE: self->mapping_mode = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_input_device_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterInputDevice *self = CLUTTER_INPUT_DEVICE (gobject); switch (prop_id) { case PROP_ID: g_value_set_int (value, self->id); break; case PROP_DEVICE_TYPE: g_value_set_enum (value, self->device_type); break; case PROP_SEAT: g_value_set_object (value, self->seat); break; case PROP_DEVICE_MODE: g_value_set_enum (value, self->device_mode); break; case PROP_BACKEND: g_value_set_object (value, self->backend); break; case PROP_NAME: g_value_set_string (value, self->device_name); break; case PROP_HAS_CURSOR: g_value_set_boolean (value, self->has_cursor); break; case PROP_N_AXES: g_value_set_uint (value, clutter_input_device_get_n_axes (self)); break; case PROP_ENABLED: g_value_set_boolean (value, self->is_enabled); break; case PROP_VENDOR_ID: g_value_set_string (value, self->vendor_id); break; case PROP_PRODUCT_ID: g_value_set_string (value, self->product_id); break; case PROP_N_RINGS: g_value_set_int (value, self->n_rings); break; case PROP_N_STRIPS: g_value_set_int (value, self->n_strips); break; case PROP_N_MODE_GROUPS: g_value_set_int (value, self->n_mode_groups); break; case PROP_DEVICE_NODE: g_value_set_string (value, self->node_path); break; case PROP_MAPPING_MODE: g_value_set_enum (value, self->mapping_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_input_device_class_init (ClutterInputDeviceClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /** * ClutterInputDevice:id: * * The unique identifier of the device * * Since: 1.2 */ obj_props[PROP_ID] = g_param_spec_int ("id", P_("Id"), P_("Unique identifier of the device"), -1, G_MAXINT, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:name: * * The name of the device * * Since: 1.2 */ obj_props[PROP_NAME] = g_param_spec_string ("name", P_("Name"), P_("The name of the device"), NULL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:device-type: * * The type of the device * * Since: 1.2 */ obj_props[PROP_DEVICE_TYPE] = g_param_spec_enum ("device-type", P_("Device Type"), P_("The type of the device"), CLUTTER_TYPE_INPUT_DEVICE_TYPE, CLUTTER_POINTER_DEVICE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:seat: * * The #ClutterSeat instance which owns the device */ obj_props[PROP_SEAT] = g_param_spec_object ("seat", P_("Seat"), P_("Seat"), CLUTTER_TYPE_SEAT, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:mode: * * The mode of the device. * * Since: 1.6 */ obj_props[PROP_DEVICE_MODE] = g_param_spec_enum ("device-mode", P_("Device Mode"), P_("The mode of the device"), CLUTTER_TYPE_INPUT_MODE, CLUTTER_INPUT_MODE_FLOATING, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:has-cursor: * * Whether the device has an on screen cursor following its movement. * * Since: 1.6 */ obj_props[PROP_HAS_CURSOR] = g_param_spec_boolean ("has-cursor", P_("Has Cursor"), P_("Whether the device has a cursor"), FALSE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:enabled: * * Whether the device is enabled. * * A device with the #ClutterInputDevice:device-mode property set * to %CLUTTER_INPUT_MODE_MASTER cannot be disabled. * * A device must be enabled in order to receive events from it. * * Since: 1.6 */ obj_props[PROP_ENABLED] = g_param_spec_boolean ("enabled", P_("Enabled"), P_("Whether the device is enabled"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterInputDevice:n-axes: * * The number of axes of the device. * * Since: 1.6 */ obj_props[PROP_N_AXES] = g_param_spec_uint ("n-axes", P_("Number of Axes"), P_("The number of axes on the device"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READABLE); /** * ClutterInputDevice:backend: * * The #ClutterBackend that created the device. * * Since: 1.6 */ obj_props[PROP_BACKEND] = g_param_spec_object ("backend", P_("Backend"), P_("The backend instance"), CLUTTER_TYPE_BACKEND, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:vendor-id: * * Vendor ID of this device. * * Since: 1.22 */ obj_props[PROP_VENDOR_ID] = g_param_spec_string ("vendor-id", P_("Vendor ID"), P_("Vendor ID"), NULL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterInputDevice:product-id: * * Product ID of this device. * * Since: 1.22 */ obj_props[PROP_PRODUCT_ID] = g_param_spec_string ("product-id", P_("Product ID"), P_("Product ID"), NULL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_N_RINGS] = g_param_spec_int ("n-rings", P_("Number of rings"), P_("Number of rings (circular sliders) in this device"), 0, G_MAXINT, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_N_STRIPS] = g_param_spec_int ("n-strips", P_("Number of strips"), P_("Number of strips (linear sliders) in this device"), 0, G_MAXINT, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_N_MODE_GROUPS] = g_param_spec_int ("n-mode-groups", P_("Number of mode groups"), P_("Number of mode groups"), 0, G_MAXINT, 0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_DEVICE_NODE] = g_param_spec_string ("device-node", P_("Device node path"), P_("Device node path"), NULL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_MAPPING_MODE] = g_param_spec_enum ("mapping-mode", P_("Device mapping mode"), P_("Device mapping mode"), CLUTTER_TYPE_INPUT_DEVICE_MAPPING, CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE, CLUTTER_PARAM_READWRITE); gobject_class->dispose = clutter_input_device_dispose; gobject_class->set_property = clutter_input_device_set_property; gobject_class->get_property = clutter_input_device_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_input_device_init (ClutterInputDevice *self) { self->id = -1; self->device_type = CLUTTER_POINTER_DEVICE; self->click_count = 0; self->current_time = self->previous_time = CLUTTER_CURRENT_TIME; self->current_x = self->previous_x = -1; self->current_y = self->previous_y = -1; self->current_button_number = self->previous_button_number = -1; self->current_state = self->previous_state = 0; self->touch_sequences_info = g_hash_table_new_full (NULL, NULL, NULL, _clutter_input_device_free_touch_info); self->inv_touch_sequence_actors = g_hash_table_new (NULL, NULL); } static ClutterTouchInfo * _clutter_input_device_ensure_touch_info (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterStage *stage) { ClutterTouchInfo *info; info = g_hash_table_lookup (device->touch_sequences_info, sequence); if (info == NULL) { info = g_slice_new0 (ClutterTouchInfo); info->sequence = sequence; g_hash_table_insert (device->touch_sequences_info, sequence, info); if (g_hash_table_size (device->touch_sequences_info) == 1) _clutter_input_device_set_stage (device, stage); } return info; } /*< private > * clutter_input_device_set_coords: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence or NULL * @x: X coordinate of the device * @y: Y coordinate of the device * * Stores the last known coordinates of the device */ void _clutter_input_device_set_coords (ClutterInputDevice *device, ClutterEventSequence *sequence, gfloat x, gfloat y, ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); if (sequence == NULL) { if (device->current_x != x) device->current_x = x; if (device->current_y != y) device->current_y = y; } else { ClutterTouchInfo *info; info = _clutter_input_device_ensure_touch_info (device, sequence, stage); info->current_x = x; info->current_y = y; } } /*< private > * clutter_input_device_set_state: * @device: a #ClutterInputDevice * @state: a bitmask of modifiers * * Stores the last known modifiers state of the device */ void _clutter_input_device_set_state (ClutterInputDevice *device, ClutterModifierType state) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); device->current_state = state; } /** * clutter_input_device_get_modifier_state: * @device: a #ClutterInputDevice * * Retrieves the current modifiers state of the device, as seen * by the last event Clutter processed. * * Return value: the last known modifier state * * Since: 1.16 */ ClutterModifierType clutter_input_device_get_modifier_state (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); return device->current_state; } /*< private > * clutter_input_device_set_time: * @device: a #ClutterInputDevice * @time_: the time * * Stores the last known event time of the device */ void _clutter_input_device_set_time (ClutterInputDevice *device, guint32 time_) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); if (device->current_time != time_) device->current_time = time_; } /*< private > * clutter_input_device_set_stage: * @device: a #ClutterInputDevice * @stage: a #ClutterStage or %NULL * * Stores the stage under the device */ void _clutter_input_device_set_stage (ClutterInputDevice *device, ClutterStage *stage) { if (device->stage == stage) return; device->stage = stage; /* we leave the ->cursor_actor in place in order to check * if we left the stage without crossing it again; this way * we can emit a leave event on the cursor actor right before * we emit the leave event on the stage. */ } /*< private > * clutter_input_device_get_stage: * @device: a #ClutterInputDevice * * Retrieves the stage currently associated with @device. * * Return value: The stage currently associated with @device. */ ClutterStage * _clutter_input_device_get_stage (ClutterInputDevice *device) { return device->stage; } static void _clutter_input_device_free_touch_info (gpointer data) { g_slice_free (ClutterTouchInfo, data); } static ClutterActor * _clutter_input_device_get_actor (ClutterInputDevice *device, ClutterEventSequence *sequence) { ClutterTouchInfo *info; if (sequence == NULL) return device->cursor_actor; info = g_hash_table_lookup (device->touch_sequences_info, sequence); return info->actor; } static void _clutter_input_device_associate_actor (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterActor *actor) { if (sequence == NULL) device->cursor_actor = actor; else { GList *sequences = g_hash_table_lookup (device->inv_touch_sequence_actors, actor); ClutterTouchInfo *info; ClutterStage *stage = CLUTTER_STAGE (clutter_actor_get_stage (actor)); info = _clutter_input_device_ensure_touch_info (device, sequence, stage); info->actor = actor; g_hash_table_insert (device->inv_touch_sequence_actors, actor, g_list_prepend (sequences, sequence)); } g_signal_connect (actor, "destroy", G_CALLBACK (on_cursor_actor_destroy), device); g_signal_connect (actor, "notify::reactive", G_CALLBACK (on_cursor_actor_reactive_changed), device); _clutter_actor_set_has_pointer (actor, TRUE); } static void _clutter_input_device_unassociate_actor (ClutterInputDevice *device, ClutterActor *actor, gboolean destroyed) { if (device->cursor_actor == actor) device->cursor_actor = NULL; else { GList *l, *sequences = g_hash_table_lookup (device->inv_touch_sequence_actors, actor); for (l = sequences; l != NULL; l = l->next) { ClutterTouchInfo *info = g_hash_table_lookup (device->touch_sequences_info, l->data); if (info) info->actor = NULL; } g_list_free (sequences); g_hash_table_remove (device->inv_touch_sequence_actors, actor); } if (destroyed == FALSE) { g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (on_cursor_actor_destroy), device); g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (on_cursor_actor_reactive_changed), device); _clutter_actor_set_has_pointer (actor, FALSE); } } static void on_cursor_actor_destroy (ClutterActor *actor, ClutterInputDevice *device) { _clutter_input_device_unassociate_actor (device, actor, TRUE); } static void on_cursor_actor_reactive_changed (ClutterActor *actor, GParamSpec *pspec, ClutterInputDevice *device) { if (!clutter_actor_get_reactive (actor)) _clutter_input_device_unassociate_actor (device, actor, FALSE); } /*< private > * clutter_input_device_set_actor: * @device: a #ClutterInputDevice * @actor: a #ClutterActor * @emit_crossing: %TRUE to emit crossing events * * Sets the actor under the pointer coordinates of @device * * This function is called by clutter_input_device_update() * and it will: * * - queue a %CLUTTER_LEAVE event on the previous pointer actor * of @device, if any * - set to %FALSE the :has-pointer property of the previous * pointer actor of @device, if any * - queue a %CLUTTER_ENTER event on the new pointer actor * - set to %TRUE the :has-pointer property of the new pointer * actor */ void _clutter_input_device_set_actor (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterActor *actor, gboolean emit_crossing) { ClutterActor *old_actor = _clutter_input_device_get_actor (device, sequence); if (old_actor == actor) return; if (old_actor != NULL) { ClutterActor *tmp_old_actor; if (emit_crossing) { ClutterEvent *event; event = clutter_event_new (CLUTTER_LEAVE); event->crossing.time = device->current_time; event->crossing.flags = 0; event->crossing.stage = device->stage; event->crossing.source = old_actor; event->crossing.x = device->current_x; event->crossing.y = device->current_y; event->crossing.related = actor; event->crossing.sequence = sequence; clutter_event_set_device (event, device); /* we need to make sure that this event is processed * before any other event we might have queued up until * now, so we go on, and synthesize the event emission * ourselves */ _clutter_process_event (event); clutter_event_free (event); } /* processing the event might have destroyed the actor */ tmp_old_actor = _clutter_input_device_get_actor (device, sequence); _clutter_input_device_unassociate_actor (device, old_actor, tmp_old_actor == NULL); old_actor = tmp_old_actor; } if (actor != NULL) { _clutter_input_device_associate_actor (device, sequence, actor); if (emit_crossing) { ClutterEvent *event; event = clutter_event_new (CLUTTER_ENTER); event->crossing.time = device->current_time; event->crossing.flags = 0; event->crossing.stage = device->stage; event->crossing.x = device->current_x; event->crossing.y = device->current_y; event->crossing.source = actor; event->crossing.related = old_actor; event->crossing.sequence = sequence; clutter_event_set_device (event, device); /* see above */ _clutter_process_event (event); clutter_event_free (event); } } } /** * clutter_input_device_get_device_type: * @device: a #ClutterInputDevice * * Retrieves the type of @device * * Return value: the type of the device * * Since: 1.0 */ ClutterInputDeviceType clutter_input_device_get_device_type (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), CLUTTER_POINTER_DEVICE); return device->device_type; } /** * clutter_input_device_get_device_id: * @device: a #ClutterInputDevice * * Retrieves the unique identifier of @device * * Return value: the identifier of the device * * Since: 1.0 */ gint clutter_input_device_get_device_id (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), -1); return device->id; } /** * clutter_input_device_set_enabled: * @device: a #ClutterInputDevice * @enabled: %TRUE to enable the @device * * Enables or disables a #ClutterInputDevice. * * Only devices with a #ClutterInputDevice:device-mode property set * to %CLUTTER_INPUT_MODE_SLAVE or %CLUTTER_INPUT_MODE_FLOATING can * be disabled. * * Since: 1.6 */ void clutter_input_device_set_enabled (ClutterInputDevice *device, gboolean enabled) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); enabled = !!enabled; if (!enabled && device->device_mode == CLUTTER_INPUT_MODE_MASTER) return; if (device->is_enabled == enabled) return; device->is_enabled = enabled; g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_ENABLED]); } /** * clutter_input_device_get_enabled: * @device: a #ClutterInputDevice * * Retrieves whether @device is enabled. * * Return value: %TRUE if the device is enabled * * Since: 1.6 */ gboolean clutter_input_device_get_enabled (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); return device->is_enabled; } /** * clutter_input_device_get_coords: * @device: a #ClutterInputDevice * @sequence: (allow-none): a #ClutterEventSequence, or %NULL if * the device is not touch-based * @point: (out caller-allocates): return location for the pointer * or touch point * * Retrieves the latest coordinates of a pointer or touch point of * @device. * * Return value: %FALSE if the device's sequence hasn't been found, * and %TRUE otherwise. * * Since: 1.12 */ gboolean clutter_input_device_get_coords (ClutterInputDevice *device, ClutterEventSequence *sequence, graphene_point_t *point) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); g_return_val_if_fail (point != NULL, FALSE); if (sequence == NULL) { point->x = device->current_x; point->y = device->current_y; } else { ClutterTouchInfo *info = g_hash_table_lookup (device->touch_sequences_info, sequence); if (info == NULL) return FALSE; point->x = info->current_x; point->y = info->current_y; } return TRUE; } /* * clutter_input_device_update: * @device: a #ClutterInputDevice * * Updates the input @device by determining the #ClutterActor underneath the * pointer's cursor * * This function calls _clutter_input_device_set_actor() if needed. * * This function only works for #ClutterInputDevice of type * %CLUTTER_POINTER_DEVICE. * * Since: 1.2 */ ClutterActor * clutter_input_device_update (ClutterInputDevice *device, ClutterEventSequence *sequence, gboolean emit_crossing) { ClutterStage *stage; ClutterActor *new_cursor_actor; ClutterActor *old_cursor_actor; graphene_point_t point = GRAPHENE_POINT_INIT (-1.0f, -1.0f); ClutterInputDeviceType device_type = device->device_type; g_assert (device_type != CLUTTER_KEYBOARD_DEVICE && device_type != CLUTTER_PAD_DEVICE); stage = device->stage; if (G_UNLIKELY (stage == NULL)) { CLUTTER_NOTE (EVENT, "No stage defined for device %d '%s'", clutter_input_device_get_device_id (device), clutter_input_device_get_device_name (device)); return NULL; } clutter_input_device_get_coords (device, sequence, &point); old_cursor_actor = _clutter_input_device_get_actor (device, sequence); new_cursor_actor = _clutter_stage_do_pick (stage, point.x, point.y, CLUTTER_PICK_REACTIVE); /* if the pick could not find an actor then we do not update the * input device, to avoid ghost enter/leave events; the pick should * never fail, except for bugs in the glReadPixels() implementation * in which case this is the safest course of action anyway */ if (new_cursor_actor == NULL) return NULL; CLUTTER_NOTE (EVENT, "Actor under cursor (device %d, at %.2f, %.2f): %s", clutter_input_device_get_device_id (device), point.x, point.y, _clutter_actor_get_debug_name (new_cursor_actor)); /* short-circuit here */ if (new_cursor_actor == old_cursor_actor) return old_cursor_actor; _clutter_input_device_set_actor (device, sequence, new_cursor_actor, emit_crossing); return new_cursor_actor; } /** * clutter_input_device_get_pointer_actor: * @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE * * Retrieves the #ClutterActor underneath the pointer of @device * * Return value: (transfer none): a pointer to the #ClutterActor or %NULL * * Since: 1.2 */ ClutterActor * clutter_input_device_get_pointer_actor (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL); return device->cursor_actor; } /** * clutter_input_device_get_pointer_stage: * @device: a #ClutterInputDevice of type %CLUTTER_POINTER_DEVICE * * Retrieves the #ClutterStage underneath the pointer of @device * * Return value: (transfer none): a pointer to the #ClutterStage or %NULL * * Since: 1.2 */ ClutterStage * clutter_input_device_get_pointer_stage (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, NULL); return device->stage; } /** * clutter_input_device_get_device_name: * @device: a #ClutterInputDevice * * Retrieves the name of the @device * * Return value: the name of the device, or %NULL. The returned string * is owned by the #ClutterInputDevice and should never be modified * or freed * * Since: 1.2 */ const gchar * clutter_input_device_get_device_name (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); return device->device_name; } /** * clutter_input_device_get_has_cursor: * @device: a #ClutterInputDevice * * Retrieves whether @device has a pointer that follows the * device motion. * * Return value: %TRUE if the device has a cursor * * Since: 1.6 */ gboolean clutter_input_device_get_has_cursor (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); return device->has_cursor; } /** * clutter_input_device_get_device_mode: * @device: a #ClutterInputDevice * * Retrieves the #ClutterInputMode of @device. * * Return value: the device mode * * Since: 1.6 */ ClutterInputMode clutter_input_device_get_device_mode (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), CLUTTER_INPUT_MODE_FLOATING); return device->device_mode; } /** * clutter_input_device_update_from_event: * @device: a #ClutterInputDevice * @event: a #ClutterEvent * @update_stage: whether to update the #ClutterStage of the @device * using the stage of the event * * Forcibly updates the state of the @device using a #ClutterEvent * * This function should never be used by applications: it is meant * for integration with embedding toolkits, like clutter-gtk * * Embedding toolkits that disable the event collection inside Clutter * need to use this function to update the state of input devices depending * on a #ClutterEvent that they are going to submit to the event handling code * in Clutter though clutter_do_event(). Since the input devices hold the state * that is going to be used to fill in fields like the #ClutterButtonEvent * click count, or to emit synthesized events like %CLUTTER_ENTER and * %CLUTTER_LEAVE, it is necessary for embedding toolkits to also be * responsible of updating the input device state. * * For instance, this might be the code to translate an embedding toolkit * native motion notification into a Clutter #ClutterMotionEvent and ask * Clutter to process it: * * |[ * ClutterEvent c_event; * * translate_native_event_to_clutter (native_event, &c_event); * * clutter_do_event (&c_event); * ]| * * Before letting clutter_do_event() process the event, it is necessary to call * clutter_input_device_update_from_event(): * * |[ * ClutterEvent c_event; * ClutterDeviceManager *manager; * ClutterInputDevice *device; * * translate_native_event_to_clutter (native_event, &c_event); * * // get the seat * seat = clutter_backend_get_deafult_seat (clutter_get_default_backend ()); * * // use the default Core Pointer that Clutter backends register by default * device = clutter_seat_get_pointer (seat); * * // update the state of the input device * clutter_input_device_update_from_event (device, &c_event, FALSE); * * clutter_do_event (&c_event); * ]| * * The @update_stage boolean argument should be used when the input device * enters and leaves a #ClutterStage; it will use the #ClutterStage field * of the passed @event to update the stage associated to the input device. * * Since: 1.2 */ void clutter_input_device_update_from_event (ClutterInputDevice *device, ClutterEvent *event, gboolean update_stage) { ClutterModifierType event_state; ClutterEventSequence *sequence; ClutterStage *event_stage; gfloat event_x, event_y; guint32 event_time; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (event != NULL); event_state = clutter_event_get_state (event); event_time = clutter_event_get_time (event); event_stage = clutter_event_get_stage (event); sequence = clutter_event_get_event_sequence (event); clutter_event_get_coords (event, &event_x, &event_y); _clutter_input_device_set_coords (device, sequence, event_x, event_y, event_stage); _clutter_input_device_set_state (device, event_state); _clutter_input_device_set_time (device, event_time); if (update_stage) _clutter_input_device_set_stage (device, event_stage); } /*< private > * clutter_input_device_reset_axes: * @device: a #ClutterInputDevice * * Resets the axes on @device */ void _clutter_input_device_reset_axes (ClutterInputDevice *device) { if (device->axes != NULL) { g_array_free (device->axes, TRUE); device->axes = NULL; g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_N_AXES]); } } /*< private > * clutter_input_device_add_axis: * @device: a #ClutterInputDevice * @axis: the axis type * @minimum: the minimum axis value * @maximum: the maximum axis value * @resolution: the axis resolution * * Adds an axis of type @axis on @device. */ guint _clutter_input_device_add_axis (ClutterInputDevice *device, ClutterInputAxis axis, gdouble minimum, gdouble maximum, gdouble resolution) { ClutterAxisInfo info; guint pos; if (device->axes == NULL) device->axes = g_array_new (FALSE, TRUE, sizeof (ClutterAxisInfo)); info.axis = axis; info.min_value = minimum; info.max_value = maximum; info.resolution = resolution; switch (axis) { case CLUTTER_INPUT_AXIS_X: case CLUTTER_INPUT_AXIS_Y: info.min_axis = 0; info.max_axis = 0; break; case CLUTTER_INPUT_AXIS_XTILT: case CLUTTER_INPUT_AXIS_YTILT: info.min_axis = -1; info.max_axis = 1; break; default: info.min_axis = 0; info.max_axis = 1; break; } device->axes = g_array_append_val (device->axes, info); pos = device->axes->len - 1; g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_N_AXES]); return pos; } /*< private > * clutter_input_translate_axis: * @device: a #ClutterInputDevice * @index_: the index of the axis * @gint: the absolute value of the axis * @axis_value: (out): the translated value of the axis * * Performs a conversion from the absolute value of the axis * to a relative value. * * The axis at @index_ must not be %CLUTTER_INPUT_AXIS_X or * %CLUTTER_INPUT_AXIS_Y. * * Return value: %TRUE if the conversion was successful */ gboolean _clutter_input_device_translate_axis (ClutterInputDevice *device, guint index_, gdouble value, gdouble *axis_value) { ClutterAxisInfo *info; gdouble width; gdouble real_value; if (device->axes == NULL || index_ >= device->axes->len) return FALSE; info = &g_array_index (device->axes, ClutterAxisInfo, index_); if (info->axis == CLUTTER_INPUT_AXIS_X || info->axis == CLUTTER_INPUT_AXIS_Y) return FALSE; if (fabs (info->max_value - info->min_value) < 0.0000001) return FALSE; width = info->max_value - info->min_value; real_value = (info->max_axis * (value - info->min_value) + info->min_axis * (info->max_value - value)) / width; if (axis_value) *axis_value = real_value; return TRUE; } /** * clutter_input_device_get_axis: * @device: a #ClutterInputDevice * @index_: the index of the axis * * Retrieves the type of axis on @device at the given index. * * Return value: the axis type * * Since: 1.6 */ ClutterInputAxis clutter_input_device_get_axis (ClutterInputDevice *device, guint index_) { ClutterAxisInfo *info; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), CLUTTER_INPUT_AXIS_IGNORE); if (device->axes == NULL) return CLUTTER_INPUT_AXIS_IGNORE; if (index_ >= device->axes->len) return CLUTTER_INPUT_AXIS_IGNORE; info = &g_array_index (device->axes, ClutterAxisInfo, index_); return info->axis; } /** * clutter_input_device_get_axis_value: * @device: a #ClutterInputDevice * @axes: (array): an array of axes values, typically * coming from clutter_event_get_axes() * @axis: the axis to extract * @value: (out): return location for the axis value * * Extracts the value of the given @axis of a #ClutterInputDevice from * an array of axis values. * * An example of typical usage for this function is: * * |[ * ClutterInputDevice *device = clutter_event_get_device (event); * gdouble *axes = clutter_event_get_axes (event, NULL); * gdouble pressure_value = 0; * * clutter_input_device_get_axis_value (device, axes, * CLUTTER_INPUT_AXIS_PRESSURE, * &pressure_value); * ]| * * Return value: %TRUE if the value was set, and %FALSE otherwise * * Since: 1.6 */ gboolean clutter_input_device_get_axis_value (ClutterInputDevice *device, gdouble *axes, ClutterInputAxis axis, gdouble *value) { gint i; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); g_return_val_if_fail (device->axes != NULL, FALSE); for (i = 0; i < device->axes->len; i++) { ClutterAxisInfo *info; info = &g_array_index (device->axes, ClutterAxisInfo, i); if (info->axis == axis) { if (value) *value = axes[i]; return TRUE; } } return FALSE; } /** * clutter_input_device_get_n_axes: * @device: a #ClutterInputDevice * * Retrieves the number of axes available on @device. * * Return value: the number of axes on the device * * Since: 1.6 */ guint clutter_input_device_get_n_axes (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); if (device->axes != NULL) return device->axes->len; return 0; } /*< private > * clutter_input_device_set_n_keys: * @device: a #ClutterInputDevice * @n_keys: the number of keys of the device * * Initializes the keys of @device. * * Call clutter_input_device_set_key() on each key to set the keyval * and modifiers. */ void _clutter_input_device_set_n_keys (ClutterInputDevice *device, guint n_keys) { if (device->keys != NULL) g_array_free (device->keys, TRUE); device->n_keys = n_keys; device->keys = g_array_sized_new (FALSE, TRUE, sizeof (ClutterKeyInfo), n_keys); } /** * clutter_input_device_get_n_keys: * @device: a #ClutterInputDevice * * Retrieves the number of keys registered for @device. * * Return value: the number of registered keys * * Since: 1.6 */ guint clutter_input_device_get_n_keys (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); return device->n_keys; } /** * clutter_input_device_set_key: * @device: a #ClutterInputDevice * @index_: the index of the key * @keyval: the keyval * @modifiers: a bitmask of modifiers * * Sets the keyval and modifiers at the given @index_ for @device. * * Clutter will use the keyval and modifiers set when filling out * an event coming from the same input device. * * Since: 1.6 */ void clutter_input_device_set_key (ClutterInputDevice *device, guint index_, guint keyval, ClutterModifierType modifiers) { ClutterKeyInfo *key_info; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (index_ < device->n_keys); key_info = &g_array_index (device->keys, ClutterKeyInfo, index_); key_info->keyval = keyval; key_info->modifiers = modifiers; } /** * clutter_input_device_get_key: * @device: a #ClutterInputDevice * @index_: the index of the key * @keyval: (out): return location for the keyval at @index_ * @modifiers: (out): return location for the modifiers at @index_ * * Retrieves the key set using clutter_input_device_set_key() * * Return value: %TRUE if a key was set at the given index * * Since: 1.6 */ gboolean clutter_input_device_get_key (ClutterInputDevice *device, guint index_, guint *keyval, ClutterModifierType *modifiers) { ClutterKeyInfo *key_info; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); if (device->keys == NULL) return FALSE; if (index_ > device->keys->len) return FALSE; key_info = &g_array_index (device->keys, ClutterKeyInfo, index_); if (!key_info->keyval && !key_info->modifiers) return FALSE; if (keyval) *keyval = key_info->keyval; if (modifiers) *modifiers = key_info->modifiers; return TRUE; } /*< private > * clutter_input_device_add_slave: * @master: a #ClutterInputDevice * @slave: a #ClutterInputDevice * * Adds @slave to the list of slave devices of @master * * This function does not increase the reference count of either @master * or @slave. */ void _clutter_input_device_add_slave (ClutterInputDevice *master, ClutterInputDevice *slave) { if (g_list_find (master->slaves, slave) == NULL) master->slaves = g_list_prepend (master->slaves, slave); } /*< private > * clutter_input_device_remove_slave: * @master: a #ClutterInputDevice * @slave: a #ClutterInputDevice * * Removes @slave from the list of slave devices of @master. * * This function does not decrease the reference count of either @master * or @slave. */ void _clutter_input_device_remove_slave (ClutterInputDevice *master, ClutterInputDevice *slave) { if (g_list_find (master->slaves, slave) != NULL) master->slaves = g_list_remove (master->slaves, slave); } /*< private > * clutter_input_device_add_sequence: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence * * Start tracking informations related to a touch point (position, * actor underneath the touch point). */ void _clutter_input_device_add_event_sequence (ClutterInputDevice *device, ClutterEvent *event) { ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); ClutterStage *stage; if (sequence == NULL) return; stage = clutter_event_get_stage (event); if (stage == NULL) return; _clutter_input_device_ensure_touch_info (device, sequence, stage); } /*< private > * clutter_input_device_remove_sequence: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence * * Stop tracking informations related to a touch point. */ void _clutter_input_device_remove_event_sequence (ClutterInputDevice *device, ClutterEvent *event) { ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); ClutterTouchInfo *info = g_hash_table_lookup (device->touch_sequences_info, sequence); if (info == NULL) return; if (info->actor != NULL) { GList *sequences = g_hash_table_lookup (device->inv_touch_sequence_actors, info->actor); sequences = g_list_remove (sequences, sequence); g_hash_table_replace (device->inv_touch_sequence_actors, info->actor, sequences); _clutter_input_device_set_actor (device, sequence, NULL, TRUE); } g_hash_table_remove (device->touch_sequences_info, sequence); } /** * clutter_input_device_get_slave_devices: * @device: a #ClutterInputDevice * * Retrieves the slave devices attached to @device. * * Return value: (transfer container) (element-type Clutter.InputDevice): a * list of #ClutterInputDevice, or %NULL. The contents of the list are * owned by the device. Use g_list_free() when done * * Since: 1.6 */ GList * clutter_input_device_get_slave_devices (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); return g_list_copy (device->slaves); } /*< internal > * clutter_input_device_set_associated_device: * @device: a #ClutterInputDevice * @associated: (allow-none): a #ClutterInputDevice, or %NULL * * Sets the associated device for @device. * * This function keeps a reference on the associated device. */ void _clutter_input_device_set_associated_device (ClutterInputDevice *device, ClutterInputDevice *associated) { if (device->associated == associated) return; if (device->associated != NULL) g_object_unref (device->associated); device->associated = associated; if (device->associated != NULL) g_object_ref (device->associated); CLUTTER_NOTE (MISC, "Associating device %d '%s' to device %d '%s'", clutter_input_device_get_device_id (device), clutter_input_device_get_device_name (device), device->associated != NULL ? clutter_input_device_get_device_id (device->associated) : -1, device->associated != NULL ? clutter_input_device_get_device_name (device->associated) : "(none)"); if (device->device_mode != CLUTTER_INPUT_MODE_MASTER) { if (device->associated != NULL) device->device_mode = CLUTTER_INPUT_MODE_SLAVE; else device->device_mode = CLUTTER_INPUT_MODE_FLOATING; g_object_notify_by_pspec (G_OBJECT (device), obj_props[PROP_DEVICE_MODE]); } } /** * clutter_input_device_get_associated_device: * @device: a #ClutterInputDevice * * Retrieves a pointer to the #ClutterInputDevice that has been * associated to @device. * * If the #ClutterInputDevice:device-mode property of @device is * set to %CLUTTER_INPUT_MODE_MASTER, this function will return * %NULL. * * Return value: (transfer none): a #ClutterInputDevice, or %NULL * * Since: 1.6 */ ClutterInputDevice * clutter_input_device_get_associated_device (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); return device->associated; } /** * clutter_input_device_keycode_to_evdev: * @device: A #ClutterInputDevice * @hardware_keycode: The hardware keycode from a #ClutterKeyEvent * @evdev_keycode: The return location for the evdev keycode * * Translates a hardware keycode from a #ClutterKeyEvent to the * equivalent evdev keycode. Note that depending on the input backend * used by Clutter this function can fail if there is no obvious * mapping between the key codes. The hardware keycode can be taken * from the #ClutterKeyEvent.hardware_keycode member of #ClutterKeyEvent. * * Return value: %TRUE if the conversion succeeded, %FALSE otherwise. * * Since: 1.10 */ gboolean clutter_input_device_keycode_to_evdev (ClutterInputDevice *device, guint hardware_keycode, guint *evdev_keycode) { ClutterInputDeviceClass *device_class; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); if (device_class->keycode_to_evdev == NULL) return FALSE; else return device_class->keycode_to_evdev (device, hardware_keycode, evdev_keycode); } void _clutter_input_device_add_scroll_info (ClutterInputDevice *device, guint index_, ClutterScrollDirection direction, gdouble increment) { ClutterScrollInfo info; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (index_ < clutter_input_device_get_n_axes (device)); info.axis_id = index_; info.direction = direction; info.increment = increment; info.last_value_valid = FALSE; if (device->scroll_info == NULL) { device->scroll_info = g_array_new (FALSE, FALSE, sizeof (ClutterScrollInfo)); } g_array_append_val (device->scroll_info, info); } gboolean _clutter_input_device_get_scroll_delta (ClutterInputDevice *device, guint index_, gdouble value, ClutterScrollDirection *direction_p, gdouble *delta_p) { guint i; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); g_return_val_if_fail (index_ < clutter_input_device_get_n_axes (device), FALSE); if (device->scroll_info == NULL) return FALSE; for (i = 0; i < device->scroll_info->len; i++) { ClutterScrollInfo *info = &g_array_index (device->scroll_info, ClutterScrollInfo, i); if (info->axis_id == index_) { if (direction_p != NULL) *direction_p = info->direction; if (delta_p != NULL) *delta_p = 0.0; if (info->last_value_valid) { if (delta_p != NULL) { *delta_p = (value - info->last_value) / info->increment; } info->last_value = value; } else { info->last_value = value; info->last_value_valid = TRUE; } return TRUE; } } return FALSE; } void _clutter_input_device_reset_scroll_info (ClutterInputDevice *device) { guint i; if (device->scroll_info == NULL) return; for (i = 0; i < device->scroll_info->len; i++) { ClutterScrollInfo *info = &g_array_index (device->scroll_info, ClutterScrollInfo, i); info->last_value_valid = FALSE; } } static void on_grab_actor_destroy (ClutterActor *actor, ClutterInputDevice *device) { switch (device->device_type) { case CLUTTER_POINTER_DEVICE: case CLUTTER_TABLET_DEVICE: device->pointer_grab_actor = NULL; break; case CLUTTER_KEYBOARD_DEVICE: device->keyboard_grab_actor = NULL; break; default: g_assert_not_reached (); } } /** * clutter_input_device_grab: * @device: a #ClutterInputDevice * @actor: a #ClutterActor * * Acquires a grab on @actor for the given @device. * * Any event coming from @device will be delivered to @actor, bypassing * the usual event delivery mechanism, until the grab is released by * calling clutter_input_device_ungrab(). * * The grab is client-side: even if the windowing system used by the Clutter * backend has the concept of "device grabs", Clutter will not use them. * * Only #ClutterInputDevice of types %CLUTTER_POINTER_DEVICE, * %CLUTTER_TABLET_DEVICE and %CLUTTER_KEYBOARD_DEVICE can hold a grab. * * Since: 1.10 */ void clutter_input_device_grab (ClutterInputDevice *device, ClutterActor *actor) { ClutterActor **grab_actor; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); switch (device->device_type) { case CLUTTER_POINTER_DEVICE: case CLUTTER_TABLET_DEVICE: grab_actor = &device->pointer_grab_actor; break; case CLUTTER_KEYBOARD_DEVICE: grab_actor = &device->keyboard_grab_actor; break; default: g_critical ("Only pointer and keyboard devices can grab an actor"); return; } if (*grab_actor != NULL) { g_signal_handlers_disconnect_by_func (*grab_actor, G_CALLBACK (on_grab_actor_destroy), device); } *grab_actor = actor; g_signal_connect (*grab_actor, "destroy", G_CALLBACK (on_grab_actor_destroy), device); } /** * clutter_input_device_ungrab: * @device: a #ClutterInputDevice * * Releases the grab on the @device, if one is in place. * * Since: 1.10 */ void clutter_input_device_ungrab (ClutterInputDevice *device) { ClutterActor **grab_actor; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); switch (device->device_type) { case CLUTTER_POINTER_DEVICE: case CLUTTER_TABLET_DEVICE: grab_actor = &device->pointer_grab_actor; break; case CLUTTER_KEYBOARD_DEVICE: grab_actor = &device->keyboard_grab_actor; break; default: return; } if (*grab_actor == NULL) return; g_signal_handlers_disconnect_by_func (*grab_actor, G_CALLBACK (on_grab_actor_destroy), device); *grab_actor = NULL; } /** * clutter_input_device_get_grabbed_actor: * @device: a #ClutterInputDevice * * Retrieves a pointer to the #ClutterActor currently grabbing all * the events coming from @device. * * Return value: (transfer none): a #ClutterActor, or %NULL * * Since: 1.10 */ ClutterActor * clutter_input_device_get_grabbed_actor (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); switch (device->device_type) { case CLUTTER_POINTER_DEVICE: case CLUTTER_TABLET_DEVICE: return device->pointer_grab_actor; case CLUTTER_KEYBOARD_DEVICE: return device->keyboard_grab_actor; default: g_critical ("Only pointer and keyboard devices can grab an actor"); } return NULL; } static void on_grab_sequence_actor_destroy (ClutterActor *actor, ClutterInputDevice *device) { ClutterEventSequence *sequence = g_hash_table_lookup (device->inv_sequence_grab_actors, actor); if (sequence != NULL) { g_hash_table_remove (device->sequence_grab_actors, sequence); g_hash_table_remove (device->inv_sequence_grab_actors, actor); } } /** * clutter_input_device_sequence_grab: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence * @actor: a #ClutterActor * * Acquires a grab on @actor for the given @device and the given touch * @sequence. * * Any touch event coming from @device and from @sequence will be * delivered to @actor, bypassing the usual event delivery mechanism, * until the grab is released by calling * clutter_input_device_sequence_ungrab(). * * The grab is client-side: even if the windowing system used by the Clutter * backend has the concept of "device grabs", Clutter will not use them. * * Since: 1.12 */ void clutter_input_device_sequence_grab (ClutterInputDevice *device, ClutterEventSequence *sequence, ClutterActor *actor) { ClutterActor *grab_actor; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); if (device->sequence_grab_actors == NULL) { grab_actor = NULL; device->sequence_grab_actors = g_hash_table_new (NULL, NULL); device->inv_sequence_grab_actors = g_hash_table_new (NULL, NULL); } else { grab_actor = g_hash_table_lookup (device->sequence_grab_actors, sequence); } if (grab_actor != NULL) { g_signal_handlers_disconnect_by_func (grab_actor, G_CALLBACK (on_grab_sequence_actor_destroy), device); g_hash_table_remove (device->sequence_grab_actors, sequence); g_hash_table_remove (device->inv_sequence_grab_actors, grab_actor); } g_hash_table_insert (device->sequence_grab_actors, sequence, actor); g_hash_table_insert (device->inv_sequence_grab_actors, actor, sequence); g_signal_connect (actor, "destroy", G_CALLBACK (on_grab_sequence_actor_destroy), device); } /** * clutter_input_device_sequence_ungrab: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence * * Releases the grab on the @device for the given @sequence, if one is * in place. * * Since: 1.12 */ void clutter_input_device_sequence_ungrab (ClutterInputDevice *device, ClutterEventSequence *sequence) { ClutterActor *grab_actor; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); if (device->sequence_grab_actors == NULL) return; grab_actor = g_hash_table_lookup (device->sequence_grab_actors, sequence); if (grab_actor == NULL) return; g_signal_handlers_disconnect_by_func (grab_actor, G_CALLBACK (on_grab_sequence_actor_destroy), device); g_hash_table_remove (device->sequence_grab_actors, sequence); g_hash_table_remove (device->inv_sequence_grab_actors, grab_actor); if (g_hash_table_size (device->sequence_grab_actors) == 0) { g_hash_table_destroy (device->sequence_grab_actors); device->sequence_grab_actors = NULL; g_hash_table_destroy (device->inv_sequence_grab_actors); device->inv_sequence_grab_actors = NULL; } } /** * clutter_input_device_sequence_get_grabbed_actor: * @device: a #ClutterInputDevice * @sequence: a #ClutterEventSequence * * Retrieves a pointer to the #ClutterActor currently grabbing the * touch events coming from @device given the @sequence. * * Return value: (transfer none): a #ClutterActor, or %NULL * * Since: 1.12 */ ClutterActor * clutter_input_device_sequence_get_grabbed_actor (ClutterInputDevice *device, ClutterEventSequence *sequence) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); if (device->sequence_grab_actors == NULL) return NULL; return g_hash_table_lookup (device->sequence_grab_actors, sequence); } /** * clutter_input_device_get_vendor_id: * @device: a slave #ClutterInputDevice * * Gets the vendor ID of this device. * * Returns: the vendor ID * * Since: 1.22 */ const gchar * clutter_input_device_get_vendor_id (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); g_return_val_if_fail (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER, NULL); return device->vendor_id; } /** * clutter_input_device_get_product_id: * @device: a slave #ClutterInputDevice * * Gets the product ID of this device. * * Returns: the product ID * * Since: 1.22 */ const gchar * clutter_input_device_get_product_id (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); g_return_val_if_fail (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER, NULL); return device->product_id; } void clutter_input_device_add_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool) { g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER); g_return_if_fail (CLUTTER_IS_INPUT_DEVICE_TOOL (tool)); if (!device->tools) device->tools = g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref); g_ptr_array_add (device->tools, tool); } ClutterInputDeviceTool * clutter_input_device_lookup_tool (ClutterInputDevice *device, guint64 serial, ClutterInputDeviceToolType type) { ClutterInputDeviceTool *tool; guint i; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); g_return_val_if_fail (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER, NULL); if (!device->tools) return NULL; for (i = 0; i < device->tools->len; i++) { tool = g_ptr_array_index (device->tools, i); if (serial == clutter_input_device_tool_get_serial (tool) && type == clutter_input_device_tool_get_tool_type (tool)) return tool; } return NULL; } void clutter_input_device_update_from_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool) { ClutterInputDeviceClass *device_class; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); if (device_class->update_from_tool) device_class->update_from_tool (device, tool); } gint clutter_input_device_get_n_rings (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); return device->n_rings; } gint clutter_input_device_get_n_strips (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); return device->n_strips; } gint clutter_input_device_get_n_mode_groups (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); g_return_val_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE, 0); return device->n_mode_groups; } gint clutter_input_device_get_group_n_modes (ClutterInputDevice *device, gint group) { ClutterInputDeviceClass *device_class; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), 0); g_return_val_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE, 0); g_return_val_if_fail (group >= 0, 0); device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); if (device_class->get_group_n_modes) return device_class->get_group_n_modes (device, group); return 0; } gboolean clutter_input_device_is_mode_switch_button (ClutterInputDevice *device, guint group, guint button) { ClutterInputDeviceClass *device_class; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); g_return_val_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE, FALSE); device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); if (device_class->is_mode_switch_button) return device_class->is_mode_switch_button (device, group, button); return FALSE; } gint clutter_input_device_get_mode_switch_button_group (ClutterInputDevice *device, guint button) { gint group; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), -1); g_return_val_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE, -1); for (group = 0; group < device->n_mode_groups; group++) { if (clutter_input_device_is_mode_switch_button (device, group, button)) return group; } return -1; } const gchar * clutter_input_device_get_device_node (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); return device->node_path; } ClutterInputDeviceMapping clutter_input_device_get_mapping_mode (ClutterInputDevice *device) { ClutterInputDeviceType device_type; g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE); device_type = clutter_input_device_get_device_type (device); g_return_val_if_fail (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE, CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE); return device->mapping_mode; } void clutter_input_device_set_mapping_mode (ClutterInputDevice *device, ClutterInputDeviceMapping mapping) { ClutterInputDeviceType device_type; g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); device_type = clutter_input_device_get_device_type (device); g_return_if_fail (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE); if (device->mapping_mode == mapping) return; device->mapping_mode = mapping; g_object_notify (G_OBJECT (device), "mapping-mode"); } gboolean clutter_input_device_is_grouped (ClutterInputDevice *device, ClutterInputDevice *other_device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (other_device), FALSE); return CLUTTER_INPUT_DEVICE_GET_CLASS (device)->is_grouped (device, other_device); } /** * clutter_input_device_get_seat: * @device: a #ClutterInputDevice * * Returns the seat the device belongs to * * Returns: (transfer none): the device seat **/ ClutterSeat * clutter_input_device_get_seat (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); return device->seat; } muffin-6.4.1/clutter/clutter/clutter-box-layout.h0000664000175000017500000002011714723361714021043 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on the NBTK NbtkBoxLayout actor by: * Thomas Wood */ #ifndef __CLUTTER_BOX_LAYOUT_H__ #define __CLUTTER_BOX_LAYOUT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_BOX_LAYOUT (clutter_box_layout_get_type ()) #define CLUTTER_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayout)) #define CLUTTER_IS_BOX_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_LAYOUT)) #define CLUTTER_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass)) #define CLUTTER_IS_BOX_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BOX_LAYOUT)) #define CLUTTER_BOX_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BOX_LAYOUT, ClutterBoxLayoutClass)) typedef struct _ClutterBoxLayout ClutterBoxLayout; typedef struct _ClutterBoxLayoutPrivate ClutterBoxLayoutPrivate; typedef struct _ClutterBoxLayoutClass ClutterBoxLayoutClass; /** * ClutterBoxLayout: * * The #ClutterBoxLayout structure contains only private data * and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterBoxLayout { /*< private >*/ ClutterLayoutManager parent_instance; ClutterBoxLayoutPrivate *priv; }; /** * ClutterBoxLayoutClass: * * The #ClutterBoxLayoutClass structure contains only private * data and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterBoxLayoutClass { /*< private >*/ ClutterLayoutManagerClass parent_class; }; CLUTTER_EXPORT GType clutter_box_layout_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager * clutter_box_layout_new (void); CLUTTER_EXPORT void clutter_box_layout_set_orientation (ClutterBoxLayout *layout, ClutterOrientation orientation); CLUTTER_EXPORT ClutterOrientation clutter_box_layout_get_orientation (ClutterBoxLayout *layout); CLUTTER_EXPORT void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, guint spacing); CLUTTER_EXPORT guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout); CLUTTER_EXPORT void clutter_box_layout_set_homogeneous (ClutterBoxLayout *layout, gboolean homogeneous); CLUTTER_EXPORT gboolean clutter_box_layout_get_homogeneous (ClutterBoxLayout *layout); CLUTTER_EXPORT void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, gboolean pack_start); CLUTTER_EXPORT gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout); CLUTTER_DEPRECATED_FOR(clutter_box_layout_set_orientation) void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, gboolean vertical); CLUTTER_DEPRECATED_FOR(clutter_box_layout_get_orientation) gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout); CLUTTER_EXPORT void clutter_box_layout_pack (ClutterBoxLayout *layout, ClutterActor *actor, gboolean expand, gboolean x_fill, gboolean y_fill, ClutterBoxAlignment x_align, ClutterBoxAlignment y_align); CLUTTER_DEPRECATED void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, ClutterActor *actor, ClutterBoxAlignment x_align, ClutterBoxAlignment y_align); CLUTTER_DEPRECATED void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, ClutterActor *actor, ClutterBoxAlignment *x_align, ClutterBoxAlignment *y_align); CLUTTER_DEPRECATED void clutter_box_layout_set_fill (ClutterBoxLayout *layout, ClutterActor *actor, gboolean x_fill, gboolean y_fill); CLUTTER_DEPRECATED void clutter_box_layout_get_fill (ClutterBoxLayout *layout, ClutterActor *actor, gboolean *x_fill, gboolean *y_fill); CLUTTER_DEPRECATED void clutter_box_layout_set_expand (ClutterBoxLayout *layout, ClutterActor *actor, gboolean expand); CLUTTER_DEPRECATED gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, ClutterActor *actor); CLUTTER_DEPRECATED void clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, gboolean animate); CLUTTER_DEPRECATED gboolean clutter_box_layout_get_use_animations (ClutterBoxLayout *layout); CLUTTER_DEPRECATED void clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, gulong mode); CLUTTER_DEPRECATED gulong clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout); CLUTTER_DEPRECATED void clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, guint msecs); CLUTTER_DEPRECATED guint clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout); G_END_DECLS #endif /* __CLUTTER_BOX_LAYOUT_H__ */ muffin-6.4.1/clutter/clutter/clutter-paint-volume.c0000664000175000017500000010341614723361714021357 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see * . * * Authors: * Robert Bragg * Emmanuele Bassi */ #include "clutter-build-config.h" #include #include #include #include "clutter-actor-private.h" #include "clutter-paint-volume-private.h" #include "clutter-private.h" #include "clutter-stage-private.h" #include "clutter-actor-box-private.h" G_DEFINE_BOXED_TYPE (ClutterPaintVolume, clutter_paint_volume, clutter_paint_volume_copy, clutter_paint_volume_free); /* * _clutter_paint_volume_new: * @actor: a #ClutterActor * * Creates a new #ClutterPaintVolume for the given @actor. * * Return value: the newly allocated #ClutterPaintVolume. Use * clutter_paint_volume_free() to free the resources it uses * * Since: 1.6 */ ClutterPaintVolume * _clutter_paint_volume_new (ClutterActor *actor) { ClutterPaintVolume *pv; g_return_val_if_fail (actor != NULL, NULL); pv = g_slice_new (ClutterPaintVolume); pv->actor = actor; memset (pv->vertices, 0, 8 * sizeof (graphene_point3d_t)); pv->is_static = FALSE; pv->is_empty = TRUE; pv->is_axis_aligned = TRUE; pv->is_complete = TRUE; pv->is_2d = TRUE; return pv; } /* Since paint volumes are used so heavily in a typical paint * traversal of a Clutter scene graph and since paint volumes often * have a very short life cycle that maps well to stack allocation we * allow initializing a static ClutterPaintVolume variable to avoid * hammering the slice allocator. * * We were seeing slice allocation take about 1% cumulative CPU time * for some very simple clutter tests which although it isn't a *lot* * this is an easy way to basically drop that to 0%. * * The PaintVolume will be internally marked as static and * clutter_paint_volume_free should still be used to "free" static * volumes. This allows us to potentially store dynamically allocated * data inside paint volumes in the future since we would be able to * free it during _paint_volume_free(). */ void _clutter_paint_volume_init_static (ClutterPaintVolume *pv, ClutterActor *actor) { pv->actor = actor; memset (pv->vertices, 0, 8 * sizeof (graphene_point3d_t)); pv->is_static = TRUE; pv->is_empty = TRUE; pv->is_axis_aligned = TRUE; pv->is_complete = TRUE; pv->is_2d = TRUE; } void _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, ClutterPaintVolume *dst_pv) { g_return_if_fail (src_pv != NULL && dst_pv != NULL); memcpy (dst_pv, src_pv, sizeof (ClutterPaintVolume)); dst_pv->is_static = TRUE; } /** * clutter_paint_volume_copy: * @pv: a #ClutterPaintVolume * * Copies @pv into a new #ClutterPaintVolume * * Return value: a newly allocated copy of a #ClutterPaintVolume * * Since: 1.6 */ ClutterPaintVolume * clutter_paint_volume_copy (const ClutterPaintVolume *pv) { ClutterPaintVolume *copy; g_return_val_if_fail (pv != NULL, NULL); copy = g_slice_dup (ClutterPaintVolume, pv); copy->is_static = FALSE; return copy; } void _clutter_paint_volume_set_from_volume (ClutterPaintVolume *pv, const ClutterPaintVolume *src) { gboolean is_static = pv->is_static; memcpy (pv, src, sizeof (ClutterPaintVolume)); pv->is_static = is_static; } /** * clutter_paint_volume_free: * @pv: a #ClutterPaintVolume * * Frees the resources allocated by @pv * * Since: 1.6 */ void clutter_paint_volume_free (ClutterPaintVolume *pv) { g_return_if_fail (pv != NULL); if (G_LIKELY (pv->is_static)) return; g_slice_free (ClutterPaintVolume, pv); } /** * clutter_paint_volume_set_origin: * @pv: a #ClutterPaintVolume * @origin: a #graphene_point3d_t * * Sets the origin of the paint volume. * * The origin is defined as the X, Y and Z coordinates of the top-left * corner of an actor's paint volume, in actor coordinates. * * The default is origin is assumed at: (0, 0, 0) * * Since: 1.6 */ void clutter_paint_volume_set_origin (ClutterPaintVolume *pv, const graphene_point3d_t *origin) { static const int key_vertices[4] = { 0, 1, 3, 4 }; float dx, dy, dz; int i; g_return_if_fail (pv != NULL); dx = origin->x - pv->vertices[0].x; dy = origin->y - pv->vertices[0].y; dz = origin->z - pv->vertices[0].z; /* If we change the origin then all the key vertices of the paint * volume need to be shifted too... */ for (i = 0; i < 4; i++) { pv->vertices[key_vertices[i]].x += dx; pv->vertices[key_vertices[i]].y += dy; pv->vertices[key_vertices[i]].z += dz; } pv->is_complete = FALSE; } /** * clutter_paint_volume_get_origin: * @pv: a #ClutterPaintVolume * @vertex: (out): the return location for a #graphene_point3d_t * * Retrieves the origin of the #ClutterPaintVolume. * * Since: 1.6 */ void clutter_paint_volume_get_origin (const ClutterPaintVolume *pv, graphene_point3d_t *vertex) { g_return_if_fail (pv != NULL); g_return_if_fail (vertex != NULL); *vertex = pv->vertices[0]; } static void _clutter_paint_volume_update_is_empty (ClutterPaintVolume *pv) { if (pv->vertices[0].x == pv->vertices[1].x && pv->vertices[0].y == pv->vertices[3].y && pv->vertices[0].z == pv->vertices[4].z) pv->is_empty = TRUE; else pv->is_empty = FALSE; } /** * clutter_paint_volume_set_width: * @pv: a #ClutterPaintVolume * @width: the width of the paint volume, in pixels * * Sets the width of the paint volume. The width is measured along * the x axis in the actor coordinates that @pv is associated with. * * Since: 1.6 */ void clutter_paint_volume_set_width (ClutterPaintVolume *pv, gfloat width) { gfloat right_xpos; g_return_if_fail (pv != NULL); g_return_if_fail (width >= 0.0f); /* If the volume is currently empty then only the origin is * currently valid */ if (pv->is_empty) pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; if (!pv->is_axis_aligned) _clutter_paint_volume_axis_align (pv); right_xpos = pv->vertices[0].x + width; /* Move the right vertices of the paint box relative to the * origin... */ pv->vertices[1].x = right_xpos; /* pv->vertices[2].x = right_xpos; NB: updated lazily */ /* pv->vertices[5].x = right_xpos; NB: updated lazily */ /* pv->vertices[6].x = right_xpos; NB: updated lazily */ pv->is_complete = FALSE; _clutter_paint_volume_update_is_empty (pv); } /** * clutter_paint_volume_get_width: * @pv: a #ClutterPaintVolume * * Retrieves the width of the volume's, axis aligned, bounding box. * * In other words; this takes into account what actor's coordinate * space @pv belongs too and conceptually fits an axis aligned box * around the volume. It returns the size of that bounding box as * measured along the x-axis. * * If, for example, clutter_actor_get_transformed_paint_volume() * is used to transform a 2D child actor that is 100px wide, 100px * high and 0px deep into container coordinates then the width might * not simply be 100px if the child actor has a 3D rotation applied to * it. * * Remember: if clutter_actor_get_transformed_paint_volume() is * used then a transformed child volume will be defined relative to the * ancestor container actor and so a 2D child actor can have a 3D * bounding volume. * * There are no accuracy guarantees for the reported width, * except that it must always be greater than, or equal to, the * actor's width. This is because actors may report simple, loose * fitting paint volumes for efficiency. * Return value: the width, in units of @pv's local coordinate system. * * Since: 1.6 */ gfloat clutter_paint_volume_get_width (const ClutterPaintVolume *pv) { g_return_val_if_fail (pv != NULL, 0.0); if (pv->is_empty) return 0; else if (!pv->is_axis_aligned) { ClutterPaintVolume tmp; float width; _clutter_paint_volume_copy_static (pv, &tmp); _clutter_paint_volume_axis_align (&tmp); width = tmp.vertices[1].x - tmp.vertices[0].x; clutter_paint_volume_free (&tmp); return width; } else return pv->vertices[1].x - pv->vertices[0].x; } /** * clutter_paint_volume_set_height: * @pv: a #ClutterPaintVolume * @height: the height of the paint volume, in pixels * * Sets the height of the paint volume. The height is measured along * the y axis in the actor coordinates that @pv is associated with. * * Since: 1.6 */ void clutter_paint_volume_set_height (ClutterPaintVolume *pv, gfloat height) { gfloat height_ypos; g_return_if_fail (pv != NULL); g_return_if_fail (height >= 0.0f); /* If the volume is currently empty then only the origin is * currently valid */ if (pv->is_empty) pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; if (!pv->is_axis_aligned) _clutter_paint_volume_axis_align (pv); height_ypos = pv->vertices[0].y + height; /* Move the bottom vertices of the paint box relative to the * origin... */ /* pv->vertices[2].y = height_ypos; NB: updated lazily */ pv->vertices[3].y = height_ypos; /* pv->vertices[6].y = height_ypos; NB: updated lazily */ /* pv->vertices[7].y = height_ypos; NB: updated lazily */ pv->is_complete = FALSE; _clutter_paint_volume_update_is_empty (pv); } /** * clutter_paint_volume_get_height: * @pv: a #ClutterPaintVolume * * Retrieves the height of the volume's, axis aligned, bounding box. * * In other words; this takes into account what actor's coordinate * space @pv belongs too and conceptually fits an axis aligned box * around the volume. It returns the size of that bounding box as * measured along the y-axis. * * If, for example, clutter_actor_get_transformed_paint_volume() * is used to transform a 2D child actor that is 100px wide, 100px * high and 0px deep into container coordinates then the height might * not simply be 100px if the child actor has a 3D rotation applied to * it. * * Remember: if clutter_actor_get_transformed_paint_volume() is * used then a transformed child volume will be defined relative to the * ancestor container actor and so a 2D child actor * can have a 3D bounding volume. * * There are no accuracy guarantees for the reported height, * except that it must always be greater than, or equal to, the actor's * height. This is because actors may report simple, loose fitting paint * volumes for efficiency. * * Return value: the height, in units of @pv's local coordinate system. * * Since: 1.6 */ gfloat clutter_paint_volume_get_height (const ClutterPaintVolume *pv) { g_return_val_if_fail (pv != NULL, 0.0); if (pv->is_empty) return 0; else if (!pv->is_axis_aligned) { ClutterPaintVolume tmp; float height; _clutter_paint_volume_copy_static (pv, &tmp); _clutter_paint_volume_axis_align (&tmp); height = tmp.vertices[3].y - tmp.vertices[0].y; clutter_paint_volume_free (&tmp); return height; } else return pv->vertices[3].y - pv->vertices[0].y; } /** * clutter_paint_volume_set_depth: * @pv: a #ClutterPaintVolume * @depth: the depth of the paint volume, in pixels * * Sets the depth of the paint volume. The depth is measured along * the z axis in the actor coordinates that @pv is associated with. * * Since: 1.6 */ void clutter_paint_volume_set_depth (ClutterPaintVolume *pv, gfloat depth) { gfloat depth_zpos; g_return_if_fail (pv != NULL); g_return_if_fail (depth >= 0.0f); /* If the volume is currently empty then only the origin is * currently valid */ if (pv->is_empty) pv->vertices[1] = pv->vertices[3] = pv->vertices[4] = pv->vertices[0]; if (!pv->is_axis_aligned) _clutter_paint_volume_axis_align (pv); depth_zpos = pv->vertices[0].z + depth; /* Move the back vertices of the paint box relative to the * origin... */ pv->vertices[4].z = depth_zpos; /* pv->vertices[5].z = depth_zpos; NB: updated lazily */ /* pv->vertices[6].z = depth_zpos; NB: updated lazily */ /* pv->vertices[7].z = depth_zpos; NB: updated lazily */ pv->is_complete = FALSE; pv->is_2d = depth ? FALSE : TRUE; _clutter_paint_volume_update_is_empty (pv); } /** * clutter_paint_volume_get_depth: * @pv: a #ClutterPaintVolume * * Retrieves the depth of the volume's, axis aligned, bounding box. * * In other words; this takes into account what actor's coordinate * space @pv belongs too and conceptually fits an axis aligned box * around the volume. It returns the size of that bounding box as * measured along the z-axis. * * If, for example, clutter_actor_get_transformed_paint_volume() * is used to transform a 2D child actor that is 100px wide, 100px * high and 0px deep into container coordinates then the depth might * not simply be 0px if the child actor has a 3D rotation applied to * it. * * Remember: if clutter_actor_get_transformed_paint_volume() is * used then the transformed volume will be defined relative to the * container actor and in container coordinates a 2D child actor * can have a 3D bounding volume. * * There are no accuracy guarantees for the reported depth, * except that it must always be greater than, or equal to, the actor's * depth. This is because actors may report simple, loose fitting paint * volumes for efficiency. * * Return value: the depth, in units of @pv's local coordinate system. * * Since: 1.6 */ gfloat clutter_paint_volume_get_depth (const ClutterPaintVolume *pv) { g_return_val_if_fail (pv != NULL, 0.0); if (pv->is_empty) return 0; else if (!pv->is_axis_aligned) { ClutterPaintVolume tmp; float depth; _clutter_paint_volume_copy_static (pv, &tmp); _clutter_paint_volume_axis_align (&tmp); depth = tmp.vertices[4].z - tmp.vertices[0].z; clutter_paint_volume_free (&tmp); return depth; } else return pv->vertices[4].z - pv->vertices[0].z; } /** * clutter_paint_volume_union: * @pv: The first #ClutterPaintVolume and destination for resulting * union * @another_pv: A second #ClutterPaintVolume to union with @pv * * Updates the geometry of @pv to encompass @pv and @another_pv. * * There are no guarantees about how precisely the two volumes * will be unioned. * * Since: 1.6 */ void clutter_paint_volume_union (ClutterPaintVolume *pv, const ClutterPaintVolume *another_pv) { ClutterPaintVolume aligned_pv; g_return_if_fail (pv != NULL); g_return_if_fail (another_pv != NULL); /* Both volumes have to belong to the same local coordinate space */ g_return_if_fail (pv->actor == another_pv->actor); /* NB: we only have to update vertices 0, 1, 3 and 4 * (See the ClutterPaintVolume typedef for more details) */ /* We special case empty volumes because otherwise we'd end up * calculating a bounding box that would enclose the origin of * the empty volume which isn't desired. */ if (another_pv->is_empty) return; if (pv->is_empty) { _clutter_paint_volume_set_from_volume (pv, another_pv); goto done; } if (!pv->is_axis_aligned) _clutter_paint_volume_axis_align (pv); if (!another_pv->is_axis_aligned) { _clutter_paint_volume_copy_static (another_pv, &aligned_pv); _clutter_paint_volume_axis_align (&aligned_pv); another_pv = &aligned_pv; } /* grow left*/ /* left vertices 0, 3, 4, 7 */ if (another_pv->vertices[0].x < pv->vertices[0].x) { int min_x = another_pv->vertices[0].x; pv->vertices[0].x = min_x; pv->vertices[3].x = min_x; pv->vertices[4].x = min_x; /* pv->vertices[7].x = min_x; */ } /* grow right */ /* right vertices 1, 2, 5, 6 */ if (another_pv->vertices[1].x > pv->vertices[1].x) { int max_x = another_pv->vertices[1].x; pv->vertices[1].x = max_x; /* pv->vertices[2].x = max_x; */ /* pv->vertices[5].x = max_x; */ /* pv->vertices[6].x = max_x; */ } /* grow up */ /* top vertices 0, 1, 4, 5 */ if (another_pv->vertices[0].y < pv->vertices[0].y) { int min_y = another_pv->vertices[0].y; pv->vertices[0].y = min_y; pv->vertices[1].y = min_y; pv->vertices[4].y = min_y; /* pv->vertices[5].y = min_y; */ } /* grow down */ /* bottom vertices 2, 3, 6, 7 */ if (another_pv->vertices[3].y > pv->vertices[3].y) { int may_y = another_pv->vertices[3].y; /* pv->vertices[2].y = may_y; */ pv->vertices[3].y = may_y; /* pv->vertices[6].y = may_y; */ /* pv->vertices[7].y = may_y; */ } /* grow forward */ /* front vertices 0, 1, 2, 3 */ if (another_pv->vertices[0].z < pv->vertices[0].z) { int min_z = another_pv->vertices[0].z; pv->vertices[0].z = min_z; pv->vertices[1].z = min_z; /* pv->vertices[2].z = min_z; */ pv->vertices[3].z = min_z; } /* grow backward */ /* back vertices 4, 5, 6, 7 */ if (another_pv->vertices[4].z > pv->vertices[4].z) { int maz_z = another_pv->vertices[4].z; pv->vertices[4].z = maz_z; /* pv->vertices[5].z = maz_z; */ /* pv->vertices[6].z = maz_z; */ /* pv->vertices[7].z = maz_z; */ } if (pv->vertices[4].z == pv->vertices[0].z) pv->is_2d = TRUE; else pv->is_2d = FALSE; done: pv->is_empty = FALSE; pv->is_complete = FALSE; } /** * clutter_paint_volume_union_box: * @pv: a #ClutterPaintVolume * @box: a #ClutterActorBox to union to @pv * * Unions the 2D region represented by @box to a #ClutterPaintVolume. * * This function is similar to clutter_paint_volume_union(), but it is * specific for 2D regions. * * Since: 1.10 */ void clutter_paint_volume_union_box (ClutterPaintVolume *pv, const ClutterActorBox *box) { ClutterPaintVolume volume; graphene_point3d_t origin; g_return_if_fail (pv != NULL); g_return_if_fail (box != NULL); _clutter_paint_volume_init_static (&volume, pv->actor); origin.x = box->x1; origin.y = box->y1; origin.z = 0.f; clutter_paint_volume_set_origin (&volume, &origin); clutter_paint_volume_set_width (&volume, box->x2 - box->x1); clutter_paint_volume_set_height (&volume, box->y2 - box->y1); clutter_paint_volume_union (pv, &volume); clutter_paint_volume_free (&volume); } /* The paint_volume setters only update vertices 0, 1, 3 and * 4 since the others can be drived from them. * * This will set pv->completed = TRUE; */ void _clutter_paint_volume_complete (ClutterPaintVolume *pv) { float dx_l2r, dy_l2r, dz_l2r; float dx_t2b, dy_t2b, dz_t2b; if (pv->is_empty) return; if (pv->is_complete) return; /* Find the vector that takes us from any vertex on the left face to * the corresponding vertex on the right face. */ dx_l2r = pv->vertices[1].x - pv->vertices[0].x; dy_l2r = pv->vertices[1].y - pv->vertices[0].y; dz_l2r = pv->vertices[1].z - pv->vertices[0].z; /* Find the vector that takes us from any vertex on the top face to * the corresponding vertex on the bottom face. */ dx_t2b = pv->vertices[3].x - pv->vertices[0].x; dy_t2b = pv->vertices[3].y - pv->vertices[0].y; dz_t2b = pv->vertices[3].z - pv->vertices[0].z; /* front-bottom-right */ pv->vertices[2].x = pv->vertices[3].x + dx_l2r; pv->vertices[2].y = pv->vertices[3].y + dy_l2r; pv->vertices[2].z = pv->vertices[3].z + dz_l2r; if (G_UNLIKELY (!pv->is_2d)) { /* back-top-right */ pv->vertices[5].x = pv->vertices[4].x + dx_l2r; pv->vertices[5].y = pv->vertices[4].y + dy_l2r; pv->vertices[5].z = pv->vertices[4].z + dz_l2r; /* back-bottom-right */ pv->vertices[6].x = pv->vertices[5].x + dx_t2b; pv->vertices[6].y = pv->vertices[5].y + dy_t2b; pv->vertices[6].z = pv->vertices[5].z + dz_t2b; /* back-bottom-left */ pv->vertices[7].x = pv->vertices[4].x + dx_t2b; pv->vertices[7].y = pv->vertices[4].y + dy_t2b; pv->vertices[7].z = pv->vertices[4].z + dz_t2b; } pv->is_complete = TRUE; } /* * _clutter_paint_volume_get_box: * @pv: a #ClutterPaintVolume * @box: a pixel aligned #ClutterActorBox * * Transforms a 3D paint volume into a 2D bounding box in the * same coordinate space as the 3D paint volume. * * To get an actors "paint box" you should first project * the paint volume into window coordinates before getting * the 2D bounding box. * * The coordinates of the returned box are not clamped to * integer pixel values; if you need them to be rounded to the * nearest integer pixel values, you can use the * clutter_actor_box_clamp_to_pixel() function. * * Since: 1.6 */ void _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, ClutterActorBox *box) { gfloat x_min, y_min, x_max, y_max; graphene_point3d_t *vertices; int count; gint i; g_return_if_fail (pv != NULL); g_return_if_fail (box != NULL); if (pv->is_empty) { box->x1 = box->x2 = pv->vertices[0].x; box->y1 = box->y2 = pv->vertices[0].y; return; } /* Updates the vertices we calculate lazily * (See ClutterPaintVolume typedef for more details) */ _clutter_paint_volume_complete (pv); vertices = pv->vertices; x_min = x_max = vertices[0].x; y_min = y_max = vertices[0].y; /* Most actors are 2D so we only have to look at the front 4 * vertices of the paint volume... */ if (G_LIKELY (pv->is_2d)) count = 4; else count = 8; for (i = 1; i < count; i++) { if (vertices[i].x < x_min) x_min = vertices[i].x; else if (vertices[i].x > x_max) x_max = vertices[i].x; if (vertices[i].y < y_min) y_min = vertices[i].y; else if (vertices[i].y > y_max) y_max = vertices[i].y; } box->x1 = x_min; box->y1 = y_min; box->x2 = x_max; box->y2 = y_max; } void _clutter_paint_volume_project (ClutterPaintVolume *pv, const CoglMatrix *modelview, const CoglMatrix *projection, const float *viewport) { int transform_count; if (pv->is_empty) { /* Just transform the origin... */ _clutter_util_fully_transform_vertices (modelview, projection, viewport, pv->vertices, pv->vertices, 1); return; } /* All the vertices must be up to date, since after the projection * it wont be trivial to derive the other vertices. */ _clutter_paint_volume_complete (pv); /* Most actors are 2D so we only have to transform the front 4 * vertices of the paint volume... */ if (G_LIKELY (pv->is_2d)) transform_count = 4; else transform_count = 8; _clutter_util_fully_transform_vertices (modelview, projection, viewport, pv->vertices, pv->vertices, transform_count); pv->is_axis_aligned = FALSE; } void _clutter_paint_volume_transform (ClutterPaintVolume *pv, const CoglMatrix *matrix) { int transform_count; if (pv->is_empty) { gfloat w = 1; /* Just transform the origin */ cogl_matrix_transform_point (matrix, &pv->vertices[0].x, &pv->vertices[0].y, &pv->vertices[0].z, &w); return; } /* All the vertices must be up to date, since after the transform * it wont be trivial to derive the other vertices. */ _clutter_paint_volume_complete (pv); /* Most actors are 2D so we only have to transform the front 4 * vertices of the paint volume... */ if (G_LIKELY (pv->is_2d)) transform_count = 4; else transform_count = 8; cogl_matrix_transform_points (matrix, 3, sizeof (graphene_point3d_t), pv->vertices, sizeof (graphene_point3d_t), pv->vertices, transform_count); pv->is_axis_aligned = FALSE; } /* Given a paint volume that has been transformed by an arbitrary * modelview and is no longer axis aligned, this derives a replacement * that is axis aligned. */ void _clutter_paint_volume_axis_align (ClutterPaintVolume *pv) { int count; int i; graphene_point3d_t origin; float max_x; float max_y; float max_z; g_return_if_fail (pv != NULL); if (pv->is_empty) return; if (G_LIKELY (pv->is_axis_aligned)) return; if (G_LIKELY (pv->vertices[0].x == pv->vertices[1].x && pv->vertices[0].y == pv->vertices[3].y && pv->vertices[0].z == pv->vertices[4].z)) { pv->is_axis_aligned = TRUE; return; } if (!pv->is_complete) _clutter_paint_volume_complete (pv); origin = pv->vertices[0]; max_x = pv->vertices[0].x; max_y = pv->vertices[0].y; max_z = pv->vertices[0].z; count = pv->is_2d ? 4 : 8; for (i = 1; i < count; i++) { if (pv->vertices[i].x < origin.x) origin.x = pv->vertices[i].x; else if (pv->vertices[i].x > max_x) max_x = pv->vertices[i].x; if (pv->vertices[i].y < origin.y) origin.y = pv->vertices[i].y; else if (pv->vertices[i].y > max_y) max_y = pv->vertices[i].y; if (pv->vertices[i].z < origin.z) origin.z = pv->vertices[i].z; else if (pv->vertices[i].z > max_z) max_z = pv->vertices[i].z; } pv->vertices[0] = origin; pv->vertices[1].x = max_x; pv->vertices[1].y = origin.y; pv->vertices[1].z = origin.z; pv->vertices[3].x = origin.x; pv->vertices[3].y = max_y; pv->vertices[3].z = origin.z; pv->vertices[4].x = origin.x; pv->vertices[4].y = origin.y; pv->vertices[4].z = max_z; pv->is_complete = FALSE; pv->is_axis_aligned = TRUE; if (pv->vertices[4].z == pv->vertices[0].z) pv->is_2d = TRUE; else pv->is_2d = FALSE; } /* * _clutter_actor_set_default_paint_volume: * @self: a #ClutterActor * @check_gtype: if not %G_TYPE_INVALID, match the type of @self against * this type * @volume: the #ClutterPaintVolume to set * * Sets the default paint volume for @self. * * This function should be called by #ClutterActor sub-classes that follow * the default assumption that their paint volume is defined by their * allocation. * * If @check_gtype is not %G_TYPE_INVALID, this function will check the * type of @self and only compute the paint volume if the type matches; * this can be used to avoid computing the paint volume for sub-classes * of an actor class * * Return value: %TRUE if the paint volume was set, and %FALSE otherwise */ gboolean _clutter_actor_set_default_paint_volume (ClutterActor *self, GType check_gtype, ClutterPaintVolume *volume) { ClutterActorBox box; if (check_gtype != G_TYPE_INVALID) { if (G_OBJECT_TYPE (self) != check_gtype) return FALSE; } /* calling clutter_actor_get_allocation_* can potentially be very * expensive, as it can result in a synchronous full stage relayout * and redraw */ if (!clutter_actor_has_allocation (self)) return FALSE; clutter_actor_get_allocation_box (self, &box); /* we only set the width and height, as the paint volume is defined * to be relative to the actor's modelview, which means that the * allocation's origin has already been applied */ clutter_paint_volume_set_width (volume, box.x2 - box.x1); clutter_paint_volume_set_height (volume, box.y2 - box.y1); return TRUE; } /** * clutter_paint_volume_set_from_allocation: * @pv: a #ClutterPaintVolume * @actor: a #ClutterActor * * Sets the #ClutterPaintVolume from the allocation of @actor. * * This function should be used when overriding the * #ClutterActorClass.get_paint_volume() by #ClutterActor sub-classes * that do not paint outside their allocation. * * A typical example is: * * |[ * static gboolean * my_actor_get_paint_volume (ClutterActor *self, * ClutterPaintVolume *volume) * { * return clutter_paint_volume_set_from_allocation (volume, self); * } * ]| * * Return value: %TRUE if the paint volume was successfully set, and %FALSE * otherwise * * Since: 1.6 */ gboolean clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv, ClutterActor *actor) { g_return_val_if_fail (pv != NULL, FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); return _clutter_actor_set_default_paint_volume (actor, G_TYPE_INVALID, pv); } /* Currently paint volumes are defined relative to a given actor, but * in some cases it is desireable to be able to change the actor that * a volume relates too (For instance for ClutterClone actors where we * need to masquarade the source actors volume as the volume for the * clone). */ void _clutter_paint_volume_set_reference_actor (ClutterPaintVolume *pv, ClutterActor *actor) { g_return_if_fail (pv != NULL); pv->actor = actor; } ClutterCullResult _clutter_paint_volume_cull (ClutterPaintVolume *pv, const ClutterPlane *planes) { int vertex_count; graphene_point3d_t *vertices = pv->vertices; gboolean partial = FALSE; int i; int j; if (pv->is_empty) return CLUTTER_CULL_RESULT_OUT; /* We expect the volume to already be transformed into eye coordinates */ g_return_val_if_fail (pv->is_complete == TRUE, CLUTTER_CULL_RESULT_IN); g_return_val_if_fail (pv->actor == NULL, CLUTTER_CULL_RESULT_IN); /* Most actors are 2D so we only have to transform the front 4 * vertices of the paint volume... */ if (G_LIKELY (pv->is_2d)) vertex_count = 4; else vertex_count = 8; for (i = 0; i < 4; i++) { const ClutterPlane *plane = &planes[i]; int out = 0; for (j = 0; j < vertex_count; j++) { graphene_vec3_t v; graphene_vec3_init (&v, vertices[j].x - graphene_vec3_get_x (&plane->v0), vertices[j].y - graphene_vec3_get_y (&plane->v0), vertices[j].z - graphene_vec3_get_z (&plane->v0)); if (graphene_vec3_dot (&plane->n, &v) < 0) out++; } if (out == vertex_count) return CLUTTER_CULL_RESULT_OUT; else if (out != 0) partial = TRUE; } if (partial) return CLUTTER_CULL_RESULT_PARTIAL; else return CLUTTER_CULL_RESULT_IN; } void _clutter_paint_volume_get_stage_paint_box (ClutterPaintVolume *pv, ClutterStage *stage, ClutterActorBox *box) { ClutterPaintVolume projected_pv; CoglMatrix modelview; CoglMatrix projection; float viewport[4]; _clutter_paint_volume_copy_static (pv, &projected_pv); cogl_matrix_init_identity (&modelview); /* If the paint volume isn't already in eye coordinates... */ if (pv->actor) _clutter_actor_apply_relative_transformation_matrix (pv->actor, NULL, &modelview); _clutter_stage_get_projection_matrix (stage, &projection); _clutter_stage_get_viewport (stage, &viewport[0], &viewport[1], &viewport[2], &viewport[3]); _clutter_paint_volume_project (&projected_pv, &modelview, &projection, viewport); _clutter_paint_volume_get_bounding_box (&projected_pv, box); if (pv->is_2d && pv->actor && clutter_actor_get_z_position (pv->actor) == 0) { /* If the volume/actor are perfectly 2D, take the bounding box as * good. We won't need to add any extra room for sub-pixel positioning * in this case. */ clutter_paint_volume_free (&projected_pv); box->x1 = CLUTTER_NEARBYINT (box->x1); box->y1 = CLUTTER_NEARBYINT (box->y1); box->x2 = CLUTTER_NEARBYINT (box->x2); box->y2 = CLUTTER_NEARBYINT (box->y2); return; } _clutter_actor_box_enlarge_for_effects (box); clutter_paint_volume_free (&projected_pv); } void _clutter_paint_volume_transform_relative (ClutterPaintVolume *pv, ClutterActor *relative_to_ancestor) { CoglMatrix matrix; ClutterActor *actor; actor = pv->actor; g_return_if_fail (actor != NULL); _clutter_paint_volume_set_reference_actor (pv, relative_to_ancestor); cogl_matrix_init_identity (&matrix); _clutter_actor_apply_relative_transformation_matrix (actor, relative_to_ancestor, &matrix); _clutter_paint_volume_transform (pv, &matrix); } muffin-6.4.1/clutter/clutter/clutter-timeline.c0000664000175000017500000021564014723361714020550 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-timeline * @short_description: A class for time-based events * @see_also: #ClutterAnimation, #ClutterAnimator, #ClutterState * * #ClutterTimeline is a base class for managing time-based event that cause * Clutter to redraw a stage, such as animations. * * Each #ClutterTimeline instance has a duration: once a timeline has been * started, using clutter_timeline_start(), it will emit a signal that can * be used to update the state of the actors. * * It is important to note that #ClutterTimeline is not a generic API for * calling closures after an interval; each Timeline is tied into the master * clock used to drive the frame cycle. If you need to schedule a closure * after an interval, see clutter_threads_add_timeout() instead. * * Users of #ClutterTimeline should connect to the #ClutterTimeline::new-frame * signal, which is emitted each time a timeline is advanced during the maste * clock iteration. The #ClutterTimeline::new-frame signal provides the time * elapsed since the beginning of the timeline, in milliseconds. A normalized * progress value can be obtained by calling clutter_timeline_get_progress(). * By using clutter_timeline_get_delta() it is possible to obtain the wallclock * time elapsed since the last emission of the #ClutterTimeline::new-frame * signal. * * Initial state can be set up by using the #ClutterTimeline::started signal, * while final state can be set up by using the #ClutterTimeline::stopped * signal. The #ClutterTimeline guarantees the emission of at least a single * #ClutterTimeline::new-frame signal, as well as the emission of the * #ClutterTimeline::completed signal every time the #ClutterTimeline reaches * its #ClutterTimeline:duration. * * It is possible to connect to specific points in the timeline progress by * adding markers using clutter_timeline_add_marker_at_time() and connecting * to the #ClutterTimeline::marker-reached signal. * * Timelines can be made to loop once they reach the end of their duration, by * using clutter_timeline_set_repeat_count(); a looping timeline will still * emit the #ClutterTimeline::completed signal once it reaches the end of its * duration at each repeat. If you want to be notified of the end of the last * repeat, use the #ClutterTimeline::stopped signal. * * Timelines have a #ClutterTimeline:direction: the default direction is * %CLUTTER_TIMELINE_FORWARD, and goes from 0 to the duration; it is possible * to change the direction to %CLUTTER_TIMELINE_BACKWARD, and have the timeline * go from the duration to 0. The direction can be automatically reversed * when reaching completion by using the #ClutterTimeline:auto-reverse property. * * Timelines are used in the Clutter animation framework by classes like * #ClutterAnimation, #ClutterAnimator, and #ClutterState. * * ## Defining Timelines in ClutterScript * * A #ClutterTimeline can be described in #ClutterScript like any * other object. Additionally, it is possible to define markers directly * inside the JSON definition by using the `markers` JSON object member, * such as: * * |[ { "type" : "ClutterTimeline", "duration" : 1000, "markers" : [ { "name" : "quarter", "time" : 250 }, { "name" : "half-time", "time" : 500 }, { "name" : "three-quarters", "time" : 750 } ] } * ]| */ #include "clutter-build-config.h" #include "clutter-timeline.h" #include "clutter-debug.h" #include "clutter-easing.h" #include "clutter-enum-types.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-master-clock.h" #include "clutter-private.h" #include "clutter-scriptable.h" #include "deprecated/clutter-timeline.h" struct _ClutterTimelinePrivate { ClutterTimelineDirection direction; guint delay_id; /* The total length in milliseconds of this timeline */ guint duration; guint delay; /* The current amount of elapsed time */ gint64 elapsed_time; /* The elapsed time since the last frame was fired */ gint64 msecs_delta; GHashTable *markers_by_name; /* Time we last advanced the elapsed time and showed a frame */ gint64 last_frame_time; /* How many times the timeline should repeat */ gint repeat_count; /* The number of times the timeline has repeated */ gint current_repeat; ClutterTimelineProgressFunc progress_func; gpointer progress_data; GDestroyNotify progress_notify; ClutterAnimationMode progress_mode; /* step() parameters */ gint n_steps; ClutterStepMode step_mode; /* cubic-bezier() parameters */ graphene_point_t cb_1; graphene_point_t cb_2; guint is_playing : 1; /* If we've just started playing and haven't yet gotten * a tick from the master clock */ guint waiting_first_tick : 1; guint auto_reverse : 1; }; typedef struct { gchar *name; GQuark quark; union { guint msecs; gdouble progress; } data; guint is_relative : 1; } TimelineMarker; enum { PROP_0, PROP_LOOP, PROP_DELAY, PROP_DURATION, PROP_DIRECTION, PROP_AUTO_REVERSE, PROP_REPEAT_COUNT, PROP_PROGRESS_MODE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; enum { NEW_FRAME, STARTED, PAUSED, COMPLETED, MARKER_REACHED, STOPPED, LAST_SIGNAL }; static guint timeline_signals[LAST_SIGNAL] = { 0, }; static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterTimeline, clutter_timeline, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterTimeline) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init)) static TimelineMarker * timeline_marker_new_time (const gchar *name, guint msecs) { TimelineMarker *marker = g_slice_new (TimelineMarker); marker->name = g_strdup (name); marker->quark = g_quark_from_string (marker->name); marker->is_relative = FALSE; marker->data.msecs = msecs; return marker; } static TimelineMarker * timeline_marker_new_progress (const gchar *name, gdouble progress) { TimelineMarker *marker = g_slice_new (TimelineMarker); marker->name = g_strdup (name); marker->quark = g_quark_from_string (marker->name); marker->is_relative = TRUE; marker->data.progress = CLAMP (progress, 0.0, 1.0); return marker; } static void timeline_marker_free (gpointer data) { if (G_LIKELY (data)) { TimelineMarker *marker = data; g_free (marker->name); g_slice_free (TimelineMarker, marker); } } /*< private > * clutter_timeline_add_marker_internal: * @timeline: a #ClutterTimeline * @marker: a TimelineMarker * * Adds @marker into the hash table of markers for @timeline. * * The TimelineMarker will either be added or, in case of collisions * with another existing marker, freed. In any case, this function * assumes the ownership of the passed @marker. */ static inline void clutter_timeline_add_marker_internal (ClutterTimeline *timeline, TimelineMarker *marker) { ClutterTimelinePrivate *priv = timeline->priv; TimelineMarker *old_marker; /* create the hash table that will hold the markers */ if (G_UNLIKELY (priv->markers_by_name == NULL)) priv->markers_by_name = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, timeline_marker_free); old_marker = g_hash_table_lookup (priv->markers_by_name, marker->name); if (old_marker != NULL) { guint msecs; if (old_marker->is_relative) msecs = old_marker->data.progress * priv->duration; else msecs = old_marker->data.msecs; g_warning ("A marker named '%s' already exists at time %d", old_marker->name, msecs); timeline_marker_free (marker); return; } g_hash_table_insert (priv->markers_by_name, marker->name, marker); } static inline void clutter_timeline_set_loop_internal (ClutterTimeline *timeline, gboolean loop) { gint old_repeat_count; old_repeat_count = timeline->priv->repeat_count; if (loop) clutter_timeline_set_repeat_count (timeline, -1); else clutter_timeline_set_repeat_count (timeline, 0); if (old_repeat_count != timeline->priv->repeat_count) g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_LOOP]); } /* Scriptable */ typedef struct _ParseClosure { ClutterTimeline *timeline; ClutterScript *script; GValue *value; gboolean result; } ParseClosure; static void parse_timeline_markers (JsonArray *array, guint index_, JsonNode *element, gpointer data) { ParseClosure *clos = data; JsonObject *object; TimelineMarker *marker; GList *markers; if (JSON_NODE_TYPE (element) != JSON_NODE_OBJECT) { g_warning ("The 'markers' member of a ClutterTimeline description " "should be an array of objects, but the element %d of the " "array is of type '%s'. The element will be ignored.", index_, json_node_type_name (element)); return; } object = json_node_get_object (element); if (!(json_object_has_member (object, "name") && (json_object_has_member (object, "time") || json_object_has_member (object, "progress")))) { g_warning ("The marker definition in a ClutterTimeline description " "must be an object with the 'name' and either the 'time' " "or the 'progress' members, but the element %d of the " "'markers' array does not have any of them.", index_); return; } if (G_IS_VALUE (clos->value)) markers = g_value_get_pointer (clos->value); else { g_value_init (clos->value, G_TYPE_POINTER); markers = NULL; } if (json_object_has_member (object, "time")) marker = timeline_marker_new_time (json_object_get_string_member (object, "name"), json_object_get_int_member (object, "time")); else marker = timeline_marker_new_progress (json_object_get_string_member (object, "name"), json_object_get_double_member (object, "progress")); markers = g_list_prepend (markers, marker); g_value_set_pointer (clos->value, markers); clos->result = TRUE; } static gboolean clutter_timeline_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { ParseClosure clos; if (strcmp (name, "markers") != 0) return FALSE; if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) return FALSE; clos.timeline = CLUTTER_TIMELINE (scriptable); clos.script = script; clos.value = value; clos.result = FALSE; json_array_foreach_element (json_node_get_array (node), parse_timeline_markers, &clos); return clos.result; } static void clutter_timeline_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { if (strcmp (name, "markers") == 0) { ClutterTimeline *timeline = CLUTTER_TIMELINE (scriptable); GList *markers = g_value_get_pointer (value); GList *m; /* the list was created through prepend() */ markers = g_list_reverse (markers); for (m = markers; m != NULL; m = m->next) clutter_timeline_add_marker_internal (timeline, m->data); g_list_free (markers); } else g_object_set_property (G_OBJECT (scriptable), name, value); } void clutter_timeline_cancel_delay (ClutterTimeline *timeline) { g_clear_handle_id (&timeline->priv->delay_id, g_source_remove); } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { iface->parse_custom_node = clutter_timeline_parse_custom_node; iface->set_custom_property = clutter_timeline_set_custom_property; } /* Object */ static void clutter_timeline_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterTimeline *timeline = CLUTTER_TIMELINE (object); switch (prop_id) { case PROP_LOOP: clutter_timeline_set_loop_internal (timeline, g_value_get_boolean (value)); break; case PROP_DELAY: clutter_timeline_set_delay (timeline, g_value_get_uint (value)); break; case PROP_DURATION: clutter_timeline_set_duration (timeline, g_value_get_uint (value)); break; case PROP_DIRECTION: clutter_timeline_set_direction (timeline, g_value_get_enum (value)); break; case PROP_AUTO_REVERSE: clutter_timeline_set_auto_reverse (timeline, g_value_get_boolean (value)); break; case PROP_REPEAT_COUNT: clutter_timeline_set_repeat_count (timeline, g_value_get_int (value)); break; case PROP_PROGRESS_MODE: clutter_timeline_set_progress_mode (timeline, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_timeline_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterTimeline *timeline = CLUTTER_TIMELINE (object); ClutterTimelinePrivate *priv = timeline->priv; switch (prop_id) { case PROP_LOOP: g_value_set_boolean (value, priv->repeat_count != 0); break; case PROP_DELAY: g_value_set_uint (value, priv->delay); break; case PROP_DURATION: g_value_set_uint (value, clutter_timeline_get_duration (timeline)); break; case PROP_DIRECTION: g_value_set_enum (value, priv->direction); break; case PROP_AUTO_REVERSE: g_value_set_boolean (value, priv->auto_reverse); break; case PROP_REPEAT_COUNT: g_value_set_int (value, priv->repeat_count); break; case PROP_PROGRESS_MODE: g_value_set_enum (value, priv->progress_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_timeline_finalize (GObject *object) { ClutterTimeline *self = CLUTTER_TIMELINE (object); ClutterTimelinePrivate *priv = self->priv; ClutterMasterClock *master_clock; if (priv->markers_by_name) g_hash_table_destroy (priv->markers_by_name); if (priv->is_playing) { master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_remove_timeline (master_clock, self); } G_OBJECT_CLASS (clutter_timeline_parent_class)->finalize (object); } static void clutter_timeline_dispose (GObject *object) { ClutterTimeline *self = CLUTTER_TIMELINE(object); ClutterTimelinePrivate *priv; priv = self->priv; clutter_timeline_cancel_delay (self); if (priv->progress_notify != NULL) { priv->progress_notify (priv->progress_data); priv->progress_func = NULL; priv->progress_data = NULL; priv->progress_notify = NULL; } G_OBJECT_CLASS (clutter_timeline_parent_class)->dispose (object); } static void clutter_timeline_class_init (ClutterTimelineClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); /** * ClutterTimeline:loop: * * Whether the timeline should automatically rewind and restart. * * As a side effect, setting this property to %TRUE will set the * #ClutterTimeline:repeat-count property to -1, while setting this * property to %FALSE will set the #ClutterTimeline:repeat-count * property to 0. * * Deprecated: 1.10: Use the #ClutterTimeline:repeat-count property instead. */ obj_props[PROP_LOOP] = g_param_spec_boolean ("loop", P_("Loop"), P_("Should the timeline automatically restart"), FALSE, CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED); /** * ClutterTimeline:delay: * * A delay, in milliseconds, that should be observed by the * timeline before actually starting. * * Since: 0.4 */ obj_props[PROP_DELAY] = g_param_spec_uint ("delay", P_("Delay"), P_("Delay before start"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterTimeline:duration: * * Duration of the timeline in milliseconds, depending on the * ClutterTimeline:fps value. * * Since: 0.6 */ obj_props[PROP_DURATION] = g_param_spec_uint ("duration", P_("Duration"), P_("Duration of the timeline in milliseconds"), 0, G_MAXUINT, 1000, CLUTTER_PARAM_READWRITE); /** * ClutterTimeline:direction: * * The direction of the timeline, either %CLUTTER_TIMELINE_FORWARD or * %CLUTTER_TIMELINE_BACKWARD. * * Since: 0.6 */ obj_props[PROP_DIRECTION] = g_param_spec_enum ("direction", P_("Direction"), P_("Direction of the timeline"), CLUTTER_TYPE_TIMELINE_DIRECTION, CLUTTER_TIMELINE_FORWARD, CLUTTER_PARAM_READWRITE); /** * ClutterTimeline:auto-reverse: * * If the direction of the timeline should be automatically reversed * when reaching the end. * * Since: 1.6 */ obj_props[PROP_AUTO_REVERSE] = g_param_spec_boolean ("auto-reverse", P_("Auto Reverse"), P_("Whether the direction should be reversed when reaching the end"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterTimeline:repeat-count: * * Defines how many times the timeline should repeat. * * If the repeat count is 0, the timeline does not repeat. * * If the repeat count is set to -1, the timeline will repeat until it is * stopped. * * Since: 1.10 */ obj_props[PROP_REPEAT_COUNT] = g_param_spec_int ("repeat-count", P_("Repeat Count"), P_("How many times the timeline should repeat"), -1, G_MAXINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterTimeline:progress-mode: * * Controls the way a #ClutterTimeline computes the normalized progress. * * Since: 1.10 */ obj_props[PROP_PROGRESS_MODE] = g_param_spec_enum ("progress-mode", P_("Progress Mode"), P_("How the timeline should compute the progress"), CLUTTER_TYPE_ANIMATION_MODE, CLUTTER_LINEAR, CLUTTER_PARAM_READWRITE); object_class->dispose = clutter_timeline_dispose; object_class->finalize = clutter_timeline_finalize; object_class->set_property = clutter_timeline_set_property; object_class->get_property = clutter_timeline_get_property; g_object_class_install_properties (object_class, PROP_LAST, obj_props); /** * ClutterTimeline::new-frame: * @timeline: the timeline which received the signal * @msecs: the elapsed time between 0 and duration * * The ::new-frame signal is emitted for each timeline running * timeline before a new frame is drawn to give animations a chance * to update the scene. */ timeline_signals[NEW_FRAME] = g_signal_new (I_("new-frame"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTimelineClass, new_frame), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); /** * ClutterTimeline::completed: * @timeline: the #ClutterTimeline which received the signal * * The #ClutterTimeline::completed signal is emitted when the timeline's * elapsed time reaches the value of the #ClutterTimeline:duration * property. * * This signal will be emitted even if the #ClutterTimeline is set to be * repeating. * * If you want to get notification on whether the #ClutterTimeline has * been stopped or has finished its run, including its eventual repeats, * you should use the #ClutterTimeline::stopped signal instead. */ timeline_signals[COMPLETED] = g_signal_new (I_("completed"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTimelineClass, completed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterTimeline::started: * @timeline: the #ClutterTimeline which received the signal * * The ::started signal is emitted when the timeline starts its run. * This might be as soon as clutter_timeline_start() is invoked or * after the delay set in the ClutterTimeline:delay property has * expired. */ timeline_signals[STARTED] = g_signal_new (I_("started"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTimelineClass, started), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterTimeline::paused: * @timeline: the #ClutterTimeline which received the signal * * The ::paused signal is emitted when clutter_timeline_pause() is invoked. */ timeline_signals[PAUSED] = g_signal_new (I_("paused"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTimelineClass, paused), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterTimeline::marker-reached: * @timeline: the #ClutterTimeline which received the signal * @marker_name: the name of the marker reached * @msecs: the elapsed time * * The ::marker-reached signal is emitted each time a timeline * reaches a marker set with * clutter_timeline_add_marker_at_time(). This signal is detailed * with the name of the marker as well, so it is possible to connect * a callback to the ::marker-reached signal for a specific marker * with: * * * clutter_timeline_add_marker_at_time (timeline, "foo", 500); * clutter_timeline_add_marker_at_time (timeline, "bar", 750); * * g_signal_connect (timeline, "marker-reached", * G_CALLBACK (each_marker_reached), NULL); * g_signal_connect (timeline, "marker-reached::foo", * G_CALLBACK (foo_marker_reached), NULL); * g_signal_connect (timeline, "marker-reached::bar", * G_CALLBACK (bar_marker_reached), NULL); * * * In the example, the first callback will be invoked for both * the "foo" and "bar" marker, while the second and third callbacks * will be invoked for the "foo" or "bar" markers, respectively. * * Since: 0.8 */ timeline_signals[MARKER_REACHED] = g_signal_new (I_("marker-reached"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_DETAILED | G_SIGNAL_NO_HOOKS, G_STRUCT_OFFSET (ClutterTimelineClass, marker_reached), NULL, NULL, _clutter_marshal_VOID__STRING_INT, G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT); /** * ClutterTimeline::stopped: * @timeline: the #ClutterTimeline that emitted the signal * @is_finished: %TRUE if the signal was emitted at the end of the * timeline. * * The #ClutterTimeline::stopped signal is emitted when the timeline * has been stopped, either because clutter_timeline_stop() has been * called, or because it has been exhausted. * * This is different from the #ClutterTimeline::completed signal, * which gets emitted after every repeat finishes. * * If the #ClutterTimeline has is marked as infinitely repeating, * this signal will never be emitted. * * Since: 1.12 */ timeline_signals[STOPPED] = g_signal_new (I_("stopped"), G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTimelineClass, stopped), NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } static void clutter_timeline_init (ClutterTimeline *self) { self->priv = clutter_timeline_get_instance_private (self); self->priv->progress_mode = CLUTTER_LINEAR; /* default steps() parameters are 1, end */ self->priv->n_steps = 1; self->priv->step_mode = CLUTTER_STEP_MODE_END; /* default cubic-bezier() paramereters are (0, 0, 1, 1) */ graphene_point_init (&self->priv->cb_1, 0, 0); graphene_point_init (&self->priv->cb_2, 1, 1); } struct CheckIfMarkerHitClosure { ClutterTimeline *timeline; ClutterTimelineDirection direction; gint new_time; gint duration; gint delta; }; static gboolean have_passed_time (const struct CheckIfMarkerHitClosure *data, gint msecs) { /* Ignore markers that are outside the duration of the timeline */ if (msecs < 0 || msecs > data->duration) return FALSE; if (data->direction == CLUTTER_TIMELINE_FORWARD) { /* We need to special case when a marker is added at the beginning of the timeline */ if (msecs == 0 && data->delta > 0 && data->new_time - data->delta <= 0) return TRUE; /* Otherwise it's just a simple test if the time is in range of the previous time and the new time */ return (msecs > data->new_time - data->delta && msecs <= data->new_time); } else { /* We need to special case when a marker is added at the end of the timeline */ if (msecs == data->duration && data->delta > 0 && data->new_time + data->delta >= data->duration) return TRUE; /* Otherwise it's just a simple test if the time is in range of the previous time and the new time */ return (msecs >= data->new_time && msecs < data->new_time + data->delta); } } static void check_if_marker_hit (const gchar *name, TimelineMarker *marker, struct CheckIfMarkerHitClosure *data) { gint msecs; if (marker->is_relative) msecs = (gdouble) data->duration * marker->data.progress; else msecs = marker->data.msecs; if (have_passed_time (data, msecs)) { CLUTTER_NOTE (SCHEDULER, "Marker '%s' reached", name); g_signal_emit (data->timeline, timeline_signals[MARKER_REACHED], marker->quark, name, msecs); } } static void check_markers (ClutterTimeline *timeline, gint delta) { ClutterTimelinePrivate *priv = timeline->priv; struct CheckIfMarkerHitClosure data; /* shortcircuit here if we don't have any marker installed */ if (priv->markers_by_name == NULL) return; /* store the details of the timeline so that changing them in a marker signal handler won't affect which markers are hit */ data.timeline = timeline; data.direction = priv->direction; data.new_time = priv->elapsed_time; data.duration = priv->duration; data.delta = delta; g_hash_table_foreach (priv->markers_by_name, (GHFunc) check_if_marker_hit, &data); } static void emit_frame_signal (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv = timeline->priv; /* see bug https://bugzilla.gnome.org/show_bug.cgi?id=654066 */ gint elapsed = (gint) priv->elapsed_time; CLUTTER_NOTE (SCHEDULER, "Emitting ::new-frame signal on timeline[%p]", timeline); g_signal_emit (timeline, timeline_signals[NEW_FRAME], 0, elapsed); } static gboolean is_complete (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv = timeline->priv; return (priv->direction == CLUTTER_TIMELINE_FORWARD ? priv->elapsed_time >= priv->duration : priv->elapsed_time <= 0); } static void set_is_playing (ClutterTimeline *timeline, gboolean is_playing) { ClutterTimelinePrivate *priv = timeline->priv; ClutterMasterClock *master_clock; is_playing = !!is_playing; if (is_playing == priv->is_playing) return; priv->is_playing = is_playing; master_clock = _clutter_master_clock_get_default (); if (priv->is_playing) { priv->waiting_first_tick = TRUE; priv->current_repeat = 0; _clutter_master_clock_add_timeline (master_clock, timeline); } else _clutter_master_clock_remove_timeline (master_clock, timeline); } static gboolean clutter_timeline_do_frame (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; priv = timeline->priv; g_object_ref (timeline); CLUTTER_NOTE (SCHEDULER, "Timeline [%p] activated (elapsed time: %ld, " "duration: %ld, msecs_delta: %ld)\n", timeline, (long) priv->elapsed_time, (long) priv->duration, (long) priv->msecs_delta); /* Advance time */ if (priv->direction == CLUTTER_TIMELINE_FORWARD) priv->elapsed_time += priv->msecs_delta; else priv->elapsed_time -= priv->msecs_delta; /* If we have not reached the end of the timeline: */ if (!is_complete (timeline)) { /* Emit the signal */ emit_frame_signal (timeline); check_markers (timeline, priv->msecs_delta); g_object_unref (timeline); return priv->is_playing; } else { /* Handle loop or stop */ ClutterTimelineDirection saved_direction = priv->direction; gint elapsed_time_delta = priv->msecs_delta; guint overflow_msecs = priv->elapsed_time; gint end_msecs; /* Update the current elapsed time in case the signal handlers * want to take a peek. If we clamp elapsed time, then we need * to correpondingly reduce elapsed_time_delta to reflect the correct * range of times */ if (priv->direction == CLUTTER_TIMELINE_FORWARD) { elapsed_time_delta -= (priv->elapsed_time - priv->duration); priv->elapsed_time = priv->duration; } else if (priv->direction == CLUTTER_TIMELINE_BACKWARD) { elapsed_time_delta -= - priv->elapsed_time; priv->elapsed_time = 0; } end_msecs = priv->elapsed_time; /* Emit the signal */ emit_frame_signal (timeline); check_markers (timeline, elapsed_time_delta); /* Did the signal handler modify the elapsed time? */ if (priv->elapsed_time != end_msecs) { g_object_unref (timeline); return TRUE; } /* Note: If the new-frame signal handler paused the timeline * on the last frame we will still go ahead and send the * completed signal */ CLUTTER_NOTE (SCHEDULER, "Timeline [%p] completed (cur: %ld, tot: %ld)", timeline, (long) priv->elapsed_time, (long) priv->msecs_delta); if (priv->is_playing && (priv->repeat_count == 0 || priv->repeat_count == priv->current_repeat)) { /* We stop the timeline now, so that the completed signal handler * may choose to re-start the timeline * * XXX Perhaps we should do this earlier, and regardless of * priv->repeat_count. Are we limiting the things that could be * done in the above new-frame signal handler? */ set_is_playing (timeline, FALSE); g_signal_emit (timeline, timeline_signals[COMPLETED], 0); g_signal_emit (timeline, timeline_signals[STOPPED], 0, TRUE); } else g_signal_emit (timeline, timeline_signals[COMPLETED], 0); priv->current_repeat += 1; if (priv->auto_reverse) { /* :auto-reverse changes the direction of the timeline */ if (priv->direction == CLUTTER_TIMELINE_FORWARD) priv->direction = CLUTTER_TIMELINE_BACKWARD; else priv->direction = CLUTTER_TIMELINE_FORWARD; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DIRECTION]); } /* Again check to see if the user has manually played with * the elapsed time, before we finally stop or loop the timeline */ if (priv->elapsed_time != end_msecs && !(/* Except allow changing time from 0 -> duration (or vice-versa) since these are considered equivalent */ (priv->elapsed_time == 0 && end_msecs == priv->duration) || (priv->elapsed_time == priv->duration && end_msecs == 0) )) { g_object_unref (timeline); return TRUE; } if (priv->repeat_count != 0) { /* We try and interpolate smoothly around a loop */ if (saved_direction == CLUTTER_TIMELINE_FORWARD) priv->elapsed_time = overflow_msecs - priv->duration; else priv->elapsed_time = priv->duration + overflow_msecs; /* Or if the direction changed, we try and bounce */ if (priv->direction != saved_direction) priv->elapsed_time = priv->duration - priv->elapsed_time; /* If we have overflowed then we are changing the elapsed time without emitting the new frame signal so we need to check for markers again */ check_markers (timeline, priv->direction == CLUTTER_TIMELINE_FORWARD ? priv->elapsed_time : priv->duration - priv->elapsed_time); g_object_unref (timeline); return TRUE; } else { clutter_timeline_rewind (timeline); g_object_unref (timeline); return FALSE; } } } static gboolean delay_timeout_func (gpointer data) { ClutterTimeline *timeline = data; ClutterTimelinePrivate *priv = timeline->priv; priv->delay_id = 0; priv->msecs_delta = 0; set_is_playing (timeline, TRUE); g_signal_emit (timeline, timeline_signals[STARTED], 0); return FALSE; } /** * clutter_timeline_start: * @timeline: A #ClutterTimeline * * Starts the #ClutterTimeline playing. **/ void clutter_timeline_start (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->delay_id || priv->is_playing) return; if (priv->duration == 0) return; if (priv->delay) priv->delay_id = clutter_threads_add_timeout (priv->delay, delay_timeout_func, timeline); else { priv->msecs_delta = 0; set_is_playing (timeline, TRUE); g_signal_emit (timeline, timeline_signals[STARTED], 0); } } /** * clutter_timeline_pause: * @timeline: A #ClutterTimeline * * Pauses the #ClutterTimeline on current frame **/ void clutter_timeline_pause (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; clutter_timeline_cancel_delay (timeline); if (!priv->is_playing) return; priv->msecs_delta = 0; set_is_playing (timeline, FALSE); g_signal_emit (timeline, timeline_signals[PAUSED], 0); } /** * clutter_timeline_stop: * @timeline: A #ClutterTimeline * * Stops the #ClutterTimeline and moves to frame 0 **/ void clutter_timeline_stop (ClutterTimeline *timeline) { gboolean was_playing; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); /* we check the is_playing here because pause() will return immediately * if the timeline wasn't playing, so we don't know if it was actually * stopped, and yet we still don't want to emit a ::stopped signal if * the timeline was not playing in the first place. */ was_playing = timeline->priv->is_playing; clutter_timeline_pause (timeline); clutter_timeline_rewind (timeline); if (was_playing) g_signal_emit (timeline, timeline_signals[STOPPED], 0, FALSE); } /** * clutter_timeline_set_loop: * @timeline: a #ClutterTimeline * @loop: %TRUE for enable looping * * Sets whether @timeline should loop. * * This function is equivalent to calling clutter_timeline_set_repeat_count() * with -1 if @loop is %TRUE, and with 0 if @loop is %FALSE. * * Deprecated: 1.10: Use clutter_timeline_set_repeat_count() instead. */ void clutter_timeline_set_loop (ClutterTimeline *timeline, gboolean loop) { g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); clutter_timeline_set_loop_internal (timeline, loop); } /** * clutter_timeline_get_loop: * @timeline: a #ClutterTimeline * * Gets whether @timeline is looping * * Return value: %TRUE if the timeline is looping * * Deprecated: 1.10: Use clutter_timeline_get_repeat_count() instead. */ gboolean clutter_timeline_get_loop (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); return timeline->priv->repeat_count != 0; } /** * clutter_timeline_rewind: * @timeline: A #ClutterTimeline * * Rewinds #ClutterTimeline to the first frame if its direction is * %CLUTTER_TIMELINE_FORWARD and the last frame if it is * %CLUTTER_TIMELINE_BACKWARD. */ void clutter_timeline_rewind (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->direction == CLUTTER_TIMELINE_FORWARD) clutter_timeline_advance (timeline, 0); else if (priv->direction == CLUTTER_TIMELINE_BACKWARD) clutter_timeline_advance (timeline, priv->duration); } /** * clutter_timeline_skip: * @timeline: A #ClutterTimeline * @msecs: Amount of time to skip * * Advance timeline by the requested time in milliseconds */ void clutter_timeline_skip (ClutterTimeline *timeline, guint msecs) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->direction == CLUTTER_TIMELINE_FORWARD) { priv->elapsed_time += msecs; if (priv->elapsed_time > priv->duration) priv->elapsed_time = 1; } else if (priv->direction == CLUTTER_TIMELINE_BACKWARD) { priv->elapsed_time -= msecs; if (priv->elapsed_time < 1) priv->elapsed_time = priv->duration - 1; } priv->msecs_delta = 0; } /** * clutter_timeline_advance: * @timeline: A #ClutterTimeline * @msecs: Time to advance to * * Advance timeline to the requested point. The point is given as a * time in milliseconds since the timeline started. * * The @timeline will not emit the #ClutterTimeline::new-frame * signal for the given time. The first ::new-frame signal after the call to * clutter_timeline_advance() will be emit the skipped markers. */ void clutter_timeline_advance (ClutterTimeline *timeline, guint msecs) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; priv->elapsed_time = CLAMP (msecs, 0, priv->duration); } /** * clutter_timeline_get_elapsed_time: * @timeline: A #ClutterTimeline * * Request the current time position of the timeline. * * Return value: current elapsed time in milliseconds. */ guint clutter_timeline_get_elapsed_time (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); return timeline->priv->elapsed_time; } /** * clutter_timeline_is_playing: * @timeline: A #ClutterTimeline * * Queries state of a #ClutterTimeline. * * Return value: %TRUE if timeline is currently playing */ gboolean clutter_timeline_is_playing (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); return timeline->priv->is_playing; } /** * clutter_timeline_clone: * @timeline: #ClutterTimeline to duplicate. * * Create a new #ClutterTimeline instance which has property values * matching that of supplied timeline. The cloned timeline will not * be started and will not be positioned to the current position of * the original @timeline: you will have to start it with * clutter_timeline_start(). * * The only cloned properties are: * * - #ClutterTimeline:duration * - #ClutterTimeline:loop * - #ClutterTimeline:delay * - #ClutterTimeline:direction * * Return value: (transfer full): a new #ClutterTimeline, cloned * from @timeline * * Since: 0.4 * * Deprecated: 1.10: Use clutter_timeline_new() or g_object_new() * instead */ ClutterTimeline * clutter_timeline_clone (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); return g_object_new (CLUTTER_TYPE_TIMELINE, "duration", timeline->priv->duration, "loop", timeline->priv->repeat_count != 0, "delay", timeline->priv->delay, "direction", timeline->priv->direction, NULL); } /** * clutter_timeline_new: * @msecs: Duration of the timeline in milliseconds * * Creates a new #ClutterTimeline with a duration of @msecs. * * Return value: the newly created #ClutterTimeline instance. Use * g_object_unref() when done using it * * Since: 0.6 */ ClutterTimeline * clutter_timeline_new (guint msecs) { return g_object_new (CLUTTER_TYPE_TIMELINE, "duration", msecs, NULL); } /** * clutter_timeline_get_delay: * @timeline: a #ClutterTimeline * * Retrieves the delay set using clutter_timeline_set_delay(). * * Return value: the delay in milliseconds. * * Since: 0.4 */ guint clutter_timeline_get_delay (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); return timeline->priv->delay; } /** * clutter_timeline_set_delay: * @timeline: a #ClutterTimeline * @msecs: delay in milliseconds * * Sets the delay, in milliseconds, before @timeline should start. * * Since: 0.4 */ void clutter_timeline_set_delay (ClutterTimeline *timeline, guint msecs) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->delay != msecs) { priv->delay = msecs; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DELAY]); } } /** * clutter_timeline_get_duration: * @timeline: a #ClutterTimeline * * Retrieves the duration of a #ClutterTimeline in milliseconds. * See clutter_timeline_set_duration(). * * Return value: the duration of the timeline, in milliseconds. * * Since: 0.6 */ guint clutter_timeline_get_duration (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); priv = timeline->priv; return priv->duration; } /** * clutter_timeline_set_duration: * @timeline: a #ClutterTimeline * @msecs: duration of the timeline in milliseconds * * Sets the duration of the timeline, in milliseconds. The speed * of the timeline depends on the ClutterTimeline:fps setting. * * Since: 0.6 */ void clutter_timeline_set_duration (ClutterTimeline *timeline, guint msecs) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (msecs > 0); priv = timeline->priv; if (priv->duration != msecs) { priv->duration = msecs; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DURATION]); } } /** * clutter_timeline_get_progress: * @timeline: a #ClutterTimeline * * The position of the timeline in a normalized [-1, 2] interval. * * The return value of this function is determined by the progress * mode set using clutter_timeline_set_progress_mode(), or by the * progress function set using clutter_timeline_set_progress_func(). * * Return value: the normalized current position in the timeline. * * Since: 0.6 */ gdouble clutter_timeline_get_progress (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0.0); priv = timeline->priv; /* short-circuit linear progress */ if (priv->progress_func == NULL) return (gdouble) priv->elapsed_time / (gdouble) priv->duration; else return priv->progress_func (timeline, (gdouble) priv->elapsed_time, (gdouble) priv->duration, priv->progress_data); } /** * clutter_timeline_get_direction: * @timeline: a #ClutterTimeline * * Retrieves the direction of the timeline set with * clutter_timeline_set_direction(). * * Return value: the direction of the timeline * * Since: 0.6 */ ClutterTimelineDirection clutter_timeline_get_direction (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_TIMELINE_FORWARD); return timeline->priv->direction; } /** * clutter_timeline_set_direction: * @timeline: a #ClutterTimeline * @direction: the direction of the timeline * * Sets the direction of @timeline, either %CLUTTER_TIMELINE_FORWARD or * %CLUTTER_TIMELINE_BACKWARD. * * Since: 0.6 */ void clutter_timeline_set_direction (ClutterTimeline *timeline, ClutterTimelineDirection direction) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->direction != direction) { priv->direction = direction; if (priv->elapsed_time == 0) priv->elapsed_time = priv->duration; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_DIRECTION]); } } /** * clutter_timeline_get_delta: * @timeline: a #ClutterTimeline * * Retrieves the amount of time elapsed since the last * ClutterTimeline::new-frame signal. * * This function is only useful inside handlers for the ::new-frame * signal, and its behaviour is undefined if the timeline is not * playing. * * Return value: the amount of time in milliseconds elapsed since the * last frame * * Since: 0.6 */ guint clutter_timeline_get_delta (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); if (!clutter_timeline_is_playing (timeline)) return 0; return timeline->priv->msecs_delta; } void _clutter_timeline_advance (ClutterTimeline *timeline, gint64 tick_time) { ClutterTimelinePrivate *priv = timeline->priv; g_object_ref (timeline); CLUTTER_NOTE (SCHEDULER, "Timeline [%p] advancing (cur: %ld, tot: %ld, " "tick_time: %lu)", timeline, (long) priv->elapsed_time, (long) priv->msecs_delta, (long) tick_time); priv->msecs_delta = tick_time; priv->is_playing = TRUE; clutter_timeline_do_frame (timeline); priv->is_playing = FALSE; g_object_unref (timeline); } /*< private > * clutter_timeline_do_tick * @timeline: a #ClutterTimeline * @tick_time: time of advance * * Advances @timeline based on the time passed in @tick_time. This * function is called by the master clock. The @timeline will use this * interval to emit the #ClutterTimeline::new-frame signal and * eventually skip frames. */ void _clutter_timeline_do_tick (ClutterTimeline *timeline, gint64 tick_time) { ClutterTimelinePrivate *priv; priv = timeline->priv; CLUTTER_NOTE (SCHEDULER, "Timeline [%p] ticked (elapsed_time: %ld, msecs_delta: %ld, " "last_frame_time: %ld, tick_time: %ld)", timeline, (long) priv->elapsed_time, (long) priv->msecs_delta, (long) priv->last_frame_time, (long) tick_time); /* Check the is_playing variable before performing the timeline tick. * This is necessary, as if a timeline is stopped in response to a * master-clock generated signal of a different timeline, this code can * still be reached. */ if (!priv->is_playing) return; if (priv->waiting_first_tick) { priv->last_frame_time = tick_time; priv->msecs_delta = 0; priv->waiting_first_tick = FALSE; clutter_timeline_do_frame (timeline); } else { gint64 msecs; msecs = tick_time - priv->last_frame_time; /* if the clock rolled back between ticks we need to * account for it; the best course of action, since the * clock roll back can happen by any arbitrary amount * of milliseconds, is to drop a frame here */ if (msecs < 0) { priv->last_frame_time = tick_time; return; } if (msecs != 0) { /* Avoid accumulating error */ priv->last_frame_time += msecs; priv->msecs_delta = msecs; clutter_timeline_do_frame (timeline); } } } /** * clutter_timeline_add_marker: * @timeline: a #ClutterTimeline * @marker_name: the unique name for this marker * @progress: the normalized value of the position of the martke * * Adds a named marker that will be hit when the timeline has reached * the specified @progress. * * Markers are unique string identifiers for a given position on the * timeline. Once @timeline reaches the given @progress of its duration, * if will emit a ::marker-reached signal for each marker attached to * that particular point. * * A marker can be removed with clutter_timeline_remove_marker(). The * timeline can be advanced to a marker using * clutter_timeline_advance_to_marker(). * * See also: clutter_timeline_add_marker_at_time() * * Since: 1.14 */ void clutter_timeline_add_marker (ClutterTimeline *timeline, const gchar *marker_name, gdouble progress) { TimelineMarker *marker; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (marker_name != NULL); marker = timeline_marker_new_progress (marker_name, progress); clutter_timeline_add_marker_internal (timeline, marker); } /** * clutter_timeline_add_marker_at_time: * @timeline: a #ClutterTimeline * @marker_name: the unique name for this marker * @msecs: position of the marker in milliseconds * * Adds a named marker that will be hit when the timeline has been * running for @msecs milliseconds. * * Markers are unique string identifiers for a given position on the * timeline. Once @timeline reaches the given @msecs, it will emit * a ::marker-reached signal for each marker attached to that position. * * A marker can be removed with clutter_timeline_remove_marker(). The * timeline can be advanced to a marker using * clutter_timeline_advance_to_marker(). * * See also: clutter_timeline_add_marker() * * Since: 0.8 */ void clutter_timeline_add_marker_at_time (ClutterTimeline *timeline, const gchar *marker_name, guint msecs) { TimelineMarker *marker; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (marker_name != NULL); g_return_if_fail (msecs <= clutter_timeline_get_duration (timeline)); marker = timeline_marker_new_time (marker_name, msecs); clutter_timeline_add_marker_internal (timeline, marker); } struct CollectMarkersClosure { guint duration; guint msecs; GArray *markers; }; static void collect_markers (const gchar *key, TimelineMarker *marker, struct CollectMarkersClosure *data) { guint msecs; if (marker->is_relative) msecs = marker->data.progress * data->duration; else msecs = marker->data.msecs; if (msecs == data->msecs) { gchar *name_copy = g_strdup (key); g_array_append_val (data->markers, name_copy); } } /** * clutter_timeline_list_markers: * @timeline: a #ClutterTimeline * @msecs: the time to check, or -1 * @n_markers: the number of markers returned * * Retrieves the list of markers at time @msecs. If @msecs is a * negative integer, all the markers attached to @timeline will be * returned. * * Return value: (transfer full) (array zero-terminated=1 length=n_markers): * a newly allocated, %NULL terminated string array containing the names * of the markers. Use g_strfreev() when done. * * Since: 0.8 */ gchar ** clutter_timeline_list_markers (ClutterTimeline *timeline, gint msecs, gsize *n_markers) { ClutterTimelinePrivate *priv; gchar **retval = NULL; gsize i; g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), NULL); priv = timeline->priv; if (G_UNLIKELY (priv->markers_by_name == NULL)) { if (n_markers) *n_markers = 0; return NULL; } if (msecs < 0) { GList *markers, *l; markers = g_hash_table_get_keys (priv->markers_by_name); retval = g_new0 (gchar*, g_list_length (markers) + 1); for (i = 0, l = markers; l != NULL; i++, l = l->next) retval[i] = g_strdup (l->data); g_list_free (markers); } else { struct CollectMarkersClosure data; data.duration = priv->duration; data.msecs = msecs; data.markers = g_array_new (TRUE, FALSE, sizeof (gchar *)); g_hash_table_foreach (priv->markers_by_name, (GHFunc) collect_markers, &data); i = data.markers->len; retval = (gchar **) (void *) g_array_free (data.markers, FALSE); } if (n_markers) *n_markers = i; return retval; } /** * clutter_timeline_advance_to_marker: * @timeline: a #ClutterTimeline * @marker_name: the name of the marker * * Advances @timeline to the time of the given @marker_name. * * Like clutter_timeline_advance(), this function will not * emit the #ClutterTimeline::new-frame for the time where @marker_name * is set, nor it will emit #ClutterTimeline::marker-reached for * @marker_name. * * Since: 0.8 */ void clutter_timeline_advance_to_marker (ClutterTimeline *timeline, const gchar *marker_name) { ClutterTimelinePrivate *priv; TimelineMarker *marker; guint msecs; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (marker_name != NULL); priv = timeline->priv; if (G_UNLIKELY (priv->markers_by_name == NULL)) { g_warning ("No marker named '%s' found.", marker_name); return; } marker = g_hash_table_lookup (priv->markers_by_name, marker_name); if (marker == NULL) { g_warning ("No marker named '%s' found.", marker_name); return; } if (marker->is_relative) msecs = marker->data.progress * priv->duration; else msecs = marker->data.msecs; clutter_timeline_advance (timeline, msecs); } /** * clutter_timeline_remove_marker: * @timeline: a #ClutterTimeline * @marker_name: the name of the marker to remove * * Removes @marker_name, if found, from @timeline. * * Since: 0.8 */ void clutter_timeline_remove_marker (ClutterTimeline *timeline, const gchar *marker_name) { ClutterTimelinePrivate *priv; TimelineMarker *marker; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (marker_name != NULL); priv = timeline->priv; if (G_UNLIKELY (priv->markers_by_name == NULL)) { g_warning ("No marker named '%s' found.", marker_name); return; } marker = g_hash_table_lookup (priv->markers_by_name, marker_name); if (!marker) { g_warning ("No marker named '%s' found.", marker_name); return; } /* this will take care of freeing the marker as well */ g_hash_table_remove (priv->markers_by_name, marker_name); } /** * clutter_timeline_has_marker: * @timeline: a #ClutterTimeline * @marker_name: the name of the marker * * Checks whether @timeline has a marker set with the given name. * * Return value: %TRUE if the marker was found * * Since: 0.8 */ gboolean clutter_timeline_has_marker (ClutterTimeline *timeline, const gchar *marker_name) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); g_return_val_if_fail (marker_name != NULL, FALSE); if (G_UNLIKELY (timeline->priv->markers_by_name == NULL)) return FALSE; return NULL != g_hash_table_lookup (timeline->priv->markers_by_name, marker_name); } /** * clutter_timeline_set_auto_reverse: * @timeline: a #ClutterTimeline * @reverse: %TRUE if the @timeline should reverse the direction * * Sets whether @timeline should reverse the direction after the * emission of the #ClutterTimeline::completed signal. * * Setting the #ClutterTimeline:auto-reverse property to %TRUE is the * equivalent of connecting a callback to the #ClutterTimeline::completed * signal and changing the direction of the timeline from that callback; * for instance, this code: * * |[ * static void * reverse_timeline (ClutterTimeline *timeline) * { * ClutterTimelineDirection dir = clutter_timeline_get_direction (timeline); * * if (dir == CLUTTER_TIMELINE_FORWARD) * dir = CLUTTER_TIMELINE_BACKWARD; * else * dir = CLUTTER_TIMELINE_FORWARD; * * clutter_timeline_set_direction (timeline, dir); * } * ... * timeline = clutter_timeline_new (1000); * clutter_timeline_set_repeat_count (timeline, -1); * g_signal_connect (timeline, "completed", * G_CALLBACK (reverse_timeline), * NULL); * ]| * * can be effectively replaced by: * * |[ * timeline = clutter_timeline_new (1000); * clutter_timeline_set_repeat_count (timeline, -1); * clutter_timeline_set_auto_reverse (timeline); * ]| * * Since: 1.6 */ void clutter_timeline_set_auto_reverse (ClutterTimeline *timeline, gboolean reverse) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); reverse = !!reverse; priv = timeline->priv; if (priv->auto_reverse != reverse) { priv->auto_reverse = reverse; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_AUTO_REVERSE]); } } /** * clutter_timeline_get_auto_reverse: * @timeline: a #ClutterTimeline * * Retrieves the value set by clutter_timeline_set_auto_reverse(). * * Return value: %TRUE if the timeline should automatically reverse, and * %FALSE otherwise * * Since: 1.6 */ gboolean clutter_timeline_get_auto_reverse (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); return timeline->priv->auto_reverse; } /** * clutter_timeline_set_repeat_count: * @timeline: a #ClutterTimeline * @count: the number of times the timeline should repeat * * Sets the number of times the @timeline should repeat. * * If @count is 0, the timeline never repeats. * * If @count is -1, the timeline will always repeat until * it's stopped. * * Since: 1.10 */ void clutter_timeline_set_repeat_count (ClutterTimeline *timeline, gint count) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (count >= -1); priv = timeline->priv; if (priv->repeat_count != count) { priv->repeat_count = count; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_REPEAT_COUNT]); } } /** * clutter_timeline_get_repeat_count: * @timeline: a #ClutterTimeline * * Retrieves the number set using clutter_timeline_set_repeat_count(). * * Return value: the number of repeats * * Since: 1.10 */ gint clutter_timeline_get_repeat_count (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); return timeline->priv->repeat_count; } /** * clutter_timeline_set_progress_func: * @timeline: a #ClutterTimeline * @func: (scope notified) (allow-none) (closure data): a progress function, or %NULL * @data: data to pass to @func * @notify: a function to be called when the progress function is removed * or the timeline is disposed * * Sets a custom progress function for @timeline. The progress function will * be called by clutter_timeline_get_progress() and will be used to compute * the progress value based on the elapsed time and the total duration of the * timeline. * * If @func is not %NULL, the #ClutterTimeline:progress-mode property will * be set to %CLUTTER_CUSTOM_MODE. * * If @func is %NULL, any previously set progress function will be unset, and * the #ClutterTimeline:progress-mode property will be set to %CLUTTER_LINEAR. * * Since: 1.10 */ void clutter_timeline_set_progress_func (ClutterTimeline *timeline, ClutterTimelineProgressFunc func, gpointer data, GDestroyNotify notify) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); priv = timeline->priv; if (priv->progress_notify != NULL) priv->progress_notify (priv->progress_data); priv->progress_func = func; priv->progress_data = data; priv->progress_notify = notify; if (priv->progress_func != NULL) priv->progress_mode = CLUTTER_CUSTOM_MODE; else priv->progress_mode = CLUTTER_LINEAR; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]); } static gdouble clutter_timeline_progress_func (ClutterTimeline *timeline, gdouble elapsed, gdouble duration, gpointer user_data G_GNUC_UNUSED) { ClutterTimelinePrivate *priv = timeline->priv; /* parametrized easing functions need to be handled separately */ switch (priv->progress_mode) { case CLUTTER_STEPS: if (priv->step_mode == CLUTTER_STEP_MODE_START) return clutter_ease_steps_start (elapsed, duration, priv->n_steps); else if (priv->step_mode == CLUTTER_STEP_MODE_END) return clutter_ease_steps_end (elapsed, duration, priv->n_steps); else g_assert_not_reached (); break; case CLUTTER_STEP_START: return clutter_ease_steps_start (elapsed, duration, 1); case CLUTTER_STEP_END: return clutter_ease_steps_end (elapsed, duration, 1); case CLUTTER_CUBIC_BEZIER: return clutter_ease_cubic_bezier (elapsed, duration, priv->cb_1.x, priv->cb_1.y, priv->cb_2.x, priv->cb_2.y); case CLUTTER_EASE: return clutter_ease_cubic_bezier (elapsed, duration, 0.25, 0.1, 0.25, 1.0); case CLUTTER_EASE_IN: return clutter_ease_cubic_bezier (elapsed, duration, 0.42, 0.0, 1.0, 1.0); case CLUTTER_EASE_OUT: return clutter_ease_cubic_bezier (elapsed, duration, 0.0, 0.0, 0.58, 1.0); case CLUTTER_EASE_IN_OUT: return clutter_ease_cubic_bezier (elapsed, duration, 0.42, 0.0, 0.58, 1.0); default: break; } return clutter_easing_for_mode (priv->progress_mode, elapsed, duration); } /** * clutter_timeline_set_progress_mode: * @timeline: a #ClutterTimeline * @mode: the progress mode, as a #ClutterAnimationMode * * Sets the progress function using a value from the #ClutterAnimationMode * enumeration. The @mode cannot be %CLUTTER_CUSTOM_MODE or bigger than * %CLUTTER_ANIMATION_LAST. * * Since: 1.10 */ void clutter_timeline_set_progress_mode (ClutterTimeline *timeline, ClutterAnimationMode mode) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (mode < CLUTTER_ANIMATION_LAST); g_return_if_fail (mode != CLUTTER_CUSTOM_MODE); priv = timeline->priv; if (priv->progress_mode == mode) return; if (priv->progress_notify != NULL) priv->progress_notify (priv->progress_data); priv->progress_mode = mode; /* short-circuit linear progress */ if (priv->progress_mode != CLUTTER_LINEAR) priv->progress_func = clutter_timeline_progress_func; else priv->progress_func = NULL; priv->progress_data = NULL; priv->progress_notify = NULL; g_object_notify_by_pspec (G_OBJECT (timeline), obj_props[PROP_PROGRESS_MODE]); } /** * clutter_timeline_get_progress_mode: * @timeline: a #ClutterTimeline * * Retrieves the progress mode set using clutter_timeline_set_progress_mode() * or clutter_timeline_set_progress_func(). * * Return value: a #ClutterAnimationMode * * Since: 1.10 */ ClutterAnimationMode clutter_timeline_get_progress_mode (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), CLUTTER_LINEAR); return timeline->priv->progress_mode; } /** * clutter_timeline_get_duration_hint: * @timeline: a #ClutterTimeline * * Retrieves the full duration of the @timeline, taking into account the * current value of the #ClutterTimeline:repeat-count property. * * If the #ClutterTimeline:repeat-count property is set to -1, this function * will return %G_MAXINT64. * * The returned value is to be considered a hint, and it's only valid * as long as the @timeline hasn't been changed. * * Return value: the full duration of the #ClutterTimeline * * Since: 1.10 */ gint64 clutter_timeline_get_duration_hint (ClutterTimeline *timeline) { ClutterTimelinePrivate *priv; g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); priv = timeline->priv; if (priv->repeat_count == 0) return priv->duration; else if (priv->repeat_count < 0) return G_MAXINT64; else return priv->repeat_count * priv->duration; } /** * clutter_timeline_get_current_repeat: * @timeline: a #ClutterTimeline * * Retrieves the current repeat for a timeline. * * Repeats start at 0. * * Return value: the current repeat * * Since: 1.10 */ gint clutter_timeline_get_current_repeat (ClutterTimeline *timeline) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), 0); return timeline->priv->current_repeat; } /** * clutter_timeline_set_step_progress: * @timeline: a #ClutterTimeline * @n_steps: the number of steps * @step_mode: whether the change should happen at the start * or at the end of the step * * Sets the #ClutterTimeline:progress-mode of the @timeline to %CLUTTER_STEPS * and provides the parameters of the step function. * * Since: 1.12 */ void clutter_timeline_set_step_progress (ClutterTimeline *timeline, gint n_steps, ClutterStepMode step_mode) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (n_steps > 0); priv = timeline->priv; if (priv->progress_mode == CLUTTER_STEPS && priv->n_steps == n_steps && priv->step_mode == step_mode) return; priv->n_steps = n_steps; priv->step_mode = step_mode; clutter_timeline_set_progress_mode (timeline, CLUTTER_STEPS); } /** * clutter_timeline_get_step_progress: * @timeline: a #ClutterTimeline * @n_steps: (out): return location for the number of steps, or %NULL * @step_mode: (out): return location for the value change policy, * or %NULL * * Retrieves the parameters of the step progress mode used by @timeline. * * Return value: %TRUE if the @timeline is using a step progress * mode, and %FALSE otherwise * * Since: 1.12 */ gboolean clutter_timeline_get_step_progress (ClutterTimeline *timeline, gint *n_steps, ClutterStepMode *step_mode) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); if (!(timeline->priv->progress_mode == CLUTTER_STEPS || timeline->priv->progress_mode == CLUTTER_STEP_START || timeline->priv->progress_mode == CLUTTER_STEP_END)) return FALSE; if (n_steps != NULL) *n_steps = timeline->priv->n_steps; if (step_mode != NULL) *step_mode = timeline->priv->step_mode; return TRUE; } /** * clutter_timeline_set_cubic_bezier_progress: * @timeline: a #ClutterTimeline * @c_1: the first control point for the cubic bezier * @c_2: the second control point for the cubic bezier * * Sets the #ClutterTimeline:progress-mode of @timeline * to %CLUTTER_CUBIC_BEZIER, and sets the two control * points for the cubic bezier. * * The cubic bezier curve is between (0, 0) and (1, 1). The X coordinate * of the two control points must be in the [ 0, 1 ] range, while the * Y coordinate of the two control points can exceed this range. * * Since: 1.12 */ void clutter_timeline_set_cubic_bezier_progress (ClutterTimeline *timeline, const graphene_point_t *c_1, const graphene_point_t *c_2) { ClutterTimelinePrivate *priv; g_return_if_fail (CLUTTER_IS_TIMELINE (timeline)); g_return_if_fail (c_1 != NULL && c_2 != NULL); priv = timeline->priv; priv->cb_1 = *c_1; priv->cb_2 = *c_2; /* ensure the range on the X coordinate */ priv->cb_1.x = CLAMP (priv->cb_1.x, 0.f, 1.f); priv->cb_2.x = CLAMP (priv->cb_2.x, 0.f, 1.f); clutter_timeline_set_progress_mode (timeline, CLUTTER_CUBIC_BEZIER); } /** * clutter_timeline_get_cubic_bezier_progress: * @timeline: a #ClutterTimeline * @c_1: (out caller-allocates): return location for the first control * point of the cubic bezier, or %NULL * @c_2: (out caller-allocates): return location for the second control * point of the cubic bezier, or %NULL * * Retrieves the control points for the cubic bezier progress mode. * * Return value: %TRUE if the @timeline is using a cubic bezier progress * more, and %FALSE otherwise * * Since: 1.12 */ gboolean clutter_timeline_get_cubic_bezier_progress (ClutterTimeline *timeline, graphene_point_t *c_1, graphene_point_t *c_2) { g_return_val_if_fail (CLUTTER_IS_TIMELINE (timeline), FALSE); if (!(timeline->priv->progress_mode == CLUTTER_CUBIC_BEZIER || timeline->priv->progress_mode == CLUTTER_EASE || timeline->priv->progress_mode == CLUTTER_EASE_IN || timeline->priv->progress_mode == CLUTTER_EASE_OUT || timeline->priv->progress_mode == CLUTTER_EASE_IN_OUT)) return FALSE; if (c_1 != NULL) *c_1 = timeline->priv->cb_1; if (c_2 != NULL) *c_2 = timeline->priv->cb_2; return TRUE; } muffin-6.4.1/clutter/clutter/clutter-color.h0000664000175000017500000001603314723361714020060 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Matthew Allum * Emmanuele Bassi * * Copyright (C) 2006, 2007, 2008 OpenedHand * Copyright (C) 2009 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_COLOR_H__ #define __CLUTTER_COLOR_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_COLOR (clutter_color_get_type ()) /** * ClutterColor: * @red: red component, between 0 and 255 * @green: green component, between 0 and 255 * @blue: blue component, between 0 and 255 * @alpha: alpha component, between 0 and 255 * * Color representation. */ struct _ClutterColor { /*< public >*/ guint8 red; guint8 green; guint8 blue; guint8 alpha; }; /** * CLUTTER_COLOR_INIT: * @r: value for the red channel, between 0 and 255 * @g: value for the green channel, between 0 and 255 * @b: value for the blue channel, between 0 and 255 * @a: value for the alpha channel, between 0 and 255 * * A macro that initializes a #ClutterColor, to be used when declaring it. * * Since: 1.12 */ #define CLUTTER_COLOR_INIT(r,g,b,a) { (r), (g), (b), (a) } CLUTTER_EXPORT GType clutter_color_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterColor *clutter_color_new (guint8 red, guint8 green, guint8 blue, guint8 alpha); CLUTTER_EXPORT ClutterColor *clutter_color_alloc (void); CLUTTER_EXPORT ClutterColor *clutter_color_init (ClutterColor *color, guint8 red, guint8 green, guint8 blue, guint8 alpha); CLUTTER_EXPORT ClutterColor *clutter_color_copy (const ClutterColor *color); CLUTTER_EXPORT void clutter_color_free (ClutterColor *color); CLUTTER_EXPORT void clutter_color_add (const ClutterColor *a, const ClutterColor *b, ClutterColor *result); CLUTTER_EXPORT void clutter_color_subtract (const ClutterColor *a, const ClutterColor *b, ClutterColor *result); CLUTTER_EXPORT void clutter_color_lighten (const ClutterColor *color, ClutterColor *result); CLUTTER_EXPORT void clutter_color_darken (const ClutterColor *color, ClutterColor *result); CLUTTER_EXPORT void clutter_color_shade (const ClutterColor *color, gdouble factor, ClutterColor *result); CLUTTER_EXPORT gchar * clutter_color_to_string (const ClutterColor *color); CLUTTER_EXPORT gboolean clutter_color_from_string (ClutterColor *color, const gchar *str); CLUTTER_EXPORT void clutter_color_to_hls (const ClutterColor *color, gfloat *hue, gfloat *luminance, gfloat *saturation); CLUTTER_EXPORT void clutter_color_from_hls (ClutterColor *color, gfloat hue, gfloat luminance, gfloat saturation); CLUTTER_EXPORT guint32 clutter_color_to_pixel (const ClutterColor *color); CLUTTER_EXPORT void clutter_color_from_pixel (ClutterColor *color, guint32 pixel); CLUTTER_EXPORT guint clutter_color_hash (gconstpointer v); CLUTTER_EXPORT gboolean clutter_color_equal (gconstpointer v1, gconstpointer v2); CLUTTER_EXPORT void clutter_color_interpolate (const ClutterColor *initial, const ClutterColor *final, gdouble progress, ClutterColor *result); #define CLUTTER_TYPE_PARAM_COLOR (clutter_param_color_get_type ()) #define CLUTTER_PARAM_SPEC_COLOR(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), CLUTTER_TYPE_PARAM_COLOR, ClutterParamSpecColor)) #define CLUTTER_IS_PARAM_SPEC_COLOR(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), CLUTTER_TYPE_PARAM_COLOR)) /** * CLUTTER_VALUE_HOLDS_COLOR: * @x: a #GValue * * Evaluates to %TRUE if @x holds a #ClutterColor. * * Since: 1.0 */ #define CLUTTER_VALUE_HOLDS_COLOR(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_COLOR)) typedef struct _ClutterParamSpecColor ClutterParamSpecColor; /** * ClutterParamSpecColor: (skip) * @default_value: default color value * * A #GParamSpec subclass for defining properties holding * a #ClutterColor. * * Since: 1.0 */ struct _ClutterParamSpecColor { /*< private >*/ GParamSpec parent_instance; /*< public >*/ ClutterColor *default_value; }; CLUTTER_EXPORT void clutter_value_set_color (GValue *value, const ClutterColor *color); CLUTTER_EXPORT const ClutterColor * clutter_value_get_color (const GValue *value); CLUTTER_EXPORT GType clutter_param_color_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GParamSpec * clutter_param_spec_color (const gchar *name, const gchar *nick, const gchar *blurb, const ClutterColor *default_value, GParamFlags flags); CLUTTER_EXPORT const ClutterColor *clutter_color_get_static (ClutterStaticColor color); G_END_DECLS #endif /* __CLUTTER_COLOR_H__ */ muffin-6.4.1/clutter/clutter/clutter-constraint-private.h0000664000175000017500000000226714723361714022602 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_CONSTRAINT_PRIVATE_H__ #define __CLUTTER_CONSTRAINT_PRIVATE_H__ #include "clutter-constraint.h" G_BEGIN_DECLS gboolean clutter_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation); G_END_DECLS #endif /* __CLUTTER_CONSTRAINT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-property-transition.c0000664000175000017500000002601614723361714023013 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-property-transition * @Title: ClutterPropertyTransition * @Short_Description: Property transitions * * #ClutterPropertyTransition is a specialized #ClutterTransition that * can be used to tween a property of a #ClutterAnimatable instance. * * #ClutterPropertyTransition is available since Clutter 1.10 */ #include "clutter-build-config.h" #include "clutter-property-transition.h" #include "clutter-animatable.h" #include "clutter-debug.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-transition.h" struct _ClutterPropertyTransitionPrivate { char *property_name; GParamSpec *pspec; }; enum { PROP_0, PROP_PROPERTY_NAME, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterPropertyTransition, clutter_property_transition, CLUTTER_TYPE_TRANSITION) static inline void clutter_property_transition_ensure_interval (ClutterPropertyTransition *transition, ClutterAnimatable *animatable, ClutterInterval *interval) { ClutterPropertyTransitionPrivate *priv = transition->priv; GValue *value_p; if (clutter_interval_is_valid (interval)) return; /* if no initial value has been set, use the current value */ value_p = clutter_interval_peek_initial_value (interval); if (!G_IS_VALUE (value_p)) { g_value_init (value_p, clutter_interval_get_value_type (interval)); clutter_animatable_get_initial_state (animatable, priv->property_name, value_p); } /* if no final value has been set, use the current value */ value_p = clutter_interval_peek_final_value (interval); if (!G_IS_VALUE (value_p)) { g_value_init (value_p, clutter_interval_get_value_type (interval)); clutter_animatable_get_initial_state (animatable, priv->property_name, value_p); } } static void clutter_property_transition_attached (ClutterTransition *transition, ClutterAnimatable *animatable) { ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition); ClutterPropertyTransitionPrivate *priv = self->priv; ClutterInterval *interval; if (priv->property_name == NULL) return; priv->pspec = clutter_animatable_find_property (animatable, priv->property_name); if (priv->pspec == NULL) return; interval = clutter_transition_get_interval (transition); if (interval == NULL) return; clutter_property_transition_ensure_interval (self, animatable, interval); } static void clutter_property_transition_detached (ClutterTransition *transition, ClutterAnimatable *animatable) { ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition); ClutterPropertyTransitionPrivate *priv = self->priv; priv->pspec = NULL; } static void clutter_property_transition_compute_value (ClutterTransition *transition, ClutterAnimatable *animatable, ClutterInterval *interval, gdouble progress) { ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (transition); ClutterPropertyTransitionPrivate *priv = self->priv; GValue value = G_VALUE_INIT; GType p_type, i_type; gboolean res; /* if we have a GParamSpec we also have an animatable instance */ if (priv->pspec == NULL) return; clutter_property_transition_ensure_interval (self, animatable, interval); p_type = G_PARAM_SPEC_VALUE_TYPE (priv->pspec); i_type = clutter_interval_get_value_type (interval); g_value_init (&value, i_type); res = clutter_animatable_interpolate_value (animatable, priv->property_name, interval, progress, &value); if (res) { if (i_type != p_type || g_type_is_a (i_type, p_type)) { if (g_value_type_transformable (i_type, p_type)) { GValue transform = G_VALUE_INIT; g_value_init (&transform, p_type); if (g_value_transform (&value, &transform)) { clutter_animatable_set_final_state (animatable, priv->property_name, &transform); } else g_warning ("%s: Unable to convert a value of type '%s' from " "the value type '%s' of the interval.", G_STRLOC, g_type_name (p_type), g_type_name (i_type)); g_value_unset (&transform); } } else clutter_animatable_set_final_state (animatable, priv->property_name, &value); } g_value_unset (&value); } static void clutter_property_transition_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterPropertyTransition *self = CLUTTER_PROPERTY_TRANSITION (gobject); switch (prop_id) { case PROP_PROPERTY_NAME: clutter_property_transition_set_property_name (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_property_transition_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterPropertyTransitionPrivate *priv = CLUTTER_PROPERTY_TRANSITION (gobject)->priv; switch (prop_id) { case PROP_PROPERTY_NAME: g_value_set_string (value, priv->property_name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_property_transition_finalize (GObject *gobject) { ClutterPropertyTransitionPrivate *priv; priv = CLUTTER_PROPERTY_TRANSITION (gobject)->priv; g_free (priv->property_name); G_OBJECT_CLASS (clutter_property_transition_parent_class)->finalize (gobject); } static void clutter_property_transition_class_init (ClutterPropertyTransitionClass *klass) { ClutterTransitionClass *transition_class = CLUTTER_TRANSITION_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); transition_class->attached = clutter_property_transition_attached; transition_class->detached = clutter_property_transition_detached; transition_class->compute_value = clutter_property_transition_compute_value; gobject_class->set_property = clutter_property_transition_set_property; gobject_class->get_property = clutter_property_transition_get_property; gobject_class->finalize = clutter_property_transition_finalize; /** * ClutterPropertyTransition:property-name: * * The name of the property of a #ClutterAnimatable to animate. * * Since: 1.10 */ obj_props[PROP_PROPERTY_NAME] = g_param_spec_string ("property-name", P_("Property Name"), P_("The name of the property to animate"), NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_property_transition_init (ClutterPropertyTransition *self) { self->priv = clutter_property_transition_get_instance_private (self); } /** * clutter_property_transition_new: * @property_name: (allow-none): a property of @animatable, or %NULL * * Creates a new #ClutterPropertyTransition. * * Return value: (transfer full): the newly created #ClutterPropertyTransition. * Use g_object_unref() when done * * Since: 1.10 */ ClutterTransition * clutter_property_transition_new (const char *property_name) { return g_object_new (CLUTTER_TYPE_PROPERTY_TRANSITION, "property-name", property_name, NULL); } /** * clutter_property_transition_set_property_name: * @transition: a #ClutterPropertyTransition * @property_name: (allow-none): a property name * * Sets the #ClutterPropertyTransition:property-name property of @transition. * * Since: 1.10 */ void clutter_property_transition_set_property_name (ClutterPropertyTransition *transition, const char *property_name) { ClutterPropertyTransitionPrivate *priv; ClutterAnimatable *animatable; g_return_if_fail (CLUTTER_IS_PROPERTY_TRANSITION (transition)); priv = transition->priv; if (g_strcmp0 (priv->property_name, property_name) == 0) return; g_free (priv->property_name); priv->property_name = g_strdup (property_name); priv->pspec = NULL; animatable = clutter_transition_get_animatable (CLUTTER_TRANSITION (transition)); if (animatable != NULL) { priv->pspec = clutter_animatable_find_property (animatable, priv->property_name); } g_object_notify_by_pspec (G_OBJECT (transition), obj_props[PROP_PROPERTY_NAME]); } /** * clutter_property_transition_get_property_name: * @transition: a #ClutterPropertyTransition * * Retrieves the value of the #ClutterPropertyTransition:property-name * property. * * Return value: the name of the property being animated, or %NULL if * none is set. The returned string is owned by the @transition and * it should not be freed. * * Since: 1.10 */ const char * clutter_property_transition_get_property_name (ClutterPropertyTransition *transition) { g_return_val_if_fail (CLUTTER_IS_PROPERTY_TRANSITION (transition), NULL); return transition->priv->property_name; } muffin-6.4.1/clutter/clutter/clutter-fixed-layout.c0000664000175000017500000001206514723361714021350 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on the fixed layout code inside clutter-group.c */ /** * SECTION:clutter-fixed-layout * @short_description: A fixed layout manager * * #ClutterFixedLayout is a layout manager implementing the same * layout policies as #ClutterGroup. * * #ClutterFixedLayout is available since Clutter 1.2 */ #include "clutter-build-config.h" #include "clutter-debug.h" #include "clutter-fixed-layout.h" #include "clutter-private.h" G_DEFINE_TYPE (ClutterFixedLayout, clutter_fixed_layout, CLUTTER_TYPE_LAYOUT_MANAGER); static void clutter_fixed_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterActor *actor, *child; gdouble min_right; gdouble natural_right; min_right = 0; natural_right = 0; actor = CLUTTER_ACTOR (container); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_x, child_min, child_natural; child_x = clutter_actor_get_x (child); clutter_actor_get_preferred_size (child, &child_min, NULL, &child_natural, NULL); if (child_x + child_min > min_right) min_right = child_x + child_min; if (child_x + child_natural > natural_right) natural_right = child_x + child_natural; } if (min_width_p) *min_width_p = min_right; if (nat_width_p) *nat_width_p = natural_right; } static void clutter_fixed_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterActor *actor, *child; gdouble min_bottom; gdouble natural_bottom; min_bottom = 0; natural_bottom = 0; actor = CLUTTER_ACTOR (container); for (child = clutter_actor_get_first_child (actor); child != NULL; child = clutter_actor_get_next_sibling (child)) { gfloat child_y, child_min, child_natural; child_y = clutter_actor_get_y (child); clutter_actor_get_preferred_size (child, NULL, &child_min, NULL, &child_natural); if (child_y + child_min > min_bottom) min_bottom = child_y + child_min; if (child_y + child_natural > natural_bottom) natural_bottom = child_y + child_natural; } if (min_height_p) *min_height_p = min_bottom; if (nat_height_p) *nat_height_p = natural_bottom; } static void clutter_fixed_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterActor *child; for (child = clutter_actor_get_first_child (CLUTTER_ACTOR (container)); child != NULL; child = clutter_actor_get_next_sibling (child)) { clutter_actor_allocate_preferred_size (child, flags); } } static void clutter_fixed_layout_class_init (ClutterFixedLayoutClass *klass) { ClutterLayoutManagerClass *manager_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); manager_class->get_preferred_width = clutter_fixed_layout_get_preferred_width; manager_class->get_preferred_height = clutter_fixed_layout_get_preferred_height; manager_class->allocate = clutter_fixed_layout_allocate; } static void clutter_fixed_layout_init (ClutterFixedLayout *self) { } /** * clutter_fixed_layout_new: * * Creates a new #ClutterFixedLayout * * Return value: the newly created #ClutterFixedLayout * * Since: 1.2 */ ClutterLayoutManager * clutter_fixed_layout_new (void) { return g_object_new (CLUTTER_TYPE_FIXED_LAYOUT, NULL); } muffin-6.4.1/clutter/clutter/clutter-child-meta.h0000664000175000017500000001020314723361714020742 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Jorn Baayen * Emmanuele Bassi * Tomas Frydrych * Øyvind Kolås * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_CHILD_META_H__ #define __CLUTTER_CHILD_META_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_CHILD_META (clutter_child_meta_get_type ()) #define CLUTTER_CHILD_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CHILD_META, ClutterChildMeta)) #define CLUTTER_CHILD_META_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_CHILD_META, ClutterChildMetaClass)) #define CLUTTER_IS_CHILD_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CHILD_META)) #define CLUTTER_IS_CHILD_META_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_CHILD_META)) #define CLUTTER_CHILD_META_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_CHILD_META, ClutterChildMetaClass)) typedef struct _ClutterChildMetaClass ClutterChildMetaClass; /** * ClutterChildMeta: * @container: the container handling this data * @actor: the actor wrapped by this data * * Base interface for container specific state for child actors. A child * data is meant to be used when you need to keep track of information * about each individual child added to a container. * * In order to use it you should create your own subclass of * #ClutterChildMeta and set the #ClutterContainerIface child_meta_type * interface member to your subclass type, like: * * |[ * static void * my_container_iface_init (ClutterContainerIface *iface) * { * // set the rest of the #ClutterContainer vtable * * container_iface->child_meta_type = MY_TYPE_CHILD_META; * } * ]| * * This will automatically create a #ClutterChildMeta of type * `MY_TYPE_CHILD_META` for every actor that is added to the container. * * The child data for an actor can be retrieved using the * clutter_container_get_child_meta() function. * * The properties of the data and your subclass can be manipulated with * clutter_container_child_set() and clutter_container_child_get() which * act like g_object_set() and g_object_get(). * * You can provide hooks for your own storage as well as control the * instantiation by overriding the #ClutterContainerIface virtual functions * #ClutterContainerIface.create_child_meta(), #ClutterContainerIface.destroy_child_meta(), * and #ClutterContainerIface.get_child_meta(). * * Since: 0.8 */ struct _ClutterChildMeta { /*< private >*/ GObject parent_instance; /*< public >*/ ClutterContainer *container; ClutterActor *actor; }; /** * ClutterChildMetaClass: * * The #ClutterChildMetaClass contains only private data * * Since: 0.8 */ struct _ClutterChildMetaClass { /*< private >*/ GObjectClass parent_class; }; CLUTTER_EXPORT GType clutter_child_meta_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterContainer * clutter_child_meta_get_container (ClutterChildMeta *data); CLUTTER_EXPORT ClutterActor * clutter_child_meta_get_actor (ClutterChildMeta *data); G_END_DECLS #endif /* __CLUTTER_CHILD_META_H__ */ muffin-6.4.1/clutter/clutter/clutter-pick-context.c0000664000175000017500000000430514723361714021344 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "clutter-build-config.h" #include "clutter-pick-context-private.h" struct _ClutterPickContext { grefcount ref_count; CoglFramebuffer *framebuffer; }; G_DEFINE_BOXED_TYPE (ClutterPickContext, clutter_pick_context, clutter_pick_context_ref, clutter_pick_context_unref) ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view) { ClutterPickContext *pick_context; pick_context = g_new0 (ClutterPickContext, 1); g_ref_count_init (&pick_context->ref_count); pick_context->framebuffer = cogl_object_ref (clutter_stage_view_get_framebuffer (view)); return pick_context; } ClutterPickContext * clutter_pick_context_ref (ClutterPickContext *pick_context) { g_ref_count_inc (&pick_context->ref_count); return pick_context; } static void clutter_pick_context_dispose (ClutterPickContext *pick_context) { g_clear_pointer (&pick_context->framebuffer, cogl_object_unref); } void clutter_pick_context_unref (ClutterPickContext *pick_context) { if (g_ref_count_dec (&pick_context->ref_count)) { clutter_pick_context_dispose (pick_context); g_free (pick_context); } } void clutter_pick_context_destroy (ClutterPickContext *pick_context) { clutter_pick_context_dispose (pick_context); clutter_pick_context_unref (pick_context); } /** * clutter_pick_context_get_framebuffer: (skip) */ CoglFramebuffer * clutter_pick_context_get_framebuffer (ClutterPickContext *pick_context) { return pick_context->framebuffer; } muffin-6.4.1/clutter/clutter/clutter-flatten-effect.h0000664000175000017500000000547114723361714021635 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Neil Roberts */ #ifndef __CLUTTER_FLATTEN_EFFECT_H__ #define __CLUTTER_FLATTEN_EFFECT_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_FLATTEN_EFFECT \ (_clutter_flatten_effect_get_type()) #define CLUTTER_FLATTEN_EFFECT(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ CLUTTER_TYPE_FLATTEN_EFFECT, \ ClutterFlattenEffect)) #define CLUTTER_FLATTEN_EFFECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ CLUTTER_TYPE_FLATTEN_EFFECT, \ ClutterFlattenEffectClass)) #define CLUTTER_IS_FLATTEN_EFFECT(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ CLUTTER_TYPE_FLATTEN_EFFECT)) #define CLUTTER_IS_FLATTEN_EFFECT_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ CLUTTER_TYPE_FLATTEN_EFFECT)) #define CLUTTER_FLATTEN_EFFECT_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ CLUTTER_FLATTEN_EFFECT, \ ClutterFlattenEffectClass)) typedef struct _ClutterFlattenEffect ClutterFlattenEffect; typedef struct _ClutterFlattenEffectClass ClutterFlattenEffectClass; typedef struct _ClutterFlattenEffectPrivate ClutterFlattenEffectPrivate; struct _ClutterFlattenEffectClass { ClutterOffscreenEffectClass parent_class; }; struct _ClutterFlattenEffect { ClutterOffscreenEffect parent; }; GType _clutter_flatten_effect_get_type (void) G_GNUC_CONST; ClutterEffect *_clutter_flatten_effect_new (void); G_END_DECLS #endif /* __CLUTTER_FLATTEN_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-content.c0000664000175000017500000002337414723361714020415 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-content * @Title: ClutterContent * @Short_Description: Delegate for painting the content of an actor * * #ClutterContent is an interface to implement types responsible for * painting the content of a #ClutterActor. * * Multiple actors can use the same #ClutterContent instance, in order * to share the resources associated with painting the same content. * * #ClutterContent is available since Clutter 1.10. */ #include "clutter-build-config.h" #include "clutter-actor-private.h" #include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-marshal.h" #include "clutter-private.h" enum { ATTACHED, DETACHED, LAST_SIGNAL }; static GQuark quark_content_actors = 0; static guint content_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_INTERFACE (ClutterContent, clutter_content, G_TYPE_OBJECT) static gboolean clutter_content_real_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height) { if (width != NULL) *width = 0.f; if (height != NULL) *height = 0.f; return FALSE; } static void clutter_content_real_attached (ClutterContent *content, ClutterActor *actor) { } static void clutter_content_real_detached (ClutterContent *content, ClutterActor *actor) { } static void clutter_content_real_invalidate (ClutterContent *content) { } static void clutter_content_real_invalidate_size (ClutterContent *content) { } static void clutter_content_real_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *context, ClutterPaintContext *paint_context) { } static void clutter_content_default_init (ClutterContentInterface *iface) { quark_content_actors = g_quark_from_static_string ("-clutter-content-actors"); iface->get_preferred_size = clutter_content_real_get_preferred_size; iface->paint_content = clutter_content_real_paint_content; iface->attached = clutter_content_real_attached; iface->detached = clutter_content_real_detached; iface->invalidate = clutter_content_real_invalidate; iface->invalidate_size = clutter_content_real_invalidate_size; /** * ClutterContent::attached: * @content: the object that emitted the signal * @actor: a #ClutterActor * * This signal is emitted each time a #ClutterContent implementation is * assigned to a #ClutterActor. * * Since: 1.10 */ content_signals[ATTACHED] = g_signal_new (I_("attached"), G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContentInterface, attached), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterContent::detached: * @content: the object that emitted the signal * @actor: a #ClutterActor * * This signal is emitted each time a #ClutterContent implementation is * removed from a #ClutterActor. * * Since: 1.10 */ content_signals[DETACHED] = g_signal_new (I_("detached"), G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContentInterface, detached), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); } /** * clutter_content_invalidate: * @content: a #ClutterContent * * Invalidates a #ClutterContent. * * This function should be called by #ClutterContent implementations when * they change the way a the content should be painted regardless of the * actor state. * * Since: 1.10 */ void clutter_content_invalidate (ClutterContent *content) { GHashTable *actors; GHashTableIter iter; gpointer key_p, value_p; g_return_if_fail (CLUTTER_IS_CONTENT (content)); CLUTTER_CONTENT_GET_IFACE (content)->invalidate (content); actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors); if (actors == NULL) return; g_hash_table_iter_init (&iter, actors); while (g_hash_table_iter_next (&iter, &key_p, &value_p)) { ClutterActor *actor = key_p; g_assert (actor != NULL); clutter_actor_queue_redraw (actor); } } /** * clutter_content_invalidate_size: * @content: a #ClutterContent * * Signals that @content's size changed. Attached actors with request mode * set to %CLUTTER_REQUEST_CONTENT_SIZE will have a relayout queued. * * Attached actors with other request modes are not redrawn. To redraw them * too, use clutter_content_invalidate(). */ void clutter_content_invalidate_size (ClutterContent *content) { ClutterActor *actor; GHashTable *actors; GHashTableIter iter; g_return_if_fail (CLUTTER_IS_CONTENT (content)); CLUTTER_CONTENT_GET_IFACE (content)->invalidate_size (content); actors = g_object_get_qdata (G_OBJECT (content), quark_content_actors); if (actors == NULL) return; g_hash_table_iter_init (&iter, actors); while (g_hash_table_iter_next (&iter, (gpointer *) &actor, NULL)) { ClutterRequestMode request_mode; g_assert (actor != NULL); request_mode = clutter_actor_get_request_mode (actor); if (request_mode == CLUTTER_REQUEST_CONTENT_SIZE) _clutter_actor_queue_only_relayout (actor); } } /*< private > * _clutter_content_attached: * @content: a #ClutterContent * @actor: a #ClutterActor * * Attaches @actor to the @content. * * This function should be used internally every time a #ClutterActor * is associated to a #ClutterContent, to set up a backpointer from * the @content to the @actor. * * This function will invoke the #ClutterContentInterface.attached() virtual * function. */ void _clutter_content_attached (ClutterContent *content, ClutterActor *actor) { GObject *obj = G_OBJECT (content); GHashTable *actors; actors = g_object_get_qdata (obj, quark_content_actors); if (actors == NULL) { actors = g_hash_table_new (NULL, NULL); g_object_set_qdata_full (obj, quark_content_actors, actors, (GDestroyNotify) g_hash_table_unref); } g_hash_table_insert (actors, actor, actor); g_signal_emit (content, content_signals[ATTACHED], 0, actor); } /*< private > * _clutter_content_detached: * @content: a #ClutterContent * @actor: a #ClutterActor * * Detaches @actor from @content. * * This function should be used internally every time a #ClutterActor * removes the association with a #ClutterContent. * * This function will invoke the #ClutterContentInterface.detached() virtual * function. */ void _clutter_content_detached (ClutterContent *content, ClutterActor *actor) { GObject *obj = G_OBJECT (content); GHashTable *actors; actors = g_object_get_qdata (obj, quark_content_actors); g_assert (actors != NULL); g_hash_table_remove (actors, actor); if (g_hash_table_size (actors) == 0) g_object_set_qdata (obj, quark_content_actors, NULL); g_signal_emit (content, content_signals[DETACHED], 0, actor); } /*< private > * _clutter_content_paint_content: * @content: a #ClutterContent * @actor: a #ClutterActor * @node: a #ClutterPaintNode * @paint_context: a #ClutterPaintContext * * Creates the render tree for the @content and @actor. * * This function will invoke the #ClutterContentInterface.paint_content() * virtual function. */ void _clutter_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *node, ClutterPaintContext *paint_context) { CLUTTER_CONTENT_GET_IFACE (content)->paint_content (content, actor, node, paint_context); } /** * clutter_content_get_preferred_size: * @content: a #ClutterContent * @width: (out): return location for the natural width of the content * @height: (out): return location for the natural height of the content * * Retrieves the natural size of the @content, if any. * * The natural size of a #ClutterContent is defined as the size the content * would have regardless of the allocation of the actor that is painting it, * for instance the size of an image data. * * Return value: %TRUE if the content has a preferred size, and %FALSE * otherwise * * Since: 1.10 */ gboolean clutter_content_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height) { g_return_val_if_fail (CLUTTER_IS_CONTENT (content), FALSE); return CLUTTER_CONTENT_GET_IFACE (content)->get_preferred_size (content, width, height); } muffin-6.4.1/clutter/clutter/clutter-shader-effect.h0000664000175000017500000001067214723361714021445 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_SHADER_EFFECT_H__ #define __CLUTTER_SHADER_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_SHADER_EFFECT (clutter_shader_effect_get_type ()) #define CLUTTER_SHADER_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SHADER_EFFECT, ClutterShaderEffect)) #define CLUTTER_IS_SHADER_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SHADER_EFFECT)) #define CLUTTER_SHADER_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SHADER_EFFECT, ClutterShaderEffectClass)) #define CLUTTER_IS_SHADER_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SHADER_EFFECT)) #define CLUTTER_SHADER_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SHADER_EFFECT, ClutterShaderEffectClass)) typedef struct _ClutterShaderEffect ClutterShaderEffect; typedef struct _ClutterShaderEffectPrivate ClutterShaderEffectPrivate; typedef struct _ClutterShaderEffectClass ClutterShaderEffectClass; /** * ClutterShaderEffect: * * The #ClutterShaderEffect structure contains * only private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterShaderEffect { /*< private >*/ ClutterOffscreenEffect parent_instance; ClutterShaderEffectPrivate *priv; }; /** * ClutterShaderEffectClass: * @get_static_shader_source: Returns the GLSL source code to use for * instances of this shader effect. Note that this function is only * called once per subclass of #ClutterShaderEffect regardless of how * many instances are used. It is expected that subclasses will return * a copy of a static string from this function. * * The #ClutterShaderEffectClass structure contains * only private data * * Since: 1.4 */ struct _ClutterShaderEffectClass { /*< private >*/ ClutterOffscreenEffectClass parent_class; /*< public >*/ gchar * (* get_static_shader_source) (ClutterShaderEffect *effect); /*< private >*/ /* padding */ void (*_clutter_shader1) (void); void (*_clutter_shader2) (void); void (*_clutter_shader3) (void); void (*_clutter_shader4) (void); void (*_clutter_shader5) (void); }; CLUTTER_EXPORT GType clutter_shader_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect * clutter_shader_effect_new (ClutterShaderType shader_type); CLUTTER_EXPORT gboolean clutter_shader_effect_set_shader_source (ClutterShaderEffect *effect, const gchar *source); CLUTTER_EXPORT void clutter_shader_effect_set_uniform (ClutterShaderEffect *effect, const gchar *name, GType gtype, gsize n_values, ...); CLUTTER_EXPORT void clutter_shader_effect_set_uniform_value (ClutterShaderEffect *effect, const gchar *name, const GValue *value); CLUTTER_EXPORT CoglHandle clutter_shader_effect_get_shader (ClutterShaderEffect *effect); CLUTTER_EXPORT CoglHandle clutter_shader_effect_get_program (ClutterShaderEffect *effect); G_END_DECLS #endif /* __CLUTTER_SHADER_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-effect.c0000664000175000017500000003105014723361714020165 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-effect * @short_description: Base class for actor effects * * The #ClutterEffect class provides a default type and API for creating * effects for generic actors. * * Effects are a #ClutterActorMeta sub-class that modify the way an actor * is painted in a way that is not part of the actor's implementation. * * Effects should be the preferred way to affect the paint sequence of an * actor without sub-classing the actor itself and overriding the * #ClutterActorClass.paint()_ virtual function. * * ## Implementing a ClutterEffect * * Creating a sub-class of #ClutterEffect requires overriding the * #ClutterEffectClass.paint() method. The implementation of the function should look * something like this: * * |[ * void effect_paint (ClutterEffect *effect, ClutterEffectPaintFlags flags) * { * // Set up initialisation of the paint such as binding a * // CoglOffscreen or other operations * * // Chain to the next item in the paint sequence. This will either call * // ‘paint’ on the next effect or just paint the actor if this is * // the last effect. * ClutterActor *actor = * clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); * * clutter_actor_continue_paint (actor); * * // perform any cleanup of state, such as popping the CoglOffscreen * } * ]| * * The effect can optionally avoid calling clutter_actor_continue_paint() to skip any * further stages of the paint sequence. This is useful for example if the effect * contains a cached image of the actor. In that case it can optimise painting by * avoiding the actor paint and instead painting the cached image. * * The %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY flag is useful in this case. Clutter will set * this flag when a redraw has been queued on the actor since it was last painted. The * effect can use this information to decide if the cached image is still valid. * * ## A simple ClutterEffect implementation * * The example below creates two rectangles: one will be painted "behind" the actor, * while another will be painted "on top" of the actor. * * The #ClutterActorMetaClass.set_actor() implementation will create the two materials * used for the two different rectangles; the #ClutterEffectClass.paint() implementation * will paint the first material using cogl_rectangle(), before continuing and then it * will paint paint the second material after. * * |[ * typedef struct { * ClutterEffect parent_instance; * * CoglHandle rect_1; * CoglHandle rect_2; * } MyEffect; * * typedef struct _ClutterEffectClass MyEffectClass; * * G_DEFINE_TYPE (MyEffect, my_effect, CLUTTER_TYPE_EFFECT); * * static void * my_effect_set_actor (ClutterActorMeta *meta, * ClutterActor *actor) * { * MyEffect *self = MY_EFFECT (meta); * * // Clear the previous state // * if (self->rect_1) * { * cogl_object_unref (self->rect_1); * self->rect_1 = NULL; * } * * if (self->rect_2) * { * cogl_object_unref (self->rect_2); * self->rect_2 = NULL; * } * * // Maintain a pointer to the actor * self->actor = actor; * * // If we've been detached by the actor then we should just bail out here * if (self->actor == NULL) * return; * * // Create a red material * self->rect_1 = cogl_material_new (); * cogl_material_set_color4f (self->rect_1, 1.0, 0.0, 0.0, 1.0); * * // Create a green material * self->rect_2 = cogl_material_new (); * cogl_material_set_color4f (self->rect_2, 0.0, 1.0, 0.0, 1.0); * } * * static gboolean * my_effect_paint (ClutterEffect *effect) * { * MyEffect *self = MY_EFFECT (effect); * gfloat width, height; * * clutter_actor_get_size (self->actor, &width, &height); * * // Paint the first rectangle in the upper left quadrant * cogl_set_source (self->rect_1); * cogl_rectangle (0, 0, width / 2, height / 2); * * // Continue to the rest of the paint sequence * clutter_actor_continue_paint (self->actor); * * // Paint the second rectangle in the lower right quadrant * cogl_set_source (self->rect_2); * cogl_rectangle (width / 2, height / 2, width, height); * } * * static void * my_effect_class_init (MyEffectClass *klass) * { * ClutterActorMetaClas *meta_class = CLUTTER_ACTOR_META_CLASS (klass); * * meta_class->set_actor = my_effect_set_actor; * * klass->paint = my_effect_paint; * } * ]| * * #ClutterEffect is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-effect.h" #include "clutter-actor-meta-private.h" #include "clutter-debug.h" #include "clutter-effect-private.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-actor-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterEffect, clutter_effect, CLUTTER_TYPE_ACTOR_META); static gboolean clutter_effect_real_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { return TRUE; } static void clutter_effect_real_post_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { } static gboolean clutter_effect_real_modify_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume) { return TRUE; } static void clutter_effect_real_paint (ClutterEffect *effect, ClutterPaintContext *paint_context, ClutterEffectPaintFlags flags) { ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect); ClutterActor *actor; gboolean pre_paint_succeeded; /* The default implementation provides a compatibility wrapper for effects that haven't migrated to use the 'paint' virtual yet. This just calls the old pre and post virtuals before chaining on */ pre_paint_succeeded = _clutter_effect_pre_paint (effect, paint_context); actor = clutter_actor_meta_get_actor (actor_meta); clutter_actor_continue_paint (actor, paint_context); if (pre_paint_succeeded) _clutter_effect_post_paint (effect, paint_context); } static void clutter_effect_real_pick (ClutterEffect *effect, ClutterPickContext *pick_context) { ClutterActorMeta *actor_meta = CLUTTER_ACTOR_META (effect); ClutterActor *actor; actor = clutter_actor_meta_get_actor (actor_meta); clutter_actor_continue_pick (actor, pick_context); } static void clutter_effect_notify (GObject *gobject, GParamSpec *pspec) { if (strcmp (pspec->name, "enabled") == 0) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject); ClutterActor *actor = clutter_actor_meta_get_actor (meta); if (actor != NULL) clutter_actor_queue_redraw (actor); } if (G_OBJECT_CLASS (clutter_effect_parent_class)->notify != NULL) G_OBJECT_CLASS (clutter_effect_parent_class)->notify (gobject, pspec); } static void clutter_effect_class_init (ClutterEffectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->notify = clutter_effect_notify; klass->pre_paint = clutter_effect_real_pre_paint; klass->post_paint = clutter_effect_real_post_paint; klass->modify_paint_volume = clutter_effect_real_modify_paint_volume; klass->paint = clutter_effect_real_paint; klass->pick = clutter_effect_real_pick; } static void clutter_effect_init (ClutterEffect *self) { } gboolean _clutter_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE); return CLUTTER_EFFECT_GET_CLASS (effect)->pre_paint (effect, paint_context); } void _clutter_effect_post_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { g_return_if_fail (CLUTTER_IS_EFFECT (effect)); CLUTTER_EFFECT_GET_CLASS (effect)->post_paint (effect, paint_context); } void _clutter_effect_paint (ClutterEffect *effect, ClutterPaintContext *paint_context, ClutterEffectPaintFlags flags) { g_return_if_fail (CLUTTER_IS_EFFECT (effect)); CLUTTER_EFFECT_GET_CLASS (effect)->paint (effect, paint_context, flags); } void _clutter_effect_pick (ClutterEffect *effect, ClutterPickContext *pick_context) { g_return_if_fail (CLUTTER_IS_EFFECT (effect)); CLUTTER_EFFECT_GET_CLASS (effect)->pick (effect, pick_context); } gboolean _clutter_effect_modify_paint_volume (ClutterEffect *effect, ClutterPaintVolume *volume) { g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE); g_return_val_if_fail (volume != NULL, FALSE); return CLUTTER_EFFECT_GET_CLASS (effect)->modify_paint_volume (effect, volume); } gboolean _clutter_effect_has_custom_paint_volume (ClutterEffect *effect) { g_return_val_if_fail (CLUTTER_IS_EFFECT (effect), FALSE); return CLUTTER_EFFECT_GET_CLASS (effect)->modify_paint_volume != clutter_effect_real_modify_paint_volume; } /** * clutter_effect_queue_repaint: * @effect: A #ClutterEffect which needs redrawing * * Queues a repaint of the effect. The effect can detect when the ‘paint’ * method is called as a result of this function because it will not * have the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY flag set. In that case the * effect is free to assume that the actor has not changed its * appearance since the last time it was painted so it doesn't need to * call clutter_actor_continue_paint() if it can draw a cached * image. This is mostly intended for effects that are using a * %CoglOffscreen to redirect the actor (such as * %ClutterOffscreenEffect). In that case the effect can save a bit of * rendering time by painting the cached texture without causing the * entire actor to be painted. * * This function can be used by effects that have their own animatable * parameters. For example, an effect which adds a varying degree of a * red tint to an actor by redirecting it through a CoglOffscreen * might have a property to specify the level of tint. When this value * changes, the underlying actor doesn't need to be redrawn so the * effect can call clutter_effect_queue_repaint() to make sure the * effect is repainted. * * Note however that modifying the position of the parent of an actor * may change the appearance of the actor because its transformation * matrix would change. In this case a redraw wouldn't be queued on * the actor itself so the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY would still * not be set. The effect can detect this case by keeping track of the * last modelview matrix that was used to render the actor and * veryifying that it remains the same in the next paint. * * Any other effects that are layered on top of the passed in effect * will still be passed the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY flag. If * anything queues a redraw on the actor without specifying an effect * or with an effect that is lower in the chain of effects than this * one then that will override this call. In that case this effect * will instead be called with the %CLUTTER_EFFECT_PAINT_ACTOR_DIRTY * flag set. * * Since: 1.8 */ void clutter_effect_queue_repaint (ClutterEffect *effect) { ClutterActor *actor; g_return_if_fail (CLUTTER_IS_EFFECT (effect)); actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); /* If the effect has no actor then nothing needs to be done */ if (actor != NULL) _clutter_actor_queue_redraw_full (actor, 0, /* flags */ NULL, /* clip volume */ effect /* effect */); } muffin-6.4.1/clutter/clutter/clutter-stage-view-private.h0000664000175000017500000000346214723361714022467 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_STAGE_VIEW_PRIVATE_H__ #define __CLUTTER_STAGE_VIEW_PRIVATE_H__ #include "clutter/clutter-stage-view.h" void clutter_stage_view_after_paint (ClutterStageView *view); gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view); void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, gboolean dirty); gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view); void clutter_stage_view_set_dirty_projection (ClutterStageView *view, gboolean dirty); void clutter_stage_view_add_redraw_clip (ClutterStageView *view, const cairo_rectangle_int_t *clip); gboolean clutter_stage_view_has_full_redraw_clip (ClutterStageView *view); gboolean clutter_stage_view_has_redraw_clip (ClutterStageView *view); const cairo_region_t * clutter_stage_view_peek_redraw_clip (ClutterStageView *view); cairo_region_t * clutter_stage_view_take_redraw_clip (ClutterStageView *view); #endif /* __CLUTTER_STAGE_VIEW_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-virtual-input-device.h0000664000175000017500000002100614723361714023016 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2016 Red Hat inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #ifndef __CLUTTER_VIRTUAL_INPUT_DEVICE_H__ #define __CLUTTER_VIRTUAL_INPUT_DEVICE_H__ #include #include #include "clutter-seat.h" #define CLUTTER_TYPE_VIRTUAL_INPUT_DEVICE (clutter_virtual_input_device_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterVirtualInputDevice, clutter_virtual_input_device, CLUTTER, VIRTUAL_INPUT_DEVICE, GObject) typedef enum _ClutterButtonState { CLUTTER_BUTTON_STATE_RELEASED, CLUTTER_BUTTON_STATE_PRESSED } ClutterButtonState; typedef enum _ClutterKeyState { CLUTTER_KEY_STATE_RELEASED, CLUTTER_KEY_STATE_PRESSED } ClutterKeyState; struct _ClutterVirtualInputDeviceClass { GObjectClass parent_class; void (*notify_relative_motion) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy); void (*notify_absolute_motion) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double x, double y); void (*notify_button) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t button, ClutterButtonState button_state); void (*notify_key) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t key, ClutterKeyState key_state); void (*notify_keyval) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t keyval, ClutterKeyState key_state); void (*notify_discrete_scroll) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source); void (*notify_scroll_continuous) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags); void (*notify_touch_down) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y); void (*notify_touch_motion) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y); void (*notify_touch_up) (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot); }; CLUTTER_EXPORT void clutter_virtual_input_device_notify_relative_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy); CLUTTER_EXPORT void clutter_virtual_input_device_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double x, double y); CLUTTER_EXPORT void clutter_virtual_input_device_notify_button (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t button, ClutterButtonState button_state); CLUTTER_EXPORT void clutter_virtual_input_device_notify_key (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t key, ClutterKeyState key_state); CLUTTER_EXPORT void clutter_virtual_input_device_notify_keyval (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t keyval, ClutterKeyState key_state); CLUTTER_EXPORT void clutter_virtual_input_device_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source); CLUTTER_EXPORT void clutter_virtual_input_device_notify_scroll_continuous (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags); CLUTTER_EXPORT void clutter_virtual_input_device_notify_touch_down (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y); CLUTTER_EXPORT void clutter_virtual_input_device_notify_touch_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y); CLUTTER_EXPORT void clutter_virtual_input_device_notify_touch_up (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot); CLUTTER_EXPORT int clutter_virtual_input_device_get_device_type (ClutterVirtualInputDevice *virtual_device); #endif /* __CLUTTER_VIRTUAL_INPUT_DEVICE_H__ */ muffin-6.4.1/clutter/clutter/clutter-marshal.list0000664000175000017500000000131114723361714021106 0ustar fabiofabioBOOLEAN:BOXED BOOLEAN:BOXED,INT,INT BOOLEAN:OBJECT,BOOLEAN BOOLEAN:OBJECT,BOXED BOOLEAN:OBJECT,BOXED,DOUBLE BOOLEAN:OBJECT,DOUBLE BOOLEAN:OBJECT,ENUM BOOLEAN:OBJECT,FLAGS BOOLEAN:STRING,UINT,FLAGS BOOLEAN:OBJECT BOOLEAN:OBJECT,FLOAT,FLOAT BOXED:UINT,UINT DOUBLE:VOID UINT:VOID VOID:BOXED,FLAGS VOID:INT64,INT64,FLOAT,BOOLEAN VOID:INT,INT VOID:INT,POINTER VOID:FLOAT,FLOAT VOID:INT,INT,INT,INT VOID:OBJECT,FLAGS VOID:OBJECT,FLAGS,BOOLEAN VOID:OBJECT,FLAGS,UINT VOID:OBJECT,FLOAT,FLOAT VOID:OBJECT,FLOAT,FLOAT,FLAGS VOID:OBJECT,OBJECT VOID:OBJECT,PARAM VOID:OBJECT,POINTER VOID:OBJECT,UINT VOID:STRING,BOOLEAN VOID:STRING,BOOLEAN,BOOLEAN VOID:STRING,INT VOID:UINT,STRING,UINT VOID:UINT,UINT VOID:STRING,INT,POINTER muffin-6.4.1/clutter/clutter/clutter-bind-constraint.c0000664000175000017500000004677714723361714022055 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-bind-constraint * @Title: ClutterBindConstraint * @Short_Description: A constraint binding the position or size of an actor * * #ClutterBindConstraint is a #ClutterConstraint that binds the * position or the size of the #ClutterActor to which it is applied * to the the position or the size of another #ClutterActor, or * "source". * * An offset can be applied to the constraint, to avoid overlapping. The offset * can also be animated. For instance, the following code will set up three * actors to be bound to the same origin: * * |[ * // source * rect[0] = clutter_rectangle_new_with_color (&red_color); * clutter_actor_set_position (rect[0], x_pos, y_pos); * clutter_actor_set_size (rect[0], 100, 100); * * // second rectangle * rect[1] = clutter_rectangle_new_with_color (&green_color); * clutter_actor_set_size (rect[1], 100, 100); * clutter_actor_set_opacity (rect[1], 0); * * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0); * clutter_actor_add_constraint_with_name (rect[1], "green-x", constraint); * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0); * clutter_actor_add_constraint_with_name (rect[1], "green-y", constraint); * * // third rectangle * rect[2] = clutter_rectangle_new_with_color (&blue_color); * clutter_actor_set_size (rect[2], 100, 100); * clutter_actor_set_opacity (rect[2], 0); * * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_X, 0.0); * clutter_actor_add_constraint_with_name (rect[2], "blue-x", constraint); * constraint = clutter_bind_constraint_new (rect[0], CLUTTER_BIND_Y, 0.0); * clutter_actor_add_constraint_with_name (rect[2], "blue-y", constraint); * ]| * * The following code animates the second and third rectangles to "expand" * them horizontally from underneath the first rectangle: * * |[ * clutter_actor_animate (rect[1], CLUTTER_EASE_OUT_CUBIC, 250, * "@constraints.green-x.offset", 100.0, * "opacity", 255, * NULL); * clutter_actor_animate (rect[2], CLUTTER_EASE_OUT_CUBIC, 250, * "@constraints.blue-x.offset", 200.0, * "opacity", 255, * NULL); * ]| * * #ClutterBindConstraint is available since Clutter 1.4 */ #include "clutter-build-config.h" #include #include "clutter-bind-constraint.h" #include "clutter-actor-meta-private.h" #include "clutter-actor-private.h" #include "clutter-constraint.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-private.h" #define CLUTTER_BIND_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BIND_CONSTRAINT, ClutterBindConstraintClass)) #define CLUTTER_IS_BIND_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BIND_CONSTRAINT)) #define CLUTTER_BIND_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BIND_CONSTRAINT, ClutterBindConstraintClass)) struct _ClutterBindConstraint { ClutterConstraint parent_instance; ClutterActor *actor; ClutterActor *source; ClutterBindCoordinate coordinate; gfloat offset; }; struct _ClutterBindConstraintClass { ClutterConstraintClass parent_class; }; enum { PROP_0, PROP_SOURCE, PROP_COORDINATE, PROP_OFFSET, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterBindConstraint, clutter_bind_constraint, CLUTTER_TYPE_CONSTRAINT); static void source_queue_relayout (ClutterActor *source, ClutterBindConstraint *bind) { if (bind->actor != NULL) _clutter_actor_queue_only_relayout (bind->actor); } static void source_destroyed (ClutterActor *actor, ClutterBindConstraint *bind) { bind->source = NULL; } static void clutter_bind_constraint_update_preferred_size (ClutterConstraint *constraint, ClutterActor *actor, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (constraint); float source_min, source_nat; if (bind->source == NULL) return; /* only these bindings affect the preferred size */ if (!(bind->coordinate == CLUTTER_BIND_WIDTH || bind->coordinate == CLUTTER_BIND_HEIGHT || bind->coordinate == CLUTTER_BIND_SIZE || bind->coordinate == CLUTTER_BIND_ALL)) return; switch (direction) { case CLUTTER_ORIENTATION_HORIZONTAL: if (bind->coordinate != CLUTTER_BIND_HEIGHT) { clutter_actor_get_preferred_width (bind->source, for_size, &source_min, &source_nat); *minimum_size = source_min; *natural_size = source_nat; } break; case CLUTTER_ORIENTATION_VERTICAL: if (bind->coordinate != CLUTTER_BIND_WIDTH) { clutter_actor_get_preferred_height (bind->source, for_size, &source_min, &source_nat); *minimum_size = source_min; *natural_size = source_nat; } break; } } static void clutter_bind_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (constraint); gfloat source_width, source_height; gfloat actor_width, actor_height; graphene_point3d_t source_position; source_position = GRAPHENE_POINT3D_INIT (0.f, 0.f, 0.f); if (bind->source == NULL) return; source_position.x = clutter_actor_get_x (bind->source); source_position.y = clutter_actor_get_y (bind->source); clutter_actor_get_size (bind->source, &source_width, &source_height); clutter_actor_box_get_size (allocation, &actor_width, &actor_height); switch (bind->coordinate) { case CLUTTER_BIND_X: allocation->x1 = source_position.x + bind->offset; allocation->x2 = allocation->x1 + actor_width; break; case CLUTTER_BIND_Y: allocation->y1 = source_position.y + bind->offset; allocation->y2 = allocation->y1 + actor_height; break; case CLUTTER_BIND_POSITION: allocation->x1 = source_position.x + bind->offset; allocation->y1 = source_position.y + bind->offset; allocation->x2 = allocation->x1 + actor_width; allocation->y2 = allocation->y1 + actor_height; break; case CLUTTER_BIND_WIDTH: allocation->x2 = allocation->x1 + source_width + bind->offset; break; case CLUTTER_BIND_HEIGHT: allocation->y2 = allocation->y1 + source_height + bind->offset; break; case CLUTTER_BIND_SIZE: allocation->x2 = allocation->x1 + source_width + bind->offset; allocation->y2 = allocation->y1 + source_height + bind->offset; break; case CLUTTER_BIND_ALL: allocation->x1 = source_position.x + bind->offset; allocation->y1 = source_position.y + bind->offset; allocation->x2 = allocation->x1 + source_width + bind->offset; allocation->y2 = allocation->y1 + source_height + bind->offset; break; default: g_assert_not_reached (); break; } clutter_actor_box_clamp_to_pixel (allocation); } static void clutter_bind_constraint_set_actor (ClutterActorMeta *meta, ClutterActor *new_actor) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (meta); ClutterActorMetaClass *parent; if (new_actor != NULL && bind->source != NULL && clutter_actor_contains (new_actor, bind->source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (bind->source), _clutter_actor_get_debug_name (new_actor), _clutter_actor_meta_get_debug_name (meta)); return; } /* store the pointer to the actor, for later use */ bind->actor = new_actor; parent = CLUTTER_ACTOR_META_CLASS (clutter_bind_constraint_parent_class); parent->set_actor (meta, new_actor); } static void clutter_bind_constraint_dispose (GObject *gobject) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject); if (bind->source != NULL) { g_signal_handlers_disconnect_by_func (bind->source, G_CALLBACK (source_destroyed), bind); g_signal_handlers_disconnect_by_func (bind->source, G_CALLBACK (source_queue_relayout), bind); bind->source = NULL; } G_OBJECT_CLASS (clutter_bind_constraint_parent_class)->dispose (gobject); } static void clutter_bind_constraint_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: clutter_bind_constraint_set_source (bind, g_value_get_object (value)); break; case PROP_COORDINATE: clutter_bind_constraint_set_coordinate (bind, g_value_get_enum (value)); break; case PROP_OFFSET: clutter_bind_constraint_set_offset (bind, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bind_constraint_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBindConstraint *bind = CLUTTER_BIND_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: g_value_set_object (value, bind->source); break; case PROP_COORDINATE: g_value_set_enum (value, bind->coordinate); break; case PROP_OFFSET: g_value_set_float (value, bind->offset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_bind_constraint_class_init (ClutterBindConstraintClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_bind_constraint_set_property; gobject_class->get_property = clutter_bind_constraint_get_property; gobject_class->dispose = clutter_bind_constraint_dispose; meta_class->set_actor = clutter_bind_constraint_set_actor; constraint_class->update_allocation = clutter_bind_constraint_update_allocation; constraint_class->update_preferred_size = clutter_bind_constraint_update_preferred_size; /** * ClutterBindConstraint:source: * * The #ClutterActor used as the source for the binding. * * The #ClutterActor must not be contained inside the actor associated * to the constraint. * * Since: 1.4 */ obj_props[PROP_SOURCE] = g_param_spec_object ("source", P_("Source"), P_("The source of the binding"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterBindConstraint:coordinate: * * The coordinate to be bound * * Since: 1.4 */ obj_props[PROP_COORDINATE] = g_param_spec_enum ("coordinate", P_("Coordinate"), P_("The coordinate to bind"), CLUTTER_TYPE_BIND_COORDINATE, CLUTTER_BIND_X, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterBindConstraint:offset: * * The offset, in pixels, to be applied to the binding * * Since: 1.4 */ obj_props[PROP_OFFSET] = g_param_spec_float ("offset", P_("Offset"), P_("The offset in pixels to apply to the binding"), -G_MAXFLOAT, G_MAXFLOAT, 0.0f, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_bind_constraint_init (ClutterBindConstraint *self) { self->actor = NULL; self->source = NULL; self->coordinate = CLUTTER_BIND_X; self->offset = 0.0f; } /** * clutter_bind_constraint_new: * @source: (allow-none): the #ClutterActor to use as the source of * the binding, or %NULL * @coordinate: the coordinate to bind * @offset: the offset to apply to the binding, in pixels * * Creates a new constraint, binding a #ClutterActor's position to * the given @coordinate of the position of @source * * Return value: the newly created #ClutterBindConstraint * * Since: 1.4 */ ClutterConstraint * clutter_bind_constraint_new (ClutterActor *source, ClutterBindCoordinate coordinate, gfloat offset) { g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL); return g_object_new (CLUTTER_TYPE_BIND_CONSTRAINT, "source", source, "coordinate", coordinate, "offset", offset, NULL); } /** * clutter_bind_constraint_set_source: * @constraint: a #ClutterBindConstraint * @source: (allow-none): a #ClutterActor, or %NULL to unset the source * * Sets the source #ClutterActor for the constraint * * Since: 1.4 */ void clutter_bind_constraint_set_source (ClutterBindConstraint *constraint, ClutterActor *source) { ClutterActor *old_source, *actor; ClutterActorMeta *meta; g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); if (constraint->source == source) return; meta = CLUTTER_ACTOR_META (constraint); actor = clutter_actor_meta_get_actor (meta); if (source != NULL && actor != NULL) { if (clutter_actor_contains (actor, source)) { g_warning (G_STRLOC ": The source actor '%s' is contained " "by the actor '%s' associated to the constraint " "'%s'", _clutter_actor_get_debug_name (source), _clutter_actor_get_debug_name (actor), _clutter_actor_meta_get_debug_name (meta)); return; } } old_source = constraint->source; if (old_source != NULL) { g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_destroyed), constraint); g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_queue_relayout), constraint); } constraint->source = source; if (constraint->source != NULL) { g_signal_connect (constraint->source, "queue-relayout", G_CALLBACK (source_queue_relayout), constraint); g_signal_connect (constraint->source, "destroy", G_CALLBACK (source_destroyed), constraint); if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); } g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_SOURCE]); } /** * clutter_bind_constraint_get_source: * @constraint: a #ClutterBindConstraint * * Retrieves the #ClutterActor set using clutter_bind_constraint_set_source() * * Return value: (transfer none): a pointer to the source actor * * Since: 1.4 */ ClutterActor * clutter_bind_constraint_get_source (ClutterBindConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint), NULL); return constraint->source; } /** * clutter_bind_constraint_set_coordinate: * @constraint: a #ClutterBindConstraint * @coordinate: the coordinate to bind * * Sets the coordinate to bind in the constraint * * Since: 1.4 */ void clutter_bind_constraint_set_coordinate (ClutterBindConstraint *constraint, ClutterBindCoordinate coordinate) { g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint)); if (constraint->coordinate == coordinate) return; constraint->coordinate = coordinate; if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_COORDINATE]); } /** * clutter_bind_constraint_get_coordinate: * @constraint: a #ClutterBindConstraint * * Retrieves the bound coordinate of the constraint * * Return value: the bound coordinate * * Since: 1.4 */ ClutterBindCoordinate clutter_bind_constraint_get_coordinate (ClutterBindConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint), CLUTTER_BIND_X); return constraint->coordinate; } /** * clutter_bind_constraint_set_offset: * @constraint: a #ClutterBindConstraint * @offset: the offset to apply, in pixels * * Sets the offset to be applied to the constraint * * Since: 1.4 */ void clutter_bind_constraint_set_offset (ClutterBindConstraint *constraint, gfloat offset) { g_return_if_fail (CLUTTER_IS_BIND_CONSTRAINT (constraint)); if (fabs (constraint->offset - offset) < 0.00001f) return; constraint->offset = offset; if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_OFFSET]); } /** * clutter_bind_constraint_get_offset: * @constraint: a #ClutterBindConstraint * * Retrieves the offset set using clutter_bind_constraint_set_offset() * * Return value: the offset, in pixels * * Since: 1.4 */ gfloat clutter_bind_constraint_get_offset (ClutterBindConstraint *bind) { g_return_val_if_fail (CLUTTER_IS_BIND_CONSTRAINT (bind), 0.0); return bind->offset; } muffin-6.4.1/clutter/clutter/clutter-transition-group.h0000664000175000017500000000662514723361714022274 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_TRANSITION_GROUP_H__ #define __CLUTTER_TRANSITION_GROUP_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_TRANSITION_GROUP (clutter_transition_group_get_type ()) #define CLUTTER_TRANSITION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TRANSITION_GROUP, ClutterTransitionGroup)) #define CLUTTER_IS_TRANSITION_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TRANSITION_GROUP)) #define CLUTTER_TRANSITION_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TRANSITION_GROUP, ClutterTransitionGroupClass)) #define CLUTTER_IS_TRANSITION_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TRANSITION_GROUP)) #define CLUTTER_TRANSITION_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TRANSITION_GROUP, ClutterTransitionGroup)) typedef struct _ClutterTransitionGroupPrivate ClutterTransitionGroupPrivate; typedef struct _ClutterTransitionGroupClass ClutterTransitionGroupClass; /** * ClutterTransitionGroup: * * The #ClutterTransitionGroup structure contains * private data and should only be accessed using the provided API. * * Since: 1.12 */ struct _ClutterTransitionGroup { /*< private >*/ ClutterTransition parent_instance; ClutterTransitionGroupPrivate *priv; }; /** * ClutterTransitionGroupClass: * * The #ClutterTransitionGroupClass structure * contains only private data. * * Since: 1.12 */ struct _ClutterTransitionGroupClass { /*< private >*/ ClutterTransitionClass parent_class; gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_transition_group_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterTransition * clutter_transition_group_new (void); CLUTTER_EXPORT void clutter_transition_group_add_transition (ClutterTransitionGroup *group, ClutterTransition *transition); CLUTTER_EXPORT void clutter_transition_group_remove_transition (ClutterTransitionGroup *group, ClutterTransition *transition); CLUTTER_EXPORT void clutter_transition_group_remove_all (ClutterTransitionGroup *group); G_END_DECLS #endif /* __CLUTTER_TRANSITION_GROUP_H__ */ muffin-6.4.1/clutter/clutter/clutter-pick-context-private.h0000664000175000017500000000167014723361714023023 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef CLUTTER_PICK_CONTEXT_PRIVATE_H #define CLUTTER_PICK_CONTEXT_PRIVATE_H #include "clutter-pick-context.h" ClutterPickContext * clutter_pick_context_new_for_view (ClutterStageView *view); #endif /* CLUTTER_PICK_CONTEXT_PRIVATE_H */ muffin-6.4.1/clutter/clutter/clutter-stage-private.h0000664000175000017500000002111214723361714021507 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_STAGE_PRIVATE_H__ #define __CLUTTER_STAGE_PRIVATE_H__ #include #include #include #include #include G_BEGIN_DECLS typedef struct _ClutterStageQueueRedrawEntry ClutterStageQueueRedrawEntry; /* stage */ ClutterStageWindow *_clutter_stage_get_default_window (void); void clutter_stage_paint_view (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip); void clutter_stage_emit_before_update (ClutterStage *stage); void clutter_stage_emit_before_paint (ClutterStage *stage); void clutter_stage_emit_after_paint (ClutterStage *stage); void clutter_stage_emit_after_update (ClutterStage *stage); CLUTTER_EXPORT void _clutter_stage_set_window (ClutterStage *stage, ClutterStageWindow *stage_window); CLUTTER_EXPORT ClutterStageWindow *_clutter_stage_get_window (ClutterStage *stage); void _clutter_stage_get_projection_matrix (ClutterStage *stage, CoglMatrix *projection); void _clutter_stage_dirty_projection (ClutterStage *stage); void _clutter_stage_set_viewport (ClutterStage *stage, float x, float y, float width, float height); void _clutter_stage_get_viewport (ClutterStage *stage, float *x, float *y, float *width, float *height); void _clutter_stage_dirty_viewport (ClutterStage *stage); void _clutter_stage_maybe_setup_viewport (ClutterStage *stage, ClutterStageView *view); void _clutter_stage_maybe_relayout (ClutterActor *stage); gboolean _clutter_stage_needs_update (ClutterStage *stage); gboolean _clutter_stage_do_update (ClutterStage *stage); CLUTTER_EXPORT void _clutter_stage_queue_event (ClutterStage *stage, ClutterEvent *event, gboolean copy_event); gboolean _clutter_stage_has_queued_events (ClutterStage *stage); void _clutter_stage_process_queued_events (ClutterStage *stage); void _clutter_stage_update_input_devices (ClutterStage *stage); gint64 _clutter_stage_get_update_time (ClutterStage *stage); void _clutter_stage_clear_update_time (ClutterStage *stage); gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage); int64_t _clutter_stage_get_next_presentation_time (ClutterStage *stage); void clutter_stage_log_pick (ClutterStage *stage, const graphene_point_t *vertices, ClutterActor *actor); void clutter_stage_push_pick_clip (ClutterStage *stage, const graphene_point_t *vertices); void clutter_stage_pop_pick_clip (ClutterStage *stage); ClutterActor *_clutter_stage_do_pick (ClutterStage *stage, float x, float y, ClutterPickMode mode); ClutterPaintVolume *_clutter_stage_paint_volume_stack_allocate (ClutterStage *stage); void _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage); const ClutterPlane *_clutter_stage_get_clip (ClutterStage *stage); ClutterStageQueueRedrawEntry *_clutter_stage_queue_actor_redraw (ClutterStage *stage, ClutterStageQueueRedrawEntry *entry, ClutterActor *actor, const ClutterPaintVolume *clip); void _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry); void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device, ClutterActor *actor); ClutterActor * _clutter_stage_get_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device); void _clutter_stage_remove_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device); void _clutter_stage_add_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence, ClutterActor *actor); ClutterActor * _clutter_stage_get_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence); void _clutter_stage_remove_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence); CLUTTER_EXPORT ClutterStageState _clutter_stage_get_state (ClutterStage *stage); CLUTTER_EXPORT gboolean _clutter_stage_is_activated (ClutterStage *stage); CLUTTER_EXPORT gboolean _clutter_stage_update_state (ClutterStage *stage, ClutterStageState unset_state, ClutterStageState set_state); void _clutter_stage_set_scale_factor (ClutterStage *stage, int factor); gboolean _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, graphene_rect_t *rect, float *view_scale); void _clutter_stage_presented (ClutterStage *stage, CoglFrameEvent frame_event, ClutterFrameInfo *frame_info); GList * _clutter_stage_peek_stage_views (ClutterStage *stage); void clutter_stage_queue_actor_relayout (ClutterStage *stage, ClutterActor *actor); G_END_DECLS #endif /* __CLUTTER_STAGE_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-child-meta.c0000664000175000017500000001167714723361714020755 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Jorn Baayen * Emmanuele Bassi * Tomas Frydrych * Øyvind Kolås * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-child-meta * @short_description: Wrapper for actors inside a container * * #ClutterChildMeta is a wrapper object created by #ClutterContainer * implementations in order to store child-specific data and properties. * * A #ClutterChildMeta wraps a #ClutterActor inside a #ClutterContainer. * * #ClutterChildMeta is available since Clutter 0.8 */ #include "clutter-build-config.h" #include "clutter-child-meta.h" #include "clutter-container.h" #include "clutter-debug.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterChildMeta, clutter_child_meta, G_TYPE_OBJECT); enum { PROP_0, PROP_CONTAINER, PROP_ACTOR, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static void clutter_child_meta_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterChildMeta *child_meta = CLUTTER_CHILD_META (object); switch (prop_id) { case PROP_CONTAINER: child_meta->container = g_value_get_object (value); break; case PROP_ACTOR: child_meta->actor = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_child_meta_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterChildMeta *child_meta = CLUTTER_CHILD_META (object); switch (prop_id) { case PROP_CONTAINER: g_value_set_object (value, child_meta->container); break; case PROP_ACTOR: g_value_set_object (value, child_meta->actor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_child_meta_class_init (ClutterChildMetaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_child_meta_set_property; gobject_class->get_property = clutter_child_meta_get_property; /** * ClutterChildMeta:container: * * The #ClutterContainer that created this #ClutterChildMeta. * * Since: 0.8 */ obj_props[PROP_CONTAINER] = g_param_spec_object ("container", P_("Container"), P_("The container that created this data"), CLUTTER_TYPE_CONTAINER, G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE); /** * ClutterChildMeta:actor: * * The #ClutterActor being wrapped by this #ClutterChildMeta * * Since: 0.8 */ obj_props[PROP_ACTOR] = g_param_spec_object ("actor", P_("Actor"), P_("The actor wrapped by this data"), CLUTTER_TYPE_ACTOR, G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_child_meta_init (ClutterChildMeta *self) { } /** * clutter_child_meta_get_container: * @data: a #ClutterChildMeta * * Retrieves the container using @data * * Return value: (transfer none): a #ClutterContainer * * Since: 0.8 */ ClutterContainer * clutter_child_meta_get_container (ClutterChildMeta *data) { g_return_val_if_fail (CLUTTER_IS_CHILD_META (data), NULL); return data->container; } /** * clutter_child_meta_get_actor: * @data: a #ClutterChildMeta * * Retrieves the actor wrapped by @data * * Return value: (transfer none): a #ClutterActor * * Since: 0.8 */ ClutterActor * clutter_child_meta_get_actor (ClutterChildMeta *data) { g_return_val_if_fail (CLUTTER_IS_CHILD_META (data), NULL); return data->actor; } muffin-6.4.1/clutter/clutter/clutter-binding-pool.c0000664000175000017500000006566214723361714021332 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-binding-pool * @short_description: Pool for key bindings * * #ClutterBindingPool is a data structure holding a set of key bindings. * Each key binding associates a key symbol (eventually with modifiers) * to an action. A callback function is associated to each action. * * For a given key symbol and modifier mask combination there can be only one * action; for each action there can be only one callback. There can be * multiple actions with the same name, and the same callback can be used * to handle multiple key bindings. * * Actors requiring key bindings should create a new #ClutterBindingPool * inside their class initialization function and then install actions * like this: * * |[ * static void * foo_class_init (FooClass *klass) * { * ClutterBindingPool *binding_pool; * * binding_pool = clutter_binding_pool_get_for_class (klass); * * clutter_binding_pool_install_action (binding_pool, "move-up", * CLUTTER_Up, 0, * G_CALLBACK (foo_action_move_up), * NULL, NULL); * clutter_binding_pool_install_action (binding_pool, "move-up", * CLUTTER_KP_Up, 0, * G_CALLBACK (foo_action_move_up), * NULL, NULL); * } * ]| * * The callback has a signature of: * * |[ * gboolean (* callback) (GObject *instance, * const gchar *action_name, * guint key_val, * ClutterModifierType modifiers, * gpointer user_data); * ]| * * The actor should then override the #ClutterActor::key-press-event and * use clutter_binding_pool_activate() to match a #ClutterKeyEvent structure * to one of the actions: * * |[ * ClutterBindingPool *pool; * * // retrieve the binding pool for the type of the actor * pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); * * // activate any callback matching the key symbol and modifiers * // mask of the key event. the returned value can be directly * // used to signal that the actor has handled the event. * return clutter_binding_pool_activate (pool, * key_event->keyval, * key_event->modifier_state, * G_OBJECT (actor)); * ]| * * The clutter_binding_pool_activate() function will return %FALSE if * no action for the given key binding was found, if the action was * blocked (using clutter_binding_pool_block_action()) or if the * key binding handler returned %FALSE. * * #ClutterBindingPool is available since Clutter 1.0 */ #include "clutter-build-config.h" #include "clutter-binding-pool.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" #define CLUTTER_BINDING_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CLUTTER_TYPE_BINDING_POOL, ClutterBindingPoolClass)) #define CLUTTER_IS_BINDING_POOL_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_BINDING_POOL)) #define CLUTTER_BINDING_POOL_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_BINDING_POOL, ClutterBindingPoolClass)) #define BINDING_MOD_MASK ((CLUTTER_SHIFT_MASK | \ CLUTTER_CONTROL_MASK | \ CLUTTER_MOD1_MASK | \ CLUTTER_SUPER_MASK | \ CLUTTER_HYPER_MASK | \ CLUTTER_META_MASK) | CLUTTER_RELEASE_MASK) typedef struct _ClutterBindingEntry ClutterBindingEntry; static GSList *clutter_binding_pools = NULL; static GQuark key_class_bindings = 0; struct _ClutterBindingPool { GObject parent_instance; gchar *name; /* interned string, do not free */ GSList *entries; GHashTable *entries_hash; }; struct _ClutterBindingPoolClass { GObjectClass parent_class; }; struct _ClutterBindingEntry { gchar *name; /* interned string, do not free */ guint key_val; ClutterModifierType modifiers; GClosure *closure; guint is_blocked : 1; }; enum { PROP_0, PROP_NAME, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterBindingPool, clutter_binding_pool, G_TYPE_OBJECT); static guint binding_entry_hash (gconstpointer v) { const ClutterBindingEntry *e = v; guint h; h = e->key_val; h ^= e->modifiers; return h; } static gint binding_entry_compare (gconstpointer v1, gconstpointer v2) { const ClutterBindingEntry *e1 = v1; const ClutterBindingEntry *e2 = v2; return (e1->key_val == e2->key_val && e1->modifiers == e2->modifiers); } static ClutterBindingEntry * binding_entry_new (const gchar *name, guint key_val, ClutterModifierType modifiers) { ClutterBindingEntry *entry; modifiers = modifiers & BINDING_MOD_MASK; entry = g_slice_new (ClutterBindingEntry); entry->key_val = key_val; entry->modifiers = modifiers; entry->name = (gchar *) g_intern_string (name); entry->closure = NULL; entry->is_blocked = FALSE; return entry; } static ClutterBindingEntry * binding_pool_lookup_entry (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers) { ClutterBindingEntry lookup_entry = { 0, }; lookup_entry.key_val = key_val; lookup_entry.modifiers = modifiers; return g_hash_table_lookup (pool->entries_hash, &lookup_entry); } static void binding_entry_free (gpointer data) { if (G_LIKELY (data)) { ClutterBindingEntry *entry = data; g_closure_unref (entry->closure); g_slice_free (ClutterBindingEntry, entry); } } static void clutter_binding_pool_finalize (GObject *gobject) { ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject); /* remove from the pools */ clutter_binding_pools = g_slist_remove (clutter_binding_pools, pool); g_hash_table_destroy (pool->entries_hash); g_slist_free_full (pool->entries, (GDestroyNotify) binding_entry_free); G_OBJECT_CLASS (clutter_binding_pool_parent_class)->finalize (gobject); } static void clutter_binding_pool_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject); switch (prop_id) { case PROP_NAME: pool->name = (gchar *) g_intern_string (g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_binding_pool_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject); switch (prop_id) { case PROP_NAME: g_value_set_string (value, pool->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_binding_pool_constructed (GObject *gobject) { ClutterBindingPool *pool = CLUTTER_BINDING_POOL (gobject); /* bad monkey! bad, bad monkey! */ if (G_UNLIKELY (pool->name == NULL)) g_critical ("No name set for ClutterBindingPool %p", pool); if (G_OBJECT_CLASS (clutter_binding_pool_parent_class)->constructed) G_OBJECT_CLASS (clutter_binding_pool_parent_class)->constructed (gobject); } static void clutter_binding_pool_class_init (ClutterBindingPoolClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructed = clutter_binding_pool_constructed; gobject_class->set_property = clutter_binding_pool_set_property; gobject_class->get_property = clutter_binding_pool_get_property; gobject_class->finalize = clutter_binding_pool_finalize; /** * ClutterBindingPool:name: * * The unique name of the #ClutterBindingPool. * * Since: 1.0 */ obj_props[PROP_NAME] = g_param_spec_string ("name", P_("Name"), P_("The unique name of the binding pool"), NULL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_binding_pool_init (ClutterBindingPool *pool) { pool->name = NULL; pool->entries = NULL; pool->entries_hash = g_hash_table_new (binding_entry_hash, binding_entry_compare); clutter_binding_pools = g_slist_prepend (clutter_binding_pools, pool); } /** * clutter_binding_pool_new: * @name: the name of the binding pool * * Creates a new #ClutterBindingPool that can be used to store * key bindings for an actor. The @name must be a unique identifier * for the binding pool, so that clutter_binding_pool_find() will * be able to return the correct binding pool. * * Return value: the newly created binding pool with the given * name. Use g_object_unref() when done. * * Since: 1.0 */ ClutterBindingPool * clutter_binding_pool_new (const gchar *name) { ClutterBindingPool *pool; g_return_val_if_fail (name != NULL, NULL); pool = clutter_binding_pool_find (name); if (G_UNLIKELY (pool)) { g_warning ("A binding pool named '%s' is already present " "in the binding pools list", pool->name); return NULL; } return g_object_new (CLUTTER_TYPE_BINDING_POOL, "name", name, NULL); } /** * clutter_binding_pool_get_for_class: * @klass: a #GObjectClass pointer * * Retrieves the #ClutterBindingPool for the given #GObject class * and, eventually, creates it. This function is a wrapper around * clutter_binding_pool_new() and uses the class type name as the * unique name for the binding pool. * * Calling this function multiple times will return the same * #ClutterBindingPool. * * A binding pool for a class can also be retrieved using * clutter_binding_pool_find() with the class type name: * * |[ * pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (instance)); * ]| * * Return value: (transfer none): the binding pool for the given class. * The returned #ClutterBindingPool is owned by Clutter and should not * be freed directly * * Since: 1.0 */ ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass) { ClutterBindingPool *pool; g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); if (G_UNLIKELY (key_class_bindings == 0)) key_class_bindings = g_quark_from_static_string ("clutter-bindings-set"); pool = g_dataset_id_get_data (klass, key_class_bindings); if (pool) return pool; pool = clutter_binding_pool_new (G_OBJECT_CLASS_NAME (klass)); g_dataset_id_set_data_full (klass, key_class_bindings, pool, g_object_unref); return pool; } /** * clutter_binding_pool_find: * @name: the name of the binding pool to find * * Finds the #ClutterBindingPool with @name. * * Return value: (transfer none): a pointer to the #ClutterBindingPool, or %NULL * * Since: 1.0 */ ClutterBindingPool * clutter_binding_pool_find (const gchar *name) { GSList *l; g_return_val_if_fail (name != NULL, NULL); for (l = clutter_binding_pools; l != NULL; l = l->next) { ClutterBindingPool *pool = l->data; if (g_str_equal (pool->name, (gpointer) name)) return pool; } return NULL; } /** * clutter_binding_pool_install_action: * @pool: a #ClutterBindingPool * @action_name: the name of the action * @key_val: key symbol * @modifiers: bitmask of modifiers * @callback: (type Clutter.BindingActionFunc): function to be called * when the action is activated * @data: data to be passed to @callback * @notify: function to be called when the action is removed * from the pool * * Installs a new action inside a #ClutterBindingPool. The action * is bound to @key_val and @modifiers. * * The same action name can be used for multiple @key_val, @modifiers * pairs. * * When an action has been activated using clutter_binding_pool_activate() * the passed @callback will be invoked (with @data). * * Actions can be blocked with clutter_binding_pool_block_action() * and then unblocked using clutter_binding_pool_unblock_action(). * * Since: 1.0 */ void clutter_binding_pool_install_action (ClutterBindingPool *pool, const gchar *action_name, guint key_val, ClutterModifierType modifiers, GCallback callback, gpointer data, GDestroyNotify notify) { ClutterBindingEntry *entry; GClosure *closure; g_return_if_fail (pool != NULL); g_return_if_fail (action_name != NULL); g_return_if_fail (key_val != 0); g_return_if_fail (callback != NULL); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (G_UNLIKELY (entry)) { g_warning ("There already is an action '%s' for the given " "key symbol of %d (modifiers: %d) installed inside " "the binding pool.", entry->name, entry->key_val, entry->modifiers); return; } else entry = binding_entry_new (action_name, key_val, modifiers); closure = g_cclosure_new (callback, data, (GClosureNotify) notify); entry->closure = g_closure_ref (closure); g_closure_sink (closure); if (G_CLOSURE_NEEDS_MARSHAL (closure)) { GClosureMarshal marshal; marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS; g_closure_set_marshal (closure, marshal); } pool->entries = g_slist_prepend (pool->entries, entry); g_hash_table_insert (pool->entries_hash, entry, entry); } /** * clutter_binding_pool_install_closure: * @pool: a #ClutterBindingPool * @action_name: the name of the action * @key_val: key symbol * @modifiers: bitmask of modifiers * @closure: a #GClosure * * A #GClosure variant of clutter_binding_pool_install_action(). * * Installs a new action inside a #ClutterBindingPool. The action * is bound to @key_val and @modifiers. * * The same action name can be used for multiple @key_val, @modifiers * pairs. * * When an action has been activated using clutter_binding_pool_activate() * the passed @closure will be invoked. * * Actions can be blocked with clutter_binding_pool_block_action() * and then unblocked using clutter_binding_pool_unblock_action(). * * Since: 1.0 */ void clutter_binding_pool_install_closure (ClutterBindingPool *pool, const gchar *action_name, guint key_val, ClutterModifierType modifiers, GClosure *closure) { ClutterBindingEntry *entry; g_return_if_fail (pool != NULL); g_return_if_fail (action_name != NULL); g_return_if_fail (key_val != 0); g_return_if_fail (closure != NULL); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (G_UNLIKELY (entry)) { g_warning ("There already is an action '%s' for the given " "key symbol of %d (modifiers: %d) installed inside " "the binding pool.", entry->name, entry->key_val, entry->modifiers); return; } else entry = binding_entry_new (action_name, key_val, modifiers); entry->closure = g_closure_ref (closure); g_closure_sink (closure); if (G_CLOSURE_NEEDS_MARSHAL (closure)) { GClosureMarshal marshal; marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS; g_closure_set_marshal (closure, marshal); } pool->entries = g_slist_prepend (pool->entries, entry); g_hash_table_insert (pool->entries_hash, entry, entry); } /** * clutter_binding_pool_override_action: * @pool: a #ClutterBindingPool * @key_val: key symbol * @modifiers: bitmask of modifiers * @callback: function to be called when the action is activated * @data: data to be passed to @callback * @notify: function to be called when the action is removed * from the pool * * Allows overriding the action for @key_val and @modifiers inside a * #ClutterBindingPool. See clutter_binding_pool_install_action(). * * When an action has been activated using clutter_binding_pool_activate() * the passed @callback will be invoked (with @data). * * Actions can be blocked with clutter_binding_pool_block_action() * and then unblocked using clutter_binding_pool_unblock_action(). * * Since: 1.0 */ void clutter_binding_pool_override_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GCallback callback, gpointer data, GDestroyNotify notify) { ClutterBindingEntry *entry; GClosure *closure; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); g_return_if_fail (callback != NULL); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (G_UNLIKELY (entry == NULL)) { g_warning ("There is no action for the given key symbol " "of %d (modifiers: %d) installed inside the " "binding pool.", key_val, modifiers); return; } if (entry->closure) { g_closure_unref (entry->closure); entry->closure = NULL; } closure = g_cclosure_new (callback, data, (GClosureNotify) notify); entry->closure = g_closure_ref (closure); g_closure_sink (closure); if (G_CLOSURE_NEEDS_MARSHAL (closure)) { GClosureMarshal marshal; marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS; g_closure_set_marshal (closure, marshal); } } /** * clutter_binding_pool_override_closure: * @pool: a #ClutterBindingPool * @key_val: key symbol * @modifiers: bitmask of modifiers * @closure: a #GClosure * * A #GClosure variant of clutter_binding_pool_override_action(). * * Allows overriding the action for @key_val and @modifiers inside a * #ClutterBindingPool. See clutter_binding_pool_install_closure(). * * When an action has been activated using clutter_binding_pool_activate() * the passed @callback will be invoked (with @data). * * Actions can be blocked with clutter_binding_pool_block_action() * and then unblocked using clutter_binding_pool_unblock_action(). * * Since: 1.0 */ void clutter_binding_pool_override_closure (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GClosure *closure) { ClutterBindingEntry *entry; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); g_return_if_fail (closure != NULL); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (G_UNLIKELY (entry == NULL)) { g_warning ("There is no action for the given key symbol " "of %d (modifiers: %d) installed inside the " "binding pool.", key_val, modifiers); return; } if (entry->closure) { g_closure_unref (entry->closure); entry->closure = NULL; } entry->closure = g_closure_ref (closure); g_closure_sink (closure); if (G_CLOSURE_NEEDS_MARSHAL (closure)) { GClosureMarshal marshal; marshal = _clutter_marshal_BOOLEAN__STRING_UINT_FLAGS; g_closure_set_marshal (closure, marshal); } } /** * clutter_binding_pool_find_action: * @pool: a #ClutterBindingPool * @key_val: a key symbol * @modifiers: a bitmask for the modifiers * * Retrieves the name of the action matching the given key symbol * and modifiers bitmask. * * Return value: the name of the action, if found, or %NULL. The * returned string is owned by the binding pool and should never * be modified or freed * * Since: 1.0 */ const gchar * clutter_binding_pool_find_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers) { ClutterBindingEntry *entry; g_return_val_if_fail (pool != NULL, NULL); g_return_val_if_fail (key_val != 0, NULL); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (!entry) return NULL; return entry->name; } /** * clutter_binding_pool_remove_action: * @pool: a #ClutterBindingPool * @key_val: a key symbol * @modifiers: a bitmask for the modifiers * * Removes the action matching the given @key_val, @modifiers pair, * if any exists. * * Since: 1.0 */ void clutter_binding_pool_remove_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers) { ClutterBindingEntry remove_entry = { 0, }; GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (key_val != 0); modifiers = modifiers & BINDING_MOD_MASK; remove_entry.key_val = key_val; remove_entry.modifiers = modifiers; for (l = pool->entries; l != NULL; l = l->data) { ClutterBindingEntry *e = l->data; if (e->key_val == remove_entry.key_val && e->modifiers == remove_entry.modifiers) { pool->entries = g_slist_remove_link (pool->entries, l); break; } } g_hash_table_remove (pool->entries_hash, &remove_entry); } static gboolean clutter_binding_entry_invoke (ClutterBindingEntry *entry, GObject *gobject) { GValue params[4] = { G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT, G_VALUE_INIT }; GValue result = G_VALUE_INIT; gboolean retval = TRUE; g_value_init (¶ms[0], G_TYPE_OBJECT); g_value_set_object (¶ms[0], gobject); g_value_init (¶ms[1], G_TYPE_STRING); g_value_set_static_string (¶ms[1], entry->name); g_value_init (¶ms[2], G_TYPE_UINT); g_value_set_uint (¶ms[2], entry->key_val); g_value_init (¶ms[3], CLUTTER_TYPE_MODIFIER_TYPE); g_value_set_flags (¶ms[3], entry->modifiers); g_value_init (&result, G_TYPE_BOOLEAN); g_closure_invoke (entry->closure, &result, 4, params, NULL); retval = g_value_get_boolean (&result); g_value_unset (&result); g_value_unset (¶ms[0]); g_value_unset (¶ms[1]); g_value_unset (¶ms[2]); g_value_unset (¶ms[3]); return retval; } /** * clutter_binding_pool_activate: * @pool: a #ClutterBindingPool * @key_val: the key symbol * @modifiers: bitmask for the modifiers * @gobject: a #GObject * * Activates the callback associated to the action that is * bound to the @key_val and @modifiers pair. * * The callback has the following signature: * * |[ * void (* callback) (GObject *gobject, * const gchar *action_name, * guint key_val, * ClutterModifierType modifiers, * gpointer user_data); * ]| * * Where the #GObject instance is @gobject and the user data * is the one passed when installing the action with * clutter_binding_pool_install_action(). * * If the action bound to the @key_val, @modifiers pair has been * blocked using clutter_binding_pool_block_action(), the callback * will not be invoked, and this function will return %FALSE. * * Return value: %TRUE if an action was found and was activated * * Since: 1.0 */ gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GObject *gobject) { ClutterBindingEntry *entry = NULL; g_return_val_if_fail (pool != NULL, FALSE); g_return_val_if_fail (key_val != 0, FALSE); g_return_val_if_fail (G_IS_OBJECT (gobject), FALSE); modifiers = (modifiers & BINDING_MOD_MASK); entry = binding_pool_lookup_entry (pool, key_val, modifiers); if (!entry) return FALSE; if (!entry->is_blocked) return clutter_binding_entry_invoke (entry, gobject); return FALSE; } /** * clutter_binding_pool_block_action: * @pool: a #ClutterBindingPool * @action_name: an action name * * Blocks all the actions with name @action_name inside @pool. * * Since: 1.0 */ void clutter_binding_pool_block_action (ClutterBindingPool *pool, const gchar *action_name) { GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (action_name != NULL); for (l = pool->entries; l != NULL; l = l->next) { ClutterBindingEntry *entry = l->data; if (g_str_equal (entry->name, (gpointer) action_name)) entry->is_blocked = TRUE; } } /** * clutter_binding_pool_unblock_action: * @pool: a #ClutterBindingPool * @action_name: an action name * * Unblockes all the actions with name @action_name inside @pool. * * Unblocking an action does not cause the callback bound to it to * be invoked in case clutter_binding_pool_activate() was called on * an action previously blocked with clutter_binding_pool_block_action(). * * Since: 1.0 */ void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, const gchar *action_name) { GSList *l; g_return_if_fail (pool != NULL); g_return_if_fail (action_name != NULL); for (l = pool->entries; l != NULL; l = l->next) { ClutterBindingEntry *entry = l->data; if (g_str_equal (entry->name, (gpointer) action_name)) entry->is_blocked = FALSE; } } muffin-6.4.1/clutter/clutter/clutter-input-pointer-a11y.c0000664000175000017500000004720314723361714022326 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Olivier Fourdan * * This reimplements in Clutter the same behavior as mousetweaks original * implementation by Gerd Kohlberger * mousetweaks Copyright (C) 2007-2010 Gerd Kohlberger */ #include "clutter-build-config.h" #include "clutter-enum-types.h" #include "clutter-input-device.h" #include "clutter-input-device-private.h" #include "clutter-input-pointer-a11y-private.h" #include "clutter-main.h" #include "clutter-virtual-input-device.h" static gboolean is_secondary_click_enabled (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return (settings.controls & CLUTTER_A11Y_SECONDARY_CLICK_ENABLED); } static gboolean is_dwell_click_enabled (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return (settings.controls & CLUTTER_A11Y_DWELL_ENABLED); } static unsigned int get_secondary_click_delay (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return settings.secondary_click_delay; } static unsigned int get_dwell_delay (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return settings.dwell_delay; } static unsigned int get_dwell_threshold (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return settings.dwell_threshold; } static ClutterPointerA11yDwellMode get_dwell_mode (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return settings.dwell_mode; } static ClutterPointerA11yDwellClickType get_dwell_click_type (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); return settings.dwell_click_type; } static ClutterPointerA11yDwellClickType get_dwell_click_type_for_direction (ClutterInputDevice *device, ClutterPointerA11yDwellDirection direction) { ClutterPointerA11ySettings settings; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); if (direction == settings.dwell_gesture_single) return CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY; else if (direction == settings.dwell_gesture_double) return CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE; else if (direction == settings.dwell_gesture_drag) return CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG; else if (direction == settings.dwell_gesture_secondary) return CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY; return CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE; } static void emit_button_press (ClutterInputDevice *device, gint button) { clutter_virtual_input_device_notify_button (device->accessibility_virtual_device, g_get_monotonic_time (), button, CLUTTER_BUTTON_STATE_PRESSED); } static void emit_button_release (ClutterInputDevice *device, gint button) { clutter_virtual_input_device_notify_button (device->accessibility_virtual_device, g_get_monotonic_time (), button, CLUTTER_BUTTON_STATE_RELEASED); } static void emit_button_click (ClutterInputDevice *device, gint button) { emit_button_press (device, button); emit_button_release (device, button); } static void restore_dwell_position (ClutterInputDevice *device) { clutter_virtual_input_device_notify_absolute_motion (device->accessibility_virtual_device, g_get_monotonic_time (), device->ptr_a11y_data->dwell_x, device->ptr_a11y_data->dwell_y); } static gboolean trigger_secondary_click (gpointer data) { ClutterInputDevice *device = data; device->ptr_a11y_data->secondary_click_triggered = TRUE; device->ptr_a11y_data->secondary_click_timer = 0; g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-stopped", device, CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK, TRUE); return G_SOURCE_REMOVE; } static void start_secondary_click_timeout (ClutterInputDevice *device) { unsigned int delay = get_secondary_click_delay (device); device->ptr_a11y_data->secondary_click_timer = clutter_threads_add_timeout (delay, trigger_secondary_click, device); g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-started", device, CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK, delay); } static void stop_secondary_click_timeout (ClutterInputDevice *device) { if (device->ptr_a11y_data->secondary_click_timer) { g_clear_handle_id (&device->ptr_a11y_data->secondary_click_timer, g_source_remove); g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-stopped", device, CLUTTER_A11Y_TIMEOUT_TYPE_SECONDARY_CLICK, FALSE); } device->ptr_a11y_data->secondary_click_triggered = FALSE; } static gboolean pointer_has_moved (ClutterInputDevice *device) { float dx, dy; gint threshold; dx = device->ptr_a11y_data->dwell_x - device->ptr_a11y_data->current_x; dy = device->ptr_a11y_data->dwell_y - device->ptr_a11y_data->current_y; threshold = get_dwell_threshold (device); /* Pythagorean theorem */ return ((dx * dx) + (dy * dy)) > (threshold * threshold); } static gboolean is_secondary_click_pending (ClutterInputDevice *device) { return device->ptr_a11y_data->secondary_click_timer != 0; } static gboolean is_secondary_click_triggered (ClutterInputDevice *device) { return device->ptr_a11y_data->secondary_click_triggered; } static gboolean is_dwell_click_pending (ClutterInputDevice *device) { return device->ptr_a11y_data->dwell_timer != 0; } static gboolean is_dwell_dragging (ClutterInputDevice *device) { return device->ptr_a11y_data->dwell_drag_started; } static gboolean is_dwell_gesturing (ClutterInputDevice *device) { return device->ptr_a11y_data->dwell_gesture_started; } static gboolean has_button_pressed (ClutterInputDevice *device) { return device->ptr_a11y_data->n_btn_pressed > 0; } static gboolean should_start_secondary_click_timeout (ClutterInputDevice *device) { return !is_dwell_dragging (device); } static gboolean should_start_dwell (ClutterInputDevice *device) { /* We should trigger a dwell if we've not already started one, and if * no button is currently pressed or we are in the middle of a dwell * drag action. */ return !is_dwell_click_pending (device) && (is_dwell_dragging (device) || !has_button_pressed (device)); } static gboolean should_stop_dwell (ClutterInputDevice *device) { /* We should stop a dwell if the motion exceeds the threshold, unless * we've started a gesture, because we want to keep the original dwell * location to both detect a gesture and restore the original pointer * location once the gesture is finished. */ return pointer_has_moved (device) && !is_dwell_gesturing (device); } static gboolean should_update_dwell_position (ClutterInputDevice *device) { return !is_dwell_gesturing (device) && !is_dwell_click_pending (device) && !is_secondary_click_pending (device); } static void update_dwell_click_type (ClutterInputDevice *device) { ClutterPointerA11ySettings settings; ClutterPointerA11yDwellClickType dwell_click_type; clutter_seat_get_pointer_a11y_settings (device->seat, &settings); dwell_click_type = settings.dwell_click_type; switch (dwell_click_type) { case CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE: case CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY: case CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE: dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY; break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG: if (!is_dwell_dragging (device)) dwell_click_type = CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY; break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY: case CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE: default: break; } if (dwell_click_type != settings.dwell_click_type) { settings.dwell_click_type = dwell_click_type; clutter_seat_set_pointer_a11y_settings (device->seat, &settings); g_signal_emit_by_name (device->seat, "ptr-a11y-dwell-click-type-changed", dwell_click_type); } } static void emit_dwell_click (ClutterInputDevice *device, ClutterPointerA11yDwellClickType dwell_click_type) { switch (dwell_click_type) { case CLUTTER_A11Y_DWELL_CLICK_TYPE_PRIMARY: emit_button_click (device, CLUTTER_BUTTON_PRIMARY); break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_DOUBLE: emit_button_click (device, CLUTTER_BUTTON_PRIMARY); emit_button_click (device, CLUTTER_BUTTON_PRIMARY); break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG: if (is_dwell_dragging (device)) { emit_button_release (device, CLUTTER_BUTTON_PRIMARY); device->ptr_a11y_data->dwell_drag_started = FALSE; } else { emit_button_press (device, CLUTTER_BUTTON_PRIMARY); device->ptr_a11y_data->dwell_drag_started = TRUE; } break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_SECONDARY: emit_button_click (device, CLUTTER_BUTTON_SECONDARY); break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_MIDDLE: emit_button_click (device, CLUTTER_BUTTON_MIDDLE); break; case CLUTTER_A11Y_DWELL_CLICK_TYPE_NONE: default: break; } } static ClutterPointerA11yDwellDirection get_dwell_direction (ClutterInputDevice *device) { float dx, dy; dx = ABS (device->ptr_a11y_data->dwell_x - device->ptr_a11y_data->current_x); dy = ABS (device->ptr_a11y_data->dwell_y - device->ptr_a11y_data->current_y); /* The pointer hasn't moved */ if (!pointer_has_moved (device)) return CLUTTER_A11Y_DWELL_DIRECTION_NONE; if (device->ptr_a11y_data->dwell_x < device->ptr_a11y_data->current_x) { if (dx > dy) return CLUTTER_A11Y_DWELL_DIRECTION_LEFT; } else { if (dx > dy) return CLUTTER_A11Y_DWELL_DIRECTION_RIGHT; } if (device->ptr_a11y_data->dwell_y < device->ptr_a11y_data->current_y) return CLUTTER_A11Y_DWELL_DIRECTION_UP; return CLUTTER_A11Y_DWELL_DIRECTION_DOWN; } static gboolean trigger_clear_dwell_gesture (gpointer data) { ClutterInputDevice *device = data; device->ptr_a11y_data->dwell_timer = 0; device->ptr_a11y_data->dwell_gesture_started = FALSE; return G_SOURCE_REMOVE; } static gboolean trigger_dwell_gesture (gpointer data) { ClutterInputDevice *device = data; ClutterPointerA11yDwellDirection direction; unsigned int delay = get_dwell_delay (device); restore_dwell_position (device); direction = get_dwell_direction (device); emit_dwell_click (device, get_dwell_click_type_for_direction (device, direction)); /* Do not clear the gesture right away, otherwise we'll start another one */ device->ptr_a11y_data->dwell_timer = clutter_threads_add_timeout (delay, trigger_clear_dwell_gesture, device); g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-stopped", device, CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE, TRUE); return G_SOURCE_REMOVE; } static void start_dwell_gesture_timeout (ClutterInputDevice *device) { unsigned int delay = get_dwell_delay (device); device->ptr_a11y_data->dwell_timer = clutter_threads_add_timeout (delay, trigger_dwell_gesture, device); device->ptr_a11y_data->dwell_gesture_started = TRUE; g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-started", device, CLUTTER_A11Y_TIMEOUT_TYPE_GESTURE, delay); } static gboolean trigger_dwell_click (gpointer data) { ClutterInputDevice *device = data; device->ptr_a11y_data->dwell_timer = 0; g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-stopped", device, CLUTTER_A11Y_TIMEOUT_TYPE_DWELL, TRUE); if (get_dwell_mode (device) == CLUTTER_A11Y_DWELL_MODE_GESTURE) { if (is_dwell_dragging (device)) emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG); else start_dwell_gesture_timeout (device); } else { emit_dwell_click (device, get_dwell_click_type (device)); update_dwell_click_type (device); } return G_SOURCE_REMOVE; } static void start_dwell_timeout (ClutterInputDevice *device) { unsigned int delay = get_dwell_delay (device); device->ptr_a11y_data->dwell_timer = clutter_threads_add_timeout (delay, trigger_dwell_click, device); g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-started", device, CLUTTER_A11Y_TIMEOUT_TYPE_DWELL, delay); } static void stop_dwell_timeout (ClutterInputDevice *device) { if (device->ptr_a11y_data->dwell_timer) { g_clear_handle_id (&device->ptr_a11y_data->dwell_timer, g_source_remove); device->ptr_a11y_data->dwell_gesture_started = FALSE; g_signal_emit_by_name (device->seat, "ptr-a11y-timeout-stopped", device, CLUTTER_A11Y_TIMEOUT_TYPE_DWELL, FALSE); } } static gboolean trigger_dwell_position_timeout (gpointer data) { ClutterInputDevice *device = data; device->ptr_a11y_data->dwell_position_timer = 0; if (is_dwell_click_enabled (device)) { if (!pointer_has_moved (device)) start_dwell_timeout (device); } return G_SOURCE_REMOVE; } static void start_dwell_position_timeout (ClutterInputDevice *device) { device->ptr_a11y_data->dwell_position_timer = clutter_threads_add_timeout (100, trigger_dwell_position_timeout, device); } static void stop_dwell_position_timeout (ClutterInputDevice *device) { g_clear_handle_id (&device->ptr_a11y_data->dwell_position_timer, g_source_remove); } static void update_dwell_position (ClutterInputDevice *device) { device->ptr_a11y_data->dwell_x = device->ptr_a11y_data->current_x; device->ptr_a11y_data->dwell_y = device->ptr_a11y_data->current_y; } static void update_current_position (ClutterInputDevice *device, float x, float y) { device->ptr_a11y_data->current_x = x; device->ptr_a11y_data->current_y = y; } static gboolean is_device_core_pointer (ClutterInputDevice *device) { ClutterInputDevice *core_pointer; core_pointer = clutter_seat_get_pointer (device->seat); if (core_pointer == NULL) return FALSE; return (core_pointer == device); } void _clutter_input_pointer_a11y_add_device (ClutterInputDevice *device) { if (!is_device_core_pointer (device)) return; device->accessibility_virtual_device = clutter_seat_create_virtual_device (device->seat, CLUTTER_POINTER_DEVICE); device->ptr_a11y_data = g_new0 (ClutterPtrA11yData, 1); } void _clutter_input_pointer_a11y_remove_device (ClutterInputDevice *device) { if (!is_device_core_pointer (device)) return; /* Terminate a drag if started */ if (is_dwell_dragging (device)) emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG); stop_dwell_position_timeout (device); stop_dwell_timeout (device); stop_secondary_click_timeout (device); g_clear_pointer (&device->ptr_a11y_data, g_free); } void _clutter_input_pointer_a11y_on_motion_event (ClutterInputDevice *device, float x, float y) { if (!is_device_core_pointer (device)) return; if (!_clutter_is_input_pointer_a11y_enabled (device)) return; update_current_position (device, x, y); if (is_secondary_click_enabled (device)) { if (pointer_has_moved (device)) stop_secondary_click_timeout (device); } if (is_dwell_click_enabled (device)) { stop_dwell_position_timeout (device); if (should_stop_dwell (device)) stop_dwell_timeout (device); if (should_start_dwell (device)) start_dwell_position_timeout (device); } if (should_update_dwell_position (device)) update_dwell_position (device); } void _clutter_input_pointer_a11y_on_button_event (ClutterInputDevice *device, int button, gboolean pressed) { if (!is_device_core_pointer (device)) return; if (!_clutter_is_input_pointer_a11y_enabled (device)) return; if (pressed) { device->ptr_a11y_data->n_btn_pressed++; stop_dwell_position_timeout (device); if (is_dwell_click_enabled (device)) stop_dwell_timeout (device); if (is_dwell_dragging (device)) stop_dwell_timeout (device); if (is_secondary_click_enabled (device)) { if (button == CLUTTER_BUTTON_PRIMARY) { if (should_start_secondary_click_timeout (device)) start_secondary_click_timeout (device); } else if (is_secondary_click_pending (device)) { stop_secondary_click_timeout (device); } } } else { if (has_button_pressed (device)) device->ptr_a11y_data->n_btn_pressed--; if (is_secondary_click_triggered (device)) { emit_button_click (device, CLUTTER_BUTTON_SECONDARY); stop_secondary_click_timeout (device); } if (is_secondary_click_pending (device)) stop_secondary_click_timeout (device); if (is_dwell_dragging (device)) emit_dwell_click (device, CLUTTER_A11Y_DWELL_CLICK_TYPE_DRAG); } } gboolean _clutter_is_input_pointer_a11y_enabled (ClutterInputDevice *device) { g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), FALSE); return (is_secondary_click_enabled (device) || is_dwell_click_enabled (device)); } muffin-6.4.1/clutter/clutter/clutter-shader-types.h0000664000175000017500000000702414723361714021352 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2008 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_SHADER_TYPES_H__ #define __CLUTTER_SHADER_TYPES_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_SHADER_FLOAT (clutter_shader_float_get_type ()) #define CLUTTER_TYPE_SHADER_INT (clutter_shader_int_get_type ()) #define CLUTTER_TYPE_SHADER_MATRIX (clutter_shader_matrix_get_type ()) typedef struct _ClutterShaderFloat ClutterShaderFloat; typedef struct _ClutterShaderInt ClutterShaderInt; typedef struct _ClutterShaderMatrix ClutterShaderMatrix; /** * CLUTTER_VALUE_HOLDS_SHADER_FLOAT: * @x: a #GValue * * Evaluates to %TRUE if @x holds a #ClutterShaderFloat. * * Since: 1.0 */ #define CLUTTER_VALUE_HOLDS_SHADER_FLOAT(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_SHADER_FLOAT)) /** * CLUTTER_VALUE_HOLDS_SHADER_INT: * @x: a #GValue * * Evaluates to %TRUE if @x holds a #ClutterShaderInt. * * Since: 1.0 */ #define CLUTTER_VALUE_HOLDS_SHADER_INT(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_SHADER_INT)) /** * CLUTTER_VALUE_HOLDS_SHADER_MATRIX: * @x: a #GValue * * Evaluates to %TRUE if @x holds a #ClutterShaderMatrix. * * Since: 1.0 */ #define CLUTTER_VALUE_HOLDS_SHADER_MATRIX(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_SHADER_MATRIX)) CLUTTER_EXPORT GType clutter_shader_float_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GType clutter_shader_int_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GType clutter_shader_matrix_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_value_set_shader_float (GValue *value, gint size, const gfloat *floats); CLUTTER_EXPORT void clutter_value_set_shader_int (GValue *value, gint size, const gint *ints); CLUTTER_EXPORT void clutter_value_set_shader_matrix (GValue *value, gint size, const gfloat *matrix); CLUTTER_EXPORT const gfloat * clutter_value_get_shader_float (const GValue *value, gsize *length); CLUTTER_EXPORT const gint * clutter_value_get_shader_int (const GValue *value, gsize *length); CLUTTER_EXPORT const gfloat * clutter_value_get_shader_matrix (const GValue *value, gsize *length); G_END_DECLS #endif /* __CLUTTER_SHADER_TYPES_H__ */ muffin-6.4.1/clutter/clutter/clutter-settings.h0000664000175000017500000000151714723361714020603 0ustar fabiofabio#ifndef __CLUTTER_SETTINGS_H__ #define __CLUTTER_SETTINGS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_SETTINGS (clutter_settings_get_type ()) #define CLUTTER_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SETTINGS, ClutterSettings)) #define CLUTTER_IS_SETTINGS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SETTINGS)) typedef struct _ClutterSettings ClutterSettings; typedef struct _ClutterSettingsClass ClutterSettingsClass; CLUTTER_EXPORT GType clutter_settings_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterSettings *clutter_settings_get_default (void); G_END_DECLS #endif /* __CLUTTER_SETTINGS_H__ */ muffin-6.4.1/clutter/clutter/clutter-actor-private.h0000664000175000017500000003422214723361714021522 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_ACTOR_PRIVATE_H__ #define __CLUTTER_ACTOR_PRIVATE_H__ #include G_BEGIN_DECLS /*< private > * ClutterRedrawFlags: * @CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION: Tells clutter the maximum * extents of what needs to be redrawn lies within the actors * current allocation. (Only use this for 2D actors though because * any actor with depth may be projected outside of its allocation) * * Flags passed to the clutter_actor_queue_redraw_with_clip () * function * * Since: 1.6 */ typedef enum { CLUTTER_REDRAW_CLIPPED_TO_ALLOCATION = 1 << 0 } ClutterRedrawFlags; /*< private > * ClutterActorTraverseFlags: * CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST: Traverse the graph in * a depth first order. * CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST: Traverse the graph in a * breadth first order. * * Controls some options for how clutter_actor_traverse() iterates * through the graph. */ typedef enum { CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST = 1L<<0, CLUTTER_ACTOR_TRAVERSE_BREADTH_FIRST = 1L<<1 } ClutterActorTraverseFlags; /*< private > * ClutterActorTraverseVisitFlags: * CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE: Continue traversing as * normal * CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN: Don't traverse the * children of the last visited actor. (Not applicable when using * %CLUTTER_ACTOR_TRAVERSE_DEPTH_FIRST_POST_ORDER since the children * are visited before having an opportunity to bail out) * CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK: Immediately bail out without * visiting any more actors. * * Each time an actor is visited during a scenegraph traversal the * ClutterTraverseCallback can return a set of flags that may affect * the continuing traversal. It may stop traversal completely, just * skip over children for the current actor or continue as normal. */ typedef enum { CLUTTER_ACTOR_TRAVERSE_VISIT_CONTINUE = 1L<<0, CLUTTER_ACTOR_TRAVERSE_VISIT_SKIP_CHILDREN = 1L<<1, CLUTTER_ACTOR_TRAVERSE_VISIT_BREAK = 1L<<2 } ClutterActorTraverseVisitFlags; /*< private > * ClutterTraverseCallback: * * The callback prototype used with clutter_actor_traverse. The * returned flags can be used to affect the continuing traversal * either by continuing as normal, skipping over children of an * actor or bailing out completely. */ typedef ClutterActorTraverseVisitFlags (*ClutterTraverseCallback) (ClutterActor *actor, gint depth, gpointer user_data); /*< private > * ClutterForeachCallback: * @actor: The actor being iterated * @user_data: The private data specified when starting the iteration * * A generic callback for iterating over actor, such as with * _clutter_actor_foreach_child. The difference when compared to * #ClutterCallback is that it returns a boolean so it is possible to break * out of an iteration early. * * Return value: %TRUE to continue iterating or %FALSE to break iteration * early. */ typedef gboolean (*ClutterForeachCallback) (ClutterActor *actor, gpointer user_data); typedef struct _AnchorCoord AnchorCoord; typedef struct _SizeRequest SizeRequest; typedef struct _ClutterLayoutInfo ClutterLayoutInfo; typedef struct _ClutterTransformInfo ClutterTransformInfo; typedef struct _ClutterAnimationInfo ClutterAnimationInfo; /* Internal helper struct to represent a point that can be stored in either direct pixel coordinates or as a fraction of the actor's size. It is used for the anchor point, scale center and rotation centers. */ struct _AnchorCoord { gboolean is_fractional; union { /* Used when is_fractional == TRUE */ struct { gdouble x; gdouble y; } fraction; /* Use when is_fractional == FALSE */ graphene_point3d_t units; } v; }; struct _SizeRequest { guint age; gfloat for_size; gfloat min_size; gfloat natural_size; }; /*< private > * ClutterLayoutInfo: * @fixed_pos: the fixed position of the actor * @margin: the composed margin of the actor * @x_align: the horizontal alignment, if the actor expands horizontally * @y_align: the vertical alignment, if the actor expands vertically * @x_expand: whether the actor should expand horizontally * @y_expand: whether the actor should expand vertically * @minimum: the fixed minimum size * @natural: the fixed natural size * * Ancillary layout information for an actor. */ struct _ClutterLayoutInfo { /* fixed position coordinates */ graphene_point_t fixed_pos; ClutterMargin margin; guint x_align : 4; guint y_align : 4; guint x_expand : 1; guint y_expand : 1; graphene_size_t minimum; graphene_size_t natural; }; const ClutterLayoutInfo * _clutter_actor_get_layout_info_or_defaults (ClutterActor *self); ClutterLayoutInfo * _clutter_actor_get_layout_info (ClutterActor *self); ClutterLayoutInfo * _clutter_actor_peek_layout_info (ClutterActor *self); struct _ClutterTransformInfo { /* rotation (angle and center) */ gdouble rx_angle; AnchorCoord rx_center; gdouble ry_angle; AnchorCoord ry_center; gdouble rz_angle; AnchorCoord rz_center; /* scaling */ gdouble scale_x; gdouble scale_y; gdouble scale_z; AnchorCoord scale_center; /* anchor point */ AnchorCoord anchor; /* translation */ graphene_point3d_t translation; /* z_position */ gfloat z_position; /* transformation center */ graphene_point_t pivot; gfloat pivot_z; CoglMatrix transform; guint transform_set : 1; CoglMatrix child_transform; guint child_transform_set : 1; }; const ClutterTransformInfo * _clutter_actor_get_transform_info_or_defaults (ClutterActor *self); ClutterTransformInfo * _clutter_actor_get_transform_info (ClutterActor *self); typedef struct _AState { guint easing_duration; guint easing_delay; ClutterAnimationMode easing_mode; } AState; struct _ClutterAnimationInfo { GArray *states; AState *cur_state; GHashTable *transitions; }; const ClutterAnimationInfo * _clutter_actor_get_animation_info_or_defaults (ClutterActor *self); ClutterAnimationInfo * _clutter_actor_get_animation_info (ClutterActor *self); ClutterTransition * _clutter_actor_create_transition (ClutterActor *self, GParamSpec *pspec, ...); gboolean _clutter_actor_foreach_child (ClutterActor *self, ClutterForeachCallback callback, gpointer user_data); void _clutter_actor_traverse (ClutterActor *actor, ClutterActorTraverseFlags flags, ClutterTraverseCallback before_children_callback, ClutterTraverseCallback after_children_callback, gpointer user_data); ClutterActor * _clutter_actor_get_stage_internal (ClutterActor *actor); void _clutter_actor_apply_modelview_transform (ClutterActor *self, CoglMatrix *matrix); void _clutter_actor_apply_relative_transformation_matrix (ClutterActor *self, ClutterActor *ancestor, CoglMatrix *matrix); void _clutter_actor_rerealize (ClutterActor *self, ClutterCallback callback, gpointer data); void _clutter_actor_set_in_clone_paint (ClutterActor *self, gboolean is_in_clone_paint); void _clutter_actor_set_enable_model_view_transform (ClutterActor *self, gboolean enable); void _clutter_actor_set_enable_paint_unmapped (ClutterActor *self, gboolean enable); void _clutter_actor_set_has_pointer (ClutterActor *self, gboolean has_pointer); void _clutter_actor_set_has_key_focus (ClutterActor *self, gboolean has_key_focus); void _clutter_actor_queue_redraw_with_clip (ClutterActor *self, ClutterRedrawFlags flags, const ClutterPaintVolume *clip_volume); void _clutter_actor_queue_redraw_full (ClutterActor *self, ClutterRedrawFlags flags, const ClutterPaintVolume *volume, ClutterEffect *effect); void _clutter_actor_finish_queue_redraw (ClutterActor *self, ClutterPaintVolume *clip); gboolean _clutter_actor_set_default_paint_volume (ClutterActor *self, GType check_gtype, ClutterPaintVolume *volume); const gchar * _clutter_actor_get_debug_name (ClutterActor *self); void _clutter_actor_push_clone_paint (void); void _clutter_actor_pop_clone_paint (void); void _clutter_actor_shader_pre_paint (ClutterActor *actor, gboolean repeat); void _clutter_actor_shader_post_paint (ClutterActor *actor); ClutterActorAlign _clutter_actor_get_effective_x_align (ClutterActor *self); void _clutter_actor_handle_event (ClutterActor *actor, const ClutterEvent *event); void _clutter_actor_attach_clone (ClutterActor *actor, ClutterActor *clone); void _clutter_actor_detach_clone (ClutterActor *actor, ClutterActor *clone); void _clutter_actor_queue_redraw_on_clones (ClutterActor *actor); void _clutter_actor_queue_relayout_on_clones (ClutterActor *actor); void _clutter_actor_queue_only_relayout (ClutterActor *actor); void _clutter_actor_queue_update_resource_scale_recursive (ClutterActor *actor); gboolean _clutter_actor_get_real_resource_scale (ClutterActor *actor, float *resource_scale); ClutterPaintNode * clutter_actor_create_texture_paint_node (ClutterActor *self, CoglTexture *texture); G_END_DECLS #endif /* __CLUTTER_ACTOR_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-virtual-input-device.c0000664000175000017500000002334514723361714023021 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #include "clutter-build-config.h" #include #include "clutter-virtual-input-device.h" #include "clutter-enum-types.h" #include "clutter-private.h" #include "clutter-seat.h" enum { PROP_0, PROP_SEAT, PROP_DEVICE_TYPE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; typedef struct _ClutterVirtualInputDevicePrivate { ClutterSeat *seat; ClutterInputDeviceType device_type; } ClutterVirtualInputDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (ClutterVirtualInputDevice, clutter_virtual_input_device, G_TYPE_OBJECT) void clutter_virtual_input_device_notify_relative_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_relative_motion (virtual_device, time_us, dx, dy); } void clutter_virtual_input_device_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double x, double y) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_absolute_motion (virtual_device, time_us, x, y); } void clutter_virtual_input_device_notify_button (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t button, ClutterButtonState button_state) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_button (virtual_device, time_us, button, button_state); } void clutter_virtual_input_device_notify_key (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t key, ClutterKeyState key_state) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_key (virtual_device, time_us, key, key_state); } void clutter_virtual_input_device_notify_keyval (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t keyval, ClutterKeyState key_state) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_keyval (virtual_device, time_us, keyval, key_state); } void clutter_virtual_input_device_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_discrete_scroll (virtual_device, time_us, direction, scroll_source); } void clutter_virtual_input_device_notify_scroll_continuous (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_scroll_continuous (virtual_device, time_us, dx, dy, scroll_source, finish_flags); } void clutter_virtual_input_device_notify_touch_down (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_touch_down (virtual_device, time_us, slot, x, y); } void clutter_virtual_input_device_notify_touch_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot, double x, double y) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_touch_motion (virtual_device, time_us, slot, x, y); } void clutter_virtual_input_device_notify_touch_up (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int slot) { ClutterVirtualInputDeviceClass *klass = CLUTTER_VIRTUAL_INPUT_DEVICE_GET_CLASS (virtual_device); klass->notify_touch_up (virtual_device, time_us, slot); } int clutter_virtual_input_device_get_device_type (ClutterVirtualInputDevice *virtual_device) { ClutterVirtualInputDevicePrivate *priv = clutter_virtual_input_device_get_instance_private (virtual_device); return priv->device_type; } static void clutter_virtual_input_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterVirtualInputDevice *virtual_device = CLUTTER_VIRTUAL_INPUT_DEVICE (object); ClutterVirtualInputDevicePrivate *priv = clutter_virtual_input_device_get_instance_private (virtual_device); switch (prop_id) { case PROP_SEAT: g_value_set_object (value, priv->seat); break; case PROP_DEVICE_TYPE: g_value_set_enum (value, priv->device_type); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_virtual_input_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterVirtualInputDevice *virtual_device = CLUTTER_VIRTUAL_INPUT_DEVICE (object); ClutterVirtualInputDevicePrivate *priv = clutter_virtual_input_device_get_instance_private (virtual_device); switch (prop_id) { case PROP_SEAT: priv->seat = g_value_get_object (value); break; case PROP_DEVICE_TYPE: priv->device_type = g_value_get_enum (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_virtual_input_device_init (ClutterVirtualInputDevice *virtual_device) { } static void clutter_virtual_input_device_class_init (ClutterVirtualInputDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = clutter_virtual_input_device_get_property; object_class->set_property = clutter_virtual_input_device_set_property; obj_props[PROP_SEAT] = g_param_spec_object ("seat", P_("Seat"), P_("Seat"), CLUTTER_TYPE_SEAT, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); obj_props[PROP_DEVICE_TYPE] = g_param_spec_enum ("device-type", P_("Device type"), P_("Device type"), CLUTTER_TYPE_INPUT_DEVICE_TYPE, CLUTTER_POINTER_DEVICE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/clutter/clutter/clutter-stage-manager-private.h0000664000175000017500000000314214723361714023122 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_STAGE_MANAGER_PRIVATE_H__ #define __CLUTTER_STAGE_MANAGER_PRIVATE_H__ #include G_BEGIN_DECLS struct _ClutterStageManager { GObject parent_instance; GSList *stages; }; /* stage manager */ void _clutter_stage_manager_add_stage (ClutterStageManager *stage_manager, ClutterStage *stage); void _clutter_stage_manager_remove_stage (ClutterStageManager *stage_manager, ClutterStage *stage); void _clutter_stage_manager_set_default_stage (ClutterStageManager *stage_manager, ClutterStage *stage); G_END_DECLS #endif /* __CLUTTER_STAGE_MANAGER_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-script.h0000664000175000017500000002160614723361714020250 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_SCRIPT_H__ #define __CLUTTER_SCRIPT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_SCRIPT (clutter_script_get_type ()) #define CLUTTER_SCRIPT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_SCRIPT, ClutterScript)) #define CLUTTER_IS_SCRIPT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_SCRIPT)) #define CLUTTER_SCRIPT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SCRIPT, ClutterScriptClass)) #define CLUTTER_IS_SCRIPT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SCRIPT)) #define CLUTTER_SCRIPT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SCRIPT, ClutterScriptClass)) typedef struct _ClutterScript ClutterScript; typedef struct _ClutterScriptPrivate ClutterScriptPrivate; typedef struct _ClutterScriptClass ClutterScriptClass; /** * ClutterScriptConnectFunc: * @script: a #ClutterScript * @object: the object to connect * @signal_name: the name of the signal * @handler_name: the name of the signal handler * @connect_object: the object to connect the signal to, or %NULL * @flags: signal connection flags * @user_data: user data to pass to the signal handler * * This is the signature of a function used to connect signals. It is used * by the clutter_script_connect_signals_full() function. It is mainly * intended for interpreted language bindings, but could be useful where the * programmer wants more control over the signal connection process. * * Since: 0.6 */ typedef void (* ClutterScriptConnectFunc) (ClutterScript *script, GObject *object, const gchar *signal_name, const gchar *handler_name, GObject *connect_object, GConnectFlags flags, gpointer user_data); /** * ClutterScriptError: * @CLUTTER_SCRIPT_ERROR_INVALID_TYPE_FUNCTION: Type function not found * or invalid * @CLUTTER_SCRIPT_ERROR_INVALID_PROPERTY: Property not found or invalid * @CLUTTER_SCRIPT_ERROR_INVALID_VALUE: Invalid value * * #ClutterScript error enumeration. * * Since: 0.6 */ typedef enum { CLUTTER_SCRIPT_ERROR_INVALID_TYPE_FUNCTION, CLUTTER_SCRIPT_ERROR_INVALID_PROPERTY, CLUTTER_SCRIPT_ERROR_INVALID_VALUE } ClutterScriptError; /** * CLUTTER_SCRIPT_ERROR: * * Error domain for the #ClutterScript errors * * Since: 0.6 */ #define CLUTTER_SCRIPT_ERROR (clutter_script_error_quark ()) CLUTTER_EXPORT GQuark clutter_script_error_quark (void); /** * ClutterScript: * * The #ClutterScript structure contains only private data * and should be accessed using the provided API * * Since: 0.6 */ struct _ClutterScript { /*< private >*/ GObject parent_instance; ClutterScriptPrivate *priv; }; /** * ClutterScriptClass: * @get_type_from_name: virtual function used to map a type name * to a #GType. This function should only be overridden by * language bindings in order to map native types to #GType. * The default implementation is equivalent to g_type_from_name() * * The #ClutterScriptClass structure contains only private data * * Since: 0.6 */ struct _ClutterScriptClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ GType (* get_type_from_name) (ClutterScript *script, const gchar *type_name); /*< private >*/ /* padding, for future expansion */ void (*_clutter_reserved1) (void); void (*_clutter_reserved2) (void); void (*_clutter_reserved3) (void); void (*_clutter_reserved4) (void); void (*_clutter_reserved5) (void); void (*_clutter_reserved6) (void); void (*_clutter_reserved7) (void); void (*_clutter_reserved8) (void); }; CLUTTER_EXPORT GType clutter_script_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterScript * clutter_script_new (void); CLUTTER_EXPORT guint clutter_script_load_from_file (ClutterScript *script, const gchar *filename, GError **error); CLUTTER_EXPORT guint clutter_script_load_from_data (ClutterScript *script, const gchar *data, gssize length, GError **error); CLUTTER_EXPORT guint clutter_script_load_from_resource (ClutterScript *script, const gchar *resource_path, GError **error); CLUTTER_EXPORT GObject * clutter_script_get_object (ClutterScript *script, const gchar *name); CLUTTER_EXPORT gint clutter_script_get_objects (ClutterScript *script, const gchar *first_name, ...) G_GNUC_NULL_TERMINATED; CLUTTER_EXPORT GList * clutter_script_list_objects (ClutterScript *script); CLUTTER_EXPORT void clutter_script_unmerge_objects (ClutterScript *script, guint merge_id); CLUTTER_EXPORT void clutter_script_ensure_objects (ClutterScript *script); CLUTTER_DEPRECATED void clutter_script_add_states (ClutterScript *script, const gchar *name, ClutterState *state); CLUTTER_DEPRECATED ClutterState * clutter_script_get_states (ClutterScript *script, const gchar *name); CLUTTER_EXPORT void clutter_script_connect_signals (ClutterScript *script, gpointer user_data); CLUTTER_EXPORT void clutter_script_connect_signals_full (ClutterScript *script, ClutterScriptConnectFunc func, gpointer user_data); CLUTTER_EXPORT void clutter_script_add_search_paths (ClutterScript *script, const gchar * const paths[], gsize n_paths); CLUTTER_EXPORT gchar * clutter_script_lookup_filename (ClutterScript *script, const gchar *filename) G_GNUC_MALLOC; CLUTTER_EXPORT GType clutter_script_get_type_from_name (ClutterScript *script, const gchar *type_name); CLUTTER_EXPORT void clutter_script_set_translation_domain (ClutterScript *script, const gchar *domain); CLUTTER_EXPORT const gchar * clutter_script_get_translation_domain (ClutterScript *script); CLUTTER_EXPORT const gchar * clutter_get_script_id (GObject *gobject); G_END_DECLS #endif /* __CLUTTER_SCRIPT_H__ */ muffin-6.4.1/clutter/clutter/clutter-image.c0000664000175000017500000003313714723361714020023 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive image' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-image * @Title: ClutterImage * @Short_Description: Image data content * * #ClutterImage is a #ClutterContent implementation that displays * image data inside a #ClutterActor. * * See [image.c](https://git.gnome.org/browse/clutter/tree/examples/image-content.c?h=clutter-1.18) * for an example of how to use #ClutterImage. * * #ClutterImage is available since Clutter 1.10. */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-image.h" #include "clutter-actor-private.h" #include "clutter-color.h" #include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-paint-node.h" #include "clutter-paint-nodes.h" #include "clutter-private.h" struct _ClutterImagePrivate { CoglTexture *texture; gint width; gint height; }; static void clutter_content_iface_init (ClutterContentInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterImage, clutter_image, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterImage) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) GQuark clutter_image_error_quark (void) { return g_quark_from_static_string ("clutter-image-error-quark"); } static void update_image_size (ClutterImage *self) { gint width, height; if (self->priv->texture == NULL) return; width = cogl_texture_get_width (self->priv->texture); height = cogl_texture_get_height (self->priv->texture); if (self->priv->width == width && self->priv->height == height) return; self->priv->width = width; self->priv->height = height; clutter_content_invalidate_size (CLUTTER_CONTENT (self)); } static void clutter_image_finalize (GObject *gobject) { ClutterImagePrivate *priv = CLUTTER_IMAGE (gobject)->priv; if (priv->texture != NULL) { cogl_object_unref (priv->texture); priv->texture = NULL; } G_OBJECT_CLASS (clutter_image_parent_class)->finalize (gobject); } static void clutter_image_class_init (ClutterImageClass *klass) { G_OBJECT_CLASS (klass)->finalize = clutter_image_finalize; } static void clutter_image_init (ClutterImage *self) { self->priv = clutter_image_get_instance_private (self); } static void clutter_image_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ClutterImagePrivate *priv = CLUTTER_IMAGE (content)->priv; ClutterPaintNode *node; if (priv->texture == NULL) return; node = clutter_actor_create_texture_paint_node (actor, priv->texture); clutter_paint_node_set_static_name (node, "Image Content"); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); } static gboolean clutter_image_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height) { ClutterImagePrivate *priv = CLUTTER_IMAGE (content)->priv; if (priv->texture == NULL) return FALSE; if (width != NULL) *width = cogl_texture_get_width (priv->texture); if (height != NULL) *height = cogl_texture_get_height (priv->texture); return TRUE; } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->get_preferred_size = clutter_image_get_preferred_size; iface->paint_content = clutter_image_paint_content; } /** * clutter_image_new: * * Creates a new #ClutterImage instance. * * Return value: (transfer full): the newly created #ClutterImage instance. * Use g_object_unref() when done. * * Since: 1.10 */ ClutterContent * clutter_image_new (void) { return g_object_new (CLUTTER_TYPE_IMAGE, NULL); } /** * clutter_image_set_data: * @image: a #ClutterImage * @data: (array): the image data, as an array of bytes * @pixel_format: the Cogl pixel format of the image data * @width: the width of the image data * @height: the height of the image data * @row_stride: the length of each row inside @data * @error: return location for a #GError, or %NULL * * Sets the image data to be displayed by @image. * * If the image data was successfully loaded, the @image will be invalidated. * * In case of error, the @error value will be set, and this function will * return %FALSE. * * The image data is copied in texture memory. * * The image data is expected to be a linear array of RGBA or RGB pixel data; * how to retrieve that data is left to platform specific image loaders. For * instance, if you use the GdkPixbuf library: * * |[ * ClutterContent *image = clutter_image_new (); * * GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (filename, NULL); * * clutter_image_set_data (CLUTTER_IMAGE (image), * gdk_pixbuf_get_pixels (pixbuf), * gdk_pixbuf_get_has_alpha (pixbuf) * ? COGL_PIXEL_FORMAT_RGBA_8888 * : COGL_PIXEL_FORMAT_RGB_888, * gdk_pixbuf_get_width (pixbuf), * gdk_pixbuf_get_height (pixbuf), * gdk_pixbuf_get_rowstride (pixbuf), * &error); * * g_object_unref (pixbuf); * ]| * * Return value: %TRUE if the image data was successfully loaded, * and %FALSE otherwise. * * Since: 1.10 */ gboolean clutter_image_set_data (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error) { ClutterImagePrivate *priv; CoglTextureFlags flags; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); priv = image->priv; if (priv->texture != NULL) cogl_object_unref (priv->texture); flags = COGL_TEXTURE_NONE; if (width >= 512 && height >= 512) flags |= COGL_TEXTURE_NO_ATLAS; priv->texture = cogl_texture_new_from_data (width, height, flags, pixel_format, COGL_PIXEL_FORMAT_ANY, row_stride, data); if (priv->texture == NULL) { g_set_error_literal (error, CLUTTER_IMAGE_ERROR, CLUTTER_IMAGE_ERROR_INVALID_DATA, "Unable to load image data"); return FALSE; } clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_set_bytes: * @image: a #ClutterImage * @data: the image data, as a #GBytes * @pixel_format: the Cogl pixel format of the image data * @width: the width of the image data * @height: the height of the image data * @row_stride: the length of each row inside @data * @error: return location for a #GError, or %NULL * * Sets the image data stored inside a #GBytes to be displayed by @image. * * If the image data was successfully loaded, the @image will be invalidated. * * In case of error, the @error value will be set, and this function will * return %FALSE. * * The image data contained inside the #GBytes is copied in texture memory, * and no additional reference is acquired on the @data. * * Return value: %TRUE if the image data was successfully loaded, * and %FALSE otherwise. * * Since: 1.12 */ gboolean clutter_image_set_bytes (ClutterImage *image, GBytes *data, CoglPixelFormat pixel_format, guint width, guint height, guint row_stride, GError **error) { ClutterImagePrivate *priv; CoglTextureFlags flags; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); priv = image->priv; if (priv->texture != NULL) cogl_object_unref (priv->texture); flags = COGL_TEXTURE_NONE; if (width >= 512 && height >= 512) flags |= COGL_TEXTURE_NO_ATLAS; priv->texture = cogl_texture_new_from_data (width, height, flags, pixel_format, COGL_PIXEL_FORMAT_ANY, row_stride, g_bytes_get_data (data, NULL)); if (priv->texture == NULL) { g_set_error_literal (error, CLUTTER_IMAGE_ERROR, CLUTTER_IMAGE_ERROR_INVALID_DATA, "Unable to load image data"); return FALSE; } clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_set_area: * @image: a #ClutterImage * @data: (array): the image data, as an array of bytes * @pixel_format: the Cogl pixel format of the image data * @rect: a rectangle indicating the area that should be set * @row_stride: the length of each row inside @data * @error: return location for a #GError, or %NULL * * Sets the image data to be display by @image, using @rect to indicate * the position and size of the image data to be set. * * If the @image does not have any image data set when this function is * called, a new texture will be created with the size of the width and * height of the rectangle, i.e. calling this function on a newly created * #ClutterImage will be the equivalent of calling clutter_image_set_data(). * * If the image data was successfully loaded, the @image will be invalidated. * * In case of error, the @error value will be set, and this function will * return %FALSE. * * The image data is copied in texture memory. * * Return value: %TRUE if the image data was successfully loaded, * and %FALSE otherwise. * * Since: 1.10 */ gboolean clutter_image_set_area (ClutterImage *image, const guint8 *data, CoglPixelFormat pixel_format, const cairo_rectangle_int_t *area, guint row_stride, GError **error) { ClutterImagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_IMAGE (image), FALSE); g_return_val_if_fail (data != NULL, FALSE); g_return_val_if_fail (area != NULL, FALSE); priv = image->priv; if (priv->texture == NULL) { CoglTextureFlags flags = COGL_TEXTURE_NONE; if (area->width >= 512 && area->height >= 512) flags |= COGL_TEXTURE_NO_ATLAS; priv->texture = cogl_texture_new_from_data (area->width, area->height, flags, pixel_format, COGL_PIXEL_FORMAT_ANY, row_stride, data); } else { gboolean res; res = cogl_texture_set_region (priv->texture, 0, 0, area->x, area->y, area->width, area->height, area->width, area->height, pixel_format, row_stride, data); if (!res) { cogl_object_unref (priv->texture); priv->texture = NULL; } } if (priv->texture == NULL) { g_set_error_literal (error, CLUTTER_IMAGE_ERROR, CLUTTER_IMAGE_ERROR_INVALID_DATA, "Unable to load image data"); return FALSE; } clutter_content_invalidate (CLUTTER_CONTENT (image)); update_image_size (image); return TRUE; } /** * clutter_image_get_texture: * @image: a #ClutterImage * * Retrieves a pointer to the Cogl texture used by @image. * * If you change the contents of the returned Cogl texture you will need * to manually invalidate the @image with clutter_content_invalidate() * in order to update the actors using @image as their content. * * Return value: (transfer none): a pointer to the Cogl texture, or %NULL * * Since: 1.10 * Stability: unstable */ CoglTexture * clutter_image_get_texture (ClutterImage *image) { g_return_val_if_fail (CLUTTER_IS_IMAGE (image), NULL); return image->priv->texture; } muffin-6.4.1/clutter/clutter/cogl/0000775000175000017500000000000014723361714016032 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/cogl/clutter-stage-cogl.h0000664000175000017500000000455214723361714021716 0ustar fabiofabio#ifndef __CLUTTER_STAGE_COGL_H__ #define __CLUTTER_STAGE_COGL_H__ #include #include #include #include "clutter/clutter-stage-window.h" G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE_COGL (_clutter_stage_cogl_get_type ()) #define CLUTTER_STAGE_COGL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_COGL, ClutterStageCogl)) #define CLUTTER_IS_STAGE_COGL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_COGL)) #define CLUTTER_STAGE_COGL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_COGL, ClutterStageCoglClass)) #define CLUTTER_IS_STAGE_COGL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_COGL)) #define CLUTTER_STAGE_COGL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_COGL, ClutterStageCoglClass)) typedef struct _ClutterStageCogl ClutterStageCogl; typedef struct _ClutterStageCoglClass ClutterStageCoglClass; G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterStageCogl, g_object_unref) #define CLUTTER_TYPE_STAGE_VIEW_COGL (clutter_stage_view_cogl_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterStageViewCogl, clutter_stage_view_cogl, CLUTTER, STAGE_VIEW_COGL, ClutterStageView) struct _ClutterStageViewCoglClass { ClutterStageViewClass parent_class; }; struct _ClutterStageCogl { GObject parent_instance; /* the stage wrapper */ ClutterStage *wrapper; /* back pointer to the backend */ ClutterBackend *backend; float refresh_rate; int pending_swaps; gint64 last_presentation_time; gint64 update_time; int64_t last_update_time; int64_t next_presentation_time; /* We only enable clipped redraws after 2 frames, since we've seen * a lot of drivers can struggle to get going and may output some * junk frames to start with. */ unsigned int frame_count; gint last_sync_delay; }; struct _ClutterStageCoglClass { GObjectClass parent_class; }; CLUTTER_EXPORT GType _clutter_stage_cogl_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void _clutter_stage_cogl_presented (ClutterStageCogl *stage_cogl, CoglFrameEvent frame_event, ClutterFrameInfo *frame_info); G_END_DECLS #endif /* __CLUTTER_STAGE_COGL_H__ */ muffin-6.4.1/clutter/clutter/cogl/clutter-stage-cogl.c0000664000175000017500000010304014723361714021701 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2007,2008,2009,2010,2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * Authors: * Matthew Allum * Robert Bragg * Neil Roberts * Emmanuele Bassi */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-config.h" #include "clutter-stage-cogl.h" #include #include #include "clutter-actor-private.h" #include "clutter-backend-private.h" #include "clutter-debug.h" #include "clutter-event.h" #include "clutter-enum-types.h" #include "clutter-feature.h" #include "clutter-main.h" #include "clutter-private.h" #include "clutter-stage-private.h" #include "clutter-stage-view-private.h" #define MAX_STACK_RECTS 256 typedef struct _ClutterStageViewCoglPrivate { /* * List of previous damaged areas in stage view framebuffer coordinate space. */ #define DAMAGE_HISTORY_MAX 16 #define DAMAGE_HISTORY(x) ((x) & (DAMAGE_HISTORY_MAX - 1)) cairo_region_t * damage_history[DAMAGE_HISTORY_MAX]; unsigned int damage_index; } ClutterStageViewCoglPrivate; G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageViewCogl, clutter_stage_view_cogl, CLUTTER_TYPE_STAGE_VIEW) static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStageCogl, _clutter_stage_cogl, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init)); enum { PROP_0, PROP_WRAPPER, PROP_BACKEND, PROP_LAST }; static void clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window, gint sync_delay); static void clutter_stage_cogl_unrealize (ClutterStageWindow *stage_window) { CLUTTER_NOTE (BACKEND, "Unrealizing Cogl stage [%p]", stage_window); } void _clutter_stage_cogl_presented (ClutterStageCogl *stage_cogl, CoglFrameEvent frame_event, ClutterFrameInfo *frame_info) { if (frame_event == COGL_FRAME_EVENT_SYNC) { /* Early versions of the swap_event implementation in Mesa * deliver BufferSwapComplete event when not selected for, * so if we get a swap event we aren't expecting, just ignore it. * * https://bugs.freedesktop.org/show_bug.cgi?id=27962 * * FIXME: This issue can be hidden inside Cogl so we shouldn't * need to care about this bug here. */ if (stage_cogl->pending_swaps > 0) stage_cogl->pending_swaps--; } else if (frame_event == COGL_FRAME_EVENT_COMPLETE) { gint64 presentation_time_cogl = frame_info->presentation_time; if (presentation_time_cogl != 0) { ClutterBackend *backend = stage_cogl->backend; CoglContext *context = clutter_backend_get_cogl_context (backend); gint64 current_time_cogl = cogl_get_clock_time (context); gint64 now = g_get_monotonic_time (); stage_cogl->last_presentation_time = now + (presentation_time_cogl - current_time_cogl) / 1000; } stage_cogl->refresh_rate = frame_info->refresh_rate; } _clutter_stage_presented (stage_cogl->wrapper, frame_event, frame_info); if (frame_event == COGL_FRAME_EVENT_COMPLETE && stage_cogl->update_time != -1) { ClutterStageWindow *stage_window = CLUTTER_STAGE_WINDOW (stage_cogl); stage_cogl->update_time = -1; clutter_stage_cogl_schedule_update (stage_window, stage_cogl->last_sync_delay); } } static gboolean clutter_stage_cogl_realize (ClutterStageWindow *stage_window) { ClutterBackend *backend; CLUTTER_NOTE (BACKEND, "Realizing stage '%s' [%p]", G_OBJECT_TYPE_NAME (stage_window), stage_window); backend = clutter_get_default_backend (); if (backend->cogl_context == NULL) { g_warning ("Failed to realize stage: missing Cogl context"); return FALSE; } return TRUE; } static void clutter_stage_cogl_schedule_update (ClutterStageWindow *stage_window, gint sync_delay) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); gint64 now; float refresh_rate; gint64 refresh_interval; int64_t min_render_time_allowed; int64_t max_render_time_allowed; int64_t next_presentation_time; if (stage_cogl->update_time != -1) return; stage_cogl->last_sync_delay = sync_delay; now = g_get_monotonic_time (); if (sync_delay < 0) { stage_cogl->update_time = now; return; } refresh_rate = stage_cogl->refresh_rate; if (refresh_rate <= 0.0) refresh_rate = clutter_get_default_frame_rate (); refresh_interval = (gint64) (0.5 + G_USEC_PER_SEC / refresh_rate); if (refresh_interval == 0) { stage_cogl->update_time = now; return; } min_render_time_allowed = refresh_interval / 2; max_render_time_allowed = refresh_interval - 1000 * sync_delay; /* Be robust in the case of incredibly bogus refresh rate */ if (max_render_time_allowed <= 0) { g_warning ("Unsupported monitor refresh rate detected. " "(Refresh rate: %.3f, refresh interval: %" G_GINT64_FORMAT ")", refresh_rate, refresh_interval); stage_cogl->update_time = now; return; } if (min_render_time_allowed > max_render_time_allowed) min_render_time_allowed = max_render_time_allowed; next_presentation_time = stage_cogl->last_presentation_time + refresh_interval; /* Get next_presentation_time closer to its final value, to reduce * the number of while iterations below. */ if (next_presentation_time < now) { int64_t last_virtual_presentation_time = now - now % refresh_interval; int64_t hardware_clock_phase = stage_cogl->last_presentation_time % refresh_interval; next_presentation_time = last_virtual_presentation_time + hardware_clock_phase; } while (next_presentation_time < now + min_render_time_allowed) next_presentation_time += refresh_interval; stage_cogl->update_time = next_presentation_time - max_render_time_allowed; if (stage_cogl->update_time == stage_cogl->last_update_time) { stage_cogl->update_time += refresh_interval; next_presentation_time += refresh_interval; } stage_cogl->next_presentation_time = next_presentation_time; } static gint64 clutter_stage_cogl_get_update_time (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); if (stage_cogl->pending_swaps) return -1; /* in the future, indefinite */ return stage_cogl->update_time; } static void clutter_stage_cogl_clear_update_time (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); stage_cogl->last_update_time = stage_cogl->update_time; stage_cogl->update_time = -1; stage_cogl->next_presentation_time = -1; } static int64_t clutter_stage_cogl_get_next_presentation_time (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); int64_t now = g_get_monotonic_time (); if (stage_cogl->next_presentation_time > 0 && stage_cogl->next_presentation_time <= now) { CLUTTER_NOTE (BACKEND, "Missed some frames. Something blocked for over " "%" G_GINT64_FORMAT "ms.", (now - stage_cogl->next_presentation_time) / 1000); stage_cogl->update_time = -1; clutter_stage_cogl_schedule_update (stage_window, stage_cogl->last_sync_delay); } return stage_cogl->next_presentation_time; } static ClutterActor * clutter_stage_cogl_get_wrapper (ClutterStageWindow *stage_window) { return CLUTTER_ACTOR (CLUTTER_STAGE_COGL (stage_window)->wrapper); } static void clutter_stage_cogl_show (ClutterStageWindow *stage_window, gboolean do_raise) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper)); } static void clutter_stage_cogl_hide (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper)); } static void clutter_stage_cogl_resize (ClutterStageWindow *stage_window, gint width, gint height) { } static inline gboolean valid_buffer_age (ClutterStageViewCogl *view_cogl, int age) { ClutterStageViewCoglPrivate *view_priv = clutter_stage_view_cogl_get_instance_private (view_cogl); if (age <= 0) return FALSE; return age < MIN (view_priv->damage_index, DAMAGE_HISTORY_MAX); } static void paint_damage_region (ClutterStageWindow *stage_window, ClutterStageView *view, cairo_region_t *swap_region, cairo_region_t *queued_redraw_clip) { CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); static CoglPipeline *overlay_blue = NULL; ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); ClutterActor *actor = CLUTTER_ACTOR (stage_cogl->wrapper); CoglMatrix transform; int n_rects, i; cogl_framebuffer_push_matrix (framebuffer); clutter_actor_get_transform (actor, &transform); cogl_framebuffer_transform (framebuffer, &transform); /* Blue for the swap region */ if (G_UNLIKELY (overlay_blue == NULL)) { overlay_blue = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (overlay_blue, 0x00, 0x00, 0x33, 0x33); } n_rects = cairo_region_num_rectangles (swap_region); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; float x_1, x_2, y_1, y_2; cairo_region_get_rectangle (swap_region, i, &rect); x_1 = rect.x; x_2 = rect.x + rect.width; y_1 = rect.y; y_2 = rect.y + rect.height; cogl_framebuffer_draw_rectangle (framebuffer, overlay_blue, x_1, y_1, x_2, y_2); } /* Red for the clip */ if (queued_redraw_clip) { static CoglPipeline *overlay_red = NULL; if (G_UNLIKELY (overlay_red == NULL)) { overlay_red = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (overlay_red, 0x33, 0x00, 0x00, 0x33); } n_rects = cairo_region_num_rectangles (queued_redraw_clip); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; float x_1, x_2, y_1, y_2; cairo_region_get_rectangle (queued_redraw_clip, i, &rect); x_1 = rect.x; x_2 = rect.x + rect.width; y_1 = rect.y; y_2 = rect.y + rect.height; cogl_framebuffer_draw_rectangle (framebuffer, overlay_red, x_1, y_1, x_2, y_2); } } cogl_framebuffer_pop_matrix (framebuffer); } static gboolean swap_framebuffer (ClutterStageWindow *stage_window, ClutterStageView *view, cairo_region_t *swap_region, gboolean swap_with_damage) { CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (view); int *damage, n_rects, i; n_rects = cairo_region_num_rectangles (swap_region); damage = g_newa (int, n_rects * 4); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (swap_region, i, &rect); damage[i * 4] = rect.x; damage[i * 4 + 1] = rect.y; damage[i * 4 + 2] = rect.width; damage[i * 4 + 3] = rect.height; } if (cogl_is_onscreen (framebuffer)) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); /* push on the screen */ if (n_rects > 0 && !swap_with_damage) { CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_region (onscreen: %p)", onscreen); cogl_onscreen_swap_region (onscreen, damage, n_rects); return FALSE; } else { CLUTTER_NOTE (BACKEND, "cogl_onscreen_swap_buffers (onscreen: %p)", onscreen); cogl_onscreen_swap_buffers_with_damage (onscreen, damage, n_rects); return TRUE; } } else { CLUTTER_NOTE (BACKEND, "cogl_framebuffer_finish (framebuffer: %p)", framebuffer); cogl_framebuffer_finish (framebuffer); return FALSE; } } static void scale_and_clamp_rect (const graphene_rect_t *rect, float scale, cairo_rectangle_int_t *dest) { graphene_rect_t tmp = *rect; graphene_rect_scale (&tmp, scale, scale, &tmp); _clutter_util_rectangle_int_extents (&tmp, dest); } static cairo_region_t * offset_scale_and_clamp_region (const cairo_region_t *region, int offset_x, int offset_y, float scale) { int n_rects, i; cairo_rectangle_int_t *rects; g_autofree cairo_rectangle_int_t *freeme = NULL; n_rects = cairo_region_num_rectangles (region); if (n_rects == 0) return cairo_region_create (); if (n_rects < MAX_STACK_RECTS) rects = g_newa (cairo_rectangle_int_t, n_rects); else rects = freeme = g_new (cairo_rectangle_int_t, n_rects); for (i = 0; i < n_rects; i++) cairo_region_get_rectangle (region, i, &rects[i]); for (i = 0; i < n_rects; i++) { graphene_rect_t tmp; _clutter_util_rect_from_rectangle (&rects[i], &tmp); graphene_rect_offset (&tmp, offset_x, offset_y); scale_and_clamp_rect (&tmp, scale, &rects[i]); } return cairo_region_create_rectangles (rects, n_rects); } static void paint_stage (ClutterStageCogl *stage_cogl, ClutterStageView *view, cairo_region_t *redraw_clip) { ClutterStage *stage = stage_cogl->wrapper; _clutter_stage_maybe_setup_viewport (stage, view); clutter_stage_paint_view (stage, view, redraw_clip); clutter_stage_view_after_paint (view); } static void fill_current_damage_history (ClutterStageView *view, cairo_region_t *damage) { ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); ClutterStageViewCoglPrivate *view_priv = clutter_stage_view_cogl_get_instance_private (view_cogl); cairo_region_t **current_fb_damage; current_fb_damage = &view_priv->damage_history[DAMAGE_HISTORY (view_priv->damage_index)]; g_clear_pointer (current_fb_damage, cairo_region_destroy); *current_fb_damage = cairo_region_copy (damage); view_priv->damage_index++; } static void fill_current_damage_history_rectangle (ClutterStageView *view, const cairo_rectangle_int_t *rect) { cairo_region_t *damage; damage = cairo_region_create_rectangle (rect); fill_current_damage_history (view, damage); cairo_region_destroy (damage); } static cairo_region_t * transform_swap_region_to_onscreen (ClutterStageView *view, cairo_region_t *swap_region) { CoglFramebuffer *framebuffer; cairo_rectangle_int_t layout; gint width, height; int n_rects, i; cairo_rectangle_int_t *rects; cairo_region_t *transformed_region; framebuffer = clutter_stage_view_get_onscreen (view); clutter_stage_view_get_layout (view, &layout); width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); n_rects = cairo_region_num_rectangles (swap_region); rects = g_newa (cairo_rectangle_int_t, n_rects); for (i = 0; i < n_rects; i++) { gfloat x1, y1, x2, y2; cairo_region_get_rectangle (swap_region, i, &rects[i]); x1 = (float) rects[i].x / layout.width; y1 = (float) rects[i].y / layout.height; x2 = (float) (rects[i].x + rects[i].width) / layout.width; y2 = (float) (rects[i].y + rects[i].height) / layout.height; clutter_stage_view_transform_to_onscreen (view, &x1, &y1); clutter_stage_view_transform_to_onscreen (view, &x2, &y2); x1 = floor (x1 * width); y1 = floor (height - (y1 * height)); x2 = ceil (x2 * width); y2 = ceil (height - (y2 * height)); rects[i].x = x1; rects[i].y = y1; rects[i].width = x2 - x1; rects[i].height = y2 - y1; } transformed_region = cairo_region_create_rectangles (rects, n_rects); return transformed_region; } static void calculate_scissor_region (cairo_rectangle_int_t *fb_clip_region, int subpixel_compensation, int fb_width, int fb_height, cairo_rectangle_int_t *out_scissor_rect) { *out_scissor_rect = *fb_clip_region; if (subpixel_compensation == 0) return; if (fb_clip_region->x > 0) out_scissor_rect->x += subpixel_compensation; if (fb_clip_region->y > 0) out_scissor_rect->y += subpixel_compensation; if (fb_clip_region->x + fb_clip_region->width < fb_width) out_scissor_rect->width -= 2 * subpixel_compensation; if (fb_clip_region->y + fb_clip_region->height < fb_height) out_scissor_rect->height -= 2 * subpixel_compensation; } static inline gboolean is_buffer_age_enabled (void) { /* Buffer age is disabled when running with CLUTTER_PAINT=damage-region, * to ensure the red damage represents the currently damaged area */ return !(clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION) && cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE); } static gboolean clutter_stage_cogl_redraw_view (ClutterStageWindow *stage_window, ClutterStageView *view) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); ClutterStageViewCogl *view_cogl = CLUTTER_STAGE_VIEW_COGL (view); ClutterStageViewCoglPrivate *view_priv = clutter_stage_view_cogl_get_instance_private (view_cogl); CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); cairo_rectangle_int_t view_rect; gboolean is_full_redraw; gboolean may_use_clipped_redraw; gboolean use_clipped_redraw; gboolean can_blit_sub_buffer; gboolean has_buffer_age; gboolean do_swap_buffer; gboolean swap_with_damage; ClutterActor *wrapper; cairo_region_t *redraw_clip; cairo_region_t *queued_redraw_clip = NULL; cairo_region_t *fb_clip_region; cairo_region_t *swap_region; cairo_rectangle_int_t redraw_rect; gboolean clip_region_empty; float fb_scale; int subpixel_compensation = 0; int fb_width, fb_height; int buffer_age; wrapper = CLUTTER_ACTOR (stage_cogl->wrapper); clutter_stage_view_get_layout (view, &view_rect); fb_scale = clutter_stage_view_get_scale (view); fb_width = cogl_framebuffer_get_width (fb); fb_height = cogl_framebuffer_get_height (fb); can_blit_sub_buffer = cogl_is_onscreen (fb) && cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION); has_buffer_age = cogl_is_onscreen (fb) && is_buffer_age_enabled (); redraw_clip = clutter_stage_view_take_redraw_clip (view); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION)) queued_redraw_clip = cairo_region_copy (redraw_clip); /* NB: a NULL redraw clip == full stage redraw */ if (!redraw_clip) is_full_redraw = TRUE; else is_full_redraw = FALSE; may_use_clipped_redraw = _clutter_stage_window_can_clip_redraws (stage_window) && (can_blit_sub_buffer || has_buffer_age) && !is_full_redraw && /* some drivers struggle to get going and produce some junk * frames when starting up... */ cogl_onscreen_get_frame_counter (COGL_ONSCREEN (fb)) > 3; if (has_buffer_age) { buffer_age = cogl_onscreen_get_buffer_age (COGL_ONSCREEN (fb)); if (!valid_buffer_age (view_cogl, buffer_age)) { CLUTTER_NOTE (CLIPPING, "Invalid back buffer(age=%d): forcing full redraw\n", buffer_age); may_use_clipped_redraw = FALSE; } } if (may_use_clipped_redraw) { fb_clip_region = offset_scale_and_clamp_region (redraw_clip, -view_rect.x, -view_rect.y, fb_scale); if (fb_scale != floorf (fb_scale)) { int n_rects, i; cairo_rectangle_int_t *rects; subpixel_compensation = ceilf (fb_scale); n_rects = cairo_region_num_rectangles (fb_clip_region); rects = g_newa (cairo_rectangle_int_t, n_rects); for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (fb_clip_region, i, &rects[i]); rects[i].x -= subpixel_compensation; rects[i].y -= subpixel_compensation; rects[i].width += 2 * subpixel_compensation; rects[i].height += 2 * subpixel_compensation; } cairo_region_destroy (fb_clip_region); fb_clip_region = cairo_region_create_rectangles (rects, n_rects); } } else { cairo_rectangle_int_t fb_rect; fb_rect = (cairo_rectangle_int_t) { .width = fb_width, .height = fb_height, }; fb_clip_region = cairo_region_create_rectangle (&fb_rect); g_clear_pointer (&redraw_clip, cairo_region_destroy); redraw_clip = cairo_region_create_rectangle (&view_rect); } if (may_use_clipped_redraw && G_LIKELY (!(clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS))) use_clipped_redraw = TRUE; else use_clipped_redraw = FALSE; clip_region_empty = may_use_clipped_redraw && cairo_region_is_empty (fb_clip_region); swap_with_damage = FALSE; if (has_buffer_age) { if (use_clipped_redraw && !clip_region_empty) { cairo_region_t *fb_damage; cairo_region_t *view_damage; int i; fill_current_damage_history (view, fb_clip_region); fb_damage = cairo_region_create (); for (i = 1; i <= buffer_age; i++) { int damage_index; damage_index = DAMAGE_HISTORY (view_priv->damage_index - i - 1); cairo_region_union (fb_damage, view_priv->damage_history[damage_index]); } /* Update the fb clip region with the extra damage. */ cairo_region_union (fb_clip_region, fb_damage); view_damage = offset_scale_and_clamp_region (fb_damage, 0, 0, 1.0f / fb_scale); cairo_region_translate (view_damage, view_rect.x, view_rect.y); cairo_region_intersect_rectangle (view_damage, &view_rect); /* Update the redraw clip region with the extra damage. */ cairo_region_union (redraw_clip, view_damage); cairo_region_destroy (view_damage); cairo_region_destroy (fb_damage); CLUTTER_NOTE (CLIPPING, "Reusing back buffer(age=%d) - repairing region: num rects: %d\n", buffer_age, cairo_region_num_rectangles (fb_clip_region)); swap_with_damage = TRUE; } else if (!use_clipped_redraw) { cairo_rectangle_int_t fb_damage; fb_damage = (cairo_rectangle_int_t) { .x = 0, .y = 0, .width = ceilf (view_rect.width * fb_scale), .height = ceilf (view_rect.height * fb_scale) }; fill_current_damage_history_rectangle (view, &fb_damage); } } if (use_clipped_redraw && clip_region_empty) { CLUTTER_NOTE (CLIPPING, "Empty stage output paint\n"); } else if (use_clipped_redraw) { cairo_rectangle_int_t clip_rect; cairo_rectangle_int_t scissor_rect; if (cairo_region_num_rectangles (fb_clip_region) == 1) { cairo_region_get_extents (fb_clip_region, &clip_rect); calculate_scissor_region (&clip_rect, subpixel_compensation, fb_width, fb_height, &scissor_rect); CLUTTER_NOTE (CLIPPING, "Stage clip pushed: x=%d, y=%d, width=%d, height=%d\n", scissor_rect.x, scissor_rect.y, scissor_rect.width, scissor_rect.height); cogl_framebuffer_push_scissor_clip (fb, scissor_rect.x, scissor_rect.y, scissor_rect.width, scissor_rect.height); } else { cogl_framebuffer_push_region_clip (fb, fb_clip_region); } paint_stage (stage_cogl, view, redraw_clip); cogl_framebuffer_pop_clip (fb); } else { CLUTTER_NOTE (CLIPPING, "Unclipped stage paint\n"); /* If we are trying to debug redraw issues then we want to pass * the redraw_clip so it can be visualized */ if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS) && may_use_clipped_redraw && !clip_region_empty) { cairo_rectangle_int_t clip_rect; cairo_rectangle_int_t scissor_rect; cairo_region_get_extents (fb_clip_region, &clip_rect); calculate_scissor_region (&clip_rect, subpixel_compensation, fb_width, fb_height, &scissor_rect); cogl_framebuffer_push_scissor_clip (fb, scissor_rect.x, scissor_rect.y, scissor_rect.width, scissor_rect.height); paint_stage (stage_cogl, view, redraw_clip); cogl_framebuffer_pop_clip (fb); } else { paint_stage (stage_cogl, view, redraw_clip); } } cairo_region_get_extents (redraw_clip, &redraw_rect); if (may_use_clipped_redraw && G_UNLIKELY ((clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS))) { CoglContext *ctx = cogl_framebuffer_get_context (fb); static CoglPipeline *outline = NULL; ClutterActor *actor = CLUTTER_ACTOR (wrapper); float x_1 = redraw_rect.x; float x_2 = redraw_rect.x + redraw_rect.width; float y_1 = redraw_rect.y; float y_2 = redraw_rect.y + redraw_rect.height; CoglVertexP2 quad[4] = { { x_1, y_1 }, { x_2, y_1 }, { x_2, y_2 }, { x_1, y_2 } }; CoglPrimitive *prim; CoglMatrix modelview; if (outline == NULL) { outline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (outline, 0xff, 0x00, 0x00, 0xff); } prim = cogl_primitive_new_p2 (ctx, COGL_VERTICES_MODE_LINE_LOOP, 4, /* n_vertices */ quad); cogl_framebuffer_push_matrix (fb); cogl_matrix_init_identity (&modelview); _clutter_actor_apply_modelview_transform (actor, &modelview); cogl_framebuffer_set_modelview_matrix (fb, &modelview); cogl_framebuffer_draw_primitive (fb, outline, prim); cogl_framebuffer_pop_matrix (fb); cogl_object_unref (prim); } /* XXX: It seems there will be a race here in that the stage * window may be resized before the cogl_onscreen_swap_region * is handled and so we may copy the wrong region. I can't * really see how we can handle this with the current state of X * but at least in this case a full redraw should be queued by * the resize anyway so it should only exhibit temporary * artefacts. */ if (use_clipped_redraw) { if (clip_region_empty) { do_swap_buffer = FALSE; } else { swap_region = cairo_region_reference (fb_clip_region); do_swap_buffer = TRUE; } } else { swap_region = cairo_region_create (); do_swap_buffer = TRUE; } g_clear_pointer (&redraw_clip, cairo_region_destroy); g_clear_pointer (&fb_clip_region, cairo_region_destroy); if (do_swap_buffer) { gboolean res; COGL_TRACE_BEGIN_SCOPED (ClutterStageCoglRedrawViewSwapFramebuffer, "Paint (swap framebuffer)"); if (clutter_stage_view_get_onscreen (view) != clutter_stage_view_get_framebuffer (view)) { cairo_region_t *transformed_swap_region; transformed_swap_region = transform_swap_region_to_onscreen (view, swap_region); cairo_region_destroy (swap_region); swap_region = transformed_swap_region; } if (queued_redraw_clip) { paint_damage_region (stage_window, view, swap_region, queued_redraw_clip); cairo_region_destroy (queued_redraw_clip); } res = swap_framebuffer (stage_window, view, swap_region, swap_with_damage); cairo_region_destroy (swap_region); return res; } else { g_clear_pointer (&queued_redraw_clip, cairo_region_destroy); return FALSE; } } static void clutter_stage_cogl_redraw (ClutterStageWindow *stage_window) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); gboolean has_redraw_clip = FALSE; gboolean swap_event = FALSE; GList *l; COGL_TRACE_BEGIN (ClutterStageCoglRedraw, "Paint (Cogl Redraw)"); for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next) { ClutterStageView *view = l->data; if (!clutter_stage_view_has_redraw_clip (view)) continue; has_redraw_clip = TRUE; break; } if (has_redraw_clip) clutter_stage_emit_before_paint (stage_cogl->wrapper); for (l = _clutter_stage_window_get_views (stage_window); l; l = l->next) { ClutterStageView *view = l->data; if (!clutter_stage_view_has_redraw_clip (view)) continue; swap_event |= clutter_stage_cogl_redraw_view (stage_window, view); } if (has_redraw_clip) clutter_stage_emit_after_paint (stage_cogl->wrapper); _clutter_stage_window_finish_frame (stage_window); if (swap_event) { /* If we have swap buffer events then cogl_onscreen_swap_buffers * will return immediately and we need to track that there is a * swap in progress... */ if (clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)) stage_cogl->pending_swaps++; } stage_cogl->frame_count++; COGL_TRACE_END (ClutterStageCoglRedraw); } static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) { iface->realize = clutter_stage_cogl_realize; iface->unrealize = clutter_stage_cogl_unrealize; iface->get_wrapper = clutter_stage_cogl_get_wrapper; iface->resize = clutter_stage_cogl_resize; iface->show = clutter_stage_cogl_show; iface->hide = clutter_stage_cogl_hide; iface->schedule_update = clutter_stage_cogl_schedule_update; iface->get_update_time = clutter_stage_cogl_get_update_time; iface->clear_update_time = clutter_stage_cogl_clear_update_time; iface->get_next_presentation_time = clutter_stage_cogl_get_next_presentation_time; iface->redraw = clutter_stage_cogl_redraw; } static void clutter_stage_cogl_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterStageCogl *self = CLUTTER_STAGE_COGL (gobject); switch (prop_id) { case PROP_WRAPPER: self->wrapper = g_value_get_object (value); break; case PROP_BACKEND: self->backend = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void _clutter_stage_cogl_class_init (ClutterStageCoglClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_stage_cogl_set_property; g_object_class_override_property (gobject_class, PROP_WRAPPER, "wrapper"); g_object_class_override_property (gobject_class, PROP_BACKEND, "backend"); } static void _clutter_stage_cogl_init (ClutterStageCogl *stage) { stage->last_presentation_time = 0; stage->refresh_rate = 0.0; stage->update_time = -1; stage->next_presentation_time = -1; } static void clutter_stage_view_cogl_init (ClutterStageViewCogl *view_cogl) { } static void clutter_stage_view_cogl_class_init (ClutterStageViewCoglClass *klass) { } muffin-6.4.1/clutter/clutter/clutter-pan-action.h0000664000175000017500000001472314723361714020777 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * Copyright (C) 2012 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emanuele Aina * * Based on ClutterDragAction, ClutterSwipeAction, and MxKineticScrollView, * written by: * Emmanuele Bassi * Tomeu Vizoso * Chris Lord */ #ifndef __CLUTTER_PAN_ACTION_H__ #define __CLUTTER_PAN_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_PAN_ACTION (clutter_pan_action_get_type ()) #define CLUTTER_PAN_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PAN_ACTION, ClutterPanAction)) #define CLUTTER_IS_PAN_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PAN_ACTION)) #define CLUTTER_PAN_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PAN_ACTION, ClutterPanActionClass)) #define CLUTTER_IS_PAN_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PAN_ACTION)) #define CLUTTER_PAN_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PAN_ACTION, ClutterPanActionClass)) typedef struct _ClutterPanAction ClutterPanAction; typedef struct _ClutterPanActionPrivate ClutterPanActionPrivate; typedef struct _ClutterPanActionClass ClutterPanActionClass; /** * ClutterPanAction: * * The #ClutterPanAction structure contains * only private data and should be accessed using the provided API * * Since: 1.12 */ struct _ClutterPanAction { /*< private >*/ ClutterGestureAction parent_instance; ClutterPanActionPrivate *priv; }; /** * ClutterPanActionClass: * @pan: class handler for the #ClutterPanAction::pan signal * @pan_stopped: class handler for the #ClutterPanAction::pan-stopped signal * * The #ClutterPanActionClass structure contains * only private data. * * Since: 1.12 */ struct _ClutterPanActionClass { /*< private >*/ ClutterGestureActionClass parent_class; /*< public >*/ gboolean (* pan) (ClutterPanAction *action, ClutterActor *actor, gboolean is_interpolated); void (* pan_stopped) (ClutterPanAction *action, ClutterActor *actor); /*< private >*/ void (* _clutter_pan_action1) (void); void (* _clutter_pan_action2) (void); void (* _clutter_pan_action3) (void); void (* _clutter_pan_action4) (void); void (* _clutter_pan_action5) (void); void (* _clutter_pan_action6) (void); }; CLUTTER_EXPORT GType clutter_pan_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction * clutter_pan_action_new (void); CLUTTER_EXPORT void clutter_pan_action_set_pan_axis (ClutterPanAction *self, ClutterPanAxis axis); CLUTTER_EXPORT ClutterPanAxis clutter_pan_action_get_pan_axis (ClutterPanAction *self); CLUTTER_EXPORT void clutter_pan_action_set_interpolate (ClutterPanAction *self, gboolean should_interpolate); CLUTTER_EXPORT gboolean clutter_pan_action_get_interpolate (ClutterPanAction *self); CLUTTER_EXPORT void clutter_pan_action_set_deceleration (ClutterPanAction *self, gdouble rate); CLUTTER_EXPORT gdouble clutter_pan_action_get_deceleration (ClutterPanAction *self); CLUTTER_EXPORT void clutter_pan_action_set_acceleration_factor (ClutterPanAction *self, gdouble factor); CLUTTER_EXPORT gdouble clutter_pan_action_get_acceleration_factor (ClutterPanAction *self); CLUTTER_EXPORT void clutter_pan_action_get_interpolated_coords (ClutterPanAction *self, gfloat *interpolated_x, gfloat *interpolated_y); CLUTTER_EXPORT gfloat clutter_pan_action_get_interpolated_delta (ClutterPanAction *self, gfloat *delta_x, gfloat *delta_y); CLUTTER_EXPORT gfloat clutter_pan_action_get_motion_delta (ClutterPanAction *self, guint point, gfloat *delta_x, gfloat *delta_y); CLUTTER_EXPORT void clutter_pan_action_get_motion_coords (ClutterPanAction *self, guint point, gfloat *motion_x, gfloat *motion_y); CLUTTER_EXPORT gfloat clutter_pan_action_get_constrained_motion_delta (ClutterPanAction *self, guint point, gfloat *delta_x, gfloat *delta_y); G_END_DECLS #endif /* __CLUTTER_PAN_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-texture-content.c0000664000175000017500000001255614723361714022113 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive image' library. * * Copyright (C) 2012 Intel Corporation. * Copyright (C) 2021 Robert Mader. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * Robert Mader */ #include "clutter-build-config.h" #include "clutter-texture-content.h" #include "clutter-actor-private.h" #include "clutter-content-private.h" #include "clutter-paint-node.h" struct _ClutterTextureContent { GObject parent_instance; CoglTexture *texture; }; static void clutter_content_iface_init (ClutterContentInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterTextureContent, clutter_texture_content, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) static void clutter_texture_content_finalize (GObject *gobject) { ClutterTextureContent *texture_content = CLUTTER_TEXTURE_CONTENT (gobject); g_clear_pointer (&texture_content->texture, cogl_object_unref); G_OBJECT_CLASS (clutter_texture_content_parent_class)->finalize (gobject); } static void clutter_texture_content_class_init (ClutterTextureContentClass *klass) { G_OBJECT_CLASS (klass)->finalize = clutter_texture_content_finalize; } static void clutter_texture_content_init (ClutterTextureContent *texture_content) { } static void clutter_texture_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ClutterTextureContent *texture_content = CLUTTER_TEXTURE_CONTENT (content); ClutterPaintNode *node; node = clutter_actor_create_texture_paint_node (actor, texture_content->texture); clutter_paint_node_set_static_name (node, "Texture Content"); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); } static gboolean clutter_texture_content_get_preferred_size (ClutterContent *content, float *width, float *height) { ClutterTextureContent *texture_content = CLUTTER_TEXTURE_CONTENT (content); if (width != NULL) *width = cogl_texture_get_width (texture_content->texture); if (height != NULL) *height = cogl_texture_get_height (texture_content->texture); return TRUE; } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->get_preferred_size = clutter_texture_content_get_preferred_size; iface->paint_content = clutter_texture_content_paint_content; } /** * clutter_texture_content_new_from_texture: * @texture: a #CoglTexture * @clip: (nullable): A clipping rectangle * * Creates a new #ClutterTextureContent instance for @texture, taking an * internal reference to @texture. * * If you change the contents of the #CoglTexture you will need * to manually invalidate the @texture_content with clutter_content_invalidate() * in order to update the actors using @texture_content as their content. * * Return value: (transfer full): the newly created #ClutterTextureContent instance. * Use g_object_unref() when done. */ ClutterContent * clutter_texture_content_new_from_texture (CoglTexture *texture, cairo_rectangle_int_t *clip) { ClutterTextureContent *texture_content; g_return_val_if_fail (texture != NULL, NULL); texture_content = g_object_new (CLUTTER_TYPE_TEXTURE_CONTENT, NULL); if (clip) { texture_content->texture = cogl_texture_new_from_sub_texture (texture, clip->x, clip->y, clip->width, clip->height); } else { texture_content->texture = cogl_object_ref (texture); } return CLUTTER_CONTENT (texture_content); } /** * clutter_texture_content_get_texture: * @texture_content: a #ClutterTextureContent * * Retrieves a pointer to the #CoglTexture used by @texture_content. * * If you change the contents of the returned #CoglTexture you will need * to manually invalidate the @texture_content with clutter_content_invalidate() * in order to update the actors using @texture_content as their content. * * Return value: (transfer none): a pointer to the #CoglTexture */ CoglTexture * clutter_texture_content_get_texture (ClutterTextureContent *texture_content) { g_return_val_if_fail (CLUTTER_IS_TEXTURE_CONTENT (texture_content), NULL); return texture_content->texture; } muffin-6.4.1/clutter/clutter/clutter-paint-node-private.h0000664000175000017500000001260314723361714022447 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_PAINT_NODE_PRIVATE_H__ #define __CLUTTER_PAINT_NODE_PRIVATE_H__ #include #include #include #include G_BEGIN_DECLS #define CLUTTER_PAINT_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNodeClass)) #define CLUTTER_IS_PAINT_NODE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PAINT_NODE)) #define CLUTTER_PAINT_NODE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PAINT_NODE, ClutterPaintNodeClass)) typedef struct _ClutterPaintOperation ClutterPaintOperation; struct _ClutterPaintNode { GTypeInstance parent_instance; ClutterPaintNode *parent; ClutterPaintNode *first_child; ClutterPaintNode *prev_sibling; ClutterPaintNode *next_sibling; ClutterPaintNode *last_child; GArray *operations; const gchar *name; guint n_children; volatile int ref_count; }; struct _ClutterPaintNodeClass { GTypeClass base_class; void (* finalize) (ClutterPaintNode *node); gboolean (* pre_draw) (ClutterPaintNode *node, ClutterPaintContext *paint_context); void (* draw) (ClutterPaintNode *node, ClutterPaintContext *paint_context); void (* post_draw) (ClutterPaintNode *node, ClutterPaintContext *paint_context); JsonNode*(* serialize) (ClutterPaintNode *node); CoglFramebuffer *(* get_framebuffer) (ClutterPaintNode *node); }; #define PAINT_OP_INIT { PAINT_OP_INVALID } typedef enum { PAINT_OP_INVALID = 0, PAINT_OP_TEX_RECT, PAINT_OP_MULTITEX_RECT, PAINT_OP_PATH, PAINT_OP_PRIMITIVE } PaintOpCode; struct _ClutterPaintOperation { PaintOpCode opcode; GArray *multitex_coords; union { float texrect[8]; CoglPath *path; CoglPrimitive *primitive; } op; }; GType _clutter_transform_node_get_type (void) G_GNUC_CONST; GType _clutter_dummy_node_get_type (void) G_GNUC_CONST; void _clutter_paint_operation_paint_rectangle (const ClutterPaintOperation *op); void _clutter_paint_operation_clip_rectangle (const ClutterPaintOperation *op); void _clutter_paint_operation_paint_path (const ClutterPaintOperation *op); void _clutter_paint_operation_clip_path (const ClutterPaintOperation *op); void _clutter_paint_operation_paint_primitive (const ClutterPaintOperation *op); void _clutter_paint_node_init_types (void); gpointer _clutter_paint_node_create (GType gtype); ClutterPaintNode * _clutter_transform_node_new (const CoglMatrix *matrix); ClutterPaintNode * _clutter_dummy_node_new (ClutterActor *actor, CoglFramebuffer *framebuffer); void _clutter_paint_node_dump_tree (ClutterPaintNode *root); G_GNUC_INTERNAL void clutter_paint_node_remove_child (ClutterPaintNode *node, ClutterPaintNode *child); G_GNUC_INTERNAL void clutter_paint_node_replace_child (ClutterPaintNode *node, ClutterPaintNode *old_child, ClutterPaintNode *new_child); G_GNUC_INTERNAL void clutter_paint_node_remove_all (ClutterPaintNode *node); G_GNUC_INTERNAL guint clutter_paint_node_get_n_children (ClutterPaintNode *node); G_GNUC_INTERNAL ClutterPaintNode * clutter_paint_node_get_first_child (ClutterPaintNode *node); G_GNUC_INTERNAL ClutterPaintNode * clutter_paint_node_get_previous_sibling (ClutterPaintNode *node); G_GNUC_INTERNAL ClutterPaintNode * clutter_paint_node_get_next_sibling (ClutterPaintNode *node); G_GNUC_INTERNAL ClutterPaintNode * clutter_paint_node_get_last_child (ClutterPaintNode *node); G_GNUC_INTERNAL ClutterPaintNode * clutter_paint_node_get_parent (ClutterPaintNode *node); G_END_DECLS #endif /* __CLUTTER_PAINT_NODE_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-debug.h0000664000175000017500000000347614723361714020037 0ustar fabiofabio#ifndef __CLUTTER_DEBUG_H__ #define __CLUTTER_DEBUG_H__ #include #include "clutter-main.h" G_BEGIN_DECLS #ifdef CLUTTER_ENABLE_DEBUG #define CLUTTER_HAS_DEBUG(type) ((clutter_debug_flags & CLUTTER_DEBUG_##type) != FALSE) #ifdef __GNUC__ /* Try the GCC extension for valists in macros */ #define CLUTTER_NOTE(type,x,a...) G_STMT_START { \ if (G_UNLIKELY (CLUTTER_HAS_DEBUG (type))) { \ _clutter_debug_message ("[" #type "]:" G_STRLOC ": " x, ##a); \ } } G_STMT_END #else /* !__GNUC__ */ /* Try the C99 version; unfortunately, this does not allow us to pass * empty arguments to the macro, which means we have to * do an intemediate printf. */ #define CLUTTER_NOTE(type,...) G_STMT_START { \ if (G_UNLIKELY (CLUTTER_HAS_DEBUG (type))) { \ gchar *_fmt = g_strdup_printf (__VA_ARGS__); \ _clutter_debug_message ("[" #type "]:" G_STRLOC ": %s", _fmt); \ g_free (_fmt); \ } } G_STMT_END #endif #else /* !CLUTTER_ENABLE_DEBUG */ #define CLUTTER_NOTE(type,...) G_STMT_START { } G_STMT_END #define CLUTTER_HAS_DEBUG(type) FALSE #endif /* CLUTTER_ENABLE_DEBUG */ extern guint clutter_debug_flags; extern guint clutter_pick_debug_flags; extern guint clutter_paint_debug_flags; void _clutter_debug_messagev (const char *format, va_list var_args) G_GNUC_PRINTF (1, 0); void _clutter_debug_message (const char *format, ...) G_GNUC_PRINTF (1, 2); G_END_DECLS #endif /* __CLUTTER_DEBUG_H__ */ muffin-6.4.1/clutter/clutter/clutter-util.c0000664000175000017500000005216514723361714017720 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-util * @short_description: Utility functions * * Various miscellaneous utilility functions. */ #include "clutter-build-config.h" #include #include #include "clutter-debug.h" #include "clutter-main.h" #include "clutter-interval.h" #include "clutter-private.h" /* Help macros to scale from OpenGL <-1,1> coordinates system to * window coordinates ranging [0,window-size] */ #define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) #define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) #define MTX_GL_SCALE_Z(z,w,v1,v2) (MTX_GL_SCALE_X ((z), (w), (v1), (v2))) void _clutter_util_fully_transform_vertices (const CoglMatrix *modelview, const CoglMatrix *projection, const float *viewport, const graphene_point3d_t *vertices_in, graphene_point3d_t *vertices_out, int n_vertices) { CoglMatrix modelview_projection; ClutterVertex4 *vertices_tmp; int i; vertices_tmp = g_alloca (sizeof (ClutterVertex4) * n_vertices); if (n_vertices >= 4) { /* XXX: we should find a way to cache this per actor */ cogl_matrix_multiply (&modelview_projection, projection, modelview); cogl_matrix_project_points (&modelview_projection, 3, sizeof (graphene_point3d_t), vertices_in, sizeof (ClutterVertex4), vertices_tmp, n_vertices); } else { cogl_matrix_transform_points (modelview, 3, sizeof (graphene_point3d_t), vertices_in, sizeof (ClutterVertex4), vertices_tmp, n_vertices); cogl_matrix_project_points (projection, 3, sizeof (ClutterVertex4), vertices_tmp, sizeof (ClutterVertex4), vertices_tmp, n_vertices); } for (i = 0; i < n_vertices; i++) { ClutterVertex4 vertex_tmp = vertices_tmp[i]; graphene_point3d_t *vertex_out = &vertices_out[i]; /* Finally translate from OpenGL coords to window coords */ vertex_out->x = MTX_GL_SCALE_X (vertex_tmp.x, vertex_tmp.w, viewport[2], viewport[0]); vertex_out->y = MTX_GL_SCALE_Y (vertex_tmp.y, vertex_tmp.w, viewport[3], viewport[1]); } } void _clutter_util_rect_from_rectangle (const cairo_rectangle_int_t *src, graphene_rect_t *dest) { *dest = (graphene_rect_t) { .origin = { .x = src->x, .y = src->y }, .size = { .width = src->width, .height = src->height } }; } void _clutter_util_rectangle_int_extents (const graphene_rect_t *src, cairo_rectangle_int_t *dest) { graphene_rect_t tmp = *src; graphene_rect_round_extents (&tmp, &tmp); *dest = (cairo_rectangle_int_t) { .x = tmp.origin.x, .y = tmp.origin.y, .width = tmp.size.width, .height = tmp.size.height, }; } void _clutter_util_rectangle_offset (const cairo_rectangle_int_t *src, int x, int y, cairo_rectangle_int_t *dest) { *dest = *src; dest->x += x; dest->y += y; } /*< private > * _clutter_util_rectangle_union: * @src1: first rectangle to union * @src2: second rectangle to union * @dest: (out): return location for the unioned rectangle * * Calculates the union of two rectangles. * * The union of rectangles @src1 and @src2 is the smallest rectangle which * includes both @src1 and @src2 within it. * * It is allowed for @dest to be the same as either @src1 or @src2. * * This function should really be in Cairo. */ void _clutter_util_rectangle_union (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2, cairo_rectangle_int_t *dest) { int dest_x, dest_y; dest_x = MIN (src1->x, src2->x); dest_y = MIN (src1->y, src2->y); dest->width = MAX (src1->x + src1->width, src2->x + src2->width) - dest_x; dest->height = MAX (src1->y + src1->height, src2->y + src2->height) - dest_y; dest->x = dest_x; dest->y = dest_y; } gboolean _clutter_util_rectangle_intersection (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2, cairo_rectangle_int_t *dest) { int x1, y1, x2, y2; x1 = MAX (src1->x, src2->x); y1 = MAX (src1->y, src2->y); x2 = MIN (src1->x + (int) src1->width, src2->x + (int) src2->width); y2 = MIN (src1->y + (int) src1->height, src2->y + (int) src2->height); if (x1 >= x2 || y1 >= y2) { dest->x = 0; dest->y = 0; dest->width = 0; dest->height = 0; return FALSE; } else { dest->x = x1; dest->y = y1; dest->width = x2 - x1; dest->height = y2 - y1; return TRUE; } } gboolean clutter_util_rectangle_equal (const cairo_rectangle_int_t *src1, const cairo_rectangle_int_t *src2) { return ((src1->x == src2->x) && (src1->y == src2->y) && (src1->width == src2->width) && (src1->height == src2->height)); } float _clutter_util_matrix_determinant (const ClutterMatrix *matrix) { return matrix->xw * matrix->yz * matrix->zy * matrix->wz - matrix->xz * matrix->yw * matrix->zy * matrix->wz - matrix->xw * matrix->yy * matrix->zz * matrix->wz + matrix->xy * matrix->yw * matrix->zz * matrix->wz + matrix->xz * matrix->yy * matrix->zw * matrix->wz - matrix->xy * matrix->yz * matrix->zw * matrix->wz - matrix->xw * matrix->yz * matrix->zx * matrix->wy + matrix->xz * matrix->yw * matrix->zx * matrix->wy + matrix->xw * matrix->yx * matrix->zz * matrix->wy - matrix->xx * matrix->yw * matrix->zz * matrix->wy - matrix->xz * matrix->yx * matrix->zw * matrix->wy + matrix->xx * matrix->yz * matrix->zw * matrix->wy + matrix->xw * matrix->yy * matrix->zx * matrix->wz - matrix->xy * matrix->yw * matrix->zx * matrix->wz - matrix->xw * matrix->yx * matrix->zy * matrix->wz + matrix->xx * matrix->yw * matrix->zy * matrix->wz + matrix->xy * matrix->yx * matrix->zw * matrix->wz - matrix->xx * matrix->yy * matrix->zw * matrix->wz - matrix->xz * matrix->yy * matrix->zx * matrix->ww + matrix->xy * matrix->yz * matrix->zx * matrix->ww + matrix->xz * matrix->yx * matrix->zy * matrix->ww - matrix->xx * matrix->yz * matrix->zy * matrix->ww - matrix->xy * matrix->yx * matrix->zz * matrix->ww + matrix->xx * matrix->yy * matrix->zz * matrix->ww; } static void _clutter_util_matrix_transpose_vector4_transform (const ClutterMatrix *matrix, const ClutterVertex4 *point, ClutterVertex4 *res) { res->x = matrix->xx * point->x + matrix->xy * point->y + matrix->xz * point->z + matrix->xw * point->w; res->y = matrix->yx * point->x + matrix->yy * point->y + matrix->yz * point->z + matrix->yw * point->w; res->z = matrix->zx * point->x + matrix->zy * point->y + matrix->zz * point->z + matrix->zw * point->w; res->w = matrix->wz * point->x + matrix->wy * point->w + matrix->wz * point->z + matrix->ww * point->w; } void _clutter_util_matrix_skew_xy (ClutterMatrix *matrix, float factor) { matrix->yx += matrix->xx * factor; matrix->yy += matrix->xy * factor; matrix->yz += matrix->xz * factor; matrix->yw += matrix->xw * factor; } void _clutter_util_matrix_skew_xz (ClutterMatrix *matrix, float factor) { matrix->zx += matrix->xx * factor; matrix->zy += matrix->xy * factor; matrix->zz += matrix->xz * factor; matrix->zw += matrix->xw * factor; } void _clutter_util_matrix_skew_yz (ClutterMatrix *matrix, float factor) { matrix->zx += matrix->yx * factor; matrix->zy += matrix->yy * factor; matrix->zz += matrix->yz * factor; matrix->zw += matrix->yw * factor; } static void _clutter_util_vertex_combine (const graphene_point3d_t *a, const graphene_point3d_t *b, double ascl, double bscl, graphene_point3d_t *res) { res->x = (ascl * a->x) + (bscl * b->x); res->y = (ascl * a->y) + (bscl * b->y); res->z = (ascl * a->z) + (bscl * b->z); } void _clutter_util_vertex4_interpolate (const ClutterVertex4 *a, const ClutterVertex4 *b, double progress, ClutterVertex4 *res) { res->x = a->x + (b->x - a->x) * progress; res->y = a->y + (b->y - a->y) * progress; res->z = a->z + (b->z - a->z) * progress; res->w = a->w + (b->w - a->w) * progress; } /*< private > * clutter_util_matrix_decompose: * @src: the matrix to decompose * @scale_p: (out caller-allocates): return location for a vertex containing * the scaling factors * @shear_p: (out) (array length=3): return location for an array of 3 * elements containing the skew factors (XY, XZ, and YZ respectively) * @rotate_p: (out caller-allocates): return location for a vertex containing * the Euler angles * @translate_p: (out caller-allocates): return location for a vertex * containing the translation vector * @perspective_p: (out caller-allocates: return location for a 4D vertex * containing the perspective * * Decomposes a #ClutterMatrix into the transformations that compose it. * * This code is based on the matrix decomposition algorithm as published in * the CSS Transforms specification by the W3C CSS working group, available * at http://www.w3.org/TR/css3-transforms/. * * The algorithm, in turn, is based on the "unmatrix" method published in * "Graphics Gems II, edited by Jim Arvo", which is available at: * http://tog.acm.org/resources/GraphicsGems/gemsii/unmatrix.c * * Return value: %TRUE if the decomposition was successful, and %FALSE * if the matrix is singular */ gboolean _clutter_util_matrix_decompose (const ClutterMatrix *src, graphene_point3d_t *scale_p, float shear_p[3], graphene_point3d_t *rotate_p, graphene_point3d_t *translate_p, ClutterVertex4 *perspective_p) { CoglMatrix matrix = *src; CoglMatrix perspective; ClutterVertex4 vertex_tmp; graphene_point3d_t row[3], pdum; int i, j; #define XY_SHEAR 0 #define XZ_SHEAR 1 #define YZ_SHEAR 2 #define MAT(m,r,c) ((float *)(m))[(c) * 4 + (r)] /* normalize the matrix */ if (matrix.ww == 0.f) return FALSE; for (i = 0; i < 4; i++) { for (j = 0; j < 4; j++) { MAT (&matrix, j, i) /= MAT (&matrix, 3, 3); } } /* perspective is used to solve for perspective, but it also provides * an easy way to test for singularity of the upper 3x3 component */ perspective = matrix; /* transpose */ MAT (&perspective, 3, 0) = 0.f; MAT (&perspective, 3, 1) = 0.f; MAT (&perspective, 3, 2) = 0.f; MAT (&perspective, 3, 3) = 1.f; if (_clutter_util_matrix_determinant (&perspective) == 0.f) return FALSE; if (MAT (&matrix, 3, 0) != 0.f || MAT (&matrix, 3, 1) != 0.f || MAT (&matrix, 3, 2) != 0.f) { CoglMatrix perspective_inv; ClutterVertex4 p; vertex_tmp.x = MAT (&matrix, 3, 0); vertex_tmp.y = MAT (&matrix, 3, 1); vertex_tmp.z = MAT (&matrix, 3, 2); vertex_tmp.w = MAT (&matrix, 3, 3); /* solve the equation by inverting perspective... */ cogl_matrix_get_inverse (&perspective, &perspective_inv); /* ... and multiplying vertex_tmp by the inverse */ _clutter_util_matrix_transpose_vector4_transform (&perspective_inv, &vertex_tmp, &p); *perspective_p = p; /* clear the perspective part */ MAT (&matrix, 3, 0) = 0.0f; MAT (&matrix, 3, 1) = 0.0f; MAT (&matrix, 3, 2) = 0.0f; MAT (&matrix, 3, 3) = 1.0f; } else { /* no perspective */ perspective_p->x = 0.0f; perspective_p->y = 0.0f; perspective_p->z = 0.0f; perspective_p->w = 1.0f; } /* translation */ translate_p->x = MAT (&matrix, 0, 3); MAT (&matrix, 0, 3) = 0.f; translate_p->y = MAT (&matrix, 1, 3); MAT (&matrix, 1, 3) = 0.f; translate_p->z = MAT (&matrix, 2, 3); MAT (&matrix, 2, 3) = 0.f; /* scale and shear; we split the upper 3x3 matrix into rows */ for (i = 0; i < 3; i++) { row[i].x = MAT (&matrix, i, 0); row[i].y = MAT (&matrix, i, 1); row[i].z = MAT (&matrix, i, 2); } /* compute scale.x and normalize the first row */ scale_p->x = graphene_point3d_length (&row[0]); graphene_point3d_normalize (&row[0], &row[0]); /* compute XY shear and make the second row orthogonal to the first */ shear_p[XY_SHEAR] = graphene_point3d_dot (&row[0], &row[1]); _clutter_util_vertex_combine (&row[1], &row[0], 1.0, -shear_p[XY_SHEAR], &row[1]); /* compute the Y scale and normalize the second row */ scale_p->y = graphene_point3d_length (&row[1]); graphene_point3d_normalize (&row[1], &row[1]); shear_p[XY_SHEAR] /= scale_p->y; /* compute XZ and YZ shears, orthogonalize the third row */ shear_p[XZ_SHEAR] = graphene_point3d_dot (&row[0], &row[2]); _clutter_util_vertex_combine (&row[2], &row[0], 1.0, -shear_p[XZ_SHEAR], &row[2]); shear_p[YZ_SHEAR] = graphene_point3d_dot (&row[1], &row[2]); _clutter_util_vertex_combine (&row[2], &row[1], 1.0, -shear_p[YZ_SHEAR], &row[2]); /* get the Z scale and normalize the third row*/ scale_p->z = graphene_point3d_length (&row[2]); graphene_point3d_normalize (&row[2], &row[2]); shear_p[XZ_SHEAR] /= scale_p->z; shear_p[YZ_SHEAR] /= scale_p->z; /* at this point, the matrix (inside row[]) is orthonormal. * check for a coordinate system flip; if the determinant * is -1, then negate the matrix and scaling factors */ graphene_point3d_cross (&row[1], &row[2], &pdum); if (graphene_point3d_dot (&row[0], &pdum) < 0.f) { scale_p->x *= -1.f; for (i = 0; i < 3; i++) { row[i].x *= -1.f; row[i].y *= -1.f; row[i].z *= -1.f; } } /* now get the rotations out */ rotate_p->y = asinf (-row[0].z); if (cosf (rotate_p->y) != 0.f) { rotate_p->x = atan2f (row[1].z, row[2].z); rotate_p->z = atan2f (row[0].y, row[0].x); } else { rotate_p->x = atan2f (-row[2].x, row[1].y); rotate_p->z = 0.f; } #undef XY_SHEAR #undef XZ_SHEAR #undef YZ_SHEAR #undef MAT return TRUE; } typedef struct { GType value_type; ClutterProgressFunc func; } ProgressData; G_LOCK_DEFINE_STATIC (progress_funcs); static GHashTable *progress_funcs = NULL; gboolean _clutter_has_progress_function (GType gtype) { const char *type_name = g_type_name (gtype); if (progress_funcs == NULL) return FALSE; return g_hash_table_lookup (progress_funcs, type_name) != NULL; } gboolean _clutter_run_progress_function (GType gtype, const GValue *initial, const GValue *final, gdouble progress, GValue *retval) { ProgressData *pdata; gboolean res; G_LOCK (progress_funcs); if (G_UNLIKELY (progress_funcs == NULL)) { res = FALSE; goto out; } pdata = g_hash_table_lookup (progress_funcs, g_type_name (gtype)); if (G_UNLIKELY (pdata == NULL)) { res = FALSE; goto out; } res = pdata->func (initial, final, progress, retval); out: G_UNLOCK (progress_funcs); return res; } static void progress_data_destroy (gpointer data_) { g_slice_free (ProgressData, data_); } /** * clutter_interval_register_progress_func: (skip) * @value_type: a #GType * @func: a #ClutterProgressFunc, or %NULL to unset a previously * set progress function * * Sets the progress function for a given @value_type, like: * * |[ * clutter_interval_register_progress_func (MY_TYPE_FOO, * my_foo_progress); * ]| * * Whenever a #ClutterInterval instance using the default * #ClutterInterval::compute_value implementation is set as an * interval between two #GValue of type @value_type, it will call * @func to establish the value depending on the given progress, * for instance: * * |[ * static gboolean * my_int_progress (const GValue *a, * const GValue *b, * gdouble progress, * GValue *retval) * { * gint ia = g_value_get_int (a); * gint ib = g_value_get_int (b); * gint res = factor * (ib - ia) + ia; * * g_value_set_int (retval, res); * * return TRUE; * } * * clutter_interval_register_progress_func (G_TYPE_INT, my_int_progress); * ]| * * To unset a previously set progress function of a #GType, pass %NULL * for @func. * * Since: 1.0 */ void clutter_interval_register_progress_func (GType value_type, ClutterProgressFunc func) { ProgressData *progress_func; const char *type_name; g_return_if_fail (value_type != G_TYPE_INVALID); type_name = g_type_name (value_type); G_LOCK (progress_funcs); if (G_UNLIKELY (progress_funcs == NULL)) progress_funcs = g_hash_table_new_full (NULL, NULL, NULL, progress_data_destroy); progress_func = g_hash_table_lookup (progress_funcs, type_name); if (G_UNLIKELY (progress_func)) { if (func == NULL) { g_hash_table_remove (progress_funcs, type_name); g_slice_free (ProgressData, progress_func); } else progress_func->func = func; } else { progress_func = g_slice_new (ProgressData); progress_func->value_type = value_type; progress_func->func = func; g_hash_table_replace (progress_funcs, (gpointer) type_name, progress_func); } G_UNLOCK (progress_funcs); } PangoDirection _clutter_pango_unichar_direction (gunichar ch) { FriBidiCharType fribidi_ch_type; G_STATIC_ASSERT (sizeof (FriBidiChar) == sizeof (gunichar)); fribidi_ch_type = fribidi_get_bidi_type (ch); if (!FRIBIDI_IS_STRONG (fribidi_ch_type)) return PANGO_DIRECTION_NEUTRAL; else if (FRIBIDI_IS_RTL (fribidi_ch_type)) return PANGO_DIRECTION_RTL; else return PANGO_DIRECTION_LTR; } PangoDirection _clutter_pango_find_base_dir (const gchar *text, gint length) { PangoDirection dir = PANGO_DIRECTION_NEUTRAL; const gchar *p; g_return_val_if_fail (text != NULL || length == 0, PANGO_DIRECTION_NEUTRAL); p = text; while ((length < 0 || p < text + length) && *p) { gunichar wc = g_utf8_get_char (p); dir = _clutter_pango_unichar_direction (wc); if (dir != PANGO_DIRECTION_NEUTRAL) break; p = g_utf8_next_char (p); } return dir; } muffin-6.4.1/clutter/clutter/clutter-actor-box-private.h0000664000175000017500000000040314723361714022302 0ustar fabiofabio#ifndef __CLUTTER_ACTOR_BOX_PRIVATE_H__ #define __CLUTTER_ACTOR_BOX_PRIVATE_H__ #include G_BEGIN_DECLS void _clutter_actor_box_enlarge_for_effects (ClutterActorBox *box); G_END_DECLS #endif /* __CLUTTER_ACTOR_BOX_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-easing.h0000664000175000017500000001227014723361714020207 0ustar fabiofabio#ifndef __CLUTTER_EASING_H__ #define __CLUTTER_EASING_H__ #include G_BEGIN_DECLS /*< private > * ClutterEasingFunc: * @t: elapsed time * @d: total duration * * Internal type for the easing functions used by Clutter. * * Return value: the interpolated value, between -1.0 and 2.0 */ typedef double (* ClutterEasingFunc) (double t, double d); G_GNUC_INTERNAL ClutterEasingFunc clutter_get_easing_func_for_mode (ClutterAnimationMode mode); G_GNUC_INTERNAL const char * clutter_get_easing_name_for_mode (ClutterAnimationMode mode); G_GNUC_INTERNAL double clutter_easing_for_mode (ClutterAnimationMode mode, double t, double d); G_GNUC_INTERNAL double clutter_linear (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_quad (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_quad (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_quad (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_cubic (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_cubic (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_cubic (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_quart (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_quart (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_quart (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_quint (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_quint (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_quint (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_sine (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_sine (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_sine (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_expo (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_expo (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_expo (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_circ (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_circ (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_circ (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_elastic (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_elastic (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_elastic (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_back (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_back (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_back (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_bounce (double t, double d); G_GNUC_INTERNAL double clutter_ease_out_bounce (double t, double d); G_GNUC_INTERNAL double clutter_ease_in_out_bounce (double t, double d); G_GNUC_INTERNAL double clutter_ease_steps_start (double t, double d, int steps); G_GNUC_INTERNAL double clutter_ease_steps_end (double t, double d, int steps); G_GNUC_INTERNAL double clutter_ease_cubic_bezier (double t, double d, double x_1, double y_1, double x_2, double y_2); G_END_DECLS #endif /* __CLUTTER_EASING_H__ */ muffin-6.4.1/clutter/clutter/clutter-event.h0000664000175000017500000006447514723361714020100 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_EVENT_H__ #define __CLUTTER_EVENT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_EVENT (clutter_event_get_type ()) #define CLUTTER_TYPE_EVENT_SEQUENCE (clutter_event_sequence_get_type ()) /** * CLUTTER_PRIORITY_EVENTS: * * Priority for event handling. * * Since: 0.4 */ #define CLUTTER_PRIORITY_EVENTS (G_PRIORITY_DEFAULT) /** * CLUTTER_CURRENT_TIME: * * Default value for "now". * * Since: 0.4 */ #define CLUTTER_CURRENT_TIME (0L) /** * CLUTTER_EVENT_PROPAGATE: * * Continues the propagation of an event; this macro should be * used in event-related signals. * * Since: 1.10 */ #define CLUTTER_EVENT_PROPAGATE (FALSE) /** * CLUTTER_EVENT_STOP: * * Stops the propagation of an event; this macro should be used * in event-related signals. * * Since: 1.10 */ #define CLUTTER_EVENT_STOP (TRUE) /** * CLUTTER_BUTTON_PRIMARY: * * The primary button of a pointer device. * * This is typically the left mouse button in a right-handed * mouse configuration. * * Since: 1.10 */ #define CLUTTER_BUTTON_PRIMARY (1) /** * CLUTTER_BUTTON_MIDDLE: * * The middle button of a pointer device. * * Since: 1.10 */ #define CLUTTER_BUTTON_MIDDLE (2) /** * CLUTTER_BUTTON_SECONDARY: * * The secondary button of a pointer device. * * This is typically the right mouse button in a right-handed * mouse configuration. * * Since: 1.10 */ #define CLUTTER_BUTTON_SECONDARY (3) typedef struct _ClutterAnyEvent ClutterAnyEvent; typedef struct _ClutterButtonEvent ClutterButtonEvent; typedef struct _ClutterKeyEvent ClutterKeyEvent; typedef struct _ClutterMotionEvent ClutterMotionEvent; typedef struct _ClutterScrollEvent ClutterScrollEvent; typedef struct _ClutterStageStateEvent ClutterStageStateEvent; typedef struct _ClutterCrossingEvent ClutterCrossingEvent; typedef struct _ClutterTouchEvent ClutterTouchEvent; typedef struct _ClutterTouchpadPinchEvent ClutterTouchpadPinchEvent; typedef struct _ClutterTouchpadSwipeEvent ClutterTouchpadSwipeEvent; typedef struct _ClutterProximityEvent ClutterProximityEvent; typedef struct _ClutterPadButtonEvent ClutterPadButtonEvent; typedef struct _ClutterPadStripEvent ClutterPadStripEvent; typedef struct _ClutterPadRingEvent ClutterPadRingEvent; typedef struct _ClutterIMEvent ClutterIMEvent; typedef struct _ClutterDeviceEvent ClutterDeviceEvent; /** * ClutterAnyEvent: * @type: event type * @time: event time * @flags: event flags * @source: event source actor * * Common members for a #ClutterEvent * * Since: 0.2 */ struct _ClutterAnyEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; }; /** * ClutterKeyEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @modifier_state: key modifiers * @keyval: raw key value * @hardware_keycode: raw hardware key value * @unicode_value: Unicode representation * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Key event * * Since: 0.2 */ struct _ClutterKeyEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterModifierType modifier_state; guint keyval; guint16 hardware_keycode; gunichar unicode_value; ClutterInputDevice *device; }; /** * ClutterButtonEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @x: event X coordinate, relative to the stage * @y: event Y coordinate, relative to the stage * @modifier_state: button modifiers * @button: event button * @click_count: number of button presses within the default time * and radius * @axes: reserved for future use * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Button event. * * The event coordinates are relative to the stage that received the * event, and can be transformed into actor-relative coordinates by * using clutter_actor_transform_stage_point(). * * Since: 0.2 */ struct _ClutterButtonEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; gfloat x; gfloat y; ClutterModifierType modifier_state; guint32 button; guint click_count; gdouble *axes; /* Future use */ ClutterInputDevice *device; }; /** * ClutterProximityEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Event for tool proximity in tablet devices * * Since: 1.28 */ struct _ClutterProximityEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterInputDevice *device; }; /** * ClutterCrossingEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @x: event X coordinate * @y: event Y coordinate * @related: actor related to the crossing * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Event for the movement of the pointer across different actors * * Since: 0.2 */ struct _ClutterCrossingEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; gfloat x; gfloat y; ClutterInputDevice *device; ClutterEventSequence *sequence; ClutterActor *related; }; /** * ClutterMotionEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @x: event X coordinate * @y: event Y coordinate * @modifier_state: button modifiers * @axes: reserved for future use * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Event for the pointer motion * * Since: 0.2 */ struct _ClutterMotionEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; gfloat x; gfloat y; ClutterModifierType modifier_state; gdouble *axes; /* Future use */ ClutterInputDevice *device; }; /** * ClutterScrollEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor * @x: event X coordinate * @y: event Y coordinate * @direction: direction of the scrolling * @modifier_state: button modifiers * @axes: reserved for future use * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * @scroll_source: the source of scroll events. This field is available since 1.26 * @finish_flags: the axes that were stopped in this event. This field is available since 1.26 * * Scroll wheel (or similar device) event * * Since: 0.2 */ struct _ClutterScrollEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; gfloat x; gfloat y; ClutterScrollDirection direction; ClutterModifierType modifier_state; gdouble *axes; /* future use */ ClutterInputDevice *device; ClutterScrollSource scroll_source; ClutterScrollFinishFlags finish_flags; }; /** * ClutterStageStateEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor (unused) * @changed_mask: bitwise OR of the changed flags * @new_state: bitwise OR of the current state flags * * Event signalling a change in the #ClutterStage state. * * Since: 0.2 */ struct _ClutterStageStateEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; /* XXX: should probably be the stage itself */ ClutterStageState changed_mask; ClutterStageState new_state; }; /** * ClutterTouchEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor (unused) * @x: the X coordinate of the pointer, relative to the stage * @y: the Y coordinate of the pointer, relative to the stage * @sequence: the event sequence that this event belongs to * @modifier_state: (type ClutterModifierType): a bit-mask representing the state * of modifier keys (e.g. Control, Shift, and Alt) and the pointer * buttons. See #ClutterModifierType * @axes: reserved * @device: the device that originated the event. If you want the physical * device the event originated from, use clutter_event_get_source_device() * * Used for touch events. * * The @type field will be one of %CLUTTER_TOUCH_BEGIN, %CLUTTER_TOUCH_END, * %CLUTTER_TOUCH_UPDATE, or %CLUTTER_TOUCH_CANCEL. * * Touch events are grouped into sequences; each touch sequence will begin * with a %CLUTTER_TOUCH_BEGIN event, progress with %CLUTTER_TOUCH_UPDATE * events, and end either with a %CLUTTER_TOUCH_END event or with a * %CLUTTER_TOUCH_CANCEL event. * * With multi-touch capable devices there can be multiple event sequence * running at the same time. * * Since: 1.10 */ struct _ClutterTouchEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; gfloat x; gfloat y; ClutterEventSequence *sequence; ClutterModifierType modifier_state; gdouble *axes; /* reserved */ ClutterInputDevice *device; }; /** * ClutterTouchpadPinchEvent: * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor (unused) * @phase: the current phase of the gesture * @x: the X coordinate of the pointer, relative to the stage * @y: the Y coordinate of the pointer, relative to the stage * @dx: movement delta of the pinch focal point in the X axis * @dy: movement delta of the pinch focal point in the Y axis * @angle_delta: angle delta in degrees, clockwise rotations are * represented by positive deltas * @scale: the current scale * * Used for touchpad pinch gesture events. The current state of the * gesture will be determined by the @phase field. * * Each event with phase %CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN * will report a @scale of 1.0, all later phases in the gesture * report the current scale relative to the initial 1.0 value * (eg. 0.5 being half the size, 2.0 twice as big). * * Since: 1.24 */ struct _ClutterTouchpadPinchEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterTouchpadGesturePhase phase; gfloat x; gfloat y; gfloat dx; gfloat dy; gfloat angle_delta; gfloat scale; guint n_fingers; }; /** * ClutterTouchpadSwipeEvent * @type: event type * @time: event time * @flags: event flags * @stage: event source stage * @source: event source actor (unused) * @phase: the current phase of the gesture * @n_fingers: the number of fingers triggering the swipe * @x: the X coordinate of the pointer, relative to the stage * @y: the Y coordinate of the pointer, relative to the stage * @dx: movement delta of the pinch focal point in the X axis * @dy: movement delta of the pinch focal point in the Y axis * * Used for touchpad swipe gesture events. The current state of the * gesture will be determined by the @phase field. * * Since: 1.24 */ struct _ClutterTouchpadSwipeEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterTouchpadGesturePhase phase; guint n_fingers; gfloat x; gfloat y; gfloat dx; gfloat dy; }; struct _ClutterPadButtonEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; guint32 button; guint32 group; ClutterInputDevice *device; guint32 mode; }; struct _ClutterPadStripEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterInputDevice *device; ClutterInputDevicePadSource strip_source; guint32 strip_number; guint32 group; gdouble value; guint32 mode; }; struct _ClutterPadRingEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterInputDevice *device; ClutterInputDevicePadSource ring_source; guint32 ring_number; guint32 group; gdouble angle; guint32 mode; }; struct _ClutterIMEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; char *text; int32_t offset; uint32_t len; }; struct _ClutterDeviceEvent { ClutterEventType type; guint32 time; ClutterEventFlags flags; ClutterStage *stage; ClutterActor *source; ClutterInputDevice *device; }; /** * ClutterEvent: * * Generic event wrapper. * * Since: 0.2 */ union _ClutterEvent { /*< private >*/ ClutterEventType type; ClutterAnyEvent any; ClutterButtonEvent button; ClutterKeyEvent key; ClutterMotionEvent motion; ClutterScrollEvent scroll; ClutterStageStateEvent stage_state; ClutterCrossingEvent crossing; ClutterTouchEvent touch; ClutterTouchpadPinchEvent touchpad_pinch; ClutterTouchpadSwipeEvent touchpad_swipe; ClutterProximityEvent proximity; ClutterPadButtonEvent pad_button; ClutterPadStripEvent pad_strip; ClutterPadRingEvent pad_ring; ClutterIMEvent im; ClutterDeviceEvent device; }; /** * ClutterEventFilterFunc: * @event: the event that is going to be emitted * @user_data: the data pointer passed to clutter_event_add_filter() * * A function pointer type used by event filters that are added with * clutter_event_add_filter(). * * Return value: %CLUTTER_EVENT_STOP to indicate that the event * has been handled or %CLUTTER_EVENT_PROPAGATE otherwise. * Returning %CLUTTER_EVENT_STOP skips any further filter * functions and prevents the signal emission for the event. * * Since: 1.18 */ typedef gboolean (* ClutterEventFilterFunc) (const ClutterEvent *event, gpointer user_data); CLUTTER_EXPORT GType clutter_event_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GType clutter_event_sequence_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT gboolean clutter_events_pending (void); CLUTTER_EXPORT ClutterEvent * clutter_event_get (void); CLUTTER_EXPORT ClutterEvent * clutter_event_peek (void); CLUTTER_EXPORT void clutter_event_put (const ClutterEvent *event); CLUTTER_EXPORT guint clutter_event_add_filter (ClutterStage *stage, ClutterEventFilterFunc func, GDestroyNotify notify, gpointer user_data); CLUTTER_EXPORT void clutter_event_remove_filter (guint id); CLUTTER_EXPORT ClutterEvent * clutter_event_new (ClutterEventType type); CLUTTER_EXPORT ClutterEvent * clutter_event_copy (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_free (ClutterEvent *event); CLUTTER_EXPORT ClutterEventType clutter_event_type (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_flags (ClutterEvent *event, ClutterEventFlags flags); CLUTTER_EXPORT ClutterEventFlags clutter_event_get_flags (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_time (ClutterEvent *event, guint32 time_); CLUTTER_EXPORT guint32 clutter_event_get_time (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_state (ClutterEvent *event, ClutterModifierType state); CLUTTER_EXPORT ClutterModifierType clutter_event_get_state (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_get_state_full (const ClutterEvent *event, ClutterModifierType *button_state, ClutterModifierType *base_state, ClutterModifierType *latched_state, ClutterModifierType *locked_state, ClutterModifierType *effective_state); CLUTTER_EXPORT void clutter_event_set_device (ClutterEvent *event, ClutterInputDevice *device); CLUTTER_EXPORT ClutterInputDevice * clutter_event_get_device (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_source_device (ClutterEvent *event, ClutterInputDevice *device); CLUTTER_EXPORT ClutterInputDevice * clutter_event_get_source_device (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_device_tool (ClutterEvent *event, ClutterInputDeviceTool *tool); CLUTTER_EXPORT ClutterInputDeviceTool *clutter_event_get_device_tool (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_source (ClutterEvent *event, ClutterActor *actor); CLUTTER_EXPORT ClutterActor * clutter_event_get_source (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_stage (ClutterEvent *event, ClutterStage *stage); CLUTTER_EXPORT ClutterStage * clutter_event_get_stage (const ClutterEvent *event); CLUTTER_EXPORT gint clutter_event_get_device_id (const ClutterEvent *event); CLUTTER_EXPORT ClutterInputDeviceType clutter_event_get_device_type (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_coords (ClutterEvent *event, gfloat x, gfloat y); CLUTTER_EXPORT void clutter_event_get_coords (const ClutterEvent *event, gfloat *x, gfloat *y); CLUTTER_EXPORT void clutter_event_get_position (const ClutterEvent *event, graphene_point_t *position); CLUTTER_EXPORT float clutter_event_get_distance (const ClutterEvent *source, const ClutterEvent *target); CLUTTER_EXPORT double clutter_event_get_angle (const ClutterEvent *source, const ClutterEvent *target); CLUTTER_EXPORT gdouble * clutter_event_get_axes (const ClutterEvent *event, guint *n_axes); CLUTTER_EXPORT gboolean clutter_event_has_shift_modifier (const ClutterEvent *event); CLUTTER_EXPORT gboolean clutter_event_has_control_modifier (const ClutterEvent *event); CLUTTER_EXPORT gboolean clutter_event_is_pointer_emulated (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_key_symbol (ClutterEvent *event, guint key_sym); CLUTTER_EXPORT guint clutter_event_get_key_symbol (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_key_code (ClutterEvent *event, guint16 key_code); CLUTTER_EXPORT guint16 clutter_event_get_key_code (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_key_unicode (ClutterEvent *event, gunichar key_unicode); CLUTTER_EXPORT gunichar clutter_event_get_key_unicode (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_button (ClutterEvent *event, guint32 button); CLUTTER_EXPORT guint32 clutter_event_get_button (const ClutterEvent *event); CLUTTER_EXPORT guint clutter_event_get_click_count (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_related (ClutterEvent *event, ClutterActor *actor); CLUTTER_EXPORT ClutterActor * clutter_event_get_related (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_scroll_direction (ClutterEvent *event, ClutterScrollDirection direction); CLUTTER_EXPORT ClutterScrollDirection clutter_event_get_scroll_direction (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_set_scroll_delta (ClutterEvent *event, gdouble dx, gdouble dy); CLUTTER_EXPORT void clutter_event_get_scroll_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy); CLUTTER_EXPORT ClutterEventSequence * clutter_event_get_event_sequence (const ClutterEvent *event); CLUTTER_EXPORT guint32 clutter_keysym_to_unicode (guint keyval); CLUTTER_EXPORT guint clutter_unicode_to_keysym (guint32 wc); CLUTTER_EXPORT guint32 clutter_get_current_event_time (void); CLUTTER_EXPORT const ClutterEvent * clutter_get_current_event (void); CLUTTER_EXPORT guint clutter_event_get_touchpad_gesture_finger_count (const ClutterEvent *event); CLUTTER_EXPORT gdouble clutter_event_get_gesture_pinch_angle_delta (const ClutterEvent *event); CLUTTER_EXPORT gdouble clutter_event_get_gesture_pinch_scale (const ClutterEvent *event); CLUTTER_EXPORT ClutterTouchpadGesturePhase clutter_event_get_gesture_phase (const ClutterEvent *event); CLUTTER_EXPORT void clutter_event_get_gesture_motion_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy); CLUTTER_EXPORT ClutterScrollSource clutter_event_get_scroll_source (const ClutterEvent *event); CLUTTER_EXPORT ClutterScrollFinishFlags clutter_event_get_scroll_finish_flags (const ClutterEvent *event); CLUTTER_EXPORT guint clutter_event_get_mode_group (const ClutterEvent *event); CLUTTER_EXPORT gboolean clutter_event_get_pad_event_details (const ClutterEvent *event, guint *number, guint *mode, gdouble *value); G_END_DECLS #endif /* __CLUTTER_EVENT_H__ */ muffin-6.4.1/clutter/clutter/clutter-property-transition.h0000664000175000017500000000646314723361714023024 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_PROPERTY_TRANSITION_H__ #define __CLUTTER_PROPERTY_TRANSITION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_PROPERTY_TRANSITION (clutter_property_transition_get_type ()) #define CLUTTER_PROPERTY_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PROPERTY_TRANSITION, ClutterPropertyTransition)) #define CLUTTER_IS_PROPERTY_TRANSITION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PROPERTY_TRANSITION)) #define CLUTTER_PROPERTY_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_PROPERTY_TRANSITION, ClutterPropertyTransitionClass)) #define CLUTTER_IS_PROPERTY_TRANSITION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_PROPERTY_TRANSITION)) #define CLUTTER_PROPERTY_TRANSITION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_PROPERTY_TRANSITION, ClutterPropertyTransitionClass)) typedef struct _ClutterPropertyTransitionPrivate ClutterPropertyTransitionPrivate; typedef struct _ClutterPropertyTransitionClass ClutterPropertyTransitionClass; /** * ClutterPropertyTransition: * * The #ClutterPropertyTransition structure contains * private data and should only be accessed using the provided API. * * Since: 1.10 */ struct _ClutterPropertyTransition { /*< private >*/ ClutterTransition parent_instance; ClutterPropertyTransitionPrivate *priv; }; /** * ClutterPropertyTransitionClass: * * The #ClutterPropertyTransitionClass structure * contains private data. * * Since: 1.10 */ struct _ClutterPropertyTransitionClass { /*< private >*/ ClutterTransitionClass parent_class; gpointer _padding[8]; }; CLUTTER_EXPORT GType clutter_property_transition_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterTransition * clutter_property_transition_new (const char *property_name); CLUTTER_EXPORT void clutter_property_transition_set_property_name (ClutterPropertyTransition *transition, const char *property_name); CLUTTER_EXPORT const char * clutter_property_transition_get_property_name (ClutterPropertyTransition *transition); G_END_DECLS #endif /* __CLUTTER_PROPERTY_TRANSITION_H__ */ muffin-6.4.1/clutter/clutter/clutter-keymap.h0000664000175000017500000000350714723361714020232 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef CLUTTER_KEYMAP_H #define CLUTTER_KEYMAP_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include typedef struct _ClutterKeymap ClutterKeymap; typedef struct _ClutterKeymapClass ClutterKeymapClass; struct _ClutterKeymapClass { GObjectClass parent_class; gboolean (* get_num_lock_state) (ClutterKeymap *keymap); gboolean (* get_caps_lock_state) (ClutterKeymap *keymap); PangoDirection (* get_direction) (ClutterKeymap *keymap); }; #define CLUTTER_TYPE_KEYMAP (clutter_keymap_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterKeymap, clutter_keymap, CLUTTER, KEYMAP, GObject) CLUTTER_EXPORT gboolean clutter_keymap_get_num_lock_state (ClutterKeymap *keymap); CLUTTER_EXPORT gboolean clutter_keymap_get_caps_lock_state (ClutterKeymap *keymap); PangoDirection clutter_keymap_get_direction (ClutterKeymap *keymap); #endif /* CLUTTER_KEYMAP_H */ muffin-6.4.1/clutter/clutter/clutter-backend.c0000664000175000017500000006704114723361714020331 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: * Matthew Allum * Emmanuele Bassi * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-backend * @short_description: Backend abstraction * * Clutter can be compiled against different backends. Each backend * has to implement a set of functions, in order to be used by Clutter. * * #ClutterBackend is the base class abstracting the various implementation; * it provides a basic API to query the backend for generic information * and settings. * * #ClutterBackend is available since Clutter 0.4 */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-backend-private.h" #include "clutter-debug.h" #include "clutter-event-private.h" #include "clutter-marshal.h" #include "clutter-mutter.h" #include "clutter-private.h" #include "clutter-stage-manager-private.h" #include "clutter-stage-private.h" #include "clutter-stage-window.h" #ifdef CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT #include "wayland/clutter-wayland-compositor.h" #endif #include #ifdef CLUTTER_INPUT_X11 #include "x11/clutter-backend-x11.h" #endif #ifdef CLUTTER_WINDOWING_EGL #include "egl/clutter-backend-eglnative.h" #endif #ifdef CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT #include #include #include "wayland/clutter-wayland-compositor.h" #endif #define DEFAULT_FONT_NAME "Sans 10" enum { RESOLUTION_CHANGED, FONT_CHANGED, SETTINGS_CHANGED, LAST_SIGNAL }; G_DEFINE_ABSTRACT_TYPE (ClutterBackend, clutter_backend, G_TYPE_OBJECT) static guint backend_signals[LAST_SIGNAL] = { 0, }; /* Global for being able to specify a compositor side wayland display * pointer before clutter initialization */ #ifdef CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT static struct wl_display *_wayland_compositor_display; #endif static void clutter_backend_dispose (GObject *gobject) { ClutterBackend *backend = CLUTTER_BACKEND (gobject); /* clear the events still in the queue of the main context */ _clutter_clear_events_queue (); g_clear_pointer (&backend->dummy_onscreen, cogl_object_unref); if (backend->stage_window) { g_object_remove_weak_pointer (G_OBJECT (backend->stage_window), (gpointer *) &backend->stage_window); } G_OBJECT_CLASS (clutter_backend_parent_class)->dispose (gobject); } static void clutter_backend_finalize (GObject *gobject) { ClutterBackend *backend = CLUTTER_BACKEND (gobject); g_source_destroy (backend->cogl_source); g_free (backend->font_name); clutter_backend_set_font_options (backend, NULL); g_clear_object (&backend->input_method); G_OBJECT_CLASS (clutter_backend_parent_class)->finalize (gobject); } static gfloat get_units_per_em (ClutterBackend *backend, PangoFontDescription *font_desc) { gfloat units_per_em = -1.0; gboolean free_font_desc = FALSE; gdouble dpi; dpi = clutter_backend_get_resolution (backend); if (font_desc == NULL) { ClutterSettings *settings; gchar *font_name = NULL; settings = clutter_settings_get_default (); g_object_get (settings, "font-name", &font_name, NULL); if (G_LIKELY (font_name != NULL && *font_name != '\0')) { font_desc = pango_font_description_from_string (font_name); free_font_desc = TRUE; g_free (font_name); } } if (font_desc != NULL) { gdouble font_size = 0; gint pango_size; gboolean is_absolute; pango_size = pango_font_description_get_size (font_desc); is_absolute = pango_font_description_get_size_is_absolute (font_desc); /* "absolute" means "device units" (usually, pixels); otherwise, * it means logical units (points) */ if (is_absolute) font_size = (gdouble) pango_size / PANGO_SCALE; else font_size = dpi * ((gdouble) pango_size / PANGO_SCALE) / 72.0f; /* 10 points at 96 DPI is 13.3 pixels */ units_per_em = (1.2f * font_size) * dpi / 96.0f; } else units_per_em = -1.0f; if (free_font_desc) pango_font_description_free (font_desc); return units_per_em; } static void clutter_backend_real_resolution_changed (ClutterBackend *backend) { ClutterMainContext *context; ClutterSettings *settings; gdouble resolution; gint dpi; settings = clutter_settings_get_default (); g_object_get (settings, "font-dpi", &dpi, NULL); if (dpi < 0) resolution = 96.0; else resolution = dpi / 1024.0; context = _clutter_context_get_default (); if (context->font_map != NULL) cogl_pango_font_map_set_resolution (context->font_map, resolution); backend->units_per_em = get_units_per_em (backend, NULL); backend->units_serial += 1; CLUTTER_NOTE (BACKEND, "Units per em: %.2f", backend->units_per_em); } static void clutter_backend_real_font_changed (ClutterBackend *backend) { backend->units_per_em = get_units_per_em (backend, NULL); backend->units_serial += 1; CLUTTER_NOTE (BACKEND, "Units per em: %.2f", backend->units_per_em); } static gboolean clutter_backend_do_real_create_context (ClutterBackend *backend, CoglDriver driver_id, GError **error) { ClutterBackendClass *klass; CoglSwapChain *swap_chain; GError *internal_error; klass = CLUTTER_BACKEND_GET_CLASS (backend); swap_chain = NULL; internal_error = NULL; CLUTTER_NOTE (BACKEND, "Creating Cogl renderer"); backend->cogl_renderer = klass->get_renderer (backend, &internal_error); if (backend->cogl_renderer == NULL) goto error; CLUTTER_NOTE (BACKEND, "Connecting the renderer"); cogl_renderer_set_driver (backend->cogl_renderer, driver_id); if (!cogl_renderer_connect (backend->cogl_renderer, &internal_error)) goto error; CLUTTER_NOTE (BACKEND, "Creating Cogl swap chain"); swap_chain = cogl_swap_chain_new (); CLUTTER_NOTE (BACKEND, "Creating Cogl display"); if (klass->get_display != NULL) { backend->cogl_display = klass->get_display (backend, backend->cogl_renderer, swap_chain, &internal_error); } else { CoglOnscreenTemplate *tmpl; gboolean res; tmpl = cogl_onscreen_template_new (swap_chain); /* XXX: I have some doubts that this is a good design. * * Conceptually should we be able to check an onscreen_template * without more details about the CoglDisplay configuration? */ res = cogl_renderer_check_onscreen_template (backend->cogl_renderer, tmpl, &internal_error); if (!res) goto error; backend->cogl_display = cogl_display_new (backend->cogl_renderer, tmpl); /* the display owns the template */ cogl_object_unref (tmpl); } if (backend->cogl_display == NULL) goto error; #ifdef CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT cogl_wayland_display_set_compositor_display (backend->cogl_display, _wayland_compositor_display); #endif CLUTTER_NOTE (BACKEND, "Setting up the display"); if (!cogl_display_setup (backend->cogl_display, &internal_error)) goto error; CLUTTER_NOTE (BACKEND, "Creating the Cogl context"); backend->cogl_context = cogl_context_new (backend->cogl_display, &internal_error); if (backend->cogl_context == NULL) goto error; /* the display owns the renderer and the swap chain */ cogl_object_unref (backend->cogl_renderer); cogl_object_unref (swap_chain); return TRUE; error: if (backend->cogl_display != NULL) { cogl_object_unref (backend->cogl_display); backend->cogl_display = NULL; } if (backend->cogl_renderer != NULL) { cogl_object_unref (backend->cogl_renderer); backend->cogl_renderer = NULL; } if (swap_chain != NULL) cogl_object_unref (swap_chain); return FALSE; } static const struct { const char *driver_name; const char *driver_desc; CoglDriver driver_id; } all_known_drivers[] = { { "gl3", "OpenGL 3.2 core profile", COGL_DRIVER_GL3 }, { "gl", "OpenGL legacy profile", COGL_DRIVER_GL }, { "gles2", "OpenGL ES 2.0", COGL_DRIVER_GLES2 }, { "any", "Default Cogl driver", COGL_DRIVER_ANY }, }; static const char *allowed_drivers; static gboolean clutter_backend_real_create_context (ClutterBackend *backend, GError **error) { GError *internal_error = NULL; const char *drivers_list; char **known_drivers; gboolean allow_any; int i; if (backend->cogl_context != NULL) return TRUE; if (allowed_drivers == NULL) allowed_drivers = CLUTTER_DRIVERS; allow_any = strstr (allowed_drivers, "*") != NULL; drivers_list = g_getenv ("CLUTTER_DRIVER"); if (drivers_list == NULL) drivers_list = allowed_drivers; known_drivers = g_strsplit (drivers_list, ",", 0); for (i = 0; backend->cogl_context == NULL && known_drivers[i] != NULL; i++) { const char *driver_name = known_drivers[i]; gboolean is_any = g_str_equal (driver_name, "*"); int j; for (j = 0; j < G_N_ELEMENTS (all_known_drivers); j++) { if (!allow_any && !is_any && !strstr (driver_name, all_known_drivers[j].driver_name)) continue; if ((allow_any && is_any) || (is_any && strstr (allowed_drivers, all_known_drivers[j].driver_name)) || g_str_equal (all_known_drivers[j].driver_name, driver_name)) { CLUTTER_NOTE (BACKEND, "Checking for the %s driver", all_known_drivers[j].driver_desc); if (clutter_backend_do_real_create_context (backend, all_known_drivers[j].driver_id, &internal_error)) break; if (internal_error) { CLUTTER_NOTE (BACKEND, "Unable to use the %s driver: %s", all_known_drivers[j].driver_desc, internal_error->message); g_clear_error (&internal_error); } } } } g_strfreev (known_drivers); if (backend->cogl_context == NULL) { if (internal_error != NULL) g_propagate_error (error, internal_error); else g_set_error_literal (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to initialize the Clutter backend: no available drivers found."); return FALSE; } backend->cogl_source = cogl_glib_source_new (backend->cogl_context, G_PRIORITY_DEFAULT); g_source_attach (backend->cogl_source, NULL); return TRUE; } static ClutterFeatureFlags clutter_backend_real_get_features (ClutterBackend *backend) { ClutterFeatureFlags flags = 0; if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN)) { CLUTTER_NOTE (BACKEND, "Cogl supports multiple onscreen framebuffers"); flags |= CLUTTER_FEATURE_STAGE_MULTIPLE; } else { CLUTTER_NOTE (BACKEND, "Cogl only supports one onscreen framebuffer"); flags |= CLUTTER_FEATURE_STAGE_STATIC; } if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_THROTTLE)) { CLUTTER_NOTE (BACKEND, "Cogl supports swap buffers throttling"); flags |= CLUTTER_FEATURE_SWAP_THROTTLE; } else CLUTTER_NOTE (BACKEND, "Cogl doesn't support swap buffers throttling"); if (cogl_clutter_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT)) { CLUTTER_NOTE (BACKEND, "Cogl supports swap buffers complete events"); flags |= CLUTTER_FEATURE_SWAP_EVENTS; } return flags; } static const char *allowed_backends; static ClutterBackend * (* custom_backend_func) (void); static const struct { const char *name; ClutterBackend * (* create_backend) (void); } available_backends[] = { #ifdef CLUTTER_WINDOWING_X11 { CLUTTER_WINDOWING_X11, clutter_backend_x11_new }, #endif #ifdef CLUTTER_WINDOWING_EGL { CLUTTER_WINDOWING_EGL, clutter_backend_egl_native_new }, #endif { NULL, NULL }, }; void clutter_set_custom_backend_func (ClutterBackend *(* func) (void)) { custom_backend_func = func; } ClutterBackend * _clutter_create_backend (void) { const char *backends_list; ClutterBackend *retval; gboolean allow_any; char **backends; int i; if (custom_backend_func) { retval = custom_backend_func (); if (!retval) g_error ("Failed to create custom backend."); return retval; } if (allowed_backends == NULL) allowed_backends = "*"; allow_any = strstr (allowed_backends, "*") != NULL; backends_list = g_getenv ("CLUTTER_BACKEND"); if (backends_list == NULL) backends_list = allowed_backends; backends = g_strsplit (backends_list, ",", 0); retval = NULL; for (i = 0; retval == NULL && backends[i] != NULL; i++) { const char *backend = backends[i]; gboolean is_any = g_str_equal (backend, "*"); int j; for (j = 0; available_backends[j].name != NULL; j++) { if ((is_any && allow_any) || (is_any && strstr (allowed_backends, available_backends[j].name)) || g_str_equal (backend, available_backends[j].name)) { retval = available_backends[j].create_backend (); if (retval != NULL) break; } } } g_strfreev (backends); if (retval == NULL) g_error ("No default Clutter backend found."); return retval; } static void clutter_backend_real_init_events (ClutterBackend *backend) { g_error ("Unknown input backend"); } static void clutter_backend_class_init (ClutterBackendClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = clutter_backend_dispose; gobject_class->finalize = clutter_backend_finalize; /** * ClutterBackend::resolution-changed: * @backend: the #ClutterBackend that emitted the signal * * The ::resolution-changed signal is emitted each time the font * resolutions has been changed through #ClutterSettings. * * Since: 1.0 */ backend_signals[RESOLUTION_CHANGED] = g_signal_new (I_("resolution-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterBackendClass, resolution_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterBackend::font-changed: * @backend: the #ClutterBackend that emitted the signal * * The ::font-changed signal is emitted each time the font options * have been changed through #ClutterSettings. * * Since: 1.0 */ backend_signals[FONT_CHANGED] = g_signal_new (I_("font-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterBackendClass, font_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterBackend::settings-changed: * @backend: the #ClutterBackend that emitted the signal * * The ::settings-changed signal is emitted each time the #ClutterSettings * properties have been changed. * * Since: 1.4 */ backend_signals[SETTINGS_CHANGED] = g_signal_new (I_("settings-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterBackendClass, settings_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); klass->resolution_changed = clutter_backend_real_resolution_changed; klass->font_changed = clutter_backend_real_font_changed; klass->init_events = clutter_backend_real_init_events; klass->create_context = clutter_backend_real_create_context; klass->get_features = clutter_backend_real_get_features; } static void clutter_backend_init (ClutterBackend *self) { self->units_per_em = -1.0; self->units_serial = 1; self->dummy_onscreen = NULL; } void _clutter_backend_add_options (ClutterBackend *backend, GOptionGroup *group) { ClutterBackendClass *klass; g_assert (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); if (klass->add_options) klass->add_options (backend, group); } gboolean _clutter_backend_pre_parse (ClutterBackend *backend, GError **error) { ClutterBackendClass *klass; g_assert (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); if (klass->pre_parse) return klass->pre_parse (backend, error); return TRUE; } gboolean _clutter_backend_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendClass *klass; g_assert (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); if (klass->post_parse) return klass->post_parse (backend, error); return TRUE; } ClutterStageWindow * _clutter_backend_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error) { ClutterBackendClass *klass; ClutterStageWindow *stage_window; g_assert (CLUTTER_IS_BACKEND (backend)); g_assert (CLUTTER_IS_STAGE (wrapper)); klass = CLUTTER_BACKEND_GET_CLASS (backend); if (klass->create_stage != NULL) stage_window = klass->create_stage (backend, wrapper, error); else stage_window = NULL; if (stage_window == NULL) return NULL; g_assert (CLUTTER_IS_STAGE_WINDOW (stage_window)); backend->stage_window = stage_window; g_object_add_weak_pointer (G_OBJECT (backend->stage_window), (gpointer *) &backend->stage_window); return stage_window; } gboolean _clutter_backend_create_context (ClutterBackend *backend, GError **error) { ClutterBackendClass *klass; klass = CLUTTER_BACKEND_GET_CLASS (backend); return klass->create_context (backend, error); } ClutterFeatureFlags _clutter_backend_get_features (ClutterBackend *backend) { ClutterBackendClass *klass; GError *error; g_assert (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); /* we need to have a context here; so we create the * GL context first and the ask for features. if the * context already exists this should be a no-op */ error = NULL; if (klass->create_context != NULL) { gboolean res; res = klass->create_context (backend, &error); if (!res) { if (error) { g_critical ("Unable to create a context: %s", error->message); g_error_free (error); } else g_critical ("Unable to create a context: unknown error"); return 0; } } if (klass->get_features) return klass->get_features (backend); return 0; } void _clutter_backend_init_events (ClutterBackend *backend) { ClutterBackendClass *klass; g_assert (CLUTTER_IS_BACKEND (backend)); klass = CLUTTER_BACKEND_GET_CLASS (backend); klass->init_events (backend); } gfloat _clutter_backend_get_units_per_em (ClutterBackend *backend, PangoFontDescription *font_desc) { /* recompute for the font description, but do not cache the result */ if (font_desc != NULL) return get_units_per_em (backend, font_desc); if (backend->units_per_em < 0) backend->units_per_em = get_units_per_em (backend, NULL); return backend->units_per_em; } void _clutter_backend_copy_event_data (ClutterBackend *backend, const ClutterEvent *src, ClutterEvent *dest) { ClutterSeatClass *seat_class; ClutterSeat *seat; seat = clutter_backend_get_default_seat (backend); seat_class = CLUTTER_SEAT_GET_CLASS (seat); seat_class->copy_event_data (seat, src, dest); } void _clutter_backend_free_event_data (ClutterBackend *backend, ClutterEvent *event) { ClutterSeatClass *seat_class; ClutterSeat *seat; seat = clutter_backend_get_default_seat (backend); seat_class = CLUTTER_SEAT_GET_CLASS (seat); seat_class->free_event_data (seat, event); } /** * clutter_get_default_backend: * * Retrieves the default #ClutterBackend used by Clutter. The * #ClutterBackend holds backend-specific configuration options. * * Return value: (transfer none): the default backend. You should * not ref or unref the returned object. Applications should rarely * need to use this. * * Since: 0.4 */ ClutterBackend * clutter_get_default_backend (void) { ClutterMainContext *clutter_context; clutter_context = _clutter_context_get_default (); return clutter_context->backend; } /** * clutter_backend_get_resolution: * @backend: a #ClutterBackend * * Gets the resolution for font handling on the screen. * * The resolution is a scale factor between points specified in a * #PangoFontDescription and cairo units. The default value is 96.0, * meaning that a 10 point font will be 13 units * high (10 * 96. / 72. = 13.3). * * Clutter will set the resolution using the current backend when * initializing; the resolution is also stored in the * #ClutterSettings:font-dpi property. * * Return value: the current resolution, or -1 if no resolution * has been set. * * Since: 0.4 */ gdouble clutter_backend_get_resolution (ClutterBackend *backend) { ClutterSettings *settings; gint resolution; g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), -1.0); settings = clutter_settings_get_default (); g_object_get (settings, "font-dpi", &resolution, NULL); if (resolution < 0) return 96.0; return resolution / 1024.0; } /** * clutter_backend_set_font_options: * @backend: a #ClutterBackend * @options: Cairo font options for the backend, or %NULL * * Sets the new font options for @backend. The #ClutterBackend will * copy the #cairo_font_options_t. * * If @options is %NULL, the first following call to * clutter_backend_get_font_options() will return the default font * options for @backend. * * This function is intended for actors creating a Pango layout * using the PangoCairo API. * * Since: 0.8 */ void clutter_backend_set_font_options (ClutterBackend *backend, const cairo_font_options_t *options) { g_return_if_fail (CLUTTER_IS_BACKEND (backend)); if (backend->font_options != options) { if (backend->font_options) cairo_font_options_destroy (backend->font_options); if (options) backend->font_options = cairo_font_options_copy (options); else backend->font_options = NULL; g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); } } /** * clutter_backend_get_font_options: * @backend: a #ClutterBackend * * Retrieves the font options for @backend. * * Return value: (transfer none): the font options of the #ClutterBackend. * The returned #cairo_font_options_t is owned by the backend and should * not be modified or freed * * Since: 0.8 */ const cairo_font_options_t * clutter_backend_get_font_options (ClutterBackend *backend) { g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); if (G_LIKELY (backend->font_options)) return backend->font_options; backend->font_options = cairo_font_options_create (); cairo_font_options_set_hint_style (backend->font_options, CAIRO_HINT_STYLE_NONE); cairo_font_options_set_subpixel_order (backend->font_options, CAIRO_SUBPIXEL_ORDER_DEFAULT); cairo_font_options_set_antialias (backend->font_options, CAIRO_ANTIALIAS_DEFAULT); g_signal_emit (backend, backend_signals[FONT_CHANGED], 0); return backend->font_options; } gint32 _clutter_backend_get_units_serial (ClutterBackend *backend) { return backend->units_serial; } gboolean _clutter_backend_translate_event (ClutterBackend *backend, gpointer native, ClutterEvent *event) { return CLUTTER_BACKEND_GET_CLASS (backend)->translate_event (backend, native, event); } /** * clutter_backend_get_cogl_context: (skip) * @backend: a #ClutterBackend * * Retrieves the #CoglContext associated with the given clutter * @backend. A #CoglContext is required when using some of the * experimental 2.0 Cogl API. * * Since CoglContext is itself experimental API this API should * be considered experimental too. * * This API is not yet supported on OSX because OSX still * uses the stub Cogl winsys and the Clutter backend doesn't * explicitly create a CoglContext. * * Return value: (transfer none): The #CoglContext associated with @backend. * * Since: 1.8 * Stability: unstable */ CoglContext * clutter_backend_get_cogl_context (ClutterBackend *backend) { return backend->cogl_context; } #ifdef CLUTTER_HAS_WAYLAND_COMPOSITOR_SUPPORT /** * clutter_wayland_set_compositor_display: * @display: A compositor side struct wl_display pointer * * This informs Clutter of your compositor side Wayland display * object. This must be called before calling clutter_init(). * * Since: 1.8 * Stability: unstable */ void clutter_wayland_set_compositor_display (void *display) { if (_clutter_context_is_initialized ()) { g_warning ("%s() can only be used before calling clutter_init()", G_STRFUNC); return; } _wayland_compositor_display = display; } #endif void clutter_set_allowed_drivers (const char *drivers) { if (_clutter_context_is_initialized ()) { g_warning ("Clutter has already been initialized.\n"); return; } allowed_drivers = g_strdup (drivers); } /** * clutter_backend_get_input_method: * @backend: the #CLutterBackend * * Returns the input method used by Clutter * * Returns: (transfer none): the input method **/ ClutterInputMethod * clutter_backend_get_input_method (ClutterBackend *backend) { return backend->input_method; } /** * clutter_backend_set_input_method: * @backend: the #ClutterBackend * @method: the input method * * Sets the input method to be used by Clutter **/ void clutter_backend_set_input_method (ClutterBackend *backend, ClutterInputMethod *method) { g_set_object (&backend->input_method, method); } ClutterStageWindow * clutter_backend_get_stage_window (ClutterBackend *backend) { return backend->stage_window; } /** * clutter_backend_get_default_seat: * @backend: the #ClutterBackend * * Returns the default seat * * Returns: (transfer none): the default seat **/ ClutterSeat * clutter_backend_get_default_seat (ClutterBackend *backend) { g_return_val_if_fail (CLUTTER_IS_BACKEND (backend), NULL); return CLUTTER_BACKEND_GET_CLASS (backend)->get_default_seat (backend); } muffin-6.4.1/clutter/clutter/clutter-build-config.h.meson0000664000175000017500000000047414723361714022426 0ustar fabiofabio/* Mutter version */ #mesondefine MUTTER_VERSION /* List of Cogl drivers */ #mesondefine CLUTTER_DRIVERS /* Have evdev support for input handling */ #mesondefine HAVE_EVDEV /* Building with libwacom for advanced tablet management */ #mesondefine HAVE_LIBWACOM /* Supports PangoFt2 */ #mesondefine HAVE_PANGO_FT2 muffin-6.4.1/clutter/clutter/clutter-input-method.c0000664000175000017500000003632314723361714021356 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "clutter-build-config.h" #include "clutter-private.h" #include "clutter/clutter-input-device-private.h" #include "clutter/clutter-input-method.h" #include "clutter/clutter-input-method-private.h" #include "clutter/clutter-input-focus-private.h" typedef struct _ClutterInputMethodPrivate ClutterInputMethodPrivate; struct _ClutterInputMethodPrivate { ClutterInputFocus *focus; ClutterInputContentHintFlags content_hints; ClutterInputContentPurpose content_purpose; gboolean can_show_preedit; }; enum { COMMIT, DELETE_SURROUNDING, REQUEST_SURROUNDING, INPUT_PANEL_STATE, CURSOR_LOCATION_CHANGED, N_SIGNALS, }; enum { PROP_0, PROP_CONTENT_HINTS, PROP_CONTENT_PURPOSE, PROP_CAN_SHOW_PREEDIT, N_PROPS }; static guint signals[N_SIGNALS] = { 0 }; static GParamSpec *pspecs[N_PROPS] = { 0 }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterInputMethod, clutter_input_method, G_TYPE_OBJECT) static void set_content_hints (ClutterInputMethod *im, ClutterInputContentHintFlags content_hints) { ClutterInputMethodPrivate *priv; priv = clutter_input_method_get_instance_private (im); priv->content_hints = content_hints; CLUTTER_INPUT_METHOD_GET_CLASS (im)->update_content_hints (im, content_hints); } static void set_content_purpose (ClutterInputMethod *im, ClutterInputContentPurpose content_purpose) { ClutterInputMethodPrivate *priv; priv = clutter_input_method_get_instance_private (im); priv->content_purpose = content_purpose; CLUTTER_INPUT_METHOD_GET_CLASS (im)->update_content_purpose (im, content_purpose); } static void set_can_show_preedit (ClutterInputMethod *im, gboolean can_show_preedit) { ClutterInputMethodPrivate *priv; priv = clutter_input_method_get_instance_private (im); priv->can_show_preedit = can_show_preedit; } static void clutter_input_method_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_CONTENT_HINTS: set_content_hints (CLUTTER_INPUT_METHOD (object), g_value_get_flags (value)); break; case PROP_CONTENT_PURPOSE: set_content_purpose (CLUTTER_INPUT_METHOD (object), g_value_get_enum (value)); break; case PROP_CAN_SHOW_PREEDIT: set_can_show_preedit (CLUTTER_INPUT_METHOD (object), g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_input_method_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterInputMethodPrivate *priv; ClutterInputMethod *im; im = CLUTTER_INPUT_METHOD (object); priv = clutter_input_method_get_instance_private (im); switch (prop_id) { case PROP_CONTENT_HINTS: g_value_set_flags (value, priv->content_hints); break; case PROP_CONTENT_PURPOSE: g_value_set_enum (value, priv->content_purpose); break; case PROP_CAN_SHOW_PREEDIT: g_value_set_boolean (value, priv->can_show_preedit); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_input_method_class_init (ClutterInputMethodClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = clutter_input_method_set_property; object_class->get_property = clutter_input_method_get_property; signals[COMMIT] = g_signal_new ("commit", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING); signals[DELETE_SURROUNDING] = g_signal_new ("delete-surrounding", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_UINT); signals[REQUEST_SURROUNDING] = g_signal_new ("request-surrounding", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[INPUT_PANEL_STATE] = g_signal_new ("input-panel-state", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_INPUT_PANEL_STATE); signals[CURSOR_LOCATION_CHANGED] = g_signal_new ("cursor-location-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, GRAPHENE_TYPE_RECT); pspecs[PROP_CONTENT_HINTS] = g_param_spec_flags ("content-hints", P_("Content hints"), P_("Content hints"), CLUTTER_TYPE_INPUT_CONTENT_HINT_FLAGS, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); pspecs[PROP_CONTENT_PURPOSE] = g_param_spec_enum ("content-purpose", P_("Content purpose"), P_("Content purpose"), CLUTTER_TYPE_INPUT_CONTENT_PURPOSE, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); pspecs[PROP_CAN_SHOW_PREEDIT] = g_param_spec_boolean ("can-show-preedit", P_("Can show preedit"), P_("Can show preedit"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, pspecs); } static void clutter_input_method_init (ClutterInputMethod *im) { } void clutter_input_method_focus_in (ClutterInputMethod *im, ClutterInputFocus *focus) { ClutterInputMethodPrivate *priv; ClutterInputMethodClass *klass; g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); g_return_if_fail (CLUTTER_IS_INPUT_FOCUS (focus)); priv = clutter_input_method_get_instance_private (im); if (priv->focus == focus) return; if (priv->focus) clutter_input_method_focus_out (im); g_set_object (&priv->focus, focus); if (focus) { klass = CLUTTER_INPUT_METHOD_GET_CLASS (im); klass->focus_in (im, focus); clutter_input_focus_focus_in (priv->focus, im); } } void clutter_input_method_focus_out (ClutterInputMethod *im) { ClutterInputMethodPrivate *priv; ClutterInputMethodClass *klass; g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); priv = clutter_input_method_get_instance_private (im); if (!priv->focus) return; clutter_input_focus_focus_out (priv->focus); g_clear_object (&priv->focus); klass = CLUTTER_INPUT_METHOD_GET_CLASS (im); klass->focus_out (im); } ClutterInputFocus * clutter_input_method_get_focus (ClutterInputMethod *im) { ClutterInputMethodPrivate *priv; priv = clutter_input_method_get_instance_private (im); return priv->focus; } static void clutter_input_method_put_im_event (ClutterInputMethod *im, ClutterEventType event_type, const gchar *text, int32_t offset, uint32_t len) { ClutterInputDevice *keyboard; ClutterSeat *seat; ClutterStage *stage; ClutterEvent *event; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); keyboard = clutter_seat_get_keyboard (seat); stage = _clutter_input_device_get_stage (keyboard); if (stage == NULL) return; event = clutter_event_new (event_type); event->im.text = g_strdup (text); event->im.offset = offset; event->im.len = len; clutter_event_set_device (event, keyboard); clutter_event_set_source_device (event, keyboard); clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_INPUT_METHOD); clutter_event_set_stage (event, stage); clutter_event_put (event); clutter_event_free (event); } void clutter_input_method_commit (ClutterInputMethod *im, const gchar *text) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); clutter_input_method_put_im_event (im, CLUTTER_IM_COMMIT, text, 0, 0); } void clutter_input_method_delete_surrounding (ClutterInputMethod *im, int offset, guint len) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); clutter_input_method_put_im_event (im, CLUTTER_IM_DELETE, NULL, offset, len); } void clutter_input_method_request_surrounding (ClutterInputMethod *im) { ClutterInputMethodPrivate *priv; g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); priv = clutter_input_method_get_instance_private (im); if (priv->focus) clutter_input_focus_request_surrounding (priv->focus); } /** * clutter_input_method_set_preedit_text: * @im: a #ClutterInputMethod * @preedit: (nullable): the preedit text, or %NULL * @cursor: the cursor * * Sets the preedit text on the current input focus. **/ void clutter_input_method_set_preedit_text (ClutterInputMethod *im, const gchar *preedit, guint cursor) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); clutter_input_method_put_im_event (im, CLUTTER_IM_PREEDIT, preedit, cursor, 0); } void clutter_input_method_notify_key_event (ClutterInputMethod *im, const ClutterEvent *event, gboolean filtered) { if (!filtered) { ClutterEvent *copy; /* XXX: we rely on the IM implementation to notify back of * key events in the exact same order they were given. */ copy = clutter_event_copy (event); clutter_event_set_flags (copy, clutter_event_get_flags (event) | CLUTTER_EVENT_FLAG_INPUT_METHOD); clutter_event_set_source_device (copy, clutter_event_get_device (copy)); clutter_event_put (copy); clutter_event_free (copy); } } void clutter_input_method_set_input_panel_state (ClutterInputMethod *im, ClutterInputPanelState state) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); g_signal_emit (im, signals[INPUT_PANEL_STATE], 0, state); } void clutter_input_method_reset (ClutterInputMethod *im) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); CLUTTER_INPUT_METHOD_GET_CLASS (im)->reset (im); } void clutter_input_method_set_cursor_location (ClutterInputMethod *im, const graphene_rect_t *rect) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); CLUTTER_INPUT_METHOD_GET_CLASS (im)->set_cursor_location (im, rect); g_signal_emit (im, signals[CURSOR_LOCATION_CHANGED], 0, rect); } void clutter_input_method_set_surrounding (ClutterInputMethod *im, const gchar *text, guint cursor, guint anchor) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); CLUTTER_INPUT_METHOD_GET_CLASS (im)->set_surrounding (im, text, cursor, anchor); } void clutter_input_method_set_content_hints (ClutterInputMethod *im, ClutterInputContentHintFlags hints) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); g_object_set (G_OBJECT (im), "content-hints", hints, NULL); } void clutter_input_method_set_content_purpose (ClutterInputMethod *im, ClutterInputContentPurpose purpose) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); g_object_set (G_OBJECT (im), "content-purpose", purpose, NULL); } void clutter_input_method_set_can_show_preedit (ClutterInputMethod *im, gboolean can_show_preedit) { g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); g_object_set (G_OBJECT (im), "can-show-preedit", can_show_preedit, NULL); } gboolean clutter_input_method_filter_key_event (ClutterInputMethod *im, const ClutterKeyEvent *key) { ClutterInputMethodClass *im_class = CLUTTER_INPUT_METHOD_GET_CLASS (im); g_return_val_if_fail (CLUTTER_IS_INPUT_METHOD (im), FALSE); g_return_val_if_fail (key != NULL, FALSE); if (clutter_event_get_flags ((ClutterEvent *) key) & CLUTTER_EVENT_FLAG_INPUT_METHOD) return FALSE; if (!im_class->filter_key_event) return FALSE; return im_class->filter_key_event (im, (const ClutterEvent *) key); } void clutter_input_method_forward_key (ClutterInputMethod *im, uint32_t keyval, uint32_t keycode, uint32_t state, uint64_t time_, gboolean press) { ClutterInputMethodPrivate *priv; ClutterInputDevice *keyboard; ClutterSeat *seat; ClutterStage *stage; ClutterEvent *event; g_return_if_fail (CLUTTER_IS_INPUT_METHOD (im)); priv = clutter_input_method_get_instance_private (im); if (!priv->focus) return; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); keyboard = clutter_seat_get_keyboard (seat); stage = _clutter_input_device_get_stage (keyboard); if (stage == NULL) return; event = clutter_event_new (press ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE); event->key.time = time_; event->key.flags = CLUTTER_EVENT_FLAG_INPUT_METHOD; event->key.modifier_state = state; event->key.keyval = keyval; event->key.hardware_keycode = keycode; event->key.unicode_value = clutter_keysym_to_unicode (keyval); clutter_event_set_device (event, keyboard); clutter_event_set_source_device (event, keyboard); clutter_event_set_stage (event, stage); clutter_event_put (event); clutter_event_free (event); } muffin-6.4.1/clutter/clutter/clutter-flow-layout.h0000664000175000017500000001303614723361714021224 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_FLOW_LAYOUT_H__ #define __CLUTTER_FLOW_LAYOUT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_FLOW_LAYOUT (clutter_flow_layout_get_type ()) #define CLUTTER_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayout)) #define CLUTTER_IS_FLOW_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FLOW_LAYOUT)) #define CLUTTER_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass)) #define CLUTTER_IS_FLOW_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FLOW_LAYOUT)) #define CLUTTER_FLOW_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FLOW_LAYOUT, ClutterFlowLayoutClass)) typedef struct _ClutterFlowLayout ClutterFlowLayout; typedef struct _ClutterFlowLayoutPrivate ClutterFlowLayoutPrivate; typedef struct _ClutterFlowLayoutClass ClutterFlowLayoutClass; /** * ClutterFlowLayout: * * The #ClutterFlowLayout structure contains only private data * and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterFlowLayout { /*< private >*/ ClutterLayoutManager parent_instance; ClutterFlowLayoutPrivate *priv; }; /** * ClutterFlowLayoutClass: * * The #ClutterFlowLayoutClass structure contains only private data * and should be accessed using the provided API * * Since: 1.2 */ struct _ClutterFlowLayoutClass { /*< private >*/ ClutterLayoutManagerClass parent_class; }; CLUTTER_EXPORT GType clutter_flow_layout_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager * clutter_flow_layout_new (ClutterFlowOrientation orientation); CLUTTER_EXPORT void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, ClutterFlowOrientation orientation); CLUTTER_EXPORT ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout); CLUTTER_EXPORT void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, gboolean homogeneous); CLUTTER_EXPORT gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout); CLUTTER_EXPORT void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, gfloat spacing); CLUTTER_EXPORT gfloat clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout); CLUTTER_EXPORT void clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, gfloat spacing); CLUTTER_EXPORT gfloat clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout); CLUTTER_EXPORT void clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, gfloat min_width, gfloat max_width); CLUTTER_EXPORT void clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, gfloat *min_width, gfloat *max_width); CLUTTER_EXPORT void clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, gfloat min_height, gfloat max_height); CLUTTER_EXPORT void clutter_flow_layout_get_row_height (ClutterFlowLayout *layout, gfloat *min_height, gfloat *max_height); CLUTTER_EXPORT void clutter_flow_layout_set_snap_to_grid (ClutterFlowLayout *layout, gboolean snap_to_grid); CLUTTER_EXPORT gboolean clutter_flow_layout_get_snap_to_grid (ClutterFlowLayout *layout); G_END_DECLS #endif /* __CLUTTER_FLOW_LAYOUT_H__ */ muffin-6.4.1/clutter/clutter/clutter-colorize-effect.h0000664000175000017500000000443314723361714022023 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_COLORIZE_EFFECT_H__ #define __CLUTTER_COLORIZE_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_COLORIZE_EFFECT (clutter_colorize_effect_get_type ()) #define CLUTTER_COLORIZE_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_COLORIZE_EFFECT, ClutterColorizeEffect)) #define CLUTTER_IS_COLORIZE_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_COLORIZE_EFFECT)) /** * ClutterColorizeEffect: * * #ClutterColorizeEffect is an opaque structure * whose members cannot be directly accessed * * Since: 1.4 */ typedef struct _ClutterColorizeEffect ClutterColorizeEffect; typedef struct _ClutterColorizeEffectClass ClutterColorizeEffectClass; CLUTTER_EXPORT GType clutter_colorize_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect *clutter_colorize_effect_new (const ClutterColor *tint); CLUTTER_EXPORT void clutter_colorize_effect_set_tint (ClutterColorizeEffect *effect, const ClutterColor *tint); CLUTTER_EXPORT void clutter_colorize_effect_get_tint (ClutterColorizeEffect *effect, ClutterColor *tint); G_END_DECLS #endif /* __CLUTTER_COLORIZE_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-paint-nodes.c0000664000175000017500000011640114723361714021156 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-paint-nodes * @Title: Paint Nodes * @Short_Description: ClutterPaintNode implementations * * Clutter provides a set of predefined #ClutterPaintNode implementations * that cover all the state changes available. */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-paint-node-private.h" #include #include #include "clutter-actor-private.h" #include "clutter-color.h" #include "clutter-debug.h" #include "clutter-private.h" #include "clutter-paint-context-private.h" #include "clutter-paint-nodes.h" static CoglPipeline *default_color_pipeline = NULL; static CoglPipeline *default_texture_pipeline = NULL; /*< private > * _clutter_paint_node_init_types: * * Initializes the required types for ClutterPaintNode subclasses */ void _clutter_paint_node_init_types (void) { CoglContext *ctx; CoglColor cogl_color; GType node_type G_GNUC_UNUSED; if (G_LIKELY (default_color_pipeline != NULL)) return; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); node_type = clutter_paint_node_get_type (); cogl_color_init_from_4f (&cogl_color, 1.0, 1.0, 1.0, 1.0); default_color_pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color (default_color_pipeline, &cogl_color); default_texture_pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_null_texture (default_texture_pipeline, 0); cogl_pipeline_set_color (default_texture_pipeline, &cogl_color); cogl_pipeline_set_layer_wrap_mode (default_texture_pipeline, 0, COGL_PIPELINE_WRAP_MODE_AUTOMATIC); } /* * Root node * * any frame can only have a since RootNode instance for each * top-level actor. */ #define clutter_root_node_get_type clutter_root_node_get_type struct _ClutterRootNode { ClutterPaintNode parent_instance; CoglFramebuffer *framebuffer; CoglBufferBit clear_flags; CoglColor clear_color; }; G_DEFINE_TYPE (ClutterRootNode, clutter_root_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_root_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterRootNode *rnode = (ClutterRootNode *) node; clutter_paint_context_push_framebuffer (paint_context, rnode->framebuffer); cogl_framebuffer_clear (rnode->framebuffer, rnode->clear_flags, &rnode->clear_color); return TRUE; } static void clutter_root_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { clutter_paint_context_pop_framebuffer (paint_context); } static void clutter_root_node_finalize (ClutterPaintNode *node) { ClutterRootNode *rnode = (ClutterRootNode *) node; cogl_object_unref (rnode->framebuffer); CLUTTER_PAINT_NODE_CLASS (clutter_root_node_parent_class)->finalize (node); } static CoglFramebuffer * clutter_root_node_get_framebuffer (ClutterPaintNode *node) { ClutterRootNode *rnode = (ClutterRootNode *) node; return rnode->framebuffer; } static void clutter_root_node_class_init (ClutterRootNodeClass *klass) { ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_root_node_pre_draw; node_class->post_draw = clutter_root_node_post_draw; node_class->finalize = clutter_root_node_finalize; node_class->get_framebuffer = clutter_root_node_get_framebuffer; } static void clutter_root_node_init (ClutterRootNode *self) { } ClutterPaintNode * clutter_root_node_new (CoglFramebuffer *framebuffer, const ClutterColor *clear_color, CoglBufferBit clear_flags) { ClutterRootNode *res; g_return_val_if_fail (framebuffer, NULL); res = _clutter_paint_node_create (CLUTTER_TYPE_ROOT_NODE); cogl_color_init_from_4ub (&res->clear_color, clear_color->red, clear_color->green, clear_color->blue, clear_color->alpha); cogl_color_premultiply (&res->clear_color); res->framebuffer = cogl_object_ref (framebuffer); res->clear_flags = clear_flags; return (ClutterPaintNode *) res; } /* * ClutterTransformNode */ struct _ClutterTransformNode { ClutterPaintNode parent_instance; CoglMatrix transform; }; struct _ClutterTransformNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterTransformNode, clutter_transform_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_transform_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterTransformNode *transform_node = (ClutterTransformNode *) node; CoglFramebuffer *fb = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_push_matrix (fb); cogl_framebuffer_transform (fb, &transform_node->transform); return TRUE; } static void clutter_transform_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { CoglFramebuffer *fb = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_pop_matrix (fb); } static void clutter_transform_node_class_init (ClutterTransformNodeClass *klass) { ClutterPaintNodeClass *node_class; node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_transform_node_pre_draw; node_class->post_draw = clutter_transform_node_post_draw; } static void clutter_transform_node_init (ClutterTransformNode *self) { cogl_matrix_init_identity (&self->transform); } /* * clutter_transform_node_new: * @transform: (nullable): the transform matrix to apply * * Return value: (transfer full): the newly created #ClutterTransformNode. * Use clutter_paint_node_unref() when done. */ ClutterPaintNode * clutter_transform_node_new (const CoglMatrix *transform) { ClutterTransformNode *res; res = _clutter_paint_node_create (CLUTTER_TYPE_TRANSFORM_NODE); if (transform) res->transform = *transform; return (ClutterPaintNode *) res; } /* * Dummy node, private * * an empty node, used temporarily until we can drop API compatibility, * and we'll be able to build a full render tree for each frame. */ #define clutter_dummy_node_get_type _clutter_dummy_node_get_type typedef struct _ClutterDummyNode ClutterDummyNode; typedef struct _ClutterPaintNodeClass ClutterDummyNodeClass; struct _ClutterDummyNode { ClutterPaintNode parent_instance; ClutterActor *actor; CoglFramebuffer *framebuffer; }; G_DEFINE_TYPE (ClutterDummyNode, clutter_dummy_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_dummy_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { return TRUE; } static JsonNode * clutter_dummy_node_serialize (ClutterPaintNode *node) { ClutterDummyNode *dnode = (ClutterDummyNode *) node; JsonBuilder *builder; JsonNode *res; if (dnode->actor == NULL) return json_node_new (JSON_NODE_NULL); builder = json_builder_new (); json_builder_begin_object (builder); json_builder_set_member_name (builder, "actor"); json_builder_add_string_value (builder, _clutter_actor_get_debug_name (dnode->actor)); json_builder_end_object (builder); res = json_builder_get_root (builder); g_object_unref (builder); return res; } static CoglFramebuffer * clutter_dummy_node_get_framebuffer (ClutterPaintNode *node) { ClutterDummyNode *dnode = (ClutterDummyNode *) node; return dnode->framebuffer; } static void clutter_dummy_node_finalize (ClutterPaintNode *node) { ClutterDummyNode *dnode = (ClutterDummyNode *) node; cogl_clear_object (&dnode->framebuffer); CLUTTER_PAINT_NODE_CLASS (clutter_dummy_node_parent_class)->finalize (node); } static void clutter_dummy_node_class_init (ClutterDummyNodeClass *klass) { ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_dummy_node_pre_draw; node_class->serialize = clutter_dummy_node_serialize; node_class->get_framebuffer = clutter_dummy_node_get_framebuffer; node_class->finalize = clutter_dummy_node_finalize; } static void clutter_dummy_node_init (ClutterDummyNode *self) { } ClutterPaintNode * _clutter_dummy_node_new (ClutterActor *actor, CoglFramebuffer *framebuffer) { ClutterPaintNode *res; ClutterDummyNode *dnode; res = _clutter_paint_node_create (_clutter_dummy_node_get_type ()); dnode = (ClutterDummyNode *) res; dnode->actor = actor; dnode->framebuffer = cogl_object_ref (framebuffer); return res; } /* * Pipeline node */ struct _ClutterPipelineNode { ClutterPaintNode parent_instance; CoglPipeline *pipeline; }; /** * ClutterPipelineNodeClass: * * The `ClutterPipelineNodeClass` structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ struct _ClutterPipelineNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterPipelineNode, clutter_pipeline_node, CLUTTER_TYPE_PAINT_NODE) static void clutter_pipeline_node_finalize (ClutterPaintNode *node) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node); if (pnode->pipeline != NULL) cogl_object_unref (pnode->pipeline); CLUTTER_PAINT_NODE_CLASS (clutter_pipeline_node_parent_class)->finalize (node); } static gboolean clutter_pipeline_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node); if (node->operations != NULL && pnode->pipeline != NULL) return TRUE; return FALSE; } static CoglFramebuffer * get_target_framebuffer (ClutterPaintNode *node, ClutterPaintContext *paint_context) { CoglFramebuffer *framebuffer; framebuffer = clutter_paint_node_get_framebuffer (node); if (framebuffer) return framebuffer; return clutter_paint_context_get_framebuffer (paint_context); } static void clutter_pipeline_node_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node); CoglFramebuffer *fb; guint i; if (pnode->pipeline == NULL) return; if (node->operations == NULL) return; fb = clutter_paint_context_get_framebuffer (paint_context); for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); switch (op->opcode) { case PAINT_OP_INVALID: break; case PAINT_OP_TEX_RECT: cogl_framebuffer_draw_textured_rectangle (fb, pnode->pipeline, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3], op->op.texrect[4], op->op.texrect[5], op->op.texrect[6], op->op.texrect[7]); break; case PAINT_OP_MULTITEX_RECT: cogl_framebuffer_draw_multitextured_rectangle (fb, pnode->pipeline, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3], (float*) op->multitex_coords->data, op->multitex_coords->len); break; case PAINT_OP_PATH: cogl_framebuffer_fill_path (fb, pnode->pipeline, op->op.path); break; case PAINT_OP_PRIMITIVE: cogl_framebuffer_draw_primitive (fb, pnode->pipeline, op->op.primitive); break; } } } static void clutter_pipeline_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { } static JsonNode * clutter_pipeline_node_serialize (ClutterPaintNode *node) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (node); JsonBuilder *builder; CoglColor color; JsonNode *res; if (pnode->pipeline == NULL) return json_node_new (JSON_NODE_NULL); builder = json_builder_new (); json_builder_begin_object (builder); cogl_pipeline_get_color (pnode->pipeline, &color); json_builder_set_member_name (builder, "color"); json_builder_begin_array (builder); json_builder_add_double_value (builder, cogl_color_get_red (&color)); json_builder_add_double_value (builder, cogl_color_get_green (&color)); json_builder_add_double_value (builder, cogl_color_get_blue (&color)); json_builder_add_double_value (builder, cogl_color_get_alpha (&color)); json_builder_end_array (builder); #if 0 json_builder_set_member_name (builder, "layers"); json_builder_begin_array (builder); cogl_pipeline_foreach_layer (pnode->pipeline, clutter_pipeline_node_serialize_layer, builder); json_builder_end_array (builder); #endif json_builder_end_object (builder); res = json_builder_get_root (builder); g_object_unref (builder); return res; } static void clutter_pipeline_node_class_init (ClutterPipelineNodeClass *klass) { ClutterPaintNodeClass *node_class; node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_pipeline_node_pre_draw; node_class->draw = clutter_pipeline_node_draw; node_class->post_draw = clutter_pipeline_node_post_draw; node_class->finalize = clutter_pipeline_node_finalize; node_class->serialize = clutter_pipeline_node_serialize; } static void clutter_pipeline_node_init (ClutterPipelineNode *self) { } /** * clutter_pipeline_node_new: (skip) * @pipeline: (allow-none): a Cogl pipeline state object, or %NULL * * Creates a new #ClutterPaintNode that will use the @pipeline to * paint its contents. * * This function will acquire a reference on the passed @pipeline, * so it is safe to call cogl_object_unref() when it returns. * * Return value: (transfer full): the newly created #ClutterPaintNode. * Use clutter_paint_node_unref() when done. * * Since: 1.10 */ ClutterPaintNode * clutter_pipeline_node_new (CoglPipeline *pipeline) { ClutterPipelineNode *res; g_return_val_if_fail (pipeline == NULL || cogl_is_pipeline (pipeline), NULL); res = _clutter_paint_node_create (CLUTTER_TYPE_PIPELINE_NODE); if (pipeline != NULL) res->pipeline = cogl_object_ref (pipeline); return (ClutterPaintNode *) res; } /* * Color node */ struct _ClutterColorNode { ClutterPipelineNode parent_instance; }; /** * ClutterColorNodeClass: * * The `ClutterColorNodeClass` structure is an * opaque type whose members cannot be directly accessed. * * Since: 1.10 */ struct _ClutterColorNodeClass { ClutterPipelineNodeClass parent_class; }; G_DEFINE_TYPE (ClutterColorNode, clutter_color_node, CLUTTER_TYPE_PIPELINE_NODE) static void clutter_color_node_class_init (ClutterColorNodeClass *klass) { } static void clutter_color_node_init (ClutterColorNode *cnode) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (cnode); g_assert (default_color_pipeline != NULL); pnode->pipeline = cogl_pipeline_copy (default_color_pipeline); } /** * clutter_color_node_new: * @color: (allow-none): the color to paint, or %NULL * * Creates a new #ClutterPaintNode that will paint a solid color * fill using @color. * * Return value: (transfer full): the newly created #ClutterPaintNode. Use * clutter_paint_node_unref() when done * * Since: 1.10 */ ClutterPaintNode * clutter_color_node_new (const ClutterColor *color) { ClutterPipelineNode *cnode; cnode = _clutter_paint_node_create (CLUTTER_TYPE_COLOR_NODE); if (color != NULL) { CoglColor cogl_color; cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); cogl_color_premultiply (&cogl_color); cogl_pipeline_set_color (cnode->pipeline, &cogl_color); } return (ClutterPaintNode *) cnode; } /* * Texture node */ struct _ClutterTextureNode { ClutterPipelineNode parent_instance; }; /** * ClutterTextureNodeClass: * * The `ClutterTextureNodeClass` structure is an * opaque type whose members cannot be directly accessed. * * Since: 1.10 */ struct _ClutterTextureNodeClass { ClutterPipelineNodeClass parent_class; }; G_DEFINE_TYPE (ClutterTextureNode, clutter_texture_node, CLUTTER_TYPE_PIPELINE_NODE) static void clutter_texture_node_class_init (ClutterTextureNodeClass *klass) { } static void clutter_texture_node_init (ClutterTextureNode *self) { ClutterPipelineNode *pnode = CLUTTER_PIPELINE_NODE (self); g_assert (default_texture_pipeline != NULL); pnode->pipeline = cogl_pipeline_copy (default_texture_pipeline); } static CoglPipelineFilter clutter_scaling_filter_to_cogl_pipeline_filter (ClutterScalingFilter filter) { switch (filter) { case CLUTTER_SCALING_FILTER_NEAREST: return COGL_PIPELINE_FILTER_NEAREST; case CLUTTER_SCALING_FILTER_LINEAR: return COGL_PIPELINE_FILTER_LINEAR; case CLUTTER_SCALING_FILTER_TRILINEAR: return COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR; } return COGL_PIPELINE_FILTER_LINEAR; } /** * clutter_texture_node_new: * @texture: a #CoglTexture * @color: (allow-none): a #ClutterColor used for blending, or %NULL * @min_filter: the minification filter for the texture * @mag_filter: the magnification filter for the texture * * Creates a new #ClutterPaintNode that will paint the passed @texture. * * This function will take a reference on @texture, so it is safe to * call cogl_object_unref() on @texture when it returns. * * The @color must not be pre-multiplied with its #ClutterColor.alpha * channel value; if @color is %NULL, a fully opaque white color will * be used for blending. * * Return value: (transfer full): the newly created #ClutterPaintNode. * Use clutter_paint_node_unref() when done * * Since: 1.10 */ ClutterPaintNode * clutter_texture_node_new (CoglTexture *texture, const ClutterColor *color, ClutterScalingFilter min_filter, ClutterScalingFilter mag_filter) { ClutterPipelineNode *tnode; CoglColor cogl_color; CoglPipelineFilter min_f, mag_f; g_return_val_if_fail (cogl_is_texture (texture), NULL); tnode = _clutter_paint_node_create (CLUTTER_TYPE_TEXTURE_NODE); cogl_pipeline_set_layer_texture (tnode->pipeline, 0, texture); min_f = clutter_scaling_filter_to_cogl_pipeline_filter (min_filter); mag_f = clutter_scaling_filter_to_cogl_pipeline_filter (mag_filter); cogl_pipeline_set_layer_filters (tnode->pipeline, 0, min_f, mag_f); if (color != NULL) { cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, color->alpha); cogl_color_premultiply (&cogl_color); } else cogl_color_init_from_4ub (&cogl_color, 255, 255, 255, 255); cogl_pipeline_set_color (tnode->pipeline, &cogl_color); return (ClutterPaintNode *) tnode; } /* * Text node */ struct _ClutterTextNode { ClutterPaintNode parent_instance; PangoLayout *layout; CoglColor color; }; /** * ClutterTextNodeClass: * * The `ClutterTextNodeClass` structure is an opaque * type whose contents cannot be directly accessed. * * Since: 1.10 */ struct _ClutterTextNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterTextNode, clutter_text_node, CLUTTER_TYPE_PAINT_NODE) static void clutter_text_node_finalize (ClutterPaintNode *node) { ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node); if (tnode->layout != NULL) g_object_unref (tnode->layout); CLUTTER_PAINT_NODE_CLASS (clutter_text_node_parent_class)->finalize (node); } static gboolean clutter_text_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node); return tnode->layout != NULL; } static void clutter_text_node_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node); PangoRectangle extents; CoglFramebuffer *fb; guint i; if (node->operations == NULL) return; fb = get_target_framebuffer (node, paint_context); pango_layout_get_pixel_extents (tnode->layout, NULL, &extents); for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; float op_width, op_height; gboolean clipped = FALSE; op = &g_array_index (node->operations, ClutterPaintOperation, i); switch (op->opcode) { case PAINT_OP_TEX_RECT: op_width = op->op.texrect[2] - op->op.texrect[0]; op_height = op->op.texrect[3] - op->op.texrect[1]; /* if the primitive size was smaller than the layout, * we clip the layout when drawin, to avoid spilling * it out */ if (extents.width > op_width || extents.height > op_height) { cogl_framebuffer_push_rectangle_clip (fb, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3]); clipped = TRUE; } cogl_pango_show_layout (fb, tnode->layout, op->op.texrect[0], op->op.texrect[1], &tnode->color); if (clipped) cogl_framebuffer_pop_clip (fb); break; case PAINT_OP_MULTITEX_RECT: case PAINT_OP_PATH: case PAINT_OP_PRIMITIVE: case PAINT_OP_INVALID: break; } } } static JsonNode * clutter_text_node_serialize (ClutterPaintNode *node) { ClutterTextNode *tnode = CLUTTER_TEXT_NODE (node); JsonBuilder *builder; JsonNode *res; builder = json_builder_new (); json_builder_begin_object (builder); json_builder_set_member_name (builder, "layout"); if (pango_layout_get_character_count (tnode->layout) > 12) { const char *text = pango_layout_get_text (tnode->layout); char *str; str = g_strndup (text, 12); json_builder_add_string_value (builder, str); g_free (str); } else json_builder_add_string_value (builder, pango_layout_get_text (tnode->layout)); json_builder_set_member_name (builder, "color"); json_builder_begin_array (builder); json_builder_add_double_value (builder, cogl_color_get_red (&tnode->color)); json_builder_add_double_value (builder, cogl_color_get_green (&tnode->color)); json_builder_add_double_value (builder, cogl_color_get_blue (&tnode->color)); json_builder_add_double_value (builder, cogl_color_get_alpha (&tnode->color)); json_builder_end_array (builder); json_builder_end_object (builder); res = json_builder_get_root (builder); g_object_unref (builder); return res; } static void clutter_text_node_class_init (ClutterTextNodeClass *klass) { ClutterPaintNodeClass *node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_text_node_pre_draw; node_class->draw = clutter_text_node_draw; node_class->finalize = clutter_text_node_finalize; node_class->serialize = clutter_text_node_serialize; } static void clutter_text_node_init (ClutterTextNode *self) { cogl_color_init_from_4f (&self->color, 0.0, 0.0, 0.0, 1.0); } /** * clutter_text_node_new: * @layout: (allow-none): a #PangoLayout, or %NULL * @color: (allow-none): the color used to paint the layout, * or %NULL * * Creates a new #ClutterPaintNode that will paint a #PangoLayout * with the given color. * * This function takes a reference on the passed @layout, so it * is safe to call g_object_unref() after it returns. * * Return value: (transfer full): the newly created #ClutterPaintNode. * Use clutter_paint_node_unref() when done * * Since: 1.10 */ ClutterPaintNode * clutter_text_node_new (PangoLayout *layout, const ClutterColor *color) { ClutterTextNode *res; g_return_val_if_fail (layout == NULL || PANGO_IS_LAYOUT (layout), NULL); res = _clutter_paint_node_create (CLUTTER_TYPE_TEXT_NODE); if (layout != NULL) res->layout = g_object_ref (layout); if (color != NULL) { cogl_color_init_from_4ub (&res->color, color->red, color->green, color->blue, color->alpha); } return (ClutterPaintNode *) res; } /* * Clip node */ struct _ClutterClipNode { ClutterPaintNode parent_instance; }; /** * ClutterClipNodeClass: * * The `ClutterClipNodeClass` structure is an opaque * type whose members cannot be directly accessed. * * Since: 1.10 */ struct _ClutterClipNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterClipNode, clutter_clip_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_clip_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { gboolean retval = FALSE; CoglFramebuffer *fb; guint i; if (node->operations == NULL) return FALSE; fb = get_target_framebuffer (node, paint_context); for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); switch (op->opcode) { case PAINT_OP_TEX_RECT: cogl_framebuffer_push_rectangle_clip (fb, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3]); retval = TRUE; break; case PAINT_OP_PATH: cogl_framebuffer_push_path_clip (fb, op->op.path); retval = TRUE; break; case PAINT_OP_MULTITEX_RECT: case PAINT_OP_PRIMITIVE: case PAINT_OP_INVALID: break; } } return retval; } static void clutter_clip_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { CoglFramebuffer *fb; guint i; if (node->operations == NULL) return; fb = get_target_framebuffer (node, paint_context); for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); switch (op->opcode) { case PAINT_OP_PATH: case PAINT_OP_TEX_RECT: cogl_framebuffer_pop_clip (fb); break; case PAINT_OP_MULTITEX_RECT: case PAINT_OP_PRIMITIVE: case PAINT_OP_INVALID: break; } } } static void clutter_clip_node_class_init (ClutterClipNodeClass *klass) { ClutterPaintNodeClass *node_class; node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_clip_node_pre_draw; node_class->post_draw = clutter_clip_node_post_draw; } static void clutter_clip_node_init (ClutterClipNode *self) { } /** * clutter_clip_node_new: * * Creates a new #ClutterPaintNode that will clip its child * nodes to the 2D regions added to it. * * Return value: (transfer full): the newly created #ClutterPaintNode. * Use clutter_paint_node_unref() when done. * * Since: 1.10 */ ClutterPaintNode * clutter_clip_node_new (void) { return _clutter_paint_node_create (CLUTTER_TYPE_CLIP_NODE); } /* * ClutterActorNode */ struct _ClutterActorNode { ClutterPaintNode parent_instance; ClutterActor *actor; }; struct _ClutterActorNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterActorNode, clutter_actor_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_actor_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node); CLUTTER_SET_PRIVATE_FLAGS (actor_node->actor, CLUTTER_IN_PAINT); return TRUE; } static void clutter_actor_node_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node); clutter_actor_continue_paint (actor_node->actor, paint_context); } static void clutter_actor_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node); CLUTTER_UNSET_PRIVATE_FLAGS (actor_node->actor, CLUTTER_IN_PAINT); } static JsonNode * clutter_actor_node_serialize (ClutterPaintNode *node) { ClutterActorNode *actor_node = CLUTTER_ACTOR_NODE (node); g_autoptr (JsonBuilder) builder = NULL; const char *debug_name; debug_name = _clutter_actor_get_debug_name (actor_node->actor); builder = json_builder_new (); json_builder_begin_object (builder); json_builder_set_member_name (builder, "actor"); json_builder_add_string_value (builder, debug_name); json_builder_end_object (builder); return json_builder_get_root (builder); } static void clutter_actor_node_class_init (ClutterActorNodeClass *klass) { ClutterPaintNodeClass *node_class; node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_actor_node_pre_draw; node_class->draw = clutter_actor_node_draw; node_class->post_draw = clutter_actor_node_post_draw; node_class->serialize = clutter_actor_node_serialize; } static void clutter_actor_node_init (ClutterActorNode *self) { } /* * clutter_actor_node_new: * @actor: the actor to paint * * Creates a new #ClutterActorNode. * * The actor is painted together with any effects * applied to it. Children of this node will draw * over the actor contents. * * Return value: (transfer full): the newly created #ClutterActorNode. * Use clutter_paint_node_unref() when done. */ ClutterPaintNode * clutter_actor_node_new (ClutterActor *actor) { ClutterActorNode *res; g_assert (actor != NULL); res = _clutter_paint_node_create (CLUTTER_TYPE_ACTOR_NODE); res->actor = actor; return (ClutterPaintNode *) res; } /* * ClutterLayerNode */ struct _ClutterLayerNode { ClutterPaintNode parent_instance; cairo_rectangle_t viewport; CoglMatrix projection; float fbo_width; float fbo_height; CoglPipeline *state; CoglFramebuffer *offscreen; CoglTexture *texture; guint8 opacity; }; struct _ClutterLayerNodeClass { ClutterPaintNodeClass parent_class; }; G_DEFINE_TYPE (ClutterLayerNode, clutter_layer_node, CLUTTER_TYPE_PAINT_NODE) static gboolean clutter_layer_node_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterLayerNode *lnode = (ClutterLayerNode *) node; CoglFramebuffer *framebuffer; CoglMatrix matrix; /* if we were unable to create an offscreen buffer for this node, then * we simply ignore it */ if (lnode->offscreen == NULL) return FALSE; /* if no geometry was submitted for this node then we simply ignore it */ if (node->operations == NULL) return FALSE; /* copy the same modelview from the current framebuffer to the one we * are going to use */ framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_get_modelview_matrix (framebuffer, &matrix); clutter_paint_context_push_framebuffer (paint_context, lnode->offscreen); cogl_framebuffer_set_modelview_matrix (lnode->offscreen, &matrix); cogl_framebuffer_set_viewport (lnode->offscreen, lnode->viewport.x, lnode->viewport.y, lnode->viewport.width, lnode->viewport.height); cogl_framebuffer_set_projection_matrix (lnode->offscreen, &lnode->projection); /* clear out the target framebuffer */ cogl_framebuffer_clear4f (lnode->offscreen, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, 0.f, 0.f, 0.f, 0.f); cogl_framebuffer_push_matrix (lnode->offscreen); /* every draw operation after this point will happen an offscreen * framebuffer */ return TRUE; } static void clutter_layer_node_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node); CoglFramebuffer *fb; guint i; /* switch to the previous framebuffer */ cogl_framebuffer_pop_matrix (lnode->offscreen); clutter_paint_context_pop_framebuffer (paint_context); fb = clutter_paint_context_get_framebuffer (paint_context); for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); switch (op->opcode) { case PAINT_OP_INVALID: break; case PAINT_OP_TEX_RECT: /* now we need to paint the texture */ cogl_framebuffer_draw_textured_rectangle (fb, lnode->state, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3], op->op.texrect[4], op->op.texrect[5], op->op.texrect[6], op->op.texrect[7]); break; case PAINT_OP_MULTITEX_RECT: cogl_framebuffer_draw_multitextured_rectangle (fb, lnode->state, op->op.texrect[0], op->op.texrect[1], op->op.texrect[2], op->op.texrect[3], (float*) op->multitex_coords->data, op->multitex_coords->len); break; case PAINT_OP_PATH: cogl_framebuffer_fill_path (fb, lnode->state, op->op.path); break; case PAINT_OP_PRIMITIVE: cogl_framebuffer_draw_primitive (fb, lnode->state, op->op.primitive); break; } } } static void clutter_layer_node_finalize (ClutterPaintNode *node) { ClutterLayerNode *lnode = CLUTTER_LAYER_NODE (node); if (lnode->state != NULL) cogl_object_unref (lnode->state); if (lnode->offscreen != NULL) cogl_object_unref (lnode->offscreen); CLUTTER_PAINT_NODE_CLASS (clutter_layer_node_parent_class)->finalize (node); } static void clutter_layer_node_class_init (ClutterLayerNodeClass *klass) { ClutterPaintNodeClass *node_class; node_class = CLUTTER_PAINT_NODE_CLASS (klass); node_class->pre_draw = clutter_layer_node_pre_draw; node_class->post_draw = clutter_layer_node_post_draw; node_class->finalize = clutter_layer_node_finalize; } static void clutter_layer_node_init (ClutterLayerNode *self) { cogl_matrix_init_identity (&self->projection); } /* * clutter_layer_node_new: * @projection: the projection matrix to use to set up the layer * @viewport: (type cairo.Rectangle): the viewport to use to set up the layer * @width: the width of the layer * @height: the height of the layer * @opacity: the opacity to be used when drawing the layer * * Creates a new #ClutterLayerNode. * * All children of this node will be painted inside a separate * framebuffer; the framebuffer will then be painted using the * given @opacity. * * Return value: (transfer full): the newly created #ClutterLayerNode. * Use clutter_paint_node_unref() when done. * * Since: 1.10 */ ClutterPaintNode * clutter_layer_node_new (const CoglMatrix *projection, const cairo_rectangle_t *viewport, float width, float height, guint8 opacity) { ClutterLayerNode *res; CoglColor color; res = _clutter_paint_node_create (CLUTTER_TYPE_LAYER_NODE); res->projection = *projection; res->viewport = *viewport; res->fbo_width = width; res->fbo_height = height; res->opacity = opacity; /* the texture backing the FBO */ res->texture = cogl_texture_new_with_size (MAX (res->fbo_width, 1), MAX (res->fbo_height, 1), COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); res->offscreen = COGL_FRAMEBUFFER (cogl_offscreen_new_to_texture (res->texture)); if (res->offscreen == NULL) { g_critical ("%s: Unable to create an offscreen buffer", G_STRLOC); cogl_object_unref (res->texture); res->texture = NULL; goto out; } cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity); /* the pipeline used to paint the texture; we use nearest * interpolation filters because the texture is always * going to be painted at a 1:1 texel:pixel ratio */ res->state = cogl_pipeline_copy (default_texture_pipeline); cogl_pipeline_set_layer_filters (res->state, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_texture (res->state, 0, res->texture); cogl_pipeline_set_color (res->state, &color); cogl_object_unref (res->texture); out: return (ClutterPaintNode *) res; } muffin-6.4.1/clutter/clutter/clutter-group.h0000664000175000017500000000565214723361714020103 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_GROUP_H__ #define __CLUTTER_GROUP_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_GROUP (clutter_group_get_type ()) #define CLUTTER_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_GROUP, ClutterGroup)) #define CLUTTER_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_GROUP, ClutterGroupClass)) #define CLUTTER_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_GROUP)) #define CLUTTER_IS_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_GROUP)) #define CLUTTER_GROUP_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_GROUP, ClutterGroupClass)) /* XXX - ClutterGroup is to be considered fully deprecated; the only * reason we keep this header is because ClutterStage inherits from * ClutterGroup, and thus we need to have a structure definition for * the Stage object to expand. */ typedef struct _ClutterGroup ClutterGroup; typedef struct _ClutterGroupClass ClutterGroupClass; typedef struct _ClutterGroupPrivate ClutterGroupPrivate; /** * ClutterGroup: * * The #ClutterGroup structure contains only private data * and should be accessed using the provided API * * Since: 0.2 */ struct _ClutterGroup { /*< private >*/ ClutterActor parent_instance; ClutterGroupPrivate *priv; }; /** * ClutterGroupClass: * * The #ClutterGroupClass structure contains only private data * * Since: 0.2 */ struct _ClutterGroupClass { /*< private >*/ ClutterActorClass parent_class; /* padding for future expansion */ void (*_clutter_reserved1) (void); void (*_clutter_reserved2) (void); void (*_clutter_reserved3) (void); void (*_clutter_reserved4) (void); void (*_clutter_reserved5) (void); void (*_clutter_reserved6) (void); }; CLUTTER_EXPORT GType clutter_group_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __CLUTTER_GROUP_H__ */ muffin-6.4.1/clutter/clutter/clutter-snap-constraint.c0000664000175000017500000004333714723361714022067 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-snap-constraint * @Title: ClutterSnapConstraint * @Short_Description: A constraint snapping two actors together * * #ClutterSnapConstraint is a constraint the snaps the edges of two * actors together, expanding the actor's allocation if necessary. * * An offset can be applied to the constraint, to provide spacing. * * #ClutterSnapConstraint is available since Clutter 1.6 */ #include "clutter-build-config.h" #include #include "clutter-snap-constraint.h" #include "clutter-actor-private.h" #include "clutter-constraint.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-private.h" #define CLUTTER_SNAP_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SNAP_CONSTRAINT, ClutterSnapConstraintClass)) #define CLUTTER_IS_SNAP_CONSTRAINT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SNAP_CONSTRAINT)) #define CLUTTER_SNAP_CONSTRAINT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SNAP_CONSTRAINT, ClutterSnapConstraintClass)) struct _ClutterSnapConstraint { ClutterConstraint parent_instance; ClutterActor *actor; ClutterActor *source; ClutterSnapEdge from_edge; ClutterSnapEdge to_edge; gfloat offset; }; struct _ClutterSnapConstraintClass { ClutterConstraintClass parent_class; }; enum { PROP_0, PROP_SOURCE, PROP_FROM_EDGE, PROP_TO_EDGE, PROP_OFFSET, PROP_LAST }; G_DEFINE_TYPE (ClutterSnapConstraint, clutter_snap_constraint, CLUTTER_TYPE_CONSTRAINT); static GParamSpec *obj_props[PROP_LAST] = { NULL, }; static void source_queue_relayout (ClutterActor *source, ClutterSnapConstraint *constraint) { if (constraint->actor != NULL) _clutter_actor_queue_only_relayout (constraint->actor); } static void source_destroyed (ClutterActor *actor, ClutterSnapConstraint *constraint) { constraint->source = NULL; } static inline void warn_horizontal_edge (const gchar *edge, ClutterActor *actor, ClutterActor *source) { g_warning (G_STRLOC ": the %s edge of actor '%s' can only be snapped " "to either the right or the left edge of actor '%s'", edge, _clutter_actor_get_debug_name (actor), _clutter_actor_get_debug_name (source)); } static inline void warn_vertical_edge (const gchar *edge, ClutterActor *actor, ClutterActor *source) { g_warning (G_STRLOC ": the %s edge of actor '%s' can only " "be snapped to the top or bottom edge of actor '%s'", edge, _clutter_actor_get_debug_name (actor), _clutter_actor_get_debug_name (source)); } static void clutter_snap_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (constraint); gfloat source_width, source_height; gfloat source_x, source_y; gfloat actor_width, actor_height; if (self->source == NULL) return; clutter_actor_get_position (self->source, &source_x, &source_y); clutter_actor_get_size (self->source, &source_width, &source_height); clutter_actor_box_get_size (allocation, &actor_width, &actor_height); switch (self->to_edge) { case CLUTTER_SNAP_EDGE_LEFT: if (self->from_edge == CLUTTER_SNAP_EDGE_LEFT) allocation->x1 = source_x + self->offset; else if (self->from_edge == CLUTTER_SNAP_EDGE_RIGHT) allocation->x2 = source_x + self->offset; else warn_horizontal_edge ("left", self->actor, self->source); break; case CLUTTER_SNAP_EDGE_RIGHT: if (self->from_edge == CLUTTER_SNAP_EDGE_RIGHT) allocation->x2 = source_x + source_width + self->offset; else if (self->from_edge == CLUTTER_SNAP_EDGE_LEFT) allocation->x1 = source_x + source_width + self->offset; else warn_horizontal_edge ("right", self->actor, self->source); break; break; case CLUTTER_SNAP_EDGE_TOP: if (self->from_edge == CLUTTER_SNAP_EDGE_TOP) allocation->y1 = source_y + self->offset; else if (self->from_edge == CLUTTER_SNAP_EDGE_BOTTOM) allocation->y2 = source_y + self->offset; else warn_vertical_edge ("top", self->actor, self->source); break; case CLUTTER_SNAP_EDGE_BOTTOM: if (self->from_edge == CLUTTER_SNAP_EDGE_BOTTOM) allocation->y2 = source_y + source_height + self->offset; else if (self->from_edge == CLUTTER_SNAP_EDGE_TOP) allocation->y1 = source_y + source_height + self->offset; else warn_vertical_edge ("bottom", self->actor, self->source); break; default: g_assert_not_reached (); break; } if (allocation->x2 - allocation->x1 < 0) allocation->x2 = allocation->x1; if (allocation->y2 - allocation->y1 < 0) allocation->y2 = allocation->y1; } static void clutter_snap_constraint_set_actor (ClutterActorMeta *meta, ClutterActor *new_actor) { ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (meta); ClutterActorMetaClass *parent; /* store the pointer to the actor, for later use */ self->actor = new_actor; parent = CLUTTER_ACTOR_META_CLASS (clutter_snap_constraint_parent_class); parent->set_actor (meta, new_actor); } static void clutter_snap_constraint_dispose (GObject *gobject) { ClutterSnapConstraint *snap = CLUTTER_SNAP_CONSTRAINT (gobject); if (snap->source != NULL) { g_signal_handlers_disconnect_by_func (snap->source, G_CALLBACK (source_destroyed), snap); g_signal_handlers_disconnect_by_func (snap->source, G_CALLBACK (source_queue_relayout), snap); snap->source = NULL; } G_OBJECT_CLASS (clutter_snap_constraint_parent_class)->dispose (gobject); } static void clutter_snap_constraint_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: clutter_snap_constraint_set_source (self, g_value_get_object (value)); break; case PROP_FROM_EDGE: clutter_snap_constraint_set_edges (self, g_value_get_enum (value), self->to_edge); break; case PROP_TO_EDGE: clutter_snap_constraint_set_edges (self, self->from_edge, g_value_get_enum (value)); break; case PROP_OFFSET: clutter_snap_constraint_set_offset (self, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_snap_constraint_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterSnapConstraint *self = CLUTTER_SNAP_CONSTRAINT (gobject); switch (prop_id) { case PROP_SOURCE: g_value_set_object (value, self->source); break; case PROP_FROM_EDGE: g_value_set_enum (value, self->from_edge); break; case PROP_TO_EDGE: g_value_set_enum (value, self->to_edge); break; case PROP_OFFSET: g_value_set_float (value, self->offset); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_snap_constraint_class_init (ClutterSnapConstraintClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterConstraintClass *constraint_class = CLUTTER_CONSTRAINT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); meta_class->set_actor = clutter_snap_constraint_set_actor; constraint_class->update_allocation = clutter_snap_constraint_update_allocation; /** * ClutterSnapConstraint:source: * * The #ClutterActor used as the source for the constraint * * Since: 1.6 */ obj_props[PROP_SOURCE] = g_param_spec_object ("source", P_("Source"), P_("The source of the constraint"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterSnapConstraint:from-edge: * * The edge of the #ClutterActor that should be snapped * * Since: 1.6 */ obj_props[PROP_FROM_EDGE] = g_param_spec_enum ("from-edge", P_("From Edge"), P_("The edge of the actor that should be snapped"), CLUTTER_TYPE_SNAP_EDGE, CLUTTER_SNAP_EDGE_RIGHT, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterSnapConstraint:to-edge: * * The edge of the #ClutterSnapConstraint:source that should be snapped * * Since: 1.6 */ obj_props[PROP_TO_EDGE] = g_param_spec_enum ("to-edge", P_("To Edge"), P_("The edge of the source that should be snapped"), CLUTTER_TYPE_SNAP_EDGE, CLUTTER_SNAP_EDGE_RIGHT, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterSnapConstraint:offset: * * The offset, in pixels, between #ClutterSnapConstraint:from-edge * and #ClutterSnapConstraint:to-edge * * Since: 1.6 */ obj_props[PROP_OFFSET] = g_param_spec_float ("offset", P_("Offset"), P_("The offset in pixels to apply to the constraint"), -G_MAXFLOAT, G_MAXFLOAT, 0.0f, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); gobject_class->dispose = clutter_snap_constraint_dispose; gobject_class->set_property = clutter_snap_constraint_set_property; gobject_class->get_property = clutter_snap_constraint_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_snap_constraint_init (ClutterSnapConstraint *self) { self->actor = NULL; self->source = NULL; self->from_edge = CLUTTER_SNAP_EDGE_RIGHT; self->to_edge = CLUTTER_SNAP_EDGE_RIGHT; self->offset = 0.0f; } /** * clutter_snap_constraint_new: * @source: (allow-none): the #ClutterActor to use as the source of * the constraint, or %NULL * @from_edge: the edge of the actor to use in the constraint * @to_edge: the edge of @source to use in the constraint * @offset: the offset to apply to the constraint, in pixels * * Creates a new #ClutterSnapConstraint that will snap a #ClutterActor * to the @edge of @source, with the given @offset. * * Return value: the newly created #ClutterSnapConstraint * * Since: 1.6 */ ClutterConstraint * clutter_snap_constraint_new (ClutterActor *source, ClutterSnapEdge from_edge, ClutterSnapEdge to_edge, gfloat offset) { g_return_val_if_fail (source == NULL || CLUTTER_IS_ACTOR (source), NULL); return g_object_new (CLUTTER_TYPE_SNAP_CONSTRAINT, "source", source, "from-edge", from_edge, "to-edge", to_edge, "offset", offset, NULL); } /** * clutter_snap_constraint_set_source: * @constraint: a #ClutterSnapConstraint * @source: (allow-none): a #ClutterActor, or %NULL to unset the source * * Sets the source #ClutterActor for the constraint * * Since: 1.6 */ void clutter_snap_constraint_set_source (ClutterSnapConstraint *constraint, ClutterActor *source) { ClutterActor *old_source; g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); if (constraint->source == source) return; old_source = constraint->source; if (old_source != NULL) { g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_destroyed), constraint); g_signal_handlers_disconnect_by_func (old_source, G_CALLBACK (source_queue_relayout), constraint); } constraint->source = source; if (constraint->source != NULL) { g_signal_connect (constraint->source, "queue-relayout", G_CALLBACK (source_queue_relayout), constraint); g_signal_connect (constraint->source, "destroy", G_CALLBACK (source_destroyed), constraint); if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); } g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_SOURCE]); } /** * clutter_snap_constraint_get_source: * @constraint: a #ClutterSnapConstraint * * Retrieves the #ClutterActor set using clutter_snap_constraint_set_source() * * Return value: (transfer none): a pointer to the source actor * * Since: 1.6 */ ClutterActor * clutter_snap_constraint_get_source (ClutterSnapConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint), NULL); return constraint->source; } /** * clutter_snap_constraint_set_edges: * @constraint: a #ClutterSnapConstraint * @from_edge: the edge on the actor * @to_edge: the edge on the source * * Sets the edges to be used by the @constraint * * The @from_edge is the edge on the #ClutterActor to which @constraint * has been added. The @to_edge is the edge of the #ClutterActor inside * the #ClutterSnapConstraint:source property. * * Since: 1.6 */ void clutter_snap_constraint_set_edges (ClutterSnapConstraint *constraint, ClutterSnapEdge from_edge, ClutterSnapEdge to_edge) { gboolean from_changed = FALSE, to_changed = FALSE; g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint)); g_object_freeze_notify (G_OBJECT (constraint)); if (constraint->from_edge != from_edge) { constraint->from_edge = from_edge; g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_FROM_EDGE]); from_changed = TRUE; } if (constraint->to_edge != to_edge) { constraint->to_edge = to_edge; g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_TO_EDGE]); to_changed = TRUE; } if ((from_changed || to_changed) && constraint->actor != NULL) { clutter_actor_queue_relayout (constraint->actor); } g_object_thaw_notify (G_OBJECT (constraint)); } /** * clutter_snap_constraint_get_edges: * @constraint: a #ClutterSnapConstraint * @from_edge: (out): return location for the actor's edge, or %NULL * @to_edge: (out): return location for the source's edge, or %NULL * * Retrieves the edges used by the @constraint * * Since: 1.6 */ void clutter_snap_constraint_get_edges (ClutterSnapConstraint *constraint, ClutterSnapEdge *from_edge, ClutterSnapEdge *to_edge) { g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint)); if (from_edge) *from_edge = constraint->from_edge; if (to_edge) *to_edge = constraint->to_edge; } /** * clutter_snap_constraint_set_offset: * @constraint: a #ClutterSnapConstraint * @offset: the offset to apply, in pixels * * Sets the offset to be applied to the constraint * * Since: 1.6 */ void clutter_snap_constraint_set_offset (ClutterSnapConstraint *constraint, gfloat offset) { g_return_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint)); if (fabs (constraint->offset - offset) < 0.00001f) return; constraint->offset = offset; if (constraint->actor != NULL) clutter_actor_queue_relayout (constraint->actor); g_object_notify_by_pspec (G_OBJECT (constraint), obj_props[PROP_OFFSET]); } /** * clutter_snap_constraint_get_offset: * @constraint: a #ClutterSnapConstraint * * Retrieves the offset set using clutter_snap_constraint_set_offset() * * Return value: the offset, in pixels * * Since: 1.6 */ gfloat clutter_snap_constraint_get_offset (ClutterSnapConstraint *constraint) { g_return_val_if_fail (CLUTTER_IS_SNAP_CONSTRAINT (constraint), 0.0); return constraint->offset; } muffin-6.4.1/clutter/clutter/clutter-event.c0000664000175000017500000015723014723361714020063 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #include "clutter-build-config.h" #include "clutter-backend-private.h" #include "clutter-debug.h" #include "clutter-event-private.h" #include "clutter-keysyms.h" #include "clutter-private.h" #include /** * SECTION:clutter-event * @short_description: User and window system events * * Windowing events handled by Clutter. * * The events usually come from the windowing backend, but can also * be synthesized by Clutter itself or by the application code. */ typedef struct _ClutterEventPrivate { ClutterEvent base; ClutterInputDevice *device; ClutterInputDevice *source_device; gdouble delta_x; gdouble delta_y; ClutterInputDeviceTool *tool; gpointer platform_data; ClutterModifierType button_state; ClutterModifierType base_state; ClutterModifierType latched_state; ClutterModifierType locked_state; guint is_pointer_emulated : 1; } ClutterEventPrivate; typedef struct _ClutterEventFilter { int id; ClutterStage *stage; ClutterEventFilterFunc func; GDestroyNotify notify; gpointer user_data; } ClutterEventFilter; static GHashTable *all_events = NULL; G_DEFINE_BOXED_TYPE (ClutterEvent, clutter_event, clutter_event_copy, clutter_event_free); static ClutterEventSequence * clutter_event_sequence_copy (ClutterEventSequence *sequence) { /* Nothing to copy here */ return sequence; } static void clutter_event_sequence_free (ClutterEventSequence *sequence) { /* Nothing to free here */ } G_DEFINE_BOXED_TYPE (ClutterEventSequence, clutter_event_sequence, clutter_event_sequence_copy, clutter_event_sequence_free); static gboolean is_event_allocated (const ClutterEvent *event) { if (all_events == NULL) return FALSE; return g_hash_table_lookup (all_events, event) != NULL; } /* * _clutter_event_get_platform_data: * @event: a #ClutterEvent * * Retrieves the pointer to platform-specific data inside an event * * Return value: a pointer to platform-specific data * * Since: 1.4 */ gpointer _clutter_event_get_platform_data (const ClutterEvent *event) { if (!is_event_allocated (event)) return NULL; return ((ClutterEventPrivate *) event)->platform_data; } /*< private > * _clutter_event_set_platform_data: * @event: a #ClutterEvent * @data: a pointer to platform-specific data * * Sets the pointer to platform-specific data inside an event * * Since: 1.4 */ void _clutter_event_set_platform_data (ClutterEvent *event, gpointer data) { if (!is_event_allocated (event)) return; ((ClutterEventPrivate *) event)->platform_data = data; } void _clutter_event_set_pointer_emulated (ClutterEvent *event, gboolean is_emulated) { if (!is_event_allocated (event)) return; ((ClutterEventPrivate *) event)->is_pointer_emulated = !!is_emulated; } /** * clutter_event_type: * @event: a #ClutterEvent * * Retrieves the type of the event. * * Return value: a #ClutterEventType */ ClutterEventType clutter_event_type (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_NOTHING); return event->type; } /** * clutter_event_get_time: * @event: a #ClutterEvent * * Retrieves the time of the event. * * Return value: the time of the event, or %CLUTTER_CURRENT_TIME * * Since: 0.4 */ guint32 clutter_event_get_time (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_CURRENT_TIME); return event->any.time; } /** * clutter_event_set_time: * @event: a #ClutterEvent * @time_: the time of the event * * Sets the time of the event. * * Since: 1.8 */ void clutter_event_set_time (ClutterEvent *event, guint32 time_) { g_return_if_fail (event != NULL); event->any.time = time_; } /** * clutter_event_get_state: * @event: a #ClutterEvent * * Retrieves the modifier state of the event. In case the window system * supports reporting latched and locked modifiers, this function returns * the effective state. * * Return value: the modifier state parameter, or 0 * * Since: 0.4 */ ClutterModifierType clutter_event_get_state (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); switch (event->type) { case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: return event->key.modifier_state; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: return event->button.modifier_state; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: return event->touch.modifier_state; case CLUTTER_MOTION: return event->motion.modifier_state; case CLUTTER_SCROLL: return event->scroll.modifier_state; default: break; } return 0; } /** * clutter_event_set_state: * @event: a #ClutterEvent * @state: the modifier state to set * * Sets the modifier state of the event. * * Since: 1.8 */ void clutter_event_set_state (ClutterEvent *event, ClutterModifierType state) { g_return_if_fail (event != NULL); switch (event->type) { case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: event->key.modifier_state = state; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: event->button.modifier_state = state; break; case CLUTTER_MOTION: event->motion.modifier_state = state; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: event->touch.modifier_state = state; break; case CLUTTER_SCROLL: event->scroll.modifier_state = state; break; default: break; } } void _clutter_event_set_state_full (ClutterEvent *event, ClutterModifierType button_state, ClutterModifierType base_state, ClutterModifierType latched_state, ClutterModifierType locked_state, ClutterModifierType effective_state) { ClutterEventPrivate *private = (ClutterEventPrivate*) event; private->button_state = button_state; private->base_state = base_state; private->latched_state = latched_state; private->locked_state = locked_state; clutter_event_set_state (event, effective_state); } /** * clutter_event_get_state_full: * @event: a #ClutterEvent * @button_state: (out) (allow-none): the pressed buttons as a mask * @base_state: (out) (allow-none): the regular pressed modifier keys * @latched_state: (out) (allow-none): the latched modifier keys (currently released but still valid for one key press/release) * @locked_state: (out) (allow-none): the locked modifier keys (valid until the lock key is pressed and released again) * @effective_state: (out) (allow-none): the logical OR of all the state bits above * * Retrieves the decomposition of the keyboard state into button, base, * latched, locked and effective. This can be used to transmit to other * applications, for example when implementing a wayland compositor. * * Since: 1.16 */ void clutter_event_get_state_full (const ClutterEvent *event, ClutterModifierType *button_state, ClutterModifierType *base_state, ClutterModifierType *latched_state, ClutterModifierType *locked_state, ClutterModifierType *effective_state) { const ClutterEventPrivate *private = (const ClutterEventPrivate*) event; g_return_if_fail (event != NULL); if (button_state) *button_state = private->button_state; if (base_state) *base_state = private->base_state; if (latched_state) *latched_state = private->latched_state; if (locked_state) *locked_state = private->locked_state; if (effective_state) *effective_state = clutter_event_get_state (event); } /** * clutter_event_get_coords: * @event: a #ClutterEvent * @x: (out): return location for the X coordinate, or %NULL * @y: (out): return location for the Y coordinate, or %NULL * * Retrieves the coordinates of @event and puts them into @x and @y. * * Since: 0.4 */ void clutter_event_get_coords (const ClutterEvent *event, gfloat *x, gfloat *y) { graphene_point_t coords; g_return_if_fail (event != NULL); clutter_event_get_position (event, &coords); if (x != NULL) *x = coords.x; if (y != NULL) *y = coords.y; } /** * clutter_event_get_position: * @event: a #ClutterEvent * @position: a #graphene_point_t * * Retrieves the event coordinates as a #graphene_point_t. * * Since: 1.12 */ void clutter_event_get_position (const ClutterEvent *event, graphene_point_t *position) { g_return_if_fail (event != NULL); g_return_if_fail (position != NULL); switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_STAGE_STATE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_DELETE: case CLUTTER_EVENT_LAST: case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: graphene_point_init (position, 0.f, 0.f); break; case CLUTTER_ENTER: case CLUTTER_LEAVE: graphene_point_init (position, event->crossing.x, event->crossing.y); break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: graphene_point_init (position, event->button.x, event->button.y); break; case CLUTTER_MOTION: graphene_point_init (position, event->motion.x, event->motion.y); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: graphene_point_init (position, event->touch.x, event->touch.y); break; case CLUTTER_SCROLL: graphene_point_init (position, event->scroll.x, event->scroll.y); break; case CLUTTER_TOUCHPAD_PINCH: graphene_point_init (position, event->touchpad_pinch.x, event->touchpad_pinch.y); break; case CLUTTER_TOUCHPAD_SWIPE: graphene_point_init (position, event->touchpad_swipe.x, event->touchpad_swipe.y); break; } } /** * clutter_event_set_coords: * @event: a #ClutterEvent * @x: the X coordinate of the event * @y: the Y coordinate of the event * * Sets the coordinates of the @event. * * Since: 1.8 */ void clutter_event_set_coords (ClutterEvent *event, gfloat x, gfloat y) { g_return_if_fail (event != NULL); switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_STAGE_STATE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_DELETE: case CLUTTER_EVENT_LAST: case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: break; case CLUTTER_ENTER: case CLUTTER_LEAVE: event->crossing.x = x; event->crossing.y = y; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: event->button.x = x; event->button.y = y; break; case CLUTTER_MOTION: event->motion.x = x; event->motion.y = y; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: event->touch.x = x; event->touch.y = y; break; case CLUTTER_SCROLL: event->scroll.x = x; event->scroll.y = y; break; case CLUTTER_TOUCHPAD_PINCH: event->touchpad_pinch.x = x; event->touchpad_pinch.y = y; break; case CLUTTER_TOUCHPAD_SWIPE: event->touchpad_swipe.x = x; event->touchpad_swipe.y = y; break; } } /** * clutter_event_get_source: * @event: a #ClutterEvent * * Retrieves the source #ClutterActor the event originated from, or * NULL if the event has no source. * * Return value: (transfer none): a #ClutterActor * * Since: 0.6 */ ClutterActor * clutter_event_get_source (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); return event->any.source; } /** * clutter_event_set_source: * @event: a #ClutterEvent * @actor: (allow-none): a #ClutterActor, or %NULL * * Sets the source #ClutterActor of @event. * * Since: 1.8 */ void clutter_event_set_source (ClutterEvent *event, ClutterActor *actor) { g_return_if_fail (event != NULL); g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); event->any.source = actor; } /** * clutter_event_get_stage: * @event: a #ClutterEvent * * Retrieves the source #ClutterStage the event originated for, or * %NULL if the event has no stage. * * Return value: (transfer none): a #ClutterStage * * Since: 0.8 */ ClutterStage * clutter_event_get_stage (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); return event->any.stage; } /** * clutter_event_set_stage: * @event: a #ClutterEvent * @stage: (allow-none): a #ClutterStage, or %NULL * * Sets the source #ClutterStage of the event. * * Since: 1.8 */ void clutter_event_set_stage (ClutterEvent *event, ClutterStage *stage) { g_return_if_fail (event != NULL); g_return_if_fail (stage == NULL || CLUTTER_IS_STAGE (stage)); if (event->any.stage == stage) return; event->any.stage = stage; } /** * clutter_event_get_flags: * @event: a #ClutterEvent * * Retrieves the #ClutterEventFlags of @event * * Return value: the event flags * * Since: 1.0 */ ClutterEventFlags clutter_event_get_flags (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_EVENT_NONE); return event->any.flags; } /** * clutter_event_set_flags: * @event: a #ClutterEvent * @flags: a binary OR of #ClutterEventFlags values * * Sets the #ClutterEventFlags of @event * * Since: 1.8 */ void clutter_event_set_flags (ClutterEvent *event, ClutterEventFlags flags) { g_return_if_fail (event != NULL); if (event->any.flags == flags) return; event->any.flags = flags; event->any.flags |= CLUTTER_EVENT_FLAG_SYNTHETIC; } /** * clutter_event_get_related: * @event: a #ClutterEvent of type %CLUTTER_ENTER or of * type %CLUTTER_LEAVE * * Retrieves the related actor of a crossing event. * * Return value: (transfer none): the related #ClutterActor, or %NULL * * Since: 1.0 */ ClutterActor * clutter_event_get_related (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE, NULL); return event->crossing.related; } /** * clutter_event_set_related: * @event: a #ClutterEvent of type %CLUTTER_ENTER or %CLUTTER_LEAVE * @actor: (allow-none): a #ClutterActor or %NULL * * Sets the related actor of a crossing event * * Since: 1.8 */ void clutter_event_set_related (ClutterEvent *event, ClutterActor *actor) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE); g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); if (event->crossing.related == actor) return; event->crossing.related = actor; } /** * clutter_event_set_scroll_delta: * @event: a #ClutterEvent of type %CLUTTER_SCROLL * @dx: delta on the horizontal axis * @dy: delta on the vertical axis * * Sets the precise scrolling information of @event. * * Since: 1.10 */ void clutter_event_set_scroll_delta (ClutterEvent *event, gdouble dx, gdouble dy) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_SCROLL); if (!is_event_allocated (event)) return; event->scroll.direction = CLUTTER_SCROLL_SMOOTH; ((ClutterEventPrivate *) event)->delta_x = dx; ((ClutterEventPrivate *) event)->delta_y = dy; } /** * clutter_event_get_scroll_delta: * @event: a #ClutterEvent of type %CLUTTER_SCROLL * @dx: (out): return location for the delta on the horizontal axis * @dy: (out): return location for the delta on the vertical axis * * Retrieves the precise scrolling information of @event. * * The @event has to have a #ClutterScrollEvent.direction value * of %CLUTTER_SCROLL_SMOOTH. * * Since: 1.10 */ void clutter_event_get_scroll_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy) { gdouble delta_x, delta_y; g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_SCROLL); g_return_if_fail (event->scroll.direction == CLUTTER_SCROLL_SMOOTH); delta_x = delta_y = 0; if (is_event_allocated (event)) { delta_x = ((ClutterEventPrivate *) event)->delta_x; delta_y = ((ClutterEventPrivate *) event)->delta_y; } if (dx != NULL) *dx = delta_x; if (dy != NULL) *dy = delta_y; } /** * clutter_event_get_scroll_direction: * @event: a #ClutterEvent of type %CLUTTER_SCROLL * * Retrieves the direction of the scrolling of @event * * Return value: the scrolling direction * * Since: 1.0 */ ClutterScrollDirection clutter_event_get_scroll_direction (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_UP); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_UP); return event->scroll.direction; } /** * clutter_event_set_scroll_direction: * @event: a #ClutterEvent * @direction: the scrolling direction * * Sets the direction of the scrolling of @event * * Since: 1.8 */ void clutter_event_set_scroll_direction (ClutterEvent *event, ClutterScrollDirection direction) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_SCROLL); event->scroll.direction = direction; } /** * clutter_event_get_button: * @event: a #ClutterEvent of type %CLUTTER_BUTTON_PRESS or * of type %CLUTTER_BUTTON_RELEASE * * Retrieves the button number of @event * * Return value: the button number * * Since: 1.0 */ guint32 clutter_event_get_button (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE || event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE, 0); if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) return event->button.button; else return event->pad_button.button; } /** * clutter_event_set_button: * @event: a #ClutterEvent or type %CLUTTER_BUTTON_PRESS or * of type %CLUTTER_BUTTON_RELEASE * @button: the button number * * Sets the button number of @event * * Since: 1.8 */ void clutter_event_set_button (ClutterEvent *event, guint32 button) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE); event->button.button = button; } /** * clutter_event_get_click_count: * @event: a #ClutterEvent of type %CLUTTER_BUTTON_PRESS or * of type %CLUTTER_BUTTON_RELEASE * * Retrieves the number of clicks of @event * * Return value: the click count * * Since: 1.0 */ guint32 clutter_event_get_click_count (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE, 0); return event->button.click_count; } /* keys */ /** * clutter_event_get_key_symbol: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or * of type %CLUTTER_KEY_RELEASE * * Retrieves the key symbol of @event * * Return value: the key symbol representing the key * * Since: 1.0 */ guint clutter_event_get_key_symbol (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); return event->key.keyval; } /** * clutter_event_set_key_symbol: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * @key_sym: the key symbol representing the key * * Sets the key symbol of @event. * * Since: 1.8 */ void clutter_event_set_key_symbol (ClutterEvent *event, guint key_sym) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE); event->key.keyval = key_sym; } /** * clutter_event_get_key_code: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS or * of type %CLUTTER_KEY_RELEASE * * Retrieves the keycode of the key that caused @event * * Return value: The keycode representing the key * * Since: 1.0 */ guint16 clutter_event_get_key_code (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); return event->key.hardware_keycode; } /** * clutter_event_set_key_code: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * @key_code: the keycode representing the key * * Sets the keycode of the @event. * * Since: 1.8 */ void clutter_event_set_key_code (ClutterEvent *event, guint16 key_code) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE); event->key.hardware_keycode = key_code; } /** * clutter_event_get_key_unicode: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * * Retrieves the unicode value for the key that caused @keyev. * * Return value: The unicode value representing the key */ gunichar clutter_event_get_key_unicode (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); if (event->key.unicode_value) return event->key.unicode_value; else return clutter_keysym_to_unicode (event->key.keyval); } /** * clutter_event_set_key_unicode: * @event: a #ClutterEvent of type %CLUTTER_KEY_PRESS * or %CLUTTER_KEY_RELEASE * @key_unicode: the Unicode value representing the key * * Sets the Unicode value of @event. * * Since: 1.8 */ void clutter_event_set_key_unicode (ClutterEvent *event, gunichar key_unicode) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE); event->key.unicode_value = key_unicode; } /** * clutter_event_get_event_sequence: * @event: a #ClutterEvent of type %CLUTTER_TOUCH_BEGIN, * %CLUTTER_TOUCH_UPDATE, %CLUTTER_TOUCH_END, or * %CLUTTER_TOUCH_CANCEL * * Retrieves the #ClutterEventSequence of @event. * * Return value: (transfer none): the event sequence, or %NULL * * Since: 1.10 */ ClutterEventSequence * clutter_event_get_event_sequence (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_UPDATE || event->type == CLUTTER_TOUCH_END || event->type == CLUTTER_TOUCH_CANCEL) return event->touch.sequence; else if (event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE) return event->crossing.sequence; return NULL; } /** * clutter_event_get_device_id: * @event: a clutter event * * Retrieves the events device id if set. * * Return value: A unique identifier for the device or -1 if the event has * no specific device set. */ gint clutter_event_get_device_id (const ClutterEvent *event) { ClutterInputDevice *device = NULL; g_return_val_if_fail (event != NULL, CLUTTER_POINTER_DEVICE); device = clutter_event_get_device (event); if (device != NULL) return clutter_input_device_get_device_id (device); return -1; } /** * clutter_event_get_device_type: * @event: a #ClutterEvent * * Retrieves the type of the device for @event * * Return value: the #ClutterInputDeviceType for the device, if * any is set * * Since: 1.0 */ ClutterInputDeviceType clutter_event_get_device_type (const ClutterEvent *event) { ClutterInputDevice *device = NULL; g_return_val_if_fail (event != NULL, CLUTTER_POINTER_DEVICE); device = clutter_event_get_device (event); if (device != NULL) return clutter_input_device_get_device_type (device); return CLUTTER_POINTER_DEVICE; } /** * clutter_event_set_device: * @event: a #ClutterEvent * @device: (allow-none): a #ClutterInputDevice, or %NULL * * Sets the device for @event. * * Since: 1.6 */ void clutter_event_set_device (ClutterEvent *event, ClutterInputDevice *device) { g_return_if_fail (event != NULL); g_return_if_fail (device == NULL || CLUTTER_IS_INPUT_DEVICE (device)); if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; g_set_object (&real_event->device, device); } switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_STAGE_STATE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_DELETE: case CLUTTER_EVENT_LAST: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: break; case CLUTTER_ENTER: case CLUTTER_LEAVE: event->crossing.device = device; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: event->button.device = device; break; case CLUTTER_MOTION: event->motion.device = device; break; case CLUTTER_SCROLL: event->scroll.device = device; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: event->touch.device = device; break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: event->key.device = device; break; case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: /* Rely on priv data for these */ break; case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: event->proximity.device = device; break; case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: event->pad_button.device = device; break; case CLUTTER_PAD_STRIP: event->pad_strip.device = device; break; case CLUTTER_PAD_RING: event->pad_ring.device = device; break; case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: event->device.device = device; break; } } /** * clutter_event_get_device: * @event: a #ClutterEvent * * Retrieves the #ClutterInputDevice for the event. * If you want the physical device the event originated from, use * clutter_event_get_source_device(). * * The #ClutterInputDevice structure is completely opaque and should * be cast to the platform-specific implementation. * * Return value: (transfer none): the #ClutterInputDevice or %NULL. The * returned device is owned by the #ClutterEvent and it should not * be unreferenced * * Since: 1.0 */ ClutterInputDevice * clutter_event_get_device (const ClutterEvent *event) { ClutterInputDevice *device = NULL; g_return_val_if_fail (event != NULL, NULL); if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; if (real_event->device != NULL) return real_event->device; } switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_STAGE_STATE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_DELETE: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: case CLUTTER_EVENT_LAST: break; case CLUTTER_ENTER: case CLUTTER_LEAVE: device = event->crossing.device; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: device = event->button.device; break; case CLUTTER_MOTION: device = event->motion.device; break; case CLUTTER_SCROLL: device = event->scroll.device; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: device = event->touch.device; break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: device = event->key.device; break; case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: /* Rely on priv data for these */ break; case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: device = event->proximity.device; break; case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: device = event->pad_button.device; break; case CLUTTER_PAD_STRIP: device = event->pad_strip.device; break; case CLUTTER_PAD_RING: device = event->pad_ring.device; break; case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: device = event->device.device; break; } return device; } /** * clutter_event_set_device_tool: * @event: a #ClutterEvent * @tool: (nullable): a #ClutterInputDeviceTool * * Sets the tool in use for this event * * Since: 1.28 **/ void clutter_event_set_device_tool (ClutterEvent *event, ClutterInputDeviceTool *tool) { g_return_if_fail (event != NULL); if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; real_event->tool = tool; } } /** * clutter_event_get_device_tool: * @event: a #ClutterEvent * * Returns the device tool that originated this event * * Returns: (transfer none): The tool of this event * * Since: 1.28 **/ ClutterInputDeviceTool * clutter_event_get_device_tool (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, NULL); if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; return real_event->tool; } return NULL; } /** * clutter_event_new: * @type: The type of event. * * Creates a new #ClutterEvent of the specified type. * * Return value: (transfer full): A newly allocated #ClutterEvent. */ ClutterEvent * clutter_event_new (ClutterEventType type) { ClutterEvent *new_event; ClutterEventPrivate *priv; priv = g_slice_new0 (ClutterEventPrivate); new_event = (ClutterEvent *) priv; new_event->type = new_event->any.type = type; if (G_UNLIKELY (all_events == NULL)) all_events = g_hash_table_new (NULL, NULL); g_hash_table_replace (all_events, priv, GUINT_TO_POINTER (1)); return new_event; } /** * clutter_event_copy: * @event: A #ClutterEvent. * * Copies @event. * * Return value: (transfer full): A newly allocated #ClutterEvent */ ClutterEvent * clutter_event_copy (const ClutterEvent *event) { ClutterEvent *new_event; ClutterEventPrivate *new_real_event; ClutterInputDevice *device; gint n_axes = 0; g_return_val_if_fail (event != NULL, NULL); new_event = clutter_event_new (CLUTTER_NOTHING); new_real_event = (ClutterEventPrivate *) new_event; *new_event = *event; if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; g_set_object (&new_real_event->device, real_event->device); g_set_object (&new_real_event->source_device, real_event->source_device); new_real_event->delta_x = real_event->delta_x; new_real_event->delta_y = real_event->delta_y; new_real_event->is_pointer_emulated = real_event->is_pointer_emulated; new_real_event->base_state = real_event->base_state; new_real_event->button_state = real_event->button_state; new_real_event->latched_state = real_event->latched_state; new_real_event->locked_state = real_event->locked_state; new_real_event->tool = real_event->tool; } device = clutter_event_get_device (event); if (device != NULL) n_axes = clutter_input_device_get_n_axes (device); switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: if (event->button.axes != NULL) { new_event->button.axes = g_memdup2 (event->button.axes, sizeof (double) * n_axes); } break; case CLUTTER_SCROLL: if (event->scroll.axes != NULL) { new_event->scroll.axes = g_memdup2 (event->scroll.axes, sizeof (double) * n_axes); } break; case CLUTTER_MOTION: if (event->motion.axes != NULL) { new_event->motion.axes = g_memdup2 (event->motion.axes, sizeof (double) * n_axes); } break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: if (event->touch.axes != NULL) { new_event->touch.axes = g_memdup2 (event->touch.axes, sizeof (double) * n_axes); } break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_PREEDIT: new_event->im.text = g_strdup (event->im.text); break; case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: new_event->device.device = event->device.device; break; default: break; } if (is_event_allocated (event)) _clutter_backend_copy_event_data (clutter_get_default_backend (), event, new_event); return new_event; } /** * clutter_event_free: * @event: A #ClutterEvent. * * Frees all resources used by @event. */ void clutter_event_free (ClutterEvent *event) { if (G_LIKELY (event != NULL)) { _clutter_backend_free_event_data (clutter_get_default_backend (), event); if (is_event_allocated (event)) { ClutterEventPrivate *real_event = (ClutterEventPrivate *) event; g_clear_object (&real_event->device); g_clear_object (&real_event->source_device); } switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: g_free (event->button.axes); break; case CLUTTER_MOTION: g_free (event->motion.axes); break; case CLUTTER_SCROLL: g_free (event->scroll.axes); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: g_free (event->touch.axes); break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_PREEDIT: g_free (event->im.text); break; default: break; } g_hash_table_remove (all_events, event); g_slice_free (ClutterEventPrivate, (ClutterEventPrivate *) event); } } /** * clutter_event_get: * * Pops an event off the event queue. Applications should not need to call * this. * * Return value: A #ClutterEvent or NULL if queue empty * * Since: 0.4 */ ClutterEvent * clutter_event_get (void) { ClutterMainContext *context = _clutter_context_get_default (); if (context->events_queue == NULL) return NULL; if (g_queue_is_empty (context->events_queue)) return NULL; return g_queue_pop_tail (context->events_queue); } /** * clutter_event_peek: * * Returns a pointer to the first event from the event queue but * does not remove it. * * Return value: (transfer none): A #ClutterEvent or NULL if queue empty. * * Since: 0.4 */ ClutterEvent * clutter_event_peek (void) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (context != NULL, NULL); if (context->events_queue == NULL) return NULL; if (g_queue_is_empty (context->events_queue)) return NULL; return g_queue_peek_tail (context->events_queue); } void _clutter_event_push (const ClutterEvent *event, gboolean do_copy) { ClutterMainContext *context = _clutter_context_get_default (); ClutterInputDevice *device; g_assert (context != NULL); if (context->events_queue == NULL) context->events_queue = g_queue_new (); /* disabled devices don't propagate events */ device = clutter_event_get_device (event); if (device != NULL) { if (event->type != CLUTTER_DEVICE_ADDED && event->type != CLUTTER_DEVICE_REMOVED && !clutter_input_device_get_enabled (device)) return; } if (do_copy) { ClutterEvent *copy; copy = clutter_event_copy (event); event = copy; } g_queue_push_head (context->events_queue, (gpointer) event); } /** * clutter_event_put: * @event: a #ClutterEvent * * Puts a copy of the event on the back of the event queue. The event will * have the %CLUTTER_EVENT_FLAG_SYNTHETIC flag set. If the source is set * event signals will be emitted for this source and capture/bubbling for * its ancestors. If the source is not set it will be generated by picking * or use the actor that currently has keyboard focus * * Since: 0.6 */ void clutter_event_put (const ClutterEvent *event) { _clutter_event_push (event, TRUE); } /** * clutter_events_pending: * * Checks if events are pending in the event queue. * * Return value: TRUE if there are pending events, FALSE otherwise. * * Since: 0.4 */ gboolean clutter_events_pending (void) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (context != NULL, FALSE); if (context->events_queue == NULL) return FALSE; return g_queue_is_empty (context->events_queue) == FALSE; } /** * clutter_get_current_event_time: * * Retrieves the timestamp of the last event, if there is an * event or if the event has a timestamp. * * Return value: the event timestamp, or %CLUTTER_CURRENT_TIME * * Since: 1.0 */ guint32 clutter_get_current_event_time (void) { const ClutterEvent* event; event = clutter_get_current_event (); if (event != NULL) return clutter_event_get_time (event); return CLUTTER_CURRENT_TIME; } /** * clutter_get_current_event: * * If an event is currently being processed, return that event. * This function is intended to be used to access event state * that might not be exposed by higher-level widgets. For * example, to get the key modifier state from a Button 'clicked' * event. * * Return value: (transfer none): The current ClutterEvent, or %NULL if none * * Since: 1.2 */ const ClutterEvent * clutter_get_current_event (void) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (context != NULL, NULL); return context->current_event != NULL ? context->current_event->data : NULL; } /** * clutter_event_get_source_device: * @event: a #ClutterEvent * * Retrieves the hardware device that originated the event. * * If you need the virtual device, use clutter_event_get_device(). * * If no hardware device originated this event, this function will * return the same device as clutter_event_get_device(). * * Return value: (transfer none): a pointer to a #ClutterInputDevice * or %NULL * * Since: 1.6 */ ClutterInputDevice * clutter_event_get_source_device (const ClutterEvent *event) { ClutterEventPrivate *real_event; if (!is_event_allocated (event)) return NULL; real_event = (ClutterEventPrivate *) event; if (real_event->source_device != NULL) return real_event->source_device; return clutter_event_get_device (event); } /** * clutter_event_set_source_device: * @event: a #ClutterEvent * @device: (allow-none): a #ClutterInputDevice * * Sets the source #ClutterInputDevice for @event. * * The #ClutterEvent must have been created using clutter_event_new(). * * Since: 1.8 */ void clutter_event_set_source_device (ClutterEvent *event, ClutterInputDevice *device) { ClutterEventPrivate *real_event; g_return_if_fail (event != NULL); g_return_if_fail (device == NULL || CLUTTER_IS_INPUT_DEVICE (device)); if (!is_event_allocated (event)) return; real_event = (ClutterEventPrivate *) event; g_set_object (&real_event->source_device, device); } /** * clutter_event_get_axes: * @event: a #ClutterEvent * @n_axes: (out): return location for the number of axes returned * * Retrieves the array of axes values attached to the event. * * Return value: (transfer none): an array of axis values * * Since: 1.6 */ gdouble * clutter_event_get_axes (const ClutterEvent *event, guint *n_axes) { gdouble *retval = NULL; guint len = 0; switch (event->type) { case CLUTTER_NOTHING: case CLUTTER_STAGE_STATE: case CLUTTER_DESTROY_NOTIFY: case CLUTTER_CLIENT_MESSAGE: case CLUTTER_DELETE: case CLUTTER_ENTER: case CLUTTER_LEAVE: case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_EVENT_LAST: case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: break; case CLUTTER_SCROLL: retval = event->scroll.axes; break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: retval = event->button.axes; break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: retval = event->touch.axes; break; case CLUTTER_MOTION: retval = event->motion.axes; break; case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: break; } if (retval != NULL) { ClutterInputDevice *device; device = clutter_event_get_device (event); if (device != NULL) len = clutter_input_device_get_n_axes (device); else retval = NULL; } if (n_axes) *n_axes = len; return retval; } /** * clutter_event_get_distance: * @source: a #ClutterEvent * @target: a #ClutterEvent * * Retrieves the distance between two events, a @source and a @target. * * Return value: the distance between two #ClutterEvent * * Since: 1.12 */ float clutter_event_get_distance (const ClutterEvent *source, const ClutterEvent *target) { graphene_point_t p0, p1; clutter_event_get_position (source, &p0); clutter_event_get_position (source, &p1); return graphene_point_distance (&p0, &p1, NULL, NULL); } /** * clutter_event_get_angle: * @source: a #ClutterEvent * @target: a #ClutterEvent * * Retrieves the angle relative from @source to @target. * * The direction of the angle is from the position X axis towards * the positive Y axis. * * Return value: the angle between two #ClutterEvent * * Since: 1.12 */ double clutter_event_get_angle (const ClutterEvent *source, const ClutterEvent *target) { graphene_point_t p0, p1; float x_distance, y_distance; double angle; clutter_event_get_position (source, &p0); clutter_event_get_position (target, &p1); if (graphene_point_equal (&p0, &p1)) return 0; graphene_point_distance (&p0, &p1, &x_distance, &y_distance); angle = atan2 (x_distance, y_distance); /* invert the angle, and shift it by 90 degrees */ angle = (2.0 * G_PI) - angle; angle += G_PI / 2.0; /* keep the angle within the [ 0, 360 ] interval */ angle = fmod (angle, 2.0 * G_PI); return angle; } /** * clutter_event_has_shift_modifier: * @event: a #ClutterEvent * * Checks whether @event has the Shift modifier mask set. * * Return value: %TRUE if the event has the Shift modifier mask set * * Since: 1.12 */ gboolean clutter_event_has_shift_modifier (const ClutterEvent *event) { return (clutter_event_get_state (event) & CLUTTER_SHIFT_MASK) != FALSE; } /** * clutter_event_has_control_modifier: * @event: a #ClutterEvent * * Checks whether @event has the Control modifier mask set. * * Return value: %TRUE if the event has the Control modifier mask set * * Since: 1.12 */ gboolean clutter_event_has_control_modifier (const ClutterEvent *event) { return (clutter_event_get_state (event) & CLUTTER_CONTROL_MASK) != FALSE; } /** * clutter_event_is_pointer_emulated: * @event: a #ClutterEvent * * Checks whether a pointer @event has been generated by the windowing * system. The returned value can be used to distinguish between events * synthesized by the windowing system itself (as opposed by Clutter). * * Return value: %TRUE if the event is pointer emulated * * Since: 1.12 */ gboolean clutter_event_is_pointer_emulated (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, FALSE); if (!is_event_allocated (event)) return FALSE; return ((ClutterEventPrivate *) event)->is_pointer_emulated; } gboolean _clutter_event_process_filters (ClutterEvent *event) { ClutterMainContext *context = _clutter_context_get_default (); GList *l, *next; /* Event filters are handled in order from least recently added to * most recently added */ for (l = context->event_filters; l; l = next) { ClutterEventFilter *event_filter = l->data; next = l->next; if (event_filter->stage && event_filter->stage != event->any.stage) continue; if (event_filter->func (event, event_filter->user_data) == CLUTTER_EVENT_STOP) return CLUTTER_EVENT_STOP; } return CLUTTER_EVENT_PROPAGATE; } /** * clutter_event_add_filter: * @stage: (allow-none): The #ClutterStage to capture events for * @func: The callback function which will be passed all events. * @notify: A #GDestroyNotify * @user_data: A data pointer to pass to the function. * * Adds a function which will be called for all events that Clutter * processes. The function will be called before any signals are * emitted for the event and it will take precedence over any grabs. * * Return value: an identifier for the event filter, to be used * with clutter_event_remove_filter(). * * Since: 1.18 */ guint clutter_event_add_filter (ClutterStage *stage, ClutterEventFilterFunc func, GDestroyNotify notify, gpointer user_data) { ClutterMainContext *context = _clutter_context_get_default (); ClutterEventFilter *event_filter = g_slice_new (ClutterEventFilter); static guint event_filter_id = 0; event_filter->stage = stage; event_filter->id = ++event_filter_id; event_filter->func = func; event_filter->notify = notify; event_filter->user_data = user_data; /* The event filters are kept in order from least recently added to * most recently added so we must add it to the end */ context->event_filters = g_list_append (context->event_filters, event_filter); return event_filter->id; } /** * clutter_event_remove_filter: * @id: The ID of the event filter, as returned from clutter_event_add_filter() * * Removes an event filter that was previously added with * clutter_event_add_filter(). * * Since: 1.18 */ void clutter_event_remove_filter (guint id) { ClutterMainContext *context = _clutter_context_get_default (); GList *l; for (l = context->event_filters; l; l = l->next) { ClutterEventFilter *event_filter = l->data; if (event_filter->id == id) { if (event_filter->notify) event_filter->notify (event_filter->user_data); context->event_filters = g_list_delete_link (context->event_filters, l); g_slice_free (ClutterEventFilter, event_filter); return; } } g_warning ("No event filter found for id: %d\n", id); } /** * clutter_event_get_touchpad_gesture_finger_count: * @event: a touchpad swipe/pinch event * * Returns the number of fingers that is triggering the touchpad gesture. * * Returns: the number of fingers in the gesture. * * Since: 1.24 **/ guint clutter_event_get_touchpad_gesture_finger_count (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_SWIPE || event->type == CLUTTER_TOUCHPAD_PINCH, 0); if (event->type == CLUTTER_TOUCHPAD_SWIPE) return event->touchpad_swipe.n_fingers; else if (event->type == CLUTTER_TOUCHPAD_PINCH) return event->touchpad_pinch.n_fingers; return 0; } /** * clutter_event_get_gesture_pinch_angle_delta: * @event: a touchpad pinch event * * Returns the angle delta reported by this specific event. * * Returns: The angle delta relative to the previous event. * * Since: 1.24 **/ gdouble clutter_event_get_gesture_pinch_angle_delta (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH, 0); return event->touchpad_pinch.angle_delta; } /** * clutter_event_get_gesture_pinch_scale: * @event: a touchpad pinch event * * Returns the current scale as reported by @event, 1.0 being the original * distance at the time the corresponding event with phase * %CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN is received. * is received. * * Returns: the current pinch gesture scale * * Since: 1.24 **/ gdouble clutter_event_get_gesture_pinch_scale (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH, 0); return event->touchpad_pinch.scale; } /** * clutter_event_get_gesture_phase: * @event: a touchpad gesture event * * Returns the phase of the event, See #ClutterTouchpadGesturePhase. * * Returns: the phase of the gesture event. **/ ClutterTouchpadGesturePhase clutter_event_get_gesture_phase (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_SWIPE, 0); if (event->type == CLUTTER_TOUCHPAD_PINCH) return event->touchpad_pinch.phase; else if (event->type == CLUTTER_TOUCHPAD_SWIPE) return event->touchpad_swipe.phase; /* Shouldn't ever happen */ return CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; }; /** * clutter_event_get_gesture_motion_delta: * @event: A clutter touchpad gesture event * @dx: (out) (allow-none): the displacement relative to the pointer * position in the X axis, or %NULL * @dy: (out) (allow-none): the displacement relative to the pointer * position in the Y axis, or %NULL * * Returns the gesture motion deltas relative to the current pointer * position. * * Since: 1.24 **/ void clutter_event_get_gesture_motion_delta (const ClutterEvent *event, gdouble *dx, gdouble *dy) { g_return_if_fail (event != NULL); g_return_if_fail (event->type == CLUTTER_TOUCHPAD_PINCH || event->type == CLUTTER_TOUCHPAD_SWIPE); if (event->type == CLUTTER_TOUCHPAD_PINCH) { if (dx) *dx = event->touchpad_pinch.dx; if (dy) *dy = event->touchpad_pinch.dy; } else if (event->type == CLUTTER_TOUCHPAD_SWIPE) { if (dx) *dx = event->touchpad_swipe.dx; if (dy) *dy = event->touchpad_swipe.dy; } } /** * clutter_event_get_scroll_source: * @event: an scroll event * * Returns the #ClutterScrollSource that applies to an scroll event. * * Returns: The source of scroll events * * Since: 1.26 **/ ClutterScrollSource clutter_event_get_scroll_source (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_SOURCE_UNKNOWN); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_SOURCE_UNKNOWN); return event->scroll.scroll_source; } /** * clutter_event_get_scroll_finish_flags: * @event: an scroll event * * Returns the #ClutterScrollFinishFlags of an scroll event. Those * can be used to determine whether post-scroll effects like kinetic * scrolling should be applied. * * Returns: The scroll finish flags * * Since: 1.26 **/ ClutterScrollFinishFlags clutter_event_get_scroll_finish_flags (const ClutterEvent *event) { g_return_val_if_fail (event != NULL, CLUTTER_SCROLL_FINISHED_NONE); g_return_val_if_fail (event->type == CLUTTER_SCROLL, CLUTTER_SCROLL_FINISHED_NONE); return event->scroll.finish_flags; } guint clutter_event_get_mode_group (const ClutterEvent *event) { g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP, 0); switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: return event->pad_button.group; case CLUTTER_PAD_RING: return event->pad_ring.group; case CLUTTER_PAD_STRIP: return event->pad_strip.group; default: return 0; } } /** * clutter_event_get_pad_event_details: * @event: a pad event * @number: (out) (optional): ring/strip/button number * @mode: (out) (optional): pad mode as per the event * @value: (out) (optional): event axis value * * Returns the details of a pad event. * * Returns: #TRUE if event details could be obtained **/ gboolean clutter_event_get_pad_event_details (const ClutterEvent *event, guint *number, guint *mode, gdouble *value) { guint n, m; gdouble v; g_return_val_if_fail (event != NULL, FALSE); g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP, FALSE); switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: n = event->pad_button.button; m = event->pad_button.mode; v = 0.0; break; case CLUTTER_PAD_RING: n = event->pad_ring.ring_number; m = event->pad_ring.mode; v = event->pad_ring.angle; break; case CLUTTER_PAD_STRIP: n = event->pad_strip.strip_number; m = event->pad_strip.mode; v = event->pad_strip.value; break; default: return FALSE; } if (number) *number = n; if (mode) *mode = m; if (value) *value = v; return TRUE; } muffin-6.4.1/clutter/clutter/clutter-fixed-layout.h0000664000175000017500000000521014723361714021347 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_FIXED_LAYOUT_H__ #define __CLUTTER_FIXED_LAYOUT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_FIXED_LAYOUT (clutter_fixed_layout_get_type ()) #define CLUTTER_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayout)) #define CLUTTER_IS_FIXED_LAYOUT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_FIXED_LAYOUT)) #define CLUTTER_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass)) #define CLUTTER_IS_FIXED_LAYOUT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_FIXED_LAYOUT)) #define CLUTTER_FIXED_LAYOUT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_FIXED_LAYOUT, ClutterFixedLayoutClass)) typedef struct _ClutterFixedLayout ClutterFixedLayout; typedef struct _ClutterFixedLayoutClass ClutterFixedLayoutClass; /** * ClutterFixedLayout: * * The #ClutterFixedLayout structure contains only private data and * it should be accessed using the provided API * * Since: 1.2 */ struct _ClutterFixedLayout { /*< private >*/ ClutterLayoutManager parent_instance; }; /** * ClutterFixedLayoutClass: * * The #ClutterFixedLayoutClass structure contains only private data * and it should be accessed using the provided API * * Since: 1.2 */ struct _ClutterFixedLayoutClass { /*< private >*/ ClutterLayoutManagerClass parent_class; }; CLUTTER_EXPORT GType clutter_fixed_layout_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterLayoutManager *clutter_fixed_layout_new (void); G_END_DECLS #endif /* __CLUTTER_FIXED_LAYOUT_H__ */ muffin-6.4.1/clutter/clutter/clutter-cairo.c0000664000175000017500000000461514723361714020035 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-cairo * @Title: Cairo integration * @Short_Description: Functions for interoperating with Cairo * * Clutter provides some utility functions for using Cairo. */ #include "clutter-build-config.h" #include "clutter-cairo.h" #include "clutter-color.h" /** * clutter_cairo_set_source_color: * @cr: a Cairo context * @color: a #ClutterColor * * Utility function for setting the source color of @cr using * a #ClutterColor. This function is the equivalent of: * * |[ * cairo_set_source_rgba (cr, * color->red / 255.0, * color->green / 255.0, * color->blue / 255.0, * color->alpha / 255.0); * ]| * * Since: 1.0 */ void clutter_cairo_set_source_color (cairo_t *cr, const ClutterColor *color) { g_return_if_fail (cr != NULL); g_return_if_fail (color != NULL); if (color->alpha == 0xff) cairo_set_source_rgb (cr, color->red / 255.0, color->green / 255.0, color->blue / 255.0); else cairo_set_source_rgba (cr, color->red / 255.0, color->green / 255.0, color->blue / 255.0, color->alpha / 255.0); } /** * clutter_cairo_clear: * @cr: a Cairo context * * Utility function to clear a Cairo context. * * Since: 1.12 */ void clutter_cairo_clear (cairo_t *cr) { cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); } muffin-6.4.1/clutter/clutter/clutter-keymap.c0000664000175000017500000000344514723361714020226 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "clutter-build-config.h" #include "clutter-keymap.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterKeymap, clutter_keymap, G_TYPE_OBJECT) enum { STATE_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; static void clutter_keymap_class_init (ClutterKeymapClass *klass) { signals[STATE_CHANGED] = g_signal_new (I_("state-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void clutter_keymap_init (ClutterKeymap *keymap) { } gboolean clutter_keymap_get_num_lock_state (ClutterKeymap *keymap) { return CLUTTER_KEYMAP_GET_CLASS (keymap)->get_num_lock_state (keymap); } gboolean clutter_keymap_get_caps_lock_state (ClutterKeymap *keymap) { return CLUTTER_KEYMAP_GET_CLASS (keymap)->get_caps_lock_state (keymap); } PangoDirection clutter_keymap_get_direction (ClutterKeymap *keymap) { return CLUTTER_KEYMAP_GET_CLASS (keymap)->get_direction (keymap); } muffin-6.4.1/clutter/clutter/clutter-deform-effect.h0000664000175000017500000001033014723361714021442 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_DEFORM_EFFECT_H__ #define __CLUTTER_DEFORM_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_DEFORM_EFFECT (clutter_deform_effect_get_type ()) #define CLUTTER_DEFORM_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_DEFORM_EFFECT, ClutterDeformEffect)) #define CLUTTER_IS_DEFORM_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_DEFORM_EFFECT)) #define CLUTTER_DEFORM_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DEFORM_EFFECT, ClutterDeformEffectClass)) #define CLUTTER_IS_DEFORM_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DEFORM_EFFECT)) #define CLUTTER_DEFORM_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DEFORM_EFFECT, ClutterDeformEffectClass)) typedef struct _ClutterDeformEffect ClutterDeformEffect; typedef struct _ClutterDeformEffectPrivate ClutterDeformEffectPrivate; typedef struct _ClutterDeformEffectClass ClutterDeformEffectClass; /** * ClutterDeformEffect: * * The #ClutterDeformEffect structure contains * only private data and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterDeformEffect { /*< private >*/ ClutterOffscreenEffect parent_instance; ClutterDeformEffectPrivate *priv; }; /** * ClutterDeformEffectClass: * @deform_vertex: virtual function; sub-classes should override this * function to compute the deformation of each vertex * * The #ClutterDeformEffectClass structure contains * only private data * * Since: 1.4 */ struct _ClutterDeformEffectClass { /*< private >*/ ClutterOffscreenEffectClass parent_class; /*< public >*/ void (* deform_vertex) (ClutterDeformEffect *effect, gfloat width, gfloat height, CoglTextureVertex *vertex); /*< private >*/ void (*_clutter_deform1) (void); void (*_clutter_deform2) (void); void (*_clutter_deform3) (void); void (*_clutter_deform4) (void); void (*_clutter_deform5) (void); void (*_clutter_deform6) (void); void (*_clutter_deform7) (void); }; CLUTTER_EXPORT GType clutter_deform_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_deform_effect_set_back_material (ClutterDeformEffect *effect, CoglHandle material); CLUTTER_EXPORT CoglHandle clutter_deform_effect_get_back_material (ClutterDeformEffect *effect); CLUTTER_EXPORT void clutter_deform_effect_set_n_tiles (ClutterDeformEffect *effect, guint x_tiles, guint y_tiles); CLUTTER_EXPORT void clutter_deform_effect_get_n_tiles (ClutterDeformEffect *effect, guint *x_tiles, guint *y_tiles); CLUTTER_EXPORT void clutter_deform_effect_invalidate (ClutterDeformEffect *effect); G_END_DECLS #endif /* __CLUTTER_DEFORM_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-offscreen-effect-private.h0000664000175000017500000000033614723361714023615 0ustar fabiofabio#ifndef __CLUTTER_OFFSCREEN_EFFECT_PRIVATE_H__ #define __CLUTTER_OFFSCREEN_EFFECT_PRIVATE_H__ #include G_BEGIN_DECLS G_END_DECLS #endif /* __CLUTTER_OFFSCREEN_EFFECT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-input-method-private.h0000664000175000017500000000436714723361714023036 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef __CLUTTER_INPUT_METHOD_PRIVATE_H__ #define __CLUTTER_INPUT_METHOD_PRIVATE_H__ ClutterInputFocus * clutter_input_method_get_focus (ClutterInputMethod *method); void clutter_input_method_reset (ClutterInputMethod *method); void clutter_input_method_set_cursor_location (ClutterInputMethod *method, const graphene_rect_t *rect); void clutter_input_method_set_surrounding (ClutterInputMethod *method, const gchar *text, guint cursor, guint anchor); void clutter_input_method_set_content_hints (ClutterInputMethod *method, ClutterInputContentHintFlags hints); void clutter_input_method_set_content_purpose (ClutterInputMethod *method, ClutterInputContentPurpose purpose); void clutter_input_method_set_can_show_preedit (ClutterInputMethod *method, gboolean can_show_preedit); gboolean clutter_input_method_filter_key_event (ClutterInputMethod *method, const ClutterKeyEvent *key); void clutter_input_method_toggle_input_panel (ClutterInputMethod *method); #endif /* __CLUTTER_INPUT_METHOD_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/x11/0000775000175000017500000000000014723361714015517 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/x11/clutter-x11.h0000664000175000017500000000705214723361714017765 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-x11 * @short_description: X11 specific API * * The X11 backend for Clutter provides some specific API, allowing * integration with the Xlibs API for embedding and manipulating the * stage window, or for trapping X errors. * * The ClutterX11 API is available since Clutter 0.6 */ #ifndef __CLUTTER_X11_H__ #define __CLUTTER_X11_H__ #include #include #include #include #include G_BEGIN_DECLS /** * ClutterX11FilterReturn: * @CLUTTER_X11_FILTER_CONTINUE: The event was not handled, continues the * processing * @CLUTTER_X11_FILTER_TRANSLATE: Native event translated into a Clutter * event, stops the processing * @CLUTTER_X11_FILTER_REMOVE: Remove the event, stops the processing * * Return values for the #ClutterX11FilterFunc function. * * Since: 0.6 */ typedef enum { CLUTTER_X11_FILTER_CONTINUE, CLUTTER_X11_FILTER_TRANSLATE, CLUTTER_X11_FILTER_REMOVE } ClutterX11FilterReturn; CLUTTER_EXPORT GType clutter_x11_filter_return_get_type (void) G_GNUC_CONST; /* * This is not used any more */ typedef struct _ClutterX11XInputDevice ClutterX11XInputDevice; /** * ClutterX11FilterFunc: * @xev: Native X11 event structure * @cev: Clutter event structure * @data: (closure): user data passed to the filter function * * Filter function for X11 native events. * * Return value: the result of the filtering * * Since: 0.6 */ typedef ClutterX11FilterReturn (*ClutterX11FilterFunc) (XEvent *xev, ClutterEvent *cev, gpointer data); CLUTTER_EXPORT void clutter_x11_trap_x_errors (void); CLUTTER_EXPORT gint clutter_x11_untrap_x_errors (void); CLUTTER_EXPORT Display *clutter_x11_get_default_display (void); CLUTTER_EXPORT int clutter_x11_get_default_screen (void); CLUTTER_EXPORT Window clutter_x11_get_root_window (void); CLUTTER_EXPORT void clutter_x11_set_display (Display * xdpy); CLUTTER_EXPORT void clutter_x11_add_filter (ClutterX11FilterFunc func, gpointer data); CLUTTER_EXPORT void clutter_x11_remove_filter (ClutterX11FilterFunc func, gpointer data); CLUTTER_EXPORT gboolean clutter_x11_has_composite_extension (void); CLUTTER_EXPORT void clutter_x11_set_use_argb_visual (gboolean use_argb); CLUTTER_EXPORT gboolean clutter_x11_get_use_argb_visual (void); CLUTTER_EXPORT void clutter_x11_set_use_stereo_stage (gboolean use_stereo); CLUTTER_EXPORT gboolean clutter_x11_get_use_stereo_stage (void); G_END_DECLS #endif /* __CLUTTER_X11_H__ */ muffin-6.4.1/clutter/clutter/x11/xsettings/0000775000175000017500000000000014723361714017547 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/x11/xsettings/xsettings-common.c0000664000175000017500000001327314723361714023237 0ustar fabiofabio/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include "clutter-build-config.h" #include "string.h" #include "stdlib.h" #include #include /* For CARD32 */ #include "xsettings-common.h" XSettingsSetting * xsettings_setting_copy (XSettingsSetting *setting) { XSettingsSetting *result; size_t str_len; result = malloc (sizeof *result); if (!result) return NULL; str_len = strlen (setting->name); result->name = malloc (str_len + 1); if (!result->name) goto err; memcpy (result->name, setting->name, str_len + 1); result->type = setting->type; switch (setting->type) { case XSETTINGS_TYPE_INT: result->data.v_int = setting->data.v_int; break; case XSETTINGS_TYPE_COLOR: result->data.v_color = setting->data.v_color; break; case XSETTINGS_TYPE_STRING: str_len = strlen (setting->data.v_string); result->data.v_string = malloc (str_len + 1); if (!result->data.v_string) goto err; memcpy (result->data.v_string, setting->data.v_string, str_len + 1); break; } result->last_change_serial = setting->last_change_serial; return result; err: if (result->name) free (result->name); free (result); return NULL; } XSettingsList * xsettings_list_copy (XSettingsList *list) { XSettingsList *new = NULL; XSettingsList *old_iter = list; XSettingsList *new_iter = NULL; while (old_iter) { XSettingsList *new_node; new_node = malloc (sizeof *new_node); if (!new_node) goto error; new_node->setting = xsettings_setting_copy (old_iter->setting); if (!new_node->setting) { free (new_node); goto error; } if (new_iter) new_iter->next = new_node; else new = new_node; new_iter = new_node; old_iter = old_iter->next; } return new; error: xsettings_list_free (new); return NULL; } int xsettings_setting_equal (XSettingsSetting *setting_a, XSettingsSetting *setting_b) { if (setting_a->type != setting_b->type) return 0; if (strcmp (setting_a->name, setting_b->name) != 0) return 0; switch (setting_a->type) { case XSETTINGS_TYPE_INT: return setting_a->data.v_int == setting_b->data.v_int; case XSETTINGS_TYPE_COLOR: return (setting_a->data.v_color.red == setting_b->data.v_color.red && setting_a->data.v_color.green == setting_b->data.v_color.green && setting_a->data.v_color.blue == setting_b->data.v_color.blue && setting_a->data.v_color.alpha == setting_b->data.v_color.alpha); case XSETTINGS_TYPE_STRING: return strcmp (setting_a->data.v_string, setting_b->data.v_string) == 0; } return 0; } void xsettings_setting_free (XSettingsSetting *setting) { if (setting->type == XSETTINGS_TYPE_STRING) free (setting->data.v_string); if (setting->name) free (setting->name); free (setting); } void xsettings_list_free (XSettingsList *list) { while (list) { XSettingsList *next = list->next; xsettings_setting_free (list->setting); free (list); list = next; } } XSettingsResult xsettings_list_insert (XSettingsList **list, XSettingsSetting *setting) { XSettingsList *node; XSettingsList *iter; XSettingsList *last = NULL; node = malloc (sizeof *node); if (!node) return XSETTINGS_NO_MEM; node->setting = setting; iter = *list; while (iter) { int cmp = strcmp (setting->name, iter->setting->name); if (cmp < 0) break; else if (cmp == 0) { free (node); return XSETTINGS_DUPLICATE_ENTRY; } last = iter; iter = iter->next; } if (last) last->next = node; else *list = node; node->next = iter; return XSETTINGS_SUCCESS; } XSettingsResult xsettings_list_delete (XSettingsList **list, const char *name) { XSettingsList *iter; XSettingsList *last = NULL; iter = *list; while (iter) { if (strcmp (name, iter->setting->name) == 0) { if (last) last->next = iter->next; else *list = iter->next; xsettings_setting_free (iter->setting); free (iter); return XSETTINGS_SUCCESS; } last = iter; iter = iter->next; } return XSETTINGS_FAILED; } XSettingsSetting * xsettings_list_lookup (XSettingsList *list, const char *name) { XSettingsList *iter; iter = list; while (iter) { if (strcmp (name, iter->setting->name) == 0) return iter->setting; iter = iter->next; } return NULL; } char xsettings_byte_order (void) { CARD32 myint = 0x01020304; return (*(char *)&myint == 1) ? MSBFirst : LSBFirst; } muffin-6.4.1/clutter/clutter/x11/xsettings/xsettings-common.h0000664000175000017500000001054714723361714023245 0ustar fabiofabio/* * Copyright © 2001 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. * * Modified by: Emmanuele Bassi, Intel Corp. */ #ifndef XSETTINGS_COMMON_H #define XSETTINGS_COMMON_H #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ /* Renames for Clutter inclusion */ #define xsettings_byte_order _clutter_xsettings_byte_order #define xsettings_client_destroy _clutter_xsettings_client_destroy #define xsettings_client_get_setting _clutter_xsettings_client_get_setting #define xsettings_client_new _clutter_xsettings_client_new #define xsettings_client_new_with_grab_funcs _clutter_xsettings_client_new_with_grab_funcs #define xsettings_client_set_grab_func _clutter_xsettings_client_set_grab_func #define xsettings_client_set_ungrab_func _clutter_xsettings_client_set_ungrab_func #define xsettings_client_process_event _clutter_xsettings_client_process_event #define xsettings_list_copy _clutter_xsettings_list_copy #define xsettings_list_delete _clutter_xsettings_list_delete #define xsettings_list_free _clutter_xsettings_list_free #define xsettings_list_insert _clutter_xsettings_list_insert #define xsettings_list_lookup _clutter_xsettings_list_lookup #define xsettings_setting_copy _clutter_xsettings_setting_copy #define xsettings_setting_equal _clutter_xsettings_setting_equal #define xsettings_setting_free _clutter_xsettings_setting_free typedef struct _XSettingsBuffer XSettingsBuffer; typedef struct _XSettingsColor XSettingsColor; typedef struct _XSettingsList XSettingsList; typedef struct _XSettingsSetting XSettingsSetting; /* Types of settings possible. Enum values correspond to * protocol values. */ typedef enum { XSETTINGS_TYPE_INT = 0, XSETTINGS_TYPE_STRING = 1, XSETTINGS_TYPE_COLOR = 2 } XSettingsType; typedef enum { XSETTINGS_SUCCESS, XSETTINGS_NO_MEM, XSETTINGS_ACCESS, XSETTINGS_FAILED, XSETTINGS_NO_ENTRY, XSETTINGS_DUPLICATE_ENTRY } XSettingsResult; struct _XSettingsBuffer { char byte_order; size_t len; unsigned char *data; unsigned char *pos; }; struct _XSettingsColor { unsigned short red, green, blue, alpha; }; struct _XSettingsList { XSettingsSetting *setting; XSettingsList *next; }; struct _XSettingsSetting { char *name; XSettingsType type; union { int v_int; char *v_string; XSettingsColor v_color; } data; unsigned long last_change_serial; }; XSettingsSetting *xsettings_setting_copy (XSettingsSetting *setting); void xsettings_setting_free (XSettingsSetting *setting); int xsettings_setting_equal (XSettingsSetting *setting_a, XSettingsSetting *setting_b); void xsettings_list_free (XSettingsList *list); XSettingsList *xsettings_list_copy (XSettingsList *list); XSettingsResult xsettings_list_insert (XSettingsList **list, XSettingsSetting *setting); XSettingsSetting *xsettings_list_lookup (XSettingsList *list, const char *name); XSettingsResult xsettings_list_delete (XSettingsList **list, const char *name); char xsettings_byte_order (void); #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* XSETTINGS_COMMON_H */ muffin-6.4.1/clutter/clutter/x11/xsettings/xsettings-client.c0000664000175000017500000003447614723361714023235 0ustar fabiofabio/* * Copyright © 2001, 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #include "clutter-build-config.h" #include #include #include #include #include #include /* For CARD16 */ #include "xsettings-client.h" struct _XSettingsClient { Display *display; int screen; XSettingsNotifyFunc notify; XSettingsWatchFunc watch; void *cb_data; XSettingsGrabFunc grab; XSettingsGrabFunc ungrab; Window manager_window; Atom manager_atom; Atom selection_atom; Atom xsettings_atom; XSettingsList *settings; }; static void notify_changes (XSettingsClient *client, XSettingsList *old_list) { XSettingsList *old_iter = old_list; XSettingsList *new_iter = client->settings; if (!client->notify) return; while (old_iter || new_iter) { int cmp; if (old_iter && new_iter) cmp = strcmp (old_iter->setting->name, new_iter->setting->name); else if (old_iter) cmp = -1; else cmp = 1; if (cmp < 0) { client->notify (old_iter->setting->name, XSETTINGS_ACTION_DELETED, NULL, client->cb_data); } else if (cmp == 0) { if (!xsettings_setting_equal (old_iter->setting, new_iter->setting)) client->notify (old_iter->setting->name, XSETTINGS_ACTION_CHANGED, new_iter->setting, client->cb_data); } else { client->notify (new_iter->setting->name, XSETTINGS_ACTION_NEW, new_iter->setting, client->cb_data); } if (old_iter) old_iter = old_iter->next; if (new_iter) new_iter = new_iter->next; } } static int ignore_errors (Display *display, XErrorEvent *event) { return True; } static char local_byte_order = '\0'; #define BYTES_LEFT(buffer) ((buffer)->data + (buffer)->len - (buffer)->pos) static XSettingsResult fetch_card16 (XSettingsBuffer *buffer, CARD16 *result) { CARD16 x; if (BYTES_LEFT (buffer) < 2) return XSETTINGS_ACCESS; x = *(CARD16 *)buffer->pos; buffer->pos += 2; if (buffer->byte_order == local_byte_order) *result = x; else *result = (x << 8) | (x >> 8); return XSETTINGS_SUCCESS; } static XSettingsResult fetch_ushort (XSettingsBuffer *buffer, unsigned short *result) { CARD16 x; XSettingsResult r; r = fetch_card16 (buffer, &x); if (r == XSETTINGS_SUCCESS) *result = x; return r; } static XSettingsResult fetch_card32 (XSettingsBuffer *buffer, CARD32 *result) { CARD32 x; if (BYTES_LEFT (buffer) < 4) return XSETTINGS_ACCESS; x = *(CARD32 *)buffer->pos; buffer->pos += 4; if (buffer->byte_order == local_byte_order) *result = x; else *result = (x << 24) | ((x & 0xff00) << 8) | ((x & 0xff0000) >> 8) | (x >> 24); return XSETTINGS_SUCCESS; } static XSettingsResult fetch_card8 (XSettingsBuffer *buffer, CARD8 *result) { if (BYTES_LEFT (buffer) < 1) return XSETTINGS_ACCESS; *result = *(CARD8 *)buffer->pos; buffer->pos += 1; return XSETTINGS_SUCCESS; } #define XSETTINGS_PAD(n,m) ((n + m - 1) & (~(m-1))) static XSettingsList * parse_settings (unsigned char *data, size_t len) { XSettingsBuffer buffer; XSettingsResult result = XSETTINGS_SUCCESS; XSettingsList *settings = NULL; CARD32 serial; CARD32 n_entries; CARD32 i; XSettingsSetting *setting = NULL; local_byte_order = xsettings_byte_order (); buffer.pos = buffer.data = data; buffer.len = len; result = fetch_card8 (&buffer, (unsigned char *)&buffer.byte_order); if (buffer.byte_order != MSBFirst && buffer.byte_order != LSBFirst) { fprintf (stderr, "Invalid byte order in XSETTINGS property\n"); result = XSETTINGS_FAILED; goto out; } buffer.pos += 3; result = fetch_card32 (&buffer, &serial); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_card32 (&buffer, &n_entries); if (result != XSETTINGS_SUCCESS) goto out; for (i = 0; i < n_entries; i++) { CARD8 type; CARD16 name_len; CARD32 v_int; size_t pad_len; result = fetch_card8 (&buffer, &type); if (result != XSETTINGS_SUCCESS) goto out; buffer.pos += 1; result = fetch_card16 (&buffer, &name_len); if (result != XSETTINGS_SUCCESS) goto out; pad_len = XSETTINGS_PAD(name_len, 4); if (BYTES_LEFT (&buffer) < pad_len) { result = XSETTINGS_ACCESS; goto out; } setting = malloc (sizeof *setting); if (!setting) { result = XSETTINGS_NO_MEM; goto out; } setting->type = XSETTINGS_TYPE_INT; /* No allocated memory */ setting->name = malloc (name_len + 1); if (!setting->name) { result = XSETTINGS_NO_MEM; goto out; } memcpy (setting->name, buffer.pos, name_len); setting->name[name_len] = '\0'; buffer.pos += pad_len; result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; setting->last_change_serial = v_int; switch (type) { case XSETTINGS_TYPE_INT: result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; setting->data.v_int = (INT32)v_int; break; case XSETTINGS_TYPE_STRING: result = fetch_card32 (&buffer, &v_int); if (result != XSETTINGS_SUCCESS) goto out; pad_len = XSETTINGS_PAD (v_int, 4); if (v_int + 1 == 0 || /* Guard against wrap-around */ BYTES_LEFT (&buffer) < pad_len) { result = XSETTINGS_ACCESS; goto out; } setting->data.v_string = malloc (v_int + 1); if (!setting->data.v_string) { result = XSETTINGS_NO_MEM; goto out; } memcpy (setting->data.v_string, buffer.pos, v_int); setting->data.v_string[v_int] = '\0'; buffer.pos += pad_len; break; case XSETTINGS_TYPE_COLOR: result = fetch_ushort (&buffer, &setting->data.v_color.red); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.green); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.blue); if (result != XSETTINGS_SUCCESS) goto out; result = fetch_ushort (&buffer, &setting->data.v_color.alpha); if (result != XSETTINGS_SUCCESS) goto out; break; default: /* Quietly ignore unknown types */ break; } setting->type = type; result = xsettings_list_insert (&settings, setting); if (result != XSETTINGS_SUCCESS) goto out; setting = NULL; } out: if (result != XSETTINGS_SUCCESS) { switch (result) { case XSETTINGS_NO_MEM: fprintf(stderr, "Out of memory reading XSETTINGS property\n"); break; case XSETTINGS_ACCESS: fprintf(stderr, "Invalid XSETTINGS property (read off end)\n"); break; case XSETTINGS_DUPLICATE_ENTRY: fprintf (stderr, "Duplicate XSETTINGS entry for '%s'\n", setting->name); case XSETTINGS_FAILED: case XSETTINGS_SUCCESS: case XSETTINGS_NO_ENTRY: break; } if (setting) xsettings_setting_free (setting); xsettings_list_free (settings); settings = NULL; } return settings; } static void read_settings (XSettingsClient *client) { Atom type; int format; unsigned long n_items; unsigned long bytes_after; unsigned char *data; int result; int (*old_handler) (Display *, XErrorEvent *); XSettingsList *old_list = client->settings; client->settings = NULL; if (client->manager_window) { old_handler = XSetErrorHandler (ignore_errors); result = XGetWindowProperty (client->display, client->manager_window, client->xsettings_atom, 0, LONG_MAX, False, client->xsettings_atom, &type, &format, &n_items, &bytes_after, &data); XSetErrorHandler (old_handler); if (result == Success && type != None) { if (type != client->xsettings_atom) { fprintf (stderr, "Invalid type for XSETTINGS property"); } else if (format != 8) { fprintf (stderr, "Invalid format for XSETTINGS property %d", format); } else client->settings = parse_settings (data, n_items); XFree (data); } } notify_changes (client, old_list); xsettings_list_free (old_list); } static void add_events (Display *display, Window window, long mask) { XWindowAttributes attr; XGetWindowAttributes (display, window, &attr); XSelectInput (display, window, attr.your_event_mask | mask); } static void check_manager_window (XSettingsClient *client) { if (client->manager_window && client->watch) client->watch (client->manager_window, False, 0, client->cb_data); if (client->grab) client->grab (client->display); else XGrabServer (client->display); client->manager_window = XGetSelectionOwner (client->display, client->selection_atom); if (client->manager_window) XSelectInput (client->display, client->manager_window, PropertyChangeMask | StructureNotifyMask); if (client->ungrab) client->ungrab (client->display); else XUngrabServer (client->display); XFlush (client->display); if (client->manager_window && client->watch) { if (!client->watch (client->manager_window, True, PropertyChangeMask | StructureNotifyMask, client->cb_data)) { /* Inability to watch the window probably means that it was destroyed * after we ungrabbed */ client->manager_window = None; return; } } read_settings (client); } XSettingsClient * xsettings_client_new (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data) { return xsettings_client_new_with_grab_funcs (display, screen, notify, watch, cb_data, NULL, NULL); } XSettingsClient * xsettings_client_new_with_grab_funcs (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data, XSettingsGrabFunc grab, XSettingsGrabFunc ungrab) { XSettingsClient *client; char buffer[256]; const char *atom_names[3]; Atom atoms[3]; client = malloc (sizeof *client); if (!client) return NULL; client->display = display; client->screen = screen; client->notify = notify; client->watch = watch; client->cb_data = cb_data; client->grab = grab; client->ungrab = ungrab; client->manager_window = None; client->settings = NULL; sprintf(buffer, "_XSETTINGS_S%d", screen); atom_names[0] = buffer; atom_names[1] = "_XSETTINGS_SETTINGS"; atom_names[2] = "MANAGER"; #ifdef HAVE_XINTERNATOMS XInternAtoms (display, atom_names, 3, False, atoms); #else atoms[0] = XInternAtom (display, atom_names[0], False); atoms[1] = XInternAtom (display, atom_names[1], False); atoms[2] = XInternAtom (display, atom_names[2], False); #endif client->selection_atom = atoms[0]; client->xsettings_atom = atoms[1]; client->manager_atom = atoms[2]; /* Select on StructureNotify so we get MANAGER events */ add_events (display, RootWindow (display, screen), StructureNotifyMask); if (client->watch) client->watch (RootWindow (display, screen), True, StructureNotifyMask, client->cb_data); check_manager_window (client); return client; } void xsettings_client_set_grab_func (XSettingsClient *client, XSettingsGrabFunc grab) { client->grab = grab; } void xsettings_client_set_ungrab_func (XSettingsClient *client, XSettingsGrabFunc ungrab) { client->ungrab = ungrab; } void xsettings_client_destroy (XSettingsClient *client) { if (client->watch) client->watch (RootWindow (client->display, client->screen), False, 0, client->cb_data); if (client->manager_window && client->watch) client->watch (client->manager_window, False, 0, client->cb_data); xsettings_list_free (client->settings); free (client); } XSettingsResult xsettings_client_get_setting (XSettingsClient *client, const char *name, XSettingsSetting **setting) { XSettingsSetting *search = xsettings_list_lookup (client->settings, name); if (search) { *setting = xsettings_setting_copy (search); return *setting ? XSETTINGS_SUCCESS : XSETTINGS_NO_MEM; } else return XSETTINGS_NO_ENTRY; } Bool xsettings_client_process_event (XSettingsClient *client, XEvent *xev) { /* The checks here will not unlikely cause us to reread * the properties from the manager window a number of * times when the manager changes from A->B. But manager changes * are going to be pretty rare. */ if (xev->xany.window == RootWindow (client->display, client->screen)) { if (xev->xany.type == ClientMessage && xev->xclient.message_type == client->manager_atom && xev->xclient.data.l[1] == client->selection_atom) { check_manager_window (client); return True; } } else if (xev->xany.window == client->manager_window) { if (xev->xany.type == DestroyNotify) { check_manager_window (client); /* let GDK do its cleanup */ return False; } else if (xev->xany.type == PropertyNotify) { read_settings (client); return True; } } return False; } muffin-6.4.1/clutter/clutter/x11/xsettings/xsettings-client.h0000664000175000017500000000625114723361714023230 0ustar fabiofabio/* * Copyright © 2001, 2007 Red Hat, Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Red Hat not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. Red Hat makes no representations about the * suitability of this software for any purpose. It is provided "as is" * without express or implied warranty. * * RED HAT DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL RED HAT * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * Author: Owen Taylor, Red Hat, Inc. */ #ifndef XSETTINGS_CLIENT_H #define XSETTINGS_CLIENT_H #include #include "xsettings-common.h" #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ typedef struct _XSettingsClient XSettingsClient; typedef enum { XSETTINGS_ACTION_NEW, XSETTINGS_ACTION_CHANGED, XSETTINGS_ACTION_DELETED } XSettingsAction; typedef void (*XSettingsNotifyFunc) (const char *name, XSettingsAction action, XSettingsSetting *setting, void *cb_data); typedef Bool (*XSettingsWatchFunc) (Window window, Bool is_start, long mask, void *cb_data); typedef void (*XSettingsGrabFunc) (Display *display); XSettingsClient *xsettings_client_new (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data); XSettingsClient *xsettings_client_new_with_grab_funcs (Display *display, int screen, XSettingsNotifyFunc notify, XSettingsWatchFunc watch, void *cb_data, XSettingsGrabFunc grab, XSettingsGrabFunc ungrab); void xsettings_client_set_grab_func (XSettingsClient *client, XSettingsGrabFunc grab); void xsettings_client_set_ungrab_func (XSettingsClient *client, XSettingsGrabFunc ungrab); void xsettings_client_destroy (XSettingsClient *client); Bool xsettings_client_process_event (XSettingsClient *client, XEvent *xev); XSettingsResult xsettings_client_get_setting (XSettingsClient *client, const char *name, XSettingsSetting **setting); #ifdef __cplusplus } #endif /* __cplusplus */ #endif /* XSETTINGS_CLIENT_H */ muffin-6.4.1/clutter/clutter/x11/clutter-settings-x11.h0000664000175000017500000000170514723361714021622 0ustar fabiofabio#ifndef __CLUTTER_SETTINGS_X11_H__ #define __CLUTTER_SETTINGS_X11_H__ /* XSETTINGS key names to ClutterSettings properties */ static const struct { const char *xsetting_name; const char *settings_property; } _clutter_settings_map[] = { { "Net/DoubleClickDistance", "double-click-distance" }, { "Net/DndDragThreshold", "dnd-drag-threshold" }, { "Gtk/FontName", "font-name" }, { "Xft/Antialias", "font-antialias" }, { "Xft/Hinting", "font-hinting" }, { "Xft/HintStyle", "font-hint-style" }, { "Xft/RGBA", "font-subpixel-order" }, { "Fontconfig/Timestamp", "fontconfig-timestamp" }, }; static const gint _n_clutter_settings_map = G_N_ELEMENTS (_clutter_settings_map); #define CLUTTER_SETTING_X11_NAME(id) (_clutter_settings_map[(id)].xsetting_name) #define CLUTTER_SETTING_PROPERTY(id) (_clutter_settings_map[(id)].settings_property) #endif /* __CLUTTER_SETTINGS_X11_H__ */ muffin-6.4.1/clutter/clutter/x11/clutter-backend-x11.c0000664000175000017500000007057114723361714021353 0ustar fabiofabio/* Clutter. * An OpenGL based 'interactive canvas' library. * Authored By Matthew Allum * Copyright (C) 2006-2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #include "clutter-build-config.h" #include #include #include #include #include #include #include #include "clutter-backend-x11.h" #include "clutter-settings-x11.h" #include "clutter-x11.h" #include "xsettings/xsettings-common.h" #include #include #include #include #include "clutter-backend.h" #include "clutter-debug.h" #include "clutter-event-private.h" #include "clutter-main.h" #include "clutter-private.h" #include "clutter-settings-private.h" G_DEFINE_TYPE (ClutterBackendX11, clutter_backend_x11, CLUTTER_TYPE_BACKEND) GType clutter_x11_filter_return_get_type (void) { static gsize g_define_type; if (g_once_init_enter (&g_define_type)) { static const GEnumValue values[] = { { CLUTTER_X11_FILTER_CONTINUE, "CLUTTER_X11_FILTER_CONTINUE", "continue" }, { CLUTTER_X11_FILTER_TRANSLATE, "CLUTTER_X11_FILTER_TRANSLATE", "translate" }, { CLUTTER_X11_FILTER_REMOVE, "CLUTTER_X11_FILTER_REMOVE", "remove" }, { 0, NULL, NULL }, }; GType id = g_enum_register_static (g_intern_static_string ("ClutterX11FilterReturn"), values); g_once_init_leave (&g_define_type, id); } return g_define_type; } /* atoms; remember to add the code that assigns the atom value to * the member of the ClutterBackendX11 structure if you add an * atom name here. do not change the order! */ static const gchar *atom_names[] = { "_NET_WM_PID", "_NET_WM_PING", "_NET_WM_STATE", "_NET_WM_USER_TIME", "WM_PROTOCOLS", "WM_DELETE_WINDOW", "_XEMBED", "_XEMBED_INFO", "_NET_WM_NAME", "UTF8_STRING", }; #define N_ATOM_NAMES G_N_ELEMENTS (atom_names) /* various flags corresponding to pre init setup calls */ static gboolean clutter_enable_xinput = TRUE; static gboolean clutter_enable_argb = FALSE; static gboolean clutter_enable_stereo = FALSE; static Display *_foreign_dpy = NULL; /* options */ static gchar *clutter_display_name = NULL; static gint clutter_screen = -1; static gboolean clutter_synchronise = FALSE; /* X error trap */ static int TrappedErrorCode = 0; static int (* old_error_handler) (Display *, XErrorEvent *); static ClutterX11FilterReturn xsettings_filter (XEvent *xevent, ClutterEvent *event, gpointer data) { ClutterBackendX11 *backend_x11 = data; _clutter_xsettings_client_process_event (backend_x11->xsettings, xevent); /* we always want the rest of the stack to get XSettings events, even * if Clutter already handled them */ return CLUTTER_X11_FILTER_CONTINUE; } static ClutterX11FilterReturn cogl_xlib_filter (XEvent *xevent, ClutterEvent *event, gpointer data) { ClutterBackend *backend = data; ClutterX11FilterReturn retval; CoglFilterReturn ret; ret = cogl_xlib_renderer_handle_event (backend->cogl_renderer, xevent); switch (ret) { case COGL_FILTER_REMOVE: retval = CLUTTER_X11_FILTER_REMOVE; break; case COGL_FILTER_CONTINUE: default: retval = CLUTTER_X11_FILTER_CONTINUE; break; } return retval; } static void clutter_backend_x11_xsettings_notify (const char *name, XSettingsAction action, XSettingsSetting *setting, void *cb_data) { ClutterSettings *settings = clutter_settings_get_default (); gint i; if (name == NULL || *name == '\0') return; if (setting == NULL) return; g_object_freeze_notify (G_OBJECT (settings)); for (i = 0; i < _n_clutter_settings_map; i++) { if (g_strcmp0 (name, CLUTTER_SETTING_X11_NAME (i)) == 0) { GValue value = G_VALUE_INIT; switch (setting->type) { case XSETTINGS_TYPE_INT: g_value_init (&value, G_TYPE_INT); g_value_set_int (&value, setting->data.v_int); break; case XSETTINGS_TYPE_STRING: g_value_init (&value, G_TYPE_STRING); g_value_set_string (&value, setting->data.v_string); break; case XSETTINGS_TYPE_COLOR: { ClutterColor color; color.red = (guint8) ((float) setting->data.v_color.red / 65535.0 * 255); color.green = (guint8) ((float) setting->data.v_color.green / 65535.0 * 255); color.blue = (guint8) ((float) setting->data.v_color.blue / 65535.0 * 255); color.alpha = (guint8) ((float) setting->data.v_color.alpha / 65535.0 * 255); g_value_init (&value, G_TYPE_BOXED); clutter_value_set_color (&value, &color); } break; } CLUTTER_NOTE (BACKEND, "Mapping XSETTING '%s' to 'ClutterSettings:%s'", CLUTTER_SETTING_X11_NAME (i), CLUTTER_SETTING_PROPERTY (i)); clutter_settings_set_property_internal (settings, CLUTTER_SETTING_PROPERTY (i), &value); g_value_unset (&value); break; } } g_object_thaw_notify (G_OBJECT (settings)); } static gboolean clutter_backend_x11_pre_parse (ClutterBackend *backend, GError **error) { const gchar *env_string; /* we don't fail here if DISPLAY is not set, as the user * might pass the --display command line switch */ env_string = g_getenv ("DISPLAY"); if (env_string) { clutter_display_name = g_strdup (env_string); env_string = NULL; } env_string = g_getenv ("CLUTTER_DISABLE_ARGB_VISUAL"); if (env_string) { clutter_enable_argb = FALSE; env_string = NULL; } env_string = g_getenv ("CLUTTER_DISABLE_XINPUT"); if (env_string) { clutter_enable_xinput = FALSE; env_string = NULL; } return TRUE; } static gboolean clutter_backend_x11_post_parse (ClutterBackend *backend, GError **error) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); Atom atoms[N_ATOM_NAMES]; if (_foreign_dpy) backend_x11->xdpy = _foreign_dpy; /* Only open connection if not already set by prior call to * clutter_x11_set_display() */ if (backend_x11->xdpy == NULL) { if (clutter_display_name != NULL && *clutter_display_name != '\0') { CLUTTER_NOTE (BACKEND, "XOpenDisplay on '%s'", clutter_display_name); backend_x11->xdpy = XOpenDisplay (clutter_display_name); if (backend_x11->xdpy == NULL) { g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to open display '%s'", clutter_display_name); return FALSE; } } else { g_set_error_literal (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, "Unable to open display. You have to set the " "DISPLAY environment variable, or use the " "--display command line argument"); return FALSE; } } g_assert (backend_x11->xdpy != NULL); CLUTTER_NOTE (BACKEND, "Getting the X screen"); /* add event filter for Cogl events */ clutter_x11_add_filter (cogl_xlib_filter, backend); if (clutter_screen == -1) backend_x11->xscreen = DefaultScreenOfDisplay (backend_x11->xdpy); else backend_x11->xscreen = ScreenOfDisplay (backend_x11->xdpy, clutter_screen); backend_x11->xscreen_num = XScreenNumberOfScreen (backend_x11->xscreen); backend_x11->xscreen_width = WidthOfScreen (backend_x11->xscreen); backend_x11->xscreen_height = HeightOfScreen (backend_x11->xscreen); backend_x11->xwin_root = RootWindow (backend_x11->xdpy, backend_x11->xscreen_num); backend_x11->display_name = g_strdup (clutter_display_name); /* create XSETTINGS client */ backend_x11->xsettings = _clutter_xsettings_client_new (backend_x11->xdpy, backend_x11->xscreen_num, clutter_backend_x11_xsettings_notify, NULL, backend_x11); /* add event filter for XSETTINGS events */ clutter_x11_add_filter (xsettings_filter, backend_x11); if (clutter_synchronise) XSynchronize (backend_x11->xdpy, True); XInternAtoms (backend_x11->xdpy, (char **) atom_names, N_ATOM_NAMES, False, atoms); backend_x11->atom_NET_WM_PID = atoms[0]; backend_x11->atom_NET_WM_PING = atoms[1]; backend_x11->atom_NET_WM_STATE = atoms[2]; backend_x11->atom_NET_WM_USER_TIME = atoms[3]; backend_x11->atom_WM_PROTOCOLS = atoms[4]; backend_x11->atom_WM_DELETE_WINDOW = atoms[5]; backend_x11->atom_XEMBED = atoms[6]; backend_x11->atom_XEMBED_INFO = atoms[7]; backend_x11->atom_NET_WM_NAME = atoms[8]; backend_x11->atom_UTF8_STRING = atoms[9]; g_free (clutter_display_name); CLUTTER_NOTE (BACKEND, "X Display '%s'[%p] opened (screen:%d, root:%u, dpi:%f)", backend_x11->display_name, backend_x11->xdpy, backend_x11->xscreen_num, (unsigned int) backend_x11->xwin_root, clutter_backend_get_resolution (backend)); return TRUE; } static const GOptionEntry entries[] = { { "display", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_STRING, &clutter_display_name, N_("X display to use"), "DISPLAY" }, { "screen", 0, G_OPTION_FLAG_IN_MAIN, G_OPTION_ARG_INT, &clutter_screen, N_("X screen to use"), "SCREEN" }, { "synch", 0, 0, G_OPTION_ARG_NONE, &clutter_synchronise, N_("Make X calls synchronous"), NULL }, { "disable-xinput", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &clutter_enable_xinput, N_("Disable XInput support"), NULL }, { NULL } }; static void clutter_backend_x11_add_options (ClutterBackend *backend, GOptionGroup *group) { g_option_group_add_entries (group, entries); } static void clutter_backend_x11_finalize (GObject *gobject) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (gobject); g_free (backend_x11->display_name); clutter_x11_remove_filter (cogl_xlib_filter, gobject); clutter_x11_remove_filter (xsettings_filter, backend_x11); _clutter_xsettings_client_destroy (backend_x11->xsettings); XCloseDisplay (backend_x11->xdpy); G_OBJECT_CLASS (clutter_backend_x11_parent_class)->finalize (gobject); } static void clutter_backend_x11_dispose (GObject *gobject) { G_OBJECT_CLASS (clutter_backend_x11_parent_class)->dispose (gobject); } static ClutterFeatureFlags clutter_backend_x11_get_features (ClutterBackend *backend) { ClutterFeatureFlags flags = CLUTTER_FEATURE_STAGE_CURSOR; flags |= CLUTTER_BACKEND_CLASS (clutter_backend_x11_parent_class)->get_features (backend); return flags; } static void update_last_event_time (ClutterBackendX11 *backend_x11, XEvent *xevent) { Time current_time = CurrentTime; Time last_time = backend_x11->last_event_time; switch (xevent->type) { case KeyPress: case KeyRelease: current_time = xevent->xkey.time; break; case ButtonPress: case ButtonRelease: current_time = xevent->xbutton.time; break; case MotionNotify: current_time = xevent->xmotion.time; break; case EnterNotify: case LeaveNotify: current_time = xevent->xcrossing.time; break; case PropertyNotify: current_time = xevent->xproperty.time; break; default: break; } /* only change the current event time if it's after the previous event * time, or if it is at least 30 seconds earlier - in case the system * clock was changed */ if ((current_time != CurrentTime) && (current_time > last_time || (last_time - current_time > (30 * 1000)))) backend_x11->last_event_time = current_time; } static gboolean clutter_backend_x11_translate_event (ClutterBackend *backend, gpointer native, ClutterEvent *event) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); XEvent *xevent = native; /* X11 filter functions have a higher priority */ if (backend_x11->event_filters != NULL) { GSList *node = backend_x11->event_filters; while (node != NULL) { ClutterX11EventFilter *filter = node->data; switch (filter->func (xevent, event, filter->data)) { case CLUTTER_X11_FILTER_CONTINUE: break; case CLUTTER_X11_FILTER_TRANSLATE: return TRUE; case CLUTTER_X11_FILTER_REMOVE: return FALSE; default: break; } node = node->next; } } /* we update the event time only for events that can * actually reach Clutter's event queue */ update_last_event_time (backend_x11, xevent); return FALSE; } static CoglRenderer * clutter_backend_x11_get_renderer (ClutterBackend *backend, GError **error) { ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (backend); Display *xdisplay = backend_x11->xdpy; CoglRenderer *renderer; CLUTTER_NOTE (BACKEND, "Creating a new Xlib renderer"); renderer = cogl_renderer_new (); cogl_renderer_add_constraint (renderer, COGL_RENDERER_CONSTRAINT_USES_X11); /* set the display object we're using */ cogl_xlib_renderer_set_foreign_display (renderer, xdisplay); return renderer; } static gboolean check_onscreen_template (CoglRenderer *renderer, CoglSwapChain *swap_chain, CoglOnscreenTemplate *onscreen_template, gboolean enable_argb, gboolean enable_stereo, GError **error) { GError *internal_error = NULL; cogl_swap_chain_set_has_alpha (swap_chain, enable_argb); cogl_onscreen_template_set_stereo_enabled (onscreen_template, clutter_enable_stereo); /* cogl_renderer_check_onscreen_template() is actually just a * shorthand for creating a CoglDisplay, and calling * cogl_display_setup() on it, then throwing the display away. If we * could just return that display, then it would be more efficient * not to use cogl_renderer_check_onscreen_template(). However, the * backend API requires that we return an CoglDisplay that has not * yet been setup, so one way or the other we'll have to discard the * first display and make a new fresh one. */ if (cogl_renderer_check_onscreen_template (renderer, onscreen_template, &internal_error)) { clutter_enable_argb = enable_argb; clutter_enable_stereo = enable_stereo; return TRUE; } else { if (enable_argb || enable_stereo) /* More possibilities to try */ CLUTTER_NOTE (BACKEND, "Creation of a CoglDisplay with alpha=%s, stereo=%s failed: %s", enable_argb ? "enabled" : "disabled", enable_stereo ? "enabled" : "disabled", internal_error != NULL ? internal_error->message : "Unknown reason"); else g_set_error_literal (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_BACKEND, internal_error != NULL ? internal_error->message : "Creation of a CoglDisplay failed"); g_clear_error (&internal_error); return FALSE; } } static CoglDisplay * clutter_backend_x11_get_display (ClutterBackend *backend, CoglRenderer *renderer, CoglSwapChain *swap_chain, GError **error) { CoglOnscreenTemplate *onscreen_template; CoglDisplay *display = NULL; gboolean res = FALSE; CLUTTER_NOTE (BACKEND, "Creating CoglDisplay, alpha=%s, stereo=%s", clutter_enable_argb ? "enabled" : "disabled", clutter_enable_stereo ? "enabled" : "disabled"); onscreen_template = cogl_onscreen_template_new (swap_chain); /* It's possible that the current renderer doesn't support transparency * or doesn't support stereo, so we try the different combinations. */ if (clutter_enable_argb && clutter_enable_stereo) res = check_onscreen_template (renderer, swap_chain, onscreen_template, TRUE, TRUE, error); /* Prioritize stereo over alpha */ if (!res && clutter_enable_stereo) res = check_onscreen_template (renderer, swap_chain, onscreen_template, FALSE, TRUE, error); if (!res && clutter_enable_argb) res = check_onscreen_template (renderer, swap_chain, onscreen_template, TRUE, FALSE, error); if (!res) res = check_onscreen_template (renderer, swap_chain, onscreen_template, FALSE, FALSE, error); if (res) display = cogl_display_new (renderer, onscreen_template); cogl_object_unref (onscreen_template); return display; } static void clutter_backend_x11_class_init (ClutterBackendX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterBackendClass *backend_class = CLUTTER_BACKEND_CLASS (klass); gobject_class->dispose = clutter_backend_x11_dispose; gobject_class->finalize = clutter_backend_x11_finalize; backend_class->pre_parse = clutter_backend_x11_pre_parse; backend_class->post_parse = clutter_backend_x11_post_parse; backend_class->add_options = clutter_backend_x11_add_options; backend_class->get_features = clutter_backend_x11_get_features; backend_class->translate_event = clutter_backend_x11_translate_event; backend_class->get_renderer = clutter_backend_x11_get_renderer; backend_class->get_display = clutter_backend_x11_get_display; } static void clutter_backend_x11_init (ClutterBackendX11 *backend_x11) { backend_x11->last_event_time = CurrentTime; } ClutterBackend * clutter_backend_x11_new (void) { return g_object_new (CLUTTER_TYPE_BACKEND_X11, NULL); } static int error_handler(Display *xdpy, XErrorEvent *error) { TrappedErrorCode = error->error_code; return 0; } /** * clutter_x11_trap_x_errors: * * Traps every X error until clutter_x11_untrap_x_errors() is called. * * Since: 0.6 */ void clutter_x11_trap_x_errors (void) { TrappedErrorCode = 0; old_error_handler = XSetErrorHandler (error_handler); } /** * clutter_x11_untrap_x_errors: * * Removes the X error trap and returns the current status. * * Return value: the trapped error code, or 0 for success * * Since: 0.4 */ gint clutter_x11_untrap_x_errors (void) { XSetErrorHandler (old_error_handler); return TrappedErrorCode; } /** * clutter_x11_get_default_display: * * Retrieves the pointer to the default display. * * Return value: (transfer none): the default display * * Since: 0.6 */ Display * clutter_x11_get_default_display (void) { ClutterBackend *backend = clutter_get_default_backend (); if (backend == NULL) { g_critical ("The Clutter backend has not been initialised"); return NULL; } if (!CLUTTER_IS_BACKEND_X11 (backend)) { g_critical ("The Clutter backend is not a X11 backend"); return NULL; } return CLUTTER_BACKEND_X11 (backend)->xdpy; } /** * clutter_x11_set_display: * @xdpy: pointer to a X display connection. * * Sets the display connection Clutter should use; must be called * before clutter_init(), clutter_init_with_args() or other functions * pertaining Clutter's initialization process. * * If you are parsing the command line arguments by retrieving Clutter's * #GOptionGroup with clutter_get_option_group() and calling * g_option_context_parse() yourself, you should also call * clutter_x11_set_display() before g_option_context_parse(). * * Since: 0.8 */ void clutter_x11_set_display (Display *xdpy) { if (_clutter_context_is_initialized ()) { g_warning ("%s() can only be used before calling clutter_init()", G_STRFUNC); return; } _foreign_dpy= xdpy; } /** * clutter_x11_get_default_screen: * * Gets the number of the default X Screen object. * * Return value: the number of the default screen * * Since: 0.6 */ int clutter_x11_get_default_screen (void) { ClutterBackend *backend = clutter_get_default_backend (); if (backend == NULL) { g_critical ("The Clutter backend has not been initialised"); return 0; } if (!CLUTTER_IS_BACKEND_X11 (backend)) { g_critical ("The Clutter backend is not a X11 backend"); return 0; } return CLUTTER_BACKEND_X11 (backend)->xscreen_num; } /** * clutter_x11_get_root_window: (skip) * * Retrieves the root window. * * Return value: the id of the root window * * Since: 0.6 */ Window clutter_x11_get_root_window (void) { ClutterBackend *backend = clutter_get_default_backend (); if (backend == NULL) { g_critical ("The Clutter backend has not been initialised"); return None; } if (!CLUTTER_IS_BACKEND_X11 (backend)) { g_critical ("The Clutter backend is not a X11 backend"); return None; } return CLUTTER_BACKEND_X11 (backend)->xwin_root; } /** * clutter_x11_add_filter: (skip) * @func: a filter function * @data: user data to be passed to the filter function, or %NULL * * Adds an event filter function. * * Since: 0.6 */ void clutter_x11_add_filter (ClutterX11FilterFunc func, gpointer data) { ClutterX11EventFilter *filter; ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11; g_return_if_fail (func != NULL); if (backend == NULL) { g_critical ("The Clutter backend has not been initialised"); return; } if (!CLUTTER_IS_BACKEND_X11 (backend)) { g_critical ("The Clutter backend is not a X11 backend"); return; } backend_x11 = CLUTTER_BACKEND_X11 (backend); filter = g_new0 (ClutterX11EventFilter, 1); filter->func = func; filter->data = data; backend_x11->event_filters = g_slist_append (backend_x11->event_filters, filter); return; } /** * clutter_x11_remove_filter: (skip) * @func: a filter function * @data: user data to be passed to the filter function, or %NULL * * Removes the given filter function. * * Since: 0.6 */ void clutter_x11_remove_filter (ClutterX11FilterFunc func, gpointer data) { GSList *tmp_list, *this; ClutterX11EventFilter *filter; ClutterBackend *backend = clutter_get_default_backend (); ClutterBackendX11 *backend_x11; g_return_if_fail (func != NULL); if (backend == NULL) { g_critical ("The Clutter backend has not been initialised"); return; } if (!CLUTTER_IS_BACKEND_X11 (backend)) { g_critical ("The Clutter backend is not a X11 backend"); return; } backend_x11 = CLUTTER_BACKEND_X11 (backend); tmp_list = backend_x11->event_filters; while (tmp_list) { filter = tmp_list->data; this = tmp_list; tmp_list = tmp_list->next; if (filter->func == func && filter->data == data) { backend_x11->event_filters = g_slist_remove_link (backend_x11->event_filters, this); g_slist_free_1 (this); g_free (filter); return; } } } /** * clutter_x11_has_composite_extension: * * Retrieves whether Clutter is running on an X11 server with the * XComposite extension * * Return value: %TRUE if the XComposite extension is available */ gboolean clutter_x11_has_composite_extension (void) { static gboolean have_composite = FALSE, done_check = FALSE; int error = 0, event = 0; Display *dpy; if (done_check) return have_composite; if (!_clutter_context_is_initialized ()) { g_critical ("X11 backend has not been initialised"); return FALSE; } dpy = clutter_x11_get_default_display(); if (dpy == NULL) return FALSE; if (XCompositeQueryExtension (dpy, &event, &error)) { int major = 0, minor = 0; if (XCompositeQueryVersion (dpy, &major, &minor)) { if (major >= 0 && minor >= 3) have_composite = TRUE; } } done_check = TRUE; return have_composite; } /** * clutter_x11_set_use_argb_visual: * @use_argb: %TRUE if ARGB visuals should be requested by default * * Sets whether the Clutter X11 backend should request ARGB visuals by default * or not. * * By default, Clutter requests RGB visuals. * * If no ARGB visuals are found, the X11 backend will fall back to * requesting a RGB visual instead. * * ARGB visuals are required for the #ClutterStage:use-alpha property to work. * * This function can only be called once, and before clutter_init() is * called. * * Since: 1.2 */ void clutter_x11_set_use_argb_visual (gboolean use_argb) { if (_clutter_context_is_initialized ()) { g_warning ("%s() can only be used before calling clutter_init()", G_STRFUNC); return; } CLUTTER_NOTE (BACKEND, "ARGB visuals are %s", use_argb ? "enabled" : "disabled"); clutter_enable_argb = use_argb; } /** * clutter_x11_get_use_argb_visual: * * Retrieves whether the Clutter X11 backend is using ARGB visuals by default * * Return value: %TRUE if ARGB visuals are queried by default * * Since: 1.2 */ gboolean clutter_x11_get_use_argb_visual (void) { return clutter_enable_argb; } /** * clutter_x11_set_use_stereo_stage: * @use_stereo: %TRUE if the stereo stages should be used if possible. * * Sets whether the backend object for Clutter stages, will, * if possible, be created with the ability to support stereo drawing * (drawing separate images for the left and right eyes). * * This function must be called before clutter_init() is called. * During paint callbacks, cogl_framebuffer_is_stereo() can be called * on the framebuffer retrieved by cogl_get_draw_framebuffer() to * determine if stereo support was successfully enabled, and * cogl_framebuffer_set_stereo_mode() to determine which buffers * will be drawn to. * * Note that this function *does not* cause the stage to be drawn * multiple times with different perspective transformations and thus * appear in 3D, it simply enables individual ClutterActors to paint * different images for the left and and right eye. * * Since: 1.22 */ void clutter_x11_set_use_stereo_stage (gboolean use_stereo) { if (_clutter_context_is_initialized ()) { g_warning ("%s() can only be used before calling clutter_init()", G_STRFUNC); return; } CLUTTER_NOTE (BACKEND, "STEREO stages are %s", use_stereo ? "enabled" : "disabled"); clutter_enable_stereo = use_stereo; } /** * clutter_x11_get_use_stereo_stage: * * Retrieves whether the Clutter X11 backend will create stereo * stages if possible. * * Return value: %TRUE if stereo stages are used if possible * * Since: 1.22 */ gboolean clutter_x11_get_use_stereo_stage (void) { return clutter_enable_stereo; } muffin-6.4.1/clutter/clutter/x11/clutter-backend-x11.h0000664000175000017500000000611514723361714021351 0ustar fabiofabio/* Clutter. * An OpenGL based 'interactive canvas' library. * Authored By Matthew Allum * Copyright (C) 2006-2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_BACKEND_X11_H__ #define __CLUTTER_BACKEND_X11_H__ #include #include #include #include #include "clutter-x11.h" #include "clutter-backend-private.h" #include "xsettings/xsettings-client.h" G_BEGIN_DECLS #define CLUTTER_TYPE_BACKEND_X11 (clutter_backend_x11_get_type ()) #define CLUTTER_BACKEND_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_X11, ClutterBackendX11)) #define CLUTTER_IS_BACKEND_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_X11)) #define CLUTTER_BACKEND_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_X11, ClutterBackendX11Class)) #define CLUTTER_IS_BACKEND_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_X11)) #define CLUTTER_BACKEND_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_X11, ClutterBackendX11Class)) typedef struct _ClutterBackendX11 ClutterBackendX11; typedef struct _ClutterBackendX11Class ClutterBackendX11Class; typedef struct _ClutterX11EventFilter ClutterX11EventFilter; G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBackendX11, g_object_unref) struct _ClutterX11EventFilter { ClutterX11FilterFunc func; gpointer data; }; struct _ClutterBackendX11 { ClutterBackend parent_instance; Display *xdpy; gchar *display_name; Screen *xscreen; int xscreen_num; int xscreen_width; int xscreen_height; Window xwin_root; /* event source */ GSList *event_filters; /* props */ Atom atom_NET_WM_PID; Atom atom_NET_WM_PING; Atom atom_NET_WM_STATE; Atom atom_NET_WM_USER_TIME; Atom atom_WM_PROTOCOLS; Atom atom_WM_DELETE_WINDOW; Atom atom_XEMBED; Atom atom_XEMBED_INFO; Atom atom_NET_WM_NAME; Atom atom_UTF8_STRING; Time last_event_time; XSettingsClient *xsettings; Window xsettings_xwin; }; struct _ClutterBackendX11Class { ClutterBackendClass parent_class; }; CLUTTER_EXPORT GType clutter_backend_x11_get_type (void) G_GNUC_CONST; ClutterBackend *clutter_backend_x11_new (void); /* Private to glx/eglx backends */ void _clutter_x11_select_events (Window xwin); G_END_DECLS #endif /* __CLUTTER_BACKEND_X11_H__ */ muffin-6.4.1/clutter/clutter/clutter-gesture-action-private.h0000664000175000017500000000174514723361714023347 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Collabora Ltd.. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_GESTURE_ACTION_PRIVATE_H__ #define __CLUTTER_GESTURE_ACTION_PRIVATE_H__ #include G_BEGIN_DECLS G_END_DECLS #endif /* __CLUTTER_GESTURE_ACTION_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-units.h0000664000175000017500000001417214723361714020106 0ustar fabiofabio/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Tomas Frydrych * Emmanuele Bassu * * Copyright (C) 2007, 2008 OpenedHand * Copyright (C) 2009 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_UNITS_H__ #define __CLUTTER_UNITS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS /** * ClutterUnits: * * An opaque structure, to be used to store sizing and positioning * values along with their unit. * * Since: 1.0 */ typedef struct _ClutterUnits ClutterUnits; struct _ClutterUnits { /*< private >*/ ClutterUnitType unit_type; gfloat value; /* pre-filled by the provided constructors */ /* cached pixel value */ gfloat pixels; /* whether the :pixels field is set */ guint pixels_set; /* the serial coming from the backend, used to evict the cache */ gint32 serial; /* padding for eventual expansion */ gint32 __padding_1; gint64 __padding_2; }; CLUTTER_EXPORT GType clutter_units_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterUnitType clutter_units_get_unit_type (const ClutterUnits *units); CLUTTER_EXPORT gfloat clutter_units_get_unit_value (const ClutterUnits *units); CLUTTER_EXPORT ClutterUnits * clutter_units_copy (const ClutterUnits *units); CLUTTER_EXPORT void clutter_units_free (ClutterUnits *units); CLUTTER_EXPORT void clutter_units_from_pixels (ClutterUnits *units, gint px); CLUTTER_EXPORT void clutter_units_from_em (ClutterUnits *units, gfloat em); CLUTTER_EXPORT void clutter_units_from_em_for_font (ClutterUnits *units, const gchar *font_name, gfloat em); CLUTTER_EXPORT void clutter_units_from_mm (ClutterUnits *units, gfloat mm); CLUTTER_EXPORT void clutter_units_from_cm (ClutterUnits *units, gfloat cm); CLUTTER_EXPORT void clutter_units_from_pt (ClutterUnits *units, gfloat pt); CLUTTER_EXPORT gfloat clutter_units_to_pixels (ClutterUnits *units); CLUTTER_EXPORT gboolean clutter_units_from_string (ClutterUnits *units, const gchar *str); CLUTTER_EXPORT gchar * clutter_units_to_string (const ClutterUnits *units); /* shorthands for the constructors */ #define clutter_units_pixels clutter_units_from_pixels #define clutter_units_em clutter_units_from_em #define clutter_units_em_for_font clutter_units_from_em_for_font #define clutter_units_mm clutter_units_from_mm #define clutter_units_cm clutter_units_from_cm #define clutter_units_pt clutter_units_from_pt #define CLUTTER_TYPE_UNITS (clutter_units_get_type ()) #define CLUTTER_TYPE_PARAM_UNITS (clutter_param_units_get_type ()) #define CLUTTER_PARAM_SPEC_UNITS(pspec) (G_TYPE_CHECK_INSTANCE_CAST ((pspec), CLUTTER_TYPE_PARAM_UNITS, ClutterParamSpecUnits)) #define CLUTTER_IS_PARAM_SPEC_UNITS(pspec) (G_TYPE_CHECK_INSTANCE_TYPE ((pspec), CLUTTER_TYPE_PARAM_UNITS)) /** * CLUTTER_VALUE_HOLDS_UNITS: * @x: a #GValue * * Evaluates to %TRUE if @x holds a #ClutterUnits value * * Since: 0.8 */ #define CLUTTER_VALUE_HOLDS_UNITS(x) (G_VALUE_HOLDS ((x), CLUTTER_TYPE_UNITS)) typedef struct _ClutterParamSpecUnits ClutterParamSpecUnits; /** * ClutterParamSpecUnits: (skip) * @default_type: default type * @default_value: default value * @minimum: lower boundary * @maximum: higher boundary * * #GParamSpec subclass for unit based properties. * * Since: 1.0 */ struct _ClutterParamSpecUnits { /*< private >*/ GParamSpec parent_instance; /*< public >*/ ClutterUnitType default_type; gfloat default_value; gfloat minimum; gfloat maximum; }; CLUTTER_EXPORT GType clutter_param_units_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT GParamSpec * clutter_param_spec_units (const gchar *name, const gchar *nick, const gchar *blurb, ClutterUnitType default_type, gfloat minimum, gfloat maximum, gfloat default_value, GParamFlags flags); CLUTTER_EXPORT void clutter_value_set_units (GValue *value, const ClutterUnits *units); CLUTTER_EXPORT const ClutterUnits * clutter_value_get_units (const GValue *value); G_END_DECLS #endif /* __CLUTTER_UNITS_H__ */ muffin-6.4.1/clutter/clutter/clutter-text-buffer.c0000664000175000017500000005462214723361714021176 0ustar fabiofabio/* clutter-text-buffer.c * Copyright (C) 2011 Collabora Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. * * Author: Stef Walter */ #include "clutter-build-config.h" #include "clutter-text-buffer.h" #include "clutter-marshal.h" #include "clutter-private.h" #include /** * SECTION:clutter-text-buffer * @title: ClutterTextBuffer * @short_description: Text buffer for ClutterText * * The #ClutterTextBuffer class contains the actual text displayed in a * #ClutterText widget. * * A single #ClutterTextBuffer object can be shared by multiple #ClutterText * widgets which will then share the same text content, but not the cursor * position, visibility attributes, icon etc. * * #ClutterTextBuffer may be derived from. Such a derived class might allow * text to be stored in an alternate location, such as non-pageable memory, * useful in the case of important passwords. Or a derived class could * integrate with an application's concept of undo/redo. * * Since: 1.10 */ /* Initial size of buffer, in bytes */ #define MIN_SIZE 16 enum { PROP_0, PROP_TEXT, PROP_LENGTH, PROP_MAX_LENGTH, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; enum { INSERTED_TEXT, DELETED_TEXT, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; struct _ClutterTextBufferPrivate { gint max_length; /* Only valid if this class is not derived */ gchar *normal_text; gsize normal_text_size; gsize normal_text_bytes; guint normal_text_chars; }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterTextBuffer, clutter_text_buffer, G_TYPE_OBJECT) /* -------------------------------------------------------------------------------- * DEFAULT IMPLEMENTATIONS OF TEXT BUFFER * * These may be overridden by a derived class, behavior may be changed etc... * The normal_text and normal_text_xxxx fields may not be valid when * this class is derived from. */ /* Overwrite a memory that might contain sensitive information. */ static void trash_area (gchar *area, gsize len) { volatile gchar *varea = (volatile gchar *)area; while (len-- > 0) *varea++ = 0; } static const gchar* clutter_text_buffer_normal_get_text (ClutterTextBuffer *buffer, gsize *n_bytes) { if (n_bytes) *n_bytes = buffer->priv->normal_text_bytes; if (!buffer->priv->normal_text) return ""; return buffer->priv->normal_text; } static guint clutter_text_buffer_normal_get_length (ClutterTextBuffer *buffer) { return buffer->priv->normal_text_chars; } static guint clutter_text_buffer_normal_insert_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars) { ClutterTextBufferPrivate *pv = buffer->priv; gsize prev_size; gsize n_bytes; gsize at; n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars; /* Need more memory */ if (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size) { gchar *et_new; prev_size = pv->normal_text_size; /* Calculate our new buffer size */ while (n_bytes + pv->normal_text_bytes + 1 > pv->normal_text_size) { if (pv->normal_text_size == 0) pv->normal_text_size = MIN_SIZE; else { if (2 * pv->normal_text_size < CLUTTER_TEXT_BUFFER_MAX_SIZE) pv->normal_text_size *= 2; else { pv->normal_text_size = CLUTTER_TEXT_BUFFER_MAX_SIZE; if (n_bytes > pv->normal_text_size - pv->normal_text_bytes - 1) { n_bytes = pv->normal_text_size - pv->normal_text_bytes - 1; n_bytes = g_utf8_find_prev_char (chars, chars + n_bytes + 1) - chars; n_chars = g_utf8_strlen (chars, n_bytes); } break; } } } /* Could be a password, so can't leave stuff in memory. */ et_new = g_malloc (pv->normal_text_size); memcpy (et_new, pv->normal_text, MIN (prev_size, pv->normal_text_size)); trash_area (pv->normal_text, prev_size); g_free (pv->normal_text); pv->normal_text = et_new; } /* Actual text insertion */ at = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text; memmove (pv->normal_text + at + n_bytes, pv->normal_text + at, pv->normal_text_bytes - at); memcpy (pv->normal_text + at, chars, n_bytes); /* Book keeping */ pv->normal_text_bytes += n_bytes; pv->normal_text_chars += n_chars; pv->normal_text[pv->normal_text_bytes] = '\0'; clutter_text_buffer_emit_inserted_text (buffer, position, chars, n_chars); return n_chars; } static guint clutter_text_buffer_normal_delete_text (ClutterTextBuffer *buffer, guint position, guint n_chars) { ClutterTextBufferPrivate *pv = buffer->priv; gsize start, end; if (position > pv->normal_text_chars) position = pv->normal_text_chars; if (position + n_chars > pv->normal_text_chars) n_chars = pv->normal_text_chars - position; if (n_chars > 0) { start = g_utf8_offset_to_pointer (pv->normal_text, position) - pv->normal_text; end = g_utf8_offset_to_pointer (pv->normal_text, position + n_chars) - pv->normal_text; memmove (pv->normal_text + start, pv->normal_text + end, pv->normal_text_bytes + 1 - end); pv->normal_text_chars -= n_chars; pv->normal_text_bytes -= (end - start); /* * Could be a password, make sure we don't leave anything sensitive after * the terminating zero. Note, that the terminating zero already trashed * one byte. */ trash_area (pv->normal_text + pv->normal_text_bytes + 1, end - start - 1); clutter_text_buffer_emit_deleted_text (buffer, position, n_chars); } return n_chars; } /* -------------------------------------------------------------------------------- * */ static void clutter_text_buffer_real_inserted_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars) { g_object_notify_by_pspec (G_OBJECT (buffer), obj_props[PROP_TEXT]); g_object_notify_by_pspec (G_OBJECT (buffer), obj_props[PROP_LENGTH]); } static void clutter_text_buffer_real_deleted_text (ClutterTextBuffer *buffer, guint position, guint n_chars) { g_object_notify_by_pspec (G_OBJECT (buffer), obj_props[PROP_TEXT]); g_object_notify_by_pspec (G_OBJECT (buffer), obj_props[PROP_LENGTH]); } /* -------------------------------------------------------------------------------- * */ static void clutter_text_buffer_init (ClutterTextBuffer *self) { self->priv = clutter_text_buffer_get_instance_private (self); self->priv->normal_text = NULL; self->priv->normal_text_chars = 0; self->priv->normal_text_bytes = 0; self->priv->normal_text_size = 0; } static void clutter_text_buffer_finalize (GObject *obj) { ClutterTextBuffer *buffer = CLUTTER_TEXT_BUFFER (obj); ClutterTextBufferPrivate *pv = buffer->priv; if (pv->normal_text) { trash_area (pv->normal_text, pv->normal_text_size); g_free (pv->normal_text); pv->normal_text = NULL; pv->normal_text_bytes = pv->normal_text_size = 0; pv->normal_text_chars = 0; } G_OBJECT_CLASS (clutter_text_buffer_parent_class)->finalize (obj); } static void clutter_text_buffer_set_property (GObject *obj, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterTextBuffer *buffer = CLUTTER_TEXT_BUFFER (obj); switch (prop_id) { case PROP_MAX_LENGTH: clutter_text_buffer_set_max_length (buffer, g_value_get_int (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void clutter_text_buffer_get_property (GObject *obj, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterTextBuffer *buffer = CLUTTER_TEXT_BUFFER (obj); switch (prop_id) { case PROP_TEXT: g_value_set_string (value, clutter_text_buffer_get_text (buffer)); break; case PROP_LENGTH: g_value_set_uint (value, clutter_text_buffer_get_length (buffer)); break; case PROP_MAX_LENGTH: g_value_set_int (value, clutter_text_buffer_get_max_length (buffer)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (obj, prop_id, pspec); break; } } static void clutter_text_buffer_class_init (ClutterTextBufferClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = clutter_text_buffer_finalize; gobject_class->set_property = clutter_text_buffer_set_property; gobject_class->get_property = clutter_text_buffer_get_property; klass->get_text = clutter_text_buffer_normal_get_text; klass->get_length = clutter_text_buffer_normal_get_length; klass->insert_text = clutter_text_buffer_normal_insert_text; klass->delete_text = clutter_text_buffer_normal_delete_text; klass->inserted_text = clutter_text_buffer_real_inserted_text; klass->deleted_text = clutter_text_buffer_real_deleted_text; /** * ClutterTextBuffer:text: * * The contents of the buffer. * * Since: 1.10 */ obj_props[PROP_TEXT] = g_param_spec_string ("text", P_("Text"), P_("The contents of the buffer"), "", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * ClutterTextBuffer:length: * * The length (in characters) of the text in buffer. * * Since: 1.10 */ obj_props[PROP_LENGTH] = g_param_spec_uint ("length", P_("Text length"), P_("Length of the text currently in the buffer"), 0, CLUTTER_TEXT_BUFFER_MAX_SIZE, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); /** * ClutterTextBuffer:max-length: * * The maximum length (in characters) of the text in the buffer. * * Since: 1.10 */ obj_props[PROP_MAX_LENGTH] = g_param_spec_int ("max-length", P_("Maximum length"), P_("Maximum number of characters for this entry. Zero if no maximum"), 0, CLUTTER_TEXT_BUFFER_MAX_SIZE, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); /** * ClutterTextBuffer::inserted-text: * @buffer: a #ClutterTextBuffer * @position: the position the text was inserted at. * @chars: The text that was inserted. * @n_chars: The number of characters that were inserted. * * This signal is emitted after text is inserted into the buffer. * * Since: 1.10 */ signals[INSERTED_TEXT] = g_signal_new (I_("inserted-text"), CLUTTER_TYPE_TEXT_BUFFER, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterTextBufferClass, inserted_text), NULL, NULL, _clutter_marshal_VOID__UINT_STRING_UINT, G_TYPE_NONE, 3, G_TYPE_UINT, G_TYPE_STRING, G_TYPE_UINT); /** * ClutterTextBuffer::deleted-text: * @buffer: a #ClutterTextBuffer * @position: the position the text was deleted at. * @n_chars: The number of characters that were deleted. * * This signal is emitted after text is deleted from the buffer. * * Since: 1.10 */ signals[DELETED_TEXT] = g_signal_new (I_("deleted-text"), CLUTTER_TYPE_TEXT_BUFFER, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterTextBufferClass, deleted_text), NULL, NULL, _clutter_marshal_VOID__UINT_UINT, G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT); } /* -------------------------------------------------------------------------------- * */ /** * clutter_text_buffer_new: * * Create a new ClutterTextBuffer object. * * Return value: A new ClutterTextBuffer object. * * Since: 1.10 **/ ClutterTextBuffer* clutter_text_buffer_new (void) { return g_object_new (CLUTTER_TYPE_TEXT_BUFFER, NULL); } /** * clutter_text_buffer_new_with_text: * @text: (allow-none): initial buffer text * @text_len: initial buffer text length, or -1 for null-terminated. * * Create a new ClutterTextBuffer object with some text. * * Return value: A new ClutterTextBuffer object. * * Since: 1.10 **/ ClutterTextBuffer* clutter_text_buffer_new_with_text (const gchar *text, gssize text_len) { ClutterTextBuffer *buffer; buffer = clutter_text_buffer_new (); clutter_text_buffer_set_text (buffer, text, text_len); return buffer; } /** * clutter_text_buffer_get_length: * @buffer: a #ClutterTextBuffer * * Retrieves the length in characters of the buffer. * * Return value: The number of characters in the buffer. * * Since: 1.10 **/ guint clutter_text_buffer_get_length (ClutterTextBuffer *buffer) { ClutterTextBufferClass *klass; g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), 0); klass = CLUTTER_TEXT_BUFFER_GET_CLASS (buffer); g_return_val_if_fail (klass->get_length != NULL, 0); return (*klass->get_length) (buffer); } /** * clutter_text_buffer_get_bytes: * @buffer: a #ClutterTextBuffer * * Retrieves the length in bytes of the buffer. * See clutter_text_buffer_get_length(). * * Return value: The byte length of the buffer. * * Since: 1.10 **/ gsize clutter_text_buffer_get_bytes (ClutterTextBuffer *buffer) { ClutterTextBufferClass *klass; gsize bytes = 0; g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), 0); klass = CLUTTER_TEXT_BUFFER_GET_CLASS (buffer); g_return_val_if_fail (klass->get_text != NULL, 0); (*klass->get_text) (buffer, &bytes); return bytes; } /** * clutter_text_buffer_get_text: * @buffer: a #ClutterTextBuffer * * Retrieves the contents of the buffer. * * The memory pointer returned by this call will not change * unless this object emits a signal, or is finalized. * * Return value: a pointer to the contents of the widget as a * string. This string points to internally allocated * storage in the buffer and must not be freed, modified or * stored. * * Since: 1.10 **/ const gchar* clutter_text_buffer_get_text (ClutterTextBuffer *buffer) { ClutterTextBufferClass *klass; g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL); klass = CLUTTER_TEXT_BUFFER_GET_CLASS (buffer); g_return_val_if_fail (klass->get_text != NULL, NULL); return (*klass->get_text) (buffer, NULL); } /** * clutter_text_buffer_set_text: * @buffer: a #ClutterTextBuffer * @chars: the new text * @n_chars: the number of characters in @text, or -1 * * Sets the text in the buffer. * * This is roughly equivalent to calling clutter_text_buffer_delete_text() * and clutter_text_buffer_insert_text(). * * Note that @n_chars is in characters, not in bytes. * * Since: 1.10 **/ void clutter_text_buffer_set_text (ClutterTextBuffer *buffer, const gchar *chars, gint n_chars) { g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer)); g_return_if_fail (chars != NULL); g_object_freeze_notify (G_OBJECT (buffer)); clutter_text_buffer_delete_text (buffer, 0, -1); clutter_text_buffer_insert_text (buffer, 0, chars, n_chars); g_object_thaw_notify (G_OBJECT (buffer)); } /** * clutter_text_buffer_set_max_length: * @buffer: a #ClutterTextBuffer * @max_length: the maximum length of the entry buffer, or 0 for no maximum. * (other than the maximum length of entries.) The value passed in will * be clamped to the range [ 0, %CLUTTER_TEXT_BUFFER_MAX_SIZE ]. * * Sets the maximum allowed length of the contents of the buffer. If * the current contents are longer than the given length, then they * will be truncated to fit. * * Since: 1.10 **/ void clutter_text_buffer_set_max_length (ClutterTextBuffer *buffer, gint max_length) { g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer)); max_length = CLAMP (max_length, 0, CLUTTER_TEXT_BUFFER_MAX_SIZE); if (max_length > 0 && clutter_text_buffer_get_length (buffer) > max_length) clutter_text_buffer_delete_text (buffer, max_length, -1); buffer->priv->max_length = max_length; g_object_notify_by_pspec (G_OBJECT (buffer), obj_props[PROP_MAX_LENGTH]); } /** * clutter_text_buffer_get_max_length: * @buffer: a #ClutterTextBuffer * * Retrieves the maximum allowed length of the text in * @buffer. See clutter_text_buffer_set_max_length(). * * Return value: the maximum allowed number of characters * in #ClutterTextBuffer, or 0 if there is no maximum. * * Since: 1.10 */ gint clutter_text_buffer_get_max_length (ClutterTextBuffer *buffer) { g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), 0); return buffer->priv->max_length; } /** * clutter_text_buffer_insert_text: * @buffer: a #ClutterTextBuffer * @position: the position at which to insert text. * @chars: the text to insert into the buffer. * @n_chars: the length of the text in characters, or -1 * * Inserts @n_chars characters of @chars into the contents of the * buffer, at position @position. * * If @n_chars is negative, then characters from chars will be inserted * until a null-terminator is found. If @position or @n_chars are out of * bounds, or the maximum buffer text length is exceeded, then they are * coerced to sane values. * * Note that the position and length are in characters, not in bytes. * * Returns: The number of characters actually inserted. * * Since: 1.10 */ guint clutter_text_buffer_insert_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, gint n_chars) { ClutterTextBufferClass *klass; ClutterTextBufferPrivate *pv; guint length; g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), 0); length = clutter_text_buffer_get_length (buffer); pv = buffer->priv; if (n_chars < 0) n_chars = g_utf8_strlen (chars, -1); /* Bring position into bounds */ if (position > length) position = length; /* Make sure not entering too much data */ if (pv->max_length > 0) { if (length >= pv->max_length) n_chars = 0; else if (length + n_chars > pv->max_length) n_chars -= (length + n_chars) - pv->max_length; } klass = CLUTTER_TEXT_BUFFER_GET_CLASS (buffer); g_return_val_if_fail (klass->insert_text != NULL, 0); return (klass->insert_text) (buffer, position, chars, n_chars); } /** * clutter_text_buffer_delete_text: * @buffer: a #ClutterTextBuffer * @position: position at which to delete text * @n_chars: number of characters to delete * * Deletes a sequence of characters from the buffer. @n_chars characters are * deleted starting at @position. If @n_chars is negative, then all characters * until the end of the text are deleted. * * If @position or @n_chars are out of bounds, then they are coerced to sane * values. * * Note that the positions are specified in characters, not bytes. * * Returns: The number of characters deleted. * * Since: 1.10 */ guint clutter_text_buffer_delete_text (ClutterTextBuffer *buffer, guint position, gint n_chars) { ClutterTextBufferClass *klass; guint length; g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), 0); length = clutter_text_buffer_get_length (buffer); if (n_chars < 0) n_chars = length; if (position > length) position = length; if (position + n_chars > length) n_chars = length - position; klass = CLUTTER_TEXT_BUFFER_GET_CLASS (buffer); g_return_val_if_fail (klass->delete_text != NULL, 0); return (klass->delete_text) (buffer, position, n_chars); } /** * clutter_text_buffer_emit_inserted_text: * @buffer: a #ClutterTextBuffer * @position: position at which text was inserted * @chars: text that was inserted * @n_chars: number of characters inserted * * Emits the #ClutterTextBuffer::inserted-text signal on @buffer. * * Used when subclassing #ClutterTextBuffer * * Since: 1.10 */ void clutter_text_buffer_emit_inserted_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars) { g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer)); g_signal_emit (buffer, signals[INSERTED_TEXT], 0, position, chars, n_chars); } /** * clutter_text_buffer_emit_deleted_text: * @buffer: a #ClutterTextBuffer * @position: position at which text was deleted * @n_chars: number of characters deleted * * Emits the #ClutterTextBuffer::deleted-text signal on @buffer. * * Used when subclassing #ClutterTextBuffer * * Since: 1.10 */ void clutter_text_buffer_emit_deleted_text (ClutterTextBuffer *buffer, guint position, guint n_chars) { g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer)); g_signal_emit (buffer, signals[DELETED_TEXT], 0, position, n_chars); } muffin-6.4.1/clutter/clutter/clutter-actor.h0000664000175000017500000016437114723361714020063 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_ACTOR_H__ #define __CLUTTER_ACTOR_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif /* clutter-actor.h */ #include #include #include #include #include #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_ACTOR (clutter_actor_get_type ()) #define CLUTTER_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ACTOR, ClutterActor)) #define CLUTTER_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ACTOR, ClutterActorClass)) #define CLUTTER_IS_ACTOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ACTOR)) #define CLUTTER_IS_ACTOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ACTOR)) #define CLUTTER_ACTOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ACTOR, ClutterActorClass)) /** * CLUTTER_ACTOR_SET_FLAGS: * @a: a #ClutterActor * @f: the #ClutterActorFlags to set * * Sets the given flags on a #ClutterActor * * Deprecated: 1.24: Changing flags directly is heavily discouraged in * newly written code. #ClutterActor will take care of setting the * internal state. */ #define CLUTTER_ACTOR_SET_FLAGS(a,f) \ CLUTTER_MACRO_DEPRECATED \ (((ClutterActor*)(a))->flags |= (f)) /** * CLUTTER_ACTOR_UNSET_FLAGS: * @a: a #ClutterActor * @f: the #ClutterActorFlags to unset * * Unsets the given flags on a #ClutterActor * * Deprecated: 1.24: Changing flags directly is heavily discouraged in * newly written code. #ClutterActor will take care of unsetting the * internal state. */ #define CLUTTER_ACTOR_UNSET_FLAGS(a,f) \ CLUTTER_MACRO_DEPRECATED \ (((ClutterActor*)(a))->flags &= ~(f)) #define CLUTTER_ACTOR_IS_MAPPED(a) \ CLUTTER_MACRO_DEPRECATED_FOR ("Deprecated macro. Use clutter_actor_is_mapped instead") \ ((((ClutterActor*)(a))->flags & CLUTTER_ACTOR_MAPPED) != FALSE) #define CLUTTER_ACTOR_IS_REALIZED(a) \ CLUTTER_MACRO_DEPRECATED_FOR ("Deprecated macro. Use clutter_actor_is_realized instead") \ ((((ClutterActor*)(a))->flags & CLUTTER_ACTOR_REALIZED) != FALSE) #define CLUTTER_ACTOR_IS_VISIBLE(a) \ CLUTTER_MACRO_DEPRECATED_FOR ("Deprecated macro. Use clutter_actor_is_visible instead") \ ((((ClutterActor*)(a))->flags & CLUTTER_ACTOR_VISIBLE) != FALSE) #define CLUTTER_ACTOR_IS_REACTIVE(a) \ CLUTTER_MACRO_DEPRECATED_FOR ("Deprecated macro. Use clutter_actor_get_reactive instead") \ ((((ClutterActor*)(a))->flags & CLUTTER_ACTOR_REACTIVE) != FALSE) typedef struct _ClutterActorClass ClutterActorClass; typedef struct _ClutterActorPrivate ClutterActorPrivate; /** * ClutterCallback: * @actor: a #ClutterActor * @data: (closure): user data * * Generic callback */ typedef void (*ClutterCallback) (ClutterActor *actor, gpointer data); /** * CLUTTER_CALLBACK: * @f: a function * * Convenience macro to cast a function to #ClutterCallback */ #define CLUTTER_CALLBACK(f) ((ClutterCallback) (f)) /** * ClutterActor: * @flags: #ClutterActorFlags * * Base class for actors. */ struct _ClutterActor { /*< private >*/ GInitiallyUnowned parent_instance; /*< public >*/ guint32 flags; /*< private >*/ guint32 private_flags; ClutterActorPrivate *priv; }; /** * ClutterActorClass: * @show: signal class handler for #ClutterActor::show; it must chain * up to the parent's implementation * @show_all: virtual function for containers and composite actors, to * determine which children should be shown when calling * clutter_actor_show_all() on the actor. Defaults to calling * clutter_actor_show(). This virtual function is deprecated and it * should not be overridden. * @hide: signal class handler for #ClutterActor::hide; it must chain * up to the parent's implementation * @hide_all: virtual function for containers and composite actors, to * determine which children should be shown when calling * clutter_actor_hide_all() on the actor. Defaults to calling * clutter_actor_hide(). This virtual function is deprecated and it * should not be overridden. * @realize: virtual function, used to allocate resources for the actor; * it should chain up to the parent's implementation. This virtual * function is deprecated and should not be overridden in newly * written code. * @unrealize: virtual function, used to deallocate resources allocated * in ::realize; it should chain up to the parent's implementation. This * function is deprecated and should not be overridden in newly * written code. * @map: virtual function for containers and composite actors, to * map their children; it must chain up to the parent's implementation. * Overriding this function is optional. * @unmap: virtual function for containers and composite actors, to * unmap their children; it must chain up to the parent's implementation. * Overriding this function is optional. * @paint: virtual function, used to paint the actor * @get_preferred_width: virtual function, used when querying the minimum * and natural widths of an actor for a given height; it is used by * clutter_actor_get_preferred_width() * @get_preferred_height: virtual function, used when querying the minimum * and natural heights of an actor for a given width; it is used by * clutter_actor_get_preferred_height() * @allocate: virtual function, used when settings the coordinates of an * actor; it is used by clutter_actor_allocate(); it must chain up to * the parent's implementation, or call clutter_actor_set_allocation() * @apply_transform: virtual function, used when applying the transformations * to an actor before painting it or when transforming coordinates or * the allocation; it must chain up to the parent's implementation * @parent_set: signal class handler for the #ClutterActor::parent-set * @destroy: signal class handler for #ClutterActor::destroy. It must * chain up to the parent's implementation * @pick: virtual function, used to draw an outline of the actor with * the given color * @queue_redraw: class handler for #ClutterActor::queue-redraw * @event: class handler for #ClutterActor::event * @button_press_event: class handler for #ClutterActor::button-press-event * @button_release_event: class handler for * #ClutterActor::button-release-event * @scroll_event: signal class closure for #ClutterActor::scroll-event * @key_press_event: signal class closure for #ClutterActor::key-press-event * @key_release_event: signal class closure for * #ClutterActor::key-release-event * @motion_event: signal class closure for #ClutterActor::motion-event * @enter_event: signal class closure for #ClutterActor::enter-event * @leave_event: signal class closure for #ClutterActor::leave-event * @captured_event: signal class closure for #ClutterActor::captured-event * @key_focus_in: signal class closure for #ClutterActor::key-focus-in * @key_focus_out: signal class closure for #ClutterActor::key-focus-out * @queue_relayout: class handler for #ClutterActor::queue-relayout * @get_accessible: virtual function, returns the accessible object that * describes the actor to an assistive technology. * @get_paint_volume: virtual function, for sub-classes to define their * #ClutterPaintVolume * @has_overlaps: virtual function for * sub-classes to advertise whether they need an offscreen redirect * to get the correct opacity. See * clutter_actor_set_offscreen_redirect() for details. * @paint_node: virtual function for creating paint nodes and attaching * them to the render tree * @touch_event: signal class closure for #ClutterActor::touch-event * * Base class for actors. */ struct _ClutterActorClass { /*< private >*/ GInitiallyUnownedClass parent_class; /*< public >*/ void (* show) (ClutterActor *self); void (* show_all) (ClutterActor *self); void (* hide) (ClutterActor *self); void (* hide_all) (ClutterActor *self); void (* realize) (ClutterActor *self); void (* unrealize) (ClutterActor *self); void (* map) (ClutterActor *self); void (* unmap) (ClutterActor *self); void (* paint) (ClutterActor *self, ClutterPaintContext *paint_context); void (* parent_set) (ClutterActor *actor, ClutterActor *old_parent); void (* destroy) (ClutterActor *self); void (* pick) (ClutterActor *actor, ClutterPickContext *pick_context); gboolean (* queue_redraw) (ClutterActor *actor, ClutterActor *leaf_that_queued, ClutterPaintVolume *paint_volume); /* size negotiation */ void (* get_preferred_width) (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p); void (* get_preferred_height) (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p); void (* allocate) (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags); /* transformations */ void (* apply_transform) (ClutterActor *actor, ClutterMatrix *matrix); /* event signals */ gboolean (* event) (ClutterActor *actor, ClutterEvent *event); gboolean (* button_press_event) (ClutterActor *actor, ClutterButtonEvent *event); gboolean (* button_release_event) (ClutterActor *actor, ClutterButtonEvent *event); gboolean (* scroll_event) (ClutterActor *actor, ClutterScrollEvent *event); gboolean (* key_press_event) (ClutterActor *actor, ClutterKeyEvent *event); gboolean (* key_release_event) (ClutterActor *actor, ClutterKeyEvent *event); gboolean (* motion_event) (ClutterActor *actor, ClutterMotionEvent *event); gboolean (* enter_event) (ClutterActor *actor, ClutterCrossingEvent *event); gboolean (* leave_event) (ClutterActor *actor, ClutterCrossingEvent *event); gboolean (* captured_event) (ClutterActor *actor, ClutterEvent *event); void (* key_focus_in) (ClutterActor *actor); void (* key_focus_out) (ClutterActor *actor); void (* queue_relayout) (ClutterActor *self); /* accessibility support */ AtkObject * (* get_accessible) (ClutterActor *self); gboolean (* get_paint_volume) (ClutterActor *actor, ClutterPaintVolume *volume); gboolean (* has_overlaps) (ClutterActor *self); void (* paint_node) (ClutterActor *self, ClutterPaintNode *root); gboolean (* touch_event) (ClutterActor *self, ClutterTouchEvent *event); gboolean (* has_accessible) (ClutterActor *self); /*< private >*/ /* padding for future expansion */ gpointer _padding_dummy[25]; }; /** * ClutterActorIter: * * An iterator structure that allows to efficiently iterate over a * section of the scene graph. * * The contents of the #ClutterActorIter structure * are private and should only be accessed using the provided API. * * Since: 1.10 */ struct _ClutterActorIter { /*< private >*/ gpointer CLUTTER_PRIVATE_FIELD (dummy1); gpointer CLUTTER_PRIVATE_FIELD (dummy2); gpointer CLUTTER_PRIVATE_FIELD (dummy3); gint CLUTTER_PRIVATE_FIELD (dummy4); gpointer CLUTTER_PRIVATE_FIELD (dummy5); }; CLUTTER_EXPORT GType clutter_actor_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_actor_new (void); CLUTTER_EXPORT void clutter_actor_set_flags (ClutterActor *self, ClutterActorFlags flags); CLUTTER_EXPORT void clutter_actor_unset_flags (ClutterActor *self, ClutterActorFlags flags); CLUTTER_EXPORT ClutterActorFlags clutter_actor_get_flags (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_show (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_hide (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_realize (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_unrealize (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_map (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_unmap (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_paint (ClutterActor *self, ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_actor_continue_paint (ClutterActor *self, ClutterPaintContext *paint_context); CLUTTER_EXPORT void clutter_actor_pick (ClutterActor *actor, ClutterPickContext *pick_context); CLUTTER_EXPORT void clutter_actor_continue_pick (ClutterActor *actor, ClutterPickContext *pick_context); CLUTTER_EXPORT void clutter_actor_queue_redraw (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_queue_redraw_with_clip (ClutterActor *self, const cairo_rectangle_int_t *clip); CLUTTER_EXPORT void clutter_actor_queue_relayout (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_destroy (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_name (ClutterActor *self, const gchar *name); CLUTTER_EXPORT const gchar * clutter_actor_get_name (ClutterActor *self); CLUTTER_EXPORT AtkObject * clutter_actor_get_accessible (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_accessible (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_is_visible (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_is_mapped (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_is_realized (ClutterActor *self); /* Size negotiation */ CLUTTER_EXPORT void clutter_actor_set_request_mode (ClutterActor *self, ClutterRequestMode mode); CLUTTER_EXPORT ClutterRequestMode clutter_actor_get_request_mode (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p); CLUTTER_EXPORT void clutter_actor_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p); CLUTTER_EXPORT void clutter_actor_get_preferred_size (ClutterActor *self, gfloat *min_width_p, gfloat *min_height_p, gfloat *natural_width_p, gfloat *natural_height_p); CLUTTER_EXPORT void clutter_actor_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_actor_allocate_preferred_size (ClutterActor *self, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_actor_allocate_available_size (ClutterActor *self, gfloat x, gfloat y, gfloat available_width, gfloat available_height, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_actor_allocate_align_fill (ClutterActor *self, const ClutterActorBox *box, gdouble x_align, gdouble y_align, gboolean x_fill, gboolean y_fill, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_actor_set_allocation (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags); CLUTTER_EXPORT void clutter_actor_get_allocation_box (ClutterActor *self, ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_get_allocation_vertices (ClutterActor *self, ClutterActor *ancestor, graphene_point3d_t *verts); CLUTTER_EXPORT gboolean clutter_actor_has_allocation (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_size (ClutterActor *self, gfloat width, gfloat height); CLUTTER_EXPORT void clutter_actor_get_size (ClutterActor *self, gfloat *width, gfloat *height); CLUTTER_EXPORT void clutter_actor_set_position (ClutterActor *self, gfloat x, gfloat y); CLUTTER_EXPORT void clutter_actor_get_position (ClutterActor *self, gfloat *x, gfloat *y); CLUTTER_EXPORT gboolean clutter_actor_get_fixed_position_set (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_fixed_position_set (ClutterActor *self, gboolean is_set); CLUTTER_EXPORT void clutter_actor_move_by (ClutterActor *self, gfloat dx, gfloat dy); /* Actor geometry */ CLUTTER_EXPORT gfloat clutter_actor_get_width (ClutterActor *self); CLUTTER_EXPORT gfloat clutter_actor_get_height (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_width (ClutterActor *self, gfloat width); CLUTTER_EXPORT void clutter_actor_set_height (ClutterActor *self, gfloat height); CLUTTER_EXPORT gfloat clutter_actor_get_x (ClutterActor *self); CLUTTER_EXPORT gfloat clutter_actor_get_y (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_x (ClutterActor *self, gfloat x); CLUTTER_EXPORT void clutter_actor_set_y (ClutterActor *self, gfloat y); CLUTTER_EXPORT void clutter_actor_set_z_position (ClutterActor *self, gfloat z_position); CLUTTER_EXPORT gfloat clutter_actor_get_z_position (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_layout_manager (ClutterActor *self, ClutterLayoutManager *manager); CLUTTER_EXPORT ClutterLayoutManager * clutter_actor_get_layout_manager (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_x_align (ClutterActor *self, ClutterActorAlign x_align); CLUTTER_EXPORT ClutterActorAlign clutter_actor_get_x_align (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_y_align (ClutterActor *self, ClutterActorAlign y_align); CLUTTER_EXPORT ClutterActorAlign clutter_actor_get_y_align (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_margin_top (ClutterActor *self, gfloat margin); CLUTTER_EXPORT gfloat clutter_actor_get_margin_top (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_margin_bottom (ClutterActor *self, gfloat margin); CLUTTER_EXPORT gfloat clutter_actor_get_margin_bottom (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_margin_left (ClutterActor *self, gfloat margin); CLUTTER_EXPORT gfloat clutter_actor_get_margin_left (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_margin_right (ClutterActor *self, gfloat margin); CLUTTER_EXPORT gfloat clutter_actor_get_margin_right (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_margin (ClutterActor *self, const ClutterMargin *margin); CLUTTER_EXPORT void clutter_actor_get_margin (ClutterActor *self, ClutterMargin *margin); CLUTTER_EXPORT void clutter_actor_set_x_expand (ClutterActor *self, gboolean expand); CLUTTER_EXPORT gboolean clutter_actor_get_x_expand (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_y_expand (ClutterActor *self, gboolean expand); CLUTTER_EXPORT gboolean clutter_actor_get_y_expand (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_needs_expand (ClutterActor *self, ClutterOrientation orientation); /* Paint */ CLUTTER_EXPORT void clutter_actor_set_clip (ClutterActor *self, gfloat xoff, gfloat yoff, gfloat width, gfloat height); CLUTTER_EXPORT void clutter_actor_remove_clip (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_clip (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_get_clip (ClutterActor *self, gfloat *xoff, gfloat *yoff, gfloat *width, gfloat *height); CLUTTER_EXPORT void clutter_actor_set_clip_to_allocation (ClutterActor *self, gboolean clip_set); CLUTTER_EXPORT gboolean clutter_actor_get_clip_to_allocation (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_opacity (ClutterActor *self, guint8 opacity); CLUTTER_EXPORT guint8 clutter_actor_get_opacity (ClutterActor *self); CLUTTER_EXPORT guint8 clutter_actor_get_paint_opacity (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_get_paint_visibility (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_offscreen_redirect (ClutterActor *self, ClutterOffscreenRedirect redirect); CLUTTER_EXPORT ClutterOffscreenRedirect clutter_actor_get_offscreen_redirect (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_should_pick_paint (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_is_in_clone_paint (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_get_paint_box (ClutterActor *self, ClutterActorBox *box); CLUTTER_EXPORT gboolean clutter_actor_get_resource_scale (ClutterActor *self, gfloat *resource_scale); CLUTTER_EXPORT gboolean clutter_actor_has_overlaps (ClutterActor *self); /* Content */ CLUTTER_EXPORT void clutter_actor_set_content (ClutterActor *self, ClutterContent *content); CLUTTER_EXPORT ClutterContent * clutter_actor_get_content (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_content_gravity (ClutterActor *self, ClutterContentGravity gravity); CLUTTER_EXPORT ClutterContentGravity clutter_actor_get_content_gravity (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_content_scaling_filters (ClutterActor *self, ClutterScalingFilter min_filter, ClutterScalingFilter mag_filter); CLUTTER_EXPORT void clutter_actor_get_content_scaling_filters (ClutterActor *self, ClutterScalingFilter *min_filter, ClutterScalingFilter *mag_filter); CLUTTER_EXPORT void clutter_actor_set_content_repeat (ClutterActor *self, ClutterContentRepeat repeat); CLUTTER_EXPORT ClutterContentRepeat clutter_actor_get_content_repeat (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_get_content_box (ClutterActor *self, ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_set_background_color (ClutterActor *self, const ClutterColor *color); CLUTTER_EXPORT void clutter_actor_get_background_color (ClutterActor *self, ClutterColor *color); CLUTTER_EXPORT const ClutterPaintVolume * clutter_actor_get_paint_volume (ClutterActor *self); CLUTTER_EXPORT const ClutterPaintVolume * clutter_actor_get_transformed_paint_volume (ClutterActor *self, ClutterActor *relative_to_ancestor); CLUTTER_EXPORT const ClutterPaintVolume * clutter_actor_get_default_paint_volume (ClutterActor *self); /* Events */ CLUTTER_EXPORT void clutter_actor_set_reactive (ClutterActor *actor, gboolean reactive); CLUTTER_EXPORT gboolean clutter_actor_get_reactive (ClutterActor *actor); CLUTTER_EXPORT gboolean clutter_actor_has_key_focus (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_grab_key_focus (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_event (ClutterActor *actor, const ClutterEvent *event, gboolean capture); CLUTTER_EXPORT gboolean clutter_actor_has_pointer (ClutterActor *self); /* Text */ CLUTTER_EXPORT PangoContext * clutter_actor_get_pango_context (ClutterActor *self); CLUTTER_EXPORT PangoContext * clutter_actor_create_pango_context (ClutterActor *self); CLUTTER_EXPORT PangoLayout * clutter_actor_create_pango_layout (ClutterActor *self, const gchar *text); CLUTTER_EXPORT void clutter_actor_set_text_direction (ClutterActor *self, ClutterTextDirection text_dir); CLUTTER_EXPORT ClutterTextDirection clutter_actor_get_text_direction (ClutterActor *self); /* Actor hierarchy */ CLUTTER_EXPORT void clutter_actor_add_child (ClutterActor *self, ClutterActor *child); CLUTTER_EXPORT void clutter_actor_insert_child_at_index (ClutterActor *self, ClutterActor *child, gint index_); CLUTTER_EXPORT void clutter_actor_insert_child_above (ClutterActor *self, ClutterActor *child, ClutterActor *sibling); CLUTTER_EXPORT void clutter_actor_insert_child_below (ClutterActor *self, ClutterActor *child, ClutterActor *sibling); CLUTTER_EXPORT void clutter_actor_replace_child (ClutterActor *self, ClutterActor *old_child, ClutterActor *new_child); CLUTTER_EXPORT void clutter_actor_remove_child (ClutterActor *self, ClutterActor *child); CLUTTER_EXPORT void clutter_actor_remove_all_children (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_destroy_all_children (ClutterActor *self); CLUTTER_EXPORT GList * clutter_actor_get_children (ClutterActor *self); CLUTTER_EXPORT gint clutter_actor_get_n_children (ClutterActor *self); CLUTTER_EXPORT ClutterActor * clutter_actor_get_child_at_index (ClutterActor *self, gint index_); CLUTTER_EXPORT ClutterActor * clutter_actor_get_previous_sibling (ClutterActor *self); CLUTTER_EXPORT ClutterActor * clutter_actor_get_next_sibling (ClutterActor *self); CLUTTER_EXPORT ClutterActor * clutter_actor_get_first_child (ClutterActor *self); CLUTTER_EXPORT ClutterActor * clutter_actor_get_last_child (ClutterActor *self); CLUTTER_EXPORT ClutterActor * clutter_actor_get_parent (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_contains (ClutterActor *self, ClutterActor *descendant); CLUTTER_EXPORT ClutterActor* clutter_actor_get_stage (ClutterActor *actor); CLUTTER_EXPORT void clutter_actor_set_child_below_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling); CLUTTER_EXPORT void clutter_actor_set_child_above_sibling (ClutterActor *self, ClutterActor *child, ClutterActor *sibling); CLUTTER_EXPORT void clutter_actor_set_child_at_index (ClutterActor *self, ClutterActor *child, gint index_); CLUTTER_EXPORT void clutter_actor_iter_init (ClutterActorIter *iter, ClutterActor *root); CLUTTER_EXPORT gboolean clutter_actor_iter_next (ClutterActorIter *iter, ClutterActor **child); CLUTTER_EXPORT gboolean clutter_actor_iter_prev (ClutterActorIter *iter, ClutterActor **child); CLUTTER_EXPORT void clutter_actor_iter_remove (ClutterActorIter *iter); CLUTTER_EXPORT void clutter_actor_iter_destroy (ClutterActorIter *iter); CLUTTER_EXPORT gboolean clutter_actor_iter_is_valid (const ClutterActorIter *iter); /* Transformations */ CLUTTER_EXPORT gboolean clutter_actor_is_rotated (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_is_scaled (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_pivot_point (ClutterActor *self, gfloat pivot_x, gfloat pivot_y); CLUTTER_EXPORT void clutter_actor_get_pivot_point (ClutterActor *self, gfloat *pivot_x, gfloat *pivot_y); CLUTTER_EXPORT void clutter_actor_set_pivot_point_z (ClutterActor *self, gfloat pivot_z); CLUTTER_EXPORT gfloat clutter_actor_get_pivot_point_z (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_rotation_angle (ClutterActor *self, ClutterRotateAxis axis, gdouble angle); CLUTTER_EXPORT gdouble clutter_actor_get_rotation_angle (ClutterActor *self, ClutterRotateAxis axis); CLUTTER_EXPORT void clutter_actor_set_scale (ClutterActor *self, gdouble scale_x, gdouble scale_y); CLUTTER_EXPORT void clutter_actor_get_scale (ClutterActor *self, gdouble *scale_x, gdouble *scale_y); CLUTTER_EXPORT void clutter_actor_set_scale_z (ClutterActor *self, gdouble scale_z); CLUTTER_EXPORT gdouble clutter_actor_get_scale_z (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_translation (ClutterActor *self, gfloat translate_x, gfloat translate_y, gfloat translate_z); CLUTTER_EXPORT void clutter_actor_get_translation (ClutterActor *self, gfloat *translate_x, gfloat *translate_y, gfloat *translate_z); CLUTTER_EXPORT void clutter_actor_set_transform (ClutterActor *self, const ClutterMatrix *transform); CLUTTER_EXPORT void clutter_actor_get_transform (ClutterActor *self, ClutterMatrix *transform); CLUTTER_EXPORT void clutter_actor_set_child_transform (ClutterActor *self, const ClutterMatrix *transform); CLUTTER_EXPORT void clutter_actor_get_child_transform (ClutterActor *self, ClutterMatrix *transform); CLUTTER_EXPORT void clutter_actor_get_transformed_position (ClutterActor *self, gfloat *x, gfloat *y); CLUTTER_EXPORT void clutter_actor_get_transformed_size (ClutterActor *self, gfloat *width, gfloat *height); CLUTTER_EXPORT gboolean clutter_actor_transform_stage_point (ClutterActor *self, gfloat x, gfloat y, gfloat *x_out, gfloat *y_out); CLUTTER_EXPORT void clutter_actor_get_abs_allocation_vertices (ClutterActor *self, graphene_point3d_t *verts); CLUTTER_EXPORT void clutter_actor_apply_transform_to_point (ClutterActor *self, const graphene_point3d_t *point, graphene_point3d_t *vertex); CLUTTER_EXPORT void clutter_actor_apply_relative_transform_to_point (ClutterActor *self, ClutterActor *ancestor, const graphene_point3d_t *point, graphene_point3d_t *vertex); /* Implicit animations */ CLUTTER_EXPORT void clutter_actor_save_easing_state (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_restore_easing_state (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_easing_mode (ClutterActor *self, ClutterAnimationMode mode); CLUTTER_EXPORT ClutterAnimationMode clutter_actor_get_easing_mode (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_easing_duration (ClutterActor *self, guint msecs); CLUTTER_EXPORT guint clutter_actor_get_easing_duration (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_easing_delay (ClutterActor *self, guint msecs); CLUTTER_EXPORT guint clutter_actor_get_easing_delay (ClutterActor *self); CLUTTER_EXPORT ClutterTransition * clutter_actor_get_transition (ClutterActor *self, const char *name); CLUTTER_EXPORT void clutter_actor_add_transition (ClutterActor *self, const char *name, ClutterTransition *transition); CLUTTER_EXPORT void clutter_actor_remove_transition (ClutterActor *self, const char *name); CLUTTER_EXPORT void clutter_actor_remove_all_transitions (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_mapped_clones (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_set_opacity_override (ClutterActor *self, gint opacity); CLUTTER_EXPORT gint clutter_actor_get_opacity_override (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_inhibit_culling (ClutterActor *actor); CLUTTER_EXPORT void clutter_actor_uninhibit_culling (ClutterActor *actor); /** * ClutterActorCreateChildFunc: * @item: (type GObject): the item in the model * @user_data: Data passed to clutter_actor_bind_model() * * Creates a #ClutterActor using the @item in the model. * * The usual way to implement this function is to create a #ClutterActor * instance and then bind the #GObject properties to the actor properties * of interest, using g_object_bind_property(). This way, when the @item * in the #GListModel changes, the #ClutterActor changes as well. * * Returns: (transfer full): The newly created child #ClutterActor * * Since: 1.24 */ typedef ClutterActor * (* ClutterActorCreateChildFunc) (gpointer item, gpointer user_data); CLUTTER_EXPORT void clutter_actor_bind_model (ClutterActor *self, GListModel *model, ClutterActorCreateChildFunc create_child_func, gpointer user_data, GDestroyNotify notify); CLUTTER_EXPORT void clutter_actor_bind_model_with_properties (ClutterActor *self, GListModel *model, GType child_type, const char *first_model_property, ...); CLUTTER_EXPORT void clutter_actor_pick_box (ClutterActor *self, ClutterPickContext *pick_context, const ClutterActorBox *box); G_END_DECLS #endif /* __CLUTTER_ACTOR_H__ */ muffin-6.4.1/clutter/clutter/clutter-autocleanups.h0000664000175000017500000001231214723361714021441 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright 2015 Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_AUTO_CLEANUPS_H__ #define __CLUTTER_AUTO_CLEANUPS_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __GI_SCANNER__ G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterActor, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterActorMeta, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterAlignConstraint, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBackend, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBindConstraint, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBindingPool, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBinLayout, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBlurEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBoxLayout, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBrightnessContrastEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterCanvas, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterChildMeta, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterClickAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterClone, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterColorizeEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterConstraint, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterContainer, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDeformEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDesaturateEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDragAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterDropAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFixedLayout, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterFlowLayout, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterGestureAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterGridLayout, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterImage, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterInputDevice, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterInterval, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterKeyframeTransition, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterLayoutManager, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterLayoutMeta, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterOffscreenEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPageTurnEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPanAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPathConstraint, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPath, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPropertyTransition, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterRotateAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterScriptable, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterScript, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterScrollActor, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterSettings, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterShaderEffect, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterSnapConstraint, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterStage, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterSwipeAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterTapAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterTextBuffer, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterText, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterTimeline, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterTransitionGroup, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterTransition, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterZoomAction, g_object_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterActorBox, clutter_actor_box_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterColor, clutter_color_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterMargin, clutter_margin_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterMatrix, clutter_matrix_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPaintContext, clutter_paint_context_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPaintNode, clutter_paint_node_unref) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPaintVolume, clutter_paint_volume_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterPathNode, clutter_path_node_free) #endif /* __GI_SCANNER__ */ #endif /* __CLUTTER_AUTO_CLEANUPS_H__ */ muffin-6.4.1/clutter/clutter/egl/0000775000175000017500000000000014723361714015655 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/egl/clutter-backend-eglnative.h0000664000175000017500000000536014723361714023055 0ustar fabiofabio/* Clutter. * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2006, 2007 OpenedHand * Copyright (C) 2010 Intel Corp * 2011 Giovanni Campagna * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Matthew Allum * Robert Bragg */ #ifndef __CLUTTER_BACKEND_EGL_NATIVE_H__ #define __CLUTTER_BACKEND_EGL_NATIVE_H__ #include #include #include #include #include #include #include "clutter-backend-private.h" G_BEGIN_DECLS #define CLUTTER_TYPE_BACKEND_EGL_NATIVE (clutter_backend_egl_native_get_type ()) #define CLUTTER_BACKEND_EGL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BACKEND_EGL_NATIVE, ClutterBackendEglNative)) #define CLUTTER_IS_BACKEND_EGL_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BACKEND_EGL_NATIVE)) #define CLUTTER_BACKEND_EGL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BACKEND_EGL_NATIVE, ClutterBackendEglNativeClass)) #define CLUTTER_IS_BACKEND_EGL_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BACKEND_EGL_NATIVE)) #define CLUTTER_BACKEND_EGL_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BACKEND_EGL_NATIVE, ClutterBackendEglNativeClass)) typedef struct _ClutterBackendEglNative ClutterBackendEglNative; typedef struct _ClutterBackendEglNativeClass ClutterBackendEglNativeClass; G_DEFINE_AUTOPTR_CLEANUP_FUNC (ClutterBackendEglNative, g_object_unref) struct _ClutterBackendEglNative { ClutterBackend parent_instance; /* event source */ GSource *event_source; /* event timer */ GTimer *event_timer; /* "xsettings" is still the defacto place for Xft settings, even in Wayland */ GSettings *xsettings; }; struct _ClutterBackendEglNativeClass { ClutterBackendClass parent_class; }; CLUTTER_EXPORT GType clutter_backend_egl_native_get_type (void) G_GNUC_CONST; ClutterBackend *clutter_backend_egl_native_new (void); G_END_DECLS #endif /* __CLUTTER_BACKEND_EGL_NATIVE_H__ */ muffin-6.4.1/clutter/clutter/egl/clutter-egl.h0000664000175000017500000000270514723361714020261 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-egl * @short_description: EGL specific API * * The EGL backend for Clutter provides some EGL specific API * * You need to include `clutter-egl.h` to have access to the functions documented here. */ #ifndef __CLUTTER_EGL_H__ #define __CLUTTER_EGL_H__ #include #include "clutter-egl-headers.h" #include G_BEGIN_DECLS /** * clutter_egl_get_egl_display: * * Retrieves the #EGLDisplay used by Clutter. * * Return value: the EGL display * * Since: 1.6 */ CLUTTER_EXPORT EGLDisplay clutter_egl_get_egl_display (void); G_END_DECLS #endif /* __CLUTTER_EGL_H__ */ muffin-6.4.1/clutter/clutter/egl/clutter-egl-headers.h0000664000175000017500000000217314723361714021671 0ustar fabiofabio/* Clutter. * An OpenGL based 'interactive canvas' library. * Authored By Matthew Allum * Copyright (C) 2006-2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef __CLUTTER_EGL_HEADERS_H__ #define __CLUTTER_EGL_HEADERS_H__ /* Clutter relies on Cogl to abstract GLES1/2/OpenGL and GLX/EGL etc * so we simply include the Cogl header to pull in the appropriate EGL * header. */ #include #include #endif /* __CLUTTER_EGL_HEADERS_H__ */ muffin-6.4.1/clutter/clutter/egl/clutter-backend-eglnative.c0000664000175000017500000002110314723361714023041 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010,2011 Intel Corporation. * 2011 Giovanni Campagna * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * Authors: * Matthew Allum * Emmanuele Bassi * Robert Bragg * Neil Roberts */ #include "clutter-build-config.h" #include #include #include #include #include #include "clutter-backend-eglnative.h" /* This is a Cogl based backend */ #include "cogl/clutter-stage-cogl.h" #include "clutter-debug.h" #include "clutter-private.h" #include "clutter-main.h" #include "clutter-stage-private.h" #include "clutter-settings-private.h" #ifdef COGL_HAS_EGL_SUPPORT #include "clutter-egl.h" #endif G_DEFINE_TYPE (ClutterBackendEglNative, clutter_backend_egl_native, CLUTTER_TYPE_BACKEND); static void clutter_backend_egl_native_dispose (GObject *gobject) { ClutterBackendEglNative *backend_egl_native = CLUTTER_BACKEND_EGL_NATIVE (gobject); g_clear_object (&backend_egl_native->xsettings); if (backend_egl_native->event_timer != NULL) { g_timer_destroy (backend_egl_native->event_timer); backend_egl_native->event_timer = NULL; } G_OBJECT_CLASS (clutter_backend_egl_native_parent_class)->dispose (gobject); } static void clutter_backend_egl_native_class_init (ClutterBackendEglNativeClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = clutter_backend_egl_native_dispose; } typedef struct { cairo_antialias_t cairo_antialias; gint clutter_font_antialias; cairo_hint_style_t cairo_hint_style; const char *clutter_font_hint_style; cairo_subpixel_order_t cairo_subpixel_order; const char *clutter_font_subpixel_order; } FontSettings; static void get_font_gsettings (GSettings *xsettings, FontSettings *output) { /* org.gnome.settings-daemon.GsdFontAntialiasingMode */ static const struct { cairo_antialias_t cairo_antialias; gint clutter_font_antialias; } antialiasings[] = { /* none=0 */ {CAIRO_ANTIALIAS_NONE, 0}, /* grayscale=1 */ {CAIRO_ANTIALIAS_GRAY, 1}, /* rgba=2 */ {CAIRO_ANTIALIAS_SUBPIXEL, 1}, }; /* org.gnome.settings-daemon.GsdFontHinting */ static const struct { cairo_hint_style_t cairo_hint_style; const char *clutter_font_hint_style; } hintings[] = { /* none=0 */ {CAIRO_HINT_STYLE_NONE, "hintnone"}, /* slight=1 */ {CAIRO_HINT_STYLE_SLIGHT, "hintslight"}, /* medium=2 */ {CAIRO_HINT_STYLE_MEDIUM, "hintmedium"}, /* full=3 */ {CAIRO_HINT_STYLE_FULL, "hintfull"}, }; /* org.gnome.settings-daemon.GsdFontRgbaOrder */ static const struct { cairo_subpixel_order_t cairo_subpixel_order; const char *clutter_font_subpixel_order; } rgba_orders[] = { /* rgba=0 */ {CAIRO_SUBPIXEL_ORDER_RGB, "rgb"}, /* XXX what is 'rgba'? */ /* rgb=1 */ {CAIRO_SUBPIXEL_ORDER_RGB, "rgb"}, /* bgr=2 */ {CAIRO_SUBPIXEL_ORDER_BGR, "bgr"}, /* vrgb=3 */ {CAIRO_SUBPIXEL_ORDER_VRGB, "vrgb"}, /* vbgr=4 */ {CAIRO_SUBPIXEL_ORDER_VBGR, "vbgr"}, }; guint i; i = g_settings_get_enum (xsettings, "hinting"); if (i < G_N_ELEMENTS (hintings)) { output->cairo_hint_style = hintings[i].cairo_hint_style; output->clutter_font_hint_style = hintings[i].clutter_font_hint_style; } else { output->cairo_hint_style = CAIRO_HINT_STYLE_DEFAULT; output->clutter_font_hint_style = NULL; } i = g_settings_get_enum (xsettings, "antialiasing"); if (i < G_N_ELEMENTS (antialiasings)) { output->cairo_antialias = antialiasings[i].cairo_antialias; output->clutter_font_antialias = antialiasings[i].clutter_font_antialias; } else { output->cairo_antialias = CAIRO_ANTIALIAS_DEFAULT; output->clutter_font_antialias = -1; } i = g_settings_get_enum (xsettings, "rgba-order"); if (i < G_N_ELEMENTS (rgba_orders)) { output->cairo_subpixel_order = rgba_orders[i].cairo_subpixel_order; output->clutter_font_subpixel_order = rgba_orders[i].clutter_font_subpixel_order; } else { output->cairo_subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; output->clutter_font_subpixel_order = NULL; } if (output->cairo_antialias == CAIRO_ANTIALIAS_GRAY) output->clutter_font_subpixel_order = "none"; } static void init_font_options (ClutterBackendEglNative *backend_egl_native) { GSettings *xsettings = backend_egl_native->xsettings; cairo_font_options_t *options = cairo_font_options_create (); FontSettings fs; get_font_gsettings (xsettings, &fs); cairo_font_options_set_hint_style (options, fs.cairo_hint_style); cairo_font_options_set_antialias (options, fs.cairo_antialias); cairo_font_options_set_subpixel_order (options, fs.cairo_subpixel_order); clutter_backend_set_font_options (CLUTTER_BACKEND (backend_egl_native), options); cairo_font_options_destroy (options); } static gboolean on_xsettings_change_event (GSettings *xsettings, gpointer keys, gint n_keys, gpointer user_data) { /* * A simpler alternative to this function that does not update the screen * immediately (like macOS :P): * * init_font_options (CLUTTER_BACKEND_EGL_NATIVE (user_data)); * * which has the added benefit of eliminating the need for all the * FontSettings.clutter_ fields. However the below approach is better for * testing settings and more consistent with the existing x11 backend... */ ClutterSettings *csettings = clutter_settings_get_default (); FontSettings fs; gint hinting; get_font_gsettings (xsettings, &fs); hinting = fs.cairo_hint_style == CAIRO_HINT_STYLE_NONE ? 0 : 1; g_object_set (csettings, "font-hinting", hinting, "font-hint-style", fs.clutter_font_hint_style, "font-antialias", fs.clutter_font_antialias, "font-subpixel-order", fs.clutter_font_subpixel_order, NULL); return FALSE; } static void clutter_backend_egl_native_init (ClutterBackendEglNative *backend_egl_native) { static const gchar xsettings_path[] = "org.cinnamon.settings-daemon.plugins.xsettings"; GSettingsSchemaSource *source = g_settings_schema_source_get_default (); GSettingsSchema *schema = g_settings_schema_source_lookup (source, xsettings_path, TRUE); if (!schema) { g_warning ("Failed to find schema: %s", xsettings_path); } else { backend_egl_native->xsettings = g_settings_new_full (schema, NULL, NULL); if (backend_egl_native->xsettings) { init_font_options (backend_egl_native); g_signal_connect (backend_egl_native->xsettings, "change-event", G_CALLBACK (on_xsettings_change_event), backend_egl_native); } } backend_egl_native->event_timer = g_timer_new (); } ClutterBackend * clutter_backend_egl_native_new (void) { return g_object_new (CLUTTER_TYPE_BACKEND_EGL_NATIVE, NULL); } /** * clutter_egl_get_egl_display: * * Retrieves the EGL display used by Clutter, if it supports the * EGL windowing system and if it is running using an EGL backend. * * Return value: the EGL display used by Clutter, or 0 * * Since: 1.6 */ EGLDisplay clutter_egl_get_egl_display (void) { ClutterBackend *backend; if (!_clutter_context_is_initialized ()) { g_critical ("The Clutter backend has not been initialized yet"); return 0; } backend = clutter_get_default_backend (); if (!CLUTTER_IS_BACKEND_EGL_NATIVE (backend)) { g_critical ("The Clutter backend is not an EGL backend"); return 0; } #ifdef COGL_HAS_EGL_SUPPORT return cogl_egl_context_get_egl_display (backend->cogl_context); #else return 0; #endif } muffin-6.4.1/clutter/clutter/clutter-input-focus.h0000664000175000017500000000647114723361714021223 0ustar fabiofabio/* * Copyright (C) 2017,2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef __CLUTTER_INPUT_FOCUS_H__ #define __CLUTTER_INPUT_FOCUS_H__ #include #define CLUTTER_TYPE_INPUT_FOCUS (clutter_input_focus_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterInputFocus, clutter_input_focus, CLUTTER, INPUT_FOCUS, GObject) struct _ClutterInputFocusClass { GObjectClass parent_class; GTypeInterface iface; void (* focus_in) (ClutterInputFocus *focus, ClutterInputMethod *input_method); void (* focus_out) (ClutterInputFocus *focus); void (* request_surrounding) (ClutterInputFocus *focus); void (* delete_surrounding) (ClutterInputFocus *focus, int offset, guint len); void (* commit_text) (ClutterInputFocus *focus, const gchar *text); void (* set_preedit_text) (ClutterInputFocus *focus, const gchar *preedit, guint cursor); }; CLUTTER_EXPORT gboolean clutter_input_focus_is_focused (ClutterInputFocus *focus); CLUTTER_EXPORT void clutter_input_focus_reset (ClutterInputFocus *focus); CLUTTER_EXPORT void clutter_input_focus_set_cursor_location (ClutterInputFocus *focus, const graphene_rect_t *rect); CLUTTER_EXPORT void clutter_input_focus_set_surrounding (ClutterInputFocus *focus, const gchar *text, guint cursor, guint anchor); CLUTTER_EXPORT void clutter_input_focus_set_content_hints (ClutterInputFocus *focus, ClutterInputContentHintFlags hint); CLUTTER_EXPORT void clutter_input_focus_set_content_purpose (ClutterInputFocus *focus, ClutterInputContentPurpose purpose); CLUTTER_EXPORT gboolean clutter_input_focus_filter_event (ClutterInputFocus *focus, const ClutterEvent *event); CLUTTER_EXPORT void clutter_input_focus_set_can_show_preedit (ClutterInputFocus *focus, gboolean can_show_preedit); CLUTTER_EXPORT void clutter_input_focus_set_input_panel_state (ClutterInputFocus *focus, ClutterInputPanelState state); #endif /* __CLUTTER_INPUT_FOCUS_H__ */ muffin-6.4.1/clutter/clutter/clutter-container.h0000664000175000017500000002111714723361714020723 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * ClutterContainer: Generic actor container interface. * Author: Emmanuele Bassi */ #ifndef __CLUTTER_CONTAINER_H__ #define __CLUTTER_CONTAINER_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_CONTAINER (clutter_container_get_type ()) #define CLUTTER_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_CONTAINER, ClutterContainer)) #define CLUTTER_IS_CONTAINER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_CONTAINER)) #define CLUTTER_CONTAINER_GET_IFACE(obj) (G_TYPE_INSTANCE_GET_INTERFACE ((obj), CLUTTER_TYPE_CONTAINER, ClutterContainerIface)) typedef struct _ClutterContainerIface ClutterContainerIface; /** * ClutterContainer: * * #ClutterContainer is an opaque structure whose members cannot be directly * accessed * * Since: 0.4 */ /** * ClutterContainerIface: * @add: virtual function for adding an actor to the container. This virtual * function is deprecated, and it should not be overridden. * @remove: virtual function for removing an actor from the container. This * virtual function is deprecated, and it should not be overridden. * @raise: virtual function for raising a child. This virtual function is * deprecated and it should not be overridden. * @lower: virtual function for lowering a child. This virtual function is * deprecated and it should not be overridden. * @sort_depth_order: virtual function for sorting the children of a * container depending on their depth. This virtual function is deprecated * and it should not be overridden. * @child_meta_type: The GType used for storing auxiliary information about * each of the containers children. * @create_child_meta: virtual function that gets called for each added * child, the function should instantiate an object of type * #ClutterContainerIface::child_meta_type, set the container and actor * fields in the instance and add the record to a data structure for * subsequent access for #ClutterContainerIface::get_child_meta * @destroy_child_meta: virtual function that gets called when a child is * removed; it shuld release all resources held by the record * @get_child_meta: return the record for a container child * @actor_added: class handler for #ClutterContainer::actor-added * @actor_removed: class handler for #ClutterContainer::actor-removed * @child_notify: class handler for #ClutterContainer::child-notify * * Base interface for container actors. The @add and @remove * virtual functions must be provided by any implementation; the other * virtual functions are optional. * * Since: 0.4 */ struct _ClutterContainerIface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ void (* add) (ClutterContainer *container, ClutterActor *actor); void (* remove) (ClutterContainer *container, ClutterActor *actor); /* child stacking */ void (* raise) (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); void (* lower) (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling); void (* sort_depth_order) (ClutterContainer *container); /* ClutterChildMeta management */ GType child_meta_type; void (* create_child_meta) (ClutterContainer *container, ClutterActor *actor); void (* destroy_child_meta) (ClutterContainer *container, ClutterActor *actor); ClutterChildMeta *(* get_child_meta) (ClutterContainer *container, ClutterActor *actor); /* signals */ void (* actor_added) (ClutterContainer *container, ClutterActor *actor); void (* actor_removed) (ClutterContainer *container, ClutterActor *actor); void (* child_notify) (ClutterContainer *container, ClutterActor *child, GParamSpec *pspec); }; CLUTTER_EXPORT GType clutter_container_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActor * clutter_container_find_child_by_name (ClutterContainer *container, const gchar *child_name); CLUTTER_EXPORT GParamSpec * clutter_container_class_find_child_property (GObjectClass *klass, const gchar *property_name); CLUTTER_EXPORT GParamSpec ** clutter_container_class_list_child_properties (GObjectClass *klass, guint *n_properties); CLUTTER_EXPORT void clutter_container_create_child_meta (ClutterContainer *container, ClutterActor *actor); CLUTTER_EXPORT void clutter_container_destroy_child_meta (ClutterContainer *container, ClutterActor *actor); CLUTTER_EXPORT ClutterChildMeta * clutter_container_get_child_meta (ClutterContainer *container, ClutterActor *actor); CLUTTER_EXPORT void clutter_container_child_set_property (ClutterContainer *container, ClutterActor *child, const gchar * property, const GValue *value); CLUTTER_EXPORT void clutter_container_child_get_property (ClutterContainer *container, ClutterActor *child, const gchar *property, GValue *value); CLUTTER_EXPORT void clutter_container_child_set (ClutterContainer *container, ClutterActor *actor, const gchar *first_prop, ...) G_GNUC_NULL_TERMINATED; CLUTTER_EXPORT void clutter_container_child_get (ClutterContainer *container, ClutterActor *actor, const gchar *first_prop, ...) G_GNUC_NULL_TERMINATED; CLUTTER_EXPORT void clutter_container_child_notify (ClutterContainer *container, ClutterActor *child, GParamSpec *pspec); G_END_DECLS #endif /* __CLUTTER_CONTAINER_H__ */ muffin-6.4.1/clutter/clutter/clutter-id-pool.c0000664000175000017500000000647714723361714020313 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006-2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * * * ClutterIDPool: pool of reusable integer ids associated with pointers. * * Author: Øyvind Kolås * */ #include "clutter-build-config.h" #include "clutter-debug.h" #include "clutter-id-pool.h" struct _ClutterIDPool { GArray *array; /* Array of pointers */ GSList *free_ids; /* A stack of freed ids */ }; ClutterIDPool * _clutter_id_pool_new (guint initial_size) { ClutterIDPool *self; self = g_slice_new (ClutterIDPool); self->array = g_array_sized_new (FALSE, FALSE, sizeof (gpointer), initial_size); self->free_ids = NULL; return self; } void _clutter_id_pool_free (ClutterIDPool *id_pool) { g_return_if_fail (id_pool != NULL); g_array_free (id_pool->array, TRUE); g_slist_free (id_pool->free_ids); g_slice_free (ClutterIDPool, id_pool); } guint32 _clutter_id_pool_add (ClutterIDPool *id_pool, gpointer ptr) { gpointer *array; guint32 retval; g_return_val_if_fail (id_pool != NULL, 0); if (id_pool->free_ids) /* There are items on our freelist, reuse one */ { array = (void*) id_pool->array->data; retval = GPOINTER_TO_UINT (id_pool->free_ids->data); id_pool->free_ids = g_slist_remove (id_pool->free_ids, id_pool->free_ids->data); array[retval] = ptr; return retval; } /* Allocate new id */ retval = id_pool->array->len; g_array_append_val (id_pool->array, ptr); return retval; } void _clutter_id_pool_remove (ClutterIDPool *id_pool, guint32 id_) { gpointer *array; g_return_if_fail (id_pool != NULL); array = (void*) id_pool->array->data; array[id_] = NULL; id_pool->free_ids = g_slist_prepend (id_pool->free_ids, GUINT_TO_POINTER (id_)); } gpointer _clutter_id_pool_lookup (ClutterIDPool *id_pool, guint32 id_) { gpointer *array; g_return_val_if_fail (id_pool != NULL, NULL); g_return_val_if_fail (id_pool->array != NULL, NULL); array = (void*) id_pool->array->data; if (id_ >= id_pool->array->len || array[id_] == NULL) { g_warning ("The required ID of %u does not refer to an existing actor; " "this usually implies that the pick() of an actor is not " "correctly implemented or that there is an error in the " "glReadPixels() implementation of the GL driver.", id_); return NULL; } return array[id_]; } muffin-6.4.1/clutter/clutter/clutter-stage-window.c0000664000175000017500000001607214723361714021350 0ustar fabiofabio#include "clutter-build-config.h" #include #include "clutter-actor.h" #include "clutter-stage-window.h" #include "clutter-private.h" /** * SECTION:clutter-stage-window * @short_description: Handles the implementation for ClutterStage * * #ClutterStageWindow is an interface that provides the implementation for the * #ClutterStage actor, abstracting away the specifics of the windowing system. */ G_DEFINE_INTERFACE (ClutterStageWindow, clutter_stage_window, G_TYPE_OBJECT); static void clutter_stage_window_default_init (ClutterStageWindowInterface *iface) { GParamSpec *pspec; pspec = g_param_spec_object ("backend", "Backend", "Back pointer to the Backend instance", CLUTTER_TYPE_BACKEND, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, pspec); pspec = g_param_spec_object ("wrapper", "Wrapper", "Back pointer to the Stage actor", CLUTTER_TYPE_STAGE, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_interface_install_property (iface, pspec); } /** * _clutter_stage_window_get_wrapper: * @window: a #ClutterStageWindow object * * Returns the pointer to the #ClutterStage it's part of. */ ClutterActor * _clutter_stage_window_get_wrapper (ClutterStageWindow *window) { return CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_wrapper (window); } void _clutter_stage_window_set_title (ClutterStageWindow *window, const gchar *title) { ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->set_title) iface->set_title (window, title); } void _clutter_stage_window_set_cursor_visible (ClutterStageWindow *window, gboolean is_visible) { ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->set_cursor_visible) iface->set_cursor_visible (window, is_visible); } gboolean _clutter_stage_window_realize (ClutterStageWindow *window) { return CLUTTER_STAGE_WINDOW_GET_IFACE (window)->realize (window); } void _clutter_stage_window_unrealize (ClutterStageWindow *window) { CLUTTER_STAGE_WINDOW_GET_IFACE (window)->unrealize (window); } void _clutter_stage_window_show (ClutterStageWindow *window, gboolean do_raise) { CLUTTER_STAGE_WINDOW_GET_IFACE (window)->show (window, do_raise); } void _clutter_stage_window_hide (ClutterStageWindow *window) { CLUTTER_STAGE_WINDOW_GET_IFACE (window)->hide (window); } void _clutter_stage_window_resize (ClutterStageWindow *window, gint width, gint height) { CLUTTER_STAGE_WINDOW_GET_IFACE (window)->resize (window, width, height); } void _clutter_stage_window_get_geometry (ClutterStageWindow *window, cairo_rectangle_int_t *geometry) { CLUTTER_STAGE_WINDOW_GET_IFACE (window)->get_geometry (window, geometry); } void _clutter_stage_window_schedule_update (ClutterStageWindow *window, int sync_delay) { ClutterStageWindowInterface *iface; g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->schedule_update == NULL) { g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)); return; } iface->schedule_update (window, sync_delay); } /** * _clutter_stage_window_get_update_time: * @window: a #ClutterStageWindow object * * See _clutter_stage_get_update_time() for more info. * * Returns: The timestamp of the update time */ gint64 _clutter_stage_window_get_update_time (ClutterStageWindow *window) { ClutterStageWindowInterface *iface; g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->get_update_time == NULL) { g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)); return 0; } return iface->get_update_time (window); } /** * _clutter_stage_window_clear_update_time: * @window: a #ClutterStageWindow object * * Clears the update time. See _clutter_stage_clear_update_time() for more info. */ void _clutter_stage_window_clear_update_time (ClutterStageWindow *window) { ClutterStageWindowInterface *iface; g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->clear_update_time == NULL) { g_assert (!clutter_feature_available (CLUTTER_FEATURE_SWAP_EVENTS)); return; } iface->clear_update_time (window); } int64_t _clutter_stage_window_get_next_presentation_time (ClutterStageWindow *window) { ClutterStageWindowInterface *iface; g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), 0); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); /* If not implemented then just revert to the old behaviour... */ if (iface->get_next_presentation_time == NULL) return _clutter_stage_window_get_update_time (window); return iface->get_next_presentation_time (window); } void _clutter_stage_window_set_accept_focus (ClutterStageWindow *window, gboolean accept_focus) { ClutterStageWindowInterface *iface; g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->set_accept_focus) iface->set_accept_focus (window, accept_focus); } void _clutter_stage_window_redraw (ClutterStageWindow *window) { ClutterStageWindowInterface *iface; g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (window)); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->redraw) iface->redraw (window); } gboolean _clutter_stage_window_can_clip_redraws (ClutterStageWindow *window) { ClutterStageWindowInterface *iface; g_return_val_if_fail (CLUTTER_IS_STAGE_WINDOW (window), FALSE); iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->can_clip_redraws != NULL) return iface->can_clip_redraws (window); return FALSE; } GList * _clutter_stage_window_get_views (ClutterStageWindow *window) { ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); return iface->get_views (window); } void _clutter_stage_window_finish_frame (ClutterStageWindow *window) { ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->finish_frame) iface->finish_frame (window); } int64_t _clutter_stage_window_get_frame_counter (ClutterStageWindow *window) { ClutterStageWindowInterface *iface = CLUTTER_STAGE_WINDOW_GET_IFACE (window); if (iface->get_frame_counter) return iface->get_frame_counter (window); else return 0; } muffin-6.4.1/clutter/clutter/clutter-colorize-effect.c0000664000175000017500000002501414723361714022014 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-colorize-effect * @short_description: A colorization effect * @see_also: #ClutterEffect, #ClutterOffscreenEffect * * #ClutterColorizeEffect is a sub-class of #ClutterEffect that * colorizes an actor with the given tint. * * #ClutterColorizeEffect is available since Clutter 1.4 */ #define CLUTTER_COLORIZE_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_COLORIZE_EFFECT, ClutterColorizeEffectClass)) #define CLUTTER_IS_COLORIZE_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_COLORIZE_EFFECT)) #define CLUTTER_COLORIZE_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_COLORIZE_EFFECT, ClutterColorizeEffectClass)) #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-colorize-effect.h" #include "cogl/cogl.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-offscreen-effect.h" #include "clutter-private.h" struct _ClutterColorizeEffect { ClutterOffscreenEffect parent_instance; /* the tint of the colorization */ ClutterColor tint; gint tint_uniform; gint tex_width; gint tex_height; CoglPipeline *pipeline; }; struct _ClutterColorizeEffectClass { ClutterOffscreenEffectClass parent_class; CoglPipeline *base_pipeline; }; /* the magic gray vec3 has been taken from the NTSC conversion weights * as defined by: * * "OpenGL Superbible, 4th Edition" * -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel * Addison-Wesley */ static const gchar *colorize_glsl_declarations = "uniform vec3 tint;\n"; static const gchar *colorize_glsl_source = "float gray = dot (cogl_color_out.rgb, vec3 (0.299, 0.587, 0.114));\n" "cogl_color_out.rgb = gray * tint;\n"; /* a lame sepia */ static const ClutterColor default_tint = { 255, 204, 153, 255 }; enum { PROP_0, PROP_TINT, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterColorizeEffect, clutter_colorize_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); static gboolean clutter_colorize_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (effect); ClutterEffectClass *parent_class; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ShaderEffect: the graphics hardware " "or the current GL driver does not implement support " "for the GLSL shading language."); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); return FALSE; } parent_class = CLUTTER_EFFECT_CLASS (clutter_colorize_effect_parent_class); if (parent_class->pre_paint (effect, paint_context)) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect); CoglHandle texture; texture = clutter_offscreen_effect_get_texture (offscreen_effect); self->tex_width = cogl_texture_get_width (texture); self->tex_height = cogl_texture_get_height (texture); cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); return TRUE; } else return FALSE; } static void clutter_colorize_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (effect); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); ClutterActor *actor; guint8 paint_opacity; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); paint_opacity = clutter_actor_get_paint_opacity (actor); cogl_pipeline_set_color4ub (self->pipeline, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_framebuffer_draw_rectangle (framebuffer, self->pipeline, 0, 0, self->tex_width, self->tex_height); } static void clutter_colorize_effect_dispose (GObject *gobject) { ClutterColorizeEffect *self = CLUTTER_COLORIZE_EFFECT (gobject); if (self->pipeline != NULL) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } G_OBJECT_CLASS (clutter_colorize_effect_parent_class)->dispose (gobject); } static void clutter_colorize_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterColorizeEffect *effect = CLUTTER_COLORIZE_EFFECT (gobject); switch (prop_id) { case PROP_TINT: clutter_colorize_effect_set_tint (effect, clutter_value_get_color (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_colorize_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterColorizeEffect *effect = CLUTTER_COLORIZE_EFFECT (gobject); switch (prop_id) { case PROP_TINT: clutter_value_set_color (value, &effect->tint); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_colorize_effect_class_init (ClutterColorizeEffectClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->paint_target = clutter_colorize_effect_paint_target; effect_class->pre_paint = clutter_colorize_effect_pre_paint; gobject_class->set_property = clutter_colorize_effect_set_property; gobject_class->get_property = clutter_colorize_effect_get_property; gobject_class->dispose = clutter_colorize_effect_dispose; /** * ClutterColorizeEffect:tint: * * The tint to apply to the actor * * Since: 1.4 */ obj_props[PROP_TINT] = clutter_param_spec_color ("tint", P_("Tint"), P_("The tint to apply"), &default_tint, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void update_tint_uniform (ClutterColorizeEffect *self) { if (self->tint_uniform > -1) { float tint[3] = { self->tint.red / 255.0, self->tint.green / 255.0, self->tint.blue / 255.0 }; cogl_pipeline_set_uniform_float (self->pipeline, self->tint_uniform, 3, /* n_components */ 1, /* count */ tint); } } static void clutter_colorize_effect_init (ClutterColorizeEffect *self) { ClutterColorizeEffectClass *klass = CLUTTER_COLORIZE_EFFECT_GET_CLASS (self); if (G_UNLIKELY (klass->base_pipeline == NULL)) { CoglSnippet *snippet; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); klass->base_pipeline = cogl_pipeline_new (ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, colorize_glsl_declarations, colorize_glsl_source); cogl_pipeline_add_snippet (klass->base_pipeline, snippet); cogl_object_unref (snippet); cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0); } self->pipeline = cogl_pipeline_copy (klass->base_pipeline); self->tint_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "tint"); self->tint = default_tint; update_tint_uniform (self); } /** * clutter_colorize_effect_new: * @tint: the color to be used * * Creates a new #ClutterColorizeEffect to be used with * clutter_actor_add_effect() * * Return value: the newly created #ClutterColorizeEffect or %NULL * * Since: 1.4 */ ClutterEffect * clutter_colorize_effect_new (const ClutterColor *tint) { return g_object_new (CLUTTER_TYPE_COLORIZE_EFFECT, "tint", tint, NULL); } /** * clutter_colorize_effect_set_tint: * @effect: a #ClutterColorizeEffect * @tint: the color to be used * * Sets the tint to be used when colorizing * * Since: 1.4 */ void clutter_colorize_effect_set_tint (ClutterColorizeEffect *effect, const ClutterColor *tint) { g_return_if_fail (CLUTTER_IS_COLORIZE_EFFECT (effect)); effect->tint = *tint; update_tint_uniform (effect); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_TINT]); } /** * clutter_colorize_effect_get_tint: * @effect: a #ClutterColorizeEffect * @tint: (out caller-allocates): return location for the color used * * Retrieves the tint used by @effect * * Since: 1.4 */ void clutter_colorize_effect_get_tint (ClutterColorizeEffect *effect, ClutterColor *tint) { g_return_if_fail (CLUTTER_IS_COLORIZE_EFFECT (effect)); g_return_if_fail (tint != NULL); *tint = effect->tint; } muffin-6.4.1/clutter/clutter/clutter-offscreen-effect.h0000664000175000017500000001127214723361714022146 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_OFFSCREEN_EFFECT_H__ #define __CLUTTER_OFFSCREEN_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_OFFSCREEN_EFFECT (clutter_offscreen_effect_get_type ()) #define CLUTTER_OFFSCREEN_EFFECT(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), CLUTTER_TYPE_OFFSCREEN_EFFECT, ClutterOffscreenEffect)) #define CLUTTER_IS_OFFSCREEN_EFFECT(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), CLUTTER_TYPE_OFFSCREEN_EFFECT)) #define CLUTTER_OFFSCREEN_EFFECT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CLUTTER_TYPE_OFFSCREEN_EFFECT, ClutterOffscreenEffectClass)) #define CLUTTER_IS_OFFSCREEN_EFFECT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_OFFSCREEN_EFFECT)) #define CLUTTER_OFFSCREEN_EFFECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_OFFSCREEN_EFFECT, ClutterOffscreenEffectClass)) typedef struct _ClutterOffscreenEffect ClutterOffscreenEffect; typedef struct _ClutterOffscreenEffectPrivate ClutterOffscreenEffectPrivate; typedef struct _ClutterOffscreenEffectClass ClutterOffscreenEffectClass; /** * ClutterOffscreenEffect: * * The #ClutterOffscreenEffect structure contains only private data * and should be accessed using the provided API * * Since: 1.4 */ struct _ClutterOffscreenEffect { /*< private >*/ ClutterEffect parent_instance; ClutterOffscreenEffectPrivate *priv; }; /** * ClutterOffscreenEffectClass: * @create_texture: virtual function * @paint_target: virtual function * * The #ClutterOffscreenEffectClass structure contains only private data * * Since: 1.4 */ struct _ClutterOffscreenEffectClass { /*< private >*/ ClutterEffectClass parent_class; /*< public >*/ CoglHandle (* create_texture) (ClutterOffscreenEffect *effect, gfloat width, gfloat height); void (* paint_target) (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context); /*< private >*/ void (* _clutter_offscreen1) (void); void (* _clutter_offscreen2) (void); void (* _clutter_offscreen3) (void); void (* _clutter_offscreen4) (void); void (* _clutter_offscreen5) (void); void (* _clutter_offscreen6) (void); void (* _clutter_offscreen7) (void); }; CLUTTER_EXPORT GType clutter_offscreen_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT CoglMaterial * clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect); CLUTTER_EXPORT CoglHandle clutter_offscreen_effect_get_texture (ClutterOffscreenEffect *effect); CLUTTER_EXPORT void clutter_offscreen_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context); CLUTTER_EXPORT CoglHandle clutter_offscreen_effect_create_texture (ClutterOffscreenEffect *effect, gfloat width, gfloat height); CLUTTER_DEPRECATED_FOR (clutter_offscreen_effect_get_target_rect) gboolean clutter_offscreen_effect_get_target_size (ClutterOffscreenEffect *effect, gfloat *width, gfloat *height); CLUTTER_EXPORT gboolean clutter_offscreen_effect_get_target_rect (ClutterOffscreenEffect *effect, graphene_rect_t *rect); G_END_DECLS #endif /* __CLUTTER_OFFSCREEN_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-brightness-contrast-effect.c0000664000175000017500000005460514723361714024201 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010-2012 Inclusive Design Research Centre, OCAD University. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Joseph Scheuhammer */ /** * SECTION:clutter-brightness-contrast-effect * @short_description: Increase/decrease brightness and/or contrast of actor. * @see_also: #ClutterEffect, #ClutterOffscreenEffect * * #ClutterBrightnessContrastEffect is a sub-class of #ClutterEffect that * changes the overall brightness of a #ClutterActor. * * #ClutterBrightnessContrastEffect is available since Clutter 1.10 */ #define CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, ClutterBrightnessContrastEffectClass)) #define CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT)) #define CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, ClutterBrightnessContrastEffectClass)) #include "clutter-build-config.h" #include #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-brightness-contrast-effect.h" #include #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-offscreen-effect.h" #include "clutter-private.h" struct _ClutterBrightnessContrastEffect { ClutterOffscreenEffect parent_instance; /* Brightness and contrast changes. */ gfloat brightness_red; gfloat brightness_green; gfloat brightness_blue; gfloat contrast_red; gfloat contrast_green; gfloat contrast_blue; gint brightness_multiplier_uniform; gint brightness_offset_uniform; gint contrast_uniform; gint tex_width; gint tex_height; CoglPipeline *pipeline; }; struct _ClutterBrightnessContrastEffectClass { ClutterOffscreenEffectClass parent_class; CoglPipeline *base_pipeline; }; /* Brightness effects in GLSL. */ static const gchar *brightness_contrast_decls = "uniform vec3 brightness_multiplier;\n" "uniform vec3 brightness_offset;\n" "uniform vec3 contrast;\n"; static const gchar *brightness_contrast_source = /* Apply the brightness. The brightness_offset is multiplied by the alpha to keep the color pre-multiplied */ "cogl_color_out.rgb = (cogl_color_out.rgb * brightness_multiplier +\n" " brightness_offset * cogl_color_out.a);\n" /* Apply the contrast */ "cogl_color_out.rgb = ((cogl_color_out.rgb - 0.5 * cogl_color_out.a) *\n" " contrast + 0.5 * cogl_color_out.a);\n"; static const ClutterColor no_brightness_change = { 0x7f, 0x7f, 0x7f, 0xff }; static const ClutterColor no_contrast_change = { 0x7f, 0x7f, 0x7f, 0xff }; static const gfloat no_change = 0.0f; enum { PROP_0, PROP_BRIGHTNESS, PROP_CONTRAST, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterBrightnessContrastEffect, clutter_brightness_contrast_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); static gboolean will_have_no_effect (ClutterBrightnessContrastEffect *self) { return (G_APPROX_VALUE (self->brightness_red, no_change, FLT_EPSILON) && G_APPROX_VALUE (self->brightness_green, no_change, FLT_EPSILON) && G_APPROX_VALUE (self->brightness_blue, no_change, FLT_EPSILON) && G_APPROX_VALUE (self->contrast_red, no_change, FLT_EPSILON) && G_APPROX_VALUE (self->contrast_green, no_change, FLT_EPSILON) && G_APPROX_VALUE (self->contrast_blue, no_change, FLT_EPSILON)); } static gboolean clutter_brightness_contrast_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (effect); ClutterEffectClass *parent_class; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; if (will_have_no_effect (self)) return FALSE; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ClutterBrightnessContrastEffect: the " "graphics hardware or the current GL driver does not " "implement support for the GLSL shading language. The " "effect will be disabled."); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); return FALSE; } parent_class = CLUTTER_EFFECT_CLASS (clutter_brightness_contrast_effect_parent_class); if (parent_class->pre_paint (effect, paint_context)) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect); CoglHandle texture; texture = clutter_offscreen_effect_get_texture (offscreen_effect); self->tex_width = cogl_texture_get_width (texture); self->tex_height = cogl_texture_get_height (texture); cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); return TRUE; } else return FALSE; } static void clutter_brightness_contrast_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (effect); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); ClutterActor *actor; guint8 paint_opacity; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); paint_opacity = clutter_actor_get_paint_opacity (actor); cogl_pipeline_set_color4ub (self->pipeline, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_framebuffer_draw_rectangle (framebuffer, self->pipeline, 0, 0, self->tex_width, self->tex_height); } static void clutter_brightness_contrast_effect_dispose (GObject *gobject) { ClutterBrightnessContrastEffect *self = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject); if (self->pipeline != NULL) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } G_OBJECT_CLASS (clutter_brightness_contrast_effect_parent_class)->dispose (gobject); } static void clutter_brightness_contrast_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBrightnessContrastEffect *effect = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject); switch (prop_id) { case PROP_BRIGHTNESS: { const ClutterColor *color = clutter_value_get_color (value); clutter_brightness_contrast_effect_set_brightness_full (effect, color->red / 127.0f - 1.0f, color->green / 127.0f - 1.0f, color->blue / 127.0f - 1.0f); } break; case PROP_CONTRAST: { const ClutterColor *color = clutter_value_get_color (value); clutter_brightness_contrast_effect_set_contrast_full (effect, color->red / 127.0f - 1.0f, color->green / 127.0f - 1.0f, color->blue / 127.0f - 1.0f); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_brightness_contrast_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBrightnessContrastEffect *effect = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT (gobject); ClutterColor color; switch (prop_id) { case PROP_BRIGHTNESS: { color.red = (effect->brightness_red + 1.0f) * 127.0f; color.green = (effect->brightness_green + 1.0f) * 127.0f; color.blue = (effect->brightness_blue + 1.0f) * 127.0f; color.alpha = 0xff; clutter_value_set_color (value, &color); } break; case PROP_CONTRAST: { color.red = (effect->contrast_red + 1.0f) * 127.0f; color.green = (effect->contrast_green + 1.0f) * 127.0f; color.blue = (effect->contrast_blue + 1.0f) * 127.0f; color.alpha = 0xff; clutter_value_set_color (value, &color); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_brightness_contrast_effect_class_init (ClutterBrightnessContrastEffectClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->paint_target = clutter_brightness_contrast_effect_paint_target; effect_class->pre_paint = clutter_brightness_contrast_effect_pre_paint; gobject_class->set_property = clutter_brightness_contrast_effect_set_property; gobject_class->get_property = clutter_brightness_contrast_effect_get_property; gobject_class->dispose = clutter_brightness_contrast_effect_dispose; /** * ClutterBrightnessContrastEffect:brightness: * * The brightness change to apply to the effect. * * This property uses a #ClutterColor to represent the changes to each * color channel. The range is [ 0, 255 ], with 127 as the value used * to indicate no change; values smaller than 127 indicate a decrease * in brightness, and values larger than 127 indicate an increase in * brightness. * * Since: 1.10 */ obj_props[PROP_BRIGHTNESS] = clutter_param_spec_color ("brightness", P_("Brightness"), P_("The brightness change to apply"), &no_brightness_change, CLUTTER_PARAM_READWRITE); /** * ClutterBrightnessContrastEffect:contrast: * * The contrast change to apply to the effect. * * This property uses a #ClutterColor to represent the changes to each * color channel. The range is [ 0, 255 ], with 127 as the value used * to indicate no change; values smaller than 127 indicate a decrease * in contrast, and values larger than 127 indicate an increase in * contrast. * * Since: 1.10 */ obj_props[PROP_CONTRAST] = clutter_param_spec_color ("contrast", P_("Contrast"), P_("The contrast change to apply"), &no_contrast_change, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void get_brightness_values (gfloat value, gfloat *multiplier, gfloat *offset) { if (value < 0.0f) { *multiplier = 1.0f + value; *offset = 0.0f; } else { *multiplier = 1.0f - value; *offset = value; } } static inline void update_uniforms (ClutterBrightnessContrastEffect *self) { if (self->brightness_multiplier_uniform > -1 && self->brightness_offset_uniform > -1) { float brightness_multiplier[3]; float brightness_offset[3]; get_brightness_values (self->brightness_red, brightness_multiplier + 0, brightness_offset + 0); get_brightness_values (self->brightness_green, brightness_multiplier + 1, brightness_offset + 1); get_brightness_values (self->brightness_blue, brightness_multiplier + 2, brightness_offset + 2); cogl_pipeline_set_uniform_float (self->pipeline, self->brightness_multiplier_uniform, 3, /* n_components */ 1, /* count */ brightness_multiplier); cogl_pipeline_set_uniform_float (self->pipeline, self->brightness_offset_uniform, 3, /* n_components */ 1, /* count */ brightness_offset); } if (self->contrast_uniform > -1) { float contrast[3] = { tan ((self->contrast_red + 1) * G_PI_4), tan ((self->contrast_green + 1) * G_PI_4), tan ((self->contrast_blue + 1) * G_PI_4) }; cogl_pipeline_set_uniform_float (self->pipeline, self->contrast_uniform, 3, /* n_components */ 1, /* count */ contrast); } } static void clutter_brightness_contrast_effect_init (ClutterBrightnessContrastEffect *self) { ClutterBrightnessContrastEffectClass *klass; self->brightness_red = no_change; self->brightness_green = no_change; self->brightness_blue = no_change; self->contrast_red = no_change; self->contrast_green = no_change; self->contrast_blue = no_change; klass = CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_GET_CLASS (self); if (G_UNLIKELY (klass->base_pipeline == NULL)) { CoglSnippet *snippet; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); klass->base_pipeline = cogl_pipeline_new (ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, brightness_contrast_decls, brightness_contrast_source); cogl_pipeline_add_snippet (klass->base_pipeline, snippet); cogl_object_unref (snippet); cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0); } self->pipeline = cogl_pipeline_copy (klass->base_pipeline); self->brightness_multiplier_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "brightness_multiplier"); self->brightness_offset_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "brightness_offset"); self->contrast_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "contrast"); update_uniforms (self); } /** * clutter_brightness_contrast_effect_new: * * Creates a new #ClutterBrightnessContrastEffect to be used with * clutter_actor_add_effect() * * Return value: (transfer full): the newly created * #ClutterBrightnessContrastEffect or %NULL. Use g_object_unref() when * done. * * Since: 1.10 */ ClutterEffect * clutter_brightness_contrast_effect_new (void) { return g_object_new (CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, NULL); } /** * clutter_brightness_contrast_effect_set_brightness_full: * @effect: a #ClutterBrightnessContrastEffect * @red: red component of the change in brightness * @green: green component of the change in brightness * @blue: blue component of the change in brightness * * The range for each component is [-1.0, 1.0] where 0.0 designates no change, * values below 0.0 mean a decrease in brightness, and values above indicate * an increase. * * Since: 1.10 */ void clutter_brightness_contrast_effect_set_brightness_full (ClutterBrightnessContrastEffect *effect, gfloat red, gfloat green, gfloat blue) { g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect)); if (G_APPROX_VALUE (red, effect->brightness_red, FLT_EPSILON) && G_APPROX_VALUE (green, effect->brightness_green, FLT_EPSILON) && G_APPROX_VALUE (blue, effect->brightness_blue, FLT_EPSILON)) return; effect->brightness_red = red; effect->brightness_green = green; effect->brightness_blue = blue; update_uniforms (effect); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_BRIGHTNESS]); } /** * clutter_brightness_contrast_effect_get_brightness: * @effect: a #ClutterBrightnessContrastEffect * @red: (out) (allow-none): return location for red component of the * change in brightness * @green: (out) (allow-none): return location for green component of the * change in brightness * @blue: (out) (allow-none): return location for blue component of the * change in brightness * * Retrieves the change in brightness used by @effect. * * Since: 1.10 */ void clutter_brightness_contrast_effect_get_brightness (ClutterBrightnessContrastEffect *effect, gfloat *red, gfloat *green, gfloat *blue) { g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect)); if (red != NULL) *red = effect->brightness_red; if (green != NULL) *green = effect->brightness_green; if (blue != NULL) *blue = effect->brightness_blue; } /** * clutter_brightness_contrast_effect_set_brightness: * @effect: a #ClutterBrightnessContrastEffect * @brightness: the brightness change for all three components (r, g, b) * * The range of @brightness is [-1.0, 1.0], where 0.0 designates no change; * a value below 0.0 indicates a decrease in brightness; and a value * above 0.0 indicates an increase of brightness. * * Since: 1.10 */ void clutter_brightness_contrast_effect_set_brightness (ClutterBrightnessContrastEffect *effect, gfloat brightness) { clutter_brightness_contrast_effect_set_brightness_full (effect, brightness, brightness, brightness); } /** * clutter_brightness_contrast_effect_set_contrast_full: * @effect: a #ClutterBrightnessContrastEffect * @red: red component of the change in contrast * @green: green component of the change in contrast * @blue: blue component of the change in contrast * * The range for each component is [-1.0, 1.0] where 0.0 designates no change, * values below 0.0 mean a decrease in contrast, and values above indicate * an increase. * * Since: 1.10 */ void clutter_brightness_contrast_effect_set_contrast_full (ClutterBrightnessContrastEffect *effect, gfloat red, gfloat green, gfloat blue) { g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect)); if (G_APPROX_VALUE (red, effect->contrast_red, FLT_EPSILON) && G_APPROX_VALUE (green, effect->contrast_green, FLT_EPSILON) && G_APPROX_VALUE (blue, effect->contrast_blue, FLT_EPSILON)) return; effect->contrast_red = red; effect->contrast_green = green; effect->contrast_blue = blue; update_uniforms (effect); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_CONTRAST]); } /** * clutter_brightness_contrast_effect_get_contrast: * @effect: a #ClutterBrightnessContrastEffect * @red: (out) (allow-none): return location for red component of the * change in contrast * @green: (out) (allow-none): return location for green component of the * change in contrast * @blue: (out) (allow-none): return location for blue component of the * change in contrast * * Retrieves the contrast value used by @effect. * * Since: 1.10 */ void clutter_brightness_contrast_effect_get_contrast (ClutterBrightnessContrastEffect *effect, gfloat *red, gfloat *green, gfloat *blue) { g_return_if_fail (CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT (effect)); if (red != NULL) *red = effect->contrast_red; if (green != NULL) *green = effect->contrast_green; if (blue != NULL) *blue = effect->contrast_blue; } /** * clutter_brightness_contrast_effect_set_contrast: * @effect: a #ClutterBrightnessContrastEffect * @contrast: contrast change for all three channels * * The range for @contrast is [-1.0, 1.0], where 0.0 designates no change; * a value below 0.0 indicates a decrease in contrast; and a value above * 0.0 indicates an increase. * * Since: 1.10 */ void clutter_brightness_contrast_effect_set_contrast (ClutterBrightnessContrastEffect *effect, gfloat contrast) { clutter_brightness_contrast_effect_set_contrast_full (effect, contrast, contrast, contrast); } muffin-6.4.1/clutter/clutter/clutter-content.h0000664000175000017500000000651614723361714020421 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_CONTENT_H__ #define __CLUTTER_CONTENT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_CONTENT (clutter_content_get_type ()) CLUTTER_EXPORT G_DECLARE_INTERFACE (ClutterContent, clutter_content, CLUTTER, CONTENT, GObject) /** * ClutterContentInterface: * @get_preferred_size: virtual function; should be overridden by subclasses * of #ClutterContent that have a natural size * @paint_content: virtual function; called each time the content needs to * paint itself * @attached: virtual function; called each time a #ClutterContent is attached * to a #ClutterActor. * @detached: virtual function; called each time a #ClutterContent is detached * from a #ClutterActor. * @invalidate: virtual function; called each time a #ClutterContent state * is changed. * * The #ClutterContentInterface structure contains only * private data. * * Since: 1.10 */ struct _ClutterContentInterface { /*< private >*/ GTypeInterface g_iface; /*< public >*/ gboolean (* get_preferred_size) (ClutterContent *content, gfloat *width, gfloat *height); void (* paint_content) (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *node, ClutterPaintContext *paint_context); void (* attached) (ClutterContent *content, ClutterActor *actor); void (* detached) (ClutterContent *content, ClutterActor *actor); void (* invalidate) (ClutterContent *content); void (* invalidate_size) (ClutterContent *content); }; CLUTTER_EXPORT gboolean clutter_content_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height); CLUTTER_EXPORT void clutter_content_invalidate (ClutterContent *content); CLUTTER_EXPORT void clutter_content_invalidate_size (ClutterContent *content); G_END_DECLS #endif /* __CLUTTER_CONTENT_H__ */ muffin-6.4.1/clutter/clutter/clutter-types.h0000664000175000017500000004133114723361714020105 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_TYPES_H__ #define __CLUTTER_TYPES_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_ACTOR_BOX (clutter_actor_box_get_type ()) #define CLUTTER_TYPE_KNOT (clutter_knot_get_type ()) #define CLUTTER_TYPE_MARGIN (clutter_margin_get_type ()) #define CLUTTER_TYPE_MATRIX (clutter_matrix_get_type ()) #define CLUTTER_TYPE_PAINT_VOLUME (clutter_paint_volume_get_type ()) #define CLUTTER_TYPE_PERSPECTIVE (clutter_perspective_get_type ()) typedef struct _ClutterActor ClutterActor; typedef struct _ClutterStage ClutterStage; typedef struct _ClutterFrameInfo ClutterFrameInfo; typedef struct _ClutterContainer ClutterContainer; /* dummy */ typedef struct _ClutterChildMeta ClutterChildMeta; typedef struct _ClutterLayoutMeta ClutterLayoutMeta; typedef struct _ClutterActorMeta ClutterActorMeta; typedef struct _ClutterLayoutManager ClutterLayoutManager; typedef struct _ClutterActorIter ClutterActorIter; typedef struct _ClutterPaintNode ClutterPaintNode; typedef struct _ClutterContent ClutterContent; /* dummy */ typedef struct _ClutterScrollActor ClutterScrollActor; typedef struct _ClutterInterval ClutterInterval; typedef struct _ClutterAnimatable ClutterAnimatable; /* dummy */ typedef struct _ClutterTimeline ClutterTimeline; typedef struct _ClutterTransition ClutterTransition; typedef struct _ClutterPropertyTransition ClutterPropertyTransition; typedef struct _ClutterKeyframeTransition ClutterKeyframeTransition; typedef struct _ClutterTransitionGroup ClutterTransitionGroup; typedef struct _ClutterAction ClutterAction; typedef struct _ClutterConstraint ClutterConstraint; typedef struct _ClutterEffect ClutterEffect; typedef struct _ClutterPath ClutterPath; typedef struct _ClutterPathNode ClutterPathNode; typedef struct _ClutterActorBox ClutterActorBox; typedef struct _ClutterColor ClutterColor; typedef struct _ClutterKnot ClutterKnot; typedef struct _ClutterMargin ClutterMargin; typedef struct _ClutterPerspective ClutterPerspective; typedef struct _ClutterAlpha ClutterAlpha; typedef struct _ClutterAnimation ClutterAnimation; typedef struct _ClutterState ClutterState; typedef struct _ClutterInputDeviceTool ClutterInputDeviceTool; typedef struct _ClutterInputDevice ClutterInputDevice; typedef struct _ClutterVirtualInputDevice ClutterVirtualInputDevice; typedef struct _ClutterInputMethod ClutterInputMethod; typedef struct _ClutterInputFocus ClutterInputFocus; typedef CoglMatrix ClutterMatrix; typedef union _ClutterEvent ClutterEvent; /** * ClutterEventSequence: * * The #ClutterEventSequence structure is an opaque * type used to denote the event sequence of a touch event. * * Since: 1.12 */ typedef struct _ClutterEventSequence ClutterEventSequence; typedef struct _ClutterShader ClutterShader; /* deprecated */ /** * ClutterPaintVolume: * * #ClutterPaintVolume is an opaque structure * whose members cannot be directly accessed. * * A #ClutterPaintVolume represents an * a bounding volume whose internal representation isn't defined but * can be set and queried in terms of an axis aligned bounding box. * * A #ClutterPaintVolume for a #ClutterActor * is defined to be relative from the current actor modelview matrix. * * Other internal representation and methods for describing the * bounding volume may be added in the future. * * Since: 1.4 */ typedef struct _ClutterPaintVolume ClutterPaintVolume; /** * ClutterActorBox: * @x1: X coordinate of the top left corner * @y1: Y coordinate of the top left corner * @x2: X coordinate of the bottom right corner * @y2: Y coordinate of the bottom right corner * * Bounding box of an actor. The coordinates of the top left and right bottom * corners of an actor. The coordinates of the two points are expressed in * pixels with sub-pixel precision */ struct _ClutterActorBox { gfloat x1; gfloat y1; gfloat x2; gfloat y2; }; /** * CLUTTER_ACTOR_BOX_INIT: * @x_1: the X coordinate of the top left corner * @y_1: the Y coordinate of the top left corner * @x_2: the X coordinate of the bottom right corner * @y_2: the Y coordinate of the bottom right corner * * A simple macro for initializing a #ClutterActorBox when declaring * it, e.g.: * * |[ * ClutterActorBox box = CLUTTER_ACTOR_BOX_INIT (0, 0, 400, 600); * ]| * * Since: 1.10 */ #define CLUTTER_ACTOR_BOX_INIT(x_1,y_1,x_2,y_2) { (x_1), (y_1), (x_2), (y_2) } /** * CLUTTER_ACTOR_BOX_INIT_ZERO: * * A simple macro for initializing a #ClutterActorBox to 0 when * declaring it, e.g.: * * |[ * ClutterActorBox box = CLUTTER_ACTOR_BOX_INIT_ZERO; * ]| * * Since: 1.12 */ #define CLUTTER_ACTOR_BOX_INIT_ZERO CLUTTER_ACTOR_BOX_INIT (0.f, 0.f, 0.f, 0.f) CLUTTER_EXPORT GType clutter_actor_box_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterActorBox *clutter_actor_box_new (gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2); CLUTTER_EXPORT ClutterActorBox *clutter_actor_box_alloc (void); CLUTTER_EXPORT ClutterActorBox *clutter_actor_box_init (ClutterActorBox *box, gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2); CLUTTER_EXPORT void clutter_actor_box_init_rect (ClutterActorBox *box, gfloat x, gfloat y, gfloat width, gfloat height); CLUTTER_EXPORT ClutterActorBox *clutter_actor_box_copy (const ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_box_free (ClutterActorBox *box); CLUTTER_EXPORT gboolean clutter_actor_box_equal (const ClutterActorBox *box_a, const ClutterActorBox *box_b); CLUTTER_EXPORT gfloat clutter_actor_box_get_x (const ClutterActorBox *box); CLUTTER_EXPORT gfloat clutter_actor_box_get_y (const ClutterActorBox *box); CLUTTER_EXPORT gfloat clutter_actor_box_get_width (const ClutterActorBox *box); CLUTTER_EXPORT gfloat clutter_actor_box_get_height (const ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_box_get_origin (const ClutterActorBox *box, gfloat *x, gfloat *y); CLUTTER_EXPORT void clutter_actor_box_get_size (const ClutterActorBox *box, gfloat *width, gfloat *height); CLUTTER_EXPORT gfloat clutter_actor_box_get_area (const ClutterActorBox *box); CLUTTER_EXPORT gboolean clutter_actor_box_contains (const ClutterActorBox *box, gfloat x, gfloat y); CLUTTER_EXPORT void clutter_actor_box_from_vertices (ClutterActorBox *box, const graphene_point3d_t verts[]); CLUTTER_EXPORT void clutter_actor_box_interpolate (const ClutterActorBox *initial, const ClutterActorBox *final, gdouble progress, ClutterActorBox *result); CLUTTER_EXPORT void clutter_actor_box_clamp_to_pixel (ClutterActorBox *box); CLUTTER_EXPORT void clutter_actor_box_union (const ClutterActorBox *a, const ClutterActorBox *b, ClutterActorBox *result); CLUTTER_EXPORT void clutter_actor_box_set_origin (ClutterActorBox *box, gfloat x, gfloat y); CLUTTER_EXPORT void clutter_actor_box_set_size (ClutterActorBox *box, gfloat width, gfloat height); CLUTTER_EXPORT void clutter_actor_box_scale (ClutterActorBox *box, gfloat scale); /** * ClutterKnot: * @x: X coordinate of the knot * @y: Y coordinate of the knot * * Point in a path behaviour. * * Since: 0.2 */ struct _ClutterKnot { gint x; gint y; }; CLUTTER_EXPORT GType clutter_knot_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterKnot *clutter_knot_copy (const ClutterKnot *knot); CLUTTER_EXPORT void clutter_knot_free (ClutterKnot *knot); CLUTTER_EXPORT gboolean clutter_knot_equal (const ClutterKnot *knot_a, const ClutterKnot *knot_b); /** * ClutterPathNode: * @type: the node's type * @points: the coordinates of the node * * Represents a single node of a #ClutterPath. * * Some of the coordinates in @points may be unused for some node * types. %CLUTTER_PATH_MOVE_TO and %CLUTTER_PATH_LINE_TO use only one * pair of coordinates, %CLUTTER_PATH_CURVE_TO uses all three and * %CLUTTER_PATH_CLOSE uses none. * * Since: 1.0 */ struct _ClutterPathNode { ClutterPathNodeType type; ClutterKnot points[3]; }; CLUTTER_EXPORT GType clutter_path_node_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPathNode *clutter_path_node_copy (const ClutterPathNode *node); CLUTTER_EXPORT void clutter_path_node_free (ClutterPathNode *node); CLUTTER_EXPORT gboolean clutter_path_node_equal (const ClutterPathNode *node_a, const ClutterPathNode *node_b); /* * ClutterPaintVolume */ CLUTTER_EXPORT GType clutter_paint_volume_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterPaintVolume *clutter_paint_volume_copy (const ClutterPaintVolume *pv); CLUTTER_EXPORT void clutter_paint_volume_free (ClutterPaintVolume *pv); CLUTTER_EXPORT void clutter_paint_volume_set_origin (ClutterPaintVolume *pv, const graphene_point3d_t *origin); CLUTTER_EXPORT void clutter_paint_volume_get_origin (const ClutterPaintVolume *pv, graphene_point3d_t *vertex); CLUTTER_EXPORT void clutter_paint_volume_set_width (ClutterPaintVolume *pv, gfloat width); CLUTTER_EXPORT gfloat clutter_paint_volume_get_width (const ClutterPaintVolume *pv); CLUTTER_EXPORT void clutter_paint_volume_set_height (ClutterPaintVolume *pv, gfloat height); CLUTTER_EXPORT gfloat clutter_paint_volume_get_height (const ClutterPaintVolume *pv); CLUTTER_EXPORT void clutter_paint_volume_set_depth (ClutterPaintVolume *pv, gfloat depth); CLUTTER_EXPORT gfloat clutter_paint_volume_get_depth (const ClutterPaintVolume *pv); CLUTTER_EXPORT void clutter_paint_volume_union (ClutterPaintVolume *pv, const ClutterPaintVolume *another_pv); CLUTTER_EXPORT void clutter_paint_volume_union_box (ClutterPaintVolume *pv, const ClutterActorBox *box); CLUTTER_EXPORT gboolean clutter_paint_volume_set_from_allocation (ClutterPaintVolume *pv, ClutterActor *actor); /** * ClutterMargin: * @left: the margin from the left * @right: the margin from the right * @top: the margin from the top * @bottom: the margin from the bottom * * A representation of the components of a margin. * * Since: 1.10 */ struct _ClutterMargin { float left; float right; float top; float bottom; }; CLUTTER_EXPORT GType clutter_margin_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterMargin * clutter_margin_new (void) G_GNUC_MALLOC; CLUTTER_EXPORT ClutterMargin * clutter_margin_copy (const ClutterMargin *margin_); CLUTTER_EXPORT void clutter_margin_free (ClutterMargin *margin_); /** * ClutterProgressFunc: * @a: the initial value of an interval * @b: the final value of an interval * @progress: the progress factor, between 0 and 1 * @retval: the value used to store the progress * * Prototype of the progress function used to compute the value * between the two ends @a and @b of an interval depending on * the value of @progress. * * The #GValue in @retval is already initialized with the same * type as @a and @b. * * This function will be called by #ClutterInterval if the * type of the values of the interval was registered using * clutter_interval_register_progress_func(). * * Return value: %TRUE if the function successfully computed * the value and stored it inside @retval * * Since: 1.0 */ typedef gboolean (* ClutterProgressFunc) (const GValue *a, const GValue *b, gdouble progress, GValue *retval); CLUTTER_EXPORT void clutter_interval_register_progress_func (GType value_type, ClutterProgressFunc func); CLUTTER_EXPORT GType clutter_matrix_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterMatrix * clutter_matrix_alloc (void); CLUTTER_EXPORT ClutterMatrix * clutter_matrix_init_identity (ClutterMatrix *matrix); CLUTTER_EXPORT ClutterMatrix * clutter_matrix_init_from_array (ClutterMatrix *matrix, const float values[16]); CLUTTER_EXPORT ClutterMatrix * clutter_matrix_init_from_matrix (ClutterMatrix *a, const ClutterMatrix *b); CLUTTER_EXPORT void clutter_matrix_free (ClutterMatrix *matrix); G_END_DECLS #endif /* __CLUTTER_TYPES_H__ */ muffin-6.4.1/clutter/clutter/clutter-actor-meta.c0000664000175000017500000004067414723361714021001 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-actor-meta * @Title: ClutterActorMeta * @Short_Description: Base class of actor modifiers * @See_Also: #ClutterAction, #ClutterConstraint * * #ClutterActorMeta is an abstract class providing a common API for * modifiers of #ClutterActor behaviour, appearance or layout. * * A #ClutterActorMeta can only be owned by a single #ClutterActor at * any time. * * Every sub-class of #ClutterActorMeta should check if the * #ClutterActorMeta:enabled property is set to %TRUE before applying * any kind of modification. * * #ClutterActorMeta is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-actor-meta-private.h" #include "clutter-debug.h" #include "clutter-private.h" struct _ClutterActorMetaPrivate { ClutterActor *actor; gulong destroy_id; gchar *name; guint is_enabled : 1; gint priority; }; enum { PROP_0, PROP_ACTOR, PROP_NAME, PROP_ENABLED, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterActorMeta, clutter_actor_meta, G_TYPE_INITIALLY_UNOWNED) static void on_actor_destroy (ClutterActor *actor, ClutterActorMeta *meta) { meta->priv->actor = NULL; } static void clutter_actor_meta_real_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { if (meta->priv->actor == actor) return; g_clear_signal_handler (&meta->priv->destroy_id, meta->priv->actor); meta->priv->actor = actor; if (meta->priv->actor != NULL) meta->priv->destroy_id = g_signal_connect (meta->priv->actor, "destroy", G_CALLBACK (on_actor_destroy), meta); } static void clutter_actor_meta_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject); switch (prop_id) { case PROP_NAME: clutter_actor_meta_set_name (meta, g_value_get_string (value)); break; case PROP_ENABLED: clutter_actor_meta_set_enabled (meta, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_actor_meta_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject); switch (prop_id) { case PROP_ACTOR: g_value_set_object (value, meta->priv->actor); break; case PROP_NAME: g_value_set_string (value, meta->priv->name); break; case PROP_ENABLED: g_value_set_boolean (value, meta->priv->is_enabled); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_actor_meta_finalize (GObject *gobject) { ClutterActorMetaPrivate *priv = CLUTTER_ACTOR_META (gobject)->priv; if (priv->actor != NULL) g_clear_signal_handler (&priv->destroy_id, priv->actor); g_free (priv->name); G_OBJECT_CLASS (clutter_actor_meta_parent_class)->finalize (gobject); } void clutter_actor_meta_class_init (ClutterActorMetaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->set_actor = clutter_actor_meta_real_set_actor; /** * ClutterActorMeta:actor: * * The #ClutterActor attached to the #ClutterActorMeta instance * * Since: 1.4 */ obj_props[PROP_ACTOR] = g_param_spec_object ("actor", P_("Actor"), P_("The actor attached to the meta"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READABLE); /** * ClutterActorMeta:name: * * The unique name to access the #ClutterActorMeta * * Since: 1.4 */ obj_props[PROP_NAME] = g_param_spec_string ("name", P_("Name"), P_("The name of the meta"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterActorMeta:enabled: * * Whether or not the #ClutterActorMeta is enabled * * Since: 1.4 */ obj_props[PROP_ENABLED] = g_param_spec_boolean ("enabled", P_("Enabled"), P_("Whether the meta is enabled"), TRUE, CLUTTER_PARAM_READWRITE); gobject_class->finalize = clutter_actor_meta_finalize; gobject_class->set_property = clutter_actor_meta_set_property; gobject_class->get_property = clutter_actor_meta_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } void clutter_actor_meta_init (ClutterActorMeta *self) { self->priv = clutter_actor_meta_get_instance_private (self); self->priv->is_enabled = TRUE; self->priv->priority = CLUTTER_ACTOR_META_PRIORITY_DEFAULT; } /** * clutter_actor_meta_set_name: * @meta: a #ClutterActorMeta * @name: the name of @meta * * Sets the name of @meta * * The name can be used to identify the #ClutterActorMeta instance * * Since: 1.4 */ void clutter_actor_meta_set_name (ClutterActorMeta *meta, const gchar *name) { g_return_if_fail (CLUTTER_IS_ACTOR_META (meta)); if (g_strcmp0 (meta->priv->name, name) == 0) return; g_free (meta->priv->name); meta->priv->name = g_strdup (name); g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_NAME]); } /** * clutter_actor_meta_get_name: * @meta: a #ClutterActorMeta * * Retrieves the name set using clutter_actor_meta_set_name() * * Return value: (transfer none): the name of the #ClutterActorMeta * instance, or %NULL if none was set. The returned string is owned * by the #ClutterActorMeta instance and it should not be modified * or freed * * Since: 1.4 */ const gchar * clutter_actor_meta_get_name (ClutterActorMeta *meta) { g_return_val_if_fail (CLUTTER_IS_ACTOR_META (meta), NULL); return meta->priv->name; } /** * clutter_actor_meta_set_enabled: * @meta: a #ClutterActorMeta * @is_enabled: whether @meta is enabled * * Sets whether @meta should be enabled or not * * Since: 1.4 */ void clutter_actor_meta_set_enabled (ClutterActorMeta *meta, gboolean is_enabled) { g_return_if_fail (CLUTTER_IS_ACTOR_META (meta)); is_enabled = !!is_enabled; if (meta->priv->is_enabled == is_enabled) return; meta->priv->is_enabled = is_enabled; g_object_notify_by_pspec (G_OBJECT (meta), obj_props[PROP_ENABLED]); } /** * clutter_actor_meta_get_enabled: * @meta: a #ClutterActorMeta * * Retrieves whether @meta is enabled * * Return value: %TRUE if the #ClutterActorMeta instance is enabled * * Since: 1.4 */ gboolean clutter_actor_meta_get_enabled (ClutterActorMeta *meta) { g_return_val_if_fail (CLUTTER_IS_ACTOR_META (meta), FALSE); return meta->priv->is_enabled; } /* * _clutter_actor_meta_set_actor * @meta: a #ClutterActorMeta * @actor: a #ClutterActor or %NULL * * Sets or unsets a back pointer to the #ClutterActor that owns * the @meta * * Since: 1.4 */ void _clutter_actor_meta_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { g_return_if_fail (CLUTTER_IS_ACTOR_META (meta)); g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); CLUTTER_ACTOR_META_GET_CLASS (meta)->set_actor (meta, actor); } /** * clutter_actor_meta_get_actor: * @meta: a #ClutterActorMeta * * Retrieves a pointer to the #ClutterActor that owns @meta * * Return value: (transfer none): a pointer to a #ClutterActor or %NULL * * Since: 1.4 */ ClutterActor * clutter_actor_meta_get_actor (ClutterActorMeta *meta) { g_return_val_if_fail (CLUTTER_IS_ACTOR_META (meta), NULL); return meta->priv->actor; } void _clutter_actor_meta_set_priority (ClutterActorMeta *meta, gint priority) { g_return_if_fail (CLUTTER_IS_ACTOR_META (meta)); /* This property shouldn't be modified after the actor meta is in use because ClutterMetaGroup doesn't resort the list when it changes. If we made the priority public then we could either make the priority a construct-only property or listen for notifications on the property from the ClutterMetaGroup and resort. */ g_return_if_fail (meta->priv->actor == NULL); meta->priv->priority = priority; } gint _clutter_actor_meta_get_priority (ClutterActorMeta *meta) { g_return_val_if_fail (CLUTTER_IS_ACTOR_META (meta), 0); return meta->priv->priority; } gboolean _clutter_actor_meta_is_internal (ClutterActorMeta *meta) { gint priority = meta->priv->priority; return (priority <= CLUTTER_ACTOR_META_PRIORITY_INTERNAL_LOW || priority >= CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH); } /* * ClutterMetaGroup: a collection of ClutterActorMeta instances */ G_DEFINE_TYPE (ClutterMetaGroup, _clutter_meta_group, G_TYPE_OBJECT); static void _clutter_meta_group_dispose (GObject *gobject) { _clutter_meta_group_clear_metas (CLUTTER_META_GROUP (gobject)); G_OBJECT_CLASS (_clutter_meta_group_parent_class)->dispose (gobject); } static void _clutter_meta_group_class_init (ClutterMetaGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->dispose = _clutter_meta_group_dispose; } static void _clutter_meta_group_init (ClutterMetaGroup *self) { } /* * _clutter_meta_group_add_meta: * @group: a #ClutterMetaGroup * @meta: a #ClutterActorMeta to add * * Adds @meta to @group * * This function will remove the floating reference of @meta or, if the * floating reference has already been sunk, add a reference to it */ void _clutter_meta_group_add_meta (ClutterMetaGroup *group, ClutterActorMeta *meta) { GList *prev = NULL, *l; if (meta->priv->actor != NULL) { g_warning ("The meta of type '%s' with name '%s' is " "already attached to actor '%s'", G_OBJECT_TYPE_NAME (meta), meta->priv->name != NULL ? meta->priv->name : "", clutter_actor_get_name (meta->priv->actor) != NULL ? clutter_actor_get_name (meta->priv->actor) : G_OBJECT_TYPE_NAME (meta->priv->actor)); return; } /* Find a meta that has lower priority and insert before that */ for (l = group->meta; l; l = l->next) if (_clutter_actor_meta_get_priority (l->data) < _clutter_actor_meta_get_priority (meta)) break; else prev = l; if (prev == NULL) group->meta = g_list_prepend (group->meta, meta); else { prev->next = g_list_prepend (prev->next, meta); prev->next->prev = prev; } g_object_ref_sink (meta); _clutter_actor_meta_set_actor (meta, group->actor); } /* * _clutter_meta_group_remove_meta: * @group: a #ClutterMetaGroup * @meta: a #ClutterActorMeta to remove * * Removes @meta from @group and releases the reference being held on it */ void _clutter_meta_group_remove_meta (ClutterMetaGroup *group, ClutterActorMeta *meta) { if (meta->priv->actor != group->actor) { g_warning ("The meta of type '%s' with name '%s' is not " "attached to the actor '%s'", G_OBJECT_TYPE_NAME (meta), meta->priv->name != NULL ? meta->priv->name : "", clutter_actor_get_name (group->actor) != NULL ? clutter_actor_get_name (group->actor) : G_OBJECT_TYPE_NAME (group->actor)); return; } _clutter_actor_meta_set_actor (meta, NULL); group->meta = g_list_remove (group->meta, meta); g_object_unref (meta); } /* * _clutter_meta_group_peek_metas: * @group: a #ClutterMetaGroup * * Returns a pointer to the #ClutterActorMeta list * * Return value: a const pointer to the #GList of #ClutterActorMeta */ const GList * _clutter_meta_group_peek_metas (ClutterMetaGroup *group) { return group->meta; } /* * _clutter_meta_group_get_metas_no_internal: * @group: a #ClutterMetaGroup * * Returns a new allocated list containing all of the metas that don't * have an internal priority. * * Return value: A GList containing non-internal metas. Free with * g_list_free. */ GList * _clutter_meta_group_get_metas_no_internal (ClutterMetaGroup *group) { GList *ret = NULL; GList *l; /* Build a new list filtering out the internal metas */ for (l = group->meta; l; l = l->next) if (!_clutter_actor_meta_is_internal (l->data)) ret = g_list_prepend (ret, l->data); return g_list_reverse (ret); } /* * _clutter_meta_group_has_metas_no_internal: * @group: a #ClutterMetaGroup * * Returns whether the group has any metas that don't have an internal priority. * * Return value: %TRUE if metas without internal priority exist * %FALSE otherwise */ gboolean _clutter_meta_group_has_metas_no_internal (ClutterMetaGroup *group) { GList *l; for (l = group->meta; l; l = l->next) if (!_clutter_actor_meta_is_internal (l->data)) return TRUE; return FALSE; } /* * _clutter_meta_group_clear_metas: * @group: a #ClutterMetaGroup * * Clears @group of all #ClutterActorMeta instances and releases * the reference on them */ void _clutter_meta_group_clear_metas (ClutterMetaGroup *group) { g_list_foreach (group->meta, (GFunc) _clutter_actor_meta_set_actor, NULL); g_list_free_full (group->meta, g_object_unref); group->meta = NULL; } /* * _clutter_meta_group_clear_metas_no_internal: * @group: a #ClutterMetaGroup * * Clears @group of all #ClutterActorMeta instances that don't have an * internal priority and releases the reference on them */ void _clutter_meta_group_clear_metas_no_internal (ClutterMetaGroup *group) { GList *internal_list = NULL; GList *l, *next; for (l = group->meta; l; l = next) { next = l->next; if (_clutter_actor_meta_is_internal (l->data)) { if (internal_list) internal_list->prev = l; l->next = internal_list; l->prev = NULL; internal_list = l; } else { _clutter_actor_meta_set_actor (l->data, NULL); g_object_unref (l->data); g_list_free_1 (l); } } group->meta = g_list_reverse (internal_list); } /* * _clutter_meta_group_get_meta: * @group: a #ClutterMetaGroup * @name: the name of the #ClutterActorMeta to retrieve * * Retrieves a named #ClutterActorMeta from @group * * Return value: a #ClutterActorMeta for the given name, or %NULL */ ClutterActorMeta * _clutter_meta_group_get_meta (ClutterMetaGroup *group, const gchar *name) { GList *l; for (l = group->meta; l != NULL; l = l->next) { ClutterActorMeta *meta = l->data; if (g_strcmp0 (meta->priv->name, name) == 0) return meta; } return NULL; } /*< private > * clutter_actor_meta_get_debug_name: * @meta: a #ClutterActorMeta * * Retrieves the name of the @meta for debugging purposes. * * Return value: (transfer none): the name of the @meta. The returned * string is owned by the @meta instance and it should not be * modified or freed */ const gchar * _clutter_actor_meta_get_debug_name (ClutterActorMeta *meta) { return meta->priv->name != NULL ? meta->priv->name : G_OBJECT_TYPE_NAME (meta); } muffin-6.4.1/clutter/clutter/clutter-stage-manager.h0000664000175000017500000000604714723361714021461 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef __CLUTTER_STAGE_MANAGER_H__ #define __CLUTTER_STAGE_MANAGER_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_STAGE_MANAGER (clutter_stage_manager_get_type ()) #define CLUTTER_STAGE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManager)) #define CLUTTER_IS_STAGE_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_STAGE_MANAGER)) #define CLUTTER_STAGE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManagerClass)) #define CLUTTER_IS_STAGE_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_STAGE_MANAGER)) #define CLUTTER_STAGE_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_STAGE_MANAGER, ClutterStageManagerClass)) typedef struct _ClutterStageManager ClutterStageManager; typedef struct _ClutterStageManagerClass ClutterStageManagerClass; /** * ClutterStageManager: * * The #ClutterStageManager structure is private. * * Since: 1.0 */ /** * ClutterStageManagerClass: * * The #ClutterStageManagerClass structure contains only private data * and should be accessed using the provided API * * Since: 1.0 */ struct _ClutterStageManagerClass { /*< private >*/ GObjectClass parent_class; void (* stage_added) (ClutterStageManager *stage_manager, ClutterStage *stage); void (* stage_removed) (ClutterStageManager *stage_manager, ClutterStage *stage); }; CLUTTER_EXPORT GType clutter_stage_manager_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterStageManager *clutter_stage_manager_get_default (void); CLUTTER_EXPORT ClutterStage * clutter_stage_manager_get_default_stage (ClutterStageManager *stage_manager); CLUTTER_EXPORT GSList * clutter_stage_manager_list_stages (ClutterStageManager *stage_manager); CLUTTER_EXPORT const GSList * clutter_stage_manager_peek_stages (ClutterStageManager *stage_manager); G_END_DECLS #endif /* __CLUTTER_STAGE_MANAGER_H__ */ muffin-6.4.1/clutter/clutter/clutter-effect.h0000664000175000017500000001124714723361714020200 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_EFFECT_H__ #define __CLUTTER_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_EFFECT (clutter_effect_get_type ()) #define CLUTTER_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_EFFECT, ClutterEffect)) #define CLUTTER_IS_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_EFFECT)) #define CLUTTER_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_EFFECT, ClutterEffectClass)) #define CLUTTER_IS_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_EFFECT)) #define CLUTTER_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_EFFECT, ClutterEffectClass)) typedef struct _ClutterEffectClass ClutterEffectClass; /** * ClutterEffect: * * The #ClutterEffect structure contains only private data and should * be accessed using the provided API * * Since: 1.4 */ struct _ClutterEffect { /*< private >*/ ClutterActorMeta parent_instance; }; /** * ClutterEffectClass: * @pre_paint: virtual function * @post_paint: virtual function * @modify_paint_volume: virtual function * @paint: virtual function * @pick: virtual function * * The #ClutterEffectClass structure contains only private data * * Since: 1.4 */ struct _ClutterEffectClass { /*< private >*/ ClutterActorMetaClass parent_class; /*< public >*/ gboolean (* pre_paint) (ClutterEffect *effect, ClutterPaintContext *paint_context); void (* post_paint) (ClutterEffect *effect, ClutterPaintContext *paint_context); gboolean (* modify_paint_volume) (ClutterEffect *effect, ClutterPaintVolume *volume); void (* paint) (ClutterEffect *effect, ClutterPaintContext *paint_context, ClutterEffectPaintFlags flags); void (* pick) (ClutterEffect *effect, ClutterPickContext *pick_context); /*< private >*/ void (* _clutter_effect4) (void); void (* _clutter_effect5) (void); void (* _clutter_effect6) (void); }; CLUTTER_EXPORT GType clutter_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT void clutter_effect_queue_repaint (ClutterEffect *effect); /* * ClutterActor API */ CLUTTER_EXPORT void clutter_actor_add_effect (ClutterActor *self, ClutterEffect *effect); CLUTTER_EXPORT void clutter_actor_add_effect_with_name (ClutterActor *self, const gchar *name, ClutterEffect *effect); CLUTTER_EXPORT void clutter_actor_remove_effect (ClutterActor *self, ClutterEffect *effect); CLUTTER_EXPORT void clutter_actor_remove_effect_by_name (ClutterActor *self, const gchar *name); CLUTTER_EXPORT GList * clutter_actor_get_effects (ClutterActor *self); CLUTTER_EXPORT ClutterEffect *clutter_actor_get_effect (ClutterActor *self, const gchar *name); CLUTTER_EXPORT void clutter_actor_clear_effects (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_effects (ClutterActor *self); G_END_DECLS #endif /* __CLUTTER_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-master-clock-default.c0000664000175000017500000004664714723361714022761 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Emmanuele Bassi * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * SECTION:clutter-master-clock-default * @short_description: The default master clock for all animations * * The #ClutterMasterClockDefault class is the default implementation * of #ClutterMasterClock. */ #include "clutter-build-config.h" #include #include "clutter-master-clock.h" #include "clutter-master-clock-default.h" #include "clutter-debug.h" #include "clutter-private.h" #include "clutter-stage-manager-private.h" #include "clutter-stage-private.h" #ifdef CLUTTER_ENABLE_DEBUG #define clutter_warn_if_over_budget(master_clock,start_time,section) G_STMT_START { \ gint64 __delta = g_get_monotonic_time () - start_time; \ gint64 __budget = master_clock->remaining_budget; \ if (__budget > 0 && __delta >= __budget) { \ _clutter_diagnostic_message ("%s took %" G_GINT64_FORMAT " microseconds " \ "more than the remaining budget of %" G_GINT64_FORMAT \ " microseconds", \ section, __delta - __budget, __budget); \ } } G_STMT_END #else #define clutter_warn_if_over_budget(master_clock,start_time,section) #endif typedef struct _ClutterClockSource ClutterClockSource; struct _ClutterMasterClockDefault { GObject parent_instance; /* the list of timelines handled by the clock */ GSList *timelines; /* the current state of the clock, in usecs */ gint64 cur_tick; #ifdef CLUTTER_ENABLE_DEBUG gint64 frame_budget; gint64 remaining_budget; #endif /* an idle source, used by the Master Clock to queue * a redraw on the stage and drive the animations */ GSource *source; guint ensure_next_iteration : 1; guint paused : 1; }; struct _ClutterClockSource { GSource source; ClutterMasterClockDefault *master_clock; }; static gboolean clutter_clock_prepare (GSource *source, gint *timeout); static gboolean clutter_clock_check (GSource *source); static gboolean clutter_clock_dispatch (GSource *source, GSourceFunc callback, gpointer user_data); static GSourceFuncs clock_funcs = { clutter_clock_prepare, clutter_clock_check, clutter_clock_dispatch, NULL }; static void clutter_master_clock_iface_init (ClutterMasterClockInterface *iface); #define clutter_master_clock_default_get_type _clutter_master_clock_default_get_type G_DEFINE_TYPE_WITH_CODE (ClutterMasterClockDefault, clutter_master_clock_default, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_MASTER_CLOCK, clutter_master_clock_iface_init)); /* * master_clock_is_running: * @master_clock: a #ClutterMasterClock * * Checks if we should currently be advancing timelines or redrawing * stages. * * Return value: %TRUE if the #ClutterMasterClock has at least * one running timeline */ static gboolean master_clock_is_running (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; stages = clutter_stage_manager_peek_stages (stage_manager); if (master_clock->paused) return FALSE; if (master_clock->timelines) return TRUE; for (l = stages; l; l = l->next) { if (clutter_actor_is_mapped (l->data) && (_clutter_stage_has_queued_events (l->data) || _clutter_stage_needs_update (l->data))) return TRUE; } if (master_clock->ensure_next_iteration) { master_clock->ensure_next_iteration = FALSE; return TRUE; } return FALSE; } static gint master_clock_get_swap_wait_time (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; gint64 min_update_time = -1; stages = clutter_stage_manager_peek_stages (stage_manager); for (l = stages; l != NULL; l = l->next) { gint64 update_time = _clutter_stage_get_update_time (l->data); if (min_update_time == -1 || (update_time != -1 && update_time < min_update_time)) min_update_time = update_time; } if (min_update_time == -1) { return -1; } else { gint64 now = g_source_get_time (master_clock->source); if (min_update_time < now) { return 0; } else { gint64 delay_us = min_update_time - now; return (delay_us + 999) / 1000; } } } static int64_t master_clock_get_next_presentation_time (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; int64_t earliest = -1; stages = clutter_stage_manager_peek_stages (stage_manager); for (l = stages; l != NULL; l = l->next) { gint64 t = _clutter_stage_get_next_presentation_time (l->data); if (earliest == -1 || (t != -1 && t < earliest)) earliest = t; } return earliest; } static void master_clock_schedule_stage_updates (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; stages = clutter_stage_manager_peek_stages (stage_manager); for (l = stages; l != NULL; l = l->next) clutter_stage_schedule_update (l->data); } static GSList * master_clock_list_ready_stages (ClutterMasterClockDefault *master_clock) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; GSList *result; stages = clutter_stage_manager_peek_stages (stage_manager); result = NULL; for (l = stages; l != NULL; l = l->next) { gint64 update_time = _clutter_stage_get_update_time (l->data); /* We carefully avoid to update stages that aren't mapped, because * they have nothing to render and this could cause a deadlock with * some of the SwapBuffers implementations (in particular * GLX_INTEL_swap_event is not emitted if nothing was rendered). * * Also, if a stage has a swap-buffers pending we don't want to draw * to it in case the driver may block the CPU while it waits for the * next backbuffer to become available. * * TODO: We should be able to identify if we are running triple or N * buffered and in these cases we can still draw if there is 1 swap * pending so we can hopefully always be ready to swap for the next * vblank and really match the vsync frequency. */ if (clutter_actor_is_mapped (l->data) && update_time != -1 && update_time <= master_clock->cur_tick) result = g_slist_prepend (result, g_object_ref (l->data)); } return g_slist_reverse (result); } static void master_clock_reschedule_stage_updates (ClutterMasterClockDefault *master_clock, GSList *stages) { const GSList *l; for (l = stages; l != NULL; l = l->next) { /* Clear the old update time */ _clutter_stage_clear_update_time (l->data); /* And if there is still work to be done, schedule a new one */ if (master_clock->timelines || _clutter_stage_has_queued_events (l->data) || _clutter_stage_needs_update (l->data)) clutter_stage_schedule_update (l->data); } } /* * master_clock_next_frame_delay: * @master_clock: a #ClutterMasterClock * * Computes the number of delay before we need to draw the next frame. * * Return value: -1 if there is no next frame pending, otherwise the * number of millseconds before the we need to draw the next frame */ static gint master_clock_next_frame_delay (ClutterMasterClockDefault *master_clock) { if (!master_clock_is_running (master_clock)) return -1; /* If all of the stages are busy waiting for a swap-buffers to complete * then we wait for one to be ready.. */ return master_clock_get_swap_wait_time (master_clock); } static void master_clock_process_events (ClutterMasterClockDefault *master_clock, GSList *stages) { GSList *l; #ifdef CLUTTER_ENABLE_DEBUG gint64 start = g_get_monotonic_time (); #endif /* Process queued events */ for (l = stages; l != NULL; l = l->next) _clutter_stage_process_queued_events (l->data); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled ()) clutter_warn_if_over_budget (master_clock, start, "Event processing"); master_clock->remaining_budget -= (g_get_monotonic_time () - start); #endif } /* * master_clock_advance_timelines: * @master_clock: a #ClutterMasterClock * * Advances all the timelines held by the master clock. This function * should be called before calling _clutter_stage_do_update() to * make sure that all the timelines are advanced and the scene is updated. */ static void master_clock_advance_timelines (ClutterMasterClockDefault *master_clock) { GSList *timelines, *l; #ifdef CLUTTER_ENABLE_DEBUG gint64 start = g_get_monotonic_time (); #endif /* we protect ourselves from timelines being removed during * the advancement by other timelines by copying the list of * timelines, taking a reference on them, iterating over the * copied list and then releasing the reference. * * we cannot simply take a reference on the timelines and still * use the list held by the master clock because the do_tick() * might result in the creation of a new timeline, which gets * added at the end of the list with no reference increase and * thus gets disposed at the end of the iteration. * * this implies that a newly added timeline will not be advanced * by this clock iteration, which is perfectly fine since we're * in its first cycle. * * we also cannot steal the master clock timelines list because * a timeline might be removed as the direct result of do_tick() * and remove_timeline() would not find the timeline, failing * and leaving a dangling pointer behind. */ timelines = g_slist_copy (master_clock->timelines); g_slist_foreach (timelines, (GFunc) g_object_ref, NULL); for (l = timelines; l != NULL; l = l->next) _clutter_timeline_do_tick (l->data, master_clock->cur_tick / 1000); g_slist_free_full (timelines, g_object_unref); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled ()) clutter_warn_if_over_budget (master_clock, start, "Animations"); master_clock->remaining_budget -= (g_get_monotonic_time () - start); #endif } static gboolean master_clock_update_stages (ClutterMasterClockDefault *master_clock, GSList *stages) { gboolean stages_updated = FALSE; GSList *l; #ifdef CLUTTER_ENABLE_DEBUG gint64 start = g_get_monotonic_time (); #endif _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_PRE_PAINT); /* Update any stage that needs redraw/relayout after the clock * is advanced. */ for (l = stages; l != NULL; l = l->next) stages_updated |= _clutter_stage_do_update (l->data); _clutter_run_repaint_functions (CLUTTER_REPAINT_FLAGS_POST_PAINT); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled ()) clutter_warn_if_over_budget (master_clock, start, "Updating the stage"); master_clock->remaining_budget -= (g_get_monotonic_time () - start); #endif return stages_updated; } /* * clutter_clock_source_new: * @master_clock: a #ClutterMasterClock for the source * * The #ClutterClockSource is an idle GSource that will queue a redraw * if @master_clock has at least a running #ClutterTimeline. The redraw * will cause @master_clock to advance all timelines, thus advancing all * animations as well. * * Return value: the newly created #GSource */ static GSource * clutter_clock_source_new (ClutterMasterClockDefault *master_clock) { GSource *source = g_source_new (&clock_funcs, sizeof (ClutterClockSource)); ClutterClockSource *clock_source = (ClutterClockSource *) source; g_source_set_name (source, "Clutter master clock"); g_source_set_priority (source, CLUTTER_PRIORITY_REDRAW); g_source_set_can_recurse (source, FALSE); clock_source->master_clock = master_clock; return source; } static gboolean clutter_clock_prepare (GSource *source, gint *timeout) { ClutterClockSource *clock_source = (ClutterClockSource *) source; ClutterMasterClockDefault *master_clock = clock_source->master_clock; int delay; _clutter_threads_acquire_lock (); if (G_UNLIKELY (clutter_paint_debug_flags & CLUTTER_DEBUG_CONTINUOUS_REDRAW)) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); const GSList *stages, *l; stages = clutter_stage_manager_peek_stages (stage_manager); /* Queue a full redraw on all of the stages */ for (l = stages; l != NULL; l = l->next) clutter_actor_queue_redraw (l->data); } delay = master_clock_next_frame_delay (master_clock); _clutter_threads_release_lock (); *timeout = delay; return delay == 0; } static gboolean clutter_clock_check (GSource *source) { ClutterClockSource *clock_source = (ClutterClockSource *) source; ClutterMasterClockDefault *master_clock = clock_source->master_clock; int delay; _clutter_threads_acquire_lock (); delay = master_clock_next_frame_delay (master_clock); _clutter_threads_release_lock (); return delay == 0; } static gboolean clutter_clock_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { ClutterClockSource *clock_source = (ClutterClockSource *) source; ClutterMasterClockDefault *master_clock = clock_source->master_clock; GSList *stages; CLUTTER_NOTE (SCHEDULER, "Master clock [tick]"); _clutter_threads_acquire_lock (); COGL_TRACE_BEGIN (ClutterMasterClockTick, "Master Clock (tick)"); /* Get the time to use for this frame */ master_clock->cur_tick = master_clock_get_next_presentation_time (master_clock); /* On the first frame the backend might not have an answer */ if (master_clock->cur_tick <= 0) master_clock->cur_tick = g_source_get_time (source); #ifdef CLUTTER_ENABLE_DEBUG master_clock->remaining_budget = master_clock->frame_budget; #endif /* We need to protect ourselves against stages being destroyed during * event handling - master_clock_list_ready_stages() returns a * list of referenced that we'll unref afterwards. */ stages = master_clock_list_ready_stages (master_clock); /* Each frame is split into three separate phases: */ /* 1. process all the events; each stage goes through its events queue * and processes each event according to its type, then emits the * various signals that are associated with the event */ master_clock_process_events (master_clock, stages); /* 2. advance the timelines */ master_clock_advance_timelines (master_clock); /* 3. relayout and redraw the stages */ master_clock_update_stages (master_clock, stages); master_clock_reschedule_stage_updates (master_clock, stages); g_slist_free_full (stages, g_object_unref); COGL_TRACE_END (ClutterMasterClockTick); _clutter_threads_release_lock (); return TRUE; } static void clutter_master_clock_default_finalize (GObject *gobject) { ClutterMasterClockDefault *master_clock = CLUTTER_MASTER_CLOCK_DEFAULT (gobject); g_slist_free (master_clock->timelines); G_OBJECT_CLASS (clutter_master_clock_default_parent_class)->finalize (gobject); } static void clutter_master_clock_default_class_init (ClutterMasterClockDefaultClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = clutter_master_clock_default_finalize; } static void clutter_master_clock_default_init (ClutterMasterClockDefault *self) { GSource *source; source = clutter_clock_source_new (self); self->source = source; self->ensure_next_iteration = FALSE; self->paused = FALSE; #ifdef CLUTTER_ENABLE_DEBUG self->frame_budget = G_USEC_PER_SEC / 60; #endif g_source_attach (source, NULL); } static void clutter_master_clock_default_add_timeline (ClutterMasterClock *clock, ClutterTimeline *timeline) { ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock; gboolean is_first; if (g_slist_find (master_clock->timelines, timeline)) return; is_first = master_clock->timelines == NULL; master_clock->timelines = g_slist_prepend (master_clock->timelines, timeline); if (is_first) { master_clock_schedule_stage_updates (master_clock); _clutter_master_clock_start_running (clock); } } static void clutter_master_clock_default_remove_timeline (ClutterMasterClock *clock, ClutterTimeline *timeline) { ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock; master_clock->timelines = g_slist_remove (master_clock->timelines, timeline); } static void clutter_master_clock_default_start_running (ClutterMasterClock *master_clock) { /* If called from a different thread, we need to wake up the * main loop to start running the timelines */ g_main_context_wakeup (NULL); } static void clutter_master_clock_default_ensure_next_iteration (ClutterMasterClock *clock) { ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock; master_clock->ensure_next_iteration = TRUE; } static void clutter_master_clock_default_set_paused (ClutterMasterClock *clock, gboolean paused) { ClutterMasterClockDefault *master_clock = (ClutterMasterClockDefault *) clock; if (paused && !master_clock->paused) { g_clear_pointer (&master_clock->source, g_source_destroy); } else if (!paused && master_clock->paused) { master_clock->source = clutter_clock_source_new (master_clock); g_source_attach (master_clock->source, NULL); } master_clock->paused = !!paused; } static void clutter_master_clock_iface_init (ClutterMasterClockInterface *iface) { iface->add_timeline = clutter_master_clock_default_add_timeline; iface->remove_timeline = clutter_master_clock_default_remove_timeline; iface->start_running = clutter_master_clock_default_start_running; iface->ensure_next_iteration = clutter_master_clock_default_ensure_next_iteration; iface->set_paused = clutter_master_clock_default_set_paused; } muffin-6.4.1/clutter/clutter/clutter-settings.c0000664000175000017500000005703714723361714020606 0ustar fabiofabio/** * SECTION:clutter-settings * @Title: ClutterSettings * @Short_Description: Settings configuration * * Clutter depends on some settings to perform operations like detecting * multiple button press events, or font options to render text. * * Usually, Clutter will strive to use the platform's settings in order * to be as much integrated as possible. It is, however, possible to * change these settings on a per-application basis, by using the * #ClutterSettings singleton object and setting its properties. It is * also possible, for toolkit developers, to retrieve the settings from * the #ClutterSettings properties when implementing new UI elements, * for instance the default font name. * * #ClutterSettings is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-settings.h" #ifdef HAVE_PANGO_FT2 /* for pango_fc_font_map_cache_clear() */ #define PANGO_ENABLE_BACKEND #include #endif /* HAVE_PANGO_FT2 */ #include "clutter-debug.h" #include "clutter-settings-private.h" #include "clutter-stage-private.h" #include "clutter-private.h" #include #define DEFAULT_FONT_NAME "Sans 12" #define CLUTTER_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_SETTINGS, ClutterSettingsClass)) #define CLUTTER_IS_SETTINGS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_SETTINGS)) #define CLUTTER_SETTINGS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_SETTINGS, ClutterSettingsClass)) /** * ClutterSettings: * * `ClutterSettings` is an opaque structure whose * members cannot be directly accessed. * * Since: 1.4 */ struct _ClutterSettings { GObject parent_instance; ClutterBackend *backend; gint double_click_time; gint double_click_distance; gint dnd_drag_threshold; gdouble resolution; gchar *font_name; gint font_dpi; gint xft_hinting; gint xft_antialias; gchar *xft_hint_style; gchar *xft_rgba; gint long_press_duration; guint last_fontconfig_timestamp; guint password_hint_time; gint unscaled_font_dpi; }; struct _ClutterSettingsClass { GObjectClass parent_class; }; enum { PROP_0, PROP_BACKEND, PROP_DOUBLE_CLICK_TIME, PROP_DOUBLE_CLICK_DISTANCE, PROP_DND_DRAG_THRESHOLD, PROP_FONT_NAME, PROP_FONT_ANTIALIAS, PROP_FONT_DPI, PROP_FONT_HINTING, PROP_FONT_HINT_STYLE, PROP_FONT_RGBA, PROP_LONG_PRESS_DURATION, PROP_FONTCONFIG_TIMESTAMP, PROP_PASSWORD_HINT_TIME, PROP_UNSCALED_FONT_DPI, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterSettings, clutter_settings, G_TYPE_OBJECT); static inline void settings_update_font_options (ClutterSettings *self) { cairo_hint_style_t hint_style = CAIRO_HINT_STYLE_NONE; cairo_antialias_t antialias_mode = CAIRO_ANTIALIAS_GRAY; cairo_subpixel_order_t subpixel_order = CAIRO_SUBPIXEL_ORDER_DEFAULT; cairo_font_options_t *options; if (self->backend == NULL) return; options = cairo_font_options_create (); cairo_font_options_set_hint_metrics (options, CAIRO_HINT_METRICS_ON); if (self->xft_hinting >= 0 && self->xft_hint_style == NULL) { hint_style = CAIRO_HINT_STYLE_NONE; } else if (self->xft_hint_style != NULL) { if (strcmp (self->xft_hint_style, "hintnone") == 0) hint_style = CAIRO_HINT_STYLE_NONE; else if (strcmp (self->xft_hint_style, "hintslight") == 0) hint_style = CAIRO_HINT_STYLE_SLIGHT; else if (strcmp (self->xft_hint_style, "hintmedium") == 0) hint_style = CAIRO_HINT_STYLE_MEDIUM; else if (strcmp (self->xft_hint_style, "hintfull") == 0) hint_style = CAIRO_HINT_STYLE_FULL; } cairo_font_options_set_hint_style (options, hint_style); if (self->xft_rgba) { if (strcmp (self->xft_rgba, "rgb") == 0) subpixel_order = CAIRO_SUBPIXEL_ORDER_RGB; else if (strcmp (self->xft_rgba, "bgr") == 0) subpixel_order = CAIRO_SUBPIXEL_ORDER_BGR; else if (strcmp (self->xft_rgba, "vrgb") == 0) subpixel_order = CAIRO_SUBPIXEL_ORDER_VRGB; else if (strcmp (self->xft_rgba, "vbgr") == 0) subpixel_order = CAIRO_SUBPIXEL_ORDER_VBGR; } cairo_font_options_set_subpixel_order (options, subpixel_order); if (self->xft_antialias >= 0 && !self->xft_antialias) antialias_mode = CAIRO_ANTIALIAS_NONE; else if (subpixel_order != CAIRO_SUBPIXEL_ORDER_DEFAULT) antialias_mode = CAIRO_ANTIALIAS_SUBPIXEL; else if (self->xft_antialias >= 0) antialias_mode = CAIRO_ANTIALIAS_GRAY; cairo_font_options_set_antialias (options, antialias_mode); CLUTTER_NOTE (BACKEND, "New font options:\n" " - font-name: %s\n" " - antialias: %d\n" " - hinting: %d\n" " - hint-style: %s\n" " - rgba: %s\n", self->font_name != NULL ? self->font_name : DEFAULT_FONT_NAME, self->xft_antialias, self->xft_hinting, self->xft_hint_style != NULL ? self->xft_hint_style : "", self->xft_rgba != NULL ? self->xft_rgba : ""); clutter_backend_set_font_options (self->backend, options); cairo_font_options_destroy (options); } static void settings_update_font_name (ClutterSettings *self) { CLUTTER_NOTE (BACKEND, "New font-name: %s", self->font_name); if (self->backend != NULL) g_signal_emit_by_name (self->backend, "font-changed"); } static void settings_update_resolution (ClutterSettings *self) { const char *scale_env = NULL; if (self->font_dpi > 0) self->resolution = (gdouble) self->font_dpi / 1024.0; else self->resolution = 96.0; scale_env = g_getenv ("GDK_DPI_SCALE"); if (scale_env != NULL) { double scale = g_ascii_strtod (scale_env, NULL); if (scale != 0 && self->resolution > 0) self->resolution *= scale; } CLUTTER_NOTE (BACKEND, "New resolution: %.2f (%s)", self->resolution, self->unscaled_font_dpi > 0 ? "unscaled" : "scaled"); if (self->backend != NULL) g_signal_emit_by_name (self->backend, "resolution-changed"); } static void settings_update_fontmap (ClutterSettings *self, guint stamp) { if (self->backend == NULL) return; #ifdef HAVE_PANGO_FT2 CLUTTER_NOTE (BACKEND, "Update fontmaps (stamp: %d)", stamp); if (self->last_fontconfig_timestamp != stamp) { ClutterMainContext *context; gboolean update_needed = FALSE; context = _clutter_context_get_default (); /* If there is no font map yet then we don't need to do anything * because the config for fontconfig will be read when it is * created */ if (context->font_map) { PangoFontMap *fontmap = PANGO_FONT_MAP (context->font_map); if (PANGO_IS_FC_FONT_MAP (fontmap) && !FcConfigUptoDate (NULL)) { pango_fc_font_map_cache_clear (PANGO_FC_FONT_MAP (fontmap)); if (FcInitReinitialize ()) update_needed = TRUE; } } self->last_fontconfig_timestamp = stamp; if (update_needed) g_signal_emit_by_name (self->backend, "font-changed"); } #endif /* HAVE_PANGO_FT2 */ } static void clutter_settings_finalize (GObject *gobject) { ClutterSettings *self = CLUTTER_SETTINGS (gobject); g_free (self->font_name); g_free (self->xft_hint_style); g_free (self->xft_rgba); G_OBJECT_CLASS (clutter_settings_parent_class)->finalize (gobject); } static void clutter_settings_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterSettings *self = CLUTTER_SETTINGS (gobject); switch (prop_id) { case PROP_BACKEND: self->backend = g_value_get_object (value); break; case PROP_DOUBLE_CLICK_TIME: self->double_click_time = g_value_get_int (value); break; case PROP_DOUBLE_CLICK_DISTANCE: self->double_click_distance = g_value_get_int (value); break; case PROP_DND_DRAG_THRESHOLD: self->dnd_drag_threshold = g_value_get_int (value); break; case PROP_FONT_NAME: g_free (self->font_name); self->font_name = g_value_dup_string (value); settings_update_font_name (self); break; case PROP_FONT_ANTIALIAS: self->xft_antialias = g_value_get_int (value); settings_update_font_options (self); break; case PROP_FONT_DPI: self->font_dpi = g_value_get_int (value); settings_update_resolution (self); break; case PROP_FONT_HINTING: self->xft_hinting = g_value_get_int (value); settings_update_font_options (self); break; case PROP_FONT_HINT_STYLE: g_free (self->xft_hint_style); self->xft_hint_style = g_value_dup_string (value); settings_update_font_options (self); break; case PROP_FONT_RGBA: g_free (self->xft_rgba); self->xft_rgba = g_value_dup_string (value); settings_update_font_options (self); break; case PROP_LONG_PRESS_DURATION: self->long_press_duration = g_value_get_int (value); break; case PROP_FONTCONFIG_TIMESTAMP: settings_update_fontmap (self, g_value_get_uint (value)); break; case PROP_PASSWORD_HINT_TIME: self->password_hint_time = g_value_get_uint (value); break; case PROP_UNSCALED_FONT_DPI: self->font_dpi = g_value_get_int (value); settings_update_resolution (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } void clutter_settings_set_property_internal (ClutterSettings *self, const char *property, GValue *value) { property = g_intern_string (property); g_object_set_property (G_OBJECT (self), property, value); } static void clutter_settings_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterSettings *self = CLUTTER_SETTINGS (gobject); switch (prop_id) { case PROP_DOUBLE_CLICK_TIME: g_value_set_int (value, self->double_click_time); break; case PROP_DOUBLE_CLICK_DISTANCE: g_value_set_int (value, self->double_click_distance); break; case PROP_DND_DRAG_THRESHOLD: g_value_set_int (value, self->dnd_drag_threshold); break; case PROP_FONT_NAME: g_value_set_string (value, self->font_name); break; case PROP_FONT_ANTIALIAS: g_value_set_int (value, self->xft_antialias); break; case PROP_FONT_DPI: g_value_set_int (value, self->resolution * 1024); break; case PROP_FONT_HINTING: g_value_set_int (value, self->xft_hinting); break; case PROP_FONT_HINT_STYLE: g_value_set_string (value, self->xft_hint_style); break; case PROP_FONT_RGBA: g_value_set_string (value, self->xft_rgba); break; case PROP_LONG_PRESS_DURATION: g_value_set_int (value, self->long_press_duration); break; case PROP_PASSWORD_HINT_TIME: g_value_set_uint (value, self->password_hint_time); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_settings_dispatch_properties_changed (GObject *gobject, guint n_pspecs, GParamSpec **pspecs) { ClutterSettings *self = CLUTTER_SETTINGS (gobject); GObjectClass *klass; /* chain up to emit ::notify */ klass = G_OBJECT_CLASS (clutter_settings_parent_class); klass->dispatch_properties_changed (gobject, n_pspecs, pspecs); /* emit settings-changed just once for multiple properties */ if (self->backend != NULL) g_signal_emit_by_name (self->backend, "settings-changed"); } static void clutter_settings_class_init (ClutterSettingsClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /** * ClutterSettings:backend: * * A back pointer to the #ClutterBackend * * Since: 1.4 * * Deprecated: 1.10 */ obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "Backend", "A pointer to the backend", CLUTTER_TYPE_BACKEND, CLUTTER_PARAM_WRITABLE | G_PARAM_DEPRECATED | G_PARAM_CONSTRUCT_ONLY); /** * ClutterSettings:double-click-time: * * The time, in milliseconds, that should elapse between button-press * events in order to increase the click count by 1. * * Since: 1.4 */ obj_props[PROP_DOUBLE_CLICK_TIME] = g_param_spec_int ("double-click-time", P_("Double Click Time"), P_("The time between clicks necessary to detect a multiple click"), 0, G_MAXINT, 400, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:double-click-distance: * * The maximum distance, in pixels, between button-press events that * determines whether or not to increase the click count by 1. * * Since: 1.4 */ obj_props[PROP_DOUBLE_CLICK_DISTANCE] = g_param_spec_int ("double-click-distance", P_("Double Click Distance"), P_("The distance between clicks necessary to detect a multiple click"), 0, G_MAXINT, 5, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:dnd-drag-threshold: * * The default distance that the cursor of a pointer device * should travel before a drag operation should start. * * Since: 1.8 */ obj_props[PROP_DND_DRAG_THRESHOLD] = g_param_spec_int ("dnd-drag-threshold", P_("Drag Threshold"), P_("The distance the cursor should travel before starting to drag"), 1, G_MAXINT, 8, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:font-name: * * The default font name that should be used by text actors, as * a string that can be passed to pango_font_description_from_string(). * * Since: 1.4 */ obj_props[PROP_FONT_NAME] = g_param_spec_string ("font-name", P_("Font Name"), P_("The description of the default font, as one that could be parsed by Pango"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:font-antialias: * * Whether or not to use antialiasing when rendering text; a value * of 1 enables it unconditionally; a value of 0 disables it * unconditionally; and -1 will use the system's default. * * Since: 1.4 */ obj_props[PROP_FONT_ANTIALIAS] = g_param_spec_int ("font-antialias", P_("Font Antialias"), P_("Whether to use antialiasing (1 to enable, 0 to disable, and -1 to use the default)"), -1, 1, -1, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:font-dpi: * * The DPI used when rendering text, as a value of 1024 * dots/inch. * * If set to -1, the system's default will be used instead * * Since: 1.4 */ obj_props[PROP_FONT_DPI] = g_param_spec_int ("font-dpi", P_("Font DPI"), P_("The resolution of the font, in 1024 * dots/inch, or -1 to use the default"), -1, 1024 * 1024, -1, CLUTTER_PARAM_READWRITE); obj_props[PROP_UNSCALED_FONT_DPI] = g_param_spec_int ("unscaled-font-dpi", P_("Font DPI"), P_("The resolution of the font, in 1024 * dots/inch, or -1 to use the default"), -1, 1024 * 1024, -1, CLUTTER_PARAM_WRITABLE); /** * ClutterSettings:font-hinting: * * Whether or not to use hinting when rendering text; a value of 1 * unconditionally enables it; a value of 0 unconditionally disables * it; and a value of -1 will use the system's default. * * Since: 1.4 */ obj_props[PROP_FONT_HINTING] = g_param_spec_int ("font-hinting", P_("Font Hinting"), P_("Whether to use hinting (1 to enable, 0 to disable and -1 to use the default)"), -1, 1, -1, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:font-hint-style: * * The style of the hinting used when rendering text. Valid values * are: * * - hintnone * - hintslight * - hintmedium * - hintfull * * Since: 1.4 */ obj_props[PROP_FONT_HINT_STYLE] = g_param_spec_string ("font-hint-style", P_("Font Hint Style"), P_("The style of hinting (hintnone, hintslight, hintmedium, hintfull)"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:font-subpixel-order: * * The type of sub-pixel antialiasing used when rendering text. Valid * values are: * * - none * - rgb * - bgr * - vrgb * - vbgr * * Since: 1.4 */ obj_props[PROP_FONT_RGBA] = g_param_spec_string ("font-subpixel-order", P_("Font Subpixel Order"), P_("The type of subpixel antialiasing (none, rgb, bgr, vrgb, vbgr)"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterSettings:long-press-duration: * * Sets the minimum duration for a press to be recognized as a long press * gesture. The duration is expressed in milliseconds. * * See also #ClutterClickAction:long-press-duration. * * Since: 1.8 */ obj_props[PROP_LONG_PRESS_DURATION] = g_param_spec_int ("long-press-duration", P_("Long Press Duration"), P_("The minimum duration for a long press gesture to be recognized"), 0, G_MAXINT, 500, CLUTTER_PARAM_READWRITE); obj_props[PROP_FONTCONFIG_TIMESTAMP] = g_param_spec_uint ("fontconfig-timestamp", P_("Fontconfig configuration timestamp"), P_("Timestamp of the current fontconfig configuration"), 0, G_MAXUINT, 0, CLUTTER_PARAM_WRITABLE); /** * ClutterText:password-hint-time: * * How long should Clutter show the last input character in editable * ClutterText actors. The value is in milliseconds. A value of 0 * disables showing the password hint. 600 is a good value for * enabling the hint. * * Since: 1.10 */ obj_props[PROP_PASSWORD_HINT_TIME] = g_param_spec_uint ("password-hint-time", P_("Password Hint Time"), P_("How long to show the last input character in hidden entries"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); gobject_class->set_property = clutter_settings_set_property; gobject_class->get_property = clutter_settings_get_property; gobject_class->dispatch_properties_changed = clutter_settings_dispatch_properties_changed; gobject_class->finalize = clutter_settings_finalize; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_settings_init (ClutterSettings *self) { self->resolution = -1.0; self->font_dpi = -1; self->unscaled_font_dpi = -1; self->double_click_time = 400; self->double_click_distance = 5; self->dnd_drag_threshold = 8; self->font_name = g_strdup (DEFAULT_FONT_NAME); self->xft_antialias = -1; self->xft_hinting = -1; self->xft_hint_style = NULL; self->xft_rgba = NULL; self->long_press_duration = 500; } /** * clutter_settings_get_default: * * Retrieves the singleton instance of #ClutterSettings * * Return value: (transfer none): the instance of #ClutterSettings. The * returned object is owned by Clutter and it should not be unreferenced * directly * * Since: 1.4 */ ClutterSettings * clutter_settings_get_default (void) { static ClutterSettings *settings = NULL; if (G_UNLIKELY (settings == NULL)) settings = g_object_new (CLUTTER_TYPE_SETTINGS, NULL); return settings; } void _clutter_settings_set_backend (ClutterSettings *settings, ClutterBackend *backend) { g_assert (CLUTTER_IS_SETTINGS (settings)); g_assert (CLUTTER_IS_BACKEND (backend)); settings->backend = backend; } #define SETTINGS_GROUP "Settings" void _clutter_settings_read_from_key_file (ClutterSettings *settings, GKeyFile *keyfile) { GObjectClass *settings_class; GObject *settings_obj; GParamSpec **pspecs; guint n_pspecs, i; if (!g_key_file_has_group (keyfile, SETTINGS_GROUP)) return; settings_obj = G_OBJECT (settings); settings_class = G_OBJECT_GET_CLASS (settings); pspecs = g_object_class_list_properties (settings_class, &n_pspecs); for (i = 0; i < n_pspecs; i++) { GParamSpec *pspec = pspecs[i]; const gchar *p_name = pspec->name; GType p_type = G_TYPE_FUNDAMENTAL (pspec->value_type); GValue value = G_VALUE_INIT; GError *key_error = NULL; g_value_init (&value, p_type); switch (p_type) { case G_TYPE_INT: case G_TYPE_UINT: { gint val; val = g_key_file_get_integer (keyfile, SETTINGS_GROUP, p_name, &key_error); if (p_type == G_TYPE_INT) g_value_set_int (&value, val); else g_value_set_uint (&value, val); } break; case G_TYPE_BOOLEAN: { gboolean val; val = g_key_file_get_boolean (keyfile, SETTINGS_GROUP, p_name, &key_error); g_value_set_boolean (&value, val); } break; case G_TYPE_FLOAT: case G_TYPE_DOUBLE: { gdouble val; val = g_key_file_get_double (keyfile, SETTINGS_GROUP, p_name, &key_error); if (p_type == G_TYPE_FLOAT) g_value_set_float (&value, val); else g_value_set_double (&value, val); } break; case G_TYPE_STRING: { gchar *val; val = g_key_file_get_string (keyfile, SETTINGS_GROUP, p_name, &key_error); g_value_take_string (&value, val); } break; } if (key_error != NULL && key_error->domain != G_KEY_FILE_ERROR && key_error->code != G_KEY_FILE_ERROR_KEY_NOT_FOUND) { g_critical ("Unable to read the value for setting '%s': %s", p_name, key_error->message); } if (key_error == NULL) g_object_set_property (settings_obj, p_name, &value); else g_error_free (key_error); g_value_unset (&value); } g_free (pspecs); } muffin-6.4.1/clutter/clutter/clutter-bezier.h0000664000175000017500000000435014723361714020221 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Tomas Frydrych * * Copyright (C) 2006, 2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_BEZIER_H__ #define __CLUTTER_BEZIER_H__ #include #include "clutter-types.h" G_BEGIN_DECLS /* This is used in _clutter_bezier_advance to represent the full length of the bezier curve. Anything less than that represents a fraction of the length */ #define CLUTTER_BEZIER_MAX_LENGTH (1 << 18) typedef struct _ClutterBezier ClutterBezier; ClutterBezier *_clutter_bezier_new (void); void _clutter_bezier_free (ClutterBezier * b); ClutterBezier *_clutter_bezier_clone_and_move (const ClutterBezier *b, gint x, gint y); void _clutter_bezier_advance (const ClutterBezier *b, gint L, ClutterKnot *knot); void _clutter_bezier_init (ClutterBezier *b, gint x_0, gint y_0, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3); void _clutter_bezier_adjust (ClutterBezier *b, ClutterKnot *knot, guint indx); guint _clutter_bezier_get_length (const ClutterBezier *b); G_END_DECLS #endif /* __CLUTTER_BEZIER_H__ */ muffin-6.4.1/clutter/clutter/clutter-rotate-action.h0000664000175000017500000000645414723361714021521 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Lionel Landwerlin */ #ifndef __CLUTTER_ROTATE_ACTION_H__ #define __CLUTTER_ROTATE_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_ROTATE_ACTION (clutter_rotate_action_get_type ()) #define CLUTTER_ROTATE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ROTATE_ACTION, ClutterRotateAction)) #define CLUTTER_IS_ROTATE_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ROTATE_ACTION)) #define CLUTTER_ROTATE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ROTATE_ACTION, ClutterRotateActionClass)) #define CLUTTER_IS_ROTATE_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ROTATE_ACTION)) #define CLUTTER_ROTATE_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ROTATE_ACTION, ClutterRotateActionClass)) typedef struct _ClutterRotateAction ClutterRotateAction; typedef struct _ClutterRotateActionPrivate ClutterRotateActionPrivate; typedef struct _ClutterRotateActionClass ClutterRotateActionClass; /** * ClutterRotateAction: * * The #ClutterRotateAction structure contains * only private data and should be accessed using the provided API * * Since: 1.12 */ struct _ClutterRotateAction { /*< private >*/ ClutterGestureAction parent_instance; ClutterRotateActionPrivate *priv; }; /** * ClutterRotateActionClass: * @rotate: class handler for the #ClutterRotateAction::rotate signal * * The #ClutterRotateActionClass structure contains * only private data. * * Since: 1.12 */ struct _ClutterRotateActionClass { /*< private >*/ ClutterGestureActionClass parent_class; /*< public >*/ gboolean (* rotate) (ClutterRotateAction *action, ClutterActor *actor, gdouble angle); /*< private >*/ void (* _clutter_rotate_action1) (void); void (* _clutter_rotate_action2) (void); void (* _clutter_rotate_action3) (void); void (* _clutter_rotate_action4) (void); void (* _clutter_rotate_action5) (void); void (* _clutter_rotate_action6) (void); void (* _clutter_rotate_action7) (void); }; CLUTTER_EXPORT GType clutter_rotate_action_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterAction *clutter_rotate_action_new (void); G_END_DECLS #endif /* __CLUTTER_ROTATE_ACTION_H__ */ muffin-6.4.1/clutter/clutter/wayland/0000775000175000017500000000000014723361714016545 5ustar fabiofabiomuffin-6.4.1/clutter/clutter/wayland/clutter-wayland-compositor.h0000664000175000017500000000251114723361714024230 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Robert Bragg */ /** * SECTION:clutter-wayland-compositor * @short_description: Wayland compositor specific APIs * * Clutter provides some Wayland specific APIs to aid in writing * Clutter based compositors. * * The Clutter Wayland compositor API is available since Clutter 1.8 */ #ifndef __CLUTTER_WAYLAND_COMPOSITOR_H__ #define __CLUTTER_WAYLAND_COMPOSITOR_H__ G_BEGIN_DECLS CLUTTER_EXPORT void clutter_wayland_set_compositor_display (void *display); G_END_DECLS #endif /* __CLUTTER_WAYLAND_COMPOSITOR_H__ */ muffin-6.4.1/clutter/clutter/clutter-units.c0000664000175000017500000005477014723361714020111 0ustar fabiofabio/* -*- mode:C; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */ /* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Tomas Frydrych * Emmanuele Bassi * * Copyright (C) 2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-units * @short_description: A logical distance unit * * #ClutterUnits is a structure holding a logical distance value along with * its type, expressed as a value of the #ClutterUnitType enumeration. It is * possible to use #ClutterUnits to store a position or a size in units * different than pixels, and convert them whenever needed (for instance * inside the #ClutterActorClass.allocate() virtual function, or inside the * #ClutterActorClass.get_preferred_width() and #ClutterActorClass.get_preferred_height() * virtual functions. * * In order to register a #ClutterUnits property, the #ClutterParamSpecUnits * #GParamSpec sub-class should be used: * * |[ * GParamSpec *pspec; * * pspec = clutter_param_spec_units ("active-width", * "Width", * "Width of the active area, in millimeters", * CLUTTER_UNIT_MM, * 0.0, 12.0, * 12.0, * G_PARAM_READWRITE); * g_object_class_install_property (gobject_class, PROP_WIDTH, pspec); * ]| * * A #GValue holding units can be manipulated using clutter_value_set_units() * and clutter_value_get_units(). #GValues containing a #ClutterUnits * value can also be transformed to #GValues initialized with * %G_TYPE_INT, %G_TYPE_FLOAT and %G_TYPE_STRING through implicit conversion * and using g_value_transform(). * * #ClutterUnits is available since Clutter 1.0 */ #include "clutter-build-config.h" #include #include #include #include "clutter-backend-private.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-units.h" #define DPI_FALLBACK (96.0) #define FLOAT_EPSILON (1e-30) static gfloat units_mm_to_pixels (gfloat mm) { ClutterBackend *backend; gdouble dpi; backend = clutter_get_default_backend (); dpi = clutter_backend_get_resolution (backend); if (dpi < 0) dpi = DPI_FALLBACK; return mm * dpi / 25.4; } static gfloat units_cm_to_pixels (gfloat cm) { return units_mm_to_pixels (cm * 10); } static gfloat units_pt_to_pixels (gfloat pt) { ClutterBackend *backend; gdouble dpi; backend = clutter_get_default_backend (); dpi = clutter_backend_get_resolution (backend); if (dpi < 0) dpi = DPI_FALLBACK; return pt * dpi / 72.0; } static gfloat units_em_to_pixels (const gchar *font_name, gfloat em) { ClutterBackend *backend = clutter_get_default_backend (); if (font_name == NULL || *font_name == '\0') return em * _clutter_backend_get_units_per_em (backend, NULL); else { PangoFontDescription *font_desc; gfloat res; font_desc = pango_font_description_from_string (font_name); if (font_desc == NULL) res = -1.0; else { res = em * _clutter_backend_get_units_per_em (backend, font_desc); pango_font_description_free (font_desc); } return res; } } /** * clutter_units_from_mm: * @units: (out caller-allocates): a #ClutterUnits * @mm: millimeters * * Stores a value in millimiters inside @units * * Since: 1.0 */ void clutter_units_from_mm (ClutterUnits *units, gfloat mm) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_MM; units->value = mm; units->pixels = units_mm_to_pixels (mm); units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_from_cm: * @units: (out caller-allocates): a #ClutterUnits * @cm: centimeters * * Stores a value in centimeters inside @units * * Since: 1.2 */ void clutter_units_from_cm (ClutterUnits *units, gfloat cm) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_CM; units->value = cm; units->pixels = units_cm_to_pixels (cm); units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_from_pt: * @units: (out caller-allocates): a #ClutterUnits * @pt: typographic points * * Stores a value in typographic points inside @units * * Since: 1.0 */ void clutter_units_from_pt (ClutterUnits *units, gfloat pt) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_POINT; units->value = pt; units->pixels = units_pt_to_pixels (pt); units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_from_em: * @units: (out caller-allocates): a #ClutterUnits * @em: em * * Stores a value in em inside @units, using the default font * name as returned by clutter_backend_get_font_name() * * Since: 1.0 */ void clutter_units_from_em (ClutterUnits *units, gfloat em) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_EM; units->value = em; units->pixels = units_em_to_pixels (NULL, em); units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_from_em_for_font: * @units: (out caller-allocates): a #ClutterUnits * @font_name: (allow-none): the font name and size * @em: em * * Stores a value in em inside @units using @font_name * * Since: 1.0 */ void clutter_units_from_em_for_font (ClutterUnits *units, const gchar *font_name, gfloat em) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_EM; units->value = em; units->pixels = units_em_to_pixels (font_name, em); units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_from_pixels: * @units: (out caller-allocates): a #ClutterUnits * @px: pixels * * Stores a value in pixels inside @units * * Since: 1.0 */ void clutter_units_from_pixels (ClutterUnits *units, gint px) { ClutterBackend *backend; g_return_if_fail (units != NULL); backend = clutter_get_default_backend (); units->unit_type = CLUTTER_UNIT_PIXEL; units->value = px; units->pixels = px; units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); } /** * clutter_units_get_unit_type: * @units: a #ClutterUnits * * Retrieves the unit type of the value stored inside @units * * Return value: a unit type * * Since: 1.0 */ ClutterUnitType clutter_units_get_unit_type (const ClutterUnits *units) { g_return_val_if_fail (units != NULL, CLUTTER_UNIT_PIXEL); return units->unit_type; } /** * clutter_units_get_unit_value: * @units: a #ClutterUnits * * Retrieves the value stored inside @units * * Return value: the value stored inside a #ClutterUnits * * Since: 1.0 */ gfloat clutter_units_get_unit_value (const ClutterUnits *units) { g_return_val_if_fail (units != NULL, 0.0); return units->value; } /** * clutter_units_copy: * @units: the #ClutterUnits to copy * * Copies @units * * Return value: (transfer full): the newly created copy of a * #ClutterUnits structure. Use clutter_units_free() to free * the allocated resources * * Since: 1.0 */ ClutterUnits * clutter_units_copy (const ClutterUnits *units) { if (units != NULL) return g_slice_dup (ClutterUnits, units); return NULL; } /** * clutter_units_free: * @units: the #ClutterUnits to free * * Frees the resources allocated by @units * * You should only call this function on a #ClutterUnits * created using clutter_units_copy() * * Since: 1.0 */ void clutter_units_free (ClutterUnits *units) { if (units != NULL) g_slice_free (ClutterUnits, units); } /** * clutter_units_to_pixels: * @units: units to convert * * Converts a value in #ClutterUnits to pixels * * Return value: the value in pixels * * Since: 1.0 */ gfloat clutter_units_to_pixels (ClutterUnits *units) { ClutterBackend *backend; g_return_val_if_fail (units != NULL, 0.0); /* if the backend settings changed we evict the cached value */ backend = clutter_get_default_backend (); if (units->serial != _clutter_backend_get_units_serial (backend)) units->pixels_set = FALSE; if (units->pixels_set) return units->pixels; switch (units->unit_type) { case CLUTTER_UNIT_MM: units->pixels = units_mm_to_pixels (units->value); break; case CLUTTER_UNIT_CM: units->pixels = units_cm_to_pixels (units->value); break; case CLUTTER_UNIT_POINT: units->pixels = units_pt_to_pixels (units->value); break; case CLUTTER_UNIT_EM: units->pixels = units_em_to_pixels (NULL, units->value); break; case CLUTTER_UNIT_PIXEL: units->pixels = units->value; break; } units->pixels_set = TRUE; units->serial = _clutter_backend_get_units_serial (backend); return units->pixels; } /** * clutter_units_from_string: * @units: (out caller-allocates): a #ClutterUnits * @str: the string to convert * * Parses a value and updates @units with it * * A #ClutterUnits expressed in string should match: * * |[ * units: wsp* unit-value wsp* unit-name? wsp* * unit-value: number * unit-name: 'px' | 'pt' | 'mm' | 'em' | 'cm' * number: digit+ * | digit* sep digit+ * sep: '.' | ',' * digit: '0' | '1' | '2' | '3' | '4' | '5' | '6' | '7' | '8' | '9' * wsp: (#0x20 | #0x9 | #0xA | #0xB | #0xC | #0xD)+ * ]| * * For instance, these are valid strings: * * |[ * 10 px * 5.1 em * 24 pt * 12.6 mm * .3 cm * ]| * * While these are not: * * |[ * 42 cats * omg!1!ponies * ]| * * If no unit is specified, pixels are assumed. * * Return value: %TRUE if the string was successfully parsed, * and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_units_from_string (ClutterUnits *units, const gchar *str) { ClutterBackend *backend; ClutterUnitType unit_type; gfloat value; g_return_val_if_fail (units != NULL, FALSE); g_return_val_if_fail (str != NULL, FALSE); /* strip leading space */ while (g_ascii_isspace (*str)) str++; if (*str == '\0') return FALSE; /* integer part */ value = (gfloat) strtoul (str, (char **) &str, 10); if (*str == '.' || *str == ',') { gfloat divisor = 0.1; /* 5.cm is not a valid number */ if (!g_ascii_isdigit (*++str)) return FALSE; while (g_ascii_isdigit (*str)) { value += (*str - '0') * divisor; divisor *= 0.1; str++; } } while (g_ascii_isspace (*str)) str++; /* assume pixels by default, if no unit is specified */ if (*str == '\0') unit_type = CLUTTER_UNIT_PIXEL; else if (strncmp (str, "em", 2) == 0) { unit_type = CLUTTER_UNIT_EM; str += 2; } else if (strncmp (str, "mm", 2) == 0) { unit_type = CLUTTER_UNIT_MM; str += 2; } else if (strncmp (str, "cm", 2) == 0) { unit_type = CLUTTER_UNIT_CM; str += 2; } else if (strncmp (str, "pt", 2) == 0) { unit_type = CLUTTER_UNIT_POINT; str += 2; } else if (strncmp (str, "px", 2) == 0) { unit_type = CLUTTER_UNIT_PIXEL; str += 2; } else return FALSE; /* ensure the unit is only followed by white space */ while (g_ascii_isspace (*str)) str++; if (*str != '\0') return FALSE; backend = clutter_get_default_backend (); units->unit_type = unit_type; units->value = value; units->pixels_set = FALSE; units->serial = _clutter_backend_get_units_serial (backend); return TRUE; } static const gchar * clutter_unit_type_name (ClutterUnitType unit_type) { switch (unit_type) { case CLUTTER_UNIT_MM: return "mm"; case CLUTTER_UNIT_CM: return "cm"; case CLUTTER_UNIT_POINT: return "pt"; case CLUTTER_UNIT_EM: return "em"; case CLUTTER_UNIT_PIXEL: return "px"; } g_warning ("Invalid unit type %d", (int) unit_type); return ""; } /** * clutter_units_to_string: * @units: a #ClutterUnits * * Converts @units into a string * * See clutter_units_from_string() for the units syntax and for * examples of output * * Fractional values are truncated to the second decimal * position for em, mm and cm, and to the first decimal position for * typographic points. Pixels are integers. * * Return value: a newly allocated string containing the encoded * #ClutterUnits value. Use g_free() to free the string * * Since: 1.0 */ gchar * clutter_units_to_string (const ClutterUnits *units) { const gchar *unit_name = NULL; const gchar *fmt = NULL; gchar buf[G_ASCII_DTOSTR_BUF_SIZE]; g_return_val_if_fail (units != NULL, NULL); switch (units->unit_type) { /* special case: there is no such thing as "half a pixel", so * we round up to the nearest integer using C default */ case CLUTTER_UNIT_PIXEL: return g_strdup_printf ("%d px", (int) units->value); case CLUTTER_UNIT_MM: unit_name = "mm"; fmt = "%.2f"; break; case CLUTTER_UNIT_CM: unit_name = "cm"; fmt = "%.2f"; break; case CLUTTER_UNIT_POINT: unit_name = "pt"; fmt = "%.1f"; break; case CLUTTER_UNIT_EM: unit_name = "em"; fmt = "%.2f"; break; default: g_assert_not_reached (); break; } g_ascii_formatd (buf, G_ASCII_DTOSTR_BUF_SIZE, fmt, units->value); return g_strconcat (buf, " ", unit_name, NULL); } /* * ClutterInterval integration */ static gboolean clutter_units_progress (const GValue *a, const GValue *b, gdouble progress, GValue *retval) { ClutterUnits *a_units = (ClutterUnits *) clutter_value_get_units (a); ClutterUnits *b_units = (ClutterUnits *) clutter_value_get_units (b); ClutterUnits res; gfloat a_px, b_px, value; a_px = clutter_units_to_pixels (a_units); b_px = clutter_units_to_pixels (b_units); value = progress * (b_px - a_px) + a_px; clutter_units_from_pixels (&res, value); clutter_value_set_units (retval, &res); return TRUE; } /* * GValue and GParamSpec integration */ /* units to integer */ static void clutter_value_transform_units_int (const GValue *src, GValue *dest) { dest->data[0].v_int = clutter_units_to_pixels (src->data[0].v_pointer); } /* integer to units */ static void clutter_value_transform_int_units (const GValue *src, GValue *dest) { clutter_units_from_pixels (dest->data[0].v_pointer, src->data[0].v_int); } /* units to float */ static void clutter_value_transform_units_float (const GValue *src, GValue *dest) { dest->data[0].v_float = clutter_units_to_pixels (src->data[0].v_pointer); } /* float to units */ static void clutter_value_transform_float_units (const GValue *src, GValue *dest) { clutter_units_from_pixels (dest->data[0].v_pointer, src->data[0].v_float); } /* units to string */ static void clutter_value_transform_units_string (const GValue *src, GValue *dest) { gchar *string = clutter_units_to_string (src->data[0].v_pointer); g_value_take_string (dest, string); } /* string to units */ static void clutter_value_transform_string_units (const GValue *src, GValue *dest) { ClutterUnits units = { CLUTTER_UNIT_PIXEL, 0.0f }; clutter_units_from_string (&units, g_value_get_string (src)); clutter_value_set_units (dest, &units); } G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterUnits, clutter_units, clutter_units_copy, clutter_units_free, CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_INT, clutter_value_transform_units_int) CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_FLOAT, clutter_value_transform_units_float) CLUTTER_REGISTER_VALUE_TRANSFORM_TO (G_TYPE_STRING, clutter_value_transform_units_string) CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_INT, clutter_value_transform_int_units) CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_FLOAT, clutter_value_transform_float_units) CLUTTER_REGISTER_VALUE_TRANSFORM_FROM (G_TYPE_STRING, clutter_value_transform_string_units) CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_units_progress)); /** * clutter_value_set_units: * @value: a #GValue initialized to %CLUTTER_TYPE_UNITS * @units: the units to set * * Sets @value to @units * * Since: 0.8 */ void clutter_value_set_units (GValue *value, const ClutterUnits *units) { g_return_if_fail (CLUTTER_VALUE_HOLDS_UNITS (value)); value->data[0].v_pointer = clutter_units_copy (units); } /** * clutter_value_get_units: * @value: a #GValue initialized to %CLUTTER_TYPE_UNITS * * Gets the #ClutterUnits contained in @value. * * Return value: the units inside the passed #GValue * * Since: 0.8 */ const ClutterUnits * clutter_value_get_units (const GValue *value) { g_return_val_if_fail (CLUTTER_VALUE_HOLDS_UNITS (value), NULL); return value->data[0].v_pointer; } static void param_units_init (GParamSpec *pspec) { ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec); uspec->minimum = -G_MAXFLOAT; uspec->maximum = G_MAXFLOAT; uspec->default_value = 0.0f; uspec->default_type = CLUTTER_UNIT_PIXEL; } static void param_units_set_default (GParamSpec *pspec, GValue *value) { ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec); ClutterUnits units; units.unit_type = uspec->default_type; units.value = uspec->default_value; units.pixels_set = FALSE; clutter_value_set_units (value, &units); } static gboolean param_units_validate (GParamSpec *pspec, GValue *value) { ClutterParamSpecUnits *uspec = CLUTTER_PARAM_SPEC_UNITS (pspec); ClutterUnits *units = value->data[0].v_pointer; ClutterUnitType otype = units->unit_type; gfloat oval = units->value; g_assert (CLUTTER_IS_PARAM_SPEC_UNITS (pspec)); if (otype != uspec->default_type) { gchar *str = clutter_units_to_string (units); g_warning ("The units value of '%s' does not have the same unit " "type as declared by the ClutterParamSpecUnits of '%s'", str, clutter_unit_type_name (uspec->default_type)); g_free (str); return FALSE; } units->value = CLAMP (units->value, uspec->minimum, uspec->maximum); return units->value != oval; } static gint param_units_values_cmp (GParamSpec *pspec, const GValue *value1, const GValue *value2) { ClutterUnits *units1 = value1->data[0].v_pointer; ClutterUnits *units2 = value2->data[0].v_pointer; gfloat v1, v2; if (units1->unit_type == units2->unit_type) { v1 = units1->value; v2 = units2->value; } else { v1 = clutter_units_to_pixels (units1); v2 = clutter_units_to_pixels (units2); } if (v1 < v2) return - (v2 - v1 > FLOAT_EPSILON); else return v1 - v2 > FLOAT_EPSILON; } GType clutter_param_units_get_type (void) { static GType pspec_type = 0; if (G_UNLIKELY (pspec_type == 0)) { const GParamSpecTypeInfo pspec_info = { sizeof (ClutterParamSpecUnits), 16, param_units_init, CLUTTER_TYPE_UNITS, NULL, param_units_set_default, param_units_validate, param_units_values_cmp, }; pspec_type = g_param_type_register_static (I_("ClutterParamSpecUnit"), &pspec_info); } return pspec_type; } /** * clutter_param_spec_units: (skip) * @name: name of the property * @nick: short name * @blurb: description (can be translatable) * @default_type: the default type for the #ClutterUnits * @minimum: lower boundary * @maximum: higher boundary * @default_value: default value * @flags: flags for the param spec * * Creates a #GParamSpec for properties using #ClutterUnits. * * Return value: the newly created #GParamSpec * * Since: 1.0 */ GParamSpec * clutter_param_spec_units (const gchar *name, const gchar *nick, const gchar *blurb, ClutterUnitType default_type, gfloat minimum, gfloat maximum, gfloat default_value, GParamFlags flags) { ClutterParamSpecUnits *uspec; g_return_val_if_fail (default_value >= minimum && default_value <= maximum, NULL); uspec = g_param_spec_internal (CLUTTER_TYPE_PARAM_UNITS, name, nick, blurb, flags); uspec->default_type = default_type; uspec->minimum = minimum; uspec->maximum = maximum; uspec->default_value = default_value; return G_PARAM_SPEC (uspec); } muffin-6.4.1/clutter/clutter/clutter-click-action.c0000664000175000017500000005572714723361714021312 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-click-action * @Title: ClutterClickAction * @Short_Description: Action for clickable actors * * #ClutterClickAction is a sub-class of #ClutterAction that implements * the logic for clickable actors, by using the low level events of * #ClutterActor, such as #ClutterActor::button-press-event and * #ClutterActor::button-release-event, to synthesize the high level * #ClutterClickAction::clicked signal. * * To use #ClutterClickAction you just need to apply it to a #ClutterActor * using clutter_actor_add_action() and connect to the * #ClutterClickAction::clicked signal: * * |[ * ClutterAction *action = clutter_click_action_new (); * * clutter_actor_add_action (actor, action); * * g_signal_connect (action, "clicked", G_CALLBACK (on_clicked), NULL); * ]| * * #ClutterClickAction also supports long press gestures: a long press is * activated if the pointer remains pressed within a certain threshold (as * defined by the #ClutterClickAction:long-press-threshold property) for a * minimum amount of time (as the defined by the * #ClutterClickAction:long-press-duration property). * The #ClutterClickAction::long-press signal is emitted multiple times, * using different #ClutterLongPressState values; to handle long presses * you should connect to the #ClutterClickAction::long-press signal and * handle the different states: * * |[ * static gboolean * on_long_press (ClutterClickAction *action, * ClutterActor *actor, * ClutterLongPressState state) * { * switch (state) * { * case CLUTTER_LONG_PRESS_QUERY: * /* return TRUE if the actor should support long press * * gestures, and FALSE otherwise; this state will be * * emitted on button presses * */ * return TRUE; * * case CLUTTER_LONG_PRESS_ACTIVATE: * /* this state is emitted if the minimum duration has * * been reached without the gesture being cancelled. * * the return value is not used * */ * return TRUE; * * case CLUTTER_LONG_PRESS_CANCEL: * /* this state is emitted if the long press was cancelled; * * for instance, the pointer went outside the actor or the * * allowed threshold, or the button was released before * * the minimum duration was reached. the return value is * * not used * */ * return FALSE; * } * } * ]| * * #ClutterClickAction is available since Clutter 1.4 */ #include "clutter-build-config.h" #include "clutter-click-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" struct _ClutterClickActionPrivate { ClutterActor *stage; gulong event_id; gulong capture_id; guint long_press_id; gint long_press_threshold; gint long_press_duration; gint drag_threshold; guint press_button; gint press_device_id; ClutterEventSequence *press_sequence; ClutterModifierType modifier_state; gfloat press_x; gfloat press_y; guint is_held : 1; guint is_pressed : 1; }; enum { PROP_0, PROP_HELD, PROP_PRESSED, PROP_LONG_PRESS_THRESHOLD, PROP_LONG_PRESS_DURATION, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; enum { CLICKED, LONG_PRESS, LAST_SIGNAL }; static guint click_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterClickAction, clutter_click_action, CLUTTER_TYPE_ACTION) /* forward declaration */ static gboolean on_captured_event (ClutterActor *stage, ClutterEvent *event, ClutterClickAction *action); static inline void click_action_set_pressed (ClutterClickAction *action, gboolean is_pressed) { ClutterClickActionPrivate *priv = action->priv; is_pressed = !!is_pressed; if (priv->is_pressed == is_pressed) return; priv->is_pressed = is_pressed; g_object_notify_by_pspec (G_OBJECT (action), obj_props[PROP_PRESSED]); } static inline void click_action_set_held (ClutterClickAction *action, gboolean is_held) { ClutterClickActionPrivate *priv = action->priv; is_held = !!is_held; if (priv->is_held == is_held) return; priv->is_held = is_held; g_object_notify_by_pspec (G_OBJECT (action), obj_props[PROP_HELD]); } static gboolean click_action_emit_long_press (gpointer data) { ClutterClickAction *action = data; ClutterClickActionPrivate *priv = action->priv; ClutterActor *actor; gboolean result; priv->long_press_id = 0; actor = clutter_actor_meta_get_actor (data); g_signal_emit (action, click_signals[LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_ACTIVATE, &result); g_clear_signal_handler (&priv->capture_id, priv->stage); click_action_set_pressed (action, FALSE); click_action_set_held (action, FALSE); return FALSE; } static inline void click_action_query_long_press (ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; ClutterActor *actor; gboolean result = FALSE; gint timeout; if (priv->long_press_duration < 0) { ClutterSettings *settings = clutter_settings_get_default (); g_object_get (settings, "long-press-duration", &timeout, NULL); } else timeout = priv->long_press_duration; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); g_signal_emit (action, click_signals[LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_QUERY, &result); if (result) { priv->long_press_id = clutter_threads_add_timeout (timeout, click_action_emit_long_press, action); } } static inline void click_action_cancel_long_press (ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; if (priv->long_press_id != 0) { ClutterActor *actor; gboolean result; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); g_clear_handle_id (&priv->long_press_id, g_source_remove); g_signal_emit (action, click_signals[LONG_PRESS], 0, actor, CLUTTER_LONG_PRESS_CANCEL, &result); } } static gboolean on_event (ClutterActor *actor, ClutterEvent *event, ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; gboolean has_button = TRUE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) return CLUTTER_EVENT_PROPAGATE; switch (clutter_event_type (event)) { case CLUTTER_TOUCH_BEGIN: has_button = FALSE; case CLUTTER_BUTTON_PRESS: if (has_button && clutter_event_get_click_count (event) != 1) return CLUTTER_EVENT_PROPAGATE; if (priv->is_held) return CLUTTER_EVENT_STOP; if (!clutter_actor_contains (actor, clutter_event_get_source (event))) return CLUTTER_EVENT_PROPAGATE; priv->press_button = has_button ? clutter_event_get_button (event) : 0; priv->press_device_id = clutter_event_get_device_id (event); priv->press_sequence = clutter_event_get_event_sequence (event); priv->modifier_state = clutter_event_get_state (event); clutter_event_get_coords (event, &priv->press_x, &priv->press_y); if (priv->long_press_threshold < 0) { ClutterSettings *settings = clutter_settings_get_default (); g_object_get (settings, "dnd-drag-threshold", &priv->drag_threshold, NULL); } else priv->drag_threshold = priv->long_press_threshold; if (priv->stage == NULL) priv->stage = clutter_actor_get_stage (actor); priv->capture_id = g_signal_connect_after (priv->stage, "captured-event", G_CALLBACK (on_captured_event), action); click_action_set_pressed (action, TRUE); click_action_set_held (action, TRUE); click_action_query_long_press (action); break; case CLUTTER_ENTER: click_action_set_pressed (action, priv->is_held); break; case CLUTTER_LEAVE: click_action_set_pressed (action, priv->is_held); click_action_cancel_long_press (action); break; default: break; } return CLUTTER_EVENT_PROPAGATE; } static gboolean on_captured_event (ClutterActor *stage, ClutterEvent *event, ClutterClickAction *action) { ClutterClickActionPrivate *priv = action->priv; ClutterActor *actor; ClutterModifierType modifier_state; gboolean has_button = TRUE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) { clutter_click_action_release (action); return CLUTTER_EVENT_PROPAGATE; } actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); switch (clutter_event_type (event)) { case CLUTTER_TOUCH_CANCEL: clutter_click_action_release (action); break; case CLUTTER_TOUCH_END: has_button = FALSE; case CLUTTER_BUTTON_RELEASE: if (!priv->is_held) return CLUTTER_EVENT_STOP; if ((has_button && clutter_event_get_button (event) != priv->press_button) || (has_button && clutter_event_get_click_count (event) != 1) || clutter_event_get_device_id (event) != priv->press_device_id || clutter_event_get_event_sequence (event) != priv->press_sequence) return CLUTTER_EVENT_PROPAGATE; click_action_set_held (action, FALSE); click_action_cancel_long_press (action); /* disconnect the capture */ g_clear_signal_handler (&priv->capture_id, priv->stage); g_clear_handle_id (&priv->long_press_id, g_source_remove); if (!clutter_actor_contains (actor, clutter_event_get_source (event))) return CLUTTER_EVENT_PROPAGATE; /* exclude any button-mask so that we can compare * the press and release states properly */ modifier_state = clutter_event_get_state (event) & ~(CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); /* if press and release states don't match we * simply ignore modifier keys. i.e. modifier keys * are expected to be pressed throughout the whole * click */ if (modifier_state != priv->modifier_state) priv->modifier_state = 0; click_action_set_pressed (action, FALSE); g_signal_emit (action, click_signals[CLICKED], 0, actor); break; case CLUTTER_MOTION: case CLUTTER_TOUCH_UPDATE: { gfloat motion_x, motion_y; gfloat delta_x, delta_y; if (clutter_event_get_device_id (event) != priv->press_device_id || clutter_event_get_event_sequence (event) != priv->press_sequence) return CLUTTER_EVENT_PROPAGATE; if (!priv->is_held) return CLUTTER_EVENT_PROPAGATE; clutter_event_get_coords (event, &motion_x, &motion_y); delta_x = ABS (motion_x - priv->press_x); delta_y = ABS (motion_y - priv->press_y); if (delta_x > priv->drag_threshold || delta_y > priv->drag_threshold) click_action_cancel_long_press (action); } break; default: break; } return CLUTTER_EVENT_STOP; } static void clutter_click_action_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterClickAction *action = CLUTTER_CLICK_ACTION (meta); ClutterClickActionPrivate *priv = action->priv; if (priv->event_id != 0) { ClutterActor *old_actor = clutter_actor_meta_get_actor (meta); if (old_actor != NULL) g_clear_signal_handler (&priv->event_id, old_actor); priv->event_id = 0; } if (priv->capture_id != 0) { if (priv->stage != NULL) g_clear_signal_handler (&priv->capture_id, priv->stage); priv->capture_id = 0; priv->stage = NULL; } g_clear_handle_id (&priv->long_press_id, g_source_remove); click_action_set_pressed (action, FALSE); click_action_set_held (action, FALSE); if (actor != NULL) priv->event_id = g_signal_connect (actor, "event", G_CALLBACK (on_event), action); CLUTTER_ACTOR_META_CLASS (clutter_click_action_parent_class)->set_actor (meta, actor); } static void clutter_click_action_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterClickActionPrivate *priv = CLUTTER_CLICK_ACTION (gobject)->priv; switch (prop_id) { case PROP_LONG_PRESS_DURATION: priv->long_press_duration = g_value_get_int (value); break; case PROP_LONG_PRESS_THRESHOLD: priv->long_press_threshold = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_click_action_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterClickActionPrivate *priv = CLUTTER_CLICK_ACTION (gobject)->priv; switch (prop_id) { case PROP_HELD: g_value_set_boolean (value, priv->is_held); break; case PROP_PRESSED: g_value_set_boolean (value, priv->is_pressed); break; case PROP_LONG_PRESS_DURATION: g_value_set_int (value, priv->long_press_duration); break; case PROP_LONG_PRESS_THRESHOLD: g_value_set_int (value, priv->long_press_threshold); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_click_action_dispose (GObject *gobject) { ClutterClickActionPrivate *priv = CLUTTER_CLICK_ACTION (gobject)->priv; g_clear_signal_handler (&priv->event_id, clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (gobject))); g_clear_signal_handler (&priv->capture_id, priv->stage); g_clear_handle_id (&priv->long_press_id, g_source_remove); G_OBJECT_CLASS (clutter_click_action_parent_class)->dispose (gobject); } static void clutter_click_action_class_init (ClutterClickActionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); meta_class->set_actor = clutter_click_action_set_actor; gobject_class->dispose = clutter_click_action_dispose; gobject_class->set_property = clutter_click_action_set_property; gobject_class->get_property = clutter_click_action_get_property; /** * ClutterClickAction:pressed: * * Whether the clickable actor should be in "pressed" state * * Since: 1.4 */ obj_props[PROP_PRESSED] = g_param_spec_boolean ("pressed", P_("Pressed"), P_("Whether the clickable should be in pressed state"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterClickAction:held: * * Whether the clickable actor has the pointer grabbed * * Since: 1.4 */ obj_props[PROP_HELD] = g_param_spec_boolean ("held", P_("Held"), P_("Whether the clickable has a grab"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterClickAction:long-press-duration: * * The minimum duration of a press for it to be recognized as a long * press gesture, in milliseconds. * * A value of -1 will make the #ClutterClickAction use the value of * the #ClutterSettings:long-press-duration property. * * Since: 1.8 */ obj_props[PROP_LONG_PRESS_DURATION] = g_param_spec_int ("long-press-duration", P_("Long Press Duration"), P_("The minimum duration of a long press to recognize the gesture"), -1, G_MAXINT, -1, CLUTTER_PARAM_READWRITE); /** * ClutterClickAction:long-press-threshold: * * The maximum allowed distance that can be covered (on both axes) before * a long press gesture is cancelled, in pixels. * * A value of -1 will make the #ClutterClickAction use the value of * the #ClutterSettings:dnd-drag-threshold property. * * Since: 1.8 */ obj_props[PROP_LONG_PRESS_THRESHOLD] = g_param_spec_int ("long-press-threshold", P_("Long Press Threshold"), P_("The maximum threshold before a long press is cancelled"), -1, G_MAXINT, -1, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); /** * ClutterClickAction::clicked: * @action: the #ClutterClickAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::clicked signal is emitted when the #ClutterActor to which * a #ClutterClickAction has been applied should respond to a * pointer button press and release events * * Since: 1.4 */ click_signals[CLICKED] = g_signal_new (I_("clicked"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterClickActionClass, clicked), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterClickAction::long-press: * @action: the #ClutterClickAction that emitted the signal * @actor: the #ClutterActor attached to the @action * @state: the long press state * * The ::long-press signal is emitted during the long press gesture * handling. * * This signal can be emitted multiple times with different states. * * The %CLUTTER_LONG_PRESS_QUERY state will be emitted on button presses, * and its return value will determine whether the long press handling * should be initiated. If the signal handlers will return %TRUE, the * %CLUTTER_LONG_PRESS_QUERY state will be followed either by a signal * emission with the %CLUTTER_LONG_PRESS_ACTIVATE state if the long press * constraints were respected, or by a signal emission with the * %CLUTTER_LONG_PRESS_CANCEL state if the long press was cancelled. * * It is possible to forcibly cancel a long press detection using * clutter_click_action_release(). * * Return value: Only the %CLUTTER_LONG_PRESS_QUERY state uses the * returned value of the handler; other states will ignore it * * Since: 1.8 */ click_signals[LONG_PRESS] = g_signal_new (I_("long-press"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterClickActionClass, long_press), NULL, NULL, _clutter_marshal_BOOLEAN__OBJECT_ENUM, G_TYPE_BOOLEAN, 2, CLUTTER_TYPE_ACTOR, CLUTTER_TYPE_LONG_PRESS_STATE); } static void clutter_click_action_init (ClutterClickAction *self) { self->priv = clutter_click_action_get_instance_private (self); self->priv->long_press_threshold = -1; self->priv->long_press_duration = -1; } /** * clutter_click_action_new: * * Creates a new #ClutterClickAction instance * * Return value: the newly created #ClutterClickAction * * Since: 1.4 */ ClutterAction * clutter_click_action_new (void) { return g_object_new (CLUTTER_TYPE_CLICK_ACTION, NULL); } /** * clutter_click_action_release: * @action: a #ClutterClickAction * * Emulates a release of the pointer button, which ungrabs the pointer * and unsets the #ClutterClickAction:pressed state. * * This function will also cancel the long press gesture if one was * initiated. * * This function is useful to break a grab, for instance after a certain * amount of time has passed. * * Since: 1.4 */ void clutter_click_action_release (ClutterClickAction *action) { ClutterClickActionPrivate *priv; g_return_if_fail (CLUTTER_IS_CLICK_ACTION (action)); priv = action->priv; if (!priv->is_held) return; /* disconnect the capture */ g_clear_signal_handler (&priv->capture_id, priv->stage); click_action_cancel_long_press (action); click_action_set_held (action, FALSE); click_action_set_pressed (action, FALSE); } /** * clutter_click_action_get_button: * @action: a #ClutterClickAction * * Retrieves the button that was pressed. * * Return value: the button value * * Since: 1.4 */ guint clutter_click_action_get_button (ClutterClickAction *action) { g_return_val_if_fail (CLUTTER_IS_CLICK_ACTION (action), 0); return action->priv->press_button; } /** * clutter_click_action_get_state: * @action: a #ClutterClickAction * * Retrieves the modifier state of the click action. * * Return value: the modifier state parameter, or 0 * * Since: 1.6 */ ClutterModifierType clutter_click_action_get_state (ClutterClickAction *action) { g_return_val_if_fail (CLUTTER_IS_CLICK_ACTION (action), 0); return action->priv->modifier_state; } /** * clutter_click_action_get_coords: * @action: a #ClutterClickAction * @press_x: (out): return location for the X coordinate, or %NULL * @press_y: (out): return location for the Y coordinate, or %NULL * * Retrieves the screen coordinates of the button press. * * Since: 1.8 */ void clutter_click_action_get_coords (ClutterClickAction *action, gfloat *press_x, gfloat *press_y) { g_return_if_fail (CLUTTER_IS_ACTION (action)); if (press_x != NULL) *press_x = action->priv->press_x; if (press_y != NULL) *press_y = action->priv->press_y; } muffin-6.4.1/clutter/clutter/clutter-event-private.h0000664000175000017500000000311314723361714021526 0ustar fabiofabio#ifndef __CLUTTER_EVENT_PRIVATE_H__ #define __CLUTTER_EVENT_PRIVATE_H__ #include G_BEGIN_DECLS CLUTTER_EXPORT void _clutter_event_set_pointer_emulated (ClutterEvent *event, gboolean is_emulated); /* Reinjecting queued events for processing */ CLUTTER_EXPORT void _clutter_process_event (ClutterEvent *event); CLUTTER_EXPORT gboolean _clutter_event_process_filters (ClutterEvent *event); /* clears the event queue inside the main context */ void _clutter_clear_events_queue (void); void _clutter_clear_events_queue_for_stage (ClutterStage *stage); CLUTTER_EXPORT void _clutter_event_set_platform_data (ClutterEvent *event, gpointer data); CLUTTER_EXPORT gpointer _clutter_event_get_platform_data (const ClutterEvent *event); CLUTTER_EXPORT void _clutter_event_set_state_full (ClutterEvent *event, ClutterModifierType button_state, ClutterModifierType base_state, ClutterModifierType latched_state, ClutterModifierType locked_state, ClutterModifierType effective_state); CLUTTER_EXPORT void _clutter_event_push (const ClutterEvent *event, gboolean do_copy); G_END_DECLS #endif /* __CLUTTER_EVENT_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-script.c0000664000175000017500000013073414723361714020246 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ /** * SECTION:clutter-script * @short_description: Loads a scene from UI definition data * * #ClutterScript is an object used for loading and building parts or a * complete scenegraph from external definition data in forms of string * buffers or files. * * The UI definition format is JSON, the JavaScript Object Notation as * described by RFC 4627. #ClutterScript can load a JSON data stream, * parse it and build all the objects defined into it. Each object must * have an "id" and a "type" properties defining the name to be used * to retrieve it from #ClutterScript with clutter_script_get_object(), * and the class type to be instanciated. Every other attribute will * be mapped to the class properties. * * A #ClutterScript holds a reference on every object it creates from * the definition data, except for the stage. Every non-actor object * will be finalized when the #ClutterScript instance holding it will * be finalized, so they need to be referenced using g_object_ref() in * order for them to survive. * * A simple object might be defined as: * * * * This will produce a red #ClutterRectangle, 100x100 pixels wide, and * with a ClutterScript id of "red-button"; it can be retrieved by calling: * * |[ * ClutterActor *red_button; * * red_button = CLUTTER_ACTOR (clutter_script_get_object (script, "red-button")); * ]| * * and then manipulated with the Clutter API. For every object created * using ClutterScript it is possible to check the id by calling * clutter_get_script_id(). * * Packing can be represented using the "children" member, and passing an * array of objects or ids of objects already defined (but not packed: the * packing rules of Clutter still apply, and an actor cannot be packed * in multiple containers without unparenting it in between). * * Signal handlers can be defined inside a Clutter UI definition file and * then autoconnected to their respective signals using the * clutter_script_connect_signals() function: * * * * Signal handler definitions must have a "name" and a "handler" members; * they can also have the "after" and "swapped" boolean members (for the * signal connection flags %G_CONNECT_AFTER and %G_CONNECT_SWAPPED * respectively) and the "object" string member for calling * g_signal_connect_object() instead of g_signal_connect(). * * Signals can also be directly attached to a specific state defined * inside a #ClutterState instance, for instance: * * |[ * ... * "signals" : [ * { * "name" : "enter-event", * "states" : "button-states", * "target-state" : "hover" * }, * { * "name" : "leave-event", * "states" : "button-states", * "target-state" : "base" * }, * { * "name" : "button-press-event", * "states" : "button-states", * "target-state" : "active", * }, * { * "name" : "key-press-event", * "states" : "button-states", * "target-state" : "key-focus", * "warp" : true * } * ], * ... * ]| * * The "states" key defines the #ClutterState instance to be used to * resolve the "target-state" key; it can be either a script id for a * #ClutterState built by the same #ClutterScript instance, or to a * #ClutterState built in code and associated to the #ClutterScript * instance through the clutter_script_add_states() function. If no * "states" key is present, then the default #ClutterState associated to * the #ClutterScript instance will be used; the default #ClutterState * can be set using clutter_script_add_states() using a %NULL name. The * "warp" key can be used to warp to a specific state instead of * animating to it. State changes on signal emission will not affect * the signal emission chain. * * Clutter reserves the following names, so classes defining properties * through the usual GObject registration process should avoid using these * names to avoid collisions: * * * * #ClutterScript is available since Clutter 0.6 */ #include "clutter-build-config.h" #include #include #include #include #include #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-actor.h" #include "clutter-stage.h" #include "clutter-script.h" #include "clutter-script-private.h" #include "clutter-scriptable.h" #include "clutter-enum-types.h" #include "clutter-private.h" #include "clutter-debug.h" #include "deprecated/clutter-alpha.h" #include "deprecated/clutter-container.h" #include "deprecated/clutter-state.h" enum { PROP_0, PROP_FILENAME_SET, PROP_FILENAME, PROP_TRANSLATION_DOMAIN, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _ClutterScriptPrivate { GHashTable *objects; guint last_merge_id; guint last_unknown; ClutterScriptParser *parser; GHashTable *states; gchar **search_paths; gchar *translation_domain; gchar *filename; guint is_filename : 1; }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterScript, clutter_script, G_TYPE_OBJECT) static GType clutter_script_real_get_type_from_name (ClutterScript *script, const gchar *type_name) { GType gtype; gtype = g_type_from_name (type_name); if (gtype != G_TYPE_INVALID) return gtype; return _clutter_script_get_type_from_class (type_name); } void property_info_free (gpointer data) { if (G_LIKELY (data)) { PropertyInfo *pinfo = data; if (pinfo->node) json_node_free (pinfo->node); if (pinfo->pspec) g_param_spec_unref (pinfo->pspec); g_free (pinfo->name); g_slice_free (PropertyInfo, pinfo); } } static void signal_info_free (gpointer data) { if (G_LIKELY (data)) { SignalInfo *sinfo = data; g_free (sinfo->name); g_free (sinfo->handler); g_free (sinfo->object); g_free (sinfo->state); g_free (sinfo->target); g_slice_free (SignalInfo, sinfo); } } void object_info_free (gpointer data) { if (G_LIKELY (data)) { ObjectInfo *oinfo = data; g_free (oinfo->id); g_free (oinfo->class_name); g_free (oinfo->type_func); g_list_free_full (oinfo->properties, property_info_free); g_list_free_full (oinfo->signals, signal_info_free); /* these are ids */ g_list_free_full (oinfo->children, g_free); /* we unref top-level objects and leave the actors alone, * unless we are unmerging in which case we have to destroy * the actor to unparent them */ if (oinfo->object != NULL) { if (oinfo->is_unmerged) { if (oinfo->is_actor && !oinfo->is_stage) clutter_actor_destroy (CLUTTER_ACTOR (oinfo->object)); } g_object_unref (oinfo->object); oinfo->object = NULL; } g_slice_free (ObjectInfo, oinfo); } } static void clutter_script_finalize (GObject *gobject) { ClutterScriptPrivate *priv = CLUTTER_SCRIPT (gobject)->priv; g_object_unref (priv->parser); g_hash_table_destroy (priv->objects); g_strfreev (priv->search_paths); g_free (priv->filename); g_hash_table_destroy (priv->states); g_free (priv->translation_domain); G_OBJECT_CLASS (clutter_script_parent_class)->finalize (gobject); } static void clutter_script_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterScript *script = CLUTTER_SCRIPT (gobject); switch (prop_id) { case PROP_TRANSLATION_DOMAIN: clutter_script_set_translation_domain (script, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_script_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterScript *script = CLUTTER_SCRIPT (gobject); switch (prop_id) { case PROP_FILENAME_SET: g_value_set_boolean (value, script->priv->is_filename); break; case PROP_FILENAME: g_value_set_string (value, script->priv->filename); break; case PROP_TRANSLATION_DOMAIN: g_value_set_string (value, script->priv->translation_domain); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_script_class_init (ClutterScriptClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->get_type_from_name = clutter_script_real_get_type_from_name; /** * ClutterScript:filename-set: * * Whether the #ClutterScript:filename property is set. If this property * is %TRUE then the currently parsed data comes from a file, and the * file name is stored inside the #ClutterScript:filename property. * * Since: 0.6 */ obj_props[PROP_FILENAME_SET] = g_param_spec_boolean ("filename-set", P_("Filename Set"), P_("Whether the :filename property is set"), FALSE, CLUTTER_PARAM_READABLE); /** * ClutterScript:filename: * * The path of the currently parsed file. If #ClutterScript:filename-set * is %FALSE then the value of this property is undefined. * * Since: 0.6 */ obj_props[PROP_FILENAME] = g_param_spec_string ("filename", P_("Filename"), P_("The path of the currently parsed file"), NULL, CLUTTER_PARAM_READABLE); /** * ClutterScript:translation-domain: * * The translation domain, used to localize strings marked as translatable * inside a UI definition. * * If #ClutterScript:translation-domain is set to %NULL, #ClutterScript * will use gettext(), otherwise g_dgettext() will be used. * * Since: 1.10 */ obj_props[PROP_TRANSLATION_DOMAIN] = g_param_spec_string ("translation-domain", P_("Translation Domain"), P_("The translation domain used to localize string"), NULL, CLUTTER_PARAM_READWRITE); gobject_class->set_property = clutter_script_set_property; gobject_class->get_property = clutter_script_get_property; gobject_class->finalize = clutter_script_finalize; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_script_init (ClutterScript *script) { ClutterScriptPrivate *priv; script->priv = priv = clutter_script_get_instance_private (script); priv->parser = g_object_new (CLUTTER_TYPE_SCRIPT_PARSER, NULL); priv->parser->script = script; priv->is_filename = FALSE; priv->last_merge_id = 0; priv->objects = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, object_info_free); priv->states = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) g_object_unref); } /** * clutter_script_new: * * Creates a new #ClutterScript instance. #ClutterScript can be used to load * objects definitions for scenegraph elements, like actors, or behavioural * elements, like timelines. The definitions must be encoded using the * JavaScript Object Notation (JSON) language. * * Return value: the newly created #ClutterScript instance. Use * g_object_unref() when done. * * Since: 0.6 */ ClutterScript * clutter_script_new (void) { return g_object_new (CLUTTER_TYPE_SCRIPT, NULL); } /** * clutter_script_load_from_file: * @script: a #ClutterScript * @filename: the full path to the definition file * @error: return location for a #GError, or %NULL * * Loads the definitions from @filename into @script and merges with * the currently loaded ones, if any. * * Return value: on error, zero is returned and @error is set * accordingly. On success, the merge id for the UI definitions is * returned. You can use the merge id with clutter_script_unmerge_objects(). * * Since: 0.6 */ guint clutter_script_load_from_file (ClutterScript *script, const gchar *filename, GError **error) { ClutterScriptPrivate *priv; GError *internal_error; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0); g_return_val_if_fail (filename != NULL, 0); priv = script->priv; g_free (priv->filename); priv->filename = g_strdup (filename); priv->is_filename = TRUE; priv->last_merge_id += 1; internal_error = NULL; json_parser_load_from_file (JSON_PARSER (priv->parser), filename, &internal_error); if (internal_error) { g_propagate_error (error, internal_error); priv->last_merge_id -= 1; return 0; } return priv->last_merge_id; } /** * clutter_script_load_from_data: * @script: a #ClutterScript * @data: a buffer containing the definitions * @length: the length of the buffer, or -1 if @data is a NUL-terminated * buffer * @error: return location for a #GError, or %NULL * * Loads the definitions from @data into @script and merges with * the currently loaded ones, if any. * * Return value: on error, zero is returned and @error is set * accordingly. On success, the merge id for the UI definitions is * returned. You can use the merge id with clutter_script_unmerge_objects(). * * Since: 0.6 */ guint clutter_script_load_from_data (ClutterScript *script, const gchar *data, gssize length, GError **error) { ClutterScriptPrivate *priv; GError *internal_error; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0); g_return_val_if_fail (data != NULL, 0); if (length < 0) length = strlen (data); priv = script->priv; g_free (priv->filename); priv->filename = NULL; priv->is_filename = FALSE; priv->last_merge_id += 1; internal_error = NULL; json_parser_load_from_data (JSON_PARSER (priv->parser), data, length, &internal_error); if (internal_error) { g_propagate_error (error, internal_error); priv->last_merge_id -= 1; return 0; } return priv->last_merge_id; } /** * clutter_script_load_from_resource: * @script: a #ClutterScript * @resource_path: the resource path of the file to parse * @error: return location for a #GError, or %NULL * * Loads the definitions from a resource file into @script and merges with * the currently loaded ones, if any. * * Return value: on error, zero is returned and @error is set * accordingly. On success, the merge id for the UI definitions is * returned. You can use the merge id with clutter_script_unmerge_objects(). * * Since: 1.10 */ guint clutter_script_load_from_resource (ClutterScript *script, const gchar *resource_path, GError **error) { GBytes *data; guint res; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0); data = g_resources_lookup_data (resource_path, 0, error); if (data == NULL) return 0; res = clutter_script_load_from_data (script, g_bytes_get_data (data, NULL), g_bytes_get_size (data), error); g_bytes_unref (data); return res; } /** * clutter_script_get_object: * @script: a #ClutterScript * @name: the name of the object to retrieve * * Retrieves the object bound to @name. This function does not increment * the reference count of the returned object. * * Return value: (transfer none): the named object, or %NULL if no object * with the given name was available * * Since: 0.6 */ GObject * clutter_script_get_object (ClutterScript *script, const gchar *name) { ObjectInfo *oinfo; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL); g_return_val_if_fail (name != NULL, NULL); oinfo = g_hash_table_lookup (script->priv->objects, name); if (!oinfo) return NULL; _clutter_script_construct_object (script, oinfo); _clutter_script_apply_properties (script, oinfo); return oinfo->object; } static gint clutter_script_get_objects_valist (ClutterScript *script, const gchar *first_name, va_list args) { gint retval = 0; const gchar *name; name = first_name; while (name) { GObject **obj = NULL; obj = va_arg (args, GObject**); *obj = clutter_script_get_object (script, name); if (*obj) retval += 1; name = va_arg (args, gchar*); } return retval; } /** * clutter_script_get_objects: * @script: a #ClutterScript * @first_name: the name of the first object to retrieve * @...: return location for a #GObject, then additional names, ending * with %NULL * * Retrieves a list of objects for the given names. After @script, object * names/return location pairs should be listed, with a %NULL pointer * ending the list, like: * * |[ * GObject *my_label, *a_button, *main_timeline; * * clutter_script_get_objects (script, * "my-label", &my_label, * "a-button", &a_button, * "main-timeline", &main_timeline, * NULL); * ]| * * Note: This function does not increment the reference count of the * returned objects. * * Return value: the number of objects returned. * * Since: 0.6 */ gint clutter_script_get_objects (ClutterScript *script, const gchar *first_name, ...) { gint retval; va_list var_args; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), 0); g_return_val_if_fail (first_name != NULL, 0); va_start (var_args, first_name); retval = clutter_script_get_objects_valist (script, first_name, var_args); va_end (var_args); return retval; } typedef struct { ClutterScript *script; guint merge_id; GSList *ids; } UnmergeData; static void remove_by_merge_id (gpointer key, gpointer value, gpointer data) { gchar *name = key; ObjectInfo *oinfo = value; UnmergeData *unmerge_data = data; if (oinfo->merge_id == unmerge_data->merge_id) { CLUTTER_NOTE (SCRIPT, "Unmerging object (id:%s, type:%s, merge-id:%d)", oinfo->id, oinfo->class_name, oinfo->merge_id); unmerge_data->ids = g_slist_prepend (unmerge_data->ids, g_strdup (name)); oinfo->is_unmerged = TRUE; } } /** * clutter_script_unmerge_objects: * @script: a #ClutterScript * @merge_id: merge id returned when loading a UI definition * * Unmerges the objects identified by @merge_id. * * Since: 0.6 */ void clutter_script_unmerge_objects (ClutterScript *script, guint merge_id) { ClutterScriptPrivate *priv; UnmergeData data; GSList *l; g_return_if_fail (CLUTTER_IS_SCRIPT (script)); g_return_if_fail (merge_id > 0); priv = script->priv; data.script = script; data.merge_id = merge_id; data.ids = NULL; g_hash_table_foreach (priv->objects, remove_by_merge_id, &data); for (l = data.ids; l != NULL; l = l->next) g_hash_table_remove (priv->objects, l->data); g_slist_free_full (data.ids, g_free); clutter_script_ensure_objects (script); } static void construct_each_objects (gpointer key, gpointer value, gpointer user_data) { ClutterScript *script = user_data; ObjectInfo *oinfo = value; /* we have unfinished business */ if (oinfo->has_unresolved) { /* this should not happen, but resilence is * a good thing in a parser */ if (oinfo->object == NULL) _clutter_script_construct_object (script, oinfo); /* this will take care of setting up properties and adding children */ _clutter_script_apply_properties (script, oinfo); } } /** * clutter_script_ensure_objects: * @script: a #ClutterScript * * Ensure that every object defined inside @script is correctly * constructed. You should rarely need to use this function. * * Since: 0.6 */ void clutter_script_ensure_objects (ClutterScript *script) { ClutterScriptPrivate *priv; g_return_if_fail (CLUTTER_IS_SCRIPT (script)); priv = script->priv; g_hash_table_foreach (priv->objects, construct_each_objects, script); } /** * clutter_script_get_type_from_name: * @script: a #ClutterScript * @type_name: name of the type to look up * * Looks up a type by name, using the virtual function that * #ClutterScript has for that purpose. This function should * rarely be used. * * Return value: the type for the requested type name, or * %G_TYPE_INVALID if not corresponding type was found. * * Since: 0.6 */ GType clutter_script_get_type_from_name (ClutterScript *script, const gchar *type_name) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), G_TYPE_INVALID); g_return_val_if_fail (type_name != NULL, G_TYPE_INVALID); return CLUTTER_SCRIPT_GET_CLASS (script)->get_type_from_name (script, type_name); } /** * clutter_get_script_id: * @gobject: a #GObject * * Retrieves the Clutter script id, if any. * * Return value: the script id, or %NULL if @object was not defined inside * a UI definition file. The returned string is owned by the object and * should never be modified or freed. * * Since: 0.6 */ const gchar * clutter_get_script_id (GObject *gobject) { g_return_val_if_fail (G_IS_OBJECT (gobject), NULL); if (CLUTTER_IS_SCRIPTABLE (gobject)) return clutter_scriptable_get_id (CLUTTER_SCRIPTABLE (gobject)); else return g_object_get_data (gobject, "clutter-script-id"); } typedef struct { GModule *module; gpointer data; } ConnectData; /* default signal connection code */ static void clutter_script_default_connect (ClutterScript *script, GObject *gobject, const gchar *signal_name, const gchar *signal_handler, GObject *connect_gobject, GConnectFlags flags, gpointer user_data) { ConnectData *data = user_data; GCallback function; if (!data->module) return; if (!g_module_symbol (data->module, signal_handler, (gpointer) &function)) { g_warning ("Could not find a signal handler '%s' for signal '%s::%s'", signal_handler, connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject) : G_OBJECT_TYPE_NAME (gobject), signal_name); return; } CLUTTER_NOTE (SCRIPT, "connecting %s::%s to %s (afetr:%s, swapped:%s, object:%s)", (connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject) : G_OBJECT_TYPE_NAME (gobject)), signal_name, signal_handler, (flags & G_CONNECT_AFTER) ? "true" : "false", (flags & G_CONNECT_SWAPPED) ? "true" : "false", (connect_gobject ? G_OBJECT_TYPE_NAME (connect_gobject) : "")); if (connect_gobject != NULL) g_signal_connect_object (gobject, signal_name, function, connect_gobject, flags); else g_signal_connect_data (gobject, signal_name, function, data->data, NULL, flags); } /** * clutter_script_connect_signals: * @script: a #ClutterScript * @user_data: data to be passed to the signal handlers, or %NULL * * Connects all the signals defined into a UI definition file to their * handlers. * * This method invokes clutter_script_connect_signals_full() internally * and uses #GModule's introspective features (by opening the current * module's scope) to look at the application's symbol table. * * Note that this function will not work if #GModule is not supported by * the platform Clutter is running on. * * Since: 0.6 */ void clutter_script_connect_signals (ClutterScript *script, gpointer user_data) { ConnectData *cd; g_return_if_fail (CLUTTER_IS_SCRIPT (script)); if (!g_module_supported ()) { g_critical ("clutter_script_connect_signals() requires a working " "GModule support from GLib"); return; } cd = g_new (ConnectData, 1); cd->module = g_module_open (NULL, 0); cd->data = user_data; clutter_script_connect_signals_full (script, clutter_script_default_connect, cd); g_module_close (cd->module); g_free (cd); } typedef struct { ClutterState *state; GObject *emitter; gchar *target; gulong signal_id; gulong hook_id; gboolean warp_to; } HookData; typedef struct { ClutterScript *script; ClutterScriptConnectFunc func; gpointer user_data; } SignalConnectData; static void hook_data_free (gpointer data) { if (G_LIKELY (data != NULL)) { HookData *hook_data = data; g_free (hook_data->target); g_slice_free (HookData, hook_data); } } static gboolean clutter_script_state_change_hook (GSignalInvocationHint *ihint, guint n_params, const GValue *params, gpointer user_data) { HookData *hook_data = user_data; GObject *emitter; emitter = g_value_get_object (¶ms[0]); if (emitter == hook_data->emitter) { if (hook_data->warp_to) clutter_state_warp_to_state (hook_data->state, hook_data->target); else clutter_state_set_state (hook_data->state, hook_data->target); } return TRUE; } static void clutter_script_remove_state_change_hook (gpointer user_data, GObject *object_p) { HookData *hook_data = user_data; g_signal_remove_emission_hook (hook_data->signal_id, hook_data->hook_id); } static void connect_each_object (gpointer key, gpointer value, gpointer data) { SignalConnectData *connect_data = data; ClutterScript *script = connect_data->script; ObjectInfo *oinfo = value; GObject *object = oinfo->object; GList *unresolved, *l; _clutter_script_construct_object (script, oinfo); unresolved = NULL; for (l = oinfo->signals; l != NULL; l = l->next) { SignalInfo *sinfo = l->data; if (sinfo->is_handler) { GObject *connect_object = NULL; if (sinfo->object) connect_object = clutter_script_get_object (script, sinfo->object); if (sinfo->object && !connect_object) unresolved = g_list_prepend (unresolved, sinfo); else { connect_data->func (script, object, sinfo->name, sinfo->handler, connect_object, sinfo->flags, connect_data->user_data); } } else { GObject *state_object = NULL; const gchar *signal_name, *signal_detail; gchar **components; GQuark signal_quark; guint signal_id; HookData *hook_data; if (sinfo->state == NULL) state_object = (GObject *) clutter_script_get_states (script, NULL); else { state_object = clutter_script_get_object (script, sinfo->state); if (state_object == NULL) state_object = (GObject *) clutter_script_get_states (script, sinfo->state); } if (state_object == NULL) continue; components = g_strsplit (sinfo->name, "::", 2); if (g_strv_length (components) == 2) { signal_name = components[0]; signal_detail = components[1]; } else { signal_name = components[0]; signal_detail = NULL; } signal_id = g_signal_lookup (signal_name, G_OBJECT_TYPE (object)); if (signal_id == 0) { g_strfreev (components); continue; } if (signal_detail != NULL) signal_quark = g_quark_from_string (signal_detail); else signal_quark = 0; hook_data = g_slice_new (HookData); hook_data->emitter = object; hook_data->state = CLUTTER_STATE (state_object); hook_data->target = g_strdup (sinfo->target); hook_data->warp_to = sinfo->warp_to; hook_data->signal_id = signal_id; hook_data->hook_id = g_signal_add_emission_hook (signal_id, signal_quark, clutter_script_state_change_hook, hook_data, hook_data_free); g_object_weak_ref (hook_data->emitter, clutter_script_remove_state_change_hook, hook_data); } signal_info_free (sinfo); } /* keep the unresolved signal handlers around, in case * clutter_script_connect_signals() is called multiple * times (e.g. after a UI definition merge) */ g_list_free (oinfo->signals); oinfo->signals = unresolved; } /** * clutter_script_connect_signals_full: * @script: a #ClutterScript * @func: (scope call): signal connection function * @user_data: data to be passed to the signal handlers, or %NULL * * Connects all the signals defined into a UI definition file to their * handlers. * * This function allows to control how the signal handlers are * going to be connected to their respective signals. It is meant * primarily for language bindings to allow resolving the function * names using the native API, but it can also be used on platforms * that do not support GModule. * * Applications should use clutter_script_connect_signals(). * * Since: 0.6 */ void clutter_script_connect_signals_full (ClutterScript *script, ClutterScriptConnectFunc func, gpointer user_data) { SignalConnectData data; g_return_if_fail (CLUTTER_IS_SCRIPT (script)); g_return_if_fail (func != NULL); data.script = script; data.func = func; data.user_data = user_data; g_hash_table_foreach (script->priv->objects, connect_each_object, &data); } GQuark clutter_script_error_quark (void) { return g_quark_from_static_string ("clutter-script-error"); } /** * clutter_script_add_search_paths: * @script: a #ClutterScript * @paths: (array length=n_paths): an array of strings containing * different search paths * @n_paths: the length of the passed array * * Adds @paths to the list of search paths held by @script. * * The search paths are used by clutter_script_lookup_filename(), which * can be used to define search paths for the textures source file name * or other custom, file-based properties. * * Since: 0.8 */ void clutter_script_add_search_paths (ClutterScript *script, const gchar * const paths[], gsize n_paths) { ClutterScriptPrivate *priv; gchar **old_paths, **new_paths; gsize old_paths_len, i; gsize iter = 0; g_return_if_fail (CLUTTER_IS_SCRIPT (script)); g_return_if_fail (paths != NULL); g_return_if_fail (n_paths > 0); priv = script->priv; if (priv->search_paths) { old_paths = priv->search_paths; old_paths_len = g_strv_length (old_paths); } else { old_paths = NULL; old_paths_len = 0; } new_paths = g_new0 (gchar*, old_paths_len + n_paths + 1); for (i = 0, iter = 0; i < old_paths_len; i++, iter++) new_paths[iter] = g_strdup (old_paths[i]); for (i = 0; i < n_paths; i++, iter++) new_paths[iter] = g_strdup (paths[i]); CLUTTER_NOTE (SCRIPT, "Added %" G_GSIZE_FORMAT " new search paths (new size: %d)", n_paths, g_strv_length (new_paths)); priv->search_paths = new_paths; if (old_paths) g_strfreev (old_paths); } /** * clutter_script_lookup_filename: * @script: a #ClutterScript * @filename: the name of the file to lookup * * Looks up @filename inside the search paths of @script. If @filename * is found, its full path will be returned . * * Return value: the full path of @filename or %NULL if no path was * found. * * Since: 0.8 */ gchar * clutter_script_lookup_filename (ClutterScript *script, const gchar *filename) { ClutterScriptPrivate *priv; gchar *dirname; gchar *retval; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL); g_return_val_if_fail (filename != NULL, NULL); if (g_path_is_absolute (filename)) return g_strdup (filename); priv = script->priv; if (priv->search_paths) { gsize paths_len, i; paths_len = g_strv_length (priv->search_paths); for (i = 0; i < paths_len; i++) { retval = g_build_filename (priv->search_paths[i], filename, NULL); if (g_file_test (retval, G_FILE_TEST_EXISTS)) return retval; else { g_free (retval); retval = NULL; } } } /* Fall back to assuming relative to our script */ if (priv->is_filename) dirname = g_path_get_dirname (script->priv->filename); else dirname = g_get_current_dir (); retval = g_build_filename (dirname, filename, NULL); if (!g_file_test (retval, G_FILE_TEST_EXISTS)) { g_free (retval); retval = NULL; } g_free (dirname); return retval; } /** * clutter_script_list_objects: * @script: a #ClutterScript * * Retrieves all the objects created by @script. * * Note: this function does not increment the reference count of the * objects it returns. * * Return value: (transfer container) (element-type GObject.Object): a list * of #GObjects, or %NULL. The objects are owned by the * #ClutterScript instance. Use g_list_free() on the returned list when * done. * * Since: 0.8 */ GList * clutter_script_list_objects (ClutterScript *script) { GList *objects, *l; GList *retval; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL); clutter_script_ensure_objects (script); if (!script->priv->objects) return NULL; retval = NULL; objects = g_hash_table_get_values (script->priv->objects); for (l = objects; l != NULL; l = l->next) { ObjectInfo *oinfo = l->data; if (oinfo->object) retval = g_list_prepend (retval, oinfo->object); } g_list_free (objects); return retval; } /** * clutter_script_add_states: * @script: a #ClutterScript * @name: (allow-none): a name for the @state, or %NULL to * set the default #ClutterState * @state: a #ClutterState * * Associates a #ClutterState to the #ClutterScript instance using the given * name. * * The #ClutterScript instance will use @state to resolve target states when * connecting signal handlers. * * The #ClutterScript instance will take a reference on the #ClutterState * passed to this function. * * Since: 1.8 * * Deprecated: 1.12 */ void clutter_script_add_states (ClutterScript *script, const gchar *name, ClutterState *state) { g_return_if_fail (CLUTTER_IS_SCRIPT (script)); g_return_if_fail (CLUTTER_IS_STATE (state)); if (name == NULL || *name == '\0') name = "__clutter_script_default_state"; g_hash_table_replace (script->priv->states, g_strdup (name), g_object_ref (state)); } /** * clutter_script_get_states: * @script: a #ClutterScript * @name: (allow-none): the name of the #ClutterState, or %NULL * * Retrieves the #ClutterState for the given @state_name. * * If @name is %NULL, this function will return the default * #ClutterState instance. * * Return value: (transfer none): a pointer to the #ClutterState for the * given name. The #ClutterState is owned by the #ClutterScript instance * and it should not be unreferenced * * Since: 1.8 * * Deprecated: 1.12 */ ClutterState * clutter_script_get_states (ClutterScript *script, const gchar *name) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL); if (name == NULL || *name == '\0') name = "__clutter_script_default_state"; return g_hash_table_lookup (script->priv->states, name); } /** * clutter_script_set_translation_domain: * @script: a #ClutterScript * @domain: (allow-none): the translation domain, or %NULL * * Sets the translation domain for @script. * * Since: 1.10 */ void clutter_script_set_translation_domain (ClutterScript *script, const gchar *domain) { g_return_if_fail (CLUTTER_IS_SCRIPT (script)); if (g_strcmp0 (domain, script->priv->translation_domain) == 0) return; g_free (script->priv->translation_domain); script->priv->translation_domain = g_strdup (domain); g_object_notify_by_pspec (G_OBJECT (script), obj_props[PROP_TRANSLATION_DOMAIN]); } /** * clutter_script_get_translation_domain: * @script: a #ClutterScript * * Retrieves the translation domain set using * clutter_script_set_translation_domain(). * * Return value: (transfer none): the translation domain, if any is set, * or %NULL * * Since: 1.10 */ const gchar * clutter_script_get_translation_domain (ClutterScript *script) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), NULL); return script->priv->translation_domain; } /* * _clutter_script_generate_fake_id: * @script: a #ClutterScript * * Generates a fake id string for object definitions without * an "id" member * * Return value: a newly-allocated string containing the fake * id. Use g_free() to free the resources allocated by the * returned value * */ gchar * _clutter_script_generate_fake_id (ClutterScript *script) { ClutterScriptPrivate *priv = script->priv; return g_strdup_printf ("script-%d-%d", priv->last_merge_id, priv->last_unknown++); } /* * _clutter_script_warn_missing_attribute: * @script: a #ClutterScript * @id_: the id of an object definition, or %NULL * @attribute: the expected attribute * * Emits a warning, using GLib's log facilities, for a missing * @attribute in an object definition, pointing to the current * location of the #ClutterScriptParser */ void _clutter_script_warn_missing_attribute (ClutterScript *script, const gchar *id_, const gchar *attribute) { ClutterScriptPrivate *priv = script->priv; JsonParser *parser = JSON_PARSER (priv->parser); gint current_line = json_parser_get_current_line (parser); if (id_ != NULL && *id_ != '\0') { g_warning ("%s:%d: object '%s' has no '%s' attribute", priv->is_filename ? priv->filename : "", current_line, id_, attribute); } else { g_warning ("%s:%d: object has no '%s' attribute", priv->is_filename ? priv->filename : "", current_line, attribute); } } /* * _clutter_script_warn_invalid_value: * @script: a #ClutterScript * @attribute: the attribute with the invalid value * @expected: a string with the expected value * @node: a #JsonNode containing the value * * Emits a warning, using GLib's log facilities, for an invalid * value found when parsing @attribute, pointing to the current * location of the #ClutterScriptParser */ void _clutter_script_warn_invalid_value (ClutterScript *script, const gchar *attribute, const gchar *expected, JsonNode *node) { ClutterScriptPrivate *priv = script->priv; JsonParser *parser = JSON_PARSER (priv->parser); gint current_line = json_parser_get_current_line (parser); if (node != NULL) { g_warning ("%s:%d: invalid value of type '%s' for attribute '%s':" "a value of type '%s' is expected", priv->is_filename ? priv->filename : "", current_line, json_node_type_name (node), attribute, expected); } else { g_warning ("%s:%d: invalid value for attribute '%s':" "a value of type '%s' is expected", priv->is_filename ? priv->filename : "", current_line, attribute, expected); } } /* * _clutter_script_get_object_info: * @script: a #ClutterScript * @script_id: the id of the object definition * * Retrieves the #ObjectInfo for the given @script_id * * Return value: a #ObjectInfo or %NULL */ ObjectInfo * _clutter_script_get_object_info (ClutterScript *script, const gchar *script_id) { ClutterScriptPrivate *priv = script->priv; return g_hash_table_lookup (priv->objects, script_id); } /* * _clutter_script_get_last_merge_id: * @script: a #ClutterScript * * Retrieves the last merge id of @script. The merge id * should be stored inside an #ObjectInfo. If you need * a unique fake id for object definitions with an "id" * member, consider using _clutter_script_generate_fake_id() * instead * * Return value: the last merge id */ guint _clutter_script_get_last_merge_id (ClutterScript *script) { return script->priv->last_merge_id; } /* * _clutter_script_add_object_info: * @script: a #ClutterScript * @oinfo: a #ObjectInfo * * Adds @oinfo inside the objects list held by @script */ void _clutter_script_add_object_info (ClutterScript *script, ObjectInfo *oinfo) { ClutterScriptPrivate *priv = script->priv; g_hash_table_steal (priv->objects, oinfo->id); g_hash_table_insert (priv->objects, oinfo->id, oinfo); } muffin-6.4.1/clutter/clutter/clutter-stage.c0000664000175000017500000043231414723361714020044 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-stage * @short_description: Top level visual element to which actors are placed. * * #ClutterStage is a top level 'window' on which child actors are placed * and manipulated. * * Backends might provide support for multiple stages. The support for this * feature can be checked at run-time using the clutter_feature_available() * function and the %CLUTTER_FEATURE_STAGE_MULTIPLE flag. If the backend used * supports multiple stages, new #ClutterStage instances can be created * using clutter_stage_new(). These stages must be managed by the developer * using clutter_actor_destroy(), which will take care of destroying all the * actors contained inside them. * * #ClutterStage is a proxy actor, wrapping the backend-specific implementation * (a #StageWindow) of the windowing system. It is possible to subclass * #ClutterStage, as long as every overridden virtual function chains up to the * parent class corresponding function. */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-stage.h" #include "deprecated/clutter-stage.h" #include "deprecated/clutter-container.h" #include "clutter-actor-private.h" #include "clutter-backend-private.h" #include "clutter-cairo.h" #include "clutter-color.h" #include "clutter-container.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-event-private.h" #include "clutter-id-pool.h" #include "clutter-input-device-private.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-master-clock.h" #include "clutter-mutter.h" #include "clutter-paint-context-private.h" #include "clutter-paint-volume-private.h" #include "clutter-pick-context-private.h" #include "clutter-private.h" #include "clutter-stage-manager-private.h" #include "clutter-stage-private.h" #include "clutter-stage-view-private.h" #include "clutter-private.h" #include "cogl/cogl.h" struct _ClutterStageQueueRedrawEntry { ClutterActor *actor; gboolean has_clip; ClutterPaintVolume clip; }; typedef struct _PickRecord { graphene_point_t vertex[4]; ClutterActor *actor; int clip_stack_top; } PickRecord; typedef struct _PickClipRecord { int prev; graphene_point_t vertex[4]; } PickClipRecord; typedef struct _PointerDeviceEntry { ClutterStage *stage; ClutterInputDevice *device; ClutterEventSequence *sequence; graphene_point_t coords; ClutterActor *current_actor; } PointerDeviceEntry; struct _ClutterStagePrivate { /* the stage implementation */ ClutterStageWindow *impl; ClutterPerspective perspective; CoglMatrix projection; CoglMatrix inverse_projection; CoglMatrix view; float viewport[4]; gchar *title; ClutterActor *key_focused_actor; GQueue *event_queue; GArray *paint_volume_stack; ClutterPlane current_clip_planes[4]; GHashTable *pending_relayouts; unsigned int pending_relayouts_version; GList *pending_queue_redraws; gint sync_delay; GTimer *fps_timer; gint32 timer_n_frames; GArray *pick_stack; GArray *pick_clip_stack; int pick_clip_stack_top; gboolean pick_stack_frozen; ClutterPickMode cached_pick_mode; #ifdef CLUTTER_ENABLE_DEBUG gulong redraw_count; #endif /* CLUTTER_ENABLE_DEBUG */ ClutterStageState current_state; gpointer paint_data; GDestroyNotify paint_notify; int update_freeze_count; gboolean needs_update; GHashTable *pointer_devices; GHashTable *touch_sequences; guint redraw_pending : 1; guint is_cursor_visible : 1; guint throttle_motion_events : 1; guint use_alpha : 1; guint min_size_changed : 1; guint accept_focus : 1; guint motion_events_enabled : 1; guint has_custom_perspective : 1; guint stage_was_relayout : 1; }; enum { PROP_0, PROP_COLOR, PROP_CURSOR_VISIBLE, PROP_PERSPECTIVE, PROP_TITLE, PROP_USE_ALPHA, PROP_KEY_FOCUS, PROP_ACCEPT_FOCUS, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; enum { ACTIVATE, DEACTIVATE, DELETE_EVENT, BEFORE_UPDATE, BEFORE_PAINT, AFTER_PAINT, AFTER_UPDATE, PAINT_VIEW, PRESENTED, LAST_SIGNAL }; static guint stage_signals[LAST_SIGNAL] = { 0, }; static const ClutterColor default_stage_color = { 255, 255, 255, 255 }; static void clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage); static void free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry); static void free_pointer_device_entry (PointerDeviceEntry *entry); static void capture_view_into (ClutterStage *stage, gboolean paint, ClutterStageView *view, cairo_rectangle_int_t *rect, uint8_t *data, int stride); static void clutter_stage_update_view_perspective (ClutterStage *stage); static void clutter_container_iface_init (ClutterContainerIface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterStage, clutter_stage, CLUTTER_TYPE_GROUP, G_ADD_PRIVATE (ClutterStage) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)) static void clutter_stage_real_add (ClutterContainer *container, ClutterActor *child) { clutter_actor_add_child (CLUTTER_ACTOR (container), child); } static void clutter_stage_real_remove (ClutterContainer *container, ClutterActor *child) { clutter_actor_remove_child (CLUTTER_ACTOR (container), child); } static void clutter_stage_real_raise (ClutterContainer *container, ClutterActor *child, ClutterActor *sibling) { clutter_actor_set_child_above_sibling (CLUTTER_ACTOR (container), child, sibling); } static void clutter_stage_real_lower (ClutterContainer *container, ClutterActor *child, ClutterActor *sibling) { clutter_actor_set_child_below_sibling (CLUTTER_ACTOR (container), child, sibling); } static void clutter_stage_real_sort_depth_order (ClutterContainer *container) { } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->add = clutter_stage_real_add; iface->remove = clutter_stage_real_remove; iface->raise = clutter_stage_real_raise; iface->lower = clutter_stage_real_lower; iface->sort_depth_order = clutter_stage_real_sort_depth_order; } static void clutter_stage_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; cairo_rectangle_int_t geom; if (priv->impl == NULL) return; _clutter_stage_window_get_geometry (priv->impl, &geom); if (min_width_p) *min_width_p = geom.width; if (natural_width_p) *natural_width_p = geom.width; } static void clutter_stage_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; cairo_rectangle_int_t geom; if (priv->impl == NULL) return; _clutter_stage_window_get_geometry (priv->impl, &geom); if (min_height_p) *min_height_p = geom.height; if (natural_height_p) *natural_height_p = geom.height; } static void add_pick_stack_weak_refs (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; int i; if (priv->pick_stack_frozen) return; for (i = 0; i < priv->pick_stack->len; i++) { PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); if (rec->actor) g_object_add_weak_pointer (G_OBJECT (rec->actor), (gpointer) &rec->actor); } priv->pick_stack_frozen = TRUE; } static void remove_pick_stack_weak_refs (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; int i; if (!priv->pick_stack_frozen) return; for (i = 0; i < priv->pick_stack->len; i++) { PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); if (rec->actor) g_object_remove_weak_pointer (G_OBJECT (rec->actor), (gpointer) &rec->actor); } priv->pick_stack_frozen = FALSE; } static void _clutter_stage_clear_pick_stack (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; remove_pick_stack_weak_refs (stage); g_array_set_size (priv->pick_stack, 0); g_array_set_size (priv->pick_clip_stack, 0); priv->pick_clip_stack_top = -1; priv->cached_pick_mode = CLUTTER_PICK_NONE; } void clutter_stage_log_pick (ClutterStage *stage, const graphene_point_t *vertices, ClutterActor *actor) { ClutterStagePrivate *priv; PickRecord rec; g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (actor != NULL); priv = stage->priv; g_assert (!priv->pick_stack_frozen); memcpy (rec.vertex, vertices, 4 * sizeof (graphene_point_t)); rec.actor = actor; rec.clip_stack_top = priv->pick_clip_stack_top; g_array_append_val (priv->pick_stack, rec); } void clutter_stage_push_pick_clip (ClutterStage *stage, const graphene_point_t *vertices) { ClutterStagePrivate *priv; PickClipRecord clip; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; g_assert (!priv->pick_stack_frozen); clip.prev = priv->pick_clip_stack_top; memcpy (clip.vertex, vertices, 4 * sizeof (graphene_point_t)); g_array_append_val (priv->pick_clip_stack, clip); priv->pick_clip_stack_top = priv->pick_clip_stack->len - 1; } void clutter_stage_pop_pick_clip (ClutterStage *stage) { ClutterStagePrivate *priv; const PickClipRecord *top; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; g_assert (!priv->pick_stack_frozen); g_assert (priv->pick_clip_stack_top >= 0); /* Individual elements of pick_clip_stack are not freed. This is so they * can be shared as part of a tree of different stacks used by different * actors in the pick_stack. The whole pick_clip_stack does however get * freed later in _clutter_stage_clear_pick_stack. */ top = &g_array_index (priv->pick_clip_stack, PickClipRecord, priv->pick_clip_stack_top); priv->pick_clip_stack_top = top->prev; } static gboolean is_quadrilateral_axis_aligned_rectangle (const graphene_point_t *vertices) { int i; for (i = 0; i < 4; i++) { if (!G_APPROX_VALUE (vertices[i].x, vertices[(i + 1) % 4].x, FLT_EPSILON) && !G_APPROX_VALUE (vertices[i].y, vertices[(i + 1) % 4].y, FLT_EPSILON)) return FALSE; } return TRUE; } static gboolean is_inside_axis_aligned_rectangle (const graphene_point_t *point, const graphene_point_t *vertices) { float min_x = FLT_MAX; float max_x = -FLT_MAX; float min_y = FLT_MAX; float max_y = -FLT_MAX; int i; for (i = 0; i < 3; i++) { min_x = MIN (min_x, vertices[i].x); min_y = MIN (min_y, vertices[i].y); max_x = MAX (max_x, vertices[i].x); max_y = MAX (max_y, vertices[i].y); } return (point->x >= min_x && point->y >= min_y && point->x < max_x && point->y < max_y); } static int clutter_point_compare_line (const graphene_point_t *p, const graphene_point_t *a, const graphene_point_t *b) { graphene_vec3_t vec_pa; graphene_vec3_t vec_pb; graphene_vec3_t cross; float cross_z; graphene_vec3_init (&vec_pa, p->x - a->x, p->y - a->y, 0.f); graphene_vec3_init (&vec_pb, p->x - b->x, p->y - b->y, 0.f); graphene_vec3_cross (&vec_pa, &vec_pb, &cross); cross_z = graphene_vec3_get_z (&cross); if (cross_z > 0.f) return 1; else if (cross_z < 0.f) return -1; else return 0; } static gboolean is_inside_unaligned_rectangle (const graphene_point_t *point, const graphene_point_t *vertices) { unsigned int i; int first_side; first_side = 0; for (i = 0; i < 4; i++) { int side; side = clutter_point_compare_line (point, &vertices[i], &vertices[(i + 1) % 4]); if (side) { if (first_side == 0) first_side = side; else if (side != first_side) return FALSE; } } if (first_side == 0) return FALSE; return TRUE; } static gboolean is_inside_input_region (const graphene_point_t *point, const graphene_point_t *vertices) { if (is_quadrilateral_axis_aligned_rectangle (vertices)) return is_inside_axis_aligned_rectangle (point, vertices); else return is_inside_unaligned_rectangle (point, vertices); } static gboolean pick_record_contains_point (ClutterStage *stage, const PickRecord *rec, float x, float y) { const graphene_point_t point = GRAPHENE_POINT_INIT (x, y); ClutterStagePrivate *priv; int clip_index; if (!is_inside_input_region (&point, rec->vertex)) return FALSE; priv = stage->priv; clip_index = rec->clip_stack_top; while (clip_index >= 0) { const PickClipRecord *clip = &g_array_index (priv->pick_clip_stack, PickClipRecord, clip_index); if (!is_inside_input_region (&point, clip->vertex)) return FALSE; clip_index = clip->prev; } return TRUE; } static void clutter_stage_add_redraw_clip (ClutterStage *stage, cairo_rectangle_int_t *clip) { GList *l; for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) { ClutterStageView *view = l->data; if (!clip) { clutter_stage_view_add_redraw_clip (view, NULL); } else { cairo_rectangle_int_t view_layout; cairo_rectangle_int_t intersection; clutter_stage_view_get_layout (view, &view_layout); if (_clutter_util_rectangle_intersection (&view_layout, clip, &intersection)) clutter_stage_view_add_redraw_clip (view, &intersection); } } } static inline void queue_full_redraw (ClutterStage *stage) { ClutterStageWindow *stage_window; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return; clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); /* Just calling clutter_actor_queue_redraw will typically only * redraw the bounding box of the children parented on the stage but * in this case we really need to ensure that the full stage is * redrawn so we add a NULL redraw clip to the stage window. */ stage_window = _clutter_stage_get_window (stage); if (stage_window == NULL) return; clutter_stage_add_redraw_clip (stage, NULL); } static gboolean stage_is_default (ClutterStage *stage) { ClutterStageManager *stage_manager; ClutterStageWindow *impl; stage_manager = clutter_stage_manager_get_default (); if (stage != clutter_stage_manager_get_default_stage (stage_manager)) return FALSE; impl = _clutter_stage_get_window (stage); if (impl != _clutter_stage_get_default_window ()) return FALSE; return TRUE; } static void clutter_stage_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; ClutterActorBox alloc = CLUTTER_ACTOR_BOX_INIT_ZERO; float old_width, old_height; float new_width, new_height; float width, height; cairo_rectangle_int_t window_size; if (priv->impl == NULL) return; /* our old allocation */ clutter_actor_get_allocation_box (self, &alloc); clutter_actor_box_get_size (&alloc, &old_width, &old_height); /* the current allocation */ clutter_actor_box_get_size (box, &width, &height); /* the current Stage implementation size */ _clutter_stage_window_get_geometry (priv->impl, &window_size); /* if the stage is fixed size (for instance, it's using a EGL framebuffer) * then we simply ignore any allocation request and override the * allocation chain - because we cannot forcibly change the size of the * stage window. */ if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_STATIC)) { CLUTTER_NOTE (LAYOUT, "Following allocation to %.2fx%.2f (absolute origin %s)", width, height, (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED) ? "changed" : "not changed"); clutter_actor_set_allocation (self, box, flags | CLUTTER_DELEGATE_LAYOUT); /* Ensure the window is sized correctly */ if (priv->min_size_changed) { gfloat min_width, min_height; gboolean min_width_set, min_height_set; g_object_get (G_OBJECT (self), "min-width", &min_width, "min-width-set", &min_width_set, "min-height", &min_height, "min-height-set", &min_height_set, NULL); if (!min_width_set) min_width = 1; if (!min_height_set) min_height = 1; if (width < min_width) width = min_width; if (height < min_height) height = min_height; priv->min_size_changed = FALSE; } if (window_size.width != CLUTTER_NEARBYINT (width) || window_size.height != CLUTTER_NEARBYINT (height)) { _clutter_stage_window_resize (priv->impl, CLUTTER_NEARBYINT (width), CLUTTER_NEARBYINT (height)); } } else { ClutterActorBox override = { 0, }; /* override the passed allocation */ override.x1 = 0; override.y1 = 0; override.x2 = window_size.width; override.y2 = window_size.height; CLUTTER_NOTE (LAYOUT, "Overriding original allocation of %.2fx%.2f " "with %.2fx%.2f (absolute origin %s)", width, height, override.x2, override.y2, (flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED) ? "changed" : "not changed"); /* and store the overridden allocation */ clutter_actor_set_allocation (self, &override, flags | CLUTTER_DELEGATE_LAYOUT); } /* reset the viewport if the allocation effectively changed */ clutter_actor_get_allocation_box (self, &alloc); clutter_actor_box_get_size (&alloc, &new_width, &new_height); if (CLUTTER_NEARBYINT (old_width) != CLUTTER_NEARBYINT (new_width) || CLUTTER_NEARBYINT (old_height) != CLUTTER_NEARBYINT (new_height)) { int real_width = CLUTTER_NEARBYINT (new_width); int real_height = CLUTTER_NEARBYINT (new_height); _clutter_stage_set_viewport (CLUTTER_STAGE (self), 0, 0, real_width, real_height); /* Note: we don't assume that set_viewport will queue a full redraw * since it may bail-out early if something preemptively set the * viewport before the stage was really allocated its new size. */ queue_full_redraw (CLUTTER_STAGE (self)); } } typedef struct _Vector4 { float x, y, z, w; } Vector4; static void _cogl_util_get_eye_planes_for_screen_poly (float *polygon, int n_vertices, float *viewport, const CoglMatrix *projection, const CoglMatrix *inverse_project, ClutterPlane *planes) { float Wc; Vector4 *tmp_poly; ClutterPlane *plane; int i; Vector4 *poly; graphene_vec3_t b; graphene_vec3_t c; int count; tmp_poly = g_alloca (sizeof (Vector4) * n_vertices * 2); #define DEPTH -50 /* Determine W in clip-space (Wc) for a point (0, 0, DEPTH, 1) * * Note: the depth could be anything except 0. * * We will transform the polygon into clip coordinates using this * depth and then into eye coordinates. Our clip planes will be * defined by triangles that extend between points of the polygon at * DEPTH and corresponding points of the same polygon at DEPTH * 2. * * NB: Wc defines the position of the clip planes in clip * coordinates. Given a screen aligned cross section through the * frustum; coordinates range from [-Wc,Wc] left to right on the * x-axis and [Wc,-Wc] top to bottom on the y-axis. */ Wc = DEPTH * projection->wz + projection->ww; #define CLIP_X(X) ((((float)X - viewport[0]) * (2.0 / viewport[2])) - 1) * Wc #define CLIP_Y(Y) ((((float)Y - viewport[1]) * (2.0 / viewport[3])) - 1) * -Wc for (i = 0; i < n_vertices; i++) { tmp_poly[i].x = CLIP_X (polygon[i * 2]); tmp_poly[i].y = CLIP_Y (polygon[i * 2 + 1]); tmp_poly[i].z = DEPTH; tmp_poly[i].w = Wc; } Wc = DEPTH * 2 * projection->wz + projection->ww; /* FIXME: technically we don't need to project all of the points * twice, it would be enough project every other point since * we can share points in this set to define the plane vectors. */ for (i = 0; i < n_vertices; i++) { tmp_poly[n_vertices + i].x = CLIP_X (polygon[i * 2]); tmp_poly[n_vertices + i].y = CLIP_Y (polygon[i * 2 + 1]); tmp_poly[n_vertices + i].z = DEPTH * 2; tmp_poly[n_vertices + i].w = Wc; } #undef CLIP_X #undef CLIP_Y cogl_matrix_project_points (inverse_project, 4, sizeof (Vector4), tmp_poly, sizeof (Vector4), tmp_poly, n_vertices * 2); /* XXX: It's quite ugly that we end up with these casts between * Vector4 types and CoglVector3s, it might be better if the * cogl_vector APIs just took pointers to floats. */ count = n_vertices - 1; for (i = 0; i < count; i++) { plane = &planes[i]; poly = &tmp_poly[i]; graphene_vec3_init (&plane->v0, poly->x, poly->y, poly->z); poly = &tmp_poly[n_vertices + i]; graphene_vec3_init (&b, poly->x, poly->y, poly->z); poly = &tmp_poly[n_vertices + i + 1]; graphene_vec3_init (&c, poly->x, poly->y, poly->z); graphene_vec3_subtract (&b, &plane->v0, &b); graphene_vec3_subtract (&c, &plane->v0, &c); graphene_vec3_cross (&b, &c, &plane->n); graphene_vec3_normalize (&plane->n, &plane->n); } plane = &planes[n_vertices - 1]; poly = &tmp_poly[0]; graphene_vec3_init (&plane->v0, poly->x, poly->y, poly->z); poly = &tmp_poly[2 * n_vertices - 1]; graphene_vec3_init (&b, poly->x, poly->y, poly->z); poly = &tmp_poly[n_vertices]; graphene_vec3_init (&c, poly->x, poly->y, poly->z); graphene_vec3_subtract (&b, &plane->v0, &b); graphene_vec3_subtract (&c, &plane->v0, &c); graphene_vec3_cross (&b, &c, &plane->n); graphene_vec3_normalize (&plane->n, &plane->n); } /* XXX: Instead of having a toplevel 2D clip region, it might be * better to have a clip volume within the view frustum. This could * allow us to avoid projecting actors into window coordinates to * be able to cull them. */ static void setup_view_for_pick_or_paint (ClutterStage *stage, ClutterStageView *view, const cairo_rectangle_int_t *clip) { ClutterStagePrivate *priv = stage->priv; cairo_rectangle_int_t view_layout; float clip_poly[8]; float viewport[4]; cairo_rectangle_int_t geom; /* Any mode of painting/picking invalidates the pick cache, unless we're * in the middle of building it. So we reset the cached flag but don't * completely clear the pick stack. */ priv->cached_pick_mode = CLUTTER_PICK_NONE; _clutter_stage_window_get_geometry (priv->impl, &geom); viewport[0] = priv->viewport[0]; viewport[1] = priv->viewport[1]; viewport[2] = priv->viewport[2]; viewport[3] = priv->viewport[3]; if (!clip) { clutter_stage_view_get_layout (view, &view_layout); clip = &view_layout; } clip_poly[0] = MAX (clip->x, 0); clip_poly[1] = MAX (clip->y, 0); clip_poly[2] = MIN (clip->x + clip->width, geom.width); clip_poly[3] = clip_poly[1]; clip_poly[4] = clip_poly[2]; clip_poly[5] = MIN (clip->y + clip->height, geom.height); clip_poly[6] = clip_poly[0]; clip_poly[7] = clip_poly[5]; CLUTTER_NOTE (CLIPPING, "Setting stage clip too: " "x=%f, y=%f, width=%f, height=%f", clip_poly[0], clip_poly[1], clip_poly[2] - clip_poly[0], clip_poly[5] - clip_poly[1]); _cogl_util_get_eye_planes_for_screen_poly (clip_poly, 4, viewport, &priv->projection, &priv->inverse_projection, priv->current_clip_planes); _clutter_stage_paint_volume_stack_free_all (stage); } static void clutter_stage_do_paint_view (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip) { ClutterPaintContext *paint_context; cairo_rectangle_int_t clip_rect; paint_context = clutter_paint_context_new_for_view (view, redraw_clip, CLUTTER_PAINT_FLAG_NONE); cairo_region_get_extents (redraw_clip, &clip_rect); setup_view_for_pick_or_paint (stage, view, &clip_rect); clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context); clutter_paint_context_destroy (paint_context); } /* This provides a common point of entry for painting the scenegraph * for picking or painting... */ void clutter_stage_paint_view (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip) { ClutterStagePrivate *priv = stage->priv; if (!priv->impl) return; COGL_TRACE_BEGIN_SCOPED (ClutterStagePaintView, "Paint (view)"); if (g_signal_has_handler_pending (stage, stage_signals[PAINT_VIEW], 0, TRUE)) g_signal_emit (stage, stage_signals[PAINT_VIEW], 0, view, redraw_clip); else CLUTTER_STAGE_GET_CLASS (stage)->paint_view (stage, view, redraw_clip); } void clutter_stage_emit_before_update (ClutterStage *stage) { g_signal_emit (stage, stage_signals[BEFORE_UPDATE], 0); } void clutter_stage_emit_before_paint (ClutterStage *stage) { g_signal_emit (stage, stage_signals[BEFORE_PAINT], 0); } void clutter_stage_emit_after_paint (ClutterStage *stage) { g_signal_emit (stage, stage_signals[AFTER_PAINT], 0); } void clutter_stage_emit_after_update (ClutterStage *stage) { g_signal_emit (stage, stage_signals[AFTER_UPDATE], 0); } /* If we don't implement this here, we get the paint function * from the deprecated clutter-group class, which doesn't * respect the Z order as it uses our empty sort_depth_order. */ static void clutter_stage_paint (ClutterActor *self, ClutterPaintContext *paint_context) { ClutterActorIter iter; ClutterActor *child; clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_paint (child, paint_context); } static void clutter_stage_pick (ClutterActor *self, ClutterPickContext *pick_context) { ClutterActorIter iter; ClutterActor *child; /* Note: we don't chain up to our parent as we don't want any geometry * emitted for the stage itself. The stage's pick id is effectively handled * by the call to cogl_clear done in clutter-main.c:_clutter_do_pick_async() */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_pick (child, pick_context); } static gboolean clutter_stage_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { /* Returning False effectively means Clutter has to assume it covers * everything... */ return FALSE; } static void clutter_stage_realize (ClutterActor *self) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; gboolean is_realized; g_assert (priv->impl != NULL); is_realized = _clutter_stage_window_realize (priv->impl); if (!is_realized) CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED); } static void clutter_stage_unrealize (ClutterActor *self) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; /* and then unrealize the implementation */ g_assert (priv->impl != NULL); _clutter_stage_window_unrealize (priv->impl); CLUTTER_ACTOR_UNSET_FLAGS (self, CLUTTER_ACTOR_REALIZED); } static void clutter_stage_show_all (ClutterActor *self) { ClutterActorIter iter; ClutterActor *child; /* we don't do a recursive show_all(), to maintain the old * invariants from ClutterGroup */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_show (child); clutter_actor_show (self); } static void clutter_stage_show (ClutterActor *self) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->show (self); /* Possibly do an allocation run so that the stage will have the right size before we map it */ _clutter_stage_maybe_relayout (self); g_assert (priv->impl != NULL); _clutter_stage_window_show (priv->impl, TRUE); } static void clutter_stage_hide_all (ClutterActor *self) { ClutterActorIter iter; ClutterActor *child; clutter_actor_hide (self); /* we don't do a recursive hide_all(), to maintain the old invariants * from ClutterGroup */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_hide (child); } static void clutter_stage_hide (ClutterActor *self) { ClutterStagePrivate *priv = CLUTTER_STAGE (self)->priv; g_assert (priv->impl != NULL); _clutter_stage_window_hide (priv->impl); CLUTTER_ACTOR_CLASS (clutter_stage_parent_class)->hide (self); } static void clutter_stage_emit_key_focus_event (ClutterStage *stage, gboolean focus_in) { ClutterStagePrivate *priv = stage->priv; if (priv->key_focused_actor == NULL) return; _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), focus_in); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]); } static void clutter_stage_real_activate (ClutterStage *stage) { clutter_stage_emit_key_focus_event (stage, TRUE); } static void clutter_stage_real_deactivate (ClutterStage *stage) { clutter_stage_emit_key_focus_event (stage, FALSE); } void _clutter_stage_queue_event (ClutterStage *stage, ClutterEvent *event, gboolean copy_event) { ClutterStagePrivate *priv; gboolean first_event; ClutterInputDevice *device; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; first_event = priv->event_queue->length == 0; if (copy_event) event = clutter_event_copy (event); /* if needed, update the state of the input device of the event. * we do it here to avoid calling the same code from every backend * event processing function */ device = clutter_event_get_device (event); if (device != NULL && event->type != CLUTTER_PROXIMITY_IN && event->type != CLUTTER_PROXIMITY_OUT) { ClutterModifierType event_state = clutter_event_get_state (event); ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); guint32 event_time = clutter_event_get_time (event); gfloat event_x, event_y; clutter_event_get_coords (event, &event_x, &event_y); _clutter_input_device_set_coords (device, sequence, event_x, event_y, stage); _clutter_input_device_set_state (device, event_state); _clutter_input_device_set_time (device, event_time); } if (first_event) { gboolean compressible = event->type == CLUTTER_MOTION || event->type == CLUTTER_TOUCH_UPDATE; if (!compressible) { _clutter_process_event (event); clutter_event_free (event); return; } } g_queue_push_tail (priv->event_queue, event); if (first_event) { ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_start_running (master_clock); clutter_stage_schedule_update (stage); } } gboolean _clutter_stage_has_queued_events (ClutterStage *stage) { ClutterStagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); priv = stage->priv; return priv->event_queue->length > 0; } void _clutter_stage_process_queued_events (ClutterStage *stage) { ClutterStagePrivate *priv; GList *events, *l; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (priv->event_queue->length == 0) return; /* In case the stage gets destroyed during event processing */ g_object_ref (stage); /* Steal events before starting processing to avoid reentrancy * issues */ events = priv->event_queue->head; priv->event_queue->head = NULL; priv->event_queue->tail = NULL; priv->event_queue->length = 0; for (l = events; l != NULL; l = l->next) { ClutterEvent *event; ClutterEvent *next_event; ClutterInputDevice *device; ClutterInputDevice *next_device; ClutterInputDeviceType device_type; gboolean check_device = FALSE; event = l->data; next_event = l->next ? l->next->data : NULL; device = clutter_event_get_device (event); if (next_event != NULL) next_device = clutter_event_get_device (next_event); else next_device = NULL; if (device != NULL && next_device != NULL) check_device = TRUE; device_type = clutter_input_device_get_device_type (device); /* Skip consecutive motion events coming from the same device, * except those of tablet tools, since users of these events * want no precision loss. */ if (priv->throttle_motion_events && next_event != NULL && device_type != CLUTTER_TABLET_DEVICE && device_type != CLUTTER_PEN_DEVICE && device_type != CLUTTER_ERASER_DEVICE) { if (event->type == CLUTTER_MOTION && (next_event->type == CLUTTER_MOTION || next_event->type == CLUTTER_LEAVE) && (!check_device || (device == next_device))) { CLUTTER_NOTE (EVENT, "Omitting motion event at %d, %d", (int) event->motion.x, (int) event->motion.y); if (next_event->type == CLUTTER_MOTION) { ClutterSeat *seat = clutter_input_device_get_seat (device); clutter_seat_compress_motion (seat, next_event, event); } goto next_event; } else if (event->type == CLUTTER_TOUCH_UPDATE && next_event->type == CLUTTER_TOUCH_UPDATE && event->touch.sequence == next_event->touch.sequence && (!check_device || (device == next_device))) { CLUTTER_NOTE (EVENT, "Omitting touch update event at %d, %d", (int) event->touch.x, (int) event->touch.y); goto next_event; } } _clutter_process_event (event); next_event: clutter_event_free (event); } g_list_free (events); g_object_unref (stage); } /** * _clutter_stage_needs_update: * @stage: A #ClutterStage * * Determines if _clutter_stage_do_update() needs to be called. * * Return value: %TRUE if the stage need layout or painting */ gboolean _clutter_stage_needs_update (ClutterStage *stage) { ClutterStagePrivate *priv; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); priv = stage->priv; return (priv->redraw_pending || priv->needs_update || g_hash_table_size (priv->pending_relayouts) > 0); } void clutter_stage_queue_actor_relayout (ClutterStage *stage, ClutterActor *actor) { ClutterStagePrivate *priv = stage->priv; if (g_hash_table_size (priv->pending_relayouts) == 0) clutter_stage_schedule_update (stage); g_hash_table_add (priv->pending_relayouts, g_object_ref (actor)); priv->pending_relayouts_version++; } void _clutter_stage_maybe_relayout (ClutterActor *actor) { ClutterStage *stage = CLUTTER_STAGE (actor); ClutterStagePrivate *priv = stage->priv; GHashTableIter iter; gpointer key; int count = 0; /* No work to do? Avoid the extraneous debug log messages too. */ if (g_hash_table_size (priv->pending_relayouts) == 0) return; CLUTTER_NOTE (ACTOR, ">>> Recomputing layout"); g_hash_table_iter_init (&iter, priv->pending_relayouts); while (g_hash_table_iter_next (&iter, &key, NULL)) { g_autoptr (ClutterActor) queued_actor = key; unsigned int old_version; g_hash_table_iter_steal (&iter); priv->pending_relayouts_version++; if (CLUTTER_ACTOR_IN_RELAYOUT (queued_actor)) /* avoid reentrancy */ continue; /* An actor may have been destroyed or hidden between queuing and now */ if (clutter_actor_get_stage (queued_actor) != actor) continue; if (queued_actor == actor) CLUTTER_NOTE (ACTOR, " Deep relayout of stage %s", _clutter_actor_get_debug_name (queued_actor)); else CLUTTER_NOTE (ACTOR, " Shallow relayout of actor %s", _clutter_actor_get_debug_name (queued_actor)); CLUTTER_SET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT); old_version = priv->pending_relayouts_version; clutter_actor_allocate_preferred_size (queued_actor, CLUTTER_ALLOCATION_NONE); CLUTTER_UNSET_PRIVATE_FLAGS (queued_actor, CLUTTER_IN_RELAYOUT); count++; /* Prevent using an iterator that's been invalidated */ if (old_version != priv->pending_relayouts_version) g_hash_table_iter_init (&iter, priv->pending_relayouts); } CLUTTER_NOTE (ACTOR, "<<< Completed recomputing layout of %d subtrees", count); if (count) priv->stage_was_relayout = TRUE; } static void clutter_stage_do_redraw (ClutterStage *stage) { ClutterActor *actor = CLUTTER_ACTOR (stage); ClutterStagePrivate *priv = stage->priv; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return; if (priv->impl == NULL) return; CLUTTER_NOTE (PAINT, "Redraw started for stage '%s'[%p]", _clutter_actor_get_debug_name (actor), stage); if (_clutter_context_get_show_fps ()) { if (priv->fps_timer == NULL) priv->fps_timer = g_timer_new (); } _clutter_stage_window_redraw (priv->impl); if (_clutter_context_get_show_fps ()) { priv->timer_n_frames += 1; if (g_timer_elapsed (priv->fps_timer, NULL) >= 1.0) { g_print ("*** FPS for %s: %i ***\n", _clutter_actor_get_debug_name (actor), priv->timer_n_frames); priv->timer_n_frames = 0; g_timer_start (priv->fps_timer); } } CLUTTER_NOTE (PAINT, "Redraw finished for stage '%s'[%p]", _clutter_actor_get_debug_name (actor), stage); } static GSList * _clutter_stage_check_updated_pointers (ClutterStage *stage) { ClutterBackend *backend; ClutterSeat *seat; GSList *updating = NULL; GList *l, *devices; graphene_point_t point; backend = clutter_get_default_backend (); seat = clutter_backend_get_default_seat (backend); devices = clutter_seat_list_devices (seat); for (l = devices; l; l = l->next) { ClutterInputDevice *dev = l->data; ClutterStageView *view; const cairo_region_t *clip; if (clutter_input_device_get_device_mode (dev) != CLUTTER_INPUT_MODE_MASTER) continue; switch (clutter_input_device_get_device_type (dev)) { case CLUTTER_POINTER_DEVICE: case CLUTTER_TABLET_DEVICE: case CLUTTER_PEN_DEVICE: case CLUTTER_ERASER_DEVICE: case CLUTTER_CURSOR_DEVICE: if (!clutter_input_device_get_coords (dev, NULL, &point)) continue; view = clutter_stage_get_view_at (stage, point.x, point.y); if (!view) continue; clip = clutter_stage_view_peek_redraw_clip (view); if (!clip || cairo_region_contains_point (clip, point.x, point.y)) updating = g_slist_prepend (updating, dev); break; default: /* Any other devices don't need checking, either because they * don't have x/y coordinates, or because they're implicitly * grabbed on an actor by default as it's the case of * touch(screens). */ break; } } g_list_free (devices); return updating; } /** * _clutter_stage_do_update: * @stage: A #ClutterStage * * Handles per-frame layout and repaint for the stage. * * Return value: %TRUE if the stage was updated */ gboolean _clutter_stage_do_update (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; gboolean stage_was_relayout = priv->stage_was_relayout; GSList *pointers = NULL; priv->stage_was_relayout = FALSE; priv->needs_update = FALSE; /* if the stage is being destroyed, or if the destruction already * happened and we don't have an StageWindow any more, then we * should bail out */ if (CLUTTER_ACTOR_IN_DESTRUCTION (stage) || priv->impl == NULL) return FALSE; if (!CLUTTER_ACTOR_IS_REALIZED (stage)) return FALSE; COGL_TRACE_BEGIN_SCOPED (ClutterStageDoUpdate, "Update"); clutter_stage_emit_before_update (stage); /* NB: We need to ensure we have an up to date layout *before* we * check or clear the pending redraws flag since a relayout may * queue a redraw. */ COGL_TRACE_BEGIN (ClutterStageRelayout, "Layout"); _clutter_stage_maybe_relayout (CLUTTER_ACTOR (stage)); COGL_TRACE_END (ClutterStageRelayout); if (!priv->redraw_pending) { clutter_stage_emit_after_update (stage); return FALSE; } if (stage_was_relayout) pointers = _clutter_stage_check_updated_pointers (stage); COGL_TRACE_BEGIN (ClutterStagePaint, "Paint"); clutter_stage_maybe_finish_queue_redraws (stage); clutter_stage_do_redraw (stage); COGL_TRACE_END (ClutterStagePaint); /* reset the guard, so that new redraws are possible */ priv->redraw_pending = FALSE; #ifdef CLUTTER_ENABLE_DEBUG if (priv->redraw_count > 0) { CLUTTER_NOTE (SCHEDULER, "Queued %lu redraws during the last cycle", priv->redraw_count); priv->redraw_count = 0; } #endif /* CLUTTER_ENABLE_DEBUG */ COGL_TRACE_BEGIN (ClutterStagePick, "Pick"); while (pointers) { clutter_input_device_update (pointers->data, NULL, TRUE); pointers = g_slist_delete_link (pointers, pointers); } COGL_TRACE_END (ClutterStagePick); clutter_stage_emit_after_update (stage); return TRUE; } static void clutter_stage_real_queue_relayout (ClutterActor *self) { ClutterStage *stage = CLUTTER_STAGE (self); ClutterActorClass *parent_class; clutter_stage_queue_actor_relayout (stage, self); /* chain up */ parent_class = CLUTTER_ACTOR_CLASS (clutter_stage_parent_class); parent_class->queue_relayout (self); } static gboolean is_full_stage_redraw_queued (ClutterStage *stage) { GList *l; for (l = _clutter_stage_peek_stage_views (stage); l; l = l->next) { ClutterStageView *view = l->data; if (!clutter_stage_view_has_full_redraw_clip (view)) return FALSE; } return TRUE; } static gboolean clutter_stage_real_queue_redraw (ClutterActor *actor, ClutterActor *leaf, ClutterPaintVolume *redraw_clip) { ClutterStage *stage = CLUTTER_STAGE (actor); ClutterStageWindow *stage_window; ClutterActorBox bounding_box; ClutterActorBox intersection_box; cairo_rectangle_int_t geom, stage_clip; if (CLUTTER_ACTOR_IN_DESTRUCTION (actor)) return TRUE; /* If the backend can't do anything with redraw clips (e.g. it already knows * it needs to redraw everything anyway) then don't spend time transforming * any clip volume into stage coordinates... */ stage_window = _clutter_stage_get_window (stage); if (stage_window == NULL) return TRUE; if (is_full_stage_redraw_queued (stage)) return FALSE; if (redraw_clip == NULL) { clutter_stage_add_redraw_clip (stage, NULL); return FALSE; } if (redraw_clip->is_empty) return TRUE; /* Convert the clip volume into stage coordinates and then into an * axis aligned stage coordinates bounding box... */ _clutter_paint_volume_get_stage_paint_box (redraw_clip, stage, &bounding_box); _clutter_stage_window_get_geometry (stage_window, &geom); intersection_box.x1 = MAX (bounding_box.x1, 0); intersection_box.y1 = MAX (bounding_box.y1, 0); intersection_box.x2 = MIN (bounding_box.x2, geom.width); intersection_box.y2 = MIN (bounding_box.y2, geom.height); /* There is no need to track degenerate/empty redraw clips */ if (intersection_box.x2 <= intersection_box.x1 || intersection_box.y2 <= intersection_box.y1) return TRUE; /* when converting to integer coordinates make sure we round the edges of the * clip rectangle outwards... */ stage_clip.x = intersection_box.x1; stage_clip.y = intersection_box.y1; stage_clip.width = intersection_box.x2 - stage_clip.x; stage_clip.height = intersection_box.y2 - stage_clip.y; clutter_stage_add_redraw_clip (stage, &stage_clip); return FALSE; } gboolean _clutter_stage_has_full_redraw_queued (ClutterStage *stage) { if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return FALSE; if (!stage->priv->redraw_pending) return FALSE; return is_full_stage_redraw_queued (stage); } static ClutterActor * _clutter_stage_do_pick_on_view (ClutterStage *stage, float x, float y, ClutterPickMode mode, ClutterStageView *view) { ClutterMainContext *context = _clutter_context_get_default (); ClutterStagePrivate *priv = stage->priv; int i; g_assert (context->pick_mode == CLUTTER_PICK_NONE); if (mode != priv->cached_pick_mode) { ClutterPickContext *pick_context; _clutter_stage_clear_pick_stack (stage); pick_context = clutter_pick_context_new_for_view (view); context->pick_mode = mode; setup_view_for_pick_or_paint (stage, view, NULL); clutter_actor_pick (CLUTTER_ACTOR (stage), pick_context); context->pick_mode = CLUTTER_PICK_NONE; priv->cached_pick_mode = mode; clutter_pick_context_destroy (pick_context); add_pick_stack_weak_refs (stage); } /* Search all "painted" pickable actors from front to back. A linear search * is required, and also performs fine since there is typically only * on the order of dozens of actors in the list (on screen) at a time. */ for (i = priv->pick_stack->len - 1; i >= 0; i--) { const PickRecord *rec = &g_array_index (priv->pick_stack, PickRecord, i); if (rec->actor && pick_record_contains_point (stage, rec, x, y)) return rec->actor; } return CLUTTER_ACTOR (stage); } /** * clutter_stage_get_view_at: (skip) */ ClutterStageView * clutter_stage_get_view_at (ClutterStage *stage, float x, float y) { ClutterStagePrivate *priv = stage->priv; GList *l; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) { ClutterStageView *view = l->data; cairo_rectangle_int_t view_layout; clutter_stage_view_get_layout (view, &view_layout); if (x >= view_layout.x && x < view_layout.x + view_layout.width && y >= view_layout.y && y < view_layout.y + view_layout.height) return view; } return NULL; } ClutterActor * _clutter_stage_do_pick (ClutterStage *stage, float x, float y, ClutterPickMode mode) { ClutterActor *actor = CLUTTER_ACTOR (stage); ClutterStagePrivate *priv = stage->priv; float stage_width, stage_height; ClutterStageView *view = NULL; priv = stage->priv; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return actor; if (G_UNLIKELY (clutter_pick_debug_flags & CLUTTER_DEBUG_NOP_PICKING)) return actor; if (G_UNLIKELY (priv->impl == NULL)) return actor; clutter_actor_get_size (CLUTTER_ACTOR (stage), &stage_width, &stage_height); if (x < 0 || x >= stage_width || y < 0 || y >= stage_height) return actor; view = clutter_stage_get_view_at (stage, x, y); if (view) return _clutter_stage_do_pick_on_view (stage, x, y, mode, view); return actor; } static gboolean clutter_stage_real_delete_event (ClutterStage *stage, ClutterEvent *event) { if (stage_is_default (stage)) clutter_main_quit (); else clutter_actor_destroy (CLUTTER_ACTOR (stage)); return CLUTTER_EVENT_STOP; } static void clutter_stage_real_apply_transform (ClutterActor *stage, CoglMatrix *matrix) { ClutterStagePrivate *priv = CLUTTER_STAGE (stage)->priv; /* FIXME: we probably shouldn't be explicitly reseting the matrix * here... */ cogl_matrix_init_identity (matrix); cogl_matrix_multiply (matrix, matrix, &priv->view); } static void clutter_stage_constructed (GObject *gobject) { ClutterStage *self = CLUTTER_STAGE (gobject); ClutterStageManager *stage_manager; stage_manager = clutter_stage_manager_get_default (); /* this will take care to sinking the floating reference */ _clutter_stage_manager_add_stage (stage_manager, self); /* if this stage has been created on a backend that does not * support multiple stages then it becomes the default stage * as well; any other attempt at creating a ClutterStage will * fail. */ if (!clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE)) { if (G_UNLIKELY (clutter_stage_manager_get_default_stage (stage_manager) != NULL)) { g_error ("Unable to create another stage: the backend of " "type '%s' does not support multiple stages. Use " "clutter_stage_manager_get_default_stage() instead " "to access the stage singleton.", G_OBJECT_TYPE_NAME (clutter_get_default_backend ())); } _clutter_stage_manager_set_default_stage (stage_manager, self); } G_OBJECT_CLASS (clutter_stage_parent_class)->constructed (gobject); } static void clutter_stage_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterStage *stage = CLUTTER_STAGE (object); switch (prop_id) { case PROP_COLOR: clutter_actor_set_background_color (CLUTTER_ACTOR (stage), clutter_value_get_color (value)); break; case PROP_CURSOR_VISIBLE: if (g_value_get_boolean (value)) clutter_stage_show_cursor (stage); else clutter_stage_hide_cursor (stage); break; case PROP_PERSPECTIVE: clutter_stage_set_perspective (stage, g_value_get_boxed (value)); break; case PROP_TITLE: clutter_stage_set_title (stage, g_value_get_string (value)); break; case PROP_USE_ALPHA: clutter_stage_set_use_alpha (stage, g_value_get_boolean (value)); break; case PROP_KEY_FOCUS: clutter_stage_set_key_focus (stage, g_value_get_object (value)); break; case PROP_ACCEPT_FOCUS: clutter_stage_set_accept_focus (stage, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_stage_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterStagePrivate *priv = CLUTTER_STAGE (gobject)->priv; switch (prop_id) { case PROP_COLOR: { ClutterColor bg_color; clutter_actor_get_background_color (CLUTTER_ACTOR (gobject), &bg_color); clutter_value_set_color (value, &bg_color); } break; case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->is_cursor_visible); break; case PROP_PERSPECTIVE: g_value_set_boxed (value, &priv->perspective); break; case PROP_TITLE: g_value_set_string (value, priv->title); break; case PROP_USE_ALPHA: g_value_set_boolean (value, priv->use_alpha); break; case PROP_KEY_FOCUS: g_value_set_object (value, priv->key_focused_actor); break; case PROP_ACCEPT_FOCUS: g_value_set_boolean (value, priv->accept_focus); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_stage_dispose (GObject *object) { ClutterStage *stage = CLUTTER_STAGE (object); ClutterStagePrivate *priv = stage->priv; ClutterStageManager *stage_manager; clutter_actor_hide (CLUTTER_ACTOR (object)); _clutter_clear_events_queue_for_stage (stage); if (priv->impl != NULL) { CLUTTER_NOTE (BACKEND, "Disposing of the stage implementation"); if (CLUTTER_ACTOR_IS_REALIZED (object)) _clutter_stage_window_unrealize (priv->impl); g_object_unref (priv->impl); priv->impl = NULL; } clutter_actor_destroy_all_children (CLUTTER_ACTOR (object)); g_list_free_full (priv->pending_queue_redraws, (GDestroyNotify) free_queue_redraw_entry); priv->pending_queue_redraws = NULL; g_clear_pointer (&priv->pending_relayouts, g_hash_table_destroy); /* this will release the reference on the stage */ stage_manager = clutter_stage_manager_get_default (); _clutter_stage_manager_remove_stage (stage_manager, stage); G_OBJECT_CLASS (clutter_stage_parent_class)->dispose (object); } static void clutter_stage_finalize (GObject *object) { ClutterStage *stage = CLUTTER_STAGE (object); ClutterStagePrivate *priv = stage->priv; g_queue_foreach (priv->event_queue, (GFunc) clutter_event_free, NULL); g_queue_free (priv->event_queue); g_hash_table_destroy (priv->pointer_devices); g_hash_table_destroy (priv->touch_sequences); g_free (priv->title); g_array_free (priv->paint_volume_stack, TRUE); _clutter_stage_clear_pick_stack (stage); g_array_free (priv->pick_clip_stack, TRUE); g_array_free (priv->pick_stack, TRUE); if (priv->fps_timer != NULL) g_timer_destroy (priv->fps_timer); if (priv->paint_notify != NULL) priv->paint_notify (priv->paint_data); G_OBJECT_CLASS (clutter_stage_parent_class)->finalize (object); } static void clutter_stage_real_paint_view (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip) { clutter_stage_do_paint_view (stage, view, redraw_clip); } static void clutter_stage_class_init (ClutterStageClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->constructed = clutter_stage_constructed; gobject_class->set_property = clutter_stage_set_property; gobject_class->get_property = clutter_stage_get_property; gobject_class->dispose = clutter_stage_dispose; gobject_class->finalize = clutter_stage_finalize; actor_class->allocate = clutter_stage_allocate; actor_class->get_preferred_width = clutter_stage_get_preferred_width; actor_class->get_preferred_height = clutter_stage_get_preferred_height; actor_class->paint = clutter_stage_paint; actor_class->pick = clutter_stage_pick; actor_class->get_paint_volume = clutter_stage_get_paint_volume; actor_class->realize = clutter_stage_realize; actor_class->unrealize = clutter_stage_unrealize; actor_class->show = clutter_stage_show; actor_class->show_all = clutter_stage_show_all; actor_class->hide = clutter_stage_hide; actor_class->hide_all = clutter_stage_hide_all; actor_class->queue_relayout = clutter_stage_real_queue_relayout; actor_class->queue_redraw = clutter_stage_real_queue_redraw; actor_class->apply_transform = clutter_stage_real_apply_transform; klass->paint_view = clutter_stage_real_paint_view; /** * ClutterStage:cursor-visible: * * Whether the mouse pointer should be visible */ obj_props[PROP_CURSOR_VISIBLE] = g_param_spec_boolean ("cursor-visible", P_("Cursor Visible"), P_("Whether the mouse pointer is visible on the main stage"), TRUE, CLUTTER_PARAM_READWRITE); /** * ClutterStage:color: * * The background color of the main stage. * * Deprecated: 1.10: Use the #ClutterActor:background-color property of * #ClutterActor instead. */ obj_props[PROP_COLOR] = clutter_param_spec_color ("color", P_("Color"), P_("The color of the stage"), &default_stage_color, CLUTTER_PARAM_READWRITE | G_PARAM_DEPRECATED); /** * ClutterStage:perspective: * * The parameters used for the perspective projection from 3D * coordinates to 2D * * Since: 0.8 */ obj_props[PROP_PERSPECTIVE] = g_param_spec_boxed ("perspective", P_("Perspective"), P_("Perspective projection parameters"), CLUTTER_TYPE_PERSPECTIVE, CLUTTER_PARAM_READWRITE); /** * ClutterStage:title: * * The stage's title - usually displayed in stage windows title decorations. * * Since: 0.4 */ obj_props[PROP_TITLE] = g_param_spec_string ("title", P_("Title"), P_("Stage Title"), NULL, CLUTTER_PARAM_READWRITE); /** * ClutterStage:use-alpha: * * Whether the #ClutterStage should honour the alpha component of the * #ClutterStage:color property when painting. If Clutter is run under * a compositing manager this will result in the stage being blended * with the underlying window(s) * * Since: 1.2 */ obj_props[PROP_USE_ALPHA] = g_param_spec_boolean ("use-alpha", P_("Use Alpha"), P_("Whether to honour the alpha component of the stage color"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterStage:key-focus: * * The #ClutterActor that will receive key events from the underlying * windowing system. * * If %NULL, the #ClutterStage will receive the events. * * Since: 1.2 */ obj_props[PROP_KEY_FOCUS] = g_param_spec_object ("key-focus", P_("Key Focus"), P_("The currently key focused actor"), CLUTTER_TYPE_ACTOR, CLUTTER_PARAM_READWRITE); /** * ClutterStage:accept-focus: * * Whether the #ClutterStage should accept key focus when shown. * * Since: 1.6 */ obj_props[PROP_ACCEPT_FOCUS] = g_param_spec_boolean ("accept-focus", P_("Accept Focus"), P_("Whether the stage should accept focus on show"), TRUE, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); /** * ClutterStage::activate: * @stage: the stage which was activated * * The ::activate signal is emitted when the stage receives key focus * from the underlying window system. * * Since: 0.6 */ stage_signals[ACTIVATE] = g_signal_new (I_("activate"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageClass, activate), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::deactivate: * @stage: the stage which was deactivated * * The ::deactivate signal is emitted when the stage loses key focus * from the underlying window system. * * Since: 0.6 */ stage_signals[DEACTIVATE] = g_signal_new (I_("deactivate"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageClass, deactivate), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::delete-event: * @stage: the stage that received the event * @event: a #ClutterEvent of type %CLUTTER_DELETE * * The ::delete-event signal is emitted when the user closes a * #ClutterStage window using the window controls. * * Clutter by default will call clutter_main_quit() if @stage is * the default stage, and clutter_actor_destroy() for any other * stage. * * It is possible to override the default behaviour by connecting * a new handler and returning %TRUE there. * * This signal is emitted only on Clutter backends that * embed #ClutterStage in native windows. It is not emitted for * backends that use a static frame buffer. * * Since: 1.2 */ stage_signals[DELETE_EVENT] = g_signal_new (I_("delete-event"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageClass, delete_event), _clutter_boolean_handled_accumulator, NULL, _clutter_marshal_BOOLEAN__BOXED, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_EVENT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * ClutterStage::before-update: * @stage: the #ClutterStage */ stage_signals[BEFORE_UPDATE] = g_signal_new (I_("before-update"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::before-paint: * @stage: the stage that received the event * * The ::before-paint signal is emitted before the stage is painted. */ stage_signals[BEFORE_PAINT] = g_signal_new (I_("before-paint"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::after-paint: * @stage: the stage that received the event * @paint_Context: the paint context * * The ::after-paint signal is emitted after the stage is painted, * but before the results are displayed on the screen. * * Since: 1.20 */ stage_signals[AFTER_PAINT] = g_signal_new (I_("after-paint"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, /* no corresponding vfunc */ NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::after-update: * @stage: the #ClutterStage */ stage_signals[AFTER_UPDATE] = g_signal_new (I_("after-update"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterStage::paint-view: * @stage: the stage that received the event * @view: a #ClutterStageView * @redraw_clip: a #cairo_region_t with the redraw clip * * The ::paint-view signal is emitted before a #ClutterStageView is being * painted. * * The view is painted in the default handler. Hence, if you want to perform * some action after the view is painted, like reading the contents of the * framebuffer, use g_signal_connect_after() or pass %G_CONNECT_AFTER. */ stage_signals[PAINT_VIEW] = g_signal_new (I_("paint-view"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterStageClass, paint_view), NULL, NULL, NULL, G_TYPE_NONE, 2, CLUTTER_TYPE_STAGE_VIEW, G_TYPE_POINTER); /** * ClutterStage::presented: (skip) * @stage: the stage that received the event * @frame_event: a #CoglFrameEvent * @frame_info: a #ClutterFrameInfo * * Signals that the #ClutterStage was presented on the screen to the user. */ stage_signals[PRESENTED] = g_signal_new (I_("presented"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, _clutter_marshal_VOID__INT_POINTER, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_POINTER); klass->activate = clutter_stage_real_activate; klass->deactivate = clutter_stage_real_deactivate; klass->delete_event = clutter_stage_real_delete_event; } static void clutter_stage_notify_min_size (ClutterStage *self) { self->priv->min_size_changed = TRUE; } static void clutter_stage_init (ClutterStage *self) { cairo_rectangle_int_t geom = { 0, }; ClutterStagePrivate *priv; ClutterStageWindow *impl; ClutterBackend *backend; GError *error; /* a stage is a top-level object */ CLUTTER_SET_PRIVATE_FLAGS (self, CLUTTER_IS_TOPLEVEL); self->priv = priv = clutter_stage_get_instance_private (self); CLUTTER_NOTE (BACKEND, "Creating stage from the default backend"); backend = clutter_get_default_backend (); error = NULL; impl = _clutter_backend_create_stage (backend, self, &error); if (G_LIKELY (impl != NULL)) { _clutter_stage_set_window (self, impl); _clutter_stage_window_get_geometry (priv->impl, &geom); } else { if (error != NULL) { g_critical ("Unable to create a new stage implementation: %s", error->message); g_error_free (error); } else g_critical ("Unable to create a new stage implementation."); } priv->event_queue = g_queue_new (); priv->is_cursor_visible = TRUE; priv->throttle_motion_events = TRUE; priv->min_size_changed = FALSE; priv->sync_delay = -1; priv->motion_events_enabled = TRUE; priv->pointer_devices = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) free_pointer_device_entry); priv->touch_sequences = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) free_pointer_device_entry); clutter_actor_set_background_color (CLUTTER_ACTOR (self), &default_stage_color); priv->pending_relayouts = g_hash_table_new_full (NULL, NULL, g_object_unref, NULL); clutter_stage_queue_actor_relayout (self, CLUTTER_ACTOR (self)); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); clutter_stage_set_title (self, g_get_prgname ()); clutter_stage_set_key_focus (self, NULL); g_signal_connect (self, "notify::min-width", G_CALLBACK (clutter_stage_notify_min_size), NULL); g_signal_connect (self, "notify::min-height", G_CALLBACK (clutter_stage_notify_min_size), NULL); _clutter_stage_set_viewport (self, 0, 0, geom.width, geom.height); priv->paint_volume_stack = g_array_new (FALSE, FALSE, sizeof (ClutterPaintVolume)); priv->pick_stack = g_array_new (FALSE, FALSE, sizeof (PickRecord)); priv->pick_clip_stack = g_array_new (FALSE, FALSE, sizeof (PickClipRecord)); priv->pick_clip_stack_top = -1; priv->cached_pick_mode = CLUTTER_PICK_NONE; } /** * clutter_stage_get_default: * * Retrieves a #ClutterStage singleton. * * This function is not as useful as it sounds, and will most likely * by deprecated in the future. Application code should only create * a #ClutterStage instance using clutter_stage_new(), and manage the * lifetime of the stage manually. * * The default stage singleton has a platform-specific behaviour: on * platforms without the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag * set, the first #ClutterStage instance will also be set to be the * default stage instance, and this function will always return a * pointer to it. * * On platforms with the %CLUTTER_FEATURE_STAGE_MULTIPLE feature flag * set, the default stage will be created by the first call to this * function, and every following call will return the same pointer to * it. * * Return value: (transfer none) (type Clutter.Stage): the main * #ClutterStage. You should never destroy or unref the returned * actor. * * Deprecated: 1.10: Use clutter_stage_new() instead. */ ClutterActor * clutter_stage_get_default (void) { ClutterStageManager *stage_manager = clutter_stage_manager_get_default (); ClutterStage *stage; stage = clutter_stage_manager_get_default_stage (stage_manager); if (G_UNLIKELY (stage == NULL)) { /* This will take care of automatically adding the stage to the * stage manager and setting it as the default. Its floating * reference will be claimed by the stage manager. */ stage = g_object_new (CLUTTER_TYPE_STAGE, NULL); _clutter_stage_manager_set_default_stage (stage_manager, stage); /* the default stage is realized by default */ clutter_actor_realize (CLUTTER_ACTOR (stage)); } return CLUTTER_ACTOR (stage); } /** * clutter_stage_set_color: * @stage: A #ClutterStage * @color: A #ClutterColor * * Sets the stage color. * * Deprecated: 1.10: Use clutter_actor_set_background_color() instead. */ void clutter_stage_set_color (ClutterStage *stage, const ClutterColor *color) { clutter_actor_set_background_color (CLUTTER_ACTOR (stage), color); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_COLOR]); } /** * clutter_stage_get_color: * @stage: A #ClutterStage * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the stage color. * * Deprecated: 1.10: Use clutter_actor_get_background_color() instead. */ void clutter_stage_get_color (ClutterStage *stage, ClutterColor *color) { clutter_actor_get_background_color (CLUTTER_ACTOR (stage), color); } static void clutter_stage_set_perspective_internal (ClutterStage *stage, ClutterPerspective *perspective) { ClutterStagePrivate *priv = stage->priv; if (priv->perspective.fovy == perspective->fovy && priv->perspective.aspect == perspective->aspect && priv->perspective.z_near == perspective->z_near && priv->perspective.z_far == perspective->z_far) return; priv->perspective = *perspective; cogl_matrix_init_identity (&priv->projection); cogl_matrix_perspective (&priv->projection, priv->perspective.fovy, priv->perspective.aspect, priv->perspective.z_near, priv->perspective.z_far); cogl_matrix_get_inverse (&priv->projection, &priv->inverse_projection); _clutter_stage_dirty_projection (stage); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } /** * clutter_stage_set_perspective: * @stage: A #ClutterStage * @perspective: A #ClutterPerspective * * Sets the stage perspective. Using this function is not recommended * because it will disable Clutter's attempts to generate an * appropriate perspective based on the size of the stage. */ void clutter_stage_set_perspective (ClutterStage *stage, ClutterPerspective *perspective) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (perspective != NULL); g_return_if_fail (perspective->z_far - perspective->z_near != 0); priv = stage->priv; /* If the application ever calls this function then we'll stop automatically updating the perspective when the stage changes size */ priv->has_custom_perspective = TRUE; clutter_stage_set_perspective_internal (stage, perspective); clutter_stage_update_view_perspective (stage); } /** * clutter_stage_get_perspective: * @stage: A #ClutterStage * @perspective: (out caller-allocates) (allow-none): return location for a * #ClutterPerspective * * Retrieves the stage perspective. */ void clutter_stage_get_perspective (ClutterStage *stage, ClutterPerspective *perspective) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (perspective != NULL); *perspective = stage->priv->perspective; } /* * clutter_stage_get_projection_matrix: * @stage: A #ClutterStage * @projection: return location for a #CoglMatrix representing the * perspective projection applied to actors on the given * @stage. * * Retrieves the @stage's projection matrix. This is derived from the * current perspective set using clutter_stage_set_perspective(). * * Since: 1.6 */ void _clutter_stage_get_projection_matrix (ClutterStage *stage, CoglMatrix *projection) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (projection != NULL); *projection = stage->priv->projection; } /* This simply provides a simple mechanism for us to ensure that * the projection matrix gets re-asserted before painting. * * This is used when switching between multiple stages */ void _clutter_stage_dirty_projection (ClutterStage *stage) { ClutterStagePrivate *priv; GList *l; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) { ClutterStageView *view = l->data; clutter_stage_view_set_dirty_projection (view, TRUE); } } /* * clutter_stage_set_viewport: * @stage: A #ClutterStage * @x: The X postition to render the stage at, in window coordinates * @y: The Y position to render the stage at, in window coordinates * @width: The width to render the stage at, in window coordinates * @height: The height to render the stage at, in window coordinates * * Sets the stage viewport. The viewport defines a final scale and * translation of your rendered stage and actors. This lets you render * your stage into a subregion of the stage window or you could use it to * pan a subregion of the stage if your stage window is smaller then * the stage. (XXX: currently this isn't possible) * * Unlike a scale and translation done using the modelview matrix this * is done after everything has had perspective projection applied, so * for example if you were to pan across a subregion of the stage using * the viewport then you would not see a change in perspective for the * actors on the stage. * * Normally the stage viewport will automatically track the size of the * stage window with no offset so the stage will fill your window. This * behaviour can be changed with the "viewport-mimics-window" property * which will automatically be set to FALSE if you use this API. If * you want to revert to the original behaviour then you should set * this property back to %TRUE using * clutter_stage_set_viewport_mimics_window(). * (XXX: If we were to make this API public then we might want to do * add that property.) * * Note: currently this interface only support integer precision * offsets and sizes for viewports but the interface takes floats because * OpenGL 4.0 has introduced floating point viewports which we might * want to expose via this API eventually. * * Since: 1.6 */ void _clutter_stage_set_viewport (ClutterStage *stage, float x, float y, float width, float height) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (x == priv->viewport[0] && y == priv->viewport[1] && width == priv->viewport[2] && height == priv->viewport[3]) return; priv->viewport[0] = x; priv->viewport[1] = y; priv->viewport[2] = width; priv->viewport[3] = height; clutter_stage_update_view_perspective (stage); _clutter_stage_dirty_viewport (stage); queue_full_redraw (stage); } /* This simply provides a simple mechanism for us to ensure that * the viewport gets re-asserted before next painting. * * This is used when switching between multiple stages */ void _clutter_stage_dirty_viewport (ClutterStage *stage) { ClutterStagePrivate *priv; GList *l; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) { ClutterStageView *view = l->data; clutter_stage_view_set_dirty_viewport (view, TRUE); } } /* * clutter_stage_get_viewport: * @stage: A #ClutterStage * @x: A location for the X position where the stage is rendered, * in window coordinates. * @y: A location for the Y position where the stage is rendered, * in window coordinates. * @width: A location for the width the stage is rendered at, * in window coordinates. * @height: A location for the height the stage is rendered at, * in window coordinates. * * Returns the viewport offset and size set using * clutter_stage_set_viewport() or if the "viewport-mimics-window" property * is TRUE then @x and @y will be set to 0 and @width and @height will equal * the width if the stage window. * * Since: 1.6 */ void _clutter_stage_get_viewport (ClutterStage *stage, float *x, float *y, float *width, float *height) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; *x = priv->viewport[0]; *y = priv->viewport[1]; *width = priv->viewport[2]; *height = priv->viewport[3]; } /** * clutter_stage_show_cursor: * @stage: a #ClutterStage * * Shows the cursor on the stage window */ void clutter_stage_show_cursor (ClutterStage *stage) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (!priv->is_cursor_visible) { ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl); ClutterStageWindowInterface *iface; iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl); if (iface->set_cursor_visible) { priv->is_cursor_visible = TRUE; iface->set_cursor_visible (impl, TRUE); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_CURSOR_VISIBLE]); } } } /** * clutter_stage_hide_cursor: * @stage: a #ClutterStage * * Makes the cursor invisible on the stage window * * Since: 0.4 */ void clutter_stage_hide_cursor (ClutterStage *stage) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (priv->is_cursor_visible) { ClutterStageWindow *impl = CLUTTER_STAGE_WINDOW (priv->impl); ClutterStageWindowInterface *iface; iface = CLUTTER_STAGE_WINDOW_GET_IFACE (impl); if (iface->set_cursor_visible) { priv->is_cursor_visible = FALSE; iface->set_cursor_visible (impl, FALSE); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_CURSOR_VISIBLE]); } } } /** * clutter_stage_read_pixels: * @stage: A #ClutterStage * @x: x coordinate of the first pixel that is read from stage * @y: y coordinate of the first pixel that is read from stage * @width: Width dimention of pixels to be read, or -1 for the * entire stage width * @height: Height dimention of pixels to be read, or -1 for the * entire stage height * * Makes a screenshot of the stage in RGBA 8bit data, returns a * linear buffer with @width * 4 as rowstride. * * The alpha data contained in the returned buffer is driver-dependent, * and not guaranteed to hold any sensible value. * * Return value: (transfer full) (array): a pointer to newly allocated memory with the buffer * or %NULL if the read failed. Use g_free() on the returned data * to release the resources it has allocated. */ guchar * clutter_stage_read_pixels (ClutterStage *stage, gint x, gint y, gint width, gint height) { ClutterStagePrivate *priv; ClutterActorBox box; GList *l; ClutterStageView *view; cairo_region_t *clip; cairo_rectangle_int_t clip_rect; CoglFramebuffer *framebuffer; float view_scale; float pixel_width; float pixel_height; uint8_t *pixels; COGL_TRACE_BEGIN_SCOPED (ClutterStageReadPixels, "Read Pixels"); g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); priv = stage->priv; clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &box); if (width < 0) width = ceilf (box.x2 - box.x1); if (height < 0) height = ceilf (box.y2 - box.y1); l = _clutter_stage_window_get_views (priv->impl); if (!l) return NULL; /* XXX: We only read the first view. Needs different API for multi view screen * capture. */ view = l->data; clutter_stage_view_get_layout (view, &clip_rect); clip = cairo_region_create_rectangle (&clip_rect); cairo_region_intersect_rectangle (clip, &(cairo_rectangle_int_t) { .x = x, .y = y, .width = width, .height = height, }); cairo_region_get_extents (clip, &clip_rect); if (clip_rect.width == 0 || clip_rect.height == 0) { cairo_region_destroy (clip); return NULL; } framebuffer = clutter_stage_view_get_framebuffer (view); clutter_stage_do_paint_view (stage, view, clip); cairo_region_destroy (clip); view_scale = clutter_stage_view_get_scale (view); pixel_width = roundf (clip_rect.width * view_scale); pixel_height = roundf (clip_rect.height * view_scale); pixels = g_malloc0 (pixel_width * pixel_height * 4); cogl_framebuffer_read_pixels (framebuffer, clip_rect.x * view_scale, clip_rect.y * view_scale, pixel_width, pixel_height, COGL_PIXEL_FORMAT_RGBA_8888, pixels); return pixels; } /** * clutter_stage_get_actor_at_pos: * @stage: a #ClutterStage * @pick_mode: how the scene graph should be painted * @x: X coordinate to check * @y: Y coordinate to check * * Checks the scene at the coordinates @x and @y and returns a pointer * to the #ClutterActor at those coordinates. The result is the actor which * would be at the specified location on the next redraw, and is not * necessarily that which was there on the previous redraw. This allows the * function to perform chronologically correctly after any queued changes to * the scene, and even if nothing has been drawn. * * By using @pick_mode it is possible to control which actors will be * painted and thus available. * * Return value: (transfer none): the actor at the specified coordinates, * if any */ ClutterActor * clutter_stage_get_actor_at_pos (ClutterStage *stage, ClutterPickMode pick_mode, float x, float y) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); return _clutter_stage_do_pick (stage, x, y, pick_mode); } /** * clutter_stage_event: * @stage: a #ClutterStage * @event: a #ClutterEvent * * This function is used to emit an event on the main stage. * * You should rarely need to use this function, except for * synthetised events. * * Return value: the return value from the signal emission * * Since: 0.4 */ gboolean clutter_stage_event (ClutterStage *stage, ClutterEvent *event) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (event != NULL, FALSE); if (event->type == CLUTTER_DELETE) { gboolean retval = FALSE; g_signal_emit_by_name (stage, "event", event, &retval); if (!retval) g_signal_emit_by_name (stage, "delete-event", event, &retval); return retval; } if (event->type != CLUTTER_STAGE_STATE) return FALSE; /* emit raw event */ if (clutter_actor_event (CLUTTER_ACTOR (stage), event, FALSE)) return TRUE; if (event->stage_state.changed_mask & CLUTTER_STAGE_STATE_ACTIVATED) { if (event->stage_state.new_state & CLUTTER_STAGE_STATE_ACTIVATED) g_signal_emit (stage, stage_signals[ACTIVATE], 0); else g_signal_emit (stage, stage_signals[DEACTIVATE], 0); } return TRUE; } /** * clutter_stage_set_title: * @stage: A #ClutterStage * @title: A utf8 string for the stage windows title. * * Sets the stage title. * * Since: 0.4 **/ void clutter_stage_set_title (ClutterStage *stage, const gchar *title) { ClutterStagePrivate *priv; ClutterStageWindow *impl; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; g_free (priv->title); priv->title = g_strdup (title); impl = CLUTTER_STAGE_WINDOW (priv->impl); if (CLUTTER_STAGE_WINDOW_GET_IFACE(impl)->set_title != NULL) CLUTTER_STAGE_WINDOW_GET_IFACE (impl)->set_title (impl, priv->title); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_TITLE]); } /** * clutter_stage_get_title: * @stage: A #ClutterStage * * Gets the stage title. * * Return value: pointer to the title string for the stage. The * returned string is owned by the actor and should not * be modified or freed. * * Since: 0.4 **/ const gchar * clutter_stage_get_title (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); return stage->priv->title; } /** * clutter_stage_set_key_focus: * @stage: the #ClutterStage * @actor: (allow-none): the actor to set key focus to, or %NULL * * Sets the key focus on @actor. An actor with key focus will receive * all the key events. If @actor is %NULL, the stage will receive * focus. * * Since: 0.6 */ void clutter_stage_set_key_focus (ClutterStage *stage, ClutterActor *actor) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (actor == NULL || CLUTTER_IS_ACTOR (actor)); priv = stage->priv; /* normalize the key focus. NULL == stage */ if (actor == CLUTTER_ACTOR (stage)) actor = NULL; /* avoid emitting signals and notifications if we're setting the same * actor as the key focus */ if (priv->key_focused_actor == actor) return; if (priv->key_focused_actor != NULL) { ClutterActor *old_focused_actor; old_focused_actor = priv->key_focused_actor; /* set key_focused_actor to NULL before emitting the signal or someone * might hide the previously focused actor in the signal handler */ priv->key_focused_actor = NULL; _clutter_actor_set_has_key_focus (old_focused_actor, FALSE); } else _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), FALSE); /* Note, if someone changes key focus in focus-out signal handler we'd be * overriding the latter call below moving the focus where it was originally * intended. The order of events would be: * 1st focus-out, 2nd focus-out (on stage), 2nd focus-in, 1st focus-in */ if (actor != NULL) { priv->key_focused_actor = actor; _clutter_actor_set_has_key_focus (actor, TRUE); } else _clutter_actor_set_has_key_focus (CLUTTER_ACTOR (stage), TRUE); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_KEY_FOCUS]); } /** * clutter_stage_get_key_focus: * @stage: the #ClutterStage * * Retrieves the actor that is currently under key focus. * * Return value: (transfer none): the actor with key focus, or the stage * * Since: 0.6 */ ClutterActor * clutter_stage_get_key_focus (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); if (stage->priv->key_focused_actor) return stage->priv->key_focused_actor; return CLUTTER_ACTOR (stage); } /*** Perspective boxed type ******/ static gpointer clutter_perspective_copy (gpointer data) { if (G_LIKELY (data)) return g_slice_dup (ClutterPerspective, data); return NULL; } static void clutter_perspective_free (gpointer data) { if (G_LIKELY (data)) g_slice_free (ClutterPerspective, data); } G_DEFINE_BOXED_TYPE (ClutterPerspective, clutter_perspective, clutter_perspective_copy, clutter_perspective_free); /** * clutter_stage_new: * * Creates a new, non-default stage. A non-default stage is a new * top-level actor which can be used as another container. It works * exactly like the default stage, but while clutter_stage_get_default() * will always return the same instance, you will have to keep a pointer * to any #ClutterStage returned by clutter_stage_new(). * * The ability to support multiple stages depends on the current * backend. Use clutter_feature_available() and * %CLUTTER_FEATURE_STAGE_MULTIPLE to check at runtime whether a * backend supports multiple stages. * * Return value: a new stage, or %NULL if the default backend does * not support multiple stages. Use clutter_actor_destroy() to * programmatically close the returned stage. * * Since: 0.8 */ ClutterActor * clutter_stage_new (void) { return g_object_new (CLUTTER_TYPE_STAGE, NULL); } /** * clutter_stage_ensure_current: * @stage: the #ClutterStage * * This function essentially makes sure the right GL context is * current for the passed stage. It is not intended to * be used by applications. * * Since: 0.8 * Deprecated: mutter: This function does not do anything. */ void clutter_stage_ensure_current (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); } /** * clutter_stage_ensure_viewport: * @stage: a #ClutterStage * * Ensures that the GL viewport is updated with the current * stage window size. * * This function will queue a redraw of @stage. * * This function should not be called by applications; it is used * when embedding a #ClutterStage into a toolkit with another * windowing system, like GTK+. * * Since: 1.0 */ void clutter_stage_ensure_viewport (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); _clutter_stage_dirty_viewport (stage); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } # define _DEG_TO_RAD(d) ((d) * ((float) G_PI / 180.0f)) /* This calculates a distance into the view frustum to position the * stage so there is a decent amount of space to position geometry * between the stage and the near clipping plane. * * Some awkward issues with this problem are: * - It's not possible to have a gap as large as the stage size with * a fov > 53° which is basically always the case since the default * fov is 60°. * - This can be deduced if you consider that this requires a * triangle as wide as it is deep to fit in the frustum in front * of the z_near plane. That triangle will always have an angle * of 53.13° at the point sitting on the z_near plane, but if the * frustum has a wider fov angle the left/right clipping planes * can never converge with the two corners of our triangle no * matter what size the triangle has. * - With a fov > 53° there is a trade off between maximizing the gap * size relative to the stage size but not loosing depth precision. * - Perhaps ideally we wouldn't just consider the fov on the y-axis * that is usually used to define a perspective, we would consider * the fov of the axis with the largest stage size so the gap would * accommodate that size best. * * After going around in circles a few times with how to handle these * issues, we decided in the end to go for the simplest solution to * start with instead of an elaborate function that handles arbitrary * fov angles that we currently have no use-case for. * * The solution assumes a fovy of 60° and for that case gives a gap * that's 85% of the stage height. We can consider more elaborate * functions if necessary later. * * One guide we had to steer the gap size we support is the * interactive test, test-texture-quality which expects to animate an * actor to +400 on the z axis with a stage size of 640x480. A gap * that's 85% of the stage height gives a gap of 408 in that case. */ static float calculate_z_translation (float z_near) { /* This solution uses fairly basic trigonometry, but is seems worth * clarifying the particular geometry we are looking at in-case * anyone wants to develop this further later. Not sure how well an * ascii diagram is going to work :-) * * |--- stage_height ---| * | stage line | * ╲━━━━━━━━━━━━━━━━━━━━━╱------------ * ╲. (2) │ .╱ | | * C ╲ . │ . ╱ gap| | * =0.5°╲ . a │ . ╱ | | * b╲(1). D│ . ╱ | | * ╲ B.│. ╱near plane | | * A= ╲━━━━━━━━━╱------------- | * 120° ╲ c │ ╱ | z_2d * ╲ │ ╱ z_near | * left ╲ │ ╱ | | * clip 60°fovy | | * plane ╳---------------------- * | * | * origin line * * The area of interest is the triangle labeled (1) at the top left * marked with the ... line (a) from where the origin line crosses * the near plane to the top left where the stage line cross the * left clip plane. * * The sides of the triangle are a, b and c and the corresponding * angles opposite those sides are A, B and C. * * The angle of C is what trades off the gap size we have relative * to the stage size vs the depth precision we have. * * As mentioned above we arove at the angle for C is by working * backwards from how much space we want for test-texture-quality. * With a stage_height of 480 we want a gap > 400, ideally we also * wanted a somewhat round number as a percentage of the height for * documentation purposes. ~87% or a gap of ~416 is the limit * because that's where we approach a C angle of 0° and effectively * loose all depth precision. * * So for our test app with a stage_height of 480 if we aim for a * gap of 408 (85% of 480) we can get the angle D as * atan (stage_height/2/408) = 30.5°. * * That gives us the angle for B as 90° - 30.5° = 59.5° * * We can already determine that A has an angle of (fovy/2 + 90°) = * 120° * * Therefore C = 180 - A - B = 0.5° * * The length of c = z_near * tan (30°) * * Now we can use the rule a/SinA = c/SinC to calculate the * length of a. After some rearranging that gives us: * * a c * ---------- = ---------- * sin (120°) sin (0.5°) * * c * sin (120°) * a = -------------- * sin (0.5°) * * And with that we can determine z_2d = cos (D) * a = * cos (30.5°) * a + z_near: * * c * sin (120°) * cos (30.5°) * z_2d = --------------------------- + z_near * sin (0.5°) */ /* We expect the compiler should boil this down to z_near * CONSTANT * already, but just in case we use precomputed constants */ #if 0 # define A tanf (_DEG_TO_RAD (30.f)) # define B sinf (_DEG_TO_RAD (120.f)) # define C cosf (_DEG_TO_RAD (30.5f)) # define D sinf (_DEG_TO_RAD (.5f)) #else # define A 0.57735025882720947265625f # define B 0.866025388240814208984375f # define C 0.86162912845611572265625f # define D 0.00872653536498546600341796875f #endif return z_near * A * B * C / D + z_near; } static void clutter_stage_update_view_perspective (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; ClutterPerspective perspective; float z_2d; perspective = priv->perspective; /* Ideally we want to regenerate the perspective matrix whenever * the size changes but if the user has provided a custom matrix * then we don't want to override it */ if (!priv->has_custom_perspective) { perspective.fovy = 60.0; /* 60 Degrees */ perspective.z_near = 0.1; perspective.aspect = priv->viewport[2] / priv->viewport[3]; z_2d = calculate_z_translation (perspective.z_near); /* NB: z_2d is only enough room for 85% of the stage_height between * the stage and the z_near plane. For behind the stage plane we * want a more consistent gap of 10 times the stage_height before * hitting the far plane so we calculate that relative to the final * height of the stage plane at the z_2d_distance we got... */ perspective.z_far = z_2d + tanf (_DEG_TO_RAD (perspective.fovy / 2.0f)) * z_2d * 20.0f; clutter_stage_set_perspective_internal (stage, &perspective); } else { z_2d = calculate_z_translation (perspective.z_near); } cogl_matrix_init_identity (&priv->view); cogl_matrix_view_2d_in_perspective (&priv->view, perspective.fovy, perspective.aspect, perspective.z_near, z_2d, priv->viewport[2], priv->viewport[3]); } void _clutter_stage_maybe_setup_viewport (ClutterStage *stage, ClutterStageView *view) { ClutterStagePrivate *priv = stage->priv; CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); if (clutter_stage_view_is_dirty_viewport (view)) { cairo_rectangle_int_t view_layout; float fb_scale; float viewport_offset_x; float viewport_offset_y; float viewport_x; float viewport_y; float viewport_width; float viewport_height; CLUTTER_NOTE (PAINT, "Setting up the viewport { w:%f, h:%f }", priv->viewport[2], priv->viewport[3]); fb_scale = clutter_stage_view_get_scale (view); clutter_stage_view_get_layout (view, &view_layout); viewport_offset_x = view_layout.x * fb_scale; viewport_offset_y = view_layout.y * fb_scale; viewport_x = roundf (priv->viewport[0] * fb_scale - viewport_offset_x); viewport_y = roundf (priv->viewport[1] * fb_scale - viewport_offset_y); viewport_width = roundf (priv->viewport[2] * fb_scale); viewport_height = roundf (priv->viewport[3] * fb_scale); cogl_framebuffer_set_viewport (fb, viewport_x, viewport_y, viewport_width, viewport_height); clutter_stage_view_set_dirty_viewport (view, FALSE); } if (clutter_stage_view_is_dirty_projection (view)) { cogl_framebuffer_set_projection_matrix (fb, &priv->projection); clutter_stage_view_set_dirty_projection (view, FALSE); } } #undef _DEG_TO_RAD /** * clutter_stage_ensure_redraw: * @stage: a #ClutterStage * * Ensures that @stage is redrawn * * This function should not be called by applications: it is * used when embedding a #ClutterStage into a toolkit with * another windowing system, like GTK+. * * Since: 1.0 */ void clutter_stage_ensure_redraw (ClutterStage *stage) { ClutterMasterClock *master_clock; ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (!_clutter_stage_needs_update (stage)) clutter_stage_schedule_update (stage); priv->redraw_pending = TRUE; master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_start_running (master_clock); } /** * clutter_stage_is_redraw_queued: (skip) */ gboolean clutter_stage_is_redraw_queued (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; return priv->redraw_pending; } /** * clutter_stage_queue_redraw: * @stage: the #ClutterStage * * Queues a redraw for the passed stage. * * Applications should call clutter_actor_queue_redraw() and not * this function. * * Since: 0.8 * * Deprecated: 1.10: Use clutter_actor_queue_redraw() instead. */ void clutter_stage_queue_redraw (ClutterStage *stage) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } /** * clutter_stage_is_default: * @stage: a #ClutterStage * * Checks if @stage is the default stage, or an instance created using * clutter_stage_new() but internally using the same implementation. * * Return value: %TRUE if the passed stage is the default one * * Since: 0.8 * * Deprecated: 1.10: Track the stage pointer inside your application * code, or use clutter_actor_get_stage() to retrieve the stage for * a given actor. */ gboolean clutter_stage_is_default (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); return stage_is_default (stage); } void _clutter_stage_set_window (ClutterStage *stage, ClutterStageWindow *stage_window) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (CLUTTER_IS_STAGE_WINDOW (stage_window)); if (stage->priv->impl != NULL) g_object_unref (stage->priv->impl); stage->priv->impl = stage_window; } ClutterStageWindow * _clutter_stage_get_window (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), NULL); return CLUTTER_STAGE_WINDOW (stage->priv->impl); } ClutterStageWindow * _clutter_stage_get_default_window (void) { ClutterStageManager *manager = clutter_stage_manager_get_default (); ClutterStage *stage; stage = clutter_stage_manager_get_default_stage (manager); if (stage == NULL) return NULL; return _clutter_stage_get_window (stage); } /** * clutter_stage_set_throttle_motion_events: * @stage: a #ClutterStage * @throttle: %TRUE to throttle motion events * * Sets whether motion events received between redraws should * be throttled or not. If motion events are throttled, those * events received by the windowing system between redraws will * be compressed so that only the last event will be propagated * to the @stage and its actors. * * This function should only be used if you want to have all * the motion events delivered to your application code. * * Since: 1.0 */ void clutter_stage_set_throttle_motion_events (ClutterStage *stage, gboolean throttle) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (priv->throttle_motion_events != throttle) priv->throttle_motion_events = throttle; } /** * clutter_stage_get_throttle_motion_events: * @stage: a #ClutterStage * * Retrieves the value set with clutter_stage_set_throttle_motion_events() * * Return value: %TRUE if the motion events are being throttled, * and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_stage_get_throttle_motion_events (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); return stage->priv->throttle_motion_events; } /** * clutter_stage_set_use_alpha: * @stage: a #ClutterStage * @use_alpha: whether the stage should honour the opacity or the * alpha channel of the stage color * * Sets whether the @stage should honour the #ClutterActor:opacity and * the alpha channel of the #ClutterStage:color * * Since: 1.2 */ void clutter_stage_set_use_alpha (ClutterStage *stage, gboolean use_alpha) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; if (priv->use_alpha != use_alpha) { priv->use_alpha = use_alpha; clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_USE_ALPHA]); } } /** * clutter_stage_get_use_alpha: * @stage: a #ClutterStage * * Retrieves the value set using clutter_stage_set_use_alpha() * * Return value: %TRUE if the stage should honour the opacity and the * alpha channel of the stage color * * Since: 1.2 */ gboolean clutter_stage_get_use_alpha (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); return stage->priv->use_alpha; } /** * clutter_stage_set_minimum_size: * @stage: a #ClutterStage * @width: width, in pixels * @height: height, in pixels * * Sets the minimum size for a stage window, if the default backend * uses #ClutterStage inside a window * * This is a convenience function, and it is equivalent to setting the * #ClutterActor:min-width and #ClutterActor:min-height on @stage * * If the current size of @stage is smaller than the minimum size, the * @stage will be resized to the new @width and @height * * Since: 1.2 */ void clutter_stage_set_minimum_size (ClutterStage *stage, guint width, guint height) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail ((width > 0) && (height > 0)); g_object_set (G_OBJECT (stage), "min-width", (gfloat) width, "min-height", (gfloat )height, NULL); } /** * clutter_stage_get_minimum_size: * @stage: a #ClutterStage * @width: (out): return location for the minimum width, in pixels, * or %NULL * @height: (out): return location for the minimum height, in pixels, * or %NULL * * Retrieves the minimum size for a stage window as set using * clutter_stage_set_minimum_size(). * * The returned size may not correspond to the actual minimum size and * it is specific to the #ClutterStage implementation inside the * Clutter backend * * Since: 1.2 */ void clutter_stage_get_minimum_size (ClutterStage *stage, guint *width_p, guint *height_p) { gfloat width, height; gboolean width_set, height_set; g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_object_get (G_OBJECT (stage), "min-width", &width, "min-width-set", &width_set, "min-height", &height, "min-height-set", &height_set, NULL); /* if not width or height have been set, then the Stage * minimum size is defined to be 1x1 */ if (!width_set) width = 1; if (!height_set) height = 1; if (width_p) *width_p = (guint) width; if (height_p) *height_p = (guint) height; } /** * clutter_stage_schedule_update: * @stage: a #ClutterStage actor * * Schedules a redraw of the #ClutterStage at the next optimal timestamp. */ void clutter_stage_schedule_update (ClutterStage *stage) { ClutterStageWindow *stage_window; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return; stage_window = _clutter_stage_get_window (stage); if (stage_window == NULL) return; stage->priv->needs_update = TRUE; return _clutter_stage_window_schedule_update (stage_window, stage->priv->sync_delay); } /** * _clutter_stage_get_update_time: * @stage: a #ClutterStage actor * * Returns the earliest time in which the stage is ready to update. The update * time is set when clutter_stage_schedule_update() is called. This can then * be used by e.g. the #ClutterMasterClock to know when the stage needs to be * redrawn. * * Returns: -1 if no redraw is needed; 0 if the backend doesn't know, or the * timestamp (in microseconds) otherwise. */ gint64 _clutter_stage_get_update_time (ClutterStage *stage) { ClutterStageWindow *stage_window; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return 0; stage_window = _clutter_stage_get_window (stage); if (stage_window == NULL) return 0; return _clutter_stage_window_get_update_time (stage_window); } /** * _clutter_stage_clear_update_time: * @stage: a #ClutterStage actor * * Resets the update time. Call this after a redraw, so that the update time * can again be updated. */ void _clutter_stage_clear_update_time (ClutterStage *stage) { ClutterStageWindow *stage_window; stage_window = _clutter_stage_get_window (stage); if (stage_window) _clutter_stage_window_clear_update_time (stage_window); } int64_t _clutter_stage_get_next_presentation_time (ClutterStage *stage) { ClutterStageWindow *stage_window; if (CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return 0; stage_window = _clutter_stage_get_window (stage); if (stage_window == NULL) return 0; return _clutter_stage_window_get_next_presentation_time (stage_window); } ClutterPaintVolume * _clutter_stage_paint_volume_stack_allocate (ClutterStage *stage) { GArray *paint_volume_stack = stage->priv->paint_volume_stack; g_array_set_size (paint_volume_stack, paint_volume_stack->len+1); return &g_array_index (paint_volume_stack, ClutterPaintVolume, paint_volume_stack->len - 1); } void _clutter_stage_paint_volume_stack_free_all (ClutterStage *stage) { GArray *paint_volume_stack = stage->priv->paint_volume_stack; int i; for (i = 0; i < paint_volume_stack->len; i++) { ClutterPaintVolume *pv = &g_array_index (paint_volume_stack, ClutterPaintVolume, i); clutter_paint_volume_free (pv); } g_array_set_size (paint_volume_stack, 0); } /* The is an out-of-band paramater available while painting that * can be used to cull actors. */ const ClutterPlane * _clutter_stage_get_clip (ClutterStage *stage) { return stage->priv->current_clip_planes; } /* When an actor queues a redraw we add it to a list on the stage that * gets processed once all updates to the stage have been finished. * * This deferred approach to processing queue_redraw requests means * that we can avoid redundant transformations of clip volumes if * something later triggers a full stage redraw anyway. It also means * we can be more sure that all the referenced actors will have valid * allocations improving the chance that we can determine the actors * paint volume so we can clip the redraw request even if the user * didn't explicitly do so. */ ClutterStageQueueRedrawEntry * _clutter_stage_queue_actor_redraw (ClutterStage *stage, ClutterStageQueueRedrawEntry *entry, ClutterActor *actor, const ClutterPaintVolume *clip) { ClutterStagePrivate *priv = stage->priv; CLUTTER_NOTE (CLIPPING, "stage_queue_actor_redraw (actor=%s, clip=%p): ", _clutter_actor_get_debug_name (actor), clip); /* Queuing a redraw or clip change invalidates the pick cache, unless we're * in the middle of building it. So we reset the cached flag but don't * completely clear the pick stack... */ priv->cached_pick_mode = CLUTTER_PICK_NONE; if (!priv->redraw_pending) { ClutterMasterClock *master_clock; CLUTTER_NOTE (PAINT, "First redraw request"); clutter_stage_schedule_update (stage); priv->redraw_pending = TRUE; master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_start_running (master_clock); } #ifdef CLUTTER_ENABLE_DEBUG else { CLUTTER_NOTE (PAINT, "Redraw request number %lu", priv->redraw_count + 1); priv->redraw_count += 1; } #endif /* CLUTTER_ENABLE_DEBUG */ if (entry) { /* Ignore all requests to queue a redraw for an actor if a full * (non-clipped) redraw of the actor has already been queued. */ if (!entry->has_clip) { CLUTTER_NOTE (CLIPPING, "Bail from stage_queue_actor_redraw (%s): " "Unclipped redraw of actor already queued", _clutter_actor_get_debug_name (actor)); return entry; } /* If queuing a clipped redraw and a clipped redraw has * previously been queued for this actor then combine the latest * clip together with the existing clip */ if (clip) clutter_paint_volume_union (&entry->clip, clip); else { clutter_paint_volume_free (&entry->clip); entry->has_clip = FALSE; } return entry; } else { entry = g_slice_new (ClutterStageQueueRedrawEntry); entry->actor = g_object_ref (actor); if (clip) { entry->has_clip = TRUE; _clutter_paint_volume_init_static (&entry->clip, actor); _clutter_paint_volume_set_from_volume (&entry->clip, clip); } else entry->has_clip = FALSE; stage->priv->pending_queue_redraws = g_list_prepend (stage->priv->pending_queue_redraws, entry); return entry; } } static void free_queue_redraw_entry (ClutterStageQueueRedrawEntry *entry) { if (entry->actor) g_object_unref (entry->actor); if (entry->has_clip) clutter_paint_volume_free (&entry->clip); g_slice_free (ClutterStageQueueRedrawEntry, entry); } void _clutter_stage_queue_redraw_entry_invalidate (ClutterStageQueueRedrawEntry *entry) { if (entry == NULL) return; if (entry->actor != NULL) { g_object_unref (entry->actor); entry->actor = NULL; } if (entry->has_clip) { clutter_paint_volume_free (&entry->clip); entry->has_clip = FALSE; } } static void clutter_stage_maybe_finish_queue_redraws (ClutterStage *stage) { /* Note: we have to repeat until the pending_queue_redraws list is * empty because actors are allowed to queue redraws in response to * the queue-redraw signal. For example Clone actors or * texture_new_from_actor actors will have to queue a redraw if * their source queues a redraw. */ while (stage->priv->pending_queue_redraws) { GList *l; /* XXX: we need to allow stage->priv->pending_queue_redraws to * be updated while we process the current entries in the list * so we steal the list pointer and then reset it to an empty * list before processing... */ GList *stolen_list = stage->priv->pending_queue_redraws; stage->priv->pending_queue_redraws = NULL; for (l = stolen_list; l; l = l->next) { ClutterStageQueueRedrawEntry *entry = l->data; ClutterPaintVolume *clip; /* NB: Entries may be invalidated if the actor gets destroyed */ if (G_LIKELY (entry->actor != NULL)) { clip = entry->has_clip ? &entry->clip : NULL; _clutter_actor_finish_queue_redraw (entry->actor, clip); } free_queue_redraw_entry (entry); } g_list_free (stolen_list); } } /** * clutter_stage_set_accept_focus: * @stage: a #ClutterStage * @accept_focus: %TRUE to accept focus on show * * Sets whether the @stage should accept the key focus when shown. * * This function should be called before showing @stage using * clutter_actor_show(). * * Since: 1.6 */ void clutter_stage_set_accept_focus (ClutterStage *stage, gboolean accept_focus) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); accept_focus = !!accept_focus; priv = stage->priv; if (priv->accept_focus != accept_focus) { _clutter_stage_window_set_accept_focus (priv->impl, accept_focus); g_object_notify_by_pspec (G_OBJECT (stage), obj_props[PROP_ACCEPT_FOCUS]); } } /** * clutter_stage_get_accept_focus: * @stage: a #ClutterStage * * Retrieves the value set with clutter_stage_set_accept_focus(). * * Return value: %TRUE if the #ClutterStage should accept focus, and %FALSE * otherwise * * Since: 1.6 */ gboolean clutter_stage_get_accept_focus (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), TRUE); return stage->priv->accept_focus; } /** * clutter_stage_set_motion_events_enabled: * @stage: a #ClutterStage * @enabled: %TRUE to enable the motion events delivery, and %FALSE * otherwise * * Sets whether per-actor motion events (and relative crossing * events) should be disabled or not. * * The default is %TRUE. * * If @enable is %FALSE the following signals will not be emitted * by the actors children of @stage: * * - #ClutterActor::motion-event * - #ClutterActor::enter-event * - #ClutterActor::leave-event * * The events will still be delivered to the #ClutterStage. * * The main side effect of this function is that disabling the motion * events will disable picking to detect the #ClutterActor underneath * the pointer for each motion event. This is useful, for instance, * when dragging a #ClutterActor across the @stage: the actor underneath * the pointer is not going to change, so it's meaningless to perform * a pick. * * Since: 1.8 */ void clutter_stage_set_motion_events_enabled (ClutterStage *stage, gboolean enabled) { ClutterStagePrivate *priv; g_return_if_fail (CLUTTER_IS_STAGE (stage)); priv = stage->priv; enabled = !!enabled; if (priv->motion_events_enabled != enabled) priv->motion_events_enabled = enabled; } /** * clutter_stage_get_motion_events_enabled: * @stage: a #ClutterStage * * Retrieves the value set using clutter_stage_set_motion_events_enabled(). * * Return value: %TRUE if the per-actor motion event delivery is enabled * and %FALSE otherwise * * Since: 1.8 */ gboolean clutter_stage_get_motion_events_enabled (ClutterStage *stage) { g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); return stage->priv->motion_events_enabled; } void _clutter_stage_add_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device, ClutterActor *actor) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_pointer_drag_actors"); if (drag_actors == NULL) { drag_actors = g_hash_table_new (NULL, NULL); g_object_set_data_full (G_OBJECT (stage), "__clutter_stage_pointer_drag_actors", drag_actors, (GDestroyNotify) g_hash_table_destroy); } g_hash_table_replace (drag_actors, device, actor); } ClutterActor * _clutter_stage_get_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_pointer_drag_actors"); if (drag_actors == NULL) return NULL; return g_hash_table_lookup (drag_actors, device); } void _clutter_stage_remove_pointer_drag_actor (ClutterStage *stage, ClutterInputDevice *device) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_pointer_drag_actors"); if (drag_actors == NULL) return; g_hash_table_remove (drag_actors, device); if (g_hash_table_size (drag_actors) == 0) g_object_set_data (G_OBJECT (stage), "__clutter_stage_pointer_drag_actors", NULL); } void _clutter_stage_add_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence, ClutterActor *actor) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_touch_drag_actors"); if (drag_actors == NULL) { drag_actors = g_hash_table_new (NULL, NULL); g_object_set_data_full (G_OBJECT (stage), "__clutter_stage_touch_drag_actors", drag_actors, (GDestroyNotify) g_hash_table_destroy); } g_hash_table_replace (drag_actors, sequence, actor); } ClutterActor * _clutter_stage_get_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_touch_drag_actors"); if (drag_actors == NULL) return NULL; return g_hash_table_lookup (drag_actors, sequence); } void _clutter_stage_remove_touch_drag_actor (ClutterStage *stage, ClutterEventSequence *sequence) { GHashTable *drag_actors; drag_actors = g_object_get_data (G_OBJECT (stage), "__clutter_stage_touch_drag_actors"); if (drag_actors == NULL) return; g_hash_table_remove (drag_actors, sequence); if (g_hash_table_size (drag_actors) == 0) g_object_set_data (G_OBJECT (stage), "__clutter_stage_touch_drag_actors", NULL); } /*< private > * _clutter_stage_get_state: * @stage: a #ClutterStage * * Retrieves the current #ClutterStageState flags associated to the @stage. * * Return value: a bitwise OR of #ClutterStageState flags */ ClutterStageState _clutter_stage_get_state (ClutterStage *stage) { return stage->priv->current_state; } /*< private > * _clutter_stage_is_activated: * @stage: a #ClutterStage * * Checks whether the @stage state includes %CLUTTER_STAGE_STATE_ACTIVATED. * * Return value: %TRUE if the @stage is active */ gboolean _clutter_stage_is_activated (ClutterStage *stage) { return (stage->priv->current_state & CLUTTER_STAGE_STATE_ACTIVATED) != 0; } /*< private > * _clutter_stage_update_state: * @stage: a #ClutterStage * @unset_flags: flags to unset * @set_flags: flags to set * * Updates the state of @stage, by unsetting the @unset_flags and setting * the @set_flags. * * If the stage state has been changed, this function will queue a * #ClutterEvent of type %CLUTTER_STAGE_STATE. * * Return value: %TRUE if the state was updated, and %FALSE otherwise */ gboolean _clutter_stage_update_state (ClutterStage *stage, ClutterStageState unset_flags, ClutterStageState set_flags) { ClutterStageState new_state; ClutterEvent *event; new_state = stage->priv->current_state; new_state |= set_flags; new_state &= ~unset_flags; if (new_state == stage->priv->current_state) return FALSE; event = clutter_event_new (CLUTTER_STAGE_STATE); clutter_event_set_stage (event, stage); event->stage_state.new_state = new_state; event->stage_state.changed_mask = new_state ^ stage->priv->current_state; stage->priv->current_state = new_state; clutter_stage_event (stage, event); clutter_event_free (event); return TRUE; } /** * clutter_stage_set_sync_delay: * @stage: a #ClutterStage * @sync_delay: number of milliseconds after frame presentation to wait * before painting the next frame. If less than zero, restores the * default behavior where redraw is throttled to the refresh rate but * not synchronized to it. * * This function enables an alternate behavior where Clutter draws at * a fixed point in time after the frame presentation time (also known * as the VBlank time). This is most useful when the application * wants to show incoming data with predictable latency. (The primary * example of this would be a window system compositor.) By synchronizing * to provide new data before Clutter redraws, an external source of * updates (in the compositor, an application) can get a reliable latency. * * The appropriate value of @sync_delay depends on the complexity of * drawing the stage's scene graph - in general a value of between 0 * and 8 ms (up to one-half of a typical 60hz frame rate) is appropriate. * using a larger value will reduce latency but risks skipping a frame if * drawing the stage takes too long. * * Since: 1.14 * Stability: unstable */ void clutter_stage_set_sync_delay (ClutterStage *stage, gint sync_delay) { g_return_if_fail (CLUTTER_IS_STAGE (stage)); stage->priv->sync_delay = sync_delay; } /** * clutter_stage_skip_sync_delay: * @stage: a #ClutterStage * * Causes the next frame for the stage to be drawn as quickly as * possible, ignoring any delay that clutter_stage_set_sync_delay() * would normally cause. * * Since: 1.14 * Stability: unstable */ void clutter_stage_skip_sync_delay (ClutterStage *stage) { ClutterStageWindow *stage_window; stage_window = _clutter_stage_get_window (stage); if (stage_window) _clutter_stage_window_schedule_update (stage_window, -1); } int64_t clutter_stage_get_frame_counter (ClutterStage *stage) { ClutterStageWindow *stage_window; stage_window = _clutter_stage_get_window (stage); return _clutter_stage_window_get_frame_counter (stage_window); } void _clutter_stage_presented (ClutterStage *stage, CoglFrameEvent frame_event, ClutterFrameInfo *frame_info) { g_signal_emit (stage, stage_signals[PRESENTED], 0, (int) frame_event, frame_info); } static void capture_view (ClutterStage *stage, gboolean paint, ClutterStageView *view, ClutterCapture *capture) { cairo_surface_t *image; uint8_t *data; int stride; cairo_rectangle_int_t *rect; float view_scale; float texture_width; float texture_height; rect = &capture->rect; view_scale = clutter_stage_view_get_scale (view); texture_width = roundf (rect->width * view_scale); texture_height = roundf (rect->height * view_scale); image = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, texture_width, texture_height); cairo_surface_set_device_scale (image, view_scale, view_scale); data = cairo_image_surface_get_data (image); stride = cairo_image_surface_get_stride (image); capture_view_into (stage, paint, view, rect, data, stride); capture->image = image; cairo_surface_mark_dirty (capture->image); } /** * clutter_stage_capture: * @stage: a #ClutterStage * @paint: whether to pain the frame * @rect: a #cairo_rectangle_int_t in stage coordinates * @out_captures: (out) (array length=out_n_captures): an array of * #ClutterCapture * @out_n_captures: (out): the number of captures in @out_captures * * Captures the stage pixels of @rect into @captures. @rect is in stage * coordinates. * * Returns: %TRUE if a #ClutterCapture has been created, %FALSE otherwise */ gboolean clutter_stage_capture (ClutterStage *stage, gboolean paint, cairo_rectangle_int_t *rect, ClutterCapture **out_captures, int *out_n_captures) { ClutterStagePrivate *priv = stage->priv; GList *views = _clutter_stage_window_get_views (priv->impl); GList *l; ClutterCapture *captures; int n_captures; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); captures = g_new0 (ClutterCapture, g_list_length (views)); n_captures = 0; for (l = views; l; l = l->next) { ClutterStageView *view = l->data; ClutterCapture *capture; cairo_rectangle_int_t view_layout; cairo_region_t *region; clutter_stage_view_get_layout (view, &view_layout); region = cairo_region_create_rectangle (&view_layout); cairo_region_intersect_rectangle (region, rect); capture = &captures[n_captures]; cairo_region_get_extents (region, &capture->rect); cairo_region_destroy (region); if (capture->rect.width == 0 || capture->rect.height == 0) continue; capture_view (stage, paint, view, capture); n_captures++; } if (n_captures == 0) g_clear_pointer (&captures, g_free); *out_captures = captures; *out_n_captures = n_captures; return n_captures > 0; } gboolean clutter_stage_get_capture_final_size (ClutterStage *stage, cairo_rectangle_int_t *rect, int *out_width, int *out_height, float *out_scale) { float max_scale; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); if (rect) { graphene_rect_t capture_rect; _clutter_util_rect_from_rectangle (rect, &capture_rect); if (!_clutter_stage_get_max_view_scale_factor_for_rect (stage, &capture_rect, &max_scale)) return FALSE; if (out_width) *out_width = (gint) roundf (rect->width * max_scale); if (out_height) *out_height = (gint) roundf (rect->height * max_scale); } else { ClutterActorBox alloc; float stage_width, stage_height; clutter_actor_get_allocation_box (CLUTTER_ACTOR (stage), &alloc); clutter_actor_box_get_size (&alloc, &stage_width, &stage_height); if (!_clutter_actor_get_real_resource_scale (CLUTTER_ACTOR (stage), &max_scale)) return FALSE; if (out_width) *out_width = (gint) roundf (stage_width * max_scale); if (out_height) *out_height = (gint) roundf (stage_height * max_scale); } if (out_scale) *out_scale = max_scale; return TRUE; } /** * clutter_stage_paint_to_framebuffer: (skip) */ void clutter_stage_paint_to_framebuffer (ClutterStage *stage, CoglFramebuffer *framebuffer, const cairo_rectangle_int_t *rect, float scale, ClutterPaintFlag paint_flags) { ClutterStagePrivate *priv = stage->priv; ClutterPaintContext *paint_context; cairo_region_t *redraw_clip; redraw_clip = cairo_region_create_rectangle (rect); paint_context = clutter_paint_context_new_for_framebuffer (framebuffer); cairo_region_destroy (redraw_clip); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_set_projection_matrix (framebuffer, &priv->projection); cogl_framebuffer_set_viewport (framebuffer, -(rect->x * scale), -(rect->y * scale), priv->viewport[2] * scale, priv->viewport[3] * scale); clutter_actor_paint (CLUTTER_ACTOR (stage), paint_context); cogl_framebuffer_pop_matrix (framebuffer); clutter_paint_context_destroy (paint_context); } /** * clutter_stage_paint_to_buffer: (skip) */ gboolean clutter_stage_paint_to_buffer (ClutterStage *stage, const cairo_rectangle_int_t *rect, float scale, uint8_t *data, int stride, CoglPixelFormat format, ClutterPaintFlag paint_flags, GError **error) { ClutterBackend *clutter_backend = clutter_get_default_backend (); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); int texture_width, texture_height; CoglTexture2D *texture; CoglOffscreen *offscreen; CoglFramebuffer *framebuffer; CoglBitmap *bitmap; texture_width = (int) roundf (rect->width * scale); texture_height = (int) roundf (rect->height * scale); texture = cogl_texture_2d_new_with_size (cogl_context, texture_width, texture_height); if (!texture) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create %dx%d texture", texture_width, texture_height); return FALSE; } offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); framebuffer = COGL_FRAMEBUFFER (offscreen); cogl_object_unref (texture); if (!cogl_framebuffer_allocate (framebuffer, error)) return FALSE; clutter_stage_paint_to_framebuffer (stage, framebuffer, rect, scale, paint_flags); bitmap = cogl_bitmap_new_for_data (cogl_context, texture_width, texture_height, format, stride, data); cogl_framebuffer_read_pixels_into_bitmap (framebuffer, 0, 0, COGL_READ_PIXELS_COLOR_BUFFER, bitmap); cogl_object_unref (bitmap); cogl_object_unref (framebuffer); return TRUE; } static void capture_view_into (ClutterStage *stage, gboolean paint, ClutterStageView *view, cairo_rectangle_int_t *rect, uint8_t *data, int stride) { g_autoptr (GError) error = NULL; float view_scale; g_return_if_fail (CLUTTER_IS_STAGE (stage)); view_scale = clutter_stage_view_get_scale (view); if (!clutter_stage_paint_to_buffer (stage, rect, view_scale, data, stride, CLUTTER_CAIRO_FORMAT_ARGB32, CLUTTER_PAINT_FLAG_NO_CURSORS, &error)) g_warning ("Failed to capture stage: %s", error->message); } void clutter_stage_capture_into (ClutterStage *stage, gboolean paint, cairo_rectangle_int_t *rect, uint8_t *data) { ClutterStagePrivate *priv = stage->priv; GList *l; int bpp = 4; int stride; stride = rect->width * 4; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) { ClutterStageView *view = l->data; cairo_rectangle_int_t view_layout; cairo_region_t *region; cairo_rectangle_int_t capture_rect; int x_offset, y_offset; clutter_stage_view_get_layout (view, &view_layout); region = cairo_region_create_rectangle (&view_layout); cairo_region_intersect_rectangle (region, rect); cairo_region_get_extents (region, &capture_rect); cairo_region_destroy (region); x_offset = capture_rect.x - rect->x; y_offset = capture_rect.y - rect->y; capture_view_into (stage, paint, view, &capture_rect, data + (x_offset * bpp) + (y_offset * stride), stride); } } /** * clutter_stage_freeze_updates: * * Freezing updates makes Clutter stop processing events, * redrawing, and advancing timelines, by pausing the master clock. This is * necessary when implementing a display server, to ensure that Clutter doesn't * keep trying to page flip when DRM master has been dropped, e.g. when VT * switched away. * * The master clock starts out running, so if you are VT switched away on * startup, you need to call this immediately. * * To thaw updates, use clutter_stage_thaw_updates(). */ void clutter_stage_freeze_updates (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; priv->update_freeze_count++; if (priv->update_freeze_count == 1) { ClutterMasterClock *master_clock; master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_set_paused (master_clock, TRUE); } } /** * clutter_stage_thaw_updates: * * Resumes a master clock that has previously been frozen with * clutter_stage_freeze_updates(), and start pumping the master clock * again at the next iteration. Note that if you're switching back to your * own VT, you should probably also queue a stage redraw with * clutter_stage_ensure_redraw(). */ void clutter_stage_thaw_updates (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; g_assert (priv->update_freeze_count > 0); priv->update_freeze_count--; if (priv->update_freeze_count == 0) { ClutterMasterClock *master_clock; master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_set_paused (master_clock, FALSE); } } GList * _clutter_stage_peek_stage_views (ClutterStage *stage) { ClutterStagePrivate *priv = stage->priv; return _clutter_stage_window_get_views (priv->impl); } void clutter_stage_update_resource_scales (ClutterStage *stage) { _clutter_actor_queue_update_resource_scale_recursive (CLUTTER_ACTOR (stage)); } gboolean _clutter_stage_get_max_view_scale_factor_for_rect (ClutterStage *stage, graphene_rect_t *rect, float *view_scale) { ClutterStagePrivate *priv = stage->priv; float scale = 0.0f; GList *l; for (l = _clutter_stage_window_get_views (priv->impl); l; l = l->next) { ClutterStageView *view = l->data; cairo_rectangle_int_t view_layout; graphene_rect_t view_rect; clutter_stage_view_get_layout (view, &view_layout); _clutter_util_rect_from_rectangle (&view_layout, &view_rect); if (graphene_rect_intersection (&view_rect, rect, NULL)) scale = MAX (clutter_stage_view_get_scale (view), scale); } if (scale == 0.0) return FALSE; *view_scale = scale; return TRUE; } static void on_device_actor_reactive_changed (ClutterActor *actor, GParamSpec *pspec, PointerDeviceEntry *entry) { } static void on_device_actor_destroyed (ClutterActor *actor, PointerDeviceEntry *entry) { /* Simply unset the current_actor pointer here, there's no need to * unset has_pointer or to disconnect any signals because the actor * is gone anyway. * Also, as soon as the next repaint happens, a repick should be triggered * and the PointerDeviceEntry will get updated again, so no need to * trigger a repick here. */ entry->current_actor = NULL; } static void free_pointer_device_entry (PointerDeviceEntry *entry) { if (entry->current_actor) { ClutterActor *actor = entry->current_actor; g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (on_device_actor_reactive_changed), entry); g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (on_device_actor_destroyed), entry); _clutter_actor_set_has_pointer (actor, FALSE); } g_free (entry); } /** * clutter_stage_get_device_coords: (skip): */ void clutter_stage_get_device_coords (ClutterStage *stage, ClutterInputDevice *device, ClutterEventSequence *sequence, graphene_point_t *coords) { ClutterStagePrivate *priv = stage->priv; PointerDeviceEntry *entry = NULL; g_return_if_fail (CLUTTER_IS_STAGE (stage)); g_return_if_fail (device != NULL); if (sequence != NULL) entry = g_hash_table_lookup (priv->touch_sequences, sequence); else entry = g_hash_table_lookup (priv->pointer_devices, device); if (entry && coords) *coords = entry->coords; } muffin-6.4.1/clutter/clutter/clutter-keysyms-table.c0000664000175000017500000040053214723361714021527 0ustar fabiofabio#include "clutter-build-config.h" #include #include "clutter-event.h" /* Code below from GDK, which contains following comment: * * Thanks to Markus G. Kuhn for the ksysym<->Unicode * mapping functions, from the xterm sources. */ static const struct { unsigned short keysym; unsigned short ucs; } clutter_keysym_to_unicode_tab[] = { { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ { 0x06ad, 0x0491 }, /* Ukrainian_ghe_with_upturn ґ CYRILLIC SMALL LETTER GHE WITH UPTURN */ { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ { 0x06bd, 0x0490 }, /* Ukrainian_GHE_WITH_UPTURN Ґ CYRILLIC CAPITAL LETTER GHE WITH UPTURN */ { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ { 0x07a5, 0x03aa }, /* Greek_IOTAdieresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ /* 0x08a1 leftradical ? ??? */ /* 0x08a2 topleftradical ? ??? */ /* 0x08a3 horizconnector ? ??? */ { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ /* 0x08a7 topleftsqbracket ? ??? */ /* 0x08a8 botleftsqbracket ? ??? */ /* 0x08a9 toprightsqbracket ? ??? */ /* 0x08aa botrightsqbracket ? ??? */ /* 0x08ab topleftparens ? ??? */ /* 0x08ac botleftparens ? ??? */ /* 0x08ad toprightparens ? ??? */ /* 0x08ae botrightparens ? ??? */ /* 0x08af leftmiddlecurlybrace ? ??? */ /* 0x08b0 rightmiddlecurlybrace ? ??? */ /* 0x08b1 topleftsummation ? ??? */ /* 0x08b2 botleftsummation ? ??? */ /* 0x08b3 topvertsummationconnector ? ??? */ /* 0x08b4 botvertsummationconnector ? ??? */ /* 0x08b5 toprightsummation ? ??? */ /* 0x08b6 botrightsummation ? ??? */ /* 0x08b7 rightmiddlesummation ? ??? */ { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ { 0x08c8, 0x2245 }, /* approximate ≅ APPROXIMATELY EQUAL TO */ /* 0x08c9 similarequal ? ??? */ { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ { 0x08dd, 0x222a }, /* union ∪ UNION */ { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ { 0x09df, 0x2422 }, /* blank ␢ BLANK SYMBOL */ { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ /* 0x09ef horizlinescan1 ? ??? */ /* 0x09f0 horizlinescan3 ? ??? */ { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ /* 0x09f2 horizlinescan7 ? ??? */ /* 0x09f3 horizlinescan9 ? ??? */ { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ { 0x0aa4, 0x2005 }, /* em4space   FOUR-PER-EM SPACE */ { 0x0aa5, 0x2007 }, /* digitspace   FIGURE SPACE */ { 0x0aa6, 0x2008 }, /* punctspace   PUNCTUATION SPACE */ { 0x0aa7, 0x2009 }, /* thinspace   THIN SPACE */ { 0x0aa8, 0x200a }, /* hairspace   HAIR SPACE */ { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ { 0x0aaa, 0x2013 }, /* endash – EN DASH */ /* 0x0aac signifblank ? ??? */ { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ /* 0x0aaf doubbaselinedot ? ??? */ { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ { 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */ { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ /* 0x0abf marker ? ??? */ { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ /* 0x0acb trademarkincircle ? ??? */ { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ { 0x0acf, 0x25a1 }, /* emopenrectangle □ WHITE SQUARE */ { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ /* 0x0ada hexagram ? ??? */ { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ { 0x0adf, 0x25a0 }, /* emfilledrect ■ BLACK SQUARE */ { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ { 0x0af1, 0x2020 }, /* dagger † DAGGER */ { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ { 0x0afc, 0x2038 }, /* caret ‸ CARET */ { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ /* 0x0aff cursor ? ??? */ { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ { 0x0bc2, 0x22a4 }, /* downtack ⊤ DOWN TACK */ { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD (Unicode 3.0) */ { 0x0bce, 0x22a5 }, /* uptack ⊥ UP TACK */ { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ { 0x0bdc, 0x22a3 }, /* lefttack ⊣ LEFT TACK */ { 0x0bfc, 0x22a2 }, /* righttack ⊢ RIGHT TACK */ { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ { 0x0dde, 0x0e3e }, /* Thai_maihanakat_maitho ฾ ??? */ { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ /* 0x0ef3 Hangul_KkogjiDalrinIeung ? ??? */ { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ /* 0x0ef9 Hangul_J_KkogjiDalrinIeung ? ??? */ { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ { 0x20a0, 0x20a0 }, /* EcuSign ₠ EURO-CURRENCY SIGN */ { 0x20a1, 0x20a1 }, /* ColonSign ₡ COLON SIGN */ { 0x20a2, 0x20a2 }, /* CruzeiroSign ₢ CRUZEIRO SIGN */ { 0x20a3, 0x20a3 }, /* FFrancSign ₣ FRENCH FRANC SIGN */ { 0x20a4, 0x20a4 }, /* LiraSign ₤ LIRA SIGN */ { 0x20a5, 0x20a5 }, /* MillSign ₥ MILL SIGN */ { 0x20a6, 0x20a6 }, /* NairaSign ₦ NAIRA SIGN */ { 0x20a7, 0x20a7 }, /* PesetaSign ₧ PESETA SIGN */ { 0x20a8, 0x20a8 }, /* RupeeSign ₨ RUPEE SIGN */ { 0x20a9, 0x20a9 }, /* WonSign ₩ WON SIGN */ { 0x20aa, 0x20aa }, /* NewSheqelSign ₪ NEW SHEQEL SIGN */ { 0x20ab, 0x20ab }, /* DongSign ₫ DONG SIGN */ { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ /* Following items added to GTK, not in the xterm table */ /* Numeric keypad */ { 0xFF80 /* Space */, ' ' }, { 0xFFAA /* Multiply */, '*' }, { 0xFFAB /* Add */, '+' }, { 0xFFAC /* Separator */, ',' }, { 0xFFAD /* Subtract */, '-' }, { 0xFFAE /* Decimal */, '.' }, { 0xFFAF /* Divide */, '/' }, { 0xFFB0 /* 0 */, '0' }, { 0xFFB1 /* 1 */, '1' }, { 0xFFB2 /* 2 */, '2' }, { 0xFFB3 /* 3 */, '3' }, { 0xFFB4 /* 4 */, '4' }, { 0xFFB5 /* 5 */, '5' }, { 0xFFB6 /* 6 */, '6' }, { 0xFFB7 /* 7 */, '7' }, { 0xFFB8 /* 8 */, '8' }, { 0xFFB9 /* 9 */, '9' }, { 0xFFBD /* Equal */, '=' }, /* End numeric keypad */ }; static const int clutter_keysym_to_unicode_tab_size = G_N_ELEMENTS (clutter_keysym_to_unicode_tab); /** * clutter_keysym_to_unicode: * @keyval: a key symbol * * Converts @keyval from a Clutter key symbol to the corresponding * ISO10646 (Unicode) character. * * Return value: a Unicode character, or 0 if there is no corresponding * character. */ guint32 clutter_keysym_to_unicode (guint keyval) { int min = 0; int max = clutter_keysym_to_unicode_tab_size - 1; int mid; /* First check for Latin-1 characters (1:1 mapping) */ if ((keyval >= 0x0020 && keyval <= 0x007e) || (keyval >= 0x00a0 && keyval <= 0x00ff)) return keyval; /* Also check for directly encoded 24-bit UCS characters: */ if ((keyval & 0xff000000) == 0x01000000) return keyval & 0x00ffffff; /* binary search in table */ while (max >= min) { mid = (min + max) / 2; if (clutter_keysym_to_unicode_tab[mid].keysym < keyval) min = mid + 1; else if (clutter_keysym_to_unicode_tab[mid].keysym > keyval) max = mid - 1; else { /* found it */ return clutter_keysym_to_unicode_tab[mid].ucs; } } /* No matching Unicode value found */ return 0; } static const struct { unsigned short keysym; unsigned short ucs; } clutter_unicode_to_keysym_tab[] = { { 0x0abd, 0x002e }, /* decimalpoint . FULL STOP */ { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ { 0x07a5, 0x03aa }, /* Greek_IOTAdieresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ { 0x0aa4, 0x2005 }, /* em4space   FOUR-PER-EM SPACE */ { 0x0aa5, 0x2007 }, /* digitspace   FIGURE SPACE */ { 0x0aa6, 0x2008 }, /* punctspace   PUNCTUATION SPACE */ { 0x0aa7, 0x2009 }, /* thinspace   THIN SPACE */ { 0x0aa8, 0x200a }, /* hairspace   HAIR SPACE */ { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ { 0x0aaa, 0x2013 }, /* endash – EN DASH */ { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ { 0x0af1, 0x2020 }, /* dagger † DAGGER */ { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ { 0x0afc, 0x2038 }, /* caret ‸ CARET */ { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ { 0x20a0, 0x20a0 }, /* EcuSign ₠ EURO-CURRENCY SIGN */ { 0x20a1, 0x20a1 }, /* ColonSign ₡ COLON SIGN */ { 0x20a2, 0x20a2 }, /* CruzeiroSign ₢ CRUZEIRO SIGN */ { 0x20a3, 0x20a3 }, /* FFrancSign ₣ FRENCH FRANC SIGN */ { 0x20a4, 0x20a4 }, /* LiraSign ₤ LIRA SIGN */ { 0x20a5, 0x20a5 }, /* MillSign ₥ MILL SIGN */ { 0x20a6, 0x20a6 }, /* NairaSign ₦ NAIRA SIGN */ { 0x20a7, 0x20a7 }, /* PesetaSign ₧ PESETA SIGN */ { 0x20a8, 0x20a8 }, /* RupeeSign ₨ RUPEE SIGN */ { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ { 0x20a9, 0x20a9 }, /* WonSign ₩ WON SIGN */ { 0x20aa, 0x20aa }, /* NewSheqelSign ₪ NEW SHEQEL SIGN */ { 0x20ab, 0x20ab }, /* DongSign ₫ DONG SIGN */ { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ { 0x08dd, 0x222a }, /* union ∪ UNION */ { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ { 0x08c8, 0x2245 }, /* approximate ≅ APPROXIMATELY EQUAL TO */ { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ { 0x0bfc, 0x22a2 }, /* righttack ⊢ RIGHT TACK */ { 0x0bdc, 0x22a3 }, /* lefttack ⊣ LEFT TACK */ { 0x0bc2, 0x22a4 }, /* downtack ⊤ DOWN TACK */ { 0x0bce, 0x22a5 }, /* uptack ⊥ UP TACK */ { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD (Unicode 3.0) */ { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ { 0x09df, 0x2422 }, /* blank ␢ BLANK SYMBOL */ { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ { 0x0adf, 0x25a0 }, /* emfilledrect ■ BLACK SQUARE */ { 0x0acf, 0x25a1 }, /* emopenrectangle □ WHITE SQUARE */ { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ }; static const int clutter_unicode_to_keysym_tab_size = G_N_ELEMENTS (clutter_unicode_to_keysym_tab); /** * clutter_unicode_to_keysym: * @wc: a ISO10646 encoded character * * Convert from a ISO10646 character to a key symbol. * * Return value: the corresponding Clutter key symbol, if one exists. * or, if there is no corresponding symbol, wc | 0x01000000 * * Since: 1.10 */ guint clutter_unicode_to_keysym (guint32 wc) { int min = 0; int max = clutter_unicode_to_keysym_tab_size - 1; int mid; /* First check for Latin-1 characters (1:1 mapping) */ if ((wc >= 0x0020 && wc <= 0x007e) || (wc >= 0x00a0 && wc <= 0x00ff)) return wc; /* Binary search in table */ while (max >= min) { mid = (min + max) / 2; if (clutter_unicode_to_keysym_tab[mid].ucs < wc) min = mid + 1; else if (clutter_unicode_to_keysym_tab[mid].ucs > wc) max = mid - 1; else { /* found it */ return clutter_unicode_to_keysym_tab[mid].keysym; } } /* No matching keysym value found, return Unicode value plus 0x01000000 * (a convention introduced in the UTF-8 work on xterm). */ return wc | 0x01000000; } muffin-6.4.1/clutter/clutter/clutter-seat.h0000664000175000017500000001475214723361714017704 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef CLUTTER_SEAT_H #define CLUTTER_SEAT_H #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include "clutter/clutter-types.h" #include "clutter/clutter-keymap.h" #include "clutter/clutter-virtual-input-device.h" #define CLUTTER_TYPE_SEAT (clutter_seat_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterSeat, clutter_seat, CLUTTER, SEAT, GObject) /** * ClutterKbdA11ySettings: * * The #ClutterKbdA11ySettings structure contains keyboard accessibility * settings * */ typedef struct _ClutterKbdA11ySettings { ClutterKeyboardA11yFlags controls; gint slowkeys_delay; gint debounce_delay; gint timeout_delay; gint mousekeys_init_delay; gint mousekeys_max_speed; gint mousekeys_accel_time; } ClutterKbdA11ySettings; /** * ClutterPointerA11ySettings: * * The #ClutterPointerA11ySettings structure contains pointer accessibility * settings * */ typedef struct _ClutterPointerA11ySettings { ClutterPointerA11yFlags controls; ClutterPointerA11yDwellClickType dwell_click_type; ClutterPointerA11yDwellMode dwell_mode; ClutterPointerA11yDwellDirection dwell_gesture_single; ClutterPointerA11yDwellDirection dwell_gesture_double; ClutterPointerA11yDwellDirection dwell_gesture_drag; ClutterPointerA11yDwellDirection dwell_gesture_secondary; gint secondary_click_delay; gint dwell_delay; gint dwell_threshold; } ClutterPointerA11ySettings; /** * ClutterVirtualDeviceType: */ typedef enum { CLUTTER_VIRTUAL_DEVICE_TYPE_NONE = 0, CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD = 1 << 0, CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER = 1 << 1, CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN = 1 << 2, } ClutterVirtualDeviceType; typedef struct _ClutterSeatClass ClutterSeatClass; struct _ClutterSeatClass { GObjectClass parent_class; ClutterInputDevice * (* get_pointer) (ClutterSeat *seat); ClutterInputDevice * (* get_keyboard) (ClutterSeat *seat); GList * (* list_devices) (ClutterSeat *seat); void (* bell_notify) (ClutterSeat *seat); ClutterKeymap * (* get_keymap) (ClutterSeat *seat); void (* compress_motion) (ClutterSeat *seat, ClutterEvent *event, const ClutterEvent *to_discard); gboolean (* handle_device_event) (ClutterSeat *seat, ClutterEvent *event); void (* warp_pointer) (ClutterSeat *seat, int x, int y); /* Event platform data */ void (* copy_event_data) (ClutterSeat *seat, const ClutterEvent *src, ClutterEvent *dest); void (* free_event_data) (ClutterSeat *seat, ClutterEvent *event); /* Keyboard accessibility */ void (* apply_kbd_a11y_settings) (ClutterSeat *seat, ClutterKbdA11ySettings *settings); /* Virtual devices */ ClutterVirtualInputDevice * (* create_virtual_device) (ClutterSeat *seat, ClutterInputDeviceType device_type); ClutterVirtualDeviceType (* get_supported_virtual_device_types) (ClutterSeat *seat); }; CLUTTER_EXPORT ClutterInputDevice * clutter_seat_get_pointer (ClutterSeat *seat); CLUTTER_EXPORT ClutterInputDevice * clutter_seat_get_keyboard (ClutterSeat *seat); CLUTTER_EXPORT GList * clutter_seat_list_devices (ClutterSeat *seat); CLUTTER_EXPORT void clutter_seat_bell_notify (ClutterSeat *seat); CLUTTER_EXPORT ClutterKeymap * clutter_seat_get_keymap (ClutterSeat *seat); CLUTTER_EXPORT void clutter_seat_set_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *settings); CLUTTER_EXPORT void clutter_seat_get_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *settings); CLUTTER_EXPORT void clutter_seat_ensure_a11y_state (ClutterSeat *seat); CLUTTER_EXPORT void clutter_seat_set_pointer_a11y_settings (ClutterSeat *seat, ClutterPointerA11ySettings *settings); CLUTTER_EXPORT void clutter_seat_get_pointer_a11y_settings (ClutterSeat *seat, ClutterPointerA11ySettings *settings); CLUTTER_EXPORT void clutter_seat_set_pointer_a11y_dwell_click_type (ClutterSeat *seat, ClutterPointerA11yDwellClickType click_type); CLUTTER_EXPORT void clutter_seat_inhibit_unfocus (ClutterSeat *seat); CLUTTER_EXPORT void clutter_seat_uninhibit_unfocus (ClutterSeat *seat); CLUTTER_EXPORT gboolean clutter_seat_is_unfocus_inhibited (ClutterSeat *seat); CLUTTER_EXPORT ClutterVirtualInputDevice *clutter_seat_create_virtual_device (ClutterSeat *seat, ClutterInputDeviceType device_type); CLUTTER_EXPORT ClutterVirtualDeviceType clutter_seat_get_supported_virtual_device_types (ClutterSeat *seat); void clutter_seat_compress_motion (ClutterSeat *seat, ClutterEvent *event, const ClutterEvent *to_discard); gboolean clutter_seat_handle_device_event (ClutterSeat *seat, ClutterEvent *event); CLUTTER_EXPORT void clutter_seat_warp_pointer (ClutterSeat *seat, int x, int y); CLUTTER_EXPORT gboolean clutter_seat_get_touch_mode (ClutterSeat *seat); #endif /* CLUTTER_SEAT_H */ muffin-6.4.1/clutter/clutter/clutter-feature.h0000664000175000017500000000243014723361714020371 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_FEATURE_H__ #define __CLUTTER_FEATURE_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS CLUTTER_EXPORT gboolean clutter_feature_available (ClutterFeatureFlags feature); CLUTTER_EXPORT ClutterFeatureFlags clutter_feature_get_all (void); G_END_DECLS #endif /* __CLUTTER_FEATURE_H__ */ muffin-6.4.1/clutter/clutter/clutter-container.c0000664000175000017500000011530314723361714020717 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * * * ClutterContainer: Generic actor container interface. * Author: Emmanuele Bassi */ #include "clutter-build-config.h" #include #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "clutter-actor-private.h" #include "clutter-child-meta.h" #include "clutter-container-private.h" #include "clutter-debug.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-enum-types.h" #define CLUTTER_CONTAINER_WARN_NOT_IMPLEMENTED(container,vfunc) \ G_STMT_START { \ g_warning ("Container of type '%s' does not implement " \ "the required ClutterContainer::%s virtual " \ "function.", \ G_OBJECT_TYPE_NAME ((container)), \ (vfunc)); \ } G_STMT_END #define CLUTTER_CONTAINER_NOTE_NOT_IMPLEMENTED(container,vfunc) \ G_STMT_START { \ CLUTTER_NOTE (ACTOR, "Container of type '%s' does not " \ "implement the ClutterContainer::%s " \ "virtual function.", \ G_OBJECT_TYPE_NAME ((container)), \ (vfunc)); \ } G_STMT_END /** * SECTION:clutter-container * @short_description: An interface for container actors * * #ClutterContainer is an interface implemented by #ClutterActor, and * it provides some common API for notifying when a child actor is added * or removed, as well as the infrastructure for accessing child properties * through #ClutterChildMeta. * * Until Clutter 1.10, the #ClutterContainer interface was also the public * API for implementing container actors; this part of the interface has * been deprecated: #ClutterContainer has a default implementation which * defers to #ClutterActor the child addition and removal, as well as the * iteration. See the documentation of #ClutterContainerIface for the list * of virtual functions that should be overridden. */ enum { ACTOR_ADDED, ACTOR_REMOVED, CHILD_NOTIFY, LAST_SIGNAL }; static guint container_signals[LAST_SIGNAL] = { 0, }; static GQuark quark_child_meta = 0; static ClutterChildMeta *get_child_meta (ClutterContainer *container, ClutterActor *actor); static void create_child_meta (ClutterContainer *container, ClutterActor *actor); static void destroy_child_meta (ClutterContainer *container, ClutterActor *actor); static void child_notify (ClutterContainer *container, ClutterActor *child, GParamSpec *pspec); typedef ClutterContainerIface ClutterContainerInterface; G_DEFINE_INTERFACE (ClutterContainer, clutter_container, G_TYPE_OBJECT); static void container_real_add (ClutterContainer *container, ClutterActor *actor) { clutter_actor_add_child (CLUTTER_ACTOR (container), actor); } static void container_real_remove (ClutterContainer *container, ClutterActor *actor) { clutter_actor_remove_child (CLUTTER_ACTOR (container), actor); } static void container_real_raise (ClutterContainer *container, ClutterActor *child, ClutterActor *sibling) { ClutterActor *self = CLUTTER_ACTOR (container); clutter_actor_set_child_above_sibling (self, child, sibling); } static void container_real_lower (ClutterContainer *container, ClutterActor *child, ClutterActor *sibling) { ClutterActor *self = CLUTTER_ACTOR (container); clutter_actor_set_child_below_sibling (self, child, sibling); } static void container_real_sort_depth_order (ClutterContainer *container) { } static void clutter_container_default_init (ClutterContainerInterface *iface) { GType iface_type = G_TYPE_FROM_INTERFACE (iface); quark_child_meta = g_quark_from_static_string ("clutter-container-child-data"); /** * ClutterContainer::actor-added: * @container: the actor which received the signal * @actor: the new child that has been added to @container * * The ::actor-added signal is emitted each time an actor * has been added to @container. * * Since: 0.4 */ container_signals[ACTOR_ADDED] = g_signal_new (I_("actor-added"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContainerIface, actor_added), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterContainer::actor-removed: * @container: the actor which received the signal * @actor: the child that has been removed from @container * * The ::actor-removed signal is emitted each time an actor * is removed from @container. * * Since: 0.4 */ container_signals[ACTOR_REMOVED] = g_signal_new (I_("actor-removed"), iface_type, G_SIGNAL_RUN_FIRST, G_STRUCT_OFFSET (ClutterContainerIface, actor_removed), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterContainer::child-notify: * @container: the container which received the signal * @actor: the child that has had a property set * @pspec: (type GParamSpec): the #GParamSpec of the property set * * The ::child-notify signal is emitted each time a property is * being set through the clutter_container_child_set() and * clutter_container_child_set_property() calls. * * Since: 0.8 */ container_signals[CHILD_NOTIFY] = g_signal_new (I_("child-notify"), iface_type, G_SIGNAL_RUN_FIRST | G_SIGNAL_DETAILED, G_STRUCT_OFFSET (ClutterContainerIface, child_notify), NULL, NULL, _clutter_marshal_VOID__OBJECT_PARAM, G_TYPE_NONE, 2, CLUTTER_TYPE_ACTOR, G_TYPE_PARAM); iface->add = container_real_add; iface->remove = container_real_remove; iface->raise = container_real_raise; iface->lower = container_real_lower; iface->sort_depth_order = container_real_sort_depth_order; iface->child_meta_type = G_TYPE_INVALID; iface->create_child_meta = create_child_meta; iface->destroy_child_meta = destroy_child_meta; iface->get_child_meta = get_child_meta; iface->child_notify = child_notify; } static inline void container_add_actor (ClutterContainer *container, ClutterActor *actor) { ClutterActor *parent; parent = clutter_actor_get_parent (actor); if (G_UNLIKELY (parent != NULL)) { g_warning ("Attempting to add actor of type '%s' to a " "container of type '%s', but the actor has " "already a parent of type '%s'.", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container)), g_type_name (G_OBJECT_TYPE (parent))); return; } clutter_container_create_child_meta (container, actor); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (_clutter_diagnostic_enabled ())) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->add != container_real_add) _clutter_diagnostic_message ("The ClutterContainer::add() virtual " "function has been deprecated and it " "should not be overridden by newly " "written code"); } #endif /* CLUTTER_ENABLE_DEBUG */ CLUTTER_CONTAINER_GET_IFACE (container)->add (container, actor); } static inline void container_remove_actor (ClutterContainer *container, ClutterActor *actor) { ClutterActor *parent; parent = clutter_actor_get_parent (actor); if (parent != CLUTTER_ACTOR (container)) { g_warning ("Attempting to remove actor of type '%s' from " "group of class '%s', but the container is not " "the actor's parent.", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } clutter_container_destroy_child_meta (container, actor); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (_clutter_diagnostic_enabled ())) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->remove != container_real_remove) _clutter_diagnostic_message ("The ClutterContainer::remove() virtual " "function has been deprecated and it " "should not be overridden by newly " "written code"); } #endif /* CLUTTER_ENABLE_DEBUG */ CLUTTER_CONTAINER_GET_IFACE (container)->remove (container, actor); } static inline void container_add_valist (ClutterContainer *container, ClutterActor *first_actor, va_list args) { ClutterActor *actor = first_actor; while (actor != NULL) { container_add_actor (container, actor); actor = va_arg (args, ClutterActor *); } } static inline void container_remove_valist (ClutterContainer *container, ClutterActor *first_actor, va_list args) { ClutterActor *actor = first_actor; while (actor != NULL) { container_remove_actor (container, actor); actor = va_arg (args, ClutterActor *); } } /** * clutter_container_add: (skip) * @container: a #ClutterContainer * @first_actor: the first #ClutterActor to add * @...: %NULL terminated list of actors to add * * Adds a list of #ClutterActors to @container. Each time and * actor is added, the "actor-added" signal is emitted. Each actor should * be parented to @container, which takes a reference on the actor. You * cannot add a #ClutterActor to more than one #ClutterContainer. * * This function will call #ClutterContainerIface.add(), which is a * deprecated virtual function. The default implementation will * call clutter_actor_add_child(). * * Since: 0.4 * * Deprecated: 1.10: Use clutter_actor_add_child() instead. */ void clutter_container_add (ClutterContainer *container, ClutterActor *first_actor, ...) { va_list args; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (first_actor)); va_start (args, first_actor); container_add_valist (container, first_actor, args); va_end (args); } /** * clutter_container_add_actor: (virtual add) * @container: a #ClutterContainer * @actor: the first #ClutterActor to add * * Adds a #ClutterActor to @container. This function will emit the * "actor-added" signal. The actor should be parented to * @container. You cannot add a #ClutterActor to more than one * #ClutterContainer. * * This function will call #ClutterContainerIface.add(), which is a * deprecated virtual function. The default implementation will * call clutter_actor_add_child(). * * Since: 0.4 * * Deprecated: 1.10: Use clutter_actor_add_child() instead. */ void clutter_container_add_actor (ClutterContainer *container, ClutterActor *actor) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); container_add_actor (container, actor); } /** * clutter_container_remove: (skip) * @container: a #ClutterContainer * @first_actor: first #ClutterActor to remove * @...: a %NULL-terminated list of actors to remove * * Removes a %NULL terminated list of #ClutterActors from * @container. Each actor should be unparented, so if you want to keep it * around you must hold a reference to it yourself, using g_object_ref(). * Each time an actor is removed, the "actor-removed" signal is * emitted by @container. * * This function will call #ClutterContainerIface.remove(), which is a * deprecated virtual function. The default implementation will call * clutter_actor_remove_child(). * * Since: 0.4 * * Deprecated: 1.10: Use clutter_actor_remove_child() instead. */ void clutter_container_remove (ClutterContainer *container, ClutterActor *first_actor, ...) { va_list var_args; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (first_actor)); va_start (var_args, first_actor); container_remove_valist (container, first_actor, var_args); va_end (var_args); } /** * clutter_container_remove_actor: (virtual remove) * @container: a #ClutterContainer * @actor: a #ClutterActor * * Removes @actor from @container. The actor should be unparented, so * if you want to keep it around you must hold a reference to it * yourself, using g_object_ref(). When the actor has been removed, * the "actor-removed" signal is emitted by @container. * * This function will call #ClutterContainerIface.remove(), which is a * deprecated virtual function. The default implementation will call * clutter_actor_remove_child(). * * Since: 0.4 * * Deprecated: 1.10: Use clutter_actor_remove_child() instead. */ void clutter_container_remove_actor (ClutterContainer *container, ClutterActor *actor) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); container_remove_actor (container, actor); } /** * clutter_container_get_children: * @container: a #ClutterContainer * * Retrieves all the children of @container. * * Return value: (element-type Clutter.Actor) (transfer container): a list * of #ClutterActors. Use g_list_free() on the returned * list when done. * * Since: 0.4 * * Deprecated: 1.10: Use clutter_actor_get_children() instead. */ GList * clutter_container_get_children (ClutterContainer *container) { g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); return clutter_actor_get_children (CLUTTER_ACTOR (container)); } /** * clutter_container_raise_child: (virtual raise) * @container: a #ClutterContainer * @actor: the actor to raise * @sibling: (allow-none): the sibling to raise to, or %NULL to raise * to the top * * Raises @actor to @sibling level, in the depth ordering. * * This function calls the #ClutterContainerIface.raise() virtual function, * which has been deprecated. The default implementation will call * clutter_actor_set_child_above_sibling(). * * Since: 0.6 * * Deprecated: 1.10: Use clutter_actor_set_child_above_sibling() instead. */ void clutter_container_raise_child (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { ClutterContainerIface *iface; ClutterActor *self; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); if (actor == sibling) return; self = CLUTTER_ACTOR (container); if (clutter_actor_get_parent (actor) != self) { g_warning ("Actor of type '%s' is not a child of the container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } if (sibling != NULL && clutter_actor_get_parent (sibling) != self) { g_warning ("Actor of type '%s' is not a child of the container " "of type '%s'", g_type_name (G_OBJECT_TYPE (sibling)), g_type_name (G_OBJECT_TYPE (container))); return; } iface = CLUTTER_CONTAINER_GET_IFACE (container); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (_clutter_diagnostic_enabled ())) { if (iface->raise != container_real_raise) _clutter_diagnostic_message ("The ClutterContainer::raise() " "virtual function has been deprecated " "and it should not be overridden by " "newly written code"); } #endif /* CLUTTER_ENABLE_DEBUG */ iface->raise (container, actor, sibling); } /** * clutter_container_lower_child: (virtual lower) * @container: a #ClutterContainer * @actor: the actor to raise * @sibling: (allow-none): the sibling to lower to, or %NULL to lower * to the bottom * * Lowers @actor to @sibling level, in the depth ordering. * * This function calls the #ClutterContainerIface.lower() virtual function, * which has been deprecated. The default implementation will call * clutter_actor_set_child_below_sibling(). * * Since: 0.6 * * Deprecated: 1.10: Use clutter_actor_set_child_below_sibling() instead. */ void clutter_container_lower_child (ClutterContainer *container, ClutterActor *actor, ClutterActor *sibling) { ClutterContainerIface *iface; ClutterActor *self; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (sibling == NULL || CLUTTER_IS_ACTOR (sibling)); if (actor == sibling) return; self = CLUTTER_ACTOR (container); if (clutter_actor_get_parent (actor) != self) { g_warning ("Actor of type '%s' is not a child of the container " "of type '%s'", g_type_name (G_OBJECT_TYPE (actor)), g_type_name (G_OBJECT_TYPE (container))); return; } if (sibling != NULL&& clutter_actor_get_parent (sibling) != self) { g_warning ("Actor of type '%s' is not a child of the container " "of type '%s'", g_type_name (G_OBJECT_TYPE (sibling)), g_type_name (G_OBJECT_TYPE (container))); return; } iface = CLUTTER_CONTAINER_GET_IFACE (container); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (_clutter_diagnostic_enabled ())) { if (iface->lower != container_real_lower) _clutter_diagnostic_message ("The ClutterContainer::lower() " "virtual function has been deprecated " "and it should not be overridden by " "newly written code"); } #endif /* CLUTTER_ENABLE_DEBUG */ iface->lower (container, actor, sibling); } /** * clutter_container_sort_depth_order: * @container: a #ClutterContainer * * Sorts a container's children using their depth. This function should not * be normally used by applications. * * Since: 0.6 * * Deprecated: 1.10: The #ClutterContainerIface.sort_depth_order() virtual * function should not be used any more; the default implementation in * #ClutterContainer does not do anything. */ void clutter_container_sort_depth_order (ClutterContainer *container) { ClutterContainerIface *iface; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); iface = CLUTTER_CONTAINER_GET_IFACE (container); #ifdef CLUTTER_ENABLE_DEBUG if (G_UNLIKELY (_clutter_diagnostic_enabled ())) { if (iface->sort_depth_order != container_real_sort_depth_order) _clutter_diagnostic_message ("The ClutterContainer::sort_depth_order() " "virtual function has been deprecated " "and it should not be overridden by " "newly written code"); } #endif /* CLUTTER_ENABLE_DEBUG */ iface->sort_depth_order (container); } /** * clutter_container_find_child_by_name: * @container: a #ClutterContainer * @child_name: the name of the requested child. * * Finds a child actor of a container by its name. Search recurses * into any child container. * * Return value: (transfer none): The child actor with the requested name, * or %NULL if no actor with that name was found. * * Since: 0.6 */ ClutterActor * clutter_container_find_child_by_name (ClutterContainer *container, const gchar *child_name) { GList *children; GList *iter; ClutterActor *actor = NULL; g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); g_return_val_if_fail (child_name != NULL, NULL); children = clutter_container_get_children (container); for (iter = children; iter; iter = g_list_next (iter)) { ClutterActor *a; const gchar *iter_name; a = CLUTTER_ACTOR (iter->data); iter_name = clutter_actor_get_name (a); if (iter_name && !strcmp (iter_name, child_name)) { actor = a; break; } if (CLUTTER_IS_CONTAINER (a)) { ClutterContainer *c = CLUTTER_CONTAINER (a); actor = clutter_container_find_child_by_name (c, child_name); if (actor) break; } } g_list_free (children); return actor; } static ClutterChildMeta * get_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); ClutterChildMeta *meta; if (iface->child_meta_type == G_TYPE_INVALID) return NULL; meta = g_object_get_qdata (G_OBJECT (actor), quark_child_meta); if (meta != NULL && meta->actor == actor) return meta; return NULL; } static void create_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); ClutterChildMeta *child_meta = NULL; if (iface->child_meta_type == G_TYPE_INVALID) return; if (!g_type_is_a (iface->child_meta_type, CLUTTER_TYPE_CHILD_META)) { g_warning ("%s: Child data of type '%s' is not a ClutterChildMeta", G_STRLOC, g_type_name (iface->child_meta_type)); return; } child_meta = g_object_new (iface->child_meta_type, "container", container, "actor", actor, NULL); g_object_set_qdata_full (G_OBJECT (actor), quark_child_meta, child_meta, (GDestroyNotify) g_object_unref); } static void destroy_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->child_meta_type == G_TYPE_INVALID) return; g_object_set_qdata (G_OBJECT (actor), quark_child_meta, NULL); } /** * clutter_container_get_child_meta: * @container: a #ClutterContainer * @actor: a #ClutterActor that is a child of @container. * * Retrieves the #ClutterChildMeta which contains the data about the * @container specific state for @actor. * * Return value: (transfer none): the #ClutterChildMeta for the @actor child * of @container or %NULL if the specifiec actor does not exist or the * container is not configured to provide #ClutterChildMetas * * Since: 0.8 */ ClutterChildMeta * clutter_container_get_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->child_meta_type == G_TYPE_INVALID) return NULL; if (G_LIKELY (iface->get_child_meta)) return iface->get_child_meta (container, actor); return NULL; } /** * clutter_container_create_child_meta: * @container: a #ClutterContainer * @actor: a #ClutterActor * * Creates the #ClutterChildMeta wrapping @actor inside the * @container, if the #ClutterContainerIface::child_meta_type * class member is not set to %G_TYPE_INVALID. * * This function is only useful when adding a #ClutterActor to * a #ClutterContainer implementation outside of the * #ClutterContainer::add() virtual function implementation. * * Applications should not call this function. * * Since: 1.2 */ void clutter_container_create_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->child_meta_type == G_TYPE_INVALID) return; g_assert (g_type_is_a (iface->child_meta_type, CLUTTER_TYPE_CHILD_META)); if (G_LIKELY (iface->create_child_meta)) iface->create_child_meta (container, actor); } /** * clutter_container_destroy_child_meta: * @container: a #ClutterContainer * @actor: a #ClutterActor * * Destroys the #ClutterChildMeta wrapping @actor inside the * @container, if any. * * This function is only useful when removing a #ClutterActor to * a #ClutterContainer implementation outside of the * #ClutterContainer::add() virtual function implementation. * * Applications should not call this function. * * Since: 1.2 */ void clutter_container_destroy_child_meta (ClutterContainer *container, ClutterActor *actor) { ClutterContainerIface *iface; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); iface = CLUTTER_CONTAINER_GET_IFACE (container); if (iface->child_meta_type == G_TYPE_INVALID) return; if (G_LIKELY (iface->destroy_child_meta)) iface->destroy_child_meta (container, actor); } /** * clutter_container_class_find_child_property: * @klass: a #GObjectClass implementing the #ClutterContainer interface. * @property_name: a property name. * * Looks up the #GParamSpec for a child property of @klass. * * Return value: (transfer none): The #GParamSpec for the property or %NULL * if no such property exist. * * Since: 0.8 */ GParamSpec * clutter_container_class_find_child_property (GObjectClass *klass, const gchar *property_name) { ClutterContainerIface *iface; GObjectClass *child_class; GParamSpec *pspec; g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); g_return_val_if_fail (property_name != NULL, NULL); g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_CLASS (klass), CLUTTER_TYPE_CONTAINER), NULL); iface = g_type_interface_peek (klass, CLUTTER_TYPE_CONTAINER); g_return_val_if_fail (iface != NULL, NULL); if (iface->child_meta_type == G_TYPE_INVALID) return NULL; child_class = g_type_class_ref (iface->child_meta_type); pspec = g_object_class_find_property (child_class, property_name); g_type_class_unref (child_class); return pspec; } /** * clutter_container_class_list_child_properties: * @klass: a #GObjectClass implementing the #ClutterContainer interface. * @n_properties: return location for length of returned array. * * Returns an array of #GParamSpec for all child properties. * * Return value: (array length=n_properties) (transfer full): an array * of #GParamSpecs which should be freed after use. * * Since: 0.8 */ GParamSpec ** clutter_container_class_list_child_properties (GObjectClass *klass, guint *n_properties) { ClutterContainerIface *iface; GObjectClass *child_class; GParamSpec **retval; g_return_val_if_fail (G_IS_OBJECT_CLASS (klass), NULL); g_return_val_if_fail (g_type_is_a (G_TYPE_FROM_CLASS (klass), CLUTTER_TYPE_CONTAINER), NULL); iface = g_type_interface_peek (klass, CLUTTER_TYPE_CONTAINER); g_return_val_if_fail (iface != NULL, NULL); if (iface->child_meta_type == G_TYPE_INVALID) return NULL; child_class = g_type_class_ref (iface->child_meta_type); retval = g_object_class_list_properties (child_class, n_properties); g_type_class_unref (child_class); return retval; } static void child_notify (ClutterContainer *container, ClutterActor *actor, GParamSpec *pspec) { } static inline void container_set_child_property (ClutterContainer *container, ClutterActor *actor, const GValue *value, GParamSpec *pspec) { ClutterChildMeta *data; data = clutter_container_get_child_meta (container, actor); g_object_set_property (G_OBJECT (data), pspec->name, value); g_signal_emit (container, container_signals[CHILD_NOTIFY], (pspec->flags & G_PARAM_STATIC_NAME) ? g_quark_from_static_string (pspec->name) : g_quark_from_string (pspec->name), actor, pspec); } /** * clutter_container_child_set_property: * @container: a #ClutterContainer * @child: a #ClutterActor that is a child of @container. * @property: the name of the property to set. * @value: the value. * * Sets a container-specific property on a child of @container. * * Since: 0.8 */ void clutter_container_child_set_property (ClutterContainer *container, ClutterActor *child, const gchar *property, const GValue *value) { GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (property != NULL); g_return_if_fail (value != NULL); klass = G_OBJECT_GET_CLASS (container); pspec = clutter_container_class_find_child_property (klass, property); if (!pspec) { g_warning ("%s: Containers of type '%s' have no child " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (container), property); return; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Child property '%s' of the container '%s' " "is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); return; } container_set_child_property (container, child, value, pspec); } /** * clutter_container_child_set: * @container: a #ClutterContainer * @actor: a #ClutterActor that is a child of @container. * @first_prop: name of the first property to be set. * @...: value for the first property, followed optionally by more name/value * pairs terminated with NULL. * * Sets container specific properties on the child of a container. * * Since: 0.8 */ void clutter_container_child_set (ClutterContainer *container, ClutterActor *actor, const gchar *first_prop, ...) { GObjectClass *klass; const gchar *name; va_list var_args; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); klass = G_OBJECT_GET_CLASS (container); va_start (var_args, first_prop); name = first_prop; while (name) { GValue value = G_VALUE_INIT; gchar *error = NULL; GParamSpec *pspec; pspec = clutter_container_class_find_child_property (klass, name); if (!pspec) { g_warning ("%s: Containers of type '%s' have no child " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (container), name); break; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Child property '%s' of the container '%s' " "is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); break; } G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); if (error) { /* we intentionally leak the GValue because it might * be in an undefined state and calling g_value_unset() * on it might crash */ g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } container_set_child_property (container, actor, &value, pspec); g_value_unset (&value); name = va_arg (var_args, gchar*); } va_end (var_args); } static inline void container_get_child_property (ClutterContainer *container, ClutterActor *actor, GValue *value, GParamSpec *pspec) { ClutterChildMeta *data; data = clutter_container_get_child_meta (container, actor); g_object_get_property (G_OBJECT (data), pspec->name, value); } /** * clutter_container_child_get_property: * @container: a #ClutterContainer * @child: a #ClutterActor that is a child of @container. * @property: the name of the property to set. * @value: the value. * * Gets a container specific property of a child of @container, In general, * a copy is made of the property contents and the caller is responsible for * freeing the memory by calling g_value_unset(). * * Note that clutter_container_child_set_property() is really intended for * language bindings, clutter_container_child_set() is much more convenient * for C programming. * * Since: 0.8 */ void clutter_container_child_get_property (ClutterContainer *container, ClutterActor *child, const gchar *property, GValue *value) { GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (property != NULL); g_return_if_fail (value != NULL); klass = G_OBJECT_GET_CLASS (container); pspec = clutter_container_class_find_child_property (klass, property); if (!pspec) { g_warning ("%s: Containers of type '%s' have no child " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (container), property); return; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("%s: Child property '%s' of the container '%s' " "is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); return; } container_get_child_property (container, child, value, pspec); } /** * clutter_container_child_get: * @container: a #ClutterContainer * @actor: a #ClutterActor that is a child of @container. * @first_prop: name of the first property to be set. * @...: value for the first property, followed optionally by more name/value * pairs terminated with NULL. * * Gets @container specific properties of an actor. * * In general, a copy is made of the property contents and the caller is * responsible for freeing the memory in the appropriate manner for the type, for * instance by calling g_free() or g_object_unref(). * * Since: 0.8 */ void clutter_container_child_get (ClutterContainer *container, ClutterActor *actor, const gchar *first_prop, ...) { GObjectClass *klass; const gchar *name; va_list var_args; g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); klass = G_OBJECT_GET_CLASS (container); va_start (var_args, first_prop); name = first_prop; while (name) { GValue value = G_VALUE_INIT; gchar *error = NULL; GParamSpec *pspec; pspec = clutter_container_class_find_child_property (klass, name); if (!pspec) { g_warning ("%s: container '%s' has no child property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (container), name); break; } if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("%s: child property '%s' of container '%s' is not readable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (container)); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); container_get_child_property (container, actor, &value, pspec); G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); name = va_arg (var_args, gchar*); } va_end (var_args); } /** * clutter_container_child_notify: * @container: a #ClutterContainer * @child: a #ClutterActor * @pspec: a #GParamSpec * * Calls the #ClutterContainerIface.child_notify() virtual function * of #ClutterContainer. The default implementation will emit the * #ClutterContainer::child-notify signal. * * Since: 1.6 */ void clutter_container_child_notify (ClutterContainer *container, ClutterActor *child, GParamSpec *pspec) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (child)); g_return_if_fail (pspec != NULL); g_return_if_fail (clutter_actor_get_parent (child) == CLUTTER_ACTOR (container)); CLUTTER_CONTAINER_GET_IFACE (container)->child_notify (container, child, pspec); } void _clutter_container_emit_actor_added (ClutterContainer *container, ClutterActor *actor) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_signal_emit (container, container_signals[ACTOR_ADDED], 0, actor); } void _clutter_container_emit_actor_removed (ClutterContainer *container, ClutterActor *actor) { g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_signal_emit (container, container_signals[ACTOR_REMOVED], 0, actor); } muffin-6.4.1/clutter/clutter/clutter-brightness-contrast-effect.h0000664000175000017500000001117114723361714024175 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010-2012 Inclusive Design Research Centre, OCAD University. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Joseph Scheuhammer */ #ifndef __CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_H__ #define __CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT (clutter_brightness_contrast_effect_get_type ()) #define CLUTTER_BRIGHTNESS_CONTRAST_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT, ClutterBrightnessContrastEffect)) #define CLUTTER_IS_BRIGHTNESS_CONTRAST_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BRIGHTNESS_CONTRAST_EFFECT)) /** * ClutterBrightnessContrastEffect: * * #ClutterBrightnessContrastEffect is an opaque structure * whose members cannot be directly accessed * * Since: 1.10 */ typedef struct _ClutterBrightnessContrastEffect ClutterBrightnessContrastEffect; typedef struct _ClutterBrightnessContrastEffectClass ClutterBrightnessContrastEffectClass; CLUTTER_EXPORT GType clutter_brightness_contrast_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect * clutter_brightness_contrast_effect_new (void); CLUTTER_EXPORT void clutter_brightness_contrast_effect_set_brightness_full (ClutterBrightnessContrastEffect *effect, float red, float green, float blue); CLUTTER_EXPORT void clutter_brightness_contrast_effect_set_brightness (ClutterBrightnessContrastEffect *effect, float brightness); CLUTTER_EXPORT void clutter_brightness_contrast_effect_get_brightness (ClutterBrightnessContrastEffect *effect, float *red, float *green, float *blue); CLUTTER_EXPORT void clutter_brightness_contrast_effect_set_contrast_full (ClutterBrightnessContrastEffect *effect, float red, float green, float blue); CLUTTER_EXPORT void clutter_brightness_contrast_effect_set_contrast (ClutterBrightnessContrastEffect *effect, float contrast); CLUTTER_EXPORT void clutter_brightness_contrast_effect_get_contrast (ClutterBrightnessContrastEffect *effect, float *red, float *green, float *blue); G_END_DECLS #endif /* __CLUTTER_BRIGHTNESS_CONTRAST_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-canvas.c0000664000175000017500000004250614723361714020214 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-canvas * @Title: ClutterCanvas * @Short_Description: Content for 2D painting * @See_Also: #ClutterContent * * The #ClutterCanvas class is a #ClutterContent implementation that allows * drawing using the Cairo API on a 2D surface. * * In order to draw on a #ClutterCanvas, you should connect a handler to the * #ClutterCanvas::draw signal; the signal will receive a #cairo_t context * that can be used to draw. #ClutterCanvas will emit the #ClutterCanvas::draw * signal when invalidated using clutter_content_invalidate(). * * See [canvas.c](https://git.gnome.org/browse/clutter/tree/examples/canvas.c?h=clutter-1.18) * for an example of how to use #ClutterCanvas. * * #ClutterCanvas is available since Clutter 1.10. */ #include "clutter-build-config.h" #include #include #include #include "clutter-canvas.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-actor-private.h" #include "clutter-backend.h" #include "clutter-cairo.h" #include "clutter-color.h" #include "clutter-content-private.h" #include "clutter-debug.h" #include "clutter-marshal.h" #include "clutter-paint-node.h" #include "clutter-paint-nodes.h" #include "clutter-private.h" #include "clutter-settings.h" struct _ClutterCanvasPrivate { cairo_t *cr; int width; int height; float scale_factor; CoglTexture *texture; gboolean dirty; CoglBitmap *buffer; }; enum { PROP_0, PROP_WIDTH, PROP_HEIGHT, PROP_SCALE_FACTOR, LAST_PROP }; static GParamSpec *obj_props[LAST_PROP] = { NULL, }; enum { DRAW, LAST_SIGNAL }; static guint canvas_signals[LAST_SIGNAL] = { 0, }; static void clutter_content_iface_init (ClutterContentInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterCanvas, clutter_canvas, G_TYPE_OBJECT, G_ADD_PRIVATE (ClutterCanvas) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) static void clutter_cairo_context_draw_marshaller (GClosure *closure, GValue *return_value, guint n_param_values, const GValue *param_values, gpointer invocation_hint, gpointer marshal_data) { cairo_t *cr = g_value_get_boxed (¶m_values[1]); cairo_save (cr); _clutter_marshal_BOOLEAN__BOXED_INT_INT (closure, return_value, n_param_values, param_values, invocation_hint, marshal_data); cairo_restore (cr); } static void clutter_canvas_finalize (GObject *gobject) { ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv; if (priv->buffer != NULL) { cogl_object_unref (priv->buffer); priv->buffer = NULL; } g_clear_pointer (&priv->texture, cogl_object_unref); G_OBJECT_CLASS (clutter_canvas_parent_class)->finalize (gobject); } static void clutter_canvas_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv; switch (prop_id) { case PROP_WIDTH: { gint new_size = g_value_get_int (value); if (priv->width != new_size) { priv->width = new_size; clutter_content_invalidate (CLUTTER_CONTENT (gobject)); } } break; case PROP_HEIGHT: { gint new_size = g_value_get_int (value); if (priv->height != new_size) { priv->height = new_size; clutter_content_invalidate (CLUTTER_CONTENT (gobject)); } } break; case PROP_SCALE_FACTOR: { gfloat new_scale_factor = g_value_get_float (value); if (priv->scale_factor != new_scale_factor) { priv->scale_factor = new_scale_factor; clutter_content_invalidate (CLUTTER_CONTENT (gobject)); } } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_canvas_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterCanvasPrivate *priv = CLUTTER_CANVAS (gobject)->priv; switch (prop_id) { case PROP_WIDTH: g_value_set_int (value, priv->width); break; case PROP_HEIGHT: g_value_set_int (value, priv->height); break; case PROP_SCALE_FACTOR: g_value_set_float (value, priv->scale_factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_canvas_class_init (ClutterCanvasClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); /** * ClutterCanvas:width: * * The width of the canvas. * * Since: 1.10 */ obj_props[PROP_WIDTH] = g_param_spec_int ("width", P_("Width"), P_("The width of the canvas"), -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterCanvas:height: * * The height of the canvas. * * Since: 1.10 */ obj_props[PROP_HEIGHT] = g_param_spec_int ("height", P_("Height"), P_("The height of the canvas"), -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterCanvas:scale-factor: * * The height of the canvas. */ obj_props[PROP_SCALE_FACTOR] = g_param_spec_float ("scale-factor", P_("Scale Factor"), P_("The Scale factor of the canvas"), 0.01f, G_MAXFLOAT, 1.0f, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterCanvas::draw: * @canvas: the #ClutterCanvas that emitted the signal * @cr: the Cairo context used to draw * @width: the width of the @canvas * @height: the height of the @canvas * * The #ClutterCanvas::draw signal is emitted each time a canvas is * invalidated. * * It is safe to connect multiple handlers to this signal: each * handler invocation will be automatically protected by cairo_save() * and cairo_restore() pairs. * * Return value: %TRUE if the signal emission should stop, and * %FALSE otherwise * * Since: 1.10 */ canvas_signals[DRAW] = g_signal_new (I_("draw"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE, G_STRUCT_OFFSET (ClutterCanvasClass, draw), _clutter_boolean_handled_accumulator, NULL, clutter_cairo_context_draw_marshaller, G_TYPE_BOOLEAN, 3, CAIRO_GOBJECT_TYPE_CONTEXT, G_TYPE_INT, G_TYPE_INT); gobject_class->set_property = clutter_canvas_set_property; gobject_class->get_property = clutter_canvas_get_property; gobject_class->finalize = clutter_canvas_finalize; g_object_class_install_properties (gobject_class, LAST_PROP, obj_props); } static void clutter_canvas_init (ClutterCanvas *self) { self->priv = clutter_canvas_get_instance_private (self); self->priv->width = -1; self->priv->height = -1; self->priv->scale_factor = 1.0f; } static void clutter_canvas_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ClutterCanvas *self = CLUTTER_CANVAS (content); ClutterCanvasPrivate *priv = self->priv; ClutterPaintNode *node; if (priv->buffer == NULL) return; if (priv->dirty) g_clear_pointer (&priv->texture, cogl_object_unref); if (priv->texture == NULL) priv->texture = cogl_texture_new_from_bitmap (priv->buffer, COGL_TEXTURE_NO_SLICING, CLUTTER_CAIRO_FORMAT_ARGB32); if (priv->texture == NULL) return; node = clutter_actor_create_texture_paint_node (actor, priv->texture); clutter_paint_node_set_static_name (node, "Canvas Content"); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); priv->dirty = FALSE; } static void clutter_canvas_emit_draw (ClutterCanvas *self) { ClutterCanvasPrivate *priv = self->priv; int real_width, real_height; cairo_surface_t *surface; gboolean mapped_buffer; unsigned char *data; CoglBuffer *buffer; gboolean res; cairo_t *cr; g_assert (priv->height > 0 && priv->width > 0); priv->dirty = TRUE; real_width = ceilf (priv->width * priv->scale_factor); real_height = ceilf (priv->height * priv->scale_factor); CLUTTER_NOTE (MISC, "Creating Cairo surface with size %d x %d", priv->width, priv->height); if (priv->buffer == NULL) { CoglContext *ctx; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); priv->buffer = cogl_bitmap_new_with_size (ctx, real_width, real_height, CLUTTER_CAIRO_FORMAT_ARGB32); } buffer = COGL_BUFFER (cogl_bitmap_get_buffer (priv->buffer)); if (buffer == NULL) return; cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); data = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_READ_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); if (data != NULL) { int bitmap_stride = cogl_bitmap_get_rowstride (priv->buffer); surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, real_width, real_height, bitmap_stride); mapped_buffer = TRUE; } else { surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, real_width, real_height); mapped_buffer = FALSE; } cairo_surface_set_device_scale (surface, priv->scale_factor, priv->scale_factor); self->priv->cr = cr = cairo_create (surface); g_signal_emit (self, canvas_signals[DRAW], 0, cr, priv->width, priv->height, &res); #ifdef CLUTTER_ENABLE_DEBUG if (_clutter_diagnostic_enabled () && cairo_status (cr)) { g_warning ("Drawing failed for [%p]: %s", self, cairo_status_to_string (cairo_status (cr))); } #endif self->priv->cr = NULL; cairo_destroy (cr); if (mapped_buffer) cogl_buffer_unmap (buffer); else { int size = cairo_image_surface_get_stride (surface) * priv->height; cogl_buffer_set_data (buffer, 0, cairo_image_surface_get_data (surface), size); } cairo_surface_destroy (surface); } static void clutter_canvas_invalidate (ClutterContent *content) { ClutterCanvas *self = CLUTTER_CANVAS (content); ClutterCanvasPrivate *priv = self->priv; if (priv->buffer != NULL) { cogl_object_unref (priv->buffer); priv->buffer = NULL; } if (priv->width <= 0 || priv->height <= 0) return; clutter_canvas_emit_draw (self); } static gboolean clutter_canvas_get_preferred_size (ClutterContent *content, gfloat *width, gfloat *height) { ClutterCanvasPrivate *priv = CLUTTER_CANVAS (content)->priv; if (priv->width < 0 || priv->height < 0) return FALSE; if (width != NULL) *width = ceilf (priv->width * priv->scale_factor); if (height != NULL) *height = ceilf (priv->height * priv->scale_factor); return TRUE; } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->invalidate = clutter_canvas_invalidate; iface->paint_content = clutter_canvas_paint_content; iface->get_preferred_size = clutter_canvas_get_preferred_size; } /** * clutter_canvas_new: * * Creates a new instance of #ClutterCanvas. * * You should call clutter_canvas_set_size() to set the size of the canvas. * * You should call clutter_content_invalidate() every time you wish to * draw the contents of the canvas. * * Return value: (transfer full): The newly allocated instance of * #ClutterCanvas. Use g_object_unref() when done. * * Since: 1.10 */ ClutterContent * clutter_canvas_new (void) { return g_object_new (CLUTTER_TYPE_CANVAS, NULL); } static gboolean clutter_canvas_invalidate_internal (ClutterCanvas *canvas, int width, int height) { gboolean width_changed = FALSE, height_changed = FALSE; gboolean res = FALSE; GObject *obj; obj = G_OBJECT (canvas); g_object_freeze_notify (obj); if (canvas->priv->width != width) { canvas->priv->width = width; width_changed = TRUE; g_object_notify_by_pspec (obj, obj_props[PROP_WIDTH]); } if (canvas->priv->height != height) { canvas->priv->height = height; height_changed = TRUE; g_object_notify_by_pspec (obj, obj_props[PROP_HEIGHT]); } if (width_changed || height_changed) { clutter_content_invalidate (CLUTTER_CONTENT (canvas)); res = TRUE; } g_object_thaw_notify (obj); return res; } /** * clutter_canvas_set_size: * @canvas: a #ClutterCanvas * @width: the width of the canvas, in pixels * @height: the height of the canvas, in pixels * * Sets the size of the @canvas, and invalidates the content. * * This function will cause the @canvas to be invalidated only * if the size of the canvas surface has changed. * * If you want to invalidate the contents of the @canvas when setting * the size, you can use the return value of the function to conditionally * call clutter_content_invalidate(): * * |[ * if (!clutter_canvas_set_size (canvas, width, height)) * clutter_content_invalidate (CLUTTER_CONTENT (canvas)); * ]| * * Return value: this function returns %TRUE if the size change * caused a content invalidation, and %FALSE otherwise * * Since: 1.10 */ gboolean clutter_canvas_set_size (ClutterCanvas *canvas, int width, int height) { g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), FALSE); g_return_val_if_fail (width >= -1 && height >= -1, FALSE); return clutter_canvas_invalidate_internal (canvas, width, height); } /** * clutter_canvas_set_scale_factor: * @canvas: a #ClutterCanvas * @scale: the integer scaling factor of the canvas * * Sets the scaling factor of the @canvas, and invalidates the content. * * This function will cause the @canvas to be invalidated only * if the scale factor of the canvas surface has changed. */ void clutter_canvas_set_scale_factor (ClutterCanvas *canvas, float scale) { g_return_if_fail (CLUTTER_IS_CANVAS (canvas)); g_return_if_fail (scale > 0.0f); if (canvas->priv->scale_factor != scale) { canvas->priv->scale_factor = scale; g_object_freeze_notify (G_OBJECT (canvas)); clutter_content_invalidate (CLUTTER_CONTENT (canvas)); g_object_thaw_notify (G_OBJECT (canvas)); g_object_notify_by_pspec (G_OBJECT (canvas), obj_props[PROP_SCALE_FACTOR]); } } /** * clutter_canvas_get_scale_factor: * @canvas: a #ClutterCanvas * * Gets the scale factor of the @canvas. * * Return value: the current @canvas scale factor or -1 if invalid */ float clutter_canvas_get_scale_factor (ClutterCanvas *canvas) { g_return_val_if_fail (CLUTTER_IS_CANVAS (canvas), -1.0f); return canvas->priv->scale_factor; } muffin-6.4.1/clutter/clutter/clutter-main.c0000664000175000017500000023306414723361714017666 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-main * @short_description: Various 'global' Clutter functions. * * Functions to retrieve various global Clutter resources and other utility * functions for mainloops, events and threads * * ## The Clutter Threading Model * * Clutter is *thread-aware*: all operations performed by Clutter are assumed * to be under the Big Clutter Lock, which is created when the threading is * initialized through clutter_init(), and entered when calling user-related * code during event handling and actor drawing. * * The only safe and portable way to use the Clutter API in a multi-threaded * environment is to only access the Clutter API from a thread that did called * clutter_init() and clutter_main(). * * The common pattern for using threads with Clutter is to use worker threads * to perform blocking operations and then install idle or timeout sources with * the result when the thread finishes, and update the UI from those callbacks. * * For a working example of how to use a worker thread to update the UI, see * [threads.c](https://git.gnome.org/browse/clutter/tree/examples/threads.c?h=clutter-1.18) */ #include "clutter-build-config.h" #include #include "clutter-actor-private.h" #include "clutter-backend-private.h" #include "clutter-config.h" #include "clutter-debug.h" #include "clutter-event-private.h" #include "clutter-feature.h" #include "clutter-input-device-private.h" #include "clutter-input-pointer-a11y-private.h" #include "clutter-graphene.h" #include "clutter-main.h" #include "clutter-master-clock.h" #include "clutter-mutter.h" #include "clutter-paint-node-private.h" #include "clutter-private.h" #include "clutter-settings-private.h" #include "clutter-stage-manager.h" #include "clutter-stage-private.h" #ifdef CLUTTER_WINDOWING_X11 #include "x11/clutter-backend-x11.h" #endif #ifdef CLUTTER_WINDOWING_EGL #include "egl/clutter-backend-eglnative.h" #endif #include #include #include "cally/cally.h" /* For accessibility support */ /* main context */ static ClutterMainContext *ClutterCntx = NULL; G_LOCK_DEFINE_STATIC (ClutterCntx); /* main lock and locking/unlocking functions */ static GMutex clutter_threads_mutex; /* command line options */ static gboolean clutter_is_initialized = FALSE; static gboolean clutter_show_fps = FALSE; static gboolean clutter_fatal_warnings = FALSE; static gboolean clutter_disable_mipmap_text = FALSE; static gboolean clutter_use_fuzzy_picking = FALSE; static gboolean clutter_enable_accessibility = TRUE; static gboolean clutter_sync_to_vblank = TRUE; static guint clutter_default_fps = 60; static ClutterTextDirection clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR; static guint clutter_main_loop_level = 0; static GSList *main_loops = NULL; /* debug flags */ guint clutter_debug_flags = 0; guint clutter_paint_debug_flags = 0; guint clutter_pick_debug_flags = 0; #ifdef CLUTTER_ENABLE_DEBUG static const GDebugKey clutter_debug_keys[] = { { "misc", CLUTTER_DEBUG_MISC }, { "actor", CLUTTER_DEBUG_ACTOR }, { "texture", CLUTTER_DEBUG_TEXTURE }, { "event", CLUTTER_DEBUG_EVENT }, { "paint", CLUTTER_DEBUG_PAINT }, { "pick", CLUTTER_DEBUG_PICK }, { "pango", CLUTTER_DEBUG_PANGO }, { "backend", CLUTTER_DEBUG_BACKEND }, { "scheduler", CLUTTER_DEBUG_SCHEDULER }, { "script", CLUTTER_DEBUG_SCRIPT }, { "shader", CLUTTER_DEBUG_SHADER }, { "animation", CLUTTER_DEBUG_ANIMATION }, { "layout", CLUTTER_DEBUG_LAYOUT }, { "clipping", CLUTTER_DEBUG_CLIPPING }, { "oob-transforms", CLUTTER_DEBUG_OOB_TRANSFORMS }, }; #endif /* CLUTTER_ENABLE_DEBUG */ static const GDebugKey clutter_pick_debug_keys[] = { { "nop-picking", CLUTTER_DEBUG_NOP_PICKING }, }; static const GDebugKey clutter_paint_debug_keys[] = { { "disable-swap-events", CLUTTER_DEBUG_DISABLE_SWAP_EVENTS }, { "disable-clipped-redraws", CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS }, { "redraws", CLUTTER_DEBUG_REDRAWS }, { "paint-volumes", CLUTTER_DEBUG_PAINT_VOLUMES }, { "disable-culling", CLUTTER_DEBUG_DISABLE_CULLING }, { "disable-offscreen-redirect", CLUTTER_DEBUG_DISABLE_OFFSCREEN_REDIRECT }, { "continuous-redraw", CLUTTER_DEBUG_CONTINUOUS_REDRAW }, { "paint-deform-tiles", CLUTTER_DEBUG_PAINT_DEFORM_TILES }, { "damage-region", CLUTTER_DEBUG_PAINT_DAMAGE_REGION }, }; static inline void clutter_threads_init_default (void) { g_mutex_init (&clutter_threads_mutex); } #define ENVIRONMENT_GROUP "Environment" #define DEBUG_GROUP "Debug" static void clutter_config_read_from_key_file (GKeyFile *keyfile) { GError *key_error = NULL; gboolean bool_value; gint int_value; gchar *str_value; if (!g_key_file_has_group (keyfile, ENVIRONMENT_GROUP)) return; str_value = g_key_file_get_string (keyfile, ENVIRONMENT_GROUP, "Drivers", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_set_allowed_drivers (str_value); g_free (str_value); bool_value = g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP, "ShowFps", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_show_fps = bool_value; bool_value = g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP, "DisableMipmappedText", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_disable_mipmap_text = bool_value; bool_value = g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP, "UseFuzzyPicking", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_use_fuzzy_picking = bool_value; bool_value = g_key_file_get_boolean (keyfile, ENVIRONMENT_GROUP, "EnableAccessibility", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_enable_accessibility = bool_value; int_value = g_key_file_get_integer (keyfile, ENVIRONMENT_GROUP, "DefaultFps", &key_error); if (key_error != NULL) g_clear_error (&key_error); else clutter_default_fps = int_value; str_value = g_key_file_get_string (keyfile, ENVIRONMENT_GROUP, "TextDirection", &key_error); if (key_error != NULL) g_clear_error (&key_error); else { if (g_strcmp0 (str_value, "rtl") == 0) clutter_text_direction = CLUTTER_TEXT_DIRECTION_RTL; else clutter_text_direction = CLUTTER_TEXT_DIRECTION_LTR; } g_free (str_value); } #ifdef CLUTTER_ENABLE_DEBUG static void clutter_debug_read_from_key_file (GKeyFile *keyfile) { GError *key_error = NULL; gchar *value; if (!g_key_file_has_group (keyfile, DEBUG_GROUP)) return; value = g_key_file_get_value (keyfile, DEBUG_GROUP, "Debug", &key_error); if (key_error == NULL) { clutter_debug_flags |= g_parse_debug_string (value, clutter_debug_keys, G_N_ELEMENTS (clutter_debug_keys)); } else g_clear_error (&key_error); g_free (value); value = g_key_file_get_value (keyfile, DEBUG_GROUP, "PaintDebug", &key_error); if (key_error == NULL) { clutter_paint_debug_flags |= g_parse_debug_string (value, clutter_paint_debug_keys, G_N_ELEMENTS (clutter_paint_debug_keys)); } else g_clear_error (&key_error); g_free (value); value = g_key_file_get_value (keyfile, DEBUG_GROUP, "PickDebug", &key_error); if (key_error == NULL) { clutter_pick_debug_flags |= g_parse_debug_string (value, clutter_pick_debug_keys, G_N_ELEMENTS (clutter_pick_debug_keys)); } else g_clear_error (&key_error); g_free (value); } #endif static void clutter_config_read_from_file (const gchar *config_path) { ClutterSettings *settings = clutter_settings_get_default (); GKeyFile *key_file = g_key_file_new (); GError *error = NULL; g_key_file_load_from_file (key_file, config_path, G_KEY_FILE_NONE, &error); if (error == NULL) { CLUTTER_NOTE (MISC, "Reading configuration from '%s'", config_path); clutter_config_read_from_key_file (key_file); #ifdef CLUTTER_ENABLE_DEBUG clutter_debug_read_from_key_file (key_file); #endif _clutter_settings_read_from_key_file (settings, key_file); } else { g_warning ("Unable to read configuration settings from '%s': %s", config_path, error->message); g_error_free (error); } g_key_file_free (key_file); } static void clutter_config_read (void) { gchar *config_path; config_path = g_build_filename (CLUTTER_SYSCONFDIR, "clutter-1.0", "settings.ini", NULL); if (g_file_test (config_path, G_FILE_TEST_EXISTS)) clutter_config_read_from_file (config_path); g_free (config_path); config_path = g_build_filename (g_get_user_config_dir (), "clutter-1.0", "settings.ini", NULL); if (g_file_test (config_path, G_FILE_TEST_EXISTS)) clutter_config_read_from_file (config_path); g_free (config_path); } gboolean _clutter_context_get_show_fps (void) { ClutterMainContext *context = _clutter_context_get_default (); return context->show_fps; } /** * clutter_get_accessibility_enabled: * * Returns whether Clutter has accessibility support enabled. As * least, a value of TRUE means that there are a proper AtkUtil * implementation available * * Return value: %TRUE if Clutter has accessibility support enabled * * Since: 1.4 */ gboolean clutter_get_accessibility_enabled (void) { return cally_get_cally_initialized (); } /** * clutter_disable_accessibility: * * Disable loading the accessibility support. It has the same effect * as setting the environment variable * CLUTTER_DISABLE_ACCESSIBILITY. For the same reason, this method * should be called before clutter_init(). * * Since: 1.14 */ void clutter_disable_accessibility (void) { if (clutter_is_initialized) { g_warning ("clutter_disable_accessibility() can only be called before " "initializing Clutter."); return; } clutter_enable_accessibility = FALSE; } static CoglPangoFontMap * clutter_context_get_pango_fontmap (void) { ClutterMainContext *self; CoglPangoFontMap *font_map; gdouble resolution; gboolean use_mipmapping; self = _clutter_context_get_default (); if (G_LIKELY (self->font_map != NULL)) return self->font_map; font_map = COGL_PANGO_FONT_MAP (cogl_pango_font_map_new ()); resolution = clutter_backend_get_resolution (self->backend); cogl_pango_font_map_set_resolution (font_map, resolution); use_mipmapping = !clutter_disable_mipmap_text; cogl_pango_font_map_set_use_mipmapping (font_map, use_mipmapping); self->font_map = font_map; return self->font_map; } static ClutterTextDirection clutter_get_text_direction (void) { ClutterTextDirection dir = CLUTTER_TEXT_DIRECTION_LTR; const gchar *direction; direction = g_getenv ("CLUTTER_TEXT_DIRECTION"); if (direction && *direction != '\0') { if (strcmp (direction, "rtl") == 0) dir = CLUTTER_TEXT_DIRECTION_RTL; else if (strcmp (direction, "ltr") == 0) dir = CLUTTER_TEXT_DIRECTION_LTR; } else { /* Re-use GTK+'s LTR/RTL handling */ const char *e = g_dgettext ("gtk30", "default:LTR"); if (strcmp (e, "default:RTL") == 0) dir = CLUTTER_TEXT_DIRECTION_RTL; else if (strcmp (e, "default:LTR") == 0) dir = CLUTTER_TEXT_DIRECTION_LTR; else g_warning ("Whoever translated default:LTR did so wrongly."); } CLUTTER_NOTE (MISC, "Text direction: %s", dir == CLUTTER_TEXT_DIRECTION_RTL ? "rtl" : "ltr"); return dir; } /** * clutter_main_quit: * * Terminates the Clutter mainloop. */ void clutter_main_quit (void) { if (main_loops == NULL) { g_critical ("Calling clutter_main_quit() without calling clutter_main() " "is not allowed. If you are using another main loop, use the " "appropriate API to terminate it."); return; } CLUTTER_NOTE (MISC, "Terminating main loop level %d", clutter_main_loop_level); g_main_loop_quit (main_loops->data); } /** * clutter_main_level: * * Retrieves the depth of the Clutter mainloop. * * Return value: The level of the mainloop. */ gint clutter_main_level (void) { return clutter_main_loop_level; } /** * clutter_main: * * Starts the Clutter mainloop. */ void clutter_main (void) { GMainLoop *loop; if (!_clutter_context_is_initialized ()) { g_warning ("Called clutter_main() but Clutter wasn't initialised. " "You must call clutter_init() first."); return; } clutter_main_loop_level++; CLUTTER_NOTE (MISC, "Entering main loop level %d", clutter_main_loop_level); loop = g_main_loop_new (NULL, TRUE); main_loops = g_slist_prepend (main_loops, loop); if (g_main_loop_is_running (main_loops->data)) { _clutter_threads_release_lock (); g_main_loop_run (loop); _clutter_threads_acquire_lock (); } main_loops = g_slist_remove (main_loops, loop); g_main_loop_unref (loop); CLUTTER_NOTE (MISC, "Leaving main loop level %d", clutter_main_loop_level); clutter_main_loop_level--; } gboolean _clutter_threads_dispatch (gpointer data) { ClutterThreadsDispatch *dispatch = data; gboolean ret = FALSE; _clutter_threads_acquire_lock (); if (!g_source_is_destroyed (g_main_current_source ())) ret = dispatch->func (dispatch->data); _clutter_threads_release_lock (); return ret; } void _clutter_threads_dispatch_free (gpointer data) { ClutterThreadsDispatch *dispatch = data; /* XXX - we cannot hold the thread lock here because the main loop * might destroy a source while still in the dispatcher function; so * knowing whether the lock is being held or not is not known a priori. * * see bug: http://bugzilla.gnome.org/show_bug.cgi?id=459555 */ if (dispatch->notify) dispatch->notify (dispatch->data); g_slice_free (ClutterThreadsDispatch, dispatch); } /** * clutter_threads_add_idle_full: (rename-to clutter_threads_add_idle) * @priority: the priority of the timeout source. Typically this will be in the * range between #G_PRIORITY_DEFAULT_IDLE and #G_PRIORITY_HIGH_IDLE * @func: function to call * @data: data to pass to the function * @notify: functio to call when the idle source is removed * * Adds a function to be called whenever there are no higher priority * events pending. If the function returns %FALSE it is automatically * removed from the list of event sources and will not be called again. * * This function can be considered a thread-safe variant of g_idle_add_full(): * it will call @function while holding the Clutter lock. It is logically * equivalent to the following implementation: * * |[ * static gboolean * idle_safe_callback (gpointer data) * { * SafeClosure *closure = data; * gboolean res = FALSE; * * // the callback does not need to acquire the Clutter * / lock itself, as it is held by the this proxy handler * // * res = closure->callback (closure->data); * * return res; * } * static gulong * add_safe_idle (GSourceFunc callback, * gpointer data) * { * SafeClosure *closure = g_new0 (SafeClosure, 1); * * closure->callback = callback; * closure->data = data; * * return g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, * idle_safe_callback, * closure, * g_free) * } *]| * * This function should be used by threaded applications to make sure * that @func is emitted under the Clutter threads lock and invoked * from the same thread that started the Clutter main loop. For instance, * it can be used to update the UI using the results from a worker * thread: * * |[ * static gboolean * update_ui (gpointer data) * { * SomeClosure *closure = data; * * // it is safe to call Clutter API from this function because * / it is invoked from the same thread that started the main * / loop and under the Clutter thread lock * // * clutter_label_set_text (CLUTTER_LABEL (closure->label), * closure->text); * * g_object_unref (closure->label); * g_free (closure); * * return FALSE; * } * * // within another thread // * closure = g_new0 (SomeClosure, 1); * // always take a reference on GObject instances // * closure->label = g_object_ref (my_application->label); * closure->text = g_strdup (processed_text_to_update_the_label); * * clutter_threads_add_idle_full (G_PRIORITY_HIGH_IDLE, * update_ui, * closure, * NULL); * ]| * * Return value: the ID (greater than 0) of the event source. * * Since: 0.4 */ guint clutter_threads_add_idle_full (gint priority, GSourceFunc func, gpointer data, GDestroyNotify notify) { ClutterThreadsDispatch *dispatch; g_return_val_if_fail (func != NULL, 0); dispatch = g_slice_new (ClutterThreadsDispatch); dispatch->func = func; dispatch->data = data; dispatch->notify = notify; return g_idle_add_full (priority, _clutter_threads_dispatch, dispatch, _clutter_threads_dispatch_free); } /** * clutter_threads_add_idle: (skip) * @func: function to call * @data: data to pass to the function * * Simple wrapper around clutter_threads_add_idle_full() using the * default priority. * * Return value: the ID (greater than 0) of the event source. * * Since: 0.4 */ guint clutter_threads_add_idle (GSourceFunc func, gpointer data) { g_return_val_if_fail (func != NULL, 0); return clutter_threads_add_idle_full (G_PRIORITY_DEFAULT_IDLE, func, data, NULL); } /** * clutter_threads_add_timeout_full: (rename-to clutter_threads_add_timeout) * @priority: the priority of the timeout source. Typically this will be in the * range between #G_PRIORITY_DEFAULT and #G_PRIORITY_HIGH. * @interval: the time between calls to the function, in milliseconds * @func: function to call * @data: data to pass to the function * @notify: function to call when the timeout source is removed * * Sets a function to be called at regular intervals holding the Clutter * threads lock, with the given priority. The function is called repeatedly * until it returns %FALSE, at which point the timeout is automatically * removed and the function will not be called again. The @notify function * is called when the timeout is removed. * * The first call to the function will be at the end of the first @interval. * * It is important to note that, due to how the Clutter main loop is * implemented, the timing will not be accurate and it will not try to * "keep up" with the interval. * * See also clutter_threads_add_idle_full(). * * Return value: the ID (greater than 0) of the event source. * * Since: 0.4 */ guint clutter_threads_add_timeout_full (gint priority, guint interval, GSourceFunc func, gpointer data, GDestroyNotify notify) { ClutterThreadsDispatch *dispatch; g_return_val_if_fail (func != NULL, 0); dispatch = g_slice_new (ClutterThreadsDispatch); dispatch->func = func; dispatch->data = data; dispatch->notify = notify; return g_timeout_add_full (priority, interval, _clutter_threads_dispatch, dispatch, _clutter_threads_dispatch_free); } /** * clutter_threads_add_timeout: (skip) * @interval: the time between calls to the function, in milliseconds * @func: function to call * @data: data to pass to the function * * Simple wrapper around clutter_threads_add_timeout_full(). * * Return value: the ID (greater than 0) of the event source. * * Since: 0.4 */ guint clutter_threads_add_timeout (guint interval, GSourceFunc func, gpointer data) { g_return_val_if_fail (func != NULL, 0); return clutter_threads_add_timeout_full (G_PRIORITY_DEFAULT, interval, func, data, NULL); } void _clutter_threads_acquire_lock (void) { g_mutex_lock (&clutter_threads_mutex); } void _clutter_threads_release_lock (void) { /* we need to trylock here, in case the lock hasn't been acquired; on * various systems trying to release a mutex that hasn't been acquired * will cause a run-time error. trylock() will either fail, in which * case we can release the lock we own; or it will succeeds, in which * case we need to release the lock we just acquired. so we ignore the * returned value. * * see: https://bugs.gnome.org/679439 */ g_mutex_trylock (&clutter_threads_mutex); g_mutex_unlock (&clutter_threads_mutex); } void _clutter_context_lock (void) { G_LOCK (ClutterCntx); } void _clutter_context_unlock (void) { G_UNLOCK (ClutterCntx); } gboolean _clutter_context_is_initialized (void) { if (ClutterCntx == NULL) return FALSE; return ClutterCntx->is_initialized; } static ClutterMainContext * clutter_context_get_default_unlocked (void) { if (G_UNLIKELY (ClutterCntx == NULL)) { ClutterMainContext *ctx; /* Read the configuration file, if any, before we set up the * whole thing, so that we can override things like the backend * and the driver */ clutter_config_read (); ClutterCntx = ctx = g_new0 (ClutterMainContext, 1); ctx->is_initialized = FALSE; /* create the windowing system backend */ ctx->backend = _clutter_create_backend (); /* create the default settings object, and store a back pointer to * the backend singleton */ ctx->settings = clutter_settings_get_default (); _clutter_settings_set_backend (ctx->settings, ctx->backend); ctx->last_repaint_id = 1; } return ClutterCntx; } ClutterMainContext * _clutter_context_get_default (void) { ClutterMainContext *retval; _clutter_context_lock (); retval = clutter_context_get_default_unlocked (); _clutter_context_unlock (); return retval; } static gboolean clutter_arg_direction_cb (const char *key, const char *value, gpointer user_data) { clutter_text_direction = (strcmp (value, "rtl") == 0) ? CLUTTER_TEXT_DIRECTION_RTL : CLUTTER_TEXT_DIRECTION_LTR; return TRUE; } #ifdef CLUTTER_ENABLE_DEBUG static gboolean clutter_arg_debug_cb (const char *key, const char *value, gpointer user_data) { clutter_debug_flags |= g_parse_debug_string (value, clutter_debug_keys, G_N_ELEMENTS (clutter_debug_keys)); return TRUE; } static gboolean clutter_arg_no_debug_cb (const char *key, const char *value, gpointer user_data) { clutter_debug_flags &= ~g_parse_debug_string (value, clutter_debug_keys, G_N_ELEMENTS (clutter_debug_keys)); return TRUE; } #endif /* CLUTTER_ENABLE_DEBUG */ GQuark clutter_init_error_quark (void) { return g_quark_from_static_string ("clutter-init-error-quark"); } static ClutterInitError clutter_init_real (GError **error) { ClutterMainContext *ctx; ClutterBackend *backend; /* Note, creates backend if not already existing, though parse args will * have likely created it */ ctx = _clutter_context_get_default (); backend = ctx->backend; if (!ctx->options_parsed) { if (error) g_set_error (error, CLUTTER_INIT_ERROR, CLUTTER_INIT_ERROR_INTERNAL, "When using clutter_get_option_group_without_init() " "you must parse options before calling clutter_init()"); else g_critical ("When using clutter_get_option_group_without_init() " "you must parse options before calling clutter_init()"); return CLUTTER_INIT_ERROR_INTERNAL; } /* * Call backend post parse hooks. */ if (!_clutter_backend_post_parse (backend, error)) return CLUTTER_INIT_ERROR_BACKEND; /* If we are displaying the regions that would get redrawn with clipped * redraws enabled we actually have to disable the clipped redrawing * because otherwise we end up with nasty trails of rectangles everywhere. */ if (clutter_paint_debug_flags & CLUTTER_DEBUG_REDRAWS) clutter_paint_debug_flags |= CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS; /* The same is true when drawing the outlines of paint volumes... */ if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_VOLUMES) { clutter_paint_debug_flags |= CLUTTER_DEBUG_DISABLE_CLIPPED_REDRAWS | CLUTTER_DEBUG_DISABLE_CULLING; } if (clutter_paint_debug_flags & CLUTTER_DEBUG_PAINT_DAMAGE_REGION) g_message ("Enabling damaged region"); /* this will take care of initializing Cogl's state and * query the GL machinery for features */ if (!_clutter_feature_init (error)) return CLUTTER_INIT_ERROR_BACKEND; clutter_text_direction = clutter_get_text_direction (); /* Initiate event collection */ _clutter_backend_init_events (ctx->backend); clutter_is_initialized = TRUE; ctx->is_initialized = TRUE; /* Initialize a11y */ if (clutter_enable_accessibility) cally_accessibility_init (); /* Initialize types required for paint nodes */ _clutter_paint_node_init_types (); return CLUTTER_INIT_SUCCESS; } static GOptionEntry clutter_args[] = { { "clutter-show-fps", 0, 0, G_OPTION_ARG_NONE, &clutter_show_fps, N_("Show frames per second"), NULL }, { "clutter-default-fps", 0, 0, G_OPTION_ARG_INT, &clutter_default_fps, N_("Default frame rate"), "FPS" }, { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &clutter_fatal_warnings, N_("Make all warnings fatal"), NULL }, { "clutter-text-direction", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_direction_cb, N_("Direction for the text"), "DIRECTION" }, { "clutter-disable-mipmapped-text", 0, 0, G_OPTION_ARG_NONE, &clutter_disable_mipmap_text, N_("Disable mipmapping on text"), NULL }, { "clutter-use-fuzzy-picking", 0, 0, G_OPTION_ARG_NONE, &clutter_use_fuzzy_picking, N_("Use 'fuzzy' picking"), NULL }, #ifdef CLUTTER_ENABLE_DEBUG { "clutter-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_debug_cb, N_("Clutter debugging flags to set"), "FLAGS" }, { "clutter-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, clutter_arg_no_debug_cb, N_("Clutter debugging flags to unset"), "FLAGS" }, #endif /* CLUTTER_ENABLE_DEBUG */ { "clutter-enable-accessibility", 0, 0, G_OPTION_ARG_NONE, &clutter_enable_accessibility, N_("Enable accessibility"), NULL }, { NULL, }, }; /* pre_parse_hook: initialise variables depending on environment * variables; these variables might be overridden by the command * line arguments that are going to be parsed after. */ static gboolean pre_parse_hook (GOptionContext *context, GOptionGroup *group, gpointer data, GError **error) { ClutterMainContext *clutter_context; ClutterBackend *backend; const char *env_string; if (clutter_is_initialized) return TRUE; clutter_context = _clutter_context_get_default (); backend = clutter_context->backend; g_assert (CLUTTER_IS_BACKEND (backend)); #ifdef CLUTTER_ENABLE_DEBUG env_string = g_getenv ("CLUTTER_DEBUG"); if (env_string != NULL) { clutter_debug_flags = g_parse_debug_string (env_string, clutter_debug_keys, G_N_ELEMENTS (clutter_debug_keys)); env_string = NULL; } #endif /* CLUTTER_ENABLE_DEBUG */ env_string = g_getenv ("CLUTTER_PICK"); if (env_string != NULL) { clutter_pick_debug_flags = g_parse_debug_string (env_string, clutter_pick_debug_keys, G_N_ELEMENTS (clutter_pick_debug_keys)); env_string = NULL; } env_string = g_getenv ("CLUTTER_PAINT"); if (env_string != NULL) { clutter_paint_debug_flags = g_parse_debug_string (env_string, clutter_paint_debug_keys, G_N_ELEMENTS (clutter_paint_debug_keys)); env_string = NULL; } env_string = g_getenv ("CLUTTER_SHOW_FPS"); if (env_string) clutter_show_fps = TRUE; env_string = g_getenv ("CLUTTER_DEFAULT_FPS"); if (env_string) { gint default_fps = g_ascii_strtoll (env_string, NULL, 10); clutter_default_fps = CLAMP (default_fps, 1, 1000); } env_string = g_getenv ("CLUTTER_DISABLE_MIPMAPPED_TEXT"); if (env_string) clutter_disable_mipmap_text = TRUE; env_string = g_getenv ("CLUTTER_FUZZY_PICK"); if (env_string) clutter_use_fuzzy_picking = TRUE; return _clutter_backend_pre_parse (backend, error); } /* post_parse_hook: initialise the context and data structures * and opens the X display */ static gboolean post_parse_hook (GOptionContext *context, GOptionGroup *group, gpointer data, GError **error) { ClutterMainContext *clutter_context; ClutterBackend *backend; if (clutter_is_initialized) return TRUE; clutter_context = _clutter_context_get_default (); backend = clutter_context->backend; g_assert (CLUTTER_IS_BACKEND (backend)); if (clutter_fatal_warnings) { GLogLevelFlags fatal_mask; fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; g_log_set_always_fatal (fatal_mask); } clutter_context->frame_rate = clutter_default_fps; clutter_context->show_fps = clutter_show_fps; clutter_context->options_parsed = TRUE; /* If not asked to defer display setup, call clutter_init_real(), * which in turn calls the backend post parse hooks. */ if (!clutter_context->defer_display_setup) return clutter_init_real (error) == CLUTTER_INIT_SUCCESS; return TRUE; } /** * clutter_get_option_group: (skip) * * Returns a #GOptionGroup for the command line arguments recognized * by Clutter. You should add this group to your #GOptionContext with * g_option_context_add_group(), if you are using g_option_context_parse() * to parse your commandline arguments. * * Calling g_option_context_parse() with Clutter's #GOptionGroup will result * in Clutter's initialization. That is, the following code: * * |[ * g_option_context_set_main_group (context, clutter_get_option_group ()); * res = g_option_context_parse (context, &argc, &argc, NULL); * ]| * * is functionally equivalent to: * * |[ * clutter_init (&argc, &argv); * ]| * * After g_option_context_parse() on a #GOptionContext containing the * Clutter #GOptionGroup has returned %TRUE, Clutter is guaranteed to be * initialized. * * Return value: (transfer full): a #GOptionGroup for the commandline arguments * recognized by Clutter * * Since: 0.2 */ GOptionGroup * clutter_get_option_group (void) { ClutterMainContext *context; GOptionGroup *group; clutter_base_init (); context = _clutter_context_get_default (); group = g_option_group_new ("clutter", "Clutter Options", "Show Clutter Options", NULL, NULL); g_option_group_set_parse_hooks (group, pre_parse_hook, post_parse_hook); g_option_group_add_entries (group, clutter_args); /* add backend-specific options */ _clutter_backend_add_options (context->backend, group); return group; } /** * clutter_get_option_group_without_init: (skip) * * Returns a #GOptionGroup for the command line arguments recognized * by Clutter. You should add this group to your #GOptionContext with * g_option_context_add_group(), if you are using g_option_context_parse() * to parse your commandline arguments. * * Unlike clutter_get_option_group(), calling g_option_context_parse() with * the #GOptionGroup returned by this function requires a subsequent explicit * call to clutter_init(); use this function when needing to set foreign * display connection with clutter_x11_set_display(), or with * `gtk_clutter_init()`. * * Return value: (transfer full): a #GOptionGroup for the commandline arguments * recognized by Clutter * * Since: 0.8 */ GOptionGroup * clutter_get_option_group_without_init (void) { ClutterMainContext *context; GOptionGroup *group; clutter_base_init (); context = _clutter_context_get_default (); context->defer_display_setup = TRUE; group = clutter_get_option_group (); return group; } /* Note that the gobject-introspection annotations for the argc/argv * parameters do not produce the right result; however, they do * allow the common case of argc=NULL, argv=NULL to work. */ /** * clutter_init_with_args: * @argc: (inout): a pointer to the number of command line arguments * @argv: (array length=argc) (inout) (allow-none): a pointer to the array * of command line arguments * @parameter_string: (allow-none): a string which is displayed in the * first line of output, after * programname [OPTION...] * @entries: (array) (allow-none): a %NULL terminated array of * #GOptionEntrys describing the options of your program * @translation_domain: (allow-none): a translation domain to use for * translating the output for the options in * @entries with gettext(), or %NULL * @error: (allow-none): a return location for a #GError * * This function does the same work as clutter_init(). Additionally, * it allows you to add your own command line options, and it * automatically generates nicely formatted * output. Note that your program will be terminated after writing * out the help output. Also note that, in case of error, the * error message will be placed inside @error instead of being * printed on the display. * * Just like clutter_init(), if this function returns an error code then * any subsequent call to any other Clutter API will result in undefined * behaviour - including segmentation faults. * * Return value: %CLUTTER_INIT_SUCCESS if Clutter has been successfully * initialised, or other values or #ClutterInitError in case of * error. * * Since: 0.2 */ ClutterInitError clutter_init_with_args (int *argc, char ***argv, const char *parameter_string, GOptionEntry *entries, const char *translation_domain, GError **error) { GOptionContext *context; GOptionGroup *group; gboolean res; ClutterMainContext *ctx; if (clutter_is_initialized) return CLUTTER_INIT_SUCCESS; clutter_base_init (); ctx = _clutter_context_get_default (); if (!ctx->defer_display_setup) { #if 0 if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); #endif context = g_option_context_new (parameter_string); group = clutter_get_option_group (); g_option_context_add_group (context, group); group = cogl_get_option_group (); g_option_context_add_group (context, group); if (entries) g_option_context_add_main_entries (context, entries, translation_domain); res = g_option_context_parse (context, argc, argv, error); g_option_context_free (context); /* if res is FALSE, the error is filled for * us by g_option_context_parse() */ if (!res) { /* if there has been an error in the initialization, the * error id will be preserved inside the GError code */ if (error && *error) return (*error)->code; else return CLUTTER_INIT_ERROR_INTERNAL; } return CLUTTER_INIT_SUCCESS; } else return clutter_init_real (error); } static gboolean clutter_parse_args (int *argc, char ***argv, GError **error) { GOptionContext *option_context; GOptionGroup *clutter_group, *cogl_group; GError *internal_error = NULL; gboolean ret = TRUE; if (clutter_is_initialized) return TRUE; option_context = g_option_context_new (NULL); g_option_context_set_ignore_unknown_options (option_context, TRUE); g_option_context_set_help_enabled (option_context, FALSE); /* Initiate any command line options from the backend */ clutter_group = clutter_get_option_group (); g_option_context_set_main_group (option_context, clutter_group); cogl_group = cogl_get_option_group (); g_option_context_add_group (option_context, cogl_group); if (!g_option_context_parse (option_context, argc, argv, &internal_error)) { g_propagate_error (error, internal_error); ret = FALSE; } g_option_context_free (option_context); return ret; } /** * clutter_init: * @argc: (inout): The number of arguments in @argv * @argv: (array length=argc) (inout) (allow-none): A pointer to an array * of arguments. * * Initialises everything needed to operate with Clutter and parses some * standard command line options; @argc and @argv are adjusted accordingly * so your own code will never see those standard arguments. * * It is safe to call this function multiple times. * * This function will not abort in case of errors during * initialization; clutter_init() will print out the error message on * stderr, and will return an error code. It is up to the application * code to handle this case. If you need to display the error message * yourself, you can use clutter_init_with_args(), which takes a #GError * pointer. * * If this function fails, and returns an error code, any subsequent * Clutter API will have undefined behaviour - including segmentation * faults and assertion failures. Make sure to handle the returned * #ClutterInitError enumeration value. * * Return value: a #ClutterInitError value */ ClutterInitError clutter_init (int *argc, char ***argv) { ClutterMainContext *ctx; GError *error = NULL; ClutterInitError res; if (clutter_is_initialized) return CLUTTER_INIT_SUCCESS; clutter_base_init (); ctx = _clutter_context_get_default (); if (!ctx->defer_display_setup) { #if 0 if (argc && *argc > 0 && *argv) g_set_prgname ((*argv)[0]); #endif /* parse_args will trigger backend creation and things like * DISPLAY connection etc. */ if (!clutter_parse_args (argc, argv, &error)) { g_critical ("Unable to initialize Clutter: %s", error->message); g_error_free (error); res = CLUTTER_INIT_ERROR_INTERNAL; } else res = CLUTTER_INIT_SUCCESS; } else { res = clutter_init_real (&error); if (error != NULL) { g_critical ("Unable to initialize Clutter: %s", error->message); g_error_free (error); } } return res; } gboolean _clutter_boolean_handled_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy) { gboolean continue_emission; gboolean signal_handled; signal_handled = g_value_get_boolean (handler_return); g_value_set_boolean (return_accu, signal_handled); continue_emission = !signal_handled; return continue_emission; } gboolean _clutter_boolean_continue_accumulator (GSignalInvocationHint *ihint, GValue *return_accu, const GValue *handler_return, gpointer dummy) { gboolean continue_emission; continue_emission = g_value_get_boolean (handler_return); g_value_set_boolean (return_accu, continue_emission); return continue_emission; } static void event_click_count_generate (ClutterEvent *event) { /* multiple button click detection */ static gint click_count = 0; static gint previous_x = -1; static gint previous_y = -1; static guint32 previous_time = 0; static gint previous_button_number = -1; ClutterInputDevice *device = NULL; ClutterSettings *settings; guint double_click_time; guint double_click_distance; settings = clutter_settings_get_default (); g_object_get (settings, "double-click-distance", &double_click_distance, "double-click-time", &double_click_time, NULL); device = clutter_event_get_device (event); if (device != NULL) { click_count = device->click_count; previous_x = device->previous_x; previous_y = device->previous_y; previous_time = device->previous_time; previous_button_number = device->previous_button_number; CLUTTER_NOTE (EVENT, "Restoring previous click count:%d (device:%d, time:%u)", click_count, clutter_input_device_get_device_id (device), previous_time); } else { CLUTTER_NOTE (EVENT, "Restoring previous click count:%d (time:%u)", click_count, previous_time); } switch (clutter_event_type (event)) { case CLUTTER_BUTTON_PRESS: /* check if we are in time and within distance to increment an * existing click count */ if (event->button.button == previous_button_number && event->button.time < (previous_time + double_click_time) && (ABS (event->button.x - previous_x) <= double_click_distance) && (ABS (event->button.y - previous_y) <= double_click_distance)) { CLUTTER_NOTE (EVENT, "Increase click count (button: %d, time: %u)", event->button.button, event->button.time); click_count += 1; } else /* start a new click count*/ { CLUTTER_NOTE (EVENT, "Reset click count (button: %d, time: %u)", event->button.button, event->button.time); click_count = 1; previous_button_number = event->button.button; } previous_x = event->button.x; previous_y = event->button.y; previous_time = event->button.time; G_GNUC_FALLTHROUGH; case CLUTTER_BUTTON_RELEASE: event->button.click_count = click_count; break; default: g_assert_not_reached (); break; } if (event->type == CLUTTER_BUTTON_PRESS && device != NULL) { CLUTTER_NOTE (EVENT, "Storing click count: %d (device:%d, time:%u)", click_count, clutter_input_device_get_device_id (device), previous_time); device->click_count = click_count; device->previous_x = previous_x; device->previous_y = previous_y; device->previous_time = previous_time; device->previous_button_number = previous_button_number; } } static inline void emit_event_chain (ClutterEvent *event) { static gboolean lock = FALSE; if (event->any.source == NULL) { CLUTTER_NOTE (EVENT, "No source set, discarding event"); return; } /* reentrancy check */ if (lock != FALSE) { g_warning ("Tried emitting event during event delivery, bailing out."); return; } lock = TRUE; _clutter_actor_handle_event (event->any.source, event); lock = FALSE; } /* * Emits a pointer event after having prepared the event for delivery (setting * source, computing click_count, generating enter/leave etc.). */ static inline void emit_pointer_event (ClutterEvent *event, ClutterInputDevice *device) { if (_clutter_event_process_filters (event)) return; if (device != NULL && device->pointer_grab_actor != NULL) clutter_actor_event (device->pointer_grab_actor, event, FALSE); else emit_event_chain (event); } static inline void emit_crossing_event (ClutterEvent *event, ClutterInputDevice *device) { ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); ClutterActor *grab_actor = NULL; if (_clutter_event_process_filters (event)) return; if (sequence) { if (device->sequence_grab_actors != NULL) grab_actor = g_hash_table_lookup (device->sequence_grab_actors, sequence); } else { if (device != NULL && device->pointer_grab_actor != NULL) grab_actor = device->pointer_grab_actor; } if (grab_actor != NULL) clutter_actor_event (grab_actor, event, FALSE); else emit_event_chain (event); } static inline void emit_touch_event (ClutterEvent *event, ClutterInputDevice *device) { ClutterActor *grab_actor = NULL; if (_clutter_event_process_filters (event)) return; if (device->sequence_grab_actors != NULL) { grab_actor = g_hash_table_lookup (device->sequence_grab_actors, event->touch.sequence); } if (grab_actor != NULL) { /* per-device sequence grab */ clutter_actor_event (grab_actor, event, FALSE); } else { /* no grab, time to capture and bubble */ emit_event_chain (event); } } static inline void emit_keyboard_event (ClutterEvent *event, ClutterInputDevice *device) { if (_clutter_event_process_filters (event)) return; if (device != NULL && device->keyboard_grab_actor != NULL) clutter_actor_event (device->keyboard_grab_actor, event, FALSE); else emit_event_chain (event); } static inline void process_key_event (ClutterEvent *event, ClutterInputDevice *device) { ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_GET_CLASS (device); if (device_class->process_kbd_a11y_event) { device_class->process_kbd_a11y_event (event, device, emit_keyboard_event); return; } emit_keyboard_event (event, device); } static gboolean is_off_stage (ClutterActor *stage, gfloat x, gfloat y) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); return (x < 0 || y < 0 || x >= width || y >= height); } /** * clutter_do_event: * @event: a #ClutterEvent. * * Processes an event. * * The @event must be a valid #ClutterEvent and have a #ClutterStage * associated to it. * * This function is only useful when embedding Clutter inside another * toolkit, and it should never be called by applications. * * Since: 0.4 */ void clutter_do_event (ClutterEvent *event) { /* we need the stage for the event */ if (event->any.stage == NULL) { g_warning ("%s: Event does not have a stage: discarding.", G_STRFUNC); return; } /* stages in destruction do not process events */ if (CLUTTER_ACTOR_IN_DESTRUCTION (event->any.stage)) return; /* Instead of processing events when received, we queue them up to * handle per-frame before animations, layout, and drawing. * * This gives us the chance to reliably compress motion events * because we've "looked ahead" and know all motion events that * will occur before drawing the frame. */ _clutter_stage_queue_event (event->any.stage, event, TRUE); } static void _clutter_process_event_details (ClutterActor *stage, ClutterMainContext *context, ClutterEvent *event) { ClutterInputDevice *device = clutter_event_get_device (event); switch (event->type) { case CLUTTER_NOTHING: event->any.source = stage; break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_STRIP: case CLUTTER_PAD_RING: case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: { ClutterActor *actor = NULL; /* check that we're not a synthetic event with source set */ if (event->any.source == NULL) { actor = clutter_stage_get_key_focus (CLUTTER_STAGE (stage)); event->any.source = actor; if (G_UNLIKELY (actor == NULL)) { g_warning ("No key focus set, discarding"); return; } } process_key_event (event, device); } break; case CLUTTER_ENTER: /* if we're entering from outside the stage we need * to check whether the pointer is actually on another * actor, and emit an additional pointer event */ if (event->any.source == stage && event->crossing.related == NULL) { ClutterActor *actor = NULL; emit_crossing_event (event, device); actor = clutter_input_device_update (device, NULL, FALSE); if (actor != stage) { ClutterEvent *crossing; /* we emit the exact same event on the actor */ crossing = clutter_event_copy (event); crossing->crossing.related = stage; crossing->crossing.source = actor; emit_crossing_event (crossing, device); clutter_event_free (crossing); } } else emit_crossing_event (event, device); break; case CLUTTER_LEAVE: /* same as CLUTTER_ENTER above: when leaving the stage * we need to also emit a CLUTTER_LEAVE event on the * actor currently underneath the device, unless it's the * stage */ if (event->any.source == stage && event->crossing.related == NULL && device->cursor_actor != stage) { ClutterEvent *crossing; crossing = clutter_event_copy (event); crossing->crossing.related = stage; crossing->crossing.source = device->cursor_actor; emit_crossing_event (crossing, device); clutter_event_free (crossing); } emit_crossing_event (event, device); break; case CLUTTER_DESTROY_NOTIFY: case CLUTTER_DELETE: event->any.source = stage; if (_clutter_event_process_filters (event)) break; /* the stage did not handle the event, so we just quit */ clutter_stage_event (CLUTTER_STAGE (stage), event); break; case CLUTTER_MOTION: #ifdef CLUTTER_WINDOWING_X11 if (!clutter_check_windowing_backend (CLUTTER_WINDOWING_X11) && !(event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) { if (_clutter_is_input_pointer_a11y_enabled (device)) { gfloat x, y; clutter_event_get_coords (event, &x, &y); _clutter_input_pointer_a11y_on_motion_event (device, x, y); } } #endif /* CLUTTER_WINDOWING_X11 */ /* only the stage gets motion events if they are enabled */ if (!clutter_stage_get_motion_events_enabled (CLUTTER_STAGE (stage)) && event->any.source == NULL) { /* Only stage gets motion events */ event->any.source = stage; if (_clutter_event_process_filters (event)) break; if (device != NULL && device->pointer_grab_actor != NULL) { clutter_actor_event (device->pointer_grab_actor, event, FALSE); break; } /* Trigger handlers on stage in both capture .. */ if (!clutter_actor_event (stage, event, TRUE)) { /* and bubbling phase */ clutter_actor_event (stage, event, FALSE); } break; } G_GNUC_FALLTHROUGH; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: #ifdef CLUTTER_WINDOWING_X11 if (!clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) { if (_clutter_is_input_pointer_a11y_enabled (device) && (event->type != CLUTTER_MOTION)) { _clutter_input_pointer_a11y_on_button_event (device, event->button.button, event->type == CLUTTER_BUTTON_PRESS); } } #endif /* CLUTTER_WINDOWING_X11 */ case CLUTTER_SCROLL: case CLUTTER_TOUCHPAD_PINCH: case CLUTTER_TOUCHPAD_SWIPE: { ClutterActor *actor; gfloat x, y; clutter_event_get_coords (event, &x, &y); /* Only do a pick to find the source if source is not already set * (as it could be in a synthetic event) */ if (event->any.source == NULL) { /* emulate X11 the implicit soft grab; the implicit soft grab * keeps relaying motion events when the stage is left with a * pointer button pressed. since this is what happens when we * disable per-actor motion events we need to maintain the same * behaviour when the per-actor motion events are enabled as * well */ if (is_off_stage (stage, x, y)) { if (event->type == CLUTTER_BUTTON_RELEASE) { CLUTTER_NOTE (EVENT, "Release off stage received at %.2f, %.2f", x, y); event->button.source = stage; event->button.click_count = 1; emit_pointer_event (event, device); } else if (event->type == CLUTTER_MOTION) { CLUTTER_NOTE (EVENT, "Motion off stage received at %.2f, %2.f", x, y); event->motion.source = stage; emit_pointer_event (event, device); } break; } /* if the backend provides a device then we should * already have everything we need to update it and * get the actor underneath */ if (device != NULL) actor = clutter_input_device_update (device, NULL, TRUE); else { CLUTTER_NOTE (EVENT, "No device found: picking"); actor = _clutter_stage_do_pick (CLUTTER_STAGE (stage), x, y, CLUTTER_PICK_REACTIVE); } if (actor == NULL) break; event->any.source = actor; } else { /* use the source already set in the synthetic event */ actor = event->any.source; } CLUTTER_NOTE (EVENT, "Reactive event received at %.2f, %.2f - actor: %p", x, y, actor); /* button presses and releases need a click count */ if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) { /* Generate click count */ event_click_count_generate (event); } emit_pointer_event (event, device); break; } case CLUTTER_TOUCH_UPDATE: /* only the stage gets motion events if they are enabled */ if (!clutter_stage_get_motion_events_enabled (CLUTTER_STAGE (stage)) && event->any.source == NULL) { ClutterActor *grab_actor = NULL; /* Only stage gets motion events */ event->any.source = stage; if (_clutter_event_process_filters (event)) break; /* global grabs */ if (device->sequence_grab_actors != NULL) { grab_actor = g_hash_table_lookup (device->sequence_grab_actors, event->touch.sequence); } if (grab_actor != NULL) { clutter_actor_event (grab_actor, event, FALSE); break; } /* Trigger handlers on stage in both capture .. */ if (!clutter_actor_event (stage, event, TRUE)) { /* and bubbling phase */ clutter_actor_event (stage, event, FALSE); } break; } G_GNUC_FALLTHROUGH; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_CANCEL: case CLUTTER_TOUCH_END: { ClutterActor *actor; ClutterEventSequence *sequence; gfloat x, y; sequence = clutter_event_get_event_sequence (event); if (event->type == CLUTTER_TOUCH_BEGIN) _clutter_input_device_add_event_sequence (device, event); clutter_event_get_coords (event, &x, &y); /* Only do a pick to find the source if source is not already set * (as it could be in a synthetic event) */ if (event->any.source == NULL) { /* same as the mouse events above, emulate the X11 implicit * soft grab */ if (is_off_stage (stage, x, y)) { CLUTTER_NOTE (EVENT, "Touch %s off stage received at %.2f, %.2f", event->type == CLUTTER_TOUCH_UPDATE ? "update" : event->type == CLUTTER_TOUCH_END ? "end" : event->type == CLUTTER_TOUCH_CANCEL ? "cancel" : "?", x, y); event->touch.source = stage; emit_touch_event (event, device); if (event->type == CLUTTER_TOUCH_END) _clutter_input_device_remove_event_sequence (device, event); break; } if (device != NULL) actor = clutter_input_device_update (device, sequence, TRUE); else { CLUTTER_NOTE (EVENT, "No device found: picking"); actor = _clutter_stage_do_pick (CLUTTER_STAGE (stage), x, y, CLUTTER_PICK_REACTIVE); } if (actor == NULL) break; event->any.source = actor; } else { /* use the source already set in the synthetic event */ actor = event->any.source; } CLUTTER_NOTE (EVENT, "Reactive event received at %.2f, %.2f - actor: %p", x, y, actor); emit_touch_event (event, device); if (event->type == CLUTTER_TOUCH_END) _clutter_input_device_remove_event_sequence (device, event); break; } case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: clutter_input_device_update_from_tool (clutter_event_get_source_device (event), clutter_event_get_device_tool (event)); if (_clutter_event_process_filters (event)) break; if (!clutter_actor_event (stage, event, TRUE)) { /* and bubbling phase */ clutter_actor_event (stage, event, FALSE); } break; case CLUTTER_STAGE_STATE: /* focus - forward to stage */ event->any.source = stage; if (!_clutter_event_process_filters (event)) clutter_stage_event (CLUTTER_STAGE (stage), event); break; case CLUTTER_CLIENT_MESSAGE: break; case CLUTTER_DEVICE_ADDED: case CLUTTER_DEVICE_REMOVED: if (!_clutter_event_process_filters (event)) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (context->backend); clutter_seat_handle_device_event (seat, event); } break; case CLUTTER_EVENT_LAST: break; } } /* * _clutter_process_event * @event: a #ClutterEvent. * * Does the actual work of processing an event that was queued earlier * out of clutter_do_event(). */ void _clutter_process_event (ClutterEvent *event) { ClutterMainContext *context; ClutterActor *stage; context = _clutter_context_get_default (); stage = CLUTTER_ACTOR (event->any.stage); if (stage == NULL) { CLUTTER_NOTE (EVENT, "Discarding event without a stage set"); return; } /* push events on a stack, so that we don't need to * add an event parameter to all signals that can be emitted within * an event chain */ context->current_event = g_slist_prepend (context->current_event, event); _clutter_process_event_details (stage, context, event); context->current_event = g_slist_delete_link (context->current_event, context->current_event); } /** * clutter_get_actor_by_gid: * @id_: a #ClutterActor unique id. * * Retrieves the #ClutterActor with @id_. * * Return value: (transfer none): the actor with the passed id or %NULL. * The returned actor does not have its reference count increased. * * Since: 0.6 * * Deprecated: 1.8: The id is deprecated, and this function always returns * %NULL. Use the proper scene graph API in #ClutterActor to find a child * of the stage. */ ClutterActor * clutter_get_actor_by_gid (guint32 id_) { return NULL; } void clutter_base_init (void) { static gboolean initialised = FALSE; if (!initialised) { initialised = TRUE; #if !GLIB_CHECK_VERSION (2, 35, 1) /* initialise GLib type system */ g_type_init (); #endif /* initialise the Big Clutter Lock™ if necessary */ clutter_threads_init_default (); clutter_graphene_init (); } } /** * clutter_get_default_frame_rate: * * Retrieves the default frame rate. See clutter_set_default_frame_rate(). * * Return value: the default frame rate * * Since: 0.6 */ guint clutter_get_default_frame_rate (void) { ClutterMainContext *context; context = _clutter_context_get_default (); return context->frame_rate; } /** * clutter_get_font_map: * * Retrieves the #PangoFontMap instance used by Clutter. * You can use the global font map object with the COGL * Pango API. * * Return value: (transfer none): the #PangoFontMap instance. The returned * value is owned by Clutter and it should never be unreferenced. * * Since: 1.0 */ PangoFontMap * clutter_get_font_map (void) { return PANGO_FONT_MAP (clutter_context_get_pango_fontmap ()); } typedef struct _ClutterRepaintFunction { guint id; ClutterRepaintFlags flags; GSourceFunc func; gpointer data; GDestroyNotify notify; } ClutterRepaintFunction; /** * clutter_threads_remove_repaint_func: * @handle_id: an unsigned integer greater than zero * * Removes the repaint function with @handle_id as its id * * Since: 1.0 */ void clutter_threads_remove_repaint_func (guint handle_id) { ClutterRepaintFunction *repaint_func; ClutterMainContext *context; GList *l; g_return_if_fail (handle_id > 0); _clutter_context_lock (); context = clutter_context_get_default_unlocked (); l = context->repaint_funcs; while (l != NULL) { repaint_func = l->data; if (repaint_func->id == handle_id) { context->repaint_funcs = g_list_remove_link (context->repaint_funcs, l); g_list_free (l); if (repaint_func->notify) repaint_func->notify (repaint_func->data); g_slice_free (ClutterRepaintFunction, repaint_func); break; } l = l->next; } _clutter_context_unlock (); } /** * clutter_threads_add_repaint_func: * @func: the function to be called within the paint cycle * @data: data to be passed to the function, or %NULL * @notify: function to be called when removing the repaint * function, or %NULL * * Adds a function to be called whenever Clutter is processing a new * frame. * * If the function returns %FALSE it is automatically removed from the * list of repaint functions and will not be called again. * * This function is guaranteed to be called from within the same thread * that called clutter_main(), and while the Clutter lock is being held; * the function will be called within the main loop, so it is imperative * that it does not block, otherwise the frame time budget may be lost. * * A repaint function is useful to ensure that an update of the scenegraph * is performed before the scenegraph is repainted. By default, a repaint * function added using this function will be invoked prior to the frame * being processed. * * Adding a repaint function does not automatically ensure that a new * frame will be queued. * * When the repaint function is removed (either because it returned %FALSE * or because clutter_threads_remove_repaint_func() has been called) the * @notify function will be called, if any is set. * * See also: clutter_threads_add_repaint_func_full() * * Return value: the ID (greater than 0) of the repaint function. You * can use the returned integer to remove the repaint function by * calling clutter_threads_remove_repaint_func(). * * Since: 1.0 */ guint clutter_threads_add_repaint_func (GSourceFunc func, gpointer data, GDestroyNotify notify) { return clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT, func, data, notify); } /** * clutter_threads_add_repaint_func_full: * @flags: flags for the repaint function * @func: the function to be called within the paint cycle * @data: data to be passed to the function, or %NULL * @notify: function to be called when removing the repaint * function, or %NULL * * Adds a function to be called whenever Clutter is processing a new * frame. * * If the function returns %FALSE it is automatically removed from the * list of repaint functions and will not be called again. * * This function is guaranteed to be called from within the same thread * that called clutter_main(), and while the Clutter lock is being held; * the function will be called within the main loop, so it is imperative * that it does not block, otherwise the frame time budget may be lost. * * A repaint function is useful to ensure that an update of the scenegraph * is performed before the scenegraph is repainted. The @flags passed to this * function will determine the section of the frame processing that will * result in @func being called. * * Adding a repaint function does not automatically ensure that a new * frame will be queued. * * When the repaint function is removed (either because it returned %FALSE * or because clutter_threads_remove_repaint_func() has been called) the * @notify function will be called, if any is set. * * Return value: the ID (greater than 0) of the repaint function. You * can use the returned integer to remove the repaint function by * calling clutter_threads_remove_repaint_func(). * * Since: 1.10 */ guint clutter_threads_add_repaint_func_full (ClutterRepaintFlags flags, GSourceFunc func, gpointer data, GDestroyNotify notify) { ClutterMainContext *context; ClutterRepaintFunction *repaint_func; g_return_val_if_fail (func != NULL, 0); _clutter_context_lock (); context = clutter_context_get_default_unlocked (); repaint_func = g_slice_new (ClutterRepaintFunction); repaint_func->id = context->last_repaint_id++; /* mask out QUEUE_REDRAW_ON_ADD, since we're going to consume it */ repaint_func->flags = flags & ~CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD; repaint_func->func = func; repaint_func->data = data; repaint_func->notify = notify; context->repaint_funcs = g_list_prepend (context->repaint_funcs, repaint_func); _clutter_context_unlock (); if ((flags & CLUTTER_REPAINT_FLAGS_QUEUE_REDRAW_ON_ADD) != 0) { ClutterMasterClock *master_clock = _clutter_master_clock_get_default (); _clutter_master_clock_ensure_next_iteration (master_clock); } return repaint_func->id; } /* * _clutter_run_repaint_functions: * @flags: only run the repaint functions matching the passed flags * * Executes the repaint functions added using the * clutter_threads_add_repaint_func() function. * * Must be called with the Clutter thread lock held. */ void _clutter_run_repaint_functions (ClutterRepaintFlags flags) { ClutterMainContext *context = _clutter_context_get_default (); ClutterRepaintFunction *repaint_func; GList *invoke_list, *reinvoke_list, *l; if (context->repaint_funcs == NULL) return; /* steal the list */ invoke_list = context->repaint_funcs; context->repaint_funcs = NULL; reinvoke_list = NULL; /* consume the whole list while we execute the functions */ while (invoke_list != NULL) { gboolean res = FALSE; repaint_func = invoke_list->data; l = invoke_list; invoke_list = g_list_remove_link (invoke_list, invoke_list); g_list_free (l); if ((repaint_func->flags & flags) != 0) res = repaint_func->func (repaint_func->data); else res = TRUE; if (res) reinvoke_list = g_list_prepend (reinvoke_list, repaint_func); else { if (repaint_func->notify != NULL) repaint_func->notify (repaint_func->data); g_slice_free (ClutterRepaintFunction, repaint_func); } } if (context->repaint_funcs != NULL) { context->repaint_funcs = g_list_concat (context->repaint_funcs, g_list_reverse (reinvoke_list)); } else context->repaint_funcs = g_list_reverse (reinvoke_list); } /** * clutter_get_default_text_direction: * * Retrieves the default direction for the text. The text direction is * determined by the locale and/or by the `CLUTTER_TEXT_DIRECTION` * environment variable. * * The default text direction can be overridden on a per-actor basis by using * clutter_actor_set_text_direction(). * * Return value: the default text direction * * Since: 1.2 */ ClutterTextDirection clutter_get_default_text_direction (void) { return clutter_text_direction; } /*< private > * clutter_clear_events_queue: * * Clears the events queue stored in the main context. */ void _clutter_clear_events_queue (void) { ClutterMainContext *context = _clutter_context_get_default (); if (context->events_queue != NULL) { g_queue_foreach (context->events_queue, (GFunc) clutter_event_free, NULL); g_queue_free (context->events_queue); context->events_queue = NULL; } } void _clutter_clear_events_queue_for_stage (ClutterStage *stage) { ClutterMainContext *context = _clutter_context_get_default (); GList *l, *next; if (context->events_queue == NULL) return; /* Remove any pending events for this stage from the event queue */ for (l = context->events_queue->head; l; l = next) { ClutterEvent *event = l->data; next = l->next; if (event->any.stage == stage) { g_queue_delete_link (context->events_queue, l); clutter_event_free (event); } } } ClutterPickMode _clutter_context_get_pick_mode (void) { ClutterMainContext *context = _clutter_context_get_default (); return context->pick_mode; } /** * clutter_check_windowing_backend: * @backend_type: the name of the backend to check * * Checks the run-time name of the Clutter windowing system backend, using * the symbolic macros like %CLUTTER_WINDOWING_X11. * * This function should be used in conjuction with the compile-time macros * inside applications and libraries that are using the platform-specific * windowing system API, to ensure that they are running on the correct * windowing system; for instance: * * |[ * #ifdef CLUTTER_WINDOWING_X11 * if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) * { * // it is safe to use the clutter_x11_* API * } * else * #endif * g_error ("Unknown Clutter backend."); * ]| * * Return value: %TRUE if the current Clutter windowing system backend is * the one checked, and %FALSE otherwise * * Since: 1.10 */ gboolean clutter_check_windowing_backend (const char *backend_type) { ClutterMainContext *context = _clutter_context_get_default (); g_return_val_if_fail (backend_type != NULL, FALSE); backend_type = g_intern_string (backend_type); #ifdef CLUTTER_WINDOWING_EGL if (backend_type == I_(CLUTTER_WINDOWING_EGL) && CLUTTER_IS_BACKEND_EGL_NATIVE (context->backend)) return TRUE; else #endif #ifdef CLUTTER_WINDOWING_X11 if (backend_type == I_(CLUTTER_WINDOWING_X11) && CLUTTER_IS_BACKEND_X11 (context->backend)) return TRUE; else #endif return FALSE; } /** * clutter_add_debug_flags: (skip) * * Adds the debug flags passed to the list of debug flags. */ void clutter_add_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_debug_flags |= debug_flags; clutter_paint_debug_flags |= draw_flags; clutter_pick_debug_flags |= pick_flags; } /** * clutter_remove_debug_flags: (skip) * * Removes the debug flags passed from the list of debug flags. */ void clutter_remove_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_debug_flags &= ~debug_flags; clutter_paint_debug_flags &= ~draw_flags; clutter_pick_debug_flags &= ~pick_flags; } void _clutter_set_sync_to_vblank (gboolean sync_to_vblank) { clutter_sync_to_vblank = !!sync_to_vblank; } void _clutter_debug_messagev (const char *format, va_list var_args) { static gint64 last_debug_stamp; gchar *stamp, *fmt; gint64 cur_time, debug_stamp; cur_time = g_get_monotonic_time (); /* if the last debug message happened less than a second ago, just * show the increments instead of the full timestamp */ if (last_debug_stamp == 0 || cur_time - last_debug_stamp >= G_USEC_PER_SEC) { debug_stamp = cur_time; last_debug_stamp = debug_stamp; stamp = g_strdup_printf ("[%16" G_GINT64_FORMAT "]", debug_stamp); } else { debug_stamp = cur_time - last_debug_stamp; stamp = g_strdup_printf ("[%+16" G_GINT64_FORMAT "]", debug_stamp); } fmt = g_strconcat (stamp, ":", format, NULL); g_free (stamp); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, var_args); g_free (fmt); } void _clutter_debug_message (const char *format, ...) { va_list args; va_start (args, format); _clutter_debug_messagev (format, args); va_end (args); } gboolean _clutter_diagnostic_enabled (void) { static const char *clutter_enable_diagnostic = NULL; if (G_UNLIKELY (clutter_enable_diagnostic == NULL)) { clutter_enable_diagnostic = g_getenv ("CLUTTER_ENABLE_DIAGNOSTIC"); if (clutter_enable_diagnostic == NULL) clutter_enable_diagnostic = "0"; } return *clutter_enable_diagnostic != '0'; } void _clutter_diagnostic_message (const char *format, ...) { va_list args; char *fmt; fmt = g_strconcat ("[DIAGNOSTIC]: ", format, NULL); va_start (args, format); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, fmt, args); va_end (args); g_free (fmt); } muffin-6.4.1/clutter/clutter/clutter-box-layout.c0000664000175000017500000021056314723361714021044 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on the NBTK NbtkBoxLayout actor by: * Thomas Wood */ /** * SECTION:clutter-box-layout * @short_description: A layout manager arranging children on a single line * * The #ClutterBoxLayout is a #ClutterLayoutManager implementing the * following layout policy: * * - all children are arranged on a single line * - the axis used is controlled by the #ClutterBoxLayout:orientation property * - the order of the packing is determined by the #ClutterBoxLayout:pack-start boolean property * - each child will be allocated to its natural size or, if #ClutterActor:x-expand or * #ClutterActor:y-expand are set, the available size * - honours the #ClutterActor's #ClutterActor:x-align and #ClutterActor:y-align properties * to fill the available size * - if the #ClutterBoxLayout:homogeneous boolean propert is set, then all widgets will * get the same size, ignoring expand settings and the preferred sizes * * It is possible to control the spacing between children of a * #ClutterBoxLayout by using clutter_box_layout_set_spacing(). * * #ClutterBoxLayout is available since Clutter 1.2 */ #include "clutter-build-config.h" #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "deprecated/clutter-alpha.h" #include "clutter-box-layout.h" #include "clutter-actor-private.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-layout-meta.h" #include "clutter-private.h" #include "clutter-types.h" #define CLUTTER_TYPE_BOX_CHILD (clutter_box_child_get_type ()) #define CLUTTER_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BOX_CHILD, ClutterBoxChild)) #define CLUTTER_IS_BOX_CHILD(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BOX_CHILD)) typedef struct _ClutterBoxChild ClutterBoxChild; typedef struct _ClutterLayoutMetaClass ClutterBoxChildClass; struct _ClutterBoxLayoutPrivate { ClutterContainer *container; guint spacing; gulong easing_mode; guint easing_duration; ClutterOrientation orientation; guint is_pack_start : 1; guint use_animations : 1; guint is_homogeneous : 1; }; struct _ClutterBoxChild { ClutterLayoutMeta parent_instance; ClutterBoxAlignment x_align; ClutterBoxAlignment y_align; guint x_fill : 1; guint y_fill : 1; guint expand : 1; }; enum { PROP_CHILD_0, PROP_CHILD_X_ALIGN, PROP_CHILD_Y_ALIGN, PROP_CHILD_X_FILL, PROP_CHILD_Y_FILL, PROP_CHILD_EXPAND, PROP_CHILD_LAST }; enum { PROP_0, PROP_SPACING, PROP_VERTICAL, PROP_HOMOGENEOUS, PROP_PACK_START, PROP_USE_ANIMATIONS, PROP_EASING_MODE, PROP_EASING_DURATION, PROP_ORIENTATION, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; GType clutter_box_child_get_type (void); G_DEFINE_TYPE (ClutterBoxChild, clutter_box_child, CLUTTER_TYPE_LAYOUT_META) G_DEFINE_TYPE_WITH_PRIVATE (ClutterBoxLayout, clutter_box_layout, CLUTTER_TYPE_LAYOUT_MANAGER) typedef struct _RequestedSize { ClutterActor *actor; gfloat minimum_size; gfloat natural_size; } RequestedSize; static float distribute_natural_allocation (float extra_space, unsigned int n_requested_sizes, RequestedSize *sizes); static void count_expand_children (ClutterLayoutManager *layout, ClutterContainer *container, gint *visible_children, gint *expand_children); /* * ClutterBoxChild */ static void box_child_set_align (ClutterBoxChild *self, ClutterBoxAlignment x_align, ClutterBoxAlignment y_align) { gboolean x_changed = FALSE, y_changed = FALSE; if (self->x_align != x_align) { self->x_align = x_align; x_changed = TRUE; } if (self->y_align != y_align) { self->y_align = y_align; y_changed = TRUE; } if (x_changed || y_changed) { ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-align"); if (y_changed) g_object_notify (G_OBJECT (self), "y-align"); } } static void box_child_set_fill (ClutterBoxChild *self, gboolean x_fill, gboolean y_fill) { gboolean x_changed = FALSE, y_changed = FALSE; if (self->x_fill != x_fill) { self->x_fill = x_fill; x_changed = TRUE; } if (self->y_fill != y_fill) { self->y_fill = y_fill; y_changed = TRUE; } if (x_changed || y_changed) { ClutterLayoutManager *layout; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); clutter_layout_manager_layout_changed (layout); if (x_changed) g_object_notify (G_OBJECT (self), "x-fill"); if (y_changed) g_object_notify (G_OBJECT (self), "y-fill"); } } static void box_child_set_expand (ClutterBoxChild *self, gboolean expand) { if (self->expand != expand) { ClutterLayoutManager *layout; self->expand = expand; layout = clutter_layout_meta_get_manager (CLUTTER_LAYOUT_META (self)); clutter_layout_manager_layout_changed (layout); g_object_notify (G_OBJECT (self), "expand"); } } static void clutter_box_child_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); switch (prop_id) { case PROP_CHILD_X_ALIGN: box_child_set_align (self, g_value_get_enum (value), self->y_align); break; case PROP_CHILD_Y_ALIGN: box_child_set_align (self, self->x_align, g_value_get_enum (value)); break; case PROP_CHILD_X_FILL: box_child_set_fill (self, g_value_get_boolean (value), self->y_fill); break; case PROP_CHILD_Y_FILL: box_child_set_fill (self, self->x_fill, g_value_get_boolean (value)); break; case PROP_CHILD_EXPAND: box_child_set_expand (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_child_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBoxChild *self = CLUTTER_BOX_CHILD (gobject); switch (prop_id) { case PROP_CHILD_X_ALIGN: g_value_set_enum (value, self->x_align); break; case PROP_CHILD_Y_ALIGN: g_value_set_enum (value, self->y_align); break; case PROP_CHILD_X_FILL: g_value_set_boolean (value, self->x_fill); break; case PROP_CHILD_Y_FILL: g_value_set_boolean (value, self->y_fill); break; case PROP_CHILD_EXPAND: g_value_set_boolean (value, self->expand); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_child_class_init (ClutterBoxChildClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = clutter_box_child_set_property; gobject_class->get_property = clutter_box_child_get_property; pspec = g_param_spec_boolean ("expand", P_("Expand"), P_("Allocate extra space for the child"), FALSE, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD_EXPAND, pspec); pspec = g_param_spec_boolean ("x-fill", P_("Horizontal Fill"), P_("Whether the child should receive priority " "when the container is allocating spare space " "on the horizontal axis"), FALSE, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD_X_FILL, pspec); pspec = g_param_spec_boolean ("y-fill", P_("Vertical Fill"), P_("Whether the child should receive priority " "when the container is allocating spare space " "on the vertical axis"), FALSE, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD_Y_FILL, pspec); pspec = g_param_spec_enum ("x-align", P_("Horizontal Alignment"), P_("Horizontal alignment of the actor within " "the cell"), CLUTTER_TYPE_BOX_ALIGNMENT, CLUTTER_BOX_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD_X_ALIGN, pspec); pspec = g_param_spec_enum ("y-align", P_("Vertical Alignment"), P_("Vertical alignment of the actor within " "the cell"), CLUTTER_TYPE_BOX_ALIGNMENT, CLUTTER_BOX_ALIGNMENT_CENTER, CLUTTER_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_CHILD_Y_ALIGN, pspec); } static void clutter_box_child_init (ClutterBoxChild *self) { self->x_align = CLUTTER_BOX_ALIGNMENT_CENTER; self->y_align = CLUTTER_BOX_ALIGNMENT_CENTER; self->x_fill = self->y_fill = FALSE; self->expand = FALSE; } static gdouble get_box_alignment_factor (ClutterBoxAlignment alignment) { switch (alignment) { case CLUTTER_BOX_ALIGNMENT_CENTER: return 0.5; case CLUTTER_BOX_ALIGNMENT_START: return 0.0; case CLUTTER_BOX_ALIGNMENT_END: return 1.0; } return 0.0; } static GType clutter_box_layout_get_child_meta_type (ClutterLayoutManager *manager) { return CLUTTER_TYPE_BOX_CHILD; } static void clutter_box_layout_set_container (ClutterLayoutManager *layout, ClutterContainer *container) { ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv; ClutterLayoutManagerClass *parent_class; priv->container = container; if (priv->container != NULL) { ClutterRequestMode request_mode; /* we need to change the :request-mode of the container * to match the orientation */ request_mode = priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), request_mode); } parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_box_layout_parent_class); parent_class->set_container (layout, container); } static void get_child_size (ClutterActor *actor, ClutterOrientation orientation, gfloat for_size, gfloat *min_size_p, gfloat *natural_size_p) { if (orientation == CLUTTER_ORIENTATION_HORIZONTAL) clutter_actor_get_preferred_width (actor, for_size, min_size_p, natural_size_p); else clutter_actor_get_preferred_height (actor, for_size, min_size_p, natural_size_p); } /* Handle the request in the orientation of the box (i.e. width request of horizontal box) */ static void get_preferred_size_for_orientation (ClutterBoxLayout *self, ClutterActor *container, gfloat for_size, gfloat *min_size_p, gfloat *natural_size_p) { ClutterBoxLayoutPrivate *priv = self->priv; ClutterActorIter iter; ClutterActor *child; gint n_children = 0; gfloat minimum, natural; float largest_min_size, largest_nat_size; minimum = natural = 0; largest_min_size = largest_nat_size = 0; clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min = 0, child_nat = 0; if (!clutter_actor_is_visible (child)) continue; n_children++; get_child_size (child, priv->orientation, for_size, &child_min, &child_nat); if (priv->is_homogeneous) { largest_min_size = MAX (largest_min_size, child_min); largest_nat_size = MAX (largest_nat_size, child_nat); } else { minimum += child_min; natural += child_nat; } } if (priv->is_homogeneous) { minimum = largest_min_size * n_children; natural = largest_nat_size * n_children; } if (n_children > 1) { minimum += priv->spacing * (n_children - 1); natural += priv->spacing * (n_children - 1); } if (min_size_p) *min_size_p = minimum; if (natural_size_p) *natural_size_p = natural; } static void get_base_size_for_opposite_orientation (ClutterBoxLayout *self, ClutterActor *container, gfloat *min_size_p, gfloat *natural_size_p) { ClutterBoxLayoutPrivate *priv = self->priv; ClutterActorIter iter; ClutterActor *child; gint n_children = 0; gfloat minimum, natural; ClutterOrientation opposite_orientation = priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL ? CLUTTER_ORIENTATION_VERTICAL : CLUTTER_ORIENTATION_HORIZONTAL; minimum = natural = 0; clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min = 0, child_nat = 0; if (!clutter_actor_is_visible (child)) continue; n_children++; get_child_size (child, opposite_orientation, -1, &child_min, &child_nat); minimum = MAX (minimum, child_min); natural = MAX (natural, child_nat); } if (min_size_p) *min_size_p = minimum; if (natural_size_p) *natural_size_p = natural; } /* Handle the request in the opposite orientation of the box * (i.e. height request of horizontal box) * * This operation requires a virtual allocation in the natural * orientation of the box, after that each element must be asked * for the size-for-virtually-allocated-size and the maximums of * each child sample will be reported as the overall * "size-for-size-in-opposite-orientation" */ static void get_preferred_size_for_opposite_orientation (ClutterBoxLayout *self, ClutterActor *container, gfloat for_size, gfloat *min_size_p, gfloat *natural_size_p) { ClutterLayoutManager *layout = CLUTTER_LAYOUT_MANAGER (self); ClutterBoxLayoutPrivate *priv = self->priv; ClutterContainer *real_container = CLUTTER_CONTAINER (container); ClutterActor *child; ClutterActorIter iter; gint nvis_children = 0, n_extra_widgets = 0; gint nexpand_children = 0, i; RequestedSize *sizes; gfloat minimum, natural, size, extra = 0; ClutterOrientation opposite_orientation = priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL ? CLUTTER_ORIENTATION_VERTICAL : CLUTTER_ORIENTATION_HORIZONTAL; minimum = natural = 0; count_expand_children (layout, real_container, &nvis_children, &nexpand_children); if (nvis_children < 1) { if (min_size_p) *min_size_p = 0; if (natural_size_p) *natural_size_p = 0; return; } /* First collect the requested sizes in the natural orientation of the box */ sizes = g_newa (RequestedSize, nvis_children); size = for_size; i = 0; clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; get_child_size (child, priv->orientation, -1, &sizes[i].minimum_size, &sizes[i].natural_size); size -= sizes[i].minimum_size; i++; } if (priv->is_homogeneous) { size = for_size - (nvis_children - 1) * priv->spacing; extra = size / nvis_children; n_extra_widgets = ((gint)size) % nvis_children; } else { size -= (nvis_children - 1) * priv->spacing; /* Bring children up to size first */ if (isnormal (size) || size == 0) { size = distribute_natural_allocation (MAX (0, size), nvis_children, sizes); } else { g_critical ("Actor %s (%p) received the invalid " "value %f as minimum/natural size\n", G_OBJECT_TYPE_NAME (container), container, size); size = 0; } /* Calculate space which hasn't distributed yet, * and is available for expanding children. */ if (nexpand_children > 0) { extra = size / nexpand_children; n_extra_widgets = ((gint)size) % nexpand_children; } } /* Distribute expand space to children */ i = 0; clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { ClutterLayoutMeta *meta; ClutterBoxChild *box_child; /* If widget is not visible, skip it. */ if (!clutter_actor_is_visible (child)) continue; meta = clutter_layout_manager_get_child_meta (layout, real_container, child); box_child = CLUTTER_BOX_CHILD (meta); if (priv->is_homogeneous) { sizes[i].minimum_size = extra; if (n_extra_widgets > 0) { sizes[i].minimum_size++; n_extra_widgets--; } } else { if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand) { sizes[i].minimum_size += extra; if (n_extra_widgets > 0) { sizes[i].minimum_size++; n_extra_widgets--; } } } i++; } /* Virtual allocation finished, now we can finally ask for the right size-for-size */ i = 0; clutter_actor_iter_init (&iter, container); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min = 0, child_nat = 0; if (!clutter_actor_is_visible (child)) continue; get_child_size (child, opposite_orientation, sizes[i].minimum_size, &child_min, &child_nat); minimum = MAX (minimum, child_min); natural = MAX (natural, child_nat); i++; } if (min_size_p) *min_size_p = minimum; if (natural_size_p) *natural_size_p = natural; } static void allocate_box_child (ClutterBoxLayout *self, ClutterContainer *container, ClutterActor *child, ClutterActorBox *child_box, ClutterAllocationFlags flags) { ClutterBoxLayoutPrivate *priv = self->priv; ClutterBoxChild *box_child; ClutterLayoutMeta *meta; meta = clutter_layout_manager_get_child_meta (CLUTTER_LAYOUT_MANAGER (self), container, child); box_child = CLUTTER_BOX_CHILD (meta); CLUTTER_NOTE (LAYOUT, "Allocation for %s { %.2f, %.2f, %.2f, %.2f }", _clutter_actor_get_debug_name (child), child_box->x1, child_box->y1, child_box->x2 - child_box->x1, child_box->y2 - child_box->y1); if (priv->use_animations) { clutter_actor_save_easing_state (child); clutter_actor_set_easing_mode (child, priv->easing_mode); clutter_actor_set_easing_duration (child, priv->easing_duration); } /* call allocate() instead of allocate_align_fill() if the actor needs * expand in either direction. this will honour the actors alignment settings */ if (clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL) || clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) clutter_actor_allocate (child, child_box, flags); else clutter_actor_allocate_align_fill (child, child_box, get_box_alignment_factor (box_child->x_align), get_box_alignment_factor (box_child->y_align), box_child->x_fill, box_child->y_fill, flags); if (priv->use_animations) clutter_actor_restore_easing_state (child); } static void clutter_box_layout_get_preferred_width (ClutterLayoutManager *layout, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); ClutterBoxLayoutPrivate *priv = self->priv; if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) { if (for_height < 0) get_base_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), min_width_p, natural_width_p); else get_preferred_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), for_height, min_width_p, natural_width_p); } else get_preferred_size_for_orientation (self, CLUTTER_ACTOR (container), for_height, min_width_p, natural_width_p); } static void clutter_box_layout_get_preferred_height (ClutterLayoutManager *layout, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (layout); ClutterBoxLayoutPrivate *priv = self->priv; if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL) { if (for_width < 0) get_base_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), min_height_p, natural_height_p); else get_preferred_size_for_opposite_orientation (self, CLUTTER_ACTOR (container), for_width, min_height_p, natural_height_p); } else get_preferred_size_for_orientation (self, CLUTTER_ACTOR (container), for_width, min_height_p, natural_height_p); } static void count_expand_children (ClutterLayoutManager *layout, ClutterContainer *container, gint *visible_children, gint *expand_children) { ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv; ClutterActor *actor, *child; ClutterActorIter iter; actor = CLUTTER_ACTOR (container); *visible_children = *expand_children = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { if (clutter_actor_is_visible (child)) { ClutterLayoutMeta *meta; *visible_children += 1; meta = clutter_layout_manager_get_child_meta (layout, container, child); if (clutter_actor_needs_expand (child, priv->orientation) || CLUTTER_BOX_CHILD (meta)->expand) *expand_children += 1; } } } /* Pulled from gtksizerequest.c from Gtk+ */ static gint compare_gap (gconstpointer p1, gconstpointer p2, gpointer data) { RequestedSize *sizes = data; const guint *c1 = p1; const guint *c2 = p2; const gint d1 = MAX (sizes[*c1].natural_size - sizes[*c1].minimum_size, 0); const gint d2 = MAX (sizes[*c2].natural_size - sizes[*c2].minimum_size, 0); gint delta = (d2 - d1); if (0 == delta) delta = (*c2 - *c1); return delta; } /* * distribute_natural_allocation: * @extra_space: Extra space to redistribute among children after subtracting * minimum sizes and any child padding from the overall allocation * @n_requested_sizes: Number of requests to fit into the allocation * @sizes: An array of structs with a client pointer and a minimum/natural size * in the orientation of the allocation. * * Distributes @extra_space to child @sizes by bringing smaller * children up to natural size first. * * The remaining space will be added to the @minimum_size member of the * RequestedSize struct. If all sizes reach their natural size then * the remaining space is returned. * * Returns: The remainder of @extra_space after redistributing space * to @sizes. * * Pulled from gtksizerequest.c from Gtk+ */ static float distribute_natural_allocation (float extra_space, unsigned int n_requested_sizes, RequestedSize *sizes) { unsigned int *spreading; int i; g_return_val_if_fail (isnormal (extra_space) || extra_space == 0, 0); g_return_val_if_fail (extra_space >= 0, 0); spreading = g_newa (unsigned int, n_requested_sizes); for (i = 0; i < n_requested_sizes; i++) spreading[i] = i; /* Distribute the container's extra space c_gap. We want to assign * this space such that the sum of extra space assigned to children * (c^i_gap) is equal to c_cap. The case that there's not enough * space for all children to take their natural size needs some * attention. The goals we want to achieve are: * * a) Maximize number of children taking their natural size. * b) The allocated size of children should be a continuous * function of c_gap. That is, increasing the container size by * one pixel should never make drastic changes in the distribution. * c) If child i takes its natural size and child j doesn't, * child j should have received at least as much gap as child i. * * The following code distributes the additional space by following * these rules. */ /* Sort descending by gap and position. */ g_qsort_with_data (spreading, n_requested_sizes, sizeof (unsigned int), compare_gap, sizes); /* Distribute available space. * This master piece of a loop was conceived by Behdad Esfahbod. */ for (i = n_requested_sizes - 1; extra_space > 0 && i >= 0; --i) { /* Divide remaining space by number of remaining children. * Sort order and reducing remaining space by assigned space * ensures that space is distributed equally. */ int glue = (extra_space + i) / (i + 1); int gap = sizes[(spreading[i])].natural_size - sizes[(spreading[i])].minimum_size; int extra = MIN (glue, gap); sizes[spreading[i]].minimum_size += extra; extra_space -= extra; } return extra_space; } /* Pulled from gtkbox.c from Gtk+ */ static void clutter_box_layout_allocate (ClutterLayoutManager *layout, ClutterContainer *container, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (layout)->priv; ClutterActor *actor, *child; gint nvis_children; gint nexpand_children; gboolean is_rtl; ClutterActorIter iter; ClutterActorBox child_allocation; RequestedSize *sizes; gint size; gint extra; gint n_extra_widgets = 0; /* Number of widgets that receive 1 extra px */ gint x = 0, y = 0, i; gfloat child_size; count_expand_children (layout, container, &nvis_children, &nexpand_children); CLUTTER_NOTE (LAYOUT, "BoxLayout for %s: visible=%d, expand=%d", _clutter_actor_get_debug_name (CLUTTER_ACTOR (container)), nvis_children, nexpand_children); /* If there is no visible child, simply return. */ if (nvis_children <= 0) return; sizes = g_newa (RequestedSize, nvis_children); if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) size = box->y2 - box->y1 - (nvis_children - 1) * priv->spacing; else size = box->x2 - box->x1 - (nvis_children - 1) * priv->spacing; actor = CLUTTER_ACTOR (container); /* Retrieve desired size for visible children. */ i = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { if (!clutter_actor_is_visible (child)) continue; if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) clutter_actor_get_preferred_height (child, box->x2 - box->x1, &sizes[i].minimum_size, &sizes[i].natural_size); else clutter_actor_get_preferred_width (child, box->y2 - box->y1, &sizes[i].minimum_size, &sizes[i].natural_size); /* Assert the api is working properly */ if (sizes[i].minimum_size < 0) { g_critical ("ClutterBoxLayout child %s minimum %s: %f < 0 for %s %f", _clutter_actor_get_debug_name (child), priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? "height" : "width", sizes[i].minimum_size, priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? "width" : "height", priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? box->x2 - box->x1 : box->y2 - box->y1); sizes[i].minimum_size = 0; } if (sizes[i].natural_size < sizes[i].minimum_size) { g_critical ("ClutterBoxLayout child %s natural %s: %f < minimum %f for %s %f", _clutter_actor_get_debug_name (child), priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? "height" : "width", sizes[i].natural_size, sizes[i].minimum_size, priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? "width" : "height", priv->orientation == CLUTTER_ORIENTATION_VERTICAL ? box->x2 - box->x1 : box->y2 - box->y1); sizes[i].natural_size = sizes[i].minimum_size; } size -= sizes[i].minimum_size; sizes[i].actor = child; i += 1; } if (priv->is_homogeneous) { /* If were homogenous we still need to run the above loop to get the * minimum sizes for children that are not going to fill */ if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) size = box->y2 - box->y1 - (nvis_children - 1) * priv->spacing; else size = box->x2 - box->x1 - (nvis_children - 1) * priv->spacing; extra = size / nvis_children; n_extra_widgets = size % nvis_children; } else { /* Bring children up to size first */ size = (gint) distribute_natural_allocation (MAX (0, (float) size), nvis_children, sizes); /* Calculate space which hasn't distributed yet, * and is available for expanding children. */ if (nexpand_children > 0) { extra = size / nexpand_children; n_extra_widgets = size % nexpand_children; } else extra = 0; } if (priv->orientation == CLUTTER_ORIENTATION_HORIZONTAL) { ClutterTextDirection text_dir; text_dir = clutter_actor_get_text_direction (CLUTTER_ACTOR (container)); is_rtl = (text_dir == CLUTTER_TEXT_DIRECTION_RTL) ? TRUE : FALSE; } else is_rtl = FALSE; /* Allocate child positions. */ if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) { child_allocation.x1 = box->x1; child_allocation.x2 = MAX (1.0, box->x2); if (priv->is_pack_start) y = box->y2 - box->y1; else y = box->y1; } else { child_allocation.y1 = box->y1; child_allocation.y2 = MAX (1.0, box->y2); if (priv->is_pack_start) x = box->x2 - box->x1; else x = box->x1; } i = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterLayoutMeta *meta; ClutterBoxChild *box_child; /* If widget is not visible, skip it. */ if (!clutter_actor_is_visible (child)) continue; meta = clutter_layout_manager_get_child_meta (layout, container, child); box_child = CLUTTER_BOX_CHILD (meta); /* Assign the child's size. */ if (priv->is_homogeneous) { child_size = extra; if (n_extra_widgets > 0) { child_size++; n_extra_widgets--; } } else { child_size = sizes[i].minimum_size; if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand) { child_size += extra; if (n_extra_widgets > 0) { child_size++; n_extra_widgets--; } } } /* Assign the child's position. */ if (priv->orientation == CLUTTER_ORIENTATION_VERTICAL) { if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand) { child_allocation.y1 = y; child_allocation.y2 = child_allocation.y1 + MAX (1.0, child_size); } else { child_allocation.y1 = y + (child_size - sizes[i].minimum_size) / 2; child_allocation.y2 = child_allocation.y1 + sizes[i].minimum_size; } if (priv->is_pack_start) { y -= child_size + priv->spacing; child_allocation.y1 -= child_size; child_allocation.y2 -= child_size; } else { y += child_size + priv->spacing; } } else /* CLUTTER_ORIENTATION_HORIZONTAL */ { if (clutter_actor_needs_expand (child, priv->orientation) || box_child->expand) { child_allocation.x1 = x; child_allocation.x2 = child_allocation.x1 + MAX (1.0, child_size); } else { child_allocation.x1 = x + (child_size - sizes[i].minimum_size) / 2; child_allocation.x2 = child_allocation.x1 + sizes[i].minimum_size; } if (priv->is_pack_start) { x -= child_size + priv->spacing; child_allocation.x1 -= child_size; child_allocation.x2 -= child_size; } else { x += child_size + priv->spacing; } if (is_rtl) { gfloat width = child_allocation.x2 - child_allocation.x1; child_allocation.x2 = box->x1 + (box->x2 - child_allocation.x1); child_allocation.x1 = child_allocation.x2 - width; } } allocate_box_child (CLUTTER_BOX_LAYOUT (layout), container, child, &child_allocation, flags); i += 1; } } static void clutter_box_layout_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterBoxLayout *self = CLUTTER_BOX_LAYOUT (gobject); switch (prop_id) { case PROP_VERTICAL: clutter_box_layout_set_vertical (self, g_value_get_boolean (value)); break; case PROP_ORIENTATION: clutter_box_layout_set_orientation (self, g_value_get_enum (value)); break; case PROP_HOMOGENEOUS: clutter_box_layout_set_homogeneous (self, g_value_get_boolean (value)); break; case PROP_SPACING: clutter_box_layout_set_spacing (self, g_value_get_uint (value)); break; case PROP_PACK_START: clutter_box_layout_set_pack_start (self, g_value_get_boolean (value)); break; case PROP_USE_ANIMATIONS: clutter_box_layout_set_use_animations (self, g_value_get_boolean (value)); break; case PROP_EASING_MODE: clutter_box_layout_set_easing_mode (self, g_value_get_ulong (value)); break; case PROP_EASING_DURATION: clutter_box_layout_set_easing_duration (self, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_layout_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterBoxLayoutPrivate *priv = CLUTTER_BOX_LAYOUT (gobject)->priv; switch (prop_id) { case PROP_VERTICAL: g_value_set_boolean (value, priv->orientation == CLUTTER_ORIENTATION_VERTICAL); break; case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; case PROP_HOMOGENEOUS: g_value_set_boolean (value, priv->is_homogeneous); break; case PROP_SPACING: g_value_set_uint (value, priv->spacing); break; case PROP_PACK_START: g_value_set_boolean (value, priv->is_pack_start); break; case PROP_USE_ANIMATIONS: g_value_set_boolean (value, priv->use_animations); break; case PROP_EASING_MODE: g_value_set_ulong (value, priv->easing_mode); break; case PROP_EASING_DURATION: g_value_set_uint (value, priv->easing_duration); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_box_layout_class_init (ClutterBoxLayoutClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterLayoutManagerClass *layout_class; layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); layout_class->get_preferred_width = clutter_box_layout_get_preferred_width; layout_class->get_preferred_height = clutter_box_layout_get_preferred_height; layout_class->allocate = clutter_box_layout_allocate; layout_class->set_container = clutter_box_layout_set_container; layout_class->get_child_meta_type = clutter_box_layout_get_child_meta_type; /** * ClutterBoxLayout:vertical: * * Whether the #ClutterBoxLayout should arrange its children * alongside the Y axis, instead of alongside the X axis * * Since: 1.2 * * Deprecated: 1.12: Use #ClutterBoxLayout:orientation instead. */ obj_props[PROP_VERTICAL] = g_param_spec_boolean ("vertical", P_("Vertical"), P_("Whether the layout should be vertical, " "rather than horizontal"), FALSE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); /** * ClutterBoxLayout:orientation: * * The orientation of the #ClutterBoxLayout, either horizontal * or vertical * * Since: 1.12 */ obj_props[PROP_ORIENTATION] = g_param_spec_enum ("orientation", P_("Orientation"), P_("The orientation of the layout"), CLUTTER_TYPE_ORIENTATION, CLUTTER_ORIENTATION_HORIZONTAL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); /** * ClutterBoxLayout:homogeneous: * * Whether the #ClutterBoxLayout should arrange its children * homogeneously, i.e. all children get the same size * * Since: 1.4 */ obj_props[PROP_HOMOGENEOUS] = g_param_spec_boolean ("homogeneous", P_("Homogeneous"), P_("Whether the layout should be homogeneous, " "i.e. all children get the same size"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterBoxLayout:pack-start: * * Whether the #ClutterBoxLayout should pack items at the start * or append them at the end * * Since: 1.2 */ obj_props[PROP_PACK_START] = g_param_spec_boolean ("pack-start", P_("Pack Start"), P_("Whether to pack items at the start of the box"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterBoxLayout:spacing: * * The spacing between children of the #ClutterBoxLayout, in pixels * * Since: 1.2 */ obj_props[PROP_SPACING] = g_param_spec_uint ("spacing", P_("Spacing"), P_("Spacing between children"), 0, G_MAXUINT, 0, CLUTTER_PARAM_READWRITE); /** * ClutterBoxLayout:use-animations: * * Whether the #ClutterBoxLayout should animate changes in the * layout, overriding the easing state of the children. * * Since: 1.2 * * Deprecated: 1.12: #ClutterBoxLayout will honour the easing state * of the children when allocating them. */ obj_props[PROP_USE_ANIMATIONS] = g_param_spec_boolean ("use-animations", P_("Use Animations"), P_("Whether layout changes should be animated"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterBoxLayout:easing-mode: * * The easing mode for the animations, in case * #ClutterBoxLayout:use-animations is set to %TRUE. * * The easing mode has the same semantics of #ClutterAnimation:mode: it can * either be a value from the #ClutterAnimationMode enumeration, like * %CLUTTER_EASE_OUT_CUBIC, or a logical id as returned by * clutter_alpha_register_func(). * * The default value is %CLUTTER_EASE_OUT_CUBIC. * * Since: 1.2 * * Deprecated: 1.12: The #ClutterBoxLayout will honour the easing state of * the children when allocating them. */ obj_props[PROP_EASING_MODE] = g_param_spec_ulong ("easing-mode", P_("Easing Mode"), P_("The easing mode of the animations"), 0, G_MAXULONG, CLUTTER_EASE_OUT_CUBIC, CLUTTER_PARAM_READWRITE); /** * ClutterBoxLayout:easing-duration: * * The duration of the animations, in case #ClutterBoxLayout:use-animations * is set to %TRUE. * * The duration is expressed in milliseconds. * * Since: 1.2 * * Deprecated: 1.12: The #ClutterBoxLayout will honour the easing state of * the children when allocating them. */ obj_props[PROP_EASING_DURATION] = g_param_spec_uint ("easing-duration", P_("Easing Duration"), P_("The duration of the animations"), 0, G_MAXUINT, 500, CLUTTER_PARAM_READWRITE); gobject_class->set_property = clutter_box_layout_set_property; gobject_class->get_property = clutter_box_layout_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_box_layout_init (ClutterBoxLayout *self) { self->priv = clutter_box_layout_get_instance_private (self); self->priv->orientation = CLUTTER_ORIENTATION_HORIZONTAL; self->priv->is_homogeneous = FALSE; self->priv->is_pack_start = FALSE; self->priv->spacing = 0; self->priv->use_animations = FALSE; self->priv->easing_mode = CLUTTER_EASE_OUT_CUBIC; self->priv->easing_duration = 500; } /** * clutter_box_layout_new: * * Creates a new #ClutterBoxLayout layout manager * * Return value: the newly created #ClutterBoxLayout * * Since: 1.2 */ ClutterLayoutManager * clutter_box_layout_new (void) { return g_object_new (CLUTTER_TYPE_BOX_LAYOUT, NULL); } /** * clutter_box_layout_set_spacing: * @layout: a #ClutterBoxLayout * @spacing: the spacing between children of the layout, in pixels * * Sets the spacing between children of @layout * * Since: 1.2 */ void clutter_box_layout_set_spacing (ClutterBoxLayout *layout, guint spacing) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->spacing != spacing) { ClutterLayoutManager *manager; priv->spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "spacing"); } } /** * clutter_box_layout_get_spacing: * @layout: a #ClutterBoxLayout * * Retrieves the spacing set using clutter_box_layout_set_spacing() * * Return value: the spacing between children of the #ClutterBoxLayout * * Since: 1.2 */ guint clutter_box_layout_get_spacing (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 0); return layout->priv->spacing; } /** * clutter_box_layout_set_vertical: * @layout: a #ClutterBoxLayout * @vertical: %TRUE if the layout should be vertical * * Sets whether @layout should arrange its children vertically alongside * the Y axis, instead of horizontally alongside the X axis * * Since: 1.2 * * Deprecated: 1.12: Use clutter_box_layout_set_orientation() instead. */ void clutter_box_layout_set_vertical (ClutterBoxLayout *layout, gboolean vertical) { ClutterOrientation new_orientation, old_orientation; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); old_orientation = layout->priv->orientation; new_orientation = vertical ? CLUTTER_ORIENTATION_VERTICAL : CLUTTER_ORIENTATION_HORIZONTAL; clutter_box_layout_set_orientation (layout, new_orientation); if (old_orientation != new_orientation) g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_VERTICAL]); } /** * clutter_box_layout_set_orientation: * @layout: a #ClutterBoxLayout * @orientation: the orientation of the #ClutterBoxLayout * * Sets the orientation of the #ClutterBoxLayout layout manager. * * Since: 1.12 */ void clutter_box_layout_set_orientation (ClutterBoxLayout *layout, ClutterOrientation orientation) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->orientation == orientation) return; priv->orientation = orientation; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (layout), obj_props[PROP_ORIENTATION]); } /** * clutter_box_layout_get_vertical: * @layout: a #ClutterBoxLayout * * Retrieves the orientation of the @layout as set using the * clutter_box_layout_set_vertical() function * * Return value: %TRUE if the #ClutterBoxLayout is arranging its children * vertically, and %FALSE otherwise * * Since: 1.2 * * Deprecated: 1.12: Use clutter_box_layout_get_orientation() instead */ gboolean clutter_box_layout_get_vertical (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); return layout->priv->orientation == CLUTTER_ORIENTATION_VERTICAL; } /** * clutter_box_layout_get_orientation: * @layout: a #ClutterBoxLayout * * Retrieves the orientation of the @layout. * * Return value: the orientation of the layout * * Since: 1.12 */ ClutterOrientation clutter_box_layout_get_orientation (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), CLUTTER_ORIENTATION_HORIZONTAL); return layout->priv->orientation; } /** * clutter_box_layout_set_homogeneous: * @layout: a #ClutterBoxLayout * @homogeneous: %TRUE if the layout should be homogeneous * * Sets whether the size of @layout children should be * homogeneous * * Since: 1.4 */ void clutter_box_layout_set_homogeneous (ClutterBoxLayout *layout, gboolean homogeneous) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->is_homogeneous != homogeneous) { ClutterLayoutManager *manager; priv->is_homogeneous = !!homogeneous; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "homogeneous"); } } /** * clutter_box_layout_get_homogeneous: * @layout: a #ClutterBoxLayout * * Retrieves if the children sizes are allocated homogeneously. * * Return value: %TRUE if the #ClutterBoxLayout is arranging its children * homogeneously, and %FALSE otherwise * * Since: 1.4 */ gboolean clutter_box_layout_get_homogeneous (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); return layout->priv->is_homogeneous; } /** * clutter_box_layout_set_pack_start: * @layout: a #ClutterBoxLayout * @pack_start: %TRUE if the @layout should pack children at the * beginning of the layout * * Sets whether children of @layout should be layed out by appending * them or by prepending them * * Since: 1.2 */ void clutter_box_layout_set_pack_start (ClutterBoxLayout *layout, gboolean pack_start) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->is_pack_start != pack_start) { ClutterLayoutManager *manager; priv->is_pack_start = pack_start ? TRUE : FALSE; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify (G_OBJECT (layout), "pack-start"); } } /** * clutter_box_layout_get_pack_start: * @layout: a #ClutterBoxLayout * * Retrieves the value set using clutter_box_layout_set_pack_start() * * Return value: %TRUE if the #ClutterBoxLayout should pack children * at the beginning of the layout, and %FALSE otherwise * * Since: 1.2 */ gboolean clutter_box_layout_get_pack_start (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); return layout->priv->is_pack_start; } /** * clutter_box_layout_pack: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor * @expand: whether the @actor should expand * @x_fill: whether the @actor should fill horizontally * @y_fill: whether the @actor should fill vertically * @x_align: the horizontal alignment policy for @actor * @y_align: the vertical alignment policy for @actor * * Packs @actor inside the #ClutterContainer associated to @layout * and sets the layout properties * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout honours #ClutterActor's * align and expand properties. The preferred way is adding * the @actor with clutter_actor_add_child() and setting * #ClutterActor:x-align, #ClutterActor:y-align, * #ClutterActor:x-expand and #ClutterActor:y-expand */ void clutter_box_layout_pack (ClutterBoxLayout *layout, ClutterActor *actor, gboolean expand, gboolean x_fill, gboolean y_fill, ClutterBoxAlignment x_align, ClutterBoxAlignment y_align) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before adding children", G_OBJECT_TYPE_NAME (layout)); return; } clutter_container_add_actor (priv->container, actor); manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); g_assert (CLUTTER_IS_BOX_CHILD (meta)); box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align); box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill); box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand); } /** * clutter_box_layout_set_alignment: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * @x_align: Horizontal alignment policy for @actor * @y_align: Vertical alignment policy for @actor * * Sets the horizontal and vertical alignment policies for @actor * inside @layout * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-align and #ClutterActor:y-align properies */ void clutter_box_layout_set_alignment (ClutterBoxLayout *layout, ClutterActor *actor, ClutterBoxAlignment x_align, ClutterBoxAlignment y_align) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); box_child_set_align (CLUTTER_BOX_CHILD (meta), x_align, y_align); } /** * clutter_box_layout_get_alignment: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * @x_align: (out): return location for the horizontal alignment policy * @y_align: (out): return location for the vertical alignment policy * * Retrieves the horizontal and vertical alignment policies for @actor * as set using clutter_box_layout_pack() or clutter_box_layout_set_alignment() * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-align and #ClutterActor:y-align properies */ void clutter_box_layout_get_alignment (ClutterBoxLayout *layout, ClutterActor *actor, ClutterBoxAlignment *x_align, ClutterBoxAlignment *y_align) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); if (x_align) *x_align = CLUTTER_BOX_CHILD (meta)->x_align; if (y_align) *y_align = CLUTTER_BOX_CHILD (meta)->y_align; } /** * clutter_box_layout_set_fill: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * @x_fill: whether @actor should fill horizontally the allocated space * @y_fill: whether @actor should fill vertically the allocated space * * Sets the horizontal and vertical fill policies for @actor * inside @layout * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-align and #ClutterActor:y-align properies */ void clutter_box_layout_set_fill (ClutterBoxLayout *layout, ClutterActor *actor, gboolean x_fill, gboolean y_fill) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); box_child_set_fill (CLUTTER_BOX_CHILD (meta), x_fill, y_fill); } /** * clutter_box_layout_get_fill: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * @x_fill: (out): return location for the horizontal fill policy * @y_fill: (out): return location for the vertical fill policy * * Retrieves the horizontal and vertical fill policies for @actor * as set using clutter_box_layout_pack() or clutter_box_layout_set_fill() * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-align and #ClutterActor:y-align properies */ void clutter_box_layout_get_fill (ClutterBoxLayout *layout, ClutterActor *actor, gboolean *x_fill, gboolean *y_fill) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); if (x_fill) *x_fill = CLUTTER_BOX_CHILD (meta)->x_fill; if (y_fill) *y_fill = CLUTTER_BOX_CHILD (meta)->y_fill; } /** * clutter_box_layout_set_expand: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * @expand: whether @actor should expand * * Sets whether @actor should expand inside @layout * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-expand and #ClutterActor:y-expand properies */ void clutter_box_layout_set_expand (ClutterBoxLayout *layout, ClutterActor *actor, gboolean expand) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); box_child_set_expand (CLUTTER_BOX_CHILD (meta), expand); } /** * clutter_box_layout_get_expand: * @layout: a #ClutterBoxLayout * @actor: a #ClutterActor child of @layout * * Retrieves whether @actor should expand inside @layout * * Return value: %TRUE if the #ClutterActor should expand, %FALSE otherwise * * Since: 1.2 * Deprecated: 1.12: #ClutterBoxLayout will honour #ClutterActor's * #ClutterActor:x-expand and #ClutterActor:y-expand properies */ gboolean clutter_box_layout_get_expand (ClutterBoxLayout *layout, ClutterActor *actor) { ClutterBoxLayoutPrivate *priv; ClutterLayoutManager *manager; ClutterLayoutMeta *meta; g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); priv = layout->priv; if (priv->container == NULL) { g_warning ("The layout of type '%s' must be associated to " "a ClutterContainer before querying layout " "properties", G_OBJECT_TYPE_NAME (layout)); return FALSE; } manager = CLUTTER_LAYOUT_MANAGER (layout); meta = clutter_layout_manager_get_child_meta (manager, priv->container, actor); if (meta == NULL) { g_warning ("No layout meta found for the child of type '%s' " "inside the layout manager of type '%s'", G_OBJECT_TYPE_NAME (actor), G_OBJECT_TYPE_NAME (manager)); return FALSE; } g_assert (CLUTTER_IS_BOX_CHILD (meta)); return CLUTTER_BOX_CHILD (meta)->expand; } /** * clutter_box_layout_set_use_animations: * @layout: a #ClutterBoxLayout * @animate: %TRUE if the @layout should use animations * * Sets whether @layout should animate changes in the layout properties * * The duration of the animations is controlled by * clutter_box_layout_set_easing_duration(); the easing mode to be used * by the animations is controlled by clutter_box_layout_set_easing_mode(). * * Enabling animations will override the easing state of each child * of the actor using @layout, and will use the #ClutterBoxLayout:easing-mode * and #ClutterBoxLayout:easing-duration properties instead. * * Since: 1.2 * * Deprecated: 1.12: The layout manager will honour the easing state * of the children when allocating them. */ void clutter_box_layout_set_use_animations (ClutterBoxLayout *layout, gboolean animate) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->use_animations != animate) { priv->use_animations = animate; g_object_notify (G_OBJECT (layout), "use-animations"); } } /** * clutter_box_layout_get_use_animations: * @layout: a #ClutterBoxLayout * * Retrieves whether @layout should animate changes in the layout properties. * * Return value: %TRUE if the animations should be used, %FALSE otherwise * * Since: 1.2 * * Deprecated: 1.12 */ gboolean clutter_box_layout_get_use_animations (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), FALSE); return layout->priv->use_animations; } /** * clutter_box_layout_set_easing_mode: * @layout: a #ClutterBoxLayout * @mode: an easing mode, either from #ClutterAnimationMode or a logical id * from clutter_alpha_register_func() * * Sets the easing mode to be used by @layout when animating changes in layout * properties. * * Since: 1.2 * * Deprecated: 1.12: The layout manager will honour the easing state * of the children when allocating them. */ void clutter_box_layout_set_easing_mode (ClutterBoxLayout *layout, gulong mode) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->easing_mode != mode) { priv->easing_mode = mode; g_object_notify (G_OBJECT (layout), "easing-mode"); } } /** * clutter_box_layout_get_easing_mode: * @layout: a #ClutterBoxLayout * * Retrieves the easing mode set using clutter_box_layout_set_easing_mode() * * Return value: an easing mode * * Since: 1.2 * * Deprecated: 1.12 */ gulong clutter_box_layout_get_easing_mode (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), CLUTTER_EASE_OUT_CUBIC); return layout->priv->easing_mode; } /** * clutter_box_layout_set_easing_duration: * @layout: a #ClutterBoxLayout * @msecs: the duration of the animations, in milliseconds * * Sets the duration of the animations used by @layout when animating changes * in the layout properties. * * Since: 1.2 * * Deprecated: 1.12: The layout manager will honour the easing state * of the children when allocating them. */ void clutter_box_layout_set_easing_duration (ClutterBoxLayout *layout, guint msecs) { ClutterBoxLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_BOX_LAYOUT (layout)); priv = layout->priv; if (priv->easing_duration != msecs) { priv->easing_duration = msecs; g_object_notify (G_OBJECT (layout), "easing-duration"); } } /** * clutter_box_layout_get_easing_duration: * @layout: a #ClutterBoxLayout * * Retrieves the duration set using clutter_box_layout_set_easing_duration() * * Return value: the duration of the animations, in milliseconds * * Since: 1.2 * * Deprecated: 1.12 */ guint clutter_box_layout_get_easing_duration (ClutterBoxLayout *layout) { g_return_val_if_fail (CLUTTER_IS_BOX_LAYOUT (layout), 500); return layout->priv->easing_duration; } muffin-6.4.1/clutter/clutter/clutter-page-turn-effect.c0000664000175000017500000002735614723361714022103 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on MxDeformPageTurn, written by: * Chris Lord */ /** * SECTION:clutter-page-turn-effect * @Title: ClutterPageTurnEffect * @Short_Description: A page turning effect * * A simple page turning effect * * #ClutterPageTurnEffect is available since Clutter 1.4 */ #include "clutter-build-config.h" #include #include "clutter-page-turn-effect.h" #include "clutter-debug.h" #include "clutter-private.h" #define CLUTTER_PAGE_TURN_EFFECT_CLASS(k) (G_TYPE_CHECK_CLASS_CAST ((k), CLUTTER_TYPE_PAGE_TURN_EFFECT, ClutterPageTurnEffectClass)) #define CLUTTER_IS_PAGE_TURN_EFFECT_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), CLUTTER_TYPE_PAGE_TURN_EFFECT)) #define CLUTTER_PAGE_TURN_EFFECT_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), CLUTTER_TYPE_PAGE_TURN_EFFECT, ClutterPageTurnEffectClass)) struct _ClutterPageTurnEffect { ClutterDeformEffect parent_instance; gdouble period; gdouble angle; gfloat radius; }; struct _ClutterPageTurnEffectClass { ClutterDeformEffectClass parent_class; }; enum { PROP_0, PROP_PERIOD, PROP_ANGLE, PROP_RADIUS, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterPageTurnEffect, clutter_page_turn_effect, CLUTTER_TYPE_DEFORM_EFFECT); static void clutter_page_turn_effect_deform_vertex (ClutterDeformEffect *effect, gfloat width, gfloat height, CoglTextureVertex *vertex) { ClutterPageTurnEffect *self = CLUTTER_PAGE_TURN_EFFECT (effect); gfloat cx, cy, rx, ry, radians, turn_angle; guint shade; if (self->period == 0.0) return; radians = self->angle / (180.0f / G_PI); /* Rotate the point around the centre of the page-curl ray to align it with * the y-axis. */ cx = (1.f - self->period) * width; cy = (1.f - self->period) * height; rx = ((vertex->x - cx) * cos (- radians)) - ((vertex->y - cy) * sin (- radians)) - self->radius; ry = ((vertex->x - cx) * sin (- radians)) + ((vertex->y - cy) * cos (- radians)); turn_angle = 0.f; if (rx > self->radius * -2.0f) { /* Calculate the curl angle as a function from the distance of the curl * ray (i.e. the page crease) */ turn_angle = (rx / self->radius * G_PI_2) - G_PI_2; shade = (sin (turn_angle) * 96.0f) + 159.0f; /* Add a gradient that makes it look like lighting and hides the switch * between textures. */ cogl_color_init_from_4ub (&vertex->color, shade, shade, shade, 0xff); } if (rx > 0) { /* Make the curl radius smaller as more circles are formed (stops * z-fighting and looks cool). Note that 10 is a semi-arbitrary * number here - divide it by two and it's the amount of space * between curled layers of the texture, in pixels. */ gfloat small_radius; small_radius = self->radius - MIN (self->radius, (turn_angle * 10) / G_PI); /* Calculate a point on a cylinder (maybe make this a cone at some * point) and rotate it by the specified angle. */ rx = (small_radius * cos (turn_angle)) + self->radius; vertex->x = (rx * cos (radians)) - (ry * sin (radians)) + cx; vertex->y = (rx * sin (radians)) + (ry * cos (radians)) + cy; vertex->z = (small_radius * sin (turn_angle)) + self->radius; } } static void clutter_page_turn_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterPageTurnEffect *effect = CLUTTER_PAGE_TURN_EFFECT (gobject); switch (prop_id) { case PROP_PERIOD: clutter_page_turn_effect_set_period (effect, g_value_get_double (value)); break; case PROP_ANGLE: clutter_page_turn_effect_set_angle (effect, g_value_get_double (value)); break; case PROP_RADIUS: clutter_page_turn_effect_set_radius (effect, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_page_turn_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterPageTurnEffect *effect = CLUTTER_PAGE_TURN_EFFECT (gobject); switch (prop_id) { case PROP_PERIOD: g_value_set_double (value, effect->period); break; case PROP_ANGLE: g_value_set_double (value, effect->angle); break; case PROP_RADIUS: g_value_set_float (value, effect->radius); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_page_turn_effect_class_init (ClutterPageTurnEffectClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterDeformEffectClass *deform_class = CLUTTER_DEFORM_EFFECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = clutter_page_turn_effect_set_property; gobject_class->get_property = clutter_page_turn_effect_get_property; /** * ClutterPageTurnEffect:period: * * The period of the page turn, between 0.0 (no curling) and * 1.0 (fully curled) * * Since: 1.4 */ pspec = g_param_spec_double ("period", "Period", "The period of the page turn", 0.0, 1.0, 0.0, CLUTTER_PARAM_READWRITE); obj_props[PROP_PERIOD] = pspec; g_object_class_install_property (gobject_class, PROP_PERIOD, pspec); /** * ClutterPageTurnEffect:angle: * * The angle of the page rotation, in degrees, between 0.0 and 360.0 * * Since: 1.4 */ pspec = g_param_spec_double ("angle", "Angle", "The angle of the page rotation, in degrees", 0.0, 360.0, 0.0, CLUTTER_PARAM_READWRITE); obj_props[PROP_ANGLE] = pspec; g_object_class_install_property (gobject_class, PROP_ANGLE, pspec); /** * ClutterPageTurnEffect:radius: * * The radius of the page curl, in pixels * * Since: 1.4 */ pspec = g_param_spec_float ("radius", "Radius", "The radius of the page curl", -G_MAXFLOAT, G_MAXFLOAT, 24.0, CLUTTER_PARAM_READWRITE); obj_props[PROP_RADIUS] = pspec; g_object_class_install_property (gobject_class, PROP_RADIUS, pspec); deform_class->deform_vertex = clutter_page_turn_effect_deform_vertex; } static void clutter_page_turn_effect_init (ClutterPageTurnEffect *self) { self->period = 0.0; self->angle = 0.0; self->radius = 24.0f; } /** * clutter_page_turn_effect_new: * @period: the period of the page curl, between 0.0 and 1.0 * @angle: the angle of the page curl, between 0.0 and 360.0 * @radius: the radius of the page curl, in pixels * * Creates a new #ClutterPageTurnEffect instance with the given parameters * * Return value: the newly created #ClutterPageTurnEffect * * Since: 1.4 */ ClutterEffect * clutter_page_turn_effect_new (gdouble period, gdouble angle, gfloat radius) { g_return_val_if_fail (period >= 0.0 && period <= 1.0, NULL); g_return_val_if_fail (angle >= 0.0 && angle <= 360.0, NULL); return g_object_new (CLUTTER_TYPE_PAGE_TURN_EFFECT, "period", period, "angle", angle, "radius", radius, NULL); } /** * clutter_page_turn_effect_set_period: * @effect: a #ClutterPageTurnEffect * @period: the period of the page curl, between 0.0 and 1.0 * * Sets the period of the page curling, between 0.0 (no curling) * and 1.0 (fully curled) * * Since: 1.4 */ void clutter_page_turn_effect_set_period (ClutterPageTurnEffect *effect, gdouble period) { g_return_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect)); g_return_if_fail (period >= 0.0 && period <= 1.0); effect->period = period; clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_PERIOD]); } /** * clutter_page_turn_effect_get_period: * @effect: a #ClutterPageTurnEffect * * Retrieves the value set using clutter_page_turn_effect_get_period() * * Return value: the period of the page curling * * Since: 1.4 */ gdouble clutter_page_turn_effect_get_period (ClutterPageTurnEffect *effect) { g_return_val_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect), 0.0); return effect->period; } /** * clutter_page_turn_effect_set_angle: * @effect: #ClutterPageTurnEffect * @angle: the angle of the page curl, in degrees * * Sets the angle of the page curling, in degrees * * Since: 1.4 */ void clutter_page_turn_effect_set_angle (ClutterPageTurnEffect *effect, gdouble angle) { g_return_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect)); g_return_if_fail (angle >= 0.0 && angle <= 360.0); effect->angle = angle; clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_ANGLE]); } /** * clutter_page_turn_effect_get_angle: * @effect: a #ClutterPageTurnEffect: * * Retrieves the value set using clutter_page_turn_effect_get_angle() * * Return value: the angle of the page curling * * Since: 1.4 */ gdouble clutter_page_turn_effect_get_angle (ClutterPageTurnEffect *effect) { g_return_val_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect), 0.0); return effect->angle; } /** * clutter_page_turn_effect_set_radius: * @effect: a #ClutterPageTurnEffect: * @radius: the radius of the page curling, in pixels * * Sets the radius of the page curling * * Since: 1.4 */ void clutter_page_turn_effect_set_radius (ClutterPageTurnEffect *effect, gfloat radius) { g_return_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect)); effect->radius = radius; clutter_deform_effect_invalidate (CLUTTER_DEFORM_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_RADIUS]); } /** * clutter_page_turn_effect_get_radius: * @effect: a #ClutterPageTurnEffect * * Retrieves the value set using clutter_page_turn_effect_set_radius() * * Return value: the radius of the page curling * * Since: 1.4 */ gfloat clutter_page_turn_effect_get_radius (ClutterPageTurnEffect *effect) { g_return_val_if_fail (CLUTTER_IS_PAGE_TURN_EFFECT (effect), 0.0); return effect->radius; } muffin-6.4.1/clutter/clutter/clutter-zoom-action.c0000664000175000017500000003644614723361714021206 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Lionel Landwerlin */ /** * SECTION:clutter-zoom-action * @Title: ClutterZoomAction * @Short_Description: Action enabling zooming on actors * * #ClutterZoomAction is a sub-class of #ClutterGestureAction that * implements all the necessary logic for zooming actors using a "pinch" * gesture between two touch points. * * The simplest usage of #ClutterZoomAction consists in adding it to * a #ClutterActor and setting it as reactive; for instance, the following * code: * * |[ * clutter_actor_add_action (actor, clutter_zoom_action_new ()); * clutter_actor_set_reactive (actor, TRUE); * ]| * * will automatically result in the actor to be scale according to the * distance between two touch points. * * Since: 1.12 */ #include "clutter-build-config.h" #include #include "clutter-zoom-action.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-gesture-action-private.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-stage-private.h" typedef struct { gfloat start_x; gfloat start_y; gfloat transformed_start_x; gfloat transformed_start_y; gfloat update_x; gfloat update_y; gfloat transformed_update_x; gfloat transformed_update_y; } ZoomPoint; struct _ClutterZoomActionPrivate { ClutterStage *stage; ClutterZoomAxis zoom_axis; ZoomPoint points[2]; graphene_point_t initial_focal_point; graphene_point_t focal_point; graphene_point_t transformed_focal_point; gfloat initial_x; gfloat initial_y; gfloat initial_z; gdouble initial_scale_x; gdouble initial_scale_y; gdouble zoom_initial_distance; }; enum { PROP_0, PROP_ZOOM_AXIS, PROP_LAST }; static GParamSpec *zoom_props[PROP_LAST] = { NULL, }; enum { ZOOM, LAST_SIGNAL }; static guint zoom_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterZoomAction, clutter_zoom_action, CLUTTER_TYPE_GESTURE_ACTION) static void capture_point_initial_position (ClutterGestureAction *action, ClutterActor *actor, gint index, ZoomPoint *point) { clutter_gesture_action_get_motion_coords (action, index, &point->start_x, &point->start_y); point->transformed_start_x = point->update_x = point->start_x; point->transformed_start_y = point->update_x = point->start_y; clutter_actor_transform_stage_point (actor, point->start_x, point->start_y, &point->transformed_start_x, &point->transformed_start_y); point->transformed_update_x = point->transformed_start_x; point->transformed_update_y = point->transformed_start_y; } static void capture_point_update_position (ClutterGestureAction *action, ClutterActor *actor, gint index, ZoomPoint *point) { clutter_gesture_action_get_motion_coords (action, index, &point->update_x, &point->update_y); point->transformed_update_x = point->update_x; point->transformed_update_y = point->update_y; clutter_actor_transform_stage_point (actor, point->update_x, point->update_y, &point->transformed_update_x, &point->transformed_update_y); } static gboolean clutter_zoom_action_gesture_begin (ClutterGestureAction *action, ClutterActor *actor) { ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv; gfloat dx, dy; capture_point_initial_position (action, actor, 0, &priv->points[0]); capture_point_initial_position (action, actor, 1, &priv->points[1]); dx = priv->points[1].transformed_start_x - priv->points[0].transformed_start_x; dy = priv->points[1].transformed_start_y - priv->points[0].transformed_start_y; priv->zoom_initial_distance = sqrt (dx * dx + dy * dy); clutter_actor_get_translation (actor, &priv->initial_x, &priv->initial_y, &priv->initial_z); clutter_actor_get_scale (actor, &priv->initial_scale_x, &priv->initial_scale_y); priv->initial_focal_point.x = (priv->points[0].start_x + priv->points[1].start_x) / 2; priv->initial_focal_point.y = (priv->points[0].start_y + priv->points[1].start_y) / 2; clutter_actor_transform_stage_point (actor, priv->initial_focal_point.x, priv->initial_focal_point.y, &priv->transformed_focal_point.x, &priv->transformed_focal_point.y); clutter_actor_set_pivot_point (actor, priv->transformed_focal_point.x / clutter_actor_get_width (actor), priv->transformed_focal_point.y / clutter_actor_get_height (actor)); return TRUE; } static gboolean clutter_zoom_action_gesture_progress (ClutterGestureAction *action, ClutterActor *actor) { ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv; gdouble distance, new_scale; gfloat dx, dy; gboolean retval; capture_point_update_position (action, actor, 0, &priv->points[0]); capture_point_update_position (action, actor, 1, &priv->points[1]); dx = priv->points[1].update_x - priv->points[0].update_x; dy = priv->points[1].update_y - priv->points[0].update_y; distance = sqrt (dx * dx + dy * dy); if (distance == 0) return TRUE; priv->focal_point.x = (priv->points[0].update_x + priv->points[1].update_x) / 2; priv->focal_point.y = (priv->points[0].update_y + priv->points[1].update_y) / 2; new_scale = distance / priv->zoom_initial_distance; g_signal_emit (action, zoom_signals[ZOOM], 0, actor, &priv->focal_point, new_scale, &retval); return TRUE; } static void clutter_zoom_action_gesture_cancel (ClutterGestureAction *action, ClutterActor *actor) { ClutterZoomActionPrivate *priv = ((ClutterZoomAction *) action)->priv; clutter_actor_set_translation (actor, priv->initial_x, priv->initial_y, priv->initial_z); clutter_actor_set_scale (actor, priv->initial_scale_x, priv->initial_scale_y); } static gboolean clutter_zoom_action_real_zoom (ClutterZoomAction *action, ClutterActor *actor, graphene_point_t *focal_point, gdouble factor) { ClutterZoomActionPrivate *priv = action->priv; gfloat x, y, z; gdouble scale_x, scale_y; graphene_point3d_t out, in; in.x = priv->transformed_focal_point.x; in.y = priv->transformed_focal_point.y; in.z = 0; clutter_actor_apply_transform_to_point (actor, &in, &out); clutter_actor_get_scale (actor, &scale_x, &scale_y); switch (priv->zoom_axis) { case CLUTTER_ZOOM_BOTH: clutter_actor_set_scale (actor, factor, factor); break; case CLUTTER_ZOOM_X_AXIS: clutter_actor_set_scale (actor, factor, scale_y); break; case CLUTTER_ZOOM_Y_AXIS: clutter_actor_set_scale (actor, scale_x, factor); break; default: break; } x = priv->initial_x + priv->focal_point.x - priv->initial_focal_point.x; y = priv->initial_y + priv->focal_point.y - priv->initial_focal_point.y; clutter_actor_get_translation (actor, NULL, NULL, &z); clutter_actor_set_translation (actor, x, y, z); return TRUE; } static void clutter_zoom_action_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterZoomAction *action = CLUTTER_ZOOM_ACTION (gobject); switch (prop_id) { case PROP_ZOOM_AXIS: clutter_zoom_action_set_zoom_axis (action, g_value_get_enum (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_zoom_action_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterZoomActionPrivate *priv = CLUTTER_ZOOM_ACTION (gobject)->priv; switch (prop_id) { case PROP_ZOOM_AXIS: g_value_set_enum (value, priv->zoom_axis); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_zoom_action_dispose (GObject *gobject) { G_OBJECT_CLASS (clutter_zoom_action_parent_class)->dispose (gobject); } static void clutter_zoom_action_constructed (GObject *gobject) { ClutterGestureAction *gesture; gesture = CLUTTER_GESTURE_ACTION (gobject); clutter_gesture_action_set_threshold_trigger_edge (gesture, CLUTTER_GESTURE_TRIGGER_EDGE_NONE); } static void clutter_zoom_action_class_init (ClutterZoomActionClass *klass) { ClutterGestureActionClass *gesture_class = CLUTTER_GESTURE_ACTION_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->constructed = clutter_zoom_action_constructed; gobject_class->set_property = clutter_zoom_action_set_property; gobject_class->get_property = clutter_zoom_action_get_property; gobject_class->dispose = clutter_zoom_action_dispose; gesture_class->gesture_begin = clutter_zoom_action_gesture_begin; gesture_class->gesture_progress = clutter_zoom_action_gesture_progress; gesture_class->gesture_cancel = clutter_zoom_action_gesture_cancel; klass->zoom = clutter_zoom_action_real_zoom; /** * ClutterZoomAction:zoom-axis: * * Constraints the zooming action to the specified axis * * Since: 1.12 */ zoom_props[PROP_ZOOM_AXIS] = g_param_spec_enum ("zoom-axis", P_("Zoom Axis"), P_("Constraints the zoom to an axis"), CLUTTER_TYPE_ZOOM_AXIS, CLUTTER_ZOOM_BOTH, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, zoom_props); /** * ClutterZoomAction::zoom: * @action: the #ClutterZoomAction that emitted the signal * @actor: the #ClutterActor attached to the action * @focal_point: the focal point of the zoom * @factor: the initial distance between the 2 touch points * * The ::zoom signal is emitted for each series of touch events that * change the distance and focal point between the touch points. * * The default handler of the signal will call * clutter_actor_set_scale() on @actor using the ratio of the first * distance between the touch points and the current distance. To * override the default behaviour, connect to this signal and return * %FALSE. * * Return value: %TRUE if the zoom should continue, and %FALSE if * the zoom should be cancelled. * * Since: 1.12 */ zoom_signals[ZOOM] = g_signal_new (I_("zoom"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterZoomActionClass, zoom), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT_BOXED_DOUBLE, G_TYPE_BOOLEAN, 3, CLUTTER_TYPE_ACTOR, GRAPHENE_TYPE_POINT, G_TYPE_DOUBLE); } static void clutter_zoom_action_init (ClutterZoomAction *self) { ClutterGestureAction *gesture; self->priv = clutter_zoom_action_get_instance_private (self); self->priv->zoom_axis = CLUTTER_ZOOM_BOTH; gesture = CLUTTER_GESTURE_ACTION (self); clutter_gesture_action_set_n_touch_points (gesture, 2); } /** * clutter_zoom_action_new: * * Creates a new #ClutterZoomAction instance * * Return value: the newly created #ClutterZoomAction * * Since: 1.12 */ ClutterAction * clutter_zoom_action_new (void) { return g_object_new (CLUTTER_TYPE_ZOOM_ACTION, NULL); } /** * clutter_zoom_action_set_zoom_axis: * @action: a #ClutterZoomAction * @axis: the axis to constraint the zooming to * * Restricts the zooming action to a specific axis * * Since: 1.12 */ void clutter_zoom_action_set_zoom_axis (ClutterZoomAction *action, ClutterZoomAxis axis) { g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action)); g_return_if_fail (axis >= CLUTTER_ZOOM_X_AXIS && axis <= CLUTTER_ZOOM_BOTH); if (action->priv->zoom_axis == axis) return; action->priv->zoom_axis = axis; g_object_notify_by_pspec (G_OBJECT (action), zoom_props[PROP_ZOOM_AXIS]); } /** * clutter_zoom_action_get_zoom_axis: * @action: a #ClutterZoomAction * * Retrieves the axis constraint set by clutter_zoom_action_set_zoom_axis() * * Return value: the axis constraint * * Since: 1.12 */ ClutterZoomAxis clutter_zoom_action_get_zoom_axis (ClutterZoomAction *action) { g_return_val_if_fail (CLUTTER_IS_ZOOM_ACTION (action), CLUTTER_ZOOM_BOTH); return action->priv->zoom_axis; } /** * clutter_zoom_action_get_focal_point: * @action: a #ClutterZoomAction * @point: (out): a #graphene_point_t * * Retrieves the focal point of the current zoom * * Since: 1.12 */ void clutter_zoom_action_get_focal_point (ClutterZoomAction *action, graphene_point_t *point) { g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action)); g_return_if_fail (point != NULL); *point = action->priv->focal_point; } /** * clutter_zoom_action_get_transformed_focal_point: * @action: a #ClutterZoomAction * @point: (out): a #graphene_point_t * * Retrieves the focal point relative to the actor's coordinates of * the current zoom * * Since: 1.12 */ void clutter_zoom_action_get_transformed_focal_point (ClutterZoomAction *action, graphene_point_t *point) { g_return_if_fail (CLUTTER_IS_ZOOM_ACTION (action)); g_return_if_fail (point != NULL); *point = action->priv->transformed_focal_point; } muffin-6.4.1/clutter/clutter/clutter-interval.h0000664000175000017500000001401314723361714020562 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_INTERVAL_H__ #define __CLUTTER_INTERVAL_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_INTERVAL (clutter_interval_get_type ()) #define CLUTTER_INTERVAL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_INTERVAL, ClutterInterval)) #define CLUTTER_IS_INTERVAL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_INTERVAL)) #define CLUTTER_INTERVAL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_INTERVAL, ClutterIntervalClass)) #define CLUTTER_IS_INTERVAL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_INTERVAL)) #define CLUTTER_INTERVAL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_INTERVAL, ClutterIntervalClass)) typedef struct _ClutterIntervalPrivate ClutterIntervalPrivate; typedef struct _ClutterIntervalClass ClutterIntervalClass; /** * ClutterInterval: * * The #ClutterInterval structure contains only private data and should * be accessed using the provided functions. * * Since: 1.0 */ struct _ClutterInterval { /*< private >*/ GInitiallyUnowned parent_instance; ClutterIntervalPrivate *priv; }; /** * ClutterIntervalClass: * @validate: virtual function for validating an interval * using a #GParamSpec * @compute_value: virtual function for computing the value * inside an interval using an adimensional factor between 0 and 1 * * The #ClutterIntervalClass contains only private data. * * Since: 1.0 */ struct _ClutterIntervalClass { /*< private >*/ GInitiallyUnownedClass parent_class; /*< public >*/ gboolean (* validate) (ClutterInterval *interval, GParamSpec *pspec); gboolean (* compute_value) (ClutterInterval *interval, gdouble factor, GValue *value); /*< private >*/ /* padding for future expansion */ void (*_clutter_reserved1) (void); void (*_clutter_reserved2) (void); void (*_clutter_reserved3) (void); void (*_clutter_reserved4) (void); void (*_clutter_reserved5) (void); void (*_clutter_reserved6) (void); }; CLUTTER_EXPORT GType clutter_interval_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterInterval *clutter_interval_new (GType gtype, ...); CLUTTER_EXPORT ClutterInterval *clutter_interval_new_with_values (GType gtype, const GValue *initial, const GValue *final); CLUTTER_EXPORT ClutterInterval *clutter_interval_clone (ClutterInterval *interval); CLUTTER_EXPORT GType clutter_interval_get_value_type (ClutterInterval *interval); CLUTTER_EXPORT void clutter_interval_set_initial (ClutterInterval *interval, ...); CLUTTER_EXPORT void clutter_interval_set_initial_value (ClutterInterval *interval, const GValue *value); CLUTTER_EXPORT void clutter_interval_get_initial_value (ClutterInterval *interval, GValue *value); CLUTTER_EXPORT GValue * clutter_interval_peek_initial_value (ClutterInterval *interval); CLUTTER_EXPORT void clutter_interval_set_final (ClutterInterval *interval, ...); CLUTTER_EXPORT void clutter_interval_set_final_value (ClutterInterval *interval, const GValue *value); CLUTTER_EXPORT void clutter_interval_get_final_value (ClutterInterval *interval, GValue *value); CLUTTER_EXPORT GValue * clutter_interval_peek_final_value (ClutterInterval *interval); CLUTTER_EXPORT void clutter_interval_set_interval (ClutterInterval *interval, ...); CLUTTER_EXPORT void clutter_interval_get_interval (ClutterInterval *interval, ...); CLUTTER_EXPORT gboolean clutter_interval_validate (ClutterInterval *interval, GParamSpec *pspec); CLUTTER_EXPORT gboolean clutter_interval_compute_value (ClutterInterval *interval, gdouble factor, GValue *value); CLUTTER_EXPORT const GValue * clutter_interval_compute (ClutterInterval *interval, gdouble factor); CLUTTER_EXPORT gboolean clutter_interval_is_valid (ClutterInterval *interval); G_END_DECLS #endif /* __CLUTTER_INTERVAL_H__ */ muffin-6.4.1/clutter/clutter/clutter-desaturate-effect.c0000664000175000017500000002636514723361714022341 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-desaturate-effect * @short_description: A desaturation effect * @see_also: #ClutterEffect, #ClutterOffscreenEffect * * #ClutterDesaturateEffect is a sub-class of #ClutterEffect that * desaturates the color of an actor and its contents. The strenght * of the desaturation effect is controllable and animatable through * the #ClutterDesaturateEffect:factor property. * * #ClutterDesaturateEffect is available since Clutter 1.4 */ #define CLUTTER_DESATURATE_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_DESATURATE_EFFECT, ClutterDesaturateEffectClass)) #define CLUTTER_IS_DESATURATE_EFFECT_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_DESATURATE_EFFECT)) #define CLUTTER_DESATURATE_EFFECT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_DESATURATE_EFFECT, ClutterDesaturateEffectClass)) #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include #include "clutter-desaturate-effect.h" #include "cogl/cogl.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-offscreen-effect.h" #include "clutter-private.h" struct _ClutterDesaturateEffect { ClutterOffscreenEffect parent_instance; /* the desaturation factor, also known as "strength" */ gdouble factor; gint factor_uniform; gint tex_width; gint tex_height; CoglPipeline *pipeline; }; struct _ClutterDesaturateEffectClass { ClutterOffscreenEffectClass parent_class; CoglPipeline *base_pipeline; }; /* the magic gray vec3 has been taken from the NTSC conversion weights * as defined by: * * "OpenGL Superbible, 4th edition" * -- Richard S. Wright Jr, Benjamin Lipchak, Nicholas Haemel * Addison-Wesley */ static const gchar *desaturate_glsl_declarations = "uniform float factor;\n" "\n" "vec3 desaturate (const vec3 color, const float desaturation)\n" "{\n" " const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n" " vec3 gray = vec3 (dot (gray_conv, color));\n" " return vec3 (mix (color.rgb, gray, desaturation));\n" "}\n"; static const gchar *desaturate_glsl_source = " cogl_color_out.rgb = desaturate (cogl_color_out.rgb, factor);\n"; enum { PROP_0, PROP_FACTOR, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (ClutterDesaturateEffect, clutter_desaturate_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); static gboolean clutter_desaturate_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect); ClutterEffectClass *parent_class; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* if we don't have support for GLSL shaders then we * forcibly disable the ActorMeta */ g_warning ("Unable to use the ShaderEffect: the graphics hardware " "or the current GL driver does not implement support " "for the GLSL shading language."); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); return FALSE; } parent_class = CLUTTER_EFFECT_CLASS (clutter_desaturate_effect_parent_class); if (parent_class->pre_paint (effect, paint_context)) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (effect); CoglHandle texture; texture = clutter_offscreen_effect_get_texture (offscreen_effect); self->tex_width = cogl_texture_get_width (texture); self->tex_height = cogl_texture_get_height (texture); cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); return TRUE; } else return FALSE; } static void clutter_desaturate_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (effect); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); ClutterActor *actor; CoglHandle texture; guint8 paint_opacity; texture = clutter_offscreen_effect_get_texture (effect); cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (effect)); paint_opacity = clutter_actor_get_paint_opacity (actor); cogl_pipeline_set_color4ub (self->pipeline, paint_opacity, paint_opacity, paint_opacity, paint_opacity); cogl_framebuffer_draw_rectangle (framebuffer, self->pipeline, 0, 0, cogl_texture_get_width (texture), cogl_texture_get_height (texture)); } static void clutter_desaturate_effect_dispose (GObject *gobject) { ClutterDesaturateEffect *self = CLUTTER_DESATURATE_EFFECT (gobject); if (self->pipeline != NULL) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } G_OBJECT_CLASS (clutter_desaturate_effect_parent_class)->dispose (gobject); } static void clutter_desaturate_effect_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterDesaturateEffect *effect = CLUTTER_DESATURATE_EFFECT (gobject); switch (prop_id) { case PROP_FACTOR: clutter_desaturate_effect_set_factor (effect, g_value_get_double (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_desaturate_effect_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterDesaturateEffect *effect = CLUTTER_DESATURATE_EFFECT (gobject); switch (prop_id) { case PROP_FACTOR: g_value_set_double (value, effect->factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void update_factor_uniform (ClutterDesaturateEffect *self) { if (self->factor_uniform > -1) cogl_pipeline_set_uniform_1f (self->pipeline, self->factor_uniform, self->factor); } static void clutter_desaturate_effect_class_init (ClutterDesaturateEffectClass *klass) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterOffscreenEffectClass *offscreen_class; offscreen_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_class->paint_target = clutter_desaturate_effect_paint_target; effect_class->pre_paint = clutter_desaturate_effect_pre_paint; /** * ClutterDesaturateEffect:factor: * * The desaturation factor, between 0.0 (no desaturation) and 1.0 (full * desaturation). * * Since: 1.4 */ obj_props[PROP_FACTOR] = g_param_spec_double ("factor", P_("Factor"), P_("The desaturation factor"), 0.0, 1.0, 1.0, CLUTTER_PARAM_READWRITE); gobject_class->dispose = clutter_desaturate_effect_dispose; gobject_class->set_property = clutter_desaturate_effect_set_property; gobject_class->get_property = clutter_desaturate_effect_get_property; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_desaturate_effect_init (ClutterDesaturateEffect *self) { ClutterDesaturateEffectClass *klass = CLUTTER_DESATURATE_EFFECT_GET_CLASS (self); if (G_UNLIKELY (klass->base_pipeline == NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglSnippet *snippet; klass->base_pipeline = cogl_pipeline_new (ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, desaturate_glsl_declarations, desaturate_glsl_source); cogl_pipeline_add_snippet (klass->base_pipeline, snippet); cogl_object_unref (snippet); cogl_pipeline_set_layer_null_texture (klass->base_pipeline, 0); } self->pipeline = cogl_pipeline_copy (klass->base_pipeline); self->factor_uniform = cogl_pipeline_get_uniform_location (self->pipeline, "factor"); self->factor = 1.0; update_factor_uniform (self); } /** * clutter_desaturate_effect_new: * @factor: the desaturation factor, between 0.0 and 1.0 * * Creates a new #ClutterDesaturateEffect to be used with * clutter_actor_add_effect() * * Return value: the newly created #ClutterDesaturateEffect or %NULL * * Since: 1.4 */ ClutterEffect * clutter_desaturate_effect_new (gdouble factor) { g_return_val_if_fail (factor >= 0.0 && factor <= 1.0, NULL); return g_object_new (CLUTTER_TYPE_DESATURATE_EFFECT, "factor", factor, NULL); } /** * clutter_desaturate_effect_set_factor: * @effect: a #ClutterDesaturateEffect * @factor: the desaturation factor, between 0.0 and 1.0 * * Sets the desaturation factor for @effect, with 0.0 being "do not desaturate" * and 1.0 being "fully desaturate" * * Since: 1.4 */ void clutter_desaturate_effect_set_factor (ClutterDesaturateEffect *effect, gdouble factor) { g_return_if_fail (CLUTTER_IS_DESATURATE_EFFECT (effect)); g_return_if_fail (factor >= 0.0 && factor <= 1.0); if (fabsf (effect->factor - factor) >= 0.00001) { effect->factor = factor; update_factor_uniform (effect); clutter_effect_queue_repaint (CLUTTER_EFFECT (effect)); g_object_notify_by_pspec (G_OBJECT (effect), obj_props[PROP_FACTOR]); } } /** * clutter_desaturate_effect_get_factor: * @effect: a #ClutterDesaturateEffect * * Retrieves the desaturation factor of @effect * * Return value: the desaturation factor * * Since: 1.4 */ gdouble clutter_desaturate_effect_get_factor (ClutterDesaturateEffect *effect) { g_return_val_if_fail (CLUTTER_IS_DESATURATE_EFFECT (effect), 0.0); return effect->factor; } muffin-6.4.1/clutter/clutter/clutter-stage-view.c0000664000175000017500000004117214723361714021012 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "clutter-build-config.h" #include "clutter/clutter-stage-view.h" #include "clutter/clutter-stage-view-private.h" #include #include #include "clutter/clutter-private.h" enum { PROP_0, PROP_LAYOUT, PROP_FRAMEBUFFER, PROP_OFFSCREEN, PROP_SHADOWFB, PROP_SCALE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; typedef struct _ClutterStageViewPrivate { cairo_rectangle_int_t layout; float scale; CoglFramebuffer *framebuffer; CoglOffscreen *offscreen; CoglPipeline *offscreen_pipeline; CoglOffscreen *shadowfb; CoglPipeline *shadowfb_pipeline; gboolean has_redraw_clip; cairo_region_t *redraw_clip; guint dirty_viewport : 1; guint dirty_projection : 1; } ClutterStageViewPrivate; G_DEFINE_TYPE_WITH_PRIVATE (ClutterStageView, clutter_stage_view, G_TYPE_OBJECT) void clutter_stage_view_get_layout (ClutterStageView *view, cairo_rectangle_int_t *rect) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); *rect = priv->layout; } /** * clutter_stage_view_get_framebuffer: * @view: a #ClutterStageView * * Retrieves the framebuffer of @view to draw to. * * Returns: (transfer none): a #CoglFramebuffer */ CoglFramebuffer * clutter_stage_view_get_framebuffer (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); if (priv->offscreen) return priv->offscreen; else if (priv->shadowfb) return priv->shadowfb; else return priv->framebuffer; } /** * clutter_stage_view_get_onscreen: * @view: a #ClutterStageView * * Retrieves the onscreen framebuffer of @view if available. * * Returns: (transfer none): a #CoglFramebuffer */ CoglFramebuffer * clutter_stage_view_get_onscreen (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->framebuffer; } static CoglPipeline * clutter_stage_view_create_framebuffer_pipeline (CoglFramebuffer *framebuffer) { CoglPipeline *pipeline; pipeline = cogl_pipeline_new (cogl_framebuffer_get_context (framebuffer)); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_texture (pipeline, 0, cogl_offscreen_get_texture (framebuffer)); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); return pipeline; } static void clutter_stage_view_ensure_offscreen_blit_pipeline (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view); g_assert (priv->offscreen != NULL); if (priv->offscreen_pipeline) return; priv->offscreen_pipeline = clutter_stage_view_create_framebuffer_pipeline (priv->offscreen); if (view_class->setup_offscreen_blit_pipeline) view_class->setup_offscreen_blit_pipeline (view, priv->offscreen_pipeline); } static void clutter_stage_view_ensure_shadowfb_blit_pipeline (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); if (priv->shadowfb_pipeline) return; priv->shadowfb_pipeline = clutter_stage_view_create_framebuffer_pipeline (priv->shadowfb); } void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); } static void clutter_stage_view_copy_to_framebuffer (ClutterStageView *view, CoglPipeline *pipeline, CoglFramebuffer *src_framebuffer, CoglFramebuffer *dst_framebuffer, gboolean can_blit) { CoglMatrix matrix; /* First, try with blit */ if (can_blit) { if (cogl_blit_framebuffer (src_framebuffer, dst_framebuffer, 0, 0, 0, 0, cogl_framebuffer_get_width (dst_framebuffer), cogl_framebuffer_get_height (dst_framebuffer), NULL)) return; } /* If blit fails, fallback to the slower painting method */ cogl_framebuffer_push_matrix (dst_framebuffer); cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, -1, 1, 0); cogl_matrix_scale (&matrix, 2, -2, 0); cogl_framebuffer_set_projection_matrix (dst_framebuffer, &matrix); cogl_framebuffer_draw_rectangle (dst_framebuffer, pipeline, 0, 0, 1, 1); cogl_framebuffer_pop_matrix (dst_framebuffer); } void clutter_stage_view_after_paint (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); if (priv->offscreen) { gboolean can_blit; CoglMatrix matrix; clutter_stage_view_ensure_offscreen_blit_pipeline (view); clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); can_blit = cogl_matrix_is_identity (&matrix); if (priv->shadowfb) { clutter_stage_view_copy_to_framebuffer (view, priv->offscreen_pipeline, priv->offscreen, priv->shadowfb, can_blit); } else { clutter_stage_view_copy_to_framebuffer (view, priv->offscreen_pipeline, priv->offscreen, priv->framebuffer, can_blit); } } if (priv->shadowfb) { clutter_stage_view_ensure_shadowfb_blit_pipeline (view); clutter_stage_view_copy_to_framebuffer (view, priv->shadowfb_pipeline, priv->shadowfb, priv->framebuffer, TRUE); } } float clutter_stage_view_get_scale (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->scale; } gboolean clutter_stage_view_is_dirty_viewport (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->dirty_viewport; } void clutter_stage_view_set_dirty_viewport (ClutterStageView *view, gboolean dirty) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); priv->dirty_viewport = dirty; } gboolean clutter_stage_view_is_dirty_projection (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->dirty_projection; } void clutter_stage_view_set_dirty_projection (ClutterStageView *view, gboolean dirty) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); priv->dirty_projection = dirty; } void clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, CoglMatrix *matrix) { ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_GET_CLASS (view); view_class->get_offscreen_transformation_matrix (view, matrix); } void clutter_stage_view_add_redraw_clip (ClutterStageView *view, const cairo_rectangle_int_t *clip) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); if (priv->has_redraw_clip && !priv->redraw_clip) return; if (!clip) { g_clear_pointer (&priv->redraw_clip, cairo_region_destroy); priv->has_redraw_clip = TRUE; return; } if (clip->width == 0 || clip->height == 0) return; if (!priv->redraw_clip) { if (!clutter_util_rectangle_equal (&priv->layout, clip)) priv->redraw_clip = cairo_region_create_rectangle (clip); } else { cairo_region_union_rectangle (priv->redraw_clip, clip); if (cairo_region_num_rectangles (priv->redraw_clip) == 1) { cairo_rectangle_int_t redraw_clip_extents; cairo_region_get_extents (priv->redraw_clip, &redraw_clip_extents); if (clutter_util_rectangle_equal (&priv->layout, &redraw_clip_extents)) g_clear_pointer (&priv->redraw_clip, cairo_region_destroy); } } priv->has_redraw_clip = TRUE; } gboolean clutter_stage_view_has_redraw_clip (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->has_redraw_clip; } gboolean clutter_stage_view_has_full_redraw_clip (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->has_redraw_clip && !priv->redraw_clip; } const cairo_region_t * clutter_stage_view_peek_redraw_clip (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); return priv->redraw_clip; } cairo_region_t * clutter_stage_view_take_redraw_clip (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); priv->has_redraw_clip = FALSE; return g_steal_pointer (&priv->redraw_clip); } void clutter_stage_view_transform_to_onscreen (ClutterStageView *view, gfloat *x, gfloat *y) { gfloat z = 0, w = 1; CoglMatrix matrix; clutter_stage_view_get_offscreen_transformation_matrix (view, &matrix); cogl_matrix_get_inverse (&matrix, &matrix); cogl_matrix_transform_point (&matrix, x, y, &z, &w); } static void clutter_stage_default_get_offscreen_transformation_matrix (ClutterStageView *view, CoglMatrix *matrix) { cogl_matrix_init_identity (matrix); } static void clutter_stage_view_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterStageView *view = CLUTTER_STAGE_VIEW (object); ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); switch (prop_id) { case PROP_LAYOUT: g_value_set_boxed (value, &priv->layout); break; case PROP_FRAMEBUFFER: g_value_set_boxed (value, priv->framebuffer); break; case PROP_OFFSCREEN: g_value_set_boxed (value, priv->offscreen); break; case PROP_SHADOWFB: g_value_set_boxed (value, priv->shadowfb); break; case PROP_SCALE: g_value_set_float (value, priv->scale); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_stage_view_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterStageView *view = CLUTTER_STAGE_VIEW (object); ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); cairo_rectangle_int_t *layout; switch (prop_id) { case PROP_LAYOUT: layout = g_value_get_boxed (value); priv->layout = *layout; break; case PROP_FRAMEBUFFER: priv->framebuffer = g_value_dup_boxed (value); #ifndef G_DISABLE_CHECKS if (priv->framebuffer) { int fb_width, fb_height; fb_width = cogl_framebuffer_get_width (priv->framebuffer); fb_height = cogl_framebuffer_get_height (priv->framebuffer); g_warn_if_fail (fabsf (roundf (fb_width / priv->scale) - fb_width / priv->scale) < FLT_EPSILON); g_warn_if_fail (fabsf (roundf (fb_height / priv->scale) - fb_height / priv->scale) < FLT_EPSILON); } #endif break; case PROP_OFFSCREEN: priv->offscreen = g_value_dup_boxed (value); break; case PROP_SHADOWFB: priv->shadowfb = g_value_dup_boxed (value); break; case PROP_SCALE: priv->scale = g_value_get_float (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void clutter_stage_view_dispose (GObject *object) { ClutterStageView *view = CLUTTER_STAGE_VIEW (object); ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); g_clear_pointer (&priv->framebuffer, cogl_object_unref); g_clear_pointer (&priv->shadowfb, cogl_object_unref); g_clear_pointer (&priv->offscreen, cogl_object_unref); g_clear_pointer (&priv->offscreen_pipeline, cogl_object_unref); g_clear_pointer (&priv->shadowfb_pipeline, cogl_object_unref); g_clear_pointer (&priv->redraw_clip, cairo_region_destroy); G_OBJECT_CLASS (clutter_stage_view_parent_class)->dispose (object); } static void clutter_stage_view_init (ClutterStageView *view) { ClutterStageViewPrivate *priv = clutter_stage_view_get_instance_private (view); priv->dirty_viewport = TRUE; priv->dirty_projection = TRUE; priv->scale = 1.0; } static void clutter_stage_view_class_init (ClutterStageViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); klass->get_offscreen_transformation_matrix = clutter_stage_default_get_offscreen_transformation_matrix; object_class->get_property = clutter_stage_view_get_property; object_class->set_property = clutter_stage_view_set_property; object_class->dispose = clutter_stage_view_dispose; obj_props[PROP_LAYOUT] = g_param_spec_boxed ("layout", "View layout", "The view layout on the screen", CAIRO_GOBJECT_TYPE_RECTANGLE_INT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); obj_props[PROP_FRAMEBUFFER] = g_param_spec_boxed ("framebuffer", "View framebuffer", "The front buffer of the view", COGL_TYPE_HANDLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); obj_props[PROP_OFFSCREEN] = g_param_spec_boxed ("offscreen", "Offscreen buffer", "Framebuffer used as intermediate buffer", COGL_TYPE_HANDLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_SHADOWFB] = g_param_spec_boxed ("shadowfb", "Shadow framebuffer", "Framebuffer used as intermediate shadow buffer", COGL_TYPE_HANDLE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_SCALE] = g_param_spec_float ("scale", "View scale", "The view scale", 0.5, G_MAXFLOAT, 1.0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/clutter/clutter/clutter-layout-manager.c0000664000175000017500000010526414723361714021667 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-layout-manager * @short_description: Layout managers base class * * #ClutterLayoutManager is a base abstract class for layout managers. A * layout manager implements the layouting policy for a composite or a * container actor: it controls the preferred size of the actor to which * it has been paired, and it controls the allocation of its children. * * Any composite or container #ClutterActor subclass can delegate the * layouting of its children to a #ClutterLayoutManager. Clutter provides * a generic container using #ClutterLayoutManager called #ClutterBox. * * Clutter provides some simple #ClutterLayoutManager sub-classes, like * #ClutterFlowLayout and #ClutterBinLayout. * * ## Implementing a ClutterLayoutManager * The implementation of a layout manager does not differ from the * implementation of the size requisition and allocation bits of * #ClutterActor, so you should read the relative documentation * forr subclassing #ClutterActor. * * The layout manager implementation can hold a back pointer to the * #ClutterContainer by implementing the #ClutterLayoutManagerClass.set_container() * virtual function. The layout manager should not hold a real reference (i.e. * call g_object_ref()) on the container actor, to avoid reference cycles. * * If a layout manager has properties affecting the layout policies then it should * emit the #ClutterLayoutManager::layout-changed signal on itself by using the * clutter_layout_manager_layout_changed() function whenever one of these properties * changes. * * ## Layout Properties * * If a layout manager has layout properties, that is properties that * should exist only as the result of the presence of a specific (layout * manager, container actor, child actor) combination, and it wishes to store * those properties inside a #ClutterLayoutMeta, then it should override the * #ClutterLayoutManagerClass.get_child_meta_type() virtual function to return * the #GType of the #ClutterLayoutMeta sub-class used to store the layout * properties; optionally, the #ClutterLayoutManager sub-class might also * override the #ClutterLayoutManagerClass.create_child_meta() virtual function * to control how the #ClutterLayoutMeta instance is created, otherwise the * default implementation will be equivalent to: * * |[ * ClutterLayoutManagerClass *klass; * GType meta_type; * * klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); * meta_type = klass->get_child_meta_type (manager); * * return g_object_new (meta_type, * "manager", manager, * "container", container, * "actor", actor, * NULL); * ]| * * Where `manager` is the #ClutterLayoutManager, `container` is the * #ClutterContainer using the #ClutterLayoutManager, and `actor` is * the #ClutterActor child of the #ClutterContainer. * * ## Using ClutterLayoutManager with ClutterScript * * #ClutterLayoutManager instances can be created in the same way * as other objects in #ClutterScript; properties can be set using the * common syntax. * * Layout properties can be set on children of a container with * a #ClutterLayoutManager using the `layout::` modifier on the property * name, for instance: * * |[ * { * "type" : "ClutterBox", * "layout-manager" : { "type" : "ClutterGridLayout" }, * "children" : [ * { * "type" : "ClutterText", * "text" : "Some text", * * "layout::row" : 0, * "layout::column" : 0, * "layout::x-align" : "left", * "layout::y-align" : "center", * "layout::x-expand" : true, * "layout::y-expand" : true * }, * { * "type" : "ClutterText", * "text" : "Some more text", * * "layout::row" : 0, * "layout::column" : 1, * "layout::x-align" : "right", * "layout::y-align" : "center", * "layout::x-expand" : true, * "layout::y-expand" : true * } * ] * } * ]| * * #ClutterLayoutManager is available since Clutter 1.2 */ #include "clutter-build-config.h" #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "deprecated/clutter-alpha.h" #include "clutter-debug.h" #include "clutter-layout-manager.h" #include "clutter-layout-meta.h" #include "clutter-marshal.h" #include "clutter-private.h" #include "clutter-timeline.h" #define LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED(m,method) G_STMT_START { \ GObject *_obj = G_OBJECT (m); \ g_warning ("Layout managers of type %s do not implement " \ "the ClutterLayoutManager::%s method", \ G_OBJECT_TYPE_NAME (_obj), \ (method)); } G_STMT_END enum { LAYOUT_CHANGED, LAST_SIGNAL }; G_DEFINE_ABSTRACT_TYPE (ClutterLayoutManager, clutter_layout_manager, G_TYPE_INITIALLY_UNOWNED) static GQuark quark_layout_meta = 0; static GQuark quark_layout_alpha = 0; static guint manager_signals[LAST_SIGNAL] = { 0, }; static void layout_manager_freeze_layout_change (ClutterLayoutManager *manager) { gpointer is_frozen; CLUTTER_NOTE (LAYOUT, "Freezing changes for manager '%s'[%p]", G_OBJECT_TYPE_NAME (manager), manager); is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (1)); else { guint level = GPOINTER_TO_UINT (is_frozen) + 1; g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (level)); } } static void layout_manager_thaw_layout_change (ClutterLayoutManager *manager) { gpointer is_frozen; is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_critical (G_STRLOC ": Mismatched thaw; you have to call " "clutter_layout_manager_freeze_layout_change() prior to " "calling clutter_layout_manager_thaw_layout_change()"); else { guint level = GPOINTER_TO_UINT (is_frozen); g_assert (level > 0); CLUTTER_NOTE (LAYOUT, "Thawing changes for manager '%s'[%p]", G_OBJECT_TYPE_NAME (manager), manager); level -= 1; if (level == 0) g_object_set_data (G_OBJECT (manager), "freeze-change", NULL); else g_object_set_data (G_OBJECT (manager), "freeze-change", GUINT_TO_POINTER (level)); } } static void layout_manager_real_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_width"); if (min_width_p) *min_width_p = 0.0; if (nat_width_p) *nat_width_p = 0.0; } static void layout_manager_real_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "get_preferred_height"); if (min_height_p) *min_height_p = 0.0; if (nat_height_p) *nat_height_p = 0.0; } static void layout_manager_real_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { LAYOUT_MANAGER_WARN_NOT_IMPLEMENTED (manager, "allocate"); } static void layout_manager_real_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { if (container != NULL) g_object_set_data (G_OBJECT (container), "clutter-layout-manager", manager); } static ClutterLayoutMeta * layout_manager_real_create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutManagerClass *klass; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); /* provide a default implementation to reduce common code */ if (meta_type != G_TYPE_INVALID) { g_assert (g_type_is_a (meta_type, CLUTTER_TYPE_LAYOUT_META)); return g_object_new (meta_type, "manager", manager, "container", container, "actor", actor, NULL); } return NULL; } static GType layout_manager_real_get_child_meta_type (ClutterLayoutManager *manager) { return G_TYPE_INVALID; } /* XXX:2.0 - Remove */ static ClutterAlpha * layout_manager_real_begin_animation (ClutterLayoutManager *manager, guint duration, gulong mode) { ClutterTimeline *timeline; ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha != NULL) { clutter_alpha_set_mode (alpha, mode); timeline = clutter_alpha_get_timeline (alpha); clutter_timeline_set_duration (timeline, duration); clutter_timeline_rewind (timeline); return alpha; }; timeline = clutter_timeline_new (duration); alpha = clutter_alpha_new_full (timeline, mode); /* let the alpha take ownership of the timeline */ g_object_unref (timeline); g_signal_connect_swapped (timeline, "new-frame", G_CALLBACK (clutter_layout_manager_layout_changed), manager); g_object_set_qdata_full (G_OBJECT (manager), quark_layout_alpha, alpha, (GDestroyNotify) g_object_unref); clutter_timeline_start (timeline); return alpha; } /* XXX:2.0 - Remove */ static gdouble layout_manager_real_get_animation_progress (ClutterLayoutManager *manager) { ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha == NULL) return 1.0; return clutter_alpha_get_alpha (alpha); } /* XXX:2.0 - Remove */ static void layout_manager_real_end_animation (ClutterLayoutManager *manager) { ClutterTimeline *timeline; ClutterAlpha *alpha; alpha = g_object_get_qdata (G_OBJECT (manager), quark_layout_alpha); if (alpha == NULL) return; timeline = clutter_alpha_get_timeline (alpha); g_assert (timeline != NULL); if (clutter_timeline_is_playing (timeline)) clutter_timeline_stop (timeline); g_signal_handlers_disconnect_by_func (timeline, G_CALLBACK (clutter_layout_manager_layout_changed), manager); g_object_set_qdata (G_OBJECT (manager), quark_layout_alpha, NULL); clutter_layout_manager_layout_changed (manager); } static void clutter_layout_manager_class_init (ClutterLayoutManagerClass *klass) { quark_layout_meta = g_quark_from_static_string ("clutter-layout-manager-child-meta"); /* XXX:2.0 - Remove */ quark_layout_alpha = g_quark_from_static_string ("clutter-layout-manager-alpha"); klass->get_preferred_width = layout_manager_real_get_preferred_width; klass->get_preferred_height = layout_manager_real_get_preferred_height; klass->allocate = layout_manager_real_allocate; klass->create_child_meta = layout_manager_real_create_child_meta; klass->get_child_meta_type = layout_manager_real_get_child_meta_type; /* XXX:2.0 - Remove */ klass->begin_animation = layout_manager_real_begin_animation; klass->get_animation_progress = layout_manager_real_get_animation_progress; klass->end_animation = layout_manager_real_end_animation; klass->set_container = layout_manager_real_set_container; /** * ClutterLayoutManager::layout-changed: * @manager: the #ClutterLayoutManager that emitted the signal * * The ::layout-changed signal is emitted each time a layout manager * has been changed. Every #ClutterActor using the @manager instance * as a layout manager should connect a handler to the ::layout-changed * signal and queue a relayout on themselves: * * |[ * static void layout_changed (ClutterLayoutManager *manager, * ClutterActor *self) * { * clutter_actor_queue_relayout (self); * } * ... * self->manager = g_object_ref_sink (manager); * g_signal_connect (self->manager, "layout-changed", * G_CALLBACK (layout_changed), * self); * ]| * * Sub-classes of #ClutterLayoutManager that implement a layout that * can be controlled or changed using parameters should emit the * ::layout-changed signal whenever one of the parameters changes, * by using clutter_layout_manager_layout_changed(). * * Since: 1.2 */ manager_signals[LAYOUT_CHANGED] = g_signal_new (I_("layout-changed"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterLayoutManagerClass, layout_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void clutter_layout_manager_init (ClutterLayoutManager *manager) { } /** * clutter_layout_manager_get_preferred_width: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @for_height: the height for which the width should be computed, or -1 * @min_width_p: (out) (allow-none): return location for the minimum width * of the layout, or %NULL * @nat_width_p: (out) (allow-none): return location for the natural width * of the layout, or %NULL * * Computes the minimum and natural widths of the @container according * to @manager. * * See also clutter_actor_get_preferred_width() * * Since: 1.2 */ void clutter_layout_manager_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->get_preferred_width (manager, container, for_height, min_width_p, nat_width_p); } /** * clutter_layout_manager_get_preferred_height: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @for_width: the width for which the height should be computed, or -1 * @min_height_p: (out) (allow-none): return location for the minimum height * of the layout, or %NULL * @nat_height_p: (out) (allow-none): return location for the natural height * of the layout, or %NULL * * Computes the minimum and natural heights of the @container according * to @manager. * * See also clutter_actor_get_preferred_height() * * Since: 1.2 */ void clutter_layout_manager_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->get_preferred_height (manager, container, for_width, min_height_p, nat_height_p); } /** * clutter_layout_manager_allocate: * @manager: a #ClutterLayoutManager * @container: the #ClutterContainer using @manager * @allocation: the #ClutterActorBox containing the allocated area * of @container * @flags: the allocation flags * * Allocates the children of @container given an area * * See also clutter_actor_allocate() * * Since: 1.2 */ void clutter_layout_manager_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (allocation != NULL); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); klass->allocate (manager, container, allocation, flags); } /** * clutter_layout_manager_layout_changed: * @manager: a #ClutterLayoutManager * * Emits the #ClutterLayoutManager::layout-changed signal on @manager * * This function should only be called by implementations of the * #ClutterLayoutManager class * * Since: 1.2 */ void clutter_layout_manager_layout_changed (ClutterLayoutManager *manager) { gpointer is_frozen; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); is_frozen = g_object_get_data (G_OBJECT (manager), "freeze-change"); if (is_frozen == NULL) g_signal_emit (manager, manager_signals[LAYOUT_CHANGED], 0); else CLUTTER_NOTE (LAYOUT, "Layout manager '%s'[%p] has been frozen", G_OBJECT_TYPE_NAME (manager), manager); } /** * clutter_layout_manager_set_container: * @manager: a #ClutterLayoutManager * @container: (allow-none): a #ClutterContainer using @manager * * If the #ClutterLayoutManager sub-class allows it, allow * adding a weak reference of the @container using @manager * from within the layout manager * * The layout manager should not increase the reference * count of the @container * * Since: 1.2 */ void clutter_layout_manager_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { ClutterLayoutManagerClass *klass; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (container == NULL || CLUTTER_IS_CONTAINER (container)); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); if (klass->set_container) klass->set_container (manager, container); } GType _clutter_layout_manager_get_child_meta_type (ClutterLayoutManager *manager) { return CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager)->get_child_meta_type (manager); } static inline ClutterLayoutMeta * create_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutManagerClass *klass; ClutterLayoutMeta *meta = NULL; layout_manager_freeze_layout_change (manager); klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); if (klass->get_child_meta_type (manager) != G_TYPE_INVALID) meta = klass->create_child_meta (manager, container, actor); layout_manager_thaw_layout_change (manager); return meta; } static inline ClutterLayoutMeta * get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { ClutterLayoutMeta *layout = NULL; layout = g_object_get_qdata (G_OBJECT (actor), quark_layout_meta); if (layout != NULL) { ClutterChildMeta *child = CLUTTER_CHILD_META (layout); if (layout->manager == manager && child->container == container && child->actor == actor) return layout; /* if the LayoutMeta referenced is not attached to the * layout manager then we simply ask the layout manager * to replace it with the right one */ } layout = create_child_meta (manager, container, actor); if (layout != NULL) { g_assert (CLUTTER_IS_LAYOUT_META (layout)); g_object_set_qdata_full (G_OBJECT (actor), quark_layout_meta, layout, (GDestroyNotify) g_object_unref); return layout; } return NULL; } /** * clutter_layout_manager_get_child_meta: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * * Retrieves the #ClutterLayoutMeta that the layout @manager associated * to the @actor child of @container, eventually by creating one if the * #ClutterLayoutManager supports layout properties * * Return value: (transfer none): a #ClutterLayoutMeta, or %NULL if the * #ClutterLayoutManager does not have layout properties. The returned * layout meta instance is owned by the #ClutterLayoutManager and it * should not be unreferenced * * Since: 1.0 */ ClutterLayoutMeta * clutter_layout_manager_get_child_meta (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor) { g_return_val_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager), NULL); g_return_val_if_fail (CLUTTER_IS_CONTAINER (container), NULL); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), NULL); return get_child_meta (manager, container, actor); } static inline gboolean layout_set_property_internal (ClutterLayoutManager *manager, GObject *gobject, GParamSpec *pspec, const GValue *value) { if (pspec->flags & G_PARAM_CONSTRUCT_ONLY) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is constructor-only", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } if (!(pspec->flags & G_PARAM_WRITABLE)) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is not writable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } g_object_set_property (gobject, pspec->name, value); return TRUE; } static inline gboolean layout_get_property_internal (ClutterLayoutManager *manager, GObject *gobject, GParamSpec *pspec, GValue *value) { if (!(pspec->flags & G_PARAM_READABLE)) { g_warning ("%s: Child property '%s' of the layout manager of " "type '%s' is not readable", G_STRLOC, pspec->name, G_OBJECT_TYPE_NAME (manager)); return FALSE; } g_object_get_property (gobject, pspec->name, value); return TRUE; } /** * clutter_layout_manager_child_set: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @first_property: the first property name * @...: a list of property name and value pairs * * Sets a list of properties and their values on the #ClutterLayoutMeta * associated by @manager to a child of @container * * Languages bindings should use clutter_layout_manager_child_set_property() * instead * * Since: 1.2 */ void clutter_layout_manager_child_set (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) { ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (first_property != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); va_start (var_args, first_property); pname = first_property; while (pname) { GValue value = G_VALUE_INIT; GParamSpec *pspec; gchar *error; gboolean res; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); break; } G_VALUE_COLLECT_INIT (&value, G_PARAM_SPEC_VALUE_TYPE (pspec), var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } res = layout_set_property_internal (manager, G_OBJECT (meta), pspec, &value); g_value_unset (&value); if (!res) break; pname = va_arg (var_args, gchar*); } va_end (var_args); } /** * clutter_layout_manager_child_set_property: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @property_name: the name of the property to set * @value: a #GValue with the value of the property to set * * Sets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * Since: 1.2 */ void clutter_layout_manager_child_set_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, const GValue *value) { ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (property_name != NULL); g_return_if_fail (value != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); pspec = g_object_class_find_property (klass, property_name); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); return; } layout_set_property_internal (manager, G_OBJECT (meta), pspec, value); } /** * clutter_layout_manager_child_get: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @first_property: the name of the first property * @...: a list of property name and return location for the value pairs * * Retrieves the values for a list of properties out of the * #ClutterLayoutMeta created by @manager and attached to the * child of a @container * * Since: 1.2 */ void clutter_layout_manager_child_get (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *first_property, ...) { ClutterLayoutMeta *meta; GObjectClass *klass; const gchar *pname; va_list var_args; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (first_property != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type '%s' do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); va_start (var_args, first_property); pname = first_property; while (pname) { GValue value = G_VALUE_INIT; GParamSpec *pspec; gchar *error; gboolean res; pspec = g_object_class_find_property (klass, pname); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), pname); break; } g_value_init (&value, G_PARAM_SPEC_VALUE_TYPE (pspec)); res = layout_get_property_internal (manager, G_OBJECT (meta), pspec, &value); if (!res) { g_value_unset (&value); break; } G_VALUE_LCOPY (&value, var_args, 0, &error); if (error) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); g_value_unset (&value); break; } g_value_unset (&value); pname = va_arg (var_args, gchar*); } va_end (var_args); } /** * clutter_layout_manager_child_get_property: * @manager: a #ClutterLayoutManager * @container: a #ClutterContainer using @manager * @actor: a #ClutterActor child of @container * @property_name: the name of the property to get * @value: a #GValue with the value of the property to get * * Gets a property on the #ClutterLayoutMeta created by @manager and * attached to a child of @container * * The #GValue must already be initialized to the type of the property * and has to be unset with g_value_unset() after extracting the real * value out of it * * Since: 1.2 */ void clutter_layout_manager_child_get_property (ClutterLayoutManager *manager, ClutterContainer *container, ClutterActor *actor, const gchar *property_name, GValue *value) { ClutterLayoutMeta *meta; GObjectClass *klass; GParamSpec *pspec; g_return_if_fail (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_return_if_fail (CLUTTER_IS_CONTAINER (container)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); g_return_if_fail (property_name != NULL); g_return_if_fail (value != NULL); meta = get_child_meta (manager, container, actor); if (meta == NULL) { g_warning ("Layout managers of type %s do not support " "layout metadata", g_type_name (G_OBJECT_TYPE (manager))); return; } klass = G_OBJECT_GET_CLASS (meta); pspec = g_object_class_find_property (klass, property_name); if (pspec == NULL) { g_warning ("%s: Layout managers of type '%s' have no layout " "property named '%s'", G_STRLOC, G_OBJECT_TYPE_NAME (manager), property_name); return; } layout_get_property_internal (manager, G_OBJECT (meta), pspec, value); } /** * clutter_layout_manager_find_child_property: * @manager: a #ClutterLayoutManager * @name: the name of the property * * Retrieves the #GParamSpec for the layout property @name inside * the #ClutterLayoutMeta sub-class used by @manager * * Return value: (transfer none): a #GParamSpec describing the property, * or %NULL if no property with that name exists. The returned * #GParamSpec is owned by the layout manager and should not be * modified or freed * * Since: 1.2 */ GParamSpec * clutter_layout_manager_find_child_property (ClutterLayoutManager *manager, const gchar *name) { ClutterLayoutManagerClass *klass; GObjectClass *meta_klass; GParamSpec *pspec; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); if (meta_type == G_TYPE_INVALID) return NULL; meta_klass = g_type_class_ref (meta_type); pspec = g_object_class_find_property (meta_klass, name); g_type_class_unref (meta_klass); return pspec; } /** * clutter_layout_manager_list_child_properties: * @manager: a #ClutterLayoutManager * @n_pspecs: (out): return location for the number of returned * #GParamSpecs * * Retrieves all the #GParamSpecs for the layout properties * stored inside the #ClutterLayoutMeta sub-class used by @manager * * Return value: (transfer full) (array length=n_pspecs): the newly-allocated, * %NULL-terminated array of #GParamSpecs. Use g_free() to free the * resources allocated for the array * * Since: 1.2 */ GParamSpec ** clutter_layout_manager_list_child_properties (ClutterLayoutManager *manager, guint *n_pspecs) { ClutterLayoutManagerClass *klass; GObjectClass *meta_klass; GParamSpec **pspecs; GType meta_type; klass = CLUTTER_LAYOUT_MANAGER_GET_CLASS (manager); meta_type = klass->get_child_meta_type (manager); if (meta_type == G_TYPE_INVALID) return NULL; meta_klass = g_type_class_ref (meta_type); pspecs = g_object_class_list_properties (meta_klass, n_pspecs); g_type_class_unref (meta_klass); return pspecs; } muffin-6.4.1/clutter/clutter/clutter-paint-volume-private.h0000664000175000017500000001347314723361714023037 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_PAINT_VOLUME_PRIVATE_H__ #define __CLUTTER_PAINT_VOLUME_PRIVATE_H__ #include #include G_BEGIN_DECLS struct _ClutterPaintVolume { /* A paint volume represents a volume in a given actors private * coordinate system. */ ClutterActor *actor; /* cuboid for the volume: * * 4━━━━━━━┓5 * ┏━━━━━━━━┓╱┃ * ┃0 ┊7 1┃ ┃ * ┃ ┄┄┄┄┄┃┄┃6 * ┃3 2┃╱ * ┗━━━━━━━━┛ * * 0: top, left (origin) : always valid * 1: top, right : always valid * 2: bottom, right : updated lazily * 3: bottom, left : always valid * * 4: top, left, back : always valid * 5: top, right, back : updated lazily * 6: bottom, right, back : updated lazily * 7: bottom, left, back : updated lazily * * Elements 0, 1, 3 and 4 are filled in by the PaintVolume setters * * Note: the reason for this ordering is that we can simply ignore * elements 4, 5, 6 and 7 most of the time for 2D actors when * calculating the projected paint box. */ graphene_point3d_t vertices[8]; /* As an optimization for internally managed PaintVolumes we allow * initializing ClutterPaintVolume variables allocated on the stack * so we can avoid hammering the slice allocator. */ guint is_static:1; /* A newly initialized PaintVolume is considered empty as it is * degenerate on all three axis. * * We consider this carefully when we union an empty volume with * another so that the union simply results in a copy of the other * volume instead of also bounding the origin of the empty volume. * * For example this is a convenient property when calculating the * volume of a container as the union of the volume of its children * where the initial volume passed to the containers * ->get_paint_volume method will be empty. */ guint is_empty:1; /* TRUE when we've updated the values we calculate lazily */ guint is_complete:1; /* TRUE if vertices 4-7 can be ignored. (Only valid if complete is * TRUE) */ guint is_2d:1; /* Set to TRUE initialy but cleared if the paint volume is * transfomed by a matrix. */ guint is_axis_aligned:1; /* Note: There is a precedence to the above bitfields that should be * considered whenever we implement code that manipulates * PaintVolumes... * * Firstly if ->is_empty == TRUE then the values for ->is_complete * and ->is_2d are undefined, so you should typically check * ->is_empty as the first priority. * * XXX: document other invariables... */ }; void _clutter_paint_volume_init_static (ClutterPaintVolume *pv, ClutterActor *actor); ClutterPaintVolume *_clutter_paint_volume_new (ClutterActor *actor); void _clutter_paint_volume_copy_static (const ClutterPaintVolume *src_pv, ClutterPaintVolume *dst_pv); void _clutter_paint_volume_set_from_volume (ClutterPaintVolume *pv, const ClutterPaintVolume *src); void _clutter_paint_volume_complete (ClutterPaintVolume *pv); void _clutter_paint_volume_transform (ClutterPaintVolume *pv, const CoglMatrix *matrix); void _clutter_paint_volume_project (ClutterPaintVolume *pv, const CoglMatrix *modelview, const CoglMatrix *projection, const float *viewport); void _clutter_paint_volume_get_bounding_box (ClutterPaintVolume *pv, ClutterActorBox *box); void _clutter_paint_volume_axis_align (ClutterPaintVolume *pv); void _clutter_paint_volume_set_reference_actor (ClutterPaintVolume *pv, ClutterActor *actor); ClutterCullResult _clutter_paint_volume_cull (ClutterPaintVolume *pv, const ClutterPlane *planes); void _clutter_paint_volume_get_stage_paint_box (ClutterPaintVolume *pv, ClutterStage *stage, ClutterActorBox *box); void _clutter_paint_volume_transform_relative (ClutterPaintVolume *pv, ClutterActor *relative_to_ancestor); G_END_DECLS #endif /* __CLUTTER_PAINT_VOLUME_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-actor-box.c0000664000175000017500000003570314723361714020640 0ustar fabiofabio#include "clutter-build-config.h" #include #include "clutter-types.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-actor-box-private.h" /** * clutter_actor_box_new: * @x_1: X coordinate of the top left point * @y_1: Y coordinate of the top left point * @x_2: X coordinate of the bottom right point * @y_2: Y coordinate of the bottom right point * * Allocates a new #ClutterActorBox using the passed coordinates * for the top left and bottom right points. * * This function is the logical equivalent of: * * |[ * clutter_actor_box_init (clutter_actor_box_alloc (), * x_1, y_1, * x_2, y_2); * ]| * * Return value: (transfer full): the newly allocated #ClutterActorBox. * Use clutter_actor_box_free() to free the resources * * Since: 1.0 */ ClutterActorBox * clutter_actor_box_new (gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2) { return clutter_actor_box_init (clutter_actor_box_alloc (), x_1, y_1, x_2, y_2); } /** * clutter_actor_box_alloc: * * Allocates a new #ClutterActorBox. * * Return value: (transfer full): the newly allocated #ClutterActorBox. * Use clutter_actor_box_free() to free its resources * * Since: 1.12 */ ClutterActorBox * clutter_actor_box_alloc (void) { return g_slice_new0 (ClutterActorBox); } /** * clutter_actor_box_init: * @box: a #ClutterActorBox * @x_1: X coordinate of the top left point * @y_1: Y coordinate of the top left point * @x_2: X coordinate of the bottom right point * @y_2: Y coordinate of the bottom right point * * Initializes @box with the given coordinates. * * Return value: (transfer none): the initialized #ClutterActorBox * * Since: 1.10 */ ClutterActorBox * clutter_actor_box_init (ClutterActorBox *box, gfloat x_1, gfloat y_1, gfloat x_2, gfloat y_2) { g_return_val_if_fail (box != NULL, NULL); box->x1 = x_1; box->y1 = y_1; box->x2 = x_2; box->y2 = y_2; return box; } /** * clutter_actor_box_init_rect: * @box: a #ClutterActorBox * @x: X coordinate of the origin * @y: Y coordinate of the origin * @width: width of the box * @height: height of the box * * Initializes @box with the given origin and size. * * Since: 1.10 */ void clutter_actor_box_init_rect (ClutterActorBox *box, gfloat x, gfloat y, gfloat width, gfloat height) { g_return_if_fail (box != NULL); box->x1 = x; box->y1 = y; box->x2 = box->x1 + width; box->y2 = box->y1 + height; } /** * clutter_actor_box_copy: * @box: a #ClutterActorBox * * Copies @box * * Return value: a newly allocated copy of #ClutterActorBox. Use * clutter_actor_box_free() to free the allocated resources * * Since: 1.0 */ ClutterActorBox * clutter_actor_box_copy (const ClutterActorBox *box) { if (G_LIKELY (box != NULL)) return g_slice_dup (ClutterActorBox, box); return NULL; } /** * clutter_actor_box_free: * @box: a #ClutterActorBox * * Frees a #ClutterActorBox allocated using clutter_actor_box_new() * or clutter_actor_box_copy() * * Since: 1.0 */ void clutter_actor_box_free (ClutterActorBox *box) { if (G_LIKELY (box != NULL)) g_slice_free (ClutterActorBox, box); } /** * clutter_actor_box_equal: * @box_a: a #ClutterActorBox * @box_b: a #ClutterActorBox * * Checks @box_a and @box_b for equality * * Return value: %TRUE if the passed #ClutterActorBox are equal * * Since: 1.0 */ gboolean clutter_actor_box_equal (const ClutterActorBox *box_a, const ClutterActorBox *box_b) { g_return_val_if_fail (box_a != NULL && box_b != NULL, FALSE); if (box_a == box_b) return TRUE; return box_a->x1 == box_b->x1 && box_a->y1 == box_b->y1 && box_a->x2 == box_b->x2 && box_a->y2 == box_b->y2; } /** * clutter_actor_box_get_x: * @box: a #ClutterActorBox * * Retrieves the X coordinate of the origin of @box * * Return value: the X coordinate of the origin * * Since: 1.0 */ gfloat clutter_actor_box_get_x (const ClutterActorBox *box) { g_return_val_if_fail (box != NULL, 0.); return box->x1; } /** * clutter_actor_box_get_y: * @box: a #ClutterActorBox * * Retrieves the Y coordinate of the origin of @box * * Return value: the Y coordinate of the origin * * Since: 1.0 */ gfloat clutter_actor_box_get_y (const ClutterActorBox *box) { g_return_val_if_fail (box != NULL, 0.); return box->y1; } /** * clutter_actor_box_get_width: * @box: a #ClutterActorBox * * Retrieves the width of the @box * * Return value: the width of the box * * Since: 1.0 */ gfloat clutter_actor_box_get_width (const ClutterActorBox *box) { g_return_val_if_fail (box != NULL, 0.); return box->x2 - box->x1; } /** * clutter_actor_box_get_height: * @box: a #ClutterActorBox * * Retrieves the height of the @box * * Return value: the height of the box * * Since: 1.0 */ gfloat clutter_actor_box_get_height (const ClutterActorBox *box) { g_return_val_if_fail (box != NULL, 0.); return box->y2 - box->y1; } /** * clutter_actor_box_get_origin: * @box: a #ClutterActorBox * @x: (out) (allow-none): return location for the X coordinate, or %NULL * @y: (out) (allow-none): return location for the Y coordinate, or %NULL * * Retrieves the origin of @box * * Since: 1.0 */ void clutter_actor_box_get_origin (const ClutterActorBox *box, gfloat *x, gfloat *y) { g_return_if_fail (box != NULL); if (x) *x = box->x1; if (y) *y = box->y1; } /** * clutter_actor_box_get_size: * @box: a #ClutterActorBox * @width: (out) (allow-none): return location for the width, or %NULL * @height: (out) (allow-none): return location for the height, or %NULL * * Retrieves the size of @box * * Since: 1.0 */ void clutter_actor_box_get_size (const ClutterActorBox *box, gfloat *width, gfloat *height) { g_return_if_fail (box != NULL); if (width) *width = box->x2 - box->x1; if (height) *height = box->y2 - box->y1; } /** * clutter_actor_box_get_area: * @box: a #ClutterActorBox * * Retrieves the area of @box * * Return value: the area of a #ClutterActorBox, in pixels * * Since: 1.0 */ gfloat clutter_actor_box_get_area (const ClutterActorBox *box) { g_return_val_if_fail (box != NULL, 0.); return (box->x2 - box->x1) * (box->y2 - box->y1); } /** * clutter_actor_box_contains: * @box: a #ClutterActorBox * @x: X coordinate of the point * @y: Y coordinate of the point * * Checks whether a point with @x, @y coordinates is contained * withing @box * * Return value: %TRUE if the point is contained by the #ClutterActorBox * * Since: 1.0 */ gboolean clutter_actor_box_contains (const ClutterActorBox *box, gfloat x, gfloat y) { g_return_val_if_fail (box != NULL, FALSE); return (x > box->x1 && x < box->x2) && (y > box->y1 && y < box->y2); } /** * clutter_actor_box_from_vertices: * @box: a #ClutterActorBox * @verts: (array fixed-size=4): array of four #graphene_point3d_t * * Calculates the bounding box represented by the four vertices; for details * of the vertex array see clutter_actor_get_abs_allocation_vertices(). * * Since: 1.0 */ void clutter_actor_box_from_vertices (ClutterActorBox *box, const graphene_point3d_t verts[]) { gfloat x_1, x_2, y_1, y_2; g_return_if_fail (box != NULL); g_return_if_fail (verts != NULL); /* 4-way min/max */ x_1 = verts[0].x; y_1 = verts[0].y; if (verts[1].x < x_1) x_1 = verts[1].x; if (verts[2].x < x_1) x_1 = verts[2].x; if (verts[3].x < x_1) x_1 = verts[3].x; if (verts[1].y < y_1) y_1 = verts[1].y; if (verts[2].y < y_1) y_1 = verts[2].y; if (verts[3].y < y_1) y_1 = verts[3].y; x_2 = verts[0].x; y_2 = verts[0].y; if (verts[1].x > x_2) x_2 = verts[1].x; if (verts[2].x > x_2) x_2 = verts[2].x; if (verts[3].x > x_2) x_2 = verts[3].x; if (verts[1].y > y_2) y_2 = verts[1].y; if (verts[2].y > y_2) y_2 = verts[2].y; if (verts[3].y > y_2) y_2 = verts[3].y; box->x1 = x_1; box->x2 = x_2; box->y1 = y_1; box->y2 = y_2; } /** * clutter_actor_box_interpolate: * @initial: the initial #ClutterActorBox * @final: the final #ClutterActorBox * @progress: the interpolation progress * @result: (out): return location for the interpolation * * Interpolates between @initial and @final #ClutterActorBoxes * using @progress * * Since: 1.2 */ void clutter_actor_box_interpolate (const ClutterActorBox *initial, const ClutterActorBox *final, gdouble progress, ClutterActorBox *result) { g_return_if_fail (initial != NULL); g_return_if_fail (final != NULL); g_return_if_fail (result != NULL); result->x1 = initial->x1 + (final->x1 - initial->x1) * progress; result->y1 = initial->y1 + (final->y1 - initial->y1) * progress; result->x2 = initial->x2 + (final->x2 - initial->x2) * progress; result->y2 = initial->y2 + (final->y2 - initial->y2) * progress; } /** * clutter_actor_box_clamp_to_pixel: * @box: (inout): the #ClutterActorBox to clamp * * Clamps the components of @box to the nearest integer * * Since: 1.2 */ void clutter_actor_box_clamp_to_pixel (ClutterActorBox *box) { g_return_if_fail (box != NULL); box->x1 = floorf (box->x1); box->y1 = floorf (box->y1); box->x2 = ceilf (box->x2); box->y2 = ceilf (box->y2); } /** * clutter_actor_box_union: * @a: (in): the first #ClutterActorBox * @b: (in): the second #ClutterActorBox * @result: (out): the #ClutterActorBox representing a union * of @a and @b * * Unions the two boxes @a and @b and stores the result in @result. * * Since: 1.4 */ void clutter_actor_box_union (const ClutterActorBox *a, const ClutterActorBox *b, ClutterActorBox *result) { g_return_if_fail (a != NULL); g_return_if_fail (b != NULL); g_return_if_fail (result != NULL); result->x1 = MIN (a->x1, b->x1); result->y1 = MIN (a->y1, b->y1); result->x2 = MAX (a->x2, b->x2); result->y2 = MAX (a->y2, b->y2); } static gboolean clutter_actor_box_progress (const GValue *a, const GValue *b, gdouble factor, GValue *retval) { ClutterActorBox res = { 0, }; clutter_actor_box_interpolate (g_value_get_boxed (a), g_value_get_boxed (b), factor, &res); g_value_set_boxed (retval, &res); return TRUE; } /** * clutter_actor_box_set_origin: * @box: a #ClutterActorBox * @x: the X coordinate of the new origin * @y: the Y coordinate of the new origin * * Changes the origin of @box, maintaining the size of the #ClutterActorBox. * * Since: 1.6 */ void clutter_actor_box_set_origin (ClutterActorBox *box, gfloat x, gfloat y) { gfloat width, height; g_return_if_fail (box != NULL); width = box->x2 - box->x1; height = box->y2 - box->y1; clutter_actor_box_init_rect (box, x, y, width, height); } /** * clutter_actor_box_set_size: * @box: a #ClutterActorBox * @width: the new width * @height: the new height * * Sets the size of @box, maintaining the origin of the #ClutterActorBox. * * Since: 1.6 */ void clutter_actor_box_set_size (ClutterActorBox *box, gfloat width, gfloat height) { g_return_if_fail (box != NULL); box->x2 = box->x1 + width; box->y2 = box->y1 + height; } void _clutter_actor_box_enlarge_for_effects (ClutterActorBox *box) { float width, height; /* The aim here is that for a given rectangle defined with floating point * coordinates we want to determine a stable quantized size in pixels * that doesn't vary due to the original box's sub-pixel position. * * The reason this is important is because effects will use this * API to determine the size of offscreen framebuffers and so for * a fixed-size object that may be animated accross the screen we * want to make sure that the stage paint-box has an equally stable * size so that effects aren't made to continuously re-allocate * a corresponding fbo. * * The other thing we consider is that the calculation of this box is * subject to floating point precision issues that might be slightly * different to the precision issues involved with actually painting the * actor, which might result in painting slightly leaking outside the * user's calculated paint-volume. For this we simply aim to pad out the * paint-volume by at least half a pixel all the way around. */ width = box->x2 - box->x1; height = box->y2 - box->y1; width = CLUTTER_NEARBYINT (width); height = CLUTTER_NEARBYINT (height); /* XXX: NB the width/height may now be up to 0.5px too small so we * must also pad by 0.25px all around to account for this. In total we * must padd by at least 0.75px around all sides. */ /* XXX: The furthest that we can overshoot the bottom right corner by * here is 1.75px in total if you consider that the 0.75 padding could * just cross an integer boundary and so ceil will effectively add 1. */ box->x2 = ceilf (box->x2 + 0.75); box->y2 = ceilf (box->y2 + 0.75); /* Now we redefine the top-left relative to the bottom right based on the * rounded width/height determined above + a constant so that the overall * size of the box will be stable and not dependant on the box's * position. * * Adding 3px to the width/height will ensure we cover the maximum of * 1.75px padding on the bottom/right and still ensure we have > 0.75px * padding on the top/left. */ box->x1 = box->x2 - width - 3; box->y1 = box->y2 - height - 3; } /** * clutter_actor_box_scale: * @box: a #ClutterActorBox * @scale: scale factor for resizing this box * * Rescale the @box by provided @scale factor. * * Since: 1.6 */ void clutter_actor_box_scale (ClutterActorBox *box, gfloat scale) { g_return_if_fail (box != NULL); box->x1 *= scale; box->x2 *= scale; box->y1 *= scale; box->y2 *= scale; } G_DEFINE_BOXED_TYPE_WITH_CODE (ClutterActorBox, clutter_actor_box, clutter_actor_box_copy, clutter_actor_box_free, CLUTTER_REGISTER_INTERVAL_PROGRESS (clutter_actor_box_progress)); muffin-6.4.1/clutter/clutter/clutter-paint-node.c0000664000175000017500000007644214723361714021005 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-paint-node * @Title: ClutterPaintNode * @Short_Description: Paint objects * * #ClutterPaintNode is an element in the render graph. * * The render graph contains all the elements that need to be painted by * Clutter when submitting a frame to the graphics system. * * The render graph is distinct from the scene graph: the scene graph is * composed by actors, which can be visible or invisible; the scene graph * elements also respond to events. The render graph, instead, is only * composed by nodes that will be painted. * * Each #ClutterActor can submit multiple #ClutterPaintNodes to * the render graph. */ /** * ClutterPaintNode: (ref-func clutter_paint_node_ref) (unref-func clutter_paint_node_unref) (set-value-func clutter_value_set_paint_node) (get-value-func clutter_value_get_paint_node) * * The `ClutterPaintNode` structure contains only private data * and it should be accessed using the provided API. * * Since: 1.10 */ /** * ClutterPaintNodeClass: * * The `ClutterPaintNodeClass` structure contains only private data. * * Since: 1.10 */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include #include #include #include "clutter-paint-node-private.h" #include "clutter-debug.h" #include "clutter-private.h" #include static inline void clutter_paint_operation_clear (ClutterPaintOperation *op); static void value_paint_node_init (GValue *value) { value->data[0].v_pointer = NULL; } static void value_paint_node_free_value (GValue *value) { if (value->data[0].v_pointer != NULL) clutter_paint_node_unref (value->data[0].v_pointer); } static void value_paint_node_copy_value (const GValue *src, GValue *dst) { if (src->data[0].v_pointer != NULL) dst->data[0].v_pointer = clutter_paint_node_ref (src->data[0].v_pointer); else dst->data[0].v_pointer = NULL; } static gpointer value_paint_node_peek_pointer (const GValue *value) { return value->data[0].v_pointer; } static gchar * value_paint_node_collect_value (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { ClutterPaintNode *node; node = collect_values[0].v_pointer; if (node == NULL) { value->data[0].v_pointer = NULL; return NULL; } if (node->parent_instance.g_class == NULL) return g_strconcat ("invalid unclassed ClutterPaintNode pointer for " "value type '", G_VALUE_TYPE_NAME (value), "'", NULL); value->data[0].v_pointer = clutter_paint_node_ref (node); return NULL; } static gchar * value_paint_node_lcopy_value (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { ClutterPaintNode **node_p = collect_values[0].v_pointer; if (node_p == NULL) return g_strconcat ("value location for '", G_VALUE_TYPE_NAME (value), "' passed as NULL", NULL); if (value->data[0].v_pointer == NULL) *node_p = NULL; else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) *node_p = value->data[0].v_pointer; else *node_p = clutter_paint_node_ref (value->data[0].v_pointer); return NULL; } static void clutter_paint_node_class_base_init (ClutterPaintNodeClass *klass) { } static void clutter_paint_node_class_base_finalize (ClutterPaintNodeClass *klass) { } static void clutter_paint_node_real_finalize (ClutterPaintNode *node) { ClutterPaintNode *iter; if (node->operations != NULL) { guint i; for (i = 0; i < node->operations->len; i++) { ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); clutter_paint_operation_clear (op); } g_array_unref (node->operations); } iter = node->first_child; while (iter != NULL) { ClutterPaintNode *next = iter->next_sibling; clutter_paint_node_remove_child (node, iter); iter = next; } g_type_free_instance ((GTypeInstance *) node); } static gboolean clutter_paint_node_real_pre_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { return FALSE; } static void clutter_paint_node_real_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { } static void clutter_paint_node_real_post_draw (ClutterPaintNode *node, ClutterPaintContext *paint_context) { } static void clutter_paint_node_class_init (ClutterPaintNodeClass *klass) { klass->pre_draw = clutter_paint_node_real_pre_draw; klass->draw = clutter_paint_node_real_draw; klass->post_draw = clutter_paint_node_real_post_draw; klass->finalize = clutter_paint_node_real_finalize; } static void clutter_paint_node_init (ClutterPaintNode *self) { self->ref_count = 1; } GType clutter_paint_node_get_type (void) { static gsize paint_node_type_id = 0; if (g_once_init_enter (&paint_node_type_id)) { static const GTypeFundamentalInfo finfo = { (G_TYPE_FLAG_CLASSED | G_TYPE_FLAG_INSTANTIATABLE | G_TYPE_FLAG_DERIVABLE | G_TYPE_FLAG_DEEP_DERIVABLE), }; static const GTypeValueTable value_table = { value_paint_node_init, value_paint_node_free_value, value_paint_node_copy_value, value_paint_node_peek_pointer, "p", value_paint_node_collect_value, "p", value_paint_node_lcopy_value, }; const GTypeInfo node_info = { sizeof (ClutterPaintNodeClass), (GBaseInitFunc) clutter_paint_node_class_base_init, (GBaseFinalizeFunc) clutter_paint_node_class_base_finalize, (GClassInitFunc) clutter_paint_node_class_init, (GClassFinalizeFunc) NULL, NULL, sizeof (ClutterPaintNode), 0, (GInstanceInitFunc) clutter_paint_node_init, &value_table, }; GType id = g_type_register_fundamental (g_type_fundamental_next (), I_("ClutterPaintNode"), &node_info, &finfo, G_TYPE_FLAG_ABSTRACT); g_once_init_leave (&paint_node_type_id, id); } return paint_node_type_id; } /** * clutter_paint_node_set_name: * @node: a #ClutterPaintNode * @name: a string annotating the @node * * Sets a user-readable @name for @node. * * The @name will be used for debugging purposes. * * The @node will intern @name using g_intern_string(). If you have access to a * static string, use clutter_paint_node_set_static_name() instead. * * Since: 1.10 */ void clutter_paint_node_set_name (ClutterPaintNode *node, const char *name) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); node->name = g_intern_string (name); } /** * clutter_paint_node_set_static_name: (skip) * * Like clutter_paint_node_set_name() but uses a static or interned string * containing the name. */ void clutter_paint_node_set_static_name (ClutterPaintNode *node, const char *name) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); node->name = name; } /** * clutter_paint_node_ref: * @node: a #ClutterPaintNode * * Acquires a reference on @node. * * Return value: (transfer full): the #ClutterPaintNode * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_ref (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); g_atomic_int_inc (&node->ref_count); return node; } /** * clutter_paint_node_unref: * @node: a #ClutterPaintNode * * Releases a reference on @node. * * Since: 1.10 */ void clutter_paint_node_unref (ClutterPaintNode *node) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); if (g_atomic_int_dec_and_test (&node->ref_count)) { ClutterPaintNodeClass *klass = CLUTTER_PAINT_NODE_GET_CLASS (node); klass->finalize (node); } } /** * clutter_paint_node_add_child: * @node: a #ClutterPaintNode * @child: the child #ClutterPaintNode to add * * Adds @child to the list of children of @node. * * This function will acquire a reference on @child. * * Since: 1.10 */ void clutter_paint_node_add_child (ClutterPaintNode *node, ClutterPaintNode *child) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (CLUTTER_IS_PAINT_NODE (child)); g_return_if_fail (node != child); g_return_if_fail (child->parent == NULL); child->parent = node; clutter_paint_node_ref (child); node->n_children += 1; child->prev_sibling = node->last_child; if (node->last_child != NULL) { ClutterPaintNode *tmp = node->last_child; tmp->next_sibling = child; } if (child->prev_sibling == NULL) node->first_child = child; if (child->next_sibling == NULL) node->last_child = child; } /** * clutter_paint_node_remove_child: * @node: a #ClutterPaintNode * @child: the #ClutterPaintNode to remove * * Removes @child from the list of children of @node. * * This function will release the reference on @child acquired by * using clutter_paint_node_add_child(). * * Since: 1.10 */ void clutter_paint_node_remove_child (ClutterPaintNode *node, ClutterPaintNode *child) { ClutterPaintNode *prev, *next; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (CLUTTER_IS_PAINT_NODE (child)); g_return_if_fail (node != child); g_return_if_fail (child->parent == node); node->n_children -= 1; prev = child->prev_sibling; next = child->next_sibling; if (prev != NULL) prev->next_sibling = next; if (next != NULL) next->prev_sibling = prev; if (node->first_child == child) node->first_child = next; if (node->last_child == child) node->last_child = prev; child->prev_sibling = NULL; child->next_sibling = NULL; child->parent = NULL; clutter_paint_node_unref (child); } /** * clutter_paint_node_replace_child: * @node: a #ClutterPaintNode * @old_child: the child replaced by @new_child * @new_child: the child that replaces @old_child * * Atomically replaces @old_child with @new_child in the list of * children of @node. * * This function will release the reference on @old_child acquired * by @node, and will acquire a new reference on @new_child. * * Since: 1.10 */ void clutter_paint_node_replace_child (ClutterPaintNode *node, ClutterPaintNode *old_child, ClutterPaintNode *new_child) { ClutterPaintNode *prev, *next; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (CLUTTER_IS_PAINT_NODE (old_child)); g_return_if_fail (old_child->parent == node); g_return_if_fail (CLUTTER_IS_PAINT_NODE (new_child)); g_return_if_fail (new_child->parent == NULL); prev = old_child->prev_sibling; next = old_child->next_sibling; new_child->parent = node; new_child->prev_sibling = prev; new_child->next_sibling = next; clutter_paint_node_ref (new_child); if (prev != NULL) prev->next_sibling = new_child; if (next != NULL) next->prev_sibling = new_child; if (node->first_child == old_child) node->first_child = new_child; if (node->last_child == old_child) node->last_child = new_child; old_child->prev_sibling = NULL; old_child->next_sibling = NULL; old_child->parent = NULL; clutter_paint_node_unref (old_child); } /** * clutter_paint_node_remove_all: * @node: a #ClutterPaintNode * * Removes all children of @node. * * This function releases the reference acquired by @node on its * children. * * Since: 1.10 */ void clutter_paint_node_remove_all (ClutterPaintNode *node) { ClutterPaintNode *iter; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); iter = node->first_child; while (iter != NULL) { ClutterPaintNode *next = iter->next_sibling; clutter_paint_node_remove_child (node, iter); iter = next; } } /** * clutter_paint_node_get_first_child: * @node: a #ClutterPaintNode * * Retrieves the first child of the @node. * * Return value: (transfer none): a pointer to the first child of * the #ClutterPaintNode. * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_get_first_child (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); return node->first_child; } /** * clutter_paint_node_get_previous_sibling: * @node: a #ClutterPaintNode * * Retrieves the previous sibling of @node. * * Return value: (transfer none): a pointer to the previous sibling * of the #ClutterPaintNode. * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_get_previous_sibling (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); return node->prev_sibling; } /** * clutter_paint_node_get_next_sibling: * @node: a #ClutterPaintNode * * Retrieves the next sibling of @node. * * Return value: (transfer none): a pointer to the next sibling * of a #ClutterPaintNode * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_get_next_sibling (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); return node->next_sibling; } /** * clutter_paint_node_get_last_child: * @node: a #ClutterPaintNode * * Retrieves the last child of @node. * * Return value: (transfer none): a pointer to the last child * of a #ClutterPaintNode * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_get_last_child (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); return node->last_child; } /** * clutter_paint_node_get_parent: * @node: a #ClutterPaintNode * * Retrieves the parent of @node. * * Return value: (transfer none): a pointer to the parent of * a #ClutterPaintNode * * Since: 1.10 */ ClutterPaintNode * clutter_paint_node_get_parent (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), NULL); return node->parent; } /** * clutter_paint_node_get_n_children: * @node: a #ClutterPaintNode * * Retrieves the number of children of @node. * * Return value: the number of children of a #ClutterPaintNode * * Since: 1.10 */ guint clutter_paint_node_get_n_children (ClutterPaintNode *node) { g_return_val_if_fail (CLUTTER_IS_PAINT_NODE (node), 0); return node->n_children; } /** * clutter_value_set_paint_node: * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE * @node: (type Clutter.PaintNode) (allow-none): a #ClutterPaintNode, or %NULL * * Sets the contents of a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE. * * This function increased the reference count of @node; if you do not wish * to increase the reference count, use clutter_value_take_paint_node() * instead. The reference count will be released by g_value_unset(). * * Since: 1.10 */ void clutter_value_set_paint_node (GValue *value, gpointer node) { ClutterPaintNode *old_node; g_return_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value)); old_node = value->data[0].v_pointer; if (node != NULL) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); value->data[0].v_pointer = clutter_paint_node_ref (node); } else value->data[0].v_pointer = NULL; if (old_node != NULL) clutter_paint_node_unref (old_node); } /** * clutter_value_take_paint_node: * @value: a #GValue, initialized with %CLUTTER_TYPE_PAINT_NODE * @node: (type Clutter.PaintNode) (allow-none): a #ClutterPaintNode, or %NULL * * Sets the contents of a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE. * * Unlike clutter_value_set_paint_node(), this function will not take a * reference on the passed @node: instead, it will take ownership of the * current reference count. * * Since: 1.10 */ void clutter_value_take_paint_node (GValue *value, gpointer node) { ClutterPaintNode *old_node; g_return_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value)); old_node = value->data[0].v_pointer; if (node != NULL) { g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); /* take over ownership */ value->data[0].v_pointer = node; } else value->data[0].v_pointer = NULL; if (old_node != NULL) clutter_paint_node_unref (old_node); } /** * clutter_value_get_paint_node: * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE * * Retrieves a pointer to the #ClutterPaintNode contained inside * the passed #GValue. * * Return value: (transfer none) (type Clutter.PaintNode): a pointer to * a #ClutterPaintNode, or %NULL * * Since: 1.10 */ gpointer clutter_value_get_paint_node (const GValue *value) { g_return_val_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value), NULL); return value->data[0].v_pointer; } /** * clutter_value_dup_paint_node: * @value: a #GValue initialized with %CLUTTER_TYPE_PAINT_NODE * * Retrieves a pointer to the #ClutterPaintNode contained inside * the passed #GValue, and if not %NULL it will increase the * reference count. * * Return value: (transfer full) (type Clutter.PaintNode): a pointer * to the #ClutterPaintNode, with its reference count increased, * or %NULL * * Since: 1.10 */ gpointer clutter_value_dup_paint_node (const GValue *value) { g_return_val_if_fail (CLUTTER_VALUE_HOLDS_PAINT_NODE (value), NULL); if (value->data[0].v_pointer != NULL) return clutter_paint_node_ref (value->data[0].v_pointer); return NULL; } static inline void clutter_paint_operation_clear (ClutterPaintOperation *op) { switch (op->opcode) { case PAINT_OP_INVALID: break; case PAINT_OP_TEX_RECT: break; case PAINT_OP_MULTITEX_RECT: if (op->multitex_coords != NULL) g_array_unref (op->multitex_coords); break; case PAINT_OP_PATH: if (op->op.path != NULL) cogl_object_unref (op->op.path); break; case PAINT_OP_PRIMITIVE: if (op->op.primitive != NULL) cogl_object_unref (op->op.primitive); break; } } static inline void clutter_paint_op_init_tex_rect (ClutterPaintOperation *op, const ClutterActorBox *rect, float x_1, float y_1, float x_2, float y_2) { clutter_paint_operation_clear (op); op->opcode = PAINT_OP_TEX_RECT; op->op.texrect[0] = rect->x1; op->op.texrect[1] = rect->y1; op->op.texrect[2] = rect->x2; op->op.texrect[3] = rect->y2; op->op.texrect[4] = x_1; op->op.texrect[5] = y_1; op->op.texrect[6] = x_2; op->op.texrect[7] = y_2; } static inline void clutter_paint_op_init_multitex_rect (ClutterPaintOperation *op, const ClutterActorBox *rect, const float *tex_coords, unsigned int tex_coords_len) { clutter_paint_operation_clear (op); op->opcode = PAINT_OP_MULTITEX_RECT; op->multitex_coords = g_array_sized_new (FALSE, FALSE, sizeof (float), tex_coords_len); g_array_append_vals (op->multitex_coords, tex_coords, tex_coords_len); op->op.texrect[0] = rect->x1; op->op.texrect[1] = rect->y1; op->op.texrect[2] = rect->x2; op->op.texrect[3] = rect->y2; } static inline void clutter_paint_op_init_path (ClutterPaintOperation *op, CoglPath *path) { clutter_paint_operation_clear (op); op->opcode = PAINT_OP_PATH; op->op.path = cogl_object_ref (path); } static inline void clutter_paint_op_init_primitive (ClutterPaintOperation *op, CoglPrimitive *primitive) { clutter_paint_operation_clear (op); op->opcode = PAINT_OP_PRIMITIVE; op->op.primitive = cogl_object_ref (primitive); } static inline void clutter_paint_node_maybe_init_operations (ClutterPaintNode *node) { if (node->operations != NULL) return; node->operations = g_array_new (FALSE, FALSE, sizeof (ClutterPaintOperation)); } /** * clutter_paint_node_add_rectangle: * @node: a #ClutterPaintNode * @rect: a #ClutterActorBox * * Adds a rectangle region to the @node, as described by the * passed @rect. * * Since: 1.10 */ void clutter_paint_node_add_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect) { ClutterPaintOperation operation = PAINT_OP_INIT; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (rect != NULL); clutter_paint_node_maybe_init_operations (node); clutter_paint_op_init_tex_rect (&operation, rect, 0.0, 0.0, 1.0, 1.0); g_array_append_val (node->operations, operation); } /** * clutter_paint_node_add_texture_rectangle: * @node: a #ClutterPaintNode * @rect: a #ClutterActorBox * @x_1: the left X coordinate of the texture * @y_1: the top Y coordinate of the texture * @x_2: the right X coordinate of the texture * @y_2: the bottom Y coordinate of the texture * * Adds a rectangle region to the @node, with texture coordinates. * * Since: 1.10 */ void clutter_paint_node_add_texture_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect, float x_1, float y_1, float x_2, float y_2) { ClutterPaintOperation operation = PAINT_OP_INIT; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (rect != NULL); clutter_paint_node_maybe_init_operations (node); clutter_paint_op_init_tex_rect (&operation, rect, x_1, y_1, x_2, y_2); g_array_append_val (node->operations, operation); } /** * clutter_paint_node_add_multitexture_rectangle: * @node: a #ClutterPaintNode * @rect: a #ClutterActorBox * @text_coords: array of multitexture values * @text_coords_len: number of items of @text_coords * * Adds a rectangle region to the @node, with multitexture coordinates. */ void clutter_paint_node_add_multitexture_rectangle (ClutterPaintNode *node, const ClutterActorBox *rect, const float *text_coords, unsigned int text_coords_len) { ClutterPaintOperation operation = PAINT_OP_INIT; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (rect != NULL); clutter_paint_node_maybe_init_operations (node); clutter_paint_op_init_multitex_rect (&operation, rect, text_coords, text_coords_len); g_array_append_val (node->operations, operation); } /** * clutter_paint_node_add_path: (skip) * @node: a #ClutterPaintNode * @path: a Cogl path * * Adds a region described as a path to the @node. * * This function acquires a reference on the passed @path, so it * is safe to call cogl_object_unref() when it returns. * * Since: 1.10 * Stability: unstable */ void clutter_paint_node_add_path (ClutterPaintNode *node, CoglPath *path) { ClutterPaintOperation operation = PAINT_OP_INIT; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (cogl_is_path (path)); clutter_paint_node_maybe_init_operations (node); clutter_paint_op_init_path (&operation, path); g_array_append_val (node->operations, operation); } /** * clutter_paint_node_add_primitive: (skip) * @node: a #ClutterPaintNode * @primitive: a Cogl primitive * * Adds a region described by a Cogl primitive to the @node. * * This function acquires a reference on @primitive, so it is safe * to call cogl_object_unref() when it returns. * * Since: 1.10 */ void clutter_paint_node_add_primitive (ClutterPaintNode *node, CoglPrimitive *primitive) { ClutterPaintOperation operation = PAINT_OP_INIT; g_return_if_fail (CLUTTER_IS_PAINT_NODE (node)); g_return_if_fail (cogl_is_primitive (primitive)); clutter_paint_node_maybe_init_operations (node); clutter_paint_op_init_primitive (&operation, primitive); g_array_append_val (node->operations, operation); } /** * clutter_paint_node_paint: * @node: a #ClutterPaintNode * * Paints the @node using the class implementation, traversing * its children, if any. */ void clutter_paint_node_paint (ClutterPaintNode *node, ClutterPaintContext *paint_context) { ClutterPaintNodeClass *klass = CLUTTER_PAINT_NODE_GET_CLASS (node); ClutterPaintNode *iter; gboolean res; res = klass->pre_draw (node, paint_context); if (res) { klass->draw (node, paint_context); } for (iter = node->first_child; iter != NULL; iter = iter->next_sibling) { clutter_paint_node_paint (iter, paint_context); } if (res) { klass->post_draw (node, paint_context); } } #ifdef CLUTTER_ENABLE_DEBUG static JsonNode * clutter_paint_node_serialize (ClutterPaintNode *node) { ClutterPaintNodeClass *klass = CLUTTER_PAINT_NODE_GET_CLASS (node); if (klass->serialize != NULL) return klass->serialize (node); return json_node_new (JSON_NODE_NULL); } static JsonNode * clutter_paint_node_to_json (ClutterPaintNode *node) { JsonBuilder *builder; JsonNode *res; builder = json_builder_new (); json_builder_begin_object (builder); json_builder_set_member_name (builder, "type"); json_builder_add_string_value (builder, g_type_name (G_TYPE_FROM_INSTANCE (node))); json_builder_set_member_name (builder, "name"); json_builder_add_string_value (builder, node->name); json_builder_set_member_name (builder, "node-data"); json_builder_add_value (builder, clutter_paint_node_serialize (node)); json_builder_set_member_name (builder, "operations"); json_builder_begin_array (builder); if (node->operations != NULL) { guint i, j; for (i = 0; i < node->operations->len; i++) { const ClutterPaintOperation *op; op = &g_array_index (node->operations, ClutterPaintOperation, i); json_builder_begin_object (builder); switch (op->opcode) { case PAINT_OP_TEX_RECT: json_builder_set_member_name (builder, "texrect"); json_builder_begin_array (builder); json_builder_add_double_value (builder, op->op.texrect[0]); json_builder_add_double_value (builder, op->op.texrect[1]); json_builder_add_double_value (builder, op->op.texrect[2]); json_builder_add_double_value (builder, op->op.texrect[3]); json_builder_add_double_value (builder, op->op.texrect[4]); json_builder_add_double_value (builder, op->op.texrect[5]); json_builder_add_double_value (builder, op->op.texrect[6]); json_builder_add_double_value (builder, op->op.texrect[7]); json_builder_end_array (builder); break; case PAINT_OP_MULTITEX_RECT: json_builder_set_member_name (builder, "texrect"); json_builder_begin_array (builder); for (j = 0; i < op->multitex_coords->len; j++) { float coord = g_array_index (op->multitex_coords, float, j); json_builder_add_double_value (builder, coord); } json_builder_end_array (builder); break; case PAINT_OP_PATH: json_builder_set_member_name (builder, "path"); json_builder_add_int_value (builder, (intptr_t) op->op.path); break; case PAINT_OP_PRIMITIVE: json_builder_set_member_name (builder, "primitive"); json_builder_add_int_value (builder, (intptr_t) op->op.primitive); break; case PAINT_OP_INVALID: break; } json_builder_end_object (builder); } } json_builder_end_array (builder); json_builder_set_member_name (builder, "children"); json_builder_begin_array (builder); if (node->first_child != NULL) { ClutterPaintNode *child; for (child = node->first_child; child != NULL; child = child->next_sibling) { JsonNode *n = clutter_paint_node_to_json (child); json_builder_add_value (builder, n); } } json_builder_end_array (builder); json_builder_end_object (builder); res = json_builder_get_root (builder); g_object_unref (builder); return res; } #endif /* CLUTTER_ENABLE_DEBUG */ void _clutter_paint_node_dump_tree (ClutterPaintNode *node) { #ifdef CLUTTER_ENABLE_DEBUG JsonGenerator *gen = json_generator_new (); char *str; gsize len; json_generator_set_root (gen, clutter_paint_node_to_json (node)); str = json_generator_to_data (gen, &len); g_print ("Render tree starting from %p:\n%s\n", node, str); g_free (str); #endif /* CLUTTER_ENABLE_DEBUG */ } /*< private > * _clutter_paint_node_create: * @gtype: a #ClutterPaintNode type * * Creates a new #ClutterPaintNode instance using @gtype * * Return value: (transfer full): the newly created #ClutterPaintNode * sub-class instance; use clutter_paint_node_unref() when done */ gpointer _clutter_paint_node_create (GType gtype) { g_return_val_if_fail (g_type_is_a (gtype, CLUTTER_TYPE_PAINT_NODE), NULL); return (gpointer) g_type_create_instance (gtype); } static ClutterPaintNode * clutter_paint_node_get_root (ClutterPaintNode *node) { ClutterPaintNode *iter; iter = node; while (iter != NULL && iter->parent != NULL) iter = iter->parent; return iter; } /** * clutter_paint_node_get_framebuffer: * @node: a #ClutterPaintNode * * Retrieves the #CoglFramebuffer that @node will draw * into, if it the root node has a custom framebuffer set. * * Returns: (transfer none): a #CoglFramebuffer or %NULL if no custom one is * set. */ CoglFramebuffer * clutter_paint_node_get_framebuffer (ClutterPaintNode *node) { ClutterPaintNode *root = clutter_paint_node_get_root (node); ClutterPaintNodeClass *klass; klass = CLUTTER_PAINT_NODE_GET_CLASS (root); if (klass->get_framebuffer != NULL) return klass->get_framebuffer (root); else return NULL; } muffin-6.4.1/clutter/clutter/clutter-flow-layout.c0000664000175000017500000012706014723361714021222 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-flow-layout * @short_description: A reflowing layout manager * * #ClutterFlowLayout is a layout manager which implements the following * policy: * * - the preferred natural size depends on the value * of the #ClutterFlowLayout:orientation property; the layout will try * to maintain all its children on a single row or * column; * - if either the width or the height allocated are * smaller than the preferred ones, the layout will wrap; in this case, * the preferred height or width, respectively, will take into account * the amount of columns and rows; * - each line (either column or row) in reflowing will * have the size of the biggest cell on that line; if the * #ClutterFlowLayout:homogeneous property is set to %FALSE the actor * will be allocated within that area, and if set to %TRUE instead the * actor will be given exactly that area; * - the size of the columns or rows can be controlled * for both minimum and maximum; the spacing can also be controlled * in both columns and rows. * * The [flow-layout example](https://git.gnome.org/browse/clutter/tree/examples/flow-layout.c?h=clutter-1.18) * shows how to use the #ClutterFlowLayout. * * #ClutterFlowLayout is available since Clutter 1.2 */ #include "clutter-build-config.h" #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "clutter-actor.h" #include "clutter-animatable.h" #include "clutter-child-meta.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-flow-layout.h" #include "clutter-layout-meta.h" #include "clutter-private.h" struct _ClutterFlowLayoutPrivate { ClutterContainer *container; ClutterFlowOrientation orientation; gfloat col_spacing; gfloat row_spacing; gfloat min_col_width; gfloat max_col_width; gfloat col_width; gfloat min_row_height; gfloat max_row_height; gfloat row_height; /* per-line size */ GArray *line_min; GArray *line_natural; gfloat req_width; gfloat req_height; guint line_count; guint is_homogeneous : 1; guint snap_to_grid : 1; }; enum { PROP_0, PROP_ORIENTATION, PROP_HOMOGENEOUS, PROP_COLUMN_SPACING, PROP_ROW_SPACING, PROP_MIN_COLUMN_WIDTH, PROP_MAX_COLUMN_WIDTH, PROP_MIN_ROW_HEGHT, PROP_MAX_ROW_HEIGHT, PROP_SNAP_TO_GRID, N_PROPERTIES }; static GParamSpec *flow_properties[N_PROPERTIES] = { NULL, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterFlowLayout, clutter_flow_layout, CLUTTER_TYPE_LAYOUT_MANAGER) static gint get_columns (ClutterFlowLayout *self, gfloat for_width) { ClutterFlowLayoutPrivate *priv = self->priv; gint n_columns; if (for_width < 0) return 1; if (priv->col_width == 0) return 1; n_columns = (gint) (for_width + priv->col_spacing) / (priv->col_width + priv->col_spacing); if (n_columns == 0) return 1; return n_columns; } static gint get_rows (ClutterFlowLayout *self, gfloat for_height) { ClutterFlowLayoutPrivate *priv = self->priv; gint n_rows; if (for_height < 0) return 1; if (priv->row_height == 0) return 1; n_rows = (gint) (for_height + priv->row_spacing) / (priv->row_height + priv->row_spacing); if (n_rows == 0) return 1; return n_rows; } static gint compute_lines (ClutterFlowLayout *self, gfloat avail_width, gfloat avail_height) { ClutterFlowLayoutPrivate *priv = self->priv; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) return get_columns (self, avail_width); else return get_rows (self, avail_height); } static void clutter_flow_layout_get_preferred_width (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; gint n_rows, line_item_count, line_count; gfloat total_min_width, total_natural_width; gfloat line_min_width, line_natural_width; gfloat max_min_width, max_natural_width; ClutterActor *actor, *child; ClutterActorIter iter; gfloat item_y; n_rows = get_rows (CLUTTER_FLOW_LAYOUT (manager), for_height); total_min_width = 0; total_natural_width = 0; line_min_width = 0; line_natural_width = 0; line_item_count = 0; line_count = 0; item_y = 0; actor = CLUTTER_ACTOR (container); /* clear the line width arrays */ if (priv->line_min != NULL) g_array_free (priv->line_min, TRUE); if (priv->line_natural != NULL) g_array_free (priv->line_natural, TRUE); priv->line_min = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); priv->line_natural = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); if (clutter_actor_get_n_children (actor) != 0) line_count = 1; max_min_width = max_natural_width = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min, child_natural; gfloat new_y, item_height; if (!clutter_actor_is_visible (child)) continue; if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) { clutter_actor_get_preferred_height (child, -1, &child_min, &child_natural); if ((priv->snap_to_grid && line_item_count == n_rows) || (!priv->snap_to_grid && item_y + child_natural > for_height)) { total_min_width += line_min_width; total_natural_width += line_natural_width; g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); line_min_width = line_natural_width = 0; line_item_count = 0; line_count += 1; item_y = 0; } if (priv->snap_to_grid) { new_y = ((line_item_count + 1) * (for_height + priv->row_spacing)) / n_rows; item_height = new_y - item_y - priv->row_spacing; } else { new_y = item_y + child_natural + priv->row_spacing; item_height = child_natural; } clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); line_min_width = MAX (line_min_width, child_min); line_natural_width = MAX (line_natural_width, child_natural); item_y = new_y; line_item_count += 1; max_min_width = MAX (max_min_width, line_min_width); max_natural_width = MAX (max_natural_width, line_natural_width); } else { clutter_actor_get_preferred_width (child, for_height, &child_min, &child_natural); max_min_width = MAX (max_min_width, child_min); max_natural_width = MAX (max_natural_width, child_natural); total_min_width += max_min_width; total_natural_width += max_natural_width; line_count += 1; } } priv->col_width = max_natural_width; if (priv->max_col_width > 0 && priv->col_width > priv->max_col_width) priv->col_width = MAX (priv->max_col_width, max_min_width); if (priv->col_width < priv->min_col_width) priv->col_width = priv->min_col_width; if (priv->orientation == CLUTTER_FLOW_VERTICAL && for_height > 0) { /* if we have a non-full row we need to add it */ if (line_item_count > 0) { total_min_width += line_min_width; total_natural_width += line_natural_width; g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); } priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * (priv->line_count - 1); total_min_width += total_spacing; total_natural_width += total_spacing; } } else { g_array_append_val (priv->line_min, line_min_width); g_array_append_val (priv->line_natural, line_natural_width); priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * (priv->line_count - 1); total_min_width += total_spacing; total_natural_width += total_spacing; } } CLUTTER_NOTE (LAYOUT, "Flow[w]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f", n_rows, priv->line_count, total_min_width, total_natural_width, for_height); priv->req_height = for_height; if (min_width_p) *min_width_p = max_min_width; if (nat_width_p) *nat_width_p = total_natural_width; } static void clutter_flow_layout_get_preferred_height (ClutterLayoutManager *manager, ClutterContainer *container, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; gint n_columns, line_item_count, line_count; gfloat total_min_height, total_natural_height; gfloat line_min_height, line_natural_height; gfloat max_min_height, max_natural_height; ClutterActor *actor, *child; ClutterActorIter iter; gfloat item_x; n_columns = get_columns (CLUTTER_FLOW_LAYOUT (manager), for_width); total_min_height = 0; total_natural_height = 0; line_min_height = 0; line_natural_height = 0; line_item_count = 0; line_count = 0; item_x = 0; actor = CLUTTER_ACTOR (container); /* clear the line height arrays */ if (priv->line_min != NULL) g_array_free (priv->line_min, TRUE); if (priv->line_natural != NULL) g_array_free (priv->line_natural, TRUE); priv->line_min = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); priv->line_natural = g_array_sized_new (FALSE, FALSE, sizeof (gfloat), 16); if (clutter_actor_get_n_children (actor) != 0) line_count = 1; max_min_height = max_natural_height = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_min, child_natural; gfloat new_x, item_width; if (!clutter_actor_is_visible (child)) continue; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) { clutter_actor_get_preferred_width (child, -1, &child_min, &child_natural); if ((priv->snap_to_grid && line_item_count == n_columns) || (!priv->snap_to_grid && item_x + child_natural > for_width)) { total_min_height += line_min_height; total_natural_height += line_natural_height; g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); line_min_height = line_natural_height = 0; line_item_count = 0; line_count += 1; item_x = 0; } if (priv->snap_to_grid) { new_x = ((line_item_count + 1) * (for_width + priv->col_spacing)) / n_columns; item_width = new_x - item_x - priv->col_spacing; } else { new_x = item_x + child_natural + priv->col_spacing; item_width = child_natural; } clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); line_min_height = MAX (line_min_height, child_min); line_natural_height = MAX (line_natural_height, child_natural); item_x = new_x; line_item_count += 1; max_min_height = MAX (max_min_height, line_min_height); max_natural_height = MAX (max_natural_height, line_natural_height); } else { clutter_actor_get_preferred_height (child, for_width, &child_min, &child_natural); max_min_height = MAX (max_min_height, child_min); max_natural_height = MAX (max_natural_height, child_natural); total_min_height += max_min_height; total_natural_height += max_natural_height; line_count += 1; } } priv->row_height = max_natural_height; if (priv->max_row_height > 0 && priv->row_height > priv->max_row_height) priv->row_height = MAX (priv->max_row_height, max_min_height); if (priv->row_height < priv->min_row_height) priv->row_height = priv->min_row_height; if (priv->orientation == CLUTTER_FLOW_HORIZONTAL && for_width > 0) { /* if we have a non-full row we need to add it */ if (line_item_count > 0) { total_min_height += line_min_height; total_natural_height += line_natural_height; g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); } priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->row_spacing * (priv->line_count - 1); total_min_height += total_spacing; total_natural_height += total_spacing; } } else { g_array_append_val (priv->line_min, line_min_height); g_array_append_val (priv->line_natural, line_natural_height); priv->line_count = line_count; if (priv->line_count > 0) { gfloat total_spacing; total_spacing = priv->col_spacing * priv->line_count; total_min_height += total_spacing; total_natural_height += total_spacing; } } CLUTTER_NOTE (LAYOUT, "Flow[h]: %d lines (%d per line): w [ %.2f, %.2f ] for h %.2f", n_columns, priv->line_count, total_min_height, total_natural_height, for_width); priv->req_width = for_width; if (min_height_p) *min_height_p = max_min_height; if (nat_height_p) *nat_height_p = total_natural_height; } static void clutter_flow_layout_allocate (ClutterLayoutManager *manager, ClutterContainer *container, const ClutterActorBox *allocation, ClutterAllocationFlags flags) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; ClutterActor *actor, *child; ClutterActorIter iter; gfloat x_off, y_off; gfloat avail_width, avail_height; gfloat item_x, item_y; gint line_item_count; gint items_per_line; gint line_index; actor = CLUTTER_ACTOR (container); if (clutter_actor_get_n_children (actor) == 0) return; clutter_actor_box_get_origin (allocation, &x_off, &y_off); clutter_actor_box_get_size (allocation, &avail_width, &avail_height); /* blow the cached preferred size and re-compute with the given * available size in case the FlowLayout wasn't given the exact * size it requested */ if ((priv->req_width >= 0 && avail_width != priv->req_width) || (priv->req_height >= 0 && avail_height != priv->req_height)) { clutter_flow_layout_get_preferred_width (manager, container, avail_height, NULL, NULL); clutter_flow_layout_get_preferred_height (manager, container, avail_width, NULL, NULL); } items_per_line = compute_lines (CLUTTER_FLOW_LAYOUT (manager), avail_width, avail_height); item_x = x_off; item_y = y_off; line_item_count = 0; line_index = 0; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { ClutterActorBox child_alloc; gfloat item_width, item_height; gfloat new_x, new_y; gfloat child_min, child_natural; if (!clutter_actor_is_visible (child)) continue; new_x = new_y = 0; if (!priv->snap_to_grid) clutter_actor_get_preferred_size (child, NULL, NULL, &item_width, &item_height); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) { if ((priv->snap_to_grid && line_item_count == items_per_line && line_item_count > 0) || (!priv->snap_to_grid && item_x + item_width > avail_width)) { item_y += g_array_index (priv->line_natural, gfloat, line_index); if (line_index >= 0) item_y += priv->row_spacing; line_item_count = 0; line_index += 1; item_x = x_off; } if (priv->snap_to_grid) { new_x = x_off + ((line_item_count + 1) * (avail_width + priv->col_spacing)) / items_per_line; item_width = new_x - item_x - priv->col_spacing; } else { new_x = item_x + item_width + priv->col_spacing; } item_height = g_array_index (priv->line_natural, gfloat, line_index); } else { if ((priv->snap_to_grid && line_item_count == items_per_line && line_item_count > 0) || (!priv->snap_to_grid && item_y + item_height > avail_height)) { item_x += g_array_index (priv->line_natural, gfloat, line_index); if (line_index >= 0) item_x += priv->col_spacing; line_item_count = 0; line_index += 1; item_y = y_off; } if (priv->snap_to_grid) { new_y = y_off + ((line_item_count + 1) * (avail_height + priv->row_spacing)) / items_per_line; item_height = new_y - item_y - priv->row_spacing; } else { new_y = item_y + item_height + priv->row_spacing; } item_width = g_array_index (priv->line_natural, gfloat, line_index); } if (!priv->is_homogeneous && !clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_HORIZONTAL)) { clutter_actor_get_preferred_width (child, item_height, &child_min, &child_natural); item_width = MIN (item_width, child_natural); } if (!priv->is_homogeneous && !clutter_actor_needs_expand (child, CLUTTER_ORIENTATION_VERTICAL)) { clutter_actor_get_preferred_height (child, item_width, &child_min, &child_natural); item_height = MIN (item_height, child_natural); } CLUTTER_NOTE (LAYOUT, "flow[line:%d, item:%d/%d] =" "{ %.2f, %.2f, %.2f, %.2f }", line_index, line_item_count + 1, items_per_line, item_x, item_y, item_width, item_height); child_alloc.x1 = ceil (item_x); child_alloc.y1 = ceil (item_y); child_alloc.x2 = ceil (child_alloc.x1 + item_width); child_alloc.y2 = ceil (child_alloc.y1 + item_height); clutter_actor_allocate (child, &child_alloc, flags); if (priv->orientation == CLUTTER_FLOW_HORIZONTAL) item_x = new_x; else item_y = new_y; line_item_count += 1; } } static void clutter_flow_layout_set_container (ClutterLayoutManager *manager, ClutterContainer *container) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (manager)->priv; ClutterLayoutManagerClass *parent_class; priv->container = container; if (priv->container != NULL) { ClutterRequestMode request_mode; /* we need to change the :request-mode of the container * to match the orientation */ request_mode = (priv->orientation == CLUTTER_FLOW_HORIZONTAL) ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), request_mode); } parent_class = CLUTTER_LAYOUT_MANAGER_CLASS (clutter_flow_layout_parent_class); parent_class->set_container (manager, container); } static void clutter_flow_layout_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterFlowLayout *self = CLUTTER_FLOW_LAYOUT (gobject); switch (prop_id) { case PROP_ORIENTATION: clutter_flow_layout_set_orientation (self, g_value_get_enum (value)); break; case PROP_HOMOGENEOUS: clutter_flow_layout_set_homogeneous (self, g_value_get_boolean (value)); break; case PROP_COLUMN_SPACING: clutter_flow_layout_set_column_spacing (self, g_value_get_float (value)); break; case PROP_ROW_SPACING: clutter_flow_layout_set_row_spacing (self, g_value_get_float (value)); break; case PROP_MIN_COLUMN_WIDTH: clutter_flow_layout_set_column_width (self, g_value_get_float (value), self->priv->max_col_width); break; case PROP_MAX_COLUMN_WIDTH: clutter_flow_layout_set_column_width (self, self->priv->min_col_width, g_value_get_float (value)); break; case PROP_MIN_ROW_HEGHT: clutter_flow_layout_set_row_height (self, g_value_get_float (value), self->priv->max_row_height); break; case PROP_MAX_ROW_HEIGHT: clutter_flow_layout_set_row_height (self, self->priv->min_row_height, g_value_get_float (value)); break; case PROP_SNAP_TO_GRID: clutter_flow_layout_set_snap_to_grid (self, g_value_get_boolean (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_flow_layout_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv; switch (prop_id) { case PROP_ORIENTATION: g_value_set_enum (value, priv->orientation); break; case PROP_HOMOGENEOUS: g_value_set_boolean (value, priv->is_homogeneous); break; case PROP_COLUMN_SPACING: g_value_set_float (value, priv->col_spacing); break; case PROP_ROW_SPACING: g_value_set_float (value, priv->row_spacing); break; case PROP_MIN_COLUMN_WIDTH: g_value_set_float (value, priv->min_col_width); break; case PROP_MAX_COLUMN_WIDTH: g_value_set_float (value, priv->max_col_width); break; case PROP_MIN_ROW_HEGHT: g_value_set_float (value, priv->min_row_height); break; case PROP_MAX_ROW_HEIGHT: g_value_set_float (value, priv->max_row_height); break; case PROP_SNAP_TO_GRID: g_value_set_boolean (value, priv->snap_to_grid); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_flow_layout_finalize (GObject *gobject) { ClutterFlowLayoutPrivate *priv = CLUTTER_FLOW_LAYOUT (gobject)->priv; if (priv->line_min != NULL) g_array_free (priv->line_min, TRUE); if (priv->line_natural != NULL) g_array_free (priv->line_natural, TRUE); G_OBJECT_CLASS (clutter_flow_layout_parent_class)->finalize (gobject); } static void clutter_flow_layout_class_init (ClutterFlowLayoutClass *klass) { GObjectClass *gobject_class; ClutterLayoutManagerClass *layout_class; gobject_class = G_OBJECT_CLASS (klass); layout_class = CLUTTER_LAYOUT_MANAGER_CLASS (klass); layout_class->get_preferred_width = clutter_flow_layout_get_preferred_width; layout_class->get_preferred_height = clutter_flow_layout_get_preferred_height; layout_class->allocate = clutter_flow_layout_allocate; layout_class->set_container = clutter_flow_layout_set_container; /** * ClutterFlowLayout:orientation: * * The orientation of the #ClutterFlowLayout. The children * of the layout will be layed out following the orientation. * * This property also controls the overflowing directions * * Since: 1.2 */ flow_properties[PROP_ORIENTATION] = g_param_spec_enum ("orientation", P_("Orientation"), P_("The orientation of the layout"), CLUTTER_TYPE_FLOW_ORIENTATION, CLUTTER_FLOW_HORIZONTAL, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT); /** * ClutterFlowLayout:homogeneous: * * Whether each child inside the #ClutterFlowLayout should receive * the same allocation * * Since: 1.2 */ flow_properties[PROP_HOMOGENEOUS] = g_param_spec_boolean ("homogeneous", P_("Homogeneous"), P_("Whether each item should receive the same allocation"), FALSE, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:column-spacing: * * The spacing between columns, in pixels; the value of this * property is honoured by horizontal non-overflowing layouts * and by vertical overflowing layouts * * Since: 1.2 */ flow_properties[PROP_COLUMN_SPACING] = g_param_spec_float ("column-spacing", P_("Column Spacing"), P_("The spacing between columns"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:row-spacing: * * The spacing between rows, in pixels; the value of this * property is honoured by vertical non-overflowing layouts and * by horizontal overflowing layouts * * Since: 1.2 */ flow_properties[PROP_ROW_SPACING] = g_param_spec_float ("row-spacing", P_("Row Spacing"), P_("The spacing between rows"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:min-column-width: * * Minimum width for each column in the layout, in pixels * * Since: 1.2 */ flow_properties[PROP_MIN_COLUMN_WIDTH] = g_param_spec_float ("min-column-width", P_("Minimum Column Width"), P_("Minimum width for each column"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:max-column-width: * * Maximum width for each column in the layout, in pixels. If * set to -1 the width will be the maximum child width * * Since: 1.2 */ flow_properties[PROP_MAX_COLUMN_WIDTH] = g_param_spec_float ("max-column-width", P_("Maximum Column Width"), P_("Maximum width for each column"), -1.0, G_MAXFLOAT, -1.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:min-row-height: * * Minimum height for each row in the layout, in pixels * * Since: 1.2 */ flow_properties[PROP_MIN_ROW_HEGHT] = g_param_spec_float ("min-row-height", P_("Minimum Row Height"), P_("Minimum height for each row"), 0.0, G_MAXFLOAT, 0.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:max-row-height: * * Maximum height for each row in the layout, in pixels. If * set to -1 the width will be the maximum child height * * Since: 1.2 */ flow_properties[PROP_MAX_ROW_HEIGHT] = g_param_spec_float ("max-row-height", P_("Maximum Row Height"), P_("Maximum height for each row"), -1.0, G_MAXFLOAT, -1.0, CLUTTER_PARAM_READWRITE); /** * ClutterFlowLayout:snap-to-grid: * * Whether the #ClutterFlowLayout should arrange its children * on a grid * * Since: 1.16 */ flow_properties[PROP_SNAP_TO_GRID] = g_param_spec_boolean ("snap-to-grid", P_("Snap to grid"), P_("Snap to grid"), TRUE, CLUTTER_PARAM_READWRITE); gobject_class->finalize = clutter_flow_layout_finalize; gobject_class->set_property = clutter_flow_layout_set_property; gobject_class->get_property = clutter_flow_layout_get_property; g_object_class_install_properties (gobject_class, N_PROPERTIES, flow_properties); } static void clutter_flow_layout_init (ClutterFlowLayout *self) { ClutterFlowLayoutPrivate *priv; self->priv = priv = clutter_flow_layout_get_instance_private (self); priv->orientation = CLUTTER_FLOW_HORIZONTAL; priv->col_spacing = 0; priv->row_spacing = 0; priv->min_col_width = priv->min_row_height = 0; priv->max_col_width = priv->max_row_height = -1; priv->line_min = NULL; priv->line_natural = NULL; priv->snap_to_grid = TRUE; } /** * clutter_flow_layout_new: * @orientation: the orientation of the flow layout * * Creates a new #ClutterFlowLayout with the given @orientation * * Return value: the newly created #ClutterFlowLayout * * Since: 1.2 */ ClutterLayoutManager * clutter_flow_layout_new (ClutterFlowOrientation orientation) { return g_object_new (CLUTTER_TYPE_FLOW_LAYOUT, "orientation", orientation, NULL); } /** * clutter_flow_layout_set_orientation: * @layout: a #ClutterFlowLayout * @orientation: the orientation of the layout * * Sets the orientation of the flow layout * * The orientation controls the direction used to allocate * the children: either horizontally or vertically. The * orientation also controls the direction of the overflowing * * Since: 1.2 */ void clutter_flow_layout_set_orientation (ClutterFlowLayout *layout, ClutterFlowOrientation orientation) { ClutterFlowLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->orientation != orientation) { ClutterLayoutManager *manager; priv->orientation = orientation; if (priv->container != NULL) { ClutterRequestMode request_mode; /* we need to change the :request-mode of the container * to match the orientation */ request_mode = (priv->orientation == CLUTTER_FLOW_HORIZONTAL) ? CLUTTER_REQUEST_HEIGHT_FOR_WIDTH : CLUTTER_REQUEST_WIDTH_FOR_HEIGHT; clutter_actor_set_request_mode (CLUTTER_ACTOR (priv->container), request_mode); } manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_ORIENTATION]); } } /** * clutter_flow_layout_get_orientation: * @layout: a #ClutterFlowLayout * * Retrieves the orientation of the @layout * * Return value: the orientation of the #ClutterFlowLayout * * Since: 1.2 */ ClutterFlowOrientation clutter_flow_layout_get_orientation (ClutterFlowLayout *layout) { g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), CLUTTER_FLOW_HORIZONTAL); return layout->priv->orientation; } /** * clutter_flow_layout_set_homogeneous: * @layout: a #ClutterFlowLayout * @homogeneous: whether the layout should be homogeneous or not * * Sets whether the @layout should allocate the same space for * each child * * Since: 1.2 */ void clutter_flow_layout_set_homogeneous (ClutterFlowLayout *layout, gboolean homogeneous) { ClutterFlowLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->is_homogeneous != homogeneous) { ClutterLayoutManager *manager; priv->is_homogeneous = homogeneous; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_HOMOGENEOUS]); } } /** * clutter_flow_layout_get_homogeneous: * @layout: a #ClutterFlowLayout * * Retrieves whether the @layout is homogeneous * * Return value: %TRUE if the #ClutterFlowLayout is homogeneous * * Since: 1.2 */ gboolean clutter_flow_layout_get_homogeneous (ClutterFlowLayout *layout) { g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE); return layout->priv->is_homogeneous; } /** * clutter_flow_layout_set_column_spacing: * @layout: a #ClutterFlowLayout * @spacing: the space between columns * * Sets the space between columns, in pixels * * Since: 1.2 */ void clutter_flow_layout_set_column_spacing (ClutterFlowLayout *layout, gfloat spacing) { ClutterFlowLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->col_spacing != spacing) { ClutterLayoutManager *manager; priv->col_spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_COLUMN_SPACING]); } } /** * clutter_flow_layout_get_column_spacing: * @layout: a #ClutterFlowLayout * * Retrieves the spacing between columns * * Return value: the spacing between columns of the #ClutterFlowLayout, * in pixels * * Since: 1.2 */ gfloat clutter_flow_layout_get_column_spacing (ClutterFlowLayout *layout) { g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0); return layout->priv->col_spacing; } /** * clutter_flow_layout_set_row_spacing: * @layout: a #ClutterFlowLayout * @spacing: the space between rows * * Sets the spacing between rows, in pixels * * Since: 1.2 */ void clutter_flow_layout_set_row_spacing (ClutterFlowLayout *layout, gfloat spacing) { ClutterFlowLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->row_spacing != spacing) { ClutterLayoutManager *manager; priv->row_spacing = spacing; manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_ROW_SPACING]); } } /** * clutter_flow_layout_get_row_spacing: * @layout: a #ClutterFlowLayout * * Retrieves the spacing between rows * * Return value: the spacing between rows of the #ClutterFlowLayout, * in pixels * * Since: 1.2 */ gfloat clutter_flow_layout_get_row_spacing (ClutterFlowLayout *layout) { g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), 0.0); return layout->priv->row_spacing; } /** * clutter_flow_layout_set_column_width: * @layout: a #ClutterFlowLayout * @min_width: minimum width of a column * @max_width: maximum width of a column * * Sets the minimum and maximum widths that a column can have * * Since: 1.2 */ void clutter_flow_layout_set_column_width (ClutterFlowLayout *layout, gfloat min_width, gfloat max_width) { ClutterFlowLayoutPrivate *priv; gboolean notify_min = FALSE, notify_max = FALSE; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->min_col_width != min_width) { priv->min_col_width = min_width; notify_min = TRUE; } if (priv->max_col_width != max_width) { priv->max_col_width = max_width; notify_max = TRUE; } g_object_freeze_notify (G_OBJECT (layout)); if (notify_min || notify_max) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); } if (notify_min) g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_MIN_COLUMN_WIDTH]); if (notify_max) g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_MAX_COLUMN_WIDTH]); g_object_thaw_notify (G_OBJECT (layout)); } /** * clutter_flow_layout_get_column_width: * @layout: a #ClutterFlowLayout * @min_width: (out): return location for the minimum column width, or %NULL * @max_width: (out): return location for the maximum column width, or %NULL * * Retrieves the minimum and maximum column widths * * Since: 1.2 */ void clutter_flow_layout_get_column_width (ClutterFlowLayout *layout, gfloat *min_width, gfloat *max_width) { g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); if (min_width) *min_width = layout->priv->min_col_width; if (max_width) *max_width = layout->priv->max_col_width; } /** * clutter_flow_layout_set_row_height: * @layout: a #ClutterFlowLayout * @min_height: the minimum height of a row * @max_height: the maximum height of a row * * Sets the minimum and maximum heights that a row can have * * Since: 1.2 */ void clutter_flow_layout_set_row_height (ClutterFlowLayout *layout, gfloat min_height, gfloat max_height) { ClutterFlowLayoutPrivate *priv; gboolean notify_min = FALSE, notify_max = FALSE; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->min_row_height != min_height) { priv->min_row_height = min_height; notify_min = TRUE; } if (priv->max_row_height != max_height) { priv->max_row_height = max_height; notify_max = TRUE; } g_object_freeze_notify (G_OBJECT (layout)); if (notify_min || notify_max) { ClutterLayoutManager *manager = CLUTTER_LAYOUT_MANAGER (layout); clutter_layout_manager_layout_changed (manager); } if (notify_min) g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_MIN_ROW_HEGHT]); if (notify_max) g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_MAX_ROW_HEIGHT]); g_object_thaw_notify (G_OBJECT (layout)); } /** * clutter_flow_layout_get_row_height: * @layout: a #ClutterFlowLayout * @min_height: (out): return location for the minimum row height, or %NULL * @max_height: (out): return location for the maximum row height, or %NULL * * Retrieves the minimum and maximum row heights * * Since: 1.2 */ void clutter_flow_layout_get_row_height (ClutterFlowLayout *layout, gfloat *min_height, gfloat *max_height) { g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); if (min_height) *min_height = layout->priv->min_row_height; if (max_height) *max_height = layout->priv->max_row_height; } /** * clutter_flow_layout_set_snap_to_grid: * @layout: a #ClutterFlowLayout * @snap_to_grid: %TRUE if @layout should place its children on a grid * * Whether the @layout should place its children on a grid. * * Since: 1.16 */ void clutter_flow_layout_set_snap_to_grid (ClutterFlowLayout *layout, gboolean snap_to_grid) { ClutterFlowLayoutPrivate *priv; g_return_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout)); priv = layout->priv; if (priv->snap_to_grid != snap_to_grid) { priv->snap_to_grid = snap_to_grid; clutter_layout_manager_layout_changed (CLUTTER_LAYOUT_MANAGER (layout)); g_object_notify_by_pspec (G_OBJECT (layout), flow_properties[PROP_SNAP_TO_GRID]); } } /** * clutter_flow_layout_get_snap_to_grid: * @layout: a #ClutterFlowLayout * * Retrieves the value of #ClutterFlowLayout:snap-to-grid property * * Return value: %TRUE if the @layout is placing its children on a grid * * Since: 1.16 */ gboolean clutter_flow_layout_get_snap_to_grid (ClutterFlowLayout *layout) { g_return_val_if_fail (CLUTTER_IS_FLOW_LAYOUT (layout), FALSE); return layout->priv->snap_to_grid; } muffin-6.4.1/clutter/clutter/clutter-easing.c0000664000175000017500000002721714723361714020211 0ustar fabiofabio#include "clutter-build-config.h" #include "clutter-easing.h" #include double clutter_linear (double t, double d) { return t / d; } double clutter_ease_in_quad (double t, double d) { double p = t / d; return p * p; } double clutter_ease_out_quad (double t, double d) { double p = t / d; return -1.0 * p * (p - 2); } double clutter_ease_in_out_quad (double t, double d) { double p = t / (d / 2); if (p < 1) return 0.5 * p * p; p -= 1; return -0.5 * (p * (p - 2) - 1); } double clutter_ease_in_cubic (double t, double d) { double p = t / d; return p * p * p; } double clutter_ease_out_cubic (double t, double d) { double p = t / d - 1; return p * p * p + 1; } double clutter_ease_in_out_cubic (double t, double d) { double p = t / (d / 2); if (p < 1) return 0.5 * p * p * p; p -= 2; return 0.5 * (p * p * p + 2); } double clutter_ease_in_quart (double t, double d) { double p = t / d; return p * p * p * p; } double clutter_ease_out_quart (double t, double d) { double p = t / d - 1; return -1.0 * (p * p * p * p - 1); } double clutter_ease_in_out_quart (double t, double d) { double p = t / (d / 2); if (p < 1) return 0.5 * p * p * p * p; p -= 2; return -0.5 * (p * p * p * p - 2); } double clutter_ease_in_quint (double t, double d) { double p = t / d; return p * p * p * p * p; } double clutter_ease_out_quint (double t, double d) { double p = t / d - 1; return p * p * p * p * p + 1; } double clutter_ease_in_out_quint (double t, double d) { double p = t / (d / 2); if (p < 1) return 0.5 * p * p * p * p * p; p -= 2; return 0.5 * (p * p * p * p * p + 2); } double clutter_ease_in_sine (double t, double d) { return -1.0 * cos (t / d * G_PI_2) + 1.0; } double clutter_ease_out_sine (double t, double d) { return sin (t / d * G_PI_2); } double clutter_ease_in_out_sine (double t, double d) { return -0.5 * (cos (G_PI * t / d) - 1); } double clutter_ease_in_expo (double t, double d) { return (t == 0) ? 0.0 : pow (2, 10 * (t / d - 1)); } double clutter_ease_out_expo (double t, double d) { return (t == d) ? 1.0 : -pow (2, -10 * t / d) + 1; } double clutter_ease_in_out_expo (double t, double d) { double p; if (t == 0) return 0.0; if (t == d) return 1.0; p = t / (d / 2); if (p < 1) return 0.5 * pow (2, 10 * (p - 1)); p -= 1; return 0.5 * (-pow (2, -10 * p) + 2); } double clutter_ease_in_circ (double t, double d) { double p = t / d; return -1.0 * (sqrt (1 - p * p) - 1); } double clutter_ease_out_circ (double t, double d) { double p = t / d - 1; return sqrt (1 - p * p); } double clutter_ease_in_out_circ (double t, double d) { double p = t / (d / 2); if (p < 1) return -0.5 * (sqrt (1 - p * p) - 1); p -= 2; return 0.5 * (sqrt (1 - p * p) + 1); } double clutter_ease_in_elastic (double t, double d) { double p = d * .3; double s = p / 4; double q = t / d; if (q == 1) return 1.0; q -= 1; return -(pow (2, 10 * q) * sin ((q * d - s) * (2 * G_PI) / p)); } double clutter_ease_out_elastic (double t, double d) { double p = d * .3; double s = p / 4; double q = t / d; if (q == 1) return 1.0; return pow (2, -10 * q) * sin ((q * d - s) * (2 * G_PI) / p) + 1.0; } double clutter_ease_in_out_elastic (double t, double d) { double p = d * (.3 * 1.5); double s = p / 4; double q = t / (d / 2); if (q == 2) return 1.0; if (q < 1) { q -= 1; return -.5 * (pow (2, 10 * q) * sin ((q * d - s) * (2 * G_PI) / p)); } else { q -= 1; return pow (2, -10 * q) * sin ((q * d - s) * (2 * G_PI) / p) * .5 + 1.0; } } double clutter_ease_in_back (double t, double d) { double p = t / d; return p * p * ((1.70158 + 1) * p - 1.70158); } double clutter_ease_out_back (double t, double d) { double p = t / d - 1; return p * p * ((1.70158 + 1) * p + 1.70158) + 1; } double clutter_ease_in_out_back (double t, double d) { double p = t / (d / 2); double s = 1.70158 * 1.525; if (p < 1) return 0.5 * (p * p * ((s + 1) * p - s)); p -= 2; return 0.5 * (p * p * ((s + 1) * p + s) + 2); } static inline double ease_out_bounce_internal (double t, double d) { double p = t / d; if (p < (1 / 2.75)) { return 7.5625 * p * p; } else if (p < (2 / 2.75)) { p -= (1.5 / 2.75); return 7.5625 * p * p + .75; } else if (p < (2.5 / 2.75)) { p -= (2.25 / 2.75); return 7.5625 * p * p + .9375; } else { p -= (2.625 / 2.75); return 7.5625 * p * p + .984375; } } static inline double ease_in_bounce_internal (double t, double d) { return 1.0 - ease_out_bounce_internal (d - t, d); } double clutter_ease_in_bounce (double t, double d) { return ease_in_bounce_internal (t, d); } double clutter_ease_out_bounce (double t, double d) { return ease_out_bounce_internal (t, d); } double clutter_ease_in_out_bounce (double t, double d) { if (t < d / 2) return ease_in_bounce_internal (t * 2, d) * 0.5; else return ease_out_bounce_internal (t * 2 - d, d) * 0.5 + 1.0 * 0.5; } static inline double ease_steps_end (double p, int n_steps) { return floor (p * (double) n_steps) / (double) n_steps; } double clutter_ease_steps_start (double t, double d, int n_steps) { return 1.0 - ease_steps_end (1.0 - (t / d), n_steps); } double clutter_ease_steps_end (double t, double d, int n_steps) { return ease_steps_end ((t / d), n_steps); } static inline double x_for_t (double t, double x_1, double x_2) { double omt = 1.0 - t; return 3.0 * omt * omt * t * x_1 + 3.0 * omt * t * t * x_2 + t * t * t; } static inline double y_for_t (double t, double y_1, double y_2) { double omt = 1.0 - t; return 3.0 * omt * omt * t * y_1 + 3.0 * omt * t * t * y_2 + t * t * t; } static inline double t_for_x (double x, double x_1, double x_2) { double min_t = 0, max_t = 1; int i; for (i = 0; i < 30; ++i) { double guess_t = (min_t + max_t) / 2.0; double guess_x = x_for_t (guess_t, x_1, x_2); if (x < guess_x) max_t = guess_t; else min_t = guess_t; } return (min_t + max_t) / 2.0; } double clutter_ease_cubic_bezier (double t, double d, double x_1, double y_1, double x_2, double y_2) { double p = t / d; if (p == 0.0) return 0.0; if (p == 1.0) return 1.0; return y_for_t (t_for_x (p, x_1, x_2), y_1, y_2); } /*< private > * _clutter_animation_modes: * * A mapping of animation modes and easing functions. */ static const struct { ClutterAnimationMode mode; ClutterEasingFunc func; const char *name; } _clutter_animation_modes[] = { { CLUTTER_CUSTOM_MODE, NULL, "custom" }, { CLUTTER_LINEAR, clutter_linear, "linear" }, { CLUTTER_EASE_IN_QUAD, clutter_ease_in_quad, "easeInQuad" }, { CLUTTER_EASE_OUT_QUAD, clutter_ease_out_quad, "easeOutQuad" }, { CLUTTER_EASE_IN_OUT_QUAD, clutter_ease_in_out_quad, "easeInOutQuad" }, { CLUTTER_EASE_IN_CUBIC, clutter_ease_in_cubic, "easeInCubic" }, { CLUTTER_EASE_OUT_CUBIC, clutter_ease_out_cubic, "easeOutCubic" }, { CLUTTER_EASE_IN_OUT_CUBIC, clutter_ease_in_out_cubic, "easeInOutCubic" }, { CLUTTER_EASE_IN_QUART, clutter_ease_in_quart, "easeInQuart" }, { CLUTTER_EASE_OUT_QUART, clutter_ease_out_quart, "easeOutQuart" }, { CLUTTER_EASE_IN_OUT_QUART, clutter_ease_in_out_quart, "easeInOutQuart" }, { CLUTTER_EASE_IN_QUINT, clutter_ease_in_quint, "easeInQuint" }, { CLUTTER_EASE_OUT_QUINT, clutter_ease_out_quint, "easeOutQuint" }, { CLUTTER_EASE_IN_OUT_QUINT, clutter_ease_in_out_quint, "easeInOutQuint" }, { CLUTTER_EASE_IN_SINE, clutter_ease_in_sine, "easeInSine" }, { CLUTTER_EASE_OUT_SINE, clutter_ease_out_sine, "easeOutSine" }, { CLUTTER_EASE_IN_OUT_SINE, clutter_ease_in_out_sine, "easeInOutSine" }, { CLUTTER_EASE_IN_EXPO, clutter_ease_in_expo, "easeInExpo" }, { CLUTTER_EASE_OUT_EXPO, clutter_ease_out_expo, "easeOutExpo" }, { CLUTTER_EASE_IN_OUT_EXPO, clutter_ease_in_out_expo, "easeInOutExpo" }, { CLUTTER_EASE_IN_CIRC, clutter_ease_in_circ, "easeInCirc" }, { CLUTTER_EASE_OUT_CIRC, clutter_ease_out_circ, "easeOutCirc" }, { CLUTTER_EASE_IN_OUT_CIRC, clutter_ease_in_out_circ, "easeInOutCirc" }, { CLUTTER_EASE_IN_ELASTIC, clutter_ease_in_elastic, "easeInElastic" }, { CLUTTER_EASE_OUT_ELASTIC, clutter_ease_out_elastic, "easeOutElastic" }, { CLUTTER_EASE_IN_OUT_ELASTIC, clutter_ease_in_out_elastic, "easeInOutElastic" }, { CLUTTER_EASE_IN_BACK, clutter_ease_in_back, "easeInBack" }, { CLUTTER_EASE_OUT_BACK, clutter_ease_out_back, "easeOutBack" }, { CLUTTER_EASE_IN_OUT_BACK, clutter_ease_in_out_back, "easeInOutBack" }, { CLUTTER_EASE_IN_BOUNCE, clutter_ease_in_bounce, "easeInBounce" }, { CLUTTER_EASE_OUT_BOUNCE, clutter_ease_out_bounce, "easeOutBounce" }, { CLUTTER_EASE_IN_OUT_BOUNCE, clutter_ease_in_out_bounce, "easeInOutBounce" }, /* the parametrized functions need a cast */ { CLUTTER_STEPS, (ClutterEasingFunc) clutter_ease_steps_end, "steps" }, { CLUTTER_STEP_START, (ClutterEasingFunc) clutter_ease_steps_start, "stepStart" }, { CLUTTER_STEP_END, (ClutterEasingFunc) clutter_ease_steps_end, "stepEnd" }, { CLUTTER_CUBIC_BEZIER, (ClutterEasingFunc) clutter_ease_cubic_bezier, "cubicBezier" }, { CLUTTER_EASE, (ClutterEasingFunc) clutter_ease_cubic_bezier, "ease" }, { CLUTTER_EASE_IN, (ClutterEasingFunc) clutter_ease_cubic_bezier, "easeIn" }, { CLUTTER_EASE_OUT, (ClutterEasingFunc) clutter_ease_cubic_bezier, "easeOut" }, { CLUTTER_EASE_IN_OUT, (ClutterEasingFunc) clutter_ease_cubic_bezier, "easeInOut" }, { CLUTTER_ANIMATION_LAST, NULL, "sentinel" }, }; ClutterEasingFunc clutter_get_easing_func_for_mode (ClutterAnimationMode mode) { g_assert (_clutter_animation_modes[mode].mode == mode); g_assert (_clutter_animation_modes[mode].func != NULL); return _clutter_animation_modes[mode].func; } const char * clutter_get_easing_name_for_mode (ClutterAnimationMode mode) { g_assert (_clutter_animation_modes[mode].mode == mode); g_assert (_clutter_animation_modes[mode].func != NULL); return _clutter_animation_modes[mode].name; } double clutter_easing_for_mode (ClutterAnimationMode mode, double t, double d) { g_assert (_clutter_animation_modes[mode].mode == mode); g_assert (_clutter_animation_modes[mode].func != NULL); return _clutter_animation_modes[mode].func (t, d); } muffin-6.4.1/clutter/clutter/clutter-animatable.c0000664000175000017500000001573314723361714021040 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-animatable * @short_description: Interface for animatable classes * * #ClutterAnimatable is an interface that allows a #GObject class * to control how a #ClutterAnimation will animate a property. * * Each #ClutterAnimatable should implement the * #ClutterAnimatableInterface.interpolate_property() virtual function of the * interface to compute the animation state between two values of an interval * depending on a progress factor, expressed as a floating point value. * * If a #ClutterAnimatable is animated by a #ClutterAnimation * instance, the #ClutterAnimation will call * clutter_animatable_interpolate_property() passing the name of the * currently animated property; the values interval; and the progress factor. * The #ClutterAnimatable implementation should return the computed value for * the animated * property. * * #ClutterAnimatable is available since Clutter 1.0 */ #include "clutter-build-config.h" #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "clutter-animatable.h" #include "clutter-interval.h" #include "clutter-debug.h" #include "clutter-private.h" #include "deprecated/clutter-animation.h" G_DEFINE_INTERFACE (ClutterAnimatable, clutter_animatable, G_TYPE_OBJECT); static void clutter_animatable_default_init (ClutterAnimatableInterface *iface) { } /** * clutter_animatable_find_property: * @animatable: a #ClutterAnimatable * @property_name: the name of the animatable property to find * * Finds the #GParamSpec for @property_name * * Return value: (transfer none): The #GParamSpec for the given property * or %NULL * * Since: 1.4 */ GParamSpec * clutter_animatable_find_property (ClutterAnimatable *animatable, const gchar *property_name) { ClutterAnimatableInterface *iface; g_return_val_if_fail (CLUTTER_IS_ANIMATABLE (animatable), NULL); g_return_val_if_fail (property_name != NULL, NULL); CLUTTER_NOTE (ANIMATION, "Looking for property '%s'", property_name); iface = CLUTTER_ANIMATABLE_GET_IFACE (animatable); if (iface->find_property != NULL) return iface->find_property (animatable, property_name); return g_object_class_find_property (G_OBJECT_GET_CLASS (animatable), property_name); } /** * clutter_animatable_get_initial_state: * @animatable: a #ClutterAnimatable * @property_name: the name of the animatable property to retrieve * @value: a #GValue initialized to the type of the property to retrieve * * Retrieves the current state of @property_name and sets @value with it * * Since: 1.4 */ void clutter_animatable_get_initial_state (ClutterAnimatable *animatable, const gchar *property_name, GValue *value) { ClutterAnimatableInterface *iface; g_return_if_fail (CLUTTER_IS_ANIMATABLE (animatable)); g_return_if_fail (property_name != NULL); CLUTTER_NOTE (ANIMATION, "Getting initial state of '%s'", property_name); iface = CLUTTER_ANIMATABLE_GET_IFACE (animatable); if (iface->get_initial_state != NULL) iface->get_initial_state (animatable, property_name, value); else g_object_get_property (G_OBJECT (animatable), property_name, value); } /** * clutter_animatable_set_final_state: * @animatable: a #ClutterAnimatable * @property_name: the name of the animatable property to set * @value: the value of the animatable property to set * * Sets the current state of @property_name to @value * * Since: 1.4 */ void clutter_animatable_set_final_state (ClutterAnimatable *animatable, const gchar *property_name, const GValue *value) { ClutterAnimatableInterface *iface; g_return_if_fail (CLUTTER_IS_ANIMATABLE (animatable)); g_return_if_fail (property_name != NULL); CLUTTER_NOTE (ANIMATION, "Setting state of property '%s'", property_name); iface = CLUTTER_ANIMATABLE_GET_IFACE (animatable); if (iface->set_final_state != NULL) iface->set_final_state (animatable, property_name, value); else g_object_set_property (G_OBJECT (animatable), property_name, value); } /** * clutter_animatable_interpolate_value: * @animatable: a #ClutterAnimatable * @property_name: the name of the property to interpolate * @interval: a #ClutterInterval with the animation range * @progress: the progress to use to interpolate between the * initial and final values of the @interval * @value: (out): return location for an initialized #GValue * using the same type of the @interval * * Asks a #ClutterAnimatable implementation to interpolate a * a named property between the initial and final values of * a #ClutterInterval, using @progress as the interpolation * value, and store the result inside @value. * * This function should be used for every property animation * involving #ClutterAnimatables. * * This function replaces clutter_animatable_animate_property(). * * Return value: %TRUE if the interpolation was successful, * and %FALSE otherwise * * Since: 1.8 */ gboolean clutter_animatable_interpolate_value (ClutterAnimatable *animatable, const gchar *property_name, ClutterInterval *interval, gdouble progress, GValue *value) { ClutterAnimatableInterface *iface; g_return_val_if_fail (CLUTTER_IS_ANIMATABLE (animatable), FALSE); g_return_val_if_fail (property_name != NULL, FALSE); g_return_val_if_fail (CLUTTER_IS_INTERVAL (interval), FALSE); g_return_val_if_fail (value != NULL, FALSE); CLUTTER_NOTE (ANIMATION, "Interpolating '%s' (progress: %.3f)", property_name, progress); iface = CLUTTER_ANIMATABLE_GET_IFACE (animatable); if (iface->interpolate_value != NULL) { return iface->interpolate_value (animatable, property_name, interval, progress, value); } else return clutter_interval_compute_value (interval, progress, value); } muffin-6.4.1/clutter/clutter/clutter-container-private.h0000664000175000017500000000235114723361714022372 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright 2020 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_CONTAINER_PRIVATE_H__ #define __CLUTTER_CONTAINER_PRIVATE_H__ #include G_BEGIN_DECLS void _clutter_container_emit_actor_added (ClutterContainer *container, ClutterActor *actor); void _clutter_container_emit_actor_removed (ClutterContainer *container, ClutterActor *actor); G_END_DECLS #endif /* __CLUTTER_CONTAINER_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-layout-meta.c0000664000175000017500000000760214723361714021200 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-layout-meta * @short_description: Wrapper for actors inside a layout manager * * #ClutterLayoutMeta is a wrapper object created by #ClutterLayoutManager * implementations in order to store child-specific data and properties. * * A #ClutterLayoutMeta wraps a #ClutterActor inside a #ClutterContainer * using a #ClutterLayoutManager. * * #ClutterLayoutMeta is available since Clutter 1.2 */ #include "clutter-build-config.h" #include "clutter-layout-meta.h" #include "clutter-debug.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterLayoutMeta, clutter_layout_meta, CLUTTER_TYPE_CHILD_META); enum { PROP_0, PROP_MANAGER, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static void clutter_layout_meta_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object); switch (prop_id) { case PROP_MANAGER: layout_meta->manager = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_layout_meta_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterLayoutMeta *layout_meta = CLUTTER_LAYOUT_META (object); switch (prop_id) { case PROP_MANAGER: g_value_set_object (value, layout_meta->manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void clutter_layout_meta_class_init (ClutterLayoutMetaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = clutter_layout_meta_set_property; gobject_class->get_property = clutter_layout_meta_get_property; /** * ClutterLayoutMeta:manager: * * The #ClutterLayoutManager that created this #ClutterLayoutMeta. * * Since: 1.2 */ pspec = g_param_spec_object ("manager", P_("Manager"), P_("The manager that created this data"), CLUTTER_TYPE_LAYOUT_MANAGER, G_PARAM_CONSTRUCT_ONLY | CLUTTER_PARAM_READWRITE); obj_props[PROP_MANAGER] = pspec; g_object_class_install_property (gobject_class, PROP_MANAGER, pspec); } static void clutter_layout_meta_init (ClutterLayoutMeta *self) { } /** * clutter_layout_meta_get_manager: * @data: a #ClutterLayoutMeta * * Retrieves the actor wrapped by @data * * Return value: (transfer none): a #ClutterLayoutManager * * Since: 1.2 */ ClutterLayoutManager * clutter_layout_meta_get_manager (ClutterLayoutMeta *data) { g_return_val_if_fail (CLUTTER_IS_LAYOUT_META (data), NULL); return data->manager; } muffin-6.4.1/clutter/clutter/clutter-flatten-effect.c0000664000175000017500000000313414723361714021622 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2011 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Neil Roberts */ /* This is an internal-only effect used to implement the 'offscreen-redirect' property of ClutterActor. It doesn't actually need to do anything on top of the ClutterOffscreenEffect class so it only exists because that class is abstract */ #include "clutter-build-config.h" #include "clutter-flatten-effect.h" #include "clutter-private.h" #include "clutter-actor-private.h" G_DEFINE_TYPE (ClutterFlattenEffect, _clutter_flatten_effect, CLUTTER_TYPE_OFFSCREEN_EFFECT); static void _clutter_flatten_effect_class_init (ClutterFlattenEffectClass *klass) { } static void _clutter_flatten_effect_init (ClutterFlattenEffect *self) { } ClutterEffect * _clutter_flatten_effect_new (void) { return g_object_new (CLUTTER_TYPE_FLATTEN_EFFECT, NULL); } muffin-6.4.1/clutter/clutter/clutter-binding-pool.h0000664000175000017500000001446514723361714021332 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_BINDING_POOL_H__ #define __CLUTTER_BINDING_POOL_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include G_BEGIN_DECLS #define CLUTTER_TYPE_BINDING_POOL (clutter_binding_pool_get_type ()) #define CLUTTER_BINDING_POOL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_BINDING_POOL, ClutterBindingPool)) #define CLUTTER_IS_BINDING_POOL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_BINDING_POOL)) /** * ClutterBindingPool: * * Container of key bindings. The #ClutterBindingPool struct is * private. * * Since: 1.0 */ typedef struct _ClutterBindingPool ClutterBindingPool; typedef struct _ClutterBindingPoolClass ClutterBindingPoolClass; /** * ClutterBindingActionFunc: * @gobject: a #GObject * @action_name: the name of the action * @key_val: the key symbol * @modifiers: bitmask of the modifier flags * @user_data: data passed to the function * * The prototype for the callback function registered with * clutter_binding_pool_install_action() and invoked by * clutter_binding_pool_activate(). * * Return value: the function should return %TRUE if the key * binding has been handled, and return %FALSE otherwise * * Since: 1.0 */ typedef gboolean (* ClutterBindingActionFunc) (GObject *gobject, const gchar *action_name, guint key_val, ClutterModifierType modifiers, gpointer user_data); CLUTTER_EXPORT GType clutter_binding_pool_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterBindingPool * clutter_binding_pool_new (const gchar *name); CLUTTER_EXPORT ClutterBindingPool * clutter_binding_pool_get_for_class (gpointer klass); CLUTTER_EXPORT ClutterBindingPool * clutter_binding_pool_find (const gchar *name); CLUTTER_EXPORT void clutter_binding_pool_install_action (ClutterBindingPool *pool, const gchar *action_name, guint key_val, ClutterModifierType modifiers, GCallback callback, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT void clutter_binding_pool_install_closure (ClutterBindingPool *pool, const gchar *action_name, guint key_val, ClutterModifierType modifiers, GClosure *closure); CLUTTER_EXPORT void clutter_binding_pool_override_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GCallback callback, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT void clutter_binding_pool_override_closure (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GClosure *closure); CLUTTER_EXPORT const gchar * clutter_binding_pool_find_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers); CLUTTER_EXPORT void clutter_binding_pool_remove_action (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers); CLUTTER_EXPORT gboolean clutter_binding_pool_activate (ClutterBindingPool *pool, guint key_val, ClutterModifierType modifiers, GObject *gobject); CLUTTER_EXPORT void clutter_binding_pool_block_action (ClutterBindingPool *pool, const gchar *action_name); CLUTTER_EXPORT void clutter_binding_pool_unblock_action (ClutterBindingPool *pool, const gchar *action_name); G_END_DECLS #endif /* __CLUTTER_BINDING_POOL_H__ */ muffin-6.4.1/clutter/clutter/clutter-action.h0000664000175000017500000000732214723361714020220 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_ACTION_H__ #define __CLUTTER_ACTION_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_ACTION (clutter_action_get_type ()) #define CLUTTER_ACTION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_ACTION, ClutterAction)) #define CLUTTER_IS_ACTION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_ACTION)) #define CLUTTER_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_ACTION, ClutterActionClass)) #define CLUTTER_IS_ACTION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_ACTION)) #define CLUTTER_ACTION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_ACTION, ClutterActionClass)) typedef struct _ClutterActionClass ClutterActionClass; /** * ClutterAction: * * The #ClutterAction structure contains only private data and * should be accessed using the provided API. * * Since: 1.4 */ struct _ClutterAction { /*< private >*/ ClutterActorMeta parent_instance; }; /** * ClutterActionClass: * * The ClutterActionClass structure contains only private data * * Since: 1.4 */ struct _ClutterActionClass { /*< private >*/ ClutterActorMetaClass parent_class; void (* _clutter_action1) (void); void (* _clutter_action2) (void); void (* _clutter_action3) (void); void (* _clutter_action4) (void); void (* _clutter_action5) (void); void (* _clutter_action6) (void); void (* _clutter_action7) (void); void (* _clutter_action8) (void); }; CLUTTER_EXPORT GType clutter_action_get_type (void) G_GNUC_CONST; /* ClutterActor API */ CLUTTER_EXPORT void clutter_actor_add_action (ClutterActor *self, ClutterAction *action); CLUTTER_EXPORT void clutter_actor_add_action_with_name (ClutterActor *self, const gchar *name, ClutterAction *action); CLUTTER_EXPORT void clutter_actor_remove_action (ClutterActor *self, ClutterAction *action); CLUTTER_EXPORT void clutter_actor_remove_action_by_name (ClutterActor *self, const gchar *name); CLUTTER_EXPORT ClutterAction *clutter_actor_get_action (ClutterActor *self, const gchar *name); CLUTTER_EXPORT GList * clutter_actor_get_actions (ClutterActor *self); CLUTTER_EXPORT void clutter_actor_clear_actions (ClutterActor *self); CLUTTER_EXPORT gboolean clutter_actor_has_actions (ClutterActor *self); G_END_DECLS #endif /* __CLUTTER_ACTION_H__ */ muffin-6.4.1/clutter/clutter/clutter-constraint.c0000664000175000017500000002412514723361714021122 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ /** * SECTION:clutter-constraint * @Title: ClutterConstraint * @Short_Description: Abstract class for constraints on position or size * @See_Also: #ClutterAction * * #ClutterConstraint is a base abstract class for modifiers of a #ClutterActor * position or size. * * A #ClutterConstraint sub-class should contain the logic for modifying * the position or size of the #ClutterActor to which it is applied, by * updating the actor's allocation. Each #ClutterConstraint can change the * allocation of the actor to which they are applied by overriding the * #ClutterConstraintClass.update_allocation() virtual function. * * #ClutterConstraint is available since Clutter 1.4 * * ## Using Constraints * * Constraints can be used with fixed layout managers, like * #ClutterFixedLayout, or with actors implicitly using a fixed layout * manager, like #ClutterGroup and #ClutterStage. * * Constraints provide a way to build user interfaces by using * relations between #ClutterActors, without explicit fixed * positioning and sizing, similarly to how fluid layout managers like * #ClutterBoxLayout lay out their children. * * Constraints are attached to a #ClutterActor, and are available * for inspection using clutter_actor_get_constraints(). * * Clutter provides different implementation of the #ClutterConstraint * abstract class, for instance: * * - #ClutterAlignConstraint, a constraint that can be used to align * an actor to another one on either the horizontal or the vertical * axis, using a normalized value between 0 and 1. * - #ClutterBindConstraint, a constraint binds the X, Y, width or height * of an actor to the corresponding position or size of a source actor, * with or without an offset. * - #ClutterSnapConstraint, a constraint that "snaps" together the edges * of two #ClutterActors; if an actor uses two constraints on both its * horizontal or vertical edges then it can also expand to fit the empty * space. * * The [constraints example](https://git.gnome.org/browse/clutter/tree/examples/constraints.c?h=clutter-1.18) * uses various types of #ClutterConstraints to lay out three actors on a * resizable stage. Only the central actor has an explicit size, and no * actor has an explicit position. * * - The #ClutterActor with #ClutterActor:name `layerA` is explicitly * sized to 100 pixels by 25 pixels, and it's added to the #ClutterStage * - two #ClutterAlignConstraints are used to anchor `layerA` to the * center of the stage, by using 0.5 as the alignment #ClutterAlignConstraint:factor on * both the X and Y axis * - the #ClutterActor with #ClutterActor:name `layerB` is added to the * #ClutterStage with no explicit size * - the #ClutterActor:x and #ClutterActor:width of `layerB` are bound * to the same properties of `layerA` using two #ClutterBindConstraint * objects, thus keeping `layerB` aligned to `layerA` * - the top edge of `layerB` is snapped together with the bottom edge * of `layerA`; the bottom edge of `layerB` is also snapped together with * the bottom edge of the #ClutterStage; an offset is given to the two * #ClutterSnapConstraintss to allow for some padding; since `layerB` is * snapped between two different #ClutterActors, its height is stretched * to match the gap * - the #ClutterActor with #ClutterActor:name `layerC` mirrors `layerB`, * snapping the top edge of the #ClutterStage to the top edge of `layerC` * and the top edge of `layerA` to the bottom edge of `layerC` * * You can try resizing interactively the #ClutterStage and verify * that the three #ClutterActors maintain the same position and * size relative to each other, and to the #ClutterStage. * * It is important to note that Clutter does not avoid loops or * competing constraints; if two or more #ClutterConstraints * are operating on the same positional or dimensional attributes of an * actor, or if the constraints on two different actors depend on each * other, then the behavior is undefined. * * ## Implementing a ClutterConstraint * * Creating a sub-class of #ClutterConstraint requires the * implementation of the #ClutterConstraintClass.update_allocation() * virtual function. * * The `update_allocation()` virtual function is called during the * allocation sequence of a #ClutterActor, and allows any #ClutterConstraint * attached to that actor to modify the allocation before it is passed to * the actor's #ClutterActorClass.allocate() implementation. * * The #ClutterActorBox passed to the `update_allocation()` implementation * contains the original allocation of the #ClutterActor, plus the eventual * modifications applied by the other #ClutterConstraints, in the same order * the constraints have been applied to the actor. * * It is not necessary for a #ClutterConstraint sub-class to chain * up to the parent's implementation. * * If a #ClutterConstraint is parametrized - i.e. if it contains * properties that affect the way the constraint is implemented - it should * call clutter_actor_queue_relayout() on the actor to which it is attached * to whenever any parameter is changed. The actor to which it is attached * can be recovered at any point using clutter_actor_meta_get_actor(). */ #include "clutter-build-config.h" #include #include "clutter-constraint-private.h" #include "clutter-actor.h" #include "clutter-actor-meta-private.h" #include "clutter-private.h" G_DEFINE_ABSTRACT_TYPE (ClutterConstraint, clutter_constraint, CLUTTER_TYPE_ACTOR_META); static void constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { } static void constraint_update_preferred_size (ClutterConstraint *constraint, ClutterActor *actor, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size) { } static void clutter_constraint_notify (GObject *gobject, GParamSpec *pspec) { if (strcmp (pspec->name, "enabled") == 0) { ClutterActorMeta *meta = CLUTTER_ACTOR_META (gobject); ClutterActor *actor = clutter_actor_meta_get_actor (meta); if (actor != NULL) clutter_actor_queue_relayout (actor); } if (G_OBJECT_CLASS (clutter_constraint_parent_class)->notify != NULL) G_OBJECT_CLASS (clutter_constraint_parent_class)->notify (gobject, pspec); } static void clutter_constraint_class_init (ClutterConstraintClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->notify = clutter_constraint_notify; klass->update_allocation = constraint_update_allocation; klass->update_preferred_size = constraint_update_preferred_size; } static void clutter_constraint_init (ClutterConstraint *self) { } /*< private > * clutter_constraint_update_allocation: * @constraint: a #ClutterConstraint * @actor: a #ClutterActor * @allocation: (inout): the allocation to modify * * Asks the @constraint to update the @allocation of a #ClutterActor. * * Returns: %TRUE if the allocation was updated */ gboolean clutter_constraint_update_allocation (ClutterConstraint *constraint, ClutterActor *actor, ClutterActorBox *allocation) { ClutterActorBox old_alloc; g_return_val_if_fail (CLUTTER_IS_CONSTRAINT (constraint), FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (actor), FALSE); g_return_val_if_fail (allocation != NULL, FALSE); old_alloc = *allocation; CLUTTER_CONSTRAINT_GET_CLASS (constraint)->update_allocation (constraint, actor, allocation); return !clutter_actor_box_equal (allocation, &old_alloc); } /** * clutter_constraint_update_preferred_size: * @constraint: a #ClutterConstraint * @actor: a #ClutterActor * @direction: a #ClutterOrientation * @for_size: the size in the opposite direction * @minimum_size: (inout): the minimum size to modify * @natural_size: (inout): the natural size to modify * * Asks the @constraint to update the size request of a #ClutterActor. */ void clutter_constraint_update_preferred_size (ClutterConstraint *constraint, ClutterActor *actor, ClutterOrientation direction, float for_size, float *minimum_size, float *natural_size) { g_return_if_fail (CLUTTER_IS_CONSTRAINT (constraint)); g_return_if_fail (CLUTTER_IS_ACTOR (actor)); CLUTTER_CONSTRAINT_GET_CLASS (constraint)->update_preferred_size (constraint, actor, direction, for_size, minimum_size, natural_size); } muffin-6.4.1/clutter/clutter/clutter-script-parser.c0000664000175000017500000020126714723361714021540 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009 Intel Corportation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Original author: * * Emmanuele Bassi */ #include "clutter-build-config.h" #include #include #include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include "deprecated/clutter-container.h" #include "deprecated/clutter-alpha.h" #include "clutter-actor.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-script.h" #include "clutter-script-private.h" #include "clutter-scriptable.h" #include "clutter-stage-manager.h" #include "clutter-private.h" static void clutter_script_parser_object_end (JsonParser *parser, JsonObject *object); static void clutter_script_parser_parse_end (JsonParser *parser); #define clutter_script_parser_get_type _clutter_script_parser_get_type G_DEFINE_TYPE (ClutterScriptParser, clutter_script_parser, JSON_TYPE_PARSER); static void clutter_script_parser_class_init (ClutterScriptParserClass *klass) { JsonParserClass *parser_class = JSON_PARSER_CLASS (klass); parser_class->object_end = clutter_script_parser_object_end; parser_class->parse_end = clutter_script_parser_parse_end; } static void clutter_script_parser_init (ClutterScriptParser *parser) { } GType _clutter_script_get_type_from_symbol (const gchar *symbol) { static GModule *module = NULL; GTypeGetFunc func; GType gtype = G_TYPE_INVALID; if (!module) module = g_module_open (NULL, 0); if (g_module_symbol (module, symbol, (gpointer)&func)) gtype = func (); return gtype; } GType _clutter_script_get_type_from_class (const gchar *name) { static GModule *module = NULL; GString *symbol_name = g_string_sized_new (64); GType gtype = G_TYPE_INVALID; GTypeGetFunc func; gchar *symbol; gint i; if (G_UNLIKELY (!module)) module = g_module_open (NULL, 0); for (i = 0; name[i] != '\0'; i++) { gchar c = name[i]; /* the standard naming policy for GObject-based libraries * is: * * NAME := INITIAL_WORD WORD+ * INITIAL_WORD := [A-Z][a-z0-9]* * WORD := [A-Z]{1,2}[a-z0-9]+ | [A-Z]{2,} * * for instance: * * GString -> g_string * GtkCTree -> gtk_ctree * ClutterX11TexturePixmap -> clutter_x11_texture_pixmap * * see: * * http://mail.gnome.org/archives/gtk-devel-list/2007-June/msg00022.html * * and: * * http://git.gnome.org/cgit/gtk+/plain/gtk/gtkbuilderparser.c */ if ((c == g_ascii_toupper (c) && i > 0 && name[i - 1] != g_ascii_toupper (name[i - 1])) || (i > 2 && name[i] == g_ascii_toupper (name[i]) && name[i - 1] == g_ascii_toupper (name[i - 1]) && name[i - 2] == g_ascii_toupper (name[i - 2]))) g_string_append_c (symbol_name, '_'); g_string_append_c (symbol_name, g_ascii_tolower (c)); } g_string_append (symbol_name, "_get_type"); symbol = g_string_free (symbol_name, FALSE); if (g_module_symbol (module, symbol, (gpointer)&func)) { CLUTTER_NOTE (SCRIPT, "Type function: %s", symbol); gtype = func (); } g_free (symbol); return gtype; } /* * clutter_script_enum_from_string: * @type: a #GType for an enumeration type * @string: the enumeration value as a string * @enum_value: (out): return location for the enumeration value as an integer * * Converts an enumeration value inside @string into a numeric * value and places it into @enum_value. * * The enumeration value can be an integer, the enumeration nick * or the enumeration name, as part of the #GEnumValue structure. * * Return value: %TRUE if the conversion was successfull. */ gboolean _clutter_script_enum_from_string (GType type, const gchar *string, gint *enum_value) { GEnumClass *eclass; GEnumValue *ev; gchar *endptr; gint value; gboolean retval = TRUE; g_return_val_if_fail (G_TYPE_IS_ENUM (type), 0); g_return_val_if_fail (string != NULL, 0); value = strtoul (string, &endptr, 0); if (endptr != string) /* parsed a number */ *enum_value = value; else { eclass = g_type_class_ref (type); ev = g_enum_get_value_by_name (eclass, string); if (!ev) ev = g_enum_get_value_by_nick (eclass, string); if (ev) *enum_value = ev->value; else retval = FALSE; g_type_class_unref (eclass); } return retval; } gboolean _clutter_script_flags_from_string (GType type, const gchar *string, gint *flags_value) { gchar *endptr, *prevptr; guint i, j, ret, value; gchar *flagstr; GFlagsValue *fv; const gchar *flag; g_return_val_if_fail (G_TYPE_IS_FLAGS (type), 0); g_return_val_if_fail (string != NULL, 0); ret = TRUE; value = strtoul (string, &endptr, 0); if (endptr != string) /* parsed a number */ *flags_value = value; else { GFlagsClass *fclass; fclass = g_type_class_ref (type); flagstr = g_strdup (string); for (value = i = j = 0; ; i++) { gboolean eos = (flagstr[i] == '\0') ? TRUE : FALSE; if (!eos && flagstr[i] != '|') continue; flag = &flagstr[j]; endptr = &flagstr[i]; if (!eos) { flagstr[i++] = '\0'; j = i; } /* trim spaces */ for (;;) { gunichar ch = g_utf8_get_char (flag); if (!g_unichar_isspace (ch)) break; flag = g_utf8_next_char (flag); } while (endptr > flag) { gunichar ch; prevptr = g_utf8_prev_char (endptr); ch = g_utf8_get_char (prevptr); if (!g_unichar_isspace (ch)) break; endptr = prevptr; } if (endptr > flag) { *endptr = '\0'; fv = g_flags_get_value_by_name (fclass, flag); if (!fv) fv = g_flags_get_value_by_nick (fclass, flag); if (fv) value |= fv->value; else { ret = FALSE; break; } } if (eos) { *flags_value = value; break; } } g_free (flagstr); g_type_class_unref (fclass); } return ret; } static gboolean parse_knot_from_array (JsonArray *array, ClutterKnot *knot) { if (json_array_get_length (array) != 2) return FALSE; knot->x = json_array_get_int_element (array, 0); knot->y = json_array_get_int_element (array, 1); return TRUE; } static gboolean parse_knot_from_object (JsonObject *object, ClutterKnot *knot) { if (json_object_has_member (object, "x")) knot->x = json_object_get_int_member (object, "x"); else knot->x = 0; if (json_object_has_member (object, "y")) knot->y = json_object_get_int_member (object, "y"); else knot->y = 0; return TRUE; } gboolean _clutter_script_parse_knot (ClutterScript *script, JsonNode *node, ClutterKnot *knot) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (knot != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_ARRAY: return parse_knot_from_array (json_node_get_array (node), knot); case JSON_NODE_OBJECT: return parse_knot_from_object (json_node_get_object (node), knot); default: break; } return FALSE; } static gboolean parse_rect_from_array (JsonArray *array, graphene_rect_t *rect) { if (json_array_get_length (array) != 4) return FALSE; graphene_rect_init (rect, json_array_get_int_element (array, 0), json_array_get_int_element (array, 1), json_array_get_int_element (array, 2), json_array_get_int_element (array, 3)); return TRUE; } static gboolean parse_rect_from_object (JsonObject *object, graphene_rect_t *rect) { if (json_object_has_member (object, "x")) rect->origin.x = json_object_get_int_member (object, "x"); else rect->origin.x = 0; if (json_object_has_member (object, "y")) rect->origin.y = json_object_get_int_member (object, "y"); else rect->origin.y = 0; if (json_object_has_member (object, "width")) rect->size.width = json_object_get_int_member (object, "width"); else rect->size.width = 0; if (json_object_has_member (object, "height")) rect->size.height = json_object_get_int_member (object, "height"); else rect->size.height = 0; return TRUE; } gboolean _clutter_script_parse_rect (ClutterScript *script, JsonNode *node, graphene_rect_t *rect) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (rect != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_ARRAY: return parse_rect_from_array (json_node_get_array (node), rect); case JSON_NODE_OBJECT: return parse_rect_from_object (json_node_get_object (node), rect); default: break; } return FALSE; } static gboolean parse_color_from_array (JsonArray *array, ClutterColor *color) { if (json_array_get_length (array) != 3 || json_array_get_length (array) != 4) return FALSE; color->red = CLAMP (json_array_get_int_element (array, 0), 0, 255); color->green = CLAMP (json_array_get_int_element (array, 1), 0, 255); color->blue = CLAMP (json_array_get_int_element (array, 2), 0, 255); if (json_array_get_length (array) == 4) color->alpha = CLAMP (json_array_get_int_element (array, 3), 0, 255); else color->alpha = 255; return TRUE; } static gboolean parse_color_from_object (JsonObject *object, ClutterColor *color) { if (json_object_has_member (object, "red")) color->red = CLAMP (json_object_get_int_member (object, "red"), 0, 255); else color->red = 0; if (json_object_has_member (object, "green")) color->green = CLAMP (json_object_get_int_member (object, "green"), 0, 255); else color->green = 0; if (json_object_has_member (object, "blue")) color->blue = CLAMP (json_object_get_int_member (object, "blue"), 0, 255); else color->blue = 0; if (json_object_has_member (object, "alpha")) color->alpha = CLAMP (json_object_get_int_member (object, "alpha"), 0, 255); else color->alpha = 255; return TRUE; } gboolean _clutter_script_parse_color (ClutterScript *script, JsonNode *node, ClutterColor *color) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (color != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_ARRAY: return parse_color_from_array (json_node_get_array (node), color); case JSON_NODE_OBJECT: return parse_color_from_object (json_node_get_object (node), color); case JSON_NODE_VALUE: return clutter_color_from_string (color, json_node_get_string (node)); default: break; } return FALSE; } static gboolean parse_point_from_array (JsonArray *array, graphene_point_t *point) { if (json_array_get_length (array) != 2) return FALSE; point->x = json_array_get_double_element (array, 0); point->y = json_array_get_double_element (array, 1); return TRUE; } static gboolean parse_point_from_object (JsonObject *object, graphene_point_t *point) { if (json_object_has_member (object, "x")) point->x = json_object_get_double_member (object, "x"); else point->x = 0.f; if (json_object_has_member (object, "y")) point->y = json_object_get_double_member (object, "y"); else point->y = 0.f; return TRUE; } gboolean _clutter_script_parse_point (ClutterScript *script, JsonNode *node, graphene_point_t *point) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (point != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_ARRAY: return parse_point_from_array (json_node_get_array (node), point); case JSON_NODE_OBJECT: return parse_point_from_object (json_node_get_object (node), point); default: break; } return FALSE; } static gboolean parse_size_from_array (JsonArray *array, graphene_size_t *size) { if (json_array_get_length (array) != 2) return FALSE; size->width = json_array_get_double_element (array, 0); size->height = json_array_get_double_element (array, 1); return TRUE; } static gboolean parse_size_from_object (JsonObject *object, graphene_size_t *size) { if (json_object_has_member (object, "width")) size->width = json_object_get_double_member (object, "width"); else size->width = 0.f; if (json_object_has_member (object, "height")) size->height = json_object_get_double_member (object, "height"); else size->height = 0.f; return TRUE; } gboolean _clutter_script_parse_size (ClutterScript *script, JsonNode *node, graphene_size_t *size) { g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (node != NULL, FALSE); g_return_val_if_fail (size != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_ARRAY: return parse_size_from_array (json_node_get_array (node), size); case JSON_NODE_OBJECT: return parse_size_from_object (json_node_get_object (node), size); default: break; } return FALSE; } const gchar * _clutter_script_get_id_from_node (JsonNode *node) { JsonObject *object; switch (JSON_NODE_TYPE (node)) { case JSON_NODE_OBJECT: object = json_node_get_object (node); if (json_object_has_member (object, "id")) return json_object_get_string_member (object, "id"); break; case JSON_NODE_VALUE: return json_node_get_string (node); default: break; } return NULL; } static GList * parse_children (ObjectInfo *oinfo, JsonNode *node) { JsonArray *array; GList *retval; guint array_len, i; if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) return NULL; retval = oinfo->children; array = json_node_get_array (node); array_len = json_array_get_length (array); for (i = 0; i < array_len; i++) { JsonNode *child = json_array_get_element (array, i); const gchar *id_; id_ = _clutter_script_get_id_from_node (child); if (id_ != NULL) retval = g_list_prepend (retval, g_strdup (id_)); } return g_list_reverse (retval); } static GList * parse_signals (ClutterScript *script, ObjectInfo *oinfo, JsonNode *node) { JsonArray *array; GList *retval; guint array_len, i; if (JSON_NODE_TYPE (node) != JSON_NODE_ARRAY) { _clutter_script_warn_invalid_value (script, "signals", "Array", node); return NULL; } retval = oinfo->signals; array = json_node_get_array (node); array_len = json_array_get_length (array); for (i = 0; i < array_len; i++) { JsonNode *val = json_array_get_element (array, i); SignalInfo *sinfo = NULL; JsonObject *object; const gchar *name; if (JSON_NODE_TYPE (val) != JSON_NODE_OBJECT) { _clutter_script_warn_invalid_value (script, "signals array", "Object", node); continue; } object = json_node_get_object (val); /* mandatory: "name" */ if (!json_object_has_member (object, "name")) { _clutter_script_warn_missing_attribute (script, NULL, "name"); continue; } else { name = json_object_get_string_member (object, "name"); if (!name) { _clutter_script_warn_invalid_value (script, "name", "string", val); continue; } } /* mandatory: "target-state" or "handler" */ if (json_object_has_member (object, "target-state")) { const gchar *state = NULL; const gchar *target = NULL; gboolean warp_to = FALSE; target = json_object_get_string_member (object, "target-state"); if (target == NULL) { _clutter_script_warn_invalid_value (script, "target-state", "string", val); continue; } if (json_object_has_member (object, "states")) state = json_object_get_string_member (object, "states"); if (json_object_has_member (object, "warp")) warp_to = json_object_get_boolean_member (object, "warp"); CLUTTER_NOTE (SCRIPT, "Added signal '%s' (states:%s, target-state:%s, warp:%s)", name, state != NULL ? state : "", target, warp_to ? "true" : "false"); sinfo = g_slice_new0 (SignalInfo); sinfo->is_handler = FALSE; sinfo->name = g_strdup (name); sinfo->state = g_strdup (state); sinfo->target = g_strdup (target); sinfo->warp_to = warp_to; } else if (json_object_has_member (object, "handler")) { const gchar *handler; const gchar *connect; GConnectFlags flags = 0; handler = json_object_get_string_member (object, "handler"); if (handler == NULL) { _clutter_script_warn_invalid_value (script, "handler", "string", val); continue; } /* optional: "object" */ if (json_object_has_member (object, "object")) connect = json_object_get_string_member (object, "object"); else connect = NULL; /* optional: "after" */ if (json_object_has_member (object, "after")) { if (json_object_get_boolean_member (object, "after")) flags |= G_CONNECT_AFTER; } /* optional: "swapped" */ if (json_object_has_member (object, "swapped")) { if (json_object_get_boolean_member (object, "swapped")) flags |= G_CONNECT_SWAPPED; } CLUTTER_NOTE (SCRIPT, "Added signal '%s' (handler:%s, object:%s, flags:%d)", name, handler, connect, flags); sinfo = g_slice_new0 (SignalInfo); sinfo->is_handler = TRUE; sinfo->name = g_strdup (name); sinfo->handler = g_strdup (handler); sinfo->object = g_strdup (connect); sinfo->flags = flags; } else _clutter_script_warn_missing_attribute (script, NULL, "handler or state"); if (sinfo != NULL) retval = g_list_prepend (retval, sinfo); } return retval; } static ClutterTimeline * construct_timeline (ClutterScript *script, JsonObject *object) { ClutterTimeline *retval = NULL; ObjectInfo *oinfo; GList *members, *l; /* we fake an ObjectInfo so we can reuse clutter_script_construct_object() * here; we do not save it inside the hash table, because if this had * been a named object then we wouldn't have ended up here in the first * place */ oinfo = g_slice_new0 (ObjectInfo); oinfo->gtype = CLUTTER_TYPE_TIMELINE; oinfo->id = g_strdup ("dummy"); members = json_object_get_members (object); for (l = members; l != NULL; l = l->next) { const gchar *name = l->data; JsonNode *node = json_object_get_member (object, name); PropertyInfo *pinfo = g_slice_new0 (PropertyInfo); pinfo->name = g_strdelimit (g_strdup (name), G_STR_DELIMITERS, '-'); pinfo->node = json_node_copy (node); oinfo->properties = g_list_prepend (oinfo->properties, pinfo); } g_list_free (members); _clutter_script_construct_object (script, oinfo); _clutter_script_apply_properties (script, oinfo); retval = CLUTTER_TIMELINE (oinfo->object); /* we transfer ownership to the alpha function, so we ref before * destroying the ObjectInfo to avoid the timeline going away */ g_object_ref (retval); object_info_free (oinfo); return retval; } /* define the names of the animation modes to match the ones * that developers might be more accustomed to */ static const struct { const gchar *name; ClutterAnimationMode mode; } animation_modes[] = { { "linear", CLUTTER_LINEAR }, { "easeInQuad", CLUTTER_EASE_IN_QUAD }, { "easeOutQuad", CLUTTER_EASE_OUT_QUAD }, { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD }, { "easeInCubic", CLUTTER_EASE_IN_CUBIC }, { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC }, { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC }, { "easeInQuart", CLUTTER_EASE_IN_QUART }, { "easeOutQuart", CLUTTER_EASE_OUT_QUART }, { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART }, { "easeInQuint", CLUTTER_EASE_IN_QUINT }, { "easeOutQuint", CLUTTER_EASE_OUT_QUINT }, { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT }, { "easeInSine", CLUTTER_EASE_IN_SINE }, { "easeOutSine", CLUTTER_EASE_OUT_SINE }, { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE }, { "easeInExpo", CLUTTER_EASE_IN_EXPO }, { "easeOutExpo", CLUTTER_EASE_OUT_EXPO }, { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO }, { "easeInCirc", CLUTTER_EASE_IN_CIRC }, { "easeOutCirc", CLUTTER_EASE_OUT_CIRC }, { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC }, { "easeInElastic", CLUTTER_EASE_IN_ELASTIC }, { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC }, { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC }, { "easeInBack", CLUTTER_EASE_IN_BACK }, { "easeOutBack", CLUTTER_EASE_OUT_BACK }, { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK }, { "easeInBounce", CLUTTER_EASE_IN_BOUNCE }, { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE }, { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE }, }; static const gint n_animation_modes = G_N_ELEMENTS (animation_modes); gulong _clutter_script_resolve_animation_mode (JsonNode *node) { gint i, res = CLUTTER_CUSTOM_MODE; if (JSON_NODE_TYPE (node) != JSON_NODE_VALUE) return CLUTTER_CUSTOM_MODE; if (json_node_get_value_type (node) == G_TYPE_INT64) return json_node_get_int (node); if (json_node_get_value_type (node) == G_TYPE_STRING) { const gchar *name = json_node_get_string (node); /* XXX - we might be able to optimize by changing the ordering * of the animation_modes array, e.g. * - special casing linear * - tokenizing ('ease', 'In', 'Sine') and matching on token * - binary searching? */ for (i = 0; i < n_animation_modes; i++) { if (strcmp (animation_modes[i].name, name) == 0) return animation_modes[i].mode; } if (_clutter_script_enum_from_string (CLUTTER_TYPE_ANIMATION_MODE, name, &res)) return res; g_warning ("Unable to find the animation mode '%s'", name); } return CLUTTER_CUSTOM_MODE; } static ClutterAlphaFunc resolve_alpha_func (const gchar *name) { static GModule *module = NULL; ClutterAlphaFunc func; CLUTTER_NOTE (SCRIPT, "Looking up '%s' alpha function", name); if (G_UNLIKELY (!module)) module = g_module_open (NULL, 0); if (g_module_symbol (module, name, (gpointer) &func)) { CLUTTER_NOTE (SCRIPT, "Found '%s' alpha function in the symbols table", name); return func; } return NULL; } GObject * _clutter_script_parse_alpha (ClutterScript *script, JsonNode *node) { GObject *retval = NULL; JsonObject *object; ClutterTimeline *timeline = NULL; ClutterAlphaFunc alpha_func = NULL; ClutterAnimationMode mode = CLUTTER_CUSTOM_MODE; JsonNode *val; gboolean unref_timeline = FALSE; if (JSON_NODE_TYPE (node) != JSON_NODE_OBJECT) return NULL; object = json_node_get_object (node); val = json_object_get_member (object, "timeline"); if (val) { if (JSON_NODE_TYPE (val) == JSON_NODE_VALUE && json_node_get_string (val) != NULL) { const gchar *id_ = json_node_get_string (val); timeline = CLUTTER_TIMELINE (clutter_script_get_object (script, id_)); } else if (JSON_NODE_TYPE (val) == JSON_NODE_OBJECT) { timeline = construct_timeline (script, json_node_get_object (val)); unref_timeline = TRUE; } } val = json_object_get_member (object, "mode"); if (val != NULL) mode = _clutter_script_resolve_animation_mode (val); if (mode == CLUTTER_CUSTOM_MODE) { val = json_object_get_member (object, "function"); if (val && json_node_get_string (val) != NULL) { alpha_func = resolve_alpha_func (json_node_get_string (val)); if (!alpha_func) { g_warning ("Unable to find the function '%s' in the " "Clutter alpha functions or the symbols table", json_node_get_string (val)); } } } CLUTTER_NOTE (SCRIPT, "Parsed alpha: %s timeline (%p) (mode:%d, func:%p)", unref_timeline ? "implicit" : "explicit", timeline ? timeline : 0x0, mode != CLUTTER_CUSTOM_MODE ? mode : 0, alpha_func ? alpha_func : 0x0); retval = g_object_new (CLUTTER_TYPE_ALPHA, NULL); if (mode != CLUTTER_CUSTOM_MODE) clutter_alpha_set_mode (CLUTTER_ALPHA (retval), mode); if (alpha_func != NULL) clutter_alpha_set_func (CLUTTER_ALPHA (retval), alpha_func, NULL, NULL); clutter_alpha_set_timeline (CLUTTER_ALPHA (retval), timeline); /* if we created an implicit timeline, the Alpha has full ownership * of it now, since it won't be accessible from ClutterScript */ if (unref_timeline) g_object_unref (timeline); return retval; } static void clutter_script_parser_object_end (JsonParser *json_parser, JsonObject *object) { ClutterScriptParser *parser = CLUTTER_SCRIPT_PARSER (json_parser); ClutterScript *script = parser->script; ObjectInfo *oinfo; JsonNode *val; const gchar *id_; GList *members, *l; /* if the object definition does not have an 'id' field we'll * fake one for it... */ if (!json_object_has_member (object, "id")) { gchar *fake; /* ... unless it doesn't even have a type - in which case * it is an internal object definition and we're not * supposed to touch it */ if (!json_object_has_member (object, "type")) return; fake = _clutter_script_generate_fake_id (script); json_object_set_string_member (object, "id", fake); CLUTTER_NOTE (SCRIPT, "Adding fake id '%s' to object of type '%s'", json_object_get_string_member (object, "id"), json_object_get_string_member (object, "type")); g_free (fake); } if (!json_object_has_member (object, "type")) { val = json_object_get_member (object, "id"); _clutter_script_warn_missing_attribute (script, json_node_get_string (val), "type"); return; } id_ = json_object_get_string_member (object, "id"); CLUTTER_NOTE (SCRIPT, "Getting object info for object '%s'", id_); oinfo = _clutter_script_get_object_info (script, id_); if (oinfo == NULL) { const gchar *class_name; oinfo = g_slice_new0 (ObjectInfo); oinfo->merge_id = _clutter_script_get_last_merge_id (script); oinfo->id = g_strdup (id_); oinfo->has_unresolved = TRUE; class_name = json_object_get_string_member (object, "type"); oinfo->class_name = g_strdup (class_name); if (json_object_has_member (object, "type_func")) { const gchar *type_func; type_func = json_object_get_string_member (object, "type_func"); oinfo->type_func = g_strdup (type_func); /* remove the type_func member; we don't want it to * pollute the object members */ json_object_remove_member (object, "type_func"); } } if (json_object_has_member (object, "children")) { val = json_object_get_member (object, "children"); oinfo->children = parse_children (oinfo, val); json_object_remove_member (object, "children"); oinfo->has_unresolved = TRUE; } if (json_object_has_member (object, "signals")) { val = json_object_get_member (object, "signals"); oinfo->signals = parse_signals (script, oinfo, val); json_object_remove_member (object, "signals"); oinfo->has_unresolved = TRUE; } if (strcmp (oinfo->class_name, "ClutterStage") == 0 && json_object_has_member (object, "is-default")) { oinfo->is_actor = TRUE; oinfo->is_stage = TRUE; oinfo->is_stage_default = json_object_get_boolean_member (object, "is-default"); json_object_remove_member (object, "is-default"); } else oinfo->is_stage_default = FALSE; members = json_object_get_members (object); for (l = members; l; l = l->next) { const gchar *name = l->data; PropertyInfo *pinfo; JsonNode *node; CLUTTER_NOTE (SCRIPT, "Object '%s' member '%s'", oinfo->id, name); /* we have already parsed these */ if (strcmp (name, "id") == 0 || strcmp (name, "type") == 0) continue; node = json_object_get_member (object, name); /* this should not really happen; we're getting a list of * member names, and if one does not map a real member * value then it's likely that something has gone wrong */ if (G_UNLIKELY (node == NULL)) { CLUTTER_NOTE (SCRIPT, "Empty node for member '%s' of object '%s' (type: %s)", name, oinfo->id, oinfo->class_name); continue; } pinfo = g_slice_new (PropertyInfo); pinfo->name = g_strdup (name); pinfo->node = json_node_copy (node); pinfo->pspec = NULL; pinfo->is_child = g_str_has_prefix (name, "child::") ? TRUE : FALSE; pinfo->is_layout = g_str_has_prefix (name, "layout::") ? TRUE : FALSE; oinfo->properties = g_list_prepend (oinfo->properties, pinfo); oinfo->has_unresolved = TRUE; } g_list_free (members); CLUTTER_NOTE (SCRIPT, "Added object '%s' (type:%s, id:%d, props:%d, signals:%d)", oinfo->id, oinfo->class_name, oinfo->merge_id, g_list_length (oinfo->properties), g_list_length (oinfo->signals)); _clutter_script_add_object_info (script, oinfo); _clutter_script_construct_object (script, oinfo); } static void clutter_script_parser_parse_end (JsonParser *parser) { clutter_script_ensure_objects (CLUTTER_SCRIPT_PARSER (parser)->script); } gboolean _clutter_script_parse_translatable_string (ClutterScript *script, JsonNode *node, char **str) { JsonObject *obj; const char *string, *domain, *context; const char *res; gboolean translatable; if (!JSON_NODE_HOLDS_OBJECT (node)) return FALSE; obj = json_node_get_object (node); if (!(json_object_has_member (obj, "translatable") && json_object_has_member (obj, "string"))) return FALSE; translatable = json_object_get_boolean_member (obj, "translatable"); string = json_object_get_string_member (obj, "string"); if (string == NULL || *string == '\0') return FALSE; if (json_object_has_member (obj, "context")) context = json_object_get_string_member (obj, "context"); else context = NULL; if (json_object_has_member (obj, "domain")) domain = json_object_get_string_member (obj, "domain"); else domain = NULL; if (domain == NULL || *domain == '\0') domain = clutter_script_get_translation_domain (script); if (translatable) { if (context != NULL && *context != '\0') res = g_dpgettext2 (domain, context, string); else res = g_dgettext (domain, string); } else res = string; if (str != NULL) *str = g_strdup (res); return TRUE; } gboolean _clutter_script_parse_node (ClutterScript *script, GValue *value, const gchar *name, JsonNode *node, GParamSpec *pspec) { GValue node_value = G_VALUE_INIT; gboolean retval = FALSE; g_return_val_if_fail (CLUTTER_IS_SCRIPT (script), FALSE); g_return_val_if_fail (name != NULL, FALSE); g_return_val_if_fail (node != NULL, FALSE); switch (JSON_NODE_TYPE (node)) { case JSON_NODE_OBJECT: /* if we don't have a GParamSpec we can't infer the type * of the property; this usually means that this property * is a custom member that will be parsed by the Scriptable * interface implementantion */ if (pspec == NULL && !G_IS_VALUE (value)) return FALSE; else { GType p_type; ObjectInfo *oinfo; const gchar *id_; if (G_IS_VALUE (value)) p_type = G_VALUE_TYPE (value); else { p_type = G_PARAM_SPEC_VALUE_TYPE (pspec); g_value_init (value, p_type); } if (g_type_is_a (p_type, G_TYPE_OBJECT)) { /* default GObject handling: we get the id and * retrieve the ObjectInfo for it; since the object * definitions are parsed leaf-first we are guaranteed * to have a defined object at this point */ id_ = _clutter_script_get_id_from_node (node); if (id_ == NULL || *id_ == '\0') return FALSE; oinfo = _clutter_script_get_object_info (script, id_); if (oinfo == NULL || oinfo->gtype == G_TYPE_INVALID ) return FALSE; if (g_type_is_a (oinfo->gtype, p_type)) { /* force construction, even though it should * not be necessary; we don't need the properties * to be applied as well: they will when the * ScriptParser finishes */ _clutter_script_construct_object (script, oinfo); g_value_set_object (value, oinfo->object); return TRUE; } } else if (p_type == CLUTTER_TYPE_KNOT) { ClutterKnot knot = { 0, }; /* knot := { "x" : (int), "y" : (int) } */ if (_clutter_script_parse_knot (script, node, &knot)) { g_value_set_boxed (value, &knot); return TRUE; } } else if (p_type == GRAPHENE_TYPE_RECT) { graphene_rect_t rect = GRAPHENE_RECT_INIT_ZERO; /* rect := { * "x" : (int), * "y" : (int), * "width" : (int), * "height" : (int) * } */ if (_clutter_script_parse_rect (script, node, &rect)) { g_value_set_boxed (value, &rect); return TRUE; } } else if (p_type == CLUTTER_TYPE_COLOR) { ClutterColor color = { 0, }; /* color := { * "red" : (int), * "green" : (int), * "blue" : (int), * "alpha" : (int) * } */ if (_clutter_script_parse_color (script, node, &color)) { g_value_set_boxed (value, &color); return TRUE; } } else if (p_type == GRAPHENE_TYPE_POINT) { graphene_point_t point = GRAPHENE_POINT_INIT_ZERO; if (_clutter_script_parse_point (script, node, &point)) { g_value_set_boxed (value, &point); return TRUE; } } else if (p_type == GRAPHENE_TYPE_SIZE) { graphene_size_t size = GRAPHENE_SIZE_INIT_ZERO; if (_clutter_script_parse_size (script, node, &size)) { g_value_set_boxed (value, &size); return TRUE; } } else if (p_type == G_TYPE_STRING) { char *str = NULL; if (_clutter_script_parse_translatable_string (script, node, &str)) { g_value_take_string (value, str); return TRUE; } } } return FALSE; case JSON_NODE_ARRAY: if (!pspec && !G_IS_VALUE (value)) return FALSE; else { if (!G_IS_VALUE (value)) g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); if (G_VALUE_HOLDS (value, CLUTTER_TYPE_KNOT)) { ClutterKnot knot = { 0, }; /* knot := [ (int), (int) ] */ if (_clutter_script_parse_knot (script, node, &knot)) { g_value_set_boxed (value, &knot); return TRUE; } } else if (G_VALUE_HOLDS (value, GRAPHENE_TYPE_RECT)) { graphene_rect_t rect = GRAPHENE_RECT_INIT_ZERO; /* rect := [ (int), (int), (int), (int) ] */ if (_clutter_script_parse_rect (script, node, &rect)) { g_value_set_boxed (value, &rect); return TRUE; } } else if (CLUTTER_VALUE_HOLDS_COLOR (value)) { ClutterColor color = { 0, }; /* color := [ (int), (int), (int), (int) ] */ if (_clutter_script_parse_color (script, node, &color)) { g_value_set_boxed (value, &color); return TRUE; } } else if (G_VALUE_HOLDS (value, GRAPHENE_TYPE_POINT)) { graphene_point_t point = GRAPHENE_POINT_INIT_ZERO; if (_clutter_script_parse_point (script, node, &point)) { g_value_set_boxed (value, &point); return TRUE; } } else if (G_VALUE_HOLDS (value, GRAPHENE_TYPE_SIZE)) { graphene_size_t size = GRAPHENE_SIZE_INIT_ZERO; if (_clutter_script_parse_size (script, node, &size)) { g_value_set_boxed (value, &size); return TRUE; } } else if (G_VALUE_HOLDS (value, G_TYPE_STRV)) { JsonArray *array = json_node_get_array (node); guint i, array_len = json_array_get_length (array); GPtrArray *str_array = g_ptr_array_sized_new (array_len); /* strv := [ (str), (str), ... ] */ for (i = 0; i < array_len; i++) { JsonNode *val = json_array_get_element (array, i); if (JSON_NODE_TYPE (val) != JSON_NODE_VALUE && json_node_get_string (val) == NULL) continue; g_ptr_array_add (str_array, (gpointer) json_node_get_string (val)); } g_value_set_boxed (value, str_array->pdata); g_ptr_array_free (str_array, TRUE); return TRUE; } } return FALSE; case JSON_NODE_NULL: return FALSE; case JSON_NODE_VALUE: json_node_get_value (node, &node_value); if (!pspec && !G_IS_VALUE (value)) g_value_init (value, G_VALUE_TYPE (&node_value)); else if (pspec) g_value_init (value, G_PARAM_SPEC_VALUE_TYPE (pspec)); switch (G_TYPE_FUNDAMENTAL (G_VALUE_TYPE (value))) { /* fundamental JSON types */ case G_TYPE_INT64: case G_TYPE_DOUBLE: case G_TYPE_STRING: case G_TYPE_BOOLEAN: g_value_copy (&node_value, value); retval = TRUE; break; case G_TYPE_INT: g_value_set_int (value, g_value_get_int64 (&node_value)); retval = TRUE; break; case G_TYPE_UINT: g_value_set_uint (value, (guint) g_value_get_int64 (&node_value)); retval = TRUE; break; case G_TYPE_ULONG: g_value_set_ulong (value, (gulong) g_value_get_int64 (&node_value)); retval = TRUE; break; case G_TYPE_UCHAR: g_value_set_uchar (value, (guchar) g_value_get_int64 (&node_value)); retval = TRUE; break; case G_TYPE_FLOAT: if (G_VALUE_HOLDS (&node_value, G_TYPE_DOUBLE)) { g_value_set_float (value, g_value_get_double (&node_value)); retval = TRUE; } else if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) { g_value_set_float (value, g_value_get_int64 (&node_value)); retval = TRUE; } break; case G_TYPE_ENUM: if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) { g_value_set_enum (value, g_value_get_int64 (&node_value)); retval = TRUE; } else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) { gint enum_value; retval = _clutter_script_enum_from_string (G_VALUE_TYPE (value), g_value_get_string (&node_value), &enum_value); if (retval) g_value_set_enum (value, enum_value); } break; case G_TYPE_FLAGS: if (G_VALUE_HOLDS (&node_value, G_TYPE_INT64)) { g_value_set_flags (value, g_value_get_int64 (&node_value)); retval = TRUE; } else if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) { gint flags_value; retval = _clutter_script_flags_from_string (G_VALUE_TYPE (value), g_value_get_string (&node_value), &flags_value); if (retval) g_value_set_flags (value, flags_value); } break; case G_TYPE_BOXED: if (G_VALUE_HOLDS (value, CLUTTER_TYPE_COLOR)) { ClutterColor color = { 0, }; retval = _clutter_script_parse_color (script, node, &color); if (retval) clutter_value_set_color (value, &color); } break; case G_TYPE_OBJECT: if (G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) { const gchar *str = g_value_get_string (&node_value); GObject *object = clutter_script_get_object (script, str); if (object) { CLUTTER_NOTE (SCRIPT, "Assigning '%s' (%s) to property '%s'", str, G_OBJECT_TYPE_NAME (object), name); g_value_set_object (value, object); retval = TRUE; } } break; default: retval = FALSE; break; } if (G_VALUE_TYPE (value) == G_TYPE_GTYPE && G_VALUE_HOLDS (&node_value, G_TYPE_STRING)) { const gchar *str = g_value_get_string (&node_value); GType type = clutter_script_get_type_from_name (script, str); g_value_set_gtype (value, type); retval = TRUE; } g_value_unset (&node_value); break; } return retval; } static GList * clutter_script_translate_parameters (ClutterScript *script, GObject *object, const gchar *name, GList *properties, GPtrArray **param_names, GArray **param_values) { ClutterScriptable *scriptable = NULL; ClutterScriptableIface *iface = NULL; GList *l, *unparsed; gboolean parse_custom = FALSE; *param_names = g_ptr_array_new_with_free_func (g_free); *param_values = g_array_new (FALSE, FALSE, sizeof (GValue)); g_array_set_clear_func (*param_values, (GDestroyNotify) g_value_unset); if (CLUTTER_IS_SCRIPTABLE (object)) { scriptable = CLUTTER_SCRIPTABLE (object); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->parse_custom_node) parse_custom = TRUE; } unparsed = NULL; for (l = properties; l != NULL; l = l->next) { PropertyInfo *pinfo = l->data; GValue value = G_VALUE_INIT; gboolean res = FALSE; if (pinfo->is_child || pinfo->is_layout) { CLUTTER_NOTE (SCRIPT, "Skipping %s property '%s'", pinfo->is_child ? "child" : "layout", pinfo->name); unparsed = g_list_prepend (unparsed, pinfo); continue; } CLUTTER_NOTE (SCRIPT, "Parsing %s property (id:%s)", pinfo->pspec ? "regular" : "custom", pinfo->name); if (parse_custom) res = iface->parse_custom_node (scriptable, script, &value, pinfo->name, pinfo->node); if (!res) res = _clutter_script_parse_node (script, &value, pinfo->name, pinfo->node, pinfo->pspec); if (!res) { CLUTTER_NOTE (SCRIPT, "Property '%s' ignored", pinfo->name); unparsed = g_list_prepend (unparsed, pinfo); continue; } g_ptr_array_add (*param_names, g_strdup (pinfo->name)); g_array_append_val (*param_values, value); property_info_free (pinfo); } g_list_free (properties); return unparsed; } static GList * clutter_script_construct_parameters (ClutterScript *script, GType gtype, const gchar *name, GList *properties, GPtrArray **construct_param_names, GArray **construct_param_values) { GObjectClass *klass; GList *l, *unparsed; klass = g_type_class_ref (gtype); g_assert (klass != NULL); *construct_param_names = g_ptr_array_new_with_free_func (g_free); *construct_param_values = g_array_new (FALSE, FALSE, sizeof (GValue)); g_array_set_clear_func (*construct_param_values, (GDestroyNotify) g_value_unset); unparsed = NULL; for (l = properties; l != NULL; l = l->next) { PropertyInfo *pinfo = l->data; GValue value = G_VALUE_INIT; GParamSpec *pspec = NULL; /* we allow custom property names for classes, so if we * don't find a corresponding GObject property for this * class we just skip it and let the class itself deal * with it later on */ pspec = g_object_class_find_property (klass, pinfo->name); if (pspec) pinfo->pspec = g_param_spec_ref (pspec); else { pinfo->pspec = NULL; unparsed = g_list_prepend (unparsed, pinfo); continue; } if (!(pspec->flags & G_PARAM_CONSTRUCT_ONLY)) { unparsed = g_list_prepend (unparsed, pinfo); continue; } if (!_clutter_script_parse_node (script, &value, pinfo->name, pinfo->node, pinfo->pspec)) { unparsed = g_list_prepend (unparsed, pinfo); continue; } g_ptr_array_add (*construct_param_names, g_strdup (pinfo->name)); g_array_append_val (*construct_param_values, value); property_info_free (pinfo); } g_list_free (properties); g_type_class_unref (klass); return unparsed; } static void apply_layout_properties (ClutterScript *script, ClutterContainer *container, ClutterActor *actor, ObjectInfo *oinfo) { ClutterScriptable *scriptable = NULL; ClutterScriptableIface *iface = NULL; gboolean parse_custom_node = FALSE; GList *l, *unresolved, *properties; ClutterLayoutManager *manager; GType meta_type; manager = g_object_get_data (G_OBJECT (container), "clutter-layout-manager"); if (manager == NULL) return; meta_type = _clutter_layout_manager_get_child_meta_type (manager); if (meta_type == G_TYPE_INVALID) return; CLUTTER_NOTE (SCRIPT, "Layout manager of type '%s' with meta type '%s'", G_OBJECT_TYPE_NAME (manager), g_type_name (meta_type)); /* shortcut, to avoid typechecking every time */ if (CLUTTER_IS_SCRIPTABLE (manager)) { scriptable = CLUTTER_SCRIPTABLE (manager); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE; } properties = oinfo->properties; oinfo->properties = NULL; unresolved = NULL; for (l = properties; l != NULL; l = l->next) { PropertyInfo *pinfo = l->data; GValue value = G_VALUE_INIT; gboolean res = FALSE; const gchar *name; if (!pinfo->is_layout) { unresolved = g_list_prepend (unresolved, pinfo); continue; } name = pinfo->name + strlen ("layout::"); pinfo->pspec = clutter_layout_manager_find_child_property (manager, name); if (pinfo->pspec != NULL) g_param_spec_ref (pinfo->pspec); CLUTTER_NOTE (SCRIPT, "Parsing %s layout property (id:%s)", pinfo->pspec != NULL ? "regular" : "custom", name); if (parse_custom_node) res = iface->parse_custom_node (scriptable, script, &value, name, pinfo->node); if (!res) res = _clutter_script_parse_node (script, &value, name, pinfo->node, pinfo->pspec); if (!res) { CLUTTER_NOTE (SCRIPT, "Layout property '%s' ignored", name); unresolved = g_list_prepend (unresolved, pinfo); continue; } CLUTTER_NOTE (SCRIPT, "Setting %s layout property '%s' (type:%s) to " "object '%s' (id:%s)", iface->set_custom_property != NULL ? "custom" : "regular", name, g_type_name (G_VALUE_TYPE (&value)), g_type_name (oinfo->gtype), oinfo->id); clutter_layout_manager_child_set_property (manager, container, actor, name, &value); g_value_unset (&value); property_info_free (pinfo); } g_list_free (properties); oinfo->properties = unresolved; } static void apply_child_properties (ClutterScript *script, ClutterContainer *container, ClutterActor *actor, ObjectInfo *oinfo) { ClutterScriptable *scriptable = NULL; ClutterScriptableIface *iface = NULL; gboolean parse_custom_node = FALSE; GList *l, *unresolved, *properties; GObjectClass *klass; GType meta_type; meta_type = CLUTTER_CONTAINER_GET_IFACE (container)->child_meta_type; if (meta_type == G_TYPE_INVALID) return; klass = G_OBJECT_GET_CLASS (container); /* shortcut, to avoid typechecking every time */ if (CLUTTER_IS_SCRIPTABLE (container)) { scriptable = CLUTTER_SCRIPTABLE (container); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); parse_custom_node = iface->parse_custom_node != NULL ? TRUE : FALSE; } properties = oinfo->properties; oinfo->properties = NULL; unresolved = NULL; for (l = properties; l != NULL; l = l->next) { PropertyInfo *pinfo = l->data; GValue value = G_VALUE_INIT; gboolean res = FALSE; const gchar *name; if (!pinfo->is_child) { unresolved = g_list_prepend (unresolved, pinfo); continue; } name = pinfo->name + strlen ("child::"); pinfo->pspec = clutter_container_class_find_child_property (klass, name); if (pinfo->pspec != NULL) g_param_spec_ref (pinfo->pspec); CLUTTER_NOTE (SCRIPT, "Parsing %s child property (id:%s)", pinfo->pspec != NULL ? "regular" : "custom", name); if (parse_custom_node) res = iface->parse_custom_node (scriptable, script, &value, name, pinfo->node); if (!res) res = _clutter_script_parse_node (script, &value, name, pinfo->node, pinfo->pspec); if (!res) { CLUTTER_NOTE (SCRIPT, "Child property '%s' ignored", name); unresolved = g_list_prepend (unresolved, pinfo); continue; } CLUTTER_NOTE (SCRIPT, "Setting %s child property '%s' (type:%s) to " "object '%s' (id:%s)", iface->set_custom_property != NULL ? "custom" : "regular", name, g_type_name (G_VALUE_TYPE (&value)), g_type_name (oinfo->gtype), oinfo->id); clutter_container_child_set_property (container, actor, name, &value); g_value_unset (&value); property_info_free (pinfo); } g_list_free (properties); oinfo->properties = unresolved; } static void add_children (ClutterScript *script, ObjectInfo *oinfo) { ClutterContainer *container = CLUTTER_CONTAINER (oinfo->object); GList *l, *unresolved; unresolved = NULL; for (l = oinfo->children; l != NULL; l = l->next) { const gchar *name = l->data; GObject *object = NULL; ObjectInfo *child_info; child_info = _clutter_script_get_object_info (script, name); if (child_info != NULL) { _clutter_script_construct_object (script, child_info); object = child_info->object; } if (object == NULL) { unresolved = g_list_prepend (unresolved, g_strdup (name)); continue; } if (!CLUTTER_IS_ACTOR (object)) { g_warning ("The object definition '%s' (type: %s) is not " "an actor, but it is referenced in the 'children' " "member of the container '%s' (type: %s); skipping.", child_info->id, g_type_name (child_info->gtype), oinfo->id, g_type_name (oinfo->gtype)); continue; } CLUTTER_NOTE (SCRIPT, "Adding children '%s' to actor of type '%s'", name, g_type_name (G_OBJECT_TYPE (container))); clutter_container_add_actor (container, CLUTTER_ACTOR (object)); } g_list_free_full (oinfo->children, g_free); oinfo->children = unresolved; } static inline void _clutter_script_check_unresolved (ClutterScript *script, ObjectInfo *oinfo) { if (oinfo->children != NULL && CLUTTER_IS_CONTAINER (oinfo->object)) add_children (script, oinfo); /* this is a bit *eugh*, but it allows us to effectively make sure * that child and layout properties are parsed and applied to the * right child */ if (oinfo->properties != NULL && CLUTTER_IS_ACTOR (oinfo->object)) { ClutterActor *parent; parent = clutter_actor_get_parent (CLUTTER_ACTOR (oinfo->object)); if (parent != NULL) { ClutterContainer *container = CLUTTER_CONTAINER (parent); ClutterActor *child; for (child = clutter_actor_get_first_child (parent); child != NULL; child = clutter_actor_get_next_sibling (child)) { ObjectInfo *child_info; const gchar *id_; id_ = clutter_get_script_id (G_OBJECT (child)); if (id_ == NULL || *id_ == '\0') continue; child_info = _clutter_script_get_object_info (script, id_); if (child_info == NULL) continue; apply_child_properties (script, container, child, child_info); apply_layout_properties (script, container, child, child_info); } } } if (oinfo->properties || oinfo->children) oinfo->has_unresolved = TRUE; else oinfo->has_unresolved = FALSE; } void _clutter_script_apply_properties (ClutterScript *script, ObjectInfo *oinfo) { ClutterScriptable *scriptable = NULL; ClutterScriptableIface *iface = NULL; gboolean set_custom_property = FALSE; GObject *object = oinfo->object; GList *properties; g_autoptr (GPtrArray) param_names = NULL; g_autoptr (GArray) param_values = NULL; guint i; if (!oinfo->has_unresolved) return; /* shortcut, to avoid typechecking every time */ if (CLUTTER_IS_SCRIPTABLE (object)) { scriptable = CLUTTER_SCRIPTABLE (object); iface = CLUTTER_SCRIPTABLE_GET_IFACE (scriptable); if (iface->set_custom_property) set_custom_property = TRUE; } /* then we get the rest of the parameters, asking the object itself * to translate them for us, if we cannot do that */ properties = oinfo->properties; oinfo->properties = clutter_script_translate_parameters (script, object, oinfo->id, properties, ¶m_names, ¶m_values); /* consume all the properties we could translate in this pass */ for (i = 0; i < param_names->len; i++) { char *name = g_ptr_array_index (param_names, i); GValue *value = &g_array_index (param_values, GValue, i); CLUTTER_NOTE (SCRIPT, "Setting %s property '%s' (type:%s) to object '%s' (id:%s)", set_custom_property ? "custom" : "regular", name, g_type_name (G_VALUE_TYPE (value)), g_type_name (oinfo->gtype), oinfo->id); if (set_custom_property) iface->set_custom_property (scriptable, script, name, value); else g_object_set_property (object, name, value); } _clutter_script_check_unresolved (script, oinfo); } void _clutter_script_construct_object (ClutterScript *script, ObjectInfo *oinfo) { g_autoptr (GPtrArray) param_names = NULL; g_autoptr (GArray) param_values = NULL; /* we have completely updated the object */ if (oinfo->object != NULL) { if (oinfo->has_unresolved) _clutter_script_check_unresolved (script, oinfo); return; } if (oinfo->gtype == G_TYPE_INVALID) { if (G_UNLIKELY (oinfo->type_func)) oinfo->gtype = _clutter_script_get_type_from_symbol (oinfo->type_func); else oinfo->gtype = clutter_script_get_type_from_name (script, oinfo->class_name); if (G_UNLIKELY (oinfo->gtype == G_TYPE_INVALID)) return; } oinfo->is_actor = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_ACTOR); if (oinfo->is_actor) oinfo->is_stage = g_type_is_a (oinfo->gtype, CLUTTER_TYPE_STAGE); if (oinfo->is_stage && oinfo->is_stage_default) { ClutterStageManager *manager = clutter_stage_manager_get_default (); GList *properties = oinfo->properties; ClutterStage *default_stage; /* the default stage is a complex beast: we cannot create it using * g_object_newv() but we need clutter_script_construct_parameters() * to add the GParamSpec to the PropertyInfo pspec member, so * that we don't have to implement every complex property (like * the "color" one) directly inside the ClutterStage class. */ oinfo->properties = clutter_script_construct_parameters (script, oinfo->gtype, oinfo->id, properties, ¶m_names, ¶m_values); default_stage = clutter_stage_manager_get_default_stage (manager); oinfo->object = G_OBJECT (default_stage); } else { GList *properties = oinfo->properties; /* every other object: first, we get the construction parameters */ oinfo->properties = clutter_script_construct_parameters (script, oinfo->gtype, oinfo->id, properties, ¶m_names, ¶m_values); oinfo->object = g_object_new_with_properties (oinfo->gtype, param_names->len, (const gchar **) param_names->pdata, (const GValue *) param_values->data); /* by sinking the floating reference, we make sure that the reference * count is correct whether the object is referenced from somewhere * else too or only by this ClutterScript object. */ g_object_ref_sink (oinfo->object); } g_assert (oinfo->object != NULL); if (CLUTTER_IS_SCRIPTABLE (oinfo->object)) clutter_scriptable_set_id (CLUTTER_SCRIPTABLE (oinfo->object), oinfo->id); else g_object_set_data_full (oinfo->object, "clutter-script-id", g_strdup (oinfo->id), g_free); _clutter_script_check_unresolved (script, oinfo); } muffin-6.4.1/clutter/clutter/clutter-text.c0000664000175000017500000061326714723361714017735 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Øyvind Kolås * Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-text * @short_description: An actor for displaying and editing text * * #ClutterText is an actor that displays custom text using Pango * as the text rendering engine. * * #ClutterText also allows inline editing of the text if the * actor is set editable using clutter_text_set_editable(). * * Selection using keyboard or pointers can be enabled using * clutter_text_set_selectable(). * * #ClutterText is available since Clutter 1.0 */ #include "clutter-build-config.h" #include #include #include "clutter-text.h" #include "clutter-actor-private.h" #include "clutter-animatable.h" #include "clutter-backend-private.h" #include "clutter-binding-pool.h" #include "clutter-color.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-keysyms.h" #include "clutter-main.h" #include "clutter-marshal.h" #include "clutter-private.h" /* includes */ #include "clutter-property-transition.h" #include "clutter-text-buffer.h" #include "clutter-units.h" #include "clutter-paint-volume-private.h" #include "clutter-scriptable.h" #include "clutter-input-focus.h" /* cursor width in pixels */ #define DEFAULT_CURSOR_SIZE 2 /* vertical padding for the cursor */ #define CURSOR_Y_PADDING 2 /* We need at least three cached layouts to run the allocation without * regenerating a new layout. First the layout will be generated at * full width to get the preferred width, then it will be generated at * the preferred width to get the preferred height and then it might * be regenerated at a different width to get the height for the * actual allocated width * * since we might get multiple queries from layout managers doing a * double-pass allocations, like tabular ones, we should use 6 slots */ #define N_CACHED_LAYOUTS 6 typedef struct _LayoutCache LayoutCache; struct _LayoutCache { /* Cached layout. Pango internally caches the computed extents * when they are requested so there is no need to cache that as * well */ PangoLayout *layout; /* A number representing the age of this cache (so that when a * new layout is needed the last used cache is replaced) */ guint age; }; struct _ClutterTextInputFocus { ClutterInputFocus parent_instance; ClutterText *text; }; struct _ClutterTextPrivate { PangoFontDescription *font_desc; /* the displayed text */ ClutterTextBuffer *buffer; gchar *font_name; gchar *preedit_str; ClutterColor text_color; LayoutCache cached_layouts[N_CACHED_LAYOUTS]; guint cache_age; /* These are the attributes set by the attributes property */ PangoAttrList *attrs; /* These are the attributes derived from the text when the use-markup property is set */ PangoAttrList *markup_attrs; /* This is the combination of the above two lists. It is set to NULL whenever either of them changes and then regenerated by merging the two lists whenever a layout is needed */ PangoAttrList *effective_attrs; /* These are the attributes for the preedit string. These are merged with the effective attributes into a temporary list before creating a layout */ PangoAttrList *preedit_attrs; /* current cursor position */ gint position; /* current 'other end of selection' position */ gint selection_bound; /* the x position in the PangoLayout, used to * avoid drifting when repeatedly moving up|down */ gint x_pos; /* the x position of the PangoLayout (in both physical and logical pixels) * when in single line mode, to scroll the contents of the * text actor */ gint text_x; gint text_logical_x; /* the y position of the PangoLayout (in both physical and logical pixels), * fixed to 0 by default for now */ gint text_y; gint text_logical_y; /* Where to draw the cursor */ graphene_rect_t cursor_rect; ClutterColor cursor_color; guint cursor_size; /* Box representing the paint volume. The box is lazily calculated and cached */ ClutterPaintVolume paint_volume; guint preedit_cursor_pos; gint preedit_n_chars; ClutterColor selection_color; ClutterColor selected_text_color; gunichar password_char; guint password_hint_id; guint password_hint_timeout; /* Signal handler for when the backend changes its font settings */ gulong settings_changed_id; /* Signal handler for when the :text-direction changes */ gulong direction_changed_id; ClutterInputFocus *input_focus; ClutterInputContentHintFlags input_hints; ClutterInputContentPurpose input_purpose; /* Signal handler for when the :resource-scale changes */ gulong resource_scale_changed_id; /* bitfields */ guint alignment : 2; guint wrap : 1; guint use_underline : 1; guint use_markup : 1; guint ellipsize : 3; guint single_line_mode : 1; guint wrap_mode : 3; guint justify : 1; guint editable : 1; guint cursor_visible : 1; guint activatable : 1; guint selectable : 1; guint selection_color_set : 1; guint in_select_drag : 1; guint in_select_touch : 1; guint cursor_color_set : 1; guint preedit_set : 1; guint is_default_font : 1; guint has_focus : 1; guint selected_text_color_set : 1; guint paint_volume_valid : 1; guint show_password_hint : 1; guint password_hint_visible : 1; guint resolved_direction : 4; }; enum { PROP_0, PROP_BUFFER, PROP_FONT_NAME, PROP_FONT_DESCRIPTION, PROP_TEXT, PROP_COLOR, PROP_USE_MARKUP, PROP_ATTRIBUTES, PROP_LINE_ALIGNMENT, PROP_LINE_WRAP, PROP_LINE_WRAP_MODE, PROP_JUSTIFY, PROP_ELLIPSIZE, PROP_POSITION, /* XXX:2.0 - remove */ PROP_SELECTION_BOUND, PROP_SELECTION_COLOR, PROP_SELECTION_COLOR_SET, PROP_CURSOR_VISIBLE, PROP_CURSOR_COLOR, PROP_CURSOR_COLOR_SET, PROP_CURSOR_SIZE, PROP_CURSOR_POSITION, PROP_EDITABLE, PROP_SELECTABLE, PROP_ACTIVATABLE, PROP_PASSWORD_CHAR, PROP_MAX_LENGTH, PROP_SINGLE_LINE_MODE, PROP_SELECTED_TEXT_COLOR, PROP_SELECTED_TEXT_COLOR_SET, PROP_INPUT_HINTS, PROP_INPUT_PURPOSE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { TEXT_CHANGED, CURSOR_EVENT, /* XXX:2.0 - remove */ ACTIVATE, INSERT_TEXT, DELETE_TEXT, CURSOR_CHANGED, LAST_SIGNAL }; static guint text_signals[LAST_SIGNAL] = { 0, }; static void clutter_text_settings_changed_cb (ClutterText *text); static void buffer_connect_signals (ClutterText *self); static void buffer_disconnect_signals (ClutterText *self); static ClutterTextBuffer *get_buffer (ClutterText *self); static const ClutterColor default_cursor_color = { 0, 0, 0, 255 }; static const ClutterColor default_selection_color = { 0, 0, 0, 255 }; static const ClutterColor default_text_color = { 0, 0, 0, 255 }; static const ClutterColor default_selected_text_color = { 0, 0, 0, 255 }; static CoglPipeline *default_color_pipeline = NULL; static ClutterAnimatableInterface *parent_animatable_iface = NULL; static ClutterScriptableIface *parent_scriptable_iface = NULL; /* ClutterTextInputFocus */ #define CLUTTER_TYPE_TEXT_INPUT_FOCUS (clutter_text_input_focus_get_type ()) G_DECLARE_FINAL_TYPE (ClutterTextInputFocus, clutter_text_input_focus, CLUTTER, TEXT_INPUT_FOCUS, ClutterInputFocus) G_DEFINE_TYPE (ClutterTextInputFocus, clutter_text_input_focus, CLUTTER_TYPE_INPUT_FOCUS) /* Utilities pango to (logical) pixels functions */ static float pixels_to_pango (float px) { return ceilf (px * (float) PANGO_SCALE); } static float logical_pixels_to_pango (float px, float scale) { return pixels_to_pango (px * scale); } static float pango_to_pixels (float size) { return ceilf (size / (float) PANGO_SCALE); } static float pango_to_logical_pixels (float size, float scale) { return pango_to_pixels (size / scale); } static void clutter_text_input_focus_request_surrounding (ClutterInputFocus *focus) { ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text; ClutterTextBuffer *buffer; const gchar *text; gint anchor_pos, cursor_pos; buffer = clutter_text_get_buffer (clutter_text); text = clutter_text_buffer_get_text (buffer); cursor_pos = clutter_text_get_cursor_position (clutter_text); if (cursor_pos < 0) cursor_pos = clutter_text_buffer_get_length (buffer); anchor_pos = clutter_text_get_selection_bound (clutter_text); if (anchor_pos < 0) anchor_pos = cursor_pos; clutter_input_focus_set_surrounding (focus, text, g_utf8_offset_to_pointer (text, cursor_pos) - text, g_utf8_offset_to_pointer (text, anchor_pos) - text); } static void clutter_text_input_focus_delete_surrounding (ClutterInputFocus *focus, int offset, guint len) { ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text; int cursor; int start; cursor = clutter_text_get_cursor_position (clutter_text); start = cursor + offset; if (start < 0) { g_warning ("The offset '%d' of deleting surrounding is larger than the cursor pos '%d'", offset, cursor); return; } if (clutter_text_get_editable (clutter_text)) clutter_text_delete_text (clutter_text, start, len); } static void clutter_text_input_focus_commit_text (ClutterInputFocus *focus, const gchar *text) { ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text; if (clutter_text_get_editable (clutter_text)) { clutter_text_delete_selection (clutter_text); clutter_text_insert_text (clutter_text, text, clutter_text_get_cursor_position (clutter_text)); clutter_text_set_preedit_string (clutter_text, NULL, NULL, 0); } } static void clutter_text_input_focus_set_preedit_text (ClutterInputFocus *focus, const gchar *preedit_text, guint cursor_pos) { ClutterText *clutter_text = CLUTTER_TEXT_INPUT_FOCUS (focus)->text; if (clutter_text_get_editable (clutter_text)) { PangoAttrList *list; list = pango_attr_list_new (); pango_attr_list_insert (list, pango_attr_underline_new (PANGO_UNDERLINE_SINGLE)); clutter_text_set_preedit_string (clutter_text, preedit_text, list, cursor_pos); pango_attr_list_unref (list); } } static void clutter_text_input_focus_class_init (ClutterTextInputFocusClass *klass) { ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass); focus_class->request_surrounding = clutter_text_input_focus_request_surrounding; focus_class->delete_surrounding = clutter_text_input_focus_delete_surrounding; focus_class->commit_text = clutter_text_input_focus_commit_text; focus_class->set_preedit_text = clutter_text_input_focus_set_preedit_text; } static void clutter_text_input_focus_init (ClutterTextInputFocus *focus) { } static ClutterInputFocus * clutter_text_input_focus_new (ClutterText *text) { ClutterTextInputFocus *focus; focus = g_object_new (CLUTTER_TYPE_TEXT_INPUT_FOCUS, NULL); focus->text = text; return CLUTTER_INPUT_FOCUS (focus); } /* ClutterText */ static void clutter_scriptable_iface_init (ClutterScriptableIface *iface); static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterText, clutter_text, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (ClutterText) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_SCRIPTABLE, clutter_scriptable_iface_init) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE, clutter_animatable_iface_init)); static inline void clutter_text_dirty_paint_volume (ClutterText *text) { ClutterTextPrivate *priv = text->priv; if (priv->paint_volume_valid) { clutter_paint_volume_free (&priv->paint_volume); priv->paint_volume_valid = FALSE; } } static inline void clutter_text_queue_redraw (ClutterActor *self) { /* This is a wrapper for clutter_actor_queue_redraw that also dirties the cached paint volume. It would be nice if we could just override the default implementation of the queue redraw signal to do this instead but that doesn't work because the signal isn't immediately emitted when queue_redraw is called. Clutter will however immediately call get_paint_volume when queue_redraw is called so we do need to dirty it immediately. */ clutter_text_dirty_paint_volume (CLUTTER_TEXT (self)); clutter_actor_queue_redraw (self); } static gboolean clutter_text_should_draw_cursor (ClutterText *self) { ClutterTextPrivate *priv = self->priv; return (priv->editable || priv->selectable) && priv->cursor_visible && priv->has_focus; } #define clutter_actor_queue_redraw \ Please_use_clutter_text_queue_redraw_instead #define offset_real(t,p) ((p) == -1 ? g_utf8_strlen ((t), -1) : (p)) static gint offset_to_bytes (const gchar *text, gint pos) { const gchar *ptr; if (pos < 0) return strlen (text); /* Loop over each character in the string until we either reach the end or the requested position */ for (ptr = text; *ptr && pos-- > 0; ptr = g_utf8_next_char (ptr)); return ptr - text; } #define bytes_to_offset(t,p) (g_utf8_pointer_to_offset ((t), (t) + (p))) static inline void clutter_text_clear_selection (ClutterText *self) { ClutterTextPrivate *priv = self->priv; if (priv->selection_bound != priv->position) { priv->selection_bound = priv->position; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]); clutter_text_queue_redraw (CLUTTER_ACTOR (self)); } } static gboolean clutter_text_is_empty (ClutterText *self) { if (self->priv->buffer == NULL) return TRUE; if (clutter_text_buffer_get_length (self->priv->buffer) == 0) return TRUE; return FALSE; } static gchar * clutter_text_get_display_text (ClutterText *self) { ClutterTextPrivate *priv = self->priv; ClutterTextBuffer *buffer; const gchar *text; /* short-circuit the case where the buffer is unset or it's empty, * to avoid creating a pointless TextBuffer and emitting * notifications with it */ if (clutter_text_is_empty (self)) return g_strdup (""); buffer = get_buffer (self); text = clutter_text_buffer_get_text (buffer); /* simple short-circuit to avoid going through GString * with an empty text and a password char set */ if (text[0] == '\0') return g_strdup (""); if (G_LIKELY (priv->password_char == 0)) return g_strdup (text); else { GString *str; gunichar invisible_char; gchar buf[7]; gint char_len, i; guint n_chars; n_chars = clutter_text_buffer_get_length (buffer); str = g_string_sized_new (clutter_text_buffer_get_bytes (buffer)); invisible_char = priv->password_char; /* we need to convert the string built of invisible * characters into UTF-8 for it to be fed to the Pango * layout */ memset (buf, 0, sizeof (buf)); char_len = g_unichar_to_utf8 (invisible_char, buf); if (priv->show_password_hint && priv->password_hint_visible) { char *last_char; for (i = 0; i < n_chars - 1; i++) g_string_append_len (str, buf, char_len); last_char = g_utf8_offset_to_pointer (text, n_chars - 1); g_string_append (str, last_char); } else { for (i = 0; i < n_chars; i++) g_string_append_len (str, buf, char_len); } return g_string_free (str, FALSE); } } static void ensure_effective_pango_scale_attribute (ClutterText *self) { float resource_scale; ClutterTextPrivate *priv = self->priv; if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale) || resource_scale == 1.0) return; if (priv->effective_attrs != NULL) { PangoAttrIterator *iter; PangoAttribute *scale_attrib; PangoAttrList *old_attributes; old_attributes = priv->effective_attrs; priv->effective_attrs = pango_attr_list_copy (priv->effective_attrs); pango_attr_list_unref (old_attributes); iter = pango_attr_list_get_iterator (priv->effective_attrs); scale_attrib = pango_attr_iterator_get (iter, PANGO_ATTR_SCALE); if (scale_attrib != NULL) resource_scale *= ((PangoAttrFloat *) scale_attrib)->value; pango_attr_iterator_destroy (iter); } else priv->effective_attrs = pango_attr_list_new (); pango_attr_list_change (priv->effective_attrs, pango_attr_scale_new (resource_scale)); } static void set_effective_pango_attributes (ClutterText *self, PangoAttrList *attributes) { ClutterTextPrivate *priv = self->priv; if (attributes != NULL) { PangoAttrList *old_attributes = priv->effective_attrs; priv->effective_attrs = pango_attr_list_ref (attributes); if (old_attributes != NULL) pango_attr_list_unref (old_attributes); } else { g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); } ensure_effective_pango_scale_attribute (self); } static inline void clutter_text_ensure_effective_attributes (ClutterText *self) { ClutterTextPrivate *priv = self->priv; /* If we already have the effective attributes then we don't need to do anything */ if (priv->effective_attrs != NULL) return; /* Same as if we don't have any attribute at all. * We also ignore markup attributes for editable. */ if (priv->attrs == NULL && (priv->editable || priv->markup_attrs == NULL)) { set_effective_pango_attributes (self, NULL); return; } if (priv->attrs != NULL) { /* If there are no markup attributes, or if this is editable (in which * case we ignore markup), then we can just use these attrs directly */ if (priv->editable || priv->markup_attrs == NULL) set_effective_pango_attributes (self, priv->attrs); else { /* Otherwise we need to merge the two lists */ PangoAttrList *effective_attrs; PangoAttrIterator *iter; GSList *attributes, *l; effective_attrs = pango_attr_list_copy (priv->markup_attrs); iter = pango_attr_list_get_iterator (priv->attrs); do { attributes = pango_attr_iterator_get_attrs (iter); for (l = attributes; l != NULL; l = l->next) { PangoAttribute *attr = l->data; pango_attr_list_insert (effective_attrs, attr); } g_slist_free (attributes); } while (pango_attr_iterator_next (iter)); pango_attr_iterator_destroy (iter); set_effective_pango_attributes (self, effective_attrs); pango_attr_list_unref (effective_attrs); } } else if (priv->markup_attrs != NULL) { set_effective_pango_attributes (self, priv->markup_attrs); } } static PangoLayout * clutter_text_create_layout_no_cache (ClutterText *text, gint width, gint height, PangoEllipsizeMode ellipsize) { ClutterTextPrivate *priv = text->priv; PangoLayout *layout; gchar *contents; gsize contents_len; layout = clutter_actor_create_pango_layout (CLUTTER_ACTOR (text), NULL); pango_layout_set_font_description (layout, priv->font_desc); contents = clutter_text_get_display_text (text); contents_len = strlen (contents); if (priv->editable && priv->preedit_set) { GString *tmp = g_string_new (contents); PangoAttrList *tmp_attrs = pango_attr_list_new (); gint cursor_index; if (priv->position == 0) cursor_index = 0; else cursor_index = offset_to_bytes (contents, priv->position); g_string_insert (tmp, cursor_index, priv->preedit_str); pango_layout_set_text (layout, tmp->str, tmp->len); if (priv->preedit_attrs != NULL) { pango_attr_list_splice (tmp_attrs, priv->preedit_attrs, cursor_index, strlen (priv->preedit_str)); pango_layout_set_attributes (layout, tmp_attrs); } g_string_free (tmp, TRUE); pango_attr_list_unref (tmp_attrs); } else { PangoDirection pango_dir; if (priv->password_char != 0) pango_dir = PANGO_DIRECTION_NEUTRAL; else pango_dir = _clutter_pango_find_base_dir (contents, contents_len); if (pango_dir == PANGO_DIRECTION_NEUTRAL) { ClutterBackend *backend = clutter_get_default_backend (); ClutterTextDirection text_dir; if (clutter_actor_has_key_focus (CLUTTER_ACTOR (text))) { ClutterSeat *seat; ClutterKeymap *keymap; seat = clutter_backend_get_default_seat (backend); keymap = clutter_seat_get_keymap (seat); pango_dir = clutter_keymap_get_direction (keymap); } else { text_dir = clutter_actor_get_text_direction (CLUTTER_ACTOR (text)); if (text_dir == CLUTTER_TEXT_DIRECTION_RTL) pango_dir = PANGO_DIRECTION_RTL; else pango_dir = PANGO_DIRECTION_LTR; } } pango_context_set_base_dir (clutter_actor_get_pango_context (CLUTTER_ACTOR (text)), pango_dir); priv->resolved_direction = pango_dir; pango_layout_set_text (layout, contents, contents_len); } /* This will merge the markup attributes and the attributes * property if needed */ clutter_text_ensure_effective_attributes (text); if (priv->effective_attrs != NULL) pango_layout_set_attributes (layout, priv->effective_attrs); pango_layout_set_alignment (layout, priv->alignment); pango_layout_set_single_paragraph_mode (layout, priv->single_line_mode); pango_layout_set_justify (layout, priv->justify); pango_layout_set_wrap (layout, priv->wrap_mode); pango_layout_set_ellipsize (layout, ellipsize); pango_layout_set_width (layout, width); pango_layout_set_height (layout, height); g_free (contents); return layout; } static void clutter_text_dirty_cache (ClutterText *text) { ClutterTextPrivate *priv = text->priv; int i; /* Delete the cached layouts so they will be recreated the next time they are needed */ for (i = 0; i < N_CACHED_LAYOUTS; i++) if (priv->cached_layouts[i].layout) { g_object_unref (priv->cached_layouts[i].layout); priv->cached_layouts[i].layout = NULL; } clutter_text_dirty_paint_volume (text); } /* * clutter_text_set_font_description_internal: * @self: a #ClutterText * @desc: a #PangoFontDescription * * Sets @desc as the font description to be used by the #ClutterText * actor. The #PangoFontDescription is copied. * * This function will also set the :font-name field as a side-effect * * This function will evict the layout cache, and queue a relayout if * the #ClutterText actor has contents. */ static inline void clutter_text_set_font_description_internal (ClutterText *self, PangoFontDescription *desc, gboolean is_default_font) { ClutterTextPrivate *priv = self->priv; priv->is_default_font = is_default_font; if (priv->font_desc == desc || pango_font_description_equal (priv->font_desc, desc)) return; if (priv->font_desc != NULL) pango_font_description_free (priv->font_desc); priv->font_desc = pango_font_description_copy (desc); /* update the font name string we use */ g_free (priv->font_name); priv->font_name = pango_font_description_to_string (priv->font_desc); clutter_text_dirty_cache (self); if (clutter_text_buffer_get_length (get_buffer (self)) != 0) clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_DESCRIPTION]); } static void clutter_text_settings_changed_cb (ClutterText *text) { ClutterTextPrivate *priv = text->priv; guint password_hint_time = 0; ClutterSettings *settings; settings = clutter_settings_get_default (); g_object_get (settings, "password-hint-time", &password_hint_time, NULL); priv->show_password_hint = password_hint_time > 0; priv->password_hint_timeout = password_hint_time; if (priv->is_default_font) { PangoFontDescription *font_desc; gchar *font_name = NULL; g_object_get (settings, "font-name", &font_name, NULL); CLUTTER_NOTE (ACTOR, "Text[%p]: default font changed to '%s'", text, font_name); font_desc = pango_font_description_from_string (font_name); clutter_text_set_font_description_internal (text, font_desc, TRUE); pango_font_description_free (font_desc); g_free (font_name); } clutter_text_dirty_cache (text); clutter_actor_queue_relayout (CLUTTER_ACTOR (text)); } static void clutter_text_direction_changed_cb (GObject *gobject, GParamSpec *pspec) { clutter_text_dirty_cache (CLUTTER_TEXT (gobject)); /* no need to queue a relayout: set_text_direction() will do that for us */ } static void clutter_text_resource_scale_changed_cb (GObject *gobject, GParamSpec *pspec) { ClutterText *self = CLUTTER_TEXT (gobject); ClutterTextPrivate *priv = self->priv; g_clear_pointer (&priv->effective_attrs, pango_attr_list_unref); clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject)); } /* * clutter_text_create_layout: * @text: a #ClutterText * @allocation_width: the allocation width * @allocation_height: the allocation height * * Like clutter_text_create_layout_no_cache(), but will also ensure * the glyphs cache. If a previously cached layout generated using the * same width is available then that will be used instead of * generating a new one. */ static PangoLayout * clutter_text_create_layout (ClutterText *text, gfloat allocation_width, gfloat allocation_height) { ClutterTextPrivate *priv = text->priv; LayoutCache *oldest_cache = priv->cached_layouts; gboolean found_free_cache = FALSE; gint width = -1; gint height = -1; PangoEllipsizeMode ellipsize = PANGO_ELLIPSIZE_NONE; int i; /* First determine the width, height, and ellipsize mode that * we need for the layout. The ellipsize mode depends on * allocation_width/allocation_size as follows: * * Cases, assuming ellipsize != NONE on actor: * * Width request: ellipsization can be set or not on layout, * doesn't matter. * * Height request: ellipsization must never be set on layout * if wrap=true, because we need to measure the wrapped * height. It must always be set if wrap=false. * * Allocate: ellipsization must always be set. * * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 */ if (priv->ellipsize != PANGO_ELLIPSIZE_NONE) { if (allocation_height < 0 && priv->wrap) ; /* must not set ellipsization on wrap=true height request */ else { if (!priv->editable) ellipsize = priv->ellipsize; } } /* When painting, we always need to set the width, since * we might need to align to the right. When getting the * height, however, there are some cases where we know that * the width won't affect the width. * * - editable, single-line text actors, since those can * scroll the layout. * - non-wrapping, non-ellipsizing actors. */ if (allocation_width >= 0 && (allocation_height >= 0 || !((priv->editable && priv->single_line_mode) || (priv->ellipsize == PANGO_ELLIPSIZE_NONE && !priv->wrap)))) { width = pixels_to_pango (allocation_width); } /* Pango only uses height if ellipsization is enabled, so don't set * height if ellipsize isn't set. Pango implicitly enables wrapping * if height is set, so don't set height if wrapping is disabled. * In other words, only set height if we want to both wrap then * ellipsize and we're not in single line mode. * * See http://bugzilla.gnome.org/show_bug.cgi?id=560931 if this * seems odd. */ if (allocation_height >= 0 && priv->wrap && priv->ellipsize != PANGO_ELLIPSIZE_NONE && !priv->single_line_mode) { height = pixels_to_pango (allocation_height); } /* Search for a cached layout with the same width and keep * track of the oldest one */ for (i = 0; i < N_CACHED_LAYOUTS; i++) { if (priv->cached_layouts[i].layout == NULL) { /* Always prefer free cache spaces */ found_free_cache = TRUE; oldest_cache = priv->cached_layouts + i; } else { PangoLayout *cached = priv->cached_layouts[i].layout; gint cached_width = pango_layout_get_width (cached); gint cached_height = pango_layout_get_height (cached); gint cached_ellipsize = pango_layout_get_ellipsize (cached); if (cached_width == width && cached_height == height && cached_ellipsize == ellipsize) { /* If this cached layout is using the same size then we can * just return that directly */ CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for size %.2fx%.2f", text, allocation_width, allocation_height); return priv->cached_layouts[i].layout; } /* When getting the preferred height for a specific width, * we might be able to reuse the layout from getting the * preferred width. If the width that the layout gives * unconstrained is less than the width that we are using * than the height will be unaffected by that width. */ if (allocation_height < 0 && cached_width == -1 && cached_ellipsize == ellipsize) { PangoRectangle logical_rect; pango_layout_get_extents (priv->cached_layouts[i].layout, NULL, &logical_rect); if (logical_rect.width <= width) { /* We've been asked for our height for the width we gave as a result * of a _get_preferred_width call */ CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache hit for size %.2fx%.2f " "(unwrapped width narrower than given width)", text, allocation_width, allocation_height); return priv->cached_layouts[i].layout; } } if (!found_free_cache && (priv->cached_layouts[i].age < oldest_cache->age)) { oldest_cache = priv->cached_layouts + i; } } } CLUTTER_NOTE (ACTOR, "ClutterText: %p: cache miss for size %.2fx%.2f", text, allocation_width, allocation_height); /* If we make it here then we didn't have a cached version so we need to recreate the layout */ if (oldest_cache->layout) g_object_unref (oldest_cache->layout); oldest_cache->layout = clutter_text_create_layout_no_cache (text, width, height, ellipsize); cogl_pango_ensure_glyph_cache_for_layout (oldest_cache->layout); /* Mark the 'time' this cache was created and advance the time */ oldest_cache->age = priv->cache_age++; return oldest_cache->layout; } static PangoLayout * create_text_layout_with_scale (ClutterText *text, gfloat allocation_width, gfloat allocation_height, gfloat scale) { if (allocation_width > 0) allocation_width = roundf (allocation_width * scale); if (allocation_height > 0) allocation_height = roundf (allocation_height * scale); return clutter_text_create_layout (text, allocation_width, allocation_height); } static PangoLayout * maybe_create_text_layout_with_resource_scale (ClutterText *text, gfloat allocation_width, gfloat allocation_height) { float resource_scale; if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (text), &resource_scale)) return NULL; return create_text_layout_with_scale (text, allocation_width, allocation_height, resource_scale); } /** * clutter_text_coords_to_position: * @self: a #ClutterText * @x: the X coordinate, relative to the actor * @y: the Y coordinate, relative to the actor * * Retrieves the position of the character at the given coordinates. * * Return: the position of the character * * Since: 1.10 */ gint clutter_text_coords_to_position (ClutterText *self, gfloat x, gfloat y) { gint index_; gint px, py; gint trailing; gfloat resource_scale; g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) return 0; /* Take any offset due to scrolling into account, and normalize * the coordinates to PangoScale units */ px = logical_pixels_to_pango (x - self->priv->text_logical_x, resource_scale); py = logical_pixels_to_pango (y - self->priv->text_logical_y, resource_scale); pango_layout_xy_to_index (clutter_text_get_layout (self), px, py, &index_, &trailing); return index_ + trailing; } static gboolean clutter_text_position_to_coords_internal (ClutterText *self, gint position, gfloat *x, gfloat *y, gfloat *line_height) { ClutterTextPrivate *priv; PangoRectangle rect; gint n_chars; gint password_char_bytes = 1; gint index_; gsize n_bytes; g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); priv = self->priv; n_chars = clutter_text_buffer_get_length (get_buffer (self)); if (priv->preedit_set) n_chars += priv->preedit_n_chars; if (position < -1 || position > n_chars) return FALSE; if (priv->password_char != 0) password_char_bytes = g_unichar_to_utf8 (priv->password_char, NULL); if (position == -1) { if (priv->password_char == 0) { n_bytes = clutter_text_buffer_get_bytes (get_buffer (self)); if (priv->editable && priv->preedit_set) index_ = n_bytes + strlen (priv->preedit_str); else index_ = n_bytes; } else index_ = n_chars * password_char_bytes; } else if (position == 0) { index_ = 0; } else { gchar *text = clutter_text_get_display_text (self); GString *tmp = g_string_new (text); gint cursor_index; cursor_index = offset_to_bytes (text, priv->position); if (priv->preedit_str != NULL) g_string_insert (tmp, cursor_index, priv->preedit_str); if (priv->password_char == 0) index_ = offset_to_bytes (tmp->str, position); else index_ = position * password_char_bytes; g_free (text); g_string_free (tmp, TRUE); } pango_layout_get_cursor_pos (clutter_text_get_layout (self), index_, &rect, NULL); if (x) { *x = pango_to_pixels (rect.x); /* Take any offset due to scrolling into account */ if (priv->single_line_mode) *x += priv->text_x; } if (y) *y = pango_to_pixels (rect.y); if (line_height) *line_height = pango_to_pixels (rect.height); return TRUE; } /** * clutter_text_position_to_coords: * @self: a #ClutterText * @position: position in characters * @x: (out): return location for the X coordinate, or %NULL * @y: (out): return location for the Y coordinate, or %NULL * @line_height: (out): return location for the line height, or %NULL * * Retrieves the coordinates of the given @position. * * Return value: %TRUE if the conversion was successful * * Since: 1.0 */ gboolean clutter_text_position_to_coords (ClutterText *self, gint position, gfloat *x, gfloat *y, gfloat *line_height) { gfloat resource_scale; gboolean ret; g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) return FALSE; ret = clutter_text_position_to_coords_internal (self, position, x, y, line_height); if (x) *x /= resource_scale; if (y) *y /= resource_scale; if (line_height) *line_height /= resource_scale; return ret; } static inline void update_cursor_location (ClutterText *self) { ClutterTextPrivate *priv = self->priv; graphene_rect_t rect; float x, y; if (!priv->editable) return; rect = priv->cursor_rect; clutter_actor_get_transformed_position (CLUTTER_ACTOR (self), &x, &y); graphene_rect_offset (&rect, x, y); clutter_input_focus_set_cursor_location (priv->input_focus, &rect); } static inline void clutter_text_ensure_cursor_position (ClutterText *self, float scale) { ClutterTextPrivate *priv = self->priv; gfloat x, y, cursor_height; graphene_rect_t cursor_rect = GRAPHENE_RECT_INIT_ZERO; gint position; position = priv->position; if (priv->editable && priv->preedit_set) { if (position == -1) position = clutter_text_buffer_get_length (get_buffer (self)); position += priv->preedit_cursor_pos; } CLUTTER_NOTE (MISC, "Cursor at %d (preedit %s at pos: %d)", position, priv->preedit_set ? "set" : "unset", priv->preedit_set ? priv->preedit_cursor_pos : 0); x = y = cursor_height = 0; clutter_text_position_to_coords_internal (self, position, &x, &y, &cursor_height); graphene_rect_init (&cursor_rect, x, y + CURSOR_Y_PADDING * scale, priv->cursor_size * scale, cursor_height - 2 * CURSOR_Y_PADDING * scale); if (!graphene_rect_equal (&priv->cursor_rect, &cursor_rect)) { priv->cursor_rect = cursor_rect; g_signal_emit (self, text_signals[CURSOR_EVENT], 0, &cursor_rect); g_signal_emit (self, text_signals[CURSOR_CHANGED], 0); update_cursor_location (self); } } /** * clutter_text_delete_selection: * @self: a #ClutterText * * Deletes the currently selected text * * This function is only useful in subclasses of #ClutterText * * Return value: %TRUE if text was deleted or if the text actor * is empty, and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_text_delete_selection (ClutterText *self) { ClutterTextPrivate *priv; gint start_index; gint end_index; gint old_position, old_selection; guint n_chars; g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); priv = self->priv; n_chars = clutter_text_buffer_get_length (get_buffer (self)); if (n_chars == 0) return TRUE; start_index = priv->position == -1 ? n_chars : priv->position; end_index = priv->selection_bound == -1 ? n_chars : priv->selection_bound; if (end_index == start_index) return FALSE; if (end_index < start_index) { gint temp = start_index; start_index = end_index; end_index = temp; } old_position = priv->position; old_selection = priv->selection_bound; clutter_text_delete_text (self, start_index, end_index); priv->position = start_index; priv->selection_bound = start_index; /* Not required to be guarded by g_object_freeze/thaw_notify */ if (priv->position != old_position) { /* XXX:2.0 - remove */ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_POSITION]); } if (priv->selection_bound != old_selection) g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]); return TRUE; } /* * Utility function to update both cursor position and selection bound * at once */ static inline void clutter_text_set_positions (ClutterText *self, gint new_pos, gint new_bound) { g_object_freeze_notify (G_OBJECT (self)); clutter_text_set_cursor_position (self, new_pos); clutter_text_set_selection_bound (self, new_bound); g_object_thaw_notify (G_OBJECT (self)); } static inline void clutter_text_set_markup_internal (ClutterText *self, const gchar *str) { ClutterTextPrivate *priv = self->priv; GError *error; gchar *text = NULL; PangoAttrList *attrs = NULL; gboolean res; g_assert (str != NULL); error = NULL; res = pango_parse_markup (str, -1, 0, &attrs, &text, NULL, &error); if (!res) { if (G_LIKELY (error != NULL)) { g_warning ("Failed to set the markup of the actor '%s': %s", _clutter_actor_get_debug_name (CLUTTER_ACTOR (self)), error->message); g_error_free (error); } else g_warning ("Failed to set the markup of the actor '%s'", _clutter_actor_get_debug_name (CLUTTER_ACTOR (self))); return; } if (text) { clutter_text_buffer_set_text (get_buffer (self), text, -1); g_free (text); } /* Store the new markup attributes */ if (priv->markup_attrs != NULL) pango_attr_list_unref (priv->markup_attrs); priv->markup_attrs = attrs; /* Clear the effective attributes so they will be regenerated when a layout is created */ if (priv->effective_attrs != NULL) { pango_attr_list_unref (priv->effective_attrs); priv->effective_attrs = NULL; } } static void clutter_text_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterText *self = CLUTTER_TEXT (gobject); switch (prop_id) { case PROP_BUFFER: clutter_text_set_buffer (self, g_value_get_object (value)); break; case PROP_TEXT: { const char *str = g_value_get_string (value); if (self->priv->use_markup) clutter_text_set_markup_internal (self, str ? str : ""); else clutter_text_buffer_set_text (get_buffer (self), str ? str : "", -1); } break; case PROP_COLOR: clutter_text_set_color (self, clutter_value_get_color (value)); break; case PROP_FONT_NAME: clutter_text_set_font_name (self, g_value_get_string (value)); break; case PROP_FONT_DESCRIPTION: clutter_text_set_font_description (self, g_value_get_boxed (value)); break; case PROP_USE_MARKUP: clutter_text_set_use_markup (self, g_value_get_boolean (value)); break; case PROP_ATTRIBUTES: clutter_text_set_attributes (self, g_value_get_boxed (value)); break; case PROP_LINE_ALIGNMENT: clutter_text_set_line_alignment (self, g_value_get_enum (value)); break; case PROP_LINE_WRAP: clutter_text_set_line_wrap (self, g_value_get_boolean (value)); break; case PROP_LINE_WRAP_MODE: clutter_text_set_line_wrap_mode (self, g_value_get_enum (value)); break; case PROP_JUSTIFY: clutter_text_set_justify (self, g_value_get_boolean (value)); break; case PROP_ELLIPSIZE: clutter_text_set_ellipsize (self, g_value_get_enum (value)); break; case PROP_POSITION: /* XXX:2.0: remove */ case PROP_CURSOR_POSITION: clutter_text_set_cursor_position (self, g_value_get_int (value)); break; case PROP_SELECTION_BOUND: clutter_text_set_selection_bound (self, g_value_get_int (value)); break; case PROP_SELECTION_COLOR: clutter_text_set_selection_color (self, g_value_get_boxed (value)); break; case PROP_CURSOR_VISIBLE: clutter_text_set_cursor_visible (self, g_value_get_boolean (value)); break; case PROP_CURSOR_COLOR: clutter_text_set_cursor_color (self, g_value_get_boxed (value)); break; case PROP_CURSOR_SIZE: clutter_text_set_cursor_size (self, g_value_get_int (value)); break; case PROP_EDITABLE: clutter_text_set_editable (self, g_value_get_boolean (value)); break; case PROP_ACTIVATABLE: clutter_text_set_activatable (self, g_value_get_boolean (value)); break; case PROP_SELECTABLE: clutter_text_set_selectable (self, g_value_get_boolean (value)); break; case PROP_PASSWORD_CHAR: clutter_text_set_password_char (self, g_value_get_uint (value)); break; case PROP_MAX_LENGTH: clutter_text_set_max_length (self, g_value_get_int (value)); break; case PROP_SINGLE_LINE_MODE: clutter_text_set_single_line_mode (self, g_value_get_boolean (value)); break; case PROP_SELECTED_TEXT_COLOR: clutter_text_set_selected_text_color (self, clutter_value_get_color (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_text_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterText *self = CLUTTER_TEXT (gobject); ClutterTextPrivate *priv = self->priv; switch (prop_id) { case PROP_BUFFER: g_value_set_object (value, clutter_text_get_buffer (self)); break; case PROP_TEXT: g_value_set_string (value, clutter_text_buffer_get_text (get_buffer (self))); break; case PROP_FONT_NAME: g_value_set_string (value, priv->font_name); break; case PROP_FONT_DESCRIPTION: g_value_set_boxed (value, priv->font_desc); break; case PROP_USE_MARKUP: g_value_set_boolean (value, priv->use_markup); break; case PROP_COLOR: clutter_value_set_color (value, &priv->text_color); break; case PROP_CURSOR_VISIBLE: g_value_set_boolean (value, priv->cursor_visible); break; case PROP_CURSOR_COLOR: clutter_value_set_color (value, &priv->cursor_color); break; case PROP_CURSOR_COLOR_SET: g_value_set_boolean (value, priv->cursor_color_set); break; case PROP_CURSOR_SIZE: g_value_set_int (value, priv->cursor_size); break; case PROP_POSITION: /* XXX:2.0 - remove */ case PROP_CURSOR_POSITION: g_value_set_int (value, priv->position); break; case PROP_SELECTION_BOUND: g_value_set_int (value, priv->selection_bound); break; case PROP_EDITABLE: g_value_set_boolean (value, priv->editable); break; case PROP_SELECTABLE: g_value_set_boolean (value, priv->selectable); break; case PROP_SELECTION_COLOR: clutter_value_set_color (value, &priv->selection_color); break; case PROP_SELECTION_COLOR_SET: g_value_set_boolean (value, priv->selection_color_set); break; case PROP_ACTIVATABLE: g_value_set_boolean (value, priv->activatable); break; case PROP_PASSWORD_CHAR: g_value_set_uint (value, priv->password_char); break; case PROP_MAX_LENGTH: g_value_set_int (value, clutter_text_buffer_get_max_length (get_buffer (self))); break; case PROP_SINGLE_LINE_MODE: g_value_set_boolean (value, priv->single_line_mode); break; case PROP_ELLIPSIZE: g_value_set_enum (value, priv->ellipsize); break; case PROP_LINE_WRAP: g_value_set_boolean (value, priv->wrap); break; case PROP_LINE_WRAP_MODE: g_value_set_enum (value, priv->wrap_mode); break; case PROP_LINE_ALIGNMENT: g_value_set_enum (value, priv->alignment); break; case PROP_JUSTIFY: g_value_set_boolean (value, priv->justify); break; case PROP_ATTRIBUTES: g_value_set_boxed (value, priv->attrs); break; case PROP_SELECTED_TEXT_COLOR: clutter_value_set_color (value, &priv->selected_text_color); break; case PROP_SELECTED_TEXT_COLOR_SET: g_value_set_boolean (value, priv->selected_text_color_set); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_text_dispose (GObject *gobject) { ClutterText *self = CLUTTER_TEXT (gobject); ClutterTextPrivate *priv = self->priv; /* get rid of the entire cache */ clutter_text_dirty_cache (self); g_clear_signal_handler (&priv->direction_changed_id, self); g_clear_signal_handler (&priv->resource_scale_changed_id, self); g_clear_signal_handler (&priv->settings_changed_id, clutter_get_default_backend ()); g_clear_handle_id (&priv->password_hint_id, g_source_remove); clutter_text_set_buffer (self, NULL); G_OBJECT_CLASS (clutter_text_parent_class)->dispose (gobject); } static void clutter_text_finalize (GObject *gobject) { ClutterText *self = CLUTTER_TEXT (gobject); ClutterTextPrivate *priv = self->priv; if (priv->font_desc) pango_font_description_free (priv->font_desc); if (priv->attrs) pango_attr_list_unref (priv->attrs); if (priv->markup_attrs) pango_attr_list_unref (priv->markup_attrs); if (priv->effective_attrs) pango_attr_list_unref (priv->effective_attrs); if (priv->preedit_attrs) pango_attr_list_unref (priv->preedit_attrs); clutter_text_dirty_paint_volume (self); g_free (priv->font_name); g_clear_object (&priv->input_focus); G_OBJECT_CLASS (clutter_text_parent_class)->finalize (gobject); } typedef void (* ClutterTextSelectionFunc) (ClutterText *text, const ClutterActorBox *box, gpointer user_data); static void clutter_text_foreach_selection_rectangle (ClutterText *self, float scale, ClutterTextSelectionFunc func, gpointer user_data) { ClutterTextPrivate *priv = self->priv; PangoLayout *layout = clutter_text_get_layout (self); gchar *utf8 = clutter_text_get_display_text (self); gint lines; gint start_index; gint end_index; gint line_no; if (priv->position == 0) start_index = 0; else start_index = offset_to_bytes (utf8, priv->position); if (priv->selection_bound == 0) end_index = 0; else end_index = offset_to_bytes (utf8, priv->selection_bound); if (start_index > end_index) { gint temp = start_index; start_index = end_index; end_index = temp; } lines = pango_layout_get_line_count (layout); for (line_no = 0; line_no < lines; line_no++) { PangoLayoutLine *line; gint n_ranges; gint *ranges; gint i; gint index_; gint maxindex; ClutterActorBox box; gfloat y, height; line = pango_layout_get_line_readonly (layout, line_no); pango_layout_line_x_to_index (line, G_MAXINT, &maxindex, NULL); if (maxindex < start_index) continue; pango_layout_line_get_x_ranges (line, start_index, end_index, &ranges, &n_ranges); pango_layout_line_x_to_index (line, 0, &index_, NULL); clutter_text_position_to_coords_internal (self, bytes_to_offset (utf8, index_), NULL, &y, &height); box.y1 = y; box.y2 = y + height; for (i = 0; i < n_ranges; i++) { gfloat range_x; gfloat range_width; range_x = pango_to_pixels (ranges[i * 2]); /* Account for any scrolling in single line mode */ if (priv->single_line_mode) range_x += priv->text_x; range_width = pango_to_pixels (ranges[i * 2 + 1] - ranges[i * 2]); box.x1 = range_x; box.x2 = ceilf (range_x + range_width); clutter_actor_box_scale (&box, scale); func (self, &box, user_data); } g_free (ranges); } g_free (utf8); } static void add_selection_rectangle_to_path (ClutterText *text, const ClutterActorBox *box, gpointer user_data) { cogl_path_rectangle (user_data, box->x1, box->y1, box->x2, box->y2); } static void clutter_text_foreach_selection_rectangle_prescaled (ClutterText *self, ClutterTextSelectionFunc func, gpointer user_data) { clutter_text_foreach_selection_rectangle (self, 1.0f, func, user_data); } /* Draws the selected text, its background, and the cursor */ static void selection_paint (ClutterText *self, CoglFramebuffer *fb) { ClutterTextPrivate *priv = self->priv; ClutterActor *actor = CLUTTER_ACTOR (self); guint8 paint_opacity = clutter_actor_get_paint_opacity (actor); const ClutterColor *color; if (!clutter_text_should_draw_cursor (self)) return; if (priv->position == priv->selection_bound) { CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline); CoglColor cogl_color; /* No selection, just draw the cursor */ if (priv->cursor_color_set) color = &priv->cursor_color; else color = &priv->text_color; cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, paint_opacity * color->alpha / 255); cogl_color_premultiply (&cogl_color); cogl_pipeline_set_color (color_pipeline, &cogl_color); cogl_framebuffer_draw_rectangle (fb, color_pipeline, priv->cursor_rect.origin.x, priv->cursor_rect.origin.y, priv->cursor_rect.origin.x + priv->cursor_rect.size.width, priv->cursor_rect.origin.y + priv->cursor_rect.size.height); } else { /* Paint selection background first */ CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline); PangoLayout *layout = clutter_text_get_layout (self); CoglPath *selection_path = cogl_path_new (); CoglColor cogl_color = { 0, }; /* Paint selection background */ if (priv->selection_color_set) color = &priv->selection_color; else if (priv->cursor_color_set) color = &priv->cursor_color; else color = &priv->text_color; cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, paint_opacity * color->alpha / 255); cogl_color_premultiply (&cogl_color); cogl_pipeline_set_color (color_pipeline, &cogl_color); clutter_text_foreach_selection_rectangle_prescaled (self, add_selection_rectangle_to_path, selection_path); cogl_framebuffer_fill_path (fb, color_pipeline, selection_path); /* Paint selected text */ cogl_framebuffer_push_path_clip (fb, selection_path); cogl_object_unref (selection_path); if (priv->selected_text_color_set) color = &priv->selected_text_color; else color = &priv->text_color; cogl_color_init_from_4ub (&cogl_color, color->red, color->green, color->blue, paint_opacity * color->alpha / 255); cogl_pango_show_layout (fb, layout, priv->text_x, 0, &cogl_color); cogl_framebuffer_pop_clip (fb); } } static gint clutter_text_move_word_backward (ClutterText *self, gint start) { gint retval = start; if (clutter_text_buffer_get_length (get_buffer (self)) > 0 && start > 0) { PangoLayout *layout = clutter_text_get_layout (self); PangoLogAttr *log_attrs = NULL; gint n_attrs = 0; pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); retval = start - 1; while (retval > 0 && !log_attrs[retval].is_word_start) retval -= 1; g_free (log_attrs); } return retval; } static gint clutter_text_move_word_forward (ClutterText *self, gint start) { gint retval = start; guint n_chars; n_chars = clutter_text_buffer_get_length (get_buffer (self)); if (n_chars > 0 && start < n_chars) { PangoLayout *layout = clutter_text_get_layout (self); PangoLogAttr *log_attrs = NULL; gint n_attrs = 0; pango_layout_get_log_attrs (layout, &log_attrs, &n_attrs); retval = start + 1; while (retval < n_chars && !log_attrs[retval].is_word_end) retval += 1; g_free (log_attrs); } return retval; } static gint clutter_text_move_line_start (ClutterText *self, gint start) { PangoLayoutLine *layout_line; PangoLayout *layout; gint line_no; gint index_; gint position; const gchar *text; layout = clutter_text_get_layout (self); text = clutter_text_buffer_get_text (get_buffer (self)); if (start == 0) index_ = 0; else index_ = offset_to_bytes (text, start); pango_layout_index_to_line_x (layout, index_, 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly (layout, line_no); if (!layout_line) return FALSE; pango_layout_line_x_to_index (layout_line, 0, &index_, NULL); position = bytes_to_offset (text, index_); return position; } static gint clutter_text_move_line_end (ClutterText *self, gint start) { ClutterTextPrivate *priv = self->priv; PangoLayoutLine *layout_line; PangoLayout *layout; gint line_no; gint index_; gint trailing; gint position; const gchar *text; layout = clutter_text_get_layout (self); text = clutter_text_buffer_get_text (get_buffer (self)); if (start == 0) index_ = 0; else index_ = offset_to_bytes (text, priv->position); pango_layout_index_to_line_x (layout, index_, 0, &line_no, NULL); layout_line = pango_layout_get_line_readonly (layout, line_no); if (!layout_line) return FALSE; pango_layout_line_x_to_index (layout_line, G_MAXINT, &index_, &trailing); index_ += trailing; position = bytes_to_offset (text, index_); return position; } static void clutter_text_select_word (ClutterText *self) { gint cursor_pos = self->priv->position; gint start_pos, end_pos; start_pos = clutter_text_move_word_backward (self, cursor_pos); end_pos = clutter_text_move_word_forward (self, cursor_pos); clutter_text_set_selection (self, start_pos, end_pos); } static void clutter_text_select_line (ClutterText *self) { ClutterTextPrivate *priv = self->priv; gint cursor_pos = priv->position; gint start_pos, end_pos; if (priv->single_line_mode) { start_pos = 0; end_pos = -1; } else { start_pos = clutter_text_move_line_start (self, cursor_pos); end_pos = clutter_text_move_line_end (self, cursor_pos); } clutter_text_set_selection (self, start_pos, end_pos); } static gboolean clutter_text_press (ClutterActor *actor, ClutterEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; ClutterEventType type = clutter_event_type (event); gboolean res = FALSE; gfloat x, y; gint index_; /* if a ClutterText is just used for display purposes, then we * should ignore the events we receive */ if (!(priv->editable || priv->selectable)) return CLUTTER_EVENT_PROPAGATE; clutter_actor_grab_key_focus (actor); clutter_input_focus_set_input_panel_state (priv->input_focus, CLUTTER_INPUT_PANEL_STATE_TOGGLE); /* if the actor is empty we just reset everything and not * set up the dragging of the selection since there's nothing * to select */ if (clutter_text_buffer_get_length (get_buffer (self)) == 0) { clutter_text_set_positions (self, -1, -1); return CLUTTER_EVENT_STOP; } clutter_event_get_coords (event, &x, &y); res = clutter_actor_transform_stage_point (actor, x, y, &x, &y); if (res) { const char *text; int offset; index_ = clutter_text_coords_to_position (self, x, y); text = clutter_text_buffer_get_text (get_buffer (self)); offset = bytes_to_offset (text, index_); /* what we select depends on the number of button clicks we * receive, and whether we are selectable: * * 1: just position the cursor and the selection * 2: select the current word * 3: select the contents of the whole actor */ if (type == CLUTTER_BUTTON_PRESS) { gint click_count = clutter_event_get_click_count (event); if (click_count == 1) { clutter_text_set_positions (self, offset, offset); } else if (priv->selectable && click_count == 2) { clutter_text_select_word (self); } else if (priv->selectable && click_count == 3) { clutter_text_select_line (self); } } else { /* touch events do not have click count */ clutter_text_set_positions (self, offset, offset); } } /* we don't need to go any further if we're not selectable */ if (!priv->selectable) return CLUTTER_EVENT_STOP; /* grab the pointer */ priv->in_select_drag = TRUE; if (type == CLUTTER_BUTTON_PRESS) { clutter_input_device_grab (clutter_event_get_device (event), actor); } else { clutter_input_device_sequence_grab (clutter_event_get_device (event), clutter_event_get_event_sequence (event), actor); priv->in_select_touch = TRUE; } return CLUTTER_EVENT_STOP; } static gboolean clutter_text_move (ClutterActor *actor, ClutterEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; gfloat x, y; gint index_, offset; gboolean res; const gchar *text; if (!priv->in_select_drag) return CLUTTER_EVENT_PROPAGATE; clutter_event_get_coords (event, &x, &y); res = clutter_actor_transform_stage_point (actor, x, y, &x, &y); if (!res) return CLUTTER_EVENT_PROPAGATE; index_ = clutter_text_coords_to_position (self, x, y); text = clutter_text_buffer_get_text (get_buffer (self)); offset = bytes_to_offset (text, index_); if (priv->selectable) clutter_text_set_cursor_position (self, offset); else clutter_text_set_positions (self, offset, offset); return CLUTTER_EVENT_STOP; } static gboolean clutter_text_release (ClutterActor *actor, ClutterEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; ClutterEventType type = clutter_event_type (event); if (priv->in_select_drag) { if (type == CLUTTER_BUTTON_RELEASE) { if (!priv->in_select_touch) { clutter_input_device_ungrab (clutter_event_get_device (event)); priv->in_select_drag = FALSE; return CLUTTER_EVENT_STOP; } } else { if (priv->in_select_touch) { ClutterInputDevice *device = clutter_event_get_device (event); ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); clutter_input_device_sequence_ungrab (device, sequence); priv->in_select_touch = FALSE; priv->in_select_drag = FALSE; return CLUTTER_EVENT_STOP; } } } return CLUTTER_EVENT_PROPAGATE; } static gboolean clutter_text_button_press (ClutterActor *actor, ClutterButtonEvent *event) { return clutter_text_press (actor, (ClutterEvent *) event); } static gboolean clutter_text_motion (ClutterActor *actor, ClutterMotionEvent *event) { return clutter_text_move (actor, (ClutterEvent *) event); } static gboolean clutter_text_button_release (ClutterActor *actor, ClutterButtonEvent *event) { return clutter_text_release (actor, (ClutterEvent *) event); } static gboolean clutter_text_touch_event (ClutterActor *actor, ClutterTouchEvent *event) { switch (event->type) { case CLUTTER_TOUCH_BEGIN: return clutter_text_press (actor, (ClutterEvent *) event); case CLUTTER_TOUCH_END: case CLUTTER_TOUCH_CANCEL: /* TODO: the cancel case probably need a special handler */ return clutter_text_release (actor, (ClutterEvent *) event); case CLUTTER_TOUCH_UPDATE: return clutter_text_move (actor, (ClutterEvent *) event); default: break; } return CLUTTER_EVENT_PROPAGATE; } static gboolean clutter_text_remove_password_hint (gpointer data) { ClutterText *self = data; self->priv->password_hint_visible = FALSE; self->priv->password_hint_id = 0; clutter_text_dirty_cache (data); clutter_text_queue_redraw (data); return G_SOURCE_REMOVE; } static gboolean clutter_text_key_press (ClutterActor *actor, ClutterKeyEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; ClutterBindingPool *pool; gboolean res; if (!priv->editable) return CLUTTER_EVENT_PROPAGATE; /* we need to use the ClutterText type name to find our own * key bindings; subclasses will override or chain up this * event handler, so they can do whatever they want there */ pool = clutter_binding_pool_find (g_type_name (CLUTTER_TYPE_TEXT)); g_assert (pool != NULL); if (!(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD) && clutter_input_focus_is_focused (priv->input_focus) && clutter_input_focus_filter_event (priv->input_focus, (ClutterEvent *) event)) return CLUTTER_EVENT_STOP; /* we allow passing synthetic events that only contain * the Unicode value and not the key symbol, unless they * contain the input method flag. */ if (event->keyval == 0 && (event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC) && !(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD)) res = FALSE; else res = clutter_binding_pool_activate (pool, event->keyval, event->modifier_state, G_OBJECT (actor)); /* if the key binding has handled the event we bail out * as fast as we can; otherwise, we try to insert the * Unicode character inside the key event into the text * actor */ if (res) return CLUTTER_EVENT_STOP; else if ((event->modifier_state & CLUTTER_CONTROL_MASK) == 0) { gunichar key_unichar; /* Skip keys when control is pressed */ key_unichar = clutter_event_get_key_unicode ((ClutterEvent *) event); /* return is reported as CR, but we want LF */ if (key_unichar == '\r') key_unichar = '\n'; if ((key_unichar == '\n' && !priv->single_line_mode) || (g_unichar_validate (key_unichar) && !g_unichar_iscntrl (key_unichar))) { /* truncate the eventual selection so that the * Unicode character can replace it */ clutter_text_delete_selection (self); clutter_text_insert_unichar (self, key_unichar); if (priv->show_password_hint) { g_clear_handle_id (&priv->password_hint_id, g_source_remove); priv->password_hint_visible = TRUE; priv->password_hint_id = clutter_threads_add_timeout (priv->password_hint_timeout, clutter_text_remove_password_hint, self); } return CLUTTER_EVENT_STOP; } } return CLUTTER_EVENT_PROPAGATE; } static gboolean clutter_text_key_release (ClutterActor *actor, ClutterKeyEvent *event) { ClutterText *self = CLUTTER_TEXT (actor); ClutterTextPrivate *priv = self->priv; if (clutter_input_focus_is_focused (priv->input_focus) && clutter_input_focus_filter_event (priv->input_focus, (ClutterEvent *) event)) return CLUTTER_EVENT_STOP; return CLUTTER_EVENT_PROPAGATE; } static void clutter_text_compute_layout_offsets (ClutterText *self, PangoLayout *layout, const ClutterActorBox *alloc, int *text_x, int *text_y) { ClutterActor *actor = CLUTTER_ACTOR (self); ClutterActorAlign x_align, y_align; PangoRectangle logical_rect; float alloc_width, alloc_height; float x, y; clutter_actor_box_get_size (alloc, &alloc_width, &alloc_height); pango_layout_get_pixel_extents (layout, NULL, &logical_rect); if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_HORIZONTAL)) x_align = _clutter_actor_get_effective_x_align (actor); else x_align = CLUTTER_ACTOR_ALIGN_FILL; if (clutter_actor_needs_expand (actor, CLUTTER_ORIENTATION_VERTICAL)) y_align = clutter_actor_get_y_align (actor); else y_align = CLUTTER_ACTOR_ALIGN_FILL; x = 0.f; switch (x_align) { case CLUTTER_ACTOR_ALIGN_FILL: case CLUTTER_ACTOR_ALIGN_START: break; case CLUTTER_ACTOR_ALIGN_END: if (alloc_width > logical_rect.width) x = alloc_width - logical_rect.width; break; case CLUTTER_ACTOR_ALIGN_CENTER: if (alloc_width > logical_rect.width) x = (alloc_width - logical_rect.width) / 2.f; break; } y = 0.f; switch (y_align) { case CLUTTER_ACTOR_ALIGN_FILL: case CLUTTER_ACTOR_ALIGN_START: break; case CLUTTER_ACTOR_ALIGN_END: if (alloc_height > logical_rect.height) y = alloc_height - logical_rect.height; break; case CLUTTER_ACTOR_ALIGN_CENTER: if (alloc_height > logical_rect.height) y = (alloc_height - logical_rect.height) / 2.f; break; } if (text_x != NULL) *text_x = floorf (x); if (text_y != NULL) *text_y = floorf (y); } #define TEXT_PADDING 2 static void clutter_text_paint (ClutterActor *self, ClutterPaintContext *paint_context) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; CoglFramebuffer *fb; PangoLayout *layout; ClutterActorBox alloc = { 0, }; CoglColor color = { 0, }; guint8 real_opacity; gint text_x = priv->text_x; gint text_y = priv->text_y; gboolean clip_set = FALSE; gboolean bg_color_set = FALSE; guint n_chars; float alloc_width; float alloc_height; float resource_scale; fb = clutter_paint_context_get_framebuffer (paint_context); /* Note that if anything in this paint method changes it needs to be reflected in the get_paint_volume implementation which is tightly tied to the workings of this function */ n_chars = clutter_text_buffer_get_length (get_buffer (text)); clutter_actor_get_allocation_box (self, &alloc); if (G_UNLIKELY (default_color_pipeline == NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); default_color_pipeline = cogl_pipeline_new (ctx); } g_assert (default_color_pipeline != NULL); g_object_get (self, "background-color-set", &bg_color_set, NULL); if (bg_color_set) { CoglPipeline *color_pipeline = cogl_pipeline_copy (default_color_pipeline); ClutterColor bg_color; clutter_actor_get_background_color (self, &bg_color); bg_color.alpha = clutter_actor_get_paint_opacity (self) * bg_color.alpha / 255; cogl_color_init_from_4ub (&color, bg_color.red, bg_color.green, bg_color.blue, bg_color.alpha); cogl_color_premultiply (&color); cogl_pipeline_set_color (color_pipeline, &color); cogl_framebuffer_draw_rectangle (fb, color_pipeline, 0, 0, clutter_actor_box_get_width (&alloc), clutter_actor_box_get_height (&alloc)); cogl_object_unref (color_pipeline); } /* don't bother painting an empty text actor, unless it's * editable, in which case we want to paint at least the * cursor */ if (n_chars == 0 && !clutter_text_should_draw_cursor (text)) return; if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (self), &resource_scale)) return; clutter_actor_box_scale (&alloc, resource_scale); clutter_actor_box_get_size (&alloc, &alloc_width, &alloc_height); if (priv->editable && priv->single_line_mode) layout = clutter_text_create_layout (text, -1, -1); else { /* the only time when we create the PangoLayout using the full * width and height of the allocation is when we can both wrap * and ellipsize */ if (priv->wrap && priv->ellipsize) { layout = clutter_text_create_layout (text, alloc_width, alloc_height); } else { /* if we're not wrapping we cannot set the height of the * layout, otherwise Pango will happily wrap the text to * fit in the rectangle - thus making the :wrap property * useless * * see bug: * * http://bugzilla.clutter-project.org/show_bug.cgi?id=2339 * * in order to fix this, we create a layout that would fit * in the assigned width, then we clip the actor if the * logical rectangle overflows the allocation. */ layout = clutter_text_create_layout (text, alloc_width, -1); } } if (resource_scale != 1.0f) { float paint_scale = 1.0f / resource_scale; cogl_framebuffer_push_matrix (fb); cogl_framebuffer_scale (fb, paint_scale, paint_scale, 1.0f); } if (clutter_text_should_draw_cursor (text)) clutter_text_ensure_cursor_position (text, resource_scale); if (priv->editable && priv->single_line_mode) { PangoRectangle logical_rect = { 0, }; gint actor_width, text_width; gboolean rtl; pango_layout_get_extents (layout, NULL, &logical_rect); cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height); clip_set = TRUE; actor_width = alloc_width - 2 * TEXT_PADDING; text_width = pango_to_pixels (logical_rect.width); rtl = priv->resolved_direction == PANGO_DIRECTION_RTL; if (actor_width < text_width) { gint cursor_x = graphene_rect_get_x (&priv->cursor_rect); if (priv->position == -1) { text_x = rtl ? TEXT_PADDING : actor_width - text_width; } else if (priv->position == 0) { text_x = rtl ? actor_width - text_width : TEXT_PADDING; } else { if (cursor_x < 0) { text_x = text_x - cursor_x - TEXT_PADDING; } else if (cursor_x > actor_width) { text_x = text_x + (actor_width - cursor_x) - TEXT_PADDING; } } } else { text_x = rtl ? actor_width - text_width : TEXT_PADDING; } } else if (!priv->editable && !(priv->wrap && priv->ellipsize)) { PangoRectangle logical_rect = { 0, }; pango_layout_get_pixel_extents (layout, NULL, &logical_rect); /* don't clip if the layout managed to fit inside our allocation */ if (logical_rect.width > alloc_width || logical_rect.height > alloc_height) { cogl_framebuffer_push_rectangle_clip (fb, 0, 0, alloc_width, alloc_height); clip_set = TRUE; } clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y); } else clutter_text_compute_layout_offsets (text, layout, &alloc, &text_x, &text_y); if (priv->text_x != text_x || priv->text_y != text_y) { priv->text_x = text_x; priv->text_y = text_y; priv->text_logical_x = roundf ((float) text_x / resource_scale); priv->text_logical_y = roundf ((float) text_y / resource_scale); clutter_text_ensure_cursor_position (text, resource_scale); } real_opacity = clutter_actor_get_paint_opacity (self) * priv->text_color.alpha / 255; CLUTTER_NOTE (PAINT, "painting text (text: '%s')", clutter_text_buffer_get_text (get_buffer (text))); cogl_color_init_from_4ub (&color, priv->text_color.red, priv->text_color.green, priv->text_color.blue, real_opacity); cogl_pango_show_layout (fb, layout, priv->text_x, priv->text_y, &color); selection_paint (text, fb); if (resource_scale != 1.0f) cogl_framebuffer_pop_matrix (fb); if (clip_set) cogl_framebuffer_pop_clip (fb); } static void add_selection_to_paint_volume (ClutterText *text, const ClutterActorBox *box, gpointer user_data) { ClutterPaintVolume *total_volume = user_data; ClutterPaintVolume rect_volume; graphene_point3d_t vertex; _clutter_paint_volume_init_static (&rect_volume, CLUTTER_ACTOR (text)); vertex.x = box->x1; vertex.y = box->y1; vertex.z = 0.0f; clutter_paint_volume_set_origin (&rect_volume, &vertex); clutter_paint_volume_set_width (&rect_volume, box->x2 - box->x1); clutter_paint_volume_set_height (&rect_volume, box->y2 - box->y1); clutter_paint_volume_union (total_volume, &rect_volume); clutter_paint_volume_free (&rect_volume); } static void clutter_text_get_paint_volume_for_cursor (ClutterText *text, float resource_scale, ClutterPaintVolume *volume) { ClutterTextPrivate *priv = text->priv; graphene_point3d_t origin; clutter_text_ensure_cursor_position (text, resource_scale); if (priv->position == priv->selection_bound) { float width, height; width = priv->cursor_rect.size.width / resource_scale; height = priv->cursor_rect.size.height / resource_scale; origin.x = priv->cursor_rect.origin.x / resource_scale; origin.y = priv->cursor_rect.origin.y / resource_scale; origin.z = 0; clutter_paint_volume_set_origin (volume, &origin); clutter_paint_volume_set_width (volume, width); clutter_paint_volume_set_height (volume, height); } else { clutter_text_foreach_selection_rectangle (text, 1.0f / resource_scale, add_selection_to_paint_volume, volume); } } static gboolean clutter_text_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; /* ClutterText uses the logical layout as the natural size of the actor. This means that it can sometimes paint outside of its allocation for example with italic fonts with serifs. Therefore we should use the ink rectangle of the layout instead */ if (!priv->paint_volume_valid) { PangoLayout *layout; PangoRectangle ink_rect; graphene_point3d_t origin; float resource_scale; /* If the text is single line editable then it gets clipped to the allocation anyway so we can just use that */ if (priv->editable && priv->single_line_mode) return _clutter_actor_set_default_paint_volume (self, CLUTTER_TYPE_TEXT, volume); if (G_OBJECT_TYPE (self) != CLUTTER_TYPE_TEXT) return FALSE; if (!clutter_actor_has_allocation (self)) return FALSE; if (!clutter_actor_get_resource_scale (self, &resource_scale)) return FALSE; _clutter_paint_volume_init_static (&priv->paint_volume, self); layout = clutter_text_get_layout (text); pango_layout_get_extents (layout, &ink_rect, NULL); origin.x = pango_to_logical_pixels (ink_rect.x, resource_scale); origin.y = pango_to_logical_pixels (ink_rect.y, resource_scale); origin.z = 0; clutter_paint_volume_set_origin (&priv->paint_volume, &origin); clutter_paint_volume_set_width (&priv->paint_volume, pango_to_logical_pixels (ink_rect.width, resource_scale)); clutter_paint_volume_set_height (&priv->paint_volume, pango_to_logical_pixels (ink_rect.height, resource_scale)); /* If the cursor is visible then that will likely be drawn outside of the ink rectangle so we should merge that in */ if (clutter_text_should_draw_cursor (text)) { ClutterPaintVolume cursor_paint_volume; _clutter_paint_volume_init_static (&cursor_paint_volume, self); clutter_text_get_paint_volume_for_cursor (text, resource_scale, &cursor_paint_volume); clutter_paint_volume_union (&priv->paint_volume, &cursor_paint_volume); clutter_paint_volume_free (&cursor_paint_volume); } priv->paint_volume_valid = TRUE; } _clutter_paint_volume_copy_static (&priv->paint_volume, volume); return TRUE; } static void clutter_text_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; PangoRectangle logical_rect = { 0, }; PangoLayout *layout; gint logical_width; gfloat layout_width; gfloat resource_scale; if (!clutter_actor_get_resource_scale (self, &resource_scale)) resource_scale = 1; layout = clutter_text_create_layout (text, -1, -1); pango_layout_get_extents (layout, NULL, &logical_rect); /* the X coordinate of the logical rectangle might be non-zero * according to the Pango documentation; hence, we need to offset * the width accordingly */ logical_width = logical_rect.x + logical_rect.width; layout_width = logical_width > 0 ? pango_to_logical_pixels (logical_width, resource_scale) : 1; if (min_width_p) { if (priv->wrap || priv->ellipsize || priv->editable) *min_width_p = 1; else *min_width_p = layout_width; } if (natural_width_p) { if (priv->editable && priv->single_line_mode) *natural_width_p = layout_width + TEXT_PADDING * 2; else *natural_width_p = layout_width; } } static void clutter_text_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv; if (for_width == 0) { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } else { PangoLayout *layout; PangoRectangle logical_rect = { 0, }; gint logical_height; gfloat layout_height; gfloat resource_scale; if (!clutter_actor_get_resource_scale (self, &resource_scale)) resource_scale = 1; if (priv->single_line_mode) for_width = -1; layout = create_text_layout_with_scale (CLUTTER_TEXT (self), for_width, -1, resource_scale); pango_layout_get_extents (layout, NULL, &logical_rect); /* the Y coordinate of the logical rectangle might be non-zero * according to the Pango documentation; hence, we need to offset * the height accordingly */ logical_height = logical_rect.y + logical_rect.height; layout_height = pango_to_logical_pixels (logical_height, resource_scale); if (min_height_p) { /* if we wrap and ellipsize then the minimum height is * going to be at least the size of the first line */ if ((priv->ellipsize && priv->wrap) && !priv->single_line_mode) { PangoLayoutLine *line; gfloat line_height; line = pango_layout_get_line_readonly (layout, 0); pango_layout_line_get_extents (line, NULL, &logical_rect); logical_height = logical_rect.y + logical_rect.height; line_height = pango_to_logical_pixels (logical_height, resource_scale); *min_height_p = line_height; } else *min_height_p = layout_height; } if (natural_height_p) *natural_height_p = layout_height; } } static void clutter_text_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterText *text = CLUTTER_TEXT (self); ClutterActorClass *parent_class; /* Ensure that there is a cached layout with the right width so * that we don't need to create the text during the paint run * * if the Text is editable and in single line mode we don't want * to have any limit on the layout size, since the paint will clip * it to the allocation of the actor */ if (text->priv->editable && text->priv->single_line_mode) clutter_text_create_layout (text, -1, -1); else maybe_create_text_layout_with_resource_scale (text, box->x2 - box->x1, box->y2 - box->y1); parent_class = CLUTTER_ACTOR_CLASS (clutter_text_parent_class); parent_class->allocate (self, box, flags); } static gboolean clutter_text_has_overlaps (ClutterActor *self) { return clutter_text_should_draw_cursor ((ClutterText *) self); } static gboolean clutter_text_event (ClutterActor *self, ClutterEvent *event) { ClutterText *text = CLUTTER_TEXT (self); ClutterTextPrivate *priv = text->priv; if (clutter_input_focus_is_focused (priv->input_focus) && (event->type == CLUTTER_IM_COMMIT || event->type == CLUTTER_IM_DELETE || event->type == CLUTTER_IM_PREEDIT)) { return clutter_input_focus_filter_event (priv->input_focus, event); } return CLUTTER_EVENT_PROPAGATE; } static void clutter_text_im_focus (ClutterText *text) { ClutterTextPrivate *priv = text->priv; ClutterBackend *backend = clutter_get_default_backend (); ClutterInputMethod *method = clutter_backend_get_input_method (backend); if (!method) return; clutter_input_method_focus_in (method, priv->input_focus); clutter_input_focus_set_content_purpose (priv->input_focus, priv->input_purpose); clutter_input_focus_set_content_hints (priv->input_focus, priv->input_hints); clutter_input_focus_set_can_show_preedit (priv->input_focus, TRUE); update_cursor_location (text); } static void clutter_text_key_focus_in (ClutterActor *actor) { ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; if (priv->editable) clutter_text_im_focus (CLUTTER_TEXT (actor)); priv->has_focus = TRUE; clutter_text_queue_redraw (actor); } static void clutter_text_key_focus_out (ClutterActor *actor) { ClutterTextPrivate *priv = CLUTTER_TEXT (actor)->priv; ClutterBackend *backend = clutter_get_default_backend (); ClutterInputMethod *method = clutter_backend_get_input_method (backend); priv->has_focus = FALSE; if (priv->editable && clutter_input_focus_is_focused (priv->input_focus)) { clutter_text_set_preedit_string (CLUTTER_TEXT (actor), NULL, NULL, 0); clutter_input_method_focus_out (method); } clutter_text_queue_redraw (actor); } static gboolean clutter_text_real_move_left (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos = priv->position; gint new_pos = 0; gint len; len = clutter_text_buffer_get_length (get_buffer (self)); g_object_freeze_notify (G_OBJECT (self)); if (pos != 0 && len != 0) { if (modifiers & CLUTTER_CONTROL_MASK) { if (pos == -1) new_pos = clutter_text_move_word_backward (self, len); else new_pos = clutter_text_move_word_backward (self, pos); } else { if (pos == -1) new_pos = len - 1; else new_pos = pos - 1; } clutter_text_set_cursor_position (self, new_pos); } if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_move_right (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos = priv->position; gint len = clutter_text_buffer_get_length (get_buffer (self)); gint new_pos = 0; g_object_freeze_notify (G_OBJECT (self)); if (pos != -1 && len !=0) { if (modifiers & CLUTTER_CONTROL_MASK) { if (pos != len) new_pos = clutter_text_move_word_forward (self, pos); } else { if (pos != len) new_pos = pos + 1; } clutter_text_set_cursor_position (self, new_pos); } if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_move_up (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; PangoLayoutLine *layout_line; PangoLayout *layout; gint line_no; gint index_, trailing; gint pos; gint x; const gchar *text; layout = clutter_text_get_layout (self); text = clutter_text_buffer_get_text (get_buffer (self)); if (priv->position == 0) index_ = 0; else index_ = offset_to_bytes (text, priv->position); pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); line_no -= 1; if (line_no < 0) return FALSE; if (priv->x_pos != -1) x = priv->x_pos; layout_line = pango_layout_get_line_readonly (layout, line_no); if (!layout_line) return FALSE; pango_layout_line_x_to_index (layout_line, x, &index_, &trailing); g_object_freeze_notify (G_OBJECT (self)); pos = bytes_to_offset (text, index_); clutter_text_set_cursor_position (self, pos + trailing); /* Store the target x position to avoid drifting left and right when moving the cursor up and down */ priv->x_pos = x; if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_move_down (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; PangoLayoutLine *layout_line; PangoLayout *layout; gint line_no; gint index_, trailing; gint x; gint pos; const gchar *text; layout = clutter_text_get_layout (self); text = clutter_text_buffer_get_text (get_buffer (self)); if (priv->position == 0) index_ = 0; else index_ = offset_to_bytes (text, priv->position); pango_layout_index_to_line_x (layout, index_, 0, &line_no, &x); if (priv->x_pos != -1) x = priv->x_pos; layout_line = pango_layout_get_line_readonly (layout, line_no + 1); if (!layout_line) return FALSE; pango_layout_line_x_to_index (layout_line, x, &index_, &trailing); g_object_freeze_notify (G_OBJECT (self)); pos = bytes_to_offset (text, index_); clutter_text_set_cursor_position (self, pos + trailing); /* Store the target x position to avoid drifting left and right when moving the cursor up and down */ priv->x_pos = x; if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_line_start (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint position; g_object_freeze_notify (G_OBJECT (self)); position = clutter_text_move_line_start (self, priv->position); clutter_text_set_cursor_position (self, position); if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_line_end (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint position; g_object_freeze_notify (G_OBJECT (self)); position = clutter_text_move_line_end (self, priv->position); clutter_text_set_cursor_position (self, position); if (!(priv->selectable && (modifiers & CLUTTER_SHIFT_MASK))) clutter_text_clear_selection (self); g_object_thaw_notify (G_OBJECT (self)); return TRUE; } static gboolean clutter_text_real_select_all (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { guint n_chars = clutter_text_buffer_get_length (get_buffer (self)); clutter_text_set_positions (self, 0, n_chars); return TRUE; } static gboolean clutter_text_real_del_next (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos; gint len; if (clutter_text_delete_selection (self)) return TRUE; pos = priv->position; len = clutter_text_buffer_get_length (get_buffer (self)); if (len && pos != -1 && pos < len) clutter_text_delete_text (self, pos, pos + 1); return TRUE; } static gboolean clutter_text_real_del_word_next (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos; gint len; pos = priv->position; len = clutter_text_buffer_get_length (get_buffer (self)); if (len && pos != -1 && pos < len) { gint end; end = clutter_text_move_word_forward (self, pos); clutter_text_delete_text (self, pos, end); if (priv->selection_bound >= end) { gint new_bound; new_bound = priv->selection_bound - (end - pos); clutter_text_set_selection_bound (self, new_bound); } else if (priv->selection_bound > pos) { clutter_text_set_selection_bound (self, pos); } } return TRUE; } static gboolean clutter_text_real_del_prev (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos; gint len; if (clutter_text_delete_selection (self)) return TRUE; pos = priv->position; len = clutter_text_buffer_get_length (get_buffer (self)); if (pos != 0 && len != 0) { if (pos == -1) { clutter_text_delete_text (self, len - 1, len); clutter_text_set_positions (self, -1, -1); } else { clutter_text_delete_text (self, pos - 1, pos); clutter_text_set_positions (self, pos - 1, pos - 1); } } return TRUE; } static gboolean clutter_text_real_del_word_prev (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { ClutterTextPrivate *priv = self->priv; gint pos; gint len; pos = priv->position; len = clutter_text_buffer_get_length (get_buffer (self)); if (pos != 0 && len != 0) { gint new_pos; if (pos == -1) { new_pos = clutter_text_move_word_backward (self, len); clutter_text_delete_text (self, new_pos, len); clutter_text_set_positions (self, -1, -1); } else { new_pos = clutter_text_move_word_backward (self, pos); clutter_text_delete_text (self, new_pos, pos); clutter_text_set_cursor_position (self, new_pos); if (priv->selection_bound >= pos) { gint new_bound; new_bound = priv->selection_bound - (pos - new_pos); clutter_text_set_selection_bound (self, new_bound); } else if (priv->selection_bound >= new_pos) { clutter_text_set_selection_bound (self, new_pos); } } } return TRUE; } static gboolean clutter_text_real_activate (ClutterText *self, const gchar *action, guint keyval, ClutterModifierType modifiers) { return clutter_text_activate (self); } static inline void clutter_text_add_move_binding (ClutterBindingPool *pool, const gchar *action, guint key_val, ClutterModifierType additional_modifiers, GCallback callback) { clutter_binding_pool_install_action (pool, action, key_val, 0, callback, NULL, NULL); clutter_binding_pool_install_action (pool, action, key_val, CLUTTER_SHIFT_MASK, callback, NULL, NULL); if (additional_modifiers != 0) { clutter_binding_pool_install_action (pool, action, key_val, additional_modifiers, callback, NULL, NULL); clutter_binding_pool_install_action (pool, action, key_val, CLUTTER_SHIFT_MASK | additional_modifiers, callback, NULL, NULL); } } static gboolean clutter_text_parse_custom_node (ClutterScriptable *scriptable, ClutterScript *script, GValue *value, const gchar *name, JsonNode *node) { if (strncmp (name, "font-description", 16) == 0) { g_value_init (value, G_TYPE_STRING); g_value_set_string (value, json_node_get_string (node)); return TRUE; } return parent_scriptable_iface->parse_custom_node (scriptable, script, value, name, node); } static void clutter_text_set_color_internal (ClutterText *self, GParamSpec *pspec, const ClutterColor *color) { ClutterTextPrivate *priv = CLUTTER_TEXT (self)->priv; GParamSpec *other = NULL; switch (pspec->param_id) { case PROP_COLOR: priv->text_color = *color; break; case PROP_CURSOR_COLOR: if (color) { priv->cursor_color = *color; priv->cursor_color_set = TRUE; } else priv->cursor_color_set = FALSE; other = obj_props[PROP_CURSOR_COLOR_SET]; break; case PROP_SELECTION_COLOR: if (color) { priv->selection_color = *color; priv->selection_color_set = TRUE; } else priv->selection_color_set = FALSE; other = obj_props[PROP_SELECTION_COLOR_SET]; break; case PROP_SELECTED_TEXT_COLOR: if (color) { priv->selected_text_color = *color; priv->selected_text_color_set = TRUE; } else priv->selected_text_color_set = FALSE; other = obj_props[PROP_SELECTED_TEXT_COLOR_SET]; break; default: g_assert_not_reached (); break; } clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), pspec); if (other) g_object_notify_by_pspec (G_OBJECT (self), other); } static void clutter_text_set_color_animated (ClutterText *self, GParamSpec *pspec, const ClutterColor *color) { ClutterActor *actor = CLUTTER_ACTOR (self); ClutterTextPrivate *priv = self->priv; const ClutterAnimationInfo *info; ClutterTransition *transition; info = _clutter_actor_get_animation_info (actor); transition = clutter_actor_get_transition (actor, pspec->name); /* jump to the end if there is no easing state, or if the easing * state has a duration of 0 msecs */ if (info->cur_state == NULL || info->cur_state->easing_duration == 0) { /* ensure that we remove any currently running transition */ if (transition != NULL) { clutter_actor_remove_transition (actor, pspec->name); transition = NULL; } clutter_text_set_color_internal (self, pspec, color); return; } if (transition == NULL) { transition = clutter_property_transition_new (pspec->name); clutter_transition_set_animatable (transition, CLUTTER_ANIMATABLE (self)); clutter_transition_set_remove_on_complete (transition, TRUE); /* delay only makes sense if the transition has just been created */ clutter_timeline_set_delay (CLUTTER_TIMELINE (transition), info->cur_state->easing_delay); clutter_actor_add_transition (actor, pspec->name, transition); /* the actor now owns the transition */ g_object_unref (transition); } /* if a transition already exist, update its bounds */ switch (pspec->param_id) { case PROP_COLOR: clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR, &priv->text_color); break; case PROP_CURSOR_COLOR: clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR, &priv->cursor_color); break; case PROP_SELECTION_COLOR: clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR, &priv->selection_color); break; case PROP_SELECTED_TEXT_COLOR: clutter_transition_set_from (transition, CLUTTER_TYPE_COLOR, &priv->selected_text_color); break; default: g_assert_not_reached (); } clutter_transition_set_to (transition, CLUTTER_TYPE_COLOR, color); /* always use the current easing state */ clutter_timeline_set_duration (CLUTTER_TIMELINE (transition), info->cur_state->easing_duration); clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (transition), info->cur_state->easing_mode); /* ensure that we start from the beginning */ clutter_timeline_rewind (CLUTTER_TIMELINE (transition)); clutter_timeline_start (CLUTTER_TIMELINE (transition)); } static void clutter_text_set_custom_property (ClutterScriptable *scriptable, ClutterScript *script, const gchar *name, const GValue *value) { if (strncmp (name, "font-description", 16) == 0) { g_assert (G_VALUE_HOLDS (value, G_TYPE_STRING)); if (g_value_get_string (value) != NULL) clutter_text_set_font_name (CLUTTER_TEXT (scriptable), g_value_get_string (value)); } else parent_scriptable_iface->set_custom_property (scriptable, script, name, value); } static void clutter_scriptable_iface_init (ClutterScriptableIface *iface) { parent_scriptable_iface = g_type_interface_peek_parent (iface); iface->parse_custom_node = clutter_text_parse_custom_node; iface->set_custom_property = clutter_text_set_custom_property; } static void clutter_text_set_final_state (ClutterAnimatable *animatable, const char *property_name, const GValue *value) { if (strcmp (property_name, "color") == 0) { const ClutterColor *color = clutter_value_get_color (value); clutter_text_set_color_internal (CLUTTER_TEXT (animatable), obj_props[PROP_COLOR], color); } else if (strcmp (property_name, "cursor-color") == 0) { const ClutterColor *color = clutter_value_get_color (value); clutter_text_set_color_internal (CLUTTER_TEXT (animatable), obj_props[PROP_CURSOR_COLOR], color); } else if (strcmp (property_name, "selected-text-color") == 0) { const ClutterColor *color = clutter_value_get_color (value); clutter_text_set_color_internal (CLUTTER_TEXT (animatable), obj_props[PROP_SELECTED_TEXT_COLOR], color); } else if (strcmp (property_name, "selection-color") == 0) { const ClutterColor *color = clutter_value_get_color (value); clutter_text_set_color_internal (CLUTTER_TEXT (animatable), obj_props[PROP_SELECTION_COLOR], color); } else parent_animatable_iface->set_final_state (animatable, property_name, value); } static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface) { parent_animatable_iface = g_type_interface_peek_parent (iface); iface->set_final_state = clutter_text_set_final_state; } static void clutter_text_class_init (ClutterTextClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterBindingPool *binding_pool; GParamSpec *pspec; gobject_class->set_property = clutter_text_set_property; gobject_class->get_property = clutter_text_get_property; gobject_class->dispose = clutter_text_dispose; gobject_class->finalize = clutter_text_finalize; actor_class->paint = clutter_text_paint; actor_class->get_paint_volume = clutter_text_get_paint_volume; actor_class->get_preferred_width = clutter_text_get_preferred_width; actor_class->get_preferred_height = clutter_text_get_preferred_height; actor_class->allocate = clutter_text_allocate; actor_class->key_press_event = clutter_text_key_press; actor_class->key_release_event = clutter_text_key_release; actor_class->button_press_event = clutter_text_button_press; actor_class->button_release_event = clutter_text_button_release; actor_class->motion_event = clutter_text_motion; actor_class->touch_event = clutter_text_touch_event; actor_class->key_focus_in = clutter_text_key_focus_in; actor_class->key_focus_out = clutter_text_key_focus_out; actor_class->has_overlaps = clutter_text_has_overlaps; actor_class->event = clutter_text_event; /** * ClutterText:buffer: * * The buffer which stores the text for this #ClutterText. * * If set to %NULL, a default buffer will be created. * * Since: 1.8 */ pspec = g_param_spec_object ("buffer", P_("Buffer"), P_("The buffer for the text"), CLUTTER_TYPE_TEXT_BUFFER, CLUTTER_PARAM_READWRITE); obj_props[PROP_BUFFER] = pspec; g_object_class_install_property (gobject_class, PROP_BUFFER, pspec); /** * ClutterText:font-name: * * The font to be used by the #ClutterText, as a string * that can be parsed by pango_font_description_from_string(). * * If set to %NULL, the default system font will be used instead. * * Since: 1.0 */ pspec = g_param_spec_string ("font-name", P_("Font Name"), P_("The font to be used by the text"), NULL, CLUTTER_PARAM_READWRITE); obj_props[PROP_FONT_NAME] = pspec; g_object_class_install_property (gobject_class, PROP_FONT_NAME, pspec); /** * ClutterText:font-description: * * The #PangoFontDescription that should be used by the #ClutterText * * If you have a string describing the font then you should look at * #ClutterText:font-name instead * * Since: 1.2 */ pspec = g_param_spec_boxed ("font-description", P_("Font Description"), P_("The font description to be used"), PANGO_TYPE_FONT_DESCRIPTION, CLUTTER_PARAM_READWRITE); obj_props[PROP_FONT_DESCRIPTION] = pspec; g_object_class_install_property (gobject_class, PROP_FONT_DESCRIPTION, pspec); /** * ClutterText:text: * * The text to render inside the actor. * * Since: 1.0 */ pspec = g_param_spec_string ("text", P_("Text"), P_("The text to render"), "", CLUTTER_PARAM_READWRITE); obj_props[PROP_TEXT] = pspec; g_object_class_install_property (gobject_class, PROP_TEXT, pspec); /** * ClutterText:color: * * The color used to render the text. * * Since: 1.0 */ pspec = clutter_param_spec_color ("color", P_("Font Color"), P_("Color of the font used by the text"), &default_text_color, CLUTTER_PARAM_READWRITE | CLUTTER_PARAM_ANIMATABLE); obj_props[PROP_COLOR] = pspec; g_object_class_install_property (gobject_class, PROP_COLOR, pspec); /** * ClutterText:editable: * * Whether key events delivered to the actor causes editing. * * Since: 1.0 */ pspec = g_param_spec_boolean ("editable", P_("Editable"), P_("Whether the text is editable"), FALSE, G_PARAM_READWRITE); obj_props[PROP_EDITABLE] = pspec; g_object_class_install_property (gobject_class, PROP_EDITABLE, pspec); /** * ClutterText:selectable: * * Whether it is possible to select text, either using the pointer * or the keyboard. * * This property depends on the #ClutterActor:reactive property being * set to %TRUE. * * Since: 1.0 */ pspec = g_param_spec_boolean ("selectable", P_("Selectable"), P_("Whether the text is selectable"), TRUE, G_PARAM_READWRITE); obj_props[PROP_SELECTABLE] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTABLE, pspec); /** * ClutterText:activatable: * * Toggles whether return invokes the activate signal or not. * * Since: 1.0 */ pspec = g_param_spec_boolean ("activatable", P_("Activatable"), P_("Whether pressing return causes the activate signal to be emitted"), TRUE, G_PARAM_READWRITE); obj_props[PROP_ACTIVATABLE] = pspec; g_object_class_install_property (gobject_class, PROP_ACTIVATABLE, pspec); /** * ClutterText:cursor-visible: * * Whether the input cursor is visible or not. * * The cursor will only be visible if this property and either * the #ClutterText:editable or the #ClutterText:selectable properties * are set to %TRUE. * * Since: 1.0 */ pspec = g_param_spec_boolean ("cursor-visible", P_("Cursor Visible"), P_("Whether the input cursor is visible"), TRUE, CLUTTER_PARAM_READWRITE); obj_props[PROP_CURSOR_VISIBLE] = pspec; g_object_class_install_property (gobject_class, PROP_CURSOR_VISIBLE, pspec); /** * ClutterText:cursor-color: * * The color of the cursor. * * Since: 1.0 */ pspec = clutter_param_spec_color ("cursor-color", P_("Cursor Color"), P_("Cursor Color"), &default_cursor_color, CLUTTER_PARAM_READWRITE | CLUTTER_PARAM_ANIMATABLE); obj_props[PROP_CURSOR_COLOR] = pspec; g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR, pspec); /** * ClutterText:cursor-color-set: * * Will be set to %TRUE if #ClutterText:cursor-color has been set. * * Since: 1.0 */ pspec = g_param_spec_boolean ("cursor-color-set", P_("Cursor Color Set"), P_("Whether the cursor color has been set"), FALSE, CLUTTER_PARAM_READABLE); obj_props[PROP_CURSOR_COLOR_SET] = pspec; g_object_class_install_property (gobject_class, PROP_CURSOR_COLOR_SET, pspec); /** * ClutterText:cursor-size: * * The size of the cursor, in pixels. If set to -1 the size used will * be the default cursor size of 2 pixels. * * Since: 1.0 */ pspec = g_param_spec_int ("cursor-size", P_("Cursor Size"), P_("The width of the cursor, in pixels"), -1, G_MAXINT, DEFAULT_CURSOR_SIZE, CLUTTER_PARAM_READWRITE); obj_props[PROP_CURSOR_SIZE] = pspec; g_object_class_install_property (gobject_class, PROP_CURSOR_SIZE, pspec); /** * ClutterText:position: * * The current input cursor position. -1 is taken to be the end of the text * * Since: 1.0 * * Deprecated: 1.12: Use ClutterText:cursor-position instead. */ pspec = g_param_spec_int ("position", P_("Cursor Position"), P_("The cursor position"), -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_DEPRECATED); obj_props[PROP_POSITION] = pspec; g_object_class_install_property (gobject_class, PROP_POSITION, pspec); /** * ClutterText:cursor-position: * * The current input cursor position. -1 is taken to be the end of the text * * Since: 1.12 */ pspec = g_param_spec_int ("cursor-position", P_("Cursor Position"), P_("The cursor position"), -1, G_MAXINT, -1, CLUTTER_PARAM_READWRITE); obj_props[PROP_CURSOR_POSITION] = pspec; g_object_class_install_property (gobject_class, PROP_CURSOR_POSITION, pspec); /** * ClutterText:selection-bound: * * The current input cursor position. -1 is taken to be the end of the text * * Since: 1.0 */ pspec = g_param_spec_int ("selection-bound", P_("Selection-bound"), P_("The cursor position of the other end of the selection"), -1, G_MAXINT, -1, CLUTTER_PARAM_READWRITE); obj_props[PROP_SELECTION_BOUND] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTION_BOUND, pspec); /** * ClutterText:selection-color: * * The color of the selection. * * Since: 1.0 */ pspec = clutter_param_spec_color ("selection-color", P_("Selection Color"), P_("Selection Color"), &default_selection_color, CLUTTER_PARAM_READWRITE | CLUTTER_PARAM_ANIMATABLE); obj_props[PROP_SELECTION_COLOR] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR, pspec); /** * ClutterText:selection-color-set: * * Will be set to %TRUE if #ClutterText:selection-color has been set. * * Since: 1.0 */ pspec = g_param_spec_boolean ("selection-color-set", P_("Selection Color Set"), P_("Whether the selection color has been set"), FALSE, CLUTTER_PARAM_READABLE); obj_props[PROP_SELECTION_COLOR_SET] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTION_COLOR_SET, pspec); /** * ClutterText:attributes: * * A list of #PangoStyleAttributes to be applied to the * contents of the #ClutterText actor. * * Since: 1.0 */ pspec = g_param_spec_boxed ("attributes", P_("Attributes"), P_("A list of style attributes to apply to the contents of the actor"), PANGO_TYPE_ATTR_LIST, CLUTTER_PARAM_READWRITE); obj_props[PROP_ATTRIBUTES] = pspec; g_object_class_install_property (gobject_class, PROP_ATTRIBUTES, pspec); /** * ClutterText:use-markup: * * Whether the text includes Pango markup. * * For more informations about the Pango markup format, see * pango_layout_set_markup() in the Pango documentation. * * It is not possible to round-trip this property between * %TRUE and %FALSE. Once a string with markup has been set on * a #ClutterText actor with :use-markup set to %TRUE, the markup * is stripped from the string. * * Since: 1.0 */ pspec = g_param_spec_boolean ("use-markup", P_("Use markup"), P_("Whether or not the text includes Pango markup"), FALSE, CLUTTER_PARAM_READWRITE); obj_props[PROP_USE_MARKUP] = pspec; g_object_class_install_property (gobject_class, PROP_USE_MARKUP, pspec); /** * ClutterText:line-wrap: * * Whether to wrap the lines of #ClutterText:text if the contents * exceed the available allocation. The wrapping strategy is * controlled by the #ClutterText:line-wrap-mode property. * * Since: 1.0 */ pspec = g_param_spec_boolean ("line-wrap", P_("Line wrap"), P_("If set, wrap the lines if the text becomes too wide"), FALSE, CLUTTER_PARAM_READWRITE); obj_props[PROP_LINE_WRAP] = pspec; g_object_class_install_property (gobject_class, PROP_LINE_WRAP, pspec); /** * ClutterText:line-wrap-mode: * * If #ClutterText:line-wrap is set to %TRUE, this property will * control how the text is wrapped. * * Since: 1.0 */ pspec = g_param_spec_enum ("line-wrap-mode", P_("Line wrap mode"), P_("Control how line-wrapping is done"), PANGO_TYPE_WRAP_MODE, PANGO_WRAP_WORD, CLUTTER_PARAM_READWRITE); obj_props[PROP_LINE_WRAP_MODE] = pspec; g_object_class_install_property (gobject_class, PROP_LINE_WRAP_MODE, pspec); /** * ClutterText:ellipsize: * * The preferred place to ellipsize the contents of the #ClutterText actor * * Since: 1.0 */ pspec = g_param_spec_enum ("ellipsize", P_("Ellipsize"), P_("The preferred place to ellipsize the string"), PANGO_TYPE_ELLIPSIZE_MODE, PANGO_ELLIPSIZE_NONE, CLUTTER_PARAM_READWRITE); obj_props[PROP_ELLIPSIZE] = pspec; g_object_class_install_property (gobject_class, PROP_ELLIPSIZE, pspec); /** * ClutterText:line-alignment: * * The preferred alignment for the text. This property controls * the alignment of multi-line paragraphs. * * Since: 1.0 */ pspec = g_param_spec_enum ("line-alignment", P_("Line Alignment"), P_("The preferred alignment for the string, for multi-line text"), PANGO_TYPE_ALIGNMENT, PANGO_ALIGN_LEFT, CLUTTER_PARAM_READWRITE); obj_props[PROP_LINE_ALIGNMENT] = pspec; g_object_class_install_property (gobject_class, PROP_LINE_ALIGNMENT, pspec); /** * ClutterText:justify: * * Whether the contents of the #ClutterText should be justified * on both margins. * * Since: 1.0 */ pspec = g_param_spec_boolean ("justify", P_("Justify"), P_("Whether the text should be justified"), FALSE, CLUTTER_PARAM_READWRITE); obj_props[PROP_JUSTIFY] = pspec; g_object_class_install_property (gobject_class, PROP_JUSTIFY, pspec); /** * ClutterText:password-char: * * If non-zero, the character that should be used in place of * the actual text in a password text actor. * * Since: 1.0 */ pspec = g_param_spec_unichar ("password-char", P_("Password Character"), P_("If non-zero, use this character to display the actor's contents"), 0, CLUTTER_PARAM_READWRITE); obj_props[PROP_PASSWORD_CHAR] = pspec; g_object_class_install_property (gobject_class, PROP_PASSWORD_CHAR, pspec); /** * ClutterText:max-length: * * The maximum length of the contents of the #ClutterText actor. * * Since: 1.0 */ pspec = g_param_spec_int ("max-length", P_("Max Length"), P_("Maximum length of the text inside the actor"), -1, G_MAXINT, 0, CLUTTER_PARAM_READWRITE); obj_props[PROP_MAX_LENGTH] = pspec; g_object_class_install_property (gobject_class, PROP_MAX_LENGTH, pspec); /** * ClutterText:single-line-mode: * * Whether the #ClutterText actor should be in single line mode * or not. A single line #ClutterText actor will only contain a * single line of text, scrolling it in case its length is bigger * than the allocated size. * * Setting this property will also set the #ClutterText:activatable * property as a side-effect. * * The #ClutterText:single-line-mode property is used only if the * #ClutterText:editable property is set to %TRUE. * * Since: 1.0 */ pspec = g_param_spec_boolean ("single-line-mode", P_("Single Line Mode"), P_("Whether the text should be a single line"), FALSE, CLUTTER_PARAM_READWRITE); obj_props[PROP_SINGLE_LINE_MODE] = pspec; g_object_class_install_property (gobject_class, PROP_SINGLE_LINE_MODE, pspec); /** * ClutterText:selected-text-color: * * The color of selected text. * * Since: 1.8 */ pspec = clutter_param_spec_color ("selected-text-color", P_("Selected Text Color"), P_("Selected Text Color"), &default_selected_text_color, CLUTTER_PARAM_READWRITE | CLUTTER_PARAM_ANIMATABLE); obj_props[PROP_SELECTED_TEXT_COLOR] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR, pspec); /** * ClutterText:selected-text-color-set: * * Will be set to %TRUE if #ClutterText:selected-text-color has been set. * * Since: 1.8 */ pspec = g_param_spec_boolean ("selected-text-color-set", P_("Selected Text Color Set"), P_("Whether the selected text color has been set"), FALSE, CLUTTER_PARAM_READABLE); obj_props[PROP_SELECTED_TEXT_COLOR_SET] = pspec; g_object_class_install_property (gobject_class, PROP_SELECTED_TEXT_COLOR_SET, pspec); pspec = g_param_spec_flags ("input-hints", P_("Input hints"), P_("Input hints"), CLUTTER_TYPE_INPUT_CONTENT_HINT_FLAGS, 0, CLUTTER_PARAM_READWRITE); obj_props[PROP_INPUT_HINTS] = pspec; g_object_class_install_property (gobject_class, PROP_INPUT_HINTS, pspec); pspec = g_param_spec_enum ("input-purpose", P_("Input purpose"), P_("Input purpose"), CLUTTER_TYPE_INPUT_CONTENT_PURPOSE, 0, CLUTTER_PARAM_READWRITE); obj_props[PROP_INPUT_PURPOSE] = pspec; g_object_class_install_property (gobject_class, PROP_INPUT_PURPOSE, pspec); /** * ClutterText::text-changed: * @self: the #ClutterText that emitted the signal * * The ::text-changed signal is emitted after @actor's text changes * * Since: 1.0 */ text_signals[TEXT_CHANGED] = g_signal_new (I_("text-changed"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTextClass, text_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterText::insert-text: * @self: the #ClutterText that emitted the signal * @new_text: the new text to insert * @new_text_length: the length of the new text, in bytes, or -1 if * new_text is nul-terminated * @position: the position, in characters, at which to insert the * new text. this is an in-out parameter. After the signal * emission is finished, it should point after the newly * inserted text. * * This signal is emitted when text is inserted into the actor by * the user. It is emitted before @self text changes. * * Since: 1.2 */ text_signals[INSERT_TEXT] = g_signal_new (I_("insert-text"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, _clutter_marshal_VOID__STRING_INT_POINTER, G_TYPE_NONE, 3, G_TYPE_STRING, G_TYPE_INT, G_TYPE_POINTER); /** * ClutterText::delete-text: * @self: the #ClutterText that emitted the signal * @start_pos: the starting position * @end_pos: the end position * * This signal is emitted when text is deleted from the actor by * the user. It is emitted before @self text changes. * * Since: 1.2 */ text_signals[DELETE_TEXT] = g_signal_new (I_("delete-text"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, _clutter_marshal_VOID__INT_INT, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); /** * ClutterText::cursor-event: * @self: the #ClutterText that emitted the signal * @rect: the coordinates of the cursor * * The ::cursor-event signal is emitted whenever the cursor position * changes inside a #ClutterText actor. Inside @rect it is stored * the current position and size of the cursor, relative to the actor * itself. * * Since: 1.0 * * Deprecated: 1.16: Use the #ClutterText::cursor-changed signal instead */ text_signals[CURSOR_EVENT] = g_signal_new (I_("cursor-event"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST | G_SIGNAL_DEPRECATED, G_STRUCT_OFFSET (ClutterTextClass, cursor_event), NULL, NULL, NULL, G_TYPE_NONE, 1, GRAPHENE_TYPE_RECT | G_SIGNAL_TYPE_STATIC_SCOPE); /** * ClutterText::cursor-changed: * @self: the #ClutterText that emitted the signal * * The ::cursor-changed signal is emitted whenever the cursor * position or size changes. * * Since: 1.16 */ text_signals[CURSOR_CHANGED] = g_signal_new (I_("cursor-changed"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTextClass, cursor_changed), NULL, NULL, NULL, G_TYPE_NONE, 0); /** * ClutterText::activate: * @self: the #ClutterText that emitted the signal * * The ::activate signal is emitted each time the actor is 'activated' * by the user, normally by pressing the 'Enter' key. The signal is * emitted only if #ClutterText:activatable is set to %TRUE. * * Since: 1.0 */ text_signals[ACTIVATE] = g_signal_new (I_("activate"), G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterTextClass, activate), NULL, NULL, NULL, G_TYPE_NONE, 0); binding_pool = clutter_binding_pool_get_for_class (klass); clutter_text_add_move_binding (binding_pool, "move-left", CLUTTER_KEY_Left, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_move_left)); clutter_text_add_move_binding (binding_pool, "move-left", CLUTTER_KEY_KP_Left, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_move_left)); clutter_text_add_move_binding (binding_pool, "move-right", CLUTTER_KEY_Right, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_move_right)); clutter_text_add_move_binding (binding_pool, "move-right", CLUTTER_KEY_KP_Right, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_move_right)); clutter_text_add_move_binding (binding_pool, "move-up", CLUTTER_KEY_Up, 0, G_CALLBACK (clutter_text_real_move_up)); clutter_text_add_move_binding (binding_pool, "move-up", CLUTTER_KEY_KP_Up, 0, G_CALLBACK (clutter_text_real_move_up)); clutter_text_add_move_binding (binding_pool, "move-down", CLUTTER_KEY_Down, 0, G_CALLBACK (clutter_text_real_move_down)); clutter_text_add_move_binding (binding_pool, "move-down", CLUTTER_KEY_KP_Down, 0, G_CALLBACK (clutter_text_real_move_down)); clutter_text_add_move_binding (binding_pool, "line-start", CLUTTER_KEY_Home, 0, G_CALLBACK (clutter_text_real_line_start)); clutter_text_add_move_binding (binding_pool, "line-start", CLUTTER_KEY_KP_Home, 0, G_CALLBACK (clutter_text_real_line_start)); clutter_text_add_move_binding (binding_pool, "line-start", CLUTTER_KEY_Begin, 0, G_CALLBACK (clutter_text_real_line_start)); clutter_text_add_move_binding (binding_pool, "line-end", CLUTTER_KEY_End, 0, G_CALLBACK (clutter_text_real_line_end)); clutter_text_add_move_binding (binding_pool, "line-end", CLUTTER_KEY_KP_End, 0, G_CALLBACK (clutter_text_real_line_end)); clutter_binding_pool_install_action (binding_pool, "select-all", CLUTTER_KEY_a, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_select_all), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "select-all", CLUTTER_KEY_A, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_select_all), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_KEY_Delete, 0, G_CALLBACK (clutter_text_real_del_next), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_KEY_Delete, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_del_word_next), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_KEY_KP_Delete, 0, G_CALLBACK (clutter_text_real_del_next), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-next", CLUTTER_KEY_KP_Delete, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_del_word_next), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-prev", CLUTTER_KEY_BackSpace, 0, G_CALLBACK (clutter_text_real_del_prev), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-prev", CLUTTER_KEY_BackSpace, CLUTTER_SHIFT_MASK, G_CALLBACK (clutter_text_real_del_prev), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "delete-prev", CLUTTER_KEY_BackSpace, CLUTTER_CONTROL_MASK, G_CALLBACK (clutter_text_real_del_word_prev), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_Return, 0, G_CALLBACK (clutter_text_real_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_KP_Enter, 0, G_CALLBACK (clutter_text_real_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_ISO_Enter, 0, G_CALLBACK (clutter_text_real_activate), NULL, NULL); } static void clutter_text_init (ClutterText *self) { ClutterSettings *settings; ClutterTextPrivate *priv; gchar *font_name; int i, password_hint_time; self->priv = priv = clutter_text_get_instance_private (self); priv->alignment = PANGO_ALIGN_LEFT; priv->wrap = FALSE; priv->wrap_mode = PANGO_WRAP_WORD; priv->ellipsize = PANGO_ELLIPSIZE_NONE; priv->use_underline = FALSE; priv->use_markup = FALSE; priv->justify = FALSE; for (i = 0; i < N_CACHED_LAYOUTS; i++) priv->cached_layouts[i].layout = NULL; /* default to "" so that clutter_text_get_text() will * return a valid string and we can safely call strlen() * or strcmp() on it */ priv->buffer = NULL; priv->text_color = default_text_color; priv->cursor_color = default_cursor_color; priv->selection_color = default_selection_color; priv->selected_text_color = default_selected_text_color; /* get the default font name from the context; we don't use * set_font_description() here because we are initializing * the Text and we don't need notifications and sanity checks */ settings = clutter_settings_get_default (); g_object_get (settings, "font-name", &font_name, "password-hint-time", &password_hint_time, NULL); priv->font_name = font_name; /* font_name is allocated */ priv->font_desc = pango_font_description_from_string (font_name); priv->is_default_font = TRUE; priv->position = -1; priv->selection_bound = -1; priv->x_pos = -1; priv->cursor_visible = TRUE; priv->editable = FALSE; priv->selectable = TRUE; priv->selection_color_set = FALSE; priv->cursor_color_set = FALSE; priv->selected_text_color_set = FALSE; priv->preedit_set = FALSE; priv->password_char = 0; priv->show_password_hint = password_hint_time > 0; priv->password_hint_timeout = password_hint_time; priv->text_y = 0; priv->cursor_size = DEFAULT_CURSOR_SIZE; priv->settings_changed_id = g_signal_connect_swapped (clutter_get_default_backend (), "settings-changed", G_CALLBACK (clutter_text_settings_changed_cb), self); priv->direction_changed_id = g_signal_connect (self, "notify::text-direction", G_CALLBACK (clutter_text_direction_changed_cb), NULL); priv->input_focus = clutter_text_input_focus_new (self); priv->resource_scale_changed_id = g_signal_connect (self, "notify::resource-scale", G_CALLBACK (clutter_text_resource_scale_changed_cb), NULL); } /** * clutter_text_new: * * Creates a new #ClutterText actor. This actor can be used to * display and edit text. * * Return value: the newly created #ClutterText actor * * Since: 1.0 */ ClutterActor * clutter_text_new (void) { return g_object_new (CLUTTER_TYPE_TEXT, NULL); } /** * clutter_text_new_full: * @font_name: a string with a font description * @text: the contents of the actor * @color: the color to be used to render @text * * Creates a new #ClutterText actor, using @font_name as the font * description; @text will be used to set the contents of the actor; * and @color will be used as the color to render @text. * * This function is equivalent to calling clutter_text_new(), * clutter_text_set_font_name(), clutter_text_set_text() and * clutter_text_set_color(). * * Return value: the newly created #ClutterText actor * * Since: 1.0 */ ClutterActor * clutter_text_new_full (const gchar *font_name, const gchar *text, const ClutterColor *color) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, "text", text, "color", color, NULL); } /** * clutter_text_new_with_text: * @font_name: (allow-none): a string with a font description * @text: the contents of the actor * * Creates a new #ClutterText actor, using @font_name as the font * description; @text will be used to set the contents of the actor. * * This function is equivalent to calling clutter_text_new(), * clutter_text_set_font_name(), and clutter_text_set_text(). * * Return value: the newly created #ClutterText actor * * Since: 1.0 */ ClutterActor * clutter_text_new_with_text (const gchar *font_name, const gchar *text) { return g_object_new (CLUTTER_TYPE_TEXT, "font-name", font_name, "text", text, NULL); } static ClutterTextBuffer* get_buffer (ClutterText *self) { ClutterTextPrivate *priv = self->priv; if (priv->buffer == NULL) { ClutterTextBuffer *buffer; buffer = clutter_text_buffer_new (); clutter_text_set_buffer (self, buffer); g_object_unref (buffer); } return priv->buffer; } /* GtkEntryBuffer signal handlers */ static void buffer_inserted_text (ClutterTextBuffer *buffer, guint position, const gchar *chars, guint n_chars, ClutterText *self) { ClutterTextPrivate *priv; gint new_position; gint new_selection_bound; priv = self->priv; if (priv->position >= 0 || priv->selection_bound >= 0) { new_position = priv->position; new_selection_bound = priv->selection_bound; if (position <= new_position) new_position += n_chars; if (position <= new_selection_bound) new_selection_bound += n_chars; if (priv->position != new_position || priv->selection_bound != new_selection_bound) clutter_text_set_positions (self, new_position, new_selection_bound); } /* TODO: What are we supposed to with the out value of position? */ } static void buffer_deleted_text (ClutterTextBuffer *buffer, guint position, guint n_chars, ClutterText *self) { ClutterTextPrivate *priv; gint new_position; gint new_selection_bound; priv = self->priv; if (priv->position >= 0 || priv->selection_bound >= 0) { new_position = priv->position; new_selection_bound = priv->selection_bound; if (position < new_position) new_position -= n_chars; if (position < new_selection_bound) new_selection_bound -= n_chars; if (priv->position != new_position || priv->selection_bound != new_selection_bound) clutter_text_set_positions (self, new_position, new_selection_bound); } } static void clutter_text_queue_redraw_or_relayout (ClutterText *self) { ClutterActor *actor = CLUTTER_ACTOR (self); gfloat preferred_width; gfloat preferred_height; clutter_text_dirty_cache (self); /* we're using our private implementations here to avoid the caching done by ClutterActor */ clutter_text_get_preferred_width (actor, -1, NULL, &preferred_width); clutter_text_get_preferred_height (actor, preferred_width, NULL, &preferred_height); if (clutter_actor_has_allocation (actor) && (fabsf (preferred_width - clutter_actor_get_width (actor)) > 0.001 || fabsf (preferred_height - clutter_actor_get_height (actor)) > 0.001)) clutter_actor_queue_relayout (actor); else clutter_text_queue_redraw (actor); } static void buffer_notify_text (ClutterTextBuffer *buffer, GParamSpec *spec, ClutterText *self) { g_object_freeze_notify (G_OBJECT (self)); clutter_text_queue_redraw_or_relayout (self); g_signal_emit (self, text_signals[TEXT_CHANGED], 0); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_TEXT]); g_object_thaw_notify (G_OBJECT (self)); } static void buffer_notify_max_length (ClutterTextBuffer *buffer, GParamSpec *spec, ClutterText *self) { g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_MAX_LENGTH]); } static void buffer_connect_signals (ClutterText *self) { ClutterTextPrivate *priv = self->priv; g_signal_connect (priv->buffer, "inserted-text", G_CALLBACK (buffer_inserted_text), self); g_signal_connect (priv->buffer, "deleted-text", G_CALLBACK (buffer_deleted_text), self); g_signal_connect (priv->buffer, "notify::text", G_CALLBACK (buffer_notify_text), self); g_signal_connect (priv->buffer, "notify::max-length", G_CALLBACK (buffer_notify_max_length), self); } static void buffer_disconnect_signals (ClutterText *self) { ClutterTextPrivate *priv = self->priv; g_signal_handlers_disconnect_by_func (priv->buffer, buffer_inserted_text, self); g_signal_handlers_disconnect_by_func (priv->buffer, buffer_deleted_text, self); g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_text, self); g_signal_handlers_disconnect_by_func (priv->buffer, buffer_notify_max_length, self); } /** * clutter_text_new_with_buffer: * @buffer: The buffer to use for the new #ClutterText. * * Creates a new entry with the specified text buffer. * * Return value: a new #ClutterText * * Since: 1.10 */ ClutterActor * clutter_text_new_with_buffer (ClutterTextBuffer *buffer) { g_return_val_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer), NULL); return g_object_new (CLUTTER_TYPE_TEXT, "buffer", buffer, NULL); } /** * clutter_text_get_buffer: * @self: a #ClutterText * * Get the #ClutterTextBuffer object which holds the text for * this widget. * * Returns: (transfer none): A #GtkEntryBuffer object. * * Since: 1.10 */ ClutterTextBuffer* clutter_text_get_buffer (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); return get_buffer (self); } /** * clutter_text_set_buffer: * @self: a #ClutterText * @buffer: a #ClutterTextBuffer * * Set the #ClutterTextBuffer object which holds the text for * this widget. * * Since: 1.10 */ void clutter_text_set_buffer (ClutterText *self, ClutterTextBuffer *buffer) { ClutterTextPrivate *priv; GObject *obj; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (buffer) { g_return_if_fail (CLUTTER_IS_TEXT_BUFFER (buffer)); g_object_ref (buffer); } if (priv->buffer) { buffer_disconnect_signals (self); g_object_unref (priv->buffer); } priv->buffer = buffer; if (priv->buffer) buffer_connect_signals (self); obj = G_OBJECT (self); g_object_freeze_notify (obj); g_object_notify_by_pspec (obj, obj_props[PROP_BUFFER]); g_object_notify_by_pspec (obj, obj_props[PROP_TEXT]); g_object_notify_by_pspec (obj, obj_props[PROP_MAX_LENGTH]); g_object_thaw_notify (obj); } /** * clutter_text_set_editable: * @self: a #ClutterText * @editable: whether the #ClutterText should be editable * * Sets whether the #ClutterText actor should be editable. * * An editable #ClutterText with key focus set using * clutter_actor_grab_key_focus() or clutter_stage_set_key_focus() * will receive key events and will update its contents accordingly. * * Since: 1.0 */ void clutter_text_set_editable (ClutterText *self, gboolean editable) { ClutterBackend *backend = clutter_get_default_backend (); ClutterInputMethod *method = clutter_backend_get_input_method (backend); ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->editable != editable) { priv->editable = editable; if (method) { if (!priv->editable && clutter_input_focus_is_focused (priv->input_focus)) clutter_input_method_focus_out (method); else if (priv->has_focus) clutter_text_im_focus (self); } clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_EDITABLE]); } } /** * clutter_text_get_editable: * @self: a #ClutterText * * Retrieves whether a #ClutterText is editable or not. * * Return value: %TRUE if the actor is editable * * Since: 1.0 */ gboolean clutter_text_get_editable (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->editable; } /** * clutter_text_set_selectable: * @self: a #ClutterText * @selectable: whether the #ClutterText actor should be selectable * * Sets whether a #ClutterText actor should be selectable. * * A selectable #ClutterText will allow selecting its contents using * the pointer or the keyboard. * * Since: 1.0 */ void clutter_text_set_selectable (ClutterText *self, gboolean selectable) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->selectable != selectable) { priv->selectable = selectable; clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTABLE]); } } /** * clutter_text_get_selectable: * @self: a #ClutterText * * Retrieves whether a #ClutterText is selectable or not. * * Return value: %TRUE if the actor is selectable * * Since: 1.0 */ gboolean clutter_text_get_selectable (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); return self->priv->selectable; } /** * clutter_text_set_activatable: * @self: a #ClutterText * @activatable: whether the #ClutterText actor should be activatable * * Sets whether a #ClutterText actor should be activatable. * * An activatable #ClutterText actor will emit the #ClutterText::activate * signal whenever the 'Enter' (or 'Return') key is pressed; if it is not * activatable, a new line will be appended to the current content. * * An activatable #ClutterText must also be set as editable using * clutter_text_set_editable(). * * Since: 1.0 */ void clutter_text_set_activatable (ClutterText *self, gboolean activatable) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->activatable != activatable) { priv->activatable = activatable; clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]); } } /** * clutter_text_get_activatable: * @self: a #ClutterText * * Retrieves whether a #ClutterText is activatable or not. * * Return value: %TRUE if the actor is activatable * * Since: 1.0 */ gboolean clutter_text_get_activatable (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); return self->priv->activatable; } /** * clutter_text_activate: * @self: a #ClutterText * * Emits the #ClutterText::activate signal, if @self has been set * as activatable using clutter_text_set_activatable(). * * This function can be used to emit the ::activate signal inside * a #ClutterActor::captured-event or #ClutterActor::key-press-event * signal handlers before the default signal handler for the * #ClutterText is invoked. * * Return value: %TRUE if the ::activate signal has been emitted, * and %FALSE otherwise * * Since: 1.0 */ gboolean clutter_text_activate (ClutterText *self) { ClutterTextPrivate *priv; g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); priv = self->priv; if (priv->activatable) { g_signal_emit (self, text_signals[ACTIVATE], 0); return TRUE; } return FALSE; } /** * clutter_text_set_cursor_visible: * @self: a #ClutterText * @cursor_visible: whether the cursor should be visible * * Sets whether the cursor of a #ClutterText actor should be * visible or not. * * The color of the cursor will be the same as the text color * unless clutter_text_set_cursor_color() has been called. * * The size of the cursor can be set using clutter_text_set_cursor_size(). * * The position of the cursor can be changed programmatically using * clutter_text_set_cursor_position(). * * Since: 1.0 */ void clutter_text_set_cursor_visible (ClutterText *self, gboolean cursor_visible) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; cursor_visible = !!cursor_visible; if (priv->cursor_visible != cursor_visible) { priv->cursor_visible = cursor_visible; clutter_text_queue_redraw_or_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_VISIBLE]); } } /** * clutter_text_get_cursor_visible: * @self: a #ClutterText * * Retrieves whether the cursor of a #ClutterText actor is visible. * * Return value: %TRUE if the cursor is visible * * Since: 1.0 */ gboolean clutter_text_get_cursor_visible (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), TRUE); return self->priv->cursor_visible; } /** * clutter_text_set_cursor_color: * @self: a #ClutterText * @color: (allow-none): the color of the cursor, or %NULL to unset it * * Sets the color of the cursor of a #ClutterText actor. * * If @color is %NULL, the cursor color will be the same as the * text color. * * Since: 1.0 */ void clutter_text_set_cursor_color (ClutterText *self, const ClutterColor *color) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_set_color_animated (self, obj_props[PROP_CURSOR_COLOR], color); } /** * clutter_text_get_cursor_color: * @self: a #ClutterText * @color: (out): return location for a #ClutterColor * * Retrieves the color of the cursor of a #ClutterText actor. * * Since: 1.0 */ void clutter_text_get_cursor_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); priv = self->priv; *color = priv->cursor_color; } /** * clutter_text_set_selection: * @self: a #ClutterText * @start_pos: start of the selection, in characters * @end_pos: end of the selection, in characters * * Selects the region of text between @start_pos and @end_pos. * * This function changes the position of the cursor to match * @start_pos and the selection bound to match @end_pos. * * Since: 1.0 */ void clutter_text_set_selection (ClutterText *self, gssize start_pos, gssize end_pos) { guint n_chars; g_return_if_fail (CLUTTER_IS_TEXT (self)); n_chars = clutter_text_buffer_get_length (get_buffer (self)); if (end_pos < 0) end_pos = n_chars; start_pos = MIN (n_chars, start_pos); end_pos = MIN (n_chars, end_pos); clutter_text_set_positions (self, start_pos, end_pos); } /** * clutter_text_get_selection: * @self: a #ClutterText * * Retrieves the currently selected text. * * Return value: a newly allocated string containing the currently * selected text, or %NULL. Use g_free() to free the returned * string. * * Since: 1.0 */ gchar * clutter_text_get_selection (ClutterText *self) { ClutterTextPrivate *priv; gchar *str; gint len; gint start_index, end_index; gint start_offset, end_offset; const gchar *text; g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); priv = self->priv; start_index = priv->position; end_index = priv->selection_bound; if (end_index == start_index) return g_strdup (""); if ((end_index != -1 && end_index < start_index) || start_index == -1) { gint temp = start_index; start_index = end_index; end_index = temp; } text = clutter_text_buffer_get_text (get_buffer (self)); start_offset = offset_to_bytes (text, start_index); end_offset = offset_to_bytes (text, end_index); len = end_offset - start_offset; str = g_malloc (len + 1); g_utf8_strncpy (str, text + start_offset, end_index - start_index); return str; } /** * clutter_text_set_selection_bound: * @self: a #ClutterText * @selection_bound: the position of the end of the selection, in characters * * Sets the other end of the selection, starting from the current * cursor position. * * If @selection_bound is -1, the selection unset. * * Since: 1.0 */ void clutter_text_set_selection_bound (ClutterText *self, gint selection_bound) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->selection_bound != selection_bound) { gint len = clutter_text_buffer_get_length (get_buffer (self));; if (selection_bound < 0 || selection_bound >= len) priv->selection_bound = -1; else priv->selection_bound = selection_bound; clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SELECTION_BOUND]); } } /** * clutter_text_get_selection_bound: * @self: a #ClutterText * * Retrieves the other end of the selection of a #ClutterText actor, * in characters from the current cursor position. * * Return value: the position of the other end of the selection * * Since: 1.0 */ gint clutter_text_get_selection_bound (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); return self->priv->selection_bound; } /** * clutter_text_set_selection_color: * @self: a #ClutterText * @color: (allow-none): the color of the selection, or %NULL to unset it * * Sets the color of the selection of a #ClutterText actor. * * If @color is %NULL, the selection color will be the same as the * cursor color, or if no cursor color is set either then it will be * the same as the text color. * * Since: 1.0 */ void clutter_text_set_selection_color (ClutterText *self, const ClutterColor *color) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_set_color_animated (self, obj_props[PROP_SELECTION_COLOR], color); } /** * clutter_text_get_selection_color: * @self: a #ClutterText * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the color of the selection of a #ClutterText actor. * * Since: 1.0 */ void clutter_text_get_selection_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); priv = self->priv; *color = priv->selection_color; } /** * clutter_text_set_selected_text_color: * @self: a #ClutterText * @color: (allow-none): the selected text color, or %NULL to unset it * * Sets the selected text color of a #ClutterText actor. * * If @color is %NULL, the selected text color will be the same as the * selection color, which then falls back to cursor, and then text color. * * Since: 1.8 */ void clutter_text_set_selected_text_color (ClutterText *self, const ClutterColor *color) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_set_color_animated (self, obj_props[PROP_SELECTED_TEXT_COLOR], color); } /** * clutter_text_get_selected_text_color: * @self: a #ClutterText * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the color of selected text of a #ClutterText actor. * * Since: 1.8 */ void clutter_text_get_selected_text_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); priv = self->priv; *color = priv->selected_text_color; } /** * clutter_text_set_font_description: * @self: a #ClutterText * @font_desc: a #PangoFontDescription * * Sets @font_desc as the font description for a #ClutterText * * The #PangoFontDescription is copied by the #ClutterText actor * so you can safely call pango_font_description_free() on it after * calling this function. * * Since: 1.2 */ void clutter_text_set_font_description (ClutterText *self, PangoFontDescription *font_desc) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_set_font_description_internal (self, font_desc, font_desc == NULL); } /** * clutter_text_get_font_description: * @self: a #ClutterText * * Retrieves the #PangoFontDescription used by @self * * Return value: a #PangoFontDescription. The returned value is owned * by the #ClutterText actor and it should not be modified or freed * * Since: 1.2 */ PangoFontDescription * clutter_text_get_font_description (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); return self->priv->font_desc; } /** * clutter_text_get_font_name: * @self: a #ClutterText * * Retrieves the font name as set by clutter_text_set_font_name(). * * Return value: a string containing the font name. The returned * string is owned by the #ClutterText actor and should not be * modified or freed * * Since: 1.0 */ const gchar * clutter_text_get_font_name (ClutterText *text) { g_return_val_if_fail (CLUTTER_IS_TEXT (text), NULL); return text->priv->font_name; } /** * clutter_text_set_font_name: * @self: a #ClutterText * @font_name: (allow-none): a font name, or %NULL to set the default font name * * Sets the font used by a #ClutterText. The @font_name string * must either be %NULL, which means that the font name from the * default #ClutterBackend will be used; or be something that can * be parsed by the pango_font_description_from_string() function, * like: * * |[ * // Set the font to the system's Sans, 10 points * clutter_text_set_font_name (text, "Sans 10"); * * // Set the font to the system's Serif, 16 pixels * clutter_text_set_font_name (text, "Serif 16px"); * * // Set the font to Helvetica, 10 points * clutter_text_set_font_name (text, "Helvetica 10"); * ]| * * Since: 1.0 */ void clutter_text_set_font_name (ClutterText *self, const gchar *font_name) { ClutterTextPrivate *priv; PangoFontDescription *desc; gboolean is_default_font; g_return_if_fail (CLUTTER_IS_TEXT (self)); /* get the default font name from the backend */ if (font_name == NULL || font_name[0] == '\0') { ClutterSettings *settings = clutter_settings_get_default (); gchar *default_font_name = NULL; g_object_get (settings, "font-name", &default_font_name, NULL); if (default_font_name != NULL) font_name = default_font_name; else { /* last fallback */ font_name = g_strdup ("Sans 12"); } is_default_font = TRUE; } else is_default_font = FALSE; priv = self->priv; if (g_strcmp0 (priv->font_name, font_name) == 0) goto out; desc = pango_font_description_from_string (font_name); if (desc == NULL) { g_warning ("Attempting to create a PangoFontDescription for " "font name '%s', but failed.", font_name); goto out; } /* this will set the font_name field as well */ clutter_text_set_font_description_internal (self, desc, is_default_font); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_FONT_NAME]); pango_font_description_free (desc); out: if (is_default_font) g_free ((gchar *) font_name); } /** * clutter_text_get_text: * @self: a #ClutterText * * Retrieves a pointer to the current contents of a #ClutterText * actor. * * If you need a copy of the contents for manipulating, either * use g_strdup() on the returned string, or use: * * |[ * copy = clutter_text_get_chars (text, 0, -1); * ]| * * Which will return a newly allocated string. * * If the #ClutterText actor is empty, this function will return * an empty string, and not %NULL. * * Return value: (transfer none): the contents of the actor. The returned * string is owned by the #ClutterText actor and should never be modified * or freed * * Since: 1.0 */ const gchar * clutter_text_get_text (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); return clutter_text_buffer_get_text (get_buffer (self)); } static inline void clutter_text_set_use_markup_internal (ClutterText *self, gboolean use_markup) { ClutterTextPrivate *priv = self->priv; if (priv->use_markup != use_markup) { priv->use_markup = use_markup; /* reset the attributes lists so that they can be * re-generated */ if (priv->effective_attrs != NULL) { pango_attr_list_unref (priv->effective_attrs); priv->effective_attrs = NULL; } if (priv->markup_attrs) { pango_attr_list_unref (priv->markup_attrs); priv->markup_attrs = NULL; } g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_USE_MARKUP]); } } /** * clutter_text_set_text: * @self: a #ClutterText * @text: (allow-none): the text to set. Passing %NULL is the same * as passing "" (the empty string) * * Sets the contents of a #ClutterText actor. * * If the #ClutterText:use-markup property was set to %TRUE it * will be reset to %FALSE as a side effect. If you want to * maintain the #ClutterText:use-markup you should use the * clutter_text_set_markup() function instead * * Since: 1.0 */ void clutter_text_set_text (ClutterText *self, const gchar *text) { g_return_if_fail (CLUTTER_IS_TEXT (self)); /* if the text is editable (i.e. there is not markup flag to reset) then * changing the contents will result in selection and cursor changes that * we should avoid */ if (self->priv->editable) { if (g_strcmp0 (clutter_text_buffer_get_text (get_buffer (self)), text) == 0) return; } clutter_text_set_use_markup_internal (self, FALSE); clutter_text_buffer_set_text (get_buffer (self), text ? text : "", -1); } /** * clutter_text_set_markup: * @self: a #ClutterText * @markup: (allow-none): a string containing Pango markup. * Passing %NULL is the same as passing "" (the empty string) * * Sets @markup as the contents of a #ClutterText. * * This is a convenience function for setting a string containing * Pango markup, and it is logically equivalent to: * * |[ * /* the order is important */ * clutter_text_set_text (CLUTTER_TEXT (actor), markup); * clutter_text_set_use_markup (CLUTTER_TEXT (actor), TRUE); * ]| * * Since: 1.0 */ void clutter_text_set_markup (ClutterText *self, const gchar *markup) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_set_use_markup_internal (self, TRUE); if (markup != NULL && *markup != '\0') clutter_text_set_markup_internal (self, markup); else clutter_text_buffer_set_text (get_buffer (self), "", 0); } /** * clutter_text_get_layout: * @self: a #ClutterText * * Retrieves the current #PangoLayout used by a #ClutterText actor. * * Return value: (transfer none): a #PangoLayout. The returned object is owned by * the #ClutterText actor and should not be modified or freed * * Since: 1.0 */ PangoLayout * clutter_text_get_layout (ClutterText *self) { PangoLayout *layout; gfloat width, height; g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); if (self->priv->editable && self->priv->single_line_mode) return clutter_text_create_layout (self, -1, -1); clutter_actor_get_size (CLUTTER_ACTOR (self), &width, &height); layout = maybe_create_text_layout_with_resource_scale (self, width, height); if (!layout) layout = clutter_text_create_layout (self, width, height); return layout; } /** * clutter_text_set_color: * @self: a #ClutterText * @color: a #ClutterColor * * Sets the color of the contents of a #ClutterText actor. * * The overall opacity of the #ClutterText actor will be the * result of the alpha value of @color and the composited * opacity of the actor itself on the scenegraph, as returned * by clutter_actor_get_paint_opacity(). * * Since: 1.0 */ void clutter_text_set_color (ClutterText *self, const ClutterColor *color) { g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); clutter_text_set_color_animated (self, obj_props[PROP_COLOR], color); } /** * clutter_text_get_color: * @self: a #ClutterText * @color: (out caller-allocates): return location for a #ClutterColor * * Retrieves the text color as set by clutter_text_set_color(). * * Since: 1.0 */ void clutter_text_get_color (ClutterText *self, ClutterColor *color) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (color != NULL); priv = self->priv; *color = priv->text_color; } /** * clutter_text_set_ellipsize: * @self: a #ClutterText * @mode: a #PangoEllipsizeMode * * Sets the mode used to ellipsize (add an ellipsis: "...") to the * text if there is not enough space to render the entire contents * of a #ClutterText actor * * Since: 1.0 */ void clutter_text_set_ellipsize (ClutterText *self, PangoEllipsizeMode mode) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (mode >= PANGO_ELLIPSIZE_NONE && mode <= PANGO_ELLIPSIZE_END); priv = self->priv; if ((PangoEllipsizeMode) priv->ellipsize != mode) { priv->ellipsize = mode; clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ELLIPSIZE]); } } /** * clutter_text_get_ellipsize: * @self: a #ClutterText * * Returns the ellipsizing position of a #ClutterText actor, as * set by clutter_text_set_ellipsize(). * * Return value: #PangoEllipsizeMode * * Since: 1.0 */ PangoEllipsizeMode clutter_text_get_ellipsize (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ELLIPSIZE_NONE); return self->priv->ellipsize; } /** * clutter_text_get_line_wrap: * @self: a #ClutterText * * Retrieves the value set using clutter_text_set_line_wrap(). * * Return value: %TRUE if the #ClutterText actor should wrap * its contents * * Since: 1.0 */ gboolean clutter_text_get_line_wrap (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->wrap; } /** * clutter_text_set_line_wrap: * @self: a #ClutterText * @line_wrap: whether the contents should wrap * * Sets whether the contents of a #ClutterText actor should wrap, * if they don't fit the size assigned to the actor. * * Since: 1.0 */ void clutter_text_set_line_wrap (ClutterText *self, gboolean line_wrap) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->wrap != line_wrap) { priv->wrap = line_wrap; clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP]); } } /** * clutter_text_set_line_wrap_mode: * @self: a #ClutterText * @wrap_mode: the line wrapping mode * * If line wrapping is enabled (see clutter_text_set_line_wrap()) this * function controls how the line wrapping is performed. The default is * %PANGO_WRAP_WORD which means wrap on word boundaries. * * Since: 1.0 */ void clutter_text_set_line_wrap_mode (ClutterText *self, PangoWrapMode wrap_mode) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->wrap_mode != wrap_mode) { priv->wrap_mode = wrap_mode; clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_WRAP_MODE]); } } /** * clutter_text_get_line_wrap_mode: * @self: a #ClutterText * * Retrieves the line wrap mode used by the #ClutterText actor. * * See clutter_text_set_line_wrap_mode (). * * Return value: the wrap mode used by the #ClutterText * * Since: 1.0 */ PangoWrapMode clutter_text_get_line_wrap_mode (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_WRAP_WORD); return self->priv->wrap_mode; } static gboolean attr_list_equal (PangoAttrList *old_attrs, PangoAttrList *new_attrs) { PangoAttrIterator *i, *j; gboolean equal = TRUE; if (old_attrs == new_attrs) return TRUE; if (old_attrs == NULL || new_attrs == NULL) return FALSE; i = pango_attr_list_get_iterator (old_attrs); j = pango_attr_list_get_iterator (new_attrs); do { GSList *old_attributes, *new_attributes, *a, *b; old_attributes = pango_attr_iterator_get_attrs (i); new_attributes = pango_attr_iterator_get_attrs (j); for (a = old_attributes, b = new_attributes; a != NULL && b != NULL && equal; a = a->next, b = b->next) { if (!pango_attribute_equal (a->data, b->data)) equal = FALSE; } if (a != NULL || b != NULL) equal = FALSE; g_slist_free_full (old_attributes, (GDestroyNotify) pango_attribute_destroy); g_slist_free_full (new_attributes, (GDestroyNotify) pango_attribute_destroy); } while (equal && pango_attr_iterator_next (i) && pango_attr_iterator_next (j)); if (pango_attr_iterator_next (i) || pango_attr_iterator_next (j)) equal = FALSE; pango_attr_iterator_destroy (i); pango_attr_iterator_destroy (j); return equal; } /** * clutter_text_set_attributes: * @self: a #ClutterText * @attrs: (allow-none): a #PangoAttrList or %NULL to unset the attributes * * Sets the attributes list that are going to be applied to the * #ClutterText contents. * * The #ClutterText actor will take a reference on the #PangoAttrList * passed to this function. * * Since: 1.0 */ void clutter_text_set_attributes (ClutterText *self, PangoAttrList *attrs) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (attr_list_equal (priv->attrs, attrs)) return; if (attrs) pango_attr_list_ref (attrs); if (priv->attrs) pango_attr_list_unref (priv->attrs); priv->attrs = attrs; /* Clear the effective attributes so they will be regenerated when a layout is created */ if (priv->effective_attrs) { pango_attr_list_unref (priv->effective_attrs); priv->effective_attrs = NULL; } clutter_text_dirty_cache (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ATTRIBUTES]); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } /** * clutter_text_get_attributes: * @self: a #ClutterText * * Gets the attribute list that was set on the #ClutterText actor * clutter_text_set_attributes(), if any. * * Return value: (transfer none): the attribute list, or %NULL if none was set. The * returned value is owned by the #ClutterText and should not be unreferenced. * * Since: 1.0 */ PangoAttrList * clutter_text_get_attributes (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); return self->priv->attrs; } /** * clutter_text_set_line_alignment: * @self: a #ClutterText * @alignment: A #PangoAlignment * * Sets the way that the lines of a wrapped label are aligned with * respect to each other. This does not affect the overall alignment * of the label within its allocated or specified width. * * To align a #ClutterText actor you should add it to a container * that supports alignment, or use the anchor point. * * Since: 1.0 */ void clutter_text_set_line_alignment (ClutterText *self, PangoAlignment alignment) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->alignment != alignment) { priv->alignment = alignment; clutter_text_queue_redraw_or_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_LINE_ALIGNMENT]); } } /** * clutter_text_get_line_alignment: * @self: a #ClutterText * * Retrieves the alignment of a #ClutterText, as set by * clutter_text_set_line_alignment(). * * Return value: a #PangoAlignment * * Since: 1.0 */ PangoAlignment clutter_text_get_line_alignment (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), PANGO_ALIGN_LEFT); return self->priv->alignment; } /** * clutter_text_set_use_markup: * @self: a #ClutterText * @setting: %TRUE if the text should be parsed for markup. * * Sets whether the contents of the #ClutterText actor contains markup * in Pango's text markup language. * * Setting #ClutterText:use-markup on an editable #ClutterText will * not have any effect except hiding the markup. * * See also #ClutterText:use-markup. * * Since: 1.0 */ void clutter_text_set_use_markup (ClutterText *self, gboolean setting) { const gchar *text; g_return_if_fail (CLUTTER_IS_TEXT (self)); text = clutter_text_buffer_get_text (get_buffer (self)); clutter_text_set_use_markup_internal (self, setting); if (setting) clutter_text_set_markup_internal (self, text); clutter_text_queue_redraw_or_relayout (self); } /** * clutter_text_get_use_markup: * @self: a #ClutterText * * Retrieves whether the contents of the #ClutterText actor should be * parsed for the Pango text markup. * * Return value: %TRUE if the contents will be parsed for markup * * Since: 1.0 */ gboolean clutter_text_get_use_markup (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->use_markup; } /** * clutter_text_set_justify: * @self: a #ClutterText * @justify: whether the text should be justified * * Sets whether the text of the #ClutterText actor should be justified * on both margins. This setting is ignored if Clutter is compiled * against Pango < 1.18. * * Since: 1.0 */ void clutter_text_set_justify (ClutterText *self, gboolean justify) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->justify != justify) { priv->justify = justify; clutter_text_queue_redraw_or_relayout (self); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_JUSTIFY]); } } /** * clutter_text_get_justify: * @self: a #ClutterText * * Retrieves whether the #ClutterText actor should justify its contents * on both margins. * * Return value: %TRUE if the text should be justified * * Since: 0.6 */ gboolean clutter_text_get_justify (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->justify; } /** * clutter_text_get_cursor_position: * @self: a #ClutterText * * Retrieves the cursor position. * * Return value: the cursor position, in characters * * Since: 1.0 */ gint clutter_text_get_cursor_position (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), -1); return self->priv->position; } /** * clutter_text_set_cursor_position: * @self: a #ClutterText * @position: the new cursor position, in characters * * Sets the cursor of a #ClutterText actor at @position. * * The position is expressed in characters, not in bytes. * * Since: 1.0 */ void clutter_text_set_cursor_position (ClutterText *self, gint position) { ClutterTextPrivate *priv; gint len; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->position == position) return; len = clutter_text_buffer_get_length (get_buffer (self)); if (position < 0 || position >= len) priv->position = -1; else priv->position = position; /* Forget the target x position so that it will be recalculated next time the cursor is moved up or down */ priv->x_pos = -1; clutter_text_queue_redraw (CLUTTER_ACTOR (self)); /* XXX:2.0 - remove */ g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_POSITION]); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_POSITION]); } /** * clutter_text_set_cursor_size: * @self: a #ClutterText * @size: the size of the cursor, in pixels, or -1 to use the * default value * * Sets the size of the cursor of a #ClutterText. The cursor * will only be visible if the #ClutterText:cursor-visible property * is set to %TRUE. * * Since: 1.0 */ void clutter_text_set_cursor_size (ClutterText *self, gint size) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->cursor_size != size) { if (size < 0) size = DEFAULT_CURSOR_SIZE; priv->cursor_size = size; clutter_text_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_CURSOR_SIZE]); } } /** * clutter_text_get_cursor_size: * @self: a #ClutterText * * Retrieves the size of the cursor of a #ClutterText actor. * * Return value: the size of the cursor, in pixels * * Since: 1.0 */ guint clutter_text_get_cursor_size (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), DEFAULT_CURSOR_SIZE); return self->priv->cursor_size; } /** * clutter_text_set_password_char: * @self: a #ClutterText * @wc: a Unicode character, or 0 to unset the password character * * Sets the character to use in place of the actual text in a * password text actor. * * If @wc is 0 the text will be displayed as it is entered in the * #ClutterText actor. * * Since: 1.0 */ void clutter_text_set_password_char (ClutterText *self, gunichar wc) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->password_char != wc) { priv->password_char = wc; clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_PASSWORD_CHAR]); } } /** * clutter_text_get_password_char: * @self: a #ClutterText * * Retrieves the character to use in place of the actual text * as set by clutter_text_set_password_char(). * * Return value: a Unicode character or 0 if the password * character is not set * * Since: 1.0 */ gunichar clutter_text_get_password_char (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return self->priv->password_char; } /** * clutter_text_set_max_length: * @self: a #ClutterText * @max: the maximum number of characters allowed in the text actor; 0 * to disable or -1 to set the length of the current string * * Sets the maximum allowed length of the contents of the actor. If the * current contents are longer than the given length, then they will be * truncated to fit. * * Since: 1.0 */ void clutter_text_set_max_length (ClutterText *self, gint max) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_buffer_set_max_length (get_buffer (self), max); } /** * clutter_text_get_max_length: * @self: a #ClutterText * * Gets the maximum length of text that can be set into a text actor. * * See clutter_text_set_max_length(). * * Return value: the maximum number of characters. * * Since: 1.0 */ gint clutter_text_get_max_length (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return clutter_text_buffer_get_max_length (get_buffer (self)); } static void clutter_text_real_insert_text (ClutterText *self, guint start_pos, const gchar *chars, guint n_chars) { gsize n_bytes; n_bytes = g_utf8_offset_to_pointer (chars, n_chars) - chars; /* * insert-text is emitted here instead of as part of a * buffer_inserted_text() callback because that should be emitted * before the buffer changes, while ClutterTextBuffer::deleted-text * is emitter after. See BG#722220 for more info. */ g_signal_emit (self, text_signals[INSERT_TEXT], 0, chars, n_bytes, &start_pos); /* * The actual insertion from the buffer. This will end firing the * following signal handlers: buffer_inserted_text(), * buffer_notify_text(), buffer_notify_max_length() */ clutter_text_buffer_insert_text (get_buffer (self), start_pos, chars, n_chars); } /** * clutter_text_insert_unichar: * @self: a #ClutterText * @wc: a Unicode character * * Inserts @wc at the current cursor position of a * #ClutterText actor. * * Since: 1.0 */ void clutter_text_insert_unichar (ClutterText *self, gunichar wc) { ClutterTextPrivate *priv; GString *new; priv = self->priv; new = g_string_new (""); g_string_append_unichar (new, wc); clutter_text_real_insert_text (self, priv->position, new->str, 1); g_string_free (new, TRUE); } /** * clutter_text_insert_text: * @self: a #ClutterText * @text: the text to be inserted * @position: the position of the insertion, or -1 * * Inserts @text into a #ClutterActor at the given position. * * If @position is a negative number, the text will be appended * at the end of the current contents of the #ClutterText. * * The position is expressed in characters, not in bytes. * * Since: 1.0 */ void clutter_text_insert_text (ClutterText *self, const gchar *text, gssize position) { g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (text != NULL); clutter_text_real_insert_text (self, position, text, g_utf8_strlen (text, -1)); } static void clutter_text_real_delete_text (ClutterText *self, gssize start_pos, gssize end_pos) { /* * delete-text is emitted here instead of as part of a * buffer_deleted_text() callback because that should be emitted * before the buffer changes, while ClutterTextBuffer::deleted-text * is emitter after. See BG#722220 for more info. */ g_signal_emit (self, text_signals[DELETE_TEXT], 0, start_pos, end_pos); /* * The actual deletion from the buffer. This will end firing the * following signal handlers: buffer_deleted_text(), * buffer_notify_text(), buffer_notify_max_length() */ clutter_text_buffer_delete_text (get_buffer (self), start_pos, end_pos - start_pos); } /** * clutter_text_delete_text: * @self: a #ClutterText * @start_pos: starting position * @end_pos: ending position * * Deletes the text inside a #ClutterText actor between @start_pos * and @end_pos. * * The starting and ending positions are expressed in characters, * not in bytes. * * Since: 1.0 */ void clutter_text_delete_text (ClutterText *self, gssize start_pos, gssize end_pos) { g_return_if_fail (CLUTTER_IS_TEXT (self)); clutter_text_real_delete_text (self, start_pos, end_pos); } /** * clutter_text_delete_chars: * @self: a #ClutterText * @n_chars: the number of characters to delete * * Deletes @n_chars inside a #ClutterText actor, starting from the * current cursor position. * * Somewhat awkwardly, the cursor position is decremented by the same * number of characters you've deleted. * * Since: 1.0 */ void clutter_text_delete_chars (ClutterText *self, guint n_chars) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; clutter_text_real_delete_text (self, priv->position, priv->position + n_chars); if (priv->position > 0) clutter_text_set_cursor_position (self, priv->position - n_chars); } /** * clutter_text_get_chars: * @self: a #ClutterText * @start_pos: start of text, in characters * @end_pos: end of text, in characters * * Retrieves the contents of the #ClutterText actor between * @start_pos and @end_pos, but not including @end_pos. * * The positions are specified in characters, not in bytes. * * Return value: a newly allocated string with the contents of * the text actor between the specified positions. Use g_free() * to free the resources when done * * Since: 1.0 */ gchar * clutter_text_get_chars (ClutterText *self, gssize start_pos, gssize end_pos) { gint start_index, end_index; guint n_chars; const gchar *text; g_return_val_if_fail (CLUTTER_IS_TEXT (self), NULL); n_chars = clutter_text_buffer_get_length (get_buffer (self)); text = clutter_text_buffer_get_text (get_buffer (self)); if (end_pos < 0) end_pos = n_chars; start_pos = MIN (n_chars, start_pos); end_pos = MIN (n_chars, end_pos); start_index = g_utf8_offset_to_pointer (text, start_pos) - text; end_index = g_utf8_offset_to_pointer (text, end_pos) - text; return g_strndup (text + start_index, end_index - start_index); } /** * clutter_text_set_single_line_mode: * @self: a #ClutterText * @single_line: whether to enable single line mode * * Sets whether a #ClutterText actor should be in single line mode * or not. Only editable #ClutterTexts can be in single line * mode. * * A text actor in single line mode will not wrap text and will clip * the visible area to the predefined size. The contents of the * text actor will scroll to display the end of the text if its length * is bigger than the allocated width. * * When setting the single line mode the #ClutterText:activatable * property is also set as a side effect. Instead of entering a new * line character, the text actor will emit the #ClutterText::activate * signal. * * Since: 1.0 */ void clutter_text_set_single_line_mode (ClutterText *self, gboolean single_line) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (priv->single_line_mode != single_line) { g_object_freeze_notify (G_OBJECT (self)); priv->single_line_mode = single_line; if (priv->single_line_mode) { priv->activatable = TRUE; g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_ACTIVATABLE]); } clutter_text_dirty_cache (self); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SINGLE_LINE_MODE]); g_object_thaw_notify (G_OBJECT (self)); } } /** * clutter_text_get_single_line_mode: * @self: a #ClutterText * * Retrieves whether the #ClutterText actor is in single line mode. * * Return value: %TRUE if the #ClutterText actor is in single line mode * * Since: 1.0 */ gboolean clutter_text_get_single_line_mode (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->single_line_mode; } /** * clutter_text_set_preedit_string: * @self: a #ClutterText * @preedit_str: (allow-none): the pre-edit string, or %NULL to unset it * @preedit_attrs: (allow-none): the pre-edit string attributes * @cursor_pos: the cursor position for the pre-edit string * * Sets, or unsets, the pre-edit string. This function is useful * for input methods to display a string (with eventual specific * Pango attributes) before it is entered inside the #ClutterText * buffer. * * The preedit string and attributes are ignored if the #ClutterText * actor is not editable. * * This function should not be used by applications * * Since: 1.2 */ void clutter_text_set_preedit_string (ClutterText *self, const gchar *preedit_str, PangoAttrList *preedit_attrs, guint cursor_pos) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; g_free (priv->preedit_str); priv->preedit_str = NULL; if (priv->preedit_attrs != NULL) { pango_attr_list_unref (priv->preedit_attrs); priv->preedit_attrs = NULL; } priv->preedit_n_chars = 0; priv->preedit_cursor_pos = 0; if (preedit_str == NULL || *preedit_str == '\0') priv->preedit_set = FALSE; else { priv->preedit_str = g_strdup (preedit_str); if (priv->preedit_str != NULL) priv->preedit_n_chars = g_utf8_strlen (priv->preedit_str, -1); else priv->preedit_n_chars = 0; if (preedit_attrs != NULL) priv->preedit_attrs = pango_attr_list_ref (preedit_attrs); priv->preedit_cursor_pos = CLAMP (cursor_pos, 0, priv->preedit_n_chars); priv->preedit_set = TRUE; } clutter_text_queue_redraw_or_relayout (self); } /** * clutter_text_get_layout_offsets: * @self: a #ClutterText * @x: (out): location to store X offset of layout, or %NULL * @y: (out): location to store Y offset of layout, or %NULL * * Obtains the coordinates where the #ClutterText will draw the #PangoLayout * representing the text. * * Since: 1.8 */ void clutter_text_get_layout_offsets (ClutterText *self, gint *x, gint *y) { ClutterTextPrivate *priv; g_return_if_fail (CLUTTER_IS_TEXT (self)); priv = self->priv; if (x != NULL) *x = priv->text_logical_x; if (y != NULL) *y = priv->text_logical_y; } /** * clutter_text_get_cursor_rect: * @self: a #ClutterText * @rect: (out caller-allocates): return location of a #ClutterRect * * Retrieves the rectangle that contains the cursor. * * The coordinates of the rectangle's origin are in actor-relative * coordinates. * * Since: 1.16 */ void clutter_text_get_cursor_rect (ClutterText *self, graphene_rect_t *rect) { g_return_if_fail (CLUTTER_IS_TEXT (self)); g_return_if_fail (rect != NULL); *rect = self->priv->cursor_rect; } void clutter_text_set_input_hints (ClutterText *self, ClutterInputContentHintFlags hints) { g_return_if_fail (CLUTTER_IS_TEXT (self)); self->priv->input_hints = hints; if (clutter_input_focus_is_focused (self->priv->input_focus)) clutter_input_focus_set_content_hints (self->priv->input_focus, hints); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_INPUT_HINTS]); } ClutterInputContentHintFlags clutter_text_get_input_hints (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return self->priv->input_hints; } void clutter_text_set_input_purpose (ClutterText *self, ClutterInputContentPurpose purpose) { g_return_if_fail (CLUTTER_IS_TEXT (self)); self->priv->input_purpose = purpose; if (clutter_input_focus_is_focused (self->priv->input_focus)) clutter_input_focus_set_content_purpose (self->priv->input_focus, purpose); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_INPUT_PURPOSE]); } ClutterInputContentPurpose clutter_text_get_input_purpose (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), 0); return self->priv->input_purpose; } gboolean clutter_text_has_preedit (ClutterText *self) { g_return_val_if_fail (CLUTTER_IS_TEXT (self), FALSE); return self->priv->preedit_set; } muffin-6.4.1/clutter/clutter/clutter-stage-view.h0000664000175000017500000000504514723361714021016 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_STAGE_VIEW_H__ #define __CLUTTER_STAGE_VIEW_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include #include #include #include "clutter-macros.h" #define CLUTTER_TYPE_STAGE_VIEW (clutter_stage_view_get_type ()) CLUTTER_EXPORT G_DECLARE_DERIVABLE_TYPE (ClutterStageView, clutter_stage_view, CLUTTER, STAGE_VIEW, GObject) struct _ClutterStageViewClass { GObjectClass parent_class; void (* setup_offscreen_blit_pipeline) (ClutterStageView *view, CoglPipeline *pipeline); void (* get_offscreen_transformation_matrix) (ClutterStageView *view, CoglMatrix *matrix); }; CLUTTER_EXPORT void clutter_stage_view_get_layout (ClutterStageView *view, cairo_rectangle_int_t *rect); CLUTTER_EXPORT CoglFramebuffer *clutter_stage_view_get_framebuffer (ClutterStageView *view); CLUTTER_EXPORT CoglFramebuffer *clutter_stage_view_get_onscreen (ClutterStageView *view); CLUTTER_EXPORT void clutter_stage_view_invalidate_offscreen_blit_pipeline (ClutterStageView *view); CLUTTER_EXPORT void clutter_stage_view_transform_to_onscreen (ClutterStageView *view, gfloat *x, gfloat *y); CLUTTER_EXPORT float clutter_stage_view_get_scale (ClutterStageView *view); CLUTTER_EXPORT void clutter_stage_view_get_offscreen_transformation_matrix (ClutterStageView *view, CoglMatrix *matrix); #endif /* __CLUTTER_STAGE_VIEW_H__ */ muffin-6.4.1/clutter/clutter/clutter-shader-types.c0000664000175000017500000003520614723361714021350 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * Chris Lord * * Copyright (C) 2008 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #include "clutter-build-config.h" #include #include #include "clutter-shader-types.h" #include "clutter-private.h" static GTypeInfo shader_float_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, }; static GTypeFundamentalInfo shader_float_finfo = { 0, }; static GTypeInfo shader_int_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, }; static GTypeFundamentalInfo shader_int_finfo = { 0, }; static GTypeInfo shader_matrix_info = { 0, NULL, NULL, NULL, NULL, NULL, 0, 0, NULL, NULL, }; static GTypeFundamentalInfo shader_matrix_finfo = { 0, }; struct _ClutterShaderFloat { gint size; float value[4]; }; struct _ClutterShaderInt { gint size; int value[4]; }; struct _ClutterShaderMatrix { gint size; float value[16]; }; static gpointer clutter_value_peek_pointer (const GValue *value) { return value->data[0].v_pointer; } /* Float */ static void clutter_value_init_shader_float (GValue *value) { value->data[0].v_pointer = g_slice_new0 (ClutterShaderFloat); } static void clutter_value_free_shader_float (GValue *value) { g_slice_free (ClutterShaderFloat, value->data[0].v_pointer); } static void clutter_value_copy_shader_float (const GValue *src, GValue *dest) { dest->data[0].v_pointer = g_slice_dup (ClutterShaderFloat, src->data[0].v_pointer); } static gchar * clutter_value_collect_shader_float (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint float_count = collect_values[0].v_int; const float *floats = collect_values[1].v_pointer; if (!floats) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); clutter_value_init_shader_float (value); clutter_value_set_shader_float (value, float_count, floats); return NULL; } static gchar * clutter_value_lcopy_shader_float (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint *float_count = collect_values[0].v_pointer; float **floats = collect_values[1].v_pointer; ClutterShaderFloat *shader_float = value->data[0].v_pointer; if (!float_count || !floats) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); *float_count = shader_float->size; *floats = g_memdup2 (shader_float->value, shader_float->size * sizeof (float)); return NULL; } static const GTypeValueTable _clutter_shader_float_value_table = { clutter_value_init_shader_float, clutter_value_free_shader_float, clutter_value_copy_shader_float, clutter_value_peek_pointer, "ip", clutter_value_collect_shader_float, "pp", clutter_value_lcopy_shader_float }; GType clutter_shader_float_get_type (void) { static GType _clutter_shader_float_type = 0; if (G_UNLIKELY (_clutter_shader_float_type == 0)) { shader_float_info.value_table = & _clutter_shader_float_value_table; _clutter_shader_float_type = g_type_register_fundamental (g_type_fundamental_next (), I_("ClutterShaderFloat"), &shader_float_info, &shader_float_finfo, 0); } return _clutter_shader_float_type; } /* Integer */ static void clutter_value_init_shader_int (GValue *value) { value->data[0].v_pointer = g_slice_new0 (ClutterShaderInt); } static void clutter_value_free_shader_int (GValue *value) { g_slice_free (ClutterShaderInt, value->data[0].v_pointer); } static void clutter_value_copy_shader_int (const GValue *src, GValue *dest) { dest->data[0].v_pointer = g_slice_dup (ClutterShaderInt, src->data[0].v_pointer); } static gchar * clutter_value_collect_shader_int (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint int_count = collect_values[0].v_int; const int *ints = collect_values[1].v_pointer; if (!ints) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); clutter_value_init_shader_int (value); clutter_value_set_shader_int (value, int_count, ints); return NULL; } static gchar * clutter_value_lcopy_shader_int (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint *int_count = collect_values[0].v_pointer; int **ints = collect_values[1].v_pointer; ClutterShaderInt *shader_int = value->data[0].v_pointer; if (!int_count || !ints) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); *int_count = shader_int->size; *ints = g_memdup2 (shader_int->value, shader_int->size * sizeof (int)); return NULL; } static const GTypeValueTable _clutter_shader_int_value_table = { clutter_value_init_shader_int, clutter_value_free_shader_int, clutter_value_copy_shader_int, clutter_value_peek_pointer, "ip", clutter_value_collect_shader_int, "pp", clutter_value_lcopy_shader_int }; GType clutter_shader_int_get_type (void) { static GType _clutter_shader_int_type = 0; if (G_UNLIKELY (_clutter_shader_int_type == 0)) { shader_int_info.value_table = & _clutter_shader_int_value_table; _clutter_shader_int_type = g_type_register_fundamental (g_type_fundamental_next (), I_("ClutterShaderInt"), &shader_int_info, &shader_int_finfo, 0); } return _clutter_shader_int_type; } /* Matrix */ static void clutter_value_init_shader_matrix (GValue *value) { value->data[0].v_pointer = g_slice_new0 (ClutterShaderMatrix); } static void clutter_value_free_shader_matrix (GValue *value) { g_slice_free (ClutterShaderMatrix, value->data[0].v_pointer); } static void clutter_value_copy_shader_matrix (const GValue *src, GValue *dest) { dest->data[0].v_pointer = g_slice_dup (ClutterShaderMatrix, src->data[0].v_pointer); } static gchar * clutter_value_collect_shader_matrix (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint float_count = collect_values[0].v_int; const float *floats = collect_values[1].v_pointer; if (!floats) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); clutter_value_init_shader_matrix (value); clutter_value_set_shader_matrix (value, float_count, floats); return NULL; } static gchar * clutter_value_lcopy_shader_matrix (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { gint *float_count = collect_values[0].v_pointer; float **floats = collect_values[1].v_pointer; ClutterShaderFloat *shader_float = value->data[0].v_pointer; if (!float_count || !floats) return g_strdup_printf ("value location for '%s' passed as NULL", G_VALUE_TYPE_NAME (value)); *float_count = shader_float->size; *floats = g_memdup2 (shader_float->value, shader_float->size * shader_float->size * sizeof (float)); return NULL; } static const GTypeValueTable _clutter_shader_matrix_value_table = { clutter_value_init_shader_matrix, clutter_value_free_shader_matrix, clutter_value_copy_shader_matrix, clutter_value_peek_pointer, "ip", clutter_value_collect_shader_matrix, "pp", clutter_value_lcopy_shader_matrix }; GType clutter_shader_matrix_get_type (void) { static GType _clutter_shader_matrix_type = 0; if (G_UNLIKELY (_clutter_shader_matrix_type == 0)) { shader_matrix_info.value_table = & _clutter_shader_matrix_value_table; _clutter_shader_matrix_type = g_type_register_fundamental (g_type_fundamental_next (), I_("ClutterShaderMatrix"), &shader_matrix_info, &shader_matrix_finfo, 0); } return _clutter_shader_matrix_type; } /* Utility functions */ /** * clutter_value_set_shader_float: * @value: a #GValue * @size: number of floating point values in @floats * @floats: (array length=size): an array of floating point values * * Sets @floats as the contents of @value. The passed #GValue * must have been initialized using %CLUTTER_TYPE_SHADER_FLOAT. * * Since: 0.8 */ void clutter_value_set_shader_float (GValue *value, gint size, const gfloat *floats) { ClutterShaderFloat *shader_float; gint i; g_return_if_fail (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (value)); g_return_if_fail (size <= 4); shader_float = value->data[0].v_pointer; shader_float->size = size; for (i = 0; i < size; i++) shader_float->value[i] = floats[i]; } /** * clutter_value_set_shader_int: * @value: a #GValue * @size: number of integer values in @ints * @ints: (array length=size): an array of integer values * * Sets @ints as the contents of @value. The passed #GValue * must have been initialized using %CLUTTER_TYPE_SHADER_INT. * * Since: 0.8 */ void clutter_value_set_shader_int (GValue *value, gint size, const gint *ints) { ClutterShaderInt *shader_int; gint i; g_return_if_fail (CLUTTER_VALUE_HOLDS_SHADER_INT (value)); g_return_if_fail (size <= 4); shader_int = value->data[0].v_pointer; shader_int->size = size; for (i = 0; i < size; i++) shader_int->value[i] = ints[i]; } /** * clutter_value_set_shader_matrix: * @value: a #GValue * @size: number of floating point values in @floats * @matrix: (array length=size): a matrix of floating point values * * Sets @matrix as the contents of @value. The passed #GValue * must have been initialized using %CLUTTER_TYPE_SHADER_MATRIX. * * Since: 0.8 */ void clutter_value_set_shader_matrix (GValue *value, gint size, const gfloat *matrix) { ClutterShaderMatrix *shader_matrix; gint i; g_return_if_fail (CLUTTER_VALUE_HOLDS_SHADER_MATRIX (value)); g_return_if_fail (size <= 4); shader_matrix = value->data[0].v_pointer; shader_matrix->size = size; for (i = 0; i < size * size; i++) shader_matrix->value[i] = matrix[i]; } /** * clutter_value_get_shader_float: * @value: a #GValue * @length: (out): return location for the number of returned floating * point values, or %NULL * * Retrieves the list of floating point values stored inside * the passed #GValue. @value must have been initialized with * %CLUTTER_TYPE_SHADER_FLOAT. * * Return value: (array length=length): the pointer to a list of * floating point values. The returned value is owned by the * #GValue and should never be modified or freed. * * Since: 0.8 */ const gfloat * clutter_value_get_shader_float (const GValue *value, gsize *length) { ClutterShaderFloat *shader_float; g_return_val_if_fail (CLUTTER_VALUE_HOLDS_SHADER_FLOAT (value), NULL); shader_float = value->data[0].v_pointer; if (length) *length = shader_float->size; return shader_float->value; } /** * clutter_value_get_shader_int: * @value: a #GValue * @length: (out): return location for the number of returned integer * values, or %NULL * * Retrieves the list of integer values stored inside the passed * #GValue. @value must have been initialized with * %CLUTTER_TYPE_SHADER_INT. * * Return value: (array length=length): the pointer to a list of * integer values. The returned value is owned by the #GValue and * should never be modified or freed. * * Since: 0.8 */ const gint * clutter_value_get_shader_int (const GValue *value, gsize *length) { ClutterShaderInt *shader_int; g_return_val_if_fail (CLUTTER_VALUE_HOLDS_SHADER_INT (value), NULL); shader_int = value->data[0].v_pointer; if (length) *length = shader_int->size; return shader_int->value; } /** * clutter_value_get_shader_matrix: * @value: a #GValue * @length: (out): return location for the number of returned floating * point values, or %NULL * * Retrieves a matrix of floating point values stored inside * the passed #GValue. @value must have been initialized with * %CLUTTER_TYPE_SHADER_MATRIX. * * Return value: (array length=length) (transfer none): the pointer to a matrix * of floating point values. The returned value is owned by the #GValue and * should never be modified or freed. * * Since: 0.8 */ const gfloat * clutter_value_get_shader_matrix (const GValue *value, gsize *length) { ClutterShaderMatrix *shader_matrix; g_return_val_if_fail (CLUTTER_VALUE_HOLDS_SHADER_MATRIX (value), NULL); shader_matrix = value->data[0].v_pointer; if (length) *length = shader_matrix->size; return shader_matrix->value; } muffin-6.4.1/clutter/clutter/clutter-offscreen-effect.c0000664000175000017500000006147214723361714022150 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authors: * Emmanuele Bassi * Robert Bragg */ /** * SECTION:clutter-offscreen-effect * @short_description: Base class for effects using offscreen buffers * @see_also: #ClutterBlurEffect, #ClutterEffect * * #ClutterOffscreenEffect is an abstract class that can be used by * #ClutterEffect sub-classes requiring access to an offscreen buffer. * * Some effects, like the fragment shader based effects, can only use GL * textures, and in order to apply those effects to any kind of actor they * require that all drawing operations are applied to an offscreen framebuffer * that gets redirected to a texture. * * #ClutterOffscreenEffect provides all the heavy-lifting for creating the * offscreen framebuffer, the redirection and the final paint of the texture on * the desired stage. * * #ClutterOffscreenEffect is available since Clutter 1.4 * * ## Implementing a ClutterOffscreenEffect * * Creating a sub-class of #ClutterOffscreenEffect requires, in case * of overriding the #ClutterEffect virtual functions, to chain up to the * #ClutterOffscreenEffect's implementation. * * On top of the #ClutterEffect's virtual functions, * #ClutterOffscreenEffect also provides a #ClutterOffscreenEffectClass.paint_target() * function, which encapsulates the effective painting of the texture that * contains the result of the offscreen redirection. * * The size of the target material is defined to be as big as the * transformed size of the #ClutterActor using the offscreen effect. * Sub-classes of #ClutterOffscreenEffect can change the texture creation * code to provide bigger textures by overriding the * #ClutterOffscreenEffectClass.create_texture() virtual function; no chain up * to the #ClutterOffscreenEffect implementation is required in this * case. */ #include "clutter-build-config.h" #include "clutter-offscreen-effect.h" #include #include "cogl/cogl.h" #include "clutter-actor-private.h" #include "clutter-debug.h" #include "clutter-private.h" #include "clutter-stage-private.h" #include "clutter-paint-context-private.h" #include "clutter-paint-volume-private.h" #include "clutter-actor-box-private.h" struct _ClutterOffscreenEffectPrivate { CoglHandle offscreen; CoglPipeline *target; CoglHandle texture; ClutterActor *actor; ClutterActor *stage; graphene_point3d_t position; int fbo_offset_x; int fbo_offset_y; /* This is the calculated size of the fbo before being passed through create_texture(). This needs to be tracked separately so that we can detect when a different size is calculated and regenerate the fbo */ int target_width; int target_height; gint old_opacity_override; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (ClutterOffscreenEffect, clutter_offscreen_effect, CLUTTER_TYPE_EFFECT) static void clutter_offscreen_effect_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (meta); ClutterOffscreenEffectPrivate *priv = self->priv; ClutterActorMetaClass *meta_class; meta_class = CLUTTER_ACTOR_META_CLASS (clutter_offscreen_effect_parent_class); meta_class->set_actor (meta, actor); /* clear out the previous state */ g_clear_pointer (&priv->offscreen, cogl_object_unref); /* we keep a back pointer here, to avoid going through the ActorMeta */ priv->actor = clutter_actor_meta_get_actor (meta); } static CoglHandle clutter_offscreen_effect_real_create_texture (ClutterOffscreenEffect *effect, gfloat width, gfloat height) { return cogl_texture_new_with_size (MAX (width, 1), MAX (height, 1), COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE); } static void ensure_pipeline_filter_for_scale (ClutterOffscreenEffect *self, float resource_scale) { CoglPipelineFilter filter; if (!self->priv->target) return; /* If no fractional scaling is set, we're always going to render the texture at a 1:1 texel:pixel ratio so, in such case we can use 'nearest' filtering to decrease the effects of rounding errors in the geometry calculation; if instead we we're using a global fractional scaling we need to make sure that we're using the default linear effect, not to create artifacts when scaling down the texture */ if (fmodf (resource_scale, 1.0f) == 0) filter = COGL_PIPELINE_FILTER_NEAREST; else filter = COGL_PIPELINE_FILTER_LINEAR; cogl_pipeline_set_layer_filters (self->priv->target, 0 /* layer_index */, filter, filter); } static gboolean update_fbo (ClutterEffect *effect, int target_width, int target_height, float resource_scale) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect); ClutterOffscreenEffectPrivate *priv = self->priv; priv->stage = clutter_actor_get_stage (priv->actor); if (priv->stage == NULL) { CLUTTER_NOTE (MISC, "The actor '%s' is not part of a stage", clutter_actor_get_name (priv->actor) == NULL ? G_OBJECT_TYPE_NAME (priv->actor) : clutter_actor_get_name (priv->actor)); return FALSE; } if (priv->target_width == target_width && priv->target_height == target_height && priv->offscreen != NULL) { ensure_pipeline_filter_for_scale (self, resource_scale); return TRUE; } if (priv->target == NULL) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); priv->target = cogl_pipeline_new (ctx); ensure_pipeline_filter_for_scale (self, resource_scale); } g_clear_pointer (&priv->texture, cogl_object_unref); g_clear_pointer (&priv->offscreen, cogl_object_unref); priv->texture = clutter_offscreen_effect_create_texture (self, target_width, target_height); if (priv->texture == NULL) return FALSE; cogl_pipeline_set_layer_texture (priv->target, 0, priv->texture); priv->target_width = target_width; priv->target_height = target_height; priv->offscreen = cogl_offscreen_new_to_texture (priv->texture); if (priv->offscreen == NULL) { g_warning ("%s: Unable to create an Offscreen buffer", G_STRLOC); cogl_object_unref (priv->target); priv->target = NULL; priv->target_width = 0; priv->target_height = 0; return FALSE; } return TRUE; } static gboolean clutter_offscreen_effect_pre_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect); ClutterOffscreenEffectPrivate *priv = self->priv; ClutterActorBox raw_box, box; ClutterActor *stage; CoglMatrix projection, old_modelview, modelview; const ClutterPaintVolume *volume; CoglColor transparent; gfloat stage_width, stage_height; gfloat target_width = -1, target_height = -1; CoglFramebuffer *framebuffer; gfloat resource_scale; gfloat ceiled_resource_scale; graphene_point3d_t local_offset; gfloat old_viewport[4]; local_offset = GRAPHENE_POINT3D_INIT (0.0f, 0.0f, 0.0f); if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (effect))) return FALSE; if (priv->actor == NULL) return FALSE; stage = _clutter_actor_get_stage_internal (priv->actor); clutter_actor_get_size (stage, &stage_width, &stage_height); if (_clutter_actor_get_real_resource_scale (priv->actor, &resource_scale)) { ceiled_resource_scale = ceilf (resource_scale); stage_width *= ceiled_resource_scale; stage_height *= ceiled_resource_scale; } else { /* We are sure we have a resource scale set to a good value at paint */ g_assert_not_reached (); } /* Get the minimal bounding box for what we want to paint, relative to the * parent of priv->actor. Note that we may actually be painting a clone of * priv->actor so we need to be careful to avoid querying the transformation * of priv->actor (like clutter_actor_get_paint_box would). Just stay in * local coordinates for now... */ volume = clutter_actor_get_paint_volume (priv->actor); if (volume) { ClutterPaintVolume mutable_volume; _clutter_paint_volume_copy_static (volume, &mutable_volume); _clutter_paint_volume_get_bounding_box (&mutable_volume, &raw_box); clutter_paint_volume_free (&mutable_volume); } else { clutter_actor_get_allocation_box (priv->actor, &raw_box); } box = raw_box; _clutter_actor_box_enlarge_for_effects (&box); priv->fbo_offset_x = box.x1 - raw_box.x1; priv->fbo_offset_y = box.y1 - raw_box.y1; clutter_actor_box_scale (&box, ceiled_resource_scale); clutter_actor_box_get_size (&box, &target_width, &target_height); target_width = ceilf (target_width); target_height = ceilf (target_height); /* First assert that the framebuffer is the right size... */ if (!update_fbo (effect, target_width, target_height, resource_scale)) return FALSE; framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_get_modelview_matrix (framebuffer, &old_modelview); clutter_paint_context_push_framebuffer (paint_context, priv->offscreen); /* We don't want the FBO contents to be transformed. That could waste memory * (e.g. during zoom), or result in something that's not rectangular (clipped * incorrectly). So drop the modelview matrix of the current paint chain. * This is fine since paint_texture runs with the same modelview matrix, * so it will come out correctly whenever that is used to put the FBO * contents on screen... */ clutter_actor_get_transform (priv->stage, &modelview); cogl_framebuffer_set_modelview_matrix (priv->offscreen, &modelview); /* Save the original viewport for calculating priv->position */ _clutter_stage_get_viewport (CLUTTER_STAGE (priv->stage), &old_viewport[0], &old_viewport[1], &old_viewport[2], &old_viewport[3]); /* Set up the viewport so that it has the same size as the stage (avoid * distortion), but translated to account for the FBO offset... */ cogl_framebuffer_set_viewport (priv->offscreen, -priv->fbo_offset_x, -priv->fbo_offset_y, stage_width, stage_height); /* Copy the stage's projection matrix across to the framebuffer */ _clutter_stage_get_projection_matrix (CLUTTER_STAGE (priv->stage), &projection); /* Now save the global position of the effect (not just of the actor). * It doesn't appear anyone actually uses this yet, but get_target_rect is * documented as returning it. So we should... */ _clutter_util_fully_transform_vertices (&old_modelview, &projection, old_viewport, &local_offset, &priv->position, 1); cogl_framebuffer_set_projection_matrix (priv->offscreen, &projection); cogl_color_init_from_4ub (&transparent, 0, 0, 0, 0); cogl_framebuffer_clear (priv->offscreen, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH, &transparent); cogl_framebuffer_push_matrix (priv->offscreen); /* Override the actor's opacity to fully opaque - we paint the offscreen * texture with the actor's paint opacity, so we need to do this to avoid * multiplying the opacity twice. */ priv->old_opacity_override = clutter_actor_get_opacity_override (priv->actor); clutter_actor_set_opacity_override (priv->actor, 0xff); return TRUE; } static void clutter_offscreen_effect_real_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterOffscreenEffectPrivate *priv = effect->priv; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); guint8 paint_opacity; paint_opacity = clutter_actor_get_paint_opacity (priv->actor); cogl_pipeline_set_color4ub (priv->target, paint_opacity, paint_opacity, paint_opacity, paint_opacity); /* At this point we are in stage coordinates translated so if * we draw our texture using a textured quad the size of the paint * box then we will overlay where the actor would have drawn if it * hadn't been redirected offscreen. */ cogl_framebuffer_draw_textured_rectangle (framebuffer, priv->target, 0, 0, cogl_texture_get_width (priv->texture), cogl_texture_get_height (priv->texture), 0.0, 0.0, 1.0, 1.0); } static void clutter_offscreen_effect_paint_texture (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { ClutterOffscreenEffectPrivate *priv = effect->priv; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglMatrix modelview; float resource_scale; cogl_framebuffer_push_matrix (framebuffer); /* The current modelview matrix is *almost* perfect already. It's only * missing a correction for the expanded FBO and offset rendering within... */ cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview); if (clutter_actor_get_resource_scale (priv->actor, &resource_scale) && resource_scale != 1.0f) { float paint_scale = 1.0f / resource_scale; cogl_matrix_scale (&modelview, paint_scale, paint_scale, 1); } cogl_matrix_translate (&modelview, priv->fbo_offset_x, priv->fbo_offset_y, 0.0f); cogl_framebuffer_set_modelview_matrix (framebuffer, &modelview); /* paint the target material; this is virtualized for * sub-classes that require special hand-holding */ clutter_offscreen_effect_paint_target (effect, paint_context); cogl_framebuffer_pop_matrix (framebuffer); } static void clutter_offscreen_effect_post_paint (ClutterEffect *effect, ClutterPaintContext *paint_context) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect); ClutterOffscreenEffectPrivate *priv = self->priv; CoglFramebuffer *framebuffer; if (priv->offscreen == NULL || priv->target == NULL || priv->actor == NULL) return; /* Restore the previous opacity override */ clutter_actor_set_opacity_override (priv->actor, priv->old_opacity_override); framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_pop_matrix (framebuffer); clutter_paint_context_pop_framebuffer (paint_context); clutter_offscreen_effect_paint_texture (self, paint_context); } static void clutter_offscreen_effect_paint (ClutterEffect *effect, ClutterPaintContext *paint_context, ClutterEffectPaintFlags flags) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (effect); ClutterOffscreenEffectPrivate *priv = self->priv; if (flags & CLUTTER_EFFECT_PAINT_BYPASS_EFFECT) { clutter_actor_continue_paint (priv->actor, paint_context); cogl_clear_object (&priv->offscreen); return; } /* If we've already got a cached image and the actor hasn't been redrawn * then we can just use the cached image in the FBO. */ if (priv->offscreen == NULL || (flags & CLUTTER_EFFECT_PAINT_ACTOR_DIRTY)) { ClutterEffectClass *effect_class = CLUTTER_EFFECT_GET_CLASS (effect); gboolean pre_paint_succeeded; pre_paint_succeeded = effect_class->pre_paint (effect, paint_context); clutter_actor_continue_paint (priv->actor, paint_context); if (pre_paint_succeeded) effect_class->post_paint (effect, paint_context); else g_clear_pointer (&priv->offscreen, cogl_object_unref); } else clutter_offscreen_effect_paint_texture (self, paint_context); } static void clutter_offscreen_effect_notify (GObject *gobject, GParamSpec *pspec) { ClutterOffscreenEffect *offscreen_effect = CLUTTER_OFFSCREEN_EFFECT (gobject); ClutterOffscreenEffectPrivate *priv = offscreen_effect->priv; if (strcmp (pspec->name, "enabled") == 0) g_clear_pointer (&priv->offscreen, cogl_object_unref); G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->notify (gobject, pspec); } static void clutter_offscreen_effect_finalize (GObject *gobject) { ClutterOffscreenEffect *self = CLUTTER_OFFSCREEN_EFFECT (gobject); ClutterOffscreenEffectPrivate *priv = self->priv; g_clear_pointer (&priv->offscreen, cogl_object_unref); g_clear_pointer (&priv->texture, cogl_object_unref); g_clear_pointer (&priv->target, cogl_object_unref); G_OBJECT_CLASS (clutter_offscreen_effect_parent_class)->finalize (gobject); } static void clutter_offscreen_effect_class_init (ClutterOffscreenEffectClass *klass) { ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); ClutterEffectClass *effect_class = CLUTTER_EFFECT_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); klass->create_texture = clutter_offscreen_effect_real_create_texture; klass->paint_target = clutter_offscreen_effect_real_paint_target; meta_class->set_actor = clutter_offscreen_effect_set_actor; effect_class->pre_paint = clutter_offscreen_effect_pre_paint; effect_class->post_paint = clutter_offscreen_effect_post_paint; effect_class->paint = clutter_offscreen_effect_paint; gobject_class->finalize = clutter_offscreen_effect_finalize; gobject_class->notify = clutter_offscreen_effect_notify; } static void clutter_offscreen_effect_init (ClutterOffscreenEffect *self) { self->priv = clutter_offscreen_effect_get_instance_private (self); } /** * clutter_offscreen_effect_get_texture: * @effect: a #ClutterOffscreenEffect * * Retrieves the texture used as a render target for the offscreen * buffer created by @effect * * You should only use the returned texture when painting. The texture * may change after ClutterEffect::pre_paint is called so the effect * implementation should update any references to the texture after * chaining-up to the parent's pre_paint implementation. This can be * used instead of clutter_offscreen_effect_get_target() when the * effect subclass wants to paint using its own material. * * Return value: (transfer none): a #CoglHandle or %NULL. The * returned texture is owned by Clutter and it should not be * modified or freed * * Since: 1.10 */ CoglHandle clutter_offscreen_effect_get_texture (ClutterOffscreenEffect *effect) { g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), NULL); return effect->priv->texture; } /** * clutter_offscreen_effect_get_target: * @effect: a #ClutterOffscreenEffect * * Retrieves the material used as a render target for the offscreen * buffer created by @effect * * You should only use the returned #CoglMaterial when painting. The * returned material might change between different frames. * * Return value: (transfer none): a #CoglMaterial or %NULL. The * returned material is owned by Clutter and it should not be * modified or freed * * Since: 1.4 */ CoglMaterial * clutter_offscreen_effect_get_target (ClutterOffscreenEffect *effect) { g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), NULL); return (CoglMaterial *)effect->priv->target; } /** * clutter_offscreen_effect_paint_target: * @effect: a #ClutterOffscreenEffect * @paint_context: a #ClutterPaintContext * * Calls the paint_target() virtual function of the @effect * * Since: 1.4 */ void clutter_offscreen_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { g_return_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect)); CLUTTER_OFFSCREEN_EFFECT_GET_CLASS (effect)->paint_target (effect, paint_context); } /** * clutter_offscreen_effect_create_texture: * @effect: a #ClutterOffscreenEffect * @width: the minimum width of the target texture * @height: the minimum height of the target texture * * Calls the create_texture() virtual function of the @effect * * Return value: (transfer full): a handle to a Cogl texture, or * %NULL. The returned handle has its reference * count increased. * * Since: 1.4 */ CoglHandle clutter_offscreen_effect_create_texture (ClutterOffscreenEffect *effect, gfloat width, gfloat height) { g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), NULL); return CLUTTER_OFFSCREEN_EFFECT_GET_CLASS (effect)->create_texture (effect, width, height); } /** * clutter_offscreen_effect_get_target_size: * @effect: a #ClutterOffscreenEffect * @width: (out): return location for the target width, or %NULL * @height: (out): return location for the target height, or %NULL * * Retrieves the size of the offscreen buffer used by @effect to * paint the actor to which it has been applied. * * This function should only be called by #ClutterOffscreenEffect * implementations, from within the #ClutterOffscreenEffectClass.paint_target() * virtual function. * * Return value: %TRUE if the offscreen buffer has a valid size, * and %FALSE otherwise * * Since: 1.8 * * Deprecated: 1.14: Use clutter_offscreen_effect_get_target_rect() instead */ gboolean clutter_offscreen_effect_get_target_size (ClutterOffscreenEffect *effect, gfloat *width, gfloat *height) { ClutterOffscreenEffectPrivate *priv; g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), FALSE); priv = effect->priv; if (priv->texture == NULL) return FALSE; if (width) *width = cogl_texture_get_width (priv->texture); if (height) *height = cogl_texture_get_height (priv->texture); return TRUE; } /** * clutter_offscreen_effect_get_target_rect: * @effect: a #ClutterOffscreenEffect * @rect: (out caller-allocates): return location for the target area * * Retrieves the origin and size of the offscreen buffer used by @effect to * paint the actor to which it has been applied. * * This function should only be called by #ClutterOffscreenEffect * implementations, from within the #ClutterOffscreenEffectClass.paint_target() * virtual function. * * Return value: %TRUE if the offscreen buffer has a valid rectangle, * and %FALSE otherwise * * Since: 1.14 */ gboolean clutter_offscreen_effect_get_target_rect (ClutterOffscreenEffect *effect, graphene_rect_t *rect) { ClutterOffscreenEffectPrivate *priv; g_return_val_if_fail (CLUTTER_IS_OFFSCREEN_EFFECT (effect), FALSE); g_return_val_if_fail (rect != NULL, FALSE); priv = effect->priv; if (priv->texture == NULL) return FALSE; graphene_rect_init (rect, priv->position.x, priv->position.y, cogl_texture_get_width (priv->texture), cogl_texture_get_height (priv->texture)); return TRUE; } muffin-6.4.1/clutter/clutter/clutter-page-turn-effect.h0000664000175000017500000000563514723361714022104 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi * * Based on MxDeformPageTurn, written by: * Chris Lord */ #ifndef __CLUTTER_PAGE_TURN_EFFECT_H__ #define __CLUTTER_PAGE_TURN_EFFECT_H__ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #include G_BEGIN_DECLS #define CLUTTER_TYPE_PAGE_TURN_EFFECT (clutter_page_turn_effect_get_type ()) #define CLUTTER_PAGE_TURN_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_PAGE_TURN_EFFECT, ClutterPageTurnEffect)) #define CLUTTER_IS_PAGE_TURN_EFFECT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_PAGE_TURN_EFFECT)) /** * ClutterPageTurnEffect: * * #ClutterPageTurnEffect is an opaque structure * whose members can only be accessed using the provided API * * Since: 1.4 */ typedef struct _ClutterPageTurnEffect ClutterPageTurnEffect; typedef struct _ClutterPageTurnEffectClass ClutterPageTurnEffectClass; CLUTTER_EXPORT GType clutter_page_turn_effect_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterEffect *clutter_page_turn_effect_new (gdouble period, gdouble angle, gfloat radius); CLUTTER_EXPORT void clutter_page_turn_effect_set_period (ClutterPageTurnEffect *effect, gdouble period); CLUTTER_EXPORT gdouble clutter_page_turn_effect_get_period (ClutterPageTurnEffect *effect); CLUTTER_EXPORT void clutter_page_turn_effect_set_angle (ClutterPageTurnEffect *effect, gdouble angle); CLUTTER_EXPORT gdouble clutter_page_turn_effect_get_angle (ClutterPageTurnEffect *effect); CLUTTER_EXPORT void clutter_page_turn_effect_set_radius (ClutterPageTurnEffect *effect, gfloat radius); CLUTTER_EXPORT gfloat clutter_page_turn_effect_get_radius (ClutterPageTurnEffect *effect); G_END_DECLS #endif /* __CLUTTER_PAGE_TURN_EFFECT_H__ */ muffin-6.4.1/clutter/clutter/clutter-timeline.h0000664000175000017500000003002314723361714020543 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Matthew Allum * * Copyright (C) 2006 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #if !defined(__CLUTTER_H_INSIDE__) && !defined(CLUTTER_COMPILATION) #error "Only can be included directly." #endif #ifndef __CLUTTER_TIMELINE_H__ #define __CLUTTER_TIMELINE_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_TIMELINE (clutter_timeline_get_type ()) #define CLUTTER_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_TIMELINE, ClutterTimeline)) #define CLUTTER_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), CLUTTER_TYPE_TIMELINE, ClutterTimelineClass)) #define CLUTTER_IS_TIMELINE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_TIMELINE)) #define CLUTTER_IS_TIMELINE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), CLUTTER_TYPE_TIMELINE)) #define CLUTTER_TIMELINE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), CLUTTER_TYPE_TIMELINE, ClutterTimelineClass)) typedef struct _ClutterTimelineClass ClutterTimelineClass; typedef struct _ClutterTimelinePrivate ClutterTimelinePrivate; /** * ClutterTimelineProgressFunc: * @timeline: a #ClutterTimeline * @elapsed: the elapsed time, in milliseconds * @total: the total duration of the timeline, in milliseconds, * @user_data: data passed to the function * * A function for defining a custom progress. * * Return value: the progress, as a floating point value between -1.0 and 2.0. * * Since: 1.10 */ typedef gdouble (* ClutterTimelineProgressFunc) (ClutterTimeline *timeline, gdouble elapsed, gdouble total, gpointer user_data); /** * ClutterTimeline: * * The #ClutterTimeline structure contains only private data * and should be accessed using the provided API * * Since: 0.2 */ struct _ClutterTimeline { /*< private >*/ GObject parent_instance; ClutterTimelinePrivate *priv; }; /** * ClutterTimelineClass: * @started: class handler for the #ClutterTimeline::started signal * @completed: class handler for the #ClutterTimeline::completed signal * @paused: class handler for the #ClutterTimeline::paused signal * @new_frame: class handler for the #ClutterTimeline::new-frame signal * @marker_reached: class handler for the #ClutterTimeline::marker-reached signal * @stopped: class handler for the #ClutterTimeline::stopped signal * * The #ClutterTimelineClass structure contains only private data * * Since: 0.2 */ struct _ClutterTimelineClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ void (*started) (ClutterTimeline *timeline); void (*completed) (ClutterTimeline *timeline); void (*paused) (ClutterTimeline *timeline); void (*new_frame) (ClutterTimeline *timeline, gint msecs); void (*marker_reached) (ClutterTimeline *timeline, const gchar *marker_name, gint msecs); void (*stopped) (ClutterTimeline *timeline, gboolean is_finished); /*< private >*/ void (*_clutter_timeline_1) (void); void (*_clutter_timeline_2) (void); void (*_clutter_timeline_3) (void); void (*_clutter_timeline_4) (void); }; CLUTTER_EXPORT GType clutter_timeline_get_type (void) G_GNUC_CONST; CLUTTER_EXPORT ClutterTimeline * clutter_timeline_new (guint msecs); CLUTTER_EXPORT guint clutter_timeline_get_duration (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_duration (ClutterTimeline *timeline, guint msecs); CLUTTER_EXPORT ClutterTimelineDirection clutter_timeline_get_direction (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_direction (ClutterTimeline *timeline, ClutterTimelineDirection direction); CLUTTER_EXPORT void clutter_timeline_start (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_pause (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_stop (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_auto_reverse (ClutterTimeline *timeline, gboolean reverse); CLUTTER_EXPORT gboolean clutter_timeline_get_auto_reverse (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_repeat_count (ClutterTimeline *timeline, gint count); CLUTTER_EXPORT gint clutter_timeline_get_repeat_count (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_rewind (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_skip (ClutterTimeline *timeline, guint msecs); CLUTTER_EXPORT void clutter_timeline_advance (ClutterTimeline *timeline, guint msecs); CLUTTER_EXPORT guint clutter_timeline_get_elapsed_time (ClutterTimeline *timeline); CLUTTER_EXPORT gdouble clutter_timeline_get_progress (ClutterTimeline *timeline); CLUTTER_EXPORT gboolean clutter_timeline_is_playing (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_delay (ClutterTimeline *timeline, guint msecs); CLUTTER_EXPORT guint clutter_timeline_get_delay (ClutterTimeline *timeline); CLUTTER_EXPORT guint clutter_timeline_get_delta (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_add_marker (ClutterTimeline *timeline, const gchar *marker_name, gdouble progress); CLUTTER_EXPORT void clutter_timeline_add_marker_at_time (ClutterTimeline *timeline, const gchar *marker_name, guint msecs); CLUTTER_EXPORT void clutter_timeline_remove_marker (ClutterTimeline *timeline, const gchar *marker_name); CLUTTER_EXPORT gchar ** clutter_timeline_list_markers (ClutterTimeline *timeline, gint msecs, gsize *n_markers) G_GNUC_MALLOC; CLUTTER_EXPORT gboolean clutter_timeline_has_marker (ClutterTimeline *timeline, const gchar *marker_name); CLUTTER_EXPORT void clutter_timeline_advance_to_marker (ClutterTimeline *timeline, const gchar *marker_name); CLUTTER_EXPORT void clutter_timeline_set_progress_func (ClutterTimeline *timeline, ClutterTimelineProgressFunc func, gpointer data, GDestroyNotify notify); CLUTTER_EXPORT void clutter_timeline_set_progress_mode (ClutterTimeline *timeline, ClutterAnimationMode mode); CLUTTER_EXPORT ClutterAnimationMode clutter_timeline_get_progress_mode (ClutterTimeline *timeline); CLUTTER_EXPORT void clutter_timeline_set_step_progress (ClutterTimeline *timeline, gint n_steps, ClutterStepMode step_mode); CLUTTER_EXPORT gboolean clutter_timeline_get_step_progress (ClutterTimeline *timeline, gint *n_steps, ClutterStepMode *step_mode); CLUTTER_EXPORT void clutter_timeline_set_cubic_bezier_progress (ClutterTimeline *timeline, const graphene_point_t *c_1, const graphene_point_t *c_2); CLUTTER_EXPORT gboolean clutter_timeline_get_cubic_bezier_progress (ClutterTimeline *timeline, graphene_point_t *c_1, graphene_point_t *c_2); CLUTTER_EXPORT gint64 clutter_timeline_get_duration_hint (ClutterTimeline *timeline); CLUTTER_EXPORT gint clutter_timeline_get_current_repeat (ClutterTimeline *timeline); G_END_DECLS #endif /* _CLUTTER_TIMELINE_H__ */ muffin-6.4.1/clutter/clutter/clutter-clone.c0000664000175000017500000003272714723361714020045 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2008 Intel Corporation. * * Authored By: Robert Bragg * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-clone * @short_description: An actor that displays a clone of a source actor * * #ClutterClone is a #ClutterActor which draws with the paint * function of another actor, scaled to fit its own allocation. * * #ClutterClone can be used to efficiently clone any other actor. * * Unlike clutter_texture_new_from_actor(), #ClutterClone does not require * the presence of support for FBOs in the underlying GL or GLES * implementation. * * #ClutterClone is available since Clutter 1.0 */ #include "clutter-build-config.h" #define CLUTTER_ENABLE_EXPERIMENTAL_API #include "clutter-actor-private.h" #include "clutter-clone.h" #include "clutter-debug.h" #include "clutter-main.h" #include "clutter-paint-volume-private.h" #include "clutter-private.h" #include "cogl/cogl.h" struct _ClutterClonePrivate { ClutterActor *clone_source; gulong source_destroy_id; }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterClone, clutter_clone, CLUTTER_TYPE_ACTOR) enum { PROP_0, PROP_SOURCE, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; static void clutter_clone_set_source_internal (ClutterClone *clone, ClutterActor *source); static void clutter_clone_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; ClutterActor *clone_source = priv->clone_source; if (clone_source == NULL) { if (min_width_p) *min_width_p = 0; if (natural_width_p) *natural_width_p = 0; } else clutter_actor_get_preferred_width (clone_source, for_height, min_width_p, natural_width_p); } static void clutter_clone_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; ClutterActor *clone_source = priv->clone_source; if (clone_source == NULL) { if (min_height_p) *min_height_p = 0; if (natural_height_p) *natural_height_p = 0; } else clutter_actor_get_preferred_height (clone_source, for_width, min_height_p, natural_height_p); } static void clutter_clone_apply_transform (ClutterActor *self, CoglMatrix *matrix) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; ClutterActorBox box, source_box; gfloat x_scale, y_scale; /* First chain up and apply all the standard ClutterActor * transformations... */ CLUTTER_ACTOR_CLASS (clutter_clone_parent_class)->apply_transform (self, matrix); /* if we don't have a source, nothing else to do */ if (priv->clone_source == NULL) return; /* get our allocated size */ clutter_actor_get_allocation_box (self, &box); /* and get the allocated size of the source */ clutter_actor_get_allocation_box (priv->clone_source, &source_box); /* We need to scale what the clone-source actor paints to fill our own * allocation... */ x_scale = clutter_actor_box_get_width (&box) / clutter_actor_box_get_width (&source_box); y_scale = clutter_actor_box_get_height (&box) / clutter_actor_box_get_height (&source_box); cogl_matrix_scale (matrix, x_scale, y_scale, x_scale); } static void clutter_clone_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { ClutterClone *self = CLUTTER_CLONE (actor); ClutterClonePrivate *priv = self->priv; gboolean was_unmapped = FALSE; if (priv->clone_source == NULL) return; CLUTTER_NOTE (PAINT, "painting clone actor '%s'", _clutter_actor_get_debug_name (actor)); /* The final bits of magic: * - We need to override the paint opacity of the actor with our own * opacity. * - We need to inform the actor that it's in a clone paint (for the function * clutter_actor_is_in_clone_paint()) * - We need to stop clutter_actor_paint applying the model view matrix of * the clone source actor. */ _clutter_actor_set_in_clone_paint (priv->clone_source, TRUE); clutter_actor_set_opacity_override (priv->clone_source, clutter_actor_get_paint_opacity (actor)); _clutter_actor_set_enable_model_view_transform (priv->clone_source, FALSE); if (!clutter_actor_is_mapped (priv->clone_source)) { _clutter_actor_set_enable_paint_unmapped (priv->clone_source, TRUE); was_unmapped = TRUE; } /* If the source isn't ultimately parented to a toplevel, it can't be * realized or painted. */ if (clutter_actor_is_realized (priv->clone_source)) { _clutter_actor_push_clone_paint (); clutter_actor_paint (priv->clone_source, paint_context); _clutter_actor_pop_clone_paint (); } if (was_unmapped) _clutter_actor_set_enable_paint_unmapped (priv->clone_source, FALSE); _clutter_actor_set_enable_model_view_transform (priv->clone_source, TRUE); clutter_actor_set_opacity_override (priv->clone_source, -1); _clutter_actor_set_in_clone_paint (priv->clone_source, FALSE); } static gboolean clutter_clone_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv; const ClutterPaintVolume *source_volume; /* if the source is not set the paint volume is defined to be empty */ if (priv->clone_source == NULL) return TRUE; /* query the volume of the source actor and simply masquarade it as * the clones volume... */ source_volume = clutter_actor_get_paint_volume (priv->clone_source); if (source_volume == NULL) return FALSE; _clutter_paint_volume_set_from_volume (volume, source_volume); _clutter_paint_volume_set_reference_actor (volume, actor); return TRUE; } static gboolean clutter_clone_has_overlaps (ClutterActor *actor) { ClutterClonePrivate *priv = CLUTTER_CLONE (actor)->priv; /* The clone has overlaps iff the source has overlaps */ if (priv->clone_source == NULL) return FALSE; return clutter_actor_has_overlaps (priv->clone_source); } static void clutter_clone_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { ClutterClonePrivate *priv = CLUTTER_CLONE (self)->priv; ClutterActorClass *parent_class; /* chain up */ parent_class = CLUTTER_ACTOR_CLASS (clutter_clone_parent_class); parent_class->allocate (self, box, flags); if (priv->clone_source == NULL) return; /* ClutterActor delays allocating until the actor is shown; however * we cannot paint it correctly in that case, so force an allocation. */ if (clutter_actor_get_parent (priv->clone_source) != NULL && !clutter_actor_has_allocation (priv->clone_source)) clutter_actor_allocate_preferred_size (priv->clone_source, flags); #if 0 /* XXX - this is wrong: ClutterClone cannot clone unparented * actors, as it will break all invariants */ /* we act like a "foster parent" for the source we are cloning; * if the source has not been parented we have to force an * allocation on it, so that we can paint it correctly from * within our paint() implementation. since the actor does not * have a parent, and thus it won't be painted by the usual * paint cycle, we can safely give it as much size as it requires */ if (clutter_actor_get_parent (priv->clone_source) == NULL) clutter_actor_allocate_preferred_size (priv->clone_source, flags); #endif } static void clutter_clone_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterClone *self = CLUTTER_CLONE (gobject); switch (prop_id) { case PROP_SOURCE: clutter_clone_set_source (self, g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_clone_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterClonePrivate *priv = CLUTTER_CLONE (gobject)->priv; switch (prop_id) { case PROP_SOURCE: g_value_set_object (value, priv->clone_source); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_clone_dispose (GObject *gobject) { clutter_clone_set_source_internal (CLUTTER_CLONE (gobject), NULL); G_OBJECT_CLASS (clutter_clone_parent_class)->dispose (gobject); } static void clutter_clone_class_init (ClutterCloneClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->apply_transform = clutter_clone_apply_transform; actor_class->paint = clutter_clone_paint; actor_class->get_paint_volume = clutter_clone_get_paint_volume; actor_class->get_preferred_width = clutter_clone_get_preferred_width; actor_class->get_preferred_height = clutter_clone_get_preferred_height; actor_class->allocate = clutter_clone_allocate; actor_class->has_overlaps = clutter_clone_has_overlaps; gobject_class->dispose = clutter_clone_dispose; gobject_class->set_property = clutter_clone_set_property; gobject_class->get_property = clutter_clone_get_property; /** * ClutterClone:source: * * This property specifies the source actor being cloned. * * Since: 1.0 */ obj_props[PROP_SOURCE] = g_param_spec_object ("source", P_("Source"), P_("Specifies the actor to be cloned"), CLUTTER_TYPE_ACTOR, G_PARAM_CONSTRUCT | CLUTTER_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_clone_init (ClutterClone *self) { self->priv = clutter_clone_get_instance_private (self); } /** * clutter_clone_new: * @source: a #ClutterActor, or %NULL * * Creates a new #ClutterActor which clones @source/ * * Return value: the newly created #ClutterClone * * Since: 1.0 */ ClutterActor * clutter_clone_new (ClutterActor *source) { return g_object_new (CLUTTER_TYPE_CLONE, "source", source, NULL); } static void on_source_destroyed (ClutterActor *source, ClutterClone *self) { clutter_clone_set_source_internal (self, NULL); } static void clutter_clone_set_source_internal (ClutterClone *self, ClutterActor *source) { ClutterClonePrivate *priv = self->priv; if (priv->clone_source == source) return; if (priv->clone_source != NULL) { g_clear_signal_handler (&priv->source_destroy_id, priv->clone_source); _clutter_actor_detach_clone (priv->clone_source, CLUTTER_ACTOR (self)); g_object_unref (priv->clone_source); priv->clone_source = NULL; } if (source != NULL) { priv->clone_source = g_object_ref (source); _clutter_actor_attach_clone (priv->clone_source, CLUTTER_ACTOR (self)); priv->source_destroy_id = g_signal_connect (priv->clone_source, "destroy", G_CALLBACK (on_source_destroyed), self); } g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_SOURCE]); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } /** * clutter_clone_set_source: * @self: a #ClutterClone * @source: (allow-none): a #ClutterActor, or %NULL * * Sets @source as the source actor to be cloned by @self. * * Since: 1.0 */ void clutter_clone_set_source (ClutterClone *self, ClutterActor *source) { g_return_if_fail (CLUTTER_IS_CLONE (self)); g_return_if_fail (source == NULL || CLUTTER_IS_ACTOR (source)); clutter_clone_set_source_internal (self, source); clutter_actor_queue_relayout (CLUTTER_ACTOR (self)); } /** * clutter_clone_get_source: * @self: a #ClutterClone * * Retrieves the source #ClutterActor being cloned by @self. * * Return value: (transfer none): the actor source for the clone * * Since: 1.0 */ ClutterActor * clutter_clone_get_source (ClutterClone *self) { g_return_val_if_fail (CLUTTER_IS_CLONE (self), NULL); return self->priv->clone_source; } muffin-6.4.1/clutter/clutter/clutter-actor-meta-private.h0000664000175000017500000000743214723361714022451 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Emmanuele Bassi */ #ifndef __CLUTTER_ACTOR_META_PRIVATE_H__ #define __CLUTTER_ACTOR_META_PRIVATE_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_META_GROUP (_clutter_meta_group_get_type ()) #define CLUTTER_META_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), CLUTTER_TYPE_META_GROUP, ClutterMetaGroup)) #define CLUTTER_IS_META_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), CLUTTER_TYPE_META_GROUP)) typedef struct _ClutterMetaGroup ClutterMetaGroup; typedef struct _ClutterMetaGroupClass ClutterMetaGroupClass; struct _ClutterMetaGroup { GObject parent_instance; ClutterActor *actor; GList *meta; }; struct _ClutterMetaGroupClass { GObjectClass parent_class; }; /* Each actor meta has a priority with zero as a default. A higher number means higher priority. Higher priority metas stay at the beginning of the list. The priority can be negative to give lower priority than the default. */ #define CLUTTER_ACTOR_META_PRIORITY_DEFAULT 0 /* Any value greater than this is considered an 'internal' priority and if we expose the priority property publicly then an application would not be able to use these values. */ #define CLUTTER_ACTOR_META_PRIORITY_INTERNAL_HIGH (G_MAXINT / 2) #define CLUTTER_ACTOR_META_PRIORITY_INTERNAL_LOW (G_MININT / 2) GType _clutter_meta_group_get_type (void) G_GNUC_CONST; void _clutter_meta_group_add_meta (ClutterMetaGroup *group, ClutterActorMeta *meta); void _clutter_meta_group_remove_meta (ClutterMetaGroup *group, ClutterActorMeta *meta); const GList * _clutter_meta_group_peek_metas (ClutterMetaGroup *group); void _clutter_meta_group_clear_metas (ClutterMetaGroup *group); ClutterActorMeta * _clutter_meta_group_get_meta (ClutterMetaGroup *group, const gchar *name); gboolean _clutter_meta_group_has_metas_no_internal (ClutterMetaGroup *group); GList * _clutter_meta_group_get_metas_no_internal (ClutterMetaGroup *group); void _clutter_meta_group_clear_metas_no_internal (ClutterMetaGroup *group); /* ActorMeta */ void _clutter_actor_meta_set_actor (ClutterActorMeta *meta, ClutterActor *actor); const gchar * _clutter_actor_meta_get_debug_name (ClutterActorMeta *meta); void _clutter_actor_meta_set_priority (ClutterActorMeta *meta, gint priority); int _clutter_actor_meta_get_priority (ClutterActorMeta *meta); gboolean _clutter_actor_meta_is_internal (ClutterActorMeta *meta); G_END_DECLS #endif /* __CLUTTER_ACTOR_META_PRIVATE_H__ */ muffin-6.4.1/clutter/clutter/clutter-gesture-action.c0000664000175000017500000012267514723361714021700 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corporation. * Copyright (C) 2011 Robert Bosch Car Multimedia GmbH. * Copyright (C) 2012 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: * Tomeu Vizoso */ /** * SECTION:clutter-gesture-action * @Title: ClutterGestureAction * @Short_Description: Action for gesture gestures * * #ClutterGestureAction is a sub-class of #ClutterAction that implements * the logic for recognizing gesture gestures. It listens for low level events * such as #ClutterButtonEvent and #ClutterMotionEvent on the stage to raise * the #ClutterGestureAction::gesture-begin, #ClutterGestureAction::gesture-progress, * and #ClutterGestureAction::gesture-end signals. * * To use #ClutterGestureAction you just need to apply it to a #ClutterActor * using clutter_actor_add_action() and connect to the signals: * * |[ * ClutterAction *action = clutter_gesture_action_new (); * * clutter_actor_add_action (actor, action); * * g_signal_connect (action, "gesture-begin", G_CALLBACK (on_gesture_begin), NULL); * g_signal_connect (action, "gesture-progress", G_CALLBACK (on_gesture_progress), NULL); * g_signal_connect (action, "gesture-end", G_CALLBACK (on_gesture_end), NULL); * ]| * * ## Creating Gesture actions * * A #ClutterGestureAction provides four separate states that can be * used to recognize or ignore gestures when writing a new action class: * * - Prepare -> Cancel * - Prepare -> Begin -> Cancel * - Prepare -> Begin -> End * - Prepare -> Begin -> Progress -> Cancel * - Prepare -> Begin -> Progress -> End * * Each #ClutterGestureAction starts in the "prepare" state, and calls * the #ClutterGestureActionClass.gesture_prepare() virtual function; this * state can be used to reset the internal state of a #ClutterGestureAction * subclass, but it can also immediately cancel a gesture without going * through the rest of the states. * * The "begin" state follows the "prepare" state, and calls the * #ClutterGestureActionClass.gesture_begin() virtual function. This state * signals the start of a gesture recognizing process. From the "begin" state * the gesture recognition process can successfully end, by going to the * "end" state; it can continue in the "progress" state, in case of a * continuous gesture; or it can be terminated, by moving to the "cancel" * state. * * In case of continuous gestures, the #ClutterGestureAction will use * the "progress" state, calling the #ClutterGestureActionClass.gesture_progress() * virtual function; the "progress" state will continue until the end of the * gesture, in which case the "end" state will be reached, or until the * gesture is cancelled, in which case the "cancel" gesture will be used * instead. * * Since: 1.8 */ #include "clutter-build-config.h" #include "clutter-gesture-action-private.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-marshal.h" #include "clutter-private.h" #include #define MAX_GESTURE_POINTS (10) #define FLOAT_EPSILON (1e-15) typedef struct { ClutterInputDevice *device; ClutterEventSequence *sequence; ClutterEvent *last_event; gfloat press_x, press_y; gint64 last_motion_time; gfloat last_motion_x, last_motion_y; gint64 last_delta_time; gfloat last_delta_x, last_delta_y; gfloat release_x, release_y; } GesturePoint; struct _ClutterGestureActionPrivate { ClutterActor *stage; gint requested_nb_points; GArray *points; gulong actor_capture_id; gulong stage_capture_id; ClutterGestureTriggerEdge edge; float distance_x, distance_y; guint in_gesture : 1; }; enum { PROP_0, PROP_N_TOUCH_POINTS, PROP_THRESHOLD_TRIGGER_EDGE, PROP_THRESHOLD_TRIGGER_DISTANCE_X, PROP_THRESHOLD_TRIGGER_DISTANCE_Y, PROP_LAST }; enum { GESTURE_BEGIN, GESTURE_PROGRESS, GESTURE_END, GESTURE_CANCEL, LAST_SIGNAL }; static GParamSpec *gesture_props[PROP_LAST]; static guint gesture_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterGestureAction, clutter_gesture_action, CLUTTER_TYPE_ACTION) static GesturePoint * gesture_register_point (ClutterGestureAction *action, ClutterEvent *event) { ClutterGestureActionPrivate *priv = action->priv; GesturePoint *point = NULL; if (priv->points->len >= MAX_GESTURE_POINTS) return NULL; g_array_set_size (priv->points, priv->points->len + 1); point = &g_array_index (priv->points, GesturePoint, priv->points->len - 1); point->last_event = clutter_event_copy (event); point->device = clutter_event_get_device (event); clutter_event_get_coords (event, &point->press_x, &point->press_y); point->last_motion_x = point->press_x; point->last_motion_y = point->press_y; point->last_motion_time = clutter_event_get_time (event); point->last_delta_x = point->last_delta_y = 0; point->last_delta_time = 0; if (clutter_event_type (event) != CLUTTER_BUTTON_PRESS) point->sequence = clutter_event_get_event_sequence (event); else point->sequence = NULL; return point; } static GesturePoint * gesture_find_point (ClutterGestureAction *action, ClutterEvent *event, gint *position) { ClutterGestureActionPrivate *priv = action->priv; GesturePoint *point = NULL; ClutterEventType type = clutter_event_type (event); ClutterInputDevice *device = clutter_event_get_device (event); ClutterEventSequence *sequence = NULL; gint i; if ((type != CLUTTER_BUTTON_PRESS) && (type != CLUTTER_BUTTON_RELEASE) && (type != CLUTTER_MOTION)) sequence = clutter_event_get_event_sequence (event); for (i = 0; i < priv->points->len; i++) { if ((g_array_index (priv->points, GesturePoint, i).device == device) && (g_array_index (priv->points, GesturePoint, i).sequence == sequence)) { if (position != NULL) *position = i; point = &g_array_index (priv->points, GesturePoint, i); break; } } return point; } static void gesture_unregister_point (ClutterGestureAction *action, gint position) { ClutterGestureActionPrivate *priv = action->priv; if (action->priv->points->len == 0) return; g_array_remove_index (priv->points, position); } static void gesture_update_motion_point (GesturePoint *point, ClutterEvent *event) { gfloat motion_x, motion_y; gint64 _time; clutter_event_get_coords (event, &motion_x, &motion_y); clutter_event_free (point->last_event); point->last_event = clutter_event_copy (event); point->last_delta_x = motion_x - point->last_motion_x; point->last_delta_y = motion_y - point->last_motion_y; point->last_motion_x = motion_x; point->last_motion_y = motion_y; _time = clutter_event_get_time (event); point->last_delta_time = _time - point->last_motion_time; point->last_motion_time = _time; } static void gesture_update_release_point (GesturePoint *point, ClutterEvent *event) { gint64 _time; clutter_event_get_coords (event, &point->release_x, &point->release_y); clutter_event_free (point->last_event); point->last_event = clutter_event_copy (event); /* Treat the release event as the continuation of the last motion, * in case the user keeps the pointer still for a while before * releasing it. */ _time = clutter_event_get_time (event); point->last_delta_time += _time - point->last_motion_time; } static gint gesture_get_default_threshold (void) { gint threshold; ClutterSettings *settings = clutter_settings_get_default (); g_object_get (settings, "dnd-drag-threshold", &threshold, NULL); return threshold; } static gboolean gesture_point_pass_threshold (ClutterGestureAction *action, GesturePoint *point, ClutterEvent *event) { float threshold_x, threshold_y; gfloat motion_x, motion_y; clutter_event_get_coords (event, &motion_x, &motion_y); clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y); if ((fabsf (point->press_y - motion_y) < threshold_y) && (fabsf (point->press_x - motion_x) < threshold_x)) return TRUE; return FALSE; } static void gesture_point_unset (GesturePoint *point) { clutter_event_free (point->last_event); } static void cancel_gesture (ClutterGestureAction *action) { ClutterGestureActionPrivate *priv = action->priv; ClutterActor *actor; priv->in_gesture = FALSE; g_clear_signal_handler (&priv->stage_capture_id, priv->stage); actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); g_signal_emit (action, gesture_signals[GESTURE_CANCEL], 0, actor); g_array_set_size (action->priv->points, 0); } static gboolean begin_gesture (ClutterGestureAction *action, ClutterActor *actor) { ClutterGestureActionPrivate *priv = action->priv; gboolean return_value; priv->in_gesture = TRUE; if (!CLUTTER_GESTURE_ACTION_GET_CLASS (action)->gesture_prepare (action, actor)) { cancel_gesture (action); return FALSE; } /* clutter_gesture_action_cancel() may have been called during * gesture_prepare(), check that the gesture is still active. */ if (!priv->in_gesture) return FALSE; g_signal_emit (action, gesture_signals[GESTURE_BEGIN], 0, actor, &return_value); if (!return_value) { cancel_gesture (action); return FALSE; } return TRUE; } static gboolean stage_captured_event_cb (ClutterActor *stage, ClutterEvent *event, ClutterGestureAction *action) { ClutterGestureActionPrivate *priv = action->priv; ClutterActor *actor; gint position; float threshold_x, threshold_y; gboolean return_value; GesturePoint *point; ClutterEventType event_type; event_type = clutter_event_type (event); if (event_type != CLUTTER_TOUCH_CANCEL && event_type != CLUTTER_TOUCH_UPDATE && event_type != CLUTTER_TOUCH_END && event_type != CLUTTER_MOTION && event_type != CLUTTER_BUTTON_RELEASE) return CLUTTER_EVENT_PROPAGATE; if ((point = gesture_find_point (action, event, &position)) == NULL) return CLUTTER_EVENT_PROPAGATE; actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); switch (clutter_event_type (event)) { case CLUTTER_MOTION: { ClutterModifierType mods = clutter_event_get_state (event); /* we might miss a button-release event in case of grabs, * so we need to check whether the button is still down * during a motion event */ if (!(mods & CLUTTER_BUTTON1_MASK)) { cancel_gesture (action); return CLUTTER_EVENT_PROPAGATE; } } /* Follow same code path as a touch event update */ case CLUTTER_TOUCH_UPDATE: if (!priv->in_gesture) { if (priv->points->len < priv->requested_nb_points) { gesture_update_motion_point (point, event); return CLUTTER_EVENT_PROPAGATE; } /* Wait until the drag threshold has been exceeded * before starting _TRIGGER_EDGE_AFTER gestures. */ if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER && gesture_point_pass_threshold (action, point, event)) { gesture_update_motion_point (point, event); return CLUTTER_EVENT_PROPAGATE; } if (!begin_gesture (action, actor)) { if ((point = gesture_find_point (action, event, &position)) != NULL) gesture_update_motion_point (point, event); return CLUTTER_EVENT_PROPAGATE; } if ((point = gesture_find_point (action, event, &position)) == NULL) return CLUTTER_EVENT_PROPAGATE; } gesture_update_motion_point (point, event); g_signal_emit (action, gesture_signals[GESTURE_PROGRESS], 0, actor, &return_value); if (!return_value) { cancel_gesture (action); return CLUTTER_EVENT_PROPAGATE; } /* Check if a _TRIGGER_EDGE_BEFORE gesture needs to be cancelled because * the drag threshold has been exceeded. */ clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y); if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_BEFORE && ((fabsf (point->press_y - point->last_motion_y) > threshold_y) || (fabsf (point->press_x - point->last_motion_x) > threshold_x))) { cancel_gesture (action); return CLUTTER_EVENT_PROPAGATE; } break; case CLUTTER_BUTTON_RELEASE: case CLUTTER_TOUCH_END: { gesture_update_release_point (point, event); if (priv->in_gesture && ((priv->points->len - 1) < priv->requested_nb_points)) { priv->in_gesture = FALSE; g_signal_emit (action, gesture_signals[GESTURE_END], 0, actor); } gesture_unregister_point (action, position); } break; case CLUTTER_TOUCH_CANCEL: { gesture_update_release_point (point, event); if (priv->in_gesture) { priv->in_gesture = FALSE; cancel_gesture (action); } gesture_unregister_point (action, position); } break; default: break; } if (priv->points->len == 0) g_clear_signal_handler (&priv->stage_capture_id, priv->stage); return CLUTTER_EVENT_PROPAGATE; } static gboolean actor_captured_event_cb (ClutterActor *actor, ClutterEvent *event, ClutterGestureAction *action) { ClutterGestureActionPrivate *priv = action->priv; GesturePoint *point G_GNUC_UNUSED; if ((clutter_event_type (event) != CLUTTER_BUTTON_PRESS) && (clutter_event_type (event) != CLUTTER_TOUCH_BEGIN)) return CLUTTER_EVENT_PROPAGATE; if (!clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (action))) return CLUTTER_EVENT_PROPAGATE; point = gesture_register_point (action, event); if (priv->stage == NULL) priv->stage = clutter_actor_get_stage (actor); if (priv->stage_capture_id == 0) priv->stage_capture_id = g_signal_connect_after (priv->stage, "captured-event", G_CALLBACK (stage_captured_event_cb), action); /* Start the gesture immediately if the gesture has no * _TRIGGER_EDGE_AFTER drag threshold. */ if ((priv->points->len >= priv->requested_nb_points) && (priv->edge != CLUTTER_GESTURE_TRIGGER_EDGE_AFTER)) begin_gesture (action, actor); return CLUTTER_EVENT_PROPAGATE; } static void clutter_gesture_action_set_actor (ClutterActorMeta *meta, ClutterActor *actor) { ClutterGestureActionPrivate *priv = CLUTTER_GESTURE_ACTION (meta)->priv; ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (clutter_gesture_action_parent_class); if (priv->actor_capture_id != 0) { ClutterActor *old_actor = clutter_actor_meta_get_actor (meta); if (old_actor != NULL) g_clear_signal_handler (&priv->actor_capture_id, old_actor); priv->actor_capture_id = 0; } if (priv->stage_capture_id != 0) { if (priv->stage != NULL) g_clear_signal_handler (&priv->stage_capture_id, priv->stage); priv->stage_capture_id = 0; priv->stage = NULL; } if (actor != NULL) { priv->actor_capture_id = g_signal_connect (actor, "captured-event", G_CALLBACK (actor_captured_event_cb), meta); } meta_class->set_actor (meta, actor); } static gboolean default_event_handler (ClutterGestureAction *action, ClutterActor *actor) { return TRUE; } static void clutter_gesture_action_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterGestureAction *self = CLUTTER_GESTURE_ACTION (gobject); switch (prop_id) { case PROP_N_TOUCH_POINTS: clutter_gesture_action_set_n_touch_points (self, g_value_get_int (value)); break; case PROP_THRESHOLD_TRIGGER_EDGE: clutter_gesture_action_set_threshold_trigger_edge (self, g_value_get_enum (value)); break; case PROP_THRESHOLD_TRIGGER_DISTANCE_X: clutter_gesture_action_set_threshold_trigger_distance (self, g_value_get_float (value), self->priv->distance_y); break; case PROP_THRESHOLD_TRIGGER_DISTANCE_Y: clutter_gesture_action_set_threshold_trigger_distance (self, self->priv->distance_x, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_gesture_action_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterGestureAction *self = CLUTTER_GESTURE_ACTION (gobject); switch (prop_id) { case PROP_N_TOUCH_POINTS: g_value_set_int (value, self->priv->requested_nb_points); break; case PROP_THRESHOLD_TRIGGER_EDGE: g_value_set_enum (value, self->priv->edge); break; case PROP_THRESHOLD_TRIGGER_DISTANCE_X: if (self->priv->distance_x > 0.0) g_value_set_float (value, self->priv->distance_x); else g_value_set_float (value, gesture_get_default_threshold ()); break; case PROP_THRESHOLD_TRIGGER_DISTANCE_Y: if (self->priv->distance_y > 0.0) g_value_set_float (value, self->priv->distance_y); else g_value_set_float (value, gesture_get_default_threshold ()); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void clutter_gesture_action_finalize (GObject *gobject) { ClutterGestureActionPrivate *priv = CLUTTER_GESTURE_ACTION (gobject)->priv; g_array_unref (priv->points); G_OBJECT_CLASS (clutter_gesture_action_parent_class)->finalize (gobject); } static void clutter_gesture_action_class_init (ClutterGestureActionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorMetaClass *meta_class = CLUTTER_ACTOR_META_CLASS (klass); gobject_class->finalize = clutter_gesture_action_finalize; gobject_class->set_property = clutter_gesture_action_set_property; gobject_class->get_property = clutter_gesture_action_get_property; meta_class->set_actor = clutter_gesture_action_set_actor; klass->gesture_begin = default_event_handler; klass->gesture_progress = default_event_handler; klass->gesture_prepare = default_event_handler; /** * ClutterGestureAction:n-touch-points: * * Number of touch points to trigger a gesture action. * * Since: 1.16 */ gesture_props[PROP_N_TOUCH_POINTS] = g_param_spec_int ("n-touch-points", P_("Number touch points"), P_("Number of touch points"), 1, G_MAXINT, 1, CLUTTER_PARAM_READWRITE); /** * ClutterGestureAction:threshold-trigger-edge: * * The trigger edge to be used by the action to either emit the * #ClutterGestureAction::gesture-begin signal or to emit the * #ClutterGestureAction::gesture-cancel signal. * * Since: 1.18 */ gesture_props[PROP_THRESHOLD_TRIGGER_EDGE] = g_param_spec_enum ("threshold-trigger-edge", P_("Threshold Trigger Edge"), P_("The trigger edge used by the action"), CLUTTER_TYPE_GESTURE_TRIGGER_EDGE, CLUTTER_GESTURE_TRIGGER_EDGE_NONE, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterGestureAction:threshold-trigger-distance-x: * * The horizontal trigger distance to be used by the action to either * emit the #ClutterGestureAction::gesture-begin signal or to emit * the #ClutterGestureAction::gesture-cancel signal. * * A negative value will be interpreted as the default drag threshold. * * Since: 1.18 */ gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_X] = g_param_spec_float ("threshold-trigger-distance-x", P_("Threshold Trigger Horizontal Distance"), P_("The horizontal trigger distance used by the action"), -1.0, G_MAXFLOAT, -1.0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); /** * ClutterGestureAction:threshold-trigger-distance-y: * * The vertical trigger distance to be used by the action to either * emit the #ClutterGestureAction::gesture-begin signal or to emit * the #ClutterGestureAction::gesture-cancel signal. * * A negative value will be interpreted as the default drag threshold. * * Since: 1.18 */ gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_Y] = g_param_spec_float ("threshold-trigger-distance-y", P_("Threshold Trigger Vertical Distance"), P_("The vertical trigger distance used by the action"), -1.0, G_MAXFLOAT, -1.0, CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (gobject_class, PROP_LAST, gesture_props); /** * ClutterGestureAction::gesture-begin: * @action: the #ClutterGestureAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::gesture_begin signal is emitted when the #ClutterActor to which * a #ClutterGestureAction has been applied starts receiving a gesture. * * Return value: %TRUE if the gesture should start, and %FALSE if * the gesture should be ignored. * * Since: 1.8 */ gesture_signals[GESTURE_BEGIN] = g_signal_new (I_("gesture-begin"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_begin), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_ACTOR); /** * ClutterGestureAction::gesture-progress: * @action: the #ClutterGestureAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::gesture-progress signal is emitted for each motion event after * the #ClutterGestureAction::gesture-begin signal has been emitted. * * Return value: %TRUE if the gesture should continue, and %FALSE if * the gesture should be cancelled. * * Since: 1.8 */ gesture_signals[GESTURE_PROGRESS] = g_signal_new (I_("gesture-progress"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_progress), _clutter_boolean_continue_accumulator, NULL, _clutter_marshal_BOOLEAN__OBJECT, G_TYPE_BOOLEAN, 1, CLUTTER_TYPE_ACTOR); /** * ClutterGestureAction::gesture-end: * @action: the #ClutterGestureAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::gesture-end signal is emitted at the end of the gesture gesture, * when the pointer's button is released * * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin * signal has been emitted first. * * Since: 1.8 */ gesture_signals[GESTURE_END] = g_signal_new (I_("gesture-end"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_end), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); /** * ClutterGestureAction::gesture-cancel: * @action: the #ClutterGestureAction that emitted the signal * @actor: the #ClutterActor attached to the @action * * The ::gesture-cancel signal is emitted when the ongoing gesture gets * cancelled from the #ClutterGestureAction::gesture-progress signal handler. * * This signal is emitted if and only if the #ClutterGestureAction::gesture-begin * signal has been emitted first. * * Since: 1.8 */ gesture_signals[GESTURE_CANCEL] = g_signal_new (I_("gesture-cancel"), G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (ClutterGestureActionClass, gesture_cancel), NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); } static void clutter_gesture_action_init (ClutterGestureAction *self) { self->priv = clutter_gesture_action_get_instance_private (self); self->priv->points = g_array_sized_new (FALSE, TRUE, sizeof (GesturePoint), 3); g_array_set_clear_func (self->priv->points, (GDestroyNotify) gesture_point_unset); self->priv->requested_nb_points = 1; self->priv->edge = CLUTTER_GESTURE_TRIGGER_EDGE_NONE; } /** * clutter_gesture_action_new: * * Creates a new #ClutterGestureAction instance. * * Return value: the newly created #ClutterGestureAction * * Since: 1.8 */ ClutterAction * clutter_gesture_action_new (void) { return g_object_new (CLUTTER_TYPE_GESTURE_ACTION, NULL); } /** * clutter_gesture_action_get_press_coords: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @press_x: (out) (allow-none): return location for the press * event's X coordinate * @press_y: (out) (allow-none): return location for the press * event's Y coordinate * * Retrieves the coordinates, in stage space, of the press event * that started the dragging for a specific touch point. * * Since: 1.8 */ void clutter_gesture_action_get_press_coords (ClutterGestureAction *action, guint point, gfloat *press_x, gfloat *press_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); g_return_if_fail (action->priv->points->len > point); if (press_x) *press_x = g_array_index (action->priv->points, GesturePoint, point).press_x; if (press_y) *press_y = g_array_index (action->priv->points, GesturePoint, point).press_y; } /** * clutter_gesture_action_get_motion_coords: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @motion_x: (out) (allow-none): return location for the latest motion * event's X coordinate * @motion_y: (out) (allow-none): return location for the latest motion * event's Y coordinate * * Retrieves the coordinates, in stage space, of the latest motion * event during the dragging. * * Since: 1.8 */ void clutter_gesture_action_get_motion_coords (ClutterGestureAction *action, guint point, gfloat *motion_x, gfloat *motion_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); g_return_if_fail (action->priv->points->len > point); if (motion_x) *motion_x = g_array_index (action->priv->points, GesturePoint, point).last_motion_x; if (motion_y) *motion_y = g_array_index (action->priv->points, GesturePoint, point).last_motion_y; } /** * clutter_gesture_action_get_motion_delta: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @delta_x: (out) (allow-none): return location for the X axis * component of the incremental motion delta * @delta_y: (out) (allow-none): return location for the Y axis * component of the incremental motion delta * * Retrieves the incremental delta since the last motion event * during the dragging. * * Return value: the distance since last motion event * * Since: 1.12 */ gfloat clutter_gesture_action_get_motion_delta (ClutterGestureAction *action, guint point, gfloat *delta_x, gfloat *delta_y) { gfloat d_x, d_y; g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); g_return_val_if_fail (action->priv->points->len > point, 0); d_x = g_array_index (action->priv->points, GesturePoint, point).last_delta_x; d_y = g_array_index (action->priv->points, GesturePoint, point).last_delta_y; if (delta_x) *delta_x = d_x; if (delta_y) *delta_y = d_y; return sqrt ((d_x * d_x) + (d_y * d_y)); } /** * clutter_gesture_action_get_release_coords: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @release_x: (out) (allow-none): return location for the X coordinate of * the last release * @release_y: (out) (allow-none): return location for the Y coordinate of * the last release * * Retrieves the coordinates, in stage space, where the touch point was * last released. * * Since: 1.8 */ void clutter_gesture_action_get_release_coords (ClutterGestureAction *action, guint point, gfloat *release_x, gfloat *release_y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); g_return_if_fail (action->priv->points->len > point); if (release_x) *release_x = g_array_index (action->priv->points, GesturePoint, point).release_x; if (release_y) *release_y = g_array_index (action->priv->points, GesturePoint, point).release_y; } /** * clutter_gesture_action_get_velocity: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * @velocity_x: (out) (allow-none): return location for the latest motion * event's X velocity * @velocity_y: (out) (allow-none): return location for the latest motion * event's Y velocity * * Retrieves the velocity, in stage pixels per millisecond, of the * latest motion event during the dragging. * * Since: 1.12 */ gfloat clutter_gesture_action_get_velocity (ClutterGestureAction *action, guint point, gfloat *velocity_x, gfloat *velocity_y) { gfloat d_x, d_y, distance, velocity; gint64 d_t; g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); g_return_val_if_fail (action->priv->points->len > point, 0); distance = clutter_gesture_action_get_motion_delta (action, point, &d_x, &d_y); d_t = g_array_index (action->priv->points, GesturePoint, point).last_delta_time; if (velocity_x) *velocity_x = d_t > FLOAT_EPSILON ? d_x / d_t : 0; if (velocity_y) *velocity_y = d_t > FLOAT_EPSILON ? d_y / d_t : 0; velocity = d_t > FLOAT_EPSILON ? distance / d_t : 0; return velocity; } /** * clutter_gesture_action_get_n_touch_points: * @action: a #ClutterGestureAction * * Retrieves the number of requested points to trigger the gesture. * * Return value: the number of points to trigger the gesture. * * Since: 1.12 */ gint clutter_gesture_action_get_n_touch_points (ClutterGestureAction *action) { g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); return action->priv->requested_nb_points; } /** * clutter_gesture_action_set_n_touch_points: * @action: a #ClutterGestureAction * @nb_points: a number of points * * Sets the number of points needed to trigger the gesture. * * Since: 1.12 */ void clutter_gesture_action_set_n_touch_points (ClutterGestureAction *action, gint nb_points) { ClutterGestureActionPrivate *priv; g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); g_return_if_fail (nb_points >= 1); priv = action->priv; if (priv->requested_nb_points == nb_points) return; priv->requested_nb_points = nb_points; if (priv->in_gesture) { if (priv->points->len < priv->requested_nb_points) cancel_gesture (action); } else if (priv->edge == CLUTTER_GESTURE_TRIGGER_EDGE_AFTER) { if (priv->points->len >= priv->requested_nb_points) { ClutterActor *actor = clutter_actor_meta_get_actor (CLUTTER_ACTOR_META (action)); gint i; float threshold_x, threshold_y; clutter_gesture_action_get_threshold_trigger_distance (action, &threshold_x, &threshold_y); for (i = 0; i < priv->points->len; i++) { GesturePoint *point = &g_array_index (priv->points, GesturePoint, i); if ((fabsf (point->press_y - point->last_motion_y) >= threshold_y) || (fabsf (point->press_x - point->last_motion_x) >= threshold_x)) { begin_gesture (action, actor); break; } } } } g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_N_TOUCH_POINTS]); } /** * clutter_gesture_action_get_n_current_points: * @action: a #ClutterGestureAction * * Retrieves the number of points currently active. * * Return value: the number of points currently active. * * Since: 1.12 */ guint clutter_gesture_action_get_n_current_points (ClutterGestureAction *action) { g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), 0); return action->priv->points->len; } /** * clutter_gesture_action_get_sequence: * @action: a #ClutterGestureAction * @point: index of a point currently active * * Retrieves the #ClutterEventSequence of a touch point. * * Return value: (transfer none): the #ClutterEventSequence of a touch point. * * Since: 1.12 */ ClutterEventSequence * clutter_gesture_action_get_sequence (ClutterGestureAction *action, guint point) { g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), NULL); g_return_val_if_fail (action->priv->points->len > point, NULL); return g_array_index (action->priv->points, GesturePoint, point).sequence; } /** * clutter_gesture_action_get_device: * @action: a #ClutterGestureAction * @point: the touch point index, with 0 being the first touch * point received by the action * * Retrieves the #ClutterInputDevice of a touch point. * * Return value: (transfer none): the #ClutterInputDevice of a touch point. * * Since: 1.12 */ ClutterInputDevice * clutter_gesture_action_get_device (ClutterGestureAction *action, guint point) { g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), NULL); g_return_val_if_fail (action->priv->points->len > point, NULL); return g_array_index (action->priv->points, GesturePoint, point).device; } /** * clutter_gesture_action_get_last_event: * @action: a #ClutterGestureAction * @point: index of a point currently active * * Retrieves a reference to the last #ClutterEvent for a touch point. Call * clutter_event_copy() if you need to store the reference somewhere. * * Return value: (transfer none): the last #ClutterEvent for a touch point. * * Since: 1.14 */ const ClutterEvent * clutter_gesture_action_get_last_event (ClutterGestureAction *action, guint point) { GesturePoint *gesture_point; g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), NULL); g_return_val_if_fail (action->priv->points->len > point, NULL); gesture_point = &g_array_index (action->priv->points, GesturePoint, point); return gesture_point->last_event; } /** * clutter_gesture_action_cancel: * @action: a #ClutterGestureAction * * Cancel a #ClutterGestureAction before it begins * * Since: 1.12 */ void clutter_gesture_action_cancel (ClutterGestureAction *action) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); cancel_gesture (action); } /** * clutter_gesture_action_set_threshold_trigger_edge: * @action: a #ClutterGestureAction * @edge: the %ClutterGestureTriggerEdge * * Sets the edge trigger for the gesture drag threshold, if any. * * This function should only be called by sub-classes of * #ClutterGestureAction during their construction phase. * * Since: 1.18 */ void clutter_gesture_action_set_threshold_trigger_edge (ClutterGestureAction *action, ClutterGestureTriggerEdge edge) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); if (action->priv->edge == edge) return; action->priv->edge = edge; g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_THRESHOLD_TRIGGER_EDGE]); } /** * clutter_gesture_action_get_threshold_trigger_edge: * @action: a #ClutterGestureAction * * Retrieves the edge trigger of the gesture @action, as set using * clutter_gesture_action_set_threshold_trigger_edge(). * * Return value: the edge trigger * * Since: 1.20 */ ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_edge (ClutterGestureAction *action) { g_return_val_if_fail (CLUTTER_IS_GESTURE_ACTION (action), CLUTTER_GESTURE_TRIGGER_EDGE_NONE); return action->priv->edge; } /** * clutter_gesture_action_get_threshold_trigger_egde: * @action: a #ClutterGestureAction * * Retrieves the edge trigger of the gesture @action, as set using * clutter_gesture_action_set_threshold_trigger_edge(). * * Return value: the edge trigger * * Since: 1.18 * * Deprecated: 1.20: Use clutter_gesture_action_get_threshold_trigger_edge() instead. */ ClutterGestureTriggerEdge clutter_gesture_action_get_threshold_trigger_egde (ClutterGestureAction *action) { return clutter_gesture_action_get_threshold_trigger_edge (action); } /** * clutter_gesture_action_set_threshold_trigger_distance: * @action: a #ClutterGestureAction * @x: the distance on the horizontal axis * @y: the distance on the vertical axis * * Sets the threshold trigger distance for the gesture drag threshold, if any. * * This function should only be called by sub-classes of * #ClutterGestureAction during their construction phase. * * Since: 1.18 */ void clutter_gesture_action_set_threshold_trigger_distance (ClutterGestureAction *action, float x, float y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); if (fabsf (x - action->priv->distance_x) > FLOAT_EPSILON) { action->priv->distance_x = x; g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_X]); } if (fabsf (y - action->priv->distance_y) > FLOAT_EPSILON) { action->priv->distance_y = y; g_object_notify_by_pspec (G_OBJECT (action), gesture_props[PROP_THRESHOLD_TRIGGER_DISTANCE_Y]); } } /** * clutter_gesture_action_get_threshold_trigger_distance: * @action: a #ClutterGestureAction * @x: (out) (allow-none): The return location for the horizontal distance, or %NULL * @y: (out) (allow-none): The return location for the vertical distance, or %NULL * * Retrieves the threshold trigger distance of the gesture @action, * as set using clutter_gesture_action_set_threshold_trigger_distance(). * * Since: 1.18 */ void clutter_gesture_action_get_threshold_trigger_distance (ClutterGestureAction *action, float *x, float *y) { g_return_if_fail (CLUTTER_IS_GESTURE_ACTION (action)); if (x != NULL) { if (action->priv->distance_x > 0.0) *x = action->priv->distance_x; else *x = gesture_get_default_threshold (); } if (y != NULL) { if (action->priv->distance_y > 0.0) *y = action->priv->distance_y; else *y = gesture_get_default_threshold (); } } muffin-6.4.1/clutter/clutter/clutter-bezier.c0000664000175000017500000003353014723361714020216 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By Tomas Frydrych * * Copyright (C) 2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "clutter-build-config.h" #include #include #include "clutter-bezier.h" #include "clutter-debug.h" /* * We have some experimental code here to allow for constant velocity * movement of actors along the bezier path, this macro enables it. */ #undef CBZ_L2T_INTERPOLATION /**************************************************************************** * ClutterBezier -- represenation of a cubic bezier curve * * (private; a building block for the public bspline object) * ****************************************************************************/ /* * The t parameter of the bezier is from interval <0,1>, so we can use * 14.18 format and special multiplication functions that preserve * more of the least significant bits but would overflow if the value * is > 1 */ #define CBZ_T_Q 18 #define CBZ_T_ONE (1 << CBZ_T_Q) #define CBZ_T_MUL(x,y) ((((x) >> 3) * ((y) >> 3)) >> 12) #define CBZ_T_POW2(x) CBZ_T_MUL (x, x) #define CBZ_T_POW3(x) CBZ_T_MUL (CBZ_T_POW2 (x), x) #define CBZ_T_DIV(x,y) ((((x) << 9)/(y)) << 9) /* * Constants for sampling of the bezier */ #define CBZ_T_SAMPLES 128 #define CBZ_T_STEP (CBZ_T_ONE / CBZ_T_SAMPLES) #define CBZ_L_STEP (CBZ_T_ONE / CBZ_T_SAMPLES) #define FIXED_BITS (32) #define FIXED_Q (FIXED_BITS - 16) #define FIXED_FROM_INT(x) ((x) << FIXED_Q) typedef gint32 _FixedT; /* * This is a private type representing a single cubic bezier */ struct _ClutterBezier { /* * bezier coefficients -- these are calculated using multiplication and * addition from integer input, so these are also integers */ gint ax; gint bx; gint cx; gint dx; gint ay; gint by; gint cy; gint dy; /* length of the bezier */ guint length; #ifdef CBZ_L2T_INTERPOLATION /* * coefficients for the L -> t bezier; these are calculated from fixed * point input, and more specifically numbers that have been normalised * to fit <0,1>, so these are also fixed point, and we can used the * _FixedT type here. */ _FixedT La; _FixedT Lb; _FixedT Lc; /* _FixedT Ld; == 0 */ #endif }; ClutterBezier * _clutter_bezier_new (void) { return g_slice_new0 (ClutterBezier); } void _clutter_bezier_free (ClutterBezier * b) { if (G_LIKELY (b)) { g_slice_free (ClutterBezier, b); } } ClutterBezier * _clutter_bezier_clone_and_move (const ClutterBezier *b, gint x, gint y) { ClutterBezier * b2 = _clutter_bezier_new (); memcpy (b2, b, sizeof (ClutterBezier)); b2->dx += x; b2->dy += y; return b2; } #ifdef CBZ_L2T_INTERPOLATION /* * L is relative advance along the bezier curve from interval <0,1> */ static _FixedT _clutter_bezier_L2t (const ClutterBezier *b, _FixedT L) { _FixedT t = CBZ_T_MUL (b->La, CBZ_T_POW3(L)) + CBZ_T_MUL (b->Lb, CBZ_T_POW2(L)) + CBZ_T_MUL (b->Lc, L); if (t > CBZ_T_ONE) t = CBZ_T_ONE; else if (t < 0) t = 0; return t; } #endif static gint _clutter_bezier_t2x (const ClutterBezier * b, _FixedT t) { /* * NB -- the int coefficients can be at most 8192 for the multiplication * to work in this fashion due to the limits of the 14.18 fixed. */ return ((b->ax*CBZ_T_POW3(t) + b->bx*CBZ_T_POW2(t) + b->cx*t) >> CBZ_T_Q) + b->dx; } static gint _clutter_bezier_t2y (const ClutterBezier * b, _FixedT t) { /* * NB -- the int coefficients can be at most 8192 for the multiplication * to work in this fashion due to the limits of the 14.18 fixed. */ return ((b->ay*CBZ_T_POW3(t) + b->by*CBZ_T_POW2(t) + b->cy*t) >> CBZ_T_Q) + b->dy; } /* * Advances along the bezier to relative length L and returns the coordinances * in knot */ void _clutter_bezier_advance (const ClutterBezier *b, gint L, ClutterKnot * knot) { #ifdef CBZ_L2T_INTERPOLATION _FixedT t = clutter_bezier_L2t (b, L); #else _FixedT t = L; #endif knot->x = _clutter_bezier_t2x (b, t); knot->y = _clutter_bezier_t2y (b, t); CLUTTER_NOTE (MISC, "advancing to relative pt %f: t %f, {%d,%d}", (double) L / (double) CBZ_T_ONE, (double) t / (double) CBZ_T_ONE, knot->x, knot->y); } static int sqrti (int number) { #if defined __SSE2__ /* The GCC built-in with SSE2 (sqrtsd) is up to twice as fast as * the pure integer code below. It is also more accurate. */ return __builtin_sqrt (number); #else /* This is a fixed point implementation of the Quake III sqrt algorithm, * described, for example, at * http://www.codemaestro.com/reviews/review00000105.html * * While the original QIII is extremely fast, the use of floating division * and multiplication makes it perform very on arm processors without FPU. * * The key to successfully replacing the floating point operations with * fixed point is in the choice of the fixed point format. The QIII * algorithm does not calculate the square root, but its reciprocal ('y' * below), which is only at the end turned to the inverse value. In order * for the algorithm to produce satisfactory results, the reciprocal value * must be represented with sufficient precission; the 16.16 we use * elsewhere in clutter is not good enough, and 10.22 is used instead. */ _FixedT x; uint32_t y_1; /* 10.22 fixed point */ uint32_t f = 0x600000; /* '1.5' as 10.22 fixed */ union { float f; uint32_t i; } flt, flt2; flt.f = number; x = FIXED_FROM_INT (number) / 2; /* The QIII initial estimate */ flt.i = 0x5f3759df - ( flt.i >> 1 ); /* Now, we convert the float to 10.22 fixed. We exploit the mechanism * described at http://www.d6.com/users/checker/pdfs/gdmfp.pdf. * * We want 22 bit fraction; a single precission float uses 23 bit * mantisa, so we only need to add 2^(23-22) (no need for the 1.5 * multiplier as we are only dealing with positive numbers). * * Note: we have to use two separate variables here -- for some reason, * if we try to use just the flt variable, gcc on ARM optimises the whole * addition out, and it all goes pear shape, since without it, the bits * in the float will not be correctly aligned. */ flt2.f = flt.f + 2.0; flt2.i &= 0x7FFFFF; /* Now we correct the estimate */ y_1 = (flt2.i >> 11) * (flt2.i >> 11); y_1 = (y_1 >> 8) * (x >> 8); y_1 = f - y_1; flt2.i = (flt2.i >> 11) * (y_1 >> 11); /* If the original argument is less than 342, we do another * iteration to improve precission (for arguments >= 342, the single * iteration produces generally better results). */ if (x < 171) { y_1 = (flt2.i >> 11) * (flt2.i >> 11); y_1 = (y_1 >> 8) * (x >> 8); y_1 = f - y_1; flt2.i = (flt2.i >> 11) * (y_1 >> 11); } /* Invert, round and convert from 10.22 to an integer * 0x1e3c68 is a magical rounding constant that produces slightly * better results than 0x200000. */ return (number * flt2.i + 0x1e3c68) >> 22; #endif } void _clutter_bezier_init (ClutterBezier *b, gint x_0, gint y_0, gint x_1, gint y_1, gint x_2, gint y_2, gint x_3, gint y_3) { _FixedT t; int i; int xp = x_0; int yp = y_0; _FixedT length [CBZ_T_SAMPLES + 1]; #ifdef CBZ_L2T_INTERPOLATION int j, k; _FixedT L; _FixedT t_equalized [CBZ_T_SAMPLES + 1]; #endif #if 0 g_debug ("Initializing bezier at {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}", x0, y0, x1, y1, x2, y2, x3, y3); #endif b->dx = x_0; b->dy = y_0; b->cx = 3 * (x_1 - x_0); b->cy = 3 * (y_1 - y_0); b->bx = 3 * (x_2 - x_1) - b->cx; b->by = 3 * (y_2 - y_1) - b->cy; b->ax = x_3 - 3 * x_2 + 3 * x_1 - x_0; b->ay = y_3 - 3 * y_2 + 3 * y_1 - y_0; #if 0 g_debug ("Cooeficients {{%d,%d},{%d,%d},{%d,%d},{%d,%d}}", b->ax, b->ay, b->bx, b->by, b->cx, b->cy, b->dx, b->dy); #endif /* * Because of the way we do the multiplication in bezeir_t2x,y * these coefficients need to be at most 0x1fff; this should be the case, * I think, but have added this warning to catch any problems -- if it * triggers, we need to change those two functions a bit. */ if (b->ax > 0x1fff || b->bx > 0x1fff || b->cx > 0x1fff) g_warning ("Calculated coefficients will result in multiplication " "overflow in clutter_bezier_t2x and clutter_bezier_t2y."); /* * Sample the bezier with CBZ_T_SAMPLES and calculate length at * each point. * * We are working with integers here, so we use the fast sqrti function. */ length[0] = 0; for (t = CBZ_T_STEP, i = 1; i <= CBZ_T_SAMPLES; ++i, t += CBZ_T_STEP) { int x = _clutter_bezier_t2x (b, t); int y = _clutter_bezier_t2y (b, t); guint l = sqrti ((y - yp)*(y - yp) + (x - xp)*(x - xp)); l += length[i-1]; length[i] = l; xp = x; yp = y; } b->length = length[CBZ_T_SAMPLES]; #if 0 g_debug ("length %d", b->length); #endif #ifdef CBZ_L2T_INTERPOLATION /* * Now normalize the length values, converting them into _FixedT */ for (i = 0; i <= CBZ_T_SAMPLES; ++i) { length[i] = (length[i] << CBZ_T_Q) / b->length; } /* * Now generate a L -> t table such that the L will equidistant * over <0,1> */ t_equalized[0] = 0; for (i = 1, j = 1, L = CBZ_L_STEP; i < CBZ_T_SAMPLES; ++i, L += CBZ_L_STEP) { _FixedT l1, l2; _FixedT d1, d2, d; _FixedT t1, t2; /* find the band for our L */ for (k = j; k < CBZ_T_SAMPLES; ++k) { if (L < length[k]) break; } /* * Now we know that L is from (length[k-1],length[k]> * We remember k-1 in order not to have to iterate over the * whole length array in the next iteration of the main loop */ j = k - 1; /* * Now interpolate equlised t as a weighted average */ l1 = length[k-1]; l2 = length[k]; d1 = l2 - L; d2 = L - l1; d = l2 - l1; t1 = (k - 1) * CBZ_T_STEP; t2 = k * CBZ_T_STEP; t_equalized[i] = (t1*d1 + t2*d2)/d; if (t_equalized[i] < t_equalized[i-1]) g_debug ("wrong t: L %f, l1 %f, l2 %f, t1 %f, t2 %f", (double) (L)/(double)CBZ_T_ONE, (double) (l1)/(double)CBZ_T_ONE, (double) (l2)/(double)CBZ_T_ONE, (double) (t1)/(double)CBZ_T_ONE, (double) (t2)/(double)CBZ_T_ONE); } t_equalized[CBZ_T_SAMPLES] = CBZ_T_ONE; /* We now fit a bezier -- at this stage, do a single fit through our values * at 0, 1/3, 2/3 and 1 * * FIXME -- do we need to use a better fitting approach to choose the best * beziere. The actual curve we acquire this way is not too bad shapwise, * but (probably due to rounding errors) the resulting curve no longer * satisfies the necessary condition that for L2 > L1, t2 > t1, which * causes oscilation. */ #if 0 /* * These are the control points we use to calculate the curve coefficients * for bezier t(L); these are not needed directly, but are implied in the * calculations below. * * (p0 is 0,0, and p3 is 1,1) */ p1 = (18 * t_equalized[CBZ_T_SAMPLES/3] - 9 * t_equalized[2*CBZ_T_SAMPLES/3] + 2 << CBZ_T_Q) / 6; p2 = (18 * t_equalized[2*CBZ_T_SAMPLES/3] - 9 * t_equalized[CBZ_T_SAMPLES/3] - (5 << CBZ_T_Q)) / 6; #endif b->Lc = (18 * t_equalized[CBZ_T_SAMPLES/3] - 9 * t_equalized[2*CBZ_T_SAMPLES/3] + (2 << CBZ_T_Q)) >> 1; b->Lb = (36 * t_equalized[2*CBZ_T_SAMPLES/3] - 45 * t_equalized[CBZ_T_SAMPLES/3] - (9 << CBZ_T_Q)) >> 1; b->La = ((27 * (t_equalized[CBZ_T_SAMPLES/3] - t_equalized[2*CBZ_T_SAMPLES/3]) + (7 << CBZ_T_Q)) >> 1) + CBZ_T_ONE; g_debug ("t(1/3) %f, t(2/3) %f", (double)t_equalized[CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE, (double)t_equalized[2*CBZ_T_SAMPLES/3]/(double)CBZ_T_ONE); g_debug ("L -> t coefficients: %f, %f, %f", (double)b->La/(double)CBZ_T_ONE, (double)b->Lb/(double)CBZ_T_ONE, (double)b->Lc/(double)CBZ_T_ONE); /* * For debugging, you can load these values into a spreadsheet and graph * them to see how well the approximation matches the data */ for (i = 0; i < CBZ_T_SAMPLES; ++i) { g_print ("%f, %f, %f\n", (double)(i*CBZ_T_STEP)/(double)CBZ_T_ONE, (double)(t_equalized[i])/(double)CBZ_T_ONE, (double)(clutter_bezier_L2t(b,i*CBZ_T_STEP))/(double)CBZ_T_ONE); } #endif } /* * Moves a control point at indx to location represented by knot */ void _clutter_bezier_adjust (ClutterBezier * b, ClutterKnot * knot, guint indx) { guint x[4], y[4]; g_assert (indx < 4); x[0] = b->dx; y[0] = b->dy; x[1] = b->cx / 3 + x[0]; y[1] = b->cy / 3 + y[0]; x[2] = b->bx / 3 + b->cx + x[1]; y[2] = b->by / 3 + b->cy + y[1]; x[3] = b->ax + x[0] + b->cx + b->bx; y[3] = b->ay + y[0] + b->cy + b->by; x[indx] = knot->x; y[indx] = knot->y; _clutter_bezier_init (b, x[0], y[0], x[1], y[1], x[2], y[2], x[3], y[3]); } guint _clutter_bezier_get_length (const ClutterBezier *b) { return b->length; } muffin-6.4.1/clutter/clutter/clutter-master-clock.h0000664000175000017500000000636314723361714021333 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Authored By: Emmanuele Bassi * * Copyright (C) 2009 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef __CLUTTER_MASTER_CLOCK_H__ #define __CLUTTER_MASTER_CLOCK_H__ #include G_BEGIN_DECLS #define CLUTTER_TYPE_MASTER_CLOCK (clutter_master_clock_get_type ()) G_DECLARE_INTERFACE (ClutterMasterClock, clutter_master_clock, CLUTTER, MASTER_CLOCK, GObject) struct _ClutterMasterClockInterface { /*< private >*/ GTypeInterface parent_iface; void (* add_timeline) (ClutterMasterClock *master_clock, ClutterTimeline *timeline); void (* remove_timeline) (ClutterMasterClock *master_clock, ClutterTimeline *timeline); void (* start_running) (ClutterMasterClock *master_clock); void (* ensure_next_iteration) (ClutterMasterClock *master_clock); void (* set_paused) (ClutterMasterClock *master_clock, gboolean paused); }; ClutterMasterClock * _clutter_master_clock_get_default (void); void _clutter_master_clock_add_timeline (ClutterMasterClock *master_clock, ClutterTimeline *timeline); void _clutter_master_clock_remove_timeline (ClutterMasterClock *master_clock, ClutterTimeline *timeline); void _clutter_master_clock_start_running (ClutterMasterClock *master_clock); void _clutter_master_clock_ensure_next_iteration (ClutterMasterClock *master_clock); void _clutter_master_clock_set_paused (ClutterMasterClock *master_clock, gboolean paused); void _clutter_timeline_advance (ClutterTimeline *timeline, gint64 tick_time); gint64 _clutter_timeline_get_delta (ClutterTimeline *timeline); void _clutter_timeline_do_tick (ClutterTimeline *timeline, gint64 tick_time); G_END_DECLS #endif /* __CLUTTER_MASTER_CLOCK_H__ */ muffin-6.4.1/clutter/clutter/clutter-keyframe-transition.c0000664000175000017500000005276114723361714022740 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ /** * SECTION:clutter-keyframe-transition * @Title: ClutterKeyframeTransition * @Short_Description: Keyframe property transition * * #ClutterKeyframeTransition allows animating a property by defining * "key frames": values at a normalized position on the transition * duration. * * The #ClutterKeyframeTransition interpolates the value of the property * to which it's bound across these key values. * * Setting up a #ClutterKeyframeTransition means providing the times, * values, and easing modes between these key frames, for instance: * * |[ * ClutterTransition *keyframe; * * keyframe = clutter_keyframe_transition_new ("opacity"); * clutter_transition_set_from (keyframe, G_TYPE_UINT, 255); * clutter_transition_set_to (keyframe, G_TYPE_UINT, 0); * clutter_keyframe_transition_set (CLUTTER_KEYFRAME_TRANSITION (keyframe), * G_TYPE_UINT, * 1, /* number of key frames */ * 0.5, 128, CLUTTER_EASE_IN_OUT_CUBIC); * ]| * * The example above sets up a keyframe transition for the #ClutterActor:opacity * property of a #ClutterActor; the transition starts and sets the value of the * property to fully transparent; between the start of the transition and its mid * point, it will animate the property to half opacity, using an easy in/easy out * progress. Once the transition reaches the mid point, it will linearly fade the * actor out until it reaches the end of the transition. * * The #ClutterKeyframeTransition will add an implicit key frame between the last * and the 1.0 value, to interpolate to the final value of the transition's * interval. * * #ClutterKeyframeTransition is available since Clutter 1.12. */ #include "clutter-build-config.h" #include "clutter-keyframe-transition.h" #include "clutter-debug.h" #include "clutter-easing.h" #include "clutter-interval.h" #include "clutter-private.h" #include "clutter-timeline.h" #include #include typedef struct _KeyFrame { double key; double start; double end; ClutterAnimationMode mode; ClutterInterval *interval; } KeyFrame; struct _ClutterKeyframeTransitionPrivate { GArray *frames; gint current_frame; }; G_DEFINE_TYPE_WITH_PRIVATE (ClutterKeyframeTransition, clutter_keyframe_transition, CLUTTER_TYPE_PROPERTY_TRANSITION) static void key_frame_free (gpointer data) { if (data != NULL) { KeyFrame *key = data; g_object_unref (key->interval); } } static int sort_by_key (gconstpointer a, gconstpointer b) { const KeyFrame *k_a = a; const KeyFrame *k_b = b; if (fabs (k_a->key - k_b->key) < 0.0001) return 0; if (k_a->key > k_b->key) return 1; return -1; } static inline void clutter_keyframe_transition_sort_frames (ClutterKeyframeTransition *transition) { if (transition->priv->frames != NULL) g_array_sort (transition->priv->frames, sort_by_key); } static inline void clutter_keyframe_transition_init_frames (ClutterKeyframeTransition *transition, gssize n_key_frames) { ClutterKeyframeTransitionPrivate *priv = transition->priv; guint i; priv->frames = g_array_sized_new (FALSE, FALSE, sizeof (KeyFrame), n_key_frames); g_array_set_clear_func (priv->frames, key_frame_free); /* we add an implicit key frame that goes to 1.0, so that the * user doesn't have to do that an can simply add key frames * in between 0.0 and 1.0 */ for (i = 0; i < n_key_frames + 1; i++) { KeyFrame frame; if (i == n_key_frames) frame.key = 1.0; else frame.key = 0.0; frame.mode = CLUTTER_LINEAR; frame.interval = NULL; g_array_insert_val (priv->frames, i, frame); } } static inline void clutter_keyframe_transition_update_frames (ClutterKeyframeTransition *transition) { ClutterKeyframeTransitionPrivate *priv = transition->priv; guint i; if (priv->frames == NULL) return; for (i = 0; i < priv->frames->len; i++) { KeyFrame *cur_frame = &g_array_index (priv->frames, KeyFrame, i); KeyFrame *prev_frame; if (i > 0) prev_frame = &g_array_index (priv->frames, KeyFrame, i - 1); else prev_frame = NULL; if (prev_frame != NULL) { cur_frame->start = prev_frame->key; if (prev_frame->interval != NULL) { const GValue *value; value = clutter_interval_peek_final_value (prev_frame->interval); if (cur_frame->interval != NULL) clutter_interval_set_initial_value (cur_frame->interval, value); else { cur_frame->interval = clutter_interval_new_with_values (G_VALUE_TYPE (value), value, NULL); } } } else cur_frame->start = 0.0; cur_frame->end = cur_frame->key; } } static void clutter_keyframe_transition_compute_value (ClutterTransition *transition, ClutterAnimatable *animatable, ClutterInterval *interval, gdouble progress) { ClutterKeyframeTransition *self = CLUTTER_KEYFRAME_TRANSITION (transition); ClutterTimeline *timeline = CLUTTER_TIMELINE (transition); ClutterKeyframeTransitionPrivate *priv = self->priv; ClutterTransitionClass *parent_class; ClutterTimelineDirection direction; ClutterInterval *real_interval; gdouble real_progress; double t, d, p; KeyFrame *cur_frame = NULL; real_interval = interval; real_progress = progress; /* if we don't have any keyframe, we behave like our parent class */ if (priv->frames == NULL) goto out; direction = clutter_timeline_get_direction (timeline); /* we need a normalized linear value */ t = clutter_timeline_get_elapsed_time (timeline); d = clutter_timeline_get_duration (timeline); p = t / d; if (priv->current_frame < 0) { if (direction == CLUTTER_TIMELINE_FORWARD) priv->current_frame = 0; else priv->current_frame = priv->frames->len - 1; } cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); /* skip to the next key frame, depending on the direction of the timeline */ if (direction == CLUTTER_TIMELINE_FORWARD) { if (p > cur_frame->end) { priv->current_frame = MIN (priv->current_frame + 1, priv->frames->len - 1); cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); } } else { if (p < cur_frame->start) { priv->current_frame = MAX (priv->current_frame - 1, 0); cur_frame = &g_array_index (priv->frames, KeyFrame, priv->current_frame); } } /* if we are at the boundaries of the transition, use the from and to * value from the transition */ if (priv->current_frame == 0) { const GValue *value; value = clutter_interval_peek_initial_value (interval); clutter_interval_set_initial_value (cur_frame->interval, value); } else if (priv->current_frame == priv->frames->len - 1) { const GValue *value; cur_frame->mode = clutter_timeline_get_progress_mode (timeline); value = clutter_interval_peek_final_value (interval); clutter_interval_set_final_value (cur_frame->interval, value); } /* update the interval to be used to interpolate the property */ real_interval = cur_frame->interval; /* normalize the progress and apply the easing mode */ real_progress = clutter_easing_for_mode ( cur_frame->mode, (p - cur_frame->start), (cur_frame->end - cur_frame->start)); #ifdef CLUTTER_ENABLE_DEBUG if (CLUTTER_HAS_DEBUG (ANIMATION)) { char *from, *to; const GValue *value; value = clutter_interval_peek_initial_value (cur_frame->interval); from = g_strdup_value_contents (value); value = clutter_interval_peek_final_value (cur_frame->interval); to = g_strdup_value_contents (value); CLUTTER_NOTE (ANIMATION, "cur_frame [%d] => { %g, %s, %s %s %s } - " "progress: %g, sub-progress: %g\n", priv->current_frame, cur_frame->key, clutter_get_easing_name_for_mode (cur_frame->mode), from, direction == CLUTTER_TIMELINE_FORWARD ? "->" : "<-", to, p, real_progress); g_free (from); g_free (to); } #endif /* CLUTTER_ENABLE_DEBUG */ out: parent_class = CLUTTER_TRANSITION_CLASS (clutter_keyframe_transition_parent_class); parent_class->compute_value (transition, animatable, real_interval, real_progress); } static void clutter_keyframe_transition_started (ClutterTimeline *timeline) { ClutterKeyframeTransition *transition; transition = CLUTTER_KEYFRAME_TRANSITION (timeline); transition->priv->current_frame = -1; clutter_keyframe_transition_sort_frames (transition); clutter_keyframe_transition_update_frames (transition); } static void clutter_keyframe_transition_completed (ClutterTimeline *timeline) { ClutterKeyframeTransitionPrivate *priv; priv = CLUTTER_KEYFRAME_TRANSITION (timeline)->priv; priv->current_frame = -1; } static void clutter_keyframe_transition_finalize (GObject *gobject) { ClutterKeyframeTransitionPrivate *priv; priv = CLUTTER_KEYFRAME_TRANSITION (gobject)->priv; if (priv->frames != NULL) g_array_unref (priv->frames); G_OBJECT_CLASS (clutter_keyframe_transition_parent_class)->finalize (gobject); } static void clutter_keyframe_transition_class_init (ClutterKeyframeTransitionClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterTimelineClass *timeline_class = CLUTTER_TIMELINE_CLASS (klass); ClutterTransitionClass *transition_class = CLUTTER_TRANSITION_CLASS (klass); gobject_class->finalize = clutter_keyframe_transition_finalize; timeline_class->started = clutter_keyframe_transition_started; timeline_class->completed = clutter_keyframe_transition_completed; transition_class->compute_value = clutter_keyframe_transition_compute_value; } static void clutter_keyframe_transition_init (ClutterKeyframeTransition *self) { self->priv = clutter_keyframe_transition_get_instance_private (self); } /** * clutter_keyframe_transition_new: * @property_name: the property to animate * * Creates a new #ClutterKeyframeTransition for @property_name. * * Return value: (transfer full): the newly allocated * #ClutterKeyframeTransition instance. Use g_object_unref() when * done to free its resources. * * Since: 1.12 */ ClutterTransition * clutter_keyframe_transition_new (const char *property_name) { return g_object_new (CLUTTER_TYPE_KEYFRAME_TRANSITION, "property-name", property_name, NULL); } /** * clutter_keyframe_transition_set_key_frames: * @transition: a #ClutterKeyframeTransition * @n_key_frames: the number of values * @key_frames: (array length=n_key_frames): an array of keys between 0.0 * and 1.0, one for each key frame * * Sets the keys for each key frame inside @transition. * * If @transition does not hold any key frame, @n_key_frames key frames * will be created; if @transition already has key frames, @key_frames must * have at least as many elements as the number of key frames. * * Since: 1.12 */ void clutter_keyframe_transition_set_key_frames (ClutterKeyframeTransition *transition, guint n_key_frames, const double *key_frames) { ClutterKeyframeTransitionPrivate *priv; guint i; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); g_return_if_fail (n_key_frames > 0); g_return_if_fail (key_frames != NULL); priv = transition->priv; if (priv->frames == NULL) clutter_keyframe_transition_init_frames (transition, n_key_frames); else g_return_if_fail (n_key_frames == priv->frames->len - 1); for (i = 0; i < n_key_frames; i++) { KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); frame->key = key_frames[i]; } } /** * clutter_keyframe_transition_set_values: * @transition: a #ClutterKeyframeTransition * @n_values: the number of values * @values: (array length=n_values): an array of values, one for each * key frame * * Sets the values for each key frame inside @transition. * * If @transition does not hold any key frame, @n_values key frames will * be created; if @transition already has key frames, @values must have * at least as many elements as the number of key frames. * * Since: 1.12 */ void clutter_keyframe_transition_set_values (ClutterKeyframeTransition *transition, guint n_values, const GValue *values) { ClutterKeyframeTransitionPrivate *priv; guint i; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); g_return_if_fail (n_values > 0); g_return_if_fail (values != NULL); priv = transition->priv; if (priv->frames == NULL) clutter_keyframe_transition_init_frames (transition, n_values); else g_return_if_fail (n_values == priv->frames->len - 1); for (i = 0; i < n_values; i++) { KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); if (frame->interval) clutter_interval_set_final_value (frame->interval, &values[i]); else frame->interval = clutter_interval_new_with_values (G_VALUE_TYPE (&values[i]), NULL, &values[i]); } } /** * clutter_keyframe_transition_set_modes: * @transition: a #ClutterKeyframeTransition * @n_modes: the number of easing modes * @modes: (array length=n_modes): an array of easing modes, one for * each key frame * * Sets the easing modes for each key frame inside @transition. * * If @transition does not hold any key frame, @n_modes key frames will * be created; if @transition already has key frames, @modes must have * at least as many elements as the number of key frames. * * Since: 1.12 */ void clutter_keyframe_transition_set_modes (ClutterKeyframeTransition *transition, guint n_modes, const ClutterAnimationMode *modes) { ClutterKeyframeTransitionPrivate *priv; guint i; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); g_return_if_fail (n_modes > 0); g_return_if_fail (modes != NULL); priv = transition->priv; if (priv->frames == NULL) clutter_keyframe_transition_init_frames (transition, n_modes); else g_return_if_fail (n_modes == priv->frames->len - 1); for (i = 0; i < n_modes; i++) { KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); frame->mode = modes[i]; } } /** * clutter_keyframe_transition_set: (skip) * @transition: a #ClutterKeyframeTransition * @gtype: the type of the values to use for the key frames * @n_key_frames: the number of key frames between the initial * and final values * @...: a list of tuples, containing the key frame index, the value * at the key frame, and the animation mode * * Sets the key frames of the @transition. * * This variadic arguments function is a convenience for C developers; * language bindings should use clutter_keyframe_transition_set_key_frames(), * clutter_keyframe_transition_set_modes(), and * clutter_keyframe_transition_set_values() instead. * * Since: 1.12 */ void clutter_keyframe_transition_set (ClutterKeyframeTransition *transition, GType gtype, guint n_key_frames, ...) { ClutterKeyframeTransitionPrivate *priv; va_list args; guint i; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); g_return_if_fail (gtype != G_TYPE_INVALID); g_return_if_fail (n_key_frames > 0); priv = transition->priv; if (priv->frames == NULL) clutter_keyframe_transition_init_frames (transition, n_key_frames); else g_return_if_fail (n_key_frames == priv->frames->len - 1); va_start (args, n_key_frames); for (i = 0; i < n_key_frames; i++) { KeyFrame *frame = &g_array_index (priv->frames, KeyFrame, i); GValue value = G_VALUE_INIT; char *error = NULL; frame->key = va_arg (args, double); G_VALUE_COLLECT_INIT (&value, gtype, args, 0, &error); if (error != NULL) { g_warning ("%s: %s", G_STRLOC, error); g_free (error); break; } frame->mode = va_arg (args, ClutterAnimationMode); g_clear_object (&frame->interval); frame->interval = clutter_interval_new_with_values (gtype, NULL, &value); g_value_unset (&value); } va_end (args); } /** * clutter_keyframe_transition_clear: * @transition: a #ClutterKeyframeTransition * * Removes all key frames from @transition. * * Since: 1.12 */ void clutter_keyframe_transition_clear (ClutterKeyframeTransition *transition) { g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); if (transition->priv->frames != NULL) { g_array_unref (transition->priv->frames); transition->priv->frames = NULL; } } /** * clutter_keyframe_transition_get_n_key_frames: * @transition: a #ClutterKeyframeTransition * * Retrieves the number of key frames inside @transition. * * Return value: the number of key frames * * Since: 1.12 */ guint clutter_keyframe_transition_get_n_key_frames (ClutterKeyframeTransition *transition) { g_return_val_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition), 0); if (transition->priv->frames == NULL) return 0; return transition->priv->frames->len - 1; } /** * clutter_keyframe_transition_set_key_frame: * @transition: a #ClutterKeyframeTransition * @index_: the index of the key frame * @key: the key of the key frame * @mode: the easing mode of the key frame * @value: a #GValue containing the value of the key frame * * Sets the details of the key frame at @index_ inside @transition. * * The @transition must already have a key frame at @index_, and @index_ * must be smaller than the number of key frames inside @transition. * * Since: 1.12 */ void clutter_keyframe_transition_set_key_frame (ClutterKeyframeTransition *transition, guint index_, double key, ClutterAnimationMode mode, const GValue *value) { ClutterKeyframeTransitionPrivate *priv; KeyFrame *frame; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); priv = transition->priv; g_return_if_fail (priv->frames != NULL); g_return_if_fail (index_ < priv->frames->len - 1); frame = &g_array_index (priv->frames, KeyFrame, index_); frame->key = key; frame->mode = mode; clutter_interval_set_final_value (frame->interval, value); } /** * clutter_keyframe_transition_get_key_frame: * @transition: a #ClutterKeyframeTransition * @index_: the index of the key frame * @key: (out) (allow-none): return location for the key, or %NULL * @mode: (out) (allow-none): return location for the easing mode, or %NULL * @value: (out caller-allocates): a #GValue initialized with the type of * the values * * Retrieves the details of the key frame at @index_ inside @transition. * * The @transition must already have key frames set, and @index_ must be * smaller than the number of key frames. * * Since: 1.12 */ void clutter_keyframe_transition_get_key_frame (ClutterKeyframeTransition *transition, guint index_, double *key, ClutterAnimationMode *mode, GValue *value) { ClutterKeyframeTransitionPrivate *priv; const KeyFrame *frame; g_return_if_fail (CLUTTER_IS_KEYFRAME_TRANSITION (transition)); priv = transition->priv; g_return_if_fail (priv->frames != NULL); g_return_if_fail (index_ < priv->frames->len - 1); frame = &g_array_index (priv->frames, KeyFrame, index_); if (key != NULL) *key = frame->key; if (mode != NULL) *mode = frame->mode; if (value != NULL) clutter_interval_get_final_value (frame->interval, value); } muffin-6.4.1/clutter/clutter/clutter-scroll-actor.c0000664000175000017500000003126214723361714021342 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2012 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:clutter-scroll-actor * @Title: ClutterScrollActor * @Short_Description: An actor for displaying a portion of its children * * #ClutterScrollActor is an actor that can be used to display a portion * of the contents of its children. * * The extent of the area of a #ClutterScrollActor is defined by the size * of its children; the visible region of the children of a #ClutterScrollActor * is set by using clutter_scroll_actor_scroll_to_point() or by using * clutter_scroll_actor_scroll_to_rect() to define a point or a rectangle * acting as the origin, respectively. * * #ClutterScrollActor does not provide pointer or keyboard event handling, * nor does it provide visible scroll handles. * * See [scroll-actor.c](https://git.gnome.org/browse/clutter/tree/examples/scroll-actor.c?h=clutter-1.18) * for an example of how to use #ClutterScrollActor. * * #ClutterScrollActor is available since Clutter 1.12. */ #include "clutter-build-config.h" #include "clutter-scroll-actor.h" #include "clutter-actor-private.h" #include "clutter-animatable.h" #include "clutter-debug.h" #include "clutter-enum-types.h" #include "clutter-private.h" #include "clutter-property-transition.h" #include "clutter-transition.h" struct _ClutterScrollActorPrivate { graphene_point_t scroll_to; ClutterScrollMode scroll_mode; ClutterTransition *transition; }; enum { PROP_0, PROP_SCROLL_MODE, PROP_LAST }; enum { ANIM_PROP_0, ANIM_PROP_SCROLL_TO, ANIM_PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; static GParamSpec *animatable_props[ANIM_PROP_LAST] = { NULL, }; static ClutterAnimatableInterface *parent_animatable_iface = NULL; static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface); G_DEFINE_TYPE_WITH_CODE (ClutterScrollActor, clutter_scroll_actor, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (ClutterScrollActor) G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_ANIMATABLE, clutter_animatable_iface_init)) static void clutter_scroll_actor_set_scroll_to_internal (ClutterScrollActor *self, const graphene_point_t *point) { ClutterScrollActorPrivate *priv = self->priv; ClutterActor *actor = CLUTTER_ACTOR (self); ClutterMatrix m = CLUTTER_MATRIX_INIT_IDENTITY; float dx, dy; if (graphene_point_equal (&priv->scroll_to, point)) return; if (point == NULL) graphene_point_init (&priv->scroll_to, 0.f, 0.f); else priv->scroll_to = *point; if (priv->scroll_mode & CLUTTER_SCROLL_HORIZONTALLY) dx = -priv->scroll_to.x; else dx = 0.f; if (priv->scroll_mode & CLUTTER_SCROLL_VERTICALLY) dy = -priv->scroll_to.y; else dy = 0.f; cogl_matrix_translate (&m, dx, dy, 0.f); clutter_actor_set_child_transform (actor, &m); } static void clutter_scroll_actor_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject); switch (prop_id) { case PROP_SCROLL_MODE: clutter_scroll_actor_set_scroll_mode (actor, g_value_get_flags (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_scroll_actor_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { ClutterScrollActor *actor = CLUTTER_SCROLL_ACTOR (gobject); switch (prop_id) { case PROP_SCROLL_MODE: g_value_set_flags (value, actor->priv->scroll_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); } } static void clutter_scroll_actor_class_init (ClutterScrollActorClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->set_property = clutter_scroll_actor_set_property; gobject_class->get_property = clutter_scroll_actor_get_property; /** * ClutterScrollActor:scroll-mode: * * The scrollin direction. * * Since: 1.12 */ obj_props[PROP_SCROLL_MODE] = g_param_spec_flags ("scroll-mode", P_("Scroll Mode"), P_("The scrolling direction"), CLUTTER_TYPE_SCROLL_MODE, CLUTTER_SCROLL_BOTH, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void clutter_scroll_actor_init (ClutterScrollActor *self) { self->priv = clutter_scroll_actor_get_instance_private (self); self->priv->scroll_mode = CLUTTER_SCROLL_BOTH; clutter_actor_set_clip_to_allocation (CLUTTER_ACTOR (self), TRUE); } static GParamSpec * clutter_scroll_actor_find_property (ClutterAnimatable *animatable, const char *property_name) { if (strcmp (property_name, "scroll-to") == 0) return animatable_props[ANIM_PROP_SCROLL_TO]; return parent_animatable_iface->find_property (animatable, property_name); } static void clutter_scroll_actor_set_final_state (ClutterAnimatable *animatable, const char *property_name, const GValue *value) { if (strcmp (property_name, "scroll-to") == 0) { ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable); const graphene_point_t *point = g_value_get_boxed (value); clutter_scroll_actor_set_scroll_to_internal (self, point); } else parent_animatable_iface->set_final_state (animatable, property_name, value); } static void clutter_scroll_actor_get_initial_state (ClutterAnimatable *animatable, const char *property_name, GValue *value) { if (strcmp (property_name, "scroll-to") == 0) { ClutterScrollActor *self = CLUTTER_SCROLL_ACTOR (animatable); g_value_set_boxed (value, &self->priv->scroll_to); } else parent_animatable_iface->get_initial_state (animatable, property_name, value); } static void clutter_animatable_iface_init (ClutterAnimatableInterface *iface) { parent_animatable_iface = g_type_interface_peek_parent (iface); animatable_props[ANIM_PROP_SCROLL_TO] = g_param_spec_boxed ("scroll-to", "Scroll To", "The point to scroll the actor to", GRAPHENE_TYPE_POINT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | CLUTTER_PARAM_ANIMATABLE); iface->find_property = clutter_scroll_actor_find_property; iface->get_initial_state = clutter_scroll_actor_get_initial_state; iface->set_final_state = clutter_scroll_actor_set_final_state; } /** * clutter_scroll_actor_new: * * Creates a new #ClutterScrollActor. * * Return value: The newly created #ClutterScrollActor * instance. * * Since: 1.12 */ ClutterActor * clutter_scroll_actor_new (void) { return g_object_new (CLUTTER_TYPE_SCROLL_ACTOR, NULL); } /** * clutter_scroll_actor_set_scroll_mode: * @actor: a #ClutterScrollActor * @mode: a #ClutterScrollMode * * Sets the #ClutterScrollActor:scroll-mode property. * * Since: 1.12 */ void clutter_scroll_actor_set_scroll_mode (ClutterScrollActor *actor, ClutterScrollMode mode) { ClutterScrollActorPrivate *priv; g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor)); priv = actor->priv; if (priv->scroll_mode == mode) return; priv->scroll_mode = mode; g_object_notify_by_pspec (G_OBJECT (actor), obj_props[PROP_SCROLL_MODE]); } /** * clutter_scroll_actor_get_scroll_mode: * @actor: a #ClutterScrollActor * * Retrieves the #ClutterScrollActor:scroll-mode property * * Return value: the scrolling mode * * Since: 1.12 */ ClutterScrollMode clutter_scroll_actor_get_scroll_mode (ClutterScrollActor *actor) { g_return_val_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor), CLUTTER_SCROLL_NONE); return actor->priv->scroll_mode; } /** * clutter_scroll_actor_scroll_to_point: * @actor: a #ClutterScrollActor * @point: a #graphene_point_t * * Scrolls the contents of @actor so that @point is the new origin * of the visible area. * * The coordinates of @point must be relative to the @actor. * * This function will use the currently set easing state of the @actor * to transition from the current scroll origin to the new one. * * Since: 1.12 */ void clutter_scroll_actor_scroll_to_point (ClutterScrollActor *actor, const graphene_point_t *point) { ClutterScrollActorPrivate *priv; const ClutterAnimationInfo *info; g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor)); g_return_if_fail (point != NULL); priv = actor->priv; info = _clutter_actor_get_animation_info (CLUTTER_ACTOR (actor)); /* jump to the end if there is no easing state, or if the easing * state has a duration of 0 msecs */ if (info->cur_state == NULL || info->cur_state->easing_duration == 0) { /* ensure that we remove any currently running transition */ if (priv->transition != NULL) { clutter_actor_remove_transition (CLUTTER_ACTOR (actor), "scroll-to"); priv->transition = NULL; } clutter_scroll_actor_set_scroll_to_internal (actor, point); return; } if (priv->transition == NULL) { priv->transition = clutter_property_transition_new ("scroll-to"); clutter_transition_set_animatable (priv->transition, CLUTTER_ANIMATABLE (actor)); clutter_transition_set_remove_on_complete (priv->transition, TRUE); /* delay only makes sense if the transition has just been created */ clutter_timeline_set_delay (CLUTTER_TIMELINE (priv->transition), info->cur_state->easing_delay); /* we need this to clear the priv->transition pointer */ g_object_add_weak_pointer (G_OBJECT (priv->transition), (gpointer *) &priv->transition); clutter_actor_add_transition (CLUTTER_ACTOR (actor), "scroll-to", priv->transition); /* the actor now owns the transition */ g_object_unref (priv->transition); } /* if a transition already exist, update its bounds */ clutter_transition_set_from (priv->transition, GRAPHENE_TYPE_POINT, &priv->scroll_to); clutter_transition_set_to (priv->transition, GRAPHENE_TYPE_POINT, point); /* always use the current easing state */ clutter_timeline_set_duration (CLUTTER_TIMELINE (priv->transition), info->cur_state->easing_duration); clutter_timeline_set_progress_mode (CLUTTER_TIMELINE (priv->transition), info->cur_state->easing_mode); /* ensure that we start from the beginning */ clutter_timeline_rewind (CLUTTER_TIMELINE (priv->transition)); clutter_timeline_start (CLUTTER_TIMELINE (priv->transition)); } /** * clutter_scroll_actor_scroll_to_rect: * @actor: a #ClutterScrollActor * @rect: a #ClutterRect * * Scrolls @actor so that @rect is in the visible portion. * * Since: 1.12 */ void clutter_scroll_actor_scroll_to_rect (ClutterScrollActor *actor, const graphene_rect_t *rect) { graphene_rect_t n_rect; g_return_if_fail (CLUTTER_IS_SCROLL_ACTOR (actor)); g_return_if_fail (rect != NULL); n_rect = *rect; /* normalize, so that we have a valid origin */ graphene_rect_normalize (&n_rect); clutter_scroll_actor_scroll_to_point (actor, &n_rect.origin); } muffin-6.4.1/clutter/clutter/clutter-keysyms.h0000664000175000017500000026257614723361714020465 0ustar fabiofabio/* Clutter * * Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * File auto-generated from script at: * http://git.clutter-project.org/clutter/plain/clutter/clutter-keysyms-update.pl * * using the input files: * http://cgit.freedesktop.org/xorg/proto/x11proto/plain/keysymdef.h * and * http://cgit.freedesktop.org/xorg/proto/x11proto/plain/XF86keysym.h */ #ifndef __CLUTTER_KEYSYMS_H__ #define __CLUTTER_KEYSYMS_H__ #define CLUTTER_KEY_VoidSymbol 0xffffff #define CLUTTER_KEY_BackSpace 0xff08 #define CLUTTER_KEY_Tab 0xff09 #define CLUTTER_KEY_Linefeed 0xff0a #define CLUTTER_KEY_Clear 0xff0b #define CLUTTER_KEY_Return 0xff0d #define CLUTTER_KEY_Pause 0xff13 #define CLUTTER_KEY_Scroll_Lock 0xff14 #define CLUTTER_KEY_Sys_Req 0xff15 #define CLUTTER_KEY_Escape 0xff1b #define CLUTTER_KEY_Delete 0xffff #define CLUTTER_KEY_Multi_key 0xff20 #define CLUTTER_KEY_Codeinput 0xff37 #define CLUTTER_KEY_SingleCandidate 0xff3c #define CLUTTER_KEY_MultipleCandidate 0xff3d #define CLUTTER_KEY_PreviousCandidate 0xff3e #define CLUTTER_KEY_Kanji 0xff21 #define CLUTTER_KEY_Muhenkan 0xff22 #define CLUTTER_KEY_Henkan_Mode 0xff23 #define CLUTTER_KEY_Henkan 0xff23 #define CLUTTER_KEY_Romaji 0xff24 #define CLUTTER_KEY_Hiragana 0xff25 #define CLUTTER_KEY_Katakana 0xff26 #define CLUTTER_KEY_Hiragana_Katakana 0xff27 #define CLUTTER_KEY_Zenkaku 0xff28 #define CLUTTER_KEY_Hankaku 0xff29 #define CLUTTER_KEY_Zenkaku_Hankaku 0xff2a #define CLUTTER_KEY_Touroku 0xff2b #define CLUTTER_KEY_Massyo 0xff2c #define CLUTTER_KEY_Kana_Lock 0xff2d #define CLUTTER_KEY_Kana_Shift 0xff2e #define CLUTTER_KEY_Eisu_Shift 0xff2f #define CLUTTER_KEY_Eisu_toggle 0xff30 #define CLUTTER_KEY_Kanji_Bangou 0xff37 #define CLUTTER_KEY_Zen_Koho 0xff3d #define CLUTTER_KEY_Mae_Koho 0xff3e #define CLUTTER_KEY_Home 0xff50 #define CLUTTER_KEY_Left 0xff51 #define CLUTTER_KEY_Up 0xff52 #define CLUTTER_KEY_Right 0xff53 #define CLUTTER_KEY_Down 0xff54 #define CLUTTER_KEY_Prior 0xff55 #define CLUTTER_KEY_Page_Up 0xff55 #define CLUTTER_KEY_Next 0xff56 #define CLUTTER_KEY_Page_Down 0xff56 #define CLUTTER_KEY_End 0xff57 #define CLUTTER_KEY_Begin 0xff58 #define CLUTTER_KEY_Select 0xff60 #define CLUTTER_KEY_Print 0xff61 #define CLUTTER_KEY_Execute 0xff62 #define CLUTTER_KEY_Insert 0xff63 #define CLUTTER_KEY_Undo 0xff65 #define CLUTTER_KEY_Redo 0xff66 #define CLUTTER_KEY_Menu 0xff67 #define CLUTTER_KEY_Find 0xff68 #define CLUTTER_KEY_Cancel 0xff69 #define CLUTTER_KEY_Help 0xff6a #define CLUTTER_KEY_Break 0xff6b #define CLUTTER_KEY_Mode_switch 0xff7e #define CLUTTER_KEY_script_switch 0xff7e #define CLUTTER_KEY_Num_Lock 0xff7f #define CLUTTER_KEY_KP_Space 0xff80 #define CLUTTER_KEY_KP_Tab 0xff89 #define CLUTTER_KEY_KP_Enter 0xff8d #define CLUTTER_KEY_KP_F1 0xff91 #define CLUTTER_KEY_KP_F2 0xff92 #define CLUTTER_KEY_KP_F3 0xff93 #define CLUTTER_KEY_KP_F4 0xff94 #define CLUTTER_KEY_KP_Home 0xff95 #define CLUTTER_KEY_KP_Left 0xff96 #define CLUTTER_KEY_KP_Up 0xff97 #define CLUTTER_KEY_KP_Right 0xff98 #define CLUTTER_KEY_KP_Down 0xff99 #define CLUTTER_KEY_KP_Prior 0xff9a #define CLUTTER_KEY_KP_Page_Up 0xff9a #define CLUTTER_KEY_KP_Next 0xff9b #define CLUTTER_KEY_KP_Page_Down 0xff9b #define CLUTTER_KEY_KP_End 0xff9c #define CLUTTER_KEY_KP_Begin 0xff9d #define CLUTTER_KEY_KP_Insert 0xff9e #define CLUTTER_KEY_KP_Delete 0xff9f #define CLUTTER_KEY_KP_Equal 0xffbd #define CLUTTER_KEY_KP_Multiply 0xffaa #define CLUTTER_KEY_KP_Add 0xffab #define CLUTTER_KEY_KP_Separator 0xffac #define CLUTTER_KEY_KP_Subtract 0xffad #define CLUTTER_KEY_KP_Decimal 0xffae #define CLUTTER_KEY_KP_Divide 0xffaf #define CLUTTER_KEY_KP_0 0xffb0 #define CLUTTER_KEY_KP_1 0xffb1 #define CLUTTER_KEY_KP_2 0xffb2 #define CLUTTER_KEY_KP_3 0xffb3 #define CLUTTER_KEY_KP_4 0xffb4 #define CLUTTER_KEY_KP_5 0xffb5 #define CLUTTER_KEY_KP_6 0xffb6 #define CLUTTER_KEY_KP_7 0xffb7 #define CLUTTER_KEY_KP_8 0xffb8 #define CLUTTER_KEY_KP_9 0xffb9 #define CLUTTER_KEY_F1 0xffbe #define CLUTTER_KEY_F2 0xffbf #define CLUTTER_KEY_F3 0xffc0 #define CLUTTER_KEY_F4 0xffc1 #define CLUTTER_KEY_F5 0xffc2 #define CLUTTER_KEY_F6 0xffc3 #define CLUTTER_KEY_F7 0xffc4 #define CLUTTER_KEY_F8 0xffc5 #define CLUTTER_KEY_F9 0xffc6 #define CLUTTER_KEY_F10 0xffc7 #define CLUTTER_KEY_F11 0xffc8 #define CLUTTER_KEY_L1 0xffc8 #define CLUTTER_KEY_F12 0xffc9 #define CLUTTER_KEY_L2 0xffc9 #define CLUTTER_KEY_F13 0xffca #define CLUTTER_KEY_L3 0xffca #define CLUTTER_KEY_F14 0xffcb #define CLUTTER_KEY_L4 0xffcb #define CLUTTER_KEY_F15 0xffcc #define CLUTTER_KEY_L5 0xffcc #define CLUTTER_KEY_F16 0xffcd #define CLUTTER_KEY_L6 0xffcd #define CLUTTER_KEY_F17 0xffce #define CLUTTER_KEY_L7 0xffce #define CLUTTER_KEY_F18 0xffcf #define CLUTTER_KEY_L8 0xffcf #define CLUTTER_KEY_F19 0xffd0 #define CLUTTER_KEY_L9 0xffd0 #define CLUTTER_KEY_F20 0xffd1 #define CLUTTER_KEY_L10 0xffd1 #define CLUTTER_KEY_F21 0xffd2 #define CLUTTER_KEY_R1 0xffd2 #define CLUTTER_KEY_F22 0xffd3 #define CLUTTER_KEY_R2 0xffd3 #define CLUTTER_KEY_F23 0xffd4 #define CLUTTER_KEY_R3 0xffd4 #define CLUTTER_KEY_F24 0xffd5 #define CLUTTER_KEY_R4 0xffd5 #define CLUTTER_KEY_F25 0xffd6 #define CLUTTER_KEY_R5 0xffd6 #define CLUTTER_KEY_F26 0xffd7 #define CLUTTER_KEY_R6 0xffd7 #define CLUTTER_KEY_F27 0xffd8 #define CLUTTER_KEY_R7 0xffd8 #define CLUTTER_KEY_F28 0xffd9 #define CLUTTER_KEY_R8 0xffd9 #define CLUTTER_KEY_F29 0xffda #define CLUTTER_KEY_R9 0xffda #define CLUTTER_KEY_F30 0xffdb #define CLUTTER_KEY_R10 0xffdb #define CLUTTER_KEY_F31 0xffdc #define CLUTTER_KEY_R11 0xffdc #define CLUTTER_KEY_F32 0xffdd #define CLUTTER_KEY_R12 0xffdd #define CLUTTER_KEY_F33 0xffde #define CLUTTER_KEY_R13 0xffde #define CLUTTER_KEY_F34 0xffdf #define CLUTTER_KEY_R14 0xffdf #define CLUTTER_KEY_F35 0xffe0 #define CLUTTER_KEY_R15 0xffe0 #define CLUTTER_KEY_Shift_L 0xffe1 #define CLUTTER_KEY_Shift_R 0xffe2 #define CLUTTER_KEY_Control_L 0xffe3 #define CLUTTER_KEY_Control_R 0xffe4 #define CLUTTER_KEY_Caps_Lock 0xffe5 #define CLUTTER_KEY_Shift_Lock 0xffe6 #define CLUTTER_KEY_Meta_L 0xffe7 #define CLUTTER_KEY_Meta_R 0xffe8 #define CLUTTER_KEY_Alt_L 0xffe9 #define CLUTTER_KEY_Alt_R 0xffea #define CLUTTER_KEY_Super_L 0xffeb #define CLUTTER_KEY_Super_R 0xffec #define CLUTTER_KEY_Hyper_L 0xffed #define CLUTTER_KEY_Hyper_R 0xffee #define CLUTTER_KEY_ISO_Lock 0xfe01 #define CLUTTER_KEY_ISO_Level2_Latch 0xfe02 #define CLUTTER_KEY_ISO_Level3_Shift 0xfe03 #define CLUTTER_KEY_ISO_Level3_Latch 0xfe04 #define CLUTTER_KEY_ISO_Level3_Lock 0xfe05 #define CLUTTER_KEY_ISO_Level5_Shift 0xfe11 #define CLUTTER_KEY_ISO_Level5_Latch 0xfe12 #define CLUTTER_KEY_ISO_Level5_Lock 0xfe13 #define CLUTTER_KEY_ISO_Group_Shift 0xff7e #define CLUTTER_KEY_ISO_Group_Latch 0xfe06 #define CLUTTER_KEY_ISO_Group_Lock 0xfe07 #define CLUTTER_KEY_ISO_Next_Group 0xfe08 #define CLUTTER_KEY_ISO_Next_Group_Lock 0xfe09 #define CLUTTER_KEY_ISO_Prev_Group 0xfe0a #define CLUTTER_KEY_ISO_Prev_Group_Lock 0xfe0b #define CLUTTER_KEY_ISO_First_Group 0xfe0c #define CLUTTER_KEY_ISO_First_Group_Lock 0xfe0d #define CLUTTER_KEY_ISO_Last_Group 0xfe0e #define CLUTTER_KEY_ISO_Last_Group_Lock 0xfe0f #define CLUTTER_KEY_ISO_Left_Tab 0xfe20 #define CLUTTER_KEY_ISO_Move_Line_Up 0xfe21 #define CLUTTER_KEY_ISO_Move_Line_Down 0xfe22 #define CLUTTER_KEY_ISO_Partial_Line_Up 0xfe23 #define CLUTTER_KEY_ISO_Partial_Line_Down 0xfe24 #define CLUTTER_KEY_ISO_Partial_Space_Left 0xfe25 #define CLUTTER_KEY_ISO_Partial_Space_Right 0xfe26 #define CLUTTER_KEY_ISO_Set_Margin_Left 0xfe27 #define CLUTTER_KEY_ISO_Set_Margin_Right 0xfe28 #define CLUTTER_KEY_ISO_Release_Margin_Left 0xfe29 #define CLUTTER_KEY_ISO_Release_Margin_Right 0xfe2a #define CLUTTER_KEY_ISO_Release_Both_Margins 0xfe2b #define CLUTTER_KEY_ISO_Fast_Cursor_Left 0xfe2c #define CLUTTER_KEY_ISO_Fast_Cursor_Right 0xfe2d #define CLUTTER_KEY_ISO_Fast_Cursor_Up 0xfe2e #define CLUTTER_KEY_ISO_Fast_Cursor_Down 0xfe2f #define CLUTTER_KEY_ISO_Continuous_Underline 0xfe30 #define CLUTTER_KEY_ISO_Discontinuous_Underline 0xfe31 #define CLUTTER_KEY_ISO_Emphasize 0xfe32 #define CLUTTER_KEY_ISO_Center_Object 0xfe33 #define CLUTTER_KEY_ISO_Enter 0xfe34 #define CLUTTER_KEY_dead_grave 0xfe50 #define CLUTTER_KEY_dead_acute 0xfe51 #define CLUTTER_KEY_dead_circumflex 0xfe52 #define CLUTTER_KEY_dead_tilde 0xfe53 #define CLUTTER_KEY_dead_perispomeni 0xfe53 #define CLUTTER_KEY_dead_macron 0xfe54 #define CLUTTER_KEY_dead_breve 0xfe55 #define CLUTTER_KEY_dead_abovedot 0xfe56 #define CLUTTER_KEY_dead_diaeresis 0xfe57 #define CLUTTER_KEY_dead_abovering 0xfe58 #define CLUTTER_KEY_dead_doubleacute 0xfe59 #define CLUTTER_KEY_dead_caron 0xfe5a #define CLUTTER_KEY_dead_cedilla 0xfe5b #define CLUTTER_KEY_dead_ogonek 0xfe5c #define CLUTTER_KEY_dead_iota 0xfe5d #define CLUTTER_KEY_dead_voiced_sound 0xfe5e #define CLUTTER_KEY_dead_semivoiced_sound 0xfe5f #define CLUTTER_KEY_dead_belowdot 0xfe60 #define CLUTTER_KEY_dead_hook 0xfe61 #define CLUTTER_KEY_dead_horn 0xfe62 #define CLUTTER_KEY_dead_stroke 0xfe63 #define CLUTTER_KEY_dead_abovecomma 0xfe64 #define CLUTTER_KEY_dead_psili 0xfe64 #define CLUTTER_KEY_dead_abovereversedcomma 0xfe65 #define CLUTTER_KEY_dead_dasia 0xfe65 #define CLUTTER_KEY_dead_doublegrave 0xfe66 #define CLUTTER_KEY_dead_belowring 0xfe67 #define CLUTTER_KEY_dead_belowmacron 0xfe68 #define CLUTTER_KEY_dead_belowcircumflex 0xfe69 #define CLUTTER_KEY_dead_belowtilde 0xfe6a #define CLUTTER_KEY_dead_belowbreve 0xfe6b #define CLUTTER_KEY_dead_belowdiaeresis 0xfe6c #define CLUTTER_KEY_dead_invertedbreve 0xfe6d #define CLUTTER_KEY_dead_belowcomma 0xfe6e #define CLUTTER_KEY_dead_currency 0xfe6f #define CLUTTER_KEY_dead_lowline 0xfe90 #define CLUTTER_KEY_dead_aboveverticalline 0xfe91 #define CLUTTER_KEY_dead_belowverticalline 0xfe92 #define CLUTTER_KEY_dead_longsolidusoverlay 0xfe93 #define CLUTTER_KEY_dead_a 0xfe80 #define CLUTTER_KEY_dead_A 0xfe81 #define CLUTTER_KEY_dead_e 0xfe82 #define CLUTTER_KEY_dead_E 0xfe83 #define CLUTTER_KEY_dead_i 0xfe84 #define CLUTTER_KEY_dead_I 0xfe85 #define CLUTTER_KEY_dead_o 0xfe86 #define CLUTTER_KEY_dead_O 0xfe87 #define CLUTTER_KEY_dead_u 0xfe88 #define CLUTTER_KEY_dead_U 0xfe89 #define CLUTTER_KEY_dead_small_schwa 0xfe8a #define CLUTTER_KEY_dead_capital_schwa 0xfe8b #define CLUTTER_KEY_dead_greek 0xfe8c #define CLUTTER_KEY_First_Virtual_Screen 0xfed0 #define CLUTTER_KEY_Prev_Virtual_Screen 0xfed1 #define CLUTTER_KEY_Next_Virtual_Screen 0xfed2 #define CLUTTER_KEY_Last_Virtual_Screen 0xfed4 #define CLUTTER_KEY_Terminate_Server 0xfed5 #define CLUTTER_KEY_AccessX_Enable 0xfe70 #define CLUTTER_KEY_AccessX_Feedback_Enable 0xfe71 #define CLUTTER_KEY_RepeatKeys_Enable 0xfe72 #define CLUTTER_KEY_SlowKeys_Enable 0xfe73 #define CLUTTER_KEY_BounceKeys_Enable 0xfe74 #define CLUTTER_KEY_StickyKeys_Enable 0xfe75 #define CLUTTER_KEY_MouseKeys_Enable 0xfe76 #define CLUTTER_KEY_MouseKeys_Accel_Enable 0xfe77 #define CLUTTER_KEY_Overlay1_Enable 0xfe78 #define CLUTTER_KEY_Overlay2_Enable 0xfe79 #define CLUTTER_KEY_AudibleBell_Enable 0xfe7a #define CLUTTER_KEY_Pointer_Left 0xfee0 #define CLUTTER_KEY_Pointer_Right 0xfee1 #define CLUTTER_KEY_Pointer_Up 0xfee2 #define CLUTTER_KEY_Pointer_Down 0xfee3 #define CLUTTER_KEY_Pointer_UpLeft 0xfee4 #define CLUTTER_KEY_Pointer_UpRight 0xfee5 #define CLUTTER_KEY_Pointer_DownLeft 0xfee6 #define CLUTTER_KEY_Pointer_DownRight 0xfee7 #define CLUTTER_KEY_Pointer_Button_Dflt 0xfee8 #define CLUTTER_KEY_Pointer_Button1 0xfee9 #define CLUTTER_KEY_Pointer_Button2 0xfeea #define CLUTTER_KEY_Pointer_Button3 0xfeeb #define CLUTTER_KEY_Pointer_Button4 0xfeec #define CLUTTER_KEY_Pointer_Button5 0xfeed #define CLUTTER_KEY_Pointer_DblClick_Dflt 0xfeee #define CLUTTER_KEY_Pointer_DblClick1 0xfeef #define CLUTTER_KEY_Pointer_DblClick2 0xfef0 #define CLUTTER_KEY_Pointer_DblClick3 0xfef1 #define CLUTTER_KEY_Pointer_DblClick4 0xfef2 #define CLUTTER_KEY_Pointer_DblClick5 0xfef3 #define CLUTTER_KEY_Pointer_Drag_Dflt 0xfef4 #define CLUTTER_KEY_Pointer_Drag1 0xfef5 #define CLUTTER_KEY_Pointer_Drag2 0xfef6 #define CLUTTER_KEY_Pointer_Drag3 0xfef7 #define CLUTTER_KEY_Pointer_Drag4 0xfef8 #define CLUTTER_KEY_Pointer_Drag5 0xfefd #define CLUTTER_KEY_Pointer_EnableKeys 0xfef9 #define CLUTTER_KEY_Pointer_Accelerate 0xfefa #define CLUTTER_KEY_Pointer_DfltBtnNext 0xfefb #define CLUTTER_KEY_Pointer_DfltBtnPrev 0xfefc #define CLUTTER_KEY_ch 0xfea0 #define CLUTTER_KEY_Ch 0xfea1 #define CLUTTER_KEY_CH 0xfea2 #define CLUTTER_KEY_c_h 0xfea3 #define CLUTTER_KEY_C_h 0xfea4 #define CLUTTER_KEY_C_H 0xfea5 #define CLUTTER_KEY_3270_Duplicate 0xfd01 #define CLUTTER_KEY_3270_FieldMark 0xfd02 #define CLUTTER_KEY_3270_Right2 0xfd03 #define CLUTTER_KEY_3270_Left2 0xfd04 #define CLUTTER_KEY_3270_BackTab 0xfd05 #define CLUTTER_KEY_3270_EraseEOF 0xfd06 #define CLUTTER_KEY_3270_EraseInput 0xfd07 #define CLUTTER_KEY_3270_Reset 0xfd08 #define CLUTTER_KEY_3270_Quit 0xfd09 #define CLUTTER_KEY_3270_PA1 0xfd0a #define CLUTTER_KEY_3270_PA2 0xfd0b #define CLUTTER_KEY_3270_PA3 0xfd0c #define CLUTTER_KEY_3270_Test 0xfd0d #define CLUTTER_KEY_3270_Attn 0xfd0e #define CLUTTER_KEY_3270_CursorBlink 0xfd0f #define CLUTTER_KEY_3270_AltCursor 0xfd10 #define CLUTTER_KEY_3270_KeyClick 0xfd11 #define CLUTTER_KEY_3270_Jump 0xfd12 #define CLUTTER_KEY_3270_Ident 0xfd13 #define CLUTTER_KEY_3270_Rule 0xfd14 #define CLUTTER_KEY_3270_Copy 0xfd15 #define CLUTTER_KEY_3270_Play 0xfd16 #define CLUTTER_KEY_3270_Setup 0xfd17 #define CLUTTER_KEY_3270_Record 0xfd18 #define CLUTTER_KEY_3270_ChangeScreen 0xfd19 #define CLUTTER_KEY_3270_DeleteWord 0xfd1a #define CLUTTER_KEY_3270_ExSelect 0xfd1b #define CLUTTER_KEY_3270_CursorSelect 0xfd1c #define CLUTTER_KEY_3270_PrintScreen 0xfd1d #define CLUTTER_KEY_3270_Enter 0xfd1e #define CLUTTER_KEY_space 0x020 #define CLUTTER_KEY_exclam 0x021 #define CLUTTER_KEY_quotedbl 0x022 #define CLUTTER_KEY_numbersign 0x023 #define CLUTTER_KEY_dollar 0x024 #define CLUTTER_KEY_percent 0x025 #define CLUTTER_KEY_ampersand 0x026 #define CLUTTER_KEY_apostrophe 0x027 #define CLUTTER_KEY_quoteright 0x027 #define CLUTTER_KEY_parenleft 0x028 #define CLUTTER_KEY_parenright 0x029 #define CLUTTER_KEY_asterisk 0x02a #define CLUTTER_KEY_plus 0x02b #define CLUTTER_KEY_comma 0x02c #define CLUTTER_KEY_minus 0x02d #define CLUTTER_KEY_period 0x02e #define CLUTTER_KEY_slash 0x02f #define CLUTTER_KEY_0 0x030 #define CLUTTER_KEY_1 0x031 #define CLUTTER_KEY_2 0x032 #define CLUTTER_KEY_3 0x033 #define CLUTTER_KEY_4 0x034 #define CLUTTER_KEY_5 0x035 #define CLUTTER_KEY_6 0x036 #define CLUTTER_KEY_7 0x037 #define CLUTTER_KEY_8 0x038 #define CLUTTER_KEY_9 0x039 #define CLUTTER_KEY_colon 0x03a #define CLUTTER_KEY_semicolon 0x03b #define CLUTTER_KEY_less 0x03c #define CLUTTER_KEY_equal 0x03d #define CLUTTER_KEY_greater 0x03e #define CLUTTER_KEY_question 0x03f #define CLUTTER_KEY_at 0x040 #define CLUTTER_KEY_A 0x041 #define CLUTTER_KEY_B 0x042 #define CLUTTER_KEY_C 0x043 #define CLUTTER_KEY_D 0x044 #define CLUTTER_KEY_E 0x045 #define CLUTTER_KEY_F 0x046 #define CLUTTER_KEY_G 0x047 #define CLUTTER_KEY_H 0x048 #define CLUTTER_KEY_I 0x049 #define CLUTTER_KEY_J 0x04a #define CLUTTER_KEY_K 0x04b #define CLUTTER_KEY_L 0x04c #define CLUTTER_KEY_M 0x04d #define CLUTTER_KEY_N 0x04e #define CLUTTER_KEY_O 0x04f #define CLUTTER_KEY_P 0x050 #define CLUTTER_KEY_Q 0x051 #define CLUTTER_KEY_R 0x052 #define CLUTTER_KEY_S 0x053 #define CLUTTER_KEY_T 0x054 #define CLUTTER_KEY_U 0x055 #define CLUTTER_KEY_V 0x056 #define CLUTTER_KEY_W 0x057 #define CLUTTER_KEY_X 0x058 #define CLUTTER_KEY_Y 0x059 #define CLUTTER_KEY_Z 0x05a #define CLUTTER_KEY_bracketleft 0x05b #define CLUTTER_KEY_backslash 0x05c #define CLUTTER_KEY_bracketright 0x05d #define CLUTTER_KEY_asciicircum 0x05e #define CLUTTER_KEY_underscore 0x05f #define CLUTTER_KEY_grave 0x060 #define CLUTTER_KEY_quoteleft 0x060 #define CLUTTER_KEY_a 0x061 #define CLUTTER_KEY_b 0x062 #define CLUTTER_KEY_c 0x063 #define CLUTTER_KEY_d 0x064 #define CLUTTER_KEY_e 0x065 #define CLUTTER_KEY_f 0x066 #define CLUTTER_KEY_g 0x067 #define CLUTTER_KEY_h 0x068 #define CLUTTER_KEY_i 0x069 #define CLUTTER_KEY_j 0x06a #define CLUTTER_KEY_k 0x06b #define CLUTTER_KEY_l 0x06c #define CLUTTER_KEY_m 0x06d #define CLUTTER_KEY_n 0x06e #define CLUTTER_KEY_o 0x06f #define CLUTTER_KEY_p 0x070 #define CLUTTER_KEY_q 0x071 #define CLUTTER_KEY_r 0x072 #define CLUTTER_KEY_s 0x073 #define CLUTTER_KEY_t 0x074 #define CLUTTER_KEY_u 0x075 #define CLUTTER_KEY_v 0x076 #define CLUTTER_KEY_w 0x077 #define CLUTTER_KEY_x 0x078 #define CLUTTER_KEY_y 0x079 #define CLUTTER_KEY_z 0x07a #define CLUTTER_KEY_braceleft 0x07b #define CLUTTER_KEY_bar 0x07c #define CLUTTER_KEY_braceright 0x07d #define CLUTTER_KEY_asciitilde 0x07e #define CLUTTER_KEY_nobreakspace 0x0a0 #define CLUTTER_KEY_exclamdown 0x0a1 #define CLUTTER_KEY_cent 0x0a2 #define CLUTTER_KEY_sterling 0x0a3 #define CLUTTER_KEY_currency 0x0a4 #define CLUTTER_KEY_yen 0x0a5 #define CLUTTER_KEY_brokenbar 0x0a6 #define CLUTTER_KEY_section 0x0a7 #define CLUTTER_KEY_diaeresis 0x0a8 #define CLUTTER_KEY_copyright 0x0a9 #define CLUTTER_KEY_ordfeminine 0x0aa #define CLUTTER_KEY_guillemotleft 0x0ab #define CLUTTER_KEY_notsign 0x0ac #define CLUTTER_KEY_hyphen 0x0ad #define CLUTTER_KEY_registered 0x0ae #define CLUTTER_KEY_macron 0x0af #define CLUTTER_KEY_degree 0x0b0 #define CLUTTER_KEY_plusminus 0x0b1 #define CLUTTER_KEY_twosuperior 0x0b2 #define CLUTTER_KEY_threesuperior 0x0b3 #define CLUTTER_KEY_acute 0x0b4 #define CLUTTER_KEY_mu 0x0b5 #define CLUTTER_KEY_paragraph 0x0b6 #define CLUTTER_KEY_periodcentered 0x0b7 #define CLUTTER_KEY_cedilla 0x0b8 #define CLUTTER_KEY_onesuperior 0x0b9 #define CLUTTER_KEY_masculine 0x0ba #define CLUTTER_KEY_guillemotright 0x0bb #define CLUTTER_KEY_onequarter 0x0bc #define CLUTTER_KEY_onehalf 0x0bd #define CLUTTER_KEY_threequarters 0x0be #define CLUTTER_KEY_questiondown 0x0bf #define CLUTTER_KEY_Agrave 0x0c0 #define CLUTTER_KEY_Aacute 0x0c1 #define CLUTTER_KEY_Acircumflex 0x0c2 #define CLUTTER_KEY_Atilde 0x0c3 #define CLUTTER_KEY_Adiaeresis 0x0c4 #define CLUTTER_KEY_Aring 0x0c5 #define CLUTTER_KEY_AE 0x0c6 #define CLUTTER_KEY_Ccedilla 0x0c7 #define CLUTTER_KEY_Egrave 0x0c8 #define CLUTTER_KEY_Eacute 0x0c9 #define CLUTTER_KEY_Ecircumflex 0x0ca #define CLUTTER_KEY_Ediaeresis 0x0cb #define CLUTTER_KEY_Igrave 0x0cc #define CLUTTER_KEY_Iacute 0x0cd #define CLUTTER_KEY_Icircumflex 0x0ce #define CLUTTER_KEY_Idiaeresis 0x0cf #define CLUTTER_KEY_ETH 0x0d0 #define CLUTTER_KEY_Eth 0x0d0 #define CLUTTER_KEY_Ntilde 0x0d1 #define CLUTTER_KEY_Ograve 0x0d2 #define CLUTTER_KEY_Oacute 0x0d3 #define CLUTTER_KEY_Ocircumflex 0x0d4 #define CLUTTER_KEY_Otilde 0x0d5 #define CLUTTER_KEY_Odiaeresis 0x0d6 #define CLUTTER_KEY_multiply 0x0d7 #define CLUTTER_KEY_Oslash 0x0d8 #define CLUTTER_KEY_Ooblique 0x0d8 #define CLUTTER_KEY_Ugrave 0x0d9 #define CLUTTER_KEY_Uacute 0x0da #define CLUTTER_KEY_Ucircumflex 0x0db #define CLUTTER_KEY_Udiaeresis 0x0dc #define CLUTTER_KEY_Yacute 0x0dd #define CLUTTER_KEY_THORN 0x0de #define CLUTTER_KEY_Thorn 0x0de #define CLUTTER_KEY_ssharp 0x0df #define CLUTTER_KEY_agrave 0x0e0 #define CLUTTER_KEY_aacute 0x0e1 #define CLUTTER_KEY_acircumflex 0x0e2 #define CLUTTER_KEY_atilde 0x0e3 #define CLUTTER_KEY_adiaeresis 0x0e4 #define CLUTTER_KEY_aring 0x0e5 #define CLUTTER_KEY_ae 0x0e6 #define CLUTTER_KEY_ccedilla 0x0e7 #define CLUTTER_KEY_egrave 0x0e8 #define CLUTTER_KEY_eacute 0x0e9 #define CLUTTER_KEY_ecircumflex 0x0ea #define CLUTTER_KEY_ediaeresis 0x0eb #define CLUTTER_KEY_igrave 0x0ec #define CLUTTER_KEY_iacute 0x0ed #define CLUTTER_KEY_icircumflex 0x0ee #define CLUTTER_KEY_idiaeresis 0x0ef #define CLUTTER_KEY_eth 0x0f0 #define CLUTTER_KEY_ntilde 0x0f1 #define CLUTTER_KEY_ograve 0x0f2 #define CLUTTER_KEY_oacute 0x0f3 #define CLUTTER_KEY_ocircumflex 0x0f4 #define CLUTTER_KEY_otilde 0x0f5 #define CLUTTER_KEY_odiaeresis 0x0f6 #define CLUTTER_KEY_division 0x0f7 #define CLUTTER_KEY_oslash 0x0f8 #define CLUTTER_KEY_ooblique 0x0f8 #define CLUTTER_KEY_ugrave 0x0f9 #define CLUTTER_KEY_uacute 0x0fa #define CLUTTER_KEY_ucircumflex 0x0fb #define CLUTTER_KEY_udiaeresis 0x0fc #define CLUTTER_KEY_yacute 0x0fd #define CLUTTER_KEY_thorn 0x0fe #define CLUTTER_KEY_ydiaeresis 0x0ff #define CLUTTER_KEY_Aogonek 0x1a1 #define CLUTTER_KEY_breve 0x1a2 #define CLUTTER_KEY_Lstroke 0x1a3 #define CLUTTER_KEY_Lcaron 0x1a5 #define CLUTTER_KEY_Sacute 0x1a6 #define CLUTTER_KEY_Scaron 0x1a9 #define CLUTTER_KEY_Scedilla 0x1aa #define CLUTTER_KEY_Tcaron 0x1ab #define CLUTTER_KEY_Zacute 0x1ac #define CLUTTER_KEY_Zcaron 0x1ae #define CLUTTER_KEY_Zabovedot 0x1af #define CLUTTER_KEY_aogonek 0x1b1 #define CLUTTER_KEY_ogonek 0x1b2 #define CLUTTER_KEY_lstroke 0x1b3 #define CLUTTER_KEY_lcaron 0x1b5 #define CLUTTER_KEY_sacute 0x1b6 #define CLUTTER_KEY_caron 0x1b7 #define CLUTTER_KEY_scaron 0x1b9 #define CLUTTER_KEY_scedilla 0x1ba #define CLUTTER_KEY_tcaron 0x1bb #define CLUTTER_KEY_zacute 0x1bc #define CLUTTER_KEY_doubleacute 0x1bd #define CLUTTER_KEY_zcaron 0x1be #define CLUTTER_KEY_zabovedot 0x1bf #define CLUTTER_KEY_Racute 0x1c0 #define CLUTTER_KEY_Abreve 0x1c3 #define CLUTTER_KEY_Lacute 0x1c5 #define CLUTTER_KEY_Cacute 0x1c6 #define CLUTTER_KEY_Ccaron 0x1c8 #define CLUTTER_KEY_Eogonek 0x1ca #define CLUTTER_KEY_Ecaron 0x1cc #define CLUTTER_KEY_Dcaron 0x1cf #define CLUTTER_KEY_Dstroke 0x1d0 #define CLUTTER_KEY_Nacute 0x1d1 #define CLUTTER_KEY_Ncaron 0x1d2 #define CLUTTER_KEY_Odoubleacute 0x1d5 #define CLUTTER_KEY_Rcaron 0x1d8 #define CLUTTER_KEY_Uring 0x1d9 #define CLUTTER_KEY_Udoubleacute 0x1db #define CLUTTER_KEY_Tcedilla 0x1de #define CLUTTER_KEY_racute 0x1e0 #define CLUTTER_KEY_abreve 0x1e3 #define CLUTTER_KEY_lacute 0x1e5 #define CLUTTER_KEY_cacute 0x1e6 #define CLUTTER_KEY_ccaron 0x1e8 #define CLUTTER_KEY_eogonek 0x1ea #define CLUTTER_KEY_ecaron 0x1ec #define CLUTTER_KEY_dcaron 0x1ef #define CLUTTER_KEY_dstroke 0x1f0 #define CLUTTER_KEY_nacute 0x1f1 #define CLUTTER_KEY_ncaron 0x1f2 #define CLUTTER_KEY_odoubleacute 0x1f5 #define CLUTTER_KEY_rcaron 0x1f8 #define CLUTTER_KEY_uring 0x1f9 #define CLUTTER_KEY_udoubleacute 0x1fb #define CLUTTER_KEY_tcedilla 0x1fe #define CLUTTER_KEY_abovedot 0x1ff #define CLUTTER_KEY_Hstroke 0x2a1 #define CLUTTER_KEY_Hcircumflex 0x2a6 #define CLUTTER_KEY_Iabovedot 0x2a9 #define CLUTTER_KEY_Gbreve 0x2ab #define CLUTTER_KEY_Jcircumflex 0x2ac #define CLUTTER_KEY_hstroke 0x2b1 #define CLUTTER_KEY_hcircumflex 0x2b6 #define CLUTTER_KEY_idotless 0x2b9 #define CLUTTER_KEY_gbreve 0x2bb #define CLUTTER_KEY_jcircumflex 0x2bc #define CLUTTER_KEY_Cabovedot 0x2c5 #define CLUTTER_KEY_Ccircumflex 0x2c6 #define CLUTTER_KEY_Gabovedot 0x2d5 #define CLUTTER_KEY_Gcircumflex 0x2d8 #define CLUTTER_KEY_Ubreve 0x2dd #define CLUTTER_KEY_Scircumflex 0x2de #define CLUTTER_KEY_cabovedot 0x2e5 #define CLUTTER_KEY_ccircumflex 0x2e6 #define CLUTTER_KEY_gabovedot 0x2f5 #define CLUTTER_KEY_gcircumflex 0x2f8 #define CLUTTER_KEY_ubreve 0x2fd #define CLUTTER_KEY_scircumflex 0x2fe #define CLUTTER_KEY_kra 0x3a2 #define CLUTTER_KEY_kappa 0x3a2 #define CLUTTER_KEY_Rcedilla 0x3a3 #define CLUTTER_KEY_Itilde 0x3a5 #define CLUTTER_KEY_Lcedilla 0x3a6 #define CLUTTER_KEY_Emacron 0x3aa #define CLUTTER_KEY_Gcedilla 0x3ab #define CLUTTER_KEY_Tslash 0x3ac #define CLUTTER_KEY_rcedilla 0x3b3 #define CLUTTER_KEY_itilde 0x3b5 #define CLUTTER_KEY_lcedilla 0x3b6 #define CLUTTER_KEY_emacron 0x3ba #define CLUTTER_KEY_gcedilla 0x3bb #define CLUTTER_KEY_tslash 0x3bc #define CLUTTER_KEY_ENG 0x3bd #define CLUTTER_KEY_eng 0x3bf #define CLUTTER_KEY_Amacron 0x3c0 #define CLUTTER_KEY_Iogonek 0x3c7 #define CLUTTER_KEY_Eabovedot 0x3cc #define CLUTTER_KEY_Imacron 0x3cf #define CLUTTER_KEY_Ncedilla 0x3d1 #define CLUTTER_KEY_Omacron 0x3d2 #define CLUTTER_KEY_Kcedilla 0x3d3 #define CLUTTER_KEY_Uogonek 0x3d9 #define CLUTTER_KEY_Utilde 0x3dd #define CLUTTER_KEY_Umacron 0x3de #define CLUTTER_KEY_amacron 0x3e0 #define CLUTTER_KEY_iogonek 0x3e7 #define CLUTTER_KEY_eabovedot 0x3ec #define CLUTTER_KEY_imacron 0x3ef #define CLUTTER_KEY_ncedilla 0x3f1 #define CLUTTER_KEY_omacron 0x3f2 #define CLUTTER_KEY_kcedilla 0x3f3 #define CLUTTER_KEY_uogonek 0x3f9 #define CLUTTER_KEY_utilde 0x3fd #define CLUTTER_KEY_umacron 0x3fe #define CLUTTER_KEY_Wcircumflex 0x1000174 #define CLUTTER_KEY_wcircumflex 0x1000175 #define CLUTTER_KEY_Ycircumflex 0x1000176 #define CLUTTER_KEY_ycircumflex 0x1000177 #define CLUTTER_KEY_Babovedot 0x1001e02 #define CLUTTER_KEY_babovedot 0x1001e03 #define CLUTTER_KEY_Dabovedot 0x1001e0a #define CLUTTER_KEY_dabovedot 0x1001e0b #define CLUTTER_KEY_Fabovedot 0x1001e1e #define CLUTTER_KEY_fabovedot 0x1001e1f #define CLUTTER_KEY_Mabovedot 0x1001e40 #define CLUTTER_KEY_mabovedot 0x1001e41 #define CLUTTER_KEY_Pabovedot 0x1001e56 #define CLUTTER_KEY_pabovedot 0x1001e57 #define CLUTTER_KEY_Sabovedot 0x1001e60 #define CLUTTER_KEY_sabovedot 0x1001e61 #define CLUTTER_KEY_Tabovedot 0x1001e6a #define CLUTTER_KEY_tabovedot 0x1001e6b #define CLUTTER_KEY_Wgrave 0x1001e80 #define CLUTTER_KEY_wgrave 0x1001e81 #define CLUTTER_KEY_Wacute 0x1001e82 #define CLUTTER_KEY_wacute 0x1001e83 #define CLUTTER_KEY_Wdiaeresis 0x1001e84 #define CLUTTER_KEY_wdiaeresis 0x1001e85 #define CLUTTER_KEY_Ygrave 0x1001ef2 #define CLUTTER_KEY_ygrave 0x1001ef3 #define CLUTTER_KEY_OE 0x13bc #define CLUTTER_KEY_oe 0x13bd #define CLUTTER_KEY_Ydiaeresis 0x13be #define CLUTTER_KEY_overline 0x47e #define CLUTTER_KEY_kana_fullstop 0x4a1 #define CLUTTER_KEY_kana_openingbracket 0x4a2 #define CLUTTER_KEY_kana_closingbracket 0x4a3 #define CLUTTER_KEY_kana_comma 0x4a4 #define CLUTTER_KEY_kana_conjunctive 0x4a5 #define CLUTTER_KEY_kana_middledot 0x4a5 #define CLUTTER_KEY_kana_WO 0x4a6 #define CLUTTER_KEY_kana_a 0x4a7 #define CLUTTER_KEY_kana_i 0x4a8 #define CLUTTER_KEY_kana_u 0x4a9 #define CLUTTER_KEY_kana_e 0x4aa #define CLUTTER_KEY_kana_o 0x4ab #define CLUTTER_KEY_kana_ya 0x4ac #define CLUTTER_KEY_kana_yu 0x4ad #define CLUTTER_KEY_kana_yo 0x4ae #define CLUTTER_KEY_kana_tsu 0x4af #define CLUTTER_KEY_kana_tu 0x4af #define CLUTTER_KEY_prolongedsound 0x4b0 #define CLUTTER_KEY_kana_A 0x4b1 #define CLUTTER_KEY_kana_I 0x4b2 #define CLUTTER_KEY_kana_U 0x4b3 #define CLUTTER_KEY_kana_E 0x4b4 #define CLUTTER_KEY_kana_O 0x4b5 #define CLUTTER_KEY_kana_KA 0x4b6 #define CLUTTER_KEY_kana_KI 0x4b7 #define CLUTTER_KEY_kana_KU 0x4b8 #define CLUTTER_KEY_kana_KE 0x4b9 #define CLUTTER_KEY_kana_KO 0x4ba #define CLUTTER_KEY_kana_SA 0x4bb #define CLUTTER_KEY_kana_SHI 0x4bc #define CLUTTER_KEY_kana_SU 0x4bd #define CLUTTER_KEY_kana_SE 0x4be #define CLUTTER_KEY_kana_SO 0x4bf #define CLUTTER_KEY_kana_TA 0x4c0 #define CLUTTER_KEY_kana_CHI 0x4c1 #define CLUTTER_KEY_kana_TI 0x4c1 #define CLUTTER_KEY_kana_TSU 0x4c2 #define CLUTTER_KEY_kana_TU 0x4c2 #define CLUTTER_KEY_kana_TE 0x4c3 #define CLUTTER_KEY_kana_TO 0x4c4 #define CLUTTER_KEY_kana_NA 0x4c5 #define CLUTTER_KEY_kana_NI 0x4c6 #define CLUTTER_KEY_kana_NU 0x4c7 #define CLUTTER_KEY_kana_NE 0x4c8 #define CLUTTER_KEY_kana_NO 0x4c9 #define CLUTTER_KEY_kana_HA 0x4ca #define CLUTTER_KEY_kana_HI 0x4cb #define CLUTTER_KEY_kana_FU 0x4cc #define CLUTTER_KEY_kana_HU 0x4cc #define CLUTTER_KEY_kana_HE 0x4cd #define CLUTTER_KEY_kana_HO 0x4ce #define CLUTTER_KEY_kana_MA 0x4cf #define CLUTTER_KEY_kana_MI 0x4d0 #define CLUTTER_KEY_kana_MU 0x4d1 #define CLUTTER_KEY_kana_ME 0x4d2 #define CLUTTER_KEY_kana_MO 0x4d3 #define CLUTTER_KEY_kana_YA 0x4d4 #define CLUTTER_KEY_kana_YU 0x4d5 #define CLUTTER_KEY_kana_YO 0x4d6 #define CLUTTER_KEY_kana_RA 0x4d7 #define CLUTTER_KEY_kana_RI 0x4d8 #define CLUTTER_KEY_kana_RU 0x4d9 #define CLUTTER_KEY_kana_RE 0x4da #define CLUTTER_KEY_kana_RO 0x4db #define CLUTTER_KEY_kana_WA 0x4dc #define CLUTTER_KEY_kana_N 0x4dd #define CLUTTER_KEY_voicedsound 0x4de #define CLUTTER_KEY_semivoicedsound 0x4df #define CLUTTER_KEY_kana_switch 0xff7e #define CLUTTER_KEY_Farsi_0 0x10006f0 #define CLUTTER_KEY_Farsi_1 0x10006f1 #define CLUTTER_KEY_Farsi_2 0x10006f2 #define CLUTTER_KEY_Farsi_3 0x10006f3 #define CLUTTER_KEY_Farsi_4 0x10006f4 #define CLUTTER_KEY_Farsi_5 0x10006f5 #define CLUTTER_KEY_Farsi_6 0x10006f6 #define CLUTTER_KEY_Farsi_7 0x10006f7 #define CLUTTER_KEY_Farsi_8 0x10006f8 #define CLUTTER_KEY_Farsi_9 0x10006f9 #define CLUTTER_KEY_Arabic_percent 0x100066a #define CLUTTER_KEY_Arabic_superscript_alef 0x1000670 #define CLUTTER_KEY_Arabic_tteh 0x1000679 #define CLUTTER_KEY_Arabic_peh 0x100067e #define CLUTTER_KEY_Arabic_tcheh 0x1000686 #define CLUTTER_KEY_Arabic_ddal 0x1000688 #define CLUTTER_KEY_Arabic_rreh 0x1000691 #define CLUTTER_KEY_Arabic_comma 0x5ac #define CLUTTER_KEY_Arabic_fullstop 0x10006d4 #define CLUTTER_KEY_Arabic_0 0x1000660 #define CLUTTER_KEY_Arabic_1 0x1000661 #define CLUTTER_KEY_Arabic_2 0x1000662 #define CLUTTER_KEY_Arabic_3 0x1000663 #define CLUTTER_KEY_Arabic_4 0x1000664 #define CLUTTER_KEY_Arabic_5 0x1000665 #define CLUTTER_KEY_Arabic_6 0x1000666 #define CLUTTER_KEY_Arabic_7 0x1000667 #define CLUTTER_KEY_Arabic_8 0x1000668 #define CLUTTER_KEY_Arabic_9 0x1000669 #define CLUTTER_KEY_Arabic_semicolon 0x5bb #define CLUTTER_KEY_Arabic_question_mark 0x5bf #define CLUTTER_KEY_Arabic_hamza 0x5c1 #define CLUTTER_KEY_Arabic_maddaonalef 0x5c2 #define CLUTTER_KEY_Arabic_hamzaonalef 0x5c3 #define CLUTTER_KEY_Arabic_hamzaonwaw 0x5c4 #define CLUTTER_KEY_Arabic_hamzaunderalef 0x5c5 #define CLUTTER_KEY_Arabic_hamzaonyeh 0x5c6 #define CLUTTER_KEY_Arabic_alef 0x5c7 #define CLUTTER_KEY_Arabic_beh 0x5c8 #define CLUTTER_KEY_Arabic_tehmarbuta 0x5c9 #define CLUTTER_KEY_Arabic_teh 0x5ca #define CLUTTER_KEY_Arabic_theh 0x5cb #define CLUTTER_KEY_Arabic_jeem 0x5cc #define CLUTTER_KEY_Arabic_hah 0x5cd #define CLUTTER_KEY_Arabic_khah 0x5ce #define CLUTTER_KEY_Arabic_dal 0x5cf #define CLUTTER_KEY_Arabic_thal 0x5d0 #define CLUTTER_KEY_Arabic_ra 0x5d1 #define CLUTTER_KEY_Arabic_zain 0x5d2 #define CLUTTER_KEY_Arabic_seen 0x5d3 #define CLUTTER_KEY_Arabic_sheen 0x5d4 #define CLUTTER_KEY_Arabic_sad 0x5d5 #define CLUTTER_KEY_Arabic_dad 0x5d6 #define CLUTTER_KEY_Arabic_tah 0x5d7 #define CLUTTER_KEY_Arabic_zah 0x5d8 #define CLUTTER_KEY_Arabic_ain 0x5d9 #define CLUTTER_KEY_Arabic_ghain 0x5da #define CLUTTER_KEY_Arabic_tatweel 0x5e0 #define CLUTTER_KEY_Arabic_feh 0x5e1 #define CLUTTER_KEY_Arabic_qaf 0x5e2 #define CLUTTER_KEY_Arabic_kaf 0x5e3 #define CLUTTER_KEY_Arabic_lam 0x5e4 #define CLUTTER_KEY_Arabic_meem 0x5e5 #define CLUTTER_KEY_Arabic_noon 0x5e6 #define CLUTTER_KEY_Arabic_ha 0x5e7 #define CLUTTER_KEY_Arabic_heh 0x5e7 #define CLUTTER_KEY_Arabic_waw 0x5e8 #define CLUTTER_KEY_Arabic_alefmaksura 0x5e9 #define CLUTTER_KEY_Arabic_yeh 0x5ea #define CLUTTER_KEY_Arabic_fathatan 0x5eb #define CLUTTER_KEY_Arabic_dammatan 0x5ec #define CLUTTER_KEY_Arabic_kasratan 0x5ed #define CLUTTER_KEY_Arabic_fatha 0x5ee #define CLUTTER_KEY_Arabic_damma 0x5ef #define CLUTTER_KEY_Arabic_kasra 0x5f0 #define CLUTTER_KEY_Arabic_shadda 0x5f1 #define CLUTTER_KEY_Arabic_sukun 0x5f2 #define CLUTTER_KEY_Arabic_madda_above 0x1000653 #define CLUTTER_KEY_Arabic_hamza_above 0x1000654 #define CLUTTER_KEY_Arabic_hamza_below 0x1000655 #define CLUTTER_KEY_Arabic_jeh 0x1000698 #define CLUTTER_KEY_Arabic_veh 0x10006a4 #define CLUTTER_KEY_Arabic_keheh 0x10006a9 #define CLUTTER_KEY_Arabic_gaf 0x10006af #define CLUTTER_KEY_Arabic_noon_ghunna 0x10006ba #define CLUTTER_KEY_Arabic_heh_doachashmee 0x10006be #define CLUTTER_KEY_Farsi_yeh 0x10006cc #define CLUTTER_KEY_Arabic_farsi_yeh 0x10006cc #define CLUTTER_KEY_Arabic_yeh_baree 0x10006d2 #define CLUTTER_KEY_Arabic_heh_goal 0x10006c1 #define CLUTTER_KEY_Arabic_switch 0xff7e #define CLUTTER_KEY_Cyrillic_GHE_bar 0x1000492 #define CLUTTER_KEY_Cyrillic_ghe_bar 0x1000493 #define CLUTTER_KEY_Cyrillic_ZHE_descender 0x1000496 #define CLUTTER_KEY_Cyrillic_zhe_descender 0x1000497 #define CLUTTER_KEY_Cyrillic_KA_descender 0x100049a #define CLUTTER_KEY_Cyrillic_ka_descender 0x100049b #define CLUTTER_KEY_Cyrillic_KA_vertstroke 0x100049c #define CLUTTER_KEY_Cyrillic_ka_vertstroke 0x100049d #define CLUTTER_KEY_Cyrillic_EN_descender 0x10004a2 #define CLUTTER_KEY_Cyrillic_en_descender 0x10004a3 #define CLUTTER_KEY_Cyrillic_U_straight 0x10004ae #define CLUTTER_KEY_Cyrillic_u_straight 0x10004af #define CLUTTER_KEY_Cyrillic_U_straight_bar 0x10004b0 #define CLUTTER_KEY_Cyrillic_u_straight_bar 0x10004b1 #define CLUTTER_KEY_Cyrillic_HA_descender 0x10004b2 #define CLUTTER_KEY_Cyrillic_ha_descender 0x10004b3 #define CLUTTER_KEY_Cyrillic_CHE_descender 0x10004b6 #define CLUTTER_KEY_Cyrillic_che_descender 0x10004b7 #define CLUTTER_KEY_Cyrillic_CHE_vertstroke 0x10004b8 #define CLUTTER_KEY_Cyrillic_che_vertstroke 0x10004b9 #define CLUTTER_KEY_Cyrillic_SHHA 0x10004ba #define CLUTTER_KEY_Cyrillic_shha 0x10004bb #define CLUTTER_KEY_Cyrillic_SCHWA 0x10004d8 #define CLUTTER_KEY_Cyrillic_schwa 0x10004d9 #define CLUTTER_KEY_Cyrillic_I_macron 0x10004e2 #define CLUTTER_KEY_Cyrillic_i_macron 0x10004e3 #define CLUTTER_KEY_Cyrillic_O_bar 0x10004e8 #define CLUTTER_KEY_Cyrillic_o_bar 0x10004e9 #define CLUTTER_KEY_Cyrillic_U_macron 0x10004ee #define CLUTTER_KEY_Cyrillic_u_macron 0x10004ef #define CLUTTER_KEY_Serbian_dje 0x6a1 #define CLUTTER_KEY_Macedonia_gje 0x6a2 #define CLUTTER_KEY_Cyrillic_io 0x6a3 #define CLUTTER_KEY_Ukrainian_ie 0x6a4 #define CLUTTER_KEY_Ukranian_je 0x6a4 #define CLUTTER_KEY_Macedonia_dse 0x6a5 #define CLUTTER_KEY_Ukrainian_i 0x6a6 #define CLUTTER_KEY_Ukranian_i 0x6a6 #define CLUTTER_KEY_Ukrainian_yi 0x6a7 #define CLUTTER_KEY_Ukranian_yi 0x6a7 #define CLUTTER_KEY_Cyrillic_je 0x6a8 #define CLUTTER_KEY_Serbian_je 0x6a8 #define CLUTTER_KEY_Cyrillic_lje 0x6a9 #define CLUTTER_KEY_Serbian_lje 0x6a9 #define CLUTTER_KEY_Cyrillic_nje 0x6aa #define CLUTTER_KEY_Serbian_nje 0x6aa #define CLUTTER_KEY_Serbian_tshe 0x6ab #define CLUTTER_KEY_Macedonia_kje 0x6ac #define CLUTTER_KEY_Ukrainian_ghe_with_upturn 0x6ad #define CLUTTER_KEY_Byelorussian_shortu 0x6ae #define CLUTTER_KEY_Cyrillic_dzhe 0x6af #define CLUTTER_KEY_Serbian_dze 0x6af #define CLUTTER_KEY_numerosign 0x6b0 #define CLUTTER_KEY_Serbian_DJE 0x6b1 #define CLUTTER_KEY_Macedonia_GJE 0x6b2 #define CLUTTER_KEY_Cyrillic_IO 0x6b3 #define CLUTTER_KEY_Ukrainian_IE 0x6b4 #define CLUTTER_KEY_Ukranian_JE 0x6b4 #define CLUTTER_KEY_Macedonia_DSE 0x6b5 #define CLUTTER_KEY_Ukrainian_I 0x6b6 #define CLUTTER_KEY_Ukranian_I 0x6b6 #define CLUTTER_KEY_Ukrainian_YI 0x6b7 #define CLUTTER_KEY_Ukranian_YI 0x6b7 #define CLUTTER_KEY_Cyrillic_JE 0x6b8 #define CLUTTER_KEY_Serbian_JE 0x6b8 #define CLUTTER_KEY_Cyrillic_LJE 0x6b9 #define CLUTTER_KEY_Serbian_LJE 0x6b9 #define CLUTTER_KEY_Cyrillic_NJE 0x6ba #define CLUTTER_KEY_Serbian_NJE 0x6ba #define CLUTTER_KEY_Serbian_TSHE 0x6bb #define CLUTTER_KEY_Macedonia_KJE 0x6bc #define CLUTTER_KEY_Ukrainian_GHE_WITH_UPTURN 0x6bd #define CLUTTER_KEY_Byelorussian_SHORTU 0x6be #define CLUTTER_KEY_Cyrillic_DZHE 0x6bf #define CLUTTER_KEY_Serbian_DZE 0x6bf #define CLUTTER_KEY_Cyrillic_yu 0x6c0 #define CLUTTER_KEY_Cyrillic_a 0x6c1 #define CLUTTER_KEY_Cyrillic_be 0x6c2 #define CLUTTER_KEY_Cyrillic_tse 0x6c3 #define CLUTTER_KEY_Cyrillic_de 0x6c4 #define CLUTTER_KEY_Cyrillic_ie 0x6c5 #define CLUTTER_KEY_Cyrillic_ef 0x6c6 #define CLUTTER_KEY_Cyrillic_ghe 0x6c7 #define CLUTTER_KEY_Cyrillic_ha 0x6c8 #define CLUTTER_KEY_Cyrillic_i 0x6c9 #define CLUTTER_KEY_Cyrillic_shorti 0x6ca #define CLUTTER_KEY_Cyrillic_ka 0x6cb #define CLUTTER_KEY_Cyrillic_el 0x6cc #define CLUTTER_KEY_Cyrillic_em 0x6cd #define CLUTTER_KEY_Cyrillic_en 0x6ce #define CLUTTER_KEY_Cyrillic_o 0x6cf #define CLUTTER_KEY_Cyrillic_pe 0x6d0 #define CLUTTER_KEY_Cyrillic_ya 0x6d1 #define CLUTTER_KEY_Cyrillic_er 0x6d2 #define CLUTTER_KEY_Cyrillic_es 0x6d3 #define CLUTTER_KEY_Cyrillic_te 0x6d4 #define CLUTTER_KEY_Cyrillic_u 0x6d5 #define CLUTTER_KEY_Cyrillic_zhe 0x6d6 #define CLUTTER_KEY_Cyrillic_ve 0x6d7 #define CLUTTER_KEY_Cyrillic_softsign 0x6d8 #define CLUTTER_KEY_Cyrillic_yeru 0x6d9 #define CLUTTER_KEY_Cyrillic_ze 0x6da #define CLUTTER_KEY_Cyrillic_sha 0x6db #define CLUTTER_KEY_Cyrillic_e 0x6dc #define CLUTTER_KEY_Cyrillic_shcha 0x6dd #define CLUTTER_KEY_Cyrillic_che 0x6de #define CLUTTER_KEY_Cyrillic_hardsign 0x6df #define CLUTTER_KEY_Cyrillic_YU 0x6e0 #define CLUTTER_KEY_Cyrillic_A 0x6e1 #define CLUTTER_KEY_Cyrillic_BE 0x6e2 #define CLUTTER_KEY_Cyrillic_TSE 0x6e3 #define CLUTTER_KEY_Cyrillic_DE 0x6e4 #define CLUTTER_KEY_Cyrillic_IE 0x6e5 #define CLUTTER_KEY_Cyrillic_EF 0x6e6 #define CLUTTER_KEY_Cyrillic_GHE 0x6e7 #define CLUTTER_KEY_Cyrillic_HA 0x6e8 #define CLUTTER_KEY_Cyrillic_I 0x6e9 #define CLUTTER_KEY_Cyrillic_SHORTI 0x6ea #define CLUTTER_KEY_Cyrillic_KA 0x6eb #define CLUTTER_KEY_Cyrillic_EL 0x6ec #define CLUTTER_KEY_Cyrillic_EM 0x6ed #define CLUTTER_KEY_Cyrillic_EN 0x6ee #define CLUTTER_KEY_Cyrillic_O 0x6ef #define CLUTTER_KEY_Cyrillic_PE 0x6f0 #define CLUTTER_KEY_Cyrillic_YA 0x6f1 #define CLUTTER_KEY_Cyrillic_ER 0x6f2 #define CLUTTER_KEY_Cyrillic_ES 0x6f3 #define CLUTTER_KEY_Cyrillic_TE 0x6f4 #define CLUTTER_KEY_Cyrillic_U 0x6f5 #define CLUTTER_KEY_Cyrillic_ZHE 0x6f6 #define CLUTTER_KEY_Cyrillic_VE 0x6f7 #define CLUTTER_KEY_Cyrillic_SOFTSIGN 0x6f8 #define CLUTTER_KEY_Cyrillic_YERU 0x6f9 #define CLUTTER_KEY_Cyrillic_ZE 0x6fa #define CLUTTER_KEY_Cyrillic_SHA 0x6fb #define CLUTTER_KEY_Cyrillic_E 0x6fc #define CLUTTER_KEY_Cyrillic_SHCHA 0x6fd #define CLUTTER_KEY_Cyrillic_CHE 0x6fe #define CLUTTER_KEY_Cyrillic_HARDSIGN 0x6ff #define CLUTTER_KEY_Greek_ALPHAaccent 0x7a1 #define CLUTTER_KEY_Greek_EPSILONaccent 0x7a2 #define CLUTTER_KEY_Greek_ETAaccent 0x7a3 #define CLUTTER_KEY_Greek_IOTAaccent 0x7a4 #define CLUTTER_KEY_Greek_IOTAdieresis 0x7a5 #define CLUTTER_KEY_Greek_IOTAdiaeresis 0x7a5 #define CLUTTER_KEY_Greek_OMICRONaccent 0x7a7 #define CLUTTER_KEY_Greek_UPSILONaccent 0x7a8 #define CLUTTER_KEY_Greek_UPSILONdieresis 0x7a9 #define CLUTTER_KEY_Greek_OMEGAaccent 0x7ab #define CLUTTER_KEY_Greek_accentdieresis 0x7ae #define CLUTTER_KEY_Greek_horizbar 0x7af #define CLUTTER_KEY_Greek_alphaaccent 0x7b1 #define CLUTTER_KEY_Greek_epsilonaccent 0x7b2 #define CLUTTER_KEY_Greek_etaaccent 0x7b3 #define CLUTTER_KEY_Greek_iotaaccent 0x7b4 #define CLUTTER_KEY_Greek_iotadieresis 0x7b5 #define CLUTTER_KEY_Greek_iotaaccentdieresis 0x7b6 #define CLUTTER_KEY_Greek_omicronaccent 0x7b7 #define CLUTTER_KEY_Greek_upsilonaccent 0x7b8 #define CLUTTER_KEY_Greek_upsilondieresis 0x7b9 #define CLUTTER_KEY_Greek_upsilonaccentdieresis 0x7ba #define CLUTTER_KEY_Greek_omegaaccent 0x7bb #define CLUTTER_KEY_Greek_ALPHA 0x7c1 #define CLUTTER_KEY_Greek_BETA 0x7c2 #define CLUTTER_KEY_Greek_GAMMA 0x7c3 #define CLUTTER_KEY_Greek_DELTA 0x7c4 #define CLUTTER_KEY_Greek_EPSILON 0x7c5 #define CLUTTER_KEY_Greek_ZETA 0x7c6 #define CLUTTER_KEY_Greek_ETA 0x7c7 #define CLUTTER_KEY_Greek_THETA 0x7c8 #define CLUTTER_KEY_Greek_IOTA 0x7c9 #define CLUTTER_KEY_Greek_KAPPA 0x7ca #define CLUTTER_KEY_Greek_LAMDA 0x7cb #define CLUTTER_KEY_Greek_LAMBDA 0x7cb #define CLUTTER_KEY_Greek_MU 0x7cc #define CLUTTER_KEY_Greek_NU 0x7cd #define CLUTTER_KEY_Greek_XI 0x7ce #define CLUTTER_KEY_Greek_OMICRON 0x7cf #define CLUTTER_KEY_Greek_PI 0x7d0 #define CLUTTER_KEY_Greek_RHO 0x7d1 #define CLUTTER_KEY_Greek_SIGMA 0x7d2 #define CLUTTER_KEY_Greek_TAU 0x7d4 #define CLUTTER_KEY_Greek_UPSILON 0x7d5 #define CLUTTER_KEY_Greek_PHI 0x7d6 #define CLUTTER_KEY_Greek_CHI 0x7d7 #define CLUTTER_KEY_Greek_PSI 0x7d8 #define CLUTTER_KEY_Greek_OMEGA 0x7d9 #define CLUTTER_KEY_Greek_alpha 0x7e1 #define CLUTTER_KEY_Greek_beta 0x7e2 #define CLUTTER_KEY_Greek_gamma 0x7e3 #define CLUTTER_KEY_Greek_delta 0x7e4 #define CLUTTER_KEY_Greek_epsilon 0x7e5 #define CLUTTER_KEY_Greek_zeta 0x7e6 #define CLUTTER_KEY_Greek_eta 0x7e7 #define CLUTTER_KEY_Greek_theta 0x7e8 #define CLUTTER_KEY_Greek_iota 0x7e9 #define CLUTTER_KEY_Greek_kappa 0x7ea #define CLUTTER_KEY_Greek_lamda 0x7eb #define CLUTTER_KEY_Greek_lambda 0x7eb #define CLUTTER_KEY_Greek_mu 0x7ec #define CLUTTER_KEY_Greek_nu 0x7ed #define CLUTTER_KEY_Greek_xi 0x7ee #define CLUTTER_KEY_Greek_omicron 0x7ef #define CLUTTER_KEY_Greek_pi 0x7f0 #define CLUTTER_KEY_Greek_rho 0x7f1 #define CLUTTER_KEY_Greek_sigma 0x7f2 #define CLUTTER_KEY_Greek_finalsmallsigma 0x7f3 #define CLUTTER_KEY_Greek_tau 0x7f4 #define CLUTTER_KEY_Greek_upsilon 0x7f5 #define CLUTTER_KEY_Greek_phi 0x7f6 #define CLUTTER_KEY_Greek_chi 0x7f7 #define CLUTTER_KEY_Greek_psi 0x7f8 #define CLUTTER_KEY_Greek_omega 0x7f9 #define CLUTTER_KEY_Greek_switch 0xff7e #define CLUTTER_KEY_leftradical 0x8a1 #define CLUTTER_KEY_topleftradical 0x8a2 #define CLUTTER_KEY_horizconnector 0x8a3 #define CLUTTER_KEY_topintegral 0x8a4 #define CLUTTER_KEY_botintegral 0x8a5 #define CLUTTER_KEY_vertconnector 0x8a6 #define CLUTTER_KEY_topleftsqbracket 0x8a7 #define CLUTTER_KEY_botleftsqbracket 0x8a8 #define CLUTTER_KEY_toprightsqbracket 0x8a9 #define CLUTTER_KEY_botrightsqbracket 0x8aa #define CLUTTER_KEY_topleftparens 0x8ab #define CLUTTER_KEY_botleftparens 0x8ac #define CLUTTER_KEY_toprightparens 0x8ad #define CLUTTER_KEY_botrightparens 0x8ae #define CLUTTER_KEY_leftmiddlecurlybrace 0x8af #define CLUTTER_KEY_rightmiddlecurlybrace 0x8b0 #define CLUTTER_KEY_topleftsummation 0x8b1 #define CLUTTER_KEY_botleftsummation 0x8b2 #define CLUTTER_KEY_topvertsummationconnector 0x8b3 #define CLUTTER_KEY_botvertsummationconnector 0x8b4 #define CLUTTER_KEY_toprightsummation 0x8b5 #define CLUTTER_KEY_botrightsummation 0x8b6 #define CLUTTER_KEY_rightmiddlesummation 0x8b7 #define CLUTTER_KEY_lessthanequal 0x8bc #define CLUTTER_KEY_notequal 0x8bd #define CLUTTER_KEY_greaterthanequal 0x8be #define CLUTTER_KEY_integral 0x8bf #define CLUTTER_KEY_therefore 0x8c0 #define CLUTTER_KEY_variation 0x8c1 #define CLUTTER_KEY_infinity 0x8c2 #define CLUTTER_KEY_nabla 0x8c5 #define CLUTTER_KEY_approximate 0x8c8 #define CLUTTER_KEY_similarequal 0x8c9 #define CLUTTER_KEY_ifonlyif 0x8cd #define CLUTTER_KEY_implies 0x8ce #define CLUTTER_KEY_identical 0x8cf #define CLUTTER_KEY_radical 0x8d6 #define CLUTTER_KEY_includedin 0x8da #define CLUTTER_KEY_includes 0x8db #define CLUTTER_KEY_intersection 0x8dc #define CLUTTER_KEY_union 0x8dd #define CLUTTER_KEY_logicaland 0x8de #define CLUTTER_KEY_logicalor 0x8df #define CLUTTER_KEY_partialderivative 0x8ef #define CLUTTER_KEY_function 0x8f6 #define CLUTTER_KEY_leftarrow 0x8fb #define CLUTTER_KEY_uparrow 0x8fc #define CLUTTER_KEY_rightarrow 0x8fd #define CLUTTER_KEY_downarrow 0x8fe #define CLUTTER_KEY_blank 0x9df #define CLUTTER_KEY_soliddiamond 0x9e0 #define CLUTTER_KEY_checkerboard 0x9e1 #define CLUTTER_KEY_ht 0x9e2 #define CLUTTER_KEY_ff 0x9e3 #define CLUTTER_KEY_cr 0x9e4 #define CLUTTER_KEY_lf 0x9e5 #define CLUTTER_KEY_nl 0x9e8 #define CLUTTER_KEY_vt 0x9e9 #define CLUTTER_KEY_lowrightcorner 0x9ea #define CLUTTER_KEY_uprightcorner 0x9eb #define CLUTTER_KEY_upleftcorner 0x9ec #define CLUTTER_KEY_lowleftcorner 0x9ed #define CLUTTER_KEY_crossinglines 0x9ee #define CLUTTER_KEY_horizlinescan1 0x9ef #define CLUTTER_KEY_horizlinescan3 0x9f0 #define CLUTTER_KEY_horizlinescan5 0x9f1 #define CLUTTER_KEY_horizlinescan7 0x9f2 #define CLUTTER_KEY_horizlinescan9 0x9f3 #define CLUTTER_KEY_leftt 0x9f4 #define CLUTTER_KEY_rightt 0x9f5 #define CLUTTER_KEY_bott 0x9f6 #define CLUTTER_KEY_topt 0x9f7 #define CLUTTER_KEY_vertbar 0x9f8 #define CLUTTER_KEY_emspace 0xaa1 #define CLUTTER_KEY_enspace 0xaa2 #define CLUTTER_KEY_em3space 0xaa3 #define CLUTTER_KEY_em4space 0xaa4 #define CLUTTER_KEY_digitspace 0xaa5 #define CLUTTER_KEY_punctspace 0xaa6 #define CLUTTER_KEY_thinspace 0xaa7 #define CLUTTER_KEY_hairspace 0xaa8 #define CLUTTER_KEY_emdash 0xaa9 #define CLUTTER_KEY_endash 0xaaa #define CLUTTER_KEY_signifblank 0xaac #define CLUTTER_KEY_ellipsis 0xaae #define CLUTTER_KEY_doubbaselinedot 0xaaf #define CLUTTER_KEY_onethird 0xab0 #define CLUTTER_KEY_twothirds 0xab1 #define CLUTTER_KEY_onefifth 0xab2 #define CLUTTER_KEY_twofifths 0xab3 #define CLUTTER_KEY_threefifths 0xab4 #define CLUTTER_KEY_fourfifths 0xab5 #define CLUTTER_KEY_onesixth 0xab6 #define CLUTTER_KEY_fivesixths 0xab7 #define CLUTTER_KEY_careof 0xab8 #define CLUTTER_KEY_figdash 0xabb #define CLUTTER_KEY_leftanglebracket 0xabc #define CLUTTER_KEY_decimalpoint 0xabd #define CLUTTER_KEY_rightanglebracket 0xabe #define CLUTTER_KEY_marker 0xabf #define CLUTTER_KEY_oneeighth 0xac3 #define CLUTTER_KEY_threeeighths 0xac4 #define CLUTTER_KEY_fiveeighths 0xac5 #define CLUTTER_KEY_seveneighths 0xac6 #define CLUTTER_KEY_trademark 0xac9 #define CLUTTER_KEY_signaturemark 0xaca #define CLUTTER_KEY_trademarkincircle 0xacb #define CLUTTER_KEY_leftopentriangle 0xacc #define CLUTTER_KEY_rightopentriangle 0xacd #define CLUTTER_KEY_emopencircle 0xace #define CLUTTER_KEY_emopenrectangle 0xacf #define CLUTTER_KEY_leftsinglequotemark 0xad0 #define CLUTTER_KEY_rightsinglequotemark 0xad1 #define CLUTTER_KEY_leftdoublequotemark 0xad2 #define CLUTTER_KEY_rightdoublequotemark 0xad3 #define CLUTTER_KEY_prescription 0xad4 #define CLUTTER_KEY_permille 0xad5 #define CLUTTER_KEY_minutes 0xad6 #define CLUTTER_KEY_seconds 0xad7 #define CLUTTER_KEY_latincross 0xad9 #define CLUTTER_KEY_hexagram 0xada #define CLUTTER_KEY_filledrectbullet 0xadb #define CLUTTER_KEY_filledlefttribullet 0xadc #define CLUTTER_KEY_filledrighttribullet 0xadd #define CLUTTER_KEY_emfilledcircle 0xade #define CLUTTER_KEY_emfilledrect 0xadf #define CLUTTER_KEY_enopencircbullet 0xae0 #define CLUTTER_KEY_enopensquarebullet 0xae1 #define CLUTTER_KEY_openrectbullet 0xae2 #define CLUTTER_KEY_opentribulletup 0xae3 #define CLUTTER_KEY_opentribulletdown 0xae4 #define CLUTTER_KEY_openstar 0xae5 #define CLUTTER_KEY_enfilledcircbullet 0xae6 #define CLUTTER_KEY_enfilledsqbullet 0xae7 #define CLUTTER_KEY_filledtribulletup 0xae8 #define CLUTTER_KEY_filledtribulletdown 0xae9 #define CLUTTER_KEY_leftpointer 0xaea #define CLUTTER_KEY_rightpointer 0xaeb #define CLUTTER_KEY_club 0xaec #define CLUTTER_KEY_diamond 0xaed #define CLUTTER_KEY_heart 0xaee #define CLUTTER_KEY_maltesecross 0xaf0 #define CLUTTER_KEY_dagger 0xaf1 #define CLUTTER_KEY_doubledagger 0xaf2 #define CLUTTER_KEY_checkmark 0xaf3 #define CLUTTER_KEY_ballotcross 0xaf4 #define CLUTTER_KEY_musicalsharp 0xaf5 #define CLUTTER_KEY_musicalflat 0xaf6 #define CLUTTER_KEY_malesymbol 0xaf7 #define CLUTTER_KEY_femalesymbol 0xaf8 #define CLUTTER_KEY_telephone 0xaf9 #define CLUTTER_KEY_telephonerecorder 0xafa #define CLUTTER_KEY_phonographcopyright 0xafb #define CLUTTER_KEY_caret 0xafc #define CLUTTER_KEY_singlelowquotemark 0xafd #define CLUTTER_KEY_doublelowquotemark 0xafe #define CLUTTER_KEY_cursor 0xaff #define CLUTTER_KEY_leftcaret 0xba3 #define CLUTTER_KEY_rightcaret 0xba6 #define CLUTTER_KEY_downcaret 0xba8 #define CLUTTER_KEY_upcaret 0xba9 #define CLUTTER_KEY_overbar 0xbc0 #define CLUTTER_KEY_downtack 0xbc2 #define CLUTTER_KEY_upshoe 0xbc3 #define CLUTTER_KEY_downstile 0xbc4 #define CLUTTER_KEY_underbar 0xbc6 #define CLUTTER_KEY_jot 0xbca #define CLUTTER_KEY_quad 0xbcc #define CLUTTER_KEY_uptack 0xbce #define CLUTTER_KEY_circle 0xbcf #define CLUTTER_KEY_upstile 0xbd3 #define CLUTTER_KEY_downshoe 0xbd6 #define CLUTTER_KEY_rightshoe 0xbd8 #define CLUTTER_KEY_leftshoe 0xbda #define CLUTTER_KEY_lefttack 0xbdc #define CLUTTER_KEY_righttack 0xbfc #define CLUTTER_KEY_hebrew_doublelowline 0xcdf #define CLUTTER_KEY_hebrew_aleph 0xce0 #define CLUTTER_KEY_hebrew_bet 0xce1 #define CLUTTER_KEY_hebrew_beth 0xce1 #define CLUTTER_KEY_hebrew_gimel 0xce2 #define CLUTTER_KEY_hebrew_gimmel 0xce2 #define CLUTTER_KEY_hebrew_dalet 0xce3 #define CLUTTER_KEY_hebrew_daleth 0xce3 #define CLUTTER_KEY_hebrew_he 0xce4 #define CLUTTER_KEY_hebrew_waw 0xce5 #define CLUTTER_KEY_hebrew_zain 0xce6 #define CLUTTER_KEY_hebrew_zayin 0xce6 #define CLUTTER_KEY_hebrew_chet 0xce7 #define CLUTTER_KEY_hebrew_het 0xce7 #define CLUTTER_KEY_hebrew_tet 0xce8 #define CLUTTER_KEY_hebrew_teth 0xce8 #define CLUTTER_KEY_hebrew_yod 0xce9 #define CLUTTER_KEY_hebrew_finalkaph 0xcea #define CLUTTER_KEY_hebrew_kaph 0xceb #define CLUTTER_KEY_hebrew_lamed 0xcec #define CLUTTER_KEY_hebrew_finalmem 0xced #define CLUTTER_KEY_hebrew_mem 0xcee #define CLUTTER_KEY_hebrew_finalnun 0xcef #define CLUTTER_KEY_hebrew_nun 0xcf0 #define CLUTTER_KEY_hebrew_samech 0xcf1 #define CLUTTER_KEY_hebrew_samekh 0xcf1 #define CLUTTER_KEY_hebrew_ayin 0xcf2 #define CLUTTER_KEY_hebrew_finalpe 0xcf3 #define CLUTTER_KEY_hebrew_pe 0xcf4 #define CLUTTER_KEY_hebrew_finalzade 0xcf5 #define CLUTTER_KEY_hebrew_finalzadi 0xcf5 #define CLUTTER_KEY_hebrew_zade 0xcf6 #define CLUTTER_KEY_hebrew_zadi 0xcf6 #define CLUTTER_KEY_hebrew_qoph 0xcf7 #define CLUTTER_KEY_hebrew_kuf 0xcf7 #define CLUTTER_KEY_hebrew_resh 0xcf8 #define CLUTTER_KEY_hebrew_shin 0xcf9 #define CLUTTER_KEY_hebrew_taw 0xcfa #define CLUTTER_KEY_hebrew_taf 0xcfa #define CLUTTER_KEY_Hebrew_switch 0xff7e #define CLUTTER_KEY_Thai_kokai 0xda1 #define CLUTTER_KEY_Thai_khokhai 0xda2 #define CLUTTER_KEY_Thai_khokhuat 0xda3 #define CLUTTER_KEY_Thai_khokhwai 0xda4 #define CLUTTER_KEY_Thai_khokhon 0xda5 #define CLUTTER_KEY_Thai_khorakhang 0xda6 #define CLUTTER_KEY_Thai_ngongu 0xda7 #define CLUTTER_KEY_Thai_chochan 0xda8 #define CLUTTER_KEY_Thai_choching 0xda9 #define CLUTTER_KEY_Thai_chochang 0xdaa #define CLUTTER_KEY_Thai_soso 0xdab #define CLUTTER_KEY_Thai_chochoe 0xdac #define CLUTTER_KEY_Thai_yoying 0xdad #define CLUTTER_KEY_Thai_dochada 0xdae #define CLUTTER_KEY_Thai_topatak 0xdaf #define CLUTTER_KEY_Thai_thothan 0xdb0 #define CLUTTER_KEY_Thai_thonangmontho 0xdb1 #define CLUTTER_KEY_Thai_thophuthao 0xdb2 #define CLUTTER_KEY_Thai_nonen 0xdb3 #define CLUTTER_KEY_Thai_dodek 0xdb4 #define CLUTTER_KEY_Thai_totao 0xdb5 #define CLUTTER_KEY_Thai_thothung 0xdb6 #define CLUTTER_KEY_Thai_thothahan 0xdb7 #define CLUTTER_KEY_Thai_thothong 0xdb8 #define CLUTTER_KEY_Thai_nonu 0xdb9 #define CLUTTER_KEY_Thai_bobaimai 0xdba #define CLUTTER_KEY_Thai_popla 0xdbb #define CLUTTER_KEY_Thai_phophung 0xdbc #define CLUTTER_KEY_Thai_fofa 0xdbd #define CLUTTER_KEY_Thai_phophan 0xdbe #define CLUTTER_KEY_Thai_fofan 0xdbf #define CLUTTER_KEY_Thai_phosamphao 0xdc0 #define CLUTTER_KEY_Thai_moma 0xdc1 #define CLUTTER_KEY_Thai_yoyak 0xdc2 #define CLUTTER_KEY_Thai_rorua 0xdc3 #define CLUTTER_KEY_Thai_ru 0xdc4 #define CLUTTER_KEY_Thai_loling 0xdc5 #define CLUTTER_KEY_Thai_lu 0xdc6 #define CLUTTER_KEY_Thai_wowaen 0xdc7 #define CLUTTER_KEY_Thai_sosala 0xdc8 #define CLUTTER_KEY_Thai_sorusi 0xdc9 #define CLUTTER_KEY_Thai_sosua 0xdca #define CLUTTER_KEY_Thai_hohip 0xdcb #define CLUTTER_KEY_Thai_lochula 0xdcc #define CLUTTER_KEY_Thai_oang 0xdcd #define CLUTTER_KEY_Thai_honokhuk 0xdce #define CLUTTER_KEY_Thai_paiyannoi 0xdcf #define CLUTTER_KEY_Thai_saraa 0xdd0 #define CLUTTER_KEY_Thai_maihanakat 0xdd1 #define CLUTTER_KEY_Thai_saraaa 0xdd2 #define CLUTTER_KEY_Thai_saraam 0xdd3 #define CLUTTER_KEY_Thai_sarai 0xdd4 #define CLUTTER_KEY_Thai_saraii 0xdd5 #define CLUTTER_KEY_Thai_saraue 0xdd6 #define CLUTTER_KEY_Thai_sarauee 0xdd7 #define CLUTTER_KEY_Thai_sarau 0xdd8 #define CLUTTER_KEY_Thai_sarauu 0xdd9 #define CLUTTER_KEY_Thai_phinthu 0xdda #define CLUTTER_KEY_Thai_maihanakat_maitho 0xdde #define CLUTTER_KEY_Thai_baht 0xddf #define CLUTTER_KEY_Thai_sarae 0xde0 #define CLUTTER_KEY_Thai_saraae 0xde1 #define CLUTTER_KEY_Thai_sarao 0xde2 #define CLUTTER_KEY_Thai_saraaimaimuan 0xde3 #define CLUTTER_KEY_Thai_saraaimaimalai 0xde4 #define CLUTTER_KEY_Thai_lakkhangyao 0xde5 #define CLUTTER_KEY_Thai_maiyamok 0xde6 #define CLUTTER_KEY_Thai_maitaikhu 0xde7 #define CLUTTER_KEY_Thai_maiek 0xde8 #define CLUTTER_KEY_Thai_maitho 0xde9 #define CLUTTER_KEY_Thai_maitri 0xdea #define CLUTTER_KEY_Thai_maichattawa 0xdeb #define CLUTTER_KEY_Thai_thanthakhat 0xdec #define CLUTTER_KEY_Thai_nikhahit 0xded #define CLUTTER_KEY_Thai_leksun 0xdf0 #define CLUTTER_KEY_Thai_leknung 0xdf1 #define CLUTTER_KEY_Thai_leksong 0xdf2 #define CLUTTER_KEY_Thai_leksam 0xdf3 #define CLUTTER_KEY_Thai_leksi 0xdf4 #define CLUTTER_KEY_Thai_lekha 0xdf5 #define CLUTTER_KEY_Thai_lekhok 0xdf6 #define CLUTTER_KEY_Thai_lekchet 0xdf7 #define CLUTTER_KEY_Thai_lekpaet 0xdf8 #define CLUTTER_KEY_Thai_lekkao 0xdf9 #define CLUTTER_KEY_Hangul 0xff31 #define CLUTTER_KEY_Hangul_Start 0xff32 #define CLUTTER_KEY_Hangul_End 0xff33 #define CLUTTER_KEY_Hangul_Hanja 0xff34 #define CLUTTER_KEY_Hangul_Jamo 0xff35 #define CLUTTER_KEY_Hangul_Romaja 0xff36 #define CLUTTER_KEY_Hangul_Codeinput 0xff37 #define CLUTTER_KEY_Hangul_Jeonja 0xff38 #define CLUTTER_KEY_Hangul_Banja 0xff39 #define CLUTTER_KEY_Hangul_PreHanja 0xff3a #define CLUTTER_KEY_Hangul_PostHanja 0xff3b #define CLUTTER_KEY_Hangul_SingleCandidate 0xff3c #define CLUTTER_KEY_Hangul_MultipleCandidate 0xff3d #define CLUTTER_KEY_Hangul_PreviousCandidate 0xff3e #define CLUTTER_KEY_Hangul_Special 0xff3f #define CLUTTER_KEY_Hangul_switch 0xff7e #define CLUTTER_KEY_Hangul_Kiyeog 0xea1 #define CLUTTER_KEY_Hangul_SsangKiyeog 0xea2 #define CLUTTER_KEY_Hangul_KiyeogSios 0xea3 #define CLUTTER_KEY_Hangul_Nieun 0xea4 #define CLUTTER_KEY_Hangul_NieunJieuj 0xea5 #define CLUTTER_KEY_Hangul_NieunHieuh 0xea6 #define CLUTTER_KEY_Hangul_Dikeud 0xea7 #define CLUTTER_KEY_Hangul_SsangDikeud 0xea8 #define CLUTTER_KEY_Hangul_Rieul 0xea9 #define CLUTTER_KEY_Hangul_RieulKiyeog 0xeaa #define CLUTTER_KEY_Hangul_RieulMieum 0xeab #define CLUTTER_KEY_Hangul_RieulPieub 0xeac #define CLUTTER_KEY_Hangul_RieulSios 0xead #define CLUTTER_KEY_Hangul_RieulTieut 0xeae #define CLUTTER_KEY_Hangul_RieulPhieuf 0xeaf #define CLUTTER_KEY_Hangul_RieulHieuh 0xeb0 #define CLUTTER_KEY_Hangul_Mieum 0xeb1 #define CLUTTER_KEY_Hangul_Pieub 0xeb2 #define CLUTTER_KEY_Hangul_SsangPieub 0xeb3 #define CLUTTER_KEY_Hangul_PieubSios 0xeb4 #define CLUTTER_KEY_Hangul_Sios 0xeb5 #define CLUTTER_KEY_Hangul_SsangSios 0xeb6 #define CLUTTER_KEY_Hangul_Ieung 0xeb7 #define CLUTTER_KEY_Hangul_Jieuj 0xeb8 #define CLUTTER_KEY_Hangul_SsangJieuj 0xeb9 #define CLUTTER_KEY_Hangul_Cieuc 0xeba #define CLUTTER_KEY_Hangul_Khieuq 0xebb #define CLUTTER_KEY_Hangul_Tieut 0xebc #define CLUTTER_KEY_Hangul_Phieuf 0xebd #define CLUTTER_KEY_Hangul_Hieuh 0xebe #define CLUTTER_KEY_Hangul_A 0xebf #define CLUTTER_KEY_Hangul_AE 0xec0 #define CLUTTER_KEY_Hangul_YA 0xec1 #define CLUTTER_KEY_Hangul_YAE 0xec2 #define CLUTTER_KEY_Hangul_EO 0xec3 #define CLUTTER_KEY_Hangul_E 0xec4 #define CLUTTER_KEY_Hangul_YEO 0xec5 #define CLUTTER_KEY_Hangul_YE 0xec6 #define CLUTTER_KEY_Hangul_O 0xec7 #define CLUTTER_KEY_Hangul_WA 0xec8 #define CLUTTER_KEY_Hangul_WAE 0xec9 #define CLUTTER_KEY_Hangul_OE 0xeca #define CLUTTER_KEY_Hangul_YO 0xecb #define CLUTTER_KEY_Hangul_U 0xecc #define CLUTTER_KEY_Hangul_WEO 0xecd #define CLUTTER_KEY_Hangul_WE 0xece #define CLUTTER_KEY_Hangul_WI 0xecf #define CLUTTER_KEY_Hangul_YU 0xed0 #define CLUTTER_KEY_Hangul_EU 0xed1 #define CLUTTER_KEY_Hangul_YI 0xed2 #define CLUTTER_KEY_Hangul_I 0xed3 #define CLUTTER_KEY_Hangul_J_Kiyeog 0xed4 #define CLUTTER_KEY_Hangul_J_SsangKiyeog 0xed5 #define CLUTTER_KEY_Hangul_J_KiyeogSios 0xed6 #define CLUTTER_KEY_Hangul_J_Nieun 0xed7 #define CLUTTER_KEY_Hangul_J_NieunJieuj 0xed8 #define CLUTTER_KEY_Hangul_J_NieunHieuh 0xed9 #define CLUTTER_KEY_Hangul_J_Dikeud 0xeda #define CLUTTER_KEY_Hangul_J_Rieul 0xedb #define CLUTTER_KEY_Hangul_J_RieulKiyeog 0xedc #define CLUTTER_KEY_Hangul_J_RieulMieum 0xedd #define CLUTTER_KEY_Hangul_J_RieulPieub 0xede #define CLUTTER_KEY_Hangul_J_RieulSios 0xedf #define CLUTTER_KEY_Hangul_J_RieulTieut 0xee0 #define CLUTTER_KEY_Hangul_J_RieulPhieuf 0xee1 #define CLUTTER_KEY_Hangul_J_RieulHieuh 0xee2 #define CLUTTER_KEY_Hangul_J_Mieum 0xee3 #define CLUTTER_KEY_Hangul_J_Pieub 0xee4 #define CLUTTER_KEY_Hangul_J_PieubSios 0xee5 #define CLUTTER_KEY_Hangul_J_Sios 0xee6 #define CLUTTER_KEY_Hangul_J_SsangSios 0xee7 #define CLUTTER_KEY_Hangul_J_Ieung 0xee8 #define CLUTTER_KEY_Hangul_J_Jieuj 0xee9 #define CLUTTER_KEY_Hangul_J_Cieuc 0xeea #define CLUTTER_KEY_Hangul_J_Khieuq 0xeeb #define CLUTTER_KEY_Hangul_J_Tieut 0xeec #define CLUTTER_KEY_Hangul_J_Phieuf 0xeed #define CLUTTER_KEY_Hangul_J_Hieuh 0xeee #define CLUTTER_KEY_Hangul_RieulYeorinHieuh 0xeef #define CLUTTER_KEY_Hangul_SunkyeongeumMieum 0xef0 #define CLUTTER_KEY_Hangul_SunkyeongeumPieub 0xef1 #define CLUTTER_KEY_Hangul_PanSios 0xef2 #define CLUTTER_KEY_Hangul_KkogjiDalrinIeung 0xef3 #define CLUTTER_KEY_Hangul_SunkyeongeumPhieuf 0xef4 #define CLUTTER_KEY_Hangul_YeorinHieuh 0xef5 #define CLUTTER_KEY_Hangul_AraeA 0xef6 #define CLUTTER_KEY_Hangul_AraeAE 0xef7 #define CLUTTER_KEY_Hangul_J_PanSios 0xef8 #define CLUTTER_KEY_Hangul_J_KkogjiDalrinIeung 0xef9 #define CLUTTER_KEY_Hangul_J_YeorinHieuh 0xefa #define CLUTTER_KEY_Korean_Won 0xeff #define CLUTTER_KEY_Armenian_ligature_ew 0x1000587 #define CLUTTER_KEY_Armenian_full_stop 0x1000589 #define CLUTTER_KEY_Armenian_verjaket 0x1000589 #define CLUTTER_KEY_Armenian_separation_mark 0x100055d #define CLUTTER_KEY_Armenian_but 0x100055d #define CLUTTER_KEY_Armenian_hyphen 0x100058a #define CLUTTER_KEY_Armenian_yentamna 0x100058a #define CLUTTER_KEY_Armenian_exclam 0x100055c #define CLUTTER_KEY_Armenian_amanak 0x100055c #define CLUTTER_KEY_Armenian_accent 0x100055b #define CLUTTER_KEY_Armenian_shesht 0x100055b #define CLUTTER_KEY_Armenian_question 0x100055e #define CLUTTER_KEY_Armenian_paruyk 0x100055e #define CLUTTER_KEY_Armenian_AYB 0x1000531 #define CLUTTER_KEY_Armenian_ayb 0x1000561 #define CLUTTER_KEY_Armenian_BEN 0x1000532 #define CLUTTER_KEY_Armenian_ben 0x1000562 #define CLUTTER_KEY_Armenian_GIM 0x1000533 #define CLUTTER_KEY_Armenian_gim 0x1000563 #define CLUTTER_KEY_Armenian_DA 0x1000534 #define CLUTTER_KEY_Armenian_da 0x1000564 #define CLUTTER_KEY_Armenian_YECH 0x1000535 #define CLUTTER_KEY_Armenian_yech 0x1000565 #define CLUTTER_KEY_Armenian_ZA 0x1000536 #define CLUTTER_KEY_Armenian_za 0x1000566 #define CLUTTER_KEY_Armenian_E 0x1000537 #define CLUTTER_KEY_Armenian_e 0x1000567 #define CLUTTER_KEY_Armenian_AT 0x1000538 #define CLUTTER_KEY_Armenian_at 0x1000568 #define CLUTTER_KEY_Armenian_TO 0x1000539 #define CLUTTER_KEY_Armenian_to 0x1000569 #define CLUTTER_KEY_Armenian_ZHE 0x100053a #define CLUTTER_KEY_Armenian_zhe 0x100056a #define CLUTTER_KEY_Armenian_INI 0x100053b #define CLUTTER_KEY_Armenian_ini 0x100056b #define CLUTTER_KEY_Armenian_LYUN 0x100053c #define CLUTTER_KEY_Armenian_lyun 0x100056c #define CLUTTER_KEY_Armenian_KHE 0x100053d #define CLUTTER_KEY_Armenian_khe 0x100056d #define CLUTTER_KEY_Armenian_TSA 0x100053e #define CLUTTER_KEY_Armenian_tsa 0x100056e #define CLUTTER_KEY_Armenian_KEN 0x100053f #define CLUTTER_KEY_Armenian_ken 0x100056f #define CLUTTER_KEY_Armenian_HO 0x1000540 #define CLUTTER_KEY_Armenian_ho 0x1000570 #define CLUTTER_KEY_Armenian_DZA 0x1000541 #define CLUTTER_KEY_Armenian_dza 0x1000571 #define CLUTTER_KEY_Armenian_GHAT 0x1000542 #define CLUTTER_KEY_Armenian_ghat 0x1000572 #define CLUTTER_KEY_Armenian_TCHE 0x1000543 #define CLUTTER_KEY_Armenian_tche 0x1000573 #define CLUTTER_KEY_Armenian_MEN 0x1000544 #define CLUTTER_KEY_Armenian_men 0x1000574 #define CLUTTER_KEY_Armenian_HI 0x1000545 #define CLUTTER_KEY_Armenian_hi 0x1000575 #define CLUTTER_KEY_Armenian_NU 0x1000546 #define CLUTTER_KEY_Armenian_nu 0x1000576 #define CLUTTER_KEY_Armenian_SHA 0x1000547 #define CLUTTER_KEY_Armenian_sha 0x1000577 #define CLUTTER_KEY_Armenian_VO 0x1000548 #define CLUTTER_KEY_Armenian_vo 0x1000578 #define CLUTTER_KEY_Armenian_CHA 0x1000549 #define CLUTTER_KEY_Armenian_cha 0x1000579 #define CLUTTER_KEY_Armenian_PE 0x100054a #define CLUTTER_KEY_Armenian_pe 0x100057a #define CLUTTER_KEY_Armenian_JE 0x100054b #define CLUTTER_KEY_Armenian_je 0x100057b #define CLUTTER_KEY_Armenian_RA 0x100054c #define CLUTTER_KEY_Armenian_ra 0x100057c #define CLUTTER_KEY_Armenian_SE 0x100054d #define CLUTTER_KEY_Armenian_se 0x100057d #define CLUTTER_KEY_Armenian_VEV 0x100054e #define CLUTTER_KEY_Armenian_vev 0x100057e #define CLUTTER_KEY_Armenian_TYUN 0x100054f #define CLUTTER_KEY_Armenian_tyun 0x100057f #define CLUTTER_KEY_Armenian_RE 0x1000550 #define CLUTTER_KEY_Armenian_re 0x1000580 #define CLUTTER_KEY_Armenian_TSO 0x1000551 #define CLUTTER_KEY_Armenian_tso 0x1000581 #define CLUTTER_KEY_Armenian_VYUN 0x1000552 #define CLUTTER_KEY_Armenian_vyun 0x1000582 #define CLUTTER_KEY_Armenian_PYUR 0x1000553 #define CLUTTER_KEY_Armenian_pyur 0x1000583 #define CLUTTER_KEY_Armenian_KE 0x1000554 #define CLUTTER_KEY_Armenian_ke 0x1000584 #define CLUTTER_KEY_Armenian_O 0x1000555 #define CLUTTER_KEY_Armenian_o 0x1000585 #define CLUTTER_KEY_Armenian_FE 0x1000556 #define CLUTTER_KEY_Armenian_fe 0x1000586 #define CLUTTER_KEY_Armenian_apostrophe 0x100055a #define CLUTTER_KEY_Georgian_an 0x10010d0 #define CLUTTER_KEY_Georgian_ban 0x10010d1 #define CLUTTER_KEY_Georgian_gan 0x10010d2 #define CLUTTER_KEY_Georgian_don 0x10010d3 #define CLUTTER_KEY_Georgian_en 0x10010d4 #define CLUTTER_KEY_Georgian_vin 0x10010d5 #define CLUTTER_KEY_Georgian_zen 0x10010d6 #define CLUTTER_KEY_Georgian_tan 0x10010d7 #define CLUTTER_KEY_Georgian_in 0x10010d8 #define CLUTTER_KEY_Georgian_kan 0x10010d9 #define CLUTTER_KEY_Georgian_las 0x10010da #define CLUTTER_KEY_Georgian_man 0x10010db #define CLUTTER_KEY_Georgian_nar 0x10010dc #define CLUTTER_KEY_Georgian_on 0x10010dd #define CLUTTER_KEY_Georgian_par 0x10010de #define CLUTTER_KEY_Georgian_zhar 0x10010df #define CLUTTER_KEY_Georgian_rae 0x10010e0 #define CLUTTER_KEY_Georgian_san 0x10010e1 #define CLUTTER_KEY_Georgian_tar 0x10010e2 #define CLUTTER_KEY_Georgian_un 0x10010e3 #define CLUTTER_KEY_Georgian_phar 0x10010e4 #define CLUTTER_KEY_Georgian_khar 0x10010e5 #define CLUTTER_KEY_Georgian_ghan 0x10010e6 #define CLUTTER_KEY_Georgian_qar 0x10010e7 #define CLUTTER_KEY_Georgian_shin 0x10010e8 #define CLUTTER_KEY_Georgian_chin 0x10010e9 #define CLUTTER_KEY_Georgian_can 0x10010ea #define CLUTTER_KEY_Georgian_jil 0x10010eb #define CLUTTER_KEY_Georgian_cil 0x10010ec #define CLUTTER_KEY_Georgian_char 0x10010ed #define CLUTTER_KEY_Georgian_xan 0x10010ee #define CLUTTER_KEY_Georgian_jhan 0x10010ef #define CLUTTER_KEY_Georgian_hae 0x10010f0 #define CLUTTER_KEY_Georgian_he 0x10010f1 #define CLUTTER_KEY_Georgian_hie 0x10010f2 #define CLUTTER_KEY_Georgian_we 0x10010f3 #define CLUTTER_KEY_Georgian_har 0x10010f4 #define CLUTTER_KEY_Georgian_hoe 0x10010f5 #define CLUTTER_KEY_Georgian_fi 0x10010f6 #define CLUTTER_KEY_Xabovedot 0x1001e8a #define CLUTTER_KEY_Ibreve 0x100012c #define CLUTTER_KEY_Zstroke 0x10001b5 #define CLUTTER_KEY_Gcaron 0x10001e6 #define CLUTTER_KEY_Ocaron 0x10001d1 #define CLUTTER_KEY_Obarred 0x100019f #define CLUTTER_KEY_xabovedot 0x1001e8b #define CLUTTER_KEY_ibreve 0x100012d #define CLUTTER_KEY_zstroke 0x10001b6 #define CLUTTER_KEY_gcaron 0x10001e7 #define CLUTTER_KEY_ocaron 0x10001d2 #define CLUTTER_KEY_obarred 0x1000275 #define CLUTTER_KEY_SCHWA 0x100018f #define CLUTTER_KEY_schwa 0x1000259 #define CLUTTER_KEY_EZH 0x10001b7 #define CLUTTER_KEY_ezh 0x1000292 #define CLUTTER_KEY_Lbelowdot 0x1001e36 #define CLUTTER_KEY_lbelowdot 0x1001e37 #define CLUTTER_KEY_Abelowdot 0x1001ea0 #define CLUTTER_KEY_abelowdot 0x1001ea1 #define CLUTTER_KEY_Ahook 0x1001ea2 #define CLUTTER_KEY_ahook 0x1001ea3 #define CLUTTER_KEY_Acircumflexacute 0x1001ea4 #define CLUTTER_KEY_acircumflexacute 0x1001ea5 #define CLUTTER_KEY_Acircumflexgrave 0x1001ea6 #define CLUTTER_KEY_acircumflexgrave 0x1001ea7 #define CLUTTER_KEY_Acircumflexhook 0x1001ea8 #define CLUTTER_KEY_acircumflexhook 0x1001ea9 #define CLUTTER_KEY_Acircumflextilde 0x1001eaa #define CLUTTER_KEY_acircumflextilde 0x1001eab #define CLUTTER_KEY_Acircumflexbelowdot 0x1001eac #define CLUTTER_KEY_acircumflexbelowdot 0x1001ead #define CLUTTER_KEY_Abreveacute 0x1001eae #define CLUTTER_KEY_abreveacute 0x1001eaf #define CLUTTER_KEY_Abrevegrave 0x1001eb0 #define CLUTTER_KEY_abrevegrave 0x1001eb1 #define CLUTTER_KEY_Abrevehook 0x1001eb2 #define CLUTTER_KEY_abrevehook 0x1001eb3 #define CLUTTER_KEY_Abrevetilde 0x1001eb4 #define CLUTTER_KEY_abrevetilde 0x1001eb5 #define CLUTTER_KEY_Abrevebelowdot 0x1001eb6 #define CLUTTER_KEY_abrevebelowdot 0x1001eb7 #define CLUTTER_KEY_Ebelowdot 0x1001eb8 #define CLUTTER_KEY_ebelowdot 0x1001eb9 #define CLUTTER_KEY_Ehook 0x1001eba #define CLUTTER_KEY_ehook 0x1001ebb #define CLUTTER_KEY_Etilde 0x1001ebc #define CLUTTER_KEY_etilde 0x1001ebd #define CLUTTER_KEY_Ecircumflexacute 0x1001ebe #define CLUTTER_KEY_ecircumflexacute 0x1001ebf #define CLUTTER_KEY_Ecircumflexgrave 0x1001ec0 #define CLUTTER_KEY_ecircumflexgrave 0x1001ec1 #define CLUTTER_KEY_Ecircumflexhook 0x1001ec2 #define CLUTTER_KEY_ecircumflexhook 0x1001ec3 #define CLUTTER_KEY_Ecircumflextilde 0x1001ec4 #define CLUTTER_KEY_ecircumflextilde 0x1001ec5 #define CLUTTER_KEY_Ecircumflexbelowdot 0x1001ec6 #define CLUTTER_KEY_ecircumflexbelowdot 0x1001ec7 #define CLUTTER_KEY_Ihook 0x1001ec8 #define CLUTTER_KEY_ihook 0x1001ec9 #define CLUTTER_KEY_Ibelowdot 0x1001eca #define CLUTTER_KEY_ibelowdot 0x1001ecb #define CLUTTER_KEY_Obelowdot 0x1001ecc #define CLUTTER_KEY_obelowdot 0x1001ecd #define CLUTTER_KEY_Ohook 0x1001ece #define CLUTTER_KEY_ohook 0x1001ecf #define CLUTTER_KEY_Ocircumflexacute 0x1001ed0 #define CLUTTER_KEY_ocircumflexacute 0x1001ed1 #define CLUTTER_KEY_Ocircumflexgrave 0x1001ed2 #define CLUTTER_KEY_ocircumflexgrave 0x1001ed3 #define CLUTTER_KEY_Ocircumflexhook 0x1001ed4 #define CLUTTER_KEY_ocircumflexhook 0x1001ed5 #define CLUTTER_KEY_Ocircumflextilde 0x1001ed6 #define CLUTTER_KEY_ocircumflextilde 0x1001ed7 #define CLUTTER_KEY_Ocircumflexbelowdot 0x1001ed8 #define CLUTTER_KEY_ocircumflexbelowdot 0x1001ed9 #define CLUTTER_KEY_Ohornacute 0x1001eda #define CLUTTER_KEY_ohornacute 0x1001edb #define CLUTTER_KEY_Ohorngrave 0x1001edc #define CLUTTER_KEY_ohorngrave 0x1001edd #define CLUTTER_KEY_Ohornhook 0x1001ede #define CLUTTER_KEY_ohornhook 0x1001edf #define CLUTTER_KEY_Ohorntilde 0x1001ee0 #define CLUTTER_KEY_ohorntilde 0x1001ee1 #define CLUTTER_KEY_Ohornbelowdot 0x1001ee2 #define CLUTTER_KEY_ohornbelowdot 0x1001ee3 #define CLUTTER_KEY_Ubelowdot 0x1001ee4 #define CLUTTER_KEY_ubelowdot 0x1001ee5 #define CLUTTER_KEY_Uhook 0x1001ee6 #define CLUTTER_KEY_uhook 0x1001ee7 #define CLUTTER_KEY_Uhornacute 0x1001ee8 #define CLUTTER_KEY_uhornacute 0x1001ee9 #define CLUTTER_KEY_Uhorngrave 0x1001eea #define CLUTTER_KEY_uhorngrave 0x1001eeb #define CLUTTER_KEY_Uhornhook 0x1001eec #define CLUTTER_KEY_uhornhook 0x1001eed #define CLUTTER_KEY_Uhorntilde 0x1001eee #define CLUTTER_KEY_uhorntilde 0x1001eef #define CLUTTER_KEY_Uhornbelowdot 0x1001ef0 #define CLUTTER_KEY_uhornbelowdot 0x1001ef1 #define CLUTTER_KEY_Ybelowdot 0x1001ef4 #define CLUTTER_KEY_ybelowdot 0x1001ef5 #define CLUTTER_KEY_Yhook 0x1001ef6 #define CLUTTER_KEY_yhook 0x1001ef7 #define CLUTTER_KEY_Ytilde 0x1001ef8 #define CLUTTER_KEY_ytilde 0x1001ef9 #define CLUTTER_KEY_Ohorn 0x10001a0 #define CLUTTER_KEY_ohorn 0x10001a1 #define CLUTTER_KEY_Uhorn 0x10001af #define CLUTTER_KEY_uhorn 0x10001b0 #define CLUTTER_KEY_EcuSign 0x10020a0 #define CLUTTER_KEY_ColonSign 0x10020a1 #define CLUTTER_KEY_CruzeiroSign 0x10020a2 #define CLUTTER_KEY_FFrancSign 0x10020a3 #define CLUTTER_KEY_LiraSign 0x10020a4 #define CLUTTER_KEY_MillSign 0x10020a5 #define CLUTTER_KEY_NairaSign 0x10020a6 #define CLUTTER_KEY_PesetaSign 0x10020a7 #define CLUTTER_KEY_RupeeSign 0x10020a8 #define CLUTTER_KEY_WonSign 0x10020a9 #define CLUTTER_KEY_NewSheqelSign 0x10020aa #define CLUTTER_KEY_DongSign 0x10020ab #define CLUTTER_KEY_EuroSign 0x20ac #define CLUTTER_KEY_zerosuperior 0x1002070 #define CLUTTER_KEY_foursuperior 0x1002074 #define CLUTTER_KEY_fivesuperior 0x1002075 #define CLUTTER_KEY_sixsuperior 0x1002076 #define CLUTTER_KEY_sevensuperior 0x1002077 #define CLUTTER_KEY_eightsuperior 0x1002078 #define CLUTTER_KEY_ninesuperior 0x1002079 #define CLUTTER_KEY_zerosubscript 0x1002080 #define CLUTTER_KEY_onesubscript 0x1002081 #define CLUTTER_KEY_twosubscript 0x1002082 #define CLUTTER_KEY_threesubscript 0x1002083 #define CLUTTER_KEY_foursubscript 0x1002084 #define CLUTTER_KEY_fivesubscript 0x1002085 #define CLUTTER_KEY_sixsubscript 0x1002086 #define CLUTTER_KEY_sevensubscript 0x1002087 #define CLUTTER_KEY_eightsubscript 0x1002088 #define CLUTTER_KEY_ninesubscript 0x1002089 #define CLUTTER_KEY_partdifferential 0x1002202 #define CLUTTER_KEY_emptyset 0x1002205 #define CLUTTER_KEY_elementof 0x1002208 #define CLUTTER_KEY_notelementof 0x1002209 #define CLUTTER_KEY_containsas 0x100220b #define CLUTTER_KEY_squareroot 0x100221a #define CLUTTER_KEY_cuberoot 0x100221b #define CLUTTER_KEY_fourthroot 0x100221c #define CLUTTER_KEY_dintegral 0x100222c #define CLUTTER_KEY_tintegral 0x100222d #define CLUTTER_KEY_because 0x1002235 #define CLUTTER_KEY_approxeq 0x1002248 #define CLUTTER_KEY_notapproxeq 0x1002247 #define CLUTTER_KEY_notidentical 0x1002262 #define CLUTTER_KEY_stricteq 0x1002263 #define CLUTTER_KEY_braille_dot_1 0xfff1 #define CLUTTER_KEY_braille_dot_2 0xfff2 #define CLUTTER_KEY_braille_dot_3 0xfff3 #define CLUTTER_KEY_braille_dot_4 0xfff4 #define CLUTTER_KEY_braille_dot_5 0xfff5 #define CLUTTER_KEY_braille_dot_6 0xfff6 #define CLUTTER_KEY_braille_dot_7 0xfff7 #define CLUTTER_KEY_braille_dot_8 0xfff8 #define CLUTTER_KEY_braille_dot_9 0xfff9 #define CLUTTER_KEY_braille_dot_10 0xfffa #define CLUTTER_KEY_braille_blank 0x1002800 #define CLUTTER_KEY_braille_dots_1 0x1002801 #define CLUTTER_KEY_braille_dots_2 0x1002802 #define CLUTTER_KEY_braille_dots_12 0x1002803 #define CLUTTER_KEY_braille_dots_3 0x1002804 #define CLUTTER_KEY_braille_dots_13 0x1002805 #define CLUTTER_KEY_braille_dots_23 0x1002806 #define CLUTTER_KEY_braille_dots_123 0x1002807 #define CLUTTER_KEY_braille_dots_4 0x1002808 #define CLUTTER_KEY_braille_dots_14 0x1002809 #define CLUTTER_KEY_braille_dots_24 0x100280a #define CLUTTER_KEY_braille_dots_124 0x100280b #define CLUTTER_KEY_braille_dots_34 0x100280c #define CLUTTER_KEY_braille_dots_134 0x100280d #define CLUTTER_KEY_braille_dots_234 0x100280e #define CLUTTER_KEY_braille_dots_1234 0x100280f #define CLUTTER_KEY_braille_dots_5 0x1002810 #define CLUTTER_KEY_braille_dots_15 0x1002811 #define CLUTTER_KEY_braille_dots_25 0x1002812 #define CLUTTER_KEY_braille_dots_125 0x1002813 #define CLUTTER_KEY_braille_dots_35 0x1002814 #define CLUTTER_KEY_braille_dots_135 0x1002815 #define CLUTTER_KEY_braille_dots_235 0x1002816 #define CLUTTER_KEY_braille_dots_1235 0x1002817 #define CLUTTER_KEY_braille_dots_45 0x1002818 #define CLUTTER_KEY_braille_dots_145 0x1002819 #define CLUTTER_KEY_braille_dots_245 0x100281a #define CLUTTER_KEY_braille_dots_1245 0x100281b #define CLUTTER_KEY_braille_dots_345 0x100281c #define CLUTTER_KEY_braille_dots_1345 0x100281d #define CLUTTER_KEY_braille_dots_2345 0x100281e #define CLUTTER_KEY_braille_dots_12345 0x100281f #define CLUTTER_KEY_braille_dots_6 0x1002820 #define CLUTTER_KEY_braille_dots_16 0x1002821 #define CLUTTER_KEY_braille_dots_26 0x1002822 #define CLUTTER_KEY_braille_dots_126 0x1002823 #define CLUTTER_KEY_braille_dots_36 0x1002824 #define CLUTTER_KEY_braille_dots_136 0x1002825 #define CLUTTER_KEY_braille_dots_236 0x1002826 #define CLUTTER_KEY_braille_dots_1236 0x1002827 #define CLUTTER_KEY_braille_dots_46 0x1002828 #define CLUTTER_KEY_braille_dots_146 0x1002829 #define CLUTTER_KEY_braille_dots_246 0x100282a #define CLUTTER_KEY_braille_dots_1246 0x100282b #define CLUTTER_KEY_braille_dots_346 0x100282c #define CLUTTER_KEY_braille_dots_1346 0x100282d #define CLUTTER_KEY_braille_dots_2346 0x100282e #define CLUTTER_KEY_braille_dots_12346 0x100282f #define CLUTTER_KEY_braille_dots_56 0x1002830 #define CLUTTER_KEY_braille_dots_156 0x1002831 #define CLUTTER_KEY_braille_dots_256 0x1002832 #define CLUTTER_KEY_braille_dots_1256 0x1002833 #define CLUTTER_KEY_braille_dots_356 0x1002834 #define CLUTTER_KEY_braille_dots_1356 0x1002835 #define CLUTTER_KEY_braille_dots_2356 0x1002836 #define CLUTTER_KEY_braille_dots_12356 0x1002837 #define CLUTTER_KEY_braille_dots_456 0x1002838 #define CLUTTER_KEY_braille_dots_1456 0x1002839 #define CLUTTER_KEY_braille_dots_2456 0x100283a #define CLUTTER_KEY_braille_dots_12456 0x100283b #define CLUTTER_KEY_braille_dots_3456 0x100283c #define CLUTTER_KEY_braille_dots_13456 0x100283d #define CLUTTER_KEY_braille_dots_23456 0x100283e #define CLUTTER_KEY_braille_dots_123456 0x100283f #define CLUTTER_KEY_braille_dots_7 0x1002840 #define CLUTTER_KEY_braille_dots_17 0x1002841 #define CLUTTER_KEY_braille_dots_27 0x1002842 #define CLUTTER_KEY_braille_dots_127 0x1002843 #define CLUTTER_KEY_braille_dots_37 0x1002844 #define CLUTTER_KEY_braille_dots_137 0x1002845 #define CLUTTER_KEY_braille_dots_237 0x1002846 #define CLUTTER_KEY_braille_dots_1237 0x1002847 #define CLUTTER_KEY_braille_dots_47 0x1002848 #define CLUTTER_KEY_braille_dots_147 0x1002849 #define CLUTTER_KEY_braille_dots_247 0x100284a #define CLUTTER_KEY_braille_dots_1247 0x100284b #define CLUTTER_KEY_braille_dots_347 0x100284c #define CLUTTER_KEY_braille_dots_1347 0x100284d #define CLUTTER_KEY_braille_dots_2347 0x100284e #define CLUTTER_KEY_braille_dots_12347 0x100284f #define CLUTTER_KEY_braille_dots_57 0x1002850 #define CLUTTER_KEY_braille_dots_157 0x1002851 #define CLUTTER_KEY_braille_dots_257 0x1002852 #define CLUTTER_KEY_braille_dots_1257 0x1002853 #define CLUTTER_KEY_braille_dots_357 0x1002854 #define CLUTTER_KEY_braille_dots_1357 0x1002855 #define CLUTTER_KEY_braille_dots_2357 0x1002856 #define CLUTTER_KEY_braille_dots_12357 0x1002857 #define CLUTTER_KEY_braille_dots_457 0x1002858 #define CLUTTER_KEY_braille_dots_1457 0x1002859 #define CLUTTER_KEY_braille_dots_2457 0x100285a #define CLUTTER_KEY_braille_dots_12457 0x100285b #define CLUTTER_KEY_braille_dots_3457 0x100285c #define CLUTTER_KEY_braille_dots_13457 0x100285d #define CLUTTER_KEY_braille_dots_23457 0x100285e #define CLUTTER_KEY_braille_dots_123457 0x100285f #define CLUTTER_KEY_braille_dots_67 0x1002860 #define CLUTTER_KEY_braille_dots_167 0x1002861 #define CLUTTER_KEY_braille_dots_267 0x1002862 #define CLUTTER_KEY_braille_dots_1267 0x1002863 #define CLUTTER_KEY_braille_dots_367 0x1002864 #define CLUTTER_KEY_braille_dots_1367 0x1002865 #define CLUTTER_KEY_braille_dots_2367 0x1002866 #define CLUTTER_KEY_braille_dots_12367 0x1002867 #define CLUTTER_KEY_braille_dots_467 0x1002868 #define CLUTTER_KEY_braille_dots_1467 0x1002869 #define CLUTTER_KEY_braille_dots_2467 0x100286a #define CLUTTER_KEY_braille_dots_12467 0x100286b #define CLUTTER_KEY_braille_dots_3467 0x100286c #define CLUTTER_KEY_braille_dots_13467 0x100286d #define CLUTTER_KEY_braille_dots_23467 0x100286e #define CLUTTER_KEY_braille_dots_123467 0x100286f #define CLUTTER_KEY_braille_dots_567 0x1002870 #define CLUTTER_KEY_braille_dots_1567 0x1002871 #define CLUTTER_KEY_braille_dots_2567 0x1002872 #define CLUTTER_KEY_braille_dots_12567 0x1002873 #define CLUTTER_KEY_braille_dots_3567 0x1002874 #define CLUTTER_KEY_braille_dots_13567 0x1002875 #define CLUTTER_KEY_braille_dots_23567 0x1002876 #define CLUTTER_KEY_braille_dots_123567 0x1002877 #define CLUTTER_KEY_braille_dots_4567 0x1002878 #define CLUTTER_KEY_braille_dots_14567 0x1002879 #define CLUTTER_KEY_braille_dots_24567 0x100287a #define CLUTTER_KEY_braille_dots_124567 0x100287b #define CLUTTER_KEY_braille_dots_34567 0x100287c #define CLUTTER_KEY_braille_dots_134567 0x100287d #define CLUTTER_KEY_braille_dots_234567 0x100287e #define CLUTTER_KEY_braille_dots_1234567 0x100287f #define CLUTTER_KEY_braille_dots_8 0x1002880 #define CLUTTER_KEY_braille_dots_18 0x1002881 #define CLUTTER_KEY_braille_dots_28 0x1002882 #define CLUTTER_KEY_braille_dots_128 0x1002883 #define CLUTTER_KEY_braille_dots_38 0x1002884 #define CLUTTER_KEY_braille_dots_138 0x1002885 #define CLUTTER_KEY_braille_dots_238 0x1002886 #define CLUTTER_KEY_braille_dots_1238 0x1002887 #define CLUTTER_KEY_braille_dots_48 0x1002888 #define CLUTTER_KEY_braille_dots_148 0x1002889 #define CLUTTER_KEY_braille_dots_248 0x100288a #define CLUTTER_KEY_braille_dots_1248 0x100288b #define CLUTTER_KEY_braille_dots_348 0x100288c #define CLUTTER_KEY_braille_dots_1348 0x100288d #define CLUTTER_KEY_braille_dots_2348 0x100288e #define CLUTTER_KEY_braille_dots_12348 0x100288f #define CLUTTER_KEY_braille_dots_58 0x1002890 #define CLUTTER_KEY_braille_dots_158 0x1002891 #define CLUTTER_KEY_braille_dots_258 0x1002892 #define CLUTTER_KEY_braille_dots_1258 0x1002893 #define CLUTTER_KEY_braille_dots_358 0x1002894 #define CLUTTER_KEY_braille_dots_1358 0x1002895 #define CLUTTER_KEY_braille_dots_2358 0x1002896 #define CLUTTER_KEY_braille_dots_12358 0x1002897 #define CLUTTER_KEY_braille_dots_458 0x1002898 #define CLUTTER_KEY_braille_dots_1458 0x1002899 #define CLUTTER_KEY_braille_dots_2458 0x100289a #define CLUTTER_KEY_braille_dots_12458 0x100289b #define CLUTTER_KEY_braille_dots_3458 0x100289c #define CLUTTER_KEY_braille_dots_13458 0x100289d #define CLUTTER_KEY_braille_dots_23458 0x100289e #define CLUTTER_KEY_braille_dots_123458 0x100289f #define CLUTTER_KEY_braille_dots_68 0x10028a0 #define CLUTTER_KEY_braille_dots_168 0x10028a1 #define CLUTTER_KEY_braille_dots_268 0x10028a2 #define CLUTTER_KEY_braille_dots_1268 0x10028a3 #define CLUTTER_KEY_braille_dots_368 0x10028a4 #define CLUTTER_KEY_braille_dots_1368 0x10028a5 #define CLUTTER_KEY_braille_dots_2368 0x10028a6 #define CLUTTER_KEY_braille_dots_12368 0x10028a7 #define CLUTTER_KEY_braille_dots_468 0x10028a8 #define CLUTTER_KEY_braille_dots_1468 0x10028a9 #define CLUTTER_KEY_braille_dots_2468 0x10028aa #define CLUTTER_KEY_braille_dots_12468 0x10028ab #define CLUTTER_KEY_braille_dots_3468 0x10028ac #define CLUTTER_KEY_braille_dots_13468 0x10028ad #define CLUTTER_KEY_braille_dots_23468 0x10028ae #define CLUTTER_KEY_braille_dots_123468 0x10028af #define CLUTTER_KEY_braille_dots_568 0x10028b0 #define CLUTTER_KEY_braille_dots_1568 0x10028b1 #define CLUTTER_KEY_braille_dots_2568 0x10028b2 #define CLUTTER_KEY_braille_dots_12568 0x10028b3 #define CLUTTER_KEY_braille_dots_3568 0x10028b4 #define CLUTTER_KEY_braille_dots_13568 0x10028b5 #define CLUTTER_KEY_braille_dots_23568 0x10028b6 #define CLUTTER_KEY_braille_dots_123568 0x10028b7 #define CLUTTER_KEY_braille_dots_4568 0x10028b8 #define CLUTTER_KEY_braille_dots_14568 0x10028b9 #define CLUTTER_KEY_braille_dots_24568 0x10028ba #define CLUTTER_KEY_braille_dots_124568 0x10028bb #define CLUTTER_KEY_braille_dots_34568 0x10028bc #define CLUTTER_KEY_braille_dots_134568 0x10028bd #define CLUTTER_KEY_braille_dots_234568 0x10028be #define CLUTTER_KEY_braille_dots_1234568 0x10028bf #define CLUTTER_KEY_braille_dots_78 0x10028c0 #define CLUTTER_KEY_braille_dots_178 0x10028c1 #define CLUTTER_KEY_braille_dots_278 0x10028c2 #define CLUTTER_KEY_braille_dots_1278 0x10028c3 #define CLUTTER_KEY_braille_dots_378 0x10028c4 #define CLUTTER_KEY_braille_dots_1378 0x10028c5 #define CLUTTER_KEY_braille_dots_2378 0x10028c6 #define CLUTTER_KEY_braille_dots_12378 0x10028c7 #define CLUTTER_KEY_braille_dots_478 0x10028c8 #define CLUTTER_KEY_braille_dots_1478 0x10028c9 #define CLUTTER_KEY_braille_dots_2478 0x10028ca #define CLUTTER_KEY_braille_dots_12478 0x10028cb #define CLUTTER_KEY_braille_dots_3478 0x10028cc #define CLUTTER_KEY_braille_dots_13478 0x10028cd #define CLUTTER_KEY_braille_dots_23478 0x10028ce #define CLUTTER_KEY_braille_dots_123478 0x10028cf #define CLUTTER_KEY_braille_dots_578 0x10028d0 #define CLUTTER_KEY_braille_dots_1578 0x10028d1 #define CLUTTER_KEY_braille_dots_2578 0x10028d2 #define CLUTTER_KEY_braille_dots_12578 0x10028d3 #define CLUTTER_KEY_braille_dots_3578 0x10028d4 #define CLUTTER_KEY_braille_dots_13578 0x10028d5 #define CLUTTER_KEY_braille_dots_23578 0x10028d6 #define CLUTTER_KEY_braille_dots_123578 0x10028d7 #define CLUTTER_KEY_braille_dots_4578 0x10028d8 #define CLUTTER_KEY_braille_dots_14578 0x10028d9 #define CLUTTER_KEY_braille_dots_24578 0x10028da #define CLUTTER_KEY_braille_dots_124578 0x10028db #define CLUTTER_KEY_braille_dots_34578 0x10028dc #define CLUTTER_KEY_braille_dots_134578 0x10028dd #define CLUTTER_KEY_braille_dots_234578 0x10028de #define CLUTTER_KEY_braille_dots_1234578 0x10028df #define CLUTTER_KEY_braille_dots_678 0x10028e0 #define CLUTTER_KEY_braille_dots_1678 0x10028e1 #define CLUTTER_KEY_braille_dots_2678 0x10028e2 #define CLUTTER_KEY_braille_dots_12678 0x10028e3 #define CLUTTER_KEY_braille_dots_3678 0x10028e4 #define CLUTTER_KEY_braille_dots_13678 0x10028e5 #define CLUTTER_KEY_braille_dots_23678 0x10028e6 #define CLUTTER_KEY_braille_dots_123678 0x10028e7 #define CLUTTER_KEY_braille_dots_4678 0x10028e8 #define CLUTTER_KEY_braille_dots_14678 0x10028e9 #define CLUTTER_KEY_braille_dots_24678 0x10028ea #define CLUTTER_KEY_braille_dots_124678 0x10028eb #define CLUTTER_KEY_braille_dots_34678 0x10028ec #define CLUTTER_KEY_braille_dots_134678 0x10028ed #define CLUTTER_KEY_braille_dots_234678 0x10028ee #define CLUTTER_KEY_braille_dots_1234678 0x10028ef #define CLUTTER_KEY_braille_dots_5678 0x10028f0 #define CLUTTER_KEY_braille_dots_15678 0x10028f1 #define CLUTTER_KEY_braille_dots_25678 0x10028f2 #define CLUTTER_KEY_braille_dots_125678 0x10028f3 #define CLUTTER_KEY_braille_dots_35678 0x10028f4 #define CLUTTER_KEY_braille_dots_135678 0x10028f5 #define CLUTTER_KEY_braille_dots_235678 0x10028f6 #define CLUTTER_KEY_braille_dots_1235678 0x10028f7 #define CLUTTER_KEY_braille_dots_45678 0x10028f8 #define CLUTTER_KEY_braille_dots_145678 0x10028f9 #define CLUTTER_KEY_braille_dots_245678 0x10028fa #define CLUTTER_KEY_braille_dots_1245678 0x10028fb #define CLUTTER_KEY_braille_dots_345678 0x10028fc #define CLUTTER_KEY_braille_dots_1345678 0x10028fd #define CLUTTER_KEY_braille_dots_2345678 0x10028fe #define CLUTTER_KEY_braille_dots_12345678 0x10028ff #define CLUTTER_KEY_Sinh_ng 0x1000d82 #define CLUTTER_KEY_Sinh_h2 0x1000d83 #define CLUTTER_KEY_Sinh_a 0x1000d85 #define CLUTTER_KEY_Sinh_aa 0x1000d86 #define CLUTTER_KEY_Sinh_ae 0x1000d87 #define CLUTTER_KEY_Sinh_aee 0x1000d88 #define CLUTTER_KEY_Sinh_i 0x1000d89 #define CLUTTER_KEY_Sinh_ii 0x1000d8a #define CLUTTER_KEY_Sinh_u 0x1000d8b #define CLUTTER_KEY_Sinh_uu 0x1000d8c #define CLUTTER_KEY_Sinh_ri 0x1000d8d #define CLUTTER_KEY_Sinh_rii 0x1000d8e #define CLUTTER_KEY_Sinh_lu 0x1000d8f #define CLUTTER_KEY_Sinh_luu 0x1000d90 #define CLUTTER_KEY_Sinh_e 0x1000d91 #define CLUTTER_KEY_Sinh_ee 0x1000d92 #define CLUTTER_KEY_Sinh_ai 0x1000d93 #define CLUTTER_KEY_Sinh_o 0x1000d94 #define CLUTTER_KEY_Sinh_oo 0x1000d95 #define CLUTTER_KEY_Sinh_au 0x1000d96 #define CLUTTER_KEY_Sinh_ka 0x1000d9a #define CLUTTER_KEY_Sinh_kha 0x1000d9b #define CLUTTER_KEY_Sinh_ga 0x1000d9c #define CLUTTER_KEY_Sinh_gha 0x1000d9d #define CLUTTER_KEY_Sinh_ng2 0x1000d9e #define CLUTTER_KEY_Sinh_nga 0x1000d9f #define CLUTTER_KEY_Sinh_ca 0x1000da0 #define CLUTTER_KEY_Sinh_cha 0x1000da1 #define CLUTTER_KEY_Sinh_ja 0x1000da2 #define CLUTTER_KEY_Sinh_jha 0x1000da3 #define CLUTTER_KEY_Sinh_nya 0x1000da4 #define CLUTTER_KEY_Sinh_jnya 0x1000da5 #define CLUTTER_KEY_Sinh_nja 0x1000da6 #define CLUTTER_KEY_Sinh_tta 0x1000da7 #define CLUTTER_KEY_Sinh_ttha 0x1000da8 #define CLUTTER_KEY_Sinh_dda 0x1000da9 #define CLUTTER_KEY_Sinh_ddha 0x1000daa #define CLUTTER_KEY_Sinh_nna 0x1000dab #define CLUTTER_KEY_Sinh_ndda 0x1000dac #define CLUTTER_KEY_Sinh_tha 0x1000dad #define CLUTTER_KEY_Sinh_thha 0x1000dae #define CLUTTER_KEY_Sinh_dha 0x1000daf #define CLUTTER_KEY_Sinh_dhha 0x1000db0 #define CLUTTER_KEY_Sinh_na 0x1000db1 #define CLUTTER_KEY_Sinh_ndha 0x1000db3 #define CLUTTER_KEY_Sinh_pa 0x1000db4 #define CLUTTER_KEY_Sinh_pha 0x1000db5 #define CLUTTER_KEY_Sinh_ba 0x1000db6 #define CLUTTER_KEY_Sinh_bha 0x1000db7 #define CLUTTER_KEY_Sinh_ma 0x1000db8 #define CLUTTER_KEY_Sinh_mba 0x1000db9 #define CLUTTER_KEY_Sinh_ya 0x1000dba #define CLUTTER_KEY_Sinh_ra 0x1000dbb #define CLUTTER_KEY_Sinh_la 0x1000dbd #define CLUTTER_KEY_Sinh_va 0x1000dc0 #define CLUTTER_KEY_Sinh_sha 0x1000dc1 #define CLUTTER_KEY_Sinh_ssha 0x1000dc2 #define CLUTTER_KEY_Sinh_sa 0x1000dc3 #define CLUTTER_KEY_Sinh_ha 0x1000dc4 #define CLUTTER_KEY_Sinh_lla 0x1000dc5 #define CLUTTER_KEY_Sinh_fa 0x1000dc6 #define CLUTTER_KEY_Sinh_al 0x1000dca #define CLUTTER_KEY_Sinh_aa2 0x1000dcf #define CLUTTER_KEY_Sinh_ae2 0x1000dd0 #define CLUTTER_KEY_Sinh_aee2 0x1000dd1 #define CLUTTER_KEY_Sinh_i2 0x1000dd2 #define CLUTTER_KEY_Sinh_ii2 0x1000dd3 #define CLUTTER_KEY_Sinh_u2 0x1000dd4 #define CLUTTER_KEY_Sinh_uu2 0x1000dd6 #define CLUTTER_KEY_Sinh_ru2 0x1000dd8 #define CLUTTER_KEY_Sinh_e2 0x1000dd9 #define CLUTTER_KEY_Sinh_ee2 0x1000dda #define CLUTTER_KEY_Sinh_ai2 0x1000ddb #define CLUTTER_KEY_Sinh_o2 0x1000ddc #define CLUTTER_KEY_Sinh_oo2 0x1000ddd #define CLUTTER_KEY_Sinh_au2 0x1000dde #define CLUTTER_KEY_Sinh_lu2 0x1000ddf #define CLUTTER_KEY_Sinh_ruu2 0x1000df2 #define CLUTTER_KEY_Sinh_luu2 0x1000df3 #define CLUTTER_KEY_Sinh_kunddaliya 0x1000df4 #define CLUTTER_KEY_ModeLock 0x1008ff01 #define CLUTTER_KEY_MonBrightnessUp 0x1008ff02 #define CLUTTER_KEY_MonBrightnessDown 0x1008ff03 #define CLUTTER_KEY_KbdLightOnOff 0x1008ff04 #define CLUTTER_KEY_KbdBrightnessUp 0x1008ff05 #define CLUTTER_KEY_KbdBrightnessDown 0x1008ff06 #define CLUTTER_KEY_Standby 0x1008ff10 #define CLUTTER_KEY_AudioLowerVolume 0x1008ff11 #define CLUTTER_KEY_AudioMute 0x1008ff12 #define CLUTTER_KEY_AudioRaiseVolume 0x1008ff13 #define CLUTTER_KEY_AudioPlay 0x1008ff14 #define CLUTTER_KEY_AudioStop 0x1008ff15 #define CLUTTER_KEY_AudioPrev 0x1008ff16 #define CLUTTER_KEY_AudioNext 0x1008ff17 #define CLUTTER_KEY_HomePage 0x1008ff18 #define CLUTTER_KEY_Mail 0x1008ff19 #define CLUTTER_KEY_Start 0x1008ff1a #define CLUTTER_KEY_Search 0x1008ff1b #define CLUTTER_KEY_AudioRecord 0x1008ff1c #define CLUTTER_KEY_Calculator 0x1008ff1d #define CLUTTER_KEY_Memo 0x1008ff1e #define CLUTTER_KEY_ToDoList 0x1008ff1f #define CLUTTER_KEY_Calendar 0x1008ff20 #define CLUTTER_KEY_PowerDown 0x1008ff21 #define CLUTTER_KEY_ContrastAdjust 0x1008ff22 #define CLUTTER_KEY_RockerUp 0x1008ff23 #define CLUTTER_KEY_RockerDown 0x1008ff24 #define CLUTTER_KEY_RockerEnter 0x1008ff25 #define CLUTTER_KEY_Back 0x1008ff26 #define CLUTTER_KEY_Forward 0x1008ff27 #define CLUTTER_KEY_Stop 0x1008ff28 #define CLUTTER_KEY_Refresh 0x1008ff29 #define CLUTTER_KEY_PowerOff 0x1008ff2a #define CLUTTER_KEY_WakeUp 0x1008ff2b #define CLUTTER_KEY_Eject 0x1008ff2c #define CLUTTER_KEY_ScreenSaver 0x1008ff2d #define CLUTTER_KEY_WWW 0x1008ff2e #define CLUTTER_KEY_Sleep 0x1008ff2f #define CLUTTER_KEY_Favorites 0x1008ff30 #define CLUTTER_KEY_AudioPause 0x1008ff31 #define CLUTTER_KEY_AudioMedia 0x1008ff32 #define CLUTTER_KEY_MyComputer 0x1008ff33 #define CLUTTER_KEY_VendorHome 0x1008ff34 #define CLUTTER_KEY_LightBulb 0x1008ff35 #define CLUTTER_KEY_Shop 0x1008ff36 #define CLUTTER_KEY_History 0x1008ff37 #define CLUTTER_KEY_OpenURL 0x1008ff38 #define CLUTTER_KEY_AddFavorite 0x1008ff39 #define CLUTTER_KEY_HotLinks 0x1008ff3a #define CLUTTER_KEY_BrightnessAdjust 0x1008ff3b #define CLUTTER_KEY_Finance 0x1008ff3c #define CLUTTER_KEY_Community 0x1008ff3d #define CLUTTER_KEY_AudioRewind 0x1008ff3e #define CLUTTER_KEY_BackForward 0x1008ff3f #define CLUTTER_KEY_Launch0 0x1008ff40 #define CLUTTER_KEY_Launch1 0x1008ff41 #define CLUTTER_KEY_Launch2 0x1008ff42 #define CLUTTER_KEY_Launch3 0x1008ff43 #define CLUTTER_KEY_Launch4 0x1008ff44 #define CLUTTER_KEY_Launch5 0x1008ff45 #define CLUTTER_KEY_Launch6 0x1008ff46 #define CLUTTER_KEY_Launch7 0x1008ff47 #define CLUTTER_KEY_Launch8 0x1008ff48 #define CLUTTER_KEY_Launch9 0x1008ff49 #define CLUTTER_KEY_LaunchA 0x1008ff4a #define CLUTTER_KEY_LaunchB 0x1008ff4b #define CLUTTER_KEY_LaunchC 0x1008ff4c #define CLUTTER_KEY_LaunchD 0x1008ff4d #define CLUTTER_KEY_LaunchE 0x1008ff4e #define CLUTTER_KEY_LaunchF 0x1008ff4f #define CLUTTER_KEY_ApplicationLeft 0x1008ff50 #define CLUTTER_KEY_ApplicationRight 0x1008ff51 #define CLUTTER_KEY_Book 0x1008ff52 #define CLUTTER_KEY_CD 0x1008ff53 #define CLUTTER_KEY_WindowClear 0x1008ff55 #define CLUTTER_KEY_Close 0x1008ff56 #define CLUTTER_KEY_Copy 0x1008ff57 #define CLUTTER_KEY_Cut 0x1008ff58 #define CLUTTER_KEY_Display 0x1008ff59 #define CLUTTER_KEY_DOS 0x1008ff5a #define CLUTTER_KEY_Documents 0x1008ff5b #define CLUTTER_KEY_Excel 0x1008ff5c #define CLUTTER_KEY_Explorer 0x1008ff5d #define CLUTTER_KEY_Game 0x1008ff5e #define CLUTTER_KEY_Go 0x1008ff5f #define CLUTTER_KEY_iTouch 0x1008ff60 #define CLUTTER_KEY_LogOff 0x1008ff61 #define CLUTTER_KEY_Market 0x1008ff62 #define CLUTTER_KEY_Meeting 0x1008ff63 #define CLUTTER_KEY_MenuKB 0x1008ff65 #define CLUTTER_KEY_MenuPB 0x1008ff66 #define CLUTTER_KEY_MySites 0x1008ff67 #define CLUTTER_KEY_New 0x1008ff68 #define CLUTTER_KEY_News 0x1008ff69 #define CLUTTER_KEY_OfficeHome 0x1008ff6a #define CLUTTER_KEY_Open 0x1008ff6b #define CLUTTER_KEY_Option 0x1008ff6c #define CLUTTER_KEY_Paste 0x1008ff6d #define CLUTTER_KEY_Phone 0x1008ff6e #define CLUTTER_KEY_Reply 0x1008ff72 #define CLUTTER_KEY_Reload 0x1008ff73 #define CLUTTER_KEY_RotateWindows 0x1008ff74 #define CLUTTER_KEY_RotationPB 0x1008ff75 #define CLUTTER_KEY_RotationKB 0x1008ff76 #define CLUTTER_KEY_Save 0x1008ff77 #define CLUTTER_KEY_ScrollUp 0x1008ff78 #define CLUTTER_KEY_ScrollDown 0x1008ff79 #define CLUTTER_KEY_ScrollClick 0x1008ff7a #define CLUTTER_KEY_Send 0x1008ff7b #define CLUTTER_KEY_Spell 0x1008ff7c #define CLUTTER_KEY_SplitScreen 0x1008ff7d #define CLUTTER_KEY_Support 0x1008ff7e #define CLUTTER_KEY_TaskPane 0x1008ff7f #define CLUTTER_KEY_Terminal 0x1008ff80 #define CLUTTER_KEY_Tools 0x1008ff81 #define CLUTTER_KEY_Travel 0x1008ff82 #define CLUTTER_KEY_UserPB 0x1008ff84 #define CLUTTER_KEY_User1KB 0x1008ff85 #define CLUTTER_KEY_User2KB 0x1008ff86 #define CLUTTER_KEY_Video 0x1008ff87 #define CLUTTER_KEY_WheelButton 0x1008ff88 #define CLUTTER_KEY_Word 0x1008ff89 #define CLUTTER_KEY_Xfer 0x1008ff8a #define CLUTTER_KEY_ZoomIn 0x1008ff8b #define CLUTTER_KEY_ZoomOut 0x1008ff8c #define CLUTTER_KEY_Away 0x1008ff8d #define CLUTTER_KEY_Messenger 0x1008ff8e #define CLUTTER_KEY_WebCam 0x1008ff8f #define CLUTTER_KEY_MailForward 0x1008ff90 #define CLUTTER_KEY_Pictures 0x1008ff91 #define CLUTTER_KEY_Music 0x1008ff92 #define CLUTTER_KEY_Battery 0x1008ff93 #define CLUTTER_KEY_Bluetooth 0x1008ff94 #define CLUTTER_KEY_WLAN 0x1008ff95 #define CLUTTER_KEY_UWB 0x1008ff96 #define CLUTTER_KEY_AudioForward 0x1008ff97 #define CLUTTER_KEY_AudioRepeat 0x1008ff98 #define CLUTTER_KEY_AudioRandomPlay 0x1008ff99 #define CLUTTER_KEY_Subtitle 0x1008ff9a #define CLUTTER_KEY_AudioCycleTrack 0x1008ff9b #define CLUTTER_KEY_CycleAngle 0x1008ff9c #define CLUTTER_KEY_FrameBack 0x1008ff9d #define CLUTTER_KEY_FrameForward 0x1008ff9e #define CLUTTER_KEY_Time 0x1008ff9f #define CLUTTER_KEY_SelectButton 0x1008ffa0 #define CLUTTER_KEY_View 0x1008ffa1 #define CLUTTER_KEY_TopMenu 0x1008ffa2 #define CLUTTER_KEY_Red 0x1008ffa3 #define CLUTTER_KEY_Green 0x1008ffa4 #define CLUTTER_KEY_Yellow 0x1008ffa5 #define CLUTTER_KEY_Blue 0x1008ffa6 #define CLUTTER_KEY_Suspend 0x1008ffa7 #define CLUTTER_KEY_Hibernate 0x1008ffa8 #define CLUTTER_KEY_TouchpadToggle 0x1008ffa9 #define CLUTTER_KEY_TouchpadOn 0x1008ffb0 #define CLUTTER_KEY_TouchpadOff 0x1008ffb1 #define CLUTTER_KEY_AudioMicMute 0x1008ffb2 #define CLUTTER_KEY_Switch_VT_1 0x1008fe01 #define CLUTTER_KEY_Switch_VT_2 0x1008fe02 #define CLUTTER_KEY_Switch_VT_3 0x1008fe03 #define CLUTTER_KEY_Switch_VT_4 0x1008fe04 #define CLUTTER_KEY_Switch_VT_5 0x1008fe05 #define CLUTTER_KEY_Switch_VT_6 0x1008fe06 #define CLUTTER_KEY_Switch_VT_7 0x1008fe07 #define CLUTTER_KEY_Switch_VT_8 0x1008fe08 #define CLUTTER_KEY_Switch_VT_9 0x1008fe09 #define CLUTTER_KEY_Switch_VT_10 0x1008fe0a #define CLUTTER_KEY_Switch_VT_11 0x1008fe0b #define CLUTTER_KEY_Switch_VT_12 0x1008fe0c #define CLUTTER_KEY_Ungrab 0x1008fe20 #define CLUTTER_KEY_ClearGrab 0x1008fe21 #define CLUTTER_KEY_Next_VMode 0x1008fe22 #define CLUTTER_KEY_Prev_VMode 0x1008fe23 #define CLUTTER_KEY_LogWindowTree 0x1008fe24 #define CLUTTER_KEY_LogGrabInfo 0x1008fe25 #endif /* __CLUTTER_KEYSYMS_H__ */ muffin-6.4.1/.github/0000775000175000017500000000000014723361714013302 5ustar fabiofabiomuffin-6.4.1/.github/workflows/0000775000175000017500000000000014723361714015337 5ustar fabiofabiomuffin-6.4.1/.github/workflows/build.yml0000664000175000017500000000112314723361714017156 0ustar fabiofabioname: Build on: push: branches: - master pull_request: branches: - master workflow_dispatch: inputs: debug_enabled: type: boolean description: 'Start an SSH server on failure.' required: false default: false jobs: build: uses: linuxmint/github-actions/.github/workflows/do-builds.yml@master with: commit_id: master ############################## Comma separated list - like 'linuxmint/xapp, linuxmint/cinnamon-desktop' dependencies: linuxmint/cinnamon-desktop ############################## muffin-6.4.1/.github/ISSUE_TEMPLATE/0000775000175000017500000000000014723361714015465 5ustar fabiofabiomuffin-6.4.1/.github/ISSUE_TEMPLATE/bug_report.yaml0000664000175000017500000000744414723361714020532 0ustar fabiofabioname: Bug Report description: File a bug report body: - type: markdown attributes: value: | ### STOP! Before continuing: - Please try searching for [existing reports](https://github.com/linuxmint/muffin/issues?q=is%3Aissue+state%3Aall) that may match the behavior you're seeing. *If you find one, add to that issue, instead of opening a new one*. Include any relevant details that may differ from the original author's. - If this is related to Cinnamon, sometimes third-party xlets (applets, desklets and extensions) can cause erroneous or unexpected behavior. Try disabling these first to see if the behavior goes away. After disabling, be sure to restart Cinnamon before continuing. Right-click an empty portion of the panel, select Troubleshooting, then 'Restart Cinnamon'. Alternately, you can open the run dialog - Alt-F2, type 'r' or 'restart', and enter. - Check your logs - there may be some obvious cause for your trouble - in Linux Mint you can check ~/.xsession-errors (a hidden file in your Home folder). - If this is a *crash*, provide information about it if possible (CoreDump file, stack trace, etc...). In Linux Mint you can check the System Reports program. - Have you tried rebooting? --- Thank you for taking the time to report this issue. To allow us to work as efficiently as possible at resolving this, we need some information from you. - type: input id: distro attributes: label: Distribution description: Which Linux distribution are you using? Please be as specific as possible. placeholder: "example: Mint 21.1" validations: required: true - type: input id: pkgver attributes: label: Package version description: Please provide the Cinnamon version. You can get this by running `cinnamon --version` in a terminal, or launching 'System Info" from the menu. placeholder: "example: 5.6.1" validations: required: true - type: input id: graphics attributes: label: Graphics hardware in use description: Please provide information about your graphics hardware, if known. If you are using a dual-gpu system please specify that also. placeholder: "example: NVIDIA GeForce GTX 1660 TI" validations: required: false - type: dropdown id: frequency attributes: label: Frequency description: How often does this behavior occur? options: - Always - Quite often - Only occasionally validations: required: true - type: textarea id: current-behavior attributes: label: Bug description description: Please describe what is happening validations: required: true - type: textarea id: steps attributes: label: Steps to reproduce description: Please try to provide **detailed** steps on the most direct way to reproduce this issue. The chances of a bug being fixed go up **considerably** if we are able to duplicate the behavior ourselves. validations: required: true - type: textarea id: expected-behavior attributes: label: Expected behavior description: Describe what you think should happen instead of the current behavior. validations: required: true - type: textarea id: more-info attributes: label: Additional information description: You can add any other information you think may be relevant. validations: required: false - type: markdown attributes: value: | #### By submitting this report you agree to behave respectfully and in a mature manner. If in doubt, refer to the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule) and [Github's Community Guidelines](https://docs.github.com/en/site-policy/github-terms/github-community-guidelines). muffin-6.4.1/.github/ISSUE_TEMPLATE/feature_request.md0000664000175000017500000000042014723361714021206 0ustar fabiofabio--- name: Feature request (OBSOLETE) about: Feature requests are no longer accepted here, please use https://github.com/orgs/linuxmint/discussions instead. title: "" labels: ["FEATURE REQUEST"] assignees: '' --- Please use: https://github.com/orgs/linuxmint/discussionsmuffin-6.4.1/cogl/0000775000175000017500000000000014723361714012666 5ustar fabiofabiomuffin-6.4.1/cogl/cogl-mutter-config.h.in0000664000175000017500000000021414723361714017146 0ustar fabiofabio/* Have GL for rendering */ #undef HAVE_COGL_GL /* Have GLES 2.0 for rendering */ #undef HAVE_COGL_GLES2 #define COGL_CONFIG_H_INCLUDED 1 muffin-6.4.1/cogl/meson.build0000664000175000017500000000423514723361714015034 0ustar fabiofabiocogl_includesubdir = join_paths(pkgname, 'cogl') cogl_includedir = join_paths(includedir, cogl_includesubdir) cogl_srcdir = meson.current_source_dir() cogl_includepath = include_directories('.', 'cogl') cdata = configuration_data() cdata.set('HAVE_COGL_GL', have_gl) cdata.set('HAVE_COGL_GLES2', have_gles2) cdata.set('HAVE_TRACING', have_profiler) cdata.set('ENABLE_UNIT_TESTS', have_cogl_tests) if default_driver != 'auto' cdata.set_quoted('COGL_DEFAULT_DRIVER', default_driver) endif cogl_config_h = configure_file( input: 'cogl-config.h.meson', output: 'cogl-config.h', configuration: cdata) cogl_pkg_deps = [ glib_dep, gobject_dep, graphene_dep, ] cogl_pkg_private_deps = [ cairo_dep, gmodule_no_export_dep, gdk_pixbuf_dep, #uprof_dep, ] if have_profiler cogl_pkg_private_deps += [ sysprof_dep, ] endif if have_wayland cogl_pkg_deps += [ wayland_server_dep, ] endif if have_egl cogl_pkg_deps += [ egl_dep, ] endif if have_x11 cogl_pkg_deps += [ x11_dep, ] cogl_pkg_private_deps += [ xext_dep, xfixes_dep, xdamage_dep, xcomposite_dep, xrandr_dep, ] endif if have_gl cogl_pkg_deps += [ gl_dep, ] endif if have_gles2 cogl_pkg_deps += [ gles2_dep, ] endif cogl_deps = [ cogl_pkg_deps, cogl_pkg_private_deps, m_dep, ] cogl_c_args = [ '-DCOGL_LOCALEDIR="@0@"'.format(localedir), '-DCOGL_COMPILATION', ] if have_gl cogl_c_args += [ '-DCOGL_GL_LIBNAME="@0@"'.format(gl_libname) ] endif if have_gles2 cogl_c_args += [ '-DCOGL_GLES2_LIBNAME="@0@"'.format(gles2_libname) ] endif cogl_debug_c_args = [] buildtype = get_option('buildtype') if get_option('debug') cogl_debug_c_args += [ '-DCOGL_GL_DEBUG', '-DCOGL_OBJECT_DEBUG', '-DCOGL_ENABLE_DEBUG', '-fno-omit-frame-pointer' ] elif buildtype != 'plain' cogl_debug_c_args += [ '-DG_DISABLE_CHECKS', '-DG_DISABLE_CAST_CHECKS' ] endif supported_cogl_debug_c_args = cc.get_supported_arguments(cogl_debug_c_args) cogl_c_args += cogl_debug_c_args if have_cogl_tests subdir('test-fixtures') endif subdir('cogl') subdir('cogl-path') subdir('cogl-pango') if have_cogl_tests subdir('tests') endif muffin-6.4.1/cogl/cogl-config.h.meson0000664000175000017500000000052414723361714016347 0ustar fabiofabio/* Have GL for rendering */ #mesondefine HAVE_COGL_GL /* Have GLES 2.0 for rendering */ #mesondefine HAVE_COGL_GLES2 /* Building with Sysprof profiling suport */ #mesondefine HAVE_TRACING /* Enable unit tests */ #mesondefine ENABLE_UNIT_TESTS /* Default COGL driver */ #mesondefine COGL_DEFAULT_DRIVER #define COGL_CONFIG_H_INCLUDED 1 muffin-6.4.1/cogl/.gitignore0000664000175000017500000000267314723361714014666 0ustar fabiofabioABOUT-NLS INSTALL Makefile Makefile.in aclocal.m4 autom4te.cache compile *.pc .deps .libs .dirstamp *.o *.lo *.la *.gcov *.exe /README stamp-enum-types stamp-marshal /build/autotools/*.m4 /build/win32/*.bat !/build/autotools/acglib.m4 !/build/autotools/introspection.m4 !/build/autotools/as-glibconfig.m4 !/build/autotools/as-linguas.m4 !/build/autotools/as-compiler-flag.m4 /build/config.guess /build/config.rpath /build/config.sub *.gir *.typelib cogl-pango.rc cogl.rc cogl-defines.h cogl-egl-defines.h cogl-enum-types.c cogl-enum-types.h cogl-gl-header.h cogl-path-enum-types.c cogl-path-enum-types.h cogl-config.h cogl-config.h.in cogl-mutter-config.h config.log config.lt config.status configure depcomp /deps/glib/glibconfig.h /deps/gmodule/gmoduleconf.h /doc/reference/cogl/cogl-docs.xml /doc/reference/cogl-2.0-experimental/cogl-2.0-experimental-docs.xml /doc/reference/cogl-gst/cogl-gst-docs.xml gtk-doc.make install-sh libtool ltmain.sh missing mkinstalldirs stamp-h1 TAGS /tests/tools/disable-npots.sh /tests/conform/test-launcher.sh /tests/interactive/wrapper.sh /tests/conform/*.bat /tests/conform/config.env /tests/conform/.log /tests/unit/.log /tests/config.env /po/POTFILES /po/*.gmo /po/Makefile.in.in /po/Makevars.template /po/Rules-quot /po/boldquot.sed /po/en@boldquot.header /po/en@quot.header /po/insert-header.sin /po/quot.sed /po/remove-potcdate.sin /po/remove-potcdate.sed /po/stamp-po *.swn *.swo *.swp *~ *.orig *.rej .DS_Store .testlogs-* muffin-6.4.1/cogl/cogl/0000775000175000017500000000000014723361714013612 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/cogl-profile.h0000664000175000017500000000430214723361714016344 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PROFILE_H__ #define __COGL_PROFILE_H__ #ifdef COGL_ENABLE_PROFILE #include extern UProfContext *_cogl_uprof_context; #define COGL_STATIC_TIMER UPROF_STATIC_TIMER #define COGL_STATIC_COUNTER UPROF_STATIC_COUNTER #define COGL_COUNTER_INC UPROF_COUNTER_INC #define COGL_COUNTER_DEC UPROF_COUNTER_DEC #define COGL_TIMER_START UPROF_TIMER_START #define COGL_TIMER_STOP UPROF_TIMER_STOP void _cogl_uprof_init (void); COGL_EXPORT void _cogl_profile_trace_message (const char *format, ...); #else #define COGL_STATIC_TIMER(A,B,C,D,E) G_STMT_START{ (void)0; }G_STMT_END #define COGL_STATIC_COUNTER(A,B,C,D) G_STMT_START{ (void)0; }G_STMT_END #define COGL_COUNTER_INC(A,B) G_STMT_START{ (void)0; }G_STMT_END #define COGL_COUNTER_DEC(A,B) G_STMT_START{ (void)0; }G_STMT_END #define COGL_TIMER_START(A,B) G_STMT_START{ (void)0; }G_STMT_END #define COGL_TIMER_STOP(A,B) G_STMT_START{ (void)0; }G_STMT_END #define _cogl_profile_trace_message g_message #endif #endif /* __COGL_PROFILE_H__ */ muffin-6.4.1/cogl/cogl/cogl-index-buffer.h0000664000175000017500000000550114723361714017264 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_INDEX_BUFFER_H__ #define __COGL_INDEX_BUFFER_H__ #include #include G_BEGIN_DECLS /** * SECTION:cogl-index-buffer * @short_description: Functions for creating and manipulating vertex * indices. * * FIXME */ #define COGL_INDEX_BUFFER(buffer) ((CoglIndexBuffer*) buffer) typedef struct _CoglIndexBuffer CoglIndexBuffer; /** * cogl_index_buffer_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_index_buffer_get_gtype (void); /** * cogl_index_buffer_new: * @context: A #CoglContext * @bytes: The number of bytes to allocate for vertex attribute data. * * Declares a new #CoglIndexBuffer of @size bytes to contain vertex * indices. Once declared, data can be set using * cogl_buffer_set_data() or by mapping it into the application's * address space using cogl_buffer_map(). * * Return value: (transfer full): A newly allocated #CoglIndexBuffer * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT CoglIndexBuffer * cogl_index_buffer_new (CoglContext *context, size_t bytes); /** * cogl_is_index_buffer: * @object: A #CoglObject * * Gets whether the given object references a #CoglIndexBuffer. * * Returns: %TRUE if the @object references a #CoglIndexBuffer, * %FALSE otherwise * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_index_buffer (void *object); G_END_DECLS #endif /* __COGL_INDEX_BUFFER_H__ */ muffin-6.4.1/cogl/cogl/cogl-gtype-private.h0000664000175000017500000003534314723361714017515 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_GTYPE_PRIVATE_H__ #define __COGL_GTYPE_PRIVATE_H__ #include "cogl-config.h" #include #include #include "cogl-object-private.h" /* Move this to public headers? */ typedef struct _CoglGtypeObject CoglGtypeObject; typedef struct _CoglGtypeClass CoglGtypeClass; struct _CoglGtypeObject { GTypeInstance parent_instance; guint dummy; }; struct _CoglGtypeClass { GTypeClass base_class; guint dummy; }; #define I_(str) (g_intern_static_string ((str))) /**/ #define COGL_GTYPE_DEFINE_BOXED(Name,underscore_name,copy_func,free_func) \ GType \ cogl_##underscore_name##_get_gtype (void) \ { \ static gsize g_type_id = 0; \ if (g_once_init_enter (&g_type_id)) \ { \ GType type = \ g_boxed_type_register_static (g_intern_static_string (I_("Cogl" # Name)), \ (GBoxedCopyFunc)copy_func, \ (GBoxedFreeFunc)free_func); \ g_once_init_leave (&g_type_id, type); \ } \ return g_type_id; \ } #define COGL_GTYPE_IMPLEMENT_INTERFACE(name) { \ const GInterfaceInfo g_implement_interface_info = { \ (GInterfaceInitFunc) _cogl_gtype_dummy_iface_init, NULL, NULL \ }; \ g_type_add_interface_static (fundamental_type_id, \ cogl_##name##_get_gtype(), \ &g_implement_interface_info); \ } #define _COGL_GTYPE_DEFINE_BASE_CLASS_BEGIN(Name,name) \ GType \ cogl_##name##_get_gtype (void) \ { \ static gsize g_type_id = 0; \ if (g_once_init_enter (&g_type_id)) \ { \ static const GTypeFundamentalInfo finfo = { \ (G_TYPE_FLAG_CLASSED | \ G_TYPE_FLAG_INSTANTIATABLE | \ G_TYPE_FLAG_DERIVABLE | \ G_TYPE_FLAG_DEEP_DERIVABLE), \ }; \ static const GTypeValueTable value_table = { \ _cogl_gtype_object_init_value, \ _cogl_gtype_object_free_value, \ _cogl_gtype_object_copy_value, \ _cogl_gtype_object_peek_pointer, \ "p", \ _cogl_gtype_object_collect_value, \ "p", \ _cogl_gtype_object_lcopy_value, \ }; \ const GTypeInfo node_info = { \ sizeof (CoglObjectClass), \ (GBaseInitFunc) _cogl_gtype_object_class_base_init, \ (GBaseFinalizeFunc) _cogl_gtype_object_class_base_finalize, \ (GClassInitFunc) _cogl_gtype_object_class_init, \ (GClassFinalizeFunc) NULL, \ NULL, \ sizeof (CoglObject), \ 0, \ (GInstanceInitFunc) _cogl_gtype_object_init, \ &value_table, \ }; \ GType fundamental_type_id = \ g_type_register_fundamental (g_type_fundamental_next (), \ I_("Cogl" # Name), \ &node_info, &finfo, \ G_TYPE_FLAG_ABSTRACT); \ g_once_init_leave (&g_type_id, \ fundamental_type_id); #define _COGL_GTYPE_DEFINE_BASE_CLASS_END() \ } \ return g_type_id; \ } #define COGL_GTYPE_DEFINE_BASE_CLASS(Name,name,...) \ _COGL_GTYPE_DEFINE_BASE_CLASS_BEGIN(Name,name) \ {__VA_ARGS__;} \ _COGL_GTYPE_DEFINE_BASE_CLASS_END() #define _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_BEGIN(Name,name) \ \ static void name##_default_init (Name##Interface *klass); \ GType \ name##_get_gtype (void) \ { \ static gsize g_type_id = 0; \ if (g_once_init_enter (&g_type_id)) \ { \ GType fundamental_type_id = \ g_type_register_static_simple (G_TYPE_INTERFACE, \ g_intern_static_string (#Name), \ sizeof (Name##Interface), \ (GClassInitFunc)name##_default_init, \ 0, \ (GInstanceInitFunc)NULL, \ (GTypeFlags) 0); \ g_type_interface_add_prerequisite (fundamental_type_id, \ cogl_object_get_gtype()); \ { /* custom code follows */ #define _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_END() \ /* following custom code */ \ } \ g_once_init_leave (&g_type_id, \ fundamental_type_id); \ } \ return g_type_id; \ } /* closes name##_get_type() */ #define COGL_GTYPE_DEFINE_INTERFACE(Name,name) \ typedef struct _Cogl##Name##Iface Cogl##Name##Iface; \ typedef Cogl##Name##Iface Cogl##Name##Interface; \ struct _Cogl##Name##Iface \ { \ /*< private >*/ \ GTypeInterface g_iface; \ }; \ _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_BEGIN (Cogl##Name, cogl_##name) \ _COGL_GTYPE_DEFINE_INTERFACE_EXTENDED_END () \ static void \ cogl_##name##_default_init (Cogl##Name##Interface *iface) \ { \ } #define _COGL_GTYPE_DEFINE_TYPE_EXTENDED_BEGIN(Name,name,parent,flags) \ \ static void name##_init (Name *self); \ static void name##_class_init (Name##Class *klass); \ static gpointer name##_parent_class = NULL; \ static gint Name##_private_offset; \ \ static void \ name##_class_intern_init (gpointer klass) \ { \ name##_parent_class = g_type_class_peek_parent (klass); \ name##_class_init ((Name##Class*) klass); \ } \ \ static inline gpointer \ name##_get_instance_private (Name *self) \ { \ return (G_STRUCT_MEMBER_P (self, Name ##_private_offset)); \ } \ \ GType \ name##_get_gtype (void) \ { \ static gsize g_type_id = 0; \ if (g_once_init_enter (&g_type_id)) \ { \ GType fundamental_type_id = \ g_type_register_static_simple (parent, \ g_intern_static_string (#Name), \ sizeof (Name##Class), \ (GClassInitFunc) name##_class_intern_init, \ sizeof (Name), \ (GInstanceInitFunc) name##_init, \ (GTypeFlags) flags); \ { /* custom code follows */ #define _COGL_GTYPE_DEFINE_TYPE_EXTENDED_END() \ /* following custom code */ \ } \ g_once_init_leave (&g_type_id, \ fundamental_type_id); \ } \ return g_type_id; \ } /* closes name##_get_type() */ #define COGL_GTYPE_DEFINE_CLASS(Name,name,...) \ typedef struct _Cogl##Name##Class Cogl##Name##Class; \ struct _Cogl##Name##Class { \ CoglObjectClass parent_class; \ }; \ _COGL_GTYPE_DEFINE_TYPE_EXTENDED_BEGIN(Cogl##Name, \ cogl_##name, \ cogl_object_get_gtype(), \ 0) \ {__VA_ARGS__;} \ _COGL_GTYPE_DEFINE_TYPE_EXTENDED_END() \ static void \ cogl_##name##_init (Cogl##Name *instance) \ { \ } \ static void \ cogl_##name##_class_init (Cogl##Name##Class *klass) \ { \ } void _cogl_gtype_object_init_value (GValue *value); void _cogl_gtype_object_free_value (GValue *value); void _cogl_gtype_object_copy_value (const GValue *src, GValue *dst); gpointer _cogl_gtype_object_peek_pointer (const GValue *value); gchar *_cogl_gtype_object_collect_value (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags); gchar *_cogl_gtype_object_lcopy_value (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags); void _cogl_gtype_object_class_base_init (CoglObjectClass *klass); void _cogl_gtype_object_class_base_finalize (CoglObjectClass *klass); void _cogl_gtype_object_class_init (CoglObjectClass *klass); void _cogl_gtype_object_init (CoglObject *object); COGL_EXPORT void cogl_object_value_set_object (GValue *value, gpointer object); COGL_EXPORT gpointer cogl_object_value_get_object (const GValue *value); void _cogl_gtype_dummy_iface_init (gpointer iface); #endif /* __COGL_GTYPE_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline.h0000664000175000017500000001343414723361714016517 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PIPELINE_H__ #define __COGL_PIPELINE_H__ /* We forward declare the CoglPipeline type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglPipeline CoglPipeline; #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-pipeline * @short_description: Functions for creating and manipulating the GPU * pipeline * * Cogl allows creating and manipulating objects representing the full * configuration of the GPU pipeline. In simplified terms the GPU * pipeline takes primitive geometry as the input, it first performs * vertex processing, allowing you to deform your geometry, then * rasterizes that (turning it from pure geometry into fragments) then * performs fragment processing including depth testing and texture * mapping. Finally it blends the result with the framebuffer. */ #define COGL_PIPELINE(OBJECT) ((CoglPipeline *)OBJECT) /** * cogl_pipeline_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_pipeline_get_gtype (void); /** * cogl_pipeline_new: (constructor) * @context: a #CoglContext * * Allocates and initializes a default simple pipeline that will color * a primitive white. * * Return value: (transfer full): a pointer to a new #CoglPipeline * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglPipeline * cogl_pipeline_new (CoglContext *context); /** * cogl_pipeline_copy: * @source: a #CoglPipeline object to copy * * Creates a new pipeline with the configuration copied from the * source pipeline. * * We would strongly advise developers to always aim to use * cogl_pipeline_copy() instead of cogl_pipeline_new() whenever there will * be any similarity between two pipelines. Copying a pipeline helps Cogl * keep track of a pipelines ancestry which we may use to help minimize GPU * state changes. * * Return value: (transfer full): a pointer to the newly allocated #CoglPipeline * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglPipeline * cogl_pipeline_copy (CoglPipeline *source); /** * cogl_is_pipeline: * @object: A #CoglObject * * Gets whether the given @object references an existing pipeline object. * * Return value: %TRUE if the @object references a #CoglPipeline, * %FALSE otherwise * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_pipeline (void *object); /** * CoglPipelineLayerCallback: * @pipeline: The #CoglPipeline whos layers are being iterated * @layer_index: The current layer index * @user_data: The private data passed to cogl_pipeline_foreach_layer() * * The callback prototype used with cogl_pipeline_foreach_layer() for * iterating all the layers of a @pipeline. * * Since: 2.0 * Stability: Unstable */ typedef gboolean (*CoglPipelineLayerCallback) (CoglPipeline *pipeline, int layer_index, void *user_data); /** * cogl_pipeline_foreach_layer: * @pipeline: A #CoglPipeline object * @callback: (scope call) (closure user_data): A #CoglPipelineLayerCallback to be * called for each layer index * @user_data: Private data that will be passed to the * callback * * Iterates all the layer indices of the given @pipeline. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_foreach_layer (CoglPipeline *pipeline, CoglPipelineLayerCallback callback, void *user_data); /** * cogl_pipeline_get_uniform_location: * @pipeline: A #CoglPipeline object * @uniform_name: The name of a uniform * * This is used to get an integer representing the uniform with the * name @uniform_name. The integer can be passed to functions such as * cogl_pipeline_set_uniform_1f() to set the value of a uniform. * * This function will always return a valid integer. Ie, unlike * OpenGL, it does not return -1 if the uniform is not available in * this pipeline so it can not be used to test whether uniforms are * present. It is not necessary to set the program on the pipeline * before calling this function. * * Return value: A integer representing the location of the given uniform. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT int cogl_pipeline_get_uniform_location (CoglPipeline *pipeline, const char *uniform_name); G_END_DECLS #endif /* __COGL_PIPELINE_H__ */ muffin-6.4.1/cogl/cogl/cogl-primitive-texture.h0000664000175000017500000000770014723361714020417 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PRIMITIVE_TEXTURE_H__ #define __COGL_PRIMITIVE_TEXTURE_H__ #include "cogl-types.h" G_BEGIN_DECLS /** * SECTION:cogl-primitive-texture * @short_description: Interface for low-level textures like * #CoglTexture2D. * * A #CoglPrimitiveTexture is a texture that is directly represented * by a single texture on the GPU. For example this could be a * #CoglTexture2D. This is opposed to high level meta textures which * may be composed of multiple primitive textures or a sub-region of * another texture such as #CoglAtlasTexture and #CoglTexture2DSliced. * * A texture that implements this interface can be directly used with * the low level cogl_primitive_draw() API. Other types of textures * need to be first resolved to primitive textures using the * #CoglMetaTexture interface. * * Most developers won't need to use this interface directly but * still it is worth understanding the distinction between high-level * and primitive textures because you may find other references in the * documentation that detail limitations of using * primitive textures. */ #if defined(__COGL_H_INSIDE__) && !defined(COGL_ENABLE_MUTTER_API) && \ !defined(COGL_GIR_SCANNING) /* For the public C api we typedef interface types as void to avoid needing * lots of casting in code and instead we will rely on runtime type checking * for these objects. */ typedef void CoglPrimitiveTexture; #else typedef struct _CoglPrimitiveTexture CoglPrimitiveTexture; #define COGL_PRIMITIVE_TEXTURE(X) ((CoglPrimitiveTexture *)X) #endif /** * cogl_is_primitive_texture: * @object: A #CoglObject pointer * * Gets whether the given object references a primitive texture object. * * Return value: %TRUE if the pointer references a primitive texture, and * %FALSE otherwise * Since: 2.0 * Stability: unstable */ gboolean cogl_is_primitive_texture (void *object); /** * cogl_primitive_texture_set_auto_mipmap: * @primitive_texture: A #CoglPrimitiveTexture * @value: The new value for whether to auto mipmap * * Sets whether the texture will automatically update the smaller * mipmap levels after any part of level 0 is updated. The update will * only occur whenever the texture is used for drawing with a texture * filter that requires the lower mipmap levels. An application should * disable this if it wants to upload its own data for the other * levels. By default auto mipmapping is enabled. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_primitive_texture_set_auto_mipmap (CoglPrimitiveTexture *primitive_texture, gboolean value); G_END_DECLS #endif /* __COGL_PRIMITIVE_TEXTURE_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-layer-state.c0000664000175000017500000015242314723361714020744 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-blend-string.h" #include "cogl-util.h" #include "cogl-matrix.h" #include "cogl-snippet-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline-layer-state-private.h" #include "string.h" #if 0 #include "cogl-context-private.h" #include "cogl-color-private.h" #endif /* * XXX: consider special casing layer->unit_index so it's not a sparse * property so instead we can assume it's valid for all layer * instances. * - We would need to initialize ->unit_index in * _cogl_pipeline_layer_copy (). * * XXX: If you use this API you should consider that the given layer * might not be writeable and so a new derived layer will be allocated * and modified instead. The layer modified will be returned so you * can identify when this happens. */ CoglPipelineLayer * _cogl_pipeline_set_layer_unit (CoglPipeline *required_owner, CoglPipelineLayer *layer, int unit_index) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_UNIT; CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, change); CoglPipelineLayer *new; if (authority->unit_index == unit_index) return layer; new = _cogl_pipeline_layer_pre_change_notify (required_owner, layer, change); if (new != layer) layer = new; else { /* If the layer we found is currently the authority on the state * we are changing see if we can revert to one of our ancestors * being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, change); if (old_authority->unit_index == unit_index) { layer->differences &= ~change; return layer; } } } layer->unit_index = unit_index; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } return layer; } CoglTexture * _cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); return authority->texture; } CoglTexture * cogl_pipeline_get_layer_texture (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayer *layer = _cogl_pipeline_get_layer (pipeline, layer_index); return _cogl_pipeline_layer_get_texture (layer); } static void _cogl_pipeline_set_layer_texture_data (CoglPipeline *pipeline, int layer_index, CoglTexture *texture) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglPipelineLayer *new; /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); if (authority->texture == texture) return; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); if (new != layer) layer = new; else { /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, change); if (old_authority->texture == texture) { layer->differences &= ~change; if (layer->texture != NULL) cogl_object_unref (layer->texture); g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); goto changed; } } } if (texture != NULL) cogl_object_ref (texture); if (layer == authority && layer->texture != NULL) cogl_object_unref (layer->texture); layer->texture = texture; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } changed: pipeline->dirty_real_blend_enable = TRUE; } void cogl_pipeline_set_layer_texture (CoglPipeline *pipeline, int layer_index, CoglTexture *texture) { _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, texture); } void cogl_pipeline_set_layer_null_texture (CoglPipeline *pipeline, int layer_index) { _cogl_pipeline_set_layer_texture_data (pipeline, layer_index, NULL); } static void _cogl_pipeline_set_layer_sampler_state (CoglPipeline *pipeline, CoglPipelineLayer *layer, CoglPipelineLayer *authority, const CoglSamplerCacheEntry *state) { CoglPipelineLayer *new; CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; if (authority->sampler_cache_entry == state) return; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); if (new != layer) layer = new; else { /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, change); if (old_authority->sampler_cache_entry == state) { layer->differences &= ~change; g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); return; } } } layer->sampler_cache_entry = state; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } } static CoglSamplerCacheWrapMode public_to_internal_wrap_mode (CoglPipelineWrapMode mode) { return (CoglSamplerCacheWrapMode)mode; } static CoglPipelineWrapMode internal_to_public_wrap_mode (CoglSamplerCacheWrapMode internal_mode) { g_return_val_if_fail (internal_mode != COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER, COGL_PIPELINE_WRAP_MODE_AUTOMATIC); return (CoglPipelineWrapMode)internal_mode; } void cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); const CoglSamplerCacheEntry *sampler_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); sampler_state = _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, authority->sampler_cache_entry, internal_mode, authority->sampler_cache_entry-> wrap_mode_t); _cogl_pipeline_set_layer_sampler_state (pipeline, layer, authority, sampler_state); } void cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); const CoglSamplerCacheEntry *sampler_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); sampler_state = _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, authority->sampler_cache_entry, authority->sampler_cache_entry-> wrap_mode_s, internal_mode); _cogl_pipeline_set_layer_sampler_state (pipeline, layer, authority, sampler_state); } void cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglSamplerCacheWrapMode internal_mode = public_to_internal_wrap_mode (mode); const CoglSamplerCacheEntry *sampler_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); sampler_state = _cogl_sampler_cache_update_wrap_modes (ctx->sampler_cache, authority->sampler_cache_entry, internal_mode, internal_mode); _cogl_pipeline_set_layer_sampler_state (pipeline, layer, authority, sampler_state); } /* FIXME: deprecate this API */ CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *authority; const CoglSamplerCacheEntry *sampler_state; g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); sampler_state = authority->sampler_cache_entry; return internal_to_public_wrap_mode (sampler_state->wrap_mode_s); } CoglPipelineWrapMode cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayer *layer; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* FIXME: we shouldn't ever construct a layer in a getter function */ return _cogl_pipeline_layer_get_wrap_mode_s (layer); } /* FIXME: deprecate this API */ CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *authority; const CoglSamplerCacheEntry *sampler_state; g_return_val_if_fail (_cogl_is_pipeline_layer (layer), FALSE); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); sampler_state = authority->sampler_cache_entry; return internal_to_public_wrap_mode (sampler_state->wrap_mode_t); } CoglPipelineWrapMode cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayer *layer; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* FIXME: we shouldn't ever construct a layer in a getter function */ return _cogl_pipeline_layer_get_wrap_mode_t (layer); } void _cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, CoglSamplerCacheWrapMode *wrap_mode_s, CoglSamplerCacheWrapMode *wrap_mode_t) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); *wrap_mode_s = authority->sampler_cache_entry->wrap_mode_s; *wrap_mode_t = authority->sampler_cache_entry->wrap_mode_t; } gboolean cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, int layer_index, gboolean enable, GError **error) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; CoglPipelineLayer *layer; CoglPipelineLayer *new; CoglPipelineLayer *authority; _COGL_GET_CONTEXT (ctx, FALSE); g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); if (authority->big_state->point_sprite_coords == enable) return TRUE; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); if (new != layer) layer = new; else { /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, change); if (old_authority->big_state->point_sprite_coords == enable) { layer->differences &= ~change; g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); return TRUE; } } } layer->big_state->point_sprite_coords = enable; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } return TRUE; } gboolean cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; CoglPipelineLayer *layer; CoglPipelineLayer *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* FIXME: we shouldn't ever construct a layer in a getter function */ authority = _cogl_pipeline_layer_get_authority (layer, change); return authority->big_state->point_sprite_coords; } static void _cogl_pipeline_layer_add_vertex_snippet (CoglPipeline *pipeline, int layer_index, CoglSnippet *snippet) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; CoglPipelineLayer *layer, *authority; /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); _cogl_pipeline_snippet_list_add (&layer->big_state->vertex_snippets, snippet); /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } } static void _cogl_pipeline_layer_add_fragment_snippet (CoglPipeline *pipeline, int layer_index, CoglSnippet *snippet) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; CoglPipelineLayer *layer, *authority; /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, change); layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, change); _cogl_pipeline_snippet_list_add (&layer->big_state->fragment_snippets, snippet); /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= change; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } } void cogl_pipeline_add_layer_snippet (CoglPipeline *pipeline, int layer_index, CoglSnippet *snippet) { g_return_if_fail (cogl_is_pipeline (pipeline)); g_return_if_fail (cogl_is_snippet (snippet)); g_return_if_fail (snippet->hook >= COGL_SNIPPET_FIRST_LAYER_HOOK); if (snippet->hook < COGL_SNIPPET_FIRST_LAYER_FRAGMENT_HOOK) _cogl_pipeline_layer_add_vertex_snippet (pipeline, layer_index, snippet); else _cogl_pipeline_layer_add_fragment_snippet (pipeline, layer_index, snippet); } gboolean _cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1, CoglPipelineEvalFlags flags) { if (authority0->texture == NULL) { if (authority1->texture == NULL) return TRUE; else return FALSE; } else if (authority1->texture == NULL) return FALSE; else { GLuint gl_handle0, gl_handle1; cogl_texture_get_gl_texture (authority0->texture, &gl_handle0, NULL); cogl_texture_get_gl_texture (authority1->texture, &gl_handle1, NULL); return gl_handle0 == gl_handle1; } } gboolean _cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { CoglPipelineLayerBigState *big_state0 = authority0->big_state; CoglPipelineLayerBigState *big_state1 = authority1->big_state; int n_args; int i; if (big_state0->texture_combine_rgb_func != big_state1->texture_combine_rgb_func) return FALSE; if (big_state0->texture_combine_alpha_func != big_state1->texture_combine_alpha_func) return FALSE; n_args = _cogl_get_n_args_for_combine_func (big_state0->texture_combine_rgb_func); for (i = 0; i < n_args; i++) { if ((big_state0->texture_combine_rgb_src[i] != big_state1->texture_combine_rgb_src[i]) || (big_state0->texture_combine_rgb_op[i] != big_state1->texture_combine_rgb_op[i])) return FALSE; } n_args = _cogl_get_n_args_for_combine_func (big_state0->texture_combine_alpha_func); for (i = 0; i < n_args; i++) { if ((big_state0->texture_combine_alpha_src[i] != big_state1->texture_combine_alpha_src[i]) || (big_state0->texture_combine_alpha_op[i] != big_state1->texture_combine_alpha_op[i])) return FALSE; } return TRUE; } gboolean _cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { return memcmp (authority0->big_state->texture_combine_constant, authority1->big_state->texture_combine_constant, sizeof (float) * 4) == 0 ? TRUE : FALSE; } gboolean _cogl_pipeline_layer_sampler_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { /* We compare the actual sampler objects rather than just the entry pointers because two states with different values can lead to the same state in GL terms when AUTOMATIC is used as a wrap mode */ return (authority0->sampler_cache_entry->sampler_object == authority1->sampler_cache_entry->sampler_object); } gboolean _cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { CoglPipelineLayerBigState *big_state0 = authority0->big_state; CoglPipelineLayerBigState *big_state1 = authority1->big_state; if (!cogl_matrix_equal (&big_state0->matrix, &big_state1->matrix)) return FALSE; return TRUE; } gboolean _cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { CoglPipelineLayerBigState *big_state0 = authority0->big_state; CoglPipelineLayerBigState *big_state1 = authority1->big_state; return big_state0->point_sprite_coords == big_state1->point_sprite_coords; } gboolean _cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> vertex_snippets, &authority1->big_state-> vertex_snippets); } gboolean _cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1) { return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> fragment_snippets, &authority1->big_state-> fragment_snippets); } static void setup_texture_combine_state (CoglBlendStringStatement *statement, CoglPipelineCombineFunc *texture_combine_func, CoglPipelineCombineSource *texture_combine_src, CoglPipelineCombineOp *texture_combine_op) { int i; switch (statement->function->type) { case COGL_BLEND_STRING_FUNCTION_REPLACE: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_REPLACE; break; case COGL_BLEND_STRING_FUNCTION_MODULATE: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE; break; case COGL_BLEND_STRING_FUNCTION_ADD: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD; break; case COGL_BLEND_STRING_FUNCTION_ADD_SIGNED: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED; break; case COGL_BLEND_STRING_FUNCTION_INTERPOLATE: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE; break; case COGL_BLEND_STRING_FUNCTION_SUBTRACT: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_SUBTRACT; break; case COGL_BLEND_STRING_FUNCTION_DOT3_RGB: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB; break; case COGL_BLEND_STRING_FUNCTION_DOT3_RGBA: *texture_combine_func = COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA; break; } for (i = 0; i < statement->function->argc; i++) { CoglBlendStringArgument *arg = &statement->args[i]; switch (arg->source.info->type) { case COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT: texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_CONSTANT; break; case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE: texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; break; case COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N: texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 + arg->source.texture; break; case COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY: texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR; break; case COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS: texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; break; default: g_warning ("Unexpected texture combine source"); texture_combine_src[i] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; } if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) { if (statement->args[i].source.one_minus) texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR; else texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR; } else { if (statement->args[i].source.one_minus) texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA; else texture_combine_op[i] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; } } } gboolean cogl_pipeline_set_layer_combine (CoglPipeline *pipeline, int layer_index, const char *combine_description, GError **error) { CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE; CoglPipelineLayer *authority; CoglPipelineLayer *layer; CoglBlendStringStatement statements[2]; CoglBlendStringStatement split[2]; CoglBlendStringStatement *rgb; CoglBlendStringStatement *a; int count; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, state); count = _cogl_blend_string_compile (combine_description, COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE, statements, error); if (!count) return FALSE; if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) { _cogl_blend_string_split_rgba_statement (statements, &split[0], &split[1]); rgb = &split[0]; a = &split[1]; } else { rgb = &statements[0]; a = &statements[1]; } /* FIXME: compare the new state with the current state! */ /* possibly flush primitives referencing the current state... */ layer = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); setup_texture_combine_state (rgb, &layer->big_state->texture_combine_rgb_func, layer->big_state->texture_combine_rgb_src, layer->big_state->texture_combine_rgb_op); setup_texture_combine_state (a, &layer->big_state->texture_combine_alpha_func, layer->big_state->texture_combine_alpha_src, layer->big_state->texture_combine_alpha_op); /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, state); if (_cogl_pipeline_layer_combine_state_equal (authority, old_authority)) { layer->differences &= ~state; g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); goto changed; } } /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= state; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } changed: pipeline->dirty_real_blend_enable = TRUE; return TRUE; } void cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline, int layer_index, const CoglColor *constant_color) { CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglPipelineLayer *new; float color_as_floats[4]; g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, state); color_as_floats[0] = cogl_color_get_red_float (constant_color); color_as_floats[1] = cogl_color_get_green_float (constant_color); color_as_floats[2] = cogl_color_get_blue_float (constant_color); color_as_floats[3] = cogl_color_get_alpha_float (constant_color); if (memcmp (authority->big_state->texture_combine_constant, color_as_floats, sizeof (float) * 4) == 0) return; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); if (new != layer) layer = new; else { /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, state); CoglPipelineLayerBigState *old_big_state = old_authority->big_state; if (memcmp (old_big_state->texture_combine_constant, color_as_floats, sizeof (float) * 4) == 0) { layer->differences &= ~state; g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); goto changed; } } } memcpy (layer->big_state->texture_combine_constant, color_as_floats, sizeof (color_as_floats)); /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= state; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } changed: pipeline->dirty_real_blend_enable = TRUE; } void _cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline, int layer_index, float *constant) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT; CoglPipelineLayer *layer; CoglPipelineLayer *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* FIXME: we shouldn't ever construct a layer in a getter function */ authority = _cogl_pipeline_layer_get_authority (layer, change); memcpy (constant, authority->big_state->texture_combine_constant, sizeof (float) * 4); } /* We should probably make a public API version of this that has a matrix out-param. For an internal API it's good to be able to avoid copying the matrix */ const CoglMatrix * _cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayerState change = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; CoglPipelineLayer *layer; CoglPipelineLayer *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); layer = _cogl_pipeline_get_layer (pipeline, layer_index); authority = _cogl_pipeline_layer_get_authority (layer, change); return &authority->big_state->matrix; } void cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline, int layer_index, const CoglMatrix *matrix) { CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_USER_MATRIX; CoglPipelineLayer *layer; CoglPipelineLayer *authority; CoglPipelineLayer *new; g_return_if_fail (cogl_is_pipeline (pipeline)); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, state); if (cogl_matrix_equal (matrix, &authority->big_state->matrix)) return; new = _cogl_pipeline_layer_pre_change_notify (pipeline, layer, state); if (new != layer) layer = new; else { /* If the original layer we found is currently the authority on * the state we are changing see if we can revert to one of our * ancestors being the authority. */ if (layer == authority && _cogl_pipeline_layer_get_parent (authority) != NULL) { CoglPipelineLayer *parent = _cogl_pipeline_layer_get_parent (authority); CoglPipelineLayer *old_authority = _cogl_pipeline_layer_get_authority (parent, state); if (cogl_matrix_equal (matrix, &old_authority->big_state->matrix)) { layer->differences &= ~state; g_assert (layer->owner == pipeline); if (layer->differences == 0) _cogl_pipeline_prune_empty_layer_difference (pipeline, layer); return; } } } layer->big_state->matrix = *matrix; /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (layer != authority) { layer->differences |= state; _cogl_pipeline_layer_prune_redundant_ancestry (layer); } } CoglTexture * _cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer) { g_return_val_if_fail (_cogl_is_pipeline_layer (layer), NULL); return _cogl_pipeline_layer_get_texture_real (layer); } gboolean _cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, int layer_index) { CoglPipelineLayer *layer; CoglPipelineLayer *authority; layer = _cogl_pipeline_get_layer (pipeline, layer_index); authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_USER_MATRIX); /* If the authority is the default pipeline then no, otherwise yes */ return _cogl_pipeline_layer_get_parent (authority) ? TRUE : FALSE; } void _cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, CoglPipelineFilter *min_filter, CoglPipelineFilter *mag_filter) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); *min_filter = authority->sampler_cache_entry->min_filter; *mag_filter = authority->sampler_cache_entry->mag_filter; } void _cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, int layer_index, CoglPipelineFilter *min_filter, CoglPipelineFilter *mag_filter) { CoglPipelineLayer *layer; CoglPipelineLayer *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); layer = _cogl_pipeline_get_layer (pipeline, layer_index); authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); *min_filter = authority->sampler_cache_entry->min_filter; *mag_filter = authority->sampler_cache_entry->mag_filter; } CoglPipelineFilter cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, int layer_index) { CoglPipelineFilter min_filter; CoglPipelineFilter mag_filter; _cogl_pipeline_get_layer_filters (pipeline, layer_index, &min_filter, &mag_filter); return min_filter; } CoglPipelineFilter cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, int layer_index) { CoglPipelineFilter min_filter; CoglPipelineFilter mag_filter; _cogl_pipeline_get_layer_filters (pipeline, layer_index, &min_filter, &mag_filter); return mag_filter; } CoglPipelineFilter _cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer) { CoglPipelineLayer *authority; g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0); authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); return authority->sampler_cache_entry->min_filter; } CoglPipelineFilter _cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer) { CoglPipelineLayer *authority; g_return_val_if_fail (_cogl_is_pipeline_layer (layer), 0); authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); return authority->sampler_cache_entry->mag_filter; } void cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, int layer_index, CoglPipelineFilter min_filter, CoglPipelineFilter mag_filter) { CoglPipelineLayerState state = COGL_PIPELINE_LAYER_STATE_SAMPLER; CoglPipelineLayer *layer; CoglPipelineLayer *authority; const CoglSamplerCacheEntry *sampler_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_is_pipeline (pipeline)); g_return_if_fail (mag_filter == COGL_PIPELINE_FILTER_NEAREST || mag_filter == COGL_PIPELINE_FILTER_LINEAR); /* Note: this will ensure that the layer exists, creating one if it * doesn't already. * * Note: If the layer already existed it's possibly owned by another * pipeline. If the layer is created then it will be owned by * pipeline. */ layer = _cogl_pipeline_get_layer (pipeline, layer_index); /* Now find the ancestor of the layer that is the authority for the * state we want to change */ authority = _cogl_pipeline_layer_get_authority (layer, state); sampler_state = _cogl_sampler_cache_update_filters (ctx->sampler_cache, authority->sampler_cache_entry, min_filter, mag_filter); _cogl_pipeline_set_layer_sampler_state (pipeline, layer, authority, sampler_state); } void cogl_pipeline_set_layer_max_mipmap_level (CoglPipeline *pipeline, int layer, int max_level) { CoglTexture *texture = cogl_pipeline_get_layer_texture (pipeline, layer); if (texture != NULL) cogl_texture_set_max_level (texture, max_level); } const CoglSamplerCacheEntry * _cogl_pipeline_layer_get_sampler_state (CoglPipelineLayer *layer) { CoglPipelineLayer *authority; authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_SAMPLER); return authority->sampler_cache_entry; } void _cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { int unit = authority->unit_index; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &unit, sizeof (unit)); } void _cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { GLuint gl_handle; cogl_texture_get_gl_texture (authority->texture, &gl_handle, NULL); state->hash = _cogl_util_one_at_a_time_hash (state->hash, &gl_handle, sizeof (gl_handle)); } void _cogl_pipeline_layer_hash_sampler_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { state->hash = _cogl_util_one_at_a_time_hash (state->hash, &authority->sampler_cache_entry, sizeof (authority->sampler_cache_entry)); } void _cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { unsigned int hash = state->hash; CoglPipelineLayerBigState *b = authority->big_state; int n_args; int i; hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_func, sizeof (b->texture_combine_rgb_func)); n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); for (i = 0; i < n_args; i++) { hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_src[i], sizeof (b->texture_combine_rgb_src[i])); hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_rgb_op[i], sizeof (b->texture_combine_rgb_op[i])); } hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_func, sizeof (b->texture_combine_alpha_func)); n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); for (i = 0; i < n_args; i++) { hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_src[i], sizeof (b->texture_combine_alpha_src[i])); hash = _cogl_util_one_at_a_time_hash (hash, &b->texture_combine_alpha_op[i], sizeof (b->texture_combine_alpha_op[i])); } state->hash = hash; } void _cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { CoglPipelineLayerBigState *b = authority->big_state; gboolean need_hash = FALSE; int n_args; int i; /* XXX: If the user also asked to hash the ALPHA_FUNC_STATE then it * would be nice if we could combine the n_args loops in this * function and _cogl_pipeline_layer_hash_combine_state. */ n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_rgb_func); for (i = 0; i < n_args; i++) { if (b->texture_combine_rgb_src[i] == COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) { /* XXX: should we be careful to only hash the alpha * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ need_hash = TRUE; goto done; } } n_args = _cogl_get_n_args_for_combine_func (b->texture_combine_alpha_func); for (i = 0; i < n_args; i++) { if (b->texture_combine_alpha_src[i] == COGL_PIPELINE_COMBINE_SOURCE_CONSTANT) { /* XXX: should we be careful to only hash the alpha * component in the COGL_PIPELINE_COMBINE_OP_SRC_ALPHA case? */ need_hash = TRUE; goto done; } } done: if (need_hash) { float *constant = b->texture_combine_constant; state->hash = _cogl_util_one_at_a_time_hash (state->hash, constant, sizeof (float) * 4); } } void _cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { CoglPipelineLayerBigState *big_state = authority->big_state; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->matrix, sizeof (float) * 16); } void _cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { CoglPipelineLayerBigState *big_state = authority->big_state; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &big_state->point_sprite_coords, sizeof (big_state->point_sprite_coords)); } void _cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { _cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets, &state->hash); } void _cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state) { _cogl_pipeline_snippet_list_hash (&authority->big_state->fragment_snippets, &state->hash); } muffin-6.4.1/cogl/cogl/cogl-matrix-stack-private.h0000664000175000017500000000747214723361714020776 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Havoc Pennington for litl * Robert Bragg */ #ifndef _COGL_MATRIX_STACK_PRIVATE_H_ #define _COGL_MATRIX_STACK_PRIVATE_H_ #include "cogl-object-private.h" #include "cogl-matrix-stack.h" #include "cogl-context.h" #include "cogl-framebuffer.h" typedef enum _CoglMatrixOp { COGL_MATRIX_OP_LOAD_IDENTITY, COGL_MATRIX_OP_TRANSLATE, COGL_MATRIX_OP_ROTATE, COGL_MATRIX_OP_ROTATE_EULER, COGL_MATRIX_OP_SCALE, COGL_MATRIX_OP_MULTIPLY, COGL_MATRIX_OP_LOAD, COGL_MATRIX_OP_SAVE, } CoglMatrixOp; struct _CoglMatrixEntry { CoglMatrixEntry *parent; CoglMatrixOp op; unsigned int ref_count; #ifdef COGL_DEBUG_ENABLED /* used for performance tracing */ int composite_gets; #endif }; typedef struct _CoglMatrixEntryTranslate { CoglMatrixEntry _parent_data; graphene_point3d_t translate; } CoglMatrixEntryTranslate; typedef struct _CoglMatrixEntryRotate { CoglMatrixEntry _parent_data; float angle; graphene_vec3_t axis; } CoglMatrixEntryRotate; typedef struct _CoglMatrixEntryRotateEuler { CoglMatrixEntry _parent_data; graphene_euler_t euler; } CoglMatrixEntryRotateEuler; typedef struct _CoglMatrixEntryScale { CoglMatrixEntry _parent_data; float x; float y; float z; } CoglMatrixEntryScale; typedef struct _CoglMatrixEntryMultiply { CoglMatrixEntry _parent_data; CoglMatrix *matrix; } CoglMatrixEntryMultiply; typedef struct _CoglMatrixEntryLoad { CoglMatrixEntry _parent_data; CoglMatrix *matrix; } CoglMatrixEntryLoad; typedef struct _CoglMatrixEntrySave { CoglMatrixEntry _parent_data; CoglMatrix *cache; gboolean cache_valid; } CoglMatrixEntrySave; typedef union _CoglMatrixEntryFull { CoglMatrixEntry any; CoglMatrixEntryTranslate translate; CoglMatrixEntryRotate rotate; CoglMatrixEntryRotateEuler rotate_euler; CoglMatrixEntryScale scale; CoglMatrixEntryMultiply multiply; CoglMatrixEntryLoad load; CoglMatrixEntrySave save; } CoglMatrixEntryFull; struct _CoglMatrixStack { CoglObject _parent; CoglContext *context; CoglMatrixEntry *last_entry; }; typedef struct _CoglMatrixEntryCache { CoglMatrixEntry *entry; gboolean flushed_identity; gboolean flipped; } CoglMatrixEntryCache; void _cogl_matrix_entry_identity_init (CoglMatrixEntry *entry); void _cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache); gboolean _cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache, CoglMatrixEntry *entry, gboolean flip); void _cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache); #endif /* _COGL_MATRIX_STACK_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/cogl-depth-state-private.h0000664000175000017500000000260114723361714020576 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #ifndef __COGL_DEPTH_STATE_PRIVATE_H #define __COGL_DEPTH_STATE_PRIVATE_H #define COGL_DEPTH_STATE_MAGIC 0xDEADBEEF #endif /* __COGL_DEPTH_STATE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-poll.h0000664000175000017500000001650014723361714015655 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_POLL_H__ #define __COGL_POLL_H__ #include #include G_BEGIN_DECLS /** * SECTION:cogl-poll * @short_description: Functions for integrating Cogl with an * application's main loop * * Cogl needs to integrate with the application's main loop so that it * can internally handle some events from the driver. All Cogl * applications must use these functions. They provide enough * information to describe the state that Cogl will need to wake up * on. An application using the GLib main loop can instead use * cogl_glib_source_new() which provides a #GSource ready to be added * to the main loop. */ /** * CoglPollFDEvent: * @COGL_POLL_FD_EVENT_IN: there is data to read * @COGL_POLL_FD_EVENT_PRI: data can be written (without blocking) * @COGL_POLL_FD_EVENT_OUT: there is urgent data to read. * @COGL_POLL_FD_EVENT_ERR: error condition * @COGL_POLL_FD_EVENT_HUP: hung up (the connection has been broken, usually * for pipes and sockets). * @COGL_POLL_FD_EVENT_NVAL: invalid request. The file descriptor is not open. * * A bitmask of events that Cogl may need to wake on for a file * descriptor. Note that these all have the same values as the * corresponding defines for the poll function call on Unix so they * may be directly passed to poll. * * Since: 1.10 * Stability: unstable */ typedef enum { COGL_POLL_FD_EVENT_IN = COGL_SYSDEF_POLLIN, COGL_POLL_FD_EVENT_PRI = COGL_SYSDEF_POLLPRI, COGL_POLL_FD_EVENT_OUT = COGL_SYSDEF_POLLOUT, COGL_POLL_FD_EVENT_ERR = COGL_SYSDEF_POLLERR, COGL_POLL_FD_EVENT_HUP = COGL_SYSDEF_POLLHUP, COGL_POLL_FD_EVENT_NVAL = COGL_SYSDEF_POLLNVAL } CoglPollFDEvent; /** * CoglPollFD: * @fd: The file descriptor to block on * @events: A bitmask of events to block on * @revents: A bitmask of returned events * * A struct for describing the state of a file descriptor that Cogl * needs to block on. The @events field contains a bitmask of * #CoglPollFDEvents that should cause the application to wake * up. After the application is woken up from idle it should pass back * an array of #CoglPollFDs to Cogl and update the @revents * mask to the actual events that occurred on the file descriptor. * * Note that CoglPollFD is deliberately exactly the same as struct * pollfd on Unix so that it can simply be cast when calling poll. * * Since: 1.10 * Stability: unstable */ typedef struct { int fd; short int events; short int revents; } CoglPollFD; /** * cogl_poll_renderer_get_info: * @renderer: A #CoglRenderer * @poll_fds: A return location for a pointer to an array * of #CoglPollFDs * @n_poll_fds: A return location for the number of entries in *@poll_fds * @timeout: A return location for the maximum length of time to wait * in microseconds, or -1 to wait indefinitely. * * Is used to integrate Cogl with an application mainloop that is based * on the unix poll(2) api (or select() or something equivalent). This * api should be called whenever an application is about to go idle so * that Cogl has a chance to describe what file descriptor events it * needs to be woken up for. * * If your application is using the Glib mainloop then you * should jump to the cogl_glib_source_new() api as a more convenient * way of integrating Cogl with the mainloop. * * After the function is called *@poll_fds will contain a pointer to * an array of #CoglPollFD structs describing the file descriptors * that Cogl expects. The fd and events members will be updated * accordingly. After the application has completed its idle it is * expected to either update the revents members directly in this * array or to create a copy of the array and update them * there. * * When the application mainloop returns from calling poll(2) (or its * equivalent) then it should call cogl_poll_renderer_dispatch() * passing a pointer the array of CoglPollFDs with updated * revent values. * * @timeout will contain a maximum amount of time to wait in * microseconds before the application should wake up or -1 if the * application should wait indefinitely. This can also be 0 if * Cogl needs to be woken up immediately. * * Return value: A "poll fd state age" that changes whenever the set * of poll_fds has changed. If this API is being used to * integrate with another system mainloop api then * knowing if the set of file descriptors and events has * really changed can help avoid redundant work * depending the api. The age isn't guaranteed to change * when the timeout changes. * * Stability: unstable * Since: 1.16 */ COGL_EXPORT int cogl_poll_renderer_get_info (CoglRenderer *renderer, CoglPollFD **poll_fds, int *n_poll_fds, int64_t *timeout); /** * cogl_poll_renderer_dispatch: * @renderer: A #CoglRenderer * @poll_fds: An array of #CoglPollFDs describing the events * that have occurred since the application went idle. * @n_poll_fds: The length of the @poll_fds array. * * This should be called whenever an application is woken up from * going idle in its main loop. The @poll_fds array should contain a * list of file descriptors matched with the events that occurred in * revents. The events field is ignored. It is safe to pass in extra * file descriptors that Cogl didn't request when calling * cogl_poll_renderer_get_info() or a shorter array missing some file * descriptors that Cogl requested. * * If your application didn't originally create a #CoglRenderer * manually then you can easily get a #CoglRenderer pointer by calling * cogl_get_renderer(). * * Stability: unstable * Since: 1.16 */ COGL_EXPORT void cogl_poll_renderer_dispatch (CoglRenderer *renderer, const CoglPollFD *poll_fds, int n_poll_fds); G_END_DECLS #endif /* __COGL_POLL_H__ */ muffin-6.4.1/cogl/cogl/cogl-pixel-format.c0000664000175000017500000001756414723361714017324 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include #include #include "cogl-pixel-format.h" /* An entry to map CoglPixelFormats to their respective properties */ typedef struct _CoglPixelFormatInfo { CoglPixelFormat cogl_format; const char *format_str; int aligned; /* Aligned components? (-1 if n/a) */ uint8_t n_planes; /* Per-plane information */ uint8_t bpp[COGL_PIXEL_FORMAT_MAX_PLANES]; /* Bytes per pixel */ } CoglPixelFormatInfo; static const CoglPixelFormatInfo format_info_table[] = { { .cogl_format = COGL_PIXEL_FORMAT_ANY, .format_str = "ANY", .n_planes = 0, .aligned = -1, .bpp = { 0 }, }, { .cogl_format = COGL_PIXEL_FORMAT_A_8, .format_str = "A_8", .n_planes = 1, .aligned = 1, .bpp = { 1 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGB_565, .format_str = "RGB_565", .n_planes = 1, .aligned = 0, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_4444, .format_str = "RGBA_4444", .n_planes = 1, .aligned = 0, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_5551, .format_str = "RGBA_5551", .n_planes = 1, .aligned = 0, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_YUV, .format_str = "YUV", .n_planes = 1, .aligned = -1, .bpp = { 0 }, }, { .cogl_format = COGL_PIXEL_FORMAT_G_8, .format_str = "G_8", .n_planes = 1, .aligned = 1, .bpp = { 1 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RG_88, .format_str = "RG_88", .n_planes = 1, .aligned = 1, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGB_888, .format_str = "RGB_888", .n_planes = 1, .aligned = 1, .bpp = { 3 }, }, { .cogl_format = COGL_PIXEL_FORMAT_BGR_888, .format_str = "BGR_888", .n_planes = 1, .aligned = 1, .bpp = { 3 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_8888, .format_str = "RGBA_8888", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_BGRA_8888, .format_str = "BGRA_8888", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ARGB_8888, .format_str = "ARGB_8888", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ABGR_8888, .format_str = "ABGR_8888", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_1010102, .format_str = "RGBA_1010102", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_BGRA_1010102, .format_str = "BGRA_1010102", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010, .format_str = "ARGB_2101010", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ABGR_2101010, .format_str = "ABGR_2101010", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE, .format_str = "RGBA_8888_PRE", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_BGRA_8888_PRE, .format_str = "BGRA_8888_PRE", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ARGB_8888_PRE, .format_str = "ARGB_8888_PRE", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ABGR_8888_PRE, .format_str = "ABGR_8888_PRE", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_4444_PRE, .format_str = "RGBA_4444_PRE", .n_planes = 1, .aligned = 0, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_5551_PRE, .format_str = "RGBA_5551_PRE", .n_planes = 1, .aligned = 0, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_RGBA_1010102_PRE, .format_str = "RGBA_1010102_PRE", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_BGRA_1010102_PRE, .format_str = "BGRA_1010102_PRE", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE, .format_str = "ARGB_2101010_PRE", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_ABGR_2101010_PRE, .format_str = "ABGR_2101010_PRE", .n_planes = 1, .aligned = 0, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_DEPTH_16, .format_str = "DEPTH_16", .n_planes = 1, .aligned = 1, .bpp = { 2 }, }, { .cogl_format = COGL_PIXEL_FORMAT_DEPTH_32, .format_str = "DEPTH_32", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, { .cogl_format = COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8, .format_str = "DEPTH_24_STENCIL_8", .n_planes = 1, .aligned = 1, .bpp = { 4 }, }, }; int cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format, int plane) { size_t i; for (i = 0; i < G_N_ELEMENTS (format_info_table); i++) { if (format_info_table[i].cogl_format == format) { g_return_val_if_fail (plane < format_info_table[i].n_planes, 0); return format_info_table[i].bpp[plane]; } } g_assert_not_reached (); } /* Note: this also refers to the mapping defined above for * cogl_pixel_format_get_bytes_per_pixel() */ gboolean _cogl_pixel_format_is_endian_dependant (CoglPixelFormat format) { int aligned = -1; size_t i; /* NB: currently checking whether the format components are aligned * or not determines whether the format is endian dependent or not. * In the future though we might consider adding formats with * aligned components that are also endian independant. */ for (i = 0; i < G_N_ELEMENTS (format_info_table); i++) { if (format_info_table[i].cogl_format == format) { aligned = format_info_table[i].aligned; break; } } g_return_val_if_fail (aligned != -1, FALSE); return aligned; } int cogl_pixel_format_get_n_planes (CoglPixelFormat format) { size_t i; for (i = 0; i < G_N_ELEMENTS (format_info_table); i++) { if (format_info_table[i].cogl_format == format) return format_info_table[i].n_planes; } g_assert_not_reached (); } const char * cogl_pixel_format_to_string (CoglPixelFormat format) { size_t i; for (i = 0; i < G_N_ELEMENTS (format_info_table); i++) { if (format_info_table[i].cogl_format == format) return format_info_table[i].format_str; } g_assert_not_reached (); } muffin-6.4.1/cogl/cogl/cogl-fence.c0000664000175000017500000001464314723361714015770 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "cogl-config.h" #include "cogl-fence.h" #include "cogl-fence-private.h" #include "cogl-context-private.h" #include "winsys/cogl-winsys-private.h" #define FENCE_CHECK_TIMEOUT 5000 /* microseconds */ void * cogl_fence_closure_get_user_data (CoglFenceClosure *closure) { return closure->user_data; } static void _cogl_fence_check (CoglFenceClosure *fence) { CoglContext *context = fence->framebuffer->context; if (fence->type == FENCE_TYPE_WINSYS) { const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); gboolean ret; ret = winsys->fence_is_complete (context, fence->fence_obj); if (!ret) return; } #ifdef GL_ARB_sync else if (fence->type == FENCE_TYPE_GL_ARB) { GLenum arb; arb = context->glClientWaitSync (fence->fence_obj, GL_SYNC_FLUSH_COMMANDS_BIT, 0); if (arb != GL_ALREADY_SIGNALED && arb != GL_CONDITION_SATISFIED) return; } #endif fence->callback (NULL, /* dummy CoglFence object */ fence->user_data); cogl_framebuffer_cancel_fence_callback (fence->framebuffer, fence); } static void _cogl_fence_poll_dispatch (void *source, int revents) { CoglContext *context = source; CoglFenceClosure *fence, *tmp; _cogl_list_for_each_safe (fence, tmp, &context->fences, link) _cogl_fence_check (fence); } static int64_t _cogl_fence_poll_prepare (void *source) { CoglContext *context = source; GList *l; /* If there are any pending fences in any of the journals then we * need to flush the journal otherwise the fence will never be * hit and the main loop might block forever */ for (l = context->framebuffers; l; l = l->next) { CoglFramebuffer *fb = l->data; if (!_cogl_list_empty (&fb->journal->pending_fences)) _cogl_framebuffer_flush_journal (fb); } if (!_cogl_list_empty (&context->fences)) return FENCE_CHECK_TIMEOUT; else return -1; } void _cogl_fence_submit (CoglFenceClosure *fence) { CoglContext *context = fence->framebuffer->context; const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); fence->type = FENCE_TYPE_ERROR; if (winsys->fence_add) { fence->fence_obj = winsys->fence_add (context); if (fence->fence_obj) { fence->type = FENCE_TYPE_WINSYS; goto done; } } #ifdef GL_ARB_sync if (context->glFenceSync) { fence->fence_obj = context->glFenceSync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); if (fence->fence_obj) { fence->type = FENCE_TYPE_GL_ARB; goto done; } } #endif done: _cogl_list_insert (context->fences.prev, &fence->link); if (!context->fences_poll_source) { context->fences_poll_source = _cogl_poll_renderer_add_source (context->display->renderer, _cogl_fence_poll_prepare, _cogl_fence_poll_dispatch, context); } } CoglFenceClosure * cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer, CoglFenceCallback callback, void *user_data) { CoglContext *context = framebuffer->context; CoglJournal *journal = framebuffer->journal; CoglFenceClosure *fence; if (!COGL_FLAGS_GET (context->features, COGL_FEATURE_ID_FENCE)) return NULL; fence = g_slice_new (CoglFenceClosure); fence->framebuffer = framebuffer; fence->callback = callback; fence->user_data = user_data; fence->fence_obj = NULL; if (journal->entries->len) { _cogl_list_insert (journal->pending_fences.prev, &fence->link); fence->type = FENCE_TYPE_PENDING; } else _cogl_fence_submit (fence); return fence; } void cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer, CoglFenceClosure *fence) { CoglContext *context = framebuffer->context; if (fence->type == FENCE_TYPE_PENDING) { _cogl_list_remove (&fence->link); } else { _cogl_list_remove (&fence->link); if (fence->type == FENCE_TYPE_WINSYS) { const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); winsys->fence_destroy (context, fence->fence_obj); } #ifdef GL_ARB_sync else if (fence->type == FENCE_TYPE_GL_ARB) { context->glDeleteSync (fence->fence_obj); } #endif } g_slice_free (CoglFenceClosure, fence); } void _cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer) { CoglJournal *journal = framebuffer->journal; CoglContext *context = framebuffer->context; CoglFenceClosure *fence, *tmp; while (!_cogl_list_empty (&journal->pending_fences)) { fence = _cogl_container_of (journal->pending_fences.next, CoglFenceClosure, link); cogl_framebuffer_cancel_fence_callback (framebuffer, fence); } _cogl_list_for_each_safe (fence, tmp, &context->fences, link) { if (fence->framebuffer == framebuffer) cogl_framebuffer_cancel_fence_callback (framebuffer, fence); } } muffin-6.4.1/cogl/cogl/cogl-magazine.c0000664000175000017500000000513414723361714016476 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * CoglMagazine provides a really light weight allocator for chunks * of memory with a pre-determined size. * * This allocator builds on CoglMemoryStack for making all initial * allocations but never frees memory back to the stack. * * Memory chunks that haven't been allocated yet are stored in a * singly linked, fifo, list. * * Allocating from a magazine is simply a question of popping an entry * from the head of the fifo list. If no entries are available then * instead allocate from the memory stack instead. * * When an entry is freed, it is put back into the fifo list for * re-use. * * No attempt is ever made to shrink the amount of memory associated * with a CoglMagazine. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-memory-stack-private.h" #include "cogl-magazine-private.h" #include #define ROUND_UP_8(X) ((X + (8 - 1)) & ~(8 - 1)) CoglMagazine * _cogl_magazine_new (size_t chunk_size, int initial_chunk_count) { CoglMagazine *magazine = g_new0 (CoglMagazine, 1); chunk_size = MAX (chunk_size, sizeof (CoglMagazineChunk)); chunk_size = ROUND_UP_8 (chunk_size); magazine->chunk_size = chunk_size; magazine->stack = _cogl_memory_stack_new (chunk_size * initial_chunk_count); magazine->head = NULL; return magazine; } void _cogl_magazine_free (CoglMagazine *magazine) { _cogl_memory_stack_free (magazine->stack); g_free (magazine); } muffin-6.4.1/cogl/cogl/cogl-pipeline-layer-state-private.h0000664000175000017500000001241714723361714022417 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_LAYER_STATE_PRIVATE_H #define __COGL_PIPELINE_LAYER_STATE_PRIVATE_H #include "cogl-pipeline-layer-state.h" #include "cogl-pipeline-private.h" CoglPipelineLayer * _cogl_pipeline_set_layer_unit (CoglPipeline *required_owner, CoglPipelineLayer *layer, int unit_index); CoglPipelineFilter _cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, int layer_index); CoglPipelineFilter _cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, int layer_index); gboolean _cogl_pipeline_layer_texture_data_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1, CoglPipelineEvalFlags flags); gboolean _cogl_pipeline_layer_combine_state_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_combine_constant_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_sampler_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_user_matrix_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_point_sprite_coords_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_vertex_snippets_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); gboolean _cogl_pipeline_layer_fragment_snippets_equal (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); void _cogl_pipeline_layer_hash_unit_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_texture_data_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_sampler_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_combine_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_combine_constant_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_user_matrix_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_point_sprite_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_vertex_snippets_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); void _cogl_pipeline_layer_hash_fragment_snippets_state (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); #endif /* __COGL_PIPELINE_LAYER_STATE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-pipeline-state.h0000664000175000017500000006435014723361714017640 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PIPELINE_STATE_H__ #define __COGL_PIPELINE_STATE_H__ #include #include #include G_BEGIN_DECLS /** * cogl_pipeline_set_color: * @pipeline: A #CoglPipeline object * @color: The components of the color * * Sets the basic color of the pipeline, used when no lighting is enabled. * * Note that if you don't add any layers to the pipeline then the color * will be blended unmodified with the destination; the default blend * expects premultiplied colors: for example, use (0.5, 0.0, 0.0, 0.5) for * semi-transparent red. See cogl_color_premultiply(). * * The default value is (1.0, 1.0, 1.0, 1.0) * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_color (CoglPipeline *pipeline, const CoglColor *color); /** * cogl_pipeline_set_color4ub: * @pipeline: A #CoglPipeline object * @red: The red component * @green: The green component * @blue: The blue component * @alpha: The alpha component * * Sets the basic color of the pipeline, used when no lighting is enabled. * * The default value is (0xff, 0xff, 0xff, 0xff) * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_color4ub (CoglPipeline *pipeline, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); /** * cogl_pipeline_set_color4f: * @pipeline: A #CoglPipeline object * @red: The red component * @green: The green component * @blue: The blue component * @alpha: The alpha component * * Sets the basic color of the pipeline, used when no lighting is enabled. * * The default value is (1.0, 1.0, 1.0, 1.0) * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_color4f (CoglPipeline *pipeline, float red, float green, float blue, float alpha); /** * cogl_pipeline_get_color: * @pipeline: A #CoglPipeline object * @color: (out): The location to store the color * * Retrieves the current pipeline color. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_get_color (CoglPipeline *pipeline, CoglColor *color); /** * CoglPipelineAlphaFunc: * @COGL_PIPELINE_ALPHA_FUNC_NEVER: Never let the fragment through. * @COGL_PIPELINE_ALPHA_FUNC_LESS: Let the fragment through if the incoming * alpha value is less than the reference alpha value * @COGL_PIPELINE_ALPHA_FUNC_EQUAL: Let the fragment through if the incoming * alpha value equals the reference alpha value * @COGL_PIPELINE_ALPHA_FUNC_LEQUAL: Let the fragment through if the incoming * alpha value is less than or equal to the reference alpha value * @COGL_PIPELINE_ALPHA_FUNC_GREATER: Let the fragment through if the incoming * alpha value is greater than the reference alpha value * @COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL: Let the fragment through if the incoming * alpha value does not equal the reference alpha value * @COGL_PIPELINE_ALPHA_FUNC_GEQUAL: Let the fragment through if the incoming * alpha value is greater than or equal to the reference alpha value. * @COGL_PIPELINE_ALPHA_FUNC_ALWAYS: Always let the fragment through. * * Alpha testing happens before blending primitives with the framebuffer and * gives an opportunity to discard fragments based on a comparison with the * incoming alpha value and a reference alpha value. The #CoglPipelineAlphaFunc * determines how the comparison is done. */ typedef enum { COGL_PIPELINE_ALPHA_FUNC_NEVER = 0x0200, COGL_PIPELINE_ALPHA_FUNC_LESS = 0x0201, COGL_PIPELINE_ALPHA_FUNC_EQUAL = 0x0202, COGL_PIPELINE_ALPHA_FUNC_LEQUAL = 0x0203, COGL_PIPELINE_ALPHA_FUNC_GREATER = 0x0204, COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL = 0x0205, COGL_PIPELINE_ALPHA_FUNC_GEQUAL = 0x0206, COGL_PIPELINE_ALPHA_FUNC_ALWAYS = 0x0207 } CoglPipelineAlphaFunc; /* NB: these values come from the equivalents in gl.h */ /** * cogl_pipeline_set_alpha_test_function: * @pipeline: A #CoglPipeline object * @alpha_func: A @CoglPipelineAlphaFunc constant * @alpha_reference: A reference point that the chosen alpha function uses * to compare incoming fragments to. * * Before a primitive is blended with the framebuffer, it goes through an * alpha test stage which lets you discard fragments based on the current * alpha value. This function lets you change the function used to evaluate * the alpha channel, and thus determine which fragments are discarded * and which continue on to the blending stage. * * The default is %COGL_PIPELINE_ALPHA_FUNC_ALWAYS * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, CoglPipelineAlphaFunc alpha_func, float alpha_reference); /** * cogl_pipeline_get_alpha_test_function: * @pipeline: A #CoglPipeline object * * Return value: The alpha test function of @pipeline. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglPipelineAlphaFunc cogl_pipeline_get_alpha_test_function (CoglPipeline *pipeline); /** * cogl_pipeline_get_alpha_test_reference: * @pipeline: A #CoglPipeline object * * Return value: The alpha test reference value of @pipeline. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT float cogl_pipeline_get_alpha_test_reference (CoglPipeline *pipeline); /** * cogl_pipeline_set_blend: * @pipeline: A #CoglPipeline object * @blend_string: A Cogl blend string * describing the desired blend function. * @error: return location for a #GError that may report lack of driver * support if you give separate blend string statements for the alpha * channel and RGB channels since some drivers, or backends such as * GLES 1.1, don't support this feature. May be %NULL, in which case a * warning will be printed out using GLib's logging facilities if an * error is encountered. * * If not already familiar; please refer here * for an overview of what blend strings are, and their syntax. * * Blending occurs after the alpha test function, and combines fragments with * the framebuffer. * Currently the only blend function Cogl exposes is ADD(). So any valid * blend statements will be of the form: * * |[ * <channel-mask>=ADD(SRC_COLOR*(<factor>), DST_COLOR*(<factor>)) * ]| * * This is the list of source-names usable as blend factors: * * SRC_COLOR: The color of the in comming fragment * DST_COLOR: The color of the framebuffer * CONSTANT: The constant set via cogl_pipeline_set_blend_constant() * * * The source names can be used according to the * color-source and factor syntax, * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would * "(CONSTANT[RGB])" * * These can also be used as factors: * * 0: (0, 0, 0, 0) * 1: (1, 1, 1, 1) * SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A]) * * * Remember; all color components are normalized to the range [0, 1] * before computing the result of blending. * * * Blend Strings/1 * Blend a non-premultiplied source over a destination with * premultiplied alpha: * * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))" * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" * * * * * Blend Strings/2 * Blend a premultiplied source over a destination with * premultiplied alpha * * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" * * * * The default blend string is: * |[ * RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A])) * ]| * * That gives normal alpha-blending when the calculated color for the pipeline * is in premultiplied form. * * Return value: %TRUE if the blend string was successfully parsed, and the * described blending is supported by the underlying driver/hardware. If * there was an error, %FALSE is returned and @error is set accordingly (if * present). * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT gboolean cogl_pipeline_set_blend (CoglPipeline *pipeline, const char *blend_string, GError **error); /** * cogl_pipeline_set_blend_constant: * @pipeline: A #CoglPipeline object * @constant_color: The constant color you want * * When blending is setup to reference a CONSTANT blend factor then * blending will depend on the constant set with this function. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_blend_constant (CoglPipeline *pipeline, const CoglColor *constant_color); /** * cogl_pipeline_set_point_size: * @pipeline: a #CoglPipeline pointer * @point_size: the new point size. * * Changes the size of points drawn when %COGL_VERTICES_MODE_POINTS is * used with the attribute buffer API. Note that typically the GPU * will only support a limited minimum and maximum range of point * sizes. If the chosen point size is outside that range then the * nearest value within that range will be used instead. The size of a * point is in screen space so it will be the same regardless of any * transformations. * * If the point size is set to 0.0 then drawing points with the * pipeline will have undefined results. This is the default value so * if an application wants to draw points it must make sure to use a * pipeline that has an explicit point size set on it. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_point_size (CoglPipeline *pipeline, float point_size); /** * cogl_pipeline_get_point_size: * @pipeline: a #CoglPipeline pointer * * Get the size of points drawn when %COGL_VERTICES_MODE_POINTS is * used with the vertex buffer API. * * Return value: the point size of the @pipeline. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT float cogl_pipeline_get_point_size (CoglPipeline *pipeline); /** * cogl_pipeline_set_per_vertex_point_size: * @pipeline: a #CoglPipeline pointer * @enable: whether to enable per-vertex point size * @error: a location to store a #GError if the change failed * * Sets whether to use a per-vertex point size or to use the value set * by cogl_pipeline_set_point_size(). If per-vertex point size is * enabled then the point size can be set for an individual point * either by drawing with a #CoglAttribute with the name * ‘cogl_point_size_in’ or by writing to the GLSL builtin * ‘cogl_point_size_out’ from a vertex shader snippet. * * If per-vertex point size is enabled and this attribute is not used * and cogl_point_size_out is not written to then the results are * undefined. * * Since: 2.0 * Stability: Unstable * Return value: %TRUE if the change suceeded or %FALSE otherwise */ COGL_EXPORT gboolean cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, gboolean enable, GError **error); /** * cogl_pipeline_get_per_vertex_point_size: * @pipeline: a #CoglPipeline pointer * * Since: 2.0 * Stability: Unstable * Return value: %TRUE if the pipeline has per-vertex point size * enabled or %FALSE otherwise. The per-vertex point size can be * enabled with cogl_pipeline_set_per_vertex_point_size(). */ COGL_EXPORT gboolean cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline); /** * cogl_pipeline_get_user_program: * @pipeline: a #CoglPipeline object. * * Queries what user program has been associated with the given * @pipeline using cogl_pipeline_set_user_program(). * * Return value: (transfer none): The current user program or %NULL. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglHandle cogl_pipeline_get_user_program (CoglPipeline *pipeline); /** * cogl_pipeline_set_user_program: * @pipeline: a #CoglPipeline object. * @program: A #CoglHandle to a linked CoglProgram * * Associates a linked CoglProgram with the given pipeline so that the * program can take full control of vertex and/or fragment processing. * * This is an example of how it can be used to associate an ARBfp * program with a #CoglPipeline: * |[ * CoglHandle shader; * CoglHandle program; * CoglPipeline *pipeline; * * shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); * cogl_shader_source (shader, * "!!ARBfp1.0\n" * "MOV result.color,fragment.color;\n" * "END\n"); * * program = cogl_create_program (); * cogl_program_attach_shader (program, shader); * cogl_program_link (program); * * pipeline = cogl_pipeline_new (); * cogl_pipeline_set_user_program (pipeline, program); * * cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); * cogl_rectangle (0, 0, 100, 100); * ]| * * It is possibly worth keeping in mind that this API is not part of * the long term design for how we want to expose shaders to Cogl * developers (We are planning on deprecating the cogl_program and * cogl_shader APIs in favour of a "snippet" framework) but in the * meantime we hope this will handle most practical GLSL and ARBfp * requirements. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_user_program (CoglPipeline *pipeline, CoglHandle program); /** * cogl_pipeline_set_depth_state: (skip) * @pipeline: A #CoglPipeline object * @state: A #CoglDepthState struct * @error: A #GError to report failures to setup the given @state. * * This commits all the depth state configured in @state struct to the * given @pipeline. The configuration values are copied into the * pipeline so there is no requirement to keep the #CoglDepthState * struct around if you don't need it any more. * * Note: Since some platforms do not support the depth range feature * it is possible for this function to fail and report an @error. * * Returns: TRUE if the GPU supports all the given @state else %FALSE * and returns an @error. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT gboolean cogl_pipeline_set_depth_state (CoglPipeline *pipeline, const CoglDepthState *state, GError **error); /** * cogl_pipeline_get_depth_state: (skip) * @pipeline: A #CoglPipeline object * @state_out: (out): A destination #CoglDepthState struct * * Retrieves the current depth state configuration for the given * @pipeline as previously set using cogl_pipeline_set_depth_state(). * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_get_depth_state (CoglPipeline *pipeline, CoglDepthState *state_out); /** * CoglPipelineCullFaceMode: * @COGL_PIPELINE_CULL_FACE_MODE_NONE: Neither face will be * culled. This is the default. * @COGL_PIPELINE_CULL_FACE_MODE_FRONT: Front faces will be culled. * @COGL_PIPELINE_CULL_FACE_MODE_BACK: Back faces will be culled. * @COGL_PIPELINE_CULL_FACE_MODE_BOTH: All faces will be culled. * * Specifies which faces should be culled. This can be set on a * pipeline using cogl_pipeline_set_cull_face_mode(). */ typedef enum { COGL_PIPELINE_CULL_FACE_MODE_NONE, COGL_PIPELINE_CULL_FACE_MODE_FRONT, COGL_PIPELINE_CULL_FACE_MODE_BACK, COGL_PIPELINE_CULL_FACE_MODE_BOTH } CoglPipelineCullFaceMode; /** * cogl_pipeline_set_cull_face_mode: * @pipeline: A #CoglPipeline * @cull_face_mode: The new mode to set * * Sets which faces will be culled when drawing. Face culling can be * used to increase efficiency by avoiding drawing faces that would * get overridden. For example, if a model has gaps so that it is * impossible to see the inside then faces which are facing away from * the screen will never be seen so there is no point in drawing * them. This can be acheived by setting the cull face mode to * %COGL_PIPELINE_CULL_FACE_MODE_BACK. * * Face culling relies on the primitives being drawn with a specific * order to represent which faces are facing inside and outside the * model. This order can be specified by calling * cogl_pipeline_set_front_face_winding(). * * Status: Unstable * Since: 2.0 */ COGL_EXPORT void cogl_pipeline_set_cull_face_mode (CoglPipeline *pipeline, CoglPipelineCullFaceMode cull_face_mode); /** * cogl_pipeline_get_cull_face_mode: * * Return value: the cull face mode that was previously set with * cogl_pipeline_set_cull_face_mode(). * * Status: Unstable * Since: 2.0 */ COGL_EXPORT CoglPipelineCullFaceMode cogl_pipeline_get_cull_face_mode (CoglPipeline *pipeline); /** * cogl_pipeline_set_front_face_winding: * @pipeline: a #CoglPipeline * @front_winding: the winding order * * The order of the vertices within a primitive specifies whether it * is considered to be front or back facing. This function specifies * which order is considered to be the front * faces. %COGL_WINDING_COUNTER_CLOCKWISE sets the front faces to * primitives with vertices in a counter-clockwise order and * %COGL_WINDING_CLOCKWISE sets them to be clockwise. The default is * %COGL_WINDING_COUNTER_CLOCKWISE. * * Status: Unstable * Since: 2.0 */ COGL_EXPORT void cogl_pipeline_set_front_face_winding (CoglPipeline *pipeline, CoglWinding front_winding); /** * cogl_pipeline_get_front_face_winding: * @pipeline: a #CoglPipeline * * The order of the vertices within a primitive specifies whether it * is considered to be front or back facing. This function specifies * which order is considered to be the front * faces. %COGL_WINDING_COUNTER_CLOCKWISE sets the front faces to * primitives with vertices in a counter-clockwise order and * %COGL_WINDING_CLOCKWISE sets them to be clockwise. The default is * %COGL_WINDING_COUNTER_CLOCKWISE. * * Returns: The @pipeline front face winding * * Status: Unstable * Since: 2.0 */ COGL_EXPORT CoglWinding cogl_pipeline_get_front_face_winding (CoglPipeline *pipeline); /** * cogl_pipeline_set_uniform_1f: * @pipeline: A #CoglPipeline object * @uniform_location: The uniform's location identifier * @value: The new value for the uniform * * Sets a new value for the uniform at @uniform_location. If this * pipeline has a user program attached and is later used as a source * for drawing, the given value will be assigned to the uniform which * can be accessed from the shader's source. The value for * @uniform_location should be retrieved from the string name of the * uniform by calling cogl_pipeline_get_uniform_location(). * * This function should be used to set uniforms that are of type * float. It can also be used to set a single member of a float array * uniform. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline, int uniform_location, float value); /** * cogl_pipeline_set_uniform_1i: * @pipeline: A #CoglPipeline object * @uniform_location: The uniform's location identifier * @value: The new value for the uniform * * Sets a new value for the uniform at @uniform_location. If this * pipeline has a user program attached and is later used as a source * for drawing, the given value will be assigned to the uniform which * can be accessed from the shader's source. The value for * @uniform_location should be retrieved from the string name of the * uniform by calling cogl_pipeline_get_uniform_location(). * * This function should be used to set uniforms that are of type * int. It can also be used to set a single member of a int array * uniform or a sampler uniform. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline, int uniform_location, int value); /** * cogl_pipeline_set_uniform_float: * @pipeline: A #CoglPipeline object * @uniform_location: The uniform's location identifier * @n_components: The number of components in the corresponding uniform's type * @count: The number of values to set * @value: Pointer to the new values to set * * Sets new values for the uniform at @uniform_location. If this * pipeline has a user program attached and is later used as a source * for drawing, the given values will be assigned to the uniform which * can be accessed from the shader's source. The value for * @uniform_location should be retrieved from the string name of the * uniform by calling cogl_pipeline_get_uniform_location(). * * This function can be used to set any floating point type uniform, * including float arrays and float vectors. For example, to set a * single vec4 uniform you would use 4 for @n_components and 1 for * @count. To set an array of 8 float values, you could use 1 for * @n_components and 8 for @count. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_uniform_float (CoglPipeline *pipeline, int uniform_location, int n_components, int count, const float *value); /** * cogl_pipeline_set_uniform_int: * @pipeline: A #CoglPipeline object * @uniform_location: The uniform's location identifier * @n_components: The number of components in the corresponding uniform's type * @count: The number of values to set * @value: Pointer to the new values to set * * Sets new values for the uniform at @uniform_location. If this * pipeline has a user program attached and is later used as a source * for drawing, the given values will be assigned to the uniform which * can be accessed from the shader's source. The value for * @uniform_location should be retrieved from the string name of the * uniform by calling cogl_pipeline_get_uniform_location(). * * This function can be used to set any integer type uniform, * including int arrays and int vectors. For example, to set a single * ivec4 uniform you would use 4 for @n_components and 1 for * @count. To set an array of 8 int values, you could use 1 for * @n_components and 8 for @count. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_uniform_int (CoglPipeline *pipeline, int uniform_location, int n_components, int count, const int *value); /** * cogl_pipeline_set_uniform_matrix: * @pipeline: A #CoglPipeline object * @uniform_location: The uniform's location identifier * @dimensions: The size of the matrix * @count: The number of values to set * @transpose: Whether to transpose the matrix * @value: Pointer to the new values to set * * Sets new values for the uniform at @uniform_location. If this * pipeline has a user program attached and is later used as a source * for drawing, the given values will be assigned to the uniform which * can be accessed from the shader's source. The value for * @uniform_location should be retrieved from the string name of the * uniform by calling cogl_pipeline_get_uniform_location(). * * This function can be used to set any matrix type uniform, including * matrix arrays. For example, to set a single mat4 uniform you would * use 4 for @dimensions and 1 for @count. To set an array of 8 * mat3 values, you could use 3 for @dimensions and 8 for @count. * * If @transpose is %FALSE then the matrix is expected to be in * column-major order or if it is %TRUE then the matrix is in * row-major order. You can pass a #CoglMatrix by calling by passing * the result of cogl_matrix_get_array() in @value and setting * @transpose to %FALSE. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline, int uniform_location, int dimensions, int count, gboolean transpose, const float *value); /** * cogl_pipeline_add_snippet: (skip) * @pipeline: A #CoglPipeline * @snippet: The #CoglSnippet to add to the vertex processing hook * * Adds a shader snippet to @pipeline. The snippet will wrap around or * replace some part of the pipeline as defined by the hook point in * @snippet. Note that some hook points are specific to a layer and * must be added with cogl_pipeline_add_layer_snippet() instead. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_add_snippet (CoglPipeline *pipeline, CoglSnippet *snippet); G_END_DECLS #endif /* __COGL_PIPELINE_STATE_H__ */ muffin-6.4.1/cogl/cogl/meson.build0000664000175000017500000003074214723361714015762 0ustar fabiofabiocogl_cogl_includesubdir = join_paths(cogl_includesubdir, 'cogl') cogl_cogl_includedir = join_paths(cogl_includedir, 'cogl') cdata = configuration_data() cdata.set('COGL_HAS_GL', have_gl) cdata.set('CLUTTER_COGL_HAS_GL', have_gl) cdata.set('COGL_HAS_GLX_SUPPORT', have_glx) cdata.set('COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT', have_wayland) cdata.set('COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT', have_egl_xlib) cdata.set('COGL_HAS_EGL_SUPPORT', have_egl) cdata.set('COGL_HAS_X11', have_x11) cdata.set('COGL_HAS_X11_SUPPORT', have_x11) cdata.set('COGL_HAS_XLIB', have_x11) cdata.set('COGL_HAS_XLIB_SUPPORT', have_x11) cdata.set('COGL_HAS_TRACING', have_profiler) cogl_defines_h = configure_file( input: 'cogl-defines.h.meson', output: 'cogl-defines.h', configuration: cdata, install_dir: cogl_cogl_includedir, install: true, ) if have_gl cogl_gl_header_includes = ['GL/gl.h'] elif have_gles2 cogl_gl_header_includes = ['GLES2/gl2.h', 'GLES2/gl2ext.h'] else error('Neither GLES2 or OpenGL was enabled') endif cogl_gl_header_includes_string = '' foreach gl_header : cogl_gl_header_includes cogl_gl_header_includes_string += '#include <@0@>\n'.format(gl_header) endforeach built_headers = [] cdata = configuration_data() cdata.set('COGL_GL_HEADER_INCLUDES', cogl_gl_header_includes_string) cogl_gl_header_h = configure_file( input: 'cogl-gl-header.h.in', output: 'cogl-gl-header.h', configuration: cdata, install: false, ) built_headers += [cogl_gl_header_h] if have_egl cogl_egl_includes_string = '#include \n#include \n#include ' else cogl_egl_includes_string = '' endif cdata = configuration_data() cdata.set('COGL_EGL_INCLUDES', cogl_egl_includes_string) cogl_egl_defines_h = configure_file( input: 'cogl-egl-defines.h.in', output: 'cogl-egl-defines.h', configuration: cdata, install: false, ) built_headers += [cogl_gl_header_h] cogl_deprecated_headers = [ 'deprecated/cogl-material-compat.h', 'deprecated/cogl-shader.h', 'deprecated/cogl-clutter.h', 'deprecated/cogl-type-casts.h', 'deprecated/cogl-auto-texture.h', ] cogl_headers = [ 'cogl1-context.h', 'cogl-bitmap.h', 'cogl-color.h', 'cogl-context.h', 'cogl-framebuffer.h', 'cogl-matrix.h', 'cogl-object.h', 'cogl-offscreen.h', 'cogl-onscreen.h', 'cogl-pipeline.h', 'cogl-pipeline-state.h', 'cogl-pipeline-layer-state.h', 'cogl-pixel-format.h', 'cogl-texture.h', 'cogl-texture-2d.h', 'cogl-texture-2d-sliced.h', 'cogl-types.h', 'cogl-trace.h', 'cogl.h', ] cogl_nonintrospected_headers = [ 'cogl-renderer.h', 'cogl-swap-chain.h', 'cogl-onscreen-template.h', 'cogl-dma-buf-handle.h', 'cogl-display.h', 'cogl-snippet.h', 'cogl-index-buffer.h', 'cogl-attribute-buffer.h', 'cogl-indices.h', 'cogl-attribute.h', 'cogl-primitive.h', 'cogl-frame-info.h', 'cogl-output.h', 'cogl-matrix-stack.h', 'cogl-poll.h', 'cogl-sub-texture.h', 'cogl-atlas-texture.h', 'cogl-meta-texture.h', 'cogl-primitive-texture.h', 'cogl-depth-state.h', 'cogl-buffer.h', 'cogl-pixel-buffer.h', 'cogl-macros.h', 'cogl-fence.h', 'cogl-version.h', 'cogl-gtype-private.h', 'cogl-glib-source.h', ] cogl_nodist_headers = [ ] cogl_noop_driver_sources = [ 'driver/nop/cogl-driver-nop.c', 'driver/nop/cogl-framebuffer-nop-private.h', 'driver/nop/cogl-framebuffer-nop.c', 'driver/nop/cogl-attribute-nop-private.h', 'driver/nop/cogl-attribute-nop.c', 'driver/nop/cogl-clip-stack-nop-private.h', 'driver/nop/cogl-clip-stack-nop.c', 'driver/nop/cogl-texture-2d-nop-private.h', 'driver/nop/cogl-texture-2d-nop.c', ] cogl_gl_prototype_headers = [ 'gl-prototypes/cogl-gles2-functions.h', 'gl-prototypes/cogl-core-functions.h', 'gl-prototypes/cogl-in-gles-core-functions.h', 'gl-prototypes/cogl-in-gles2-core-functions.h', 'gl-prototypes/cogl-glsl-functions.h', ] cogl_common_driver_sources = [ 'driver/gl/cogl-util-gl-private.h', 'driver/gl/cogl-util-gl.c', 'driver/gl/cogl-framebuffer-gl-private.h', 'driver/gl/cogl-framebuffer-gl.c', 'driver/gl/cogl-texture-gl-private.h', 'driver/gl/cogl-texture-gl.c', 'driver/gl/cogl-texture-2d-gl-private.h', 'driver/gl/cogl-texture-2d-gl.c', 'driver/gl/cogl-attribute-gl-private.h', 'driver/gl/cogl-attribute-gl.c', 'driver/gl/cogl-clip-stack-gl-private.h', 'driver/gl/cogl-clip-stack-gl.c', 'driver/gl/cogl-buffer-gl-private.h', 'driver/gl/cogl-buffer-gl.c', 'driver/gl/cogl-bitmap-gl-private.h', 'driver/gl/cogl-bitmap-gl.c', 'driver/gl/cogl-pipeline-opengl.c', 'driver/gl/cogl-pipeline-opengl-private.h', 'driver/gl/cogl-pipeline-fragend-glsl.c', 'driver/gl/cogl-pipeline-fragend-glsl-private.h', 'driver/gl/cogl-pipeline-vertend-glsl.c', 'driver/gl/cogl-pipeline-vertend-glsl-private.h', 'driver/gl/cogl-pipeline-progend-glsl.c', 'driver/gl/cogl-pipeline-progend-glsl-private.h', ] gl_driver_sources = [ 'driver/gl/gl/cogl-driver-gl.c', 'driver/gl/gl/cogl-texture-driver-gl.c', ] gles_driver_sources = [ 'driver/gl/gles/cogl-driver-gles.c', 'driver/gl/gles/cogl-texture-driver-gles.c', ] cogl_driver_sources = [ cogl_noop_driver_sources, cogl_common_driver_sources, ] if have_gl cogl_driver_sources += gl_driver_sources endif if have_gles2 cogl_driver_sources += gles_driver_sources endif cogl_sources = [ cogl_driver_sources, 'winsys/cogl-winsys-private.h', 'winsys/cogl-winsys.c', 'cogl-private.h', 'cogl-i18n-private.h', 'cogl-debug.h', 'cogl-debug-options.h', 'cogl-dma-buf-handle.c', 'cogl-gpu-info.c', 'cogl-gpu-info-private.h', 'cogl-context-private.h', 'cogl-context.c', 'cogl-renderer-private.h', 'cogl-renderer.h', 'cogl-renderer.c', 'cogl-swap-chain-private.h', 'cogl-swap-chain.h', 'cogl-swap-chain.c', 'cogl-onscreen-template-private.h', 'cogl-onscreen-template.h', 'cogl-onscreen-template.c', 'cogl-display-private.h', 'cogl-display.h', 'cogl-display.c', 'cogl-driver.h', 'cogl.c', 'cogl-pixel-format.c', 'cogl-object-private.h', 'cogl-object.h', 'cogl-object.c', 'cogl-util.h', 'cogl-util.c', 'cogl-bitmap-private.h', 'cogl-bitmap.c', 'cogl-bitmap-conversion.c', 'cogl-bitmap-packing.h', 'cogl-primitives-private.h', 'cogl-primitives.c', 'cogl-bitmap-pixbuf.c', 'cogl-clip-stack.h', 'cogl-clip-stack.c', 'cogl-feature-private.h', 'cogl-feature-private.c', 'cogl-color-private.h', 'cogl-color.c', 'cogl-buffer-private.h', 'cogl-buffer.c', 'cogl-pixel-buffer-private.h', 'cogl-pixel-buffer.c', 'cogl-index-buffer-private.h', 'cogl-index-buffer.c', 'cogl-attribute-buffer-private.h', 'cogl-attribute-buffer.c', 'cogl-indices-private.h', 'cogl-indices.c', 'cogl-attribute-private.h', 'cogl-attribute.c', 'cogl-primitive-private.h', 'cogl-primitive.c', 'cogl-matrix.c', 'cogl-matrix-private.h', 'cogl-matrix-stack.c', 'cogl-matrix-stack-private.h', 'cogl-depth-state.c', 'cogl-depth-state-private.h', 'cogl-node.c', 'cogl-node-private.h', 'cogl-pipeline.c', 'cogl-pipeline-private.h', 'cogl-pipeline-layer.c', 'cogl-pipeline-layer-private.h', 'cogl-pipeline-state.c', 'cogl-pipeline-layer-state-private.h', 'cogl-pipeline-layer-state.c', 'cogl-pipeline-state-private.h', 'cogl-pipeline-debug.c', 'cogl-glsl-shader.c', 'cogl-glsl-shader-private.h', 'cogl-glsl-shader-boilerplate.h', 'cogl-pipeline-snippet-private.h', 'cogl-pipeline-snippet.c', 'cogl-pipeline-cache.h', 'cogl-pipeline-cache.c', 'cogl-pipeline-hash-table.h', 'cogl-pipeline-hash-table.c', 'cogl-sampler-cache.c', 'cogl-sampler-cache-private.h', 'cogl-blend-string.c', 'cogl-blend-string.h', 'cogl-debug.c', 'cogl-trace.c', 'cogl-sub-texture-private.h', 'cogl-texture-private.h', 'cogl-texture-2d-private.h', 'cogl-texture-2d-sliced-private.h', 'cogl-texture-driver.h', 'cogl-sub-texture.c', 'cogl-texture.c', 'cogl-texture-2d.c', 'cogl-texture-2d-sliced.c', 'cogl-rectangle-map.h', 'cogl-rectangle-map.c', 'cogl-atlas.h', 'cogl-atlas.c', 'cogl-atlas-texture-private.h', 'cogl-atlas-texture.c', 'cogl-meta-texture.c', 'cogl-primitive-texture.c', 'cogl-blit.h', 'cogl-blit.c', 'cogl-spans.h', 'cogl-spans.c', 'cogl-journal-private.h', 'cogl-journal.c', 'cogl-frame-info-private.h', 'cogl-frame-info.c', 'cogl-framebuffer-private.h', 'cogl-framebuffer.c', 'cogl-onscreen-private.h', 'cogl-onscreen.c', 'cogl-output-private.h', 'cogl-output.c', 'cogl-profile.h', 'cogl-profile.c', 'cogl-flags.h', 'cogl-bitmask.h', 'cogl-bitmask.c', 'cogl-gtype.c', 'cogl-gtype-private.h', 'cogl-point-in-poly-private.h', 'cogl-point-in-poly.c', 'cogl-list.c', 'cogl-list.h', 'cogl-boxed-value.h', 'cogl-boxed-value.c', 'cogl-snippet-private.h', 'cogl-snippet.c', 'cogl-poll-private.h', 'cogl-poll.c', 'gl-prototypes/cogl-all-functions.h', 'cogl-memory-stack-private.h', 'cogl-memory-stack.c', 'cogl-magazine-private.h', 'cogl-magazine.c', 'cogl-closure-list-private.h', 'cogl-closure-list.c', 'cogl-fence.c', 'cogl-fence-private.h', 'deprecated/cogl-material-compat.c', 'deprecated/cogl-program.c', 'deprecated/cogl-program-private.h', 'deprecated/cogl-auto-texture.c', 'deprecated/cogl-shader-private.h', 'deprecated/cogl-shader.c', 'deprecated/cogl-clutter.c', 'cogl-glib-source.c', 'cogl-mutter.h', ] if have_x11 cogl_nonintrospected_headers += [ 'winsys/cogl-texture-pixmap-x11.h', 'cogl-xlib.h', ] cogl_sources += [ 'cogl-x11-renderer-private.h', 'cogl-xlib-private.h', 'cogl-xlib-renderer-private.h', 'cogl-xlib-renderer.c', 'winsys/cogl-texture-pixmap-x11-private.h', 'winsys/cogl-texture-pixmap-x11.c', ] cogl_headers += [ 'cogl-xlib-renderer.h' ] endif if have_glx cogl_nonintrospected_headers += [ 'winsys/cogl-glx.h', ] cogl_sources += [ 'winsys/cogl-glx-display-private.h', 'winsys/cogl-glx-renderer-private.h', 'winsys/cogl-winsys-glx-feature-functions.h', 'winsys/cogl-winsys-glx-private.h', 'winsys/cogl-winsys-glx.c', ] endif if have_wayland cogl_nonintrospected_headers += [ 'cogl-wayland-server.h', ] endif if have_egl cogl_nonintrospected_headers += [ 'cogl-egl.h', cogl_egl_defines_h, ] cogl_sources += [ 'cogl-egl-private.h', 'winsys/cogl-winsys-egl.c', 'winsys/cogl-winsys-egl-feature-functions.h', 'winsys/cogl-winsys-egl-private.h', ] endif if have_egl_xlib cogl_sources += [ 'winsys/cogl-winsys-egl-x11.c', 'winsys/cogl-winsys-egl-x11-private.h', ] endif cogl_introspected_headers = [ cogl_headers, cogl_deprecated_headers, ] cogl_headers_all = [ cogl_introspected_headers, cogl_nonintrospected_headers, cogl_deprecated_headers, ] cogl_test_deps = [] if have_cogl_tests cogl_test_deps += [libmutter_cogl_test_fixtures_dep] endif libmutter_cogl_name = 'muffin-cogl-' + libmutter_api_version libmutter_cogl = shared_library(libmutter_cogl_name, sources: [cogl_sources, cogl_headers_all], version: '0.0.0', soversion: 0, c_args: cogl_c_args, include_directories: cogl_includepath, dependencies: [cogl_deps, cogl_test_deps], gnu_symbol_visibility: 'hidden', install_rpath: pkglibdir, install_dir: pkglibdir, install: true, ) libmutter_cogl_dep = declare_dependency( dependencies: [cogl_deps], link_with: libmutter_cogl, ) if have_introspection libmutter_cogl_gir = gnome.generate_gir(libmutter_cogl, sources: cogl_introspected_headers, nsversion: libmutter_api_version, namespace: 'Cogl', includes: ['cairo-1.0', 'GL-1.0', 'GObject-2.0', 'Graphene-1.0'], dependencies: [cogl_deps], extra_args: introspection_args + [ '-UCOGL_COMPILATION', '-D__COGL_H_INSIDE__', '-D__COGL_XLIB_H_INSIDE__', '-D__COGL_EGL_H_INSIDE__', '-D__COGL_GLX_H_INSIDE__', '-DCOGL_GIR_SCANNING', ], header: 'cogl/cogl.h', install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true ) endif install_headers([ cogl_headers, cogl_nonintrospected_headers, ], subdir: cogl_cogl_includesubdir) install_headers([ cogl_deprecated_headers, ], subdir: join_paths(cogl_cogl_includesubdir, 'deprecated')) install_headers(cogl_gl_prototype_headers, subdir: join_paths(cogl_cogl_includesubdir, 'gl-prototypes')) pkg.generate(libmutter_cogl, name: 'Cogl', filebase: libmutter_cogl_name, description: 'An object oriented GL/GLES Abstraction/Utility Layer in mutter', libraries: [m_dep], subdirs: join_paths(pkgname, 'cogl'), requires: [cogl_pkg_deps], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, ], install_dir: pcdir, ) muffin-6.4.1/cogl/cogl/cogl-pipeline.c0000664000175000017500000027376414723361714016530 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-context-private.h" #include "cogl-object.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-state-private.h" #include "cogl-pipeline-layer-state-private.h" #include "cogl-texture-private.h" #include "cogl-blend-string.h" #include "cogl-journal-private.h" #include "cogl-color-private.h" #include "cogl-util.h" #include "cogl-profile.h" #include "cogl-depth-state-private.h" #include "cogl1-context.h" #include "cogl-gtype-private.h" #include #include #include static void _cogl_pipeline_free (CoglPipeline *tex); static void recursively_free_layer_caches (CoglPipeline *pipeline); static gboolean _cogl_pipeline_is_weak (CoglPipeline *pipeline); const CoglPipelineFragend *_cogl_pipeline_fragend; const CoglPipelineVertend *_cogl_pipeline_vertend; const CoglPipelineProgend *_cogl_pipeline_progend; #include "driver/gl/cogl-pipeline-fragend-glsl-private.h" #include "driver/gl/cogl-pipeline-vertend-glsl-private.h" #include "driver/gl/cogl-pipeline-progend-glsl-private.h" COGL_OBJECT_DEFINE (Pipeline, pipeline); COGL_GTYPE_DEFINE_CLASS (Pipeline, pipeline); /* * This initializes the first pipeline owned by the Cogl context. All * subsequently instantiated pipelines created via the cogl_pipeline_new() * API will initially be a copy of this pipeline. * * The default pipeline is the topmost ancester for all pipelines. */ void _cogl_pipeline_init_default_pipeline (void) { /* Create new - blank - pipeline */ CoglPipeline *pipeline = g_slice_new0 (CoglPipeline); /* XXX: NB: It's important that we zero this to avoid polluting * pipeline hash values with un-initialized data */ CoglPipelineBigState *big_state = g_slice_new0 (CoglPipelineBigState); CoglPipelineAlphaFuncState *alpha_state = &big_state->alpha_state; CoglPipelineBlendState *blend_state = &big_state->blend_state; CoglPipelineCullFaceState *cull_face_state = &big_state->cull_face_state; CoglPipelineUniformsState *uniforms_state = &big_state->uniforms_state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Take this opportunity to setup the backends... */ _cogl_pipeline_fragend = &_cogl_pipeline_glsl_fragend; _cogl_pipeline_progend = &_cogl_pipeline_glsl_progend; _cogl_pipeline_vertend = &_cogl_pipeline_glsl_vertend; _cogl_pipeline_node_init (COGL_NODE (pipeline)); pipeline->is_weak = FALSE; pipeline->journal_ref_count = 0; pipeline->differences = COGL_PIPELINE_STATE_ALL_SPARSE; pipeline->real_blend_enable = FALSE; pipeline->layer_differences = NULL; pipeline->n_layers = 0; pipeline->big_state = big_state; pipeline->has_big_state = TRUE; pipeline->static_breadcrumb = "default pipeline"; pipeline->has_static_breadcrumb = TRUE; pipeline->age = 0; /* Use the same defaults as the GL spec... */ cogl_color_init_from_4ub (&pipeline->color, 0xff, 0xff, 0xff, 0xff); /* Use the same defaults as the GL spec... */ alpha_state->alpha_func = COGL_PIPELINE_ALPHA_FUNC_ALWAYS; alpha_state->alpha_func_reference = 0.0; /* Not the same as the GL default, but seems saner... */ #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) blend_state->blend_equation_rgb = GL_FUNC_ADD; blend_state->blend_equation_alpha = GL_FUNC_ADD; blend_state->blend_src_factor_alpha = GL_ONE; blend_state->blend_dst_factor_alpha = GL_ONE_MINUS_SRC_ALPHA; cogl_color_init_from_4ub (&blend_state->blend_constant, 0x00, 0x00, 0x00, 0x00); #endif blend_state->blend_src_factor_rgb = GL_ONE; blend_state->blend_dst_factor_rgb = GL_ONE_MINUS_SRC_ALPHA; big_state->user_program = NULL; cogl_depth_state_init (&big_state->depth_state); big_state->point_size = 0.0f; cull_face_state->mode = COGL_PIPELINE_CULL_FACE_MODE_NONE; cull_face_state->front_winding = COGL_WINDING_COUNTER_CLOCKWISE; _cogl_bitmask_init (&uniforms_state->override_mask); _cogl_bitmask_init (&uniforms_state->changed_mask); uniforms_state->override_values = NULL; ctx->default_pipeline = _cogl_pipeline_object_new (pipeline); } static void _cogl_pipeline_unparent (CoglNode *pipeline) { /* Chain up */ _cogl_pipeline_node_unparent_real (pipeline); } static gboolean recursively_free_layer_caches_cb (CoglNode *node, void *user_data) { recursively_free_layer_caches (COGL_PIPELINE (node)); return TRUE; } /* This recursively frees the layers_cache of a pipeline and all of * its descendants. * * For instance if we change a pipelines ->layer_differences list * then that pipeline and all of its descendants may now have * incorrect layer caches. */ static void recursively_free_layer_caches (CoglPipeline *pipeline) { /* Note: we maintain the invariable that if a pipeline already has a * dirty layers_cache then so do all of its descendants. */ if (pipeline->layers_cache_dirty) return; if (G_UNLIKELY (pipeline->layers_cache != pipeline->short_layers_cache)) g_slice_free1 (sizeof (CoglPipelineLayer *) * pipeline->n_layers, pipeline->layers_cache); pipeline->layers_cache_dirty = TRUE; _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), recursively_free_layer_caches_cb, NULL); } static void _cogl_pipeline_set_parent (CoglPipeline *pipeline, CoglPipeline *parent, gboolean take_strong_reference) { /* Chain up */ _cogl_pipeline_node_set_parent_real (COGL_NODE (pipeline), COGL_NODE (parent), _cogl_pipeline_unparent, take_strong_reference); /* Since we just changed the ancestry of the pipeline its cache of * layers could now be invalid so free it... */ if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) recursively_free_layer_caches (pipeline); } static void _cogl_pipeline_promote_weak_ancestors (CoglPipeline *strong) { CoglNode *n; g_return_if_fail (!strong->is_weak); /* If the parent of strong is weak, then we want to promote it by taking a reference on strong's grandparent. We don't need to take a reference on strong's direct parent */ if (COGL_NODE (strong)->parent == NULL) return; for (n = COGL_NODE (strong)->parent; /* We can assume that all weak pipelines have a parent */ COGL_PIPELINE (n)->is_weak; n = n->parent) /* 'n' is weak so we take a reference on its parent */ cogl_object_ref (n->parent); } static void _cogl_pipeline_revert_weak_ancestors (CoglPipeline *strong) { CoglNode *n; g_return_if_fail (!strong->is_weak); /* This reverts the effect of calling _cogl_pipeline_promote_weak_ancestors */ if (COGL_NODE (strong)->parent == NULL) return; for (n = COGL_NODE (strong)->parent; /* We can assume that all weak pipelines have a parent */ COGL_PIPELINE (n)->is_weak; n = n->parent) /* 'n' is weak so we unref its parent */ cogl_object_unref (n->parent); } /* XXX: Always have an eye out for opportunities to lower the cost of * cogl_pipeline_copy. */ static CoglPipeline * _cogl_pipeline_copy (CoglPipeline *src, gboolean is_weak) { CoglPipeline *pipeline = g_slice_new (CoglPipeline); _cogl_pipeline_node_init (COGL_NODE (pipeline)); pipeline->is_weak = is_weak; pipeline->journal_ref_count = 0; pipeline->differences = 0; pipeline->has_big_state = FALSE; /* NB: real_blend_enable isn't a sparse property, it's valid for * every pipeline node so we have fast access to it. */ pipeline->real_blend_enable = src->real_blend_enable; pipeline->dirty_real_blend_enable = src->dirty_real_blend_enable; pipeline->unknown_color_alpha = src->unknown_color_alpha; /* XXX: * consider generalizing the idea of "cached" properties. These * would still have an authority like other sparse properties but * you wouldn't have to walk up the ancestry to find the authority * because the value would be cached directly in each pipeline. */ pipeline->layers_cache_dirty = TRUE; pipeline->has_static_breadcrumb = FALSE; pipeline->age = 0; _cogl_pipeline_set_parent (pipeline, src, !is_weak); /* The semantics for copying a weak pipeline are that we promote all * weak ancestors to temporarily become strong pipelines until the * copy is freed. */ if (!is_weak) _cogl_pipeline_promote_weak_ancestors (pipeline); return _cogl_pipeline_object_new (pipeline); } CoglPipeline * cogl_pipeline_copy (CoglPipeline *src) { return _cogl_pipeline_copy (src, FALSE); } CoglPipeline * _cogl_pipeline_weak_copy (CoglPipeline *pipeline, CoglPipelineDestroyCallback callback, void *user_data) { CoglPipeline *copy; CoglPipeline *copy_pipeline; copy = _cogl_pipeline_copy (pipeline, TRUE); copy_pipeline = COGL_PIPELINE (copy); copy_pipeline->destroy_callback = callback; copy_pipeline->destroy_data = user_data; return copy; } CoglPipeline * cogl_pipeline_new (CoglContext *context) { CoglPipeline *new; new = cogl_pipeline_copy (context->default_pipeline); #ifdef COGL_DEBUG_ENABLED _cogl_pipeline_set_static_breadcrumb (new, "new"); #endif return new; } static gboolean destroy_weak_children_cb (CoglNode *node, void *user_data) { CoglPipeline *pipeline = COGL_PIPELINE (node); if (_cogl_pipeline_is_weak (pipeline)) { _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), destroy_weak_children_cb, NULL); pipeline->destroy_callback (pipeline, pipeline->destroy_data); _cogl_pipeline_unparent (COGL_NODE (pipeline)); } return TRUE; } static void _cogl_pipeline_free (CoglPipeline *pipeline) { if (!pipeline->is_weak) _cogl_pipeline_revert_weak_ancestors (pipeline); /* Weak pipelines don't take a reference on their parent */ _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), destroy_weak_children_cb, NULL); g_assert (_cogl_list_empty (&COGL_NODE (pipeline)->children)); _cogl_pipeline_unparent (COGL_NODE (pipeline)); if (pipeline->differences & COGL_PIPELINE_STATE_USER_SHADER && pipeline->big_state->user_program) cogl_object_unref (pipeline->big_state->user_program); if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) { CoglPipelineUniformsState *uniforms_state = &pipeline->big_state->uniforms_state; int n_overrides = _cogl_bitmask_popcount (&uniforms_state->override_mask); int i; for (i = 0; i < n_overrides; i++) _cogl_boxed_value_destroy (uniforms_state->override_values + i); g_free (uniforms_state->override_values); _cogl_bitmask_destroy (&uniforms_state->override_mask); _cogl_bitmask_destroy (&uniforms_state->changed_mask); } if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) g_list_free_full (pipeline->layer_differences, cogl_object_unref); if (pipeline->differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) _cogl_pipeline_snippet_list_free (&pipeline->big_state->vertex_snippets); if (pipeline->differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) _cogl_pipeline_snippet_list_free (&pipeline->big_state->fragment_snippets); if (pipeline->differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) g_slice_free (CoglPipelineBigState, pipeline->big_state); recursively_free_layer_caches (pipeline); g_slice_free (CoglPipeline, pipeline); } gboolean _cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline) { g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); return pipeline->real_blend_enable; } static void _cogl_pipeline_update_layers_cache (CoglPipeline *pipeline) { /* Note: we assume this pipeline is a _LAYERS authority */ int n_layers; CoglPipeline *current; int layers_found; if (G_LIKELY (!pipeline->layers_cache_dirty) || pipeline->n_layers == 0) return; pipeline->layers_cache_dirty = FALSE; n_layers = pipeline->n_layers; if (G_LIKELY (n_layers < G_N_ELEMENTS (pipeline->short_layers_cache))) { pipeline->layers_cache = pipeline->short_layers_cache; memset (pipeline->layers_cache, 0, sizeof (CoglPipelineLayer *) * G_N_ELEMENTS (pipeline->short_layers_cache)); } else { pipeline->layers_cache = g_slice_alloc0 (sizeof (CoglPipelineLayer *) * n_layers); } /* Notes: * * Each pipeline doesn't have to contain a complete list of the layers * it depends on, some of them are indirectly referenced through the * pipeline's ancestors. * * pipeline->layer_differences only contains a list of layers that * have changed in relation to its parent. * * pipeline->layer_differences is not maintained sorted, but it * won't contain multiple layers corresponding to a particular * ->unit_index. * * Some of the ancestor pipelines may reference layers with * ->unit_index values >= n_layers so we ignore them. * * As we ascend through the ancestors we are searching for any * CoglPipelineLayers corresponding to the texture ->unit_index * values in the range [0,n_layers-1]. As soon as a pointer is found * we ignore layers of further ancestors with the same ->unit_index * values. */ layers_found = 0; for (current = pipeline; _cogl_pipeline_get_parent (current); current = _cogl_pipeline_get_parent (current)) { GList *l; if (!(current->differences & COGL_PIPELINE_STATE_LAYERS)) continue; for (l = current->layer_differences; l; l = l->next) { CoglPipelineLayer *layer = l->data; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); if (unit_index < n_layers && !pipeline->layers_cache[unit_index]) { pipeline->layers_cache[unit_index] = layer; layers_found++; if (layers_found == n_layers) return; } } } g_warn_if_reached (); } /* XXX: Be carefull when using this API that the callback given doesn't result * in the layer cache being invalidated during the iteration! */ void _cogl_pipeline_foreach_layer_internal (CoglPipeline *pipeline, CoglPipelineInternalLayerCallback callback, void *user_data) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); int n_layers; int i; gboolean cont; n_layers = authority->n_layers; if (n_layers == 0) return; _cogl_pipeline_update_layers_cache (authority); for (i = 0, cont = TRUE; i < n_layers && cont == TRUE; i++) { g_return_if_fail (authority->layers_cache_dirty == FALSE); cont = callback (authority->layers_cache[i], user_data); } } gboolean _cogl_pipeline_layer_numbers_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1) { CoglPipeline *authority0 = _cogl_pipeline_get_authority (pipeline0, COGL_PIPELINE_STATE_LAYERS); CoglPipeline *authority1 = _cogl_pipeline_get_authority (pipeline1, COGL_PIPELINE_STATE_LAYERS); int n_layers = authority0->n_layers; int i; if (authority1->n_layers != n_layers) return FALSE; _cogl_pipeline_update_layers_cache (authority0); _cogl_pipeline_update_layers_cache (authority1); for (i = 0; i < n_layers; i++) { CoglPipelineLayer *layer0 = authority0->layers_cache[i]; CoglPipelineLayer *layer1 = authority1->layers_cache[i]; if (layer0->index != layer1->index) return FALSE; } return TRUE; } gboolean _cogl_pipeline_layer_and_unit_numbers_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1) { CoglPipeline *authority0 = _cogl_pipeline_get_authority (pipeline0, COGL_PIPELINE_STATE_LAYERS); CoglPipeline *authority1 = _cogl_pipeline_get_authority (pipeline1, COGL_PIPELINE_STATE_LAYERS); int n_layers = authority0->n_layers; int i; if (authority1->n_layers != n_layers) return FALSE; _cogl_pipeline_update_layers_cache (authority0); _cogl_pipeline_update_layers_cache (authority1); for (i = 0; i < n_layers; i++) { CoglPipelineLayer *layer0 = authority0->layers_cache[i]; CoglPipelineLayer *layer1 = authority1->layers_cache[i]; int unit0, unit1; if (layer0->index != layer1->index) return FALSE; unit0 = _cogl_pipeline_layer_get_unit_index (layer0); unit1 = _cogl_pipeline_layer_get_unit_index (layer1); if (unit0 != unit1) return FALSE; } return TRUE; } typedef struct { int i; int *indices; } AppendLayerIndexState; static gboolean append_layer_index_cb (CoglPipelineLayer *layer, void *user_data) { AppendLayerIndexState *state = user_data; state->indices[state->i++] = layer->index; return TRUE; } void cogl_pipeline_foreach_layer (CoglPipeline *pipeline, CoglPipelineLayerCallback callback, void *user_data) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); AppendLayerIndexState state; gboolean cont; int i; /* XXX: We don't know what the user is going to want to do to the layers * but any modification of layers can result in the layer graph changing * which could confuse _cogl_pipeline_foreach_layer_internal(). We first * get a list of layer indices which will remain valid so long as the * user doesn't remove layers. */ state.i = 0; state.indices = g_alloca (authority->n_layers * sizeof (int)); _cogl_pipeline_foreach_layer_internal (pipeline, append_layer_index_cb, &state); for (i = 0, cont = TRUE; i < authority->n_layers && cont; i++) cont = callback (pipeline, state.indices[i], user_data); } static gboolean layer_has_alpha_cb (CoglPipelineLayer *layer, void *data) { gboolean *has_alpha = data; *has_alpha = _cogl_pipeline_layer_has_alpha (layer); /* return FALSE to stop iterating layers if we find any layer * has alpha ... * * FIXME: actually we should never be bailing out because it's * always possible that a later layer could discard any previous * alpha! */ return !(*has_alpha); } /* NB: If this pipeline returns FALSE that doesn't mean that the * pipeline is definitely opaque, it just means that that the * given changes dont imply transparency. * * If you want to find out of the pipeline is opaque then assuming * this returns FALSE for a set of changes then you can follow * up */ static gboolean _cogl_pipeline_change_implies_transparency (CoglPipeline *pipeline, unsigned int changes, const CoglColor *override_color, gboolean unknown_color_alpha) { /* In the case of a layer state change we need to check everything * else first since they contribute to the has_alpha status of the * "PREVIOUS" layer. */ if (changes & COGL_PIPELINE_STATE_LAYERS) changes = COGL_PIPELINE_STATE_AFFECTS_BLENDING; if (unknown_color_alpha) return TRUE; if ((override_color && cogl_color_get_alpha_byte (override_color) != 0xff)) return TRUE; if (changes & COGL_PIPELINE_STATE_COLOR) { CoglColor tmp; cogl_pipeline_get_color (pipeline, &tmp); if (cogl_color_get_alpha_byte (&tmp) != 0xff) return TRUE; } if (changes & COGL_PIPELINE_STATE_USER_SHADER) { /* We can't make any assumptions about the alpha channel if the user * is using an unknown fragment shader. * * TODO: check that it isn't just a vertex shader! */ if (_cogl_pipeline_get_user_program (pipeline) != NULL) return TRUE; } if (changes & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) { if (_cogl_pipeline_has_non_layer_fragment_snippets (pipeline)) return TRUE; } if (changes & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) { if (_cogl_pipeline_has_non_layer_vertex_snippets (pipeline)) return TRUE; } if (changes & COGL_PIPELINE_STATE_LAYERS) { /* has_alpha tracks the alpha status of the GL_PREVIOUS layer. * To start with that's defined by the pipeline color which * must be fully opaque if we got this far. */ gboolean has_alpha = FALSE; _cogl_pipeline_foreach_layer_internal (pipeline, layer_has_alpha_cb, &has_alpha); if (has_alpha) return TRUE; } return FALSE; } static gboolean _cogl_pipeline_needs_blending_enabled (CoglPipeline *pipeline, unsigned int changes, const CoglColor *override_color, gboolean unknown_color_alpha) { CoglPipeline *blend_authority; CoglPipelineBlendState *blend_state; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BLENDING))) return FALSE; blend_authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND); blend_state = &blend_authority->big_state->blend_state; /* We are trying to identify some cases that are equivalent to * blending being disable, where the output is simply GL_SRC_COLOR. * * Note: we currently only consider a few cases that can be * optimized but there could be opportunities to special case more * blend functions later. */ /* As the most common way that we currently use to effectively * disable blending is to use an equation of * "RGBA=ADD(SRC_COLOR, 0)" that's the first thing we check * for... */ if (blend_state->blend_equation_rgb == GL_FUNC_ADD && blend_state->blend_equation_alpha == GL_FUNC_ADD && blend_state->blend_src_factor_alpha == GL_ONE && blend_state->blend_dst_factor_alpha == GL_ZERO) { return FALSE; } /* NB: The default blending equation for Cogl is * "RGBA=ADD(SRC_COLOR, DST_COLOR * (1-SRC_COLOR[A]))" * * Next we check if the default blending equation is being used. If * so then we follow that by looking for cases where SRC_COLOR[A] == * 1 since that simplifies "DST_COLOR * (1-SRC_COLOR[A])" to 0 which * also effectively requires no blending. */ if (blend_state->blend_equation_rgb != GL_FUNC_ADD || blend_state->blend_equation_alpha != GL_FUNC_ADD) return TRUE; if (blend_state->blend_src_factor_alpha != GL_ONE || blend_state->blend_dst_factor_alpha != GL_ONE_MINUS_SRC_ALPHA) return TRUE; if (blend_state->blend_src_factor_rgb != GL_ONE || blend_state->blend_dst_factor_rgb != GL_ONE_MINUS_SRC_ALPHA) return TRUE; /* Given the above constraints, it's now a case of finding any * SRC_ALPHA that != 1 */ if (_cogl_pipeline_change_implies_transparency (pipeline, changes, override_color, unknown_color_alpha)) return TRUE; /* At this point, considering just the state that has changed it * looks like blending isn't needed. If blending was previously * enabled though it could be that some other state still requires * that we have blending enabled because it implies transparency. * In this case we still need to go and check the other state... * * XXX: We could explicitly keep track of the mask of state groups * that are currently causing blending to be enabled so that we * never have to resort to checking *all* the state and can instead * always limit the check to those in the mask. */ if (pipeline->real_blend_enable) { unsigned int other_state = COGL_PIPELINE_STATE_AFFECTS_BLENDING & ~changes; if (other_state && _cogl_pipeline_change_implies_transparency (pipeline, other_state, NULL, FALSE)) return TRUE; } return FALSE; } static void _cogl_pipeline_copy_differences (CoglPipeline *dest, CoglPipeline *src, unsigned long differences) { CoglPipelineBigState *big_state; if (differences & COGL_PIPELINE_STATE_COLOR) dest->color = src->color; if (differences & COGL_PIPELINE_STATE_LAYERS) { GList *l; if (dest->differences & COGL_PIPELINE_STATE_LAYERS && dest->layer_differences) g_list_free_full (dest->layer_differences, cogl_object_unref); for (l = src->layer_differences; l; l = l->next) { /* NB: a layer can't have more than one ->owner so we can't * simply take a references on each of the original * layer_differences, we have to derive new layers from the * originals instead. */ CoglPipelineLayer *copy = _cogl_pipeline_layer_copy (l->data); _cogl_pipeline_add_layer_difference (dest, copy, FALSE); cogl_object_unref (copy); } /* Note: we initialize n_layers after adding the layer differences * since the act of adding the layers will initialize n_layers to 0 * because dest isn't initially a STATE_LAYERS authority. */ dest->n_layers = src->n_layers; } if (differences & COGL_PIPELINE_STATE_NEEDS_BIG_STATE) { if (!dest->has_big_state) { dest->big_state = g_slice_new (CoglPipelineBigState); dest->has_big_state = TRUE; } big_state = dest->big_state; } else goto check_for_blending_change; if (differences & COGL_PIPELINE_STATE_ALPHA_FUNC) big_state->alpha_state.alpha_func = src->big_state->alpha_state.alpha_func; if (differences & COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE) big_state->alpha_state.alpha_func_reference = src->big_state->alpha_state.alpha_func_reference; if (differences & COGL_PIPELINE_STATE_BLEND) { memcpy (&big_state->blend_state, &src->big_state->blend_state, sizeof (CoglPipelineBlendState)); } if (differences & COGL_PIPELINE_STATE_USER_SHADER) { if (src->big_state->user_program) big_state->user_program = cogl_object_ref (src->big_state->user_program); else big_state->user_program = NULL; } if (differences & COGL_PIPELINE_STATE_DEPTH) { memcpy (&big_state->depth_state, &src->big_state->depth_state, sizeof (CoglDepthState)); } if (differences & COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE) big_state->non_zero_point_size = src->big_state->non_zero_point_size; if (differences & COGL_PIPELINE_STATE_POINT_SIZE) big_state->point_size = src->big_state->point_size; if (differences & COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE) big_state->per_vertex_point_size = src->big_state->per_vertex_point_size; if (differences & COGL_PIPELINE_STATE_CULL_FACE) { memcpy (&big_state->cull_face_state, &src->big_state->cull_face_state, sizeof (CoglPipelineCullFaceState)); } if (differences & COGL_PIPELINE_STATE_UNIFORMS) { int n_overrides = _cogl_bitmask_popcount (&src->big_state->uniforms_state.override_mask); int i; big_state->uniforms_state.override_values = g_malloc (n_overrides * sizeof (CoglBoxedValue)); for (i = 0; i < n_overrides; i++) { CoglBoxedValue *dst_bv = big_state->uniforms_state.override_values + i; const CoglBoxedValue *src_bv = src->big_state->uniforms_state.override_values + i; _cogl_boxed_value_copy (dst_bv, src_bv); } _cogl_bitmask_init (&big_state->uniforms_state.override_mask); _cogl_bitmask_set_bits (&big_state->uniforms_state.override_mask, &src->big_state->uniforms_state.override_mask); _cogl_bitmask_init (&big_state->uniforms_state.changed_mask); } if (differences & COGL_PIPELINE_STATE_VERTEX_SNIPPETS) _cogl_pipeline_snippet_list_copy (&big_state->vertex_snippets, &src->big_state->vertex_snippets); if (differences & COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS) _cogl_pipeline_snippet_list_copy (&big_state->fragment_snippets, &src->big_state->fragment_snippets); /* XXX: we shouldn't bother doing this in most cases since * _copy_differences is typically used to initialize pipeline state * by copying it from the current authority, so it's not actually * *changing* anything. */ check_for_blending_change: if (differences & COGL_PIPELINE_STATE_AFFECTS_BLENDING) dest->dirty_real_blend_enable = TRUE; dest->differences |= differences; } static void _cogl_pipeline_init_multi_property_sparse_state (CoglPipeline *pipeline, CoglPipelineState change) { CoglPipeline *authority; g_return_if_fail (change & COGL_PIPELINE_STATE_ALL_SPARSE); if (!(change & COGL_PIPELINE_STATE_MULTI_PROPERTY)) return; authority = _cogl_pipeline_get_authority (pipeline, change); switch (change) { /* XXX: avoid using a default: label so we get a warning if we * don't explicitly handle a newly defined state-group here. */ case COGL_PIPELINE_STATE_COLOR: case COGL_PIPELINE_STATE_ALPHA_FUNC: case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE: case COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE: case COGL_PIPELINE_STATE_POINT_SIZE: case COGL_PIPELINE_STATE_USER_SHADER: case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE: case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE: g_return_if_reached (); case COGL_PIPELINE_STATE_LAYERS: pipeline->n_layers = authority->n_layers; pipeline->layer_differences = NULL; break; case COGL_PIPELINE_STATE_BLEND: { memcpy (&pipeline->big_state->blend_state, &authority->big_state->blend_state, sizeof (CoglPipelineBlendState)); break; } case COGL_PIPELINE_STATE_DEPTH: { memcpy (&pipeline->big_state->depth_state, &authority->big_state->depth_state, sizeof (CoglDepthState)); break; } case COGL_PIPELINE_STATE_CULL_FACE: { memcpy (&pipeline->big_state->cull_face_state, &authority->big_state->cull_face_state, sizeof (CoglPipelineCullFaceState)); break; } case COGL_PIPELINE_STATE_UNIFORMS: { CoglPipelineUniformsState *uniforms_state = &pipeline->big_state->uniforms_state; _cogl_bitmask_init (&uniforms_state->override_mask); _cogl_bitmask_init (&uniforms_state->changed_mask); uniforms_state->override_values = NULL; break; } case COGL_PIPELINE_STATE_VERTEX_SNIPPETS: _cogl_pipeline_snippet_list_copy (&pipeline->big_state->vertex_snippets, &authority->big_state->vertex_snippets); break; case COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS: _cogl_pipeline_snippet_list_copy (&pipeline->big_state->fragment_snippets, &authority->big_state-> fragment_snippets); break; } } static gboolean check_if_strong_cb (CoglNode *node, void *user_data) { CoglPipeline *pipeline = COGL_PIPELINE (node); gboolean *has_strong_child = user_data; if (!_cogl_pipeline_is_weak (pipeline)) { *has_strong_child = TRUE; return FALSE; } return TRUE; } static gboolean has_strong_children (CoglPipeline *pipeline) { gboolean has_strong_child = FALSE; _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), check_if_strong_cb, &has_strong_child); return has_strong_child; } static gboolean _cogl_pipeline_is_weak (CoglPipeline *pipeline) { if (pipeline->is_weak && !has_strong_children (pipeline)) return TRUE; else return FALSE; } static gboolean reparent_children_cb (CoglNode *node, void *user_data) { CoglPipeline *pipeline = COGL_PIPELINE (node); CoglPipeline *parent = user_data; _cogl_pipeline_set_parent (pipeline, parent, TRUE); return TRUE; } void _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color, gboolean from_layer_change) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* If primitives have been logged in the journal referencing the * current state of this pipeline we need to flush the journal * before we can modify it... */ if (pipeline->journal_ref_count) { gboolean skip_journal_flush = FALSE; /* XXX: We don't usually need to flush the journal just due to * color changes since pipeline colors are logged in the * journal's vertex buffer. The exception is when the change in * color enables or disables the need for blending. */ if (change == COGL_PIPELINE_STATE_COLOR) { gboolean will_need_blending = _cogl_pipeline_needs_blending_enabled (pipeline, change, new_color, FALSE); gboolean blend_enable = pipeline->real_blend_enable ? TRUE : FALSE; if (will_need_blending == blend_enable) skip_journal_flush = TRUE; } if (!skip_journal_flush) { /* XXX: note we use cogl_flush() not _cogl_flush_journal() so * we will flush *all* known journals that might reference the * current pipeline. */ cogl_flush (); } } /* XXX: * To simplify things for the vertex, fragment and program backends * we are careful about how we report STATE_LAYERS changes. * * All STATE_LAYERS change notifications with the exception of * ->n_layers will also result in layer_pre_change_notifications. * * For backends that perform code generation for fragment processing * they typically need to understand the details of how layers get * changed to determine if they need to repeat codegen. It doesn't * help them to report a pipeline STATE_LAYERS change for all layer * changes since it's so broad, they really need to wait for the * specific layer change to be notified. What does help though is * to report a STATE_LAYERS change for a change in ->n_layers * because they typically do need to repeat codegen in that case. * * Here we ensure that change notifications against a pipeline or * against a layer are mutually exclusive as far as fragment, vertex * and program backends are concerned. * * NB: A pipeline can potentially have private state from multiple * backends associated with it because descendants may cache state * with an ancestor to maximize the chance that it can later be * re-used by other descendants and a descendent can require a * different backend to an ancestor. */ if (!from_layer_change) { const CoglPipelineProgend *progend = _cogl_pipeline_progend; const CoglPipelineVertend *vertend = _cogl_pipeline_vertend; const CoglPipelineFragend *fragend = _cogl_pipeline_fragend; if (vertend->pipeline_pre_change_notify) vertend->pipeline_pre_change_notify (pipeline, change, new_color); /* TODO: make the vertend and fragend implementation details * of the progend */ if (fragend->pipeline_pre_change_notify) fragend->pipeline_pre_change_notify (pipeline, change, new_color); if (progend->pipeline_pre_change_notify) progend->pipeline_pre_change_notify (pipeline, change, new_color); } /* There may be an arbitrary tree of descendants of this pipeline; * any of which may indirectly depend on this pipeline as the * authority for some set of properties. (Meaning for example that * one of its descendants derives its color or blending state from * this pipeline.) * * We can't modify any property that this pipeline is the authority * for unless we create another pipeline to take its place first and * make sure descendants reference this new pipeline instead. */ /* The simplest descendants to handle are weak pipelines; we simply * destroy them if we are modifying a pipeline they depend on. This * means weak pipelines never cause us to do a copy-on-write. */ _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), destroy_weak_children_cb, NULL); /* If there are still children remaining though we'll need to * perform a copy-on-write and reparent the dependants as children * of the copy. */ if (!_cogl_list_empty (&COGL_NODE (pipeline)->children)) { CoglPipeline *new_authority; COGL_STATIC_COUNTER (pipeline_copy_on_write_counter, "pipeline copy on write counter", "Increments each time a pipeline " "must be copied to allow modification", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, pipeline_copy_on_write_counter); new_authority = cogl_pipeline_copy (_cogl_pipeline_get_parent (pipeline)); #ifdef COGL_DEBUG_ENABLED _cogl_pipeline_set_static_breadcrumb (new_authority, "pre_change_notify:copy-on-write"); #endif /* We could explicitly walk the descendants, OR together the set * of differences that we determine this pipeline is the * authority on and only copy those differences copied across. * * Or, if we don't explicitly walk the descendants we at least * know that pipeline->differences represents the largest set of * differences that this pipeline could possibly be an authority * on. * * We do the later just because it's simplest, but we might need * to come back to this later... */ _cogl_pipeline_copy_differences (new_authority, pipeline, pipeline->differences); /* Reparent the dependants of pipeline to be children of * new_authority instead... */ _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), reparent_children_cb, new_authority); /* The children will keep the new authority alive so drop the * reference we got when copying... */ cogl_object_unref (new_authority); } /* At this point we know we have a pipeline with no strong * dependants (though we may have some weak children) so we are now * free to modify the pipeline. */ pipeline->age++; if (change & COGL_PIPELINE_STATE_NEEDS_BIG_STATE && !pipeline->has_big_state) { pipeline->big_state = g_slice_new (CoglPipelineBigState); pipeline->has_big_state = TRUE; } /* Note: conceptually we have just been notified that a single * property value is about to change, but since some state-groups * contain multiple properties and 'pipeline' is about to take over * being the authority for the property's corresponding state-group * we need to maintain the integrity of the other property values * too. * * To ensure this we handle multi-property state-groups by copying * all the values from the old-authority to the new... * * We don't have to worry about non-sparse property groups since * we never take over being an authority for such properties so * they automatically maintain integrity. */ if (change & COGL_PIPELINE_STATE_ALL_SPARSE && !(pipeline->differences & change)) { _cogl_pipeline_init_multi_property_sparse_state (pipeline, change); pipeline->differences |= change; } /* Each pipeline has a sorted cache of the layers it depends on * which will need updating via _cogl_pipeline_update_layers_cache * if a pipeline's layers are changed. */ if (change == COGL_PIPELINE_STATE_LAYERS) recursively_free_layer_caches (pipeline); /* If the pipeline being changed is the same as the last pipeline we * flushed then we keep a track of the changes so we can try to * minimize redundant OpenGL calls if the same pipeline is flushed * again. */ if (ctx->current_pipeline == pipeline) ctx->current_pipeline_changes_since_flush |= change; } void _cogl_pipeline_add_layer_difference (CoglPipeline *pipeline, CoglPipelineLayer *layer, gboolean inc_n_layers) { g_return_if_fail (layer->owner == NULL); layer->owner = pipeline; cogl_object_ref (layer); /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ /* Note: the last argument to _cogl_pipeline_pre_change_notify is * needed to differentiate STATE_LAYER changes which don't affect * the number of layers from those that do. NB: Layer change * notifications that don't change the number of layers don't get * forwarded to the fragend. */ _cogl_pipeline_pre_change_notify (pipeline, COGL_PIPELINE_STATE_LAYERS, NULL, !inc_n_layers); pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; pipeline->layer_differences = g_list_prepend (pipeline->layer_differences, layer); if (inc_n_layers) pipeline->n_layers++; /* Adding a layer difference may mean this pipeline now overrides * all of the layers of its parent which might make the parent * redundant so we should try to prune the hierarchy */ _cogl_pipeline_prune_redundant_ancestry (pipeline); } void _cogl_pipeline_remove_layer_difference (CoglPipeline *pipeline, CoglPipelineLayer *layer, gboolean dec_n_layers) { /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ /* Note: the last argument to _cogl_pipeline_pre_change_notify is * needed to differentiate STATE_LAYER changes which don't affect * the number of layers from those that do. NB: Layer change * notifications that don't change the number of layers don't get * forwarded to the fragend. */ _cogl_pipeline_pre_change_notify (pipeline, COGL_PIPELINE_STATE_LAYERS, NULL, !dec_n_layers); /* We only need to remove the layer difference if the pipeline is * currently the owner. If it is not the owner then one of two * things will happen to make sure this layer is replaced. If it is * the last layer being removed then decrementing n_layers will * ensure that the last layer is skipped. If it is any other layer * then the subsequent layers will have been shifted down and cause * it be replaced */ if (layer->owner == pipeline) { layer->owner = NULL; cogl_object_unref (layer); pipeline->layer_differences = g_list_remove (pipeline->layer_differences, layer); } pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; if (dec_n_layers) pipeline->n_layers--; } static void _cogl_pipeline_try_reverting_layers_authority (CoglPipeline *authority, CoglPipeline *old_authority) { if (authority->layer_differences == NULL && _cogl_pipeline_get_parent (authority)) { /* If the previous _STATE_LAYERS authority has the same * ->n_layers then we can revert to that being the authority * again. */ if (!old_authority) { old_authority = _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority), COGL_PIPELINE_STATE_LAYERS); } if (old_authority->n_layers == authority->n_layers) authority->differences &= ~COGL_PIPELINE_STATE_LAYERS; } } void _cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline, gboolean unknown_color_alpha) { CoglPipeline *parent; unsigned int differences; if (pipeline->dirty_real_blend_enable == FALSE && pipeline->unknown_color_alpha == unknown_color_alpha) return; if (pipeline->dirty_real_blend_enable) { differences = pipeline->differences; parent = _cogl_pipeline_get_parent (pipeline); while (parent->dirty_real_blend_enable) { differences |= parent->differences; parent = _cogl_pipeline_get_parent (parent); } /* We initialize the pipeline's real_blend_enable with a known * reference value from its nearest ancestor with clean state so * we can then potentially reduce the work involved in checking * if the pipeline really needs blending itself because we can * just look at the things that differ between the ancestor and * this pipeline. */ pipeline->real_blend_enable = parent->real_blend_enable; } else /* pipeline->unknown_color_alpha != unknown_color_alpha */ differences = 0; /* Note we don't call _cogl_pipeline_pre_change_notify() for this * state change because ->real_blend_enable is lazily derived from * other state while flushing the pipeline and we'd need to avoid * recursion problems in cases where _pre_change_notify() flushes * the journal if the pipeline is referenced by a journal. */ pipeline->real_blend_enable = _cogl_pipeline_needs_blending_enabled (pipeline, differences, NULL, unknown_color_alpha); pipeline->dirty_real_blend_enable = FALSE; pipeline->unknown_color_alpha = unknown_color_alpha; } typedef struct { int keep_n; int current_pos; int first_index_to_prune; } CoglPipelinePruneLayersInfo; static gboolean update_prune_layers_info_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelinePruneLayersInfo *state = user_data; if (state->current_pos == state->keep_n) { state->first_index_to_prune = layer->index; return FALSE; } state->current_pos++; return TRUE; } void _cogl_pipeline_prune_to_n_layers (CoglPipeline *pipeline, int n) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); CoglPipelinePruneLayersInfo state; GList *l; GList *next; if (authority->n_layers <= n) return; /* This call to foreach_layer_internal needs to be done before * calling pre_change_notify because it recreates the layer cache. * We are relying on pre_change_notify to clear the layer cache * before we change the number of layers */ state.keep_n = n; state.current_pos = 0; _cogl_pipeline_foreach_layer_internal (pipeline, update_prune_layers_info_cb, &state); _cogl_pipeline_pre_change_notify (pipeline, COGL_PIPELINE_STATE_LAYERS, NULL, FALSE); pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; pipeline->n_layers = n; /* It's possible that this pipeline owns some of the layers being * discarded, so we'll need to unlink them... */ for (l = pipeline->layer_differences; l; l = next) { CoglPipelineLayer *layer = l->data; next = l->next; /* we're modifying the list we're iterating */ if (layer->index >= state.first_index_to_prune) _cogl_pipeline_remove_layer_difference (pipeline, layer, FALSE); } pipeline->differences |= COGL_PIPELINE_STATE_LAYERS; } typedef struct { /* The layer we are trying to find */ int layer_index; /* The layer we find or untouched if not found */ CoglPipelineLayer *layer; /* If the layer can't be found then a new layer should be * inserted after this texture unit index... */ int insert_after; /* When adding a layer we need the list of layers to shift up * to a new texture unit. When removing we need the list of * layers to shift down. * * Note: the list isn't sorted */ CoglPipelineLayer **layers_to_shift; int n_layers_to_shift; /* When adding a layer we don't need a complete list of * layers_to_shift if we find a layer already corresponding to the * layer_index. */ gboolean ignore_shift_layers_if_found; } CoglPipelineLayerInfo; /* Returns TRUE once we know there is nothing more to update */ static gboolean update_layer_info (CoglPipelineLayer *layer, CoglPipelineLayerInfo *layer_info) { if (layer->index == layer_info->layer_index) { layer_info->layer = layer; if (layer_info->ignore_shift_layers_if_found) return TRUE; } else if (layer->index < layer_info->layer_index) { int unit_index = _cogl_pipeline_layer_get_unit_index (layer); layer_info->insert_after = unit_index; } else layer_info->layers_to_shift[layer_info->n_layers_to_shift++] = layer; return FALSE; } /* Returns FALSE to break out of a _foreach_layer () iteration */ static gboolean update_layer_info_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineLayerInfo *layer_info = user_data; if (update_layer_info (layer, layer_info)) return FALSE; /* break */ else return TRUE; /* continue */ } static void _cogl_pipeline_get_layer_info (CoglPipeline *pipeline, CoglPipelineLayerInfo *layer_info) { /* Note: we are assuming this pipeline is a _STATE_LAYERS authority */ int n_layers = pipeline->n_layers; int i; /* FIXME: _cogl_pipeline_foreach_layer_internal now calls * _cogl_pipeline_update_layers_cache anyway so this codepath is * pointless! */ if (layer_info->ignore_shift_layers_if_found && pipeline->layers_cache_dirty) { /* The expectation is that callers of * _cogl_pipeline_get_layer_info are likely to be modifying the * list of layers associated with a pipeline so in this case * where we don't have a cache of the layers and we don't * necessarily have to iterate all the layers of the pipeline we * use a foreach_layer callback instead of updating the cache * and iterating that as below. */ _cogl_pipeline_foreach_layer_internal (pipeline, update_layer_info_cb, layer_info); return; } _cogl_pipeline_update_layers_cache (pipeline); for (i = 0; i < n_layers; i++) { CoglPipelineLayer *layer = pipeline->layers_cache[i]; if (update_layer_info (layer, layer_info)) return; } } CoglPipelineLayer * _cogl_pipeline_get_layer_with_flags (CoglPipeline *pipeline, int layer_index, CoglPipelineGetLayerFlags flags) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); CoglPipelineLayerInfo layer_info; CoglPipelineLayer *layer; int unit_index; int i; CoglContext *ctx; /* The layer index of the layer we want info about */ layer_info.layer_index = layer_index; /* If a layer already exists with the given index this will be * updated. */ layer_info.layer = NULL; /* If a layer isn't found for the given index we'll need to know * where to insert a new layer. */ layer_info.insert_after = -1; /* If a layer can't be found then we'll need to insert a new layer * and bump up the texture unit for all layers with an index * > layer_index. */ layer_info.layers_to_shift = g_alloca (sizeof (CoglPipelineLayer *) * authority->n_layers); layer_info.n_layers_to_shift = 0; /* If an exact match is found though we don't need a complete * list of layers with indices > layer_index... */ layer_info.ignore_shift_layers_if_found = TRUE; _cogl_pipeline_get_layer_info (authority, &layer_info); if (layer_info.layer || (flags & COGL_PIPELINE_GET_LAYER_NO_CREATE)) return layer_info.layer; ctx = _cogl_context_get_default (); unit_index = layer_info.insert_after + 1; if (unit_index == 0) layer = _cogl_pipeline_layer_copy (ctx->default_layer_0); else { CoglPipelineLayer *new; layer = _cogl_pipeline_layer_copy (ctx->default_layer_n); new = _cogl_pipeline_set_layer_unit (NULL, layer, unit_index); /* Since we passed a newly allocated layer we wouldn't expect * _set_layer_unit() to have to allocate *another* layer. */ g_assert (new == layer); } layer->index = layer_index; for (i = 0; i < layer_info.n_layers_to_shift; i++) { CoglPipelineLayer *shift_layer = layer_info.layers_to_shift[i]; unit_index = _cogl_pipeline_layer_get_unit_index (shift_layer); _cogl_pipeline_set_layer_unit (pipeline, shift_layer, unit_index + 1); /* NB: shift_layer may not be writeable so _set_layer_unit() * will allocate a derived layer internally which will become * owned by pipeline. Check the return value if we need to do * anything else with this layer. */ } _cogl_pipeline_add_layer_difference (pipeline, layer, TRUE); cogl_object_unref (layer); return layer; } void _cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority, CoglPipelineLayer *layer) { /* Find the GList link that references the empty layer */ GList *link = g_list_find (layers_authority->layer_differences, layer); /* No pipeline directly owns the root node layer so this is safe... */ CoglPipelineLayer *layer_parent = _cogl_pipeline_layer_get_parent (layer); CoglPipelineLayerInfo layer_info; CoglPipeline *old_layers_authority; g_return_if_fail (link != NULL); /* If the layer's parent doesn't have an owner then we can simply * take ownership ourselves and drop our reference on the empty * layer. We don't want to take ownership of the root node layer so * we also need to verify that the parent has a parent */ if (layer_parent->index == layer->index && layer_parent->owner == NULL && _cogl_pipeline_layer_get_parent (layer_parent) != NULL) { cogl_object_ref (layer_parent); layer_parent->owner = layers_authority; link->data = layer_parent; cogl_object_unref (layer); recursively_free_layer_caches (layers_authority); return; } /* Now we want to find the layer that would become the authority for * layer->index if we were to remove layer from * layers_authority->layer_differences */ /* The layer index of the layer we want info about */ layer_info.layer_index = layer->index; /* If a layer already exists with the given index this will be * updated. */ layer_info.layer = NULL; /* If a layer can't be found then we'll need to insert a new layer * and bump up the texture unit for all layers with an index * > layer_index. */ layer_info.layers_to_shift = g_alloca (sizeof (CoglPipelineLayer *) * layers_authority->n_layers); layer_info.n_layers_to_shift = 0; /* If an exact match is found though we don't need a complete * list of layers with indices > layer_index... */ layer_info.ignore_shift_layers_if_found = TRUE; /* We know the default/root pipeline isn't a LAYERS authority so it's * safe to use the result of _cogl_pipeline_get_parent (layers_authority) * without checking it. */ old_layers_authority = _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (layers_authority), COGL_PIPELINE_STATE_LAYERS); _cogl_pipeline_get_layer_info (old_layers_authority, &layer_info); /* If layer is the defining layer for the corresponding ->index then * we can't get rid of it. */ if (!layer_info.layer) return; /* If the layer that would become the authority for layer->index is * _cogl_pipeline_layer_get_parent (layer) then we can simply remove the * layer difference. */ if (layer_info.layer == _cogl_pipeline_layer_get_parent (layer)) { _cogl_pipeline_remove_layer_difference (layers_authority, layer, FALSE); _cogl_pipeline_try_reverting_layers_authority (layers_authority, old_layers_authority); } } typedef struct { int i; CoglPipeline *pipeline; unsigned long fallback_layers; } CoglPipelineFallbackState; static gboolean fallback_layer_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineFallbackState *state = user_data; CoglPipeline *pipeline = state->pipeline; CoglTexture *texture = NULL; COGL_STATIC_COUNTER (layer_fallback_counter, "layer fallback counter", "Increments each time a layer's texture is " "forced to a fallback texture", 0 /* no application private data */); _COGL_GET_CONTEXT (ctx, FALSE); if (!(state->fallback_layers & 1<i)) return TRUE; COGL_COUNTER_INC (_cogl_uprof_context, layer_fallback_counter); texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); if (texture == NULL) { g_warning ("We don't have a fallback texture we can use to fill " "in for an invalid pipeline layer, since it was " "using an unsupported texture target "); /* might get away with this... */ texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); } cogl_pipeline_set_layer_texture (pipeline, layer->index, texture); state->i++; return TRUE; } typedef struct { CoglPipeline *pipeline; CoglTexture *texture; } CoglPipelineOverrideLayerState; static gboolean override_layer_texture_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineOverrideLayerState *state = user_data; cogl_pipeline_set_layer_texture (state->pipeline, layer->index, state->texture); return TRUE; } void _cogl_pipeline_apply_overrides (CoglPipeline *pipeline, CoglPipelineFlushOptions *options) { COGL_STATIC_COUNTER (apply_overrides_counter, "pipeline overrides counter", "Increments each time we have to apply " "override options to a pipeline", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, apply_overrides_counter); if (options->flags & COGL_PIPELINE_FLUSH_DISABLE_MASK) { int i; /* NB: we can assume that once we see one bit to disable * a layer, all subsequent layers are also disabled. */ for (i = 0; i < 32 && options->disable_layers & (1<flags & COGL_PIPELINE_FLUSH_FALLBACK_MASK) { CoglPipelineFallbackState state; state.i = 0; state.pipeline = pipeline; state.fallback_layers = options->fallback_layers; _cogl_pipeline_foreach_layer_internal (pipeline, fallback_layer_cb, &state); } if (options->flags & COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE) { CoglPipelineOverrideLayerState state; _cogl_pipeline_prune_to_n_layers (pipeline, 1); /* NB: we are overriding the first layer, but we don't know * the user's given layer_index, which is why we use * _cogl_pipeline_foreach_layer_internal() here even though we know * there's only one layer. */ state.pipeline = pipeline; state.texture = options->layer0_override_texture; _cogl_pipeline_foreach_layer_internal (pipeline, override_layer_texture_cb, &state); } } static gboolean _cogl_pipeline_layers_equal (CoglPipeline *authority0, CoglPipeline *authority1, unsigned long differences, CoglPipelineEvalFlags flags) { int i; if (authority0->n_layers != authority1->n_layers) return FALSE; _cogl_pipeline_update_layers_cache (authority0); _cogl_pipeline_update_layers_cache (authority1); for (i = 0; i < authority0->n_layers; i++) { if (!_cogl_pipeline_layer_equal (authority0->layers_cache[i], authority1->layers_cache[i], differences, flags)) return FALSE; } return TRUE; } /* Determine the mask of differences between two pipelines */ unsigned long _cogl_pipeline_compare_differences (CoglPipeline *pipeline0, CoglPipeline *pipeline1) { GSList *head0 = NULL; GSList *head1 = NULL; CoglPipeline *node0; CoglPipeline *node1; int len0 = 0; int len1 = 0; int count; GSList *common_ancestor0; GSList *common_ancestor1; unsigned long pipelines_difference = 0; /* Algorithm: * * 1) Walk the ancestors of each pipeline to the root node, adding a * pointer to each ancester node to two linked lists * * 2) Compare the lists to find the nodes where they start to * differ marking the common_ancestor node for each list. * * 3) For each list now iterate starting after the common_ancestor * nodes ORing each nodes ->difference mask into the final * differences mask. */ for (node0 = pipeline0; node0; node0 = _cogl_pipeline_get_parent (node0)) { GSList *link = alloca (sizeof (GSList)); link->next = head0; link->data = node0; head0 = link; len0++; } for (node1 = pipeline1; node1; node1 = _cogl_pipeline_get_parent (node1)) { GSList *link = alloca (sizeof (GSList)); link->next = head1; link->data = node1; head1 = link; len1++; } /* NB: There's no point looking at the head entries since we know both * pipelines must have the same default pipeline as their root node. */ common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; count = MIN (len0, len1) - 1; while (count--) { if (head0->data != head1->data) break; common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; } for (head0 = common_ancestor0->next; head0; head0 = head0->next) { node0 = head0->data; pipelines_difference |= node0->differences; } for (head1 = common_ancestor1->next; head1; head1 = head1->next) { node1 = head1->data; pipelines_difference |= node1->differences; } return pipelines_difference; } static void _cogl_pipeline_resolve_authorities (CoglPipeline *pipeline, unsigned long differences, CoglPipeline **authorities) { unsigned long remaining = differences; CoglPipeline *authority = pipeline; do { unsigned long found = authority->differences & remaining; int i; if (found == 0) continue; for (i = 0; TRUE; i++) { unsigned long state = (1L< found) break; } remaining &= ~found; if (remaining == 0) return; } while ((authority = _cogl_pipeline_get_parent (authority))); g_assert (remaining == 0); } /* Comparison of two arbitrary pipelines is done by: * 1) walking up the parents of each pipeline until a common * ancestor is found, and at each step ORing together the * difference masks. * * 2) using the final difference mask to determine which state * groups to compare. * * This is used, for example, by the Cogl journal to compare pipelines so that * it can split up geometry that needs different OpenGL state. * * XXX: When comparing texture layers, _cogl_pipeline_equal will actually * compare the underlying GL texture handle that the Cogl texture uses so that * atlas textures and sub textures will be considered equal if they point to * the same texture. This is useful for comparing pipelines in the journal but * it means that _cogl_pipeline_equal doesn't strictly compare whether the * pipelines are the same. If we needed those semantics we could perhaps add * another function or some flags to control the behaviour. * * XXX: Similarly when comparing the wrap modes, * COGL_PIPELINE_WRAP_MODE_AUTOMATIC is considered to be the same as * COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE because once they get to the * journal stage they act exactly the same. */ gboolean _cogl_pipeline_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1, unsigned int differences, unsigned long layer_differences, CoglPipelineEvalFlags flags) { unsigned long pipelines_difference; CoglPipeline *authorities0[COGL_PIPELINE_STATE_SPARSE_COUNT]; CoglPipeline *authorities1[COGL_PIPELINE_STATE_SPARSE_COUNT]; int bit; gboolean ret; COGL_STATIC_TIMER (pipeline_equal_timer, "Mainloop", /* parent */ "_cogl_pipeline_equal", "The time spent comparing cogl pipelines", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, pipeline_equal_timer); if (pipeline0 == pipeline1) { ret = TRUE; goto done; } ret = FALSE; _cogl_pipeline_update_real_blend_enable (pipeline0, FALSE); _cogl_pipeline_update_real_blend_enable (pipeline1, FALSE); /* First check non-sparse properties */ if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE && pipeline0->real_blend_enable != pipeline1->real_blend_enable) goto done; /* Then check sparse properties */ pipelines_difference = _cogl_pipeline_compare_differences (pipeline0, pipeline1); /* Only compare the sparse state groups requested by the caller... */ pipelines_difference &= differences; _cogl_pipeline_resolve_authorities (pipeline0, pipelines_difference, authorities0); _cogl_pipeline_resolve_authorities (pipeline1, pipelines_difference, authorities1); COGL_FLAGS_FOREACH_START (&pipelines_difference, 1, bit) { /* XXX: We considered having an array of callbacks for each state index * that we'd call here but decided that this way the compiler is more * likely going to be able to in-line the comparison functions and use * the index to jump straight to the required code. */ switch ((CoglPipelineStateIndex)bit) { case COGL_PIPELINE_STATE_COLOR_INDEX: if (!cogl_color_equal (&authorities0[bit]->color, &authorities1[bit]->color)) goto done; break; case COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX: if (!_cogl_pipeline_alpha_func_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX: if (!_cogl_pipeline_alpha_func_reference_state_equal ( authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_BLEND_INDEX: /* We don't need to compare the detailed blending state if we know * blending is disabled for both pipelines. */ if (pipeline0->real_blend_enable) { if (!_cogl_pipeline_blend_state_equal (authorities0[bit], authorities1[bit])) goto done; } break; case COGL_PIPELINE_STATE_DEPTH_INDEX: if (!_cogl_pipeline_depth_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_CULL_FACE_INDEX: if (!_cogl_pipeline_cull_face_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX: if (!_cogl_pipeline_non_zero_point_size_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_POINT_SIZE_INDEX: if (!_cogl_pipeline_point_size_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX: if (!_cogl_pipeline_per_vertex_point_size_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_USER_SHADER_INDEX: if (!_cogl_pipeline_user_shader_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_UNIFORMS_INDEX: if (!_cogl_pipeline_uniforms_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX: if (!_cogl_pipeline_vertex_snippets_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX: if (!_cogl_pipeline_fragment_snippets_state_equal (authorities0[bit], authorities1[bit])) goto done; break; case COGL_PIPELINE_STATE_LAYERS_INDEX: { if (!_cogl_pipeline_layers_equal (authorities0[bit], authorities1[bit], layer_differences, flags)) goto done; break; } case COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX: case COGL_PIPELINE_STATE_COUNT: g_warn_if_reached (); } } COGL_FLAGS_FOREACH_END; ret = TRUE; done: COGL_TIMER_STOP (_cogl_uprof_context, pipeline_equal_timer); return ret; } void _cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline) { CoglPipeline *new_parent = _cogl_pipeline_get_parent (pipeline); /* Before considering pruning redundant ancestry we check if this * pipeline is an authority for layer state and if so only consider * reparenting if it *owns* all the layers it depends on. NB: A * pipeline can be be a STATE_LAYERS authority but it may still * defer to its ancestors to define the state for some of its * layers. * * For example a pipeline that derives from a parent with 5 layers * can become a STATE_LAYERS authority by simply changing it's * ->n_layers count to 4 and in that case it can still defer to its * ancestors to define the state of those 4 layers. * * If a pipeline depends on any ancestors for layer state then we * immediatly bail out. */ if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) { if (pipeline->n_layers != g_list_length (pipeline->layer_differences)) return; } /* walk up past ancestors that are now redundant and potentially * reparent the pipeline. */ while (_cogl_pipeline_get_parent (new_parent) && (new_parent->differences | pipeline->differences) == pipeline->differences) new_parent = _cogl_pipeline_get_parent (new_parent); if (new_parent != _cogl_pipeline_get_parent (pipeline)) { gboolean is_weak = _cogl_pipeline_is_weak (pipeline); _cogl_pipeline_set_parent (pipeline, new_parent, is_weak ? FALSE : TRUE); } } void _cogl_pipeline_update_authority (CoglPipeline *pipeline, CoglPipeline *authority, CoglPipelineState state, CoglPipelineStateComparitor comparitor) { /* If we are the current authority see if we can revert to one of * our ancestors being the authority */ if (pipeline == authority && _cogl_pipeline_get_parent (authority) != NULL) { CoglPipeline *parent = _cogl_pipeline_get_parent (authority); CoglPipeline *old_authority = _cogl_pipeline_get_authority (parent, state); if (comparitor (authority, old_authority)) pipeline->differences &= ~state; } else if (pipeline != authority) { /* If we weren't previously the authority on this state then we * need to extended our differences mask and so it's possible * that some of our ancestry will now become redundant, so we * aim to reparent ourselves if that's true... */ pipeline->differences |= state; _cogl_pipeline_prune_redundant_ancestry (pipeline); } } unsigned long _cogl_pipeline_get_age (CoglPipeline *pipeline) { g_return_val_if_fail (cogl_is_pipeline (pipeline), 0); return pipeline->age; } void cogl_pipeline_remove_layer (CoglPipeline *pipeline, int layer_index) { CoglPipeline *authority; CoglPipelineLayerInfo layer_info; int i; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); /* The layer index of the layer we want info about */ layer_info.layer_index = layer_index; /* This will be updated with a reference to the layer being removed * if it can be found. */ layer_info.layer = NULL; /* This will be filled in with a list of layers that need to be * dropped down to a lower texture unit to fill the gap of the * removed layer. */ layer_info.layers_to_shift = g_alloca (sizeof (CoglPipelineLayer *) * authority->n_layers); layer_info.n_layers_to_shift = 0; /* Unlike when we query layer info when adding a layer we must * always have a complete layers_to_shift list... */ layer_info.ignore_shift_layers_if_found = FALSE; _cogl_pipeline_get_layer_info (authority, &layer_info); if (layer_info.layer == NULL) return; for (i = 0; i < layer_info.n_layers_to_shift; i++) { CoglPipelineLayer *shift_layer = layer_info.layers_to_shift[i]; int unit_index = _cogl_pipeline_layer_get_unit_index (shift_layer); _cogl_pipeline_set_layer_unit (pipeline, shift_layer, unit_index - 1); /* NB: shift_layer may not be writeable so _set_layer_unit() * will allocate a derived layer internally which will become * owned by pipeline. Check the return value if we need to do * anything else with this layer. */ } _cogl_pipeline_remove_layer_difference (pipeline, layer_info.layer, TRUE); _cogl_pipeline_try_reverting_layers_authority (pipeline, NULL); pipeline->dirty_real_blend_enable = TRUE; } int cogl_pipeline_get_n_layers (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), 0); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_LAYERS); return authority->n_layers; } void _cogl_pipeline_pre_paint_for_layer (CoglPipeline *pipeline, int layer_id) { CoglPipelineLayer *layer = _cogl_pipeline_get_layer (pipeline, layer_id); _cogl_pipeline_layer_pre_paint (layer); } /* While a pipeline is referenced by the Cogl journal we can not allow * modifications, so this gives us a mechanism to track journal * references separately */ CoglPipeline * _cogl_pipeline_journal_ref (CoglPipeline *pipeline) { pipeline->journal_ref_count++; return cogl_object_ref (pipeline); } void _cogl_pipeline_journal_unref (CoglPipeline *pipeline) { pipeline->journal_ref_count--; cogl_object_unref (pipeline); } #ifdef COGL_DEBUG_ENABLED void _cogl_pipeline_set_static_breadcrumb (CoglPipeline *pipeline, const char *breadcrumb) { pipeline->has_static_breadcrumb = TRUE; pipeline->static_breadcrumb = breadcrumb; } #endif typedef void (*LayerStateHashFunction) (CoglPipelineLayer *authority, CoglPipelineLayer **authorities, CoglPipelineHashState *state); static LayerStateHashFunction layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; /* XXX: We don't statically initialize the array of hash functions, so * we won't get caught out by later re-indexing the groups for some * reason. */ void _cogl_pipeline_init_layer_state_hash_functions (void) { CoglPipelineLayerStateIndex _index; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_UNIT_INDEX] = _cogl_pipeline_layer_hash_unit_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX] = _cogl_pipeline_layer_hash_texture_data_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX] = _cogl_pipeline_layer_hash_sampler_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX] = _cogl_pipeline_layer_hash_combine_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX] = _cogl_pipeline_layer_hash_combine_constant_state; layer_state_hash_functions[COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX] = _cogl_pipeline_layer_hash_user_matrix_state; _index = COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX; layer_state_hash_functions[_index] = _cogl_pipeline_layer_hash_point_sprite_state; _index = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX; layer_state_hash_functions[_index] = _cogl_pipeline_layer_hash_point_sprite_state; _index = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX; layer_state_hash_functions[_index] = _cogl_pipeline_layer_hash_fragment_snippets_state; { /* So we get a big error if we forget to update this code! */ _COGL_STATIC_ASSERT (COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT == 9, "Don't forget to install a hash function for new " "pipeline state and update assert at end of " "_cogl_pipeline_init_state_hash_functions"); } } static gboolean _cogl_pipeline_hash_layer_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineHashState *state = user_data; unsigned long differences = state->layer_differences; CoglPipelineLayer *authorities[COGL_PIPELINE_LAYER_STATE_COUNT]; unsigned long mask; int i; /* Theoretically we would hash non-sparse layer state here but * currently layers don't have any. */ /* XXX: we resolve all the authorities here - not just those * corresponding to hash_state->layer_differences - because * the hashing of some state groups actually depends on the values * in other groups. For example we don't hash layer combine * constants if they are aren't referenced by the current layer * combine function. */ mask = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; _cogl_pipeline_layer_resolve_authorities (layer, mask, authorities); /* So we go right ahead and hash the sparse state... */ for (i = 0; i < COGL_PIPELINE_LAYER_STATE_COUNT; i++) { unsigned long current_state = (1L< differences) break; } return TRUE; } void _cogl_pipeline_hash_layers_state (CoglPipeline *authority, CoglPipelineHashState *state) { state->hash = _cogl_util_one_at_a_time_hash (state->hash, &authority->n_layers, sizeof (authority->n_layers)); _cogl_pipeline_foreach_layer_internal (authority, _cogl_pipeline_hash_layer_cb, state); } typedef void (*StateHashFunction) (CoglPipeline *authority, CoglPipelineHashState *state); static StateHashFunction state_hash_functions[COGL_PIPELINE_STATE_SPARSE_COUNT]; /* We don't statically initialize the array of hash functions * so we won't get caught out by later re-indexing the groups for * some reason. */ void _cogl_pipeline_init_state_hash_functions (void) { state_hash_functions[COGL_PIPELINE_STATE_COLOR_INDEX] = _cogl_pipeline_hash_color_state; state_hash_functions[COGL_PIPELINE_STATE_LAYERS_INDEX] = _cogl_pipeline_hash_layers_state; state_hash_functions[COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX] = _cogl_pipeline_hash_alpha_func_state; state_hash_functions[COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX] = _cogl_pipeline_hash_alpha_func_reference_state; state_hash_functions[COGL_PIPELINE_STATE_BLEND_INDEX] = _cogl_pipeline_hash_blend_state; state_hash_functions[COGL_PIPELINE_STATE_USER_SHADER_INDEX] = _cogl_pipeline_hash_user_shader_state; state_hash_functions[COGL_PIPELINE_STATE_DEPTH_INDEX] = _cogl_pipeline_hash_depth_state; state_hash_functions[COGL_PIPELINE_STATE_CULL_FACE_INDEX] = _cogl_pipeline_hash_cull_face_state; state_hash_functions[COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX] = _cogl_pipeline_hash_non_zero_point_size_state; state_hash_functions[COGL_PIPELINE_STATE_POINT_SIZE_INDEX] = _cogl_pipeline_hash_point_size_state; state_hash_functions[COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX] = _cogl_pipeline_hash_per_vertex_point_size_state; state_hash_functions[COGL_PIPELINE_STATE_UNIFORMS_INDEX] = _cogl_pipeline_hash_uniforms_state; state_hash_functions[COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX] = _cogl_pipeline_hash_vertex_snippets_state; state_hash_functions[COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX] = _cogl_pipeline_hash_fragment_snippets_state; { /* So we get a big error if we forget to update this code! */ _COGL_STATIC_ASSERT (COGL_PIPELINE_STATE_SPARSE_COUNT == 14, "Make sure to install a hash function for " "newly added pipeline state and update assert " "in _cogl_pipeline_init_state_hash_functions"); } } unsigned int _cogl_pipeline_hash (CoglPipeline *pipeline, unsigned int differences, unsigned long layer_differences, CoglPipelineEvalFlags flags) { CoglPipeline *authorities[COGL_PIPELINE_STATE_SPARSE_COUNT]; unsigned int mask; int i; CoglPipelineHashState state; unsigned int final_hash = 0; state.hash = 0; state.layer_differences = layer_differences; state.flags = flags; _cogl_pipeline_update_real_blend_enable (pipeline, FALSE); /* hash non-sparse state */ if (differences & COGL_PIPELINE_STATE_REAL_BLEND_ENABLE) { gboolean enable = pipeline->real_blend_enable; state.hash = _cogl_util_one_at_a_time_hash (state.hash, &enable, sizeof (enable)); } /* hash sparse state */ mask = differences & COGL_PIPELINE_STATE_ALL_SPARSE; _cogl_pipeline_resolve_authorities (pipeline, mask, authorities); for (i = 0; i < COGL_PIPELINE_STATE_SPARSE_COUNT; i++) { unsigned int current_state = (1< differences) break; } return _cogl_util_one_at_a_time_mix (final_hash); } typedef struct { CoglContext *context; CoglPipeline *src_pipeline; CoglPipeline *dst_pipeline; unsigned int layer_differences; } DeepCopyData; static gboolean deep_copy_layer_cb (CoglPipelineLayer *src_layer, void *user_data) { DeepCopyData *data = user_data; CoglPipelineLayer *dst_layer; unsigned int differences = data->layer_differences; dst_layer = _cogl_pipeline_get_layer (data->dst_pipeline, src_layer->index); while (src_layer != data->context->default_layer_n && src_layer != data->context->default_layer_0 && differences) { unsigned long to_copy = differences & src_layer->differences; if (to_copy) { _cogl_pipeline_layer_copy_differences (dst_layer, src_layer, to_copy); differences ^= to_copy; } src_layer = COGL_PIPELINE_LAYER (COGL_NODE (src_layer)->parent); } return TRUE; } CoglPipeline * _cogl_pipeline_deep_copy (CoglPipeline *pipeline, unsigned long differences, unsigned long layer_differences) { CoglPipeline *new, *authority; gboolean copy_layer_state; _COGL_GET_CONTEXT (ctx, NULL); if ((differences & COGL_PIPELINE_STATE_LAYERS)) { copy_layer_state = TRUE; differences &= ~COGL_PIPELINE_STATE_LAYERS; } else copy_layer_state = FALSE; new = cogl_pipeline_new (ctx); for (authority = pipeline; authority != ctx->default_pipeline && differences; authority = COGL_PIPELINE (COGL_NODE (authority)->parent)) { unsigned long to_copy = differences & authority->differences; if (to_copy) { _cogl_pipeline_copy_differences (new, authority, to_copy); differences ^= to_copy; } } if (copy_layer_state) { DeepCopyData data; /* The unit index doesn't need to be copied because it should * end up with the same values anyway because the new pipeline * will have the same indices as the source pipeline */ layer_differences &= ~COGL_PIPELINE_LAYER_STATE_UNIT; data.context = ctx; data.src_pipeline = pipeline; data.dst_pipeline = new; data.layer_differences = layer_differences; _cogl_pipeline_foreach_layer_internal (pipeline, deep_copy_layer_cb, &data); } return new; } typedef struct { int i; CoglPipelineLayer **layers; } AddLayersToArrayState; static gboolean add_layer_to_array_cb (CoglPipelineLayer *layer, void *user_data) { AddLayersToArrayState *state = user_data; state->layers[state->i++] = layer; return TRUE; } /* This tries to find the oldest ancestor whose pipeline and layer state matches the given flags. This is mostly used to detect code gen authorities so that we can reduce the numer of programs generated */ CoglPipeline * _cogl_pipeline_find_equivalent_parent (CoglPipeline *pipeline, CoglPipelineState pipeline_state, CoglPipelineLayerState layer_state) { CoglPipeline *authority0; CoglPipeline *authority1; int n_layers; CoglPipelineLayer **authority0_layers; CoglPipelineLayer **authority1_layers; /* Find the first pipeline that modifies state that affects the * state or any layer state... */ authority0 = _cogl_pipeline_get_authority (pipeline, pipeline_state | COGL_PIPELINE_STATE_LAYERS); /* Find the next ancestor after that, that also modifies the * state... */ if (_cogl_pipeline_get_parent (authority0)) { authority1 = _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority0), pipeline_state | COGL_PIPELINE_STATE_LAYERS); } else return authority0; n_layers = cogl_pipeline_get_n_layers (authority0); for (;;) { AddLayersToArrayState state; int i; if (n_layers != cogl_pipeline_get_n_layers (authority1)) return authority0; /* If the programs differ by anything that isn't part of the layer state then we can't continue */ if (pipeline_state && (_cogl_pipeline_compare_differences (authority0, authority1) & pipeline_state)) return authority0; authority0_layers = g_alloca (sizeof (CoglPipelineLayer *) * n_layers); state.i = 0; state.layers = authority0_layers; _cogl_pipeline_foreach_layer_internal (authority0, add_layer_to_array_cb, &state); authority1_layers = g_alloca (sizeof (CoglPipelineLayer *) * n_layers); state.i = 0; state.layers = authority1_layers; _cogl_pipeline_foreach_layer_internal (authority1, add_layer_to_array_cb, &state); for (i = 0; i < n_layers; i++) { unsigned long layer_differences; if (authority0_layers[i] == authority1_layers[i]) continue; layer_differences = _cogl_pipeline_layer_compare_differences (authority0_layers[i], authority1_layers[i]); if (layer_differences & layer_state) return authority0; } /* Find the next ancestor after that, that also modifies state * affecting codegen... */ if (!_cogl_pipeline_get_parent (authority1)) break; authority0 = authority1; authority1 = _cogl_pipeline_get_authority (_cogl_pipeline_get_parent (authority1), pipeline_state | COGL_PIPELINE_STATE_LAYERS); if (authority1 == authority0) break; } return authority1; } CoglPipelineState _cogl_pipeline_get_state_for_vertex_codegen (CoglContext *context) { CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS | COGL_PIPELINE_STATE_USER_SHADER | COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE | COGL_PIPELINE_STATE_VERTEX_SNIPPETS); state |= COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE; return state; } CoglPipelineLayerState _cogl_pipeline_get_layer_state_for_fragment_codegen (CoglContext *context) { CoglPipelineLayerState state = (COGL_PIPELINE_LAYER_STATE_COMBINE | COGL_PIPELINE_LAYER_STATE_UNIT | COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS); /* Since the driver supports GLSL then we might be using gl_PointCoord * to implement the sprite coords. In that case the generated code * depends on the point sprite state */ state |= COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS; return state; } CoglPipelineState _cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context) { CoglPipelineState state = (COGL_PIPELINE_STATE_LAYERS | COGL_PIPELINE_STATE_USER_SHADER | COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS | COGL_PIPELINE_STATE_ALPHA_FUNC); return state; } int cogl_pipeline_get_uniform_location (CoglPipeline *pipeline, const char *uniform_name) { void *location_ptr; char *uniform_name_copy; _COGL_GET_CONTEXT (ctx, -1); /* This API is designed as if the uniform locations are specific to a pipeline but they are actually unique across a whole CoglContext. Potentially this could just be cogl_context_get_uniform_location but it seems to make sense to keep the API this way so that we can change the internals if need be. */ /* Look for an existing uniform with this name */ if (g_hash_table_lookup_extended (ctx->uniform_name_hash, uniform_name, NULL, &location_ptr)) return GPOINTER_TO_INT (location_ptr); uniform_name_copy = g_strdup (uniform_name); g_ptr_array_add (ctx->uniform_names, uniform_name_copy); g_hash_table_insert (ctx->uniform_name_hash, uniform_name_copy, GINT_TO_POINTER (ctx->n_uniform_names)); return ctx->n_uniform_names++; } muffin-6.4.1/cogl/cogl/cogl-index-buffer-private.h0000664000175000017500000000266414723361714020743 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_INDEX_BUFFER_PRIVATE_H #define __COGL_INDEX_BUFFER_PRIVATE_H #include "cogl-buffer-private.h" struct _CoglIndexBuffer { CoglBuffer _parent; }; #endif /* __COGL_INDEX_BUFFER_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-wayland-server.h0000664000175000017500000000603614723361714017655 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef __COGL_WAYLAND_SERVER_H #define __COGL_WAYLAND_SERVER_H #include /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_WAYLAND_SERVER_ #endif #endif /* COGL_COMPILATION */ #include #include G_BEGIN_DECLS /** * cogl_wayland_display_set_compositor_display: * @display: a #CoglDisplay * @wayland_display: A compositor's Wayland display pointer * * Informs Cogl of a compositor's Wayland display pointer. This * enables Cogl to register private wayland extensions required to * pass buffers between the clients and compositor. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_wayland_display_set_compositor_display (CoglDisplay *display, struct wl_display *wayland_display); G_END_DECLS /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_WAYLAND_SERVER_ #undef __COGL_H_INSIDE__ #undef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_WAYLAND_SERVER_ #endif #endif /* __COGL_WAYLAND_SERVER_H */ muffin-6.4.1/cogl/cogl/cogl-matrix.h0000664000175000017500000005712414723361714016222 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_MATRIX_H #define __COGL_MATRIX_H #include #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-matrix * @short_description: Functions for initializing and manipulating 4x4 matrices * * Matrices are used in Cogl to describe affine model-view transforms, texture * transforms, and projective transforms. This exposes a utility API that can * be used for direct manipulation of these matrices. */ /** * CoglMatrix: * * A CoglMatrix holds a 4x4 transform matrix. This is a single precision, * column-major matrix which means it is compatible with what OpenGL expects. * * A CoglMatrix can represent transforms such as, rotations, scaling, * translation, sheering, and linear projections. You can combine these * transforms by multiplying multiple matrices in the order you want them * applied. * * The transformation of a vertex (x, y, z, w) by a CoglMatrix is given by: * * |[ * x_new = xx * x + xy * y + xz * z + xw * w * y_new = yx * x + yy * y + yz * z + yw * w * z_new = zx * x + zy * y + zz * z + zw * w * w_new = wx * x + wy * y + wz * z + ww * w * ]| * * Where w is normally 1 * * You must consider the members of the CoglMatrix structure read only, * and all matrix modifications must be done via the cogl_matrix API. This * allows Cogl to annotate the matrices internally. Violation of this will give * undefined results. If you need to initialize a matrix with a constant other * than the identity matrix you can use cogl_matrix_init_from_array(). */ struct _CoglMatrix { /* column 0 */ float xx; float yx; float zx; float wx; /* column 1 */ float xy; float yy; float zy; float wy; /* column 2 */ float xz; float yz; float zz; float wz; /* column 3 */ float xw; float yw; float zw; float ww; /*< private >*/ /* Note: we may want to extend this later with private flags * and a cache of the inverse transform matrix. */ float COGL_PRIVATE (inv)[16]; unsigned long COGL_PRIVATE (type); unsigned long COGL_PRIVATE (flags); unsigned long COGL_PRIVATE (_padding3); }; COGL_STRUCT_SIZE_ASSERT (CoglMatrix, 128 + sizeof (unsigned long) * 3); /** * cogl_matrix_init_identity: * @matrix: A 4x4 transformation matrix * * Resets matrix to the identity matrix: * * |[ * .xx=1; .xy=0; .xz=0; .xw=0; * .yx=0; .yy=1; .yz=0; .yw=0; * .zx=0; .zy=0; .zz=1; .zw=0; * .wx=0; .wy=0; .wz=0; .ww=1; * ]| */ COGL_EXPORT void cogl_matrix_init_identity (CoglMatrix *matrix); /** * cogl_matrix_init_translation: * @matrix: A 4x4 transformation matrix * @tx: x coordinate of the translation vector * @ty: y coordinate of the translation vector * @tz: z coordinate of the translation vector * * Resets matrix to the (tx, ty, tz) translation matrix: * * |[ * .xx=1; .xy=0; .xz=0; .xw=tx; * .yx=0; .yy=1; .yz=0; .yw=ty; * .zx=0; .zy=0; .zz=1; .zw=tz; * .wx=0; .wy=0; .wz=0; .ww=1; * ]| * * Since: 2.0 */ COGL_EXPORT void cogl_matrix_init_translation (CoglMatrix *matrix, float tx, float ty, float tz); /** * cogl_matrix_multiply: * @result: The address of a 4x4 matrix to store the result in * @a: A 4x4 transformation matrix * @b: A 4x4 transformation matrix * * Multiplies the two supplied matrices together and stores * the resulting matrix inside @result. * * It is possible to multiply the @a matrix in-place, so * @result can be equal to @a but can't be equal to @b. */ COGL_EXPORT void cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, const CoglMatrix *b); /** * cogl_matrix_rotate: * @matrix: A 4x4 transformation matrix * @angle: The angle you want to rotate in degrees * @x: X component of your rotation vector * @y: Y component of your rotation vector * @z: Z component of your rotation vector * * Multiplies @matrix with a rotation matrix that applies a rotation * of @angle degrees around the specified 3D vector. */ COGL_EXPORT void cogl_matrix_rotate (CoglMatrix *matrix, float angle, float x, float y, float z); /** * cogl_matrix_rotate_euler: * @matrix: A 4x4 transformation matrix * @euler: A euler describing a rotation * * Multiplies @matrix with a rotation transformation described by the * given #graphene_euler_t. * * Since: 2.0 */ COGL_EXPORT void cogl_matrix_rotate_euler (CoglMatrix *matrix, const graphene_euler_t *euler); /** * cogl_matrix_translate: * @matrix: A 4x4 transformation matrix * @x: The X translation you want to apply * @y: The Y translation you want to apply * @z: The Z translation you want to apply * * Multiplies @matrix with a transform matrix that translates along * the X, Y and Z axis. */ COGL_EXPORT void cogl_matrix_translate (CoglMatrix *matrix, float x, float y, float z); /** * cogl_matrix_scale: * @matrix: A 4x4 transformation matrix * @sx: The X scale factor * @sy: The Y scale factor * @sz: The Z scale factor * * Multiplies @matrix with a transform matrix that scales along the X, * Y and Z axis. */ COGL_EXPORT void cogl_matrix_scale (CoglMatrix *matrix, float sx, float sy, float sz); /** * cogl_matrix_look_at: * @matrix: A 4x4 transformation matrix * @eye_position_x: The X coordinate to look from * @eye_position_y: The Y coordinate to look from * @eye_position_z: The Z coordinate to look from * @object_x: The X coordinate of the object to look at * @object_y: The Y coordinate of the object to look at * @object_z: The Z coordinate of the object to look at * @world_up_x: The X component of the world's up direction vector * @world_up_y: The Y component of the world's up direction vector * @world_up_z: The Z component of the world's up direction vector * * Applies a view transform @matrix that positions the camera at * the coordinate (@eye_position_x, @eye_position_y, @eye_position_z) * looking towards an object at the coordinate (@object_x, @object_y, * @object_z). The top of the camera is aligned to the given world up * vector, which is normally simply (0, 1, 0) to map up to the * positive direction of the y axis. * * Because there is a lot of missleading documentation online for * gluLookAt regarding the up vector we want to try and be a bit * clearer here. * * The up vector should simply be relative to your world coordinates * and does not need to change as you move the eye and object * positions. Many online sources may claim that the up vector needs * to be perpendicular to the vector between the eye and object * position (partly because the man page is somewhat missleading) but * that is not necessary for this function. * * You should never look directly along the world-up * vector. * * It is assumed you are using a typical projection matrix where * your origin maps to the center of your viewport. * * Almost always when you use this function it should be the first * transform applied to a new modelview transform * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_matrix_look_at (CoglMatrix *matrix, float eye_position_x, float eye_position_y, float eye_position_z, float object_x, float object_y, float object_z, float world_up_x, float world_up_y, float world_up_z); /** * cogl_matrix_frustum: * @matrix: A 4x4 transformation matrix * @left: X position of the left clipping plane where it * intersects the near clipping plane * @right: X position of the right clipping plane where it * intersects the near clipping plane * @bottom: Y position of the bottom clipping plane where it * intersects the near clipping plane * @top: Y position of the top clipping plane where it intersects * the near clipping plane * @z_near: The distance to the near clipping plane (Must be positive) * @z_far: The distance to the far clipping plane (Must be positive) * * Multiplies @matrix by the given frustum perspective matrix. */ COGL_EXPORT void cogl_matrix_frustum (CoglMatrix *matrix, float left, float right, float bottom, float top, float z_near, float z_far); /** * cogl_matrix_perspective: * @matrix: A 4x4 transformation matrix * @fov_y: Vertical field of view angle in degrees. * @aspect: The (width over height) aspect ratio for display * @z_near: The distance to the near clipping plane (Must be positive, * and must not be 0) * @z_far: The distance to the far clipping plane (Must be positive) * * Multiplies @matrix by the described perspective matrix * * You should be careful not to have to great a @z_far / @z_near * ratio since that will reduce the effectiveness of depth testing * since there wont be enough precision to identify the depth of * objects near to each other. */ COGL_EXPORT void cogl_matrix_perspective (CoglMatrix *matrix, float fov_y, float aspect, float z_near, float z_far); /** * cogl_matrix_orthographic: * @matrix: A 4x4 transformation matrix * @x_1: The x coordinate for the first vertical clipping plane * @y_1: The y coordinate for the first horizontal clipping plane * @x_2: The x coordinate for the second vertical clipping plane * @y_2: The y coordinate for the second horizontal clipping plane * @near: The distance to the near clipping * plane (will be negative if the plane is * behind the viewer) * @far: The distance to the far clipping * plane (will be negative if the plane is * behind the viewer) * * Multiplies @matrix by a parallel projection matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_matrix_orthographic (CoglMatrix *matrix, float x_1, float y_1, float x_2, float y_2, float near, float far); /** * cogl_matrix_view_2d_in_frustum: * @matrix: A 4x4 transformation matrix * @left: coord of left vertical clipping plane * @right: coord of right vertical clipping plane * @bottom: coord of bottom horizontal clipping plane * @top: coord of top horizontal clipping plane * @z_near: The distance to the near clip plane. Never pass 0 and always pass * a positive number. * @z_2d: The distance to the 2D plane. (Should always be positive and * be between @z_near and the z_far value that was passed to * cogl_matrix_frustum()) * @width_2d: The width of the 2D coordinate system * @height_2d: The height of the 2D coordinate system * * Multiplies @matrix by a view transform that maps the 2D coordinates * (0,0) top left and (@width_2d,@height_2d) bottom right the full viewport * size. Geometry at a depth of 0 will now lie on this 2D plane. * * Note: this doesn't multiply the matrix by any projection matrix, * but it assumes you have a perspective projection as defined by * passing the corresponding arguments to cogl_matrix_frustum(). * Toolkits such as Clutter that mix 2D and 3D drawing can use this to * create a 2D coordinate system within a 3D perspective projected * view frustum. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_matrix_view_2d_in_frustum (CoglMatrix *matrix, float left, float right, float bottom, float top, float z_near, float z_2d, float width_2d, float height_2d); /** * cogl_matrix_view_2d_in_perspective: * @fov_y: A field of view angle for the Y axis * @aspect: The ratio of width to height determining the field of view angle * for the x axis. * @z_near: The distance to the near clip plane. Never pass 0 and always pass * a positive number. * @z_2d: The distance to the 2D plane. (Should always be positive and * be between @z_near and the z_far value that was passed to * cogl_matrix_frustum()) * @width_2d: The width of the 2D coordinate system * @height_2d: The height of the 2D coordinate system * * Multiplies @matrix by a view transform that maps the 2D coordinates * (0,0) top left and (@width_2d,@height_2d) bottom right the full viewport * size. Geometry at a depth of 0 will now lie on this 2D plane. * * Note: this doesn't multiply the matrix by any projection matrix, * but it assumes you have a perspective projection as defined by * passing the corresponding arguments to cogl_matrix_perspective(). * * Toolkits such as Clutter that mix 2D and 3D drawing can use this to * create a 2D coordinate system within a 3D perspective projected * view frustum. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_matrix_view_2d_in_perspective (CoglMatrix *matrix, float fov_y, float aspect, float z_near, float z_2d, float width_2d, float height_2d); /** * cogl_matrix_init_from_array: * @matrix: A 4x4 transformation matrix * @array: A linear array of 16 floats (column-major order) * * Initializes @matrix with the contents of @array */ COGL_EXPORT void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array); /** * cogl_matrix_get_array: * @matrix: A 4x4 transformation matrix * * Casts @matrix to a float array which can be directly passed to OpenGL. * * Return value: a pointer to the float array */ COGL_EXPORT const float * cogl_matrix_get_array (const CoglMatrix *matrix); /** * cogl_matrix_init_from_euler: * @matrix: A 4x4 transformation matrix * @euler: A #graphene_euler_t * * Initializes @matrix from a #graphene_euler_t rotation. */ COGL_EXPORT void cogl_matrix_init_from_euler (CoglMatrix *matrix, const graphene_euler_t *euler); /** * cogl_matrix_equal: * @v1: A 4x4 transformation matrix * @v2: A 4x4 transformation matrix * * Compares two matrices to see if they represent the same * transformation. Although internally the matrices may have different * annotations associated with them and may potentially have a cached * inverse matrix these are not considered in the comparison. * * Since: 1.4 */ COGL_EXPORT gboolean cogl_matrix_equal (const void *v1, const void *v2); /** * cogl_matrix_copy: * @matrix: A 4x4 transformation matrix you want to copy * * Allocates a new #CoglMatrix on the heap and initializes it with * the same values as @matrix. * * Return value: (transfer full): A newly allocated #CoglMatrix which * should be freed using cogl_matrix_free() * * Since: 1.6 */ COGL_EXPORT CoglMatrix * cogl_matrix_copy (const CoglMatrix *matrix); /** * cogl_matrix_free: * @matrix: A 4x4 transformation matrix you want to free * * Frees a #CoglMatrix that was previously allocated via a call to * cogl_matrix_copy(). * * Since: 1.6 */ COGL_EXPORT void cogl_matrix_free (CoglMatrix *matrix); /** * cogl_matrix_get_inverse: * @matrix: A 4x4 transformation matrix * @inverse: (out): The destination for a 4x4 inverse transformation matrix * * Gets the inverse transform of a given matrix and uses it to initialize * a new #CoglMatrix. * * Although the first parameter is annotated as const to indicate * that the transform it represents isn't modified this function may * technically save a copy of the inverse transform within the given * #CoglMatrix so that subsequent requests for the inverse transform may * avoid costly inversion calculations. * * Return value: %TRUE if the inverse was successfully calculated or %FALSE * for degenerate transformations that can't be inverted (in this case the * @inverse matrix will simply be initialized with the identity matrix) * * Since: 1.2 */ COGL_EXPORT gboolean cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse); /* FIXME: to be consistent with cogl_matrix_{transform,project}_points * this could be renamed to cogl_matrix_project_point for Cogl 2.0... */ /** * cogl_matrix_transform_point: * @matrix: A 4x4 transformation matrix * @x: (inout): The X component of your points position * @y: (inout): The Y component of your points position * @z: (inout): The Z component of your points position * @w: (inout): The W component of your points position * * Transforms a point whos position is given and returned as four float * components. */ COGL_EXPORT void cogl_matrix_transform_point (const CoglMatrix *matrix, float *x, float *y, float *z, float *w); /** * cogl_matrix_transform_points: * @matrix: A transformation matrix * @n_components: The number of position components for each input point. * (either 2 or 3) * @stride_in: The stride in bytes between input points. * @points_in: A pointer to the first component of the first input point. * @stride_out: The stride in bytes between output points. * @points_out: A pointer to the first component of the first output point. * @n_points: The number of points to transform. * * Transforms an array of input points and writes the result to * another array of output points. The input points can either have 2 * or 3 components each. The output points always have 3 components. * The output array can simply point to the input array to do the * transform in-place. * * If you need to transform 4 component points see * cogl_matrix_project_points(). * * Here's an example with differing input/output strides: * |[ * typedef struct { * float x,y; * uint8_t r,g,b,a; * float s,t,p; * } MyInVertex; * typedef struct { * uint8_t r,g,b,a; * float x,y,z; * } MyOutVertex; * MyInVertex vertices[N_VERTICES]; * MyOutVertex results[N_VERTICES]; * CoglMatrix matrix; * * my_load_vertices (vertices); * my_get_matrix (&matrix); * * cogl_matrix_transform_points (&matrix, * 2, * sizeof (MyInVertex), * &vertices[0].x, * sizeof (MyOutVertex), * &results[0].x, * N_VERTICES); * ]| * * Stability: unstable */ COGL_EXPORT void cogl_matrix_transform_points (const CoglMatrix *matrix, int n_components, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points); /** * cogl_matrix_project_points: * @matrix: A projection matrix * @n_components: The number of position components for each input point. * (either 2, 3 or 4) * @stride_in: The stride in bytes between input points. * @points_in: A pointer to the first component of the first input point. * @stride_out: The stride in bytes between output points. * @points_out: A pointer to the first component of the first output point. * @n_points: The number of points to transform. * * Projects an array of input points and writes the result to another * array of output points. The input points can either have 2, 3 or 4 * components each. The output points always have 4 components (known * as homogenous coordinates). The output array can simply point to * the input array to do the transform in-place. * * Here's an example with differing input/output strides: * |[ * typedef struct { * float x,y; * uint8_t r,g,b,a; * float s,t,p; * } MyInVertex; * typedef struct { * uint8_t r,g,b,a; * float x,y,z; * } MyOutVertex; * MyInVertex vertices[N_VERTICES]; * MyOutVertex results[N_VERTICES]; * CoglMatrix matrix; * * my_load_vertices (vertices); * my_get_matrix (&matrix); * * cogl_matrix_project_points (&matrix, * 2, * sizeof (MyInVertex), * &vertices[0].x, * sizeof (MyOutVertex), * &results[0].x, * N_VERTICES); * ]| * * Stability: unstable */ COGL_EXPORT void cogl_matrix_project_points (const CoglMatrix *matrix, int n_components, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points); /** * cogl_matrix_is_identity: * @matrix: A #CoglMatrix * * Determines if the given matrix is an identity matrix. * * Returns: %TRUE if @matrix is an identity matrix else %FALSE * Since: 1.8 */ COGL_EXPORT gboolean cogl_matrix_is_identity (const CoglMatrix *matrix); /** * cogl_matrix_transpose: * @matrix: A #CoglMatrix * * Replaces @matrix with its transpose. Ie, every element (i,j) in the * new matrix is taken from element (j,i) in the old matrix. * * Since: 1.10 */ COGL_EXPORT void cogl_matrix_transpose (CoglMatrix *matrix); /** * cogl_debug_matrix_print: * @matrix: A #CoglMatrix * * Prints the contents of a #CoglMatrix to stdout. * * Since: 2.0 */ COGL_EXPORT void cogl_debug_matrix_print (const CoglMatrix *matrix); #define COGL_GTYPE_TYPE_MATRIX (cogl_matrix_get_gtype ()) /** * cogl_matrix_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_matrix_get_gtype (void); /** * cogl_gtype_matrix_get_type: * * Returns: the GType for the registered "CoglMatrix" boxed type. This * can be used for example to define GObject properties that accept a * #CoglMatrix value. * * Deprecated: 1.18: Use cogl_matrix_get_gtype() instead. */ COGL_EXPORT GType cogl_gtype_matrix_get_type (void); G_END_DECLS #endif /* __COGL_MATRIX_H */ muffin-6.4.1/cogl/cogl/cogl-texture-2d.c0000664000175000017500000003633414723361714016714 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-private.h" #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-texture-driver.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-journal-private.h" #include "cogl-framebuffer-private.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-texture-2d-gl-private.h" #ifdef COGL_HAS_EGL_SUPPORT #include "winsys/cogl-winsys-egl-private.h" #endif #include #include #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT #include "cogl-wayland-server.h" #endif static void _cogl_texture_2d_free (CoglTexture2D *tex_2d); COGL_TEXTURE_DEFINE (Texture2D, texture_2d); COGL_GTYPE_DEFINE_CLASS (Texture2D, texture_2d, COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); static const CoglTextureVtable cogl_texture_2d_vtable; typedef struct _CoglTexture2DManualRepeatData { CoglTexture2D *tex_2d; CoglMetaTextureCallback callback; void *user_data; } CoglTexture2DManualRepeatData; static void _cogl_texture_2d_free (CoglTexture2D *tex_2d) { CoglContext *ctx = COGL_TEXTURE (tex_2d)->context; ctx->driver_vtable->texture_2d_free (tex_2d); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_2d)); } void _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, gboolean value) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); tex_2d->auto_mipmap = value; } CoglTexture2D * _cogl_texture_2d_create_base (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format, CoglTextureLoader *loader) { CoglTexture2D *tex_2d = g_new (CoglTexture2D, 1); CoglTexture *tex = COGL_TEXTURE (tex_2d); _cogl_texture_init (tex, ctx, width, height, internal_format, loader, &cogl_texture_2d_vtable); tex_2d->mipmaps_dirty = TRUE; tex_2d->auto_mipmap = TRUE; tex_2d->is_get_data_supported = TRUE; tex_2d->gl_target = GL_TEXTURE_2D; ctx->driver_vtable->texture_2d_init (tex_2d); return _cogl_texture_2d_object_new (tex_2d); } CoglTexture2D * cogl_texture_2d_new_with_size (CoglContext *ctx, int width, int height) { CoglTextureLoader *loader; g_return_val_if_fail (width >= 1, NULL); g_return_val_if_fail (height >= 1, NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; loader->src.sized.width = width; loader->src.sized.height = height; return _cogl_texture_2d_create_base (ctx, width, height, COGL_PIXEL_FORMAT_RGBA_8888_PRE, loader); } static gboolean _cogl_texture_2d_allocate (CoglTexture *tex, GError **error) { CoglContext *ctx = tex->context; return ctx->driver_vtable->texture_2d_allocate (tex, error); } CoglTexture2D * _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, gboolean can_convert_in_place) { CoglTextureLoader *loader; g_return_val_if_fail (bmp != NULL, NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; loader->src.bitmap.bitmap = cogl_object_ref (bmp); loader->src.bitmap.can_convert_in_place = can_convert_in_place; return _cogl_texture_2d_create_base (_cogl_bitmap_get_context (bmp), cogl_bitmap_get_width (bmp), cogl_bitmap_get_height (bmp), cogl_bitmap_get_format (bmp), loader); } CoglTexture2D * cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp) { return _cogl_texture_2d_new_from_bitmap (bmp, FALSE); /* can't convert in place */ } CoglTexture2D * cogl_texture_2d_new_from_file (CoglContext *ctx, const char *filename, GError **error) { CoglBitmap *bmp; CoglTexture2D *tex_2d = NULL; g_return_val_if_fail (error == NULL || *error == NULL, NULL); bmp = _cogl_bitmap_from_file (ctx, filename, error); if (bmp == NULL) return NULL; tex_2d = _cogl_texture_2d_new_from_bitmap (bmp, TRUE); /* can convert in-place */ cogl_object_unref (bmp); return tex_2d; } CoglTexture2D * cogl_texture_2d_new_from_data (CoglContext *ctx, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error) { CoglBitmap *bmp; CoglTexture2D *tex_2d; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); g_return_val_if_fail (data != NULL, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); /* Wrap the data into a bitmap */ bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); tex_2d = cogl_texture_2d_new_from_bitmap (bmp); cogl_object_unref (bmp); if (tex_2d && !cogl_texture_allocate (COGL_TEXTURE (tex_2d), error)) { cogl_object_unref (tex_2d); return NULL; } return tex_2d; } #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) /* NB: The reason we require the width, height and format to be passed * even though they may seem redundant is because GLES 1/2 don't * provide a way to query these properties. */ CoglTexture2D * cogl_egl_texture_2d_new_from_image (CoglContext *ctx, int width, int height, CoglPixelFormat format, EGLImageKHR image, CoglEglImageFlags flags, GError **error) { CoglTextureLoader *loader; CoglTexture2D *tex; g_return_val_if_fail (_cogl_context_get_winsys (ctx)->constraints & COGL_RENDERER_CONSTRAINT_USES_EGL, NULL); g_return_val_if_fail (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE), NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE; loader->src.egl_image.image = image; loader->src.egl_image.width = width; loader->src.egl_image.height = height; loader->src.egl_image.format = format; loader->src.egl_image.flags = flags; tex = _cogl_texture_2d_create_base (ctx, width, height, format, loader); if (!cogl_texture_allocate (COGL_TEXTURE (tex), error)) { cogl_object_unref (tex); return NULL; } return tex; } #endif /* defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) */ void _cogl_texture_2d_externally_modified (CoglTexture *texture) { if (!cogl_is_texture_2d (texture)) return; COGL_TEXTURE_2D (texture)->mipmaps_dirty = TRUE; } void _cogl_texture_2d_copy_from_framebuffer (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; /* Assert that the storage for this texture has been allocated */ cogl_texture_allocate (tex, NULL); /* (abort on error) */ ctx->driver_vtable->texture_2d_copy_from_framebuffer (tex_2d, src_x, src_y, width, height, src_fb, dst_x, dst_y, level); tex_2d->mipmaps_dirty = TRUE; } static int _cogl_texture_2d_get_max_waste (CoglTexture *tex) { return -1; } static gboolean _cogl_texture_2d_is_sliced (CoglTexture *tex) { return FALSE; } static gboolean _cogl_texture_2d_can_hardware_repeat (CoglTexture *tex) { return TRUE; } static void _cogl_texture_2d_transform_coords_to_gl (CoglTexture *tex, float *s, float *t) { /* The texture coordinates map directly so we don't need to do anything */ } static CoglTransformResult _cogl_texture_2d_transform_quad_coords_to_gl (CoglTexture *tex, float *coords) { /* The texture coordinates map directly so we don't need to do anything other than check for repeats */ int i; for (i = 0; i < 4; i++) if (coords[i] < 0.0f || coords[i] > 1.0f) { /* Repeat is needed */ return (_cogl_texture_2d_can_hardware_repeat (tex) ? COGL_TRANSFORM_HARDWARE_REPEAT : COGL_TRANSFORM_SOFTWARE_REPEAT); } /* No repeat is needed */ return COGL_TRANSFORM_NO_REPEAT; } static gboolean _cogl_texture_2d_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target) { CoglContext *ctx = tex->context; CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); if (ctx->driver_vtable->texture_2d_get_gl_handle) { GLuint handle; if (out_gl_target) *out_gl_target = tex_2d->gl_target; handle = ctx->driver_vtable->texture_2d_get_gl_handle (tex_2d); if (out_gl_handle) *out_gl_handle = handle; return handle ? TRUE : FALSE; } else return FALSE; } static void _cogl_texture_2d_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); /* Only update if the mipmaps are dirty */ if ((flags & COGL_TEXTURE_NEEDS_MIPMAP) && tex_2d->auto_mipmap && tex_2d->mipmaps_dirty) { CoglContext *ctx = tex->context; /* Since we are about to ask the GPU to generate mipmaps of tex, we * better make sure tex is up-to-date. */ _cogl_texture_flush_journal_rendering (tex); ctx->driver_vtable->texture_2d_generate_mipmap (tex_2d); tex_2d->mipmaps_dirty = FALSE; } } static void _cogl_texture_2d_ensure_non_quad_rendering (CoglTexture *tex) { /* Nothing needs to be done */ } static gboolean _cogl_texture_2d_set_region (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int width, int height, int level, CoglBitmap *bmp, GError **error) { CoglContext *ctx = tex->context; CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); if (!ctx->driver_vtable->texture_2d_copy_from_bitmap (tex_2d, src_x, src_y, width, height, bmp, dst_x, dst_y, level, error)) { return FALSE; } tex_2d->mipmaps_dirty = TRUE; return TRUE; } static gboolean _cogl_texture_2d_is_get_data_supported (CoglTexture *tex) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); CoglContext *ctx = tex->context; return ctx->driver_vtable->texture_2d_is_get_data_supported (tex_2d); } static gboolean _cogl_texture_2d_get_data (CoglTexture *tex, CoglPixelFormat format, int rowstride, uint8_t *data) { CoglContext *ctx = tex->context; if (ctx->driver_vtable->texture_2d_get_data) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); ctx->driver_vtable->texture_2d_get_data (tex_2d, format, rowstride, data); return TRUE; } else return FALSE; } static CoglPixelFormat _cogl_texture_2d_get_format (CoglTexture *tex) { return COGL_TEXTURE_2D (tex)->internal_format; } static GLenum _cogl_texture_2d_get_gl_format (CoglTexture *tex) { return COGL_TEXTURE_2D (tex)->gl_internal_format; } static const CoglTextureVtable cogl_texture_2d_vtable = { TRUE, /* primitive */ _cogl_texture_2d_allocate, _cogl_texture_2d_set_region, _cogl_texture_2d_is_get_data_supported, _cogl_texture_2d_get_data, NULL, /* foreach_sub_texture_in_region */ _cogl_texture_2d_get_max_waste, _cogl_texture_2d_is_sliced, _cogl_texture_2d_can_hardware_repeat, _cogl_texture_2d_transform_coords_to_gl, _cogl_texture_2d_transform_quad_coords_to_gl, _cogl_texture_2d_get_gl_texture, _cogl_texture_2d_gl_flush_legacy_texobj_filters, _cogl_texture_2d_pre_paint, _cogl_texture_2d_ensure_non_quad_rendering, _cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes, _cogl_texture_2d_get_format, _cogl_texture_2d_get_gl_format, _cogl_texture_2d_set_auto_mipmap }; muffin-6.4.1/cogl/cogl/cogl-dma-buf-handle.h0000664000175000017500000000504514723361714017455 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2020 Endless, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Georges Basile Stavracas Neto */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_DMA_BUF_HANDLE_H__ #define __COGL_DMA_BUF_HANDLE_H__ #include #include /** * cogl_dma_buf_handle_new: (skip) */ COGL_EXPORT CoglDmaBufHandle * cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, int dmabuf_fd, gpointer data, GDestroyNotify destroy_func); /** * cogl_dma_buf_handle_free: (skip) * * Releases @dmabuf_handle; it is a programming error to release * an already released handle. */ COGL_EXPORT void cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle); /** * cogl_dma_buf_handle_get_framebuffer: (skip) * * Retrieves the #CoglFramebuffer, backed by an exported DMABuf buffer, * of @dmabuf_handle. * * Returns: (transfer none): a #CoglFramebuffer */ COGL_EXPORT CoglFramebuffer * cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle); /** * cogl_dma_buf_handle_get_fd: (skip) * * Retrieves the file descriptor of @dmabuf_handle. * * Returns: a valid file descriptor */ COGL_EXPORT int cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle); #endif /* __COGL_DMA_BUF_HANDLE_H__ */ muffin-6.4.1/cogl/cogl/cogl-onscreen-template.c0000664000175000017500000000576214723361714020337 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-object.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-gtype-private.h" #include static void _cogl_onscreen_template_free (CoglOnscreenTemplate *onscreen_template); COGL_OBJECT_DEFINE (OnscreenTemplate, onscreen_template); COGL_GTYPE_DEFINE_CLASS (OnscreenTemplate, onscreen_template); static void _cogl_onscreen_template_free (CoglOnscreenTemplate *onscreen_template) { g_slice_free (CoglOnscreenTemplate, onscreen_template); } CoglOnscreenTemplate * cogl_onscreen_template_new (CoglSwapChain *swap_chain) { CoglOnscreenTemplate *onscreen_template = g_slice_new0 (CoglOnscreenTemplate); char *user_config; onscreen_template->config.swap_chain = swap_chain; if (swap_chain) cogl_object_ref (swap_chain); else onscreen_template->config.swap_chain = cogl_swap_chain_new (); onscreen_template->config.need_stencil = TRUE; onscreen_template->config.samples_per_pixel = 0; user_config = getenv ("COGL_POINT_SAMPLES_PER_PIXEL"); if (user_config) { unsigned long samples_per_pixel = strtoul (user_config, NULL, 10); if (samples_per_pixel != ULONG_MAX) onscreen_template->config.samples_per_pixel = samples_per_pixel; } return _cogl_onscreen_template_object_new (onscreen_template); } void cogl_onscreen_template_set_samples_per_pixel ( CoglOnscreenTemplate *onscreen_template, int samples_per_pixel) { onscreen_template->config.samples_per_pixel = samples_per_pixel; } void cogl_onscreen_template_set_stereo_enabled ( CoglOnscreenTemplate *onscreen_template, gboolean enabled) { onscreen_template->config.stereo_enabled = enabled; } muffin-6.4.1/cogl/cogl/cogl-private.h0000664000175000017500000000627114723361714016365 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PRIVATE_H__ #define __COGL_PRIVATE_H__ #include #include "cogl-context.h" #include "cogl-flags.h" G_BEGIN_DECLS typedef enum { COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, COGL_PRIVATE_FEATURE_PBOS, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL, COGL_PRIVATE_FEATURE_OES_EGL_SYNC, /* If this is set then the winsys is responsible for queueing dirty * events. Otherwise a dirty event will be queued when the onscreen * is first allocated or when it is shown or resized */ COGL_PRIVATE_FEATURE_DIRTY_EVENTS, /* This feature allows for explicitly selecting a GL-based backend, * as opposed to nop or (in the future) Vulkan. */ COGL_PRIVATE_FEATURE_ANY_GL, COGL_N_PRIVATE_FEATURES } CoglPrivateFeature; /* Sometimes when evaluating pipelines, either during comparisons or * if calculating a hash value we need to tweak the evaluation * semantics */ typedef enum _CoglPipelineEvalFlags { COGL_PIPELINE_EVAL_FLAG_NONE = 0 } CoglPipelineEvalFlags; void _cogl_transform_point (const CoglMatrix *matrix_mv, const CoglMatrix *matrix_p, const float *viewport, float *x, float *y); gboolean _cogl_check_extension (const char *name, char * const *ext); void _cogl_init (void); #define _cogl_has_private_feature(ctx, feature) \ COGL_FLAGS_GET ((ctx)->private_features, (feature)) G_END_DECLS #endif /* __COGL_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-mutter.h0000664000175000017500000000352114723361714016226 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2016 Red Hat Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_MUTTER_H___ #define __COGL_MUTTER_H___ #include "cogl-config.h" #include "cogl-defines.h" #include #include #include #include #if defined (COGL_HAS_EGL_SUPPORT) #include #endif #include COGL_EXPORT void cogl_renderer_set_custom_winsys (CoglRenderer *renderer, CoglCustomWinsysVtableGetter winsys_vtable_getter, void *user_data); #endif /* __COGL_MUTTER_H___ */ muffin-6.4.1/cogl/cogl/cogl-buffer.h0000664000175000017500000002407514723361714016166 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C)2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_BUFFER_H__ #define __COGL_BUFFER_H__ #include G_BEGIN_DECLS /** * SECTION:cogl-buffer * @short_description: Common buffer functions, including data upload APIs * @stability: unstable * * The CoglBuffer API provides a common interface to manipulate * buffers that have been allocated either via cogl_pixel_buffer_new() * or cogl_attribute_buffer_new(). The API allows you to upload data * to these buffers and define usage hints that help Cogl manage your * buffer optimally. * * Data can either be uploaded by supplying a pointer and size so Cogl * can copy your data, or you can mmap() a CoglBuffer and then you can * copy data to the buffer directly. * * One of the most common uses for CoglBuffers is to upload texture * data asynchronously since the ability to mmap the buffers into * the CPU makes it possible for another thread to handle the IO * of loading an image file and unpacking it into the mapped buffer * without blocking other Cogl operations. */ #if defined(__COGL_H_INSIDE__) && !defined(COGL_ENABLE_MUTTER_API) && \ !defined(COGL_GIR_SCANNING) /* For the public C api we typedef interface types as void to avoid needing * lots of casting in code and instead we will rely on runtime type checking * for these objects. */ typedef void CoglBuffer; #else typedef struct _CoglBuffer CoglBuffer; #define COGL_BUFFER(buffer) ((CoglBuffer *)(buffer)) #endif #define COGL_BUFFER_ERROR (_cogl_buffer_error_domain ()) /** * CoglBufferError: * @COGL_BUFFER_ERROR_MAP: A buffer could not be mapped either * because the feature isn't supported or because a system * limitation was hit. * * Error enumeration for #CoglBuffer * * Stability: unstable */ typedef enum /*< prefix=COGL_BUFFER_ERROR >*/ { COGL_BUFFER_ERROR_MAP } CoglBufferError; uint32_t _cogl_buffer_error_domain (void); /** * cogl_is_buffer: * @object: a buffer object * * Checks whether @buffer is a buffer object. * * Return value: %TRUE if the handle is a CoglBuffer, and %FALSE otherwise * * Since: 1.2 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_buffer (void *object); /** * cogl_buffer_get_size: * @buffer: a buffer object * * Retrieves the size of buffer * * Return value: the size of the buffer in bytes * * Since: 1.2 * Stability: unstable */ COGL_EXPORT unsigned int cogl_buffer_get_size (CoglBuffer *buffer); /** * CoglBufferUpdateHint: * @COGL_BUFFER_UPDATE_HINT_STATIC: the buffer will not change over time * @COGL_BUFFER_UPDATE_HINT_DYNAMIC: the buffer will change from time to time * @COGL_BUFFER_UPDATE_HINT_STREAM: the buffer will be used once or a couple of * times * * The update hint on a buffer allows the user to give some detail on how often * the buffer data is going to be updated. * * Since: 1.2 * Stability: unstable */ typedef enum /*< prefix=COGL_BUFFER_UPDATE_HINT >*/ { COGL_BUFFER_UPDATE_HINT_STATIC, COGL_BUFFER_UPDATE_HINT_DYNAMIC, COGL_BUFFER_UPDATE_HINT_STREAM } CoglBufferUpdateHint; /** * cogl_buffer_set_update_hint: * @buffer: a buffer object * @hint: the new hint * * Sets the update hint on a buffer. See #CoglBufferUpdateHint for a description * of the available hints. * * Since: 1.2 * Stability: unstable */ COGL_EXPORT void cogl_buffer_set_update_hint (CoglBuffer *buffer, CoglBufferUpdateHint hint); /** * cogl_buffer_get_update_hint: * @buffer: a buffer object * * Retrieves the update hints set using cogl_buffer_set_update_hint() * * Return value: the #CoglBufferUpdateHint currently used by the buffer * * Since: 1.2 * Stability: unstable */ COGL_EXPORT CoglBufferUpdateHint cogl_buffer_get_update_hint (CoglBuffer *buffer); /** * CoglBufferAccess: * @COGL_BUFFER_ACCESS_READ: the buffer will be read * @COGL_BUFFER_ACCESS_WRITE: the buffer will written to * @COGL_BUFFER_ACCESS_READ_WRITE: the buffer will be used for both reading and * writing * * The access hints for cogl_buffer_set_update_hint() * * Since: 1.2 * Stability: unstable */ typedef enum /*< prefix=COGL_BUFFER_ACCESS >*/ { COGL_BUFFER_ACCESS_READ = 1 << 0, COGL_BUFFER_ACCESS_WRITE = 1 << 1, COGL_BUFFER_ACCESS_READ_WRITE = COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE } CoglBufferAccess; /** * CoglBufferMapHint: * @COGL_BUFFER_MAP_HINT_DISCARD: Tells Cogl that you plan to replace * all the buffer's contents. When this flag is used to map a * buffer, the entire contents of the buffer become undefined, even * if only a subregion of the buffer is mapped. * @COGL_BUFFER_MAP_HINT_DISCARD_RANGE: Tells Cogl that you plan to * replace all the contents of the mapped region. The contents of * the region specified are undefined after this flag is used to * map a buffer. * * Hints to Cogl about how you are planning to modify the data once it * is mapped. * * Since: 1.4 * Stability: unstable */ typedef enum /*< prefix=COGL_BUFFER_MAP_HINT >*/ { COGL_BUFFER_MAP_HINT_DISCARD = 1 << 0, COGL_BUFFER_MAP_HINT_DISCARD_RANGE = 1 << 1 } CoglBufferMapHint; /** * cogl_buffer_map: * @buffer: a buffer object * @access: how the mapped buffer will be used by the application * @hints: A mask of #CoglBufferMapHints that tell Cogl how * the data will be modified once mapped. * * Maps the buffer into the application address space for direct * access. This is equivalent to calling cogl_buffer_map_range() with * zero as the offset and the size of the entire buffer as the size. * * It is strongly recommended that you pass * %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace * all the buffer's data. This way if the buffer is currently being * used by the GPU then the driver won't have to stall the CPU and * wait for the hardware to finish because it can instead allocate a * new buffer to map. * * The behaviour is undefined if you access the buffer in a way * conflicting with the @access mask you pass. It is also an error to * release your last reference while the buffer is mapped. * * Return value: (transfer none): A pointer to the mapped memory or * %NULL is the call fails * * Since: 1.2 * Stability: unstable */ COGL_EXPORT void * cogl_buffer_map (CoglBuffer *buffer, CoglBufferAccess access, CoglBufferMapHint hints); /** * cogl_buffer_map_range: * @buffer: a buffer object * @offset: Offset within the buffer to start the mapping * @size: The size of data to map * @access: how the mapped buffer will be used by the application * @hints: A mask of #CoglBufferMapHints that tell Cogl how * the data will be modified once mapped. * @error: A #GError for catching exceptional errors * * Maps a sub-region of the buffer into the application's address space * for direct access. * * It is strongly recommended that you pass * %COGL_BUFFER_MAP_HINT_DISCARD as a hint if you are going to replace * all the buffer's data. This way if the buffer is currently being * used by the GPU then the driver won't have to stall the CPU and * wait for the hardware to finish because it can instead allocate a * new buffer to map. You can pass * %COGL_BUFFER_MAP_HINT_DISCARD_RANGE instead if you want the * regions outside of the mapping to be retained. * * The behaviour is undefined if you access the buffer in a way * conflicting with the @access mask you pass. It is also an error to * release your last reference while the buffer is mapped. * * Return value: (transfer none): A pointer to the mapped memory or * %NULL is the call fails * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void * cogl_buffer_map_range (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); /** * cogl_buffer_unmap: * @buffer: a buffer object * * Unmaps a buffer previously mapped by cogl_buffer_map(). * * Since: 1.2 * Stability: unstable */ COGL_EXPORT void cogl_buffer_unmap (CoglBuffer *buffer); /** * cogl_buffer_set_data: * @buffer: a buffer object * @offset: destination offset (in bytes) in the buffer * @data: a pointer to the data to be copied into the buffer * @size: number of bytes to copy * * Updates part of the buffer with new data from @data. Where to put this new * data is controlled by @offset and @offset + @data should be less than the * buffer size. * * Return value: %TRUE is the operation succeeded, %FALSE otherwise * * Since: 1.2 * Stability: unstable */ COGL_EXPORT gboolean cogl_buffer_set_data (CoglBuffer *buffer, size_t offset, const void *data, size_t size); G_END_DECLS #endif /* __COGL_BUFFER_H__ */ muffin-6.4.1/cogl/cogl/cogl-gl-header.h.in0000664000175000017500000000301614723361714017142 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(COGL_COMPILATION) && !defined(COGL_ENABLE_MUTTER_API) #error "cogl-gl-header.h should only be included when compiling Cogl" #endif #ifndef __COGL_GL_HEADER_H__ #define __COGL_GL_HEADER_H__ #include "cogl-defines.h" @COGL_GL_HEADER_INCLUDES@ #ifndef GL_OES_EGL_image #define GLeglImageOES void * #endif #endif /* __COGL_GL_HEADER_H__ */ muffin-6.4.1/cogl/cogl/cogl-texture.c0000664000175000017500000011715414723361714016411 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * Copyright (C) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Matthew Allum * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-bitmap.h" #include "cogl-bitmap-private.h" #include "cogl-buffer-private.h" #include "cogl-pixel-buffer-private.h" #include "cogl-private.h" #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d-sliced-private.h" #include "cogl-texture-2d-private.h" #include "cogl-sub-texture-private.h" #include "cogl-atlas-texture-private.h" #include "cogl-pipeline.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-object-private.h" #include "cogl-framebuffer-private.h" #include "cogl1-context.h" #include "cogl-sub-texture.h" #include "cogl-primitive-texture.h" #include "cogl-gtype-private.h" #include #include #include /* This isn't defined in the GLES headers */ #ifndef GL_RED #define GL_RED 0x1903 #endif COGL_GTYPE_DEFINE_INTERFACE (Texture, texture); uint32_t cogl_texture_error_quark (void) { return g_quark_from_static_string ("cogl-texture-error-quark"); } /* XXX: * The CoglObject macros don't support any form of inheritance, so for * now we implement the CoglObject support for the CoglTexture * abstract class manually. */ static GSList *_cogl_texture_types; void _cogl_texture_register_texture_type (const CoglObjectClass *klass) { _cogl_texture_types = g_slist_prepend (_cogl_texture_types, (void *) klass); } gboolean cogl_is_texture (void *object) { CoglObject *obj = (CoglObject *)object; GSList *l; if (object == NULL) return FALSE; for (l = _cogl_texture_types; l; l = l->next) if (l->data == obj->klass) return TRUE; return FALSE; } void _cogl_texture_init (CoglTexture *texture, CoglContext *context, int width, int height, CoglPixelFormat src_format, CoglTextureLoader *loader, const CoglTextureVtable *vtable) { texture->context = context; texture->max_level_set = 0; texture->max_level_requested = 1000; /* OpenGL default GL_TEXTURE_MAX_LEVEL */ texture->width = width; texture->height = height; texture->allocated = FALSE; texture->vtable = vtable; texture->framebuffers = NULL; texture->loader = loader; _cogl_texture_set_internal_format (texture, src_format); /* Although we want to initialize texture::components according * to the source format, we always want the internal layout to * be considered premultiplied by default. * * NB: this ->premultiplied state is user configurable so to avoid * awkward documentation, setting this to 'true' does not depend on * ->components having an alpha component (we will simply ignore the * premultiplied status later if there is no alpha component). * This way we don't have to worry about updating the * ->premultiplied state in _set_components(). Similarly we don't * have to worry about updating the ->components state in * _set_premultiplied(). */ texture->premultiplied = TRUE; } static void _cogl_texture_free_loader (CoglTexture *texture) { if (texture->loader) { CoglTextureLoader *loader = texture->loader; switch (loader->src_type) { case COGL_TEXTURE_SOURCE_TYPE_SIZED: case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE_EXTERNAL: break; case COGL_TEXTURE_SOURCE_TYPE_BITMAP: cogl_object_unref (loader->src.bitmap.bitmap); break; } g_slice_free (CoglTextureLoader, loader); texture->loader = NULL; } } CoglTextureLoader * _cogl_texture_create_loader (void) { return g_slice_new0 (CoglTextureLoader); } void _cogl_texture_free (CoglTexture *texture) { _cogl_texture_free_loader (texture); g_free (texture); } gboolean _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, CoglPixelFormat dst_format) { return ((src_format & dst_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8 && dst_format != COGL_PIXEL_FORMAT_A_8 && (src_format & COGL_PREMULT_BIT) != (dst_format & COGL_PREMULT_BIT)); } gboolean cogl_texture_is_get_data_supported (CoglTexture *texture) { if (texture->vtable->is_get_data_supported) return texture->vtable->is_get_data_supported (texture); else return TRUE; } unsigned int cogl_texture_get_width (CoglTexture *texture) { return texture->width; } unsigned int cogl_texture_get_height (CoglTexture *texture) { return texture->height; } CoglPixelFormat _cogl_texture_get_format (CoglTexture *texture) { if (!texture->allocated) cogl_texture_allocate (texture, NULL); return texture->vtable->get_format (texture); } int cogl_texture_get_max_waste (CoglTexture *texture) { return texture->vtable->get_max_waste (texture); } int _cogl_texture_get_n_levels (CoglTexture *texture) { int width = cogl_texture_get_width (texture); int height = cogl_texture_get_height (texture); int max_dimension = MAX (width, height); int n_levels = _cogl_util_fls (max_dimension); return MIN (n_levels, texture->max_level_requested + 1); } void cogl_texture_set_max_level (CoglTexture *texture, int max_level) { texture->max_level_requested = max_level; } void _cogl_texture_get_level_size (CoglTexture *texture, int level, int *width, int *height, int *depth) { int current_width = cogl_texture_get_width (texture); int current_height = cogl_texture_get_height (texture); int current_depth = 0; int i; /* NB: The OpenGL spec (like D3D) uses a floor() convention to * round down the size of a mipmap level when dividing the size * of the previous level results in a fraction... */ for (i = 0; i < level; i++) { current_width = MAX (1, current_width >> 1); current_height = MAX (1, current_height >> 1); current_depth = MAX (1, current_depth >> 1); } if (width) *width = current_width; if (height) *height = current_height; if (depth) *depth = current_depth; } gboolean cogl_texture_is_sliced (CoglTexture *texture) { if (!texture->allocated) cogl_texture_allocate (texture, NULL); return texture->vtable->is_sliced (texture); } /* If this returns FALSE, that implies _foreach_sub_texture_in_region * will be needed to iterate over multiple sub textures for regions whos * texture coordinates extend out of the range [0,1] */ gboolean _cogl_texture_can_hardware_repeat (CoglTexture *texture) { if (!texture->allocated) cogl_texture_allocate (texture, NULL); return texture->vtable->can_hardware_repeat (texture); } /* NB: You can't use this with textures comprised of multiple sub textures (use * cogl_texture_is_sliced() to check) since coordinate transformation for such * textures will be different for each slice. */ void _cogl_texture_transform_coords_to_gl (CoglTexture *texture, float *s, float *t) { texture->vtable->transform_coords_to_gl (texture, s, t); } CoglTransformResult _cogl_texture_transform_quad_coords_to_gl (CoglTexture *texture, float *coords) { return texture->vtable->transform_quad_coords_to_gl (texture, coords); } gboolean cogl_texture_get_gl_texture (CoglTexture *texture, GLuint *out_gl_handle, GLenum *out_gl_target) { if (!texture->allocated) cogl_texture_allocate (texture, NULL); return texture->vtable->get_gl_texture (texture, out_gl_handle, out_gl_target); } void _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags) { /* Assert that the storage for the texture exists already if we're * about to reference it for painting. * * Note: we abort on error here since it's a bit late to do anything * about it if we fail to allocate the texture and the app could * have explicitly allocated the texture earlier to handle problems * gracefully. * * XXX: Maybe it could even be considered a programmer error if the * texture hasn't been allocated by this point since it implies we * are abount to paint with undefined texture contents? */ cogl_texture_allocate (texture, NULL); texture->vtable->pre_paint (texture, flags); } void _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture) { texture->vtable->ensure_non_quad_rendering (texture); } gboolean _cogl_texture_set_region_from_bitmap (CoglTexture *texture, int src_x, int src_y, int width, int height, CoglBitmap *bmp, int dst_x, int dst_y, int level, GError **error) { g_return_val_if_fail (cogl_bitmap_get_width (bmp) - src_x >= width, FALSE); g_return_val_if_fail (cogl_bitmap_get_height (bmp) - src_y >= height, FALSE); g_return_val_if_fail (width > 0, FALSE); g_return_val_if_fail (height > 0, FALSE); /* Assert that the storage for this texture has been allocated */ if (!cogl_texture_allocate (texture, error)) return FALSE; /* Note that we don't prepare the bitmap for upload here because some backends may be internally using a different format for the actual GL texture than that reported by _cogl_texture_get_format. For example the atlas textures are always stored in an RGBA texture even if the texture format is advertised as RGB. */ return texture->vtable->set_region (texture, src_x, src_y, dst_x, dst_y, width, height, level, bmp, error); } gboolean cogl_texture_set_region_from_bitmap (CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, unsigned int dst_width, unsigned int dst_height, CoglBitmap *bitmap) { GError *ignore_error = NULL; gboolean status = _cogl_texture_set_region_from_bitmap (texture, src_x, src_y, dst_width, dst_height, bitmap, dst_x, dst_y, 0, /* level */ &ignore_error); g_clear_error (&ignore_error); return status; } gboolean _cogl_texture_set_region (CoglTexture *texture, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, int dst_x, int dst_y, int level, GError **error) { CoglContext *ctx = texture->context; CoglBitmap *source_bmp; gboolean ret; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); /* Rowstride from width if none specified */ if (rowstride == 0) rowstride = cogl_pixel_format_get_bytes_per_pixel (format, 0) * width; /* Init source bitmap */ source_bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); ret = _cogl_texture_set_region_from_bitmap (texture, 0, 0, width, height, source_bmp, dst_x, dst_y, level, error); cogl_object_unref (source_bmp); return ret; } gboolean cogl_texture_set_region (CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, unsigned int dst_width, unsigned int dst_height, int width, int height, CoglPixelFormat format, unsigned int rowstride, const uint8_t *data) { GError *ignore_error = NULL; const uint8_t *first_pixel; int bytes_per_pixel; gboolean status; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); /* Rowstride from width if none specified */ bytes_per_pixel = cogl_pixel_format_get_bytes_per_pixel (format, 0); if (rowstride == 0) rowstride = bytes_per_pixel * width; first_pixel = data + rowstride * src_y + bytes_per_pixel * src_x; status = _cogl_texture_set_region (texture, dst_width, dst_height, format, rowstride, first_pixel, dst_x, dst_y, 0, &ignore_error); g_clear_error (&ignore_error); return status; } gboolean cogl_texture_set_data (CoglTexture *texture, CoglPixelFormat format, int rowstride, const uint8_t *data, int level, GError **error) { int level_width; int level_height; _cogl_texture_get_level_size (texture, level, &level_width, &level_height, NULL); return _cogl_texture_set_region (texture, level_width, level_height, format, rowstride, data, 0, 0, /* dest x, y */ level, error); } static gboolean get_texture_bits_via_offscreen (CoglTexture *meta_texture, CoglTexture *sub_texture, int x, int y, int width, int height, uint8_t *dst_bits, unsigned int dst_rowstride, CoglPixelFormat closest_format) { CoglContext *ctx = sub_texture->context; CoglOffscreen *offscreen; CoglFramebuffer *framebuffer; CoglBitmap *bitmap; gboolean ret; GError *ignore_error = NULL; CoglPixelFormat real_format; offscreen = _cogl_offscreen_new_with_texture_full (sub_texture, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0); framebuffer = COGL_FRAMEBUFFER (offscreen); if (!cogl_framebuffer_allocate (framebuffer, &ignore_error)) { g_error_free (ignore_error); return FALSE; } /* Currently the framebuffer's internal format corresponds to the * internal format of @sub_texture but in the case of atlas textures * it's possible that this format doesn't reflect the correct * premultiplied alpha status or what components are valid since * atlas textures are always stored in a shared texture with a * format of _RGBA_8888. * * Here we override the internal format to make sure the * framebuffer's internal format matches the internal format of the * parent meta_texture instead. */ real_format = _cogl_texture_get_format (meta_texture); _cogl_framebuffer_set_internal_format (framebuffer, real_format); bitmap = cogl_bitmap_new_for_data (ctx, width, height, closest_format, dst_rowstride, dst_bits); ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, x, y, COGL_READ_PIXELS_COLOR_BUFFER, bitmap, &ignore_error); g_clear_error (&ignore_error); cogl_object_unref (bitmap); cogl_object_unref (framebuffer); return ret; } static gboolean get_texture_bits_via_copy (CoglTexture *texture, int x, int y, int width, int height, uint8_t *dst_bits, unsigned int dst_rowstride, CoglPixelFormat dst_format) { unsigned int full_rowstride; uint8_t *full_bits; gboolean ret = TRUE; int bpp; int full_tex_width, full_tex_height; g_return_val_if_fail (dst_format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (dst_format) == 1, FALSE); full_tex_width = cogl_texture_get_width (texture); full_tex_height = cogl_texture_get_height (texture); bpp = cogl_pixel_format_get_bytes_per_pixel (dst_format, 0); full_rowstride = bpp * full_tex_width; full_bits = g_malloc (full_rowstride * full_tex_height); if (texture->vtable->get_data (texture, dst_format, full_rowstride, full_bits)) { uint8_t *dst = dst_bits; uint8_t *src = full_bits + x * bpp + y * full_rowstride; int i; for (i = 0; i < height; i++) { memcpy (dst, src, bpp * width); dst += dst_rowstride; src += full_rowstride; } } else ret = FALSE; g_free (full_bits); return ret; } typedef struct { CoglTexture *meta_texture; int orig_width; int orig_height; CoglBitmap *target_bmp; uint8_t *target_bits; gboolean success; GError *error; } CoglTextureGetData; static void texture_get_cb (CoglTexture *subtexture, const float *subtexture_coords, const float *virtual_coords, void *user_data) { CoglTextureGetData *tg_data = user_data; CoglTexture *meta_texture = tg_data->meta_texture; CoglPixelFormat closest_format = cogl_bitmap_get_format (tg_data->target_bmp); /* We already asserted that we have a single plane format */ int bpp = cogl_pixel_format_get_bytes_per_pixel (closest_format, 0); unsigned int rowstride = cogl_bitmap_get_rowstride (tg_data->target_bmp); int subtexture_width = cogl_texture_get_width (subtexture); int subtexture_height = cogl_texture_get_height (subtexture); int x_in_subtexture = (int) (0.5 + subtexture_width * subtexture_coords[0]); int y_in_subtexture = (int) (0.5 + subtexture_height * subtexture_coords[1]); int width = ((int) (0.5 + subtexture_width * subtexture_coords[2]) - x_in_subtexture); int height = ((int) (0.5 + subtexture_height * subtexture_coords[3]) - y_in_subtexture); int x_in_bitmap = (int) (0.5 + tg_data->orig_width * virtual_coords[0]); int y_in_bitmap = (int) (0.5 + tg_data->orig_height * virtual_coords[1]); uint8_t *dst_bits; if (!tg_data->success) return; dst_bits = tg_data->target_bits + x_in_bitmap * bpp + y_in_bitmap * rowstride; /* If we can read everything as a single slice, then go ahead and do that * to avoid allocating an FBO. We'll leave it up to the GL implementation to * do glGetTexImage as efficiently as possible. (GLES doesn't have that, * so we'll fall through) */ if (x_in_subtexture == 0 && y_in_subtexture == 0 && width == subtexture_width && height == subtexture_height) { if (subtexture->vtable->get_data (subtexture, closest_format, rowstride, dst_bits)) return; } /* Next best option is a FBO and glReadPixels */ if (get_texture_bits_via_offscreen (meta_texture, subtexture, x_in_subtexture, y_in_subtexture, width, height, dst_bits, rowstride, closest_format)) return; /* Getting ugly: read the entire texture, copy out the part we want */ if (get_texture_bits_via_copy (subtexture, x_in_subtexture, y_in_subtexture, width, height, dst_bits, rowstride, closest_format)) return; /* No luck, the caller will fall back to the draw-to-backbuffer and * read implementation */ tg_data->success = FALSE; } int cogl_texture_get_data (CoglTexture *texture, CoglPixelFormat format, unsigned int rowstride, uint8_t *data) { CoglContext *ctx = texture->context; int bpp; int byte_size; CoglPixelFormat closest_format; GLenum closest_gl_format; GLenum closest_gl_type; CoglBitmap *target_bmp; int tex_width; int tex_height; CoglPixelFormat texture_format; GError *ignore_error = NULL; CoglTextureGetData tg_data; texture_format = _cogl_texture_get_format (texture); /* Default to internal format if none specified */ if (format == COGL_PIXEL_FORMAT_ANY) format = texture_format; /* We only support single plane formats */ g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, 0); tex_width = cogl_texture_get_width (texture); tex_height = cogl_texture_get_height (texture); /* Rowstride from texture width if none specified */ bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); if (rowstride == 0) rowstride = tex_width * bpp; /* Return byte size if only that requested */ byte_size = tex_height * rowstride; if (data == NULL) return byte_size; closest_format = ctx->texture_driver->find_best_gl_get_data_format (ctx, format, &closest_gl_format, &closest_gl_type); /* We can assume that whatever data GL gives us will have the premult status of the original texture */ if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (closest_format)) closest_format = ((closest_format & ~COGL_PREMULT_BIT) | (texture_format & COGL_PREMULT_BIT)); /* If the application is requesting a conversion from a * component-alpha texture and the driver doesn't support them * natively then we can only read into an alpha-format buffer. In * this case the driver will be faking the alpha textures with a * red-component texture and it won't swizzle to the correct format * while reading */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES)) { if (texture_format == COGL_PIXEL_FORMAT_A_8) { closest_format = COGL_PIXEL_FORMAT_A_8; closest_gl_format = GL_RED; closest_gl_type = GL_UNSIGNED_BYTE; } else if (format == COGL_PIXEL_FORMAT_A_8) { /* If we are converting to a component-alpha texture then we * need to read all of the components to a temporary buffer * because there is no way to get just the 4th component. * Note: it doesn't matter whether the texture is * pre-multiplied here because we're only going to look at * the alpha component */ closest_format = COGL_PIXEL_FORMAT_RGBA_8888; closest_gl_format = GL_RGBA; closest_gl_type = GL_UNSIGNED_BYTE; } } /* Is the requested format supported? */ if (closest_format == format) /* Target user data directly */ target_bmp = cogl_bitmap_new_for_data (ctx, tex_width, tex_height, format, rowstride, data); else { target_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, tex_width, tex_height, closest_format, &ignore_error); if (!target_bmp) { g_error_free (ignore_error); return 0; } } tg_data.target_bits = _cogl_bitmap_map (target_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, &ignore_error); if (tg_data.target_bits) { tg_data.meta_texture = texture; tg_data.orig_width = tex_width; tg_data.orig_height = tex_height; tg_data.target_bmp = target_bmp; tg_data.error = NULL; tg_data.success = TRUE; /* If there are any dependent framebuffers on the texture then we need to flush their journals so the texture contents will be up-to-date */ _cogl_texture_flush_journal_rendering (texture); /* Iterating through the subtextures allows piecing together * the data for a sliced texture, and allows us to do the * read-from-framebuffer logic here in a simple fashion rather than * passing offsets down through the code. */ cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, texture_get_cb, &tg_data); _cogl_bitmap_unmap (target_bmp); } else { g_error_free (ignore_error); tg_data.success = FALSE; } /* XXX: In some cases this api may fail to read back the texture * data; such as for GLES which doesn't support glGetTexImage */ if (!tg_data.success) { cogl_object_unref (target_bmp); return 0; } /* Was intermediate used? */ if (closest_format != format) { CoglBitmap *new_bmp; gboolean result; GError *error = NULL; /* Convert to requested format directly into the user's buffer */ new_bmp = cogl_bitmap_new_for_data (ctx, tex_width, tex_height, format, rowstride, data); result = _cogl_bitmap_convert_into_bitmap (target_bmp, new_bmp, &error); if (!result) { g_error_free (error); /* Return failure after cleaning up */ byte_size = 0; } cogl_object_unref (new_bmp); } cogl_object_unref (target_bmp); return byte_size; } static void _cogl_texture_framebuffer_destroy_cb (void *user_data, void *instance) { CoglTexture *tex = user_data; CoglFramebuffer *framebuffer = instance; tex->framebuffers = g_list_remove (tex->framebuffers, framebuffer); } void _cogl_texture_associate_framebuffer (CoglTexture *texture, CoglFramebuffer *framebuffer) { static CoglUserDataKey framebuffer_destroy_notify_key; /* Note: we don't take a reference on the framebuffer here because * that would introduce a circular reference. */ texture->framebuffers = g_list_prepend (texture->framebuffers, framebuffer); /* Since we haven't taken a reference on the framebuffer we setup * some private data so we will be notified if it is destroyed... */ _cogl_object_set_user_data (COGL_OBJECT (framebuffer), &framebuffer_destroy_notify_key, texture, _cogl_texture_framebuffer_destroy_cb); } const GList * _cogl_texture_get_associated_framebuffers (CoglTexture *texture) { return texture->framebuffers; } void _cogl_texture_flush_journal_rendering (CoglTexture *texture) { GList *l; /* It could be that a referenced texture is part of a framebuffer * which has an associated journal that must be flushed before it * can be sampled from by the current primitive... */ for (l = texture->framebuffers; l; l = l->next) _cogl_framebuffer_flush_journal (l->data); } /* This function lets you define a meta texture as a grid of textures * whereby the x and y grid-lines are defined by an array of * CoglSpans. With that grid based description this function can then * iterate all the cells of the grid that lye within a region * specified as virtual, meta-texture, coordinates. This function can * also cope with regions that extend beyond the original meta-texture * grid by iterating cells repeatedly according to the wrap_x/y * arguments. * * To differentiate between texture coordinates of a specific, real, * slice texture and the texture coordinates of a composite, meta * texture, the coordinates of the meta texture are called "virtual" * coordinates and the coordinates of spans are called "slice" * coordinates. * * Note: no guarantee is given about the order in which the slices * will be visited. * * Note: The slice coordinates passed to @callback are always * normalized coordinates even if the span coordinates aren't * normalized. */ void _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans, int n_x_spans, CoglSpan *y_spans, int n_y_spans, CoglTexture **textures, float *virtual_coords, float x_normalize_factor, float y_normalize_factor, CoglPipelineWrapMode wrap_x, CoglPipelineWrapMode wrap_y, CoglMetaTextureCallback callback, void *user_data) { CoglSpanIter iter_x; CoglSpanIter iter_y; float slice_coords[4]; float span_virtual_coords[4]; /* Iterate the y axis of the virtual rectangle */ for (_cogl_span_iter_begin (&iter_y, y_spans, n_y_spans, y_normalize_factor, virtual_coords[1], virtual_coords[3], wrap_y); !_cogl_span_iter_end (&iter_y); _cogl_span_iter_next (&iter_y)) { if (iter_y.flipped) { slice_coords[1] = iter_y.intersect_end; slice_coords[3] = iter_y.intersect_start; span_virtual_coords[1] = iter_y.intersect_end; span_virtual_coords[3] = iter_y.intersect_start; } else { slice_coords[1] = iter_y.intersect_start; slice_coords[3] = iter_y.intersect_end; span_virtual_coords[1] = iter_y.intersect_start; span_virtual_coords[3] = iter_y.intersect_end; } /* Map the current intersection to normalized slice coordinates */ slice_coords[1] = (slice_coords[1] - iter_y.pos) / iter_y.span->size; slice_coords[3] = (slice_coords[3] - iter_y.pos) / iter_y.span->size; /* Iterate the x axis of the virtual rectangle */ for (_cogl_span_iter_begin (&iter_x, x_spans, n_x_spans, x_normalize_factor, virtual_coords[0], virtual_coords[2], wrap_x); !_cogl_span_iter_end (&iter_x); _cogl_span_iter_next (&iter_x)) { CoglTexture *span_tex; if (iter_x.flipped) { slice_coords[0] = iter_x.intersect_end; slice_coords[2] = iter_x.intersect_start; span_virtual_coords[0] = iter_x.intersect_end; span_virtual_coords[2] = iter_x.intersect_start; } else { slice_coords[0] = iter_x.intersect_start; slice_coords[2] = iter_x.intersect_end; span_virtual_coords[0] = iter_x.intersect_start; span_virtual_coords[2] = iter_x.intersect_end; } /* Map the current intersection to normalized slice coordinates */ slice_coords[0] = (slice_coords[0] - iter_x.pos) / iter_x.span->size; slice_coords[2] = (slice_coords[2] - iter_x.pos) / iter_x.span->size; /* Pluck out the cogl texture for this span */ span_tex = textures[iter_y.index * n_x_spans + iter_x.index]; callback (COGL_TEXTURE (span_tex), slice_coords, span_virtual_coords, user_data); } } } void _cogl_texture_set_allocated (CoglTexture *texture, CoglPixelFormat internal_format, int width, int height) { _cogl_texture_set_internal_format (texture, internal_format); texture->width = width; texture->height = height; texture->allocated = TRUE; _cogl_texture_free_loader (texture); } gboolean cogl_texture_allocate (CoglTexture *texture, GError **error) { if (texture->allocated) return TRUE; if (texture->components == COGL_TEXTURE_COMPONENTS_RG && !cogl_has_feature (texture->context, COGL_FEATURE_ID_TEXTURE_RG)) g_set_error (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_FORMAT, "A red-green texture was requested but the driver " "does not support them"); texture->allocated = texture->vtable->allocate (texture, error); return texture->allocated; } void _cogl_texture_set_internal_format (CoglTexture *texture, CoglPixelFormat internal_format) { texture->premultiplied = FALSE; if (internal_format == COGL_PIXEL_FORMAT_ANY) internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; if (internal_format == COGL_PIXEL_FORMAT_A_8) { texture->components = COGL_TEXTURE_COMPONENTS_A; return; } else if (internal_format == COGL_PIXEL_FORMAT_RG_88) { texture->components = COGL_TEXTURE_COMPONENTS_RG; return; } else if (internal_format & COGL_DEPTH_BIT) { texture->components = COGL_TEXTURE_COMPONENTS_DEPTH; return; } else if (internal_format & COGL_A_BIT) { texture->components = COGL_TEXTURE_COMPONENTS_RGBA; if (internal_format & COGL_PREMULT_BIT) texture->premultiplied = TRUE; return; } else texture->components = COGL_TEXTURE_COMPONENTS_RGB; } CoglPixelFormat _cogl_texture_determine_internal_format (CoglTexture *texture, CoglPixelFormat src_format) { switch (texture->components) { case COGL_TEXTURE_COMPONENTS_DEPTH: if (src_format & COGL_DEPTH_BIT) return src_format; else { CoglContext *ctx = texture->context; if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) { return COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8; } else return COGL_PIXEL_FORMAT_DEPTH_16; } case COGL_TEXTURE_COMPONENTS_A: return COGL_PIXEL_FORMAT_A_8; case COGL_TEXTURE_COMPONENTS_RG: return COGL_PIXEL_FORMAT_RG_88; case COGL_TEXTURE_COMPONENTS_RGB: if (src_format != COGL_PIXEL_FORMAT_ANY && !(src_format & COGL_A_BIT) && !(src_format & COGL_DEPTH_BIT)) return src_format; else return COGL_PIXEL_FORMAT_RGB_888; case COGL_TEXTURE_COMPONENTS_RGBA: { CoglPixelFormat format; if (src_format != COGL_PIXEL_FORMAT_ANY && (src_format & COGL_A_BIT) && src_format != COGL_PIXEL_FORMAT_A_8) format = src_format; else format = COGL_PIXEL_FORMAT_RGBA_8888; if (texture->premultiplied) { if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) return format |= COGL_PREMULT_BIT; else return COGL_PIXEL_FORMAT_RGBA_8888_PRE; } else return format & ~COGL_PREMULT_BIT; } } g_return_val_if_reached (COGL_PIXEL_FORMAT_RGBA_8888_PRE); } void cogl_texture_set_components (CoglTexture *texture, CoglTextureComponents components) { g_return_if_fail (!texture->allocated); if (texture->components == components) return; texture->components = components; } CoglTextureComponents cogl_texture_get_components (CoglTexture *texture) { return texture->components; } void cogl_texture_set_premultiplied (CoglTexture *texture, gboolean premultiplied) { g_return_if_fail (!texture->allocated); premultiplied = !!premultiplied; if (texture->premultiplied == premultiplied) return; texture->premultiplied = premultiplied; } gboolean cogl_texture_get_premultiplied (CoglTexture *texture) { return texture->premultiplied; } void _cogl_texture_copy_internal_format (CoglTexture *src, CoglTexture *dest) { cogl_texture_set_components (dest, src->components); cogl_texture_set_premultiplied (dest, src->premultiplied); } muffin-6.4.1/cogl/cogl/cogl-output-private.h0000664000175000017500000000335314723361714017721 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_OUTPUT_PRIVATE_H #define __COGL_OUTPUT_PRIVATE_H #include "cogl-output.h" #include "cogl-object-private.h" struct _CoglOutput { CoglObject _parent; char *name; int x; /* Must be first field for _cogl_output_values_equal() */ int y; int width; int height; int mm_width; int mm_height; float refresh_rate; CoglSubpixelOrder subpixel_order; }; CoglOutput *_cogl_output_new (const char *name); gboolean _cogl_output_values_equal (CoglOutput *output, CoglOutput *other); #endif /* __COGL_OUTPUT_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-pixel-format.h0000664000175000017500000003152714723361714017324 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PIXEL_FORMAT_H__ #define __COGL_PIXEL_FORMAT_H__ #include #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-pixel-format * @short_description: Pixel formats supported by Cogl * * The pixel format of an image descrbes how the bits of each pixel are * represented in memory. For example: an image can be laid out as one long * sequence of pixels, where each pixel is a sequence of 8 bits of Red, Green * and Blue. The amount of bits that are used can be different for each pixel * format, as well as the components (for example an Alpha layer to include * transparency, or non_RGBA). * * Other examples of factors that can influence the layout in memory are the * system's endianness. */ #define COGL_A_BIT (1 << 4) #define COGL_BGR_BIT (1 << 5) #define COGL_AFIRST_BIT (1 << 6) #define COGL_PREMULT_BIT (1 << 7) #define COGL_DEPTH_BIT (1 << 8) #define COGL_STENCIL_BIT (1 << 9) /* XXX: Notes to those adding new formats here... * * First this diagram outlines how we allocate the 32bits of a * CoglPixelFormat currently... * * 6 bits for flags * |-----| * enum unused 4 bits for the bytes-per-pixel * and component alignment info * |------| |-------------| |--| * 00000000 xxxxxxxx xxxxxxSD PFBA0000 * ^ stencil * ^ depth * ^ premult * ^ alpha first * ^ bgr order * ^ has alpha * * The most awkward part about the formats is how we use the last 4 * bits to encode the bytes per pixel and component alignment * information. Ideally we should have had 3 bits for the bpp and a * flag for alignment but we didn't plan for that in advance so we * instead use a small lookup table to query the bpp and whether the * components are byte aligned or not. * * The mapping is the following (see discussion on bug #660188): * * 0 = undefined * 1, 8 = 1 bpp (e.g. A_8, G_8) * 2 = 3 bpp, aligned (e.g. 888) * 3 = 4 bpp, aligned (e.g. 8888) * 4-6 = 2 bpp, not aligned (e.g. 565, 4444, 5551) * 7 = YUV: undefined bpp, undefined alignment * 9 = 2 bpp, aligned * 10 = depth, aligned (8, 16, 24, 32, 32f) * 11 = undefined * 12 = 3 bpp, not aligned * 13 = 4 bpp, not aligned (e.g. 2101010) * 14-15 = undefined * * Note: the gap at 10-11 is just because we wanted to maintain that * all non-aligned formats have the third bit set in case that's * useful later. * * Since we don't want to waste bits adding more and more flags, we'd * like to see most new pixel formats that can't be represented * uniquely with the existing flags in the least significant byte * simply be enumerated with sequential values in the most significant * enum byte. * * Note: Cogl avoids exposing any padded XRGB or RGBX formats and * instead we leave it up to applications to decided whether they * consider the A component as padding or valid data. We shouldn't * change this policy without good reasoning. * * So to add a new format: * 1) Use the mapping table above to figure out what to but in * the lowest nibble. * 2) OR in the COGL_PREMULT_BIT, COGL_AFIRST_BIT, COGL_A_BIT and * COGL_BGR_BIT flags as appropriate. * 3) If the result is not yet unique then also combine with an * increment of the last sequence number in the most significant * byte. * * The last sequence number used was 0 (i.e. no formats currently need * a sequence number) * Update this note whenever a new sequence number is used. */ /** * CoglPixelFormat: * @COGL_PIXEL_FORMAT_ANY: Any format * @COGL_PIXEL_FORMAT_A_8: 8 bits alpha mask * @COGL_PIXEL_FORMAT_RG_88: RG, 16 bits. Note that red-green textures * are only available if %COGL_FEATURE_ID_TEXTURE_RG is advertised. * See cogl_texture_set_components() for details. * @COGL_PIXEL_FORMAT_RGB_565: RGB, 16 bits * @COGL_PIXEL_FORMAT_RGBA_4444: RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_5551: RGBA, 16 bits * @COGL_PIXEL_FORMAT_YUV: Not currently supported * @COGL_PIXEL_FORMAT_G_8: Single luminance component * @COGL_PIXEL_FORMAT_RGB_888: RGB, 24 bits * @COGL_PIXEL_FORMAT_BGR_888: BGR, 24 bits * @COGL_PIXEL_FORMAT_RGBA_8888: RGBA, 32 bits * @COGL_PIXEL_FORMAT_BGRA_8888: BGRA, 32 bits * @COGL_PIXEL_FORMAT_ARGB_8888: ARGB, 32 bits * @COGL_PIXEL_FORMAT_ABGR_8888: ABGR, 32 bits * @COGL_PIXEL_FORMAT_RGBA_1010102 : RGBA, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_BGRA_1010102 : BGRA, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_ARGB_2101010 : ARGB, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_ABGR_2101010 : ABGR, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_RGBA_8888_PRE: Premultiplied RGBA, 32 bits * @COGL_PIXEL_FORMAT_BGRA_8888_PRE: Premultiplied BGRA, 32 bits * @COGL_PIXEL_FORMAT_ARGB_8888_PRE: Premultiplied ARGB, 32 bits * @COGL_PIXEL_FORMAT_ABGR_8888_PRE: Premultiplied ABGR, 32 bits * @COGL_PIXEL_FORMAT_RGBA_4444_PRE: Premultiplied RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_5551_PRE: Premultiplied RGBA, 16 bits * @COGL_PIXEL_FORMAT_RGBA_1010102_PRE: Premultiplied RGBA, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_BGRA_1010102_PRE: Premultiplied BGRA, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_ARGB_2101010_PRE: Premultiplied ARGB, 32 bits, 10 bpc * @COGL_PIXEL_FORMAT_ABGR_2101010_PRE: Premultiplied ABGR, 32 bits, 10 bpc * * Pixel formats used by Cogl. For the formats with a byte per * component, the order of the components specify the order in * increasing memory addresses. So for example * %COGL_PIXEL_FORMAT_RGB_888 would have the red component in the * lowest address, green in the next address and blue after that * regardless of the endianness of the system. * * For the formats with non byte aligned components the component * order specifies the order within a 16-bit or 32-bit number from * most significant bit to least significant. So for * %COGL_PIXEL_FORMAT_RGB_565, the red component would be in bits * 11-15, the green component would be in 6-11 and the blue component * would be in 1-5. Therefore the order in memory depends on the * endianness of the system. * * When uploading a texture %COGL_PIXEL_FORMAT_ANY can be used as the * internal format. Cogl will try to pick the best format to use * internally and convert the texture data if necessary. * * Since: 0.8 */ typedef enum /*< prefix=COGL_PIXEL_FORMAT >*/ { COGL_PIXEL_FORMAT_ANY = 0, COGL_PIXEL_FORMAT_A_8 = 1 | COGL_A_BIT, COGL_PIXEL_FORMAT_RGB_565 = 4, COGL_PIXEL_FORMAT_RGBA_4444 = 5 | COGL_A_BIT, COGL_PIXEL_FORMAT_RGBA_5551 = 6 | COGL_A_BIT, COGL_PIXEL_FORMAT_YUV = 7, COGL_PIXEL_FORMAT_G_8 = 8, COGL_PIXEL_FORMAT_RG_88 = 9, COGL_PIXEL_FORMAT_RGB_888 = 2, COGL_PIXEL_FORMAT_BGR_888 = (2 | COGL_BGR_BIT), COGL_PIXEL_FORMAT_RGBA_8888 = (3 | COGL_A_BIT), COGL_PIXEL_FORMAT_BGRA_8888 = (3 | COGL_A_BIT | COGL_BGR_BIT), COGL_PIXEL_FORMAT_ARGB_8888 = (3 | COGL_A_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_ABGR_8888 = (3 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_RGBA_1010102 = (13 | COGL_A_BIT), COGL_PIXEL_FORMAT_BGRA_1010102 = (13 | COGL_A_BIT | COGL_BGR_BIT), COGL_PIXEL_FORMAT_ARGB_2101010 = (13 | COGL_A_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_ABGR_2101010 = (13 | COGL_A_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_RGBA_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_BGRA_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT), COGL_PIXEL_FORMAT_ARGB_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_ABGR_8888_PRE = (3 | COGL_A_BIT | COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT), COGL_PIXEL_FORMAT_RGBA_4444_PRE = (COGL_PIXEL_FORMAT_RGBA_4444 | COGL_A_BIT | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_RGBA_5551_PRE = (COGL_PIXEL_FORMAT_RGBA_5551 | COGL_A_BIT | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_RGBA_1010102_PRE = (COGL_PIXEL_FORMAT_RGBA_1010102 | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_BGRA_1010102_PRE = (COGL_PIXEL_FORMAT_BGRA_1010102 | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_ARGB_2101010_PRE = (COGL_PIXEL_FORMAT_ARGB_2101010 | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_ABGR_2101010_PRE = (COGL_PIXEL_FORMAT_ABGR_2101010 | COGL_PREMULT_BIT), COGL_PIXEL_FORMAT_DEPTH_16 = (9 | COGL_DEPTH_BIT), COGL_PIXEL_FORMAT_DEPTH_32 = (3 | COGL_DEPTH_BIT), COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8 = (3 | COGL_DEPTH_BIT | COGL_STENCIL_BIT) } CoglPixelFormat; /** * COGL_PIXEL_FORMAT_MAX_PLANES: * * The maximum number of planes of a pixel format (see also * cogl_pixel_format_get_planes()). */ #define COGL_PIXEL_FORMAT_MAX_PLANES (4) /** * cogl_pixel_format_get_bytes_per_pixel: * @format: The pixel format * @plane: The index of the plane (should not be more than the number of planes * in the given format). * * Queries the number of bytes per pixel for a given format in the given plane. * * Returns: The number of bytes per pixel in the given format's given plane. */ COGL_EXPORT int cogl_pixel_format_get_bytes_per_pixel (CoglPixelFormat format, int plane); /* * _cogl_pixel_format_has_aligned_components: * @format: a #CoglPixelFormat * * Queries whether the ordering of the components for the given * @format depend on the endianness of the host CPU or if the * components can be accessed using bit shifting and bitmasking by * loading a whole pixel into a word. * * XXX: If we ever consider making something like this public we * should really try to think of a better name and come up with * much clearer documentation since it really depends on what * point of view you consider this from whether a format like * COGL_PIXEL_FORMAT_RGBA_8888 is endian dependent. E.g. If you * read an RGBA_8888 pixel into a uint32 * it's endian dependent how you mask out the different channels. * But If you already have separate color components and you want * to write them to an RGBA_8888 pixel then the bytes can be * written sequentially regardless of the endianness. * * Return value: %TRUE if you need to consider the host CPU * endianness when dealing with the given @format * else %FALSE. */ gboolean _cogl_pixel_format_is_endian_dependant (CoglPixelFormat format); /* * COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT(format): * @format: a #CoglPixelFormat * * Returns TRUE if the pixel format can take a premult bit. This is * currently true for all formats that have an alpha channel except * COGL_PIXEL_FORMAT_A_8 (because that doesn't have any other * components to multiply by the alpha). */ #define COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT(format) \ (((format) & COGL_A_BIT) && (format) != COGL_PIXEL_FORMAT_A_8) /** * cogl_pixel_format_get_n_planes: * @format: The format for which to get the number of planes * * Returns the number of planes the given CoglPixelFormat specifies. * * Returns: The no. of planes of @format (at most %COGL_PIXEL_FORMAT_MAX_PLANES) */ COGL_EXPORT int cogl_pixel_format_get_n_planes (CoglPixelFormat format); /** * cogl_pixel_format_to_string: * @format: a #CoglPixelFormat * * Returns a string representation of @format, useful for debugging purposes. * * Returns: (transfer none): A string representation of @format. */ COGL_EXPORT const char * cogl_pixel_format_to_string (CoglPixelFormat format); G_END_DECLS #endif /* __COGL_PIXEL_FORMAT_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-layer-state.h0000664000175000017500000005154714723361714020756 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PIPELINE_LAYER_STATE_H__ #define __COGL_PIPELINE_LAYER_STATE_H__ #include #include #include #include G_BEGIN_DECLS /** * CoglPipelineFilter: * @COGL_PIPELINE_FILTER_NEAREST: Measuring in manhatten distance from the, * current pixel center, use the nearest texture texel * @COGL_PIPELINE_FILTER_LINEAR: Use the weighted average of the 4 texels * nearest the current pixel center * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose * texel size most closely matches the current pixel, and use the * %COGL_PIPELINE_FILTER_NEAREST criterion * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose * texel size most closely matches the current pixel, and use the * %COGL_PIPELINE_FILTER_LINEAR criterion * @COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels * whose texel size most closely matches the current pixel, use * the %COGL_PIPELINE_FILTER_NEAREST criterion on each one and take * their weighted average * @COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels * whose texel size most closely matches the current pixel, use * the %COGL_PIPELINE_FILTER_LINEAR criterion on each one and take * their weighted average * * Texture filtering is used whenever the current pixel maps either to more * than one texture element (texel) or less than one. These filter enums * correspond to different strategies used to come up with a pixel color, by * possibly referring to multiple neighbouring texels and taking a weighted * average or simply using the nearest texel. */ typedef enum { COGL_PIPELINE_FILTER_NEAREST = 0x2600, COGL_PIPELINE_FILTER_LINEAR = 0x2601, COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700, COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701, COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702, COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703 } CoglPipelineFilter; /* NB: these values come from the equivalents in gl.h */ /** * CoglPipelineWrapMode: * @COGL_PIPELINE_WRAP_MODE_REPEAT: The texture will be repeated. This * is useful for example to draw a tiled background. * @COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the * range 0→1 will sample copies of the edge pixels of the * texture. This is useful to avoid artifacts if only one copy of * the texture is being rendered. * @COGL_PIPELINE_WRAP_MODE_AUTOMATIC: Cogl will try to automatically * decide which of the above two to use. For cogl_rectangle(), it * will use repeat mode if any of the texture coordinates are * outside the range 0→1, otherwise it will use clamp to edge. For * cogl_polygon() it will always use repeat mode. For * cogl_vertex_buffer_draw() it will use repeat mode except for * layers that have point sprite coordinate generation enabled. This * is the default value. * * The wrap mode specifies what happens when texture coordinates * outside the range 0→1 are used. Note that if the filter mode is * anything but %COGL_PIPELINE_FILTER_NEAREST then texels outside the * range 0→1 might be used even when the coordinate is exactly 0 or 1 * because OpenGL will try to sample neighbouring pixels. For example * if you are trying to render the full texture then you may get * artifacts around the edges when the pixels from the other side are * merged in if the wrap mode is set to repeat. * * Since: 2.0 */ /* GL_ALWAYS is just used here as a value that is known not to clash * with any valid GL wrap modes * * XXX: keep the values in sync with the CoglPipelineWrapModeInternal * enum so no conversion is actually needed. */ typedef enum { COGL_PIPELINE_WRAP_MODE_REPEAT = 0x2901, COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT = 0x8370, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE = 0x812F, COGL_PIPELINE_WRAP_MODE_AUTOMATIC = 0x0207 /* GL_ALWAYS */ } CoglPipelineWrapMode; /* NB: these values come from the equivalents in gl.h */ /** * cogl_pipeline_set_layer: * @pipeline: A #CoglPipeline object * @layer_index: the index of the layer * @texture: a #CoglTexture for the layer object * * In addition to the standard OpenGL lighting model a Cogl pipeline may have * one or more layers comprised of textures that can be blended together in * order, with a number of different texture combine modes. This function * defines a new texture layer. * * The index values of multiple layers do not have to be consecutive; it is * only their relative order that is important. * * The @texture parameter can also be %NULL in which case the pipeline * will use a default white texture. The type of the default texture * will be the same as whatever texture was last used for the pipeline * or %COGL_TEXTURE_TYPE_2D if none has been specified yet. To * explicitly specify the type of default texture required, use * cogl_pipeline_set_layer_null_texture() instead. * * In the future, we may define other types of pipeline layers, such * as purely GLSL based layers. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_texture (CoglPipeline *pipeline, int layer_index, CoglTexture *texture); /** * cogl_pipeline_set_layer_null_texture: * @pipeline: A #CoglPipeline * @layer_index: The layer number to modify * * Sets the texture for this layer to be the default texture for the * given type. The default texture is a 1x1 pixel white texture. * * This function is mostly useful if you want to create a base * pipeline that you want to create multiple copies from using * cogl_pipeline_copy(). In that case this function can be used to * specify the texture type so that any pipeline copies can share the * internal texture type state for efficiency. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_null_texture (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_get_layer_texture: * @pipeline: A #CoglPipeline object * @layer_index: the index of the layer * * Return value: (transfer none): the texture that was set for the * given layer of the pipeline or %NULL if no texture was set. * Stability: unstable * Since: 1.10 */ COGL_EXPORT CoglTexture * cogl_pipeline_get_layer_texture (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_remove_layer: * @pipeline: A #CoglPipeline object * @layer_index: Specifies the layer you want to remove * * This function removes a layer from your pipeline * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_remove_layer (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_set_layer_combine: * @pipeline: A #CoglPipeline object * @layer_index: Specifies the layer you want define a combine function for * @blend_string: A Cogl blend string * describing the desired texture combine function. * @error: A #GError that may report parse errors or lack of GPU/driver * support. May be %NULL, in which case a warning will be printed out if an * error is encountered. * * If not already familiar; you can refer * here for an overview of what blend * strings are and there syntax. * * These are all the functions available for texture combining: * * REPLACE(arg0) = arg0 * MODULATE(arg0, arg1) = arg0 x arg1 * ADD(arg0, arg1) = arg0 + arg1 * ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5 * INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2) * SUBTRACT(arg0, arg1) = arg0 - arg1 * * * DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) * * * * * DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) * * * * * Refer to the * color-source syntax for * describing the arguments. The valid source names for texture combining * are: * * * TEXTURE * Use the color from the current texture layer * * * TEXTURE_0, TEXTURE_1, etc * Use the color from the specified texture layer * * * CONSTANT * Use the color from the constant given with * cogl_pipeline_set_layer_combine_constant() * * * PRIMARY * Use the color of the pipeline as set with * cogl_pipeline_set_color() * * * PREVIOUS * Either use the texture color from the previous layer, or * if this is layer 0, use the color of the pipeline as set with * cogl_pipeline_set_color() * * * * * Layer Combine Examples * This is effectively what the default blending is: * * RGBA = MODULATE (PREVIOUS, TEXTURE) * * This could be used to cross-fade between two images, using * the alpha component of a constant as the interpolator. The constant * color is given by calling * cogl_pipeline_set_layer_combine_constant(). * * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A]) * * * * You can't give a multiplication factor for arguments as you can * with blending. * * Return value: %TRUE if the blend string was successfully parsed, and the * described texture combining is supported by the underlying driver and * or hardware. On failure, %FALSE is returned and @error is set * * Since: 2.0 * Stability: unstable */ COGL_EXPORT gboolean cogl_pipeline_set_layer_combine (CoglPipeline *pipeline, int layer_index, const char *blend_string, GError **error); /** * cogl_pipeline_set_layer_combine_constant: * @pipeline: A #CoglPipeline object * @layer_index: Specifies the layer you want to specify a constant used * for texture combining * @constant: The constant color you want * * When you are using the 'CONSTANT' color source in a layer combine * description then you can use this function to define its value. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_combine_constant (CoglPipeline *pipeline, int layer_index, const CoglColor *constant); /** * cogl_pipeline_set_layer_matrix: * @pipeline: A #CoglPipeline object * @layer_index: the index for the layer inside @pipeline * @matrix: the transformation matrix for the layer * * This function lets you set a matrix that can be used to e.g. translate * and rotate a single layer of a pipeline used to fill your geometry. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_matrix (CoglPipeline *pipeline, int layer_index, const CoglMatrix *matrix); /** * cogl_pipeline_get_n_layers: * @pipeline: A #CoglPipeline object * * Retrieves the number of layers defined for the given @pipeline * * Return value: the number of layers * * Since: 2.0 * Stability: unstable */ COGL_EXPORT int cogl_pipeline_get_n_layers (CoglPipeline *pipeline); /** * cogl_pipeline_set_layer_filters: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * @min_filter: the filter used when scaling a texture down. * @mag_filter: the filter used when magnifying a texture. * * Changes the decimation and interpolation filters used when a texture is * drawn at other scales than 100%. * * It is an error to pass anything other than * %COGL_PIPELINE_FILTER_NEAREST or %COGL_PIPELINE_FILTER_LINEAR as * magnification filters since magnification doesn't ever need to * reference values stored in the mipmap chain. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_filters (CoglPipeline *pipeline, int layer_index, CoglPipelineFilter min_filter, CoglPipelineFilter mag_filter); /** * cogl_pipeline_get_layer_min_filter: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * * Retrieves the currently set minification #CoglPipelineFilter set on * the specified layer. The miniifcation filter determines how the * layer should be sampled when down-scaled. * * The default filter is %COGL_PIPELINE_FILTER_LINEAR but this can be * changed using cogl_pipeline_set_layer_filters(). * * Return value: The minification #CoglPipelineFilter for the * specified layer. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglPipelineFilter cogl_pipeline_get_layer_min_filter (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_get_layer_mag_filter: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * * Retrieves the currently set magnification #CoglPipelineFilter set on * the specified layer. The magnification filter determines how the * layer should be sampled when up-scaled. * * The default filter is %COGL_PIPELINE_FILTER_LINEAR but this can be * changed using cogl_pipeline_set_layer_filters(). * * Return value: The magnification #CoglPipelineFilter for the * specified layer. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglPipelineFilter cogl_pipeline_get_layer_mag_filter (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_set_layer_point_sprite_coords_enabled: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * @enable: whether to enable point sprite coord generation. * @error: A return location for a #GError, or NULL to ignore errors. * * When rendering points, if @enable is %TRUE then the texture * coordinates for this layer will be replaced with coordinates that * vary from 0.0 to 1.0 across the primitive. The top left of the * point will have the coordinates 0.0,0.0 and the bottom right will * have 1.0,1.0. If @enable is %FALSE then the coordinates will be * fixed for the entire point. * * Return value: %TRUE if the function succeeds, %FALSE otherwise. * Since: 2.0 * Stability: unstable */ COGL_EXPORT gboolean cogl_pipeline_set_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, int layer_index, gboolean enable, GError **error); /** * cogl_pipeline_get_layer_point_sprite_coords_enabled: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to check. * * Gets whether point sprite coordinate generation is enabled for this * texture layer. * * Return value: whether the texture coordinates will be replaced with * point sprite coordinates. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT gboolean cogl_pipeline_get_layer_point_sprite_coords_enabled (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_get_layer_wrap_mode_s: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * * Returns the wrap mode for the 's' coordinate of texture lookups on this * layer. * * Return value: the wrap mode for the 's' coordinate of texture lookups on * this layer. * * Since: 1.6 * Stability: unstable */ COGL_EXPORT CoglPipelineWrapMode cogl_pipeline_get_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_set_layer_wrap_mode_s: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * @mode: the new wrap mode * * Sets the wrap mode for the 's' coordinate of texture lookups on this layer. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_wrap_mode_s (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode); /** * cogl_pipeline_get_layer_wrap_mode_t: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * * Returns the wrap mode for the 't' coordinate of texture lookups on this * layer. * * Return value: the wrap mode for the 't' coordinate of texture lookups on * this layer. * * Since: 1.6 * Stability: unstable */ COGL_EXPORT CoglPipelineWrapMode cogl_pipeline_get_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index); /** * cogl_pipeline_set_layer_wrap_mode_t: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * @mode: the new wrap mode * * Sets the wrap mode for the 't' coordinate of texture lookups on this layer. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_wrap_mode_t (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode); /** * cogl_pipeline_set_layer_wrap_mode: * @pipeline: A #CoglPipeline object * @layer_index: the layer number to change. * @mode: the new wrap mode * * Sets the wrap mode for all three coordinates of texture lookups on * this layer. This is equivalent to calling * cogl_pipeline_set_layer_wrap_mode_s() and * cogl_pipeline_set_layer_wrap_mode_t() separately. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_pipeline_set_layer_wrap_mode (CoglPipeline *pipeline, int layer_index, CoglPipelineWrapMode mode); /** * cogl_pipeline_add_layer_snippet: (skip) * @pipeline: A #CoglPipeline * @layer: The layer to hook the snippet to * @snippet: A #CoglSnippet * * Adds a shader snippet that will hook on to the given layer of the * pipeline. The exact part of the pipeline that the snippet wraps * around depends on the hook that is given to * cogl_snippet_new(). Note that some hooks can't be used with a layer * and need to be added with cogl_pipeline_add_snippet() instead. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_pipeline_add_layer_snippet (CoglPipeline *pipeline, int layer, CoglSnippet *snippet); COGL_EXPORT void cogl_pipeline_set_layer_max_mipmap_level (CoglPipeline *pipeline, int layer, int max_level); G_END_DECLS #endif /* __COGL_PIPELINE_LAYER_STATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-cache.c0000664000175000017500000001660614723361714017557 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-cache.h" #include "cogl-pipeline-hash-table.h" struct _CoglPipelineCache { CoglPipelineHashTable fragment_hash; CoglPipelineHashTable vertex_hash; CoglPipelineHashTable combined_hash; }; CoglPipelineCache * _cogl_pipeline_cache_new (void) { g_autofree CoglPipelineCache *cache = g_new (CoglPipelineCache, 1); unsigned long vertex_state; unsigned long layer_vertex_state; unsigned int fragment_state; unsigned int layer_fragment_state; _COGL_GET_CONTEXT (ctx, 0); vertex_state = _cogl_pipeline_get_state_for_vertex_codegen (ctx); layer_vertex_state = COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN; fragment_state = _cogl_pipeline_get_state_for_fragment_codegen (ctx); layer_fragment_state = _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx); _cogl_pipeline_hash_table_init (&cache->vertex_hash, vertex_state, layer_vertex_state, "vertex shaders"); _cogl_pipeline_hash_table_init (&cache->fragment_hash, fragment_state, layer_fragment_state, "fragment shaders"); _cogl_pipeline_hash_table_init (&cache->combined_hash, vertex_state | fragment_state, layer_vertex_state | layer_fragment_state, "programs"); return g_steal_pointer (&cache); } void _cogl_pipeline_cache_free (CoglPipelineCache *cache) { _cogl_pipeline_hash_table_destroy (&cache->fragment_hash); _cogl_pipeline_hash_table_destroy (&cache->vertex_hash); _cogl_pipeline_hash_table_destroy (&cache->combined_hash); g_free (cache); } CoglPipelineCacheEntry * _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { return _cogl_pipeline_hash_table_get (&cache->fragment_hash, key_pipeline); } CoglPipelineCacheEntry * _cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { return _cogl_pipeline_hash_table_get (&cache->vertex_hash, key_pipeline); } CoglPipelineCacheEntry * _cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline) { return _cogl_pipeline_hash_table_get (&cache->combined_hash, key_pipeline); } #ifdef ENABLE_UNIT_TESTS static void create_pipelines (CoglPipeline **pipelines, int n_pipelines) { int i; for (i = 0; i < n_pipelines; i++) { char *source = g_strdup_printf (" cogl_color_out = " "vec4 (%f, 0.0, 0.0, 1.0);\n", i / 255.0f); CoglSnippet *snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ source); g_free (source); pipelines[i] = cogl_pipeline_new (test_ctx); cogl_pipeline_add_snippet (pipelines[i], snippet); cogl_object_unref (snippet); } /* Test that drawing with them works. This should create the entries * in the cache */ for (i = 0; i < n_pipelines; i++) { cogl_framebuffer_draw_rectangle (test_fb, pipelines[i], i, 0, i + 1, 1); test_utils_check_pixel_rgb (test_fb, i, 0, i, 0, 0); } } UNIT_TEST (check_pipeline_pruning, TEST_REQUIREMENT_GLSL, /* requirements */ 0 /* no failure cases */) { CoglPipeline *pipelines[18]; int fb_width, fb_height; CoglPipelineHashTable *fragment_hash = &test_ctx->pipeline_cache->fragment_hash; CoglPipelineHashTable *combined_hash = &test_ctx->pipeline_cache->combined_hash; int i; fb_width = cogl_framebuffer_get_width (test_fb); fb_height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100); /* Create 18 unique pipelines. This should end up being more than * the initial expected minimum size so it will trigger the garbage * collection. However all of the pipelines will be in use so they * won't be collected */ create_pipelines (pipelines, 18); /* These pipelines should all have unique entries in the cache. We * should have run the garbage collection once and at that point the * expected minimum size would have been 17 */ g_assert_cmpint (g_hash_table_size (fragment_hash->table), ==, 18); g_assert_cmpint (g_hash_table_size (combined_hash->table), ==, 18); g_assert_cmpint (fragment_hash->expected_min_size, ==, 17); g_assert_cmpint (combined_hash->expected_min_size, ==, 17); /* Destroy the original pipelines and create some new ones. This * should run the garbage collector again but this time the * pipelines won't be in use so it should free some of them */ for (i = 0; i < 18; i++) cogl_object_unref (pipelines[i]); create_pipelines (pipelines, 18); /* The garbage collection should have freed half of the original 18 * pipelines which means there should now be 18*1.5 = 27 */ g_assert_cmpint (g_hash_table_size (fragment_hash->table), ==, 27); g_assert_cmpint (g_hash_table_size (combined_hash->table), ==, 27); /* The 35th pipeline would have caused the garbage collection. At * that point there would be 35-18=17 used unique pipelines. */ g_assert_cmpint (fragment_hash->expected_min_size, ==, 17); g_assert_cmpint (combined_hash->expected_min_size, ==, 17); for (i = 0; i < 18; i++) cogl_object_unref (pipelines[i]); } #endif /* ENABLE_UNIT_TESTS */ muffin-6.4.1/cogl/cogl/cogl-output.c0000664000175000017500000000525614723361714016250 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-output-private.h" #include "cogl-gtype-private.h" #include static void _cogl_output_free (CoglOutput *output); COGL_OBJECT_DEFINE (Output, output); COGL_GTYPE_DEFINE_CLASS (Output, output); CoglOutput * _cogl_output_new (const char *name) { CoglOutput *output; output = g_slice_new0 (CoglOutput); output->name = g_strdup (name); return _cogl_output_object_new (output); } static void _cogl_output_free (CoglOutput *output) { g_free (output->name); g_slice_free (CoglOutput, output); } gboolean _cogl_output_values_equal (CoglOutput *output, CoglOutput *other) { return memcmp ((const char *)output + G_STRUCT_OFFSET (CoglOutput, x), (const char *)other + G_STRUCT_OFFSET (CoglOutput, x), sizeof (CoglOutput) - G_STRUCT_OFFSET (CoglOutput, x)) == 0; } int cogl_output_get_x (CoglOutput *output) { return output->x; } int cogl_output_get_y (CoglOutput *output) { return output->y; } int cogl_output_get_width (CoglOutput *output) { return output->width; } int cogl_output_get_height (CoglOutput *output) { return output->height; } int cogl_output_get_mm_width (CoglOutput *output) { return output->mm_width; } int cogl_output_get_mm_height (CoglOutput *output) { return output->mm_height; } CoglSubpixelOrder cogl_output_get_subpixel_order (CoglOutput *output) { return output->subpixel_order; } float cogl_output_get_refresh_rate (CoglOutput *output) { return output->refresh_rate; } muffin-6.4.1/cogl/cogl/cogl-clip-stack.c0000664000175000017500000003254514723361714016743 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include #include #include "cogl-clip-stack.h" #include "cogl-context-private.h" #include "cogl-framebuffer-private.h" #include "cogl-journal-private.h" #include "cogl-util.h" #include "cogl-matrix-private.h" #include "cogl-primitives-private.h" #include "cogl-private.h" #include "cogl-attribute-private.h" #include "cogl-primitive-private.h" #include "cogl1-context.h" #include "cogl-offscreen.h" #include "cogl-matrix-stack.h" static void * _cogl_clip_stack_push_entry (CoglClipStack *clip_stack, size_t size, CoglClipStackType type) { CoglClipStack *entry = g_slice_alloc (size); /* The new entry starts with a ref count of 1 because the stack holds a reference to it as it is the top entry */ entry->ref_count = 1; entry->type = type; entry->parent = clip_stack; /* We don't need to take a reference to the parent from the entry because the we are stealing the ref in the new stack top */ return entry; } static void get_transformed_corners (float x_1, float y_1, float x_2, float y_2, CoglMatrix *modelview, CoglMatrix *projection, const float *viewport, float *transformed_corners) { int i; transformed_corners[0] = x_1; transformed_corners[1] = y_1; transformed_corners[2] = x_2; transformed_corners[3] = y_1; transformed_corners[4] = x_2; transformed_corners[5] = y_2; transformed_corners[6] = x_1; transformed_corners[7] = y_2; /* Project the coordinates to window space coordinates */ for (i = 0; i < 4; i++) { float *v = transformed_corners + i * 2; _cogl_transform_point (modelview, projection, viewport, v, v + 1); } } /* Sets the window-space bounds of the entry based on the projected coordinates of the given rectangle */ static void _cogl_clip_stack_entry_set_bounds (CoglClipStack *entry, float *transformed_corners) { float min_x = G_MAXFLOAT, min_y = G_MAXFLOAT; float max_x = -G_MAXFLOAT, max_y = -G_MAXFLOAT; int i; for (i = 0; i < 4; i++) { float *v = transformed_corners + i * 2; if (v[0] > max_x) max_x = v[0]; if (v[0] < min_x) min_x = v[0]; if (v[1] > max_y) max_y = v[1]; if (v[1] < min_y) min_y = v[1]; } entry->bounds_x0 = floorf (min_x); entry->bounds_x1 = ceilf (max_x); entry->bounds_y0 = floorf (min_y); entry->bounds_y1 = ceilf (max_y); } CoglClipStack * _cogl_clip_stack_push_window_rectangle (CoglClipStack *stack, int x_offset, int y_offset, int width, int height) { CoglClipStack *entry; entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackWindowRect), COGL_CLIP_STACK_WINDOW_RECT); entry->bounds_x0 = x_offset; entry->bounds_x1 = x_offset + width; entry->bounds_y0 = y_offset; entry->bounds_y1 = y_offset + height; return entry; } CoglClipStack * _cogl_clip_stack_push_rectangle (CoglClipStack *stack, float x_1, float y_1, float x_2, float y_2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport) { CoglClipStackRect *entry; CoglMatrix modelview; CoglMatrix projection; CoglMatrix modelview_projection; /* Corners of the given rectangle in an clockwise order: * (0, 1) (2, 3) * * * * (6, 7) (4, 5) */ float rect[] = { x_1, y_1, x_2, y_1, x_2, y_2, x_1, y_2 }; /* Make a new entry */ entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackRect), COGL_CLIP_STACK_RECT); entry->x0 = x_1; entry->y0 = y_1; entry->x1 = x_2; entry->y1 = y_2; entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); cogl_matrix_entry_get (modelview_entry, &modelview); cogl_matrix_entry_get (projection_entry, &projection); cogl_matrix_multiply (&modelview_projection, &projection, &modelview); /* Technically we could avoid the viewport transform at this point * if we want to make this a bit faster. */ _cogl_transform_point (&modelview, &projection, viewport, &rect[0], &rect[1]); _cogl_transform_point (&modelview, &projection, viewport, &rect[2], &rect[3]); _cogl_transform_point (&modelview, &projection, viewport, &rect[4], &rect[5]); _cogl_transform_point (&modelview, &projection, viewport, &rect[6], &rect[7]); /* If the fully transformed rectangle isn't still axis aligned we * can't handle it using a scissor. * * We don't use an epsilon here since we only really aim to catch * simple cases where the transform doesn't leave the rectangle screen * aligned and don't mind some false positives. */ if (rect[0] != rect[6] || rect[1] != rect[3] || rect[2] != rect[4] || rect[7] != rect[5]) { entry->can_be_scissor = FALSE; _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, rect); } else { CoglClipStack *base_entry = (CoglClipStack *) entry; x_1 = rect[0]; y_1 = rect[1]; x_2 = rect[4]; y_2 = rect[5]; /* Consider that the modelview matrix may flip the rectangle * along the x or y axis... */ #define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) if (x_1 > x_2) SWAP (x_1, x_2); if (y_1 > y_2) SWAP (y_1, y_2); #undef SWAP base_entry->bounds_x0 = COGL_UTIL_NEARBYINT (x_1); base_entry->bounds_y0 = COGL_UTIL_NEARBYINT (y_1); base_entry->bounds_x1 = COGL_UTIL_NEARBYINT (x_2); base_entry->bounds_y1 = COGL_UTIL_NEARBYINT (y_2); entry->can_be_scissor = TRUE; } return (CoglClipStack *) entry; } CoglClipStack * _cogl_clip_stack_push_primitive (CoglClipStack *stack, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport) { CoglClipStackPrimitive *entry; CoglMatrix modelview; CoglMatrix projection; float transformed_corners[8]; entry = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackPrimitive), COGL_CLIP_STACK_PRIMITIVE); entry->primitive = cogl_object_ref (primitive); entry->matrix_entry = cogl_matrix_entry_ref (modelview_entry); entry->bounds_x1 = bounds_x1; entry->bounds_y1 = bounds_y1; entry->bounds_x2 = bounds_x2; entry->bounds_y2 = bounds_y2; cogl_matrix_entry_get (modelview_entry, &modelview); cogl_matrix_entry_get (projection_entry, &projection); get_transformed_corners (bounds_x1, bounds_y1, bounds_x2, bounds_y2, &modelview, &projection, viewport, transformed_corners); /* NB: this is referring to the bounds in window coordinates as opposed * to the bounds above in primitive local coordinates. */ _cogl_clip_stack_entry_set_bounds ((CoglClipStack *) entry, transformed_corners); return (CoglClipStack *) entry; } CoglClipStack * cogl_clip_stack_push_region (CoglClipStack *stack, cairo_region_t *region) { CoglClipStack *entry; CoglClipStackRegion *entry_region; cairo_rectangle_int_t bounds; entry_region = _cogl_clip_stack_push_entry (stack, sizeof (CoglClipStackRegion), COGL_CLIP_STACK_REGION); entry = (CoglClipStack *) entry_region; cairo_region_get_extents (region, &bounds); entry->bounds_x0 = bounds.x; entry->bounds_x1 = bounds.x + bounds.width; entry->bounds_y0 = bounds.y; entry->bounds_y1 = bounds.y + bounds.height; entry_region->region = cairo_region_reference (region); return entry; } CoglClipStack * _cogl_clip_stack_ref (CoglClipStack *entry) { /* A NULL pointer is considered a valid stack so we should accept that as an argument */ if (entry) entry->ref_count++; return entry; } void _cogl_clip_stack_unref (CoglClipStack *entry) { /* Unref all of the entries until we hit the root of the list or the entry still has a remaining reference */ while (entry && --entry->ref_count <= 0) { CoglClipStack *parent = entry->parent; switch (entry->type) { case COGL_CLIP_STACK_RECT: { CoglClipStackRect *rect = (CoglClipStackRect *) entry; cogl_matrix_entry_unref (rect->matrix_entry); g_slice_free1 (sizeof (CoglClipStackRect), entry); break; } case COGL_CLIP_STACK_WINDOW_RECT: g_slice_free1 (sizeof (CoglClipStackWindowRect), entry); break; case COGL_CLIP_STACK_PRIMITIVE: { CoglClipStackPrimitive *primitive_entry = (CoglClipStackPrimitive *) entry; cogl_matrix_entry_unref (primitive_entry->matrix_entry); cogl_object_unref (primitive_entry->primitive); g_slice_free1 (sizeof (CoglClipStackPrimitive), entry); break; } case COGL_CLIP_STACK_REGION: { CoglClipStackRegion *region = (CoglClipStackRegion *) entry; cairo_region_destroy (region->region); g_slice_free1 (sizeof (CoglClipStackRegion), entry); break; } default: g_assert_not_reached (); } entry = parent; } } CoglClipStack * _cogl_clip_stack_pop (CoglClipStack *stack) { CoglClipStack *new_top; g_return_val_if_fail (stack != NULL, NULL); /* To pop we are moving the top of the stack to the old top's parent node. The stack always needs to have a reference to the top entry so we must take a reference to the new top. The stack would have previously had a reference to the old top so we need to decrease the ref count on that. We need to ref the new head first in case this stack was the only thing referencing the old top. In that case the call to _cogl_clip_stack_entry_unref will unref the parent. */ new_top = stack->parent; _cogl_clip_stack_ref (new_top); _cogl_clip_stack_unref (stack); return new_top; } void _cogl_clip_stack_get_bounds (CoglClipStack *stack, int *scissor_x0, int *scissor_y0, int *scissor_x1, int *scissor_y1) { CoglClipStack *entry; *scissor_x0 = 0; *scissor_y0 = 0; *scissor_x1 = G_MAXINT; *scissor_y1 = G_MAXINT; for (entry = stack; entry; entry = entry->parent) { /* Get the intersection of the current scissor and the bounding box of this clip */ _cogl_util_scissor_intersect (entry->bounds_x0, entry->bounds_y0, entry->bounds_x1, entry->bounds_y1, scissor_x0, scissor_y0, scissor_x1, scissor_y1); } } void _cogl_clip_stack_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; ctx->driver_vtable->clip_stack_flush (stack, framebuffer); } muffin-6.4.1/cogl/cogl/cogl-memory-stack-private.h0000664000175000017500000000312314723361714020767 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_MEMORY_STACK__ #define __COGL_MEMORY_STACK__ #include typedef struct _CoglMemoryStack CoglMemoryStack; CoglMemoryStack * _cogl_memory_stack_new (size_t initial_size_bytes); void * _cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes); void _cogl_memory_stack_rewind (CoglMemoryStack *stack); void _cogl_memory_stack_free (CoglMemoryStack *stack); #endif /* __COGL_MEMORY_STACK__ */ muffin-6.4.1/cogl/cogl/cogl-matrix.c0000664000175000017500000020520714723361714016212 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2011 Intel Corporation. * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ /* * Copyright (C) 1999-2005 Brian Paul All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included * in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* * Note: a lot of this code is based on code that was taken from Mesa. * * Changes compared to the original code from Mesa: * * - instead of allocating matrix->m and matrix->inv using malloc, our * public CoglMatrix typedef is large enough to directly contain the * matrix, its inverse, a type and a set of flags. * - instead of having a _cogl_matrix_analyse which updates the type, * flags and inverse, we have _cogl_matrix_update_inverse which * essentially does the same thing (internally making use of * _cogl_matrix_update_type_and_flags()) but with additional guards in * place to bail out when the inverse matrix is still valid. * - when initializing a matrix with the identity matrix we don't * immediately initialize the inverse matrix; rather we just set the * dirty flag for the inverse (since it's likely the user won't request * the inverse of the identity matrix) */ #include "cogl-config.h" #include #include #include #include #include #include #include #include COGL_GTYPE_DEFINE_BOXED (Matrix, matrix, cogl_matrix_copy, cogl_matrix_free); /* * Symbolic names to some of the entries in the matrix * * These are handy for the viewport mapping, which is expressed as a matrix. */ #define MAT_SX 0 #define MAT_SY 5 #define MAT_SZ 10 #define MAT_TX 12 #define MAT_TY 13 #define MAT_TZ 14 /* * These identify different kinds of 4x4 transformation matrices and we use * this information to find fast-paths when available. */ enum CoglMatrixType { COGL_MATRIX_TYPE_GENERAL, /**< general 4x4 matrix */ COGL_MATRIX_TYPE_IDENTITY, /**< identity matrix */ COGL_MATRIX_TYPE_3D_NO_ROT, /**< orthogonal projection and others... */ COGL_MATRIX_TYPE_PERSPECTIVE, /**< perspective projection matrix */ COGL_MATRIX_TYPE_2D, /**< 2-D transformation */ COGL_MATRIX_TYPE_2D_NO_ROT, /**< 2-D scale & translate only */ COGL_MATRIX_TYPE_3D, /**< 3-D transformation */ COGL_MATRIX_N_TYPES } ; #define DEG2RAD (G_PI/180.0) /* Dot product of two 2-element vectors */ #define DOT2(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] ) /* Dot product of two 3-element vectors */ #define DOT3(A,B) ( (A)[0]*(B)[0] + (A)[1]*(B)[1] + (A)[2]*(B)[2] ) #define CROSS3(N, U, V) \ do { \ (N)[0] = (U)[1]*(V)[2] - (U)[2]*(V)[1]; \ (N)[1] = (U)[2]*(V)[0] - (U)[0]*(V)[2]; \ (N)[2] = (U)[0]*(V)[1] - (U)[1]*(V)[0]; \ } while (0) #define SUB_3V(DST, SRCA, SRCB) \ do { \ (DST)[0] = (SRCA)[0] - (SRCB)[0]; \ (DST)[1] = (SRCA)[1] - (SRCB)[1]; \ (DST)[2] = (SRCA)[2] - (SRCB)[2]; \ } while (0) #define LEN_SQUARED_3FV( V ) ((V)[0]*(V)[0]+(V)[1]*(V)[1]+(V)[2]*(V)[2]) /* * \defgroup MatFlags MAT_FLAG_XXX-flags * * Bitmasks to indicate different kinds of 4x4 matrices in CoglMatrix::flags */ #define MAT_FLAG_IDENTITY 0 /*< is an identity matrix flag. * (Not actually used - the identity * matrix is identified by the absense * of all other flags.) */ #define MAT_FLAG_GENERAL 0x1 /*< is a general matrix flag */ #define MAT_FLAG_ROTATION 0x2 /*< is a rotation matrix flag */ #define MAT_FLAG_TRANSLATION 0x4 /*< is a translation matrix flag */ #define MAT_FLAG_UNIFORM_SCALE 0x8 /*< is an uniform scaling matrix flag */ #define MAT_FLAG_GENERAL_SCALE 0x10 /*< is a general scaling matrix flag */ #define MAT_FLAG_GENERAL_3D 0x20 /*< general 3D matrix flag */ #define MAT_FLAG_PERSPECTIVE 0x40 /*< is a perspective proj matrix flag */ #define MAT_FLAG_SINGULAR 0x80 /*< is a singular matrix flag */ #define MAT_DIRTY_TYPE 0x100 /*< matrix type is dirty */ #define MAT_DIRTY_FLAGS 0x200 /*< matrix flags are dirty */ #define MAT_DIRTY_INVERSE 0x400 /*< matrix inverse is dirty */ /* angle preserving matrix flags mask */ #define MAT_FLAGS_ANGLE_PRESERVING (MAT_FLAG_ROTATION | \ MAT_FLAG_TRANSLATION | \ MAT_FLAG_UNIFORM_SCALE) /* geometry related matrix flags mask */ #define MAT_FLAGS_GEOMETRY (MAT_FLAG_GENERAL | \ MAT_FLAG_ROTATION | \ MAT_FLAG_TRANSLATION | \ MAT_FLAG_UNIFORM_SCALE | \ MAT_FLAG_GENERAL_SCALE | \ MAT_FLAG_GENERAL_3D | \ MAT_FLAG_PERSPECTIVE | \ MAT_FLAG_SINGULAR) /* length preserving matrix flags mask */ #define MAT_FLAGS_LENGTH_PRESERVING (MAT_FLAG_ROTATION | \ MAT_FLAG_TRANSLATION) /* 3D (non-perspective) matrix flags mask */ #define MAT_FLAGS_3D (MAT_FLAG_ROTATION | \ MAT_FLAG_TRANSLATION | \ MAT_FLAG_UNIFORM_SCALE | \ MAT_FLAG_GENERAL_SCALE | \ MAT_FLAG_GENERAL_3D) /* dirty matrix flags mask */ #define MAT_DIRTY_ALL (MAT_DIRTY_TYPE | \ MAT_DIRTY_FLAGS | \ MAT_DIRTY_INVERSE) /* * Test geometry related matrix flags. * * @mat a pointer to a CoglMatrix structure. * @a flags mask. * * Returns: non-zero if all geometry related matrix flags are contained within * the mask, or zero otherwise. */ #define TEST_MAT_FLAGS(mat, a) \ ((MAT_FLAGS_GEOMETRY & (~(a)) & ((mat)->flags) ) == 0) /* * Names of the corresponding CoglMatrixType values. */ static const char *types[] = { "COGL_MATRIX_TYPE_GENERAL", "COGL_MATRIX_TYPE_IDENTITY", "COGL_MATRIX_TYPE_3D_NO_ROT", "COGL_MATRIX_TYPE_PERSPECTIVE", "COGL_MATRIX_TYPE_2D", "COGL_MATRIX_TYPE_2D_NO_ROT", "COGL_MATRIX_TYPE_3D" }; /* * Identity matrix. */ static float identity[16] = { 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0 }; #define A(row,col) a[(col<<2)+row] #define B(row,col) b[(col<<2)+row] #define R(row,col) result[(col<<2)+row] /* * Perform a full 4x4 matrix multiplication. * * It's assumed that @result != @b. @product == @a is allowed. * * KW: 4*16 = 64 multiplications */ static void matrix_multiply4x4 (float *result, const float *a, const float *b) { int i; for (i = 0; i < 4; i++) { const float ai0 = A(i,0), ai1=A(i,1), ai2=A(i,2), ai3=A(i,3); R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0) + ai3 * B(3,0); R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1) + ai3 * B(3,1); R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2) + ai3 * B(3,2); R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3 * B(3,3); } } /* * Multiply two matrices known to occupy only the top three rows, such * as typical model matrices, and orthogonal matrices. * * @a matrix. * @b matrix. * @product will receive the product of \p a and \p b. */ static void matrix_multiply3x4 (float *result, const float *a, const float *b) { int i; for (i = 0; i < 3; i++) { const float ai0 = A(i,0), ai1 = A(i,1), ai2 = A(i,2), ai3 = A(i,3); R(i,0) = ai0 * B(0,0) + ai1 * B(1,0) + ai2 * B(2,0); R(i,1) = ai0 * B(0,1) + ai1 * B(1,1) + ai2 * B(2,1); R(i,2) = ai0 * B(0,2) + ai1 * B(1,2) + ai2 * B(2,2); R(i,3) = ai0 * B(0,3) + ai1 * B(1,3) + ai2 * B(2,3) + ai3; } R(3,0) = 0; R(3,1) = 0; R(3,2) = 0; R(3,3) = 1; } #undef A #undef B #undef R /* * Multiply a matrix by an array of floats with known properties. * * @mat pointer to a CoglMatrix structure containing the left multiplication * matrix, and that will receive the product result. * @m right multiplication matrix array. * @flags flags of the matrix \p m. * * Joins both flags and marks the type and inverse as dirty. Calls * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() * otherwise. */ static void matrix_multiply_array_with_flags (CoglMatrix *result, const float *array, unsigned int flags) { result->flags |= (flags | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); if (TEST_MAT_FLAGS (result, MAT_FLAGS_3D)) matrix_multiply3x4 ((float *)result, (float *)result, array); else matrix_multiply4x4 ((float *)result, (float *)result, array); } /* Joins both flags and marks the type and inverse as dirty. Calls * matrix_multiply3x4() if both matrices are 3D, or matrix_multiply4x4() * otherwise. */ static void _cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, const CoglMatrix *b) { result->flags = (a->flags | b->flags | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); if (TEST_MAT_FLAGS(result, MAT_FLAGS_3D)) matrix_multiply3x4 ((float *)result, (float *)a, (float *)b); else matrix_multiply4x4 ((float *)result, (float *)a, (float *)b); } void cogl_matrix_multiply (CoglMatrix *result, const CoglMatrix *a, const CoglMatrix *b) { _cogl_matrix_multiply (result, a, b); _COGL_MATRIX_DEBUG_PRINT (result); } #if 0 /* Marks the matrix flags with general flag, and type and inverse dirty flags. * Calls matrix_multiply4x4() for the multiplication. */ static void _cogl_matrix_multiply_array (CoglMatrix *result, const float *array) { result->flags |= (MAT_FLAG_GENERAL | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE | MAT_DIRTY_FLAGS); matrix_multiply4x4 ((float *)result, (float *)result, (float *)array); } #endif /* * Print a matrix array. * * Called by _cogl_matrix_print() to print a matrix or its inverse. */ static void print_matrix_floats (const char *prefix, const float m[16]) { int i; for (i = 0;i < 4; i++) g_print ("%s\t%f %f %f %f\n", prefix, m[i], m[4+i], m[8+i], m[12+i] ); } void _cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix) { if (!(matrix->flags & MAT_DIRTY_TYPE)) { g_return_if_fail (matrix->type < COGL_MATRIX_N_TYPES); g_print ("%sMatrix type: %s, flags: %x\n", prefix, types[matrix->type], (int)matrix->flags); } else g_print ("%sMatrix type: DIRTY, flags: %x\n", prefix, (int)matrix->flags); print_matrix_floats (prefix, (float *)matrix); g_print ("%sInverse: \n", prefix); if (!(matrix->flags & MAT_DIRTY_INVERSE)) { float prod[16]; print_matrix_floats (prefix, matrix->inv); matrix_multiply4x4 (prod, (float *)matrix, matrix->inv); g_print ("%sMat * Inverse:\n", prefix); print_matrix_floats (prefix, prod); } else g_print ("%s - not available\n", prefix); } /* * Dumps the contents of a CoglMatrix structure. */ void cogl_debug_matrix_print (const CoglMatrix *matrix) { _cogl_matrix_prefix_print ("", matrix); } /* * References an element of 4x4 matrix. * * @m matrix array. * @c column of the desired element. * @r row of the desired element. * * Returns: value of the desired element. * * Calculate the linear storage index of the element and references it. */ #define MAT(m,r,c) (m)[(c)*4+(r)] /* * Swaps the values of two floating pointer variables. * * Used by invert_matrix_general() to swap the row pointers. */ #define SWAP_ROWS(a, b) { float *_tmp = a; (a)=(b); (b)=_tmp; } /* * Compute inverse of 4x4 transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * \author * Code contributed by Jacques Leroy jle@star.be * * Calculates the inverse matrix by performing the gaussian matrix reduction * with partial pivoting followed by back/substitution with the loops manually * unrolled. */ static gboolean invert_matrix_general (CoglMatrix *matrix) { const float *m = (float *)matrix; float *out = matrix->inv; float wtmp[4][8]; float m0, m1, m2, m3, s; float *r0, *r1, *r2, *r3; r0 = wtmp[0], r1 = wtmp[1], r2 = wtmp[2], r3 = wtmp[3]; r0[0] = MAT (m, 0, 0), r0[1] = MAT (m, 0, 1), r0[2] = MAT (m, 0, 2), r0[3] = MAT (m, 0, 3), r0[4] = 1.0, r0[5] = r0[6] = r0[7] = 0.0, r1[0] = MAT (m, 1, 0), r1[1] = MAT (m, 1, 1), r1[2] = MAT (m, 1, 2), r1[3] = MAT (m, 1, 3), r1[5] = 1.0, r1[4] = r1[6] = r1[7] = 0.0, r2[0] = MAT (m, 2, 0), r2[1] = MAT (m, 2, 1), r2[2] = MAT (m, 2, 2), r2[3] = MAT (m, 2, 3), r2[6] = 1.0, r2[4] = r2[5] = r2[7] = 0.0, r3[0] = MAT (m, 3, 0), r3[1] = MAT (m, 3, 1), r3[2] = MAT (m, 3, 2), r3[3] = MAT (m, 3, 3), r3[7] = 1.0, r3[4] = r3[5] = r3[6] = 0.0; /* choose pivot - or die */ if (fabsf (r3[0]) > fabsf (r2[0])) SWAP_ROWS (r3, r2); if (fabsf (r2[0]) > fabsf (r1[0])) SWAP_ROWS (r2, r1); if (fabsf (r1[0]) > fabsf (r0[0])) SWAP_ROWS (r1, r0); if (0.0 == r0[0]) return FALSE; /* eliminate first variable */ m1 = r1[0]/r0[0]; m2 = r2[0]/r0[0]; m3 = r3[0]/r0[0]; s = r0[1]; r1[1] -= m1 * s; r2[1] -= m2 * s; r3[1] -= m3 * s; s = r0[2]; r1[2] -= m1 * s; r2[2] -= m2 * s; r3[2] -= m3 * s; s = r0[3]; r1[3] -= m1 * s; r2[3] -= m2 * s; r3[3] -= m3 * s; s = r0[4]; if (s != 0.0) { r1[4] -= m1 * s; r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r0[5]; if (s != 0.0) { r1[5] -= m1 * s; r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r0[6]; if (s != 0.0) { r1[6] -= m1 * s; r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r0[7]; if (s != 0.0) { r1[7] -= m1 * s; r2[7] -= m2 * s; r3[7] -= m3 * s; } /* choose pivot - or die */ if (fabsf (r3[1]) > fabsf (r2[1])) SWAP_ROWS (r3, r2); if (fabsf (r2[1]) > fabsf (r1[1])) SWAP_ROWS (r2, r1); if (0.0 == r1[1]) return FALSE; /* eliminate second variable */ m2 = r2[1] / r1[1]; m3 = r3[1] / r1[1]; r2[2] -= m2 * r1[2]; r3[2] -= m3 * r1[2]; r2[3] -= m2 * r1[3]; r3[3] -= m3 * r1[3]; s = r1[4]; if (0.0 != s) { r2[4] -= m2 * s; r3[4] -= m3 * s; } s = r1[5]; if (0.0 != s) { r2[5] -= m2 * s; r3[5] -= m3 * s; } s = r1[6]; if (0.0 != s) { r2[6] -= m2 * s; r3[6] -= m3 * s; } s = r1[7]; if (0.0 != s) { r2[7] -= m2 * s; r3[7] -= m3 * s; } /* choose pivot - or die */ if (fabsf (r3[2]) > fabsf (r2[2])) SWAP_ROWS (r3, r2); if (0.0 == r2[2]) return FALSE; /* eliminate third variable */ m3 = r3[2] / r2[2]; r3[3] -= m3 * r2[3], r3[4] -= m3 * r2[4], r3[5] -= m3 * r2[5], r3[6] -= m3 * r2[6], r3[7] -= m3 * r2[7]; /* last check */ if (0.0 == r3[3]) return FALSE; s = 1.0f / r3[3]; /* now back substitute row 3 */ r3[4] *= s; r3[5] *= s; r3[6] *= s; r3[7] *= s; m2 = r2[3]; /* now back substitute row 2 */ s = 1.0f / r2[2]; r2[4] = s * (r2[4] - r3[4] * m2), r2[5] = s * (r2[5] - r3[5] * m2), r2[6] = s * (r2[6] - r3[6] * m2), r2[7] = s * (r2[7] - r3[7] * m2); m1 = r1[3]; r1[4] -= r3[4] * m1, r1[5] -= r3[5] * m1, r1[6] -= r3[6] * m1, r1[7] -= r3[7] * m1; m0 = r0[3]; r0[4] -= r3[4] * m0, r0[5] -= r3[5] * m0, r0[6] -= r3[6] * m0, r0[7] -= r3[7] * m0; m1 = r1[2]; /* now back substitute row 1 */ s = 1.0f / r1[1]; r1[4] = s * (r1[4] - r2[4] * m1), r1[5] = s * (r1[5] - r2[5] * m1), r1[6] = s * (r1[6] - r2[6] * m1), r1[7] = s * (r1[7] - r2[7] * m1); m0 = r0[2]; r0[4] -= r2[4] * m0, r0[5] -= r2[5] * m0, r0[6] -= r2[6] * m0, r0[7] -= r2[7] * m0; m0 = r0[1]; /* now back substitute row 0 */ s = 1.0f / r0[0]; r0[4] = s * (r0[4] - r1[4] * m0), r0[5] = s * (r0[5] - r1[5] * m0), r0[6] = s * (r0[6] - r1[6] * m0), r0[7] = s * (r0[7] - r1[7] * m0); MAT (out, 0, 0) = r0[4]; MAT (out, 0, 1) = r0[5], MAT (out, 0, 2) = r0[6]; MAT (out, 0, 3) = r0[7], MAT (out, 1, 0) = r1[4]; MAT (out, 1, 1) = r1[5], MAT (out, 1, 2) = r1[6]; MAT (out, 1, 3) = r1[7], MAT (out, 2, 0) = r2[4]; MAT (out, 2, 1) = r2[5], MAT (out, 2, 2) = r2[6]; MAT (out, 2, 3) = r2[7], MAT (out, 3, 0) = r3[4]; MAT (out, 3, 1) = r3[5], MAT (out, 3, 2) = r3[6]; MAT (out, 3, 3) = r3[7]; return TRUE; } #undef SWAP_ROWS /* * Compute inverse of a general 3d transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * \author Adapted from graphics gems II. * * Calculates the inverse of the upper left by first calculating its * determinant and multiplying it to the symmetric adjust matrix of each * element. Finally deals with the translation part by transforming the * original translation vector using by the calculated submatrix inverse. */ static gboolean invert_matrix_3d_general (CoglMatrix *matrix) { const float *in = (float *)matrix; float *out = matrix->inv; float pos, neg, t; float det; /* Calculate the determinant of upper left 3x3 submatrix and * determine if the matrix is singular. */ pos = neg = 0.0; t = MAT (in,0,0) * MAT (in,1,1) * MAT (in,2,2); if (t >= 0.0) pos += t; else neg += t; t = MAT (in,1,0) * MAT (in,2,1) * MAT (in,0,2); if (t >= 0.0) pos += t; else neg += t; t = MAT (in,2,0) * MAT (in,0,1) * MAT (in,1,2); if (t >= 0.0) pos += t; else neg += t; t = -MAT (in,2,0) * MAT (in,1,1) * MAT (in,0,2); if (t >= 0.0) pos += t; else neg += t; t = -MAT (in,1,0) * MAT (in,0,1) * MAT (in,2,2); if (t >= 0.0) pos += t; else neg += t; t = -MAT (in,0,0) * MAT (in,2,1) * MAT (in,1,2); if (t >= 0.0) pos += t; else neg += t; det = pos + neg; if (det*det < 1e-25) return FALSE; det = 1.0f / det; MAT (out,0,0) = ( (MAT (in, 1, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 1, 2) )*det); MAT (out,0,1) = (- (MAT (in, 0, 1)*MAT (in, 2, 2) - MAT (in, 2, 1)*MAT (in, 0, 2) )*det); MAT (out,0,2) = ( (MAT (in, 0, 1)*MAT (in, 1, 2) - MAT (in, 1, 1)*MAT (in, 0, 2) )*det); MAT (out,1,0) = (- (MAT (in,1,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,1,2) )*det); MAT (out,1,1) = ( (MAT (in,0,0)*MAT (in,2,2) - MAT (in,2,0)*MAT (in,0,2) )*det); MAT (out,1,2) = (- (MAT (in,0,0)*MAT (in,1,2) - MAT (in,1,0)*MAT (in,0,2) )*det); MAT (out,2,0) = ( (MAT (in,1,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,1,1) )*det); MAT (out,2,1) = (- (MAT (in,0,0)*MAT (in,2,1) - MAT (in,2,0)*MAT (in,0,1) )*det); MAT (out,2,2) = ( (MAT (in,0,0)*MAT (in,1,1) - MAT (in,1,0)*MAT (in,0,1) )*det); /* Do the translation part */ MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + MAT (in, 1, 3) * MAT (out, 0, 1) + MAT (in, 2, 3) * MAT (out, 0, 2) ); MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + MAT (in, 1, 3) * MAT (out, 1, 1) + MAT (in, 2, 3) * MAT (out, 1, 2) ); MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2 ,0) + MAT (in, 1, 3) * MAT (out, 2, 1) + MAT (in, 2, 3) * MAT (out, 2, 2) ); return TRUE; } /* * Compute inverse of a 3d transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * If the matrix is not an angle preserving matrix then calls * invert_matrix_3d_general for the actual calculation. Otherwise calculates * the inverse matrix analyzing and inverting each of the scaling, rotation and * translation parts. */ static gboolean invert_matrix_3d (CoglMatrix *matrix) { const float *in = (float *)matrix; float *out = matrix->inv; memcpy (out, identity, 16 * sizeof (float)); if (!TEST_MAT_FLAGS(matrix, MAT_FLAGS_ANGLE_PRESERVING)) return invert_matrix_3d_general (matrix); if (matrix->flags & MAT_FLAG_UNIFORM_SCALE) { float scale = (MAT (in, 0, 0) * MAT (in, 0, 0) + MAT (in, 0, 1) * MAT (in, 0, 1) + MAT (in, 0, 2) * MAT (in, 0, 2)); if (scale == 0.0) return FALSE; scale = 1.0f / scale; /* Transpose and scale the 3 by 3 upper-left submatrix. */ MAT (out, 0, 0) = scale * MAT (in, 0, 0); MAT (out, 1, 0) = scale * MAT (in, 0, 1); MAT (out, 2, 0) = scale * MAT (in, 0, 2); MAT (out, 0, 1) = scale * MAT (in, 1, 0); MAT (out, 1, 1) = scale * MAT (in, 1, 1); MAT (out, 2, 1) = scale * MAT (in, 1, 2); MAT (out, 0, 2) = scale * MAT (in, 2, 0); MAT (out, 1, 2) = scale * MAT (in, 2, 1); MAT (out, 2, 2) = scale * MAT (in, 2, 2); } else if (matrix->flags & MAT_FLAG_ROTATION) { /* Transpose the 3 by 3 upper-left submatrix. */ MAT (out, 0, 0) = MAT (in, 0, 0); MAT (out, 1, 0) = MAT (in, 0, 1); MAT (out, 2, 0) = MAT (in, 0, 2); MAT (out, 0, 1) = MAT (in, 1, 0); MAT (out, 1, 1) = MAT (in, 1, 1); MAT (out, 2, 1) = MAT (in, 1, 2); MAT (out, 0, 2) = MAT (in, 2, 0); MAT (out, 1, 2) = MAT (in, 2, 1); MAT (out, 2, 2) = MAT (in, 2, 2); } else { /* pure translation */ memcpy (out, identity, 16 * sizeof (float)); MAT (out, 0, 3) = - MAT (in, 0, 3); MAT (out, 1, 3) = - MAT (in, 1, 3); MAT (out, 2, 3) = - MAT (in, 2, 3); return TRUE; } if (matrix->flags & MAT_FLAG_TRANSLATION) { /* Do the translation part */ MAT (out,0,3) = - (MAT (in, 0, 3) * MAT (out, 0, 0) + MAT (in, 1, 3) * MAT (out, 0, 1) + MAT (in, 2, 3) * MAT (out, 0, 2) ); MAT (out,1,3) = - (MAT (in, 0, 3) * MAT (out, 1, 0) + MAT (in, 1, 3) * MAT (out, 1, 1) + MAT (in, 2, 3) * MAT (out, 1, 2) ); MAT (out,2,3) = - (MAT (in, 0, 3) * MAT (out, 2, 0) + MAT (in, 1, 3) * MAT (out, 2, 1) + MAT (in, 2, 3) * MAT (out, 2, 2) ); } else MAT (out, 0, 3) = MAT (out, 1, 3) = MAT (out, 2, 3) = 0.0; return TRUE; } /* * Compute inverse of an identity transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: always %TRUE. * * Simply copies identity into CoglMatrix::inv. */ static gboolean invert_matrix_identity (CoglMatrix *matrix) { memcpy (matrix->inv, identity, 16 * sizeof (float)); return TRUE; } /* * Compute inverse of a no-rotation 3d transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * Calculates the */ static gboolean invert_matrix_3d_no_rotation (CoglMatrix *matrix) { const float *in = (float *)matrix; float *out = matrix->inv; if (MAT (in,0,0) == 0 || MAT (in,1,1) == 0 || MAT (in,2,2) == 0) return FALSE; memcpy (out, identity, 16 * sizeof (float)); MAT (out,0,0) = 1.0f / MAT (in,0,0); MAT (out,1,1) = 1.0f / MAT (in,1,1); MAT (out,2,2) = 1.0f / MAT (in,2,2); if (matrix->flags & MAT_FLAG_TRANSLATION) { MAT (out,0,3) = - (MAT (in,0,3) * MAT (out,0,0)); MAT (out,1,3) = - (MAT (in,1,3) * MAT (out,1,1)); MAT (out,2,3) = - (MAT (in,2,3) * MAT (out,2,2)); } return TRUE; } /* * Compute inverse of a no-rotation 2d transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * Calculates the inverse matrix by applying the inverse scaling and * translation to the identity matrix. */ static gboolean invert_matrix_2d_no_rotation (CoglMatrix *matrix) { const float *in = (float *)matrix; float *out = matrix->inv; if (MAT (in, 0, 0) == 0 || MAT (in, 1, 1) == 0) return FALSE; memcpy (out, identity, 16 * sizeof (float)); MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); if (matrix->flags & MAT_FLAG_TRANSLATION) { MAT (out, 0, 3) = - (MAT (in, 0, 3) * MAT (out, 0, 0)); MAT (out, 1, 3) = - (MAT (in, 1, 3) * MAT (out, 1, 1)); } return TRUE; } #if 0 /* broken */ static gboolean invert_matrix_perspective (CoglMatrix *matrix) { const float *in = matrix; float *out = matrix->inv; if (MAT (in,2,3) == 0) return FALSE; memcpy( out, identity, 16 * sizeof(float) ); MAT (out, 0, 0) = 1.0f / MAT (in, 0, 0); MAT (out, 1, 1) = 1.0f / MAT (in, 1, 1); MAT (out, 0, 3) = MAT (in, 0, 2); MAT (out, 1, 3) = MAT (in, 1, 2); MAT (out,2,2) = 0; MAT (out,2,3) = -1; MAT (out,3,2) = 1.0f / MAT (in,2,3); MAT (out,3,3) = MAT (in,2,2) * MAT (out,3,2); return TRUE; } #endif /* * Matrix inversion function pointer type. */ typedef gboolean (*inv_mat_func)(CoglMatrix *matrix); /* * Table of the matrix inversion functions according to the matrix type. */ static inv_mat_func inv_mat_tab[7] = { invert_matrix_general, invert_matrix_identity, invert_matrix_3d_no_rotation, #if 0 /* Don't use this function for now - it fails when the projection matrix * is premultiplied by a translation (ala Chromium's tilesort SPU). */ invert_matrix_perspective, #else invert_matrix_general, #endif invert_matrix_3d, /* lazy! */ invert_matrix_2d_no_rotation, invert_matrix_3d }; #define ZERO(x) (1<flags &= ~MAT_FLAGS_GEOMETRY; /* Check for translation - no-one really cares */ if ((mask & MASK_NO_TRX) != MASK_NO_TRX) matrix->flags |= MAT_FLAG_TRANSLATION; /* Do the real work */ if (mask == (unsigned int) MASK_IDENTITY) matrix->type = COGL_MATRIX_TYPE_IDENTITY; else if ((mask & MASK_2D_NO_ROT) == (unsigned int) MASK_2D_NO_ROT) { matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; if ((mask & MASK_NO_2D_SCALE) != MASK_NO_2D_SCALE) matrix->flags |= MAT_FLAG_GENERAL_SCALE; } else if ((mask & MASK_2D) == (unsigned int) MASK_2D) { float mm = DOT2 (m, m); float m4m4 = DOT2 (m+4,m+4); float mm4 = DOT2 (m,m+4); matrix->type = COGL_MATRIX_TYPE_2D; /* Check for scale */ if (SQ (mm-1) > SQ (1e-6) || SQ (m4m4-1) > SQ (1e-6)) matrix->flags |= MAT_FLAG_GENERAL_SCALE; /* Check for rotation */ if (SQ (mm4) > SQ (1e-6)) matrix->flags |= MAT_FLAG_GENERAL_3D; else matrix->flags |= MAT_FLAG_ROTATION; } else if ((mask & MASK_3D_NO_ROT) == (unsigned int) MASK_3D_NO_ROT) { matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; /* Check for scale */ if (SQ (m[0]-m[5]) < SQ (1e-6) && SQ (m[0]-m[10]) < SQ (1e-6)) { if (SQ (m[0]-1.0) > SQ (1e-6)) matrix->flags |= MAT_FLAG_UNIFORM_SCALE; } else matrix->flags |= MAT_FLAG_GENERAL_SCALE; } else if ((mask & MASK_3D) == (unsigned int) MASK_3D) { float c1 = DOT3 (m,m); float c2 = DOT3 (m+4,m+4); float c3 = DOT3 (m+8,m+8); float d1 = DOT3 (m, m+4); float cp[3]; matrix->type = COGL_MATRIX_TYPE_3D; /* Check for scale */ if (SQ (c1-c2) < SQ (1e-6) && SQ (c1-c3) < SQ (1e-6)) { if (SQ (c1-1.0) > SQ (1e-6)) matrix->flags |= MAT_FLAG_UNIFORM_SCALE; /* else no scale at all */ } else matrix->flags |= MAT_FLAG_GENERAL_SCALE; /* Check for rotation */ if (SQ (d1) < SQ (1e-6)) { CROSS3 ( cp, m, m+4); SUB_3V ( cp, cp, (m+8)); if (LEN_SQUARED_3FV(cp) < SQ(1e-6)) matrix->flags |= MAT_FLAG_ROTATION; else matrix->flags |= MAT_FLAG_GENERAL_3D; } else matrix->flags |= MAT_FLAG_GENERAL_3D; /* shear, etc */ } else if ((mask & MASK_PERSPECTIVE) == MASK_PERSPECTIVE && m[11]==-1.0f) { matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; matrix->flags |= MAT_FLAG_GENERAL; } else { matrix->type = COGL_MATRIX_TYPE_GENERAL; matrix->flags |= MAT_FLAG_GENERAL; } } /* * Analyze a matrix given that its flags are accurate. * * This is the more common operation, hopefully. */ static void analyse_from_flags (CoglMatrix *matrix) { const float *m = (float *)matrix; if (TEST_MAT_FLAGS(matrix, 0)) matrix->type = COGL_MATRIX_TYPE_IDENTITY; else if (TEST_MAT_FLAGS(matrix, (MAT_FLAG_TRANSLATION | MAT_FLAG_UNIFORM_SCALE | MAT_FLAG_GENERAL_SCALE))) { if ( m[10] == 1.0f && m[14] == 0.0f ) matrix->type = COGL_MATRIX_TYPE_2D_NO_ROT; else matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; } else if (TEST_MAT_FLAGS (matrix, MAT_FLAGS_3D)) { if ( m[ 8]==0.0f && m[ 9]==0.0f && m[2]==0.0f && m[6]==0.0f && m[10]==1.0f && m[14]==0.0f) { matrix->type = COGL_MATRIX_TYPE_2D; } else matrix->type = COGL_MATRIX_TYPE_3D; } else if ( m[4]==0.0f && m[12]==0.0f && m[1]==0.0f && m[13]==0.0f && m[2]==0.0f && m[6]==0.0f && m[3]==0.0f && m[7]==0.0f && m[11]==-1.0f && m[15]==0.0f) { matrix->type = COGL_MATRIX_TYPE_PERSPECTIVE; } else matrix->type = COGL_MATRIX_TYPE_GENERAL; } /* * Analyze and update the type and flags of a matrix. * * If the matrix type is dirty then calls either analyse_from_scratch() or * analyse_from_flags() to determine its type, according to whether the flags * are dirty or not, respectively. If the matrix has an inverse and it's dirty * then calls matrix_invert(). Finally clears the dirty flags. */ static void _cogl_matrix_update_type_and_flags (CoglMatrix *matrix) { if (matrix->flags & MAT_DIRTY_TYPE) { if (matrix->flags & MAT_DIRTY_FLAGS) analyse_from_scratch (matrix); else analyse_from_flags (matrix); } matrix->flags &= ~(MAT_DIRTY_FLAGS | MAT_DIRTY_TYPE); } /* * Compute inverse of a transformation matrix. * * @mat pointer to a CoglMatrix structure. The matrix inverse will be * stored in the CoglMatrix::inv attribute. * * Returns: %TRUE for success, %FALSE for failure (\p singular matrix). * * Calls the matrix inversion function in inv_mat_tab corresponding to the * given matrix type. In case of failure, updates the MAT_FLAG_SINGULAR flag, * and copies the identity matrix into CoglMatrix::inv. */ static gboolean _cogl_matrix_update_inverse (CoglMatrix *matrix) { if (matrix->flags & MAT_DIRTY_FLAGS || matrix->flags & MAT_DIRTY_INVERSE) { _cogl_matrix_update_type_and_flags (matrix); if (inv_mat_tab[matrix->type](matrix)) matrix->flags &= ~MAT_FLAG_SINGULAR; else { matrix->flags |= MAT_FLAG_SINGULAR; memcpy (matrix->inv, identity, 16 * sizeof (float)); } matrix->flags &= ~MAT_DIRTY_INVERSE; } if (matrix->flags & MAT_FLAG_SINGULAR) return FALSE; else return TRUE; } gboolean cogl_matrix_get_inverse (const CoglMatrix *matrix, CoglMatrix *inverse) { if (_cogl_matrix_update_inverse ((CoglMatrix *)matrix)) { cogl_matrix_init_from_array (inverse, matrix->inv); return TRUE; } else { cogl_matrix_init_identity (inverse); return FALSE; } } /* * Generate a 4x4 transformation matrix from glRotate parameters, and * post-multiply the input matrix by it. * * \author * This function was contributed by Erich Boleyn (erich@uruk.org). * Optimizations contributed by Rudolf Opalla (rudi@khm.de). */ static void _cogl_matrix_rotate (CoglMatrix *matrix, float angle, float x, float y, float z) { float xx, yy, zz, xy, yz, zx, xs, ys, zs, one_c, s, c; float m[16]; gboolean optimized; s = sinf (angle * DEG2RAD); c = cosf (angle * DEG2RAD); memcpy (m, identity, 16 * sizeof (float)); optimized = FALSE; #define M(row,col) m[col*4+row] if (x == 0.0f) { if (y == 0.0f) { if (z != 0.0f) { optimized = TRUE; /* rotate only around z-axis */ M (0,0) = c; M (1,1) = c; if (z < 0.0f) { M (0,1) = s; M (1,0) = -s; } else { M (0,1) = -s; M (1,0) = s; } } } else if (z == 0.0f) { optimized = TRUE; /* rotate only around y-axis */ M (0,0) = c; M (2,2) = c; if (y < 0.0f) { M (0,2) = -s; M (2,0) = s; } else { M (0,2) = s; M (2,0) = -s; } } } else if (y == 0.0f) { if (z == 0.0f) { optimized = TRUE; /* rotate only around x-axis */ M (1,1) = c; M (2,2) = c; if (x < 0.0f) { M (1,2) = s; M (2,1) = -s; } else { M (1,2) = -s; M (2,1) = s; } } } if (!optimized) { const float mag = sqrtf (x * x + y * y + z * z); if (mag <= 1.0e-4) { /* no rotation, leave mat as-is */ return; } x /= mag; y /= mag; z /= mag; /* * Arbitrary axis rotation matrix. * * This is composed of 5 matrices, Rz, Ry, T, Ry', Rz', multiplied * like so: Rz * Ry * T * Ry' * Rz'. T is the final rotation * (which is about the X-axis), and the two composite transforms * Ry' * Rz' and Rz * Ry are (respectively) the rotations necessary * from the arbitrary axis to the X-axis then back. They are * all elementary rotations. * * Rz' is a rotation about the Z-axis, to bring the axis vector * into the x-z plane. Then Ry' is applied, rotating about the * Y-axis to bring the axis vector parallel with the X-axis. The * rotation about the X-axis is then performed. Ry and Rz are * simply the respective inverse transforms to bring the arbitrary * axis back to it's original orientation. The first transforms * Rz' and Ry' are considered inverses, since the data from the * arbitrary axis gives you info on how to get to it, not how * to get away from it, and an inverse must be applied. * * The basic calculation used is to recognize that the arbitrary * axis vector (x, y, z), since it is of unit length, actually * represents the sines and cosines of the angles to rotate the * X-axis to the same orientation, with theta being the angle about * Z and phi the angle about Y (in the order described above) * as follows: * * cos ( theta ) = x / sqrt ( 1 - z^2 ) * sin ( theta ) = y / sqrt ( 1 - z^2 ) * * cos ( phi ) = sqrt ( 1 - z^2 ) * sin ( phi ) = z * * Note that cos ( phi ) can further be inserted to the above * formulas: * * cos ( theta ) = x / cos ( phi ) * sin ( theta ) = y / sin ( phi ) * * ...etc. Because of those relations and the standard trigonometric * relations, it is pssible to reduce the transforms down to what * is used below. It may be that any primary axis chosen will give the * same results (modulo a sign convention) using thie method. * * Particularly nice is to notice that all divisions that might * have caused trouble when parallel to certain planes or * axis go away with care paid to reducing the expressions. * After checking, it does perform correctly under all cases, since * in all the cases of division where the denominator would have * been zero, the numerator would have been zero as well, giving * the expected result. */ xx = x * x; yy = y * y; zz = z * z; xy = x * y; yz = y * z; zx = z * x; xs = x * s; ys = y * s; zs = z * s; one_c = 1.0f - c; /* We already hold the identity-matrix so we can skip some statements */ M (0,0) = (one_c * xx) + c; M (0,1) = (one_c * xy) - zs; M (0,2) = (one_c * zx) + ys; /* M (0,3) = 0.0f; */ M (1,0) = (one_c * xy) + zs; M (1,1) = (one_c * yy) + c; M (1,2) = (one_c * yz) - xs; /* M (1,3) = 0.0f; */ M (2,0) = (one_c * zx) - ys; M (2,1) = (one_c * yz) + xs; M (2,2) = (one_c * zz) + c; /* M (2,3) = 0.0f; */ /* M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = 0.0f; M (3,3) = 1.0f; */ } #undef M matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_ROTATION); } void cogl_matrix_rotate (CoglMatrix *matrix, float angle, float x, float y, float z) { _cogl_matrix_rotate (matrix, angle, x, y, z); _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_matrix_rotate_euler (CoglMatrix *matrix, const graphene_euler_t *euler) { CoglMatrix rotation_transform; cogl_matrix_init_from_euler (&rotation_transform, euler); cogl_matrix_multiply (matrix, matrix, &rotation_transform); } /* * Apply a perspective projection matrix. * * Creates the projection matrix and multiplies it with matrix, marking the * MAT_FLAG_PERSPECTIVE flag. */ static void _cogl_matrix_frustum (CoglMatrix *matrix, float left, float right, float bottom, float top, float nearval, float farval) { float x, y, a, b, c, d; float m[16]; x = (2.0f * nearval) / (right - left); y = (2.0f * nearval) / (top - bottom); a = (right + left) / (right - left); b = (top + bottom) / (top - bottom); c = -(farval + nearval) / ( farval - nearval); d = -(2.0f * farval * nearval) / (farval - nearval); /* error? */ #define M(row,col) m[col*4+row] M (0,0) = x; M (0,1) = 0.0f; M (0,2) = a; M (0,3) = 0.0f; M (1,0) = 0.0f; M (1,1) = y; M (1,2) = b; M (1,3) = 0.0f; M (2,0) = 0.0f; M (2,1) = 0.0f; M (2,2) = c; M (2,3) = d; M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = -1.0f; M (3,3) = 0.0f; #undef M matrix_multiply_array_with_flags (matrix, m, MAT_FLAG_PERSPECTIVE); } void cogl_matrix_frustum (CoglMatrix *matrix, float left, float right, float bottom, float top, float z_near, float z_far) { _cogl_matrix_frustum (matrix, left, right, bottom, top, z_near, z_far); _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_matrix_perspective (CoglMatrix *matrix, float fov_y, float aspect, float z_near, float z_far) { float ymax = z_near * tan (fov_y * G_PI / 360.0); cogl_matrix_frustum (matrix, -ymax * aspect, /* left */ ymax * aspect, /* right */ -ymax, /* bottom */ ymax, /* top */ z_near, z_far); _COGL_MATRIX_DEBUG_PRINT (matrix); } /* * Apply an orthographic projection matrix. * * Creates the projection matrix and multiplies it with matrix, marking the * MAT_FLAG_GENERAL_SCALE and MAT_FLAG_TRANSLATION flags. */ static void _cogl_matrix_orthographic (CoglMatrix *matrix, float x_1, float y_1, float x_2, float y_2, float nearval, float farval) { float m[16]; #define M(row, col) m[col * 4 + row] M (0,0) = 2.0f / (x_2 - x_1); M (0,1) = 0.0f; M (0,2) = 0.0f; M (0,3) = -(x_2 + x_1) / (x_2 - x_1); M (1,0) = 0.0f; M (1,1) = 2.0f / (y_1 - y_2); M (1,2) = 0.0f; M (1,3) = -(y_1 + y_2) / (y_1 - y_2); M (2,0) = 0.0f; M (2,1) = 0.0f; M (2,2) = -2.0f / (farval - nearval); M (2,3) = -(farval + nearval) / (farval - nearval); M (3,0) = 0.0f; M (3,1) = 0.0f; M (3,2) = 0.0f; M (3,3) = 1.0f; #undef M matrix_multiply_array_with_flags (matrix, m, (MAT_FLAG_GENERAL_SCALE | MAT_FLAG_TRANSLATION)); } void cogl_matrix_orthographic (CoglMatrix *matrix, float x_1, float y_1, float x_2, float y_2, float near, float far) { _cogl_matrix_orthographic (matrix, x_1, y_1, x_2, y_2, near, far); _COGL_MATRIX_DEBUG_PRINT (matrix); } /* * Multiply a matrix with a general scaling matrix. * * Multiplies in-place the elements of matrix by the scale factors. Checks if * the scales factors are roughly the same, marking the MAT_FLAG_UNIFORM_SCALE * flag, or MAT_FLAG_GENERAL_SCALE. Marks the MAT_DIRTY_TYPE and * MAT_DIRTY_INVERSE dirty flags. */ static void _cogl_matrix_scale (CoglMatrix *matrix, float x, float y, float z) { float *m = (float *)matrix; m[0] *= x; m[4] *= y; m[8] *= z; m[1] *= x; m[5] *= y; m[9] *= z; m[2] *= x; m[6] *= y; m[10] *= z; m[3] *= x; m[7] *= y; m[11] *= z; if (fabsf (x - y) < 1e-8 && fabsf (x - z) < 1e-8) matrix->flags |= MAT_FLAG_UNIFORM_SCALE; else matrix->flags |= MAT_FLAG_GENERAL_SCALE; matrix->flags |= (MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); } void cogl_matrix_scale (CoglMatrix *matrix, float sx, float sy, float sz) { _cogl_matrix_scale (matrix, sx, sy, sz); _COGL_MATRIX_DEBUG_PRINT (matrix); } /* * Multiply a matrix with a translation matrix. * * Adds the translation coordinates to the elements of matrix in-place. Marks * the MAT_FLAG_TRANSLATION flag, and the MAT_DIRTY_TYPE and MAT_DIRTY_INVERSE * dirty flags. */ static void _cogl_matrix_translate (CoglMatrix *matrix, float x, float y, float z) { float *m = (float *)matrix; m[12] = m[0] * x + m[4] * y + m[8] * z + m[12]; m[13] = m[1] * x + m[5] * y + m[9] * z + m[13]; m[14] = m[2] * x + m[6] * y + m[10] * z + m[14]; m[15] = m[3] * x + m[7] * y + m[11] * z + m[15]; matrix->flags |= (MAT_FLAG_TRANSLATION | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); } void cogl_matrix_translate (CoglMatrix *matrix, float x, float y, float z) { _cogl_matrix_translate (matrix, x, y, z); _COGL_MATRIX_DEBUG_PRINT (matrix); } #if 0 /* * Set matrix to do viewport and depthrange mapping. * Transforms Normalized Device Coords to window/Z values. */ static void _cogl_matrix_viewport (CoglMatrix *matrix, float x, float y, float width, float height, float zNear, float zFar, float depthMax) { float *m = (float *)matrix; m[MAT_SX] = width / 2.0f; m[MAT_TX] = m[MAT_SX] + x; m[MAT_SY] = height / 2.0f; m[MAT_TY] = m[MAT_SY] + y; m[MAT_SZ] = depthMax * ((zFar - zNear) / 2.0f); m[MAT_TZ] = depthMax * ((zFar - zNear) / 2.0f + zNear); matrix->flags = MAT_FLAG_GENERAL_SCALE | MAT_FLAG_TRANSLATION; matrix->type = COGL_MATRIX_TYPE_3D_NO_ROT; } #endif /* * Set a matrix to the identity matrix. * * @mat matrix. * * Copies ::identity into \p CoglMatrix::m, and into CoglMatrix::inv if * not NULL. Sets the matrix type to identity, resets the flags. It * doesn't initialize the inverse matrix, it just marks it dirty. */ static void _cogl_matrix_init_identity (CoglMatrix *matrix) { memcpy (matrix, identity, 16 * sizeof (float)); matrix->type = COGL_MATRIX_TYPE_IDENTITY; matrix->flags = MAT_DIRTY_INVERSE; } void cogl_matrix_init_identity (CoglMatrix *matrix) { _cogl_matrix_init_identity (matrix); _COGL_MATRIX_DEBUG_PRINT (matrix); } /* * Set a matrix to the (tx, ty, tz) translation matrix. * * @matix matrix. * @tx x coordinate of the translation vector * @ty y coordinate of the translation vector * @tz z coordinate of the translation vector */ static void _cogl_matrix_init_translation (CoglMatrix *matrix, float tx, float ty, float tz) { memcpy (matrix, identity, 16 * sizeof (float)); matrix->xw = tx; matrix->yw = ty; matrix->zw = tz; matrix->type = COGL_MATRIX_TYPE_3D; matrix->flags = MAT_FLAG_TRANSLATION | MAT_DIRTY_INVERSE; } void cogl_matrix_init_translation (CoglMatrix *matrix, float tx, float ty, float tz) { _cogl_matrix_init_translation (matrix, tx, ty, tz); _COGL_MATRIX_DEBUG_PRINT (matrix); } #if 0 /* * Test if the given matrix preserves vector lengths. */ static gboolean _cogl_matrix_is_length_preserving (const CoglMatrix *m) { return TEST_MAT_FLAGS (m, MAT_FLAGS_LENGTH_PRESERVING); } /* * Test if the given matrix does any rotation. * (or perhaps if the upper-left 3x3 is non-identity) */ static gboolean _cogl_matrix_has_rotation (const CoglMatrix *matrix) { if (matrix->flags & (MAT_FLAG_GENERAL | MAT_FLAG_ROTATION | MAT_FLAG_GENERAL_3D | MAT_FLAG_PERSPECTIVE)) return TRUE; else return FALSE; } static gboolean _cogl_matrix_is_general_scale (const CoglMatrix *matrix) { return (matrix->flags & MAT_FLAG_GENERAL_SCALE) ? TRUE : FALSE; } static gboolean _cogl_matrix_is_dirty (const CoglMatrix *matrix) { return (matrix->flags & MAT_DIRTY_ALL) ? TRUE : FALSE; } #endif /* * Loads a matrix array into CoglMatrix. * * @m matrix array. * @mat matrix. * * Copies \p m into CoglMatrix::m and marks the MAT_FLAG_GENERAL and * MAT_DIRTY_ALL * flags. */ static void _cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) { memcpy (matrix, array, 16 * sizeof (float)); matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); } void cogl_matrix_init_from_array (CoglMatrix *matrix, const float *array) { _cogl_matrix_init_from_array (matrix, array); _COGL_MATRIX_DEBUG_PRINT (matrix); } void _cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, const CoglMatrix *src) { memcpy (matrix, src, 16 * sizeof (float)); matrix->type = src->type; matrix->flags = src->flags | MAT_DIRTY_INVERSE; } void cogl_matrix_init_from_euler (CoglMatrix *matrix, const graphene_euler_t *euler) { /* Convert angles to radians */ float heading_rad = graphene_euler_get_y (euler) / 180.0f * G_PI; float pitch_rad = graphene_euler_get_x (euler) / 180.0f * G_PI; float roll_rad = graphene_euler_get_z (euler) / 180.0f * G_PI; /* Pre-calculate the sin and cos */ float sin_heading = sinf (heading_rad); float cos_heading = cosf (heading_rad); float sin_pitch = sinf (pitch_rad); float cos_pitch = cosf (pitch_rad); float sin_roll = sinf (roll_rad); float cos_roll = cosf (roll_rad); /* These calculations are based on the following website but they * use a different order for the rotations so it has been modified * slightly. * http://www.euclideanspace.com/maths/geometry/ * rotations/conversions/eulerToMatrix/index.htm */ /* Heading rotation x=0, y=1, z=0 gives: * * [ ch 0 sh 0 ] * [ 0 1 0 0 ] * [ -sh 0 ch 0 ] * [ 0 0 0 1 ] * * Pitch rotation x=1, y=0, z=0 gives: * [ 1 0 0 0 ] * [ 0 cp -sp 0 ] * [ 0 sp cp 0 ] * [ 0 0 0 1 ] * * Roll rotation x=0, y=0, z=1 gives: * [ cr -sr 0 0 ] * [ sr cr 0 0 ] * [ 0 0 1 0 ] * [ 0 0 0 1 ] * * Heading matrix * pitch matrix = * [ ch sh*sp cp*sh 0 ] * [ 0 cp -sp 0 ] * [ -sh ch*sp ch*cp 0 ] * [ 0 0 0 1 ] * * That matrix * roll matrix = * [ ch*cr + sh*sp*sr sh*sp*cr - ch*sr sh*cp 0 ] * [ cp*sr cp*cr -sp 0 ] * [ ch*sp*sr - sh*cr sh*sr + ch*sp*cr ch*cp 0 ] * [ 0 0 0 1 ] */ matrix->xx = cos_heading * cos_roll + sin_heading * sin_pitch * sin_roll; matrix->yx = cos_pitch * sin_roll; matrix->zx = cos_heading * sin_pitch * sin_roll - sin_heading * cos_roll; matrix->wx = 0.0f; matrix->xy = sin_heading * sin_pitch * cos_roll - cos_heading * sin_roll; matrix->yy = cos_pitch * cos_roll; matrix->zy = sin_heading * sin_roll + cos_heading * sin_pitch * cos_roll; matrix->wy = 0.0f; matrix->xz = sin_heading * cos_pitch; matrix->yz = -sin_pitch; matrix->zz = cos_heading * cos_pitch; matrix->wz = 0; matrix->xw = 0; matrix->yw = 0; matrix->zw = 0; matrix->ww = 1; matrix->flags = (MAT_FLAG_GENERAL | MAT_DIRTY_ALL); } /* * Transpose a float matrix. */ static void _cogl_matrix_util_transposef (float to[16], const float from[16]) { to[0] = from[0]; to[1] = from[4]; to[2] = from[8]; to[3] = from[12]; to[4] = from[1]; to[5] = from[5]; to[6] = from[9]; to[7] = from[13]; to[8] = from[2]; to[9] = from[6]; to[10] = from[10]; to[11] = from[14]; to[12] = from[3]; to[13] = from[7]; to[14] = from[11]; to[15] = from[15]; } void cogl_matrix_view_2d_in_frustum (CoglMatrix *matrix, float left, float right, float bottom, float top, float z_near, float z_2d, float width_2d, float height_2d) { float left_2d_plane = left / z_near * z_2d; float right_2d_plane = right / z_near * z_2d; float bottom_2d_plane = bottom / z_near * z_2d; float top_2d_plane = top / z_near * z_2d; float width_2d_start = right_2d_plane - left_2d_plane; float height_2d_start = top_2d_plane - bottom_2d_plane; /* Factors to scale from framebuffer geometry to frustum * cross-section geometry. */ float width_scale = width_2d_start / width_2d; float height_scale = height_2d_start / height_2d; cogl_matrix_translate (matrix, left_2d_plane, top_2d_plane, -z_2d); cogl_matrix_scale (matrix, width_scale, -height_scale, width_scale); } /* Assuming a symmetric perspective matrix is being used for your * projective transform this convenience function lets you compose a * view transform such that geometry on the z=0 plane will map to * screen coordinates with a top left origin of (0,0) and with the * given width and height. */ void cogl_matrix_view_2d_in_perspective (CoglMatrix *matrix, float fov_y, float aspect, float z_near, float z_2d, float width_2d, float height_2d) { float top = z_near * tan (fov_y * G_PI / 360.0); cogl_matrix_view_2d_in_frustum (matrix, -top * aspect, top * aspect, -top, top, z_near, z_2d, width_2d, height_2d); } gboolean cogl_matrix_equal (const void *v1, const void *v2) { const CoglMatrix *a = v1; const CoglMatrix *b = v2; g_return_val_if_fail (v1 != NULL, FALSE); g_return_val_if_fail (v2 != NULL, FALSE); /* We want to avoid having a fuzzy _equal() function (e.g. that uses * an arbitrary epsilon value) since this function noteably conforms * to the prototype suitable for use with g_hash_table_new() and a * fuzzy hash function isn't really appropriate for comparing hash * table keys since it's possible that you could end up fetching * different values if you end up with multiple similar keys in use * at the same time. If you consider that fuzzyness allows cases * such as A == B == C but A != C then you could also end up loosing * values in a hash table. * * We do at least use the == operator to compare values though so * that -0 is considered equal to 0. */ /* XXX: We don't compare the flags, inverse matrix or padding */ if (a->xx == b->xx && a->xy == b->xy && a->xz == b->xz && a->xw == b->xw && a->yx == b->yx && a->yy == b->yy && a->yz == b->yz && a->yw == b->yw && a->zx == b->zx && a->zy == b->zy && a->zz == b->zz && a->zw == b->zw && a->wx == b->wx && a->wy == b->wy && a->wz == b->wz && a->ww == b->ww) return TRUE; else return FALSE; } CoglMatrix * cogl_matrix_copy (const CoglMatrix *matrix) { if (G_LIKELY (matrix)) return g_slice_dup (CoglMatrix, matrix); return NULL; } void cogl_matrix_free (CoglMatrix *matrix) { g_slice_free (CoglMatrix, matrix); } const float * cogl_matrix_get_array (const CoglMatrix *matrix) { return (float *)matrix; } void cogl_matrix_transform_point (const CoglMatrix *matrix, float *x, float *y, float *z, float *w) { float _x = *x, _y = *y, _z = *z, _w = *w; *x = matrix->xx * _x + matrix->xy * _y + matrix->xz * _z + matrix->xw * _w; *y = matrix->yx * _x + matrix->yy * _y + matrix->yz * _z + matrix->yw * _w; *z = matrix->zx * _x + matrix->zy * _y + matrix->zz * _z + matrix->zw * _w; *w = matrix->wx * _x + matrix->wy * _y + matrix->wz * _z + matrix->ww * _w; } typedef struct _Point2f { float x; float y; } Point2f; typedef struct _Point3f { float x; float y; float z; } Point3f; typedef struct _Point4f { float x; float y; float z; float w; } Point4f; static void _cogl_matrix_transform_points_f2 (const CoglMatrix *matrix, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { int i; for (i = 0; i < n_points; i++) { Point2f p = *(Point2f *)((uint8_t *)points_in + i * stride_in); Point3f *o = (Point3f *)((uint8_t *)points_out + i * stride_out); o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xw; o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yw; o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zw; } } static void _cogl_matrix_project_points_f2 (const CoglMatrix *matrix, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { int i; for (i = 0; i < n_points; i++) { Point2f p = *(Point2f *)((uint8_t *)points_in + i * stride_in); Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xw; o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yw; o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zw; o->w = matrix->wx * p.x + matrix->wy * p.y + matrix->ww; } } static void _cogl_matrix_transform_points_f3 (const CoglMatrix *matrix, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { int i; for (i = 0; i < n_points; i++) { Point3f p = *(Point3f *)((uint8_t *)points_in + i * stride_in); Point3f *o = (Point3f *)((uint8_t *)points_out + i * stride_out); o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xz * p.z + matrix->xw; o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yz * p.z + matrix->yw; o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zz * p.z + matrix->zw; } } static void _cogl_matrix_project_points_f3 (const CoglMatrix *matrix, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { int i; for (i = 0; i < n_points; i++) { Point3f p = *(Point3f *)((uint8_t *)points_in + i * stride_in); Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xz * p.z + matrix->xw; o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yz * p.z + matrix->yw; o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zz * p.z + matrix->zw; o->w = matrix->wx * p.x + matrix->wy * p.y + matrix->wz * p.z + matrix->ww; } } static void _cogl_matrix_project_points_f4 (const CoglMatrix *matrix, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { int i; for (i = 0; i < n_points; i++) { Point4f p = *(Point4f *)((uint8_t *)points_in + i * stride_in); Point4f *o = (Point4f *)((uint8_t *)points_out + i * stride_out); o->x = matrix->xx * p.x + matrix->xy * p.y + matrix->xz * p.z + matrix->xw * p.w; o->y = matrix->yx * p.x + matrix->yy * p.y + matrix->yz * p.z + matrix->yw * p.w; o->z = matrix->zx * p.x + matrix->zy * p.y + matrix->zz * p.z + matrix->zw * p.w; o->w = matrix->wx * p.x + matrix->wy * p.y + matrix->wz * p.z + matrix->ww * p.w; } } void cogl_matrix_transform_points (const CoglMatrix *matrix, int n_components, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { /* The results of transforming always have three components... */ g_return_if_fail (stride_out >= sizeof (Point3f)); if (n_components == 2) _cogl_matrix_transform_points_f2 (matrix, stride_in, points_in, stride_out, points_out, n_points); else { g_return_if_fail (n_components == 3); _cogl_matrix_transform_points_f3 (matrix, stride_in, points_in, stride_out, points_out, n_points); } } void cogl_matrix_project_points (const CoglMatrix *matrix, int n_components, size_t stride_in, const void *points_in, size_t stride_out, void *points_out, int n_points) { if (n_components == 2) _cogl_matrix_project_points_f2 (matrix, stride_in, points_in, stride_out, points_out, n_points); else if (n_components == 3) _cogl_matrix_project_points_f3 (matrix, stride_in, points_in, stride_out, points_out, n_points); else { g_return_if_fail (n_components == 4); _cogl_matrix_project_points_f4 (matrix, stride_in, points_in, stride_out, points_out, n_points); } } gboolean cogl_matrix_is_identity (const CoglMatrix *matrix) { if (!(matrix->flags & MAT_DIRTY_TYPE) && matrix->type == COGL_MATRIX_TYPE_IDENTITY) return TRUE; else return memcmp (matrix, identity, sizeof (float) * 16) == 0; } void cogl_matrix_look_at (CoglMatrix *matrix, float eye_position_x, float eye_position_y, float eye_position_z, float object_x, float object_y, float object_z, float world_up_x, float world_up_y, float world_up_z) { CoglMatrix tmp; graphene_vec3_t forward; graphene_vec3_t side; graphene_vec3_t up; /* Get a unit viewing direction vector */ graphene_vec3_init (&forward, object_x - eye_position_x, object_y - eye_position_y, object_z - eye_position_z); graphene_vec3_normalize (&forward, &forward); graphene_vec3_init (&up, world_up_x, world_up_y, world_up_z); /* Take the sideways direction as being perpendicular to the viewing * direction and the word up vector. */ graphene_vec3_cross (&forward, &up, &side); graphene_vec3_normalize (&side, &side); /* Now we have unit sideways and forward-direction vectors calculate * a new mutually perpendicular up vector. */ graphene_vec3_cross (&side, &forward, &up); tmp.xx = graphene_vec3_get_x (&side); tmp.yx = graphene_vec3_get_y (&side); tmp.zx = graphene_vec3_get_z (&side); tmp.wx = 0; tmp.xy = graphene_vec3_get_x (&up); tmp.yy = graphene_vec3_get_y (&up); tmp.zy = graphene_vec3_get_z (&up); tmp.wy = 0; tmp.xz = -graphene_vec3_get_x (&forward); tmp.yz = -graphene_vec3_get_y (&forward); tmp.zz = -graphene_vec3_get_z (&forward); tmp.wz = 0; tmp.xw = 0; tmp.yw = 0; tmp.zw = 0; tmp.ww = 1; tmp.flags = (MAT_FLAG_GENERAL_3D | MAT_DIRTY_TYPE | MAT_DIRTY_INVERSE); cogl_matrix_translate (&tmp, -eye_position_x, -eye_position_y, -eye_position_z); cogl_matrix_multiply (matrix, matrix, &tmp); } void cogl_matrix_transpose (CoglMatrix *matrix) { float new_values[16]; /* We don't need to do anything if the matrix is the identity matrix */ if (!(matrix->flags & MAT_DIRTY_TYPE) && matrix->type == COGL_MATRIX_TYPE_IDENTITY) return; _cogl_matrix_util_transposef (new_values, cogl_matrix_get_array (matrix)); cogl_matrix_init_from_array (matrix, new_values); } GType cogl_gtype_matrix_get_type (void) { return cogl_matrix_get_gtype (); } muffin-6.4.1/cogl/cogl/cogl-xlib-renderer.c0000664000175000017500000004452114723361714017450 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-xlib-renderer.h" #include "cogl-util.h" #include "cogl-object.h" #include "cogl-output-private.h" #include "cogl-renderer-private.h" #include "cogl-xlib-renderer-private.h" #include "cogl-x11-renderer-private.h" #include "cogl-poll-private.h" #include "winsys/cogl-winsys-private.h" #include #include #include #include #include static char *_cogl_x11_display_name = NULL; static GList *_cogl_xlib_renderers = NULL; static void _xlib_renderer_data_free (CoglXlibRenderer *data) { if (data->xvisinfo) XFree (data->xvisinfo); g_slice_free (CoglXlibRenderer, data); } CoglXlibRenderer * _cogl_xlib_renderer_get_data (CoglRenderer *renderer) { /* Constructs a CoglXlibRenderer struct on demand and attaches it to the object using user data. It's done this way instead of using a subclassing hierarchy in the winsys data because all EGL winsys's need the EGL winsys data but only one of them wants the Xlib data. */ if (!renderer->custom_winsys_user_data) renderer->custom_winsys_user_data = g_slice_new0 (CoglXlibRenderer); return renderer->custom_winsys_user_data; } static void register_xlib_renderer (CoglRenderer *renderer) { GList *l; for (l = _cogl_xlib_renderers; l; l = l->next) if (l->data == renderer) return; _cogl_xlib_renderers = g_list_prepend (_cogl_xlib_renderers, renderer); } static void unregister_xlib_renderer (CoglRenderer *renderer) { _cogl_xlib_renderers = g_list_remove (_cogl_xlib_renderers, renderer); } static CoglRenderer * get_renderer_for_xdisplay (Display *xdpy) { GList *l; for (l = _cogl_xlib_renderers; l; l = l->next) { CoglRenderer *renderer = l->data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); if (xlib_renderer->xdpy == xdpy) return renderer; } return NULL; } static int error_handler (Display *xdpy, XErrorEvent *error) { CoglRenderer *renderer; CoglXlibRenderer *xlib_renderer; renderer = get_renderer_for_xdisplay (xdpy); xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_assert (xlib_renderer->trap_state); xlib_renderer->trap_state->trapped_error_code = error->error_code; return 0; } void _cogl_xlib_renderer_trap_errors (CoglRenderer *renderer, CoglXlibTrapState *state) { CoglXlibRenderer *xlib_renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); state->trapped_error_code = 0; state->old_error_handler = XSetErrorHandler (error_handler); state->old_state = xlib_renderer->trap_state; xlib_renderer->trap_state = state; } int _cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer, CoglXlibTrapState *state) { CoglXlibRenderer *xlib_renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_assert (state == xlib_renderer->trap_state); XSetErrorHandler (state->old_error_handler); xlib_renderer->trap_state = state->old_state; return state->trapped_error_code; } static Display * assert_xlib_display (CoglRenderer *renderer, GError **error) { Display *xdpy = cogl_xlib_renderer_get_foreign_display (renderer); CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); /* A foreign display may have already been set... */ if (xdpy) { xlib_renderer->xdpy = xdpy; return xdpy; } xdpy = XOpenDisplay (_cogl_x11_display_name); if (xdpy == NULL) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN, "Failed to open X Display %s", _cogl_x11_display_name); return NULL; } xlib_renderer->xdpy = xdpy; return xdpy; } static int compare_outputs (CoglOutput *a, CoglOutput *b) { return strcmp (a->name, b->name); } #define CSO(X) COGL_SUBPIXEL_ORDER_ ## X static CoglSubpixelOrder subpixel_map[6][6] = { { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* 0 */ { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB) }, /* 90 */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* 180 */ { CSO(UNKNOWN), CSO(NONE), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR) }, /* 270 */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_BGR), CSO(HORIZONTAL_RGB), CSO(VERTICAL_RGB), CSO(VERTICAL_BGR) }, /* Reflect_X */ { CSO(UNKNOWN), CSO(NONE), CSO(HORIZONTAL_RGB), CSO(HORIZONTAL_BGR), CSO(VERTICAL_BGR), CSO(VERTICAL_RGB) }, /* Reflect_Y */ }; #undef CSO static void update_outputs (CoglRenderer *renderer, gboolean notify) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); XRRScreenResources *resources; CoglXlibTrapState state; gboolean error = FALSE; GList *new_outputs = NULL; GList *l, *m; gboolean changed = FALSE; int i; xlib_renderer->outputs_update_serial = XNextRequest (xlib_renderer->xdpy); resources = XRRGetScreenResources (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy)); _cogl_xlib_renderer_trap_errors (renderer, &state); for (i = 0; resources && i < resources->ncrtc && !error; i++) { XRRCrtcInfo *crtc_info = NULL; XRROutputInfo *output_info = NULL; CoglOutput *output; float refresh_rate = 0; int j; crtc_info = XRRGetCrtcInfo (xlib_renderer->xdpy, resources, resources->crtcs[i]); if (crtc_info == NULL) { error = TRUE; goto next; } if (crtc_info->mode == None) goto next; for (j = 0; j < resources->nmode; j++) { if (resources->modes[j].id == crtc_info->mode) refresh_rate = (resources->modes[j].dotClock / ((float)resources->modes[j].hTotal * resources->modes[j].vTotal)); } output_info = XRRGetOutputInfo (xlib_renderer->xdpy, resources, crtc_info->outputs[0]); if (output_info == NULL) { error = TRUE; goto next; } output = _cogl_output_new (output_info->name); output->x = crtc_info->x; output->y = crtc_info->y; output->width = crtc_info->width; output->height = crtc_info->height; if ((crtc_info->rotation & (RR_Rotate_90 | RR_Rotate_270)) != 0) { output->mm_width = output_info->mm_height; output->mm_height = output_info->mm_width; } else { output->mm_width = output_info->mm_width; output->mm_height = output_info->mm_height; } output->refresh_rate = refresh_rate; switch (output_info->subpixel_order) { case SubPixelUnknown: default: output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; case SubPixelNone: output->subpixel_order = COGL_SUBPIXEL_ORDER_NONE; break; case SubPixelHorizontalRGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case SubPixelHorizontalBGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case SubPixelVerticalRGB: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case SubPixelVerticalBGR: output->subpixel_order = COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; } output->subpixel_order = COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; /* Handle the effect of rotation and reflection on subpixel order (ugh) */ for (j = 0; j < 6; j++) { if ((crtc_info->rotation & (1 << j)) != 0) output->subpixel_order = subpixel_map[j][output->subpixel_order]; } new_outputs = g_list_prepend (new_outputs, output); next: if (crtc_info != NULL) XFree (crtc_info); if (output_info != NULL) XFree (output_info); } XFree (resources); if (!error) { new_outputs = g_list_sort (new_outputs, (GCompareFunc)compare_outputs); l = new_outputs; m = renderer->outputs; while (l || m) { int cmp; CoglOutput *output_l = l ? (CoglOutput *)l->data : NULL; CoglOutput *output_m = m ? (CoglOutput *)m->data : NULL; if (l && m) cmp = compare_outputs (output_l, output_m); else if (l) cmp = -1; else cmp = 1; if (cmp == 0) { GList *m_next = m->next; if (!_cogl_output_values_equal (output_l, output_m)) { renderer->outputs = g_list_remove_link (renderer->outputs, m); renderer->outputs = g_list_insert_before (renderer->outputs, m_next, output_l); cogl_object_ref (output_l); changed = TRUE; } l = l->next; m = m_next; } else if (cmp < 0) { renderer->outputs = g_list_insert_before (renderer->outputs, m, output_l); cogl_object_ref (output_l); changed = TRUE; l = l->next; } else { GList *m_next = m->next; renderer->outputs = g_list_remove_link (renderer->outputs, m); changed = TRUE; m = m_next; } } } g_list_free_full (new_outputs, (GDestroyNotify)cogl_object_unref); _cogl_xlib_renderer_untrap_errors (renderer, &state); if (changed) { const CoglWinsysVtable *winsys = renderer->winsys_vtable; if (notify) COGL_NOTE (WINSYS, "Outputs changed:"); else COGL_NOTE (WINSYS, "Outputs:"); for (l = renderer->outputs; l; l = l->next) { CoglOutput *output = l->data; const char *subpixel_string; switch (output->subpixel_order) { case COGL_SUBPIXEL_ORDER_UNKNOWN: default: subpixel_string = "unknown"; break; case COGL_SUBPIXEL_ORDER_NONE: subpixel_string = "none"; break; case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: subpixel_string = "horizontal_rgb"; break; case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: subpixel_string = "horizontal_bgr"; break; case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: subpixel_string = "vertical_rgb"; break; case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: subpixel_string = "vertical_bgr"; break; } COGL_NOTE (WINSYS, " %10s: +%d+%dx%dx%d mm=%dx%d dpi=%.1fx%.1f " "subpixel_order=%s refresh_rate=%.3f", output->name, output->x, output->y, output->width, output->height, output->mm_width, output->mm_height, output->width / (output->mm_width / 25.4), output->height / (output->mm_height / 25.4), subpixel_string, output->refresh_rate); } if (notify && winsys->renderer_outputs_changed != NULL) winsys->renderer_outputs_changed (renderer); } } static CoglFilterReturn randr_filter (XEvent *event, void *data) { CoglRenderer *renderer = data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglX11Renderer *x11_renderer = (CoglX11Renderer *) xlib_renderer; if (x11_renderer->randr_base != -1 && (event->xany.type == x11_renderer->randr_base + RRScreenChangeNotify || event->xany.type == x11_renderer->randr_base + RRNotify) && event->xany.serial >= xlib_renderer->outputs_update_serial) update_outputs (renderer, TRUE); return COGL_FILTER_CONTINUE; } static int64_t prepare_xlib_events_timeout (void *user_data) { CoglRenderer *renderer = user_data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); return XPending (xlib_renderer->xdpy) ? 0 : -1; } static void dispatch_xlib_events (void *user_data, int revents) { CoglRenderer *renderer = user_data; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); if (renderer->xlib_enable_event_retrieval) while (XPending (xlib_renderer->xdpy)) { XEvent xevent; XNextEvent (xlib_renderer->xdpy, &xevent); cogl_xlib_renderer_handle_event (renderer, &xevent); } } gboolean _cogl_xlib_renderer_connect (CoglRenderer *renderer, GError **error) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglX11Renderer *x11_renderer = (CoglX11Renderer *) xlib_renderer; int damage_error; int randr_error; if (!assert_xlib_display (renderer, error)) return FALSE; if (getenv ("COGL_X11_SYNC")) XSynchronize (xlib_renderer->xdpy, TRUE); /* Check whether damage events are supported on this display */ if (!XDamageQueryExtension (xlib_renderer->xdpy, &x11_renderer->damage_base, &damage_error)) x11_renderer->damage_base = -1; /* Check whether randr is supported on this display */ if (!XRRQueryExtension (xlib_renderer->xdpy, &x11_renderer->randr_base, &randr_error)) x11_renderer->randr_base = -1; xlib_renderer->trap_state = NULL; if (renderer->xlib_enable_event_retrieval) { _cogl_poll_renderer_add_fd (renderer, ConnectionNumber (xlib_renderer->xdpy), COGL_POLL_FD_EVENT_IN, prepare_xlib_events_timeout, dispatch_xlib_events, renderer); } XRRSelectInput(xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask); update_outputs (renderer, FALSE); register_xlib_renderer (renderer); cogl_xlib_renderer_add_filter (renderer, randr_filter, renderer); return TRUE; } void _cogl_xlib_renderer_disconnect (CoglRenderer *renderer) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); g_list_free_full (renderer->outputs, (GDestroyNotify)cogl_object_unref); renderer->outputs = NULL; if (!renderer->foreign_xdpy && xlib_renderer->xdpy) XCloseDisplay (xlib_renderer->xdpy); g_clear_pointer (&renderer->custom_winsys_user_data, _xlib_renderer_data_free); unregister_xlib_renderer (renderer); } Display * cogl_xlib_renderer_get_display (CoglRenderer *renderer) { CoglXlibRenderer *xlib_renderer; g_return_val_if_fail (cogl_is_renderer (renderer), NULL); xlib_renderer = _cogl_xlib_renderer_get_data (renderer); return xlib_renderer->xdpy; } CoglFilterReturn cogl_xlib_renderer_handle_event (CoglRenderer *renderer, XEvent *event) { return _cogl_renderer_handle_native_event (renderer, event); } void cogl_xlib_renderer_add_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data) { _cogl_renderer_add_native_filter (renderer, (CoglNativeFilterFunc)func, data); } void cogl_xlib_renderer_remove_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data) { _cogl_renderer_remove_native_filter (renderer, (CoglNativeFilterFunc)func, data); } CoglOutput * _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, int x, int y, int width, int height) { int max_overlap = 0; CoglOutput *max_overlapped = NULL; GList *l; int xa1 = x, xa2 = x + width; int ya1 = y, ya2 = y + height; for (l = renderer->outputs; l; l = l->next) { CoglOutput *output = l->data; int xb1 = output->x, xb2 = output->x + output->width; int yb1 = output->y, yb2 = output->y + output->height; int overlap_x = MIN(xa2, xb2) - MAX(xa1, xb1); int overlap_y = MIN(ya2, yb2) - MAX(ya1, yb1); if (overlap_x > 0 && overlap_y > 0) { int overlap = overlap_x * overlap_y; if (overlap > max_overlap) { max_overlap = overlap; max_overlapped = output; } } } return max_overlapped; } muffin-6.4.1/cogl/cogl/cogl-xlib.h0000664000175000017500000000517414723361714015652 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_XLIB_H__ #define __COGL_XLIB_H__ #include /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_XLIB_H_INSIDE__ */ #ifndef __COGL_XLIB_H_INSIDE__ #define __COGL_XLIB_H_INSIDE__ #endif /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ #endif #endif /* COGL_COMPILATION */ #include #include #include /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ #undef __COGL_H_INSIDE__ #undef __COGL_XLIB_H_INSIDE__ #undef __COGL_XLIB_H_MUST_UNDEF_COGL_H_INSIDE__ #endif #endif /* __COGL_XLIB_H__ */ muffin-6.4.1/cogl/cogl/cogl-trace.c0000664000175000017500000001741214723361714016003 0ustar fabiofabio/* * Copyright 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "cogl-config.h" #include "cogl/cogl-trace.h" #ifdef HAVE_TRACING #include #include #include #include #include #include #define COGL_TRACE_OUTPUT_FILE "cogl-trace-sp-capture.syscap" #define BUFFER_LENGTH (4096 * 4) struct _CoglTraceContext { SysprofCaptureWriter *writer; }; typedef struct _CoglTraceThreadContext { int cpu_id; GPid pid; char *group; } CoglTraceThreadContext; typedef struct { int fd; char *filename; char *group; } TraceData; static void trace_data_free (gpointer user_data) { TraceData *data = user_data; data->fd = -1; g_clear_pointer (&data->group, g_free); g_clear_pointer (&data->filename, g_free); g_free (data); } static void cogl_trace_thread_context_free (gpointer data); GPrivate cogl_trace_thread_data = G_PRIVATE_INIT (cogl_trace_thread_context_free); CoglTraceContext *cogl_trace_context; GMutex cogl_trace_mutex; static CoglTraceContext * cogl_trace_context_new (int fd, const char *filename) { CoglTraceContext *context; SysprofCaptureWriter *writer; if (fd != -1) { g_debug ("Initializing trace context with fd=%d", fd); writer = sysprof_capture_writer_new_from_fd (fd, BUFFER_LENGTH); } else if (filename != NULL) { g_debug ("Initializing trace context with filename='%s'", filename); writer = sysprof_capture_writer_new (filename, BUFFER_LENGTH); } else { g_debug ("Initializing trace context with default filename"); writer = sysprof_capture_writer_new (COGL_TRACE_OUTPUT_FILE, BUFFER_LENGTH); } context = g_new0 (CoglTraceContext, 1); context->writer = writer; return context; } static void cogl_trace_context_free (CoglTraceContext *trace_context) { g_clear_pointer (&trace_context->writer, sysprof_capture_writer_unref); g_free (trace_context); } static void ensure_trace_context (TraceData *data) { g_mutex_lock (&cogl_trace_mutex); if (!cogl_trace_context) cogl_trace_context = cogl_trace_context_new (data->fd, data->filename); g_mutex_unlock (&cogl_trace_mutex); } static CoglTraceThreadContext * cogl_trace_thread_context_new (const char *group) { CoglTraceThreadContext *thread_context; pid_t tid; tid = (pid_t) syscall (SYS_gettid); thread_context = g_new0 (CoglTraceThreadContext, 1); thread_context->cpu_id = -1; thread_context->pid = getpid (); thread_context->group = group ? g_strdup (group) : g_strdup_printf ("t:%d", tid); return thread_context; } static gboolean enable_tracing_idle_callback (gpointer user_data) { CoglTraceThreadContext *thread_context = g_private_get (&cogl_trace_thread_data); TraceData *data = user_data; ensure_trace_context (data); if (thread_context) { g_warning ("Tracing already enabled"); return G_SOURCE_REMOVE; } thread_context = cogl_trace_thread_context_new (data->group); g_private_set (&cogl_trace_thread_data, thread_context); return G_SOURCE_REMOVE; } static void cogl_trace_thread_context_free (gpointer data) { CoglTraceThreadContext *thread_context = data; if (!thread_context) return; g_free (thread_context->group); g_free (thread_context); } static gboolean disable_tracing_idle_callback (gpointer user_data) { CoglTraceThreadContext *thread_context = g_private_get (&cogl_trace_thread_data); CoglTraceContext *trace_context; if (!thread_context) { g_warning ("Tracing not enabled"); return G_SOURCE_REMOVE; } g_private_replace (&cogl_trace_thread_data, NULL); g_mutex_lock (&cogl_trace_mutex); trace_context = cogl_trace_context; sysprof_capture_writer_flush (trace_context->writer); g_clear_pointer (&cogl_trace_context, cogl_trace_context_free); g_mutex_unlock (&cogl_trace_mutex); return G_SOURCE_REMOVE; } static void set_tracing_enabled_on_thread (GMainContext *main_context, const char *group, int fd, const char *filename) { TraceData *data; GSource *source; data = g_new0 (TraceData, 1); data->fd = fd; data->group = group ? strdup (group) : NULL; data->filename = filename ? strdup (filename) : NULL; source = g_idle_source_new (); g_source_set_callback (source, enable_tracing_idle_callback, data, trace_data_free); g_source_attach (source, main_context); g_source_unref (source); } void cogl_set_tracing_enabled_on_thread_with_fd (GMainContext *main_context, const char *group, int fd) { set_tracing_enabled_on_thread (main_context, group, fd, NULL); } void cogl_set_tracing_enabled_on_thread (GMainContext *main_context, const char *group, const char *filename) { set_tracing_enabled_on_thread (main_context, group, -1, filename); } void cogl_set_tracing_disabled_on_thread (GMainContext *main_context) { GSource *source; source = g_idle_source_new (); g_source_set_callback (source, disable_tracing_idle_callback, NULL, NULL); g_source_attach (source, main_context); g_source_unref (source); } void cogl_trace_end (CoglTraceHead *head) { SysprofTimeStamp end_time; CoglTraceContext *trace_context; CoglTraceThreadContext *trace_thread_context; end_time = g_get_monotonic_time () * 1000; trace_context = cogl_trace_context; trace_thread_context = g_private_get (&cogl_trace_thread_data); g_mutex_lock (&cogl_trace_mutex); if (!sysprof_capture_writer_add_mark (trace_context->writer, head->begin_time, trace_thread_context->cpu_id, trace_thread_context->pid, (uint64_t) end_time - head->begin_time, trace_thread_context->group, head->name, NULL)) { /* XXX: g_main_context_get_thread_default() might be wrong, it probably * needs to store the GMainContext in CoglTraceThreadContext when creating * and use it here. */ if (errno == EPIPE) cogl_set_tracing_disabled_on_thread (g_main_context_get_thread_default ()); } g_mutex_unlock (&cogl_trace_mutex); } #else #include #include void cogl_set_tracing_enabled_on_thread_with_fd (void *data, const char *group, int fd) { fprintf (stderr, "Tracing not enabled"); } void cogl_set_tracing_enabled_on_thread (void *data, const char *group, const char *filename) { fprintf (stderr, "Tracing not enabled"); } void cogl_set_tracing_disabled_on_thread (void *data) { fprintf (stderr, "Tracing not enabled"); } #endif /* HAVE_TRACING */ muffin-6.4.1/cogl/cogl/cogl-fence.h0000664000175000017500000001105114723361714015763 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_FENCE_H__ #define __COGL_FENCE_H__ #include #include /** * SECTION:cogl-fence * @short_description: Functions for notification of command completion * * Cogl allows notification of GPU command completion; users may mark * points in the GPU command stream and receive notification when the GPU * has executed to that point. */ /** * CoglFence: * * An opaque object representing a fence. This type is currently * unused but in the future may be used to pass extra information * about the fence completion. * * Since: 2.0 * Stability: Unstable */ typedef struct _CoglFence CoglFence; /** * CoglFenceCallback: * @fence: Unused. In the future this parameter may be used to pass * extra information about the fence completion but for now it * should be ignored. * @user_data: The private data passed to cogl_framebuffer_add_fence_callback() * * The callback prototype used with * cogl_framebuffer_add_fence_callback() for notification of GPU * command completion. * * Since: 2.0 * Stability: Unstable */ typedef void (* CoglFenceCallback) (CoglFence *fence, void *user_data); /** * CoglFenceClosure: * * An opaque type representing one future callback to be made when the * GPU command stream has passed a certain point. * * Since: 2.0 * Stability: Unstable */ typedef struct _CoglFenceClosure CoglFenceClosure; /** * cogl_frame_closure_get_user_data: * @closure: A #CoglFenceClosure returned from cogl_framebuffer_add_fence() * * Returns the user_data submitted to cogl_framebuffer_add_fence() which * returned a given #CoglFenceClosure. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void * cogl_fence_closure_get_user_data (CoglFenceClosure *closure); /** * cogl_framebuffer_add_fence_callback: * @framebuffer: The #CoglFramebuffer the commands have been submitted to * @callback: (scope notified): A #CoglFenceCallback to be called when * all commands submitted to Cogl have been executed * @user_data: (closure): Private data that will be passed to the callback * * Calls the provided callback when all previously-submitted commands have * been executed by the GPU. * * Returns non-NULL if the fence succeeded, or %NULL if it was unable to * be inserted and the callback will never be called. The user does not * need to free the closure; it will be freed automatically when the * callback is called, or cancelled. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglFenceClosure * cogl_framebuffer_add_fence_callback (CoglFramebuffer *framebuffer, CoglFenceCallback callback, void *user_data); /** * cogl_framebuffer_cancel_fence_callback: * @framebuffer: The #CoglFramebuffer the commands were submitted to * @closure: The #CoglFenceClosure returned from * cogl_framebuffer_add_fence_callback() * * Removes a fence previously submitted with * cogl_framebuffer_add_fence_callback(); the callback will not be * called. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_framebuffer_cancel_fence_callback (CoglFramebuffer *framebuffer, CoglFenceClosure *closure); #endif /* __COGL_FENCE_H__ */ muffin-6.4.1/cogl/cogl/cogl-gtype.c0000664000175000017500000000670514723361714016040 0ustar fabiofabio#include "cogl-gtype-private.h" #include void _cogl_gtype_object_init_value (GValue *value) { value->data[0].v_pointer = NULL; } void _cogl_gtype_object_free_value (GValue *value) { if (value->data[0].v_pointer != NULL) cogl_object_unref (value->data[0].v_pointer); } void _cogl_gtype_object_copy_value (const GValue *src, GValue *dst) { if (src->data[0].v_pointer != NULL) dst->data[0].v_pointer = cogl_object_ref (src->data[0].v_pointer); else dst->data[0].v_pointer = NULL; } gpointer _cogl_gtype_object_peek_pointer (const GValue *value) { return value->data[0].v_pointer; } gchar * _cogl_gtype_object_collect_value (GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { CoglObject *object; object = collect_values[0].v_pointer; if (object == NULL) { value->data[0].v_pointer = NULL; return NULL; } if (object->klass == NULL) return g_strconcat ("invalid unclassed CoglObject pointer for " "value type '", G_VALUE_TYPE_NAME (value), "'", NULL); value->data[0].v_pointer = cogl_object_ref (object); return NULL; } gchar * _cogl_gtype_object_lcopy_value (const GValue *value, guint n_collect_values, GTypeCValue *collect_values, guint collect_flags) { CoglObject **object_p = collect_values[0].v_pointer; if (object_p == NULL) return g_strconcat ("value location for '", G_VALUE_TYPE_NAME (value), "' passed as NULL", NULL); if (value->data[0].v_pointer == NULL) *object_p = NULL; else if (collect_flags & G_VALUE_NOCOPY_CONTENTS) *object_p = value->data[0].v_pointer; else *object_p = cogl_object_ref (value->data[0].v_pointer); return NULL; } void _cogl_gtype_object_class_base_init (CoglObjectClass *klass) { } void _cogl_gtype_object_class_base_finalize (CoglObjectClass *klass) { } void _cogl_gtype_object_class_init (CoglObjectClass *klass) { } void _cogl_gtype_object_init (CoglObject *object) { } void _cogl_gtype_dummy_iface_init (gpointer iface) { } /** * cogl_object_value_set_object: * @value: a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT * @object: (type Cogl.GtypeObject) (allow-none): a #CoglGtypeObject, or %NULL * * Sets the contents of a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT. * */ void cogl_object_value_set_object (GValue *value, gpointer object) { CoglObject *old_object; old_object = value->data[0].v_pointer; if (object != NULL) { /* take over ownership */ value->data[0].v_pointer = object; } else value->data[0].v_pointer = NULL; if (old_object != NULL) cogl_object_unref (old_object); } /** * cogl_object_value_get_object: * @value: a #GValue initialized with %COGL_GTYPE_TYPE_OBJECT * * Retrieves a pointer to the #CoglGtypeObject contained inside * the passed #GValue. * * Return value: (transfer none) (type Cogl.GtypeObject): a pointer to * a #CoglGtypeObject, or %NULL */ gpointer cogl_object_value_get_object (const GValue *value) { return value->data[0].v_pointer; } muffin-6.4.1/cogl/cogl/cogl-primitive.h0000664000175000017500000010231114723361714016713 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PRIMITIVE_H__ #define __COGL_PRIMITIVE_H__ /* We forward declare the CoglPrimitive type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglPrimitive CoglPrimitive; #include /* for CoglVerticesMode */ #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-primitive * @short_description: Functions for creating, manipulating and drawing * primitives * * FIXME */ /** * CoglPrimitive: (ref-func cogl_object_ref) (unref-func cogl_object_unref) * (set-value-func cogl_object_value_set_object) * (get-value-func cogl_object_value_get_object) */ /** * cogl_primitive_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_primitive_get_gtype (void); /** * CoglVertexP2: * @x: The x component of a position attribute * @y: The y component of a position attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p2(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y; } CoglVertexP2; /** * CoglVertexP3: * @x: The x component of a position attribute * @y: The y component of a position attribute * @z: The z component of a position attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p3(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y, z; } CoglVertexP3; /** * CoglVertexP2C4: * @x: The x component of a position attribute * @y: The y component of a position attribute * @r: The red component of a color attribute * @b: The green component of a color attribute * @g: The blue component of a color attribute * @a: The alpha component of a color attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p2c4(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y; uint8_t r, g, b, a; } CoglVertexP2C4; /** * CoglVertexP3C4: * @x: The x component of a position attribute * @y: The y component of a position attribute * @z: The z component of a position attribute * @r: The red component of a color attribute * @b: The green component of a color attribute * @g: The blue component of a color attribute * @a: The alpha component of a color attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p3c4(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y, z; uint8_t r, g, b, a; } CoglVertexP3C4; /** * CoglVertexP2T2: * @x: The x component of a position attribute * @y: The y component of a position attribute * @s: The s component of a texture coordinate attribute * @t: The t component of a texture coordinate attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p2t2(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y; float s, t; } CoglVertexP2T2; /** * CoglVertexP3T2: * @x: The x component of a position attribute * @y: The y component of a position attribute * @z: The z component of a position attribute * @s: The s component of a texture coordinate attribute * @t: The t component of a texture coordinate attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p3t2(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y, z; float s, t; } CoglVertexP3T2; /** * CoglVertexP2T2C4: * @x: The x component of a position attribute * @y: The y component of a position attribute * @s: The s component of a texture coordinate attribute * @t: The t component of a texture coordinate attribute * @r: The red component of a color attribute * @b: The green component of a color attribute * @g: The blue component of a color attribute * @a: The alpha component of a color attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p3t2c4(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y; float s, t; uint8_t r, g, b, a; } CoglVertexP2T2C4; /** * CoglVertexP3T2C4: * @x: The x component of a position attribute * @y: The y component of a position attribute * @z: The z component of a position attribute * @s: The s component of a texture coordinate attribute * @t: The t component of a texture coordinate attribute * @r: The red component of a color attribute * @b: The green component of a color attribute * @g: The blue component of a color attribute * @a: The alpha component of a color attribute * * A convenience vertex definition that can be used with * cogl_primitive_new_p3t2c4(). * * Since: 1.6 * Stability: Unstable */ typedef struct { float x, y, z; float s, t; uint8_t r, g, b, a; } CoglVertexP3T2C4; /** * cogl_primitive_new: * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to process when drawing * @...: A %NULL terminated list of attributes * * Combines a set of #CoglAttributes with a specific draw @mode * and defines a vertex count so a #CoglPrimitive object can be retained and * drawn later with no addition information required. * * The value passed as @n_vertices will simply update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * * Return value: (transfer full): A newly allocated #CoglPrimitive object * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new (CoglVerticesMode mode, int n_vertices, ...); /** * cogl_primitive_new_with_attributes: (skip) * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to process when drawing * @attributes: An array of CoglAttribute * @n_attributes: The number of attributes * * Combines a set of #CoglAttributes with a specific draw @mode * and defines a vertex count so a #CoglPrimitive object can be retained and * drawn later with no addition information required. * * The value passed as @n_vertices will simply update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * * Return value: (transfer full): A newly allocated #CoglPrimitive object * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_with_attributes (CoglVerticesMode mode, int n_vertices, CoglAttribute **attributes, int n_attributes); /** * cogl_primitive_new_p2: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP2): An array * of #CoglVertexP2 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position * attribute with a #CoglAttribute and upload your data. * * For example to draw a convex polygon you can do: * |[ * CoglVertexP2 triangle[] = * { * { 0, 300 }, * { 150, 0, }, * { 300, 300 } * }; * prim = cogl_primitive_new_p2 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p2 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP2 *data); /** * cogl_primitive_new_p3: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP3): An array of * #CoglVertexP3 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position * attribute with a #CoglAttribute and upload your data. * * For example to draw a convex polygon you can do: * |[ * CoglVertexP3 triangle[] = * { * { 0, 300, 0 }, * { 150, 0, 0 }, * { 300, 300, 0 } * }; * prim = cogl_primitive_new_p3 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p3 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP3 *data); /** * cogl_primitive_new_p2c4: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP2C4): An array * of #CoglVertexP2C4 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position * and color attributes with #CoglAttributes and upload * your data. * * For example to draw a convex polygon with a linear gradient you * can do: * |[ * CoglVertexP2C4 triangle[] = * { * { 0, 300, 0xff, 0x00, 0x00, 0xff }, * { 150, 0, 0x00, 0xff, 0x00, 0xff }, * { 300, 300, 0xff, 0x00, 0x00, 0xff } * }; * prim = cogl_primitive_new_p2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p2c4 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP2C4 *data); /** * cogl_primitive_new_p3c4: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP3C4): An array * of #CoglVertexP3C4 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position * and color attributes with #CoglAttributes and upload * your data. * * For example to draw a convex polygon with a linear gradient you * can do: * |[ * CoglVertexP3C4 triangle[] = * { * { 0, 300, 0, 0xff, 0x00, 0x00, 0xff }, * { 150, 0, 0, 0x00, 0xff, 0x00, 0xff }, * { 300, 300, 0, 0xff, 0x00, 0x00, 0xff } * }; * prim = cogl_primitive_new_p3c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p3c4 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP3C4 *data); /** * cogl_primitive_new_p2t2: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP2T2): An array * of #CoglVertexP2T2 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position and * texture coordinate attributes with #CoglAttributes and * upload your data. * * For example to draw a convex polygon with texture mapping you can * do: * |[ * CoglVertexP2T2 triangle[] = * { * { 0, 300, 0.0, 1.0}, * { 150, 0, 0.5, 0.0}, * { 300, 300, 1.0, 1.0} * }; * prim = cogl_primitive_new_p2t2 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p2t2 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP2T2 *data); /** * cogl_primitive_new_p3t2: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP3T2): An array * of #CoglVertexP3T2 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position and * texture coordinate attributes with #CoglAttributes and * upload your data. * * For example to draw a convex polygon with texture mapping you can * do: * |[ * CoglVertexP3T2 triangle[] = * { * { 0, 300, 0, 0.0, 1.0}, * { 150, 0, 0, 0.5, 0.0}, * { 300, 300, 0, 1.0, 1.0} * }; * prim = cogl_primitive_new_p3t2 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p3t2 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP3T2 *data); /** * cogl_primitive_new_p2t2c4: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP2T2C4): An * array of #CoglVertexP2T2C4 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position, texture * coordinate and color attributes with #CoglAttributes and * upload your data. * * For example to draw a convex polygon with texture mapping and a * linear gradient you can do: * |[ * CoglVertexP2T2C4 triangle[] = * { * { 0, 300, 0.0, 1.0, 0xff, 0x00, 0x00, 0xff}, * { 150, 0, 0.5, 0.0, 0x00, 0xff, 0x00, 0xff}, * { 300, 300, 1.0, 1.0, 0xff, 0x00, 0x00, 0xff} * }; * prim = cogl_primitive_new_p2t2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p2t2c4 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP2T2C4 *data); /** * cogl_primitive_new_p3t2c4: (skip) * @context: A #CoglContext * @mode: A #CoglVerticesMode defining how to draw the vertices * @n_vertices: The number of vertices to read from @data and also * the number of vertices to read when later drawing. * @data: (array length=n_vertices): (type Cogl.VertexP3T2C4): An * array of #CoglVertexP3T2C4 vertices * * Provides a convenient way to describe a primitive, such as a single * triangle strip or a triangle fan, that will internally allocate the * necessary #CoglAttributeBuffer storage, describe the position, texture * coordinate and color attributes with #CoglAttributes and * upload your data. * * For example to draw a convex polygon with texture mapping and a * linear gradient you can do: * |[ * CoglVertexP3T2C4 triangle[] = * { * { 0, 300, 0, 0.0, 1.0, 0xff, 0x00, 0x00, 0xff}, * { 150, 0, 0, 0.5, 0.0, 0x00, 0xff, 0x00, 0xff}, * { 300, 300, 0, 1.0, 1.0, 0xff, 0x00, 0x00, 0xff} * }; * prim = cogl_primitive_new_p3t2c4 (COGL_VERTICES_MODE_TRIANGLE_FAN, * 3, triangle); * cogl_primitive_draw (prim); * ]| * * The value passed as @n_vertices is initially used to determine how * much can be read from @data but it will also be used to update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to read when drawing. * The primitive API doesn't support drawing with sliced * textures (since switching between slices implies changing state and * so that implies multiple primitives need to be submitted). You * should pass the %COGL_TEXTURE_NO_SLICING flag to all textures that * might be used while drawing with this API. If your hardware doesn't * support non-power of two textures (For example you are using GLES * 1.1) then you will need to make sure your assets are resized to a * power-of-two size (though they don't have to be square) * * Return value: (transfer full): A newly allocated #CoglPrimitive * with a reference of 1. This can be freed using cogl_object_unref(). * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_new_p3t2c4 (CoglContext *context, CoglVerticesMode mode, int n_vertices, const CoglVertexP3T2C4 *data); COGL_EXPORT int cogl_primitive_get_first_vertex (CoglPrimitive *primitive); COGL_EXPORT void cogl_primitive_set_first_vertex (CoglPrimitive *primitive, int first_vertex); /** * cogl_primitive_get_n_vertices: * @primitive: A #CoglPrimitive object * * Queries the number of vertices to read when drawing the given * @primitive. Usually this value is implicitly set when associating * vertex data or indices with a #CoglPrimitive. * * If cogl_primitive_set_indices() has been used to associate a * sequence of #CoglIndices with the given @primitive then the * number of vertices to read can also be phrased as the number * of indices to read. * * To be clear; it doesn't refer to the number of vertices - in * terms of data - associated with the primitive it's just the number * of vertices to read and draw. * * Returns: The number of vertices to read when drawing. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_primitive_get_n_vertices (CoglPrimitive *primitive); /** * cogl_primitive_set_n_vertices: * @primitive: A #CoglPrimitive object * @n_vertices: The number of vertices to read when drawing. * * Specifies how many vertices should be read when drawing the given * @primitive. * * Usually this value is set implicitly when associating vertex data * or indices with a #CoglPrimitive. * * To be clear; it doesn't refer to the number of vertices - in * terms of data - associated with the primitive it's just the number * of vertices to read and draw. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_primitive_set_n_vertices (CoglPrimitive *primitive, int n_vertices); COGL_EXPORT CoglVerticesMode cogl_primitive_get_mode (CoglPrimitive *primitive); COGL_EXPORT void cogl_primitive_set_mode (CoglPrimitive *primitive, CoglVerticesMode mode); /** * cogl_primitive_set_attributes: (skip) * @primitive: A #CoglPrimitive object * @attributes: an array of #CoglAttribute pointers * @n_attributes: the number of elements in @attributes * * Replaces all the attributes of the given #CoglPrimitive object. * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT void cogl_primitive_set_attributes (CoglPrimitive *primitive, CoglAttribute **attributes, int n_attributes); /** * cogl_primitive_set_indices: (skip) * @primitive: A #CoglPrimitive * @indices: A #CoglIndices array * @n_indices: The number of indices to reference when drawing * * Associates a sequence of #CoglIndices with the given @primitive. * * #CoglIndices provide a way to virtualize your real vertex data by * providing a sequence of indices that index into your real vertex * data. The GPU will walk though the index values to indirectly * lookup the data for each vertex instead of sequentially walking * through the data directly. This lets you save memory by indexing * shared data multiple times instead of duplicating the data. * * The value passed as @n_indices will simply update the * #CoglPrimitive n_vertices property as if * cogl_primitive_set_n_vertices() were called. This property defines * the number of vertices to draw or, put another way, how many * indices should be read from @indices when drawing. * * The #CoglPrimitive first_vertex property * also affects drawing with indices by defining the first entry of the * indices to start drawing from. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_primitive_set_indices (CoglPrimitive *primitive, CoglIndices *indices, int n_indices); /** * cogl_primitive_get_indices: (skip) * @primitive: A #CoglPrimitive * * Return value: (transfer none): the indices that were set with * cogl_primitive_set_indices() or %NULL if no indices were set. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglIndices * cogl_primitive_get_indices (CoglPrimitive *primitive); /** * cogl_primitive_copy: * @primitive: A primitive copy * * Makes a copy of an existing #CoglPrimitive. Note that the primitive * is a shallow copy which means it will use the same attributes and * attribute buffers as the original primitive. * * Return value: (transfer full): the new primitive * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglPrimitive * cogl_primitive_copy (CoglPrimitive *primitive); /** * cogl_is_primitive: * @object: A #CoglObject * * Gets whether the given object references a #CoglPrimitive. * * Returns: %TRUE if the @object references a #CoglPrimitive, * %FALSE otherwise * * Since: 1.6 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_primitive (void *object); /** * CoglPrimitiveAttributeCallback: * @primitive: The #CoglPrimitive whose attributes are being iterated * @attribute: The #CoglAttribute * @user_data: The private data passed to cogl_primitive_foreach_attribute() * * The callback prototype used with cogl_primitive_foreach_attribute() * for iterating all the attributes of a #CoglPrimitive. * * The function should return TRUE to continue iteration or FALSE to * stop. * * Since: 1.10 * Stability: Unstable */ typedef gboolean (* CoglPrimitiveAttributeCallback) (CoglPrimitive *primitive, CoglAttribute *attribute, void *user_data); /** * cogl_primitive_foreach_attribute: * @primitive: A #CoglPrimitive object * @callback: (scope call): A #CoglPrimitiveAttributeCallback to be * called for each attribute * @user_data: (closure): Private data that will be passed to the * callback * * Iterates all the attributes of the given #CoglPrimitive. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_primitive_foreach_attribute (CoglPrimitive *primitive, CoglPrimitiveAttributeCallback callback, void *user_data); /** * cogl_primitive_draw: (skip) * @primitive: A #CoglPrimitive geometry object * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * * Draws the given @primitive geometry to the specified destination * @framebuffer using the graphics processing state described by @pipeline. * * This drawing api doesn't support high-level meta texture types such * as #CoglTexture2DSliced so it is the user's responsibility to * ensure that only low-level textures that can be directly sampled by * a GPU such as #CoglTexture2D are associated with layers of the given * @pipeline. * * Stability: unstable * Since: 1.16 */ COGL_EXPORT void cogl_primitive_draw (CoglPrimitive *primitive, CoglFramebuffer *framebuffer, CoglPipeline *pipeline); G_END_DECLS #endif /* __COGL_PRIMITIVE_H__ */ muffin-6.4.1/cogl/cogl/cogl-x11-renderer-private.h0000664000175000017500000000257714723361714020605 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_RENDERER_X11_PRIVATE_H #define __COGL_RENDERER_X11_PRIVATE_H typedef struct _CoglX11Renderer { int damage_base; int randr_base; } CoglX11Renderer; #endif /* __COGL_RENDERER_X11_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-primitives-private.h0000664000175000017500000000525514723361714020557 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PRIMITIVES_PRIVATE_H #define __COGL_PRIMITIVES_PRIVATE_H #include G_BEGIN_DECLS /* Draws a rectangle without going through the journal so that it will be flushed immediately. This should only be used in situations where the code may be called while the journal is already being flushed. In that case using the journal would go wrong */ void _cogl_rectangle_immediate (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2); void cogl_2d_primitives_immediate (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, const CoglVertexP2 *vertices, unsigned int n_vertices); typedef struct _CoglMultiTexturedRect { const float *position; /* x0,y0,x1,y1 */ const float *tex_coords; /* (tx0,ty0,tx1,ty1)(tx0,ty0,tx1,ty1)(... */ int tex_coords_len; /* number of floats in tex_coords? */ } CoglMultiTexturedRect; void _cogl_framebuffer_draw_multitextured_rectangles ( CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglMultiTexturedRect *rects, int n_rects); G_END_DECLS #endif /* __COGL_PRIMITIVES_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-poll-private.h0000664000175000017500000000537714723361714017337 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_POLL_PRIVATE_H__ #define __COGL_POLL_PRIVATE_H__ #include "cogl-poll.h" #include "cogl-renderer.h" #include "cogl-closure-list-private.h" void _cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd); typedef int64_t (*CoglPollPrepareCallback) (void *user_data); typedef void (*CoglPollDispatchCallback) (void *user_data, int revents); COGL_EXPORT void _cogl_poll_renderer_add_fd (CoglRenderer *renderer, int fd, CoglPollFDEvent events, CoglPollPrepareCallback prepare, CoglPollDispatchCallback dispatch, void *user_data); void _cogl_poll_renderer_modify_fd (CoglRenderer *renderer, int fd, CoglPollFDEvent events); typedef struct _CoglPollSource CoglPollSource; CoglPollSource * _cogl_poll_renderer_add_source (CoglRenderer *renderer, CoglPollPrepareCallback prepare, CoglPollDispatchCallback dispatch, void *user_data); void _cogl_poll_renderer_remove_source (CoglRenderer *renderer, CoglPollSource *source); typedef void (*CoglIdleCallback) (void *user_data); COGL_EXPORT CoglClosure * _cogl_poll_renderer_add_idle (CoglRenderer *renderer, CoglIdleCallback idle_cb, void *user_data, CoglUserDataDestroyCallback destroy_cb); #endif /* __COGL_POLL_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-buffer-private.h0000664000175000017500000001264214723361714017633 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ #ifndef __COGL_BUFFER_PRIVATE_H__ #define __COGL_BUFFER_PRIVATE_H__ #include #include "cogl-object-private.h" #include "cogl-buffer.h" #include "cogl-context.h" #include "cogl-gl-header.h" G_BEGIN_DECLS typedef struct _CoglBufferVtable CoglBufferVtable; struct _CoglBufferVtable { void * (* map_range) (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); void (* unmap) (CoglBuffer *buffer); gboolean (* set_data) (CoglBuffer *buffer, unsigned int offset, const void *data, unsigned int size, GError **error); }; typedef enum _CoglBufferFlags { COGL_BUFFER_FLAG_NONE = 0, COGL_BUFFER_FLAG_BUFFER_OBJECT = 1UL << 0, /* real openGL buffer object */ COGL_BUFFER_FLAG_MAPPED = 1UL << 1, COGL_BUFFER_FLAG_MAPPED_FALLBACK = 1UL << 2 } CoglBufferFlags; typedef enum { COGL_BUFFER_USAGE_HINT_TEXTURE, COGL_BUFFER_USAGE_HINT_ATTRIBUTE_BUFFER, COGL_BUFFER_USAGE_HINT_INDEX_BUFFER } CoglBufferUsageHint; typedef enum { COGL_BUFFER_BIND_TARGET_PIXEL_PACK, COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, COGL_BUFFER_BIND_TARGET_COUNT } CoglBufferBindTarget; struct _CoglBuffer { CoglObject _parent; CoglContext *context; CoglBufferVtable vtable; CoglBufferBindTarget last_target; CoglBufferFlags flags; GLuint gl_handle; /* OpenGL handle */ unsigned int size; /* size of the buffer, in bytes */ CoglBufferUsageHint usage_hint; CoglBufferUpdateHint update_hint; /* points to the mapped memory when the CoglBuffer is a VBO, PBO, * ... or points to allocated memory in the fallback paths */ uint8_t *data; int immutable_ref; unsigned int store_created:1; }; /* This is used to register a type to the list of handle types that will be considered a texture in cogl_is_texture() */ void _cogl_buffer_register_buffer_type (const CoglObjectClass *klass); #define COGL_BUFFER_DEFINE(TypeName, type_name) \ COGL_OBJECT_DEFINE_WITH_CODE \ (TypeName, type_name, \ _cogl_buffer_register_buffer_type (&_cogl_##type_name##_class)) void _cogl_buffer_initialize (CoglBuffer *buffer, CoglContext *context, size_t size, CoglBufferBindTarget default_target, CoglBufferUsageHint usage_hint, CoglBufferUpdateHint update_hint); void _cogl_buffer_fini (CoglBuffer *buffer); CoglBufferUsageHint _cogl_buffer_get_usage_hint (CoglBuffer *buffer); CoglBuffer * _cogl_buffer_immutable_ref (CoglBuffer *buffer); void _cogl_buffer_immutable_unref (CoglBuffer *buffer); gboolean _cogl_buffer_set_data (CoglBuffer *buffer, size_t offset, const void *data, size_t size, GError **error); void * _cogl_buffer_map (CoglBuffer *buffer, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); /* This is a wrapper around cogl_buffer_map_range for internal use when we want to map the buffer for write only to replace the entire contents. If the map fails then it will fallback to writing to a temporary buffer. When _cogl_buffer_unmap_for_fill_or_fallback is called the temporary buffer will be copied into the array. Note that these calls share a global array so they can not be nested. */ void * _cogl_buffer_map_range_for_fill_or_fallback (CoglBuffer *buffer, size_t offset, size_t size); COGL_EXPORT void * _cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer); COGL_EXPORT void _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer); G_END_DECLS #endif /* __COGL_BUFFER_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-debug.c0000664000175000017500000002362314723361714015774 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-i18n-private.h" #include "cogl-private.h" #include "cogl-debug.h" #include "cogl1-context.h" /* XXX: If you add a debug option, please also add an option * definition to cogl-debug-options.h. This will enable us - for * example - to emit a "help" description for the option. */ /* NB: Only these options get enabled if COGL_DEBUG=all is * used since they don't affect the behaviour of Cogl they * simply print out verbose information */ static const GDebugKey cogl_log_debug_keys[] = { { "object", COGL_DEBUG_OBJECT }, { "slicing", COGL_DEBUG_SLICING }, { "atlas", COGL_DEBUG_ATLAS }, { "blend-strings", COGL_DEBUG_BLEND_STRINGS }, { "journal", COGL_DEBUG_JOURNAL }, { "batching", COGL_DEBUG_BATCHING }, { "matrices", COGL_DEBUG_MATRICES }, { "draw", COGL_DEBUG_DRAW }, { "opengl", COGL_DEBUG_OPENGL }, { "pango", COGL_DEBUG_PANGO }, { "show-source", COGL_DEBUG_SHOW_SOURCE}, { "offscreen", COGL_DEBUG_OFFSCREEN }, { "texture-pixmap", COGL_DEBUG_TEXTURE_PIXMAP }, { "bitmap", COGL_DEBUG_BITMAP }, { "clipping", COGL_DEBUG_CLIPPING }, { "winsys", COGL_DEBUG_WINSYS }, { "performance", COGL_DEBUG_PERFORMANCE } }; static const int n_cogl_log_debug_keys = G_N_ELEMENTS (cogl_log_debug_keys); static const GDebugKey cogl_behavioural_debug_keys[] = { { "rectangles", COGL_DEBUG_RECTANGLES }, { "disable-batching", COGL_DEBUG_DISABLE_BATCHING }, { "disable-pbos", COGL_DEBUG_DISABLE_PBOS }, { "disable-software-transform", COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM }, { "dump-atlas-image", COGL_DEBUG_DUMP_ATLAS_IMAGE }, { "disable-atlas", COGL_DEBUG_DISABLE_ATLAS }, { "disable-shared-atlas", COGL_DEBUG_DISABLE_SHARED_ATLAS }, { "disable-texturing", COGL_DEBUG_DISABLE_TEXTURING}, { "disable-blending", COGL_DEBUG_DISABLE_BLENDING}, { "wireframe", COGL_DEBUG_WIREFRAME}, { "disable-software-clip", COGL_DEBUG_DISABLE_SOFTWARE_CLIP}, { "disable-program-caches", COGL_DEBUG_DISABLE_PROGRAM_CACHES}, { "disable-fast-read-pixel", COGL_DEBUG_DISABLE_FAST_READ_PIXEL} }; static const int n_cogl_behavioural_debug_keys = G_N_ELEMENTS (cogl_behavioural_debug_keys); unsigned long _cogl_debug_flags[COGL_DEBUG_N_LONGS]; GHashTable *_cogl_debug_instances; static void _cogl_parse_debug_string_for_keys (const char *value, gboolean enable, const GDebugKey *keys, unsigned int nkeys) { int long_num, key_num; /* g_parse_debug_string expects the value field in GDebugKey to be a mask in an unsigned int but the flags are stored in an array of multiple longs so we need to build a separate array for each possible unsigned int */ for (long_num = 0; long_num < COGL_DEBUG_N_LONGS; long_num++) { int int_num; for (int_num = 0; int_num < sizeof (unsigned long) / sizeof (unsigned int); int_num++) { GDebugKey keys_for_int[sizeof (unsigned int) * 8]; int nkeys_for_int = 0; for (key_num = 0; key_num < nkeys; key_num++) { int long_index = COGL_FLAGS_GET_INDEX (keys[key_num].value); int int_index = (keys[key_num].value % (sizeof (unsigned long) * 8) / (sizeof (unsigned int) * 8)); if (long_index == long_num && int_index == int_num) { keys_for_int[nkeys_for_int] = keys[key_num]; keys_for_int[nkeys_for_int].value = COGL_FLAGS_GET_MASK (keys[key_num].value) >> (int_num * sizeof (unsigned int) * 8); nkeys_for_int++; } } if (nkeys_for_int > 0) { unsigned long mask = ((unsigned long) g_parse_debug_string (value, keys_for_int, nkeys_for_int)) << (int_num * sizeof (unsigned int) * 8); if (enable) _cogl_debug_flags[long_num] |= mask; else _cogl_debug_flags[long_num] &= ~mask; } } } } void _cogl_parse_debug_string (const char *value, gboolean enable, gboolean ignore_help) { if (ignore_help && strcmp (value, "help") == 0) return; /* We don't want to let g_parse_debug_string handle "all" because * literally enabling all the debug options wouldn't be useful to * anyone; instead the all option enables all non behavioural * options. */ if (strcmp (value, "all") == 0 || strcmp (value, "verbose") == 0) { int i; for (i = 0; i < n_cogl_log_debug_keys; i++) if (enable) COGL_DEBUG_SET_FLAG (cogl_log_debug_keys[i].value); else COGL_DEBUG_CLEAR_FLAG (cogl_log_debug_keys[i].value); } else if (g_ascii_strcasecmp (value, "help") == 0) { g_printerr ("\n\n%28s\n", _("Supported debug values:")); #define OPT(MASK_NAME, GROUP, NAME, NAME_FORMATTED, DESCRIPTION) \ g_printerr ("%28s %s\n", NAME ":", DESCRIPTION); #include "cogl-debug-options.h" g_printerr ("\n%28s\n", _("Special debug values:")); OPT (IGNORED, "ignored", "all", "ignored", \ N_("Enables all non-behavioural debug options")); OPT (IGNORED, "ignored", "verbose", "ignored", \ N_("Enables all non-behavioural debug options")); #undef OPT g_printerr ("\n" "%28s\n" " COGL_DISABLE_GL_EXTENSIONS: %s\n" " COGL_OVERRIDE_GL_VERSION: %s\n", _("Additional environment variables:"), _("Comma-separated list of GL extensions to pretend are " "disabled"), _("Override the GL version that Cogl will assume the driver " "supports")); exit (1); } else { _cogl_parse_debug_string_for_keys (value, enable, cogl_log_debug_keys, n_cogl_log_debug_keys); _cogl_parse_debug_string_for_keys (value, enable, cogl_behavioural_debug_keys, n_cogl_behavioural_debug_keys); } } #ifdef COGL_ENABLE_DEBUG static gboolean cogl_arg_debug_cb (const char *key, const char *value, void *user_data) { _cogl_parse_debug_string (value, TRUE /* enable the flags */, FALSE /* don't ignore help */); return TRUE; } static gboolean cogl_arg_no_debug_cb (const char *key, const char *value, void *user_data) { _cogl_parse_debug_string (value, FALSE, /* disable the flags */ TRUE /* ignore help */); return TRUE; } #endif /* COGL_ENABLE_DEBUG */ static GOptionEntry cogl_args[] = { #ifdef COGL_ENABLE_DEBUG { "cogl-debug", 0, 0, G_OPTION_ARG_CALLBACK, cogl_arg_debug_cb, N_("Cogl debugging flags to set"), "FLAGS" }, { "cogl-no-debug", 0, 0, G_OPTION_ARG_CALLBACK, cogl_arg_no_debug_cb, N_("Cogl debugging flags to unset"), "FLAGS" }, #endif /* COGL_ENABLE_DEBUG */ { NULL, }, }; void _cogl_debug_check_environment (void) { const char *env_string; env_string = g_getenv ("COGL_DEBUG"); if (env_string != NULL) { _cogl_parse_debug_string (env_string, TRUE /* enable the flags */, FALSE /* don't ignore help */); env_string = NULL; } env_string = g_getenv ("COGL_NO_DEBUG"); if (env_string != NULL) { _cogl_parse_debug_string (env_string, FALSE /* disable the flags */, FALSE /* don't ignore help */); env_string = NULL; } } static gboolean pre_parse_hook (GOptionContext *context, GOptionGroup *group, void *data, GError **error) { _cogl_init (); return TRUE; } /* XXX: GOption based library initialization is not reliable because the * GOption API has no way to represent dependencies between libraries. */ GOptionGroup * cogl_get_option_group (void) { GOptionGroup *group; group = g_option_group_new ("cogl", _("Cogl Options"), _("Show Cogl options"), NULL, NULL); g_option_group_set_parse_hooks (group, pre_parse_hook, NULL); g_option_group_add_entries (group, cogl_args); return group; } muffin-6.4.1/cogl/cogl/cogl-egl.h0000664000175000017500000000630614723361714015461 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_EGL_H__ #define __COGL_EGL_H__ /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_EGL_H_INSIDE__ */ #ifndef __COGL_EGL_H_INSIDE__ #define __COGL_EGL_H_INSIDE__ #endif /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_EGL__ #endif #endif /* COGL_COMPILATION */ #include #include G_BEGIN_DECLS /** * cogl_egl_context_get_egl_display: * @context: A #CoglContext pointer * * If you have done a runtime check to determine that Cogl is using * EGL internally then this API can be used to retrieve the EGLDisplay * handle that was setup internally. The result is undefined if Cogl * is not using EGL. * * Note: The current window system backend can be checked using * cogl_renderer_get_winsys_id(). * * Return value: The internally setup EGLDisplay handle. * Since: 1.8 * Stability: unstable */ COGL_EXPORT EGLDisplay cogl_egl_context_get_egl_display (CoglContext *context); G_END_DECLS /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_EGL__ #undef __COGL_H_INSIDE__ #undef __COGL_EGL_H_INSIDE__ #undef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_EGL__ #endif #endif /* __COGL_EGL_H__ */ muffin-6.4.1/cogl/cogl/cogl-rectangle-map.h0000664000175000017500000000536114723361714017431 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_RECTANGLE_MAP_H #define __COGL_RECTANGLE_MAP_H #include #include "cogl-types.h" typedef struct _CoglRectangleMap CoglRectangleMap; typedef struct _CoglRectangleMapEntry CoglRectangleMapEntry; typedef void (* CoglRectangleMapCallback) (const CoglRectangleMapEntry *entry, void *rectangle_data, void *user_data); struct _CoglRectangleMapEntry { unsigned int x, y; unsigned int width, height; }; CoglRectangleMap * _cogl_rectangle_map_new (unsigned int width, unsigned int height, GDestroyNotify value_destroy_func); gboolean _cogl_rectangle_map_add (CoglRectangleMap *map, unsigned int width, unsigned int height, void *data, CoglRectangleMapEntry *rectangle); void _cogl_rectangle_map_remove (CoglRectangleMap *map, const CoglRectangleMapEntry *rectangle); unsigned int _cogl_rectangle_map_get_width (CoglRectangleMap *map); unsigned int _cogl_rectangle_map_get_height (CoglRectangleMap *map); unsigned int _cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map); unsigned int _cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map); void _cogl_rectangle_map_foreach (CoglRectangleMap *map, CoglRectangleMapCallback callback, void *data); void _cogl_rectangle_map_free (CoglRectangleMap *map); #endif /* __COGL_RECTANGLE_MAP_H */ muffin-6.4.1/cogl/cogl/cogl-glsl-shader.c0000664000175000017500000001467614723361714017123 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg * Neil Roberts */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-glsl-shader-private.h" #include "cogl-glsl-shader-boilerplate.h" #include "driver/gl/cogl-util-gl-private.h" #include #include static gboolean add_layer_vertex_boilerplate_cb (CoglPipelineLayer *layer, void *user_data) { GString *layer_declarations = user_data; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); g_string_append_printf (layer_declarations, "attribute vec4 cogl_tex_coord%d_in;\n" "#define cogl_texture_matrix%i cogl_texture_matrix[%i]\n" "#define cogl_tex_coord%i_out _cogl_tex_coord[%i]\n", layer->index, layer->index, unit_index, layer->index, unit_index); return TRUE; } static gboolean add_layer_fragment_boilerplate_cb (CoglPipelineLayer *layer, void *user_data) { GString *layer_declarations = user_data; g_string_append_printf (layer_declarations, "#define cogl_tex_coord%i_in _cogl_tex_coord[%i]\n", layer->index, _cogl_pipeline_layer_get_unit_index (layer)); return TRUE; } void _cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, GLuint shader_gl_handle, GLenum shader_gl_type, CoglPipeline *pipeline, GLsizei count_in, const char **strings_in, const GLint *lengths_in) { const char *vertex_boilerplate; const char *fragment_boilerplate; const char **strings = g_alloca (sizeof (char *) * (count_in + 4)); GLint *lengths = g_alloca (sizeof (GLint) * (count_in + 4)); char *version_string; int count = 0; int n_layers; vertex_boilerplate = _COGL_VERTEX_SHADER_BOILERPLATE; fragment_boilerplate = _COGL_FRAGMENT_SHADER_BOILERPLATE; version_string = g_strdup_printf ("#version %i\n\n", ctx->glsl_version_to_use); strings[count] = version_string; lengths[count++] = -1; if (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL)) { static const char image_external_extension[] = "#extension GL_OES_EGL_image_external : require\n"; strings[count] = image_external_extension; lengths[count++] = sizeof (image_external_extension) - 1; } if (shader_gl_type == GL_VERTEX_SHADER) { strings[count] = vertex_boilerplate; lengths[count++] = strlen (vertex_boilerplate); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { strings[count] = fragment_boilerplate; lengths[count++] = strlen (fragment_boilerplate); } n_layers = cogl_pipeline_get_n_layers (pipeline); if (n_layers) { GString *layer_declarations = ctx->codegen_boilerplate_buffer; g_string_set_size (layer_declarations, 0); g_string_append_printf (layer_declarations, "varying vec4 _cogl_tex_coord[%d];\n", n_layers); if (shader_gl_type == GL_VERTEX_SHADER) { g_string_append_printf (layer_declarations, "uniform mat4 cogl_texture_matrix[%d];\n", n_layers); _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_vertex_boilerplate_cb, layer_declarations); } else if (shader_gl_type == GL_FRAGMENT_SHADER) { _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_fragment_boilerplate_cb, layer_declarations); } strings[count] = layer_declarations->str; lengths[count++] = -1; /* null terminated */ } memcpy (strings + count, strings_in, sizeof (char *) * count_in); if (lengths_in) memcpy (lengths + count, lengths_in, sizeof (GLint) * count_in); else { int i; for (i = 0; i < count_in; i++) lengths[count + i] = -1; /* null terminated */ } count += count_in; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_SHOW_SOURCE))) { GString *buf = g_string_new (NULL); int i; g_string_append_printf (buf, "%s shader:\n", shader_gl_type == GL_VERTEX_SHADER ? "vertex" : "fragment"); for (i = 0; i < count; i++) if (lengths[i] != -1) g_string_append_len (buf, strings[i], lengths[i]); else g_string_append (buf, strings[i]); g_message ("%s", buf->str); g_string_free (buf, TRUE); } GE( ctx, glShaderSource (shader_gl_handle, count, (const char **) strings, lengths) ); g_free (version_string); } muffin-6.4.1/cogl/cogl/winsys/0000775000175000017500000000000014723361714015146 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/winsys/cogl-glx-renderer-private.h0000664000175000017500000000672514723361714022321 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_RENDERER_GLX_PRIVATE_H #define __COGL_RENDERER_GLX_PRIVATE_H #include #include "cogl-object-private.h" #include "cogl-xlib-renderer-private.h" typedef struct _CoglGLXRenderer { int glx_major; int glx_minor; int glx_error_base; int glx_event_base; /* Vblank stuff */ int dri_fd; /* enumeration with relatioship between OML_sync_control * UST (unadjusted-system-time) and the system clock */ enum { COGL_GLX_UST_IS_UNKNOWN, COGL_GLX_UST_IS_GETTIMEOFDAY, COGL_GLX_UST_IS_MONOTONIC_TIME, COGL_GLX_UST_IS_OTHER } ust_type; /* GModule pointing to libGL which we use to get glX functions out of */ GModule *libgl_module; CoglClosure *flush_notifications_idle; /* Copy of the winsys features that are based purely on the * information we can get without using a GL context. We want to * determine this before we have a context so that we can use the * function pointers from the extensions earlier. This is necessary * to use the glXCreateContextAttribs function. */ unsigned long base_winsys_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_WINSYS_FEATURE_N_FEATURES)]; /* Function pointers for core GLX functionality. We can't just link against these directly because we need to conditionally load libGL when we are using GLX so that it won't conflict with a GLES library if we are using EGL + GLES. These are just the functions that we want to use before calling glXGetProcAddress */ Bool (* glXQueryExtension) (Display *dpy, int *errorb, int *event); const char * (* glXQueryExtensionsString) (Display *dpy, int screen); Bool (* glXQueryVersion) (Display *dpy, int *maj, int *min); void * (* glXGetProcAddress) (const GLubyte *procName); int (* glXQueryDrawable) (Display *dpy, GLXDrawable drawable, int attribute, unsigned int *value); /* Function pointers for GLX specific extensions */ #define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d, e, f, g) #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ ret (APIENTRY * name) args; #define COGL_WINSYS_FEATURE_END() #include "winsys/cogl-winsys-glx-feature-functions.h" #undef COGL_WINSYS_FEATURE_BEGIN #undef COGL_WINSYS_FEATURE_FUNCTION #undef COGL_WINSYS_FEATURE_END } CoglGLXRenderer; #endif /* __COGL_RENDERER_GLX_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-texture-pixmap-x11.h0000664000175000017500000002101614723361714021644 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_PIXMAP_X11_H #define __COGL_TEXTURE_PIXMAP_X11_H /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_TEXTURE_PIXMAP_X11_ #endif #endif /* COGL_COMPILATION */ #include #include G_BEGIN_DECLS /** * SECTION:cogl-texture-pixmap-x11 * @short_description: Functions for creating and manipulating 2D meta * textures derived from X11 pixmaps. * * These functions allow high-level meta textures (See the * #CoglMetaTexture interface) that derive their contents from an X11 * pixmap. */ typedef struct _CoglTexturePixmapX11 CoglTexturePixmapX11; #define COGL_TEXTURE_PIXMAP_X11(X) ((CoglTexturePixmapX11 *)X) /** * cogl_texture_pixmap_x11_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_texture_pixmap_x11_get_gtype (void); typedef enum { COGL_TEXTURE_PIXMAP_X11_DAMAGE_RAW_RECTANGLES, COGL_TEXTURE_PIXMAP_X11_DAMAGE_DELTA_RECTANGLES, COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX, COGL_TEXTURE_PIXMAP_X11_DAMAGE_NON_EMPTY } CoglTexturePixmapX11ReportLevel; /** * COGL_TEXTURE_PIXMAP_X11_ERROR: * * #GError domain for texture-pixmap-x11 errors. * * Since: 1.10 */ #define COGL_TEXTURE_PIXMAP_X11_ERROR (cogl_texture_pixmap_x11_error_quark ()) /** * CoglTexturePixmapX11Error: * @COGL_TEXTURE_PIXMAP_X11_ERROR_X11: An X11 protocol error * * Error codes that can be thrown when performing texture-pixmap-x11 * operations. * * Since: 1.10 */ typedef enum { COGL_TEXTURE_PIXMAP_X11_ERROR_X11, } CoglTexturePixmapX11Error; COGL_EXPORT uint32_t cogl_texture_pixmap_x11_error_quark (void); /** * cogl_texture_pixmap_x11_new: * @context: A #CoglContext * @pixmap: A X11 pixmap ID * @automatic_updates: Whether to automatically copy the contents of * the pixmap to the texture. * @error: A #GError for exceptions * * Creates a texture that contains the contents of @pixmap. If * @automatic_updates is %TRUE then Cogl will attempt to listen for * damage events on the pixmap and automatically update the texture * when it changes. * * Return value: a new #CoglTexturePixmapX11 instance * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new (CoglContext *context, uint32_t pixmap, gboolean automatic_updates, GError **error); /** * cogl_texture_pixmap_x11_new_left: * @context: A #CoglContext * @pixmap: A X11 pixmap ID * @automatic_updates: Whether to automatically copy the contents of * the pixmap to the texture. * @error: A #GError for exceptions * * Creates one of a pair of textures to contain the contents of @pixmap, * which has stereo content. (Different images for the right and left eyes.) * The left image is drawn using this texture; the right image is drawn * using a texture created by calling * cogl_texture_pixmap_x11_new_right() and passing in this texture as an * argument. * * In general, you should not use this function unless you have * queried the %GLX_STEREO_TREE_EXT attribute of the corresponding * window using glXQueryDrawable() and determined that the window is * stereo. Note that this attribute can change over time and * notification is also provided through events defined in the * EXT_stereo_tree GLX extension. As long as the system has support for * stereo content, drawing using the left and right pixmaps will not * produce an error even if the window doesn't have stereo * content any more, but drawing with the right pixmap will produce * undefined output, so you need to listen for these events and * re-render to avoid race conditions. (Recreating a non-stereo * pixmap is not necessary, but may save resources.) * * Return value: a new #CoglTexturePixmapX11 instance * * Since: 1.20 * Stability: Unstable */ COGL_EXPORT CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new_left (CoglContext *context, uint32_t pixmap, gboolean automatic_updates, GError **error); /** * cogl_texture_pixmap_x11_new_right: * @left_texture: A #CoglTexturePixmapX11 instance created with * cogl_texture_pixmap_x11_new_left(). * * Creates a texture object that corresponds to the right-eye image * of a pixmap with stereo content. @left_texture must have been * created using cogl_texture_pixmap_x11_new_left(). * * Return value: a new #CoglTexturePixmapX11 instance * * Since: 1.20 * Stability: Unstable */ COGL_EXPORT CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new_right (CoglTexturePixmapX11 *left_texture); /** * cogl_texture_pixmap_x11_update_area: * @texture: A #CoglTexturePixmapX11 instance * @x: x coordinate of the area to update * @y: y coordinate of the area to update * @width: width of the area to update * @height: height of the area to update * * Forces an update of the given @texture so that it is refreshed with * the contents of the pixmap that was given to * cogl_texture_pixmap_x11_new(). * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT void cogl_texture_pixmap_x11_update_area (CoglTexturePixmapX11 *texture, int x, int y, int width, int height); /** * cogl_texture_pixmap_x11_is_using_tfp_extension: * @texture: A #CoglTexturePixmapX11 instance * * Checks whether the given @texture is using the * GLX_EXT_texture_from_pixmap or similar extension to copy the * contents of the pixmap to the texture. This extension is usually * implemented as zero-copy operation so it implies the updates are * working efficiently. * * Return value: %TRUE if the texture is using an efficient extension * and %FALSE otherwise * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT gboolean cogl_texture_pixmap_x11_is_using_tfp_extension (CoglTexturePixmapX11 *texture); /** * cogl_is_texture_pixmap_x11: * @object: A pointer to a #CoglObject * * Checks whether @object points to a #CoglTexturePixmapX11 instance. * * Return value: %TRUE if the object is a #CoglTexturePixmapX11, and * %FALSE otherwise * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_texture_pixmap_x11 (void *object); G_END_DECLS /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_TEXTURE_PIXMAP_X11_ #undef __COGL_H_INSIDE__ #undef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_TEXTURE_PIXMAP_X11_ #endif #endif /* __COGL_TEXTURE_PIXMAP_X11_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-egl.c0000664000175000017500000007743114723361714020351 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-i18n-private.h" #include "cogl-util.h" #include "cogl-feature-private.h" #include "cogl-context-private.h" #include "cogl-framebuffer.h" #include "cogl-onscreen-private.h" #include "cogl-swap-chain-private.h" #include "cogl-renderer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-egl.h" #include "cogl-private.h" #include "cogl-trace.h" #include "winsys/cogl-winsys-egl-private.h" #include "winsys/cogl-winsys-private.h" #include #include #include #include #include #ifndef EGL_KHR_create_context #define EGL_CONTEXT_MAJOR_VERSION_KHR 0x3098 #define EGL_CONTEXT_MINOR_VERSION_KHR 0x30FB #define EGL_CONTEXT_FLAGS_KHR 0x30FC #define EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR 0x30FD #define EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR 0x31BD #define EGL_OPENGL_ES3_BIT_KHR 0x0040 #define EGL_NO_RESET_NOTIFICATION_KHR 0x31BE #define EGL_LOSE_CONTEXT_ON_RESET_KHR 0x31BF #define EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR 0x00000002 #define EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR 0x00000004 #define EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR 0x00000001 #define EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR 0x00000002 #endif #ifndef EGL_IMG_context_priority #define EGL_CONTEXT_PRIORITY_LEVEL_IMG 0x3100 #define EGL_CONTEXT_PRIORITY_HIGH_IMG 0x3101 #define EGL_CONTEXT_PRIORITY_MEDIUM_IMG 0x3102 #define EGL_CONTEXT_PRIORITY_LOW_IMG 0x3103 #endif #define MAX_EGL_CONFIG_ATTRIBS 30 /* Define a set of arrays containing the functions required from GL for each winsys feature */ #define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \ egl_private_flags) \ static const CoglFeatureFunction \ cogl_egl_feature_ ## name ## _funcs[] = { #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglRendererEGL, pf_ ## name) }, #define COGL_WINSYS_FEATURE_END() \ { NULL, 0 }, \ }; #include "winsys/cogl-winsys-egl-feature-functions.h" /* Define an array of features */ #undef COGL_WINSYS_FEATURE_BEGIN #define COGL_WINSYS_FEATURE_BEGIN(name, namespaces, extension_names, \ egl_private_flags) \ { 255, 255, 0, namespaces, extension_names, \ egl_private_flags, \ 0, \ cogl_egl_feature_ ## name ## _funcs }, #undef COGL_WINSYS_FEATURE_FUNCTION #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) #undef COGL_WINSYS_FEATURE_END #define COGL_WINSYS_FEATURE_END() static const CoglFeatureData winsys_feature_data[] = { #include "winsys/cogl-winsys-egl-feature-functions.h" }; static GCallback _cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, const char *name, gboolean in_core) { void *ptr = NULL; if (!in_core) ptr = eglGetProcAddress (name); /* eglGetProcAddress doesn't support fetching core API so we need to get that separately with GModule */ if (ptr == NULL) g_module_symbol (renderer->libgl_module, name, &ptr); return ptr; } static void _cogl_winsys_renderer_disconnect (CoglRenderer *renderer) { /* This function must be overridden by a platform winsys */ g_assert_not_reached (); } /* Updates all the function pointers */ static void check_egl_extensions (CoglRenderer *renderer) { CoglRendererEGL *egl_renderer = renderer->winsys; const char *egl_extensions; char **split_extensions; int i; egl_extensions = eglQueryString (egl_renderer->edpy, EGL_EXTENSIONS); split_extensions = g_strsplit (egl_extensions, " ", 0 /* max_tokens */); COGL_NOTE (WINSYS, " EGL Extensions: %s", egl_extensions); egl_renderer->private_features = 0; for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++) if (_cogl_feature_check (renderer, "EGL", winsys_feature_data + i, 0, 0, COGL_DRIVER_GL, /* the driver isn't used */ split_extensions, egl_renderer)) { egl_renderer->private_features |= winsys_feature_data[i].feature_flags_private; } g_strfreev (split_extensions); } gboolean _cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer, GError **error) { CoglRendererEGL *egl_renderer = renderer->winsys; if (!eglInitialize (egl_renderer->edpy, &egl_renderer->egl_version_major, &egl_renderer->egl_version_minor)) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "Couldn't initialize EGL"); return FALSE; } check_egl_extensions (renderer); return TRUE; } static gboolean _cogl_winsys_renderer_connect (CoglRenderer *renderer, GError **error) { /* This function must be overridden by a platform winsys */ g_assert_not_reached (); return FALSE; } static void egl_attributes_from_framebuffer_config (CoglDisplay *display, CoglFramebufferConfig *config, EGLint *attributes) { CoglRenderer *renderer = display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; int i = 0; /* Let the platform add attributes first, including setting the * EGL_SURFACE_TYPE */ i = egl_renderer->platform_vtable->add_config_attributes (display, config, attributes); if (config->need_stencil) { attributes[i++] = EGL_STENCIL_SIZE; attributes[i++] = 2; } attributes[i++] = EGL_RED_SIZE; attributes[i++] = 1; attributes[i++] = EGL_GREEN_SIZE; attributes[i++] = 1; attributes[i++] = EGL_BLUE_SIZE; attributes[i++] = 1; attributes[i++] = EGL_ALPHA_SIZE; attributes[i++] = config->swap_chain->has_alpha ? 1 : EGL_DONT_CARE; attributes[i++] = EGL_DEPTH_SIZE; attributes[i++] = 1; attributes[i++] = EGL_BUFFER_SIZE; attributes[i++] = EGL_DONT_CARE; attributes[i++] = EGL_RENDERABLE_TYPE; attributes[i++] = ((renderer->driver == COGL_DRIVER_GL || renderer->driver == COGL_DRIVER_GL3) ? EGL_OPENGL_BIT : EGL_OPENGL_ES2_BIT); if (config->samples_per_pixel) { attributes[i++] = EGL_SAMPLE_BUFFERS; attributes[i++] = 1; attributes[i++] = EGL_SAMPLES; attributes[i++] = config->samples_per_pixel; } attributes[i++] = EGL_NONE; g_assert (i < MAX_EGL_CONFIG_ATTRIBS); } EGLBoolean _cogl_winsys_egl_make_current (CoglDisplay *display, EGLSurface draw, EGLSurface read, EGLContext context) { CoglDisplayEGL *egl_display = display->winsys; CoglRendererEGL *egl_renderer = display->renderer->winsys; EGLBoolean ret; if (egl_display->current_draw_surface == draw && egl_display->current_read_surface == read && egl_display->current_context == context) return EGL_TRUE; ret = eglMakeCurrent (egl_renderer->edpy, draw, read, context); egl_display->current_draw_surface = draw; egl_display->current_read_surface = read; egl_display->current_context = context; return ret; } EGLBoolean _cogl_winsys_egl_ensure_current (CoglDisplay *display) { CoglDisplayEGL *egl_display = display->winsys; CoglRendererEGL *egl_renderer = display->renderer->winsys; return eglMakeCurrent (egl_renderer->edpy, egl_display->current_draw_surface, egl_display->current_read_surface, egl_display->current_context); } static void cleanup_context (CoglDisplay *display) { CoglRenderer *renderer = display->renderer; CoglDisplayEGL *egl_display = display->winsys; CoglRendererEGL *egl_renderer = renderer->winsys; if (egl_display->egl_context != EGL_NO_CONTEXT) { _cogl_winsys_egl_make_current (display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); eglDestroyContext (egl_renderer->edpy, egl_display->egl_context); egl_display->egl_context = EGL_NO_CONTEXT; } if (egl_renderer->platform_vtable->cleanup_context) egl_renderer->platform_vtable->cleanup_context (display); } static gboolean try_create_context (CoglDisplay *display, GError **error) { CoglRenderer *renderer = display->renderer; CoglDisplayEGL *egl_display = display->winsys; CoglRendererEGL *egl_renderer = renderer->winsys; EGLDisplay edpy; EGLConfig config; EGLint attribs[11]; EGLint cfg_attribs[MAX_EGL_CONFIG_ATTRIBS]; GError *config_error = NULL; const char *error_message; int i = 0; g_return_val_if_fail (egl_display->egl_context == NULL, TRUE); if (renderer->driver == COGL_DRIVER_GL || renderer->driver == COGL_DRIVER_GL3) eglBindAPI (EGL_OPENGL_API); else if (renderer->driver == COGL_DRIVER_GLES2) eglBindAPI (EGL_OPENGL_ES_API); egl_attributes_from_framebuffer_config (display, &display->onscreen_template->config, cfg_attribs); edpy = egl_renderer->edpy; if (!egl_renderer->platform_vtable->choose_config (display, cfg_attribs, &config, &config_error)) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Couldn't choose config: %s", config_error->message); g_error_free (config_error); goto err; } egl_display->egl_config = config; if (display->renderer->driver == COGL_DRIVER_GL3) { if (!(egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT)) { error_message = "Driver does not support GL 3 contexts"; goto fail; } /* Try to get a core profile 3.1 context with no deprecated features */ attribs[i++] = EGL_CONTEXT_MAJOR_VERSION_KHR; attribs[i++] = 3; attribs[i++] = EGL_CONTEXT_MINOR_VERSION_KHR; attribs[i++] = 1; attribs[i++] = EGL_CONTEXT_FLAGS_KHR; attribs[i++] = EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR; attribs[i++] = EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR; attribs[i++] = EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR; } else if (display->renderer->driver == COGL_DRIVER_GLES2) { attribs[i++] = EGL_CONTEXT_CLIENT_VERSION; attribs[i++] = 2; } if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_CONTEXT_PRIORITY) { attribs[i++] = EGL_CONTEXT_PRIORITY_LEVEL_IMG; attribs[i++] = EGL_CONTEXT_PRIORITY_HIGH_IMG; } attribs[i++] = EGL_NONE; egl_display->egl_context = eglCreateContext (edpy, config, EGL_NO_CONTEXT, attribs); if (egl_display->egl_context == EGL_NO_CONTEXT) { error_message = "Unable to create a suitable EGL context"; goto fail; } if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_CONTEXT_PRIORITY) { EGLint value = EGL_CONTEXT_PRIORITY_MEDIUM_IMG; eglQueryContext (egl_renderer->edpy, egl_display->egl_context, EGL_CONTEXT_PRIORITY_LEVEL_IMG, &value); if (value != EGL_CONTEXT_PRIORITY_HIGH_IMG) g_message ("Failed to obtain high priority context"); } if (egl_renderer->platform_vtable->context_created && !egl_renderer->platform_vtable->context_created (display, error)) return FALSE; return TRUE; fail: g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "%s", error_message); err: cleanup_context (display); return FALSE; } static void _cogl_winsys_display_destroy (CoglDisplay *display) { CoglRendererEGL *egl_renderer = display->renderer->winsys; CoglDisplayEGL *egl_display = display->winsys; g_return_if_fail (egl_display != NULL); cleanup_context (display); if (egl_renderer->platform_vtable->display_destroy) egl_renderer->platform_vtable->display_destroy (display); g_slice_free (CoglDisplayEGL, display->winsys); display->winsys = NULL; } static gboolean _cogl_winsys_display_setup (CoglDisplay *display, GError **error) { CoglDisplayEGL *egl_display; CoglRenderer *renderer = display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; g_return_val_if_fail (display->winsys == NULL, FALSE); egl_display = g_slice_new0 (CoglDisplayEGL); display->winsys = egl_display; #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT if (display->wayland_compositor_display) { struct wl_display *wayland_display = display->wayland_compositor_display; CoglRendererEGL *egl_renderer = display->renderer->winsys; if (egl_renderer->pf_eglBindWaylandDisplay) egl_renderer->pf_eglBindWaylandDisplay (egl_renderer->edpy, wayland_display); } #endif if (egl_renderer->platform_vtable->display_setup && !egl_renderer->platform_vtable->display_setup (display, error)) goto error; if (!try_create_context (display, error)) goto error; egl_display->found_egl_config = TRUE; return TRUE; error: _cogl_winsys_display_destroy (display); return FALSE; } static gboolean _cogl_winsys_context_init (CoglContext *context, GError **error) { CoglRenderer *renderer = context->display->renderer; CoglDisplayEGL *egl_display = context->display->winsys; CoglRendererEGL *egl_renderer = renderer->winsys; context->winsys = g_new0 (CoglContextEGL, 1); g_return_val_if_fail (egl_display->egl_context, FALSE); memset (context->winsys_features, 0, sizeof (context->winsys_features)); check_egl_extensions (renderer); if (!_cogl_context_update_features (context, error)) return FALSE; if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SWAP_REGION) { COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION, TRUE); COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); } if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) && _cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_OES_EGL_SYNC)) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE); if (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE) { COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_BUFFER_AGE, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE); } if (egl_renderer->platform_vtable->context_init && !egl_renderer->platform_vtable->context_init (context, error)) return FALSE; return TRUE; } static void _cogl_winsys_context_deinit (CoglContext *context) { CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; if (egl_renderer->platform_vtable->context_deinit) egl_renderer->platform_vtable->context_deinit (context); g_free (context->winsys); } static gboolean _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglDisplay *display = context->display; CoglDisplayEGL *egl_display = display->winsys; CoglRenderer *renderer = display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; EGLint attributes[MAX_EGL_CONFIG_ATTRIBS]; EGLConfig egl_config; EGLint config_count = 0; EGLBoolean status; g_return_val_if_fail (egl_display->egl_context, FALSE); egl_attributes_from_framebuffer_config (display, &framebuffer->config, attributes); status = eglChooseConfig (egl_renderer->edpy, attributes, &egl_config, 1, &config_count); if (status != EGL_TRUE || config_count == 0) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "Failed to find a suitable EGL configuration"); return FALSE; } /* Update the real number of samples_per_pixel now that we have * found an egl_config... */ if (framebuffer->config.samples_per_pixel) { EGLint samples; status = eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_SAMPLES, &samples); g_return_val_if_fail (status == EGL_TRUE, TRUE); framebuffer->samples_per_pixel = samples; } onscreen->winsys = g_slice_new0 (CoglOnscreenEGL); if (egl_renderer->platform_vtable->onscreen_init && !egl_renderer->platform_vtable->onscreen_init (onscreen, egl_config, error)) { g_slice_free (CoglOnscreenEGL, onscreen->winsys); return FALSE; } return TRUE; } static void _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglDisplayEGL *egl_display = context->display->winsys; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; /* If we never successfully allocated then there's nothing to do */ if (egl_onscreen == NULL) return; if (egl_onscreen->egl_surface != EGL_NO_SURFACE) { /* Cogl always needs a valid context bound to something so if we * are destroying the onscreen that is currently bound we'll * switch back to the dummy drawable. */ if ((egl_display->dummy_surface != EGL_NO_SURFACE || (egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) != 0) && (egl_display->current_draw_surface == egl_onscreen->egl_surface || egl_display->current_read_surface == egl_onscreen->egl_surface)) { _cogl_winsys_egl_make_current (context->display, egl_display->dummy_surface, egl_display->dummy_surface, egl_display->current_context); } if (eglDestroySurface (egl_renderer->edpy, egl_onscreen->egl_surface) == EGL_FALSE) g_warning ("Failed to destroy EGL surface"); egl_onscreen->egl_surface = EGL_NO_SURFACE; } if (egl_renderer->platform_vtable->onscreen_deinit) egl_renderer->platform_vtable->onscreen_deinit (onscreen); g_slice_free (CoglOnscreenEGL, onscreen->winsys); onscreen->winsys = NULL; } static gboolean bind_onscreen_with_context (CoglOnscreen *onscreen, EGLContext egl_context) { CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); CoglContext *context = fb->context; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; gboolean status = _cogl_winsys_egl_make_current (context->display, egl_onscreen->egl_surface, egl_onscreen->egl_surface, egl_context); if (status) { CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; eglSwapInterval (egl_renderer->edpy, 1); } return status; } static gboolean bind_onscreen (CoglOnscreen *onscreen) { CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); CoglContext *context = fb->context; CoglDisplayEGL *egl_display = context->display->winsys; return bind_onscreen_with_context (onscreen, egl_display->egl_context); } static void _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) { bind_onscreen (onscreen); } #ifndef EGL_BUFFER_AGE_EXT #define EGL_BUFFER_AGE_EXT 0x313D #endif static int _cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglDisplayEGL *egl_display = context->display->winsys; EGLSurface surface = egl_onscreen->egl_surface; static gboolean warned = FALSE; int age = 0; if (!(egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_BUFFER_AGE)) return 0; if (!_cogl_winsys_egl_make_current (context->display, surface, surface, egl_display->egl_context)) return 0; if (!eglQuerySurface (egl_renderer->edpy, surface, EGL_BUFFER_AGE_EXT, &age)) { if (!warned) g_critical ("Failed to query buffer age, got error %x", eglGetError ()); warned = TRUE; } else { warned = FALSE; } return age; } static void _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, const int *user_rectangles, int n_rectangles) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); int framebuffer_height = cogl_framebuffer_get_height (framebuffer); int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); int i; /* eglSwapBuffersRegion expects rectangles relative to the * bottom left corner but we are given rectangles relative to * the top left so we need to flip them... */ memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4); for (i = 0; i < n_rectangles; i++) { int *rect = &rectangles[4 * i]; rect[1] = framebuffer_height - rect[1] - rect[3]; } /* At least for eglSwapBuffers the EGL spec says that the surface to swap must be bound to the current context. It looks like Mesa also validates that this is the case for eglSwapBuffersRegion so we must bind here too */ _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen), COGL_FRAMEBUFFER (onscreen), COGL_FRAMEBUFFER_STATE_BIND); if (egl_renderer->pf_eglSwapBuffersRegion (egl_renderer->edpy, egl_onscreen->egl_surface, n_rectangles, rectangles) == EGL_FALSE) g_warning ("Error reported by eglSwapBuffersRegion"); } static void _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; COGL_TRACE_BEGIN_SCOPED (CoglOnscreenEGLSwapBuffersWithDamage, "Onscreen (eglSwapBuffers)"); /* The specification for EGL (at least in 1.4) says that the surface needs to be bound to the current context for the swap to work although it may change in future. Mesa explicitly checks for this and just returns an error if this is not the case so we can't just pretend this isn't in the spec. */ _cogl_framebuffer_flush_state (COGL_FRAMEBUFFER (onscreen), COGL_FRAMEBUFFER (onscreen), COGL_FRAMEBUFFER_STATE_BIND); if (n_rectangles && egl_renderer->pf_eglSwapBuffersWithDamage) { CoglFramebuffer *fb = COGL_FRAMEBUFFER (onscreen); size_t size = n_rectangles * sizeof (int) * 4; int *flipped = alloca (size); int i; memcpy (flipped, rectangles, size); for (i = 0; i < n_rectangles; i++) { const int *rect = rectangles + 4 * i; int *flip_rect = flipped + 4 * i; flip_rect[1] = fb->height - rect[1] - rect[3]; } if (egl_renderer->pf_eglSwapBuffersWithDamage (egl_renderer->edpy, egl_onscreen->egl_surface, flipped, n_rectangles) == EGL_FALSE) g_warning ("Error reported by eglSwapBuffersWithDamage"); } else eglSwapBuffers (egl_renderer->edpy, egl_onscreen->egl_surface); } #if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) static void * _cogl_winsys_fence_add (CoglContext *context) { CoglRendererEGL *renderer = context->display->renderer->winsys; void *ret; if (renderer->pf_eglCreateSync) ret = renderer->pf_eglCreateSync (renderer->edpy, EGL_SYNC_FENCE_KHR, NULL); else ret = NULL; return ret; } static gboolean _cogl_winsys_fence_is_complete (CoglContext *context, void *fence) { CoglRendererEGL *renderer = context->display->renderer->winsys; EGLint ret; ret = renderer->pf_eglClientWaitSync (renderer->edpy, fence, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR, 0); return (ret == EGL_CONDITION_SATISFIED_KHR); } static void _cogl_winsys_fence_destroy (CoglContext *context, void *fence) { CoglRendererEGL *renderer = context->display->renderer->winsys; renderer->pf_eglDestroySync (renderer->edpy, fence); } #endif static CoglWinsysVtable _cogl_winsys_vtable = { .constraints = COGL_RENDERER_CONSTRAINT_USES_EGL, /* This winsys is only used as a base for the EGL-platform winsys's so it does not have an ID or a name */ .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address, .renderer_connect = _cogl_winsys_renderer_connect, .renderer_disconnect = _cogl_winsys_renderer_disconnect, .display_setup = _cogl_winsys_display_setup, .display_destroy = _cogl_winsys_display_destroy, .context_init = _cogl_winsys_context_init, .context_deinit = _cogl_winsys_context_deinit, .onscreen_init = _cogl_winsys_onscreen_init, .onscreen_deinit = _cogl_winsys_onscreen_deinit, .onscreen_bind = _cogl_winsys_onscreen_bind, .onscreen_swap_buffers_with_damage = _cogl_winsys_onscreen_swap_buffers_with_damage, .onscreen_swap_region = _cogl_winsys_onscreen_swap_region, .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age, #if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) .fence_add = _cogl_winsys_fence_add, .fence_is_complete = _cogl_winsys_fence_is_complete, .fence_destroy = _cogl_winsys_fence_destroy, #endif }; /* XXX: we use a function because no doubt someone will complain * about using c99 member initializers because they aren't portable * to windows. We want to avoid having to rigidly follow the real * order of members since some members are #ifdefd and we'd have * to mirror the #ifdefing to add padding etc. For any winsys that * can assume the platform has a sane compiler then we can just use * c99 initializers for insane platforms they can initialize * the members by name in a function. */ const CoglWinsysVtable * _cogl_winsys_egl_get_vtable (void) { return &_cogl_winsys_vtable; } #ifdef EGL_KHR_image_base EGLImageKHR _cogl_egl_create_image (CoglContext *ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attribs) { CoglDisplayEGL *egl_display = ctx->display->winsys; CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; EGLContext egl_ctx; g_return_val_if_fail (egl_renderer->pf_eglCreateImage, EGL_NO_IMAGE_KHR); /* The EGL_KHR_image_pixmap spec explicitly states that EGL_NO_CONTEXT must * always be used in conjunction with the EGL_NATIVE_PIXMAP_KHR target */ #ifdef EGL_KHR_image_pixmap if (target == EGL_NATIVE_PIXMAP_KHR) egl_ctx = EGL_NO_CONTEXT; else #endif #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT /* The WL_bind_wayland_display spec states that EGL_NO_CONTEXT is to be used * in conjunction with the EGL_WAYLAND_BUFFER_WL target */ if (target == EGL_WAYLAND_BUFFER_WL) egl_ctx = EGL_NO_CONTEXT; else #endif egl_ctx = egl_display->egl_context; return egl_renderer->pf_eglCreateImage (egl_renderer->edpy, egl_ctx, target, buffer, attribs); } void _cogl_egl_destroy_image (CoglContext *ctx, EGLImageKHR image) { CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; g_return_if_fail (egl_renderer->pf_eglDestroyImage); egl_renderer->pf_eglDestroyImage (egl_renderer->edpy, image); } #endif #ifdef EGL_WL_bind_wayland_display gboolean _cogl_egl_query_wayland_buffer (CoglContext *ctx, struct wl_resource *buffer, int attribute, int *value) { CoglRendererEGL *egl_renderer = ctx->display->renderer->winsys; g_return_val_if_fail (egl_renderer->pf_eglQueryWaylandBuffer, FALSE); return egl_renderer->pf_eglQueryWaylandBuffer (egl_renderer->edpy, buffer, attribute, value); } #endif EGLDisplay cogl_egl_context_get_egl_display (CoglContext *context) { CoglRendererEGL *egl_renderer = context->display->renderer->winsys; return egl_renderer->edpy; } muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-egl-x11-private.h0000664000175000017500000000264214723361714022425 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_WINSYS_EGL_X11_PRIVATE_H #define __COGL_WINSYS_EGL_X11_PRIVATE_H #include "winsys/cogl-winsys-private.h" COGL_EXPORT const CoglWinsysVtable * _cogl_winsys_egl_xlib_get_vtable (void); #endif /* __COGL_WINSYS_EGL_X11_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-glx-feature-functions.h0000664000175000017500000002165414723361714024034 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This can be included multiple times with different definitions for * the COGL_WINSYS_FEATURE_* functions. */ /* Macro prototypes: * COGL_WINSYS_FEATURE_BEGIN (major_glx_version, minor_glx_version, * name, namespaces, extension_names, * implied_legacy_feature_flags, * implied_winsys_feature) * COGL_WINSYS_FEATURE_FUNCTION (return_type, function_name, * (arguments)) * ... * COGL_WINSYS_FEATURE_END () * * Note: You can list multiple namespace and extension names if the * corresponding _FEATURE_FUNCTIONS have the same semantics accross * the different extension variants. * * XXX: NB: Don't add a trailing semicolon when using these macros */ /* Base functions that we assume are always available */ COGL_WINSYS_FEATURE_BEGIN (0, 0, /* always available */ base_glx_functions, "\0", "\0", 0, /* no implied public feature */ 0 /* no winsys feature */) COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyContext, (Display *dpy, GLXContext ctx)) COGL_WINSYS_FEATURE_FUNCTION (void, glXSwapBuffers, (Display *dpy, GLXDrawable drawable)) COGL_WINSYS_FEATURE_FUNCTION (Bool, glXIsDirect, (Display *dpy, GLXContext ctx)) COGL_WINSYS_FEATURE_FUNCTION (int, glXGetFBConfigAttrib, (Display *dpy, GLXFBConfig config, int attribute, int *value)) COGL_WINSYS_FEATURE_FUNCTION (GLXWindow, glXCreateWindow, (Display *dpy, GLXFBConfig config, Window win, const int *attribList)) COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyWindow, (Display *dpy, GLXWindow window)) COGL_WINSYS_FEATURE_FUNCTION (GLXPixmap, glXCreatePixmap, (Display *dpy, GLXFBConfig config, Pixmap pixmap, const int *attribList)) COGL_WINSYS_FEATURE_FUNCTION (void, glXDestroyPixmap, (Display *dpy, GLXPixmap pixmap)) COGL_WINSYS_FEATURE_FUNCTION (GLXContext, glXCreateNewContext, (Display *dpy, GLXFBConfig config, int renderType, GLXContext shareList, Bool direct)) COGL_WINSYS_FEATURE_FUNCTION (Bool, glXMakeContextCurrent, (Display *dpy, GLXDrawable draw, GLXDrawable read, GLXContext ctx)) COGL_WINSYS_FEATURE_FUNCTION (void, glXSelectEvent, (Display *dpy, GLXDrawable drawable, unsigned long mask)) COGL_WINSYS_FEATURE_FUNCTION (GLXFBConfig *, glXGetFBConfigs, (Display *dpy, int screen, int *nelements)) COGL_WINSYS_FEATURE_FUNCTION (GLXFBConfig *, glXChooseFBConfig, (Display *dpy, int screen, const int *attrib_list, int *nelements)) COGL_WINSYS_FEATURE_FUNCTION (XVisualInfo *, glXGetVisualFromFBConfig, (Display *dpy, GLXFBConfig config)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, texture_from_pixmap, "EXT\0", "texture_from_pixmap\0", 0, COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP) COGL_WINSYS_FEATURE_FUNCTION (void, glXBindTexImage, (Display *display, GLXDrawable drawable, int buffer, int *attribList)) COGL_WINSYS_FEATURE_FUNCTION (void, glXReleaseTexImage, (Display *display, GLXDrawable drawable, int buffer)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, video_sync, "SGI\0", "video_sync\0", 0, COGL_WINSYS_FEATURE_VBLANK_COUNTER) COGL_WINSYS_FEATURE_FUNCTION (int, glXGetVideoSync, (unsigned int *count)) COGL_WINSYS_FEATURE_FUNCTION (int, glXWaitVideoSync, (int divisor, int remainder, unsigned int *count)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, swap_control, "SGI\0", "swap_control\0", 0, COGL_WINSYS_FEATURE_SWAP_THROTTLE) COGL_WINSYS_FEATURE_FUNCTION (int, glXSwapInterval, (int interval)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, sync_control, "OML\0", "sync_control\0", 0, 0) COGL_WINSYS_FEATURE_FUNCTION (Bool, glXGetSyncValues, (Display* dpy, GLXDrawable drawable, int64_t* ust, int64_t* msc, int64_t* sbc)) COGL_WINSYS_FEATURE_FUNCTION (Bool, glXWaitForMsc, (Display* dpy, GLXDrawable drawable, int64_t target_msc, int64_t divisor, int64_t remainder, int64_t* ust, int64_t* msc, int64_t* sbc)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, copy_sub_buffer, "MESA\0", "copy_sub_buffer\0", 0, /* We initially assumed that copy_sub_buffer is synchronized on * which is only the case for a subset of GPUs for example it is not * synchronized on INTEL gen6 and gen7, so we remove this assumption * for now */ #if 0 COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED) #endif 0) COGL_WINSYS_FEATURE_FUNCTION (void, glXCopySubBuffer, (Display *dpy, GLXDrawable drawable, int x, int y, int width, int height)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, swap_event, "INTEL\0", "swap_event\0", 0, COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, create_context, "ARB\0", "create_context", 0, 0) COGL_WINSYS_FEATURE_FUNCTION (GLXContext, glXCreateContextAttribs, (Display *dpy, GLXFBConfig config, GLXContext share_context, Bool direct, const int *attrib_list)) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (255, 255, buffer_age, "EXT\0", "buffer_age\0", 0, COGL_WINSYS_FEATURE_BUFFER_AGE) COGL_WINSYS_FEATURE_END () muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-glx-private.h0000664000175000017500000000255014723361714022037 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_WINSYS_GLX_PRIVATE_H #define __COGL_WINSYS_GLX_PRIVATE_H COGL_EXPORT const CoglWinsysVtable * _cogl_winsys_glx_get_vtable (void); #endif /* __COGL_WINSYS_GLX_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-texture-pixmap-x11-private.h0000664000175000017500000000554114723361714023321 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H #define __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H #include #include #include #include #ifdef COGL_HAS_GLX_SUPPORT #include #endif #include "cogl-object-private.h" #include "cogl-texture-private.h" #include "cogl-texture-pixmap-x11.h" typedef struct _CoglDamageRectangle CoglDamageRectangle; struct _CoglDamageRectangle { unsigned int x1; unsigned int y1; unsigned int x2; unsigned int y2; }; /* For stereo, there are a pair of textures, but we want to share most * other state (the GLXPixmap, visual, etc.) The way we do this is that * the left-eye texture has all the state (there is in fact, no internal * difference between the a MONO and a LEFT texture ), and the * right-eye texture simply points to the left eye texture, with all * other fields ignored. */ typedef enum { COGL_TEXTURE_PIXMAP_MONO, COGL_TEXTURE_PIXMAP_LEFT, COGL_TEXTURE_PIXMAP_RIGHT } CoglTexturePixmapStereoMode; struct _CoglTexturePixmapX11 { CoglTexture _parent; CoglTexturePixmapStereoMode stereo_mode; CoglTexturePixmapX11 *left; /* Set only if stereo_mode=RIGHT */ Pixmap pixmap; CoglTexture *tex; unsigned int depth; Visual *visual; XImage *image; XShmSegmentInfo shm_info; Damage damage; CoglTexturePixmapX11ReportLevel damage_report_level; gboolean damage_owned; CoglDamageRectangle damage_rect; void *winsys; /* During the pre_paint method, this will be set to TRUE if we should use the winsys texture, otherwise we will use the regular texture */ gboolean use_winsys_texture; }; #endif /* __COGL_TEXTURE_PIXMAP_X11_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-egl-private.h0000664000175000017500000001437414723361714022023 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_WINSYS_EGL_PRIVATE_H #define __COGL_WINSYS_EGL_PRIVATE_H #include "cogl-defines.h" #include "cogl-context.h" #include "cogl-context-private.h" #include "cogl-framebuffer-private.h" #include "winsys/cogl-winsys-private.h" /* XXX: depending on what version of Mesa you have then * eglQueryWaylandBuffer may take a wl_buffer or wl_resource argument * and the EGL header will only forward declare the corresponding * type. * * The use of wl_buffer has been deprecated and so internally we * assume that eglQueryWaylandBuffer takes a wl_resource but for * compatibility we forward declare wl_resource in case we are * building with EGL headers that still use wl_buffer. * * Placing the forward declaration here means it comes before we * #include cogl-winsys-egl-feature-functions.h bellow which * declares lots of function pointers for accessing EGL extensions * and cogl-winsys-egl.c will include this header before it also * includes cogl-winsys-egl-feature-functions.h that may depend * on this type. */ #ifdef EGL_WL_bind_wayland_display struct wl_resource; #endif typedef struct _CoglWinsysEGLVtable { gboolean (* display_setup) (CoglDisplay *display, GError **error); void (* display_destroy) (CoglDisplay *display); gboolean (* context_created) (CoglDisplay *display, GError **error); void (* cleanup_context) (CoglDisplay *display); gboolean (* context_init) (CoglContext *context, GError **error); void (* context_deinit) (CoglContext *context); gboolean (* onscreen_init) (CoglOnscreen *onscreen, EGLConfig config, GError **error); void (* onscreen_deinit) (CoglOnscreen *onscreen); int (* add_config_attributes) (CoglDisplay *display, CoglFramebufferConfig *config, EGLint *attributes); gboolean (* choose_config) (CoglDisplay *display, EGLint *attributes, EGLConfig *out_config, GError **error); } CoglWinsysEGLVtable; typedef enum _CoglEGLWinsysFeature { COGL_EGL_WINSYS_FEATURE_SWAP_REGION =1L<<0, COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP =1L<<1, COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER =1L<<2, COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT =1L<<3, COGL_EGL_WINSYS_FEATURE_BUFFER_AGE =1L<<4, COGL_EGL_WINSYS_FEATURE_FENCE_SYNC =1L<<5, COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT =1L<<6, COGL_EGL_WINSYS_FEATURE_CONTEXT_PRIORITY =1L<<7, } CoglEGLWinsysFeature; typedef struct _CoglRendererEGL { CoglEGLWinsysFeature private_features; EGLDisplay edpy; EGLint egl_version_major; EGLint egl_version_minor; CoglClosure *resize_notify_idle; /* Data specific to the EGL platform */ void *platform; /* vtable for platform specific parts */ const CoglWinsysEGLVtable *platform_vtable; /* Function pointers for EGL specific extensions */ #define COGL_WINSYS_FEATURE_BEGIN(a, b, c, d) #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ ret (APIENTRY * pf_ ## name) args; #define COGL_WINSYS_FEATURE_END() #include "winsys/cogl-winsys-egl-feature-functions.h" #undef COGL_WINSYS_FEATURE_BEGIN #undef COGL_WINSYS_FEATURE_FUNCTION #undef COGL_WINSYS_FEATURE_END } CoglRendererEGL; typedef struct _CoglDisplayEGL { EGLContext egl_context; EGLSurface dummy_surface; EGLSurface egl_surface; EGLConfig egl_config; gboolean found_egl_config; EGLSurface current_read_surface; EGLSurface current_draw_surface; EGLContext current_context; /* Platform specific display data */ void *platform; } CoglDisplayEGL; typedef struct _CoglContextEGL { EGLSurface saved_draw_surface; EGLSurface saved_read_surface; } CoglContextEGL; typedef struct _CoglOnscreenEGL { EGLSurface egl_surface; gboolean pending_resize_notify; /* Platform specific data */ void *platform; } CoglOnscreenEGL; COGL_EXPORT const CoglWinsysVtable * _cogl_winsys_egl_get_vtable (void); COGL_EXPORT EGLBoolean _cogl_winsys_egl_make_current (CoglDisplay *display, EGLSurface draw, EGLSurface read, EGLContext context); COGL_EXPORT EGLBoolean _cogl_winsys_egl_ensure_current (CoglDisplay *display); #ifdef EGL_KHR_image_base EGLImageKHR _cogl_egl_create_image (CoglContext *ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attribs); void _cogl_egl_destroy_image (CoglContext *ctx, EGLImageKHR image); #endif #ifdef EGL_WL_bind_wayland_display gboolean _cogl_egl_query_wayland_buffer (CoglContext *ctx, struct wl_resource *buffer, int attribute, int *value); #endif COGL_EXPORT gboolean _cogl_winsys_egl_renderer_connect_common (CoglRenderer *renderer, GError **error); #endif /* __COGL_WINSYS_EGL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-texture-pixmap-x11.c0000664000175000017500000011054114723361714021641 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts * Johan Bilien * Robert Bragg */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-util.h" #include "cogl-texture-pixmap-x11.h" #include "cogl-texture-pixmap-x11-private.h" #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d-private.h" #include "cogl-texture-2d-sliced.h" #include "cogl-context-private.h" #include "cogl-display-private.h" #include "cogl-renderer-private.h" #include "cogl-object-private.h" #include "cogl-xlib.h" #include "cogl-xlib-renderer-private.h" #include "cogl-x11-renderer-private.h" #include "cogl-private.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "winsys/cogl-winsys-private.h" #include #include #include #include #include #include #include static void _cogl_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap); COGL_TEXTURE_DEFINE (TexturePixmapX11, texture_pixmap_x11); COGL_GTYPE_DEFINE_CLASS (TexturePixmapX11, texture_pixmap_x11); static const CoglTextureVtable cogl_texture_pixmap_x11_vtable; uint32_t cogl_texture_pixmap_x11_error_quark (void) { return g_quark_from_static_string ("cogl-texture-pixmap-error-quark"); } static void cogl_damage_rectangle_union (CoglDamageRectangle *damage_rect, int x, int y, int width, int height) { /* If the damage region is empty then we'll just copy the new rectangle directly */ if (damage_rect->x1 == damage_rect->x2 || damage_rect->y1 == damage_rect->y2) { damage_rect->x1 = x; damage_rect->y1 = y; damage_rect->x2 = x + width; damage_rect->y2 = y + height; } else { if (damage_rect->x1 > x) damage_rect->x1 = x; if (damage_rect->y1 > y) damage_rect->y1 = y; if (damage_rect->x2 < x + width) damage_rect->x2 = x + width; if (damage_rect->y2 < y + height) damage_rect->y2 = y + height; } } static gboolean cogl_damage_rectangle_is_whole (const CoglDamageRectangle *damage_rect, unsigned int width, unsigned int height) { return (damage_rect->x1 == 0 && damage_rect->y1 == 0 && damage_rect->x2 == width && damage_rect->y2 == height); } static const CoglWinsysVtable * _cogl_texture_pixmap_x11_get_winsys (CoglTexturePixmapX11 *tex_pixmap) { /* FIXME: A CoglContext should be reachable from a CoglTexture * pointer */ _COGL_GET_CONTEXT (ctx, NULL); return ctx->display->renderer->winsys_vtable; } static void process_damage_event (CoglTexturePixmapX11 *tex_pixmap, XDamageNotifyEvent *damage_event) { CoglTexture *tex = COGL_TEXTURE (tex_pixmap); Display *display; enum { DO_NOTHING, NEEDS_SUBTRACT, NEED_BOUNDING_BOX } handle_mode; const CoglWinsysVtable *winsys; _COGL_GET_CONTEXT (ctxt, NO_RETVAL); display = cogl_xlib_renderer_get_display (ctxt->display->renderer); COGL_NOTE (TEXTURE_PIXMAP, "Damage event received for %p", tex_pixmap); switch (tex_pixmap->damage_report_level) { case COGL_TEXTURE_PIXMAP_X11_DAMAGE_RAW_RECTANGLES: /* For raw rectangles we don't need do look at the damage region at all because the damage area is directly given in the event struct and the reporting of events is not affected by clearing the damage region */ handle_mode = DO_NOTHING; break; case COGL_TEXTURE_PIXMAP_X11_DAMAGE_DELTA_RECTANGLES: case COGL_TEXTURE_PIXMAP_X11_DAMAGE_NON_EMPTY: /* For delta rectangles and non empty we'll query the damage region for the bounding box */ handle_mode = NEED_BOUNDING_BOX; break; case COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX: /* For bounding box we need to clear the damage region but we don't actually care what it was because the damage event itself contains the bounding box of the region */ handle_mode = NEEDS_SUBTRACT; break; default: g_assert_not_reached (); } /* If the damage already covers the whole rectangle then we don't need to request the bounding box of the region because we're going to update the whole texture anyway. */ if (cogl_damage_rectangle_is_whole (&tex_pixmap->damage_rect, tex->width, tex->height)) { if (handle_mode != DO_NOTHING) XDamageSubtract (display, tex_pixmap->damage, None, None); } else if (handle_mode == NEED_BOUNDING_BOX) { XserverRegion parts; int r_count; XRectangle r_bounds; XRectangle *r_damage; /* We need to extract the damage region so we can get the bounding box */ parts = XFixesCreateRegion (display, 0, 0); XDamageSubtract (display, tex_pixmap->damage, None, parts); r_damage = XFixesFetchRegionAndBounds (display, parts, &r_count, &r_bounds); cogl_damage_rectangle_union (&tex_pixmap->damage_rect, r_bounds.x, r_bounds.y, r_bounds.width, r_bounds.height); if (r_damage) XFree (r_damage); XFixesDestroyRegion (display, parts); } else { if (handle_mode == NEEDS_SUBTRACT) /* We still need to subtract from the damage region but we don't care what the region actually was */ XDamageSubtract (display, tex_pixmap->damage, None, None); cogl_damage_rectangle_union (&tex_pixmap->damage_rect, damage_event->area.x, damage_event->area.y, damage_event->area.width, damage_event->area.height); } if (tex_pixmap->winsys) { /* If we're using the texture from pixmap extension then there's no point in getting the region and we can just mark that the texture needs updating */ winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); winsys->texture_pixmap_x11_damage_notify (tex_pixmap); } } static int _cogl_xlib_get_damage_base (void) { CoglX11Renderer *x11_renderer; _COGL_GET_CONTEXT (ctxt, -1); x11_renderer = (CoglX11Renderer *) _cogl_xlib_renderer_get_data (ctxt->display->renderer); return x11_renderer->damage_base; } static CoglFilterReturn _cogl_texture_pixmap_x11_filter (XEvent *event, void *data) { CoglTexturePixmapX11 *tex_pixmap = data; int damage_base; _COGL_GET_CONTEXT (ctxt, COGL_FILTER_CONTINUE); damage_base = _cogl_xlib_get_damage_base (); if (event->type == damage_base + XDamageNotify) { XDamageNotifyEvent *damage_event = (XDamageNotifyEvent *) event; if (damage_event->damage == tex_pixmap->damage) process_damage_event (tex_pixmap, damage_event); } return COGL_FILTER_CONTINUE; } static void set_damage_object_internal (CoglContext *ctx, CoglTexturePixmapX11 *tex_pixmap, Damage damage, CoglTexturePixmapX11ReportLevel report_level) { Display *display = cogl_xlib_renderer_get_display (ctx->display->renderer); if (tex_pixmap->damage) { cogl_xlib_renderer_remove_filter (ctx->display->renderer, _cogl_texture_pixmap_x11_filter, tex_pixmap); if (tex_pixmap->damage_owned) { XDamageDestroy (display, tex_pixmap->damage); tex_pixmap->damage_owned = FALSE; } } tex_pixmap->damage = damage; tex_pixmap->damage_report_level = report_level; if (damage) cogl_xlib_renderer_add_filter (ctx->display->renderer, _cogl_texture_pixmap_x11_filter, tex_pixmap); } static CoglTexturePixmapX11 * _cogl_texture_pixmap_x11_new (CoglContext *ctxt, uint32_t pixmap, gboolean automatic_updates, CoglTexturePixmapStereoMode stereo_mode, GError **error) { CoglTexturePixmapX11 *tex_pixmap = g_new (CoglTexturePixmapX11, 1); Display *display = cogl_xlib_renderer_get_display (ctxt->display->renderer); Window pixmap_root_window; int pixmap_x, pixmap_y; unsigned int pixmap_width, pixmap_height; unsigned int pixmap_border_width; CoglPixelFormat internal_format; CoglTexture *tex = COGL_TEXTURE (tex_pixmap); XWindowAttributes window_attributes; int damage_base; const CoglWinsysVtable *winsys; if (!XGetGeometry (display, pixmap, &pixmap_root_window, &pixmap_x, &pixmap_y, &pixmap_width, &pixmap_height, &pixmap_border_width, &tex_pixmap->depth)) { g_free (tex_pixmap); g_set_error_literal (error, COGL_TEXTURE_PIXMAP_X11_ERROR, COGL_TEXTURE_PIXMAP_X11_ERROR_X11, "Unable to query pixmap size"); return NULL; } /* Note: the detailed pixel layout doesn't matter here, we are just * interested in RGB vs RGBA... */ internal_format = (tex_pixmap->depth >= 32 ? COGL_PIXEL_FORMAT_RGBA_8888_PRE : COGL_PIXEL_FORMAT_RGB_888); _cogl_texture_init (tex, ctxt, pixmap_width, pixmap_height, internal_format, NULL, /* no loader */ &cogl_texture_pixmap_x11_vtable); tex_pixmap->pixmap = pixmap; tex_pixmap->stereo_mode = stereo_mode; tex_pixmap->left = NULL; tex_pixmap->image = NULL; tex_pixmap->shm_info.shmid = -1; tex_pixmap->tex = NULL; tex_pixmap->damage_owned = FALSE; tex_pixmap->damage = 0; /* We need a visual to use for shared memory images so we'll query it from the pixmap's root window */ if (!XGetWindowAttributes (display, pixmap_root_window, &window_attributes)) { g_free (tex_pixmap); g_set_error_literal (error, COGL_TEXTURE_PIXMAP_X11_ERROR, COGL_TEXTURE_PIXMAP_X11_ERROR_X11, "Unable to query root window attributes"); return NULL; } tex_pixmap->visual = window_attributes.visual; /* If automatic updates are requested and the Xlib connection supports damage events then we'll register a damage object on the pixmap */ damage_base = _cogl_xlib_get_damage_base (); if (automatic_updates && damage_base >= 0) { Damage damage = XDamageCreate (display, pixmap, XDamageReportBoundingBox); set_damage_object_internal (ctxt, tex_pixmap, damage, COGL_TEXTURE_PIXMAP_X11_DAMAGE_BOUNDING_BOX); tex_pixmap->damage_owned = TRUE; } /* Assume the entire pixmap is damaged to begin with */ tex_pixmap->damage_rect.x1 = 0; tex_pixmap->damage_rect.x2 = pixmap_width; tex_pixmap->damage_rect.y1 = 0; tex_pixmap->damage_rect.y2 = pixmap_height; winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); if (winsys->texture_pixmap_x11_create) { tex_pixmap->use_winsys_texture = winsys->texture_pixmap_x11_create (tex_pixmap); } else tex_pixmap->use_winsys_texture = FALSE; if (!tex_pixmap->use_winsys_texture) tex_pixmap->winsys = NULL; _cogl_texture_set_allocated (tex, internal_format, pixmap_width, pixmap_height); return _cogl_texture_pixmap_x11_object_new (tex_pixmap); } CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new (CoglContext *ctxt, uint32_t pixmap, gboolean automatic_updates, GError **error) { return _cogl_texture_pixmap_x11_new (ctxt, pixmap, automatic_updates, COGL_TEXTURE_PIXMAP_MONO, error); } CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new_left (CoglContext *ctxt, uint32_t pixmap, gboolean automatic_updates, GError **error) { return _cogl_texture_pixmap_x11_new (ctxt, pixmap, automatic_updates, COGL_TEXTURE_PIXMAP_LEFT, error); } CoglTexturePixmapX11 * cogl_texture_pixmap_x11_new_right (CoglTexturePixmapX11 *tfp_left) { CoglTexture *texture_left = COGL_TEXTURE (tfp_left); CoglTexturePixmapX11 *tfp_right; CoglPixelFormat internal_format; g_return_val_if_fail (tfp_left->stereo_mode == COGL_TEXTURE_PIXMAP_LEFT, NULL); tfp_right = g_new0 (CoglTexturePixmapX11, 1); tfp_right->stereo_mode = COGL_TEXTURE_PIXMAP_RIGHT; tfp_right->left = cogl_object_ref (tfp_left); internal_format = (tfp_left->depth >= 32 ? COGL_PIXEL_FORMAT_RGBA_8888_PRE : COGL_PIXEL_FORMAT_RGB_888); _cogl_texture_init (COGL_TEXTURE (tfp_right), texture_left->context, texture_left->width, texture_left->height, internal_format, NULL, /* no loader */ &cogl_texture_pixmap_x11_vtable); _cogl_texture_set_allocated (COGL_TEXTURE (tfp_right), internal_format, texture_left->width, texture_left->height); return _cogl_texture_pixmap_x11_object_new (tfp_right); } static gboolean _cogl_texture_pixmap_x11_allocate (CoglTexture *tex, GError **error) { return TRUE; } /* Tries to allocate enough shared mem to handle a full size * update size of the X Pixmap. */ static void try_alloc_shm (CoglTexturePixmapX11 *tex_pixmap) { CoglTexture *tex = COGL_TEXTURE (tex_pixmap); XImage *dummy_image; Display *display; _COGL_GET_CONTEXT (ctx, NO_RETVAL); display = cogl_xlib_renderer_get_display (ctx->display->renderer); if (!XShmQueryExtension (display)) return; /* We are creating a dummy_image so we can have Xlib calculate * image->bytes_per_line - including any magic padding it may * want - for the largest possible ximage we might need to use * when handling updates to the texture. * * Note: we pass a NULL shminfo here, but that has no bearing * on the setup of the XImage, except that ximage->obdata will * == NULL. */ dummy_image = XShmCreateImage (display, tex_pixmap->visual, tex_pixmap->depth, ZPixmap, NULL, NULL, /* shminfo, */ tex->width, tex->height); if (!dummy_image) goto failed_image_create; tex_pixmap->shm_info.shmid = shmget (IPC_PRIVATE, dummy_image->bytes_per_line * dummy_image->height, IPC_CREAT | 0777); if (tex_pixmap->shm_info.shmid == -1) goto failed_shmget; tex_pixmap->shm_info.shmaddr = shmat (tex_pixmap->shm_info.shmid, 0, 0); if (tex_pixmap->shm_info.shmaddr == (void *) -1) goto failed_shmat; tex_pixmap->shm_info.readOnly = False; if (XShmAttach (display, &tex_pixmap->shm_info) == 0) goto failed_xshmattach; XDestroyImage (dummy_image); return; failed_xshmattach: g_warning ("XShmAttach failed"); shmdt (tex_pixmap->shm_info.shmaddr); failed_shmat: g_warning ("shmat failed"); shmctl (tex_pixmap->shm_info.shmid, IPC_RMID, 0); failed_shmget: g_warning ("shmget failed"); XDestroyImage (dummy_image); failed_image_create: tex_pixmap->shm_info.shmid = -1; } void cogl_texture_pixmap_x11_update_area (CoglTexturePixmapX11 *tex_pixmap, int x, int y, int width, int height) { /* We'll queue the update for both the GLX texture and the regular texture because we can't determine which will be needed until we actually render something */ if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) tex_pixmap = tex_pixmap->left; if (tex_pixmap->winsys) { const CoglWinsysVtable *winsys; winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); winsys->texture_pixmap_x11_damage_notify (tex_pixmap); } cogl_damage_rectangle_union (&tex_pixmap->damage_rect, x, y, width, height); } gboolean cogl_texture_pixmap_x11_is_using_tfp_extension (CoglTexturePixmapX11 *tex_pixmap) { if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) tex_pixmap = tex_pixmap->left; return !!tex_pixmap->winsys; } static CoglTexture * create_fallback_texture (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format) { CoglTexture *tex; GError *skip_error = NULL; /* First try creating a fast-path non-sliced texture */ tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); _cogl_texture_set_internal_format (tex, internal_format); /* TODO: instead of allocating storage here it would be better * if we had some api that let us just check that the size is * supported by the hardware so storage could be allocated * lazily when uploading data. */ if (!cogl_texture_allocate (tex, &skip_error)) { g_error_free (skip_error); cogl_object_unref (tex); tex = NULL; } if (!tex) { CoglTexture2DSliced *tex_2ds = cogl_texture_2d_sliced_new_with_size (ctx, width, height, COGL_TEXTURE_MAX_WASTE); tex = COGL_TEXTURE (tex_2ds); _cogl_texture_set_internal_format (tex, internal_format); } return tex; } static void _cogl_texture_pixmap_x11_update_image_texture (CoglTexturePixmapX11 *tex_pixmap) { CoglTexture *tex = COGL_TEXTURE (tex_pixmap); Display *display; Visual *visual; CoglPixelFormat image_format; XImage *image; int src_x, src_y; int x, y, width, height; int bpp; int offset; GError *ignore = NULL; _COGL_GET_CONTEXT (ctx, NO_RETVAL); display = cogl_xlib_renderer_get_display (ctx->display->renderer); visual = tex_pixmap->visual; /* If the damage region is empty then there's nothing to do */ if (tex_pixmap->damage_rect.x2 == tex_pixmap->damage_rect.x1) return; x = tex_pixmap->damage_rect.x1; y = tex_pixmap->damage_rect.y1; width = tex_pixmap->damage_rect.x2 - x; height = tex_pixmap->damage_rect.y2 - y; /* We lazily create the texture the first time it is needed in case this texture can be entirely handled using the GLX texture instead */ if (tex_pixmap->tex == NULL) { CoglPixelFormat texture_format; texture_format = (tex_pixmap->depth >= 32 ? COGL_PIXEL_FORMAT_RGBA_8888_PRE : COGL_PIXEL_FORMAT_RGB_888); tex_pixmap->tex = create_fallback_texture (ctx, tex->width, tex->height, texture_format); } if (tex_pixmap->image == NULL) { /* If we also haven't got a shm segment then this must be the first time we've tried to update, so lets try allocating shm first */ if (tex_pixmap->shm_info.shmid == -1) try_alloc_shm (tex_pixmap); if (tex_pixmap->shm_info.shmid == -1) { COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XGetImage", tex_pixmap); /* We'll fallback to using a regular XImage. We'll download the entire area instead of a sub region because presumably if this is the first update then the entire pixmap is needed anyway and it saves trying to manually allocate an XImage at the right size */ tex_pixmap->image = XGetImage (display, tex_pixmap->pixmap, 0, 0, tex->width, tex->height, AllPlanes, ZPixmap); image = tex_pixmap->image; src_x = x; src_y = y; } else { COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XShmGetImage", tex_pixmap); /* Create a temporary image using the beginning of the shared memory segment and the right size for the region we want to update. We need to reallocate the XImage every time because there is no XShmGetSubImage. */ image = XShmCreateImage (display, tex_pixmap->visual, tex_pixmap->depth, ZPixmap, NULL, &tex_pixmap->shm_info, width, height); image->data = tex_pixmap->shm_info.shmaddr; src_x = 0; src_y = 0; XShmGetImage (display, tex_pixmap->pixmap, image, x, y, AllPlanes); } } else { COGL_NOTE (TEXTURE_PIXMAP, "Updating %p using XGetSubImage", tex_pixmap); image = tex_pixmap->image; src_x = x; src_y = y; XGetSubImage (display, tex_pixmap->pixmap, x, y, width, height, AllPlanes, ZPixmap, image, x, y); } image_format = _cogl_util_pixel_format_from_masks (visual->red_mask, visual->green_mask, visual->blue_mask, image->depth, image->bits_per_pixel, image->byte_order == LSBFirst); g_return_if_fail (cogl_pixel_format_get_n_planes (image_format) == 1); bpp = cogl_pixel_format_get_bytes_per_pixel (image_format, 0); offset = image->bytes_per_line * src_y + bpp * src_x; _cogl_texture_set_region (tex_pixmap->tex, width, height, image_format, image->bytes_per_line, ((const uint8_t *) image->data) + offset, x, y, 0, /* level */ &ignore); /* If we have a shared memory segment then the XImage would be a temporary one with no data allocated so we can just XFree it */ if (tex_pixmap->shm_info.shmid != -1) XFree (image); memset (&tex_pixmap->damage_rect, 0, sizeof (CoglDamageRectangle)); } static void _cogl_texture_pixmap_x11_set_use_winsys_texture (CoglTexturePixmapX11 *tex_pixmap, gboolean new_value) { if (tex_pixmap->use_winsys_texture != new_value) { /* Notify cogl-pipeline.c that the texture's underlying GL texture * storage is changing so it knows it may need to bind a new texture * if the CoglTexture is reused with the same texture unit. */ _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (tex_pixmap)); tex_pixmap->use_winsys_texture = new_value; } } static void _cogl_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, gboolean needs_mipmap) { CoglTexturePixmapStereoMode stereo_mode = tex_pixmap->stereo_mode; if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) tex_pixmap = tex_pixmap->left; if (tex_pixmap->winsys) { const CoglWinsysVtable *winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); if (winsys->texture_pixmap_x11_update (tex_pixmap, stereo_mode, needs_mipmap)) { _cogl_texture_pixmap_x11_set_use_winsys_texture (tex_pixmap, TRUE); return; } } /* If it didn't work then fallback to using XGetImage. This may be temporary */ _cogl_texture_pixmap_x11_set_use_winsys_texture (tex_pixmap, FALSE); _cogl_texture_pixmap_x11_update_image_texture (tex_pixmap); } static CoglTexture * _cogl_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap) { CoglTexturePixmapX11 *original_pixmap = tex_pixmap; CoglTexture *tex; int i; CoglTexturePixmapStereoMode stereo_mode = tex_pixmap->stereo_mode; if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) tex_pixmap = tex_pixmap->left; /* We try getting the texture twice, once without flushing the updates and once with. If pre_paint has been called already then we should have a good idea of which texture to use so we don't want to mess with that by ensuring the updates. However, if we couldn't find a texture then we'll just make a best guess by flushing without expecting mipmap support and try again. This would happen for example if an application calls get_gl_texture before the first paint */ for (i = 0; i < 2; i++) { if (tex_pixmap->use_winsys_texture) { const CoglWinsysVtable *winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); tex = winsys->texture_pixmap_x11_get_texture (tex_pixmap, stereo_mode); } else tex = tex_pixmap->tex; if (tex) return tex; _cogl_texture_pixmap_x11_update (original_pixmap, FALSE); } g_assert_not_reached (); return NULL; } static gboolean _cogl_texture_pixmap_x11_set_region (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, int level, CoglBitmap *bmp, GError **error) { /* This doesn't make much sense for texture from pixmap so it's not supported */ g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Explicitly setting a region of a TFP texture " "unsupported"); return FALSE; } static gboolean _cogl_texture_pixmap_x11_get_data (CoglTexture *tex, CoglPixelFormat format, int rowstride, uint8_t *data) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ return cogl_texture_get_data (child_tex, format, rowstride, data); } static void _cogl_texture_pixmap_x11_foreach_sub_texture_in_region (CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (child_tex), virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, callback, user_data); } static int _cogl_texture_pixmap_x11_get_max_waste (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); return cogl_texture_get_max_waste (child_tex); } static gboolean _cogl_texture_pixmap_x11_is_sliced (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); return cogl_texture_is_sliced (child_tex); } static gboolean _cogl_texture_pixmap_x11_can_hardware_repeat (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); return _cogl_texture_can_hardware_repeat (child_tex); } static void _cogl_texture_pixmap_x11_transform_coords_to_gl (CoglTexture *tex, float *s, float *t) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ _cogl_texture_transform_coords_to_gl (child_tex, s, t); } static CoglTransformResult _cogl_texture_pixmap_x11_transform_quad_coords_to_gl (CoglTexture *tex, float *coords) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ return _cogl_texture_transform_quad_coords_to_gl (child_tex, coords); } static gboolean _cogl_texture_pixmap_x11_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ return cogl_texture_get_gl_texture (child_tex, out_gl_handle, out_gl_target); } static void _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ _cogl_texture_gl_flush_legacy_texobj_filters (child_tex, min_filter, mag_filter); } static void _cogl_texture_pixmap_x11_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex; _cogl_texture_pixmap_x11_update (tex_pixmap, !!(flags & COGL_TEXTURE_NEEDS_MIPMAP)); child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); _cogl_texture_pre_paint (child_tex, flags); } static void _cogl_texture_pixmap_x11_ensure_non_quad_rendering (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ _cogl_texture_ensure_non_quad_rendering (child_tex); } static void _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ _cogl_texture_gl_flush_legacy_texobj_wrap_modes (child_tex, wrap_mode_s, wrap_mode_t); } static CoglPixelFormat _cogl_texture_pixmap_x11_get_format (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); /* Forward on to the child texture */ return _cogl_texture_get_format (child_tex); } static GLenum _cogl_texture_pixmap_x11_get_gl_format (CoglTexture *tex) { CoglTexturePixmapX11 *tex_pixmap = COGL_TEXTURE_PIXMAP_X11 (tex); CoglTexture *child_tex = _cogl_texture_pixmap_x11_get_texture (tex_pixmap); return _cogl_texture_gl_get_format (child_tex); } static void _cogl_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) { Display *display; _COGL_GET_CONTEXT (ctxt, NO_RETVAL); if (tex_pixmap->stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) { cogl_object_unref (tex_pixmap->left); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_pixmap)); return; } display = cogl_xlib_renderer_get_display (ctxt->display->renderer); set_damage_object_internal (ctxt, tex_pixmap, 0, 0); if (tex_pixmap->image) XDestroyImage (tex_pixmap->image); if (tex_pixmap->shm_info.shmid != -1) { XShmDetach (display, &tex_pixmap->shm_info); shmdt (tex_pixmap->shm_info.shmaddr); shmctl (tex_pixmap->shm_info.shmid, IPC_RMID, 0); } if (tex_pixmap->tex) cogl_object_unref (tex_pixmap->tex); if (tex_pixmap->winsys) { const CoglWinsysVtable *winsys = _cogl_texture_pixmap_x11_get_winsys (tex_pixmap); winsys->texture_pixmap_x11_free (tex_pixmap); } /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_pixmap)); } static const CoglTextureVtable cogl_texture_pixmap_x11_vtable = { FALSE, /* not primitive */ _cogl_texture_pixmap_x11_allocate, _cogl_texture_pixmap_x11_set_region, NULL, /* is_get_data_supported */ _cogl_texture_pixmap_x11_get_data, _cogl_texture_pixmap_x11_foreach_sub_texture_in_region, _cogl_texture_pixmap_x11_get_max_waste, _cogl_texture_pixmap_x11_is_sliced, _cogl_texture_pixmap_x11_can_hardware_repeat, _cogl_texture_pixmap_x11_transform_coords_to_gl, _cogl_texture_pixmap_x11_transform_quad_coords_to_gl, _cogl_texture_pixmap_x11_get_gl_texture, _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_filters, _cogl_texture_pixmap_x11_pre_paint, _cogl_texture_pixmap_x11_ensure_non_quad_rendering, _cogl_texture_pixmap_x11_gl_flush_legacy_texobj_wrap_modes, _cogl_texture_pixmap_x11_get_format, _cogl_texture_pixmap_x11_get_gl_format, NULL /* set_auto_mipmap */ }; muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-egl-feature-functions.h0000664000175000017500000001464214723361714024010 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This can be included multiple times with different definitions for * the COGL_WINSYS_FEATURE_* functions. */ /* Macro prototypes: * COGL_WINSYS_FEATURE_BEGIN (name, namespaces, extension_names, * implied_private_egl_feature_flags) * COGL_WINSYS_FEATURE_FUNCTION (return_type, function_name, * (arguments)) * ... * COGL_WINSYS_FEATURE_END () * * Note: You can list multiple namespace and extension names if the * corresponding _FEATURE_FUNCTIONS have the same semantics accross * the different extension variants. * * XXX: NB: Don't add a trailing semicolon when using these macros */ COGL_WINSYS_FEATURE_BEGIN (swap_region, "NOK\0", "swap_region\0", COGL_EGL_WINSYS_FEATURE_SWAP_REGION) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersRegion, (EGLDisplay dpy, EGLSurface surface, EGLint numRects, const EGLint *rects)) COGL_WINSYS_FEATURE_END () /* XXX: These macros can't handle falling back to looking for * EGL_KHR_image if EGL_KHR_image_base and EGL_KHR_image_pixmap aren't * found... */ #ifdef EGL_KHR_image_base COGL_WINSYS_FEATURE_BEGIN (image_base, "KHR\0", "image_base\0", 0) COGL_WINSYS_FEATURE_FUNCTION (EGLImageKHR, eglCreateImage, (EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list)) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroyImage, (EGLDisplay dpy, EGLImageKHR image)) COGL_WINSYS_FEATURE_END () #endif COGL_WINSYS_FEATURE_BEGIN (image_pixmap, "KHR\0", "image_pixmap\0", COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP) COGL_WINSYS_FEATURE_END () #ifdef EGL_WL_bind_wayland_display COGL_WINSYS_FEATURE_BEGIN (bind_wayland_display, "WL\0", "bind_wayland_display\0", COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_WAYLAND_BUFFER) COGL_WINSYS_FEATURE_FUNCTION (EGLImageKHR, eglBindWaylandDisplay, (EGLDisplay dpy, struct wl_display *wayland_display)) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglUnbindWaylandDisplay, (EGLDisplay dpy, struct wl_display *wayland_display)) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglQueryWaylandBuffer, (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value)) COGL_WINSYS_FEATURE_END () #endif /* EGL_WL_bind_wayland_display */ COGL_WINSYS_FEATURE_BEGIN (create_context, "KHR\0", "create_context\0", COGL_EGL_WINSYS_FEATURE_CREATE_CONTEXT) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (buffer_age, "EXT\0", "buffer_age\0", COGL_EGL_WINSYS_FEATURE_BUFFER_AGE) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (swap_buffers_with_damage, "EXT\0", "swap_buffers_with_damage\0", 0) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglSwapBuffersWithDamage, (EGLDisplay dpy, EGLSurface surface, const EGLint *rects, EGLint n_rects)) COGL_WINSYS_FEATURE_END () #if defined(EGL_KHR_fence_sync) || defined(EGL_KHR_reusable_sync) COGL_WINSYS_FEATURE_BEGIN (fence_sync, "KHR\0", "fence_sync\0", COGL_EGL_WINSYS_FEATURE_FENCE_SYNC) COGL_WINSYS_FEATURE_FUNCTION (EGLSyncKHR, eglCreateSync, (EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)) COGL_WINSYS_FEATURE_FUNCTION (EGLint, eglClientWaitSync, (EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout)) COGL_WINSYS_FEATURE_FUNCTION (EGLBoolean, eglDestroySync, (EGLDisplay dpy, EGLSyncKHR sync)) COGL_WINSYS_FEATURE_END () #endif COGL_WINSYS_FEATURE_BEGIN (surfaceless_context, "KHR\0", "surfaceless_context\0", COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) COGL_WINSYS_FEATURE_END () COGL_WINSYS_FEATURE_BEGIN (context_priority, "IMG\0", "context_priority\0", COGL_EGL_WINSYS_FEATURE_CONTEXT_PRIORITY) COGL_WINSYS_FEATURE_END () muffin-6.4.1/cogl/cogl/winsys/cogl-glx.h0000664000175000017500000000510214723361714017031 0ustar fabiofabio/* * Cogl * * A Low-Level GPU Graphics and Utilities API * * Copyright (C) 2014 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_GLX_H__ #define __COGL_GLX_H__ /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_GLX_H_INSIDE__ */ #ifndef __COGL_GLX_H_INSIDE__ #define __COGL_GLX_H_INSIDE__ #endif /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_GLX_ #endif #endif /* COGL_COMPILATION */ #include #include /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_GLX_ #warning #undef __COGL_H_INSIDE__ #undef __COGL_GLX_H_INSIDE__ #undef __COGL_MUST_UNDEF_COGL_H_INSIDE_COGL_GLX_ #endif #endif /* __COGL_GLX_H__ */ muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-glx.c0000664000175000017500000025317514723361714020375 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010,2011,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-i18n-private.h" #include "cogl-util.h" #include "cogl-feature-private.h" #include "cogl-context-private.h" #include "cogl-framebuffer.h" #include "cogl-swap-chain-private.h" #include "cogl-renderer-private.h" #include "cogl-glx-renderer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-glx-display-private.h" #include "cogl-private.h" #include "cogl-texture-2d-private.h" #include "cogl-frame-info-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #include "cogl-swap-chain-private.h" #include "cogl-xlib-renderer.h" #include "cogl-util.h" #include "cogl-poll-private.h" #include "cogl-version.h" #include "cogl-glx.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "winsys/cogl-winsys-private.h" #include "winsys/cogl-winsys-glx-private.h" #include #include #include #include #include #include #include #include #include #include #include /* This is a relatively new extension */ #ifndef GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV #define GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV 0x20F7 #endif #define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask) #define MAX_GLX_CONFIG_ATTRIBS 30 typedef struct _CoglContextGLX { GLXDrawable current_drawable; } CoglContextGLX; typedef struct _CoglOnscreenXlib { Window xwin; int x, y; CoglOutput *output; } CoglOnscreenXlib; typedef struct _CoglOnscreenGLX { CoglOnscreenXlib _parent; GLXDrawable glxwin; uint32_t last_swap_vsync_counter; uint32_t pending_sync_notify; uint32_t pending_complete_notify; uint32_t pending_resize_notify; } CoglOnscreenGLX; typedef struct _CoglPixmapTextureEyeGLX { CoglTexture *glx_tex; gboolean bind_tex_image_queued; gboolean pixmap_bound; } CoglPixmapTextureEyeGLX; typedef struct _CoglTexturePixmapGLX { GLXPixmap glx_pixmap; gboolean has_mipmap_space; gboolean can_mipmap; CoglPixmapTextureEyeGLX left; CoglPixmapTextureEyeGLX right; } CoglTexturePixmapGLX; /* Define a set of arrays containing the functions required from GL for each winsys feature */ #define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version, \ name, namespaces, extension_names, \ feature_flags, \ winsys_feature) \ static const CoglFeatureFunction \ cogl_glx_feature_ ## name ## _funcs[] = { #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) \ { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglGLXRenderer, name) }, #define COGL_WINSYS_FEATURE_END() \ { NULL, 0 }, \ }; #include "winsys/cogl-winsys-glx-feature-functions.h" /* Define an array of features */ #undef COGL_WINSYS_FEATURE_BEGIN #define COGL_WINSYS_FEATURE_BEGIN(major_version, minor_version, \ name, namespaces, extension_names, \ feature_flags, \ winsys_feature) \ { major_version, minor_version, \ 0, namespaces, extension_names, \ 0, \ winsys_feature, \ cogl_glx_feature_ ## name ## _funcs }, #undef COGL_WINSYS_FEATURE_FUNCTION #define COGL_WINSYS_FEATURE_FUNCTION(ret, name, args) #undef COGL_WINSYS_FEATURE_END #define COGL_WINSYS_FEATURE_END() static const CoglFeatureData winsys_feature_data[] = { #include "winsys/cogl-winsys-glx-feature-functions.h" }; static GCallback _cogl_winsys_renderer_get_proc_address (CoglRenderer *renderer, const char *name, gboolean in_core) { CoglGLXRenderer *glx_renderer = renderer->winsys; /* The GLX_ARB_get_proc_address extension documents that this should * work for core functions too so we don't need to do anything * special with in_core */ return glx_renderer->glXGetProcAddress ((const GLubyte *) name); } static CoglOnscreen * find_onscreen_for_xid (CoglContext *context, uint32_t xid) { GList *l; for (l = context->framebuffers; l; l = l->next) { CoglFramebuffer *framebuffer = l->data; CoglOnscreenXlib *xlib_onscreen; if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN) continue; /* Does the GLXEvent have the GLXDrawable or the X Window? */ xlib_onscreen = COGL_ONSCREEN (framebuffer)->winsys; if (xlib_onscreen != NULL && xlib_onscreen->xwin == (Window)xid) return COGL_ONSCREEN (framebuffer); } return NULL; } static int64_t get_monotonic_time_ns (void) { struct timespec ts; clock_gettime (CLOCK_MONOTONIC, &ts); return ts.tv_sec * G_GINT64_CONSTANT (1000000000) + ts.tv_nsec; } static void ensure_ust_type (CoglRenderer *renderer, GLXDrawable drawable) { CoglGLXRenderer *glx_renderer = renderer->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); int64_t ust; int64_t msc; int64_t sbc; struct timeval tv; int64_t current_system_time; int64_t current_monotonic_time; if (glx_renderer->ust_type != COGL_GLX_UST_IS_UNKNOWN) return; glx_renderer->ust_type = COGL_GLX_UST_IS_OTHER; if (glx_renderer->glXGetSyncValues == NULL) goto out; if (!glx_renderer->glXGetSyncValues (xlib_renderer->xdpy, drawable, &ust, &msc, &sbc)) goto out; /* This is the time source that existing (buggy) linux drm drivers * use */ gettimeofday (&tv, NULL); current_system_time = (tv.tv_sec * G_GINT64_CONSTANT (1000000)) + tv.tv_usec; if (current_system_time > ust - 1000000 && current_system_time < ust + 1000000) { glx_renderer->ust_type = COGL_GLX_UST_IS_GETTIMEOFDAY; goto out; } /* This is the time source that the newer (fixed) linux drm * drivers use (Linux >= 3.8) */ current_monotonic_time = get_monotonic_time_ns () / 1000; if (current_monotonic_time > ust - 1000000 && current_monotonic_time < ust + 1000000) { glx_renderer->ust_type = COGL_GLX_UST_IS_MONOTONIC_TIME; goto out; } out: COGL_NOTE (WINSYS, "Classified OML system time as: %s", glx_renderer->ust_type == COGL_GLX_UST_IS_GETTIMEOFDAY ? "gettimeofday" : (glx_renderer->ust_type == COGL_GLX_UST_IS_MONOTONIC_TIME ? "monotonic" : "other")); return; } static int64_t ust_to_nanoseconds (CoglRenderer *renderer, GLXDrawable drawable, int64_t ust) { CoglGLXRenderer *glx_renderer = renderer->winsys; ensure_ust_type (renderer, drawable); switch (glx_renderer->ust_type) { case COGL_GLX_UST_IS_UNKNOWN: g_assert_not_reached (); break; case COGL_GLX_UST_IS_GETTIMEOFDAY: case COGL_GLX_UST_IS_MONOTONIC_TIME: return 1000 * ust; case COGL_GLX_UST_IS_OTHER: /* In this case the scale of UST is undefined so we can't easily * scale to nanoseconds. * * For example the driver may be reporting the rdtsc CPU counter * as UST values and so the scale would need to be determined * empirically. * * Potentially we could block for a known duration within * ensure_ust_type() to measure the timescale of UST but for now * we just ignore unknown time sources */ return 0; } return 0; } static int64_t _cogl_winsys_get_clock_time (CoglContext *context) { CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; if (!glx_renderer->glXWaitForMsc) return get_monotonic_time_ns (); /* We don't call ensure_ust_type() because we don't have a drawable * to work with. cogl_get_clock_time() is documented to only work * once a valid, non-zero, timestamp has been retrieved from Cogl. */ switch (glx_renderer->ust_type) { case COGL_GLX_UST_IS_UNKNOWN: case COGL_GLX_UST_IS_OTHER: return 0; case COGL_GLX_UST_IS_GETTIMEOFDAY: { struct timeval tv; gettimeofday(&tv, NULL); return tv.tv_sec * G_GINT64_CONSTANT (1000000000) + tv.tv_usec * G_GINT64_CONSTANT (1000); } case COGL_GLX_UST_IS_MONOTONIC_TIME: { return get_monotonic_time_ns (); } } g_assert_not_reached(); return 0; } static void flush_pending_notifications_cb (void *data, void *user_data) { CoglFramebuffer *framebuffer = data; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenGLX *glx_onscreen = onscreen->winsys; while (glx_onscreen->pending_sync_notify > 0 || glx_onscreen->pending_complete_notify > 0 || glx_onscreen->pending_resize_notify > 0) { if (glx_onscreen->pending_sync_notify > 0) { CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos); _cogl_onscreen_notify_frame_sync (onscreen, info); glx_onscreen->pending_sync_notify--; } if (glx_onscreen->pending_complete_notify > 0) { CoglFrameInfo *info = g_queue_pop_head (&onscreen->pending_frame_infos); _cogl_onscreen_notify_complete (onscreen, info); cogl_object_unref (info); glx_onscreen->pending_complete_notify--; } if (glx_onscreen->pending_resize_notify > 0) { _cogl_onscreen_notify_resize (onscreen); glx_onscreen->pending_resize_notify--; } } } } static void flush_pending_notifications_idle (void *user_data) { CoglContext *context = user_data; CoglRenderer *renderer = context->display->renderer; CoglGLXRenderer *glx_renderer = renderer->winsys; /* This needs to be disconnected before invoking the callbacks in * case the callbacks cause it to be queued again */ _cogl_closure_disconnect (glx_renderer->flush_notifications_idle); glx_renderer->flush_notifications_idle = NULL; g_list_foreach (context->framebuffers, flush_pending_notifications_cb, NULL); } static void set_sync_pending (CoglOnscreen *onscreen) { CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglGLXRenderer *glx_renderer = renderer->winsys; /* We only want to dispatch sync events when the application calls * cogl_context_dispatch so instead of immediately notifying we * queue an idle callback */ if (!glx_renderer->flush_notifications_idle) { glx_renderer->flush_notifications_idle = _cogl_poll_renderer_add_idle (renderer, flush_pending_notifications_idle, context, NULL); } glx_onscreen->pending_sync_notify++; } static void set_complete_pending (CoglOnscreen *onscreen) { CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglGLXRenderer *glx_renderer = renderer->winsys; /* We only want to notify swap completion when the application calls * cogl_context_dispatch so instead of immediately notifying we * queue an idle callback */ if (!glx_renderer->flush_notifications_idle) { glx_renderer->flush_notifications_idle = _cogl_poll_renderer_add_idle (renderer, flush_pending_notifications_idle, context, NULL); } glx_onscreen->pending_complete_notify++; } static void notify_swap_buffers (CoglContext *context, GLXBufferSwapComplete *swap_event) { CoglOnscreen *onscreen = find_onscreen_for_xid (context, (uint32_t)swap_event->drawable); CoglOnscreenGLX *glx_onscreen; if (!onscreen) return; glx_onscreen = onscreen->winsys; /* We only want to notify that the swap is complete when the application calls cogl_context_dispatch so instead of immediately notifying we'll set a flag to remember to notify later */ set_sync_pending (onscreen); if (swap_event->ust != 0) { CoglFrameInfo *info = g_queue_peek_head (&onscreen->pending_frame_infos); info->presentation_time = ust_to_nanoseconds (context->display->renderer, glx_onscreen->glxwin, swap_event->ust); } set_complete_pending (onscreen); } static void update_output (CoglOnscreen *onscreen) { CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglDisplay *display = context->display; CoglOutput *output; int width, height; width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); output = _cogl_xlib_renderer_output_for_rectangle (display->renderer, xlib_onscreen->x, xlib_onscreen->y, width, height); if (xlib_onscreen->output != output) { if (xlib_onscreen->output) cogl_object_unref (xlib_onscreen->output); xlib_onscreen->output = output; if (output) cogl_object_ref (xlib_onscreen->output); } } static void notify_resize (CoglContext *context, XConfigureEvent *configure_event) { CoglOnscreen *onscreen = find_onscreen_for_xid (context, configure_event->window); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglRenderer *renderer = context->display->renderer; CoglGLXRenderer *glx_renderer = renderer->winsys; CoglOnscreenGLX *glx_onscreen; CoglOnscreenXlib *xlib_onscreen; if (!onscreen) return; glx_onscreen = onscreen->winsys; xlib_onscreen = onscreen->winsys; _cogl_framebuffer_winsys_update_size (framebuffer, configure_event->width, configure_event->height); /* We only want to notify that a resize happened when the * application calls cogl_context_dispatch so instead of immediately * notifying we queue an idle callback */ if (!glx_renderer->flush_notifications_idle) { glx_renderer->flush_notifications_idle = _cogl_poll_renderer_add_idle (renderer, flush_pending_notifications_idle, context, NULL); } glx_onscreen->pending_resize_notify++; { int x, y; if (configure_event->send_event) { x = configure_event->x; y = configure_event->y; } else { Window child; XTranslateCoordinates (configure_event->display, configure_event->window, DefaultRootWindow (configure_event->display), 0, 0, &x, &y, &child); } xlib_onscreen->x = x; xlib_onscreen->y = y; update_output (onscreen); } } static CoglFilterReturn glx_event_filter_cb (XEvent *xevent, void *data) { CoglContext *context = data; #ifdef GLX_INTEL_swap_event CoglGLXRenderer *glx_renderer; #endif if (xevent->type == ConfigureNotify) { notify_resize (context, &xevent->xconfigure); /* we let ConfigureNotify pass through */ return COGL_FILTER_CONTINUE; } #ifdef GLX_INTEL_swap_event glx_renderer = context->display->renderer->winsys; if (xevent->type == (glx_renderer->glx_event_base + GLX_BufferSwapComplete)) { GLXBufferSwapComplete *swap_event = (GLXBufferSwapComplete *) xevent; notify_swap_buffers (context, swap_event); /* remove SwapComplete events from the queue */ return COGL_FILTER_REMOVE; } #endif /* GLX_INTEL_swap_event */ if (xevent->type == Expose) { CoglOnscreen *onscreen = find_onscreen_for_xid (context, xevent->xexpose.window); if (onscreen) { CoglOnscreenDirtyInfo info; info.x = xevent->xexpose.x; info.y = xevent->xexpose.y; info.width = xevent->xexpose.width; info.height = xevent->xexpose.height; _cogl_onscreen_queue_dirty (onscreen, &info); } return COGL_FILTER_CONTINUE; } return COGL_FILTER_CONTINUE; } static void _cogl_winsys_renderer_disconnect (CoglRenderer *renderer) { CoglGLXRenderer *glx_renderer = renderer->winsys; _cogl_xlib_renderer_disconnect (renderer); if (glx_renderer->libgl_module) g_module_close (glx_renderer->libgl_module); g_slice_free (CoglGLXRenderer, renderer->winsys); } static gboolean update_all_outputs (CoglRenderer *renderer) { GList *l; _COGL_GET_CONTEXT (context, FALSE); if (context->display == NULL) /* during connection */ return FALSE; if (context->display->renderer != renderer) return FALSE; for (l = context->framebuffers; l; l = l->next) { CoglFramebuffer *framebuffer = l->data; if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN) continue; update_output (COGL_ONSCREEN (framebuffer)); } return TRUE; } static void _cogl_winsys_renderer_outputs_changed (CoglRenderer *renderer) { update_all_outputs (renderer); } static gboolean resolve_core_glx_functions (CoglRenderer *renderer, GError **error) { CoglGLXRenderer *glx_renderer; glx_renderer = renderer->winsys; if (!g_module_symbol (glx_renderer->libgl_module, "glXQueryExtension", (void **) &glx_renderer->glXQueryExtension) || !g_module_symbol (glx_renderer->libgl_module, "glXQueryVersion", (void **) &glx_renderer->glXQueryVersion) || !g_module_symbol (glx_renderer->libgl_module, "glXQueryExtensionsString", (void **) &glx_renderer->glXQueryExtensionsString) || (!g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddress", (void **) &glx_renderer->glXGetProcAddress) && !g_module_symbol (glx_renderer->libgl_module, "glXGetProcAddressARB", (void **) &glx_renderer->glXGetProcAddress)) || !g_module_symbol (glx_renderer->libgl_module, "glXQueryDrawable", (void **) &glx_renderer->glXQueryDrawable)) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "Failed to resolve required GLX symbol"); return FALSE; } return TRUE; } static void update_base_winsys_features (CoglRenderer *renderer) { CoglGLXRenderer *glx_renderer = renderer->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); const char *glx_extensions; int default_screen; char **split_extensions; int i; default_screen = DefaultScreen (xlib_renderer->xdpy); glx_extensions = glx_renderer->glXQueryExtensionsString (xlib_renderer->xdpy, default_screen); COGL_NOTE (WINSYS, " GLX Extensions: %s", glx_extensions); split_extensions = g_strsplit (glx_extensions, " ", 0 /* max_tokens */); for (i = 0; i < G_N_ELEMENTS (winsys_feature_data); i++) if (_cogl_feature_check (renderer, "GLX", winsys_feature_data + i, glx_renderer->glx_major, glx_renderer->glx_minor, COGL_DRIVER_GL, /* the driver isn't used */ split_extensions, glx_renderer)) { if (winsys_feature_data[i].winsys_feature) COGL_FLAGS_SET (glx_renderer->base_winsys_features, winsys_feature_data[i].winsys_feature, TRUE); } g_strfreev (split_extensions); /* The GLX_SGI_video_sync spec explicitly states this extension * only works for direct contexts; we don't know per-renderer * if the context is direct or not, so we turn off the feature * flag; we still use the extension within this file looking * instead at glx_display->have_vblank_counter. */ COGL_FLAGS_SET (glx_renderer->base_winsys_features, COGL_WINSYS_FEATURE_VBLANK_COUNTER, FALSE); COGL_FLAGS_SET (glx_renderer->base_winsys_features, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); /* Because of the direct-context dependency, the VBLANK_WAIT feature * doesn't reflect the presence of GLX_SGI_video_sync. */ if (glx_renderer->glXWaitForMsc) COGL_FLAGS_SET (glx_renderer->base_winsys_features, COGL_WINSYS_FEATURE_VBLANK_WAIT, TRUE); } static gboolean _cogl_winsys_renderer_connect (CoglRenderer *renderer, GError **error) { CoglGLXRenderer *glx_renderer; CoglXlibRenderer *xlib_renderer; renderer->winsys = g_slice_new0 (CoglGLXRenderer); glx_renderer = renderer->winsys; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); if (!_cogl_xlib_renderer_connect (renderer, error)) goto error; if (renderer->driver != COGL_DRIVER_GL && renderer->driver != COGL_DRIVER_GL3) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "GLX Backend can only be used in conjunction with OpenGL"); goto error; } glx_renderer->libgl_module = g_module_open (COGL_GL_LIBNAME, G_MODULE_BIND_LAZY); if (glx_renderer->libgl_module == NULL) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "Failed to dynamically open the OpenGL library"); goto error; } if (!resolve_core_glx_functions (renderer, error)) goto error; if (!glx_renderer->glXQueryExtension (xlib_renderer->xdpy, &glx_renderer->glx_error_base, &glx_renderer->glx_event_base)) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "XServer appears to lack required GLX support"); goto error; } /* XXX: Note: For a long time Mesa exported a hybrid GLX, exporting * extensions specified to require GLX 1.3, but still reporting 1.2 * via glXQueryVersion. */ if (!glx_renderer->glXQueryVersion (xlib_renderer->xdpy, &glx_renderer->glx_major, &glx_renderer->glx_minor) || !(glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 2)) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "XServer appears to lack required GLX 1.2 support"); goto error; } update_base_winsys_features (renderer); glx_renderer->dri_fd = -1; return TRUE; error: _cogl_winsys_renderer_disconnect (renderer); return FALSE; } static gboolean update_winsys_features (CoglContext *context, GError **error) { CoglGLXDisplay *glx_display = context->display->winsys; CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; g_return_val_if_fail (glx_display->glx_context, FALSE); if (!_cogl_context_update_features (context, error)) return FALSE; memcpy (context->winsys_features, glx_renderer->base_winsys_features, sizeof (context->winsys_features)); if (glx_renderer->glXCopySubBuffer || context->glBlitFramebuffer) COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION, TRUE); /* Note: glXCopySubBuffer and glBlitFramebuffer won't be throttled * by the SwapInterval so we have to throttle swap_region requests * manually... */ if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION) && (glx_display->have_vblank_counter || glx_display->can_vblank_wait)) COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, TRUE); if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) { COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE); /* TODO: remove this deprecated feature */ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE); COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_PRESENTATION_TIME, TRUE); } /* We'll manually handle queueing dirty events in response to * Expose events from X */ COGL_FLAGS_SET (context->private_features, COGL_PRIVATE_FEATURE_DIRTY_EVENTS, TRUE); if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_BUFFER_AGE, TRUE); return TRUE; } static void glx_attributes_from_framebuffer_config (CoglDisplay *display, CoglFramebufferConfig *config, int *attributes) { CoglGLXRenderer *glx_renderer = display->renderer->winsys; int i = 0; attributes[i++] = GLX_DRAWABLE_TYPE; attributes[i++] = GLX_WINDOW_BIT; attributes[i++] = GLX_RENDER_TYPE; attributes[i++] = GLX_RGBA_BIT; attributes[i++] = GLX_DOUBLEBUFFER; attributes[i++] = GL_TRUE; attributes[i++] = GLX_RED_SIZE; attributes[i++] = 1; attributes[i++] = GLX_GREEN_SIZE; attributes[i++] = 1; attributes[i++] = GLX_BLUE_SIZE; attributes[i++] = 1; attributes[i++] = GLX_ALPHA_SIZE; attributes[i++] = config->swap_chain->has_alpha ? 1 : GLX_DONT_CARE; attributes[i++] = GLX_DEPTH_SIZE; attributes[i++] = 1; attributes[i++] = GLX_STENCIL_SIZE; attributes[i++] = config->need_stencil ? 1: GLX_DONT_CARE; if (config->stereo_enabled) { attributes[i++] = GLX_STEREO; attributes[i++] = TRUE; } if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4 && config->samples_per_pixel) { attributes[i++] = GLX_SAMPLE_BUFFERS; attributes[i++] = 1; attributes[i++] = GLX_SAMPLES; attributes[i++] = config->samples_per_pixel; } attributes[i++] = None; g_assert (i < MAX_GLX_CONFIG_ATTRIBS); } /* It seems the GLX spec never defined an invalid GLXFBConfig that * we could overload as an indication of error, so we have to return * an explicit boolean status. */ static gboolean find_fbconfig (CoglDisplay *display, CoglFramebufferConfig *config, GLXFBConfig *config_ret, GError **error) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglGLXRenderer *glx_renderer = display->renderer->winsys; GLXFBConfig *configs = NULL; int n_configs; static int attributes[MAX_GLX_CONFIG_ATTRIBS]; gboolean ret = TRUE; int xscreen_num = DefaultScreen (xlib_renderer->xdpy); glx_attributes_from_framebuffer_config (display, config, attributes); configs = glx_renderer->glXChooseFBConfig (xlib_renderer->xdpy, xscreen_num, attributes, &n_configs); if (!configs || n_configs == 0) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Failed to find any compatible fbconfigs"); ret = FALSE; goto done; } if (config->swap_chain->has_alpha) { int i; for (i = 0; i < n_configs; i++) { XVisualInfo *vinfo; vinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, configs[i]); if (vinfo == NULL) continue; if (vinfo->depth == 32 && (vinfo->red_mask | vinfo->green_mask | vinfo->blue_mask) != 0xffffffff) { COGL_NOTE (WINSYS, "Found an ARGB FBConfig [index:%d]", i); *config_ret = configs[i]; goto done; } } g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to find fbconfig with rgba visual"); ret = FALSE; goto done; } else { COGL_NOTE (WINSYS, "Using the first available FBConfig"); *config_ret = configs[0]; } done: XFree (configs); return ret; } static GLXContext create_gl3_context (CoglDisplay *display, GLXFBConfig fb_config) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglGLXRenderer *glx_renderer = display->renderer->winsys; /* We want a core profile 3.1 context with no deprecated features */ static const int attrib_list[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 1, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, None }; /* NV_robustness_video_memory_purge relies on GLX_ARB_create_context and in part on ARB_robustness. Namely, it needs the notification strategy to be set to GLX_LOSE_CONTEXT_ON_RESET_ARB and that the driver exposes the GetGraphicsResetStatusARB function. This means we don't actually enable robust buffer access. */ static const int attrib_list_reset_on_purge[] = { GLX_CONTEXT_MAJOR_VERSION_ARB, 3, GLX_CONTEXT_MINOR_VERSION_ARB, 1, GLX_CONTEXT_PROFILE_MASK_ARB, GLX_CONTEXT_CORE_PROFILE_BIT_ARB, GLX_CONTEXT_FLAGS_ARB, GLX_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB, GLX_GENERATE_RESET_ON_VIDEO_MEMORY_PURGE_NV, GL_TRUE, GLX_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB, GLX_LOSE_CONTEXT_ON_RESET_ARB, None }; /* Make sure that the display supports the GLX_ARB_create_context extension */ if (glx_renderer->glXCreateContextAttribs == NULL) return NULL; /* We can't check the presence of this extension with the usual COGL_WINSYS_FEATURE machinery because that only gets initialized later when the CoglContext is created. */ if (display->renderer->xlib_want_reset_on_video_memory_purge && strstr (glx_renderer->glXQueryExtensionsString (xlib_renderer->xdpy, DefaultScreen (xlib_renderer->xdpy)), "GLX_NV_robustness_video_memory_purge")) { CoglXlibTrapState old_state; GLXContext ctx; _cogl_xlib_renderer_trap_errors (display->renderer, &old_state); ctx = glx_renderer->glXCreateContextAttribs (xlib_renderer->xdpy, fb_config, NULL /* share_context */, True, /* direct */ attrib_list_reset_on_purge); if (!_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state) && ctx) return ctx; } return glx_renderer->glXCreateContextAttribs (xlib_renderer->xdpy, fb_config, NULL /* share_context */, True, /* direct */ attrib_list); } static gboolean create_context (CoglDisplay *display, GError **error) { CoglGLXDisplay *glx_display = display->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglGLXRenderer *glx_renderer = display->renderer->winsys; gboolean support_transparent_windows = display->onscreen_template->config.swap_chain->has_alpha; GLXFBConfig config; GError *fbconfig_error = NULL; XSetWindowAttributes attrs; XVisualInfo *xvisinfo; GLXDrawable dummy_drawable; CoglXlibTrapState old_state; g_return_val_if_fail (glx_display->glx_context == NULL, TRUE); glx_display->found_fbconfig = find_fbconfig (display, &display->onscreen_template->config, &config, &fbconfig_error); if (!glx_display->found_fbconfig) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to find suitable fbconfig for the GLX context: %s", fbconfig_error->message); g_error_free (fbconfig_error); return FALSE; } glx_display->fbconfig = config; glx_display->fbconfig_has_rgba_visual = support_transparent_windows; COGL_NOTE (WINSYS, "Creating GLX Context (display: %p)", xlib_renderer->xdpy); _cogl_xlib_renderer_trap_errors (display->renderer, &old_state); if (display->renderer->driver == COGL_DRIVER_GL3) glx_display->glx_context = create_gl3_context (display, config); else glx_display->glx_context = glx_renderer->glXCreateNewContext (xlib_renderer->xdpy, config, GLX_RGBA_TYPE, NULL, True); if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state) || glx_display->glx_context == NULL) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to create suitable GL context"); return FALSE; } glx_display->is_direct = glx_renderer->glXIsDirect (xlib_renderer->xdpy, glx_display->glx_context); glx_display->have_vblank_counter = glx_display->is_direct && glx_renderer->glXWaitVideoSync; glx_display->can_vblank_wait = glx_renderer->glXWaitForMsc || glx_display->have_vblank_counter; COGL_NOTE (WINSYS, "Setting %s context", glx_display->is_direct ? "direct" : "indirect"); /* XXX: GLX doesn't let us make a context current without a window * so we create a dummy window that we can use while no CoglOnscreen * framebuffer is in use. */ xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, config); if (xvisinfo == NULL) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to retrieve the X11 visual"); return FALSE; } _cogl_xlib_renderer_trap_errors (display->renderer, &old_state); attrs.override_redirect = True; attrs.colormap = XCreateColormap (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), xvisinfo->visual, AllocNone); attrs.border_pixel = 0; glx_display->dummy_xwin = XCreateWindow (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), -100, -100, 1, 1, 0, xvisinfo->depth, CopyFromParent, xvisinfo->visual, CWOverrideRedirect | CWColormap | CWBorderPixel, &attrs); /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3) { glx_display->dummy_glxwin = glx_renderer->glXCreateWindow (xlib_renderer->xdpy, config, glx_display->dummy_xwin, NULL); } if (glx_display->dummy_glxwin) dummy_drawable = glx_display->dummy_glxwin; else dummy_drawable = glx_display->dummy_xwin; COGL_NOTE (WINSYS, "Selecting dummy 0x%x for the GLX context", (unsigned int) dummy_drawable); glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, dummy_drawable, dummy_drawable, glx_display->glx_context); xlib_renderer->xvisinfo = xvisinfo; if (_cogl_xlib_renderer_untrap_errors (display->renderer, &old_state)) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to select the newly created GLX context"); return FALSE; } return TRUE; } static void _cogl_winsys_display_destroy (CoglDisplay *display) { CoglGLXDisplay *glx_display = display->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglGLXRenderer *glx_renderer = display->renderer->winsys; g_return_if_fail (glx_display != NULL); if (glx_display->glx_context) { glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, None, None, NULL); glx_renderer->glXDestroyContext (xlib_renderer->xdpy, glx_display->glx_context); glx_display->glx_context = NULL; } if (glx_display->dummy_glxwin) { glx_renderer->glXDestroyWindow (xlib_renderer->xdpy, glx_display->dummy_glxwin); glx_display->dummy_glxwin = None; } if (glx_display->dummy_xwin) { XDestroyWindow (xlib_renderer->xdpy, glx_display->dummy_xwin); glx_display->dummy_xwin = None; } g_slice_free (CoglGLXDisplay, display->winsys); display->winsys = NULL; } static gboolean _cogl_winsys_display_setup (CoglDisplay *display, GError **error) { CoglGLXDisplay *glx_display; int i; g_return_val_if_fail (display->winsys == NULL, FALSE); glx_display = g_slice_new0 (CoglGLXDisplay); display->winsys = glx_display; if (!create_context (display, error)) goto error; for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++) glx_display->glx_cached_configs[i].depth = -1; return TRUE; error: _cogl_winsys_display_destroy (display); return FALSE; } static gboolean _cogl_winsys_context_init (CoglContext *context, GError **error) { context->winsys = g_new0 (CoglContextGLX, 1); cogl_xlib_renderer_add_filter (context->display->renderer, glx_event_filter_cb, context); return update_winsys_features (context, error); } static void _cogl_winsys_context_deinit (CoglContext *context) { cogl_xlib_renderer_remove_filter (context->display->renderer, glx_event_filter_cb, context); g_free (context->winsys); } static gboolean _cogl_winsys_onscreen_init (CoglOnscreen *onscreen, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglDisplay *display = context->display; CoglGLXDisplay *glx_display = display->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglGLXRenderer *glx_renderer = display->renderer->winsys; Window xwin; CoglOnscreenXlib *xlib_onscreen; CoglOnscreenGLX *glx_onscreen; GLXFBConfig fbconfig; GError *fbconfig_error = NULL; g_return_val_if_fail (glx_display->glx_context, FALSE); if (!find_fbconfig (display, &framebuffer->config, &fbconfig, &fbconfig_error)) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Unable to find suitable fbconfig for the GLX context: %s", fbconfig_error->message); g_error_free (fbconfig_error); return FALSE; } /* Update the real number of samples_per_pixel now that we have * found an fbconfig... */ if (framebuffer->config.samples_per_pixel) { int samples; int status = glx_renderer->glXGetFBConfigAttrib (xlib_renderer->xdpy, fbconfig, GLX_SAMPLES, &samples); g_return_val_if_fail (status == Success, TRUE); framebuffer->samples_per_pixel = samples; } /* FIXME: We need to explicitly Select for ConfigureNotify events. * We need to document that for windows we create then toolkits * must be careful not to clear event mask bits that we select. */ { int width; int height; CoglXlibTrapState state; XVisualInfo *xvisinfo; XSetWindowAttributes xattr; unsigned long mask; int xerror; width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); _cogl_xlib_renderer_trap_errors (display->renderer, &state); xvisinfo = glx_renderer->glXGetVisualFromFBConfig (xlib_renderer->xdpy, fbconfig); if (xvisinfo == NULL) { g_set_error_literal (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "Unable to retrieve the X11 visual of context's " "fbconfig"); return FALSE; } /* window attributes */ xattr.background_pixel = WhitePixel (xlib_renderer->xdpy, DefaultScreen (xlib_renderer->xdpy)); xattr.border_pixel = 0; /* XXX: is this an X resource that we are leaking‽... */ xattr.colormap = XCreateColormap (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), xvisinfo->visual, AllocNone); xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK; mask = CWBorderPixel | CWColormap | CWEventMask; xwin = XCreateWindow (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), 0, 0, width, height, 0, xvisinfo->depth, InputOutput, xvisinfo->visual, mask, &xattr); XFree (xvisinfo); XSync (xlib_renderer->xdpy, False); xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state); if (xerror) { char message[1000]; XGetErrorText (xlib_renderer->xdpy, xerror, message, sizeof (message)); g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "X error while creating Window for CoglOnscreen: %s", message); return FALSE; } } onscreen->winsys = g_slice_new0 (CoglOnscreenGLX); xlib_onscreen = onscreen->winsys; glx_onscreen = onscreen->winsys; xlib_onscreen->xwin = xwin; /* Try and create a GLXWindow to use with extensions dependent on * GLX versions >= 1.3 that don't accept regular X Windows as GLX * drawables. */ if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 3) { glx_onscreen->glxwin = glx_renderer->glXCreateWindow (xlib_renderer->xdpy, fbconfig, xlib_onscreen->xwin, NULL); } #ifdef GLX_INTEL_swap_event if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) { GLXDrawable drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; /* similarly to above, we unconditionally select this event * because we rely on it to advance the master clock, and * drive redraw/relayout, animations and event handling. */ glx_renderer->glXSelectEvent (xlib_renderer->xdpy, drawable, GLX_BUFFER_SWAP_COMPLETE_INTEL_MASK); } #endif /* GLX_INTEL_swap_event */ return TRUE; } static void _cogl_winsys_onscreen_deinit (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglContextGLX *glx_context = context->winsys; CoglGLXDisplay *glx_display = context->display->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; CoglXlibTrapState old_state; CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys; GLXDrawable drawable; /* If we never successfully allocated then there's nothing to do */ if (glx_onscreen == NULL) return; if (xlib_onscreen->output != NULL) { cogl_object_unref (xlib_onscreen->output); xlib_onscreen->output = NULL; } _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state); drawable = glx_onscreen->glxwin == None ? xlib_onscreen->xwin : glx_onscreen->glxwin; /* Cogl always needs a valid context bound to something so if we are * destroying the onscreen that is currently bound we'll switch back * to the dummy drawable. Although the documentation for * glXDestroyWindow states that a currently bound window won't * actually be destroyed until it is unbound, it looks like this * doesn't work if the X window itself is destroyed */ if (drawable == glx_context->current_drawable) { GLXDrawable dummy_drawable = (glx_display->dummy_glxwin == None ? glx_display->dummy_xwin : glx_display->dummy_glxwin); glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, dummy_drawable, dummy_drawable, glx_display->glx_context); glx_context->current_drawable = dummy_drawable; } if (glx_onscreen->glxwin != None) { glx_renderer->glXDestroyWindow (xlib_renderer->xdpy, glx_onscreen->glxwin); glx_onscreen->glxwin = None; } if (xlib_onscreen->xwin != None) { XDestroyWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); xlib_onscreen->xwin = None; } else xlib_onscreen->xwin = None; XSync (xlib_renderer->xdpy, False); _cogl_xlib_renderer_untrap_errors (context->display->renderer, &old_state); g_slice_free (CoglOnscreenGLX, onscreen->winsys); onscreen->winsys = NULL; } static void _cogl_winsys_onscreen_bind (CoglOnscreen *onscreen) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglContextGLX *glx_context = context->winsys; CoglGLXDisplay *glx_display = context->display->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglXlibTrapState old_state; GLXDrawable drawable; drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; if (glx_context->current_drawable == drawable) return; _cogl_xlib_renderer_trap_errors (context->display->renderer, &old_state); COGL_NOTE (WINSYS, "MakeContextCurrent dpy: %p, window: 0x%x, context: %p", xlib_renderer->xdpy, (unsigned int) drawable, glx_display->glx_context); glx_renderer->glXMakeContextCurrent (xlib_renderer->xdpy, drawable, drawable, glx_display->glx_context); /* In case we are using GLX_SGI_swap_control for vblank syncing * we need call glXSwapIntervalSGI here to make sure that it * affects the current drawable. * * Note: we explicitly set to 0 when we aren't using the swap * interval to synchronize since some drivers have a default * swap interval of 1. Sadly some drivers even ignore requests * to disable the swap interval. * * NB: glXSwapIntervalSGI applies to the context not the * drawable which is why we can't just do this once when the * framebuffer is allocated. * * FIXME: We should check for GLX_EXT_swap_control which allows * per framebuffer swap intervals. GLX_MESA_swap_control also * allows per-framebuffer swap intervals but the semantics tend * to be more muddled since Mesa drivers tend to expose both the * MESA and SGI extensions which should technically be mutually * exclusive. */ if (glx_renderer->glXSwapInterval) glx_renderer->glXSwapInterval (1); XSync (xlib_renderer->xdpy, False); /* FIXME: We should be reporting a GError here */ if (_cogl_xlib_renderer_untrap_errors (context->display->renderer, &old_state)) { g_warning ("X Error received while making drawable 0x%08lX current", drawable); return; } glx_context->current_drawable = drawable; } static void _cogl_winsys_wait_for_gpu (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *ctx = framebuffer->context; ctx->glFinish (); } static void _cogl_winsys_wait_for_vblank (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *ctx = framebuffer->context; CoglGLXRenderer *glx_renderer; CoglXlibRenderer *xlib_renderer; CoglGLXDisplay *glx_display; glx_renderer = ctx->display->renderer->winsys; xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer); glx_display = ctx->display->winsys; if (glx_display->can_vblank_wait) { CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos); if (glx_renderer->glXWaitForMsc) { CoglOnscreenGLX *glx_onscreen = onscreen->winsys; Drawable drawable = glx_onscreen->glxwin; int64_t ust; int64_t msc; int64_t sbc; glx_renderer->glXWaitForMsc (xlib_renderer->xdpy, drawable, 0, 1, 0, &ust, &msc, &sbc); info->presentation_time = ust_to_nanoseconds (ctx->display->renderer, drawable, ust); } else { uint32_t current_count; glx_renderer->glXGetVideoSync (¤t_count); glx_renderer->glXWaitVideoSync (2, (current_count + 1) % 2, ¤t_count); info->presentation_time = get_monotonic_time_ns (); } } } static uint32_t _cogl_winsys_get_vsync_counter (CoglContext *ctx) { uint32_t video_sync_count; CoglGLXRenderer *glx_renderer; glx_renderer = ctx->display->renderer->winsys; glx_renderer->glXGetVideoSync (&video_sync_count); return video_sync_count; } #ifndef GLX_BACK_BUFFER_AGE_EXT #define GLX_BACK_BUFFER_AGE_EXT 0x20F4 #endif static int _cogl_winsys_onscreen_get_buffer_age (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys; CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; GLXDrawable drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; unsigned int age; if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_BUFFER_AGE)) return 0; glx_renderer->glXQueryDrawable (xlib_renderer->xdpy, drawable, GLX_BACK_BUFFER_AGE_EXT, &age); return age; } static void set_frame_info_output (CoglOnscreen *onscreen, CoglOutput *output) { CoglFrameInfo *info = g_queue_peek_tail (&onscreen->pending_frame_infos); info->output = output; if (output) { float refresh_rate = cogl_output_get_refresh_rate (output); if (refresh_rate != 0.0) info->refresh_rate = refresh_rate; } } static void _cogl_winsys_onscreen_swap_region (CoglOnscreen *onscreen, const int *user_rectangles, int n_rectangles) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; CoglGLXDisplay *glx_display = context->display->winsys; CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys; GLXDrawable drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; uint32_t end_frame_vsync_counter = 0; gboolean have_counter; gboolean can_wait; int x_min = 0, x_max = 0, y_min = 0, y_max = 0; /* * We assume that glXCopySubBuffer is synchronized which means it won't prevent multiple * blits per retrace if they can all be performed in the blanking period. If that's the * case then we still want to use the vblank sync menchanism but * we only need it to throttle redraws. */ gboolean blit_sub_buffer_is_synchronized = _cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED); int framebuffer_width = cogl_framebuffer_get_width (framebuffer); int framebuffer_height = cogl_framebuffer_get_height (framebuffer); int *rectangles = g_alloca (sizeof (int) * n_rectangles * 4); int i; /* glXCopySubBuffer expects rectangles relative to the bottom left corner but * we are given rectangles relative to the top left so we need to flip * them... */ memcpy (rectangles, user_rectangles, sizeof (int) * n_rectangles * 4); for (i = 0; i < n_rectangles; i++) { int *rect = &rectangles[4 * i]; if (i == 0) { x_min = rect[0]; x_max = rect[0] + rect[2]; y_min = rect[1]; y_max = rect[1] + rect[3]; } else { x_min = MIN (x_min, rect[0]); x_max = MAX (x_max, rect[0] + rect[2]); y_min = MIN (y_min, rect[1]); y_max = MAX (y_max, rect[1] + rect[3]); } rect[1] = framebuffer_height - rect[1] - rect[3]; } _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_BIND); have_counter = glx_display->have_vblank_counter; can_wait = glx_display->can_vblank_wait; /* We need to ensure that all the rendering is done, otherwise * redraw operations that are slower than the framerate can * queue up in the pipeline during a heavy animation, causing a * larger and larger backlog of rendering visible as lag to the * user. * * For an exaggerated example consider rendering at 60fps (so 16ms * per frame) and you have a really slow frame that takes 160ms to * render, even though painting the scene and issuing the commands * to the GPU takes no time at all. If all we did was use the * video_sync extension to throttle the painting done by the CPU * then every 16ms we would have another frame queued up even though * the GPU has only rendered one tenth of the current frame. By the * time the GPU would get to the 2nd frame there would be 9 frames * waiting to be rendered. * * The problem is that we don't currently have a good way to throttle * the GPU, only the CPU so we have to resort to synchronizing the * GPU with the CPU to throttle it. * * Note: since calling glFinish() and synchronizing the CPU with * the GPU is far from ideal, we hope that this is only a short * term solution. * - One idea is to using sync objects to track render * completion so we can throttle the backlog (ideally with an * additional extension that lets us get notifications in our * mainloop instead of having to busy wait for the * completion.) * - Another option is to support clipped redraws by reusing the * contents of old back buffers such that we can flip instead * of using a blit and then we can use GLX_INTEL_swap_events * to throttle. For this though we would still probably want an * additional extension so we can report the limited region of * the window damage to X/compositors. */ _cogl_winsys_wait_for_gpu (onscreen); if (blit_sub_buffer_is_synchronized && have_counter && can_wait) { end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context); /* If we have the GLX_SGI_video_sync extension then we can * be a bit smarter about how we throttle blits by avoiding * any waits if we can see that the video sync count has * already progressed. */ if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) _cogl_winsys_wait_for_vblank (onscreen); } else if (can_wait) _cogl_winsys_wait_for_vblank (onscreen); if (glx_renderer->glXCopySubBuffer) { Display *xdpy = xlib_renderer->xdpy; int i; for (i = 0; i < n_rectangles; i++) { int *rect = &rectangles[4 * i]; glx_renderer->glXCopySubBuffer (xdpy, drawable, rect[0], rect[1], rect[2], rect[3]); } } else if (context->glBlitFramebuffer) { int i; /* XXX: checkout how this state interacts with the code to use * glBlitFramebuffer in Neil's texture atlasing branch */ /* glBlitFramebuffer is affected by the scissor so we need to * ensure we have flushed an empty clip stack to get rid of it. * We also mark that the clip state is dirty so that it will be * flushed to the correct state the next time something is * drawn */ _cogl_clip_stack_flush (NULL, framebuffer); context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; context->glDrawBuffer (GL_FRONT); for (i = 0; i < n_rectangles; i++) { int *rect = &rectangles[4 * i]; int x2 = rect[0] + rect[2]; int y2 = rect[1] + rect[3]; context->glBlitFramebuffer (rect[0], rect[1], x2, y2, rect[0], rect[1], x2, y2, GL_COLOR_BUFFER_BIT, GL_NEAREST); } context->glDrawBuffer (context->current_gl_draw_buffer); } /* NB: unlike glXSwapBuffers, glXCopySubBuffer and * glBlitFramebuffer don't issue an implicit glFlush() so we * have to flush ourselves if we want the request to complete in * a finite amount of time since otherwise the driver can batch * the command indefinitely. */ context->glFlush (); /* NB: It's important we save the counter we read before acting on * the swap request since if we are mixing and matching different * swap methods between frames we don't want to read the timer e.g. * after calling glFinish() some times and not for others. * * In other words; this way we consistently save the time at the end * of the applications frame such that the counter isn't muddled by * the varying costs of different swap methods. */ if (have_counter) glx_onscreen->last_swap_vsync_counter = end_frame_vsync_counter; { CoglOutput *output; x_min = CLAMP (x_min, 0, framebuffer_width); x_max = CLAMP (x_max, 0, framebuffer_width); y_min = CLAMP (y_min, 0, framebuffer_width); y_max = CLAMP (y_max, 0, framebuffer_height); output = _cogl_xlib_renderer_output_for_rectangle (context->display->renderer, xlib_onscreen->x + x_min, xlib_onscreen->y + y_min, x_max - x_min, y_max - y_min); set_frame_info_output (onscreen, output); } /* XXX: we don't get SwapComplete events based on how we implement * the _swap_region() API but if cogl-onscreen.c knows we are * handling _SYNC and _COMPLETE events in the winsys then we need to * send fake events in this case. */ if (_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) { set_sync_pending (onscreen); set_complete_pending (onscreen); } } static void _cogl_winsys_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglGLXRenderer *glx_renderer = context->display->renderer->winsys; CoglGLXDisplay *glx_display = context->display->winsys; CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; CoglOnscreenGLX *glx_onscreen = onscreen->winsys; gboolean have_counter; GLXDrawable drawable; /* XXX: theoretically this shouldn't be necessary but at least with * the Intel drivers we have see that if we don't call * glXMakeContextCurrent for the drawable we are swapping then * we get a BadDrawable error from the X server. */ _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_BIND); drawable = glx_onscreen->glxwin ? glx_onscreen->glxwin : xlib_onscreen->xwin; have_counter = glx_display->have_vblank_counter; if (!glx_renderer->glXSwapInterval) { gboolean can_wait = have_counter || glx_display->can_vblank_wait; uint32_t end_frame_vsync_counter = 0; /* If the swap_region API is also being used then we need to track * the vsync counter for each swap request so we can manually * throttle swap_region requests. */ if (have_counter) end_frame_vsync_counter = _cogl_winsys_get_vsync_counter (context); /* If we are going to wait for VBLANK manually, we not only * need to flush out pending drawing to the GPU before we * sleep, we need to wait for it to finish. Otherwise, we * may end up with the situation: * * - We finish drawing - GPU drawing continues * - We go to sleep - GPU drawing continues * VBLANK - We call glXSwapBuffers - GPU drawing continues * - GPU drawing continues * - Swap buffers happens * * Producing a tear. Calling glFinish() first will cause us * to properly wait for the next VBLANK before we swap. This * obviously does not happen when we use _GLX_SWAP and let * the driver do the right thing */ _cogl_winsys_wait_for_gpu (onscreen); if (have_counter && can_wait) { if (glx_onscreen->last_swap_vsync_counter == end_frame_vsync_counter) _cogl_winsys_wait_for_vblank (onscreen); } else if (can_wait) _cogl_winsys_wait_for_vblank (onscreen); } glx_renderer->glXSwapBuffers (xlib_renderer->xdpy, drawable); if (have_counter) glx_onscreen->last_swap_vsync_counter = _cogl_winsys_get_vsync_counter (context); set_frame_info_output (onscreen, xlib_onscreen->output); } static uint32_t _cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen) { CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; return xlib_onscreen->xwin; } static void _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, gboolean visibility) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; if (visibility) XMapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); else XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); } static void _cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen, gboolean resizable) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglOnscreenXlib *xlib_onscreen = onscreen->winsys; XSizeHints *size_hints = XAllocSizeHints (); if (resizable) { /* TODO: Add cogl_onscreen_request_minimum_size () */ size_hints->min_width = 1; size_hints->min_height = 1; size_hints->max_width = INT_MAX; size_hints->max_height = INT_MAX; } else { int width = cogl_framebuffer_get_width (framebuffer); int height = cogl_framebuffer_get_height (framebuffer); size_hints->min_width = width; size_hints->min_height = height; size_hints->max_width = width; size_hints->max_height = height; } XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints); XFree (size_hints); } static gboolean get_fbconfig_for_depth (CoglContext *context, unsigned int depth, gboolean stereo, GLXFBConfig *fbconfig_ret, gboolean *can_mipmap_ret) { CoglXlibRenderer *xlib_renderer; CoglGLXRenderer *glx_renderer; CoglGLXDisplay *glx_display; Display *dpy; GLXFBConfig *fbconfigs; int n_elements, i; int db, stencil, alpha, mipmap, rgba, value; int spare_cache_slot = 0; gboolean found = FALSE; xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); glx_renderer = context->display->renderer->winsys; glx_display = context->display->winsys; /* Check if we've already got a cached config for this depth and stereo */ for (i = 0; i < COGL_GLX_N_CACHED_CONFIGS; i++) if (glx_display->glx_cached_configs[i].depth == -1) spare_cache_slot = i; else if (glx_display->glx_cached_configs[i].depth == depth && glx_display->glx_cached_configs[i].stereo == stereo) { *fbconfig_ret = glx_display->glx_cached_configs[i].fb_config; *can_mipmap_ret = glx_display->glx_cached_configs[i].can_mipmap; return glx_display->glx_cached_configs[i].found; } dpy = xlib_renderer->xdpy; fbconfigs = glx_renderer->glXGetFBConfigs (dpy, DefaultScreen (dpy), &n_elements); db = G_MAXSHORT; stencil = G_MAXSHORT; mipmap = 0; rgba = 0; for (i = 0; i < n_elements; i++) { XVisualInfo *vi; int visual_depth; vi = glx_renderer->glXGetVisualFromFBConfig (dpy, fbconfigs[i]); if (vi == NULL) continue; visual_depth = vi->depth; XFree (vi); if (visual_depth != depth) continue; glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_ALPHA_SIZE, &alpha); glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BUFFER_SIZE, &value); if (value != depth && (value - alpha) != depth) continue; glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_STEREO, &value); if (!!value != !!stereo) continue; if (glx_renderer->glx_major == 1 && glx_renderer->glx_minor >= 4) { glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_SAMPLES, &value); if (value > 1) continue; } value = 0; if (depth == 32) { glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGBA_EXT, &value); if (value) rgba = 1; } if (!value) { if (rgba) continue; glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_TEXTURE_RGB_EXT, &value); if (!value) continue; } glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_DOUBLEBUFFER, &value); if (value > db) continue; db = value; glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_STENCIL_SIZE, &value); if (value > stencil) continue; stencil = value; glx_renderer->glXGetFBConfigAttrib (dpy, fbconfigs[i], GLX_BIND_TO_MIPMAP_TEXTURE_EXT, &value); if (value < mipmap) continue; mipmap = value; *fbconfig_ret = fbconfigs[i]; *can_mipmap_ret = mipmap; found = TRUE; } if (n_elements) XFree (fbconfigs); glx_display->glx_cached_configs[spare_cache_slot].depth = depth; glx_display->glx_cached_configs[spare_cache_slot].found = found; glx_display->glx_cached_configs[spare_cache_slot].fb_config = *fbconfig_ret; glx_display->glx_cached_configs[spare_cache_slot].can_mipmap = mipmap; return found; } static gboolean try_create_glx_pixmap (CoglContext *context, CoglTexturePixmapX11 *tex_pixmap, gboolean mipmap) { CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; CoglRenderer *renderer; CoglXlibRenderer *xlib_renderer; CoglGLXRenderer *glx_renderer; Display *dpy; /* We have to initialize this *opaque* variable because gcc tries to * be too smart for its own good and warns that the variable may be * used uninitialized otherwise. */ GLXFBConfig fb_config = (GLXFBConfig)0; int attribs[7]; int i = 0; CoglXlibTrapState trap_state; unsigned int depth = tex_pixmap->depth; Visual* visual = tex_pixmap->visual; renderer = context->display->renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); glx_renderer = renderer->winsys; dpy = xlib_renderer->xdpy; if (!get_fbconfig_for_depth (context, depth, tex_pixmap->stereo_mode != COGL_TEXTURE_PIXMAP_MONO, &fb_config, &glx_tex_pixmap->can_mipmap)) { COGL_NOTE (TEXTURE_PIXMAP, "No suitable FBConfig found for depth %i", depth); return FALSE; } if (!glx_tex_pixmap->can_mipmap) mipmap = FALSE; attribs[i++] = GLX_TEXTURE_FORMAT_EXT; /* Check whether an alpha channel is used by comparing the total * number of 1-bits in color masks against the color depth requested * by the client. */ if (_cogl_util_popcountl (visual->red_mask | visual->green_mask | visual->blue_mask) == depth) attribs[i++] = GLX_TEXTURE_FORMAT_RGB_EXT; else attribs[i++] = GLX_TEXTURE_FORMAT_RGBA_EXT; attribs[i++] = GLX_MIPMAP_TEXTURE_EXT; attribs[i++] = mipmap; attribs[i++] = GLX_TEXTURE_TARGET_EXT; attribs[i++] = GLX_TEXTURE_2D_EXT; attribs[i++] = None; /* We need to trap errors from glXCreatePixmap because it can * sometimes fail during normal usage. For example on NVidia it gets * upset if you try to create two GLXPixmaps for the same drawable. */ _cogl_xlib_renderer_trap_errors (renderer, &trap_state); glx_tex_pixmap->glx_pixmap = glx_renderer->glXCreatePixmap (dpy, fb_config, tex_pixmap->pixmap, attribs); glx_tex_pixmap->has_mipmap_space = mipmap; XSync (dpy, False); if (_cogl_xlib_renderer_untrap_errors (renderer, &trap_state)) { COGL_NOTE (TEXTURE_PIXMAP, "Failed to create pixmap for %p", tex_pixmap); _cogl_xlib_renderer_trap_errors (renderer, &trap_state); glx_renderer->glXDestroyPixmap (dpy, glx_tex_pixmap->glx_pixmap); XSync (dpy, False); _cogl_xlib_renderer_untrap_errors (renderer, &trap_state); glx_tex_pixmap->glx_pixmap = None; return FALSE; } return TRUE; } static gboolean _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) { CoglTexturePixmapGLX *glx_tex_pixmap; CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context; if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP)) { tex_pixmap->winsys = NULL; return FALSE; } glx_tex_pixmap = g_new0 (CoglTexturePixmapGLX, 1); glx_tex_pixmap->glx_pixmap = None; glx_tex_pixmap->can_mipmap = FALSE; glx_tex_pixmap->has_mipmap_space = FALSE; glx_tex_pixmap->left.glx_tex = NULL; glx_tex_pixmap->right.glx_tex = NULL; glx_tex_pixmap->left.bind_tex_image_queued = TRUE; glx_tex_pixmap->right.bind_tex_image_queued = TRUE; glx_tex_pixmap->left.pixmap_bound = FALSE; glx_tex_pixmap->right.pixmap_bound = FALSE; tex_pixmap->winsys = glx_tex_pixmap; if (!try_create_glx_pixmap (ctx, tex_pixmap, FALSE)) { tex_pixmap->winsys = NULL; g_free (glx_tex_pixmap); return FALSE; } return TRUE; } static void free_glx_pixmap (CoglContext *context, CoglTexturePixmapGLX *glx_tex_pixmap) { CoglXlibTrapState trap_state; CoglRenderer *renderer; CoglXlibRenderer *xlib_renderer; CoglGLXRenderer *glx_renderer; renderer = context->display->renderer; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); glx_renderer = renderer->winsys; if (glx_tex_pixmap->left.pixmap_bound) glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, glx_tex_pixmap->glx_pixmap, GLX_FRONT_LEFT_EXT); if (glx_tex_pixmap->right.pixmap_bound) glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, glx_tex_pixmap->glx_pixmap, GLX_FRONT_RIGHT_EXT); /* FIXME - we need to trap errors and synchronize here because * of ordering issues between the XPixmap destruction and the * GLXPixmap destruction. * * If the X pixmap is destroyed, the GLX pixmap is destroyed as * well immediately, and thus, when Cogl calls glXDestroyPixmap() * it'll cause a BadDrawable error. * * this is technically a bug in the X server, which should not * destroy either pixmaps until the call to glXDestroyPixmap(); so * at some point we should revisit this code and remove the * trap+sync after verifying that the destruction is indeed safe. * * for reference, see: * http://bugzilla.clutter-project.org/show_bug.cgi?id=2324 */ _cogl_xlib_renderer_trap_errors (renderer, &trap_state); glx_renderer->glXDestroyPixmap (xlib_renderer->xdpy, glx_tex_pixmap->glx_pixmap); XSync (xlib_renderer->xdpy, False); _cogl_xlib_renderer_untrap_errors (renderer, &trap_state); glx_tex_pixmap->glx_pixmap = None; glx_tex_pixmap->left.pixmap_bound = FALSE; glx_tex_pixmap->right.pixmap_bound = FALSE; } static void _cogl_winsys_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) { CoglTexturePixmapGLX *glx_tex_pixmap; if (!tex_pixmap->winsys) return; glx_tex_pixmap = tex_pixmap->winsys; free_glx_pixmap (COGL_TEXTURE (tex_pixmap)->context, glx_tex_pixmap); if (glx_tex_pixmap->left.glx_tex) cogl_object_unref (glx_tex_pixmap->left.glx_tex); if (glx_tex_pixmap->right.glx_tex) cogl_object_unref (glx_tex_pixmap->right.glx_tex); tex_pixmap->winsys = NULL; g_free (glx_tex_pixmap); } static gboolean _cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode, gboolean needs_mipmap) { CoglTexture *tex = COGL_TEXTURE (tex_pixmap); CoglContext *ctx = COGL_TEXTURE (tex_pixmap)->context; CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; CoglPixmapTextureEyeGLX *texture_info; int buffer; CoglGLXRenderer *glx_renderer; if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) { texture_info = &glx_tex_pixmap->right; buffer = GLX_FRONT_RIGHT_EXT; } else { texture_info = &glx_tex_pixmap->left; buffer = GLX_FRONT_LEFT_EXT; } /* If we don't have a GLX pixmap then fallback */ if (glx_tex_pixmap->glx_pixmap == None) return FALSE; glx_renderer = ctx->display->renderer->winsys; /* Lazily create a texture to hold the pixmap */ if (texture_info->glx_tex == NULL) { CoglPixelFormat texture_format; GError *error = NULL; texture_format = (tex_pixmap->depth >= 32 ? COGL_PIXEL_FORMAT_RGBA_8888_PRE : COGL_PIXEL_FORMAT_RGB_888); texture_info->glx_tex = COGL_TEXTURE ( cogl_texture_2d_new_with_size (ctx, tex->width, tex->height)); _cogl_texture_set_internal_format (tex, texture_format); if (cogl_texture_allocate (texture_info->glx_tex, &error)) COGL_NOTE (TEXTURE_PIXMAP, "Created a texture 2d for %p", tex_pixmap); else { COGL_NOTE (TEXTURE_PIXMAP, "Falling back for %p because a " "texture 2d could not be created: %s", tex_pixmap, error->message); g_error_free (error); free_glx_pixmap (ctx, glx_tex_pixmap); return FALSE; } } if (needs_mipmap) { /* If we can't support mipmapping then temporarily fallback */ if (!glx_tex_pixmap->can_mipmap) return FALSE; /* Recreate the GLXPixmap if it wasn't previously created with a * mipmap tree */ if (!glx_tex_pixmap->has_mipmap_space) { free_glx_pixmap (ctx, glx_tex_pixmap); COGL_NOTE (TEXTURE_PIXMAP, "Recreating GLXPixmap with mipmap " "support for %p", tex_pixmap); if (!try_create_glx_pixmap (ctx, tex_pixmap, TRUE)) { /* If the pixmap failed then we'll permanently fallback * to using XImage. This shouldn't happen. */ COGL_NOTE (TEXTURE_PIXMAP, "Falling back to XGetImage " "updates for %p because creating the GLXPixmap " "with mipmap support failed", tex_pixmap); if (texture_info->glx_tex) cogl_object_unref (texture_info->glx_tex); return FALSE; } glx_tex_pixmap->left.bind_tex_image_queued = TRUE; glx_tex_pixmap->right.bind_tex_image_queued = TRUE; } } if (texture_info->bind_tex_image_queued) { GLuint gl_handle, gl_target; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (ctx->display->renderer); cogl_texture_get_gl_texture (texture_info->glx_tex, &gl_handle, &gl_target); COGL_NOTE (TEXTURE_PIXMAP, "Rebinding GLXPixmap for %p", tex_pixmap); _cogl_bind_gl_texture_transient (gl_target, gl_handle); if (texture_info->pixmap_bound) glx_renderer->glXReleaseTexImage (xlib_renderer->xdpy, glx_tex_pixmap->glx_pixmap, buffer); glx_renderer->glXBindTexImage (xlib_renderer->xdpy, glx_tex_pixmap->glx_pixmap, buffer, NULL); /* According to the recommended usage in the spec for * GLX_EXT_texture_pixmap we should release the texture after * we've finished drawing with it and it is undefined what * happens if you render to a pixmap that is bound to a texture. * However that would require the texture backend to know when * Cogl has finished painting and it may be more expensive to * keep unbinding the texture. Leaving it bound appears to work * on Mesa and NVidia drivers and it is also what Compiz does so * it is probably ok */ texture_info->bind_tex_image_queued = FALSE; texture_info->pixmap_bound = TRUE; _cogl_texture_2d_externally_modified (texture_info->glx_tex); } return TRUE; } static void _cogl_winsys_texture_pixmap_x11_damage_notify (CoglTexturePixmapX11 *tex_pixmap) { CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; glx_tex_pixmap->left.bind_tex_image_queued = TRUE; glx_tex_pixmap->right.bind_tex_image_queued = TRUE; } static CoglTexture * _cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode) { CoglTexturePixmapGLX *glx_tex_pixmap = tex_pixmap->winsys; if (stereo_mode == COGL_TEXTURE_PIXMAP_RIGHT) return glx_tex_pixmap->right.glx_tex; else return glx_tex_pixmap->left.glx_tex; } static CoglWinsysVtable _cogl_winsys_vtable = { .id = COGL_WINSYS_ID_GLX, .name = "GLX", .constraints = (COGL_RENDERER_CONSTRAINT_USES_X11 | COGL_RENDERER_CONSTRAINT_USES_XLIB), .renderer_get_proc_address = _cogl_winsys_renderer_get_proc_address, .renderer_connect = _cogl_winsys_renderer_connect, .renderer_disconnect = _cogl_winsys_renderer_disconnect, .renderer_outputs_changed = _cogl_winsys_renderer_outputs_changed, .display_setup = _cogl_winsys_display_setup, .display_destroy = _cogl_winsys_display_destroy, .context_init = _cogl_winsys_context_init, .context_deinit = _cogl_winsys_context_deinit, .context_get_clock_time = _cogl_winsys_get_clock_time, .onscreen_init = _cogl_winsys_onscreen_init, .onscreen_deinit = _cogl_winsys_onscreen_deinit, .onscreen_bind = _cogl_winsys_onscreen_bind, .onscreen_swap_buffers_with_damage = _cogl_winsys_onscreen_swap_buffers_with_damage, .onscreen_swap_region = _cogl_winsys_onscreen_swap_region, .onscreen_get_buffer_age = _cogl_winsys_onscreen_get_buffer_age, .onscreen_x11_get_window_xid = _cogl_winsys_onscreen_x11_get_window_xid, .onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility, .onscreen_set_resizable = _cogl_winsys_onscreen_set_resizable, /* X11 tfp support... */ /* XXX: instead of having a rather monolithic winsys vtable we could * perhaps look for a way to separate these... */ .texture_pixmap_x11_create = _cogl_winsys_texture_pixmap_x11_create, .texture_pixmap_x11_free = _cogl_winsys_texture_pixmap_x11_free, .texture_pixmap_x11_update = _cogl_winsys_texture_pixmap_x11_update, .texture_pixmap_x11_damage_notify = _cogl_winsys_texture_pixmap_x11_damage_notify, .texture_pixmap_x11_get_texture = _cogl_winsys_texture_pixmap_x11_get_texture, }; /* XXX: we use a function because no doubt someone will complain * about using c99 member initializers because they aren't portable * to windows. We want to avoid having to rigidly follow the real * order of members since some members are #ifdefd and we'd have * to mirror the #ifdefing to add padding etc. For any winsys that * can assume the platform has a sane compiler then we can just use * c99 initializers for insane platforms they can initialize * the members by name in a function. */ COGL_EXPORT const CoglWinsysVtable * _cogl_winsys_glx_get_vtable (void) { return &_cogl_winsys_vtable; } muffin-6.4.1/cogl/cogl/winsys/cogl-winsys.c0000664000175000017500000000313414723361714017571 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-context-private.h" #include uint32_t _cogl_winsys_error_quark (void) { return g_quark_from_static_string ("cogl-winsys-error-quark"); } /* FIXME: we should distinguish renderer and context features */ gboolean _cogl_winsys_has_feature (CoglWinsysFeature feature) { _COGL_GET_CONTEXT (ctx, FALSE); return COGL_FLAGS_GET (ctx->winsys_features, feature); } muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-egl-x11.c0000664000175000017500000006437214723361714020760 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-xlib-renderer-private.h" #include "cogl-xlib-renderer.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #include "cogl-display-private.h" #include "cogl-renderer-private.h" #include "cogl-texture-pixmap-x11-private.h" #include "cogl-texture-2d-private.h" #include "cogl-texture-2d.h" #include "cogl-poll-private.h" #include "winsys/cogl-winsys-egl-x11-private.h" #include "winsys/cogl-winsys-egl-private.h" #define COGL_ONSCREEN_X11_EVENT_MASK (StructureNotifyMask | ExposureMask) static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable; typedef struct _CoglDisplayXlib { Window dummy_xwin; } CoglDisplayXlib; typedef struct _CoglOnscreenXlib { Window xwin; } CoglOnscreenXlib; #ifdef EGL_KHR_image_pixmap typedef struct _CoglTexturePixmapEGL { EGLImageKHR image; CoglTexture *texture; } CoglTexturePixmapEGL; #endif static CoglOnscreen * find_onscreen_for_xid (CoglContext *context, uint32_t xid) { GList *l; for (l = context->framebuffers; l; l = l->next) { CoglFramebuffer *framebuffer = l->data; CoglOnscreenEGL *egl_onscreen; CoglOnscreenXlib *xlib_onscreen; if (framebuffer->type != COGL_FRAMEBUFFER_TYPE_ONSCREEN) continue; egl_onscreen = COGL_ONSCREEN (framebuffer)->winsys; xlib_onscreen = egl_onscreen->platform; if (xlib_onscreen->xwin == (Window)xid) return COGL_ONSCREEN (framebuffer); } return NULL; } static void flush_pending_resize_notifications_cb (void *data, void *user_data) { CoglFramebuffer *framebuffer = data; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; if (egl_onscreen->pending_resize_notify) { _cogl_onscreen_notify_resize (onscreen); egl_onscreen->pending_resize_notify = FALSE; } } } static void flush_pending_resize_notifications_idle (void *user_data) { CoglContext *context = user_data; CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; /* This needs to be disconnected before invoking the callbacks in * case the callbacks cause it to be queued again */ _cogl_closure_disconnect (egl_renderer->resize_notify_idle); egl_renderer->resize_notify_idle = NULL; g_list_foreach (context->framebuffers, flush_pending_resize_notifications_cb, NULL); } static void notify_resize (CoglContext *context, Window drawable, int width, int height) { CoglRenderer *renderer = context->display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglOnscreen *onscreen = find_onscreen_for_xid (context, drawable); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *egl_onscreen; if (!onscreen) return; egl_onscreen = onscreen->winsys; _cogl_framebuffer_winsys_update_size (framebuffer, width, height); /* We only want to notify that a resize happened when the * application calls cogl_context_dispatch so instead of immediately * notifying we queue an idle callback */ if (!egl_renderer->resize_notify_idle) { egl_renderer->resize_notify_idle = _cogl_poll_renderer_add_idle (renderer, flush_pending_resize_notifications_idle, context, NULL); } egl_onscreen->pending_resize_notify = TRUE; } static CoglFilterReturn event_filter_cb (XEvent *xevent, void *data) { CoglContext *context = data; if (xevent->type == ConfigureNotify) { notify_resize (context, xevent->xconfigure.window, xevent->xconfigure.width, xevent->xconfigure.height); } else if (xevent->type == Expose) { CoglOnscreen *onscreen = find_onscreen_for_xid (context, xevent->xexpose.window); if (onscreen) { CoglOnscreenDirtyInfo info; info.x = xevent->xexpose.x; info.y = xevent->xexpose.y; info.width = xevent->xexpose.width; info.height = xevent->xexpose.height; _cogl_onscreen_queue_dirty (onscreen, &info); } } return COGL_FILTER_CONTINUE; } static XVisualInfo * get_visual_info (CoglDisplay *display, EGLConfig egl_config) { CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (display->renderer); CoglRendererEGL *egl_renderer = display->renderer->winsys; XVisualInfo visinfo_template; int template_mask = 0; XVisualInfo *visinfo = NULL; int visinfos_count; EGLint visualid, red_size, green_size, blue_size, alpha_size; eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_NATIVE_VISUAL_ID, &visualid); if (visualid != 0) { visinfo_template.visualid = visualid; template_mask |= VisualIDMask; } else { /* some EGL drivers don't implement the EGL_NATIVE_VISUAL_ID * attribute, so attempt to find the closest match. */ eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_RED_SIZE, &red_size); eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_GREEN_SIZE, &green_size); eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_BLUE_SIZE, &blue_size); eglGetConfigAttrib (egl_renderer->edpy, egl_config, EGL_ALPHA_SIZE, &alpha_size); visinfo_template.depth = red_size + green_size + blue_size + alpha_size; template_mask |= VisualDepthMask; visinfo_template.screen = DefaultScreen (xlib_renderer->xdpy); template_mask |= VisualScreenMask; } visinfo = XGetVisualInfo (xlib_renderer->xdpy, template_mask, &visinfo_template, &visinfos_count); return visinfo; } static void _cogl_winsys_renderer_disconnect (CoglRenderer *renderer) { CoglRendererEGL *egl_renderer = renderer->winsys; _cogl_xlib_renderer_disconnect (renderer); eglTerminate (egl_renderer->edpy); g_slice_free (CoglRendererEGL, egl_renderer); } static EGLDisplay _cogl_winsys_egl_get_display (void *native) { EGLDisplay dpy = NULL; const char *client_exts = eglQueryString (NULL, EGL_EXTENSIONS); if (g_strstr_len (client_exts, -1, "EGL_KHR_platform_base")) { PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = (void *) eglGetProcAddress ("eglGetPlatformDisplay"); if (get_platform_display) dpy = get_platform_display (EGL_PLATFORM_X11_KHR, native, NULL); if (dpy) return dpy; } if (g_strstr_len (client_exts, -1, "EGL_EXT_platform_base")) { PFNEGLGETPLATFORMDISPLAYEXTPROC get_platform_display = (void *) eglGetProcAddress ("eglGetPlatformDisplayEXT"); if (get_platform_display) dpy = get_platform_display (EGL_PLATFORM_X11_KHR, native, NULL); if (dpy) return dpy; } return eglGetDisplay ((EGLNativeDisplayType) native); } static gboolean _cogl_winsys_renderer_connect (CoglRenderer *renderer, GError **error) { CoglRendererEGL *egl_renderer; CoglXlibRenderer *xlib_renderer; renderer->winsys = g_slice_new0 (CoglRendererEGL); egl_renderer = renderer->winsys; xlib_renderer = _cogl_xlib_renderer_get_data (renderer); egl_renderer->platform_vtable = &_cogl_winsys_egl_vtable; if (!_cogl_xlib_renderer_connect (renderer, error)) goto error; egl_renderer->edpy = _cogl_winsys_egl_get_display (xlib_renderer->xdpy); if (!_cogl_winsys_egl_renderer_connect_common (renderer, error)) goto error; return TRUE; error: _cogl_winsys_renderer_disconnect (renderer); return FALSE; } static int _cogl_winsys_egl_add_config_attributes (CoglDisplay *display, CoglFramebufferConfig *config, EGLint *attributes) { int i = 0; attributes[i++] = EGL_SURFACE_TYPE; attributes[i++] = EGL_WINDOW_BIT; return i; } static gboolean _cogl_winsys_egl_choose_config (CoglDisplay *display, EGLint *attributes, EGLConfig *out_config, GError **error) { CoglRenderer *renderer = display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; EGLint config_count = 0; EGLBoolean status; status = eglChooseConfig (egl_renderer->edpy, attributes, out_config, 1, &config_count); if (status != EGL_TRUE || config_count == 0) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "No compatible EGL configs found"); return FALSE; } return TRUE; } static gboolean _cogl_winsys_egl_display_setup (CoglDisplay *display, GError **error) { CoglDisplayEGL *egl_display = display->winsys; CoglDisplayXlib *xlib_display; xlib_display = g_slice_new0 (CoglDisplayXlib); egl_display->platform = xlib_display; return TRUE; } static void _cogl_winsys_egl_display_destroy (CoglDisplay *display) { CoglDisplayEGL *egl_display = display->winsys; g_slice_free (CoglDisplayXlib, egl_display->platform); } static gboolean _cogl_winsys_egl_context_init (CoglContext *context, GError **error) { cogl_xlib_renderer_add_filter (context->display->renderer, event_filter_cb, context); COGL_FLAGS_SET (context->winsys_features, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); /* We'll manually handle queueing dirty events in response to * Expose events from X */ COGL_FLAGS_SET (context->private_features, COGL_PRIVATE_FEATURE_DIRTY_EVENTS, TRUE); return TRUE; } static void _cogl_winsys_egl_context_deinit (CoglContext *context) { cogl_xlib_renderer_remove_filter (context->display->renderer, event_filter_cb, context); } static gboolean _cogl_winsys_egl_onscreen_init (CoglOnscreen *onscreen, EGLConfig egl_config, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglDisplay *display = context->display; CoglRenderer *renderer = display->renderer; CoglRendererEGL *egl_renderer = renderer->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglOnscreenXlib *xlib_onscreen; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; Window xwin; /* FIXME: We need to explicitly Select for ConfigureNotify events. * We need to document that for windows we create then toolkits * must be careful not to clear event mask bits that we select. */ { int width; int height; CoglXlibTrapState state; XVisualInfo *xvisinfo; XSetWindowAttributes xattr; unsigned long mask; int xerror; width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); _cogl_xlib_renderer_trap_errors (display->renderer, &state); xvisinfo = get_visual_info (display, egl_config); if (xvisinfo == NULL) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "Unable to retrieve the X11 visual of context's " "fbconfig"); return FALSE; } /* window attributes */ xattr.background_pixel = WhitePixel (xlib_renderer->xdpy, DefaultScreen (xlib_renderer->xdpy)); xattr.border_pixel = 0; /* XXX: is this an X resource that we are leaking‽... */ xattr.colormap = XCreateColormap (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), xvisinfo->visual, AllocNone); xattr.event_mask = COGL_ONSCREEN_X11_EVENT_MASK; mask = CWBorderPixel | CWColormap | CWEventMask; xwin = XCreateWindow (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), 0, 0, width, height, 0, xvisinfo->depth, InputOutput, xvisinfo->visual, mask, &xattr); XFree (xvisinfo); XSync (xlib_renderer->xdpy, False); xerror = _cogl_xlib_renderer_untrap_errors (display->renderer, &state); if (xerror) { char message[1000]; XGetErrorText (xlib_renderer->xdpy, xerror, message, sizeof (message)); g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "X error while creating Window for CoglOnscreen: %s", message); return FALSE; } } xlib_onscreen = g_slice_new (CoglOnscreenXlib); egl_onscreen->platform = xlib_onscreen; xlib_onscreen->xwin = xwin; egl_onscreen->egl_surface = eglCreateWindowSurface (egl_renderer->edpy, egl_config, (EGLNativeWindowType) xlib_onscreen->xwin, NULL); return TRUE; } static void _cogl_winsys_egl_onscreen_deinit (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglRenderer *renderer = context->display->renderer; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglXlibTrapState old_state; CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; _cogl_xlib_renderer_trap_errors (renderer, &old_state); if (xlib_onscreen->xwin != None) { XDestroyWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); xlib_onscreen->xwin = None; } else xlib_onscreen->xwin = None; XSync (xlib_renderer->xdpy, False); if (_cogl_xlib_renderer_untrap_errors (renderer, &old_state) != Success) g_warning ("X Error while destroying X window"); g_slice_free (CoglOnscreenXlib, xlib_onscreen); } static void _cogl_winsys_onscreen_set_visibility (CoglOnscreen *onscreen, gboolean visibility) { CoglContext *context = COGL_FRAMEBUFFER (onscreen)->context; CoglRenderer *renderer = context->display->renderer; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; CoglOnscreenXlib *xlib_onscreen = onscreen_egl->platform; if (visibility) XMapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); else XUnmapWindow (xlib_renderer->xdpy, xlib_onscreen->xwin); } static void _cogl_winsys_onscreen_set_resizable (CoglOnscreen *onscreen, gboolean resizable) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *context = framebuffer->context; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (context->display->renderer); CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; XSizeHints *size_hints = XAllocSizeHints (); if (resizable) { /* TODO: Add cogl_onscreen_request_minimum_size () */ size_hints->min_width = 1; size_hints->min_height = 1; size_hints->max_width = INT_MAX; size_hints->max_height = INT_MAX; } else { int width = cogl_framebuffer_get_width (framebuffer); int height = cogl_framebuffer_get_height (framebuffer); size_hints->min_width = width; size_hints->min_height = height; size_hints->max_width = width; size_hints->max_height = height; } XSetWMNormalHints (xlib_renderer->xdpy, xlib_onscreen->xwin, size_hints); XFree (size_hints); } static uint32_t _cogl_winsys_onscreen_x11_get_window_xid (CoglOnscreen *onscreen) { CoglOnscreenEGL *egl_onscreen = onscreen->winsys; CoglOnscreenXlib *xlib_onscreen = egl_onscreen->platform; return xlib_onscreen->xwin; } static gboolean _cogl_winsys_egl_context_created (CoglDisplay *display, GError **error) { CoglRenderer *renderer = display->renderer; CoglDisplayEGL *egl_display = display->winsys; CoglRendererEGL *egl_renderer = renderer->winsys; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglDisplayXlib *xlib_display = egl_display->platform; XVisualInfo *xvisinfo; XSetWindowAttributes attrs; const char *error_message; xvisinfo = get_visual_info (display, egl_display->egl_config); if (xvisinfo == NULL) { error_message = "Unable to find suitable X visual"; goto fail; } attrs.override_redirect = True; attrs.colormap = XCreateColormap (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), xvisinfo->visual, AllocNone); attrs.border_pixel = 0; if ((egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0) { xlib_display->dummy_xwin = XCreateWindow (xlib_renderer->xdpy, DefaultRootWindow (xlib_renderer->xdpy), -100, -100, 1, 1, 0, xvisinfo->depth, CopyFromParent, xvisinfo->visual, CWOverrideRedirect | CWColormap | CWBorderPixel, &attrs); egl_display->dummy_surface = eglCreateWindowSurface (egl_renderer->edpy, egl_display->egl_config, (EGLNativeWindowType) xlib_display->dummy_xwin, NULL); if (egl_display->dummy_surface == EGL_NO_SURFACE) { error_message = "Unable to create an EGL surface"; XFree (xvisinfo); goto fail; } } xlib_renderer->xvisinfo = xvisinfo; if (!_cogl_winsys_egl_make_current (display, egl_display->dummy_surface, egl_display->dummy_surface, egl_display->egl_context)) { if (egl_display->dummy_surface == EGL_NO_SURFACE) error_message = "Unable to eglMakeCurrent with no surface"; else error_message = "Unable to eglMakeCurrent with dummy surface"; goto fail; } return TRUE; fail: g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "%s", error_message); return FALSE; } static void _cogl_winsys_egl_cleanup_context (CoglDisplay *display) { CoglDisplayEGL *egl_display = display->winsys; CoglDisplayXlib *xlib_display = egl_display->platform; CoglRenderer *renderer = display->renderer; CoglXlibRenderer *xlib_renderer = _cogl_xlib_renderer_get_data (renderer); CoglRendererEGL *egl_renderer = renderer->winsys; if (egl_display->dummy_surface != EGL_NO_SURFACE) { eglDestroySurface (egl_renderer->edpy, egl_display->dummy_surface); egl_display->dummy_surface = EGL_NO_SURFACE; } if (xlib_display->dummy_xwin) { XDestroyWindow (xlib_renderer->xdpy, xlib_display->dummy_xwin); xlib_display->dummy_xwin = None; } } #ifdef EGL_KHR_image_pixmap static gboolean _cogl_winsys_texture_pixmap_x11_create (CoglTexturePixmapX11 *tex_pixmap) { CoglTexture *tex = COGL_TEXTURE (tex_pixmap); CoglContext *ctx = tex->context; CoglTexturePixmapEGL *egl_tex_pixmap; EGLint attribs[] = {EGL_IMAGE_PRESERVED_KHR, EGL_TRUE, EGL_NONE}; CoglPixelFormat texture_format; CoglRendererEGL *egl_renderer; egl_renderer = ctx->display->renderer->winsys; if (!(egl_renderer->private_features & COGL_EGL_WINSYS_FEATURE_EGL_IMAGE_FROM_X11_PIXMAP) || !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE)) { tex_pixmap->winsys = NULL; return FALSE; } egl_tex_pixmap = g_new0 (CoglTexturePixmapEGL, 1); egl_tex_pixmap->image = _cogl_egl_create_image (ctx, EGL_NATIVE_PIXMAP_KHR, (EGLClientBuffer)tex_pixmap->pixmap, attribs); if (egl_tex_pixmap->image == EGL_NO_IMAGE_KHR) { g_free (egl_tex_pixmap); return FALSE; } texture_format = (tex_pixmap->depth >= 32 ? COGL_PIXEL_FORMAT_RGBA_8888_PRE : COGL_PIXEL_FORMAT_RGB_888); egl_tex_pixmap->texture = COGL_TEXTURE ( cogl_egl_texture_2d_new_from_image (ctx, tex->width, tex->height, texture_format, egl_tex_pixmap->image, COGL_EGL_IMAGE_FLAG_NONE, NULL)); tex_pixmap->winsys = egl_tex_pixmap; return TRUE; } static void _cogl_winsys_texture_pixmap_x11_free (CoglTexturePixmapX11 *tex_pixmap) { CoglTexturePixmapEGL *egl_tex_pixmap; /* FIXME: It should be possible to get to a CoglContext from any * CoglTexture pointer. */ _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!tex_pixmap->winsys) return; egl_tex_pixmap = tex_pixmap->winsys; if (egl_tex_pixmap->texture) cogl_object_unref (egl_tex_pixmap->texture); if (egl_tex_pixmap->image != EGL_NO_IMAGE_KHR) _cogl_egl_destroy_image (ctx, egl_tex_pixmap->image); tex_pixmap->winsys = NULL; g_free (egl_tex_pixmap); } static gboolean _cogl_winsys_texture_pixmap_x11_update (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode, gboolean needs_mipmap) { if (needs_mipmap) return FALSE; return TRUE; } static void _cogl_winsys_texture_pixmap_x11_damage_notify (CoglTexturePixmapX11 *tex_pixmap) { } static CoglTexture * _cogl_winsys_texture_pixmap_x11_get_texture (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode) { CoglTexturePixmapEGL *egl_tex_pixmap = tex_pixmap->winsys; return egl_tex_pixmap->texture; } #endif /* EGL_KHR_image_pixmap */ static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable = { .add_config_attributes = _cogl_winsys_egl_add_config_attributes, .choose_config = _cogl_winsys_egl_choose_config, .display_setup = _cogl_winsys_egl_display_setup, .display_destroy = _cogl_winsys_egl_display_destroy, .context_created = _cogl_winsys_egl_context_created, .cleanup_context = _cogl_winsys_egl_cleanup_context, .context_init = _cogl_winsys_egl_context_init, .context_deinit = _cogl_winsys_egl_context_deinit, .onscreen_init = _cogl_winsys_egl_onscreen_init, .onscreen_deinit = _cogl_winsys_egl_onscreen_deinit }; COGL_EXPORT const CoglWinsysVtable * _cogl_winsys_egl_xlib_get_vtable (void) { static gboolean vtable_inited = FALSE; static CoglWinsysVtable vtable; if (!vtable_inited) { /* The EGL_X11 winsys is a subclass of the EGL winsys so we start by copying its vtable */ vtable = *_cogl_winsys_egl_get_vtable (); vtable.id = COGL_WINSYS_ID_EGL_XLIB; vtable.name = "EGL_XLIB"; vtable.constraints |= (COGL_RENDERER_CONSTRAINT_USES_X11 | COGL_RENDERER_CONSTRAINT_USES_XLIB); vtable.renderer_connect = _cogl_winsys_renderer_connect; vtable.renderer_disconnect = _cogl_winsys_renderer_disconnect; vtable.onscreen_set_visibility = _cogl_winsys_onscreen_set_visibility; vtable.onscreen_set_resizable = _cogl_winsys_onscreen_set_resizable; vtable.onscreen_x11_get_window_xid = _cogl_winsys_onscreen_x11_get_window_xid; #ifdef EGL_KHR_image_pixmap /* X11 tfp support... */ /* XXX: instead of having a rather monolithic winsys vtable we could * perhaps look for a way to separate these... */ vtable.texture_pixmap_x11_create = _cogl_winsys_texture_pixmap_x11_create; vtable.texture_pixmap_x11_free = _cogl_winsys_texture_pixmap_x11_free; vtable.texture_pixmap_x11_update = _cogl_winsys_texture_pixmap_x11_update; vtable.texture_pixmap_x11_damage_notify = _cogl_winsys_texture_pixmap_x11_damage_notify; vtable.texture_pixmap_x11_get_texture = _cogl_winsys_texture_pixmap_x11_get_texture; #endif /* EGL_KHR_image_pixmap) */ vtable_inited = TRUE; } return &vtable; } muffin-6.4.1/cogl/cogl/winsys/cogl-glx-display-private.h0000664000175000017500000000373414723361714022155 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_DISPLAY_GLX_PRIVATE_H #define __COGL_DISPLAY_GLX_PRIVATE_H #include "cogl-object-private.h" typedef struct _CoglGLXCachedConfig { /* This will be -1 if there is no cached config in this slot */ int depth; gboolean found; GLXFBConfig fb_config; gboolean stereo; gboolean can_mipmap; } CoglGLXCachedConfig; #define COGL_GLX_N_CACHED_CONFIGS 6 typedef struct _CoglGLXDisplay { CoglGLXCachedConfig glx_cached_configs[COGL_GLX_N_CACHED_CONFIGS]; gboolean found_fbconfig; gboolean fbconfig_has_rgba_visual; gboolean is_direct; gboolean have_vblank_counter; gboolean can_vblank_wait; GLXFBConfig fbconfig; /* Single context for all wins */ GLXContext glx_context; GLXWindow dummy_glxwin; Window dummy_xwin; } CoglGLXDisplay; #endif /* __COGL_DISPLAY_GLX_PRIVATE_H */ muffin-6.4.1/cogl/cogl/winsys/cogl-winsys-private.h0000664000175000017500000001137714723361714021256 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_WINSYS_PRIVATE_H #define __COGL_WINSYS_PRIVATE_H #include "cogl-renderer.h" #include "cogl-onscreen.h" #ifdef COGL_HAS_XLIB_SUPPORT #include "cogl-texture-pixmap-x11-private.h" #endif #ifdef COGL_HAS_XLIB_SUPPORT #include #include "cogl-texture-pixmap-x11-private.h" #endif #ifdef COGL_HAS_EGL_SUPPORT #include "cogl-egl-private.h" #endif #include "cogl-poll.h" COGL_EXPORT uint32_t _cogl_winsys_error_quark (void); #define COGL_WINSYS_ERROR (_cogl_winsys_error_quark ()) typedef enum /*< prefix=COGL_WINSYS_ERROR >*/ { COGL_WINSYS_ERROR_INIT, COGL_WINSYS_ERROR_CREATE_CONTEXT, COGL_WINSYS_ERROR_CREATE_ONSCREEN, COGL_WINSYS_ERROR_MAKE_CURRENT, } CoglWinsysError; typedef struct _CoglWinsysVtable { CoglWinsysID id; CoglRendererConstraint constraints; const char *name; /* Required functions */ GCallback (*renderer_get_proc_address) (CoglRenderer *renderer, const char *name, gboolean in_core); gboolean (*renderer_connect) (CoglRenderer *renderer, GError **error); void (*renderer_disconnect) (CoglRenderer *renderer); void (*renderer_outputs_changed) (CoglRenderer *renderer); gboolean (*display_setup) (CoglDisplay *display, GError **error); void (*display_destroy) (CoglDisplay *display); CoglDmaBufHandle * (*renderer_create_dma_buf) (CoglRenderer *renderer, int width, int height, GError **error); gboolean (*context_init) (CoglContext *context, GError **error); void (*context_deinit) (CoglContext *context); gboolean (*onscreen_init) (CoglOnscreen *onscreen, GError **error); void (*onscreen_deinit) (CoglOnscreen *onscreen); void (*onscreen_bind) (CoglOnscreen *onscreen); void (*onscreen_swap_buffers_with_damage) (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles); void (*onscreen_set_visibility) (CoglOnscreen *onscreen, gboolean visibility); /* Optional functions */ int64_t (*context_get_clock_time) (CoglContext *context); void (*onscreen_swap_region) (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles); void (*onscreen_set_resizable) (CoglOnscreen *onscreen, gboolean resizable); int (*onscreen_get_buffer_age) (CoglOnscreen *onscreen); uint32_t (*onscreen_x11_get_window_xid) (CoglOnscreen *onscreen); #ifdef COGL_HAS_XLIB_SUPPORT gboolean (*texture_pixmap_x11_create) (CoglTexturePixmapX11 *tex_pixmap); void (*texture_pixmap_x11_free) (CoglTexturePixmapX11 *tex_pixmap); gboolean (*texture_pixmap_x11_update) (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode, gboolean needs_mipmap); void (*texture_pixmap_x11_damage_notify) (CoglTexturePixmapX11 *tex_pixmap); CoglTexture * (*texture_pixmap_x11_get_texture) (CoglTexturePixmapX11 *tex_pixmap, CoglTexturePixmapStereoMode stereo_mode); #endif void * (*fence_add) (CoglContext *ctx); gboolean (*fence_is_complete) (CoglContext *ctx, void *fence); void (*fence_destroy) (CoglContext *ctx, void *fence); } CoglWinsysVtable; typedef const CoglWinsysVtable *(*CoglWinsysVtableGetter) (void); gboolean _cogl_winsys_has_feature (CoglWinsysFeature feature); #endif /* __COGL_WINSYS_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-onscreen-template-private.h0000664000175000017500000000276214723361714022011 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_ONSCREEN_TEMPLATE_PRIVATE_H #define __COGL_ONSCREEN_TEMPLATE_PRIVATE_H #include "cogl-object-private.h" #include "cogl-swap-chain.h" #include "cogl-framebuffer-private.h" struct _CoglOnscreenTemplate { CoglObject _parent; CoglFramebufferConfig config; }; #endif /* __COGL_ONSCREEN_TEMPLATE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-closure-list-private.h0000664000175000017500000001074214723361714021006 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef _COGL_CLOSURE_LIST_PRIVATE_H_ #define _COGL_CLOSURE_LIST_PRIVATE_H_ #include "cogl-object.h" #include "cogl-list.h" /* * This implements a list of callbacks that can be used a bit like * signals in GObject, but that don't have any marshalling overhead. * * The idea is that any Cogl code that wants to provide a callback * point will provide api to add a callback for that particular point. * The function can take a function pointer with the correct * signature. Internally the Cogl code can use _cogl_closure_list_add, * _cogl_closure_disconnect and _cogl_closure_list_disconnect_all * * In the future we could consider exposing the CoglClosure type which * would allow applications to use _cogl_closure_disconnect() directly * so we don't need to expose new disconnect apis for each callback * point. */ typedef struct _CoglClosure { CoglList link; void *function; void *user_data; CoglUserDataDestroyCallback destroy_cb; } CoglClosure; /* * _cogl_closure_disconnect: * @closure: A closure connected to a Cogl closure list * * Removes the given closure from the callback list it is connected to * and destroys it. If the closure was created with a destroy function * then it will be invoked. */ COGL_EXPORT void _cogl_closure_disconnect (CoglClosure *closure); void _cogl_closure_list_disconnect_all (CoglList *list); CoglClosure * _cogl_closure_list_add (CoglList *list, void *function, void *user_data, CoglUserDataDestroyCallback destroy_cb); /* * _cogl_closure_list_invoke: * @list: A pointer to a CoglList containing CoglClosures * @cb_type: The name of a typedef for the closure callback function signature * @...: The the arguments to pass to the callback * * A convenience macro to invoke a closure list with a variable number * of arguments that will be passed to the closure callback functions. * * Note that the arguments will be evaluated multiple times so it is * not safe to pass expressions that have side-effects. * * Note also that this function ignores the return value from the * callbacks. If you want to handle the return value you should * manually iterate the list and invoke the callbacks yourself. */ #define _cogl_closure_list_invoke(list, cb_type, ...) \ G_STMT_START { \ CoglClosure *_c, *_tmp; \ \ _cogl_list_for_each_safe (_c, _tmp, (list), link) \ { \ cb_type _cb = _c->function; \ _cb (__VA_ARGS__, _c->user_data); \ } \ } G_STMT_END #define _cogl_closure_list_invoke_no_args(list) \ G_STMT_START { \ CoglClosure *_c, *_tmp; \ \ _cogl_list_for_each_safe (_c, _tmp, (list), link) \ { \ void (*_cb)(void *) = _c->function; \ _cb (_c->user_data); \ } \ } G_STMT_END #endif /* _COGL_CLOSURE_LIST_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/cogl-point-in-poly.c0000664000175000017500000001012114723361714017411 0ustar fabiofabio/* * Point Inclusion in Polygon Test * * Copyright (c) 1970-2003, Wm. Randolph Franklin * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimers. * 2. Redistributions in binary form must reproduce the above * copyright notice in the documentation and/or other materials * provided with the distribution. * 3. The name of W. Randolph Franklin may not be used to endorse or * promote products derived from this Software without specific * prior written permission. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * Note: * The algorithm for this point_in_poly() function was learnt from: * http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-point-in-poly-private.h" #include /* We've made a notable change to the original algorithm referenced * above to make sure we have reliable results for screen aligned * rectangles even though there may be some numerical in-precision in * how the vertices of the polygon were calculated. * * We've avoided introducing an epsilon factor to the comparisons * since we feel there's a risk of changing some semantics in ways that * might not be desirable. One of those is that if you transform two * polygons which share an edge and test a point close to that edge * then this algorithm will currently give a positive result for only * one polygon. * * Another concern is the way this algorithm resolves the corner case * where the horizontal ray being cast to count edge crossings may * cross directly through a vertex. The solution is based on the "idea * of Simulation of Simplicity" and "pretends to shift the ray * infinitesimally down so that it either clearly intersects, or * clearly doesn't touch". I'm not familiar with the idea myself so I * expect a misplaced epsilon is likely to break that aspect of the * algorithm. * * The simple solution we've gone for is to pixel align the polygon * vertices which should eradicate most noise due to in-precision. */ int _cogl_util_point_in_screen_poly (float point_x, float point_y, void *vertices, size_t stride, int n_vertices) { int i, j, c = 0; for (i = 0, j = n_vertices - 1; i < n_vertices; j = i++) { float vert_xi = *(float *)((uint8_t *)vertices + i * stride); float vert_xj = *(float *)((uint8_t *)vertices + j * stride); float vert_yi = *(float *)((uint8_t *)vertices + i * stride + sizeof (float)); float vert_yj = *(float *)((uint8_t *)vertices + j * stride + sizeof (float)); vert_xi = COGL_UTIL_NEARBYINT (vert_xi); vert_xj = COGL_UTIL_NEARBYINT (vert_xj); vert_yi = COGL_UTIL_NEARBYINT (vert_yi); vert_yj = COGL_UTIL_NEARBYINT (vert_yj); if (((vert_yi > point_y) != (vert_yj > point_y)) && (point_x < (vert_xj - vert_xi) * (point_y - vert_yi) / (vert_yj - vert_yi) + vert_xi) ) c = !c; } return c; } muffin-6.4.1/cogl/cogl/cogl-attribute.c0000664000175000017500000005140314723361714016706 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-journal-private.h" #include "cogl-attribute.h" #include "cogl-attribute-private.h" #include "cogl-pipeline.h" #include "cogl-pipeline-private.h" #include "cogl-texture-private.h" #include "cogl-framebuffer-private.h" #include "cogl-indices-private.h" #include "cogl-private.h" #include "cogl-gtype-private.h" #include #include #include /* This isn't defined in the GLES headers */ #ifndef GL_UNSIGNED_INT #define GL_UNSIGNED_INT 0x1405 #endif static void _cogl_attribute_free (CoglAttribute *attribute); COGL_OBJECT_DEFINE (Attribute, attribute); COGL_GTYPE_DEFINE_CLASS (Attribute, attribute); static gboolean validate_cogl_attribute_name (const char *name, const char **real_attribute_name, CoglAttributeNameID *name_id, gboolean *normalized, int *layer_number) { name = name + 5; /* skip "cogl_" */ *normalized = FALSE; *layer_number = 0; if (strcmp (name, "position_in") == 0) *name_id = COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY; else if (strcmp (name, "color_in") == 0) { *name_id = COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY; *normalized = TRUE; } else if (strcmp (name, "tex_coord_in") == 0) { *real_attribute_name = "cogl_tex_coord0_in"; *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY; } else if (strncmp (name, "tex_coord", strlen ("tex_coord")) == 0) { char *endptr; *layer_number = strtoul (name + 9, &endptr, 10); if (strcmp (endptr, "_in") != 0) { g_warning ("Texture coordinate attributes should either be named " "\"cogl_tex_coord_in\" or named with a texture unit index " "like \"cogl_tex_coord2_in\"\n"); return FALSE; } *name_id = COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY; } else if (strcmp (name, "normal_in") == 0) { *name_id = COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY; *normalized = TRUE; } else if (strcmp (name, "point_size_in") == 0) *name_id = COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY; else { g_warning ("Unknown cogl_* attribute name cogl_%s\n", name); return FALSE; } return TRUE; } CoglAttributeNameState * _cogl_attribute_register_attribute_name (CoglContext *context, const char *name) { CoglAttributeNameState *name_state = g_new (CoglAttributeNameState, 1); int name_index = context->n_attribute_names++; char *name_copy = g_strdup (name); name_state->name = NULL; name_state->name_index = name_index; if (strncmp (name, "cogl_", 5) == 0) { if (!validate_cogl_attribute_name (name, &name_state->name, &name_state->name_id, &name_state->normalized_default, &name_state->layer_number)) goto error; } else { name_state->name_id = COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY; name_state->normalized_default = FALSE; name_state->layer_number = 0; } if (name_state->name == NULL) name_state->name = name_copy; g_hash_table_insert (context->attribute_name_states_hash, name_copy, name_state); if (G_UNLIKELY (context->attribute_name_index_map == NULL)) context->attribute_name_index_map = g_array_new (FALSE, FALSE, sizeof (void *)); g_array_set_size (context->attribute_name_index_map, name_index + 1); g_array_index (context->attribute_name_index_map, CoglAttributeNameState *, name_index) = name_state; return name_state; error: g_free (name_state); return NULL; } static gboolean validate_n_components (const CoglAttributeNameState *name_state, int n_components) { switch (name_state->name_id) { case COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY: if (G_UNLIKELY (n_components != 1)) { g_critical ("The point size attribute can only have one " "component"); return FALSE; } break; case COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY: case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: case COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY: case COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY: case COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY: return TRUE; } return TRUE; } CoglAttribute * cogl_attribute_new (CoglAttributeBuffer *attribute_buffer, const char *name, size_t stride, size_t offset, int n_components, CoglAttributeType type) { CoglAttribute *attribute = g_slice_new (CoglAttribute); CoglBuffer *buffer = COGL_BUFFER (attribute_buffer); CoglContext *ctx = buffer->context; attribute->is_buffered = TRUE; attribute->name_state = g_hash_table_lookup (ctx->attribute_name_states_hash, name); if (!attribute->name_state) { CoglAttributeNameState *name_state = _cogl_attribute_register_attribute_name (ctx, name); if (!name_state) goto error; attribute->name_state = name_state; } attribute->d.buffered.attribute_buffer = cogl_object_ref (attribute_buffer); attribute->d.buffered.stride = stride; attribute->d.buffered.offset = offset; attribute->d.buffered.n_components = n_components; attribute->d.buffered.type = type; attribute->immutable_ref = 0; if (attribute->name_state->name_id != COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY) { if (!validate_n_components (attribute->name_state, n_components)) return NULL; attribute->normalized = attribute->name_state->normalized_default; } else attribute->normalized = FALSE; return _cogl_attribute_object_new (attribute); error: _cogl_attribute_free (attribute); return NULL; } static CoglAttribute * _cogl_attribute_new_const (CoglContext *context, const char *name, int n_components, int n_columns, gboolean transpose, const float *value) { CoglAttribute *attribute = g_slice_new (CoglAttribute); attribute->name_state = g_hash_table_lookup (context->attribute_name_states_hash, name); if (!attribute->name_state) { CoglAttributeNameState *name_state = _cogl_attribute_register_attribute_name (context, name); if (!name_state) goto error; attribute->name_state = name_state; } if (!validate_n_components (attribute->name_state, n_components)) goto error; attribute->is_buffered = FALSE; attribute->normalized = FALSE; attribute->d.constant.context = cogl_object_ref (context); attribute->d.constant.boxed.v.array = NULL; if (n_columns == 1) { _cogl_boxed_value_set_float (&attribute->d.constant.boxed, n_components, 1, value); } else { /* FIXME: Up until GL[ES] 3 only square matrices were supported * and we don't currently expose non-square matrices in Cogl. */ g_return_val_if_fail (n_columns == n_components, NULL); _cogl_boxed_value_set_matrix (&attribute->d.constant.boxed, n_columns, 1, transpose, value); } return _cogl_attribute_object_new (attribute); error: _cogl_attribute_free (attribute); return NULL; } CoglAttribute * cogl_attribute_new_const_1f (CoglContext *context, const char *name, float value) { return _cogl_attribute_new_const (context, name, 1, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ &value); } CoglAttribute * cogl_attribute_new_const_2fv (CoglContext *context, const char *name, const float *value) { return _cogl_attribute_new_const (context, name, 2, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ value); } CoglAttribute * cogl_attribute_new_const_3fv (CoglContext *context, const char *name, const float *value) { return _cogl_attribute_new_const (context, name, 3, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ value); } CoglAttribute * cogl_attribute_new_const_4fv (CoglContext *context, const char *name, const float *value) { return _cogl_attribute_new_const (context, name, 4, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ value); } CoglAttribute * cogl_attribute_new_const_2f (CoglContext *context, const char *name, float component0, float component1) { float vec2[2] = { component0, component1 }; return _cogl_attribute_new_const (context, name, 2, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ vec2); } CoglAttribute * cogl_attribute_new_const_3f (CoglContext *context, const char *name, float component0, float component1, float component2) { float vec3[3] = { component0, component1, component2 }; return _cogl_attribute_new_const (context, name, 3, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ vec3); } CoglAttribute * cogl_attribute_new_const_4f (CoglContext *context, const char *name, float component0, float component1, float component2, float component3) { float vec4[4] = { component0, component1, component2, component3 }; return _cogl_attribute_new_const (context, name, 4, /* n_components */ 1, /* 1 column vector */ FALSE, /* no transpose */ vec4); } CoglAttribute * cogl_attribute_new_const_2x2fv (CoglContext *context, const char *name, const float *matrix2x2, gboolean transpose) { return _cogl_attribute_new_const (context, name, 2, /* n_components */ 2, /* 2 column vector */ FALSE, /* no transpose */ matrix2x2); } CoglAttribute * cogl_attribute_new_const_3x3fv (CoglContext *context, const char *name, const float *matrix3x3, gboolean transpose) { return _cogl_attribute_new_const (context, name, 3, /* n_components */ 3, /* 3 column vector */ FALSE, /* no transpose */ matrix3x3); } CoglAttribute * cogl_attribute_new_const_4x4fv (CoglContext *context, const char *name, const float *matrix4x4, gboolean transpose) { return _cogl_attribute_new_const (context, name, 4, /* n_components */ 4, /* 4 column vector */ FALSE, /* no transpose */ matrix4x4); } gboolean cogl_attribute_get_normalized (CoglAttribute *attribute) { g_return_val_if_fail (cogl_is_attribute (attribute), FALSE); return attribute->normalized; } static void warn_about_midscene_changes (void) { static gboolean seen = FALSE; if (!seen) { g_warning ("Mid-scene modification of attributes has " "undefined results\n"); seen = TRUE; } } void cogl_attribute_set_normalized (CoglAttribute *attribute, gboolean normalized) { g_return_if_fail (cogl_is_attribute (attribute)); if (G_UNLIKELY (attribute->immutable_ref)) warn_about_midscene_changes (); attribute->normalized = normalized; } CoglAttributeBuffer * cogl_attribute_get_buffer (CoglAttribute *attribute) { g_return_val_if_fail (cogl_is_attribute (attribute), NULL); g_return_val_if_fail (attribute->is_buffered, NULL); return attribute->d.buffered.attribute_buffer; } void cogl_attribute_set_buffer (CoglAttribute *attribute, CoglAttributeBuffer *attribute_buffer) { g_return_if_fail (cogl_is_attribute (attribute)); g_return_if_fail (attribute->is_buffered); if (G_UNLIKELY (attribute->immutable_ref)) warn_about_midscene_changes (); cogl_object_ref (attribute_buffer); cogl_object_unref (attribute->d.buffered.attribute_buffer); attribute->d.buffered.attribute_buffer = attribute_buffer; } CoglAttribute * _cogl_attribute_immutable_ref (CoglAttribute *attribute) { CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer); g_return_val_if_fail (cogl_is_attribute (attribute), NULL); attribute->immutable_ref++; _cogl_buffer_immutable_ref (buffer); return attribute; } void _cogl_attribute_immutable_unref (CoglAttribute *attribute) { CoglBuffer *buffer = COGL_BUFFER (attribute->d.buffered.attribute_buffer); g_return_if_fail (cogl_is_attribute (attribute)); g_return_if_fail (attribute->immutable_ref > 0); attribute->immutable_ref--; _cogl_buffer_immutable_unref (buffer); } static void _cogl_attribute_free (CoglAttribute *attribute) { if (attribute->is_buffered) cogl_object_unref (attribute->d.buffered.attribute_buffer); else _cogl_boxed_value_destroy (&attribute->d.constant.boxed); g_slice_free (CoglAttribute, attribute); } static gboolean validate_layer_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { CoglTexture *texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); CoglFlushLayerState *state = user_data; gboolean status = TRUE; /* invalid textures will be handled correctly in * _cogl_pipeline_flush_layers_gl_state */ if (texture == NULL) goto validated; _cogl_texture_flush_journal_rendering (texture); /* Give the texture a chance to know that we're rendering non-quad shaped primitives. If the texture is in an atlas it will be migrated */ _cogl_texture_ensure_non_quad_rendering (texture); /* We need to ensure the mipmaps are ready before deciding * anything else about the texture because the texture storate * could completely change if it needs to be migrated out of the * atlas and will affect how we validate the layer. */ _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); if (!_cogl_texture_can_hardware_repeat (texture)) { g_warning ("Disabling layer %d of the current source material, " "because texturing with the vertex buffer API is not " "currently supported using sliced textures, or textures " "with waste\n", layer_index); /* XXX: maybe we can add a mechanism for users to forcibly use * textures with waste where it would be their responsability to use * texture coords in the range [0,1] such that sampling outside isn't * required. We can then use a texture matrix (or a modification of * the users own matrix) to map 1 to the edge of the texture data. * * Potentially, given the same guarantee as above we could also * support a single sliced layer too. We would have to redraw the * vertices once for each layer, each time with a fiddled texture * matrix. */ state->fallback_layers |= (1 << state->unit); state->options.flags |= COGL_PIPELINE_FLUSH_FALLBACK_MASK; } validated: state->unit++; return status; } void _cogl_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes) { CoglContext *ctx = framebuffer->context; CoglFlushLayerState layers_state; CoglPipeline *copy = NULL; if (!(flags & COGL_DRAW_SKIP_JOURNAL_FLUSH)) _cogl_journal_flush (framebuffer->journal); layers_state.unit = 0; layers_state.options.flags = 0; layers_state.fallback_layers = 0; if (!(flags & COGL_DRAW_SKIP_PIPELINE_VALIDATION)) cogl_pipeline_foreach_layer (pipeline, validate_layer_cb, &layers_state); /* NB: _cogl_framebuffer_flush_state may disrupt various state (such * as the pipeline state) when flushing the clip stack, so should * always be done first when preparing to draw. We need to do this * before setting up the array pointers because setting up the clip * stack can cause some drawing which would change the array * pointers. */ if (!(flags & COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH)) _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_ALL); /* In cogl_read_pixels we have a fast-path when reading a single * pixel and the scene is just comprised of simple rectangles still * in the journal. For this optimization to work we need to track * when the framebuffer really does get drawn to. */ _cogl_framebuffer_mark_clear_clip_dirty (framebuffer); ctx->driver_vtable->flush_attributes_state (framebuffer, pipeline, &layers_state, flags, attributes, n_attributes); if (copy) cogl_object_unref (copy); } int _cogl_attribute_get_n_components (CoglAttribute *attribute) { if (attribute->is_buffered) return attribute->d.buffered.n_components; else return attribute->d.constant.boxed.size; } muffin-6.4.1/cogl/cogl/cogl-sub-texture-private.h0000664000175000017500000000437514723361714020655 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_SUB_TEXTURE_PRIVATE_H #define __COGL_SUB_TEXTURE_PRIVATE_H #include "cogl-texture-private.h" #include struct _CoglSubTexture { CoglTexture _parent; /* This is the texture that was passed in to _cogl_sub_texture_new. If this is also a sub texture then we will use the full texture from that to render instead of making a chain. However we want to preserve the next texture in case the user is expecting us to keep a reference and also so that we can later add a cogl_sub_texture_get_parent_texture() function. */ CoglTexture *next_texture; /* This is the texture that will actually be used to draw. It will point to the end of the chain if a sub texture of a sub texture is created */ CoglTexture *full_texture; /* The offset of the region represented by this sub-texture. This is * the offset in full_texture which won't necessarily be the same as * the offset passed to _cogl_sub_texture_new if next_texture is * actually already a sub texture */ int sub_x; int sub_y; }; #endif /* __COGL_SUB_TEXTURE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-closure-list.c0000664000175000017500000000406714723361714017334 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "cogl-config.h" #include #include "cogl-closure-list-private.h" void _cogl_closure_disconnect (CoglClosure *closure) { _cogl_list_remove (&closure->link); if (closure->destroy_cb) closure->destroy_cb (closure->user_data); g_slice_free (CoglClosure, closure); } void _cogl_closure_list_disconnect_all (CoglList *list) { CoglClosure *closure, *next; _cogl_list_for_each_safe (closure, next, list, link) _cogl_closure_disconnect (closure); } CoglClosure * _cogl_closure_list_add (CoglList *list, void *function, void *user_data, CoglUserDataDestroyCallback destroy_cb) { CoglClosure *closure = g_slice_new (CoglClosure); closure->function = function; closure->user_data = user_data; closure->destroy_cb = destroy_cb; _cogl_list_insert (list, &closure->link); return closure; } muffin-6.4.1/cogl/cogl/cogl-spans.c0000664000175000017500000001230614723361714016026 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "math.h" #include "cogl-util.h" #include "cogl-spans.h" void _cogl_span_iter_update (CoglSpanIter *iter) { /* Pick current span */ iter->span = &iter->spans[iter->index]; /* Offset next position by span size */ iter->next_pos = iter->pos + iter->span->size - iter->span->waste; /* Check if span intersects the area to cover */ if (iter->next_pos <= iter->cover_start || iter->pos >= iter->cover_end) { /* Intersection undefined */ iter->intersects = FALSE; return; } iter->intersects = TRUE; /* Clip start position to coverage area */ if (iter->pos < iter->cover_start) iter->intersect_start = iter->cover_start; else iter->intersect_start = iter->pos; /* Clip end position to coverage area */ if (iter->next_pos > iter->cover_end) iter->intersect_end = iter->cover_end; else iter->intersect_end = iter->next_pos; } void _cogl_span_iter_begin (CoglSpanIter *iter, const CoglSpan *spans, int n_spans, float normalize_factor, float cover_start, float cover_end, CoglPipelineWrapMode wrap_mode) { /* XXX: If CLAMP_TO_EDGE needs to be emulated then it needs to be * done at a higher level than here... */ g_return_if_fail (wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT || wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT); iter->span = NULL; iter->spans = spans; iter->n_spans = n_spans; /* We always iterate in a positive direction from the origin. If * iter->flipped == TRUE that means whoever is using this API should * interpreted the current span as extending in the opposite direction. I.e. * it extends to the left if iterating the X axis, or up if the Y axis. */ if (cover_start > cover_end) { float tmp = cover_start; cover_start = cover_end; cover_end = tmp; iter->flipped = TRUE; } else iter->flipped = FALSE; /* The texture spans cover the normalized texture coordinate space ranging * from [0,1] but to help support repeating of sliced textures we allow * iteration of any range so we need to relate the start of the range to the * nearest point equivalent to 0. */ if (normalize_factor != 1.0) { float cover_start_normalized = cover_start / normalize_factor; iter->origin = floorf (cover_start_normalized) * normalize_factor; } else iter->origin = floorf (cover_start); iter->wrap_mode = wrap_mode; if (wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT) iter->index = 0; else if (wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT) { if ((int)iter->origin % 2) { iter->index = iter->n_spans - 1; iter->mirror_direction = -1; iter->flipped = !iter->flipped; } else { iter->index = 0; iter->mirror_direction = 1; } } else g_warn_if_reached (); iter->cover_start = cover_start; iter->cover_end = cover_end; iter->pos = iter->origin; /* Update intersection */ _cogl_span_iter_update (iter); while (iter->next_pos <= iter->cover_start) _cogl_span_iter_next (iter); } void _cogl_span_iter_next (CoglSpanIter *iter) { /* Move current position */ iter->pos = iter->next_pos; if (iter->wrap_mode == COGL_PIPELINE_WRAP_MODE_REPEAT) iter->index = (iter->index + 1) % iter->n_spans; else if (iter->wrap_mode == COGL_PIPELINE_WRAP_MODE_MIRRORED_REPEAT) { iter->index += iter->mirror_direction; if (iter->index == iter->n_spans || iter->index == -1) { iter->mirror_direction = -iter->mirror_direction; iter->index += iter->mirror_direction; iter->flipped = !iter->flipped; } } else g_warn_if_reached (); /* Update intersection */ _cogl_span_iter_update (iter); } gboolean _cogl_span_iter_end (CoglSpanIter *iter) { /* End reached when whole area covered */ return iter->pos >= iter->cover_end; } muffin-6.4.1/cogl/cogl/cogl-texture-2d-sliced.c0000664000175000017500000013432114723361714020150 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Matthew Allum * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-private.h" #include "cogl-util.h" #include "cogl-bitmap.h" #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-texture-2d-sliced-private.h" #include "cogl-texture-driver.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-spans.h" #include "cogl-journal-private.h" #include "cogl-primitive-texture.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include #include #include static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds); COGL_TEXTURE_DEFINE (Texture2DSliced, texture_2d_sliced); COGL_GTYPE_DEFINE_CLASS (Texture2DSliced, texture_2d_sliced, COGL_GTYPE_IMPLEMENT_INTERFACE (texture)); static const CoglTextureVtable cogl_texture_2d_sliced_vtable; typedef struct _ForeachData { CoglMetaTextureCallback callback; void *user_data; float x_normalize_factor; float y_normalize_factor; } ForeachData; static void re_normalize_sub_texture_coords_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { ForeachData *data = user_data; /* The coordinates passed to the span iterating code were * un-normalized so we need to renormalize them before passing them * on */ float re_normalized_coords[4] = { meta_coords[0] * data->x_normalize_factor, meta_coords[1] * data->y_normalize_factor, meta_coords[2] * data->x_normalize_factor, meta_coords[3] * data->y_normalize_factor }; data->callback (sub_texture, sub_texture_coords, re_normalized_coords, data->user_data); } static void _cogl_texture_2d_sliced_foreach_sub_texture_in_region ( CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglSpan *x_spans = (CoglSpan *)tex_2ds->slice_x_spans->data; CoglSpan *y_spans = (CoglSpan *)tex_2ds->slice_y_spans->data; CoglTexture **textures = (CoglTexture **)tex_2ds->slice_textures->data; float un_normalized_coords[4]; ForeachData data; /* NB: its convenient for us to store non-normalized coordinates in * our CoglSpans but that means we need to un-normalize the incoming * virtual coordinates and make sure we re-normalize the coordinates * before calling the given callback. */ data.callback = callback; data.user_data = user_data; data.x_normalize_factor = 1.0f / tex->width; data.y_normalize_factor = 1.0f / tex->height; un_normalized_coords[0] = virtual_tx_1 * tex->width; un_normalized_coords[1] = virtual_ty_1 * tex->height; un_normalized_coords[2] = virtual_tx_2 * tex->width; un_normalized_coords[3] = virtual_ty_2 * tex->height; /* Note that the normalize factors passed here are the reciprocal of * the factors calculated above because the span iterating code * normalizes by dividing by the factor instead of multiplying */ _cogl_texture_spans_foreach_in_region (x_spans, tex_2ds->slice_x_spans->len, y_spans, tex_2ds->slice_y_spans->len, textures, un_normalized_coords, tex->width, tex->height, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, re_normalize_sub_texture_coords_cb, &data); } static uint8_t * _cogl_texture_2d_sliced_allocate_waste_buffer (CoglTexture2DSliced *tex_2ds, CoglPixelFormat format) { CoglSpan *last_x_span; CoglSpan *last_y_span; uint8_t *waste_buf = NULL; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); /* If the texture has any waste then allocate a buffer big enough to fill the gaps */ last_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, tex_2ds->slice_x_spans->len - 1); last_y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, tex_2ds->slice_y_spans->len - 1); if (last_x_span->waste > 0 || last_y_span->waste > 0) { int bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); CoglSpan *first_x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); CoglSpan *first_y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); unsigned int right_size = first_y_span->size * last_x_span->waste; unsigned int bottom_size = first_x_span->size * last_y_span->waste; waste_buf = g_malloc (MAX (right_size, bottom_size) * bpp); } return waste_buf; } static gboolean _cogl_texture_2d_sliced_set_waste (CoglTexture2DSliced *tex_2ds, CoglBitmap *source_bmp, CoglTexture2D *slice_tex, uint8_t *waste_buf, CoglSpan *x_span, CoglSpan *y_span, CoglSpanIter *x_iter, CoglSpanIter *y_iter, int src_x, int src_y, int dst_x, int dst_y, GError **error) { gboolean need_x, need_y; CoglContext *ctx = COGL_TEXTURE (tex_2ds)->context; /* If the x_span is sliced and the upload touches the rightmost pixels then fill the waste with copies of the pixels */ need_x = x_span->waste > 0 && x_iter->intersect_end - x_iter->pos >= x_span->size - x_span->waste; /* same for the bottom-most pixels */ need_y = y_span->waste > 0 && y_iter->intersect_end - y_iter->pos >= y_span->size - y_span->waste; if (need_x || need_y) { int bmp_rowstride = cogl_bitmap_get_rowstride (source_bmp); CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp; uint8_t *bmp_data; const uint8_t *src; uint8_t *dst; unsigned int wy, wx; CoglBitmap *waste_bmp; /* We only support single plane formats here */ if (cogl_pixel_format_get_n_planes (source_format) == 1) return FALSE; bmp_data = _cogl_bitmap_map (source_bmp, COGL_BUFFER_ACCESS_READ, 0, error); if (bmp_data == NULL) return FALSE; bpp = cogl_pixel_format_get_bytes_per_pixel (source_format, 0); if (need_x) { src = (bmp_data + ((src_y + (int) y_iter->intersect_start - dst_y) * bmp_rowstride) + (src_x + (int)x_span->start + (int)x_span->size - (int)x_span->waste - dst_x - 1) * bpp); dst = waste_buf; for (wy = 0; wy < y_iter->intersect_end - y_iter->intersect_start; wy++) { for (wx = 0; wx < x_span->waste; wx++) { memcpy (dst, src, bpp); dst += bpp; } src += bmp_rowstride; } waste_bmp = cogl_bitmap_new_for_data (ctx, x_span->waste, y_iter->intersect_end - y_iter->intersect_start, source_format, x_span->waste * bpp, waste_buf); if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), 0, /* src_x */ 0, /* src_y */ x_span->waste, /* width */ /* height */ y_iter->intersect_end - y_iter->intersect_start, waste_bmp, /* dst_x */ x_span->size - x_span->waste, y_iter->intersect_start - y_span->start, /* dst_y */ 0, /* level */ error)) { cogl_object_unref (waste_bmp); _cogl_bitmap_unmap (source_bmp); return FALSE; } cogl_object_unref (waste_bmp); } if (need_y) { unsigned int copy_width, intersect_width; src = (bmp_data + ((src_x + (int) x_iter->intersect_start - dst_x) * bpp) + (src_y + (int)y_span->start + (int)y_span->size - (int)y_span->waste - dst_y - 1) * bmp_rowstride); dst = waste_buf; if (x_iter->intersect_end - x_iter->pos >= x_span->size - x_span->waste) copy_width = x_span->size + x_iter->pos - x_iter->intersect_start; else copy_width = x_iter->intersect_end - x_iter->intersect_start; intersect_width = x_iter->intersect_end - x_iter->intersect_start; for (wy = 0; wy < y_span->waste; wy++) { memcpy (dst, src, intersect_width * bpp); dst += intersect_width * bpp; for (wx = intersect_width; wx < copy_width; wx++) { memcpy (dst, dst - bpp, bpp); dst += bpp; } } waste_bmp = cogl_bitmap_new_for_data (ctx, copy_width, y_span->waste, source_format, copy_width * bpp, waste_buf); if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), 0, /* src_x */ 0, /* src_y */ copy_width, /* width */ y_span->waste, /* height */ waste_bmp, /* dst_x */ x_iter->intersect_start - x_iter->pos, /* dst_y */ y_span->size - y_span->waste, 0, /* level */ error)) { cogl_object_unref (waste_bmp); _cogl_bitmap_unmap (source_bmp); return FALSE; } cogl_object_unref (waste_bmp); } _cogl_bitmap_unmap (source_bmp); } return TRUE; } static gboolean _cogl_texture_2d_sliced_upload_bitmap (CoglTexture2DSliced *tex_2ds, CoglBitmap *bmp, GError **error) { CoglSpan *x_span; CoglSpan *y_span; CoglTexture2D *slice_tex; int x, y; uint8_t *waste_buf; CoglPixelFormat bmp_format; bmp_format = cogl_bitmap_get_format (bmp); waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, bmp_format); /* Iterate vertical slices */ for (y = 0; y < tex_2ds->slice_y_spans->len; ++y) { y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y); /* Iterate horizontal slices */ for (x = 0; x < tex_2ds->slice_x_spans->len; ++x) { int slice_num = y * tex_2ds->slice_x_spans->len + x; CoglSpanIter x_iter, y_iter; x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x); /* Pick the gl texture object handle */ slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, slice_num); if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), x_span->start, /* src x */ y_span->start, /* src y */ x_span->size - x_span->waste, /* width */ y_span->size - y_span->waste, /* height */ bmp, 0, /* dst x */ 0, /* dst y */ 0, /* level */ error)) { if (waste_buf) g_free (waste_buf); return FALSE; } /* Set up a fake iterator that covers the whole slice */ x_iter.intersect_start = x_span->start; x_iter.intersect_end = (x_span->start + x_span->size - x_span->waste); x_iter.pos = x_span->start; y_iter.intersect_start = y_span->start; y_iter.intersect_end = (y_span->start + y_span->size - y_span->waste); y_iter.pos = y_span->start; if (!_cogl_texture_2d_sliced_set_waste (tex_2ds, bmp, slice_tex, waste_buf, x_span, y_span, &x_iter, &y_iter, 0, /* src_x */ 0, /* src_y */ 0, /* dst_x */ 0, error)) /* dst_y */ { if (waste_buf) g_free (waste_buf); return FALSE; } } } if (waste_buf) g_free (waste_buf); return TRUE; } static gboolean _cogl_texture_2d_sliced_upload_subregion (CoglTexture2DSliced *tex_2ds, int src_x, int src_y, int dst_x, int dst_y, int width, int height, CoglBitmap *source_bmp, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglSpan *x_span; CoglSpan *y_span; CoglSpanIter x_iter; CoglSpanIter y_iter; CoglTexture2D *slice_tex; int source_x = 0, source_y = 0; int inter_w = 0, inter_h = 0; int local_x = 0, local_y = 0; uint8_t *waste_buf; CoglPixelFormat source_format; source_format = cogl_bitmap_get_format (source_bmp); waste_buf = _cogl_texture_2d_sliced_allocate_waste_buffer (tex_2ds, source_format); /* Iterate vertical spans */ for (source_y = src_y, _cogl_span_iter_begin (&y_iter, (CoglSpan *)tex_2ds->slice_y_spans->data, tex_2ds->slice_y_spans->len, tex->height, dst_y, dst_y + height, COGL_PIPELINE_WRAP_MODE_REPEAT); !_cogl_span_iter_end (&y_iter); _cogl_span_iter_next (&y_iter), source_y += inter_h ) { y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y_iter.index); /* Iterate horizontal spans */ for (source_x = src_x, _cogl_span_iter_begin (&x_iter, (CoglSpan *)tex_2ds->slice_x_spans->data, tex_2ds->slice_x_spans->len, tex->width, dst_x, dst_x + width, COGL_PIPELINE_WRAP_MODE_REPEAT); !_cogl_span_iter_end (&x_iter); _cogl_span_iter_next (&x_iter), source_x += inter_w ) { int slice_num; x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x_iter.index); /* Pick intersection width and height */ inter_w = (x_iter.intersect_end - x_iter.intersect_start); inter_h = (y_iter.intersect_end - y_iter.intersect_start); /* Localize intersection top-left corner to slice*/ local_x = (x_iter.intersect_start - x_iter.pos); local_y = (y_iter.intersect_start - y_iter.pos); slice_num = y_iter.index * tex_2ds->slice_x_spans->len + x_iter.index; /* Pick slice texture */ slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, slice_num); if (!_cogl_texture_set_region_from_bitmap (COGL_TEXTURE (slice_tex), source_x, source_y, inter_w, /* width */ inter_h, /* height */ source_bmp, local_x, /* dst x */ local_y, /* dst y */ 0, /* level */ error)) { if (waste_buf) g_free (waste_buf); return FALSE; } if (!_cogl_texture_2d_sliced_set_waste (tex_2ds, source_bmp, slice_tex, waste_buf, x_span, y_span, &x_iter, &y_iter, src_x, src_y, dst_x, dst_y, error)) { if (waste_buf) g_free (waste_buf); return FALSE; } } } if (waste_buf) g_free (waste_buf); return TRUE; } static int _cogl_rect_slices_for_size (int size_to_fill, int max_span_size, int max_waste, GArray *out_spans) { int n_spans = 0; CoglSpan span; /* Init first slice span */ span.start = 0; span.size = max_span_size; span.waste = 0; /* Repeat until whole area covered */ while (size_to_fill >= span.size) { /* Add another slice span of same size */ if (out_spans) g_array_append_val (out_spans, span); span.start += span.size; size_to_fill -= span.size; n_spans++; } /* Add one last smaller slice span */ if (size_to_fill > 0) { span.size = size_to_fill; if (out_spans) g_array_append_val (out_spans, span); n_spans++; } return n_spans; } static void _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); int i; /* Pass the set wrap mode on to all of the child textures */ for (i = 0; i < tex_2ds->slice_textures->len; i++) { CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); _cogl_texture_gl_flush_legacy_texobj_wrap_modes (COGL_TEXTURE (slice_tex), wrap_mode_s, wrap_mode_t); } } static void free_spans (CoglTexture2DSliced *tex_2ds) { if (tex_2ds->slice_x_spans != NULL) { g_array_free (tex_2ds->slice_x_spans, TRUE); tex_2ds->slice_x_spans = NULL; } if (tex_2ds->slice_y_spans != NULL) { g_array_free (tex_2ds->slice_y_spans, TRUE); tex_2ds->slice_y_spans = NULL; } } static gboolean setup_spans (CoglContext *ctx, CoglTexture2DSliced *tex_2ds, int width, int height, int max_waste, CoglPixelFormat internal_format, GError **error) { int max_width; int max_height; int n_x_slices; int n_y_slices; int (*slices_for_size) (int, int, int, GArray*); /* Initialize size of largest slice according to supported features */ max_width = width; max_height = height; slices_for_size = _cogl_rect_slices_for_size; /* Negative number means no slicing forced by the user */ if (max_waste <= -1) { CoglSpan span; /* Check if size supported else bail out */ if (!ctx->driver_vtable->texture_2d_can_create (ctx, max_width, max_height, internal_format)) { g_set_error (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "Sliced texture size of %d x %d not possible " "with max waste set to -1", width, height); return FALSE; } n_x_slices = 1; n_y_slices = 1; /* Init span arrays */ tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), 1); /* Add a single span for width and height */ span.start = 0; span.size = max_width; span.waste = max_width - width; g_array_append_val (tex_2ds->slice_x_spans, span); span.size = max_height; span.waste = max_height - height; g_array_append_val (tex_2ds->slice_y_spans, span); } else { /* Decrease the size of largest slice until supported by GL */ while (!ctx->driver_vtable->texture_2d_can_create (ctx, max_width, max_height, internal_format)) { /* Alternate between width and height */ if (max_width > max_height) max_width /= 2; else max_height /= 2; if (max_width == 0 || max_height == 0) { /* Maybe it would be ok to just g_warn_if_reached() for this * codepath */ g_set_error (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "No suitable slice geometry found"); free_spans (tex_2ds); return FALSE; } } /* Determine the slices required to cover the bitmap area */ n_x_slices = slices_for_size (width, max_width, max_waste, NULL); n_y_slices = slices_for_size (height, max_height, max_waste, NULL); /* Init span arrays with reserved size */ tex_2ds->slice_x_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), n_x_slices); tex_2ds->slice_y_spans = g_array_sized_new (FALSE, FALSE, sizeof (CoglSpan), n_y_slices); /* Fill span arrays with info */ slices_for_size (width, max_width, max_waste, tex_2ds->slice_x_spans); slices_for_size (height, max_height, max_waste, tex_2ds->slice_y_spans); } return TRUE; } static void free_slices (CoglTexture2DSliced *tex_2ds) { if (tex_2ds->slice_textures != NULL) { int i; for (i = 0; i < tex_2ds->slice_textures->len; i++) { CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); cogl_object_unref (slice_tex); } g_array_free (tex_2ds->slice_textures, TRUE); } free_spans (tex_2ds); } static gboolean allocate_slices (CoglTexture2DSliced *tex_2ds, int width, int height, int max_waste, CoglPixelFormat internal_format, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglContext *ctx = tex->context; int n_x_slices; int n_y_slices; int n_slices; int x, y; CoglSpan *x_span; CoglSpan *y_span; tex_2ds->internal_format = internal_format; if (!setup_spans (ctx, tex_2ds, width, height, max_waste, internal_format, error)) { return FALSE; } n_x_slices = tex_2ds->slice_x_spans->len; n_y_slices = tex_2ds->slice_y_spans->len; n_slices = n_x_slices * n_y_slices; tex_2ds->slice_textures = g_array_sized_new (FALSE, FALSE, sizeof (CoglTexture2D *), n_slices); /* Allocate each slice */ for (y = 0; y < n_y_slices; ++y) { y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, y); for (x = 0; x < n_x_slices; ++x) { CoglTexture *slice; x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, x); COGL_NOTE (SLICING, "CREATE SLICE (%d,%d)\tsize (%d,%d)", x, y, (int)(x_span->size - x_span->waste), (int)(y_span->size - y_span->waste)); slice = COGL_TEXTURE ( cogl_texture_2d_new_with_size (ctx, x_span->size, y_span->size)); _cogl_texture_copy_internal_format (tex, slice); g_array_append_val (tex_2ds->slice_textures, slice); if (!cogl_texture_allocate (slice, error)) { free_slices (tex_2ds); return FALSE; } } } return TRUE; } static void _cogl_texture_2d_sliced_free (CoglTexture2DSliced *tex_2ds) { free_slices (tex_2ds); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (tex_2ds)); } static CoglTexture2DSliced * _cogl_texture_2d_sliced_create_base (CoglContext *ctx, int width, int height, int max_waste, CoglPixelFormat internal_format, CoglTextureLoader *loader) { CoglTexture2DSliced *tex_2ds = g_new0 (CoglTexture2DSliced, 1); _cogl_texture_init (COGL_TEXTURE (tex_2ds), ctx, width, height, internal_format, loader, &cogl_texture_2d_sliced_vtable); tex_2ds->max_waste = max_waste; return _cogl_texture_2d_sliced_object_new (tex_2ds); } CoglTexture2DSliced * cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, int width, int height, int max_waste) { CoglTextureLoader *loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; loader->src.sized.width = width; loader->src.sized.height = height; return _cogl_texture_2d_sliced_create_base (ctx, width, height, max_waste, COGL_PIXEL_FORMAT_RGBA_8888_PRE, loader); } CoglTexture2DSliced * _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, int max_waste, gboolean can_convert_in_place) { CoglTextureLoader *loader; g_return_val_if_fail (cogl_is_bitmap (bmp), NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; loader->src.bitmap.bitmap = cogl_object_ref (bmp); loader->src.bitmap.can_convert_in_place = can_convert_in_place; return _cogl_texture_2d_sliced_create_base (_cogl_bitmap_get_context (bmp), cogl_bitmap_get_width (bmp), cogl_bitmap_get_height (bmp), max_waste, cogl_bitmap_get_format (bmp), loader); } CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, int max_waste) { return _cogl_texture_2d_sliced_new_from_bitmap (bmp, max_waste, FALSE); } CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, int width, int height, int max_waste, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error) { CoglBitmap *bmp; CoglTexture2DSliced *tex_2ds; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); g_return_val_if_fail (data != NULL, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); /* Wrap the data into a bitmap */ bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); tex_2ds = cogl_texture_2d_sliced_new_from_bitmap (bmp, max_waste); cogl_object_unref (bmp); if (tex_2ds && !cogl_texture_allocate (COGL_TEXTURE (tex_2ds), error)) { cogl_object_unref (tex_2ds); return NULL; } return tex_2ds; } CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, const char *filename, int max_waste, GError **error) { CoglBitmap *bmp; CoglTexture2DSliced *tex_2ds = NULL; g_return_val_if_fail (error == NULL || *error == NULL, NULL); bmp = _cogl_bitmap_from_file (ctx, filename, error); if (bmp == NULL) return NULL; tex_2ds = _cogl_texture_2d_sliced_new_from_bitmap (bmp, max_waste, TRUE); /* can convert in-place */ cogl_object_unref (bmp); return tex_2ds; } static gboolean allocate_with_size (CoglTexture2DSliced *tex_2ds, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglPixelFormat internal_format = _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); if (allocate_slices (tex_2ds, loader->src.sized.width, loader->src.sized.height, tex_2ds->max_waste, internal_format, error)) { _cogl_texture_set_allocated (COGL_TEXTURE (tex_2ds), internal_format, loader->src.sized.width, loader->src.sized.height); return TRUE; } else return FALSE; } static gboolean allocate_from_bitmap (CoglTexture2DSliced *tex_2ds, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2ds); CoglBitmap *bmp = loader->src.bitmap.bitmap; int width = cogl_bitmap_get_width (bmp); int height = cogl_bitmap_get_height (bmp); gboolean can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglPixelFormat internal_format; CoglBitmap *upload_bmp; g_return_val_if_fail (tex_2ds->slice_textures == NULL, FALSE); internal_format = _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return FALSE; if (!allocate_slices (tex_2ds, width, height, tex_2ds->max_waste, internal_format, error)) { cogl_object_unref (upload_bmp); return FALSE; } if (!_cogl_texture_2d_sliced_upload_bitmap (tex_2ds, upload_bmp, error)) { free_slices (tex_2ds); cogl_object_unref (upload_bmp); return FALSE; } cogl_object_unref (upload_bmp); _cogl_texture_set_allocated (tex, internal_format, width, height); return TRUE; } static gboolean _cogl_texture_2d_sliced_allocate (CoglTexture *tex, GError **error) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglTextureLoader *loader = tex->loader; g_return_val_if_fail (loader, FALSE); switch (loader->src_type) { case COGL_TEXTURE_SOURCE_TYPE_SIZED: return allocate_with_size (tex_2ds, loader, error); case COGL_TEXTURE_SOURCE_TYPE_BITMAP: return allocate_from_bitmap (tex_2ds, loader, error); default: break; } g_return_val_if_reached (FALSE); } static int _cogl_texture_2d_sliced_get_max_waste (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); return tex_2ds->max_waste; } static gboolean _cogl_texture_2d_sliced_is_sliced (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); /* It's only after allocating a sliced texture that we will know * whether it really needed to be sliced... */ if (!tex->allocated) cogl_texture_allocate (tex, NULL); if (tex_2ds->slice_x_spans->len != 1 || tex_2ds->slice_y_spans->len != 1) return TRUE; else return FALSE; } static gboolean _cogl_texture_2d_sliced_can_hardware_repeat (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglTexture2D *slice_tex; CoglSpan *x_span; CoglSpan *y_span; /* If there's more than one texture then we can't hardware repeat */ if (tex_2ds->slice_textures->len != 1) return FALSE; /* If there's any waste then we can't hardware repeat */ x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); if (x_span->waste > 0 || y_span->waste > 0) return FALSE; /* Otherwise pass the query on to the single slice texture */ slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); return _cogl_texture_can_hardware_repeat (COGL_TEXTURE (slice_tex)); } static void _cogl_texture_2d_sliced_transform_coords_to_gl (CoglTexture *tex, float *s, float *t) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglSpan *x_span; CoglSpan *y_span; CoglTexture2D *slice_tex; g_assert (!_cogl_texture_2d_sliced_is_sliced (tex)); /* Don't include the waste in the texture coordinates */ x_span = &g_array_index (tex_2ds->slice_x_spans, CoglSpan, 0); y_span = &g_array_index (tex_2ds->slice_y_spans, CoglSpan, 0); *s *= tex->width / (float)x_span->size; *t *= tex->height / (float)y_span->size; /* Let the child texture further transform the coords */ slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); _cogl_texture_transform_coords_to_gl (COGL_TEXTURE (slice_tex), s, t); } static CoglTransformResult _cogl_texture_2d_sliced_transform_quad_coords_to_gl (CoglTexture *tex, float *coords) { gboolean need_repeat = FALSE; int i; /* This is a bit lazy - in the case where the quad lies entirely * within a single slice we could avoid the fallback. But that * could likely lead to visual inconsistency if the fallback involves * dropping layers, so this might be the right thing to do anyways. */ if (_cogl_texture_2d_sliced_is_sliced (tex)) return COGL_TRANSFORM_SOFTWARE_REPEAT; for (i = 0; i < 4; i++) if (coords[i] < 0.0f || coords[i] > 1.0f) need_repeat = TRUE; if (need_repeat && !_cogl_texture_2d_sliced_can_hardware_repeat (tex)) return COGL_TRANSFORM_SOFTWARE_REPEAT; _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 0, coords + 1); _cogl_texture_2d_sliced_transform_coords_to_gl (tex, coords + 2, coords + 3); return (need_repeat ? COGL_TRANSFORM_HARDWARE_REPEAT : COGL_TRANSFORM_NO_REPEAT); } static gboolean _cogl_texture_2d_sliced_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglTexture2D *slice_tex; if (tex_2ds->slice_textures == NULL) return FALSE; if (tex_2ds->slice_textures->len < 1) return FALSE; slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); return cogl_texture_get_gl_texture (COGL_TEXTURE (slice_tex), out_gl_handle, out_gl_target); } static void _cogl_texture_2d_sliced_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglTexture2D *slice_tex; int i; g_return_if_fail (tex_2ds->slice_textures != NULL); /* Apply new filters to every slice. The slice texture itself should cache the value and avoid resubmitting the same filter value to GL */ for (i = 0; i < tex_2ds->slice_textures->len; i++) { slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); _cogl_texture_gl_flush_legacy_texobj_filters (COGL_TEXTURE (slice_tex), min_filter, mag_filter); } } static void _cogl_texture_2d_sliced_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); int i; g_return_if_fail (tex_2ds->slice_textures != NULL); /* Pass the pre-paint on to every slice */ for (i = 0; i < tex_2ds->slice_textures->len; i++) { CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); _cogl_texture_pre_paint (COGL_TEXTURE (slice_tex), flags); } } static void _cogl_texture_2d_sliced_ensure_non_quad_rendering (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); int i; g_return_if_fail (tex_2ds->slice_textures != NULL); /* Pass the call on to every slice */ for (i = 0; i < tex_2ds->slice_textures->len; i++) { CoglTexture2D *slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, i); _cogl_texture_ensure_non_quad_rendering (COGL_TEXTURE (slice_tex)); } } static gboolean _cogl_texture_2d_sliced_set_region (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, int level, CoglBitmap *bmp, GError **error) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglBitmap *upload_bmp; gboolean status; upload_bmp = _cogl_bitmap_convert_for_upload (bmp, _cogl_texture_get_format (tex), FALSE, /* can't convert in place */ error); if (!upload_bmp) return FALSE; status = _cogl_texture_2d_sliced_upload_subregion (tex_2ds, src_x, src_y, dst_x, dst_y, dst_width, dst_height, upload_bmp, error); cogl_object_unref (upload_bmp); return status; } static CoglPixelFormat _cogl_texture_2d_sliced_get_format (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); return tex_2ds->internal_format; } static GLenum _cogl_texture_2d_sliced_get_gl_format (CoglTexture *tex) { CoglTexture2DSliced *tex_2ds = COGL_TEXTURE_2D_SLICED (tex); CoglTexture2D *slice_tex; /* Assert that we've allocated our slices at this point */ cogl_texture_allocate (tex, NULL); /* (abort on error) */ /* Pass the call on to the first slice */ slice_tex = g_array_index (tex_2ds->slice_textures, CoglTexture2D *, 0); return _cogl_texture_gl_get_format (COGL_TEXTURE (slice_tex)); } static const CoglTextureVtable cogl_texture_2d_sliced_vtable = { FALSE, /* not primitive */ _cogl_texture_2d_sliced_allocate, _cogl_texture_2d_sliced_set_region, NULL, /* is_get_data_supported */ NULL, /* get_data */ _cogl_texture_2d_sliced_foreach_sub_texture_in_region, _cogl_texture_2d_sliced_get_max_waste, _cogl_texture_2d_sliced_is_sliced, _cogl_texture_2d_sliced_can_hardware_repeat, _cogl_texture_2d_sliced_transform_coords_to_gl, _cogl_texture_2d_sliced_transform_quad_coords_to_gl, _cogl_texture_2d_sliced_get_gl_texture, _cogl_texture_2d_sliced_gl_flush_legacy_texobj_filters, _cogl_texture_2d_sliced_pre_paint, _cogl_texture_2d_sliced_ensure_non_quad_rendering, _cogl_texture_2d_sliced_gl_flush_legacy_texobj_wrap_modes, _cogl_texture_2d_sliced_get_format, _cogl_texture_2d_sliced_get_gl_format, NULL /* set_auto_mipmap */ }; muffin-6.4.1/cogl/cogl/cogl-context.h0000664000175000017500000003035614723361714016400 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_CONTEXT_H__ #define __COGL_CONTEXT_H__ /* We forward declare the CoglContext type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglContext CoglContext; #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-context * @short_description: The top level application context. * * A #CoglContext is the top most sandbox of Cogl state for an * application or toolkit. Its main purpose is to act as a sandbox * for the memory management of state objects. Normally an application * will only create a single context since there is no way to share * resources between contexts. * * For those familiar with OpenGL or perhaps Cairo it should be * understood that unlike these APIs a Cogl context isn't a rendering * context as such. In other words Cogl doesn't aim to provide a state * machine style model for configuring rendering parameters. Most * rendering state in Cogl is directly associated with user managed * objects called pipelines and geometry is drawn with a specific * pipeline object to a framebuffer object and those 3 things fully * define the state for drawing. This is an important part of Cogl's * design since it helps you write orthogonal rendering components * that can all access the same GPU without having to worry about * what state other components have left you with. * * Cogl does not maintain internal references to the context for * resources that depend on the context so applications. This is to * help applications control the lifetime a context without us needing to * introduce special api to handle the breakup of internal circular * references due to internal resources and caches associated with the * context. * * One a context has been destroyed then all directly or indirectly * dependant resources will be in an inconsistent state and should not * be manipulated or queried in any way. * * For applications that rely on the operating system to clean up * resources this policy shouldn't affect them, but for applications * that need to carefully destroy and re-create Cogl contexts multiple * times throughout their lifetime (such as Android applications) they * should be careful to destroy all context dependant resources, such as * framebuffers or textures etc before unrefing and destroying the * context. */ #define COGL_CONTEXT(OBJECT) ((CoglContext *)OBJECT) /** * cogl_context_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_context_get_gtype (void); /** * cogl_context_new: (constructor) (skip) * @display: (allow-none): A #CoglDisplay pointer * @error: A GError return location. * * Creates a new #CoglContext which acts as an application sandbox * for any state objects that are allocated. * * Return value: (transfer full): A newly allocated #CoglContext * Since: 1.8 * Stability: unstable */ COGL_EXPORT CoglContext * cogl_context_new (CoglDisplay *display, GError **error); /** * cogl_context_get_display: (skip) * @context: A #CoglContext pointer * * Retrieves the #CoglDisplay that is internally associated with the * given @context. This will return the same #CoglDisplay that was * passed to cogl_context_new() or if %NULL was passed to * cogl_context_new() then this function returns a pointer to the * display that was automatically setup internally. * * Return value: (transfer none): The #CoglDisplay associated with the * given @context. * Since: 1.8 * Stability: unstable */ COGL_EXPORT CoglDisplay * cogl_context_get_display (CoglContext *context); /** * cogl_context_get_renderer: (skip) * @context: A #CoglContext pointer * * Retrieves the #CoglRenderer that is internally associated with the * given @context. This will return the same #CoglRenderer that was * passed to cogl_display_new() or if %NULL was passed to * cogl_display_new() or cogl_context_new() then this function returns * a pointer to the renderer that was automatically connected * internally. * * Return value: (transfer none): The #CoglRenderer associated with the * given @context. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglRenderer * cogl_context_get_renderer (CoglContext *context); /** * cogl_is_context: * @object: An object or %NULL * * Gets whether the given object references an existing context object. * * Return value: %TRUE if the @object references a #CoglContext, * %FALSE otherwise * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_context (void *object); /* XXX: not guarded by the EXPERIMENTAL_API defines to avoid * upsetting glib-mkenums, but this can still be considered implicitly * experimental since it's only useable with experimental API... */ /** * CoglFeatureID: * @COGL_FEATURE_ID_TEXTURE_RG: Support for * %COGL_TEXTURE_COMPONENTS_RG as the internal components of a * texture. * @COGL_FEATURE_ID_UNSIGNED_INT_INDICES: Set if * %COGL_INDICES_TYPE_UNSIGNED_INT is supported in * cogl_indices_new(). * @COGL_FEATURE_ID_MAP_BUFFER_FOR_READ: Whether cogl_buffer_map() is * supported with CoglBufferAccess including read support. * @COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE: Whether cogl_buffer_map() is * supported with CoglBufferAccess including write support. * @COGL_FEATURE_ID_SWAP_BUFFERS_EVENT: * Available if the window system supports reporting an event * for swap buffer completions. * @COGL_FEATURE_ID_BUFFER_AGE: Available if the age of #CoglOnscreen back * buffers are tracked and so cogl_onscreen_get_buffer_age() can be * expected to return age values other than 0. * @COGL_FEATURE_ID_PRESENTATION_TIME: Whether frame presentation * time stamps will be recorded in #CoglFrameInfo objects. * * All the capabilities that can vary between different GPUs supported * by Cogl. Applications that depend on any of these features should explicitly * check for them using cogl_has_feature() or cogl_has_features(). * * Since: 1.10 */ typedef enum _CoglFeatureID { COGL_FEATURE_ID_UNSIGNED_INT_INDICES, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, COGL_FEATURE_ID_PRESENTATION_TIME, COGL_FEATURE_ID_FENCE, COGL_FEATURE_ID_TEXTURE_RG, COGL_FEATURE_ID_BUFFER_AGE, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, /*< private >*/ _COGL_N_FEATURE_IDS /*< skip >*/ } CoglFeatureID; /** * cogl_has_feature: * @context: A #CoglContext pointer * @feature: A #CoglFeatureID * * Checks if a given @feature is currently available * * Cogl does not aim to be a lowest common denominator API, it aims to * expose all the interesting features of GPUs to application which * means applications have some responsibility to explicitly check * that certain features are available before depending on them. * * Returns: %TRUE if the @feature is currently supported or %FALSE if * not. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_has_feature (CoglContext *context, CoglFeatureID feature); /** * cogl_has_features: * @context: A #CoglContext pointer * @...: A 0 terminated list of CoglFeatureIDs * * Checks if a list of features are all currently available. * * This checks all of the listed features using cogl_has_feature() and * returns %TRUE if all the features are available or %FALSE * otherwise. * * Return value: %TRUE if all the features are available, %FALSE * otherwise. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_has_features (CoglContext *context, ...); /** * CoglFeatureCallback: * @feature: A single feature currently supported by Cogl * @user_data: A private pointer passed to cogl_foreach_feature(). * * A callback used with cogl_foreach_feature() for enumerating all * context level features supported by Cogl. * * Since: 0.10 * Stability: unstable */ typedef void (*CoglFeatureCallback) (CoglFeatureID feature, void *user_data); /** * cogl_foreach_feature: * @context: A #CoglContext pointer * @callback: (scope call): A #CoglFeatureCallback called for each * supported feature * @user_data: (closure): Private data to pass to the callback * * Iterates through all the context level features currently supported * for a given @context and for each feature @callback is called. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_foreach_feature (CoglContext *context, CoglFeatureCallback callback, void *user_data); /** * cogl_get_clock_time: * @context: a #CoglContext pointer * * Returns the current time value from Cogl's internal clock. This * clock is used for measuring times such as the presentation time * in a #CoglFrameInfo. * * This method is meant for converting timestamps retrieved from Cogl * to other time systems, and is not meant to be used as a standalone * timing system. For that reason, if this function is called without * having retrieved a valid (non-zero) timestamp from Cogl first, it * may return 0 to indicate that Cogl has no active internal clock. * * Return value: the time value for the Cogl clock, in nanoseconds * from an arbitrary point in time, or 0 if Cogl doesn't have an * active internal clock. * Since: 1.14 * Stability: unstable */ COGL_EXPORT int64_t cogl_get_clock_time (CoglContext *context); /** * CoglGraphicsResetStatus: * @COGL_GRAPHICS_RESET_STATUS_NO_ERROR: * @COGL_GRAPHICS_RESET_STATUS_GUILTY_CONTEXT_RESET: * @COGL_GRAPHICS_RESET_STATUS_INNOCENT_CONTEXT_RESET: * @COGL_GRAPHICS_RESET_STATUS_UNKNOWN_CONTEXT_RESET: * @COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET: * * All the error values that might be returned by * cogl_get_graphics_reset_status(). Each value's meaning corresponds * to the similarly named value defined in the ARB_robustness and * NV_robustness_video_memory_purge extensions. */ typedef enum _CoglGraphicsResetStatus { COGL_GRAPHICS_RESET_STATUS_NO_ERROR, COGL_GRAPHICS_RESET_STATUS_GUILTY_CONTEXT_RESET, COGL_GRAPHICS_RESET_STATUS_INNOCENT_CONTEXT_RESET, COGL_GRAPHICS_RESET_STATUS_UNKNOWN_CONTEXT_RESET, COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET, } CoglGraphicsResetStatus; /** * cogl_get_graphics_reset_status: * @context: a #CoglContext pointer * * Returns the graphics reset status as reported by * GetGraphicsResetStatusARB defined in the ARB_robustness extension. * * Note that Cogl doesn't normally enable the ARB_robustness * extension in which case this will only ever return * #COGL_GRAPHICS_RESET_STATUS_NO_ERROR. * * Applications must explicitly use a backend specific method to * request that errors get reported such as X11's * cogl_xlib_renderer_request_reset_on_video_memory_purge(). * * Return value: a #CoglGraphicsResetStatus */ COGL_EXPORT CoglGraphicsResetStatus cogl_get_graphics_reset_status (CoglContext *context); G_END_DECLS #endif /* __COGL_CONTEXT_H__ */ muffin-6.4.1/cogl/cogl/cogl-swap-chain.h0000664000175000017500000000400714723361714016740 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_SWAP_CHAIN_H__ #define __COGL_SWAP_CHAIN_H__ #include #include G_BEGIN_DECLS typedef struct _CoglSwapChain CoglSwapChain; /** * cogl_swap_chain_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_swap_chain_get_gtype (void); COGL_EXPORT CoglSwapChain * cogl_swap_chain_new (void); COGL_EXPORT void cogl_swap_chain_set_has_alpha (CoglSwapChain *swap_chain, gboolean has_alpha); COGL_EXPORT void cogl_swap_chain_set_length (CoglSwapChain *swap_chain, int length); COGL_EXPORT gboolean cogl_is_swap_chain (void *object); G_END_DECLS #endif /* __COGL_SWAP_CHAIN_H__ */ muffin-6.4.1/cogl/cogl/cogl-bitmask.c0000664000175000017500000003223014723361714016332 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include #include #include "cogl-bitmask.h" #include "cogl-util.h" #include "cogl-flags.h" /* This code assumes that we can cast an unsigned long to a pointer and back without losing any data */ _COGL_STATIC_ASSERT (sizeof (unsigned long) <= sizeof (void *), "This toolchain breaks Cogl's assumption that it can " "safely cast an unsigned long to a pointer without " "loosing data"); #define ARRAY_INDEX(bit_num) \ ((bit_num) / (sizeof (unsigned long) * 8)) #define BIT_INDEX(bit_num) \ ((bit_num) & (sizeof (unsigned long) * 8 - 1)) #define BIT_MASK(bit_num) \ (1UL << BIT_INDEX (bit_num)) gboolean _cogl_bitmask_get_from_array (const CoglBitmask *bitmask, unsigned int bit_num) { GArray *array = (GArray *) *bitmask; /* If the index is off the end of the array then assume the bit is not set */ if (bit_num >= sizeof (unsigned long) * 8 * array->len) return FALSE; else return !!(g_array_index (array, unsigned long, ARRAY_INDEX (bit_num)) & BIT_MASK (bit_num)); } static void _cogl_bitmask_convert_to_array (CoglBitmask *bitmask) { GArray *array; /* Fetch the old values */ unsigned long old_values = _cogl_bitmask_to_bits (bitmask); array = g_array_new (FALSE, /* not zero-terminated */ TRUE, /* do clear new entries */ sizeof (unsigned long)); /* Copy the old values back in */ g_array_append_val (array, old_values); *bitmask = (struct _CoglBitmaskImaginaryType *) array; } void _cogl_bitmask_set_in_array (CoglBitmask *bitmask, unsigned int bit_num, gboolean value) { GArray *array; unsigned int array_index; unsigned long new_value_mask; /* If the bitmask is not already an array then we need to allocate one */ if (!_cogl_bitmask_has_array (bitmask)) _cogl_bitmask_convert_to_array (bitmask); array = (GArray *) *bitmask; array_index = ARRAY_INDEX (bit_num); /* Grow the array if necessary. This will clear the new data */ if (array_index >= array->len) g_array_set_size (array, array_index + 1); new_value_mask = BIT_MASK (bit_num); if (value) g_array_index (array, unsigned long, array_index) |= new_value_mask; else g_array_index (array, unsigned long, array_index) &= ~new_value_mask; } void _cogl_bitmask_set_bits (CoglBitmask *dst, const CoglBitmask *src) { if (_cogl_bitmask_has_array (src)) { GArray *src_array, *dst_array; int i; if (!_cogl_bitmask_has_array (dst)) _cogl_bitmask_convert_to_array (dst); dst_array = (GArray *) *dst; src_array = (GArray *) *src; if (dst_array->len < src_array->len) g_array_set_size (dst_array, src_array->len); for (i = 0; i < src_array->len; i++) g_array_index (dst_array, unsigned long, i) |= g_array_index (src_array, unsigned long, i); } else if (_cogl_bitmask_has_array (dst)) { GArray *dst_array; dst_array = (GArray *) *dst; g_array_index (dst_array, unsigned long, 0) |= _cogl_bitmask_to_bits (src); } else *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) | _cogl_bitmask_to_bits (src)); } void _cogl_bitmask_set_range_in_array (CoglBitmask *bitmask, unsigned int n_bits, gboolean value) { GArray *array; unsigned int array_index, bit_index; if (n_bits == 0) return; /* If the bitmask is not already an array then we need to allocate one */ if (!_cogl_bitmask_has_array (bitmask)) _cogl_bitmask_convert_to_array (bitmask); array = (GArray *) *bitmask; /* Get the array index of the top most value that will be touched */ array_index = ARRAY_INDEX (n_bits - 1); /* Get the bit index of the top most value */ bit_index = BIT_INDEX (n_bits - 1); /* Grow the array if necessary. This will clear the new data */ if (array_index >= array->len) g_array_set_size (array, array_index + 1); if (value) { /* Set the bits that are touching this index */ g_array_index (array, unsigned long, array_index) |= ~0UL >> (sizeof (unsigned long) * 8 - 1 - bit_index); /* Set all of the bits in any lesser indices */ memset (array->data, 0xff, sizeof (unsigned long) * array_index); } else { /* Clear the bits that are touching this index */ g_array_index (array, unsigned long, array_index) &= ~1UL << bit_index; /* Clear all of the bits in any lesser indices */ memset (array->data, 0x00, sizeof (unsigned long) * array_index); } } void _cogl_bitmask_xor_bits (CoglBitmask *dst, const CoglBitmask *src) { if (_cogl_bitmask_has_array (src)) { GArray *src_array, *dst_array; int i; if (!_cogl_bitmask_has_array (dst)) _cogl_bitmask_convert_to_array (dst); dst_array = (GArray *) *dst; src_array = (GArray *) *src; if (dst_array->len < src_array->len) g_array_set_size (dst_array, src_array->len); for (i = 0; i < src_array->len; i++) g_array_index (dst_array, unsigned long, i) ^= g_array_index (src_array, unsigned long, i); } else if (_cogl_bitmask_has_array (dst)) { GArray *dst_array; dst_array = (GArray *) *dst; g_array_index (dst_array, unsigned long, 0) ^= _cogl_bitmask_to_bits (src); } else *dst = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (dst) ^ _cogl_bitmask_to_bits (src)); } void _cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask) { GArray *array = (GArray *) *bitmask; memset (array->data, 0, sizeof (unsigned long) * array->len); } void _cogl_bitmask_foreach (const CoglBitmask *bitmask, CoglBitmaskForeachFunc func, void *user_data) { if (_cogl_bitmask_has_array (bitmask)) { GArray *array = (GArray *) *bitmask; const unsigned long *values = &g_array_index (array, unsigned long, 0); int bit_num; COGL_FLAGS_FOREACH_START (values, array->len, bit_num) { if (!func (bit_num, user_data)) return; } COGL_FLAGS_FOREACH_END; } else { unsigned long mask = _cogl_bitmask_to_bits (bitmask); int bit_num; COGL_FLAGS_FOREACH_START (&mask, 1, bit_num) { if (!func (bit_num, user_data)) return; } COGL_FLAGS_FOREACH_END; } } void _cogl_bitmask_set_flags_array (const CoglBitmask *bitmask, unsigned long *flags) { const GArray *array = (const GArray *) *bitmask; int i; for (i = 0; i < array->len; i++) flags[i] |= g_array_index (array, unsigned long, i); } int _cogl_bitmask_popcount_in_array (const CoglBitmask *bitmask) { const GArray *array = (const GArray *) *bitmask; int pop = 0; int i; for (i = 0; i < array->len; i++) pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i)); return pop; } int _cogl_bitmask_popcount_upto_in_array (const CoglBitmask *bitmask, int upto) { const GArray *array = (const GArray *) *bitmask; if (upto >= array->len * sizeof (unsigned long) * 8) return _cogl_bitmask_popcount_in_array (bitmask); else { unsigned long top_mask; int array_index = ARRAY_INDEX (upto); int bit_index = BIT_INDEX (upto); int pop = 0; int i; for (i = 0; i < array_index; i++) pop += _cogl_util_popcountl (g_array_index (array, unsigned long, i)); top_mask = g_array_index (array, unsigned long, array_index); return pop + _cogl_util_popcountl (top_mask & ((1UL << bit_index) - 1)); } } typedef struct { int n_bits; int *bits; } CheckData; static gboolean check_bit (int bit_num, void *user_data) { CheckData *data = user_data; int i; for (i = 0; i < data->n_bits; i++) if (data->bits[i] == bit_num) { data->bits[i] = -1; return TRUE; } g_assert_not_reached (); return TRUE; } static void verify_bits (const CoglBitmask *bitmask, ...) { CheckData data; va_list ap, ap_copy; int i; va_start (ap, bitmask); G_VA_COPY (ap_copy, ap); for (data.n_bits = 0; va_arg (ap, int) != -1; data.n_bits++); data.bits = alloca (data.n_bits * (sizeof (int))); G_VA_COPY (ap, ap_copy); for (i = 0; i < data.n_bits; i++) data.bits[i] = va_arg (ap, int); _cogl_bitmask_foreach (bitmask, check_bit, &data); for (i = 0; i < data.n_bits; i++) g_assert_cmpint (data.bits[i], ==, -1); g_assert_cmpint (_cogl_bitmask_popcount (bitmask), ==, data.n_bits); for (i = 0; i < 1024; i++) { int upto_popcount = 0; int j; G_VA_COPY (ap, ap_copy); for (j = 0; j < data.n_bits; j++) if (va_arg (ap, int) < i) upto_popcount++; g_assert_cmpint (_cogl_bitmask_popcount_upto (bitmask, i), ==, upto_popcount); G_VA_COPY (ap, ap_copy); for (j = 0; j < data.n_bits; j++) if (va_arg (ap, int) == i) break; g_assert_cmpint (_cogl_bitmask_get (bitmask, i), ==, (j < data.n_bits)); } } UNIT_TEST (check_bitmask_api, 0 /* no requirements */, 0 /* no failure cases */) { CoglBitmask bitmask; CoglBitmask other_bitmask; /* A dummy bit to make it use arrays sometimes */ int dummy_bit; int i; for (dummy_bit = -1; dummy_bit < 256; dummy_bit += 40) { _cogl_bitmask_init (&bitmask); _cogl_bitmask_init (&other_bitmask); if (dummy_bit != -1) _cogl_bitmask_set (&bitmask, dummy_bit, TRUE); verify_bits (&bitmask, dummy_bit, -1); _cogl_bitmask_set (&bitmask, 1, TRUE); _cogl_bitmask_set (&bitmask, 4, TRUE); _cogl_bitmask_set (&bitmask, 5, TRUE); verify_bits (&bitmask, 1, 4, 5, dummy_bit, -1); _cogl_bitmask_set (&bitmask, 4, FALSE); verify_bits (&bitmask, 1, 5, dummy_bit, -1); _cogl_bitmask_clear_all (&bitmask); verify_bits (&bitmask, -1); if (dummy_bit != -1) _cogl_bitmask_set (&bitmask, dummy_bit, TRUE); verify_bits (&bitmask, dummy_bit, -1); _cogl_bitmask_set (&bitmask, 1, TRUE); _cogl_bitmask_set (&bitmask, 4, TRUE); _cogl_bitmask_set (&bitmask, 5, TRUE); _cogl_bitmask_set (&other_bitmask, 5, TRUE); _cogl_bitmask_set (&other_bitmask, 6, TRUE); _cogl_bitmask_set_bits (&bitmask, &other_bitmask); verify_bits (&bitmask, 1, 4, 5, 6, dummy_bit, -1); verify_bits (&other_bitmask, 5, 6, -1); _cogl_bitmask_set (&bitmask, 6, FALSE); verify_bits (&bitmask, 1, 4, 5, dummy_bit, -1); _cogl_bitmask_xor_bits (&bitmask, &other_bitmask); verify_bits (&bitmask, 1, 4, 6, dummy_bit, -1); verify_bits (&other_bitmask, 5, 6, -1); _cogl_bitmask_set_range (&bitmask, 5, TRUE); verify_bits (&bitmask, 0, 1, 2, 3, 4, 6, dummy_bit, -1); _cogl_bitmask_set_range (&bitmask, 4, FALSE); verify_bits (&bitmask, 4, 6, dummy_bit, -1); _cogl_bitmask_destroy (&other_bitmask); _cogl_bitmask_destroy (&bitmask); } /* Extra tests for really long bitmasks */ _cogl_bitmask_init (&bitmask); _cogl_bitmask_set_range (&bitmask, 400, TRUE); _cogl_bitmask_init (&other_bitmask); _cogl_bitmask_set (&other_bitmask, 5, TRUE); _cogl_bitmask_xor_bits (&bitmask, &other_bitmask); for (i = 0; i < 1024; i++) g_assert_cmpint (_cogl_bitmask_get (&bitmask, i), ==, (i == 5 ? FALSE : i < 400 ? TRUE : FALSE)); _cogl_bitmask_set_range (&other_bitmask, 500, TRUE); _cogl_bitmask_set_bits (&bitmask, &other_bitmask); for (i = 0; i < 1024; i++) g_assert_cmpint (_cogl_bitmask_get (&bitmask, i), ==, (i < 500)); } muffin-6.4.1/cogl/cogl/cogl-renderer-private.h0000664000175000017500000000633514723361714020172 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_RENDERER_PRIVATE_H #define __COGL_RENDERER_PRIVATE_H #include #include "cogl-object-private.h" #include "cogl-driver.h" #include "cogl-texture-driver.h" #include "cogl-context.h" #include "cogl-closure-list-private.h" #include "winsys/cogl-winsys-private.h" typedef const CoglWinsysVtable *(*CoglCustomWinsysVtableGetter) (CoglRenderer *renderer); struct _CoglRenderer { CoglObject _parent; gboolean connected; CoglDriver driver_override; const CoglDriverVtable *driver_vtable; const CoglTextureDriver *texture_driver; const CoglWinsysVtable *winsys_vtable; void *custom_winsys_user_data; CoglCustomWinsysVtableGetter custom_winsys_vtable_getter; CoglWinsysID winsys_id_override; GList *constraints; GArray *poll_fds; int poll_fds_age; GList *poll_sources; CoglList idle_closures; GList *outputs; #ifdef COGL_HAS_XLIB_SUPPORT Display *foreign_xdpy; gboolean xlib_enable_event_retrieval; gboolean xlib_want_reset_on_video_memory_purge; #endif CoglDriver driver; unsigned long private_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)]; GModule *libgl_module; /* List of callback functions that will be given every native event */ GSList *event_filters; void *winsys; }; typedef CoglFilterReturn (* CoglNativeFilterFunc) (void *native_event, void *data); CoglFilterReturn _cogl_renderer_handle_native_event (CoglRenderer *renderer, void *event); void _cogl_renderer_add_native_filter (CoglRenderer *renderer, CoglNativeFilterFunc func, void *data); void _cogl_renderer_remove_native_filter (CoglRenderer *renderer, CoglNativeFilterFunc func, void *data); void * _cogl_renderer_get_proc_address (CoglRenderer *renderer, const char *name, gboolean in_core); #endif /* __COGL_RENDERER_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-clip-stack.h0000664000175000017500000001671314723361714016747 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_CLIP_STACK_H #define __COGL_CLIP_STACK_H #include "cogl-matrix.h" #include "cogl-primitive.h" #include "cogl-framebuffer.h" #include "cogl-matrix-stack.h" /* The clip stack works like a GSList where only a pointer to the top of the stack is stored. The empty clip stack is represented simply by the NULL pointer. When an entry is added to or removed from the stack the new top of the stack is returned. When an entry is pushed a new clip stack entry is created which effectively takes ownership of the reference on the old entry. Therefore unrefing the top entry effectively loses ownership of all entries in the stack */ typedef struct _CoglClipStack CoglClipStack; typedef struct _CoglClipStackRect CoglClipStackRect; typedef struct _CoglClipStackWindowRect CoglClipStackWindowRect; typedef struct _CoglClipStackPrimitive CoglClipStackPrimitive; typedef struct _CoglClipStackRegion CoglClipStackRegion; typedef enum { COGL_CLIP_STACK_RECT, COGL_CLIP_STACK_WINDOW_RECT, COGL_CLIP_STACK_PRIMITIVE, COGL_CLIP_STACK_REGION, } CoglClipStackType; /* A clip stack consists a list of entries. Each entry has a reference * count and a link to its parent node. The child takes a reference on * the parent and the CoglClipStack holds a reference to the top of * the stack. There are no links back from the parent to the * children. This allows stacks that have common ancestry to share the * entries. * * For example, the following sequence of operations would generate * the tree below: * * CoglClipStack *stack_a = NULL; * stack_a = _cogl_clip_stack_push_rectangle (stack_a, ...); * stack_a = _cogl_clip_stack_push_rectangle (stack_a, ...); * stack_a = _cogl_clip_stack_push_primitive (stack_a, ...); * CoglClipStack *stack_b = NULL; * stack_b = cogl_clip_stack_push_window_rectangle (stack_b, ...); * * stack_a * \ holds a ref to * +-----------+ * | prim node | * |ref count 1| * +-----------+ * \ * +-----------+ +-----------+ * both tops hold | rect node | | rect node | * a ref to the |ref count 2|--|ref count 1| * same rect node +-----------+ +-----------+ * / * +-----------+ * | win. rect | * |ref count 1| * +-----------+ * / holds a ref to * stack_b * */ struct _CoglClipStack { /* This will be null if there is no parent. If it is not null then this node must be holding a reference to the parent */ CoglClipStack *parent; CoglClipStackType type; /* All clip entries have a window-space bounding box which we can use to calculate a scissor. The scissor limits the clip so that we don't need to do a full stencil clear if the stencil buffer is needed. This is stored in Cogl's coordinate space (ie, 0,0 is the top left) */ int bounds_x0; int bounds_y0; int bounds_x1; int bounds_y1; unsigned int ref_count; }; struct _CoglClipStackRect { CoglClipStack _parent_data; /* The rectangle for this clip */ float x0; float y0; float x1; float y1; /* The matrix that was current when the clip was set */ CoglMatrixEntry *matrix_entry; /* If this is true then the clip for this rectangle is entirely described by the scissor bounds. This implies that the rectangle is screen aligned and we don't need to use the stencil buffer to set the clip. We keep the entry as a rect entry rather than a window rect entry so that it will be easier to detect if the modelview matrix is that same as when a rectangle is added to the journal. In that case we can use the original clip coordinates and modify the rectangle instead. */ gboolean can_be_scissor; }; struct _CoglClipStackWindowRect { CoglClipStack _parent_data; /* The window rect clip doesn't need any specific data because it just adds to the scissor clip */ }; struct _CoglClipStackPrimitive { CoglClipStack _parent_data; /* The matrix that was current when the clip was set */ CoglMatrixEntry *matrix_entry; CoglPrimitive *primitive; float bounds_x1; float bounds_y1; float bounds_x2; float bounds_y2; }; struct _CoglClipStackRegion { CoglClipStack _parent_data; cairo_region_t *region; }; CoglClipStack * _cogl_clip_stack_push_window_rectangle (CoglClipStack *stack, int x_offset, int y_offset, int width, int height); COGL_EXPORT CoglClipStack * _cogl_clip_stack_push_rectangle (CoglClipStack *stack, float x_1, float y_1, float x_2, float y_2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport); COGL_EXPORT CoglClipStack * _cogl_clip_stack_push_primitive (CoglClipStack *stack, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport); CoglClipStack * cogl_clip_stack_push_region (CoglClipStack *stack, cairo_region_t *region); CoglClipStack * _cogl_clip_stack_pop (CoglClipStack *stack); void _cogl_clip_stack_get_bounds (CoglClipStack *stack, int *scissor_x0, int *scissor_y0, int *scissor_x1, int *scissor_y1); void _cogl_clip_stack_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer); CoglClipStack * _cogl_clip_stack_ref (CoglClipStack *stack); void _cogl_clip_stack_unref (CoglClipStack *stack); #endif /* __COGL_CLIP_STACK_H */ muffin-6.4.1/cogl/cogl/cogl-snippet-private.h0000664000175000017500000000475614723361714020053 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #ifndef __COGL_SNIPPET_PRIVATE_H #define __COGL_SNIPPET_PRIVATE_H #include #include "cogl-snippet.h" #include "cogl-object-private.h" /* These values are also used in the enum for CoglSnippetHook. They are copied here because we don't really want these names to be part of the public API */ #define COGL_SNIPPET_HOOK_BAND_SIZE 2048 #define COGL_SNIPPET_FIRST_PIPELINE_HOOK 0 #define COGL_SNIPPET_FIRST_PIPELINE_VERTEX_HOOK \ COGL_SNIPPET_FIRST_PIPELINE_HOOK #define COGL_SNIPPET_FIRST_PIPELINE_FRAGMENT_HOOK \ (COGL_SNIPPET_FIRST_PIPELINE_VERTEX_HOOK + COGL_SNIPPET_HOOK_BAND_SIZE) #define COGL_SNIPPET_FIRST_LAYER_HOOK (COGL_SNIPPET_HOOK_BAND_SIZE * 2) #define COGL_SNIPPET_FIRST_LAYER_VERTEX_HOOK COGL_SNIPPET_FIRST_LAYER_HOOK #define COGL_SNIPPET_FIRST_LAYER_FRAGMENT_HOOK \ (COGL_SNIPPET_FIRST_LAYER_VERTEX_HOOK + COGL_SNIPPET_HOOK_BAND_SIZE) struct _CoglSnippet { CoglObject _parent; CoglSnippetHook hook; /* This is set to TRUE the first time the snippet is attached to the pipeline. After that any attempts to modify the snippet will be ignored. */ gboolean immutable; char *declarations; char *pre; char *replace; char *post; }; void _cogl_snippet_make_immutable (CoglSnippet *snippet); #endif /* __COGL_SNIPPET_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-object-private.h0000664000175000017500000003436014723361714017631 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #ifndef __COGL_OBJECT_PRIVATE_H #define __COGL_OBJECT_PRIVATE_H #include #include "cogl-types.h" #include "cogl-object.h" #include "cogl-debug.h" /* For compatability until all components have been converted */ typedef struct _CoglObjectClass CoglHandleClass; typedef struct _CoglObject CoglHandleObject; /* XXX: sadly we didn't fully consider when we copied the cairo API * for _set_user_data that the callback doesn't get a pointer to the * instance which is desired in most cases. This means you tend to end * up creating micro allocations for the private data just so you can * pair up the data of interest with the original instance for * identification when it is later destroyed. * * Internally we use a small hack to avoid needing these micro * allocations by actually passing the instance as a second argument * to the callback */ typedef void (*CoglUserDataDestroyInternalCallback) (void *user_data, void *instance); typedef struct _CoglObjectClass { GTypeClass base_class; const char *name; void *virt_free; void *virt_unref; } CoglObjectClass; #define COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES 2 typedef struct { CoglUserDataKey *key; void *user_data; CoglUserDataDestroyInternalCallback destroy; } CoglUserDataEntry; /* All Cogl objects inherit from this base object by adding a member: * * CoglObject _parent; * * at the top of its main structure. This structure is initialized * when you call _cogl_#type_name#_object_new (new_object); */ struct _CoglObject { CoglObjectClass *klass; /* equivalent to GTypeInstance */ CoglUserDataEntry user_data_entry[ COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES]; GArray *user_data_array; int n_user_data_entries; unsigned int ref_count; }; /* Helper macro to encapsulate the common code for COGL reference counted objects */ #ifdef COGL_OBJECT_DEBUG #define _COGL_OBJECT_DEBUG_NEW(type_name, obj) \ COGL_NOTE (OBJECT, "COGL " G_STRINGIFY (type_name) " NEW %p %i", \ (obj), (obj)->ref_count) #define _COGL_OBJECT_DEBUG_REF(type_name, object) G_STMT_START { \ CoglObject *__obj = (CoglObject *)object; \ COGL_NOTE (OBJECT, "COGL %s REF %p %i", \ (__obj)->klass->name, \ (__obj), (__obj)->ref_count); } G_STMT_END #define _COGL_OBJECT_DEBUG_UNREF(type_name, object) G_STMT_START { \ CoglObject *__obj = (CoglObject *)object; \ COGL_NOTE (OBJECT, "COGL %s UNREF %p %i", \ (__obj)->klass->name, \ (__obj), (__obj)->ref_count - 1); } G_STMT_END #define COGL_OBJECT_DEBUG_FREE(obj) \ COGL_NOTE (OBJECT, "COGL %s FREE %p", \ (obj)->klass->name, (obj)) #else /* !COGL_OBJECT_DEBUG */ #define _COGL_OBJECT_DEBUG_NEW(type_name, obj) #define _COGL_OBJECT_DEBUG_REF(type_name, obj) #define _COGL_OBJECT_DEBUG_UNREF(type_name, obj) #define COGL_OBJECT_DEBUG_FREE(obj) #endif /* COGL_OBJECT_DEBUG */ #define _COGL_GTYPE_INIT_CLASS(type_name) do { \ _cogl_##type_name##_class.base_class.g_type = cogl_##type_name##_get_gtype (); \ } while (0) #define COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ CoglObjectClass _cogl_##type_name##_class; \ static unsigned long _cogl_object_##type_name##_count; \ \ static inline void \ _cogl_object_##type_name##_inc (void) \ { \ _cogl_object_##type_name##_count++; \ } \ \ static inline void \ _cogl_object_##type_name##_dec (void) \ { \ _cogl_object_##type_name##_count--; \ } \ \ static void \ _cogl_object_##type_name##_indirect_free (CoglObject *obj) \ { \ _cogl_##type_name##_free ((Cogl##TypeName *) obj); \ _cogl_object_##type_name##_dec (); \ } \ \ static void \ _cogl_object_##type_name##_class_init (void) \ { \ _cogl_object_##type_name##_count = 0; \ \ if (_cogl_debug_instances == NULL) \ _cogl_debug_instances = \ g_hash_table_new (g_str_hash, g_str_equal); \ \ _cogl_##type_name##_class.virt_free = \ _cogl_object_##type_name##_indirect_free; \ _cogl_##type_name##_class.virt_unref = \ _cogl_object_default_unref; \ _cogl_##type_name##_class.name = "Cogl"#TypeName; \ \ g_hash_table_insert (_cogl_debug_instances, \ (void *) _cogl_##type_name##_class.name, \ &_cogl_object_##type_name##_count); \ \ { code; } \ } \ \ static Cogl##TypeName * \ _cogl_##type_name##_object_new (Cogl##TypeName *new_obj) \ { \ CoglObject *obj = (CoglObject *)&new_obj->_parent; \ obj->ref_count = 0; \ cogl_object_ref (obj); \ obj->n_user_data_entries = 0; \ obj->user_data_array = NULL; \ \ obj->klass = &_cogl_##type_name##_class; \ if (!obj->klass->virt_free) \ { \ _cogl_object_##type_name##_class_init (); \ } \ \ _cogl_object_##type_name##_inc (); \ _COGL_OBJECT_DEBUG_NEW (TypeName, obj); \ return new_obj; \ } #define COGL_OBJECT_DEFINE_WITH_CODE_GTYPE(TypeName, type_name, code) \ \ COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, \ type_name, \ do { code; } while (0); \ _COGL_GTYPE_INIT_CLASS (type_name)) \ \ gboolean \ cogl_is_##type_name (void *object) \ { \ CoglObject *obj = object; \ \ if (object == NULL) \ return FALSE; \ \ return obj->klass == &_cogl_##type_name##_class; \ } #define COGL_OBJECT_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ gboolean \ cogl_is_##type_name (void *object) \ { \ CoglObject *obj = object; \ \ if (object == NULL) \ return FALSE; \ \ return obj->klass == &_cogl_##type_name##_class; \ } #define COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ COGL_OBJECT_COMMON_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ gboolean \ _cogl_is_##type_name (void *object) \ { \ CoglObject *obj = object; \ \ if (object == NULL) \ return FALSE; \ \ return obj->klass == &_cogl_##type_name##_class; \ } #define COGL_OBJECT_DEFINE(TypeName, type_name) \ COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (TypeName, type_name, (void) 0) #define COGL_OBJECT_INTERNAL_DEFINE(TypeName, type_name) \ COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE (TypeName, type_name, (void) 0) /* For temporary compatability */ #define COGL_HANDLE_INTERNAL_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE (TypeName, type_name, code) \ \ static Cogl##TypeName * \ _cogl_##type_name##_handle_new (CoglHandle handle) \ { \ return _cogl_##type_name##_object_new (handle); \ } #define COGL_HANDLE_DEFINE_WITH_CODE(TypeName, type_name, code) \ \ COGL_OBJECT_DEFINE_WITH_CODE (TypeName, type_name, code) \ \ static Cogl##TypeName * \ _cogl_##type_name##_handle_new (CoglHandle handle) \ { \ return _cogl_##type_name##_object_new (handle); \ } #define COGL_HANDLE_DEFINE(TypeName, type_name) \ COGL_HANDLE_DEFINE_WITH_CODE (TypeName, type_name, (void) 0) void _cogl_object_set_user_data (CoglObject *object, CoglUserDataKey *key, void *user_data, CoglUserDataDestroyInternalCallback destroy); COGL_EXPORT void _cogl_object_default_unref (void *obj); #endif /* __COGL_OBJECT_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-atlas-texture-private.h0000664000175000017500000000570414723361714021165 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef _COGL_ATLAS_TEXTURE_PRIVATE_H_ #define _COGL_ATLAS_TEXTURE_PRIVATE_H_ #include "cogl-object-private.h" #include "cogl-texture-private.h" #include "cogl-rectangle-map.h" #include "cogl-atlas.h" #include "cogl-atlas-texture.h" struct _CoglAtlasTexture { CoglTexture _parent; /* The format that the texture is in. This isn't necessarily the same format as the atlas texture because we can store pre-multiplied and non-pre-multiplied textures together */ CoglPixelFormat internal_format; /* The rectangle that was used to add this texture to the atlas. This includes the 1-pixel border */ CoglRectangleMapEntry rectangle; /* The atlas that this texture is in. If the texture is no longer in an atlas then this will be NULL. A reference is taken on the atlas by the texture (but not vice versa so there is no cycle) */ CoglAtlas *atlas; /* Either a CoglSubTexture representing the atlas region for easy * rendering or if the texture has been migrated out of the atlas it * may be some other texture type such as CoglTexture2D */ CoglTexture *sub_texture; }; CoglAtlasTexture * _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, gboolean can_convert_in_place); COGL_EXPORT void _cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, GHookFunc callback, void *user_data); COGL_EXPORT void _cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, GHookFunc callback, void *user_data); gboolean _cogl_is_atlas_texture (void *object); #endif /* _COGL_ATLAS_TEXTURE_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/cogl-snippet.h0000664000175000017500000007606614723361714016406 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_SNIPPET_H__ #define __COGL_SNIPPET_H__ G_BEGIN_DECLS /** * SECTION:cogl-snippet * @short_description: Functions for creating and manipulating shader snippets * * #CoglSnippets are used to modify or replace parts of a * #CoglPipeline using GLSL. GLSL is a programming language supported * by OpenGL on programmable hardware to provide a more flexible * description of what should be rendered. A description of GLSL * itself is outside the scope of this documentation but any good * OpenGL book should help to describe it. * * Unlike in OpenGL, when using GLSL with Cogl it is possible to write * short snippets to replace small sections of the pipeline instead of * having to replace the whole of either the vertex or fragment * pipelines. Of course it is also possible to replace the whole of * the pipeline if needed. * * Each snippet is a standalone chunk of code which would attach to * the pipeline at a particular point. The code is split into four * separate strings (all of which are optional): * * * * declarations * * The code in this string will be inserted outside of any function in * the global scope of the shader. This can be used to declare * uniforms, attributes, varyings and functions to be used by the * snippet. * * * * pre * * The code in this string will be inserted before the hook point. * * * * post * * The code in this string will be inserted after the hook point. This * can be used to modify the results of the builtin generated code for * that hook point. * * * * replace * * If present the code in this string will replace the generated code * for the hook point. * * * * * All of the strings apart from the declarations string of a pipeline * are generated in a single function so they can share variables * declared from one string in another. The scope of the code is * limited to each snippet so local variables declared in the snippet * will not collide with variables declared in another * snippet. However, code in the 'declarations' string is global to * the shader so it is the application's responsibility to ensure that * variables declared here will not collide with those from other * snippets. * * The snippets can be added to a pipeline with * cogl_pipeline_add_snippet() or * cogl_pipeline_add_layer_snippet(). Which function to use depends on * which hook the snippet is targetting. The snippets are all * generated in the order they are added to the pipeline. That is, the * post strings are executed in the order they are added to the * pipeline and the pre strings are executed in reverse order. If any * replace strings are given for a snippet then any other snippets * with the same hook added before that snippet will be ignored. The * different hooks are documented under #CoglSnippetHook. * * For portability with GLES2, it is recommended not to use the GLSL * builtin names such as gl_FragColor. Instead there are replacement * names under the cogl_* namespace which can be used instead. These * are: * * * * uniform mat4 * cogl_modelview_matrix * * The current modelview matrix. This is equivalent to * #gl_ModelViewMatrix. * * * * uniform mat4 * cogl_projection_matrix * * The current projection matrix. This is equivalent to * #gl_ProjectionMatrix. * * * * uniform mat4 * cogl_modelview_projection_matrix * * The combined modelview and projection matrix. A vertex shader * would typically use this to transform the incoming vertex * position. The separate modelview and projection matrices are * usually only needed for lighting calculations. This is * equivalent to #gl_ModelViewProjectionMatrix. * * * * uniform mat4 * cogl_texture_matrix[] * * An array of matrices for transforming the texture * coordinates. This is equivalent to #gl_TextureMatrix. * * * * * In a vertex shader, the following are also available: * * * * attribute vec4 * cogl_position_in * * The incoming vertex position. This is equivalent to #gl_Vertex. * * * * attribute vec4 * cogl_color_in * * The incoming vertex color. This is equivalent to #gl_Color. * * * * attribute vec4 * cogl_tex_coord_in * * The texture coordinate for layer 0. This is an alternative name * for #cogl_tex_coord0_in. * * * * attribute vec4 * cogl_tex_coord0_in * * The texture coordinate for the layer 0. This is equivalent to * #gl_MultiTexCoord0. There will also be #cogl_tex_coord1_in and * so on if more layers are added to the pipeline. * * * * attribute vec3 * cogl_normal_in * * The normal of the vertex. This is equivalent to #gl_Normal. * * * * vec4 * cogl_position_out * * The calculated position of the vertex. This must be written to * in all vertex shaders. This is equivalent to #gl_Position. * * * * float * cogl_point_size_in * * The incoming point size from the cogl_point_size_in attribute. * This is only available if * cogl_pipeline_set_per_vertex_point_size() is set on the * pipeline. * * * * float * cogl_point_size_out * * The calculated size of a point. This is equivalent to #gl_PointSize. * * * * varying vec4 * cogl_color_out * * The calculated color of a vertex. This is equivalent to #gl_FrontColor. * * * * varying vec4 * cogl_tex_coord0_out * * The calculated texture coordinate for layer 0 of the pipeline. * This is equivalent to #gl_TexCoord[0]. There will also be * #cogl_tex_coord1_out and so on if more layers are added to the * pipeline. In the fragment shader, this varying is called * #cogl_tex_coord0_in. * * * * * In a fragment shader, the following are also available: * * * * varying vec4 cogl_color_in * * The calculated color of a vertex. This is equivalent to #gl_FrontColor. * * * * varying vec4 * cogl_tex_coord0_in * * The texture coordinate for layer 0. This is equivalent to * #gl_TexCoord[0]. There will also be #cogl_tex_coord1_in and so * on if more layers are added to the pipeline. * * * * vec4 cogl_color_out * * The final calculated color of the fragment. All fragment shaders * must write to this variable. This is equivalent to * #gl_FrontColor. * * * * float cogl_depth_out * * An optional output variable specifying the depth value to use * for this fragment. This is equivalent to #gl_FragDepth. * * * * bool cogl_front_facing * * A readonly variable that will be true if the current primitive * is front facing. This can be used to implement two-sided * coloring algorithms. This is equivalent to #gl_FrontFacing. * * * * vec2 cogl_point_coord * * When rendering points, this will contain a vec2 which represents * the position within the point of the current fragment. * vec2(0.0,0.0) will be the topleft of the point and vec2(1.0,1.0) * will be the bottom right. Note that there is currently a bug in * Cogl where when rendering to an offscreen buffer these * coordinates will be upside-down. The value is undefined when not * rendering points. * * * * * Here is an example of using a snippet to add a desaturate effect to the * generated color on a pipeline. * * * CoglPipeline *pipeline = cogl_pipeline_new (); * * /* Set up the pipeline here, ie by adding a texture or other * layers */ * * /* Create the snippet. The first string is the declarations which * we will use to add a uniform. The second is the 'post' string which * will contain the code to perform the desaturation. */ * CoglSnippet *snippet = * cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, * "uniform float factor;", * "float gray = dot (vec3 (0.299, 0.587, 0.114), " * " cogl_color_out.rgb);" * "cogl_color_out.rgb = mix (vec3 (gray)," * " cogl_color_out.rgb," * " factor);"); * * /* Add it to the pipeline */ * cogl_pipeline_add_snippet (pipeline, snippet); * /* The pipeline keeps a reference to the snippet * so we don't need to */ * cogl_object_unref (snippet); * * /* Update the custom uniform on the pipeline */ * int location = cogl_pipeline_get_uniform_location (pipeline, "factor"); * cogl_pipeline_set_uniform_1f (pipeline, location, 0.5f); * * /* Now we can render with the snippet as usual */ * cogl_push_source (pipeline); * cogl_rectangle (0, 0, 10, 10); * cogl_pop_source (); * */ typedef struct _CoglSnippet CoglSnippet; #define COGL_SNIPPET(OBJECT) ((CoglSnippet *)OBJECT) /** * cogl_snippet_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_snippet_get_gtype (void); /* Enumeration of all the hook points that a snippet can be attached to within a pipeline. */ /** * CoglSnippetHook: * @COGL_SNIPPET_HOOK_VERTEX_GLOBALS: A hook for declaring global data * that can be shared with all other snippets that are on a vertex * hook. * @COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS: A hook for declaring global * data wthat can be shared with all other snippets that are on a * fragment hook. * @COGL_SNIPPET_HOOK_VERTEX: A hook for the entire vertex processing * stage of the pipeline. * @COGL_SNIPPET_HOOK_VERTEX_TRANSFORM: A hook for the vertex transformation. * @COGL_SNIPPET_HOOK_POINT_SIZE: A hook for manipulating the point * size of a vertex. This is only used if * cogl_pipeline_set_per_vertex_point_size() is enabled on the * pipeline. * @COGL_SNIPPET_HOOK_FRAGMENT: A hook for the entire fragment * processing stage of the pipeline. * @COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM: A hook for applying the * layer matrix to a texture coordinate for a layer. * @COGL_SNIPPET_HOOK_LAYER_FRAGMENT: A hook for the fragment * processing of a particular layer. * @COGL_SNIPPET_HOOK_TEXTURE_LOOKUP: A hook for the texture lookup * stage of a given layer in a pipeline. * * #CoglSnippetHook is used to specify a location within a * #CoglPipeline where the code of the snippet should be used when it * is attached to a pipeline. * * * * %COGL_SNIPPET_HOOK_VERTEX_GLOBALS * * * Adds a shader snippet at the beginning of the global section of the * shader for the vertex processing. Any declarations here can be * shared with all other snippets that are attached to a vertex hook. * Only the ‘declarations’ string is used and the other strings are * ignored. * * * * * %COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS * * * Adds a shader snippet at the beginning of the global section of the * shader for the fragment processing. Any declarations here can be * shared with all other snippets that are attached to a fragment * hook. Only the ‘declarations’ string is used and the other strings * are ignored. * * * * * %COGL_SNIPPET_HOOK_VERTEX * * * Adds a shader snippet that will hook on to the vertex processing * stage of the pipeline. This gives a chance for the application to * modify the vertex attributes generated by the shader. Typically the * snippet will modify cogl_color_out or cogl_position_out builtins. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted at the top of the * main() function before any vertex processing is done. * * * The ‘replace’ string in @snippet will be used instead of the * generated vertex processing if it is present. This can be used if * the application wants to provide a complete vertex shader and * doesn't need the generated output from Cogl. * * * The ‘post’ string in @snippet will be inserted after all of the * standard vertex processing is done. This can be used to modify the * outputs. * * * * * %COGL_SNIPPET_HOOK_VERTEX_TRANSFORM * * * Adds a shader snippet that will hook on to the vertex transform stage. * Typically the snippet will use the cogl_modelview_matrix, * cogl_projection_matrix and cogl_modelview_projection_matrix matrices and the * cogl_position_in attribute. The hook must write to cogl_position_out. * The default processing for this hook will multiply cogl_position_in by * the combined modelview-projection matrix and store it on cogl_position_out. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted at the top of the * main() function before the vertex transform is done. * * * The ‘replace’ string in @snippet will be used instead of the * generated vertex transform if it is present. * * * The ‘post’ string in @snippet will be inserted after all of the * standard vertex transformation is done. This can be used to modify the * cogl_position_out in addition to the default processing. * * * * * %COGL_SNIPPET_HOOK_POINT_SIZE * * * Adds a shader snippet that will hook on to the point size * calculation step within the vertex shader stage. The snippet should * write to the builtin cogl_point_size_out with the new point size. * The snippet can either read cogl_point_size_in directly and write a * new value or first read an existing value in cogl_point_size_out * that would be set by a previous snippet. Note that this hook is * only used if cogl_pipeline_set_per_vertex_point_size() is enabled * on the pipeline. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted just before * calculating the point size. * * * The ‘replace’ string in @snippet will be used instead of the * generated point size calculation if it is present. * * * The ‘post’ string in @snippet will be inserted after the * standard point size calculation is done. This can be used to modify * cogl_point_size_out in addition to the default processing. * * * * * %COGL_SNIPPET_HOOK_FRAGMENT * * * Adds a shader snippet that will hook on to the fragment processing * stage of the pipeline. This gives a chance for the application to * modify the fragment color generated by the shader. Typically the * snippet will modify cogl_color_out. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted at the top of the * main() function before any fragment processing is done. * * * The ‘replace’ string in @snippet will be used instead of the * generated fragment processing if it is present. This can be used if * the application wants to provide a complete fragment shader and * doesn't need the generated output from Cogl. * * * The ‘post’ string in @snippet will be inserted after all of the * standard fragment processing is done. At this point the generated * value for the rest of the pipeline state will already be in * cogl_color_out so the application can modify the result by altering * this variable. * * * * * %COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM * * * Adds a shader snippet that will hook on to the texture coordinate * transformation of a particular layer. This can be used to replace * the processing for a layer or to modify the results. * * * Within the snippet code for this hook there are two extra * variables. The first is a mat4 called cogl_matrix which represents * the user matrix for this layer. The second is called cogl_tex_coord * and represents the incoming and outgoing texture coordinate. On * entry to the hook, cogl_tex_coord contains the value of the * corresponding texture coordinate attribute for this layer. The hook * is expected to modify this variable. The output will be passed as a * varying to the fragment processing stage. The default code will * just multiply cogl_matrix by cogl_tex_coord and store the result in * cogl_tex_coord. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted just before the * fragment processing for this layer. At this point cogl_tex_coord * still contains the value of the texture coordinate attribute. * * * If a ‘replace’ string is given then this will be used instead of * the default fragment processing for this layer. The snippet can * modify cogl_tex_coord or leave it as is to apply no transformation. * * * The ‘post’ string in @snippet will be inserted just after the * transformation. At this point cogl_tex_coord will contain the * results of the transformation but it can be further modified by the * snippet. * * * * * %COGL_SNIPPET_HOOK_LAYER_FRAGMENT * * * Adds a shader snippet that will hook on to the fragment processing * of a particular layer. This can be used to replace the processing * for a layer or to modify the results. * * * Within the snippet code for this hook there is an extra vec4 * variable called ‘cogl_layer’. This contains the resulting color * that will be used for the layer. This can be modified in the ‘post’ * section or it the default processing can be replaced entirely using * the ‘replace’ section. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted just before the * fragment processing for this layer. * * * If a ‘replace’ string is given then this will be used instead of * the default fragment processing for this layer. The snippet must write to * the ‘cogl_layer’ variable in that case. * * * The ‘post’ string in @snippet will be inserted just after the * fragment processing for the layer. The results can be modified by changing * the value of the ‘cogl_layer’ variable. * * * * * %COGL_SNIPPET_HOOK_TEXTURE_LOOKUP * * * Adds a shader snippet that will hook on to the texture lookup part * of a given layer. This gives a chance for the application to modify * the coordinates that will be used for the texture lookup or to * alter the returned texel. * * * Within the snippet code for this hook there are three extra * variables available. ‘cogl_sampler’ is a sampler object * representing the sampler for the layer where the snippet is * attached. ‘cogl_tex_coord’ is a vec4 which contains the texture * coordinates that will be used for the texture lookup. This can be * modified. ‘cogl_texel’ will contain the result of the texture * lookup. This can also be modified. * * * The ‘declarations’ string in @snippet will be inserted in the * global scope of the shader. Use this to declare any uniforms, * attributes or functions that the snippet requires. * * * The ‘pre’ string in @snippet will be inserted at the top of the * main() function before any fragment processing is done. This is a * good place to modify the cogl_tex_coord variable. * * * If a ‘replace’ string is given then this will be used instead of a * the default texture lookup. The snippet would typically use its own * sampler in this case. * * * The ‘post’ string in @snippet will be inserted after texture lookup * has been preformed. Here the snippet can modify the cogl_texel * variable to alter the returned texel. * * * * * * Since: 1.10 * Stability: Unstable */ typedef enum { /* Per pipeline vertex hooks */ COGL_SNIPPET_HOOK_VERTEX = 0, COGL_SNIPPET_HOOK_VERTEX_TRANSFORM, COGL_SNIPPET_HOOK_VERTEX_GLOBALS, COGL_SNIPPET_HOOK_POINT_SIZE, /* Per pipeline fragment hooks */ COGL_SNIPPET_HOOK_FRAGMENT = 2048, COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS, /* Per layer vertex hooks */ COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM = 4096, /* Per layer fragment hooks */ COGL_SNIPPET_HOOK_LAYER_FRAGMENT = 6144, COGL_SNIPPET_HOOK_TEXTURE_LOOKUP } CoglSnippetHook; /** * cogl_snippet_new: * @hook: The point in the pipeline that this snippet will wrap around * or replace. * @declarations: The source code for the declarations for this * snippet or %NULL. See cogl_snippet_set_declarations(). * @post: The source code to run after the hook point where this * shader snippet is attached or %NULL. See cogl_snippet_set_post(). * * Allocates and initializes a new snippet with the given source strings. * * Return value: a pointer to a new #CoglSnippet * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT CoglSnippet * cogl_snippet_new (CoglSnippetHook hook, const char *declarations, const char *post); /** * cogl_snippet_get_hook: * @snippet: A #CoglSnippet * * Return value: the hook that was set when cogl_snippet_new() was * called. * Since: 1.10 * Stability: Unstable */ COGL_EXPORT CoglSnippetHook cogl_snippet_get_hook (CoglSnippet *snippet); /** * cogl_is_snippet: * @object: A #CoglObject pointer * * Gets whether the given @object references an existing snippet object. * * Return value: %TRUE if the @object references a #CoglSnippet, * %FALSE otherwise * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_snippet (void *object); /** * cogl_snippet_set_declarations: * @snippet: A #CoglSnippet * @declarations: The new source string for the declarations section * of this snippet. * * Sets a source string that will be inserted in the global scope of * the generated shader when this snippet is used on a pipeline. This * string is typically used to declare uniforms, attributes or * functions that will be used by the other parts of the snippets. * * This function should only be called before the snippet is attached * to its first pipeline. After that the snippet should be considered * immutable. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_snippet_set_declarations (CoglSnippet *snippet, const char *declarations); /** * cogl_snippet_get_declarations: * @snippet: A #CoglSnippet * * Return value: the source string that was set with * cogl_snippet_set_declarations() or %NULL if none was set. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT const char * cogl_snippet_get_declarations (CoglSnippet *snippet); /** * cogl_snippet_set_pre: * @snippet: A #CoglSnippet * @pre: The new source string for the pre section of this snippet. * * Sets a source string that will be inserted before the hook point in * the generated shader for the pipeline that this snippet is attached * to. Please see the documentation of each hook point in * #CoglPipeline for a description of how this string should be used. * * This function should only be called before the snippet is attached * to its first pipeline. After that the snippet should be considered * immutable. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_snippet_set_pre (CoglSnippet *snippet, const char *pre); /** * cogl_snippet_get_pre: * @snippet: A #CoglSnippet * * Return value: the source string that was set with * cogl_snippet_set_pre() or %NULL if none was set. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT const char * cogl_snippet_get_pre (CoglSnippet *snippet); /** * cogl_snippet_set_replace: * @snippet: A #CoglSnippet * @replace: The new source string for the replace section of this snippet. * * Sets a source string that will be used instead of any generated * source code or any previous snippets for this hook point. Please * see the documentation of each hook point in #CoglPipeline for a * description of how this string should be used. * * This function should only be called before the snippet is attached * to its first pipeline. After that the snippet should be considered * immutable. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_snippet_set_replace (CoglSnippet *snippet, const char *replace); /** * cogl_snippet_get_replace: * @snippet: A #CoglSnippet * * Return value: the source string that was set with * cogl_snippet_set_replace() or %NULL if none was set. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT const char * cogl_snippet_get_replace (CoglSnippet *snippet); /** * cogl_snippet_set_post: * @snippet: A #CoglSnippet * @post: The new source string for the post section of this snippet. * * Sets a source string that will be inserted after the hook point in * the generated shader for the pipeline that this snippet is attached * to. Please see the documentation of each hook point in * #CoglPipeline for a description of how this string should be used. * * This function should only be called before the snippet is attached * to its first pipeline. After that the snippet should be considered * immutable. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT void cogl_snippet_set_post (CoglSnippet *snippet, const char *post); /** * cogl_snippet_get_post: * @snippet: A #CoglSnippet * * Return value: the source string that was set with * cogl_snippet_set_post() or %NULL if none was set. * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT const char * cogl_snippet_get_post (CoglSnippet *snippet); G_END_DECLS #endif /* __COGL_SNIPPET_H__ */ muffin-6.4.1/cogl/cogl/cogl1-context.h0000664000175000017500000001335014723361714016454 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_1_CONTEXT_H__ #define __COGL_1_CONTEXT_H__ #include #include #include #include G_BEGIN_DECLS /** * cogl_get_option_group: * * Retrieves the #GOptionGroup used by Cogl to parse the command * line options. Clutter uses this to handle the Cogl command line * options during its initialization process. * * Return value: a #GOptionGroup * * Since: 1.0 * Deprecated: 1.16: Not replaced */ COGL_DEPRECATED COGL_EXPORT GOptionGroup * cogl_get_option_group (void); /* Misc */ /** * cogl_get_proc_address: (skip) * @name: the name of the function. * * Gets a pointer to a given GL or GL ES extension function. This acts * as a wrapper around glXGetProcAddress() or whatever is the * appropriate function for the current backend. * * This function should not be used to query core opengl API * symbols since eglGetProcAddress for example doesn't allow this and * and may return a junk pointer if you do. * * Return value: a pointer to the requested function or %NULL if the * function is not available. */ COGL_EXPORT GCallback cogl_get_proc_address (const char *name); /** * cogl_set_depth_test_enabled: * @setting: %TRUE to enable depth testing or %FALSE to disable. * * Sets whether depth testing is enabled. If it is disabled then the * order that actors are layered on the screen depends solely on the * order specified using clutter_actor_raise() and * clutter_actor_lower(), otherwise it will also take into account the * actor's depth. Depth testing is disabled by default. * * Deprecated: 1.16: Use cogl_pipeline_set_depth_state() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_depth_state) COGL_EXPORT void cogl_set_depth_test_enabled (gboolean setting); /** * cogl_get_depth_test_enabled: * * Queries if depth testing has been enabled via cogl_set_depth_test_enable() * * Return value: %TRUE if depth testing is enabled, and %FALSE otherwise * * Deprecated: 1.16: Use cogl_pipeline_set_depth_state() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_depth_state) COGL_EXPORT gboolean cogl_get_depth_test_enabled (void); /** * cogl_set_backface_culling_enabled: * @setting: %TRUE to enable backface culling or %FALSE to disable. * * Sets whether textures positioned so that their backface is showing * should be hidden. This can be used to efficiently draw two-sided * textures or fully closed cubes without enabling depth testing. This * only affects calls to the cogl_rectangle* family of functions and * cogl_vertex_buffer_draw*. Backface culling is disabled by default. * * Deprecated: 1.16: Use cogl_pipeline_set_cull_face_mode() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_cull_face_mode) COGL_EXPORT void cogl_set_backface_culling_enabled (gboolean setting); /** * cogl_get_backface_culling_enabled: * * Queries if backface culling has been enabled via * cogl_set_backface_culling_enabled() * * Return value: %TRUE if backface culling is enabled, and %FALSE otherwise * * Deprecated: 1.16: Use cogl_pipeline_get_cull_face_mode() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_get_cull_face_mode) COGL_EXPORT gboolean cogl_get_backface_culling_enabled (void); /** * cogl_flush: * * This function should only need to be called in exceptional circumstances. * * As an optimization Cogl drawing functions may batch up primitives * internally, so if you are trying to use raw GL outside of Cogl you stand a * better chance of being successful if you ask Cogl to flush any batched * geometry before making your state changes. * * It only ensure that the underlying driver is issued all the commands * necessary to draw the batched primitives. It provides no guarantees about * when the driver will complete the rendering. * * This provides no guarantees about the GL state upon returning and to avoid * confusing Cogl you should aim to restore any changes you make before * resuming use of Cogl. * * If you are making state changes with the intention of affecting Cogl drawing * primitives you are 100% on your own since you stand a good chance of * conflicting with Cogl internals. For example clutter-gst which currently * uses direct GL calls to bind ARBfp programs will very likely break when Cogl * starts to use ARBfb programs itself for the material API. * * Since: 1.0 */ COGL_EXPORT void cogl_flush (void); G_END_DECLS #endif /* __COGL_1_CONTEXT_H__ */ muffin-6.4.1/cogl/cogl/cogl-pixel-buffer-private.h0000664000175000017500000000310414723361714020743 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ #ifndef __COGL_PIXEL_BUFFER_PRIVATE_H__ #define __COGL_PIXEL_BUFFER_PRIVATE_H__ #include "cogl-object-private.h" #include "cogl-buffer-private.h" #include G_BEGIN_DECLS struct _CoglPixelBuffer { CoglBuffer _parent; }; G_END_DECLS #endif /* __COGL_PIXEL_BUFFER_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-color.c0000664000175000017500000002062514723361714016023 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-util.h" #include "cogl-color.h" #include "cogl-color-private.h" #include "cogl-gtype-private.h" COGL_GTYPE_DEFINE_BOXED (Color, color, cogl_color_copy, cogl_color_free); CoglColor * cogl_color_new (void) { return g_slice_new (CoglColor); } CoglColor * cogl_color_copy (const CoglColor *color) { if (G_LIKELY (color)) return g_slice_dup (CoglColor, color); return NULL; } void cogl_color_free (CoglColor *color) { if (G_LIKELY (color)) g_slice_free (CoglColor, color); } void cogl_color_init_from_4ub (CoglColor *color, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { g_return_if_fail (color != NULL); color->red = red; color->green = green; color->blue = blue; color->alpha = alpha; } void cogl_color_init_from_4f (CoglColor *color, float red, float green, float blue, float alpha) { g_return_if_fail (color != NULL); color->red = (red * 255); color->green = (green * 255); color->blue = (blue * 255); color->alpha = (alpha * 255); } void cogl_color_init_from_4fv (CoglColor *color, const float *color_array) { g_return_if_fail (color != NULL); color->red = (color_array[0] * 255); color->green = (color_array[1] * 255); color->blue = (color_array[2] * 255); color->alpha = (color_array[3] * 255); } unsigned char cogl_color_get_red_byte (const CoglColor *color) { return color->red; } float cogl_color_get_red_float (const CoglColor *color) { return (float) color->red / 255.0; } float cogl_color_get_red (const CoglColor *color) { return ((float) color->red / 255.0); } unsigned char cogl_color_get_green_byte (const CoglColor *color) { return color->green; } float cogl_color_get_green_float (const CoglColor *color) { return (float) color->green / 255.0; } float cogl_color_get_green (const CoglColor *color) { return ((float) color->green / 255.0); } unsigned char cogl_color_get_blue_byte (const CoglColor *color) { return color->blue; } float cogl_color_get_blue_float (const CoglColor *color) { return (float) color->blue / 255.0; } float cogl_color_get_blue (const CoglColor *color) { return ((float) color->blue / 255.0); } unsigned char cogl_color_get_alpha_byte (const CoglColor *color) { return color->alpha; } float cogl_color_get_alpha_float (const CoglColor *color) { return (float) color->alpha / 255.0; } float cogl_color_get_alpha (const CoglColor *color) { return ((float) color->alpha / 255.0); } void cogl_color_set_red_byte (CoglColor *color, unsigned char red) { color->red = red; } void cogl_color_set_red_float (CoglColor *color, float red) { color->red = red * 255.0; } void cogl_color_set_red (CoglColor *color, float red) { color->red = red * 255.0; } void cogl_color_set_green_byte (CoglColor *color, unsigned char green) { color->green = green; } void cogl_color_set_green_float (CoglColor *color, float green) { color->green = green * 255.0; } void cogl_color_set_green (CoglColor *color, float green) { color->green = green * 255.0; } void cogl_color_set_blue_byte (CoglColor *color, unsigned char blue) { color->blue = blue; } void cogl_color_set_blue_float (CoglColor *color, float blue) { color->blue = blue * 255.0; } void cogl_color_set_blue (CoglColor *color, float blue) { color->blue = blue * 255.0; } void cogl_color_set_alpha_byte (CoglColor *color, unsigned char alpha) { color->alpha = alpha; } void cogl_color_set_alpha_float (CoglColor *color, float alpha) { color->alpha = alpha * 255.0; } void cogl_color_set_alpha (CoglColor *color, float alpha) { color->alpha = alpha * 255.0; } void cogl_color_premultiply (CoglColor *color) { color->red = (color->red * color->alpha + 128) / 255; color->green = (color->green * color->alpha + 128) / 255; color->blue = (color->blue * color->alpha + 128) / 255; } void cogl_color_unpremultiply (CoglColor *color) { if (color->alpha != 0) { color->red = (color->red * 255) / color->alpha; color->green = (color->green * 255) / color->alpha; color->blue = (color->blue * 255) / color->alpha; } } gboolean cogl_color_equal (const void *v1, const void *v2) { const uint32_t *c1 = v1, *c2 = v2; g_return_val_if_fail (v1 != NULL, FALSE); g_return_val_if_fail (v2 != NULL, FALSE); /* XXX: We don't compare the padding */ return *c1 == *c2 ? TRUE : FALSE; } void _cogl_color_get_rgba_4ubv (const CoglColor *color, uint8_t *dest) { memcpy (dest, color, 4); } void cogl_color_to_hsl (const CoglColor *color, float *hue, float *saturation, float *luminance) { float red, green, blue; float min, max, delta; float h, l, s; red = color->red / 255.0; green = color->green / 255.0; blue = color->blue / 255.0; if (red > green) { if (red > blue) max = red; else max = blue; if (green < blue) min = green; else min = blue; } else { if (green > blue) max = green; else max = blue; if (red < blue) min = red; else min = blue; } l = (max + min) / 2; s = 0; h = 0; if (max != min) { if (l <= 0.5) s = (max - min) / (max + min); else s = (max - min) / (2.0 - max - min); delta = max - min; if (red == max) h = (green - blue) / delta; else if (green == max) h = 2.0 + (blue - red) / delta; else if (blue == max) h = 4.0 + (red - green) / delta; h *= 60; if (h < 0) h += 360.0; } if (hue) *hue = h; if (luminance) *luminance = l; if (saturation) *saturation = s; } void cogl_color_init_from_hsl (CoglColor *color, float hue, float saturation, float luminance) { float tmp1, tmp2; float tmp3[3]; float clr[3]; int i; hue /= 360.0; if (saturation == 0) { cogl_color_init_from_4f (color, luminance, luminance, luminance, 1.0f); return; } if (luminance <= 0.5) tmp2 = luminance * (1.0 + saturation); else tmp2 = luminance + saturation - (luminance * saturation); tmp1 = 2.0 * luminance - tmp2; tmp3[0] = hue + 1.0 / 3.0; tmp3[1] = hue; tmp3[2] = hue - 1.0 / 3.0; for (i = 0; i < 3; i++) { if (tmp3[i] < 0) tmp3[i] += 1.0; if (tmp3[i] > 1) tmp3[i] -= 1.0; if (6.0 * tmp3[i] < 1.0) clr[i] = tmp1 + (tmp2 - tmp1) * tmp3[i] * 6.0; else if (2.0 * tmp3[i] < 1.0) clr[i] = tmp2; else if (3.0 * tmp3[i] < 2.0) clr[i] = (tmp1 + (tmp2 - tmp1) * ((2.0 / 3.0) - tmp3[i]) * 6.0); else clr[i] = tmp1; } cogl_color_init_from_4f (color, clr[0], clr[1], clr[2], 1.0f); } muffin-6.4.1/cogl/cogl/cogl-primitive.c0000664000175000017500000005330314723361714016714 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-object-private.h" #include "cogl-primitive.h" #include "cogl-primitive-private.h" #include "cogl-attribute-private.h" #include "cogl-framebuffer-private.h" #include "cogl-gtype-private.h" #include #include static void _cogl_primitive_free (CoglPrimitive *primitive); COGL_OBJECT_DEFINE (Primitive, primitive); COGL_GTYPE_DEFINE_CLASS (Primitive, primitive); CoglPrimitive * cogl_primitive_new_with_attributes (CoglVerticesMode mode, int n_vertices, CoglAttribute **attributes, int n_attributes) { CoglPrimitive *primitive; int i; primitive = g_slice_alloc (sizeof (CoglPrimitive) + sizeof (CoglAttribute *) * (n_attributes - 1)); primitive->mode = mode; primitive->first_vertex = 0; primitive->n_vertices = n_vertices; primitive->indices = NULL; primitive->immutable_ref = 0; primitive->n_attributes = n_attributes; primitive->n_embedded_attributes = n_attributes; primitive->attributes = &primitive->embedded_attribute; for (i = 0; i < n_attributes; i++) { CoglAttribute *attribute = attributes[i]; cogl_object_ref (attribute); g_return_val_if_fail (cogl_is_attribute (attribute), NULL); primitive->attributes[i] = attribute; } return _cogl_primitive_object_new (primitive); } /* This is just an internal convenience wrapper around new_with_attributes that also unrefs the attributes. It is just used for the builtin struct constructors */ static CoglPrimitive * _cogl_primitive_new_with_attributes_unref (CoglVerticesMode mode, int n_vertices, CoglAttribute **attributes, int n_attributes) { CoglPrimitive *primitive; int i; primitive = cogl_primitive_new_with_attributes (mode, n_vertices, attributes, n_attributes); for (i = 0; i < n_attributes; i++) cogl_object_unref (attributes[i]); return primitive; } CoglPrimitive * cogl_primitive_new (CoglVerticesMode mode, int n_vertices, ...) { va_list ap; int n_attributes; CoglAttribute **attributes; int i; CoglAttribute *attribute; va_start (ap, n_vertices); for (n_attributes = 0; va_arg (ap, CoglAttribute *); n_attributes++) ; va_end (ap); attributes = g_alloca (sizeof (CoglAttribute *) * n_attributes); va_start (ap, n_vertices); for (i = 0; (attribute = va_arg (ap, CoglAttribute *)); i++) attributes[i] = attribute; va_end (ap); return cogl_primitive_new_with_attributes (mode, n_vertices, attributes, i); } CoglPrimitive * cogl_primitive_new_p2 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP2 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2), data); CoglAttribute *attributes[1]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP2), offsetof (CoglVertexP2, x), 2, COGL_ATTRIBUTE_TYPE_FLOAT); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 1); } CoglPrimitive * cogl_primitive_new_p3 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP3 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3), data); CoglAttribute *attributes[1]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP3), offsetof (CoglVertexP3, x), 3, COGL_ATTRIBUTE_TYPE_FLOAT); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 1); } CoglPrimitive * cogl_primitive_new_p2c4 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP2C4 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2C4), data); CoglAttribute *attributes[2]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP2C4), offsetof (CoglVertexP2C4, x), 2, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_color_in", sizeof (CoglVertexP2C4), offsetof (CoglVertexP2C4, r), 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 2); } CoglPrimitive * cogl_primitive_new_p3c4 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP3C4 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3C4), data); CoglAttribute *attributes[2]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP3C4), offsetof (CoglVertexP3C4, x), 3, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_color_in", sizeof (CoglVertexP3C4), offsetof (CoglVertexP3C4, r), 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 2); } CoglPrimitive * cogl_primitive_new_p2t2 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP2T2 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2T2), data); CoglAttribute *attributes[2]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP2T2), offsetof (CoglVertexP2T2, x), 2, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2T2), offsetof (CoglVertexP2T2, s), 2, COGL_ATTRIBUTE_TYPE_FLOAT); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 2); } CoglPrimitive * cogl_primitive_new_p3t2 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP3T2 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3T2), data); CoglAttribute *attributes[2]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP3T2), offsetof (CoglVertexP3T2, x), 3, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP3T2), offsetof (CoglVertexP3T2, s), 2, COGL_ATTRIBUTE_TYPE_FLOAT); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 2); } CoglPrimitive * cogl_primitive_new_p2t2c4 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP2T2C4 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP2T2C4), data); CoglAttribute *attributes[3]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP2T2C4), offsetof (CoglVertexP2T2C4, x), 2, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2T2C4), offsetof (CoglVertexP2T2C4, s), 2, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[2] = cogl_attribute_new (attribute_buffer, "cogl_color_in", sizeof (CoglVertexP2T2C4), offsetof (CoglVertexP2T2C4, r), 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 3); } CoglPrimitive * cogl_primitive_new_p3t2c4 (CoglContext *ctx, CoglVerticesMode mode, int n_vertices, const CoglVertexP3T2C4 *data) { CoglAttributeBuffer *attribute_buffer = cogl_attribute_buffer_new (ctx, n_vertices * sizeof (CoglVertexP3T2C4), data); CoglAttribute *attributes[3]; attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP3T2C4), offsetof (CoglVertexP3T2C4, x), 3, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (attribute_buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP3T2C4), offsetof (CoglVertexP3T2C4, s), 2, COGL_ATTRIBUTE_TYPE_FLOAT); attributes[2] = cogl_attribute_new (attribute_buffer, "cogl_color_in", sizeof (CoglVertexP3T2C4), offsetof (CoglVertexP3T2C4, r), 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); cogl_object_unref (attribute_buffer); return _cogl_primitive_new_with_attributes_unref (mode, n_vertices, attributes, 3); } static void _cogl_primitive_free (CoglPrimitive *primitive) { int i; for (i = 0; i < primitive->n_attributes; i++) cogl_object_unref (primitive->attributes[i]); if (primitive->attributes != &primitive->embedded_attribute) g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, primitive->attributes); if (primitive->indices) cogl_object_unref (primitive->indices); g_slice_free1 (sizeof (CoglPrimitive) + sizeof (CoglAttribute *) * (primitive->n_embedded_attributes - 1), primitive); } static void warn_about_midscene_changes (void) { static gboolean seen = FALSE; if (!seen) { g_warning ("Mid-scene modification of primitives has " "undefined results\n"); seen = TRUE; } } void cogl_primitive_set_attributes (CoglPrimitive *primitive, CoglAttribute **attributes, int n_attributes) { int i; g_return_if_fail (cogl_is_primitive (primitive)); if (G_UNLIKELY (primitive->immutable_ref)) { warn_about_midscene_changes (); return; } /* NB: we don't unref the previous attributes before refing the new * in case we would end up releasing the last reference for an * attribute thats actually in the new list too. */ for (i = 0; i < n_attributes; i++) { g_return_if_fail (cogl_is_attribute (attributes[i])); cogl_object_ref (attributes[i]); } for (i = 0; i < primitive->n_attributes; i++) cogl_object_unref (primitive->attributes[i]); /* First try to use the embedded storage assocated with the * primitive, else fallback to slice allocating separate storage for * the attribute pointers... */ if (n_attributes <= primitive->n_embedded_attributes) { if (primitive->attributes != &primitive->embedded_attribute) g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, primitive->attributes); primitive->attributes = &primitive->embedded_attribute; } else { if (primitive->attributes != &primitive->embedded_attribute) g_slice_free1 (sizeof (CoglAttribute *) * primitive->n_attributes, primitive->attributes); primitive->attributes = g_slice_alloc (sizeof (CoglAttribute *) * n_attributes); } memcpy (primitive->attributes, attributes, sizeof (CoglAttribute *) * n_attributes); primitive->n_attributes = n_attributes; } int cogl_primitive_get_first_vertex (CoglPrimitive *primitive) { g_return_val_if_fail (cogl_is_primitive (primitive), 0); return primitive->first_vertex; } void cogl_primitive_set_first_vertex (CoglPrimitive *primitive, int first_vertex) { g_return_if_fail (cogl_is_primitive (primitive)); if (G_UNLIKELY (primitive->immutable_ref)) { warn_about_midscene_changes (); return; } primitive->first_vertex = first_vertex; } int cogl_primitive_get_n_vertices (CoglPrimitive *primitive) { g_return_val_if_fail (cogl_is_primitive (primitive), 0); return primitive->n_vertices; } void cogl_primitive_set_n_vertices (CoglPrimitive *primitive, int n_vertices) { g_return_if_fail (cogl_is_primitive (primitive)); primitive->n_vertices = n_vertices; } CoglVerticesMode cogl_primitive_get_mode (CoglPrimitive *primitive) { g_return_val_if_fail (cogl_is_primitive (primitive), 0); return primitive->mode; } void cogl_primitive_set_mode (CoglPrimitive *primitive, CoglVerticesMode mode) { g_return_if_fail (cogl_is_primitive (primitive)); if (G_UNLIKELY (primitive->immutable_ref)) { warn_about_midscene_changes (); return; } primitive->mode = mode; } void cogl_primitive_set_indices (CoglPrimitive *primitive, CoglIndices *indices, int n_indices) { g_return_if_fail (cogl_is_primitive (primitive)); if (G_UNLIKELY (primitive->immutable_ref)) { warn_about_midscene_changes (); return; } if (indices) cogl_object_ref (indices); if (primitive->indices) cogl_object_unref (primitive->indices); primitive->indices = indices; primitive->n_vertices = n_indices; } CoglIndices * cogl_primitive_get_indices (CoglPrimitive *primitive) { return primitive->indices; } CoglPrimitive * cogl_primitive_copy (CoglPrimitive *primitive) { CoglPrimitive *copy; copy = cogl_primitive_new_with_attributes (primitive->mode, primitive->n_vertices, primitive->attributes, primitive->n_attributes); cogl_primitive_set_indices (copy, primitive->indices, primitive->n_vertices); cogl_primitive_set_first_vertex (copy, primitive->first_vertex); return copy; } CoglPrimitive * _cogl_primitive_immutable_ref (CoglPrimitive *primitive) { int i; g_return_val_if_fail (cogl_is_primitive (primitive), NULL); primitive->immutable_ref++; for (i = 0; i < primitive->n_attributes; i++) _cogl_attribute_immutable_ref (primitive->attributes[i]); return primitive; } void _cogl_primitive_immutable_unref (CoglPrimitive *primitive) { int i; g_return_if_fail (cogl_is_primitive (primitive)); g_return_if_fail (primitive->immutable_ref > 0); primitive->immutable_ref--; for (i = 0; i < primitive->n_attributes; i++) _cogl_attribute_immutable_unref (primitive->attributes[i]); } void cogl_primitive_foreach_attribute (CoglPrimitive *primitive, CoglPrimitiveAttributeCallback callback, void *user_data) { int i; for (i = 0; i < primitive->n_attributes; i++) if (!callback (primitive, primitive->attributes[i], user_data)) break; } void _cogl_primitive_draw (CoglPrimitive *primitive, CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglDrawFlags flags) { if (primitive->indices) _cogl_framebuffer_draw_indexed_attributes (framebuffer, pipeline, primitive->mode, primitive->first_vertex, primitive->n_vertices, primitive->indices, primitive->attributes, primitive->n_attributes, flags); else _cogl_framebuffer_draw_attributes (framebuffer, pipeline, primitive->mode, primitive->first_vertex, primitive->n_vertices, primitive->attributes, primitive->n_attributes, flags); } void cogl_primitive_draw (CoglPrimitive *primitive, CoglFramebuffer *framebuffer, CoglPipeline *pipeline) { _cogl_primitive_draw (primitive, framebuffer, pipeline, 0 /* flags */); } muffin-6.4.1/cogl/cogl/cogl-pipeline-cache.h0000664000175000017500000000655514723361714017566 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PIPELINE_CACHE_H__ #define __COGL_PIPELINE_CACHE_H__ #include "cogl-pipeline.h" typedef struct _CoglPipelineCache CoglPipelineCache; typedef struct { CoglPipeline *pipeline; /* Number of usages of this template. If this drops to zero then it * will be a candidate for removal from the cache */ int usage_count; } CoglPipelineCacheEntry; CoglPipelineCache * _cogl_pipeline_cache_new (void); void _cogl_pipeline_cache_free (CoglPipelineCache *cache); /* * Gets a pipeline from the cache that has the same state as * @key_pipeline for the state in * COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no * matching pipline already then a copy of key_pipeline is stored in * the cache so that it will be used next time the function is called * with a similar pipeline. In that case the copy itself will be * returned */ CoglPipelineCacheEntry * _cogl_pipeline_cache_get_fragment_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline); /* * Gets a pipeline from the cache that has the same state as * @key_pipeline for the state in * COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN. If there is no * matching pipline already then a copy of key_pipeline is stored in * the cache so that it will be used next time the function is called * with a similar pipeline. In that case the copy itself will be * returned */ CoglPipelineCacheEntry * _cogl_pipeline_cache_get_vertex_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline); /* * Gets a pipeline from the cache that has the same state as * @key_pipeline for the combination of the state state in * COGL_PIPELINE_STATE_AFFECTS_VERTEX_CODEGEN and * COGL_PIPELINE_STATE_AFFECTS_FRAGMENT_CODEGEN. If there is no * matching pipline already then a copy of key_pipeline is stored in * the cache so that it will be used next time the function is called * with a similar pipeline. In that case the copy itself will be * returned */ CoglPipelineCacheEntry * _cogl_pipeline_cache_get_combined_template (CoglPipelineCache *cache, CoglPipeline *key_pipeline); #endif /* __COGL_PIPELINE_CACHE_H__ */ muffin-6.4.1/cogl/cogl/cogl-bitmap-pixbuf.c0000664000175000017500000001012414723361714017445 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-bitmap-private.h" #include "cogl-context-private.h" #include "cogl-private.h" #include #include gboolean _cogl_bitmap_get_size_from_file (const char *filename, int *width, int *height) { g_return_val_if_fail (filename != NULL, FALSE); if (gdk_pixbuf_get_file_info (filename, width, height) != NULL) return TRUE; return FALSE; } CoglBitmap * _cogl_bitmap_from_file (CoglContext *ctx, const char *filename, GError **error) { static CoglUserDataKey pixbuf_key; GdkPixbuf *pixbuf; gboolean has_alpha; GdkColorspace color_space; CoglPixelFormat pixel_format; int width; int height; int rowstride; int bits_per_sample; int n_channels; CoglBitmap *bmp; GError *glib_error = NULL; /* Load from file using GdkPixbuf */ pixbuf = gdk_pixbuf_new_from_file (filename, &glib_error); if (pixbuf == NULL) { g_propagate_error (error, glib_error); return FALSE; } /* Get pixbuf properties */ has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); color_space = gdk_pixbuf_get_colorspace (pixbuf); width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); rowstride = gdk_pixbuf_get_rowstride (pixbuf); bits_per_sample = gdk_pixbuf_get_bits_per_sample (pixbuf); n_channels = gdk_pixbuf_get_n_channels (pixbuf); /* According to current docs this should be true and so * the translation to cogl pixel format below valid */ g_assert (bits_per_sample == 8); if (has_alpha) g_assert (n_channels == 4); else g_assert (n_channels == 3); /* Translate to cogl pixel format */ switch (color_space) { case GDK_COLORSPACE_RGB: /* The only format supported by GdkPixbuf so far */ pixel_format = has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888; break; default: /* Ouch, spec changed! */ g_object_unref (pixbuf); return FALSE; } /* We just use the data directly from the pixbuf so that we don't have to copy to a seperate buffer. Note that Cogl is expected not to read past the end of bpp*width on the last row even if the rowstride is much larger so we don't need to worry about GdkPixbuf's semantics that it may under-allocate the buffer. */ bmp = cogl_bitmap_new_for_data (ctx, width, height, pixel_format, rowstride, gdk_pixbuf_get_pixels (pixbuf)); cogl_object_set_user_data (COGL_OBJECT (bmp), &pixbuf_key, pixbuf, g_object_unref); return bmp; } muffin-6.4.1/cogl/cogl/cogl-bitmask.h0000664000175000017500000002316014723361714016341 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts */ #ifndef __COGL_BITMASK_H #define __COGL_BITMASK_H #include #include "cogl-util.h" G_BEGIN_DECLS /* * CoglBitmask implements a growable array of bits. A CoglBitmask can * be allocated on the stack but it must be initialised with * _cogl_bitmask_init() before use and then destroyed with * _cogl_bitmask_destroy(). A CoglBitmask will try to avoid allocating * any memory unless more than the number of bits in a long - 1 bits * are needed. * * Internally a CoglBitmask is a pointer. If the least significant bit * of the pointer is 1 then the rest of the bits are directly used as * part of the bitmask, otherwise it is a pointer to a GArray of * unsigned ints. This relies on the fact the g_malloc will return a * pointer aligned to at least two bytes (so that the least * significant bit of the address is always 0). It also assumes that * the size of a pointer is always greater than or equal to the size * of a long (although there is a compile time assert to verify this). * * If the maximum possible bit number in the set is known at compile * time, it may make more sense to use the macros in cogl-flags.h * instead of this type. */ typedef struct _CoglBitmaskImaginaryType *CoglBitmask; /* These are internal helper macros */ #define _cogl_bitmask_to_number(bitmask) \ ((unsigned long) (*bitmask)) #define _cogl_bitmask_to_bits(bitmask) \ (_cogl_bitmask_to_number (bitmask) >> 1UL) /* The least significant bit is set to mark that no array has been allocated yet */ #define _cogl_bitmask_from_bits(bits) \ ((void *) ((((unsigned long) (bits)) << 1UL) | 1UL)) /* Internal helper macro to determine whether this bitmask has a GArray allocated or whether the pointer is just used directly */ #define _cogl_bitmask_has_array(bitmask) \ (!(_cogl_bitmask_to_number (bitmask) & 1UL)) /* Number of bits we can use before needing to allocate an array */ #define COGL_BITMASK_MAX_DIRECT_BITS (sizeof (unsigned long) * 8 - 1) /* * _cogl_bitmask_init: * @bitmask: A pointer to a bitmask * * Initialises the cogl bitmask. This must be called before any other * bitmask functions are called. Initially all of the values are * zero */ #define _cogl_bitmask_init(bitmask) \ G_STMT_START { *(bitmask) = _cogl_bitmask_from_bits (0); } G_STMT_END gboolean _cogl_bitmask_get_from_array (const CoglBitmask *bitmask, unsigned int bit_num); void _cogl_bitmask_set_in_array (CoglBitmask *bitmask, unsigned int bit_num, gboolean value); void _cogl_bitmask_set_range_in_array (CoglBitmask *bitmask, unsigned int n_bits, gboolean value); void _cogl_bitmask_clear_all_in_array (CoglBitmask *bitmask); void _cogl_bitmask_set_flags_array (const CoglBitmask *bitmask, unsigned long *flags); int _cogl_bitmask_popcount_in_array (const CoglBitmask *bitmask); int _cogl_bitmask_popcount_upto_in_array (const CoglBitmask *bitmask, int upto); /* * cogl_bitmask_set_bits: * @dst: The bitmask to modify * @src: The bitmask to copy bits from * * This makes sure that all of the bits that are set in @src are also * set in @dst. Any unset bits in @src are left alone in @dst. */ void _cogl_bitmask_set_bits (CoglBitmask *dst, const CoglBitmask *src); /* * cogl_bitmask_xor_bits: * @dst: The bitmask to modify * @src: The bitmask to copy bits from * * For every bit that is set in src, the corresponding bit in dst is * inverted. */ void _cogl_bitmask_xor_bits (CoglBitmask *dst, const CoglBitmask *src); /* The foreach function can return FALSE to stop iteration */ typedef gboolean (* CoglBitmaskForeachFunc) (int bit_num, void *user_data); /* * cogl_bitmask_foreach: * @bitmask: A pointer to a bitmask * @func: A callback function * @user_data: A pointer to pass to the callback * * This calls @func for each bit that is set in @bitmask. */ void _cogl_bitmask_foreach (const CoglBitmask *bitmask, CoglBitmaskForeachFunc func, void *user_data); /* * _cogl_bitmask_get: * @bitmask: A pointer to a bitmask * @bit_num: A bit number * * Return value: whether bit number @bit_num is set in @bitmask */ static inline gboolean _cogl_bitmask_get (const CoglBitmask *bitmask, unsigned int bit_num) { if (_cogl_bitmask_has_array (bitmask)) return _cogl_bitmask_get_from_array (bitmask, bit_num); else if (bit_num >= COGL_BITMASK_MAX_DIRECT_BITS) return FALSE; else return !!(_cogl_bitmask_to_bits (bitmask) & (1UL << bit_num)); } /* * _cogl_bitmask_set: * @bitmask: A pointer to a bitmask * @bit_num: A bit number * @value: The new value * * Sets or resets a bit number @bit_num in @bitmask according to @value. */ static inline void _cogl_bitmask_set (CoglBitmask *bitmask, unsigned int bit_num, gboolean value) { if (_cogl_bitmask_has_array (bitmask) || bit_num >= COGL_BITMASK_MAX_DIRECT_BITS) _cogl_bitmask_set_in_array (bitmask, bit_num, value); else if (value) *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) | (1UL << bit_num)); else *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) & ~(1UL << bit_num)); } /* * _cogl_bitmask_set_range: * @bitmask: A pointer to a bitmask * @n_bits: The number of bits to set * @value: The value to set * * Sets the first @n_bits in @bitmask to @value. */ static inline void _cogl_bitmask_set_range (CoglBitmask *bitmask, unsigned int n_bits, gboolean value) { if (_cogl_bitmask_has_array (bitmask) || n_bits > COGL_BITMASK_MAX_DIRECT_BITS) _cogl_bitmask_set_range_in_array (bitmask, n_bits, value); else if (value) *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) | ~(~0UL << n_bits)); else *bitmask = _cogl_bitmask_from_bits (_cogl_bitmask_to_bits (bitmask) & (~0UL << n_bits)); } /* * _cogl_bitmask_destroy: * @bitmask: A pointer to a bitmask * * Destroys any resources allocated by the bitmask */ static inline void _cogl_bitmask_destroy (CoglBitmask *bitmask) { if (_cogl_bitmask_has_array (bitmask)) g_array_free ((GArray *) *bitmask, TRUE); } /* * _cogl_bitmask_clear_all: * @bitmask: A pointer to a bitmask * * Clears all the bits in a bitmask without destroying any resources. */ static inline void _cogl_bitmask_clear_all (CoglBitmask *bitmask) { if (_cogl_bitmask_has_array (bitmask)) _cogl_bitmask_clear_all_in_array (bitmask); else *bitmask = _cogl_bitmask_from_bits (0); } /* * _cogl_bitmask_set_flags: * @bitmask: A pointer to a bitmask * @flags: An array of flags * * Bitwise or's the bits from @bitmask into the flags array (see * cogl-flags) pointed to by @flags. */ static inline void _cogl_bitmask_set_flags (const CoglBitmask *bitmask, unsigned long *flags) { if (_cogl_bitmask_has_array (bitmask)) _cogl_bitmask_set_flags_array (bitmask, flags); else flags[0] |= _cogl_bitmask_to_bits (bitmask); } /* * _cogl_bitmask_popcount: * @bitmask: A pointer to a bitmask * * Counts the number of bits that are set in the bitmask. * * Return value: the number of bits set in @bitmask. */ static inline int _cogl_bitmask_popcount (const CoglBitmask *bitmask) { return (_cogl_bitmask_has_array (bitmask) ? _cogl_bitmask_popcount_in_array (bitmask) : _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask))); } /* * _cogl_bitmask_popcount: * @Bitmask: A pointer to a bitmask * @upto: The maximum bit index to consider * * Counts the number of bits that are set and have an index which is * less than @upto. * * Return value: the number of bits set in @bitmask that are less than @upto. */ static inline int _cogl_bitmask_popcount_upto (const CoglBitmask *bitmask, int upto) { if (_cogl_bitmask_has_array (bitmask)) return _cogl_bitmask_popcount_upto_in_array (bitmask, upto); else if (upto >= (int) COGL_BITMASK_MAX_DIRECT_BITS) return _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask)); else return _cogl_util_popcountl (_cogl_bitmask_to_bits (bitmask) & ((1UL << upto) - 1)); } G_END_DECLS #endif /* __COGL_BITMASK_H */ muffin-6.4.1/cogl/cogl/cogl-macros.h0000664000175000017500000000556414723361714016203 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_MACROS_H__ #define __COGL_MACROS_H__ #include /* These macros are used to mark deprecated functions, and thus have * to be exposed in a public header. * * They are only intended for internal use and should not be used by * other projects. */ #if defined(COGL_DISABLE_DEPRECATION_WARNINGS) || defined(COGL_COMPILATION) #define COGL_DEPRECATED #define COGL_DEPRECATED_FOR(f) #define COGL_UNAVAILABLE(maj,min) #else /* COGL_DISABLE_DEPRECATION_WARNINGS */ #if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1) #define COGL_DEPRECATED __attribute__((__deprecated__)) #elif defined(_MSC_VER) && (_MSC_VER >= 1300) #define COGL_DEPRECATED __declspec(deprecated) #else #define COGL_DEPRECATED #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #define COGL_DEPRECATED_FOR(f) __attribute__((__deprecated__("Use '" #f "' instead"))) #elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) #define COGL_DEPRECATED_FOR(f) __declspec(deprecated("is deprecated. Use '" #f "' instead")) #else #define COGL_DEPRECATED_FOR(f) G_DEPRECATED #endif #if __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5) #define COGL_UNAVAILABLE(maj,min) __attribute__((deprecated("Not available before " #maj "." #min))) #elif defined(_MSC_FULL_VER) && (_MSC_FULL_VER > 140050320) #define COGL_UNAVAILABLE(maj,min) __declspec(deprecated("is not available before " #maj "." #min)) #else #define COGL_UNAVAILABLE(maj,min) #endif #endif /* COGL_DISABLE_DEPRECATION_WARNINGS */ #define COGL_EXPORT __attribute__((visibility("default"))) extern #endif /* __COGL_MACROS_H__ */ muffin-6.4.1/cogl/cogl/cogl-trace.h0000664000175000017500000000652414723361714016012 0ustar fabiofabio/* * Copyright 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef COGL_TRACE_H #define COGL_TRACE_H #include #include #include #include "cogl/cogl-defines.h" #include "cogl/cogl-macros.h" #ifdef COGL_HAS_TRACING typedef struct _CoglTraceContext CoglTraceContext; typedef struct _CoglTraceHead { uint64_t begin_time; const char *name; } CoglTraceHead; COGL_EXPORT GPrivate cogl_trace_thread_data; COGL_EXPORT CoglTraceContext *cogl_trace_context; COGL_EXPORT GMutex cogl_trace_mutex; COGL_EXPORT void cogl_set_tracing_enabled_on_thread_with_fd (GMainContext *main_context, const char *group, int fd); COGL_EXPORT void cogl_set_tracing_enabled_on_thread (GMainContext *main_context, const char *group, const char *filename); COGL_EXPORT void cogl_set_tracing_disabled_on_thread (GMainContext *main_context); static inline void cogl_trace_begin (CoglTraceHead *head, const char *name) { head->begin_time = g_get_monotonic_time () * 1000; head->name = name; } COGL_EXPORT void cogl_trace_end (CoglTraceHead *head); static inline void cogl_auto_trace_end_helper (CoglTraceHead **head) { if (*head) cogl_trace_end (*head); } #define COGL_TRACE_BEGIN(Name, description) \ CoglTraceHead CoglTrace##Name = { 0 }; \ if (g_private_get (&cogl_trace_thread_data)) \ cogl_trace_begin (&CoglTrace##Name, description); \ #define COGL_TRACE_END(Name)\ if (g_private_get (&cogl_trace_thread_data)) \ cogl_trace_end (&CoglTrace##Name); #define COGL_TRACE_BEGIN_SCOPED(Name, description) \ CoglTraceHead CoglTrace##Name = { 0 }; \ __attribute__((cleanup (cogl_auto_trace_end_helper))) \ CoglTraceHead *ScopedCoglTrace##Name = NULL; \ if (g_private_get (&cogl_trace_thread_data)) \ { \ cogl_trace_begin (&CoglTrace##Name, description); \ ScopedCoglTrace##Name = &CoglTrace##Name; \ } #else /* COGL_HAS_TRACING */ #include #define COGL_TRACE_BEGIN(Name, description) (void) 0 #define COGL_TRACE_END(Name) (void) 0 #define COGL_TRACE_BEGIN_SCOPED(Name, description) (void) 0 COGL_EXPORT void cogl_set_tracing_enabled_on_thread_with_fd (void *data, const char *group, int fd); COGL_EXPORT void cogl_set_tracing_enabled_on_thread (void *data, const char *group, const char *filename); COGL_EXPORT void cogl_set_tracing_disabled_on_thread (void *data); #endif /* COGL_HAS_TRACING */ #endif /* COGL_TRACE_H */ muffin-6.4.1/cogl/cogl/cogl-framebuffer.h0000664000175000017500000016217014723361714017200 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2011 Intel Corporation. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_FRAMEBUFFER_H #define __COGL_FRAMEBUFFER_H /* We forward declare the CoglFramebuffer type here to avoid some circular * dependency issues with the following headers. */ #if defined(__COGL_H_INSIDE__) && !defined(COGL_ENABLE_MUTTER_API) && \ !defined(COGL_GIR_SCANNING) /* For the public C api we typedef interface types as void to avoid needing * lots of casting in code and instead we will rely on runtime type checking * for these objects. */ typedef void CoglFramebuffer; #else typedef struct _CoglFramebuffer CoglFramebuffer; #define COGL_FRAMEBUFFER(X) ((CoglFramebuffer *)(X)) #endif #include #include #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-framebuffer * @short_description: A common interface for manipulating framebuffers * * Framebuffers are a collection of buffers that can be rendered too. * A framebuffer may be comprised of one or more color buffers, an * optional depth buffer and an optional stencil buffer. Other * configuration parameters are associated with framebuffers too such * as whether the framebuffer supports multi-sampling (an anti-aliasing * technique) or dithering. * * There are two kinds of framebuffer in Cogl, #CoglOnscreen * framebuffers and #CoglOffscreen framebuffers. As the names imply * offscreen framebuffers are for rendering something offscreen * (perhaps to a texture which is bound as one of the color buffers). * The exact semantics of onscreen framebuffers depends on the window * system backend that you are using, but typically you can expect * rendering to a #CoglOnscreen framebuffer will be immediately * visible to the user. * * If you want to create a new framebuffer then you should start by * looking at the #CoglOnscreen and #CoglOffscreen constructor * functions, such as cogl_offscreen_new_with_texture() or * cogl_onscreen_new(). The #CoglFramebuffer interface deals with * all aspects that are common between those two types of framebuffer. * * Setup of a new CoglFramebuffer happens in two stages. There is a * configuration stage where you specify all the options and ancillary * buffers you want associated with your framebuffer and then when you * are happy with the configuration you can "allocate" the framebuffer * using cogl_framebuffer_allocate(). Technically explicitly calling * cogl_framebuffer_allocate() is optional for convenience and the * framebuffer will automatically be allocated when you first try to * draw to it, but if you do the allocation manually then you can * also catch any possible errors that may arise from your * configuration. */ /** * cogl_framebuffer_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_framebuffer_get_gtype (void); /** * cogl_framebuffer_allocate: * @framebuffer: A #CoglFramebuffer * @error: A pointer to a #GError for returning exceptions. * * Explicitly allocates a configured #CoglFramebuffer allowing developers to * check and handle any errors that might arise from an unsupported * configuration so that fallback configurations may be tried. * * Many applications don't support any fallback options at least when * they are initially developed and in that case the don't need to use this API * since Cogl will automatically allocate a framebuffer when it first gets * used. The disadvantage of relying on automatic allocation is that the * program will abort with an error message if there is an error during * automatic allocation. * * Return value: %TRUE if there were no error allocating the framebuffer, else %FALSE. * Since: 1.8 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_allocate (CoglFramebuffer *framebuffer, GError **error); /** * cogl_framebuffer_get_width: * @framebuffer: A #CoglFramebuffer * * Queries the current width of the given @framebuffer. * * Return value: The width of @framebuffer. * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_width (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_height: * @framebuffer: A #CoglFramebuffer * * Queries the current height of the given @framebuffer. * * Return value: The height of @framebuffer. * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_height (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_set_viewport: * @framebuffer: A #CoglFramebuffer * @x: The top-left x coordinate of the viewport origin (only integers * supported currently) * @y: The top-left y coordinate of the viewport origin (only integers * supported currently) * @width: The width of the viewport (only integers supported currently) * @height: The height of the viewport (only integers supported currently) * * Defines a scale and offset for everything rendered relative to the * top-left of the destination framebuffer. * * By default the viewport has an origin of (0,0) and width and height * that match the framebuffer's size. Assuming a default projection and * modelview matrix then you could translate the contents of a window * down and right by leaving the viewport size unchanged by moving the * offset to (10,10). The viewport coordinates are measured in pixels. * If you left the x and y origin as (0,0) you could scale the windows * contents down by specify and width and height that's half the real * size of the framebuffer. * * Although the function takes floating point arguments, existing * drivers only allow the use of integer values. In the future floating * point values will be exposed via a checkable feature. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer, float x, float y, float width, float height); /** * cogl_framebuffer_get_viewport_x: * @framebuffer: A #CoglFramebuffer * * Queries the x coordinate of the viewport origin as set using cogl_framebuffer_set_viewport() * or the default value which is 0. * * Return value: The x coordinate of the viewport origin. * Since: 1.8 * Stability: unstable */ COGL_EXPORT float cogl_framebuffer_get_viewport_x (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_viewport_y: * @framebuffer: A #CoglFramebuffer * * Queries the y coordinate of the viewport origin as set using cogl_framebuffer_set_viewport() * or the default value which is 0. * * Return value: The y coordinate of the viewport origin. * Since: 1.8 * Stability: unstable */ COGL_EXPORT float cogl_framebuffer_get_viewport_y (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_viewport_width: * @framebuffer: A #CoglFramebuffer * * Queries the width of the viewport as set using cogl_framebuffer_set_viewport() * or the default value which is the width of the framebuffer. * * Return value: The width of the viewport. * Since: 1.8 * Stability: unstable */ COGL_EXPORT float cogl_framebuffer_get_viewport_width (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_viewport_height: * @framebuffer: A #CoglFramebuffer * * Queries the height of the viewport as set using cogl_framebuffer_set_viewport() * or the default value which is the height of the framebuffer. * * Return value: The height of the viewport. * Since: 1.8 * Stability: unstable */ COGL_EXPORT float cogl_framebuffer_get_viewport_height (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_viewport4fv: * @framebuffer: A #CoglFramebuffer * @viewport: (out caller-allocates) (array fixed-size=4): A pointer to an * array of 4 floats to receive the (x, y, width, height) * components of the current viewport. * * Queries the x, y, width and height components of the current viewport as set * using cogl_framebuffer_set_viewport() or the default values which are 0, 0, * framebuffer_width and framebuffer_height. The values are written into the * given @viewport array. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_get_viewport4fv (CoglFramebuffer *framebuffer, float *viewport); /** * cogl_framebuffer_push_matrix: * @framebuffer: A #CoglFramebuffer pointer * * Copies the current model-view matrix onto the matrix stack. The matrix * can later be restored with cogl_framebuffer_pop_matrix(). * * Since: 1.10 */ COGL_EXPORT void cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_pop_matrix: * @framebuffer: A #CoglFramebuffer pointer * * Restores the model-view matrix on the top of the matrix stack. * * Since: 1.10 */ COGL_EXPORT void cogl_framebuffer_pop_matrix (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_identity_matrix: * @framebuffer: A #CoglFramebuffer pointer * * Resets the current model-view matrix to the identity matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_identity_matrix (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_scale: * @framebuffer: A #CoglFramebuffer pointer * @x: Amount to scale along the x-axis * @y: Amount to scale along the y-axis * @z: Amount to scale along the z-axis * * Multiplies the current model-view matrix by one that scales the x, * y and z axes by the given values. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_scale (CoglFramebuffer *framebuffer, float x, float y, float z); /** * cogl_framebuffer_translate: * @framebuffer: A #CoglFramebuffer pointer * @x: Distance to translate along the x-axis * @y: Distance to translate along the y-axis * @z: Distance to translate along the z-axis * * Multiplies the current model-view matrix by one that translates the * model along all three axes according to the given values. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_translate (CoglFramebuffer *framebuffer, float x, float y, float z); /** * cogl_framebuffer_rotate: * @framebuffer: A #CoglFramebuffer pointer * @angle: Angle in degrees to rotate. * @x: X-component of vertex to rotate around. * @y: Y-component of vertex to rotate around. * @z: Z-component of vertex to rotate around. * * Multiplies the current model-view matrix by one that rotates the * model around the axis-vector specified by @x, @y and @z. The * rotation follows the right-hand thumb rule so for example rotating * by 10 degrees about the axis-vector (0, 0, 1) causes a small * counter-clockwise rotation. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_rotate (CoglFramebuffer *framebuffer, float angle, float x, float y, float z); /** * cogl_framebuffer_rotate_euler: * @framebuffer: A #CoglFramebuffer pointer * @euler: A #graphene_euler_t * * Multiplies the current model-view matrix by one that rotates * according to the rotation described by @euler. * * Since: 2.0 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_rotate_euler (CoglFramebuffer *framebuffer, const graphene_euler_t *euler); /** * cogl_framebuffer_transform: * @framebuffer: A #CoglFramebuffer pointer * @matrix: the matrix to multiply with the current model-view * * Multiplies the current model-view matrix by the given matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_transform (CoglFramebuffer *framebuffer, const CoglMatrix *matrix); /** * cogl_framebuffer_get_modelview_matrix: * @framebuffer: A #CoglFramebuffer pointer * @matrix: (out): return location for the model-view matrix * * Stores the current model-view matrix in @matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix); /** * cogl_framebuffer_set_modelview_matrix: * @framebuffer: A #CoglFramebuffer pointer * @matrix: the new model-view matrix * * Sets @matrix as the new model-view matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_modelview_matrix (CoglFramebuffer *framebuffer, const CoglMatrix *matrix); /** * cogl_framebuffer_perspective: * @framebuffer: A #CoglFramebuffer pointer * @fov_y: Vertical field of view angle in degrees. * @aspect: The (width over height) aspect ratio for display * @z_near: The distance to the near clipping plane (Must be positive, * and must not be 0) * @z_far: The distance to the far clipping plane (Must be positive) * * Replaces the current projection matrix with a perspective matrix * based on the provided values. * * You should be careful not to have to great a @z_far / @z_near * ratio since that will reduce the effectiveness of depth testing * since there wont be enough precision to identify the depth of * objects near to each other. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_perspective (CoglFramebuffer *framebuffer, float fov_y, float aspect, float z_near, float z_far); /** * cogl_framebuffer_frustum: * @framebuffer: A #CoglFramebuffer pointer * @left: X position of the left clipping plane where it * intersects the near clipping plane * @right: X position of the right clipping plane where it * intersects the near clipping plane * @bottom: Y position of the bottom clipping plane where it * intersects the near clipping plane * @top: Y position of the top clipping plane where it intersects * the near clipping plane * @z_near: The distance to the near clipping plane (Must be positive) * @z_far: The distance to the far clipping plane (Must be positive) * * Replaces the current projection matrix with a perspective matrix * for a given viewing frustum defined by 4 side clip planes that * all cross through the origin and 2 near and far clip planes. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_frustum (CoglFramebuffer *framebuffer, float left, float right, float bottom, float top, float z_near, float z_far); /** * cogl_framebuffer_orthographic: * @framebuffer: A #CoglFramebuffer pointer * @x_1: The x coordinate for the first vertical clipping plane * @y_1: The y coordinate for the first horizontal clipping plane * @x_2: The x coordinate for the second vertical clipping plane * @y_2: The y coordinate for the second horizontal clipping plane * @near: The distance to the near clipping * plane (will be negative if the plane is * behind the viewer) * @far: The distance to the far clipping * plane (will be negative if the plane is * behind the viewer) * * Replaces the current projection matrix with an orthographic projection * matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_orthographic (CoglFramebuffer *framebuffer, float x_1, float y_1, float x_2, float y_2, float near, float far); /** * cogl_framebuffer_get_projection_matrix: * @framebuffer: A #CoglFramebuffer pointer * @matrix: (out): return location for the projection matrix * * Stores the current projection matrix in @matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix); /** * cogl_framebuffer_set_projection_matrix: * @framebuffer: A #CoglFramebuffer pointer * @matrix: the new projection matrix * * Sets @matrix as the new projection matrix. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_projection_matrix (CoglFramebuffer *framebuffer, const CoglMatrix *matrix); /** * cogl_framebuffer_push_scissor_clip: * @framebuffer: A #CoglFramebuffer pointer * @x: left edge of the clip rectangle in window coordinates * @y: top edge of the clip rectangle in window coordinates * @width: width of the clip rectangle * @height: height of the clip rectangle * * Specifies a rectangular clipping area for all subsequent drawing * operations. Any drawing commands that extend outside the rectangle * will be clipped so that only the portion inside the rectangle will * be displayed. The rectangle dimensions are not transformed by the * current model-view matrix. * * The rectangle is intersected with the current clip region. To undo * the effect of this function, call cogl_framebuffer_pop_clip(). * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_push_scissor_clip (CoglFramebuffer *framebuffer, int x, int y, int width, int height); /** * cogl_framebuffer_push_rectangle_clip: * @framebuffer: A #CoglFramebuffer pointer * @x_1: x coordinate for top left corner of the clip rectangle * @y_1: y coordinate for top left corner of the clip rectangle * @x_2: x coordinate for bottom right corner of the clip rectangle * @y_2: y coordinate for bottom right corner of the clip rectangle * * Specifies a modelview transformed rectangular clipping area for all * subsequent drawing operations. Any drawing commands that extend * outside the rectangle will be clipped so that only the portion * inside the rectangle will be displayed. The rectangle dimensions * are transformed by the current model-view matrix. * * The rectangle is intersected with the current clip region. To undo * the effect of this function, call cogl_framebuffer_pop_clip(). * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer, float x_1, float y_1, float x_2, float y_2); /** * cogl_framebuffer_push_primitive_clip: (skip) * @framebuffer: A #CoglFramebuffer pointer * @primitive: A #CoglPrimitive describing a flat 2D shape * @bounds_x1: x coordinate for the top-left corner of the primitives * bounds * @bounds_y1: y coordinate for the top-left corner of the primitives * bounds * @bounds_x2: x coordinate for the bottom-right corner of the * primitives bounds. * @bounds_y2: y coordinate for the bottom-right corner of the * primitives bounds. * * Sets a new clipping area using a 2D shaped described with a * #CoglPrimitive. The shape must not contain self overlapping * geometry and must lie on a single 2D plane. A bounding box of the * 2D shape in local coordinates (the same coordinates used to * describe the shape) must be given. It is acceptable for the bounds * to be larger than the true bounds but behaviour is undefined if the * bounds are smaller than the true bounds. * * The primitive is transformed by the current model-view matrix and * the silhouette is intersected with the previous clipping area. To * restore the previous clipping area, call * cogl_framebuffer_pop_clip(). * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2); COGL_EXPORT void cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer, cairo_region_t *region); /** * cogl_framebuffer_pop_clip: * @framebuffer: A #CoglFramebuffer pointer * * Reverts the clipping region to the state before the last call to * cogl_framebuffer_push_scissor_clip(), cogl_framebuffer_push_rectangle_clip() * cogl_framebuffer_push_path_clip(), or cogl_framebuffer_push_primitive_clip(). * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_red_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of red bits of @framebuffer * * Return value: the number of bits * * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_green_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of green bits of @framebuffer * * Return value: the number of bits * * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_blue_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of blue bits of @framebuffer * * Return value: the number of bits * * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_alpha_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of alpha bits of @framebuffer * * Return value: the number of bits * * Since: 1.8 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_depth_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of depth bits of @framebuffer * * Return value: the number of bits * * Since: 2.0 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer); /* * cogl_framebuffer_get_is_stereo: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves whether @framebuffer has separate left and right * buffers for use with stereo drawing. See * cogl_framebuffer_set_stereo_mode(). * * Return value: %TRUE if @framebuffer has separate left and * right buffers. * * Since: 1.20 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_get_is_stereo (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_get_dither_enabled: * @framebuffer: a pointer to a #CoglFramebuffer * * Returns whether dithering has been requested for the given @framebuffer. * See cogl_framebuffer_set_dither_enabled() for more details about dithering. * * This may return %TRUE even when the underlying @framebuffer * display pipeline does not support dithering. This value only represents * the user's request for dithering. * * Return value: %TRUE if dithering has been requested or %FALSE if not. * Since: 1.8 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_get_dither_enabled (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_set_dither_enabled: * @framebuffer: a pointer to a #CoglFramebuffer * @dither_enabled: %TRUE to enable dithering or %FALSE to disable * * Enables or disabled dithering if supported by the hardware. * * Dithering is a hardware dependent technique to increase the visible * color resolution beyond what the underlying hardware supports by playing * tricks with the colors placed into the framebuffer to give the illusion * of other colors. (For example this can be compared to half-toning used * by some news papers to show varying levels of grey even though their may * only be black and white are available). * * If the current display pipeline for @framebuffer does not support dithering * then this has no affect. * * Dithering is enabled by default. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_dither_enabled (CoglFramebuffer *framebuffer, gboolean dither_enabled); /** * cogl_framebuffer_get_depth_write_enabled: * @framebuffer: a pointer to a #CoglFramebuffer * * Queries whether depth buffer writing is enabled for @framebuffer. This * can be controlled via cogl_framebuffer_set_depth_write_enabled(). * * Return value: %TRUE if depth writing is enabled or %FALSE if not. * Since: 1.18 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_get_depth_write_enabled (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_set_depth_write_enabled: * @framebuffer: a pointer to a #CoglFramebuffer * @depth_write_enabled: %TRUE to enable depth writing or %FALSE to disable * * Enables or disables depth buffer writing when rendering to @framebuffer. * If depth writing is enabled for both the framebuffer and the rendering * pipeline, and the framebuffer has an associated depth buffer, depth * information will be written to this buffer during rendering. * * Depth buffer writing is enabled by default. * * Since: 1.18 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_depth_write_enabled (CoglFramebuffer *framebuffer, gboolean depth_write_enabled); /** * cogl_framebuffer_get_stereo_mode: * @framebuffer: a pointer to a #CoglFramebuffer * * Gets the current #CoglStereoMode, which defines which stereo buffers * should be drawn to. See cogl_framebuffer_set_stereo_mode(). * * Returns: A #CoglStereoMode * Since: 1.20 * Stability: unstable */ COGL_EXPORT CoglStereoMode cogl_framebuffer_get_stereo_mode (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_set_stereo_mode: * @framebuffer: a pointer to a #CoglFramebuffer * @stereo_mode: A #CoglStereoMode specifying which stereo buffers * should be drawn tow. * * Sets which stereo buffers should be drawn to. The default * is %COGL_STEREO_BOTH, which means that both the left and * right buffers will be affected by drawing. For this to have * an effect, the display system must support stereo drawables, * and the framebuffer must have been created with stereo * enabled. (See cogl_onscreen_template_set_stereo_enabled(), * cogl_framebuffer_get_is_stereo().) * * Since: 1.20 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_stereo_mode (CoglFramebuffer *framebuffer, CoglStereoMode stereo_mode); /** * cogl_framebuffer_set_samples_per_pixel: * @framebuffer: A #CoglFramebuffer framebuffer * @samples_per_pixel: The minimum number of samples per pixel * * Requires that when rendering to @framebuffer then @n point samples * should be made per pixel which will all contribute to the final * resolved color for that pixel. The idea is that the hardware aims * to get quality similar to what you would get if you rendered * everything twice as big (for 4 samples per pixel) and then scaled * that image back down with filtering. It can effectively remove the * jagged edges of polygons and should be more efficient than if you * were to manually render at a higher resolution and downscale * because the hardware is often able to take some shortcuts. For * example the GPU may only calculate a single texture sample for all * points of a single pixel, and for tile based architectures all the * extra sample data (such as depth and stencil samples) may be * handled on-chip and so avoid increased demand on system memory * bandwidth. * * By default this value is usually set to 0 and that is referred to * as "single-sample" rendering. A value of 1 or greater is referred * to as "multisample" rendering. * * There are some semantic differences between single-sample * rendering and multisampling with just 1 point sample such as it * being redundant to use the cogl_framebuffer_resolve_samples() and * cogl_framebuffer_resolve_samples_region() apis with single-sample * rendering. * * It's recommended that * cogl_framebuffer_resolve_samples_region() be explicitly used at the * end of rendering to a point sample buffer to minimize the number of * samples that get resolved. By default Cogl will implicitly resolve * all framebuffer samples but if only a small region of a * framebuffer has changed this can lead to redundant work being * done. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer, int samples_per_pixel); /** * cogl_framebuffer_get_samples_per_pixel: * @framebuffer: A #CoglFramebuffer framebuffer * * Gets the number of points that are sampled per-pixel when * rasterizing geometry. Usually by default this will return 0 which * means that single-sample not multisample rendering has been chosen. * When using a GPU supporting multisample rendering it's possible to * increase the number of samples per pixel using * cogl_framebuffer_set_samples_per_pixel(). * * Calling cogl_framebuffer_get_samples_per_pixel() before the * framebuffer has been allocated will simply return the value set * using cogl_framebuffer_set_samples_per_pixel(). After the * framebuffer has been allocated the value will reflect the actual * number of samples that will be made by the GPU. * * Returns: The number of point samples made per pixel when * rasterizing geometry or 0 if single-sample rendering * has been chosen. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT int cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_resolve_samples: * @framebuffer: A #CoglFramebuffer framebuffer * * When point sample rendering (also known as multisample rendering) * has been enabled via cogl_framebuffer_set_samples_per_pixel() * then you can optionally call this function (or * cogl_framebuffer_resolve_samples_region()) to explicitly resolve * the point samples into values for the final color buffer. * * Some GPUs will implicitly resolve the point samples during * rendering and so this function is effectively a nop, but with other * architectures it is desirable to defer the resolve step until the * end of the frame. * * Since Cogl will automatically ensure samples are resolved if the * target color buffer is used as a source this API only needs to be * used if explicit control is desired - perhaps because you want to * ensure that the resolve is completed in advance to avoid later * having to wait for the resolve to complete. * * If you are performing incremental updates to a framebuffer you * should consider using cogl_framebuffer_resolve_samples_region() * instead to avoid resolving redundant pixels. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_resolve_samples_region: * @framebuffer: A #CoglFramebuffer framebuffer * @x: top-left x coordinate of region to resolve * @y: top-left y coordinate of region to resolve * @width: width of region to resolve * @height: height of region to resolve * * When point sample rendering (also known as multisample rendering) * has been enabled via cogl_framebuffer_set_samples_per_pixel() * then you can optionally call this function (or * cogl_framebuffer_resolve_samples()) to explicitly resolve the point * samples into values for the final color buffer. * * Some GPUs will implicitly resolve the point samples during * rendering and so this function is effectively a nop, but with other * architectures it is desirable to defer the resolve step until the * end of the frame. * * Use of this API is recommended if incremental, small updates to * a framebuffer are being made because by default Cogl will * implicitly resolve all the point samples of the framebuffer which * can result in redundant work if only a small number of samples have * changed. * * Because some GPUs implicitly resolve point samples this function * only guarantees that at-least the region specified will be resolved * and if you have rendered to a larger region then it's possible that * other samples may be implicitly resolved. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer, int x, int y, int width, int height); /** * cogl_framebuffer_get_context: * @framebuffer: A #CoglFramebuffer * * Can be used to query the #CoglContext a given @framebuffer was * instantiated within. This is the #CoglContext that was passed to * cogl_onscreen_new() for example. * * Return value: (transfer none): The #CoglContext that the given * @framebuffer was instantiated within. * Since: 1.8 * Stability: unstable */ COGL_EXPORT CoglContext * cogl_framebuffer_get_context (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_clear: * @framebuffer: A #CoglFramebuffer * @buffers: A mask of #CoglBufferBit's identifying which auxiliary * buffers to clear * @color: The color to clear the color buffer too if specified in * @buffers. * * Clears all the auxiliary buffers identified in the @buffers mask, and if * that includes the color buffer then the specified @color is used. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_clear (CoglFramebuffer *framebuffer, unsigned long buffers, const CoglColor *color); /** * cogl_framebuffer_clear4f: * @framebuffer: A #CoglFramebuffer * @buffers: A mask of #CoglBufferBit's identifying which auxiliary * buffers to clear * @red: The red component of color to clear the color buffer too if * specified in @buffers. * @green: The green component of color to clear the color buffer too if * specified in @buffers. * @blue: The blue component of color to clear the color buffer too if * specified in @buffers. * @alpha: The alpha component of color to clear the color buffer too if * specified in @buffers. * * Clears all the auxiliary buffers identified in the @buffers mask, and if * that includes the color buffer then the specified @color is used. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha); /** * cogl_framebuffer_draw_primitive: (skip) * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @primitive: A #CoglPrimitive geometry object * * Draws the given @primitive geometry to the specified destination * @framebuffer using the graphics processing state described by @pipeline. * * This drawing api doesn't support high-level meta texture types such * as #CoglTexture2DSliced so it is the user's responsibility to * ensure that only low-level textures that can be directly sampled by * a GPU such as #CoglTexture2D are associated with layers of the given * @pipeline. * * This api doesn't support any of the legacy global state options such * as cogl_set_depth_test_enabled() or * cogl_set_backface_culling_enabled(). * * Stability: unstable * Since: 1.10 * Deprecated: 1.16: Use #CoglPrimitives and * cogl_primitive_draw() instead */ COGL_DEPRECATED_FOR (cogl_primitive_draw) COGL_EXPORT void cogl_framebuffer_draw_primitive (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPrimitive *primitive); /** * cogl_framebuffer_draw_rectangle: * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @x_1: X coordinate of the top-left corner * @y_1: Y coordinate of the top-left corner * @x_2: X coordinate of the bottom-right corner * @y_2: Y coordinate of the bottom-right corner * * Draws a rectangle to @framebuffer with the given @pipeline state * and with the top left corner positioned at (@x_1, @y_1) and the * bottom right corner positioned at (@x_2, @y_2). * * The position is the position before the rectangle has been * transformed by the model-view matrix and the projection * matrix. * * If you want to describe a rectangle with a texture mapped on * it then you can use * cogl_framebuffer_draw_textured_rectangle(). * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_draw_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2); /** * cogl_framebuffer_draw_textured_rectangle: * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @x_1: x coordinate upper left on screen. * @y_1: y coordinate upper left on screen. * @x_2: x coordinate lower right on screen. * @y_2: y coordinate lower right on screen. * @s_1: S texture coordinate of the top-left coorner * @t_1: T texture coordinate of the top-left coorner * @s_2: S texture coordinate of the bottom-right coorner * @t_2: T texture coordinate of the bottom-right coorner * * Draws a textured rectangle to @framebuffer using the given * @pipeline state with the top left corner positioned at (@x_1, @y_1) * and the bottom right corner positioned at (@x_2, @y_2). The top * left corner will have texture coordinates of (@s_1, @t_1) and the * bottom right corner will have texture coordinates of (@s_2, @t_2). * * The position is the position before the rectangle has been * transformed by the model-view matrix and the projection * matrix. * * This is a high level drawing api that can handle any kind of * #CoglMetaTexture texture such as #CoglTexture2DSliced textures * which may internally be comprised of multiple low-level textures. * This is unlike low-level drawing apis such as cogl_primitive_draw() * which only support low level texture types that are directly * supported by GPUs such as #CoglTexture2D. * * The given texture coordinates will only be used for the first * texture layer of the pipeline and if your pipeline has more than * one layer then all other layers will have default texture * coordinates of @s_1=0.0 @t_1=0.0 @s_2=1.0 @t_2=1.0 * * The given texture coordinates should always be normalized such that * (0, 0) corresponds to the top left and (1, 1) corresponds to the * bottom right. To map an entire texture across the rectangle pass * in @s_1=0, @t_1=0, @s_2=1, @t_2=1. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_draw_textured_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2, float s_1, float t_1, float s_2, float t_2); /** * cogl_framebuffer_draw_multitextured_rectangle: * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @x_1: x coordinate upper left on screen. * @y_1: y coordinate upper left on screen. * @x_2: x coordinate lower right on screen. * @y_2: y coordinate lower right on screen. * @tex_coords: (in) (array) (transfer none): An array containing groups of * 4 float values: [s_1, t_1, s_2, t_2] that are interpreted as two texture * coordinates; one for the top left texel, and one for the bottom right * texel. Each value should be between 0.0 and 1.0, where the coordinate * (0.0, 0.0) represents the top left of the texture, and (1.0, 1.0) the * bottom right. * @tex_coords_len: The length of the @tex_coords array. (For one layer * and one group of texture coordinates, this would be 4) * * Draws a textured rectangle to @framebuffer with the given @pipeline * state with the top left corner positioned at (@x_1, @y_1) and the * bottom right corner positioned at (@x_2, @y_2). As a pipeline may * contain multiple texture layers this interface lets you supply * texture coordinates for each layer of the pipeline. * * The position is the position before the rectangle has been * transformed by the model-view matrix and the projection * matrix. * * This is a high level drawing api that can handle any kind of * #CoglMetaTexture texture for the first layer such as * #CoglTexture2DSliced textures which may internally be comprised of * multiple low-level textures. This is unlike low-level drawing apis * such as cogl_primitive_draw() which only support low level texture * types that are directly supported by GPUs such as #CoglTexture2D. * * This api can not currently handle multiple high-level meta * texture layers. The first layer may be a high level meta texture * such as #CoglTexture2DSliced but all other layers much be low * level textures such as #CoglTexture2D. * * The top left texture coordinate for layer 0 of any pipeline will be * (tex_coords[0], tex_coords[1]) and the bottom right coordinate will * be (tex_coords[2], tex_coords[3]). The coordinates for layer 1 * would be (tex_coords[4], tex_coords[5]) (tex_coords[6], * tex_coords[7]) and so on... * * The given texture coordinates should always be normalized such that * (0, 0) corresponds to the top left and (1, 1) corresponds to the * bottom right. To map an entire texture across the rectangle pass * in tex_coords[0]=0, tex_coords[1]=0, tex_coords[2]=1, * tex_coords[3]=1. * * The first pair of coordinates are for the first layer (with the * smallest layer index) and if you supply less texture coordinates * than there are layers in the current source material then default * texture coordinates (0.0, 0.0, 1.0, 1.0) are generated. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_draw_multitextured_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2, const float *tex_coords, int tex_coords_len); /** * cogl_framebuffer_draw_rectangles: * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @coordinates: (in) (array) (transfer none): an array of coordinates * containing groups of 4 float values: [x_1, y_1, x_2, y_2] that are * interpreted as two position coordinates; one for the top left of * the rectangle (x1, y1), and one for the bottom right of the * rectangle (x2, y2). * @n_rectangles: number of rectangles defined in @coordinates. * * Draws a series of rectangles to @framebuffer with the given * @pipeline state in the same way that * cogl_framebuffer_draw_rectangle() does. * * The top left corner of the first rectangle is positioned at * (coordinates[0], coordinates[1]) and the bottom right corner is * positioned at (coordinates[2], coordinates[3]). The positions for * the second rectangle are (coordinates[4], coordinates[5]) and * (coordinates[6], coordinates[7]) and so on... * * The position is the position before the rectangle has been * transformed by the model-view matrix and the projection * matrix. * * As a general rule for better performance its recommended to use * this this API instead of calling * cogl_framebuffer_draw_textured_rectangle() separately for multiple * rectangles if all of the rectangles will be drawn together with the * same @pipeline state. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_draw_rectangles (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *coordinates, unsigned int n_rectangles); /** * cogl_framebuffer_draw_textured_rectangles: * @framebuffer: A destination #CoglFramebuffer * @pipeline: A #CoglPipeline state object * @coordinates: (in) (array) (transfer none): an array containing * groups of 8 float values: [x_1, y_1, x_2, y_2, s_1, t_1, s_2, t_2] * that have the same meaning as the arguments for * cogl_framebuffer_draw_textured_rectangle(). * @n_rectangles: number of rectangles to @coordinates to draw * * Draws a series of rectangles to @framebuffer with the given * @pipeline state in the same way that * cogl_framebuffer_draw_textured_rectangle() does. * * The position is the position before the rectangle has been * transformed by the model-view matrix and the projection * matrix. * * This is a high level drawing api that can handle any kind of * #CoglMetaTexture texture such as #CoglTexture2DSliced textures * which may internally be comprised of multiple low-level textures. * This is unlike low-level drawing apis such as cogl_primitive_draw() * which only support low level texture types that are directly * supported by GPUs such as #CoglTexture2D. * * The top left corner of the first rectangle is positioned at * (coordinates[0], coordinates[1]) and the bottom right corner is * positioned at (coordinates[2], coordinates[3]). The top left * texture coordinate is (coordinates[4], coordinates[5]) and the * bottom right texture coordinate is (coordinates[6], * coordinates[7]). The coordinates for subsequent rectangles * are defined similarly by the subsequent coordinates. * * As a general rule for better performance its recommended to use * this this API instead of calling * cogl_framebuffer_draw_textured_rectangle() separately for multiple * rectangles if all of the rectangles will be drawn together with the * same @pipeline state. * * The given texture coordinates should always be normalized such that * (0, 0) corresponds to the top left and (1, 1) corresponds to the * bottom right. To map an entire texture across the rectangle pass * in tex_coords[0]=0, tex_coords[1]=0, tex_coords[2]=1, * tex_coords[3]=1. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *coordinates, unsigned int n_rectangles); /* XXX: Should we take an n_buffers + buffer id array instead of using * the CoglBufferBits type which doesn't seem future proof? */ /** * cogl_framebuffer_discard_buffers: * @framebuffer: A #CoglFramebuffer * @buffers: A #CoglBufferBit mask of which ancillary buffers you want * to discard. * * Declares that the specified @buffers no longer need to be referenced * by any further rendering commands. This can be an important * optimization to avoid subsequent frames of rendering depending on * the results of a previous frame. * * For example; some tile-based rendering GPUs are able to avoid allocating and * accessing system memory for the depth and stencil buffer so long as these * buffers are not required as input for subsequent frames and that can save a * significant amount of memory bandwidth used to save and restore their * contents to system memory between frames. * * It is currently considered an error to try and explicitly discard the color * buffer by passing %COGL_BUFFER_BIT_COLOR. This is because the color buffer is * already implicitly discard when you finish rendering to a #CoglOnscreen * framebuffer, and it's not meaningful to try and discard the color buffer of * a #CoglOffscreen framebuffer since they are single-buffered. * * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); /** * cogl_framebuffer_finish: * @framebuffer: A #CoglFramebuffer pointer * * This blocks the CPU until all pending rendering associated with the * specified framebuffer has completed. It's very rare that developers should * ever need this level of synchronization with the GPU and should never be * used unless you clearly understand why you need to explicitly force * synchronization. * * One example might be for benchmarking purposes to be sure timing * measurements reflect the time that the GPU is busy for not just the time it * takes to queue rendering commands. * * Stability: unstable * Since: 1.10 */ COGL_EXPORT void cogl_framebuffer_finish (CoglFramebuffer *framebuffer); /** * cogl_framebuffer_read_pixels_into_bitmap: * @framebuffer: A #CoglFramebuffer * @x: The x position to read from * @y: The y position to read from * @source: Identifies which auxillary buffer you want to read * (only COGL_READ_PIXELS_COLOR_BUFFER supported currently) * @bitmap: The bitmap to store the results in. * * This reads a rectangle of pixels from the given framebuffer where * position (0, 0) is the top left. The pixel at (x, y) is the first * read, and a rectangle of pixels with the same size as the bitmap is * read right and downwards from that point. * * Currently Cogl assumes that the framebuffer is in a premultiplied * format so if the format of @bitmap is non-premultiplied it will * convert it. To read the pixel values without any conversion you * should either specify a format that doesn't use an alpha channel or * use one of the formats ending in PRE. * * Return value: %TRUE if the read succeeded or %FALSE otherwise. The * function is only likely to fail if the bitmap points to a pixel * buffer and it could not be mapped. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap); /** * cogl_framebuffer_read_pixels: * @framebuffer: A #CoglFramebuffer * @x: The x position to read from * @y: The y position to read from * @width: The width of the region of rectangles to read * @height: The height of the region of rectangles to read * @format: The pixel format to store the data in * @pixels: The address of the buffer to store the data in * * This is a convenience wrapper around * cogl_framebuffer_read_pixels_into_bitmap() which allocates a * temporary #CoglBitmap to read pixel data directly into the given * buffer. The rowstride of the buffer is assumed to be the width of * the region times the bytes per pixel of the format. The source for * the data is always taken from the color buffer. If you want to use * any other rowstride or source, please use the * cogl_framebuffer_read_pixels_into_bitmap() function directly. * * The implementation of the function looks like this: * * |[ * bitmap = cogl_bitmap_new_for_data (context, * width, height, * format, * /* rowstride */ * bpp * width, * pixels); * cogl_framebuffer_read_pixels_into_bitmap (framebuffer, * x, y, * COGL_READ_PIXELS_COLOR_BUFFER, * bitmap); * cogl_object_unref (bitmap); * ]| * * Return value: %TRUE if the read succeeded or %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer, int x, int y, int width, int height, CoglPixelFormat format, uint8_t *pixels); COGL_EXPORT uint32_t cogl_framebuffer_error_quark (void); /** * COGL_FRAMEBUFFER_ERROR: * * An error domain for reporting #CoglFramebuffer exceptions */ #define COGL_FRAMEBUFFER_ERROR (cogl_framebuffer_error_quark ()) typedef enum /*< prefix=COGL_FRAMEBUFFER_ERROR >*/ { COGL_FRAMEBUFFER_ERROR_ALLOCATE } CoglFramebufferError; /** * cogl_is_framebuffer: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglFramebuffer. * * Return value: %TRUE if the object references a #CoglFramebuffer * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_framebuffer (void *object); /** * cogl_blit_framebuffer: * @src: The source #CoglFramebuffer * @dest: The destination #CoglFramebuffer * @src_x: Source x position * @src_y: Source y position * @dst_x: Destination x position * @dst_y: Destination y position * @width: Width of region to copy * @height: Height of region to copy * @error: optional error object * * @return FALSE for an immediately detected error, TRUE otherwise. * * This blits a region of the color buffer of the source buffer * to the destination buffer. This function should only be * called if the COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER feature is * advertised. * * The source and destination rectangles are defined in offscreen * framebuffer orientation. When copying between an offscreen and * onscreen framebuffers, the image is y-flipped accordingly. * * The two buffers must have the same value types (e.g. floating-point, * unsigned int, signed int, or fixed-point), but color formats do not * need to match. This limitation comes from OpenGL ES 3.0 definition * of glBlitFramebuffer. * * Note that this function differs a lot from the glBlitFramebuffer * function provided by the GL_EXT_framebuffer_blit extension. Notably * it doesn't support having different sizes for the source and * destination rectangle. This doesn't seem * like a particularly useful feature. If the application wanted to * scale the results it may make more sense to draw a primitive * instead. * * The GL function is documented to be affected by the scissor. This * function therefore ensure that an empty clip stack is flushed * before performing the blit which means the scissor is effectively * ignored. * * The function also doesn't support specifying the buffers to copy * and instead only the color buffer is copied. When copying the depth * or stencil buffers the extension on GLES2.0 only supports copying * the full buffer which would be awkward to document with this * API. If we wanted to support that feature it may be better to have * a separate function to copy the entire buffer for a given mask. * * The @c error argument is optional, it can be NULL. If it is not NULL * and this function returns FALSE, an error object with a code from * COGL_SYSTEM_ERROR will be created. */ COGL_EXPORT gboolean cogl_blit_framebuffer (CoglFramebuffer *src, CoglFramebuffer *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height, GError **error); /** * cogl_framebuffer_flush: * @framebuffer: A #CoglFramebuffer pointer * * Flushes @framebuffer to ensure the current batch of commands is * submitted to the GPU. * * Unlike cogl_framebuffer_finish(), this does not block the CPU. */ void cogl_framebuffer_flush (CoglFramebuffer *framebuffer); G_END_DECLS #endif /* __COGL_FRAMEBUFFER_H */ muffin-6.4.1/cogl/cogl/cogl-i18n-private.h0000664000175000017500000000250114723361714017132 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef _COGL_I18N_PRIVATE_H_ #define _COGL_I18N_PRIVATE_H_ #include #define _(X) X #define N_(X) X #endif /* _COGL_I18N_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/cogl-util.h0000664000175000017500000001264614723361714015673 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_UTIL_H #define __COGL_UTIL_H #include #include #include #include #include "cogl-types.h" #include /* Double check that config.h has been included */ #ifndef COGL_CONFIG_H_INCLUDED #error "cogl-config.h must be included before including cogl-util.h" #endif int _cogl_util_next_p2 (int a); /* The signbit macro is defined by ISO C99 so it should be available, however if it's not we can fallback to an evil hack */ #ifdef signbit #define cogl_util_float_signbit(x) signbit(x) #else /* This trick was stolen from here: http://lists.boost.org/Archives/boost/2006/08/108731.php It xors the integer reinterpretations of -1.0f and 1.0f. In theory they should only differ by the signbit so that gives a mask for the sign which we can just test against the value */ static inline gboolean cogl_util_float_signbit (float x) { static const union { float f; uint32_t i; } negative_one = { -1.0f }; static const union { float f; uint32_t i; } positive_one = { +1.0f }; union { float f; uint32_t i; } value = { x }; return !!((negative_one.i ^ positive_one.i) & value.i); } #endif /* This is a replacement for the nearbyint function which always rounds to the nearest integer. nearbyint is apparently a C99 function so it might not always be available but also it seems in glibc it is defined as a function call so this macro could end up faster anyway. We can't just add 0.5f because it will break for negative numbers. */ #define COGL_UTIL_NEARBYINT(x) ((int) ((x) < 0.0f ? (x) - 0.5f : (x) + 0.5f)) /* Returns whether the given integer is a power of two */ static inline gboolean _cogl_util_is_pot (unsigned int num) { /* Make sure there is only one bit set */ return (num & (num - 1)) == 0; } /* Split Bob Jenkins' One-at-a-Time hash * * This uses the One-at-a-Time hash algorithm designed by Bob Jenkins * but the mixing step is split out so the function can be used in a * more incremental fashion. */ static inline unsigned int _cogl_util_one_at_a_time_hash (unsigned int hash, const void *key, size_t bytes) { const unsigned char *p = key; size_t i; for (i = 0; i < bytes; i++) { hash += p[i]; hash += (hash << 10); hash ^= (hash >> 6); } return hash; } unsigned int _cogl_util_one_at_a_time_mix (unsigned int hash); #define _cogl_util_ffsl __builtin_ffsl static inline unsigned int _cogl_util_fls (unsigned int n) { return n == 0 ? 0 : sizeof (unsigned int) * 8 - __builtin_clz (n); } #define _cogl_util_popcountl __builtin_popcountl /* Match a CoglPixelFormat according to channel masks, color depth, * bits per pixel and byte order. These information are provided by * the Visual and XImage structures. * * If no specific pixel format could be found, COGL_PIXEL_FORMAT_ANY * is returned. */ CoglPixelFormat _cogl_util_pixel_format_from_masks (unsigned long r_mask, unsigned long g_mask, unsigned long b_mask, int depth, int bpp, int byte_order); /* _COGL_STATIC_ASSERT: * @expression: An expression to assert evaluates to true at compile * time. * @message: A message to print to the console if the assertion fails * at compile time. * * Allows you to assert that an expression evaluates to true at * compile time and aborts compilation if not. If possible message * will also be printed if the assertion fails. */ #define _COGL_STATIC_ASSERT(EXPRESSION, MESSAGE) \ _Static_assert (EXPRESSION, MESSAGE); static inline void _cogl_util_scissor_intersect (int rect_x0, int rect_y0, int rect_x1, int rect_y1, int *scissor_x0, int *scissor_y0, int *scissor_x1, int *scissor_y1) { *scissor_x0 = MAX (*scissor_x0, rect_x0); *scissor_y0 = MAX (*scissor_y0, rect_y0); *scissor_x1 = MIN (*scissor_x1, rect_x1); *scissor_y1 = MIN (*scissor_y1, rect_y1); } #endif /* __COGL_UTIL_H */ muffin-6.4.1/cogl/cogl/cogl-matrix-private.h0000664000175000017500000000345214723361714017665 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_MATRIX_PRIVATE_H #define __COGL_MATRIX_PRIVATE_H #include G_BEGIN_DECLS #define _COGL_MATRIX_DEBUG_PRINT(MATRIX) \ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_MATRICES))) \ { \ g_print ("%s:\n", G_STRFUNC); \ cogl_debug_matrix_print (MATRIX); \ } void _cogl_matrix_prefix_print (const char *prefix, const CoglMatrix *matrix); void _cogl_matrix_init_from_matrix_without_inverse (CoglMatrix *matrix, const CoglMatrix *src); G_END_DECLS #endif /* __COGL_MATRIX_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-display-private.h0000664000175000017500000000321514723361714020023 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_DISPLAY_PRIVATE_H #define __COGL_DISPLAY_PRIVATE_H #include "cogl-object-private.h" #include "cogl-display.h" #include "cogl-renderer.h" #include "cogl-onscreen-template.h" struct _CoglDisplay { CoglObject _parent; gboolean setup; CoglRenderer *renderer; CoglOnscreenTemplate *onscreen_template; #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT struct wl_display *wayland_compositor_display; #endif void *winsys; }; #endif /* __COGL_DISPLAY_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-bitmap-private.h0000664000175000017500000001371414723361714017637 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007 OpenedHand * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_BITMAP_H #define __COGL_BITMAP_H #include #include "cogl-object-private.h" #include "cogl-buffer.h" #include "cogl-bitmap.h" struct _CoglBitmap { CoglObject _parent; /* Pointer back to the context that this bitmap was created with */ CoglContext *context; CoglPixelFormat format; int width; int height; int rowstride; uint8_t *data; gboolean mapped; gboolean bound; /* If this is non-null then 'data' is ignored and instead it is fetched from this shared bitmap. */ CoglBitmap *shared_bmp; /* If this is non-null then 'data' is treated as an offset into the buffer and map will divert to mapping the buffer */ CoglBuffer *buffer; }; /* * _cogl_bitmap_new_with_malloc_buffer: * @context: A #CoglContext * @width: width of the bitmap in pixels * @height: height of the bitmap in pixels * @format: the format of the pixels the array will store * @error: A #GError for catching exceptional errors or %NULL * * This is equivalent to cogl_bitmap_new_with_size() except that it * allocated the buffer using g_malloc() instead of creating a * #CoglPixelBuffer. The buffer will be automatically destroyed when * the bitmap is freed. * * Return value: a #CoglPixelBuffer representing the newly created array * * Since: 1.10 * Stability: Unstable */ CoglBitmap * _cogl_bitmap_new_with_malloc_buffer (CoglContext *context, unsigned int width, unsigned int height, CoglPixelFormat format, GError **error); /* The idea of this function is that it will create a bitmap that shares the actual data with another bitmap. This is needed for the atlas texture backend because it needs upload a bitmap to a sub texture but override the format so that it ignores the premult flag. */ CoglBitmap * _cogl_bitmap_new_shared (CoglBitmap *shared_bmp, CoglPixelFormat format, int width, int height, int rowstride); CoglBitmap * _cogl_bitmap_convert (CoglBitmap *bmp, CoglPixelFormat dst_format, GError **error); CoglBitmap * _cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error); gboolean _cogl_bitmap_convert_into_bitmap (CoglBitmap *src_bmp, CoglBitmap *dst_bmp, GError **error); CoglBitmap * _cogl_bitmap_from_file (CoglContext *ctx, const char *filename, GError **error); gboolean _cogl_bitmap_unpremult (CoglBitmap *dst_bmp, GError **error); gboolean _cogl_bitmap_premult (CoglBitmap *dst_bmp, GError **error); gboolean _cogl_bitmap_convert_premult_status (CoglBitmap *bmp, CoglPixelFormat dst_format, GError **error); gboolean _cogl_bitmap_copy_subregion (CoglBitmap *src, CoglBitmap *dst, int src_x, int src_y, int dst_x, int dst_y, int width, int height, GError **error); /* Creates a deep copy of the source bitmap */ CoglBitmap * _cogl_bitmap_copy (CoglBitmap *src_bmp, GError **error); gboolean _cogl_bitmap_get_size_from_file (const char *filename, int *width, int *height); void _cogl_bitmap_set_format (CoglBitmap *bitmap, CoglPixelFormat format); /* Maps the bitmap so that the pixels can be accessed directly or if the bitmap is just a memory bitmap then it just returns the pointer to memory. Note that the bitmap isn't guaranteed to allocated to the full size of rowstride*height so it is not safe to read up to the rowstride of the last row. This will be the case if the user uploads data using gdk_pixbuf_new_subpixbuf with a sub region containing the last row of the pixbuf because in that case the rowstride can be much larger than the width of the image */ uint8_t * _cogl_bitmap_map (CoglBitmap *bitmap, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); void _cogl_bitmap_unmap (CoglBitmap *bitmap); CoglContext * _cogl_bitmap_get_context (CoglBitmap *bitmap); #endif /* __COGL_BITMAP_H */ muffin-6.4.1/cogl/cogl/cogl-blend-string.c0000664000175000017500000007226314723361714017302 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include #include #include "cogl-context-private.h" #include "cogl-debug.h" #include "cogl-blend-string.h" typedef enum _ParserState { PARSER_STATE_EXPECT_DEST_CHANNELS, PARSER_STATE_SCRAPING_DEST_CHANNELS, PARSER_STATE_EXPECT_FUNCTION_NAME, PARSER_STATE_SCRAPING_FUNCTION_NAME, PARSER_STATE_EXPECT_ARG_START, PARSER_STATE_EXPECT_STATEMENT_END } ParserState; typedef enum _ParserArgState { PARSER_ARG_STATE_START, PARSER_ARG_STATE_EXPECT_MINUS, PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME, PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME, PARSER_ARG_STATE_MAYBE_COLOR_MASK, PARSER_ARG_STATE_SCRAPING_MASK, PARSER_ARG_STATE_MAYBE_MULT, PARSER_ARG_STATE_EXPECT_OPEN_PAREN, PARSER_ARG_STATE_EXPECT_FACTOR, PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE, PARSER_ARG_STATE_MAYBE_MINUS, PARSER_ARG_STATE_EXPECT_CLOSE_PAREN, PARSER_ARG_STATE_EXPECT_END } ParserArgState; #define DEFINE_COLOR_SOURCE(NAME, NAME_LEN) \ {/*.type = */COGL_BLEND_STRING_COLOR_SOURCE_ ## NAME, \ /*.name = */#NAME, \ /*.name_len = */NAME_LEN} static CoglBlendStringColorSourceInfo blending_color_sources[] = { DEFINE_COLOR_SOURCE (SRC_COLOR, 9), DEFINE_COLOR_SOURCE (DST_COLOR, 9), DEFINE_COLOR_SOURCE (CONSTANT, 8) }; static CoglBlendStringColorSourceInfo tex_combine_color_sources[] = { DEFINE_COLOR_SOURCE (TEXTURE, 7), /* DEFINE_COLOR_SOURCE (TEXTURE_N, *) - handled manually */ DEFINE_COLOR_SOURCE (PRIMARY, 7), DEFINE_COLOR_SOURCE (CONSTANT, 8), DEFINE_COLOR_SOURCE (PREVIOUS, 8) }; static CoglBlendStringColorSourceInfo tex_combine_texture_n_color_source = { /*.type = */COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, /*.name = */"TEXTURE_N", /*.name_len = */0 }; #undef DEFINE_COLOR_SOURCE #define DEFINE_FUNCTION(NAME, NAME_LEN, ARGC) \ { /*.type = */COGL_BLEND_STRING_FUNCTION_ ## NAME, \ /*.name = */#NAME, \ /*.name_len = */NAME_LEN, \ /*.argc = */ARGC } /* NB: These must be sorted so any name that's a subset of another * comes later than the longer name. */ static CoglBlendStringFunctionInfo tex_combine_functions[] = { DEFINE_FUNCTION (REPLACE, 7, 1), DEFINE_FUNCTION (MODULATE, 8, 2), DEFINE_FUNCTION (ADD_SIGNED, 10, 2), DEFINE_FUNCTION (ADD, 3, 2), DEFINE_FUNCTION (INTERPOLATE, 11, 3), DEFINE_FUNCTION (SUBTRACT, 8, 2), DEFINE_FUNCTION (DOT3_RGBA, 9, 2), DEFINE_FUNCTION (DOT3_RGB, 8, 2) }; static CoglBlendStringFunctionInfo blend_functions[] = { DEFINE_FUNCTION (ADD, 3, 2) }; #undef DEFINE_FUNCTION uint32_t cogl_blend_string_error_quark (void) { return g_quark_from_static_string ("cogl-blend-string-error-quark"); } void _cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, CoglBlendStringStatement *rgb, CoglBlendStringStatement *a) { int i; memcpy (rgb, statement, sizeof (CoglBlendStringStatement)); memcpy (a, statement, sizeof (CoglBlendStringStatement)); rgb->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; a->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; for (i = 0; i < statement->function->argc; i++) { CoglBlendStringArgument *arg = &statement->args[i]; CoglBlendStringArgument *rgb_arg = &rgb->args[i]; CoglBlendStringArgument *a_arg = &a->args[i]; if (arg->source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) { rgb_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; a_arg->source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; } if (arg->factor.is_color && arg->factor.source.mask == COGL_BLEND_STRING_CHANNEL_MASK_RGBA) { rgb_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; a_arg->factor.source.mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; } } } static gboolean validate_tex_combine_statements (CoglBlendStringStatement *statements, int n_statements, GError **error) { int i, j; const char *error_string; CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; for (i = 0; i < n_statements; i++) { for (j = 0; j < statements[i].function->argc; j++) { CoglBlendStringArgument *arg = &statements[i].args[j]; if (arg->source.is_zero) { error_string = "You can't use the constant '0' as a texture " "combine argument"; goto error; } if (!arg->factor.is_one) { error_string = "Argument factors are only relevant to blending " "not texture combining"; goto error; } } } return TRUE; error: g_set_error (error, COGL_BLEND_STRING_ERROR, detail, "Invalid texture combine string: %s", error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Invalid texture combine string: %s", error_string); } return FALSE; } static gboolean validate_blend_statements (CoglBlendStringStatement *statements, int n_statements, GError **error) { int i, j; const char *error_string; CoglBlendStringError detail = COGL_BLEND_STRING_ERROR_INVALID_ERROR; _COGL_GET_CONTEXT (ctx, 0); for (i = 0; i < n_statements; i++) for (j = 0; j < statements[i].function->argc; j++) { CoglBlendStringArgument *arg = &statements[i].args[j]; if (arg->source.is_zero) continue; if ((j == 0 && arg->source.info->type != COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) || (j == 1 && arg->source.info->type != COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR)) { error_string = "For blending you must always use SRC_COLOR " "for arg0 and DST_COLOR for arg1"; goto error; } } return TRUE; error: g_set_error (error, COGL_BLEND_STRING_ERROR, detail, "Invalid blend string: %s", error_string); return FALSE; } static gboolean validate_statements_for_context (CoglBlendStringStatement *statements, int n_statements, CoglBlendStringContext context, GError **error) { const char *error_string; if (n_statements == 1) { if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) { error_string = "You need to also give a blend statement for the RGB" "channels"; goto error; } else if (statements[0].mask == COGL_BLEND_STRING_CHANNEL_MASK_RGB) { error_string = "You need to also give a blend statement for the " "Alpha channel"; goto error; } } if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) return validate_blend_statements (statements, n_statements, error); else return validate_tex_combine_statements (statements, n_statements, error); error: g_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_INVALID_ERROR, "Invalid %s string: %s", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Invalid %s string: %s", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", error_string); } return FALSE; } static void print_argument (CoglBlendStringArgument *arg) { const char *mask_names[] = { "RGB", "A", "RGBA" }; g_print (" Arg:\n"); g_print (" is zero = %s\n", arg->source.is_zero ? "yes" : "no"); if (!arg->source.is_zero) { g_print (" color source = %s\n", arg->source.info->name); g_print (" one minus = %s\n", arg->source.one_minus ? "yes" : "no"); g_print (" mask = %s\n", mask_names[arg->source.mask]); g_print (" texture = %d\n", arg->source.texture); g_print ("\n"); g_print (" factor is_one = %s\n", arg->factor.is_one ? "yes" : "no"); g_print (" factor is_src_alpha_saturate = %s\n", arg->factor.is_src_alpha_saturate ? "yes" : "no"); g_print (" factor is_color = %s\n", arg->factor.is_color ? "yes" : "no"); if (arg->factor.is_color) { g_print (" factor color:is zero = %s\n", arg->factor.source.is_zero ? "yes" : "no"); g_print (" factor color:color source = %s\n", arg->factor.source.info->name); g_print (" factor color:one minus = %s\n", arg->factor.source.one_minus ? "yes" : "no"); g_print (" factor color:mask = %s\n", mask_names[arg->factor.source.mask]); g_print (" factor color:texture = %d\n", arg->factor.source.texture); } } } static void print_statement (int num, CoglBlendStringStatement *statement) { const char *mask_names[] = { "RGB", "A", "RGBA" }; int i; g_print ("Statement %d:\n", num); g_print (" Destination channel mask = %s\n", mask_names[statement->mask]); g_print (" Function = %s\n", statement->function->name); for (i = 0; i < statement->function->argc; i++) print_argument (&statement->args[i]); } static const CoglBlendStringFunctionInfo * get_function_info (const char *mark, const char *p, CoglBlendStringContext context) { size_t len = p - mark; CoglBlendStringFunctionInfo *functions; size_t array_len; int i; if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) { functions = blend_functions; array_len = G_N_ELEMENTS (blend_functions); } else { functions = tex_combine_functions; array_len = G_N_ELEMENTS (tex_combine_functions); } for (i = 0; i < array_len; i++) { if (len >= functions[i].name_len && strncmp (mark, functions[i].name, functions[i].name_len) == 0) return &functions[i]; } return NULL; } static const CoglBlendStringColorSourceInfo * get_color_src_info (const char *mark, const char *p, CoglBlendStringContext context) { size_t len = p - mark; CoglBlendStringColorSourceInfo *sources; size_t array_len; int i; if (context == COGL_BLEND_STRING_CONTEXT_BLENDING) { sources = blending_color_sources; array_len = G_N_ELEMENTS (blending_color_sources); } else { sources = tex_combine_color_sources; array_len = G_N_ELEMENTS (tex_combine_color_sources); } if (len >= 8 && strncmp (mark, "TEXTURE_", 8) == 0 && g_ascii_isdigit (mark[8])) { return &tex_combine_texture_n_color_source; } for (i = 0; i < array_len; i++) { if (len >= sources[i].name_len && strncmp (mark, sources[i].name, sources[i].name_len) == 0) return &sources[i]; } return NULL; } static gboolean is_symbol_char (const char c) { return (g_ascii_isalpha (c) || c == '_') ? TRUE : FALSE; } static gboolean is_alphanum_char (const char c) { return (g_ascii_isalnum (c) || c == '_') ? TRUE : FALSE; } static gboolean parse_argument (const char *string, /* original user string */ const char **ret_p, /* start of argument IN:OUT */ const CoglBlendStringStatement *statement, int current_arg, CoglBlendStringArgument *arg, /* OUT */ CoglBlendStringContext context, GError **error) { const char *p = *ret_p; const char *mark = NULL; const char *error_string = NULL; ParserArgState state = PARSER_ARG_STATE_START; gboolean parsing_factor = FALSE; gboolean implicit_factor_brace = FALSE; arg->source.is_zero = FALSE; arg->source.info = NULL; arg->source.texture = 0; arg->source.one_minus = FALSE; arg->source.mask = statement->mask; arg->factor.is_one = FALSE; arg->factor.is_color = FALSE; arg->factor.is_src_alpha_saturate = FALSE; arg->factor.source.is_zero = FALSE; arg->factor.source.info = NULL; arg->factor.source.texture = 0; arg->factor.source.one_minus = FALSE; arg->factor.source.mask = statement->mask; do { if (g_ascii_isspace (*p)) continue; if (*p == '\0') { error_string = "Unexpected end of string while parsing argument"; goto error; } switch (state) { case PARSER_ARG_STATE_START: if (*p == '1') state = PARSER_ARG_STATE_EXPECT_MINUS; else if (*p == '0') { arg->source.is_zero = TRUE; state = PARSER_ARG_STATE_EXPECT_END; } else { p--; /* backtrack */ state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; } continue; case PARSER_ARG_STATE_EXPECT_MINUS: if (*p != '-') { error_string = "expected a '-' following the 1"; goto error; } arg->source.one_minus = TRUE; state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; continue; case PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME: if (!is_symbol_char (*p)) { error_string = "expected a color source name"; goto error; } state = PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME; mark = p; if (parsing_factor) arg->factor.is_color = TRUE; G_GNUC_FALLTHROUGH; case PARSER_ARG_STATE_SCRAPING_COLOR_SRC_NAME: if (!is_symbol_char (*p)) { CoglBlendStringColorSource *source = parsing_factor ? &arg->factor.source : &arg->source; source->info = get_color_src_info (mark, p, context); if (!source->info) { error_string = "Unknown color source name"; goto error; } if (source->info->type == COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N) { char *endp; source->texture = strtoul (&mark[strlen ("TEXTURE_")], &endp, 10); if (mark == endp) { error_string = "invalid texture number given with " "TEXTURE_N color source"; goto error; } p = endp; } state = PARSER_ARG_STATE_MAYBE_COLOR_MASK; } else continue; G_GNUC_FALLTHROUGH; case PARSER_ARG_STATE_MAYBE_COLOR_MASK: if (*p != '[') { p--; /* backtrack */ if (!parsing_factor) state = PARSER_ARG_STATE_MAYBE_MULT; else state = PARSER_ARG_STATE_EXPECT_END; continue; } state = PARSER_ARG_STATE_SCRAPING_MASK; mark = p; G_GNUC_FALLTHROUGH; case PARSER_ARG_STATE_SCRAPING_MASK: if (*p == ']') { size_t len = p - mark; CoglBlendStringColorSource *source = parsing_factor ? &arg->factor.source : &arg->source; if (len == 5 && strncmp (mark, "[RGBA", len) == 0) { if (statement->mask != COGL_BLEND_STRING_CHANNEL_MASK_RGBA) { error_string = "You can't use an RGBA color mask if the " "statement hasn't also got an RGBA= mask"; goto error; } source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; } else if (len == 4 && strncmp (mark, "[RGB", len) == 0) source->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; else if (len == 2 && strncmp (mark, "[A", len) == 0) source->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; else { error_string = "Expected a channel mask of [RGBA]" "[RGB] or [A]"; goto error; } if (parsing_factor) state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; else state = PARSER_ARG_STATE_MAYBE_MULT; } continue; case PARSER_ARG_STATE_EXPECT_OPEN_PAREN: if (*p != '(') { if (is_alphanum_char (*p)) { p--; /* compensate for implicit brace and ensure this * char gets considered part of the blend factor */ implicit_factor_brace = TRUE; } else { error_string = "Expected '(' around blend factor or alpha " "numeric character for blend factor name"; goto error; } } else implicit_factor_brace = FALSE; parsing_factor = TRUE; state = PARSER_ARG_STATE_EXPECT_FACTOR; continue; case PARSER_ARG_STATE_EXPECT_FACTOR: if (*p == '1') state = PARSER_ARG_STATE_MAYBE_MINUS; else if (*p == '0') { arg->source.is_zero = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } else { state = PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE; mark = p; } continue; case PARSER_ARG_STATE_MAYBE_SRC_ALPHA_SATURATE: if (!is_symbol_char (*p)) { size_t len = p - mark; if (len >= strlen ("SRC_ALPHA_SATURATE") && strncmp (mark, "SRC_ALPHA_SATURATE", len) == 0) { arg->factor.is_src_alpha_saturate = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } else { state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; p = mark - 1; /* backtrack */ } } continue; case PARSER_ARG_STATE_MAYBE_MINUS: if (*p == '-') { if (implicit_factor_brace) { error_string = "Expected ( ) braces around blend factor with " "a subtraction"; goto error; } arg->factor.source.one_minus = TRUE; state = PARSER_ARG_STATE_EXPECT_COLOR_SRC_NAME; } else { arg->factor.is_one = TRUE; state = PARSER_ARG_STATE_EXPECT_CLOSE_PAREN; } continue; case PARSER_ARG_STATE_EXPECT_CLOSE_PAREN: if (implicit_factor_brace) { p--; state = PARSER_ARG_STATE_EXPECT_END; continue; } if (*p != ')') { error_string = "Expected closing parenthesis after blend factor"; goto error; } state = PARSER_ARG_STATE_EXPECT_END; continue; case PARSER_ARG_STATE_MAYBE_MULT: if (*p == '*') { state = PARSER_ARG_STATE_EXPECT_OPEN_PAREN; continue; } arg->factor.is_one = TRUE; state = PARSER_ARG_STATE_EXPECT_END; G_GNUC_FALLTHROUGH; case PARSER_ARG_STATE_EXPECT_END: if (*p != ',' && *p != ')') { error_string = "expected , or )"; goto error; } *ret_p = p - 1; return TRUE; } } while (p++); error: { int offset = p - string; g_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, "Syntax error for argument %d at offset %d: %s", current_arg, offset, error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Syntax error for argument %d at offset %d: %s", current_arg, offset, error_string); } return FALSE; } } int _cogl_blend_string_compile (const char *string, CoglBlendStringContext context, CoglBlendStringStatement *statements, GError **error) { const char *p = string; const char *mark = NULL; const char *error_string; ParserState state = PARSER_STATE_EXPECT_DEST_CHANNELS; CoglBlendStringStatement *statement = statements; int current_statement = 0; int current_arg = 0; int remaining_argc = 0; #if 0 COGL_DEBUG_SET_FLAG (COGL_DEBUG_BLEND_STRINGS); #endif if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { COGL_NOTE (BLEND_STRINGS, "Compiling %s string:\n%s\n", context == COGL_BLEND_STRING_CONTEXT_BLENDING ? "blend" : "texture combine", string); } do { if (g_ascii_isspace (*p)) continue; if (*p == '\0') { switch (state) { case PARSER_STATE_EXPECT_DEST_CHANNELS: if (current_statement != 0) goto finished; error_string = "Empty statement"; goto error; case PARSER_STATE_SCRAPING_DEST_CHANNELS: error_string = "Expected an '=' following the destination " "channel mask"; goto error; case PARSER_STATE_EXPECT_FUNCTION_NAME: error_string = "Expected a function name"; goto error; case PARSER_STATE_SCRAPING_FUNCTION_NAME: error_string = "Expected parenthesis after the function name"; goto error; case PARSER_STATE_EXPECT_ARG_START: error_string = "Expected to find the start of an argument"; goto error; case PARSER_STATE_EXPECT_STATEMENT_END: error_string = "Expected closing parenthesis for statement"; goto error; } } switch (state) { case PARSER_STATE_EXPECT_DEST_CHANNELS: mark = p; state = PARSER_STATE_SCRAPING_DEST_CHANNELS; G_GNUC_FALLTHROUGH; case PARSER_STATE_SCRAPING_DEST_CHANNELS: if (*p != '=') continue; if (strncmp (mark, "RGBA", 4) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGBA; else if (strncmp (mark, "RGB", 3) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_RGB; else if (strncmp (mark, "A", 1) == 0) statement->mask = COGL_BLEND_STRING_CHANNEL_MASK_ALPHA; else { error_string = "Unknown destination channel mask; " "expected RGBA=, RGB= or A="; goto error; } state = PARSER_STATE_EXPECT_FUNCTION_NAME; continue; case PARSER_STATE_EXPECT_FUNCTION_NAME: mark = p; state = PARSER_STATE_SCRAPING_FUNCTION_NAME; G_GNUC_FALLTHROUGH; case PARSER_STATE_SCRAPING_FUNCTION_NAME: if (*p != '(') { if (!is_alphanum_char (*p)) { error_string = "non alpha numeric character in function" "name"; goto error; } continue; } statement->function = get_function_info (mark, p, context); if (!statement->function) { error_string = "Unknown function name"; goto error; } remaining_argc = statement->function->argc; current_arg = 0; state = PARSER_STATE_EXPECT_ARG_START; G_GNUC_FALLTHROUGH; case PARSER_STATE_EXPECT_ARG_START: if (*p != '(' && *p != ',') continue; if (remaining_argc) { p++; /* parse_argument expects to see the first char of the arg */ if (!parse_argument (string, &p, statement, current_arg, &statement->args[current_arg], context, error)) return 0; current_arg++; remaining_argc--; } if (!remaining_argc) state = PARSER_STATE_EXPECT_STATEMENT_END; continue; case PARSER_STATE_EXPECT_STATEMENT_END: if (*p != ')') { error_string = "Expected end of statement"; goto error; } state = PARSER_STATE_EXPECT_DEST_CHANNELS; if (current_statement++ == 1) goto finished; statement = &statements[current_statement]; } } while (p++); finished: if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { if (current_statement > 0) print_statement (0, &statements[0]); if (current_statement > 1) print_statement (1, &statements[1]); } if (!validate_statements_for_context (statements, current_statement, context, error)) return 0; return current_statement; error: { int offset = p - string; g_set_error (error, COGL_BLEND_STRING_ERROR, COGL_BLEND_STRING_ERROR_PARSE_ERROR, "Syntax error at offset %d: %s", offset, error_string); if (COGL_DEBUG_ENABLED (COGL_DEBUG_BLEND_STRINGS)) { g_debug ("Syntax error at offset %d: %s", offset, error_string); } return 0; } } /* * INTERNAL TESTING CODE ... */ struct _TestString { const char *string; CoglBlendStringContext context; }; /* FIXME: this should probably be moved to a unit test */ int _cogl_blend_string_test (void); int _cogl_blend_string_test (void) { struct _TestString strings[] = { {" A = MODULATE ( TEXTURE[RGB], PREVIOUS[A], PREVIOUS[A] ) ", COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, {" RGB = MODULATE ( TEXTURE[RGB], PREVIOUS[A] ) ", COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, {"A=ADD(TEXTURE[A],PREVIOUS[RGB])", COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE }, {"RGBA = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))", COGL_BLEND_STRING_CONTEXT_BLENDING }, {"RGB = ADD(SRC_COLOR, DST_COLOR*(0))", COGL_BLEND_STRING_CONTEXT_BLENDING }, {"RGB = ADD(SRC_COLOR, 0)", COGL_BLEND_STRING_CONTEXT_BLENDING }, {"RGB = ADD()", COGL_BLEND_STRING_CONTEXT_BLENDING }, {"RGB = ADD(SRC_COLOR, 0, DST_COLOR)", COGL_BLEND_STRING_CONTEXT_BLENDING }, {NULL} }; int i; GError *error = NULL; for (i = 0; strings[i].string; i++) { CoglBlendStringStatement statements[2]; int count = _cogl_blend_string_compile (strings[i].string, strings[i].context, statements, &error); if (!count) { g_print ("Failed to parse string:\n%s\n%s\n", strings[i].string, error->message); g_error_free (error); error = NULL; continue; } g_print ("Original:\n"); g_print ("%s\n", strings[i].string); if (count > 0) print_statement (0, &statements[0]); if (count > 1) print_statement (1, &statements[1]); } return 0; } muffin-6.4.1/cogl/cogl/cogl-xlib-renderer-private.h0000664000175000017500000000646614723361714021133 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_RENDERER_XLIB_PRIVATE_H #define __COGL_RENDERER_XLIB_PRIVATE_H #include #include "cogl-object-private.h" #include "cogl-xlib-private.h" #include "cogl-x11-renderer-private.h" #include "cogl-context.h" #include "cogl-output.h" typedef struct _CoglXlibRenderer { CoglX11Renderer _parent; Display *xdpy; /* Current top of the XError trap state stack. The actual memory for these is expected to be allocated on the stack by the caller */ CoglXlibTrapState *trap_state; unsigned long outputs_update_serial; XVisualInfo *xvisinfo; } CoglXlibRenderer; gboolean _cogl_xlib_renderer_connect (CoglRenderer *renderer, GError **error); void _cogl_xlib_renderer_disconnect (CoglRenderer *renderer); /* * cogl_xlib_renderer_trap_errors: * @state: A temporary place to store data for the trap. * * Traps every X error until _cogl_xlib_renderer_untrap_errors() * called. You should allocate an uninitialised CoglXlibTrapState * struct on the stack to pass to this function. The same pointer * should later be passed to _cogl_xlib_renderer_untrap_errors(). * * Calls to _cogl_xlib_renderer_trap_errors() can be nested as long as * _cogl_xlib_renderer_untrap_errors() is called with the * corresponding state pointers in reverse order. */ void _cogl_xlib_renderer_trap_errors (CoglRenderer *renderer, CoglXlibTrapState *state); /* * cogl_xlib_renderer_untrap_errors: * @state: The state that was passed to _cogl_xlib_renderer_trap_errors(). * * Removes the X error trap and returns the current status. * * Return value: the trapped error code, or 0 for success */ int _cogl_xlib_renderer_untrap_errors (CoglRenderer *renderer, CoglXlibTrapState *state); CoglXlibRenderer * _cogl_xlib_renderer_get_data (CoglRenderer *renderer); CoglOutput * _cogl_xlib_renderer_output_for_rectangle (CoglRenderer *renderer, int x, int y, int width, int height); #endif /* __COGL_RENDERER_XLIB_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-color.h0000664000175000017500000003176714723361714016041 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_COLOR_H__ #define __COGL_COLOR_H__ /** * SECTION:cogl-color * @short_description: A generic color definition * * #CoglColor is a simple structure holding the definition of a color such * that it can be efficiently used by GL * * Since: 1.0 */ #include #include #include G_BEGIN_DECLS /** * cogl_color_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_color_get_gtype (void); /** * cogl_color_new: * * Creates a new (empty) color * * Return value: a newly-allocated #CoglColor. Use cogl_color_free() * to free the allocated resources * * Since: 1.0 */ COGL_EXPORT CoglColor * cogl_color_new (void); /** * cogl_color_copy: * @color: the color to copy * * Creates a copy of @color * * Return value: a newly-allocated #CoglColor. Use cogl_color_free() * to free the allocate resources * * Since: 1.0 */ COGL_EXPORT CoglColor * cogl_color_copy (const CoglColor *color); /** * cogl_color_free: * @color: the color to free * * Frees the resources allocated by cogl_color_new() and cogl_color_copy() * * Since: 1.0 */ COGL_EXPORT void cogl_color_free (CoglColor *color); /** * cogl_color_init_from_4ub: * @color: A pointer to a #CoglColor to initialize * @red: value of the red channel, between 0 and 255 * @green: value of the green channel, between 0 and 255 * @blue: value of the blue channel, between 0 and 255 * @alpha: value of the alpha channel, between 0 and 255 * * Sets the values of the passed channels into a #CoglColor. * * Since: 1.4 */ COGL_EXPORT void cogl_color_init_from_4ub (CoglColor *color, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); /** * cogl_color_init_from_4f: * @color: A pointer to a #CoglColor to initialize * @red: value of the red channel, between 0 and 1.0 * @green: value of the green channel, between 0 and 1.0 * @blue: value of the blue channel, between 0 and 1.0 * @alpha: value of the alpha channel, between 0 and 1.0 * * Sets the values of the passed channels into a #CoglColor * * Since: 1.4 */ COGL_EXPORT void cogl_color_init_from_4f (CoglColor *color, float red, float green, float blue, float alpha); /** * cogl_color_init_from_4fv: * @color: A pointer to a #CoglColor to initialize * @color_array: a pointer to an array of 4 float color components * * Sets the values of the passed channels into a #CoglColor * * Since: 1.4 */ COGL_EXPORT void cogl_color_init_from_4fv (CoglColor *color, const float *color_array); /** * cogl_color_get_red_byte: * @color: a #CoglColor * * Retrieves the red channel of @color as a byte value * between 0 and 255 * * Return value: the red channel of the passed color * * Since: 1.0 */ COGL_EXPORT unsigned char cogl_color_get_red_byte (const CoglColor *color); /** * cogl_color_get_green_byte: * @color: a #CoglColor * * Retrieves the green channel of @color as a byte value * between 0 and 255 * * Return value: the green channel of the passed color * * Since: 1.0 */ COGL_EXPORT unsigned char cogl_color_get_green_byte (const CoglColor *color); /** * cogl_color_get_blue_byte: * @color: a #CoglColor * * Retrieves the blue channel of @color as a byte value * between 0 and 255 * * Return value: the blue channel of the passed color * * Since: 1.0 */ COGL_EXPORT unsigned char cogl_color_get_blue_byte (const CoglColor *color); /** * cogl_color_get_alpha_byte: * @color: a #CoglColor * * Retrieves the alpha channel of @color as a byte value * between 0 and 255 * * Return value: the alpha channel of the passed color * * Since: 1.0 */ COGL_EXPORT unsigned char cogl_color_get_alpha_byte (const CoglColor *color); /** * cogl_color_get_red_float: * @color: a #CoglColor * * Retrieves the red channel of @color as a floating point * value between 0.0 and 1.0 * * Return value: the red channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_red_float (const CoglColor *color); /** * cogl_color_get_green_float: * @color: a #CoglColor * * Retrieves the green channel of @color as a floating point * value between 0.0 and 1.0 * * Return value: the green channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_green_float (const CoglColor *color); /** * cogl_color_get_blue_float: * @color: a #CoglColor * * Retrieves the blue channel of @color as a floating point * value between 0.0 and 1.0 * * Return value: the blue channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_blue_float (const CoglColor *color); /** * cogl_color_get_alpha_float: * @color: a #CoglColor * * Retrieves the alpha channel of @color as a floating point * value between 0.0 and 1.0 * * Return value: the alpha channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_alpha_float (const CoglColor *color); /** * cogl_color_get_red: * @color: a #CoglColor * * Retrieves the red channel of @color as a fixed point * value between 0 and 1.0. * * Return value: the red channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_red (const CoglColor *color); /** * cogl_color_get_green: * @color: a #CoglColor * * Retrieves the green channel of @color as a fixed point * value between 0 and 1.0. * * Return value: the green channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_green (const CoglColor *color); /** * cogl_color_get_blue: * @color: a #CoglColor * * Retrieves the blue channel of @color as a fixed point * value between 0 and 1.0. * * Return value: the blue channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_blue (const CoglColor *color); /** * cogl_color_get_alpha: * @color: a #CoglColor * * Retrieves the alpha channel of @color as a fixed point * value between 0 and 1.0. * * Return value: the alpha channel of the passed color * * Since: 1.0 */ COGL_EXPORT float cogl_color_get_alpha (const CoglColor *color); /** * cogl_color_set_red_byte: * @color: a #CoglColor * @red: a byte value between 0 and 255 * * Sets the red channel of @color to @red. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_red_byte (CoglColor *color, unsigned char red); /** * cogl_color_set_green_byte: * @color: a #CoglColor * @green: a byte value between 0 and 255 * * Sets the green channel of @color to @green. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_green_byte (CoglColor *color, unsigned char green); /** * cogl_color_set_blue_byte: * @color: a #CoglColor * @blue: a byte value between 0 and 255 * * Sets the blue channel of @color to @blue. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_blue_byte (CoglColor *color, unsigned char blue); /** * cogl_color_set_alpha_byte: * @color: a #CoglColor * @alpha: a byte value between 0 and 255 * * Sets the alpha channel of @color to @alpha. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_alpha_byte (CoglColor *color, unsigned char alpha); /** * cogl_color_set_red_float: * @color: a #CoglColor * @red: a float value between 0.0f and 1.0f * * Sets the red channel of @color to @red. * * since: 1.4 */ COGL_EXPORT void cogl_color_set_red_float (CoglColor *color, float red); /** * cogl_color_set_green_float: * @color: a #CoglColor * @green: a float value between 0.0f and 1.0f * * Sets the green channel of @color to @green. * * since: 1.4 */ COGL_EXPORT void cogl_color_set_green_float (CoglColor *color, float green); /** * cogl_color_set_blue_float: * @color: a #CoglColor * @blue: a float value between 0.0f and 1.0f * * Sets the blue channel of @color to @blue. * * since: 1.4 */ COGL_EXPORT void cogl_color_set_blue_float (CoglColor *color, float blue); /** * cogl_color_set_alpha_float: * @color: a #CoglColor * @alpha: a float value between 0.0f and 1.0f * * Sets the alpha channel of @color to @alpha. * * since: 1.4 */ COGL_EXPORT void cogl_color_set_alpha_float (CoglColor *color, float alpha); /** * cogl_color_set_red: * @color: a #CoglColor * @red: a float value between 0.0f and 1.0f * * Sets the red channel of @color to @red. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_red (CoglColor *color, float red); /** * cogl_color_set_green: * @color: a #CoglColor * @green: a float value between 0.0f and 1.0f * * Sets the green channel of @color to @green. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_green (CoglColor *color, float green); /** * cogl_color_set_blue: * @color: a #CoglColor * @blue: a float value between 0.0f and 1.0f * * Sets the blue channel of @color to @blue. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_blue (CoglColor *color, float blue); /** * cogl_color_set_alpha: * @color: a #CoglColor * @alpha: a float value between 0.0f and 1.0f * * Sets the alpha channel of @color to @alpha. * * Since: 1.4 */ COGL_EXPORT void cogl_color_set_alpha (CoglColor *color, float alpha); /** * cogl_color_premultiply: * @color: the color to premultiply * * Converts a non-premultiplied color to a pre-multiplied color. For * example, semi-transparent red is (1.0, 0, 0, 0.5) when non-premultiplied * and (0.5, 0, 0, 0.5) when premultiplied. * * Since: 1.0 */ COGL_EXPORT void cogl_color_premultiply (CoglColor *color); /** * cogl_color_unpremultiply: * @color: the color to unpremultiply * * Converts a pre-multiplied color to a non-premultiplied color. For * example, semi-transparent red is (0.5, 0, 0, 0.5) when premultiplied * and (1.0, 0, 0, 0.5) when non-premultiplied. * * Since: 1.4 */ COGL_EXPORT void cogl_color_unpremultiply (CoglColor *color); /** * cogl_color_equal: * @v1: a #CoglColor * @v2: a #CoglColor * * Compares two #CoglColors and checks if they are the same. * * This function can be passed to g_hash_table_new() as the @key_equal_func * parameter, when using #CoglColors as keys in a #GHashTable. * * Return value: %TRUE if the two colors are the same. * * Since: 1.0 */ COGL_EXPORT gboolean cogl_color_equal (const void *v1, const void *v2); /** * cogl_color_to_hsl: * @color: a #CoglColor * @hue: (out): return location for the hue value or %NULL * @saturation: (out): return location for the saturation value or %NULL * @luminance: (out): return location for the luminance value or %NULL * * Converts @color to the HLS format. * * The @hue value is in the 0 .. 360 range. The @luminance and * @saturation values are in the 0 .. 1 range. * * Since: 1.16 */ COGL_EXPORT void cogl_color_to_hsl (const CoglColor *color, float *hue, float *saturation, float *luminance); /** * cogl_color_init_from_hsl: * @color: (out): return location for a #CoglColor * @hue: hue value, in the 0 .. 360 range * @saturation: saturation value, in the 0 .. 1 range * @luminance: luminance value, in the 0 .. 1 range * * Converts a color expressed in HLS (hue, luminance and saturation) * values into a #CoglColor. * * Since: 1.16 */ COGL_EXPORT void cogl_color_init_from_hsl (CoglColor *color, float hue, float saturation, float luminance); G_END_DECLS #endif /* __COGL_COLOR_H__ */ muffin-6.4.1/cogl/cogl/cogl-egl-defines.h.in0000664000175000017500000000252614723361714017501 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_EGL_DEFINES_H__ #define __COGL_EGL_DEFINES_H__ #ifdef COGL_HAS_EGL_SUPPORT @COGL_EGL_INCLUDES@ #endif /* COGL_HAS_EGL_SUPPORT */ #endif muffin-6.4.1/cogl/cogl/cogl-sub-texture.c0000664000175000017500000003457114723361714017201 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-sub-texture-private.h" #include "cogl-sub-texture.h" #include "cogl-context-private.h" #include "cogl-object.h" #include "cogl-texture-driver.h" #include "cogl-texture-2d.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include #include static void _cogl_sub_texture_free (CoglSubTexture *sub_tex); COGL_TEXTURE_DEFINE (SubTexture, sub_texture); COGL_GTYPE_DEFINE_CLASS (SubTexture, sub_texture); static const CoglTextureVtable cogl_sub_texture_vtable; static void _cogl_sub_texture_unmap_quad (CoglSubTexture *sub_tex, float *coords) { CoglTexture *tex = COGL_TEXTURE (sub_tex); float width = cogl_texture_get_width (sub_tex->full_texture); float height = cogl_texture_get_height (sub_tex->full_texture); coords[0] = (coords[0] * width - sub_tex->sub_x) / tex->width; coords[1] = (coords[1] * height - sub_tex->sub_y) / tex->height; coords[2] = (coords[2] * width - sub_tex->sub_x) / tex->width; coords[3] = (coords[3] * height - sub_tex->sub_y) / tex->height; } static void _cogl_sub_texture_map_quad (CoglSubTexture *sub_tex, float *coords) { CoglTexture *tex = COGL_TEXTURE (sub_tex); float width = cogl_texture_get_width (sub_tex->full_texture); float height = cogl_texture_get_height (sub_tex->full_texture); coords[0] = (coords[0] * tex->width + sub_tex->sub_x) / width; coords[1] = (coords[1] * tex->height + sub_tex->sub_y) / height; coords[2] = (coords[2] * tex->width + sub_tex->sub_x) / width; coords[3] = (coords[3] * tex->height + sub_tex->sub_y) / height; } typedef struct _CoglSubTextureForeachData { CoglSubTexture *sub_tex; CoglMetaTextureCallback callback; void *user_data; } CoglSubTextureForeachData; static void unmap_coords_cb (CoglTexture *slice_texture, const float *slice_texture_coords, const float *meta_coords, void *user_data) { CoglSubTextureForeachData *data = user_data; float unmapped_coords[4]; memcpy (unmapped_coords, meta_coords, sizeof (unmapped_coords)); _cogl_sub_texture_unmap_quad (data->sub_tex, unmapped_coords); data->callback (slice_texture, slice_texture_coords, unmapped_coords, data->user_data); } static void _cogl_sub_texture_foreach_sub_texture_in_region ( CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); CoglTexture *full_texture = sub_tex->full_texture; float mapped_coords[4] = { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; float virtual_coords[4] = { virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2}; /* map the virtual coordinates to ->full_texture coordinates */ _cogl_sub_texture_map_quad (sub_tex, mapped_coords); /* TODO: Add something like cogl_is_low_level_texture() */ if (cogl_is_texture_2d (full_texture)) { callback (sub_tex->full_texture, mapped_coords, virtual_coords, user_data); } else { CoglSubTextureForeachData data; data.sub_tex = sub_tex; data.callback = callback; data.user_data = user_data; cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (full_texture), mapped_coords[0], mapped_coords[1], mapped_coords[2], mapped_coords[3], COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, unmap_coords_cb, &data); } } static void _cogl_sub_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); _cogl_texture_gl_flush_legacy_texobj_wrap_modes (sub_tex->full_texture, wrap_mode_s, wrap_mode_t); } static void _cogl_sub_texture_free (CoglSubTexture *sub_tex) { cogl_object_unref (sub_tex->next_texture); cogl_object_unref (sub_tex->full_texture); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (sub_tex)); } CoglSubTexture * cogl_sub_texture_new (CoglContext *ctx, CoglTexture *next_texture, int sub_x, int sub_y, int sub_width, int sub_height) { CoglTexture *full_texture; CoglSubTexture *sub_tex; CoglTexture *tex; unsigned int next_width, next_height; next_width = cogl_texture_get_width (next_texture); next_height = cogl_texture_get_height (next_texture); /* The region must specify a non-zero subset of the full texture */ g_return_val_if_fail (sub_x >= 0 && sub_y >= 0, NULL); g_return_val_if_fail (sub_width > 0 && sub_height > 0, NULL); g_return_val_if_fail (sub_x + sub_width <= next_width, NULL); g_return_val_if_fail (sub_y + sub_height <= next_height, NULL); sub_tex = g_new (CoglSubTexture, 1); tex = COGL_TEXTURE (sub_tex); _cogl_texture_init (tex, ctx, sub_width, sub_height, _cogl_texture_get_format (next_texture), NULL, /* no loader */ &cogl_sub_texture_vtable); /* If the next texture is also a sub texture we can avoid one level of indirection by referencing the full texture of that texture instead. */ if (cogl_is_sub_texture (next_texture)) { CoglSubTexture *other_sub_tex = COGL_SUB_TEXTURE (next_texture); full_texture = other_sub_tex->full_texture; sub_x += other_sub_tex->sub_x; sub_y += other_sub_tex->sub_y; } else full_texture = next_texture; sub_tex->next_texture = cogl_object_ref (next_texture); sub_tex->full_texture = cogl_object_ref (full_texture); sub_tex->sub_x = sub_x; sub_tex->sub_y = sub_y; return _cogl_sub_texture_object_new (sub_tex); } static gboolean _cogl_sub_texture_allocate (CoglTexture *tex, GError **error) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); gboolean status = cogl_texture_allocate (sub_tex->full_texture, error); _cogl_texture_set_allocated (tex, _cogl_texture_get_format (sub_tex->full_texture), tex->width, tex->height); return status; } CoglTexture * cogl_sub_texture_get_parent (CoglSubTexture *sub_texture) { return sub_texture->next_texture; } static int _cogl_sub_texture_get_max_waste (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return cogl_texture_get_max_waste (sub_tex->full_texture); } static gboolean _cogl_sub_texture_is_sliced (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return cogl_texture_is_sliced (sub_tex->full_texture); } static gboolean _cogl_sub_texture_can_hardware_repeat (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); /* We can hardware repeat if the subtexture actually represents all of the of the full texture */ return (tex->width == cogl_texture_get_width (sub_tex->full_texture) && tex->height == cogl_texture_get_height (sub_tex->full_texture) && _cogl_texture_can_hardware_repeat (sub_tex->full_texture)); } static void _cogl_sub_texture_transform_coords_to_gl (CoglTexture *tex, float *s, float *t) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); /* This won't work if the sub texture is not the size of the full texture and the coordinates are outside the range [0,1] */ *s = ((*s * tex->width + sub_tex->sub_x) / cogl_texture_get_width (sub_tex->full_texture)); *t = ((*t * tex->height + sub_tex->sub_y) / cogl_texture_get_height (sub_tex->full_texture)); _cogl_texture_transform_coords_to_gl (sub_tex->full_texture, s, t); } static CoglTransformResult _cogl_sub_texture_transform_quad_coords_to_gl (CoglTexture *tex, float *coords) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); int i; /* We can't support repeating with this method. In this case cogl-primitives will resort to manual repeating */ for (i = 0; i < 4; i++) if (coords[i] < 0.0f || coords[i] > 1.0f) return COGL_TRANSFORM_SOFTWARE_REPEAT; _cogl_sub_texture_map_quad (sub_tex, coords); return _cogl_texture_transform_quad_coords_to_gl (sub_tex->full_texture, coords); } static gboolean _cogl_sub_texture_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return cogl_texture_get_gl_texture (sub_tex->full_texture, out_gl_handle, out_gl_target); } static void _cogl_sub_texture_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); _cogl_texture_gl_flush_legacy_texobj_filters (sub_tex->full_texture, min_filter, mag_filter); } static void _cogl_sub_texture_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); _cogl_texture_pre_paint (sub_tex->full_texture, flags); } static void _cogl_sub_texture_ensure_non_quad_rendering (CoglTexture *tex) { } static gboolean _cogl_sub_texture_set_region (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, int level, CoglBitmap *bmp, GError **error) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); if (level != 0) { int full_width = cogl_texture_get_width (sub_tex->full_texture); int full_height = cogl_texture_get_width (sub_tex->full_texture); g_return_val_if_fail (sub_tex->sub_x == 0 && cogl_texture_get_width (tex) == full_width, FALSE); g_return_val_if_fail (sub_tex->sub_y == 0 && cogl_texture_get_height (tex) == full_height, FALSE); } return _cogl_texture_set_region_from_bitmap (sub_tex->full_texture, src_x, src_y, dst_width, dst_height, bmp, dst_x + sub_tex->sub_x, dst_y + sub_tex->sub_y, level, error); } static gboolean _cogl_sub_texture_is_get_data_supported (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return cogl_texture_is_get_data_supported (sub_tex->full_texture); } static CoglPixelFormat _cogl_sub_texture_get_format (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return _cogl_texture_get_format (sub_tex->full_texture); } static GLenum _cogl_sub_texture_get_gl_format (CoglTexture *tex) { CoglSubTexture *sub_tex = COGL_SUB_TEXTURE (tex); return _cogl_texture_gl_get_format (sub_tex->full_texture); } static const CoglTextureVtable cogl_sub_texture_vtable = { FALSE, /* not primitive */ _cogl_sub_texture_allocate, _cogl_sub_texture_set_region, _cogl_sub_texture_is_get_data_supported, NULL, /* get_data */ _cogl_sub_texture_foreach_sub_texture_in_region, _cogl_sub_texture_get_max_waste, _cogl_sub_texture_is_sliced, _cogl_sub_texture_can_hardware_repeat, _cogl_sub_texture_transform_coords_to_gl, _cogl_sub_texture_transform_quad_coords_to_gl, _cogl_sub_texture_get_gl_texture, _cogl_sub_texture_gl_flush_legacy_texobj_filters, _cogl_sub_texture_pre_paint, _cogl_sub_texture_ensure_non_quad_rendering, _cogl_sub_texture_gl_flush_legacy_texobj_wrap_modes, _cogl_sub_texture_get_format, _cogl_sub_texture_get_gl_format, NULL /* set_auto_mipmap */ }; muffin-6.4.1/cogl/cogl/cogl-atlas.c0000664000175000017500000005622714723361714016020 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-atlas.h" #include "cogl-rectangle-map.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-texture-2d-sliced.h" #include "cogl-texture-driver.h" #include "cogl-debug.h" #include "cogl-framebuffer-private.h" #include "cogl-blit.h" #include "cogl-private.h" #include static void _cogl_atlas_free (CoglAtlas *atlas); COGL_OBJECT_INTERNAL_DEFINE (Atlas, atlas); CoglAtlas * _cogl_atlas_new (CoglPixelFormat texture_format, CoglAtlasFlags flags, CoglAtlasUpdatePositionCallback update_position_cb) { CoglAtlas *atlas = g_new (CoglAtlas, 1); atlas->update_position_cb = update_position_cb; atlas->map = NULL; atlas->texture = NULL; atlas->flags = flags; atlas->texture_format = texture_format; g_hook_list_init (&atlas->pre_reorganize_callbacks, sizeof (GHook)); g_hook_list_init (&atlas->post_reorganize_callbacks, sizeof (GHook)); return _cogl_atlas_object_new (atlas); } static void _cogl_atlas_free (CoglAtlas *atlas) { COGL_NOTE (ATLAS, "%p: Atlas destroyed", atlas); if (atlas->texture) cogl_object_unref (atlas->texture); if (atlas->map) _cogl_rectangle_map_free (atlas->map); g_hook_list_clear (&atlas->pre_reorganize_callbacks); g_hook_list_clear (&atlas->post_reorganize_callbacks); g_free (atlas); } typedef struct _CoglAtlasRepositionData { /* The current user data for this texture */ void *user_data; /* The old and new positions of the texture */ CoglRectangleMapEntry old_position; CoglRectangleMapEntry new_position; } CoglAtlasRepositionData; static void _cogl_atlas_migrate (CoglAtlas *atlas, unsigned int n_textures, CoglAtlasRepositionData *textures, CoglTexture *old_texture, CoglTexture *new_texture, void *skip_user_data) { unsigned int i; CoglBlitData blit_data; /* If the 'disable migrate' flag is set then we won't actually copy the textures to their new location. Instead we'll just invoke the callback to update the position */ if ((atlas->flags & COGL_ATLAS_DISABLE_MIGRATION)) for (i = 0; i < n_textures; i++) /* Update the texture position */ atlas->update_position_cb (textures[i].user_data, new_texture, &textures[i].new_position); else { _cogl_blit_begin (&blit_data, new_texture, old_texture); for (i = 0; i < n_textures; i++) { /* Skip the texture that is being added because it doesn't contain any data yet */ if (textures[i].user_data != skip_user_data) _cogl_blit (&blit_data, textures[i].old_position.x, textures[i].old_position.y, textures[i].new_position.x, textures[i].new_position.y, textures[i].new_position.width, textures[i].new_position.height); /* Update the texture position */ atlas->update_position_cb (textures[i].user_data, new_texture, &textures[i].new_position); } _cogl_blit_end (&blit_data); } } typedef struct _CoglAtlasGetRectanglesData { CoglAtlasRepositionData *textures; /* Number of textures found so far */ unsigned int n_textures; } CoglAtlasGetRectanglesData; static void _cogl_atlas_get_rectangles_cb (const CoglRectangleMapEntry *rectangle, void *rect_data, void *user_data) { CoglAtlasGetRectanglesData *data = user_data; data->textures[data->n_textures].old_position = *rectangle; data->textures[data->n_textures++].user_data = rect_data; } static void _cogl_atlas_get_next_size (unsigned int *map_width, unsigned int *map_height) { /* Double the size of the texture by increasing whichever dimension is smaller */ if (*map_width < *map_height) *map_width <<= 1; else *map_height <<= 1; } static void _cogl_atlas_get_initial_size (CoglPixelFormat format, unsigned int *map_width, unsigned int *map_height) { unsigned int size; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_pixel_format_get_n_planes (format) == 1); ctx->driver_vtable->pixel_format_to_gl (ctx, format, &gl_intformat, &gl_format, &gl_type); /* At least on Intel hardware, the texture size will be rounded up to at least 1MB so we might as well try to aim for that as an initial minimum size. If the format is only 1 byte per pixel we can use 1024x1024, otherwise we'll assume it will take 4 bytes per pixel and use 512x512. */ if (cogl_pixel_format_get_bytes_per_pixel (format, 0) == 1) size = 1024; else size = 512; /* Some platforms might not support this large size so we'll decrease the size until it can */ while (size > 1 && !ctx->texture_driver->size_supported (ctx, GL_TEXTURE_2D, gl_intformat, gl_format, gl_type, size, size)) size >>= 1; *map_width = size; *map_height = size; } static CoglRectangleMap * _cogl_atlas_create_map (CoglPixelFormat format, unsigned int map_width, unsigned int map_height, unsigned int n_textures, CoglAtlasRepositionData *textures) { GLenum gl_intformat; GLenum gl_format; GLenum gl_type; _COGL_GET_CONTEXT (ctx, NULL); ctx->driver_vtable->pixel_format_to_gl (ctx, format, &gl_intformat, &gl_format, &gl_type); /* Keep trying increasingly larger atlases until we can fit all of the textures */ while (ctx->texture_driver->size_supported (ctx, GL_TEXTURE_2D, gl_intformat, gl_format, gl_type, map_width, map_height)) { CoglRectangleMap *new_atlas = _cogl_rectangle_map_new (map_width, map_height, NULL); unsigned int i; COGL_NOTE (ATLAS, "Trying to resize the atlas to %ux%u", map_width, map_height); /* Add all of the textures and keep track of the new position */ for (i = 0; i < n_textures; i++) if (!_cogl_rectangle_map_add (new_atlas, textures[i].old_position.width, textures[i].old_position.height, textures[i].user_data, &textures[i].new_position)) break; /* If the atlas can contain all of the textures then we have a winner */ if (i >= n_textures) return new_atlas; else COGL_NOTE (ATLAS, "Atlas size abandoned after trying " "%u out of %u textures", i, n_textures); _cogl_rectangle_map_free (new_atlas); _cogl_atlas_get_next_size (&map_width, &map_height); } /* If we get here then there's no atlas that can accommodate all of the rectangles */ return NULL; } static CoglTexture2D * _cogl_atlas_create_texture (CoglAtlas *atlas, int width, int height) { CoglTexture2D *tex; GError *ignore_error = NULL; _COGL_GET_CONTEXT (ctx, NULL); g_return_val_if_fail ( cogl_pixel_format_get_n_planes (atlas->texture_format) == 1, NULL); if ((atlas->flags & COGL_ATLAS_CLEAR_TEXTURE)) { uint8_t *clear_data; CoglBitmap *clear_bmp; int bpp = cogl_pixel_format_get_bytes_per_pixel (atlas->texture_format, 0); /* Create a buffer of zeroes to initially clear the texture */ clear_data = g_malloc0 (width * height * bpp); clear_bmp = cogl_bitmap_new_for_data (ctx, width, height, atlas->texture_format, width * bpp, clear_data); tex = cogl_texture_2d_new_from_bitmap (clear_bmp); _cogl_texture_set_internal_format (COGL_TEXTURE (tex), atlas->texture_format); if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) { g_error_free (ignore_error); cogl_object_unref (tex); tex = NULL; } cogl_object_unref (clear_bmp); g_free (clear_data); } else { tex = cogl_texture_2d_new_with_size (ctx, width, height); _cogl_texture_set_internal_format (COGL_TEXTURE (tex), atlas->texture_format); if (!cogl_texture_allocate (COGL_TEXTURE (tex), &ignore_error)) { g_error_free (ignore_error); cogl_object_unref (tex); tex = NULL; } } return tex; } static int _cogl_atlas_compare_size_cb (const void *a, const void *b) { const CoglAtlasRepositionData *ta = a; const CoglAtlasRepositionData *tb = b; unsigned int a_size, b_size; a_size = ta->old_position.width * ta->old_position.height; b_size = tb->old_position.width * tb->old_position.height; return a_size < b_size ? 1 : a_size > b_size ? -1 : 0; } static void _cogl_atlas_notify_pre_reorganize (CoglAtlas *atlas) { g_hook_list_invoke (&atlas->pre_reorganize_callbacks, FALSE); } static void _cogl_atlas_notify_post_reorganize (CoglAtlas *atlas) { g_hook_list_invoke (&atlas->post_reorganize_callbacks, FALSE); } gboolean _cogl_atlas_reserve_space (CoglAtlas *atlas, unsigned int width, unsigned int height, void *user_data) { CoglAtlasGetRectanglesData data; CoglRectangleMap *new_map; CoglTexture2D *new_tex; unsigned int map_width = 0, map_height = 0; gboolean ret; CoglRectangleMapEntry new_position; /* Check if we can fit the rectangle into the existing map */ if (atlas->map && _cogl_rectangle_map_add (atlas->map, width, height, user_data, &new_position)) { COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), /* waste as a percentage */ _cogl_rectangle_map_get_remaining_space (atlas->map) * 100 / (_cogl_rectangle_map_get_width (atlas->map) * _cogl_rectangle_map_get_height (atlas->map))); atlas->update_position_cb (user_data, atlas->texture, &new_position); return TRUE; } /* If we make it here then we need to reorganize the atlas. First we'll notify any users of the atlas that this is going to happen so that for example in CoglAtlasTexture it can notify that the storage has changed and cause a flush */ _cogl_atlas_notify_pre_reorganize (atlas); /* Get an array of all the textures currently in the atlas. */ data.n_textures = 0; if (atlas->map == NULL) data.textures = g_malloc (sizeof (CoglAtlasRepositionData)); else { unsigned int n_rectangles = _cogl_rectangle_map_get_n_rectangles (atlas->map); data.textures = g_malloc (sizeof (CoglAtlasRepositionData) * (n_rectangles + 1)); _cogl_rectangle_map_foreach (atlas->map, _cogl_atlas_get_rectangles_cb, &data); } /* Add the new rectangle as a dummy texture so that it can be positioned with the rest */ data.textures[data.n_textures].old_position.x = 0; data.textures[data.n_textures].old_position.y = 0; data.textures[data.n_textures].old_position.width = width; data.textures[data.n_textures].old_position.height = height; data.textures[data.n_textures++].user_data = user_data; /* The atlasing algorithm works a lot better if the rectangles are added in decreasing order of size so we'll first sort the array */ qsort (data.textures, data.n_textures, sizeof (CoglAtlasRepositionData), _cogl_atlas_compare_size_cb); /* Try to create a new atlas that can contain all of the textures */ if (atlas->map) { map_width = _cogl_rectangle_map_get_width (atlas->map); map_height = _cogl_rectangle_map_get_height (atlas->map); /* If there is enough space in for the new rectangle in the existing atlas with at least 6% waste we'll start with the same size, otherwise we'll immediately double it */ if ((map_width * map_height - _cogl_rectangle_map_get_remaining_space (atlas->map) + width * height) * 53 / 50 > map_width * map_height) _cogl_atlas_get_next_size (&map_width, &map_height); } else _cogl_atlas_get_initial_size (atlas->texture_format, &map_width, &map_height); new_map = _cogl_atlas_create_map (atlas->texture_format, map_width, map_height, data.n_textures, data.textures); /* If we can't create a map with the texture then give up */ if (new_map == NULL) { COGL_NOTE (ATLAS, "%p: Could not fit texture in the atlas", atlas); ret = FALSE; } /* We need to migrate the existing textures into a new texture */ else if ((new_tex = _cogl_atlas_create_texture (atlas, _cogl_rectangle_map_get_width (new_map), _cogl_rectangle_map_get_height (new_map))) == NULL) { COGL_NOTE (ATLAS, "%p: Could not create a CoglTexture2D", atlas); _cogl_rectangle_map_free (new_map); ret = FALSE; } else { int waste; COGL_NOTE (ATLAS, "%p: Atlas %s with size %ix%i", atlas, atlas->map == NULL || _cogl_rectangle_map_get_width (atlas->map) != _cogl_rectangle_map_get_width (new_map) || _cogl_rectangle_map_get_height (atlas->map) != _cogl_rectangle_map_get_height (new_map) ? "resized" : "reorganized", _cogl_rectangle_map_get_width (new_map), _cogl_rectangle_map_get_height (new_map)); if (atlas->map) { /* Move all the textures to the right position in the new texture. This will also update the texture's rectangle */ _cogl_atlas_migrate (atlas, data.n_textures, data.textures, atlas->texture, COGL_TEXTURE (new_tex), user_data); _cogl_rectangle_map_free (atlas->map); cogl_object_unref (atlas->texture); } else /* We know there's only one texture so we can just directly update the rectangle from its new position */ atlas->update_position_cb (data.textures[0].user_data, COGL_TEXTURE (new_tex), &data.textures[0].new_position); atlas->map = new_map; atlas->texture = COGL_TEXTURE (new_tex); waste = (_cogl_rectangle_map_get_remaining_space (atlas->map) * 100 / (_cogl_rectangle_map_get_width (atlas->map) * _cogl_rectangle_map_get_height (atlas->map))); COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), waste); ret = TRUE; } g_free (data.textures); _cogl_atlas_notify_post_reorganize (atlas); return ret; } void _cogl_atlas_remove (CoglAtlas *atlas, const CoglRectangleMapEntry *rectangle) { _cogl_rectangle_map_remove (atlas->map, rectangle); COGL_NOTE (ATLAS, "%p: Removed rectangle sized %ix%i", atlas, rectangle->width, rectangle->height); COGL_NOTE (ATLAS, "%p: Atlas is %ix%i, has %i textures and is %i%% waste", atlas, _cogl_rectangle_map_get_width (atlas->map), _cogl_rectangle_map_get_height (atlas->map), _cogl_rectangle_map_get_n_rectangles (atlas->map), _cogl_rectangle_map_get_remaining_space (atlas->map) * 100 / (_cogl_rectangle_map_get_width (atlas->map) * _cogl_rectangle_map_get_height (atlas->map))); }; static CoglTexture * create_migration_texture (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format) { CoglTexture *tex; GError *skip_error = NULL; /* First try creating a fast-path non-sliced texture */ tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); _cogl_texture_set_internal_format (tex, internal_format); /* TODO: instead of allocating storage here it would be better * if we had some api that let us just check that the size is * supported by the hardware so storage could be allocated * lazily when uploading data. */ if (!cogl_texture_allocate (tex, &skip_error)) { g_error_free (skip_error); cogl_object_unref (tex); tex = NULL; } if (!tex) { CoglTexture2DSliced *tex_2ds = cogl_texture_2d_sliced_new_with_size (ctx, width, height, COGL_TEXTURE_MAX_WASTE); _cogl_texture_set_internal_format (COGL_TEXTURE (tex_2ds), internal_format); tex = COGL_TEXTURE (tex_2ds); } return tex; } CoglTexture * _cogl_atlas_copy_rectangle (CoglAtlas *atlas, int x, int y, int width, int height, CoglPixelFormat internal_format) { CoglTexture *tex; CoglBlitData blit_data; GError *ignore_error = NULL; _COGL_GET_CONTEXT (ctx, NULL); /* Create a new texture at the right size */ tex = create_migration_texture (ctx, width, height, internal_format); if (!cogl_texture_allocate (tex, &ignore_error)) { g_error_free (ignore_error); cogl_object_unref (tex); return NULL; } /* Blit the data out of the atlas to the new texture. If FBOs aren't available this will end up having to copy the entire atlas texture */ _cogl_blit_begin (&blit_data, tex, atlas->texture); _cogl_blit (&blit_data, x, y, 0, 0, width, height); _cogl_blit_end (&blit_data); return tex; } void _cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, GHookFunc pre_callback, GHookFunc post_callback, void *user_data) { if (pre_callback) { GHook *hook = g_hook_alloc (&atlas->post_reorganize_callbacks); hook->func = pre_callback; hook->data = user_data; g_hook_prepend (&atlas->pre_reorganize_callbacks, hook); } if (post_callback) { GHook *hook = g_hook_alloc (&atlas->pre_reorganize_callbacks); hook->func = post_callback; hook->data = user_data; g_hook_prepend (&atlas->post_reorganize_callbacks, hook); } } void _cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, GHookFunc pre_callback, GHookFunc post_callback, void *user_data) { if (pre_callback) { GHook *hook = g_hook_find_func_data (&atlas->pre_reorganize_callbacks, FALSE, pre_callback, user_data); if (hook) g_hook_destroy_link (&atlas->pre_reorganize_callbacks, hook); } if (post_callback) { GHook *hook = g_hook_find_func_data (&atlas->post_reorganize_callbacks, FALSE, post_callback, user_data); if (hook) g_hook_destroy_link (&atlas->post_reorganize_callbacks, hook); } } muffin-6.4.1/cogl/cogl/cogl-pipeline-debug.c0000664000175000017500000002076614723361714017604 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-layer-private.h" #include "cogl-node-private.h" #include typedef struct { int parent_id; int *node_id_ptr; GString *graph; int indent; } PrintDebugState; static gboolean dump_layer_cb (CoglNode *node, void *user_data) { CoglPipelineLayer *layer = COGL_PIPELINE_LAYER (node); PrintDebugState *state = user_data; int layer_id = *state->node_id_ptr; PrintDebugState state_out; GString *changes_label; gboolean changes = FALSE; if (state->parent_id >= 0) g_string_append_printf (state->graph, "%*slayer%p -> layer%p;\n", state->indent, "", layer->_parent.parent, layer); g_string_append_printf (state->graph, "%*slayer%p [label=\"layer=0x%p\\n" "ref count=%d\" " "color=\"blue\"];\n", state->indent, "", layer, layer, COGL_OBJECT (layer)->ref_count); changes_label = g_string_new (""); g_string_append_printf (changes_label, "%*slayer%p -> layer_state%d [weight=100];\n" "%*slayer_state%d [shape=box label=\"", state->indent, "", layer, layer_id, state->indent, "", layer_id); if (layer->differences & COGL_PIPELINE_LAYER_STATE_UNIT) { changes = TRUE; g_string_append_printf (changes_label, "\\lunit=%u\\n", layer->unit_index); } if (layer->differences & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) { changes = TRUE; g_string_append_printf (changes_label, "\\ltexture=%p\\n", layer->texture); } if (changes) { g_string_append_printf (changes_label, "\"];\n"); g_string_append (state->graph, changes_label->str); g_string_free (changes_label, TRUE); } state_out.parent_id = layer_id; state_out.node_id_ptr = state->node_id_ptr; (*state_out.node_id_ptr)++; state_out.graph = state->graph; state_out.indent = state->indent + 2; _cogl_pipeline_node_foreach_child (COGL_NODE (layer), dump_layer_cb, &state_out); return TRUE; } static gboolean dump_layer_ref_cb (CoglPipelineLayer *layer, void *data) { PrintDebugState *state = data; int pipeline_id = *state->node_id_ptr; g_string_append_printf (state->graph, "%*spipeline_state%d -> layer%p;\n", state->indent, "", pipeline_id, layer); return TRUE; } static gboolean dump_pipeline_cb (CoglNode *node, void *user_data) { CoglPipeline *pipeline = COGL_PIPELINE (node); PrintDebugState *state = user_data; int pipeline_id = *state->node_id_ptr; PrintDebugState state_out; GString *changes_label; gboolean changes = FALSE; gboolean layers = FALSE; if (state->parent_id >= 0) g_string_append_printf (state->graph, "%*spipeline%d -> pipeline%d;\n", state->indent, "", state->parent_id, pipeline_id); g_string_append_printf (state->graph, "%*spipeline%d [label=\"pipeline=0x%p\\n" "ref count=%d\\n" "breadcrumb=\\\"%s\\\"\" color=\"red\"];\n", state->indent, "", pipeline_id, pipeline, COGL_OBJECT (pipeline)->ref_count, pipeline->has_static_breadcrumb ? #ifdef COGL_DEBUG_ENABLED pipeline->static_breadcrumb : "NULL" #else "NULL" #endif ); changes_label = g_string_new (""); g_string_append_printf (changes_label, "%*spipeline%d -> pipeline_state%d [weight=100];\n" "%*spipeline_state%d [shape=box label=\"", state->indent, "", pipeline_id, pipeline_id, state->indent, "", pipeline_id); if (pipeline->differences & COGL_PIPELINE_STATE_COLOR) { changes = TRUE; g_string_append_printf (changes_label, "\\lcolor=0x%02X%02X%02X%02X\\n", cogl_color_get_red_byte (&pipeline->color), cogl_color_get_green_byte (&pipeline->color), cogl_color_get_blue_byte (&pipeline->color), cogl_color_get_alpha_byte (&pipeline->color)); } if (pipeline->differences & COGL_PIPELINE_STATE_BLEND) { changes = TRUE; g_string_append_printf (changes_label, "\\lblend\\n"); } if (pipeline->differences & COGL_PIPELINE_STATE_LAYERS) { changes = TRUE; layers = TRUE; g_string_append_printf (changes_label, "\\ln_layers=%d\\n", pipeline->n_layers); } if (changes) { g_string_append_printf (changes_label, "\"];\n"); g_string_append (state->graph, changes_label->str); g_string_free (changes_label, TRUE); } if (layers) { g_list_foreach (pipeline->layer_differences, (GFunc)dump_layer_ref_cb, state); } state_out.parent_id = pipeline_id; state_out.node_id_ptr = state->node_id_ptr; (*state_out.node_id_ptr)++; state_out.graph = state->graph; state_out.indent = state->indent + 2; _cogl_pipeline_node_foreach_child (COGL_NODE (pipeline), dump_pipeline_cb, &state_out); return TRUE; } /* This function is just here to be called from GDB so we don't really want to put a declaration in a header and we just add it here to avoid a warning */ void _cogl_debug_dump_pipelines_dot_file (const char *filename); void _cogl_debug_dump_pipelines_dot_file (const char *filename) { GString *graph; PrintDebugState layer_state; PrintDebugState pipeline_state; int layer_id = 0; int pipeline_id = 0; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!ctx->default_pipeline) return; graph = g_string_new (""); g_string_append_printf (graph, "digraph {\n"); layer_state.graph = graph; layer_state.parent_id = -1; layer_state.node_id_ptr = &layer_id; layer_state.indent = 0; dump_layer_cb ((CoglNode *)ctx->default_layer_0, &layer_state); pipeline_state.graph = graph; pipeline_state.parent_id = -1; pipeline_state.node_id_ptr = &pipeline_id; pipeline_state.indent = 0; dump_pipeline_cb ((CoglNode *)ctx->default_pipeline, &pipeline_state); g_string_append_printf (graph, "}\n"); if (filename) g_file_set_contents (filename, graph->str, -1, NULL); else g_print ("%s", graph->str); g_string_free (graph, TRUE); } muffin-6.4.1/cogl/cogl/cogl-util.c0000664000175000017500000001504714723361714015664 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-util.h" #include "cogl-private.h" /* * cogl_util_next_p2: * @a: Value to get the next power of two * * Calculates the next power of two greater than or equal to @a. * * Return value: @a if @a is already a power of two, otherwise returns * the next nearest power of two. */ COGL_EXPORT int _cogl_util_next_p2 (int a) { int rval = 1; while (rval < a) rval <<= 1; return rval; } unsigned int _cogl_util_one_at_a_time_mix (unsigned int hash) { hash += ( hash << 3 ); hash ^= ( hash >> 11 ); hash += ( hash << 15 ); return hash; } /* tests/conform/test-bitmask.c tests some cogl internals and includes this * file directly but since these functions depend on other internal Cogl * symbols we hide them from test-bitmask.c * * XXX: maybe there's a better way for us to handle internal testing * to avoid needing hacks like this. */ #ifndef _COGL_IN_TEST_BITMASK /* Given a set of red, green and blue component masks, a depth and * bits per pixel this function tries to determine a corresponding * CoglPixelFormat. * * The depth is measured in bits not including padding for un-used * alpha. The bits per pixel (bpp) does include padding for un-used * alpha. * * This function firstly aims to match formats with RGB ordered * components and only considers alpha coming first, in the most * significant bits. If the function fails to match then it recurses * by either switching the r and b masks around to check for BGR * ordered formats or it recurses with the masks shifted to check for * formats where the alpha component is the least significant bits. */ static CoglPixelFormat _cogl_util_pixel_format_from_masks_real (unsigned long r_mask, unsigned long g_mask, unsigned long b_mask, int depth, int bpp, gboolean check_bgr, gboolean check_afirst, int recursion_depth) { CoglPixelFormat image_format; if (depth == 24 && bpp == 24 && r_mask == 0xff0000 && g_mask == 0xff00 && b_mask == 0xff) { return COGL_PIXEL_FORMAT_RGB_888; } else if ((depth == 24 || depth == 32) && bpp == 32 && r_mask == 0xff0000 && g_mask == 0xff00 && b_mask == 0xff) { return COGL_PIXEL_FORMAT_ARGB_8888_PRE; } else if ((depth == 30 || depth == 32) && r_mask == 0x3ff00000 && g_mask == 0xffc00 && b_mask == 0x3ff) { return COGL_PIXEL_FORMAT_ARGB_2101010_PRE; } else if (depth == 16 && bpp == 16 && r_mask == 0xf800 && g_mask == 0x7e0 && b_mask == 0x1f) { return COGL_PIXEL_FORMAT_RGB_565; } if (recursion_depth == 2) return 0; /* Check for BGR ordering if we didn't find a match */ if (check_bgr) { image_format = _cogl_util_pixel_format_from_masks_real (b_mask, g_mask, r_mask, depth, bpp, FALSE, TRUE, recursion_depth + 1); if (image_format) return image_format ^ COGL_BGR_BIT; } /* Check for alpha in the least significant bits if we still * haven't found a match... */ if (check_afirst && depth != bpp) { int shift = bpp - depth; image_format = _cogl_util_pixel_format_from_masks_real (r_mask >> shift, g_mask >> shift, b_mask >> shift, depth, bpp, TRUE, FALSE, recursion_depth + 1); if (image_format) return image_format ^ COGL_AFIRST_BIT; } return 0; } CoglPixelFormat _cogl_util_pixel_format_from_masks (unsigned long r_mask, unsigned long g_mask, unsigned long b_mask, int depth, int bpp, gboolean byte_order_is_lsb_first) { CoglPixelFormat image_format = _cogl_util_pixel_format_from_masks_real (r_mask, g_mask, b_mask, depth, bpp, TRUE, TRUE, 0); if (!image_format) { const char *byte_order[] = { "MSB first", "LSB first" }; g_warning ("Could not find a matching pixel format for red mask=0x%lx," "green mask=0x%lx, blue mask=0x%lx at depth=%d, bpp=%d " "and byte order=%s\n", r_mask, g_mask, b_mask, depth, bpp, byte_order[!!byte_order_is_lsb_first]); return 0; } /* If the image is in little-endian then the order in memory is reversed */ if (byte_order_is_lsb_first && _cogl_pixel_format_is_endian_dependant (image_format)) { image_format ^= COGL_BGR_BIT; if (image_format & COGL_A_BIT) image_format ^= COGL_AFIRST_BIT; } return image_format; } #endif /* _COGL_IN_TEST_BITMASK */ muffin-6.4.1/cogl/cogl/cogl-texture-2d-private.h0000664000175000017500000001006314723361714020360 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_2D_PRIVATE_H #define __COGL_TEXTURE_2D_PRIVATE_H #include "cogl-object-private.h" #include "cogl-pipeline-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d.h" struct _CoglTexture2D { CoglTexture _parent; /* The internal format of the GL texture represented as a CoglPixelFormat */ CoglPixelFormat internal_format; gboolean auto_mipmap; gboolean mipmaps_dirty; gboolean is_get_data_supported; /* TODO: factor out these OpenGL specific members into some form * of driver private state. */ /* The internal format of the GL texture represented as a GL enum */ GLenum gl_internal_format; /* The texture object number */ GLuint gl_texture; GLenum gl_target; GLenum gl_legacy_texobj_min_filter; GLenum gl_legacy_texobj_mag_filter; GLint gl_legacy_texobj_wrap_mode_s; GLint gl_legacy_texobj_wrap_mode_t; CoglTexturePixel first_pixel; struct { void *user_data; GDestroyNotify destroy; } egl_image_external; }; CoglTexture2D * _cogl_texture_2d_new_from_bitmap (CoglBitmap *bmp, gboolean can_convert_in_place); CoglTexture2D * _cogl_texture_2d_create_base (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format, CoglTextureLoader *loader); void _cogl_texture_2d_set_auto_mipmap (CoglTexture *tex, gboolean value); /* * _cogl_texture_2d_externally_modified: * @texture: A #CoglTexture2D object * * This should be called whenever the texture is modified other than * by using cogl_texture_set_region. It will cause the mipmaps to be * invalidated */ void _cogl_texture_2d_externally_modified (CoglTexture *texture); /* * _cogl_texture_2d_copy_from_framebuffer: * @texture: A #CoglTexture2D pointer * @src_x: X-position to within the framebuffer to read from * @src_y: Y-position to within the framebuffer to read from * @width: width of the rectangle to copy * @height: height of the rectangle to copy * @src_fb: A source #CoglFramebuffer to copy from * @dst_x: X-position to store the image within the texture * @dst_y: Y-position to store the image within the texture * @level: The mipmap level of @texture to copy too * * This copies a portion of the given @src_fb into the * texture. */ void _cogl_texture_2d_copy_from_framebuffer (CoglTexture2D *texture, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level); #endif /* __COGL_TEXTURE_2D_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-sampler-cache.c0000664000175000017500000002707714723361714017421 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-sampler-cache-private.h" #include "cogl-context-private.h" #include "driver/gl/cogl-util-gl-private.h" #ifndef GL_TEXTURE_WRAP_R #define GL_TEXTURE_WRAP_R 0x8072 #endif struct _CoglSamplerCache { CoglContext *context; /* The samplers are hashed in two tables. One is using the enum values that Cogl exposes (so it can include the 'automatic' wrap mode) and the other is using the converted values that will be given to GL. The first is used to get a unique pointer for the sampler state so that pipelines only need to store a single pointer instead of the whole state and the second is used so that only a single GL sampler object will be created for each unique GL state. */ GHashTable *hash_table_cogl; GHashTable *hash_table_gl; /* This is used for generated fake unique sampler object numbers when the sampler object extension is not supported */ GLuint next_fake_sampler_object_number; }; static CoglSamplerCacheWrapMode get_real_wrap_mode (CoglSamplerCacheWrapMode wrap_mode) { if (wrap_mode == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) return COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE; return wrap_mode; } static void canonicalize_key (CoglSamplerCacheEntry *key) { /* This converts the wrap modes to the enums that will actually be given to GL so that it can be used as a key to get a unique GL sampler object for the state */ key->wrap_mode_s = get_real_wrap_mode (key->wrap_mode_s); key->wrap_mode_t = get_real_wrap_mode (key->wrap_mode_t); } static gboolean wrap_mode_equal_gl (CoglSamplerCacheWrapMode wrap_mode0, CoglSamplerCacheWrapMode wrap_mode1) { /* We want to compare the actual GLenum that will be used so that if two different wrap_modes actually use the same GL state we'll still use the same sampler object */ return get_real_wrap_mode (wrap_mode0) == get_real_wrap_mode (wrap_mode1); } static gboolean sampler_state_equal_gl (const void *value0, const void *value1) { const CoglSamplerCacheEntry *state0 = value0; const CoglSamplerCacheEntry *state1 = value1; return (state0->mag_filter == state1->mag_filter && state0->min_filter == state1->min_filter && wrap_mode_equal_gl (state0->wrap_mode_s, state1->wrap_mode_s) && wrap_mode_equal_gl (state0->wrap_mode_t, state1->wrap_mode_t)); } static unsigned int hash_wrap_mode_gl (unsigned int hash, CoglSamplerCacheWrapMode wrap_mode) { /* We want to hash the actual GLenum that will be used so that if two different wrap_modes actually use the same GL state we'll still use the same sampler object */ GLenum real_wrap_mode = get_real_wrap_mode (wrap_mode); return _cogl_util_one_at_a_time_hash (hash, &real_wrap_mode, sizeof (real_wrap_mode)); } static unsigned int hash_sampler_state_gl (const void *key) { const CoglSamplerCacheEntry *entry = key; unsigned int hash = 0; hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, sizeof (entry->mag_filter)); hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, sizeof (entry->min_filter)); hash = hash_wrap_mode_gl (hash, entry->wrap_mode_s); hash = hash_wrap_mode_gl (hash, entry->wrap_mode_t); return _cogl_util_one_at_a_time_mix (hash); } static gboolean sampler_state_equal_cogl (const void *value0, const void *value1) { const CoglSamplerCacheEntry *state0 = value0; const CoglSamplerCacheEntry *state1 = value1; return (state0->mag_filter == state1->mag_filter && state0->min_filter == state1->min_filter && state0->wrap_mode_s == state1->wrap_mode_s && state0->wrap_mode_t == state1->wrap_mode_t); } static unsigned int hash_sampler_state_cogl (const void *key) { const CoglSamplerCacheEntry *entry = key; unsigned int hash = 0; hash = _cogl_util_one_at_a_time_hash (hash, &entry->mag_filter, sizeof (entry->mag_filter)); hash = _cogl_util_one_at_a_time_hash (hash, &entry->min_filter, sizeof (entry->min_filter)); hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_s, sizeof (entry->wrap_mode_s)); hash = _cogl_util_one_at_a_time_hash (hash, &entry->wrap_mode_t, sizeof (entry->wrap_mode_t)); return _cogl_util_one_at_a_time_mix (hash); } CoglSamplerCache * _cogl_sampler_cache_new (CoglContext *context) { CoglSamplerCache *cache = g_new (CoglSamplerCache, 1); /* No reference is taken on the context because it would create a circular reference */ cache->context = context; cache->hash_table_gl = g_hash_table_new (hash_sampler_state_gl, sampler_state_equal_gl); cache->hash_table_cogl = g_hash_table_new (hash_sampler_state_cogl, sampler_state_equal_cogl); cache->next_fake_sampler_object_number = 1; return cache; } static void set_wrap_mode (CoglContext *context, GLuint sampler_object, GLenum param, CoglSamplerCacheWrapMode wrap_mode) { GE( context, glSamplerParameteri (sampler_object, param, wrap_mode) ); } static CoglSamplerCacheEntry * _cogl_sampler_cache_get_entry_gl (CoglSamplerCache *cache, const CoglSamplerCacheEntry *key) { CoglSamplerCacheEntry *entry; entry = g_hash_table_lookup (cache->hash_table_gl, key); if (entry == NULL) { CoglContext *context = cache->context; entry = g_slice_dup (CoglSamplerCacheEntry, key); if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) { GE( context, glGenSamplers (1, &entry->sampler_object) ); GE( context, glSamplerParameteri (entry->sampler_object, GL_TEXTURE_MIN_FILTER, entry->min_filter) ); GE( context, glSamplerParameteri (entry->sampler_object, GL_TEXTURE_MAG_FILTER, entry->mag_filter) ); set_wrap_mode (context, entry->sampler_object, GL_TEXTURE_WRAP_S, entry->wrap_mode_s); set_wrap_mode (context, entry->sampler_object, GL_TEXTURE_WRAP_T, entry->wrap_mode_t); } else { /* If sampler objects aren't supported then we'll invent a unique number so that pipelines can still compare the unique state just by comparing the sampler object numbers */ entry->sampler_object = cache->next_fake_sampler_object_number++; } g_hash_table_insert (cache->hash_table_gl, entry, entry); } return entry; } static CoglSamplerCacheEntry * _cogl_sampler_cache_get_entry_cogl (CoglSamplerCache *cache, const CoglSamplerCacheEntry *key) { CoglSamplerCacheEntry *entry; entry = g_hash_table_lookup (cache->hash_table_cogl, key); if (entry == NULL) { CoglSamplerCacheEntry canonical_key; CoglSamplerCacheEntry *gl_entry; entry = g_slice_dup (CoglSamplerCacheEntry, key); /* Get the sampler object number from the canonical GL version of the sampler state cache */ canonical_key = *key; canonicalize_key (&canonical_key); gl_entry = _cogl_sampler_cache_get_entry_gl (cache, &canonical_key); entry->sampler_object = gl_entry->sampler_object; g_hash_table_insert (cache->hash_table_cogl, entry, entry); } return entry; } const CoglSamplerCacheEntry * _cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache) { CoglSamplerCacheEntry key; key.wrap_mode_s = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; key.wrap_mode_t = COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC; key.min_filter = GL_LINEAR; key.mag_filter = GL_LINEAR; return _cogl_sampler_cache_get_entry_cogl (cache, &key); } const CoglSamplerCacheEntry * _cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, const CoglSamplerCacheEntry *old_entry, CoglSamplerCacheWrapMode wrap_mode_s, CoglSamplerCacheWrapMode wrap_mode_t) { CoglSamplerCacheEntry key = *old_entry; key.wrap_mode_s = wrap_mode_s; key.wrap_mode_t = wrap_mode_t; return _cogl_sampler_cache_get_entry_cogl (cache, &key); } const CoglSamplerCacheEntry * _cogl_sampler_cache_update_filters (CoglSamplerCache *cache, const CoglSamplerCacheEntry *old_entry, GLenum min_filter, GLenum mag_filter) { CoglSamplerCacheEntry key = *old_entry; key.min_filter = min_filter; key.mag_filter = mag_filter; return _cogl_sampler_cache_get_entry_cogl (cache, &key); } static void hash_table_free_gl_cb (void *key, void *value, void *user_data) { CoglContext *context = user_data; CoglSamplerCacheEntry *entry = value; if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) GE( context, glDeleteSamplers (1, &entry->sampler_object) ); g_slice_free (CoglSamplerCacheEntry, entry); } static void hash_table_free_cogl_cb (void *key, void *value, void *user_data) { CoglSamplerCacheEntry *entry = value; g_slice_free (CoglSamplerCacheEntry, entry); } void _cogl_sampler_cache_free (CoglSamplerCache *cache) { g_hash_table_foreach (cache->hash_table_gl, hash_table_free_gl_cb, cache->context); g_hash_table_destroy (cache->hash_table_gl); g_hash_table_foreach (cache->hash_table_cogl, hash_table_free_cogl_cb, cache->context); g_hash_table_destroy (cache->hash_table_cogl); g_free (cache); } muffin-6.4.1/cogl/cogl/cogl-list.h0000664000175000017500000001215514723361714015664 0ustar fabiofabio/* * Copyright © 2008 Kristian Høgsberg * Copyright © 2012, 2013 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* This list implementation is based on the Wayland source code */ #ifndef COGL_LIST_H #define COGL_LIST_H #include /** * CoglList - linked list * * The list head is of "CoglList" type, and must be initialized * using cogl_list_init(). All entries in the list must be of the same * type. The item type must have a "CoglList" member. This * member will be initialized by cogl_list_insert(). There is no need to * call cogl_list_init() on the individual item. To query if the list is * empty in O(1), use cogl_list_empty(). * * Let's call the list reference "CoglList foo_list", the item type as * "item_t", and the item member as "CoglList link". The following code * * The following code will initialize a list: * * cogl_list_init (foo_list); * cogl_list_insert (foo_list, item1); Pushes item1 at the head * cogl_list_insert (foo_list, item2); Pushes item2 at the head * cogl_list_insert (item2, item3); Pushes item3 after item2 * * The list now looks like [item2, item3, item1] * * Will iterate the list in ascending order: * * item_t *item; * cogl_list_for_each(item, foo_list, link) { * Do_something_with_item(item); * } */ typedef struct _CoglList CoglList; struct _CoglList { CoglList *prev; CoglList *next; }; void _cogl_list_init (CoglList *list); void _cogl_list_insert (CoglList *list, CoglList *elm); void _cogl_list_remove (CoglList *elm); int _cogl_list_length (CoglList *list); int _cogl_list_empty (CoglList *list); void _cogl_list_insert_list (CoglList *list, CoglList *other); /* This assigns to iterator first so that taking a reference to it * later in the second step won't be an undefined operation. It * assigns the value of list_node rather than 0 so that it is possible * have list_node be based on the previous value of iterator. In that * respect iterator is just used as a convenient temporary variable. * The compiler optimises all of this down to a single subtraction by * a constant */ #define _cogl_list_set_iterator(list_node, iterator, member) \ ((iterator) = (void *) (list_node), \ (iterator) = (void *) ((char *) (iterator) - \ (((char *) &(iterator)->member) - \ (char *) (iterator)))) #define _cogl_container_of(ptr, type, member) \ (type *) ((char *) (ptr) - offsetof (type, member)) #define _cogl_list_for_each(pos, head, member) \ for (_cogl_list_set_iterator ((head)->next, pos, member); \ &pos->member != (head); \ _cogl_list_set_iterator (pos->member.next, pos, member)) #define _cogl_list_for_each_safe(pos, tmp, head, member) \ for (_cogl_list_set_iterator ((head)->next, pos, member), \ _cogl_list_set_iterator ((pos)->member.next, tmp, member); \ &pos->member != (head); \ pos = tmp, \ _cogl_list_set_iterator (pos->member.next, tmp, member)) #define _cogl_list_for_each_reverse(pos, head, member) \ for (_cogl_list_set_iterator ((head)->prev, pos, member); \ &pos->member != (head); \ _cogl_list_set_iterator (pos->member.prev, pos, member)) #define _cogl_list_for_each_reverse_safe(pos, tmp, head, member) \ for (_cogl_list_set_iterator ((head)->prev, pos, member), \ _cogl_list_set_iterator ((pos)->member.prev, tmp, member); \ &pos->member != (head); \ pos = tmp, \ _cogl_list_set_iterator (pos->member.prev, tmp, member)) #endif /* COGL_LIST_H */ muffin-6.4.1/cogl/cogl/cogl-pipeline-hash-table.c0000664000175000017500000001636314723361714020524 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-hash-table.h" #include "cogl-pipeline-cache.h" typedef struct { CoglPipelineCacheEntry parent; /* Calculating the hash is a little bit expensive for pipelines so * we don't want to do it repeatedly for entries that are already in * the hash table. Instead we cache the value here and calculate it * outside of the GHashTable. */ unsigned int hash_value; /* GHashTable annoyingly doesn't let us pass a user data pointer to * the hash and equal functions so to work around it we have to * store the pointer in every hash table entry. We will use this * entry as both the key and the value */ CoglPipelineHashTable *hash; /* The number of unique pipelines that had been created when this * pipeline was last accessed */ int age; } CoglPipelineHashTableEntry; static void value_destroy_cb (void *value) { CoglPipelineHashTableEntry *entry = value; cogl_object_unref (entry->parent.pipeline); g_slice_free (CoglPipelineHashTableEntry, entry); } static unsigned int entry_hash (const void *data) { const CoglPipelineHashTableEntry *entry = data; return entry->hash_value; } static gboolean entry_equal (const void *a, const void *b) { const CoglPipelineHashTableEntry *entry_a = a; const CoglPipelineHashTableEntry *entry_b = b; const CoglPipelineHashTable *hash = entry_a->hash; return _cogl_pipeline_equal (entry_a->parent.pipeline, entry_b->parent.pipeline, hash->main_state, hash->layer_state, 0); } void _cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash, unsigned int main_state, unsigned int layer_state, const char *debug_string) { hash->n_unique_pipelines = 0; hash->debug_string = debug_string; hash->main_state = main_state; hash->layer_state = layer_state; /* We'll only start pruning once we get to 16 unique pipelines */ hash->expected_min_size = 8; hash->table = g_hash_table_new_full (entry_hash, entry_equal, NULL, /* key destroy */ value_destroy_cb); } void _cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash) { g_hash_table_destroy (hash->table); } static void collect_prunable_entries_cb (void *key, void *value, void *user_data) { GQueue *entries = user_data; CoglPipelineCacheEntry *entry = value; if (entry->usage_count == 0) g_queue_push_tail (entries, entry); } static int compare_pipeline_age_cb (const void *a, const void *b) { const CoglPipelineHashTableEntry *ae = a; const CoglPipelineHashTableEntry *be = b; return be->age - ae->age; } static void prune_old_pipelines (CoglPipelineHashTable *hash) { GQueue entries; GList *l; int i; /* Collect all of the prunable entries into a GQueue */ g_queue_init (&entries); g_hash_table_foreach (hash->table, collect_prunable_entries_cb, &entries); /* Sort the entries by increasing order of age */ entries.head = g_list_sort (entries.head, compare_pipeline_age_cb); /* The +1 is to include the pipeline that we're about to add */ hash->expected_min_size = (g_hash_table_size (hash->table) - entries.length + 1); /* Remove oldest half of the prunable pipelines. We still want to * keep some of the prunable entries that are recently used because * it's not unlikely that the application will recreate the same * pipeline */ for (l = entries.head, i = 0; i < entries.length / 2; l = l->next, i++) { CoglPipelineCacheEntry *entry = l->data; g_hash_table_remove (hash->table, entry); } g_list_free (entries.head); } CoglPipelineCacheEntry * _cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash, CoglPipeline *key_pipeline) { CoglPipelineHashTableEntry dummy_entry; CoglPipelineHashTableEntry *entry; unsigned int copy_state; dummy_entry.parent.pipeline = key_pipeline; dummy_entry.hash = hash; dummy_entry.hash_value = _cogl_pipeline_hash (key_pipeline, hash->main_state, hash->layer_state, 0); entry = g_hash_table_lookup (hash->table, &dummy_entry); if (entry) { entry->age = hash->n_unique_pipelines; return &entry->parent; } if (hash->n_unique_pipelines == 50) g_warning ("Over 50 separate %s have been generated which is very " "unusual, so something is probably wrong!\n", hash->debug_string); /* If we are going to have more than twice the expected minimum * number of pipelines in the hash then we'll try pruning and update * the minimum */ if (g_hash_table_size (hash->table) >= hash->expected_min_size * 2) prune_old_pipelines (hash); entry = g_slice_new (CoglPipelineHashTableEntry); entry->parent.usage_count = 0; entry->hash = hash; entry->hash_value = dummy_entry.hash_value; entry->age = hash->n_unique_pipelines; copy_state = hash->main_state; if (hash->layer_state) copy_state |= COGL_PIPELINE_STATE_LAYERS; /* Create a new pipeline that is a child of the root pipeline * instead of a normal copy so that the template pipeline won't hold * a reference to the original pipeline */ entry->parent.pipeline = _cogl_pipeline_deep_copy (key_pipeline, copy_state, hash->layer_state); g_hash_table_insert (hash->table, entry, entry); hash->n_unique_pipelines++; return &entry->parent; } muffin-6.4.1/cogl/cogl/cogl-display.c0000664000175000017500000001042614723361714016350 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-object.h" #include "cogl-display-private.h" #include "cogl-renderer-private.h" #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT #include "cogl-wayland-server.h" #endif #include "cogl-gtype-private.h" #include "winsys/cogl-winsys-private.h" static void _cogl_display_free (CoglDisplay *display); COGL_OBJECT_DEFINE (Display, display); COGL_GTYPE_DEFINE_CLASS (Display, display); static const CoglWinsysVtable * _cogl_display_get_winsys (CoglDisplay *display) { return display->renderer->winsys_vtable; } static void _cogl_display_free (CoglDisplay *display) { const CoglWinsysVtable *winsys; if (display->setup) { winsys = _cogl_display_get_winsys (display); winsys->display_destroy (display); display->setup = FALSE; } if (display->renderer) { cogl_object_unref (display->renderer); display->renderer = NULL; } if (display->onscreen_template) { cogl_object_unref (display->onscreen_template); display->onscreen_template = NULL; } g_slice_free (CoglDisplay, display); } CoglDisplay * cogl_display_new (CoglRenderer *renderer, CoglOnscreenTemplate *onscreen_template) { CoglDisplay *display = g_slice_new0 (CoglDisplay); GError *error = NULL; _cogl_init (); display->renderer = renderer; if (renderer) cogl_object_ref (renderer); else display->renderer = cogl_renderer_new (); if (!cogl_renderer_connect (display->renderer, &error)) g_error ("Failed to connect to renderer: %s\n", error->message); display->setup = FALSE; display = _cogl_display_object_new (display); cogl_display_set_onscreen_template (display, onscreen_template); return display; } CoglRenderer * cogl_display_get_renderer (CoglDisplay *display) { return display->renderer; } void cogl_display_set_onscreen_template (CoglDisplay *display, CoglOnscreenTemplate *onscreen_template) { g_return_if_fail (display->setup == FALSE); if (onscreen_template) cogl_object_ref (onscreen_template); if (display->onscreen_template) cogl_object_unref (display->onscreen_template); display->onscreen_template = onscreen_template; /* NB: we want to maintain the invariable that there is always an * onscreen template associated with a CoglDisplay... */ if (!onscreen_template) display->onscreen_template = cogl_onscreen_template_new (NULL); } gboolean cogl_display_setup (CoglDisplay *display, GError **error) { const CoglWinsysVtable *winsys; if (display->setup) return TRUE; winsys = _cogl_display_get_winsys (display); if (!winsys->display_setup (display, error)) return FALSE; display->setup = TRUE; return TRUE; } #ifdef COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT void cogl_wayland_display_set_compositor_display (CoglDisplay *display, struct wl_display *wayland_display) { g_return_if_fail (display->setup == FALSE); display->wayland_compositor_display = wayland_display; } #endif muffin-6.4.1/cogl/cogl/cogl-rectangle-map.c0000664000175000017500000005620414723361714017426 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-util.h" #include "cogl-rectangle-map.h" #include "cogl-debug.h" /* Implements a data structure which keeps track of unused sub-rectangles within a larger rectangle using a binary tree structure. The algorithm for this is based on the description here: http://www.blackpawn.com/texts/lightmaps/default.html */ #ifdef COGL_ENABLE_DEBUG /* The cairo header is only used for debugging to generate an image of the atlas */ #include static void _cogl_rectangle_map_dump_image (CoglRectangleMap *map); #endif /* COGL_ENABLE_DEBUG */ typedef struct _CoglRectangleMapNode CoglRectangleMapNode; typedef struct _CoglRectangleMapStackEntry CoglRectangleMapStackEntry; typedef void (* CoglRectangleMapInternalForeachCb) (CoglRectangleMapNode *node, void *data); typedef enum { COGL_RECTANGLE_MAP_BRANCH, COGL_RECTANGLE_MAP_FILLED_LEAF, COGL_RECTANGLE_MAP_EMPTY_LEAF } CoglRectangleMapNodeType; struct _CoglRectangleMap { CoglRectangleMapNode *root; unsigned int n_rectangles; unsigned int space_remaining; GDestroyNotify value_destroy_func; /* Stack used for walking the structure. This is only used during the lifetime of a single function call but it is kept here as an optimisation to avoid reallocating it every time it is needed */ GArray *stack; }; struct _CoglRectangleMapNode { CoglRectangleMapNodeType type; CoglRectangleMapEntry rectangle; unsigned int largest_gap; CoglRectangleMapNode *parent; union { /* Fields used when this is a branch */ struct { CoglRectangleMapNode *left; CoglRectangleMapNode *right; } branch; /* Field used when this is a filled leaf */ void *data; } d; }; struct _CoglRectangleMapStackEntry { /* The node to search */ CoglRectangleMapNode *node; /* Index of next branch of this node to explore. Basically either 0 to go left or 1 to go right */ gboolean next_index; }; static CoglRectangleMapNode * _cogl_rectangle_map_node_new (void) { return g_slice_new (CoglRectangleMapNode); } static void _cogl_rectangle_map_node_free (CoglRectangleMapNode *node) { g_slice_free (CoglRectangleMapNode, node); } CoglRectangleMap * _cogl_rectangle_map_new (unsigned int width, unsigned int height, GDestroyNotify value_destroy_func) { CoglRectangleMap *map = g_new (CoglRectangleMap, 1); CoglRectangleMapNode *root = _cogl_rectangle_map_node_new (); root->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; root->parent = NULL; root->rectangle.x = 0; root->rectangle.y = 0; root->rectangle.width = width; root->rectangle.height = height; root->largest_gap = width * height; map->root = root; map->n_rectangles = 0; map->value_destroy_func = value_destroy_func; map->space_remaining = width * height; map->stack = g_array_new (FALSE, FALSE, sizeof (CoglRectangleMapStackEntry)); return map; } static void _cogl_rectangle_map_stack_push (GArray *stack, CoglRectangleMapNode *node, gboolean next_index) { CoglRectangleMapStackEntry *new_entry; g_array_set_size (stack, stack->len + 1); new_entry = &g_array_index (stack, CoglRectangleMapStackEntry, stack->len - 1); new_entry->node = node; new_entry->next_index = next_index; } static void _cogl_rectangle_map_stack_pop (GArray *stack) { g_array_set_size (stack, stack->len - 1); } static CoglRectangleMapStackEntry * _cogl_rectangle_map_stack_get_top (GArray *stack) { return &g_array_index (stack, CoglRectangleMapStackEntry, stack->len - 1); } static CoglRectangleMapNode * _cogl_rectangle_map_node_split_horizontally (CoglRectangleMapNode *node, unsigned int left_width) { /* Splits the node horizontally (according to emacs' definition, not vim) by converting it to a branch and adding two new leaf nodes. The leftmost branch will have the width left_width and will be returned. If the node is already just the right size it won't do anything */ CoglRectangleMapNode *left_node, *right_node; if (node->rectangle.width == left_width) return node; left_node = _cogl_rectangle_map_node_new (); left_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; left_node->parent = node; left_node->rectangle.x = node->rectangle.x; left_node->rectangle.y = node->rectangle.y; left_node->rectangle.width = left_width; left_node->rectangle.height = node->rectangle.height; left_node->largest_gap = (left_node->rectangle.width * left_node->rectangle.height); node->d.branch.left = left_node; right_node = _cogl_rectangle_map_node_new (); right_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; right_node->parent = node; right_node->rectangle.x = node->rectangle.x + left_width; right_node->rectangle.y = node->rectangle.y; right_node->rectangle.width = node->rectangle.width - left_width; right_node->rectangle.height = node->rectangle.height; right_node->largest_gap = (right_node->rectangle.width * right_node->rectangle.height); node->d.branch.right = right_node; node->type = COGL_RECTANGLE_MAP_BRANCH; return left_node; } static CoglRectangleMapNode * _cogl_rectangle_map_node_split_vertically (CoglRectangleMapNode *node, unsigned int top_height) { /* Splits the node vertically (according to emacs' definition, not vim) by converting it to a branch and adding two new leaf nodes. The topmost branch will have the height top_height and will be returned. If the node is already just the right size it won't do anything */ CoglRectangleMapNode *top_node, *bottom_node; if (node->rectangle.height == top_height) return node; top_node = _cogl_rectangle_map_node_new (); top_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; top_node->parent = node; top_node->rectangle.x = node->rectangle.x; top_node->rectangle.y = node->rectangle.y; top_node->rectangle.width = node->rectangle.width; top_node->rectangle.height = top_height; top_node->largest_gap = (top_node->rectangle.width * top_node->rectangle.height); node->d.branch.left = top_node; bottom_node = _cogl_rectangle_map_node_new (); bottom_node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; bottom_node->parent = node; bottom_node->rectangle.x = node->rectangle.x; bottom_node->rectangle.y = node->rectangle.y + top_height; bottom_node->rectangle.width = node->rectangle.width; bottom_node->rectangle.height = node->rectangle.height - top_height; bottom_node->largest_gap = (bottom_node->rectangle.width * bottom_node->rectangle.height); node->d.branch.right = bottom_node; node->type = COGL_RECTANGLE_MAP_BRANCH; return top_node; } #ifdef COGL_ENABLE_DEBUG static unsigned int _cogl_rectangle_map_verify_recursive (CoglRectangleMapNode *node) { /* This is just used for debugging the data structure. It recursively walks the tree to verify that the largest gap values all add up */ switch (node->type) { case COGL_RECTANGLE_MAP_BRANCH: { int sum = _cogl_rectangle_map_verify_recursive (node->d.branch.left) + _cogl_rectangle_map_verify_recursive (node->d.branch.right); g_assert (node->largest_gap == MAX (node->d.branch.left->largest_gap, node->d.branch.right->largest_gap)); return sum; } case COGL_RECTANGLE_MAP_EMPTY_LEAF: g_assert (node->largest_gap == node->rectangle.width * node->rectangle.height); return 0; case COGL_RECTANGLE_MAP_FILLED_LEAF: g_assert (node->largest_gap == 0); return 1; } return 0; } static unsigned int _cogl_rectangle_map_get_space_remaining_recursive (CoglRectangleMapNode *node) { /* This is just used for debugging the data structure. It recursively walks the tree to verify that the remaining space value adds up */ switch (node->type) { case COGL_RECTANGLE_MAP_BRANCH: { CoglRectangleMapNode *l = node->d.branch.left; CoglRectangleMapNode *r = node->d.branch.right; return (_cogl_rectangle_map_get_space_remaining_recursive (l) + _cogl_rectangle_map_get_space_remaining_recursive (r)); } case COGL_RECTANGLE_MAP_EMPTY_LEAF: return node->rectangle.width * node->rectangle.height; case COGL_RECTANGLE_MAP_FILLED_LEAF: return 0; } return 0; } static void _cogl_rectangle_map_verify (CoglRectangleMap *map) { unsigned int actual_n_rectangles = _cogl_rectangle_map_verify_recursive (map->root); unsigned int actual_space_remaining = _cogl_rectangle_map_get_space_remaining_recursive (map->root); g_assert_cmpuint (actual_n_rectangles, ==, map->n_rectangles); g_assert_cmpuint (actual_space_remaining, ==, map->space_remaining); } #endif /* COGL_ENABLE_DEBUG */ gboolean _cogl_rectangle_map_add (CoglRectangleMap *map, unsigned int width, unsigned int height, void *data, CoglRectangleMapEntry *rectangle) { unsigned int rectangle_size = width * height; /* Stack of nodes to search in */ GArray *stack = map->stack; CoglRectangleMapNode *found_node = NULL; /* Zero-sized rectangles break the algorithm for removing rectangles so we'll disallow them */ g_return_val_if_fail (width > 0 && height > 0, FALSE); /* Start with the root node */ g_array_set_size (stack, 0); _cogl_rectangle_map_stack_push (stack, map->root, FALSE); /* Depth-first search for an empty node that is big enough */ while (stack->len > 0) { CoglRectangleMapStackEntry *stack_top; CoglRectangleMapNode *node; int next_index; /* Pop an entry off the stack */ stack_top = _cogl_rectangle_map_stack_get_top (stack); node = stack_top->node; next_index = stack_top->next_index; _cogl_rectangle_map_stack_pop (stack); /* Regardless of the type of the node, there's no point descending any further if the new rectangle won't fit within it */ if (node->rectangle.width >= width && node->rectangle.height >= height && node->largest_gap >= rectangle_size) { if (node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) { /* We've found a node we can use */ found_node = node; break; } else if (node->type == COGL_RECTANGLE_MAP_BRANCH) { if (next_index) /* Try the right branch */ _cogl_rectangle_map_stack_push (stack, node->d.branch.right, 0); else { /* Make sure we remember to try the right branch once we've finished descending the left branch */ _cogl_rectangle_map_stack_push (stack, node, 1); /* Try the left branch */ _cogl_rectangle_map_stack_push (stack, node->d.branch.left, 0); } } } } if (found_node) { CoglRectangleMapNode *node; /* Split according to whichever axis will leave us with the largest space */ if (found_node->rectangle.width - width > found_node->rectangle.height - height) { found_node = _cogl_rectangle_map_node_split_horizontally (found_node, width); found_node = _cogl_rectangle_map_node_split_vertically (found_node, height); } else { found_node = _cogl_rectangle_map_node_split_vertically (found_node, height); found_node = _cogl_rectangle_map_node_split_horizontally (found_node, width); } found_node->type = COGL_RECTANGLE_MAP_FILLED_LEAF; found_node->d.data = data; found_node->largest_gap = 0; if (rectangle) *rectangle = found_node->rectangle; /* Walk back up the tree and update the stored largest gap for the node's sub tree */ for (node = found_node->parent; node; node = node->parent) { /* This node is a parent so it should always be a branch */ g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH); node->largest_gap = MAX (node->d.branch.left->largest_gap, node->d.branch.right->largest_gap); } /* There is now an extra rectangle in the map */ map->n_rectangles++; /* and less space */ map->space_remaining -= rectangle_size; #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DUMP_ATLAS_IMAGE))) { _cogl_rectangle_map_dump_image (map); /* Dumping the rectangle map is really slow so we might as well verify the space remaining here as it is also quite slow */ _cogl_rectangle_map_verify (map); } #endif return TRUE; } else return FALSE; } void _cogl_rectangle_map_remove (CoglRectangleMap *map, const CoglRectangleMapEntry *rectangle) { CoglRectangleMapNode *node = map->root; unsigned int rectangle_size = rectangle->width * rectangle->height; /* We can do a binary-chop down the search tree to find the rectangle */ while (node->type == COGL_RECTANGLE_MAP_BRANCH) { CoglRectangleMapNode *left_node = node->d.branch.left; /* If and only if the rectangle is in the left node then the x,y position of the rectangle will be within the node's rectangle */ if (rectangle->x < left_node->rectangle.x + left_node->rectangle.width && rectangle->y < left_node->rectangle.y + left_node->rectangle.height) /* Go left */ node = left_node; else /* Go right */ node = node->d.branch.right; } /* Make sure we found the right node */ if (node->type != COGL_RECTANGLE_MAP_FILLED_LEAF || node->rectangle.x != rectangle->x || node->rectangle.y != rectangle->y || node->rectangle.width != rectangle->width || node->rectangle.height != rectangle->height) /* This should only happen if someone tried to remove a rectangle that was not in the map so something has gone wrong */ g_return_if_reached (); else { /* Convert the node back to an empty node */ if (map->value_destroy_func) map->value_destroy_func (node->d.data); node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; node->largest_gap = rectangle_size; /* Walk back up the tree combining branch nodes that have two empty leaves back into a single empty leaf */ for (node = node->parent; node; node = node->parent) { /* This node is a parent so it should always be a branch */ g_assert (node->type == COGL_RECTANGLE_MAP_BRANCH); if (node->d.branch.left->type == COGL_RECTANGLE_MAP_EMPTY_LEAF && node->d.branch.right->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) { _cogl_rectangle_map_node_free (node->d.branch.left); _cogl_rectangle_map_node_free (node->d.branch.right); node->type = COGL_RECTANGLE_MAP_EMPTY_LEAF; node->largest_gap = (node->rectangle.width * node->rectangle.height); } else break; } /* Reduce the amount of space remaining in all of the parents further up the chain */ for (; node; node = node->parent) node->largest_gap = MAX (node->d.branch.left->largest_gap, node->d.branch.right->largest_gap); /* There is now one less rectangle */ g_assert (map->n_rectangles > 0); map->n_rectangles--; /* and more space */ map->space_remaining += rectangle_size; } #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DUMP_ATLAS_IMAGE))) { _cogl_rectangle_map_dump_image (map); /* Dumping the rectangle map is really slow so we might as well verify the space remaining here as it is also quite slow */ _cogl_rectangle_map_verify (map); } #endif } unsigned int _cogl_rectangle_map_get_width (CoglRectangleMap *map) { return map->root->rectangle.width; } unsigned int _cogl_rectangle_map_get_height (CoglRectangleMap *map) { return map->root->rectangle.height; } unsigned int _cogl_rectangle_map_get_remaining_space (CoglRectangleMap *map) { return map->space_remaining; } unsigned int _cogl_rectangle_map_get_n_rectangles (CoglRectangleMap *map) { return map->n_rectangles; } static void _cogl_rectangle_map_internal_foreach (CoglRectangleMap *map, CoglRectangleMapInternalForeachCb func, void *data) { /* Stack of nodes to search in */ GArray *stack = map->stack; /* Start with the root node */ g_array_set_size (stack, 0); _cogl_rectangle_map_stack_push (stack, map->root, 0); /* Iterate all nodes depth-first */ while (stack->len > 0) { CoglRectangleMapStackEntry *stack_top = _cogl_rectangle_map_stack_get_top (stack); CoglRectangleMapNode *node = stack_top->node; switch (node->type) { case COGL_RECTANGLE_MAP_BRANCH: if (stack_top->next_index == 0) { /* Next time we come back to this node, go to the right */ stack_top->next_index = 1; /* Explore the left branch next */ _cogl_rectangle_map_stack_push (stack, node->d.branch.left, 0); } else if (stack_top->next_index == 1) { /* Next time we come back to this node, stop processing it */ stack_top->next_index = 2; /* Explore the right branch next */ _cogl_rectangle_map_stack_push (stack, node->d.branch.right, 0); } else { /* We're finished with this node so we can call the callback */ func (node, data); _cogl_rectangle_map_stack_pop (stack); } break; default: /* Some sort of leaf node, just call the callback */ func (node, data); _cogl_rectangle_map_stack_pop (stack); break; } } /* The stack should now be empty */ g_assert (stack->len == 0); } typedef struct _CoglRectangleMapForeachClosure { CoglRectangleMapCallback callback; void *data; } CoglRectangleMapForeachClosure; static void _cogl_rectangle_map_foreach_cb (CoglRectangleMapNode *node, void *data) { CoglRectangleMapForeachClosure *closure = data; if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF) closure->callback (&node->rectangle, node->d.data, closure->data); } void _cogl_rectangle_map_foreach (CoglRectangleMap *map, CoglRectangleMapCallback callback, void *data) { CoglRectangleMapForeachClosure closure; closure.callback = callback; closure.data = data; _cogl_rectangle_map_internal_foreach (map, _cogl_rectangle_map_foreach_cb, &closure); } static void _cogl_rectangle_map_free_cb (CoglRectangleMapNode *node, void *data) { CoglRectangleMap *map = data; if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF && map->value_destroy_func) map->value_destroy_func (node->d.data); _cogl_rectangle_map_node_free (node); } void _cogl_rectangle_map_free (CoglRectangleMap *map) { _cogl_rectangle_map_internal_foreach (map, _cogl_rectangle_map_free_cb, map); g_array_free (map->stack, TRUE); g_free (map); } #ifdef COGL_ENABLE_DEBUG static void _cogl_rectangle_map_dump_image_cb (CoglRectangleMapNode *node, void *data) { cairo_t *cr = data; if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF || node->type == COGL_RECTANGLE_MAP_EMPTY_LEAF) { /* Fill the rectangle using a different colour depending on whether the rectangle is used */ if (node->type == COGL_RECTANGLE_MAP_FILLED_LEAF) cairo_set_source_rgb (cr, 0.0, 0.0, 1.0); else cairo_set_source_rgb (cr, 0.0, 0.0, 0.0); cairo_rectangle (cr, node->rectangle.x, node->rectangle.y, node->rectangle.width, node->rectangle.height); cairo_fill_preserve (cr); /* Draw a white outline around the rectangle */ cairo_set_source_rgb (cr, 1.0, 1.0, 1.0); cairo_stroke (cr); } } static void _cogl_rectangle_map_dump_image (CoglRectangleMap *map) { /* This dumps a png to help visualize the map. Each leaf rectangle is drawn with a white outline. Unused leaves are filled in black and used leaves are blue */ cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_RGB24, _cogl_rectangle_map_get_width (map), _cogl_rectangle_map_get_height (map)); cairo_t *cr = cairo_create (surface); _cogl_rectangle_map_internal_foreach (map, _cogl_rectangle_map_dump_image_cb, cr); cairo_destroy (cr); cairo_surface_write_to_png (surface, "cogl-rectangle-map-dump.png"); cairo_surface_destroy (surface); } #endif /* COGL_ENABLE_DEBUG */ muffin-6.4.1/cogl/cogl/cogl-meta-texture.c0000664000175000017500000004771314723361714017340 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-texture.h" #include "cogl-matrix.h" #include "cogl-spans.h" #include "cogl-meta-texture.h" #include "cogl-texture-private.h" #include #include typedef struct _ForeachData { float meta_region_coords[4]; CoglPipelineWrapMode wrap_s; CoglPipelineWrapMode wrap_t; CoglMetaTextureCallback callback; void *user_data; int width; int height; CoglTexture *padded_textures[9]; const float *grid_slice_texture_coords; float slice_offset_s; float slice_offset_t; float slice_range_s; float slice_range_t; } ForeachData; static void padded_grid_repeat_cb (CoglTexture *slice_texture, const float *slice_texture_coords, const float *meta_coords, void *user_data) { ForeachData *data; float mapped_coords[4]; /* Ignore padding slices for the current grid */ if (!slice_texture) return; data = user_data; /* NB: the slice_texture_coords[] we get here will always be * normalized. * * We now need to map the normalized slice_texture_coords[] we have * here back to the real slice coordinates we saved in the previous * stage... */ mapped_coords[0] = slice_texture_coords[0] * data->slice_range_s + data->slice_offset_s; mapped_coords[1] = slice_texture_coords[1] * data->slice_range_t + data->slice_offset_t; mapped_coords[2] = slice_texture_coords[2] * data->slice_range_s + data->slice_offset_s; mapped_coords[3] = slice_texture_coords[3] * data->slice_range_t + data->slice_offset_t; data->callback (slice_texture, mapped_coords, meta_coords, data->user_data); } static int setup_padded_spans (CoglSpan *spans, float start, float end, float range, int *real_index) { int span_index = 0; if (start > 0) { spans[0].start = 0; spans[0].size = start; spans[0].waste = 0; span_index++; spans[1].start = spans[0].size; } else spans[span_index].start = 0; spans[span_index].size = end - start; spans[span_index].waste = 0; *real_index = span_index; span_index++; if (end < range) { spans[span_index].start = spans[span_index - 1].start + spans[span_index - 1].size; spans[span_index].size = range - end; spans[span_index].waste = 0; span_index++; } return span_index; } /* This handles each sub-texture within the range [0,1] of our * original meta texture and repeats each one separately across the * users requested virtual texture coordinates. * * A notable advantage of this approach is that we will batch * together callbacks corresponding to the same underlying slice * together. */ static void create_grid_and_repeat_cb (CoglTexture *slice_texture, const float *slice_texture_coords, const float *meta_coords, void *user_data) { ForeachData *data = user_data; CoglSpan x_spans[3]; int n_x_spans; int x_real_index; CoglSpan y_spans[3]; int n_y_spans; int y_real_index; /* NB: This callback is called for each slice of the meta-texture * in the range [0,1]. * * We define a "padded grid" for each slice of the meta-texture in * the range [0,1]. The x axis and y axis grid lines are defined * using CoglSpans. * * The padded grid maps over the meta-texture coordinates in the * range [0,1] but only contains one valid cell that corresponds to * current slice being iterated and all the surrounding cells just * provide padding. * * Once we've defined our padded grid we then repeat that across * the user's original region, calling their callback whenever * we see our current slice - ignoring padding. * * NB: we can assume meta_coords[] are normalized at this point * since TextureRectangles aren't iterated with this code-path. * * NB: spans are always defined using non-normalized coordinates */ n_x_spans = setup_padded_spans (x_spans, meta_coords[0] * data->width, meta_coords[2] * data->width, data->width, &x_real_index); n_y_spans = setup_padded_spans (y_spans, meta_coords[1] * data->height, meta_coords[3] * data->height, data->height, &y_real_index); data->padded_textures[n_x_spans * y_real_index + x_real_index] = slice_texture; /* Our callback is going to be passed normalized slice texture * coordinates, and we will need to map the range [0,1] to the real * slice_texture_coords we have here... */ data->grid_slice_texture_coords = slice_texture_coords; data->slice_range_s = fabs (data->grid_slice_texture_coords[2] - data->grid_slice_texture_coords[0]); data->slice_range_t = fabs (data->grid_slice_texture_coords[3] - data->grid_slice_texture_coords[1]); data->slice_offset_s = MIN (data->grid_slice_texture_coords[0], data->grid_slice_texture_coords[2]); data->slice_offset_t = MIN (data->grid_slice_texture_coords[1], data->grid_slice_texture_coords[3]); /* Now actually iterate the region the user originally requested * using the current padded grid */ _cogl_texture_spans_foreach_in_region (x_spans, n_x_spans, y_spans, n_y_spans, data->padded_textures, data->meta_region_coords, data->width, data->height, data->wrap_s, data->wrap_t, padded_grid_repeat_cb, data); /* Clear the padded_textures ready for the next iteration */ data->padded_textures[n_x_spans * y_real_index + x_real_index] = NULL; } #define SWAP(A,B) do { float tmp = B; B = A; A = tmp; } while (0) typedef struct _ClampData { float start; float end; gboolean s_flipped; gboolean t_flipped; CoglMetaTextureCallback callback; void *user_data; } ClampData; static void clamp_s_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { ClampData *clamp_data = user_data; float mapped_meta_coords[4] = { clamp_data->start, meta_coords[1], clamp_data->end, meta_coords[3] }; if (clamp_data->s_flipped) SWAP (mapped_meta_coords[0], mapped_meta_coords[2]); /* NB: we never need to flip the t coords when dealing with * s-axis clamping so no need to check if ->t_flipped */ clamp_data->callback (sub_texture, sub_texture_coords, mapped_meta_coords, clamp_data->user_data); } static void clamp_t_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { ClampData *clamp_data = user_data; float mapped_meta_coords[4] = { meta_coords[0], clamp_data->start, meta_coords[2], clamp_data->end, }; if (clamp_data->s_flipped) SWAP (mapped_meta_coords[0], mapped_meta_coords[2]); if (clamp_data->t_flipped) SWAP (mapped_meta_coords[1], mapped_meta_coords[3]); clamp_data->callback (sub_texture, sub_texture_coords, mapped_meta_coords, clamp_data->user_data); } static gboolean foreach_clamped_region (CoglMetaTexture *meta_texture, float *tx_1, float *ty_1, float *tx_2, float *ty_2, CoglPipelineWrapMode wrap_s, CoglPipelineWrapMode wrap_t, CoglMetaTextureCallback callback, void *user_data) { float width = cogl_texture_get_width (COGL_TEXTURE (meta_texture)); ClampData clamp_data; /* Consider that *tx_1 may be > *tx_2 and to simplify things * we just flip them around if that's the case and keep a note * of the fact that they are flipped. */ if (*tx_1 > *tx_2) { SWAP (*tx_1, *tx_2); clamp_data.s_flipped = TRUE; } else clamp_data.s_flipped = FALSE; /* The same goes for ty_1 and ty_2... */ if (*ty_1 > *ty_2) { SWAP (*ty_1, *ty_2); clamp_data.t_flipped = TRUE; } else clamp_data.t_flipped = FALSE; clamp_data.callback = callback; clamp_data.user_data = user_data; if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) { float max_s_coord = 1.0; float half_texel_width; half_texel_width = max_s_coord / (width * 2); /* Handle any left clamped region */ if (*tx_1 < 0) { clamp_data.start = *tx_1; clamp_data.end = MIN (0, *tx_2);; cogl_meta_texture_foreach_in_region (meta_texture, half_texel_width, *ty_1, half_texel_width, *ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, wrap_t, clamp_s_cb, &clamp_data); /* Have we handled everything? */ if (*tx_2 <= 0) return TRUE; /* clamp tx_1 since we've handled everything with x < 0 */ *tx_1 = 0; } /* Handle any right clamped region - including the corners */ if (*tx_2 > max_s_coord) { clamp_data.start = MAX (max_s_coord, *tx_1); clamp_data.end = *tx_2; cogl_meta_texture_foreach_in_region (meta_texture, max_s_coord - half_texel_width, *ty_1, max_s_coord - half_texel_width, *ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, wrap_t, clamp_s_cb, &clamp_data); /* Have we handled everything? */ if (*tx_1 >= max_s_coord) return TRUE; /* clamp tx_2 since we've handled everything with x > * max_s_coord */ *tx_2 = max_s_coord; } } if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) { float height = cogl_texture_get_height (COGL_TEXTURE (meta_texture)); float max_t_coord = 1.0; float half_texel_height; half_texel_height = max_t_coord / (height * 2); /* Handle any top clamped region */ if (*ty_1 < 0) { clamp_data.start = *ty_1; clamp_data.end = MIN (0, *ty_2);; cogl_meta_texture_foreach_in_region (meta_texture, *tx_1, half_texel_height, *tx_2, half_texel_height, wrap_s, COGL_PIPELINE_WRAP_MODE_REPEAT, clamp_t_cb, &clamp_data); /* Have we handled everything? */ if (*tx_2 <= 0) return TRUE; /* clamp ty_1 since we've handled everything with y < 0 */ *ty_1 = 0; } /* Handle any bottom clamped region */ if (*ty_2 > max_t_coord) { clamp_data.start = MAX (max_t_coord, *ty_1);; clamp_data.end = *ty_2; cogl_meta_texture_foreach_in_region (meta_texture, *tx_1, max_t_coord - half_texel_height, *tx_2, max_t_coord - half_texel_height, wrap_s, COGL_PIPELINE_WRAP_MODE_REPEAT, clamp_t_cb, &clamp_data); /* Have we handled everything? */ if (*ty_1 >= max_t_coord) return TRUE; /* clamp ty_2 since we've handled everything with y > * max_t_coord */ *ty_2 = max_t_coord; } } if (clamp_data.s_flipped) SWAP (*tx_1, *tx_2); if (clamp_data.t_flipped) SWAP (*ty_1, *ty_2); return FALSE; } typedef struct _NormalizeData { CoglMetaTextureCallback callback; void *user_data; float s_normalize_factor; float t_normalize_factor; } NormalizeData; static void normalize_meta_coords_cb (CoglTexture *slice_texture, const float *slice_coords, const float *meta_coords, void *user_data) { NormalizeData *data = user_data; float normalized_meta_coords[4] = { meta_coords[0] * data->s_normalize_factor, meta_coords[1] * data->t_normalize_factor, meta_coords[2] * data->s_normalize_factor, meta_coords[3] * data->t_normalize_factor }; data->callback (slice_texture, slice_coords, normalized_meta_coords, data->user_data); } void cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture, float tx_1, float ty_1, float tx_2, float ty_2, CoglPipelineWrapMode wrap_s, CoglPipelineWrapMode wrap_t, CoglMetaTextureCallback callback, void *user_data) { CoglTexture *texture = COGL_TEXTURE (meta_texture); float width = cogl_texture_get_width (texture); float height = cogl_texture_get_height (texture); NormalizeData normalize_data; if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) wrap_s = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) wrap_t = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE || wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) { gboolean finished = foreach_clamped_region (meta_texture, &tx_1, &ty_1, &tx_2, &ty_2, wrap_s, wrap_t, callback, user_data); if (finished) return; /* Since clamping has been handled we now want to normalize our * wrap modes we se can assume from this point on we don't * need to consider CLAMP_TO_EDGE. (NB: The spans code will * assert that CLAMP_TO_EDGE isn't requested) */ if (wrap_s == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT; if (wrap_t == COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE) wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT; } /* It makes things simpler to deal with non-normalized region * coordinates beyond this point and only re-normalize just before * calling the user's callback... */ normalize_data.callback = callback; normalize_data.user_data = user_data; normalize_data.s_normalize_factor = 1.0f / width; normalize_data.t_normalize_factor = 1.0f / height; callback = normalize_meta_coords_cb; user_data = &normalize_data; tx_1 *= width; ty_1 *= height; tx_2 *= width; ty_2 *= height; /* XXX: at some point this wont be routed through the CoglTexture * vtable, instead there will be a separate CoglMetaTexture * interface vtable. */ if (texture->vtable->foreach_sub_texture_in_region) { ForeachData data; data.meta_region_coords[0] = tx_1; data.meta_region_coords[1] = ty_1; data.meta_region_coords[2] = tx_2; data.meta_region_coords[3] = ty_2; data.wrap_s = wrap_s; data.wrap_t = wrap_t; data.callback = callback; data.user_data = user_data; data.width = width; data.height = height; memset (data.padded_textures, 0, sizeof (data.padded_textures)); /* * 1) We iterate all the slices of the meta-texture only within * the range [0,1]. * * 2) We define a "padded grid" for each slice of the * meta-texture in the range [0,1]. * * The padded grid maps over the meta-texture coordinates in * the range [0,1] but only contains one valid cell that * corresponds to current slice being iterated and all the * surrounding cells just provide padding. * * 3) Once we've defined our padded grid we then repeat that * across the user's original region, calling their callback * whenever we see our current slice - ignoring padding. * * A notable benefit of this design is that repeating a texture * made of multiple slices will result in us repeating each * slice in-turn so the user gets repeat callbacks for the same * texture batched together. For manual emulation of texture * repeats done by drawing geometry this makes it more likely * that we can batch geometry. */ texture->vtable->foreach_sub_texture_in_region (texture, 0, 0, 1, 1, create_grid_and_repeat_cb, &data); } else { CoglSpan x_span = { 0, width, 0 }; CoglSpan y_span = { 0, height, 0 }; float meta_region_coords[4] = { tx_1, ty_1, tx_2, ty_2 }; _cogl_texture_spans_foreach_in_region (&x_span, 1, &y_span, 1, &texture, meta_region_coords, width, height, wrap_s, wrap_t, callback, user_data); } } #undef SWAP muffin-6.4.1/cogl/cogl/cogl-atlas-texture.h0000664000175000017500000002225614723361714017516 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef _COGL_ATLAS_TEXTURE_H_ #define _COGL_ATLAS_TEXTURE_H_ #include #include G_BEGIN_DECLS /** * SECTION:cogl-atlas-texture * @short_description: Functions for managing textures in Cogl's global * set of texture atlases * * A texture atlas is a texture that contains many smaller images that * an application is interested in. These are packed together as a way * of optimizing drawing with those images by avoiding the costs of * repeatedly telling the hardware to change what texture it should * sample from. This can enable more geometry to be batched together * into few draw calls. * * Each #CoglContext has an shared, pool of texture atlases that are * are managed by Cogl. * * This api lets applications upload texture data into one of Cogl's * shared texture atlases using a high-level #CoglAtlasTexture which * represents a sub-region of one of these atlases. * * A #CoglAtlasTexture is a high-level meta texture which has * some limitations to be aware of. Please see the documentation for * #CoglMetaTexture for more details. */ typedef struct _CoglAtlasTexture CoglAtlasTexture; #define COGL_ATLAS_TEXTURE(tex) ((CoglAtlasTexture *) tex) /** * cogl_atlas_texture_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_atlas_texture_get_gtype (void); /** * cogl_atlas_texture_new_with_size: * @ctx: A #CoglContext * @width: The width of your atlased texture. * @height: The height of your atlased texture. * * Creates a #CoglAtlasTexture with a given @width and @height. A * #CoglAtlasTexture represents a sub-region within one of Cogl's * shared texture atlases. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Allocate call can fail if Cogl considers the internal * format to be incompatible with the format of its internal * atlases. * * The returned #CoglAtlasTexture is a high-level meta-texture * with some limitations. See the documentation for #CoglMetaTexture * for more details. * * Returns: (transfer full): A new #CoglAtlasTexture object. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglAtlasTexture * cogl_atlas_texture_new_with_size (CoglContext *ctx, int width, int height); /** * cogl_atlas_texture_new_from_file: * @ctx: A #CoglContext * @filename: the file to load * @error: A #GError to catch exceptional errors or %NULL * * Creates a #CoglAtlasTexture from an image file. A #CoglAtlasTexture * represents a sub-region within one of Cogl's shared texture * atlases. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Allocate call can fail if Cogl considers the internal * format to be incompatible with the format of its internal * atlases. * * The returned #CoglAtlasTexture is a high-level meta-texture * with some limitations. See the documentation for #CoglMetaTexture * for more details. * * Return value: (transfer full): A new #CoglAtlasTexture object or * %NULL on failure and @error will be updated. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglAtlasTexture * cogl_atlas_texture_new_from_file (CoglContext *ctx, const char *filename, GError **error); /** * cogl_atlas_texture_new_from_data: * @ctx: A #CoglContext * @width: width of texture in pixels * @height: height of texture in pixels * @format: the #CoglPixelFormat the buffer is stored in in RAM * @rowstride: the memory offset in bytes between the start of each * row in @data. A value of 0 will make Cogl automatically * calculate @rowstride from @width and @format. * @data: pointer to the memory region where the source buffer resides * @error: A #GError to catch exceptional errors or %NULL * * Creates a new #CoglAtlasTexture texture based on data residing in * memory. A #CoglAtlasTexture represents a sub-region within one of * Cogl's shared texture atlases. * * This api will always immediately allocate GPU memory for the * texture and upload the given data so that the @data pointer does * not need to remain valid once this function returns. This means it * is not possible to configure the texture before it is allocated. If * you do need to configure the texture before allocation (to specify * constraints on the internal format for example) then you can * instead create a #CoglBitmap for your data and use * cogl_atlas_texture_new_from_bitmap() or use * cogl_atlas_texture_new_with_size() and then upload data using * cogl_texture_set_data() * * Allocate call can fail if Cogl considers the internal * format to be incompatible with the format of its internal * atlases. * * The returned #CoglAtlasTexture is a high-level * meta-texture with some limitations. See the documentation for * #CoglMetaTexture for more details. * * Return value: (transfer full): A new #CoglAtlasTexture object or * %NULL on failure and @error will be updated. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglAtlasTexture * cogl_atlas_texture_new_from_data (CoglContext *ctx, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error); /** * cogl_atlas_texture_new_from_bitmap: * @bitmap: A #CoglBitmap * * Creates a new #CoglAtlasTexture texture based on data residing in a * @bitmap. A #CoglAtlasTexture represents a sub-region within one of * Cogl's shared texture atlases. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or preferably let Cogl * automatically allocate storage lazily when it may know more about * how the texture is being used and can optimize how it is allocated. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Allocate call can fail if Cogl considers the internal * format to be incompatible with the format of its internal * atlases. * * The returned #CoglAtlasTexture is a high-level meta-texture * with some limitations. See the documentation for #CoglMetaTexture * for more details. * * Returns: (transfer full): A new #CoglAtlasTexture object. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglAtlasTexture * cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp); /** * cogl_is_atlas_texture: * @object: a #CoglObject * * Checks whether the given object references a #CoglAtlasTexture * * Return value: %TRUE if the passed object represents an atlas * texture and %FALSE otherwise * * Since: 1.16 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_atlas_texture (void *object); G_END_DECLS #endif /* _COGL_ATLAS_TEXTURE_H_ */ muffin-6.4.1/cogl/cogl/deprecated/0000775000175000017500000000000014723361714015712 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/deprecated/cogl-framebuffer-deprecated.h0000664000175000017500000001301714723361714023371 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2014 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_FRAMEBUFFER_DEPRECATED_H__ #define __COGL_FRAMEBUFFER_DEPRECATED_H__ #include #include G_BEGIN_DECLS /** * cogl_set_framebuffer: (skip) * @buffer: A #CoglFramebuffer object, either onscreen or offscreen. * * This redirects all subsequent drawing to the specified framebuffer. This can * either be an offscreen buffer created with cogl_offscreen_new_to_texture () * or in the future it may be an onscreen framebuffers too. * * Since: 1.2 * Deprecated: 1.16: The latest drawing apis take explicit * #CoglFramebuffer arguments so this stack of * framebuffers shouldn't be used anymore. */ COGL_DEPRECATED void cogl_set_framebuffer (CoglFramebuffer *buffer); /** * cogl_push_framebuffer: (skip) * @buffer: A #CoglFramebuffer object, either onscreen or offscreen. * * Redirects all subsequent drawing to the specified framebuffer. This can * either be an offscreen buffer created with cogl_offscreen_new_to_texture () * or in the future it may be an onscreen framebuffer too. * * You should understand that a framebuffer owns the following state: * * The projection matrix * The modelview matrix stack * The viewport * The clip stack * * So these items will automatically be saved and restored when you * push and pop between different framebuffers. * * Also remember a newly allocated framebuffer will have an identity matrix for * the projection and modelview matrices which gives you a coordinate space * like OpenGL with (-1, -1) corresponding to the top left of the viewport, * (1, 1) corresponding to the bottom right and +z coming out towards the * viewer. * * If you want to set up a coordinate space like Clutter does with (0, 0) * corresponding to the top left and (framebuffer_width, framebuffer_height) * corresponding to the bottom right you can do so like this: * * |[ * static void * setup_viewport (unsigned int width, * unsigned int height, * float fovy, * float aspect, * float z_near, * float z_far) * { * float z_camera; * CoglMatrix projection_matrix; * CoglMatrix mv_matrix; * * cogl_set_viewport (0, 0, width, height); * cogl_perspective (fovy, aspect, z_near, z_far); * * cogl_get_projection_matrix (&projection_matrix); * z_camera = 0.5 * projection_matrix.xx; * * cogl_matrix_init_identity (&mv_matrix); * cogl_matrix_translate (&mv_matrix, -0.5f, -0.5f, -z_camera); * cogl_matrix_scale (&mv_matrix, 1.0f / width, -1.0f / height, 1.0f / width); * cogl_matrix_translate (&mv_matrix, 0.0f, -1.0 * height, 0.0f); * cogl_set_modelview_matrix (&mv_matrix); * } * * static void * my_init_framebuffer (ClutterStage *stage, * CoglFramebuffer *framebuffer, * unsigned int framebuffer_width, * unsigned int framebuffer_height) * { * ClutterPerspective perspective; * * clutter_stage_get_perspective (stage, &perspective); * * cogl_push_framebuffer (framebuffer); * setup_viewport (framebuffer_width, * framebuffer_height, * perspective.fovy, * perspective.aspect, * perspective.z_near, * perspective.z_far); * } * ]| * * The previous framebuffer can be restored by calling cogl_pop_framebuffer() * * Since: 1.2 * Deprecated: 1.16: The latest drawing apis take explicit * #CoglFramebuffer arguments so this stack of * framebuffers shouldn't be used anymore. */ COGL_DEPRECATED void cogl_push_framebuffer (CoglFramebuffer *buffer); /** * cogl_pop_framebuffer: (skip) * * Restores the framebuffer that was previously at the top of the stack. * All subsequent drawing will be redirected to this framebuffer. * * Since: 1.2 * Deprecated: 1.16: The latest drawing apis take explicit * #CoglFramebuffer arguments so this stack of * framebuffers shouldn't be used anymore. */ COGL_DEPRECATED void cogl_pop_framebuffer (void); G_END_DECLS #endif /* __COGL_FRAMEBUFFER_DEPRECATED_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-type-casts.h0000664000175000017500000000407214723361714021104 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_TYPE_CASTS_H__ #define __COGL_TYPE_CASTS_H__ /* The various interface types in Cogl used to be more strongly typed * which required lots type casting by developers. We provided * macros for performing these casts following a widely used Gnome * coding style. Since we now consistently typedef these interfaces * as void for the public C api and use runtime type checking to * catch programming errors the casts have become redundant and * so these macros are only kept for compatibility... */ #if !defined(COGL_ENABLE_MUTTER_API) && !defined(COGL_GIR_SCANNING) #define COGL_FRAMEBUFFER(X) (X) #define COGL_BUFFER(X) (X) #define COGL_TEXTURE(X) (X) #define COGL_META_TEXTURE(X) (X) #define COGL_PRIMITIVE_TEXTURE(X) (X) #endif #endif /* __COGL_TYPE_CASTS_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-shader.h0000664000175000017500000003741314723361714020263 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_SHADER_H__ #define __COGL_SHADER_H__ #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-shaders * @short_description: Fuctions for accessing the programmable GL pipeline * * Cogl allows accessing the GL programmable pipeline in order to create * vertex and fragment shaders. * * When using GLSL Cogl provides replacement names for most of the * builtin varyings and uniforms. It is recommended to use these names * wherever possible to increase portability between OpenGL 2.0 and * GLES 2.0. GLES 2.0 does not have most of the builtins under their * original names so they will only work with the Cogl names. * * For use in all GLSL shaders, the Cogl builtins are as follows: * * * * * uniform mat4 * cogl_modelview_matrix * * The current modelview matrix. This is equivalent to * #gl_ModelViewMatrix. * * * * uniform mat4 * cogl_projection_matrix * * The current projection matrix. This is equivalent to * #gl_ProjectionMatrix. * * * * uniform mat4 * cogl_modelview_projection_matrix * * The combined modelview and projection matrix. A vertex shader * would typically use this to transform the incoming vertex * position. The separate modelview and projection matrices are * usually only needed for lighting calculations. This is * equivalent to #gl_ModelViewProjectionMatrix. * * * * uniform mat4 * cogl_texture_matrix[] * * An array of matrices for transforming the texture * coordinates. This is equivalent to #gl_TextureMatrix. * * * * * * In a vertex shader, the following are also available: * * * * * attribute vec4 * cogl_position_in * * The incoming vertex position. This is equivalent to #gl_Vertex. * * * * attribute vec4 * cogl_color_in * * The incoming vertex color. This is equivalent to #gl_Color. * * * * attribute vec4 * cogl_tex_coord_in * * The texture coordinate for the first texture unit. This is * equivalent to #gl_MultiTexCoord0. * * * * attribute vec4 * cogl_tex_coord0_in * * The texture coordinate for the first texture unit. This is * equivalent to #gl_MultiTexCoord0. There is also * #cogl_tex_coord1_in and so on. * * * * attribute vec3 * cogl_normal_in * * The normal of the vertex. This is equivalent to #gl_Normal. * * * * vec4 * cogl_position_out * * The calculated position of the vertex. This must be written to * in all vertex shaders. This is equivalent to #gl_Position. * * * * float * cogl_point_size_out * * The calculated size of a point. This is equivalent to #gl_PointSize. * * * * varying vec4 * cogl_color_out * * The calculated color of a vertex. This is equivalent to #gl_FrontColor. * * * * varying vec4 * cogl_tex_coord_out[] * * An array of calculated texture coordinates for a vertex. This is * equivalent to #gl_TexCoord. * * * * * * In a fragment shader, the following are also available: * * * * * varying vec4 cogl_color_in * * The calculated color of a vertex. This is equivalent to #gl_FrontColor. * * * * varying vec4 * cogl_tex_coord_in[] * * An array of calculated texture coordinates for a vertex. This is * equivalent to #gl_TexCoord. * * * * vec4 cogl_color_out * * The final calculated color of the fragment. All fragment shaders * must write to this variable. This is equivalent to * #gl_FrontColor. * * * * float cogl_depth_out * * An optional output variable specifying the depth value to use * for this fragment. This is equivalent to #gl_FragDepth. * * * * bool cogl_front_facing * * A readonly variable that will be true if the current primitive * is front facing. This can be used to implement two-sided * coloring algorithms. This is equivalent to #gl_FrontFacing. * * * * * * It's worth nothing that this API isn't what Cogl would like to have * in the long term and it may be removed in Cogl 2.0. The * experimental #CoglShader API is the proposed replacement. */ /** * CoglShaderType: * @COGL_SHADER_TYPE_VERTEX: A program for proccessing vertices * @COGL_SHADER_TYPE_FRAGMENT: A program for processing fragments * * Types of shaders * * Since: 1.0 */ typedef enum { COGL_SHADER_TYPE_VERTEX, COGL_SHADER_TYPE_FRAGMENT } CoglShaderType; /** * cogl_create_shader: * @shader_type: COGL_SHADER_TYPE_VERTEX or COGL_SHADER_TYPE_FRAGMENT. * * Create a new shader handle, use cogl_shader_source() to set the * source code to be used on it. * * Returns: a new shader handle. * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT CoglHandle cogl_create_shader (CoglShaderType shader_type); /** * cogl_is_shader: * @handle: A CoglHandle * * Gets whether the given handle references an existing shader object. * * Returns: %TRUE if the handle references a shader, * %FALSE otherwise * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT gboolean cogl_is_shader (CoglHandle handle); /** * cogl_shader_source: * @shader: #CoglHandle for a shader. * @source: Shader source. * * Replaces the current source associated with a shader with a new * one. * * Please see above * for a description of the recommended format for the shader code. * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_shader_source (CoglHandle shader, const char *source); /** * cogl_shader_get_type: * @handle: #CoglHandle for a shader. * * Retrieves the type of a shader #CoglHandle * * Return value: %COGL_SHADER_TYPE_VERTEX if the shader is a vertex processor * or %COGL_SHADER_TYPE_FRAGMENT if the shader is a frament processor * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT CoglShaderType cogl_shader_get_type (CoglHandle handle); /** * cogl_create_program: * * Create a new cogl program object that can be used to replace parts of the GL * rendering pipeline with custom code. * * Returns: a new cogl program. * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT CoglHandle cogl_create_program (void); /** * cogl_is_program: * @handle: A CoglHandle * * Gets whether the given handle references an existing program object. * * Returns: %TRUE if the handle references a program, * %FALSE otherwise * * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT gboolean cogl_is_program (CoglHandle handle); /** * cogl_program_attach_shader: * @program_handle: a #CoglHandle for a shdaer program. * @shader_handle: a #CoglHandle for a vertex of fragment shader. * * Attaches a shader to a program object. A program can have multiple * vertex or fragment shaders but only one of them may provide a * main() function. It is allowed to use a program with only a vertex * shader or only a fragment shader. * * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_attach_shader (CoglHandle program_handle, CoglHandle shader_handle); /** * cogl_program_link: * @handle: a #CoglHandle for a shader program. * * Links a program making it ready for use. Note that calling this * function is optional. If it is not called the program will * automatically be linked the first time it is used. * * Deprecated: 1.16: Use #CoglSnippet api */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_link (CoglHandle handle); /** * cogl_program_get_uniform_location: * @handle: a #CoglHandle for a shader program. * @uniform_name: the name of a uniform. * * Retrieve the location (offset) of a uniform variable in a shader program, * a uniform is a variable that is constant for all vertices/fragments for a * shader object and is possible to modify as an external parameter. * * Return value: the offset of a uniform in a specified program. * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT int cogl_program_get_uniform_location (CoglHandle handle, const char *uniform_name); /** * cogl_program_set_uniform_1f: * @program: A #CoglHandle for a linked program * @uniform_location: the uniform location retrieved from * cogl_program_get_uniform_location(). * @value: the new value of the uniform. * * Changes the value of a floating point uniform for the given linked * @program. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_set_uniform_1f (CoglHandle program, int uniform_location, float value); /** * cogl_program_set_uniform_1i: * @program: A #CoglHandle for a linked program * @uniform_location: the uniform location retrieved from * cogl_program_get_uniform_location(). * @value: the new value of the uniform. * * Changes the value of an integer uniform for the given linked * @program. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_set_uniform_1i (CoglHandle program, int uniform_location, int value); /** * cogl_program_set_uniform_float: * @program: A #CoglHandle for a linked program * @uniform_location: the uniform location retrieved from * cogl_program_get_uniform_location(). * @n_components: The number of components for the uniform. For * example with glsl you'd use 3 for a vec3 or 4 for a vec4. * @count: For uniform arrays this is the array length otherwise just * pass 1 * @value: (array length=count): the new value of the uniform[s]. * * Changes the value of a float vector uniform, or uniform array for * the given linked @program. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_set_uniform_float (CoglHandle program, int uniform_location, int n_components, int count, const float *value); /** * cogl_program_set_uniform_int: * @program: A #CoglHandle for a linked program * @uniform_location: the uniform location retrieved from * cogl_program_get_uniform_location(). * @n_components: The number of components for the uniform. For * example with glsl you'd use 3 for a vec3 or 4 for a vec4. * @count: For uniform arrays this is the array length otherwise just * pass 1 * @value: (array length=count): the new value of the uniform[s]. * * Changes the value of a int vector uniform, or uniform array for * the given linked @program. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_set_uniform_int (CoglHandle program, int uniform_location, int n_components, int count, const int *value); /** * cogl_program_set_uniform_matrix: * @program: A #CoglHandle for a linked program * @uniform_location: the uniform location retrieved from * cogl_program_get_uniform_location(). * @dimensions: The dimensions of the matrix. So for for example pass * 2 for a 2x2 matrix or 3 for 3x3. * @count: For uniform arrays this is the array length otherwise just * pass 1 * @transpose: Whether to transpose the matrix when setting the uniform. * @value: (array length=count): the new value of the uniform. * * Changes the value of a matrix uniform, or uniform array in the * given linked @program. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_program_set_uniform_matrix (CoglHandle program, int uniform_location, int dimensions, int count, gboolean transpose, const float *value); G_END_DECLS #endif /* __COGL_SHADER_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-program-private.h0000664000175000017500000000542114723361714022126 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PROGRAM_H #define __COGL_PROGRAM_H #include "cogl-object-private.h" #include "cogl-shader-private.h" typedef struct _CoglProgram CoglProgram; struct _CoglProgram { CoglHandleObject _parent; GSList *attached_shaders; GArray *custom_uniforms; /* An age counter that changes whenever the list of shaders is modified */ unsigned int age; }; typedef struct _CoglProgramUniform CoglProgramUniform; struct _CoglProgramUniform { char *name; CoglBoxedValue value; /* The cached GL location for this uniform. This is only valid between calls to _cogl_program_dirty_all_uniforms */ GLint location; /* Whether we have a location yet */ unsigned int location_valid : 1; /* Whether the uniform value has changed since the last time the uniforms were flushed */ unsigned int dirty : 1; }; /* Internal function to flush the custom uniforms for the given use program. This assumes the target GL program is already bound. The gl_program still needs to be passed so that CoglProgram can query the uniform locations. gl_program_changed should be set to TRUE if we are flushing the uniforms against a different GL program from the last time it was flushed. This will cause it to requery all of the locations and assume that all uniforms are dirty */ void _cogl_program_flush_uniforms (CoglProgram *program, GLuint gl_program, gboolean gl_program_changed); gboolean _cogl_program_has_fragment_shader (CoglHandle handle); gboolean _cogl_program_has_vertex_shader (CoglHandle handle); #endif /* __COGL_PROGRAM_H */ muffin-6.4.1/cogl/cogl/deprecated/cogl-auto-texture.c0000664000175000017500000002543714723361714021461 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2011,2012 Intel Corporation. * Copyright (C) 2010 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Matthew Allum * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-texture.h" #include "cogl-util.h" #include "cogl-texture-2d.h" #include "cogl-texture-2d-private.h" #include "cogl-primitive-texture.h" #include "cogl-texture-2d-sliced-private.h" #include "cogl-private.h" #include "cogl-object.h" #include "cogl-bitmap-private.h" #include "cogl-atlas-texture-private.h" #include "cogl-sub-texture.h" #include "deprecated/cogl-auto-texture.h" static CoglTexture * _cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglTextureFlags flags, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error); static void set_auto_mipmap_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture), FALSE); } CoglTexture * cogl_texture_new_with_size (unsigned int width, unsigned int height, CoglTextureFlags flags, CoglPixelFormat internal_format) { CoglTexture *tex; GError *skip_error = NULL; _COGL_GET_CONTEXT (ctx, NULL); /* First try creating a fast-path non-sliced texture */ tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); _cogl_texture_set_internal_format (tex, internal_format); if (!cogl_texture_allocate (tex, &skip_error)) { g_error_free (skip_error); skip_error = NULL; cogl_object_unref (tex); tex = NULL; } if (!tex) { /* If it fails resort to sliced textures */ int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; tex = COGL_TEXTURE (cogl_texture_2d_sliced_new_with_size (ctx, width, height, max_waste)); _cogl_texture_set_internal_format (tex, internal_format); } /* NB: This api existed before Cogl introduced lazy allocation of * textures and so we maintain its original synchronous allocation * semantics and return NULL if allocation fails... */ if (!cogl_texture_allocate (tex, &skip_error)) { g_error_free (skip_error); cogl_object_unref (tex); return NULL; } if (tex && flags & COGL_TEXTURE_NO_AUTO_MIPMAP) { cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, set_auto_mipmap_cb, NULL); } return tex; } static CoglTexture * _cogl_texture_new_from_data (CoglContext *ctx, int width, int height, CoglTextureFlags flags, CoglPixelFormat format, CoglPixelFormat internal_format, int rowstride, const uint8_t *data, GError **error) { CoglBitmap *bmp; CoglTexture *tex; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); g_return_val_if_fail (data != NULL, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); /* Wrap the data into a bitmap */ bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); tex = _cogl_texture_new_from_bitmap (bmp, flags, internal_format, FALSE, /* can't convert in place */ error); cogl_object_unref (bmp); return tex; } CoglTexture * cogl_texture_new_from_data (int width, int height, CoglTextureFlags flags, CoglPixelFormat format, CoglPixelFormat internal_format, int rowstride, const uint8_t *data) { GError *ignore_error = NULL; CoglTexture *tex; _COGL_GET_CONTEXT (ctx, NULL); tex = _cogl_texture_new_from_data (ctx, width, height, flags, format, internal_format, rowstride, data, &ignore_error); if (!tex) g_error_free (ignore_error); return tex; } static CoglTexture * _cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglTextureFlags flags, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error) { CoglTexture *tex; GError *internal_error = NULL; if (!flags && !COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_ATLAS)) { /* First try putting the texture in the atlas */ CoglAtlasTexture *atlas_tex = _cogl_atlas_texture_new_from_bitmap (bitmap, can_convert_in_place); _cogl_texture_set_internal_format (COGL_TEXTURE (atlas_tex), internal_format); if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) return COGL_TEXTURE (atlas_tex); g_error_free (internal_error); internal_error = NULL; cogl_object_unref (atlas_tex); } /* If that doesn't work try a fast path 2D texture */ tex = COGL_TEXTURE (_cogl_texture_2d_new_from_bitmap (bitmap, can_convert_in_place)); _cogl_texture_set_internal_format (tex, internal_format); if (!cogl_texture_allocate (tex, &internal_error)) { g_error_free (internal_error); internal_error = NULL; cogl_object_unref (tex); tex = NULL; } if (!tex) { /* Otherwise create a sliced texture */ int max_waste = flags & COGL_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; tex = COGL_TEXTURE (_cogl_texture_2d_sliced_new_from_bitmap (bitmap, max_waste, can_convert_in_place)); _cogl_texture_set_internal_format (tex, internal_format); if (!cogl_texture_allocate (tex, error)) { cogl_object_unref (tex); tex = NULL; } } if (tex && flags & COGL_TEXTURE_NO_AUTO_MIPMAP) { cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, set_auto_mipmap_cb, NULL); } return tex; } CoglTexture * cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglTextureFlags flags, CoglPixelFormat internal_format) { GError *ignore_error = NULL; CoglTexture *tex = _cogl_texture_new_from_bitmap (bitmap, flags, internal_format, FALSE, /* can't convert in-place */ &ignore_error); if (!tex) g_error_free (ignore_error); return tex; } CoglTexture * cogl_texture_new_from_file (const char *filename, CoglTextureFlags flags, CoglPixelFormat internal_format, GError **error) { CoglBitmap *bmp; CoglTexture *texture = NULL; _COGL_GET_CONTEXT (ctx, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); bmp = cogl_bitmap_new_from_file (filename, error); if (bmp == NULL) return NULL; texture = _cogl_texture_new_from_bitmap (bmp, flags, internal_format, TRUE, /* can convert in-place */ error); cogl_object_unref (bmp); return texture; } CoglTexture * cogl_texture_new_from_sub_texture (CoglTexture *full_texture, int sub_x, int sub_y, int sub_width, int sub_height) { _COGL_GET_CONTEXT (ctx, NULL); return COGL_TEXTURE (cogl_sub_texture_new (ctx, full_texture, sub_x, sub_y, sub_width, sub_height)); } muffin-6.4.1/cogl/cogl/deprecated/cogl-shader.c0000664000175000017500000001264414723361714020255 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-glsl-shader-private.h" #include "cogl-glsl-shader-boilerplate.h" #include "driver/gl/cogl-util-gl-private.h" #include "deprecated/cogl-shader-private.h" #include #include static void _cogl_shader_free (CoglShader *shader); COGL_HANDLE_DEFINE (Shader, shader); #ifndef GL_FRAGMENT_SHADER #define GL_FRAGMENT_SHADER 0x8B30 #endif #ifndef GL_VERTEX_SHADER #define GL_VERTEX_SHADER 0x8B31 #endif static void _cogl_shader_free (CoglShader *shader) { /* Frees shader resources but its handle is not released! Do that separately before this! */ _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader->gl_handle) GE (ctx, glDeleteShader (shader->gl_handle)); g_slice_free (CoglShader, shader); } CoglHandle cogl_create_shader (CoglShaderType type) { CoglShader *shader; _COGL_GET_CONTEXT (ctx, NULL); switch (type) { case COGL_SHADER_TYPE_VERTEX: case COGL_SHADER_TYPE_FRAGMENT: break; default: g_warning ("Unexpected shader type (0x%08lX) given to " "cogl_create_shader", (unsigned long) type); return NULL; } shader = g_slice_new (CoglShader); shader->gl_handle = 0; shader->compilation_pipeline = NULL; shader->type = type; return _cogl_shader_handle_new (shader); } static void delete_shader (CoglShader *shader) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader->gl_handle) GE (ctx, glDeleteShader (shader->gl_handle)); shader->gl_handle = 0; if (shader->compilation_pipeline) { cogl_object_unref (shader->compilation_pipeline); shader->compilation_pipeline = NULL; } } void cogl_shader_source (CoglHandle handle, const char *source) { CoglShader *shader; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!cogl_is_shader (handle)) return; shader = handle; shader->source = g_strdup (source); } void _cogl_shader_compile_real (CoglHandle handle, CoglPipeline *pipeline) { CoglShader *shader = handle; GLenum gl_type; GLint status; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader->gl_handle) { CoglPipeline *prev = shader->compilation_pipeline; /* XXX: currently the only things that will affect the * boilerplate for user shaders, apart from driver features, * are the pipeline layer-indices and texture-unit-indices */ if (pipeline == prev || _cogl_pipeline_layer_and_unit_numbers_equal (prev, pipeline)) return; } if (shader->gl_handle) delete_shader (shader); switch (shader->type) { case COGL_SHADER_TYPE_VERTEX: gl_type = GL_VERTEX_SHADER; break; case COGL_SHADER_TYPE_FRAGMENT: gl_type = GL_FRAGMENT_SHADER; break; default: g_assert_not_reached (); break; } shader->gl_handle = ctx->glCreateShader (gl_type); _cogl_glsl_shader_set_source_with_boilerplate (ctx, shader->gl_handle, gl_type, pipeline, 1, (const char **) &shader->source, NULL); GE (ctx, glCompileShader (shader->gl_handle)); shader->compilation_pipeline = cogl_object_ref (pipeline); GE (ctx, glGetShaderiv (shader->gl_handle, GL_COMPILE_STATUS, &status)); if (!status) { char buffer[512]; int len = 0; ctx->glGetShaderInfoLog (shader->gl_handle, 511, &len, buffer); buffer[len] = '\0'; g_warning ("Failed to compile GLSL program:\n" "src:\n%s\n" "error:\n%s\n", shader->source, buffer); } } CoglShaderType cogl_shader_get_type (CoglHandle handle) { CoglShader *shader; _COGL_GET_CONTEXT (ctx, COGL_SHADER_TYPE_VERTEX); if (!cogl_is_shader (handle)) { g_warning ("Non shader handle type passed to cogl_shader_get_type"); return COGL_SHADER_TYPE_VERTEX; } shader = handle; return shader->type; } muffin-6.4.1/cogl/cogl/deprecated/cogl-clutter.h0000664000175000017500000000313714723361714020473 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_CLUTTER_H__ #define __COGL_CLUTTER_H__ G_BEGIN_DECLS #define cogl_clutter_winsys_has_feature cogl_clutter_winsys_has_feature_CLUTTER COGL_DEPRECATED_FOR (cogl_has_feature) COGL_EXPORT gboolean cogl_clutter_winsys_has_feature (CoglWinsysFeature feature); G_END_DECLS #endif /* __COGL_CLUTTER_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-program.c0000664000175000017500000002162714723361714020457 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-util.h" #include "driver/gl/cogl-util-gl-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "deprecated/cogl-shader-private.h" #include "deprecated/cogl-program-private.h" #include static void _cogl_program_free (CoglProgram *program); COGL_HANDLE_DEFINE (Program, program); /* A CoglProgram is effectively just a list of shaders that will be used together and a set of values for the custom uniforms. No actual GL program is created - instead this is the responsibility of the GLSL material backend. The uniform values are collected in an array and then flushed whenever the material backend requests it. */ static void _cogl_program_free (CoglProgram *program) { int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Unref all of the attached shaders and destroy the list */ g_slist_free_full (program->attached_shaders, cogl_object_unref); for (i = 0; i < program->custom_uniforms->len; i++) { CoglProgramUniform *uniform = &g_array_index (program->custom_uniforms, CoglProgramUniform, i); g_free (uniform->name); if (uniform->value.count > 1) g_free (uniform->value.v.array); } g_array_free (program->custom_uniforms, TRUE); g_slice_free (CoglProgram, program); } CoglHandle cogl_create_program (void) { CoglProgram *program; program = g_slice_new0 (CoglProgram); program->custom_uniforms = g_array_new (FALSE, FALSE, sizeof (CoglProgramUniform)); program->age = 0; return _cogl_program_handle_new (program); } void cogl_program_attach_shader (CoglHandle program_handle, CoglHandle shader_handle) { CoglProgram *program; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!cogl_is_program (program_handle) || !cogl_is_shader (shader_handle)) return; program = program_handle; program->attached_shaders = g_slist_prepend (program->attached_shaders, cogl_object_ref (shader_handle)); program->age++; } void cogl_program_link (CoglHandle handle) { /* There's no point in linking the program here because it will have to be relinked with a different fixed functionality shader whenever the settings change */ } int cogl_program_get_uniform_location (CoglHandle handle, const char *uniform_name) { int i; CoglProgram *program; CoglProgramUniform *uniform; if (!cogl_is_program (handle)) return -1; program = handle; /* We can't just ask the GL program object for the uniform location directly because it will change every time the program is linked with a different shader. Instead we make our own mapping of uniform numbers and cache the names */ for (i = 0; i < program->custom_uniforms->len; i++) { uniform = &g_array_index (program->custom_uniforms, CoglProgramUniform, i); if (!strcmp (uniform->name, uniform_name)) return i; } /* Create a new uniform with the given name */ g_array_set_size (program->custom_uniforms, program->custom_uniforms->len + 1); uniform = &g_array_index (program->custom_uniforms, CoglProgramUniform, program->custom_uniforms->len - 1); uniform->name = g_strdup (uniform_name); memset (&uniform->value, 0, sizeof (CoglBoxedValue)); uniform->dirty = TRUE; uniform->location_valid = FALSE; return program->custom_uniforms->len - 1; } static CoglProgramUniform * cogl_program_modify_uniform (CoglProgram *program, int uniform_no) { CoglProgramUniform *uniform; g_return_val_if_fail (cogl_is_program (program), NULL); g_return_val_if_fail (uniform_no >= 0 && uniform_no < program->custom_uniforms->len, NULL); uniform = &g_array_index (program->custom_uniforms, CoglProgramUniform, uniform_no); uniform->dirty = TRUE; return uniform; } void cogl_program_set_uniform_1f (CoglHandle handle, int uniform_location, float value) { CoglProgramUniform *uniform; uniform = cogl_program_modify_uniform (handle, uniform_location); _cogl_boxed_value_set_1f (&uniform->value, value); } void cogl_program_set_uniform_1i (CoglHandle handle, int uniform_location, int value) { CoglProgramUniform *uniform; uniform = cogl_program_modify_uniform (handle, uniform_location); _cogl_boxed_value_set_1i (&uniform->value, value); } void cogl_program_set_uniform_float (CoglHandle handle, int uniform_location, int n_components, int count, const float *value) { CoglProgramUniform *uniform; uniform = cogl_program_modify_uniform (handle, uniform_location); _cogl_boxed_value_set_float (&uniform->value, n_components, count, value); } void cogl_program_set_uniform_int (CoglHandle handle, int uniform_location, int n_components, int count, const int *value) { CoglProgramUniform *uniform; uniform = cogl_program_modify_uniform (handle, uniform_location); _cogl_boxed_value_set_int (&uniform->value, n_components, count, value); } void cogl_program_set_uniform_matrix (CoglHandle handle, int uniform_location, int dimensions, int count, gboolean transpose, const float *value) { CoglProgramUniform *uniform; uniform = cogl_program_modify_uniform (handle, uniform_location); _cogl_boxed_value_set_matrix (&uniform->value, dimensions, count, transpose, value); } void _cogl_program_flush_uniforms (CoglProgram *program, GLuint gl_program, gboolean gl_program_changed) { CoglProgramUniform *uniform; int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); for (i = 0; i < program->custom_uniforms->len; i++) { uniform = &g_array_index (program->custom_uniforms, CoglProgramUniform, i); if (gl_program_changed || uniform->dirty) { if (gl_program_changed || !uniform->location_valid) { uniform->location = ctx->glGetUniformLocation (gl_program, uniform->name); uniform->location_valid = TRUE; } /* If the uniform isn't really in the program then there's no need to actually set it */ if (uniform->location != -1) { _cogl_boxed_value_set_uniform (ctx, uniform->location, &uniform->value); } uniform->dirty = FALSE; } } } static gboolean _cogl_program_has_shader_type (CoglProgram *program, CoglShaderType type) { GSList *l; for (l = program->attached_shaders; l; l = l->next) { CoglShader *shader = l->data; if (shader->type == type) return TRUE; } return FALSE; } gboolean _cogl_program_has_fragment_shader (CoglHandle handle) { return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_FRAGMENT); } gboolean _cogl_program_has_vertex_shader (CoglHandle handle) { return _cogl_program_has_shader_type (handle, COGL_SHADER_TYPE_VERTEX); } muffin-6.4.1/cogl/cogl/deprecated/cogl-auto-texture.h0000664000175000017500000001627114723361714021462 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2014 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_AUTO_TEXTURE_H__ #define __COGL_AUTO_TEXTURE_H__ G_BEGIN_DECLS #include /** * cogl_texture_new_with_size: * @width: width of texture in pixels. * @height: height of texture in pixels. * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE * @internal_format: the #CoglPixelFormat to use for the GPU storage of the * texture. * * Creates a new #CoglTexture with the specified dimensions and pixel format. * * Return value: (transfer full): A newly created #CoglTexture or %NULL on failure * * Since: 0.8 * Deprecated: 1.18: Use specific constructors such as * cogl_texture_2d_new_with_size() */ COGL_DEPRECATED_FOR (cogl_texture_2d_new_with_size__OR__cogl_texture_2d_sliced_new_with_size) COGL_EXPORT CoglTexture * cogl_texture_new_with_size (unsigned int width, unsigned int height, CoglTextureFlags flags, CoglPixelFormat internal_format); /** * cogl_texture_new_from_file: * @filename: the file to load * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE * @internal_format: the #CoglPixelFormat to use for the GPU storage of the * texture. If %COGL_PIXEL_FORMAT_ANY is given then a premultiplied * format similar to the format of the source data will be used. The * default blending equations of Cogl expect premultiplied color data; * the main use of passing a non-premultiplied format here is if you * have non-premultiplied source data and are going to adjust the blend * mode (see cogl_material_set_blend()) or use the data for something * other than straight blending. * @error: return location for a #GError or %NULL * * Creates a #CoglTexture from an image file. * * Return value: (transfer full): A newly created #CoglTexture or * %NULL on failure * * Since: 0.8 * Deprecated: 1.18: Use specific constructors such as * cogl_texture_2d_new_from_file() */ COGL_DEPRECATED_FOR (cogl_texture_2d_new_from_file__OR__cogl_texture_2d_sliced_new_from_file) COGL_EXPORT CoglTexture * cogl_texture_new_from_file (const char *filename, CoglTextureFlags flags, CoglPixelFormat internal_format, GError **error); /** * cogl_texture_new_from_data: * @width: width of texture in pixels * @height: height of texture in pixels * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE * @format: the #CoglPixelFormat the buffer is stored in in RAM * @internal_format: the #CoglPixelFormat that will be used for storing * the buffer on the GPU. If COGL_PIXEL_FORMAT_ANY is given then a * premultiplied format similar to the format of the source data will * be used. The default blending equations of Cogl expect premultiplied * color data; the main use of passing a non-premultiplied format here * is if you have non-premultiplied source data and are going to adjust * the blend mode (see cogl_material_set_blend()) or use the data for * something other than straight blending. * @rowstride: the memory offset in bytes between the starts of * scanlines in @data * @data: (array): pointer the memory region where the source buffer resides * * Creates a new #CoglTexture based on data residing in memory. * * Return value: (transfer full): A newly created #CoglTexture or * %NULL on failure * * Since: 0.8 * Deprecated: 1.18: Use specific constructors such as * cogl_texture_2d_new_from_data() */ COGL_DEPRECATED_FOR (cogl_texture_2d_new_from_data__OR__cogl_texture_2d_sliced_new_from_data) COGL_EXPORT CoglTexture * cogl_texture_new_from_data (int width, int height, CoglTextureFlags flags, CoglPixelFormat format, CoglPixelFormat internal_format, int rowstride, const uint8_t *data); /** * cogl_texture_new_from_bitmap: * @bitmap: A #CoglBitmap pointer * @flags: Optional flags for the texture, or %COGL_TEXTURE_NONE * @internal_format: the #CoglPixelFormat to use for the GPU storage of the * texture * * Creates a #CoglTexture from a #CoglBitmap. * * Return value: (transfer full): A newly created #CoglTexture or * %NULL on failure * * Since: 1.0 * Deprecated: 1.18: Use specific constructors such as * cogl_texture_2d_new_from_bitmap() */ COGL_DEPRECATED_FOR (cogl_texture_2d_new_from_bitmap__OR__cogl_texture_2d_sliced_new_from_bitmap) COGL_EXPORT CoglTexture * cogl_texture_new_from_bitmap (CoglBitmap *bitmap, CoglTextureFlags flags, CoglPixelFormat internal_format); /** * cogl_texture_new_from_sub_texture: * @full_texture: a #CoglTexture pointer * @sub_x: X coordinate of the top-left of the subregion * @sub_y: Y coordinate of the top-left of the subregion * @sub_width: Width in pixels of the subregion * @sub_height: Height in pixels of the subregion * * Creates a new texture which represents a subregion of another * texture. The GL resources will be shared so that no new texture * data is actually allocated. * * Sub textures have undefined behaviour texture coordinates outside * of the range [0,1] are used. * * The sub texture will keep a reference to the full texture so you do * not need to keep one separately if you only want to use the sub * texture. * * Return value: (transfer full): A newly created #CoglTexture or * %NULL on failure * Since: 1.2 * Deprecated: 1.18: Use cogl_sub_texture_new() */ COGL_DEPRECATED_FOR (cogl_sub_texture_new) COGL_EXPORT CoglTexture * cogl_texture_new_from_sub_texture (CoglTexture *full_texture, int sub_x, int sub_y, int sub_width, int sub_height); G_END_DECLS #endif /* __COGL_AUTO_TEXTURE_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-shader-private.h0000664000175000017500000000404714723361714021730 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_SHADER_H #define __COGL_SHADER_H #include "cogl-object-private.h" #include "cogl-shader.h" #include "cogl-gl-header.h" #include "cogl-pipeline.h" typedef struct _CoglShader CoglShader; struct _CoglShader { CoglHandleObject _parent; GLuint gl_handle; CoglPipeline *compilation_pipeline; CoglShaderType type; char *source; }; void _cogl_shader_compile_real (CoglHandle handle, CoglPipeline *pipeline); void _cogl_shader_set_source_with_boilerplate (GLuint shader_gl_handle, GLenum shader_gl_type, int n_tex_coord_attribs, GLsizei count_in, const char **strings_in, const GLint *lengths_in); #endif /* __COGL_SHADER_H */ muffin-6.4.1/cogl/cogl/deprecated/cogl-material-compat.h0000664000175000017500000006135014723361714022071 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_MATERIAL_H__ #define __COGL_MATERIAL_H__ #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-material * @short_description: Fuctions for creating and manipulating materials * * COGL allows creating and manipulating materials used to fill in * geometry. Materials may simply be lighting attributes (such as an * ambient and diffuse colour) or might represent one or more textures * blended together. */ typedef struct _CoglMaterial CoglMaterial; typedef struct _CoglMaterialLayer CoglMaterialLayer; #define COGL_TYPE_MATERIAL (cogl_material_get_type ()) COGL_EXPORT GType cogl_material_get_type (void); #define COGL_MATERIAL(OBJECT) ((CoglMaterial *)OBJECT) /** * CoglMaterialFilter: * @COGL_MATERIAL_FILTER_NEAREST: Measuring in manhatten distance from the, * current pixel center, use the nearest texture texel * @COGL_MATERIAL_FILTER_LINEAR: Use the weighted average of the 4 texels * nearest the current pixel center * @COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST: Select the mimap level whose * texel size most closely matches the current pixel, and use the * %COGL_MATERIAL_FILTER_NEAREST criterion * @COGL_MATERIAL_FILTER_LINEAR_MIPMAP_NEAREST: Select the mimap level whose * texel size most closely matches the current pixel, and use the * %COGL_MATERIAL_FILTER_LINEAR criterion * @COGL_MATERIAL_FILTER_NEAREST_MIPMAP_LINEAR: Select the two mimap levels * whose texel size most closely matches the current pixel, use * the %COGL_MATERIAL_FILTER_NEAREST criterion on each one and take * their weighted average * @COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR: Select the two mimap levels * whose texel size most closely matches the current pixel, use * the %COGL_MATERIAL_FILTER_LINEAR criterion on each one and take * their weighted average * * Texture filtering is used whenever the current pixel maps either to more * than one texture element (texel) or less than one. These filter enums * correspond to different strategies used to come up with a pixel color, by * possibly referring to multiple neighbouring texels and taking a weighted * average or simply using the nearest texel. */ typedef enum { COGL_MATERIAL_FILTER_NEAREST = 0x2600, COGL_MATERIAL_FILTER_LINEAR = 0x2601, COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST = 0x2700, COGL_MATERIAL_FILTER_LINEAR_MIPMAP_NEAREST = 0x2701, COGL_MATERIAL_FILTER_NEAREST_MIPMAP_LINEAR = 0x2702, COGL_MATERIAL_FILTER_LINEAR_MIPMAP_LINEAR = 0x2703 } CoglMaterialFilter; /* NB: these values come from the equivalents in gl.h */ /** * CoglMaterialWrapMode: * @COGL_MATERIAL_WRAP_MODE_REPEAT: The texture will be repeated. This * is useful for example to draw a tiled background. * @COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE: The coordinates outside the * range 0→1 will sample copies of the edge pixels of the * texture. This is useful to avoid artifacts if only one copy of * the texture is being rendered. * @COGL_MATERIAL_WRAP_MODE_AUTOMATIC: Cogl will try to automatically * decide which of the above two to use. For cogl_rectangle(), it * will use repeat mode if any of the texture coordinates are * outside the range 0→1, otherwise it will use clamp to edge. For * cogl_polygon() it will always use repeat mode. For * cogl_vertex_buffer_draw() it will use repeat mode except for * layers that have point sprite coordinate generation enabled. This * is the default value. * * The wrap mode specifies what happens when texture coordinates * outside the range 0→1 are used. Note that if the filter mode is * anything but %COGL_MATERIAL_FILTER_NEAREST then texels outside the * range 0→1 might be used even when the coordinate is exactly 0 or 1 * because OpenGL will try to sample neighbouring pixels. For example * if you are trying to render the full texture then you may get * artifacts around the edges when the pixels from the other side are * merged in if the wrap mode is set to repeat. * * Since: 1.4 */ /* GL_ALWAYS is just used here as a value that is known not to clash * with any valid GL wrap modes * * XXX: keep the values in sync with the CoglMaterialWrapModeInternal * enum so no conversion is actually needed. */ typedef enum { COGL_MATERIAL_WRAP_MODE_REPEAT = 0x2901, COGL_MATERIAL_WRAP_MODE_CLAMP_TO_EDGE = 0x812F, COGL_MATERIAL_WRAP_MODE_AUTOMATIC = 0x0207 } CoglMaterialWrapMode; /* NB: these values come from the equivalents in gl.h */ /** * cogl_material_new: * * Allocates and initializes a blank white material * * Return value: a pointer to a new #CoglMaterial * Deprecated: 1.16: Use cogl_pipeline_new() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_new) COGL_EXPORT CoglMaterial * cogl_material_new (void); /** * cogl_material_set_color: * @material: A #CoglMaterial object * @color: The components of the color * * Sets the basic color of the material, used when no lighting is enabled. * * Note that if you don't add any layers to the material then the color * will be blended unmodified with the destination; the default blend * expects premultiplied colors: for example, use (0.5, 0.0, 0.0, 0.5) for * semi-transparent red. See cogl_color_premultiply(). * * The default value is (1.0, 1.0, 1.0, 1.0) * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_color() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_color) COGL_EXPORT void cogl_material_set_color (CoglMaterial *material, const CoglColor *color); /** * cogl_material_set_color4ub: * @material: A #CoglMaterial object * @red: The red component * @green: The green component * @blue: The blue component * @alpha: The alpha component * * Sets the basic color of the material, used when no lighting is enabled. * * The default value is (0xff, 0xff, 0xff, 0xff) * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_color4ub() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_color4ub) COGL_EXPORT void cogl_material_set_color4ub (CoglMaterial *material, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha); /** * CoglMaterialAlphaFunc: * @COGL_MATERIAL_ALPHA_FUNC_NEVER: Never let the fragment through. * @COGL_MATERIAL_ALPHA_FUNC_LESS: Let the fragment through if the incoming * alpha value is less than the reference alpha value * @COGL_MATERIAL_ALPHA_FUNC_EQUAL: Let the fragment through if the incoming * alpha value equals the reference alpha value * @COGL_MATERIAL_ALPHA_FUNC_LEQUAL: Let the fragment through if the incoming * alpha value is less than or equal to the reference alpha value * @COGL_MATERIAL_ALPHA_FUNC_GREATER: Let the fragment through if the incoming * alpha value is greater than the reference alpha value * @COGL_MATERIAL_ALPHA_FUNC_NOTEQUAL: Let the fragment through if the incoming * alpha value does not equal the reference alpha value * @COGL_MATERIAL_ALPHA_FUNC_GEQUAL: Let the fragment through if the incoming * alpha value is greater than or equal to the reference alpha value. * @COGL_MATERIAL_ALPHA_FUNC_ALWAYS: Always let the fragment through. * * Alpha testing happens before blending primitives with the framebuffer and * gives an opportunity to discard fragments based on a comparison with the * incoming alpha value and a reference alpha value. The #CoglMaterialAlphaFunc * determines how the comparison is done. */ typedef enum { COGL_MATERIAL_ALPHA_FUNC_NEVER = 0x0200, COGL_MATERIAL_ALPHA_FUNC_LESS = 0x0201, COGL_MATERIAL_ALPHA_FUNC_EQUAL = 0x0202, COGL_MATERIAL_ALPHA_FUNC_LEQUAL = 0x0203, COGL_MATERIAL_ALPHA_FUNC_GREATER = 0x0204, COGL_MATERIAL_ALPHA_FUNC_NOTEQUAL = 0x0205, COGL_MATERIAL_ALPHA_FUNC_GEQUAL = 0x0206, COGL_MATERIAL_ALPHA_FUNC_ALWAYS = 0x0207 } CoglMaterialAlphaFunc; /** * cogl_material_set_alpha_test_function: * @material: A #CoglMaterial object * @alpha_func: A @CoglMaterialAlphaFunc constant * @alpha_reference: A reference point that the chosen alpha function uses * to compare incoming fragments to. * * Before a primitive is blended with the framebuffer, it goes through an * alpha test stage which lets you discard fragments based on the current * alpha value. This function lets you change the function used to evaluate * the alpha channel, and thus determine which fragments are discarded * and which continue on to the blending stage. * * The default is %COGL_MATERIAL_ALPHA_FUNC_ALWAYS * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_alpha_test_function() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_alpha_test_function) COGL_EXPORT void cogl_material_set_alpha_test_function (CoglMaterial *material, CoglMaterialAlphaFunc alpha_func, float alpha_reference); /** * cogl_material_set_blend: * @material: A #CoglMaterial object * @blend_string: A Cogl blend string * describing the desired blend function. * @error: return location for a #GError that may report lack of driver * support if you give separate blend string statements for the alpha * channel and RGB channels since some drivers, or backends such as * GLES 1.1, don't support this feature. May be %NULL, in which case a * warning will be printed out using GLib's logging facilities if an * error is encountered. * * If not already familiar; please refer here * for an overview of what blend strings are, and their syntax. * * Blending occurs after the alpha test function, and combines fragments with * the framebuffer. * Currently the only blend function Cogl exposes is ADD(). So any valid * blend statements will be of the form: * * |[ * <channel-mask>=ADD(SRC_COLOR*(<factor>), DST_COLOR*(<factor>)) * ]| * * The brackets around blend factors are currently not * optional! * * This is the list of source-names usable as blend factors: * * SRC_COLOR: The color of the in comming fragment * DST_COLOR: The color of the framebuffer * CONSTANT: The constant set via cogl_material_set_blend_constant() * * * The source names can be used according to the * color-source and factor syntax, * so for example "(1-SRC_COLOR[A])" would be a valid factor, as would * "(CONSTANT[RGB])" * * These can also be used as factors: * * 0: (0, 0, 0, 0) * 1: (1, 1, 1, 1) * SRC_ALPHA_SATURATE_FACTOR: (f,f,f,1) where f = MIN(SRC_COLOR[A],1-DST_COLOR[A]) * * * Remember; all color components are normalized to the range [0, 1] * before computing the result of blending. * * * Blend Strings/1 * Blend a non-premultiplied source over a destination with * premultiplied alpha: * * "RGB = ADD(SRC_COLOR*(SRC_COLOR[A]), DST_COLOR*(1-SRC_COLOR[A]))" * "A = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" * * * * * Blend Strings/2 * Blend a premultiplied source over a destination with * premultiplied alpha * * "RGBA = ADD(SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A]))" * * * * The default blend string is: * |[ * RGBA = ADD (SRC_COLOR, DST_COLOR*(1-SRC_COLOR[A])) * ]| * * That gives normal alpha-blending when the calculated color for the material * is in premultiplied form. * * Return value: %TRUE if the blend string was successfully parsed, and the * described blending is supported by the underlying driver/hardware. If * there was an error, %FALSE is returned and @error is set accordingly (if * present). * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_blend() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_blend) COGL_EXPORT gboolean cogl_material_set_blend (CoglMaterial *material, const char *blend_string, GError **error); /** * cogl_material_set_blend_constant: * @material: A #CoglMaterial object * @constant_color: The constant color you want * * When blending is setup to reference a CONSTANT blend factor then * blending will depend on the constant set with this function. * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_blend_constant() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_blend_constant) COGL_EXPORT void cogl_material_set_blend_constant (CoglMaterial *material, const CoglColor *constant_color); /** * cogl_material_set_point_size: * @material: a material. * @point_size: the new point size. * * Changes the size of points drawn when %COGL_VERTICES_MODE_POINTS is * used with the vertex buffer API. Note that typically the GPU will * only support a limited minimum and maximum range of point sizes. If * the chosen point size is outside that range then the nearest value * within that range will be used instead. The size of a point is in * screen space so it will be the same regardless of any * transformations. The default point size is 1.0. * * Since: 1.4 * Deprecated: 1.16: Use cogl_pipeline_set_point_size() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_point_size) COGL_EXPORT void cogl_material_set_point_size (CoglMaterial *material, float point_size); /** * cogl_material_set_user_program: * @material: a #CoglMaterial object. * @program: A #CoglHandle to a linked CoglProgram * * Associates a linked CoglProgram with the given material so that the * program can take full control of vertex and/or fragment processing. * * This is an example of how it can be used to associate an ARBfp * program with a #CoglMaterial: * |[ * CoglHandle shader; * CoglHandle program; * CoglMaterial *material; * * shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); * cogl_shader_source (shader, * "!!ARBfp1.0\n" * "MOV result.color,fragment.color;\n" * "END\n"); * * program = cogl_create_program (); * cogl_program_attach_shader (program, shader); * cogl_program_link (program); * * material = cogl_material_new (); * cogl_material_set_user_program (material, program); * * cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); * cogl_rectangle (0, 0, 100, 100); * ]| * * It is possibly worth keeping in mind that this API is not part of * the long term design for how we want to expose shaders to Cogl * developers (We are planning on deprecating the cogl_program and * cogl_shader APIs in favour of a "snippet" framework) but in the * meantime we hope this will handle most practical GLSL and ARBfp * requirements. * * Since: 1.4 * Deprecated: 1.16: Use #CoglSnippet api instead instead */ COGL_DEPRECATED_FOR (cogl_snippet_) COGL_EXPORT void cogl_material_set_user_program (CoglMaterial *material, CoglHandle program); /** * cogl_material_set_layer: * @material: A #CoglMaterial object * @layer_index: the index of the layer * @texture: a #CoglHandle for the layer object * * In addition to the standard OpenGL lighting model a Cogl material may have * one or more layers comprised of textures that can be blended together in * order, with a number of different texture combine modes. This function * defines a new texture layer. * * The index values of multiple layers do not have to be consecutive; it is * only their relative order that is important. * * In the future, we may define other types of material layers, such * as purely GLSL based layers. * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_layer() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer) COGL_EXPORT void cogl_material_set_layer (CoglMaterial *material, int layer_index, CoglHandle texture); /** * cogl_material_set_layer_combine: * @material: A #CoglMaterial object * @layer_index: Specifies the layer you want define a combine function for * @blend_string: A Cogl blend string * describing the desired texture combine function. * @error: A #GError that may report parse errors or lack of GPU/driver * support. May be %NULL, in which case a warning will be printed out if an * error is encountered. * * If not already familiar; you can refer * here for an overview of what blend * strings are and there syntax. * * These are all the functions available for texture combining: * * REPLACE(arg0) = arg0 * MODULATE(arg0, arg1) = arg0 x arg1 * ADD(arg0, arg1) = arg0 + arg1 * ADD_SIGNED(arg0, arg1) = arg0 + arg1 - 0.5 * INTERPOLATE(arg0, arg1, arg2) = arg0 x arg2 + arg1 x (1 - arg2) * SUBTRACT(arg0, arg1) = arg0 - arg1 * * * DOT3_RGB(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) * * * * * DOT3_RGBA(arg0, arg1) = 4 x ((arg0[R] - 0.5)) * (arg1[R] - 0.5) + * (arg0[G] - 0.5)) * (arg1[G] - 0.5) + * (arg0[B] - 0.5)) * (arg1[B] - 0.5)) * * * * * Refer to the * color-source syntax for * describing the arguments. The valid source names for texture combining * are: * * * TEXTURE * Use the color from the current texture layer * * * TEXTURE_0, TEXTURE_1, etc * Use the color from the specified texture layer * * * CONSTANT * Use the color from the constant given with * cogl_material_set_layer_constant() * * * PRIMARY * Use the color of the material as set with * cogl_material_set_color() * * * PREVIOUS * Either use the texture color from the previous layer, or * if this is layer 0, use the color of the material as set with * cogl_material_set_color() * * * * * Layer Combine Examples * This is effectively what the default blending is: * * RGBA = MODULATE (PREVIOUS, TEXTURE) * * This could be used to cross-fade between two images, using * the alpha component of a constant as the interpolator. The constant * color is given by calling cogl_material_set_layer_constant. * * RGBA = INTERPOLATE (PREVIOUS, TEXTURE, CONSTANT[A]) * * * * You can't give a multiplication factor for arguments as you can * with blending. * * Return value: %TRUE if the blend string was successfully parsed, and the * described texture combining is supported by the underlying driver and * or hardware. On failure, %FALSE is returned and @error is set * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_layer_combine() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer_combine) COGL_EXPORT gboolean cogl_material_set_layer_combine (CoglMaterial *material, int layer_index, const char *blend_string, GError **error); /** * cogl_material_set_layer_combine_constant: * @material: A #CoglMaterial object * @layer_index: Specifies the layer you want to specify a constant used * for texture combining * @constant: The constant color you want * * When you are using the 'CONSTANT' color source in a layer combine * description then you can use this function to define its value. * * Since: 1.0 * Deprecated: 1.16: Use cogl_pipeline_set_layer_combine_constant() * instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer_combine_constant) COGL_EXPORT void cogl_material_set_layer_combine_constant (CoglMaterial *material, int layer_index, const CoglColor *constant); /** * cogl_material_set_layer_matrix: * @material: A #CoglMaterial object * @layer_index: the index for the layer inside @material * @matrix: the transformation matrix for the layer * * This function lets you set a matrix that can be used to e.g. translate * and rotate a single layer of a material used to fill your geometry. * Deprecated: 1.16: Use cogl_pipeline_set_layer_matrix() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer_matrix) COGL_EXPORT void cogl_material_set_layer_matrix (CoglMaterial *material, int layer_index, const CoglMatrix *matrix); /** * cogl_material_set_layer_filters: * @material: A #CoglMaterial object * @layer_index: the layer number to change. * @min_filter: the filter used when scaling a texture down. * @mag_filter: the filter used when magnifying a texture. * * Changes the decimation and interpolation filters used when a texture is * drawn at other scales than 100%. * Deprecated: 1.16: Use cogl_pipeline_set_layer_filters() instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer_filters) COGL_EXPORT void cogl_material_set_layer_filters (CoglMaterial *material, int layer_index, CoglMaterialFilter min_filter, CoglMaterialFilter mag_filter); /** * cogl_material_set_layer_point_sprite_coords_enabled: * @material: a #CoglHandle to a material. * @layer_index: the layer number to change. * @enable: whether to enable point sprite coord generation. * @error: A return location for a GError, or NULL to ignore errors. * * When rendering points, if @enable is %TRUE then the texture * coordinates for this layer will be replaced with coordinates that * vary from 0.0 to 1.0 across the primitive. The top left of the * point will have the coordinates 0.0,0.0 and the bottom right will * have 1.0,1.0. If @enable is %FALSE then the coordinates will be * fixed for the entire point. * * Return value: %TRUE if the function succeeds, %FALSE otherwise. * Since: 1.4 * Deprecated: 1.16: Use cogl_pipeline_set_layer_point_sprite_coords_enabled() * instead */ COGL_DEPRECATED_FOR (cogl_pipeline_set_layer_point_sprite_coords_enabled) COGL_EXPORT gboolean cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, int layer_index, gboolean enable, GError **error); G_END_DECLS #endif /* __COGL_MATERIAL_H__ */ muffin-6.4.1/cogl/cogl/deprecated/cogl-clutter.c0000664000175000017500000000333514723361714020466 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include #include "cogl-util.h" #include "cogl-types.h" #include "cogl-private.h" #include "cogl-context-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #ifdef COGL_HAS_XLIB_SUPPORT #include "cogl-xlib-renderer.h" #endif #include "winsys/cogl-winsys-private.h" #include "deprecated/cogl-clutter.h" gboolean cogl_clutter_winsys_has_feature (CoglWinsysFeature feature) { return _cogl_winsys_has_feature (feature); } muffin-6.4.1/cogl/cogl/deprecated/cogl-material-compat.c0000664000175000017500000001262414723361714022064 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include #include #include #include #include G_DEFINE_BOXED_TYPE (CoglMaterial, cogl_material, cogl_object_ref, cogl_object_unref) CoglMaterial * cogl_material_new (void) { _COGL_GET_CONTEXT(ctx, NULL); return COGL_MATERIAL (cogl_pipeline_new (ctx)); } void cogl_material_set_color (CoglMaterial *material, const CoglColor *color) { cogl_pipeline_set_color (COGL_PIPELINE (material), color); } void cogl_material_set_color4ub (CoglMaterial *material, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { cogl_pipeline_set_color4ub (COGL_PIPELINE (material), red, green, blue, alpha); } gboolean cogl_material_set_blend (CoglMaterial *material, const char *blend_string, GError **error) { return cogl_pipeline_set_blend (COGL_PIPELINE (material), blend_string, error); } void cogl_material_set_blend_constant (CoglMaterial *material, const CoglColor *constant_color) { cogl_pipeline_set_blend_constant (COGL_PIPELINE (material), constant_color); } void cogl_material_set_point_size (CoglMaterial *material, float point_size) { cogl_pipeline_set_point_size (COGL_PIPELINE (material), point_size); } void cogl_material_set_user_program (CoglMaterial *material, CoglHandle program) { cogl_pipeline_set_user_program (COGL_PIPELINE (material), program); } void cogl_material_set_layer (CoglMaterial *material, int layer_index, CoglHandle texture) { cogl_pipeline_set_layer_texture (COGL_PIPELINE (material), layer_index, texture); } gboolean cogl_material_set_layer_combine (CoglMaterial *material, int layer_index, const char *blend_string, GError **error) { return cogl_pipeline_set_layer_combine (COGL_PIPELINE (material), layer_index, blend_string, error); } void cogl_material_set_layer_combine_constant (CoglMaterial *material, int layer_index, const CoglColor *constant) { cogl_pipeline_set_layer_combine_constant (COGL_PIPELINE (material), layer_index, constant); } void cogl_material_set_layer_matrix (CoglMaterial *material, int layer_index, const CoglMatrix *matrix) { cogl_pipeline_set_layer_matrix (COGL_PIPELINE (material), layer_index, matrix); } void cogl_material_set_layer_filters (CoglMaterial *material, int layer_index, CoglMaterialFilter min_filter, CoglMaterialFilter mag_filter) { cogl_pipeline_set_layer_filters (COGL_PIPELINE (material), layer_index, min_filter, mag_filter); } gboolean cogl_material_set_layer_point_sprite_coords_enabled (CoglMaterial *material, int layer_index, gboolean enable, GError **error) { CoglPipeline *pipeline = COGL_PIPELINE (material); return cogl_pipeline_set_layer_point_sprite_coords_enabled (pipeline, layer_index, enable, error); } muffin-6.4.1/cogl/cogl/cogl-glib-source.h0000664000175000017500000000631114723361714017121 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_GSOURCE_H__ #define __COGL_GSOURCE_H__ #include #include G_BEGIN_DECLS /** * cogl_glib_source_new: * @context: A #CoglContext * @priority: The priority of the #GSource * * Creates a #GSource which handles Cogl's internal system event * processing. This can be used as a convenience instead of * cogl_poll_renderer_get_info() and cogl_poll_renderer_dispatch() in * applications that are already using the GLib main loop. After this * is called the #GSource should be attached to the main loop using * g_source_attach(). * * Applications that manually connect to a #CoglRenderer before they * create a #CoglContext should instead use * cogl_glib_renderer_source_new() so that events may be dispatched * before a context has been created. In that case you don't need to * use this api in addition later, it is simply enough to use * cogl_glib_renderer_source_new() instead. * * This api is actually just a thin convenience wrapper around * cogl_glib_renderer_source_new() * * Return value: a new #GSource * * Stability: unstable * Since: 1.10 */ COGL_EXPORT GSource * cogl_glib_source_new (CoglContext *context, int priority); /** * cogl_glib_renderer_source_new: * @renderer: A #CoglRenderer * @priority: The priority of the #GSource * * Creates a #GSource which handles Cogl's internal system event * processing. This can be used as a convenience instead of * cogl_poll_renderer_get_info() and cogl_poll_renderer_dispatch() in * applications that are already using the GLib main loop. After this * is called the #GSource should be attached to the main loop using * g_source_attach(). * * Return value: a new #GSource * * Stability: unstable * Since: 1.16 */ COGL_EXPORT GSource * cogl_glib_renderer_source_new (CoglRenderer *renderer, int priority); G_END_DECLS #endif /* __COGL_GSOURCE_H__ */ muffin-6.4.1/cogl/cogl/cogl-bitmap-conversion.c0000664000175000017500000005372014723361714020346 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-private.h" #include "cogl-bitmap-private.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" #include #define component_type uint8_t #define component_size 8 /* We want to specially optimise the packing when we are converting to/from an 8-bit type so that it won't do anything. That way for example if we are just doing a swizzle conversion then the inner loop for the conversion will be really simple */ #define UNPACK_BYTE(b) (b) #define PACK_BYTE(b) (b) #include "cogl-bitmap-packing.h" #undef PACK_BYTE #undef UNPACK_BYTE #undef component_type #undef component_size #define component_type uint16_t #define component_size 16 #define UNPACK_BYTE(b) (((b) * 65535 + 127) / 255) #define PACK_BYTE(b) (((b) * 255 + 32767) / 65535) #include "cogl-bitmap-packing.h" #undef PACK_BYTE #undef UNPACK_BYTE #undef component_type #undef component_size /* (Un)Premultiplication */ inline static void _cogl_unpremult_alpha_0 (uint8_t *dst) { dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = 0; } inline static void _cogl_unpremult_alpha_last (uint8_t *dst) { uint8_t alpha = dst[3]; dst[0] = (dst[0] * 255) / alpha; dst[1] = (dst[1] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha; } inline static void _cogl_unpremult_alpha_first (uint8_t *dst) { uint8_t alpha = dst[0]; dst[1] = (dst[1] * 255) / alpha; dst[2] = (dst[2] * 255) / alpha; dst[3] = (dst[3] * 255) / alpha; } /* No division form of floor((c*a + 128)/255) (I first encountered * this in the RENDER implementation in the X server.) Being exact * is important for a == 255 - we want to get exactly c. */ #define MULT(d,a,t) \ G_STMT_START { \ t = d * a + 128; \ d = ((t >> 8) + t) >> 8; \ } G_STMT_END inline static void _cogl_premult_alpha_last (uint8_t *dst) { uint8_t alpha = dst[3]; /* Using a separate temporary per component has given slightly better * code generation with GCC in the past; it shouldn't do any worse in * any case. */ unsigned int t1, t2, t3; MULT(dst[0], alpha, t1); MULT(dst[1], alpha, t2); MULT(dst[2], alpha, t3); } inline static void _cogl_premult_alpha_first (uint8_t *dst) { uint8_t alpha = dst[0]; unsigned int t1, t2, t3; MULT(dst[1], alpha, t1); MULT(dst[2], alpha, t2); MULT(dst[3], alpha, t3); } #undef MULT /* Use the SSE optimized version to premult four pixels at once when it is available. The same assembler code works for x86 and x86-64 because it doesn't refer to any non-SSE registers directly */ #if defined(__SSE2__) && defined(__GNUC__) \ && (defined(__x86_64) || defined(__i386)) #define COGL_USE_PREMULT_SSE2 #endif #ifdef COGL_USE_PREMULT_SSE2 inline static void _cogl_premult_alpha_last_four_pixels_sse2 (uint8_t *p) { /* 8 copies of 128 used below */ static const int16_t eight_halves[8] __attribute__ ((aligned (16))) = { 128, 128, 128, 128, 128, 128, 128, 128 }; /* Mask of the rgb components of the four pixels */ static const int8_t just_rgb[16] __attribute__ ((aligned (16))) = { 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x00 }; /* Each SSE register only holds two pixels because we need to work with 16-bit intermediate values. We still do four pixels by interleaving two registers in the hope that it will pipeline better */ asm (/* Load eight_halves into xmm5 for later */ "movdqa (%1), %%xmm5\n" /* Clear xmm3 */ "pxor %%xmm3, %%xmm3\n" /* Load two pixels from p into the low half of xmm0 */ "movlps (%0), %%xmm0\n" /* Load the next set of two pixels from p into the low half of xmm1 */ "movlps 8(%0), %%xmm1\n" /* Unpack 8 bytes from the low quad-words in each register to 8 16-bit values */ "punpcklbw %%xmm3, %%xmm0\n" "punpcklbw %%xmm3, %%xmm1\n" /* Copy alpha values of the first pixel in xmm0 to all components of the first pixel in xmm2 */ "pshuflw $255, %%xmm0, %%xmm2\n" /* same for xmm1 and xmm3 */ "pshuflw $255, %%xmm1, %%xmm3\n" /* The above also copies the second pixel directly so we now want to replace the RGB components with copies of the alpha components */ "pshufhw $255, %%xmm2, %%xmm2\n" "pshufhw $255, %%xmm3, %%xmm3\n" /* Multiply the rgb components by the alpha */ "pmullw %%xmm2, %%xmm0\n" "pmullw %%xmm3, %%xmm1\n" /* Add 128 to each component */ "paddw %%xmm5, %%xmm0\n" "paddw %%xmm5, %%xmm1\n" /* Copy the results to temporary registers xmm4 and xmm5 */ "movdqa %%xmm0, %%xmm4\n" "movdqa %%xmm1, %%xmm5\n" /* Divide the results by 256 */ "psrlw $8, %%xmm0\n" "psrlw $8, %%xmm1\n" /* Add the temporaries back in */ "paddw %%xmm4, %%xmm0\n" "paddw %%xmm5, %%xmm1\n" /* Divide again */ "psrlw $8, %%xmm0\n" "psrlw $8, %%xmm1\n" /* Pack the results back as bytes */ "packuswb %%xmm1, %%xmm0\n" /* Load just_rgb into xmm3 for later */ "movdqa (%2), %%xmm3\n" /* Reload all four pixels into xmm2 */ "movups (%0), %%xmm2\n" /* Mask out the alpha from the results */ "andps %%xmm3, %%xmm0\n" /* Mask out the RGB from the original four pixels */ "andnps %%xmm2, %%xmm3\n" /* Combine the two to get the right alpha values */ "orps %%xmm3, %%xmm0\n" /* Write to memory */ "movdqu %%xmm0, (%0)\n" : /* no outputs */ : "r" (p), "r" (eight_halves), "r" (just_rgb) : "xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5"); } #endif /* COGL_USE_PREMULT_SSE2 */ static void _cogl_bitmap_premult_unpacked_span_8 (uint8_t *data, int width) { #ifdef COGL_USE_PREMULT_SSE2 /* Process 4 pixels at a time */ while (width >= 4) { _cogl_premult_alpha_last_four_pixels_sse2 (data); data += 4 * 4; width -= 4; } /* If there are any pixels left we will fall through and handle them below */ #endif /* COGL_USE_PREMULT_SSE2 */ while (width-- > 0) { _cogl_premult_alpha_last (data); data += 4; } } static void _cogl_bitmap_unpremult_unpacked_span_8 (uint8_t *data, int width) { int x; for (x = 0; x < width; x++) { if (data[3] == 0) _cogl_unpremult_alpha_0 (data); else _cogl_unpremult_alpha_last (data); data += 4; } } static void _cogl_bitmap_unpremult_unpacked_span_16 (uint16_t *data, int width) { while (width-- > 0) { uint16_t alpha = data[3]; if (alpha == 0) memset (data, 0, sizeof (uint16_t) * 3); else { data[0] = (data[0] * 65535) / alpha; data[1] = (data[1] * 65535) / alpha; data[2] = (data[2] * 65535) / alpha; } } } static void _cogl_bitmap_premult_unpacked_span_16 (uint16_t *data, int width) { while (width-- > 0) { uint16_t alpha = data[3]; data[0] = (data[0] * alpha) / 65535; data[1] = (data[1] * alpha) / 65535; data[2] = (data[2] * alpha) / 65535; } } static gboolean _cogl_bitmap_can_fast_premult (CoglPixelFormat format) { switch (format & ~COGL_PREMULT_BIT) { case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ABGR_8888: return TRUE; default: return FALSE; } } static gboolean _cogl_bitmap_needs_short_temp_buffer (CoglPixelFormat format) { /* If the format is using more than 8 bits per component then we'll unpack into a 16-bit per component buffer instead of 8-bit so we won't lose as much precision. If we ever add support for formats with more than 16 bits for at least one of the components then we should probably do something else here, maybe convert to floats */ switch (format) { case COGL_PIXEL_FORMAT_DEPTH_16: case COGL_PIXEL_FORMAT_DEPTH_32: case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: case COGL_PIXEL_FORMAT_ANY: case COGL_PIXEL_FORMAT_YUV: g_assert_not_reached (); case COGL_PIXEL_FORMAT_A_8: case COGL_PIXEL_FORMAT_RG_88: case COGL_PIXEL_FORMAT_RGB_565: case COGL_PIXEL_FORMAT_RGBA_4444: case COGL_PIXEL_FORMAT_RGBA_5551: case COGL_PIXEL_FORMAT_G_8: case COGL_PIXEL_FORMAT_RGB_888: case COGL_PIXEL_FORMAT_BGR_888: case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ABGR_8888: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: case COGL_PIXEL_FORMAT_BGRA_8888_PRE: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: case COGL_PIXEL_FORMAT_ABGR_8888_PRE: case COGL_PIXEL_FORMAT_RGBA_4444_PRE: case COGL_PIXEL_FORMAT_RGBA_5551_PRE: return FALSE; case COGL_PIXEL_FORMAT_RGBA_1010102: case COGL_PIXEL_FORMAT_BGRA_1010102: case COGL_PIXEL_FORMAT_ARGB_2101010: case COGL_PIXEL_FORMAT_ABGR_2101010: case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: return TRUE; } g_assert_not_reached (); return FALSE; } gboolean _cogl_bitmap_convert_into_bitmap (CoglBitmap *src_bmp, CoglBitmap *dst_bmp, GError **error) { uint8_t *src_data; uint8_t *dst_data; uint8_t *src; uint8_t *dst; void *tmp_row; int src_rowstride; int dst_rowstride; int y; int width, height; CoglPixelFormat src_format; CoglPixelFormat dst_format; gboolean use_16; gboolean need_premult; src_format = cogl_bitmap_get_format (src_bmp); src_rowstride = cogl_bitmap_get_rowstride (src_bmp); dst_format = cogl_bitmap_get_format (dst_bmp); dst_rowstride = cogl_bitmap_get_rowstride (dst_bmp); width = cogl_bitmap_get_width (src_bmp); height = cogl_bitmap_get_height (src_bmp); g_return_val_if_fail (width == cogl_bitmap_get_width (dst_bmp), FALSE); g_return_val_if_fail (height == cogl_bitmap_get_height (dst_bmp), FALSE); need_premult = ((src_format & COGL_PREMULT_BIT) != (dst_format & COGL_PREMULT_BIT) && src_format != COGL_PIXEL_FORMAT_A_8 && dst_format != COGL_PIXEL_FORMAT_A_8 && (src_format & dst_format & COGL_A_BIT)); /* If the base format is the same then we can just copy the bitmap instead */ if ((src_format & ~COGL_PREMULT_BIT) == (dst_format & ~COGL_PREMULT_BIT) && (!need_premult || _cogl_bitmap_can_fast_premult (dst_format))) { if (!_cogl_bitmap_copy_subregion (src_bmp, dst_bmp, 0, 0, /* src_x / src_y */ 0, 0, /* dst_x / dst_y */ width, height, error)) return FALSE; if (need_premult) { if ((dst_format & COGL_PREMULT_BIT)) { if (!_cogl_bitmap_premult (dst_bmp, error)) return FALSE; } else { if (!_cogl_bitmap_unpremult (dst_bmp, error)) return FALSE; } } return TRUE; } src_data = _cogl_bitmap_map (src_bmp, COGL_BUFFER_ACCESS_READ, 0, error); if (src_data == NULL) return FALSE; dst_data = _cogl_bitmap_map (dst_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, error); if (dst_data == NULL) { _cogl_bitmap_unmap (src_bmp); return FALSE; } use_16 = _cogl_bitmap_needs_short_temp_buffer (dst_format); /* Allocate a buffer to hold a temporary RGBA row */ tmp_row = g_malloc (width * (use_16 ? sizeof (uint16_t) : sizeof (uint8_t)) * 4); /* FIXME: Optimize */ for (y = 0; y < height; y++) { src = src_data + y * src_rowstride; dst = dst_data + y * dst_rowstride; if (use_16) _cogl_unpack_16 (src_format, src, tmp_row, width); else _cogl_unpack_8 (src_format, src, tmp_row, width); /* Handle premultiplication */ if (need_premult) { if (dst_format & COGL_PREMULT_BIT) { if (use_16) _cogl_bitmap_premult_unpacked_span_16 (tmp_row, width); else _cogl_bitmap_premult_unpacked_span_8 (tmp_row, width); } else { if (use_16) _cogl_bitmap_unpremult_unpacked_span_16 (tmp_row, width); else _cogl_bitmap_unpremult_unpacked_span_8 (tmp_row, width); } } if (use_16) _cogl_pack_16 (dst_format, tmp_row, dst, width); else _cogl_pack_8 (dst_format, tmp_row, dst, width); } _cogl_bitmap_unmap (src_bmp); _cogl_bitmap_unmap (dst_bmp); g_free (tmp_row); return TRUE; } CoglBitmap * _cogl_bitmap_convert (CoglBitmap *src_bmp, CoglPixelFormat dst_format, GError **error) { CoglBitmap *dst_bmp; int width, height; _COGL_GET_CONTEXT (ctx, NULL); width = cogl_bitmap_get_width (src_bmp); height = cogl_bitmap_get_height (src_bmp); dst_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, dst_format, error); if (!dst_bmp) return NULL; if (!_cogl_bitmap_convert_into_bitmap (src_bmp, dst_bmp, error)) { cogl_object_unref (dst_bmp); return NULL; } return dst_bmp; } static gboolean driver_can_convert (CoglContext *ctx, CoglPixelFormat src_format, CoglPixelFormat internal_format) { if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION)) return FALSE; if (src_format == internal_format) return TRUE; /* If the driver doesn't natively support alpha textures then it * won't work correctly to convert to/from component-alpha * textures */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && (src_format == COGL_PIXEL_FORMAT_A_8 || internal_format == COGL_PIXEL_FORMAT_A_8)) return FALSE; /* Same for red-green textures. If red-green textures aren't * supported then the internal format should never be RG_88 but we * should still be able to convert from an RG source image */ if (!cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_RG) && src_format == COGL_PIXEL_FORMAT_RG_88) return FALSE; return TRUE; } CoglBitmap * _cogl_bitmap_convert_for_upload (CoglBitmap *src_bmp, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error) { CoglContext *ctx = _cogl_bitmap_get_context (src_bmp); CoglPixelFormat src_format = cogl_bitmap_get_format (src_bmp); CoglBitmap *dst_bmp; g_return_val_if_fail (internal_format != COGL_PIXEL_FORMAT_ANY, NULL); /* OpenGL supports specifying a different format for the internal format when uploading texture data. We should use this to convert formats because it is likely to be faster and support more types than the Cogl bitmap code. However under GLES the internal format must be the same as the bitmap format and it only supports a limited number of formats so we must convert using the Cogl bitmap code instead */ if (driver_can_convert (ctx, src_format, internal_format)) { /* If the source format does not have the same premult flag as the internal_format then we need to copy and convert it */ if (_cogl_texture_needs_premult_conversion (src_format, internal_format)) { if (can_convert_in_place) { if (_cogl_bitmap_convert_premult_status (src_bmp, (src_format ^ COGL_PREMULT_BIT), error)) { dst_bmp = cogl_object_ref (src_bmp); } else return NULL; } else { dst_bmp = _cogl_bitmap_convert (src_bmp, src_format ^ COGL_PREMULT_BIT, error); if (dst_bmp == NULL) return NULL; } } else dst_bmp = cogl_object_ref (src_bmp); } else { CoglPixelFormat closest_format; closest_format = ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, NULL, /* ignore gl intformat */ NULL, /* ignore gl format */ NULL); /* ignore gl type */ if (closest_format != src_format) dst_bmp = _cogl_bitmap_convert (src_bmp, closest_format, error); else dst_bmp = cogl_object_ref (src_bmp); } return dst_bmp; } gboolean _cogl_bitmap_unpremult (CoglBitmap *bmp, GError **error) { uint8_t *p, *data; uint16_t *tmp_row; int x,y; CoglPixelFormat format; int width, height; int rowstride; format = cogl_bitmap_get_format (bmp); width = cogl_bitmap_get_width (bmp); height = cogl_bitmap_get_height (bmp); rowstride = cogl_bitmap_get_rowstride (bmp); if ((data = _cogl_bitmap_map (bmp, COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE, 0, error)) == NULL) return FALSE; /* If we can't directly unpremult the data inline then we'll allocate a temporary row and unpack the data. This assumes if we can fast premult then we can also fast unpremult */ if (_cogl_bitmap_can_fast_premult (format)) tmp_row = NULL; else tmp_row = g_malloc (sizeof (uint16_t) * 4 * width); for (y = 0; y < height; y++) { p = (uint8_t*) data + y * rowstride; if (tmp_row) { _cogl_unpack_16 (format, p, tmp_row, width); _cogl_bitmap_unpremult_unpacked_span_16 (tmp_row, width); _cogl_pack_16 (format, tmp_row, p, width); } else { if (format & COGL_AFIRST_BIT) { for (x = 0; x < width; x++) { if (p[0] == 0) _cogl_unpremult_alpha_0 (p); else _cogl_unpremult_alpha_first (p); p += 4; } } else _cogl_bitmap_unpremult_unpacked_span_8 (p, width); } } g_free (tmp_row); _cogl_bitmap_unmap (bmp); _cogl_bitmap_set_format (bmp, format & ~COGL_PREMULT_BIT); return TRUE; } gboolean _cogl_bitmap_premult (CoglBitmap *bmp, GError **error) { uint8_t *p, *data; uint16_t *tmp_row; int x,y; CoglPixelFormat format; int width, height; int rowstride; format = cogl_bitmap_get_format (bmp); width = cogl_bitmap_get_width (bmp); height = cogl_bitmap_get_height (bmp); rowstride = cogl_bitmap_get_rowstride (bmp); if ((data = _cogl_bitmap_map (bmp, COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE, 0, error)) == NULL) return FALSE; /* If we can't directly premult the data inline then we'll allocate a temporary row and unpack the data. */ if (_cogl_bitmap_can_fast_premult (format)) tmp_row = NULL; else tmp_row = g_malloc (sizeof (uint16_t) * 4 * width); for (y = 0; y < height; y++) { p = (uint8_t*) data + y * rowstride; if (tmp_row) { _cogl_unpack_16 (format, p, tmp_row, width); _cogl_bitmap_premult_unpacked_span_16 (tmp_row, width); _cogl_pack_16 (format, tmp_row, p, width); } else { if (format & COGL_AFIRST_BIT) { for (x = 0; x < width; x++) { _cogl_premult_alpha_first (p); p += 4; } } else _cogl_bitmap_premult_unpacked_span_8 (p, width); } } g_free (tmp_row); _cogl_bitmap_unmap (bmp); _cogl_bitmap_set_format (bmp, format | COGL_PREMULT_BIT); return TRUE; } muffin-6.4.1/cogl/cogl/cogl-magazine-private.h0000664000175000017500000000426714723361714020161 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_MAGAZINE_PRIVATE_H__ #define __COGL_MAGAZINE_PRIVATE_H__ #include #include "cogl-memory-stack-private.h" typedef struct _CoglMagazineChunk CoglMagazineChunk; struct _CoglMagazineChunk { CoglMagazineChunk *next; }; typedef struct _CoglMagazine { size_t chunk_size; CoglMemoryStack *stack; CoglMagazineChunk *head; } CoglMagazine; CoglMagazine * _cogl_magazine_new (size_t chunk_size, int initial_chunk_count); static inline void * _cogl_magazine_chunk_alloc (CoglMagazine *magazine) { if (G_LIKELY (magazine->head)) { CoglMagazineChunk *chunk = magazine->head; magazine->head = chunk->next; return chunk; } else return _cogl_memory_stack_alloc (magazine->stack, magazine->chunk_size); } static inline void _cogl_magazine_chunk_free (CoglMagazine *magazine, void *data) { CoglMagazineChunk *chunk = data; chunk->next = magazine->head; magazine->head = chunk; } void _cogl_magazine_free (CoglMagazine *magazine); #endif /* __COGL_MAGAZINE_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-depth-state.h0000664000175000017500000002012214723361714017124 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_DEPTH_STATE_H__ #define __COGL_DEPTH_STATE_H__ G_BEGIN_DECLS /** * SECTION:cogl-depth-state * @short_description: Functions for describing the depth testing * state of your GPU. */ /** * CoglDepthState: * * Since: 2.0 */ typedef struct { /*< private >*/ uint32_t COGL_PRIVATE (magic); gboolean COGL_PRIVATE (test_enabled); CoglDepthTestFunction COGL_PRIVATE (test_function); gboolean COGL_PRIVATE (write_enabled); float COGL_PRIVATE (range_near); float COGL_PRIVATE (range_far); uint32_t COGL_PRIVATE (padding0); uint32_t COGL_PRIVATE (padding1); uint32_t COGL_PRIVATE (padding2); uint32_t COGL_PRIVATE (padding3); uint32_t COGL_PRIVATE (padding4); uint32_t COGL_PRIVATE (padding5); uint32_t COGL_PRIVATE (padding6); uint32_t COGL_PRIVATE (padding7); uint32_t COGL_PRIVATE (padding8); uint32_t COGL_PRIVATE (padding9); } CoglDepthState; /** * cogl_depth_state_init: * @state: A #CoglDepthState struct * * Initializes the members of @state to their default values. * * You should never pass an un initialized #CoglDepthState structure * to cogl_pipeline_set_depth_state(). * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_init (CoglDepthState *state); /** * cogl_depth_state_set_test_enabled: * @state: A #CoglDepthState struct * @enable: The enable state you want * * Enables or disables depth testing according to the value of * @enable. * * If depth testing is enable then the #CoglDepthTestFunction set * using cogl_depth_state_set_test_function() us used to evaluate * the depth value of incoming fragments against the corresponding * value stored in the current depth buffer, and if the test passes * then the fragments depth value is used to update the depth buffer. * (unless you have disabled depth writing via * cogl_depth_state_set_write_enabled()) * * By default depth testing is disabled. * * NB: this won't directly affect the state of the GPU. You have * to then set the state on a #CoglPipeline using * cogl_pipeline_set_depth_state() * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_set_test_enabled (CoglDepthState *state, gboolean enable); /** * cogl_depth_state_get_test_enabled: * @state: A #CoglDepthState struct * * Gets the current depth test enabled state as previously set by * cogl_depth_state_set_test_enabled(). * * Returns: The pipeline's current depth test enabled state. * Since: 2.0 * Stability: Unstable */ COGL_EXPORT gboolean cogl_depth_state_get_test_enabled (CoglDepthState *state); /** * cogl_depth_state_set_write_enabled: * @state: A #CoglDepthState struct * @enable: The enable state you want * * Enables or disables depth buffer writing according to the value of * @enable. Normally when depth testing is enabled and the comparison * between a fragment's depth value and the corresponding depth buffer * value passes then the fragment's depth is written to the depth * buffer unless writing is disabled here. * * By default depth writing is enabled * * NB: this won't directly affect the state of the GPU. You have * to then set the state on a #CoglPipeline using * cogl_pipeline_set_depth_state() * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_set_write_enabled (CoglDepthState *state, gboolean enable); /** * cogl_depth_state_get_write_enabled: * @state: A #CoglDepthState struct * * Gets the depth writing enable state as set by the corresponding * cogl_depth_state_set_write_enabled(). * * Returns: The current depth writing enable state * Since: 2.0 * Stability: Unstable */ COGL_EXPORT gboolean cogl_depth_state_get_write_enabled (CoglDepthState *state); /** * cogl_depth_state_set_test_function: * @state: A #CoglDepthState struct * @function: The #CoglDepthTestFunction to set * * Sets the #CoglDepthTestFunction used to compare the depth value of * an incoming fragment against the corresponding value in the current * depth buffer. * * By default the depth test function is %COGL_DEPTH_TEST_FUNCTION_LESS * * NB: this won't directly affect the state of the GPU. You have * to then set the state on a #CoglPipeline using * cogl_pipeline_set_depth_state() * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_set_test_function (CoglDepthState *state, CoglDepthTestFunction function); /** * cogl_depth_state_get_test_function: * @state: A #CoglDepthState struct * * Gets the current depth test enable state as previously set via * cogl_depth_state_set_test_enabled(). * * Returns: The current depth test enable state. * Since: 2.0 * Stability: Unstable */ COGL_EXPORT CoglDepthTestFunction cogl_depth_state_get_test_function (CoglDepthState *state); /** * cogl_depth_state_set_range: * @state: A #CoglDepthState object * @near_val: The near component of the desired depth range which will be * clamped to the range [0, 1] * @far_val: The far component of the desired depth range which will be * clamped to the range [0, 1] * * Sets the range to map depth values in normalized device coordinates * to before writing out to a depth buffer. * * After your geometry has be transformed, clipped and had perspective * division applied placing it in normalized device * coordinates all depth values between the near and far z clipping * planes are in the range -1 to 1. Before writing any depth value to * the depth buffer though the value is mapped into the range [0, 1]. * * With this function you can change the range which depth values are * mapped too although the range must still lye within the range [0, * 1]. * * By default normalized device coordinate depth values are mapped to * the full range of depth buffer values, [0, 1]. * * NB: this won't directly affect the state of the GPU. You have * to then set the state on a #CoglPipeline using * cogl_pipeline_set_depth_state(). * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_set_range (CoglDepthState *state, float near_val, float far_val); /** * cogl_depth_state_get_range: * @state: A #CoglDepthState object * @near_val: A pointer to store the near component of the depth range * @far_val: A pointer to store the far component of the depth range * * Gets the current range to which normalized depth values are mapped * before writing to the depth buffer. This corresponds to the range * set with cogl_depth_state_set_range(). * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_depth_state_get_range (CoglDepthState *state, float *near_val, float *far_val); G_END_DECLS #endif /* __COGL_DEPTH_STATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-offscreen.h0000664000175000017500000001171714723361714016666 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_OFFSCREEN_H__ #define __COGL_OFFSCREEN_H__ #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-offscreen * @short_description: Functions for creating and manipulating offscreen * framebuffers. * * Cogl allows creating and operating on offscreen framebuffers. */ typedef struct _CoglOffscreen CoglOffscreen; #define COGL_OFFSCREEN(X) ((CoglOffscreen *)X) /** * cogl_offscreen_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_offscreen_get_gtype (void); /* Offscreen api */ /** * cogl_offscreen_new_with_texture: * @texture: A #CoglTexture pointer * * This creates an offscreen framebuffer object using the given * @texture as the primary color buffer. It doesn't just initialize * the contents of the offscreen buffer with the @texture; they are * tightly bound so that drawing to the offscreen buffer effectively * updates the contents of the given texture. You don't need to * destroy the offscreen buffer before you can use the @texture again. * * This api only works with low-level #CoglTexture types such as * #CoglTexture2D and not with meta-texture types such as * #CoglTexture2DSliced. * * The storage for the framebuffer is actually allocated lazily * so this function will never return %NULL to indicate a runtime * error. This means it is still possible to configure the framebuffer * before it is really allocated. * * Simple applications without full error handling can simply rely on * Cogl to lazily allocate the storage of framebuffers but you should * be aware that if Cogl encounters an error (such as running out of * GPU memory) then your application will simply abort with an error * message. If you need to be able to catch such exceptions at runtime * then you can explicitly allocate your framebuffer when you have * finished configuring it by calling cogl_framebuffer_allocate() and * passing in a #GError argument to catch any exceptions. * * Return value: (transfer full): a newly instantiated #CoglOffscreen * framebuffer. */ COGL_EXPORT CoglOffscreen * cogl_offscreen_new_with_texture (CoglTexture *texture); /** * cogl_offscreen_new_to_texture: * @texture: A #CoglTexture pointer * * This creates an offscreen buffer object using the given @texture as the * primary color buffer. It doesn't just initialize the contents of the * offscreen buffer with the @texture; they are tightly bound so that * drawing to the offscreen buffer effectivly updates the contents of the * given texture. You don't need to destroy the offscreen buffer before * you can use the @texture again. * * This only works with low-level #CoglTexture types such as * #CoglTexture2D and not with meta-texture types such as * #CoglTexture2DSliced. * * Return value: (transfer full): a newly instantiated #CoglOffscreen * framebuffer or %NULL if it wasn't possible to create the * buffer. * Deprecated: 1.16: Use cogl_offscreen_new_with_texture instead. */ COGL_DEPRECATED_FOR (cogl_offscreen_new_with_texture) COGL_EXPORT CoglOffscreen * cogl_offscreen_new_to_texture (CoglTexture *texture); /** * cogl_is_offscreen: * @object: A pointer to a #CoglObject * * Determines whether the given #CoglObject references an offscreen * framebuffer object. * * Returns: %TRUE if @object is a #CoglOffscreen framebuffer, * %FALSE otherwise */ COGL_EXPORT gboolean cogl_is_offscreen (void *object); /** * cogl_offscreen_get_texture: (skip) */ COGL_EXPORT CoglTexture * cogl_offscreen_get_texture (CoglOffscreen *offscreen); G_END_DECLS #endif /* __COGL_OFFSCREEN_H__ */ muffin-6.4.1/cogl/cogl/cogl-driver.h0000664000175000017500000002312214723361714016200 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_DRIVER_H #define __COGL_DRIVER_H #include "cogl-context.h" #include "cogl-offscreen.h" #include "cogl-framebuffer-private.h" #include "cogl-attribute-private.h" typedef struct _CoglDriverVtable CoglDriverVtable; struct _CoglDriverVtable { gboolean (* context_init) (CoglContext *context); void (* context_deinit) (CoglContext *context); /* TODO: factor this out since this is OpenGL specific and * so can be ignored by non-OpenGL drivers. */ gboolean (* pixel_format_from_gl_internal) (CoglContext *context, GLenum gl_int_format, CoglPixelFormat *out_format); /* TODO: factor this out since this is OpenGL specific and * so can be ignored by non-OpenGL drivers. */ CoglPixelFormat (* pixel_format_to_gl) (CoglContext *context, CoglPixelFormat format, GLenum *out_glintformat, GLenum *out_glformat, GLenum *out_gltype); gboolean (* update_features) (CoglContext *context, GError **error); gboolean (* offscreen_allocate) (CoglOffscreen *offscreen, GError **error); void (* offscreen_free) (CoglOffscreen *offscreen); void (* framebuffer_flush_state) (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state); void (* framebuffer_clear) (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha); void (* framebuffer_query_bits) (CoglFramebuffer *framebuffer, CoglFramebufferBits *bits); void (* framebuffer_finish) (CoglFramebuffer *framebuffer); void (* framebuffer_flush) (CoglFramebuffer *framebuffer); void (* framebuffer_discard_buffers) (CoglFramebuffer *framebuffer, unsigned long buffers); void (* framebuffer_draw_attributes) (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); void (* framebuffer_draw_indexed_attributes) (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); gboolean (* framebuffer_read_pixels_into_bitmap) (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error); /* Destroys any driver specific resources associated with the given * 2D texture. */ void (* texture_2d_free) (CoglTexture2D *tex_2d); /* Returns TRUE if the driver can support creating a 2D texture with * the given geometry and specified internal format. */ gboolean (* texture_2d_can_create) (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format); /* Initializes driver private state before allocating any specific * storage for a 2D texture, where base texture and texture 2D * members will already be initialized before passing control to * the driver. */ void (* texture_2d_init) (CoglTexture2D *tex_2d); /* Allocates (uninitialized) storage for the given texture according * to the configured size and format of the texture */ gboolean (* texture_2d_allocate) (CoglTexture *tex, GError **error); /* Initialize the specified region of storage of the given texture * with the contents of the specified framebuffer region */ void (* texture_2d_copy_from_framebuffer) (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level); /* If the given texture has a corresponding OpenGL texture handle * then return that. * * This is optional */ unsigned int (* texture_2d_get_gl_handle) (CoglTexture2D *tex_2d); /* Update all mipmap levels > 0 */ void (* texture_2d_generate_mipmap) (CoglTexture2D *tex_2d); /* Initialize the specified region of storage of the given texture * with the contents of the specified bitmap region * * Since this may need to create the underlying storage first * it may throw a NO_MEMORY error. */ gboolean (* texture_2d_copy_from_bitmap) (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bitmap, int dst_x, int dst_y, int level, GError **error); gboolean (* texture_2d_is_get_data_supported) (CoglTexture2D *tex_2d); /* Reads back the full contents of the given texture and write it to * @data in the given @format and with the given @rowstride. * * This is optional */ void (* texture_2d_get_data) (CoglTexture2D *tex_2d, CoglPixelFormat format, int rowstride, uint8_t *data); /* Prepares for drawing by flushing the journal, framebuffer state, * pipeline state and attribute state. */ void (* flush_attributes_state) (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglFlushLayerState *layer_state, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes); /* Flushes the clip stack to the GPU using a combination of the * stencil buffer, scissor and clip plane state. */ void (* clip_stack_flush) (CoglClipStack *stack, CoglFramebuffer *framebuffer); /* Enables the driver to create some meta data to represent a buffer * but with no corresponding storage allocated yet. */ void (* buffer_create) (CoglBuffer *buffer); void (* buffer_destroy) (CoglBuffer *buffer); /* Maps a buffer into the CPU */ void * (* buffer_map_range) (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); /* Unmaps a buffer */ void (* buffer_unmap) (CoglBuffer *buffer); /* Uploads data to the buffer without needing to map it necessarily */ gboolean (* buffer_set_data) (CoglBuffer *buffer, unsigned int offset, const void *data, unsigned int size, GError **error); }; #define COGL_DRIVER_ERROR (_cogl_driver_error_quark ()) typedef enum /*< prefix=COGL_DRIVER_ERROR >*/ { COGL_DRIVER_ERROR_UNKNOWN_VERSION, COGL_DRIVER_ERROR_INVALID_VERSION, COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, COGL_DRIVER_ERROR_FAILED_TO_LOAD_LIBRARY } CoglDriverError; uint32_t _cogl_driver_error_quark (void); #endif /* __COGL_DRIVER_H */ muffin-6.4.1/cogl/cogl/cogl-index-buffer.c0000664000175000017500000000741014723361714017260 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-object-private.h" #include "cogl-indices.h" #include "cogl-indices-private.h" #include "cogl-context-private.h" #include "cogl-gtype-private.h" static void _cogl_index_buffer_free (CoglIndexBuffer *indices); COGL_BUFFER_DEFINE (IndexBuffer, index_buffer); COGL_GTYPE_DEFINE_CLASS (IndexBuffer, index_buffer); /* XXX: Unlike the wiki design this just takes a size. A single * indices buffer should be able to contain multiple ranges of indices * which the wiki design doesn't currently consider. */ CoglIndexBuffer * cogl_index_buffer_new (CoglContext *context, size_t bytes) { CoglIndexBuffer *indices = g_slice_new (CoglIndexBuffer); /* parent's constructor */ _cogl_buffer_initialize (COGL_BUFFER (indices), context, bytes, COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, COGL_BUFFER_USAGE_HINT_INDEX_BUFFER, COGL_BUFFER_UPDATE_HINT_STATIC); return _cogl_index_buffer_object_new (indices); } static void _cogl_index_buffer_free (CoglIndexBuffer *indices) { /* parent's destructor */ _cogl_buffer_fini (COGL_BUFFER (indices)); g_slice_free (CoglIndexBuffer, indices); } /* XXX: do we want a convenience function like this as an alternative * to using cogl_buffer_set_data? The advantage of this is that we can * track meta data such as the indices type and max_index_value for a * range as part of the indices buffer. If we just leave people to use * cogl_buffer_set_data then we either need a way to specify the type * and max index value at draw time or we'll want a separate way to * declare the type and max value for a range after uploading the * data. * * XXX: I think in the end it'll be that CoglIndices are to * CoglIndexBuffers as CoglAttributes are to CoglAttributeBuffers. I.e * a CoglIndexBuffer is a lite subclass of CoglBuffer that simply * implies that the buffer will later be bound as indices but doesn't * track more detailed meta data. CoglIndices build on a * CoglIndexBuffer and define the type and max_index_value for some * sub-range of a CoglIndexBuffer. */ #if 0 void cogl_index_buffer_set_data (CoglIndexBuffer *indices, CoglIndicesType type, int max_index_value, size_t write_offset, void *user_indices, int n_indices) { GList *l; for (l = indices->ranges; l; l = l->next) { } cogl_buffer_set } #endif muffin-6.4.1/cogl/cogl/cogl-pipeline-snippet-private.h0000664000175000017500000000715314723361714021650 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #ifndef __COGL_PIPELINE_SNIPPET_PRIVATE_H #define __COGL_PIPELINE_SNIPPET_PRIVATE_H #include #include "cogl-snippet.h" typedef struct { GList *entries; } CoglPipelineSnippetList; /* Arguments to pass to _cogl_pipeline_snippet_generate_code() */ typedef struct { CoglPipelineSnippetList *snippets; /* Only snippets at this hook point will be used */ CoglSnippetHook hook; /* The final function to chain on to after all of the snippets code has been run */ const char *chain_function; /* The name of the final generated function */ const char *final_name; /* A prefix to insert before each generate function name */ const char *function_prefix; /* The return type of all of the functions, or NULL to use void */ const char *return_type; /* A variable to return from the functions. The snippets are expected to modify this variable. Ignored if return_type is NULL */ const char *return_variable; /* If this is TRUE then it won't allocate a separate variable for the return value. Instead it is expected that the snippet will modify one of the argument variables directly and that will be returned */ gboolean return_variable_is_argument; /* The argument names or NULL if there are none */ const char *arguments; /* The argument types or NULL */ const char *argument_declarations; /* The string to generate the source into */ GString *source_buf; } CoglPipelineSnippetData; void _cogl_pipeline_snippet_generate_code (const CoglPipelineSnippetData *data); void _cogl_pipeline_snippet_generate_declarations (GString *declarations_buf, CoglSnippetHook hook, CoglPipelineSnippetList *list); void _cogl_pipeline_snippet_list_free (CoglPipelineSnippetList *list); void _cogl_pipeline_snippet_list_add (CoglPipelineSnippetList *list, CoglSnippet *snippet); void _cogl_pipeline_snippet_list_copy (CoglPipelineSnippetList *dst, const CoglPipelineSnippetList *src); void _cogl_pipeline_snippet_list_hash (CoglPipelineSnippetList *list, unsigned int *hash); gboolean _cogl_pipeline_snippet_list_equal (CoglPipelineSnippetList *list0, CoglPipelineSnippetList *list1); #endif /* __COGL_PIPELINE_SNIPPET_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-debug-options.h0000664000175000017500000001277614723361714017501 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ OPT (OBJECT, N_("Cogl Tracing"), "ref-counts", N_("CoglObject references"), N_("Debug ref counting issues for CoglObjects")) OPT (SLICING, N_("Cogl Tracing"), "slicing", N_("Trace Texture Slicing"), N_("debug the creation of texture slices")) OPT (ATLAS, N_("Cogl Tracing"), "atlas", N_("Trace Atlas Textures"), N_("Debug texture atlas management")) OPT (BLEND_STRINGS, N_("Cogl Tracing"), "blend-strings", N_("Trace Blend Strings"), N_("Debug CoglBlendString parsing")) OPT (JOURNAL, N_("Cogl Tracing"), "journal", N_("Trace Journal"), N_("View all the geometry passing through the journal")) OPT (BATCHING, N_("Cogl Tracing"), "batching", N_("Trace Batching"), N_("Show how geometry is being batched in the journal")) OPT (MATRICES, N_("Cogl Tracing"), "matrices", N_("Trace matrices"), N_("Trace all matrix manipulation")) /* XXX we should replace the "draw" option its very hand wavy... */ OPT (DRAW, N_("Cogl Tracing"), "draw", N_("Trace Misc Drawing"), N_("Trace some misc drawing operations")) OPT (PANGO, N_("Cogl Tracing"), "pango", N_("Trace Pango Renderer"), N_("Trace the Cogl Pango renderer")) OPT (TEXTURE_PIXMAP, N_("Cogl Tracing"), "texture-pixmap", N_("Trace CoglTexturePixmap backend"), N_("Trace the Cogl texture pixmap backend")) OPT (RECTANGLES, N_("Visualize"), "rectangles", N_("Outline rectangles"), N_("Add wire outlines for all rectangular geometry")) OPT (WIREFRAME, N_("Visualize"), "wireframe", N_("Show wireframes"), N_("Add wire outlines for all geometry")) OPT (DISABLE_BATCHING, N_("Root Cause"), "disable-batching", N_("Disable Journal batching"), N_("Disable batching of geometry in the Cogl Journal.")) OPT (DISABLE_PBOS, N_("Root Cause"), "disable-pbos", N_("Disable GL Pixel Buffers"), N_("Disable use of OpenGL pixel buffer objects")) OPT (DISABLE_SOFTWARE_TRANSFORM, N_("Root Cause"), "disable-software-transform", N_("Disable software rect transform"), N_("Use the GPU to transform rectangular geometry")) OPT (DUMP_ATLAS_IMAGE, N_("Cogl Specialist"), "dump-atlas-image", N_("Dump atlas images"), N_("Dump texture atlas changes to an image file")) OPT (DISABLE_ATLAS, N_("Root Cause"), "disable-atlas", N_("Disable texture atlasing"), N_("Disable use of texture atlasing")) OPT (DISABLE_SHARED_ATLAS, N_("Root Cause"), "disable-shared-atlas", N_("Disable sharing the texture atlas between text and images"), N_("When this is set the glyph cache will always use a separate texture " "for its atlas. Otherwise it will try to share the atlas with images.")) OPT (DISABLE_TEXTURING, N_("Root Cause"), "disable-texturing", N_("Disable texturing"), N_("Disable texturing any primitives")) OPT (DISABLE_BLENDING, N_("Root Cause"), "disable-blending", N_("Disable blending"), N_("Disable use of blending")) OPT (DISABLE_SOFTWARE_CLIP, N_("Root Cause"), "disable-software-clip", N_("Disable software clipping"), N_("Disables Cogl's attempts to clip some rectangles in software.")) OPT (SHOW_SOURCE, N_("Cogl Tracing"), "show-source", N_("Show source"), N_("Show generated GLSL source code")) OPT (OPENGL, N_("Cogl Tracing"), "opengl", N_("Trace some OpenGL"), N_("Traces some select OpenGL calls")) OPT (OFFSCREEN, N_("Cogl Tracing"), "offscreen", N_("Trace offscreen support"), N_("Debug offscreen support")) OPT (DISABLE_BLENDING, N_("Root Cause"), "disable-program-caches", N_("Disable program caches"), N_("Disable fallback caches for glsl programs")) OPT (DISABLE_FAST_READ_PIXEL, N_("Root Cause"), "disable-fast-read-pixel", N_("Disable read pixel optimization"), N_("Disable optimization for reading 1px for simple " "scenes of opaque rectangles")) OPT (CLIPPING, N_("Cogl Tracing"), "clipping", N_("Trace clipping"), N_("Logs information about how Cogl is implementing clipping")) OPT (PERFORMANCE, N_("Cogl Tracing"), "performance", N_("Trace performance concerns"), N_("Tries to highlight sub-optimal Cogl usage.")) muffin-6.4.1/cogl/cogl/cogl-context.c0000664000175000017500000004457414723361714016402 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-object.h" #include "cogl-private.h" #include "cogl-profile.h" #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-display-private.h" #include "cogl-renderer-private.h" #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-pipeline-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #include "cogl-attribute-private.h" #include "cogl1-context.h" #include "cogl-gpu-info-private.h" #include "cogl-gtype-private.h" #include "winsys/cogl-winsys-private.h" #include #include /* These aren't defined in the GLES headers */ #ifndef GL_POINT_SPRITE #define GL_POINT_SPRITE 0x8861 #endif #ifndef GL_NUM_EXTENSIONS #define GL_NUM_EXTENSIONS 0x821D #endif /* This is a relatively new extension */ #ifndef GL_PURGED_CONTEXT_RESET_NV #define GL_PURGED_CONTEXT_RESET_NV 0x92BB #endif /* These aren't defined in the GLES2 headers */ #ifndef GL_GUILTY_CONTEXT_RESET_ARB #define GL_GUILTY_CONTEXT_RESET_ARB 0x8253 #endif #ifndef GL_INNOCENT_CONTEXT_RESET_ARB #define GL_INNOCENT_CONTEXT_RESET_ARB 0x8254 #endif #ifndef GL_UNKNOWN_CONTEXT_RESET_ARB #define GL_UNKNOWN_CONTEXT_RESET_ARB 0x8255 #endif static void _cogl_context_free (CoglContext *context); COGL_OBJECT_DEFINE (Context, context); COGL_GTYPE_DEFINE_CLASS (Context, context); extern void _cogl_create_context_driver (CoglContext *context); static CoglContext *_cogl_context = NULL; static void _cogl_init_feature_overrides (CoglContext *ctx) { if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PBOS))) COGL_FLAGS_SET (ctx->private_features, COGL_PRIVATE_FEATURE_PBOS, FALSE); } const CoglWinsysVtable * _cogl_context_get_winsys (CoglContext *context) { return context->display->renderer->winsys_vtable; } static const CoglDriverVtable * _cogl_context_get_driver (CoglContext *context) { return context->driver_vtable; } /* For reference: There was some deliberation over whether to have a * constructor that could throw an exception but looking at standard * practices with several high level OO languages including python, C++, * C# Java and Ruby they all support exceptions in constructors and the * general consensus appears to be that throwing an exception is neater * than successfully constructing with an internal error status that * would then have to be explicitly checked via some form of ::is_ok() * method. */ CoglContext * cogl_context_new (CoglDisplay *display, GError **error) { CoglContext *context; uint8_t white_pixel[] = { 0xff, 0xff, 0xff, 0xff }; const CoglWinsysVtable *winsys; int i; _cogl_init (); #ifdef COGL_ENABLE_PROFILE /* We need to be absolutely sure that uprof has been initialized * before calling _cogl_uprof_init. uprof_init (NULL, NULL) * will be a NOP if it has been initialized but it will also * mean subsequent parsing of the UProf GOptionGroup will have no * affect. * * Sadly GOptionGroup based library initialization is extremely * fragile by design because GOptionGroups have no notion of * dependencies and so the order things are initialized isn't * currently under tight control. */ uprof_init (NULL, NULL); _cogl_uprof_init (); #endif /* Allocate context memory */ context = g_malloc0 (sizeof (CoglContext)); /* Convert the context into an object immediately in case any of the code below wants to verify that the context pointer is a valid object */ _cogl_context_object_new (context); /* XXX: Gross hack! * Currently everything in Cogl just assumes there is a default * context which it can access via _COGL_GET_CONTEXT() including * code used to construct a CoglContext. Until all of that code * has been updated to take an explicit context argument we have * to immediately make our pointer the default context. */ _cogl_context = context; /* Init default values */ memset (context->features, 0, sizeof (context->features)); memset (context->private_features, 0, sizeof (context->private_features)); memset (context->winsys_features, 0, sizeof (context->winsys_features)); if (!display) { CoglRenderer *renderer = cogl_renderer_new (); if (!cogl_renderer_connect (renderer, error)) { g_free (context); return NULL; } display = cogl_display_new (renderer, NULL); cogl_object_unref(renderer); } else cogl_object_ref (display); if (!cogl_display_setup (display, error)) { cogl_object_unref (display); g_free (context); return NULL; } context->display = display; /* This is duplicated data, but it's much more convenient to have the driver attached to the context and the value is accessed a lot throughout Cogl */ context->driver = display->renderer->driver; /* Again this is duplicated data, but it convenient to be able * access these from the context. */ context->driver_vtable = display->renderer->driver_vtable; context->texture_driver = display->renderer->texture_driver; for (i = 0; i < G_N_ELEMENTS (context->private_features); i++) context->private_features[i] |= display->renderer->private_features[i]; winsys = _cogl_context_get_winsys (context); if (!winsys->context_init (context, error)) { cogl_object_unref (display); g_free (context); return NULL; } if (!context->driver_vtable->context_init (context)) { cogl_object_unref (display); g_free (context); return NULL; } context->attribute_name_states_hash = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); context->attribute_name_index_map = NULL; context->n_attribute_names = 0; /* The "cogl_color_in" attribute needs a deterministic name_index * so we make sure it's the first attribute name we register */ _cogl_attribute_register_attribute_name (context, "cogl_color_in"); context->uniform_names = g_ptr_array_new_with_free_func ((GDestroyNotify) g_free); context->uniform_name_hash = g_hash_table_new (g_str_hash, g_str_equal); context->n_uniform_names = 0; /* Initialise the driver specific state */ _cogl_init_feature_overrides (context); context->sampler_cache = _cogl_sampler_cache_new (context); _cogl_pipeline_init_default_pipeline (); _cogl_pipeline_init_default_layers (); _cogl_pipeline_init_state_hash_functions (); _cogl_pipeline_init_layer_state_hash_functions (); context->current_clip_stack_valid = FALSE; context->current_clip_stack = NULL; context->legacy_backface_culling_enabled = FALSE; cogl_matrix_init_identity (&context->identity_matrix); cogl_matrix_init_identity (&context->y_flip_matrix); cogl_matrix_scale (&context->y_flip_matrix, 1, -1, 1); context->opaque_color_pipeline = cogl_pipeline_new (context); context->codegen_header_buffer = g_string_new (""); context->codegen_source_buffer = g_string_new (""); context->codegen_boilerplate_buffer = g_string_new (""); context->default_gl_texture_2d_tex = NULL; context->framebuffers = NULL; context->current_draw_buffer = NULL; context->current_read_buffer = NULL; context->current_draw_buffer_state_flushed = 0; context->current_draw_buffer_changes = COGL_FRAMEBUFFER_STATE_ALL; context->swap_callback_closures = g_hash_table_new (g_direct_hash, g_direct_equal); _cogl_list_init (&context->onscreen_events_queue); _cogl_list_init (&context->onscreen_dirty_queue); context->journal_flush_attributes_array = g_array_new (TRUE, FALSE, sizeof (CoglAttribute *)); context->journal_clip_bounds = NULL; context->polygon_vertices = g_array_new (FALSE, FALSE, sizeof (float)); context->current_pipeline = NULL; context->current_pipeline_changes_since_flush = 0; context->current_pipeline_with_color_attrib = FALSE; _cogl_bitmask_init (&context->enabled_custom_attributes); _cogl_bitmask_init (&context->enable_custom_attributes_tmp); _cogl_bitmask_init (&context->changed_bits_tmp); context->max_texture_units = -1; context->max_activateable_texture_units = -1; context->current_gl_program = 0; context->current_gl_dither_enabled = TRUE; context->gl_blend_enable_cache = FALSE; context->depth_test_enabled_cache = FALSE; context->depth_test_function_cache = COGL_DEPTH_TEST_FUNCTION_LESS; context->depth_writing_enabled_cache = TRUE; context->depth_range_near_cache = 0; context->depth_range_far_cache = 1; context->legacy_depth_test_enabled = FALSE; context->pipeline_cache = _cogl_pipeline_cache_new (); for (i = 0; i < COGL_BUFFER_BIND_TARGET_COUNT; i++) context->current_buffer[i] = NULL; context->current_path = NULL; context->stencil_pipeline = cogl_pipeline_new (context); context->quad_buffer_indices_byte = NULL; context->quad_buffer_indices = NULL; context->quad_buffer_indices_len = 0; context->rectangle_byte_indices = NULL; context->rectangle_short_indices = NULL; context->rectangle_short_indices_len = 0; context->texture_download_pipeline = NULL; context->blit_texture_pipeline = NULL; context->current_modelview_entry = NULL; context->current_projection_entry = NULL; _cogl_matrix_entry_identity_init (&context->identity_entry); _cogl_matrix_entry_cache_init (&context->builtin_flushed_projection); _cogl_matrix_entry_cache_init (&context->builtin_flushed_modelview); /* Create default textures used for fall backs */ context->default_gl_texture_2d_tex = cogl_texture_2d_new_from_data (context, 1, 1, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 0, /* rowstride */ white_pixel, NULL); /* abort on error */ context->atlases = NULL; g_hook_list_init (&context->atlas_reorganize_callbacks, sizeof (GHook)); context->buffer_map_fallback_array = g_byte_array_new (); context->buffer_map_fallback_in_use = FALSE; _cogl_list_init (&context->fences); return context; } static void _cogl_context_free (CoglContext *context) { const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); const CoglDriverVtable *driver = _cogl_context_get_driver (context); winsys->context_deinit (context); if (context->current_path) cogl_object_unref (context->current_path); if (context->default_gl_texture_2d_tex) cogl_object_unref (context->default_gl_texture_2d_tex); if (context->opaque_color_pipeline) cogl_object_unref (context->opaque_color_pipeline); if (context->blit_texture_pipeline) cogl_object_unref (context->blit_texture_pipeline); if (context->swap_callback_closures) g_hash_table_destroy (context->swap_callback_closures); if (context->journal_flush_attributes_array) g_array_free (context->journal_flush_attributes_array, TRUE); if (context->journal_clip_bounds) g_array_free (context->journal_clip_bounds, TRUE); if (context->polygon_vertices) g_array_free (context->polygon_vertices, TRUE); if (context->quad_buffer_indices_byte) cogl_object_unref (context->quad_buffer_indices_byte); if (context->quad_buffer_indices) cogl_object_unref (context->quad_buffer_indices); if (context->rectangle_byte_indices) cogl_object_unref (context->rectangle_byte_indices); if (context->rectangle_short_indices) cogl_object_unref (context->rectangle_short_indices); if (context->default_pipeline) cogl_object_unref (context->default_pipeline); if (context->dummy_layer_dependant) cogl_object_unref (context->dummy_layer_dependant); if (context->default_layer_n) cogl_object_unref (context->default_layer_n); if (context->default_layer_0) cogl_object_unref (context->default_layer_0); if (context->current_clip_stack_valid) _cogl_clip_stack_unref (context->current_clip_stack); g_slist_free (context->atlases); g_hook_list_clear (&context->atlas_reorganize_callbacks); _cogl_bitmask_destroy (&context->enabled_custom_attributes); _cogl_bitmask_destroy (&context->enable_custom_attributes_tmp); _cogl_bitmask_destroy (&context->changed_bits_tmp); if (context->current_modelview_entry) cogl_matrix_entry_unref (context->current_modelview_entry); if (context->current_projection_entry) cogl_matrix_entry_unref (context->current_projection_entry); _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_projection); _cogl_matrix_entry_cache_destroy (&context->builtin_flushed_modelview); _cogl_pipeline_cache_free (context->pipeline_cache); _cogl_sampler_cache_free (context->sampler_cache); g_ptr_array_free (context->uniform_names, TRUE); g_hash_table_destroy (context->uniform_name_hash); g_hash_table_destroy (context->attribute_name_states_hash); g_array_free (context->attribute_name_index_map, TRUE); g_byte_array_free (context->buffer_map_fallback_array, TRUE); driver->context_deinit (context); cogl_object_unref (context->display); g_free (context); } CoglContext * _cogl_context_get_default (void) { GError *error = NULL; /* Create if doesn't exist yet */ if (_cogl_context == NULL) { _cogl_context = cogl_context_new (NULL, &error); if (!_cogl_context) { g_warning ("Failed to create default context: %s", error->message); g_error_free (error); } } return _cogl_context; } CoglDisplay * cogl_context_get_display (CoglContext *context) { return context->display; } CoglRenderer * cogl_context_get_renderer (CoglContext *context) { return context->display->renderer; } gboolean _cogl_context_update_features (CoglContext *context, GError **error) { return context->driver_vtable->update_features (context, error); } void _cogl_context_set_current_projection_entry (CoglContext *context, CoglMatrixEntry *entry) { cogl_matrix_entry_ref (entry); if (context->current_projection_entry) cogl_matrix_entry_unref (context->current_projection_entry); context->current_projection_entry = entry; } void _cogl_context_set_current_modelview_entry (CoglContext *context, CoglMatrixEntry *entry) { cogl_matrix_entry_ref (entry); if (context->current_modelview_entry) cogl_matrix_entry_unref (context->current_modelview_entry); context->current_modelview_entry = entry; } char ** _cogl_context_get_gl_extensions (CoglContext *context) { const char *env_disabled_extensions; char **ret; /* In GL 3, querying GL_EXTENSIONS is deprecated so we have to build * the array using glGetStringi instead */ #ifdef HAVE_COGL_GL if (context->driver == COGL_DRIVER_GL3) { int num_extensions, i; context->glGetIntegerv (GL_NUM_EXTENSIONS, &num_extensions); ret = g_malloc (sizeof (char *) * (num_extensions + 1)); for (i = 0; i < num_extensions; i++) { const char *ext = (const char *) context->glGetStringi (GL_EXTENSIONS, i); ret[i] = g_strdup (ext); } ret[num_extensions] = NULL; } else #endif { const char *all_extensions = (const char *) context->glGetString (GL_EXTENSIONS); ret = g_strsplit (all_extensions, " ", 0 /* max tokens */); } if ((env_disabled_extensions = g_getenv ("COGL_DISABLE_GL_EXTENSIONS"))) { char **split_env_disabled_extensions; char **src, **dst; if (env_disabled_extensions) split_env_disabled_extensions = g_strsplit (env_disabled_extensions, ",", 0 /* no max tokens */); else split_env_disabled_extensions = NULL; for (dst = ret, src = ret; *src; src++) { char **d; if (split_env_disabled_extensions) for (d = split_env_disabled_extensions; *d; d++) if (!strcmp (*src, *d)) goto disabled; *(dst++) = *src; continue; disabled: g_free (*src); continue; } *dst = NULL; if (split_env_disabled_extensions) g_strfreev (split_env_disabled_extensions); } return ret; } const char * _cogl_context_get_gl_version (CoglContext *context) { const char *version_override; if ((version_override = g_getenv ("COGL_OVERRIDE_GL_VERSION"))) return version_override; else return (const char *) context->glGetString (GL_VERSION); } int64_t cogl_get_clock_time (CoglContext *context) { const CoglWinsysVtable *winsys = _cogl_context_get_winsys (context); if (winsys->context_get_clock_time) return winsys->context_get_clock_time (context); else return 0; } CoglGraphicsResetStatus cogl_get_graphics_reset_status (CoglContext *context) { if (!context->glGetGraphicsResetStatus) return COGL_GRAPHICS_RESET_STATUS_NO_ERROR; switch (context->glGetGraphicsResetStatus ()) { case GL_GUILTY_CONTEXT_RESET_ARB: return COGL_GRAPHICS_RESET_STATUS_GUILTY_CONTEXT_RESET; case GL_INNOCENT_CONTEXT_RESET_ARB: return COGL_GRAPHICS_RESET_STATUS_INNOCENT_CONTEXT_RESET; case GL_UNKNOWN_CONTEXT_RESET_ARB: return COGL_GRAPHICS_RESET_STATUS_UNKNOWN_CONTEXT_RESET; case GL_PURGED_CONTEXT_RESET_NV: return COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET; default: return COGL_GRAPHICS_RESET_STATUS_NO_ERROR; } } muffin-6.4.1/cogl/cogl/cogl-primitives.c0000664000175000017500000006621014723361714017100 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-context-private.h" #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline-private.h" #include "cogl-framebuffer-private.h" #include "cogl-attribute-private.h" #include "cogl-private.h" #include "cogl-meta-texture.h" #include "cogl-framebuffer-private.h" #include "cogl1-context.h" #include "cogl-primitives-private.h" #include #include #define _COGL_MAX_BEZ_RECURSE_DEPTH 16 typedef struct _TextureSlicedQuadState { CoglFramebuffer *framebuffer; CoglPipeline *pipeline; CoglTexture *main_texture; float tex_virtual_origin_x; float tex_virtual_origin_y; float quad_origin_x; float quad_origin_y; float v_to_q_scale_x; float v_to_q_scale_y; float quad_len_x; float quad_len_y; gboolean flipped_x; gboolean flipped_y; } TextureSlicedQuadState; typedef struct _TextureSlicedPolygonState { const CoglTextureVertex *vertices; int n_vertices; int stride; CoglAttribute **attributes; } TextureSlicedPolygonState; static void log_quad_sub_textures_cb (CoglTexture *texture, const float *subtexture_coords, const float *virtual_coords, void *user_data) { TextureSlicedQuadState *state = user_data; CoglFramebuffer *framebuffer = state->framebuffer; CoglTexture *texture_override; float quad_coords[4]; #define TEX_VIRTUAL_TO_QUAD(V, Q, AXIS) \ do { \ Q = V - state->tex_virtual_origin_##AXIS; \ Q *= state->v_to_q_scale_##AXIS; \ if (state->flipped_##AXIS) \ Q = state->quad_len_##AXIS - Q; \ Q += state->quad_origin_##AXIS; \ } while (0); TEX_VIRTUAL_TO_QUAD (virtual_coords[0], quad_coords[0], x); TEX_VIRTUAL_TO_QUAD (virtual_coords[1], quad_coords[1], y); TEX_VIRTUAL_TO_QUAD (virtual_coords[2], quad_coords[2], x); TEX_VIRTUAL_TO_QUAD (virtual_coords[3], quad_coords[3], y); #undef TEX_VIRTUAL_TO_QUAD COGL_NOTE (DRAW, "~~~~~ slice\n" "qx1: %f\t" "qy1: %f\n" "qx2: %f\t" "qy2: %f\n" "tx1: %f\t" "ty1: %f\n" "tx2: %f\t" "ty2: %f\n", quad_coords[0], quad_coords[1], quad_coords[2], quad_coords[3], subtexture_coords[0], subtexture_coords[1], subtexture_coords[2], subtexture_coords[3]); /* We only need to override the texture if it's different from the main texture */ if (texture == state->main_texture) texture_override = NULL; else texture_override = texture; _cogl_journal_log_quad (framebuffer->journal, quad_coords, state->pipeline, 1, /* one layer */ texture_override, /* replace the layer0 texture */ subtexture_coords, 4); } typedef struct _ValidateFirstLayerState { CoglPipeline *override_pipeline; } ValidateFirstLayerState; static gboolean validate_first_layer_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { ValidateFirstLayerState *state = user_data; CoglPipelineWrapMode clamp_to_edge = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; CoglPipelineWrapMode wrap_s; CoglPipelineWrapMode wrap_t; /* We can't use hardware repeat so we need to set clamp to edge * otherwise it might pull in edge pixels from the other side. By * default WRAP_MODE_AUTOMATIC becomes CLAMP_TO_EDGE so we only need * to override if the wrap mode isn't already automatic or * clamp_to_edge. */ wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index); if (wrap_s != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && wrap_s != COGL_PIPELINE_WRAP_MODE_AUTOMATIC) { if (!state->override_pipeline) state->override_pipeline = cogl_pipeline_copy (pipeline); cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline, layer_index, clamp_to_edge); } wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index); if (wrap_t != COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE && wrap_t != COGL_PIPELINE_WRAP_MODE_AUTOMATIC) { if (!state->override_pipeline) state->override_pipeline = cogl_pipeline_copy (pipeline); cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline, layer_index, clamp_to_edge); } return FALSE; } /* This path doesn't currently support multitexturing but is used for * CoglTextures that don't support repeating using the GPU so we need to * manually emit extra geometry to fake the repeating. This includes: * * - CoglTexture2DSliced: when made of > 1 slice or if the users given * texture coordinates require repeating, * - CoglTexture2DAtlas: if the users given texture coordinates require * repeating, * - CoglTexturePixmap: if the users given texture coordinates require * repeating */ /* TODO: support multitexturing */ static void _cogl_texture_quad_multiple_primitives (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglTexture *texture, int layer_index, const float *position, float tx_1, float ty_1, float tx_2, float ty_2) { TextureSlicedQuadState state; gboolean tex_virtual_flipped_x; gboolean tex_virtual_flipped_y; gboolean quad_flipped_x; gboolean quad_flipped_y; ValidateFirstLayerState validate_first_layer_state; CoglPipelineWrapMode wrap_s, wrap_t; wrap_s = cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index); wrap_t = cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index); validate_first_layer_state.override_pipeline = NULL; cogl_pipeline_foreach_layer (pipeline, validate_first_layer_cb, &validate_first_layer_state); state.framebuffer = framebuffer; state.main_texture = texture; if (validate_first_layer_state.override_pipeline) state.pipeline = validate_first_layer_state.override_pipeline; else state.pipeline = pipeline; /* Get together the data we need to transform the virtual texture * coordinates of each slice into quad coordinates... * * NB: We need to consider that the quad coordinates and the texture * coordinates may be inverted along the x or y axis, and must preserve the * inversions when we emit the final geometry. */ #define X0 0 #define Y0 1 #define X1 2 #define Y1 3 tex_virtual_flipped_x = (tx_1 > tx_2) ? TRUE : FALSE; tex_virtual_flipped_y = (ty_1 > ty_2) ? TRUE : FALSE; state.tex_virtual_origin_x = tex_virtual_flipped_x ? tx_2 : tx_1; state.tex_virtual_origin_y = tex_virtual_flipped_y ? ty_2 : ty_1; quad_flipped_x = (position[X0] > position[X1]) ? TRUE : FALSE; quad_flipped_y = (position[Y0] > position[Y1]) ? TRUE : FALSE; state.quad_origin_x = quad_flipped_x ? position[X1] : position[X0]; state.quad_origin_y = quad_flipped_y ? position[Y1] : position[Y0]; /* flatten the two forms of coordinate inversion into one... */ state.flipped_x = tex_virtual_flipped_x ^ quad_flipped_x; state.flipped_y = tex_virtual_flipped_y ^ quad_flipped_y; /* We use the _len_AXIS naming here instead of _width and _height because * log_quad_slice_cb uses a macro with symbol concatenation to handle both * axis, so this is more convenient... */ state.quad_len_x = fabs (position[X1] - position[X0]); state.quad_len_y = fabs (position[Y1] - position[Y0]); #undef X0 #undef Y0 #undef X1 #undef Y1 state.v_to_q_scale_x = fabs (state.quad_len_x / (tx_2 - tx_1)); state.v_to_q_scale_y = fabs (state.quad_len_y / (ty_2 - ty_1)); /* For backwards compatablity the default wrap mode for cogl_rectangle() is * _REPEAT... */ if (wrap_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) wrap_s = COGL_PIPELINE_WRAP_MODE_REPEAT; if (wrap_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) wrap_t = COGL_PIPELINE_WRAP_MODE_REPEAT; cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (texture), tx_1, ty_1, tx_2, ty_2, wrap_s, wrap_t, log_quad_sub_textures_cb, &state); if (validate_first_layer_state.override_pipeline) cogl_object_unref (validate_first_layer_state.override_pipeline); } typedef struct _ValidateTexCoordsState { int i; int n_layers; const float *user_tex_coords; int user_tex_coords_len; float *final_tex_coords; CoglPipeline *override_pipeline; gboolean needs_multiple_primitives; } ValidateTexCoordsState; /* * Validate the texture coordinates for this rectangle. */ static gboolean validate_tex_coords_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { ValidateTexCoordsState *state = user_data; CoglTexture *texture; const float *in_tex_coords; float *out_tex_coords; float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; CoglTransformResult transform_result; state->i++; /* FIXME: we should be able to avoid this copying when no * transform is required by the texture backend and the user * has supplied enough coordinates for all the layers. */ /* If the user didn't supply texture coordinates for this layer then use the default coords */ if (state->i >= state->user_tex_coords_len / 4) in_tex_coords = default_tex_coords; else in_tex_coords = &state->user_tex_coords[state->i * 4]; out_tex_coords = &state->final_tex_coords[state->i * 4]; memcpy (out_tex_coords, in_tex_coords, sizeof (float) * 4); texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); /* NB: NULL textures are handled by _cogl_pipeline_flush_gl_state */ if (!texture) return TRUE; /* Convert the texture coordinates to GL. */ transform_result = _cogl_texture_transform_quad_coords_to_gl (texture, out_tex_coords); /* If the texture has waste or we are using GL_TEXTURE_RECT we * can't handle texture repeating so we can't use the layer if * repeating is required. * * NB: We already know that no texture matrix is being used if the * texture doesn't support hardware repeat. */ if (transform_result == COGL_TRANSFORM_SOFTWARE_REPEAT) { if (state->i == 0) { if (state->n_layers > 1) { static gboolean warning_seen = FALSE; if (!warning_seen) g_warning ("Skipping layers 1..n of your material since " "the first layer doesn't support hardware " "repeat (e.g. because of waste or use of " "GL_TEXTURE_RECTANGLE_ARB) and you supplied " "texture coordinates outside the range [0,1]." "Falling back to software repeat assuming " "layer 0 is the most important one keep"); warning_seen = TRUE; } if (state->override_pipeline) cogl_object_unref (state->override_pipeline); state->needs_multiple_primitives = TRUE; return FALSE; } else { static gboolean warning_seen = FALSE; if (!warning_seen) g_warning ("Skipping layer %d of your material " "since you have supplied texture coords " "outside the range [0,1] but the texture " "doesn't support hardware repeat (e.g. " "because of waste or use of " "GL_TEXTURE_RECTANGLE_ARB). This isn't " "supported with multi-texturing.", state->i); warning_seen = TRUE; cogl_pipeline_set_layer_texture (pipeline, layer_index, NULL); } } /* By default WRAP_MODE_AUTOMATIC becomes to CLAMP_TO_EDGE. If the texture coordinates need repeating then we'll override this to GL_REPEAT. Otherwise we'll leave it at CLAMP_TO_EDGE so that it won't blend in pixels from the opposite side when the full texture is drawn with GL_LINEAR filter mode */ if (transform_result == COGL_TRANSFORM_HARDWARE_REPEAT) { if (cogl_pipeline_get_layer_wrap_mode_s (pipeline, layer_index) == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) { if (!state->override_pipeline) state->override_pipeline = cogl_pipeline_copy (pipeline); cogl_pipeline_set_layer_wrap_mode_s (state->override_pipeline, layer_index, COGL_PIPELINE_WRAP_MODE_REPEAT); } if (cogl_pipeline_get_layer_wrap_mode_t (pipeline, layer_index) == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) { if (!state->override_pipeline) state->override_pipeline = cogl_pipeline_copy (pipeline); cogl_pipeline_set_layer_wrap_mode_t (state->override_pipeline, layer_index, COGL_PIPELINE_WRAP_MODE_REPEAT); } } return TRUE; } /* This path supports multitexturing but only when each of the layers is * handled with a single GL texture. Also if repeating is necessary then * _cogl_texture_can_hardware_repeat() must return TRUE. * This includes layers made from: * * - CoglTexture2DSliced: if only comprised of a single slice with optional * waste, assuming the users given texture coordinates don't require * repeating. * - CoglTexture{1D,2D}: always. * - CoglTexture2DAtlas: assuming the users given texture coordinates don't * require repeating. * - CoglTexturePixmap: assuming the users given texture coordinates don't * require repeating. */ static gboolean _cogl_multitexture_quad_single_primitive (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *position, const float *user_tex_coords, int user_tex_coords_len) { int n_layers = cogl_pipeline_get_n_layers (pipeline); ValidateTexCoordsState state; float *final_tex_coords = alloca (sizeof (float) * 4 * n_layers); state.i = -1; state.n_layers = n_layers; state.user_tex_coords = user_tex_coords; state.user_tex_coords_len = user_tex_coords_len; state.final_tex_coords = final_tex_coords; state.override_pipeline = NULL; state.needs_multiple_primitives = FALSE; cogl_pipeline_foreach_layer (pipeline, validate_tex_coords_cb, &state); if (state.needs_multiple_primitives) return FALSE; if (state.override_pipeline) pipeline = state.override_pipeline; _cogl_journal_log_quad (framebuffer->journal, position, pipeline, n_layers, NULL, /* no texture override */ final_tex_coords, n_layers * 4); if (state.override_pipeline) cogl_object_unref (state.override_pipeline); return TRUE; } typedef struct _ValidateLayerState { CoglContext *ctx; int i; int first_layer; CoglPipeline *override_source; gboolean all_use_sliced_quad_fallback; } ValidateLayerState; static gboolean _cogl_rectangles_validate_layer_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { ValidateLayerState *state = user_data; CoglTexture *texture; state->i++; /* We need to ensure the mipmaps are ready before deciding * anything else about the texture because the texture storage * could completely change if it needs to be migrated out of the * atlas and will affect how we validate the layer. * * FIXME: this needs to be generalized. There could be any * number of things that might require a shuffling of the * underlying texture storage. We could add two mechanisms to * generalize this a bit... * * 1) add a _cogl_pipeline_layer_update_storage() function that * would for instance consider if mipmapping is necessary and * potentially migrate the texture from an atlas. * * 2) allow setting of transient primitive-flags on a pipeline * that may affect the outcome of _update_storage(). One flag * could indicate that we expect to sample beyond the bounds of * the texture border. * * flags = COGL_PIPELINE_PRIMITIVE_FLAG_VALID_BORDERS; * _cogl_pipeline_layer_assert_primitive_flags (layer, flags) * _cogl_pipeline_layer_update_storage (layer) * enqueue primitive in journal * * when the primitive is dequeued and drawn we should: * _cogl_pipeline_flush_gl_state (pipeline) * draw primitive * _cogl_pipeline_unassert_primitive_flags (layer, flags); * * _cogl_pipeline_layer_update_storage should take into * consideration all the asserted primitive requirements. (E.g. * there could be multiple primitives in the journal - or in a * renderlist in the future - that need mipmaps or that need * valid contents beyond their borders (for cogl_polygon) * meaning they can't work with textures in an atas, so * _cogl_pipeline_layer_update_storage would pass on these * requirements to the texture atlas backend which would make * sure the referenced texture is migrated out of the atlas and * mipmaps are generated.) */ _cogl_pipeline_pre_paint_for_layer (pipeline, layer_index); texture = cogl_pipeline_get_layer_texture (pipeline, layer_index); /* NULL textures are handled by * _cogl_pipeline_flush_gl_state */ if (texture == NULL) return TRUE; if (state->i == 0) state->first_layer = layer_index; /* XXX: * For now, if the first layer is sliced then all other layers are * ignored since we currently don't support multi-texturing with * sliced textures. If the first layer is not sliced then any other * layers found to be sliced will be skipped. (with a warning) * * TODO: Add support for multi-texturing rectangles with sliced * textures if no texture matrices are in use. */ if (cogl_texture_is_sliced (texture)) { if (state->i == 0) { if (cogl_pipeline_get_n_layers (pipeline) > 1) { static gboolean warning_seen = FALSE; if (!state->override_source) state->override_source = cogl_pipeline_copy (pipeline); _cogl_pipeline_prune_to_n_layers (state->override_source, 1); if (!warning_seen) g_warning ("Skipping layers 1..n of your pipeline since " "the first layer is sliced. We don't currently " "support any multi-texturing with sliced " "textures but assume layer 0 is the most " "important to keep"); warning_seen = TRUE; } state->all_use_sliced_quad_fallback = TRUE; return FALSE; } else { static gboolean warning_seen = FALSE; CoglTexture2D *tex_2d; if (!warning_seen) g_warning ("Skipping layer %d of your pipeline consisting of " "a sliced texture (unsupported for multi texturing)", state->i); warning_seen = TRUE; /* Note: currently only 2D textures can be sliced. */ tex_2d = state->ctx->default_gl_texture_2d_tex; cogl_pipeline_set_layer_texture (pipeline, layer_index, COGL_TEXTURE (tex_2d)); return TRUE; } } #ifdef COGL_ENABLE_DEBUG /* If the texture can't be repeated with the GPU (e.g. because it has * waste or if using GL_TEXTURE_RECTANGLE_ARB) then if a texture matrix * is also in use we don't know if the result will end up trying * to texture from the waste area. * * Note: we check can_hardware_repeat() first since it's cheaper. * * Note: cases where the texture coordinates will require repeating * will be caught by later validation. */ if (!_cogl_texture_can_hardware_repeat (texture) && _cogl_pipeline_layer_has_user_matrix (pipeline, layer_index)) { static gboolean warning_seen = FALSE; if (!warning_seen) g_warning ("layer %d of your pipeline uses a custom " "texture matrix but because the texture doesn't " "support hardware repeating you may see artefacts " "due to sampling beyond the texture's bounds.", state->i); warning_seen = TRUE; } #endif return TRUE; } void _cogl_framebuffer_draw_multitextured_rectangles ( CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglMultiTexturedRect *rects, int n_rects) { CoglContext *ctx = framebuffer->context; CoglPipeline *original_pipeline; ValidateLayerState state; int i; original_pipeline = pipeline; /* * Validate all the layers of the current source pipeline... */ state.ctx = ctx; state.i = -1; state.first_layer = 0; state.override_source = NULL; state.all_use_sliced_quad_fallback = FALSE; cogl_pipeline_foreach_layer (pipeline, _cogl_rectangles_validate_layer_cb, &state); if (state.override_source) pipeline = state.override_source; /* * Emit geometry for each of the rectangles... */ for (i = 0; i < n_rects; i++) { CoglTexture *texture; const float default_tex_coords[4] = {0.0, 0.0, 1.0, 1.0}; const float *tex_coords; if (!state.all_use_sliced_quad_fallback) { gboolean success = _cogl_multitexture_quad_single_primitive (framebuffer, pipeline, rects[i].position, rects[i].tex_coords, rects[i].tex_coords_len); /* NB: If _cogl_multitexture_quad_single_primitive fails then it * means the user tried to use texture repeat with a texture that * can't be repeated by the GPU (e.g. due to waste or use of * GL_TEXTURE_RECTANGLE_ARB) */ if (success) continue; } /* If multitexturing failed or we are drawing with a sliced texture * then we only support a single layer so we pluck out the texture * from the first pipeline layer... */ texture = cogl_pipeline_get_layer_texture (pipeline, state.first_layer); if (rects[i].tex_coords) tex_coords = rects[i].tex_coords; else tex_coords = default_tex_coords; COGL_NOTE (DRAW, "Drawing Tex Quad (Multi-Prim Mode)"); _cogl_texture_quad_multiple_primitives (framebuffer, pipeline, texture, state.first_layer, rects[i].position, tex_coords[0], tex_coords[1], tex_coords[2], tex_coords[3]); } if (pipeline != original_pipeline) cogl_object_unref (pipeline); } void cogl_2d_primitives_immediate (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, const CoglVertexP2 *vertices, unsigned int n_vertices) { CoglContext *ctx = framebuffer->context; CoglAttributeBuffer *attribute_buffer; CoglAttribute *attributes[1]; size_t vertices_size = sizeof (CoglVertexP2) * n_vertices; attribute_buffer = cogl_attribute_buffer_new (ctx, vertices_size, vertices); attributes[0] = cogl_attribute_new (attribute_buffer, "cogl_position_in", sizeof (CoglVertexP2), /* stride */ 0, /* offset */ 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); _cogl_framebuffer_draw_attributes (framebuffer, pipeline, mode, 0, /* first_index */ n_vertices, attributes, 1, COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH); cogl_object_unref (attributes[0]); cogl_object_unref (attribute_buffer); } void _cogl_rectangle_immediate (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2) { CoglVertexP2 vertices[4] = { {x_1, y_1}, {x_1, y_2}, {x_2, y_1}, {x_2, y_2} }; cogl_2d_primitives_immediate (framebuffer, pipeline, COGL_VERTICES_MODE_TRIANGLE_STRIP, vertices, 4); } muffin-6.4.1/cogl/cogl/cogl-onscreen-template.h0000664000175000017500000000774314723361714020345 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_ONSCREEN_TEMPLATE_H__ #define __COGL_ONSCREEN_TEMPLATE_H__ #include #include G_BEGIN_DECLS typedef struct _CoglOnscreenTemplate CoglOnscreenTemplate; #define COGL_ONSCREEN_TEMPLATE(OBJECT) ((CoglOnscreenTemplate *)OBJECT) /** * cogl_onscreen_template_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_onscreen_template_get_gtype (void); COGL_EXPORT CoglOnscreenTemplate * cogl_onscreen_template_new (CoglSwapChain *swap_chain); /** * cogl_onscreen_template_set_samples_per_pixel: * @onscreen_template: A #CoglOnscreenTemplate template framebuffer * @n: The minimum number of samples per pixel * * Requires that any future CoglOnscreen framebuffers derived from * this template must support making at least @n samples per pixel * which will all contribute to the final resolved color for that * pixel. * * By default this value is usually set to 0 and that is referred to * as "single-sample" rendering. A value of 1 or greater is referred * to as "multisample" rendering. * * There are some semantic differences between single-sample * rendering and multisampling with just 1 point sample such as it * being redundant to use the cogl_framebuffer_resolve_samples() and * cogl_framebuffer_resolve_samples_region() apis with single-sample * rendering. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_template_set_samples_per_pixel ( CoglOnscreenTemplate *onscreen_template, int n); /** * cogl_onscreen_template_set_stereo_enabled: * @onscreen_template: A #CoglOnscreenTemplate template framebuffer * @enabled: Whether framebuffers are created with stereo buffers * * Sets whether future #CoglOnscreen framebuffers derived from this * template are attempted to be created with both left and right * buffers, for use with stereo display. If the display system * does not support stereo, then creation of the framebuffer will * fail. * * Since: 1.20 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_template_set_stereo_enabled ( CoglOnscreenTemplate *onscreen_template, gboolean enabled); /** * cogl_is_onscreen_template: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglOnscreenTemplate. * * Return value: %TRUE if the object references a #CoglOnscreenTemplate * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_onscreen_template (void *object); G_END_DECLS #endif /* __COGL_ONSCREEN_TEMPLATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-swap-chain.c0000664000175000017500000000417014723361714016734 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-object.h" #include "cogl-swap-chain-private.h" #include "cogl-swap-chain.h" #include "cogl-gtype-private.h" static void _cogl_swap_chain_free (CoglSwapChain *swap_chain); COGL_OBJECT_DEFINE (SwapChain, swap_chain); COGL_GTYPE_DEFINE_CLASS (SwapChain, swap_chain); static void _cogl_swap_chain_free (CoglSwapChain *swap_chain) { g_slice_free (CoglSwapChain, swap_chain); } CoglSwapChain * cogl_swap_chain_new (void) { CoglSwapChain *swap_chain = g_slice_new0 (CoglSwapChain); swap_chain->length = -1; /* no preference */ return _cogl_swap_chain_object_new (swap_chain); } void cogl_swap_chain_set_has_alpha (CoglSwapChain *swap_chain, gboolean has_alpha) { swap_chain->has_alpha = has_alpha; } void cogl_swap_chain_set_length (CoglSwapChain *swap_chain, int length) { swap_chain->length = length; } muffin-6.4.1/cogl/cogl/cogl-pipeline-private.h0000664000175000017500000007146214723361714020174 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_PRIVATE_H #define __COGL_PIPELINE_PRIVATE_H #include "cogl-node-private.h" #include "cogl-pipeline-layer-private.h" #include "cogl-pipeline.h" #include "cogl-matrix.h" #include "cogl-object-private.h" #include "cogl-profile.h" #include "cogl-list.h" #include "cogl-boxed-value.h" #include "cogl-pipeline-snippet-private.h" #include "cogl-pipeline-state.h" #include "cogl-framebuffer.h" #include "cogl-bitmask.h" #include #if !(defined(HAVE_COGL_GL) || defined(HAVE_COGL_GLES2)) #error No drivers defined #endif /* defined(HAVE_COGL_GL) || defined(HAVE_COGL_GLES2) */ /* XXX: should I rename these as * COGL_PIPELINE_STATE_INDEX_XYZ... ? */ typedef enum { /* sparse state */ COGL_PIPELINE_STATE_COLOR_INDEX, COGL_PIPELINE_STATE_LAYERS_INDEX, COGL_PIPELINE_STATE_ALPHA_FUNC_INDEX, COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE_INDEX, COGL_PIPELINE_STATE_BLEND_INDEX, COGL_PIPELINE_STATE_USER_SHADER_INDEX, COGL_PIPELINE_STATE_DEPTH_INDEX, COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE_INDEX, COGL_PIPELINE_STATE_POINT_SIZE_INDEX, COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE_INDEX, COGL_PIPELINE_STATE_CULL_FACE_INDEX, COGL_PIPELINE_STATE_UNIFORMS_INDEX, COGL_PIPELINE_STATE_VERTEX_SNIPPETS_INDEX, COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS_INDEX, /* non-sparse */ COGL_PIPELINE_STATE_REAL_BLEND_ENABLE_INDEX, COGL_PIPELINE_STATE_COUNT } CoglPipelineStateIndex; #define COGL_PIPELINE_STATE_SPARSE_COUNT (COGL_PIPELINE_STATE_COUNT - 1) /* Used in pipeline->differences masks and for notifying pipeline * state changes. * * XXX: If you add or remove state groups here you may need to update * some of the state masks following this enum too! * * FIXME: perhaps it would be better to rename this enum to * CoglPipelineStateGroup to better convey the fact that a single enum * here can map to multiple properties. */ typedef enum _CoglPipelineState { COGL_PIPELINE_STATE_COLOR = 1L<big_state. */ /* Layers represent their state in a tree structure where some of * the state relating to a given pipeline or layer may actually be * owned by one if is ancestors in the tree. We have a common data * type to track the tree heirachy so we can share code... */ CoglNode _parent; /* When weak pipelines are destroyed the user is notified via this * callback */ CoglPipelineDestroyCallback destroy_callback; /* When notifying that a weak pipeline has been destroyed this * private data is passed to the above callback */ void *destroy_data; /* We need to track if a pipeline is referenced in the journal * because we can't allow modification to these pipelines without * flushing the journal first */ unsigned int journal_ref_count; /* A mask of which sparse state groups are different in this * pipeline in comparison to its parent. */ unsigned int differences; /* Whenever a pipeline is modified we increment the age. There's no * guarantee that it won't wrap but it can nevertheless be a * convenient mechanism to determine when a pipeline has been * changed to you can invalidate some some associated cache that * depends on the old state. */ unsigned int age; /* This is the primary color of the pipeline. * * This is a sparse property, ref COGL_PIPELINE_STATE_COLOR */ CoglColor color; /* A pipeline may be made up with multiple layers used to combine * textures together. * * This is sparse state, ref COGL_PIPELINE_STATE_LAYERS */ unsigned int n_layers; GList *layer_differences; /* As a basic way to reduce memory usage we divide the pipeline * state into two groups; the minimal state modified in 90% of * all pipelines and the rest, so that the second group can * be allocated dynamically when required... */ CoglPipelineBigState *big_state; #ifdef COGL_DEBUG_ENABLED /* For debugging purposes it's possible to associate a static const * string with a pipeline which can be an aid when trying to trace * where the pipeline originates from */ const char *static_breadcrumb; #endif /* Cached state... */ /* A cached, complete list of the layers this pipeline depends * on sorted by layer->unit_index. */ CoglPipelineLayer **layers_cache; /* To avoid a separate ->layers_cache allocation for common * pipelines with only a few layers... */ CoglPipelineLayer *short_layers_cache[3]; /* XXX: consider adding an authorities cache to speed up sparse * property value lookups: * CoglPipeline *authorities_cache[COGL_PIPELINE_N_SPARSE_PROPERTIES]; * and corresponding authorities_cache_dirty:1 bitfield */ /* bitfields */ /* Weak pipelines don't count as dependants on their parents which * means that the parent pipeline can be modified without * considering how the modifications may affect the weak pipeline. */ unsigned int is_weak:1; /* Determines if pipeline->big_state is valid */ unsigned int has_big_state:1; /* There are many factors that can determine if we need to enable * blending, this holds our final decision */ unsigned int real_blend_enable:1; /* Since the code for deciding if blending really needs to be * enabled for a particular pipeline is quite expensive we update * the real_blend_enable flag lazily when flushing a pipeline if * this dirty flag has been set. */ unsigned int dirty_real_blend_enable:1; /* Whenever a pipeline is flushed we keep track of whether the * pipeline was used with a color attribute where we don't know * whether the colors are opaque. The real_blend_enable state * depends on this, and must be updated whenever this changes (even * if dirty_real_blend_enable isn't set) */ unsigned int unknown_color_alpha:1; unsigned int layers_cache_dirty:1; #ifdef COGL_DEBUG_ENABLED /* For debugging purposes it's possible to associate a static const * string with a pipeline which can be an aid when trying to trace * where the pipeline originates from */ unsigned int has_static_breadcrumb:1; #endif }; typedef struct _CoglPipelineFragend { void (*start) (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference); gboolean (*add_layer) (CoglPipeline *pipeline, CoglPipelineLayer *layer, unsigned long layers_difference); gboolean (*end) (CoglPipeline *pipeline, unsigned long pipelines_difference); void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color); void (*layer_pre_change_notify) (CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change); } CoglPipelineFragend; typedef struct _CoglPipelineVertend { void (*start) (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference); gboolean (*add_layer) (CoglPipeline *pipeline, CoglPipelineLayer *layer, unsigned long layers_difference, CoglFramebuffer *framebuffer); gboolean (*end) (CoglPipeline *pipeline, unsigned long pipelines_difference); void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color); void (*layer_pre_change_notify) (CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change); } CoglPipelineVertend; typedef struct { gboolean (*start) (CoglPipeline *pipeline); void (*end) (CoglPipeline *pipeline, unsigned long pipelines_difference); void (*pipeline_pre_change_notify) (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color); void (*layer_pre_change_notify) (CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change); /* This is called after all of the other functions whenever the pipeline is flushed, even if the pipeline hasn't changed since the last flush */ void (* pre_paint) (CoglPipeline *pipeline, CoglFramebuffer *framebuffer); } CoglPipelineProgend; extern const CoglPipelineFragend *_cogl_pipeline_fragend; extern const CoglPipelineVertend *_cogl_pipeline_vertend; extern const CoglPipelineProgend *_cogl_pipeline_progend; void _cogl_pipeline_init_default_pipeline (void); static inline CoglPipeline * _cogl_pipeline_get_parent (CoglPipeline *pipeline) { CoglNode *parent_node = COGL_NODE (pipeline)->parent; return COGL_PIPELINE (parent_node); } static inline CoglPipeline * _cogl_pipeline_get_authority (CoglPipeline *pipeline, unsigned long difference) { CoglPipeline *authority = pipeline; while (!(authority->differences & difference)) authority = _cogl_pipeline_get_parent (authority); return authority; } typedef gboolean (*CoglPipelineStateComparitor) (CoglPipeline *authority0, CoglPipeline *authority1); void _cogl_pipeline_update_authority (CoglPipeline *pipeline, CoglPipeline *authority, CoglPipelineState state, CoglPipelineStateComparitor comparitor); void _cogl_pipeline_pre_change_notify (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color, gboolean from_layer_change); void _cogl_pipeline_prune_redundant_ancestry (CoglPipeline *pipeline); void _cogl_pipeline_update_real_blend_enable (CoglPipeline *pipeline, gboolean unknown_color_alpha); typedef enum { COGL_PIPELINE_GET_LAYER_NO_CREATE = 1<<0 } CoglPipelineGetLayerFlags; CoglPipelineLayer * _cogl_pipeline_get_layer_with_flags (CoglPipeline *pipeline, int layer_index, CoglPipelineGetLayerFlags flags); #define _cogl_pipeline_get_layer(p, l) \ _cogl_pipeline_get_layer_with_flags (p, l, 0) gboolean _cogl_is_pipeline_layer (void *object); void _cogl_pipeline_prune_empty_layer_difference (CoglPipeline *layers_authority, CoglPipelineLayer *layer); /* * SECTION:cogl-pipeline-internals * @short_description: Functions for creating custom primitives that make use * of Cogl pipelines for filling. * * Normally you shouldn't need to use this API directly, but if you need to * developing a custom/specialised primitive - probably using raw OpenGL - then * this API aims to expose enough of the pipeline internals to support being * able to fill your geometry according to a given Cogl pipeline. */ gboolean _cogl_pipeline_get_real_blend_enabled (CoglPipeline *pipeline); /* * Calls the pre_paint method on the layer texture if there is * one. This will determine whether mipmaps are needed based on the * filter settings. */ void _cogl_pipeline_pre_paint_for_layer (CoglPipeline *pipeline, int layer_id); /* * CoglPipelineFlushFlag: * @COGL_PIPELINE_FLUSH_FALLBACK_MASK: The fallback_layers member is set to * a uint32_t mask of the layers that can't be supported with the user * supplied texture and need to be replaced with fallback textures. (1 = * fallback, and the least significant bit = layer 0) * @COGL_PIPELINE_FLUSH_DISABLE_MASK: The disable_layers member is set to * a uint32_t mask of the layers that you want to completly disable * texturing for (1 = fallback, and the least significant bit = layer 0) * @COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE: The layer0_override_texture member is * set to a GLuint OpenGL texture name to override the texture used for * layer 0 of the pipeline. This is intended for dealing with sliced * textures where you will need to point to each of the texture slices in * turn when drawing your geometry. Passing a value of 0 is the same as * not passing the option at all. * @COGL_PIPELINE_FLUSH_SKIP_GL_COLOR: When flushing the GL state for the * pipeline don't call glColor. */ typedef enum _CoglPipelineFlushFlag { COGL_PIPELINE_FLUSH_FALLBACK_MASK = 1L<<0, COGL_PIPELINE_FLUSH_DISABLE_MASK = 1L<<1, COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE = 1L<<2, COGL_PIPELINE_FLUSH_SKIP_GL_COLOR = 1L<<3 } CoglPipelineFlushFlag; /* * CoglPipelineFlushOptions: * */ typedef struct _CoglPipelineFlushOptions { CoglPipelineFlushFlag flags; uint32_t fallback_layers; uint32_t disable_layers; CoglTexture *layer0_override_texture; } CoglPipelineFlushOptions; unsigned int _cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func); /* * _cogl_pipeline_weak_copy: * @pipeline: A #CoglPipeline object * @callback: A callback to notify when your weak pipeline is destroyed * @user_data: Private data to pass to your given callback. * * Returns a weak copy of the given source @pipeline. Unlike a normal * copy no internal reference is taken on the source @pipeline and you * can expect that later modifications of the source pipeline (or in * fact any other pipeline) can result in the weak pipeline being * destroyed. * * To understand this better its good to know a bit about the internal * design of #CoglPipeline... * * Internally #CoglPipelines are represented as a graph of * property diff's, where each node is a diff of properties that gets * applied on top of its parent. Copying a pipeline creates an empty * diff and a child->parent relationship between the empty diff and * the source @pipeline, parent. * * Because of this internal graph design a single #CoglPipeline may * indirectly depend on a chain of ancestors to fully define all of * its properties. Because a node depends on its ancestors it normally * owns a reference to its parent to stop it from being freed. Also if * you try to modify a pipeline with children we internally use a * copy-on-write mechanism to ensure that you don't indirectly change * the properties those children. * * Weak pipelines avoid the use of copy-on-write to preserve the * integrity of weak dependants and instead weak dependants are * simply destroyed allowing the parent to be modified directly. Also * because weak pipelines don't own a reference to their parent they * won't stop the source @pipeline from being freed when the user * releases their reference on it. * * Because weak pipelines don't own a reference on their parent they * are the recommended mechanism for creating derived pipelines that you * want to cache as a private property of the original pipeline * because they won't result in a circular dependency. * * An example use case: * * Consider for example you are implementing a custom primitive that is * not compatible with certain source pipelines. To handle this you * implement a validation stage that given an arbitrary pipeline as * input will create a derived pipeline that is suitable for drawing * your primitive. * * Because you don't want to have to repeat this validation every time * the same incompatible pipeline is given as input you want to cache * the result as a private property of the original pipeline. If the * derived pipeline were created using cogl_pipeline_copy that would * create a circular dependency so the original pipeline can never be * freed. * * If you instead create a weak copy you won't stop the original pipeline * from being freed if it's no longer needed, and you will instead simply * be notified that your weak pipeline has been destroyed. * * This is the recommended coding pattern for validating an input * pipeline and caching a derived result: * |[ * static CoglUserDataKey _cogl_my_cache_key; * * typedef struct { * CoglPipeline *validated_source; * } MyValidatedMaterialCache; * * static void * destroy_cache_cb (CoglObject *object, void *user_data) * { * g_slice_free (MyValidatedMaterialCache, user_data); * } * * static void * invalidate_cache_cb (CoglPipeline *destroyed, void *user_data) * { * MyValidatedMaterialCache *cache = user_data; * cogl_object_unref (cache->validated_source); * cache->validated_source = NULL; * } * * static CoglPipeline * * get_validated_pipeline (CoglPipeline *source) * { * MyValidatedMaterialCache *cache = * cogl_object_get_user_data (COGL_OBJECT (source), * &_cogl_my_cache_key); * if (G_UNLIKELY (cache == NULL)) * { * cache = g_slice_new (MyValidatedMaterialCache); * cogl_object_set_user_data (COGL_OBJECT (source), * &_cogl_my_cache_key, * cache, destroy_cache_cb); * cache->validated_source = source; * } * * if (G_UNLIKELY (cache->validated_source == NULL)) * { * cache->validated_source = source; * * / * Start validating source... * / * * / * If you find you need to change something... * / * if (cache->validated_source == source) * cache->validated_source = * cogl_pipeline_weak_copy (source, * invalidate_cache_cb, * cache); * * / * Modify cache->validated_source * / * } * * return cache->validated_source; * } * ]| */ CoglPipeline * _cogl_pipeline_weak_copy (CoglPipeline *pipeline, CoglPipelineDestroyCallback callback, void *user_data); void _cogl_pipeline_set_progend (CoglPipeline *pipeline, int progend); void _cogl_pipeline_get_colorubv (CoglPipeline *pipeline, uint8_t *color); /* XXX: At some point it could be good for this to accept a mask of * the state groups we are interested in comparing since we can * probably use that information in a number situations to reduce * the work we do. */ unsigned long _cogl_pipeline_compare_differences (CoglPipeline *pipeline0, CoglPipeline *pipeline1); gboolean _cogl_pipeline_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1, unsigned int differences, unsigned long layer_differences, CoglPipelineEvalFlags flags); unsigned int _cogl_pipeline_hash (CoglPipeline *pipeline, unsigned int differences, unsigned long layer_differences, CoglPipelineEvalFlags flags); /* Makes a copy of the given pipeline that is a child of the root * pipeline rather than a child of the source pipeline. That way the * new pipeline won't hold a reference to the source pipeline. The * differences specified in @differences and @layer_differences are * copied across and all other state is left with the default * values. */ CoglPipeline * _cogl_pipeline_deep_copy (CoglPipeline *pipeline, unsigned long differences, unsigned long layer_differences); CoglPipeline * _cogl_pipeline_journal_ref (CoglPipeline *pipeline); void _cogl_pipeline_journal_unref (CoglPipeline *pipeline); const CoglMatrix * _cogl_pipeline_get_layer_matrix (CoglPipeline *pipeline, int layer_index); void _cogl_pipeline_texture_storage_change_notify (CoglTexture *texture); void _cogl_pipeline_apply_legacy_state (CoglPipeline *pipeline); void _cogl_pipeline_apply_overrides (CoglPipeline *pipeline, CoglPipelineFlushOptions *options); #ifdef COGL_DEBUG_ENABLED void _cogl_pipeline_set_static_breadcrumb (CoglPipeline *pipeline, const char *breadcrumb); #endif unsigned long _cogl_pipeline_get_age (CoglPipeline *pipeline); void _cogl_pipeline_add_layer_difference (CoglPipeline *pipeline, CoglPipelineLayer *layer, gboolean inc_n_layers); void _cogl_pipeline_remove_layer_difference (CoglPipeline *pipeline, CoglPipelineLayer *layer, gboolean dec_n_layers); CoglPipeline * _cogl_pipeline_find_equivalent_parent (CoglPipeline *pipeline, CoglPipelineState pipeline_state, CoglPipelineLayerState layer_state); void _cogl_pipeline_get_layer_combine_constant (CoglPipeline *pipeline, int layer_index, float *constant); COGL_EXPORT void _cogl_pipeline_prune_to_n_layers (CoglPipeline *pipeline, int n); /* * API to support the deprecate cogl_pipeline_layer_xyz functions... */ typedef gboolean (*CoglPipelineInternalLayerCallback) (CoglPipelineLayer *layer, void *user_data); COGL_EXPORT void _cogl_pipeline_foreach_layer_internal (CoglPipeline *pipeline, CoglPipelineInternalLayerCallback callback, void *user_data); gboolean _cogl_pipeline_layer_numbers_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1); gboolean _cogl_pipeline_layer_and_unit_numbers_equal (CoglPipeline *pipeline0, CoglPipeline *pipeline1); gboolean _cogl_pipeline_need_texture_combine_separate (CoglPipelineLayer *combine_authority); void _cogl_pipeline_init_state_hash_functions (void); void _cogl_pipeline_init_layer_state_hash_functions (void); CoglPipelineState _cogl_pipeline_get_state_for_vertex_codegen (CoglContext *context); CoglPipelineLayerState _cogl_pipeline_get_layer_state_for_fragment_codegen (CoglContext *context); CoglPipelineState _cogl_pipeline_get_state_for_fragment_codegen (CoglContext *context); #endif /* __COGL_PIPELINE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-boxed-value.h0000664000175000017500000000644114723361714017125 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_BOXED_VALUE_H #define __COGL_BOXED_VALUE_H #include #include "cogl-context.h" typedef enum { COGL_BOXED_NONE, COGL_BOXED_INT, COGL_BOXED_FLOAT, COGL_BOXED_MATRIX } CoglBoxedType; typedef struct _CoglBoxedValue { CoglBoxedType type; int size, count; union { float float_value[4]; int int_value[4]; float matrix[16]; float *float_array; int *int_array; void *array; } v; } CoglBoxedValue; #define _cogl_boxed_value_init(bv) \ G_STMT_START { \ CoglBoxedValue *_bv = (bv); \ _bv->type = COGL_BOXED_NONE; \ _bv->count = 1; \ } G_STMT_END gboolean _cogl_boxed_value_equal (const CoglBoxedValue *bva, const CoglBoxedValue *bvb); void _cogl_boxed_value_set_1f (CoglBoxedValue *bv, float value); void _cogl_boxed_value_set_1i (CoglBoxedValue *bv, int value); void _cogl_boxed_value_set_float (CoglBoxedValue *bv, int n_components, int count, const float *value); void _cogl_boxed_value_set_int (CoglBoxedValue *bv, int n_components, int count, const int *value); void _cogl_boxed_value_set_matrix (CoglBoxedValue *bv, int dimensions, int count, gboolean transpose, const float *value); /* * _cogl_boxed_value_copy: * @dst: The destination boxed value * @src: The source boxed value * * This copies @src to @dst. It is assumed that @dst is initialised. */ void _cogl_boxed_value_copy (CoglBoxedValue *dst, const CoglBoxedValue *src); void _cogl_boxed_value_destroy (CoglBoxedValue *bv); void _cogl_boxed_value_set_uniform (CoglContext *ctx, int location, const CoglBoxedValue *value); #endif /* __COGL_BOXED_VALUE_H */ muffin-6.4.1/cogl/cogl/cogl-pipeline-state-private.h0000664000175000017500000001365414723361714021311 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_STATE_PRIVATE_H #define __COGL_PIPELINE_STATE_PRIVATE_H CoglPipeline * _cogl_pipeline_get_user_program (CoglPipeline *pipeline); gboolean _cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline); gboolean _cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline); gboolean _cogl_pipeline_has_non_layer_vertex_snippets (CoglPipeline *pipeline); gboolean _cogl_pipeline_has_non_layer_fragment_snippets (CoglPipeline *pipeline); gboolean _cogl_pipeline_color_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_alpha_func_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_alpha_func_reference_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_blend_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_depth_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_non_zero_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_per_vertex_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_logic_ops_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_user_shader_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_cull_face_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); gboolean _cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0, CoglPipeline *authority1); void _cogl_pipeline_hash_color_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_layers_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_alpha_func_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_alpha_func_reference_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_blend_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_user_shader_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_depth_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_non_zero_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_per_vertex_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_logic_ops_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_cull_face_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_uniforms_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority, CoglPipelineHashState *state); void _cogl_pipeline_compare_uniform_differences (unsigned long *differences, CoglPipeline *pipeline0, CoglPipeline *pipeline1); #endif /* __COGL_PIPELINE_STATE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-matrix-stack.h0000664000175000017500000005553414723361714017330 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Havoc Pennington for litl * Robert Bragg */ #ifndef _COGL_MATRIX_STACK_H_ #define _COGL_MATRIX_STACK_H_ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #include "cogl-matrix.h" #include "cogl-context.h" #include /** * SECTION:cogl-matrix-stack * @short_description: Functions for efficiently tracking many * related transformations * * Matrices can be used (for example) to describe the model-view * transforms of objects, texture transforms, and projective * transforms. * * The #CoglMatrix api provides a good way to manipulate individual * matrices representing a single transformation but if you need to * track many-many such transformations for many objects that are * organized in a scenegraph for example then using a separate * #CoglMatrix for each object may not be the most efficient way. * * A #CoglMatrixStack enables applications to track lots of * transformations that are related to each other in some kind of * hierarchy. In a scenegraph for example if you want to know how to * transform a particular node then you usually have to walk up * through the ancestors and accumulate their transforms before * finally applying the transform of the node itself. In this model * things are grouped together spatially according to their ancestry * and all siblings with the same parent share the same initial * transformation. The #CoglMatrixStack API is suited to tracking lots * of transformations that fit this kind of model. * * Compared to using the #CoglMatrix api directly to track many * related transforms, these can be some advantages to using a * #CoglMatrixStack: * * Faster equality comparisons of transformations * Efficient comparisons of the differences between arbitrary * transformations * Avoid redundant arithmetic related to common transforms * * Can be more space efficient (not always though) * * * For reference (to give an idea of when a #CoglMatrixStack can * provide a space saving) a #CoglMatrix can be expected to take 72 * bytes whereas a single #CoglMatrixEntry in a #CoglMatrixStack is * currently around 32 bytes on a 32bit CPU or 36 bytes on a 64bit * CPU. An entry is needed for each individual operation applied to * the stack (such as rotate, scale, translate) so if most of your * leaf node transformations only need one or two simple operations * relative to their parent then a matrix stack will likely take less * space than having a #CoglMatrix for each node. * * Even without any space saving though the ability to perform fast * comparisons and avoid redundant arithmetic (especially sine and * cosine calculations for rotations) can make using a matrix stack * worthwhile. */ /** * CoglMatrixStack: * * Tracks your current position within a hierarchy and lets you build * up a graph of transformations as you traverse through a hierarchy * such as a scenegraph. * * A #CoglMatrixStack always maintains a reference to a single * transformation at any point in time, representing the * transformation at the current position in the hierarchy. You can * get a reference to the current transformation by calling * cogl_matrix_stack_get_entry(). * * When a #CoglMatrixStack is first created with * cogl_matrix_stack_new() then it is conceptually positioned at the * root of your hierarchy and the current transformation simply * represents an identity transformation. * * As you traverse your object hierarchy (your scenegraph) then you * should call cogl_matrix_stack_push() whenever you move down one * level and call cogl_matrix_stack_pop() whenever you move back up * one level towards the root. * * At any time you can apply a set of operations, such as "rotate", * "scale", "translate" on top of the current transformation of a * #CoglMatrixStack using functions such as * cogl_matrix_stack_rotate(), cogl_matrix_stack_scale() and * cogl_matrix_stack_translate(). These operations will derive a new * current transformation and will never affect a transformation * that you have referenced using cogl_matrix_stack_get_entry(). * * Internally applying operations to a #CoglMatrixStack builds up a * graph of #CoglMatrixEntry structures which each represent a single * immutable transform. */ typedef struct _CoglMatrixStack CoglMatrixStack; /** * cogl_matrix_stack_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_matrix_stack_get_gtype (void); /** * CoglMatrixEntry: * * Represents a single immutable transformation that was retrieved * from a #CoglMatrixStack using cogl_matrix_stack_get_entry(). * * Internally a #CoglMatrixEntry represents a single matrix * operation (such as "rotate", "scale", "translate") which is applied * to the transform of a single parent entry. * * Using the #CoglMatrixStack api effectively builds up a graph of * these immutable #CoglMatrixEntry structures whereby operations * that can be shared between multiple transformations will result * in shared #CoglMatrixEntry nodes in the graph. * * When a #CoglMatrixStack is first created it references one * #CoglMatrixEntry that represents a single "load identity" * operation. This serves as the root entry and all operations * that are then applied to the stack will extend the graph * starting from this root "load identity" entry. * * Given the typical usage model for a #CoglMatrixStack and the way * the entries are built up while traversing a scenegraph then in most * cases where an application is interested in comparing two * transformations for equality then it is enough to simply compare * two #CoglMatrixEntry pointers directly. Technically this can lead * to false negatives that could be identified with a deeper * comparison but often these false negatives are unlikely and * don't matter anyway so this enables extremely cheap comparisons. * * #CoglMatrixEntrys are reference counted using * cogl_matrix_entry_ref() and cogl_matrix_entry_unref() not with * cogl_object_ref() and cogl_object_unref(). */ typedef struct _CoglMatrixEntry CoglMatrixEntry; /** * cogl_matrix_entry_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_matrix_entry_get_gtype (void); /** * cogl_matrix_stack_new: * @ctx: A #CoglContext * * Allocates a new #CoglMatrixStack that can be used to build up * transformations relating to objects in a scenegraph like hierarchy. * (See the description of #CoglMatrixStack and #CoglMatrixEntry for * more details of what a matrix stack is best suited for) * * When a #CoglMatrixStack is first allocated it is conceptually * positioned at the root of your scenegraph hierarchy. As you * traverse your scenegraph then you should call * cogl_matrix_stack_push() whenever you move down a level and * cogl_matrix_stack_pop() whenever you move back up a level towards * the root. * * Once you have allocated a #CoglMatrixStack you can get a reference * to the current transformation for the current position in the * hierarchy by calling cogl_matrix_stack_get_entry(). * * Once you have allocated a #CoglMatrixStack you can apply operations * such as rotate, scale and translate to modify the current transform * for the current position in the hierarchy by calling * cogl_matrix_stack_rotate(), cogl_matrix_stack_scale() and * cogl_matrix_stack_translate(). * * Return value: (transfer full): A newly allocated #CoglMatrixStack */ COGL_EXPORT CoglMatrixStack * cogl_matrix_stack_new (CoglContext *ctx); /** * cogl_matrix_stack_push: * @stack: A #CoglMatrixStack * * Saves the current transform and starts a new transform that derives * from the current transform. * * This is usually called while traversing a scenegraph whenever you * traverse one level deeper. cogl_matrix_stack_pop() can then be * called when going back up one layer to restore the previous * transform of an ancestor. */ COGL_EXPORT void cogl_matrix_stack_push (CoglMatrixStack *stack); /** * cogl_matrix_stack_pop: * @stack: A #CoglMatrixStack * * Restores the previous transform that was last saved by calling * cogl_matrix_stack_push(). * * This is usually called while traversing a scenegraph whenever you * return up one level in the graph towards the root node. */ COGL_EXPORT void cogl_matrix_stack_pop (CoglMatrixStack *stack); /** * cogl_matrix_stack_load_identity: * @stack: A #CoglMatrixStack * * Resets the current matrix to the identity matrix. */ COGL_EXPORT void cogl_matrix_stack_load_identity (CoglMatrixStack *stack); /** * cogl_matrix_stack_scale: * @stack: A #CoglMatrixStack * @x: Amount to scale along the x-axis * @y: Amount to scale along the y-axis * @z: Amount to scale along the z-axis * * Multiplies the current matrix by one that scales the x, y and z * axes by the given values. */ COGL_EXPORT void cogl_matrix_stack_scale (CoglMatrixStack *stack, float x, float y, float z); /** * cogl_matrix_stack_translate: * @stack: A #CoglMatrixStack * @x: Distance to translate along the x-axis * @y: Distance to translate along the y-axis * @z: Distance to translate along the z-axis * * Multiplies the current matrix by one that translates along all * three axes according to the given values. */ COGL_EXPORT void cogl_matrix_stack_translate (CoglMatrixStack *stack, float x, float y, float z); /** * cogl_matrix_stack_rotate: * @stack: A #CoglMatrixStack * @angle: Angle in degrees to rotate. * @x: X-component of vertex to rotate around. * @y: Y-component of vertex to rotate around. * @z: Z-component of vertex to rotate around. * * Multiplies the current matrix by one that rotates the around the * axis-vector specified by @x, @y and @z. The rotation follows the * right-hand thumb rule so for example rotating by 10 degrees about * the axis-vector (0, 0, 1) causes a small counter-clockwise * rotation. */ COGL_EXPORT void cogl_matrix_stack_rotate (CoglMatrixStack *stack, float angle, float x, float y, float z); /** * cogl_matrix_stack_rotate_euler: * @stack: A #CoglMatrixStack * @euler: A #graphene_euler_t * * Multiplies the current matrix by one that rotates according to the * rotation described by @euler. */ COGL_EXPORT void cogl_matrix_stack_rotate_euler (CoglMatrixStack *stack, const graphene_euler_t *euler); /** * cogl_matrix_stack_multiply: * @stack: A #CoglMatrixStack * @matrix: the matrix to multiply with the current model-view * * Multiplies the current matrix by the given matrix. */ COGL_EXPORT void cogl_matrix_stack_multiply (CoglMatrixStack *stack, const CoglMatrix *matrix); /** * cogl_matrix_stack_frustum: * @stack: A #CoglMatrixStack * @left: X position of the left clipping plane where it * intersects the near clipping plane * @right: X position of the right clipping plane where it * intersects the near clipping plane * @bottom: Y position of the bottom clipping plane where it * intersects the near clipping plane * @top: Y position of the top clipping plane where it intersects * the near clipping plane * @z_near: The distance to the near clipping plane (Must be positive) * @z_far: The distance to the far clipping plane (Must be positive) * * Replaces the current matrix with a perspective matrix for a given * viewing frustum defined by 4 side clip planes that all cross * through the origin and 2 near and far clip planes. */ COGL_EXPORT void cogl_matrix_stack_frustum (CoglMatrixStack *stack, float left, float right, float bottom, float top, float z_near, float z_far); /** * cogl_matrix_stack_perspective: * @stack: A #CoglMatrixStack * @fov_y: Vertical field of view angle in degrees. * @aspect: The (width over height) aspect ratio for display * @z_near: The distance to the near clipping plane (Must be positive, * and must not be 0) * @z_far: The distance to the far clipping plane (Must be positive) * * Replaces the current matrix with a perspective matrix based on the * provided values. * * You should be careful not to have too great a @z_far / @z_near * ratio since that will reduce the effectiveness of depth testing * since there wont be enough precision to identify the depth of * objects near to each other. */ COGL_EXPORT void cogl_matrix_stack_perspective (CoglMatrixStack *stack, float fov_y, float aspect, float z_near, float z_far); /** * cogl_matrix_stack_orthographic: * @stack: A #CoglMatrixStack * @x_1: The x coordinate for the first vertical clipping plane * @y_1: The y coordinate for the first horizontal clipping plane * @x_2: The x coordinate for the second vertical clipping plane * @y_2: The y coordinate for the second horizontal clipping plane * @near: The distance to the near clipping * plane (will be negative if the plane is * behind the viewer) * @far: The distance to the far clipping * plane (will be negative if the plane is * behind the viewer) * * Replaces the current matrix with an orthographic projection matrix. */ COGL_EXPORT void cogl_matrix_stack_orthographic (CoglMatrixStack *stack, float x_1, float y_1, float x_2, float y_2, float near, float far); /** * cogl_matrix_stack_get_inverse: * @stack: A #CoglMatrixStack * @inverse: (out): The destination for a 4x4 inverse transformation matrix * * Gets the inverse transform of the current matrix and uses it to * initialize a new #CoglMatrix. * * Return value: %TRUE if the inverse was successfully calculated or %FALSE * for degenerate transformations that can't be inverted (in this case the * @inverse matrix will simply be initialized with the identity matrix) */ COGL_EXPORT gboolean cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, CoglMatrix *inverse); /** * cogl_matrix_stack_get_entry: * @stack: A #CoglMatrixStack * * Gets a reference to the current transform represented by a * #CoglMatrixEntry pointer. * * The transform represented by a #CoglMatrixEntry is * immutable. * * #CoglMatrixEntrys are reference counted using * cogl_matrix_entry_ref() and cogl_matrix_entry_unref() and you * should call cogl_matrix_entry_unref() when you are finished with * and entry you get via cogl_matrix_stack_get_entry(). * * Return value: (transfer none): A pointer to the #CoglMatrixEntry * representing the current matrix stack transform. */ COGL_EXPORT CoglMatrixEntry * cogl_matrix_stack_get_entry (CoglMatrixStack *stack); /** * cogl_matrix_stack_get: * @stack: A #CoglMatrixStack * @matrix: (out): The potential destination for the current matrix * * Resolves the current @stack transform into a #CoglMatrix by * combining the operations that have been applied to build up the * current transform. * * There are two possible ways that this function may return its * result depending on whether the stack is able to directly point * to an internal #CoglMatrix or whether the result needs to be * composed of multiple operations. * * If an internal matrix contains the required result then this * function will directly return a pointer to that matrix, otherwise * if the function returns %NULL then @matrix will be initialized * to match the current transform of @stack. * * @matrix will be left untouched if a direct pointer is * returned. * * Return value: A direct pointer to the current transform or %NULL * and in that case @matrix will be initialized with * the value of the current transform. */ COGL_EXPORT CoglMatrix * cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix); /** * cogl_matrix_entry_get: * @entry: A #CoglMatrixEntry * @matrix: (out): The potential destination for the transform as * a matrix * * Resolves the current @entry transform into a #CoglMatrix by * combining the sequence of operations that have been applied to * build up the current transform. * * There are two possible ways that this function may return its * result depending on whether it's possible to directly point * to an internal #CoglMatrix or whether the result needs to be * composed of multiple operations. * * If an internal matrix contains the required result then this * function will directly return a pointer to that matrix, otherwise * if the function returns %NULL then @matrix will be initialized * to match the transform of @entry. * * @matrix will be left untouched if a direct pointer is * returned. * * Return value: A direct pointer to a #CoglMatrix transform or %NULL * and in that case @matrix will be initialized with * the effective transform represented by @entry. */ COGL_EXPORT CoglMatrix * cogl_matrix_entry_get (CoglMatrixEntry *entry, CoglMatrix *matrix); /** * cogl_matrix_stack_set: * @stack: A #CoglMatrixStack * @matrix: A #CoglMatrix replace the current matrix value with * * Replaces the current @stack matrix value with the value of @matrix. * This effectively discards any other operations that were applied * since the last time cogl_matrix_stack_push() was called or since * the stack was initialized. */ COGL_EXPORT void cogl_matrix_stack_set (CoglMatrixStack *stack, const CoglMatrix *matrix); /** * cogl_is_matrix_stack: * @object: a #CoglObject * * Determines if the given #CoglObject refers to a #CoglMatrixStack. * * Return value: %TRUE if @object is a #CoglMatrixStack, otherwise * %FALSE. */ COGL_EXPORT gboolean cogl_is_matrix_stack (void *object); /** * cogl_matrix_entry_calculate_translation: * @entry0: The first reference transform * @entry1: A second reference transform * @x: (out): The destination for the x-component of the translation * @y: (out): The destination for the y-component of the translation * @z: (out): The destination for the z-component of the translation * * Determines if the only difference between two transforms is a * translation and if so returns what the @x, @y, and @z components of * the translation are. * * If the difference between the two translations involves anything * other than a translation then the function returns %FALSE. * * Return value: %TRUE if the only difference between the transform of * @entry0 and the transform of @entry1 is a translation, * otherwise %FALSE. */ COGL_EXPORT gboolean cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0, CoglMatrixEntry *entry1, float *x, float *y, float *z); /** * cogl_matrix_entry_is_identity: * @entry: A #CoglMatrixEntry * * Determines whether @entry is known to represent an identity * transform. * * If this returns %TRUE then the entry is definitely the identity * matrix. If it returns %FALSE it may or may not be the identity * matrix but no expensive comparison is performed to verify it. * * Return value: %TRUE if @entry is definitely an identity transform, * otherwise %FALSE. */ COGL_EXPORT gboolean cogl_matrix_entry_is_identity (CoglMatrixEntry *entry); /** * cogl_matrix_entry_equal: * @entry0: The first #CoglMatrixEntry to compare * @entry1: A second #CoglMatrixEntry to compare * * Compares two arbitrary #CoglMatrixEntry transforms for equality * returning %TRUE if they are equal or %FALSE otherwise. * * In many cases it is unnecessary to use this api and instead * direct pointer comparisons of entries are good enough and much * cheaper too. * * Return value: %TRUE if @entry0 represents the same transform as * @entry1, otherwise %FALSE. */ COGL_EXPORT gboolean cogl_matrix_entry_equal (CoglMatrixEntry *entry0, CoglMatrixEntry *entry1); /** * cogl_debug_matrix_entry_print: * @entry: A #CoglMatrixEntry * * Allows visualizing the operations that build up the given @entry * for debugging purposes by printing to stdout. */ COGL_EXPORT void cogl_debug_matrix_entry_print (CoglMatrixEntry *entry); /** * cogl_matrix_entry_ref: * @entry: A #CoglMatrixEntry * * Takes a reference on the given @entry to ensure the @entry stays * alive and remains valid. When you are finished with the @entry then * you should call cogl_matrix_entry_unref(). * * It is an error to pass an @entry pointer to cogl_object_ref() and * cogl_object_unref() */ COGL_EXPORT CoglMatrixEntry * cogl_matrix_entry_ref (CoglMatrixEntry *entry); /** * cogl_matrix_entry_unref: * @entry: A #CoglMatrixEntry * * Releases a reference on @entry either taken by calling * cogl_matrix_entry_unref() or to release the reference given when * calling cogl_matrix_stack_get_entry(). */ COGL_EXPORT void cogl_matrix_entry_unref (CoglMatrixEntry *entry); #endif /* _COGL_MATRIX_STACK_H_ */ muffin-6.4.1/cogl/cogl/cogl-pixel-buffer.c0000664000175000017500000000725314723361714017277 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ /* For an overview of the functionality implemented here, please see * cogl-buffer-array.h, which contains the gtk-doc section overview for the * Pixel Buffers API. */ #include "cogl-config.h" #include #include #include #include "cogl-private.h" #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-object.h" #include "cogl-pixel-buffer-private.h" #include "cogl-pixel-buffer.h" #include "cogl-gtype-private.h" /* * GL/GLES compatibility defines for the buffer API: */ #if defined (HAVE_COGL_GL) #ifndef GL_PIXEL_UNPACK_BUFFER #define GL_PIXEL_UNPACK_BUFFER GL_PIXEL_UNPACK_BUFFER_ARB #endif #ifndef GL_PIXEL_PACK_BUFFER #define GL_PIXEL_PACK_BUFFER GL_PIXEL_PACK_BUFFER_ARB #endif #endif static void _cogl_pixel_buffer_free (CoglPixelBuffer *buffer); COGL_BUFFER_DEFINE (PixelBuffer, pixel_buffer) COGL_GTYPE_DEFINE_CLASS (PixelBuffer, pixel_buffer) static CoglPixelBuffer * _cogl_pixel_buffer_new (CoglContext *context, size_t size, const void *data, GError **error) { CoglPixelBuffer *pixel_buffer = g_slice_new0 (CoglPixelBuffer); CoglBuffer *buffer = COGL_BUFFER (pixel_buffer); /* parent's constructor */ _cogl_buffer_initialize (buffer, context, size, COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, COGL_BUFFER_USAGE_HINT_TEXTURE, COGL_BUFFER_UPDATE_HINT_STATIC); _cogl_pixel_buffer_object_new (pixel_buffer); if (data) { if (!_cogl_buffer_set_data (COGL_BUFFER (pixel_buffer), 0, data, size, error)) { cogl_object_unref (pixel_buffer); return NULL; } } return pixel_buffer; } CoglPixelBuffer * cogl_pixel_buffer_new (CoglContext *context, size_t size, const void *data) { GError *ignore_error = NULL; CoglPixelBuffer *buffer = _cogl_pixel_buffer_new (context, size, data, &ignore_error); g_clear_error (&ignore_error); return buffer; } static void _cogl_pixel_buffer_free (CoglPixelBuffer *buffer) { /* parent's destructor */ _cogl_buffer_fini (COGL_BUFFER (buffer)); g_slice_free (CoglPixelBuffer, buffer); } muffin-6.4.1/cogl/cogl/cogl-boxed-value.c0000664000175000017500000002355614723361714017126 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-boxed-value.h" #include "cogl-context-private.h" #include "driver/gl/cogl-util-gl-private.h" gboolean _cogl_boxed_value_equal (const CoglBoxedValue *bva, const CoglBoxedValue *bvb) { const void *pa, *pb; if (bva->type != bvb->type) return FALSE; switch (bva->type) { case COGL_BOXED_NONE: return TRUE; case COGL_BOXED_INT: if (bva->size != bvb->size || bva->count != bvb->count) return FALSE; if (bva->count == 1) { pa = bva->v.int_value; pb = bvb->v.int_value; } else { pa = bva->v.int_array; pb = bvb->v.int_array; } return !memcmp (pa, pb, sizeof (int) * bva->size * bva->count); case COGL_BOXED_FLOAT: if (bva->size != bvb->size || bva->count != bvb->count) return FALSE; if (bva->count == 1) { pa = bva->v.float_value; pb = bvb->v.float_value; } else { pa = bva->v.float_array; pb = bvb->v.float_array; } return !memcmp (pa, pb, sizeof (float) * bva->size * bva->count); case COGL_BOXED_MATRIX: if (bva->size != bvb->size || bva->count != bvb->count) return FALSE; if (bva->count == 1) { pa = bva->v.matrix; pb = bvb->v.matrix; } else { pa = bva->v.array; pb = bvb->v.array; } return !memcmp (pa, pb, sizeof (float) * bva->size * bva->size * bva->count); } g_warn_if_reached (); return FALSE; } static void _cogl_boxed_value_tranpose (float *dst, int size, const float *src) { int y, x; /* If the value is transposed we'll just transpose it now as it * is copied into the boxed value instead of passing TRUE to * glUniformMatrix because that is not supported on GLES and it * doesn't seem like the GL driver would be able to do anything * much smarter than this anyway */ for (y = 0; y < size; y++) for (x = 0; x < size; x++) *(dst++) = src[y + x * size]; } static void _cogl_boxed_value_set_x (CoglBoxedValue *bv, int size, int count, CoglBoxedType type, size_t value_size, const void *value, gboolean transpose) { if (count == 1) { if (bv->count > 1) g_free (bv->v.array); if (transpose) _cogl_boxed_value_tranpose (bv->v.float_value, size, value); else memcpy (bv->v.float_value, value, value_size); } else { if (bv->count > 1) { if (bv->count != count || bv->size != size || bv->type != type) { g_free (bv->v.array); bv->v.array = g_malloc (count * value_size); } } else bv->v.array = g_malloc (count * value_size); if (transpose) { int value_num; for (value_num = 0; value_num < count; value_num++) _cogl_boxed_value_tranpose (bv->v.float_array + value_num * size * size, size, (const float *) value + value_num * size * size); } else memcpy (bv->v.array, value, count * value_size); } bv->type = type; bv->size = size; bv->count = count; } void _cogl_boxed_value_set_1f (CoglBoxedValue *bv, float value) { _cogl_boxed_value_set_x (bv, 1, 1, COGL_BOXED_FLOAT, sizeof (float), &value, FALSE); } void _cogl_boxed_value_set_1i (CoglBoxedValue *bv, int value) { _cogl_boxed_value_set_x (bv, 1, 1, COGL_BOXED_INT, sizeof (int), &value, FALSE); } void _cogl_boxed_value_set_float (CoglBoxedValue *bv, int n_components, int count, const float *value) { _cogl_boxed_value_set_x (bv, n_components, count, COGL_BOXED_FLOAT, sizeof (float) * n_components, value, FALSE); } void _cogl_boxed_value_set_int (CoglBoxedValue *bv, int n_components, int count, const int *value) { _cogl_boxed_value_set_x (bv, n_components, count, COGL_BOXED_INT, sizeof (int) * n_components, value, FALSE); } void _cogl_boxed_value_set_matrix (CoglBoxedValue *bv, int dimensions, int count, gboolean transpose, const float *value) { _cogl_boxed_value_set_x (bv, dimensions, count, COGL_BOXED_MATRIX, sizeof (float) * dimensions * dimensions, value, transpose); } void _cogl_boxed_value_copy (CoglBoxedValue *dst, const CoglBoxedValue *src) { *dst = *src; if (src->count > 1) { switch (src->type) { case COGL_BOXED_NONE: break; case COGL_BOXED_INT: dst->v.int_array = g_memdup2 (src->v.int_array, src->size * src->count * sizeof (int)); break; case COGL_BOXED_FLOAT: dst->v.float_array = g_memdup2 (src->v.float_array, src->size * src->count * sizeof (float)); break; case COGL_BOXED_MATRIX: dst->v.float_array = g_memdup2 (src->v.float_array, src->size * src->size * src->count * sizeof (float)); break; } } } void _cogl_boxed_value_destroy (CoglBoxedValue *bv) { if (bv->count > 1) g_free (bv->v.array); } void _cogl_boxed_value_set_uniform (CoglContext *ctx, GLint location, const CoglBoxedValue *value) { switch (value->type) { case COGL_BOXED_NONE: break; case COGL_BOXED_INT: { const int *ptr; if (value->count == 1) ptr = value->v.int_value; else ptr = value->v.int_array; switch (value->size) { case 1: GE( ctx, glUniform1iv (location, value->count, ptr) ); break; case 2: GE( ctx, glUniform2iv (location, value->count, ptr) ); break; case 3: GE( ctx, glUniform3iv (location, value->count, ptr) ); break; case 4: GE( ctx, glUniform4iv (location, value->count, ptr) ); break; } } break; case COGL_BOXED_FLOAT: { const float *ptr; if (value->count == 1) ptr = value->v.float_value; else ptr = value->v.float_array; switch (value->size) { case 1: GE( ctx, glUniform1fv (location, value->count, ptr) ); break; case 2: GE( ctx, glUniform2fv (location, value->count, ptr) ); break; case 3: GE( ctx, glUniform3fv (location, value->count, ptr) ); break; case 4: GE( ctx, glUniform4fv (location, value->count, ptr) ); break; } } break; case COGL_BOXED_MATRIX: { const float *ptr; if (value->count == 1) ptr = value->v.matrix; else ptr = value->v.float_array; switch (value->size) { case 2: GE( ctx, glUniformMatrix2fv (location, value->count, FALSE, ptr) ); break; case 3: GE( ctx, glUniformMatrix3fv (location, value->count, FALSE, ptr) ); break; case 4: GE( ctx, glUniformMatrix4fv (location, value->count, FALSE, ptr) ); break; } } break; } } muffin-6.4.1/cogl/cogl/cogl-feature-private.c0000664000175000017500000001710714723361714020011 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-context-private.h" #include "cogl-feature-private.h" #include "cogl-renderer-private.h" #include "cogl-private.h" gboolean _cogl_feature_check (CoglRenderer *renderer, const char *driver_prefix, const CoglFeatureData *data, int gl_major, int gl_minor, CoglDriver driver, char * const *extensions, void *function_table) { const char *suffix = NULL; int func_num; CoglExtGlesAvailability gles_availability = 0; gboolean in_core; switch (driver) { case COGL_DRIVER_GLES2: gles_availability = COGL_EXT_IN_GLES2; if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0)) gles_availability |= COGL_EXT_IN_GLES3; break; case COGL_DRIVER_ANY: g_assert_not_reached (); case COGL_DRIVER_NOP: case COGL_DRIVER_GL: case COGL_DRIVER_GL3: break; } /* First check whether the functions should be directly provided by GL */ if (((driver == COGL_DRIVER_GL || driver == COGL_DRIVER_GL3) && COGL_CHECK_GL_VERSION (gl_major, gl_minor, data->min_gl_major, data->min_gl_minor)) || (data->gles_availability & gles_availability)) { suffix = ""; in_core = TRUE; } else { /* Otherwise try all of the extensions */ const char *namespace, *namespace_suffix; unsigned int namespace_len; for (namespace = data->namespaces; *namespace; namespace += strlen (namespace) + 1) { const char *extension; GString *full_extension_name = g_string_new (""); /* If the namespace part contains a ':' then the suffix for the function names is different from the name space */ if ((namespace_suffix = strchr (namespace, ':'))) { namespace_len = namespace_suffix - namespace; namespace_suffix++; } else { namespace_len = strlen (namespace); namespace_suffix = namespace; } for (extension = data->extension_names; *extension; extension += strlen (extension) + 1) { g_string_assign (full_extension_name, driver_prefix); g_string_append_c (full_extension_name, '_'); g_string_append_len (full_extension_name, namespace, namespace_len); g_string_append_c (full_extension_name, '_'); g_string_append (full_extension_name, extension); if (_cogl_check_extension (full_extension_name->str, extensions)) break; } g_string_free (full_extension_name, TRUE); /* If we found an extension with this namespace then use it as the suffix */ if (*extension) { suffix = namespace_suffix; break; } } in_core = FALSE; } /* If we couldn't find anything that provides the functions then give up */ if (suffix == NULL) goto error; /* Try to get all of the entry points */ for (func_num = 0; data->functions[func_num].name; func_num++) { void *func; char *full_function_name; full_function_name = g_strconcat (data->functions[func_num].name, suffix, NULL); func = _cogl_renderer_get_proc_address (renderer, full_function_name, in_core); g_free (full_function_name); if (func == NULL) goto error; /* Set the function pointer in the context */ *(void **) ((uint8_t *) function_table + data->functions[func_num].pointer_offset) = func; } return TRUE; /* If the extension isn't found or one of the functions wasn't found * then set all of the functions pointers to NULL so Cogl can safely * do feature testing by just looking at the function pointers */ error: for (func_num = 0; data->functions[func_num].name; func_num++) *(void **) ((uint8_t *) function_table + data->functions[func_num].pointer_offset) = NULL; return FALSE; } /* Define a set of arrays containing the functions required from GL for each feature */ #define COGL_EXT_BEGIN(name, \ min_gl_major, min_gl_minor, \ gles_availability, \ namespaces, extension_names) \ static const CoglFeatureFunction cogl_ext_ ## name ## _funcs[] = { #define COGL_EXT_FUNCTION(ret, name, args) \ { G_STRINGIFY (name), G_STRUCT_OFFSET (CoglContext, name) }, #define COGL_EXT_END() \ { NULL, 0 }, \ }; #include "gl-prototypes/cogl-all-functions.h" /* Define an array of features */ #undef COGL_EXT_BEGIN #define COGL_EXT_BEGIN(name, \ min_gl_major, min_gl_minor, \ gles_availability, \ namespaces, extension_names) \ { min_gl_major, min_gl_minor, gles_availability, namespaces, \ extension_names, 0, 0, \ cogl_ext_ ## name ## _funcs }, #undef COGL_EXT_FUNCTION #define COGL_EXT_FUNCTION(ret, name, args) #undef COGL_EXT_END #define COGL_EXT_END() static const CoglFeatureData cogl_feature_ext_functions_data[] = { #include "gl-prototypes/cogl-all-functions.h" }; void _cogl_feature_check_ext_functions (CoglContext *context, int gl_major, int gl_minor, char * const *gl_extensions) { int i; for (i = 0; i < G_N_ELEMENTS (cogl_feature_ext_functions_data); i++) _cogl_feature_check (context->display->renderer, "GL", cogl_feature_ext_functions_data + i, gl_major, gl_minor, context->driver, gl_extensions, context); } muffin-6.4.1/cogl/cogl/cogl-dma-buf-handle.c0000664000175000017500000000525614723361714017454 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2020 Endless, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Georges Basile Stavracas Neto */ #include "cogl-config.h" #include "cogl-dma-buf-handle.h" #include "cogl-object.h" #include struct _CoglDmaBufHandle { CoglFramebuffer *framebuffer; int dmabuf_fd; gpointer user_data; GDestroyNotify destroy_func; }; CoglDmaBufHandle * cogl_dma_buf_handle_new (CoglFramebuffer *framebuffer, int dmabuf_fd, gpointer user_data, GDestroyNotify destroy_func) { CoglDmaBufHandle *dmabuf_handle; g_assert (framebuffer); g_assert (dmabuf_fd != -1); dmabuf_handle = g_new0 (CoglDmaBufHandle, 1); dmabuf_handle->framebuffer = cogl_object_ref (framebuffer); dmabuf_handle->dmabuf_fd = dmabuf_fd; dmabuf_handle->user_data = user_data; dmabuf_handle->destroy_func = destroy_func; return dmabuf_handle; } void cogl_dma_buf_handle_free (CoglDmaBufHandle *dmabuf_handle) { g_return_if_fail (dmabuf_handle != NULL); g_clear_pointer (&dmabuf_handle->framebuffer, cogl_object_unref); if (dmabuf_handle->destroy_func) g_clear_pointer (&dmabuf_handle->user_data, dmabuf_handle->destroy_func); if (dmabuf_handle->dmabuf_fd != -1) close (dmabuf_handle->dmabuf_fd); g_free (dmabuf_handle); } CoglFramebuffer * cogl_dma_buf_handle_get_framebuffer (CoglDmaBufHandle *dmabuf_handle) { return dmabuf_handle->framebuffer; } int cogl_dma_buf_handle_get_fd (CoglDmaBufHandle *dmabuf_handle) { return dmabuf_handle->dmabuf_fd; } muffin-6.4.1/cogl/cogl/cogl-gpu-info.c0000664000175000017500000003543314723361714016434 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include #include #include "cogl-gpu-info-private.h" #include "cogl-context-private.h" #include "cogl-version.h" typedef struct { const char *renderer_string; const char *version_string; const char *vendor_string; } CoglGpuInfoStrings; typedef struct CoglGpuInfoArchitectureDescription { CoglGpuInfoArchitecture architecture; const char *name; CoglGpuInfoArchitectureFlag flags; gboolean (* check_function) (const CoglGpuInfoStrings *strings); } CoglGpuInfoArchitectureDescription; typedef struct { CoglGpuInfoVendor vendor; const char *name; gboolean (* check_function) (const CoglGpuInfoStrings *strings); const CoglGpuInfoArchitectureDescription *architectures; } CoglGpuInfoVendorDescription; typedef struct { CoglGpuInfoDriverPackage driver_package; const char *name; gboolean (* check_function) (const CoglGpuInfoStrings *strings, int *version_out); } CoglGpuInfoDriverPackageDescription; static gboolean _cogl_gpu_info_parse_version_string (const char *version_string, int n_components, const char **tail, int *version_ret) { int version = 0; uint64_t part; int i; for (i = 0; ; i++) { errno = 0; part = g_ascii_strtoull (version_string, (char **) &version_string, 10); if (errno || part > COGL_VERSION_MAX_COMPONENT_VALUE) return FALSE; version |= part << ((2 - i) * COGL_VERSION_COMPONENT_BITS); if (i + 1 >= n_components) break; if (*version_string != '.') return FALSE; version_string++; } if (version_ret) *version_ret = version; if (tail) *tail = version_string; return TRUE; } static gboolean match_phrase (const char *string, const char *phrase) { const char *part = strstr (string, phrase); int len; if (part == NULL) return FALSE; /* The match must either be at the beginning of the string or preceded by a space. */ if (part > string && part[-1] != ' ') return FALSE; /* Also match must either be at end of string or followed by a * space. */ len = strlen (phrase); if (part[len] != '\0' && part[len] != ' ') return FALSE; return TRUE; } static gboolean check_intel_vendor (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "Intel(R)"); } static gboolean check_imagination_technologies_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Imagination Technologies") != 0) return FALSE; return TRUE; } static gboolean check_arm_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "ARM") != 0) return FALSE; return TRUE; } static gboolean check_qualcomm_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Qualcomm") != 0) return FALSE; return TRUE; } static gboolean check_nvidia_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "NVIDIA") != 0 && strcmp (strings->vendor_string, "NVIDIA Corporation") != 0) return FALSE; return TRUE; } static gboolean check_ati_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "ATI") != 0) return FALSE; return TRUE; } static gboolean check_mesa_vendor (const CoglGpuInfoStrings *strings) { if (strcmp (strings->vendor_string, "Tungsten Graphics, Inc") == 0) return TRUE; else if (strcmp (strings->vendor_string, "VMware, Inc.") == 0) return TRUE; else if (strcmp (strings->vendor_string, "Mesa Project") == 0) return TRUE; return FALSE; } static gboolean check_true (const CoglGpuInfoStrings *strings) { /* This is a last resort so it always matches */ return TRUE; } static gboolean check_sandybridge_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "Sandybridge"); } static gboolean check_llvmpipe_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "llvmpipe"); } static gboolean check_softpipe_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "softpipe"); } static gboolean check_swrast_architecture (const CoglGpuInfoStrings *strings) { return match_phrase (strings->renderer_string, "software rasterizer") || match_phrase (strings->renderer_string, "Software Rasterizer"); } static gboolean check_sgx_architecture (const CoglGpuInfoStrings *strings) { if (strncmp (strings->renderer_string, "PowerVR SGX", 12) != 0) return FALSE; return TRUE; } static gboolean check_mali_architecture (const CoglGpuInfoStrings *strings) { if (strncmp (strings->renderer_string, "Mali-", 5) != 0) return FALSE; return TRUE; } static const CoglGpuInfoArchitectureDescription intel_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE, "Sandybridge", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_sandybridge_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription powervr_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_SGX, "SGX", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_DEFERRED, check_sgx_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED, check_true } }; static const CoglGpuInfoArchitectureDescription arm_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_MALI, "Mali", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_mali_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription mesa_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE, "LLVM Pipe", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_llvmpipe_architecture }, { COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE, "Softpipe", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_softpipe_architecture }, { COGL_GPU_INFO_ARCHITECTURE_SWRAST, "SWRast", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE, check_swrast_architecture }, { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoArchitectureDescription unknown_architectures[] = { { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, "Unknown", COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE | COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE, check_true } }; static const CoglGpuInfoVendorDescription _cogl_gpu_info_vendors[] = { { COGL_GPU_INFO_VENDOR_INTEL, "Intel", check_intel_vendor, intel_architectures }, { COGL_GPU_INFO_VENDOR_IMAGINATION_TECHNOLOGIES, "Imagination Technologies", check_imagination_technologies_vendor, powervr_architectures }, { COGL_GPU_INFO_VENDOR_ARM, "ARM", check_arm_vendor, arm_architectures }, { COGL_GPU_INFO_VENDOR_QUALCOMM, "Qualcomm", check_qualcomm_vendor, unknown_architectures }, { COGL_GPU_INFO_VENDOR_NVIDIA, "Nvidia", check_nvidia_vendor, unknown_architectures }, { COGL_GPU_INFO_VENDOR_ATI, "ATI", check_ati_vendor, unknown_architectures }, /* Must be last */ { COGL_GPU_INFO_VENDOR_MESA, "Mesa", check_mesa_vendor, mesa_architectures }, { COGL_GPU_INFO_VENDOR_UNKNOWN, "Unknown", check_true, unknown_architectures } }; static gboolean check_mesa_driver_package (const CoglGpuInfoStrings *strings, int *version_ret) { uint64_t micro_part; const char *v; /* The version string should always begin a two-part GL version number */ if (!_cogl_gpu_info_parse_version_string (strings->version_string, 2, /* n_components */ &v, /* tail */ NULL /* version_ret */)) return FALSE; /* In mesa this will be followed optionally by "(Core Profile)" and * then "Mesa" */ v = strstr (v, " Mesa "); if (!v) return FALSE; v += 6; /* Next there will be a version string that is at least two components. On a git devel build the version will be something like "-devel" instead */ if (!_cogl_gpu_info_parse_version_string (v, 2, /* n_components */ &v, /* tail */ version_ret)) return FALSE; /* If it is a development build then we'll just leave the micro number as 0 */ if (g_str_has_prefix (v, "-devel")) return TRUE; /* Otherwise there should be a micro version number */ if (*v != '.') return FALSE; errno = 0; micro_part = g_ascii_strtoull (v + 1, NULL /* endptr */, 10 /* base */); if (errno || micro_part > COGL_VERSION_MAX_COMPONENT_VALUE) return FALSE; *version_ret = COGL_VERSION_ENCODE (COGL_VERSION_GET_MAJOR (*version_ret), COGL_VERSION_GET_MINOR (*version_ret), micro_part); return TRUE; } UNIT_TEST (check_mesa_driver_package_parser, 0, /* no requirements */ 0 /* no failure cases */) { /* renderer_string, version_string, vendor_string;*/ const CoglGpuInfoStrings test_strings[2] = { { NULL, "3.1 Mesa 9.2-devel15436ad", NULL }, { NULL, "3.1 (Core Profile) Mesa 9.2.0-devel (git-15436ad)", NULL } }; int i; int version; for (i = 0; i < G_N_ELEMENTS (test_strings); i++) { g_assert (check_mesa_driver_package (&test_strings[i], &version)); g_assert_cmpint (version, ==, COGL_VERSION_ENCODE (9, 2, 0)); } } static gboolean check_unknown_driver_package (const CoglGpuInfoStrings *strings, int *version_out) { *version_out = 0; /* This is a last resort so it always matches */ return TRUE; } static const CoglGpuInfoDriverPackageDescription _cogl_gpu_info_driver_packages[] = { { COGL_GPU_INFO_DRIVER_PACKAGE_MESA, "Mesa", check_mesa_driver_package }, /* Must be last */ { COGL_GPU_INFO_DRIVER_PACKAGE_UNKNOWN, "Unknown", check_unknown_driver_package } }; void _cogl_gpu_info_init (CoglContext *ctx, CoglGpuInfo *gpu) { CoglGpuInfoStrings strings; int i; strings.renderer_string = (const char *) ctx->glGetString (GL_RENDERER); strings.version_string = _cogl_context_get_gl_version (ctx); strings.vendor_string = (const char *) ctx->glGetString (GL_VENDOR); /* Determine the driver package */ for (i = 0; ; i++) { const CoglGpuInfoDriverPackageDescription *description = _cogl_gpu_info_driver_packages + i; if (description->check_function (&strings, &gpu->driver_package_version)) { gpu->driver_package = description->driver_package; gpu->driver_package_name = description->name; break; } } /* Determine the GPU vendor */ for (i = 0; ; i++) { const CoglGpuInfoVendorDescription *description = _cogl_gpu_info_vendors + i; if (description->check_function (&strings)) { int j; gpu->vendor = description->vendor; gpu->vendor_name = description->name; for (j = 0; ; j++) { const CoglGpuInfoArchitectureDescription *architecture = description->architectures + j; if (architecture->check_function (&strings)) { gpu->architecture = architecture->architecture; gpu->architecture_name = architecture->name; gpu->architecture_flags = architecture->flags; goto probed; } } } } probed: COGL_NOTE (WINSYS, "Driver package = %s, vendor = %s, architecture = %s\n", gpu->driver_package_name, gpu->vendor_name, gpu->architecture_name); /* Determine the driver bugs */ gpu->driver_bugs = 0; } muffin-6.4.1/cogl/cogl/cogl-frame-info.c0000664000175000017500000000422714723361714016730 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-frame-info-private.h" #include "cogl-gtype-private.h" static void _cogl_frame_info_free (CoglFrameInfo *info); COGL_OBJECT_DEFINE (FrameInfo, frame_info); COGL_GTYPE_DEFINE_CLASS (FrameInfo, frame_info); CoglFrameInfo * _cogl_frame_info_new (void) { CoglFrameInfo *info; info = g_slice_new0 (CoglFrameInfo); return _cogl_frame_info_object_new (info); } static void _cogl_frame_info_free (CoglFrameInfo *info) { g_slice_free (CoglFrameInfo, info); } int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info) { return info->frame_counter; } int64_t cogl_frame_info_get_presentation_time (CoglFrameInfo *info) { return info->presentation_time; } float cogl_frame_info_get_refresh_rate (CoglFrameInfo *info) { return info->refresh_rate; } CoglOutput * cogl_frame_info_get_output (CoglFrameInfo *info) { return info->output; } int64_t cogl_frame_info_get_global_frame_counter (CoglFrameInfo *info) { return info->global_frame_counter; } muffin-6.4.1/cogl/cogl/cogl-journal-private.h0000664000175000017500000001031114723361714020023 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_JOURNAL_PRIVATE_H #define __COGL_JOURNAL_PRIVATE_H #include "cogl-texture.h" #include "cogl-object-private.h" #include "cogl-clip-stack.h" #include "cogl-fence-private.h" #define COGL_JOURNAL_VBO_POOL_SIZE 8 typedef struct _CoglJournal { CoglObject _parent; /* A pointer the framebuffer that is using this journal. This is only valid when the journal is not empty. It *does* take a reference on the framebuffer. Although this creates a circular reference, the framebuffer has special code to handle the case where the journal is the only thing holding a reference and it will cause the journal to flush */ CoglFramebuffer *framebuffer; GArray *entries; GArray *vertices; size_t needed_vbo_len; /* A pool of attribute buffers is used so that we can avoid repeatedly reallocating buffers. Only one of these buffers at a time will be used by Cogl but we keep more than one alive anyway in case the GL driver is internally using the buffer and it would have to allocate a new one when we start writing to it */ CoglAttributeBuffer *vbo_pool[COGL_JOURNAL_VBO_POOL_SIZE]; /* The next vbo to use from the pool. We just cycle through them in order */ unsigned int next_vbo_in_pool; int fast_read_pixel_count; CoglList pending_fences; } CoglJournal; /* To improve batching of geometry when submitting vertices to OpenGL we * log the texture rectangles we want to draw to a journal, so when we * later flush the journal we aim to batch data, and gl draw calls. */ typedef struct _CoglJournalEntry { CoglPipeline *pipeline; CoglMatrixEntry *modelview_entry; CoglClipStack *clip_stack; float viewport[4]; gboolean dither_enabled; /* Offset into ctx->logged_vertices */ size_t array_offset; int n_layers; } CoglJournalEntry; CoglJournal * _cogl_journal_new (CoglFramebuffer *framebuffer); void _cogl_journal_log_quad (CoglJournal *journal, const float *position, CoglPipeline *pipeline, int n_layers, CoglTexture *layer0_override_texture, const float *tex_coords, unsigned int tex_coords_len); void _cogl_journal_flush (CoglJournal *journal); void _cogl_journal_discard (CoglJournal *journal); gboolean _cogl_journal_all_entries_within_bounds (CoglJournal *journal, float clip_x0, float clip_y0, float clip_x1, float clip_y1); gboolean _cogl_journal_try_read_pixel (CoglJournal *journal, int x, int y, CoglBitmap *bitmap, gboolean *found_intersection); gboolean _cogl_is_journal (void *object); #endif /* __COGL_JOURNAL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-blit.c0000664000175000017500000003327314723361714015642 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-util.h" #include "cogl-blit.h" #include "cogl-context-private.h" #include "cogl-framebuffer-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-private.h" #include "cogl1-context.h" static const CoglBlitMode *_cogl_blit_default_mode = NULL; static gboolean _cogl_blit_texture_render_begin (CoglBlitData *data) { CoglContext *ctx = data->src_tex->context; CoglOffscreen *offscreen; CoglFramebuffer *fb; CoglPipeline *pipeline; unsigned int dst_width, dst_height; GError *ignore_error = NULL; offscreen = _cogl_offscreen_new_with_texture_full (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); fb = COGL_FRAMEBUFFER (offscreen); if (!cogl_framebuffer_allocate (fb, &ignore_error)) { g_error_free (ignore_error); cogl_object_unref (fb); return FALSE; } data->dest_fb = fb; dst_width = cogl_texture_get_width (data->dst_tex); dst_height = cogl_texture_get_height (data->dst_tex); /* Set up an orthographic projection so we can use pixel coordinates to render to the texture */ cogl_framebuffer_orthographic (fb, 0, 0, dst_width, dst_height, -1 /* near */, 1 /* far */); /* We cache a pipeline used for migrating on to the context so that it doesn't have to continuously regenerate a shader program */ if (ctx->blit_texture_pipeline == NULL) { ctx->blit_texture_pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_filters (ctx->blit_texture_pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); /* Disable blending by just directly taking the contents of the source texture */ cogl_pipeline_set_blend (ctx->blit_texture_pipeline, "RGBA = ADD(SRC_COLOR, 0)", NULL); } pipeline = ctx->blit_texture_pipeline; cogl_pipeline_set_layer_texture (pipeline, 0, data->src_tex); data->pipeline = pipeline; return TRUE; } static void _cogl_blit_texture_render_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height) { cogl_framebuffer_draw_textured_rectangle (data->dest_fb, data->pipeline, dst_x, dst_y, dst_x + width, dst_y + height, src_x / (float) data->src_width, src_y / (float) data->src_height, (src_x + width) / (float) data->src_width, (src_y + height) / (float) data->src_height); } static void _cogl_blit_texture_render_end (CoglBlitData *data) { CoglContext *ctx = data->src_tex->context; /* Attach the target texture to the texture render pipeline so that we don't keep a reference to the source texture forever. This is assuming that the destination texture will live for a long time which is currently the case when cogl_blit_* is used from the atlas code. It may be better in future to keep around a set of dummy 1x1 textures for each texture target that we could bind instead. This would also be useful when using a pipeline as a hash table key such as for the ARBfp program cache. */ cogl_pipeline_set_layer_texture (ctx->blit_texture_pipeline, 0, data->dst_tex); cogl_object_unref (data->dest_fb); } static gboolean _cogl_blit_framebuffer_begin (CoglBlitData *data) { CoglContext *ctx = data->src_tex->context; CoglOffscreen *dst_offscreen = NULL, *src_offscreen = NULL; CoglFramebuffer *dst_fb, *src_fb; GError *ignore_error = NULL; /* We can only blit between FBOs if both textures have the same premult convention and the blit framebuffer extension is supported. */ if ((_cogl_texture_get_format (data->src_tex) & COGL_PREMULT_BIT) != (_cogl_texture_get_format (data->dst_tex) & COGL_PREMULT_BIT) || !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) return FALSE; dst_offscreen = _cogl_offscreen_new_with_texture_full (data->dst_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); dst_fb = COGL_FRAMEBUFFER (dst_offscreen); if (!cogl_framebuffer_allocate (dst_fb, &ignore_error)) { g_error_free (ignore_error); goto error; } src_offscreen= _cogl_offscreen_new_with_texture_full (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); src_fb = COGL_FRAMEBUFFER (src_offscreen); if (!cogl_framebuffer_allocate (src_fb, &ignore_error)) { g_error_free (ignore_error); goto error; } data->src_fb = src_fb; data->dest_fb = dst_fb; return TRUE; error: if (dst_offscreen) cogl_object_unref (dst_offscreen); if (src_offscreen) cogl_object_unref (src_offscreen); return FALSE; } static void _cogl_blit_framebuffer_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height) { cogl_blit_framebuffer (data->src_fb, data->dest_fb, src_x, src_y, dst_x, dst_y, width, height, NULL); } static void _cogl_blit_framebuffer_end (CoglBlitData *data) { cogl_object_unref (data->src_fb); cogl_object_unref (data->dest_fb); } static gboolean _cogl_blit_copy_tex_sub_image_begin (CoglBlitData *data) { CoglOffscreen *offscreen; CoglFramebuffer *fb; GError *ignore_error = NULL; /* This will only work if the target texture is a CoglTexture2D */ if (!cogl_is_texture_2d (data->dst_tex)) return FALSE; offscreen = _cogl_offscreen_new_with_texture_full (data->src_tex, COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL, 0 /* level */); fb = COGL_FRAMEBUFFER (offscreen); if (!cogl_framebuffer_allocate (fb, &ignore_error)) { g_error_free (ignore_error); cogl_object_unref (fb); return FALSE; } data->src_fb = fb; return TRUE; } static void _cogl_blit_copy_tex_sub_image_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height) { _cogl_texture_2d_copy_from_framebuffer (COGL_TEXTURE_2D (data->dst_tex), src_x, src_y, width, height, data->src_fb, dst_x, dst_y, 0); /* level */ } static void _cogl_blit_copy_tex_sub_image_end (CoglBlitData *data) { cogl_object_unref (data->src_fb); } static gboolean _cogl_blit_get_tex_data_begin (CoglBlitData *data) { data->format = _cogl_texture_get_format (data->src_tex); g_return_val_if_fail (cogl_pixel_format_get_n_planes (data->format) == 1, FALSE); data->bpp = cogl_pixel_format_get_bytes_per_pixel (data->format, 0); data->image_data = g_malloc (data->bpp * data->src_width * data->src_height); cogl_texture_get_data (data->src_tex, data->format, data->src_width * data->bpp, data->image_data); return TRUE; } static void _cogl_blit_get_tex_data_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height) { GError *ignore = NULL; int rowstride = data->src_width * data->bpp; int offset = rowstride * src_y + src_x * data->bpp; _cogl_texture_set_region (data->dst_tex, width, height, data->format, rowstride, data->image_data + offset, dst_x, dst_y, 0, /* level */ &ignore); /* TODO: support chaining up errors during the blit */ } static void _cogl_blit_get_tex_data_end (CoglBlitData *data) { g_free (data->image_data); } /* These should be specified in order of preference */ static const CoglBlitMode _cogl_blit_modes[] = { { "texture-render", _cogl_blit_texture_render_begin, _cogl_blit_texture_render_blit, _cogl_blit_texture_render_end }, { "framebuffer", _cogl_blit_framebuffer_begin, _cogl_blit_framebuffer_blit, _cogl_blit_framebuffer_end }, { "copy-tex-sub-image", _cogl_blit_copy_tex_sub_image_begin, _cogl_blit_copy_tex_sub_image_blit, _cogl_blit_copy_tex_sub_image_end }, { "get-tex-data", _cogl_blit_get_tex_data_begin, _cogl_blit_get_tex_data_blit, _cogl_blit_get_tex_data_end } }; void _cogl_blit_begin (CoglBlitData *data, CoglTexture *dst_tex, CoglTexture *src_tex) { int i; if (_cogl_blit_default_mode == NULL) { const char *default_mode_string; /* Allow the default to be specified with an environment variable. For the time being these functions are only used when blitting between atlas textures so the environment variable is named to be specific to the atlas code. If we want to use the code in other places we should create another environment variable for each specific use case */ if ((default_mode_string = g_getenv ("COGL_ATLAS_DEFAULT_BLIT_MODE"))) { for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++) if (!strcmp (_cogl_blit_modes[i].name, default_mode_string)) { _cogl_blit_default_mode = _cogl_blit_modes + i; break; } if (i >= G_N_ELEMENTS (_cogl_blit_modes)) { g_warning ("Unknown blit mode %s", default_mode_string); _cogl_blit_default_mode = _cogl_blit_modes; } } else /* Default to the first blit mode */ _cogl_blit_default_mode = _cogl_blit_modes; } memset (data, 0, sizeof (CoglBlitData)); data->dst_tex = dst_tex; data->src_tex = src_tex; data->src_width = cogl_texture_get_width (src_tex); data->src_height = cogl_texture_get_height (src_tex); /* Try the default blit mode first */ if (!_cogl_blit_default_mode->begin_func (data)) { COGL_NOTE (ATLAS, "Failed to set up blit mode %s", _cogl_blit_default_mode->name); /* Try all of the other modes in order */ for (i = 0; i < G_N_ELEMENTS (_cogl_blit_modes); i++) if (_cogl_blit_modes + i != _cogl_blit_default_mode && _cogl_blit_modes[i].begin_func (data)) { /* Use this mode as the default from now on */ _cogl_blit_default_mode = _cogl_blit_modes + i; break; } else COGL_NOTE (ATLAS, "Failed to set up blit mode %s", _cogl_blit_modes[i].name); /* The last blit mode can't fail so this should never happen */ g_return_if_fail (i < G_N_ELEMENTS (_cogl_blit_modes)); } data->blit_mode = _cogl_blit_default_mode; COGL_NOTE (ATLAS, "Setup blit using %s", _cogl_blit_default_mode->name); } void _cogl_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height) { data->blit_mode->blit_func (data, src_x, src_y, dst_x, dst_y, width, height); } void _cogl_blit_end (CoglBlitData *data) { data->blit_mode->end_func (data); } muffin-6.4.1/cogl/cogl/cogl-snippet.c0000664000175000017500000001053714723361714016370 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-types.h" #include "cogl-snippet-private.h" #include "cogl-util.h" #include "cogl-gtype-private.h" static void _cogl_snippet_free (CoglSnippet *snippet); COGL_OBJECT_DEFINE (Snippet, snippet); COGL_GTYPE_DEFINE_CLASS (Snippet, snippet); CoglSnippet * cogl_snippet_new (CoglSnippetHook hook, const char *declarations, const char *post) { CoglSnippet *snippet = g_slice_new0 (CoglSnippet); _cogl_snippet_object_new (snippet); snippet->hook = hook; cogl_snippet_set_declarations (snippet, declarations); cogl_snippet_set_post (snippet, post); return snippet; } CoglSnippetHook cogl_snippet_get_hook (CoglSnippet *snippet) { g_return_val_if_fail (cogl_is_snippet (snippet), 0); return snippet->hook; } static gboolean _cogl_snippet_modify (CoglSnippet *snippet) { if (snippet->immutable) { g_warning ("A CoglSnippet should not be modified once it has been " "attached to a pipeline. Any modifications after that point " "will be ignored."); return FALSE; } return TRUE; } void cogl_snippet_set_declarations (CoglSnippet *snippet, const char *declarations) { g_return_if_fail (cogl_is_snippet (snippet)); if (!_cogl_snippet_modify (snippet)) return; g_free (snippet->declarations); snippet->declarations = declarations ? g_strdup (declarations) : NULL; } const char * cogl_snippet_get_declarations (CoglSnippet *snippet) { g_return_val_if_fail (cogl_is_snippet (snippet), NULL); return snippet->declarations; } void cogl_snippet_set_pre (CoglSnippet *snippet, const char *pre) { g_return_if_fail (cogl_is_snippet (snippet)); if (!_cogl_snippet_modify (snippet)) return; g_free (snippet->pre); snippet->pre = pre ? g_strdup (pre) : NULL; } const char * cogl_snippet_get_pre (CoglSnippet *snippet) { g_return_val_if_fail (cogl_is_snippet (snippet), NULL); return snippet->pre; } void cogl_snippet_set_replace (CoglSnippet *snippet, const char *replace) { g_return_if_fail (cogl_is_snippet (snippet)); if (!_cogl_snippet_modify (snippet)) return; g_free (snippet->replace); snippet->replace = replace ? g_strdup (replace) : NULL; } const char * cogl_snippet_get_replace (CoglSnippet *snippet) { g_return_val_if_fail (cogl_is_snippet (snippet), NULL); return snippet->replace; } void cogl_snippet_set_post (CoglSnippet *snippet, const char *post) { g_return_if_fail (cogl_is_snippet (snippet)); if (!_cogl_snippet_modify (snippet)) return; g_free (snippet->post); snippet->post = post ? g_strdup (post) : NULL; } const char * cogl_snippet_get_post (CoglSnippet *snippet) { g_return_val_if_fail (cogl_is_snippet (snippet), NULL); return snippet->post; } void _cogl_snippet_make_immutable (CoglSnippet *snippet) { snippet->immutable = TRUE; } static void _cogl_snippet_free (CoglSnippet *snippet) { g_free (snippet->declarations); g_free (snippet->pre); g_free (snippet->replace); g_free (snippet->post); g_slice_free (CoglSnippet, snippet); } muffin-6.4.1/cogl/cogl/cogl-gpu-info-private.h0000664000175000017500000000627714723361714020115 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_GPU_INFO_PRIVATE_H #define __COGL_GPU_INFO_PRIVATE_H #include "cogl-context.h" typedef enum _CoglGpuInfoArchitectureFlag { COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_IMMEDIATE_MODE, COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_TILED, COGL_GPU_INFO_ARCHITECTURE_FLAG_VERTEX_SOFTWARE, COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_IMMEDIATE_MODE, COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_DEFERRED, COGL_GPU_INFO_ARCHITECTURE_FLAG_FRAGMENT_SOFTWARE } CoglGpuInfoArchitectureFlag; typedef enum _CoglGpuInfoArchitecture { COGL_GPU_INFO_ARCHITECTURE_UNKNOWN, COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE, COGL_GPU_INFO_ARCHITECTURE_SGX, COGL_GPU_INFO_ARCHITECTURE_MALI, COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE, COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE, COGL_GPU_INFO_ARCHITECTURE_SWRAST } CoglGpuInfoArchitecture; typedef enum { COGL_GPU_INFO_VENDOR_UNKNOWN, COGL_GPU_INFO_VENDOR_INTEL, COGL_GPU_INFO_VENDOR_IMAGINATION_TECHNOLOGIES, COGL_GPU_INFO_VENDOR_ARM, COGL_GPU_INFO_VENDOR_QUALCOMM, COGL_GPU_INFO_VENDOR_NVIDIA, COGL_GPU_INFO_VENDOR_ATI, COGL_GPU_INFO_VENDOR_MESA } CoglGpuInfoVendor; typedef enum { COGL_GPU_INFO_DRIVER_PACKAGE_UNKNOWN, COGL_GPU_INFO_DRIVER_PACKAGE_MESA } CoglGpuInfoDriverPackage; typedef enum { COGL_GPU_INFO_DRIVER_STUB } CoglGpuInfoDriverBug; typedef struct _CoglGpuInfoVersion CoglGpuInfoVersion; typedef struct _CoglGpuInfo CoglGpuInfo; struct _CoglGpuInfo { CoglGpuInfoVendor vendor; const char *vendor_name; CoglGpuInfoDriverPackage driver_package; const char *driver_package_name; int driver_package_version; CoglGpuInfoArchitecture architecture; const char *architecture_name; CoglGpuInfoArchitectureFlag architecture_flags; CoglGpuInfoDriverBug driver_bugs; }; /* * _cogl_gpu_info_init: * @ctx: A #CoglContext * @gpu: A return location for the GPU information * * Determines information about the GPU and driver from the given * context. */ void _cogl_gpu_info_init (CoglContext *ctx, CoglGpuInfo *gpu); #endif /* __COGL_GPU_INFO_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-frame-info-private.h0000664000175000017500000000307414723361714020404 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_FRAME_INFO_PRIVATE_H #define __COGL_FRAME_INFO_PRIVATE_H #include "cogl-frame-info.h" #include "cogl-object-private.h" struct _CoglFrameInfo { CoglObject _parent; int64_t frame_counter; int64_t presentation_time; float refresh_rate; int64_t global_frame_counter; CoglOutput *output; }; CoglFrameInfo *_cogl_frame_info_new (void); #endif /* __COGL_FRAME_INFO_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-bitmap.c0000664000175000017500000003025114723361714016155 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-debug.h" #include "cogl-private.h" #include "cogl-bitmap-private.h" #include "cogl-buffer-private.h" #include "cogl-pixel-buffer.h" #include "cogl-context-private.h" #include "cogl-gtype-private.h" #include static void _cogl_bitmap_free (CoglBitmap *bmp); COGL_OBJECT_DEFINE (Bitmap, bitmap); COGL_GTYPE_DEFINE_CLASS (Bitmap, bitmap); static void _cogl_bitmap_free (CoglBitmap *bmp) { g_assert (!bmp->mapped); g_assert (!bmp->bound); if (bmp->shared_bmp) cogl_object_unref (bmp->shared_bmp); if (bmp->buffer) cogl_object_unref (bmp->buffer); g_slice_free (CoglBitmap, bmp); } gboolean _cogl_bitmap_convert_premult_status (CoglBitmap *bmp, CoglPixelFormat dst_format, GError **error) { /* Do we need to unpremultiply? */ if ((bmp->format & COGL_PREMULT_BIT) > 0 && (dst_format & COGL_PREMULT_BIT) == 0 && COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (dst_format)) return _cogl_bitmap_unpremult (bmp, error); /* Do we need to premultiply? */ if ((bmp->format & COGL_PREMULT_BIT) == 0 && COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (bmp->format) && (dst_format & COGL_PREMULT_BIT) > 0) /* Try premultiplying using imaging library */ return _cogl_bitmap_premult (bmp, error); return TRUE; } CoglBitmap * _cogl_bitmap_copy (CoglBitmap *src_bmp, GError **error) { CoglBitmap *dst_bmp; CoglPixelFormat src_format = cogl_bitmap_get_format (src_bmp); int width = cogl_bitmap_get_width (src_bmp); int height = cogl_bitmap_get_height (src_bmp); dst_bmp = _cogl_bitmap_new_with_malloc_buffer (src_bmp->context, width, height, src_format, error); if (!dst_bmp) return NULL; if (!_cogl_bitmap_copy_subregion (src_bmp, dst_bmp, 0, 0, /* src_x/y */ 0, 0, /* dst_x/y */ width, height, error)) { cogl_object_unref (dst_bmp); return NULL; } return dst_bmp; } gboolean _cogl_bitmap_copy_subregion (CoglBitmap *src, CoglBitmap *dst, int src_x, int src_y, int dst_x, int dst_y, int width, int height, GError **error) { uint8_t *srcdata; uint8_t *dstdata; int bpp; int line; gboolean succeeded = FALSE; /* Intended only for fast copies when format is equal! */ g_return_val_if_fail ((src->format & ~COGL_PREMULT_BIT) == (dst->format & ~COGL_PREMULT_BIT), FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (src->format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (src->format, 0); if ((srcdata = _cogl_bitmap_map (src, COGL_BUFFER_ACCESS_READ, 0, error))) { if ((dstdata = _cogl_bitmap_map (dst, COGL_BUFFER_ACCESS_WRITE, 0, error))) { srcdata += src_y * src->rowstride + src_x * bpp; dstdata += dst_y * dst->rowstride + dst_x * bpp; for (line = 0; line < height; ++line) { memcpy (dstdata, srcdata, width * bpp); srcdata += src->rowstride; dstdata += dst->rowstride; } succeeded = TRUE; _cogl_bitmap_unmap (dst); } _cogl_bitmap_unmap (src); } return succeeded; } gboolean cogl_bitmap_get_size_from_file (const char *filename, int *width, int *height) { return _cogl_bitmap_get_size_from_file (filename, width, height); } CoglBitmap * cogl_bitmap_new_for_data (CoglContext *context, int width, int height, CoglPixelFormat format, int rowstride, uint8_t *data) { CoglBitmap *bmp; g_return_val_if_fail (cogl_is_context (context), NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); bmp = g_slice_new (CoglBitmap); bmp->context = context; bmp->format = format; bmp->width = width; bmp->height = height; bmp->rowstride = rowstride; bmp->data = data; bmp->mapped = FALSE; bmp->bound = FALSE; bmp->shared_bmp = NULL; bmp->buffer = NULL; return _cogl_bitmap_object_new (bmp); } CoglBitmap * _cogl_bitmap_new_with_malloc_buffer (CoglContext *context, unsigned int width, unsigned int height, CoglPixelFormat format, GError **error) { static CoglUserDataKey bitmap_free_key; int bpp; int rowstride; uint8_t *data; CoglBitmap *bitmap; g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); /* Try to malloc the data */ bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); rowstride = ((width * bpp) + 3) & ~3; data = g_try_malloc (rowstride * height); if (!data) { g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_NO_MEMORY, "Failed to allocate memory for bitmap"); return NULL; } /* Now create the bitmap */ bitmap = cogl_bitmap_new_for_data (context, width, height, format, rowstride, data); cogl_object_set_user_data (COGL_OBJECT (bitmap), &bitmap_free_key, data, g_free); return bitmap; } CoglBitmap * _cogl_bitmap_new_shared (CoglBitmap *shared_bmp, CoglPixelFormat format, int width, int height, int rowstride) { CoglBitmap *bmp; bmp = cogl_bitmap_new_for_data (shared_bmp->context, width, height, format, rowstride, NULL /* data */); bmp->shared_bmp = cogl_object_ref (shared_bmp); return bmp; } CoglBitmap * cogl_bitmap_new_from_file (const char *filename, GError **error) { _COGL_GET_CONTEXT (ctx, NULL); g_return_val_if_fail (filename != NULL, NULL); g_return_val_if_fail (error == NULL || *error == NULL, NULL); return _cogl_bitmap_from_file (ctx, filename, error); } CoglBitmap * cogl_bitmap_new_from_buffer (CoglBuffer *buffer, CoglPixelFormat format, int width, int height, int rowstride, int offset) { CoglBitmap *bmp; g_return_val_if_fail (cogl_is_buffer (buffer), NULL); bmp = cogl_bitmap_new_for_data (buffer->context, width, height, format, rowstride, NULL /* data */); bmp->buffer = cogl_object_ref (buffer); bmp->data = GINT_TO_POINTER (offset); return bmp; } CoglBitmap * cogl_bitmap_new_with_size (CoglContext *context, unsigned int width, unsigned int height, CoglPixelFormat format) { CoglPixelBuffer *pixel_buffer; CoglBitmap *bitmap; unsigned int rowstride; /* creating a buffer to store "any" format does not make sense */ g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); /* for now we fallback to cogl_pixel_buffer_new, later, we could ask * libdrm a tiled buffer for instance */ rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); pixel_buffer = cogl_pixel_buffer_new (context, height * rowstride, NULL); /* data */ g_return_val_if_fail (pixel_buffer != NULL, NULL); bitmap = cogl_bitmap_new_from_buffer (COGL_BUFFER (pixel_buffer), format, width, height, rowstride, 0 /* offset */); cogl_object_unref (pixel_buffer); return bitmap; } CoglPixelFormat cogl_bitmap_get_format (CoglBitmap *bitmap) { return bitmap->format; } void _cogl_bitmap_set_format (CoglBitmap *bitmap, CoglPixelFormat format) { bitmap->format = format; } int cogl_bitmap_get_width (CoglBitmap *bitmap) { return bitmap->width; } int cogl_bitmap_get_height (CoglBitmap *bitmap) { return bitmap->height; } int cogl_bitmap_get_rowstride (CoglBitmap *bitmap) { return bitmap->rowstride; } CoglPixelBuffer * cogl_bitmap_get_buffer (CoglBitmap *bitmap) { while (bitmap->shared_bmp) bitmap = bitmap->shared_bmp; return COGL_PIXEL_BUFFER (bitmap->buffer); } uint32_t cogl_bitmap_error_quark (void) { return g_quark_from_static_string ("cogl-bitmap-error-quark"); } uint8_t * _cogl_bitmap_map (CoglBitmap *bitmap, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) return _cogl_bitmap_map (bitmap->shared_bmp, access, hints, error); g_assert (!bitmap->mapped); if (bitmap->buffer) { uint8_t *data = _cogl_buffer_map (bitmap->buffer, access, hints, error); COGL_NOTE (BITMAP, "A pixel array is being mapped from a bitmap. This " "usually means that some conversion on the pixel array is " "needed so a sub-optimal format is being used."); if (data) { bitmap->mapped = TRUE; return data + GPOINTER_TO_INT (bitmap->data); } else return NULL; } else { bitmap->mapped = TRUE; return bitmap->data; } } void _cogl_bitmap_unmap (CoglBitmap *bitmap) { /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) { _cogl_bitmap_unmap (bitmap->shared_bmp); return; } g_assert (bitmap->mapped); bitmap->mapped = FALSE; if (bitmap->buffer) cogl_buffer_unmap (bitmap->buffer); } CoglContext * _cogl_bitmap_get_context (CoglBitmap *bitmap) { return bitmap->context; } muffin-6.4.1/cogl/cogl/cogl-meta-texture.h0000664000175000017500000002070214723361714017332 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_META_TEXTURE_H__ #define __COGL_META_TEXTURE_H__ #include G_BEGIN_DECLS /** * SECTION:cogl-meta-texture * @short_description: Interface for high-level textures built from * low-level textures like #CoglTexture2D. * * Cogl helps to make it easy to deal with high level textures such * as #CoglAtlasTextures, #CoglSubTextures, * #CoglTexturePixmapX11 textures and #CoglTexture2DSliced textures * consistently. * * A #CoglMetaTexture is a texture that might internally be * represented by one or more low-level #CoglTextures * such as #CoglTexture2D. These low-level textures are the only ones * that a GPU really understands but because applications often want * more high-level texture abstractions (such as storing multiple * textures inside one larger "atlas" texture) it's desirable to be * able to deal with these using a common interface. * * For example the GPU is not able to automatically handle repeating a * texture that is part of a larger atlas texture but if you use * %COGL_PIPELINE_WRAP_MODE_REPEAT with an atlas texture when drawing * with cogl_rectangle() you should see that it "Just Works™" - at * least if you don't use multi-texturing. The reason this works is * because cogl_rectangle() internally understands the #CoglMetaTexture * interface and is able to manually resolve the low-level textures * using this interface and by making multiple draw calls it can * emulate the texture repeat modes. * * Cogl doesn't aim to pretend that meta-textures are just like real * textures because it would get extremely complex to try and emulate * low-level GPU semantics transparently for these textures. The low * level drawing APIs of Cogl, such as cogl_primitive_draw() don't * actually know anything about the #CoglMetaTexture interface and its * the developer's responsibility to resolve all textures referenced * by a #CoglPipeline to low-level textures before drawing. * * If you want to develop custom primitive APIs like * cogl_framebuffer_draw_rectangle() and you want to support drawing * with #CoglAtlasTextures or #CoglSubTextures for * example, then you will need to use this #CoglMetaTexture interface * to be able to resolve high-level textures into low-level textures * before drawing with Cogl's low-level drawing APIs such as * cogl_primitive_draw(). * * Most developers won't need to use this interface directly * but still it is worth understanding the distinction between * low-level and meta textures because you may find other references * in the documentation that detail limitations of using * meta-textures. */ #if defined(__COGL_H_INSIDE__) && !defined(COGL_ENABLE_MUTTER_API) && \ !defined(COGL_GIR_SCANNING) /* For the public C api we typedef interface types as void to avoid needing * lots of casting in code and instead we will rely on runtime type checking * for these objects. */ typedef void CoglMetaTexture; #else typedef struct _CoglMetaTexture CoglMetaTexture; #define COGL_META_TEXTURE(X) ((CoglMetaTexture *)X) #endif /** * CoglMetaTextureCallback: * @sub_texture: A low-level #CoglTexture making up part of a * #CoglMetaTexture. * @sub_texture_coords: A float 4-tuple ordered like * (tx1,ty1,tx2,ty2) defining what region of the * current @sub_texture maps to a sub-region of a * #CoglMetaTexture. (tx1,ty1) is the top-left * sub-region coordinate and (tx2,ty2) is the * bottom-right. These are low-level texture * coordinates. * @meta_coords: A float 4-tuple ordered like (tx1,ty1,tx2,ty2) * defining what sub-region of a #CoglMetaTexture this * low-level @sub_texture maps too. (tx1,ty1) is * the top-left sub-region coordinate and (tx2,ty2) is * the bottom-right. These are high-level meta-texture * coordinates. * @user_data: A private pointer passed to * cogl_meta_texture_foreach_in_region(). * * A callback used with cogl_meta_texture_foreach_in_region() to * retrieve details of all the low-level #CoglTextures that * make up a given #CoglMetaTexture. * * Since: 1.10 * Stability: unstable */ typedef void (*CoglMetaTextureCallback) (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data); /** * cogl_meta_texture_foreach_in_region: * @meta_texture: An object implementing the #CoglMetaTexture * interface. * @tx_1: The top-left x coordinate of the region to iterate * @ty_1: The top-left y coordinate of the region to iterate * @tx_2: The bottom-right x coordinate of the region to iterate * @ty_2: The bottom-right y coordinate of the region to iterate * @wrap_s: The wrap mode for the x-axis * @wrap_t: The wrap mode for the y-axis * @callback: A #CoglMetaTextureCallback pointer to be called * for each low-level texture within the specified region. * @user_data: A private pointer that is passed to @callback. * * Allows you to manually iterate the low-level textures that define a * given region of a high-level #CoglMetaTexture. * * For example cogl_texture_2d_sliced_new_with_size() can be used to * create a meta texture that may slice a large image into multiple, * smaller power-of-two sized textures. These high level textures are * not directly understood by a GPU and so this API must be used to * manually resolve the underlying textures for drawing. * * All high level textures (#CoglAtlasTexture, #CoglSubTexture, * #CoglTexturePixmapX11, and #CoglTexture2DSliced) can be handled * consistently using this interface which greately simplifies * implementing primitives that support all texture types. * * For example if you use the cogl_rectangle() API then Cogl will * internally use this API to resolve the low level textures of any * meta textures you have associated with CoglPipeline layers. * * The low level drawing APIs such as cogl_primitive_draw() * don't understand the #CoglMetaTexture interface and so it is your * responsibility to use this API to resolve all CoglPipeline textures * into low-level textures before drawing. * * For each low-level texture that makes up part of the given region * of the @meta_texture, @callback is called specifying how the * low-level texture maps to the original region. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_meta_texture_foreach_in_region (CoglMetaTexture *meta_texture, float tx_1, float ty_1, float tx_2, float ty_2, CoglPipelineWrapMode wrap_s, CoglPipelineWrapMode wrap_t, CoglMetaTextureCallback callback, void *user_data); G_END_DECLS #endif /* __COGL_META_TEXTURE_H__ */ muffin-6.4.1/cogl/cogl/cogl-indices.h0000664000175000017500000001264514723361714016333 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_INDICES_H__ #define __COGL_INDICES_H__ /* We forward declare the CoglIndices type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglIndices CoglIndices; #include #include G_BEGIN_DECLS /** * SECTION:cogl-indices * @short_description: Describe vertex indices stored in a #CoglIndexBuffer. * * Indices allow you to avoid duplicating vertices in your vertex data * by virtualizing your data and instead providing a sequence of index * values that tell the GPU which data should be used for each vertex. * * If the GPU is given a sequence of indices it doesn't simply walk * through each vertex of your data in order it will instead walk * through the indices which can provide random access to the * underlying data. * * Since it's very common to have duplicate vertices when describing a * shape as a list of triangles it can often be a significant space * saving to describe geometry using indices. Reducing the size of * your models can make it cheaper to map them into the GPU by * reducing the demand on memory bandwidth and may help to make better * use of your GPUs internal vertex caching. * * For example, to describe a quadrilateral as 2 triangles for the GPU * you could either provide data with 6 vertices or instead with * indices you can provide vertex data for just 4 vertices and an * index buffer that specfies the 6 vertices by indexing the shared * vertices multiple times. * * |[ * CoglVertexP2 quad_vertices[] = { * {x0, y0}, //0 = top left * {x1, y1}, //1 = bottom left * {x2, y2}, //2 = bottom right * {x3, y3}, //3 = top right * }; * //tell the gpu how to interpret the quad as 2 triangles... * unsigned char indices[] = {0, 1, 2, 0, 2, 3}; * ]| * * Even in the above illustration we see a saving of 10bytes for one * quad compared to having data for 6 vertices and no indices but if * you need to draw 100s or 1000s of quads then its really quite * significant. * * Something else to consider is that often indices can be defined * once and remain static while the vertex data may change for * animations perhaps. That means you may be able to ignore the * negligable cost of mapping your indices into the GPU if they don't * ever change. * * The above illustration is actually a good example of static indices * because it's really common that developers have quad mesh data that * they need to display and we know exactly what that indices array * needs to look like depending on the number of quads that need to be * drawn. It doesn't matter how the quads might be animated and * changed the indices will remain the same. Cogl even has a utility * (cogl_get_rectangle_indices()) to get access to re-useable indices * for drawing quads as above. */ /** * cogl_indices_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_indices_get_gtype (void); COGL_EXPORT CoglIndices * cogl_indices_new (CoglContext *context, CoglIndicesType type, const void *indices_data, int n_indices); COGL_EXPORT CoglIndices * cogl_indices_new_for_buffer (CoglIndicesType type, CoglIndexBuffer *buffer, size_t offset); COGL_EXPORT CoglIndexBuffer * cogl_indices_get_buffer (CoglIndices *indices); COGL_EXPORT CoglIndicesType cogl_indices_get_type (CoglIndices *indices); COGL_EXPORT size_t cogl_indices_get_offset (CoglIndices *indices); COGL_EXPORT void cogl_indices_set_offset (CoglIndices *indices, size_t offset); COGL_EXPORT CoglIndices * cogl_get_rectangle_indices (CoglContext *context, int n_rectangles); /** * cogl_is_indices: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglIndices. * * Return value: %TRUE if the object references a #CoglIndices * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_indices (void *object); G_END_DECLS #endif /* __COGL_INDICES_H__ */ muffin-6.4.1/cogl/cogl/cogl-texture-2d-sliced-private.h0000664000175000017500000000354714723361714021632 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_2D_SLICED_PRIVATE_H #define __COGL_TEXTURE_2D_SLICED_PRIVATE_H #include "cogl-bitmap-private.h" #include "cogl-pipeline-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-sliced.h" #include struct _CoglTexture2DSliced { CoglTexture _parent; GArray *slice_x_spans; GArray *slice_y_spans; GArray *slice_textures; int max_waste; CoglPixelFormat internal_format; }; CoglTexture2DSliced * _cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, int max_waste, gboolean can_convert_in_place); #endif /* __COGL_TEXTURE_2D_SLICED_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-indices-private.h0000664000175000017500000000327414723361714020001 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_INDICES_PRIVATE_H #define __COGL_INDICES_PRIVATE_H #include "cogl-object-private.h" #include "cogl-index-buffer-private.h" #include "cogl-types.h" struct _CoglIndices { CoglObject _parent; CoglIndexBuffer *buffer; size_t offset; CoglIndicesType type; int immutable_ref; }; CoglIndices * _cogl_indices_immutable_ref (CoglIndices *indices); void _cogl_indices_immutable_unref (CoglIndices *indices); #endif /* __COGL_INDICES_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-onscreen.c0000664000175000017500000004372014723361714016522 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-onscreen-private.h" #include "cogl-frame-info-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl1-context.h" #include "cogl-closure-list-private.h" #include "cogl-poll-private.h" #include "cogl-gtype-private.h" static void _cogl_onscreen_free (CoglOnscreen *onscreen); COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (Onscreen, onscreen, _cogl_onscreen_class.virt_unref = _cogl_framebuffer_unref); COGL_GTYPE_DEFINE_CLASS (Onscreen, onscreen, COGL_GTYPE_IMPLEMENT_INTERFACE (framebuffer)); static gpointer cogl_dummy_copy (gpointer data) { return data; } static void cogl_dummy_free (gpointer data) { } COGL_GTYPE_DEFINE_BOXED (FrameClosure, frame_closure, cogl_dummy_copy, cogl_dummy_free); COGL_GTYPE_DEFINE_BOXED (OnscreenResizeClosure, onscreen_resize_closure, cogl_dummy_copy, cogl_dummy_free); COGL_GTYPE_DEFINE_BOXED (OnscreenDirtyClosure, onscreen_dirty_closure, cogl_dummy_copy, cogl_dummy_free); static void _cogl_onscreen_init_from_template (CoglOnscreen *onscreen, CoglOnscreenTemplate *onscreen_template) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); _cogl_list_init (&onscreen->frame_closures); _cogl_list_init (&onscreen->resize_closures); _cogl_list_init (&onscreen->dirty_closures); framebuffer->config = onscreen_template->config; cogl_object_ref (framebuffer->config.swap_chain); } /* XXX: While we still have backend in Clutter we need a dummy object * to represent the CoglOnscreen framebuffer that the backend * creates... */ CoglOnscreen * _cogl_onscreen_new (void) { g_autofree CoglOnscreen *onscreen_ptr = g_new0 (CoglOnscreen, 1); CoglOnscreen *onscreen; _COGL_GET_CONTEXT (ctx, NULL); onscreen = g_steal_pointer (&onscreen_ptr); _cogl_framebuffer_init (COGL_FRAMEBUFFER (onscreen), ctx, COGL_FRAMEBUFFER_TYPE_ONSCREEN, 0x1eadbeef, /* width */ 0x1eadbeef); /* height */ /* NB: make sure to pass positive width/height numbers here * because otherwise we'll hit input validation assertions!*/ _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); COGL_FRAMEBUFFER (onscreen)->allocated = TRUE; /* XXX: Note we don't initialize onscreen->winsys in this case. */ return _cogl_onscreen_object_new (onscreen); } CoglOnscreen * cogl_onscreen_new (CoglContext *ctx, int width, int height) { CoglOnscreen *onscreen; /* FIXME: We are assuming onscreen buffers will always be premultiplied so we'll set the premult flag on the bitmap format. This will usually be correct because the result of the default blending operations for Cogl ends up with premultiplied data in the framebuffer. However it is possible for the framebuffer to be in whatever format depending on what CoglPipeline is used to render to it. Eventually we may want to add a way for an application to inform Cogl that the framebuffer is not premultiplied in case it is being used for some special purpose. */ onscreen = g_new0 (CoglOnscreen, 1); _cogl_framebuffer_init (COGL_FRAMEBUFFER (onscreen), ctx, COGL_FRAMEBUFFER_TYPE_ONSCREEN, width, /* width */ height); /* height */ _cogl_onscreen_init_from_template (onscreen, ctx->display->onscreen_template); return _cogl_onscreen_object_new (onscreen); } static void _cogl_onscreen_free (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); CoglFrameInfo *frame_info; _cogl_closure_list_disconnect_all (&onscreen->resize_closures); _cogl_closure_list_disconnect_all (&onscreen->frame_closures); _cogl_closure_list_disconnect_all (&onscreen->dirty_closures); while ((frame_info = g_queue_pop_tail (&onscreen->pending_frame_infos))) cogl_object_unref (frame_info); g_queue_clear (&onscreen->pending_frame_infos); winsys->onscreen_deinit (onscreen); g_return_if_fail (onscreen->winsys == NULL); /* Chain up to parent */ _cogl_framebuffer_free (framebuffer); g_free (onscreen); } static void notify_event (CoglOnscreen *onscreen, CoglFrameEvent event, CoglFrameInfo *info) { _cogl_closure_list_invoke (&onscreen->frame_closures, CoglFrameCallback, onscreen, event, info); } static void _cogl_dispatch_onscreen_cb (CoglContext *context) { CoglOnscreenEvent *event, *tmp; CoglList queue; /* Dispatching the event callback may cause another frame to be * drawn which in may cause another event to be queued immediately. * To make sure this loop will only dispatch one set of events we'll * steal the queue and iterate that separately */ _cogl_list_init (&queue); _cogl_list_insert_list (&queue, &context->onscreen_events_queue); _cogl_list_init (&context->onscreen_events_queue); _cogl_closure_disconnect (context->onscreen_dispatch_idle); context->onscreen_dispatch_idle = NULL; _cogl_list_for_each_safe (event, tmp, &queue, link) { CoglOnscreen *onscreen = event->onscreen; CoglFrameInfo *info = event->info; notify_event (onscreen, event->type, info); cogl_object_unref (onscreen); cogl_object_unref (info); g_slice_free (CoglOnscreenEvent, event); } while (!_cogl_list_empty (&context->onscreen_dirty_queue)) { CoglOnscreenQueuedDirty *qe = _cogl_container_of (context->onscreen_dirty_queue.next, CoglOnscreenQueuedDirty, link); _cogl_list_remove (&qe->link); _cogl_closure_list_invoke (&qe->onscreen->dirty_closures, CoglOnscreenDirtyCallback, qe->onscreen, &qe->info); cogl_object_unref (qe->onscreen); g_slice_free (CoglOnscreenQueuedDirty, qe); } } static void _cogl_onscreen_queue_dispatch_idle (CoglOnscreen *onscreen) { CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; if (!ctx->onscreen_dispatch_idle) { ctx->onscreen_dispatch_idle = _cogl_poll_renderer_add_idle (ctx->display->renderer, (CoglIdleCallback) _cogl_dispatch_onscreen_cb, ctx, NULL); } } void _cogl_onscreen_queue_dirty (CoglOnscreen *onscreen, const CoglOnscreenDirtyInfo *info) { CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; CoglOnscreenQueuedDirty *qe = g_slice_new (CoglOnscreenQueuedDirty); qe->onscreen = cogl_object_ref (onscreen); qe->info = *info; _cogl_list_insert (ctx->onscreen_dirty_queue.prev, &qe->link); _cogl_onscreen_queue_dispatch_idle (onscreen); } void _cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenDirtyInfo info; info.x = 0; info.y = 0; info.width = framebuffer->width; info.height = framebuffer->height; _cogl_onscreen_queue_dirty (onscreen, &info); } void _cogl_onscreen_queue_event (CoglOnscreen *onscreen, CoglFrameEvent type, CoglFrameInfo *info) { CoglContext *ctx = COGL_FRAMEBUFFER (onscreen)->context; CoglOnscreenEvent *event = g_slice_new (CoglOnscreenEvent); event->onscreen = cogl_object_ref (onscreen); event->info = cogl_object_ref (info); event->type = type; _cogl_list_insert (ctx->onscreen_events_queue.prev, &event->link); _cogl_onscreen_queue_dispatch_idle (onscreen); } void cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys; CoglFrameInfo *info; g_return_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); info = _cogl_frame_info_new (); info->frame_counter = onscreen->frame_counter; g_queue_push_tail (&onscreen->pending_frame_infos, info); /* FIXME: we shouldn't need to flush *all* journals here! */ cogl_flush (); winsys = _cogl_framebuffer_get_winsys (framebuffer); winsys->onscreen_swap_buffers_with_damage (onscreen, rectangles, n_rectangles); cogl_framebuffer_discard_buffers (framebuffer, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_STENCIL); if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) { CoglFrameInfo *info; g_warn_if_fail (onscreen->pending_frame_infos.length == 1); info = g_queue_pop_tail (&onscreen->pending_frame_infos); _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info); _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); cogl_object_unref (info); } onscreen->frame_counter++; } void cogl_onscreen_swap_buffers (CoglOnscreen *onscreen) { cogl_onscreen_swap_buffers_with_damage (onscreen, NULL, 0); } void cogl_onscreen_swap_region (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys; CoglFrameInfo *info; g_return_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN); info = _cogl_frame_info_new (); info->frame_counter = onscreen->frame_counter; g_queue_push_tail (&onscreen->pending_frame_infos, info); /* FIXME: we shouldn't need to flush *all* journals here! */ cogl_flush (); winsys = _cogl_framebuffer_get_winsys (framebuffer); /* This should only be called if the winsys advertises COGL_WINSYS_FEATURE_SWAP_REGION */ g_return_if_fail (winsys->onscreen_swap_region != NULL); winsys->onscreen_swap_region (COGL_ONSCREEN (framebuffer), rectangles, n_rectangles); cogl_framebuffer_discard_buffers (framebuffer, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_STENCIL); if (!_cogl_winsys_has_feature (COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT)) { CoglFrameInfo *info; g_warn_if_fail (onscreen->pending_frame_infos.length == 1); info = g_queue_pop_tail (&onscreen->pending_frame_infos); _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_SYNC, info); _cogl_onscreen_queue_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); cogl_object_unref (info); } onscreen->frame_counter++; } int cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys; g_return_val_if_fail (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN, 0); winsys = _cogl_framebuffer_get_winsys (framebuffer); if (!winsys->onscreen_get_buffer_age) return 0; return winsys->onscreen_get_buffer_age (onscreen); } #ifdef COGL_HAS_X11_SUPPORT uint32_t cogl_x11_onscreen_get_window_xid (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); /* This should only be called for x11 onscreens */ g_return_val_if_fail (winsys->onscreen_x11_get_window_xid != NULL, 0); return winsys->onscreen_x11_get_window_xid (onscreen); } #endif /* COGL_HAS_X11_SUPPORT */ CoglFrameClosure * cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, CoglFrameCallback callback, void *user_data, CoglUserDataDestroyCallback destroy) { return _cogl_closure_list_add (&onscreen->frame_closures, callback, user_data, destroy); } void cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen, CoglFrameClosure *closure) { g_return_if_fail (closure); _cogl_closure_disconnect (closure); } void cogl_onscreen_show (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); const CoglWinsysVtable *winsys; if (!framebuffer->allocated) { if (!cogl_framebuffer_allocate (framebuffer, NULL)) return; } winsys = _cogl_framebuffer_get_winsys (framebuffer); if (winsys->onscreen_set_visibility) winsys->onscreen_set_visibility (onscreen, TRUE); } void cogl_onscreen_hide (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); if (framebuffer->allocated) { const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); if (winsys->onscreen_set_visibility) winsys->onscreen_set_visibility (onscreen, FALSE); } } void _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info) { notify_event (onscreen, COGL_FRAME_EVENT_SYNC, info); } void _cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info) { notify_event (onscreen, COGL_FRAME_EVENT_COMPLETE, info); } void _cogl_onscreen_notify_resize (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); _cogl_closure_list_invoke (&onscreen->resize_closures, CoglOnscreenResizeCallback, onscreen, framebuffer->width, framebuffer->height); } void _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer, int width, int height) { if (framebuffer->width == width && framebuffer->height == height) return; framebuffer->width = width; framebuffer->height = height; cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); if (!_cogl_has_private_feature (framebuffer->context, COGL_PRIVATE_FEATURE_DIRTY_EVENTS)) _cogl_onscreen_queue_full_dirty (COGL_ONSCREEN (framebuffer)); } void cogl_onscreen_set_resizable (CoglOnscreen *onscreen, gboolean resizable) { CoglFramebuffer *framebuffer; const CoglWinsysVtable *winsys; if (onscreen->resizable == resizable) return; onscreen->resizable = resizable; framebuffer = COGL_FRAMEBUFFER (onscreen); if (framebuffer->allocated) { winsys = _cogl_framebuffer_get_winsys (COGL_FRAMEBUFFER (onscreen)); if (winsys->onscreen_set_resizable) winsys->onscreen_set_resizable (onscreen, resizable); } } gboolean cogl_onscreen_get_resizable (CoglOnscreen *onscreen) { return onscreen->resizable; } CoglOnscreenResizeClosure * cogl_onscreen_add_resize_callback (CoglOnscreen *onscreen, CoglOnscreenResizeCallback callback, void *user_data, CoglUserDataDestroyCallback destroy) { return _cogl_closure_list_add (&onscreen->resize_closures, callback, user_data, destroy); } void cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen, CoglOnscreenResizeClosure *closure) { _cogl_closure_disconnect (closure); } CoglOnscreenDirtyClosure * cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen, CoglOnscreenDirtyCallback callback, void *user_data, CoglUserDataDestroyCallback destroy) { return _cogl_closure_list_add (&onscreen->dirty_closures, callback, user_data, destroy); } void cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen, CoglOnscreenDirtyClosure *closure) { g_return_if_fail (closure); _cogl_closure_disconnect (closure); } int64_t cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen) { return onscreen->frame_counter; } muffin-6.4.1/cogl/cogl/cogl-texture-2d.h0000664000175000017500000002225614723361714016717 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_TEXTURE_2D_H #define __COGL_TEXTURE_2D_H #include "cogl-context.h" #include "cogl-bitmap.h" #ifdef COGL_HAS_EGL_SUPPORT #include "cogl-egl-defines.h" #endif G_BEGIN_DECLS /** * SECTION:cogl-texture-2d * @short_description: Functions for creating and manipulating 2D textures * * These functions allow low-level 2D textures to be allocated. These * differ from sliced textures for example which may internally be * made up of multiple 2D textures, or atlas textures where Cogl must * internally modify user texture coordinates before they can be used * by the GPU. */ typedef struct _CoglTexture2D CoglTexture2D; #define COGL_TEXTURE_2D(X) ((CoglTexture2D *)X) typedef enum _CoglEglImageFlags { COGL_EGL_IMAGE_FLAG_NONE = 0, COGL_EGL_IMAGE_FLAG_NO_GET_DATA = 1 << 0, } CoglEglImageFlags; /** * cogl_texture_2d_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_texture_2d_get_gtype (void); /** * cogl_is_texture_2d: * @object: A #CoglObject * * Gets whether the given object references an existing #CoglTexture2D * object. * * Return value: %TRUE if the object references a #CoglTexture2D, * %FALSE otherwise */ COGL_EXPORT gboolean cogl_is_texture_2d (void *object); /** * cogl_texture_2d_new_with_size: (skip) * @ctx: A #CoglContext * @width: Width of the texture to allocate * @height: Height of the texture to allocate * * Creates a low-level #CoglTexture2D texture with a given @width and * @height that your GPU can texture from directly. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or preferably let Cogl * automatically allocate storage lazily when it may know more about * how the texture is being used and can optimize how it is allocated. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Returns: (transfer full): A new #CoglTexture2D object with no storage yet allocated. * * Since: 2.0 */ COGL_EXPORT CoglTexture2D * cogl_texture_2d_new_with_size (CoglContext *ctx, int width, int height); /** * cogl_texture_2d_new_from_file: (skip) * @ctx: A #CoglContext * @filename: the file to load * @error: A #GError to catch exceptional errors or %NULL * * Creates a low-level #CoglTexture2D texture from an image file. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or preferably let Cogl * automatically allocate storage lazily when it may know more about * how the texture is being used and can optimize how it is allocated. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Return value: (transfer full): A newly created #CoglTexture2D or %NULL on failure * and @error will be updated. * * Since: 1.16 */ COGL_EXPORT CoglTexture2D * cogl_texture_2d_new_from_file (CoglContext *ctx, const char *filename, GError **error); /** * cogl_texture_2d_new_from_data: (skip) * @ctx: A #CoglContext * @width: width of texture in pixels * @height: height of texture in pixels * @format: the #CoglPixelFormat the buffer is stored in in RAM * @rowstride: the memory offset in bytes between the starts of * scanlines in @data. A value of 0 will make Cogl automatically * calculate @rowstride from @width and @format. * @data: pointer the memory region where the source buffer resides * @error: A #GError for exceptions * * Creates a low-level #CoglTexture2D texture based on data residing * in memory. * * This api will always immediately allocate GPU memory for the * texture and upload the given data so that the @data pointer does * not need to remain valid once this function returns. This means it * is not possible to configure the texture before it is allocated. If * you do need to configure the texture before allocation (to specify * constraints on the internal format for example) then you can * instead create a #CoglBitmap for your data and use * cogl_texture_2d_new_from_bitmap() or use * cogl_texture_2d_new_with_size() and then upload data using * cogl_texture_set_data() * * Returns: (transfer full): A newly allocated #CoglTexture2D, or if * the size is not supported (because it is too large or a * non-power-of-two size that the hardware doesn't support) * it will return %NULL and set @error. * * Since: 2.0 */ COGL_EXPORT CoglTexture2D * cogl_texture_2d_new_from_data (CoglContext *ctx, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error); /** * cogl_texture_2d_new_from_bitmap: * @bitmap: A #CoglBitmap * * Creates a low-level #CoglTexture2D texture based on data residing * in a #CoglBitmap. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or preferably let Cogl * automatically allocate storage lazily when it may know more about * how the texture is being used and can optimize how it is allocated. * * The texture is still configurable until it has been allocated so * for example you can influence the internal format of the texture * using cogl_texture_set_components() and * cogl_texture_set_premultiplied(). * * Returns: (transfer full): A newly allocated #CoglTexture2D * * Since: 2.0 * Stability: unstable */ COGL_EXPORT CoglTexture2D * cogl_texture_2d_new_from_bitmap (CoglBitmap *bitmap); /** * cogl_egl_texture_2d_new_from_image: (skip) */ #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) /* NB: The reason we require the width, height and format to be passed * even though they may seem redundant is because GLES 1/2 don't * provide a way to query these properties. */ COGL_EXPORT CoglTexture2D * cogl_egl_texture_2d_new_from_image (CoglContext *ctx, int width, int height, CoglPixelFormat format, EGLImageKHR image, CoglEglImageFlags flags, GError **error); typedef gboolean (*CoglTexture2DEGLImageExternalAlloc) (CoglTexture2D *tex_2d, gpointer user_data, GError **error); /** * cogl_texture_2d_new_from_egl_image_external: (skip) */ COGL_EXPORT CoglTexture2D * cogl_texture_2d_new_from_egl_image_external (CoglContext *ctx, int width, int height, CoglTexture2DEGLImageExternalAlloc alloc, gpointer user_data, GDestroyNotify destroy, GError **error); COGL_EXPORT void cogl_texture_2d_egl_image_external_bind (CoglTexture2D *tex_2d); COGL_EXPORT void cogl_texture_2d_egl_image_external_alloc_finish (CoglTexture2D *tex_2d, void *user_data, GDestroyNotify destroy); #endif G_END_DECLS #endif /* __COGL_TEXTURE_2D_H */ muffin-6.4.1/cogl/cogl/cogl-debug.h0000664000175000017500000000720614723361714016000 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_DEBUG_H__ #define __COGL_DEBUG_H__ #include "cogl-profile.h" #include "cogl-flags.h" #include "cogl-util.h" #include G_BEGIN_DECLS typedef enum { COGL_DEBUG_SLICING, COGL_DEBUG_OFFSCREEN, COGL_DEBUG_DRAW, COGL_DEBUG_PANGO, COGL_DEBUG_RECTANGLES, COGL_DEBUG_OBJECT, COGL_DEBUG_BLEND_STRINGS, COGL_DEBUG_DISABLE_BATCHING, COGL_DEBUG_DISABLE_PBOS, COGL_DEBUG_JOURNAL, COGL_DEBUG_BATCHING, COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM, COGL_DEBUG_MATRICES, COGL_DEBUG_ATLAS, COGL_DEBUG_DUMP_ATLAS_IMAGE, COGL_DEBUG_DISABLE_ATLAS, COGL_DEBUG_DISABLE_SHARED_ATLAS, COGL_DEBUG_OPENGL, COGL_DEBUG_DISABLE_TEXTURING, COGL_DEBUG_SHOW_SOURCE, COGL_DEBUG_DISABLE_BLENDING, COGL_DEBUG_TEXTURE_PIXMAP, COGL_DEBUG_BITMAP, COGL_DEBUG_WIREFRAME, COGL_DEBUG_DISABLE_SOFTWARE_CLIP, COGL_DEBUG_DISABLE_PROGRAM_CACHES, COGL_DEBUG_DISABLE_FAST_READ_PIXEL, COGL_DEBUG_CLIPPING, COGL_DEBUG_WINSYS, COGL_DEBUG_PERFORMANCE, COGL_DEBUG_N_FLAGS } CoglDebugFlags; COGL_EXPORT GHashTable *_cogl_debug_instances; #define COGL_DEBUG_N_LONGS COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_DEBUG_N_FLAGS) COGL_EXPORT unsigned long _cogl_debug_flags[COGL_DEBUG_N_LONGS]; #define COGL_DEBUG_ENABLED(flag) \ COGL_FLAGS_GET (_cogl_debug_flags, flag) #define COGL_DEBUG_SET_FLAG(flag) \ COGL_FLAGS_SET (_cogl_debug_flags, flag, TRUE) #define COGL_DEBUG_CLEAR_FLAG(flag) \ COGL_FLAGS_SET (_cogl_debug_flags, flag, FALSE) #ifdef __GNUC__ #define COGL_NOTE(type,x,a...) G_STMT_START { \ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_##type))) { \ _cogl_profile_trace_message ("[" #type "] " G_STRLOC " & " x, ##a); \ } } G_STMT_END #else #define COGL_NOTE(type,...) G_STMT_START { \ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_##type))) { \ char *_fmt = g_strdup_printf (__VA_ARGS__); \ _cogl_profile_trace_message ("[" #type "] " G_STRLOC " & %s", _fmt);\ g_free (_fmt); \ } } G_STMT_END #endif /* __GNUC__ */ void _cogl_debug_check_environment (void); void _cogl_parse_debug_string (const char *value, gboolean enable, gboolean ignore_help); G_END_DECLS #endif /* __COGL_DEBUG_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-hash-table.h0000664000175000017500000000570714723361714020531 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PIPELINE_HASH_H__ #define __COGL_PIPELINE_HASH_H__ #include "cogl-pipeline-cache.h" typedef struct { /* Total number of pipelines that were ever added to the hash. This * is not decremented when a pipeline is removed. It is only used to * generate a warning if an unusually high number of pipelines are * generated */ int n_unique_pipelines; /* This is the expected minimum size we could prune the hash table * to if we were to remove all pipelines that are not in use. This * is only updated after we prune the table */ int expected_min_size; /* String that will be used to describe the usage of this hash table * in the debug warning when too many pipelines are generated. This * must be a static string because it won't be copied or freed */ const char *debug_string; unsigned int main_state; unsigned int layer_state; GHashTable *table; } CoglPipelineHashTable; void _cogl_pipeline_hash_table_init (CoglPipelineHashTable *hash, unsigned int main_state, unsigned int layer_state, const char *debug_string); void _cogl_pipeline_hash_table_destroy (CoglPipelineHashTable *hash); /* * Gets a pipeline from the hash that has the same state as * @key_pipeline according to the limited state bits passed to * _cogl_pipeline_hash_table_init(). If there is no matching pipelines * already then a copy of key_pipeline is stored in the hash so that * it will be used next time the function is called with a similar * pipeline. In that case the copy itself will be returned */ CoglPipelineCacheEntry * _cogl_pipeline_hash_table_get (CoglPipelineHashTable *hash, CoglPipeline *key_pipeline); #endif /* __COGL_PIPELINE_HASH_H__ */ muffin-6.4.1/cogl/cogl/cogl-display.h0000664000175000017500000001657614723361714016371 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_DISPLAY_H__ #define __COGL_DISPLAY_H__ #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-display * @short_description: Common aspects of a display pipeline * * The basic intention for this object is to let the application * configure common display preferences before creating a context, and * there are a few different aspects to this... * * Firstly there are options directly relating to the physical display * pipeline that is currently being used including the digital to * analogue conversion hardware and the screens the user sees. * * Another aspect is that display options may constrain or affect how * onscreen framebuffers should later be configured. The original * rationale for the display object in fact was to let us handle GLX * and EGLs requirements that framebuffers must be "compatible" with * the config associated with the current context meaning we have to * force the user to describe how they would like to create their * onscreen windows before we can choose a suitable fbconfig and * create a GLContext. */ typedef struct _CoglDisplay CoglDisplay; #define COGL_DISPLAY(OBJECT) ((CoglDisplay *)OBJECT) /** * cogl_display_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_display_get_gtype (void); /** * cogl_display_new: * @renderer: A #CoglRenderer * @onscreen_template: A #CoglOnscreenTemplate * * Explicitly allocates a new #CoglDisplay object to encapsulate the * common state of the display pipeline that applies to the whole * application. * * Many applications don't need to explicitly use * cogl_display_new() and can just jump straight to cogl_context_new() * and pass a %NULL display argument so Cogl will automatically * connect and setup a renderer and display. * * A @display can only be made for a specific choice of renderer which * is why this takes the @renderer argument. * * A common use for explicitly allocating a display object is to * define a template for allocating onscreen framebuffers which is * what the @onscreen_template argument is for, or alternatively * you can use cogl_display_set_onscreen_template(). * * When a display is first allocated via cogl_display_new() it is in a * mutable configuration mode. It's designed this way so we can * extend the apis available for configuring a display without * requiring huge numbers of constructor arguments. * * When you have finished configuring a display object you can * optionally call cogl_display_setup() to explicitly apply the * configuration and check for errors. Alternaitvely you can pass the * display to cogl_context_new() and Cogl will implicitly apply your * configuration but if there are errors then the application will * abort with a message. For simple applications with no fallback * options then relying on the implicit setup can be fine. * * Return value: (transfer full): A newly allocated #CoglDisplay * object in a mutable configuration mode. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglDisplay * cogl_display_new (CoglRenderer *renderer, CoglOnscreenTemplate *onscreen_template); /** * cogl_display_get_renderer: * @display: a #CoglDisplay * * Queries the #CoglRenderer associated with the given @display. * * Return value: (transfer none): The associated #CoglRenderer * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglRenderer * cogl_display_get_renderer (CoglDisplay *display); /** * cogl_display_set_onscreen_template: * @display: a #CoglDisplay * @onscreen_template: A template for creating #CoglOnscreen framebuffers * * Specifies a template for creating #CoglOnscreen framebuffers. * * Depending on the system, the constraints for creating #CoglOnscreen * framebuffers need to be known before setting up a #CoglDisplay because the * final setup of the display may constrain how onscreen framebuffers may be * allocated. If Cogl knows how an application wants to allocate onscreen * framebuffers then it can try to make sure to setup the display accordingly. * * Since: 1.16 * Stability: unstable */ COGL_EXPORT void cogl_display_set_onscreen_template (CoglDisplay *display, CoglOnscreenTemplate *onscreen_template); /** * cogl_display_setup: * @display: a #CoglDisplay * @error: return location for a #GError * * Explicitly sets up the given @display object. Use of this api is * optional since Cogl will internally setup the display if not done * explicitly. * * When a display is first allocated via cogl_display_new() it is in a * mutable configuration mode. This allows us to extend the apis * available for configuring a display without requiring huge numbers * of constructor arguments. * * Its possible to request a configuration that might not be * supportable on the current system and so this api provides a means * to apply the configuration explicitly but if it fails then an * exception will be returned so you can handle the error gracefully * and perhaps fall back to an alternative configuration. * * If you instead rely on Cogl implicitly calling cogl_display_setup() * for you then if there is an error with the configuration you won't * get an opportunity to handle that and the application may abort * with a message. For simple applications that don't have any * fallback options this behaviour may be fine. * * Return value: Returns %TRUE if there was no error, else it returns * %FALSE and returns an exception via @error. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_display_setup (CoglDisplay *display, GError **error); /** * cogl_is_display: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglDisplay. * * Return value: %TRUE if the object references a #CoglDisplay * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_display (void *object); G_END_DECLS #endif /* __COGL_DISPLAY_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-layer.c0000664000175000017500000007635114723361714017633 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline.h" #include "cogl-pipeline-layer-private.h" #include "cogl-pipeline-layer-state-private.h" #include "cogl-pipeline-layer-state.h" #include "cogl-node-private.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include static void _cogl_pipeline_layer_free (CoglPipelineLayer *layer); /* This type was made deprecated before the cogl_is_pipeline_layer function was ever exposed in the public headers so there's no need to make the cogl_is_pipeline_layer function public. We use INTERNAL so that the cogl_is_* function won't get defined */ COGL_OBJECT_INTERNAL_DEFINE (PipelineLayer, pipeline_layer); CoglPipelineLayer * _cogl_pipeline_layer_get_authority (CoglPipelineLayer *layer, unsigned long difference) { CoglPipelineLayer *authority = layer; while (!(authority->differences & difference)) authority = _cogl_pipeline_layer_get_parent (authority); return authority; } int _cogl_pipeline_layer_get_unit_index (CoglPipelineLayer *layer) { CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_UNIT); return authority->unit_index; } gboolean _cogl_pipeline_layer_has_alpha (CoglPipelineLayer *layer) { CoglPipelineLayer *combine_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_COMBINE); CoglPipelineLayerBigState *big_state = combine_authority->big_state; CoglPipelineLayer *tex_authority; CoglPipelineLayer *snippets_authority; /* has_alpha maintains the alpha status for the GL_PREVIOUS layer */ /* For anything but the default texture combine we currently just * assume it may result in an alpha value < 1 * * FIXME: we could do better than this. */ if (big_state->texture_combine_alpha_func != COGL_PIPELINE_COMBINE_FUNC_MODULATE || big_state->texture_combine_alpha_src[0] != COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS || big_state->texture_combine_alpha_op[0] != COGL_PIPELINE_COMBINE_OP_SRC_ALPHA || big_state->texture_combine_alpha_src[1] != COGL_PIPELINE_COMBINE_SOURCE_TEXTURE || big_state->texture_combine_alpha_op[1] != COGL_PIPELINE_COMBINE_OP_SRC_ALPHA) { return TRUE; } /* NB: A layer may have a combine mode set on it but not yet * have an associated texture which would mean we'd fallback * to the default texture which doesn't have an alpha component */ tex_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); if (tex_authority->texture && _cogl_texture_get_format (tex_authority->texture) & COGL_A_BIT) { return TRUE; } /* All bets are off if the layer contains any snippets */ snippets_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS); if (snippets_authority->big_state->vertex_snippets.entries != NULL) return TRUE; snippets_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS); if (snippets_authority->big_state->fragment_snippets.entries != NULL) return TRUE; return FALSE; } unsigned int _cogl_get_n_args_for_combine_func (CoglPipelineCombineFunc func) { switch (func) { case COGL_PIPELINE_COMBINE_FUNC_REPLACE: return 1; case COGL_PIPELINE_COMBINE_FUNC_MODULATE: case COGL_PIPELINE_COMBINE_FUNC_ADD: case COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED: case COGL_PIPELINE_COMBINE_FUNC_SUBTRACT: case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB: case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA: return 2; case COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE: return 3; } return 0; } void _cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest, CoglPipelineLayer *src, unsigned long differences) { CoglPipelineLayerBigState *big_dest, *big_src; if ((differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) && !dest->has_big_state) { dest->big_state = g_slice_new (CoglPipelineLayerBigState); dest->has_big_state = TRUE; } big_dest = dest->big_state; big_src = src->big_state; dest->differences |= differences; while (differences) { int index = ffs (differences) - 1; differences &= ~(1 << index); /* This convoluted switch statement is just here so that we'll * get a warning if a new state is added without handling it * here */ switch (index) { case COGL_PIPELINE_LAYER_STATE_COUNT: case COGL_PIPELINE_LAYER_STATE_UNIT_INDEX: g_warn_if_reached (); break; case COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX: dest->texture = src->texture; if (dest->texture) cogl_object_ref (dest->texture); break; case COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX: dest->sampler_cache_entry = src->sampler_cache_entry; break; case COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX: { CoglPipelineCombineFunc func; int n_args, i; func = big_src->texture_combine_rgb_func; big_dest->texture_combine_rgb_func = func; n_args = _cogl_get_n_args_for_combine_func (func); for (i = 0; i < n_args; i++) { big_dest->texture_combine_rgb_src[i] = big_src->texture_combine_rgb_src[i]; big_dest->texture_combine_rgb_op[i] = big_src->texture_combine_rgb_op[i]; } func = big_src->texture_combine_alpha_func; big_dest->texture_combine_alpha_func = func; n_args = _cogl_get_n_args_for_combine_func (func); for (i = 0; i < n_args; i++) { big_dest->texture_combine_alpha_src[i] = big_src->texture_combine_alpha_src[i]; big_dest->texture_combine_alpha_op[i] = big_src->texture_combine_alpha_op[i]; } } break; case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX: memcpy (big_dest->texture_combine_constant, big_src->texture_combine_constant, sizeof (big_dest->texture_combine_constant)); break; case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX: big_dest->point_sprite_coords = big_src->point_sprite_coords; break; case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX: _cogl_pipeline_snippet_list_copy (&big_dest->vertex_snippets, &big_src->vertex_snippets); break; case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX: _cogl_pipeline_snippet_list_copy (&big_dest->fragment_snippets, &big_src->fragment_snippets); break; } } } static void _cogl_pipeline_layer_init_multi_property_sparse_state ( CoglPipelineLayer *layer, CoglPipelineLayerState change) { CoglPipelineLayer *authority; /* Nothing to initialize in these cases since they are all comprised * of one member which we expect to immediately be overwritten. */ if (!(change & COGL_PIPELINE_LAYER_STATE_MULTI_PROPERTY)) return; authority = _cogl_pipeline_layer_get_authority (layer, change); switch (change) { /* XXX: avoid using a default: label so we get a warning if we * don't explicitly handle a newly defined state-group here. */ case COGL_PIPELINE_LAYER_STATE_UNIT: case COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA: case COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS: case COGL_PIPELINE_LAYER_STATE_USER_MATRIX: case COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT: case COGL_PIPELINE_LAYER_STATE_SAMPLER: g_return_if_reached (); /* XXX: technically we could probably even consider these as * single property state-groups from the pov that currently the * corresponding property setters always update all of the values * at the same time. */ case COGL_PIPELINE_LAYER_STATE_COMBINE: { int n_args; int i; CoglPipelineLayerBigState *src_big_state = authority->big_state; CoglPipelineLayerBigState *dest_big_state = layer->big_state; GLint func = src_big_state->texture_combine_rgb_func; dest_big_state->texture_combine_rgb_func = func; n_args = _cogl_get_n_args_for_combine_func (func); for (i = 0; i < n_args; i++) { dest_big_state->texture_combine_rgb_src[i] = src_big_state->texture_combine_rgb_src[i]; dest_big_state->texture_combine_rgb_op[i] = src_big_state->texture_combine_rgb_op[i]; } func = src_big_state->texture_combine_alpha_func; dest_big_state->texture_combine_alpha_func = func; n_args = _cogl_get_n_args_for_combine_func (func); for (i = 0; i < n_args; i++) { dest_big_state->texture_combine_alpha_src[i] = src_big_state->texture_combine_alpha_src[i]; dest_big_state->texture_combine_alpha_op[i] = src_big_state->texture_combine_alpha_op[i]; } break; } case COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS: _cogl_pipeline_snippet_list_copy (&layer->big_state->vertex_snippets, &authority->big_state-> vertex_snippets); break; case COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS: _cogl_pipeline_snippet_list_copy (&layer->big_state->fragment_snippets, &authority->big_state-> fragment_snippets); break; } } /* NB: If a layer has descendants we can't modify the layer * NB: If the layer is owned and the owner has descendants we can't * modify the layer. * * This function will allocate a new derived layer if you are trying * to change the state of a layer with dependants (as described above) * so you must always check the return value. * * If a new layer is returned it will be owned by required_owner. * (NB: a layer is always modified with respect to a pipeline - the * "required_owner") * * required_owner can only by NULL for new, currently unowned layers * with no dependants. */ CoglPipelineLayer * _cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, CoglPipelineLayer *layer, CoglPipelineLayerState change) { CoglTextureUnit *unit; /* Identify the case where the layer is new with no owner or * dependants and so we don't need to do anything. */ if (_cogl_list_empty (&COGL_NODE (layer)->children) && layer->owner == NULL) goto init_layer_state; /* We only allow a NULL required_owner for new layers */ g_return_val_if_fail (required_owner != NULL, layer); /* Chain up: * A modification of a layer is indirectly also a modification of * its owner so first make sure to flush the journal of any * references to the current owner state and if necessary perform * a copy-on-write for the required_owner if it has dependants. */ _cogl_pipeline_pre_change_notify (required_owner, COGL_PIPELINE_STATE_LAYERS, NULL, TRUE); /* Unlike pipelines; layers are simply considered immutable once * they have dependants - either direct children, or another * pipeline as an owner. */ if (!_cogl_list_empty (&COGL_NODE (layer)->children) || layer->owner != required_owner) { CoglPipelineLayer *new = _cogl_pipeline_layer_copy (layer); if (layer->owner == required_owner) _cogl_pipeline_remove_layer_difference (required_owner, layer, FALSE); _cogl_pipeline_add_layer_difference (required_owner, new, FALSE); cogl_object_unref (new); layer = new; goto init_layer_state; } /* Note: At this point we know there is only one pipeline dependant on * this layer (required_owner), and there are no other layers * dependant on this layer so it's ok to modify it. */ /* NB: Although layers can have private state associated with them * by multiple backends we know that a layer can't be *changed* if * it has multiple dependants so if we reach here we know we only * have a single owner and can only be associated with a single * backend that needs to be notified of the layer change... */ { const CoglPipelineProgend *progend = _cogl_pipeline_progend; const CoglPipelineFragend *fragend = _cogl_pipeline_fragend; const CoglPipelineVertend *vertend = _cogl_pipeline_vertend; if (fragend->layer_pre_change_notify) fragend->layer_pre_change_notify (required_owner, layer, change); if (vertend->layer_pre_change_notify) vertend->layer_pre_change_notify (required_owner, layer, change); if (progend->layer_pre_change_notify) progend->layer_pre_change_notify (required_owner, layer, change); } /* If the layer being changed is the same as the last layer we * flushed to the corresponding texture unit then we keep a track of * the changes so we can try to minimize redundant OpenGL calls if * the same layer is flushed again. */ unit = _cogl_get_texture_unit (_cogl_pipeline_layer_get_unit_index (layer)); if (unit->layer == layer) unit->layer_changes_since_flush |= change; init_layer_state: if (required_owner) required_owner->age++; if (change & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE && !layer->has_big_state) { layer->big_state = g_slice_new (CoglPipelineLayerBigState); layer->has_big_state = TRUE; } /* Note: conceptually we have just been notified that a single * property value is about to change, but since some state-groups * contain multiple properties and 'layer' is about to take over * being the authority for the property's corresponding state-group * we need to maintain the integrity of the other property values * too. * * To ensure this we handle multi-property state-groups by copying * all the values from the old-authority to the new... * * We don't have to worry about non-sparse property groups since * we never take over being an authority for such properties so * they automatically maintain integrity. */ if (change & COGL_PIPELINE_LAYER_STATE_ALL_SPARSE && !(layer->differences & change)) { _cogl_pipeline_layer_init_multi_property_sparse_state (layer, change); layer->differences |= change; } return layer; } static void _cogl_pipeline_layer_unparent (CoglNode *layer) { /* Chain up */ _cogl_pipeline_node_unparent_real (layer); } static void _cogl_pipeline_layer_set_parent (CoglPipelineLayer *layer, CoglPipelineLayer *parent) { /* Chain up */ _cogl_pipeline_node_set_parent_real (COGL_NODE (layer), COGL_NODE (parent), _cogl_pipeline_layer_unparent, TRUE); } CoglPipelineLayer * _cogl_pipeline_layer_copy (CoglPipelineLayer *src) { CoglPipelineLayer *layer = g_slice_new (CoglPipelineLayer); _cogl_pipeline_node_init (COGL_NODE (layer)); layer->owner = NULL; layer->index = src->index; layer->differences = 0; layer->has_big_state = FALSE; _cogl_pipeline_layer_set_parent (layer, src); return _cogl_pipeline_layer_object_new (layer); } /* XXX: This is duplicated logic; the same as for * _cogl_pipeline_prune_redundant_ancestry it would be nice to find a * way to consolidate these functions! */ void _cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer) { CoglPipelineLayer *new_parent = _cogl_pipeline_layer_get_parent (layer); /* walk up past ancestors that are now redundant and potentially * reparent the layer. */ while (_cogl_pipeline_layer_get_parent (new_parent) && (new_parent->differences | layer->differences) == layer->differences) new_parent = _cogl_pipeline_layer_get_parent (new_parent); _cogl_pipeline_layer_set_parent (layer, new_parent); } /* Determine the mask of differences between two layers. * * XXX: If layers and pipelines could both be cast to a common Tree * type of some kind then we could have a unified * compare_differences() function. */ unsigned long _cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0, CoglPipelineLayer *layer1) { GSList *head0 = NULL; GSList *head1 = NULL; CoglPipelineLayer *node0; CoglPipelineLayer *node1; int len0 = 0; int len1 = 0; int count; GSList *common_ancestor0; GSList *common_ancestor1; unsigned long layers_difference = 0; /* Algorithm: * * 1) Walk the ancestors of each layer to the root node, adding a * pointer to each ancester node to two linked lists * * 2) Compare the lists to find the nodes where they start to * differ marking the common_ancestor node for each list. * * 3) For each list now iterate starting after the common_ancestor * nodes ORing each nodes ->difference mask into the final * differences mask. */ for (node0 = layer0; node0; node0 = _cogl_pipeline_layer_get_parent (node0)) { GSList *link = alloca (sizeof (GSList)); link->next = head0; link->data = node0; head0 = link; len0++; } for (node1 = layer1; node1; node1 = _cogl_pipeline_layer_get_parent (node1)) { GSList *link = alloca (sizeof (GSList)); link->next = head1; link->data = node1; head1 = link; len1++; } /* NB: There's no point looking at the head entries since we know both layers * must have the same default layer as their root node. */ common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; count = MIN (len0, len1) - 1; while (count--) { if (head0->data != head1->data) break; common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; } for (head0 = common_ancestor0->next; head0; head0 = head0->next) { node0 = head0->data; layers_difference |= node0->differences; } for (head1 = common_ancestor1->next; head1; head1 = head1->next) { node1 = head1->data; layers_difference |= node1->differences; } return layers_difference; } static gboolean layer_state_equal (CoglPipelineLayerStateIndex state_index, CoglPipelineLayer **authorities0, CoglPipelineLayer **authorities1, CoglPipelineLayerStateComparitor comparitor) { return comparitor (authorities0[state_index], authorities1[state_index]); } void _cogl_pipeline_layer_resolve_authorities (CoglPipelineLayer *layer, unsigned long differences, CoglPipelineLayer **authorities) { unsigned long remaining = differences; CoglPipelineLayer *authority = layer; do { unsigned long found = authority->differences & remaining; int i; if (found == 0) continue; for (i = 0; TRUE; i++) { unsigned long state = (1L< found) break; } remaining &= ~found; if (remaining == 0) return; } while ((authority = _cogl_pipeline_layer_get_parent (authority))); g_assert (remaining == 0); } gboolean _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, CoglPipelineLayer *layer1, unsigned long differences_mask, CoglPipelineEvalFlags flags) { unsigned long layers_difference; CoglPipelineLayer *authorities0[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; CoglPipelineLayer *authorities1[COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT]; if (layer0 == layer1) return TRUE; layers_difference = _cogl_pipeline_layer_compare_differences (layer0, layer1); /* Only compare the sparse state groups requested by the caller... */ layers_difference &= differences_mask; _cogl_pipeline_layer_resolve_authorities (layer0, layers_difference, authorities0); _cogl_pipeline_layer_resolve_authorities (layer1, layers_difference, authorities1); if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) { CoglPipelineLayerStateIndex state_index = COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX; if (!_cogl_pipeline_layer_texture_data_equal (authorities0[state_index], authorities1[state_index], flags)) return FALSE; } if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, authorities0, authorities1, _cogl_pipeline_layer_combine_state_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, authorities0, authorities1, _cogl_pipeline_layer_combine_constant_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, authorities0, authorities1, _cogl_pipeline_layer_sampler_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_USER_MATRIX && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, authorities0, authorities1, _cogl_pipeline_layer_user_matrix_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, authorities0, authorities1, _cogl_pipeline_layer_point_sprite_coords_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX, authorities0, authorities1, _cogl_pipeline_layer_vertex_snippets_equal)) return FALSE; if (layers_difference & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS && !layer_state_equal (COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX, authorities0, authorities1, _cogl_pipeline_layer_fragment_snippets_equal)) return FALSE; return TRUE; } static void _cogl_pipeline_layer_free (CoglPipelineLayer *layer) { _cogl_pipeline_layer_unparent (COGL_NODE (layer)); if (layer->differences & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA && layer->texture != NULL) cogl_object_unref (layer->texture); if (layer->differences & COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS) _cogl_pipeline_snippet_list_free (&layer->big_state->vertex_snippets); if (layer->differences & COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS) _cogl_pipeline_snippet_list_free (&layer->big_state->fragment_snippets); if (layer->differences & COGL_PIPELINE_LAYER_STATE_NEEDS_BIG_STATE) g_slice_free (CoglPipelineLayerBigState, layer->big_state); g_slice_free (CoglPipelineLayer, layer); } void _cogl_pipeline_init_default_layers (void) { CoglPipelineLayer *layer = g_slice_new0 (CoglPipelineLayer); CoglPipelineLayerBigState *big_state = g_slice_new0 (CoglPipelineLayerBigState); CoglPipelineLayer *new; _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_pipeline_node_init (COGL_NODE (layer)); layer->index = 0; layer->differences = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; layer->unit_index = 0; layer->texture = NULL; layer->sampler_cache_entry = _cogl_sampler_cache_get_default_entry (ctx->sampler_cache); layer->big_state = big_state; layer->has_big_state = TRUE; /* Choose the same default combine mode as OpenGL: * RGBA = MODULATE(PREVIOUS[RGBA],TEXTURE[RGBA]) */ big_state->texture_combine_rgb_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE; big_state->texture_combine_rgb_src[0] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; big_state->texture_combine_rgb_src[1] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; big_state->texture_combine_rgb_op[0] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR; big_state->texture_combine_rgb_op[1] = COGL_PIPELINE_COMBINE_OP_SRC_COLOR; big_state->texture_combine_alpha_func = COGL_PIPELINE_COMBINE_FUNC_MODULATE; big_state->texture_combine_alpha_src[0] = COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS; big_state->texture_combine_alpha_src[1] = COGL_PIPELINE_COMBINE_SOURCE_TEXTURE; big_state->texture_combine_alpha_op[0] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; big_state->texture_combine_alpha_op[1] = COGL_PIPELINE_COMBINE_OP_SRC_ALPHA; big_state->point_sprite_coords = FALSE; cogl_matrix_init_identity (&big_state->matrix); ctx->default_layer_0 = _cogl_pipeline_layer_object_new (layer); /* TODO: we should make default_layer_n comprise of two * descendants of default_layer_0: * - the first descendant should change the texture combine * to what we expect is most commonly used for multitexturing * - the second should revert the above change. * * why? the documentation for how a new layer is initialized * doesn't say that layers > 0 have different defaults so unless * we change the documentation we can't use different defaults, * but if the user does what we expect and changes the * texture combine then we can revert the authority to the * first descendant which means we can maximize the number * of layers with a common ancestor. * * The main problem will be that we'll need to disable the * optimizations for flattening the ancestry when we make * the second descendant which reverts the state. */ ctx->default_layer_n = _cogl_pipeline_layer_copy (layer); new = _cogl_pipeline_set_layer_unit (NULL, ctx->default_layer_n, 1); g_assert (new == ctx->default_layer_n); /* Since we passed a newly allocated layer we don't expect that * _set_layer_unit() will have to allocate *another* layer. */ /* Finally we create a dummy dependant for ->default_layer_n which * effectively ensures that ->default_layer_n and ->default_layer_0 * remain immutable. */ ctx->dummy_layer_dependant = _cogl_pipeline_layer_copy (ctx->default_layer_n); } void _cogl_pipeline_layer_pre_paint (CoglPipelineLayer *layer) { CoglPipelineLayer *texture_authority; texture_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA); if (texture_authority->texture != NULL) { CoglTexturePrePaintFlags flags = 0; CoglPipelineFilter min_filter; CoglPipelineFilter mag_filter; _cogl_pipeline_layer_get_filters (layer, &min_filter, &mag_filter); if (min_filter == COGL_PIPELINE_FILTER_NEAREST_MIPMAP_NEAREST || min_filter == COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST || min_filter == COGL_PIPELINE_FILTER_NEAREST_MIPMAP_LINEAR || min_filter == COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR) flags |= COGL_TEXTURE_NEEDS_MIPMAP; _cogl_texture_pre_paint (texture_authority->texture, flags); } } /* Determines if we need to handle the RGB and A texture combining * separately or is the same function used for both channel masks and * with the same arguments... */ gboolean _cogl_pipeline_layer_needs_combine_separate (CoglPipelineLayer *combine_authority) { CoglPipelineLayerBigState *big_state = combine_authority->big_state; int n_args; int i; if (big_state->texture_combine_rgb_func != big_state->texture_combine_alpha_func) return TRUE; n_args = _cogl_get_n_args_for_combine_func (big_state->texture_combine_rgb_func); for (i = 0; i < n_args; i++) { if (big_state->texture_combine_rgb_src[i] != big_state->texture_combine_alpha_src[i]) return TRUE; /* * We can allow some variation of the source operands without * needing a separation... * * "A = REPLACE (CONSTANT[A])" + either of the following... * "RGB = REPLACE (CONSTANT[RGB])" * "RGB = REPLACE (CONSTANT[A])" * * can be combined as: * "RGBA = REPLACE (CONSTANT)" or * "RGBA = REPLACE (CONSTANT[A])" or * * And "A = REPLACE (1-CONSTANT[A])" + either of the following... * "RGB = REPLACE (1-CONSTANT)" or * "RGB = REPLACE (1-CONSTANT[A])" * * can be combined as: * "RGBA = REPLACE (1-CONSTANT)" or * "RGBA = REPLACE (1-CONSTANT[A])" */ switch (big_state->texture_combine_alpha_op[i]) { case GL_SRC_ALPHA: switch (big_state->texture_combine_rgb_op[i]) { case GL_SRC_COLOR: case GL_SRC_ALPHA: break; default: return FALSE; } break; case GL_ONE_MINUS_SRC_ALPHA: switch (big_state->texture_combine_rgb_op[i]) { case GL_ONE_MINUS_SRC_COLOR: case GL_ONE_MINUS_SRC_ALPHA: break; default: return FALSE; } break; default: return FALSE; /* impossible */ } } return FALSE; } muffin-6.4.1/cogl/cogl/cogl-context-private.h0000664000175000017500000002726514723361714020055 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_CONTEXT_PRIVATE_H #define __COGL_CONTEXT_PRIVATE_H #include "cogl-context.h" #include "cogl-flags.h" #include "cogl-display-private.h" #include "cogl-clip-stack.h" #include "cogl-matrix-stack.h" #include "cogl-pipeline-private.h" #include "cogl-buffer-private.h" #include "cogl-bitmask.h" #include "cogl-atlas.h" #include "cogl-driver.h" #include "cogl-texture-driver.h" #include "cogl-pipeline-cache.h" #include "cogl-texture-2d.h" #include "cogl-sampler-cache-private.h" #include "cogl-gpu-info-private.h" #include "cogl-gl-header.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-private.h" #include "cogl-fence-private.h" #include "cogl-poll-private.h" #include "cogl-path/cogl-path-types.h" #include "cogl-private.h" #include "winsys/cogl-winsys-private.h" typedef struct { GLfloat v[3]; GLfloat t[2]; GLubyte c[4]; } CoglTextureGLVertex; struct _CoglContext { CoglObject _parent; CoglDisplay *display; CoglDriver driver; /* Information about the GPU and driver which we can use to determine certain workarounds */ CoglGpuInfo gpu; /* vtables for the driver functions */ const CoglDriverVtable *driver_vtable; const CoglTextureDriver *texture_driver; int glsl_major; int glsl_minor; /* This is the GLSL version that we will claim that snippets are * written against using the #version pragma. This will be the * largest version that is less than or equal to the version * provided by the driver without massively altering the syntax. Eg, * we wouldn't use version 1.3 even if it is available because that * removes the ‘attribute’ and ‘varying’ keywords. */ int glsl_version_to_use; /* Features cache */ unsigned long features[COGL_FLAGS_N_LONGS_FOR_SIZE (_COGL_N_FEATURE_IDS)]; unsigned long private_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)]; CoglPipeline *default_pipeline; CoglPipelineLayer *default_layer_0; CoglPipelineLayer *default_layer_n; CoglPipelineLayer *dummy_layer_dependant; GHashTable *attribute_name_states_hash; GArray *attribute_name_index_map; int n_attribute_names; CoglBitmask enabled_custom_attributes; /* These are temporary bitmasks that are used when disabling * builtin and custom attribute arrays. They are here just * to avoid allocating new ones each time */ CoglBitmask enable_custom_attributes_tmp; CoglBitmask changed_bits_tmp; gboolean legacy_backface_culling_enabled; /* A few handy matrix constants */ CoglMatrix identity_matrix; CoglMatrix y_flip_matrix; /* The matrix stack entries that should be flushed during the next * pipeline state flush */ CoglMatrixEntry *current_projection_entry; CoglMatrixEntry *current_modelview_entry; CoglMatrixEntry identity_entry; /* A cache of the last (immutable) matrix stack entries that were * flushed to the GL matrix builtins */ CoglMatrixEntryCache builtin_flushed_projection; CoglMatrixEntryCache builtin_flushed_modelview; GArray *texture_units; int active_texture_unit; /* Only used for comparing other pipelines when reading pixels. */ CoglPipeline *opaque_color_pipeline; GString *codegen_header_buffer; GString *codegen_source_buffer; GString *codegen_boilerplate_buffer; CoglPipelineCache *pipeline_cache; /* Textures */ CoglTexture2D *default_gl_texture_2d_tex; /* Central list of all framebuffers so all journals can be flushed * at any time. */ GList *framebuffers; /* Global journal buffers */ GArray *journal_flush_attributes_array; GArray *journal_clip_bounds; GArray *polygon_vertices; /* Some simple caching, to minimize state changes... */ CoglPipeline *current_pipeline; unsigned long current_pipeline_changes_since_flush; gboolean current_pipeline_with_color_attrib; gboolean current_pipeline_unknown_color_alpha; unsigned long current_pipeline_age; gboolean gl_blend_enable_cache; gboolean depth_test_enabled_cache; CoglDepthTestFunction depth_test_function_cache; gboolean depth_writing_enabled_cache; float depth_range_near_cache; float depth_range_far_cache; gboolean legacy_depth_test_enabled; CoglBuffer *current_buffer[COGL_BUFFER_BIND_TARGET_COUNT]; /* Framebuffers */ unsigned long current_draw_buffer_state_flushed; unsigned long current_draw_buffer_changes; CoglFramebuffer *current_draw_buffer; CoglFramebuffer *current_read_buffer; gboolean have_last_offscreen_allocate_flags; CoglOffscreenAllocateFlags last_offscreen_allocate_flags; GHashTable *swap_callback_closures; int next_swap_callback_id; CoglList onscreen_events_queue; CoglList onscreen_dirty_queue; CoglClosure *onscreen_dispatch_idle; /* This becomes TRUE the first time the context is bound to an * onscreen buffer. This is used by cogl-framebuffer-gl to determine * when to initialise the glDrawBuffer state */ gboolean was_bound_to_onscreen; /* Primitives */ CoglPath *current_path; CoglPipeline *stencil_pipeline; /* Pre-generated VBOs containing indices to generate GL_TRIANGLES out of a vertex array of quads */ CoglIndices *quad_buffer_indices_byte; unsigned int quad_buffer_indices_len; CoglIndices *quad_buffer_indices; CoglIndices *rectangle_byte_indices; CoglIndices *rectangle_short_indices; int rectangle_short_indices_len; CoglPipeline *texture_download_pipeline; CoglPipeline *blit_texture_pipeline; GSList *atlases; GHookList atlas_reorganize_callbacks; /* This debugging variable is used to pick a colour for visually displaying the quad batches. It needs to be global so that it can be reset by cogl_clear. It needs to be reset to increase the chances of getting the same colour during an animation */ uint8_t journal_rectangles_color; /* Cached values for GL_MAX_TEXTURE_[IMAGE_]UNITS to avoid calling glGetInteger too often */ GLint max_texture_units; GLint max_texture_image_units; GLint max_activateable_texture_units; /* Fragment processing programs */ GLuint current_gl_program; gboolean current_gl_dither_enabled; GLenum current_gl_draw_buffer; /* Clipping */ /* TRUE if we have a valid clipping stack flushed. In that case current_clip_stack will describe what the current state is. If this is FALSE then the current clip stack is completely unknown so it will need to be reflushed. In that case current_clip_stack doesn't need to be a valid pointer. We can't just use NULL in current_clip_stack to mark a dirty state because NULL is a valid stack (meaning no clipping) */ gboolean current_clip_stack_valid; /* The clip state that was flushed. This isn't intended to be used as a stack to push and pop new entries. Instead the current stack that the user wants is part of the framebuffer state. This is just used to record the flush state so we can avoid flushing the same state multiple times. When the clip state is flushed this will hold a reference */ CoglClipStack *current_clip_stack; /* This is used as a temporary buffer to fill a CoglBuffer when cogl_buffer_map fails and we only want to map to fill it with new data */ GByteArray *buffer_map_fallback_array; gboolean buffer_map_fallback_in_use; size_t buffer_map_fallback_offset; CoglSamplerCache *sampler_cache; unsigned long winsys_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_WINSYS_FEATURE_N_FEATURES)]; void *winsys; /* Array of names of uniforms. These are used like quarks to give a unique number to each uniform name except that we ensure that they increase sequentially so that we can use the id as an index into a bitfield representing the uniforms that a pipeline overrides from its parent. */ GPtrArray *uniform_names; /* A hash table to quickly get an index given an existing name. The name strings are owned by the uniform_names array. The values are the uniform location cast to a pointer. */ GHashTable *uniform_name_hash; int n_uniform_names; CoglPollSource *fences_poll_source; CoglList fences; /* This defines a list of function pointers that Cogl uses from either GL or GLES. All functions are accessed indirectly through these pointers rather than linking to them directly */ #ifndef APIENTRY #define APIENTRY #endif #define COGL_EXT_BEGIN(name, \ min_gl_major, min_gl_minor, \ gles_availability, \ extension_suffixes, extension_names) #define COGL_EXT_FUNCTION(ret, name, args) \ ret (APIENTRY * name) args; #define COGL_EXT_END() #include "gl-prototypes/cogl-all-functions.h" #undef COGL_EXT_BEGIN #undef COGL_EXT_FUNCTION #undef COGL_EXT_END }; COGL_EXPORT CoglContext * _cogl_context_get_default (void); const CoglWinsysVtable * _cogl_context_get_winsys (CoglContext *context); /* Query the GL extensions and lookup the corresponding function * pointers. Theoretically the list of extensions can change for * different GL contexts so it is the winsys backend's responsiblity * to know when to re-query the GL extensions. The backend should also * check whether the GL context is supported by Cogl. If not it should * return FALSE and set @error */ gboolean _cogl_context_update_features (CoglContext *context, GError **error); /* Obtains the context and returns retval if NULL */ #define _COGL_GET_CONTEXT(ctxvar, retval) \ CoglContext *ctxvar = _cogl_context_get_default (); \ if (ctxvar == NULL) return retval; #define NO_RETVAL void _cogl_context_set_current_projection_entry (CoglContext *context, CoglMatrixEntry *entry); void _cogl_context_set_current_modelview_entry (CoglContext *context, CoglMatrixEntry *entry); /* * _cogl_context_get_gl_extensions: * @context: A CoglContext * * Return value: a NULL-terminated array of strings representing the * supported extensions by the current driver. This array is owned * by the caller and should be freed with g_strfreev(). */ char ** _cogl_context_get_gl_extensions (CoglContext *context); const char * _cogl_context_get_gl_version (CoglContext *context); #endif /* __COGL_CONTEXT_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-color-private.h0000664000175000017500000000324414723361714017476 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_COLOR_PRIVATE_PRIVATE_H #define __COGL_COLOR_PRIVATE_PRIVATE_H #include "cogl-color.h" #include /* cogl-pipeline.c wants to be able to hash CoglColor data so it needs * the exact data size to be able to avoid reading the padding bytes. */ #define _COGL_COLOR_DATA_SIZE 4 void _cogl_color_get_rgba_4ubv (const CoglColor *color, uint8_t *dest); #endif /* __COGL_COLOR_PRIVATE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-journal.c0000664000175000017500000020373214723361714016361 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-context-private.h" #include "cogl-journal-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline-private.h" #include "cogl-framebuffer-private.h" #include "cogl-profile.h" #include "cogl-attribute-private.h" #include "cogl-point-in-poly-private.h" #include "cogl-private.h" #include "cogl1-context.h" #include #include #include /* XXX NB: * The data logged in logged_vertices is formatted as follows: * * Per entry: * 4 RGBA GLubytes for the color * 2 floats for the top left position * 2 * n_layers floats for the top left texture coordinates * 2 floats for the bottom right position * 2 * n_layers floats for the bottom right texture coordinates */ #define GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS(N_LAYERS) \ (N_LAYERS * 2 + 2) /* XXX NB: * Once in the vertex array, the journal's vertex data is arranged as follows: * 4 vertices per quad: * 2 or 3 GLfloats per position (3 when doing software transforms) * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * * Where n_layers corresponds to the number of pipeline layers enabled * * To avoid frequent changes in the stride of our vertex data we always pad * n_layers to be >= 2 * * There will be four vertices per quad in the vertex array * * When we are transforming quads in software we need to also track the z * coordinate of transformed vertices. * * So for a given number of layers this gets the stride in 32bit words: */ #define SW_TRANSFORM (!(COGL_DEBUG_ENABLED \ (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) #define POS_STRIDE (SW_TRANSFORM ? 3 : 2) /* number of 32bit words */ #define N_POS_COMPONENTS POS_STRIDE #define COLOR_STRIDE 1 /* number of 32bit words */ #define TEX_STRIDE 2 /* number of 32bit words */ #define MIN_LAYER_PADING 2 #define GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS(N_LAYERS) \ (POS_STRIDE + COLOR_STRIDE + \ TEX_STRIDE * (N_LAYERS < MIN_LAYER_PADING ? MIN_LAYER_PADING : N_LAYERS)) /* If a batch is longer than this threshold then we'll assume it's not worth doing software clipping and it's cheaper to program the GPU to do the clip */ #define COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD 8 typedef struct _CoglJournalFlushState { CoglContext *ctx; CoglJournal *journal; CoglAttributeBuffer *attribute_buffer; GArray *attributes; int current_attribute; size_t stride; size_t array_offset; GLuint current_vertex; CoglIndices *indices; size_t indices_type_size; CoglPipeline *pipeline; } CoglJournalFlushState; typedef void (*CoglJournalBatchCallback) (CoglJournalEntry *start, int n_entries, void *data); typedef gboolean (*CoglJournalBatchTest) (CoglJournalEntry *entry0, CoglJournalEntry *entry1); static void _cogl_journal_free (CoglJournal *journal); COGL_OBJECT_INTERNAL_DEFINE (Journal, journal); static void _cogl_journal_free (CoglJournal *journal) { int i; if (journal->entries) g_array_free (journal->entries, TRUE); if (journal->vertices) g_array_free (journal->vertices, TRUE); for (i = 0; i < COGL_JOURNAL_VBO_POOL_SIZE; i++) if (journal->vbo_pool[i]) cogl_object_unref (journal->vbo_pool[i]); g_slice_free (CoglJournal, journal); } CoglJournal * _cogl_journal_new (CoglFramebuffer *framebuffer) { CoglJournal *journal = g_slice_new0 (CoglJournal); /* The journal keeps a pointer back to the framebuffer because there is effectively a 1:1 mapping between journals and framebuffers. However, to avoid a circular reference the journal doesn't take a reference unless it is non-empty. The framebuffer has a special unref implementation to ensure that the journal is flushed when the journal is the only thing keeping it alive */ journal->framebuffer = framebuffer; journal->entries = g_array_new (FALSE, FALSE, sizeof (CoglJournalEntry)); journal->vertices = g_array_new (FALSE, FALSE, sizeof (float)); _cogl_list_init (&journal->pending_fences); return _cogl_journal_object_new (journal); } static void _cogl_journal_dump_logged_quad (uint8_t *data, int n_layers) { size_t stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); int i; g_print ("n_layers = %d; rgba=0x%02X%02X%02X%02X\n", n_layers, data[0], data[1], data[2], data[3]); data += 4; for (i = 0; i < 2; i++) { float *v = (float *)data + (i * stride); int j; g_print ("v%d: x = %f, y = %f", i, v[0], v[1]); for (j = 0; j < n_layers; j++) { float *t = v + 2 + TEX_STRIDE * j; g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); } g_print ("\n"); } } static void _cogl_journal_dump_quad_vertices (uint8_t *data, int n_layers) { size_t stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers); int i; g_print ("n_layers = %d; stride = %d; pos stride = %d; color stride = %d; " "tex stride = %d; stride in bytes = %d\n", n_layers, (int)stride, POS_STRIDE, COLOR_STRIDE, TEX_STRIDE, (int)stride * 4); for (i = 0; i < 4; i++) { float *v = (float *)data + (i * stride); uint8_t *c = data + (POS_STRIDE * 4) + (i * stride * 4); int j; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) g_print ("v%d: x = %f, y = %f, rgba=0x%02X%02X%02X%02X", i, v[0], v[1], c[0], c[1], c[2], c[3]); else g_print ("v%d: x = %f, y = %f, z = %f, rgba=0x%02X%02X%02X%02X", i, v[0], v[1], v[2], c[0], c[1], c[2], c[3]); for (j = 0; j < n_layers; j++) { float *t = v + POS_STRIDE + COLOR_STRIDE + TEX_STRIDE * j; g_print (", tx%d = %f, ty%d = %f", j, t[0], j, t[1]); } g_print ("\n"); } } static void _cogl_journal_dump_quad_batch (uint8_t *data, int n_layers, int n_quads) { size_t byte_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; int i; g_print ("_cogl_journal_dump_quad_batch: n_layers = %d, n_quads = %d\n", n_layers, n_quads); for (i = 0; i < n_quads; i++) _cogl_journal_dump_quad_vertices (data + byte_stride * 2 * i, n_layers); } static void batch_and_call (CoglJournalEntry *entries, int n_entries, CoglJournalBatchTest can_batch_callback, CoglJournalBatchCallback batch_callback, void *data) { int i; int batch_len = 1; CoglJournalEntry *batch_start = entries; if (n_entries < 1) return; for (i = 1; i < n_entries; i++) { CoglJournalEntry *entry0 = &entries[i - 1]; CoglJournalEntry *entry1 = entry0 + 1; if (can_batch_callback (entry0, entry1)) { batch_len++; continue; } batch_callback (batch_start, batch_len, data); batch_start = entry1; batch_len = 1; } /* The last batch... */ batch_callback (batch_start, batch_len, data); } static void _cogl_journal_flush_modelview_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CoglContext *ctx = state->ctx; CoglFramebuffer *framebuffer = state->journal->framebuffer; CoglAttribute **attributes; CoglDrawFlags draw_flags = (COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH); COGL_STATIC_TIMER (time_flush_modelview_and_entries, "flush: pipeline+entries", /* parent */ "flush: modelview+entries", "The time spent flushing modelview + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_modelview_and_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: modelview batch len = %d\n", batch_len); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) _cogl_context_set_current_modelview_entry (ctx, batch_start->modelview_entry); attributes = (CoglAttribute **)state->attributes->data; if (!_cogl_pipeline_get_real_blend_enabled (state->pipeline)) draw_flags |= COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE; if (batch_len > 1) { CoglVerticesMode mode = COGL_VERTICES_MODE_TRIANGLES; int first_vertex = state->current_vertex * 6 / 4; _cogl_framebuffer_draw_indexed_attributes (framebuffer, state->pipeline, mode, first_vertex, batch_len * 6, state->indices, attributes, state->attributes->len, draw_flags); } else { _cogl_framebuffer_draw_attributes (framebuffer, state->pipeline, COGL_VERTICES_MODE_TRIANGLE_FAN, state->current_vertex, 4, attributes, state->attributes->len, draw_flags); } /* DEBUGGING CODE XXX: This path will cause all rectangles to be * drawn with a coloured outline. Each batch will be rendered with * the same color. This may e.g. help with debugging texture slicing * issues, visually seeing what is batched and debugging blending * issues, plus it looks quite cool. */ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES))) { static CoglPipeline *outline = NULL; uint8_t color_intensity; int i; CoglAttribute *loop_attributes[1]; if (outline == NULL) outline = cogl_pipeline_new (ctx); /* The least significant three bits represent the three components so that the order of colours goes red, green, yellow, blue, magenta, cyan. Black and white are skipped. The next two bits give four scales of intensity for those colours in the order 0xff, 0xcc, 0x99, and 0x66. This gives a total of 24 colours. If there are more than 24 batches on the stage then it will wrap around */ color_intensity = 0xff - 0x33 * (ctx->journal_rectangles_color >> 3); cogl_pipeline_set_color4ub (outline, (ctx->journal_rectangles_color & 1) ? color_intensity : 0, (ctx->journal_rectangles_color & 2) ? color_intensity : 0, (ctx->journal_rectangles_color & 4) ? color_intensity : 0, 0xff); loop_attributes[0] = attributes[0]; /* we just want the position */ for (i = 0; i < batch_len; i++) _cogl_framebuffer_draw_attributes (framebuffer, outline, COGL_VERTICES_MODE_LINE_LOOP, 4 * i + state->current_vertex, 4, loop_attributes, 1, draw_flags); /* Go to the next color */ do ctx->journal_rectangles_color = ((ctx->journal_rectangles_color + 1) & ((1 << 5) - 1)); /* We don't want to use black or white */ while ((ctx->journal_rectangles_color & 0x07) == 0 || (ctx->journal_rectangles_color & 0x07) == 0x07); } state->current_vertex += (4 * batch_len); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_modelview_and_entries); } static gboolean compare_entry_modelviews (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { /* Batch together quads with the same model view matrix */ return entry0->modelview_entry == entry1->modelview_entry; } /* At this point we have a run of quads that we know have compatible * pipelines, but they may not all have the same modelview matrix */ static void _cogl_journal_flush_pipeline_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; COGL_STATIC_TIMER (time_flush_pipeline_entries, "flush: texcoords+pipeline+entries", /* parent */ "flush: pipeline+entries", "The time spent flushing pipeline + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_pipeline_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: pipeline batch len = %d\n", batch_len); state->pipeline = batch_start->pipeline; /* If we haven't transformed the quads in software then we need to also break * up batches according to changes in the modelview matrix... */ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) { batch_and_call (batch_start, batch_len, compare_entry_modelviews, _cogl_journal_flush_modelview_and_entries, data); } else _cogl_journal_flush_modelview_and_entries (batch_start, batch_len, data); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_pipeline_entries); } static gboolean compare_entry_pipelines (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { /* batch rectangles using compatible pipelines */ if (_cogl_pipeline_equal (entry0->pipeline, entry1->pipeline, (COGL_PIPELINE_STATE_ALL & ~COGL_PIPELINE_STATE_COLOR), COGL_PIPELINE_LAYER_STATE_ALL, 0)) return TRUE; else return FALSE; } typedef struct _CreateAttributeState { int current; CoglJournalFlushState *flush_state; } CreateAttributeState; static gboolean create_attribute_cb (CoglPipeline *pipeline, int layer_number, void *user_data) { CreateAttributeState *state = user_data; CoglJournalFlushState *flush_state = state->flush_state; CoglAttribute **attribute_entry = &g_array_index (flush_state->attributes, CoglAttribute *, state->current + 2); const char *names[] = { "cogl_tex_coord0_in", "cogl_tex_coord1_in", "cogl_tex_coord2_in", "cogl_tex_coord3_in", "cogl_tex_coord4_in", "cogl_tex_coord5_in", "cogl_tex_coord6_in", "cogl_tex_coord7_in" }; char *name; /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: * 2 or 3 floats per position (3 when doing software transforms) * 4 RGBA bytes, * 2 floats per tex coord * n_layers * (though n_layers may be padded; see definition of * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) */ name = layer_number < 8 ? (char *)names[layer_number] : g_strdup_printf ("cogl_tex_coord%d_in", layer_number); /* XXX: it may be worth having some form of static initializer for * attributes... */ *attribute_entry = cogl_attribute_new (flush_state->attribute_buffer, name, flush_state->stride, flush_state->array_offset + (POS_STRIDE + COLOR_STRIDE) * 4 + TEX_STRIDE * 4 * state->current, 2, COGL_ATTRIBUTE_TYPE_FLOAT); if (layer_number >= 8) g_free (name); state->current++; return TRUE; } /* Since the stride may not reflect the number of texture layers in use * (due to padding) we deal with texture coordinate offsets separately * from vertex and color offsets... */ static void _cogl_journal_flush_texcoord_vbo_offsets_and_entries ( CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CreateAttributeState create_attrib_state; int i; COGL_STATIC_TIMER (time_flush_texcoord_pipeline_entries, "flush: vbo+texcoords+pipeline+entries", /* parent */ "flush: texcoords+pipeline+entries", "The time spent flushing texcoord offsets + pipeline " "+ entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_texcoord_pipeline_entries); /* NB: attributes 0 and 1 are position and color */ for (i = 2; i < state->attributes->len; i++) cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i)); g_array_set_size (state->attributes, batch_start->n_layers + 2); create_attrib_state.current = 0; create_attrib_state.flush_state = state; cogl_pipeline_foreach_layer (batch_start->pipeline, create_attribute_cb, &create_attrib_state); batch_and_call (batch_start, batch_len, compare_entry_pipelines, _cogl_journal_flush_pipeline_and_entries, data); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_texcoord_pipeline_entries); } static gboolean compare_entry_layer_numbers (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { if (_cogl_pipeline_layer_numbers_equal (entry0->pipeline, entry1->pipeline)) return TRUE; else return FALSE; } /* At this point we know the stride has changed from the previous batch * of journal entries */ static void _cogl_journal_flush_vbo_offsets_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CoglContext *ctx = state->journal->framebuffer->context; size_t stride; int i; CoglAttribute **attribute_entry; COGL_STATIC_TIMER (time_flush_vbo_texcoord_pipeline_entries, "flush: clip+vbo+texcoords+pipeline+entries", /* parent */ "flush: vbo+texcoords+pipeline+entries", "The time spent flushing vbo + texcoord offsets + " "pipeline + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_vbo_texcoord_pipeline_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: vbo offset batch len = %d\n", batch_len); /* XXX NB: * Our journal's vertex data is arranged as follows: * 4 vertices per quad: * 2 or 3 GLfloats per position (3 when doing software transforms) * 4 RGBA GLubytes, * 2 GLfloats per tex coord * n_layers * (though n_layers may be padded; see definition of * GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS for details) */ stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (batch_start->n_layers); stride *= sizeof (float); state->stride = stride; for (i = 0; i < state->attributes->len; i++) cogl_object_unref (g_array_index (state->attributes, CoglAttribute *, i)); g_array_set_size (state->attributes, 2); attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 0); *attribute_entry = cogl_attribute_new (state->attribute_buffer, "cogl_position_in", stride, state->array_offset, N_POS_COMPONENTS, COGL_ATTRIBUTE_TYPE_FLOAT); attribute_entry = &g_array_index (state->attributes, CoglAttribute *, 1); *attribute_entry = cogl_attribute_new (state->attribute_buffer, "cogl_color_in", stride, state->array_offset + (POS_STRIDE * 4), 4, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); state->indices = cogl_get_rectangle_indices (ctx, batch_len); /* We only create new Attributes when the stride within the * AttributeBuffer changes. (due to a change in the number of pipeline * layers) While the stride remains constant we walk forward through * the above AttributeBuffer using a vertex offset passed to * cogl_draw_attributes */ state->current_vertex = 0; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL)) && cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ)) { uint8_t *verts; /* Mapping a buffer for read is probably a really bad thing to do but this will only happen during debugging so it probably doesn't matter */ verts = ((uint8_t *)_cogl_buffer_map (COGL_BUFFER (state->attribute_buffer), COGL_BUFFER_ACCESS_READ, 0, NULL) + state->array_offset); _cogl_journal_dump_quad_batch (verts, batch_start->n_layers, batch_len); cogl_buffer_unmap (COGL_BUFFER (state->attribute_buffer)); } batch_and_call (batch_start, batch_len, compare_entry_layer_numbers, _cogl_journal_flush_texcoord_vbo_offsets_and_entries, data); /* progress forward through the VBO containing all our vertices */ state->array_offset += (stride * 4 * batch_len); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL))) g_print ("new vbo offset = %lu\n", (unsigned long)state->array_offset); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_vbo_texcoord_pipeline_entries); } static gboolean compare_entry_strides (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { /* Currently the only thing that affects the stride for our vertex arrays * is the number of pipeline layers. We need to update our VBO offsets * whenever the stride changes. */ /* TODO: We should be padding the n_layers == 1 case as if it were * n_layers == 2 so we can reduce the need to split batches. */ if (entry0->n_layers == entry1->n_layers || (entry0->n_layers <= MIN_LAYER_PADING && entry1->n_layers <= MIN_LAYER_PADING)) return TRUE; else return FALSE; } /* At this point we know the batch has a unique clip stack */ static void _cogl_journal_flush_clip_stacks_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CoglFramebuffer *framebuffer = state->journal->framebuffer; CoglContext *ctx = framebuffer->context; CoglMatrixStack *projection_stack; COGL_STATIC_TIMER (time_flush_clip_stack_pipeline_entries, "Journal Flush", /* parent */ "flush: clip+vbo+texcoords+pipeline+entries", "The time spent flushing clip + vbo + texcoord offsets + " "pipeline + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_clip_stack_pipeline_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: clip stack batch len = %d\n", batch_len); _cogl_clip_stack_flush (batch_start->clip_stack, framebuffer); /* XXX: Because we are manually flushing clip state here we need to * make sure that the clip state gets updated the next time we flush * framebuffer state by marking the current framebuffer's clip state * as changed. */ ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; /* If we have transformed all our quads at log time then we ensure * no further model transform is applied by loading the identity * matrix here. We need to do this after flushing the clip stack * because the clip stack flushing code can modify the current * modelview matrix entry */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM)))) _cogl_context_set_current_modelview_entry (ctx, &ctx->identity_entry); /* Setting up the clip state can sometimes also update the current * projection matrix entry so we should update it again. This will have * no affect if the clip code didn't modify the projection */ projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); _cogl_context_set_current_projection_entry (ctx, projection_stack->last_entry); batch_and_call (batch_start, batch_len, compare_entry_strides, _cogl_journal_flush_vbo_offsets_and_entries, /* callback */ data); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_clip_stack_pipeline_entries); } typedef struct { float x_1, y_1; float x_2, y_2; } ClipBounds; static gboolean can_software_clip_entry (CoglJournalEntry *journal_entry, CoglJournalEntry *prev_journal_entry, CoglClipStack *clip_stack, ClipBounds *clip_bounds_out) { CoglPipeline *pipeline = journal_entry->pipeline; CoglClipStack *clip_entry; int layer_num; clip_bounds_out->x_1 = -G_MAXFLOAT; clip_bounds_out->y_1 = -G_MAXFLOAT; clip_bounds_out->x_2 = G_MAXFLOAT; clip_bounds_out->y_2 = G_MAXFLOAT; /* Check the pipeline is usable. We can short-cut here for entries using the same pipeline as the previous entry */ if (prev_journal_entry == NULL || pipeline != prev_journal_entry->pipeline) { /* If the pipeline has a user program then we can't reliably modify the texture coordinates */ if (cogl_pipeline_get_user_program (pipeline)) return FALSE; /* If any of the pipeline layers have a texture matrix then we can't reliably modify the texture coordinates */ for (layer_num = cogl_pipeline_get_n_layers (pipeline) - 1; layer_num >= 0; layer_num--) if (_cogl_pipeline_layer_has_user_matrix (pipeline, layer_num)) return FALSE; } /* Now we need to verify that each clip entry's matrix is just a translation of the journal entry's modelview matrix. We can also work out the bounds of the clip in modelview space using this translation */ for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent) { float rect_x1, rect_y1, rect_x2, rect_y2; CoglClipStackRect *clip_rect; float tx, ty, tz; CoglMatrixEntry *modelview_entry; clip_rect = (CoglClipStackRect *) clip_entry; modelview_entry = journal_entry->modelview_entry; if (!cogl_matrix_entry_calculate_translation (clip_rect->matrix_entry, modelview_entry, &tx, &ty, &tz)) return FALSE; if (clip_rect->x0 < clip_rect->x1) { rect_x1 = clip_rect->x0; rect_x2 = clip_rect->x1; } else { rect_x1 = clip_rect->x1; rect_x2 = clip_rect->x0; } if (clip_rect->y0 < clip_rect->y1) { rect_y1 = clip_rect->y0; rect_y2 = clip_rect->y1; } else { rect_y1 = clip_rect->y1; rect_y2 = clip_rect->y0; } clip_bounds_out->x_1 = MAX (clip_bounds_out->x_1, rect_x1 - tx); clip_bounds_out->y_1 = MAX (clip_bounds_out->y_1, rect_y1 - ty); clip_bounds_out->x_2 = MIN (clip_bounds_out->x_2, rect_x2 - tx); clip_bounds_out->y_2 = MIN (clip_bounds_out->y_2, rect_y2 - ty); } if (clip_bounds_out->x_2 <= clip_bounds_out->x_1 || clip_bounds_out->y_2 <= clip_bounds_out->y_1) memset (clip_bounds_out, 0, sizeof (ClipBounds)); return TRUE; } static void software_clip_entry (CoglJournalEntry *journal_entry, float *verts, ClipBounds *clip_bounds) { size_t stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (journal_entry->n_layers); float rx1, ry1, rx2, ry2; float vx1, vy1, vx2, vy2; int layer_num; /* Remove the clip on the entry */ _cogl_clip_stack_unref (journal_entry->clip_stack); journal_entry->clip_stack = NULL; vx1 = verts[0]; vy1 = verts[1]; vx2 = verts[stride]; vy2 = verts[stride + 1]; if (vx1 < vx2) { rx1 = vx1; rx2 = vx2; } else { rx1 = vx2; rx2 = vx1; } if (vy1 < vy2) { ry1 = vy1; ry2 = vy2; } else { ry1 = vy2; ry2 = vy1; } rx1 = CLAMP (rx1, clip_bounds->x_1, clip_bounds->x_2); ry1 = CLAMP (ry1, clip_bounds->y_1, clip_bounds->y_2); rx2 = CLAMP (rx2, clip_bounds->x_1, clip_bounds->x_2); ry2 = CLAMP (ry2, clip_bounds->y_1, clip_bounds->y_2); /* Check if the rectangle intersects the clip at all */ if (rx1 == rx2 || ry1 == ry2) /* Will set all of the vertex data to 0 in the hope that this will create a degenerate rectangle and the GL driver will be able to clip it quickly */ memset (verts, 0, sizeof (float) * stride * 2); else { if (vx1 > vx2) { float t = rx1; rx1 = rx2; rx2 = t; } if (vy1 > vy2) { float t = ry1; ry1 = ry2; ry2 = t; } verts[0] = rx1; verts[1] = ry1; verts[stride] = rx2; verts[stride + 1] = ry2; /* Convert the rectangle coordinates to a fraction of the original rectangle */ rx1 = (rx1 - vx1) / (vx2 - vx1); ry1 = (ry1 - vy1) / (vy2 - vy1); rx2 = (rx2 - vx1) / (vx2 - vx1); ry2 = (ry2 - vy1) / (vy2 - vy1); for (layer_num = 0; layer_num < journal_entry->n_layers; layer_num++) { float *t = verts + 2 + 2 * layer_num; float tx1 = t[0], ty1 = t[1]; float tx2 = t[stride], ty2 = t[stride + 1]; t[0] = rx1 * (tx2 - tx1) + tx1; t[1] = ry1 * (ty2 - ty1) + ty1; t[stride] = rx2 * (tx2 - tx1) + tx1; t[stride + 1] = ry2 * (ty2 - ty1) + ty1; } } } static void maybe_software_clip_entries (CoglJournalEntry *batch_start, int batch_len, CoglJournalFlushState *state) { CoglContext *ctx; CoglJournal *journal; CoglClipStack *clip_stack, *clip_entry; int entry_num; /* This tries to find cases where the entry is logged with a clip but it would be faster to modify the vertex and texture coordinates rather than flush the clip so that it can batch better */ /* If the batch is reasonably long then it's worthwhile programming the GPU to do the clip */ if (batch_len >= COGL_JOURNAL_HARDWARE_CLIP_THRESHOLD) return; clip_stack = batch_start->clip_stack; if (clip_stack == NULL) return; /* Verify that all of the clip stack entries are a simple rectangle clip */ for (clip_entry = clip_stack; clip_entry; clip_entry = clip_entry->parent) if (clip_entry->type != COGL_CLIP_STACK_RECT) return; ctx = state->ctx; journal = state->journal; /* This scratch buffer is used to store the translation for each entry in the journal. We store it in a separate buffer because it's expensive to calculate but at this point we still don't know whether we can clip all of the entries so we don't want to do the rest of the dependant calculations until we're sure we can. */ if (ctx->journal_clip_bounds == NULL) ctx->journal_clip_bounds = g_array_new (FALSE, FALSE, sizeof (ClipBounds)); g_array_set_size (ctx->journal_clip_bounds, batch_len); for (entry_num = 0; entry_num < batch_len; entry_num++) { CoglJournalEntry *journal_entry = batch_start + entry_num; CoglJournalEntry *prev_journal_entry = entry_num ? batch_start + (entry_num - 1) : NULL; ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, ClipBounds, entry_num); if (!can_software_clip_entry (journal_entry, prev_journal_entry, clip_stack, clip_bounds)) return; } /* If we make it here then we know we can software clip the entire batch */ COGL_NOTE (CLIPPING, "Software clipping a batch of length %i", batch_len); for (entry_num = 0; entry_num < batch_len; entry_num++) { CoglJournalEntry *journal_entry = batch_start + entry_num; float *verts = &g_array_index (journal->vertices, float, journal_entry->array_offset + 1); ClipBounds *clip_bounds = &g_array_index (ctx->journal_clip_bounds, ClipBounds, entry_num); software_clip_entry (journal_entry, verts, clip_bounds); } return; } static void _cogl_journal_maybe_software_clip_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; COGL_STATIC_TIMER (time_check_software_clip, "Journal Flush", /* parent */ "flush: software clipping", "Time spent software clipping", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_check_software_clip); maybe_software_clip_entries (batch_start, batch_len, state); COGL_TIMER_STOP (_cogl_uprof_context, time_check_software_clip); } static gboolean compare_entry_clip_stacks (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { return entry0->clip_stack == entry1->clip_stack; } static void _cogl_journal_flush_dither_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CoglFramebuffer *framebuffer = state->journal->framebuffer; CoglContext *ctx = framebuffer->context; COGL_STATIC_TIMER (time_flush_dither_and_entries, "Journal Flush", /* parent */ "flush: viewport+dither+clip+vbo+texcoords+pipeline+entries", "The time spent flushing viewport + dither + clip + vbo + " "texcoord offsets + pipeline + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_dither_and_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: dither batch len = %d\n", batch_len); cogl_framebuffer_set_dither_enabled (framebuffer, batch_start->dither_enabled); ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_DITHER; _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_DITHER); batch_and_call (batch_start, batch_len, compare_entry_clip_stacks, _cogl_journal_flush_clip_stacks_and_entries, state); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_dither_and_entries); } static gboolean compare_entry_dither_states (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { return entry0->dither_enabled == entry1->dither_enabled; } static void _cogl_journal_flush_viewport_and_entries (CoglJournalEntry *batch_start, int batch_len, void *data) { CoglJournalFlushState *state = data; CoglFramebuffer *framebuffer = state->journal->framebuffer; CoglContext *ctx = framebuffer->context; float current_viewport[4]; COGL_STATIC_TIMER (time_flush_viewport_and_entries, "Journal Flush", /* parent */ "flush: viewport+clip+vbo+texcoords+pipeline+entries", "The time spent flushing viewport + clip + vbo + texcoord offsets + " "pipeline + entries", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, time_flush_viewport_and_entries); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: viewport batch len = %d\n", batch_len); ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_VIEWPORT; cogl_framebuffer_get_viewport4fv (framebuffer, current_viewport); cogl_framebuffer_set_viewport4fv (framebuffer, batch_start->viewport); _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_VIEWPORT); batch_and_call (batch_start, batch_len, compare_entry_dither_states, _cogl_journal_flush_dither_and_entries, state); if (memcmp (batch_start->viewport, current_viewport, sizeof (float) * 4) != 0) cogl_framebuffer_set_viewport4fv (framebuffer, current_viewport); COGL_TIMER_STOP (_cogl_uprof_context, time_flush_viewport_and_entries); } static gboolean compare_entry_viewports (CoglJournalEntry *entry0, CoglJournalEntry *entry1) { return memcmp (entry0->viewport, entry1->viewport, sizeof (float) * 4) == 0; } /* Gets a new vertex array from the pool. A reference is taken on the array so it can be treated as if it was just newly allocated */ static CoglAttributeBuffer * create_attribute_buffer (CoglJournal *journal, size_t n_bytes) { CoglAttributeBuffer *vbo; CoglContext *ctx = journal->framebuffer->context; vbo = journal->vbo_pool[journal->next_vbo_in_pool]; if (vbo == NULL) { vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes); journal->vbo_pool[journal->next_vbo_in_pool] = vbo; } else if (cogl_buffer_get_size (COGL_BUFFER (vbo)) < n_bytes) { /* If the buffer is too small then we'll just recreate it */ cogl_object_unref (vbo); vbo = cogl_attribute_buffer_new_with_size (ctx, n_bytes); journal->vbo_pool[journal->next_vbo_in_pool] = vbo; } journal->next_vbo_in_pool = ((journal->next_vbo_in_pool + 1) % COGL_JOURNAL_VBO_POOL_SIZE); return cogl_object_ref (vbo); } static CoglAttributeBuffer * upload_vertices (CoglJournal *journal, const CoglJournalEntry *entries, int n_entries, size_t needed_vbo_len, GArray *vertices) { CoglAttributeBuffer *attribute_buffer; CoglBuffer *buffer; const float *vin; float *vout; int entry_num; int i; CoglMatrixEntry *last_modelview_entry = NULL; CoglMatrix modelview; g_assert (needed_vbo_len); attribute_buffer = create_attribute_buffer (journal, needed_vbo_len * 4); buffer = COGL_BUFFER (attribute_buffer); cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); vout = _cogl_buffer_map_range_for_fill_or_fallback (buffer, 0, /* offset */ needed_vbo_len * 4); vin = &g_array_index (vertices, float, 0); /* Expand the number of vertices from 2 to 4 while uploading */ for (entry_num = 0; entry_num < n_entries; entry_num++) { const CoglJournalEntry *entry = entries + entry_num; size_t vb_stride = GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (entry->n_layers); size_t array_stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers); /* Copy the color to all four of the vertices */ for (i = 0; i < 4; i++) memcpy (vout + vb_stride * i + POS_STRIDE, vin, 4); vin++; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_TRANSFORM))) { vout[vb_stride * 0] = vin[0]; vout[vb_stride * 0 + 1] = vin[1]; vout[vb_stride * 1] = vin[0]; vout[vb_stride * 1 + 1] = vin[array_stride + 1]; vout[vb_stride * 2] = vin[array_stride]; vout[vb_stride * 2 + 1] = vin[array_stride + 1]; vout[vb_stride * 3] = vin[array_stride]; vout[vb_stride * 3 + 1] = vin[1]; } else { float v[8]; v[0] = vin[0]; v[1] = vin[1]; v[2] = vin[0]; v[3] = vin[array_stride + 1]; v[4] = vin[array_stride]; v[5] = vin[array_stride + 1]; v[6] = vin[array_stride]; v[7] = vin[1]; if (entry->modelview_entry != last_modelview_entry) cogl_matrix_entry_get (entry->modelview_entry, &modelview); cogl_matrix_transform_points (&modelview, 2, /* n_components */ sizeof (float) * 2, /* stride_in */ v, /* points_in */ /* strideout */ vb_stride * sizeof (float), vout, /* points_out */ 4 /* n_points */); } for (i = 0; i < entry->n_layers; i++) { const float *tin = vin + 2; float *tout = vout + POS_STRIDE + COLOR_STRIDE; tout[vb_stride * 0 + i * 2] = tin[i * 2]; tout[vb_stride * 0 + 1 + i * 2] = tin[i * 2 + 1]; tout[vb_stride * 1 + i * 2] = tin[i * 2]; tout[vb_stride * 1 + 1 + i * 2] = tin[array_stride + i * 2 + 1]; tout[vb_stride * 2 + i * 2] = tin[array_stride + i * 2]; tout[vb_stride * 2 + 1 + i * 2] = tin[array_stride + i * 2 + 1]; tout[vb_stride * 3 + i * 2] = tin[array_stride + i * 2]; tout[vb_stride * 3 + 1 + i * 2] = tin[i * 2 + 1]; } vin += array_stride * 2; vout += vb_stride * 4; } _cogl_buffer_unmap_for_fill_or_fallback (buffer); return attribute_buffer; } void _cogl_journal_discard (CoglJournal *journal) { int i; if (journal->entries->len <= 0) return; for (i = 0; i < journal->entries->len; i++) { CoglJournalEntry *entry = &g_array_index (journal->entries, CoglJournalEntry, i); _cogl_pipeline_journal_unref (entry->pipeline); cogl_matrix_entry_unref (entry->modelview_entry); _cogl_clip_stack_unref (entry->clip_stack); } g_array_set_size (journal->entries, 0); g_array_set_size (journal->vertices, 0); journal->needed_vbo_len = 0; journal->fast_read_pixel_count = 0; /* The journal only holds a reference to the framebuffer while the journal is not empty */ cogl_object_unref (journal->framebuffer); } /* Note: A return value of FALSE doesn't mean 'no' it means * 'unknown' */ gboolean _cogl_journal_all_entries_within_bounds (CoglJournal *journal, float clip_x0, float clip_y0, float clip_x1, float clip_y1) { CoglJournalEntry *entry = (CoglJournalEntry *)journal->entries->data; CoglClipStack *clip_entry; CoglClipStack *reference = NULL; int bounds_x0; int bounds_y0; int bounds_x1; int bounds_y1; int i; if (journal->entries->len == 0) return TRUE; /* Find the shortest clip_stack ancestry that leaves us in the * required bounds */ for (clip_entry = entry->clip_stack; clip_entry; clip_entry = clip_entry->parent) { _cogl_clip_stack_get_bounds (clip_entry, &bounds_x0, &bounds_y0, &bounds_x1, &bounds_y1); if (bounds_x0 >= clip_x0 && bounds_y0 >= clip_y0 && bounds_x1 <= clip_x1 && bounds_y1 <= clip_y1) reference = clip_entry; else break; } if (!reference) return FALSE; /* For the remaining journal entries we will only verify they share * 'reference' as an ancestor in their clip stack since that's * enough to know that they would be within the required bounds. */ for (i = 1; i < journal->entries->len; i++) { gboolean found_reference = FALSE; entry = &g_array_index (journal->entries, CoglJournalEntry, i); for (clip_entry = entry->clip_stack; clip_entry; clip_entry = clip_entry->parent) { if (clip_entry == reference) { found_reference = TRUE; break; } } if (!found_reference) return FALSE; } return TRUE; } static void post_fences (CoglJournal *journal) { CoglFenceClosure *fence, *tmp; _cogl_list_for_each_safe (fence, tmp, &journal->pending_fences, link) { _cogl_list_remove (&fence->link); _cogl_fence_submit (fence); } } /* XXX NB: When _cogl_journal_flush() returns all state relating * to pipelines, all glEnable flags and current matrix state * is undefined. */ void _cogl_journal_flush (CoglJournal *journal) { CoglFramebuffer *framebuffer; CoglContext *ctx; CoglJournalFlushState state; int i; COGL_STATIC_TIMER (flush_timer, "Mainloop", /* parent */ "Journal Flush", "The time spent flushing the Cogl journal", 0 /* no application private data */); COGL_STATIC_TIMER (discard_timer, "Journal Flush", /* parent */ "flush: discard", "The time spent discarding the Cogl journal after a flush", 0 /* no application private data */); if (journal->entries->len == 0) { post_fences (journal); return; } framebuffer = journal->framebuffer; ctx = framebuffer->context; /* The entries in this journal may depend on images in other * framebuffers which may require that we flush the journals * associated with those framebuffers before we can flush * this journal... */ _cogl_framebuffer_flush_dependency_journals (framebuffer); /* Note: we start the timer after flushing dependency journals so * that the timer isn't started recursively. */ COGL_TIMER_START (_cogl_uprof_context, flush_timer); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_BATCHING))) g_print ("BATCHING: journal len = %d\n", journal->entries->len); /* NB: the journal deals with flushing the viewport, the modelview * stack and clip state manually */ _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_ALL & ~(COGL_FRAMEBUFFER_STATE_DITHER | COGL_FRAMEBUFFER_STATE_VIEWPORT | COGL_FRAMEBUFFER_STATE_MODELVIEW | COGL_FRAMEBUFFER_STATE_CLIP)); /* We need to mark the current modelview state of the framebuffer as * dirty because we are going to manually replace it */ ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; state.ctx = ctx; state.journal = journal; state.attributes = ctx->journal_flush_attributes_array; if (G_UNLIKELY ((COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SOFTWARE_CLIP)) == 0)) { /* We do an initial walk of the journal to analyse the clip stack batches to see if we can do software clipping. We do this as a separate walk of the journal because we can modify entries and this may end up joining together clip stack batches in the next iteration. */ batch_and_call ((CoglJournalEntry *)journal->entries->data, /* first entry */ journal->entries->len, /* max number of entries to consider */ compare_entry_clip_stacks, _cogl_journal_maybe_software_clip_entries, /* callback */ &state); /* data */ } /* We upload the vertices after the clip stack pass in case it modifies the entries */ state.attribute_buffer = upload_vertices (journal, &g_array_index (journal->entries, CoglJournalEntry, 0), journal->entries->len, journal->needed_vbo_len, journal->vertices); state.array_offset = 0; /* batch_and_call() batches a list of journal entries according to some * given criteria and calls a callback once for each determined batch. * * The process of flushing the journal is staggered to reduce the amount * of driver/GPU state changes necessary: * 1) We split the entries according to the clip state. * 2) We split the entries according to the stride of the vertices: * Each time the stride of our vertex data changes we need to call * gl{Vertex,Color}Pointer to inform GL of new VBO offsets. * Currently the only thing that affects the stride of our vertex data * is the number of pipeline layers. * 3) We split the entries explicitly by the number of pipeline layers: * We pad our vertex data when the number of layers is < 2 so that we * can minimize changes in stride. * 4) We then split according to compatible Cogl pipelines: * This is where we flush pipeline state * 5) Finally we split according to modelview matrix changes: * This is when we finally tell GL to draw something. * Note: Splitting by modelview changes is skipped when are doing the * vertex transformation in software at log time. */ batch_and_call ((CoglJournalEntry *)journal->entries->data, journal->entries->len, compare_entry_viewports, _cogl_journal_flush_viewport_and_entries, &state); for (i = 0; i < state.attributes->len; i++) cogl_object_unref (g_array_index (state.attributes, CoglAttribute *, i)); g_array_set_size (state.attributes, 0); cogl_object_unref (state.attribute_buffer); COGL_TIMER_START (_cogl_uprof_context, discard_timer); _cogl_journal_discard (journal); COGL_TIMER_STOP (_cogl_uprof_context, discard_timer); post_fences (journal); COGL_TIMER_STOP (_cogl_uprof_context, flush_timer); } static gboolean add_framebuffer_deps_cb (CoglPipelineLayer *layer, void *user_data) { CoglFramebuffer *framebuffer = user_data; CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer); const GList *l; if (!texture) return TRUE; for (l = _cogl_texture_get_associated_framebuffers (texture); l; l = l->next) _cogl_framebuffer_add_dependency (framebuffer, l->data); return TRUE; } void _cogl_journal_log_quad (CoglJournal *journal, const float *position, CoglPipeline *pipeline, int n_layers, CoglTexture *layer0_override_texture, const float *tex_coords, unsigned int tex_coords_len) { CoglFramebuffer *framebuffer = journal->framebuffer; size_t stride; int next_vert; float *v; int i; int next_entry; uint32_t disable_layers; CoglJournalEntry *entry; CoglPipeline *final_pipeline; CoglClipStack *clip_stack; CoglPipelineFlushOptions flush_options; CoglMatrixStack *modelview_stack; COGL_STATIC_TIMER (log_timer, "Mainloop", /* parent */ "Journal Log", "The time spent logging in the Cogl journal", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, log_timer); /* If the framebuffer was previously empty then we'll take a reference to the current framebuffer. This reference will be removed when the journal is flushed */ if (journal->vertices->len == 0) cogl_object_ref (framebuffer); /* The vertex data is logged into a separate array. The data needs to be copied into a vertex array before it's given to GL so we only store two vertices per quad and expand it to four while uploading. */ /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS for details * about how we pack our vertex data */ stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (n_layers); next_vert = journal->vertices->len; g_array_set_size (journal->vertices, next_vert + 2 * stride + 1); v = &g_array_index (journal->vertices, float, next_vert); /* We calculate the needed size of the vbo as we go because it depends on the number of layers in each entry and it's not easy calculate based on the length of the logged vertices array */ journal->needed_vbo_len += GET_JOURNAL_VB_STRIDE_FOR_N_LAYERS (n_layers) * 4; /* XXX: All the jumping around to fill in this strided buffer doesn't * seem ideal. */ /* FIXME: This is a hacky optimization, since it will break if we * change the definition of CoglColor: */ _cogl_pipeline_get_colorubv (pipeline, (uint8_t *) v); v++; memcpy (v, position, sizeof (float) * 2); memcpy (v + stride, position + 2, sizeof (float) * 2); for (i = 0; i < n_layers; i++) { /* XXX: See definition of GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS * for details about how we pack our vertex data */ GLfloat *t = v + 2 + i * 2; memcpy (t, tex_coords + i * 4, sizeof (float) * 2); memcpy (t + stride, tex_coords + i * 4 + 2, sizeof (float) * 2); } if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_JOURNAL))) { g_print ("Logged new quad:\n"); v = &g_array_index (journal->vertices, float, next_vert); _cogl_journal_dump_logged_quad ((uint8_t *)v, n_layers); } next_entry = journal->entries->len; g_array_set_size (journal->entries, next_entry + 1); entry = &g_array_index (journal->entries, CoglJournalEntry, next_entry); entry->n_layers = n_layers; entry->array_offset = next_vert; final_pipeline = pipeline; flush_options.flags = 0; if (G_UNLIKELY (cogl_pipeline_get_n_layers (pipeline) != n_layers)) { disable_layers = (1 << n_layers) - 1; disable_layers = ~disable_layers; flush_options.disable_layers = disable_layers; flush_options.flags |= COGL_PIPELINE_FLUSH_DISABLE_MASK; } if (G_UNLIKELY (layer0_override_texture)) { flush_options.flags |= COGL_PIPELINE_FLUSH_LAYER0_OVERRIDE; flush_options.layer0_override_texture = layer0_override_texture; } if (G_UNLIKELY (flush_options.flags)) { final_pipeline = cogl_pipeline_copy (pipeline); _cogl_pipeline_apply_overrides (final_pipeline, &flush_options); } entry->pipeline = _cogl_pipeline_journal_ref (final_pipeline); clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer); entry->clip_stack = _cogl_clip_stack_ref (clip_stack); entry->dither_enabled = cogl_framebuffer_get_dither_enabled (framebuffer); cogl_framebuffer_get_viewport4fv (framebuffer, entry->viewport); if (G_UNLIKELY (final_pipeline != pipeline)) cogl_object_unref (final_pipeline); modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); entry->modelview_entry = cogl_matrix_entry_ref (modelview_stack->last_entry); _cogl_pipeline_foreach_layer_internal (pipeline, add_framebuffer_deps_cb, framebuffer); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_BATCHING))) _cogl_journal_flush (journal); COGL_TIMER_STOP (_cogl_uprof_context, log_timer); } static void entry_to_screen_polygon (CoglFramebuffer *framebuffer, const CoglJournalEntry *entry, float *vertices, float *poly) { size_t array_stride = GET_JOURNAL_ARRAY_STRIDE_FOR_N_LAYERS (entry->n_layers); CoglMatrixStack *projection_stack; CoglMatrix projection; CoglMatrix modelview; int i; const float *viewport = entry->viewport; poly[0] = vertices[0]; poly[1] = vertices[1]; poly[2] = 0; poly[3] = 1; poly[4] = vertices[0]; poly[5] = vertices[array_stride + 1]; poly[6] = 0; poly[7] = 1; poly[8] = vertices[array_stride]; poly[9] = vertices[array_stride + 1]; poly[10] = 0; poly[11] = 1; poly[12] = vertices[array_stride]; poly[13] = vertices[1]; poly[14] = 0; poly[15] = 1; /* TODO: perhaps split the following out into a more generalized * _cogl_transform_points utility... */ cogl_matrix_entry_get (entry->modelview_entry, &modelview); cogl_matrix_transform_points (&modelview, 2, /* n_components */ sizeof (float) * 4, /* stride_in */ poly, /* points_in */ /* strideout */ sizeof (float) * 4, poly, /* points_out */ 4 /* n_points */); projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); cogl_matrix_stack_get (projection_stack, &projection); cogl_matrix_project_points (&projection, 3, /* n_components */ sizeof (float) * 4, /* stride_in */ poly, /* points_in */ /* strideout */ sizeof (float) * 4, poly, /* points_out */ 4 /* n_points */); /* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) * to Cogl window/framebuffer coordinates (ranging from 0 to buffer-size) with * (0,0) being top left. */ #define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) /* Note: for Y we first flip all coordinates around the X axis while in * normalized device coodinates */ #define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) /* Scale from normalized device coordinates (in range [-1,1]) to * window coordinates ranging [0,window-size] ... */ for (i = 0; i < 4; i++) { float w = poly[4 * i + 3]; /* Perform perspective division */ poly[4 * i] /= w; poly[4 * i + 1] /= w; /* Apply viewport transform */ poly[4 * i] = VIEWPORT_TRANSFORM_X (poly[4 * i], viewport[0], viewport[2]); poly[4 * i + 1] = VIEWPORT_TRANSFORM_Y (poly[4 * i + 1], viewport[1], viewport[3]); } #undef VIEWPORT_TRANSFORM_X #undef VIEWPORT_TRANSFORM_Y } static gboolean try_checking_point_hits_entry_after_clipping (CoglFramebuffer *framebuffer, CoglJournalEntry *entry, float *vertices, float x, float y, gboolean *hit) { gboolean can_software_clip = TRUE; gboolean needs_software_clip = FALSE; CoglClipStack *clip_entry; *hit = TRUE; /* Verify that all of the clip stack entries are simple rectangle * clips */ for (clip_entry = entry->clip_stack; clip_entry; clip_entry = clip_entry->parent) { if (x < clip_entry->bounds_x0 || x >= clip_entry->bounds_x1 || y < clip_entry->bounds_y0 || y >= clip_entry->bounds_y1) { *hit = FALSE; return TRUE; } if (clip_entry->type == COGL_CLIP_STACK_WINDOW_RECT) { /* XXX: technically we could still run the software clip in * this case because for our purposes we know this clip * can be ignored now, but [can_]sofware_clip_entry() doesn't * know this and will bail out. */ can_software_clip = FALSE; } else if (clip_entry->type == COGL_CLIP_STACK_RECT) { CoglClipStackRect *rect_entry = (CoglClipStackRect *)entry; if (rect_entry->can_be_scissor == FALSE) needs_software_clip = TRUE; /* If can_be_scissor is TRUE then we know it's screen * aligned and the hit test we did above has determined * that we are inside this clip. */ } else return FALSE; } if (needs_software_clip) { ClipBounds clip_bounds; float poly[16]; if (!can_software_clip) return FALSE; if (!can_software_clip_entry (entry, NULL, entry->clip_stack, &clip_bounds)) return FALSE; software_clip_entry (entry, vertices, &clip_bounds); entry_to_screen_polygon (framebuffer, entry, vertices, poly); *hit = _cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4); return TRUE; } return TRUE; } gboolean _cogl_journal_try_read_pixel (CoglJournal *journal, int x, int y, CoglBitmap *bitmap, gboolean *found_intersection) { CoglContext *ctx; CoglPixelFormat format; int i; /* XXX: this number has been plucked out of thin air, but the idea * is that if so many pixels are being read from the same un-changed * journal than we expect that it will be more efficient to fail * here so we end up flushing and rendering the journal so that * further reads can directly read from the framebuffer. There will * be a bit more lag to flush the render but if there are going to * continue being lots of arbitrary single pixel reads they will end * up faster in the end. */ if (journal->fast_read_pixel_count > 50) return FALSE; format = cogl_bitmap_get_format (bitmap); if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE && format != COGL_PIXEL_FORMAT_RGBA_8888) return FALSE; ctx = _cogl_bitmap_get_context (bitmap); *found_intersection = FALSE; /* NB: The most recently added journal entry is the last entry, and * assuming this is a simple scene only comprised of opaque coloured * rectangles with no special pipelines involved (e.g. enabling * depth testing) then we can assume painter's algorithm for the * entries and so our fast read-pixel just needs to walk backwards * through the journal entries trying to intersect each entry with * the given point of interest. */ for (i = journal->entries->len - 1; i >= 0; i--) { CoglJournalEntry *entry = &g_array_index (journal->entries, CoglJournalEntry, i); uint8_t *color = (uint8_t *)&g_array_index (journal->vertices, float, entry->array_offset); float *vertices = (float *)color + 1; float poly[16]; CoglFramebuffer *framebuffer = journal->framebuffer; uint8_t *pixel; GError *ignore_error; entry_to_screen_polygon (framebuffer, entry, vertices, poly); if (!_cogl_util_point_in_screen_poly (x, y, poly, sizeof (float) * 4, 4)) continue; if (entry->clip_stack) { gboolean hit; if (!try_checking_point_hits_entry_after_clipping (framebuffer, entry, vertices, x, y, &hit)) return FALSE; /* hit couldn't be determined */ if (!hit) continue; } *found_intersection = TRUE; /* If we find that the rectangle the point of interest * intersects has any state more complex than a constant opaque * color then we bail out. */ if (!_cogl_pipeline_equal (ctx->opaque_color_pipeline, entry->pipeline, (COGL_PIPELINE_STATE_ALL & ~COGL_PIPELINE_STATE_COLOR), COGL_PIPELINE_LAYER_STATE_ALL, 0)) return FALSE; /* we currently only care about cases where the premultiplied or * unpremultipled colors are equivalent... */ if (color[3] != 0xff) return FALSE; pixel = _cogl_bitmap_map (bitmap, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, &ignore_error); if (pixel == NULL) { g_error_free (ignore_error); return FALSE; } pixel[0] = color[0]; pixel[1] = color[1]; pixel[2] = color[2]; pixel[3] = color[3]; _cogl_bitmap_unmap (bitmap); goto success; } success: journal->fast_read_pixel_count++; return TRUE; } muffin-6.4.1/cogl/cogl/cogl-blit.h0000664000175000017500000000563314723361714015646 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_BLIT_H #define __COGL_BLIT_H #include #include "cogl-object-private.h" #include "cogl-texture.h" #include "cogl-framebuffer.h" /* This structures and functions are used when a series of blits needs to be performed between two textures. In this case there are multiple methods we can use, most of which involve transferring between an FBO bound to the texture. */ typedef struct _CoglBlitData CoglBlitData; typedef gboolean (* CoglBlitBeginFunc) (CoglBlitData *data); typedef void (* CoglBlitEndFunc) (CoglBlitData *data); typedef void (* CoglBlitFunc) (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height); typedef struct { const char *name; CoglBlitBeginFunc begin_func; CoglBlitFunc blit_func; CoglBlitEndFunc end_func; } CoglBlitMode; struct _CoglBlitData { CoglTexture *src_tex, *dst_tex; unsigned int src_width; unsigned int src_height; const CoglBlitMode *blit_mode; /* If we're not using an FBO then we g_malloc a buffer and copy the complete texture data in */ unsigned char *image_data; CoglPixelFormat format; int bpp; CoglFramebuffer *src_fb; CoglFramebuffer *dest_fb; CoglPipeline *pipeline; }; void _cogl_blit_begin (CoglBlitData *data, CoglTexture *dst_tex, CoglTexture *src_tex); void _cogl_blit (CoglBlitData *data, int src_x, int src_y, int dst_x, int dst_y, int width, int height); void _cogl_blit_end (CoglBlitData *data); #endif /* __COGL_BLIT_H */ muffin-6.4.1/cogl/cogl/cogl-xlib-private.h0000664000175000017500000000323514723361714017316 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_XLIB_PRIVATE_H #define __COGL_XLIB_PRIVATE_H #include typedef struct _CoglXlibTrapState CoglXlibTrapState; struct _CoglXlibTrapState { /* These values are intended to be internal to * _cogl_xlib_{un,}trap_errors but they need to be in the header so * that the struct can be allocated on the stack */ int (* old_error_handler) (Display *, XErrorEvent *); int trapped_error_code; CoglXlibTrapState *old_state; }; #endif /* __COGL_XLIB_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-texture-private.h0000664000175000017500000003350114723361714020057 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_PRIVATE_H #define __COGL_TEXTURE_PRIVATE_H #include "cogl-bitmap-private.h" #include "cogl-object-private.h" #include "cogl-pipeline-private.h" #include "cogl-spans.h" #include "cogl-meta-texture.h" #include "cogl-framebuffer.h" #include "cogl-texture-2d.h" #ifdef COGL_HAS_EGL_SUPPORT #include "cogl-egl-defines.h" #endif typedef struct _CoglTextureVtable CoglTextureVtable; /* Encodes three possibiloities result of transforming a quad */ typedef enum { /* quad doesn't cross the boundaries of a texture */ COGL_TRANSFORM_NO_REPEAT, /* quad crosses boundaries, hardware wrap mode can handle */ COGL_TRANSFORM_HARDWARE_REPEAT, /* quad crosses boundaries, needs software fallback; * for a sliced texture, this might not actually involve * repeating, just a quad crossing multiple slices */ COGL_TRANSFORM_SOFTWARE_REPEAT, } CoglTransformResult; /* Flags given to the pre_paint method */ typedef enum { /* The texture is going to be used with filters that require mipmapping. This gives the texture the opportunity to automatically update the mipmap tree */ COGL_TEXTURE_NEEDS_MIPMAP = 1 } CoglTexturePrePaintFlags; struct _CoglTextureVtable { /* Virtual functions that must be implemented for a texture backend */ gboolean is_primitive; gboolean (* allocate) (CoglTexture *tex, GError **error); /* This should update the specified sub region of the texture with a sub region of the given bitmap. The bitmap is not converted before being set so the caller is expected to have called _cogl_bitmap_convert_for_upload with a suitable internal_format before passing here */ gboolean (* set_region) (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, int level, CoglBitmap *bitmap, GError **error); gboolean (* is_get_data_supported) (CoglTexture *texture); /* This should copy the image data of the texture into @data. The requested format will have been first passed through ctx->texture_driver->find_best_gl_get_data_format so it should always be a format that is valid for GL (ie, no conversion should be necessary). */ gboolean (* get_data) (CoglTexture *tex, CoglPixelFormat format, int rowstride, uint8_t *data); void (* foreach_sub_texture_in_region) (CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data); int (* get_max_waste) (CoglTexture *tex); gboolean (* is_sliced) (CoglTexture *tex); gboolean (* can_hardware_repeat) (CoglTexture *tex); void (* transform_coords_to_gl) (CoglTexture *tex, float *s, float *t); CoglTransformResult (* transform_quad_coords_to_gl) (CoglTexture *tex, float *coords); gboolean (* get_gl_texture) (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target); /* OpenGL driver specific virtual function */ void (* gl_flush_legacy_texobj_filters) (CoglTexture *tex, GLenum min_filter, GLenum mag_filter); void (* pre_paint) (CoglTexture *tex, CoglTexturePrePaintFlags flags); void (* ensure_non_quad_rendering) (CoglTexture *tex); /* OpenGL driver specific virtual function */ void (* gl_flush_legacy_texobj_wrap_modes) (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t); CoglPixelFormat (* get_format) (CoglTexture *tex); GLenum (* get_gl_format) (CoglTexture *tex); /* Only needs to be implemented if is_primitive == TRUE */ void (* set_auto_mipmap) (CoglTexture *texture, gboolean value); }; typedef enum _CoglTextureSoureType { COGL_TEXTURE_SOURCE_TYPE_SIZED = 1, COGL_TEXTURE_SOURCE_TYPE_BITMAP, COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE, COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE_EXTERNAL } CoglTextureSourceType; typedef struct _CoglTextureLoader { CoglTextureSourceType src_type; union { struct { int width; int height; int depth; /* for 3d textures */ } sized; struct { CoglBitmap *bitmap; int height; /* for 3d textures */ int depth; /* for 3d textures */ gboolean can_convert_in_place; } bitmap; #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) struct { EGLImageKHR image; int width; int height; CoglPixelFormat format; CoglEglImageFlags flags; } egl_image; #endif #if defined (COGL_HAS_EGL_SUPPORT) struct { int width; int height; CoglTexture2DEGLImageExternalAlloc alloc; CoglPixelFormat format; } egl_image_external; #endif struct { int width; int height; unsigned int gl_handle; CoglPixelFormat format; } gl_foreign; } src; } CoglTextureLoader; struct _CoglTexture { CoglObject _parent; CoglContext *context; CoglTextureLoader *loader; GList *framebuffers; int max_level_set; int max_level_requested; int width; int height; gboolean allocated; /* * Internal format */ CoglTextureComponents components; unsigned int premultiplied:1; const CoglTextureVtable *vtable; }; typedef enum _CoglTextureChangeFlags { /* Whenever the internals of a texture are changed such that the * underlying GL textures that represent the CoglTexture change then * we notify cogl-material.c via * _cogl_pipeline_texture_pre_change_notify */ COGL_TEXTURE_CHANGE_GL_TEXTURES } CoglTextureChangeFlags; typedef struct _CoglTexturePixel CoglTexturePixel; /* This is used by the texture backends to store the first pixel of each GL texture. This is only used when glGenerateMipmap is not available so that we can temporarily set GL_GENERATE_MIPMAP and reupload a pixel */ struct _CoglTexturePixel { /* We need to store the format of the pixel because we store the data in the source format which might end up being different for each slice if a subregion is updated with a different format */ GLenum gl_format; GLenum gl_type; uint8_t data[4]; }; void _cogl_texture_init (CoglTexture *texture, CoglContext *ctx, int width, int height, CoglPixelFormat src_format, CoglTextureLoader *loader, const CoglTextureVtable *vtable); void _cogl_texture_free (CoglTexture *texture); /* This is used to register a type to the list of handle types that will be considered a texture in cogl_is_texture() */ void _cogl_texture_register_texture_type (const CoglObjectClass *klass); #define COGL_TEXTURE_DEFINE(TypeName, type_name) \ COGL_OBJECT_DEFINE_WITH_CODE_GTYPE \ (TypeName, type_name, \ _cogl_texture_register_texture_type (&_cogl_##type_name##_class)) #define COGL_TEXTURE_INTERNAL_DEFINE(TypeName, type_name) \ COGL_OBJECT_INTERNAL_DEFINE_WITH_CODE \ (TypeName, type_name, \ _cogl_texture_register_texture_type (&_cogl_##type_name##_class)) COGL_EXPORT gboolean _cogl_texture_can_hardware_repeat (CoglTexture *texture); void _cogl_texture_transform_coords_to_gl (CoglTexture *texture, float *s, float *t); CoglTransformResult _cogl_texture_transform_quad_coords_to_gl (CoglTexture *texture, float *coords); void _cogl_texture_pre_paint (CoglTexture *texture, CoglTexturePrePaintFlags flags); void _cogl_texture_ensure_non_quad_rendering (CoglTexture *texture); /* * This determines a CoglPixelFormat according to texture::components * and texture::premultiplied (i.e. the user required components and * whether the texture should be considered premultiplied) * * A reference/source format can be given (or COGL_PIXEL_FORMAT_ANY) * and wherever possible this function tries to simply return the * given source format if its compatible with the required components. * * Texture backends can call this when allocating a texture to know * how to convert a source image in preparation for uploading. */ CoglPixelFormat _cogl_texture_determine_internal_format (CoglTexture *texture, CoglPixelFormat src_format); /* This is called by texture backends when they have successfully * allocated a texture. * * Most texture backends currently track the internal layout of * textures using a CoglPixelFormat which will be finalized when a * texture is allocated. At this point we need to update * texture::components and texture::premultiplied according to the * determined layout. * * XXX: Going forward we should probably aim to stop using * CoglPixelFormat at all for tracking the internal layout of * textures. */ void _cogl_texture_set_internal_format (CoglTexture *texture, CoglPixelFormat internal_format); void _cogl_texture_associate_framebuffer (CoglTexture *texture, CoglFramebuffer *framebuffer); const GList * _cogl_texture_get_associated_framebuffers (CoglTexture *texture); void _cogl_texture_flush_journal_rendering (CoglTexture *texture); void _cogl_texture_spans_foreach_in_region (CoglSpan *x_spans, int n_x_spans, CoglSpan *y_spans, int n_y_spans, CoglTexture **textures, float *virtual_coords, float x_normalize_factor, float y_normalize_factor, CoglPipelineWrapMode wrap_x, CoglPipelineWrapMode wrap_y, CoglMetaTextureCallback callback, void *user_data); COGL_EXPORT gboolean _cogl_texture_set_region (CoglTexture *texture, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, int dst_x, int dst_y, int level, GError **error); gboolean _cogl_texture_set_region_from_bitmap (CoglTexture *texture, int src_x, int src_y, int width, int height, CoglBitmap *bmp, int dst_x, int dst_y, int level, GError **error); gboolean _cogl_texture_needs_premult_conversion (CoglPixelFormat src_format, CoglPixelFormat dst_format); int _cogl_texture_get_n_levels (CoglTexture *texture); void cogl_texture_set_max_level (CoglTexture *texture, int max_level); void _cogl_texture_get_level_size (CoglTexture *texture, int level, int *width, int *height, int *depth); void _cogl_texture_set_allocated (CoglTexture *texture, CoglPixelFormat internal_format, int width, int height); COGL_EXPORT CoglPixelFormat _cogl_texture_get_format (CoglTexture *texture); CoglTextureLoader * _cogl_texture_create_loader (void); void _cogl_texture_copy_internal_format (CoglTexture *src, CoglTexture *dest); #endif /* __COGL_TEXTURE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-attribute-private.h0000664000175000017500000001006414723361714020361 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_ATTRIBUTE_PRIVATE_H #define __COGL_ATTRIBUTE_PRIVATE_H #include "cogl-object-private.h" #include "cogl-attribute.h" #include "cogl-framebuffer.h" #include "cogl-pipeline-private.h" #include "cogl-boxed-value.h" typedef enum { COGL_ATTRIBUTE_NAME_ID_POSITION_ARRAY, COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY, COGL_ATTRIBUTE_NAME_ID_TEXTURE_COORD_ARRAY, COGL_ATTRIBUTE_NAME_ID_NORMAL_ARRAY, COGL_ATTRIBUTE_NAME_ID_POINT_SIZE_ARRAY, COGL_ATTRIBUTE_NAME_ID_CUSTOM_ARRAY } CoglAttributeNameID; typedef struct _CoglAttributeNameState { const char *name; CoglAttributeNameID name_id; int name_index; gboolean normalized_default; int layer_number; } CoglAttributeNameState; struct _CoglAttribute { CoglObject _parent; const CoglAttributeNameState *name_state; gboolean normalized; gboolean is_buffered; union { struct { CoglAttributeBuffer *attribute_buffer; size_t stride; size_t offset; int n_components; CoglAttributeType type; } buffered; struct { CoglContext *context; CoglBoxedValue boxed; } constant; } d; int immutable_ref; }; typedef enum { COGL_DRAW_SKIP_JOURNAL_FLUSH = 1 << 0, COGL_DRAW_SKIP_PIPELINE_VALIDATION = 1 << 1, COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH = 1 << 2, /* By default the vertex attribute drawing code will assume that if there is a color attribute array enabled then we can't determine if the colors will be opaque so we need to enabling blending. However when drawing from the journal we know what the contents of the color array is so we can override this by passing this flag. */ COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE = 1 << 3, /* This forcibly disables the debug option to divert all drawing to * wireframes */ COGL_DRAW_SKIP_DEBUG_WIREFRAME = 1 << 4 } CoglDrawFlags; /* During CoglContext initialization we register the "cogl_color_in" * attribute name so it gets a global name_index of 0. We need to know * the name_index for "cogl_color_in" in * _cogl_pipeline_flush_gl_state() */ #define COGL_ATTRIBUTE_COLOR_NAME_INDEX 0 CoglAttributeNameState * _cogl_attribute_register_attribute_name (CoglContext *context, const char *name); CoglAttribute * _cogl_attribute_immutable_ref (CoglAttribute *attribute); void _cogl_attribute_immutable_unref (CoglAttribute *attribute); typedef struct { int unit; CoglPipelineFlushOptions options; uint32_t fallback_layers; } CoglFlushLayerState; void _cogl_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes); int _cogl_attribute_get_n_components (CoglAttribute *attribute); #endif /* __COGL_ATTRIBUTE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-sampler-cache-private.h0000664000175000017500000000626014723361714021065 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_SAMPLER_CACHE_PRIVATE_H #define __COGL_SAMPLER_CACHE_PRIVATE_H #include "cogl-context.h" #include "cogl-gl-header.h" /* These aren't defined in the GLES headers */ #ifndef GL_CLAMP_TO_BORDER #define GL_CLAMP_TO_BORDER 0x812d #endif #ifndef GL_MIRRORED_REPEAT #define GL_MIRRORED_REPEAT 0x8370 #endif /* GL_ALWAYS is just used here as a value that is known not to clash * with any valid GL wrap modes. * * XXX: keep the values in sync with the CoglPipelineWrapMode enum * so no conversion is actually needed. */ typedef enum _CoglSamplerCacheWrapMode { COGL_SAMPLER_CACHE_WRAP_MODE_REPEAT = GL_REPEAT, COGL_SAMPLER_CACHE_WRAP_MODE_MIRRORED_REPEAT = GL_MIRRORED_REPEAT, COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_EDGE = GL_CLAMP_TO_EDGE, COGL_SAMPLER_CACHE_WRAP_MODE_CLAMP_TO_BORDER = GL_CLAMP_TO_BORDER, COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC = GL_ALWAYS } CoglSamplerCacheWrapMode; typedef struct _CoglSamplerCache CoglSamplerCache; typedef struct _CoglSamplerCacheEntry { GLuint sampler_object; GLenum min_filter; GLenum mag_filter; CoglSamplerCacheWrapMode wrap_mode_s; CoglSamplerCacheWrapMode wrap_mode_t; } CoglSamplerCacheEntry; CoglSamplerCache * _cogl_sampler_cache_new (CoglContext *context); const CoglSamplerCacheEntry * _cogl_sampler_cache_get_default_entry (CoglSamplerCache *cache); const CoglSamplerCacheEntry * _cogl_sampler_cache_update_wrap_modes (CoglSamplerCache *cache, const CoglSamplerCacheEntry *old_entry, CoglSamplerCacheWrapMode wrap_mode_s, CoglSamplerCacheWrapMode wrap_mode_t); const CoglSamplerCacheEntry * _cogl_sampler_cache_update_filters (CoglSamplerCache *cache, const CoglSamplerCacheEntry *old_entry, GLenum min_filter, GLenum mag_filter); void _cogl_sampler_cache_free (CoglSamplerCache *cache); #endif /* __COGL_SAMPLER_CACHE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-poll.c0000664000175000017500000001557714723361714015665 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-poll.h" #include "cogl-poll-private.h" #include "cogl-renderer-private.h" #include "winsys/cogl-winsys-private.h" struct _CoglPollSource { int fd; CoglPollPrepareCallback prepare; CoglPollDispatchCallback dispatch; void *user_data; }; int cogl_poll_renderer_get_info (CoglRenderer *renderer, CoglPollFD **poll_fds, int *n_poll_fds, int64_t *timeout) { GList *l, *next; g_return_val_if_fail (cogl_is_renderer (renderer), 0); g_return_val_if_fail (poll_fds != NULL, 0); g_return_val_if_fail (n_poll_fds != NULL, 0); g_return_val_if_fail (timeout != NULL, 0); *timeout = -1; if (!_cogl_list_empty (&renderer->idle_closures)) *timeout = 0; /* This loop needs to cope with the prepare callback removing its * own fd */ for (l = renderer->poll_sources; l; l = next) { CoglPollSource *source = l->data; next = l->next; if (source->prepare) { int64_t source_timeout = source->prepare (source->user_data); if (source_timeout >= 0 && (*timeout == -1 || *timeout > source_timeout)) *timeout = source_timeout; } } /* This is deliberately set after calling the prepare callbacks in * case one of them removes its fd */ *poll_fds = (void *)renderer->poll_fds->data; *n_poll_fds = renderer->poll_fds->len; return renderer->poll_fds_age; } void cogl_poll_renderer_dispatch (CoglRenderer *renderer, const CoglPollFD *poll_fds, int n_poll_fds) { GList *l, *next; g_return_if_fail (cogl_is_renderer (renderer)); _cogl_closure_list_invoke_no_args (&renderer->idle_closures); /* This loop needs to cope with the dispatch callback removing its * own fd */ for (l = renderer->poll_sources; l; l = next) { CoglPollSource *source = l->data; int i; next = l->next; if (source->fd == -1) { source->dispatch (source->user_data, 0); continue; } for (i = 0; i < n_poll_fds; i++) { const CoglPollFD *pollfd = &poll_fds[i]; if (pollfd->fd == source->fd) { source->dispatch (source->user_data, pollfd->revents); break; } } } } static int find_pollfd (CoglRenderer *renderer, int fd) { int i; for (i = 0; i < renderer->poll_fds->len; i++) { CoglPollFD *pollfd = &g_array_index (renderer->poll_fds, CoglPollFD, i); if (pollfd->fd == fd) return i; } return -1; } void _cogl_poll_renderer_remove_fd (CoglRenderer *renderer, int fd) { int i = find_pollfd (renderer, fd); GList *l; if (i < 0) return; g_array_remove_index_fast (renderer->poll_fds, i); renderer->poll_fds_age++; for (l = renderer->poll_sources; l; l = l->next) { CoglPollSource *source = l->data; if (source->fd == fd) { renderer->poll_sources = g_list_delete_link (renderer->poll_sources, l); g_slice_free (CoglPollSource, source); break; } } } void _cogl_poll_renderer_modify_fd (CoglRenderer *renderer, int fd, CoglPollFDEvent events) { int fd_index = find_pollfd (renderer, fd); if (fd_index == -1) g_warn_if_reached (); else { CoglPollFD *pollfd = &g_array_index (renderer->poll_sources, CoglPollFD, fd_index); pollfd->events = events; renderer->poll_fds_age++; } } void _cogl_poll_renderer_add_fd (CoglRenderer *renderer, int fd, CoglPollFDEvent events, CoglPollPrepareCallback prepare, CoglPollDispatchCallback dispatch, void *user_data) { CoglPollFD pollfd = { fd, events }; CoglPollSource *source; _cogl_poll_renderer_remove_fd (renderer, fd); source = g_slice_new0 (CoglPollSource); source->fd = fd; source->prepare = prepare; source->dispatch = dispatch; source->user_data = user_data; renderer->poll_sources = g_list_prepend (renderer->poll_sources, source); g_array_append_val (renderer->poll_fds, pollfd); renderer->poll_fds_age++; } CoglPollSource * _cogl_poll_renderer_add_source (CoglRenderer *renderer, CoglPollPrepareCallback prepare, CoglPollDispatchCallback dispatch, void *user_data) { CoglPollSource *source; source = g_slice_new0 (CoglPollSource); source->fd = -1; source->prepare = prepare; source->dispatch = dispatch; source->user_data = user_data; renderer->poll_sources = g_list_prepend (renderer->poll_sources, source); return source; } void _cogl_poll_renderer_remove_source (CoglRenderer *renderer, CoglPollSource *source) { GList *l; for (l = renderer->poll_sources; l; l = l->next) { if (l->data == source) { renderer->poll_sources = g_list_delete_link (renderer->poll_sources, l); g_slice_free (CoglPollSource, source); break; } } } CoglClosure * _cogl_poll_renderer_add_idle (CoglRenderer *renderer, CoglIdleCallback idle_cb, void *user_data, CoglUserDataDestroyCallback destroy_cb) { return _cogl_closure_list_add (&renderer->idle_closures, idle_cb, user_data, destroy_cb); } muffin-6.4.1/cogl/cogl/cogl-onscreen.h0000664000175000017500000007061414723361714016531 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011,2012,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_ONSCREEN_H #define __COGL_ONSCREEN_H #include #include #include #include #include G_BEGIN_DECLS typedef struct _CoglOnscreen CoglOnscreen; #define COGL_ONSCREEN(X) ((CoglOnscreen *)(X)) /** * cogl_onscreen_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_onscreen_get_gtype (void); /** * cogl_onscreen_new: (constructor) (skip) * @context: A #CoglContext * @width: The desired framebuffer width * @height: The desired framebuffer height * * Instantiates an "unallocated" #CoglOnscreen framebuffer that may be * configured before later being allocated, either implicitly when * it is first used or explicitly via cogl_framebuffer_allocate(). * * Return value: (transfer full): A newly instantiated #CoglOnscreen framebuffer * Since: 1.8 * Stability: unstable */ COGL_EXPORT CoglOnscreen * cogl_onscreen_new (CoglContext *context, int width, int height); #ifdef COGL_HAS_X11 /** * cogl_x11_onscreen_get_window_xid: * @onscreen: A #CoglOnscreen framebuffer * * Assuming you know the given @onscreen framebuffer is based on an x11 window * this queries the XID of that window. If * cogl_x11_onscreen_set_foreign_window_xid() was previously called then it * will return that same XID otherwise it will be the XID of a window Cogl * created internally. If the window has not been allocated yet and a foreign * xid has not been set then it's undefined what value will be returned. * * It's undefined what this function does if called when not using an x11 based * renderer. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT uint32_t cogl_x11_onscreen_get_window_xid (CoglOnscreen *onscreen); #endif /* COGL_HAS_X11 */ /** * cogl_onscreen_show: * @onscreen: The onscreen framebuffer to make visible * * This requests to make @onscreen visible to the user. * * Actually the precise semantics of this function depend on the * window system currently in use, and if you don't have a * multi-windowining system this function may in-fact do nothing. * * This function will implicitly allocate the given @onscreen * framebuffer before showing it if it hasn't already been allocated. * * When using the Wayland winsys calling this will set the surface to * a toplevel type which will make it appear. If the application wants * to set a different type for the surface, it can avoid calling * cogl_onscreen_show() and set its own type directly with the Wayland * client API via cogl_wayland_onscreen_get_surface(). * * Since Cogl doesn't explicitly track the visibility status of * onscreen framebuffers it wont try to avoid redundant window system * requests e.g. to show an already visible window. This also means * that it's acceptable to alternatively use native APIs to show and * hide windows without confusing Cogl. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_onscreen_show (CoglOnscreen *onscreen); /** * cogl_onscreen_hide: * @onscreen: The onscreen framebuffer to make invisible * * This requests to make @onscreen invisible to the user. * * Actually the precise semantics of this function depend on the * window system currently in use, and if you don't have a * multi-windowining system this function may in-fact do nothing. * * This function does not implicitly allocate the given @onscreen * framebuffer before hiding it. * * Since Cogl doesn't explicitly track the visibility status of * onscreen framebuffers it wont try to avoid redundant window system * requests e.g. to show an already visible window. This also means * that it's acceptable to alternatively use native APIs to show and * hide windows without confusing Cogl. * * Since: 2.0 * Stability: Unstable */ COGL_EXPORT void cogl_onscreen_hide (CoglOnscreen *onscreen); /** * cogl_onscreen_swap_buffers: * @onscreen: A #CoglOnscreen framebuffer * * Swaps the current back buffer being rendered too, to the front for display. * * This function also implicitly discards the contents of the color, depth and * stencil buffers as if cogl_framebuffer_discard_buffers() were used. The * significance of the discard is that you should not expect to be able to * start a new frame that incrementally builds on the contents of the previous * frame. * * It is highly recommended that applications use * cogl_onscreen_swap_buffers_with_damage() instead whenever possible * and also use the cogl_onscreen_get_buffer_age() api so they can * perform incremental updates to older buffers instead of having to * render a full buffer for every frame. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_swap_buffers (CoglOnscreen *onscreen); /** * cogl_onscreen_get_buffer_age: * @onscreen: A #CoglOnscreen framebuffer * * Gets the current age of the buffer contents. * * This function allows applications to query the age of the current * back buffer contents for a #CoglOnscreen as the number of frames * elapsed since the contents were most recently defined. * * These age values exposes enough information to applications about * how Cogl internally manages back buffers to allow applications to * re-use the contents of old frames and minimize how much must be * redrawn for the next frame. * * The back buffer contents can either be reported as invalid (has an * age of 0) or it may be reported to be the same contents as from n * frames prior to the current frame. * * The queried value remains valid until the next buffer swap. * * One caveat is that under X11 the buffer age does not reflect * changes to buffer contents caused by the window systems. X11 * applications must track Expose events to determine what buffer * regions need to additionally be repaired each frame. * * The recommended way to take advantage of this buffer age api is to * build up a circular buffer of length 3 for tracking damage regions * over the last 3 frames and when starting a new frame look at the * age of the buffer and combine the damage regions for the current * frame with the damage regions of previous @age frames so you know * everything that must be redrawn to update the old contents for the * new frame. * * If the system doesn't not support being able to track the age * of back buffers then this function will always return 0 which * implies that the contents are undefined. * * The %COGL_FEATURE_ID_BUFFER_AGE feature can optionally be * explicitly checked to determine if Cogl is currently tracking the * age of #CoglOnscreen back buffer contents. If this feature is * missing then this function will always return 0. * * Return value: The age of the buffer contents or 0 when the buffer * contents are undefined. * * Since: 1.14 * Stability: stable */ COGL_EXPORT int cogl_onscreen_get_buffer_age (CoglOnscreen *onscreen); /** * cogl_onscreen_swap_buffers_with_damage: * @onscreen: A #CoglOnscreen framebuffer * @rectangles: An array of integer 4-tuples representing damaged * rectangles as (x, y, width, height) tuples. * @n_rectangles: The number of 4-tuples to be read from @rectangles * * Swaps the current back buffer being rendered too, to the front for * display and provides information to any system compositor about * what regions of the buffer have changed (damage) with respect to * the last swapped buffer. * * This function has the same semantics as * cogl_framebuffer_swap_buffers() except that it additionally allows * applications to pass a list of damaged rectangles which may be * passed on to a compositor so that it can minimize how much of the * screen is redrawn in response to this applications newly swapped * front buffer. * * For example if your application is only animating a small object in * the corner of the screen and everything else is remaining static * then it can help the compositor to know that only the bottom right * corner of your newly swapped buffer has really changed with respect * to your previously swapped front buffer. * * If @n_rectangles is 0 then the whole buffer will implicitly be * reported as damaged as if cogl_onscreen_swap_buffers() had been * called. * * This function also implicitly discards the contents of the color, * depth and stencil buffers as if cogl_framebuffer_discard_buffers() * were used. The significance of the discard is that you should not * expect to be able to start a new frame that incrementally builds on * the contents of the previous frame. If you want to perform * incremental updates to older back buffers then please refer to the * cogl_onscreen_get_buffer_age() api. * * Whenever possible it is recommended that applications use this * function instead of cogl_onscreen_swap_buffers() to improve * performance when running under a compositor. * * It is highly recommended to use this API in conjunction with * the cogl_onscreen_get_buffer_age() api so that your application can * perform incremental rendering based on old back buffers. * * Since: 1.16 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles); /** * cogl_onscreen_swap_region: * @onscreen: A #CoglOnscreen framebuffer * @rectangles: An array of integer 4-tuples representing rectangles as * (x, y, width, height) tuples. * @n_rectangles: The number of 4-tuples to be read from @rectangles * * Swaps a region of the back buffer being rendered too, to the front for * display. @rectangles represents the region as array of @n_rectangles each * defined by 4 sequential (x, y, width, height) integers. * * This function also implicitly discards the contents of the color, depth and * stencil buffers as if cogl_framebuffer_discard_buffers() were used. The * significance of the discard is that you should not expect to be able to * start a new frame that incrementally builds on the contents of the previous * frame. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_swap_region (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles); /** * CoglFrameEvent: * @COGL_FRAME_EVENT_SYNC: Notifies that the system compositor has * acknowledged a frame and is ready for a * new frame to be created. * @COGL_FRAME_EVENT_COMPLETE: Notifies that a frame has ended. This * is a good time for applications to * collect statistics about the frame * since the #CoglFrameInfo should hold * the most data at this point. No other * events should be expected after a * @COGL_FRAME_EVENT_COMPLETE event. * * Identifiers that are passed to #CoglFrameCallback functions * (registered using cogl_onscreen_add_frame_callback()) that * mark the progression of a frame in some way which usually * means that new information will have been accumulated in the * frame's corresponding #CoglFrameInfo object. * * The last event that will be sent for a frame will be a * @COGL_FRAME_EVENT_COMPLETE event and so these are a good * opportunity to collect statistics about a frame since the * #CoglFrameInfo should hold the most data at this point. * * A frame may not be completed before the next frame can start * so applications should avoid needing to collect all statistics for * a particular frame before they can start a new frame. * * Since: 1.14 * Stability: unstable */ typedef enum _CoglFrameEvent { COGL_FRAME_EVENT_SYNC = 1, COGL_FRAME_EVENT_COMPLETE } CoglFrameEvent; /** * CoglFrameCallback: * @onscreen: The onscreen that the frame is associated with * @event: A #CoglFrameEvent notifying how the frame has progressed * @info: The meta information, such as timing information, about * the frame that has progressed. * @user_data: The user pointer passed to * cogl_onscreen_add_frame_callback() * * Is a callback that can be registered via * cogl_onscreen_add_frame_callback() to be called when a frame * progresses in some notable way. * * Please see the documentation for #CoglFrameEvent and * cogl_onscreen_add_frame_callback() for more details about what * events can be notified. * * Since: 1.14 * Stability: unstable */ typedef void (*CoglFrameCallback) (CoglOnscreen *onscreen, CoglFrameEvent event, CoglFrameInfo *info, void *user_data); /** * CoglFrameClosure: * * An opaque type that tracks a #CoglFrameCallback and associated user * data. A #CoglFrameClosure pointer will be returned from * cogl_onscreen_add_frame_callback() and it allows you to remove a * callback later using cogl_onscreen_remove_frame_callback(). * * Since: 1.14 * Stability: unstable */ typedef struct _CoglClosure CoglFrameClosure; /** * cogl_frame_closure_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_frame_closure_get_gtype (void); /** * cogl_onscreen_add_frame_callback: * @onscreen: A #CoglOnscreen framebuffer * @callback: (scope notified) (closure user_data): A callback function to call for frame events * @user_data: A private pointer to be passed to @callback * @destroy: (allow-none): An optional callback to destroy @user_data * when the @callback is removed or @onscreen is freed. * * Installs a @callback function that will be called for significant * events relating to the given @onscreen framebuffer. * * The @callback will be used to notify when the system compositor is * ready for this application to render a new frame. In this case * %COGL_FRAME_EVENT_SYNC will be passed as the event argument to the * given @callback in addition to the #CoglFrameInfo corresponding to * the frame beeing acknowledged by the compositor. * * The @callback will also be called to notify when the frame has * ended. In this case %COGL_FRAME_EVENT_COMPLETE will be passed as * the event argument to the given @callback in addition to the * #CoglFrameInfo corresponding to the newly presented frame. The * meaning of "ended" here simply means that no more timing * information will be collected within the corresponding * #CoglFrameInfo and so this is a good opportunity to analyse the * given info. It does not necessarily mean that the GPU has finished * rendering the corresponding frame. * * We highly recommend throttling your application according to * %COGL_FRAME_EVENT_SYNC events so that your application can avoid * wasting resources, drawing more frames than your system compositor * can display. * * Return value: a #CoglFrameClosure pointer that can be used to * remove the callback and associated @user_data later. * Since: 1.14 * Stability: unstable */ COGL_EXPORT CoglFrameClosure * cogl_onscreen_add_frame_callback (CoglOnscreen *onscreen, CoglFrameCallback callback, void *user_data, CoglUserDataDestroyCallback destroy); /** * cogl_onscreen_remove_frame_callback: * @onscreen: A #CoglOnscreen * @closure: A #CoglFrameClosure returned from * cogl_onscreen_add_frame_callback() * * Removes a callback and associated user data that were previously * registered using cogl_onscreen_add_frame_callback(). * * If a destroy callback was passed to * cogl_onscreen_add_frame_callback() to destroy the user data then * this will get called. * * Since: 1.14 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_remove_frame_callback (CoglOnscreen *onscreen, CoglFrameClosure *closure); /** * cogl_onscreen_set_resizable: * @onscreen: A #CoglOnscreen framebuffer * * Lets you request Cogl to mark an @onscreen framebuffer as * resizable or not. * * By default, if possible, a @onscreen will be created by Cogl * as non resizable, but it is not guaranteed that this is always * possible for all window systems. * * Cogl does not know whether marking the @onscreen framebuffer * is truly meaningful for your current window system (consider * applications being run fullscreen on a phone or TV) so this * function may not have any useful effect. If you are running on a * multi windowing system such as X11 or Win32 or OSX then Cogl will * request to the window system that users be allowed to resize the * @onscreen, although it's still possible that some other window * management policy will block this possibility. * * Whenever an @onscreen framebuffer is resized the viewport * will be automatically updated to match the new size of the * framebuffer with an origin of (0,0). If your application needs more * specialized control of the viewport it will need to register a * resize handler using cogl_onscreen_add_resize_callback() so that it * can track when the viewport has been changed automatically. * * Since: 2.0 */ COGL_EXPORT void cogl_onscreen_set_resizable (CoglOnscreen *onscreen, gboolean resizable); /** * cogl_onscreen_get_resizable: * @onscreen: A #CoglOnscreen framebuffer * * Lets you query whether @onscreen has been marked as resizable via * the cogl_onscreen_set_resizable() api. * * By default, if possible, a @onscreen will be created by Cogl * as non resizable, but it is not guaranteed that this is always * possible for all window systems. * * If cogl_onscreen_set_resizable(@onscreen, %TRUE) has been * previously called then this function will return %TRUE, but it's * possible that the current windowing system being used does not * support window resizing (consider fullscreen windows on a phone or * a TV). This function is not aware of whether resizing is truly * meaningful with your window system, only whether the @onscreen has * been marked as resizable. * * Return value: Returns whether @onscreen has been marked as * resizable or not. * Since: 2.0 */ COGL_EXPORT gboolean cogl_onscreen_get_resizable (CoglOnscreen *onscreen); /** * CoglOnscreenResizeCallback: * @onscreen: A #CoglOnscreen framebuffer that was resized * @width: The new width of @onscreen * @height: The new height of @onscreen * @user_data: The private passed to * cogl_onscreen_add_resize_callback() * * Is a callback type used with the * cogl_onscreen_add_resize_callback() allowing applications to be * notified whenever an @onscreen framebuffer is resized. * * Cogl automatically updates the viewport of an @onscreen * framebuffer that is resized so this callback is also an indication * that the viewport has been modified too * * A resize callback will only ever be called while dispatching * Cogl events from the system mainloop; so for example during * cogl_poll_renderer_dispatch(). This is so that callbacks shouldn't * occur while an application might have arbitrary locks held for * example. * * Since: 2.0 */ typedef void (*CoglOnscreenResizeCallback) (CoglOnscreen *onscreen, int width, int height, void *user_data); /** * CoglOnscreenResizeClosure: * * An opaque type that tracks a #CoglOnscreenResizeCallback and * associated user data. A #CoglOnscreenResizeClosure pointer will be * returned from cogl_onscreen_add_resize_callback() and it allows you * to remove a callback later using * cogl_onscreen_remove_resize_callback(). * * Since: 2.0 * Stability: unstable */ typedef struct _CoglClosure CoglOnscreenResizeClosure; /** * cogl_onscreen_resize_closure_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_onscreen_resize_closure_get_gtype (void); /** * cogl_onscreen_add_resize_callback: * @onscreen: A #CoglOnscreen framebuffer * @callback: (scope notified) (closure user_data): A #CoglOnscreenResizeCallback to call when * the @onscreen changes size. * @user_data: Private data to be passed to @callback. * @destroy: (allow-none): An optional callback to destroy @user_data * when the @callback is removed or @onscreen is freed. * * Registers a @callback with @onscreen that will be called whenever * the @onscreen framebuffer changes size. * * The @callback can be removed using * cogl_onscreen_remove_resize_callback() passing the returned closure * pointer. * * Since Cogl automatically updates the viewport of an @onscreen * framebuffer that is resized, a resize callback can also be used to * track when the viewport has been changed automatically by Cogl in * case your application needs more specialized control over the * viewport. * * A resize callback will only ever be called while dispatching * Cogl events from the system mainloop; so for example during * cogl_poll_renderer_dispatch(). This is so that callbacks shouldn't * occur while an application might have arbitrary locks held for * example. * * Return value: a #CoglOnscreenResizeClosure pointer that can be used to * remove the callback and associated @user_data later. * Since: 2.0 */ COGL_EXPORT CoglOnscreenResizeClosure * cogl_onscreen_add_resize_callback (CoglOnscreen *onscreen, CoglOnscreenResizeCallback callback, void *user_data, CoglUserDataDestroyCallback destroy); /** * cogl_onscreen_remove_resize_callback: * @onscreen: A #CoglOnscreen framebuffer * @closure: An identifier returned from cogl_onscreen_add_resize_callback() * * Removes a resize @callback and @user_data pair that were previously * associated with @onscreen via cogl_onscreen_add_resize_callback(). * * Since: 2.0 */ COGL_EXPORT void cogl_onscreen_remove_resize_callback (CoglOnscreen *onscreen, CoglOnscreenResizeClosure *closure); /** * CoglOnscreenDirtyInfo: * @x: Left edge of the dirty rectangle * @y: Top edge of the dirty rectangle, measured from the top of the window * @width: Width of the dirty rectangle * @height: Height of the dirty rectangle * * A structure passed to callbacks registered using * cogl_onscreen_add_dirty_callback(). The members describe a * rectangle within the onscreen buffer that should be redrawn. * * Since: 1.16 * Stability: unstable */ typedef struct _CoglOnscreenDirtyInfo CoglOnscreenDirtyInfo; struct _CoglOnscreenDirtyInfo { int x, y; int width, height; }; /** * CoglOnscreenDirtyCallback: * @onscreen: The onscreen that the frame is associated with * @info: A #CoglOnscreenDirtyInfo struct containing the details of the * dirty area * @user_data: The user pointer passed to * cogl_onscreen_add_frame_callback() * * Is a callback that can be registered via * cogl_onscreen_add_dirty_callback() to be called when the windowing * system determines that a region of the onscreen window has been * lost and the application should redraw it. * * Since: 1.16 * Stability: unstable */ typedef void (*CoglOnscreenDirtyCallback) (CoglOnscreen *onscreen, const CoglOnscreenDirtyInfo *info, void *user_data); /** * CoglOnscreenDirtyClosure: * * An opaque type that tracks a #CoglOnscreenDirtyCallback and associated * user data. A #CoglOnscreenDirtyClosure pointer will be returned from * cogl_onscreen_add_dirty_callback() and it allows you to remove a * callback later using cogl_onscreen_remove_dirty_callback(). * * Since: 1.16 * Stability: unstable */ typedef struct _CoglClosure CoglOnscreenDirtyClosure; /** * cogl_onscreen_dirty_closure_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_onscreen_dirty_closure_get_gtype (void); /** * cogl_onscreen_add_dirty_callback: * @onscreen: A #CoglOnscreen framebuffer * @callback: (scope notified) (closure user_data): A callback function to call for dirty events * @user_data: A private pointer to be passed to @callback * @destroy: (allow-none): An optional callback to destroy @user_data when the * @callback is removed or @onscreen is freed. * * Installs a @callback function that will be called whenever the * window system has lost the contents of a region of the onscreen * buffer and the application should redraw it to repair the buffer. * For example this may happen in a window system without a compositor * if a window that was previously covering up the onscreen window has * been moved causing a region of the onscreen to be exposed. * * The @callback will be passed a #CoglOnscreenDirtyInfo struct which * decribes a rectangle containing the newly dirtied region. Note that * this may be called multiple times to describe a non-rectangular * region composed of multiple smaller rectangles. * * The dirty events are separate from %COGL_FRAME_EVENT_SYNC events so * the application should also listen for this event before rendering * the dirty region to ensure that the framebuffer is actually ready * for rendering. * * Return value: a #CoglOnscreenDirtyClosure pointer that can be used to * remove the callback and associated @user_data later. * Since: 1.16 * Stability: unstable */ COGL_EXPORT CoglOnscreenDirtyClosure * cogl_onscreen_add_dirty_callback (CoglOnscreen *onscreen, CoglOnscreenDirtyCallback callback, void *user_data, CoglUserDataDestroyCallback destroy); /** * cogl_onscreen_remove_dirty_callback: * @onscreen: A #CoglOnscreen * @closure: A #CoglOnscreenDirtyClosure returned from * cogl_onscreen_add_dirty_callback() * * Removes a callback and associated user data that were previously * registered using cogl_onscreen_add_dirty_callback(). * * If a destroy callback was passed to * cogl_onscreen_add_dirty_callback() to destroy the user data then * this will also get called. * * Since: 1.16 * Stability: unstable */ COGL_EXPORT void cogl_onscreen_remove_dirty_callback (CoglOnscreen *onscreen, CoglOnscreenDirtyClosure *closure); /** * cogl_is_onscreen: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglOnscreen. * * Return value: %TRUE if the object references a #CoglOnscreen * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_onscreen (void *object); /** * cogl_onscreen_get_frame_counter: * * Gets the value of the framebuffers frame counter. This is * a counter that increases by one each time * cogl_onscreen_swap_buffers() or cogl_onscreen_swap_region() * is called. * * Return value: the current frame counter value * Since: 1.14 * Stability: unstable */ COGL_EXPORT int64_t cogl_onscreen_get_frame_counter (CoglOnscreen *onscreen); G_END_DECLS #endif /* __COGL_ONSCREEN_H */ muffin-6.4.1/cogl/cogl/cogl-glsl-shader-boilerplate.h0000664000175000017500000000601014723361714021407 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #ifndef __COGL_SHADER_BOILERPLATE_H #define __COGL_SHADER_BOILERPLATE_H #define _COGL_COMMON_SHADER_BOILERPLATE \ "#define COGL_VERSION 100\n" \ "\n" \ "uniform mat4 cogl_modelview_matrix;\n" \ "uniform mat4 cogl_modelview_projection_matrix;\n" \ "uniform mat4 cogl_projection_matrix;\n" /* This declares all of the variables that we might need. This is * working on the assumption that the compiler will optimise them out * if they are not actually used. The GLSL spec at least implies that * this will happen for varyings but it doesn't explicitly so for * attributes */ #define _COGL_VERTEX_SHADER_BOILERPLATE \ _COGL_COMMON_SHADER_BOILERPLATE \ "#define cogl_color_out _cogl_color\n" \ "varying vec4 _cogl_color;\n" \ "#define cogl_tex_coord_out _cogl_tex_coord\n" \ "#define cogl_position_out gl_Position\n" \ "#define cogl_point_size_out gl_PointSize\n" \ "\n" \ "attribute vec4 cogl_color_in;\n" \ "attribute vec4 cogl_position_in;\n" \ "#define cogl_tex_coord_in cogl_tex_coord0_in;\n" \ "attribute vec3 cogl_normal_in;\n" #define _COGL_FRAGMENT_SHADER_BOILERPLATE \ "#ifdef GL_ES\n" \ "precision highp float;\n" \ "#endif\n" \ _COGL_COMMON_SHADER_BOILERPLATE \ "\n" \ "varying vec4 _cogl_color;\n" \ "\n" \ "#define cogl_color_in _cogl_color\n" \ "#define cogl_tex_coord_in _cogl_tex_coord\n" \ "\n" \ "#define cogl_color_out gl_FragColor\n" \ "#define cogl_depth_out gl_FragDepth\n" \ "\n" \ "#define cogl_front_facing gl_FrontFacing\n" \ "\n" \ "#define cogl_point_coord gl_PointCoord\n" #if 0 /* GLSL 1.2 has a bottom left origin, though later versions * allow use of an origin_upper_left keyword which would be * more appropriate for Cogl. */ "#define coglFragCoord gl_FragCoord\n" #endif #endif /* __COGL_SHADER_BOILERPLATE_H */ muffin-6.4.1/cogl/cogl/cogl-bitmap-packing.h0000664000175000017500000005521514723361714017603 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This file is included multiple times with different definitions for the component_type type (either uint8_t or uint16_t). The code ends up exactly the same for both but we only want to end up hitting the 16-bit path when one of the types in the conversion is > 8 bits per component. */ /* Unpacking to RGBA */ #define UNPACK_1(b) ((b) * ((1 << (sizeof (component_type) * 8)) - 1)) #define UNPACK_2(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ 1) / 3) #define UNPACK_4(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ 7) / 15) #define UNPACK_5(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ 15) / 31) #define UNPACK_6(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ 31) / 63) #define UNPACK_10(b) (((b) * ((1 << (sizeof (component_type) * 8)) - 1) + \ 511) / 1023) inline static void G_PASTE (_cogl_unpack_a_8_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = 0; dst[1] = 0; dst[2] = 0; dst[3] = UNPACK_BYTE (*src); dst += 4; src++; } } inline static void G_PASTE (_cogl_unpack_g_8_, component_size) (const uint8_t *src, component_type *dst, int width) { /* FIXME: I'm not sure if this is right. It looks like Nvidia and Mesa handle luminance textures differently. Maybe we should consider just removing luminance textures for Cogl 2.0 because they have been removed in GL 3.0 */ while (width-- > 0) { component_type v = UNPACK_BYTE (src[0]); dst[0] = v; dst[1] = v; dst[2] = v; dst[3] = UNPACK_BYTE (255); dst += 4; src++; } } inline static void G_PASTE (_cogl_unpack_rg_88_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[0]); dst[1] = UNPACK_BYTE (src[1]); dst[2] = 0; dst[3] = UNPACK_BYTE (255); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_rgb_888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[0]); dst[1] = UNPACK_BYTE (src[1]); dst[2] = UNPACK_BYTE (src[2]); dst[3] = UNPACK_BYTE (255); dst += 4; src += 3; } } inline static void G_PASTE (_cogl_unpack_bgr_888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[2]); dst[1] = UNPACK_BYTE (src[1]); dst[2] = UNPACK_BYTE (src[0]); dst[3] = UNPACK_BYTE (255); dst += 4; src += 3; } } inline static void G_PASTE (_cogl_unpack_bgra_8888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[2]); dst[1] = UNPACK_BYTE (src[1]); dst[2] = UNPACK_BYTE (src[0]); dst[3] = UNPACK_BYTE (src[3]); dst += 4; src += 4; } } inline static void G_PASTE (_cogl_unpack_argb_8888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[1]); dst[1] = UNPACK_BYTE (src[2]); dst[2] = UNPACK_BYTE (src[3]); dst[3] = UNPACK_BYTE (src[0]); dst += 4; src += 4; } } inline static void G_PASTE (_cogl_unpack_abgr_8888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[3]); dst[1] = UNPACK_BYTE (src[2]); dst[2] = UNPACK_BYTE (src[1]); dst[3] = UNPACK_BYTE (src[0]); dst += 4; src += 4; } } inline static void G_PASTE (_cogl_unpack_rgba_8888_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { dst[0] = UNPACK_BYTE (src[0]); dst[1] = UNPACK_BYTE (src[1]); dst[2] = UNPACK_BYTE (src[2]); dst[3] = UNPACK_BYTE (src[3]); dst += 4; src += 4; } } inline static void G_PASTE (_cogl_unpack_rgb_565_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint16_t v = *(const uint16_t *) src; dst[0] = UNPACK_5 (v >> 11); dst[1] = UNPACK_6 ((v >> 5) & 63); dst[2] = UNPACK_5 (v & 31); dst[3] = UNPACK_BYTE (255); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_rgba_4444_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint16_t v = *(const uint16_t *) src; dst[0] = UNPACK_4 (v >> 12); dst[1] = UNPACK_4 ((v >> 8) & 15); dst[2] = UNPACK_4 ((v >> 4) & 15); dst[3] = UNPACK_4 (v & 15); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_rgba_5551_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint16_t v = *(const uint16_t *) src; dst[0] = UNPACK_5 (v >> 11); dst[1] = UNPACK_5 ((v >> 6) & 31); dst[2] = UNPACK_5 ((v >> 1) & 31); dst[3] = UNPACK_1 (v & 1); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_rgba_1010102_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint32_t v = *(const uint32_t *) src; dst[0] = UNPACK_10 (v >> 22); dst[1] = UNPACK_10 ((v >> 12) & 1023); dst[2] = UNPACK_10 ((v >> 2) & 1023); dst[3] = UNPACK_2 (v & 3); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_bgra_1010102_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint32_t v = *(const uint32_t *) src; dst[2] = UNPACK_10 (v >> 22); dst[1] = UNPACK_10 ((v >> 12) & 1023); dst[0] = UNPACK_10 ((v >> 2) & 1023); dst[3] = UNPACK_2 (v & 3); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_argb_2101010_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint32_t v = *(const uint32_t *) src; dst[3] = UNPACK_2 (v >> 30); dst[0] = UNPACK_10 ((v >> 20) & 1023); dst[1] = UNPACK_10 ((v >> 10) & 1023); dst[2] = UNPACK_10 (v & 1023); dst += 4; src += 2; } } inline static void G_PASTE (_cogl_unpack_abgr_2101010_, component_size) (const uint8_t *src, component_type *dst, int width) { while (width-- > 0) { uint32_t v = *(const uint32_t *) src; dst[3] = UNPACK_2 (v >> 30); dst[2] = UNPACK_10 ((v >> 20) & 1023); dst[1] = UNPACK_10 ((v >> 10) & 1023); dst[0] = UNPACK_10 (v & 1023); dst += 4; src += 2; } } #undef UNPACK_1 #undef UNPACK_2 #undef UNPACK_4 #undef UNPACK_5 #undef UNPACK_6 #undef UNPACK_10 inline static void G_PASTE (_cogl_unpack_, component_size) (CoglPixelFormat format, const uint8_t *src, component_type *dst, int width) { switch (format) { case COGL_PIXEL_FORMAT_A_8: G_PASTE (_cogl_unpack_a_8_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_G_8: G_PASTE (_cogl_unpack_g_8_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RG_88: G_PASTE (_cogl_unpack_rg_88_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGB_888: G_PASTE (_cogl_unpack_rgb_888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGR_888: G_PASTE (_cogl_unpack_bgr_888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: G_PASTE (_cogl_unpack_rgba_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_BGRA_8888_PRE: G_PASTE (_cogl_unpack_bgra_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: G_PASTE (_cogl_unpack_argb_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ABGR_8888: case COGL_PIXEL_FORMAT_ABGR_8888_PRE: G_PASTE (_cogl_unpack_abgr_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGB_565: G_PASTE (_cogl_unpack_rgb_565_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_4444: case COGL_PIXEL_FORMAT_RGBA_4444_PRE: G_PASTE (_cogl_unpack_rgba_4444_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_5551: case COGL_PIXEL_FORMAT_RGBA_5551_PRE: G_PASTE (_cogl_unpack_rgba_5551_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_1010102: case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: G_PASTE (_cogl_unpack_rgba_1010102_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGRA_1010102: case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: G_PASTE (_cogl_unpack_bgra_1010102_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ARGB_2101010: case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: G_PASTE (_cogl_unpack_argb_2101010_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ABGR_2101010: case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: G_PASTE (_cogl_unpack_abgr_2101010_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_DEPTH_16: case COGL_PIXEL_FORMAT_DEPTH_32: case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: case COGL_PIXEL_FORMAT_ANY: case COGL_PIXEL_FORMAT_YUV: g_assert_not_reached (); } } /* Packing from RGBA */ /* Pack and round to nearest */ #define PACK_SIZE(b, max) \ (((b) * (max) + (1 << (sizeof (component_type) * 8 - 1)) - 1) / \ ((1 << (sizeof (component_type) * 8)) - 1)) #define PACK_1(b) PACK_SIZE (b, 1) #define PACK_2(b) PACK_SIZE (b, 3) #define PACK_4(b) PACK_SIZE (b, 15) #define PACK_5(b) PACK_SIZE (b, 31) #define PACK_6(b) PACK_SIZE (b, 63) #define PACK_10(b) PACK_SIZE (b, 1023) inline static void G_PASTE (_cogl_pack_a_8_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { *dst = PACK_BYTE (src[3]); src += 4; dst++; } } inline static void G_PASTE (_cogl_pack_g_8_, component_size) (const component_type *src, uint8_t *dst, int width) { /* FIXME: I'm not sure if this is right. It looks like Nvidia and Mesa handle luminance textures differently. Maybe we should consider just removing luminance textures for Cogl 2.0 because they have been removed in GL 3.0 */ while (width-- > 0) { component_type v = (src[0] + src[1] + src[2]) / 3; *dst = PACK_BYTE (v); src += 4; dst++; } } inline static void G_PASTE (_cogl_pack_rg_88_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[0] = PACK_BYTE (src[0]); dst[1] = PACK_BYTE (src[1]); src += 4; dst += 2; } } inline static void G_PASTE (_cogl_pack_rgb_888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[0] = PACK_BYTE (src[0]); dst[1] = PACK_BYTE (src[1]); dst[2] = PACK_BYTE (src[2]); src += 4; dst += 3; } } inline static void G_PASTE (_cogl_pack_bgr_888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[2] = PACK_BYTE (src[0]); dst[1] = PACK_BYTE (src[1]); dst[0] = PACK_BYTE (src[2]); src += 4; dst += 3; } } inline static void G_PASTE (_cogl_pack_bgra_8888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[2] = PACK_BYTE (src[0]); dst[1] = PACK_BYTE (src[1]); dst[0] = PACK_BYTE (src[2]); dst[3] = PACK_BYTE (src[3]); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_argb_8888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[1] = PACK_BYTE (src[0]); dst[2] = PACK_BYTE (src[1]); dst[3] = PACK_BYTE (src[2]); dst[0] = PACK_BYTE (src[3]); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_abgr_8888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[3] = PACK_BYTE (src[0]); dst[2] = PACK_BYTE (src[1]); dst[1] = PACK_BYTE (src[2]); dst[0] = PACK_BYTE (src[3]); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_rgba_8888_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { dst[0] = PACK_BYTE (src[0]); dst[1] = PACK_BYTE (src[1]); dst[2] = PACK_BYTE (src[2]); dst[3] = PACK_BYTE (src[3]); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_rgb_565_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint16_t *v = (uint16_t *) dst; *v = ((PACK_5 (src[0]) << 11) | (PACK_6 (src[1]) << 5) | PACK_5 (src[2])); src += 4; dst += 2; } } inline static void G_PASTE (_cogl_pack_rgba_4444_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint16_t *v = (uint16_t *) dst; *v = ((PACK_4 (src[0]) << 12) | (PACK_4 (src[1]) << 8) | (PACK_4 (src[2]) << 4) | PACK_4 (src[3])); src += 4; dst += 2; } } inline static void G_PASTE (_cogl_pack_rgba_5551_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint16_t *v = (uint16_t *) dst; *v = ((PACK_5 (src[0]) << 11) | (PACK_5 (src[1]) << 6) | (PACK_5 (src[2]) << 1) | PACK_1 (src[3])); src += 4; dst += 2; } } inline static void G_PASTE (_cogl_pack_rgba_1010102_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint32_t *v = (uint32_t *) dst; *v = ((PACK_10 (src[0]) << 22) | (PACK_10 (src[1]) << 12) | (PACK_10 (src[2]) << 2) | PACK_2 (src[3])); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_bgra_1010102_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint32_t *v = (uint32_t *) dst; *v = ((PACK_10 (src[2]) << 22) | (PACK_10 (src[1]) << 12) | (PACK_10 (src[0]) << 2) | PACK_2 (src[3])); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_argb_2101010_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint32_t *v = (uint32_t *) dst; *v = ((PACK_2 (src[3]) << 30) | (PACK_10 (src[0]) << 20) | (PACK_10 (src[1]) << 10) | PACK_10 (src[2])); src += 4; dst += 4; } } inline static void G_PASTE (_cogl_pack_abgr_2101010_, component_size) (const component_type *src, uint8_t *dst, int width) { while (width-- > 0) { uint32_t *v = (uint32_t *) dst; *v = ((PACK_2 (src[3]) << 30) | (PACK_10 (src[2]) << 20) | (PACK_10 (src[1]) << 10) | PACK_10 (src[0])); src += 4; dst += 4; } } #undef PACK_SIZE #undef PACK_1 #undef PACK_2 #undef PACK_4 #undef PACK_5 #undef PACK_6 #undef PACK_10 inline static void G_PASTE (_cogl_pack_, component_size) (CoglPixelFormat format, const component_type *src, uint8_t *dst, int width) { switch (format) { case COGL_PIXEL_FORMAT_A_8: G_PASTE (_cogl_pack_a_8_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_G_8: G_PASTE (_cogl_pack_g_8_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RG_88: G_PASTE (_cogl_pack_rg_88_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGB_888: G_PASTE (_cogl_pack_rgb_888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGR_888: G_PASTE (_cogl_pack_bgr_888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: G_PASTE (_cogl_pack_rgba_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_BGRA_8888_PRE: G_PASTE (_cogl_pack_bgra_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: G_PASTE (_cogl_pack_argb_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ABGR_8888: case COGL_PIXEL_FORMAT_ABGR_8888_PRE: G_PASTE (_cogl_pack_abgr_8888_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGB_565: G_PASTE (_cogl_pack_rgb_565_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_4444: case COGL_PIXEL_FORMAT_RGBA_4444_PRE: G_PASTE (_cogl_pack_rgba_4444_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_5551: case COGL_PIXEL_FORMAT_RGBA_5551_PRE: G_PASTE (_cogl_pack_rgba_5551_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_RGBA_1010102: case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: G_PASTE (_cogl_pack_rgba_1010102_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_BGRA_1010102: case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: G_PASTE (_cogl_pack_bgra_1010102_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ARGB_2101010: case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: G_PASTE (_cogl_pack_argb_2101010_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_ABGR_2101010: case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: G_PASTE (_cogl_pack_abgr_2101010_, component_size) (src, dst, width); break; case COGL_PIXEL_FORMAT_DEPTH_16: case COGL_PIXEL_FORMAT_DEPTH_32: case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: case COGL_PIXEL_FORMAT_ANY: case COGL_PIXEL_FORMAT_YUV: g_assert_not_reached (); } } muffin-6.4.1/cogl/cogl/cogl-defines.h.meson0000664000175000017500000000343414723361714017446 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include #define COGL_SYSDEF_POLLIN POLLIN #define COGL_SYSDEF_POLLPRI POLLPRI #define COGL_SYSDEF_POLLOUT POLLOUT #define COGL_SYSDEF_POLLERR POLLERR #define COGL_SYSDEF_POLLHUP POLLHUP #define COGL_SYSDEF_POLLNVAL POLLNVAL #mesondefine COGL_HAS_GL #mesondefine CLUTTER_COGL_HAS_GL #mesondefine COGL_HAS_GLX_SUPPORT #mesondefine COGL_HAS_WAYLAND_EGL_SERVER_SUPPORT #mesondefine COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT #mesondefine COGL_HAS_EGL_SUPPORT #mesondefine COGL_HAS_X11 #mesondefine COGL_HAS_X11_SUPPORT #mesondefine COGL_HAS_XLIB #mesondefine COGL_HAS_XLIB_SUPPORT #mesondefine COGL_HAS_TRACING muffin-6.4.1/cogl/cogl/cogl-profile.c0000664000175000017500000000764514723361714016354 0ustar fabiofabio#include "cogl-config.h" #ifdef COGL_ENABLE_PROFILE #include "cogl-profile.h" #include "cogl-debug.h" #include "cogl-i18n-private.h" #include UProfContext *_cogl_uprof_context; static gboolean debug_option_getter (void *user_data) { unsigned int shift = GPOINTER_TO_UINT (user_data); return COGL_DEBUG_ENABLED (shift); } static void debug_option_setter (gboolean value, void *user_data) { unsigned int shift = GPOINTER_TO_UINT (user_data); if (value) COGL_DEBUG_SET_FLAG (shift); else COGL_DEBUG_CLEAR_FLAG (shift); } static void print_exit_report (void) { if (getenv ("COGL_PROFILE_OUTPUT_REPORT")) { UProfContext *mainloop_context; UProfTimerResult *mainloop_timer; UProfReport *report; /* NB: uprof provides a shared context for mainloop statistics * which needs to be setup by the application which controls the * mainloop. * * If no "Mainloop" timer has been setup then we print a warning * since we can't provide a meaningful Cogl report without one. */ mainloop_context = uprof_get_mainloop_context (); mainloop_timer = uprof_context_get_timer_result (mainloop_context, "Mainloop"); /* just bail out if the mainloop timer wasn't hit */ if (!mainloop_timer) { g_warning ("\n\n" "No UProf \"Mainloop\" timer was setup by the " "application therefore we\ncan't provide a meaningful " "profile report.\n" "\n" "This should be done automatically if you are using Clutter " "(if\nbuilt with --enable-profile)\n" "\n" "If you aren't using Clutter then you can declare a " "\"Mainloop\" UProf\ntimer in your application like this:\n\n" " UPROF_STATIC_TIMER (mainloop_timer, \n" " NULL,\n" " \"Mainloop\",\n" " \"Time in glib mainloop\",\n" " 0);\n" "\n" "And start/stop it around your mainloop like this:\n" "\n" " UPROF_TIMER_START (uprof_get_mainloop_context (), mainloop_timer);\n" " g_main_loop_run (loop);\n" " UPROF_TIMER_STOP (uprof_get_mainloop_context (), mainloop_timer);\n"); return; } report = uprof_report_new ("Cogl report"); uprof_report_add_context (report, _cogl_uprof_context); uprof_report_print (report); uprof_report_unref (report); } uprof_context_unref (_cogl_uprof_context); } void _cogl_uprof_init (void) { _cogl_uprof_context = uprof_context_new ("Cogl"); uprof_context_link (_cogl_uprof_context, uprof_get_mainloop_context ()); #define OPT(MASK_NAME, GROUP, NAME, NAME_FORMATTED, DESCRIPTION) \ G_STMT_START { \ int shift = COGL_DEBUG_ ## MASK_NAME; \ uprof_context_add_boolean_option (_cogl_uprof_context, \ GROUP, \ NAME, \ NAME_FORMATTED, \ DESCRIPTION, \ debug_option_getter, \ debug_option_setter, \ GUINT_TO_POINTER (shift)); \ } G_STMT_END; #include "cogl-debug-options.h" #undef OPT atexit (print_exit_report); } void _cogl_profile_trace_message (const char *format, ...) { va_list ap; va_start (ap, format); g_logv (G_LOG_DOMAIN, G_LOG_LEVEL_MESSAGE, format, ap); va_end (ap); if (_cogl_uprof_context) uprof_context_vtrace_message (_cogl_uprof_context, format, ap); } #endif muffin-6.4.1/cogl/cogl/cogl-object.h0000664000175000017500000001641614723361714016163 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_OBJECT_H #define __COGL_OBJECT_H #include #include G_BEGIN_DECLS typedef struct _CoglObject CoglObject; #define COGL_OBJECT(X) ((CoglObject *)X) /** * CoglObject: (ref-func cogl_object_ref) (unref-func cogl_object_unref) * (set-value-func cogl_object_value_set_object) * (get-value-func cogl_object_value_get_object) */ /** * cogl_object_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_object_get_gtype (void); /** * cogl_object_ref: (skip) * @object: a #CoglObject * * Increases the reference count of @object by 1 * * Returns: the @object, with its reference count increased */ COGL_EXPORT void * cogl_object_ref (void *object); /** * cogl_object_unref: (skip) * @object: a #CoglObject * * Drecreases the reference count of @object by 1; if the reference * count reaches 0, the resources allocated by @object will be freed */ COGL_EXPORT void cogl_object_unref (void *object); /** * cogl_clear_object: (skip) * @object_ptr: a pointer to a #CoglObject reference * * Clears a reference to a #CoglObject. * * @object_ptr must not be %NULL. * * If the reference is %NULL then this function does nothing. * Otherwise, the reference count of the object is decreased using * cogl_object_unref() and the pointer is set to %NULL. */ #define cogl_clear_object(object_ptr) g_clear_pointer ((object_ptr), cogl_object_unref) /** * CoglUserDataKey: * @unused: ignored. * * A #CoglUserDataKey is used to declare a key for attaching data to a * #CoglObject using cogl_object_set_user_data. The typedef only exists as a * formality to make code self documenting since only the unique address of a * #CoglUserDataKey is used. * * Typically you would declare a static #CoglUserDataKey and set private data * on an object something like this: * * |[ * static CoglUserDataKey path_private_key; * * static void * destroy_path_private_cb (void *data) * { * g_free (data); * } * * static void * my_path_set_data (CoglPath *path, void *data) * { * cogl_object_set_user_data (COGL_OBJECT (path), * &private_key, * data, * destroy_path_private_cb); * } * ]| * * Since: 1.4 */ typedef struct { int unused; } CoglUserDataKey; /** * CoglUserDataDestroyCallback: * @user_data: The data whos association with a #CoglObject has been * destoyed. * * When associating private data with a #CoglObject a callback can be * given which will be called either if the object is destroyed or if * cogl_object_set_user_data() is called with NULL user_data for the * same key. * * Since: 1.4 */ typedef GDestroyNotify CoglUserDataDestroyCallback; /** * CoglDebugObjectTypeInfo: * @name: A human readable name for the type. * @instance_count: The number of objects of this type that are * currently in use * * This struct is used to pass information to the callback when * cogl_debug_object_foreach_type() is called. * * Since: 1.8 * Stability: unstable */ typedef struct { const char *name; unsigned long instance_count; } CoglDebugObjectTypeInfo; /** * CoglDebugObjectForeachTypeCallback: * @info: A pointer to a struct containing information about the type. * * A callback function to use for cogl_debug_object_foreach_type(). * * Since: 1.8 * Stability: unstable */ typedef void (* CoglDebugObjectForeachTypeCallback) (const CoglDebugObjectTypeInfo *info, void *user_data); /** * cogl_object_set_user_data: (skip) * @object: The object to associate private data with * @key: The address of a #CoglUserDataKey which provides a unique value * with which to index the private data. * @user_data: The data to associate with the given object, * or %NULL to remove a previous association. * @destroy: A #CoglUserDataDestroyCallback to call if the object is * destroyed or if the association is removed by later setting * %NULL data for the same key. * * Associates some private @user_data with a given #CoglObject. To * later remove the association call cogl_object_set_user_data() with * the same @key but NULL for the @user_data. * * Since: 1.4 */ COGL_EXPORT void cogl_object_set_user_data (CoglObject *object, CoglUserDataKey *key, void *user_data, CoglUserDataDestroyCallback destroy); /** * cogl_object_get_user_data: (skip) * @object: The object with associated private data to query * @key: The address of a #CoglUserDataKey which provides a unique value * with which to index the private data. * * Finds the user data previously associated with @object using * the given @key. If no user data has been associated with @object * for the given @key this function returns NULL. * * Returns: (transfer none): The user data previously associated * with @object using the given @key; or %NULL if no associated * data is found. * * Since: 1.4 */ COGL_EXPORT void * cogl_object_get_user_data (CoglObject *object, CoglUserDataKey *key); /** * cogl_debug_object_foreach_type: * @func: (scope call): A callback function for each type * @user_data: (closure): A pointer to pass to @func * * Invokes @func once for each type of object that Cogl uses and * passes a count of the number of objects for that type. This is * intended to be used solely for debugging purposes to track down * issues with objects leaking. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_debug_object_foreach_type (CoglDebugObjectForeachTypeCallback func, void *user_data); /** * cogl_debug_object_print_instances: * * Prints a list of all the object types that Cogl uses along with the * number of objects of that type that are currently in use. This is * intended to be used solely for debugging purposes to track down * issues with objects leaking. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT void cogl_debug_object_print_instances (void); G_END_DECLS #endif /* __COGL_OBJECT_H */ muffin-6.4.1/cogl/cogl/cogl.h0000664000175000017500000001207614723361714014715 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_H__ #define __COGL_H__ #ifdef COGL_COMPILATION #error " shouldn't be included internally" #endif /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_MUST_UNDEF_COGL_H_INSIDE__ #endif #include /* We currently keep gtype integration delimited in case we eventually * want to split it out into a separate utility library when Cogl * becomes a standalone project. (like cairo-gobject.so) */ #define _COGL_SUPPORTS_GTYPE_INTEGRATION /* * API common to the 1.x and 2.0 api... */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* * 1.x only api... */ #if 0 #ifndef COGL_ENABLE_EXPERIMENTAL_2_0_API #warning #endif #endif /* It would be good to move these casts up into 1.x only api if we can * update Clutter, Mutter and GnomeShell to avoid redundant casts when * they enable the experimental api... */ #include #include #include #include #ifdef COGL_ENABLE_MUTTER_API #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* XXX: This will definitly go away once all the Clutter winsys * code has been migrated down into Cogl! */ #include /* * Cogl Path api compatability * * The cogl_path_ api used to be part of the core Cogl api so for * compatability we include cogl-path.h via cogl.h * * Note: we have to make sure not to include cogl-path.h while * building core cogl or generating the Cogl .gir data because * cogl-path now gets built after cogl and some cogl-path headers are * only generated at build time... */ #if !defined (COGL_COMPILATION) && \ !defined (COGL_GIR_SCANNING) #include #endif /** * SECTION:cogl * @short_description: General purpose API * * General utility functions for COGL. */ /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_MUST_UNDEF_COGL_H_INSIDE__ #undef __COGL_H_INSIDE__ #undef __COGL_MUST_UNDEF_COGL_H_INSIDE__ #endif #endif /* __COGL_H__ */ muffin-6.4.1/cogl/cogl/cogl-flags.h0000664000175000017500000001172214723361714016004 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts */ #ifndef __COGL_FLAGS_H #define __COGL_FLAGS_H #include #include "cogl-util.h" G_BEGIN_DECLS /* These are macros used to implement a fixed-size array of bits. This should be used instead of CoglBitmask when the maximum bit number that will be set is known at compile time, for example when setting for recording a set of known available features */ /* The bits are stored in an array of unsigned longs. To use these macros, you would typically have an enum defining the available bits with an extra last enum to define the maximum value. Then to store the flags you would declare an array of unsigned longs sized using COGL_FLAGS_N_LONGS_FOR_SIZE, eg: typedef enum { FEATURE_A, FEATURE_B, FEATURE_C, N_FEATURES } Features; unsigned long feature_flags[COGL_FLAGS_N_LONGS_FOR_SIZE (N_FEATURES)]; */ #define COGL_FLAGS_N_LONGS_FOR_SIZE(size) \ (((size) + \ (sizeof (unsigned long) * 8 - 1)) \ / (sizeof (unsigned long) * 8)) /* @flag is expected to be constant so these should result in a constant expression. This means that setting a flag is equivalent to just setting in a bit in a global variable at a known location */ #define COGL_FLAGS_GET_INDEX(flag) \ ((flag) / (sizeof (unsigned long) * 8)) #define COGL_FLAGS_GET_MASK(flag) \ (1UL << ((unsigned long) (flag) & \ (sizeof (unsigned long) * 8 - 1))) #define COGL_FLAGS_GET(array, flag) \ (!!((array)[COGL_FLAGS_GET_INDEX (flag)] & \ COGL_FLAGS_GET_MASK (flag))) /* The expectation here is that @value will be constant so the if statement will be optimised out */ #define COGL_FLAGS_SET(array, flag, value) \ G_STMT_START { \ if (value) \ ((array)[COGL_FLAGS_GET_INDEX (flag)] |= \ COGL_FLAGS_GET_MASK (flag)); \ else \ ((array)[COGL_FLAGS_GET_INDEX (flag)] &= \ ~COGL_FLAGS_GET_MASK (flag)); \ } G_STMT_END /* Macros to help iterate an array of flags. It should be used like * this: * * int n_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (...); * unsigned long flags[n_longs]; * int bit_num; * * COGL_FLAGS_FOREACH_START (flags, n_longs, bit_num) * { * do_something_with_the_bit (bit_num); * } * COGL_FLAGS_FOREACH_END; */ #define COGL_FLAGS_FOREACH_START(array, n_longs, bit) \ G_STMT_START { \ const unsigned long *_p = (array); \ int _n_longs = (n_longs); \ int _i; \ \ for (_i = 0; _i < _n_longs; _i++) \ { \ unsigned long _mask = *(_p++); \ \ (bit) = _i * sizeof (unsigned long) * 8 - 1; \ \ while (_mask) \ { \ int _next_bit = _cogl_util_ffsl (_mask); \ (bit) += _next_bit; \ /* This odd two-part shift is to avoid */ \ /* shifting by sizeof (long)*8 which has */ \ /* undefined results according to the */ \ /* C spec (and seems to be a no-op in */ \ /* practice) */ \ _mask = (_mask >> (_next_bit - 1)) >> 1; \ #define COGL_FLAGS_FOREACH_END \ } } } G_STMT_END G_END_DECLS #endif /* __COGL_FLAGS_H */ muffin-6.4.1/cogl/cogl/cogl-atlas.h0000664000175000017500000000667214723361714016024 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_ATLAS_H #define __COGL_ATLAS_H #include "cogl-rectangle-map.h" #include "cogl-object-private.h" #include "cogl-texture.h" typedef void (* CoglAtlasUpdatePositionCallback) (void *user_data, CoglTexture *new_texture, const CoglRectangleMapEntry *rect); typedef enum { COGL_ATLAS_CLEAR_TEXTURE = (1 << 0), COGL_ATLAS_DISABLE_MIGRATION = (1 << 1) } CoglAtlasFlags; typedef struct _CoglAtlas CoglAtlas; #define COGL_ATLAS(object) ((CoglAtlas *) object) struct _CoglAtlas { CoglObject _parent; CoglRectangleMap *map; CoglTexture *texture; CoglPixelFormat texture_format; CoglAtlasFlags flags; CoglAtlasUpdatePositionCallback update_position_cb; GHookList pre_reorganize_callbacks; GHookList post_reorganize_callbacks; }; COGL_EXPORT CoglAtlas * _cogl_atlas_new (CoglPixelFormat texture_format, CoglAtlasFlags flags, CoglAtlasUpdatePositionCallback update_position_cb); COGL_EXPORT gboolean _cogl_atlas_reserve_space (CoglAtlas *atlas, unsigned int width, unsigned int height, void *user_data); void _cogl_atlas_remove (CoglAtlas *atlas, const CoglRectangleMapEntry *rectangle); CoglTexture * _cogl_atlas_copy_rectangle (CoglAtlas *atlas, int x, int y, int width, int height, CoglPixelFormat format); COGL_EXPORT void _cogl_atlas_add_reorganize_callback (CoglAtlas *atlas, GHookFunc pre_callback, GHookFunc post_callback, void *user_data); void _cogl_atlas_remove_reorganize_callback (CoglAtlas *atlas, GHookFunc pre_callback, GHookFunc post_callback, void *user_data); gboolean _cogl_is_atlas (void *object); #endif /* __COGL_ATLAS_H */ muffin-6.4.1/cogl/cogl/cogl-feature-private.h0000664000175000017500000000730014723361714020010 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_FEATURE_PRIVATE_H #define __COGL_FEATURE_PRIVATE_H #include #include "cogl-context.h" #include "cogl-renderer.h" #define COGL_CHECK_GL_VERSION(driver_major, driver_minor, \ target_major, target_minor) \ ((driver_major) > (target_major) || \ ((driver_major) == (target_major) && (driver_minor) >= (target_minor))) typedef enum { COGL_EXT_IN_GL = (1 << 0), COGL_EXT_IN_GLES2 = (1 << 1), COGL_EXT_IN_GLES3 = (1 << 2) } CoglExtGlesAvailability; typedef struct _CoglFeatureFunction CoglFeatureFunction; struct _CoglFeatureFunction { /* The name of the function without the "EXT" or "ARB" suffix */ const char *name; /* The offset in the context of where to store the function pointer */ unsigned int pointer_offset; }; typedef struct _CoglFeatureData CoglFeatureData; struct _CoglFeatureData { /* A minimum GL version which the functions should be defined in without needing an extension. Set to 255,255 if it's only provided in an extension */ int min_gl_major, min_gl_minor; /* Flags specifying which versions of GLES the feature is available in core in */ CoglExtGlesAvailability gles_availability; /* \0 separated list of namespaces to try. Eg "EXT\0ARB\0" */ const char *namespaces; /* \0 separated list of required extension names without the GL_EXT or GL_ARB prefix. Any of the extensions must be available for the feature to be considered available. If the suffix for an extension is different from the namespace, you can specify it with a ':' after the namespace */ const char *extension_names; /* A set of private feature flags to enable if the extension is * available */ int feature_flags_private; /* An optional corresponding winsys feature. */ CoglWinsysFeature winsys_feature; /* A list of functions required for this feature. Terminated with a NULL name */ const CoglFeatureFunction *functions; }; gboolean _cogl_feature_check (CoglRenderer *renderer, const char *driver_prefix, const CoglFeatureData *data, int gl_major, int gl_minor, CoglDriver driver, char * const *extensions, void *function_table); void _cogl_feature_check_ext_functions (CoglContext *context, int gl_major, int gl_minor, char * const *gl_extensions); #endif /* __COGL_FEATURE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-point-in-poly-private.h0000664000175000017500000000312514723361714021074 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_POINT_INT_POLYGON_PRIVATE_H #define __COGL_POINT_INT_POLYGON_PRIVATE_H #include G_BEGIN_DECLS int _cogl_util_point_in_screen_poly (float point_x, float point_y, void *vertices, size_t stride, int n_vertices); G_END_DECLS #endif /* __COGL_POINT_INT_POLYGON_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-attribute-buffer-private.h0000664000175000017500000000270414723361714021632 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_ATTRIBUTE_BUFFER_PRIVATE_H #define __COGL_ATTRIBUTE_BUFFER_PRIVATE_H #include "cogl-buffer-private.h" struct _CoglAttributeBuffer { CoglBuffer _parent; }; #endif /* __COGL_ATTRIBUTE_BUFFER_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-attribute-buffer.c0000664000175000017500000000652514723361714020162 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-object-private.h" #include "cogl-attribute-buffer.h" #include "cogl-attribute-buffer-private.h" #include "cogl-context-private.h" #include "cogl-gtype-private.h" static void _cogl_attribute_buffer_free (CoglAttributeBuffer *array); COGL_BUFFER_DEFINE (AttributeBuffer, attribute_buffer); COGL_GTYPE_DEFINE_CLASS (AttributeBuffer, attribute_buffer); CoglAttributeBuffer * cogl_attribute_buffer_new_with_size (CoglContext *context, size_t bytes) { CoglAttributeBuffer *buffer = g_slice_new (CoglAttributeBuffer); /* parent's constructor */ _cogl_buffer_initialize (COGL_BUFFER (buffer), context, bytes, COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, COGL_BUFFER_USAGE_HINT_ATTRIBUTE_BUFFER, COGL_BUFFER_UPDATE_HINT_STATIC); return _cogl_attribute_buffer_object_new (buffer); } CoglAttributeBuffer * cogl_attribute_buffer_new (CoglContext *context, size_t bytes, const void *data) { CoglAttributeBuffer *buffer; buffer = cogl_attribute_buffer_new_with_size (context, bytes); /* Note: to keep the common cases simple this API doesn't throw * GErrors, so developers can assume this function never returns * NULL and we will simply abort on error. * * Developers wanting to catch errors can use * cogl_attribute_buffer_new_with_size() and catch errors when later * calling cogl_buffer_set_data() or cogl_buffer_map(). */ /* XXX: NB: for Cogl 2.0 we don't allow NULL data here but we can't * break the api for 1.x and so we keep the check for now. */ if (data) _cogl_buffer_set_data (COGL_BUFFER (buffer), 0, data, bytes, NULL); return buffer; } static void _cogl_attribute_buffer_free (CoglAttributeBuffer *array) { /* parent's destructor */ _cogl_buffer_fini (COGL_BUFFER (array)); g_slice_free (CoglAttributeBuffer, array); } muffin-6.4.1/cogl/cogl/cogl-depth-state.c0000664000175000017500000000651114723361714017125 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-depth-state-private.h" #include "cogl-depth-state.h" void cogl_depth_state_init (CoglDepthState *state) { state->magic = COGL_DEPTH_STATE_MAGIC; /* The same as the GL defaults */ state->test_enabled = FALSE; state->write_enabled = TRUE; state->test_function = COGL_DEPTH_TEST_FUNCTION_LESS; state->range_near = 0; state->range_far = 1; } void cogl_depth_state_set_test_enabled (CoglDepthState *state, gboolean enabled) { g_return_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC); state->test_enabled = enabled; } gboolean cogl_depth_state_get_test_enabled (CoglDepthState *state) { g_return_val_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); return state->test_enabled; } void cogl_depth_state_set_write_enabled (CoglDepthState *state, gboolean enabled) { g_return_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC); state->write_enabled = enabled; } gboolean cogl_depth_state_get_write_enabled (CoglDepthState *state) { g_return_val_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); return state->write_enabled; } void cogl_depth_state_set_test_function (CoglDepthState *state, CoglDepthTestFunction function) { g_return_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC); state->test_function = function; } CoglDepthTestFunction cogl_depth_state_get_test_function (CoglDepthState *state) { g_return_val_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); return state->test_function; } void cogl_depth_state_set_range (CoglDepthState *state, float near, float far) { g_return_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC); state->range_near = near; state->range_far = far; } void cogl_depth_state_get_range (CoglDepthState *state, float *near_out, float *far_out) { g_return_if_fail (state->magic == COGL_DEPTH_STATE_MAGIC); *near_out = state->range_near; *far_out = state->range_far; } muffin-6.4.1/cogl/cogl/cogl-renderer.h0000664000175000017500000003260314723361714016517 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_RENDERER_H__ #define __COGL_RENDERER_H__ #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-renderer * @short_description: Choosing a means to render * * A #CoglRenderer represents a means to render. It encapsulates the * selection of an underlying driver, such as OpenGL or OpenGL-ES and * a selection of a window system binding API such as GLX or EGL. * * A #CoglRenderer has two states, "unconnected" and "connected". When * a renderer is first instantiated using cogl_renderer_new() it is * unconnected so that it can be configured and constraints can be * specified for how the backend driver and window system should be * chosen. * * After configuration a #CoglRenderer can (optionally) be explicitly * connected using cogl_renderer_connect() which allows for the * handling of connection errors so that fallback configurations can * be tried if necessary. Applications that don't support any * fallbacks though can skip using cogl_renderer_connect() and leave * Cogl to automatically connect the renderer. * * Once you have a configured #CoglRenderer it can be used to create a * #CoglDisplay object using cogl_display_new(). * * Many applications don't need to explicitly use * cogl_renderer_new() or cogl_display_new() and can just jump * straight to cogl_context_new() and pass a %NULL display argument so * Cogl will automatically connect and setup a renderer and * display. */ /** * COGL_RENDERER_ERROR: * * An error domain for exceptions reported by Cogl */ #define COGL_RENDERER_ERROR cogl_renderer_error_quark () COGL_EXPORT uint32_t cogl_renderer_error_quark (void); typedef struct _CoglRenderer CoglRenderer; /** * cogl_renderer_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_renderer_get_gtype (void); /** * cogl_is_renderer: * @object: A #CoglObject pointer * * Determines if the given @object is a #CoglRenderer * * Return value: %TRUE if @object is a #CoglRenderer, else %FALSE. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_renderer (void *object); /** * cogl_renderer_new: * * Instantiates a new (unconnected) #CoglRenderer object. A * #CoglRenderer represents a means to render. It encapsulates the * selection of an underlying driver, such as OpenGL or OpenGL-ES and * a selection of a window system binding API such as GLX or EGL. * * While the renderer is unconnected it can be configured so that * applications may specify backend constraints, such as "must use * x11" for example via cogl_renderer_add_constraint(). * * There are also some platform specific configuration apis such * as cogl_xlib_renderer_set_foreign_display() that may also be * used while the renderer is unconnected. * * Once the renderer has been configured, then it may (optionally) be * explicitly connected using cogl_renderer_connect() which allows * errors to be handled gracefully and potentially fallback * configurations can be tried out if there are initial failures. * * If a renderer is not explicitly connected then cogl_display_new() * will automatically connect the renderer for you. If you don't * have any code to deal with error/fallback situations then its fine * to just let Cogl do the connection for you. * * Once you have setup your renderer then the next step is to create a * #CoglDisplay using cogl_display_new(). * * Many applications don't need to explicitly use * cogl_renderer_new() or cogl_display_new() and can just jump * straight to cogl_context_new() and pass a %NULL display argument * so Cogl will automatically connect and setup a renderer and * display. * * Return value: (transfer full): A newly created #CoglRenderer. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglRenderer * cogl_renderer_new (void); /* optional configuration APIs */ /** * CoglWinsysID: * @COGL_WINSYS_ID_ANY: Implies no preference for which backend is used * @COGL_WINSYS_ID_STUB: Use the no-op stub backend * @COGL_WINSYS_ID_GLX: Use the GLX window system binding API * @COGL_WINSYS_ID_EGL_XLIB: Use EGL with the X window system via XLib * * Identifies specific window system backends that Cogl supports. * * These can be used to query what backend Cogl is using or to try and * explicitly select a backend to use. */ typedef enum { COGL_WINSYS_ID_ANY, COGL_WINSYS_ID_STUB, COGL_WINSYS_ID_GLX, COGL_WINSYS_ID_EGL_XLIB, COGL_WINSYS_ID_CUSTOM, } CoglWinsysID; /** * cogl_renderer_set_winsys_id: * @renderer: A #CoglRenderer * @winsys_id: An ID of the winsys you explicitly want to use. * * This allows you to explicitly select a winsys backend to use instead * of letting Cogl automatically select a backend. * * if you select an unsupported backend then cogl_renderer_connect() * will fail and report an error. * * This may only be called on an un-connected #CoglRenderer. */ COGL_EXPORT void cogl_renderer_set_winsys_id (CoglRenderer *renderer, CoglWinsysID winsys_id); /** * cogl_renderer_get_winsys_id: * @renderer: A #CoglRenderer * * Queries which window system backend Cogl has chosen to use. * * This may only be called on a connected #CoglRenderer. * * Returns: The #CoglWinsysID corresponding to the chosen window * system backend. */ COGL_EXPORT CoglWinsysID cogl_renderer_get_winsys_id (CoglRenderer *renderer); /** * cogl_renderer_check_onscreen_template: (skip) * @renderer: A #CoglRenderer * @onscreen_template: A #CoglOnscreenTemplate * @error: A pointer to a #GError for reporting exceptions * * Tests if a given @onscreen_template can be supported with the given * @renderer. * * Return value: %TRUE if the @onscreen_template can be supported, * else %FALSE. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_renderer_check_onscreen_template (CoglRenderer *renderer, CoglOnscreenTemplate *onscreen_template, GError **error); /* Final connection API */ /** * cogl_renderer_connect: * @renderer: An unconnected #CoglRenderer * @error: a pointer to a #GError for reporting exceptions * * Connects the configured @renderer. Renderer connection isn't a * very active process, it basically just means validating that * any given constraint criteria can be satisfied and that a * usable driver and window system backend can be found. * * Return value: %TRUE if there was no error while connecting the * given @renderer. %FALSE if there was an error. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_renderer_connect (CoglRenderer *renderer, GError **error); /** * CoglRendererConstraint: * @COGL_RENDERER_CONSTRAINT_USES_X11: Require the renderer to be X11 based * @COGL_RENDERER_CONSTRAINT_USES_XLIB: Require the renderer to be X11 * based and use Xlib * @COGL_RENDERER_CONSTRAINT_USES_EGL: Require the renderer to be EGL based * * These constraint flags are hard-coded features of the different renderer * backends. Sometimes a platform may support multiple rendering options which * Cogl will usually choose from automatically. Some of these features are * important to higher level applications and frameworks though, such as * whether a renderer is X11 based because an application might only support * X11 based input handling. An application might also need to ensure EGL is * used internally too if they depend on access to an EGLDisplay for some * purpose. * * Applications should ideally minimize how many of these constraints * they depend on to ensure maximum portability. * * Since: 1.10 * Stability: unstable */ typedef enum { COGL_RENDERER_CONSTRAINT_USES_X11 = (1 << 0), COGL_RENDERER_CONSTRAINT_USES_XLIB = (1 << 1), COGL_RENDERER_CONSTRAINT_USES_EGL = (1 << 2), } CoglRendererConstraint; /** * cogl_renderer_add_constraint: * @renderer: An unconnected #CoglRenderer * @constraint: A #CoglRendererConstraint to add * * This adds a renderer selection @constraint. * * Applications should ideally minimize how many of these constraints they * depend on to ensure maximum portability. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_renderer_add_constraint (CoglRenderer *renderer, CoglRendererConstraint constraint); /** * cogl_renderer_remove_constraint: * @renderer: An unconnected #CoglRenderer * @constraint: A #CoglRendererConstraint to remove * * This removes a renderer selection @constraint. * * Applications should ideally minimize how many of these constraints they * depend on to ensure maximum portability. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_renderer_remove_constraint (CoglRenderer *renderer, CoglRendererConstraint constraint); /** * CoglDriver: * @COGL_DRIVER_ANY: Implies no preference for which driver is used * @COGL_DRIVER_NOP: A No-Op driver. * @COGL_DRIVER_GL: An OpenGL driver. * @COGL_DRIVER_GL3: An OpenGL driver using the core GL 3.1 profile * @COGL_DRIVER_GLES2: An OpenGL ES 2.0 driver. * * Identifiers for underlying hardware drivers that may be used by * Cogl for rendering. * * Since: 1.10 * Stability: unstable */ typedef enum { COGL_DRIVER_ANY, COGL_DRIVER_NOP, COGL_DRIVER_GL, COGL_DRIVER_GL3, COGL_DRIVER_GLES2, } CoglDriver; /** * cogl_renderer_set_driver: * @renderer: An unconnected #CoglRenderer * * Requests that Cogl should try to use a specific underlying driver * for rendering. * * If you select an unsupported driver then cogl_renderer_connect() * will fail and report an error. Most applications should not * explicitly select a driver and should rely on Cogl automatically * choosing the driver. * * This may only be called on an un-connected #CoglRenderer. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT void cogl_renderer_set_driver (CoglRenderer *renderer, CoglDriver driver); /** * cogl_renderer_get_driver: * @renderer: A connected #CoglRenderer * * Queries what underlying driver is being used by Cogl. * * This may only be called on a connected #CoglRenderer. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglDriver cogl_renderer_get_driver (CoglRenderer *renderer); /** * CoglOutputCallback: * @output: The current display output being iterated * @user_data: The user pointer passed to * cogl_renderer_foreach_output() * * A callback type that can be passed to * cogl_renderer_foreach_output() for iterating display outputs for a * given renderer. * * Since: 1.14 * Stability: Unstable */ typedef void (*CoglOutputCallback) (CoglOutput *output, void *user_data); /** * cogl_renderer_foreach_output: * @renderer: A connected #CoglRenderer * @callback: (scope call): A #CoglOutputCallback to be called for * each display output * @user_data: A user pointer to be passed to @callback * * Iterates all known display outputs for the given @renderer and * passes a corresponding #CoglOutput pointer to the given @callback * for each one, along with the given @user_data. * * Since: 1.14 * Stability: Unstable */ COGL_EXPORT void cogl_renderer_foreach_output (CoglRenderer *renderer, CoglOutputCallback callback, void *user_data); /** * cogl_renderer_create_dma_buf: (skip) * @renderer: A #CoglRenderer * @width: width of the new * @height: height of the new * @error: (nullable): return location for a #GError * * Creates a new #CoglFramebuffer with @width x @height, and format * hardcoded to XRGB, and exports the new framebuffer's DMA buffer * handle. * * Returns: (nullable)(transfer full): a #CoglDmaBufHandle. The * return result must be released with cogl_dma_buf_handle_free() * after use. */ COGL_EXPORT CoglDmaBufHandle * cogl_renderer_create_dma_buf (CoglRenderer *renderer, int width, int height, GError **error); G_END_DECLS #endif /* __COGL_RENDERER_H__ */ muffin-6.4.1/cogl/cogl/cogl-attribute.h0000664000175000017500000004632514723361714016722 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_ATTRIBUTE_H__ #define __COGL_ATTRIBUTE_H__ /* We forward declare the CoglAttribute type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglAttribute CoglAttribute; #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-attribute * @short_description: Functions for declaring and drawing vertex * attributes * * FIXME */ /** * cogl_attribute_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_attribute_get_gtype (void); /** * cogl_attribute_new: (constructor) * @attribute_buffer: The #CoglAttributeBuffer containing the actual * attribute data * @name: The name of the attribute (used to reference it from GLSL) * @stride: The number of bytes to jump to get to the next attribute * value for the next vertex. (Usually * sizeof (MyVertex)) * @offset: The byte offset from the start of @attribute_buffer for * the first attribute value. (Usually * offsetof (MyVertex, component0) * @components: The number of components (e.g. 4 for an rgba color or * 3 for and (x,y,z) position) * @type: FIXME * * Describes the layout for a list of vertex attribute values (For * example, a list of texture coordinates or colors). * * The @name is used to access the attribute inside a GLSL vertex * shader and there are some special names you should use if they are * applicable: * * "cogl_position_in" (used for vertex positions) * "cogl_color_in" (used for vertex colors) * "cogl_tex_coord0_in", "cogl_tex_coord1", ... * (used for vertex texture coordinates) * "cogl_normal_in" (used for vertex normals) * "cogl_point_size_in" (used to set the size of points * per-vertex. Note this can only be used if * %COGL_FEATURE_ID_POINT_SIZE_ATTRIBUTE is advertised and * cogl_pipeline_set_per_vertex_point_size() is called on the pipeline. * * * * The attribute values corresponding to different vertices can either * be tightly packed or interleaved with other attribute values. For * example it's common to define a structure for a single vertex like: * |[ * typedef struct * { * float x, y, z; /* position attribute */ * float s, t; /* texture coordinate attribute */ * } MyVertex; * ]| * * And then create an array of vertex data something like: * |[ * MyVertex vertices[100] = { .... } * ]| * * In this case, to describe either the position or texture coordinate * attribute you have to move sizeof (MyVertex) bytes to * move from one vertex to the next. This is called the attribute * @stride. If you weren't interleving attributes and you instead had * a packed array of float x, y pairs then the attribute stride would * be (2 * sizeof (float)). So the @stride is the number of * bytes to move to find the attribute value of the next vertex. * * Normally a list of attributes starts at the beginning of an array. * So for the MyVertex example above the @offset is the * offset inside the MyVertex structure to the first * component of the attribute. For the texture coordinate attribute * the offset would be offsetof (MyVertex, s) or instead of * using the offsetof macro you could use sizeof (float) * * 3. If you've divided your @array into blocks of non-interleved * attributes then you will need to calculate the @offset as the number of * bytes in blocks preceding the attribute you're describing. * * An attribute often has more than one component. For example a color * is often comprised of 4 red, green, blue and alpha @components, and a * position may be comprised of 2 x and y @components. You should aim * to keep the number of components to a minimum as more components * means more data needs to be mapped into the GPU which can be a * bottlneck when dealing with a large number of vertices. * * Finally you need to specify the component data type. Here you * should aim to use the smallest type that meets your precision * requirements. Again the larger the type then more data needs to be * mapped into the GPU which can be a bottlneck when dealing with * a large number of vertices. * * Return value: (transfer full): A newly allocated #CoglAttribute * describing the layout for a list of attribute values * stored in @array. * * Since: 1.4 * Stability: Unstable */ /* XXX: look for a precedent to see if the stride/offset args should * have a different order. */ COGL_EXPORT CoglAttribute * cogl_attribute_new (CoglAttributeBuffer *attribute_buffer, const char *name, size_t stride, size_t offset, int components, CoglAttributeType type); /** * cogl_attribute_new_const_1f: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @value: The constant value for the attribute * * Creates a new, single component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constant @value is a single precision floating point scalar * which should have a corresponding declaration in GLSL code like: * * [| * attribute float name; * |] * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant @value. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_1f (CoglContext *context, const char *name, float value); /** * cogl_attribute_new_const_2f: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @component0: The first component of a 2 component vector * @component1: The second component of a 2 component vector * * Creates a new, 2 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (@component0, @component1) represent a 2 component * float vector which should have a corresponding declaration in GLSL * code like: * * [| * attribute vec2 name; * |] * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_2f (CoglContext *context, const char *name, float component0, float component1); /** * cogl_attribute_new_const_3f: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @component0: The first component of a 3 component vector * @component1: The second component of a 3 component vector * @component2: The third component of a 3 component vector * * Creates a new, 3 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (@component0, @component1, @component2) represent a 3 * component float vector which should have a corresponding * declaration in GLSL code like: * * [| * attribute vec3 name; * |] * * unless the built in name "cogl_normal_in" is being used where no * explicit GLSL declaration need be made. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_3f (CoglContext *context, const char *name, float component0, float component1, float component2); /** * cogl_attribute_new_const_4f: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @component0: The first component of a 4 component vector * @component1: The second component of a 4 component vector * @component2: The third component of a 4 component vector * @component3: The fourth component of a 4 component vector * * Creates a new, 4 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (@component0, @component1, @component2, @constant3) * represent a 4 component float vector which should have a * corresponding declaration in GLSL code like: * * [| * attribute vec4 name; * |] * * unless one of the built in names "cogl_color_in", * "cogl_tex_coord0_in or "cogl_tex_coord1_in" etc is being used where * no explicit GLSL declaration need be made. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_4f (CoglContext *context, const char *name, float component0, float component1, float component2, float component3); /** * cogl_attribute_new_const_2fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @value: A pointer to a 2 component float vector * * Creates a new, 2 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (value[0], value[1]) represent a 2 component float * vector which should have a corresponding declaration in GLSL code * like: * * [| * attribute vec2 name; * |] * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_2fv (CoglContext *context, const char *name, const float *value); /** * cogl_attribute_new_const_3fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @value: A pointer to a 3 component float vector * * Creates a new, 3 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (value[0], value[1], value[2]) represent a 3 * component float vector which should have a corresponding * declaration in GLSL code like: * * [| * attribute vec3 name; * |] * * unless the built in name "cogl_normal_in" is being used where no * explicit GLSL declaration need be made. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_3fv (CoglContext *context, const char *name, const float *value); /** * cogl_attribute_new_const_4fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @value: A pointer to a 4 component float vector * * Creates a new, 4 component, attribute whose value remains * constant across all the vertices of a primitive without needing to * duplicate the value for each vertex. * * The constants (value[0], value[1], value[2], value[3]) represent a * 4 component float vector which should have a corresponding * declaration in GLSL code like: * * [| * attribute vec4 name; * |] * * unless one of the built in names "cogl_color_in", * "cogl_tex_coord0_in or "cogl_tex_coord1_in" etc is being used where * no explicit GLSL declaration need be made. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant vector. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_4fv (CoglContext *context, const char *name, const float *value); /** * cogl_attribute_new_const_2x2fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @matrix2x2: A pointer to a 2 by 2 matrix * @transpose: Whether the matrix should be transposed on upload or * not * * Creates a new matrix attribute whose value remains constant * across all the vertices of a primitive without needing to duplicate * the value for each vertex. * * @matrix2x2 represent a square 2 by 2 matrix specified in * column-major order (each pair of consecutive numbers represents a * column) which should have a corresponding declaration in GLSL code * like: * * [| * attribute mat2 name; * |] * * If @transpose is %TRUE then all matrix components are rotated * around the diagonal of the matrix such that the first column * becomes the first row and the second column becomes the second row. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant matrix. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_2x2fv (CoglContext *context, const char *name, const float *matrix2x2, gboolean transpose); /** * cogl_attribute_new_const_3x3fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @matrix3x3: A pointer to a 3 by 3 matrix * @transpose: Whether the matrix should be transposed on upload or * not * * Creates a new matrix attribute whose value remains constant * across all the vertices of a primitive without needing to duplicate * the value for each vertex. * * @matrix3x3 represent a square 3 by 3 matrix specified in * column-major order (each triple of consecutive numbers represents a * column) which should have a corresponding declaration in GLSL code * like: * * [| * attribute mat3 name; * |] * * If @transpose is %TRUE then all matrix components are rotated * around the diagonal of the matrix such that the first column * becomes the first row and the second column becomes the second row * etc. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant matrix. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_3x3fv (CoglContext *context, const char *name, const float *matrix3x3, gboolean transpose); /** * cogl_attribute_new_const_4x4fv: * @context: A #CoglContext * @name: The name of the attribute (used to reference it from GLSL) * @matrix4x4: A pointer to a 4 by 4 matrix * @transpose: Whether the matrix should be transposed on upload or * not * * Creates a new matrix attribute whose value remains constant * across all the vertices of a primitive without needing to duplicate * the value for each vertex. * * @matrix4x4 represent a square 4 by 4 matrix specified in * column-major order (each 4-tuple of consecutive numbers represents a * column) which should have a corresponding declaration in GLSL code * like: * * [| * attribute mat4 name; * |] * * If @transpose is %TRUE then all matrix components are rotated * around the diagonal of the matrix such that the first column * becomes the first row and the second column becomes the second row * etc. * * Return value: (transfer full): A newly allocated #CoglAttribute * representing the given constant matrix. */ COGL_EXPORT CoglAttribute * cogl_attribute_new_const_4x4fv (CoglContext *context, const char *name, const float *matrix4x4, gboolean transpose); /** * cogl_attribute_set_normalized: * @attribute: A #CoglAttribute * @normalized: The new value for the normalized property. * * Sets whether fixed point attribute types are mapped to the range * 0→1. For example when this property is TRUE and a * %COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE type is used then the value 255 * will be mapped to 1.0. * * The default value of this property depends on the name of the * attribute. For the builtin properties cogl_color_in and * cogl_normal_in it will default to TRUE and for all other names it * will default to FALSE. * * Stability: unstable * Since: 1.10 */ COGL_EXPORT void cogl_attribute_set_normalized (CoglAttribute *attribute, gboolean normalized); /** * cogl_attribute_get_normalized: * @attribute: A #CoglAttribute * * Return value: the value of the normalized property set with * cogl_attribute_set_normalized(). * * Stability: unstable * Since: 1.10 */ COGL_EXPORT gboolean cogl_attribute_get_normalized (CoglAttribute *attribute); /** * cogl_attribute_get_buffer: * @attribute: A #CoglAttribute * * Return value: (transfer none): the #CoglAttributeBuffer that was * set with cogl_attribute_set_buffer() or cogl_attribute_new(). * * Stability: unstable * Since: 1.10 */ COGL_EXPORT CoglAttributeBuffer * cogl_attribute_get_buffer (CoglAttribute *attribute); /** * cogl_attribute_set_buffer: * @attribute: A #CoglAttribute * @attribute_buffer: A #CoglAttributeBuffer * * Sets a new #CoglAttributeBuffer for the attribute. * * Stability: unstable * Since: 1.10 */ COGL_EXPORT void cogl_attribute_set_buffer (CoglAttribute *attribute, CoglAttributeBuffer *attribute_buffer); /** * cogl_is_attribute: * @object: A #CoglObject * * Gets whether the given object references a #CoglAttribute. * * Return value: %TRUE if the @object references a #CoglAttribute, * %FALSE otherwise */ COGL_EXPORT gboolean cogl_is_attribute (void *object); G_END_DECLS #endif /* __COGL_ATTRIBUTE_H__ */ muffin-6.4.1/cogl/cogl/driver/0000775000175000017500000000000014723361714015105 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/driver/gl/0000775000175000017500000000000014723361714015507 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl.c0000664000175000017500000010410314723361714022774 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-offscreen.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-pipeline-cache.h" #include "cogl-pipeline-state-private.h" #include "cogl-attribute-private.h" #include "cogl-framebuffer-private.h" #include "driver/gl/cogl-pipeline-fragend-glsl-private.h" #include "driver/gl/cogl-pipeline-vertend-glsl-private.h" #include "driver/gl/cogl-pipeline-progend-glsl-private.h" #include "deprecated/cogl-program-private.h" /* These are used to generalise updating some uniforms that are required when building for drivers missing some fixed function state that we use */ typedef void (* UpdateUniformFunc) (CoglPipeline *pipeline, int uniform_location, void *getter_func); static void update_float_uniform (CoglPipeline *pipeline, int uniform_location, void *getter_func); typedef struct { const char *uniform_name; void *getter_func; UpdateUniformFunc update_func; CoglPipelineState change; } BuiltinUniformData; static BuiltinUniformData builtin_uniforms[] = { { "cogl_point_size_in", cogl_pipeline_get_point_size, update_float_uniform, COGL_PIPELINE_STATE_POINT_SIZE }, { "_cogl_alpha_test_ref", cogl_pipeline_get_alpha_test_reference, update_float_uniform, COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE }, }; const CoglPipelineProgend _cogl_pipeline_glsl_progend; typedef struct _UnitState { unsigned int dirty_combine_constant:1; unsigned int dirty_texture_matrix:1; GLint combine_constant_uniform; GLint texture_matrix_uniform; } UnitState; typedef struct { unsigned int ref_count; /* Age that the user program had last time we generated a GL program. If it's different then we need to relink the program */ unsigned int user_program_age; GLuint program; unsigned long dirty_builtin_uniforms; GLint builtin_uniform_locations[G_N_ELEMENTS (builtin_uniforms)]; GLint modelview_uniform; GLint projection_uniform; GLint mvp_uniform; CoglMatrixEntryCache projection_cache; CoglMatrixEntryCache modelview_cache; /* We need to track the last pipeline that the program was used with * so know if we need to update all of the uniforms */ CoglPipeline *last_used_for_pipeline; /* Array of GL uniform locations indexed by Cogl's uniform location. We are careful only to allocated this array if a custom uniform is actually set */ GArray *uniform_locations; /* Array of attribute locations. */ GArray *attribute_locations; /* The 'flip' uniform is used to flip the geometry upside-down when the framebuffer requires it only when there are vertex snippets. Otherwise this is acheived using the projection matrix */ GLint flip_uniform; int flushed_flip_state; UnitState *unit_state; CoglPipelineCacheEntry *cache_entry; } CoglPipelineProgramState; static CoglUserDataKey program_state_key; static CoglPipelineProgramState * get_program_state (CoglPipeline *pipeline) { return cogl_object_get_user_data (COGL_OBJECT (pipeline), &program_state_key); } #define UNIFORM_LOCATION_UNKNOWN -2 #define ATTRIBUTE_LOCATION_UNKNOWN -2 /* Under GLES2 the vertex attribute API needs to query the attribute numbers because it can't used the fixed function API to set the builtin attributes. We cache the attributes here because the progend knows when the program is changed so it can clear the cache. This should always be called after the pipeline is flushed so they can assert that the gl program is valid */ /* All attributes names get internally mapped to a global set of * sequential indices when they are setup which we need to need to * then be able to map to a GL attribute location once we have * a linked GLSL program */ int _cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline, int name_index) { CoglPipelineProgramState *program_state = get_program_state (pipeline); int *locations; _COGL_GET_CONTEXT (ctx, -1); g_return_val_if_fail (program_state != NULL, -1); g_return_val_if_fail (program_state->program != 0, -1); if (G_UNLIKELY (program_state->attribute_locations == NULL)) program_state->attribute_locations = g_array_new (FALSE, FALSE, sizeof (int)); if (G_UNLIKELY (program_state->attribute_locations->len <= name_index)) { int i = program_state->attribute_locations->len; g_array_set_size (program_state->attribute_locations, name_index + 1); for (; i < program_state->attribute_locations->len; i++) g_array_index (program_state->attribute_locations, int, i) = ATTRIBUTE_LOCATION_UNKNOWN; } locations = &g_array_index (program_state->attribute_locations, int, 0); if (locations[name_index] == ATTRIBUTE_LOCATION_UNKNOWN) { CoglAttributeNameState *name_state = g_array_index (ctx->attribute_name_index_map, CoglAttributeNameState *, name_index); g_return_val_if_fail (name_state != NULL, 0); GE_RET( locations[name_index], ctx, glGetAttribLocation (program_state->program, name_state->name) ); } return locations[name_index]; } static void clear_attribute_cache (CoglPipelineProgramState *program_state) { if (program_state->attribute_locations) { g_array_free (program_state->attribute_locations, TRUE); program_state->attribute_locations = NULL; } } static void clear_flushed_matrix_stacks (CoglPipelineProgramState *program_state) { _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); _cogl_matrix_entry_cache_init (&program_state->projection_cache); _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); _cogl_matrix_entry_cache_init (&program_state->modelview_cache); } static CoglPipelineProgramState * program_state_new (int n_layers, CoglPipelineCacheEntry *cache_entry) { CoglPipelineProgramState *program_state; program_state = g_slice_new (CoglPipelineProgramState); program_state->ref_count = 1; program_state->program = 0; program_state->unit_state = g_new (UnitState, n_layers); program_state->uniform_locations = NULL; program_state->attribute_locations = NULL; program_state->cache_entry = cache_entry; _cogl_matrix_entry_cache_init (&program_state->modelview_cache); _cogl_matrix_entry_cache_init (&program_state->projection_cache); return program_state; } static void destroy_program_state (void *user_data, void *instance) { CoglPipelineProgramState *program_state = user_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* If the program state was last used for this pipeline then clear it so that if same address gets used again for a new pipeline then we won't think it's the same pipeline and avoid updating the uniforms */ if (program_state->last_used_for_pipeline == instance) program_state->last_used_for_pipeline = NULL; if (program_state->cache_entry && program_state->cache_entry->pipeline != instance) program_state->cache_entry->usage_count--; if (--program_state->ref_count == 0) { clear_attribute_cache (program_state); _cogl_matrix_entry_cache_destroy (&program_state->projection_cache); _cogl_matrix_entry_cache_destroy (&program_state->modelview_cache); if (program_state->program) GE( ctx, glDeleteProgram (program_state->program) ); g_free (program_state->unit_state); if (program_state->uniform_locations) g_array_free (program_state->uniform_locations, TRUE); g_slice_free (CoglPipelineProgramState, program_state); } } static void set_program_state (CoglPipeline *pipeline, CoglPipelineProgramState *program_state) { if (program_state) { program_state->ref_count++; /* If we're not setting the state on the template pipeline then * mark it as a usage of the pipeline cache entry */ if (program_state->cache_entry && program_state->cache_entry->pipeline != pipeline) program_state->cache_entry->usage_count++; } _cogl_object_set_user_data (COGL_OBJECT (pipeline), &program_state_key, program_state, destroy_program_state); } static void dirty_program_state (CoglPipeline *pipeline) { cogl_object_set_user_data (COGL_OBJECT (pipeline), &program_state_key, NULL, NULL); } static void link_program (GLint gl_program) { GLint link_status; _COGL_GET_CONTEXT (ctx, NO_RETVAL); GE( ctx, glLinkProgram (gl_program) ); GE( ctx, glGetProgramiv (gl_program, GL_LINK_STATUS, &link_status) ); if (!link_status) { GLint log_length; GLsizei out_log_length; char *log; GE( ctx, glGetProgramiv (gl_program, GL_INFO_LOG_LENGTH, &log_length) ); log = g_malloc (log_length); GE( ctx, glGetProgramInfoLog (gl_program, log_length, &out_log_length, log) ); g_warning ("Failed to link GLSL program:\n%.*s\n", log_length, log); g_free (log); } } typedef struct { int unit; GLuint gl_program; gboolean update_all; CoglPipelineProgramState *program_state; } UpdateUniformsState; static gboolean get_uniform_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { UpdateUniformsState *state = user_data; CoglPipelineProgramState *program_state = state->program_state; UnitState *unit_state = &program_state->unit_state[state->unit]; GLint uniform_location; _COGL_GET_CONTEXT (ctx, FALSE); /* We can reuse the source buffer to create the uniform name because the program has now been linked */ g_string_set_size (ctx->codegen_source_buffer, 0); g_string_append_printf (ctx->codegen_source_buffer, "cogl_sampler%i", layer_index); GE_RET( uniform_location, ctx, glGetUniformLocation (state->gl_program, ctx->codegen_source_buffer->str) ); /* We can set the uniform immediately because the samplers are the unit index not the texture object number so it will never change. Unfortunately GL won't let us use a constant instead of a uniform */ if (uniform_location != -1) GE( ctx, glUniform1i (uniform_location, state->unit) ); g_string_set_size (ctx->codegen_source_buffer, 0); g_string_append_printf (ctx->codegen_source_buffer, "_cogl_layer_constant_%i", layer_index); GE_RET( uniform_location, ctx, glGetUniformLocation (state->gl_program, ctx->codegen_source_buffer->str) ); unit_state->combine_constant_uniform = uniform_location; g_string_set_size (ctx->codegen_source_buffer, 0); g_string_append_printf (ctx->codegen_source_buffer, "cogl_texture_matrix[%i]", layer_index); GE_RET( uniform_location, ctx, glGetUniformLocation (state->gl_program, ctx->codegen_source_buffer->str) ); unit_state->texture_matrix_uniform = uniform_location; state->unit++; return TRUE; } static gboolean update_constants_cb (CoglPipeline *pipeline, int layer_index, void *user_data) { UpdateUniformsState *state = user_data; CoglPipelineProgramState *program_state = state->program_state; UnitState *unit_state = &program_state->unit_state[state->unit++]; _COGL_GET_CONTEXT (ctx, FALSE); if (unit_state->combine_constant_uniform != -1 && (state->update_all || unit_state->dirty_combine_constant)) { float constant[4]; _cogl_pipeline_get_layer_combine_constant (pipeline, layer_index, constant); GE (ctx, glUniform4fv (unit_state->combine_constant_uniform, 1, constant)); unit_state->dirty_combine_constant = FALSE; } if (unit_state->texture_matrix_uniform != -1 && (state->update_all || unit_state->dirty_texture_matrix)) { const CoglMatrix *matrix; const float *array; matrix = _cogl_pipeline_get_layer_matrix (pipeline, layer_index); array = cogl_matrix_get_array (matrix); GE (ctx, glUniformMatrix4fv (unit_state->texture_matrix_uniform, 1, FALSE, array)); unit_state->dirty_texture_matrix = FALSE; } return TRUE; } static void update_builtin_uniforms (CoglContext *context, CoglPipeline *pipeline, GLuint gl_program, CoglPipelineProgramState *program_state) { int i; if (program_state->dirty_builtin_uniforms == 0) return; for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) if ((program_state->dirty_builtin_uniforms & (1 << i)) && program_state->builtin_uniform_locations[i] != -1) builtin_uniforms[i].update_func (pipeline, program_state ->builtin_uniform_locations[i], builtin_uniforms[i].getter_func); program_state->dirty_builtin_uniforms = 0; } typedef struct { CoglPipelineProgramState *program_state; unsigned long *uniform_differences; int n_differences; CoglContext *ctx; const CoglBoxedValue *values; int value_index; } FlushUniformsClosure; static gboolean flush_uniform_cb (int uniform_num, void *user_data) { FlushUniformsClosure *data = user_data; if (COGL_FLAGS_GET (data->uniform_differences, uniform_num)) { GArray *uniform_locations; GLint uniform_location; if (data->program_state->uniform_locations == NULL) data->program_state->uniform_locations = g_array_new (FALSE, FALSE, sizeof (GLint)); uniform_locations = data->program_state->uniform_locations; if (uniform_locations->len <= uniform_num) { unsigned int old_len = uniform_locations->len; g_array_set_size (uniform_locations, uniform_num + 1); while (old_len <= uniform_num) { g_array_index (uniform_locations, GLint, old_len) = UNIFORM_LOCATION_UNKNOWN; old_len++; } } uniform_location = g_array_index (uniform_locations, GLint, uniform_num); if (uniform_location == UNIFORM_LOCATION_UNKNOWN) { const char *uniform_name = g_ptr_array_index (data->ctx->uniform_names, uniform_num); uniform_location = data->ctx->glGetUniformLocation (data->program_state->program, uniform_name); g_array_index (uniform_locations, GLint, uniform_num) = uniform_location; } if (uniform_location != -1) _cogl_boxed_value_set_uniform (data->ctx, uniform_location, data->values + data->value_index); data->n_differences--; COGL_FLAGS_SET (data->uniform_differences, uniform_num, FALSE); } data->value_index++; return data->n_differences > 0; } static void _cogl_pipeline_progend_glsl_flush_uniforms (CoglPipeline *pipeline, CoglPipelineProgramState * program_state, GLuint gl_program, gboolean program_changed) { CoglPipelineUniformsState *uniforms_state; FlushUniformsClosure data; int n_uniform_longs; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) uniforms_state = &pipeline->big_state->uniforms_state; else uniforms_state = NULL; data.program_state = program_state; data.ctx = ctx; n_uniform_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names); data.uniform_differences = g_newa (unsigned long, n_uniform_longs); /* Try to find a common ancestor for the values that were already flushed on the pipeline that this program state was last used for so we can avoid flushing those */ if (program_changed || program_state->last_used_for_pipeline == NULL) { if (program_changed) { /* The program has changed so all of the uniform locations are invalid */ if (program_state->uniform_locations) g_array_set_size (program_state->uniform_locations, 0); } /* We need to flush everything so mark all of the uniforms as dirty */ memset (data.uniform_differences, 0xff, n_uniform_longs * sizeof (unsigned long)); data.n_differences = G_MAXINT; } else if (program_state->last_used_for_pipeline) { int i; memset (data.uniform_differences, 0, n_uniform_longs * sizeof (unsigned long)); _cogl_pipeline_compare_uniform_differences (data.uniform_differences, program_state->last_used_for_pipeline, pipeline); /* We need to be sure to flush any uniforms that have changed since the last flush */ if (uniforms_state) _cogl_bitmask_set_flags (&uniforms_state->changed_mask, data.uniform_differences); /* Count the number of differences. This is so we can stop early when we've flushed all of them */ data.n_differences = 0; for (i = 0; i < n_uniform_longs; i++) data.n_differences += _cogl_util_popcountl (data.uniform_differences[i]); } while (pipeline && data.n_differences > 0) { if (pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS) { const CoglPipelineUniformsState *parent_uniforms_state = &pipeline->big_state->uniforms_state; data.values = parent_uniforms_state->override_values; data.value_index = 0; _cogl_bitmask_foreach (&parent_uniforms_state->override_mask, flush_uniform_cb, &data); } pipeline = _cogl_pipeline_get_parent (pipeline); } if (uniforms_state) _cogl_bitmask_clear_all (&uniforms_state->changed_mask); } static gboolean _cogl_pipeline_progend_glsl_start (CoglPipeline *pipeline) { return TRUE; } static void _cogl_pipeline_progend_glsl_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { CoglPipelineProgramState *program_state; GLuint gl_program; gboolean program_changed = FALSE; UpdateUniformsState state; CoglProgram *user_program; CoglPipelineCacheEntry *cache_entry = NULL; _COGL_GET_CONTEXT (ctx, NO_RETVAL); program_state = get_program_state (pipeline); user_program = cogl_pipeline_get_user_program (pipeline); if (program_state == NULL) { CoglPipeline *authority; /* Get the authority for anything affecting program state. This should include both fragment codegen state and vertex codegen state */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, (_cogl_pipeline_get_state_for_vertex_codegen (ctx) | _cogl_pipeline_get_state_for_fragment_codegen (ctx)) & ~COGL_PIPELINE_STATE_LAYERS, _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); program_state = get_program_state (authority); if (program_state == NULL) { /* Check if there is already a similar cached pipeline whose program state we can share */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { cache_entry = _cogl_pipeline_cache_get_combined_template (ctx->pipeline_cache, authority); program_state = get_program_state (cache_entry->pipeline); } if (program_state) program_state->ref_count++; else program_state = program_state_new (cogl_pipeline_get_n_layers (authority), cache_entry); set_program_state (authority, program_state); program_state->ref_count--; if (cache_entry) set_program_state (cache_entry->pipeline, program_state); } if (authority != pipeline) set_program_state (pipeline, program_state); } /* If the program has changed since the last link then we do * need to relink */ if (program_state->program && user_program && user_program->age != program_state->user_program_age) { GE( ctx, glDeleteProgram (program_state->program) ); program_state->program = 0; } if (program_state->program == 0) { GLuint backend_shader; GSList *l; GE_RET( program_state->program, ctx, glCreateProgram () ); /* Attach all of the shader from the user program */ if (user_program) { for (l = user_program->attached_shaders; l; l = l->next) { CoglShader *shader = l->data; _cogl_shader_compile_real (shader, pipeline); GE( ctx, glAttachShader (program_state->program, shader->gl_handle) ); } program_state->user_program_age = user_program->age; } /* Attach any shaders from the GLSL backends */ if ((backend_shader = _cogl_pipeline_fragend_glsl_get_shader (pipeline))) GE( ctx, glAttachShader (program_state->program, backend_shader) ); if ((backend_shader = _cogl_pipeline_vertend_glsl_get_shader (pipeline))) GE( ctx, glAttachShader (program_state->program, backend_shader) ); /* XXX: OpenGL as a special case requires the vertex position to * be bound to generic attribute 0 so for simplicity we * unconditionally bind the cogl_position_in attribute here... */ GE( ctx, glBindAttribLocation (program_state->program, 0, "cogl_position_in")); link_program (program_state->program); program_changed = TRUE; } gl_program = program_state->program; if (ctx->current_gl_program != gl_program) { _cogl_gl_util_clear_gl_errors (ctx); ctx->glUseProgram (gl_program); if (_cogl_gl_util_get_error (ctx) == GL_NO_ERROR) ctx->current_gl_program = gl_program; else { GE( ctx, glUseProgram (0) ); ctx->current_gl_program = 0; } } state.unit = 0; state.gl_program = gl_program; state.program_state = program_state; if (program_changed) { cogl_pipeline_foreach_layer (pipeline, get_uniform_cb, &state); clear_attribute_cache (program_state); GE_RET (program_state->flip_uniform, ctx, glGetUniformLocation (gl_program, "_cogl_flip_vector")); program_state->flushed_flip_state = -1; } state.unit = 0; state.update_all = (program_changed || program_state->last_used_for_pipeline != pipeline); cogl_pipeline_foreach_layer (pipeline, update_constants_cb, &state); if (program_changed) { int i; clear_flushed_matrix_stacks (program_state); for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) GE_RET( program_state->builtin_uniform_locations[i], ctx, glGetUniformLocation (gl_program, builtin_uniforms[i].uniform_name) ); GE_RET( program_state->modelview_uniform, ctx, glGetUniformLocation (gl_program, "cogl_modelview_matrix") ); GE_RET( program_state->projection_uniform, ctx, glGetUniformLocation (gl_program, "cogl_projection_matrix") ); GE_RET( program_state->mvp_uniform, ctx, glGetUniformLocation (gl_program, "cogl_modelview_projection_matrix") ); } if (program_changed || program_state->last_used_for_pipeline != pipeline) program_state->dirty_builtin_uniforms = ~(unsigned long) 0; update_builtin_uniforms (ctx, pipeline, gl_program, program_state); _cogl_pipeline_progend_glsl_flush_uniforms (pipeline, program_state, gl_program, program_changed); if (user_program) _cogl_program_flush_uniforms (user_program, gl_program, program_changed); /* We need to track the last pipeline that the program was used with * so know if we need to update all of the uniforms */ program_state->last_used_for_pipeline = pipeline; } static void _cogl_pipeline_progend_glsl_pre_change_notify (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if ((change & (_cogl_pipeline_get_state_for_vertex_codegen (ctx) | _cogl_pipeline_get_state_for_fragment_codegen (ctx)))) { dirty_program_state (pipeline); } else { int i; for (i = 0; i < G_N_ELEMENTS (builtin_uniforms); i++) if (change & builtin_uniforms[i].change) { CoglPipelineProgramState *program_state = get_program_state (pipeline); if (program_state) program_state->dirty_builtin_uniforms |= 1 << i; return; } } } /* NB: layers are considered immutable once they have any dependants * so although multiple pipelines can end up depending on a single * static layer, we can guarantee that if a layer is being *changed* * then it can only have one pipeline depending on it. * * XXX: Don't forget this is *pre* change, we can't read the new value * yet! */ static void _cogl_pipeline_progend_glsl_layer_pre_change_notify ( CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if ((change & (_cogl_pipeline_get_layer_state_for_fragment_codegen (ctx) | COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN))) { dirty_program_state (owner); } else if (change & COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT) { CoglPipelineProgramState *program_state = get_program_state (owner); if (program_state) { int unit_index = _cogl_pipeline_layer_get_unit_index (layer); program_state->unit_state[unit_index].dirty_combine_constant = TRUE; } } else if (change & COGL_PIPELINE_LAYER_STATE_USER_MATRIX) { CoglPipelineProgramState *program_state = get_program_state (owner); if (program_state) { int unit_index = _cogl_pipeline_layer_get_unit_index (layer); program_state->unit_state[unit_index].dirty_texture_matrix = TRUE; } } } static void _cogl_pipeline_progend_glsl_pre_paint (CoglPipeline *pipeline, CoglFramebuffer *framebuffer) { gboolean needs_flip; CoglMatrixEntry *projection_entry; CoglMatrixEntry *modelview_entry; CoglPipelineProgramState *program_state; gboolean modelview_changed; gboolean projection_changed; gboolean need_modelview; gboolean need_projection; CoglMatrix modelview, projection; _COGL_GET_CONTEXT (ctx, NO_RETVAL); program_state = get_program_state (pipeline); projection_entry = ctx->current_projection_entry; modelview_entry = ctx->current_modelview_entry; /* An initial pipeline is flushed while creating the context. At this point there are no matrices selected so we can't do anything */ if (modelview_entry == NULL || projection_entry == NULL) return; needs_flip = cogl_is_offscreen (ctx->current_draw_buffer); projection_changed = _cogl_matrix_entry_cache_maybe_update (&program_state->projection_cache, projection_entry, (needs_flip && program_state->flip_uniform == -1)); modelview_changed = _cogl_matrix_entry_cache_maybe_update (&program_state->modelview_cache, modelview_entry, /* never flip modelview */ FALSE); if (modelview_changed || projection_changed) { if (program_state->mvp_uniform != -1) need_modelview = need_projection = TRUE; else { need_projection = (program_state->projection_uniform != -1 && projection_changed); need_modelview = (program_state->modelview_uniform != -1 && modelview_changed); } if (need_modelview) cogl_matrix_entry_get (modelview_entry, &modelview); if (need_projection) { if (needs_flip && program_state->flip_uniform == -1) { CoglMatrix tmp_matrix; cogl_matrix_entry_get (projection_entry, &tmp_matrix); cogl_matrix_multiply (&projection, &ctx->y_flip_matrix, &tmp_matrix); } else cogl_matrix_entry_get (projection_entry, &projection); } if (projection_changed && program_state->projection_uniform != -1) GE (ctx, glUniformMatrix4fv (program_state->projection_uniform, 1, /* count */ FALSE, /* transpose */ cogl_matrix_get_array (&projection))); if (modelview_changed && program_state->modelview_uniform != -1) GE (ctx, glUniformMatrix4fv (program_state->modelview_uniform, 1, /* count */ FALSE, /* transpose */ cogl_matrix_get_array (&modelview))); if (program_state->mvp_uniform != -1) { /* The journal usually uses an identity matrix for the modelview so we can optimise this common case by avoiding the matrix multiplication */ if (cogl_matrix_entry_is_identity (modelview_entry)) { GE (ctx, glUniformMatrix4fv (program_state->mvp_uniform, 1, /* count */ FALSE, /* transpose */ cogl_matrix_get_array (&projection))); } else { CoglMatrix combined; cogl_matrix_multiply (&combined, &projection, &modelview); GE (ctx, glUniformMatrix4fv (program_state->mvp_uniform, 1, /* count */ FALSE, /* transpose */ cogl_matrix_get_array (&combined))); } } } if (program_state->flip_uniform != -1 && program_state->flushed_flip_state != needs_flip) { static const float do_flip[4] = { 1.0f, -1.0f, 1.0f, 1.0f }; static const float dont_flip[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; GE( ctx, glUniform4fv (program_state->flip_uniform, 1, /* count */ needs_flip ? do_flip : dont_flip) ); program_state->flushed_flip_state = needs_flip; } } static void update_float_uniform (CoglPipeline *pipeline, int uniform_location, void *getter_func) { float (* float_getter_func) (CoglPipeline *) = getter_func; float value; _COGL_GET_CONTEXT (ctx, NO_RETVAL); value = float_getter_func (pipeline); GE( ctx, glUniform1f (uniform_location, value) ); } const CoglPipelineProgend _cogl_pipeline_glsl_progend = { _cogl_pipeline_progend_glsl_start, _cogl_pipeline_progend_glsl_end, _cogl_pipeline_progend_glsl_pre_change_notify, _cogl_pipeline_progend_glsl_layer_pre_change_notify, _cogl_pipeline_progend_glsl_pre_paint }; muffin-6.4.1/cogl/cogl/driver/gl/cogl-util-gl-private.h0000664000175000017500000000745514723361714021642 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #ifndef _COGL_UTIL_GL_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context.h" #include "cogl-gl-header.h" #include "cogl-texture.h" /* In OpenGL ES context, GL_CONTEXT_LOST has a _KHR prefix */ #ifndef GL_CONTEXT_LOST #define GL_CONTEXT_LOST GL_CONTEXT_LOST_KHR #endif #ifdef COGL_GL_DEBUG const char * _cogl_gl_error_to_string (GLenum error_code); #define GE(ctx, x) G_STMT_START { \ GLenum __err; \ (ctx)->x; \ while ((__err = (ctx)->glGetError ()) != GL_NO_ERROR && __err != GL_CONTEXT_LOST) \ { \ g_warning ("%s: GL error (%d): %s\n", \ G_STRLOC, \ __err, \ _cogl_gl_error_to_string (__err)); \ } } G_STMT_END #define GE_RET(ret, ctx, x) G_STMT_START { \ GLenum __err; \ ret = (ctx)->x; \ while ((__err = (ctx)->glGetError ()) != GL_NO_ERROR && __err != GL_CONTEXT_LOST) \ { \ g_warning ("%s: GL error (%d): %s\n", \ G_STRLOC, \ __err, \ _cogl_gl_error_to_string (__err)); \ } } G_STMT_END #else /* !COGL_GL_DEBUG */ #define GE(ctx, x) ((ctx)->x) #define GE_RET(ret, ctx, x) (ret = ((ctx)->x)) #endif /* COGL_GL_DEBUG */ gboolean _cogl_driver_gl_context_init (CoglContext *context); void _cogl_driver_gl_context_deinit (CoglContext *context); GLenum _cogl_gl_util_get_error (CoglContext *ctx); void _cogl_gl_util_clear_gl_errors (CoglContext *ctx); gboolean _cogl_gl_util_catch_out_of_memory (CoglContext *ctx, GError **error); /* Parses a GL version number stored in a string. @version_string must * point to the beginning of the version number (ie, it can't point to * the "OpenGL ES" part on GLES). The version number can be followed * by the end of the string, a space or a full stop. Anything else * will be treated as invalid. Returns TRUE and sets major_out and * minor_out if it is succesfully parsed or FALSE otherwise. */ gboolean _cogl_gl_util_parse_gl_version (const char *version_string, int *major_out, int *minor_out); #endif /* _COGL_UTIL_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl-private.h0000664000175000017500000000304614723361714024425 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H #define __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H #include "cogl-pipeline-private.h" extern const CoglPipelineFragend _cogl_pipeline_glsl_fragend; GLuint _cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline); #endif /* __COGL_PIPELINE_FRAGEND_GLSL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-texture-gl.c0000664000175000017500000001170714723361714020703 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #include "cogl-config.h" #include #include "cogl-context-private.h" #include "cogl-util.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" static inline int calculate_alignment (int rowstride) { int alignment = 1 << (ffs (rowstride) - 1); return MIN (alignment, 8); } void _cogl_texture_gl_prep_alignment_for_pixels_upload (CoglContext *ctx, int pixels_rowstride) { GE( ctx, glPixelStorei (GL_UNPACK_ALIGNMENT, calculate_alignment (pixels_rowstride)) ); } void _cogl_texture_gl_prep_alignment_for_pixels_download (CoglContext *ctx, int bpp, int width, int rowstride) { int alignment; /* If no padding is needed then we can always use an alignment of 1. * We want to do this even though it is equivalent to the alignment * of the rowstride because the Intel driver in Mesa currently has * an optimisation when reading data into a PBO that only works if * the alignment is exactly 1. * * https://bugs.freedesktop.org/show_bug.cgi?id=46632 */ if (rowstride == bpp * width) alignment = 1; else alignment = calculate_alignment (rowstride); GE( ctx, glPixelStorei (GL_PACK_ALIGNMENT, alignment) ); } void _cogl_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *texture, unsigned int wrap_mode_s, unsigned int wrap_mode_t) { texture->vtable->gl_flush_legacy_texobj_wrap_modes (texture, wrap_mode_s, wrap_mode_t); } void _cogl_texture_gl_flush_legacy_texobj_filters (CoglTexture *texture, unsigned int min_filter, unsigned int mag_filter) { texture->vtable->gl_flush_legacy_texobj_filters (texture, min_filter, mag_filter); } /* GL and GLES3 have this by default, but GLES2 does not except via extension. * So really it's probably always available. Even if we used it and it wasn't * available in some driver then there are no adverse consequences to the * command simply being ignored... */ #ifndef GL_TEXTURE_MAX_LEVEL #define GL_TEXTURE_MAX_LEVEL 0x813D #endif void cogl_texture_gl_set_max_level (CoglTexture *texture, int max_level) { CoglContext *ctx = texture->context; if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL)) { GLuint gl_handle; GLenum gl_target; cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); texture->max_level_set = max_level; _cogl_bind_gl_texture_transient (gl_target, gl_handle); GE( ctx, glTexParameteri (gl_target, GL_TEXTURE_MAX_LEVEL, texture->max_level_set)); } } void _cogl_texture_gl_generate_mipmaps (CoglTexture *texture) { CoglContext *ctx = texture->context; int n_levels = _cogl_texture_get_n_levels (texture); GLuint gl_handle; GLenum gl_target; if (texture->max_level_set != n_levels - 1) cogl_texture_gl_set_max_level (texture, n_levels - 1); cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); _cogl_bind_gl_texture_transient (gl_target, gl_handle); GE( ctx, glGenerateMipmap (gl_target) ); } GLenum _cogl_texture_gl_get_format (CoglTexture *texture) { return texture->vtable->get_gl_format (texture); } muffin-6.4.1/cogl/cogl/driver/gl/cogl-buffer-gl-private.h0000664000175000017500000000444214723361714022127 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_BUFFER_GL_PRIVATE_H_ #define _COGL_BUFFER_GL_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context.h" #include "cogl-buffer.h" #include "cogl-buffer-private.h" void _cogl_buffer_gl_create (CoglBuffer *buffer); void _cogl_buffer_gl_destroy (CoglBuffer *buffer); void * _cogl_buffer_gl_map_range (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); void _cogl_buffer_gl_unmap (CoglBuffer *buffer); gboolean _cogl_buffer_gl_set_data (CoglBuffer *buffer, unsigned int offset, const void *data, unsigned int size, GError **error); void * _cogl_buffer_gl_bind (CoglBuffer *buffer, CoglBufferBindTarget target, GError **error); void _cogl_buffer_gl_unbind (CoglBuffer *buffer); #endif /* _COGL_BUFFER_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-framebuffer-gl-private.h0000664000175000017500000000767314723361714023153 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_FRAMEBUFFER_GL_PRIVATE_H__ #define __COGL_FRAMEBUFFER_GL_PRIVATE_H__ gboolean _cogl_offscreen_gl_allocate (CoglOffscreen *offscreen, GError **error); void _cogl_offscreen_gl_free (CoglOffscreen *offscreen); void _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state); void _cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha); void _cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, CoglFramebufferBits *bits); void _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer); void _cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer); void _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); void _cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target); void _cogl_framebuffer_gl_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); void _cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); gboolean _cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error); #endif /* __COGL_FRAMEBUFFER_GL_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-texture-gl-private.h0000664000175000017500000000457614723361714022366 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * */ #ifndef _COGL_TEXTURE_GL_PRIVATE_H_ #define _COGL_TEXTURE_GL_PRIVATE_H_ #include "cogl-context.h" void _cogl_texture_gl_prep_alignment_for_pixels_upload (CoglContext *ctx, int pixels_rowstride); void _cogl_texture_gl_prep_alignment_for_pixels_download (CoglContext *ctx, int bpp, int width, int rowstride); void _cogl_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *texture, unsigned int wrap_mode_s, unsigned int wrap_mode_t); void _cogl_texture_gl_flush_legacy_texobj_filters (CoglTexture *texture, unsigned int min_filter, unsigned int mag_filter); void cogl_texture_gl_set_max_level (CoglTexture *texture, int max_level); void _cogl_texture_gl_generate_mipmaps (CoglTexture *texture); GLenum _cogl_texture_gl_get_format (CoglTexture *texture); #endif /* _COGL_TEXTURE_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-buffer-gl.c0000664000175000017500000002767114723361714020463 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2011,2012,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "driver/gl/cogl-buffer-gl-private.h" #include "driver/gl/cogl-util-gl-private.h" /* * GL/GLES compatibility defines for the buffer API: */ #ifndef GL_PIXEL_PACK_BUFFER #define GL_PIXEL_PACK_BUFFER 0x88EB #endif #ifndef GL_PIXEL_UNPACK_BUFFER #define GL_PIXEL_UNPACK_BUFFER 0x88EC #endif #ifndef GL_ARRAY_BUFFER #define GL_ARRAY_BUFFER 0x8892 #endif #ifndef GL_ELEMENT_ARRAY_BUFFER #define GL_ARRAY_BUFFER 0x8893 #endif #ifndef GL_READ_ONLY #define GL_READ_ONLY 0x88B8 #endif #ifndef GL_WRITE_ONLY #define GL_WRITE_ONLY 0x88B9 #endif #ifndef GL_READ_WRITE #define GL_READ_WRITE 0x88BA #endif #ifndef GL_MAP_READ_BIT #define GL_MAP_READ_BIT 0x0001 #endif #ifndef GL_MAP_WRITE_BIT #define GL_MAP_WRITE_BIT 0x0002 #endif #ifndef GL_MAP_INVALIDATE_RANGE_BIT #define GL_MAP_INVALIDATE_RANGE_BIT 0x0004 #endif #ifndef GL_MAP_INVALIDATE_BUFFER_BIT #define GL_MAP_INVALIDATE_BUFFER_BIT 0x0008 #endif void _cogl_buffer_gl_create (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; GE (ctx, glGenBuffers (1, &buffer->gl_handle)); } void _cogl_buffer_gl_destroy (CoglBuffer *buffer) { GE( buffer->context, glDeleteBuffers (1, &buffer->gl_handle) ); } static GLenum update_hints_to_gl_enum (CoglBuffer *buffer) { /* usage hint is always DRAW for now */ switch (buffer->update_hint) { case COGL_BUFFER_UPDATE_HINT_STATIC: return GL_STATIC_DRAW; case COGL_BUFFER_UPDATE_HINT_DYNAMIC: return GL_DYNAMIC_DRAW; case COGL_BUFFER_UPDATE_HINT_STREAM: /* OpenGL ES 1.1 only knows about STATIC_DRAW and DYNAMIC_DRAW */ #if defined(HAVE_COGL_GL) || defined(HAVE_COGL_GLES2) return GL_STREAM_DRAW; #else return GL_DYNAMIC_DRAW; #endif } g_assert_not_reached (); return 0; } static GLenum convert_bind_target_to_gl_target (CoglBufferBindTarget target) { switch (target) { case COGL_BUFFER_BIND_TARGET_PIXEL_PACK: return GL_PIXEL_PACK_BUFFER; case COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK: return GL_PIXEL_UNPACK_BUFFER; case COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER: return GL_ARRAY_BUFFER; case COGL_BUFFER_BIND_TARGET_INDEX_BUFFER: return GL_ELEMENT_ARRAY_BUFFER; default: g_return_val_if_reached (COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK); } } static gboolean recreate_store (CoglBuffer *buffer, GError **error) { CoglContext *ctx = buffer->context; GLenum gl_target; GLenum gl_enum; /* This assumes the buffer is already bound */ gl_target = convert_bind_target_to_gl_target (buffer->last_target); gl_enum = update_hints_to_gl_enum (buffer); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); ctx->glBufferData (gl_target, buffer->size, NULL, gl_enum); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) return FALSE; buffer->store_created = TRUE; return TRUE; } static GLenum _cogl_buffer_access_to_gl_enum (CoglBufferAccess access) { if ((access & COGL_BUFFER_ACCESS_READ_WRITE) == COGL_BUFFER_ACCESS_READ_WRITE) return GL_READ_WRITE; else if (access & COGL_BUFFER_ACCESS_WRITE) return GL_WRITE_ONLY; else return GL_READ_ONLY; } static void * _cogl_buffer_bind_no_create (CoglBuffer *buffer, CoglBufferBindTarget target) { CoglContext *ctx = buffer->context; g_return_val_if_fail (buffer != NULL, NULL); /* Don't allow binding the buffer to multiple targets at the same time */ g_return_val_if_fail (ctx->current_buffer[buffer->last_target] != buffer, NULL); /* Don't allow nesting binds to the same target */ g_return_val_if_fail (ctx->current_buffer[target] == NULL, NULL); buffer->last_target = target; ctx->current_buffer[target] = buffer; if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) { GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target); GE( ctx, glBindBuffer (gl_target, buffer->gl_handle) ); return NULL; } else return buffer->data; } void * _cogl_buffer_gl_map_range (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { uint8_t *data; CoglBufferBindTarget target; GLenum gl_target; CoglContext *ctx = buffer->context; if (((access & COGL_BUFFER_ACCESS_READ) && !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ)) || ((access & COGL_BUFFER_ACCESS_WRITE) && !cogl_has_feature (ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE))) { g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Tried to map a buffer with unsupported access mode"); return NULL; } target = buffer->last_target; _cogl_buffer_bind_no_create (buffer, target); gl_target = convert_bind_target_to_gl_target (target); if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) && offset == 0 && size >= buffer->size) hints |= COGL_BUFFER_MAP_HINT_DISCARD; /* If the map buffer range extension is supported then we will * always use it even if we are mapping the full range because the * normal mapping function doesn't support passing the discard * hints */ if (ctx->glMapBufferRange) { GLbitfield gl_access = 0; gboolean should_recreate_store = !buffer->store_created; if ((access & COGL_BUFFER_ACCESS_READ)) gl_access |= GL_MAP_READ_BIT; if ((access & COGL_BUFFER_ACCESS_WRITE)) gl_access |= GL_MAP_WRITE_BIT; if ((hints & COGL_BUFFER_MAP_HINT_DISCARD)) { /* glMapBufferRange generates an error if you pass the * discard hint along with asking for read access. However * it can make sense to ask for both if write access is also * requested so that the application can immediately read * back what it just wrote. To work around the restriction * in GL we just recreate the buffer storage in that case * which is an alternative way to indicate that the buffer * contents can be discarded. */ if ((access & COGL_BUFFER_ACCESS_READ)) should_recreate_store = TRUE; else gl_access |= GL_MAP_INVALIDATE_BUFFER_BIT; } else if ((hints & COGL_BUFFER_MAP_HINT_DISCARD_RANGE) && !(access & COGL_BUFFER_ACCESS_READ)) gl_access |= GL_MAP_INVALIDATE_RANGE_BIT; if (should_recreate_store) { if (!recreate_store (buffer, error)) { _cogl_buffer_gl_unbind (buffer); return NULL; } } /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); data = ctx->glMapBufferRange (gl_target, offset, size, gl_access); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) { _cogl_buffer_gl_unbind (buffer); return NULL; } g_return_val_if_fail (data != NULL, NULL); } else { /* create an empty store if we don't have one yet. creating the store * lazily allows the user of the CoglBuffer to set a hint before the * store is created. */ if (!buffer->store_created || (hints & COGL_BUFFER_MAP_HINT_DISCARD)) { if (!recreate_store (buffer, error)) { _cogl_buffer_gl_unbind (buffer); return NULL; } } /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); data = ctx->glMapBuffer (gl_target, _cogl_buffer_access_to_gl_enum (access)); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) { _cogl_buffer_gl_unbind (buffer); return NULL; } g_return_val_if_fail (data != NULL, NULL); data += offset; } if (data) buffer->flags |= COGL_BUFFER_FLAG_MAPPED; _cogl_buffer_gl_unbind (buffer); return data; } void _cogl_buffer_gl_unmap (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; _cogl_buffer_bind_no_create (buffer, buffer->last_target); GE( ctx, glUnmapBuffer (convert_bind_target_to_gl_target (buffer->last_target)) ); buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED; _cogl_buffer_gl_unbind (buffer); } gboolean _cogl_buffer_gl_set_data (CoglBuffer *buffer, unsigned int offset, const void *data, unsigned int size, GError **error) { CoglBufferBindTarget target; GLenum gl_target; CoglContext *ctx = buffer->context; gboolean status = TRUE; GError *internal_error = NULL; target = buffer->last_target; _cogl_buffer_gl_bind (buffer, target, &internal_error); /* NB: _cogl_buffer_gl_bind() may return NULL in non-error * conditions so we have to explicity check internal_error * to see if an exception was thrown. */ if (internal_error) { g_propagate_error (error, internal_error); return FALSE; } gl_target = convert_bind_target_to_gl_target (target); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); ctx->glBufferSubData (gl_target, offset, size, data); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_buffer_gl_unbind (buffer); return status; } void * _cogl_buffer_gl_bind (CoglBuffer *buffer, CoglBufferBindTarget target, GError **error) { void *ret; ret = _cogl_buffer_bind_no_create (buffer, target); /* create an empty store if we don't have one yet. creating the store * lazily allows the user of the CoglBuffer to set a hint before the * store is created. */ if ((buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) && !buffer->store_created) { if (!recreate_store (buffer, error)) { _cogl_buffer_gl_unbind (buffer); return NULL; } } return ret; } void _cogl_buffer_gl_unbind (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; g_return_if_fail (buffer != NULL); /* the unbind should pair up with a previous bind */ g_return_if_fail (ctx->current_buffer[buffer->last_target] == buffer); if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) { GLenum gl_target = convert_bind_target_to_gl_target (buffer->last_target); GE( ctx, glBindBuffer (gl_target, 0) ); } ctx->current_buffer[buffer->last_target] = NULL; } muffin-6.4.1/cogl/cogl/driver/gl/cogl-texture-2d-gl-private.h0000664000175000017500000001060714723361714022661 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_TEXTURE_2D_GL_PRIVATE_H_ #define _COGL_TEXTURE_2D_GL_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context-private.h" #include "cogl-texture.h" void _cogl_texture_2d_gl_free (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_gl_can_create (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format); void _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_gl_allocate (CoglTexture *tex, GError **error); CoglTexture2D * _cogl_texture_2d_gl_new_from_bitmap (CoglBitmap *bmp, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error); #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) CoglTexture2D * _cogl_egl_texture_2d_gl_new_from_image (CoglContext *ctx, int width, int height, CoglPixelFormat format, EGLImageKHR image, GError **error); #endif void _cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter); void _cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t); void _cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level); unsigned int _cogl_texture_2d_gl_get_gl_handle (CoglTexture2D *tex_2d); void _cogl_texture_2d_gl_generate_mipmap (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bitmap, int dst_x, int dst_y, int level, GError **error); gboolean _cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d); void _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d, CoglPixelFormat format, int rowstride, uint8_t *data); #endif /* _COGL_TEXTURE_2D_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-bitmap-gl-private.h0000664000175000017500000000366014723361714022133 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007 OpenedHand * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_BITMAP_GL_PRIVATE_H #define __COGL_BITMAP_GL_PRIVATE_H #include "cogl-bitmap-private.h" /* These two are replacements for map and unmap that should used when * the pointer is going to be passed to GL for pixel packing or * unpacking. The address might not be valid for reading if the bitmap * was created with new_from_buffer but it will however be good to * pass to glTexImage2D for example. The access should be READ for * unpacking and WRITE for packing. It can not be both */ uint8_t * _cogl_bitmap_gl_bind (CoglBitmap *bitmap, CoglBufferAccess access, CoglBufferMapHint hints, GError **error); void _cogl_bitmap_gl_unbind (CoglBitmap *bitmap); #endif /* __COGL_BITMAP_GL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-attribute-gl-private.h0000664000175000017500000000347614723361714022667 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_ATTRIBUTE_GL_PRIVATE_H_ #define _COGL_ATTRIBUTE_GL_PRIVATE_H_ #include "cogl-types.h" #include "cogl-framebuffer.h" #include "cogl-attribute.h" #include "cogl-attribute-private.h" void _cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglFlushLayerState *layers_state, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes); #endif /* _COGL_ATTRIBUTE_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-attribute-gl.c0000664000175000017500000002435114723361714021205 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2011,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-context-private.h" #include "cogl-attribute.h" #include "cogl-attribute-private.h" #include "driver/gl/cogl-attribute-gl-private.h" #include "driver/gl/cogl-buffer-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-pipeline-progend-glsl-private.h" #include "driver/gl/cogl-util-gl-private.h" typedef struct _ForeachChangedBitState { CoglContext *context; const CoglBitmask *new_bits; CoglPipeline *pipeline; } ForeachChangedBitState; static gboolean toggle_custom_attribute_enabled_cb (int bit_num, void *user_data) { ForeachChangedBitState *state = user_data; gboolean enabled = _cogl_bitmask_get (state->new_bits, bit_num); CoglContext *context = state->context; if (enabled) GE( context, glEnableVertexAttribArray (bit_num) ); else GE( context, glDisableVertexAttribArray (bit_num) ); return TRUE; } static void foreach_changed_bit_and_save (CoglContext *context, CoglBitmask *current_bits, const CoglBitmask *new_bits, CoglBitmaskForeachFunc callback, ForeachChangedBitState *state) { /* Get the list of bits that are different */ _cogl_bitmask_clear_all (&context->changed_bits_tmp); _cogl_bitmask_set_bits (&context->changed_bits_tmp, current_bits); _cogl_bitmask_xor_bits (&context->changed_bits_tmp, new_bits); /* Iterate over each bit to change */ state->new_bits = new_bits; _cogl_bitmask_foreach (&context->changed_bits_tmp, callback, state); /* Store the new values */ _cogl_bitmask_clear_all (current_bits); _cogl_bitmask_set_bits (current_bits, new_bits); } static void setup_generic_buffered_attribute (CoglContext *context, CoglPipeline *pipeline, CoglAttribute *attribute, uint8_t *base) { int name_index = attribute->name_state->name_index; int attrib_location = _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); if (attrib_location == -1) return; GE( context, glVertexAttribPointer (attrib_location, attribute->d.buffered.n_components, attribute->d.buffered.type, attribute->normalized, attribute->d.buffered.stride, base + attribute->d.buffered.offset) ); _cogl_bitmask_set (&context->enable_custom_attributes_tmp, attrib_location, TRUE); } static void setup_generic_const_attribute (CoglContext *context, CoglPipeline *pipeline, CoglAttribute *attribute) { int name_index = attribute->name_state->name_index; int attrib_location = _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); int columns; int i; if (attrib_location == -1) return; if (attribute->d.constant.boxed.type == COGL_BOXED_MATRIX) columns = attribute->d.constant.boxed.size; else columns = 1; /* Note: it's ok to access a COGL_BOXED_FLOAT as a matrix with only * one column... */ switch (attribute->d.constant.boxed.size) { case 1: GE( context, glVertexAttrib1fv (attrib_location, attribute->d.constant.boxed.v.matrix)); break; case 2: for (i = 0; i < columns; i++) GE( context, glVertexAttrib2fv (attrib_location + i, attribute->d.constant.boxed.v.matrix)); break; case 3: for (i = 0; i < columns; i++) GE( context, glVertexAttrib3fv (attrib_location + i, attribute->d.constant.boxed.v.matrix)); break; case 4: for (i = 0; i < columns; i++) GE( context, glVertexAttrib4fv (attrib_location + i, attribute->d.constant.boxed.v.matrix)); break; default: g_warn_if_reached (); } } static void apply_attribute_enable_updates (CoglContext *context, CoglPipeline *pipeline) { ForeachChangedBitState changed_bits_state; changed_bits_state.context = context; changed_bits_state.pipeline = pipeline; changed_bits_state.new_bits = &context->enable_custom_attributes_tmp; foreach_changed_bit_and_save (context, &context->enabled_custom_attributes, &context->enable_custom_attributes_tmp, toggle_custom_attribute_enabled_cb, &changed_bits_state); } void _cogl_gl_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglFlushLayerState *layers_state, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes) { CoglContext *ctx = framebuffer->context; int i; gboolean with_color_attrib = FALSE; gboolean unknown_color_alpha = FALSE; CoglPipeline *copy = NULL; /* Iterate the attributes to see if we have a color attribute which * may affect our decision to enable blending or not. * * We need to do this before flushing the pipeline. */ for (i = 0; i < n_attributes; i++) switch (attributes[i]->name_state->name_id) { case COGL_ATTRIBUTE_NAME_ID_COLOR_ARRAY: if ((flags & COGL_DRAW_COLOR_ATTRIBUTE_IS_OPAQUE) == 0 && _cogl_attribute_get_n_components (attributes[i]) == 4) unknown_color_alpha = TRUE; with_color_attrib = TRUE; break; default: break; } if (G_UNLIKELY (layers_state->options.flags)) { /* If we haven't already created a derived pipeline... */ if (!copy) { copy = cogl_pipeline_copy (pipeline); pipeline = copy; } _cogl_pipeline_apply_overrides (pipeline, &layers_state->options); /* TODO: * overrides = cogl_pipeline_get_data (pipeline, * last_overrides_key); * if (overrides) * { * age = cogl_pipeline_get_age (pipeline); * XXX: actually we also need to check for legacy_state * if (overrides->ags != age || * memcmp (&overrides->options, &options, * sizeof (options) != 0) * { * cogl_object_unref (overrides->weak_pipeline); * g_slice_free (Overrides, overrides); * overrides = NULL; * } * } * if (!overrides) * { * overrides = g_slice_new (Overrides); * overrides->weak_pipeline = * cogl_pipeline_weak_copy (pipeline); * _cogl_pipeline_apply_overrides (overrides->weak_pipeline, * &options); * * cogl_pipeline_set_data (pipeline, last_overrides_key, * weak_overrides, * free_overrides_cb, * NULL); * } * pipeline = overrides->weak_pipeline; */ } _cogl_pipeline_flush_gl_state (ctx, pipeline, framebuffer, with_color_attrib, unknown_color_alpha); _cogl_bitmask_clear_all (&ctx->enable_custom_attributes_tmp); /* Bind the attribute pointers. We need to do this after the * pipeline is flushed because when using GLSL that is the only * point when we can determine the attribute locations */ for (i = 0; i < n_attributes; i++) { CoglAttribute *attribute = attributes[i]; CoglAttributeBuffer *attribute_buffer; CoglBuffer *buffer; uint8_t *base; if (attribute->is_buffered) { attribute_buffer = cogl_attribute_get_buffer (attribute); buffer = COGL_BUFFER (attribute_buffer); /* Note: we don't try and catch errors with binding buffers * here since OOM errors at this point indicate that nothing * has yet been uploaded to attribute buffer which we * consider to be a programmer error. */ base = _cogl_buffer_gl_bind (buffer, COGL_BUFFER_BIND_TARGET_ATTRIBUTE_BUFFER, NULL); setup_generic_buffered_attribute (ctx, pipeline, attribute, base); _cogl_buffer_gl_unbind (buffer); } else { setup_generic_const_attribute (ctx, pipeline, attribute); } } apply_attribute_enable_updates (ctx, pipeline); if (copy) cogl_object_unref (copy); } muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-opengl-private.h0000664000175000017500000001317414723361714023347 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_OPENGL_PRIVATE_H #define __COGL_PIPELINE_OPENGL_PRIVATE_H #include "cogl-pipeline-private.h" #include "cogl-matrix-stack.h" /* * cogl-pipeline.c owns the GPU's texture unit state so we have some * private structures for describing the current state of a texture * unit that we track in a per context array (ctx->texture_units) that * grows according to the largest texture unit used so far... * * Roughly speaking the members in this structure are of two kinds: * either they are a low level reflection of the state we send to * OpenGL or they are for high level meta data assoicated with the * texture unit when flushing CoglPipelineLayers that is typically * used to optimize subsequent re-flushing of the same layer. * * The low level members are at the top, and the high level members * start with the .layer member. */ typedef struct _CoglTextureUnit { /* The base 0 texture unit index which can be used with * glActiveTexture () */ int index; /* The GL target currently glEnabled or 0 if nothing is * enabled. This is only used by the fixed pipeline fragend */ GLenum enabled_gl_target; /* The raw GL texture object name for which we called glBindTexture when * we flushed the last layer. (NB: The CoglTexture associated * with a layer may represent more than one GL texture) */ GLuint gl_texture; /* The target of the GL texture object. This is just used so that we * can quickly determine the intended target to flush when * dirty_gl_texture == TRUE */ GLenum gl_target; /* We have many components in Cogl that need to temporarily bind arbitrary * textures e.g. to query texture object parameters and since we don't * want that to result in too much redundant reflushing of layer state * when all that's needed is to re-bind the layer's gl_texture we use this * to track when the unit->gl_texture state is out of sync with the GL * texture object really bound too (GL_TEXTURE0+unit->index). * * XXX: as a further optimization cogl-pipeline.c uses a convention * of always using texture unit 1 for these transient bindings so we * can assume this is only ever TRUE for unit 1. */ gboolean dirty_gl_texture; /* A matrix stack giving us the means to associate a texture * transform matrix with the texture unit. */ CoglMatrixStack *matrix_stack; /* * Higher level layer state associated with the unit... */ /* The CoglPipelineLayer whos state was flushed to update this * texture unit last. * * This will be set to NULL if the layer is modified or freed which * means when we come to flush a layer; if this pointer is still * valid and == to the layer being flushed we don't need to update * any texture unit state. */ CoglPipelineLayer *layer; /* To help minimize the state changes required we track the * difference flags associated with the layer whos state was last * flushed to update this texture unit. * * Note: we track this explicitly because .layer may get invalidated * if that layer is modified or deleted. Even if the layer is * invalidated though these flags can be used to optimize the state * flush of the next layer */ unsigned long layer_changes_since_flush; /* Whenever a CoglTexture's internal GL texture storage changes * cogl-pipeline.c is notified with a call to * _cogl_pipeline_texture_storage_change_notify which inturn sets * this to TRUE for each texture unit that it is currently bound * too. When we later come to flush some pipeline state then we will * always check this to potentially force an update of the texture * state even if the pipeline hasn't changed. */ gboolean texture_storage_changed; } CoglTextureUnit; CoglTextureUnit * _cogl_get_texture_unit (int index_); void _cogl_destroy_texture_units (CoglContext *ctx); void _cogl_set_active_texture_unit (int unit_index); void _cogl_bind_gl_texture_transient (GLenum gl_target, GLuint gl_texture); void _cogl_delete_gl_texture (GLuint gl_texture); void _cogl_pipeline_flush_gl_state (CoglContext *context, CoglPipeline *pipeline, CoglFramebuffer *framebuffer, gboolean skip_gl_state, gboolean unknown_color_alpha); #endif /* __COGL_PIPELINE_OPENGL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-bitmap-gl.c0000664000175000017500000000735214723361714020460 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-debug.h" #include "cogl-private.h" #include "cogl-bitmap-private.h" #include "cogl-buffer-private.h" #include "cogl-pixel-buffer.h" #include "cogl-context-private.h" #include "cogl-gtype-private.h" #include "cogl-buffer-gl-private.h" #include "cogl-bitmap-gl-private.h" uint8_t * _cogl_bitmap_gl_bind (CoglBitmap *bitmap, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { uint8_t *ptr; GError *internal_error = NULL; g_return_val_if_fail (access & (COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE), NULL); /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) return _cogl_bitmap_gl_bind (bitmap->shared_bmp, access, hints, error); g_return_val_if_fail (!bitmap->bound, NULL); /* If the bitmap wasn't created from a buffer then the implementation of bind is the same as map */ if (bitmap->buffer == NULL) { uint8_t *data = _cogl_bitmap_map (bitmap, access, hints, error); if (data) bitmap->bound = TRUE; return data; } if (access == COGL_BUFFER_ACCESS_READ) ptr = _cogl_buffer_gl_bind (bitmap->buffer, COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK, &internal_error); else if (access == COGL_BUFFER_ACCESS_WRITE) ptr = _cogl_buffer_gl_bind (bitmap->buffer, COGL_BUFFER_BIND_TARGET_PIXEL_PACK, &internal_error); else { ptr = NULL; g_assert_not_reached (); return NULL; } /* NB: _cogl_buffer_gl_bind() may return NULL in non-error * conditions so we have to explicitly check internal_error to see * if an exception was thrown */ if (internal_error) { g_propagate_error (error, internal_error); return NULL; } bitmap->bound = TRUE; /* The data pointer actually stores the offset */ return ptr + GPOINTER_TO_INT (bitmap->data); } void _cogl_bitmap_gl_unbind (CoglBitmap *bitmap) { /* Divert to another bitmap if this data is shared */ if (bitmap->shared_bmp) { _cogl_bitmap_gl_unbind (bitmap->shared_bmp); return; } g_assert (bitmap->bound); bitmap->bound = FALSE; /* If the bitmap wasn't created from a pixel array then the implementation of unbind is the same as unmap */ if (bitmap->buffer) _cogl_buffer_gl_unbind (bitmap->buffer); else _cogl_bitmap_unmap (bitmap); } muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-fragend-glsl.c0000664000175000017500000011377614723361714022764 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-pipeline-layer-private.h" #include "cogl-blend-string.h" #include "cogl-snippet-private.h" #include "cogl-list.h" #include "driver/gl/cogl-util-gl-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-pipeline-cache.h" #include "cogl-glsl-shader-private.h" #include "driver/gl/cogl-pipeline-fragend-glsl-private.h" #include "deprecated/cogl-shader-private.h" #include "deprecated/cogl-program-private.h" #include /* * GL/GLES compatability defines for pipeline thingies: */ /* This might not be defined on GLES */ #ifndef GL_TEXTURE_3D #define GL_TEXTURE_3D 0x806F #endif const CoglPipelineFragend _cogl_pipeline_glsl_backend; typedef struct _UnitState { unsigned int sampled:1; unsigned int combine_constant_used:1; } UnitState; typedef struct _LayerData { CoglList link; /* Layer index for the for the previous layer. This isn't necessarily the same as this layer's index - 1 because the indices can have gaps. If this is the first layer then it will be -1 */ int previous_layer_index; CoglPipelineLayer *layer; } LayerData; typedef struct { int ref_count; GLuint gl_shader; GString *header, *source; UnitState *unit_state; /* List of layers that we haven't generated code for yet. These are in reverse order. As soon as we're about to generate code for layer we'll remove it from the list so we don't generate it again */ CoglList layers; CoglPipelineCacheEntry *cache_entry; } CoglPipelineShaderState; static CoglUserDataKey shader_state_key; static void ensure_layer_generated (CoglPipeline *pipeline, int layer_num); static CoglPipelineShaderState * shader_state_new (int n_layers, CoglPipelineCacheEntry *cache_entry) { CoglPipelineShaderState *shader_state; shader_state = g_slice_new0 (CoglPipelineShaderState); shader_state->ref_count = 1; shader_state->unit_state = g_new0 (UnitState, n_layers); shader_state->cache_entry = cache_entry; return shader_state; } static CoglPipelineShaderState * get_shader_state (CoglPipeline *pipeline) { return cogl_object_get_user_data (COGL_OBJECT (pipeline), &shader_state_key); } static void destroy_shader_state (void *user_data, void *instance) { CoglPipelineShaderState *shader_state = user_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader_state->cache_entry && shader_state->cache_entry->pipeline != instance) shader_state->cache_entry->usage_count--; if (--shader_state->ref_count == 0) { if (shader_state->gl_shader) GE( ctx, glDeleteShader (shader_state->gl_shader) ); g_free (shader_state->unit_state); g_slice_free (CoglPipelineShaderState, shader_state); } } static void set_shader_state (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { if (shader_state) { shader_state->ref_count++; /* If we're not setting the state on the template pipeline then * mark it as a usage of the pipeline cache entry */ if (shader_state->cache_entry && shader_state->cache_entry->pipeline != pipeline) shader_state->cache_entry->usage_count++; } _cogl_object_set_user_data (COGL_OBJECT (pipeline), &shader_state_key, shader_state, destroy_shader_state); } static void dirty_shader_state (CoglPipeline *pipeline) { cogl_object_set_user_data (COGL_OBJECT (pipeline), &shader_state_key, NULL, NULL); } GLuint _cogl_pipeline_fragend_glsl_get_shader (CoglPipeline *pipeline) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); if (shader_state) return shader_state->gl_shader; else return 0; } static CoglPipelineSnippetList * get_fragment_snippets (CoglPipeline *pipeline) { pipeline = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS); return &pipeline->big_state->fragment_snippets; } static CoglPipelineSnippetList * get_layer_fragment_snippets (CoglPipelineLayer *layer) { unsigned long state = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; layer = _cogl_pipeline_layer_get_authority (layer, state); return &layer->big_state->fragment_snippets; } static gboolean has_replace_hook (CoglPipelineLayer *layer, CoglSnippetHook hook) { GList *l; for (l = get_layer_fragment_snippets (layer)->entries; l; l = l->next) { CoglSnippet *snippet = l->data; if (snippet->hook == hook && snippet->replace) return TRUE; } return FALSE; } static gboolean add_layer_declaration_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineShaderState *shader_state = user_data; g_string_append_printf (shader_state->header, "uniform sampler2D cogl_sampler%i;\n", layer->index); return TRUE; } static void add_layer_declarations (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { /* We always emit sampler uniforms in case there will be custom * layer snippets that want to sample arbitrary layers. */ _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_declaration_cb, shader_state); } static void add_global_declarations (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { CoglSnippetHook hook = COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS; CoglPipelineSnippetList *snippets = get_fragment_snippets (pipeline); /* Add the global data hooks. All of the code in these snippets is * always added and only the declarations data is used */ _cogl_pipeline_snippet_generate_declarations (shader_state->header, hook, snippets); } static void _cogl_pipeline_fragend_glsl_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; CoglPipeline *authority; CoglPipelineCacheEntry *cache_entry = NULL; CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Now lookup our glsl backend private state */ shader_state = get_shader_state (pipeline); if (shader_state == NULL) { /* If we don't have an associated glsl shader yet then find the * glsl-authority (the oldest ancestor whose state will result in * the same shader being generated as for this pipeline). * * We always make sure to associate new shader with the * glsl-authority to maximize the chance that other pipelines can * share it. */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, _cogl_pipeline_get_state_for_fragment_codegen (ctx) & ~COGL_PIPELINE_STATE_LAYERS, _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx)); shader_state = get_shader_state (authority); /* If we don't have an existing program associated with the * glsl-authority then start generating code for a new shader... */ if (shader_state == NULL) { /* Check if there is already a similar cached pipeline whose shader state we can share */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { cache_entry = _cogl_pipeline_cache_get_fragment_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (cache_entry->pipeline); } if (shader_state) shader_state->ref_count++; else shader_state = shader_state_new (n_layers, cache_entry); set_shader_state (authority, shader_state); shader_state->ref_count--; if (cache_entry) set_shader_state (cache_entry->pipeline, shader_state); } /* If the pipeline isn't actually its own glsl-authority * then take a reference to the program state associated * with the glsl-authority... */ if (authority != pipeline) set_shader_state (pipeline, shader_state); } if (user_program) { /* If the user program contains a fragment shader then we don't need to generate one */ if (_cogl_program_has_fragment_shader (user_program)) { if (shader_state->gl_shader) { GE( ctx, glDeleteShader (shader_state->gl_shader) ); shader_state->gl_shader = 0; } return; } } if (shader_state->gl_shader) return; /* If we make it here then we have a glsl_shader_state struct without a gl_shader either because this is the first time we've encountered it or because the user program has changed */ /* We reuse two grow-only GStrings for code-gen. One string contains the uniform and attribute declarations while the other contains the main function. We need two strings because we need to dynamically declare attributes as the add_layer callback is invoked */ g_string_set_size (ctx->codegen_header_buffer, 0); g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->header = ctx->codegen_header_buffer; shader_state->source = ctx->codegen_source_buffer; _cogl_list_init (&shader_state->layers); add_layer_declarations (pipeline, shader_state); add_global_declarations (pipeline, shader_state); g_string_append (shader_state->source, "void\n" "cogl_generated_source ()\n" "{\n"); for (i = 0; i < n_layers; i++) { shader_state->unit_state[i].sampled = FALSE; shader_state->unit_state[i].combine_constant_used = FALSE; } } static void add_constant_lookup (CoglPipelineShaderState *shader_state, CoglPipeline *pipeline, CoglPipelineLayer *layer, const char *swizzle) { g_string_append_printf (shader_state->header, "_cogl_layer_constant_%i.%s", layer->index, swizzle); } static void ensure_texture_lookup_generated (CoglPipelineShaderState *shader_state, CoglPipeline *pipeline, CoglPipelineLayer *layer) { int unit_index = _cogl_pipeline_layer_get_unit_index (layer); CoglPipelineSnippetData snippet_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader_state->unit_state[unit_index].sampled) return; shader_state->unit_state[unit_index].sampled = TRUE; g_string_append_printf (shader_state->header, "vec4 cogl_texel%i;\n", layer->index); g_string_append_printf (shader_state->source, " cogl_texel%i = cogl_texture_lookup%i (" "cogl_sampler%i, ", layer->index, layer->index, layer->index); if (cogl_pipeline_get_layer_point_sprite_coords_enabled (pipeline, layer->index)) g_string_append_printf (shader_state->source, "vec4 (cogl_point_coord, 0.0, 1.0)"); else g_string_append_printf (shader_state->source, "cogl_tex_coord%i_in", layer->index); g_string_append (shader_state->source, ");\n"); /* There's no need to generate the real texture lookup if it's going to be replaced */ if (!has_replace_hook (layer, COGL_SNIPPET_HOOK_TEXTURE_LOOKUP)) { g_string_append_printf (shader_state->header, "vec4\n" "cogl_real_texture_lookup%i (sampler2D tex,\n" " vec4 coords)\n" "{\n" " return ", layer->index); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_TEXTURING))) g_string_append (shader_state->header, "vec4 (1.0, 1.0, 1.0, 1.0);\n"); else g_string_append (shader_state->header, "texture2D (tex, coords.st);\n"); g_string_append (shader_state->header, "}\n"); } /* Wrap the texture lookup in any snippets that have been hooked */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = get_layer_fragment_snippets (layer); snippet_data.hook = COGL_SNIPPET_HOOK_TEXTURE_LOOKUP; snippet_data.chain_function = g_strdup_printf ("cogl_real_texture_lookup%i", layer->index); snippet_data.final_name = g_strdup_printf ("cogl_texture_lookup%i", layer->index); snippet_data.function_prefix = g_strdup_printf ("cogl_texture_lookup_hook%i", layer->index); snippet_data.return_type = "vec4"; snippet_data.return_variable = "cogl_texel"; snippet_data.arguments = "cogl_sampler, cogl_tex_coord"; snippet_data.argument_declarations = g_strdup ("sampler2D cogl_sampler, vec4 cogl_tex_coord"); snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); g_free ((char *) snippet_data.chain_function); g_free ((char *) snippet_data.final_name); g_free ((char *) snippet_data.function_prefix); g_free ((char *) snippet_data.argument_declarations); } static void add_arg (CoglPipelineShaderState *shader_state, CoglPipeline *pipeline, CoglPipelineLayer *layer, int previous_layer_index, CoglPipelineCombineSource src, CoglPipelineCombineOp operand, const char *swizzle) { GString *shader_source = shader_state->header; char alpha_swizzle[5] = "aaaa"; g_string_append_c (shader_source, '('); if (operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR || operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA) g_string_append_printf (shader_source, "vec4(1.0, 1.0, 1.0, 1.0).%s - ", swizzle); /* If the operand is reading from the alpha then replace the swizzle with the same number of copies of the alpha */ if (operand == COGL_PIPELINE_COMBINE_OP_SRC_ALPHA || operand == COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA) { alpha_swizzle[strlen (swizzle)] = '\0'; swizzle = alpha_swizzle; } switch (src) { case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: g_string_append_printf (shader_source, "cogl_texel%i.%s", layer->index, swizzle); break; case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: add_constant_lookup (shader_state, pipeline, layer, swizzle); break; case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: if (previous_layer_index >= 0) { g_string_append_printf (shader_source, "cogl_layer%i.%s", previous_layer_index, swizzle); break; } /* flow through */ case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: g_string_append_printf (shader_source, "cogl_color_in.%s", swizzle); break; default: { int layer_num = src - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; CoglPipelineLayer *other_layer = _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); if (other_layer == NULL) { static gboolean warning_seen = FALSE; if (!warning_seen) { g_warning ("The application is trying to use a texture " "combine with a layer number that does not exist"); warning_seen = TRUE; } g_string_append_printf (shader_source, "vec4 (1.0, 1.0, 1.0, 1.0).%s", swizzle); } else g_string_append_printf (shader_source, "cogl_texel%i.%s", other_layer->index, swizzle); } break; } g_string_append_c (shader_source, ')'); } static void ensure_arg_generated (CoglPipeline *pipeline, CoglPipelineLayer *layer, int previous_layer_index, CoglPipelineCombineSource src) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); switch (src) { case COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR: /* This doesn't involve any other layers */ break; case COGL_PIPELINE_COMBINE_SOURCE_CONSTANT: { int unit_index = _cogl_pipeline_layer_get_unit_index (layer); /* Create a sampler uniform for this layer if we haven't already */ if (!shader_state->unit_state[unit_index].combine_constant_used) { g_string_append_printf (shader_state->header, "uniform vec4 _cogl_layer_constant_%i;\n", layer->index); shader_state->unit_state[unit_index].combine_constant_used = TRUE; } } break; case COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS: if (previous_layer_index >= 0) ensure_layer_generated (pipeline, previous_layer_index); break; case COGL_PIPELINE_COMBINE_SOURCE_TEXTURE: ensure_texture_lookup_generated (shader_state, pipeline, layer); break; default: if (src >= COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0) { int layer_num = src - COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0; CoglPipelineGetLayerFlags flags = COGL_PIPELINE_GET_LAYER_NO_CREATE; CoglPipelineLayer *other_layer = _cogl_pipeline_get_layer_with_flags (pipeline, layer_num, flags); if (other_layer) ensure_texture_lookup_generated (shader_state, pipeline, other_layer); } break; } } static void ensure_args_for_func (CoglPipeline *pipeline, CoglPipelineLayer *layer, int previous_layer_index, CoglPipelineCombineFunc function, CoglPipelineCombineSource *src) { int n_args = _cogl_get_n_args_for_combine_func (function); int i; for (i = 0; i < n_args; i++) ensure_arg_generated (pipeline, layer, previous_layer_index, src[i]); } static void append_masked_combine (CoglPipeline *pipeline, CoglPipelineLayer *layer, int previous_layer_index, const char *swizzle, CoglPipelineCombineFunc function, CoglPipelineCombineSource *src, CoglPipelineCombineOp *op) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); GString *shader_source = shader_state->header; g_string_append_printf (shader_state->header, " cogl_layer.%s = ", swizzle); switch (function) { case COGL_PIPELINE_COMBINE_FUNC_REPLACE: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); break; case COGL_PIPELINE_COMBINE_FUNC_MODULATE: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); g_string_append (shader_source, " * "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], swizzle); break; case COGL_PIPELINE_COMBINE_FUNC_ADD: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); g_string_append (shader_source, " + "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], swizzle); break; case COGL_PIPELINE_COMBINE_FUNC_ADD_SIGNED: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); g_string_append (shader_source, " + "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], swizzle); g_string_append_printf (shader_source, " - vec4(0.5, 0.5, 0.5, 0.5).%s", swizzle); break; case COGL_PIPELINE_COMBINE_FUNC_SUBTRACT: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); g_string_append (shader_source, " - "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], swizzle); break; case COGL_PIPELINE_COMBINE_FUNC_INTERPOLATE: add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], swizzle); g_string_append (shader_source, " * "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[2], op[2], swizzle); g_string_append (shader_source, " + "); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], swizzle); g_string_append_printf (shader_source, " * (vec4(1.0, 1.0, 1.0, 1.0).%s - ", swizzle); add_arg (shader_state, pipeline, layer, previous_layer_index, src[2], op[2], swizzle); g_string_append_c (shader_source, ')'); break; case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGB: case COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA: g_string_append (shader_source, "vec4(4.0 * (("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], "r"); g_string_append (shader_source, " - 0.5) * ("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], "r"); g_string_append (shader_source, " - 0.5) + ("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], "g"); g_string_append (shader_source, " - 0.5) * ("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], "g"); g_string_append (shader_source, " - 0.5) + ("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[0], op[0], "b"); g_string_append (shader_source, " - 0.5) * ("); add_arg (shader_state, pipeline, layer, previous_layer_index, src[1], op[1], "b"); g_string_append_printf (shader_source, " - 0.5))).%s", swizzle); break; } g_string_append_printf (shader_source, ";\n"); } static void ensure_layer_generated (CoglPipeline *pipeline, int layer_index) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); CoglPipelineLayer *combine_authority; CoglPipelineLayerBigState *big_state; CoglPipelineLayer *layer; CoglPipelineSnippetData snippet_data; LayerData *layer_data; /* Find the layer that corresponds to this layer_num */ _cogl_list_for_each (layer_data, &shader_state->layers, link) { layer = layer_data->layer; if (layer->index == layer_index) goto found; } /* If we didn't find it then we can assume the layer has already been generated */ return; found: /* Remove the layer from the list so we don't generate it again */ _cogl_list_remove (&layer_data->link); combine_authority = _cogl_pipeline_layer_get_authority (layer, COGL_PIPELINE_LAYER_STATE_COMBINE); big_state = combine_authority->big_state; /* Make a global variable for the result of the layer code */ g_string_append_printf (shader_state->header, "vec4 cogl_layer%i;\n", layer_index); /* Skip the layer generation if there is a snippet that replaces the default layer code. This is important because generating this code may cause the code for other layers to be generated and stored in the global variable. If this code isn't actually used then the global variables would be uninitialised and they may be used from other layers */ if (!has_replace_hook (layer, COGL_SNIPPET_HOOK_LAYER_FRAGMENT)) { ensure_args_for_func (pipeline, layer, layer_data->previous_layer_index, big_state->texture_combine_rgb_func, big_state->texture_combine_rgb_src); ensure_args_for_func (pipeline, layer, layer_data->previous_layer_index, big_state->texture_combine_alpha_func, big_state->texture_combine_alpha_src); g_string_append_printf (shader_state->header, "vec4\n" "cogl_real_generate_layer%i ()\n" "{\n" " vec4 cogl_layer;\n", layer_index); if (!_cogl_pipeline_layer_needs_combine_separate (combine_authority) || /* GL_DOT3_RGBA Is a bit weird as a GL_COMBINE_RGB function * since if you use it, it overrides your ALPHA function... */ big_state->texture_combine_rgb_func == COGL_PIPELINE_COMBINE_FUNC_DOT3_RGBA) append_masked_combine (pipeline, layer, layer_data->previous_layer_index, "rgba", big_state->texture_combine_rgb_func, big_state->texture_combine_rgb_src, big_state->texture_combine_rgb_op); else { append_masked_combine (pipeline, layer, layer_data->previous_layer_index, "rgb", big_state->texture_combine_rgb_func, big_state->texture_combine_rgb_src, big_state->texture_combine_rgb_op); append_masked_combine (pipeline, layer, layer_data->previous_layer_index, "a", big_state->texture_combine_alpha_func, big_state->texture_combine_alpha_src, big_state->texture_combine_alpha_op); } g_string_append (shader_state->header, " return cogl_layer;\n" "}\n"); } /* Wrap the layer code in any snippets that have been hooked */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = get_layer_fragment_snippets (layer); snippet_data.hook = COGL_SNIPPET_HOOK_LAYER_FRAGMENT; snippet_data.chain_function = g_strdup_printf ("cogl_real_generate_layer%i", layer_index); snippet_data.final_name = g_strdup_printf ("cogl_generate_layer%i", layer_index); snippet_data.function_prefix = g_strdup_printf ("cogl_generate_layer%i", layer_index); snippet_data.return_type = "vec4"; snippet_data.return_variable = "cogl_layer"; snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); g_free ((char *) snippet_data.chain_function); g_free ((char *) snippet_data.final_name); g_free ((char *) snippet_data.function_prefix); g_string_append_printf (shader_state->source, " cogl_layer%i = cogl_generate_layer%i ();\n", layer_index, layer_index); g_slice_free (LayerData, layer_data); } static gboolean _cogl_pipeline_fragend_glsl_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *layer, unsigned long layers_difference) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); LayerData *layer_data; if (!shader_state->source) return TRUE; /* Store the layers in reverse order */ layer_data = g_slice_new (LayerData); layer_data->layer = layer; if (_cogl_list_empty (&shader_state->layers)) { layer_data->previous_layer_index = -1; } else { LayerData *first = _cogl_container_of (shader_state->layers.next, LayerData, link); layer_data->previous_layer_index = first->layer->index; } _cogl_list_insert (&shader_state->layers, &layer_data->link); return TRUE; } /* GLES2 and GL3 don't have alpha testing so we need to implement it in the shader */ #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) static void add_alpha_test_snippet (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { CoglPipelineAlphaFunc alpha_func; alpha_func = cogl_pipeline_get_alpha_test_function (pipeline); if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_ALWAYS) /* Do nothing */ return; if (alpha_func == COGL_PIPELINE_ALPHA_FUNC_NEVER) { /* Always discard the fragment */ g_string_append (shader_state->source, " discard;\n"); return; } /* For all of the other alpha functions we need a uniform for the reference */ g_string_append (shader_state->header, "uniform float _cogl_alpha_test_ref;\n"); g_string_append (shader_state->source, " if (cogl_color_out.a "); switch (alpha_func) { case COGL_PIPELINE_ALPHA_FUNC_LESS: g_string_append (shader_state->source, ">="); break; case COGL_PIPELINE_ALPHA_FUNC_EQUAL: g_string_append (shader_state->source, "!="); break; case COGL_PIPELINE_ALPHA_FUNC_LEQUAL: g_string_append (shader_state->source, ">"); break; case COGL_PIPELINE_ALPHA_FUNC_GREATER: g_string_append (shader_state->source, "<="); break; case COGL_PIPELINE_ALPHA_FUNC_NOTEQUAL: g_string_append (shader_state->source, "=="); break; case COGL_PIPELINE_ALPHA_FUNC_GEQUAL: g_string_append (shader_state->source, "< "); break; case COGL_PIPELINE_ALPHA_FUNC_ALWAYS: case COGL_PIPELINE_ALPHA_FUNC_NEVER: g_assert_not_reached (); break; } g_string_append (shader_state->source, " _cogl_alpha_test_ref)\n discard;\n"); } #endif /* HAVE_COGL_GLES2 */ static gboolean _cogl_pipeline_fragend_glsl_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); _COGL_GET_CONTEXT (ctx, FALSE); if (shader_state->source) { const char *source_strings[2]; GLint lengths[2]; GLint compile_status; GLuint shader; CoglPipelineSnippetData snippet_data; COGL_STATIC_COUNTER (fragend_glsl_compile_counter, "glsl fragment compile counter", "Increments each time a new GLSL " "fragment shader is compiled", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, fragend_glsl_compile_counter); /* We only need to generate code to calculate the fragment value for the last layer. If the value of this layer depends on any previous layers then it will recursively generate the code for those layers */ if (!_cogl_list_empty (&shader_state->layers)) { CoglPipelineLayer *last_layer; LayerData *layer_data, *tmp; layer_data = _cogl_container_of (shader_state->layers.next, LayerData, link); last_layer = layer_data->layer; ensure_layer_generated (pipeline, last_layer->index); g_string_append_printf (shader_state->source, " cogl_color_out = cogl_layer%i;\n", last_layer->index); _cogl_list_for_each_safe (layer_data, tmp, &shader_state->layers, link) g_slice_free (LayerData, layer_data); } else g_string_append (shader_state->source, " cogl_color_out = cogl_color_in;\n"); add_alpha_test_snippet (pipeline, shader_state); /* Close the function surrounding the generated fragment processing */ g_string_append (shader_state->source, "}\n"); /* Add all of the hooks for fragment processing */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = get_fragment_snippets (pipeline); snippet_data.hook = COGL_SNIPPET_HOOK_FRAGMENT; snippet_data.chain_function = "cogl_generated_source"; snippet_data.final_name = "main"; snippet_data.function_prefix = "cogl_fragment_hook"; snippet_data.source_buf = shader_state->source; _cogl_pipeline_snippet_generate_code (&snippet_data); GE_RET( shader, ctx, glCreateShader (GL_FRAGMENT_SHADER) ); lengths[0] = shader_state->header->len; source_strings[0] = shader_state->header->str; lengths[1] = shader_state->source->len; source_strings[1] = shader_state->source->str; _cogl_glsl_shader_set_source_with_boilerplate (ctx, shader, GL_FRAGMENT_SHADER, pipeline, 2, /* count */ source_strings, lengths); GE( ctx, glCompileShader (shader) ); GE( ctx, glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); if (!compile_status) { GLint len = 0; char *shader_log; GE( ctx, glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); shader_log = g_alloca (len); GE( ctx, glGetShaderInfoLog (shader, len, &len, shader_log) ); g_warning ("Shader compilation failed:\n%s", shader_log); } shader_state->header = NULL; shader_state->source = NULL; shader_state->gl_shader = shader; } return TRUE; } static void _cogl_pipeline_fragend_glsl_pre_change_notify (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if ((change & _cogl_pipeline_get_state_for_fragment_codegen (ctx))) dirty_shader_state (pipeline); } /* NB: layers are considered immutable once they have any dependants * so although multiple pipelines can end up depending on a single * static layer, we can guarantee that if a layer is being *changed* * then it can only have one pipeline depending on it. * * XXX: Don't forget this is *pre* change, we can't read the new value * yet! */ static void _cogl_pipeline_fragend_glsl_layer_pre_change_notify ( CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if ((change & _cogl_pipeline_get_layer_state_for_fragment_codegen (ctx))) { dirty_shader_state (owner); return; } /* TODO: we could be saving snippets of texture combine code along * with each layer and then when a layer changes we would just free * the snippet. */ } const CoglPipelineFragend _cogl_pipeline_glsl_fragend = { _cogl_pipeline_fragend_glsl_start, _cogl_pipeline_fragend_glsl_add_layer, _cogl_pipeline_fragend_glsl_end, _cogl_pipeline_fragend_glsl_pre_change_notify, _cogl_pipeline_fragend_glsl_layer_pre_change_notify }; muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl-private.h0000664000175000017500000000304614723361714024466 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H #define __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H #include "cogl-pipeline-private.h" extern const CoglPipelineVertend _cogl_pipeline_glsl_vertend; GLuint _cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline); #endif /* __COGL_PIPELINE_VERTEND_GLSL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-clip-stack-gl.c0000664000175000017500000004627614723361714021246 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010,2011,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-primitives-private.h" #include "cogl-primitive-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-clip-stack-gl-private.h" #ifndef GL_CLIP_PLANE0 #define GL_CLIP_PLANE0 0x3000 #define GL_CLIP_PLANE1 0x3001 #define GL_CLIP_PLANE2 0x3002 #define GL_CLIP_PLANE3 0x3003 #define GL_CLIP_PLANE4 0x3004 #define GL_CLIP_PLANE5 0x3005 #endif static void add_stencil_clip_rectangle (CoglFramebuffer *framebuffer, CoglMatrixEntry *modelview_entry, float x_1, float y_1, float x_2, float y_2, gboolean merge) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglMatrixEntry *old_projection_entry, *old_modelview_entry; /* NB: This can be called while flushing the journal so we need * to be very conservative with what state we change. */ old_projection_entry = g_steal_pointer (&ctx->current_projection_entry); old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry); ctx->current_projection_entry = projection_stack->last_entry; ctx->current_modelview_entry = modelview_entry; GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( ctx, glDepthMask (FALSE) ); if (merge) { /* Add one to every pixel of the stencil buffer in the rectangle */ GE( ctx, glStencilFunc (GL_NEVER, 0x1, 0x3) ); GE( ctx, glStencilOp (GL_INCR, GL_INCR, GL_INCR) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, x_1, y_1, x_2, y_2); /* Subtract one from all pixels in the stencil buffer so that only pixels where both the original stencil buffer and the rectangle are set will be valid */ GE( ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR) ); ctx->current_projection_entry = &ctx->identity_entry; ctx->current_modelview_entry = &ctx->identity_entry; _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); } else { GE( ctx, glEnable (GL_STENCIL_TEST) ); GE( ctx, glStencilMask (0x1) ); /* Initially disallow everything */ GE( ctx, glClearStencil (0) ); GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) ); /* Punch out a hole to allow the rectangle */ GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, x_1, y_1, x_2, y_2); } ctx->current_projection_entry = old_projection_entry; ctx->current_modelview_entry = old_modelview_entry; /* Restore the stencil mode */ GE( ctx, glDepthMask (TRUE) ); GE( ctx, glColorMask (TRUE, TRUE, TRUE, TRUE) ); GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); } static void add_stencil_clip_region (CoglFramebuffer *framebuffer, cairo_region_t *region, gboolean merge) { CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglMatrixEntry *old_projection_entry, *old_modelview_entry; CoglMatrix matrix; int num_rectangles = cairo_region_num_rectangles (region); int i; CoglVertexP2 *vertices; /* NB: This can be called while flushing the journal so we need * to be very conservative with what state we change. */ old_projection_entry = g_steal_pointer (&ctx->current_projection_entry); old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry); ctx->current_projection_entry = &ctx->identity_entry; ctx->current_modelview_entry = &ctx->identity_entry; /* The coordinates in the region are meant to be window coordinates, * make a matrix that translates those across the viewport, and into * the default [-1, -1, 1, 1] range. */ cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, -1, 1, 0); cogl_matrix_scale (&matrix, 2.0 / framebuffer->viewport_width, - 2.0 / framebuffer->viewport_height, 1); cogl_matrix_translate (&matrix, - framebuffer->viewport_x, - framebuffer->viewport_y, 0); GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( ctx, glDepthMask (FALSE) ); if (merge) { GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x3) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_INCR) ); } else { GE( ctx, glEnable (GL_STENCIL_TEST) ); GE( ctx, glStencilMask (0x1) ); /* Initially disallow everything */ GE( ctx, glClearStencil (0) ); GE( ctx, glClear (GL_STENCIL_BUFFER_BIT) ); /* Punch out holes to allow the rectangles */ GE( ctx, glStencilFunc (GL_ALWAYS, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_REPLACE) ); } vertices = g_alloca (sizeof (CoglVertexP2) * num_rectangles * 6); for (i = 0; i < num_rectangles; i++) { cairo_rectangle_int_t rect; float x1, y1, z1, w1; float x2, y2, z2, w2; CoglVertexP2 *v = vertices + i * 6; cairo_region_get_rectangle (region, i, &rect); x1 = rect.x; y1 = rect.y; z1 = 0.f; w1 = 1.f; x2 = rect.x + rect.width; y2 = rect.y + rect.height; z2 = 0.f; w2 = 1.f; cogl_matrix_transform_point (&matrix, &x1, &y1, &z1, &w1); cogl_matrix_transform_point (&matrix, &x2, &y2, &z2, &w2); v[0].x = x1; v[0].y = y1; v[1].x = x1; v[1].y = y2; v[2].x = x2; v[2].y = y1; v[3].x = x1; v[3].y = y2; v[4].x = x2; v[4].y = y2; v[5].x = x2; v[5].y = y1; } cogl_2d_primitives_immediate (framebuffer, ctx->stencil_pipeline, COGL_VERTICES_MODE_TRIANGLES, vertices, 6 * num_rectangles); if (merge) { /* Subtract one from all pixels in the stencil buffer so that * only pixels where both the original stencil buffer and the * region are set will be valid */ GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_DECR) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); } ctx->current_projection_entry = old_projection_entry; ctx->current_modelview_entry = old_modelview_entry; /* Restore the stencil mode */ GE (ctx, glDepthMask (TRUE)); GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE)); GE( ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1) ); GE( ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP) ); } typedef void (*SilhouettePaintCallback) (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, void *user_data); static void add_stencil_clip_silhouette (CoglFramebuffer *framebuffer, SilhouettePaintCallback silhouette_callback, CoglMatrixEntry *modelview_entry, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, gboolean merge, gboolean need_clear, void *user_data) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglMatrixEntry *old_projection_entry, *old_modelview_entry; /* NB: This can be called while flushing the journal so we need * to be very conservative with what state we change. */ old_projection_entry = g_steal_pointer (&ctx->current_projection_entry); old_modelview_entry = g_steal_pointer (&ctx->current_modelview_entry); ctx->current_projection_entry = projection_stack->last_entry; ctx->current_modelview_entry = modelview_entry; _cogl_pipeline_flush_gl_state (ctx, ctx->stencil_pipeline, framebuffer, FALSE, FALSE); GE( ctx, glEnable (GL_STENCIL_TEST) ); GE( ctx, glColorMask (FALSE, FALSE, FALSE, FALSE) ); GE( ctx, glDepthMask (FALSE) ); if (merge) { GE (ctx, glStencilMask (2)); GE (ctx, glStencilFunc (GL_LEQUAL, 0x2, 0x6)); } else { /* If we're not using the stencil buffer for clipping then we don't need to clear the whole stencil buffer, just the area that will be drawn */ if (need_clear) /* If this is being called from the clip stack code then it will have set up a scissor for the minimum bounding box of all of the clips. That box will likely mean that this _cogl_clear won't need to clear the entire buffer. _cogl_framebuffer_clear_without_flush4f is used instead of cogl_clear because it won't try to flush the journal */ _cogl_framebuffer_clear_without_flush4f (framebuffer, COGL_BUFFER_BIT_STENCIL, 0, 0, 0, 0); else { /* Just clear the bounding box */ GE( ctx, glStencilMask (~(GLuint) 0) ); GE( ctx, glStencilOp (GL_ZERO, GL_ZERO, GL_ZERO) ); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, bounds_x1, bounds_y1, bounds_x2, bounds_y2); } GE (ctx, glStencilMask (1)); GE (ctx, glStencilFunc (GL_LEQUAL, 0x1, 0x3)); } GE (ctx, glStencilOp (GL_INVERT, GL_INVERT, GL_INVERT)); silhouette_callback (framebuffer, ctx->stencil_pipeline, user_data); if (merge) { /* Now we have the new stencil buffer in bit 1 and the old stencil buffer in bit 0 so we need to intersect them */ GE (ctx, glStencilMask (3)); GE (ctx, glStencilFunc (GL_NEVER, 0x2, 0x3)); GE (ctx, glStencilOp (GL_DECR, GL_DECR, GL_DECR)); /* Decrement all of the bits twice so that only pixels where the value is 3 will remain */ ctx->current_projection_entry = &ctx->identity_entry; ctx->current_modelview_entry = &ctx->identity_entry; _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); _cogl_rectangle_immediate (framebuffer, ctx->stencil_pipeline, -1.0, -1.0, 1.0, 1.0); } ctx->current_projection_entry = old_projection_entry; ctx->current_modelview_entry = old_modelview_entry; GE (ctx, glStencilMask (~(GLuint) 0)); GE (ctx, glDepthMask (TRUE)); GE (ctx, glColorMask (TRUE, TRUE, TRUE, TRUE)); GE (ctx, glStencilFunc (GL_EQUAL, 0x1, 0x1)); GE (ctx, glStencilOp (GL_KEEP, GL_KEEP, GL_KEEP)); } static void paint_primitive_silhouette (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, void *user_data) { _cogl_primitive_draw (user_data, framebuffer, pipeline, COGL_DRAW_SKIP_JOURNAL_FLUSH | COGL_DRAW_SKIP_PIPELINE_VALIDATION | COGL_DRAW_SKIP_FRAMEBUFFER_FLUSH); } static void add_stencil_clip_primitive (CoglFramebuffer *framebuffer, CoglMatrixEntry *modelview_entry, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2, gboolean merge, gboolean need_clear) { add_stencil_clip_silhouette (framebuffer, paint_primitive_silhouette, modelview_entry, bounds_x1, bounds_y1, bounds_x2, bounds_y2, merge, need_clear, primitive); } void _cogl_clip_stack_gl_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; gboolean using_stencil_buffer = FALSE; int scissor_x0; int scissor_y0; int scissor_x1; int scissor_y1; CoglClipStack *entry; int scissor_y_start; /* If we have already flushed this state then we don't need to do anything */ if (ctx->current_clip_stack_valid) { if (ctx->current_clip_stack == stack) return; _cogl_clip_stack_unref (ctx->current_clip_stack); } ctx->current_clip_stack_valid = TRUE; ctx->current_clip_stack = _cogl_clip_stack_ref (stack); GE( ctx, glDisable (GL_STENCIL_TEST) ); /* If the stack is empty then there's nothing else to do */ if (stack == NULL) { COGL_NOTE (CLIPPING, "Flushed empty clip stack"); GE (ctx, glDisable (GL_SCISSOR_TEST)); return; } /* Calculate the scissor rect first so that if we eventually have to clear the stencil buffer then the clear will be clipped to the intersection of all of the bounding boxes. This saves having to clear the whole stencil buffer */ _cogl_clip_stack_get_bounds (stack, &scissor_x0, &scissor_y0, &scissor_x1, &scissor_y1); /* Enable scissoring as soon as possible */ if (scissor_x0 >= scissor_x1 || scissor_y0 >= scissor_y1) scissor_x0 = scissor_y0 = scissor_x1 = scissor_y1 = scissor_y_start = 0; else { /* We store the entry coordinates in Cogl coordinate space * but OpenGL requires the window origin to be the bottom * left so we may need to convert the incoming coordinates. * * NB: Cogl forces all offscreen rendering to be done upside * down so in this case no conversion is needed. */ if (cogl_is_offscreen (framebuffer)) scissor_y_start = scissor_y0; else { int framebuffer_height = cogl_framebuffer_get_height (framebuffer); scissor_y_start = framebuffer_height - scissor_y1; } } COGL_NOTE (CLIPPING, "Flushing scissor to (%i, %i, %i, %i)", scissor_x0, scissor_y0, scissor_x1, scissor_y1); GE (ctx, glEnable (GL_SCISSOR_TEST)); GE (ctx, glScissor (scissor_x0, scissor_y_start, scissor_x1 - scissor_x0, scissor_y1 - scissor_y0)); /* Add all of the entries. This will end up adding them in the reverse order that they were specified but as all of the clips are intersecting it should work out the same regardless of the order */ for (entry = stack; entry; entry = entry->parent) { switch (entry->type) { case COGL_CLIP_STACK_PRIMITIVE: { CoglClipStackPrimitive *primitive_entry = (CoglClipStackPrimitive *) entry; COGL_NOTE (CLIPPING, "Adding stencil clip for primitive"); add_stencil_clip_primitive (framebuffer, primitive_entry->matrix_entry, primitive_entry->primitive, primitive_entry->bounds_x1, primitive_entry->bounds_y1, primitive_entry->bounds_x2, primitive_entry->bounds_y2, using_stencil_buffer, TRUE); using_stencil_buffer = TRUE; break; } case COGL_CLIP_STACK_RECT: { CoglClipStackRect *rect = (CoglClipStackRect *) entry; /* We don't need to do anything extra if the clip for this rectangle was entirely described by its scissor bounds */ if (!rect->can_be_scissor) { COGL_NOTE (CLIPPING, "Adding stencil clip for rectangle"); add_stencil_clip_rectangle (framebuffer, rect->matrix_entry, rect->x0, rect->y0, rect->x1, rect->y1, using_stencil_buffer); using_stencil_buffer = TRUE; } break; } case COGL_CLIP_STACK_REGION: { CoglClipStackRegion *region = (CoglClipStackRegion *) entry; /* If nrectangles <= 1, it can be fully represented with the * scissor clip. */ if (cairo_region_num_rectangles (region->region) > 1) { COGL_NOTE (CLIPPING, "Adding stencil clip for region"); add_stencil_clip_region (framebuffer, region->region, using_stencil_buffer); using_stencil_buffer = TRUE; } break; } case COGL_CLIP_STACK_WINDOW_RECT: break; /* We don't need to do anything for window space rectangles because * their functionality is entirely implemented by the entry bounding * box */ } } } muffin-6.4.1/cogl/cogl/driver/gl/gl/0000775000175000017500000000000014723361714016111 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/driver/gl/gl/cogl-texture-driver-gl.c0000664000175000017500000003527614723361714022605 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Matthew Allum * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-private.h" #include "cogl-util.h" #include "cogl-bitmap.h" #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "driver/gl/cogl-bitmap-gl-private.h" #include #include #include #ifndef GL_TEXTURE_SWIZZLE_RGBA #define GL_TEXTURE_SWIZZLE_RGBA 0x8E46 #endif static GLuint _cogl_texture_driver_gen (CoglContext *ctx, GLenum gl_target, CoglPixelFormat internal_format) { GLuint tex; GE (ctx, glGenTextures (1, &tex)); _cogl_bind_gl_texture_transient (gl_target, tex); switch (gl_target) { case GL_TEXTURE_2D: /* In case automatic mipmap generation gets disabled for this * texture but a minification filter depending on mipmap * interpolation is selected then we initialize the max mipmap * level to 0 so OpenGL will consider the texture storage to be * "complete". */ #ifdef HAVE_COGL_GL if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL)) GE( ctx, glTexParameteri (gl_target, GL_TEXTURE_MAX_LEVEL, 0)); #endif /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ GE( ctx, glTexParameteri (gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); break; case GL_TEXTURE_RECTANGLE_ARB: /* Texture rectangles already default to GL_LINEAR so nothing needs to be done */ break; default: g_assert_not_reached(); } /* If the driver doesn't support alpha textures directly then we'll * fake them by setting the swizzle parameters */ if (internal_format == COGL_PIXEL_FORMAT_A_8 && !_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) { static const GLint red_swizzle[] = { GL_ZERO, GL_ZERO, GL_ZERO, GL_RED }; GE( ctx, glTexParameteriv (gl_target, GL_TEXTURE_SWIZZLE_RGBA, red_swizzle) ); } return tex; } /* OpenGL - unlike GLES - can upload a sub region of pixel data from a larger * source buffer */ static void prep_gl_for_pixels_upload_full (CoglContext *ctx, int pixels_rowstride, int image_height, int pixels_src_x, int pixels_src_y, int pixels_bpp) { GE( ctx, glPixelStorei (GL_UNPACK_ROW_LENGTH, pixels_rowstride / pixels_bpp) ); GE( ctx, glPixelStorei (GL_UNPACK_SKIP_PIXELS, pixels_src_x) ); GE( ctx, glPixelStorei (GL_UNPACK_SKIP_ROWS, pixels_src_y) ); _cogl_texture_gl_prep_alignment_for_pixels_upload (ctx, pixels_rowstride); } /* OpenGL - unlike GLES - can download pixel data into a sub region of * a larger destination buffer */ static void prep_gl_for_pixels_download_full (CoglContext *ctx, int image_width, int pixels_rowstride, int image_height, int pixels_src_x, int pixels_src_y, int pixels_bpp) { GE( ctx, glPixelStorei (GL_PACK_ROW_LENGTH, pixels_rowstride / pixels_bpp) ); GE( ctx, glPixelStorei (GL_PACK_SKIP_PIXELS, pixels_src_x) ); GE( ctx, glPixelStorei (GL_PACK_SKIP_ROWS, pixels_src_y) ); _cogl_texture_gl_prep_alignment_for_pixels_download (ctx, pixels_bpp, image_width, pixels_rowstride); } static void _cogl_texture_driver_prep_gl_for_pixels_download (CoglContext *ctx, int image_width, int pixels_rowstride, int pixels_bpp) { prep_gl_for_pixels_download_full (ctx, pixels_rowstride, image_width, 0 /* image height */, 0, 0, /* pixels_src_x/y */ pixels_bpp); } static gboolean _cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx, CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, int width, int height, int level, CoglBitmap *source_bmp, GLuint source_gl_format, GLuint source_gl_type, GError **error) { GLenum gl_target; GLuint gl_handle; uint8_t *data; CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp; gboolean status = TRUE; GError *internal_error = NULL; int level_width; int level_height; g_return_val_if_fail (source_format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (source_format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (source_format, 0); cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error); /* NB: _cogl_bitmap_gl_bind() may return NULL when successfull so we * have to explicitly check the cogl error pointer to catch * problems... */ if (internal_error) { g_propagate_error (error, internal_error); return FALSE; } /* Setup gl alignment to match rowstride and top-left corner */ prep_gl_for_pixels_upload_full (ctx, cogl_bitmap_get_rowstride (source_bmp), 0, src_x, src_y, bpp); _cogl_bind_gl_texture_transient (gl_target, gl_handle); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); _cogl_texture_get_level_size (texture, level, &level_width, &level_height, NULL); if (level_width == width && level_height == height) { /* GL gets upset if you use glTexSubImage2D to initialize the * contents of a mipmap level so we make sure to use * glTexImage2D if we are uploading a full mipmap level. */ ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), width, height, 0, source_gl_format, source_gl_type, data); } else { /* GL gets upset if you use glTexSubImage2D to initialize the * contents of a mipmap level so if this is the first time * we've seen a request to upload to this level we call * glTexImage2D first to assert that the storage for this * level exists. */ if (texture->max_level_set < level) { ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), level_width, level_height, 0, source_gl_format, source_gl_type, NULL); } ctx->glTexSubImage2D (gl_target, level, dst_x, dst_y, width, height, source_gl_format, source_gl_type, data); } if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_bitmap_gl_unbind (source_bmp); return status; } static gboolean _cogl_texture_driver_upload_to_gl (CoglContext *ctx, GLenum gl_target, GLuint gl_handle, CoglBitmap *source_bmp, GLint internal_gl_format, GLuint source_gl_format, GLuint source_gl_type, GError **error) { uint8_t *data; CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp; gboolean status = TRUE; GError *internal_error = NULL; g_return_val_if_fail (source_format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (source_format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (source_format, 0); data = _cogl_bitmap_gl_bind (source_bmp, COGL_BUFFER_ACCESS_READ, 0, /* hints */ &internal_error); /* NB: _cogl_bitmap_gl_bind() may return NULL when successful so we * have to explicitly check the cogl error pointer to catch * problems... */ if (internal_error) { g_propagate_error (error, internal_error); return FALSE; } /* Setup gl alignment to match rowstride and top-left corner */ prep_gl_for_pixels_upload_full (ctx, cogl_bitmap_get_rowstride (source_bmp), 0, 0, 0, bpp); _cogl_bind_gl_texture_transient (gl_target, gl_handle); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); ctx->glTexImage2D (gl_target, 0, internal_gl_format, cogl_bitmap_get_width (source_bmp), cogl_bitmap_get_height (source_bmp), 0, source_gl_format, source_gl_type, data); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_bitmap_gl_unbind (source_bmp); return status; } static gboolean _cogl_texture_driver_gl_get_tex_image (CoglContext *ctx, GLenum gl_target, GLenum dest_gl_format, GLenum dest_gl_type, uint8_t *dest) { GE (ctx, glGetTexImage (gl_target, 0, /* level */ dest_gl_format, dest_gl_type, (GLvoid *)dest)); return TRUE; } static gboolean _cogl_texture_driver_size_supported (CoglContext *ctx, GLenum gl_target, GLenum gl_intformat, GLenum gl_format, GLenum gl_type, int width, int height) { GLenum proxy_target; GLint new_width = 0; if (gl_target == GL_TEXTURE_2D) proxy_target = GL_PROXY_TEXTURE_2D; #ifdef HAVE_COGL_GL else if (gl_target == GL_TEXTURE_RECTANGLE_ARB) proxy_target = GL_PROXY_TEXTURE_RECTANGLE_ARB; #endif else /* Unknown target, assume it's not supported */ return FALSE; /* Proxy texture allows for a quick check for supported size */ GE( ctx, glTexImage2D (proxy_target, 0, gl_intformat, width, height, 0 /* border */, gl_format, gl_type, NULL) ); GE( ctx, glGetTexLevelParameteriv (proxy_target, 0, GL_TEXTURE_WIDTH, &new_width) ); return new_width != 0; } static CoglPixelFormat _cogl_texture_driver_find_best_gl_get_data_format (CoglContext *context, CoglPixelFormat format, GLenum *closest_gl_format, GLenum *closest_gl_type) { return context->driver_vtable->pixel_format_to_gl (context, format, NULL, /* don't need */ closest_gl_format, closest_gl_type); } const CoglTextureDriver _cogl_texture_driver_gl = { _cogl_texture_driver_gen, _cogl_texture_driver_upload_subregion_to_gl, _cogl_texture_driver_upload_to_gl, _cogl_texture_driver_prep_gl_for_pixels_download, _cogl_texture_driver_gl_get_tex_image, _cogl_texture_driver_size_supported, _cogl_texture_driver_find_best_gl_get_data_format }; muffin-6.4.1/cogl/cogl/driver/gl/gl/cogl-driver-gl.c0000664000175000017500000004405614723361714021103 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-context-private.h" #include "cogl-feature-private.h" #include "cogl-renderer-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-framebuffer-gl-private.h" #include "driver/gl/cogl-texture-2d-gl-private.h" #include "driver/gl/cogl-attribute-gl-private.h" #include "driver/gl/cogl-clip-stack-gl-private.h" #include "driver/gl/cogl-buffer-gl-private.h" static gboolean _cogl_driver_gl_real_context_init (CoglContext *context) { _cogl_driver_gl_context_init (context); if ((context->driver == COGL_DRIVER_GL3)) { GLuint vertex_array; /* In a forward compatible context, GL 3 doesn't support rendering * using the default vertex array object. Cogl doesn't use vertex * array objects yet so for now we just create a dummy array * object that we will use as our own default object. Eventually * it could be good to attach the vertex array objects to * CoglPrimitives */ context->glGenVertexArrays (1, &vertex_array); context->glBindVertexArray (vertex_array); } /* As far as I can tell, GL_POINT_SPRITE doesn't have any effect unless GL_COORD_REPLACE is enabled for an individual layer. Therefore it seems like it should be ok to just leave it enabled all the time instead of having to have a set property on each pipeline to track whether any layers have point sprite coords enabled. We don't need to do this for GL3 or GLES2 because point sprites are handled using a builtin varying in the shader. */ if (context->driver == COGL_DRIVER_GL) GE (context, glEnable (GL_POINT_SPRITE)); /* There's no enable for this in GLES2, it's always on */ if (context->driver == COGL_DRIVER_GL || context->driver == COGL_DRIVER_GL3) GE (context, glEnable (GL_PROGRAM_POINT_SIZE) ); return TRUE; } static gboolean _cogl_driver_pixel_format_from_gl_internal (CoglContext *context, GLenum gl_int_format, CoglPixelFormat *out_format) { /* It doesn't really matter we convert to exact same format (some have no cogl match anyway) since format is re-matched against cogl when getting or setting texture image data. */ switch (gl_int_format) { case GL_ALPHA: case GL_ALPHA4: case GL_ALPHA8: case GL_ALPHA12: case GL_ALPHA16: /* Cogl only supports one single-component texture so if we have * ended up with a red texture then it is probably being used as * a component-alpha texture */ case GL_RED: *out_format = COGL_PIXEL_FORMAT_A_8; return TRUE; case GL_LUMINANCE: case GL_LUMINANCE4: case GL_LUMINANCE8: case GL_LUMINANCE12: case GL_LUMINANCE16: *out_format = COGL_PIXEL_FORMAT_G_8; return TRUE; case GL_RG: *out_format = COGL_PIXEL_FORMAT_RG_88; return TRUE; case GL_RGB: case GL_RGB4: case GL_RGB5: case GL_RGB8: case GL_RGB10: case GL_RGB12: case GL_RGB16: case GL_R3_G3_B2: *out_format = COGL_PIXEL_FORMAT_RGB_888; return TRUE; case GL_RGBA: case GL_RGBA2: case GL_RGBA4: case GL_RGB5_A1: case GL_RGBA8: case GL_RGB10_A2: case GL_RGBA12: case GL_RGBA16: *out_format = COGL_PIXEL_FORMAT_RGBA_8888; return TRUE; } return FALSE; } static CoglPixelFormat _cogl_driver_pixel_format_to_gl (CoglContext *context, CoglPixelFormat format, GLenum *out_glintformat, GLenum *out_glformat, GLenum *out_gltype) { CoglPixelFormat required_format; GLenum glintformat = 0; GLenum glformat = 0; GLenum gltype = 0; required_format = format; /* Find GL equivalents */ switch (format) { case COGL_PIXEL_FORMAT_A_8: /* If the driver doesn't natively support alpha textures then we * will use a red component texture with a swizzle to implement * the texture */ if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) == 0) { glintformat = GL_RED; glformat = GL_RED; } else { glintformat = GL_ALPHA; glformat = GL_ALPHA; } gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_G_8: glintformat = GL_LUMINANCE; glformat = GL_LUMINANCE; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_RG_88: if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG)) { glintformat = GL_RG; glformat = GL_RG; } else { /* If red-green textures aren't supported then we'll use RGB * as an internal format. Note this should only end up * mattering for downloading the data because Cogl will * refuse to allocate a texture with RG components if RG * textures aren't supported */ glintformat = GL_RGB; glformat = GL_RGB; required_format = COGL_PIXEL_FORMAT_RGB_888; } gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_RGB_888: glintformat = GL_RGB; glformat = GL_RGB; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_BGR_888: glintformat = GL_RGB; glformat = GL_BGR; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_BGRA_8888_PRE: glintformat = GL_RGBA; glformat = GL_BGRA; gltype = GL_UNSIGNED_BYTE; break; /* The following two types of channel ordering * have no GL equivalent unless defined using * system word byte ordering */ case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: glintformat = GL_RGBA; glformat = GL_BGRA; #if G_BYTE_ORDER == G_LITTLE_ENDIAN gltype = GL_UNSIGNED_INT_8_8_8_8; #else gltype = GL_UNSIGNED_INT_8_8_8_8_REV; #endif break; case COGL_PIXEL_FORMAT_ABGR_8888: case COGL_PIXEL_FORMAT_ABGR_8888_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; #if G_BYTE_ORDER == G_LITTLE_ENDIAN gltype = GL_UNSIGNED_INT_8_8_8_8; #else gltype = GL_UNSIGNED_INT_8_8_8_8_REV; #endif break; case COGL_PIXEL_FORMAT_RGBA_1010102: case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_INT_10_10_10_2; break; case COGL_PIXEL_FORMAT_BGRA_1010102: case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: glintformat = GL_RGBA; glformat = GL_BGRA; gltype = GL_UNSIGNED_INT_10_10_10_2; break; case COGL_PIXEL_FORMAT_ABGR_2101010: case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_INT_2_10_10_10_REV; break; case COGL_PIXEL_FORMAT_ARGB_2101010: case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: glintformat = GL_RGBA; glformat = GL_BGRA; gltype = GL_UNSIGNED_INT_2_10_10_10_REV; break; /* The following three types of channel ordering * are always defined using system word byte * ordering (even according to GLES spec) */ case COGL_PIXEL_FORMAT_RGB_565: glintformat = GL_RGB; glformat = GL_RGB; gltype = GL_UNSIGNED_SHORT_5_6_5; break; case COGL_PIXEL_FORMAT_RGBA_4444: case COGL_PIXEL_FORMAT_RGBA_4444_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_4_4_4_4; break; case COGL_PIXEL_FORMAT_RGBA_5551: case COGL_PIXEL_FORMAT_RGBA_5551_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_5_5_5_1; break; case COGL_PIXEL_FORMAT_DEPTH_16: glintformat = GL_DEPTH_COMPONENT16; glformat = GL_DEPTH_COMPONENT; gltype = GL_UNSIGNED_SHORT; break; case COGL_PIXEL_FORMAT_DEPTH_32: glintformat = GL_DEPTH_COMPONENT32; glformat = GL_DEPTH_COMPONENT; gltype = GL_UNSIGNED_INT; break; case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: glintformat = GL_DEPTH_STENCIL; glformat = GL_DEPTH_STENCIL; gltype = GL_UNSIGNED_INT_24_8; break; case COGL_PIXEL_FORMAT_ANY: case COGL_PIXEL_FORMAT_YUV: g_assert_not_reached (); break; } /* All of the pixel formats are handled above so if this hits then we've been given an invalid pixel format */ g_assert (glformat != 0); if (out_glintformat != NULL) *out_glintformat = glintformat; if (out_glformat != NULL) *out_glformat = glformat; if (out_gltype != NULL) *out_gltype = gltype; return required_format; } static gboolean _cogl_get_gl_version (CoglContext *ctx, int *major_out, int *minor_out) { const char *version_string; /* Get the OpenGL version number */ if ((version_string = _cogl_context_get_gl_version (ctx)) == NULL) return FALSE; return _cogl_gl_util_parse_gl_version (version_string, major_out, minor_out); } static gboolean check_gl_version (CoglContext *ctx, char **gl_extensions, GError **error) { int major, minor; if (!_cogl_get_gl_version (ctx, &major, &minor)) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_UNKNOWN_VERSION, "The OpenGL version could not be determined"); return FALSE; } /* We require GLSL 1.20, which is implied by OpenGL 2.1. */ if (!COGL_CHECK_GL_VERSION (major, minor, 2, 1)) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_INVALID_VERSION, "OpenGL 2.1 or better is required"); return FALSE; } return TRUE; } static gboolean _cogl_driver_update_features (CoglContext *ctx, GError **error) { unsigned long private_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)] = { 0 }; char **gl_extensions; const char *glsl_version; int gl_major = 0, gl_minor = 0; int i; /* We have to special case getting the pointer to the glGetString* functions because we need to use them to determine what functions we can expect */ ctx->glGetString = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetString", TRUE); ctx->glGetStringi = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetStringi", TRUE); ctx->glGetIntegerv = (void *) _cogl_renderer_get_proc_address (ctx->display->renderer, "glGetIntegerv", TRUE); gl_extensions = _cogl_context_get_gl_extensions (ctx); if (!check_gl_version (ctx, gl_extensions, error)) return FALSE; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) { char *all_extensions = g_strjoinv (" ", gl_extensions); COGL_NOTE (WINSYS, "Checking features\n" " GL_VENDOR: %s\n" " GL_RENDERER: %s\n" " GL_VERSION: %s\n" " GL_EXTENSIONS: %s", ctx->glGetString (GL_VENDOR), ctx->glGetString (GL_RENDERER), _cogl_context_get_gl_version (ctx), all_extensions); g_free (all_extensions); } _cogl_get_gl_version (ctx, &gl_major, &gl_minor); _cogl_gpu_info_init (ctx, &ctx->gpu); ctx->glsl_major = 1; ctx->glsl_minor = 2; ctx->glsl_version_to_use = 120; glsl_version = (char *)ctx->glGetString (GL_SHADING_LANGUAGE_VERSION); _cogl_gl_util_parse_gl_version (glsl_version, &ctx->glsl_major, &ctx->glsl_minor); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); _cogl_feature_check_ext_functions (ctx, gl_major, gl_minor, gl_extensions); if (_cogl_check_extension ("GL_MESA_pack_invert", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, TRUE); if (!ctx->glGenRenderbuffers) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, "Framebuffer objects are required to use the GL driver"); return FALSE; } COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS, TRUE); if (ctx->glBlitFramebuffer) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_PBOS, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, TRUE); COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); if (ctx->glEGLImageTargetTexture2D) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, TRUE); if (_cogl_check_extension ("GL_EXT_packed_depth_stencil", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL, TRUE); if (ctx->glGenSamplers) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS, TRUE); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 3) || _cogl_check_extension ("GL_ARB_texture_swizzle", gl_extensions) || _cogl_check_extension ("GL_EXT_texture_swizzle", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE, TRUE); if (ctx->driver == COGL_DRIVER_GL) { /* Features which are not available in GL 3 */ COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, TRUE); } COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ANY_GL, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_FORMAT_CONVERSION, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_QUERY_TEXTURE_PARAMETERS, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_MAX_LEVEL, TRUE); if (ctx->glFenceSync) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_FENCE, TRUE); if (COGL_CHECK_GL_VERSION (gl_major, gl_minor, 3, 0) || _cogl_check_extension ("GL_ARB_texture_rg", gl_extensions)) COGL_FLAGS_SET (ctx->features, COGL_FEATURE_ID_TEXTURE_RG, TRUE); /* Cache features */ for (i = 0; i < G_N_ELEMENTS (private_features); i++) ctx->private_features[i] |= private_features[i]; g_strfreev (gl_extensions); if (!COGL_FLAGS_GET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && !COGL_FLAGS_GET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_SWIZZLE)) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_NO_SUITABLE_DRIVER_FOUND, "The GL_ARB_texture_swizzle extension is required " "to use the GL3 driver"); return FALSE; } return TRUE; } const CoglDriverVtable _cogl_driver_gl = { _cogl_driver_gl_real_context_init, _cogl_driver_gl_context_deinit, _cogl_driver_pixel_format_from_gl_internal, _cogl_driver_pixel_format_to_gl, _cogl_driver_update_features, _cogl_offscreen_gl_allocate, _cogl_offscreen_gl_free, _cogl_framebuffer_gl_flush_state, _cogl_framebuffer_gl_clear, _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, _cogl_framebuffer_gl_draw_attributes, _cogl_framebuffer_gl_draw_indexed_attributes, _cogl_framebuffer_gl_read_pixels_into_bitmap, _cogl_texture_2d_gl_free, _cogl_texture_2d_gl_can_create, _cogl_texture_2d_gl_init, _cogl_texture_2d_gl_allocate, _cogl_texture_2d_gl_copy_from_framebuffer, _cogl_texture_2d_gl_get_gl_handle, _cogl_texture_2d_gl_generate_mipmap, _cogl_texture_2d_gl_copy_from_bitmap, _cogl_texture_2d_gl_is_get_data_supported, _cogl_texture_2d_gl_get_data, _cogl_gl_flush_attributes_state, _cogl_clip_stack_gl_flush, _cogl_buffer_gl_create, _cogl_buffer_gl_destroy, _cogl_buffer_gl_map_range, _cogl_buffer_gl_unmap, _cogl_buffer_gl_set_data, }; muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-opengl.c0000664000175000017500000011457014723361714021674 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-pipeline-private.h" #include "cogl-context-private.h" #include "cogl-texture-private.h" #include "cogl-framebuffer-private.h" #include "cogl-offscreen.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "driver/gl/cogl-pipeline-progend-glsl-private.h" #include #include #include /* * GL/GLES compatability defines for pipeline thingies: */ /* These aren't defined in the GLES headers */ #ifndef GL_POINT_SPRITE #define GL_POINT_SPRITE 0x8861 #endif #ifndef GL_COORD_REPLACE #define GL_COORD_REPLACE 0x8862 #endif #ifndef GL_CLAMP_TO_BORDER #define GL_CLAMP_TO_BORDER 0x812d #endif static void texture_unit_init (CoglContext *ctx, CoglTextureUnit *unit, int index_) { unit->index = index_; unit->enabled_gl_target = 0; unit->gl_texture = 0; unit->gl_target = 0; unit->dirty_gl_texture = FALSE; unit->matrix_stack = cogl_matrix_stack_new (ctx); unit->layer = NULL; unit->layer_changes_since_flush = 0; unit->texture_storage_changed = FALSE; } static void texture_unit_free (CoglTextureUnit *unit) { if (unit->layer) cogl_object_unref (unit->layer); cogl_object_unref (unit->matrix_stack); } CoglTextureUnit * _cogl_get_texture_unit (int index_) { _COGL_GET_CONTEXT (ctx, NULL); if (ctx->texture_units->len < (index_ + 1)) { int i; int prev_len = ctx->texture_units->len; ctx->texture_units = g_array_set_size (ctx->texture_units, index_ + 1); for (i = prev_len; i <= index_; i++) { CoglTextureUnit *unit = &g_array_index (ctx->texture_units, CoglTextureUnit, i); texture_unit_init (ctx, unit, i); } } return &g_array_index (ctx->texture_units, CoglTextureUnit, index_); } void _cogl_destroy_texture_units (CoglContext *ctx) { int i; for (i = 0; i < ctx->texture_units->len; i++) { CoglTextureUnit *unit = &g_array_index (ctx->texture_units, CoglTextureUnit, i); texture_unit_free (unit); } g_array_free (ctx->texture_units, TRUE); } void _cogl_set_active_texture_unit (int unit_index) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->active_texture_unit != unit_index) { GE (ctx, glActiveTexture (GL_TEXTURE0 + unit_index)); ctx->active_texture_unit = unit_index; } } /* Note: _cogl_bind_gl_texture_transient conceptually has slightly * different semantics to OpenGL's glBindTexture because Cogl never * cares about tracking multiple textures bound to different targets * on the same texture unit. * * glBindTexture lets you bind multiple textures to a single texture * unit if they are bound to different targets. So it does something * like: * unit->current_texture[target] = texture; * * Cogl only lets you associate one texture with the currently active * texture unit, so the target is basically a redundant parameter * that's implicitly set on that texture. * * Technically this is just a thin wrapper around glBindTexture so * actually it does have the GL semantics but it seems worth * mentioning the conceptual difference in case anyone wonders why we * don't associate the gl_texture with a gl_target in the * CoglTextureUnit. */ void _cogl_bind_gl_texture_transient (GLenum gl_target, GLuint gl_texture) { CoglTextureUnit *unit; _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* We choose to always make texture unit 1 active for transient * binds so that in the common case where multitexturing isn't used * we can simply ignore the state of this texture unit. Notably we * didn't use a large texture unit (.e.g. (GL_MAX_TEXTURE_UNITS - 1) * in case the driver doesn't have a sparse data structure for * texture units. */ _cogl_set_active_texture_unit (1); unit = _cogl_get_texture_unit (1); if (unit->gl_texture == gl_texture && !unit->dirty_gl_texture) return; GE (ctx, glBindTexture (gl_target, gl_texture)); unit->dirty_gl_texture = TRUE; } void _cogl_delete_gl_texture (GLuint gl_texture) { int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); for (i = 0; i < ctx->texture_units->len; i++) { CoglTextureUnit *unit = &g_array_index (ctx->texture_units, CoglTextureUnit, i); if (unit->gl_texture == gl_texture) { unit->gl_texture = 0; unit->gl_target = 0; unit->dirty_gl_texture = FALSE; } } GE (ctx, glDeleteTextures (1, &gl_texture)); } /* Whenever the underlying GL texture storage of a CoglTexture is * changed (e.g. due to migration out of a texture atlas) then we are * notified. This lets us ensure that we reflush that texture's state * if it is reused again with the same texture unit. */ void _cogl_pipeline_texture_storage_change_notify (CoglTexture *texture) { int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); for (i = 0; i < ctx->texture_units->len; i++) { CoglTextureUnit *unit = &g_array_index (ctx->texture_units, CoglTextureUnit, i); if (unit->layer && _cogl_pipeline_layer_get_texture (unit->layer) == texture) unit->texture_storage_changed = TRUE; /* NB: the texture may be bound to multiple texture units so * we continue to check the rest */ } } #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) static gboolean blend_factor_uses_constant (GLenum blend_factor) { return (blend_factor == GL_CONSTANT_COLOR || blend_factor == GL_ONE_MINUS_CONSTANT_COLOR || blend_factor == GL_CONSTANT_ALPHA || blend_factor == GL_ONE_MINUS_CONSTANT_ALPHA); } #endif static void flush_depth_state (CoglContext *ctx, CoglDepthState *depth_state) { gboolean depth_writing_enabled = depth_state->write_enabled; if (ctx->current_draw_buffer) depth_writing_enabled &= ctx->current_draw_buffer->depth_writing_enabled; if (ctx->depth_test_enabled_cache != depth_state->test_enabled) { if (depth_state->test_enabled == TRUE) { GE (ctx, glEnable (GL_DEPTH_TEST)); if (ctx->current_draw_buffer) ctx->current_draw_buffer->depth_buffer_clear_needed = TRUE; } else GE (ctx, glDisable (GL_DEPTH_TEST)); ctx->depth_test_enabled_cache = depth_state->test_enabled; } if (ctx->depth_test_function_cache != depth_state->test_function && depth_state->test_enabled == TRUE) { GE (ctx, glDepthFunc (depth_state->test_function)); ctx->depth_test_function_cache = depth_state->test_function; } if (ctx->depth_writing_enabled_cache != depth_writing_enabled) { GE (ctx, glDepthMask (depth_writing_enabled ? GL_TRUE : GL_FALSE)); ctx->depth_writing_enabled_cache = depth_writing_enabled; } if ((ctx->depth_range_near_cache != depth_state->range_near || ctx->depth_range_far_cache != depth_state->range_far)) { if (ctx->driver == COGL_DRIVER_GLES2) GE (ctx, glDepthRangef (depth_state->range_near, depth_state->range_far)); else GE (ctx, glDepthRange (depth_state->range_near, depth_state->range_far)); ctx->depth_range_near_cache = depth_state->range_near; ctx->depth_range_far_cache = depth_state->range_far; } } UNIT_TEST (check_gl_blend_enable, 0 /* no requirements */, 0 /* no failure cases */) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); /* By default blending should be disabled */ g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); _cogl_framebuffer_flush_journal (test_fb); /* After drawing an opaque rectangle blending should still be * disabled */ g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); cogl_pipeline_set_color4f (pipeline, 0, 0, 0, 0); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); _cogl_framebuffer_flush_journal (test_fb); /* After drawing a transparent rectangle blending should be enabled */ g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 1); cogl_pipeline_set_blend (pipeline, "RGBA=ADD(SRC_COLOR, 0)", NULL); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 1, 1); _cogl_framebuffer_flush_journal (test_fb); /* After setting a blend string that effectively disables blending * then blending should be disabled */ g_assert_cmpint (test_ctx->gl_blend_enable_cache, ==, 0); } static void _cogl_pipeline_flush_color_blend_alpha_depth_state ( CoglPipeline *pipeline, unsigned long pipelines_difference, gboolean with_color_attrib) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (pipelines_difference & COGL_PIPELINE_STATE_BLEND) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_BLEND); CoglPipelineBlendState *blend_state = &authority->big_state->blend_state; #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) if (blend_factor_uses_constant (blend_state->blend_src_factor_rgb) || blend_factor_uses_constant (blend_state ->blend_src_factor_alpha) || blend_factor_uses_constant (blend_state->blend_dst_factor_rgb) || blend_factor_uses_constant (blend_state->blend_dst_factor_alpha)) { float red = cogl_color_get_red_float (&blend_state->blend_constant); float green = cogl_color_get_green_float (&blend_state->blend_constant); float blue = cogl_color_get_blue_float (&blend_state->blend_constant); float alpha = cogl_color_get_alpha_float (&blend_state->blend_constant); GE (ctx, glBlendColor (red, green, blue, alpha)); } GE (ctx, glBlendEquationSeparate (blend_state->blend_equation_rgb, blend_state->blend_equation_alpha)); GE (ctx, glBlendFuncSeparate (blend_state->blend_src_factor_rgb, blend_state->blend_dst_factor_rgb, blend_state->blend_src_factor_alpha, blend_state->blend_dst_factor_alpha)); } #endif if (pipelines_difference & COGL_PIPELINE_STATE_DEPTH) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_DEPTH); CoglDepthState *depth_state = &authority->big_state->depth_state; flush_depth_state (ctx, depth_state); } if (pipelines_difference & COGL_PIPELINE_STATE_CULL_FACE) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_CULL_FACE); CoglPipelineCullFaceState *cull_face_state = &authority->big_state->cull_face_state; if (cull_face_state->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) GE( ctx, glDisable (GL_CULL_FACE) ); else { gboolean invert_winding; GE( ctx, glEnable (GL_CULL_FACE) ); switch (cull_face_state->mode) { case COGL_PIPELINE_CULL_FACE_MODE_NONE: g_assert_not_reached (); case COGL_PIPELINE_CULL_FACE_MODE_FRONT: GE( ctx, glCullFace (GL_FRONT) ); break; case COGL_PIPELINE_CULL_FACE_MODE_BACK: GE( ctx, glCullFace (GL_BACK) ); break; case COGL_PIPELINE_CULL_FACE_MODE_BOTH: GE( ctx, glCullFace (GL_FRONT_AND_BACK) ); break; } /* If we are painting to an offscreen framebuffer then we need to invert the winding of the front face because everything is painted upside down */ invert_winding = cogl_is_offscreen (ctx->current_draw_buffer); switch (cull_face_state->front_winding) { case COGL_WINDING_CLOCKWISE: GE( ctx, glFrontFace (invert_winding ? GL_CCW : GL_CW) ); break; case COGL_WINDING_COUNTER_CLOCKWISE: GE( ctx, glFrontFace (invert_winding ? GL_CW : GL_CCW) ); break; } } } if (pipeline->real_blend_enable != ctx->gl_blend_enable_cache) { if (pipeline->real_blend_enable) GE (ctx, glEnable (GL_BLEND)); else GE (ctx, glDisable (GL_BLEND)); /* XXX: we shouldn't update any other blend state if blending * is disabled! */ ctx->gl_blend_enable_cache = pipeline->real_blend_enable; } } static int get_max_activateable_texture_units (void) { _COGL_GET_CONTEXT (ctx, 0); if (G_UNLIKELY (ctx->max_activateable_texture_units == -1)) { GLint values[3]; int n_values = 0; int i; #ifdef HAVE_COGL_GL if (ctx->driver != COGL_DRIVER_GLES2) { /* GL_MAX_TEXTURE_COORDS defines the number of texture coordinates * that can be uploaded (but doesn't necessarily relate to how many * texture images can be sampled) */ GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_COORDS, values + n_values++)); GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, values + n_values++)); } #endif /* HAVE_COGL_GL */ #ifdef HAVE_COGL_GLES2 if (ctx->driver == COGL_DRIVER_GLES2) { GE (ctx, glGetIntegerv (GL_MAX_VERTEX_ATTRIBS, values + n_values)); /* Two of the vertex attribs need to be used for the position and color */ values[n_values++] -= 2; GE (ctx, glGetIntegerv (GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, values + n_values++)); } #endif #ifdef HAVE_COGL_GL if (ctx->driver == COGL_DRIVER_GL) { /* GL_MAX_TEXTURE_UNITS defines the number of units that are usable from the fixed function pipeline, therefore it isn't available in GLES2. These are also tied to the number of texture coordinates that can be uploaded so it should be less than that available from the shader extensions */ GE (ctx, glGetIntegerv (GL_MAX_TEXTURE_UNITS, values + n_values++)); } #endif g_assert (n_values <= G_N_ELEMENTS (values) && n_values > 0); /* Use the maximum value */ ctx->max_activateable_texture_units = values[0]; for (i = 1; i < n_values; i++) ctx->max_activateable_texture_units = MAX (values[i], ctx->max_activateable_texture_units); } return ctx->max_activateable_texture_units; } typedef struct { int i; unsigned long *layer_differences; } CoglPipelineFlushLayerState; static gboolean flush_layers_common_gl_state_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineFlushLayerState *flush_state = user_data; int unit_index = flush_state->i; CoglTextureUnit *unit = _cogl_get_texture_unit (unit_index); unsigned long layers_difference = flush_state->layer_differences[unit_index]; _COGL_GET_CONTEXT (ctx, FALSE); /* There may not be enough texture units so we can bail out if * that's the case... */ if (G_UNLIKELY (unit_index >= get_max_activateable_texture_units ())) { static gboolean shown_warning = FALSE; if (!shown_warning) { g_warning ("Your hardware does not have enough texture units" "to handle this many texture layers"); shown_warning = TRUE; } return FALSE; } if (layers_difference & COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA) { CoglTexture *texture = _cogl_pipeline_layer_get_texture_real (layer); GLuint gl_texture; GLenum gl_target; if (texture == NULL) texture = COGL_TEXTURE (ctx->default_gl_texture_2d_tex); cogl_texture_get_gl_texture (texture, &gl_texture, &gl_target); _cogl_set_active_texture_unit (unit_index); /* NB: There are several Cogl components and some code in * Clutter that will temporarily bind arbitrary GL textures to * query and modify texture object parameters. If you look at * _cogl_bind_gl_texture_transient() you can see we make sure * that such code always binds to texture unit 1 which means we * can't rely on the unit->gl_texture state if unit->index == 1. * * Because texture unit 1 is a bit special we actually defer any * necessary glBindTexture for it until the end of * _cogl_pipeline_flush_gl_state(). * * NB: we get notified whenever glDeleteTextures is used (see * _cogl_delete_gl_texture()) where we invalidate * unit->gl_texture references to deleted textures so it's safe * to compare unit->gl_texture with gl_texture. (Without the * hook it would be possible to delete a GL texture and create a * new one with the same name and comparing unit->gl_texture and * gl_texture wouldn't detect that.) * * NB: for foreign textures we don't know how the deletion of * the GL texture objects correspond to the deletion of the * CoglTextures so if there was previously a foreign texture * associated with the texture unit then we can't assume that we * aren't seeing a recycled texture name so we have to bind. */ if (unit->gl_texture != gl_texture) { if (unit_index == 1) unit->dirty_gl_texture = TRUE; else GE (ctx, glBindTexture (gl_target, gl_texture)); unit->gl_texture = gl_texture; unit->gl_target = gl_target; } /* The texture_storage_changed boolean indicates if the * CoglTexture's underlying GL texture storage has changed since * it was flushed to the texture unit. We've just flushed the * latest state so we can reset this. */ unit->texture_storage_changed = FALSE; } if ((layers_difference & COGL_PIPELINE_LAYER_STATE_SAMPLER) && _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) { const CoglSamplerCacheEntry *sampler_state; sampler_state = _cogl_pipeline_layer_get_sampler_state (layer); GE( ctx, glBindSampler (unit_index, sampler_state->sampler_object) ); } cogl_object_ref (layer); if (unit->layer != NULL) cogl_object_unref (unit->layer); unit->layer = layer; unit->layer_changes_since_flush = 0; flush_state->i++; return TRUE; } static void _cogl_pipeline_flush_common_gl_state (CoglPipeline *pipeline, unsigned long pipelines_difference, unsigned long *layer_differences, gboolean with_color_attrib) { CoglPipelineFlushLayerState state; _COGL_GET_CONTEXT (ctx, NO_RETVAL); _cogl_pipeline_flush_color_blend_alpha_depth_state (pipeline, pipelines_difference, with_color_attrib); state.i = 0; state.layer_differences = layer_differences; _cogl_pipeline_foreach_layer_internal (pipeline, flush_layers_common_gl_state_cb, &state); } /* Re-assert the layer's wrap modes on the given CoglTexture. * * Note: we don't simply forward the wrap modes to layer->texture * since the actual texture being used may have been overridden. */ static void _cogl_pipeline_layer_forward_wrap_modes (CoglPipelineLayer *layer, CoglTexture *texture) { CoglSamplerCacheWrapMode wrap_mode_s, wrap_mode_t; GLenum gl_wrap_mode_s, gl_wrap_mode_t; if (texture == NULL) return; _cogl_pipeline_layer_get_wrap_modes (layer, &wrap_mode_s, &wrap_mode_t); /* Update the wrap mode on the texture object. The texture backend should cache the value so that it will be a no-op if the object already has the same wrap mode set. The backend is best placed to do this because it knows how many of the coordinates will actually be used (ie, a 1D texture only cares about the 's' coordinate but a 3D texture would use all three). GL uses the wrap mode as part of the texture object state but we are pretending it's part of the per-layer environment state. This will break if the application tries to use different modes in different layers using the same texture. */ if (wrap_mode_s == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) gl_wrap_mode_s = GL_CLAMP_TO_EDGE; else gl_wrap_mode_s = wrap_mode_s; if (wrap_mode_t == COGL_SAMPLER_CACHE_WRAP_MODE_AUTOMATIC) gl_wrap_mode_t = GL_CLAMP_TO_EDGE; else gl_wrap_mode_t = wrap_mode_t; _cogl_texture_gl_flush_legacy_texobj_wrap_modes (texture, gl_wrap_mode_s, gl_wrap_mode_t); } /* OpenGL associates the min/mag filters and repeat modes with the * texture object not the texture unit so we always have to re-assert * the filter and repeat modes whenever we use a texture since it may * be referenced by multiple pipelines with different modes. * * This function is bypassed in favour of sampler objects if * GL_ARB_sampler_objects is advertised. This fallback won't work if * the same texture is bound to multiple layers with different sampler * state. */ static void foreach_texture_unit_update_filter_and_wrap_modes (void) { int i; _COGL_GET_CONTEXT (ctx, NO_RETVAL); for (i = 0; i < ctx->texture_units->len; i++) { CoglTextureUnit *unit = &g_array_index (ctx->texture_units, CoglTextureUnit, i); if (unit->layer) { CoglTexture *texture = _cogl_pipeline_layer_get_texture (unit->layer); if (texture != NULL) { CoglPipelineFilter min; CoglPipelineFilter mag; _cogl_pipeline_layer_get_filters (unit->layer, &min, &mag); _cogl_texture_gl_flush_legacy_texobj_filters (texture, min, mag); _cogl_pipeline_layer_forward_wrap_modes (unit->layer, texture); } } } } typedef struct { int i; unsigned long *layer_differences; } CoglPipelineCompareLayersState; static gboolean compare_layer_differences_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineCompareLayersState *state = user_data; CoglTextureUnit *unit = _cogl_get_texture_unit (state->i); if (unit->layer == layer) state->layer_differences[state->i] = unit->layer_changes_since_flush; else if (unit->layer) { state->layer_differences[state->i] = unit->layer_changes_since_flush; state->layer_differences[state->i] |= _cogl_pipeline_layer_compare_differences (layer, unit->layer); } else state->layer_differences[state->i] = COGL_PIPELINE_LAYER_STATE_ALL_SPARSE; /* XXX: There is always a possibility that a CoglTexture's * underlying GL texture storage has been changed since it was last * bound to a texture unit which is why we have a callback into * _cogl_pipeline_texture_storage_change_notify whenever a textures * underlying GL texture storage changes which will set the * unit->texture_intern_changed flag. If we see that's been set here * then we force an update of the texture state... */ if (unit->texture_storage_changed) state->layer_differences[state->i] |= COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA; state->i++; return TRUE; } typedef struct { CoglFramebuffer *framebuffer; const CoglPipelineVertend *vertend; const CoglPipelineFragend *fragend; CoglPipeline *pipeline; unsigned long *layer_differences; gboolean error_adding_layer; gboolean added_layer; } CoglPipelineAddLayerState; static gboolean vertend_add_layer_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineAddLayerState *state = user_data; const CoglPipelineVertend *vertend = state->vertend; CoglPipeline *pipeline = state->pipeline; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); /* Either generate per layer code snippets or setup the * fixed function glTexEnv for each layer... */ if (G_LIKELY (vertend->add_layer (pipeline, layer, state->layer_differences[unit_index], state->framebuffer))) state->added_layer = TRUE; else { state->error_adding_layer = TRUE; return FALSE; } return TRUE; } static gboolean fragend_add_layer_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineAddLayerState *state = user_data; const CoglPipelineFragend *fragend = state->fragend; CoglPipeline *pipeline = state->pipeline; int unit_index = _cogl_pipeline_layer_get_unit_index (layer); /* Either generate per layer code snippets or setup the * fixed function glTexEnv for each layer... */ if (G_LIKELY (fragend->add_layer (pipeline, layer, state->layer_differences[unit_index]))) state->added_layer = TRUE; else { state->error_adding_layer = TRUE; return FALSE; } return TRUE; } /* * _cogl_pipeline_flush_gl_state: * * Details of override options: * ->fallback_mask: is a bitmask of the pipeline layers that need to be * replaced with the default, fallback textures. The fallback textures are * fully transparent textures so they hopefully wont contribute to the * texture combining. * * The intention of fallbacks is to try and preserve * the number of layers the user is expecting so that texture coordinates * they gave will mostly still correspond to the textures they intended, and * have a fighting chance of looking close to their originally intended * result. * * ->disable_mask: is a bitmask of the pipeline layers that will simply have * texturing disabled. It's only really intended for disabling all layers * > X; i.e. we'd expect to see a contiguous run of 0 starting from the LSB * and at some point the remaining bits flip to 1. It might work to disable * arbitrary layers; though I'm not sure a.t.m how OpenGL would take to * that. * * The intention of the disable_mask is for emitting geometry when the user * hasn't supplied enough texture coordinates for all the layers and it's * not possible to auto generate default texture coordinates for those * layers. * * ->layer0_override_texture: forcibly tells us to bind this GL texture name for * layer 0 instead of plucking the gl_texture from the CoglTexture of layer * 0. * * The intention of this is for any primitives that supports sliced textures. * The code will can iterate each of the slices and re-flush the pipeline * forcing the GL texture of each slice in turn. * * ->wrap_mode_overrides: overrides the wrap modes set on each * layer. This is used to implement the automatic wrap mode. * * XXX: It might also help if we could specify a texture matrix for code * dealing with slicing that would be multiplied with the users own matrix. * * Normaly texture coords in the range [0, 1] refer to the extents of the * texture, but when your GL texture represents a slice of the real texture * (from the users POV) then a texture matrix would be a neat way of * transforming the mapping for each slice. * * Currently for textured rectangles we manually calculate the texture * coords for each slice based on the users given coords, but this solution * isn't ideal. */ void _cogl_pipeline_flush_gl_state (CoglContext *ctx, CoglPipeline *pipeline, CoglFramebuffer *framebuffer, gboolean with_color_attrib, gboolean unknown_color_alpha) { CoglPipeline *current_pipeline = ctx->current_pipeline; unsigned long pipelines_difference; int n_layers; unsigned long *layer_differences; CoglTextureUnit *unit1; const CoglPipelineProgend *progend; COGL_STATIC_TIMER (pipeline_flush_timer, "Mainloop", /* parent */ "Material Flush", "The time spent flushing material state", 0 /* no application private data */); COGL_TIMER_START (_cogl_uprof_context, pipeline_flush_timer); /* Bail out asap if we've been asked to re-flush the already current * pipeline and we can see the pipeline hasn't changed */ if (current_pipeline == pipeline && ctx->current_pipeline_age == pipeline->age && ctx->current_pipeline_with_color_attrib == with_color_attrib && ctx->current_pipeline_unknown_color_alpha == unknown_color_alpha) goto done; else { /* Update derived state (currently just the 'real_blend_enable' * state) and determine a mask of state that differs between the * current pipeline and the one we are flushing. * * Note updating the derived state is done before doing any * pipeline comparisons so that we can correctly compare the * 'real_blend_enable' state itself. */ if (current_pipeline == pipeline) { pipelines_difference = ctx->current_pipeline_changes_since_flush; if (pipelines_difference & COGL_PIPELINE_STATE_AFFECTS_BLENDING || pipeline->unknown_color_alpha != unknown_color_alpha) { gboolean save_real_blend_enable = pipeline->real_blend_enable; _cogl_pipeline_update_real_blend_enable (pipeline, unknown_color_alpha); if (save_real_blend_enable != pipeline->real_blend_enable) pipelines_difference |= COGL_PIPELINE_STATE_REAL_BLEND_ENABLE; } } else if (current_pipeline) { pipelines_difference = ctx->current_pipeline_changes_since_flush; _cogl_pipeline_update_real_blend_enable (pipeline, unknown_color_alpha); pipelines_difference |= _cogl_pipeline_compare_differences (ctx->current_pipeline, pipeline); } else { _cogl_pipeline_update_real_blend_enable (pipeline, unknown_color_alpha); pipelines_difference = COGL_PIPELINE_STATE_ALL; } } /* Get a layer_differences mask for each layer to be flushed */ n_layers = cogl_pipeline_get_n_layers (pipeline); if (n_layers) { CoglPipelineCompareLayersState state; layer_differences = g_alloca (sizeof (unsigned long) * n_layers); memset (layer_differences, 0, sizeof (unsigned long) * n_layers); state.i = 0; state.layer_differences = layer_differences; _cogl_pipeline_foreach_layer_internal (pipeline, compare_layer_differences_cb, &state); } else layer_differences = NULL; /* First flush everything that's the same regardless of which * pipeline backend is being used... * * 1) top level state: * glColor (or skip if a vertex attribute is being used for color) * blend state * alpha test state (except for GLES 2.0) * * 2) then foreach layer: * determine gl_target/gl_texture * bind texture * * Note: After _cogl_pipeline_flush_common_gl_state you can expect * all state of the layers corresponding texture unit to be * updated. */ _cogl_pipeline_flush_common_gl_state (pipeline, pipelines_difference, layer_differences, with_color_attrib); /* Now flush the fragment, vertex and program state according to the * current progend backend. * * Note: Some backends may not support the current pipeline * configuration and in that case it will report and error and we * will look for a different backend. * * NB: if pipeline->progend != COGL_PIPELINE_PROGEND_UNDEFINED then * we have previously managed to successfully flush this pipeline * with the given progend so we will simply use that to avoid * fallback code paths. */ do { const CoglPipelineVertend *vertend; const CoglPipelineFragend *fragend; CoglPipelineAddLayerState state; progend = _cogl_pipeline_progend; if (G_UNLIKELY (!progend->start (pipeline))) continue; vertend = _cogl_pipeline_vertend; vertend->start (pipeline, n_layers, pipelines_difference); state.framebuffer = framebuffer; state.vertend = vertend; state.pipeline = pipeline; state.layer_differences = layer_differences; state.error_adding_layer = FALSE; state.added_layer = FALSE; _cogl_pipeline_foreach_layer_internal (pipeline, vertend_add_layer_cb, &state); if (G_UNLIKELY (state.error_adding_layer)) continue; if (G_UNLIKELY (!vertend->end (pipeline, pipelines_difference))) continue; /* Now prepare the fragment processing state (fragend) * * NB: We can't combine the setup of the vertend and fragend * since the backends that do code generation share * ctx->codegen_source_buffer as a scratch buffer. */ fragend = _cogl_pipeline_fragend; state.fragend = fragend; fragend->start (pipeline, n_layers, pipelines_difference); _cogl_pipeline_foreach_layer_internal (pipeline, fragend_add_layer_cb, &state); if (G_UNLIKELY (state.error_adding_layer)) continue; if (G_UNLIKELY (!fragend->end (pipeline, pipelines_difference))) continue; if (progend->end) progend->end (pipeline, pipelines_difference); break; } while (0); /* FIXME: This reference is actually resulting in lots of * copy-on-write reparenting because one-shot pipelines end up * living for longer than necessary and so any later modification of * the parent will cause a copy-on-write. * * XXX: The issue should largely go away when we switch to using * weak pipelines for overrides. */ cogl_object_ref (pipeline); if (ctx->current_pipeline != NULL) cogl_object_unref (ctx->current_pipeline); ctx->current_pipeline = pipeline; ctx->current_pipeline_changes_since_flush = 0; ctx->current_pipeline_with_color_attrib = with_color_attrib; ctx->current_pipeline_unknown_color_alpha = unknown_color_alpha; ctx->current_pipeline_age = pipeline->age; done: progend = _cogl_pipeline_progend; /* We can't assume the color will be retained between flushes when * using the glsl progend because the generic attribute values are * not stored as part of the program object so they could be * overridden by any attribute changes in another program */ if (!with_color_attrib) { int attribute; CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); int name_index = COGL_ATTRIBUTE_COLOR_NAME_INDEX; attribute = _cogl_pipeline_progend_glsl_get_attrib_location (pipeline, name_index); if (attribute != -1) GE (ctx, glVertexAttrib4f (attribute, cogl_color_get_red_float (&authority->color), cogl_color_get_green_float (&authority->color), cogl_color_get_blue_float (&authority->color), cogl_color_get_alpha_float (&authority->color))); } /* Give the progend a chance to update any uniforms that might not * depend on the material state. This is used on GLES2 to update the * matrices */ if (progend->pre_paint) progend->pre_paint (pipeline, framebuffer); /* Handle the fact that OpenGL associates texture filter and wrap * modes with the texture objects not the texture units... */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS)) foreach_texture_unit_update_filter_and_wrap_modes (); /* If this pipeline has more than one layer then we always need * to make sure we rebind the texture for unit 1. * * NB: various components of Cogl may temporarily bind arbitrary * textures to texture unit 1 so they can query and modify texture * object parameters. cogl-pipeline.c (See * _cogl_bind_gl_texture_transient) */ unit1 = _cogl_get_texture_unit (1); if (cogl_pipeline_get_n_layers (pipeline) > 1 && unit1->dirty_gl_texture) { _cogl_set_active_texture_unit (1); GE (ctx, glBindTexture (unit1->gl_target, unit1->gl_texture)); unit1->dirty_gl_texture = FALSE; } COGL_TIMER_STOP (_cogl_uprof_context, pipeline_flush_timer); } muffin-6.4.1/cogl/cogl/driver/gl/cogl-framebuffer-gl.c0000664000175000017500000014271214723361714021470 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2012 Intel Corporation. * Copyright (C) 2018 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-framebuffer-private.h" #include "cogl-texture-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-framebuffer-gl-private.h" #include "driver/gl/cogl-bitmap-gl-private.h" #include "driver/gl/cogl-buffer-gl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include #include #ifndef GL_FRAMEBUFFER #define GL_FRAMEBUFFER 0x8D40 #endif #ifndef GL_RENDERBUFFER #define GL_RENDERBUFFER 0x8D41 #endif #ifndef GL_STENCIL_ATTACHMENT #define GL_STENCIL_ATTACHMENT 0x8D00 #endif #ifndef GL_COLOR_ATTACHMENT0 #define GL_COLOR_ATTACHMENT0 0x8CE0 #endif #ifndef GL_FRAMEBUFFER_COMPLETE #define GL_FRAMEBUFFER_COMPLETE 0x8CD5 #endif #ifndef GL_STENCIL_INDEX8 #define GL_STENCIL_INDEX8 0x8D48 #endif #ifndef GL_DEPTH_STENCIL #define GL_DEPTH_STENCIL 0x84F9 #endif #ifndef GL_DEPTH24_STENCIL8 #define GL_DEPTH24_STENCIL8 0x88F0 #endif #ifndef GL_DEPTH_ATTACHMENT #define GL_DEPTH_ATTACHMENT 0x8D00 #endif #ifndef GL_DEPTH_STENCIL_ATTACHMENT #define GL_DEPTH_STENCIL_ATTACHMENT 0x821A #endif #ifndef GL_DEPTH_COMPONENT16 #define GL_DEPTH_COMPONENT16 0x81A5 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE 0x8212 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE 0x8213 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE 0x8214 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE 0x8215 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE 0x8216 #endif #ifndef GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE #define GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE 0x8217 #endif #ifndef GL_READ_FRAMEBUFFER #define GL_READ_FRAMEBUFFER 0x8CA8 #endif #ifndef GL_DRAW_FRAMEBUFFER #define GL_DRAW_FRAMEBUFFER 0x8CA9 #endif #ifndef GL_TEXTURE_SAMPLES_IMG #define GL_TEXTURE_SAMPLES_IMG 0x9136 #endif #ifndef GL_PACK_INVERT_MESA #define GL_PACK_INVERT_MESA 0x8758 #endif #ifndef GL_PACK_REVERSE_ROW_ORDER_ANGLE #define GL_PACK_REVERSE_ROW_ORDER_ANGLE 0x93A4 #endif #ifndef GL_BACK_LEFT #define GL_BACK_LEFT 0x0402 #endif #ifndef GL_BACK_RIGHT #define GL_BACK_RIGHT 0x0403 #endif #ifndef GL_COLOR #define GL_COLOR 0x1800 #endif #ifndef GL_DEPTH #define GL_DEPTH 0x1801 #endif #ifndef GL_STENCIL #define GL_STENCIL 0x1802 #endif static void _cogl_framebuffer_gl_flush_viewport_state (CoglFramebuffer *framebuffer) { float gl_viewport_y; g_return_if_fail (framebuffer->viewport_width >= 0); g_return_if_fail (framebuffer->viewport_height >= 0); /* Convert the Cogl viewport y offset to an OpenGL viewport y offset * NB: OpenGL defines its window and viewport origins to be bottom * left, while Cogl defines them to be top left. * NB: We render upside down to offscreen framebuffers so we don't * need to convert the y offset in this case. */ if (cogl_is_offscreen (framebuffer)) gl_viewport_y = framebuffer->viewport_y; else gl_viewport_y = framebuffer->height - (framebuffer->viewport_y + framebuffer->viewport_height); COGL_NOTE (OPENGL, "Calling glViewport(%f, %f, %f, %f)", framebuffer->viewport_x, gl_viewport_y, framebuffer->viewport_width, framebuffer->viewport_height); GE (framebuffer->context, glViewport (framebuffer->viewport_x, gl_viewport_y, framebuffer->viewport_width, framebuffer->viewport_height)); } static void _cogl_framebuffer_gl_flush_clip_state (CoglFramebuffer *framebuffer) { _cogl_clip_stack_flush (framebuffer->clip_stack, framebuffer); } static void _cogl_framebuffer_gl_flush_dither_state (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; if (ctx->current_gl_dither_enabled != framebuffer->dither_enabled) { if (framebuffer->dither_enabled) GE (ctx, glEnable (GL_DITHER)); else GE (ctx, glDisable (GL_DITHER)); ctx->current_gl_dither_enabled = framebuffer->dither_enabled; } } static void _cogl_framebuffer_gl_flush_modelview_state (CoglFramebuffer *framebuffer) { CoglMatrixEntry *modelview_entry = _cogl_framebuffer_get_modelview_entry (framebuffer); _cogl_context_set_current_modelview_entry (framebuffer->context, modelview_entry); } static void _cogl_framebuffer_gl_flush_projection_state (CoglFramebuffer *framebuffer) { CoglMatrixEntry *projection_entry = _cogl_framebuffer_get_projection_entry (framebuffer); _cogl_context_set_current_projection_entry (framebuffer->context, projection_entry); } static void _cogl_framebuffer_gl_flush_front_face_winding_state (CoglFramebuffer *framebuffer) { CoglContext *context = framebuffer->context; CoglPipelineCullFaceMode mode; /* NB: The face winding state is actually owned by the current * CoglPipeline. * * If we don't have a current pipeline then we can just assume that * when we later do flush a pipeline we will check the current * framebuffer to know how to setup the winding */ if (!context->current_pipeline) return; mode = cogl_pipeline_get_cull_face_mode (context->current_pipeline); /* If the current CoglPipeline has a culling mode that doesn't care * about the winding we can avoid forcing an update of the state and * bail out. */ if (mode == COGL_PIPELINE_CULL_FACE_MODE_NONE || mode == COGL_PIPELINE_CULL_FACE_MODE_BOTH) return; /* Since the winding state is really owned by the current pipeline * the way we "flush" an updated winding is to dirty the pipeline * state... */ context->current_pipeline_changes_since_flush |= COGL_PIPELINE_STATE_CULL_FACE; context->current_pipeline_age--; } static void _cogl_framebuffer_gl_flush_stereo_mode_state (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; GLenum draw_buffer = GL_BACK; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN) return; if (!ctx->glDrawBuffer) return; /* The one-shot default draw buffer setting in _cogl_framebuffer_gl_bind * must have already happened. If not it would override what we set here. */ g_assert (ctx->was_bound_to_onscreen); switch (framebuffer->stereo_mode) { case COGL_STEREO_BOTH: draw_buffer = GL_BACK; break; case COGL_STEREO_LEFT: draw_buffer = GL_BACK_LEFT; break; case COGL_STEREO_RIGHT: draw_buffer = GL_BACK_RIGHT; break; } if (ctx->current_gl_draw_buffer != draw_buffer) { GE (ctx, glDrawBuffer (draw_buffer)); ctx->current_gl_draw_buffer = draw_buffer; } } void _cogl_framebuffer_gl_bind (CoglFramebuffer *framebuffer, GLenum target) { CoglContext *ctx = framebuffer->context; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN) { CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer); GE (ctx, glBindFramebuffer (target, offscreen->gl_framebuffer.fbo_handle)); } else { const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); winsys->onscreen_bind (COGL_ONSCREEN (framebuffer)); GE (ctx, glBindFramebuffer (target, 0)); /* Initialise the glDrawBuffer state the first time the context * is bound to the default framebuffer. If the winsys is using a * surfaceless context for the initial make current then the * default draw buffer will be GL_NONE so we need to correct * that. We can't do it any earlier because binding GL_BACK when * there is no default framebuffer won't work */ if (!ctx->was_bound_to_onscreen) { if (ctx->glDrawBuffer) { GE (ctx, glDrawBuffer (GL_BACK)); } else if (ctx->glDrawBuffers) { /* glDrawBuffer isn't available on GLES 3.0 so we need * to be able to use glDrawBuffers as well. On GLES 2 * neither is available but the state should always be * GL_BACK anyway so we don't need to set anything. On * desktop GL this must be GL_BACK_LEFT instead of * GL_BACK but as this code path will only be hit for * GLES we can just use GL_BACK. */ static const GLenum buffers[] = { GL_BACK }; GE (ctx, glDrawBuffers (G_N_ELEMENTS (buffers), buffers)); } ctx->was_bound_to_onscreen = TRUE; } } } void _cogl_framebuffer_gl_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state) { CoglContext *ctx = draw_buffer->context; unsigned long differences; int bit; /* We can assume that any state that has changed for the current * framebuffer is different to the currently flushed value. */ differences = ctx->current_draw_buffer_changes; /* Any state of the current framebuffer that hasn't already been * flushed is assumed to be unknown so we will always flush that * state if asked. */ differences |= ~ctx->current_draw_buffer_state_flushed; /* We only need to consider the state we've been asked to flush */ differences &= state; if (ctx->current_draw_buffer != draw_buffer) { /* If the previous draw buffer is NULL then we'll assume everything has changed. This can happen if a framebuffer is destroyed while it is the last flushed draw buffer. In that case the framebuffer destructor will set ctx->current_draw_buffer to NULL */ if (ctx->current_draw_buffer == NULL) differences |= state; else /* NB: we only need to compare the state we're being asked to flush * and we don't need to compare the state we've already decided * we will definitely flush... */ differences |= _cogl_framebuffer_compare (ctx->current_draw_buffer, draw_buffer, state & ~differences); /* NB: we don't take a reference here, to avoid a circular * reference. */ ctx->current_draw_buffer = draw_buffer; ctx->current_draw_buffer_state_flushed = 0; } if (ctx->current_read_buffer != read_buffer && state & COGL_FRAMEBUFFER_STATE_BIND) { differences |= COGL_FRAMEBUFFER_STATE_BIND; /* NB: we don't take a reference here, to avoid a circular * reference. */ ctx->current_read_buffer = read_buffer; } if (!differences) return; /* Lazily ensure the framebuffers have been allocated */ if (G_UNLIKELY (!draw_buffer->allocated)) cogl_framebuffer_allocate (draw_buffer, NULL); if (G_UNLIKELY (!read_buffer->allocated)) cogl_framebuffer_allocate (read_buffer, NULL); /* We handle buffer binding separately since the method depends on whether * we are binding the same buffer for read and write or not unlike all * other state that only relates to the draw_buffer. */ if (differences & COGL_FRAMEBUFFER_STATE_BIND) { if (draw_buffer == read_buffer) _cogl_framebuffer_gl_bind (draw_buffer, GL_FRAMEBUFFER); else { /* NB: Currently we only take advantage of binding separate * read/write buffers for framebuffer blit purposes. */ g_return_if_fail (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)); _cogl_framebuffer_gl_bind (draw_buffer, GL_DRAW_FRAMEBUFFER); _cogl_framebuffer_gl_bind (read_buffer, GL_READ_FRAMEBUFFER); } differences &= ~COGL_FRAMEBUFFER_STATE_BIND; } COGL_FLAGS_FOREACH_START (&differences, 1, bit) { /* XXX: We considered having an array of callbacks for each state index * that we'd call here but decided that this way the compiler is more * likely going to be able to in-line the flush functions and use the * index to jump straight to the required code. */ switch (bit) { case COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT: _cogl_framebuffer_gl_flush_viewport_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_CLIP: _cogl_framebuffer_gl_flush_clip_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_DITHER: _cogl_framebuffer_gl_flush_dither_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW: _cogl_framebuffer_gl_flush_modelview_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION: _cogl_framebuffer_gl_flush_projection_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING: _cogl_framebuffer_gl_flush_front_face_winding_state (draw_buffer); break; case COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE: /* Nothing to do for depth write state change; the state will always * be taken into account when flushing the pipeline's depth state. */ break; case COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE: _cogl_framebuffer_gl_flush_stereo_mode_state (draw_buffer); break; default: g_warn_if_reached (); } } COGL_FLAGS_FOREACH_END; ctx->current_draw_buffer_state_flushed |= state; ctx->current_draw_buffer_changes &= ~state; } static CoglTexture * attach_depth_texture (CoglContext *ctx, CoglTexture *depth_texture, CoglOffscreenAllocateFlags flags) { GLuint tex_gl_handle; GLenum tex_gl_target; if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) { /* attach a GL_DEPTH_STENCIL texture to the GL_DEPTH_ATTACHMENT and * GL_STENCIL_ATTACHMENT attachement points */ g_assert (_cogl_texture_get_format (depth_texture) == COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8); cogl_texture_get_gl_texture (depth_texture, &tex_gl_handle, &tex_gl_target); GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, tex_gl_target, tex_gl_handle, 0)); GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, tex_gl_target, tex_gl_handle, 0)); } else if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) { /* attach a newly created GL_DEPTH_COMPONENT16 texture to the * GL_DEPTH_ATTACHMENT attachement point */ g_assert (_cogl_texture_get_format (depth_texture) == COGL_PIXEL_FORMAT_DEPTH_16); cogl_texture_get_gl_texture (COGL_TEXTURE (depth_texture), &tex_gl_handle, &tex_gl_target); GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, tex_gl_target, tex_gl_handle, 0)); } return COGL_TEXTURE (depth_texture); } static GList * try_creating_renderbuffers (CoglContext *ctx, int width, int height, CoglOffscreenAllocateFlags flags, int n_samples) { GList *renderbuffers = NULL; GLuint gl_depth_stencil_handle; if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL) { GLenum format; /* WebGL adds a GL_DEPTH_STENCIL_ATTACHMENT and requires that we * use the GL_DEPTH_STENCIL format. */ /* Although GL_OES_packed_depth_stencil is mostly equivalent to * GL_EXT_packed_depth_stencil, one notable difference is that * GL_OES_packed_depth_stencil doesn't allow GL_DEPTH_STENCIL to * be passed as an internal format to glRenderbufferStorage. */ if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL)) format = GL_DEPTH_STENCIL; else { g_return_val_if_fail ( _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL), NULL); format = GL_DEPTH24_STENCIL8; } /* Create a renderbuffer for depth and stenciling */ GE (ctx, glGenRenderbuffers (1, &gl_depth_stencil_handle)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_stencil_handle)); if (n_samples) GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, n_samples, format, width, height)); else GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, format, width, height)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_depth_stencil_handle)); GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_depth_stencil_handle)); renderbuffers = g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_stencil_handle)); } if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH) { GLuint gl_depth_handle; GE (ctx, glGenRenderbuffers (1, &gl_depth_handle)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_depth_handle)); /* For now we just ask for GL_DEPTH_COMPONENT16 since this is all that's * available under GLES */ if (n_samples) GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, n_samples, GL_DEPTH_COMPONENT16, width, height)); else GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl_depth_handle)); renderbuffers = g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_depth_handle)); } if (flags & COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL) { GLuint gl_stencil_handle; GE (ctx, glGenRenderbuffers (1, &gl_stencil_handle)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, gl_stencil_handle)); if (n_samples) GE (ctx, glRenderbufferStorageMultisampleIMG (GL_RENDERBUFFER, n_samples, GL_STENCIL_INDEX8, width, height)); else GE (ctx, glRenderbufferStorage (GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height)); GE (ctx, glBindRenderbuffer (GL_RENDERBUFFER, 0)); GE (ctx, glFramebufferRenderbuffer (GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl_stencil_handle)); renderbuffers = g_list_prepend (renderbuffers, GUINT_TO_POINTER (gl_stencil_handle)); } return renderbuffers; } static void delete_renderbuffers (CoglContext *ctx, GList *renderbuffers) { GList *l; for (l = renderbuffers; l; l = l->next) { GLuint renderbuffer = GPOINTER_TO_UINT (l->data); GE (ctx, glDeleteRenderbuffers (1, &renderbuffer)); } g_list_free (renderbuffers); } /* * NB: This function may be called with a standalone GLES2 context * bound so we can create a shadow framebuffer that wraps the same * CoglTexture as the given CoglOffscreen. This function shouldn't * modify anything in */ static gboolean try_creating_fbo (CoglContext *ctx, CoglTexture *texture, int texture_level, int texture_level_width, int texture_level_height, CoglTexture *depth_texture, CoglFramebufferConfig *config, CoglOffscreenAllocateFlags flags, CoglGLFramebuffer *gl_framebuffer) { GLuint tex_gl_handle; GLenum tex_gl_target; GLenum status; int n_samples; if (!cogl_texture_get_gl_texture (texture, &tex_gl_handle, &tex_gl_target)) return FALSE; if (tex_gl_target != GL_TEXTURE_2D #ifdef HAVE_COGL_GL && tex_gl_target != GL_TEXTURE_RECTANGLE_ARB #endif ) return FALSE; if (config->samples_per_pixel) { if (!ctx->glFramebufferTexture2DMultisampleIMG) return FALSE; n_samples = config->samples_per_pixel; } else n_samples = 0; /* We are about to generate and bind a new fbo, so we pretend to * change framebuffer state so that the old framebuffer will be * rebound again before drawing. */ ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_BIND; /* Generate framebuffer */ ctx->glGenFramebuffers (1, &gl_framebuffer->fbo_handle); GE (ctx, glBindFramebuffer (GL_FRAMEBUFFER, gl_framebuffer->fbo_handle)); if (n_samples) { GE (ctx, glFramebufferTexture2DMultisampleIMG (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_gl_target, tex_gl_handle, n_samples, texture_level)); } else GE (ctx, glFramebufferTexture2D (GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, tex_gl_target, tex_gl_handle, texture_level)); /* attach either a depth/stencil texture, a depth texture or render buffers * depending on what we've been asked to provide */ if (depth_texture && flags & (COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL | COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH)) { attach_depth_texture (ctx, depth_texture, flags); /* Let's clear the flags that are now fulfilled as we might need to * create renderbuffers (for the ALLOCATE_FLAG_DEPTH | * ALLOCATE_FLAG_STENCIL case) */ flags &= ~(COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL | COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH); } if (flags) { gl_framebuffer->renderbuffers = try_creating_renderbuffers (ctx, texture_level_width, texture_level_height, flags, n_samples); } /* Make sure it's complete */ status = ctx->glCheckFramebufferStatus (GL_FRAMEBUFFER); if (status != GL_FRAMEBUFFER_COMPLETE) { GE (ctx, glDeleteFramebuffers (1, &gl_framebuffer->fbo_handle)); delete_renderbuffers (ctx, gl_framebuffer->renderbuffers); gl_framebuffer->renderbuffers = NULL; return FALSE; } /* Update the real number of samples_per_pixel now that we have a * complete framebuffer */ if (n_samples) { GLenum attachment = GL_COLOR_ATTACHMENT0; GLenum pname = GL_TEXTURE_SAMPLES_IMG; int texture_samples; GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, attachment, pname, &texture_samples) ); gl_framebuffer->samples_per_pixel = texture_samples; } return TRUE; } gboolean _cogl_offscreen_gl_allocate (CoglOffscreen *offscreen, GError **error) { CoglFramebuffer *fb = COGL_FRAMEBUFFER (offscreen); CoglContext *ctx = fb->context; CoglOffscreenAllocateFlags flags; CoglGLFramebuffer *gl_framebuffer = &offscreen->gl_framebuffer; int level_width; int level_height; g_return_val_if_fail (offscreen->texture_level < _cogl_texture_get_n_levels (offscreen->texture), FALSE); _cogl_texture_get_level_size (offscreen->texture, offscreen->texture_level, &level_width, &level_height, NULL); /* XXX: The framebuffer_object spec isn't clear in defining whether attaching * a texture as a renderbuffer with mipmap filtering enabled while the * mipmaps have not been uploaded should result in an incomplete framebuffer * object. (different drivers make different decisions) * * To avoid an error with drivers that do consider this a problem we * explicitly set non mipmapped filters here. These will later be reset when * the texture is actually used for rendering according to the filters set on * the corresponding CoglPipeline. */ _cogl_texture_gl_flush_legacy_texobj_filters (offscreen->texture, GL_NEAREST, GL_NEAREST); if (((offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL) && try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = 0, gl_framebuffer)) || (ctx->have_last_offscreen_allocate_flags && try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = ctx->last_offscreen_allocate_flags, gl_framebuffer)) || ( /* NB: WebGL introduces a DEPTH_STENCIL_ATTACHMENT and doesn't * need an extension to handle _FLAG_DEPTH_STENCIL */ (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_EXT_PACKED_DEPTH_STENCIL) || _cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL)) && try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH_STENCIL, gl_framebuffer)) || try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH | COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, gl_framebuffer) || try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = COGL_OFFSCREEN_ALLOCATE_FLAG_STENCIL, gl_framebuffer) || try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = COGL_OFFSCREEN_ALLOCATE_FLAG_DEPTH, gl_framebuffer) || try_creating_fbo (ctx, offscreen->texture, offscreen->texture_level, level_width, level_height, offscreen->depth_texture, &fb->config, flags = 0, gl_framebuffer)) { fb->samples_per_pixel = gl_framebuffer->samples_per_pixel; if (!(offscreen->create_flags & COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL)) { /* Record that the last set of flags succeeded so that we can try that set first next time */ ctx->last_offscreen_allocate_flags = flags; ctx->have_last_offscreen_allocate_flags = TRUE; } /* Save the flags we managed to successfully allocate the * renderbuffers with in case we need to make renderbuffers for a * GLES2 context later */ offscreen->allocation_flags = flags; return TRUE; } else { g_set_error (error, COGL_FRAMEBUFFER_ERROR, COGL_FRAMEBUFFER_ERROR_ALLOCATE, "Failed to create an OpenGL framebuffer object"); return FALSE; } } void _cogl_offscreen_gl_free (CoglOffscreen *offscreen) { CoglContext *ctx = COGL_FRAMEBUFFER (offscreen)->context; delete_renderbuffers (ctx, offscreen->gl_framebuffer.renderbuffers); GE (ctx, glDeleteFramebuffers (1, &offscreen->gl_framebuffer.fbo_handle)); } void _cogl_framebuffer_gl_clear (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha) { CoglContext *ctx = framebuffer->context; GLbitfield gl_buffers = 0; if (buffers & COGL_BUFFER_BIT_COLOR) { GE( ctx, glClearColor (red, green, blue, alpha) ); gl_buffers |= GL_COLOR_BUFFER_BIT; } if (buffers & COGL_BUFFER_BIT_DEPTH) { gl_buffers |= GL_DEPTH_BUFFER_BIT; if (ctx->depth_writing_enabled_cache != framebuffer->depth_writing_enabled) { GE( ctx, glDepthMask (framebuffer->depth_writing_enabled)); ctx->depth_writing_enabled_cache = framebuffer->depth_writing_enabled; /* Make sure the DepthMask is updated when the next primitive is drawn */ ctx->current_pipeline_changes_since_flush |= COGL_PIPELINE_STATE_DEPTH; ctx->current_pipeline_age--; } } if (buffers & COGL_BUFFER_BIT_STENCIL) gl_buffers |= GL_STENCIL_BUFFER_BIT; GE (ctx, glClear (gl_buffers)); } static inline void _cogl_framebuffer_init_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; if (G_LIKELY (!framebuffer->dirty_bitmasks)) return; cogl_framebuffer_allocate (framebuffer, NULL); _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_BIND); #ifdef HAVE_COGL_GL if ((ctx->driver == COGL_DRIVER_GL3 && framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) || (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_QUERY_FRAMEBUFFER_BITS) && framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN)) { gboolean is_offscreen = framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN; const struct { GLenum attachment, pname; size_t offset; } params[] = { { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, offsetof (CoglFramebufferBits, red) }, { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE, offsetof (CoglFramebufferBits, green) }, { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE, offsetof (CoglFramebufferBits, blue) }, { is_offscreen ? GL_COLOR_ATTACHMENT0 : GL_BACK_LEFT, GL_FRAMEBUFFER_ATTACHMENT_ALPHA_SIZE, offsetof (CoglFramebufferBits, alpha) }, { is_offscreen ? GL_DEPTH_ATTACHMENT : GL_DEPTH, GL_FRAMEBUFFER_ATTACHMENT_DEPTH_SIZE, offsetof (CoglFramebufferBits, depth) }, { is_offscreen ? GL_STENCIL_ATTACHMENT : GL_STENCIL, GL_FRAMEBUFFER_ATTACHMENT_STENCIL_SIZE, offsetof (CoglFramebufferBits, stencil) }, }; int i; for (i = 0; i < G_N_ELEMENTS (params); i++) { int *value = (int *) ((uint8_t *) &framebuffer->bits + params[i].offset); GE( ctx, glGetFramebufferAttachmentParameteriv (GL_FRAMEBUFFER, params[i].attachment, params[i].pname, value) ); } } else #endif /* HAVE_COGL_GL */ { GE( ctx, glGetIntegerv (GL_RED_BITS, &framebuffer->bits.red) ); GE( ctx, glGetIntegerv (GL_GREEN_BITS, &framebuffer->bits.green) ); GE( ctx, glGetIntegerv (GL_BLUE_BITS, &framebuffer->bits.blue) ); GE( ctx, glGetIntegerv (GL_ALPHA_BITS, &framebuffer->bits.alpha) ); GE( ctx, glGetIntegerv (GL_DEPTH_BITS, &framebuffer->bits.depth) ); GE( ctx, glGetIntegerv (GL_STENCIL_BITS, &framebuffer->bits.stencil) ); } /* If we don't have alpha textures then the alpha bits are actually * stored in the red component */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES) && framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN && framebuffer->internal_format == COGL_PIXEL_FORMAT_A_8) { framebuffer->bits.alpha = framebuffer->bits.red; framebuffer->bits.red = 0; } COGL_NOTE (OFFSCREEN, "RGBA/D/S Bits for framebuffer[%p, %s]: %d, %d, %d, %d, %d, %d", framebuffer, framebuffer->type == COGL_FRAMEBUFFER_TYPE_OFFSCREEN ? "offscreen" : "onscreen", framebuffer->bits.red, framebuffer->bits.blue, framebuffer->bits.green, framebuffer->bits.alpha, framebuffer->bits.depth, framebuffer->bits.stencil); framebuffer->dirty_bitmasks = FALSE; } void _cogl_framebuffer_gl_query_bits (CoglFramebuffer *framebuffer, CoglFramebufferBits *bits) { _cogl_framebuffer_init_bits (framebuffer); /* TODO: cache these in some driver specific location not * directly as part of CoglFramebuffer. */ *bits = framebuffer->bits; } void _cogl_framebuffer_gl_finish (CoglFramebuffer *framebuffer) { GE (framebuffer->context, glFinish ()); } void _cogl_framebuffer_gl_flush (CoglFramebuffer *framebuffer) { GE (framebuffer->context, glFlush ()); } void _cogl_framebuffer_gl_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers) { CoglContext *ctx = framebuffer->context; if (ctx->glDiscardFramebuffer) { GLenum attachments[3]; int i = 0; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { if (buffers & COGL_BUFFER_BIT_COLOR) attachments[i++] = GL_COLOR; if (buffers & COGL_BUFFER_BIT_DEPTH) attachments[i++] = GL_DEPTH; if (buffers & COGL_BUFFER_BIT_STENCIL) attachments[i++] = GL_STENCIL; } else { if (buffers & COGL_BUFFER_BIT_COLOR) attachments[i++] = GL_COLOR_ATTACHMENT0; if (buffers & COGL_BUFFER_BIT_DEPTH) attachments[i++] = GL_DEPTH_ATTACHMENT; if (buffers & COGL_BUFFER_BIT_STENCIL) attachments[i++] = GL_STENCIL_ATTACHMENT; } _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_BIND); GE (ctx, glDiscardFramebuffer (GL_FRAMEBUFFER, i, attachments)); } } void _cogl_framebuffer_gl_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { _cogl_flush_attributes_state (framebuffer, pipeline, flags, attributes, n_attributes); GE (framebuffer->context, glDrawArrays ((GLenum)mode, first_vertex, n_vertices)); } static size_t sizeof_index_type (CoglIndicesType type) { switch (type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: return 1; case COGL_INDICES_TYPE_UNSIGNED_SHORT: return 2; case COGL_INDICES_TYPE_UNSIGNED_INT: return 4; } g_return_val_if_reached (0); } void _cogl_framebuffer_gl_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { CoglBuffer *buffer; uint8_t *base; size_t buffer_offset; size_t index_size; GLenum indices_gl_type = 0; _cogl_flush_attributes_state (framebuffer, pipeline, flags, attributes, n_attributes); buffer = COGL_BUFFER (cogl_indices_get_buffer (indices)); /* Note: we don't try and catch errors with binding the index buffer * here since OOM errors at this point indicate that nothing has yet * been uploaded to the indices buffer which we consider to be a * programmer error. */ base = _cogl_buffer_gl_bind (buffer, COGL_BUFFER_BIND_TARGET_INDEX_BUFFER, NULL); buffer_offset = cogl_indices_get_offset (indices); index_size = sizeof_index_type (cogl_indices_get_type (indices)); switch (cogl_indices_get_type (indices)) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: indices_gl_type = GL_UNSIGNED_BYTE; break; case COGL_INDICES_TYPE_UNSIGNED_SHORT: indices_gl_type = GL_UNSIGNED_SHORT; break; case COGL_INDICES_TYPE_UNSIGNED_INT: indices_gl_type = GL_UNSIGNED_INT; break; } GE (framebuffer->context, glDrawElements ((GLenum)mode, n_vertices, indices_gl_type, base + buffer_offset + index_size * first_vertex)); _cogl_buffer_gl_unbind (buffer); } gboolean _cogl_framebuffer_gl_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error) { CoglContext *ctx = framebuffer->context; int framebuffer_height = cogl_framebuffer_get_height (framebuffer); int width = cogl_bitmap_get_width (bitmap); int height = cogl_bitmap_get_height (bitmap); CoglPixelFormat format = cogl_bitmap_get_format (bitmap); CoglPixelFormat required_format; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; GLenum gl_pack_enum = GL_FALSE; gboolean pack_invert_set; int status = FALSE; g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_BIND); /* The y co-ordinate should be given in OpenGL's coordinate system * so 0 is the bottom row * * NB: all offscreen rendering is done upside down so no conversion * is necissary in this case. */ if (!cogl_is_offscreen (framebuffer)) y = framebuffer_height - y - height; required_format = ctx->driver_vtable->pixel_format_to_gl (ctx, format, &gl_intformat, &gl_format, &gl_type); /* NB: All offscreen rendering is done upside down so there is no need * to flip in this case... */ if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_MESA_PACK_INVERT) && (source & COGL_READ_PIXELS_NO_FLIP) == 0 && !cogl_is_offscreen (framebuffer)) { if (ctx->driver == COGL_DRIVER_GLES2) gl_pack_enum = GL_PACK_REVERSE_ROW_ORDER_ANGLE; else gl_pack_enum = GL_PACK_INVERT_MESA; GE (ctx, glPixelStorei (gl_pack_enum, TRUE)); pack_invert_set = TRUE; } else pack_invert_set = FALSE; /* Under GLES only GL_RGBA with GL_UNSIGNED_BYTE as well as an implementation specific format under GL_IMPLEMENTATION_COLOR_READ_FORMAT_OES and GL_IMPLEMENTATION_COLOR_READ_TYPE_OES is supported. We could try to be more clever and check if the requested type matches that but we would need some reliable functions to convert from GL types to Cogl types. For now, lets just always read in GL_RGBA/GL_UNSIGNED_BYTE and convert if necessary. We also need to use this intermediate buffer if the rowstride has padding because GLES does not support setting GL_ROW_LENGTH */ if ((!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT) && (gl_format != GL_RGBA || gl_type != GL_UNSIGNED_BYTE || cogl_bitmap_get_rowstride (bitmap) != 4 * width)) || (required_format & ~COGL_PREMULT_BIT) != (format & ~COGL_PREMULT_BIT)) { CoglBitmap *tmp_bmp; CoglPixelFormat read_format; int bpp, rowstride; uint8_t *tmp_data; gboolean succeeded; if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_READ_PIXELS_ANY_FORMAT)) read_format = required_format; else { read_format = COGL_PIXEL_FORMAT_RGBA_8888; gl_format = GL_RGBA; gl_type = GL_UNSIGNED_BYTE; } if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (read_format)) read_format = ((read_format & ~COGL_PREMULT_BIT) | (framebuffer->internal_format & COGL_PREMULT_BIT)); tmp_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, read_format, error); if (!tmp_bmp) goto EXIT; bpp = cogl_pixel_format_get_bytes_per_pixel (read_format, 0); rowstride = cogl_bitmap_get_rowstride (tmp_bmp); ctx->texture_driver->prep_gl_for_pixels_download (ctx, rowstride, width, bpp); /* Note: we don't worry about catching errors here since we know * we won't be lazily allocating storage for this buffer so it * won't fail due to lack of memory. */ tmp_data = _cogl_bitmap_gl_bind (tmp_bmp, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, NULL); GE( ctx, glReadPixels (x, y, width, height, gl_format, gl_type, tmp_data) ); _cogl_bitmap_gl_unbind (tmp_bmp); succeeded = _cogl_bitmap_convert_into_bitmap (tmp_bmp, bitmap, error); cogl_object_unref (tmp_bmp); if (!succeeded) goto EXIT; } else { CoglBitmap *shared_bmp; CoglPixelFormat bmp_format; int bpp, rowstride; gboolean succeeded = FALSE; uint8_t *pixels; GError *internal_error = NULL; rowstride = cogl_bitmap_get_rowstride (bitmap); /* We match the premultiplied state of the target buffer to the * premultiplied state of the framebuffer so that it will get * converted to the right format below */ if (COGL_PIXEL_FORMAT_CAN_HAVE_PREMULT (format)) bmp_format = ((format & ~COGL_PREMULT_BIT) | (framebuffer->internal_format & COGL_PREMULT_BIT)); else bmp_format = format; if (bmp_format != format) shared_bmp = _cogl_bitmap_new_shared (bitmap, bmp_format, width, height, rowstride); else shared_bmp = cogl_object_ref (bitmap); bpp = cogl_pixel_format_get_bytes_per_pixel (bmp_format, 0); ctx->texture_driver->prep_gl_for_pixels_download (ctx, rowstride, width, bpp); pixels = _cogl_bitmap_gl_bind (shared_bmp, COGL_BUFFER_ACCESS_WRITE, 0, /* hints */ &internal_error); /* NB: _cogl_bitmap_gl_bind() can return NULL in sucessfull * cases so we have to explicitly check the cogl error pointer * to know if there was a problem */ if (internal_error) { cogl_object_unref (shared_bmp); g_propagate_error (error, internal_error); goto EXIT; } GE( ctx, glReadPixels (x, y, width, height, gl_format, gl_type, pixels) ); _cogl_bitmap_gl_unbind (shared_bmp); /* Convert to the premult format specified by the caller in-place. This will do nothing if the premult status is already correct. */ if (_cogl_bitmap_convert_premult_status (shared_bmp, format, error)) succeeded = TRUE; cogl_object_unref (shared_bmp); if (!succeeded) goto EXIT; } /* NB: All offscreen rendering is done upside down so there is no need * to flip in this case... */ if (!cogl_is_offscreen (framebuffer) && (source & COGL_READ_PIXELS_NO_FLIP) == 0 && !pack_invert_set) { uint8_t *temprow; int rowstride; uint8_t *pixels; rowstride = cogl_bitmap_get_rowstride (bitmap); pixels = _cogl_bitmap_map (bitmap, COGL_BUFFER_ACCESS_READ | COGL_BUFFER_ACCESS_WRITE, 0, /* hints */ error); if (pixels == NULL) goto EXIT; temprow = g_alloca (rowstride * sizeof (uint8_t)); /* vertically flip the buffer in-place */ for (y = 0; y < height / 2; y++) { if (y != height - y - 1) /* skip center row */ { memcpy (temprow, pixels + y * rowstride, rowstride); memcpy (pixels + y * rowstride, pixels + (height - y - 1) * rowstride, rowstride); memcpy (pixels + (height - y - 1) * rowstride, temprow, rowstride); } } _cogl_bitmap_unmap (bitmap); } status = TRUE; EXIT: /* Currently this function owns the pack_invert state and we don't want this * to interfere with other Cogl components so all other code can assume that * we leave the pack_invert state off. */ if (pack_invert_set) GE (ctx, glPixelStorei (gl_pack_enum, FALSE)); return status; } muffin-6.4.1/cogl/cogl/driver/gl/cogl-util-gl.c0000664000175000017500000001166014723361714020156 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-types.h" #include "cogl-context-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-util-gl-private.h" #ifdef COGL_GL_DEBUG /* GL error to string conversion */ static const struct { GLuint error_code; const char *error_string; } gl_errors[] = { { GL_NO_ERROR, "No error" }, { GL_INVALID_ENUM, "Invalid enumeration value" }, { GL_INVALID_VALUE, "Invalid value" }, { GL_INVALID_OPERATION, "Invalid operation" }, #ifdef HAVE_COGL_GL { GL_STACK_OVERFLOW, "Stack overflow" }, { GL_STACK_UNDERFLOW, "Stack underflow" }, #endif { GL_OUT_OF_MEMORY, "Out of memory" }, #ifdef GL_INVALID_FRAMEBUFFER_OPERATION_EXT { GL_INVALID_FRAMEBUFFER_OPERATION_EXT, "Invalid framebuffer operation" } #endif }; static const unsigned int n_gl_errors = G_N_ELEMENTS (gl_errors); const char * _cogl_gl_error_to_string (GLenum error_code) { int i; for (i = 0; i < n_gl_errors; i++) { if (gl_errors[i].error_code == error_code) return gl_errors[i].error_string; } return "Unknown GL error"; } #endif /* COGL_GL_DEBUG */ gboolean _cogl_driver_gl_context_init (CoglContext *context) { context->texture_units = g_array_new (FALSE, FALSE, sizeof (CoglTextureUnit)); /* See cogl-pipeline.c for more details about why we leave texture unit 1 * active by default... */ context->active_texture_unit = 1; GE (context, glActiveTexture (GL_TEXTURE1)); return TRUE; } void _cogl_driver_gl_context_deinit (CoglContext *context) { _cogl_destroy_texture_units (context); } GLenum _cogl_gl_util_get_error (CoglContext *ctx) { GLenum gl_error = ctx->glGetError (); if (gl_error != GL_NO_ERROR && gl_error != GL_CONTEXT_LOST) return gl_error; else return GL_NO_ERROR; } void _cogl_gl_util_clear_gl_errors (CoglContext *ctx) { GLenum gl_error; while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR && gl_error != GL_CONTEXT_LOST) ; } gboolean _cogl_gl_util_catch_out_of_memory (CoglContext *ctx, GError **error) { GLenum gl_error; gboolean out_of_memory = FALSE; while ((gl_error = ctx->glGetError ()) != GL_NO_ERROR && gl_error != GL_CONTEXT_LOST) { if (gl_error == GL_OUT_OF_MEMORY) out_of_memory = TRUE; #ifdef COGL_GL_DEBUG else { g_warning ("%s: GL error (%d): %s\n", G_STRLOC, gl_error, _cogl_gl_error_to_string (gl_error)); } #endif } if (out_of_memory) { g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_NO_MEMORY, "Out of memory"); return TRUE; } return FALSE; } gboolean _cogl_gl_util_parse_gl_version (const char *version_string, int *major_out, int *minor_out) { const char *major_end, *minor_end; int major = 0, minor = 0; /* Extract the major number */ for (major_end = version_string; *major_end >= '0' && *major_end <= '9'; major_end++) major = (major * 10) + *major_end - '0'; /* If there were no digits or the major number isn't followed by a dot then it is invalid */ if (major_end == version_string || *major_end != '.') return FALSE; /* Extract the minor number */ for (minor_end = major_end + 1; *minor_end >= '0' && *minor_end <= '9'; minor_end++) minor = (minor * 10) + *minor_end - '0'; /* If there were no digits or there is an unexpected character then it is invalid */ if (minor_end == major_end + 1 || (*minor_end && *minor_end != ' ' && *minor_end != '.')) return FALSE; *major_out = major; *minor_out = minor; return TRUE; } muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-vertend-glsl.c0000664000175000017500000005405714723361714023021 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-pipeline-state-private.h" #include "cogl-glsl-shader-private.h" #include "driver/gl/cogl-pipeline-vertend-glsl-private.h" #include "deprecated/cogl-program-private.h" const CoglPipelineVertend _cogl_pipeline_glsl_vertend; typedef struct { unsigned int ref_count; GLuint gl_shader; GString *header, *source; CoglPipelineCacheEntry *cache_entry; } CoglPipelineShaderState; static CoglUserDataKey shader_state_key; static CoglPipelineShaderState * shader_state_new (CoglPipelineCacheEntry *cache_entry) { CoglPipelineShaderState *shader_state; shader_state = g_slice_new0 (CoglPipelineShaderState); shader_state->ref_count = 1; shader_state->cache_entry = cache_entry; return shader_state; } static CoglPipelineShaderState * get_shader_state (CoglPipeline *pipeline) { return cogl_object_get_user_data (COGL_OBJECT (pipeline), &shader_state_key); } static void destroy_shader_state (void *user_data, void *instance) { CoglPipelineShaderState *shader_state = user_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (shader_state->cache_entry && shader_state->cache_entry->pipeline != instance) shader_state->cache_entry->usage_count--; if (--shader_state->ref_count == 0) { if (shader_state->gl_shader) GE( ctx, glDeleteShader (shader_state->gl_shader) ); g_slice_free (CoglPipelineShaderState, shader_state); } } static void set_shader_state (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { if (shader_state) { shader_state->ref_count++; /* If we're not setting the state on the template pipeline then * mark it as a usage of the pipeline cache entry */ if (shader_state->cache_entry && shader_state->cache_entry->pipeline != pipeline) shader_state->cache_entry->usage_count++; } _cogl_object_set_user_data (COGL_OBJECT (pipeline), &shader_state_key, shader_state, destroy_shader_state); } static void dirty_shader_state (CoglPipeline *pipeline) { cogl_object_set_user_data (COGL_OBJECT (pipeline), &shader_state_key, NULL, NULL); } GLuint _cogl_pipeline_vertend_glsl_get_shader (CoglPipeline *pipeline) { CoglPipelineShaderState *shader_state = get_shader_state (pipeline); if (shader_state) return shader_state->gl_shader; else return 0; } static CoglPipelineSnippetList * get_vertex_snippets (CoglPipeline *pipeline) { pipeline = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_VERTEX_SNIPPETS); return &pipeline->big_state->vertex_snippets; } static CoglPipelineSnippetList * get_layer_vertex_snippets (CoglPipelineLayer *layer) { unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; layer = _cogl_pipeline_layer_get_authority (layer, state); return &layer->big_state->vertex_snippets; } static gboolean add_layer_declaration_cb (CoglPipelineLayer *layer, void *user_data) { CoglPipelineShaderState *shader_state = user_data; g_string_append_printf (shader_state->header, "uniform sampler2D cogl_sampler%i;\n", layer->index); return TRUE; } static void add_layer_declarations (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { /* We always emit sampler uniforms in case there will be custom * layer snippets that want to sample arbitrary layers. */ _cogl_pipeline_foreach_layer_internal (pipeline, add_layer_declaration_cb, shader_state); } static void add_global_declarations (CoglPipeline *pipeline, CoglPipelineShaderState *shader_state) { CoglSnippetHook hook = COGL_SNIPPET_HOOK_VERTEX_GLOBALS; CoglPipelineSnippetList *snippets = get_vertex_snippets (pipeline); /* Add the global data hooks. All of the code in these snippets is * always added and only the declarations data is used */ _cogl_pipeline_snippet_generate_declarations (shader_state->header, hook, snippets); } static void _cogl_pipeline_vertend_glsl_start (CoglPipeline *pipeline, int n_layers, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; CoglPipelineCacheEntry *cache_entry = NULL; CoglProgram *user_program = cogl_pipeline_get_user_program (pipeline); _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Now lookup our glsl backend private state (allocating if * necessary) */ shader_state = get_shader_state (pipeline); if (shader_state == NULL) { CoglPipeline *authority; /* Get the authority for anything affecting vertex shader state */ authority = _cogl_pipeline_find_equivalent_parent (pipeline, _cogl_pipeline_get_state_for_vertex_codegen (ctx) & ~COGL_PIPELINE_STATE_LAYERS, COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN); shader_state = get_shader_state (authority); if (shader_state == NULL) { /* Check if there is already a similar cached pipeline whose shader state we can share */ if (G_LIKELY (!(COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_PROGRAM_CACHES)))) { cache_entry = _cogl_pipeline_cache_get_vertex_template (ctx->pipeline_cache, authority); shader_state = get_shader_state (cache_entry->pipeline); } if (shader_state) shader_state->ref_count++; else shader_state = shader_state_new (cache_entry); set_shader_state (authority, shader_state); shader_state->ref_count--; if (cache_entry) set_shader_state (cache_entry->pipeline, shader_state); } if (authority != pipeline) set_shader_state (pipeline, shader_state); } if (user_program) { /* If the user program contains a vertex shader then we don't need to generate one */ if (_cogl_program_has_vertex_shader (user_program)) { if (shader_state->gl_shader) { GE( ctx, glDeleteShader (shader_state->gl_shader) ); shader_state->gl_shader = 0; } return; } } if (shader_state->gl_shader) return; /* If we make it here then we have a shader_state struct without a gl_shader either because this is the first time we've encountered it or because the user program has changed */ /* We reuse two grow-only GStrings for code-gen. One string contains the uniform and attribute declarations while the other contains the main function. We need two strings because we need to dynamically declare attributes as the add_layer callback is invoked */ g_string_set_size (ctx->codegen_header_buffer, 0); g_string_set_size (ctx->codegen_source_buffer, 0); shader_state->header = ctx->codegen_header_buffer; shader_state->source = ctx->codegen_source_buffer; add_layer_declarations (pipeline, shader_state); add_global_declarations (pipeline, shader_state); g_string_append (shader_state->source, "void\n" "cogl_generated_source ()\n" "{\n"); if (cogl_pipeline_get_per_vertex_point_size (pipeline)) g_string_append (shader_state->header, "attribute float cogl_point_size_in;\n"); else { /* There is no builtin uniform for the point size on GLES2 so we need to copy it from the custom uniform in the vertex shader if we're not using per-vertex point sizes, however we'll only do this if the point-size is non-zero. Toggle the point size between zero and non-zero causes a state change which generates a new program */ if (cogl_pipeline_get_point_size (pipeline) > 0.0f) { g_string_append (shader_state->header, "uniform float cogl_point_size_in;\n"); g_string_append (shader_state->source, " cogl_point_size_out = cogl_point_size_in;\n"); } } } static gboolean _cogl_pipeline_vertend_glsl_add_layer (CoglPipeline *pipeline, CoglPipelineLayer *layer, unsigned long layers_difference, CoglFramebuffer *framebuffer) { CoglPipelineShaderState *shader_state; CoglPipelineSnippetData snippet_data; int layer_index = layer->index; _COGL_GET_CONTEXT (ctx, FALSE); shader_state = get_shader_state (pipeline); if (shader_state->source == NULL) return TRUE; /* Transform the texture coordinates by the layer's user matrix. * * FIXME: this should avoid doing the transform if there is no user * matrix set. This might need a separate layer state flag for * whether there is a user matrix * * FIXME: we could be more clever here and try to detect if the * fragment program is going to use the texture coordinates and * avoid setting them if not */ g_string_append_printf (shader_state->header, "vec4\n" "cogl_real_transform_layer%i (mat4 matrix, " "vec4 tex_coord)\n" "{\n" " return matrix * tex_coord;\n" "}\n", layer_index); /* Wrap the layer code in any snippets that have been hooked */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = get_layer_vertex_snippets (layer); snippet_data.hook = COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM; snippet_data.chain_function = g_strdup_printf ("cogl_real_transform_layer%i", layer_index); snippet_data.final_name = g_strdup_printf ("cogl_transform_layer%i", layer_index); snippet_data.function_prefix = g_strdup_printf ("cogl_transform_layer%i", layer_index); snippet_data.return_type = "vec4"; snippet_data.return_variable = "cogl_tex_coord"; snippet_data.return_variable_is_argument = TRUE; snippet_data.arguments = "cogl_matrix, cogl_tex_coord"; snippet_data.argument_declarations = "mat4 cogl_matrix, vec4 cogl_tex_coord"; snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); g_free ((char *) snippet_data.chain_function); g_free ((char *) snippet_data.final_name); g_free ((char *) snippet_data.function_prefix); g_string_append_printf (shader_state->source, " cogl_tex_coord%i_out = " "cogl_transform_layer%i (cogl_texture_matrix%i,\n" " " " cogl_tex_coord%i_in);\n", layer_index, layer_index, layer_index, layer_index); return TRUE; } static gboolean _cogl_pipeline_vertend_glsl_end (CoglPipeline *pipeline, unsigned long pipelines_difference) { CoglPipelineShaderState *shader_state; _COGL_GET_CONTEXT (ctx, FALSE); shader_state = get_shader_state (pipeline); if (shader_state->source) { const char *source_strings[2]; GLint lengths[2]; GLint compile_status; GLuint shader; CoglPipelineSnippetData snippet_data; CoglPipelineSnippetList *vertex_snippets; gboolean has_per_vertex_point_size = cogl_pipeline_get_per_vertex_point_size (pipeline); COGL_STATIC_COUNTER (vertend_glsl_compile_counter, "glsl vertex compile counter", "Increments each time a new GLSL " "vertex shader is compiled", 0 /* no application private data */); COGL_COUNTER_INC (_cogl_uprof_context, vertend_glsl_compile_counter); g_string_append (shader_state->header, "void\n" "cogl_real_vertex_transform ()\n" "{\n" " cogl_position_out = " "cogl_modelview_projection_matrix * " "cogl_position_in;\n" "}\n"); g_string_append (shader_state->source, " cogl_vertex_transform ();\n"); if (has_per_vertex_point_size) { g_string_append (shader_state->header, "void\n" "cogl_real_point_size_calculation ()\n" "{\n" " cogl_point_size_out = cogl_point_size_in;\n" "}\n"); g_string_append (shader_state->source, " cogl_point_size_calculation ();\n"); } g_string_append (shader_state->source, " cogl_color_out = cogl_color_in;\n" "}\n"); vertex_snippets = get_vertex_snippets (pipeline); /* Add hooks for the vertex transform part */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = vertex_snippets; snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX_TRANSFORM; snippet_data.chain_function = "cogl_real_vertex_transform"; snippet_data.final_name = "cogl_vertex_transform"; snippet_data.function_prefix = "cogl_vertex_transform"; snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); /* Add hooks for the point size calculation part */ if (has_per_vertex_point_size) { memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = vertex_snippets; snippet_data.hook = COGL_SNIPPET_HOOK_POINT_SIZE; snippet_data.chain_function = "cogl_real_point_size_calculation"; snippet_data.final_name = "cogl_point_size_calculation"; snippet_data.function_prefix = "cogl_point_size_calculation"; snippet_data.source_buf = shader_state->header; _cogl_pipeline_snippet_generate_code (&snippet_data); } /* Add all of the hooks for vertex processing */ memset (&snippet_data, 0, sizeof (snippet_data)); snippet_data.snippets = vertex_snippets; snippet_data.hook = COGL_SNIPPET_HOOK_VERTEX; snippet_data.chain_function = "cogl_generated_source"; snippet_data.final_name = "cogl_vertex_hook"; snippet_data.function_prefix = "cogl_vertex_hook"; snippet_data.source_buf = shader_state->source; _cogl_pipeline_snippet_generate_code (&snippet_data); g_string_append (shader_state->source, "void\n" "main ()\n" "{\n" " cogl_vertex_hook ();\n"); /* If there are any snippets then we can't rely on the projection matrix to flip the rendering for offscreen buffers so we'll need to flip it using an extra statement and a uniform */ if (_cogl_pipeline_has_vertex_snippets (pipeline)) { g_string_append (shader_state->header, "uniform vec4 _cogl_flip_vector;\n"); g_string_append (shader_state->source, " cogl_position_out *= _cogl_flip_vector;\n"); } g_string_append (shader_state->source, "}\n"); GE_RET( shader, ctx, glCreateShader (GL_VERTEX_SHADER) ); lengths[0] = shader_state->header->len; source_strings[0] = shader_state->header->str; lengths[1] = shader_state->source->len; source_strings[1] = shader_state->source->str; _cogl_glsl_shader_set_source_with_boilerplate (ctx, shader, GL_VERTEX_SHADER, pipeline, 2, /* count */ source_strings, lengths); GE( ctx, glCompileShader (shader) ); GE( ctx, glGetShaderiv (shader, GL_COMPILE_STATUS, &compile_status) ); if (!compile_status) { GLint len = 0; char *shader_log; GE( ctx, glGetShaderiv (shader, GL_INFO_LOG_LENGTH, &len) ); shader_log = g_alloca (len); GE( ctx, glGetShaderInfoLog (shader, len, &len, shader_log) ); g_warning ("Shader compilation failed:\n%s", shader_log); } shader_state->header = NULL; shader_state->source = NULL; shader_state->gl_shader = shader; } return TRUE; } static void _cogl_pipeline_vertend_glsl_pre_change_notify (CoglPipeline *pipeline, CoglPipelineState change, const CoglColor *new_color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if ((change & _cogl_pipeline_get_state_for_vertex_codegen (ctx))) dirty_shader_state (pipeline); } /* NB: layers are considered immutable once they have any dependants * so although multiple pipelines can end up depending on a single * static layer, we can guarantee that if a layer is being *changed* * then it can only have one pipeline depending on it. * * XXX: Don't forget this is *pre* change, we can't read the new value * yet! */ static void _cogl_pipeline_vertend_glsl_layer_pre_change_notify ( CoglPipeline *owner, CoglPipelineLayer *layer, CoglPipelineLayerState change) { CoglPipelineShaderState *shader_state; shader_state = get_shader_state (owner); if (!shader_state) return; if ((change & COGL_PIPELINE_LAYER_STATE_AFFECTS_VERTEX_CODEGEN)) { dirty_shader_state (owner); return; } /* TODO: we could be saving snippets of texture combine code along * with each layer and then when a layer changes we would just free * the snippet. */ } const CoglPipelineVertend _cogl_pipeline_glsl_vertend = { _cogl_pipeline_vertend_glsl_start, _cogl_pipeline_vertend_glsl_add_layer, _cogl_pipeline_vertend_glsl_end, _cogl_pipeline_vertend_glsl_pre_change_notify, _cogl_pipeline_vertend_glsl_layer_pre_change_notify }; UNIT_TEST (check_point_size_shader, 0 /* no requirements */, 0 /* no failure cases */) { CoglPipeline *pipelines[4]; CoglPipelineShaderState *shader_states[G_N_ELEMENTS (pipelines)]; int i; /* Default pipeline with zero point size */ pipelines[0] = cogl_pipeline_new (test_ctx); /* Point size 1 */ pipelines[1] = cogl_pipeline_new (test_ctx); cogl_pipeline_set_point_size (pipelines[1], 1.0f); /* Point size 2 */ pipelines[2] = cogl_pipeline_new (test_ctx); cogl_pipeline_set_point_size (pipelines[2], 2.0f); /* Same as the first pipeline, but reached by restoring the old * state from a copy */ pipelines[3] = cogl_pipeline_copy (pipelines[1]); cogl_pipeline_set_point_size (pipelines[3], 0.0f); /* Draw something with all of the pipelines to make sure their state * is flushed */ for (i = 0; i < G_N_ELEMENTS (pipelines); i++) cogl_framebuffer_draw_rectangle (test_fb, pipelines[i], 0.0f, 0.0f, 10.0f, 10.0f); cogl_framebuffer_finish (test_fb); /* Get all of the shader states. These might be NULL if the driver * is not using GLSL */ for (i = 0; i < G_N_ELEMENTS (pipelines); i++) shader_states[i] = get_shader_state (pipelines[i]); /* If the first two pipelines are using GLSL then they should have * the same shader unless there is no builtin uniform for the point * size */ if (shader_states[0]) { g_assert (shader_states[0] != shader_states[1]); } /* The second and third pipelines should always have the same shader * state because only toggling between zero and non-zero should * change the shader */ g_assert (shader_states[1] == shader_states[2]); /* The fourth pipeline should be exactly the same as the first */ g_assert (shader_states[0] == shader_states[3]); } muffin-6.4.1/cogl/cogl/driver/gl/cogl-texture-2d-gl.c0000664000175000017500000005341214723361714021205 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2011,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-texture-private.h" #include "cogl-texture-2d-private.h" #include "driver/gl/cogl-texture-2d-gl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-util-gl-private.h" #if defined (COGL_HAS_EGL_SUPPORT) /* We need this define from GLES2, but can't include the header as its type definitions may conflict with the GL ones */ #ifndef GL_OES_EGL_image_external #define GL_OES_EGL_image_external 1 #define GL_TEXTURE_EXTERNAL_OES 0x8D65 #define GL_TEXTURE_BINDING_EXTERNAL_OES 0x8D67 #define GL_REQUIRED_TEXTURE_IMAGE_UNITS_OES 0x8D68 #define GL_SAMPLER_EXTERNAL_OES 0x8D66 #endif /* GL_OES_EGL_image_external */ #endif /* defined (COGL_HAS_EGL_SUPPORT) */ void _cogl_texture_2d_gl_free (CoglTexture2D *tex_2d) { if (tex_2d->gl_texture) _cogl_delete_gl_texture (tex_2d->gl_texture); #if defined (COGL_HAS_EGL_SUPPORT) g_clear_pointer (&tex_2d->egl_image_external.user_data, tex_2d->egl_image_external.destroy); #endif } gboolean _cogl_texture_2d_gl_can_create (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format) { GLenum gl_intformat; GLenum gl_format; GLenum gl_type; /* We only support single plane formats for now */ if (cogl_pixel_format_get_n_planes (internal_format) != 1) return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, &gl_format, &gl_type); /* Check that the driver can create a texture with that size */ if (!ctx->texture_driver->size_supported (ctx, GL_TEXTURE_2D, gl_intformat, gl_format, gl_type, width, height)) return FALSE; return TRUE; } void _cogl_texture_2d_gl_init (CoglTexture2D *tex_2d) { tex_2d->gl_texture = 0; /* We default to GL_LINEAR for both filters */ tex_2d->gl_legacy_texobj_min_filter = GL_LINEAR; tex_2d->gl_legacy_texobj_mag_filter = GL_LINEAR; /* Wrap mode not yet set */ tex_2d->gl_legacy_texobj_wrap_mode_s = GL_FALSE; tex_2d->gl_legacy_texobj_wrap_mode_t = GL_FALSE; tex_2d->egl_image_external.user_data = NULL; tex_2d->egl_image_external.destroy = NULL; } static gboolean allocate_with_size (CoglTexture2D *tex_2d, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglPixelFormat internal_format; int width = loader->src.sized.width; int height = loader->src.sized.height; CoglContext *ctx = tex->context; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; GLenum gl_texture; internal_format = _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); if (!_cogl_texture_2d_gl_can_create (ctx, width, height, internal_format)) { g_set_error_literal (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "Failed to create texture 2d due to size/format" " constraints"); return FALSE; } ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, &gl_format, &gl_type); gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); tex_2d->gl_internal_format = gl_intformat; _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, gl_texture); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); ctx->glTexImage2D (GL_TEXTURE_2D, 0, gl_intformat, width, height, 0, gl_format, gl_type, NULL); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) { GE( ctx, glDeleteTextures (1, &gl_texture) ); return FALSE; } tex_2d->gl_texture = gl_texture; tex_2d->gl_internal_format = gl_intformat; tex_2d->internal_format = internal_format; _cogl_texture_set_allocated (tex, internal_format, width, height); return TRUE; } static gboolean allocate_from_bitmap (CoglTexture2D *tex_2d, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglBitmap *bmp = loader->src.bitmap.bitmap; CoglContext *ctx = _cogl_bitmap_get_context (bmp); CoglPixelFormat internal_format; int width = cogl_bitmap_get_width (bmp); int height = cogl_bitmap_get_height (bmp); gboolean can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglBitmap *upload_bmp; GLenum gl_intformat; GLenum gl_format; GLenum gl_type; internal_format = _cogl_texture_determine_internal_format (tex, cogl_bitmap_get_format (bmp)); if (!_cogl_texture_2d_gl_can_create (ctx, width, height, internal_format)) { g_set_error_literal (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE, "Failed to create texture 2d due to size/format" " constraints"); return FALSE; } upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, cogl_bitmap_get_format (upload_bmp), NULL, /* internal format */ &gl_format, &gl_type); ctx->driver_vtable->pixel_format_to_gl (ctx, internal_format, &gl_intformat, NULL, NULL); tex_2d->gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); if (!ctx->texture_driver->upload_to_gl (ctx, GL_TEXTURE_2D, tex_2d->gl_texture, upload_bmp, gl_intformat, gl_format, gl_type, error)) { cogl_object_unref (upload_bmp); return FALSE; } tex_2d->gl_internal_format = gl_intformat; cogl_object_unref (upload_bmp); tex_2d->internal_format = internal_format; _cogl_texture_set_allocated (tex, internal_format, width, height); return TRUE; } #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) static gboolean allocate_from_egl_image (CoglTexture2D *tex_2d, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; CoglPixelFormat internal_format = loader->src.egl_image.format; tex_2d->gl_texture = ctx->texture_driver->gen (ctx, GL_TEXTURE_2D, internal_format); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture); _cogl_gl_util_clear_gl_errors (ctx); ctx->glEGLImageTargetTexture2D (GL_TEXTURE_2D, loader->src.egl_image.image); if (_cogl_gl_util_get_error (ctx) != GL_NO_ERROR) { g_set_error_literal (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_BAD_PARAMETER, "Could not create a CoglTexture2D from a given " "EGLImage"); GE( ctx, glDeleteTextures (1, &tex_2d->gl_texture) ); return FALSE; } tex_2d->internal_format = internal_format; tex_2d->is_get_data_supported = !(loader->src.egl_image.flags & COGL_EGL_IMAGE_FLAG_NO_GET_DATA); _cogl_texture_set_allocated (tex, internal_format, loader->src.egl_image.width, loader->src.egl_image.height); return TRUE; } #endif #if defined (COGL_HAS_EGL_SUPPORT) static gboolean allocate_custom_egl_image_external (CoglTexture2D *tex_2d, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; CoglPixelFormat external_format; CoglPixelFormat internal_format; external_format = loader->src.egl_image_external.format; internal_format = _cogl_texture_determine_internal_format (tex, external_format); _cogl_gl_util_clear_gl_errors (ctx); GE (ctx, glActiveTexture (GL_TEXTURE0)); GE (ctx, glGenTextures (1, &tex_2d->gl_texture)); GE (ctx, glBindTexture (GL_TEXTURE_EXTERNAL_OES, tex_2d->gl_texture)); if (_cogl_gl_util_get_error (ctx) != GL_NO_ERROR) { g_set_error_literal (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_BAD_PARAMETER, "Could not create a CoglTexture2D from a given " "EGLImage"); GE( ctx, glDeleteTextures (1, &tex_2d->gl_texture) ); return FALSE; } GE (ctx, glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GE (ctx, glTexParameteri(GL_TEXTURE_EXTERNAL_OES, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); if (!loader->src.egl_image_external.alloc (tex_2d, tex_2d->egl_image_external.user_data, error)) { GE (ctx, glBindTexture (GL_TEXTURE_EXTERNAL_OES, 0)); GE (ctx, glDeleteTextures (1, &tex_2d->gl_texture)); return FALSE; } GE (ctx, glBindTexture (GL_TEXTURE_EXTERNAL_OES, 0)); tex_2d->internal_format = internal_format; tex_2d->gl_target = GL_TEXTURE_EXTERNAL_OES; tex_2d->is_get_data_supported = FALSE; return TRUE; } CoglTexture2D * cogl_texture_2d_new_from_egl_image_external (CoglContext *ctx, int width, int height, CoglTexture2DEGLImageExternalAlloc alloc, gpointer user_data, GDestroyNotify destroy, GError **error) { CoglTextureLoader *loader; CoglTexture2D *tex_2d; CoglPixelFormat internal_format = COGL_PIXEL_FORMAT_ANY; g_return_val_if_fail (_cogl_context_get_winsys (ctx)->constraints & COGL_RENDERER_CONSTRAINT_USES_EGL, NULL); g_return_val_if_fail (cogl_has_feature (ctx, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL), NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE_EXTERNAL; loader->src.egl_image_external.width = width; loader->src.egl_image_external.height = height; loader->src.egl_image_external.alloc = alloc; loader->src.egl_image_external.format = internal_format; tex_2d = _cogl_texture_2d_create_base (ctx, width, height, internal_format, loader); tex_2d->egl_image_external.user_data = user_data; tex_2d->egl_image_external.destroy = destroy; return tex_2d; } #endif /* defined (COGL_HAS_EGL_SUPPORT) */ gboolean _cogl_texture_2d_gl_allocate (CoglTexture *tex, GError **error) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); CoglTextureLoader *loader = tex->loader; g_return_val_if_fail (loader, FALSE); switch (loader->src_type) { case COGL_TEXTURE_SOURCE_TYPE_SIZED: return allocate_with_size (tex_2d, loader, error); case COGL_TEXTURE_SOURCE_TYPE_BITMAP: return allocate_from_bitmap (tex_2d, loader, error); case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE: #if defined (COGL_HAS_EGL_SUPPORT) && defined (EGL_KHR_image_base) return allocate_from_egl_image (tex_2d, loader, error); #else g_return_val_if_reached (FALSE); #endif case COGL_TEXTURE_SOURCE_TYPE_EGL_IMAGE_EXTERNAL: #if defined (COGL_HAS_EGL_SUPPORT) return allocate_custom_egl_image_external (tex_2d, loader, error); #else g_return_val_if_reached (FALSE); #endif } g_return_val_if_reached (FALSE); } void _cogl_texture_2d_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); CoglContext *ctx = tex->context; if (min_filter == tex_2d->gl_legacy_texobj_min_filter && mag_filter == tex_2d->gl_legacy_texobj_mag_filter) return; /* Store new values */ tex_2d->gl_legacy_texobj_min_filter = min_filter; tex_2d->gl_legacy_texobj_mag_filter = mag_filter; /* Apply new filters to the texture */ _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture); GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter) ); GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter) ); } void _cogl_texture_2d_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { CoglTexture2D *tex_2d = COGL_TEXTURE_2D (tex); CoglContext *ctx = tex->context; /* Only set the wrap mode if it's different from the current value to avoid too many GL calls. Texture 2D doesn't make use of the r coordinate so we can ignore its wrap mode */ if (tex_2d->gl_legacy_texobj_wrap_mode_s != wrap_mode_s || tex_2d->gl_legacy_texobj_wrap_mode_t != wrap_mode_t) { _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture); GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrap_mode_s) ); GE( ctx, glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, wrap_mode_t) ); tex_2d->gl_legacy_texobj_wrap_mode_s = wrap_mode_s; tex_2d->gl_legacy_texobj_wrap_mode_t = wrap_mode_t; } } void _cogl_texture_2d_gl_copy_from_framebuffer (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; /* Make sure the current framebuffers are bound, though we don't need to * flush the clip state here since we aren't going to draw to the * framebuffer. */ _cogl_framebuffer_flush_state (ctx->current_draw_buffer, src_fb, COGL_FRAMEBUFFER_STATE_ALL & ~COGL_FRAMEBUFFER_STATE_CLIP); _cogl_bind_gl_texture_transient (GL_TEXTURE_2D, tex_2d->gl_texture); ctx->glCopyTexSubImage2D (GL_TEXTURE_2D, 0, /* level */ dst_x, dst_y, src_x, src_y, width, height); } unsigned int _cogl_texture_2d_gl_get_gl_handle (CoglTexture2D *tex_2d) { return tex_2d->gl_texture; } void _cogl_texture_2d_gl_generate_mipmap (CoglTexture2D *tex_2d) { _cogl_texture_gl_generate_mipmaps (COGL_TEXTURE (tex_2d)); } gboolean _cogl_texture_2d_gl_copy_from_bitmap (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bmp, int dst_x, int dst_y, int level, GError **error) { CoglTexture *tex = COGL_TEXTURE (tex_2d); CoglContext *ctx = tex->context; CoglBitmap *upload_bmp; CoglPixelFormat upload_format; GLenum gl_format; GLenum gl_type; gboolean status = TRUE; upload_bmp = _cogl_bitmap_convert_for_upload (bmp, _cogl_texture_get_format (tex), FALSE, /* can't convert in place */ error); if (upload_bmp == NULL) return FALSE; upload_format = cogl_bitmap_get_format (upload_bmp); /* Only support single plane formats */ if (upload_format == COGL_PIXEL_FORMAT_ANY || cogl_pixel_format_get_n_planes (upload_format) != 1) return FALSE; ctx->driver_vtable->pixel_format_to_gl (ctx, upload_format, NULL, /* internal gl format */ &gl_format, &gl_type); if (tex->max_level_set < level) cogl_texture_gl_set_max_level (tex, level); status = ctx->texture_driver->upload_subregion_to_gl (ctx, tex, src_x, src_y, dst_x, dst_y, width, height, level, upload_bmp, gl_format, gl_type, error); cogl_object_unref (upload_bmp); return status; } gboolean _cogl_texture_2d_gl_is_get_data_supported (CoglTexture2D *tex_2d) { return tex_2d->is_get_data_supported; } void _cogl_texture_2d_gl_get_data (CoglTexture2D *tex_2d, CoglPixelFormat format, int rowstride, uint8_t *data) { CoglContext *ctx = COGL_TEXTURE (tex_2d)->context; uint8_t bpp; int width = COGL_TEXTURE (tex_2d)->width; GLenum gl_format; GLenum gl_type; g_return_if_fail (format != COGL_PIXEL_FORMAT_ANY); g_return_if_fail (cogl_pixel_format_get_n_planes (format) == 1); bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); ctx->driver_vtable->pixel_format_to_gl (ctx, format, NULL, /* internal format */ &gl_format, &gl_type); ctx->texture_driver->prep_gl_for_pixels_download (ctx, rowstride, width, bpp); _cogl_bind_gl_texture_transient (tex_2d->gl_target, tex_2d->gl_texture); ctx->texture_driver->gl_get_tex_image (ctx, tex_2d->gl_target, gl_format, gl_type, data); } muffin-6.4.1/cogl/cogl/driver/gl/gles/0000775000175000017500000000000014723361714016441 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/driver/gl/gles/cogl-texture-driver-gles.c0000664000175000017500000003667414723361714023470 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Matthew Allum * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-private.h" #include "cogl-util.h" #include "cogl-bitmap.h" #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-pipeline.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include "driver/gl/cogl-bitmap-gl-private.h" #include #include #include #ifndef GL_TEXTURE_3D #define GL_TEXTURE_3D 0x806F #endif #ifndef GL_MAX_3D_TEXTURE_SIZE_OES #define GL_MAX_3D_TEXTURE_SIZE_OES 0x8073 #endif /* This extension isn't available for GLES 1.1 so these won't be defined */ #ifndef GL_UNPACK_ROW_LENGTH #define GL_UNPACK_ROW_LENGTH 0x0CF2 #endif #ifndef GL_UNPACK_SKIP_ROWS #define GL_UNPACK_SKIP_ROWS 0x0CF3 #endif #ifndef GL_UNPACK_SKIP_PIXELS #define GL_UNPACK_SKIP_PIXELS 0x0CF4 #endif static GLuint _cogl_texture_driver_gen (CoglContext *ctx, GLenum gl_target, CoglPixelFormat internal_format) { GLuint tex; GE (ctx, glGenTextures (1, &tex)); _cogl_bind_gl_texture_transient (gl_target, tex); switch (gl_target) { case GL_TEXTURE_2D: case GL_TEXTURE_3D: /* GL_TEXTURE_MAG_FILTER defaults to GL_LINEAR, no need to set it */ GE( ctx, glTexParameteri (gl_target, GL_TEXTURE_MIN_FILTER, GL_LINEAR) ); break; default: g_assert_not_reached(); } return tex; } static void prep_gl_for_pixels_upload_full (CoglContext *ctx, int pixels_rowstride, int pixels_src_x, int pixels_src_y, int pixels_bpp) { if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE)) { GE( ctx, glPixelStorei (GL_UNPACK_ROW_LENGTH, pixels_rowstride / pixels_bpp) ); GE( ctx, glPixelStorei (GL_UNPACK_SKIP_PIXELS, pixels_src_x) ); GE( ctx, glPixelStorei (GL_UNPACK_SKIP_ROWS, pixels_src_y) ); } else { g_assert (pixels_src_x == 0); g_assert (pixels_src_y == 0); } _cogl_texture_gl_prep_alignment_for_pixels_upload (ctx, pixels_rowstride); } static void _cogl_texture_driver_prep_gl_for_pixels_upload (CoglContext *ctx, int pixels_rowstride, int pixels_bpp) { prep_gl_for_pixels_upload_full (ctx, pixels_rowstride, 0, 0, /* src_x/y */ pixels_bpp); } static void _cogl_texture_driver_prep_gl_for_pixels_download (CoglContext *ctx, int pixels_rowstride, int image_width, int pixels_bpp) { _cogl_texture_gl_prep_alignment_for_pixels_download (ctx, pixels_bpp, image_width, pixels_rowstride); } static CoglBitmap * prepare_bitmap_alignment_for_upload (CoglContext *ctx, CoglBitmap *src_bmp, GError **error) { CoglPixelFormat format = cogl_bitmap_get_format (src_bmp); int bpp; int src_rowstride = cogl_bitmap_get_rowstride (src_bmp); int width = cogl_bitmap_get_width (src_bmp); int alignment = 1; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); if (_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE) || src_rowstride == 0) return cogl_object_ref (src_bmp); /* Work out the alignment of the source rowstride */ alignment = 1 << (ffs (src_rowstride) - 1); alignment = MIN (alignment, 8); /* If the aligned data equals the rowstride then we can upload from the bitmap directly using GL_UNPACK_ALIGNMENT */ if (((width * bpp + alignment - 1) & ~(alignment - 1)) == src_rowstride) return cogl_object_ref (src_bmp); /* Otherwise we need to copy the bitmap to pack the alignment because GLES has no GL_ROW_LENGTH */ else return _cogl_bitmap_copy (src_bmp, error); } static gboolean _cogl_texture_driver_upload_subregion_to_gl (CoglContext *ctx, CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, int width, int height, int level, CoglBitmap *source_bmp, GLuint source_gl_format, GLuint source_gl_type, GError **error) { GLenum gl_target; GLuint gl_handle; uint8_t *data; CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp; CoglBitmap *slice_bmp; int rowstride; gboolean status = TRUE; GError *internal_error = NULL; int level_width; int level_height; g_return_val_if_fail (source_format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (source_format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (source_format, 0); cogl_texture_get_gl_texture (texture, &gl_handle, &gl_target); /* If we have the GL_EXT_unpack_subimage extension then we can upload from subregions directly. Otherwise we may need to copy the bitmap */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE) && (src_x != 0 || src_y != 0 || width != cogl_bitmap_get_width (source_bmp) || height != cogl_bitmap_get_height (source_bmp))) { slice_bmp = _cogl_bitmap_new_with_malloc_buffer (ctx, width, height, source_format, error); if (!slice_bmp) return FALSE; if (!_cogl_bitmap_copy_subregion (source_bmp, slice_bmp, src_x, src_y, 0, 0, /* dst_x/y */ width, height, error)) { cogl_object_unref (slice_bmp); return FALSE; } src_x = src_y = 0; } else { slice_bmp = prepare_bitmap_alignment_for_upload (ctx, source_bmp, error); if (!slice_bmp) return FALSE; } rowstride = cogl_bitmap_get_rowstride (slice_bmp); /* Setup gl alignment to match rowstride and top-left corner */ prep_gl_for_pixels_upload_full (ctx, rowstride, src_x, src_y, bpp); data = _cogl_bitmap_gl_bind (slice_bmp, COGL_BUFFER_ACCESS_READ, 0, &internal_error); /* NB: _cogl_bitmap_gl_bind() may return NULL when successfull so we * have to explicitly check the cogl error pointer to catch * problems... */ if (internal_error) { g_propagate_error (error, internal_error); cogl_object_unref (slice_bmp); return FALSE; } _cogl_bind_gl_texture_transient (gl_target, gl_handle); /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); _cogl_texture_get_level_size (texture, level, &level_width, &level_height, NULL); if (level_width == width && level_height == height) { /* GL gets upset if you use glTexSubImage2D to define the * contents of a mipmap level so we make sure to use * glTexImage2D if we are uploading a full mipmap level. */ ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), width, height, 0, source_gl_format, source_gl_type, data); } else { /* GL gets upset if you use glTexSubImage2D to initialize the * contents of a mipmap level so if this is the first time * we've seen a request to upload to this level we call * glTexImage2D first to assert that the storage for this * level exists. */ if (texture->max_level_set < level) { ctx->glTexImage2D (gl_target, level, _cogl_texture_gl_get_format (texture), level_width, level_height, 0, source_gl_format, source_gl_type, NULL); } ctx->glTexSubImage2D (gl_target, level, dst_x, dst_y, width, height, source_gl_format, source_gl_type, data); } if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_bitmap_gl_unbind (slice_bmp); cogl_object_unref (slice_bmp); return status; } static gboolean _cogl_texture_driver_upload_to_gl (CoglContext *ctx, GLenum gl_target, GLuint gl_handle, CoglBitmap *source_bmp, GLint internal_gl_format, GLuint source_gl_format, GLuint source_gl_type, GError **error) { CoglPixelFormat source_format = cogl_bitmap_get_format (source_bmp); int bpp; int rowstride; int bmp_width = cogl_bitmap_get_width (source_bmp); int bmp_height = cogl_bitmap_get_height (source_bmp); CoglBitmap *bmp; uint8_t *data; GError *internal_error = NULL; gboolean status = TRUE; g_return_val_if_fail (source_format != COGL_PIXEL_FORMAT_ANY, FALSE); g_return_val_if_fail (cogl_pixel_format_get_n_planes (source_format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (source_format, 0); bmp = prepare_bitmap_alignment_for_upload (ctx, source_bmp, error); if (!bmp) return FALSE; rowstride = cogl_bitmap_get_rowstride (bmp); /* Setup gl alignment to match rowstride and top-left corner */ _cogl_texture_driver_prep_gl_for_pixels_upload (ctx, rowstride, bpp); _cogl_bind_gl_texture_transient (gl_target, gl_handle); data = _cogl_bitmap_gl_bind (bmp, COGL_BUFFER_ACCESS_READ, 0, /* hints */ &internal_error); /* NB: _cogl_bitmap_gl_bind() may return NULL when successful so we * have to explicitly check the cogl error pointer to catch * problems... */ if (internal_error) { cogl_object_unref (bmp); g_propagate_error (error, internal_error); return FALSE; } /* Clear any GL errors */ _cogl_gl_util_clear_gl_errors (ctx); ctx->glTexImage2D (gl_target, 0, internal_gl_format, bmp_width, bmp_height, 0, source_gl_format, source_gl_type, data); if (_cogl_gl_util_catch_out_of_memory (ctx, error)) status = FALSE; _cogl_bitmap_gl_unbind (bmp); cogl_object_unref (bmp); return status; } /* NB: GLES doesn't support glGetTexImage2D, so cogl-texture will instead * fallback to a generic render + readpixels approach to downloading * texture data. (See _cogl_texture_draw_and_read() ) */ static gboolean _cogl_texture_driver_gl_get_tex_image (CoglContext *ctx, GLenum gl_target, GLenum dest_gl_format, GLenum dest_gl_type, uint8_t *dest) { return FALSE; } static gboolean _cogl_texture_driver_size_supported (CoglContext *ctx, GLenum gl_target, GLenum gl_intformat, GLenum gl_format, GLenum gl_type, int width, int height) { GLint max_size; /* GLES doesn't support a proxy texture target so let's at least check whether the size is greater than GL_MAX_TEXTURE_SIZE */ GE( ctx, glGetIntegerv (GL_MAX_TEXTURE_SIZE, &max_size) ); return width <= max_size && height <= max_size; } static CoglPixelFormat _cogl_texture_driver_find_best_gl_get_data_format (CoglContext *context, CoglPixelFormat format, GLenum *closest_gl_format, GLenum *closest_gl_type) { /* Find closest format that's supported by GL (Can't use _cogl_pixel_format_to_gl since available formats when reading pixels on GLES are severely limited) */ *closest_gl_format = GL_RGBA; *closest_gl_type = GL_UNSIGNED_BYTE; return COGL_PIXEL_FORMAT_RGBA_8888; } const CoglTextureDriver _cogl_texture_driver_gles = { _cogl_texture_driver_gen, _cogl_texture_driver_upload_subregion_to_gl, _cogl_texture_driver_upload_to_gl, _cogl_texture_driver_prep_gl_for_pixels_download, _cogl_texture_driver_gl_get_tex_image, _cogl_texture_driver_size_supported, _cogl_texture_driver_find_best_gl_get_data_format }; muffin-6.4.1/cogl/cogl/driver/gl/gles/cogl-driver-gles.c0000664000175000017500000003332114723361714021754 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-context-private.h" #include "cogl-feature-private.h" #include "cogl-renderer-private.h" #include "cogl-private.h" #include "driver/gl/cogl-util-gl-private.h" #include "driver/gl/cogl-framebuffer-gl-private.h" #include "driver/gl/cogl-texture-2d-gl-private.h" #include "driver/gl/cogl-attribute-gl-private.h" #include "driver/gl/cogl-clip-stack-gl-private.h" #include "driver/gl/cogl-buffer-gl-private.h" #ifndef GL_UNSIGNED_INT_24_8 #define GL_UNSIGNED_INT_24_8 0x84FA #endif #ifndef GL_DEPTH_STENCIL #define GL_DEPTH_STENCIL 0x84F9 #endif #ifndef GL_RG #define GL_RG 0x8227 #endif #ifndef GL_RG8 #define GL_RG8 0x822B #endif static gboolean _cogl_driver_pixel_format_from_gl_internal (CoglContext *context, GLenum gl_int_format, CoglPixelFormat *out_format) { return TRUE; } static CoglPixelFormat _cogl_driver_pixel_format_to_gl (CoglContext *context, CoglPixelFormat format, GLenum *out_glintformat, GLenum *out_glformat, GLenum *out_gltype) { CoglPixelFormat required_format; GLenum glintformat; GLenum glformat = 0; GLenum gltype; required_format = format; /* Find GL equivalents */ switch (format) { case COGL_PIXEL_FORMAT_A_8: glintformat = GL_ALPHA; glformat = GL_ALPHA; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_G_8: glintformat = GL_LUMINANCE; glformat = GL_LUMINANCE; gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_RG_88: if (cogl_has_feature (context, COGL_FEATURE_ID_TEXTURE_RG)) { glintformat = GL_RG8; glformat = GL_RG; } else { /* If red-green textures aren't supported then we'll use RGB * as an internal format. Note this should only end up * mattering for downloading the data because Cogl will * refuse to allocate a texture with RG components if RG * textures aren't supported */ glintformat = GL_RGB; glformat = GL_RGB; required_format = COGL_PIXEL_FORMAT_RGB_888; } gltype = GL_UNSIGNED_BYTE; break; case COGL_PIXEL_FORMAT_BGRA_8888: case COGL_PIXEL_FORMAT_BGRA_8888_PRE: /* There is an extension to support this format */ if (_cogl_has_private_feature (context, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888)) { /* For some reason the extension says you have to specify BGRA for the internal format too */ glintformat = GL_BGRA_EXT; glformat = GL_BGRA_EXT; gltype = GL_UNSIGNED_BYTE; required_format = format; break; } /* flow through */ /* Just one 24-bit ordering supported */ case COGL_PIXEL_FORMAT_RGB_888: case COGL_PIXEL_FORMAT_BGR_888: glintformat = GL_RGB; glformat = GL_RGB; gltype = GL_UNSIGNED_BYTE; required_format = COGL_PIXEL_FORMAT_RGB_888; break; /* Just one 32-bit ordering supported */ case COGL_PIXEL_FORMAT_RGBA_8888: case COGL_PIXEL_FORMAT_RGBA_8888_PRE: case COGL_PIXEL_FORMAT_ARGB_8888: case COGL_PIXEL_FORMAT_ARGB_8888_PRE: case COGL_PIXEL_FORMAT_ABGR_8888: case COGL_PIXEL_FORMAT_ABGR_8888_PRE: case COGL_PIXEL_FORMAT_RGBA_1010102: case COGL_PIXEL_FORMAT_RGBA_1010102_PRE: case COGL_PIXEL_FORMAT_BGRA_1010102: case COGL_PIXEL_FORMAT_BGRA_1010102_PRE: case COGL_PIXEL_FORMAT_ABGR_2101010: case COGL_PIXEL_FORMAT_ABGR_2101010_PRE: case COGL_PIXEL_FORMAT_ARGB_2101010: case COGL_PIXEL_FORMAT_ARGB_2101010_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_BYTE; required_format = COGL_PIXEL_FORMAT_RGBA_8888; required_format |= (format & COGL_PREMULT_BIT); break; /* The following three types of channel ordering * are always defined using system word byte * ordering (even according to GLES spec) */ case COGL_PIXEL_FORMAT_RGB_565: glintformat = GL_RGB; glformat = GL_RGB; gltype = GL_UNSIGNED_SHORT_5_6_5; break; case COGL_PIXEL_FORMAT_RGBA_4444: case COGL_PIXEL_FORMAT_RGBA_4444_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_4_4_4_4; break; case COGL_PIXEL_FORMAT_RGBA_5551: case COGL_PIXEL_FORMAT_RGBA_5551_PRE: glintformat = GL_RGBA; glformat = GL_RGBA; gltype = GL_UNSIGNED_SHORT_5_5_5_1; break; case COGL_PIXEL_FORMAT_DEPTH_16: glintformat = GL_DEPTH_COMPONENT; glformat = GL_DEPTH_COMPONENT; gltype = GL_UNSIGNED_SHORT; break; case COGL_PIXEL_FORMAT_DEPTH_32: glintformat = GL_DEPTH_COMPONENT; glformat = GL_DEPTH_COMPONENT; gltype = GL_UNSIGNED_INT; break; case COGL_PIXEL_FORMAT_DEPTH_24_STENCIL_8: glintformat = GL_DEPTH_STENCIL; glformat = GL_DEPTH_STENCIL; gltype = GL_UNSIGNED_INT_24_8; break; case COGL_PIXEL_FORMAT_ANY: case COGL_PIXEL_FORMAT_YUV: g_assert_not_reached (); break; } /* All of the pixel formats are handled above so if this hits then we've been given an invalid pixel format */ g_assert (glformat != 0); if (out_glintformat != NULL) *out_glintformat = glintformat; if (out_glformat != NULL) *out_glformat = glformat; if (out_gltype != NULL) *out_gltype = gltype; return required_format; } static gboolean _cogl_get_gl_version (CoglContext *ctx, int *major_out, int *minor_out) { const char *version_string; /* Get the OpenGL version number */ if ((version_string = _cogl_context_get_gl_version (ctx)) == NULL) return FALSE; if (!g_str_has_prefix (version_string, "OpenGL ES ")) return FALSE; return _cogl_gl_util_parse_gl_version (version_string + 10, major_out, minor_out); } static gboolean _cogl_driver_update_features (CoglContext *context, GError **error) { unsigned long private_features [COGL_FLAGS_N_LONGS_FOR_SIZE (COGL_N_PRIVATE_FEATURES)] = { 0 }; char **gl_extensions; int gl_major, gl_minor; int i; /* We have to special case getting the pointer to the glGetString function because we need to use it to determine what functions we can expect */ context->glGetString = (void *) _cogl_renderer_get_proc_address (context->display->renderer, "glGetString", TRUE); context->glGetStringi = (void *) _cogl_renderer_get_proc_address (context->display->renderer, "glGetStringi", TRUE); gl_extensions = _cogl_context_get_gl_extensions (context); if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WINSYS))) { char *all_extensions = g_strjoinv (" ", gl_extensions); COGL_NOTE (WINSYS, "Checking features\n" " GL_VENDOR: %s\n" " GL_RENDERER: %s\n" " GL_VERSION: %s\n" " GL_EXTENSIONS: %s", context->glGetString (GL_VENDOR), context->glGetString (GL_RENDERER), _cogl_context_get_gl_version (context), all_extensions); g_free (all_extensions); } context->glsl_major = 1; context->glsl_minor = 0; context->glsl_version_to_use = 100; _cogl_gpu_info_init (context, &context->gpu); if (!_cogl_get_gl_version (context, &gl_major, &gl_minor)) { gl_major = 1; gl_minor = 1; } if (!COGL_CHECK_GL_VERSION (gl_major, gl_minor, 2, 0)) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_INVALID_VERSION, "OpenGL ES 2.0 or better is required"); return FALSE; } _cogl_feature_check_ext_functions (context, gl_major, gl_minor, gl_extensions); if (_cogl_check_extension ("GL_ANGLE_pack_reverse_row_order", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_MESA_PACK_INVERT, TRUE); /* Note GLES 2 core doesn't support mipmaps for npot textures or * repeat modes other than CLAMP_TO_EDGE. */ COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ANY_GL, TRUE); COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_ALPHA_TEXTURES, TRUE); if (context->glGenSamplers) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_SAMPLER_OBJECTS, TRUE); if (context->glBlitFramebuffer) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER, TRUE); if (_cogl_check_extension ("GL_OES_element_index_uint", gl_extensions)) { COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_UNSIGNED_INT_INDICES, TRUE); } if (context->glMapBuffer) { /* The GL_OES_mapbuffer extension doesn't support mapping for read */ COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); } if (context->glMapBufferRange) { /* MapBufferRange in ES3+ does support mapping for read */ COGL_FLAGS_SET(context->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE, TRUE); COGL_FLAGS_SET(context->features, COGL_FEATURE_ID_MAP_BUFFER_FOR_READ, TRUE); } if (context->glEGLImageTargetTexture2D) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_2D_FROM_EGL_IMAGE, TRUE); if (_cogl_check_extension ("GL_OES_packed_depth_stencil", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_OES_PACKED_DEPTH_STENCIL, TRUE); if (_cogl_check_extension ("GL_EXT_texture_format_BGRA8888", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_TEXTURE_FORMAT_BGRA8888, TRUE); if (_cogl_check_extension ("GL_EXT_unpack_subimage", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_UNPACK_SUBIMAGE, TRUE); /* A nameless vendor implemented the extension, but got the case wrong * per the spec. */ if (_cogl_check_extension ("GL_OES_EGL_sync", gl_extensions) || _cogl_check_extension ("GL_OES_egl_sync", gl_extensions)) COGL_FLAGS_SET (private_features, COGL_PRIVATE_FEATURE_OES_EGL_SYNC, TRUE); #ifdef GL_ARB_sync if (context->glFenceSync) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_FENCE, TRUE); #endif if (_cogl_check_extension ("GL_EXT_texture_rg", gl_extensions)) COGL_FLAGS_SET (context->features, COGL_FEATURE_ID_TEXTURE_RG, TRUE); /* Cache features */ for (i = 0; i < G_N_ELEMENTS (private_features); i++) context->private_features[i] |= private_features[i]; g_strfreev (gl_extensions); return TRUE; } static gboolean _cogl_driver_texture_2d_is_get_data_supported (CoglTexture2D *tex_2d) { return FALSE; } const CoglDriverVtable _cogl_driver_gles = { _cogl_driver_gl_context_init, _cogl_driver_gl_context_deinit, _cogl_driver_pixel_format_from_gl_internal, _cogl_driver_pixel_format_to_gl, _cogl_driver_update_features, _cogl_offscreen_gl_allocate, _cogl_offscreen_gl_free, _cogl_framebuffer_gl_flush_state, _cogl_framebuffer_gl_clear, _cogl_framebuffer_gl_query_bits, _cogl_framebuffer_gl_finish, _cogl_framebuffer_gl_flush, _cogl_framebuffer_gl_discard_buffers, _cogl_framebuffer_gl_draw_attributes, _cogl_framebuffer_gl_draw_indexed_attributes, _cogl_framebuffer_gl_read_pixels_into_bitmap, _cogl_texture_2d_gl_free, _cogl_texture_2d_gl_can_create, _cogl_texture_2d_gl_init, _cogl_texture_2d_gl_allocate, _cogl_texture_2d_gl_copy_from_framebuffer, _cogl_texture_2d_gl_get_gl_handle, _cogl_texture_2d_gl_generate_mipmap, _cogl_texture_2d_gl_copy_from_bitmap, _cogl_driver_texture_2d_is_get_data_supported, NULL, /* texture_2d_get_data */ _cogl_gl_flush_attributes_state, _cogl_clip_stack_gl_flush, _cogl_buffer_gl_create, _cogl_buffer_gl_destroy, _cogl_buffer_gl_map_range, _cogl_buffer_gl_unmap, _cogl_buffer_gl_set_data, }; muffin-6.4.1/cogl/cogl/driver/gl/cogl-pipeline-progend-glsl-private.h0000664000175000017500000000321714723361714024455 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #ifndef __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H #define __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H #include "cogl-pipeline-private.h" #include "cogl-attribute-private.h" extern const CoglPipelineProgend _cogl_pipeline_glsl_progend; int _cogl_pipeline_progend_glsl_get_attrib_location (CoglPipeline *pipeline, int name_index); #endif /* __COGL_PIPELINE_PROGEND_GLSL_PRIVATE_H */ muffin-6.4.1/cogl/cogl/driver/gl/cogl-clip-stack-gl-private.h0000664000175000017500000000304614723361714022707 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_CLIP_STACK_GL_PRIVATE_H_ #define _COGL_CLIP_STACK_GL_PRIVATE_H_ #include "cogl-types.h" #include "cogl-framebuffer.h" #include "cogl-clip-stack.h" void _cogl_clip_stack_gl_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer); #endif /* _COGL_CLIP_STACK_GL_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/nop/0000775000175000017500000000000014723361714015701 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/driver/nop/cogl-attribute-nop-private.h0000664000175000017500000000341314723361714023242 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_ATTRIBUTE_NOP_PRIVATE_H_ #define _COGL_ATTRIBUTE_NOP_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context-private.h" void _cogl_nop_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglFlushLayerState *layers_state, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes); #endif /* _COGL_ATTRIBUTE_NOP_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/nop/cogl-framebuffer-nop.c0000664000175000017500000000766114723361714022057 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-framebuffer-nop-private.h" #include #include void _cogl_framebuffer_nop_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state) { } gboolean _cogl_offscreen_nop_allocate (CoglOffscreen *offscreen, GError **error) { return TRUE; } void _cogl_offscreen_nop_free (CoglOffscreen *offscreen) { } void _cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha) { } void _cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, CoglFramebufferBits *bits) { memset (bits, 0, sizeof (CoglFramebufferBits)); } void _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer) { } void _cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer) { } void _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers) { } void _cogl_framebuffer_nop_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { } void _cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { } gboolean _cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error) { return TRUE; } muffin-6.4.1/cogl/cogl/driver/nop/cogl-clip-stack-nop-private.h0000664000175000017500000000302214723361714023265 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_CLIP_STACK_NOP_PRIVATE_H_ #define _COGL_CLIP_STACK_NOP_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context-private.h" void _cogl_clip_stack_nop_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer); #endif /* _COGL_CLIP_STACK_NOP_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/nop/cogl-attribute-nop.c0000664000175000017500000000332614723361714021570 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-types.h" #include "cogl-framebuffer.h" #include "cogl-attribute.h" #include "cogl-attribute-private.h" #include "cogl-attribute-nop-private.h" void _cogl_nop_flush_attributes_state (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglFlushLayerState *layers_state, CoglDrawFlags flags, CoglAttribute **attributes, int n_attributes) { } muffin-6.4.1/cogl/cogl/driver/nop/cogl-clip-stack-nop.c0000664000175000017500000000265014723361714021616 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-clip-stack.h" #include "cogl-clip-stack-nop-private.h" #include "cogl-framebuffer-private.h" void _cogl_clip_stack_nop_flush (CoglClipStack *stack, CoglFramebuffer *framebuffer) { } muffin-6.4.1/cogl/cogl/driver/nop/cogl-texture-2d-nop.c0000664000175000017500000000741014723361714021566 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2011,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-texture-2d-nop-private.h" #include "cogl-texture-2d-private.h" void _cogl_texture_2d_nop_free (CoglTexture2D *tex_2d) { } gboolean _cogl_texture_2d_nop_can_create (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format) { return TRUE; } void _cogl_texture_2d_nop_init (CoglTexture2D *tex_2d) { } gboolean _cogl_texture_2d_nop_allocate (CoglTexture *tex, GError **error) { return TRUE; } void _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { } void _cogl_texture_2d_nop_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { } void _cogl_texture_2d_nop_copy_from_framebuffer (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level) { } unsigned int _cogl_texture_2d_nop_get_gl_handle (CoglTexture2D *tex_2d) { return 0; } void _cogl_texture_2d_nop_generate_mipmap (CoglTexture2D *tex_2d) { } gboolean _cogl_texture_2d_nop_copy_from_bitmap (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bitmap, int dst_x, int dst_y, int level, GError **error) { return TRUE; } void _cogl_texture_2d_nop_get_data (CoglTexture2D *tex_2d, CoglPixelFormat format, size_t rowstride, uint8_t *data) { } muffin-6.4.1/cogl/cogl/driver/nop/cogl-texture-2d-nop-private.h0000664000175000017500000000726014723361714023246 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_TEXTURE_2D_NOP_PRIVATE_H_ #define _COGL_TEXTURE_2D_NOP_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context-private.h" #include "cogl-texture.h" void _cogl_texture_2d_nop_free (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_nop_can_create (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format); void _cogl_texture_2d_nop_init (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_nop_allocate (CoglTexture *tex, GError **error); void _cogl_texture_2d_nop_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter); void _cogl_texture_2d_nop_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t); void _cogl_texture_2d_nop_copy_from_framebuffer (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglFramebuffer *src_fb, int dst_x, int dst_y, int level); unsigned int _cogl_texture_2d_nop_get_gl_handle (CoglTexture2D *tex_2d); void _cogl_texture_2d_nop_generate_mipmap (CoglTexture2D *tex_2d); gboolean _cogl_texture_2d_nop_copy_from_bitmap (CoglTexture2D *tex_2d, int src_x, int src_y, int width, int height, CoglBitmap *bitmap, int dst_x, int dst_y, int level, GError **error); void _cogl_texture_2d_nop_get_data (CoglTexture2D *tex_2d, CoglPixelFormat format, size_t rowstride, uint8_t *data); #endif /* _COGL_TEXTURE_2D_NOP_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/nop/cogl-framebuffer-nop-private.h0000664000175000017500000000766714723361714023542 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ #define _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ #include "cogl-types.h" #include "cogl-context-private.h" gboolean _cogl_offscreen_nop_allocate (CoglOffscreen *offscreen, GError **error); void _cogl_offscreen_nop_free (CoglOffscreen *offscreen); void _cogl_framebuffer_nop_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state); void _cogl_framebuffer_nop_clear (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha); void _cogl_framebuffer_nop_query_bits (CoglFramebuffer *framebuffer, CoglFramebufferBits *bits); void _cogl_framebuffer_nop_finish (CoglFramebuffer *framebuffer); void _cogl_framebuffer_nop_flush (CoglFramebuffer *framebuffer); void _cogl_framebuffer_nop_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers); void _cogl_framebuffer_nop_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); void _cogl_framebuffer_nop_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); gboolean _cogl_framebuffer_nop_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error); #endif /* _COGL_FRAMEBUFFER_NOP_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/driver/nop/cogl-driver-nop.c0000664000175000017500000000600214723361714021052 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-private.h" #include "cogl-context-private.h" #include "cogl-feature-private.h" #include "cogl-renderer-private.h" #include "cogl-framebuffer-nop-private.h" #include "cogl-texture-2d-nop-private.h" #include "cogl-attribute-nop-private.h" #include "cogl-clip-stack-nop-private.h" static gboolean _cogl_driver_update_features (CoglContext *ctx, GError **error) { /* _cogl_gpu_info_init (ctx, &ctx->gpu); */ memset (ctx->private_features, 0, sizeof (ctx->private_features)); return TRUE; } static gboolean _cogl_driver_nop_context_init (CoglContext *context) { return TRUE; } static void _cogl_driver_nop_context_deinit (CoglContext *context) { } const CoglDriverVtable _cogl_driver_nop = { _cogl_driver_nop_context_init, _cogl_driver_nop_context_deinit, NULL, /* pixel_format_from_gl_internal */ NULL, /* pixel_format_to_gl */ _cogl_driver_update_features, _cogl_offscreen_nop_allocate, _cogl_offscreen_nop_free, _cogl_framebuffer_nop_flush_state, _cogl_framebuffer_nop_clear, _cogl_framebuffer_nop_query_bits, _cogl_framebuffer_nop_finish, _cogl_framebuffer_nop_flush, _cogl_framebuffer_nop_discard_buffers, _cogl_framebuffer_nop_draw_attributes, _cogl_framebuffer_nop_draw_indexed_attributes, _cogl_framebuffer_nop_read_pixels_into_bitmap, _cogl_texture_2d_nop_free, _cogl_texture_2d_nop_can_create, _cogl_texture_2d_nop_init, _cogl_texture_2d_nop_allocate, _cogl_texture_2d_nop_copy_from_framebuffer, _cogl_texture_2d_nop_get_gl_handle, _cogl_texture_2d_nop_generate_mipmap, _cogl_texture_2d_nop_copy_from_bitmap, NULL, /* texture_2d_is_get_data_supported */ NULL, /* texture_2d_get_data */ _cogl_nop_flush_attributes_state, _cogl_clip_stack_nop_flush, }; muffin-6.4.1/cogl/cogl/cogl-node.c0000664000175000017500000000641714723361714015635 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-node-private.h" void _cogl_pipeline_node_init (CoglNode *node) { node->parent = NULL; _cogl_list_init (&node->children); } void _cogl_pipeline_node_set_parent_real (CoglNode *node, CoglNode *parent, CoglNodeUnparentVFunc unparent, gboolean take_strong_reference) { /* NB: the old parent may indirectly be keeping the new parent alive * so we have to ref the new parent before unrefing the old. * * Note: we take a reference here regardless of * take_strong_reference because weak children may need special * handling when the parent disposes itself which relies on a * consistent link to all weak nodes. Once the node is linked to its * parent then we remove the reference at the end if * take_strong_reference == FALSE. */ cogl_object_ref (parent); if (node->parent) unparent (node); _cogl_list_insert (&parent->children, &node->link); node->parent = parent; node->has_parent_reference = take_strong_reference; /* Now that there is a consistent parent->child link we can remove * the parent reference if no reference was requested. If it turns * out that the new parent was only being kept alive by the old * parent then it will be disposed of here. */ if (!take_strong_reference) cogl_object_unref (parent); } void _cogl_pipeline_node_unparent_real (CoglNode *node) { CoglNode *parent = node->parent; if (parent == NULL) return; g_return_if_fail (!_cogl_list_empty (&parent->children)); _cogl_list_remove (&node->link); if (node->has_parent_reference) cogl_object_unref (parent); node->parent = NULL; } void _cogl_pipeline_node_foreach_child (CoglNode *node, CoglNodeChildCallback callback, void *user_data) { CoglNode *child, *next; _cogl_list_for_each_safe (child, next, &node->children, link) callback (child, user_data); } muffin-6.4.1/cogl/cogl/cogl-pipeline-snippet.c0000664000175000017500000002142014723361714020164 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011, 2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-types.h" #include "cogl-pipeline-snippet-private.h" #include "cogl-snippet-private.h" #include "cogl-util.h" /* Helper functions that are used by both GLSL pipeline backends */ void _cogl_pipeline_snippet_generate_code (const CoglPipelineSnippetData *data) { GList *first_snippet, *l; CoglSnippet *snippet; int snippet_num = 0; int n_snippets = 0; first_snippet = data->snippets->entries; /* First count the number of snippets so we can easily tell when we're at the last one */ for (l = data->snippets->entries; l; l = l->next) { snippet = l->data; if (snippet->hook == data->hook) { /* Don't bother processing any previous snippets if we reach one that has a replacement */ if (snippet->replace) { n_snippets = 1; first_snippet = l; } else n_snippets++; } } /* If there weren't any snippets then generate a stub function with the final name */ if (n_snippets == 0) { if (data->return_type) g_string_append_printf (data->source_buf, "\n" "%s\n" "%s (%s)\n" "{\n" " return %s (%s);\n" "}\n", data->return_type, data->final_name, data->argument_declarations ? data->argument_declarations : "", data->chain_function, data->arguments ? data->arguments : ""); else g_string_append_printf (data->source_buf, "\n" "void\n" "%s (%s)\n" "{\n" " %s (%s);\n" "}\n", data->final_name, data->argument_declarations ? data->argument_declarations : "", data->chain_function, data->arguments ? data->arguments : ""); return; } for (l = first_snippet; snippet_num < n_snippets; l = l->next) { snippet = l->data; if (snippet->hook == data->hook) { const char *source; if ((source = cogl_snippet_get_declarations (snippet))) g_string_append (data->source_buf, source); g_string_append_printf (data->source_buf, "\n" "%s\n", data->return_type ? data->return_type : "void"); if (snippet_num + 1 < n_snippets) g_string_append_printf (data->source_buf, "%s_%i", data->function_prefix, snippet_num); else g_string_append (data->source_buf, data->final_name); g_string_append (data->source_buf, " ("); if (data->argument_declarations) g_string_append (data->source_buf, data->argument_declarations); g_string_append (data->source_buf, ")\n" "{\n"); if (data->return_type && !data->return_variable_is_argument) g_string_append_printf (data->source_buf, " %s %s;\n" "\n", data->return_type, data->return_variable); if ((source = cogl_snippet_get_pre (snippet))) g_string_append (data->source_buf, source); /* Chain on to the next function, or bypass it if there is a replace string */ if ((source = cogl_snippet_get_replace (snippet))) g_string_append (data->source_buf, source); else { g_string_append (data->source_buf, " "); if (data->return_type) g_string_append_printf (data->source_buf, "%s = ", data->return_variable); if (snippet_num > 0) g_string_append_printf (data->source_buf, "%s_%i", data->function_prefix, snippet_num - 1); else g_string_append (data->source_buf, data->chain_function); g_string_append (data->source_buf, " ("); if (data->arguments) g_string_append (data->source_buf, data->arguments); g_string_append (data->source_buf, ");\n"); } if ((source = cogl_snippet_get_post (snippet))) g_string_append (data->source_buf, source); if (data->return_type) g_string_append_printf (data->source_buf, " return %s;\n", data->return_variable); g_string_append (data->source_buf, "}\n"); snippet_num++; } } } void _cogl_pipeline_snippet_generate_declarations (GString *declarations_buf, CoglSnippetHook hook, CoglPipelineSnippetList *snippets) { GList *l; for (l = snippets->entries; l; l = l->next) { CoglSnippet *snippet = l->data; if (snippet->hook == hook) { const char *source; if ((source = cogl_snippet_get_declarations (snippet))) g_string_append (declarations_buf, source); } } } void _cogl_pipeline_snippet_list_free (CoglPipelineSnippetList *list) { GList *l, *tmp; for (l = list->entries; l; l = tmp) { tmp = l->next; cogl_object_unref (l->data); g_list_free_1 (l); } } void _cogl_pipeline_snippet_list_add (CoglPipelineSnippetList *list, CoglSnippet *snippet) { list->entries = g_list_append (list->entries, cogl_object_ref (snippet)); _cogl_snippet_make_immutable (snippet); } void _cogl_pipeline_snippet_list_copy (CoglPipelineSnippetList *dst, const CoglPipelineSnippetList *src) { GQueue queue = G_QUEUE_INIT; const GList *l; for (l = src->entries; l; l = l->next) g_queue_push_tail (&queue, cogl_object_ref (l->data)); dst->entries = queue.head; } void _cogl_pipeline_snippet_list_hash (CoglPipelineSnippetList *list, unsigned int *hash) { GList *l; for (l = list->entries; l; l = l->next) { CoglSnippet *snippet = l->data; *hash = _cogl_util_one_at_a_time_hash (*hash, &snippet, sizeof (CoglSnippet *)); } } gboolean _cogl_pipeline_snippet_list_equal (CoglPipelineSnippetList *list0, CoglPipelineSnippetList *list1) { GList *l0, *l1; for (l0 = list0->entries, l1 = list1->entries; l0 && l1; l0 = l0->next, l1 = l1->next) if (l0->data != l1->data) return FALSE; return l0 == NULL && l1 == NULL; } muffin-6.4.1/cogl/cogl/cogl-bitmap.h0000664000175000017500000002220214723361714016157 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_BITMAP_H__ #define __COGL_BITMAP_H__ /* XXX: We forward declare CoglBitmap here to allow for circular * dependencies between some headers */ typedef struct _CoglBitmap CoglBitmap; #include #include #include #include #include #include G_BEGIN_DECLS /** * cogl_bitmap_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_bitmap_get_gtype (void); /** * SECTION:cogl-bitmap * @short_description: Functions for loading images * * Cogl allows loading image data into memory as CoglBitmaps without * loading them immediately into GPU textures. * * #CoglBitmap is available since Cogl 1.0 */ /** * cogl_bitmap_new_from_file: * @filename: the file to load. * @error: a #GError or %NULL. * * Loads an image file from disk. This function can be safely called from * within a thread. * * Return value: (transfer full): a #CoglBitmap to the new loaded * image data, or %NULL if loading the image failed. * * Since: 1.0 */ COGL_EXPORT CoglBitmap * cogl_bitmap_new_from_file (const char *filename, GError **error); /** * cogl_bitmap_new_from_buffer: (skip) * @buffer: A #CoglBuffer containing image data * @format: The #CoglPixelFormat defining the format of the image data * in the given @buffer. * @width: The width of the image data in the given @buffer. * @height: The height of the image data in the given @buffer. * @rowstride: The rowstride in bytes of the image data in the given @buffer. * @offset: The offset into the given @buffer to the first pixel that * should be considered part of the #CoglBitmap. * * Wraps some image data that has been uploaded into a #CoglBuffer as * a #CoglBitmap. The data is not copied in this process. * * Return value: (transfer full): a #CoglBitmap encapsulating the given @buffer. * * Since: 1.8 * Stability: unstable */ COGL_EXPORT CoglBitmap * cogl_bitmap_new_from_buffer (CoglBuffer *buffer, CoglPixelFormat format, int width, int height, int rowstride, int offset); /** * cogl_bitmap_new_with_size: (skip) * @context: A #CoglContext * @width: width of the bitmap in pixels * @height: height of the bitmap in pixels * @format: the format of the pixels the array will store * * Creates a new #CoglBitmap with the given width, height and format. * The initial contents of the bitmap are undefined. * * The data for the bitmap will be stored in a newly created * #CoglPixelBuffer. You can get a pointer to the pixel buffer using * cogl_bitmap_get_buffer(). The #CoglBuffer API can then be * used to fill the bitmap with data. * * Cogl will try its best to provide a hardware array you can * map, write into and effectively do a zero copy upload when creating * a texture from it with cogl_texture_new_from_bitmap(). For various * reasons, such arrays are likely to have a stride larger than width * * bytes_per_pixel. The user must take the stride into account when * writing into it. The stride can be retrieved with * cogl_bitmap_get_rowstride(). * * Return value: (transfer full): a #CoglPixelBuffer representing the * newly created array or %NULL on failure * * Since: 1.10 * Stability: Unstable */ COGL_EXPORT CoglBitmap * cogl_bitmap_new_with_size (CoglContext *context, unsigned int width, unsigned int height, CoglPixelFormat format); /** * cogl_bitmap_new_for_data: (skip) * @context: A #CoglContext * @width: The width of the bitmap. * @height: The height of the bitmap. * @format: The format of the pixel data. * @rowstride: The rowstride of the bitmap (the number of bytes from * the start of one row of the bitmap to the next). * @data: A pointer to the data. The bitmap will take ownership of this data. * * Creates a bitmap using some existing data. The data is not copied * so the application must keep the buffer alive for the lifetime of * the #CoglBitmap. This can be used for example with * cogl_framebuffer_read_pixels_into_bitmap() to read data directly * into an application buffer with the specified rowstride. * * Return value: (transfer full): A new #CoglBitmap. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglBitmap * cogl_bitmap_new_for_data (CoglContext *context, int width, int height, CoglPixelFormat format, int rowstride, uint8_t *data); /** * cogl_bitmap_get_format: * @bitmap: A #CoglBitmap * * Return value: the #CoglPixelFormat that the data for the bitmap is in. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglPixelFormat cogl_bitmap_get_format (CoglBitmap *bitmap); /** * cogl_bitmap_get_width: * @bitmap: A #CoglBitmap * * Return value: the width of the bitmap * Since: 1.10 * Stability: unstable */ COGL_EXPORT int cogl_bitmap_get_width (CoglBitmap *bitmap); /** * cogl_bitmap_get_height: * @bitmap: A #CoglBitmap * * Return value: the height of the bitmap * Since: 1.10 * Stability: unstable */ COGL_EXPORT int cogl_bitmap_get_height (CoglBitmap *bitmap); /** * cogl_bitmap_get_rowstride: * @bitmap: A #CoglBitmap * * Return value: the rowstride of the bitmap. This is the number of * bytes between the address of start of one row to the address of the * next row in the image. * Since: 1.10 * Stability: unstable */ COGL_EXPORT int cogl_bitmap_get_rowstride (CoglBitmap *bitmap); /** * cogl_bitmap_get_buffer: (skip) * @bitmap: A #CoglBitmap * * Return value: (transfer none): the #CoglPixelBuffer that this * buffer uses for storage. Note that if the bitmap was created with * cogl_bitmap_new_from_file() then it will not actually be using a * pixel buffer and this function will return %NULL. * Stability: unstable * Since: 1.10 */ COGL_EXPORT CoglPixelBuffer * cogl_bitmap_get_buffer (CoglBitmap *bitmap); /** * cogl_bitmap_get_size_from_file: * @filename: the file to check * @width: (out): return location for the bitmap width, or %NULL * @height: (out): return location for the bitmap height, or %NULL * * Parses an image file enough to extract the width and height * of the bitmap. * * Return value: %TRUE if the image was successfully parsed * * Since: 1.0 */ COGL_EXPORT gboolean cogl_bitmap_get_size_from_file (const char *filename, int *width, int *height); /** * cogl_is_bitmap: * @object: a #CoglObject pointer * * Checks whether @object is a #CoglBitmap * * Return value: %TRUE if the passed @object represents a bitmap, * and %FALSE otherwise * * Since: 1.0 */ COGL_EXPORT gboolean cogl_is_bitmap (void *object); /** * COGL_BITMAP_ERROR: * * #GError domain for bitmap errors. * * Since: 1.4 */ #define COGL_BITMAP_ERROR (cogl_bitmap_error_quark ()) /** * CoglBitmapError: * @COGL_BITMAP_ERROR_FAILED: Generic failure code, something went * wrong. * @COGL_BITMAP_ERROR_UNKNOWN_TYPE: Unknown image type. * @COGL_BITMAP_ERROR_CORRUPT_IMAGE: An image file was broken somehow. * * Error codes that can be thrown when performing bitmap * operations. Note that gdk_pixbuf_new_from_file() can also throw * errors directly from the underlying image loading library. For * example, if GdkPixbuf is used then errors #GdkPixbufErrors * will be used directly. * * Since: 1.4 */ typedef enum { COGL_BITMAP_ERROR_FAILED, COGL_BITMAP_ERROR_UNKNOWN_TYPE, COGL_BITMAP_ERROR_CORRUPT_IMAGE } CoglBitmapError; COGL_EXPORT uint32_t cogl_bitmap_error_quark (void); G_END_DECLS #endif /* __COGL_BITMAP_H__ */ muffin-6.4.1/cogl/cogl/cogl-spans.h0000664000175000017500000000436314723361714016037 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_SPANS_PRIVATE_H #define __COGL_SPANS_PRIVATE_H #include "cogl-object-private.h" #include "cogl-pipeline-layer-state.h" typedef struct _CoglSpan { float start; float size; float waste; } CoglSpan; typedef struct _CoglSpanIter { int index; const CoglSpan *spans; int n_spans; const CoglSpan *span; float pos; float next_pos; float origin; float cover_start; float cover_end; float intersect_start; float intersect_end; gboolean intersects; gboolean flipped; CoglPipelineWrapMode wrap_mode; int mirror_direction; } CoglSpanIter; void _cogl_span_iter_update (CoglSpanIter *iter); void _cogl_span_iter_begin (CoglSpanIter *iter, const CoglSpan *spans, int n_spans, float normalize_factor, float cover_start, float cover_end, CoglPipelineWrapMode wrap_mode); void _cogl_span_iter_next (CoglSpanIter *iter); gboolean _cogl_span_iter_end (CoglSpanIter *iter); #endif /* __COGL_SPANS_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-output.h0000664000175000017500000001655314723361714016257 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Owen Taylor */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_OUTPUT_H #define __COGL_OUTPUT_H #include #include G_BEGIN_DECLS /** * SECTION:cogl-output * @short_description: information about an output device * * The #CoglOutput object holds information about an output device * such as a monitor or laptop display. It can be queried to find * out the position of the output with respect to the screen * coordinate system and other information such as the resolution * and refresh rate of the device. * * There can be any number of outputs which may overlap: the * same area of the screen may be displayed by multiple output * devices. * * XXX: though it's possible to query the position of the output * with respect to screen coordinates, there is currently no way * of finding out the position of a #CoglOnscreen in screen * coordinates, at least without using windowing-system specific * API's, so it's not easy to get the output positions relative * to the #CoglOnscreen. */ typedef struct _CoglOutput CoglOutput; #define COGL_OUTPUT(X) ((CoglOutput *)(X)) /** * cogl_output_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_output_get_gtype (void); /** * CoglSubpixelOrder: * @COGL_SUBPIXEL_ORDER_UNKNOWN: the layout of subpixel * components for the device is unknown. * @COGL_SUBPIXEL_ORDER_NONE: the device displays colors * without geometrically-separated subpixel components, * or the positioning or colors of the components do not * match any of the values in the enumeration. * @COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: the device has * horizontally arranged components in the order * red-green-blue from left to right. * @COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: the device has * horizontally arranged components in the order * blue-green-red from left to right. * @COGL_SUBPIXEL_ORDER_VERTICAL_RGB: the device has * vertically arranged components in the order * red-green-blue from top to bottom. * @COGL_SUBPIXEL_ORDER_VERTICAL_BGR: the device has * vertically arranged components in the order * blue-green-red from top to bottom. * * Some output devices (such as LCD panels) display colors * by making each pixel consist of smaller "subpixels" * that each have a particular color. By using knowledge * of the layout of this subpixel components, it is possible * to create image content with higher resolution than the * pixel grid. * * Since: 1.14 * Stability: unstable */ typedef enum { COGL_SUBPIXEL_ORDER_UNKNOWN, COGL_SUBPIXEL_ORDER_NONE, COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB, COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR, COGL_SUBPIXEL_ORDER_VERTICAL_RGB, COGL_SUBPIXEL_ORDER_VERTICAL_BGR } CoglSubpixelOrder; /** * cogl_is_output: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglOutput. * * Return value: %TRUE if the object references a #CoglOutput * and %FALSE otherwise. * Since: 1.14 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_output (void *object); /** * cogl_output_get_x: * @output: a #CoglOutput * * Gets the X position of the output with respect to the coordinate * system of the screen. * * Return value: the X position of the output as a pixel offset * from the left side of the screen coordinate space * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_x (CoglOutput *output); /** * cogl_output_get_y: * @output: a #CoglOutput * * Gets the Y position of the output with respect to the coordinate * system of the screen. * * Return value: the Y position of the output as a pixel offset * from the top side of the screen coordinate space * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_y (CoglOutput *output); /** * cogl_output_get_width: * @output: a #CoglOutput * * Gets the width of the output in pixels. * * Return value: the width of the output in pixels * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_width (CoglOutput *output); /** * cogl_output_get_height: * @output: a #CoglOutput * * Gets the height of the output in pixels. * * Return value: the height of the output in pixels * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_height (CoglOutput *output); /** * cogl_output_get_mm_width: * @output: a #CoglOutput * * Gets the physical width of the output. In some cases (such as * as a projector), the value returned here might correspond to * nominal resolution rather than the actual physical size of the * output device. * * Return value: the height of the output in millimeters. A value * of 0 indicates the width is unknown * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_mm_width (CoglOutput *output); /** * cogl_output_get_mm_height: * @output: a #CoglOutput * * Gets the physical height of the output. In some cases (such as * as a projector), the value returned here might correspond to * nominal resolution rather than the actual physical size of the * output device. * * Return value: the height of the output in millimeters. A value * of 0 indicates that the height is unknown * Since: 1.14 * Stability: unstable */ COGL_EXPORT int cogl_output_get_mm_height (CoglOutput *output); /** * cogl_output_get_subpixel_order: * @output: a #CoglOutput * * For an output device where each pixel is made up of smaller components * with different colors, returns the layout of the subpixel * components. * * Return value: the order of subpixel components for the output device * Since: 1.14 * Stability: unstable */ COGL_EXPORT CoglSubpixelOrder cogl_output_get_subpixel_order (CoglOutput *output); /** * cogl_output_get_refresh_rate: * @output: a #CoglOutput * * Gets the number of times per second that the output device refreshes * the display contents. * * Return value: the refresh rate of the output device. A value of zero * indicates that the refresh rate is unknown. * Since: 1.14 * Stability: unstable */ COGL_EXPORT float cogl_output_get_refresh_rate (CoglOutput *output); G_END_DECLS #endif /* __COGL_OUTPUT_H */ muffin-6.4.1/cogl/cogl/cogl-pipeline-layer-private.h0000664000175000017500000003110014723361714021267 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PIPELINE_LAYER_PRIVATE_H #define __COGL_PIPELINE_LAYER_PRIVATE_H #include "cogl-private.h" #include "cogl-pipeline.h" #include "cogl-node-private.h" #include "cogl-texture.h" #include "cogl-matrix.h" #include "cogl-pipeline-layer-state.h" #include "cogl-pipeline-snippet-private.h" #include "cogl-sampler-cache-private.h" #include typedef struct _CoglPipelineLayer CoglPipelineLayer; #define COGL_PIPELINE_LAYER(OBJECT) ((CoglPipelineLayer *)OBJECT) /* XXX: should I rename these as * COGL_PIPELINE_LAYER_STATE_INDEX_XYZ... ? */ typedef enum { /* sparse state */ COGL_PIPELINE_LAYER_STATE_UNIT_INDEX, COGL_PIPELINE_LAYER_STATE_TEXTURE_DATA_INDEX, COGL_PIPELINE_LAYER_STATE_SAMPLER_INDEX, COGL_PIPELINE_LAYER_STATE_COMBINE_INDEX, COGL_PIPELINE_LAYER_STATE_COMBINE_CONSTANT_INDEX, COGL_PIPELINE_LAYER_STATE_USER_MATRIX_INDEX, COGL_PIPELINE_LAYER_STATE_POINT_SPRITE_COORDS_INDEX, COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS_INDEX, COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS_INDEX, /* note: layers don't currently have any non-sparse state */ COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT, COGL_PIPELINE_LAYER_STATE_COUNT = COGL_PIPELINE_LAYER_STATE_SPARSE_COUNT } CoglPipelineLayerStateIndex; /* XXX: If you add or remove state groups here you may need to update * some of the state masks following this enum too! * * FIXME: perhaps it would be better to rename this enum to * CoglPipelineLayerStateGroup to better convey the fact that a single * enum here can map to multiple properties. */ typedef enum { COGL_PIPELINE_LAYER_STATE_UNIT = 1L< TEXTURE0 to store very large layer numbers */ COGL_PIPELINE_COMBINE_SOURCE_TEXTURE, COGL_PIPELINE_COMBINE_SOURCE_CONSTANT, COGL_PIPELINE_COMBINE_SOURCE_PRIMARY_COLOR, COGL_PIPELINE_COMBINE_SOURCE_PREVIOUS, COGL_PIPELINE_COMBINE_SOURCE_TEXTURE0 } CoglPipelineCombineSource; typedef enum { /* These are the same values as GL */ COGL_PIPELINE_COMBINE_OP_SRC_COLOR = 0x0300, COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_COLOR = 0x0301, COGL_PIPELINE_COMBINE_OP_SRC_ALPHA = 0x0302, COGL_PIPELINE_COMBINE_OP_ONE_MINUS_SRC_ALPHA = 0x0303 } CoglPipelineCombineOp; typedef struct { /* The texture combine state determines how the color of individual * texture fragments are calculated. */ CoglPipelineCombineFunc texture_combine_rgb_func; CoglPipelineCombineSource texture_combine_rgb_src[3]; CoglPipelineCombineOp texture_combine_rgb_op[3]; CoglPipelineCombineFunc texture_combine_alpha_func; CoglPipelineCombineSource texture_combine_alpha_src[3]; CoglPipelineCombineOp texture_combine_alpha_op[3]; float texture_combine_constant[4]; /* The texture matrix dscribes how to transform texture coordinates */ CoglMatrix matrix; CoglPipelineSnippetList vertex_snippets; CoglPipelineSnippetList fragment_snippets; gboolean point_sprite_coords; } CoglPipelineLayerBigState; struct _CoglPipelineLayer { /* XXX: Please think twice about adding members that *have* be * initialized during a _cogl_pipeline_layer_copy. We are aiming * to have copies be as cheap as possible and copies may be * done by the primitives APIs which means they may happen * in performance critical code paths. * * XXX: If you are extending the state we track please consider if * the state is expected to vary frequently across many pipelines or * if the state can be shared among many derived pipelines instead. * This will determine if the state should be added directly to this * structure which will increase the memory overhead for *all* * layers or if instead it can go under ->big_state. */ /* Layers represent their state in a tree structure where some of * the state relating to a given pipeline or layer may actually be * owned by one if is ancestors in the tree. We have a common data * type to track the tree heirachy so we can share code... */ CoglNode _parent; /* Some layers have a pipeline owner, which is to say that the layer * is referenced in that pipelines->layer_differences list. A layer * doesn't always have an owner and may simply be an ancestor for * other layers that keeps track of some shared state. */ CoglPipeline *owner; /* The lowest index is blended first then others on top */ int index; /* A mask of which state groups are different in this layer * in comparison to its parent. */ unsigned int differences; /* Common differences * * As a basic way to reduce memory usage we divide the layer * state into two groups; the minimal state modified in 90% of * all layers and the rest, so that the second group can * be allocated dynamically when required. */ /* Each layer is directly associated with a single texture unit */ int unit_index; /* The texture for this layer, or NULL for an empty * layer */ CoglTexture *texture; const CoglSamplerCacheEntry *sampler_cache_entry; /* Infrequent differences aren't currently tracked in * a separate, dynamically allocated structure as they are * for pipelines... */ CoglPipelineLayerBigState *big_state; /* bitfields */ /* Determines if layer->big_state is valid */ unsigned int has_big_state:1; }; typedef gboolean (*CoglPipelineLayerStateComparitor) (CoglPipelineLayer *authority0, CoglPipelineLayer *authority1); void _cogl_pipeline_init_default_layers (void); static inline CoglPipelineLayer * _cogl_pipeline_layer_get_parent (CoglPipelineLayer *layer) { CoglNode *parent_node = COGL_NODE (layer)->parent; return COGL_PIPELINE_LAYER (parent_node); } CoglPipelineLayer * _cogl_pipeline_layer_copy (CoglPipelineLayer *layer); void _cogl_pipeline_layer_resolve_authorities (CoglPipelineLayer *layer, unsigned long differences, CoglPipelineLayer **authorities); gboolean _cogl_pipeline_layer_equal (CoglPipelineLayer *layer0, CoglPipelineLayer *layer1, unsigned long differences_mask, CoglPipelineEvalFlags flags); CoglPipelineLayer * _cogl_pipeline_layer_pre_change_notify (CoglPipeline *required_owner, CoglPipelineLayer *layer, CoglPipelineLayerState change); void _cogl_pipeline_layer_prune_redundant_ancestry (CoglPipelineLayer *layer); gboolean _cogl_pipeline_layer_has_alpha (CoglPipelineLayer *layer); gboolean _cogl_pipeline_layer_has_user_matrix (CoglPipeline *pipeline, int layer_index); /* * Calls the pre_paint method on the layer texture if there is * one. This will determine whether mipmaps are needed based on the * filter settings. */ void _cogl_pipeline_layer_pre_paint (CoglPipelineLayer *layerr); void _cogl_pipeline_layer_get_wrap_modes (CoglPipelineLayer *layer, CoglSamplerCacheWrapMode *wrap_mode_s, CoglSamplerCacheWrapMode *wrap_mode_t); void _cogl_pipeline_layer_get_filters (CoglPipelineLayer *layer, CoglPipelineFilter *min_filter, CoglPipelineFilter *mag_filter); const CoglSamplerCacheEntry * _cogl_pipeline_layer_get_sampler_state (CoglPipelineLayer *layer); void _cogl_pipeline_get_layer_filters (CoglPipeline *pipeline, int layer_index, CoglPipelineFilter *min_filter, CoglPipelineFilter *mag_filter); typedef enum { COGL_PIPELINE_LAYER_TYPE_TEXTURE } CoglPipelineLayerType; CoglPipelineLayerType _cogl_pipeline_layer_get_type (CoglPipelineLayer *layer); COGL_EXPORT CoglTexture * _cogl_pipeline_layer_get_texture (CoglPipelineLayer *layer); CoglTexture * _cogl_pipeline_layer_get_texture_real (CoglPipelineLayer *layer); CoglPipelineFilter _cogl_pipeline_layer_get_min_filter (CoglPipelineLayer *layer); CoglPipelineFilter _cogl_pipeline_layer_get_mag_filter (CoglPipelineLayer *layer); CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_s (CoglPipelineLayer *layer); CoglPipelineWrapMode _cogl_pipeline_layer_get_wrap_mode_t (CoglPipelineLayer *layer); void _cogl_pipeline_layer_copy_differences (CoglPipelineLayer *dest, CoglPipelineLayer *src, unsigned long differences); unsigned long _cogl_pipeline_layer_compare_differences (CoglPipelineLayer *layer0, CoglPipelineLayer *layer1); CoglPipelineLayer * _cogl_pipeline_layer_get_authority (CoglPipelineLayer *layer, unsigned long difference); int _cogl_pipeline_layer_get_unit_index (CoglPipelineLayer *layer); gboolean _cogl_pipeline_layer_needs_combine_separate (CoglPipelineLayer *combine_authority); #endif /* __COGL_PIPELINE_LAYER_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl.c0000664000175000017500000001304214723361714014702 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include #include #include "cogl-i18n-private.h" #include "cogl-debug.h" #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-pipeline-private.h" #include "cogl-framebuffer-private.h" #include "cogl-matrix-private.h" #include "cogl-journal-private.h" #include "cogl-bitmap-private.h" #include "cogl-texture-private.h" #include "cogl-texture-driver.h" #include "cogl-attribute-private.h" #include "cogl-framebuffer-private.h" #include "cogl-renderer-private.h" #include "cogl-private.h" #include "cogl1-context.h" #include "cogl-offscreen.h" #include "winsys/cogl-winsys-private.h" GCallback cogl_get_proc_address (const char* name) { _COGL_GET_CONTEXT (ctx, NULL); return _cogl_renderer_get_proc_address (ctx->display->renderer, name, FALSE); } gboolean _cogl_check_extension (const char *name, char * const *ext) { while (*ext) if (!strcmp (name, *ext)) return TRUE; else ext++; return FALSE; } /* XXX: This API has been deprecated */ void cogl_set_depth_test_enabled (gboolean setting) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->legacy_depth_test_enabled == setting) return; ctx->legacy_depth_test_enabled = setting; } /* XXX: This API has been deprecated */ gboolean cogl_get_depth_test_enabled (void) { _COGL_GET_CONTEXT (ctx, FALSE); return ctx->legacy_depth_test_enabled; } void cogl_set_backface_culling_enabled (gboolean setting) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (ctx->legacy_backface_culling_enabled == setting) return; ctx->legacy_backface_culling_enabled = setting; } gboolean cogl_get_backface_culling_enabled (void) { _COGL_GET_CONTEXT (ctx, FALSE); return ctx->legacy_backface_culling_enabled; } gboolean cogl_has_feature (CoglContext *ctx, CoglFeatureID feature) { return COGL_FLAGS_GET (ctx->features, feature); } gboolean cogl_has_features (CoglContext *ctx, ...) { va_list args; CoglFeatureID feature; va_start (args, ctx); while ((feature = va_arg (args, CoglFeatureID))) if (!cogl_has_feature (ctx, feature)) return FALSE; va_end (args); return TRUE; } void cogl_foreach_feature (CoglContext *ctx, CoglFeatureCallback callback, void *user_data) { int i; for (i = 0; i < _COGL_N_FEATURE_IDS; i++) if (COGL_FLAGS_GET (ctx->features, i)) callback (i, user_data); } void cogl_flush (void) { GList *l; _COGL_GET_CONTEXT (ctx, NO_RETVAL); for (l = ctx->framebuffers; l; l = l->next) _cogl_framebuffer_flush_journal (l->data); } uint32_t _cogl_driver_error_quark (void) { return g_quark_from_static_string ("cogl-driver-error-quark"); } /* Scale from OpenGL normalized device coordinates (ranging from -1 to 1) * to Cogl window/framebuffer coordinates (ranging from 0 to buffer-size) with * (0,0) being top left. */ #define VIEWPORT_TRANSFORM_X(x, vp_origin_x, vp_width) \ ( ( ((x) + 1.0) * ((vp_width) / 2.0) ) + (vp_origin_x) ) /* Note: for Y we first flip all coordinates around the X axis while in * normalized device coodinates */ #define VIEWPORT_TRANSFORM_Y(y, vp_origin_y, vp_height) \ ( ( ((-(y)) + 1.0) * ((vp_height) / 2.0) ) + (vp_origin_y) ) /* Transform a homogeneous vertex position from model space to Cogl * window coordinates (with 0,0 being top left) */ void _cogl_transform_point (const CoglMatrix *matrix_mv, const CoglMatrix *matrix_p, const float *viewport, float *x, float *y) { float z = 0; float w = 1; /* Apply the modelview matrix transform */ cogl_matrix_transform_point (matrix_mv, x, y, &z, &w); /* Apply the projection matrix transform */ cogl_matrix_transform_point (matrix_p, x, y, &z, &w); /* Perform perspective division */ *x /= w; *y /= w; /* Apply viewport transform */ *x = VIEWPORT_TRANSFORM_X (*x, viewport[0], viewport[2]); *y = VIEWPORT_TRANSFORM_Y (*y, viewport[1], viewport[3]); } #undef VIEWPORT_TRANSFORM_X #undef VIEWPORT_TRANSFORM_Y uint32_t _cogl_system_error_quark (void) { return g_quark_from_static_string ("cogl-system-error-quark"); } void _cogl_init (void) { static gboolean initialized = FALSE; if (initialized == FALSE) { #if !GLIB_CHECK_VERSION (2, 36, 0) g_type_init (); #endif _cogl_debug_check_environment (); initialized = TRUE; } } muffin-6.4.1/cogl/cogl/cogl-frame-info.h0000664000175000017500000001046614723361714016737 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Owen Taylor */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_FRAME_INFO_H #define __COGL_FRAME_INFO_H #include #include #include #include G_BEGIN_DECLS typedef struct _CoglFrameInfo CoglFrameInfo; #define COGL_FRAME_INFO(X) ((CoglFrameInfo *)(X)) /** * cogl_frame_info_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_frame_info_get_gtype (void); /** * cogl_is_frame_info: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglFrameInfo. * * Return value: %TRUE if the object references a #CoglFrameInfo * and %FALSE otherwise. * Since: 2.0 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_frame_info (void *object); /** * cogl_frame_info_get_frame_counter: * @info: a #CoglFrameInfo object * * Gets the frame counter for the #CoglOnscreen that corresponds * to this frame. * * Return value: The frame counter value * Since: 1.14 * Stability: unstable */ COGL_EXPORT int64_t cogl_frame_info_get_frame_counter (CoglFrameInfo *info); /** * cogl_frame_info_get_presentation_time: * @info: a #CoglFrameInfo object * * Gets the presentation time for the frame. This is the time at which * the frame became visible to the user. * * The presentation time measured in nanoseconds, is based on * cogl_get_clock_time(). * * Linux kernel version less that 3.8 can result in * non-monotonic timestamps being reported when using a drm based * OpenGL driver. Also some buggy Mesa drivers up to 9.0.1 may also * incorrectly report non-monotonic timestamps. * * Return value: the presentation time for the frame * Since: 1.14 * Stability: unstable */ COGL_EXPORT int64_t cogl_frame_info_get_presentation_time (CoglFrameInfo *info); /** * cogl_frame_info_get_refresh_rate: * @info: a #CoglFrameInfo object * * Gets the refresh rate in Hertz for the output that the frame was on * at the time the frame was presented. * * Some platforms can't associate a #CoglOutput with a * #CoglFrameInfo object but are able to report a refresh rate via * this api. Therefore if you need this information then this api is * more reliable than using cogl_frame_info_get_output() followed by * cogl_output_get_refresh_rate(). * * Return value: the refresh rate in Hertz * Since: 1.14 * Stability: unstable */ COGL_EXPORT float cogl_frame_info_get_refresh_rate (CoglFrameInfo *info); /** * cogl_frame_info_get_output: * @info: a #CoglFrameInfo object * * Gets the #CoglOutput that the swapped frame was presented to. * * Return value: (transfer none): The #CoglOutput that the frame was * presented to, or %NULL if this could not be determined. * Since: 1.14 * Stability: unstable */ COGL_EXPORT CoglOutput * cogl_frame_info_get_output (CoglFrameInfo *info); /** * cogl_frame_info_get_global_frame_counter: (skip) */ COGL_EXPORT int64_t cogl_frame_info_get_global_frame_counter (CoglFrameInfo *info); G_END_DECLS #endif /* __COGL_FRAME_INFO_H */ muffin-6.4.1/cogl/cogl/cogl-atlas-texture.c0000664000175000017500000010777714723361714017525 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-debug.h" #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-atlas-texture-private.h" #include "cogl-texture-2d-private.h" #include "cogl-sub-texture-private.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-texture-driver.h" #include "cogl-rectangle-map.h" #include "cogl-journal-private.h" #include "cogl-atlas.h" #include "cogl1-context.h" #include "cogl-sub-texture.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-pipeline-opengl-private.h" #include "driver/gl/cogl-texture-gl-private.h" #include static void _cogl_atlas_texture_free (CoglAtlasTexture *sub_tex); COGL_TEXTURE_DEFINE (AtlasTexture, atlas_texture); COGL_GTYPE_DEFINE_CLASS (AtlasTexture, atlas_texture); static const CoglTextureVtable cogl_atlas_texture_vtable; static CoglSubTexture * _cogl_atlas_texture_create_sub_texture (CoglTexture *full_texture, const CoglRectangleMapEntry *rectangle) { CoglContext *ctx = full_texture->context; /* Create a subtexture for the given rectangle not including the 1-pixel border */ return cogl_sub_texture_new (ctx, full_texture, rectangle->x + 1, rectangle->y + 1, rectangle->width - 2, rectangle->height - 2); } static void _cogl_atlas_texture_update_position_cb (void *user_data, CoglTexture *new_texture, const CoglRectangleMapEntry *rectangle) { CoglAtlasTexture *atlas_tex = user_data; /* Update the sub texture */ if (atlas_tex->sub_texture) cogl_object_unref (atlas_tex->sub_texture); atlas_tex->sub_texture = COGL_TEXTURE ( _cogl_atlas_texture_create_sub_texture (new_texture, rectangle)); /* Update the position */ atlas_tex->rectangle = *rectangle; } static void _cogl_atlas_texture_pre_reorganize_foreach_cb (const CoglRectangleMapEntry *entry, void *rectangle_data, void *user_data) { CoglAtlasTexture *atlas_tex = rectangle_data; /* Keep a reference to the texture because we don't want it to be destroyed during the reorganization */ cogl_object_ref (atlas_tex); /* Notify cogl-pipeline.c that the texture's underlying GL texture * storage is changing so it knows it may need to bind a new texture * if the CoglTexture is reused with the same texture unit. */ _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex)); } static void _cogl_atlas_texture_pre_reorganize_cb (void *data) { CoglAtlas *atlas = data; /* We don't know if any journal entries currently depend on OpenGL * texture coordinates that would be invalidated by reorganizing * this atlas so we flush all journals before migrating. * * We are assuming that texture atlas migration never happens * during a flush so we don't have to consider recursion here. */ cogl_flush (); if (atlas->map) _cogl_rectangle_map_foreach (atlas->map, _cogl_atlas_texture_pre_reorganize_foreach_cb, NULL); } typedef struct { CoglAtlasTexture **textures; /* Number of textures found so far */ unsigned int n_textures; } CoglAtlasTextureGetRectanglesData; static void _cogl_atlas_texture_get_rectangles_cb (const CoglRectangleMapEntry *entry, void *rectangle_data, void *user_data) { CoglAtlasTextureGetRectanglesData *data = user_data; data->textures[data->n_textures++] = rectangle_data; } static void _cogl_atlas_texture_post_reorganize_cb (void *user_data) { CoglAtlas *atlas = user_data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (atlas->map) { CoglAtlasTextureGetRectanglesData data; unsigned int i; data.textures = g_new (CoglAtlasTexture *, _cogl_rectangle_map_get_n_rectangles (atlas->map)); data.n_textures = 0; /* We need to remove all of the references that we took during the preorganize callback. We have to get a separate array of the textures because CoglRectangleMap doesn't support removing rectangles during iteration */ _cogl_rectangle_map_foreach (atlas->map, _cogl_atlas_texture_get_rectangles_cb, &data); for (i = 0; i < data.n_textures; i++) { /* Ignore textures that don't have an atlas yet. This will happen when a new texture is added because we allocate the structure for the texture so that it can get stored in the atlas but it isn't a valid object yet */ if (data.textures[i]->atlas) cogl_object_unref (data.textures[i]); } g_free (data.textures); } /* Notify any listeners that an atlas has changed */ g_hook_list_invoke (&ctx->atlas_reorganize_callbacks, FALSE); } static void _cogl_atlas_texture_atlas_destroyed_cb (void *user_data) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); /* Remove the atlas from the global list */ ctx->atlases = g_slist_remove (ctx->atlases, user_data); } static CoglAtlas * _cogl_atlas_texture_create_atlas (CoglContext *ctx) { static CoglUserDataKey atlas_private_key; CoglAtlas *atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_RGBA_8888, 0, _cogl_atlas_texture_update_position_cb); _cogl_atlas_add_reorganize_callback (atlas, _cogl_atlas_texture_pre_reorganize_cb, _cogl_atlas_texture_post_reorganize_cb, atlas); ctx->atlases = g_slist_prepend (ctx->atlases, atlas); /* Set some data on the atlas so we can get notification when it is destroyed in order to remove it from the list. ctx->atlases effectively holds a weak reference. We don't need a strong reference because the atlas textures take a reference on the atlas so it will stay alive */ cogl_object_set_user_data (COGL_OBJECT (atlas), &atlas_private_key, atlas, _cogl_atlas_texture_atlas_destroyed_cb); return atlas; } static void _cogl_atlas_texture_foreach_sub_texture_in_region ( CoglTexture *tex, float virtual_tx_1, float virtual_ty_1, float virtual_tx_2, float virtual_ty_2, CoglMetaTextureCallback callback, void *user_data) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); CoglMetaTexture *meta_texture = COGL_META_TEXTURE (atlas_tex->sub_texture); /* Forward on to the sub texture */ cogl_meta_texture_foreach_in_region (meta_texture, virtual_tx_1, virtual_ty_1, virtual_tx_2, virtual_ty_2, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, callback, user_data); } static void _cogl_atlas_texture_gl_flush_legacy_texobj_wrap_modes (CoglTexture *tex, GLenum wrap_mode_s, GLenum wrap_mode_t) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ _cogl_texture_gl_flush_legacy_texobj_wrap_modes (atlas_tex->sub_texture, wrap_mode_s, wrap_mode_t); } static void _cogl_atlas_texture_remove_from_atlas (CoglAtlasTexture *atlas_tex) { if (atlas_tex->atlas) { _cogl_atlas_remove (atlas_tex->atlas, &atlas_tex->rectangle); cogl_object_unref (atlas_tex->atlas); atlas_tex->atlas = NULL; } } static void _cogl_atlas_texture_free (CoglAtlasTexture *atlas_tex) { _cogl_atlas_texture_remove_from_atlas (atlas_tex); if (atlas_tex->sub_texture) cogl_object_unref (atlas_tex->sub_texture); /* Chain up */ _cogl_texture_free (COGL_TEXTURE (atlas_tex)); } static int _cogl_atlas_texture_get_max_waste (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return cogl_texture_get_max_waste (atlas_tex->sub_texture); } static gboolean _cogl_atlas_texture_is_sliced (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return cogl_texture_is_sliced (atlas_tex->sub_texture); } static gboolean _cogl_atlas_texture_can_hardware_repeat (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return _cogl_texture_can_hardware_repeat (atlas_tex->sub_texture); } static void _cogl_atlas_texture_transform_coords_to_gl (CoglTexture *tex, float *s, float *t) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ _cogl_texture_transform_coords_to_gl (atlas_tex->sub_texture, s, t); } static CoglTransformResult _cogl_atlas_texture_transform_quad_coords_to_gl (CoglTexture *tex, float *coords) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return _cogl_texture_transform_quad_coords_to_gl (atlas_tex->sub_texture, coords); } static gboolean _cogl_atlas_texture_get_gl_texture (CoglTexture *tex, GLuint *out_gl_handle, GLenum *out_gl_target) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return cogl_texture_get_gl_texture (atlas_tex->sub_texture, out_gl_handle, out_gl_target); } static void _cogl_atlas_texture_gl_flush_legacy_texobj_filters (CoglTexture *tex, GLenum min_filter, GLenum mag_filter) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ _cogl_texture_gl_flush_legacy_texobj_filters (atlas_tex->sub_texture, min_filter, mag_filter); } static void _cogl_atlas_texture_migrate_out_of_atlas (CoglAtlasTexture *atlas_tex) { CoglTexture *standalone_tex; /* Make sure this texture is not in the atlas */ if (!atlas_tex->atlas) return; COGL_NOTE (ATLAS, "Migrating texture out of the atlas"); /* We don't know if any journal entries currently depend on * OpenGL texture coordinates that would be invalidated by * migrating textures in this atlas so we flush all journals * before migrating. * * We are assuming that texture atlas migration never happens * during a flush so we don't have to consider recursion here. */ cogl_flush (); standalone_tex = _cogl_atlas_copy_rectangle (atlas_tex->atlas, atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + 1, atlas_tex->rectangle.width - 2, atlas_tex->rectangle.height - 2, atlas_tex->internal_format); /* Note: we simply silently ignore failures to migrate a texture * out (most likely due to lack of memory) and hope for the * best. * * Maybe we should find a way to report the problem back to the * app. */ if (!standalone_tex) return; /* Notify cogl-pipeline.c that the texture's underlying GL texture * storage is changing so it knows it may need to bind a new texture * if the CoglTexture is reused with the same texture unit. */ _cogl_pipeline_texture_storage_change_notify (COGL_TEXTURE (atlas_tex)); /* We need to unref the sub texture after doing the copy because the copy can involve rendering which might cause the texture to be used if it is used from a layer that is left in a texture unit */ cogl_object_unref (atlas_tex->sub_texture); atlas_tex->sub_texture = standalone_tex; _cogl_atlas_texture_remove_from_atlas (atlas_tex); } static void _cogl_atlas_texture_pre_paint (CoglTexture *tex, CoglTexturePrePaintFlags flags) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); if ((flags & COGL_TEXTURE_NEEDS_MIPMAP)) /* Mipmaps do not work well with the current atlas so instead we'll just migrate the texture out and use a regular texture */ _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); /* Forward on to the sub texture */ _cogl_texture_pre_paint (atlas_tex->sub_texture, flags); } static void _cogl_atlas_texture_ensure_non_quad_rendering (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Sub textures can't support non-quad rendering so we'll just migrate the texture out */ _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); /* Forward on to the sub texture */ _cogl_texture_ensure_non_quad_rendering (atlas_tex->sub_texture); } static gboolean _cogl_atlas_texture_set_region_with_border (CoglAtlasTexture *atlas_tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, CoglBitmap *bmp, GError **error) { CoglAtlas *atlas = atlas_tex->atlas; /* Copy the central data */ if (!_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, dst_width, dst_height, bmp, dst_x + atlas_tex->rectangle.x + 1, dst_y + atlas_tex->rectangle.y + 1, 0, /* level 0 */ error)) return FALSE; /* Update the left edge pixels */ if (dst_x == 0 && !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, 1, dst_height, bmp, atlas_tex->rectangle.x, dst_y + atlas_tex->rectangle.y + 1, 0, /* level 0 */ error)) return FALSE; /* Update the right edge pixels */ if (dst_x + dst_width == atlas_tex->rectangle.width - 2 && !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x + dst_width - 1, src_y, 1, dst_height, bmp, atlas_tex->rectangle.x + atlas_tex->rectangle.width - 1, dst_y + atlas_tex->rectangle.y + 1, 0, /* level 0 */ error)) return FALSE; /* Update the top edge pixels */ if (dst_y == 0 && !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y, dst_width, 1, bmp, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y, 0, /* level 0 */ error)) return FALSE; /* Update the bottom edge pixels */ if (dst_y + dst_height == atlas_tex->rectangle.height - 2 && !_cogl_texture_set_region_from_bitmap (atlas->texture, src_x, src_y + dst_height - 1, dst_width, 1, bmp, dst_x + atlas_tex->rectangle.x + 1, atlas_tex->rectangle.y + atlas_tex->rectangle.height - 1, 0, /* level 0 */ error)) return FALSE; return TRUE; } static CoglBitmap * _cogl_atlas_texture_convert_bitmap_for_upload (CoglAtlasTexture *atlas_tex, CoglBitmap *bmp, CoglPixelFormat internal_format, gboolean can_convert_in_place, GError **error) { CoglBitmap *upload_bmp; CoglBitmap *override_bmp; /* We'll prepare to upload using the format of the actual texture of the atlas texture instead of the format reported by _cogl_texture_get_format which would be the original internal format specified when the texture was created. However we'll preserve the premult status of the internal format because the images are all stored in the original premult format of the orignal format so we do need to trigger the conversion */ internal_format = (COGL_PIXEL_FORMAT_RGBA_8888 | (internal_format & COGL_PREMULT_BIT)); upload_bmp = _cogl_bitmap_convert_for_upload (bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return NULL; /* We'll create another bitmap which uses the same data but overrides the format to remove the premult flag so that uploads to the atlas texture won't trigger the conversion again */ override_bmp = _cogl_bitmap_new_shared (upload_bmp, cogl_bitmap_get_format (upload_bmp) & ~COGL_PREMULT_BIT, cogl_bitmap_get_width (upload_bmp), cogl_bitmap_get_height (upload_bmp), cogl_bitmap_get_rowstride (upload_bmp)); cogl_object_unref (upload_bmp); return override_bmp; } static gboolean _cogl_atlas_texture_set_region (CoglTexture *tex, int src_x, int src_y, int dst_x, int dst_y, int dst_width, int dst_height, int level, CoglBitmap *bmp, GError **error) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); if (level != 0 && atlas_tex->atlas) _cogl_atlas_texture_migrate_out_of_atlas (atlas_tex); /* If the texture is in the atlas then we need to copy the edge pixels to the border */ if (atlas_tex->atlas) { gboolean ret; CoglBitmap *upload_bmp = _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, bmp, atlas_tex->internal_format, FALSE, /* can't convert in place */ error); if (!upload_bmp) return FALSE; /* Upload the data ignoring the premult bit */ ret = _cogl_atlas_texture_set_region_with_border (atlas_tex, src_x, src_y, dst_x, dst_y, dst_width, dst_height, upload_bmp, error); cogl_object_unref (upload_bmp); return ret; } else /* Otherwise we can just forward on to the sub texture */ return _cogl_texture_set_region_from_bitmap (atlas_tex->sub_texture, src_x, src_y, dst_width, dst_height, bmp, dst_x, dst_y, level, error); } static CoglPixelFormat _cogl_atlas_texture_get_format (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* We don't want to forward this on the sub-texture because it isn't the necessarily the same format. This will happen if the texture isn't pre-multiplied */ return atlas_tex->internal_format; } static GLenum _cogl_atlas_texture_get_gl_format (CoglTexture *tex) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); /* Forward on to the sub texture */ return _cogl_texture_gl_get_format (atlas_tex->sub_texture); } static gboolean _cogl_atlas_texture_can_use_format (CoglPixelFormat format) { /* We don't care about the ordering or the premult status and we can accept RGBA or RGB textures. Although we could also accept luminance and alpha only textures or 16-bit formats it seems that if the application is explicitly using these formats then they've got a reason to want the lower memory requirements so putting them in the atlas might not be a good idea */ format &= ~(COGL_PREMULT_BIT | COGL_BGR_BIT | COGL_AFIRST_BIT); return (format == COGL_PIXEL_FORMAT_RGB_888 || format == COGL_PIXEL_FORMAT_RGBA_8888); } static CoglAtlasTexture * _cogl_atlas_texture_create_base (CoglContext *ctx, int width, int height, CoglPixelFormat internal_format, CoglTextureLoader *loader) { CoglAtlasTexture *atlas_tex; COGL_NOTE (ATLAS, "Adding texture of size %ix%i", width, height); /* We need to allocate the texture now because we need the pointer to set as the data for the rectangle in the atlas */ atlas_tex = g_new0 (CoglAtlasTexture, 1); /* Mark it as having no atlas so we don't try to unref it in _cogl_atlas_texture_post_reorganize_cb */ atlas_tex->atlas = NULL; _cogl_texture_init (COGL_TEXTURE (atlas_tex), ctx, width, height, internal_format, loader, &cogl_atlas_texture_vtable); atlas_tex->sub_texture = NULL; atlas_tex->atlas = NULL; return _cogl_atlas_texture_object_new (atlas_tex); } CoglAtlasTexture * cogl_atlas_texture_new_with_size (CoglContext *ctx, int width, int height) { CoglTextureLoader *loader; /* We can't atlas zero-sized textures because it breaks the atlas * data structure */ g_return_val_if_fail (width > 0 && height > 0, NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_SIZED; loader->src.sized.width = width; loader->src.sized.height = height; return _cogl_atlas_texture_create_base (ctx, width, height, COGL_PIXEL_FORMAT_RGBA_8888_PRE, loader); } static gboolean allocate_space (CoglAtlasTexture *atlas_tex, int width, int height, CoglPixelFormat internal_format, GError **error) { CoglTexture *tex = COGL_TEXTURE (atlas_tex); CoglContext *ctx = tex->context; CoglAtlas *atlas; GSList *l; /* If the texture is in a strange format then we won't use it */ if (!_cogl_atlas_texture_can_use_format (internal_format)) { COGL_NOTE (ATLAS, "Texture can not be added because the " "format is unsupported"); g_set_error_literal (error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_FORMAT, "Texture format unsuitable for atlasing"); return FALSE; } /* Look for an existing atlas that can hold the texture */ for (l = ctx->atlases; l; l = l->next) { /* We need to take a reference on the atlas before trying to * reserve space because in some circumstances atlas migration * can cause the atlas to be freed */ atlas = cogl_object_ref (l->data); /* Try to make some space in the atlas for the texture */ if (_cogl_atlas_reserve_space (atlas, /* Add two pixels for the border */ width + 2, height + 2, atlas_tex)) { /* keep the atlas reference */ break; } else { cogl_object_unref (atlas); } } /* If we couldn't find a suitable atlas then start another */ if (l == NULL) { atlas = _cogl_atlas_texture_create_atlas (ctx); COGL_NOTE (ATLAS, "Created new atlas for textures: %p", atlas); if (!_cogl_atlas_reserve_space (atlas, /* Add two pixels for the border */ width + 2, height + 2, atlas_tex)) { /* Ok, this means we really can't add it to the atlas */ cogl_object_unref (atlas); g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_NO_MEMORY, "Not enough memory to atlas texture"); return FALSE; } } atlas_tex->internal_format = internal_format; atlas_tex->atlas = atlas; return TRUE; } static gboolean allocate_with_size (CoglAtlasTexture *atlas_tex, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (atlas_tex); CoglPixelFormat internal_format = _cogl_texture_determine_internal_format (tex, COGL_PIXEL_FORMAT_ANY); if (allocate_space (atlas_tex, loader->src.sized.width, loader->src.sized.height, internal_format, error)) { _cogl_texture_set_allocated (COGL_TEXTURE (atlas_tex), internal_format, loader->src.sized.width, loader->src.sized.height); return TRUE; } else return FALSE; } static gboolean allocate_from_bitmap (CoglAtlasTexture *atlas_tex, CoglTextureLoader *loader, GError **error) { CoglTexture *tex = COGL_TEXTURE (atlas_tex); CoglBitmap *bmp = loader->src.bitmap.bitmap; CoglPixelFormat bmp_format = cogl_bitmap_get_format (bmp); int width = cogl_bitmap_get_width (bmp); int height = cogl_bitmap_get_height (bmp); gboolean can_convert_in_place = loader->src.bitmap.can_convert_in_place; CoglPixelFormat internal_format; CoglBitmap *upload_bmp; g_return_val_if_fail (atlas_tex->atlas == NULL, FALSE); internal_format = _cogl_texture_determine_internal_format (tex, bmp_format); upload_bmp = _cogl_atlas_texture_convert_bitmap_for_upload (atlas_tex, bmp, internal_format, can_convert_in_place, error); if (upload_bmp == NULL) return FALSE; if (!allocate_space (atlas_tex, width, height, internal_format, error)) { cogl_object_unref (upload_bmp); return FALSE; } /* Defer to set_region so that we can share the code for copying the edge pixels to the border. */ if (!_cogl_atlas_texture_set_region_with_border (atlas_tex, 0, /* src_x */ 0, /* src_y */ 0, /* dst_x */ 0, /* dst_y */ width, /* dst_width */ height, /* dst_height */ upload_bmp, error)) { _cogl_atlas_texture_remove_from_atlas (atlas_tex); cogl_object_unref (upload_bmp); return FALSE; } cogl_object_unref (upload_bmp); _cogl_texture_set_allocated (tex, internal_format, width, height); return TRUE; } static gboolean _cogl_atlas_texture_allocate (CoglTexture *tex, GError **error) { CoglAtlasTexture *atlas_tex = COGL_ATLAS_TEXTURE (tex); CoglTextureLoader *loader = tex->loader; g_return_val_if_fail (loader, FALSE); switch (loader->src_type) { case COGL_TEXTURE_SOURCE_TYPE_SIZED: return allocate_with_size (atlas_tex, loader, error); case COGL_TEXTURE_SOURCE_TYPE_BITMAP: return allocate_from_bitmap (atlas_tex, loader, error); default: break; } g_return_val_if_reached (FALSE); } CoglAtlasTexture * _cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp, gboolean can_convert_in_place) { CoglTextureLoader *loader; g_return_val_if_fail (cogl_is_bitmap (bmp), NULL); loader = _cogl_texture_create_loader (); loader->src_type = COGL_TEXTURE_SOURCE_TYPE_BITMAP; loader->src.bitmap.bitmap = cogl_object_ref (bmp); loader->src.bitmap.can_convert_in_place = can_convert_in_place; return _cogl_atlas_texture_create_base (_cogl_bitmap_get_context (bmp), cogl_bitmap_get_width (bmp), cogl_bitmap_get_height (bmp), cogl_bitmap_get_format (bmp), loader); } CoglAtlasTexture * cogl_atlas_texture_new_from_bitmap (CoglBitmap *bmp) { return _cogl_atlas_texture_new_from_bitmap (bmp, FALSE); } CoglAtlasTexture * cogl_atlas_texture_new_from_data (CoglContext *ctx, int width, int height, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error) { CoglBitmap *bmp; CoglAtlasTexture *atlas_tex; g_return_val_if_fail (format != COGL_PIXEL_FORMAT_ANY, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, NULL); g_return_val_if_fail (data != NULL, NULL); /* Rowstride from width if not given */ if (rowstride == 0) rowstride = width * cogl_pixel_format_get_bytes_per_pixel (format, 0); /* Wrap the data into a bitmap */ bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); atlas_tex = cogl_atlas_texture_new_from_bitmap (bmp); cogl_object_unref (bmp); if (atlas_tex && !cogl_texture_allocate (COGL_TEXTURE (atlas_tex), error)) { cogl_object_unref (atlas_tex); return NULL; } return atlas_tex; } CoglAtlasTexture * cogl_atlas_texture_new_from_file (CoglContext *ctx, const char *filename, GError **error) { CoglBitmap *bmp; CoglAtlasTexture *atlas_tex = NULL; g_return_val_if_fail (error == NULL || *error == NULL, NULL); bmp = cogl_bitmap_new_from_file (filename, error); if (bmp == NULL) return NULL; atlas_tex = _cogl_atlas_texture_new_from_bitmap (bmp, TRUE); /* convert in-place */ cogl_object_unref (bmp); return atlas_tex; } void _cogl_atlas_texture_add_reorganize_callback (CoglContext *ctx, GHookFunc callback, void *user_data) { GHook *hook = g_hook_alloc (&ctx->atlas_reorganize_callbacks); hook->func = callback; hook->data = user_data; g_hook_prepend (&ctx->atlas_reorganize_callbacks, hook); } void _cogl_atlas_texture_remove_reorganize_callback (CoglContext *ctx, GHookFunc callback, void *user_data) { GHook *hook = g_hook_find_func_data (&ctx->atlas_reorganize_callbacks, FALSE, callback, user_data); if (hook) g_hook_destroy_link (&ctx->atlas_reorganize_callbacks, hook); } static const CoglTextureVtable cogl_atlas_texture_vtable = { FALSE, /* not primitive */ _cogl_atlas_texture_allocate, _cogl_atlas_texture_set_region, NULL, /* is_get_data_supported */ NULL, /* get_data */ _cogl_atlas_texture_foreach_sub_texture_in_region, _cogl_atlas_texture_get_max_waste, _cogl_atlas_texture_is_sliced, _cogl_atlas_texture_can_hardware_repeat, _cogl_atlas_texture_transform_coords_to_gl, _cogl_atlas_texture_transform_quad_coords_to_gl, _cogl_atlas_texture_get_gl_texture, _cogl_atlas_texture_gl_flush_legacy_texobj_filters, _cogl_atlas_texture_pre_paint, _cogl_atlas_texture_ensure_non_quad_rendering, _cogl_atlas_texture_gl_flush_legacy_texobj_wrap_modes, _cogl_atlas_texture_get_format, _cogl_atlas_texture_get_gl_format, NULL /* set_auto_mipmap */ }; muffin-6.4.1/cogl/cogl/cogl-node-private.h0000664000175000017500000000563014723361714017306 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_NODE_PRIVATE_H #define __COGL_NODE_PRIVATE_H #include "cogl-object-private.h" #include "cogl-list.h" typedef struct _CoglNode CoglNode; /* Pipelines and layers represent their state in a tree structure where * some of the state relating to a given pipeline or layer may actually * be owned by one if is ancestors in the tree. We have a common data * type to track the tree heirachy so we can share code... */ struct _CoglNode { /* the parent in terms of class hierarchy, so anything inheriting * from CoglNode also inherits from CoglObject. */ CoglObject _parent; /* The parent pipeline/layer */ CoglNode *parent; /* The list entry here contains pointers to the node's siblings */ CoglList link; /* List of children */ CoglList children; /* TRUE if the node took a strong reference on its parent. Weak * pipelines for instance don't take a reference on their parent. */ gboolean has_parent_reference; }; #define COGL_NODE(X) ((CoglNode *)(X)) void _cogl_pipeline_node_init (CoglNode *node); typedef void (*CoglNodeUnparentVFunc) (CoglNode *node); void _cogl_pipeline_node_set_parent_real (CoglNode *node, CoglNode *parent, CoglNodeUnparentVFunc unparent, gboolean take_strong_reference); void _cogl_pipeline_node_unparent_real (CoglNode *node); typedef gboolean (*CoglNodeChildCallback) (CoglNode *child, void *user_data); void _cogl_pipeline_node_foreach_child (CoglNode *node, CoglNodeChildCallback callback, void *user_data); #endif /* __COGL_NODE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-primitive-private.h0000664000175000017500000000413014723361714020363 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef __COGL_PRIMITIVE_PRIVATE_H #define __COGL_PRIMITIVE_PRIVATE_H #include "cogl-object-private.h" #include "cogl-attribute-buffer-private.h" #include "cogl-attribute-private.h" #include "cogl-framebuffer.h" struct _CoglPrimitive { CoglObject _parent; CoglIndices *indices; CoglVerticesMode mode; int first_vertex; int n_vertices; int immutable_ref; CoglAttribute **attributes; int n_attributes; int n_embedded_attributes; CoglAttribute *embedded_attribute; }; CoglPrimitive * _cogl_primitive_immutable_ref (CoglPrimitive *primitive); void _cogl_primitive_immutable_unref (CoglPrimitive *primitive); COGL_EXPORT void _cogl_primitive_draw (CoglPrimitive *primitive, CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglDrawFlags flags); #endif /* __COGL_PRIMITIVE_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-matrix-stack.c0000664000175000017500000007304014723361714017313 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009,2010,2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Havoc Pennington for litl * Robert Bragg * Neil Roberts */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-matrix-stack.h" #include "cogl-framebuffer-private.h" #include "cogl-object-private.h" #include "cogl-offscreen.h" #include "cogl-matrix-private.h" #include "cogl-magazine-private.h" #include "cogl-gtype-private.h" #include "driver/gl/cogl-util-gl-private.h" static void _cogl_matrix_stack_free (CoglMatrixStack *stack); COGL_OBJECT_DEFINE (MatrixStack, matrix_stack); COGL_GTYPE_DEFINE_CLASS (MatrixStack, matrix_stack); COGL_GTYPE_DEFINE_BOXED (MatrixEntry, matrix_entry, cogl_matrix_entry_ref, cogl_matrix_entry_unref); static CoglMagazine *cogl_matrix_stack_magazine; static CoglMagazine *cogl_matrix_stack_matrices_magazine; /* XXX: Note: this leaves entry->parent uninitialized! */ static CoglMatrixEntry * _cogl_matrix_entry_new (CoglMatrixOp operation) { CoglMatrixEntry *entry = _cogl_magazine_chunk_alloc (cogl_matrix_stack_magazine); entry->ref_count = 1; entry->op = operation; #ifdef COGL_DEBUG_ENABLED entry->composite_gets = 0; #endif return entry; } static void * _cogl_matrix_stack_push_entry (CoglMatrixStack *stack, CoglMatrixEntry *entry) { /* NB: The initial reference of the entry is transferred to the * stack here. * * The stack only maintains a reference to the top of the stack (the * last entry pushed) and each entry in-turn maintains a reference * to its parent. * * We don't need to take a reference to the parent from the entry * here because the we are stealing the reference that was held by * the stack while that parent was previously the top of the stack. */ entry->parent = stack->last_entry; stack->last_entry = entry; return entry; } static void * _cogl_matrix_stack_push_operation (CoglMatrixStack *stack, CoglMatrixOp operation) { CoglMatrixEntry *entry = _cogl_matrix_entry_new (operation); _cogl_matrix_stack_push_entry (stack, entry); return entry; } static void * _cogl_matrix_stack_push_replacement_entry (CoglMatrixStack *stack, CoglMatrixOp operation) { CoglMatrixEntry *old_top = stack->last_entry; CoglMatrixEntry *new_top; /* This would only be called for operations that completely replace * the matrix. In that case we don't need to keep a reference to * anything up to the last save entry. This optimisation could be * important for applications that aren't using the stack but * instead just perform their own matrix manipulations and load a * new stack every frame. If this optimisation isn't done then the * stack would just grow endlessly. See the comments * cogl_matrix_stack_pop for a description of how popping works. */ for (new_top = old_top; new_top->op != COGL_MATRIX_OP_SAVE && new_top->parent; new_top = new_top->parent) ; cogl_matrix_entry_ref (new_top); cogl_matrix_entry_unref (old_top); stack->last_entry = new_top; return _cogl_matrix_stack_push_operation (stack, operation); } void _cogl_matrix_entry_identity_init (CoglMatrixEntry *entry) { entry->ref_count = 1; entry->op = COGL_MATRIX_OP_LOAD_IDENTITY; entry->parent = NULL; #ifdef COGL_DEBUG_ENABLED entry->composite_gets = 0; #endif } void cogl_matrix_stack_load_identity (CoglMatrixStack *stack) { _cogl_matrix_stack_push_replacement_entry (stack, COGL_MATRIX_OP_LOAD_IDENTITY); } void cogl_matrix_stack_translate (CoglMatrixStack *stack, float x, float y, float z) { CoglMatrixEntryTranslate *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_TRANSLATE); graphene_point3d_init (&entry->translate, x, y, z); } void cogl_matrix_stack_rotate (CoglMatrixStack *stack, float angle, float x, float y, float z) { CoglMatrixEntryRotate *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_ROTATE); entry->angle = angle; graphene_vec3_init (&entry->axis, x, y, z); } void cogl_matrix_stack_rotate_euler (CoglMatrixStack *stack, const graphene_euler_t *euler) { CoglMatrixEntryRotateEuler *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_ROTATE_EULER); graphene_euler_init_from_euler (&entry->euler, euler); } void cogl_matrix_stack_scale (CoglMatrixStack *stack, float x, float y, float z) { CoglMatrixEntryScale *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_SCALE); entry->x = x; entry->y = y; entry->z = z; } void cogl_matrix_stack_multiply (CoglMatrixStack *stack, const CoglMatrix *matrix) { CoglMatrixEntryMultiply *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_MULTIPLY); entry->matrix = _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); cogl_matrix_init_from_array (entry->matrix, (float *)matrix); } void cogl_matrix_stack_set (CoglMatrixStack *stack, const CoglMatrix *matrix) { CoglMatrixEntryLoad *entry; entry = _cogl_matrix_stack_push_replacement_entry (stack, COGL_MATRIX_OP_LOAD); entry->matrix = _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); cogl_matrix_init_from_array (entry->matrix, (float *)matrix); } void cogl_matrix_stack_frustum (CoglMatrixStack *stack, float left, float right, float bottom, float top, float z_near, float z_far) { CoglMatrixEntryLoad *entry; entry = _cogl_matrix_stack_push_replacement_entry (stack, COGL_MATRIX_OP_LOAD); entry->matrix = _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); cogl_matrix_init_identity (entry->matrix); cogl_matrix_frustum (entry->matrix, left, right, bottom, top, z_near, z_far); } void cogl_matrix_stack_perspective (CoglMatrixStack *stack, float fov_y, float aspect, float z_near, float z_far) { CoglMatrixEntryLoad *entry; entry = _cogl_matrix_stack_push_replacement_entry (stack, COGL_MATRIX_OP_LOAD); entry->matrix = _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); cogl_matrix_init_identity (entry->matrix); cogl_matrix_perspective (entry->matrix, fov_y, aspect, z_near, z_far); } void cogl_matrix_stack_orthographic (CoglMatrixStack *stack, float x_1, float y_1, float x_2, float y_2, float near, float far) { CoglMatrixEntryLoad *entry; entry = _cogl_matrix_stack_push_replacement_entry (stack, COGL_MATRIX_OP_LOAD); entry->matrix = _cogl_magazine_chunk_alloc (cogl_matrix_stack_matrices_magazine); cogl_matrix_init_identity (entry->matrix); cogl_matrix_orthographic (entry->matrix, x_1, y_1, x_2, y_2, near, far); } void cogl_matrix_stack_push (CoglMatrixStack *stack) { CoglMatrixEntrySave *entry; entry = _cogl_matrix_stack_push_operation (stack, COGL_MATRIX_OP_SAVE); entry->cache_valid = FALSE; } CoglMatrixEntry * cogl_matrix_entry_ref (CoglMatrixEntry *entry) { /* A NULL pointer is considered a valid stack so we should accept that as an argument */ if (entry) entry->ref_count++; return entry; } void cogl_matrix_entry_unref (CoglMatrixEntry *entry) { CoglMatrixEntry *parent; for (; entry && --entry->ref_count <= 0; entry = parent) { parent = entry->parent; switch (entry->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_TRANSLATE: case COGL_MATRIX_OP_ROTATE: case COGL_MATRIX_OP_ROTATE_EULER: case COGL_MATRIX_OP_SCALE: break; case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *multiply = (CoglMatrixEntryMultiply *)entry; _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, multiply->matrix); break; } case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, load->matrix); break; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; if (save->cache_valid) _cogl_magazine_chunk_free (cogl_matrix_stack_matrices_magazine, save->cache); break; } } _cogl_magazine_chunk_free (cogl_matrix_stack_magazine, entry); } } void cogl_matrix_stack_pop (CoglMatrixStack *stack) { CoglMatrixEntry *old_top; CoglMatrixEntry *new_top; g_return_if_fail (stack != NULL); old_top = stack->last_entry; g_return_if_fail (old_top != NULL); /* To pop we are moving the top of the stack to the old top's parent * node. The stack always needs to have a reference to the top entry * so we must take a reference to the new top. The stack would have * previously had a reference to the old top so we need to decrease * the ref count on that. We need to ref the new head first in case * this stack was the only thing referencing the old top. In that * case the call to cogl_matrix_entry_unref will unref the parent. */ /* Find the last save operation and remove it */ /* XXX: it would be an error to pop to the very beginning of the * stack so we don't need to check for NULL pointer dereferencing. */ for (new_top = old_top; new_top->op != COGL_MATRIX_OP_SAVE; new_top = new_top->parent) ; new_top = new_top->parent; cogl_matrix_entry_ref (new_top); cogl_matrix_entry_unref (old_top); stack->last_entry = new_top; } gboolean cogl_matrix_stack_get_inverse (CoglMatrixStack *stack, CoglMatrix *inverse) { CoglMatrix matrix; CoglMatrix *internal = cogl_matrix_stack_get (stack, &matrix); if (internal) return cogl_matrix_get_inverse (internal, inverse); else return cogl_matrix_get_inverse (&matrix, inverse); } /* In addition to writing the stack matrix into the give @matrix * argument this function *may* sometimes also return a pointer * to a matrix too so if we are querying the inverse matrix we * should query from the return matrix so that the result can * be cached within the stack. */ CoglMatrix * cogl_matrix_entry_get (CoglMatrixEntry *entry, CoglMatrix *matrix) { int depth; CoglMatrixEntry *current; CoglMatrixEntry **children; int i; for (depth = 0, current = entry; current; current = current->parent, depth++) { switch (current->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: cogl_matrix_init_identity (matrix); goto initialized; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)current; _cogl_matrix_init_from_matrix_without_inverse (matrix, load->matrix); goto initialized; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)current; if (!save->cache_valid) { CoglMagazine *matrices_magazine = cogl_matrix_stack_matrices_magazine; save->cache = _cogl_magazine_chunk_alloc (matrices_magazine); cogl_matrix_entry_get (current->parent, save->cache); save->cache_valid = TRUE; } _cogl_matrix_init_from_matrix_without_inverse (matrix, save->cache); goto initialized; } default: continue; } } initialized: if (depth == 0) { switch (entry->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_TRANSLATE: case COGL_MATRIX_OP_ROTATE: case COGL_MATRIX_OP_ROTATE_EULER: case COGL_MATRIX_OP_SCALE: case COGL_MATRIX_OP_MULTIPLY: return NULL; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; return load->matrix; } case COGL_MATRIX_OP_SAVE: { CoglMatrixEntrySave *save = (CoglMatrixEntrySave *)entry; return save->cache; } } g_warn_if_reached (); return NULL; } #ifdef COGL_ENABLE_DEBUG if (!current) { g_warning ("Inconsistent matrix stack"); return NULL; } entry->composite_gets++; #endif children = g_alloca (sizeof (CoglMatrixEntry) * depth); /* We need walk the list of entries from the init/load/save entry * back towards the leaf node but the nodes don't link to their * children so we need to re-walk them here to add to a separate * array. */ for (i = depth - 1, current = entry; i >= 0 && current; i--, current = current->parent) { children[i] = current; } #ifdef COGL_ENABLE_DEBUG if (COGL_DEBUG_ENABLED (COGL_DEBUG_PERFORMANCE) && entry->composite_gets >= 2) { COGL_NOTE (PERFORMANCE, "Re-composing a matrix stack entry multiple times"); } #endif for (i = 0; i < depth; i++) { switch (children[i]->op) { case COGL_MATRIX_OP_TRANSLATE: { CoglMatrixEntryTranslate *translate = (CoglMatrixEntryTranslate *)children[i]; cogl_matrix_translate (matrix, translate->translate.x, translate->translate.y, translate->translate.z); continue; } case COGL_MATRIX_OP_ROTATE: { CoglMatrixEntryRotate *rotate= (CoglMatrixEntryRotate *)children[i]; cogl_matrix_rotate (matrix, rotate->angle, graphene_vec3_get_x (&rotate->axis), graphene_vec3_get_y (&rotate->axis), graphene_vec3_get_z (&rotate->axis)); continue; } case COGL_MATRIX_OP_ROTATE_EULER: { CoglMatrixEntryRotateEuler *rotate = (CoglMatrixEntryRotateEuler *)children[i]; cogl_matrix_rotate_euler (matrix, &rotate->euler); continue; } case COGL_MATRIX_OP_SCALE: { CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)children[i]; cogl_matrix_scale (matrix, scale->x, scale->y, scale->z); continue; } case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *multiply = (CoglMatrixEntryMultiply *)children[i]; cogl_matrix_multiply (matrix, matrix, multiply->matrix); continue; } case COGL_MATRIX_OP_LOAD_IDENTITY: case COGL_MATRIX_OP_LOAD: case COGL_MATRIX_OP_SAVE: g_warn_if_reached (); continue; } } return NULL; } CoglMatrixEntry * cogl_matrix_stack_get_entry (CoglMatrixStack *stack) { return stack->last_entry; } /* In addition to writing the stack matrix into the give @matrix * argument this function *may* sometimes also return a pointer * to a matrix too so if we are querying the inverse matrix we * should query from the return matrix so that the result can * be cached within the stack. */ CoglMatrix * cogl_matrix_stack_get (CoglMatrixStack *stack, CoglMatrix *matrix) { return cogl_matrix_entry_get (stack->last_entry, matrix); } static void _cogl_matrix_stack_free (CoglMatrixStack *stack) { cogl_matrix_entry_unref (stack->last_entry); g_slice_free (CoglMatrixStack, stack); } CoglMatrixStack * cogl_matrix_stack_new (CoglContext *ctx) { CoglMatrixStack *stack = g_slice_new (CoglMatrixStack); if (G_UNLIKELY (cogl_matrix_stack_magazine == NULL)) { cogl_matrix_stack_magazine = _cogl_magazine_new (sizeof (CoglMatrixEntryFull), 20); cogl_matrix_stack_matrices_magazine = _cogl_magazine_new (sizeof (CoglMatrix), 20); } stack->context = ctx; stack->last_entry = NULL; cogl_matrix_entry_ref (&ctx->identity_entry); _cogl_matrix_stack_push_entry (stack, &ctx->identity_entry); return _cogl_matrix_stack_object_new (stack); } static CoglMatrixEntry * _cogl_matrix_entry_skip_saves (CoglMatrixEntry *entry) { /* We currently assume that every stack starts with an * _OP_LOAD_IDENTITY so we don't need to worry about * NULL pointer dereferencing here. */ while (entry->op == COGL_MATRIX_OP_SAVE) entry = entry->parent; return entry; } gboolean cogl_matrix_entry_calculate_translation (CoglMatrixEntry *entry0, CoglMatrixEntry *entry1, float *x, float *y, float *z) { GSList *head0 = NULL; GSList *head1 = NULL; CoglMatrixEntry *node0; CoglMatrixEntry *node1; int len0 = 0; int len1 = 0; int count; GSList *common_ancestor0; GSList *common_ancestor1; /* Algorithm: * * 1) Ignoring _OP_SAVE entries walk the ancestors of each entry to * the root node or any non-translation node, adding a pointer to * each ancestor node to two linked lists. * * 2) Compare the lists to find the nodes where they start to * differ marking the common_ancestor node for each list. * * 3) For the list corresponding to entry0, start iterating after * the common ancestor applying the negative of all translations * to x, y and z. * * 4) For the list corresponding to entry1, start iterating after * the common ancestor applying the positive of all translations * to x, y and z. * * If we come across any non-translation operations during 3) or 4) * then bail out returning FALSE. */ for (node0 = entry0; node0; node0 = node0->parent) { GSList *link; if (node0->op == COGL_MATRIX_OP_SAVE) continue; link = alloca (sizeof (GSList)); link->next = head0; link->data = node0; head0 = link; len0++; if (node0->op != COGL_MATRIX_OP_TRANSLATE) break; } for (node1 = entry1; node1; node1 = node1->parent) { GSList *link; if (node1->op == COGL_MATRIX_OP_SAVE) continue; link = alloca (sizeof (GSList)); link->next = head1; link->data = node1; head1 = link; len1++; if (node1->op != COGL_MATRIX_OP_TRANSLATE) break; } if (head0->data != head1->data) return FALSE; common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; count = MIN (len0, len1) - 1; while (count--) { if (head0->data != head1->data) break; common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; } *x = 0; *y = 0; *z = 0; for (head0 = common_ancestor0->next; head0; head0 = head0->next) { CoglMatrixEntryTranslate *translate; node0 = head0->data; if (node0->op != COGL_MATRIX_OP_TRANSLATE) return FALSE; translate = (CoglMatrixEntryTranslate *)node0; *x = *x - translate->translate.x; *y = *y - translate->translate.y; *z = *z - translate->translate.z; } for (head1 = common_ancestor1->next; head1; head1 = head1->next) { CoglMatrixEntryTranslate *translate; node1 = head1->data; if (node1->op != COGL_MATRIX_OP_TRANSLATE) return FALSE; translate = (CoglMatrixEntryTranslate *)node1; *x = *x + translate->translate.x; *y = *y + translate->translate.y; *z = *z + translate->translate.z; } return TRUE; } gboolean cogl_matrix_entry_is_identity (CoglMatrixEntry *entry) { return entry ? entry->op == COGL_MATRIX_OP_LOAD_IDENTITY : FALSE; } gboolean cogl_matrix_entry_equal (CoglMatrixEntry *entry0, CoglMatrixEntry *entry1) { for (; entry0 && entry1; entry0 = entry0->parent, entry1 = entry1->parent) { entry0 = _cogl_matrix_entry_skip_saves (entry0); entry1 = _cogl_matrix_entry_skip_saves (entry1); if (entry0 == entry1) return TRUE; if (entry0->op != entry1->op) return FALSE; switch (entry0->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: return TRUE; case COGL_MATRIX_OP_TRANSLATE: { CoglMatrixEntryTranslate *translate0 = (CoglMatrixEntryTranslate *)entry0; CoglMatrixEntryTranslate *translate1 = (CoglMatrixEntryTranslate *)entry1; /* We could perhaps use an epsilon to compare here? * I expect the false negatives are probaly never going to * be a problem and this is a bit cheaper. */ if (!graphene_point3d_equal (&translate0->translate, &translate1->translate)) return FALSE; } break; case COGL_MATRIX_OP_ROTATE: { CoglMatrixEntryRotate *rotate0 = (CoglMatrixEntryRotate *)entry0; CoglMatrixEntryRotate *rotate1 = (CoglMatrixEntryRotate *)entry1; if (rotate0->angle != rotate1->angle || !graphene_vec3_equal (&rotate0->axis, &rotate1->axis)) return FALSE; } break; case COGL_MATRIX_OP_ROTATE_EULER: { CoglMatrixEntryRotateEuler *rotate0 = (CoglMatrixEntryRotateEuler *)entry0; CoglMatrixEntryRotateEuler *rotate1 = (CoglMatrixEntryRotateEuler *)entry1; if (!graphene_euler_equal (&rotate0->euler, &rotate1->euler)) return FALSE; } break; case COGL_MATRIX_OP_SCALE: { CoglMatrixEntryScale *scale0 = (CoglMatrixEntryScale *)entry0; CoglMatrixEntryScale *scale1 = (CoglMatrixEntryScale *)entry1; if (scale0->x != scale1->x || scale0->y != scale1->y || scale0->z != scale1->z) return FALSE; } break; case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *mult0 = (CoglMatrixEntryMultiply *)entry0; CoglMatrixEntryMultiply *mult1 = (CoglMatrixEntryMultiply *)entry1; if (!cogl_matrix_equal (mult0->matrix, mult1->matrix)) return FALSE; } break; case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load0 = (CoglMatrixEntryLoad *)entry0; CoglMatrixEntryLoad *load1 = (CoglMatrixEntryLoad *)entry1; /* There's no need to check any further since an * _OP_LOAD makes all the ancestors redundant as far as * the final matrix value is concerned. */ return cogl_matrix_equal (load0->matrix, load1->matrix); } case COGL_MATRIX_OP_SAVE: /* We skip over saves above so we shouldn't see save entries */ g_warn_if_reached (); } } return FALSE; } void cogl_debug_matrix_entry_print (CoglMatrixEntry *entry) { int depth; CoglMatrixEntry *e; CoglMatrixEntry **children; int i; for (depth = 0, e = entry; e; e = e->parent) depth++; children = g_alloca (sizeof (CoglMatrixEntry) * depth); for (i = depth - 1, e = entry; i >= 0 && e; i--, e = e->parent) { children[i] = e; } g_print ("MatrixEntry %p =\n", entry); for (i = 0; i < depth; i++) { entry = children[i]; switch (entry->op) { case COGL_MATRIX_OP_LOAD_IDENTITY: g_print (" LOAD IDENTITY\n"); continue; case COGL_MATRIX_OP_TRANSLATE: { CoglMatrixEntryTranslate *translate = (CoglMatrixEntryTranslate *)entry; g_print (" TRANSLATE X=%f Y=%f Z=%f\n", translate->translate.x, translate->translate.y, translate->translate.z); continue; } case COGL_MATRIX_OP_ROTATE: { CoglMatrixEntryRotate *rotate = (CoglMatrixEntryRotate *)entry; g_print (" ROTATE ANGLE=%f X=%f Y=%f Z=%f\n", rotate->angle, graphene_vec3_get_x (&rotate->axis), graphene_vec3_get_y (&rotate->axis), graphene_vec3_get_z (&rotate->axis)); continue; } case COGL_MATRIX_OP_ROTATE_EULER: { CoglMatrixEntryRotateEuler *rotate = (CoglMatrixEntryRotateEuler *)entry; g_print (" ROTATE EULER heading=%f pitch=%f roll=%f\n", graphene_euler_get_y (&rotate->euler), graphene_euler_get_x (&rotate->euler), graphene_euler_get_z (&rotate->euler)); continue; } case COGL_MATRIX_OP_SCALE: { CoglMatrixEntryScale *scale = (CoglMatrixEntryScale *)entry; g_print (" SCALE X=%f Y=%f Z=%f\n", scale->x, scale->y, scale->z); continue; } case COGL_MATRIX_OP_MULTIPLY: { CoglMatrixEntryMultiply *mult = (CoglMatrixEntryMultiply *)entry; g_print (" MULT:\n"); _cogl_matrix_prefix_print (" ", mult->matrix); continue; } case COGL_MATRIX_OP_LOAD: { CoglMatrixEntryLoad *load = (CoglMatrixEntryLoad *)entry; g_print (" LOAD:\n"); _cogl_matrix_prefix_print (" ", load->matrix); continue; } case COGL_MATRIX_OP_SAVE: g_print (" SAVE\n"); } } } void _cogl_matrix_entry_cache_init (CoglMatrixEntryCache *cache) { cache->entry = NULL; cache->flushed_identity = FALSE; cache->flipped = FALSE; } /* NB: This function can report false negatives since it never does a * deep comparison of the stack matrices. */ gboolean _cogl_matrix_entry_cache_maybe_update (CoglMatrixEntryCache *cache, CoglMatrixEntry *entry, gboolean flip) { gboolean is_identity; gboolean updated = FALSE; if (cache->flipped != flip) { cache->flipped = flip; updated = TRUE; } is_identity = (entry->op == COGL_MATRIX_OP_LOAD_IDENTITY); if (cache->flushed_identity != is_identity) { cache->flushed_identity = is_identity; updated = TRUE; } if (cache->entry != entry) { cogl_matrix_entry_ref (entry); if (cache->entry) cogl_matrix_entry_unref (cache->entry); cache->entry = entry; /* We want to make sure here that if the cache->entry and the * given @entry are both identity matrices then even though they * are different entries we don't want to consider this an * update... */ updated |= !is_identity; } return updated; } void _cogl_matrix_entry_cache_destroy (CoglMatrixEntryCache *cache) { if (cache->entry) cogl_matrix_entry_unref (cache->entry); } muffin-6.4.1/cogl/cogl/cogl-blend-string.h0000664000175000017500000001017014723361714017274 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #ifndef COGL_BLEND_STRING_H #define COGL_BLEND_STRING_H #include #include typedef enum _CoglBlendStringContext { COGL_BLEND_STRING_CONTEXT_BLENDING, COGL_BLEND_STRING_CONTEXT_TEXTURE_COMBINE } CoglBlendStringContext; /* NB: debug stringify code will get upset if these * are re-ordered */ typedef enum _CoglBlendStringChannelMask { COGL_BLEND_STRING_CHANNEL_MASK_RGB, COGL_BLEND_STRING_CHANNEL_MASK_ALPHA, COGL_BLEND_STRING_CHANNEL_MASK_RGBA } CoglBlendStringChannelMask; typedef enum _CoglBlendStringColorSourceType { /* blending */ COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR, COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR, /* shared */ COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT, /* texture combining */ COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE, COGL_BLEND_STRING_COLOR_SOURCE_TEXTURE_N, COGL_BLEND_STRING_COLOR_SOURCE_PRIMARY, COGL_BLEND_STRING_COLOR_SOURCE_PREVIOUS } CoglBlendStringColorSourceType; typedef struct _CoglBlendStringColorSourceInfo { CoglBlendStringColorSourceType type; const char *name; size_t name_len; } CoglBlendStringColorSourceInfo; typedef struct _CoglBlendStringColorSource { gboolean is_zero; const CoglBlendStringColorSourceInfo *info; int texture; /* for the TEXTURE_N color source */ gboolean one_minus; CoglBlendStringChannelMask mask; } CoglBlendStringColorSource; typedef struct _CoglBlendStringFactor { gboolean is_one; gboolean is_src_alpha_saturate; gboolean is_color; CoglBlendStringColorSource source; } CoglBlendStringFactor; typedef struct _CoglBlendStringArgument { CoglBlendStringColorSource source; CoglBlendStringFactor factor; } CoglBlendStringArgument; typedef enum _CoglBlendStringFunctionType { /* shared */ COGL_BLEND_STRING_FUNCTION_ADD, /* texture combine only */ COGL_BLEND_STRING_FUNCTION_REPLACE, COGL_BLEND_STRING_FUNCTION_MODULATE, COGL_BLEND_STRING_FUNCTION_ADD_SIGNED, COGL_BLEND_STRING_FUNCTION_INTERPOLATE, COGL_BLEND_STRING_FUNCTION_SUBTRACT, COGL_BLEND_STRING_FUNCTION_DOT3_RGB, COGL_BLEND_STRING_FUNCTION_DOT3_RGBA } CoglBlendStringFunctionType; typedef struct _CoglBlendStringFunctionInfo { enum _CoglBlendStringFunctionType type; const char *name; size_t name_len; int argc; } CoglBlendStringFunctionInfo; typedef struct _CoglBlendStringStatement { CoglBlendStringChannelMask mask; const CoglBlendStringFunctionInfo *function; CoglBlendStringArgument args[3]; } CoglBlendStringStatement; gboolean _cogl_blend_string_compile (const char *string, CoglBlendStringContext context, CoglBlendStringStatement *statements, GError **error); void _cogl_blend_string_split_rgba_statement (CoglBlendStringStatement *statement, CoglBlendStringStatement *rgb, CoglBlendStringStatement *a); #endif /* COGL_BLEND_STRING_H */ muffin-6.4.1/cogl/cogl/cogl-texture-2d-sliced.h0000664000175000017500000003203514723361714020154 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Robert Bragg */ #ifndef __COGL_TEXURE_2D_SLICED_H #define __COGL_TEXURE_2D_SLICED_H #include "cogl-context.h" #include "cogl-types.h" /** * SECTION:cogl-texture-2d-sliced * @short_description: Functions for creating and manipulating 2D meta * textures that may internally be comprised of * multiple 2D textures with power-of-two sizes. * * These functions allow high-level meta textures (See the * #CoglMetaTexture interface) to be allocated that may internally be * comprised of multiple 2D texture "slices" with power-of-two sizes. * * This API can be useful when working with GPUs that don't have * native support for non-power-of-two textures or if you want to load * a texture that is larger than the GPUs maximum texture size limits. * * The algorithm for slicing works by first trying to map a virtual * size to the next larger power-of-two size and then seeing how many * wasted pixels that would result in. For example if you have a * virtual texture that's 259 texels wide, the next pot size = 512 and * the amount of waste would be 253 texels. If the amount of waste is * above a max-waste threshold then we would next slice that texture * into one that's 256 texels and then looking at how many more texels * remain unallocated after that we choose the next power-of-two size. * For the example of a 259 texel image that would mean having a 256 * texel wide texture, leaving 3 texels unallocated so we'd then * create a 4 texel wide texture - now there is only one texel of * waste. The algorithm continues to slice the right most textures * until the amount of waste is less than or equal to a specfied * max-waste threshold. The same logic for slicing from left to right * is also applied from top to bottom. */ typedef struct _CoglTexture2DSliced CoglTexture2DSliced; #define COGL_TEXTURE_2D_SLICED(X) ((CoglTexture2DSliced *)X) /** * cogl_texture_2d_sliced_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_texture_2d_sliced_get_gtype (void); /** * cogl_texture_2d_sliced_new_with_size: (skip) * @ctx: A #CoglContext * @width: The virtual width of your sliced texture. * @height: The virtual height of your sliced texture. * @max_waste: The threshold of how wide a strip of wasted texels * are allowed along the right and bottom textures before * they must be sliced to reduce the amount of waste. A * negative can be passed to disable slicing. * * Creates a #CoglTexture2DSliced that may internally be comprised of * 1 or more #CoglTexture2D textures depending on GPU limitations. * For example if the GPU only supports power-of-two sized textures * then a sliced texture will turn a non-power-of-two size into a * combination of smaller power-of-two sized textures. If the * requested texture size is larger than is supported by the hardware * then the texture will be sliced into smaller textures that can be * accessed by the hardware. * * @max_waste is used as a threshold for recursively slicing the * right-most or bottom-most slices into smaller sizes until the * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * * It's possible for the allocation of a sliced texture to fail * later due to impossible slicing constraints if a negative * @max_waste value is given. If the given virtual texture size size * is larger than is supported by the hardware but slicing is disabled * the texture size would be too large to handle. * * Returns: (transfer full): A new #CoglTexture2DSliced object with no storage * allocated yet. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglTexture2DSliced * cogl_texture_2d_sliced_new_with_size (CoglContext *ctx, int width, int height, int max_waste); /** * cogl_texture_2d_sliced_new_from_file: (skip) * @ctx: A #CoglContext * @filename: the file to load * @max_waste: The threshold of how wide a strip of wasted texels * are allowed along the right and bottom textures before * they must be sliced to reduce the amount of waste. A * negative can be passed to disable slicing. * @error: A #GError to catch exceptional errors or %NULL * * Creates a #CoglTexture2DSliced from an image file. * * A #CoglTexture2DSliced may internally be comprised of 1 or more * #CoglTexture2D textures depending on GPU limitations. For example * if the GPU only supports power-of-two sized textures then a sliced * texture will turn a non-power-of-two size into a combination of * smaller power-of-two sized textures. If the requested texture size * is larger than is supported by the hardware then the texture will * be sliced into smaller textures that can be accessed by the * hardware. * * @max_waste is used as a threshold for recursively slicing the * right-most or bottom-most slices into smaller sizes until the * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * * It's possible for the allocation of a sliced texture to fail * later due to impossible slicing constraints if a negative * @max_waste value is given. If the given virtual texture size is * larger than is supported by the hardware but slicing is disabled * the texture size would be too large to handle. * * Return value: (transfer full): A newly created #CoglTexture2DSliced * or %NULL on failure and @error will be updated. * * Since: 1.16 */ COGL_EXPORT CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_file (CoglContext *ctx, const char *filename, int max_waste, GError **error); /** * cogl_texture_2d_sliced_new_from_data: (skip) * @ctx: A #CoglContext * @width: width of texture in pixels * @height: height of texture in pixels * @format: the #CoglPixelFormat the buffer is stored in in RAM * @max_waste: The threshold of how wide a strip of wasted texels * are allowed along the right and bottom textures before * they must be sliced to reduce the amount of waste. A * negative can be passed to disable slicing. * @rowstride: the memory offset in bytes between the start of each * row in @data. A value of 0 will make Cogl automatically * calculate @rowstride from @width and @format. * @data: pointer the memory region where the source buffer resides * @error: A #GError to catch exceptional errors or %NULL * * Creates a new #CoglTexture2DSliced texture based on data residing * in memory. * * A #CoglTexture2DSliced may internally be comprised of 1 or more * #CoglTexture2D textures depending on GPU limitations. For example * if the GPU only supports power-of-two sized textures then a sliced * texture will turn a non-power-of-two size into a combination of * smaller power-of-two sized textures. If the requested texture size * is larger than is supported by the hardware then the texture will * be sliced into smaller textures that can be accessed by the * hardware. * * @max_waste is used as a threshold for recursively slicing the * right-most or bottom-most slices into smaller sizes until the * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * * This api will always immediately allocate GPU memory for all * the required texture slices and upload the given data so that the * @data pointer does not need to remain valid once this function * returns. This means it is not possible to configure the texture * before it is allocated. If you do need to configure the texture * before allocation (to specify constraints on the internal format * for example) then you can instead create a #CoglBitmap for your * data and use cogl_texture_2d_sliced_new_from_bitmap() or use * cogl_texture_2d_sliced_new_with_size() and then upload data using * cogl_texture_set_data() * * It's possible for the allocation of a sliced texture to fail * due to impossible slicing constraints if a negative @max_waste * value is given. If the given virtual texture size is larger than is * supported by the hardware but slicing is disabled the texture size * would be too large to handle. * * Return value: (transfer full): A newly created #CoglTexture2DSliced * or %NULL on failure and @error will be updated. * * Since: 1.16 */ COGL_EXPORT CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_data (CoglContext *ctx, int width, int height, int max_waste, CoglPixelFormat format, int rowstride, const uint8_t *data, GError **error); /** * cogl_texture_2d_sliced_new_from_bitmap: * @bmp: A #CoglBitmap * @max_waste: The threshold of how wide a strip of wasted texels * are allowed along the right and bottom textures before * they must be sliced to reduce the amount of waste. A * negative can be passed to disable slicing. * * Creates a new #CoglTexture2DSliced texture based on data residing * in a bitmap. * * A #CoglTexture2DSliced may internally be comprised of 1 or more * #CoglTexture2D textures depending on GPU limitations. For example * if the GPU only supports power-of-two sized textures then a sliced * texture will turn a non-power-of-two size into a combination of * smaller power-of-two sized textures. If the requested texture size * is larger than is supported by the hardware then the texture will * be sliced into smaller textures that can be accessed by the * hardware. * * @max_waste is used as a threshold for recursively slicing the * right-most or bottom-most slices into smaller sizes until the * wasted padding at the bottom and right of the textures is less than * specified. A negative @max_waste will disable slicing. * * The storage for the texture is not allocated before this function * returns. You can call cogl_texture_allocate() to explicitly * allocate the underlying storage or let Cogl automatically allocate * storage lazily. * * It's possible for the allocation of a sliced texture to fail * later due to impossible slicing constraints if a negative * @max_waste value is given. If the given virtual texture size is * larger than is supported by the hardware but slicing is disabled * the texture size would be too large to handle. * * Return value: (transfer full): A newly created #CoglTexture2DSliced * or %NULL on failure and @error will be updated. * * Since: 1.16 */ COGL_EXPORT CoglTexture2DSliced * cogl_texture_2d_sliced_new_from_bitmap (CoglBitmap *bmp, int max_waste); /** * cogl_is_texture_2d_sliced: * @object: A #CoglObject pointer * * Gets whether the given object references a #CoglTexture2DSliced. * * Return value: %TRUE if the object references a #CoglTexture2DSliced * and %FALSE otherwise. * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_texture_2d_sliced (void *object); #endif /* __COGL_TEXURE_2D_SLICED_H */ muffin-6.4.1/cogl/cogl/cogl-swap-chain-private.h0000664000175000017500000000263014723361714020410 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_SWAP_CHAIN_PRIVATE_H #define __COGL_SWAP_CHAIN_PRIVATE_H #include "cogl-object-private.h" struct _CoglSwapChain { CoglObject _parent; gboolean has_alpha; int length; }; #endif /* __COGL_SWAP_CHAIN_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-fence-private.h0000664000175000017500000000344714723361714017445 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Collabora Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_FENCE_PRIVATE_H__ #define __COGL_FENCE_PRIVATE_H__ #include "cogl-fence.h" #include "cogl-list.h" #include "winsys/cogl-winsys-private.h" typedef enum { FENCE_TYPE_PENDING, #ifdef GL_ARB_sync FENCE_TYPE_GL_ARB, #endif FENCE_TYPE_WINSYS, FENCE_TYPE_ERROR } CoglFenceType; struct _CoglFenceClosure { CoglList link; CoglFramebuffer *framebuffer; CoglFenceType type; void *fence_obj; CoglFenceCallback callback; void *user_data; }; void _cogl_fence_submit (CoglFenceClosure *fence); void _cogl_fence_cancel_fences_for_framebuffer (CoglFramebuffer *framebuffer); #endif /* __COGL_FENCE_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/gl-prototypes/0000775000175000017500000000000014723361714016442 5ustar fabiofabiomuffin-6.4.1/cogl/cogl/gl-prototypes/cogl-in-gles-core-functions.h0000664000175000017500000000504614723361714024034 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is included multiple times with different definitions for * these macros. The macros are given the following arguments: * * COGL_EXT_BEGIN: * * @name: a unique symbol name for this feature * * @min_gl_major: the major part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * @min_gl_minor: the minor part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * * @gles_availability: flags to specify which versions of GLES the * functions are available in. Should be a combination of * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. * * @extension_suffixes: A zero-separated list of suffixes in a * string. These are appended to the extension name to get a complete * extension name to try. The suffix is also appended to all of the * function names. The suffix can optionally include a ':' to specify * an alternate suffix for the function names. * * @extension_names: A list of extension names to try. If any of these * extensions match then it will be used. */ COGL_EXT_BEGIN (only_in_both_gles, 4, 1, COGL_EXT_IN_GLES2, "ARB\0", "ES2_compatibility\0") COGL_EXT_FUNCTION (void, glDepthRangef, (GLfloat near_val, GLfloat far_val)) COGL_EXT_END () muffin-6.4.1/cogl/cogl/gl-prototypes/cogl-in-gles2-core-functions.h0000664000175000017500000001363214723361714024116 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is included multiple times with different definitions for * these macros. The macros are given the following arguments: * * COGL_EXT_BEGIN: * * @name: a unique symbol name for this feature * * @min_gl_major: the major part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * @min_gl_minor: the minor part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * * @gles_availability: flags to specify which versions of GLES the * functions are available in. Should be a combination of * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. * * @extension_suffixes: A zero-separated list of suffixes in a * string. These are appended to the extension name to get a complete * extension name to try. The suffix is also appended to all of the * function names. The suffix can optionally include a ':' to specify * an alternate suffix for the function names. * * @extension_names: A list of extension names to try. If any of these * extensions match then it will be used. */ COGL_EXT_BEGIN (offscreen, 3, 0, COGL_EXT_IN_GLES2, /* for some reason the ARB version of this extension doesn't have an ARB suffix for the functions */ "ARB:\0EXT\0OES\0", "framebuffer_object\0") COGL_EXT_FUNCTION (void, glGenRenderbuffers, (GLsizei n, GLuint *renderbuffers)) COGL_EXT_FUNCTION (void, glDeleteRenderbuffers, (GLsizei n, const GLuint *renderbuffers)) COGL_EXT_FUNCTION (void, glBindRenderbuffer, (GLenum target, GLuint renderbuffer)) COGL_EXT_FUNCTION (void, glRenderbufferStorage, (GLenum target, GLenum internalformat, GLsizei width, GLsizei height)) COGL_EXT_FUNCTION (void, glGenFramebuffers, (GLsizei n, GLuint *framebuffers)) COGL_EXT_FUNCTION (void, glBindFramebuffer, (GLenum target, GLuint framebuffer)) COGL_EXT_FUNCTION (void, glFramebufferTexture2D, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level)) COGL_EXT_FUNCTION (void, glFramebufferRenderbuffer, (GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)) COGL_EXT_FUNCTION (GLenum, glCheckFramebufferStatus, (GLenum target)) COGL_EXT_FUNCTION (void, glDeleteFramebuffers, (GLsizei n, const GLuint *framebuffers)) COGL_EXT_FUNCTION (void, glGenerateMipmap, (GLenum target)) COGL_EXT_FUNCTION (void, glGetFramebufferAttachmentParameteriv, (GLenum target, GLenum attachment, GLenum pname, GLint *params)) COGL_EXT_END () COGL_EXT_BEGIN (blending, 1, 2, COGL_EXT_IN_GLES2, "\0", "\0") COGL_EXT_FUNCTION (void, glBlendColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)) COGL_EXT_END () /* Optional, declared in 1.4 or GLES 1.2 */ COGL_EXT_BEGIN (blend_func_separate, 1, 4, COGL_EXT_IN_GLES2, "EXT\0", "blend_func_separate\0") COGL_EXT_FUNCTION (void, glBlendFuncSeparate, (GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha)) COGL_EXT_END () /* Optional, declared in 2.0 */ COGL_EXT_BEGIN (blend_equation_separate, 2, 0, COGL_EXT_IN_GLES2, "EXT\0", "blend_equation_separate\0") COGL_EXT_FUNCTION (void, glBlendEquationSeparate, (GLenum modeRGB, GLenum modeAlpha)) COGL_EXT_END () muffin-6.4.1/cogl/cogl/gl-prototypes/cogl-core-functions.h0000664000175000017500000001641014723361714022475 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is included multiple times with different definitions for * these macros. The macros are given the following arguments: * * COGL_EXT_BEGIN: * * @name: a unique symbol name for this feature * * @min_gl_major: the major part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * @min_gl_minor: the minor part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * * @gles_availability: flags to specify which versions of GLES the * functions are available in. Should be a combination of * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. * * @extension_suffixes: A zero-separated list of suffixes in a * string. These are appended to the extension name to get a complete * extension name to try. The suffix is also appended to all of the * function names. The suffix can optionally include a ':' to specify * an alternate suffix for the function names. * * @extension_names: A list of extension names to try. If any of these * extensions match then it will be used. */ /* These are the core GL functions which we assume will always be available */ COGL_EXT_BEGIN (core, 0, 0, COGL_EXT_IN_GLES2, "\0", "\0") COGL_EXT_FUNCTION (void, glActiveTexture, (GLenum texture)) COGL_EXT_FUNCTION (void, glBindBuffer, (GLenum target, GLuint buffer)) COGL_EXT_FUNCTION (void, glBindTexture, (GLenum target, GLuint texture)) COGL_EXT_FUNCTION (void, glBufferData, (GLenum target, GLsizeiptr size, const GLvoid *data, GLenum usage)) COGL_EXT_FUNCTION (void, glBufferSubData, (GLenum target, GLintptr offset, GLsizeiptr size, const GLvoid *data)) COGL_EXT_FUNCTION (void, glClear, (GLbitfield mask)) COGL_EXT_FUNCTION (void, glClearColor, (GLclampf red, GLclampf green, GLclampf blue, GLclampf alpha)) COGL_EXT_FUNCTION (void, glClearStencil, (GLint s)) COGL_EXT_FUNCTION (void, glColorMask, (GLboolean red, GLboolean green, GLboolean blue, GLboolean alpha)) COGL_EXT_FUNCTION (void, glCopyTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height)) COGL_EXT_FUNCTION (void, glDeleteBuffers, (GLsizei n, const GLuint *buffers)) COGL_EXT_FUNCTION (void, glDeleteTextures, (GLsizei n, const GLuint* textures)) COGL_EXT_FUNCTION (void, glDepthFunc, (GLenum func)) COGL_EXT_FUNCTION (void, glDepthMask, (GLboolean flag)) COGL_EXT_FUNCTION (void, glDisable, (GLenum cap)) COGL_EXT_FUNCTION (void, glDrawArrays, (GLenum mode, GLint first, GLsizei count)) COGL_EXT_FUNCTION (void, glDrawElements, (GLenum mode, GLsizei count, GLenum type, const GLvoid* indices)) COGL_EXT_FUNCTION (void, glEnable, (GLenum cap)) COGL_EXT_FUNCTION (void, glFinish, (void)) COGL_EXT_FUNCTION (void, glFlush, (void)) COGL_EXT_FUNCTION (void, glFrontFace, (GLenum mode)) COGL_EXT_FUNCTION (void, glCullFace, (GLenum mode)) COGL_EXT_FUNCTION (void, glGenBuffers, (GLsizei n, GLuint *buffers)) COGL_EXT_FUNCTION (void, glGenTextures, (GLsizei n, GLuint* textures)) COGL_EXT_FUNCTION (GLenum, glGetError, (void)) COGL_EXT_FUNCTION (void, glGetIntegerv, (GLenum pname, GLint* params)) COGL_EXT_FUNCTION (const GLubyte*, glGetString, (GLenum name)) COGL_EXT_FUNCTION (GLboolean, glIsTexture, (GLuint texture)) COGL_EXT_FUNCTION (void, glPixelStorei, (GLenum pname, GLint param)) COGL_EXT_FUNCTION (void, glReadPixels, (GLint x, GLint y, GLsizei width, GLsizei height, GLenum format, GLenum type, GLvoid* pixels)) COGL_EXT_FUNCTION (void, glScissor, (GLint x, GLint y, GLsizei width, GLsizei height)) COGL_EXT_FUNCTION (void, glStencilFunc, (GLenum func, GLint ref, GLuint mask)) COGL_EXT_FUNCTION (void, glStencilMask, (GLuint mask)) COGL_EXT_FUNCTION (void, glStencilOp, (GLenum fail, GLenum zfail, GLenum zpass)) COGL_EXT_FUNCTION (void, glTexImage2D, (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)) COGL_EXT_FUNCTION (void, glTexParameteri, (GLenum target, GLenum pname, GLint param)) COGL_EXT_FUNCTION (void, glTexParameteriv, (GLenum target, GLenum pname, const GLint* params)) COGL_EXT_FUNCTION (void, glTexSubImage2D, (GLenum target, GLint level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height, GLenum format, GLenum type, const GLvoid* pixels)) COGL_EXT_FUNCTION (void, glViewport, (GLint x, GLint y, GLsizei width, GLsizei height)) COGL_EXT_END () muffin-6.4.1/cogl/cogl/gl-prototypes/cogl-gles2-functions.h0000664000175000017500000000326214723361714022562 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* The functions in this file are part of the core GL,GLES1 and GLES2 apis */ #include "cogl-core-functions.h" /* The functions in this file are core to GLES1 and GLES2 but not core * to GL but they may be extensions available for GL */ #include "cogl-in-gles-core-functions.h" /* The functions in this file are core to GLES2 only but * may be extensions for GLES1 and GL */ #include "cogl-in-gles2-core-functions.h" /* These are APIs for using GLSL used by GL and GLES2 */ #include "cogl-glsl-functions.h" muffin-6.4.1/cogl/cogl/gl-prototypes/cogl-all-functions.h0000664000175000017500000002143114723361714022314 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is included multiple times with different definitions for * these macros. The macros are given the following arguments: * * COGL_EXT_BEGIN: * * @name: a unique symbol name for this feature * * @min_gl_major: the major part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * @min_gl_minor: the minor part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * * @gles_availability: flags to specify which versions of GLES the * functions are available in. Should be a combination of * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. * * @extension_suffixes: A zero-separated list of suffixes in a * string. These are appended to the extension name to get a complete * extension name to try. The suffix is also appended to all of the * function names. The suffix can optionally include a ':' to specify * an alternate suffix for the function names. * * @extension_names: A list of extension names to try. If any of these * extensions match then it will be used. */ /* The functions in this file are part of the core GL,GLES1 and GLES2 apis */ #include "cogl-core-functions.h" /* The functions in this file are core to GLES2 only but * may be extensions for GLES1 and GL */ #include "cogl-in-gles2-core-functions.h" /* The functions in this file are core to GLES1 and GLES2 but not core * to GL but they may be extensions available for GL */ #include "cogl-in-gles-core-functions.h" /* These are GLSL shader APIs core to GL 2.0 and GLES2 */ #include "cogl-glsl-functions.h" /* These are the core GL functions which are only available in big GL */ COGL_EXT_BEGIN (only_in_big_gl, 0, 0, 0, /* not in GLES */ "\0", "\0") COGL_EXT_FUNCTION (void, glGetTexLevelParameteriv, (GLenum target, GLint level, GLenum pname, GLint *params)) COGL_EXT_FUNCTION (void, glGetTexImage, (GLenum target, GLint level, GLenum format, GLenum type, GLvoid *pixels)) COGL_EXT_FUNCTION (void, glDepthRange, (double near_val, double far_val)) COGL_EXT_FUNCTION (void, glDrawBuffer, (GLenum mode)) COGL_EXT_END () /* GLES doesn't support mapping buffers in core so this has to be a separate check */ COGL_EXT_BEGIN (map_vbos, 1, 5, 0, /* not in GLES core */ "ARB\0OES\0", "vertex_buffer_object\0mapbuffer\0") COGL_EXT_FUNCTION (void *, glMapBuffer, (GLenum target, GLenum access)) COGL_EXT_FUNCTION (GLboolean, glUnmapBuffer, (GLenum target)) COGL_EXT_END () COGL_EXT_BEGIN (offscreen_blit, 3, 0, COGL_EXT_IN_GLES3, "EXT\0NV\0", "framebuffer_blit\0") COGL_EXT_FUNCTION (void, glBlitFramebuffer, (GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter)) COGL_EXT_END () COGL_EXT_BEGIN (EGL_image, 255, 255, 0, /* not in either GLES */ "OES\0", "EGL_image\0") COGL_EXT_FUNCTION (void, glEGLImageTargetTexture2D, (GLenum target, GLeglImageOES image)) COGL_EXT_END () COGL_EXT_BEGIN (framebuffer_discard, 255, 255, 0, /* not in either GLES */ "EXT\0", "framebuffer_discard\0") COGL_EXT_FUNCTION (void, glDiscardFramebuffer, (GLenum target, GLsizei numAttachments, const GLenum *attachments)) COGL_EXT_END () COGL_EXT_BEGIN (IMG_multisampled_render_to_texture, 255, 255, 0, /* not in either GLES */ "\0", "IMG_multisampled_render_to_texture\0") COGL_EXT_FUNCTION (void, glRenderbufferStorageMultisampleIMG, (GLenum target, GLsizei samples, GLenum internal_format, GLsizei width, GLsizei height)) COGL_EXT_FUNCTION (void, glFramebufferTexture2DMultisampleIMG, (GLenum target, GLenum attachment, GLenum textarget, GLuint texture, GLint level, GLsizei samples)) COGL_EXT_END () COGL_EXT_BEGIN (ARB_sampler_objects, 3, 3, COGL_EXT_IN_GLES3, "ARB:\0", "sampler_objects\0") COGL_EXT_FUNCTION (void, glGenSamplers, (GLsizei count, GLuint *samplers)) COGL_EXT_FUNCTION (void, glDeleteSamplers, (GLsizei count, const GLuint *samplers)) COGL_EXT_FUNCTION (void, glBindSampler, (GLuint unit, GLuint sampler)) COGL_EXT_FUNCTION (void, glSamplerParameteri, (GLuint sampler, GLenum pname, GLint param)) COGL_EXT_END () COGL_EXT_BEGIN (only_gl3, 3, 0, COGL_EXT_IN_GLES3, "\0", "\0") COGL_EXT_FUNCTION (const GLubyte *, glGetStringi, (GLenum name, GLuint index)) COGL_EXT_END () COGL_EXT_BEGIN (vertex_array_object, 3, 0, COGL_EXT_IN_GLES3, "ARB\0OES\0", "vertex_array_object\0") COGL_EXT_FUNCTION (void, glBindVertexArray, (GLuint array)) COGL_EXT_FUNCTION (void, glGenVertexArrays, (GLsizei n, GLuint *arrays)) COGL_EXT_END () COGL_EXT_BEGIN (map_region, 3, 0, COGL_EXT_IN_GLES3, "ARB:\0", "map_buffer_range\0") COGL_EXT_FUNCTION (GLvoid *, glMapBufferRange, (GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access)) COGL_EXT_END () #ifdef GL_ARB_sync COGL_EXT_BEGIN (sync, 3, 2, COGL_EXT_IN_GLES3, "ARB:\0", "sync\0") COGL_EXT_FUNCTION (GLsync, glFenceSync, (GLenum condition, GLbitfield flags)) COGL_EXT_FUNCTION (GLenum, glClientWaitSync, (GLsync sync, GLbitfield flags, GLuint64 timeout)) COGL_EXT_FUNCTION (void, glDeleteSync, (GLsync sync)) COGL_EXT_END () #endif COGL_EXT_BEGIN (draw_buffers, 2, 0, COGL_EXT_IN_GLES3, "ARB\0EXT\0", "draw_buffers\0") COGL_EXT_FUNCTION (void, glDrawBuffers, (GLsizei n, const GLenum *bufs)) COGL_EXT_END () COGL_EXT_BEGIN (robustness, 255, 255, 0, "ARB\0", "robustness\0") COGL_EXT_FUNCTION (GLenum, glGetGraphicsResetStatus, (void)) COGL_EXT_END () COGL_EXT_BEGIN (multitexture_part1, 1, 3, 0, "ARB\0", "multitexture\0") COGL_EXT_FUNCTION (void, glClientActiveTexture, (GLenum texture)) COGL_EXT_END () muffin-6.4.1/cogl/cogl/gl-prototypes/cogl-glsl-functions.h0000664000175000017500000002127114723361714022507 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009, 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is included multiple times with different definitions for * these macros. The macros are given the following arguments: * * COGL_EXT_BEGIN: * * @name: a unique symbol name for this feature * * @min_gl_major: the major part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * @min_gl_minor: the minor part of the minimum GL version where these * functions are available in core, or 255 if it isn't available in * any version. * * @gles_availability: flags to specify which versions of GLES the * functions are available in. Should be a combination of * COGL_EXT_IN_GLES and COGL_EXT_IN_GLES2. * * @extension_suffixes: A zero-separated list of suffixes in a * string. These are appended to the extension name to get a complete * extension name to try. The suffix is also appended to all of the * function names. The suffix can optionally include a ':' to specify * an alternate suffix for the function names. * * @extension_names: A list of extension names to try. If any of these * extensions match then it will be used. */ /* This lists functions that are unique to GL 2.0 or GLES 2.0 and are * not in the old GLSL extensions */ COGL_EXT_BEGIN (shaders_glsl_2_only, 2, 0, COGL_EXT_IN_GLES2, "\0", "\0") COGL_EXT_FUNCTION (GLuint, glCreateProgram, (void)) COGL_EXT_FUNCTION (GLuint, glCreateShader, (GLenum shaderType)) COGL_EXT_FUNCTION (void, glDeleteShader, (GLuint shader)) COGL_EXT_FUNCTION (void, glAttachShader, (GLuint program, GLuint shader)) COGL_EXT_FUNCTION (void, glUseProgram, (GLuint program)) COGL_EXT_FUNCTION (void, glDeleteProgram, (GLuint program)) COGL_EXT_FUNCTION (void, glGetShaderInfoLog, (GLuint shader, GLsizei maxLength, GLsizei *length, char *infoLog)) COGL_EXT_FUNCTION (void, glGetProgramInfoLog, (GLuint program, GLsizei bufSize, GLsizei *length, char *infoLog)) COGL_EXT_FUNCTION (void, glGetShaderiv, (GLuint shader, GLenum pname, GLint *params)) COGL_EXT_FUNCTION (void, glGetProgramiv, (GLuint program, GLenum pname, GLint *params)) COGL_EXT_END () /* These functions are provided by GL_ARB_shader_objects or are in GL * 2.0 core */ COGL_EXT_BEGIN (shader_objects_or_gl2, 2, 0, COGL_EXT_IN_GLES2, "ARB\0", "shader_objects\0") COGL_EXT_FUNCTION (void, glShaderSource, (GLuint shader, GLsizei count, const char * const *string, const GLint *length)) COGL_EXT_FUNCTION (void, glCompileShader, (GLuint shader)) COGL_EXT_FUNCTION (void, glLinkProgram, (GLuint program)) COGL_EXT_FUNCTION (GLint, glGetUniformLocation, (GLuint program, const char *name)) COGL_EXT_FUNCTION (void, glUniform1f, (GLint location, GLfloat v0)) COGL_EXT_FUNCTION (void, glUniform1fv, (GLint location, GLsizei count, const GLfloat * value)) COGL_EXT_FUNCTION (void, glUniform2fv, (GLint location, GLsizei count, const GLfloat * value)) COGL_EXT_FUNCTION (void, glUniform3fv, (GLint location, GLsizei count, const GLfloat * value)) COGL_EXT_FUNCTION (void, glUniform4fv, (GLint location, GLsizei count, const GLfloat * value)) COGL_EXT_FUNCTION (void, glUniform1i, (GLint location, GLint v0)) COGL_EXT_FUNCTION (void, glUniform1iv, (GLint location, GLsizei count, const GLint * value)) COGL_EXT_FUNCTION (void, glUniform2iv, (GLint location, GLsizei count, const GLint * value)) COGL_EXT_FUNCTION (void, glUniform3iv, (GLint location, GLsizei count, const GLint * value)) COGL_EXT_FUNCTION (void, glUniform4iv, (GLint location, GLsizei count, const GLint * value)) COGL_EXT_FUNCTION (void, glUniformMatrix2fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)) COGL_EXT_FUNCTION (void, glUniformMatrix3fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)) COGL_EXT_FUNCTION (void, glUniformMatrix4fv, (GLint location, GLsizei count, GLboolean transpose, const GLfloat *value)) COGL_EXT_END () /* These functions are provided by GL_ARB_vertex_shader or are in GL * 2.0 core */ COGL_EXT_BEGIN (vertex_shaders, 2, 0, COGL_EXT_IN_GLES2, "ARB\0", "vertex_shader\0") COGL_EXT_FUNCTION (void, glVertexAttribPointer, (GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const GLvoid *pointer)) COGL_EXT_FUNCTION (void, glEnableVertexAttribArray, (GLuint index)) COGL_EXT_FUNCTION (void, glDisableVertexAttribArray, (GLuint index)) COGL_EXT_FUNCTION (void, glVertexAttrib1fv, (GLuint indx, const GLfloat* values)) COGL_EXT_FUNCTION (void, glVertexAttrib2fv, (GLuint indx, const GLfloat* values)) COGL_EXT_FUNCTION (void, glVertexAttrib3fv, (GLuint indx, const GLfloat* values)) COGL_EXT_FUNCTION (void, glVertexAttrib4f, (GLuint index, GLfloat x, GLfloat y, GLfloat z, GLfloat w)) COGL_EXT_FUNCTION (void, glVertexAttrib4fv, (GLuint indx, const GLfloat* values)) COGL_EXT_FUNCTION (GLint, glGetAttribLocation, (GLuint program, const char *name)) COGL_EXT_FUNCTION (void, glBindAttribLocation, (GLuint program, GLuint index, const GLchar* name)) COGL_EXT_END () muffin-6.4.1/cogl/cogl/cogl-pixel-buffer.h0000664000175000017500000000615314723361714017302 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PIXEL_BUFFER_H__ #define __COGL_PIXEL_BUFFER_H__ /* XXX: We forward declare CoglPixelBuffer here to allow for circular * dependencies between some headers */ typedef struct _CoglPixelBuffer CoglPixelBuffer; #include #include #include G_BEGIN_DECLS #define COGL_PIXEL_BUFFER(buffer) ((CoglPixelBuffer *)(buffer)) /** * CoglPixelBuffer: (skip) */ /** * cogl_pixel_buffer_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_pixel_buffer_get_gtype (void); /** * cogl_pixel_buffer_new: * @context: A #CoglContext * @size: The number of bytes to allocate for the pixel data. * @data: An optional pointer to vertex data to upload immediately * * Declares a new #CoglPixelBuffer of @size bytes to contain arrays of * pixels. Once declared, data can be set using cogl_buffer_set_data() * or by mapping it into the application's address space using * cogl_buffer_map(). * * If @data isn't %NULL then @size bytes will be read from @data and * immediately copied into the new buffer. * * Return value: (transfer full): a newly allocated #CoglPixelBuffer * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglPixelBuffer * cogl_pixel_buffer_new (CoglContext *context, size_t size, const void *data); /** * cogl_is_pixel_buffer: * @object: a #CoglObject to test * * Checks whether @object is a pixel buffer. * * Return value: %TRUE if the @object is a pixel buffer, and %FALSE * otherwise * * Since: 1.2 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_pixel_buffer (void *object); G_END_DECLS #endif /* __COGL_PIXEL_BUFFER_H__ */ muffin-6.4.1/cogl/cogl/cogl-buffer.c0000664000175000017500000002554014723361714016157 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Damien Lespiau * Robert Bragg */ /* For an overview of the functionality implemented here, please see * cogl-buffer.h, which contains the gtk-doc section overview for the * Pixel Buffers API. */ #include "cogl-config.h" #include #include #include #include "cogl-util.h" #include "cogl-context-private.h" #include "cogl-object-private.h" #include "cogl-pixel-buffer-private.h" /* XXX: * The CoglObject macros don't support any form of inheritance, so for * now we implement the CoglObject support for the CoglBuffer * abstract class manually. */ static GSList *_cogl_buffer_types; void _cogl_buffer_register_buffer_type (const CoglObjectClass *klass) { _cogl_buffer_types = g_slist_prepend (_cogl_buffer_types, (void *) klass); } gboolean cogl_is_buffer (void *object) { const CoglObject *obj = object; GSList *l; if (object == NULL) return FALSE; for (l = _cogl_buffer_types; l; l = l->next) if (l->data == obj->klass) return TRUE; return FALSE; } /* * Fallback path, buffer->data points to a malloc'ed buffer. */ static void * malloc_map_range (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { buffer->flags |= COGL_BUFFER_FLAG_MAPPED; return buffer->data + offset; } static void malloc_unmap (CoglBuffer *buffer) { buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED; } static gboolean malloc_set_data (CoglBuffer *buffer, unsigned int offset, const void *data, unsigned int size, GError **error) { memcpy (buffer->data + offset, data, size); return TRUE; } void _cogl_buffer_initialize (CoglBuffer *buffer, CoglContext *ctx, size_t size, CoglBufferBindTarget default_target, CoglBufferUsageHint usage_hint, CoglBufferUpdateHint update_hint) { gboolean use_malloc = FALSE; buffer->context = ctx; buffer->flags = COGL_BUFFER_FLAG_NONE; buffer->store_created = FALSE; buffer->size = size; buffer->last_target = default_target; buffer->usage_hint = usage_hint; buffer->update_hint = update_hint; buffer->data = NULL; buffer->immutable_ref = 0; if (default_target == COGL_BUFFER_BIND_TARGET_PIXEL_PACK || default_target == COGL_BUFFER_BIND_TARGET_PIXEL_UNPACK) { if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_PBOS)) use_malloc = TRUE; } if (use_malloc) { buffer->vtable.map_range = malloc_map_range; buffer->vtable.unmap = malloc_unmap; buffer->vtable.set_data = malloc_set_data; buffer->data = g_malloc (size); } else { buffer->vtable.map_range = ctx->driver_vtable->buffer_map_range; buffer->vtable.unmap = ctx->driver_vtable->buffer_unmap; buffer->vtable.set_data = ctx->driver_vtable->buffer_set_data; ctx->driver_vtable->buffer_create (buffer); buffer->flags |= COGL_BUFFER_FLAG_BUFFER_OBJECT; } } void _cogl_buffer_fini (CoglBuffer *buffer) { g_return_if_fail (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED)); g_return_if_fail (buffer->immutable_ref == 0); if (buffer->flags & COGL_BUFFER_FLAG_BUFFER_OBJECT) buffer->context->driver_vtable->buffer_destroy (buffer); else g_free (buffer->data); } unsigned int cogl_buffer_get_size (CoglBuffer *buffer) { if (!cogl_is_buffer (buffer)) return 0; return COGL_BUFFER (buffer)->size; } void cogl_buffer_set_update_hint (CoglBuffer *buffer, CoglBufferUpdateHint hint) { if (!cogl_is_buffer (buffer)) return; if (G_UNLIKELY (hint > COGL_BUFFER_UPDATE_HINT_STREAM)) hint = COGL_BUFFER_UPDATE_HINT_STATIC; buffer->update_hint = hint; } CoglBufferUpdateHint cogl_buffer_get_update_hint (CoglBuffer *buffer) { if (!cogl_is_buffer (buffer)) return FALSE; return buffer->update_hint; } static void warn_about_midscene_changes (void) { static gboolean seen = FALSE; if (!seen) { g_warning ("Mid-scene modification of buffers has " "undefined results\n"); seen = TRUE; } } void * _cogl_buffer_map (CoglBuffer *buffer, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { g_return_val_if_fail (cogl_is_buffer (buffer), NULL); return cogl_buffer_map_range (buffer, 0, buffer->size, access, hints, error); } void * cogl_buffer_map (CoglBuffer *buffer, CoglBufferAccess access, CoglBufferMapHint hints) { GError *ignore_error = NULL; void *ptr = cogl_buffer_map_range (buffer, 0, buffer->size, access, hints, &ignore_error); g_clear_error (&ignore_error); return ptr; } void * cogl_buffer_map_range (CoglBuffer *buffer, size_t offset, size_t size, CoglBufferAccess access, CoglBufferMapHint hints, GError **error) { g_return_val_if_fail (cogl_is_buffer (buffer), NULL); g_return_val_if_fail (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED), NULL); if (G_UNLIKELY (buffer->immutable_ref)) warn_about_midscene_changes (); buffer->data = buffer->vtable.map_range (buffer, offset, size, access, hints, error); return buffer->data; } void cogl_buffer_unmap (CoglBuffer *buffer) { if (!cogl_is_buffer (buffer)) return; if (!(buffer->flags & COGL_BUFFER_FLAG_MAPPED)) return; buffer->vtable.unmap (buffer); } void * _cogl_buffer_map_for_fill_or_fallback (CoglBuffer *buffer) { return _cogl_buffer_map_range_for_fill_or_fallback (buffer, 0, buffer->size); } void * _cogl_buffer_map_range_for_fill_or_fallback (CoglBuffer *buffer, size_t offset, size_t size) { CoglContext *ctx = buffer->context; void *ret; GError *ignore_error = NULL; g_return_val_if_fail (!ctx->buffer_map_fallback_in_use, NULL); ctx->buffer_map_fallback_in_use = TRUE; ret = cogl_buffer_map_range (buffer, offset, size, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, &ignore_error); if (ret) return ret; g_error_free (ignore_error); /* If the map fails then we'll use a temporary buffer to fill the data and then upload it using cogl_buffer_set_data when the buffer is unmapped. The temporary buffer is shared to avoid reallocating it every time */ g_byte_array_set_size (ctx->buffer_map_fallback_array, size); ctx->buffer_map_fallback_offset = offset; buffer->flags |= COGL_BUFFER_FLAG_MAPPED_FALLBACK; return ctx->buffer_map_fallback_array->data; } void _cogl_buffer_unmap_for_fill_or_fallback (CoglBuffer *buffer) { CoglContext *ctx = buffer->context; g_return_if_fail (ctx->buffer_map_fallback_in_use); ctx->buffer_map_fallback_in_use = FALSE; if ((buffer->flags & COGL_BUFFER_FLAG_MAPPED_FALLBACK)) { /* Note: don't try to catch OOM errors here since the use cases * we currently have for this api (the journal and path stroke * tesselator) don't have anything particularly sensible they * can do in response to a failure anyway so it seems better to * simply abort instead. * * If we find this is a problem for real world applications * then in the path tesselation case we could potentially add an * explicit cogl_path_tesselate_stroke() api that can throw an * error for the app to cache. For the journal we could * potentially flush the journal in smaller batches so we use * smaller buffers, though that would probably not help for * deferred renderers. */ _cogl_buffer_set_data (buffer, ctx->buffer_map_fallback_offset, ctx->buffer_map_fallback_array->data, ctx->buffer_map_fallback_array->len, NULL); buffer->flags &= ~COGL_BUFFER_FLAG_MAPPED_FALLBACK; } else cogl_buffer_unmap (buffer); } gboolean _cogl_buffer_set_data (CoglBuffer *buffer, size_t offset, const void *data, size_t size, GError **error) { g_return_val_if_fail (cogl_is_buffer (buffer), FALSE); g_return_val_if_fail ((offset + size) <= buffer->size, FALSE); if (G_UNLIKELY (buffer->immutable_ref)) warn_about_midscene_changes (); return buffer->vtable.set_data (buffer, offset, data, size, error); } gboolean cogl_buffer_set_data (CoglBuffer *buffer, size_t offset, const void *data, size_t size) { GError *ignore_error = NULL; gboolean status = _cogl_buffer_set_data (buffer, offset, data, size, &ignore_error); g_clear_error (&ignore_error); return status; } CoglBuffer * _cogl_buffer_immutable_ref (CoglBuffer *buffer) { g_return_val_if_fail (cogl_is_buffer (buffer), NULL); buffer->immutable_ref++; return buffer; } void _cogl_buffer_immutable_unref (CoglBuffer *buffer) { g_return_if_fail (cogl_is_buffer (buffer)); g_return_if_fail (buffer->immutable_ref > 0); buffer->immutable_ref--; } muffin-6.4.1/cogl/cogl/cogl-indices.c0000664000175000017500000001645114723361714016325 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg * Neil Roberts */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-object-private.h" #include "cogl-context-private.h" #include "cogl-indices.h" #include "cogl-indices-private.h" #include "cogl-index-buffer.h" #include "cogl-gtype-private.h" #include static void _cogl_indices_free (CoglIndices *indices); COGL_OBJECT_DEFINE (Indices, indices); COGL_GTYPE_DEFINE_CLASS (Indices, indices); static size_t sizeof_indices_type (CoglIndicesType type) { switch (type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: return 1; case COGL_INDICES_TYPE_UNSIGNED_SHORT: return 2; case COGL_INDICES_TYPE_UNSIGNED_INT: return 4; } g_return_val_if_reached (0); } CoglIndices * cogl_indices_new_for_buffer (CoglIndicesType type, CoglIndexBuffer *buffer, size_t offset) { CoglIndices *indices = g_slice_new (CoglIndices); indices->buffer = cogl_object_ref (buffer); indices->offset = offset; indices->type = type; indices->immutable_ref = 0; return _cogl_indices_object_new (indices); } CoglIndices * cogl_indices_new (CoglContext *context, CoglIndicesType type, const void *indices_data, int n_indices) { size_t buffer_bytes = sizeof_indices_type (type) * n_indices; CoglIndexBuffer *index_buffer = cogl_index_buffer_new (context, buffer_bytes); CoglBuffer *buffer = COGL_BUFFER (index_buffer); CoglIndices *indices; GError *ignore_error = NULL; _cogl_buffer_set_data (buffer, 0, indices_data, buffer_bytes, &ignore_error); if (ignore_error) { g_error_free (ignore_error); cogl_object_unref (index_buffer); return NULL; } indices = cogl_indices_new_for_buffer (type, index_buffer, 0); cogl_object_unref (index_buffer); return indices; } CoglIndexBuffer * cogl_indices_get_buffer (CoglIndices *indices) { return indices->buffer; } CoglIndicesType cogl_indices_get_type (CoglIndices *indices) { g_return_val_if_fail (cogl_is_indices (indices), COGL_INDICES_TYPE_UNSIGNED_BYTE); return indices->type; } size_t cogl_indices_get_offset (CoglIndices *indices) { g_return_val_if_fail (cogl_is_indices (indices), 0); return indices->offset; } static void warn_about_midscene_changes (void) { static gboolean seen = FALSE; if (!seen) { g_warning ("Mid-scene modification of indices has " "undefined results\n"); seen = TRUE; } } void cogl_indices_set_offset (CoglIndices *indices, size_t offset) { g_return_if_fail (cogl_is_indices (indices)); if (G_UNLIKELY (indices->immutable_ref)) warn_about_midscene_changes (); indices->offset = offset; } static void _cogl_indices_free (CoglIndices *indices) { cogl_object_unref (indices->buffer); g_slice_free (CoglIndices, indices); } CoglIndices * _cogl_indices_immutable_ref (CoglIndices *indices) { g_return_val_if_fail (cogl_is_indices (indices), NULL); indices->immutable_ref++; _cogl_buffer_immutable_ref (COGL_BUFFER (indices->buffer)); return indices; } void _cogl_indices_immutable_unref (CoglIndices *indices) { g_return_if_fail (cogl_is_indices (indices)); g_return_if_fail (indices->immutable_ref > 0); indices->immutable_ref--; _cogl_buffer_immutable_unref (COGL_BUFFER (indices->buffer)); } CoglIndices * cogl_get_rectangle_indices (CoglContext *ctx, int n_rectangles) { int n_indices = n_rectangles * 6; /* Check if the largest index required will fit in a byte array... */ if (n_indices <= 256 / 4 * 6) { /* Generate the byte array if we haven't already */ if (ctx->rectangle_byte_indices == NULL) { uint8_t *byte_array = g_malloc (256 / 4 * 6 * sizeof (uint8_t)); uint8_t *p = byte_array; int i, vert_num = 0; for (i = 0; i < 256 / 4; i++) { *(p++) = vert_num + 0; *(p++) = vert_num + 1; *(p++) = vert_num + 2; *(p++) = vert_num + 0; *(p++) = vert_num + 2; *(p++) = vert_num + 3; vert_num += 4; } ctx->rectangle_byte_indices = cogl_indices_new (ctx, COGL_INDICES_TYPE_UNSIGNED_BYTE, byte_array, 256 / 4 * 6); g_free (byte_array); } return ctx->rectangle_byte_indices; } else { if (ctx->rectangle_short_indices_len < n_indices) { uint16_t *short_array; uint16_t *p; int i, vert_num = 0; if (ctx->rectangle_short_indices != NULL) cogl_object_unref (ctx->rectangle_short_indices); /* Pick a power of two >= MAX (512, n_indices) */ if (ctx->rectangle_short_indices_len == 0) ctx->rectangle_short_indices_len = 512; while (ctx->rectangle_short_indices_len < n_indices) ctx->rectangle_short_indices_len *= 2; /* Over-allocate to generate a whole number of quads */ p = short_array = g_malloc ((ctx->rectangle_short_indices_len + 5) / 6 * 6 * sizeof (uint16_t)); /* Fill in the complete quads */ for (i = 0; i < ctx->rectangle_short_indices_len; i += 6) { *(p++) = vert_num + 0; *(p++) = vert_num + 1; *(p++) = vert_num + 2; *(p++) = vert_num + 0; *(p++) = vert_num + 2; *(p++) = vert_num + 3; vert_num += 4; } ctx->rectangle_short_indices = cogl_indices_new (ctx, COGL_INDICES_TYPE_UNSIGNED_SHORT, short_array, ctx->rectangle_short_indices_len); g_free (short_array); } return ctx->rectangle_short_indices; } } muffin-6.4.1/cogl/cogl/cogl-texture.h0000664000175000017500000004146414723361714016416 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_TEXTURE_H__ #define __COGL_TEXTURE_H__ /* We forward declare the CoglTexture type here to avoid some circular * dependency issues with the following headers. */ #if defined(__COGL_H_INSIDE__) && !defined(COGL_ENABLE_MUTTER_API) && \ !defined(COGL_GIR_SCANNING) /* For the public C api we typedef interface types as void to avoid needing * lots of casting in code and instead we will rely on runtime type checking * for these objects. */ typedef void CoglTexture; #else typedef struct _CoglTexture CoglTexture; #define COGL_TEXTURE(X) ((CoglTexture *)X) #endif #include #include #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-texture * @short_description: Functions for creating and manipulating textures * * Cogl allows creating and manipulating textures using a uniform * API that tries to hide all the various complexities of creating, * loading and manipulating textures. */ #define COGL_TEXTURE_MAX_WASTE 127 /** * cogl_texture_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_texture_get_gtype (void); /** * COGL_TEXTURE_ERROR: * * #GError domain for texture errors. * * Since: 1.8 * Stability: Unstable */ #define COGL_TEXTURE_ERROR (cogl_texture_error_quark ()) /** * CoglTextureError: * @COGL_TEXTURE_ERROR_SIZE: Unsupported size * @COGL_TEXTURE_ERROR_FORMAT: Unsupported format * @COGL_TEXTURE_ERROR_TYPE: A primitive texture type that is * unsupported by the driver was used * * Error codes that can be thrown when allocating textures. * * Since: 1.8 * Stability: Unstable */ typedef enum { COGL_TEXTURE_ERROR_SIZE, COGL_TEXTURE_ERROR_FORMAT, COGL_TEXTURE_ERROR_BAD_PARAMETER, COGL_TEXTURE_ERROR_TYPE } CoglTextureError; COGL_EXPORT uint32_t cogl_texture_error_quark (void); /** * cogl_is_texture: * @object: A #CoglObject pointer * * Gets whether the given object references a texture object. * * Return value: %TRUE if the @object references a texture, and * %FALSE otherwise */ COGL_EXPORT gboolean cogl_is_texture (void *object); /** * CoglTextureComponents: * @COGL_TEXTURE_COMPONENTS_A: Only the alpha component * @COGL_TEXTURE_COMPONENTS_RG: Red and green components. Note that * this can only be used if the %COGL_FEATURE_ID_TEXTURE_RG feature * is advertised. * @COGL_TEXTURE_COMPONENTS_RGB: Red, green and blue components * @COGL_TEXTURE_COMPONENTS_RGBA: Red, green, blue and alpha components * @COGL_TEXTURE_COMPONENTS_DEPTH: Only a depth component * * See cogl_texture_set_components(). * * Since: 1.18 */ typedef enum _CoglTextureComponents { COGL_TEXTURE_COMPONENTS_A = 1, COGL_TEXTURE_COMPONENTS_RG, COGL_TEXTURE_COMPONENTS_RGB, COGL_TEXTURE_COMPONENTS_RGBA, COGL_TEXTURE_COMPONENTS_DEPTH } CoglTextureComponents; /** * cogl_texture_set_components: * @texture: a #CoglTexture pointer. * * Affects the internal storage format for this texture by specifying * what components will be required for sampling later. * * This api affects how data is uploaded to the GPU since unused * components can potentially be discarded from source data. * * For textures created by the ‘_with_size’ constructors the default * is %COGL_TEXTURE_COMPONENTS_RGBA. The other constructors which take * a %CoglBitmap or a data pointer default to the same components as * the pixel format of the data. * * Note that the %COGL_TEXTURE_COMPONENTS_RG format is not available * on all drivers. The availability can be determined by checking for * the %COGL_FEATURE_ID_TEXTURE_RG feature. If this format is used on * a driver where it is not available then %COGL_TEXTURE_ERROR_FORMAT * will be raised when the texture is allocated. Even if the feature * is not available then %COGL_PIXEL_FORMAT_RG_88 can still be used as * an image format as long as %COGL_TEXTURE_COMPONENTS_RG isn't used * as the texture's components. * * Since: 1.18 */ COGL_EXPORT void cogl_texture_set_components (CoglTexture *texture, CoglTextureComponents components); /** * cogl_texture_get_components: * @texture: a #CoglTexture pointer. * * Queries what components the given @texture stores internally as set * via cogl_texture_set_components(). * * For textures created by the ‘_with_size’ constructors the default * is %COGL_TEXTURE_COMPONENTS_RGBA. The other constructors which take * a %CoglBitmap or a data pointer default to the same components as * the pixel format of the data. * * Since: 1.18 */ COGL_EXPORT CoglTextureComponents cogl_texture_get_components (CoglTexture *texture); /** * cogl_texture_set_premultiplied: * @texture: a #CoglTexture pointer. * @premultiplied: Whether any internally stored red, green or blue * components are pre-multiplied by an alpha * component. * * Affects the internal storage format for this texture by specifying * whether red, green and blue color components should be stored as * pre-multiplied alpha values. * * This api affects how data is uploaded to the GPU since Cogl will * convert source data to have premultiplied or unpremultiplied * components according to this state. * * For example if you create a texture via * cogl_texture_2d_new_with_size() and then upload data via * cogl_texture_set_data() passing a source format of * %COGL_PIXEL_FORMAT_RGBA_8888 then Cogl will internally multiply the * red, green and blue components of the source data by the alpha * component, for each pixel so that the internally stored data has * pre-multiplied alpha components. If you instead upload data that * already has pre-multiplied components by passing * %COGL_PIXEL_FORMAT_RGBA_8888_PRE as the source format to * cogl_texture_set_data() then the data can be uploaded without being * converted. * * By default the @premultipled state is @TRUE. * * Since: 1.18 */ COGL_EXPORT void cogl_texture_set_premultiplied (CoglTexture *texture, gboolean premultiplied); /** * cogl_texture_get_premultiplied: * @texture: a #CoglTexture pointer. * * Queries the pre-multiplied alpha status for internally stored red, * green and blue components for the given @texture as set by * cogl_texture_set_premultiplied(). * * By default the pre-multipled state is @TRUE. * * Return value: %TRUE if red, green and blue components are * internally stored pre-multiplied by the alpha * value or %FALSE if not. * Since: 1.18 */ COGL_EXPORT gboolean cogl_texture_get_premultiplied (CoglTexture *texture); /** * cogl_texture_get_width: * @texture: a #CoglTexture pointer. * * Queries the width of a cogl texture. * * Return value: the width of the GPU side texture in pixels */ COGL_EXPORT unsigned int cogl_texture_get_width (CoglTexture *texture); /** * cogl_texture_get_height: * @texture: a #CoglTexture pointer. * * Queries the height of a cogl texture. * * Return value: the height of the GPU side texture in pixels */ COGL_EXPORT unsigned int cogl_texture_get_height (CoglTexture *texture); /** * cogl_texture_get_max_waste: * @texture: a #CoglTexture pointer. * * Queries the maximum wasted (unused) pixels in one dimension of a GPU side * texture. * * Return value: the maximum waste */ COGL_EXPORT int cogl_texture_get_max_waste (CoglTexture *texture); /** * cogl_texture_is_sliced: * @texture: a #CoglTexture pointer. * * Queries if a texture is sliced (stored as multiple GPU side tecture * objects). * * Return value: %TRUE if the texture is sliced, %FALSE if the texture * is stored as a single GPU texture */ COGL_EXPORT gboolean cogl_texture_is_sliced (CoglTexture *texture); /** * cogl_texture_get_gl_texture: * @texture: a #CoglTexture pointer. * @out_gl_handle: (out) (allow-none): pointer to return location for the * textures GL handle, or %NULL. * @out_gl_target: (out) (allow-none): pointer to return location for the * GL target type, or %NULL. * * Queries the GL handles for a GPU side texture through its #CoglTexture. * * If the texture is spliced the data for the first sub texture will be * queried. * * Return value: %TRUE if the handle was successfully retrieved, %FALSE * if the handle was invalid */ COGL_EXPORT gboolean cogl_texture_get_gl_texture (CoglTexture *texture, unsigned int *out_gl_handle, unsigned int *out_gl_target); /** * cogl_texture_get_data: * @texture: a #CoglTexture pointer. * @format: the #CoglPixelFormat to store the texture as. * @rowstride: the rowstride of @data in bytes or pass 0 to calculate * from the bytes-per-pixel of @format multiplied by the * @texture width. * @data: (array) (nullable): memory location to write the @texture's contents, * or %NULL to only query the data size through the return value. * * Copies the pixel data from a cogl texture to system memory. * * Don't pass the value of cogl_texture_get_rowstride() as the * @rowstride argument, the rowstride should be the rowstride you * want for the destination @data buffer not the rowstride of the * source texture * * Return value: the size of the texture data in bytes */ COGL_EXPORT int cogl_texture_get_data (CoglTexture *texture, CoglPixelFormat format, unsigned int rowstride, uint8_t *data); /** * cogl_texture_set_region: * @texture: a #CoglTexture. * @src_x: upper left coordinate to use from source data. * @src_y: upper left coordinate to use from source data. * @dst_x: upper left destination horizontal coordinate. * @dst_y: upper left destination vertical coordinate. * @dst_width: width of destination region to write. (Must be less * than or equal to @width) * @dst_height: height of destination region to write. (Must be less * than or equal to @height) * @width: width of source data buffer. * @height: height of source data buffer. * @format: the #CoglPixelFormat used in the source buffer. * @rowstride: rowstride of source buffer (computed from width if none * specified) * @data: (array): the actual pixel data. * * Sets the pixels in a rectangular subregion of @texture from an in-memory * buffer containing pixel data. * * The region set can't be larger than the source @data * * Return value: %TRUE if the subregion upload was successful, and * %FALSE otherwise */ COGL_EXPORT gboolean cogl_texture_set_region (CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, unsigned int dst_width, unsigned int dst_height, int width, int height, CoglPixelFormat format, unsigned int rowstride, const uint8_t *data); /** * cogl_texture_set_data: * @texture a #CoglTexture. * @format: the #CoglPixelFormat used in the source @data buffer. * @rowstride: rowstride of the source @data buffer (computed from * the texture width and @format if it equals 0) * @data: (array): the source data, pointing to the first top-left pixel to set * @level: The mipmap level to update (Normally 0 for the largest, * base texture) * @error: A #GError to return exceptional errors * * Sets all the pixels for a given mipmap @level by copying the pixel * data pointed to by the @data argument into the given @texture. * * @data should point to the first pixel to copy corresponding * to the top left of the mipmap @level being set. * * If @rowstride equals 0 then it will be automatically calculated * from the width of the mipmap level and the bytes-per-pixel for the * given @format. * * A mipmap @level of 0 corresponds to the largest, base image of a * texture and @level 1 is half the width and height of level 0. If * dividing any dimension of the previous level by two results in a * fraction then round the number down (floor()), but clamp to 1 * something like this: * * |[ * next_width = MAX (1, floor (prev_width)); * ]| * * You can determine the number of mipmap levels for a given texture * like this: * * |[ * n_levels = 1 + floor (log2 (max_dimension)); * ]| * * Where %max_dimension is the larger of cogl_texture_get_width() and * cogl_texture_get_height(). * * It is an error to pass a @level number >= the number of levels that * @texture can have according to the above calculation. * * Since the storage for a #CoglTexture is allocated lazily then * if the given @texture has not previously been allocated then this * api can return %FALSE and throw an exceptional @error if there is * not enough memory to allocate storage for @texture. * * Return value: %TRUE if the data upload was successful, and * %FALSE otherwise */ COGL_EXPORT gboolean cogl_texture_set_data (CoglTexture *texture, CoglPixelFormat format, int rowstride, const uint8_t *data, int level, GError **error); /** * cogl_texture_set_region_from_bitmap: * @texture: a #CoglTexture pointer * @src_x: upper left coordinate to use from the source bitmap. * @src_y: upper left coordinate to use from the source bitmap * @dst_x: upper left destination horizontal coordinate. * @dst_y: upper left destination vertical coordinate. * @dst_width: width of destination region to write. (Must be less * than or equal to the bitmap width) * @dst_height: height of destination region to write. (Must be less * than or equal to the bitmap height) * @bitmap: The source bitmap to read from * * Copies a specified source region from @bitmap to the position * (@src_x, @src_y) of the given destination texture @handle. * * The region updated can't be larger than the source * bitmap * * Return value: %TRUE if the subregion upload was successful, and * %FALSE otherwise * * Since: 1.8 * Stability: unstable */ COGL_EXPORT gboolean cogl_texture_set_region_from_bitmap (CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, unsigned int dst_width, unsigned int dst_height, CoglBitmap *bitmap); /** * cogl_texture_allocate: * @texture: A #CoglTexture * @error: A #GError to return exceptional errors or %NULL * * Explicitly allocates the storage for the given @texture which * allows you to be sure that there is enough memory for the * texture and if not then the error can be handled gracefully. * * Normally applications don't need to use this api directly * since the texture will be implicitly allocated when data is set on * the texture, or if the texture is attached to a #CoglOffscreen * framebuffer and rendered too. * * Return value: %TRUE if the texture was successfully allocated, * otherwise %FALSE and @error will be updated if it * wasn't %NULL. */ COGL_EXPORT gboolean cogl_texture_allocate (CoglTexture *texture, GError **error); /** * cogl_texture_is_get_data_supported: (skip) */ COGL_EXPORT gboolean cogl_texture_is_get_data_supported (CoglTexture *texture); G_END_DECLS #endif /* __COGL_TEXTURE_H__ */ muffin-6.4.1/cogl/cogl/cogl-pipeline-state.c0000664000175000017500000015156114723361714017634 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-context-private.h" #include "cogl-color-private.h" #include "cogl-blend-string.h" #include "cogl-util.h" #include "cogl-depth-state-private.h" #include "cogl-pipeline-state-private.h" #include "cogl-snippet-private.h" #include #include "string.h" #ifndef GL_FUNC_ADD #define GL_FUNC_ADD 0x8006 #endif CoglPipeline * _cogl_pipeline_get_user_program (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_USER_SHADER); return authority->big_state->user_program; } gboolean _cogl_pipeline_color_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return cogl_color_equal (&authority0->color, &authority1->color); } gboolean _cogl_pipeline_alpha_func_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { CoglPipelineAlphaFuncState *alpha_state0 = &authority0->big_state->alpha_state; CoglPipelineAlphaFuncState *alpha_state1 = &authority1->big_state->alpha_state; return alpha_state0->alpha_func == alpha_state1->alpha_func; } gboolean _cogl_pipeline_alpha_func_reference_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { CoglPipelineAlphaFuncState *alpha_state0 = &authority0->big_state->alpha_state; CoglPipelineAlphaFuncState *alpha_state1 = &authority1->big_state->alpha_state; return (alpha_state0->alpha_func_reference == alpha_state1->alpha_func_reference); } gboolean _cogl_pipeline_blend_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { CoglPipelineBlendState *blend_state0 = &authority0->big_state->blend_state; CoglPipelineBlendState *blend_state1 = &authority1->big_state->blend_state; _COGL_GET_CONTEXT (ctx, FALSE); if (blend_state0->blend_equation_rgb != blend_state1->blend_equation_rgb) return FALSE; if (blend_state0->blend_equation_alpha != blend_state1->blend_equation_alpha) return FALSE; if (blend_state0->blend_src_factor_alpha != blend_state1->blend_src_factor_alpha) return FALSE; if (blend_state0->blend_dst_factor_alpha != blend_state1->blend_dst_factor_alpha) return FALSE; if (blend_state0->blend_src_factor_rgb != blend_state1->blend_src_factor_rgb) return FALSE; if (blend_state0->blend_dst_factor_rgb != blend_state1->blend_dst_factor_rgb) return FALSE; if (blend_state0->blend_src_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || blend_state0->blend_src_factor_rgb == GL_CONSTANT_COLOR || blend_state0->blend_dst_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || blend_state0->blend_dst_factor_rgb == GL_CONSTANT_COLOR) { if (!cogl_color_equal (&blend_state0->blend_constant, &blend_state1->blend_constant)) return FALSE; } return TRUE; } gboolean _cogl_pipeline_depth_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { if (authority0->big_state->depth_state.test_enabled == FALSE && authority1->big_state->depth_state.test_enabled == FALSE) return TRUE; else { CoglDepthState *s0 = &authority0->big_state->depth_state; CoglDepthState *s1 = &authority1->big_state->depth_state; return s0->test_enabled == s1->test_enabled && s0->test_function == s1->test_function && s0->write_enabled == s1->write_enabled && s0->range_near == s1->range_near && s0->range_far == s1->range_far; } } gboolean _cogl_pipeline_non_zero_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return (authority0->big_state->non_zero_point_size == authority1->big_state->non_zero_point_size); } gboolean _cogl_pipeline_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return authority0->big_state->point_size == authority1->big_state->point_size; } gboolean _cogl_pipeline_per_vertex_point_size_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return (authority0->big_state->per_vertex_point_size == authority1->big_state->per_vertex_point_size); } gboolean _cogl_pipeline_cull_face_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { CoglPipelineCullFaceState *cull_face_state0 = &authority0->big_state->cull_face_state; CoglPipelineCullFaceState *cull_face_state1 = &authority1->big_state->cull_face_state; /* The cull face state is considered equal if two pipelines are both set to no culling. If the front winding property is ever used for anything else or the comparison is used not just for drawing then this would have to change */ if (cull_face_state0->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) return cull_face_state1->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE; return (cull_face_state0->mode == cull_face_state1->mode && cull_face_state0->front_winding == cull_face_state1->front_winding); } gboolean _cogl_pipeline_user_shader_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return (authority0->big_state->user_program == authority1->big_state->user_program); } typedef struct { const CoglBoxedValue **dst_values; const CoglBoxedValue *src_values; int override_count; } GetUniformsClosure; static gboolean get_uniforms_cb (int uniform_num, void *user_data) { GetUniformsClosure *data = user_data; if (data->dst_values[uniform_num] == NULL) data->dst_values[uniform_num] = data->src_values + data->override_count; data->override_count++; return TRUE; } static void _cogl_pipeline_get_all_uniform_values (CoglPipeline *pipeline, const CoglBoxedValue **values) { GetUniformsClosure data; _COGL_GET_CONTEXT (ctx, NO_RETVAL); memset (values, 0, sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); data.dst_values = values; do { if ((pipeline->differences & COGL_PIPELINE_STATE_UNIFORMS)) { const CoglPipelineUniformsState *uniforms_state = &pipeline->big_state->uniforms_state; data.override_count = 0; data.src_values = uniforms_state->override_values; _cogl_bitmask_foreach (&uniforms_state->override_mask, get_uniforms_cb, &data); } pipeline = _cogl_pipeline_get_parent (pipeline); } while (pipeline); } gboolean _cogl_pipeline_uniforms_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { unsigned long *differences; const CoglBoxedValue **values0, **values1; int n_longs; int i; _COGL_GET_CONTEXT (ctx, FALSE); if (authority0 == authority1) return TRUE; values0 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); values1 = g_alloca (sizeof (const CoglBoxedValue *) * ctx->n_uniform_names); n_longs = COGL_FLAGS_N_LONGS_FOR_SIZE (ctx->n_uniform_names); differences = g_alloca (n_longs * sizeof (unsigned long)); memset (differences, 0, sizeof (unsigned long) * n_longs); _cogl_pipeline_compare_uniform_differences (differences, authority0, authority1); _cogl_pipeline_get_all_uniform_values (authority0, values0); _cogl_pipeline_get_all_uniform_values (authority1, values1); COGL_FLAGS_FOREACH_START (differences, n_longs, i) { const CoglBoxedValue *value0 = values0[i]; const CoglBoxedValue *value1 = values1[i]; if (value0 == NULL) { if (value1 != NULL && value1->type != COGL_BOXED_NONE) return FALSE; } else if (value1 == NULL) { if (value0 != NULL && value0->type != COGL_BOXED_NONE) return FALSE; } else if (!_cogl_boxed_value_equal (value0, value1)) return FALSE; } COGL_FLAGS_FOREACH_END; return TRUE; } gboolean _cogl_pipeline_vertex_snippets_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> vertex_snippets, &authority1->big_state-> vertex_snippets); } gboolean _cogl_pipeline_fragment_snippets_state_equal (CoglPipeline *authority0, CoglPipeline *authority1) { return _cogl_pipeline_snippet_list_equal (&authority0->big_state-> fragment_snippets, &authority1->big_state-> fragment_snippets); } void cogl_pipeline_get_color (CoglPipeline *pipeline, CoglColor *color) { CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); *color = authority->color; } /* This is used heavily by the cogl journal when logging quads */ void _cogl_pipeline_get_colorubv (CoglPipeline *pipeline, uint8_t *color) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_COLOR); _cogl_color_get_rgba_4ubv (&authority->color, color); } void cogl_pipeline_set_color (CoglPipeline *pipeline, const CoglColor *color) { CoglPipelineState state = COGL_PIPELINE_STATE_COLOR; CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); if (cogl_color_equal (color, &authority->color)) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, color, FALSE); pipeline->color = *color; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_color_equal); pipeline->dirty_real_blend_enable = TRUE; } void cogl_pipeline_set_color4ub (CoglPipeline *pipeline, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { CoglColor color; cogl_color_init_from_4ub (&color, red, green, blue, alpha); cogl_pipeline_set_color (pipeline, &color); } void cogl_pipeline_set_color4f (CoglPipeline *pipeline, float red, float green, float blue, float alpha) { CoglColor color; cogl_color_init_from_4f (&color, red, green, blue, alpha); cogl_pipeline_set_color (pipeline, &color); } static void _cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, CoglPipelineAlphaFunc alpha_func) { CoglPipelineState state = COGL_PIPELINE_STATE_ALPHA_FUNC; CoglPipeline *authority; CoglPipelineAlphaFuncState *alpha_state; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); alpha_state = &authority->big_state->alpha_state; if (alpha_state->alpha_func == alpha_func) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); alpha_state = &pipeline->big_state->alpha_state; alpha_state->alpha_func = alpha_func; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_alpha_func_state_equal); } static void _cogl_pipeline_set_alpha_test_function_reference (CoglPipeline *pipeline, float alpha_reference) { CoglPipelineState state = COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE; CoglPipeline *authority; CoglPipelineAlphaFuncState *alpha_state; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); alpha_state = &authority->big_state->alpha_state; if (alpha_state->alpha_func_reference == alpha_reference) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); alpha_state = &pipeline->big_state->alpha_state; alpha_state->alpha_func_reference = alpha_reference; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_alpha_func_reference_state_equal); } void cogl_pipeline_set_alpha_test_function (CoglPipeline *pipeline, CoglPipelineAlphaFunc alpha_func, float alpha_reference) { _cogl_pipeline_set_alpha_test_function (pipeline, alpha_func); _cogl_pipeline_set_alpha_test_function_reference (pipeline, alpha_reference); } CoglPipelineAlphaFunc cogl_pipeline_get_alpha_test_function (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), 0); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_ALPHA_FUNC); return authority->big_state->alpha_state.alpha_func; } float cogl_pipeline_get_alpha_test_reference (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), 0.0f); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_ALPHA_FUNC_REFERENCE); return authority->big_state->alpha_state.alpha_func_reference; } static GLenum arg_to_gl_blend_factor (CoglBlendStringArgument *arg) { if (arg->source.is_zero) return GL_ZERO; if (arg->factor.is_one) return GL_ONE; else if (arg->factor.is_src_alpha_saturate) return GL_SRC_ALPHA_SATURATE; else if (arg->factor.source.info->type == COGL_BLEND_STRING_COLOR_SOURCE_SRC_COLOR) { if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) { if (arg->factor.source.one_minus) return GL_ONE_MINUS_SRC_COLOR; else return GL_SRC_COLOR; } else { if (arg->factor.source.one_minus) return GL_ONE_MINUS_SRC_ALPHA; else return GL_SRC_ALPHA; } } else if (arg->factor.source.info->type == COGL_BLEND_STRING_COLOR_SOURCE_DST_COLOR) { if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) { if (arg->factor.source.one_minus) return GL_ONE_MINUS_DST_COLOR; else return GL_DST_COLOR; } else { if (arg->factor.source.one_minus) return GL_ONE_MINUS_DST_ALPHA; else return GL_DST_ALPHA; } } #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) else if (arg->factor.source.info->type == COGL_BLEND_STRING_COLOR_SOURCE_CONSTANT) { if (arg->factor.source.mask != COGL_BLEND_STRING_CHANNEL_MASK_ALPHA) { if (arg->factor.source.one_minus) return GL_ONE_MINUS_CONSTANT_COLOR; else return GL_CONSTANT_COLOR; } else { if (arg->factor.source.one_minus) return GL_ONE_MINUS_CONSTANT_ALPHA; else return GL_CONSTANT_ALPHA; } } #endif g_warning ("Unable to determine valid blend factor from blend string\n"); return GL_ONE; } static void setup_blend_state (CoglBlendStringStatement *statement, GLenum *blend_equation, GLint *blend_src_factor, GLint *blend_dst_factor) { switch (statement->function->type) { case COGL_BLEND_STRING_FUNCTION_ADD: *blend_equation = GL_FUNC_ADD; break; /* TODO - add more */ default: g_warning ("Unsupported blend function given"); *blend_equation = GL_FUNC_ADD; } *blend_src_factor = arg_to_gl_blend_factor (&statement->args[0]); *blend_dst_factor = arg_to_gl_blend_factor (&statement->args[1]); } gboolean cogl_pipeline_set_blend (CoglPipeline *pipeline, const char *blend_description, GError **error) { CoglPipelineState state = COGL_PIPELINE_STATE_BLEND; CoglPipeline *authority; CoglBlendStringStatement statements[2]; CoglBlendStringStatement *rgb; CoglBlendStringStatement *a; int count; CoglPipelineBlendState *blend_state; _COGL_GET_CONTEXT (ctx, FALSE); g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); count = _cogl_blend_string_compile (blend_description, COGL_BLEND_STRING_CONTEXT_BLENDING, statements, error); if (!count) return FALSE; if (count == 1) rgb = a = statements; else { rgb = &statements[0]; a = &statements[1]; } authority = _cogl_pipeline_get_authority (pipeline, state); /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); blend_state = &pipeline->big_state->blend_state; setup_blend_state (rgb, &blend_state->blend_equation_rgb, &blend_state->blend_src_factor_rgb, &blend_state->blend_dst_factor_rgb); setup_blend_state (a, &blend_state->blend_equation_alpha, &blend_state->blend_src_factor_alpha, &blend_state->blend_dst_factor_alpha); /* If we are the current authority see if we can revert to one of our * ancestors being the authority */ if (pipeline == authority && _cogl_pipeline_get_parent (authority) != NULL) { CoglPipeline *parent = _cogl_pipeline_get_parent (authority); CoglPipeline *old_authority = _cogl_pipeline_get_authority (parent, state); if (_cogl_pipeline_blend_state_equal (authority, old_authority)) pipeline->differences &= ~state; } /* If we weren't previously the authority on this state then we need * to extended our differences mask and so it's possible that some * of our ancestry will now become redundant, so we aim to reparent * ourselves if that's true... */ if (pipeline != authority) { pipeline->differences |= state; _cogl_pipeline_prune_redundant_ancestry (pipeline); } pipeline->dirty_real_blend_enable = TRUE; return TRUE; } void cogl_pipeline_set_blend_constant (CoglPipeline *pipeline, const CoglColor *constant_color) { _COGL_GET_CONTEXT (ctx, NO_RETVAL); g_return_if_fail (cogl_is_pipeline (pipeline)); #if defined(HAVE_COGL_GLES2) || defined(HAVE_COGL_GL) { CoglPipelineState state = COGL_PIPELINE_STATE_BLEND; CoglPipeline *authority; CoglPipelineBlendState *blend_state; authority = _cogl_pipeline_get_authority (pipeline, state); blend_state = &authority->big_state->blend_state; if (cogl_color_equal (constant_color, &blend_state->blend_constant)) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); blend_state = &pipeline->big_state->blend_state; blend_state->blend_constant = *constant_color; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_blend_state_equal); pipeline->dirty_real_blend_enable = TRUE; } #endif } CoglHandle cogl_pipeline_get_user_program (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_USER_SHADER); return authority->big_state->user_program; } /* XXX: for now we don't mind if the program has vertex shaders * attached but if we ever make a similar API public we should only * allow attaching of programs containing fragment shaders. Eventually * we will have a CoglPipeline abstraction to also cover vertex * processing. */ void cogl_pipeline_set_user_program (CoglPipeline *pipeline, CoglHandle program) { CoglPipelineState state = COGL_PIPELINE_STATE_USER_SHADER; CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); if (authority->big_state->user_program == program) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); /* If we are the current authority see if we can revert to one of our * ancestors being the authority */ if (pipeline == authority && _cogl_pipeline_get_parent (authority) != NULL) { CoglPipeline *parent = _cogl_pipeline_get_parent (authority); CoglPipeline *old_authority = _cogl_pipeline_get_authority (parent, state); if (old_authority->big_state->user_program == program) pipeline->differences &= ~state; } else if (pipeline != authority) { /* If we weren't previously the authority on this state then we * need to extended our differences mask and so it's possible * that some of our ancestry will now become redundant, so we * aim to reparent ourselves if that's true... */ pipeline->differences |= state; _cogl_pipeline_prune_redundant_ancestry (pipeline); } if (program != NULL) cogl_object_ref (program); if (authority == pipeline && pipeline->big_state->user_program != NULL) cogl_object_unref (pipeline->big_state->user_program); pipeline->big_state->user_program = program; pipeline->dirty_real_blend_enable = TRUE; } gboolean cogl_pipeline_set_depth_state (CoglPipeline *pipeline, const CoglDepthState *depth_state, GError **error) { CoglPipelineState state = COGL_PIPELINE_STATE_DEPTH; CoglPipeline *authority; CoglDepthState *orig_state; _COGL_GET_CONTEXT (ctx, FALSE); g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); g_return_val_if_fail (depth_state->magic == COGL_DEPTH_STATE_MAGIC, FALSE); authority = _cogl_pipeline_get_authority (pipeline, state); orig_state = &authority->big_state->depth_state; if (orig_state->test_enabled == depth_state->test_enabled && orig_state->write_enabled == depth_state->write_enabled && orig_state->test_function == depth_state->test_function && orig_state->range_near == depth_state->range_near && orig_state->range_far == depth_state->range_far) return TRUE; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->depth_state = *depth_state; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_depth_state_equal); return TRUE; } void cogl_pipeline_get_depth_state (CoglPipeline *pipeline, CoglDepthState *state) { CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_DEPTH); *state = authority->big_state->depth_state; } void cogl_pipeline_set_cull_face_mode (CoglPipeline *pipeline, CoglPipelineCullFaceMode cull_face_mode) { CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; CoglPipeline *authority; CoglPipelineCullFaceState *cull_face_state; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); cull_face_state = &authority->big_state->cull_face_state; if (cull_face_state->mode == cull_face_mode) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->cull_face_state.mode = cull_face_mode; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_cull_face_state_equal); } void cogl_pipeline_set_front_face_winding (CoglPipeline *pipeline, CoglWinding front_winding) { CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; CoglPipeline *authority; CoglPipelineCullFaceState *cull_face_state; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); cull_face_state = &authority->big_state->cull_face_state; if (cull_face_state->front_winding == front_winding) return; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->cull_face_state.front_winding = front_winding; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_cull_face_state_equal); } CoglPipelineCullFaceMode cogl_pipeline_get_cull_face_mode (CoglPipeline *pipeline) { CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), COGL_PIPELINE_CULL_FACE_MODE_NONE); authority = _cogl_pipeline_get_authority (pipeline, state); return authority->big_state->cull_face_state.mode; } CoglWinding cogl_pipeline_get_front_face_winding (CoglPipeline *pipeline) { CoglPipelineState state = COGL_PIPELINE_STATE_CULL_FACE; CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), COGL_WINDING_CLOCKWISE); authority = _cogl_pipeline_get_authority (pipeline, state); return authority->big_state->cull_face_state.front_winding; } float cogl_pipeline_get_point_size (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_POINT_SIZE); return authority->big_state->point_size; } static void _cogl_pipeline_set_non_zero_point_size (CoglPipeline *pipeline, gboolean value) { CoglPipelineState state = COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE; CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->non_zero_point_size = !!value; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_non_zero_point_size_equal); } void cogl_pipeline_set_point_size (CoglPipeline *pipeline, float point_size) { CoglPipelineState state = COGL_PIPELINE_STATE_POINT_SIZE; CoglPipeline *authority; g_return_if_fail (cogl_is_pipeline (pipeline)); authority = _cogl_pipeline_get_authority (pipeline, state); if (authority->big_state->point_size == point_size) return; /* Changing the point size may additionally modify * COGL_PIPELINE_STATE_NON_ZERO_POINT_SIZE. */ if ((authority->big_state->point_size > 0.0f) != (point_size > 0.0f)) _cogl_pipeline_set_non_zero_point_size (pipeline, point_size > 0.0f); /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->point_size = point_size; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_point_size_equal); } gboolean cogl_pipeline_set_per_vertex_point_size (CoglPipeline *pipeline, gboolean enable, GError **error) { CoglPipelineState state = COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE; CoglPipeline *authority; _COGL_GET_CONTEXT (ctx, FALSE); g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); authority = _cogl_pipeline_get_authority (pipeline, state); enable = !!enable; if (authority->big_state->per_vertex_point_size == enable) return TRUE; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); pipeline->big_state->per_vertex_point_size = enable; _cogl_pipeline_update_authority (pipeline, authority, state, _cogl_pipeline_point_size_equal); return TRUE; } gboolean cogl_pipeline_get_per_vertex_point_size (CoglPipeline *pipeline) { CoglPipeline *authority; g_return_val_if_fail (cogl_is_pipeline (pipeline), FALSE); authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_PER_VERTEX_POINT_SIZE); return authority->big_state->per_vertex_point_size; } static CoglBoxedValue * _cogl_pipeline_override_uniform (CoglPipeline *pipeline, int location) { CoglPipelineState state = COGL_PIPELINE_STATE_UNIFORMS; CoglPipelineUniformsState *uniforms_state; int override_index; _COGL_GET_CONTEXT (ctx, NULL); g_return_val_if_fail (cogl_is_pipeline (pipeline), NULL); g_return_val_if_fail (location >= 0, NULL); g_return_val_if_fail (location < ctx->n_uniform_names, NULL); /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); uniforms_state = &pipeline->big_state->uniforms_state; /* Count the number of bits that are set below this location. That should give us the position where our new value should lie */ override_index = _cogl_bitmask_popcount_upto (&uniforms_state->override_mask, location); _cogl_bitmask_set (&uniforms_state->changed_mask, location, TRUE); /* If this pipeline already has an override for this value then we can just use it directly */ if (_cogl_bitmask_get (&uniforms_state->override_mask, location)) return uniforms_state->override_values + override_index; /* We need to create a new override value in the right position within the array. This is pretty inefficient but the hope is that it will be much more common to modify an existing uniform rather than modify a new one so it is more important to optimise the former case. */ if (uniforms_state->override_values == NULL) { g_assert (override_index == 0); uniforms_state->override_values = g_new (CoglBoxedValue, 1); } else { /* We need to grow the array and copy in the old values */ CoglBoxedValue *old_values = uniforms_state->override_values; int old_size = _cogl_bitmask_popcount (&uniforms_state->override_mask); uniforms_state->override_values = g_new (CoglBoxedValue, old_size + 1); /* Copy in the old values leaving a gap for the new value */ memcpy (uniforms_state->override_values, old_values, sizeof (CoglBoxedValue) * override_index); memcpy (uniforms_state->override_values + override_index + 1, old_values + override_index, sizeof (CoglBoxedValue) * (old_size - override_index)); g_free (old_values); } _cogl_boxed_value_init (uniforms_state->override_values + override_index); _cogl_bitmask_set (&uniforms_state->override_mask, location, TRUE); return uniforms_state->override_values + override_index; } void cogl_pipeline_set_uniform_1f (CoglPipeline *pipeline, int uniform_location, float value) { CoglBoxedValue *boxed_value; boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); _cogl_boxed_value_set_1f (boxed_value, value); } void cogl_pipeline_set_uniform_1i (CoglPipeline *pipeline, int uniform_location, int value) { CoglBoxedValue *boxed_value; boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); _cogl_boxed_value_set_1i (boxed_value, value); } void cogl_pipeline_set_uniform_float (CoglPipeline *pipeline, int uniform_location, int n_components, int count, const float *value) { CoglBoxedValue *boxed_value; boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); _cogl_boxed_value_set_float (boxed_value, n_components, count, value); } void cogl_pipeline_set_uniform_int (CoglPipeline *pipeline, int uniform_location, int n_components, int count, const int *value) { CoglBoxedValue *boxed_value; boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); _cogl_boxed_value_set_int (boxed_value, n_components, count, value); } void cogl_pipeline_set_uniform_matrix (CoglPipeline *pipeline, int uniform_location, int dimensions, int count, gboolean transpose, const float *value) { CoglBoxedValue *boxed_value; boxed_value = _cogl_pipeline_override_uniform (pipeline, uniform_location); _cogl_boxed_value_set_matrix (boxed_value, dimensions, count, transpose, value); } static void _cogl_pipeline_add_vertex_snippet (CoglPipeline *pipeline, CoglSnippet *snippet) { CoglPipelineState state = COGL_PIPELINE_STATE_VERTEX_SNIPPETS; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); _cogl_pipeline_snippet_list_add (&pipeline->big_state->vertex_snippets, snippet); } static void _cogl_pipeline_add_fragment_snippet (CoglPipeline *pipeline, CoglSnippet *snippet) { CoglPipelineState state = COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS; /* - Flush journal primitives referencing the current state. * - Make sure the pipeline has no dependants so it may be modified. * - If the pipeline isn't currently an authority for the state being * changed, then initialize that state from the current authority. */ _cogl_pipeline_pre_change_notify (pipeline, state, NULL, FALSE); _cogl_pipeline_snippet_list_add (&pipeline->big_state->fragment_snippets, snippet); } void cogl_pipeline_add_snippet (CoglPipeline *pipeline, CoglSnippet *snippet) { g_return_if_fail (cogl_is_pipeline (pipeline)); g_return_if_fail (cogl_is_snippet (snippet)); g_return_if_fail (snippet->hook < COGL_SNIPPET_FIRST_LAYER_HOOK); if (snippet->hook < COGL_SNIPPET_FIRST_PIPELINE_FRAGMENT_HOOK) _cogl_pipeline_add_vertex_snippet (pipeline, snippet); else _cogl_pipeline_add_fragment_snippet (pipeline, snippet); } gboolean _cogl_pipeline_has_non_layer_vertex_snippets (CoglPipeline *pipeline) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_VERTEX_SNIPPETS); return authority->big_state->vertex_snippets.entries != NULL; } static gboolean check_layer_has_vertex_snippet (CoglPipelineLayer *layer, void *user_data) { unsigned long state = COGL_PIPELINE_LAYER_STATE_VERTEX_SNIPPETS; CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, state); gboolean *found_vertex_snippet = user_data; if (authority->big_state->vertex_snippets.entries) { *found_vertex_snippet = TRUE; return FALSE; } return TRUE; } gboolean _cogl_pipeline_has_vertex_snippets (CoglPipeline *pipeline) { gboolean found_vertex_snippet = FALSE; if (_cogl_pipeline_has_non_layer_vertex_snippets (pipeline)) return TRUE; _cogl_pipeline_foreach_layer_internal (pipeline, check_layer_has_vertex_snippet, &found_vertex_snippet); return found_vertex_snippet; } gboolean _cogl_pipeline_has_non_layer_fragment_snippets (CoglPipeline *pipeline) { CoglPipeline *authority = _cogl_pipeline_get_authority (pipeline, COGL_PIPELINE_STATE_FRAGMENT_SNIPPETS); return authority->big_state->fragment_snippets.entries != NULL; } static gboolean check_layer_has_fragment_snippet (CoglPipelineLayer *layer, void *user_data) { unsigned long state = COGL_PIPELINE_LAYER_STATE_FRAGMENT_SNIPPETS; CoglPipelineLayer *authority = _cogl_pipeline_layer_get_authority (layer, state); gboolean *found_fragment_snippet = user_data; if (authority->big_state->fragment_snippets.entries) { *found_fragment_snippet = TRUE; return FALSE; } return TRUE; } gboolean _cogl_pipeline_has_fragment_snippets (CoglPipeline *pipeline) { gboolean found_fragment_snippet = FALSE; if (_cogl_pipeline_has_non_layer_fragment_snippets (pipeline)) return TRUE; _cogl_pipeline_foreach_layer_internal (pipeline, check_layer_has_fragment_snippet, &found_fragment_snippet); return found_fragment_snippet; } void _cogl_pipeline_hash_color_state (CoglPipeline *authority, CoglPipelineHashState *state) { state->hash = _cogl_util_one_at_a_time_hash (state->hash, &authority->color, _COGL_COLOR_DATA_SIZE); } void _cogl_pipeline_hash_alpha_func_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglPipelineAlphaFuncState *alpha_state = &authority->big_state->alpha_state; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &alpha_state->alpha_func, sizeof (alpha_state->alpha_func)); } void _cogl_pipeline_hash_alpha_func_reference_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglPipelineAlphaFuncState *alpha_state = &authority->big_state->alpha_state; float ref = alpha_state->alpha_func_reference; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &ref, sizeof (float)); } void _cogl_pipeline_hash_blend_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglPipelineBlendState *blend_state = &authority->big_state->blend_state; unsigned int hash; _COGL_GET_CONTEXT (ctx, NO_RETVAL); if (!authority->real_blend_enable) return; hash = state->hash; hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_equation_rgb, sizeof (blend_state->blend_equation_rgb)); hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_equation_alpha, sizeof (blend_state->blend_equation_alpha)); hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_src_factor_alpha, sizeof (blend_state->blend_src_factor_alpha)); hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_dst_factor_alpha, sizeof (blend_state->blend_dst_factor_alpha)); if (blend_state->blend_src_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || blend_state->blend_src_factor_rgb == GL_CONSTANT_COLOR || blend_state->blend_dst_factor_rgb == GL_ONE_MINUS_CONSTANT_COLOR || blend_state->blend_dst_factor_rgb == GL_CONSTANT_COLOR) { hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_constant, sizeof (blend_state->blend_constant)); } hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_src_factor_rgb, sizeof (blend_state->blend_src_factor_rgb)); hash = _cogl_util_one_at_a_time_hash (hash, &blend_state->blend_dst_factor_rgb, sizeof (blend_state->blend_dst_factor_rgb)); state->hash = hash; } void _cogl_pipeline_hash_user_shader_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglHandle user_program = authority->big_state->user_program; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &user_program, sizeof (user_program)); } void _cogl_pipeline_hash_depth_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglDepthState *depth_state = &authority->big_state->depth_state; unsigned int hash = state->hash; if (depth_state->test_enabled) { uint8_t enabled = depth_state->test_enabled; CoglDepthTestFunction function = depth_state->test_function; hash = _cogl_util_one_at_a_time_hash (hash, &enabled, sizeof (enabled)); hash = _cogl_util_one_at_a_time_hash (hash, &function, sizeof (function)); } if (depth_state->write_enabled) { uint8_t enabled = depth_state->write_enabled; float near_val = depth_state->range_near; float far_val = depth_state->range_far; hash = _cogl_util_one_at_a_time_hash (hash, &enabled, sizeof (enabled)); hash = _cogl_util_one_at_a_time_hash (hash, &near_val, sizeof (near_val)); hash = _cogl_util_one_at_a_time_hash (hash, &far_val, sizeof (far_val)); } state->hash = hash; } void _cogl_pipeline_hash_non_zero_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state) { gboolean non_zero_point_size = authority->big_state->non_zero_point_size; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &non_zero_point_size, sizeof (non_zero_point_size)); } void _cogl_pipeline_hash_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state) { float point_size = authority->big_state->point_size; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &point_size, sizeof (point_size)); } void _cogl_pipeline_hash_per_vertex_point_size_state (CoglPipeline *authority, CoglPipelineHashState *state) { gboolean per_vertex_point_size = authority->big_state->per_vertex_point_size; state->hash = _cogl_util_one_at_a_time_hash (state->hash, &per_vertex_point_size, sizeof (per_vertex_point_size)); } void _cogl_pipeline_hash_cull_face_state (CoglPipeline *authority, CoglPipelineHashState *state) { CoglPipelineCullFaceState *cull_face_state = &authority->big_state->cull_face_state; /* The cull face state is considered equal if two pipelines are both set to no culling. If the front winding property is ever used for anything else or the hashing is used not just for drawing then this would have to change */ if (cull_face_state->mode == COGL_PIPELINE_CULL_FACE_MODE_NONE) state->hash = _cogl_util_one_at_a_time_hash (state->hash, &cull_face_state->mode, sizeof (CoglPipelineCullFaceMode)); else state->hash = _cogl_util_one_at_a_time_hash (state->hash, cull_face_state, sizeof (CoglPipelineCullFaceState)); } void _cogl_pipeline_hash_uniforms_state (CoglPipeline *authority, CoglPipelineHashState *state) { /* This isn't used anywhere yet because the uniform state doesn't affect program generation. It's quite a hassle to implement so let's just leave it until something actually needs it */ g_warn_if_reached (); } void _cogl_pipeline_compare_uniform_differences (unsigned long *differences, CoglPipeline *pipeline0, CoglPipeline *pipeline1) { GSList *head0 = NULL; GSList *head1 = NULL; CoglPipeline *node0; CoglPipeline *node1; int len0 = 0; int len1 = 0; int count; GSList *common_ancestor0; GSList *common_ancestor1; /* This algorithm is copied from _cogl_pipeline_compare_differences(). It might be nice to share the code more */ for (node0 = pipeline0; node0; node0 = _cogl_pipeline_get_parent (node0)) { GSList *link = alloca (sizeof (GSList)); link->next = head0; link->data = node0; head0 = link; len0++; } for (node1 = pipeline1; node1; node1 = _cogl_pipeline_get_parent (node1)) { GSList *link = alloca (sizeof (GSList)); link->next = head1; link->data = node1; head1 = link; len1++; } /* NB: There's no point looking at the head entries since we know both * pipelines must have the same default pipeline as their root node. */ common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; count = MIN (len0, len1) - 1; while (count--) { if (head0->data != head1->data) break; common_ancestor0 = head0; common_ancestor1 = head1; head0 = head0->next; head1 = head1->next; } for (head0 = common_ancestor0->next; head0; head0 = head0->next) { node0 = head0->data; if ((node0->differences & COGL_PIPELINE_STATE_UNIFORMS)) { const CoglPipelineUniformsState *uniforms_state = &node0->big_state->uniforms_state; _cogl_bitmask_set_flags (&uniforms_state->override_mask, differences); } } for (head1 = common_ancestor1->next; head1; head1 = head1->next) { node1 = head1->data; if ((node1->differences & COGL_PIPELINE_STATE_UNIFORMS)) { const CoglPipelineUniformsState *uniforms_state = &node1->big_state->uniforms_state; _cogl_bitmask_set_flags (&uniforms_state->override_mask, differences); } } } void _cogl_pipeline_hash_vertex_snippets_state (CoglPipeline *authority, CoglPipelineHashState *state) { _cogl_pipeline_snippet_list_hash (&authority->big_state->vertex_snippets, &state->hash); } void _cogl_pipeline_hash_fragment_snippets_state (CoglPipeline *authority, CoglPipelineHashState *state) { _cogl_pipeline_snippet_list_hash (&authority->big_state->fragment_snippets, &state->hash); } UNIT_TEST (check_blend_constant_ancestry, 0 /* no requirements */, 0 /* no known failures */) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglNode *node; int pipeline_length = 0; int i; /* Repeatedly making a copy of a pipeline and changing the same * state (in this case the blend constant) shouldn't cause a long * chain of pipelines to be created because the redundant ancestry * should be pruned. */ for (i = 0; i < 20; i++) { CoglColor color; CoglPipeline *tmp_pipeline; cogl_color_init_from_4f (&color, i / 20.0f, 0.0f, 0.0f, 1.0f); tmp_pipeline = cogl_pipeline_copy (pipeline); cogl_object_unref (pipeline); pipeline = tmp_pipeline; cogl_pipeline_set_blend_constant (pipeline, &color); } for (node = (CoglNode *) pipeline; node; node = node->parent) pipeline_length++; g_assert_cmpint (pipeline_length, <=, 2); cogl_object_unref (pipeline); } UNIT_TEST (check_uniform_ancestry, 0 /* no requirements */, TEST_KNOWN_FAILURE) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglNode *node; int pipeline_length = 0; int i; /* Repeatedly making a copy of a pipeline and changing a uniform * shouldn't cause a long chain of pipelines to be created */ for (i = 0; i < 20; i++) { CoglPipeline *tmp_pipeline; int uniform_location; tmp_pipeline = cogl_pipeline_copy (pipeline); cogl_object_unref (pipeline); pipeline = tmp_pipeline; uniform_location = cogl_pipeline_get_uniform_location (pipeline, "a_uniform"); cogl_pipeline_set_uniform_1i (pipeline, uniform_location, i); } for (node = (CoglNode *) pipeline; node; node = node->parent) pipeline_length++; g_assert_cmpint (pipeline_length, <=, 2); cogl_object_unref (pipeline); } muffin-6.4.1/cogl/cogl/cogl-egl-private.h0000664000175000017500000000263314723361714017130 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_EGL_PRIVATE_H__ #define __COGL_EGL_PRIVATE_H__ #include "cogl-egl-defines.h" #if defined(GL_OES_EGL_image) && !defined(GLeglImageOES) #define GLeglImageOES void * #endif #endif /* __COGL_EGL_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl/cogl-attribute-buffer.h0000664000175000017500000001131714723361714020162 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Robert Bragg */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_ATTRIBUTE_BUFFER_H__ #define __COGL_ATTRIBUTE_BUFFER_H__ /* We forward declare the CoglAttributeBuffer type here to avoid some circular * dependency issues with the following headers. */ typedef struct _CoglAttributeBuffer CoglAttributeBuffer; #include #include G_BEGIN_DECLS /** * SECTION:cogl-attribute-buffer * @short_description: Functions for creating and manipulating attribute * buffers * * FIXME */ #define COGL_ATTRIBUTE_BUFFER(buffer) ((CoglAttributeBuffer *)(buffer)) /** * cogl_attribute_buffer_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_attribute_buffer_get_gtype (void); /** * cogl_attribute_buffer_new_with_size: * @context: A #CoglContext * @bytes: The number of bytes to allocate for vertex attribute data. * * Describes a new #CoglAttributeBuffer of @size bytes to contain * arrays of vertex attribute data. Afterwards data can be set using * cogl_buffer_set_data() or by mapping it into the application's * address space using cogl_buffer_map(). * * The underlying storage of this buffer isn't allocated by this * function so that you have an opportunity to use the * cogl_buffer_set_update_hint() and cogl_buffer_set_usage_hint() * functions which may influence how the storage is allocated. The * storage will be allocated once you upload data to the buffer. * * Note: You can assume this function always succeeds and won't return * %NULL * * Return value: (transfer full): A newly allocated #CoglAttributeBuffer. Never %NULL. * * Stability: Unstable */ COGL_EXPORT CoglAttributeBuffer * cogl_attribute_buffer_new_with_size (CoglContext *context, size_t bytes); /** * cogl_attribute_buffer_new: * @context: A #CoglContext * @bytes: The number of bytes to allocate for vertex attribute data. * @data: (array length=bytes): An optional pointer to vertex data to * upload immediately. * * Describes a new #CoglAttributeBuffer of @size bytes to contain * arrays of vertex attribute data and also uploads @size bytes read * from @data to the new buffer. * * You should never pass a %NULL data pointer. * * This function does not report out-of-memory errors back to * the caller by returning %NULL and so you can assume this function * always succeeds. * * In the unlikely case that there is an out of memory problem * then Cogl will abort the application with a message. If your * application needs to gracefully handle out-of-memory errors then * you can use cogl_attribute_buffer_new_with_size() and then * explicitly catch errors with cogl_buffer_set_data() or * cogl_buffer_map(). * * Return value: (transfer full): A newly allocated #CoglAttributeBuffer (never %NULL) * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT CoglAttributeBuffer * cogl_attribute_buffer_new (CoglContext *context, size_t bytes, const void *data); /** * cogl_is_attribute_buffer: * @object: A #CoglObject * * Gets whether the given object references a #CoglAttributeBuffer. * * Returns: %TRUE if @object references a #CoglAttributeBuffer, * %FALSE otherwise * * Since: 1.4 * Stability: Unstable */ COGL_EXPORT gboolean cogl_is_attribute_buffer (void *object); G_END_DECLS #endif /* __COGL_ATTRIBUTE_BUFFER_H__ */ muffin-6.4.1/cogl/cogl/cogl-primitive-texture.c0000664000175000017500000000354214723361714020412 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include "cogl-primitive-texture.h" #include "cogl-texture-private.h" gboolean cogl_is_primitive_texture (void *object) { return (cogl_is_texture (object) && COGL_TEXTURE (object)->vtable->is_primitive); } void cogl_primitive_texture_set_auto_mipmap (CoglPrimitiveTexture *primitive_texture, gboolean value) { CoglTexture *texture; g_return_if_fail (cogl_is_primitive_texture (primitive_texture)); texture = COGL_TEXTURE (primitive_texture); g_assert (texture->vtable->set_auto_mipmap != NULL); texture->vtable->set_auto_mipmap (texture, value); } muffin-6.4.1/cogl/cogl/cogl-xlib-renderer.h0000664000175000017500000001713614723361714017457 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #if !defined(__COGL_XLIB_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_XLIB_RENDERER_H__ #define __COGL_XLIB_RENDERER_H__ #include #include /* NB: this is a top-level header that can be included directly but we * want to be careful not to define __COGL_H_INSIDE__ when this is * included internally while building Cogl itself since * __COGL_H_INSIDE__ is used in headers to guard public vs private api * definitions */ #ifndef COGL_COMPILATION /* Note: When building Cogl .gir we explicitly define * __COGL_H_INSIDE__ */ #ifndef __COGL_H_INSIDE__ #define __COGL_H_INSIDE__ #define __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE_COGL_XLIB_RENDERER_ #endif #endif /* COGL_COMPILATION */ #include G_BEGIN_DECLS /** * cogl_xlib_renderer_handle_event: (skip) * @renderer: a #CoglRenderer * @event: pointer to an XEvent structure * * This function processes a single event; it can be used to hook into * external event retrieval (for example that done by Clutter or * GDK). * * Return value: #CoglFilterReturn. %COGL_FILTER_REMOVE indicates that * Cogl has internally handled the event and the caller should do no * further processing. %COGL_FILTER_CONTINUE indicates that Cogl is * either not interested in the event, or has used the event to update * internal state without taking any exclusive action. */ COGL_EXPORT CoglFilterReturn cogl_xlib_renderer_handle_event (CoglRenderer *renderer, XEvent *event); /* * CoglXlibFilterFunc: * @event: pointer to an XEvent structure * @data: the data that was given when the filter was added * * A callback function that can be registered with * cogl_xlib_renderer_add_filter(). The function should return * %COGL_FILTER_REMOVE if it wants to prevent further processing or * %COGL_FILTER_CONTINUE otherwise. */ typedef CoglFilterReturn (* CoglXlibFilterFunc) (XEvent *event, void *data); /** * cogl_xlib_renderer_add_filter: (skip) * @renderer: a #CoglRenderer * @func: the callback function * @data: user data passed to @func when called * * Adds a callback function that will receive all native events. The * function can stop further processing of the event by return * %COGL_FILTER_REMOVE. */ COGL_EXPORT void cogl_xlib_renderer_add_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data); /** * cogl_xlib_renderer_remove_filter: (skip) * @renderer: a #CoglRenderer * @func: the callback function * @data: user data given when the callback was installed * * Removes a callback that was previously added with * cogl_xlib_renderer_add_filter(). */ COGL_EXPORT void cogl_xlib_renderer_remove_filter (CoglRenderer *renderer, CoglXlibFilterFunc func, void *data); /** * cogl_xlib_renderer_get_foreign_display: (skip) * @renderer: a #CoglRenderer * * Return value: the foreign Xlib display that will be used by any Xlib based * winsys backend. The display needs to be set with * cogl_xlib_renderer_set_foreign_display() before this function is called. */ COGL_EXPORT Display * cogl_xlib_renderer_get_foreign_display (CoglRenderer *renderer); /** * cogl_xlib_renderer_set_foreign_display: (skip) * @renderer: a #CoglRenderer * * Sets a foreign Xlib display that Cogl will use for and Xlib based winsys * backend. * * Note that calling this function will automatically disable Cogl's * event retrieval. Cogl still needs to see all of the X events so the * application should also use cogl_xlib_renderer_handle_event() if it * uses this function. */ COGL_EXPORT void cogl_xlib_renderer_set_foreign_display (CoglRenderer *renderer, Display *display); /** * cogl_xlib_renderer_get_display: (skip) */ COGL_EXPORT Display * cogl_xlib_renderer_get_display (CoglRenderer *renderer); /** * cogl_xlib_renderer_request_reset_on_video_memory_purge: (skip) * @renderer: a #CoglRenderer * @enable: The new value * * Sets whether Cogl should make use of the * NV_robustness_video_memory_purge extension, if exposed by the * driver, by initializing the GLX context appropriately. * * The extension is only useful when running on certain versions of * the NVIDIA driver. Quoting from the spec: * * "The NVIDIA OpenGL driver architecture on Linux has a limitation: * resources located in video memory are not persistent across certain * events. VT switches, suspend/resume events, and mode switching * events may erase the contents of video memory. Any resource that * is located exclusively in video memory, such as framebuffer objects * (FBOs), will be lost." * * "This extension provides a way for applications to discover when video * memory content has been lost, so that the application can re-populate * the video memory content as necessary." * * "Any driver that exposes this extension is a driver that considers * video memory to be volatile. Once the driver stack has been * improved, the extension will no longer be exposed." * * cogl_get_graphics_reset_status() needs to be called at least once * every frame to find out if video memory was purged. * * Note that this doesn't cause Cogl to enable robust buffer access * but other context reset errors may still happen and be reported via * cogl_get_graphics_reset_status() if external factors cause the * driver to trigger them. * * This defaults to %FALSE and is effective only if called before * cogl_display_setup() . */ COGL_EXPORT void cogl_xlib_renderer_request_reset_on_video_memory_purge (CoglRenderer *renderer, gboolean enable); G_END_DECLS /* The gobject introspection scanner seems to parse public headers in * isolation which means we need to be extra careful about how we * define and undefine __COGL_H_INSIDE__ used to detect when internal * headers are incorrectly included by developers. In the gobject * introspection case we have to manually define __COGL_H_INSIDE__ as * a commandline argument for the scanner which means we must be * careful not to undefine it in a header... */ #ifdef __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE_COGL_XLIB_RENDERER_ #undef __COGL_H_INSIDE__ #undef __COGL_XLIB_RENDERER_H_MUST_UNDEF_COGL_H_INSIDE_COGL_XLIB_RENDERER_ #endif #endif /* __COGL_XLIB_RENDERER_H__ */ muffin-6.4.1/cogl/cogl/cogl-memory-stack.c0000664000175000017500000001370214723361714017316 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * CoglMemoryStack provides a really simple, but lightning fast * memory stack allocation strategy: * * - The underlying pool of memory is grow-only. * - The pool is considered to be a stack which may be comprised * of multiple smaller stacks. Allocation is done as follows: * - If there's enough memory in the current sub-stack then the * stack-pointer will be returned as the allocation and the * stack-pointer will be incremented by the allocation size. * - If there isn't enough memory in the current sub-stack * then a new sub-stack is allocated twice as big as the current * sub-stack or twice as big as the requested allocation size if * that's bigger and the stack-pointer is set to the start of the * new sub-stack. * - Allocations can't be freed in a random-order, you can only * rewind the entire stack back to the start. There is no * the concept of stack frames to allow partial rewinds. * * For example; we plan to use this in our tesselator which has to * allocate lots of small vertex, edge and face structures because * when tesselation has been finished we just want to free the whole * lot in one go. * * * Authors: * Robert Bragg */ #include "cogl-config.h" #include "cogl-memory-stack-private.h" #include "cogl-list.h" #include #include typedef struct _CoglMemorySubStack { CoglList link; size_t bytes; uint8_t *data; } CoglMemorySubStack; struct _CoglMemoryStack { CoglList sub_stacks; CoglMemorySubStack *sub_stack; size_t sub_stack_offset; }; static CoglMemorySubStack * _cogl_memory_sub_stack_alloc (size_t bytes) { CoglMemorySubStack *sub_stack = g_slice_new (CoglMemorySubStack); sub_stack->bytes = bytes; sub_stack->data = g_malloc (bytes); return sub_stack; } static void _cogl_memory_stack_add_sub_stack (CoglMemoryStack *stack, size_t sub_stack_bytes) { CoglMemorySubStack *sub_stack = _cogl_memory_sub_stack_alloc (sub_stack_bytes); _cogl_list_insert (stack->sub_stacks.prev, &sub_stack->link); stack->sub_stack = sub_stack; stack->sub_stack_offset = 0; } CoglMemoryStack * _cogl_memory_stack_new (size_t initial_size_bytes) { CoglMemoryStack *stack = g_slice_new0 (CoglMemoryStack); _cogl_list_init (&stack->sub_stacks); _cogl_memory_stack_add_sub_stack (stack, initial_size_bytes); return stack; } void * _cogl_memory_stack_alloc (CoglMemoryStack *stack, size_t bytes) { CoglMemorySubStack *sub_stack; void *ret; sub_stack = stack->sub_stack; if (G_LIKELY (sub_stack->bytes - stack->sub_stack_offset >= bytes)) { ret = sub_stack->data + stack->sub_stack_offset; stack->sub_stack_offset += bytes; return ret; } /* If the stack has been rewound and then a large initial allocation * is made then we may need to skip over one or more of the * sub-stacks that are too small for the requested allocation * size... */ for (_cogl_list_set_iterator (sub_stack->link.next, sub_stack, link); &sub_stack->link != &stack->sub_stacks; _cogl_list_set_iterator (sub_stack->link.next, sub_stack, link)) { if (sub_stack->bytes >= bytes) { ret = sub_stack->data; stack->sub_stack = sub_stack; stack->sub_stack_offset = bytes; return ret; } } /* Finally if we couldn't find a free sub-stack with enough space * for the requested allocation we allocate another sub-stack that's * twice as big as the last sub-stack or twice as big as the * requested allocation if that's bigger. */ sub_stack = _cogl_container_of (stack->sub_stacks.prev, CoglMemorySubStack, link); _cogl_memory_stack_add_sub_stack (stack, MAX (sub_stack->bytes, bytes) * 2); sub_stack = _cogl_container_of (stack->sub_stacks.prev, CoglMemorySubStack, link); stack->sub_stack_offset += bytes; return sub_stack->data; } void _cogl_memory_stack_rewind (CoglMemoryStack *stack) { stack->sub_stack = _cogl_container_of (stack->sub_stacks.next, CoglMemorySubStack, link); stack->sub_stack_offset = 0; } static void _cogl_memory_sub_stack_free (CoglMemorySubStack *sub_stack) { g_free (sub_stack->data); g_slice_free (CoglMemorySubStack, sub_stack); } void _cogl_memory_stack_free (CoglMemoryStack *stack) { while (!_cogl_list_empty (&stack->sub_stacks)) { CoglMemorySubStack *sub_stack = _cogl_container_of (stack->sub_stacks.next, CoglMemorySubStack, link); _cogl_list_remove (&sub_stack->link); _cogl_memory_sub_stack_free (sub_stack); } g_slice_free (CoglMemoryStack, stack); } muffin-6.4.1/cogl/cogl/cogl-glib-source.c0000664000175000017500000001245314723361714017120 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include "cogl-glib-source.h" #include "cogl-poll.h" typedef struct _CoglGLibSource { GSource source; CoglRenderer *renderer; GArray *poll_fds; int poll_fds_age; int64_t expiration_time; } CoglGLibSource; static gboolean cogl_glib_source_prepare (GSource *source, int *timeout) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; CoglPollFD *poll_fds; int n_poll_fds; int64_t cogl_timeout; int age; int i; age = cogl_poll_renderer_get_info (cogl_source->renderer, &poll_fds, &n_poll_fds, &cogl_timeout); /* We have to be careful not to call g_source_add/remove_poll unless * the FDs have changed because it will cause the main loop to * immediately wake up. If we call it every time the source is * prepared it will effectively never go idle. */ if (age != cogl_source->poll_fds_age) { /* Remove any existing polls before adding the new ones */ for (i = 0; i < cogl_source->poll_fds->len; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); g_source_remove_poll (source, poll_fd); } g_array_set_size (cogl_source->poll_fds, n_poll_fds); for (i = 0; i < n_poll_fds; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); poll_fd->fd = poll_fds[i].fd; g_source_add_poll (source, poll_fd); } } cogl_source->poll_fds_age = age; /* Update the events */ for (i = 0; i < n_poll_fds; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); poll_fd->events = poll_fds[i].events; poll_fd->revents = 0; } if (cogl_timeout == -1) { *timeout = -1; cogl_source->expiration_time = -1; } else { /* Round up to ensure that we don't try again too early */ *timeout = (cogl_timeout + 999) / 1000; cogl_source->expiration_time = (g_source_get_time (source) + cogl_timeout); } return *timeout == 0; } static gboolean cogl_glib_source_check (GSource *source) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; int i; if (cogl_source->expiration_time >= 0 && g_source_get_time (source) >= cogl_source->expiration_time) return TRUE; for (i = 0; i < cogl_source->poll_fds->len; i++) { GPollFD *poll_fd = &g_array_index (cogl_source->poll_fds, GPollFD, i); if (poll_fd->revents != 0) return TRUE; } return FALSE; } static gboolean cogl_glib_source_dispatch (GSource *source, GSourceFunc callback, void *user_data) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; CoglPollFD *poll_fds = (CoglPollFD *) &g_array_index (cogl_source->poll_fds, GPollFD, 0); cogl_poll_renderer_dispatch (cogl_source->renderer, poll_fds, cogl_source->poll_fds->len); return TRUE; } static void cogl_glib_source_finalize (GSource *source) { CoglGLibSource *cogl_source = (CoglGLibSource *) source; g_array_free (cogl_source->poll_fds, TRUE); } static GSourceFuncs cogl_glib_source_funcs = { cogl_glib_source_prepare, cogl_glib_source_check, cogl_glib_source_dispatch, cogl_glib_source_finalize }; GSource * cogl_glib_renderer_source_new (CoglRenderer *renderer, int priority) { GSource *source; CoglGLibSource *cogl_source; source = g_source_new (&cogl_glib_source_funcs, sizeof (CoglGLibSource)); cogl_source = (CoglGLibSource *) source; cogl_source->renderer = renderer; cogl_source->poll_fds = g_array_new (FALSE, FALSE, sizeof (GPollFD)); if (priority != G_PRIORITY_DEFAULT) g_source_set_priority (source, priority); return source; } GSource * cogl_glib_source_new (CoglContext *context, int priority) { return cogl_glib_renderer_source_new (cogl_context_get_renderer (context), priority); } muffin-6.4.1/cogl/cogl/cogl-glsl-shader-private.h0000664000175000017500000000341114723361714020561 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef _COGL_GLSL_SHADER_PRIVATE_H_ #define _COGL_GLSL_SHADER_PRIVATE_H_ void _cogl_glsl_shader_set_source_with_boilerplate (CoglContext *ctx, GLuint shader_gl_handle, GLenum shader_gl_type, CoglPipeline *pipeline, GLsizei count_in, const char **strings_in, const GLint *lengths_in); #endif /* _COGL_GLSL_SHADER_PRIVATE_H_ */ muffin-6.4.1/cogl/cogl/cogl-list.c0000664000175000017500000000440114723361714015652 0ustar fabiofabio/* * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2011, 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* This list implementation is based on the Wayland source code */ #include "cogl-config.h" #include #include #include "cogl-list.h" void _cogl_list_init (CoglList *list) { list->prev = list; list->next = list; } void _cogl_list_insert (CoglList *list, CoglList *elm) { elm->prev = list; elm->next = list->next; list->next = elm; elm->next->prev = elm; } void _cogl_list_remove (CoglList *elm) { elm->prev->next = elm->next; elm->next->prev = elm->prev; elm->next = NULL; elm->prev = NULL; } int _cogl_list_length (CoglList *list) { CoglList *e; int count; count = 0; e = list->next; while (e != list) { e = e->next; count++; } return count; } int _cogl_list_empty (CoglList *list) { return list->next == list; } void _cogl_list_insert_list (CoglList *list, CoglList *other) { if (_cogl_list_empty (other)) return; other->next->prev = list; other->prev->next = list->next; list->next->prev = other->prev; list->next = other->next; } muffin-6.4.1/cogl/cogl/cogl-texture-driver.h0000664000175000017500000001253114723361714017700 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_TEXTURE_DRIVER_H #define __COGL_TEXTURE_DRIVER_H typedef struct _CoglTextureDriver CoglTextureDriver; struct _CoglTextureDriver { /* * A very small wrapper around glGenTextures() that ensures we default to * non-mipmap filters when creating textures. This is to save some memory as * the driver will not allocate room for the mipmap tree. */ GLuint (* gen) (CoglContext *ctx, GLenum gl_target, CoglPixelFormat internal_format); /* * This uploads a sub-region from source_bmp to a single GL texture * handle (i.e a single CoglTexture slice) * * It also updates the array of tex->first_pixels[slice_index] if * dst_{x,y} == 0 * * The driver abstraction is in place because GLES doesn't support the pixel * store options required to source from a subregion, so for GLES we have * to manually create a transient source bitmap. * * XXX: sorry for the ridiculous number of arguments :-( */ gboolean (* upload_subregion_to_gl) (CoglContext *ctx, CoglTexture *texture, int src_x, int src_y, int dst_x, int dst_y, int width, int height, int level, CoglBitmap *source_bmp, GLuint source_gl_format, GLuint source_gl_type, GError **error); /* * Replaces the contents of the GL texture with the entire bitmap. On * GL this just directly calls glTexImage2D, but under GLES it needs * to copy the bitmap if the rowstride is not a multiple of a possible * alignment value because there is no GL_UNPACK_ROW_LENGTH */ gboolean (* upload_to_gl) (CoglContext *ctx, GLenum gl_target, GLuint gl_handle, CoglBitmap *source_bmp, GLint internal_gl_format, GLuint source_gl_format, GLuint source_gl_type, GError **error); /* * This sets up the glPixelStore state for an download to a destination with * the same size, and with no offset. */ /* NB: GLES can't download pixel data into a sub region of a larger * destination buffer, the GL driver has a more flexible version of * this function that it uses internally. */ void (* prep_gl_for_pixels_download) (CoglContext *ctx, int image_width, int pixels_rowstride, int pixels_bpp); /* * This driver abstraction is needed because GLES doesn't support * glGetTexImage (). On GLES this currently just returns FALSE which * will lead to a generic fallback path being used that simply * renders the texture and reads it back from the framebuffer. (See * _cogl_texture_draw_and_read () ) */ gboolean (* gl_get_tex_image) (CoglContext *ctx, GLenum gl_target, GLenum dest_gl_format, GLenum dest_gl_type, uint8_t *dest); /* * It may depend on the driver as to what texture sizes are supported... */ gboolean (* size_supported) (CoglContext *ctx, GLenum gl_target, GLenum gl_intformat, GLenum gl_format, GLenum gl_type, int width, int height); /* * The driver may impose constraints on what formats can be used to store * texture data read from textures. For example GLES currently only supports * RGBA_8888, and so we need to manually convert the data if the final * destination has another format. */ CoglPixelFormat (* find_best_gl_get_data_format) (CoglContext *context, CoglPixelFormat format, GLenum *closest_gl_format, GLenum *closest_gl_type); }; #endif /* __COGL_TEXTURE_DRIVER_H */ muffin-6.4.1/cogl/cogl/cogl-types.h0000664000175000017500000003774514723361714016071 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_TYPES_H__ #define __COGL_TYPES_H__ #include #include #include #include #include #include G_BEGIN_DECLS /** * SECTION:cogl-types * @short_description: Types used throughout the library * * General types used by various Cogl functions. */ /* Some structures are meant to be opaque but they have public definitions because we want the size to be public so they can be allocated on the stack. This macro is used to ensure that users don't accidentally access private members */ #ifdef COGL_COMPILATION #define COGL_PRIVATE(x) x #else #define COGL_PRIVATE(x) private_member_ ## x #endif /* To help catch accidental changes to public structs that should * be stack allocated we use this macro to compile time assert that * a struct size is as expected. */ #define COGL_STRUCT_SIZE_ASSERT(TYPE, SIZE) \ typedef struct { \ char compile_time_assert_ ## TYPE ## _size[ \ (sizeof (TYPE) == (SIZE)) ? 1 : -1]; \ } _ ## TYPE ## SizeCheck /** * CoglHandle: * * Type used for storing references to cogl objects, the CoglHandle is * a fully opaque type without any public data members. */ typedef void * CoglHandle; #define COGL_TYPE_HANDLE (cogl_handle_get_type ()) COGL_EXPORT GType cogl_handle_get_type (void) G_GNUC_CONST; /* We forward declare this in cogl-types to avoid circular dependencies * between cogl-matrix.h and cogl-quaterion.h */ typedef struct _CoglMatrix CoglMatrix; /** * CoglAngle: * * Integer representation of an angle such that 1024 corresponds to * full circle (i.e., 2 * pi). * * Since: 1.0 */ typedef int32_t CoglAngle; typedef struct _CoglColor CoglColor; typedef struct _CoglTextureVertex CoglTextureVertex; /** * CoglDmaBufHandle: (skip) * * An opaque type that tracks the lifetime of a DMA buffer fd. Release * with cogl_dma_buf_handle_free(). */ typedef struct _CoglDmaBufHandle CoglDmaBufHandle; /* Enum declarations */ #define COGL_A_BIT (1 << 4) #define COGL_BGR_BIT (1 << 5) #define COGL_AFIRST_BIT (1 << 6) #define COGL_PREMULT_BIT (1 << 7) #define COGL_DEPTH_BIT (1 << 8) #define COGL_STENCIL_BIT (1 << 9) /** * CoglBufferTarget: * @COGL_WINDOW_BUFFER: FIXME * @COGL_OFFSCREEN_BUFFER: FIXME * * Target flags for FBOs. * * Since: 0.8 */ typedef enum { COGL_WINDOW_BUFFER = (1 << 1), COGL_OFFSCREEN_BUFFER = (1 << 2) } CoglBufferTarget; /** * CoglColor: * @red: amount of red * @green: amount of green * @blue: amount of green * @alpha: alpha * * A structure for holding a color definition. The contents of * the CoglColor structure are private and should never by accessed * directly. * * Since: 1.0 */ struct _CoglColor { /*< private >*/ uint8_t COGL_PRIVATE (red); uint8_t COGL_PRIVATE (green); uint8_t COGL_PRIVATE (blue); uint8_t COGL_PRIVATE (alpha); /* padding in case we want to change to floats at * some point */ uint32_t COGL_PRIVATE (padding0); uint32_t COGL_PRIVATE (padding1); uint32_t COGL_PRIVATE (padding2); }; COGL_STRUCT_SIZE_ASSERT (CoglColor, 16); /** * CoglTextureVertex: * @x: Model x-coordinate * @y: Model y-coordinate * @z: Model z-coordinate * @tx: Texture x-coordinate * @ty: Texture y-coordinate * @color: The color to use at this vertex. This is ignored if * use_color is %FALSE when calling cogl_polygon() * * Used to specify vertex information when calling cogl_polygon() */ struct _CoglTextureVertex { float x, y, z; float tx, ty; CoglColor color; }; COGL_STRUCT_SIZE_ASSERT (CoglTextureVertex, 36); /** * CoglTextureFlags: * @COGL_TEXTURE_NONE: No flags specified * @COGL_TEXTURE_NO_AUTO_MIPMAP: Disables the automatic generation of * the mipmap pyramid from the base level image whenever it is * updated. The mipmaps are only generated when the texture is * rendered with a mipmap filter so it should be free to leave out * this flag when using other filtering modes * @COGL_TEXTURE_NO_SLICING: Disables the slicing of the texture * @COGL_TEXTURE_NO_ATLAS: Disables the insertion of the texture inside * the texture atlas used by Cogl * * Flags to pass to the cogl_texture_new_* family of functions. * * Since: 1.0 */ typedef enum { COGL_TEXTURE_NONE = 0, COGL_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, COGL_TEXTURE_NO_SLICING = 1 << 1, COGL_TEXTURE_NO_ATLAS = 1 << 2 } CoglTextureFlags; /** * COGL_BLEND_STRING_ERROR: * * #GError domain for blend string parser errors * * Since: 1.0 */ #define COGL_BLEND_STRING_ERROR (cogl_blend_string_error_quark ()) /** * CoglBlendStringError: * @COGL_BLEND_STRING_ERROR_PARSE_ERROR: Generic parse error * @COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR: Argument parse error * @COGL_BLEND_STRING_ERROR_INVALID_ERROR: Internal parser error * @COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR: Blend string not * supported by the GPU * * Error enumeration for the blend strings parser * * Since: 1.0 */ typedef enum /*< prefix=COGL_BLEND_STRING_ERROR >*/ { COGL_BLEND_STRING_ERROR_PARSE_ERROR, COGL_BLEND_STRING_ERROR_ARGUMENT_PARSE_ERROR, COGL_BLEND_STRING_ERROR_INVALID_ERROR, COGL_BLEND_STRING_ERROR_GPU_UNSUPPORTED_ERROR } CoglBlendStringError; uint32_t cogl_blend_string_error_quark (void); #define COGL_SYSTEM_ERROR (_cogl_system_error_quark ()) /** * CoglSystemError: * @COGL_SYSTEM_ERROR_UNSUPPORTED: You tried to use a feature or * configuration not currently available. * @COGL_SYSTEM_ERROR_NO_MEMORY: You tried to allocate a resource * such as a texture and there wasn't enough memory. * * Error enumeration for Cogl * * The @COGL_SYSTEM_ERROR_UNSUPPORTED error can be thrown for a * variety of reasons. For example: * * * You've tried to use a feature that is not * advertised by cogl_has_feature(). * The GPU can not handle the configuration you have * requested. An example might be if you try to use too many texture * layers in a single #CoglPipeline * The driver does not support some * configuration. * * * Currently this is only used by Cogl API marked as experimental so * this enum should also be considered experimental. * * Since: 1.4 * Stability: unstable */ typedef enum /*< prefix=COGL_ERROR >*/ { COGL_SYSTEM_ERROR_UNSUPPORTED, COGL_SYSTEM_ERROR_NO_MEMORY } CoglSystemError; COGL_EXPORT uint32_t _cogl_system_error_quark (void); /** * CoglAttributeType: * @COGL_ATTRIBUTE_TYPE_BYTE: Data is the same size of a byte * @COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE: Data is the same size of an * unsigned byte * @COGL_ATTRIBUTE_TYPE_SHORT: Data is the same size of a short integer * @COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT: Data is the same size of * an unsigned short integer * @COGL_ATTRIBUTE_TYPE_FLOAT: Data is the same size of a float * * Data types for the components of a vertex attribute. * * Since: 1.0 */ typedef enum { COGL_ATTRIBUTE_TYPE_BYTE = 0x1400, COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE = 0x1401, COGL_ATTRIBUTE_TYPE_SHORT = 0x1402, COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT = 0x1403, COGL_ATTRIBUTE_TYPE_FLOAT = 0x1406 } CoglAttributeType; /** * CoglIndicesType: * @COGL_INDICES_TYPE_UNSIGNED_BYTE: Your indices are unsigned bytes * @COGL_INDICES_TYPE_UNSIGNED_SHORT: Your indices are unsigned shorts * @COGL_INDICES_TYPE_UNSIGNED_INT: Your indices are unsigned ints * * You should aim to use the smallest data type that gives you enough * range, since it reduces the size of your index array and can help * reduce the demand on memory bandwidth. * * Note that %COGL_INDICES_TYPE_UNSIGNED_INT is only supported if the * %COGL_FEATURE_ID_UNSIGNED_INT_INDICES feature is available. This * should always be available on OpenGL but on OpenGL ES it will only * be available if the GL_OES_element_index_uint extension is * advertized. */ typedef enum { COGL_INDICES_TYPE_UNSIGNED_BYTE, COGL_INDICES_TYPE_UNSIGNED_SHORT, COGL_INDICES_TYPE_UNSIGNED_INT } CoglIndicesType; /** * CoglVerticesMode: * @COGL_VERTICES_MODE_POINTS: FIXME, equivalent to * GL_POINTS * @COGL_VERTICES_MODE_LINES: FIXME, equivalent to GL_LINES * @COGL_VERTICES_MODE_LINE_LOOP: FIXME, equivalent to * GL_LINE_LOOP * @COGL_VERTICES_MODE_LINE_STRIP: FIXME, equivalent to * GL_LINE_STRIP * @COGL_VERTICES_MODE_TRIANGLES: FIXME, equivalent to * GL_TRIANGLES * @COGL_VERTICES_MODE_TRIANGLE_STRIP: FIXME, equivalent to * GL_TRIANGLE_STRIP * @COGL_VERTICES_MODE_TRIANGLE_FAN: FIXME, equivalent to GL_TRIANGLE_FAN * * Different ways of interpreting vertices when drawing. * * Since: 1.0 */ typedef enum { COGL_VERTICES_MODE_POINTS = 0x0000, COGL_VERTICES_MODE_LINES = 0x0001, COGL_VERTICES_MODE_LINE_LOOP = 0x0002, COGL_VERTICES_MODE_LINE_STRIP = 0x0003, COGL_VERTICES_MODE_TRIANGLES = 0x0004, COGL_VERTICES_MODE_TRIANGLE_STRIP = 0x0005, COGL_VERTICES_MODE_TRIANGLE_FAN = 0x0006 } CoglVerticesMode; /* NB: The above definitions are taken from gl.h equivalents */ /* XXX: should this be CoglMaterialDepthTestFunction? * It makes it very verbose but would be consistent with * CoglMaterialWrapMode */ /** * CoglDepthTestFunction: * @COGL_DEPTH_TEST_FUNCTION_NEVER: Never passes. * @COGL_DEPTH_TEST_FUNCTION_LESS: Passes if the fragment's depth * value is less than the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_EQUAL: Passes if the fragment's depth * value is equal to the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_LEQUAL: Passes if the fragment's depth * value is less or equal to the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_GREATER: Passes if the fragment's depth * value is greater than the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_NOTEQUAL: Passes if the fragment's depth * value is not equal to the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_GEQUAL: Passes if the fragment's depth * value greater than or equal to the value currently in the depth buffer. * @COGL_DEPTH_TEST_FUNCTION_ALWAYS: Always passes. * * When using depth testing one of these functions is used to compare * the depth of an incoming fragment against the depth value currently * stored in the depth buffer. The function is changed using * cogl_depth_state_set_test_function(). * * The test is only done when depth testing is explicitly enabled. (See * cogl_depth_state_set_test_enabled()) */ typedef enum { COGL_DEPTH_TEST_FUNCTION_NEVER = 0x0200, COGL_DEPTH_TEST_FUNCTION_LESS = 0x0201, COGL_DEPTH_TEST_FUNCTION_EQUAL = 0x0202, COGL_DEPTH_TEST_FUNCTION_LEQUAL = 0x0203, COGL_DEPTH_TEST_FUNCTION_GREATER = 0x0204, COGL_DEPTH_TEST_FUNCTION_NOTEQUAL = 0x0205, COGL_DEPTH_TEST_FUNCTION_GEQUAL = 0x0206, COGL_DEPTH_TEST_FUNCTION_ALWAYS = 0x0207 } CoglDepthTestFunction; /* NB: The above definitions are taken from gl.h equivalents */ typedef enum /*< prefix=COGL_RENDERER_ERROR >*/ { COGL_RENDERER_ERROR_XLIB_DISPLAY_OPEN, COGL_RENDERER_ERROR_BAD_CONSTRAINT } CoglRendererError; /** * CoglFilterReturn: * @COGL_FILTER_CONTINUE: The event was not handled, continues the * processing * @COGL_FILTER_REMOVE: Remove the event, stops the processing * * Return values for the #CoglXlibFilterFunc and #CoglWin32FilterFunc functions. * * Stability: Unstable */ typedef enum _CoglFilterReturn { /*< prefix=COGL_FILTER >*/ COGL_FILTER_CONTINUE, COGL_FILTER_REMOVE } CoglFilterReturn; typedef enum _CoglWinsysFeature { /* Available if the window system can support multiple onscreen * framebuffers at the same time. */ COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, /* Available if onscreen framebuffer swaps can be automatically * throttled to the vblank frequency. */ COGL_WINSYS_FEATURE_SWAP_THROTTLE, /* Available if its possible to query a counter that * increments at each vblank. */ COGL_WINSYS_FEATURE_VBLANK_COUNTER, /* Available if its possible to wait until the next vertical * blank period */ COGL_WINSYS_FEATURE_VBLANK_WAIT, /* Available if the window system supports mapping native * pixmaps to textures. */ COGL_WINSYS_FEATURE_TEXTURE_FROM_PIXMAP, /* Available if the window system supports reporting an event * for swap buffer completions. */ COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, /* Available if it's possible to swap a list of sub rectangles * from the back buffer to the front buffer */ COGL_WINSYS_FEATURE_SWAP_REGION, /* Available if swap_region requests can be automatically throttled * to the vblank frequency. */ COGL_WINSYS_FEATURE_SWAP_REGION_THROTTLE, /* Available if the swap region implementation won't tear and thus * only needs to be throttled to the framerate */ COGL_WINSYS_FEATURE_SWAP_REGION_SYNCHRONIZED, /* Avaiable if the age of the back buffer can be queried */ COGL_WINSYS_FEATURE_BUFFER_AGE, /* Avaiable if the winsys directly handles _SYNC and _COMPLETE events */ COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, COGL_WINSYS_FEATURE_N_FEATURES } CoglWinsysFeature; /** * CoglWinding: * @COGL_WINDING_CLOCKWISE: Vertices are in a clockwise order * @COGL_WINDING_COUNTER_CLOCKWISE: Vertices are in a counter-clockwise order * * Enum used to represent the two directions of rotation. This can be * used to set the front face for culling by calling * cogl_pipeline_set_front_face_winding(). */ typedef enum { COGL_WINDING_CLOCKWISE, COGL_WINDING_COUNTER_CLOCKWISE } CoglWinding; /** * CoglBufferBit: * @COGL_BUFFER_BIT_COLOR: Selects the primary color buffer * @COGL_BUFFER_BIT_DEPTH: Selects the depth buffer * @COGL_BUFFER_BIT_STENCIL: Selects the stencil buffer * * Types of auxiliary buffers * * Since: 1.0 */ typedef enum { COGL_BUFFER_BIT_COLOR = 1L<<0, COGL_BUFFER_BIT_DEPTH = 1L<<1, COGL_BUFFER_BIT_STENCIL = 1L<<2 } CoglBufferBit; /** * CoglReadPixelsFlags: * @COGL_READ_PIXELS_COLOR_BUFFER: Read from the color buffer * * Flags for cogl_framebuffer_read_pixels_into_bitmap() * * Since: 1.0 */ typedef enum /*< prefix=COGL_READ_PIXELS >*/ { COGL_READ_PIXELS_COLOR_BUFFER = 1L << 0 } CoglReadPixelsFlags; /** * CoglStereoMode: * @COGL_STEREO_BOTH: draw to both stereo buffers * @COGL_STEREO_LEFT: draw only to the left stereo buffer * @COGL_STEREO_RIGHT: draw only to the left stereo buffer * * Represents how draw should affect the two buffers * of a stereo framebuffer. See cogl_framebuffer_set_stereo_mode(). */ typedef enum { COGL_STEREO_BOTH, COGL_STEREO_LEFT, COGL_STEREO_RIGHT } CoglStereoMode; G_END_DECLS #endif /* __COGL_TYPES_H__ */ muffin-6.4.1/cogl/cogl/cogl-renderer.c0000664000175000017500000005020414723361714016507 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include #include "cogl-util.h" #include "cogl-private.h" #include "cogl-object.h" #include "cogl-context-private.h" #include "cogl-mutter.h" #include "cogl-renderer.h" #include "cogl-renderer-private.h" #include "cogl-display-private.h" #include "cogl-gtype-private.h" #include "winsys/cogl-winsys-private.h" #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT #include "winsys/cogl-winsys-egl-x11-private.h" #endif #ifdef COGL_HAS_GLX_SUPPORT #include "winsys/cogl-winsys-glx-private.h" #endif #ifdef COGL_HAS_XLIB_SUPPORT #include "cogl-xlib-renderer.h" #endif #ifdef HAVE_COGL_GL extern const CoglTextureDriver _cogl_texture_driver_gl; extern const CoglDriverVtable _cogl_driver_gl; #endif #if defined (HAVE_COGL_GLES2) extern const CoglTextureDriver _cogl_texture_driver_gles; extern const CoglDriverVtable _cogl_driver_gles; #endif extern const CoglDriverVtable _cogl_driver_nop; typedef struct _CoglDriverDescription { CoglDriver id; const char *name; /* It would be nice to make this a pointer and then use a compound * literal from C99 to initialise it but we probably can't get away * with using C99 here. Instead we'll just use a fixed-size array. * GCC should complain if someone adds an 8th feature to a * driver. */ const CoglPrivateFeature private_features[8]; const CoglDriverVtable *vtable; const CoglTextureDriver *texture_driver; const char *libgl_name; } CoglDriverDescription; static CoglDriverDescription _cogl_drivers[] = { #ifdef HAVE_COGL_GL { COGL_DRIVER_GL, "gl", { COGL_PRIVATE_FEATURE_ANY_GL, -1 }, &_cogl_driver_gl, &_cogl_texture_driver_gl, COGL_GL_LIBNAME, }, { COGL_DRIVER_GL3, "gl3", { COGL_PRIVATE_FEATURE_ANY_GL, -1 }, &_cogl_driver_gl, &_cogl_texture_driver_gl, COGL_GL_LIBNAME, }, #endif #ifdef HAVE_COGL_GLES2 { COGL_DRIVER_GLES2, "gles2", { COGL_PRIVATE_FEATURE_ANY_GL, -1 }, &_cogl_driver_gles, &_cogl_texture_driver_gles, COGL_GLES2_LIBNAME, }, #endif { COGL_DRIVER_NOP, "nop", { -1 }, &_cogl_driver_nop, NULL, /* texture driver */ NULL /* libgl_name */ } }; static CoglWinsysVtableGetter _cogl_winsys_vtable_getters[] = { #ifdef COGL_HAS_GLX_SUPPORT _cogl_winsys_glx_get_vtable, #endif #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT _cogl_winsys_egl_xlib_get_vtable, #endif }; static void _cogl_renderer_free (CoglRenderer *renderer); COGL_OBJECT_DEFINE (Renderer, renderer); COGL_GTYPE_DEFINE_CLASS (Renderer, renderer); typedef struct _CoglNativeFilterClosure { CoglNativeFilterFunc func; void *data; } CoglNativeFilterClosure; uint32_t cogl_renderer_error_quark (void) { return g_quark_from_static_string ("cogl-renderer-error-quark"); } static const CoglWinsysVtable * _cogl_renderer_get_winsys (CoglRenderer *renderer) { return renderer->winsys_vtable; } static void native_filter_closure_free (CoglNativeFilterClosure *closure) { g_slice_free (CoglNativeFilterClosure, closure); } static void _cogl_renderer_free (CoglRenderer *renderer) { const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); _cogl_closure_list_disconnect_all (&renderer->idle_closures); if (winsys) winsys->renderer_disconnect (renderer); if (renderer->libgl_module) g_module_close (renderer->libgl_module); g_slist_free_full (renderer->event_filters, (GDestroyNotify) native_filter_closure_free); g_array_free (renderer->poll_fds, TRUE); g_free (renderer); } CoglRenderer * cogl_renderer_new (void) { CoglRenderer *renderer = g_new0 (CoglRenderer, 1); _cogl_init (); renderer->connected = FALSE; renderer->event_filters = NULL; renderer->poll_fds = g_array_new (FALSE, TRUE, sizeof (CoglPollFD)); _cogl_list_init (&renderer->idle_closures); #ifdef COGL_HAS_XLIB_SUPPORT renderer->xlib_enable_event_retrieval = TRUE; #endif return _cogl_renderer_object_new (renderer); } #ifdef COGL_HAS_XLIB_SUPPORT void cogl_xlib_renderer_set_foreign_display (CoglRenderer *renderer, Display *xdisplay) { g_return_if_fail (cogl_is_renderer (renderer)); /* NB: Renderers are considered immutable once connected */ g_return_if_fail (!renderer->connected); renderer->foreign_xdpy = xdisplay; /* If the application is using a foreign display then we can assume it will also do its own event retrieval */ renderer->xlib_enable_event_retrieval = FALSE; } Display * cogl_xlib_renderer_get_foreign_display (CoglRenderer *renderer) { g_return_val_if_fail (cogl_is_renderer (renderer), NULL); return renderer->foreign_xdpy; } void cogl_xlib_renderer_request_reset_on_video_memory_purge (CoglRenderer *renderer, gboolean enable) { g_return_if_fail (cogl_is_renderer (renderer)); g_return_if_fail (!renderer->connected); renderer->xlib_want_reset_on_video_memory_purge = enable; } #endif /* COGL_HAS_XLIB_SUPPORT */ gboolean cogl_renderer_check_onscreen_template (CoglRenderer *renderer, CoglOnscreenTemplate *onscreen_template, GError **error) { CoglDisplay *display; if (!cogl_renderer_connect (renderer, error)) return FALSE; display = cogl_display_new (renderer, onscreen_template); if (!cogl_display_setup (display, error)) { cogl_object_unref (display); return FALSE; } cogl_object_unref (display); return TRUE; } typedef gboolean (*CoglDriverCallback) (CoglDriverDescription *description, void *user_data); static void foreach_driver_description (CoglDriver driver_override, CoglDriverCallback callback, void *user_data) { #ifdef COGL_DEFAULT_DRIVER const CoglDriverDescription *default_driver = NULL; #endif int i; if (driver_override != COGL_DRIVER_ANY) { for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) { if (_cogl_drivers[i].id == driver_override) { callback (&_cogl_drivers[i], user_data); return; } } g_warn_if_reached (); return; } #ifdef COGL_DEFAULT_DRIVER for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) { const CoglDriverDescription *desc = &_cogl_drivers[i]; if (g_ascii_strcasecmp (desc->name, COGL_DEFAULT_DRIVER) == 0) { default_driver = desc; break; } } if (default_driver) { if (!callback (default_driver, user_data)) return; } #endif for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) { #ifdef COGL_DEFAULT_DRIVER if (&_cogl_drivers[i] == default_driver) continue; #endif if (!callback (&_cogl_drivers[i], user_data)) return; } } static CoglDriver driver_name_to_id (const char *name) { int i; for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) { if (g_ascii_strcasecmp (_cogl_drivers[i].name, name) == 0) return _cogl_drivers[i].id; } return COGL_DRIVER_ANY; } static const char * driver_id_to_name (CoglDriver id) { switch (id) { case COGL_DRIVER_GL: return "gl"; case COGL_DRIVER_GL3: return "gl3"; case COGL_DRIVER_GLES2: return "gles2"; case COGL_DRIVER_NOP: return "nop"; case COGL_DRIVER_ANY: g_warn_if_reached (); return "any"; } g_warn_if_reached (); return "unknown"; } typedef struct _SatisfyConstraintsState { const CoglDriverDescription *driver_description; } SatisfyConstraintsState; /* XXX this is still uglier than it needs to be */ static gboolean satisfy_constraints (CoglDriverDescription *description, void *user_data) { SatisfyConstraintsState *state = user_data; state->driver_description = description; return FALSE; } static gboolean _cogl_renderer_choose_driver (CoglRenderer *renderer, GError **error) { const char *driver_name = g_getenv ("COGL_DRIVER"); CoglDriver driver_override = COGL_DRIVER_ANY; const char *invalid_override = NULL; const char *libgl_name; SatisfyConstraintsState state; const CoglDriverDescription *desc; int i; if (driver_name) { driver_override = driver_name_to_id (driver_name); if (driver_override == COGL_DRIVER_ANY) invalid_override = driver_name; } if (renderer->driver_override != COGL_DRIVER_ANY) { if (driver_override != COGL_DRIVER_ANY && renderer->driver_override != driver_override) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_BAD_CONSTRAINT, "Application driver selection conflicts with driver " "specified in configuration"); return FALSE; } driver_override = renderer->driver_override; } if (driver_override != COGL_DRIVER_ANY) { gboolean found = FALSE; int i; for (i = 0; i < G_N_ELEMENTS (_cogl_drivers); i++) { if (_cogl_drivers[i].id == driver_override) { found = TRUE; break; } } if (!found) invalid_override = driver_id_to_name (driver_override); } if (invalid_override) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_BAD_CONSTRAINT, "Driver \"%s\" is not available", invalid_override); return FALSE; } state.driver_description = NULL; foreach_driver_description (driver_override, satisfy_constraints, &state); if (!state.driver_description) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_BAD_CONSTRAINT, "No suitable driver found"); return FALSE; } desc = state.driver_description; renderer->driver = desc->id; renderer->driver_vtable = desc->vtable; renderer->texture_driver = desc->texture_driver; libgl_name = desc->libgl_name; memset(renderer->private_features, 0, sizeof (renderer->private_features)); for (i = 0; desc->private_features[i] != -1; i++) COGL_FLAGS_SET (renderer->private_features, desc->private_features[i], TRUE); if (COGL_FLAGS_GET (renderer->private_features, COGL_PRIVATE_FEATURE_ANY_GL)) { renderer->libgl_module = g_module_open (libgl_name, G_MODULE_BIND_LAZY); if (renderer->libgl_module == NULL) { g_set_error (error, COGL_DRIVER_ERROR, COGL_DRIVER_ERROR_FAILED_TO_LOAD_LIBRARY, "Failed to dynamically open the GL library \"%s\"", libgl_name); return FALSE; } } return TRUE; } /* Final connection API */ void cogl_renderer_set_custom_winsys (CoglRenderer *renderer, CoglCustomWinsysVtableGetter winsys_vtable_getter, void *user_data) { renderer->custom_winsys_user_data = user_data; renderer->custom_winsys_vtable_getter = winsys_vtable_getter; } static gboolean connect_custom_winsys (CoglRenderer *renderer, GError **error) { const CoglWinsysVtable *winsys; GError *tmp_error = NULL; GString *error_message; winsys = renderer->custom_winsys_vtable_getter (renderer); renderer->winsys_vtable = winsys; error_message = g_string_new (""); if (!winsys->renderer_connect (renderer, &tmp_error)) { g_string_append_c (error_message, '\n'); g_string_append (error_message, tmp_error->message); g_error_free (tmp_error); } else { renderer->connected = TRUE; g_string_free (error_message, TRUE); return TRUE; } renderer->winsys_vtable = NULL; g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "Failed to connected to any renderer: %s", error_message->str); g_string_free (error_message, TRUE); return FALSE; } gboolean cogl_renderer_connect (CoglRenderer *renderer, GError **error) { int i; GString *error_message; gboolean constraints_failed = FALSE; if (renderer->connected) return TRUE; /* The driver needs to be chosen before connecting the renderer because eglInitialize requires the library containing the GL API to be loaded before its called */ if (!_cogl_renderer_choose_driver (renderer, error)) return FALSE; if (renderer->custom_winsys_vtable_getter) return connect_custom_winsys (renderer, error); error_message = g_string_new (""); for (i = 0; i < G_N_ELEMENTS (_cogl_winsys_vtable_getters); i++) { const CoglWinsysVtable *winsys = _cogl_winsys_vtable_getters[i](); GError *tmp_error = NULL; GList *l; gboolean skip_due_to_constraints = FALSE; if (renderer->winsys_id_override != COGL_WINSYS_ID_ANY) { if (renderer->winsys_id_override != winsys->id) continue; } else { char *user_choice = getenv ("COGL_RENDERER"); if (user_choice && g_ascii_strcasecmp (winsys->name, user_choice) != 0) continue; } for (l = renderer->constraints; l; l = l->next) { CoglRendererConstraint constraint = GPOINTER_TO_UINT (l->data); if (!(winsys->constraints & constraint)) { skip_due_to_constraints = TRUE; break; } } if (skip_due_to_constraints) { constraints_failed |= TRUE; continue; } /* At least temporarily we will associate this winsys with * the renderer in-case ->renderer_connect calls API that * wants to query the current winsys... */ renderer->winsys_vtable = winsys; if (!winsys->renderer_connect (renderer, &tmp_error)) { g_string_append_c (error_message, '\n'); g_string_append (error_message, tmp_error->message); g_error_free (tmp_error); } else { renderer->connected = TRUE; g_string_free (error_message, TRUE); return TRUE; } } if (!renderer->connected) { if (constraints_failed) { g_set_error (error, COGL_RENDERER_ERROR, COGL_RENDERER_ERROR_BAD_CONSTRAINT, "Failed to connected to any renderer due to constraints"); return FALSE; } renderer->winsys_vtable = NULL; g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_INIT, "Failed to connected to any renderer: %s", error_message->str); g_string_free (error_message, TRUE); return FALSE; } return TRUE; } CoglFilterReturn _cogl_renderer_handle_native_event (CoglRenderer *renderer, void *event) { GSList *l, *next; /* Pass the event on to all of the registered filters in turn */ for (l = renderer->event_filters; l; l = next) { CoglNativeFilterClosure *closure = l->data; /* The next pointer is taken now so that we can handle the closure being removed during emission */ next = l->next; if (closure->func (event, closure->data) == COGL_FILTER_REMOVE) return COGL_FILTER_REMOVE; } /* If the backend for the renderer also wants to see the events, it should just register its own filter */ return COGL_FILTER_CONTINUE; } void _cogl_renderer_add_native_filter (CoglRenderer *renderer, CoglNativeFilterFunc func, void *data) { CoglNativeFilterClosure *closure; closure = g_slice_new (CoglNativeFilterClosure); closure->func = func; closure->data = data; renderer->event_filters = g_slist_prepend (renderer->event_filters, closure); } void _cogl_renderer_remove_native_filter (CoglRenderer *renderer, CoglNativeFilterFunc func, void *data) { GSList *l, *prev = NULL; for (l = renderer->event_filters; l; prev = l, l = l->next) { CoglNativeFilterClosure *closure = l->data; if (closure->func == func && closure->data == data) { native_filter_closure_free (closure); if (prev) prev->next = g_slist_delete_link (prev->next, l); else renderer->event_filters = g_slist_delete_link (renderer->event_filters, l); break; } } } void cogl_renderer_set_winsys_id (CoglRenderer *renderer, CoglWinsysID winsys_id) { g_return_if_fail (!renderer->connected); renderer->winsys_id_override = winsys_id; } CoglWinsysID cogl_renderer_get_winsys_id (CoglRenderer *renderer) { g_return_val_if_fail (renderer->connected, 0); return renderer->winsys_vtable->id; } void * _cogl_renderer_get_proc_address (CoglRenderer *renderer, const char *name, gboolean in_core) { const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); return winsys->renderer_get_proc_address (renderer, name, in_core); } void cogl_renderer_add_constraint (CoglRenderer *renderer, CoglRendererConstraint constraint) { g_return_if_fail (!renderer->connected); renderer->constraints = g_list_prepend (renderer->constraints, GUINT_TO_POINTER (constraint)); } void cogl_renderer_remove_constraint (CoglRenderer *renderer, CoglRendererConstraint constraint) { g_return_if_fail (!renderer->connected); renderer->constraints = g_list_remove (renderer->constraints, GUINT_TO_POINTER (constraint)); } void cogl_renderer_set_driver (CoglRenderer *renderer, CoglDriver driver) { g_return_if_fail (!renderer->connected); renderer->driver_override = driver; } CoglDriver cogl_renderer_get_driver (CoglRenderer *renderer) { g_return_val_if_fail (renderer->connected, 0); return renderer->driver; } void cogl_renderer_foreach_output (CoglRenderer *renderer, CoglOutputCallback callback, void *user_data) { GList *l; g_return_if_fail (renderer->connected); g_return_if_fail (callback != NULL); for (l = renderer->outputs; l; l = l->next) callback (l->data, user_data); } CoglDmaBufHandle * cogl_renderer_create_dma_buf (CoglRenderer *renderer, int width, int height, GError **error) { const CoglWinsysVtable *winsys = _cogl_renderer_get_winsys (renderer); if (winsys->renderer_create_dma_buf) return winsys->renderer_create_dma_buf (renderer, width, height, error); return NULL; } muffin-6.4.1/cogl/cogl/cogl-framebuffer.c0000664000175000017500000022572014723361714017174 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2012 Intel Corporation. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #include "cogl-config.h" #include #include "cogl-debug.h" #include "cogl-context-private.h" #include "cogl-display-private.h" #include "cogl-renderer-private.h" #include "cogl-object-private.h" #include "cogl-util.h" #include "cogl-texture-private.h" #include "cogl-framebuffer-private.h" #include "cogl-onscreen-template-private.h" #include "cogl-clip-stack.h" #include "cogl-journal-private.h" #include "cogl-pipeline-state-private.h" #include "cogl-matrix-private.h" #include "cogl-primitive-private.h" #include "cogl-offscreen.h" #include "cogl1-context.h" #include "cogl-private.h" #include "cogl-primitives-private.h" #include "cogl-gtype-private.h" #include "winsys/cogl-winsys-private.h" extern CoglObjectClass _cogl_onscreen_class; #ifdef COGL_ENABLE_DEBUG static CoglUserDataKey wire_pipeline_key; #endif static void _cogl_offscreen_free (CoglOffscreen *offscreen); COGL_OBJECT_DEFINE_WITH_CODE_GTYPE (Offscreen, offscreen, _cogl_offscreen_class.virt_unref = _cogl_framebuffer_unref); COGL_GTYPE_DEFINE_CLASS (Offscreen, offscreen, COGL_GTYPE_IMPLEMENT_INTERFACE (framebuffer)); COGL_GTYPE_DEFINE_INTERFACE (Framebuffer, framebuffer); /* XXX: * The CoglObject macros don't support any form of inheritance, so for * now we implement the CoglObject support for the CoglFramebuffer * abstract class manually. */ uint32_t cogl_framebuffer_error_quark (void) { return g_quark_from_static_string ("cogl-framebuffer-error-quark"); } gboolean cogl_is_framebuffer (void *object) { CoglObject *obj = object; if (obj == NULL) return FALSE; return (obj->klass == &_cogl_onscreen_class || obj->klass == &_cogl_offscreen_class); } void _cogl_framebuffer_init (CoglFramebuffer *framebuffer, CoglContext *ctx, CoglFramebufferType type, int width, int height) { framebuffer->context = ctx; framebuffer->type = type; framebuffer->width = width; framebuffer->height = height; framebuffer->internal_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; framebuffer->viewport_x = 0; framebuffer->viewport_y = 0; framebuffer->viewport_width = width; framebuffer->viewport_height = height; framebuffer->viewport_age = 0; framebuffer->viewport_age_for_scissor_workaround = -1; framebuffer->dither_enabled = TRUE; framebuffer->depth_writing_enabled = TRUE; framebuffer->depth_buffer_clear_needed = TRUE; framebuffer->modelview_stack = cogl_matrix_stack_new (ctx); framebuffer->projection_stack = cogl_matrix_stack_new (ctx); framebuffer->dirty_bitmasks = TRUE; framebuffer->samples_per_pixel = 0; framebuffer->clip_stack = NULL; framebuffer->journal = _cogl_journal_new (framebuffer); /* Ensure we know the framebuffer->clear_color* members can't be * referenced for our fast-path read-pixel optimization (see * _cogl_journal_try_read_pixel()) until some region of the * framebuffer is initialized. */ framebuffer->clear_clip_dirty = TRUE; /* XXX: We have to maintain a central list of all framebuffers * because at times we need to be able to flush all known journals. * * Examples where we need to flush all journals are: * - because journal entries can reference OpenGL texture * coordinates that may not survive texture-atlas reorganization * so we need the ability to flush those entries. * - because although we generally advise against modifying * pipelines after construction we have to handle that possibility * and since pipelines may be referenced in journal entries we * need to be able to flush them before allowing the pipelines to * be changed. * * Note we don't maintain a list of journals and associate * framebuffers with journals by e.g. having a journal->framebuffer * reference since that would introduce a circular reference. * * Note: As a future change to try and remove the need to index all * journals it might be possible to defer resolving of OpenGL * texture coordinates for rectangle primitives until we come to * flush a journal. This would mean for instance that a single * rectangle entry in a journal could later be expanded into * multiple quad primitives to handle sliced textures but would mean * we don't have to worry about retaining references to OpenGL * texture coordinates that may later become invalid. */ ctx->framebuffers = g_list_prepend (ctx->framebuffers, framebuffer); } void _cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer, CoglPixelFormat internal_format) { framebuffer->internal_format = internal_format; } void _cogl_framebuffer_free (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; _cogl_fence_cancel_fences_for_framebuffer (framebuffer); _cogl_clip_stack_unref (framebuffer->clip_stack); cogl_object_unref (framebuffer->modelview_stack); framebuffer->modelview_stack = NULL; cogl_object_unref (framebuffer->projection_stack); framebuffer->projection_stack = NULL; cogl_object_unref (framebuffer->journal); ctx->framebuffers = g_list_remove (ctx->framebuffers, framebuffer); if (ctx->current_draw_buffer == framebuffer) ctx->current_draw_buffer = NULL; if (ctx->current_read_buffer == framebuffer) ctx->current_read_buffer = NULL; } const CoglWinsysVtable * _cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer) { return framebuffer->context->display->renderer->winsys_vtable; } /* This version of cogl_clear can be used internally as an alternative * to avoid flushing the journal or the framebuffer state. This is * needed when doing operations that may be called whiling flushing * the journal */ void _cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha) { CoglContext *ctx = framebuffer->context; if (!buffers) { static gboolean shown = FALSE; if (!shown) { g_warning ("You should specify at least one auxiliary buffer " "when calling cogl_framebuffer_clear"); } return; } ctx->driver_vtable->framebuffer_clear (framebuffer, buffers, red, green, blue, alpha); } void _cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer) { framebuffer->clear_clip_dirty = TRUE; } void cogl_framebuffer_clear4f (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha) { CoglClipStack *clip_stack = _cogl_framebuffer_get_clip_stack (framebuffer); gboolean had_depth_and_color_buffer_bits; int scissor_x0; int scissor_y0; int scissor_x1; int scissor_y1; had_depth_and_color_buffer_bits = (buffers & COGL_BUFFER_BIT_DEPTH) && (buffers & COGL_BUFFER_BIT_COLOR); if (!framebuffer->depth_buffer_clear_needed && (buffers & COGL_BUFFER_BIT_DEPTH)) buffers &= ~(COGL_BUFFER_BIT_DEPTH); if (buffers == 0) return; _cogl_clip_stack_get_bounds (clip_stack, &scissor_x0, &scissor_y0, &scissor_x1, &scissor_y1); /* NB: the previous clear could have had an arbitrary clip. * NB: everything for the last frame might still be in the journal * but we can't assume anything about how each entry was * clipped. * NB: Clutter will scissor its pick renders which would mean all * journal entries have a common ClipStack entry, but without * a layering violation Cogl has to explicitly walk the journal * entries to determine if this is the case. * NB: We have a software only read-pixel optimization in the * journal that determines the color at a given framebuffer * coordinate for simple scenes without rendering with the GPU. * When Clutter is hitting this fast-path we can expect to * receive calls to clear the framebuffer with an un-flushed * journal. * NB: To fully support software based picking for Clutter we * need to be able to reliably detect when the contents of a * journal can be discarded and when we can skip the call to * glClear because it matches the previous clear request. */ /* Note: we don't check for the stencil buffer being cleared here * since there isn't any public cogl api to manipulate the stencil * buffer. * * Note: we check for an exact clip match here because * 1) a smaller clip could mean existing journal entries may * need to contribute to regions outside the new clear-clip * 2) a larger clip would mean we need to issue a real * glClear and we only care about cases avoiding a * glClear. * * Note: Comparing without an epsilon is considered * appropriate here. */ if (had_depth_and_color_buffer_bits && !framebuffer->clear_clip_dirty && framebuffer->clear_color_red == red && framebuffer->clear_color_green == green && framebuffer->clear_color_blue == blue && framebuffer->clear_color_alpha == alpha && scissor_x0 == framebuffer->clear_clip_x0 && scissor_y0 == framebuffer->clear_clip_y0 && scissor_x1 == framebuffer->clear_clip_x1 && scissor_y1 == framebuffer->clear_clip_y1) { /* NB: We only have to consider the clip state of journal * entries if the current clear is clipped since otherwise we * know every pixel of the framebuffer is affected by the clear * and so all journal entries become redundant and can simply be * discarded. */ if (clip_stack) { /* * Note: the function for checking the journal entries is * quite strict. It avoids detailed checking of all entry * clip_stacks by only checking the details of the first * entry and then it only verifies that the remaining * entries share the same clip_stack ancestry. This means * it's possible for some false negatives here but that will * just result in us falling back to a real clear. */ if (_cogl_journal_all_entries_within_bounds (framebuffer->journal, scissor_x0, scissor_y0, scissor_x1, scissor_y1)) { _cogl_journal_discard (framebuffer->journal); goto cleared; } } else { _cogl_journal_discard (framebuffer->journal); goto cleared; } } COGL_NOTE (DRAW, "Clear begin"); _cogl_framebuffer_flush_journal (framebuffer); /* NB: _cogl_framebuffer_flush_state may disrupt various state (such * as the pipeline state) when flushing the clip stack, so should * always be done first when preparing to draw. */ _cogl_framebuffer_flush_state (framebuffer, framebuffer, COGL_FRAMEBUFFER_STATE_ALL); _cogl_framebuffer_clear_without_flush4f (framebuffer, buffers, red, green, blue, alpha); /* This is a debugging variable used to visually display the quad * batches from the journal. It is reset here to increase the * chances of getting the same colours for each frame during an * animation */ if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_RECTANGLES)) && buffers & COGL_BUFFER_BIT_COLOR) { framebuffer->context->journal_rectangles_color = 1; } COGL_NOTE (DRAW, "Clear end"); cleared: _cogl_framebuffer_mark_clear_clip_dirty (framebuffer); if (buffers & COGL_BUFFER_BIT_DEPTH) framebuffer->depth_buffer_clear_needed = FALSE; if (had_depth_and_color_buffer_bits) { /* For our fast-path for reading back a single pixel of simple * scenes where the whole frame is in the journal we need to * track the cleared color of the framebuffer in case the point * read doesn't intersect any of the journal rectangles. */ framebuffer->clear_clip_dirty = FALSE; framebuffer->clear_color_red = red; framebuffer->clear_color_green = green; framebuffer->clear_color_blue = blue; framebuffer->clear_color_alpha = alpha; /* NB: A clear may be scissored so we need to track the extents * that the clear is applicable too... */ _cogl_clip_stack_get_bounds (clip_stack, &framebuffer->clear_clip_x0, &framebuffer->clear_clip_y0, &framebuffer->clear_clip_x1, &framebuffer->clear_clip_y1); } } /* Note: the 'buffers' and 'color' arguments were switched around on * purpose compared to the original cogl_clear API since it was odd * that you would be expected to specify a color before even * necessarily choosing to clear the color buffer. */ void cogl_framebuffer_clear (CoglFramebuffer *framebuffer, unsigned long buffers, const CoglColor *color) { cogl_framebuffer_clear4f (framebuffer, buffers, cogl_color_get_red_float (color), cogl_color_get_green_float (color), cogl_color_get_blue_float (color), cogl_color_get_alpha_float (color)); } /* We will lazily allocate framebuffers if necessary when querying * their size/viewport but note we need to be careful in the case of * onscreen framebuffers that are instantiated with an initial request * size that we don't trigger an allocation when this is queried since * that would lead to a recursion when the winsys backend queries this * requested size during allocation. */ static void ensure_size_initialized (CoglFramebuffer *framebuffer) { /* In the case of offscreen framebuffers backed by a texture then * until that texture has been allocated we might not know the size * of the framebuffer */ if (framebuffer->width < 0) { /* Currently we assume the size is always initialized for * onscreen framebuffers. */ g_return_if_fail (cogl_is_offscreen (framebuffer)); /* We also assume the size would have been initialized if the * framebuffer were allocated. */ g_return_if_fail (!framebuffer->allocated); cogl_framebuffer_allocate (framebuffer, NULL); } } int cogl_framebuffer_get_width (CoglFramebuffer *framebuffer) { ensure_size_initialized (framebuffer); return framebuffer->width; } int cogl_framebuffer_get_height (CoglFramebuffer *framebuffer) { ensure_size_initialized (framebuffer); return framebuffer->height; } CoglClipStack * _cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer) { return framebuffer->clip_stack; } void cogl_framebuffer_set_viewport4fv (CoglFramebuffer *framebuffer, float *viewport) { if (framebuffer->viewport_x == viewport[0] && framebuffer->viewport_y == viewport[1] && framebuffer->viewport_width == viewport[2] && framebuffer->viewport_height == viewport[3]) return; framebuffer->viewport_x = viewport[0]; framebuffer->viewport_y = viewport[1]; framebuffer->viewport_width = viewport[2]; framebuffer->viewport_height = viewport[3]; framebuffer->viewport_age++; } void cogl_framebuffer_set_viewport (CoglFramebuffer *framebuffer, float x, float y, float width, float height) { g_return_if_fail (width > 0 && height > 0); if (framebuffer->viewport_x == x && framebuffer->viewport_y == y && framebuffer->viewport_width == width && framebuffer->viewport_height == height) return; framebuffer->viewport_x = x; framebuffer->viewport_y = y; framebuffer->viewport_width = width; framebuffer->viewport_height = height; } float cogl_framebuffer_get_viewport_x (CoglFramebuffer *framebuffer) { return framebuffer->viewport_x; } float cogl_framebuffer_get_viewport_y (CoglFramebuffer *framebuffer) { return framebuffer->viewport_y; } float cogl_framebuffer_get_viewport_width (CoglFramebuffer *framebuffer) { ensure_size_initialized (framebuffer); return framebuffer->viewport_width; } float cogl_framebuffer_get_viewport_height (CoglFramebuffer *framebuffer) { ensure_size_initialized (framebuffer); return framebuffer->viewport_height; } void cogl_framebuffer_get_viewport4fv (CoglFramebuffer *framebuffer, float *viewport) { ensure_size_initialized (framebuffer); viewport[0] = framebuffer->viewport_x; viewport[1] = framebuffer->viewport_y; viewport[2] = framebuffer->viewport_width; viewport[3] = framebuffer->viewport_height; } CoglMatrixStack * _cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer) { return framebuffer->modelview_stack; } CoglMatrixStack * _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer) { return framebuffer->projection_stack; } void _cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer, CoglFramebuffer *dependency) { GList *l; for (l = framebuffer->deps; l; l = l->next) { CoglFramebuffer *existing_dep = l->data; if (existing_dep == dependency) return; } /* TODO: generalize the primed-array type structure we e.g. use for * cogl_object_set_user_data or for pipeline children as a way to * avoid quite a lot of mid-scene micro allocations here... */ framebuffer->deps = g_list_prepend (framebuffer->deps, cogl_object_ref (dependency)); } void _cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer) { _cogl_journal_flush (framebuffer->journal); } void _cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer) { GList *l; for (l = framebuffer->deps; l; l = l->next) _cogl_framebuffer_flush_journal (l->data); for (l = framebuffer->deps; l; l = l->next) cogl_object_unref (l->data); g_list_free (framebuffer->deps); framebuffer->deps = NULL; } CoglOffscreen * _cogl_offscreen_new_with_texture_full (CoglTexture *texture, CoglOffscreenFlags create_flags, int level) { CoglContext *ctx = texture->context; CoglOffscreen *offscreen; CoglFramebuffer *fb; CoglOffscreen *ret; g_return_val_if_fail (cogl_is_texture (texture), NULL); offscreen = g_new0 (CoglOffscreen, 1); offscreen->texture = cogl_object_ref (texture); offscreen->texture_level = level; offscreen->create_flags = create_flags; fb = COGL_FRAMEBUFFER (offscreen); /* NB: we can't assume we can query the texture's width yet, since * it might not have been allocated yet and for example if the * texture is being loaded from a file then the file might not * have been read yet. */ _cogl_framebuffer_init (fb, ctx, COGL_FRAMEBUFFER_TYPE_OFFSCREEN, -1, /* unknown width, until allocation */ -1); /* unknown height until allocation */ ret = _cogl_offscreen_object_new (offscreen); _cogl_texture_associate_framebuffer (texture, fb); return ret; } /* XXX: deprecated api */ CoglOffscreen * cogl_offscreen_new_to_texture (CoglTexture *texture) { CoglOffscreen *ret = _cogl_offscreen_new_with_texture_full (texture, 0, 0); GError *error = NULL; if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (ret), &error)) { cogl_object_unref (ret); g_error_free (error); ret = NULL; } return ret; } CoglOffscreen * cogl_offscreen_new_with_texture (CoglTexture *texture) { return _cogl_offscreen_new_with_texture_full (texture, 0, 0); } CoglTexture * cogl_offscreen_get_texture (CoglOffscreen *offscreen) { return offscreen->texture; } static void _cogl_offscreen_free (CoglOffscreen *offscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (offscreen); CoglContext *ctx = framebuffer->context; ctx->driver_vtable->offscreen_free (offscreen); /* Chain up to parent */ _cogl_framebuffer_free (framebuffer); if (offscreen->texture != NULL) cogl_object_unref (offscreen->texture); if (offscreen->depth_texture != NULL) cogl_object_unref (offscreen->depth_texture); g_free (offscreen); } gboolean cogl_framebuffer_allocate (CoglFramebuffer *framebuffer, GError **error) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); const CoglWinsysVtable *winsys = _cogl_framebuffer_get_winsys (framebuffer); CoglContext *ctx = framebuffer->context; if (framebuffer->allocated) return TRUE; if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { if (!winsys->onscreen_init (onscreen, error)) return FALSE; /* If the winsys doesn't support dirty events then we'll report * one on allocation so that if the application only paints in * response to dirty events then it will at least paint once to * start */ if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_DIRTY_EVENTS)) _cogl_onscreen_queue_full_dirty (onscreen); } else { CoglOffscreen *offscreen = COGL_OFFSCREEN (framebuffer); if (!cogl_texture_allocate (offscreen->texture, error)) return FALSE; /* NB: it's only after allocating the texture that we will * determine whether a texture needs slicing... */ if (cogl_texture_is_sliced (offscreen->texture)) { g_set_error (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Can't create offscreen framebuffer from " "sliced texture"); return FALSE; } /* Now that the texture has been allocated we can determine a * size for the framebuffer... */ framebuffer->width = cogl_texture_get_width (offscreen->texture); framebuffer->height = cogl_texture_get_height (offscreen->texture); framebuffer->viewport_width = framebuffer->width; framebuffer->viewport_height = framebuffer->height; /* Forward the texture format as the internal format of the * framebuffer */ framebuffer->internal_format = _cogl_texture_get_format (offscreen->texture); if (!ctx->driver_vtable->offscreen_allocate (offscreen, error)) return FALSE; } framebuffer->allocated = TRUE; return TRUE; } static unsigned long _cogl_framebuffer_compare_viewport_state (CoglFramebuffer *a, CoglFramebuffer *b) { if (a->viewport_x != b->viewport_x || a->viewport_y != b->viewport_y || a->viewport_width != b->viewport_width || a->viewport_height != b->viewport_height || /* NB: we render upside down to offscreen framebuffers and that * can affect how we setup the GL viewport... */ a->type != b->type) return COGL_FRAMEBUFFER_STATE_VIEWPORT; else return 0; } static unsigned long _cogl_framebuffer_compare_clip_state (CoglFramebuffer *a, CoglFramebuffer *b) { if (a->clip_stack != b->clip_stack) return COGL_FRAMEBUFFER_STATE_CLIP; else return 0; } static unsigned long _cogl_framebuffer_compare_dither_state (CoglFramebuffer *a, CoglFramebuffer *b) { return a->dither_enabled != b->dither_enabled ? COGL_FRAMEBUFFER_STATE_DITHER : 0; } static unsigned long _cogl_framebuffer_compare_modelview_state (CoglFramebuffer *a, CoglFramebuffer *b) { /* We always want to flush the modelview state. All this does is set the current modelview stack on the context to the framebuffer's stack. */ return COGL_FRAMEBUFFER_STATE_MODELVIEW; } static unsigned long _cogl_framebuffer_compare_projection_state (CoglFramebuffer *a, CoglFramebuffer *b) { /* We always want to flush the projection state. All this does is set the current projection stack on the context to the framebuffer's stack. */ return COGL_FRAMEBUFFER_STATE_PROJECTION; } static unsigned long _cogl_framebuffer_compare_front_face_winding_state (CoglFramebuffer *a, CoglFramebuffer *b) { if (a->type != b->type) return COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING; else return 0; } static unsigned long _cogl_framebuffer_compare_depth_write_state (CoglFramebuffer *a, CoglFramebuffer *b) { return a->depth_writing_enabled != b->depth_writing_enabled ? COGL_FRAMEBUFFER_STATE_DEPTH_WRITE : 0; } static unsigned long _cogl_framebuffer_compare_stereo_mode (CoglFramebuffer *a, CoglFramebuffer *b) { return a->stereo_mode != b->stereo_mode ? COGL_FRAMEBUFFER_STATE_STEREO_MODE : 0; } unsigned long _cogl_framebuffer_compare (CoglFramebuffer *a, CoglFramebuffer *b, unsigned long state) { unsigned long differences = 0; int bit; if (state & COGL_FRAMEBUFFER_STATE_BIND) { differences |= COGL_FRAMEBUFFER_STATE_BIND; state &= ~COGL_FRAMEBUFFER_STATE_BIND; } COGL_FLAGS_FOREACH_START (&state, 1, bit) { /* XXX: We considered having an array of callbacks for each state index * that we'd call here but decided that this way the compiler is more * likely going to be able to in-line the comparison functions and use * the index to jump straight to the required code. */ switch (bit) { case COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT: differences |= _cogl_framebuffer_compare_viewport_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_CLIP: differences |= _cogl_framebuffer_compare_clip_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_DITHER: differences |= _cogl_framebuffer_compare_dither_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW: differences |= _cogl_framebuffer_compare_modelview_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION: differences |= _cogl_framebuffer_compare_projection_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING: differences |= _cogl_framebuffer_compare_front_face_winding_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE: differences |= _cogl_framebuffer_compare_depth_write_state (a, b); break; case COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE: differences |= _cogl_framebuffer_compare_stereo_mode (a, b); break; default: g_warn_if_reached (); } } COGL_FLAGS_FOREACH_END; return differences; } void _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state) { CoglContext *ctx = draw_buffer->context; ctx->driver_vtable->framebuffer_flush_state (draw_buffer, read_buffer, state); } int cogl_framebuffer_get_red_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.red; } int cogl_framebuffer_get_green_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.green; } int cogl_framebuffer_get_blue_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.blue; } int cogl_framebuffer_get_alpha_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.alpha; } int cogl_framebuffer_get_depth_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.depth; } int _cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; CoglFramebufferBits bits; ctx->driver_vtable->framebuffer_query_bits (framebuffer, &bits); return bits.stencil; } gboolean cogl_framebuffer_get_is_stereo (CoglFramebuffer *framebuffer) { return framebuffer->config.stereo_enabled; } CoglStereoMode cogl_framebuffer_get_stereo_mode (CoglFramebuffer *framebuffer) { return framebuffer->stereo_mode; } void cogl_framebuffer_set_stereo_mode (CoglFramebuffer *framebuffer, CoglStereoMode stereo_mode) { if (framebuffer->stereo_mode == stereo_mode) return; /* Stereo mode changes don't go through the journal */ _cogl_framebuffer_flush_journal (framebuffer); framebuffer->stereo_mode = stereo_mode; if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_STEREO_MODE; } gboolean cogl_framebuffer_get_depth_write_enabled (CoglFramebuffer *framebuffer) { return framebuffer->depth_writing_enabled; } void cogl_framebuffer_set_depth_write_enabled (CoglFramebuffer *framebuffer, gboolean depth_write_enabled) { if (framebuffer->depth_writing_enabled == depth_write_enabled) return; /* XXX: Currently depth write changes don't go through the journal */ _cogl_framebuffer_flush_journal (framebuffer); framebuffer->depth_writing_enabled = depth_write_enabled; if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_DEPTH_WRITE; } gboolean cogl_framebuffer_get_dither_enabled (CoglFramebuffer *framebuffer) { return framebuffer->dither_enabled; } void cogl_framebuffer_set_dither_enabled (CoglFramebuffer *framebuffer, gboolean dither_enabled) { if (framebuffer->dither_enabled == dither_enabled) return; framebuffer->dither_enabled = dither_enabled; } int cogl_framebuffer_get_samples_per_pixel (CoglFramebuffer *framebuffer) { if (framebuffer->allocated) return framebuffer->samples_per_pixel; else return framebuffer->config.samples_per_pixel; } void cogl_framebuffer_set_samples_per_pixel (CoglFramebuffer *framebuffer, int samples_per_pixel) { g_return_if_fail (!framebuffer->allocated); framebuffer->config.samples_per_pixel = samples_per_pixel; } void cogl_framebuffer_resolve_samples (CoglFramebuffer *framebuffer) { cogl_framebuffer_resolve_samples_region (framebuffer, 0, 0, framebuffer->width, framebuffer->height); /* TODO: Make this happen implicitly when the resolve texture next gets used * as a source, either via cogl_texture_get_data(), via cogl_read_pixels() or * if used as a source for rendering. We would also implicitly resolve if * necessary before freeing a CoglFramebuffer. * * This API should still be kept but it is optional, only necessary * if the user wants to explicitly control when the resolve happens e.g. * to ensure it's done in advance of it being used as a source. * * Every texture should have a CoglFramebuffer *needs_resolve member * internally. When the texture gets validated before being used as a source * we should first check the needs_resolve pointer and if set we'll * automatically call cogl_framebuffer_resolve_samples (). * * Calling cogl_framebuffer_resolve_samples() or * cogl_framebuffer_resolve_samples_region() should reset the textures * needs_resolve pointer to NULL. * * Rendering anything to a framebuffer will cause the corresponding * texture's ->needs_resolve pointer to be set. * * XXX: Note: we only need to address this TODO item when adding support for * EXT_framebuffer_multisample because currently we only support hardware * that resolves implicitly anyway. */ } void cogl_framebuffer_resolve_samples_region (CoglFramebuffer *framebuffer, int x, int y, int width, int height) { /* NOP for now since we don't support EXT_framebuffer_multisample yet which * requires an explicit resolve. */ } CoglContext * cogl_framebuffer_get_context (CoglFramebuffer *framebuffer) { g_return_val_if_fail (framebuffer != NULL, NULL); return framebuffer->context; } static gboolean _cogl_framebuffer_try_fast_read_pixel (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap) { gboolean found_intersection; CoglPixelFormat format; if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_FAST_READ_PIXEL))) return FALSE; if (source != COGL_READ_PIXELS_COLOR_BUFFER) return FALSE; format = cogl_bitmap_get_format (bitmap); if (format != COGL_PIXEL_FORMAT_RGBA_8888_PRE && format != COGL_PIXEL_FORMAT_RGBA_8888) return FALSE; if (!_cogl_journal_try_read_pixel (framebuffer->journal, x, y, bitmap, &found_intersection)) return FALSE; /* If we can't determine the color from the primitives in the * journal then see if we can use the last recorded clear color */ /* If _cogl_journal_try_read_pixel() failed even though there was an * intersection of the given point with a primitive in the journal * then we can't fallback to the framebuffer's last clear color... * */ if (found_intersection) return TRUE; /* If the framebuffer has been rendered too since it was last * cleared then we can't return the last known clear color. */ if (framebuffer->clear_clip_dirty) return FALSE; if (x >= framebuffer->clear_clip_x0 && x < framebuffer->clear_clip_x1 && y >= framebuffer->clear_clip_y0 && y < framebuffer->clear_clip_y1) { uint8_t *pixel; GError *ignore_error = NULL; /* we currently only care about cases where the premultiplied or * unpremultipled colors are equivalent... */ if (framebuffer->clear_color_alpha != 1.0) return FALSE; pixel = _cogl_bitmap_map (bitmap, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD, &ignore_error); if (pixel == NULL) { g_error_free (ignore_error); return FALSE; } pixel[0] = framebuffer->clear_color_red * 255.0; pixel[1] = framebuffer->clear_color_green * 255.0; pixel[2] = framebuffer->clear_color_blue * 255.0; pixel[3] = framebuffer->clear_color_alpha * 255.0; _cogl_bitmap_unmap (bitmap); return TRUE; } return FALSE; } gboolean _cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error) { CoglContext *ctx; int width; int height; g_return_val_if_fail (source & COGL_READ_PIXELS_COLOR_BUFFER, FALSE); g_return_val_if_fail (cogl_is_framebuffer (framebuffer), FALSE); if (!cogl_framebuffer_allocate (framebuffer, error)) return FALSE; width = cogl_bitmap_get_width (bitmap); height = cogl_bitmap_get_height (bitmap); if (width == 1 && height == 1 && !framebuffer->clear_clip_dirty) { /* If everything drawn so far for this frame is still in the * Journal then if all of the rectangles only have a flat * opaque color we have a fast-path for reading a single pixel * that avoids the relatively high cost of flushing primitives * to be drawn on the GPU (considering how simple the geometry * is in this case) and then blocking on the long GPU pipelines * for the result. */ if (_cogl_framebuffer_try_fast_read_pixel (framebuffer, x, y, source, bitmap)) return TRUE; } ctx = cogl_framebuffer_get_context (framebuffer); /* make sure any batched primitives get emitted to the driver * before issuing our read pixels... */ _cogl_framebuffer_flush_journal (framebuffer); return ctx->driver_vtable->framebuffer_read_pixels_into_bitmap (framebuffer, x, y, source, bitmap, error); } gboolean cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap) { GError *ignore_error = NULL; gboolean status = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, x, y, source, bitmap, &ignore_error); g_clear_error (&ignore_error); return status; } gboolean cogl_framebuffer_read_pixels (CoglFramebuffer *framebuffer, int x, int y, int width, int height, CoglPixelFormat format, uint8_t *pixels) { int bpp; CoglBitmap *bitmap; gboolean ret; g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); bitmap = cogl_bitmap_new_for_data (framebuffer->context, width, height, format, bpp * width, /* rowstride */ pixels); /* Note: we don't try and catch errors here since we created the * bitmap storage up-front and can assume we wont hit an * out-of-memory error which should be the only exception * this api throws. */ ret = _cogl_framebuffer_read_pixels_into_bitmap (framebuffer, x, y, COGL_READ_PIXELS_COLOR_BUFFER, bitmap, NULL); cogl_object_unref (bitmap); return ret; } gboolean cogl_blit_framebuffer (CoglFramebuffer *src, CoglFramebuffer *dest, int src_x, int src_y, int dst_x, int dst_y, int width, int height, GError **error) { CoglContext *ctx = src->context; int src_x1, src_y1, src_x2, src_y2; int dst_x1, dst_y1, dst_x2, dst_y2; if (!_cogl_has_private_feature (ctx, COGL_PRIVATE_FEATURE_BLIT_FRAMEBUFFER)) { g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "Cogl BLIT_FRAMEBUFFER is not supported by the system."); return FALSE; } /* The buffers must use the same premult convention */ if ((src->internal_format & COGL_PREMULT_BIT) != (dest->internal_format & COGL_PREMULT_BIT)) { g_set_error_literal (error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_UNSUPPORTED, "cogl_blit_framebuffer premult mismatch."); return FALSE; } /* Make sure any batched primitives get submitted to the driver * before blitting */ _cogl_framebuffer_flush_journal (src); /* Make sure the current framebuffers are bound. We explicitly avoid flushing the clip state so we can bind our own empty state */ _cogl_framebuffer_flush_state (dest, src, COGL_FRAMEBUFFER_STATE_ALL & ~COGL_FRAMEBUFFER_STATE_CLIP); /* Flush any empty clip stack because glBlitFramebuffer is affected by the scissor and we want to hide this feature for the Cogl API because it's not obvious to an app how the clip state will affect the scissor */ _cogl_clip_stack_flush (NULL, dest); /* XXX: Because we are manually flushing clip state here we need to * make sure that the clip state gets updated the next time we flush * framebuffer state by marking the current framebuffer's clip state * as changed */ ctx->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; /* Offscreens we do the normal way, onscreens need an y-flip. Even if * we consider offscreens to be rendered upside-down, the offscreen * orientation is in this function's API. */ if (cogl_is_offscreen (src)) { src_x1 = src_x; src_y1 = src_y; src_x2 = src_x + width; src_y2 = src_y + height; } else { src_x1 = src_x; src_y1 = cogl_framebuffer_get_height (src) - src_y; src_x2 = src_x + width; src_y2 = src_y1 - height; } if (cogl_is_offscreen (dest)) { dst_x1 = dst_x; dst_y1 = dst_y; dst_x2 = dst_x + width; dst_y2 = dst_y + height; } else { dst_x1 = dst_x; dst_y1 = cogl_framebuffer_get_height (dest) - dst_y; dst_x2 = dst_x + width; dst_y2 = dst_y1 - height; } ctx->glBlitFramebuffer (src_x1, src_y1, src_x2, src_y2, dst_x1, dst_y1, dst_x2, dst_y2, GL_COLOR_BUFFER_BIT, GL_NEAREST); return TRUE; } void cogl_framebuffer_discard_buffers (CoglFramebuffer *framebuffer, unsigned long buffers) { CoglContext *ctx = framebuffer->context; g_return_if_fail (buffers & COGL_BUFFER_BIT_COLOR); ctx->driver_vtable->framebuffer_discard_buffers (framebuffer, buffers); } void cogl_framebuffer_finish (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; _cogl_framebuffer_flush_journal (framebuffer); ctx->driver_vtable->framebuffer_finish (framebuffer); } void cogl_framebuffer_flush (CoglFramebuffer *framebuffer) { CoglContext *ctx = framebuffer->context; _cogl_framebuffer_flush_journal (framebuffer); ctx->driver_vtable->framebuffer_flush (framebuffer); } void cogl_framebuffer_push_matrix (CoglFramebuffer *framebuffer) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_push (modelview_stack); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_pop_matrix (CoglFramebuffer *framebuffer) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_pop (modelview_stack); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_identity_matrix (CoglFramebuffer *framebuffer) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_load_identity (modelview_stack); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_scale (CoglFramebuffer *framebuffer, float x, float y, float z) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_scale (modelview_stack, x, y, z); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_translate (CoglFramebuffer *framebuffer, float x, float y, float z) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_translate (modelview_stack, x, y, z); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_rotate (CoglFramebuffer *framebuffer, float angle, float x, float y, float z) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_rotate (modelview_stack, angle, x, y, z); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_rotate_euler (CoglFramebuffer *framebuffer, const graphene_euler_t *euler) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_rotate_euler (modelview_stack, euler); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_transform (CoglFramebuffer *framebuffer, const CoglMatrix *matrix) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_multiply (modelview_stack, matrix); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; } void cogl_framebuffer_perspective (CoglFramebuffer *framebuffer, float fov_y, float aspect, float z_near, float z_far) { float ymax = z_near * tanf (fov_y * G_PI / 360.0); cogl_framebuffer_frustum (framebuffer, -ymax * aspect, /* left */ ymax * aspect, /* right */ -ymax, /* bottom */ ymax, /* top */ z_near, z_far); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_PROJECTION; } void cogl_framebuffer_frustum (CoglFramebuffer *framebuffer, float left, float right, float bottom, float top, float z_near, float z_far) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); /* XXX: The projection matrix isn't currently tracked in the journal * so we need to flush all journaled primitives first... */ _cogl_framebuffer_flush_journal (framebuffer); cogl_matrix_stack_load_identity (projection_stack); cogl_matrix_stack_frustum (projection_stack, left, right, bottom, top, z_near, z_far); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_PROJECTION; } void cogl_framebuffer_orthographic (CoglFramebuffer *framebuffer, float x_1, float y_1, float x_2, float y_2, float near, float far) { CoglMatrix ortho; CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); /* XXX: The projection matrix isn't currently tracked in the journal * so we need to flush all journaled primitives first... */ _cogl_framebuffer_flush_journal (framebuffer); cogl_matrix_init_identity (&ortho); cogl_matrix_orthographic (&ortho, x_1, y_1, x_2, y_2, near, far); cogl_matrix_stack_set (projection_stack, &ortho); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_PROJECTION; } void cogl_framebuffer_get_modelview_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix) { CoglMatrixEntry *modelview_entry = _cogl_framebuffer_get_modelview_entry (framebuffer); cogl_matrix_entry_get (modelview_entry, matrix); _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_framebuffer_set_modelview_matrix (CoglFramebuffer *framebuffer, const CoglMatrix *matrix) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); cogl_matrix_stack_set (modelview_stack, matrix); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_MODELVIEW; _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_framebuffer_get_projection_matrix (CoglFramebuffer *framebuffer, CoglMatrix *matrix) { CoglMatrixEntry *projection_entry = _cogl_framebuffer_get_projection_entry (framebuffer); cogl_matrix_entry_get (projection_entry, matrix); _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_framebuffer_set_projection_matrix (CoglFramebuffer *framebuffer, const CoglMatrix *matrix) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); /* XXX: The projection matrix isn't currently tracked in the journal * so we need to flush all journaled primitives first... */ _cogl_framebuffer_flush_journal (framebuffer); cogl_matrix_stack_set (projection_stack, matrix); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_PROJECTION; _COGL_MATRIX_DEBUG_PRINT (matrix); } void cogl_framebuffer_push_scissor_clip (CoglFramebuffer *framebuffer, int x, int y, int width, int height) { framebuffer->clip_stack = _cogl_clip_stack_push_window_rectangle (framebuffer->clip_stack, x, y, width, height); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } void cogl_framebuffer_push_rectangle_clip (CoglFramebuffer *framebuffer, float x_1, float y_1, float x_2, float y_2) { CoglMatrixEntry *modelview_entry = _cogl_framebuffer_get_modelview_entry (framebuffer); CoglMatrixEntry *projection_entry = _cogl_framebuffer_get_projection_entry (framebuffer); /* XXX: It would be nicer if we stored the private viewport as a * vec4 so we could avoid this redundant copy. */ float viewport[] = { framebuffer->viewport_x, framebuffer->viewport_y, framebuffer->viewport_width, framebuffer->viewport_height }; framebuffer->clip_stack = _cogl_clip_stack_push_rectangle (framebuffer->clip_stack, x_1, y_1, x_2, y_2, modelview_entry, projection_entry, viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } void cogl_framebuffer_push_primitive_clip (CoglFramebuffer *framebuffer, CoglPrimitive *primitive, float bounds_x1, float bounds_y1, float bounds_x2, float bounds_y2) { CoglMatrixEntry *modelview_entry = _cogl_framebuffer_get_modelview_entry (framebuffer); CoglMatrixEntry *projection_entry = _cogl_framebuffer_get_projection_entry (framebuffer); /* XXX: It would be nicer if we stored the private viewport as a * vec4 so we could avoid this redundant copy. */ float viewport[] = { framebuffer->viewport_x, framebuffer->viewport_y, framebuffer->viewport_width, framebuffer->viewport_height }; framebuffer->clip_stack = _cogl_clip_stack_push_primitive (framebuffer->clip_stack, primitive, bounds_x1, bounds_y1, bounds_x2, bounds_y2, modelview_entry, projection_entry, viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } void cogl_framebuffer_push_region_clip (CoglFramebuffer *framebuffer, cairo_region_t *region) { framebuffer->clip_stack = cogl_clip_stack_push_region (framebuffer->clip_stack, region); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } void cogl_framebuffer_pop_clip (CoglFramebuffer *framebuffer) { framebuffer->clip_stack = _cogl_clip_stack_pop (framebuffer->clip_stack); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } void _cogl_framebuffer_unref (CoglFramebuffer *framebuffer) { /* The journal holds a reference to the framebuffer whenever it is non-empty. Therefore if the journal is non-empty and we will have exactly one reference then we know the journal is the only thing keeping the framebuffer alive. In that case we want to flush the journal and let the framebuffer die. It is fine at this point if flushing the journal causes something else to take a reference to it and it comes back to life */ if (framebuffer->journal->entries->len > 0) { unsigned int ref_count = ((CoglObject *) framebuffer)->ref_count; /* There should be at least two references - the one we are about to drop and the one held by the journal */ if (ref_count < 2) g_warning ("Inconsistent ref count on a framebuffer with journal " "entries."); if (ref_count == 2) _cogl_framebuffer_flush_journal (framebuffer); } /* Chain-up */ _cogl_object_default_unref (framebuffer); } #ifdef COGL_ENABLE_DEBUG static int get_index (void *indices, CoglIndicesType type, int _index) { if (!indices) return _index; switch (type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: return ((uint8_t *)indices)[_index]; case COGL_INDICES_TYPE_UNSIGNED_SHORT: return ((uint16_t *)indices)[_index]; case COGL_INDICES_TYPE_UNSIGNED_INT: return ((uint32_t *)indices)[_index]; } g_return_val_if_reached (0); } static void add_line (uint32_t *line_indices, int base, void *user_indices, CoglIndicesType user_indices_type, int index0, int index1, int *pos) { index0 = get_index (user_indices, user_indices_type, index0); index1 = get_index (user_indices, user_indices_type, index1); line_indices[(*pos)++] = base + index0; line_indices[(*pos)++] = base + index1; } static int get_line_count (CoglVerticesMode mode, int n_vertices) { if (mode == COGL_VERTICES_MODE_TRIANGLES && (n_vertices % 3) == 0) { return n_vertices; } else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && n_vertices >= 3) { return 2 * n_vertices - 3; } else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && n_vertices >= 3) { return 2 * n_vertices - 3; } /* In the journal we are a bit sneaky and actually use GL_QUADS * which isn't actually a valid CoglVerticesMode! */ #ifdef HAVE_COGL_GL else if (mode == GL_QUADS && (n_vertices % 4) == 0) { return n_vertices; } #endif g_return_val_if_reached (0); } static CoglIndices * get_wire_line_indices (CoglContext *ctx, CoglVerticesMode mode, int first_vertex, int n_vertices_in, CoglIndices *user_indices, int *n_indices) { int n_lines; uint32_t *line_indices; CoglIndexBuffer *index_buffer; void *indices; CoglIndicesType indices_type; int base = first_vertex; int pos; int i; CoglIndices *ret; if (user_indices) { index_buffer = cogl_indices_get_buffer (user_indices); indices = _cogl_buffer_map (COGL_BUFFER (index_buffer), COGL_BUFFER_ACCESS_READ, 0, NULL); indices_type = cogl_indices_get_type (user_indices); } else { index_buffer = NULL; indices = NULL; indices_type = COGL_INDICES_TYPE_UNSIGNED_BYTE; } n_lines = get_line_count (mode, n_vertices_in); /* Note: we are using COGL_INDICES_TYPE_UNSIGNED_INT so 4 bytes per index. */ line_indices = g_malloc (4 * n_lines * 2); pos = 0; if (mode == COGL_VERTICES_MODE_TRIANGLES && (n_vertices_in % 3) == 0) { for (i = 0; i < n_vertices_in; i += 3) { add_line (line_indices, base, indices, indices_type, i, i+1, &pos); add_line (line_indices, base, indices, indices_type, i+1, i+2, &pos); add_line (line_indices, base, indices, indices_type, i+2, i, &pos); } } else if (mode == COGL_VERTICES_MODE_TRIANGLE_FAN && n_vertices_in >= 3) { add_line (line_indices, base, indices, indices_type, 0, 1, &pos); add_line (line_indices, base, indices, indices_type, 1, 2, &pos); add_line (line_indices, base, indices, indices_type, 0, 2, &pos); for (i = 3; i < n_vertices_in; i++) { add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); add_line (line_indices, base, indices, indices_type, 0, i, &pos); } } else if (mode == COGL_VERTICES_MODE_TRIANGLE_STRIP && n_vertices_in >= 3) { add_line (line_indices, base, indices, indices_type, 0, 1, &pos); add_line (line_indices, base, indices, indices_type, 1, 2, &pos); add_line (line_indices, base, indices, indices_type, 0, 2, &pos); for (i = 3; i < n_vertices_in; i++) { add_line (line_indices, base, indices, indices_type, i - 1, i, &pos); add_line (line_indices, base, indices, indices_type, i - 2, i, &pos); } } /* In the journal we are a bit sneaky and actually use GL_QUADS * which isn't actually a valid CoglVerticesMode! */ #ifdef HAVE_COGL_GL else if (mode == GL_QUADS && (n_vertices_in % 4) == 0) { for (i = 0; i < n_vertices_in; i += 4) { add_line (line_indices, base, indices, indices_type, i, i + 1, &pos); add_line (line_indices, base, indices, indices_type, i + 1, i + 2, &pos); add_line (line_indices, base, indices, indices_type, i + 2, i + 3, &pos); add_line (line_indices, base, indices, indices_type, i + 3, i, &pos); } } #endif if (user_indices) cogl_buffer_unmap (COGL_BUFFER (index_buffer)); *n_indices = n_lines * 2; ret = cogl_indices_new (ctx, COGL_INDICES_TYPE_UNSIGNED_INT, line_indices, *n_indices); g_free (line_indices); return ret; } static void pipeline_destroyed_cb (CoglPipeline *weak_pipeline, void *user_data) { CoglPipeline *original_pipeline = user_data; /* XXX: I think we probably need to provide a custom unref function for * CoglPipeline because it's possible that we will reach this callback * because original_pipeline is being freed which means cogl_object_unref * will have already freed any associated user data. * * Setting more user data here will *probably* succeed but that may allocate * a new user-data array which could be leaked. * * Potentially we could have a _cogl_object_free_user_data function so * that a custom unref function could be written that can destroy weak * pipeline children before removing user data. */ cogl_object_set_user_data (COGL_OBJECT (original_pipeline), &wire_pipeline_key, NULL, NULL); cogl_object_unref (weak_pipeline); } static void draw_wireframe (CoglContext *ctx, CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglIndices *indices, CoglDrawFlags flags) { CoglIndices *wire_indices; CoglPipeline *wire_pipeline; int n_indices; wire_indices = get_wire_line_indices (ctx, mode, first_vertex, n_vertices, indices, &n_indices); wire_pipeline = cogl_object_get_user_data (COGL_OBJECT (pipeline), &wire_pipeline_key); if (!wire_pipeline) { static CoglSnippet *snippet = NULL; wire_pipeline = _cogl_pipeline_weak_copy (pipeline, pipeline_destroyed_cb, NULL); cogl_object_set_user_data (COGL_OBJECT (pipeline), &wire_pipeline_key, wire_pipeline, NULL); /* If we have glsl then the pipeline may have an associated * vertex program and since we'd like to see the results of the * vertex program in the wireframe we just add a final clobber * of the wire color leaving the rest of the state untouched. */ /* The snippet is cached so that it will reuse the program * from the pipeline cache if possible */ if (snippet == NULL) { snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_color_out = " "vec4 (0.0, 1.0, 0.0, 1.0);\n"); } cogl_pipeline_add_snippet (wire_pipeline, snippet); } /* temporarily disable the wireframe to avoid recursion! */ flags |= COGL_DRAW_SKIP_DEBUG_WIREFRAME; _cogl_framebuffer_draw_indexed_attributes ( framebuffer, wire_pipeline, COGL_VERTICES_MODE_LINES, 0, n_indices, wire_indices, attributes, n_attributes, flags); COGL_DEBUG_SET_FLAG (COGL_DEBUG_WIREFRAME); cogl_object_unref (wire_indices); } #endif /* This can be called directly by the CoglJournal to draw attributes * skipping the implicit journal flush, the framebuffer flush and * pipeline validation. */ void _cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) && (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) && mode != COGL_VERTICES_MODE_LINES && mode != COGL_VERTICES_MODE_LINE_LOOP && mode != COGL_VERTICES_MODE_LINE_STRIP) draw_wireframe (framebuffer->context, framebuffer, pipeline, mode, first_vertex, n_vertices, attributes, n_attributes, NULL, flags); else #endif { CoglContext *ctx = framebuffer->context; ctx->driver_vtable->framebuffer_draw_attributes (framebuffer, pipeline, mode, first_vertex, n_vertices, attributes, n_attributes, flags); } } void _cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags) { #ifdef COGL_ENABLE_DEBUG if (G_UNLIKELY (COGL_DEBUG_ENABLED (COGL_DEBUG_WIREFRAME) && (flags & COGL_DRAW_SKIP_DEBUG_WIREFRAME) == 0) && mode != COGL_VERTICES_MODE_LINES && mode != COGL_VERTICES_MODE_LINE_LOOP && mode != COGL_VERTICES_MODE_LINE_STRIP) draw_wireframe (framebuffer->context, framebuffer, pipeline, mode, first_vertex, n_vertices, attributes, n_attributes, indices, flags); else #endif { CoglContext *ctx = framebuffer->context; ctx->driver_vtable->framebuffer_draw_indexed_attributes (framebuffer, pipeline, mode, first_vertex, n_vertices, indices, attributes, n_attributes, flags); } } void cogl_framebuffer_draw_primitive (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPrimitive *primitive) { _cogl_primitive_draw (primitive, framebuffer, pipeline, 0); } void cogl_framebuffer_draw_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2) { const float position[4] = {x_1, y_1, x_2, y_2}; CoglMultiTexturedRect rect; /* XXX: All the _*_rectangle* APIs normalize their input into an array of * _CoglMultiTexturedRect rectangles and pass these on to our work horse; * _cogl_framebuffer_draw_multitextured_rectangles. */ rect.position = position; rect.tex_coords = NULL; rect.tex_coords_len = 0; _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, pipeline, &rect, 1); } void cogl_framebuffer_draw_textured_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2, float s_1, float t_1, float s_2, float t_2) { const float position[4] = {x_1, y_1, x_2, y_2}; const float tex_coords[4] = {s_1, t_1, s_2, t_2}; CoglMultiTexturedRect rect; /* XXX: All the _*_rectangle* APIs normalize their input into an array of * CoglMultiTexturedRect rectangles and pass these on to our work horse; * _cogl_framebuffer_draw_multitextured_rectangles. */ rect.position = position; rect.tex_coords = tex_coords; rect.tex_coords_len = 4; _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, pipeline, &rect, 1); } void cogl_framebuffer_draw_multitextured_rectangle (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x_1, float y_1, float x_2, float y_2, const float *tex_coords, int tex_coords_len) { const float position[4] = {x_1, y_1, x_2, y_2}; CoglMultiTexturedRect rect; /* XXX: All the _*_rectangle* APIs normalize their input into an array of * CoglMultiTexturedRect rectangles and pass these on to our work horse; * _cogl_framebuffer_draw_multitextured_rectangles. */ rect.position = position; rect.tex_coords = tex_coords; rect.tex_coords_len = tex_coords_len; _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, pipeline, &rect, 1); } void cogl_framebuffer_draw_rectangles (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *coordinates, unsigned int n_rectangles) { CoglMultiTexturedRect *rects; int i; /* XXX: All the _*_rectangle* APIs normalize their input into an array of * CoglMultiTexturedRect rectangles and pass these on to our work horse; * _cogl_framebuffer_draw_multitextured_rectangles. */ rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect)); for (i = 0; i < n_rectangles; i++) { rects[i].position = &coordinates[i * 4]; rects[i].tex_coords = NULL; rects[i].tex_coords_len = 0; } _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, pipeline, rects, n_rectangles); } void cogl_framebuffer_draw_textured_rectangles (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, const float *coordinates, unsigned int n_rectangles) { CoglMultiTexturedRect *rects; int i; /* XXX: All the _*_rectangle* APIs normalize their input into an array of * _CoglMultiTexturedRect rectangles and pass these on to our work horse; * _cogl_framebuffer_draw_multitextured_rectangles. */ rects = g_alloca (n_rectangles * sizeof (CoglMultiTexturedRect)); for (i = 0; i < n_rectangles; i++) { rects[i].position = &coordinates[i * 8]; rects[i].tex_coords = &coordinates[i * 8 + 4]; rects[i].tex_coords_len = 4; } _cogl_framebuffer_draw_multitextured_rectangles (framebuffer, pipeline, rects, n_rectangles); } muffin-6.4.1/cogl/cogl/cogl-onscreen-private.h0000664000175000017500000000562414723361714020200 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_ONSCREEN_PRIVATE_H #define __COGL_ONSCREEN_PRIVATE_H #include "cogl-onscreen.h" #include "cogl-framebuffer-private.h" #include "cogl-closure-list-private.h" #include "cogl-list.h" #include typedef struct _CoglOnscreenEvent { CoglList link; CoglOnscreen *onscreen; CoglFrameInfo *info; CoglFrameEvent type; } CoglOnscreenEvent; typedef struct _CoglOnscreenQueuedDirty { CoglList link; CoglOnscreen *onscreen; CoglOnscreenDirtyInfo info; } CoglOnscreenQueuedDirty; struct _CoglOnscreen { CoglFramebuffer _parent; CoglList frame_closures; gboolean resizable; CoglList resize_closures; CoglList dirty_closures; int64_t frame_counter; int64_t swap_frame_counter; /* frame counter at last all to * cogl_onscreen_swap_region() or * cogl_onscreen_swap_buffers() */ GQueue pending_frame_infos; void *winsys; }; CoglOnscreen * _cogl_onscreen_new (void); COGL_EXPORT void _cogl_framebuffer_winsys_update_size (CoglFramebuffer *framebuffer, int width, int height); void _cogl_onscreen_queue_event (CoglOnscreen *onscreen, CoglFrameEvent type, CoglFrameInfo *info); COGL_EXPORT void _cogl_onscreen_notify_frame_sync (CoglOnscreen *onscreen, CoglFrameInfo *info); COGL_EXPORT void _cogl_onscreen_notify_complete (CoglOnscreen *onscreen, CoglFrameInfo *info); void _cogl_onscreen_notify_resize (CoglOnscreen *onscreen); void _cogl_onscreen_queue_dirty (CoglOnscreen *onscreen, const CoglOnscreenDirtyInfo *info); void _cogl_onscreen_queue_full_dirty (CoglOnscreen *onscreen); #endif /* __COGL_ONSCREEN_PRIVATE_H */ muffin-6.4.1/cogl/cogl/cogl-object.c0000664000175000017500000001765714723361714016166 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Robert Bragg */ #include "cogl-config.h" #include #include #include "cogl-util.h" #include "cogl-types.h" #include "cogl-object-private.h" #include "cogl-gtype-private.h" COGL_GTYPE_DEFINE_BASE_CLASS (Object, object); void * cogl_object_ref (void *object) { CoglObject *obj = object; g_return_val_if_fail (object != NULL, NULL); obj->ref_count++; return object; } void _cogl_object_default_unref (void *object) { CoglObject *obj = object; g_return_if_fail (object != NULL); g_return_if_fail (obj->ref_count > 0); if (--obj->ref_count < 1) { void (*free_func)(void *obj); if (obj->n_user_data_entries) { int i; int count = MIN (obj->n_user_data_entries, COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); for (i = 0; i < count; i++) { CoglUserDataEntry *entry = &obj->user_data_entry[i]; if (entry->destroy) entry->destroy (entry->user_data, obj); } if (obj->user_data_array != NULL) { for (i = 0; i < obj->user_data_array->len; i++) { CoglUserDataEntry *entry = &g_array_index (obj->user_data_array, CoglUserDataEntry, i); if (entry->destroy) entry->destroy (entry->user_data, obj); } g_array_free (obj->user_data_array, TRUE); } } COGL_OBJECT_DEBUG_FREE (obj); free_func = obj->klass->virt_free; free_func (obj); } } void cogl_object_unref (void *obj) { void (* unref_func) (void *); g_return_if_fail (obj != NULL); unref_func = ((CoglObject *) obj)->klass->virt_unref; unref_func (obj); } GType cogl_handle_get_type (void) { static GType our_type = 0; /* XXX: We are keeping the "CoglHandle" name for now incase it would * break bindings to change to "CoglObject" */ if (G_UNLIKELY (our_type == 0)) our_type = g_boxed_type_register_static (g_intern_static_string ("CoglHandle"), (GBoxedCopyFunc) cogl_object_ref, (GBoxedFreeFunc) cogl_object_unref); return our_type; } /* XXX: Unlike for cogl_object_get_user_data this code will return * an empty entry if available and no entry for the given key can be * found. */ static CoglUserDataEntry * _cogl_object_find_entry (CoglObject *object, CoglUserDataKey *key) { CoglUserDataEntry *entry = NULL; int count; int i; count = MIN (object->n_user_data_entries, COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); for (i = 0; i < count; i++) { CoglUserDataEntry *current = &object->user_data_entry[i]; if (current->key == key) return current; if (current->user_data == NULL) entry = current; } if (G_UNLIKELY (object->user_data_array != NULL)) { for (i = 0; i < object->user_data_array->len; i++) { CoglUserDataEntry *current = &g_array_index (object->user_data_array, CoglUserDataEntry, i); if (current->key == key) return current; if (current->user_data == NULL) entry = current; } } return entry; } void _cogl_object_set_user_data (CoglObject *object, CoglUserDataKey *key, void *user_data, CoglUserDataDestroyInternalCallback destroy) { CoglUserDataEntry new_entry; CoglUserDataEntry *entry; if (user_data) { new_entry.key = key; new_entry.user_data = user_data; new_entry.destroy = destroy; } else memset (&new_entry, 0, sizeof (new_entry)); entry = _cogl_object_find_entry (object, key); if (entry) { if (G_LIKELY (entry->destroy)) entry->destroy (entry->user_data, object); } else { /* NB: Setting a value of NULL is documented to delete the * corresponding entry so we can return immediately in this * case. */ if (user_data == NULL) return; if (G_LIKELY (object->n_user_data_entries < COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES)) entry = &object->user_data_entry[object->n_user_data_entries++]; else { if (G_UNLIKELY (object->user_data_array == NULL)) { object->user_data_array = g_array_new (FALSE, FALSE, sizeof (CoglUserDataEntry)); } g_array_set_size (object->user_data_array, object->user_data_array->len + 1); entry = &g_array_index (object->user_data_array, CoglUserDataEntry, object->user_data_array->len - 1); object->n_user_data_entries++; } } *entry = new_entry; } void cogl_object_set_user_data (CoglObject *object, CoglUserDataKey *key, void *user_data, CoglUserDataDestroyCallback destroy) { _cogl_object_set_user_data (object, key, user_data, (CoglUserDataDestroyInternalCallback)destroy); } void * cogl_object_get_user_data (CoglObject *object, CoglUserDataKey *key) { int count; int i; count = MIN (object->n_user_data_entries, COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES); for (i = 0; i < count; i++) { CoglUserDataEntry *entry = &object->user_data_entry[i]; if (entry->key == key) return entry->user_data; } if (object->user_data_array != NULL) { for (i = 0; i < object->user_data_array->len; i++) { CoglUserDataEntry *entry = &g_array_index (object->user_data_array, CoglUserDataEntry, i); if (entry->key == key) return entry->user_data; } } return NULL; } void cogl_debug_object_foreach_type (CoglDebugObjectForeachTypeCallback func, void *user_data) { GHashTableIter iter; unsigned long *instance_count; CoglDebugObjectTypeInfo info; g_hash_table_iter_init (&iter, _cogl_debug_instances); while (g_hash_table_iter_next (&iter, (void *) &info.name, (void *) &instance_count)) { info.instance_count = *instance_count; func (&info, user_data); } } static void print_instances_cb (const CoglDebugObjectTypeInfo *info, void *user_data) { g_print ("\t%s: %lu\n", info->name, info->instance_count); } void cogl_debug_object_print_instances (void) { g_print ("Cogl instances:\n"); cogl_debug_object_foreach_type (print_instances_cb, NULL); } muffin-6.4.1/cogl/cogl/cogl-defines.h.in0000664000175000017500000000260514723361714016732 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_DEFINES_H__ #define __COGL_DEFINES_H__ @COGL_DEFINES@ #endif muffin-6.4.1/cogl/cogl/cogl-version.h0000664000175000017500000001031414723361714016371 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2012,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_VERSION_H__ #define __COGL_VERSION_H__ #include /* Macros to handle compacting a 3-component version number into an * int for quick comparison. This assumes all of the components are <= * 1023 and that an int is >= 31 bits */ #define COGL_VERSION_COMPONENT_BITS 10 #define COGL_VERSION_MAX_COMPONENT_VALUE \ ((1 << COGL_VERSION_COMPONENT_BITS) - 1) /** * COGL_VERSION_ENCODE: * @major: The major part of a version number * @minor: The minor part of a version number * @micro: The micro part of a version number * * Encodes a 3 part version number into a single integer. This can be * used to compare the Cogl version. For example if there is a known * bug in Cogl versions between 1.3.2 and 1.3.4 you could use the * following code to provide a workaround: * * |[ * #if COGL_VERSION >= COGL_VERSION_ENCODE (1, 3, 2) && \ * COGL_VERSION <= COGL_VERSION_ENCODE (1, 3, 4) * /* Do the workaround */ * #endif * ]| * * Since: 1.12.0 */ #define COGL_VERSION_ENCODE(major, minor, micro) \ (((major) << (COGL_VERSION_COMPONENT_BITS * 2)) | \ ((minor) << COGL_VERSION_COMPONENT_BITS) \ | (micro)) /** * COGL_VERSION_GET_MAJOR: * @version: An encoded version number * * Extracts the major part of an encoded version number. * * Since: 1.12.0 */ #define COGL_VERSION_GET_MAJOR(version) \ (((version) >> (COGL_VERSION_COMPONENT_BITS * 2)) \ & COGL_VERSION_MAX_COMPONENT_VALUE) /** * COGL_VERSION_GET_MINOR: * @version: An encoded version number * * Extracts the minor part of an encoded version number. * * Since: 1.12.0 */ #define COGL_VERSION_GET_MINOR(version) \ (((version) >> COGL_VERSION_COMPONENT_BITS) & \ COGL_VERSION_MAX_COMPONENT_VALUE) /** * COGL_VERSION_GET_MICRO: * @version: An encoded version number * * Extracts the micro part of an encoded version number. * * Since: 1.12.0 */ #define COGL_VERSION_GET_MICRO(version) \ ((version) & COGL_VERSION_MAX_COMPONENT_VALUE) /** * COGL_VERSION_CHECK: * @major: The major part of a version number * @minor: The minor part of a version number * @micro: The micro part of a version number * * A convenient macro to check whether the Cogl version being compiled * against is at least the given version number. For example if the * function cogl_pipeline_frobnicate was added in version 2.0.1 and * you want to conditionally use that function when it is available, * you could write the following: * * |[ * #if COGL_VERSION_CHECK (2, 0, 1) * cogl_pipeline_frobnicate (pipeline); * #else * /* Frobnication is not supported. Use a red color instead */ * cogl_pipeline_set_color_4f (pipeline, 1.0f, 0.0f, 0.0f, 1.0f); * #endif * ]| * * Return value: %TRUE if the Cogl version being compiled against is * greater than or equal to the given three part version number. * Since: 1.12.0 */ #define COGL_VERSION_CHECK(major, minor, micro) \ (COGL_VERSION >= COGL_VERSION_ENCODE (major, minor, micro)) #endif /* __COGL_VERSION_H__ */ muffin-6.4.1/cogl/cogl/cogl-sub-texture.h0000664000175000017500000001061714723361714017201 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_SUB_TEXTURE_H #define __COGL_SUB_TEXTURE_H G_BEGIN_DECLS /** * SECTION:cogl-sub-texture * @short_description: Functions for creating and manipulating * sub-textures. * * These functions allow high-level textures to be created that * represent a sub-region of another texture. For example these * can be used to implement custom texture atlasing schemes. */ #define COGL_SUB_TEXTURE(tex) ((CoglSubTexture *) tex) typedef struct _CoglSubTexture CoglSubTexture; /** * cogl_sub_texture_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ GType cogl_sub_texture_get_gtype (void); /** * cogl_sub_texture_new: * @ctx: A #CoglContext pointer * @parent_texture: The full texture containing a sub-region you want * to make a #CoglSubTexture from. * @sub_x: The top-left x coordinate of the parent region to make * a texture from. * @sub_y: The top-left y coordinate of the parent region to make * a texture from. * @sub_width: The width of the parent region to make a texture from. * @sub_height: The height of the parent region to make a texture * from. * * Creates a high-level #CoglSubTexture representing a sub-region of * any other #CoglTexture. The sub-region must strictly lye within the * bounds of the @parent_texture. The returned texture implements the * #CoglMetaTexture interface because it's not a low level texture * that hardware can understand natively. * * Remember: Unless you are using high level drawing APIs such * as cogl_rectangle() or other APIs documented to understand the * #CoglMetaTexture interface then you need to use the * #CoglMetaTexture interface to resolve a #CoglSubTexture into a * low-level texture before drawing. * * Return value: (transfer full): A newly allocated #CoglSubTexture * representing a sub-region of @parent_texture. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglSubTexture * cogl_sub_texture_new (CoglContext *ctx, CoglTexture *parent_texture, int sub_x, int sub_y, int sub_width, int sub_height); /** * cogl_sub_texture_get_parent: * @sub_texture: A pointer to a #CoglSubTexture * * Retrieves the parent texture that @sub_texture derives its content * from. This is the texture that was passed to * cogl_sub_texture_new() as the parent_texture argument. * * Return value: (transfer none): The parent texture that @sub_texture * derives its content from. * Since: 1.10 * Stability: unstable */ COGL_EXPORT CoglTexture * cogl_sub_texture_get_parent (CoglSubTexture *sub_texture); /** * cogl_is_sub_texture: * @object: a #CoglObject * * Checks whether @object is a #CoglSubTexture. * * Return value: %TRUE if the passed @object represents a * #CoglSubTexture and %FALSE otherwise. * * Since: 1.10 * Stability: unstable */ COGL_EXPORT gboolean cogl_is_sub_texture (void *object); G_END_DECLS #endif /* __COGL_SUB_TEXTURE_H */ muffin-6.4.1/cogl/cogl/cogl-framebuffer-private.h0000664000175000017500000003203314723361714020642 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009 Intel Corporation. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_FRAMEBUFFER_PRIVATE_H #define __COGL_FRAMEBUFFER_PRIVATE_H #include "cogl-object-private.h" #include "cogl-matrix-stack-private.h" #include "cogl-journal-private.h" #include "winsys/cogl-winsys-private.h" #include "cogl-attribute-private.h" #include "cogl-offscreen.h" #include "cogl-gl-header.h" #include "cogl-clip-stack.h" typedef enum _CoglFramebufferType { COGL_FRAMEBUFFER_TYPE_ONSCREEN, COGL_FRAMEBUFFER_TYPE_OFFSCREEN } CoglFramebufferType; typedef struct { CoglSwapChain *swap_chain; gboolean need_stencil; int samples_per_pixel; gboolean stereo_enabled; } CoglFramebufferConfig; /* Flags to pass to _cogl_offscreen_new_with_texture_full */ typedef enum { COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL = 1 } CoglOffscreenFlags; /* XXX: The order of these indices determines the order they are * flushed. * * Flushing clip state may trash the modelview and projection matrices * so we must do it before flushing the matrices. */ typedef enum _CoglFramebufferStateIndex { COGL_FRAMEBUFFER_STATE_INDEX_BIND = 0, COGL_FRAMEBUFFER_STATE_INDEX_VIEWPORT = 1, COGL_FRAMEBUFFER_STATE_INDEX_CLIP = 2, COGL_FRAMEBUFFER_STATE_INDEX_DITHER = 3, COGL_FRAMEBUFFER_STATE_INDEX_MODELVIEW = 4, COGL_FRAMEBUFFER_STATE_INDEX_PROJECTION = 5, COGL_FRAMEBUFFER_STATE_INDEX_FRONT_FACE_WINDING = 6, COGL_FRAMEBUFFER_STATE_INDEX_DEPTH_WRITE = 7, COGL_FRAMEBUFFER_STATE_INDEX_STEREO_MODE = 8, COGL_FRAMEBUFFER_STATE_INDEX_MAX = 9 } CoglFramebufferStateIndex; typedef enum _CoglFramebufferState { COGL_FRAMEBUFFER_STATE_BIND = 1<<0, COGL_FRAMEBUFFER_STATE_VIEWPORT = 1<<1, COGL_FRAMEBUFFER_STATE_CLIP = 1<<2, COGL_FRAMEBUFFER_STATE_DITHER = 1<<3, COGL_FRAMEBUFFER_STATE_MODELVIEW = 1<<4, COGL_FRAMEBUFFER_STATE_PROJECTION = 1<<5, COGL_FRAMEBUFFER_STATE_FRONT_FACE_WINDING = 1<<6, COGL_FRAMEBUFFER_STATE_DEPTH_WRITE = 1<<7, COGL_FRAMEBUFFER_STATE_STEREO_MODE = 1<<8 } CoglFramebufferState; #define COGL_FRAMEBUFFER_STATE_ALL ((1<config to configure if we want a depth or stencil buffer so * we can get rid of these flags */ CoglOffscreenFlags create_flags; }; void _cogl_framebuffer_init (CoglFramebuffer *framebuffer, CoglContext *ctx, CoglFramebufferType type, int width, int height); /* XXX: For a public api we might instead want a way to explicitly * set the _premult status of a framebuffer or what components we * care about instead of exposing the CoglPixelFormat * internal_format. * * The current use case for this api is where we create an offscreen * framebuffer for a shared atlas texture that has a format of * RGBA_8888 disregarding the premultiplied alpha status for * individual atlased textures or whether the alpha component is being * discarded. We want to overried the internal_format that will be * derived from the texture. */ void _cogl_framebuffer_set_internal_format (CoglFramebuffer *framebuffer, CoglPixelFormat internal_format); void _cogl_framebuffer_free (CoglFramebuffer *framebuffer); const CoglWinsysVtable * _cogl_framebuffer_get_winsys (CoglFramebuffer *framebuffer); void _cogl_framebuffer_clear_without_flush4f (CoglFramebuffer *framebuffer, unsigned long buffers, float red, float green, float blue, float alpha); void _cogl_framebuffer_mark_clear_clip_dirty (CoglFramebuffer *framebuffer); /* * _cogl_framebuffer_get_clip_stack: * @framebuffer: A #CoglFramebuffer * * Gets a pointer to the current clip stack. A reference is not taken on the * stack so if you want to keep it you should call * _cogl_clip_stack_ref(). * * Return value: a pointer to the @framebuffer clip stack. */ CoglClipStack * _cogl_framebuffer_get_clip_stack (CoglFramebuffer *framebuffer); COGL_EXPORT CoglMatrixStack * _cogl_framebuffer_get_modelview_stack (CoglFramebuffer *framebuffer); COGL_EXPORT CoglMatrixStack * _cogl_framebuffer_get_projection_stack (CoglFramebuffer *framebuffer); void _cogl_framebuffer_add_dependency (CoglFramebuffer *framebuffer, CoglFramebuffer *dependency); void _cogl_framebuffer_flush_journal (CoglFramebuffer *framebuffer); void _cogl_framebuffer_flush_dependency_journals (CoglFramebuffer *framebuffer); void _cogl_framebuffer_flush_state (CoglFramebuffer *draw_buffer, CoglFramebuffer *read_buffer, CoglFramebufferState state); CoglFramebuffer * _cogl_get_read_framebuffer (void); GSList * _cogl_create_framebuffer_stack (void); void _cogl_free_framebuffer_stack (GSList *stack); /* * _cogl_offscreen_new_with_texture_full: * @texture: A #CoglTexture pointer * @create_flags: Flags specifying how to create the FBO * @level: The mipmap level within the texture to target * * Creates a new offscreen buffer which will target the given * texture. By default the buffer will have a depth and stencil * buffer. This can be disabled by passing * %COGL_OFFSCREEN_DISABLE_DEPTH_AND_STENCIL in @create_flags. * * Return value: the new CoglOffscreen object. */ CoglOffscreen * _cogl_offscreen_new_with_texture_full (CoglTexture *texture, CoglOffscreenFlags create_flags, int level); void _cogl_framebuffer_save_clip_stack (CoglFramebuffer *framebuffer); void _cogl_framebuffer_restore_clip_stack (CoglFramebuffer *framebuffer); void _cogl_framebuffer_unref (CoglFramebuffer *framebuffer); /* This can be called directly by the CoglJournal to draw attributes * skipping the implicit journal flush, the framebuffer flush and * pipeline validation. */ void _cogl_framebuffer_draw_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); void _cogl_framebuffer_draw_indexed_attributes (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglVerticesMode mode, int first_vertex, int n_vertices, CoglIndices *indices, CoglAttribute **attributes, int n_attributes, CoglDrawFlags flags); void cogl_framebuffer_set_viewport4fv (CoglFramebuffer *framebuffer, float *viewport); unsigned long _cogl_framebuffer_compare (CoglFramebuffer *a, CoglFramebuffer *b, unsigned long state); static inline CoglMatrixEntry * _cogl_framebuffer_get_modelview_entry (CoglFramebuffer *framebuffer) { CoglMatrixStack *modelview_stack = _cogl_framebuffer_get_modelview_stack (framebuffer); return modelview_stack->last_entry; } static inline CoglMatrixEntry * _cogl_framebuffer_get_projection_entry (CoglFramebuffer *framebuffer) { CoglMatrixStack *projection_stack = _cogl_framebuffer_get_projection_stack (framebuffer); return projection_stack->last_entry; } gboolean _cogl_framebuffer_read_pixels_into_bitmap (CoglFramebuffer *framebuffer, int x, int y, CoglReadPixelsFlags source, CoglBitmap *bitmap, GError **error); /* * _cogl_framebuffer_get_stencil_bits: * @framebuffer: a pointer to a #CoglFramebuffer * * Retrieves the number of stencil bits of @framebuffer * * Return value: the number of bits * * Since: 2.0 * Stability: unstable */ COGL_EXPORT int _cogl_framebuffer_get_stencil_bits (CoglFramebuffer *framebuffer); #endif /* __COGL_FRAMEBUFFER_PRIVATE_H */ muffin-6.4.1/cogl/cogl-path/0000775000175000017500000000000014723361714014544 5ustar fabiofabiomuffin-6.4.1/cogl/cogl-path/cogl-path-enum-types.c.in0000664000175000017500000000212214723361714021274 0ustar fabiofabio/*** BEGIN file-header ***/ #include "cogl-config.h" /* We need to undefine this so that we will be sure to include * cogl-path.h instead of cogl2-path.h when we include the framebuffer * header. Otherwise it will include both headers and it won't * compile. */ #undef COGL_ENABLE_EXPERIMENTAL_2_0_API #include "cogl-path-enum-types.h" /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static gsize g_enum_type_id = 0; if (g_once_init_enter (&g_enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType id; id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_enum_type_id, id); } return g_enum_type_id; } /*** END value-tail ***/ muffin-6.4.1/cogl/cogl-path/meson.build0000664000175000017500000000452514723361714016714 0ustar fabiofabiocogl_path_includesubdir = join_paths(cogl_includesubdir, 'cogl-path') cogl_path_includedir = join_paths(cogl_includedir, 'cogl-path') cogl_path_public_headers = [ 'cogl-path.h', 'cogl-path-functions.h', 'cogl-path-types.h', ] cogl_path_sources = [ 'cogl-path.c', 'cogl-path-private.h', 'tesselator/dict-list.h', 'tesselator/dict.c', 'tesselator/dict.h', 'tesselator/geom.c', 'tesselator/geom.h', 'tesselator/gluos.h', 'tesselator/memalloc.h', 'tesselator/mesh.c', 'tesselator/mesh.h', 'tesselator/normal.c', 'tesselator/normal.h', 'tesselator/priorityq-heap.h', 'tesselator/priorityq-sort.h', 'tesselator/priorityq.c', 'tesselator/priorityq.h', 'tesselator/render.c', 'tesselator/render.h', 'tesselator/sweep.c', 'tesselator/sweep.h', 'tesselator/tess.c', 'tesselator/tess.h', 'tesselator/tesselator.h', 'tesselator/tessmono.c', 'tesselator/tessmono.h', ] cogl_path_includepath = include_directories('.') libmutter_cogl_path_enum_types = gnome.mkenums('cogl-path-enum-types', sources: 'cogl-path-types.h', c_template: 'cogl-path-enum-types.c.in', h_template: 'cogl-path-enum-types.h.in', install_dir: cogl_path_includedir, install_header: true, ) libmutter_cogl_path_enum_types_h = libmutter_cogl_path_enum_types[1] cogl_path_sources += libmutter_cogl_path_enum_types cogl_path_c_args = [ cogl_c_args, ] libmutter_cogl_path = shared_library('muffin-cogl-path-' + libmutter_api_version, sources: [cogl_path_sources, cogl_path_public_headers], version: '0.0.0', soversion: 0, c_args: cogl_path_c_args, include_directories: [cogl_includepath, cogl_path_includepath], gnu_symbol_visibility: 'hidden', dependencies: libmutter_cogl_dep, install_rpath: pkglibdir, install_dir: pkglibdir, install: true, ) libmutter_cogl_path_dep = declare_dependency( sources: [libmutter_cogl_path_enum_types_h], link_with: libmutter_cogl_path ) install_headers(cogl_path_public_headers, subdir: cogl_path_includesubdir) pkg.generate(libmutter_cogl_path, name: 'CoglPath', filebase: 'muffin-cogl-path-' + libmutter_api_version, description: 'A 2D path drawing library for Cogl in mutter', subdirs: join_paths(pkgname, 'cogl'), requires: [cogl_pkg_deps, libmutter_cogl_name], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, ], install_dir: pcdir, ) muffin-6.4.1/cogl/cogl-path/cogl-path-enum-types.h.in0000664000175000017500000000102314723361714021300 0ustar fabiofabio/*** BEGIN file-header ***/ #ifndef __COGL_PATH_ENUM_TYPES_H__ #define __COGL_PATH_ENUM_TYPES_H__ #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* __COGL_PATH_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) G_GNUC_CONST; #define COGL_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ muffin-6.4.1/cogl/cogl-path/cogl-path-functions.h0000664000175000017500000003547314723361714020615 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PATH_FUNCTIONS_H__ #define __COGL_PATH_FUNCTIONS_H__ #include #ifdef COGL_COMPILATION #include "cogl-context.h" #else #include #endif #include G_BEGIN_DECLS /** * cogl_path_get_gtype: * * Returns: a #GType that can be used with the GLib type system. */ COGL_EXPORT GType cogl_path_get_gtype (void); #define cogl_path_new cogl2_path_new /** * cogl_path_new: * * Creates a new, empty path object. The default fill rule is * %COGL_PATH_FILL_RULE_EVEN_ODD. * * Return value: A pointer to a newly allocated #CoglPath, which can * be freed using cogl_object_unref(). * * Since: 2.0 */ COGL_EXPORT CoglPath * cogl_path_new (void); /** * cogl_path_copy: * @path: A #CoglPath object * * Returns a new copy of the path in @path. The new path has a * reference count of 1 so you should unref it with * cogl_object_unref() if you no longer need it. * * Internally the path will share the data until one of the paths is * modified so copying paths should be relatively cheap. * * Return value: (transfer full): a copy of the path in @path. * * Since: 2.0 */ COGL_EXPORT CoglPath * cogl_path_copy (CoglPath *path); /** * cogl_is_path: * @object: A #CoglObject * * Gets whether the given object references an existing path object. * * Return value: %TRUE if the object references a #CoglPath, * %FALSE otherwise. * * Since: 2.0 */ COGL_EXPORT gboolean cogl_is_path (void *object); #define cogl_path_move_to cogl2_path_move_to /** * cogl_path_move_to: * @x: X coordinate of the pen location to move to. * @y: Y coordinate of the pen location to move to. * * Moves the pen to the given location. If there is an existing path * this will start a new disjoint subpath. * * Since: 2.0 */ COGL_EXPORT void cogl_path_move_to (CoglPath *path, float x, float y); #define cogl_path_rel_move_to cogl2_path_rel_move_to /** * cogl_path_rel_move_to: * @x: X offset from the current pen location to move the pen to. * @y: Y offset from the current pen location to move the pen to. * * Moves the pen to the given offset relative to the current pen * location. If there is an existing path this will start a new * disjoint subpath. * * Since: 2.0 */ COGL_EXPORT void cogl_path_rel_move_to (CoglPath *path, float x, float y); #define cogl_path_line_to cogl2_path_line_to /** * cogl_path_line_to: * @x: X coordinate of the end line vertex * @y: Y coordinate of the end line vertex * * Adds a straight line segment to the current path that ends at the * given coordinates. * * Since: 2.0 */ COGL_EXPORT void cogl_path_line_to (CoglPath *path, float x, float y); #define cogl_path_rel_line_to cogl2_path_rel_line_to /** * cogl_path_rel_line_to: * @x: X offset from the current pen location of the end line vertex * @y: Y offset from the current pen location of the end line vertex * * Adds a straight line segment to the current path that ends at the * given coordinates relative to the current pen location. * * Since: 2.0 */ COGL_EXPORT void cogl_path_rel_line_to (CoglPath *path, float x, float y); #define cogl_path_arc cogl2_path_arc /** * cogl_path_arc: * @center_x: X coordinate of the elliptical arc center * @center_y: Y coordinate of the elliptical arc center * @radius_x: X radius of the elliptical arc * @radius_y: Y radius of the elliptical arc * @angle_1: Angle in degrees at which the arc begin * @angle_2: Angle in degrees at which the arc ends * * Adds an elliptical arc segment to the current path. A straight line * segment will link the current pen location with the first vertex * of the arc. If you perform a move_to to the arcs start just before * drawing it you create a free standing arc. * * The angles are measured in degrees where 0° is in the direction of * the positive X axis and 90° is in the direction of the positive Y * axis. The angle of the arc begins at @angle_1 and heads towards * @angle_2 (so if @angle_2 is less than @angle_1 it will decrease, * otherwise it will increase). * * Since: 2.0 */ COGL_EXPORT void cogl_path_arc (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y, float angle_1, float angle_2); #define cogl_path_curve_to cogl2_path_curve_to /** * cogl_path_curve_to: * @x_1: X coordinate of the second bezier control point * @y_1: Y coordinate of the second bezier control point * @x_2: X coordinate of the third bezier control point * @y_2: Y coordinate of the third bezier control point * @x_3: X coordinate of the fourth bezier control point * @y_3: Y coordinate of the fourth bezier control point * * Adds a cubic bezier curve segment to the current path with the given * second, third and fourth control points and using current pen location * as the first control point. * * Since: 2.0 */ COGL_EXPORT void cogl_path_curve_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float x_3, float y_3); #define cogl_path_rel_curve_to cogl2_path_rel_curve_to /** * cogl_path_rel_curve_to: * @x_1: X coordinate of the second bezier control point * @y_1: Y coordinate of the second bezier control point * @x_2: X coordinate of the third bezier control point * @y_2: Y coordinate of the third bezier control point * @x_3: X coordinate of the fourth bezier control point * @y_3: Y coordinate of the fourth bezier control point * * Adds a cubic bezier curve segment to the current path with the given * second, third and fourth control points and using current pen location * as the first control point. The given coordinates are relative to the * current pen location. * * Since: 2.0 */ COGL_EXPORT void cogl_path_rel_curve_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float x_3, float y_3); #define cogl_path_close cogl2_path_close /** * cogl_path_close: * * Closes the path being constructed by adding a straight line segment * to it that ends at the first vertex of the path. * * Since: 2.0 */ COGL_EXPORT void cogl_path_close (CoglPath *path); #define cogl_path_line cogl2_path_line /** * cogl_path_line: * @x_1: X coordinate of the start line vertex * @y_1: Y coordinate of the start line vertex * @x_2: X coordinate of the end line vertex * @y_2: Y coordinate of the end line vertex * * Constructs a straight line shape starting and ending at the given * coordinates. If there is an existing path this will start a new * disjoint sub-path. * * Since: 2.0 */ COGL_EXPORT void cogl_path_line (CoglPath *path, float x_1, float y_1, float x_2, float y_2); #define cogl_path_polyline cogl2_path_polyline /** * cogl_path_polyline: * @coords: (in) (array) (transfer none): A pointer to the first element of an * array of fixed-point values that specify the vertex coordinates. * @num_points: The total number of vertices. * * Constructs a series of straight line segments, starting from the * first given vertex coordinate. If there is an existing path this * will start a new disjoint sub-path. Each subsequent segment starts * where the previous one ended and ends at the next given vertex * coordinate. * * The coords array must contain 2 * num_points values. The first value * represents the X coordinate of the first vertex, the second value * represents the Y coordinate of the first vertex, continuing in the same * fashion for the rest of the vertices. (num_points - 1) segments will * be constructed. * * Since: 2.0 */ COGL_EXPORT void cogl_path_polyline (CoglPath *path, const float *coords, int num_points); #define cogl_path_polygon cogl2_path_polygon /** * cogl_path_polygon: * @coords: (in) (array) (transfer none): A pointer to the first element of * an array of fixed-point values that specify the vertex coordinates. * @num_points: The total number of vertices. * * Constructs a polygonal shape of the given number of vertices. If * there is an existing path this will start a new disjoint sub-path. * * The coords array must contain 2 * num_points values. The first value * represents the X coordinate of the first vertex, the second value * represents the Y coordinate of the first vertex, continuing in the same * fashion for the rest of the vertices. * * Since: 2.0 */ COGL_EXPORT void cogl_path_polygon (CoglPath *path, const float *coords, int num_points); #define cogl_path_rectangle cogl2_path_rectangle /** * cogl_path_rectangle: * @x_1: X coordinate of the top-left corner. * @y_1: Y coordinate of the top-left corner. * @x_2: X coordinate of the bottom-right corner. * @y_2: Y coordinate of the bottom-right corner. * * Constructs a rectangular shape at the given coordinates. If there * is an existing path this will start a new disjoint sub-path. * * Since: 2.0 */ COGL_EXPORT void cogl_path_rectangle (CoglPath *path, float x_1, float y_1, float x_2, float y_2); #define cogl_path_ellipse cogl2_path_ellipse /** * cogl_path_ellipse: * @center_x: X coordinate of the ellipse center * @center_y: Y coordinate of the ellipse center * @radius_x: X radius of the ellipse * @radius_y: Y radius of the ellipse * * Constructs an ellipse shape. If there is an existing path this will * start a new disjoint sub-path. * * Since: 2.0 */ COGL_EXPORT void cogl_path_ellipse (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y); #define cogl_path_round_rectangle cogl2_path_round_rectangle /** * cogl_path_round_rectangle: * @x_1: X coordinate of the top-left corner. * @y_1: Y coordinate of the top-left corner. * @x_2: X coordinate of the bottom-right corner. * @y_2: Y coordinate of the bottom-right corner. * @radius: Radius of the corner arcs. * @arc_step: Angle increment resolution for subdivision of * the corner arcs. * * Constructs a rectangular shape with rounded corners. If there is an * existing path this will start a new disjoint sub-path. * * Since: 2.0 */ COGL_EXPORT void cogl_path_round_rectangle (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float radius, float arc_step); #define cogl_path_set_fill_rule cogl2_path_set_fill_rule /** * cogl_path_set_fill_rule: * @fill_rule: The new fill rule. * * Sets the fill rule of the current path to @fill_rule. This will * affect how the path is filled when cogl_path_fill() is later * called. Note that the fill rule state is attached to the path so * calling cogl_get_path() will preserve the fill rule and calling * cogl_path_new() will reset the fill rule back to the default. * * Since: 2.0 */ COGL_EXPORT void cogl_path_set_fill_rule (CoglPath *path, CoglPathFillRule fill_rule); #define cogl_path_get_fill_rule cogl2_path_get_fill_rule /** * cogl_path_get_fill_rule: * * Retrieves the fill rule set using cogl_path_set_fill_rule(). * * Return value: the fill rule that is used for the current path. * * Since: 2.0 */ COGL_EXPORT CoglPathFillRule cogl_path_get_fill_rule (CoglPath *path); /** * cogl_framebuffer_fill_path: * @framebuffer: A #CoglFramebuffer * @pipeline: A #CoglPipeline to render with * @path: The #CoglPath to fill * * Fills the interior of the path using the fragment operations * defined by the pipeline. * * The interior of the shape is determined using the fill rule of the * path. See %CoglPathFillRule for details. * * The result of referencing sliced textures in your current * pipeline when filling a path are undefined. You should pass * the %COGL_TEXTURE_NO_SLICING flag when loading any texture you will * use while filling a path. * * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPath *path); /** * cogl_framebuffer_stroke_path: * @framebuffer: A #CoglFramebuffer * @pipeline: A #CoglPipeline to render with * @path: The #CoglPath to stroke * * Strokes the edge of the path using the fragment operations defined * by the pipeline. The stroke line will have a width of 1 pixel * regardless of the current transformation matrix. * * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPath *path); /** * cogl_framebuffer_push_path_clip: * @framebuffer: A #CoglFramebuffer pointer * @path: The path to clip with. * * Sets a new clipping area using the silhouette of the specified, * filled @path. The clipping area is intersected with the previous * clipping area. To restore the previous clipping area, call * cogl_framebuffer_pop_clip(). * * Since: 1.0 * Stability: unstable */ COGL_EXPORT void cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, CoglPath *path); G_END_DECLS #endif /* __COGL_PATH_FUNCTIONS_H__ */ muffin-6.4.1/cogl/cogl-path/cogl-path.c0000664000175000017500000012547114723361714016600 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2007,2008,2009,2010,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Ivan Leben * Øyvind Kolås * Neil Roberts * Robert Bragg */ #include "cogl-config.h" #include "cogl-util.h" #include "cogl-object.h" #include "cogl-context-private.h" #include "cogl-journal-private.h" #include "cogl-pipeline-private.h" #include "cogl-framebuffer-private.h" #include "cogl-primitive-private.h" #include "cogl-texture-private.h" #include "cogl-primitives-private.h" #include "cogl-private.h" #include "cogl-attribute-private.h" #include "cogl1-context.h" #include "tesselator/tesselator.h" #include "cogl-path/cogl-path.h" #include "cogl-path-private.h" #include "cogl-gtype-private.h" #include #include #define _COGL_MAX_BEZ_RECURSE_DEPTH 16 static void _cogl_path_free (CoglPath *path); static void _cogl_path_build_fill_attribute_buffer (CoglPath *path); static CoglPrimitive *_cogl_path_get_fill_primitive (CoglPath *path); static void _cogl_path_build_stroke_attribute_buffer (CoglPath *path); COGL_OBJECT_DEFINE (Path, path); COGL_GTYPE_DEFINE_CLASS (Path, path); static void _cogl_path_data_clear_vbos (CoglPathData *data) { int i; if (data->fill_attribute_buffer) { cogl_object_unref (data->fill_attribute_buffer); cogl_object_unref (data->fill_vbo_indices); for (i = 0; i < COGL_PATH_N_ATTRIBUTES; i++) cogl_object_unref (data->fill_attributes[i]); data->fill_attribute_buffer = NULL; } if (data->fill_primitive) { cogl_object_unref (data->fill_primitive); data->fill_primitive = NULL; } if (data->stroke_attribute_buffer) { cogl_object_unref (data->stroke_attribute_buffer); for (i = 0; i < data->stroke_n_attributes; i++) cogl_object_unref (data->stroke_attributes[i]); g_free (data->stroke_attributes); data->stroke_attribute_buffer = NULL; } } static void _cogl_path_data_unref (CoglPathData *data) { if (--data->ref_count <= 0) { _cogl_path_data_clear_vbos (data); g_array_free (data->path_nodes, TRUE); g_slice_free (CoglPathData, data); } } static void _cogl_path_modify (CoglPath *path) { /* This needs to be called whenever the path is about to be modified to implement copy-on-write semantics */ /* If there is more than one path using the data then we need to copy the data instead */ if (path->data->ref_count != 1) { CoglPathData *old_data = path->data; path->data = g_slice_dup (CoglPathData, old_data); path->data->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); g_array_append_vals (path->data->path_nodes, old_data->path_nodes->data, old_data->path_nodes->len); path->data->fill_attribute_buffer = NULL; path->data->fill_primitive = NULL; path->data->stroke_attribute_buffer = NULL; path->data->ref_count = 1; _cogl_path_data_unref (old_data); } else /* The path is altered so the vbos will now be invalid */ _cogl_path_data_clear_vbos (path->data); } void cogl2_path_set_fill_rule (CoglPath *path, CoglPathFillRule fill_rule) { g_return_if_fail (cogl_is_path (path)); if (path->data->fill_rule != fill_rule) { _cogl_path_modify (path); path->data->fill_rule = fill_rule; } } CoglPathFillRule cogl2_path_get_fill_rule (CoglPath *path) { g_return_val_if_fail (cogl_is_path (path), COGL_PATH_FILL_RULE_NON_ZERO); return path->data->fill_rule; } static void _cogl_path_add_node (CoglPath *path, gboolean new_sub_path, float x, float y) { CoglPathNode new_node; CoglPathData *data; _cogl_path_modify (path); data = path->data; new_node.x = x; new_node.y = y; new_node.path_size = 0; if (new_sub_path || data->path_nodes->len == 0) data->last_path = data->path_nodes->len; g_array_append_val (data->path_nodes, new_node); g_array_index (data->path_nodes, CoglPathNode, data->last_path).path_size++; if (data->path_nodes->len == 1) { data->path_nodes_min.x = data->path_nodes_max.x = x; data->path_nodes_min.y = data->path_nodes_max.y = y; } else { if (x < data->path_nodes_min.x) data->path_nodes_min.x = x; if (x > data->path_nodes_max.x) data->path_nodes_max.x = x; if (y < data->path_nodes_min.y) data->path_nodes_min.y = y; if (y > data->path_nodes_max.y) data->path_nodes_max.y = y; } /* Once the path nodes have been modified then we'll assume it's no longer a rectangle. cogl2_path_rectangle will set this back to TRUE if this has been called from there */ data->is_rectangle = FALSE; } static void _cogl_path_stroke_nodes (CoglPath *path, CoglFramebuffer *framebuffer, CoglPipeline *pipeline) { CoglPathData *data; CoglPipeline *copy = NULL; unsigned int path_start; int path_num = 0; CoglPathNode *node; g_return_if_fail (cogl_is_path (path)); g_return_if_fail (cogl_is_framebuffer (framebuffer)); g_return_if_fail (cogl_is_pipeline (pipeline)); data = path->data; if (data->path_nodes->len == 0) return; if (cogl_pipeline_get_n_layers (pipeline) != 0) { copy = cogl_pipeline_copy (pipeline); _cogl_pipeline_prune_to_n_layers (copy, 0); pipeline = copy; } _cogl_path_build_stroke_attribute_buffer (path); for (path_start = 0; path_start < data->path_nodes->len; path_start += node->path_size) { CoglPrimitive *primitive; node = &g_array_index (data->path_nodes, CoglPathNode, path_start); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_LINE_STRIP, node->path_size, &data->stroke_attributes[path_num], 1); cogl_primitive_draw (primitive, framebuffer, pipeline); cogl_object_unref (primitive); path_num++; } if (copy) cogl_object_unref (copy); } void _cogl_path_get_bounds (CoglPath *path, float *min_x, float *min_y, float *max_x, float *max_y) { CoglPathData *data = path->data; if (data->path_nodes->len == 0) { *min_x = 0.0f; *min_y = 0.0f; *max_x = 0.0f; *max_y = 0.0f; } else { *min_x = data->path_nodes_min.x; *min_y = data->path_nodes_min.y; *max_x = data->path_nodes_max.x; *max_y = data->path_nodes_max.y; } } static void _cogl_path_fill_nodes_with_clipped_rectangle (CoglPath *path, CoglFramebuffer *framebuffer, CoglPipeline *pipeline) { /* We need at least three stencil bits to combine clips */ if (_cogl_framebuffer_get_stencil_bits (framebuffer) >= 3) { static gboolean seen_warning = FALSE; if (!seen_warning) { g_warning ("Paths can not be filled using materials with " "sliced textures unless there is a stencil " "buffer"); seen_warning = TRUE; } } cogl_framebuffer_push_path_clip (framebuffer, path); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, path->data->path_nodes_min.x, path->data->path_nodes_min.y, path->data->path_nodes_max.x, path->data->path_nodes_max.y); cogl_framebuffer_pop_clip (framebuffer); } static gboolean validate_layer_cb (CoglPipelineLayer *layer, void *user_data) { gboolean *needs_fallback = user_data; CoglTexture *texture = _cogl_pipeline_layer_get_texture (layer); /* If any of the layers of the current pipeline contain sliced * textures or textures with waste then it won't work to draw the * path directly. Instead we fallback to pushing the path as a clip * on the clip-stack and drawing the path's bounding rectangle * instead. */ if (texture != NULL && (cogl_texture_is_sliced (texture) || !_cogl_texture_can_hardware_repeat (texture))) *needs_fallback = TRUE; return !*needs_fallback; } static void _cogl_path_fill_nodes (CoglPath *path, CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglDrawFlags flags) { if (path->data->path_nodes->len == 0) return; /* If the path is a simple rectangle then we can divert to using cogl_framebuffer_draw_rectangle which should be faster because it can go through the journal instead of uploading the geometry just for two triangles */ if (path->data->is_rectangle && flags == 0) { float x_1, y_1, x_2, y_2; _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, x_1, y_1, x_2, y_2); } else { gboolean needs_fallback = FALSE; CoglPrimitive *primitive; _cogl_pipeline_foreach_layer_internal (pipeline, validate_layer_cb, &needs_fallback); if (needs_fallback) { _cogl_path_fill_nodes_with_clipped_rectangle (path, framebuffer, pipeline); return; } primitive = _cogl_path_get_fill_primitive (path); _cogl_primitive_draw (primitive, framebuffer, pipeline, flags); } } void cogl2_path_move_to (CoglPath *path, float x, float y) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); _cogl_path_add_node (path, TRUE, x, y); data = path->data; data->path_start.x = x; data->path_start.y = y; data->path_pen = data->path_start; } void cogl2_path_rel_move_to (CoglPath *path, float x, float y) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); data = path->data; cogl2_path_move_to (path, data->path_pen.x + x, data->path_pen.y + y); } void cogl2_path_line_to (CoglPath *path, float x, float y) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); _cogl_path_add_node (path, FALSE, x, y); data = path->data; data->path_pen.x = x; data->path_pen.y = y; } void cogl2_path_rel_line_to (CoglPath *path, float x, float y) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); data = path->data; cogl2_path_line_to (path, data->path_pen.x + x, data->path_pen.y + y); } void cogl2_path_close (CoglPath *path) { g_return_if_fail (cogl_is_path (path)); _cogl_path_add_node (path, FALSE, path->data->path_start.x, path->data->path_start.y); path->data->path_pen = path->data->path_start; } void cogl2_path_line (CoglPath *path, float x_1, float y_1, float x_2, float y_2) { cogl2_path_move_to (path, x_1, y_1); cogl2_path_line_to (path, x_2, y_2); } void cogl2_path_polyline (CoglPath *path, const float *coords, int num_points) { int c = 0; g_return_if_fail (cogl_is_path (path)); cogl2_path_move_to (path, coords[0], coords[1]); for (c = 1; c < num_points; ++c) cogl2_path_line_to (path, coords[2*c], coords[2*c+1]); } void cogl2_path_polygon (CoglPath *path, const float *coords, int num_points) { cogl2_path_polyline (path, coords, num_points); cogl2_path_close (path); } void cogl2_path_rectangle (CoglPath *path, float x_1, float y_1, float x_2, float y_2) { gboolean is_rectangle; /* If the path was previously empty and the rectangle isn't mirrored then we'll record that this is a simple rectangle path so that we can optimise it */ is_rectangle = (path->data->path_nodes->len == 0 && x_2 >= x_1 && y_2 >= y_1); cogl2_path_move_to (path, x_1, y_1); cogl2_path_line_to (path, x_2, y_1); cogl2_path_line_to (path, x_2, y_2); cogl2_path_line_to (path, x_1, y_2); cogl2_path_close (path); path->data->is_rectangle = is_rectangle; } gboolean _cogl_path_is_rectangle (CoglPath *path) { return path->data->is_rectangle; } static void _cogl_path_arc (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y, float angle_1, float angle_2, float angle_step, unsigned int move_first) { float a = 0x0; float cosa = 0x0; float sina = 0x0; float px = 0x0; float py = 0x0; /* Fix invalid angles */ if (angle_1 == angle_2 || angle_step == 0x0) return; if (angle_step < 0x0) angle_step = -angle_step; /* Walk the arc by given step */ a = angle_1; while (a != angle_2) { cosa = cosf (a * (G_PI/180.0)); sina = sinf (a * (G_PI/180.0)); px = center_x + (cosa * radius_x); py = center_y + (sina * radius_y); if (a == angle_1 && move_first) cogl2_path_move_to (path, px, py); else cogl2_path_line_to (path, px, py); if (G_LIKELY (angle_2 > angle_1)) { a += angle_step; if (a > angle_2) a = angle_2; } else { a -= angle_step; if (a < angle_2) a = angle_2; } } /* Make sure the final point is drawn */ cosa = cosf (angle_2 * (G_PI/180.0)); sina = sinf (angle_2 * (G_PI/180.0)); px = center_x + (cosa * radius_x); py = center_y + (sina * radius_y); cogl2_path_line_to (path, px, py); } void cogl2_path_arc (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y, float angle_1, float angle_2) { float angle_step = 10; g_return_if_fail (cogl_is_path (path)); /* it is documented that a move to is needed to create a freestanding * arc */ _cogl_path_arc (path, center_x, center_y, radius_x, radius_y, angle_1, angle_2, angle_step, 0 /* no move */); } static void _cogl_path_rel_arc (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y, float angle_1, float angle_2, float angle_step) { CoglPathData *data; data = path->data; _cogl_path_arc (path, data->path_pen.x + center_x, data->path_pen.y + center_y, radius_x, radius_y, angle_1, angle_2, angle_step, 0 /* no move */); } void cogl2_path_ellipse (CoglPath *path, float center_x, float center_y, float radius_x, float radius_y) { float angle_step = 10; g_return_if_fail (cogl_is_path (path)); /* FIXME: if shows to be slow might be optimized * by mirroring just a quarter of it */ _cogl_path_arc (path, center_x, center_y, radius_x, radius_y, 0, 360, angle_step, 1 /* move first */); cogl2_path_close (path); } void cogl2_path_round_rectangle (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float radius, float arc_step) { float inner_width = x_2 - x_1 - radius * 2; float inner_height = y_2 - y_1 - radius * 2; g_return_if_fail (cogl_is_path (path)); cogl2_path_move_to (path, x_1, y_1 + radius); _cogl_path_rel_arc (path, radius, 0, radius, radius, 180, 270, arc_step); cogl2_path_line_to (path, path->data->path_pen.x + inner_width, path->data->path_pen.y); _cogl_path_rel_arc (path, 0, radius, radius, radius, -90, 0, arc_step); cogl2_path_line_to (path, path->data->path_pen.x, path->data->path_pen.y + inner_height); _cogl_path_rel_arc (path, -radius, 0, radius, radius, 0, 90, arc_step); cogl2_path_line_to (path, path->data->path_pen.x - inner_width, path->data->path_pen.y); _cogl_path_rel_arc (path, 0, -radius, radius, radius, 90, 180, arc_step); cogl2_path_close (path); } static void _cogl_path_bezier3_sub (CoglPath *path, CoglBezCubic *cubic) { CoglBezCubic cubics[_COGL_MAX_BEZ_RECURSE_DEPTH]; CoglBezCubic *cleft; CoglBezCubic *cright; CoglBezCubic *c; floatVec2 dif1; floatVec2 dif2; floatVec2 mm; floatVec2 c1; floatVec2 c2; floatVec2 c3; floatVec2 c4; floatVec2 c5; int cindex; /* Put first curve on stack */ cubics[0] = *cubic; cindex = 0; while (cindex >= 0) { c = &cubics[cindex]; /* Calculate distance of control points from their * counterparts on the line between end points */ dif1.x = (c->p2.x * 3) - (c->p1.x * 2) - c->p4.x; dif1.y = (c->p2.y * 3) - (c->p1.y * 2) - c->p4.y; dif2.x = (c->p3.x * 3) - (c->p4.x * 2) - c->p1.x; dif2.y = (c->p3.y * 3) - (c->p4.y * 2) - c->p1.y; if (dif1.x < 0) dif1.x = -dif1.x; if (dif1.y < 0) dif1.y = -dif1.y; if (dif2.x < 0) dif2.x = -dif2.x; if (dif2.y < 0) dif2.y = -dif2.y; /* Pick the greatest of two distances */ if (dif1.x < dif2.x) dif1.x = dif2.x; if (dif1.y < dif2.y) dif1.y = dif2.y; /* Cancel if the curve is flat enough */ if (dif1.x + dif1.y <= 1.0 || cindex == _COGL_MAX_BEZ_RECURSE_DEPTH-1) { /* Add subdivision point (skip last) */ if (cindex == 0) return; _cogl_path_add_node (path, FALSE, c->p4.x, c->p4.y); --cindex; continue; } /* Left recursion goes on top of stack! */ cright = c; cleft = &cubics[++cindex]; /* Subdivide into 2 sub-curves */ c1.x = ((c->p1.x + c->p2.x) / 2); c1.y = ((c->p1.y + c->p2.y) / 2); mm.x = ((c->p2.x + c->p3.x) / 2); mm.y = ((c->p2.y + c->p3.y) / 2); c5.x = ((c->p3.x + c->p4.x) / 2); c5.y = ((c->p3.y + c->p4.y) / 2); c2.x = ((c1.x + mm.x) / 2); c2.y = ((c1.y + mm.y) / 2); c4.x = ((mm.x + c5.x) / 2); c4.y = ((mm.y + c5.y) / 2); c3.x = ((c2.x + c4.x) / 2); c3.y = ((c2.y + c4.y) / 2); /* Add left recursion to stack */ cleft->p1 = c->p1; cleft->p2 = c1; cleft->p3 = c2; cleft->p4 = c3; /* Add right recursion to stack */ cright->p1 = c3; cright->p2 = c4; cright->p3 = c5; cright->p4 = c->p4; } } void cogl2_path_curve_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float x_3, float y_3) { CoglBezCubic cubic; g_return_if_fail (cogl_is_path (path)); /* Prepare cubic curve */ cubic.p1 = path->data->path_pen; cubic.p2.x = x_1; cubic.p2.y = y_1; cubic.p3.x = x_2; cubic.p3.y = y_2; cubic.p4.x = x_3; cubic.p4.y = y_3; /* Run subdivision */ _cogl_path_bezier3_sub (path, &cubic); /* Add last point */ _cogl_path_add_node (path, FALSE, cubic.p4.x, cubic.p4.y); path->data->path_pen = cubic.p4; } void cogl2_path_rel_curve_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2, float x_3, float y_3) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); data = path->data; cogl2_path_curve_to (path, data->path_pen.x + x_1, data->path_pen.y + y_1, data->path_pen.x + x_2, data->path_pen.y + y_2, data->path_pen.x + x_3, data->path_pen.y + y_3); } CoglPath * cogl2_path_new (void) { CoglPath *path; CoglPathData *data; _COGL_GET_CONTEXT (ctx, NULL); path = g_slice_new (CoglPath); data = path->data = g_slice_new (CoglPathData); data->ref_count = 1; data->context = ctx; data->fill_rule = COGL_PATH_FILL_RULE_EVEN_ODD; data->path_nodes = g_array_new (FALSE, FALSE, sizeof (CoglPathNode)); data->last_path = 0; data->fill_attribute_buffer = NULL; data->stroke_attribute_buffer = NULL; data->fill_primitive = NULL; data->is_rectangle = FALSE; return _cogl_path_object_new (path); } CoglPath * cogl_path_copy (CoglPath *old_path) { CoglPath *new_path; g_return_val_if_fail (cogl_is_path (old_path), NULL); new_path = g_slice_new (CoglPath); new_path->data = old_path->data; new_path->data->ref_count++; return _cogl_path_object_new (new_path); } static void _cogl_path_free (CoglPath *path) { _cogl_path_data_unref (path->data); g_slice_free (CoglPath, path); } /* If second order beziers were needed the following code could * be re-enabled: */ #if 0 static void _cogl_path_bezier2_sub (CoglPath *path, CoglBezQuad *quad) { CoglBezQuad quads[_COGL_MAX_BEZ_RECURSE_DEPTH]; CoglBezQuad *qleft; CoglBezQuad *qright; CoglBezQuad *q; floatVec2 mid; floatVec2 dif; floatVec2 c1; floatVec2 c2; floatVec2 c3; int qindex; /* Put first curve on stack */ quads[0] = *quad; qindex = 0; /* While stack is not empty */ while (qindex >= 0) { q = &quads[qindex]; /* Calculate distance of control point from its * counterpart on the line between end points */ mid.x = ((q->p1.x + q->p3.x) / 2); mid.y = ((q->p1.y + q->p3.y) / 2); dif.x = (q->p2.x - mid.x); dif.y = (q->p2.y - mid.y); if (dif.x < 0) dif.x = -dif.x; if (dif.y < 0) dif.y = -dif.y; /* Cancel if the curve is flat enough */ if (dif.x + dif.y <= 1.0 || qindex == _COGL_MAX_BEZ_RECURSE_DEPTH - 1) { /* Add subdivision point (skip last) */ if (qindex == 0) return; _cogl_path_add_node (path, FALSE, q->p3.x, q->p3.y); --qindex; continue; } /* Left recursion goes on top of stack! */ qright = q; qleft = &quads[++qindex]; /* Subdivide into 2 sub-curves */ c1.x = ((q->p1.x + q->p2.x) / 2); c1.y = ((q->p1.y + q->p2.y) / 2); c3.x = ((q->p2.x + q->p3.x) / 2); c3.y = ((q->p2.y + q->p3.y) / 2); c2.x = ((c1.x + c3.x) / 2); c2.y = ((c1.y + c3.y) / 2); /* Add left recursion onto stack */ qleft->p1 = q->p1; qleft->p2 = c1; qleft->p3 = c2; /* Add right recursion onto stack */ qright->p1 = c2; qright->p2 = c3; qright->p3 = q->p3; } } void cogl_path_curve2_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2) { CoglBezQuad quad; /* Prepare quadratic curve */ quad.p1 = path->data->path_pen; quad.p2.x = x_1; quad.p2.y = y_1; quad.p3.x = x_2; quad.p3.y = y_2; /* Run subdivision */ _cogl_path_bezier2_sub (&quad); /* Add last point */ _cogl_path_add_node (FALSE, quad.p3.x, quad.p3.y); path->data->path_pen = quad.p3; } void cogl_rel_curve2_to (CoglPath *path, float x_1, float y_1, float x_2, float y_2) { CoglPathData *data; g_return_if_fail (cogl_is_path (path)); data = path->data; cogl_path_curve2_to (data->path_pen.x + x_1, data->path_pen.y + y_1, data->path_pen.x + x_2, data->path_pen.y + y_2); } #endif typedef struct _CoglPathTesselator CoglPathTesselator; typedef struct _CoglPathTesselatorVertex CoglPathTesselatorVertex; struct _CoglPathTesselator { GLUtesselator *glu_tess; GLenum primitive_type; int vertex_number; /* Array of CoglPathTesselatorVertex. This needs to grow when the combine callback is called */ GArray *vertices; /* Array of integers for the indices into the vertices array. Each element will either be uint8_t, uint16_t or uint32_t depending on the number of vertices */ GArray *indices; CoglIndicesType indices_type; /* Indices used to split fans and strips */ int index_a, index_b; }; struct _CoglPathTesselatorVertex { float x, y, s, t; }; static void _cogl_path_tesselator_begin (GLenum type, CoglPathTesselator *tess) { g_assert (type == GL_TRIANGLES || type == GL_TRIANGLE_FAN || type == GL_TRIANGLE_STRIP); tess->primitive_type = type; tess->vertex_number = 0; } static CoglIndicesType _cogl_path_tesselator_get_indices_type_for_size (int n_vertices) { if (n_vertices <= 256) return COGL_INDICES_TYPE_UNSIGNED_BYTE; else if (n_vertices <= 65536) return COGL_INDICES_TYPE_UNSIGNED_SHORT; else return COGL_INDICES_TYPE_UNSIGNED_INT; } static void _cogl_path_tesselator_allocate_indices_array (CoglPathTesselator *tess) { switch (tess->indices_type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: tess->indices = g_array_new (FALSE, FALSE, sizeof (uint8_t)); break; case COGL_INDICES_TYPE_UNSIGNED_SHORT: tess->indices = g_array_new (FALSE, FALSE, sizeof (uint16_t)); break; case COGL_INDICES_TYPE_UNSIGNED_INT: tess->indices = g_array_new (FALSE, FALSE, sizeof (uint32_t)); break; } } static void _cogl_path_tesselator_add_index (CoglPathTesselator *tess, int vertex_index) { switch (tess->indices_type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: { uint8_t val = vertex_index; g_array_append_val (tess->indices, val); } break; case COGL_INDICES_TYPE_UNSIGNED_SHORT: { uint16_t val = vertex_index; g_array_append_val (tess->indices, val); } break; case COGL_INDICES_TYPE_UNSIGNED_INT: { uint32_t val = vertex_index; g_array_append_val (tess->indices, val); } break; } } static void _cogl_path_tesselator_vertex (void *vertex_data, CoglPathTesselator *tess) { int vertex_index; vertex_index = GPOINTER_TO_INT (vertex_data); /* This tries to convert all of the primitives into GL_TRIANGLES with indices to share vertices */ switch (tess->primitive_type) { case GL_TRIANGLES: /* Directly use the vertex */ _cogl_path_tesselator_add_index (tess, vertex_index); break; case GL_TRIANGLE_FAN: if (tess->vertex_number == 0) tess->index_a = vertex_index; else if (tess->vertex_number == 1) tess->index_b = vertex_index; else { /* Create a triangle with the first vertex, the previous vertex and this vertex */ _cogl_path_tesselator_add_index (tess, tess->index_a); _cogl_path_tesselator_add_index (tess, tess->index_b); _cogl_path_tesselator_add_index (tess, vertex_index); /* Next time we will use this vertex as the previous vertex */ tess->index_b = vertex_index; } break; case GL_TRIANGLE_STRIP: if (tess->vertex_number == 0) tess->index_a = vertex_index; else if (tess->vertex_number == 1) tess->index_b = vertex_index; else { _cogl_path_tesselator_add_index (tess, tess->index_a); _cogl_path_tesselator_add_index (tess, tess->index_b); _cogl_path_tesselator_add_index (tess, vertex_index); if (tess->vertex_number & 1) tess->index_b = vertex_index; else tess->index_a = vertex_index; } break; default: g_assert_not_reached (); } tess->vertex_number++; } static void _cogl_path_tesselator_end (CoglPathTesselator *tess) { tess->primitive_type = GL_FALSE; } static void _cogl_path_tesselator_combine (double coords[3], void *vertex_data[4], float weight[4], void **out_data, CoglPathTesselator *tess) { CoglPathTesselatorVertex *vertex; CoglIndicesType new_indices_type; int i; /* Add a new vertex to the array */ g_array_set_size (tess->vertices, tess->vertices->len + 1); vertex = &g_array_index (tess->vertices, CoglPathTesselatorVertex, tess->vertices->len - 1); /* The data is just the index to the vertex */ *out_data = GINT_TO_POINTER (tess->vertices->len - 1); /* Set the coordinates of the new vertex */ vertex->x = coords[0]; vertex->y = coords[1]; /* Generate the texture coordinates as the weighted average of the four incoming coordinates */ vertex->s = 0.0f; vertex->t = 0.0f; for (i = 0; i < 4; i++) { CoglPathTesselatorVertex *old_vertex = &g_array_index (tess->vertices, CoglPathTesselatorVertex, GPOINTER_TO_INT (vertex_data[i])); vertex->s += old_vertex->s * weight[i]; vertex->t += old_vertex->t * weight[i]; } /* Check if we've reached the limit for the data type of our indices */ new_indices_type = _cogl_path_tesselator_get_indices_type_for_size (tess->vertices->len); if (new_indices_type != tess->indices_type) { CoglIndicesType old_indices_type = new_indices_type; GArray *old_vertices = tess->indices; /* Copy the indices to an array of the new type */ tess->indices_type = new_indices_type; _cogl_path_tesselator_allocate_indices_array (tess); switch (old_indices_type) { case COGL_INDICES_TYPE_UNSIGNED_BYTE: for (i = 0; i < old_vertices->len; i++) _cogl_path_tesselator_add_index (tess, g_array_index (old_vertices, uint8_t, i)); break; case COGL_INDICES_TYPE_UNSIGNED_SHORT: for (i = 0; i < old_vertices->len; i++) _cogl_path_tesselator_add_index (tess, g_array_index (old_vertices, uint16_t, i)); break; case COGL_INDICES_TYPE_UNSIGNED_INT: for (i = 0; i < old_vertices->len; i++) _cogl_path_tesselator_add_index (tess, g_array_index (old_vertices, uint32_t, i)); break; } g_array_free (old_vertices, TRUE); } } static void _cogl_path_build_fill_attribute_buffer (CoglPath *path) { CoglPathTesselator tess; unsigned int path_start = 0; CoglPathData *data = path->data; int i; /* If we've already got a vbo then we don't need to do anything */ if (data->fill_attribute_buffer) return; tess.primitive_type = FALSE; /* Generate a vertex for each point on the path */ tess.vertices = g_array_new (FALSE, FALSE, sizeof (CoglPathTesselatorVertex)); g_array_set_size (tess.vertices, data->path_nodes->len); for (i = 0; i < data->path_nodes->len; i++) { CoglPathNode *node = &g_array_index (data->path_nodes, CoglPathNode, i); CoglPathTesselatorVertex *vertex = &g_array_index (tess.vertices, CoglPathTesselatorVertex, i); vertex->x = node->x; vertex->y = node->y; /* Add texture coordinates so that a texture would be drawn to fit the bounding box of the path and then cropped by the path */ if (data->path_nodes_min.x == data->path_nodes_max.x) vertex->s = 0.0f; else vertex->s = ((node->x - data->path_nodes_min.x) / (data->path_nodes_max.x - data->path_nodes_min.x)); if (data->path_nodes_min.y == data->path_nodes_max.y) vertex->t = 0.0f; else vertex->t = ((node->y - data->path_nodes_min.y) / (data->path_nodes_max.y - data->path_nodes_min.y)); } tess.indices_type = _cogl_path_tesselator_get_indices_type_for_size (data->path_nodes->len); _cogl_path_tesselator_allocate_indices_array (&tess); tess.glu_tess = gluNewTess (); if (data->fill_rule == COGL_PATH_FILL_RULE_EVEN_ODD) gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); else gluTessProperty (tess.glu_tess, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); /* All vertices are on the xy-plane */ gluTessNormal (tess.glu_tess, 0.0, 0.0, 1.0); gluTessCallback (tess.glu_tess, GLU_TESS_BEGIN_DATA, (GCallback) _cogl_path_tesselator_begin); gluTessCallback (tess.glu_tess, GLU_TESS_VERTEX_DATA, (GCallback) _cogl_path_tesselator_vertex); gluTessCallback (tess.glu_tess, GLU_TESS_END_DATA, (GCallback) _cogl_path_tesselator_end); gluTessCallback (tess.glu_tess, GLU_TESS_COMBINE_DATA, (GCallback) _cogl_path_tesselator_combine); gluTessBeginPolygon (tess.glu_tess, &tess); while (path_start < data->path_nodes->len) { CoglPathNode *node = &g_array_index (data->path_nodes, CoglPathNode, path_start); gluTessBeginContour (tess.glu_tess); for (i = 0; i < node->path_size; i++) { double vertex[3] = { node[i].x, node[i].y, 0.0 }; gluTessVertex (tess.glu_tess, vertex, GINT_TO_POINTER (i + path_start)); } gluTessEndContour (tess.glu_tess); path_start += node->path_size; } gluTessEndPolygon (tess.glu_tess); gluDeleteTess (tess.glu_tess); data->fill_attribute_buffer = cogl_attribute_buffer_new (data->context, sizeof (CoglPathTesselatorVertex) * tess.vertices->len, tess.vertices->data); g_array_free (tess.vertices, TRUE); data->fill_attributes[0] = cogl_attribute_new (data->fill_attribute_buffer, "cogl_position_in", sizeof (CoglPathTesselatorVertex), G_STRUCT_OFFSET (CoglPathTesselatorVertex, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); data->fill_attributes[1] = cogl_attribute_new (data->fill_attribute_buffer, "cogl_tex_coord0_in", sizeof (CoglPathTesselatorVertex), G_STRUCT_OFFSET (CoglPathTesselatorVertex, s), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); data->fill_vbo_indices = cogl_indices_new (data->context, tess.indices_type, tess.indices->data, tess.indices->len); data->fill_vbo_n_indices = tess.indices->len; g_array_free (tess.indices, TRUE); } static CoglPrimitive * _cogl_path_get_fill_primitive (CoglPath *path) { if (path->data->fill_primitive) return path->data->fill_primitive; _cogl_path_build_fill_attribute_buffer (path); path->data->fill_primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, path->data->fill_vbo_n_indices, path->data->fill_attributes, COGL_PATH_N_ATTRIBUTES); cogl_primitive_set_indices (path->data->fill_primitive, path->data->fill_vbo_indices, path->data->fill_vbo_n_indices); return path->data->fill_primitive; } static CoglClipStack * _cogl_clip_stack_push_from_path (CoglClipStack *stack, CoglPath *path, CoglMatrixEntry *modelview_entry, CoglMatrixEntry *projection_entry, const float *viewport) { float x_1, y_1, x_2, y_2; _cogl_path_get_bounds (path, &x_1, &y_1, &x_2, &y_2); /* If the path is a simple rectangle then we can divert to pushing a rectangle clip instead which usually won't involve the stencil buffer */ if (_cogl_path_is_rectangle (path)) return _cogl_clip_stack_push_rectangle (stack, x_1, y_1, x_2, y_2, modelview_entry, projection_entry, viewport); else { CoglPrimitive *primitive = _cogl_path_get_fill_primitive (path); return _cogl_clip_stack_push_primitive (stack, primitive, x_1, y_1, x_2, y_2, modelview_entry, projection_entry, viewport); } } void cogl_framebuffer_push_path_clip (CoglFramebuffer *framebuffer, CoglPath *path) { CoglMatrixEntry *modelview_entry = _cogl_framebuffer_get_modelview_entry (framebuffer); CoglMatrixEntry *projection_entry = _cogl_framebuffer_get_projection_entry (framebuffer); /* XXX: It would be nicer if we stored the private viewport as a * vec4 so we could avoid this redundant copy. */ float viewport[] = { framebuffer->viewport_x, framebuffer->viewport_y, framebuffer->viewport_width, framebuffer->viewport_height }; framebuffer->clip_stack = _cogl_clip_stack_push_from_path (framebuffer->clip_stack, path, modelview_entry, projection_entry, viewport); if (framebuffer->context->current_draw_buffer == framebuffer) framebuffer->context->current_draw_buffer_changes |= COGL_FRAMEBUFFER_STATE_CLIP; } static void _cogl_path_build_stroke_attribute_buffer (CoglPath *path) { CoglPathData *data = path->data; CoglBuffer *buffer; unsigned int n_attributes = 0; unsigned int path_start; CoglPathNode *node; floatVec2 *buffer_p; unsigned int i; /* If we've already got a cached vbo then we don't need to do anything */ if (data->stroke_attribute_buffer) return; data->stroke_attribute_buffer = cogl_attribute_buffer_new_with_size (data->context, data->path_nodes->len * sizeof (floatVec2)); buffer = COGL_BUFFER (data->stroke_attribute_buffer); buffer_p = _cogl_buffer_map_for_fill_or_fallback (buffer); /* Copy the vertices in and count the number of sub paths. Each sub path will form a separate attribute so we can paint the disjoint line strips */ for (path_start = 0; path_start < data->path_nodes->len; path_start += node->path_size) { node = &g_array_index (data->path_nodes, CoglPathNode, path_start); for (i = 0; i < node->path_size; i++) { buffer_p[path_start + i].x = node[i].x; buffer_p[path_start + i].y = node[i].y; } n_attributes++; } _cogl_buffer_unmap_for_fill_or_fallback (buffer); data->stroke_attributes = g_new (CoglAttribute *, n_attributes); /* Now we can loop the sub paths again to create the attributes */ for (i = 0, path_start = 0; path_start < data->path_nodes->len; i++, path_start += node->path_size) { node = &g_array_index (data->path_nodes, CoglPathNode, path_start); data->stroke_attributes[i] = cogl_attribute_new (data->stroke_attribute_buffer, "cogl_position_in", sizeof (floatVec2), path_start * sizeof (floatVec2), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); } data->stroke_n_attributes = n_attributes; } void cogl_framebuffer_fill_path (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPath *path) { g_return_if_fail (cogl_is_framebuffer (framebuffer)); g_return_if_fail (cogl_is_pipeline (pipeline)); g_return_if_fail (cogl_is_path (path)); _cogl_path_fill_nodes (path, framebuffer, pipeline, 0 /* flags */); } void cogl_framebuffer_stroke_path (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglPath *path) { g_return_if_fail (cogl_is_framebuffer (framebuffer)); g_return_if_fail (cogl_is_pipeline (pipeline)); g_return_if_fail (cogl_is_path (path)); _cogl_path_stroke_nodes (path, framebuffer, pipeline); } muffin-6.4.1/cogl/cogl-path/cogl-path.h0000664000175000017500000000445214723361714016600 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PATH_H__ #define __COGL_PATH_H__ /** * SECTION:cogl-paths * @short_description: Functions for constructing and drawing 2D paths. * * There are two levels on which drawing with cogl-paths can be used. * The highest level functions construct various simple primitive * shapes to be either filled or stroked. Using a lower-level set of * functions more complex and arbitrary paths can be constructed by * concatenating straight line, bezier curve and arc segments. * * When constructing arbitrary paths, the current pen location is * initialized using the move_to command. The subsequent path segments * implicitly use the last pen location as their first vertex and move * the pen location to the last vertex they produce at the end. Also * there are special versions of functions that allow specifying the * vertices of the path segments relative to the last pen location * rather then in the absolute coordinates. */ #include #include #include #include #endif /* __COGL_PATH_H__ */ muffin-6.4.1/cogl/cogl-path/cogl-path-types.h0000664000175000017500000000610114723361714017733 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008,2009,2013 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #if !defined(__COGL_H_INSIDE__) && !defined(COGL_COMPILATION) #error "Only can be included directly." #endif #ifndef __COGL_PATH_TYPES_H__ #define __COGL_PATH_TYPES_H__ #include G_BEGIN_DECLS typedef struct _CoglPath CoglPath; /** * CoglPathFillRule: * @COGL_PATH_FILL_RULE_NON_ZERO: Each time the line crosses an edge of * the path from left to right one is added to a counter and each time * it crosses from right to left the counter is decremented. If the * counter is non-zero then the point will be filled. See . * @COGL_PATH_FILL_RULE_EVEN_ODD: If the line crosses an edge of the * path an odd number of times then the point will filled, otherwise * it won't. See . * * #CoglPathFillRule is used to determine how a path is filled. There * are two options - 'non-zero' and 'even-odd'. To work out whether any * point will be filled imagine drawing an infinetely long line in any * direction from that point. The number of times and the direction * that the edges of the path crosses this line determines whether the * line is filled as described below. Any open sub paths are treated * as if there was an extra line joining the first point and the last * point. * * The default fill rule when creating a path is %COGL_PATH_FILL_RULE_EVEN_ODD. * *
* Example of filling various paths using the non-zero rule * *
* *
* Example of filling various paths using the even-odd rule * *
* * Since: 1.4 */ typedef enum { COGL_PATH_FILL_RULE_NON_ZERO, COGL_PATH_FILL_RULE_EVEN_ODD } CoglPathFillRule; G_END_DECLS #endif /* __COGL_PATH_TYPES_H__ */ muffin-6.4.1/cogl/cogl-path/cogl-path.symbols0000664000175000017500000000232314723361714020034 0ustar fabiofabio/* cogl1-path-functions.h */ cogl_clip_push_from_path cogl_clip_push_from_path_preserve cogl_get_path cogl_is_path cogl_path_arc cogl_path_close cogl_path_copy cogl_path_curve_to cogl_path_ellipse cogl_path_fill cogl_path_fill_preserve cogl_path_get_fill_rule #ifdef COGL_HAS_GTYPE_SUPPORT cogl_path_get_gtype #endif cogl_path_line cogl_path_line_to cogl_path_move_to cogl_path_new cogl_path_polygon cogl_path_polyline cogl_path_rectangle cogl_path_rel_curve_to cogl_path_rel_line_to cogl_path_rel_move_to cogl_path_round_rectangle cogl_path_set_fill_rule cogl_path_stroke cogl_path_stroke_preserve cogl_set_path /* cogl2-path-functions.h */ cogl_framebuffer_fill_path cogl_framebuffer_push_path_clip cogl_framebuffer_stroke_path cogl2_clip_push_from_path cogl2_path_arc cogl2_path_close cogl2_path_curve_to cogl2_path_ellipse cogl2_path_fill cogl2_path_get_fill_rule cogl2_path_line cogl2_path_line_to cogl2_path_move_to cogl2_path_new cogl2_path_polygon cogl2_path_polyline cogl2_path_rectangle cogl2_path_rel_curve_to cogl2_path_rel_line_to cogl2_path_rel_move_to cogl2_path_round_rectangle cogl2_path_set_fill_rule cogl2_path_stroke /* cogl-path-enums.h-contents may change as header is generated */ cogl_path_fill_rule_get_type muffin-6.4.1/cogl/cogl-path/cogl-path-private.h0000664000175000017500000000650514723361714020251 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ #ifndef __COGL_PATH_PRIVATE_H #define __COGL_PATH_PRIVATE_H #include "cogl-object.h" #include "cogl-attribute-private.h" typedef struct _floatVec2 { float x; float y; } floatVec2; typedef struct _CoglPathNode { float x; float y; unsigned int path_size; } CoglPathNode; typedef struct _CoglBezQuad { floatVec2 p1; floatVec2 p2; floatVec2 p3; } CoglBezQuad; typedef struct _CoglBezCubic { floatVec2 p1; floatVec2 p2; floatVec2 p3; floatVec2 p4; } CoglBezCubic; typedef struct _CoglPathData CoglPathData; struct _CoglPath { CoglObject _parent; CoglPathData *data; }; #define COGL_PATH_N_ATTRIBUTES 2 struct _CoglPathData { unsigned int ref_count; CoglContext *context; CoglPathFillRule fill_rule; GArray *path_nodes; floatVec2 path_start; floatVec2 path_pen; unsigned int last_path; floatVec2 path_nodes_min; floatVec2 path_nodes_max; CoglAttributeBuffer *fill_attribute_buffer; CoglIndices *fill_vbo_indices; unsigned int fill_vbo_n_indices; CoglAttribute *fill_attributes[COGL_PATH_N_ATTRIBUTES + 1]; CoglPrimitive *fill_primitive; CoglAttributeBuffer *stroke_attribute_buffer; CoglAttribute **stroke_attributes; unsigned int stroke_n_attributes; /* This is used as an optimisation for when the path contains a single contour specified using cogl2_path_rectangle. Cogl is more optimised to handle rectangles than paths so we can detect this case and divert to the journal or a rectangle clip. If it is TRUE then the entire path can be described by calling _cogl_path_get_bounds */ gboolean is_rectangle; }; void _cogl_add_path_to_stencil_buffer (CoglPath *path, gboolean merge, gboolean need_clear); void _cogl_path_get_bounds (CoglPath *path, float *min_x, float *min_y, float *max_x, float *max_y); gboolean _cogl_path_is_rectangle (CoglPath *path); #endif /* __COGL_PATH_PRIVATE_H */ muffin-6.4.1/cogl/cogl-path/tesselator/0000775000175000017500000000000014723361714016731 5ustar fabiofabiomuffin-6.4.1/cogl/cogl-path/tesselator/render.c0000664000175000017500000003674714723361714020375 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include #include "mesh.h" #include "tess.h" #include "render.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /* This structure remembers the information we need about a primitive * to be able to render it later, once we have determined which * primitive is able to use the most triangles. */ struct FaceCount { long size; /* number of triangles used */ GLUhalfEdge *eStart; /* edge where this primitive starts */ void (*render)(GLUtesselator *, GLUhalfEdge *, long); /* routine to render this primitive */ }; static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ); static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ); static void RenderFan( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *eStart, long size ); static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ); static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *head ); /************************ Strips and Fans decomposition ******************/ /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle * fans, strips, and separate triangles. A substantial effort is made * to use as few rendering primitives as possible (ie. to make the fans * and strips as large as possible). * * The rendering output is provided as callbacks (see the api). */ void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ) { GLUface *f; /* Make a list of separate triangles so we can render them all at once */ tess->lonelyTriList = NULL; for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { f->marked = FALSE; } for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { /* We examine all faces in an arbitrary order. Whenever we find * an unprocessed face F, we output a group of faces including F * whose size is maximum. */ if( f->inside && ! f->marked ) { RenderMaximumFaceGroup( tess, f ); assert( f->marked ); } } if( tess->lonelyTriList != NULL ) { RenderLonelyTriangles( tess, tess->lonelyTriList ); tess->lonelyTriList = NULL; } } static void RenderMaximumFaceGroup( GLUtesselator *tess, GLUface *fOrig ) { /* We want to find the largest triangle fan or strip of unmarked faces * which includes the given face fOrig. There are 3 possible fans * passing through fOrig (one centered at each vertex), and 3 possible * strips (one for each CCW permutation of the vertices). Our strategy * is to try all of these, and take the primitive which uses the most * triangles (a greedy approach). */ GLUhalfEdge *e = fOrig->anEdge; struct FaceCount max, newFace; max.size = 1; max.eStart = e; max.render = &RenderTriangle; if( ! tess->flagBoundary ) { newFace = MaximumFan( e ); if( newFace.size > max.size ) { max = newFace; } newFace = MaximumFan( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } newFace = MaximumFan( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } newFace = MaximumStrip( e ); if( newFace.size > max.size ) { max = newFace; } newFace = MaximumStrip( e->Lnext ); if( newFace.size > max.size ) { max = newFace; } newFace = MaximumStrip( e->Lprev ); if( newFace.size > max.size ) { max = newFace; } } (*(max.render))( tess, max.eStart, max.size ); } /* Macros which keep track of faces we have marked temporarily, and allow * us to backtrack when necessary. With triangle fans, this is not * really necessary, since the only awkward case is a loop of triangles * around a single origin vertex. However with strips the situation is * more complicated, and we need a general tracking method like the * one here. */ #define Marked(f) (! (f)->inside || (f)->marked) #define AddToTrail(f,t) ((f)->trail = (t), (t) = (f), (f)->marked = TRUE) #define FreeTrail(t) do { \ while( (t) != NULL ) { \ (t)->marked = FALSE; t = (t)->trail; \ } \ } while(0) /* absorb trailing semicolon */ static struct FaceCount MaximumFan( GLUhalfEdge *eOrig ) { /* eOrig->Lface is the face we want to render. We want to find the size * of a maximal fan around eOrig->Org. To do this we just walk around * the origin vertex as far as possible in both directions. */ struct FaceCount newFace = { 0, NULL, &RenderFan }; GLUface *trail = NULL; GLUhalfEdge *e; for( e = eOrig; ! Marked( e->Lface ); e = e->Onext ) { AddToTrail( e->Lface, trail ); ++newFace.size; } for( e = eOrig; ! Marked( e->Rface ); e = e->Oprev ) { AddToTrail( e->Rface, trail ); ++newFace.size; } newFace.eStart = e; /*LINTED*/ FreeTrail( trail ); return newFace; } #define IsEven(n) (((n) & 1) == 0) static struct FaceCount MaximumStrip( GLUhalfEdge *eOrig ) { /* Here we are looking for a maximal strip that contains the vertices * eOrig->Org, eOrig->Dst, eOrig->Lnext->Dst (in that order or the * reverse, such that all triangles are oriented CCW). * * Again we walk forward and backward as far as possible. However for * strips there is a twist: to get CCW orientations, there must be * an *even* number of triangles in the strip on one side of eOrig. * We walk the strip starting on a side with an even number of triangles; * if both side have an odd number, we are forced to shorten one side. */ struct FaceCount newFace = { 0, NULL, &RenderStrip }; long headSize = 0, tailSize = 0; GLUface *trail = NULL; GLUhalfEdge *e, *eTail, *eHead; for( e = eOrig; ! Marked( e->Lface ); ++tailSize, e = e->Onext ) { AddToTrail( e->Lface, trail ); ++tailSize; e = e->Dprev; if( Marked( e->Lface )) break; AddToTrail( e->Lface, trail ); } eTail = e; for( e = eOrig; ! Marked( e->Rface ); ++headSize, e = e->Dnext ) { AddToTrail( e->Rface, trail ); ++headSize; e = e->Oprev; if( Marked( e->Rface )) break; AddToTrail( e->Rface, trail ); } eHead = e; newFace.size = tailSize + headSize; if( IsEven( tailSize )) { newFace.eStart = eTail->Sym; } else if( IsEven( headSize )) { newFace.eStart = eHead; } else { /* Both sides have odd length, we must shorten one of them. In fact, * we must start from eHead to guarantee inclusion of eOrig->Lface. */ --newFace.size; newFace.eStart = eHead->Onext; } /*LINTED*/ FreeTrail( trail ); return newFace; } static void RenderTriangle( GLUtesselator *tess, GLUhalfEdge *e, long size ) { /* Just add the triangle to a triangle list, so we can render all * the separate triangles at once. */ assert( size == 1 ); AddToTrail( e->Lface, tess->lonelyTriList ); } static void RenderLonelyTriangles( GLUtesselator *tess, GLUface *f ) { /* Now we render all the separate triangles which could not be * grouped into a triangle fan or strip. */ GLUhalfEdge *e; int newState; int edgeState = -1; /* force edge state output for first vertex */ CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLES ); for( ; f != NULL; f = f->trail ) { /* Loop once for each edge (there will always be 3 edges) */ e = f->anEdge; do { if( tess->flagBoundary ) { /* Set the "edge state" to TRUE just before we output the * first vertex of each edge on the polygon boundary. */ newState = ! e->Rface->inside; if( edgeState != newState ) { edgeState = newState; CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA( edgeState ); } } CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); e = e->Lnext; } while( e != f->anEdge ); } CALL_END_OR_END_DATA(); } static void RenderFan( GLUtesselator *tess, GLUhalfEdge *e, long size ) { /* Render as many CCW triangles as possible in a fan starting from * edge "e". The fan *should* contain exactly "size" triangles * (otherwise we've goofed up somewhere). */ CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_FAN ); CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); while( ! Marked( e->Lface )) { e->Lface->marked = TRUE; --size; e = e->Onext; CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); } assert( size == 0 ); CALL_END_OR_END_DATA(); } static void RenderStrip( GLUtesselator *tess, GLUhalfEdge *e, long size ) { /* Render as many CCW triangles as possible in a strip starting from * edge "e". The strip *should* contain exactly "size" triangles * (otherwise we've goofed up somewhere). */ CALL_BEGIN_OR_BEGIN_DATA( GL_TRIANGLE_STRIP ); CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); while( ! Marked( e->Lface )) { e->Lface->marked = TRUE; --size; e = e->Dprev; CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); if( Marked( e->Lface )) break; e->Lface->marked = TRUE; --size; e = e->Onext; CALL_VERTEX_OR_VERTEX_DATA( e->Dst->data ); } assert( size == 0 ); CALL_END_OR_END_DATA(); } /************************ Boundary contour decomposition ******************/ /* __gl_renderBoundary( tess, mesh ) takes a mesh, and outputs one * contour for each face marked "inside". The rendering output is * provided as callbacks (see the api). */ void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ) { GLUface *f; GLUhalfEdge *e; for( f = mesh->fHead.next; f != &mesh->fHead; f = f->next ) { if( f->inside ) { CALL_BEGIN_OR_BEGIN_DATA( GL_LINE_LOOP ); e = f->anEdge; do { CALL_VERTEX_OR_VERTEX_DATA( e->Org->data ); e = e->Lnext; } while( e != f->anEdge ); CALL_END_OR_END_DATA(); } } } /************************ Quick-and-dirty decomposition ******************/ #define SIGN_INCONSISTENT 2 static int ComputeNormal( GLUtesselator *tess, GLdouble norm[3], int check ) /* * If check==FALSE, we compute the polygon normal and place it in norm[]. * If check==TRUE, we check that each triangle in the fan from v0 has a * consistent orientation with respect to norm[]. If triangles are * consistently oriented CCW, return 1; if CW, return -1; if all triangles * are degenerate return 0; otherwise (no consistent orientation) return * SIGN_INCONSISTENT. */ { CachedVertex *v0 = tess->cache; CachedVertex *vn = v0 + tess->cacheCount; CachedVertex *vc; GLdouble dot, xc, yc, zc, xp, yp, zp, n[3]; int sign = 0; /* Find the polygon normal. It is important to get a reasonable * normal even when the polygon is self-intersecting (eg. a bowtie). * Otherwise, the computed normal could be very tiny, but perpendicular * to the true plane of the polygon due to numerical noise. Then all * the triangles would appear to be degenerate and we would incorrectly * decompose the polygon as a fan (or simply not render it at all). * * We use a sum-of-triangles normal algorithm rather than the more * efficient sum-of-trapezoids method (used in CheckOrientation() * in normal.c). This lets us explicitly reverse the signed area * of some triangles to get a reasonable normal in the self-intersecting * case. */ if( ! check ) { norm[0] = norm[1] = norm[2] = 0.0; } vc = v0 + 1; xc = vc->coords[0] - v0->coords[0]; yc = vc->coords[1] - v0->coords[1]; zc = vc->coords[2] - v0->coords[2]; while( ++vc < vn ) { xp = xc; yp = yc; zp = zc; xc = vc->coords[0] - v0->coords[0]; yc = vc->coords[1] - v0->coords[1]; zc = vc->coords[2] - v0->coords[2]; /* Compute (vp - v0) cross (vc - v0) */ n[0] = yp*zc - zp*yc; n[1] = zp*xc - xp*zc; n[2] = xp*yc - yp*xc; dot = n[0]*norm[0] + n[1]*norm[1] + n[2]*norm[2]; if( ! check ) { /* Reverse the contribution of back-facing triangles to get * a reasonable normal for self-intersecting polygons (see above) */ if( dot >= 0 ) { norm[0] += n[0]; norm[1] += n[1]; norm[2] += n[2]; } else { norm[0] -= n[0]; norm[1] -= n[1]; norm[2] -= n[2]; } } else if( dot != 0 ) { /* Check the new orientation for consistency with previous triangles */ if( dot > 0 ) { if( sign < 0 ) return SIGN_INCONSISTENT; sign = 1; } else { if( sign > 0 ) return SIGN_INCONSISTENT; sign = -1; } } } return sign; } /* __gl_renderCache( tess ) takes a single contour and tries to render it * as a triangle fan. This handles convex polygons, as well as some * non-convex polygons if we get lucky. * * Returns TRUE if the polygon was successfully rendered. The rendering * output is provided as callbacks (see the api). */ GLboolean __gl_renderCache( GLUtesselator *tess ) { CachedVertex *v0 = tess->cache; CachedVertex *vn = v0 + tess->cacheCount; CachedVertex *vc; GLdouble norm[3]; int sign; if( tess->cacheCount < 3 ) { /* Degenerate contour -- no output */ return TRUE; } norm[0] = tess->normal[0]; norm[1] = tess->normal[1]; norm[2] = tess->normal[2]; if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { ComputeNormal( tess, norm, FALSE ); } sign = ComputeNormal( tess, norm, TRUE ); if( sign == SIGN_INCONSISTENT ) { /* Fan triangles did not have a consistent orientation */ return FALSE; } if( sign == 0 ) { /* All triangles were degenerate */ return TRUE; } /* Make sure we do the right thing for each winding rule */ switch( tess->windingRule ) { case GLU_TESS_WINDING_ODD: case GLU_TESS_WINDING_NONZERO: break; case GLU_TESS_WINDING_POSITIVE: if( sign < 0 ) return TRUE; break; case GLU_TESS_WINDING_NEGATIVE: if( sign > 0 ) return TRUE; break; case GLU_TESS_WINDING_ABS_GEQ_TWO: return TRUE; } CALL_BEGIN_OR_BEGIN_DATA( tess->boundaryOnly ? GL_LINE_LOOP : (tess->cacheCount > 3) ? GL_TRIANGLE_FAN : GL_TRIANGLES ); CALL_VERTEX_OR_VERTEX_DATA( v0->data ); if( sign > 0 ) { for( vc = v0+1; vc < vn; ++vc ) { CALL_VERTEX_OR_VERTEX_DATA( vc->data ); } } else { for( vc = vn-1; vc > v0; --vc ) { CALL_VERTEX_OR_VERTEX_DATA( vc->data ); } } CALL_END_OR_END_DATA(); return TRUE; } muffin-6.4.1/cogl/cogl-path/tesselator/dict-list.h0000664000175000017500000000671014723361714021002 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __dict_list_h_ #define __dict_list_h_ /* Use #define's so that another heap implementation can use this one */ #define DictKey DictListKey #define Dict DictList #define DictNode DictListNode #define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) #define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) #define dictSearch(dict,key) __gl_dictListSearch(dict,key) #define dictInsert(dict,key) __gl_dictListInsert(dict,key) #define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) #define dictDelete(dict,node) __gl_dictListDelete(dict,node) #define dictKey(n) __gl_dictListKey(n) #define dictSucc(n) __gl_dictListSucc(n) #define dictPred(n) __gl_dictListPred(n) #define dictMin(d) __gl_dictListMin(d) #define dictMax(d) __gl_dictListMax(d) typedef void *DictKey; typedef struct Dict Dict; typedef struct DictNode DictNode; Dict *dictNewDict( void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) ); void dictDeleteDict( Dict *dict ); /* Search returns the node with the smallest key greater than or equal * to the given key. If there is no such key, returns a node whose * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. */ DictNode *dictSearch( Dict *dict, DictKey key ); DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); void dictDelete( Dict *dict, DictNode *node ); #define __gl_dictListKey(n) ((n)->key) #define __gl_dictListSucc(n) ((n)->next) #define __gl_dictListPred(n) ((n)->prev) #define __gl_dictListMin(d) ((d)->head.next) #define __gl_dictListMax(d) ((d)->head.prev) #define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) /*** Private data structures ***/ struct DictNode { DictKey key; DictNode *next; DictNode *prev; }; struct Dict { DictNode head; void *frame; int (*leq)(void *frame, DictKey key1, DictKey key2); }; #endif muffin-6.4.1/cogl/cogl-path/tesselator/tessmono.c0000664000175000017500000001611614723361714020751 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include "geom.h" #include "mesh.h" #include "tessmono.h" #include #define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ eDst->Sym->winding += eSrc->Sym->winding) /* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region * (what else would it do??) The region must consist of a single * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this * case means that any vertical line intersects the interior of the * region in a single interval. * * Tessellation consists of adding interior edges (actually pairs of * half-edges), to split the region into non-overlapping triangles. * * The basic idea is explained in Preparata and Shamos (which I don''t * have handy right now), although their implementation is more * complicated than this one. The are two edge chains, an upper chain * and a lower chain. We process all vertices from both chains in order, * from right to left. * * The algorithm ensures that the following invariant holds after each * vertex is processed: the untessellated region consists of two * chains, where one chain (say the upper) is a single edge, and * the other chain is concave. The left vertex of the single edge * is always to the left of all vertices in the concave chain. * * Each step consists of adding the rightmost unprocessed vertex to one * of the two chains, and forming a fan of triangles from the rightmost * of two chain endpoints. Determining whether we can add each triangle * to the fan is a simple orientation test. By making the fan as large * as possible, we restore the invariant (check it yourself). */ int __gl_meshTessellateMonoRegion( GLUface *face ) { GLUhalfEdge *up, *lo; /* All edges are oriented CCW around the boundary of the region. * First, find the half-edge whose origin vertex is rightmost. * Since the sweep goes from left to right, face->anEdge should * be close to the edge we want. */ up = face->anEdge; assert( up->Lnext != up && up->Lnext->Lnext != up ); for( ; VertLeq( up->Dst, up->Org ); up = up->Lprev ) ; for( ; VertLeq( up->Org, up->Dst ); up = up->Lnext ) ; lo = up->Lprev; while( up->Lnext != lo ) { if( VertLeq( up->Dst, lo->Org )) { /* up->Dst is on the left. It is safe to form triangles from lo->Org. * The EdgeGoesLeft test guarantees progress even when some triangles * are CW, given that the upper and lower chains are truly monotone. */ while( lo->Lnext != up && (EdgeGoesLeft( lo->Lnext ) || EdgeSign( lo->Org, lo->Dst, lo->Lnext->Dst ) <= 0 )) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); if (tempHalfEdge == NULL) return 0; lo = tempHalfEdge->Sym; } lo = lo->Lprev; } else { /* lo->Org is on the left. We can make CCW triangles from up->Dst. */ while( lo->Lnext != up && (EdgeGoesRight( up->Lprev ) || EdgeSign( up->Dst, up->Org, up->Lprev->Org ) >= 0 )) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( up, up->Lprev ); if (tempHalfEdge == NULL) return 0; up = tempHalfEdge->Sym; } up = up->Lnext; } } /* Now lo->Org == up->Dst == the leftmost vertex. The remaining region * can be tessellated in a fan from this leftmost vertex. */ assert( lo->Lnext != up ); while( lo->Lnext->Lnext != up ) { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( lo->Lnext, lo ); if (tempHalfEdge == NULL) return 0; lo = tempHalfEdge->Sym; } return 1; } /* __gl_meshTessellateInterior( mesh ) tessellates each region of * the mesh which is marked "inside" the polygon. Each such region * must be monotone. */ int __gl_meshTessellateInterior( GLUmesh *mesh ) { GLUface *f, *next; /*LINTED*/ for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { /* Make sure we don''t try to tessellate the new triangles. */ next = f->next; if( f->inside ) { if ( !__gl_meshTessellateMonoRegion( f ) ) return 0; } } return 1; } /* __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces * which are not marked "inside" the polygon. Since further mesh operations * on NULL faces are not allowed, the main purpose is to clean up the * mesh so that exterior loops are not represented in the data structure. */ void __gl_meshDiscardExterior( GLUmesh *mesh ) { GLUface *f, *next; /*LINTED*/ for( f = mesh->fHead.next; f != &mesh->fHead; f = next ) { /* Since f will be destroyed, save its next pointer. */ next = f->next; if( ! f->inside ) { __gl_meshZapFace( f ); } } } #define MARKED_FOR_DELETION 0x7fffffff /* __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the * winding numbers on all edges so that regions marked "inside" the * polygon have a winding number of "value", and regions outside * have a winding number of 0. * * If keepOnlyBoundary is TRUE, it also deletes all edges which do not * separate an interior region from an exterior one. */ int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, GLboolean keepOnlyBoundary ) { GLUhalfEdge *e, *eNext; for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { eNext = e->next; if( e->Rface->inside != e->Lface->inside ) { /* This is a boundary edge (one side is interior, one is exterior). */ e->winding = (e->Lface->inside) ? value : -value; } else { /* Both regions are interior, or both are exterior. */ if( ! keepOnlyBoundary ) { e->winding = 0; } else { if ( !__gl_meshDelete( e ) ) return 0; } } } return 1; } muffin-6.4.1/cogl/cogl-path/tesselator/priorityq-heap.c0000664000175000017500000001462214723361714022057 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include #include #include "priorityq-heap.h" #include "memalloc.h" #define INIT_SIZE 32 #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifdef FOR_TRITE_TEST_PROGRAM #define LEQ(x,y) (*pq->leq)(x,y) #else /* Violates modularity, but a little faster */ #include "geom.h" #define LEQ(x,y) VertLeq((GLUvertex *)x, (GLUvertex *)y) #endif /* really __gl_pqHeapNewPriorityQ */ PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) { PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); if (pq == NULL) return NULL; pq->size = 0; pq->max = INIT_SIZE; pq->nodes = (PQnode *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->nodes[0]) ); if (pq->nodes == NULL) { memFree(pq); return NULL; } pq->handles = (PQhandleElem *)memAlloc( (INIT_SIZE + 1) * sizeof(pq->handles[0]) ); if (pq->handles == NULL) { memFree(pq->nodes); memFree(pq); return NULL; } pq->initialized = FALSE; pq->freeList = 0; pq->leq = leq; pq->nodes[1].handle = 1; /* so that Minimum() returns NULL */ pq->handles[1].key = NULL; return pq; } /* really __gl_pqHeapDeletePriorityQ */ void pqDeletePriorityQ( PriorityQ *pq ) { memFree( pq->handles ); memFree( pq->nodes ); memFree( pq ); } static void FloatDown( PriorityQ *pq, long curr ) { PQnode *n = pq->nodes; PQhandleElem *h = pq->handles; PQhandle hCurr, hChild; long child; hCurr = n[curr].handle; for( ;; ) { child = curr << 1; if( child < pq->size && LEQ( h[n[child+1].handle].key, h[n[child].handle].key )) { ++child; } assert(child <= pq->max); hChild = n[child].handle; if( child > pq->size || LEQ( h[hCurr].key, h[hChild].key )) { n[curr].handle = hCurr; h[hCurr].node = curr; break; } n[curr].handle = hChild; h[hChild].node = curr; curr = child; } } static void FloatUp( PriorityQ *pq, long curr ) { PQnode *n = pq->nodes; PQhandleElem *h = pq->handles; PQhandle hCurr, hParent; long parent; hCurr = n[curr].handle; for( ;; ) { parent = curr >> 1; hParent = n[parent].handle; if( parent == 0 || LEQ( h[hParent].key, h[hCurr].key )) { n[curr].handle = hCurr; h[hCurr].node = curr; break; } n[curr].handle = hParent; h[hParent].node = curr; curr = parent; } } /* really __gl_pqHeapInit */ void pqInit( PriorityQ *pq ) { long i; /* This method of building a heap is O(n), rather than O(n lg n). */ for( i = pq->size; i >= 1; --i ) { FloatDown( pq, i ); } pq->initialized = TRUE; } /* really __gl_pqHeapInsert */ /* returns LONG_MAX iff out of memory */ PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) { long curr; PQhandle free_handle; curr = ++ pq->size; if( (curr*2) > pq->max ) { PQnode *saveNodes= pq->nodes; PQhandleElem *saveHandles= pq->handles; /* If the heap overflows, double its size. */ pq->max <<= 1; pq->nodes = (PQnode *)memRealloc( pq->nodes, (size_t) ((pq->max + 1) * sizeof( pq->nodes[0] ))); if (pq->nodes == NULL) { pq->nodes = saveNodes; /* restore ptr to free upon return */ return LONG_MAX; } pq->handles = (PQhandleElem *)memRealloc( pq->handles, (size_t) ((pq->max + 1) * sizeof( pq->handles[0] ))); if (pq->handles == NULL) { pq->handles = saveHandles; /* restore ptr to free upon return */ return LONG_MAX; } } if( pq->freeList == 0 ) { free_handle = curr; } else { free_handle = pq->freeList; pq->freeList = pq->handles[free_handle].node; } pq->nodes[curr].handle = free_handle; pq->handles[free_handle].node = curr; pq->handles[free_handle].key = keyNew; if( pq->initialized ) { FloatUp( pq, curr ); } assert(free_handle != LONG_MAX); return free_handle; } /* really __gl_pqHeapExtractMin */ PQkey pqExtractMin( PriorityQ *pq ) { PQnode *n = pq->nodes; PQhandleElem *h = pq->handles; PQhandle hMin = n[1].handle; PQkey min = h[hMin].key; if( pq->size > 0 ) { n[1].handle = n[pq->size].handle; h[n[1].handle].node = 1; h[hMin].key = NULL; h[hMin].node = pq->freeList; pq->freeList = hMin; if( -- pq->size > 0 ) { FloatDown( pq, 1 ); } } return min; } /* really __gl_pqHeapDelete */ void pqDelete( PriorityQ *pq, PQhandle hCurr ) { PQnode *n = pq->nodes; PQhandleElem *h = pq->handles; long curr; assert( hCurr >= 1 && hCurr <= pq->max && h[hCurr].key != NULL ); curr = h[hCurr].node; n[curr].handle = n[pq->size].handle; h[n[curr].handle].node = curr; if( curr <= -- pq->size ) { if( curr <= 1 || LEQ( h[n[curr>>1].handle].key, h[n[curr].handle].key )) { FloatDown( pq, curr ); } else { FloatUp( pq, curr ); } } h[hCurr].key = NULL; h[hCurr].node = pq->freeList; pq->freeList = hCurr; } muffin-6.4.1/cogl/cogl-path/tesselator/tesselator.h0000664000175000017500000001205514723361714021272 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * Copyright (C) 2010 Intel Corporation * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ #ifndef __TESSELATOR_H__ #define __TESSELATOR_H__ /* This just includes the defines needed by the tesselator code */ #include "cogl/cogl-defines.h" #include "cogl/cogl-gl-header.h" typedef struct GLUtesselator GLUtesselator; #define GLU_TESS_MAX_COORD 1.0e150 void gluBeginPolygon (GLUtesselator* tess); void gluDeleteTess (GLUtesselator* tess); void gluEndPolygon (GLUtesselator* tess); typedef void (_GLUfuncptr)(void); void gluGetTessProperty (GLUtesselator* tess, GLenum which, double* data); GLUtesselator *gluNewTess (void); void gluNextContour (GLUtesselator* tess, GLenum type); void gluTessBeginContour (GLUtesselator* tess); void gluTessBeginPolygon (GLUtesselator* tess, GLvoid* data); void gluTessCallback (GLUtesselator* tess, GLenum which, _GLUfuncptr CallBackFunc); void gluTessEndContour (GLUtesselator* tess); void gluTessEndPolygon (GLUtesselator* tess); void gluTessNormal (GLUtesselator* tess, double valueX, double valueY, double valueZ); void gluTessProperty (GLUtesselator* tess, GLenum which, double data); void gluTessVertex (GLUtesselator* tess, double *location, GLvoid* data); /* ErrorCode */ #define GLU_INVALID_ENUM 100900 #define GLU_INVALID_VALUE 100901 #define GLU_OUT_OF_MEMORY 100902 /* TessCallback */ #define GLU_TESS_BEGIN 100100 #define GLU_BEGIN 100100 #define GLU_TESS_VERTEX 100101 #define GLU_VERTEX 100101 #define GLU_TESS_END 100102 #define GLU_END 100102 #define GLU_TESS_ERROR 100103 #define GLU_TESS_EDGE_FLAG 100104 #define GLU_EDGE_FLAG 100104 #define GLU_TESS_COMBINE 100105 #define GLU_TESS_BEGIN_DATA 100106 #define GLU_TESS_VERTEX_DATA 100107 #define GLU_TESS_END_DATA 100108 #define GLU_TESS_ERROR_DATA 100109 #define GLU_TESS_EDGE_FLAG_DATA 100110 #define GLU_TESS_COMBINE_DATA 100111 /* TessContour */ #define GLU_CW 100120 #define GLU_CCW 100121 #define GLU_INTERIOR 100122 #define GLU_EXTERIOR 100123 #define GLU_UNKNOWN 100124 /* TessProperty */ #define GLU_TESS_WINDING_RULE 100140 #define GLU_TESS_BOUNDARY_ONLY 100141 #define GLU_TESS_TOLERANCE 100142 /* TessError */ #define GLU_TESS_ERROR1 100151 #define GLU_TESS_ERROR2 100152 #define GLU_TESS_ERROR3 100153 #define GLU_TESS_ERROR4 100154 #define GLU_TESS_ERROR5 100155 #define GLU_TESS_ERROR6 100156 #define GLU_TESS_ERROR7 100157 #define GLU_TESS_ERROR8 100158 #define GLU_TESS_MISSING_BEGIN_POLYGON 100151 #define GLU_TESS_MISSING_BEGIN_CONTOUR 100152 #define GLU_TESS_MISSING_END_POLYGON 100153 #define GLU_TESS_MISSING_END_CONTOUR 100154 #define GLU_TESS_COORD_TOO_LARGE 100155 #define GLU_TESS_NEED_COMBINE_CALLBACK 100156 /* TessWinding */ #define GLU_TESS_WINDING_ODD 100130 #define GLU_TESS_WINDING_NONZERO 100131 #define GLU_TESS_WINDING_POSITIVE 100132 #define GLU_TESS_WINDING_NEGATIVE 100133 #define GLU_TESS_WINDING_ABS_GEQ_TWO 100134 #endif /* __TESSELATOR_H__ */ muffin-6.4.1/cogl/cogl-path/tesselator/mesh.c0000664000175000017500000005350514723361714020041 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include #include "mesh.h" #include "memalloc.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif static GLUvertex *allocVertex(void) { return (GLUvertex *)memAlloc( sizeof( GLUvertex )); } static GLUface *allocFace(void) { return (GLUface *)memAlloc( sizeof( GLUface )); } /************************ Utility Routines ************************/ /* Allocate and free half-edges in pairs for efficiency. * The *only* place that should use this fact is allocation/free. */ typedef struct { GLUhalfEdge e, eSym; } EdgePair; /* MakeEdge creates a new pair of half-edges which form their own loop. * No vertex or face structures are allocated, but these must be assigned * before the current edge operation is completed. */ static GLUhalfEdge *MakeEdge( GLUhalfEdge *eNext ) { GLUhalfEdge *e; GLUhalfEdge *eSym; GLUhalfEdge *ePrev; EdgePair *pair = (EdgePair *)memAlloc( sizeof( EdgePair )); if (pair == NULL) return NULL; e = &pair->e; eSym = &pair->eSym; /* Make sure eNext points to the first edge of the edge pair */ if( eNext->Sym < eNext ) { eNext = eNext->Sym; } /* Insert in circular doubly-linked list before eNext. * Note that the prev pointer is stored in Sym->next. */ ePrev = eNext->Sym->next; eSym->next = ePrev; ePrev->Sym->next = e; e->next = eNext; eNext->Sym->next = eSym; e->Sym = eSym; e->Onext = e; e->Lnext = eSym; e->Org = NULL; e->Lface = NULL; e->winding = 0; e->activeRegion = NULL; eSym->Sym = e; eSym->Onext = eSym; eSym->Lnext = e; eSym->Org = NULL; eSym->Lface = NULL; eSym->winding = 0; eSym->activeRegion = NULL; return e; } /* Splice( a, b ) is best described by the Guibas/Stolfi paper or the * CS348a notes (see mesh.h). Basically it modifies the mesh so that * a->Onext and b->Onext are exchanged. This can have various effects * depending on whether a and b belong to different face or vertex rings. * For more explanation see __gl_meshSplice() below. */ static void Splice( GLUhalfEdge *a, GLUhalfEdge *b ) { GLUhalfEdge *aOnext = a->Onext; GLUhalfEdge *bOnext = b->Onext; aOnext->Sym->Lnext = b; bOnext->Sym->Lnext = a; a->Onext = bOnext; b->Onext = aOnext; } /* MakeVertex( newVertex, eOrig, vNext ) attaches a new vertex and makes it the * origin of all edges in the vertex loop to which eOrig belongs. "vNext" gives * a place to insert the new vertex in the global vertex list. We insert * the new vertex *before* vNext so that algorithms which walk the vertex * list will not see the newly created vertices. */ static void MakeVertex( GLUvertex *newVertex, GLUhalfEdge *eOrig, GLUvertex *vNext ) { GLUhalfEdge *e; GLUvertex *vPrev; GLUvertex *vNew = newVertex; assert(vNew != NULL); /* insert in circular doubly-linked list before vNext */ vPrev = vNext->prev; vNew->prev = vPrev; vPrev->next = vNew; vNew->next = vNext; vNext->prev = vNew; vNew->anEdge = eOrig; vNew->data = NULL; /* leave coords, s, t undefined */ /* fix other edges on this vertex loop */ e = eOrig; do { e->Org = vNew; e = e->Onext; } while( e != eOrig ); } /* MakeFace( newFace, eOrig, fNext ) attaches a new face and makes it the left * face of all edges in the face loop to which eOrig belongs. "fNext" gives * a place to insert the new face in the global face list. We insert * the new face *before* fNext so that algorithms which walk the face * list will not see the newly created faces. */ static void MakeFace( GLUface *newFace, GLUhalfEdge *eOrig, GLUface *fNext ) { GLUhalfEdge *e; GLUface *fPrev; GLUface *fNew = newFace; assert(fNew != NULL); /* insert in circular doubly-linked list before fNext */ fPrev = fNext->prev; fNew->prev = fPrev; fPrev->next = fNew; fNew->next = fNext; fNext->prev = fNew; fNew->anEdge = eOrig; fNew->data = NULL; fNew->trail = NULL; fNew->marked = FALSE; /* The new face is marked "inside" if the old one was. This is a * convenience for the common case where a face has been split in two. */ fNew->inside = fNext->inside; /* fix other edges on this face loop */ e = eOrig; do { e->Lface = fNew; e = e->Lnext; } while( e != eOrig ); } /* KillEdge( eDel ) destroys an edge (the half-edges eDel and eDel->Sym), * and removes from the global edge list. */ static void KillEdge( GLUhalfEdge *eDel ) { GLUhalfEdge *ePrev, *eNext; /* Half-edges are allocated in pairs, see EdgePair above */ if( eDel->Sym < eDel ) { eDel = eDel->Sym; } /* delete from circular doubly-linked list */ eNext = eDel->next; ePrev = eDel->Sym->next; eNext->Sym->next = ePrev; ePrev->Sym->next = eNext; memFree( eDel ); } /* KillVertex( vDel ) destroys a vertex and removes it from the global * vertex list. It updates the vertex loop to point to a given new vertex. */ static void KillVertex( GLUvertex *vDel, GLUvertex *newOrg ) { GLUhalfEdge *e, *eStart = vDel->anEdge; GLUvertex *vPrev, *vNext; /* change the origin of all affected edges */ e = eStart; do { e->Org = newOrg; e = e->Onext; } while( e != eStart ); /* delete from circular doubly-linked list */ vPrev = vDel->prev; vNext = vDel->next; vNext->prev = vPrev; vPrev->next = vNext; memFree( vDel ); } /* KillFace( fDel ) destroys a face and removes it from the global face * list. It updates the face loop to point to a given new face. */ static void KillFace( GLUface *fDel, GLUface *newLface ) { GLUhalfEdge *e, *eStart = fDel->anEdge; GLUface *fPrev, *fNext; /* change the left face of all affected edges */ e = eStart; do { e->Lface = newLface; e = e->Lnext; } while( e != eStart ); /* delete from circular doubly-linked list */ fPrev = fDel->prev; fNext = fDel->next; fNext->prev = fPrev; fPrev->next = fNext; memFree( fDel ); } /****************** Basic Edge Operations **********************/ /* __gl_meshMakeEdge creates one edge, two vertices, and a loop (face). * The loop consists of the two new half-edges. */ GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ) { GLUvertex *newVertex1= allocVertex(); GLUvertex *newVertex2= allocVertex(); GLUface *newFace= allocFace(); GLUhalfEdge *e; /* if any one is null then all get freed */ if (newVertex1 == NULL || newVertex2 == NULL || newFace == NULL) { if (newVertex1 != NULL) memFree(newVertex1); if (newVertex2 != NULL) memFree(newVertex2); if (newFace != NULL) memFree(newFace); return NULL; } e = MakeEdge( &mesh->eHead ); if (e == NULL) { memFree(newVertex1); memFree(newVertex2); memFree(newFace); return NULL; } MakeVertex( newVertex1, e, &mesh->vHead ); MakeVertex( newVertex2, e->Sym, &mesh->vHead ); MakeFace( newFace, e, &mesh->fHead ); return e; } /* __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the * mesh connectivity and topology. It changes the mesh so that * eOrg->Onext <- OLD( eDst->Onext ) * eDst->Onext <- OLD( eOrg->Onext ) * where OLD(...) means the value before the meshSplice operation. * * This can have two effects on the vertex structure: * - if eOrg->Org != eDst->Org, the two vertices are merged together * - if eOrg->Org == eDst->Org, the origin is split into two vertices * In both cases, eDst->Org is changed and eOrg->Org is untouched. * * Similarly (and independently) for the face structure, * - if eOrg->Lface == eDst->Lface, one loop is split into two * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. * * Some special cases: * If eDst == eOrg, the operation has no effect. * If eDst == eOrg->Lnext, the new face will have a single edge. * If eDst == eOrg->Lprev, the old face will have a single edge. * If eDst == eOrg->Onext, the new vertex will have a single edge. * If eDst == eOrg->Oprev, the old vertex will have a single edge. */ int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) { int joiningLoops = FALSE; int joiningVertices = FALSE; if( eOrg == eDst ) return 1; if( eDst->Org != eOrg->Org ) { /* We are merging two disjoint vertices -- destroy eDst->Org */ joiningVertices = TRUE; KillVertex( eDst->Org, eOrg->Org ); } if( eDst->Lface != eOrg->Lface ) { /* We are connecting two disjoint loops -- destroy eDst->Lface */ joiningLoops = TRUE; KillFace( eDst->Lface, eOrg->Lface ); } /* Change the edge structure */ Splice( eDst, eOrg ); if( ! joiningVertices ) { GLUvertex *newVertex= allocVertex(); if (newVertex == NULL) return 0; /* We split one vertex into two -- the new vertex is eDst->Org. * Make sure the old vertex points to a valid half-edge. */ MakeVertex( newVertex, eDst, eOrg->Org ); eOrg->Org->anEdge = eOrg; } if( ! joiningLoops ) { GLUface *newFace= allocFace(); if (newFace == NULL) return 0; /* We split one loop into two -- the new loop is eDst->Lface. * Make sure the old face points to a valid half-edge. */ MakeFace( newFace, eDst, eOrg->Lface ); eOrg->Lface->anEdge = eOrg; } return 1; } /* __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; * the newly created loop will contain eDel->Dst. If the deletion of eDel * would create isolated vertices, those are deleted as well. * * This function could be implemented as two calls to __gl_meshSplice * plus a few calls to memFree, but this would allocate and delete * unnecessary vertices and faces. */ int __gl_meshDelete( GLUhalfEdge *eDel ) { GLUhalfEdge *eDelSym = eDel->Sym; int joiningLoops = FALSE; /* First step: disconnect the origin vertex eDel->Org. We make all * changes to get a consistent mesh in this "intermediate" state. */ if( eDel->Lface != eDel->Rface ) { /* We are joining two loops into one -- remove the left face */ joiningLoops = TRUE; KillFace( eDel->Lface, eDel->Rface ); } if( eDel->Onext == eDel ) { KillVertex( eDel->Org, NULL ); } else { /* Make sure that eDel->Org and eDel->Rface point to valid half-edges */ eDel->Rface->anEdge = eDel->Oprev; eDel->Org->anEdge = eDel->Onext; Splice( eDel, eDel->Oprev ); if( ! joiningLoops ) { GLUface *newFace= allocFace(); if (newFace == NULL) return 0; /* We are splitting one loop into two -- create a new loop for eDel. */ MakeFace( newFace, eDel, eDel->Lface ); } } /* Claim: the mesh is now in a consistent state, except that eDel->Org * may have been deleted. Now we disconnect eDel->Dst. */ if( eDelSym->Onext == eDelSym ) { KillVertex( eDelSym->Org, NULL ); KillFace( eDelSym->Lface, NULL ); } else { /* Make sure that eDel->Dst and eDel->Lface point to valid half-edges */ eDel->Lface->anEdge = eDelSym->Oprev; eDelSym->Org->anEdge = eDelSym->Onext; Splice( eDelSym, eDelSym->Oprev ); } /* Any isolated vertices or faces have already been freed. */ KillEdge( eDel ); return 1; } /******************** Other Edge Operations **********************/ /* All these routines can be implemented with the basic edge * operations above. They are provided for convenience and efficiency. */ /* __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. * eOrg and eNew will have the same left face. */ GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ) { GLUhalfEdge *eNewSym; GLUhalfEdge *eNew = MakeEdge( eOrg ); if (eNew == NULL) return NULL; eNewSym = eNew->Sym; /* Connect the new edge appropriately */ Splice( eNew, eOrg->Lnext ); /* Set the vertex and face information */ eNew->Org = eOrg->Dst; { GLUvertex *newVertex= allocVertex(); if (newVertex == NULL) return NULL; MakeVertex( newVertex, eNewSym, eNew->Org ); } eNew->Lface = eNewSym->Lface = eOrg->Lface; return eNew; } /* __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. * eOrg and eNew will have the same left face. */ GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ) { GLUhalfEdge *eNew; GLUhalfEdge *tempHalfEdge= __gl_meshAddEdgeVertex( eOrg ); if (tempHalfEdge == NULL) return NULL; eNew = tempHalfEdge->Sym; /* Disconnect eOrg from eOrg->Dst and connect it to eNew->Org */ Splice( eOrg->Sym, eOrg->Sym->Oprev ); Splice( eOrg->Sym, eNew ); /* Set the vertex and face information */ eOrg->Dst = eNew->Org; eNew->Dst->anEdge = eNew->Sym; /* may have pointed to eOrg->Sym */ eNew->Rface = eOrg->Rface; eNew->winding = eOrg->winding; /* copy old winding information */ eNew->Sym->winding = eOrg->Sym->winding; return eNew; } /* __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst * to eDst->Org, and returns the corresponding half-edge eNew. * If eOrg->Lface == eDst->Lface, this splits one loop into two, * and the newly created loop is eNew->Lface. Otherwise, two disjoint * loops are merged into one, and the loop eDst->Lface is destroyed. * * If (eOrg == eDst), the new face will have only two edges. * If (eOrg->Lnext == eDst), the old face is reduced to a single edge. * If (eOrg->Lnext->Lnext == eDst), the old face is reduced to two edges. */ GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ) { GLUhalfEdge *eNewSym; int joiningLoops = FALSE; GLUhalfEdge *eNew = MakeEdge( eOrg ); if (eNew == NULL) return NULL; eNewSym = eNew->Sym; if( eDst->Lface != eOrg->Lface ) { /* We are connecting two disjoint loops -- destroy eDst->Lface */ joiningLoops = TRUE; KillFace( eDst->Lface, eOrg->Lface ); } /* Connect the new edge appropriately */ Splice( eNew, eOrg->Lnext ); Splice( eNewSym, eDst ); /* Set the vertex and face information */ eNew->Org = eOrg->Dst; eNewSym->Org = eDst->Org; eNew->Lface = eNewSym->Lface = eOrg->Lface; /* Make sure the old face points to a valid half-edge */ eOrg->Lface->anEdge = eNewSym; if( ! joiningLoops ) { GLUface *newFace= allocFace(); if (newFace == NULL) return NULL; /* We split one loop into two -- the new loop is eNew->Lface */ MakeFace( newFace, eNew, eOrg->Lface ); } return eNew; } /******************** Other Operations **********************/ /* __gl_meshZapFace( fZap ) destroys a face and removes it from the * global face list. All edges of fZap will have a NULL pointer as their * left face. Any edges which also have a NULL pointer as their right face * are deleted entirely (along with any isolated vertices this produces). * An entire mesh can be deleted by zapping its faces, one at a time, * in any order. Zapped faces cannot be used in further mesh operations! */ void __gl_meshZapFace( GLUface *fZap ) { GLUhalfEdge *eStart = fZap->anEdge; GLUhalfEdge *e, *eNext, *eSym; GLUface *fPrev, *fNext; /* walk around face, deleting edges whose right face is also NULL */ eNext = eStart->Lnext; do { e = eNext; eNext = e->Lnext; e->Lface = NULL; if( e->Rface == NULL ) { /* delete the edge -- see __gl_MeshDelete above */ if( e->Onext == e ) { KillVertex( e->Org, NULL ); } else { /* Make sure that e->Org points to a valid half-edge */ e->Org->anEdge = e->Onext; Splice( e, e->Oprev ); } eSym = e->Sym; if( eSym->Onext == eSym ) { KillVertex( eSym->Org, NULL ); } else { /* Make sure that eSym->Org points to a valid half-edge */ eSym->Org->anEdge = eSym->Onext; Splice( eSym, eSym->Oprev ); } KillEdge( e ); } } while( e != eStart ); /* delete from circular doubly-linked list */ fPrev = fZap->prev; fNext = fZap->next; fNext->prev = fPrev; fPrev->next = fNext; memFree( fZap ); } /* __gl_meshNewMesh() creates a new mesh with no edges, no vertices, * and no loops (what we usually call a "face"). */ GLUmesh *__gl_meshNewMesh( void ) { GLUvertex *v; GLUface *f; GLUhalfEdge *e; GLUhalfEdge *eSym; GLUmesh *mesh = (GLUmesh *)memAlloc( sizeof( GLUmesh )); if (mesh == NULL) { return NULL; } v = &mesh->vHead; f = &mesh->fHead; e = &mesh->eHead; eSym = &mesh->eHeadSym; v->next = v->prev = v; v->anEdge = NULL; v->data = NULL; f->next = f->prev = f; f->anEdge = NULL; f->data = NULL; f->trail = NULL; f->marked = FALSE; f->inside = FALSE; e->next = e; e->Sym = eSym; e->Onext = NULL; e->Lnext = NULL; e->Org = NULL; e->Lface = NULL; e->winding = 0; e->activeRegion = NULL; eSym->next = eSym; eSym->Sym = e; eSym->Onext = NULL; eSym->Lnext = NULL; eSym->Org = NULL; eSym->Lface = NULL; eSym->winding = 0; eSym->activeRegion = NULL; return mesh; } /* __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in * both meshes, and returns the new mesh (the old meshes are destroyed). */ GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ) { GLUface *f1 = &mesh1->fHead; GLUvertex *v1 = &mesh1->vHead; GLUhalfEdge *e1 = &mesh1->eHead; GLUface *f2 = &mesh2->fHead; GLUvertex *v2 = &mesh2->vHead; GLUhalfEdge *e2 = &mesh2->eHead; /* Add the faces, vertices, and edges of mesh2 to those of mesh1 */ if( f2->next != f2 ) { f1->prev->next = f2->next; f2->next->prev = f1->prev; f2->prev->next = f1; f1->prev = f2->prev; } if( v2->next != v2 ) { v1->prev->next = v2->next; v2->next->prev = v1->prev; v2->prev->next = v1; v1->prev = v2->prev; } if( e2->next != e2 ) { e1->Sym->next->Sym->next = e2->next; e2->next->Sym->next = e1->Sym->next; e2->Sym->next->Sym->next = e1; e1->Sym->next = e2->Sym->next; } memFree( mesh2 ); return mesh1; } #ifdef DELETE_BY_ZAPPING /* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. */ void __gl_meshDeleteMesh( GLUmesh *mesh ) { GLUface *fHead = &mesh->fHead; while( fHead->next != fHead ) { __gl_meshZapFace( fHead->next ); } assert( mesh->vHead.next == &mesh->vHead ); memFree( mesh ); } #else /* __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. */ void __gl_meshDeleteMesh( GLUmesh *mesh ) { GLUface *f, *fNext; GLUvertex *v, *vNext; GLUhalfEdge *e, *eNext; for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { fNext = f->next; memFree( f ); } for( v = mesh->vHead.next; v != &mesh->vHead; v = vNext ) { vNext = v->next; memFree( v ); } for( e = mesh->eHead.next; e != &mesh->eHead; e = eNext ) { /* One call frees both e and e->Sym (see EdgePair above) */ eNext = e->next; memFree( e ); } memFree( mesh ); } #endif #ifndef NDEBUG /* __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. */ void __gl_meshCheckMesh( GLUmesh *mesh ) { GLUface *fHead = &mesh->fHead; GLUvertex *vHead = &mesh->vHead; GLUhalfEdge *eHead = &mesh->eHead; GLUface *f, *fPrev; GLUvertex *v, *vPrev; GLUhalfEdge *e, *ePrev; fPrev = fHead; for( fPrev = fHead ; (f = fPrev->next) != fHead; fPrev = f) { assert( f->prev == fPrev ); e = f->anEdge; do { assert( e->Sym != e ); assert( e->Sym->Sym == e ); assert( e->Lnext->Onext->Sym == e ); assert( e->Onext->Sym->Lnext == e ); assert( e->Lface == f ); e = e->Lnext; } while( e != f->anEdge ); } assert( f->prev == fPrev && f->anEdge == NULL && f->data == NULL ); vPrev = vHead; for( vPrev = vHead ; (v = vPrev->next) != vHead; vPrev = v) { assert( v->prev == vPrev ); e = v->anEdge; do { assert( e->Sym != e ); assert( e->Sym->Sym == e ); assert( e->Lnext->Onext->Sym == e ); assert( e->Onext->Sym->Lnext == e ); assert( e->Org == v ); e = e->Onext; } while( e != v->anEdge ); } assert( v->prev == vPrev && v->anEdge == NULL && v->data == NULL ); ePrev = eHead; for( ePrev = eHead ; (e = ePrev->next) != eHead; ePrev = e) { assert( e->Sym->next == ePrev->Sym ); assert( e->Sym != e ); assert( e->Sym->Sym == e ); assert( e->Org != NULL ); assert( e->Dst != NULL ); assert( e->Lnext->Onext->Sym == e ); assert( e->Onext->Sym->Lnext == e ); } assert( e->Sym->next == ePrev->Sym && e->Sym == &mesh->eHeadSym && e->Sym->Sym == e && e->Org == NULL && e->Dst == NULL && e->Lface == NULL && e->Rface == NULL ); } #endif muffin-6.4.1/cogl/cogl-path/tesselator/gluos.h0000664000175000017500000000007514723361714020235 0ustar fabiofabio/* This is a stub header to avoid having to change tess.c */ muffin-6.4.1/cogl/cogl-path/tesselator/normal.h0000664000175000017500000000352014723361714020372 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __normal_h_ #define __normal_h_ #include "tess.h" #include "tesselator.h" /* __gl_projectPolygon( tess ) determines the polygon normal * and project vertices onto the plane of the polygon. */ void __gl_projectPolygon( GLUtesselator *tess ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/README0000664000175000017500000004674314723361714017627 0ustar fabiofabio/* */ General Polygon Tesselation --------------------------- This note describes a tesselator for polygons consisting of one or more closed contours. It is backward-compatible with the current OpenGL Utilities tesselator, and is intended to replace it. Here is a summary of the major differences: - input contours can be intersecting, self-intersecting, or degenerate. - supports a choice of several winding rules for determining which parts of the polygon are on the "interior". This makes it possible to do CSG operations on polygons. - boundary extraction: instead of tesselating the polygon, returns a set of closed contours which separate the interior from the exterior. - returns the output as a small number of triangle fans and strips, rather than a list of independent triangles (when possible). - output is available as an explicit mesh (a quad-edge structure), in addition to the normal callback interface. - the algorithm used is extremely robust. The interface ------------- The tesselator state is maintained in a "tesselator object". These are allocated and destroyed using GLUtesselator *gluNewTess( void ); void gluDeleteTess( GLUtesselator *tess ); Several tesselator objects may be used simultaneously. Inputs ------ The input contours are specified with the following routines: void gluTessBeginPolygon( GLUtesselator *tess ); void gluTessBeginContour( GLUtesselator *tess ); void gluTessVertex( GLUtesselator *tess, GLUcoord coords[3], void *data ); void gluTessEndContour( GLUtesselator *tess ); void gluTessEndPolygon( GLUtesselator *tess ); Within each BeginPolygon/EndPolygon pair, there can be zero or more calls to BeginContour/EndContour. Within each contour, there are zero or more calls to gluTessVertex(). The vertices specify a closed contour (the last vertex of each contour is automatically linked to the first). "coords" give the coordinates of the vertex in 3-space. For useful results, all vertices should lie in some plane, since the vertices are projected onto a plane before tesselation. "data" is a pointer to a user-defined vertex structure, which typically contains other information such as color, texture coordinates, normal, etc. It is used to refer to the vertex during rendering. The library can be compiled in single- or double-precision; the type GLUcoord represents either "float" or "double" accordingly. The GLU version will be available in double-precision only. Compile with GLU_TESS_API_FLOAT defined to get the single-precision version. When EndPolygon is called, the tesselation algorithm determines which regions are interior to the given contours, according to one of several "winding rules" described below. The interior regions are then tesselated, and the output is provided as callbacks. Rendering Callbacks ------------------- Callbacks are specified by the client using void gluTessCallback( GLUtesselator *tess, GLenum which, void (*fn)()); If "fn" is NULL, any previously defined callback is discarded. The callbacks used to provide output are: /* which == */ void begin( GLenum type ); /* GLU_TESS_BEGIN */ void edgeFlag( GLboolean flag ); /* GLU_TESS_EDGE_FLAG */ void vertex( void *data ); /* GLU_TESS_VERTEX */ void end( void ); /* GLU_TESS_END */ Any of the callbacks may be left undefined; if so, the corresponding information will not be supplied during rendering. The "begin" callback indicates the start of a primitive; type is one of GL_TRIANGLE_STRIP, GL_TRIANGLE_FAN, or GL_TRIANGLES (but see the notes on "boundary extraction" below). It is followed by any number of "vertex" callbacks, which supply the vertices in the same order as expected by the corresponding glBegin() call. After the last vertex of a given primitive, there is a callback to "end". If the "edgeFlag" callback is provided, no triangle fans or strips will be used. When edgeFlag is called, if "flag" is GL_TRUE then each vertex which follows begins an edge which lies on the polygon boundary (ie. an edge which separates an interior region from an exterior one). If "flag" is GL_FALSE, each vertex which follows begins an edge which lies in the polygon interior. "edgeFlag" will be called before the first call to "vertex". Other Callbacks --------------- void mesh( GLUmesh *mesh ); /* GLU_TESS_MESH */ - Returns an explicit mesh, represented using the quad-edge structure (Guibas/Stolfi '85). Other implementations of this interface might use a different mesh structure, so this is available only only as an SGI extension. When the mesh is no longer needed, it should be freed using void gluDeleteMesh( GLUmesh *mesh ); There is a brief description of this data structure in the include file "mesh.h". For the full details, see L. Guibas and J. Stolfi, Primitives for the manipulation of general subdivisions and the computation of Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985. For an introduction, see the course notes for CS348a, "Mathematical Foundations of Computer Graphics", available at the Stanford bookstore (and taught during the fall quarter). void error( GLenum errno ); /* GLU_TESS_ERROR */ - errno is one of GLU_TESS_MISSING_BEGIN_POLYGON, GLU_TESS_MISSING_END_POLYGON, GLU_TESS_MISSING_BEGIN_CONTOUR, GLU_TESS_MISSING_END_CONTOUR, GLU_TESS_COORD_TOO_LARGE, GLU_TESS_NEED_COMBINE_CALLBACK The first four are obvious. The interface recovers from these errors by inserting the missing call(s). GLU_TESS_COORD_TOO_LARGE says that some vertex coordinate exceeded the predefined constant GLU_TESS_MAX_COORD in absolute value, and that the value has been clamped. (Coordinate values must be small enough so that two can be multiplied together without overflow.) GLU_TESS_NEED_COMBINE_CALLBACK says that the algorithm detected an intersection between two edges in the input data, and the "combine" callback (below) was not provided. No output will be generated. void combine( GLUcoord coords[3], void *data[4], /* GLU_TESS_COMBINE */ GLUcoord weight[4], void **outData ); - When the algorithm detects an intersection, or wishes to merge features, it needs to create a new vertex. The vertex is defined as a linear combination of up to 4 existing vertices, referenced by data[0..3]. The coefficients of the linear combination are given by weight[0..3]; these weights always sum to 1.0. All vertex pointers are valid even when some of the weights are zero. "coords" gives the location of the new vertex. The user must allocate another vertex, interpolate parameters using "data" and "weights", and return the new vertex pointer in "outData". This handle is supplied during rendering callbacks. For example, if the polygon lies in an arbitrary plane in 3-space, and we associate a color with each vertex, the combine callback might look like this: void myCombine( GLUcoord coords[3], VERTEX *d[4], GLUcoord w[4], VERTEX **dataOut ) { VERTEX *new = new_vertex(); new->x = coords[0]; new->y = coords[1]; new->z = coords[2]; new->r = w[0]*d[0]->r + w[1]*d[1]->r + w[2]*d[2]->r + w[3]*d[3]->r; new->g = w[0]*d[0]->g + w[1]*d[1]->g + w[2]*d[2]->g + w[3]*d[3]->g; new->b = w[0]*d[0]->b + w[1]*d[1]->b + w[2]*d[2]->b + w[3]*d[3]->b; new->a = w[0]*d[0]->a + w[1]*d[1]->a + w[2]*d[2]->a + w[3]*d[3]->a; *dataOut = new; } If the algorithm detects an intersection, then the "combine" callback must be defined, and must write a non-NULL pointer into "dataOut". Otherwise the GLU_TESS_NEED_COMBINE_CALLBACK error occurs, and no output is generated. This is the only error that can occur during tesselation and rendering. Control over Tesselation ------------------------ void gluTessProperty( GLUtesselator *tess, GLenum which, GLUcoord value ); Properties defined: - GLU_TESS_WINDING_RULE. Possible values: GLU_TESS_WINDING_ODD GLU_TESS_WINDING_NONZERO GLU_TESS_WINDING_POSITIVE GLU_TESS_WINDING_NEGATIVE GLU_TESS_WINDING_ABS_GEQ_TWO The input contours parition the plane into regions. A winding rule determines which of these regions are inside the polygon. For a single contour C, the winding number of a point x is simply the signed number of revolutions we make around x as we travel once around C (where CCW is positive). When there are several contours, the individual winding numbers are summed. This procedure associates a signed integer value with each point x in the plane. Note that the winding number is the same for all points in a single region. The winding rule classifies a region as "inside" if its winding number belongs to the chosen category (odd, nonzero, positive, negative, or absolute value of at least two). The current GLU tesselator implements the "odd" rule. The "nonzero" rule is another common way to define the interior. The other three rules are useful for polygon CSG operations (see below). - GLU_TESS_BOUNDARY_ONLY. Values: TRUE (non-zero) or FALSE (zero). If TRUE, returns a set of closed contours which separate the polygon interior and exterior (rather than a tesselation). Exterior contours are oriented CCW with respect to the normal, interior contours are oriented CW. The GLU_TESS_BEGIN callback uses the type GL_LINE_LOOP for each contour. - GLU_TESS_TOLERANCE. Value: a real number between 0.0 and 1.0. This specifies a tolerance for merging features to reduce the size of the output. For example, two vertices which are very close to each other might be replaced by a single vertex. The tolerance is multiplied by the largest coordinate magnitude of any input vertex; this specifies the maximum distance that any feature can move as the result of a single merge operation. If a single feature takes part in several merge operations, the total distance moved could be larger. Feature merging is completely optional; the tolerance is only a hint. The implementation is free to merge in some cases and not in others, or to never merge features at all. The default tolerance is zero. The current implementation merges vertices only if they are exactly coincident, regardless of the current tolerance. A vertex is spliced into an edge only if the implementation is unable to distinguish which side of the edge the vertex lies on. Two edges are merged only when both endpoints are identical. void gluTessNormal( GLUtesselator *tess, GLUcoord x, GLUcoord y, GLUcoord z ) - Lets the user supply the polygon normal, if known. All input data is projected into a plane perpendicular to the normal before tesselation. All output triangles are oriented CCW with respect to the normal (CW orientation can be obtained by reversing the sign of the supplied normal). For example, if you know that all polygons lie in the x-y plane, call "gluTessNormal(tess, 0.0, 0.0, 1.0)" before rendering any polygons. - If the supplied normal is (0,0,0) (the default value), the normal is determined as follows. The direction of the normal, up to its sign, is found by fitting a plane to the vertices, without regard to how the vertices are connected. It is expected that the input data lies approximately in plane; otherwise projection perpendicular to the computed normal may substantially change the geometry. The sign of the normal is chosen so that the sum of the signed areas of all input contours is non-negative (where a CCW contour has positive area). - The supplied normal persists until it is changed by another call to gluTessNormal. Backward compatibility with the GLU tesselator ---------------------------------------------- The preferred interface is the one described above. The following routines are obsolete, and are provided only for backward compatibility: typedef GLUtesselator GLUtriangulatorObj; /* obsolete name */ void gluBeginPolygon( GLUtesselator *tess ); void gluNextContour( GLUtesselator *tess, GLenum type ); void gluEndPolygon( GLUtesselator *tess ); "type" is one of GLU_EXTERIOR, GLU_INTERIOR, GLU_CCW, GLU_CW, or GLU_UNKNOWN. It is ignored by the current GLU tesselator. GLU_BEGIN, GLU_VERTEX, GLU_END, GLU_ERROR, and GLU_EDGE_FLAG are defined as synonyms for GLU_TESS_BEGIN, GLU_TESS_VERTEX, GLU_TESS_END, GLU_TESS_ERROR, and GLU_TESS_EDGE_FLAG. Polygon CSG operations ---------------------- The features of the tesselator make it easy to find the union, difference, or intersection of several polygons. First, assume that each polygon is defined so that the winding number is 0 for each exterior region, and 1 for each interior region. Under this model, CCW contours define the outer boundary of the polygon, and CW contours define holes. Contours may be nested, but a nested contour must be oriented oppositely from the contour that contains it. If the original polygons do not satisfy this description, they can be converted to this form by first running the tesselator with the GLU_TESS_BOUNDARY_ONLY property turned on. This returns a list of contours satisfying the restriction above. By allocating two tesselator objects, the callbacks from one tesselator can be fed directly to the input of another. Given two or more polygons of the form above, CSG operations can be implemented as follows: Union Draw all the input contours as a single polygon. The winding number of each resulting region is the number of original polygons which cover it. The union can be extracted using the GLU_TESS_WINDING_NONZERO or GLU_TESS_WINDING_POSITIVE winding rules. Note that with the nonzero rule, we would get the same result if all contour orientations were reversed. Intersection (two polygons at a time only) Draw a single polygon using the contours from both input polygons. Extract the result using GLU_TESS_WINDING_ABS_GEQ_TWO. (Since this winding rule looks at the absolute value, reversing all contour orientations does not change the result.) Difference Suppose we want to compute A \ (B union C union D). Draw a single polygon consisting of the unmodified contours from A, followed by the contours of B,C,D with the vertex order reversed (this changes the winding number of the interior regions to -1). To extract the result, use the GLU_TESS_WINDING_POSITIVE rule. If B,C,D are the result of a GLU_TESS_BOUNDARY_ONLY call, an alternative to reversing the vertex order is to reverse the sign of the supplied normal. For example in the x-y plane, call gluTessNormal( tess, 0.0, 0.0, -1.0 ). Performance ----------- The tesselator is not intended for immediate-mode rendering; when possible the output should be cached in a user structure or display list. General polygon tesselation is an inherently difficult problem, especially given the goal of extreme robustness. The implementation makes an effort to output a small number of fans and strips; this should improve the rendering performance when the output is used in a display list. Single-contour input polygons are first tested to see whether they can be rendered as a triangle fan with respect to the first vertex (to avoid running the full decomposition algorithm on convex polygons). Non-convex polygons may be rendered by this "fast path" as well, if the algorithm gets lucky in its choice of a starting vertex. For best performance follow these guidelines: - supply the polygon normal, if available, using gluTessNormal(). This represents about 10% of the computation time. For example, if all polygons lie in the x-y plane, use gluTessNormal(tess,0,0,1). - render many polygons using the same tesselator object, rather than allocating a new tesselator for each one. (In a multi-threaded, multi-processor environment you may get better performance using several tesselators.) Comparison with the GLU tesselator ---------------------------------- On polygons which make it through the "fast path", the tesselator is 3 to 5 times faster than the GLU tesselator. On polygons which don't make it through the fast path (but which don't have self-intersections or degeneracies), it is about 2 times slower. On polygons with self-intersections or degeneraces, there is nothing to compare against. The new tesselator generates many more fans and strips, reducing the number of vertices that need to be sent to the hardware. Key to the statistics: vert number of input vertices on all contours cntr number of input contours tri number of triangles in all output primitives strip number of triangle strips fan number of triangle fans ind number of independent triangles ms number of milliseconds for tesselation (on a 150MHz R4400 Indy) Convex polygon examples: New: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.0459 ms Old: 3 vert, 1 cntr, 1 tri, 0 strip, 0 fan, 1 ind, 0.149 ms New: 4 vert, 1 cntr, 2 tri, 0 strip, 1 fan, 0 ind, 0.0459 ms Old: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.161 ms New: 36 vert, 1 cntr, 34 tri, 0 strip, 1 fan, 0 ind, 0.153 ms Old: 36 vert, 1 cntr, 34 tri, 0 strip, 0 fan, 34 ind, 0.621 ms Concave single-contour polygons: New: 5 vert, 1 cntr, 3 tri, 0 strip, 1 fan, 0 ind, 0.052 ms Old: 5 vert, 1 cntr, 3 tri, 0 strip, 0 fan, 3 ind, 0.252 ms New: 19 vert, 1 cntr, 17 tri, 2 strip, 2 fan, 1 ind, 0.911 ms Old: 19 vert, 1 cntr, 17 tri, 0 strip, 0 fan, 17 ind, 0.529 ms New: 151 vert, 1 cntr, 149 tri, 13 strip, 18 fan, 3 ind, 6.82 ms Old: 151 vert, 1 cntr, 149 tri, 0 strip, 3 fan, 143 ind, 2.7 ms New: 574 vert, 1 cntr, 572 tri, 59 strip, 54 fan, 11 ind, 26.6 ms Old: 574 vert, 1 cntr, 572 tri, 0 strip, 31 fan, 499 ind, 12.4 ms Multiple contours, but no intersections: New: 7 vert, 2 cntr, 7 tri, 1 strip, 0 fan, 0 ind, 0.527 ms Old: 7 vert, 2 cntr, 7 tri, 0 strip, 0 fan, 7 ind, 0.274 ms New: 81 vert, 6 cntr, 89 tri, 9 strip, 7 fan, 6 ind, 3.88 ms Old: 81 vert, 6 cntr, 89 tri, 0 strip, 13 fan, 61 ind, 2.2 ms New: 391 vert, 19 cntr, 413 tri, 37 strip, 32 fan, 26 ind, 20.2 ms Old: 391 vert, 19 cntr, 413 tri, 0 strip, 25 fan, 363 ind, 8.68 ms Self-intersecting and degenerate examples: Bowtie: 4 vert, 1 cntr, 2 tri, 0 strip, 0 fan, 2 ind, 0.483 ms Star: 5 vert, 1 cntr, 5 tri, 0 strip, 0 fan, 5 ind, 0.91 ms Random: 24 vert, 7 cntr, 46 tri, 2 strip, 12 fan, 7 ind, 5.32 ms Font: 333 vert, 2 cntr, 331 tri, 32 strip, 16 fan, 3 ind, 14.1 ms : 167 vert, 35 cntr, 254 tri, 8 strip, 56 fan, 52 ind, 46.3 ms : 78 vert, 1 cntr, 2675 tri, 148 strip, 207 fan, 180 ind, 243 ms : 12480 vert, 2 cntr, 12478 tri, 736 strip,1275 fan, 5 ind, 1010 ms muffin-6.4.1/cogl/cogl-path/tesselator/priorityq.c0000664000175000017500000001601314723361714021140 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include #include /* LONG_MAX */ #include "memalloc.h" /* Include all the code for the regular heap-based queue here. */ #include "priorityq-heap.c" /* Now redefine all the function names to map to their "Sort" versions. */ #include "priorityq-sort.h" /* really __gl_pqSortNewPriorityQ */ PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ) { PriorityQ *pq = (PriorityQ *)memAlloc( sizeof( PriorityQ )); if (pq == NULL) return NULL; pq->heap = __gl_pqHeapNewPriorityQ( leq ); if (pq->heap == NULL) { memFree(pq); return NULL; } pq->keys = (PQHeapKey *)memAlloc( INIT_SIZE * sizeof(pq->keys[0]) ); if (pq->keys == NULL) { __gl_pqHeapDeletePriorityQ(pq->heap); memFree(pq); return NULL; } pq->order = NULL; pq->size = 0; pq->max = INIT_SIZE; pq->initialized = FALSE; pq->leq = leq; return pq; } /* really __gl_pqSortDeletePriorityQ */ void pqDeletePriorityQ( PriorityQ *pq ) { assert(pq != NULL); if (pq->heap != NULL) __gl_pqHeapDeletePriorityQ( pq->heap ); if (pq->order != NULL) memFree( pq->order ); if (pq->keys != NULL) memFree( pq->keys ); memFree( pq ); } #define LT(x,y) (! LEQ(y,x)) #define GT(x,y) (! LEQ(x,y)) #define Swap(a,b) do{PQkey *tmp = *a; *a = *b; *b = tmp;}while(0) /* really __gl_pqSortInit */ int pqInit( PriorityQ *pq ) { PQkey **p, **r, **i, **j, *piv; struct { PQkey **p, **r; } Stack[50], *top = Stack; unsigned long seed = 2016473283; /* Create an array of indirect pointers to the keys, so that we * the handles we have returned are still valid. */ /* pq->order = (PQHeapKey **)memAlloc( (size_t) (pq->size * sizeof(pq->order[0])) ); */ pq->order = (PQHeapKey **)memAlloc( (size_t) ((pq->size+1) * sizeof(pq->order[0])) ); /* the previous line is a patch to compensate for the fact that IBM */ /* machines return a null on a malloc of zero bytes (unlike SGI), */ /* so we have to put in this defense to guard against a memory */ /* fault four lines down. from fossum@austin.ibm.com. */ if (pq->order == NULL) return 0; p = pq->order; r = p + pq->size - 1; for( piv = pq->keys, i = p; i <= r; ++piv, ++i ) { *i = piv; } /* Sort the indirect pointers in descending order, * using randomized Quicksort */ top->p = p; top->r = r; ++top; while( --top >= Stack ) { p = top->p; r = top->r; while( r > p + 10 ) { seed = seed * 1539415821 + 1; i = p + seed % (r - p + 1); piv = *i; *i = *p; *p = piv; i = p - 1; j = r + 1; do { do { ++i; } while( GT( **i, *piv )); do { --j; } while( LT( **j, *piv )); Swap( i, j ); } while( i < j ); Swap( i, j ); /* Undo last swap */ if( i - p < r - j ) { top->p = j+1; top->r = r; ++top; r = i-1; } else { top->p = p; top->r = i-1; ++top; p = j+1; } } /* Insertion sort small lists */ for( i = p+1; i <= r; ++i ) { piv = *i; for( j = i; j > p && LT( **(j-1), *piv ); --j ) { *j = *(j-1); } *j = piv; } } pq->max = pq->size; pq->initialized = TRUE; __gl_pqHeapInit( pq->heap ); /* always succeeds */ #ifndef NDEBUG p = pq->order; r = p + pq->size - 1; for( i = p; i < r; ++i ) { assert( LEQ( **(i+1), **i )); } #endif return 1; } /* really __gl_pqSortInsert */ /* returns LONG_MAX iff out of memory */ PQhandle pqInsert( PriorityQ *pq, PQkey keyNew ) { long curr; if( pq->initialized ) { return __gl_pqHeapInsert( pq->heap, keyNew ); } curr = pq->size; if( ++ pq->size >= pq->max ) { PQkey *saveKey= pq->keys; /* If the heap overflows, double its size. */ pq->max <<= 1; pq->keys = (PQHeapKey *)memRealloc( pq->keys, (size_t) (pq->max * sizeof( pq->keys[0] ))); if (pq->keys == NULL) { pq->keys = saveKey; /* restore ptr to free upon return */ return LONG_MAX; } } assert(curr != LONG_MAX); pq->keys[curr] = keyNew; /* Negative handles index the sorted array. */ return -(curr+1); } /* really __gl_pqSortExtractMin */ PQkey pqExtractMin( PriorityQ *pq ) { PQkey sortMin, heapMin; if( pq->size == 0 ) { return __gl_pqHeapExtractMin( pq->heap ); } sortMin = *(pq->order[pq->size-1]); if( ! __gl_pqHeapIsEmpty( pq->heap )) { heapMin = __gl_pqHeapMinimum( pq->heap ); if( LEQ( heapMin, sortMin )) { return __gl_pqHeapExtractMin( pq->heap ); } } do { -- pq->size; } while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ); return sortMin; } /* really __gl_pqSortMinimum */ PQkey pqMinimum( PriorityQ *pq ) { PQkey sortMin, heapMin; if( pq->size == 0 ) { return __gl_pqHeapMinimum( pq->heap ); } sortMin = *(pq->order[pq->size-1]); if( ! __gl_pqHeapIsEmpty( pq->heap )) { heapMin = __gl_pqHeapMinimum( pq->heap ); if( LEQ( heapMin, sortMin )) { return heapMin; } } return sortMin; } /* really __gl_pqSortIsEmpty */ int pqIsEmpty( PriorityQ *pq ) { return (pq->size == 0) && __gl_pqHeapIsEmpty( pq->heap ); } /* really __gl_pqSortDelete */ void pqDelete( PriorityQ *pq, PQhandle curr ) { if( curr >= 0 ) { __gl_pqHeapDelete( pq->heap, curr ); return; } curr = -(curr+1); assert( curr < pq->max && pq->keys[curr] != NULL ); pq->keys[curr] = NULL; while( pq->size > 0 && *(pq->order[pq->size-1]) == NULL ) { -- pq->size; } } muffin-6.4.1/cogl/cogl-path/tesselator/dict.h0000664000175000017500000000671014723361714020031 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __dict_list_h_ #define __dict_list_h_ /* Use #define's so that another heap implementation can use this one */ #define DictKey DictListKey #define Dict DictList #define DictNode DictListNode #define dictNewDict(frame,leq) __gl_dictListNewDict(frame,leq) #define dictDeleteDict(dict) __gl_dictListDeleteDict(dict) #define dictSearch(dict,key) __gl_dictListSearch(dict,key) #define dictInsert(dict,key) __gl_dictListInsert(dict,key) #define dictInsertBefore(dict,node,key) __gl_dictListInsertBefore(dict,node,key) #define dictDelete(dict,node) __gl_dictListDelete(dict,node) #define dictKey(n) __gl_dictListKey(n) #define dictSucc(n) __gl_dictListSucc(n) #define dictPred(n) __gl_dictListPred(n) #define dictMin(d) __gl_dictListMin(d) #define dictMax(d) __gl_dictListMax(d) typedef void *DictKey; typedef struct Dict Dict; typedef struct DictNode DictNode; Dict *dictNewDict( void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) ); void dictDeleteDict( Dict *dict ); /* Search returns the node with the smallest key greater than or equal * to the given key. If there is no such key, returns a node whose * key is NULL. Similarly, Succ(Max(d)) has a NULL key, etc. */ DictNode *dictSearch( Dict *dict, DictKey key ); DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ); void dictDelete( Dict *dict, DictNode *node ); #define __gl_dictListKey(n) ((n)->key) #define __gl_dictListSucc(n) ((n)->next) #define __gl_dictListPred(n) ((n)->prev) #define __gl_dictListMin(d) ((d)->head.next) #define __gl_dictListMax(d) ((d)->head.prev) #define __gl_dictListInsert(d,k) (dictInsertBefore((d),&(d)->head,(k))) /*** Private data structures ***/ struct DictNode { DictKey key; DictNode *next; DictNode *prev; }; struct Dict { DictNode head; void *frame; int (*leq)(void *frame, DictKey key1, DictKey key2); }; #endif muffin-6.4.1/cogl/cogl-path/tesselator/tessmono.h0000664000175000017500000000616014723361714020754 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __tessmono_h_ #define __tessmono_h_ /* __gl_meshTessellateMonoRegion( face ) tessellates a monotone region * (what else would it do??) The region must consist of a single * loop of half-edges (see mesh.h) oriented CCW. "Monotone" in this * case means that any vertical line intersects the interior of the * region in a single interval. * * Tessellation consists of adding interior edges (actually pairs of * half-edges), to split the region into non-overlapping triangles. * * __gl_meshTessellateInterior( mesh ) tessellates each region of * the mesh which is marked "inside" the polygon. Each such region * must be monotone. * * __gl_meshDiscardExterior( mesh ) zaps (ie. sets to NULL) all faces * which are not marked "inside" the polygon. Since further mesh operations * on NULL faces are not allowed, the main purpose is to clean up the * mesh so that exterior loops are not represented in the data structure. * * __gl_meshSetWindingNumber( mesh, value, keepOnlyBoundary ) resets the * winding numbers on all edges so that regions marked "inside" the * polygon have a winding number of "value", and regions outside * have a winding number of 0. * * If keepOnlyBoundary is TRUE, it also deletes all edges which do not * separate an interior region from an exterior one. */ int __gl_meshTessellateMonoRegion( GLUface *face ); int __gl_meshTessellateInterior( GLUmesh *mesh ); void __gl_meshDiscardExterior( GLUmesh *mesh ); int __gl_meshSetWindingNumber( GLUmesh *mesh, int value, GLboolean keepOnlyBoundary ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/priorityq-sort.h0000664000175000017500000001012614723361714022131 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __priorityq_sort_h_ #define __priorityq_sort_h_ #include "priorityq-heap.h" #undef PQkey #undef PQhandle #undef PriorityQ #undef pqNewPriorityQ #undef pqDeletePriorityQ #undef pqInit #undef pqInsert #undef pqMinimum #undef pqExtractMin #undef pqDelete #undef pqIsEmpty /* Use #define's so that another heap implementation can use this one */ #define PQkey PQSortKey #define PQhandle PQSortHandle #define PriorityQ PriorityQSort #define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) #define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) /* The basic operations are insertion of a new key (pqInsert), * and examination/extraction of a key whose value is minimum * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); * for this purpose pqInsert returns a "handle" which is supplied * as the argument. * * An initial heap may be created efficiently by calling pqInsert * repeatedly, then calling pqInit. In any case pqInit must be called * before any operations other than pqInsert are used. * * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. * This may also be tested with pqIsEmpty. */ #define pqInit(pq) __gl_pqSortInit(pq) #define pqInsert(pq,key) __gl_pqSortInsert(pq,key) #define pqMinimum(pq) __gl_pqSortMinimum(pq) #define pqExtractMin(pq) __gl_pqSortExtractMin(pq) #define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) #define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) /* Since we support deletion the data structure is a little more * complicated than an ordinary heap. "nodes" is the heap itself; * active nodes are stored in the range 1..pq->size. When the * heap exceeds its allocated size (pq->max), its size doubles. * The children of node i are nodes 2i and 2i+1. * * Each node stores an index into an array "handles". Each handle * stores a key, plus a pointer back to the node which currently * represents that key (ie. nodes[handles[i].node].handle == i). */ typedef PQHeapKey PQkey; typedef PQHeapHandle PQhandle; typedef struct PriorityQ PriorityQ; struct PriorityQ { PriorityQHeap *heap; PQkey *keys; PQkey **order; PQhandle size, max; int initialized; int (*leq)(PQkey key1, PQkey key2); }; PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); void pqDeletePriorityQ( PriorityQ *pq ); int pqInit( PriorityQ *pq ); PQhandle pqInsert( PriorityQ *pq, PQkey key ); PQkey pqExtractMin( PriorityQ *pq ); void pqDelete( PriorityQ *pq, PQhandle handle ); PQkey pqMinimum( PriorityQ *pq ); int pqIsEmpty( PriorityQ *pq ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/normal.c0000664000175000017500000001713614723361714020375 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include "mesh.h" #include "tess.h" #include "normal.h" #include #include #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #define Dot(u,v) (u[0]*v[0] + u[1]*v[1] + u[2]*v[2]) #if 0 static void Normalize( GLdouble v[3] ) { GLdouble len = v[0]*v[0] + v[1]*v[1] + v[2]*v[2]; assert( len > 0 ); len = sqrt( len ); v[0] /= len; v[1] /= len; v[2] /= len; } #endif #undef ABS #define ABS(x) ((x) < 0 ? -(x) : (x)) static int LongAxis( GLdouble v[3] ) { int i = 0; if( ABS(v[1]) > ABS(v[0]) ) { i = 1; } if( ABS(v[2]) > ABS(v[i]) ) { i = 2; } return i; } static void ComputeNormal( GLUtesselator *tess, GLdouble norm[3] ) { GLUvertex *v, *v1, *v2; GLdouble c, tLen2, maxLen2; GLdouble maxVal[3], minVal[3], d1[3], d2[3], tNorm[3]; GLUvertex *maxVert[3], *minVert[3]; GLUvertex *vHead = &tess->mesh->vHead; int i; maxVal[0] = maxVal[1] = maxVal[2] = -2 * GLU_TESS_MAX_COORD; minVal[0] = minVal[1] = minVal[2] = 2 * GLU_TESS_MAX_COORD; for( v = vHead->next; v != vHead; v = v->next ) { for( i = 0; i < 3; ++i ) { c = v->coords[i]; if( c < minVal[i] ) { minVal[i] = c; minVert[i] = v; } if( c > maxVal[i] ) { maxVal[i] = c; maxVert[i] = v; } } } /* Find two vertices separated by at least 1/sqrt(3) of the maximum * distance between any two vertices */ i = 0; if( maxVal[1] - minVal[1] > maxVal[0] - minVal[0] ) { i = 1; } if( maxVal[2] - minVal[2] > maxVal[i] - minVal[i] ) { i = 2; } if( minVal[i] >= maxVal[i] ) { /* All vertices are the same -- normal doesn't matter */ norm[0] = 0; norm[1] = 0; norm[2] = 1; return; } /* Look for a third vertex which forms the triangle with maximum area * (Length of normal == twice the triangle area) */ maxLen2 = 0; v1 = minVert[i]; v2 = maxVert[i]; d1[0] = v1->coords[0] - v2->coords[0]; d1[1] = v1->coords[1] - v2->coords[1]; d1[2] = v1->coords[2] - v2->coords[2]; for( v = vHead->next; v != vHead; v = v->next ) { d2[0] = v->coords[0] - v2->coords[0]; d2[1] = v->coords[1] - v2->coords[1]; d2[2] = v->coords[2] - v2->coords[2]; tNorm[0] = d1[1]*d2[2] - d1[2]*d2[1]; tNorm[1] = d1[2]*d2[0] - d1[0]*d2[2]; tNorm[2] = d1[0]*d2[1] - d1[1]*d2[0]; tLen2 = tNorm[0]*tNorm[0] + tNorm[1]*tNorm[1] + tNorm[2]*tNorm[2]; if( tLen2 > maxLen2 ) { maxLen2 = tLen2; norm[0] = tNorm[0]; norm[1] = tNorm[1]; norm[2] = tNorm[2]; } } if( maxLen2 <= 0 ) { /* All points lie on a single line -- any decent normal will do */ norm[0] = norm[1] = norm[2] = 0; norm[LongAxis(d1)] = 1; } } static void CheckOrientation( GLUtesselator *tess ) { GLdouble area; GLUface *f, *fHead = &tess->mesh->fHead; GLUvertex *v, *vHead = &tess->mesh->vHead; GLUhalfEdge *e; /* When we compute the normal automatically, we choose the orientation * so that the sum of the signed areas of all contours is non-negative. */ area = 0; for( f = fHead->next; f != fHead; f = f->next ) { e = f->anEdge; if( e->winding <= 0 ) continue; do { area += (e->Org->s - e->Dst->s) * (e->Org->t + e->Dst->t); e = e->Lnext; } while( e != f->anEdge ); } if( area < 0 ) { /* Reverse the orientation by flipping all the t-coordinates */ for( v = vHead->next; v != vHead; v = v->next ) { v->t = - v->t; } tess->tUnit[0] = - tess->tUnit[0]; tess->tUnit[1] = - tess->tUnit[1]; tess->tUnit[2] = - tess->tUnit[2]; } } #ifdef FOR_TRITE_TEST_PROGRAM #include extern int RandomSweep; #define S_UNIT_X (RandomSweep ? (2*drand48()-1) : 1.0) #define S_UNIT_Y (RandomSweep ? (2*drand48()-1) : 0.0) #else #if defined(SLANTED_SWEEP) /* The "feature merging" is not intended to be complete. There are * special cases where edges are nearly parallel to the sweep line * which are not implemented. The algorithm should still behave * robustly (ie. produce a reasonable tesselation) in the presence * of such edges, however it may miss features which could have been * merged. We could minimize this effect by choosing the sweep line * direction to be something unusual (ie. not parallel to one of the * coordinate axes). */ #define S_UNIT_X 0.50941539564955385 /* Pre-normalized */ #define S_UNIT_Y 0.86052074622010633 #else #define S_UNIT_X 1.0 #define S_UNIT_Y 0.0 #endif #endif /* Determine the polygon normal and project vertices onto the plane * of the polygon. */ void __gl_projectPolygon( GLUtesselator *tess ) { GLUvertex *v, *vHead = &tess->mesh->vHead; GLdouble norm[3]; GLdouble *sUnit, *tUnit; int i, computedNormal = FALSE; norm[0] = tess->normal[0]; norm[1] = tess->normal[1]; norm[2] = tess->normal[2]; if( norm[0] == 0 && norm[1] == 0 && norm[2] == 0 ) { ComputeNormal( tess, norm ); computedNormal = TRUE; } sUnit = tess->sUnit; tUnit = tess->tUnit; i = LongAxis( norm ); #if defined(FOR_TRITE_TEST_PROGRAM) || defined(TRUE_PROJECT) /* Choose the initial sUnit vector to be approximately perpendicular * to the normal. */ Normalize( norm ); sUnit[i] = 0; sUnit[(i+1)%3] = S_UNIT_X; sUnit[(i+2)%3] = S_UNIT_Y; /* Now make it exactly perpendicular */ w = Dot( sUnit, norm ); sUnit[0] -= w * norm[0]; sUnit[1] -= w * norm[1]; sUnit[2] -= w * norm[2]; Normalize( sUnit ); /* Choose tUnit so that (sUnit,tUnit,norm) form a right-handed frame */ tUnit[0] = norm[1]*sUnit[2] - norm[2]*sUnit[1]; tUnit[1] = norm[2]*sUnit[0] - norm[0]*sUnit[2]; tUnit[2] = norm[0]*sUnit[1] - norm[1]*sUnit[0]; Normalize( tUnit ); #else /* Project perpendicular to a coordinate axis -- better numerically */ sUnit[i] = 0; sUnit[(i+1)%3] = S_UNIT_X; sUnit[(i+2)%3] = S_UNIT_Y; tUnit[i] = 0; tUnit[(i+1)%3] = (norm[i] > 0) ? -S_UNIT_Y : S_UNIT_Y; tUnit[(i+2)%3] = (norm[i] > 0) ? S_UNIT_X : -S_UNIT_X; #endif /* Project the vertices onto the sweep plane */ for( v = vHead->next; v != vHead; v = v->next ) { v->s = Dot( v->coords, sUnit ); v->t = Dot( v->coords, tUnit ); } if( computedNormal ) { CheckOrientation( tess ); } } muffin-6.4.1/cogl/cogl-path/tesselator/priorityq.h0000664000175000017500000001012614723361714021144 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __priorityq_sort_h_ #define __priorityq_sort_h_ #include "priorityq-heap.h" #undef PQkey #undef PQhandle #undef PriorityQ #undef pqNewPriorityQ #undef pqDeletePriorityQ #undef pqInit #undef pqInsert #undef pqMinimum #undef pqExtractMin #undef pqDelete #undef pqIsEmpty /* Use #define's so that another heap implementation can use this one */ #define PQkey PQSortKey #define PQhandle PQSortHandle #define PriorityQ PriorityQSort #define pqNewPriorityQ(leq) __gl_pqSortNewPriorityQ(leq) #define pqDeletePriorityQ(pq) __gl_pqSortDeletePriorityQ(pq) /* The basic operations are insertion of a new key (pqInsert), * and examination/extraction of a key whose value is minimum * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); * for this purpose pqInsert returns a "handle" which is supplied * as the argument. * * An initial heap may be created efficiently by calling pqInsert * repeatedly, then calling pqInit. In any case pqInit must be called * before any operations other than pqInsert are used. * * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. * This may also be tested with pqIsEmpty. */ #define pqInit(pq) __gl_pqSortInit(pq) #define pqInsert(pq,key) __gl_pqSortInsert(pq,key) #define pqMinimum(pq) __gl_pqSortMinimum(pq) #define pqExtractMin(pq) __gl_pqSortExtractMin(pq) #define pqDelete(pq,handle) __gl_pqSortDelete(pq,handle) #define pqIsEmpty(pq) __gl_pqSortIsEmpty(pq) /* Since we support deletion the data structure is a little more * complicated than an ordinary heap. "nodes" is the heap itself; * active nodes are stored in the range 1..pq->size. When the * heap exceeds its allocated size (pq->max), its size doubles. * The children of node i are nodes 2i and 2i+1. * * Each node stores an index into an array "handles". Each handle * stores a key, plus a pointer back to the node which currently * represents that key (ie. nodes[handles[i].node].handle == i). */ typedef PQHeapKey PQkey; typedef PQHeapHandle PQhandle; typedef struct PriorityQ PriorityQ; struct PriorityQ { PriorityQHeap *heap; PQkey *keys; PQkey **order; PQhandle size, max; int initialized; int (*leq)(PQkey key1, PQkey key2); }; PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); void pqDeletePriorityQ( PriorityQ *pq ); int pqInit( PriorityQ *pq ); PQhandle pqInsert( PriorityQ *pq, PQkey key ); PQkey pqExtractMin( PriorityQ *pq ); void pqDelete( PriorityQ *pq, PQhandle handle ); PQkey pqMinimum( PriorityQ *pq ); int pqIsEmpty( PriorityQ *pq ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/priorityq-heap.h0000664000175000017500000001001714723361714022056 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __priorityq_heap_h_ #define __priorityq_heap_h_ /* Use #define's so that another heap implementation can use this one */ #define PQkey PQHeapKey #define PQhandle PQHeapHandle #define PriorityQ PriorityQHeap #define pqNewPriorityQ(leq) __gl_pqHeapNewPriorityQ(leq) #define pqDeletePriorityQ(pq) __gl_pqHeapDeletePriorityQ(pq) /* The basic operations are insertion of a new key (pqInsert), * and examination/extraction of a key whose value is minimum * (pqMinimum/pqExtractMin). Deletion is also allowed (pqDelete); * for this purpose pqInsert returns a "handle" which is supplied * as the argument. * * An initial heap may be created efficiently by calling pqInsert * repeatedly, then calling pqInit. In any case pqInit must be called * before any operations other than pqInsert are used. * * If the heap is empty, pqMinimum/pqExtractMin will return a NULL key. * This may also be tested with pqIsEmpty. */ #define pqInit(pq) __gl_pqHeapInit(pq) #define pqInsert(pq,key) __gl_pqHeapInsert(pq,key) #define pqMinimum(pq) __gl_pqHeapMinimum(pq) #define pqExtractMin(pq) __gl_pqHeapExtractMin(pq) #define pqDelete(pq,handle) __gl_pqHeapDelete(pq,handle) #define pqIsEmpty(pq) __gl_pqHeapIsEmpty(pq) /* Since we support deletion the data structure is a little more * complicated than an ordinary heap. "nodes" is the heap itself; * active nodes are stored in the range 1..pq->size. When the * heap exceeds its allocated size (pq->max), its size doubles. * The children of node i are nodes 2i and 2i+1. * * Each node stores an index into an array "handles". Each handle * stores a key, plus a pointer back to the node which currently * represents that key (ie. nodes[handles[i].node].handle == i). */ typedef void *PQkey; typedef long PQhandle; typedef struct PriorityQ PriorityQ; typedef struct { PQhandle handle; } PQnode; typedef struct { PQkey key; PQhandle node; } PQhandleElem; struct PriorityQ { PQnode *nodes; PQhandleElem *handles; long size, max; PQhandle freeList; int initialized; int (*leq)(PQkey key1, PQkey key2); }; PriorityQ *pqNewPriorityQ( int (*leq)(PQkey key1, PQkey key2) ); void pqDeletePriorityQ( PriorityQ *pq ); void pqInit( PriorityQ *pq ); PQhandle pqInsert( PriorityQ *pq, PQkey key ); PQkey pqExtractMin( PriorityQ *pq ); void pqDelete( PriorityQ *pq, PQhandle handle ); #define __gl_pqHeapMinimum(pq) ((pq)->handles[(pq)->nodes[1].handle].key) #define __gl_pqHeapIsEmpty(pq) ((pq)->size == 0) #endif muffin-6.4.1/cogl/cogl-path/tesselator/geom.c0000664000175000017500000002124114723361714020024 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include "mesh.h" #include "geom.h" int __gl_vertLeq( GLUvertex *u, GLUvertex *v ) { /* Returns TRUE if u is lexicographically <= v. */ return VertLeq( u, v ); } GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) { /* Given three vertices u,v,w such that VertLeq(u,v) && VertLeq(v,w), * evaluates the t-coord of the edge uw at the s-coord of the vertex v. * Returns v->t - (uw)(v->s), ie. the signed distance from uw to v. * If uw is vertical (and thus passes thru v), the result is zero. * * The calculation is extremely accurate and stable, even when v * is very close to u or w. In particular if we set v->t = 0 and * let r be the negated result (this evaluates (uw)(v->s)), then * r is guaranteed to satisfy MIN(u->t,w->t) <= r <= MAX(u->t,w->t). */ GLdouble gapL, gapR; assert( VertLeq( u, v ) && VertLeq( v, w )); gapL = v->s - u->s; gapR = w->s - v->s; if( gapL + gapR > 0 ) { if( gapL < gapR ) { return (v->t - u->t) + (u->t - w->t) * (gapL / (gapL + gapR)); } else { return (v->t - w->t) + (w->t - u->t) * (gapR / (gapL + gapR)); } } /* vertical line */ return 0; } GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) { /* Returns a number whose sign matches EdgeEval(u,v,w) but which * is cheaper to evaluate. Returns > 0, == 0 , or < 0 * as v is above, on, or below the edge uw. */ GLdouble gapL, gapR; assert( VertLeq( u, v ) && VertLeq( v, w )); gapL = v->s - u->s; gapR = w->s - v->s; if( gapL + gapR > 0 ) { return (v->t - w->t) * gapL + (v->t - u->t) * gapR; } /* vertical line */ return 0; } /*********************************************************************** * Define versions of EdgeSign, EdgeEval with s and t transposed. */ GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ) { /* Given three vertices u,v,w such that TransLeq(u,v) && TransLeq(v,w), * evaluates the t-coord of the edge uw at the s-coord of the vertex v. * Returns v->s - (uw)(v->t), ie. the signed distance from uw to v. * If uw is vertical (and thus passes thru v), the result is zero. * * The calculation is extremely accurate and stable, even when v * is very close to u or w. In particular if we set v->s = 0 and * let r be the negated result (this evaluates (uw)(v->t)), then * r is guaranteed to satisfy MIN(u->s,w->s) <= r <= MAX(u->s,w->s). */ GLdouble gapL, gapR; assert( TransLeq( u, v ) && TransLeq( v, w )); gapL = v->t - u->t; gapR = w->t - v->t; if( gapL + gapR > 0 ) { if( gapL < gapR ) { return (v->s - u->s) + (u->s - w->s) * (gapL / (gapL + gapR)); } else { return (v->s - w->s) + (w->s - u->s) * (gapR / (gapL + gapR)); } } /* vertical line */ return 0; } GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ) { /* Returns a number whose sign matches TransEval(u,v,w) but which * is cheaper to evaluate. Returns > 0, == 0 , or < 0 * as v is above, on, or below the edge uw. */ GLdouble gapL, gapR; assert( TransLeq( u, v ) && TransLeq( v, w )); gapL = v->t - u->t; gapR = w->t - v->t; if( gapL + gapR > 0 ) { return (v->s - w->s) * gapL + (v->s - u->s) * gapR; } /* vertical line */ return 0; } int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ) { /* For almost-degenerate situations, the results are not reliable. * Unless the floating-point arithmetic can be performed without * rounding errors, *any* implementation will give incorrect results * on some degenerate inputs, so the client must have some way to * handle this situation. */ return (u->s*(v->t - w->t) + v->s*(w->t - u->t) + w->s*(u->t - v->t)) >= 0; } /* Given parameters a,x,b,y returns the value (b*x+a*y)/(a+b), * or (x+y)/2 if a==b==0. It requires that a,b >= 0, and enforces * this in the rare case that one argument is slightly negative. * The implementation is extremely stable numerically. * In particular it guarantees that the result r satisfies * MIN(x,y) <= r <= MAX(x,y), and the results are very accurate * even when a and b differ greatly in magnitude. */ #define RealInterpolate(a,x,b,y) \ (a = (a < 0) ? 0 : a, b = (b < 0) ? 0 : b, \ ((a <= b) ? ((b == 0) ? ((x+y) / 2) \ : (x + (y-x) * (a/(a+b)))) \ : (y + (x-y) * (b/(a+b))))) #ifndef FOR_TRITE_TEST_PROGRAM #define Interpolate(a,x,b,y) RealInterpolate(a,x,b,y) #else /* Claim: the ONLY property the sweep algorithm relies on is that * MIN(x,y) <= r <= MAX(x,y). This is a nasty way to test that. */ #include extern int RandomInterpolate; GLdouble Interpolate( GLdouble a, GLdouble x, GLdouble b, GLdouble y) { printf("*********************%d\n",RandomInterpolate); if( RandomInterpolate ) { a = 1.2 * drand48() - 0.1; a = (a < 0) ? 0 : ((a > 1) ? 1 : a); b = 1.0 - a; } return RealInterpolate(a,x,b,y); } #endif #define Swap(a,b) do { GLUvertex *t = a; a = b; b = t; } while (0) void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, GLUvertex *o2, GLUvertex *d2, GLUvertex *v ) /* Given edges (o1,d1) and (o2,d2), compute their point of intersection. * The computed point is guaranteed to lie in the intersection of the * bounding rectangles defined by each edge. */ { GLdouble z1, z2; /* This is certainly not the most efficient way to find the intersection * of two line segments, but it is very numerically stable. * * Strategy: find the two middle vertices in the VertLeq ordering, * and interpolate the intersection s-value from these. Then repeat * using the TransLeq ordering to find the intersection t-value. */ if( ! VertLeq( o1, d1 )) { Swap( o1, d1 ); } if( ! VertLeq( o2, d2 )) { Swap( o2, d2 ); } if( ! VertLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } if( ! VertLeq( o2, d1 )) { /* Technically, no intersection -- do our best */ v->s = (o2->s + d1->s) / 2; } else if( VertLeq( d1, d2 )) { /* Interpolate between o2 and d1 */ z1 = EdgeEval( o1, o2, d1 ); z2 = EdgeEval( o2, d1, d2 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->s = Interpolate( z1, o2->s, z2, d1->s ); } else { /* Interpolate between o2 and d2 */ z1 = EdgeSign( o1, o2, d1 ); z2 = -EdgeSign( o1, d2, d1 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->s = Interpolate( z1, o2->s, z2, d2->s ); } /* Now repeat the process for t */ if( ! TransLeq( o1, d1 )) { Swap( o1, d1 ); } if( ! TransLeq( o2, d2 )) { Swap( o2, d2 ); } if( ! TransLeq( o1, o2 )) { Swap( o1, o2 ); Swap( d1, d2 ); } if( ! TransLeq( o2, d1 )) { /* Technically, no intersection -- do our best */ v->t = (o2->t + d1->t) / 2; } else if( TransLeq( d1, d2 )) { /* Interpolate between o2 and d1 */ z1 = TransEval( o1, o2, d1 ); z2 = TransEval( o2, d1, d2 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->t = Interpolate( z1, o2->t, z2, d1->t ); } else { /* Interpolate between o2 and d2 */ z1 = TransSign( o1, o2, d1 ); z2 = -TransSign( o1, d2, d1 ); if( z1+z2 < 0 ) { z1 = -z1; z2 = -z2; } v->t = Interpolate( z1, o2->t, z2, d2->t ); } } muffin-6.4.1/cogl/cogl-path/tesselator/render.h0000664000175000017500000000423414723361714020364 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __render_h_ #define __render_h_ #include "mesh.h" #include "tesselator.h" /* __gl_renderMesh( tess, mesh ) takes a mesh and breaks it into triangle * fans, strips, and separate triangles. A substantial effort is made * to use as few rendering primitives as possible (ie. to make the fans * and strips as large as possible). * * The rendering output is provided as callbacks (see the api). */ void __gl_renderMesh( GLUtesselator *tess, GLUmesh *mesh ); void __gl_renderBoundary( GLUtesselator *tess, GLUmesh *mesh ); GLboolean __gl_renderCache( GLUtesselator *tess ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/geom.h0000664000175000017500000000651414723361714020037 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __geom_h_ #define __geom_h_ #include "mesh.h" #ifdef NO_BRANCH_CONDITIONS /* MIPS architecture has special instructions to evaluate boolean * conditions -- more efficient than branching, IF you can get the * compiler to generate the right instructions (SGI compiler doesn't) */ #define VertEq(u,v) (((u)->s == (v)->s) & ((u)->t == (v)->t)) #define VertLeq(u,v) (((u)->s < (v)->s) | \ ((u)->s == (v)->s & (u)->t <= (v)->t)) #else #define VertEq(u,v) ((u)->s == (v)->s && (u)->t == (v)->t) #define VertLeq(u,v) (((u)->s < (v)->s) || \ ((u)->s == (v)->s && (u)->t <= (v)->t)) #endif #define EdgeEval(u,v,w) __gl_edgeEval(u,v,w) #define EdgeSign(u,v,w) __gl_edgeSign(u,v,w) /* Versions of VertLeq, EdgeSign, EdgeEval with s and t transposed. */ #define TransLeq(u,v) (((u)->t < (v)->t) || \ ((u)->t == (v)->t && (u)->s <= (v)->s)) #define TransEval(u,v,w) __gl_transEval(u,v,w) #define TransSign(u,v,w) __gl_transSign(u,v,w) #define EdgeGoesLeft(e) VertLeq( (e)->Dst, (e)->Org ) #define EdgeGoesRight(e) VertLeq( (e)->Org, (e)->Dst ) #undef ABS #define ABS(x) ((x) < 0 ? -(x) : (x)) #define VertL1dist(u,v) (ABS(u->s - v->s) + ABS(u->t - v->t)) #define VertCCW(u,v,w) __gl_vertCCW(u,v,w) int __gl_vertLeq( GLUvertex *u, GLUvertex *v ); GLdouble __gl_edgeEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); GLdouble __gl_edgeSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); GLdouble __gl_transEval( GLUvertex *u, GLUvertex *v, GLUvertex *w ); GLdouble __gl_transSign( GLUvertex *u, GLUvertex *v, GLUvertex *w ); int __gl_vertCCW( GLUvertex *u, GLUvertex *v, GLUvertex *w ); void __gl_edgeIntersect( GLUvertex *o1, GLUvertex *d1, GLUvertex *o2, GLUvertex *d2, GLUvertex *v ); #endif muffin-6.4.1/cogl/cogl-path/tesselator/sweep.c0000664000175000017500000013655714723361714020241 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include #include /* longjmp */ #include /* LONG_MAX */ #include "mesh.h" #include "geom.h" #include "tess.h" #include "dict.h" #include "priorityq.h" #include "memalloc.h" #include "sweep.h" #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif #ifdef FOR_TRITE_TEST_PROGRAM extern void DebugEvent( GLUtesselator *tess ); #else #define DebugEvent( tess ) #endif /* * Invariants for the Edge Dictionary. * - each pair of adjacent edges e2=Succ(e1) satisfies EdgeLeq(e1,e2) * at any valid location of the sweep event * - if EdgeLeq(e2,e1) as well (at any valid sweep event), then e1 and e2 * share a common endpoint * - for each e, e->Dst has been processed, but not e->Org * - each edge e satisfies VertLeq(e->Dst,event) && VertLeq(event,e->Org) * where "event" is the current sweep line event. * - no edge e has zero length * * Invariants for the Mesh (the processed portion). * - the portion of the mesh left of the sweep line is a planar graph, * ie. there is *some* way to embed it in the plane * - no processed edge has zero length * - no two processed vertices have identical coordinates * - each "inside" region is monotone, ie. can be broken into two chains * of monotonically increasing vertices according to VertLeq(v1,v2) * - a non-invariant: these chains may intersect (very slightly) * * Invariants for the Sweep. * - if none of the edges incident to the event vertex have an activeRegion * (ie. none of these edges are in the edge dictionary), then the vertex * has only right-going edges. * - if an edge is marked "fixUpperEdge" (it is a temporary edge introduced * by ConnectRightVertex), then it is the only right-going edge from * its associated vertex. (This says that these edges exist only * when it is necessary.) */ #undef MAX #undef MIN #define MAX(x,y) ((x) >= (y) ? (x) : (y)) #define MIN(x,y) ((x) <= (y) ? (x) : (y)) /* When we merge two edges into one, we need to compute the combined * winding of the new edge. */ #define AddWinding(eDst,eSrc) (eDst->winding += eSrc->winding, \ eDst->Sym->winding += eSrc->Sym->winding) static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ); static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ); static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ); static int EdgeLeq( GLUtesselator *tess, ActiveRegion *reg1, ActiveRegion *reg2 ) /* * Both edges must be directed from right to left (this is the canonical * direction for the upper edge of each region). * * The strategy is to evaluate a "t" value for each edge at the * current sweep line position, given by tess->event. The calculations * are designed to be very stable, but of course they are not perfect. * * Special case: if both edge destinations are at the sweep event, * we sort the edges by slope (they would otherwise compare equally). */ { GLUvertex *event = tess->event; GLUhalfEdge *e1, *e2; GLdouble t1, t2; e1 = reg1->eUp; e2 = reg2->eUp; if( e1->Dst == event ) { if( e2->Dst == event ) { /* Two edges right of the sweep line which meet at the sweep event. * Sort them by slope. */ if( VertLeq( e1->Org, e2->Org )) { return EdgeSign( e2->Dst, e1->Org, e2->Org ) <= 0; } return EdgeSign( e1->Dst, e2->Org, e1->Org ) >= 0; } return EdgeSign( e2->Dst, event, e2->Org ) <= 0; } if( e2->Dst == event ) { return EdgeSign( e1->Dst, event, e1->Org ) >= 0; } /* General case - compute signed distance *from* e1, e2 to event */ t1 = EdgeEval( e1->Dst, event, e1->Org ); t2 = EdgeEval( e2->Dst, event, e2->Org ); return (t1 >= t2); } static void DeleteRegion( GLUtesselator *tess, ActiveRegion *reg ) { if( reg->fixUpperEdge ) { /* It was created with zero winding number, so it better be * deleted with zero winding number (ie. it better not get merged * with a real edge). */ assert( reg->eUp->winding == 0 ); } reg->eUp->activeRegion = NULL; dictDelete( tess->dict, reg->nodeUp ); /* __gl_dictListDelete */ memFree( reg ); } static int FixUpperEdge( ActiveRegion *reg, GLUhalfEdge *newEdge ) /* * Replace an upper edge which needs fixing (see ConnectRightVertex). */ { assert( reg->fixUpperEdge ); if ( !__gl_meshDelete( reg->eUp ) ) return 0; reg->fixUpperEdge = FALSE; reg->eUp = newEdge; newEdge->activeRegion = reg; return 1; } static ActiveRegion *TopLeftRegion( ActiveRegion *reg ) { GLUvertex *org = reg->eUp->Org; GLUhalfEdge *e; /* Find the region above the uppermost edge with the same origin */ do { reg = RegionAbove( reg ); } while( reg->eUp->Org == org ); /* If the edge above was a temporary edge introduced by ConnectRightVertex, * now is the time to fix it. */ if( reg->fixUpperEdge ) { e = __gl_meshConnect( RegionBelow(reg)->eUp->Sym, reg->eUp->Lnext ); if (e == NULL) return NULL; if ( !FixUpperEdge( reg, e ) ) return NULL; reg = RegionAbove( reg ); } return reg; } static ActiveRegion *TopRightRegion( ActiveRegion *reg ) { GLUvertex *dst = reg->eUp->Dst; /* Find the region above the uppermost edge with the same destination */ do { reg = RegionAbove( reg ); } while( reg->eUp->Dst == dst ); return reg; } static ActiveRegion *AddRegionBelow( GLUtesselator *tess, ActiveRegion *regAbove, GLUhalfEdge *eNewUp ) /* * Add a new active region to the sweep line, *somewhere* below "regAbove" * (according to where the new edge belongs in the sweep-line dictionary). * The upper edge of the new region will be "eNewUp". * Winding number and "inside" flag are not updated. */ { ActiveRegion *regNew = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); if (regNew == NULL) longjmp(tess->env,1); regNew->eUp = eNewUp; /* __gl_dictListInsertBefore */ regNew->nodeUp = dictInsertBefore( tess->dict, regAbove->nodeUp, regNew ); if (regNew->nodeUp == NULL) longjmp(tess->env,1); regNew->fixUpperEdge = FALSE; regNew->sentinel = FALSE; regNew->dirty = FALSE; eNewUp->activeRegion = regNew; return regNew; } static GLboolean IsWindingInside( GLUtesselator *tess, int n ) { switch( tess->windingRule ) { case GLU_TESS_WINDING_ODD: return (n & 1); case GLU_TESS_WINDING_NONZERO: return (n != 0); case GLU_TESS_WINDING_POSITIVE: return (n > 0); case GLU_TESS_WINDING_NEGATIVE: return (n < 0); case GLU_TESS_WINDING_ABS_GEQ_TWO: return (n >= 2) || (n <= -2); } /*LINTED*/ assert( FALSE ); /*NOTREACHED*/ return GL_FALSE; /* avoid compiler complaints */ } static void ComputeWinding( GLUtesselator *tess, ActiveRegion *reg ) { reg->windingNumber = RegionAbove(reg)->windingNumber + reg->eUp->winding; reg->inside = IsWindingInside( tess, reg->windingNumber ); } static void FinishRegion( GLUtesselator *tess, ActiveRegion *reg ) /* * Delete a region from the sweep line. This happens when the upper * and lower chains of a region meet (at a vertex on the sweep line). * The "inside" flag is copied to the appropriate mesh face (we could * not do this before -- since the structure of the mesh is always * changing, this face may not have even existed until now). */ { GLUhalfEdge *e = reg->eUp; GLUface *f = e->Lface; f->inside = reg->inside; f->anEdge = e; /* optimization for __gl_meshTessellateMonoRegion() */ DeleteRegion( tess, reg ); } static GLUhalfEdge *FinishLeftRegions( GLUtesselator *tess, ActiveRegion *regFirst, ActiveRegion *regLast ) /* * We are given a vertex with one or more left-going edges. All affected * edges should be in the edge dictionary. Starting at regFirst->eUp, * we walk down deleting all regions where both edges have the same * origin vOrg. At the same time we copy the "inside" flag from the * active region to the face, since at this point each face will belong * to at most one region (this was not necessarily true until this point * in the sweep). The walk stops at the region above regLast; if regLast * is NULL we walk as far as possible. At the same time we relink the * mesh if necessary, so that the ordering of edges around vOrg is the * same as in the dictionary. */ { ActiveRegion *reg, *regPrev; GLUhalfEdge *e, *ePrev; regPrev = regFirst; ePrev = regFirst->eUp; while( regPrev != regLast ) { regPrev->fixUpperEdge = FALSE; /* placement was OK */ reg = RegionBelow( regPrev ); e = reg->eUp; if( e->Org != ePrev->Org ) { if( ! reg->fixUpperEdge ) { /* Remove the last left-going edge. Even though there are no further * edges in the dictionary with this origin, there may be further * such edges in the mesh (if we are adding left edges to a vertex * that has already been processed). Thus it is important to call * FinishRegion rather than just DeleteRegion. */ FinishRegion( tess, regPrev ); break; } /* If the edge below was a temporary edge introduced by * ConnectRightVertex, now is the time to fix it. */ e = __gl_meshConnect( ePrev->Lprev, e->Sym ); if (e == NULL) longjmp(tess->env,1); if ( !FixUpperEdge( reg, e ) ) longjmp(tess->env,1); } /* Relink edges so that ePrev->Onext == e */ if( ePrev->Onext != e ) { if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); if ( !__gl_meshSplice( ePrev, e ) ) longjmp(tess->env,1); } FinishRegion( tess, regPrev ); /* may change reg->eUp */ ePrev = reg->eUp; regPrev = reg; } return ePrev; } static void AddRightEdges( GLUtesselator *tess, ActiveRegion *regUp, GLUhalfEdge *eFirst, GLUhalfEdge *eLast, GLUhalfEdge *eTopLeft, GLboolean cleanUp ) /* * Purpose: insert right-going edges into the edge dictionary, and update * winding numbers and mesh connectivity appropriately. All right-going * edges share a common origin vOrg. Edges are inserted CCW starting at * eFirst; the last edge inserted is eLast->Oprev. If vOrg has any * left-going edges already processed, then eTopLeft must be the edge * such that an imaginary upward vertical segment from vOrg would be * contained between eTopLeft->Oprev and eTopLeft; otherwise eTopLeft * should be NULL. */ { ActiveRegion *reg, *regPrev; GLUhalfEdge *e, *ePrev; int firstTime = TRUE; /* Insert the new right-going edges in the dictionary */ e = eFirst; do { assert( VertLeq( e->Org, e->Dst )); AddRegionBelow( tess, regUp, e->Sym ); e = e->Onext; } while ( e != eLast ); /* Walk *all* right-going edges from e->Org, in the dictionary order, * updating the winding numbers of each region, and re-linking the mesh * edges to match the dictionary ordering (if necessary). */ if( eTopLeft == NULL ) { eTopLeft = RegionBelow( regUp )->eUp->Rprev; } regPrev = regUp; ePrev = eTopLeft; for( ;; ) { reg = RegionBelow( regPrev ); e = reg->eUp->Sym; if( e->Org != ePrev->Org ) break; if( e->Onext != ePrev ) { /* Unlink e from its current position, and relink below ePrev */ if ( !__gl_meshSplice( e->Oprev, e ) ) longjmp(tess->env,1); if ( !__gl_meshSplice( ePrev->Oprev, e ) ) longjmp(tess->env,1); } /* Compute the winding number and "inside" flag for the new regions */ reg->windingNumber = regPrev->windingNumber - e->winding; reg->inside = IsWindingInside( tess, reg->windingNumber ); /* Check for two outgoing edges with same slope -- process these * before any intersection tests (see example in __gl_computeInterior). */ regPrev->dirty = TRUE; if( ! firstTime && CheckForRightSplice( tess, regPrev )) { AddWinding( e, ePrev ); DeleteRegion( tess, regPrev ); if ( !__gl_meshDelete( ePrev ) ) longjmp(tess->env,1); } firstTime = FALSE; regPrev = reg; ePrev = e; } regPrev->dirty = TRUE; assert( regPrev->windingNumber - e->winding == reg->windingNumber ); if( cleanUp ) { /* Check for intersections between newly adjacent edges. */ WalkDirtyRegions( tess, regPrev ); } } static void CallCombine( GLUtesselator *tess, GLUvertex *isect, void *data[4], GLfloat weights[4], int needed ) { GLdouble coords[3]; /* Copy coord data in case the callback changes it. */ coords[0] = isect->coords[0]; coords[1] = isect->coords[1]; coords[2] = isect->coords[2]; isect->data = NULL; CALL_COMBINE_OR_COMBINE_DATA( coords, data, weights, &isect->data ); if( isect->data == NULL ) { if( ! needed ) { isect->data = data[0]; } else if( ! tess->fatalError ) { /* The only way fatal error is when two edges are found to intersect, * but the user has not provided the callback necessary to handle * generated intersection points. */ CALL_ERROR_OR_ERROR_DATA( GLU_TESS_NEED_COMBINE_CALLBACK ); tess->fatalError = TRUE; } } } static void SpliceMergeVertices( GLUtesselator *tess, GLUhalfEdge *e1, GLUhalfEdge *e2 ) /* * Two vertices with idential coordinates are combined into one. * e1->Org is kept, while e2->Org is discarded. */ { void *data[4] = { NULL, NULL, NULL, NULL }; GLfloat weights[4] = { 0.5, 0.5, 0.0, 0.0 }; data[0] = e1->Org->data; data[1] = e2->Org->data; CallCombine( tess, e1->Org, data, weights, FALSE ); if ( !__gl_meshSplice( e1, e2 ) ) longjmp(tess->env,1); } static void VertexWeights( GLUvertex *isect, GLUvertex *org, GLUvertex *dst, GLfloat *weights ) /* * Find some weights which describe how the intersection vertex is * a linear combination of "org" and "dest". Each of the two edges * which generated "isect" is allocated 50% of the weight; each edge * splits the weight between its org and dst according to the * relative distance to "isect". */ { GLdouble t1 = VertL1dist( org, isect ); GLdouble t2 = VertL1dist( dst, isect ); weights[0] = 0.5 * t2 / (t1 + t2); weights[1] = 0.5 * t1 / (t1 + t2); isect->coords[0] += weights[0]*org->coords[0] + weights[1]*dst->coords[0]; isect->coords[1] += weights[0]*org->coords[1] + weights[1]*dst->coords[1]; isect->coords[2] += weights[0]*org->coords[2] + weights[1]*dst->coords[2]; } static void GetIntersectData( GLUtesselator *tess, GLUvertex *isect, GLUvertex *orgUp, GLUvertex *dstUp, GLUvertex *orgLo, GLUvertex *dstLo ) /* * We've computed a new intersection point, now we need a "data" pointer * from the user so that we can refer to this new vertex in the * rendering callbacks. */ { void *data[4]; GLfloat weights[4]; data[0] = orgUp->data; data[1] = dstUp->data; data[2] = orgLo->data; data[3] = dstLo->data; isect->coords[0] = isect->coords[1] = isect->coords[2] = 0; VertexWeights( isect, orgUp, dstUp, &weights[0] ); VertexWeights( isect, orgLo, dstLo, &weights[2] ); CallCombine( tess, isect, data, weights, TRUE ); } static int CheckForRightSplice( GLUtesselator *tess, ActiveRegion *regUp ) /* * Check the upper and lower edge of "regUp", to make sure that the * eUp->Org is above eLo, or eLo->Org is below eUp (depending on which * origin is leftmost). * * The main purpose is to splice right-going edges with the same * dest vertex and nearly identical slopes (ie. we can't distinguish * the slopes numerically). However the splicing can also help us * to recover from numerical errors. For example, suppose at one * point we checked eUp and eLo, and decided that eUp->Org is barely * above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * our test so that now eUp->Org is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants. * * One possibility is to check these edges for intersection again * (ie. CheckForIntersect). This is what we do if possible. However * CheckForIntersect requires that tess->event lies between eUp and eLo, * so that it has something to fall back on when the intersection * calculation gives us an unusable answer. So, for those cases where * we can't check for intersection, this routine fixes the problem * by just splicing the offending vertex into the other edge. * This is a guaranteed solution, no matter how degenerate things get. * Basically this is a combinatorial solution to a numerical problem. */ { ActiveRegion *regLo = RegionBelow(regUp); GLUhalfEdge *eUp = regUp->eUp; GLUhalfEdge *eLo = regLo->eUp; if( VertLeq( eUp->Org, eLo->Org )) { if( EdgeSign( eLo->Dst, eUp->Org, eLo->Org ) > 0 ) return FALSE; /* eUp->Org appears to be below eLo */ if( ! VertEq( eUp->Org, eLo->Org )) { /* Splice eUp->Org into eLo */ if ( __gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eUp, eLo->Oprev ) ) longjmp(tess->env,1); regUp->dirty = regLo->dirty = TRUE; } else if( eUp->Org != eLo->Org ) { /* merge the two vertices, discarding eUp->Org */ pqDelete( tess->pq, eUp->Org->pqHandle ); /* __gl_pqSortDelete */ SpliceMergeVertices( tess, eLo->Oprev, eUp ); } } else { if( EdgeSign( eUp->Dst, eLo->Org, eUp->Org ) < 0 ) return FALSE; /* eLo->Org appears to be above eUp, so splice eLo->Org into eUp */ RegionAbove(regUp)->dirty = regUp->dirty = TRUE; if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); } return TRUE; } static int CheckForLeftSplice( GLUtesselator *tess, ActiveRegion *regUp ) /* * Check the upper and lower edge of "regUp", to make sure that the * eUp->Dst is above eLo, or eLo->Dst is below eUp (depending on which * destination is rightmost). * * Theoretically, this should always be true. However, splitting an edge * into two pieces can change the results of previous tests. For example, * suppose at one point we checked eUp and eLo, and decided that eUp->Dst * is barely above eLo. Then later, we split eLo into two edges (eg. from * a splice operation like this one). This can change the result of * the test so that now eUp->Dst is incident to eLo, or barely below it. * We must correct this condition to maintain the dictionary invariants * (otherwise new edges might get inserted in the wrong place in the * dictionary, and bad stuff will happen). * * We fix the problem by just splicing the offending vertex into the * other edge. */ { ActiveRegion *regLo = RegionBelow(regUp); GLUhalfEdge *eUp = regUp->eUp; GLUhalfEdge *eLo = regLo->eUp; GLUhalfEdge *e; assert( ! VertEq( eUp->Dst, eLo->Dst )); if( VertLeq( eUp->Dst, eLo->Dst )) { if( EdgeSign( eUp->Dst, eLo->Dst, eUp->Org ) < 0 ) return FALSE; /* eLo->Dst is above eUp, so splice eLo->Dst into eUp */ RegionAbove(regUp)->dirty = regUp->dirty = TRUE; e = __gl_meshSplitEdge( eUp ); if (e == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eLo->Sym, e ) ) longjmp(tess->env,1); e->Lface->inside = regUp->inside; } else { if( EdgeSign( eLo->Dst, eUp->Dst, eLo->Org ) > 0 ) return FALSE; /* eUp->Dst is below eLo, so splice eUp->Dst into eLo */ regUp->dirty = regLo->dirty = TRUE; e = __gl_meshSplitEdge( eLo ); if (e == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eUp->Lnext, eLo->Sym ) ) longjmp(tess->env,1); e->Rface->inside = regUp->inside; } return TRUE; } static int CheckForIntersect( GLUtesselator *tess, ActiveRegion *regUp ) /* * Check the upper and lower edges of the given region to see if * they intersect. If so, create the intersection and add it * to the data structures. * * Returns TRUE if adding the new intersection resulted in a recursive * call to AddRightEdges(); in this case all "dirty" regions have been * checked for intersections, and possibly regUp has been deleted. */ { ActiveRegion *regLo = RegionBelow(regUp); GLUhalfEdge *eUp = regUp->eUp; GLUhalfEdge *eLo = regLo->eUp; GLUvertex *orgUp = eUp->Org; GLUvertex *orgLo = eLo->Org; GLUvertex *dstUp = eUp->Dst; GLUvertex *dstLo = eLo->Dst; GLdouble tMinUp, tMaxLo; GLUvertex isect, *orgMin; GLUhalfEdge *e; assert( ! VertEq( dstLo, dstUp )); assert( EdgeSign( dstUp, tess->event, orgUp ) <= 0 ); assert( EdgeSign( dstLo, tess->event, orgLo ) >= 0 ); assert( orgUp != tess->event && orgLo != tess->event ); assert( ! regUp->fixUpperEdge && ! regLo->fixUpperEdge ); if( orgUp == orgLo ) return FALSE; /* right endpoints are the same */ tMinUp = MIN( orgUp->t, dstUp->t ); tMaxLo = MAX( orgLo->t, dstLo->t ); if( tMinUp > tMaxLo ) return FALSE; /* t ranges do not overlap */ if( VertLeq( orgUp, orgLo )) { if( EdgeSign( dstLo, orgUp, orgLo ) > 0 ) return FALSE; } else { if( EdgeSign( dstUp, orgLo, orgUp ) < 0 ) return FALSE; } /* At this point the edges intersect, at least marginally */ DebugEvent( tess ); __gl_edgeIntersect( dstUp, orgUp, dstLo, orgLo, &isect ); /* The following properties are guaranteed: */ assert( MIN( orgUp->t, dstUp->t ) <= isect.t ); assert( isect.t <= MAX( orgLo->t, dstLo->t )); assert( MIN( dstLo->s, dstUp->s ) <= isect.s ); assert( isect.s <= MAX( orgLo->s, orgUp->s )); if( VertLeq( &isect, tess->event )) { /* The intersection point lies slightly to the left of the sweep line, * so move it until it''s slightly to the right of the sweep line. * (If we had perfect numerical precision, this would never happen * in the first place). The easiest and safest thing to do is * replace the intersection by tess->event. */ isect.s = tess->event->s; isect.t = tess->event->t; } /* Similarly, if the computed intersection lies to the right of the * rightmost origin (which should rarely happen), it can cause * unbelievable inefficiency on sufficiently degenerate inputs. * (If you have the test program, try running test54.d with the * "X zoom" option turned on). */ orgMin = VertLeq( orgUp, orgLo ) ? orgUp : orgLo; if( VertLeq( orgMin, &isect )) { isect.s = orgMin->s; isect.t = orgMin->t; } if( VertEq( &isect, orgUp ) || VertEq( &isect, orgLo )) { /* Easy case -- intersection at one of the right endpoints */ (void) CheckForRightSplice( tess, regUp ); return FALSE; } if( (! VertEq( dstUp, tess->event ) && EdgeSign( dstUp, tess->event, &isect ) >= 0) || (! VertEq( dstLo, tess->event ) && EdgeSign( dstLo, tess->event, &isect ) <= 0 )) { /* Very unusual -- the new upper or lower edge would pass on the * wrong side of the sweep event, or through it. This can happen * due to very small numerical errors in the intersection calculation. */ if( dstLo == tess->event ) { /* Splice dstLo into eUp, and process the new region(s) */ if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eLo->Sym, eUp ) ) longjmp(tess->env,1); regUp = TopLeftRegion( regUp ); if (regUp == NULL) longjmp(tess->env,1); eUp = RegionBelow(regUp)->eUp; FinishLeftRegions( tess, RegionBelow(regUp), regLo ); AddRightEdges( tess, regUp, eUp->Oprev, eUp, eUp, TRUE ); return TRUE; } if( dstUp == tess->event ) { /* Splice dstUp into eLo, and process the new region(s) */ if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eUp->Lnext, eLo->Oprev ) ) longjmp(tess->env,1); regLo = regUp; regUp = TopRightRegion( regUp ); e = RegionBelow(regUp)->eUp->Rprev; regLo->eUp = eLo->Oprev; eLo = FinishLeftRegions( tess, regLo, NULL ); AddRightEdges( tess, regUp, eLo->Onext, eUp->Rprev, e, TRUE ); return TRUE; } /* Special case: called from ConnectRightVertex. If either * edge passes on the wrong side of tess->event, split it * (and wait for ConnectRightVertex to splice it appropriately). */ if( EdgeSign( dstUp, tess->event, &isect ) >= 0 ) { RegionAbove(regUp)->dirty = regUp->dirty = TRUE; if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); eUp->Org->s = tess->event->s; eUp->Org->t = tess->event->t; } if( EdgeSign( dstLo, tess->event, &isect ) <= 0 ) { regUp->dirty = regLo->dirty = TRUE; if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); eLo->Org->s = tess->event->s; eLo->Org->t = tess->event->t; } /* leave the rest for ConnectRightVertex */ return FALSE; } /* General case -- split both edges, splice into new vertex. * When we do the splice operation, the order of the arguments is * arbitrary as far as correctness goes. However, when the operation * creates a new face, the work done is proportional to the size of * the new face. We expect the faces in the processed part of * the mesh (ie. eUp->Lface) to be smaller than the faces in the * unprocessed original contours (which will be eLo->Oprev->Lface). */ if (__gl_meshSplitEdge( eUp->Sym ) == NULL) longjmp(tess->env,1); if (__gl_meshSplitEdge( eLo->Sym ) == NULL) longjmp(tess->env,1); if ( !__gl_meshSplice( eLo->Oprev, eUp ) ) longjmp(tess->env,1); eUp->Org->s = isect.s; eUp->Org->t = isect.t; eUp->Org->pqHandle = pqInsert( tess->pq, eUp->Org ); /* __gl_pqSortInsert */ if (eUp->Org->pqHandle == LONG_MAX) { pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ tess->pq = NULL; longjmp(tess->env,1); } GetIntersectData( tess, eUp->Org, orgUp, dstUp, orgLo, dstLo ); RegionAbove(regUp)->dirty = regUp->dirty = regLo->dirty = TRUE; return FALSE; } static void WalkDirtyRegions( GLUtesselator *tess, ActiveRegion *regUp ) /* * When the upper or lower edge of any region changes, the region is * marked "dirty". This routine walks through all the dirty regions * and makes sure that the dictionary invariants are satisfied * (see the comments at the beginning of this file). Of course * new dirty regions can be created as we make changes to restore * the invariants. */ { ActiveRegion *regLo = RegionBelow(regUp); GLUhalfEdge *eUp, *eLo; for( ;; ) { /* Find the lowest dirty region (we walk from the bottom up). */ while( regLo->dirty ) { regUp = regLo; regLo = RegionBelow(regLo); } if( ! regUp->dirty ) { regLo = regUp; regUp = RegionAbove( regUp ); if( regUp == NULL || ! regUp->dirty ) { /* We've walked all the dirty regions */ return; } } regUp->dirty = FALSE; eUp = regUp->eUp; eLo = regLo->eUp; if( eUp->Dst != eLo->Dst ) { /* Check that the edge ordering is obeyed at the Dst vertices. */ if( CheckForLeftSplice( tess, regUp )) { /* If the upper or lower edge was marked fixUpperEdge, then * we no longer need it (since these edges are needed only for * vertices which otherwise have no right-going edges). */ if( regLo->fixUpperEdge ) { DeleteRegion( tess, regLo ); if ( !__gl_meshDelete( eLo ) ) longjmp(tess->env,1); regLo = RegionBelow( regUp ); eLo = regLo->eUp; } else if( regUp->fixUpperEdge ) { DeleteRegion( tess, regUp ); if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); regUp = RegionAbove( regLo ); eUp = regUp->eUp; } } } if( eUp->Org != eLo->Org ) { if( eUp->Dst != eLo->Dst && ! regUp->fixUpperEdge && ! regLo->fixUpperEdge && (eUp->Dst == tess->event || eLo->Dst == tess->event) ) { /* When all else fails in CheckForIntersect(), it uses tess->event * as the intersection location. To make this possible, it requires * that tess->event lie between the upper and lower edges, and also * that neither of these is marked fixUpperEdge (since in the worst * case it might splice one of these edges into tess->event, and * violate the invariant that fixable edges are the only right-going * edge from their associated vertex). */ if( CheckForIntersect( tess, regUp )) { /* WalkDirtyRegions() was called recursively; we're done */ return; } } else { /* Even though we can't use CheckForIntersect(), the Org vertices * may violate the dictionary edge ordering. Check and correct this. */ (void) CheckForRightSplice( tess, regUp ); } } if( eUp->Org == eLo->Org && eUp->Dst == eLo->Dst ) { /* A degenerate loop consisting of only two edges -- delete it. */ AddWinding( eLo, eUp ); DeleteRegion( tess, regUp ); if ( !__gl_meshDelete( eUp ) ) longjmp(tess->env,1); regUp = RegionAbove( regLo ); } } } static void ConnectRightVertex( GLUtesselator *tess, ActiveRegion *regUp, GLUhalfEdge *eBottomLeft ) /* * Purpose: connect a "right" vertex vEvent (one where all edges go left) * to the unprocessed portion of the mesh. Since there are no right-going * edges, two regions (one above vEvent and one below) are being merged * into one. "regUp" is the upper of these two regions. * * There are two reasons for doing this (adding a right-going edge): * - if the two regions being merged are "inside", we must add an edge * to keep them separated (the combined region would not be monotone). * - in any case, we must leave some record of vEvent in the dictionary, * so that we can merge vEvent with features that we have not seen yet. * For example, maybe there is a vertical edge which passes just to * the right of vEvent; we would like to splice vEvent into this edge. * * However, we don't want to connect vEvent to just any vertex. We don''t * want the new edge to cross any other edges; otherwise we will create * intersection vertices even when the input data had no self-intersections. * (This is a bad thing; if the user's input data has no intersections, * we don't want to generate any false intersections ourselves.) * * Our eventual goal is to connect vEvent to the leftmost unprocessed * vertex of the combined region (the union of regUp and regLo). * But because of unseen vertices with all right-going edges, and also * new vertices which may be created by edge intersections, we don''t * know where that leftmost unprocessed vertex is. In the meantime, we * connect vEvent to the closest vertex of either chain, and mark the region * as "fixUpperEdge". This flag says to delete and reconnect this edge * to the next processed vertex on the boundary of the combined region. * Quite possibly the vertex we connected to will turn out to be the * closest one, in which case we won''t need to make any changes. */ { GLUhalfEdge *eNew; GLUhalfEdge *eTopLeft = eBottomLeft->Onext; ActiveRegion *regLo = RegionBelow(regUp); GLUhalfEdge *eUp = regUp->eUp; GLUhalfEdge *eLo = regLo->eUp; int degenerate = FALSE; if( eUp->Dst != eLo->Dst ) { (void) CheckForIntersect( tess, regUp ); } /* Possible new degeneracies: upper or lower edge of regUp may pass * through vEvent, or may coincide with new intersection vertex */ if( VertEq( eUp->Org, tess->event )) { if ( !__gl_meshSplice( eTopLeft->Oprev, eUp ) ) longjmp(tess->env,1); regUp = TopLeftRegion( regUp ); if (regUp == NULL) longjmp(tess->env,1); eTopLeft = RegionBelow( regUp )->eUp; FinishLeftRegions( tess, RegionBelow(regUp), regLo ); degenerate = TRUE; } if( VertEq( eLo->Org, tess->event )) { if ( !__gl_meshSplice( eBottomLeft, eLo->Oprev ) ) longjmp(tess->env,1); eBottomLeft = FinishLeftRegions( tess, regLo, NULL ); degenerate = TRUE; } if( degenerate ) { AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); return; } /* Non-degenerate situation -- need to add a temporary, fixable edge. * Connect to the closer of eLo->Org, eUp->Org. */ if( VertLeq( eLo->Org, eUp->Org )) { eNew = eLo->Oprev; } else { eNew = eUp; } eNew = __gl_meshConnect( eBottomLeft->Lprev, eNew ); if (eNew == NULL) longjmp(tess->env,1); /* Prevent cleanup, otherwise eNew might disappear before we've even * had a chance to mark it as a temporary edge. */ AddRightEdges( tess, regUp, eNew, eNew->Onext, eNew->Onext, FALSE ); eNew->Sym->activeRegion->fixUpperEdge = TRUE; WalkDirtyRegions( tess, regUp ); } /* Because vertices at exactly the same location are merged together * before we process the sweep event, some degenerate cases can't occur. * However if someone eventually makes the modifications required to * merge features which are close together, the cases below marked * TOLERANCE_NONZERO will be useful. They were debugged before the * code to merge identical vertices in the main loop was added. */ #define TOLERANCE_NONZERO FALSE static void ConnectLeftDegenerate( GLUtesselator *tess, ActiveRegion *regUp, GLUvertex *vEvent ) /* * The event vertex lies exacty on an already-processed edge or vertex. * Adding the new vertex involves splicing it into the already-processed * part of the mesh. */ { GLUhalfEdge *e, *eTopLeft, *eTopRight, *eLast; ActiveRegion *reg; e = regUp->eUp; if( VertEq( e->Org, vEvent )) { /* e->Org is an unprocessed vertex - just combine them, and wait * for e->Org to be pulled from the queue */ assert( TOLERANCE_NONZERO ); SpliceMergeVertices( tess, e, vEvent->anEdge ); return; } if( ! VertEq( e->Dst, vEvent )) { /* General case -- splice vEvent into edge e which passes through it */ if (__gl_meshSplitEdge( e->Sym ) == NULL) longjmp(tess->env,1); if( regUp->fixUpperEdge ) { /* This edge was fixable -- delete unused portion of original edge */ if ( !__gl_meshDelete( e->Onext ) ) longjmp(tess->env,1); regUp->fixUpperEdge = FALSE; } if ( !__gl_meshSplice( vEvent->anEdge, e ) ) longjmp(tess->env,1); SweepEvent( tess, vEvent ); /* recurse */ return; } /* vEvent coincides with e->Dst, which has already been processed. * Splice in the additional right-going edges. */ assert( TOLERANCE_NONZERO ); regUp = TopRightRegion( regUp ); reg = RegionBelow( regUp ); eTopRight = reg->eUp->Sym; eTopLeft = eLast = eTopRight->Onext; if( reg->fixUpperEdge ) { /* Here e->Dst has only a single fixable edge going right. * We can delete it since now we have some real right-going edges. */ assert( eTopLeft != eTopRight ); /* there are some left edges too */ DeleteRegion( tess, reg ); if ( !__gl_meshDelete( eTopRight ) ) longjmp(tess->env,1); eTopRight = eTopLeft->Oprev; } if ( !__gl_meshSplice( vEvent->anEdge, eTopRight ) ) longjmp(tess->env,1); if( ! EdgeGoesLeft( eTopLeft )) { /* e->Dst had no left-going edges -- indicate this to AddRightEdges() */ eTopLeft = NULL; } AddRightEdges( tess, regUp, eTopRight->Onext, eLast, eTopLeft, TRUE ); } static void ConnectLeftVertex( GLUtesselator *tess, GLUvertex *vEvent ) /* * Purpose: connect a "left" vertex (one where both edges go right) * to the processed portion of the mesh. Let R be the active region * containing vEvent, and let U and L be the upper and lower edge * chains of R. There are two possibilities: * * - the normal case: split R into two regions, by connecting vEvent to * the rightmost vertex of U or L lying to the left of the sweep line * * - the degenerate case: if vEvent is close enough to U or L, we * merge vEvent into that edge chain. The subcases are: * - merging with the rightmost vertex of U or L * - merging with the active edge of U or L * - merging with an already-processed portion of U or L */ { ActiveRegion *regUp, *regLo, *reg; GLUhalfEdge *eUp, *eLo, *eNew; ActiveRegion tmp; /* assert( vEvent->anEdge->Onext->Onext == vEvent->anEdge ); */ /* Get a pointer to the active region containing vEvent */ tmp.eUp = vEvent->anEdge->Sym; /* __GL_DICTLISTKEY */ /* __gl_dictListSearch */ regUp = (ActiveRegion *)dictKey( dictSearch( tess->dict, &tmp )); regLo = RegionBelow( regUp ); eUp = regUp->eUp; eLo = regLo->eUp; /* Try merging with U or L first */ if( EdgeSign( eUp->Dst, vEvent, eUp->Org ) == 0 ) { ConnectLeftDegenerate( tess, regUp, vEvent ); return; } /* Connect vEvent to rightmost processed vertex of either chain. * e->Dst is the vertex that we will connect to vEvent. */ reg = VertLeq( eLo->Dst, eUp->Dst ) ? regUp : regLo; if( regUp->inside || reg->fixUpperEdge) { if( reg == regUp ) { eNew = __gl_meshConnect( vEvent->anEdge->Sym, eUp->Lnext ); if (eNew == NULL) longjmp(tess->env,1); } else { GLUhalfEdge *tempHalfEdge= __gl_meshConnect( eLo->Dnext, vEvent->anEdge); if (tempHalfEdge == NULL) longjmp(tess->env,1); eNew = tempHalfEdge->Sym; } if( reg->fixUpperEdge ) { if ( !FixUpperEdge( reg, eNew ) ) longjmp(tess->env,1); } else { ComputeWinding( tess, AddRegionBelow( tess, regUp, eNew )); } SweepEvent( tess, vEvent ); } else { /* The new vertex is in a region which does not belong to the polygon. * We don''t need to connect this vertex to the rest of the mesh. */ AddRightEdges( tess, regUp, vEvent->anEdge, vEvent->anEdge, NULL, TRUE ); } } static void SweepEvent( GLUtesselator *tess, GLUvertex *vEvent ) /* * Does everything necessary when the sweep line crosses a vertex. * Updates the mesh and the edge dictionary. */ { ActiveRegion *regUp, *reg; GLUhalfEdge *e, *eTopLeft, *eBottomLeft; tess->event = vEvent; /* for access in EdgeLeq() */ DebugEvent( tess ); /* Check if this vertex is the right endpoint of an edge that is * already in the dictionary. In this case we don't need to waste * time searching for the location to insert new edges. */ e = vEvent->anEdge; while( e->activeRegion == NULL ) { e = e->Onext; if( e == vEvent->anEdge ) { /* All edges go right -- not incident to any processed edges */ ConnectLeftVertex( tess, vEvent ); return; } } /* Processing consists of two phases: first we "finish" all the * active regions where both the upper and lower edges terminate * at vEvent (ie. vEvent is closing off these regions). * We mark these faces "inside" or "outside" the polygon according * to their winding number, and delete the edges from the dictionary. * This takes care of all the left-going edges from vEvent. */ regUp = TopLeftRegion( e->activeRegion ); if (regUp == NULL) longjmp(tess->env,1); reg = RegionBelow( regUp ); eTopLeft = reg->eUp; eBottomLeft = FinishLeftRegions( tess, reg, NULL ); /* Next we process all the right-going edges from vEvent. This * involves adding the edges to the dictionary, and creating the * associated "active regions" which record information about the * regions between adjacent dictionary edges. */ if( eBottomLeft->Onext == eTopLeft ) { /* No right-going edges -- add a temporary "fixable" edge */ ConnectRightVertex( tess, regUp, eBottomLeft ); } else { AddRightEdges( tess, regUp, eBottomLeft->Onext, eTopLeft, eTopLeft, TRUE ); } } /* Make the sentinel coordinates big enough that they will never be * merged with real input features. (Even with the largest possible * input contour and the maximum tolerance of 1.0, no merging will be * done with coordinates larger than 3 * GLU_TESS_MAX_COORD). */ #define SENTINEL_COORD (4 * GLU_TESS_MAX_COORD) static void AddSentinel( GLUtesselator *tess, GLdouble t ) /* * We add two sentinel edges above and below all other edges, * to avoid special cases at the top and bottom. */ { GLUhalfEdge *e; ActiveRegion *reg = (ActiveRegion *)memAlloc( sizeof( ActiveRegion )); if (reg == NULL) longjmp(tess->env,1); e = __gl_meshMakeEdge( tess->mesh ); if (e == NULL) longjmp(tess->env,1); e->Org->s = SENTINEL_COORD; e->Org->t = t; e->Dst->s = -SENTINEL_COORD; e->Dst->t = t; tess->event = e->Dst; /* initialize it */ reg->eUp = e; reg->windingNumber = 0; reg->inside = FALSE; reg->fixUpperEdge = FALSE; reg->sentinel = TRUE; reg->dirty = FALSE; reg->nodeUp = dictInsert( tess->dict, reg ); /* __gl_dictListInsertBefore */ if (reg->nodeUp == NULL) longjmp(tess->env,1); } static void InitEdgeDict( GLUtesselator *tess ) /* * We maintain an ordering of edge intersections with the sweep line. * This order is maintained in a dynamic dictionary. */ { /* __gl_dictListNewDict */ tess->dict = dictNewDict( tess, (int (*)(void *, DictKey, DictKey)) EdgeLeq ); if (tess->dict == NULL) longjmp(tess->env,1); AddSentinel( tess, -SENTINEL_COORD ); AddSentinel( tess, SENTINEL_COORD ); } static void DoneEdgeDict( GLUtesselator *tess ) { ActiveRegion *reg; #ifndef NDEBUG int fixedEdges = 0; #endif /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ while( (reg = (ActiveRegion *)dictKey( dictMin( tess->dict ))) != NULL ) { /* * At the end of all processing, the dictionary should contain * only the two sentinel edges, plus at most one "fixable" edge * created by ConnectRightVertex(). */ if( ! reg->sentinel ) { assert( reg->fixUpperEdge ); assert( ++fixedEdges == 1 ); } assert( reg->windingNumber == 0 ); DeleteRegion( tess, reg ); /* __gl_meshDelete( reg->eUp );*/ } dictDeleteDict( tess->dict ); /* __gl_dictListDeleteDict */ } static void RemoveDegenerateEdges( GLUtesselator *tess ) /* * Remove zero-length edges, and contours with fewer than 3 vertices. */ { GLUhalfEdge *e, *eNext, *eLnext; GLUhalfEdge *eHead = &tess->mesh->eHead; /*LINTED*/ for( e = eHead->next; e != eHead; e = eNext ) { eNext = e->next; eLnext = e->Lnext; if( VertEq( e->Org, e->Dst ) && e->Lnext->Lnext != e ) { /* Zero-length edge, contour has at least 3 edges */ SpliceMergeVertices( tess, eLnext, e ); /* deletes e->Org */ if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); /* e is a self-loop */ e = eLnext; eLnext = e->Lnext; } if( eLnext->Lnext == e ) { /* Degenerate contour (one or two edges) */ if( eLnext != e ) { if( eLnext == eNext || eLnext == eNext->Sym ) { eNext = eNext->next; } if ( !__gl_meshDelete( eLnext ) ) longjmp(tess->env,1); } if( e == eNext || e == eNext->Sym ) { eNext = eNext->next; } if ( !__gl_meshDelete( e ) ) longjmp(tess->env,1); } } } static int InitPriorityQ( GLUtesselator *tess ) /* * Insert all vertices into the priority queue which determines the * order in which vertices cross the sweep line. */ { PriorityQ *pq; GLUvertex *v, *vHead; /* __gl_pqSortNewPriorityQ */ pq = tess->pq = pqNewPriorityQ( (int (*)(PQkey, PQkey)) __gl_vertLeq ); if (pq == NULL) return 0; vHead = &tess->mesh->vHead; for( v = vHead->next; v != vHead; v = v->next ) { v->pqHandle = pqInsert( pq, v ); /* __gl_pqSortInsert */ if (v->pqHandle == LONG_MAX) break; } if (v != vHead || !pqInit( pq ) ) { /* __gl_pqSortInit */ pqDeletePriorityQ(tess->pq); /* __gl_pqSortDeletePriorityQ */ tess->pq = NULL; return 0; } return 1; } static void DonePriorityQ( GLUtesselator *tess ) { pqDeletePriorityQ( tess->pq ); /* __gl_pqSortDeletePriorityQ */ } static int RemoveDegenerateFaces( GLUmesh *mesh ) /* * Delete any degenerate faces with only two edges. WalkDirtyRegions() * will catch almost all of these, but it won't catch degenerate faces * produced by splice operations on already-processed edges. * The two places this can happen are in FinishLeftRegions(), when * we splice in a "temporary" edge produced by ConnectRightVertex(), * and in CheckForLeftSplice(), where we splice already-processed * edges to ensure that our dictionary invariants are not violated * by numerical errors. * * In both these cases it is *very* dangerous to delete the offending * edge at the time, since one of the routines further up the stack * will sometimes be keeping a pointer to that edge. */ { GLUface *f, *fNext; GLUhalfEdge *e; /*LINTED*/ for( f = mesh->fHead.next; f != &mesh->fHead; f = fNext ) { fNext = f->next; e = f->anEdge; assert( e->Lnext != e ); if( e->Lnext->Lnext == e ) { /* A face with only two edges */ AddWinding( e->Onext, e ); if ( !__gl_meshDelete( e ) ) return 0; } } return 1; } int __gl_computeInterior( GLUtesselator *tess ) /* * __gl_computeInterior( tess ) computes the planar arrangement specified * by the given contours, and further subdivides this arrangement * into regions. Each region is marked "inside" if it belongs * to the polygon, according to the rule given by tess->windingRule. * Each interior region is guaranteed be monotone. */ { GLUvertex *v, *vNext; tess->fatalError = FALSE; /* Each vertex defines an event for our sweep line. Start by inserting * all the vertices in a priority queue. Events are processed in * lexicographic order, ie. * * e1 < e2 iff e1.x < e2.x || (e1.x == e2.x && e1.y < e2.y) */ RemoveDegenerateEdges( tess ); if ( !InitPriorityQ( tess ) ) return 0; /* if error */ InitEdgeDict( tess ); /* __gl_pqSortExtractMin */ while( (v = (GLUvertex *)pqExtractMin( tess->pq )) != NULL ) { for( ;; ) { vNext = (GLUvertex *)pqMinimum( tess->pq ); /* __gl_pqSortMinimum */ if( vNext == NULL || ! VertEq( vNext, v )) break; /* Merge together all vertices at exactly the same location. * This is more efficient than processing them one at a time, * simplifies the code (see ConnectLeftDegenerate), and is also * important for correct handling of certain degenerate cases. * For example, suppose there are two identical edges A and B * that belong to different contours (so without this code they would * be processed by separate sweep events). Suppose another edge C * crosses A and B from above. When A is processed, we split it * at its intersection point with C. However this also splits C, * so when we insert B we may compute a slightly different * intersection point. This might leave two edges with a small * gap between them. This kind of error is especially obvious * when using boundary extraction (GLU_TESS_BOUNDARY_ONLY). */ vNext = (GLUvertex *)pqExtractMin( tess->pq ); /* __gl_pqSortExtractMin*/ SpliceMergeVertices( tess, v->anEdge, vNext->anEdge ); } SweepEvent( tess, v ); } /* Set tess->event for debugging purposes */ /* __GL_DICTLISTKEY */ /* __GL_DICTLISTMIN */ tess->event = ((ActiveRegion *) dictKey( dictMin( tess->dict )))->eUp->Org; DebugEvent( tess ); DoneEdgeDict( tess ); DonePriorityQ( tess ); if ( !RemoveDegenerateFaces( tess->mesh ) ) return 0; __gl_meshCheckMesh( tess->mesh ); return 1; } muffin-6.4.1/cogl/cogl-path/tesselator/tess.c0000664000175000017500000004230514723361714020057 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include "gluos.h" #include #include #include #include "memalloc.h" #include "tess.h" #include "mesh.h" #include "normal.h" #include "sweep.h" #include "tessmono.h" #include "render.h" #define GLU_TESS_DEFAULT_TOLERANCE 0.0 #define GLU_TESS_MESH 100112 /* void (*)(GLUmesh *mesh) */ #ifndef TRUE #define TRUE 1 #endif #ifndef FALSE #define FALSE 0 #endif /*ARGSUSED*/ static void GLAPIENTRY noBegin( GLenum type ) {} /*ARGSUSED*/ static void GLAPIENTRY noEdgeFlag( GLboolean boundaryEdge ) {} /*ARGSUSED*/ static void GLAPIENTRY noVertex( void *data ) {} /*ARGSUSED*/ static void GLAPIENTRY noEnd( void ) {} /*ARGSUSED*/ static void GLAPIENTRY noError( GLenum errnum ) {} /*ARGSUSED*/ static void GLAPIENTRY noCombine( GLdouble coords[3], void *data[4], GLfloat weight[4], void **dataOut ) {} /*ARGSUSED*/ static void GLAPIENTRY noMesh( GLUmesh *mesh ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noEndData( void *polygonData ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData ) {} /*ARGSUSED*/ void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4], GLfloat weight[4], void **outData, void *polygonData ) {} /* Half-edges are allocated in pairs (see mesh.c) */ typedef struct { GLUhalfEdge e, eSym; } EdgePair; #undef MAX #define MAX(a,b) ((a) > (b) ? (a) : (b)) #define MAX_FAST_ALLOC (MAX(sizeof(EdgePair), \ MAX(sizeof(GLUvertex),sizeof(GLUface)))) GLUtesselator * GLAPIENTRY gluNewTess( void ) { GLUtesselator *tess; /* Only initialize fields which can be changed by the api. Other fields * are initialized where they are used. */ if (memInit( MAX_FAST_ALLOC ) == 0) { return 0; /* out of memory */ } tess = (GLUtesselator *)memAlloc( sizeof( GLUtesselator )); if (tess == NULL) { return 0; /* out of memory */ } tess->state = T_DORMANT; tess->normal[0] = 0; tess->normal[1] = 0; tess->normal[2] = 0; tess->relTolerance = GLU_TESS_DEFAULT_TOLERANCE; tess->windingRule = GLU_TESS_WINDING_ODD; tess->flagBoundary = FALSE; tess->boundaryOnly = FALSE; tess->callBegin = &noBegin; tess->callEdgeFlag = &noEdgeFlag; tess->callVertex = &noVertex; tess->callEnd = &noEnd; tess->callError = &noError; tess->callCombine = &noCombine; tess->callMesh = &noMesh; tess->callBeginData= &__gl_noBeginData; tess->callEdgeFlagData= &__gl_noEdgeFlagData; tess->callVertexData= &__gl_noVertexData; tess->callEndData= &__gl_noEndData; tess->callErrorData= &__gl_noErrorData; tess->callCombineData= &__gl_noCombineData; tess->polygonData= NULL; return tess; } static void MakeDormant( GLUtesselator *tess ) { /* Return the tessellator to its original dormant state. */ if( tess->mesh != NULL ) { __gl_meshDeleteMesh( tess->mesh ); } tess->state = T_DORMANT; tess->lastEdge = NULL; tess->mesh = NULL; } #define RequireState( tess, s ) if( tess->state != s ) GotoState(tess,s) static void GotoState( GLUtesselator *tess, enum TessState newState ) { while( tess->state != newState ) { /* We change the current state one level at a time, to get to * the desired state. */ if( tess->state < newState ) { switch( tess->state ) { case T_DORMANT: CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_POLYGON ); gluTessBeginPolygon( tess, NULL ); break; case T_IN_POLYGON: CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_BEGIN_CONTOUR ); gluTessBeginContour( tess ); break; default: ; } } else { switch( tess->state ) { case T_IN_CONTOUR: CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_CONTOUR ); gluTessEndContour( tess ); break; case T_IN_POLYGON: CALL_ERROR_OR_ERROR_DATA( GLU_TESS_MISSING_END_POLYGON ); /* gluTessEndPolygon( tess ) is too much work! */ MakeDormant( tess ); break; default: ; } } } } void GLAPIENTRY gluDeleteTess( GLUtesselator *tess ) { RequireState( tess, T_DORMANT ); memFree( tess ); } void GLAPIENTRY gluTessProperty( GLUtesselator *tess, GLenum which, GLdouble value ) { GLenum windingRule; switch( which ) { case GLU_TESS_TOLERANCE: if( value < 0.0 || value > 1.0 ) break; tess->relTolerance = value; return; case GLU_TESS_WINDING_RULE: windingRule = (GLenum) value; if( windingRule != value ) break; /* not an integer */ switch( windingRule ) { case GLU_TESS_WINDING_ODD: case GLU_TESS_WINDING_NONZERO: case GLU_TESS_WINDING_POSITIVE: case GLU_TESS_WINDING_NEGATIVE: case GLU_TESS_WINDING_ABS_GEQ_TWO: tess->windingRule = windingRule; return; default: break; } case GLU_TESS_BOUNDARY_ONLY: tess->boundaryOnly = (value != 0); return; default: CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); return; } CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_VALUE ); } /* Returns tessellator property */ void GLAPIENTRY gluGetTessProperty( GLUtesselator *tess, GLenum which, GLdouble *value ) { switch (which) { case GLU_TESS_TOLERANCE: /* tolerance should be in range [0..1] */ assert(0.0 <= tess->relTolerance && tess->relTolerance <= 1.0); *value= tess->relTolerance; break; case GLU_TESS_WINDING_RULE: assert(tess->windingRule == GLU_TESS_WINDING_ODD || tess->windingRule == GLU_TESS_WINDING_NONZERO || tess->windingRule == GLU_TESS_WINDING_POSITIVE || tess->windingRule == GLU_TESS_WINDING_NEGATIVE || tess->windingRule == GLU_TESS_WINDING_ABS_GEQ_TWO); *value= tess->windingRule; break; case GLU_TESS_BOUNDARY_ONLY: assert(tess->boundaryOnly == TRUE || tess->boundaryOnly == FALSE); *value= tess->boundaryOnly; break; default: *value= 0.0; CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); break; } } /* gluGetTessProperty() */ void GLAPIENTRY gluTessNormal( GLUtesselator *tess, GLdouble x, GLdouble y, GLdouble z ) { tess->normal[0] = x; tess->normal[1] = y; tess->normal[2] = z; } void GLAPIENTRY gluTessCallback( GLUtesselator *tess, GLenum which, _GLUfuncptr fn) { switch( which ) { case GLU_TESS_BEGIN: tess->callBegin = (fn == NULL) ? &noBegin : (void (GLAPIENTRY *)(GLenum)) fn; return; case GLU_TESS_BEGIN_DATA: tess->callBeginData = (fn == NULL) ? &__gl_noBeginData : (void (GLAPIENTRY *)(GLenum, void *)) fn; return; case GLU_TESS_EDGE_FLAG: tess->callEdgeFlag = (fn == NULL) ? &noEdgeFlag : (void (GLAPIENTRY *)(GLboolean)) fn; /* If the client wants boundary edges to be flagged, * we render everything as separate triangles (no strips or fans). */ tess->flagBoundary = (fn != NULL); return; case GLU_TESS_EDGE_FLAG_DATA: tess->callEdgeFlagData= (fn == NULL) ? &__gl_noEdgeFlagData : (void (GLAPIENTRY *)(GLboolean, void *)) fn; /* If the client wants boundary edges to be flagged, * we render everything as separate triangles (no strips or fans). */ tess->flagBoundary = (fn != NULL); return; case GLU_TESS_VERTEX: tess->callVertex = (fn == NULL) ? &noVertex : (void (GLAPIENTRY *)(void *)) fn; return; case GLU_TESS_VERTEX_DATA: tess->callVertexData = (fn == NULL) ? &__gl_noVertexData : (void (GLAPIENTRY *)(void *, void *)) fn; return; case GLU_TESS_END: tess->callEnd = (fn == NULL) ? &noEnd : (void (GLAPIENTRY *)(void)) fn; return; case GLU_TESS_END_DATA: tess->callEndData = (fn == NULL) ? &__gl_noEndData : (void (GLAPIENTRY *)(void *)) fn; return; case GLU_TESS_ERROR: tess->callError = (fn == NULL) ? &noError : (void (GLAPIENTRY *)(GLenum)) fn; return; case GLU_TESS_ERROR_DATA: tess->callErrorData = (fn == NULL) ? &__gl_noErrorData : (void (GLAPIENTRY *)(GLenum, void *)) fn; return; case GLU_TESS_COMBINE: tess->callCombine = (fn == NULL) ? &noCombine : (void (GLAPIENTRY *)(GLdouble [3],void *[4], GLfloat [4], void ** )) fn; return; case GLU_TESS_COMBINE_DATA: tess->callCombineData = (fn == NULL) ? &__gl_noCombineData : (void (GLAPIENTRY *)(GLdouble [3], void *[4], GLfloat [4], void **, void *)) fn; return; case GLU_TESS_MESH: tess->callMesh = (fn == NULL) ? &noMesh : (void (GLAPIENTRY *)(GLUmesh *)) fn; return; default: CALL_ERROR_OR_ERROR_DATA( GLU_INVALID_ENUM ); return; } } static int AddVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) { GLUhalfEdge *e; e = tess->lastEdge; if( e == NULL ) { /* Make a self-loop (one vertex, one edge). */ e = __gl_meshMakeEdge( tess->mesh ); if (e == NULL) return 0; if ( !__gl_meshSplice( e, e->Sym ) ) return 0; } else { /* Create a new vertex and edge which immediately follow e * in the ordering around the left face. */ if (__gl_meshSplitEdge( e ) == NULL) return 0; e = e->Lnext; } /* The new vertex is now e->Org. */ e->Org->data = data; e->Org->coords[0] = coords[0]; e->Org->coords[1] = coords[1]; e->Org->coords[2] = coords[2]; /* The winding of an edge says how the winding number changes as we * cross from the edge''s right face to its left face. We add the * vertices in such an order that a CCW contour will add +1 to * the winding number of the region inside the contour. */ e->winding = 1; e->Sym->winding = -1; tess->lastEdge = e; return 1; } static void CacheVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) { CachedVertex *v = &tess->cache[tess->cacheCount]; v->data = data; v->coords[0] = coords[0]; v->coords[1] = coords[1]; v->coords[2] = coords[2]; ++tess->cacheCount; } static int EmptyCache( GLUtesselator *tess ) { CachedVertex *v = tess->cache; CachedVertex *vLast; tess->mesh = __gl_meshNewMesh(); if (tess->mesh == NULL) return 0; for( vLast = v + tess->cacheCount; v < vLast; ++v ) { if ( !AddVertex( tess, v->coords, v->data ) ) return 0; } tess->cacheCount = 0; tess->emptyCache = FALSE; return 1; } void GLAPIENTRY gluTessVertex( GLUtesselator *tess, GLdouble coords[3], void *data ) { int i, tooLarge = FALSE; GLdouble x, clamped[3]; RequireState( tess, T_IN_CONTOUR ); if( tess->emptyCache ) { if ( !EmptyCache( tess ) ) { CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); return; } tess->lastEdge = NULL; } for( i = 0; i < 3; ++i ) { x = coords[i]; if( x < - GLU_TESS_MAX_COORD ) { x = - GLU_TESS_MAX_COORD; tooLarge = TRUE; } if( x > GLU_TESS_MAX_COORD ) { x = GLU_TESS_MAX_COORD; tooLarge = TRUE; } clamped[i] = x; } if( tooLarge ) { CALL_ERROR_OR_ERROR_DATA( GLU_TESS_COORD_TOO_LARGE ); } if( tess->mesh == NULL ) { if( tess->cacheCount < TESS_MAX_CACHE ) { CacheVertex( tess, clamped, data ); return; } if ( !EmptyCache( tess ) ) { CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); return; } } if ( !AddVertex( tess, clamped, data ) ) { CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); } } void GLAPIENTRY gluTessBeginPolygon( GLUtesselator *tess, void *data ) { RequireState( tess, T_DORMANT ); tess->state = T_IN_POLYGON; tess->cacheCount = 0; tess->emptyCache = FALSE; tess->mesh = NULL; tess->polygonData= data; } void GLAPIENTRY gluTessBeginContour( GLUtesselator *tess ) { RequireState( tess, T_IN_POLYGON ); tess->state = T_IN_CONTOUR; tess->lastEdge = NULL; if( tess->cacheCount > 0 ) { /* Just set a flag so we don't get confused by empty contours * -- these can be generated accidentally with the obsolete * NextContour() interface. */ tess->emptyCache = TRUE; } } void GLAPIENTRY gluTessEndContour( GLUtesselator *tess ) { RequireState( tess, T_IN_CONTOUR ); tess->state = T_IN_POLYGON; } void GLAPIENTRY gluTessEndPolygon( GLUtesselator *tess ) { GLUmesh *mesh; if (setjmp(tess->env) != 0) { /* come back here if out of memory */ CALL_ERROR_OR_ERROR_DATA( GLU_OUT_OF_MEMORY ); return; } RequireState( tess, T_IN_POLYGON ); tess->state = T_DORMANT; if( tess->mesh == NULL ) { if( ! tess->flagBoundary && tess->callMesh == &noMesh ) { /* Try some special code to make the easy cases go quickly * (eg. convex polygons). This code does NOT handle multiple contours, * intersections, edge flags, and of course it does not generate * an explicit mesh either. */ if( __gl_renderCache( tess )) { tess->polygonData= NULL; return; } } if ( !EmptyCache( tess ) ) longjmp(tess->env,1); /* could've used a label*/ } /* Determine the polygon normal and project vertices onto the plane * of the polygon. */ __gl_projectPolygon( tess ); /* __gl_computeInterior( tess ) computes the planar arrangement specified * by the given contours, and further subdivides this arrangement * into regions. Each region is marked "inside" if it belongs * to the polygon, according to the rule given by tess->windingRule. * Each interior region is guaranteed be monotone. */ if ( !__gl_computeInterior( tess ) ) { longjmp(tess->env,1); /* could've used a label */ } mesh = tess->mesh; if( ! tess->fatalError ) { int rc = 1; /* If the user wants only the boundary contours, we throw away all edges * except those which separate the interior from the exterior. * Otherwise we tessellate all the regions marked "inside". */ if( tess->boundaryOnly ) { rc = __gl_meshSetWindingNumber( mesh, 1, TRUE ); } else { rc = __gl_meshTessellateInterior( mesh ); } if (rc == 0) longjmp(tess->env,1); /* could've used a label */ __gl_meshCheckMesh( mesh ); if( tess->callBegin != &noBegin || tess->callEnd != &noEnd || tess->callVertex != &noVertex || tess->callEdgeFlag != &noEdgeFlag || tess->callBeginData != &__gl_noBeginData || tess->callEndData != &__gl_noEndData || tess->callVertexData != &__gl_noVertexData || tess->callEdgeFlagData != &__gl_noEdgeFlagData ) { if( tess->boundaryOnly ) { __gl_renderBoundary( tess, mesh ); /* output boundary contours */ } else { __gl_renderMesh( tess, mesh ); /* output strips and fans */ } } if( tess->callMesh != &noMesh ) { /* Throw away the exterior faces, so that all faces are interior. * This way the user doesn't have to check the "inside" flag, * and we don't need to even reveal its existence. It also leaves * the freedom for an implementation to not generate the exterior * faces in the first place. */ __gl_meshDiscardExterior( mesh ); (*tess->callMesh)( mesh ); /* user wants the mesh itself */ tess->mesh = NULL; tess->polygonData= NULL; return; } } __gl_meshDeleteMesh( mesh ); tess->polygonData= NULL; tess->mesh = NULL; } /*XXXblythe unused function*/ #if 0 void GLAPIENTRY gluDeleteMesh( GLUmesh *mesh ) { __gl_meshDeleteMesh( mesh ); } #endif /*******************************************************/ /* Obsolete calls -- for backward compatibility */ void GLAPIENTRY gluBeginPolygon( GLUtesselator *tess ) { gluTessBeginPolygon( tess, NULL ); gluTessBeginContour( tess ); } /*ARGSUSED*/ void GLAPIENTRY gluNextContour( GLUtesselator *tess, GLenum type ) { gluTessEndContour( tess ); gluTessBeginContour( tess ); } void GLAPIENTRY gluEndPolygon( GLUtesselator *tess ) { gluTessEndContour( tess ); gluTessEndPolygon( tess ); } muffin-6.4.1/cogl/cogl-path/tesselator/sweep.h0000664000175000017500000000651314723361714020232 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __sweep_h_ #define __sweep_h_ #include "mesh.h" #include "tesselator.h" /* __gl_computeInterior( tess ) computes the planar arrangement specified * by the given contours, and further subdivides this arrangement * into regions. Each region is marked "inside" if it belongs * to the polygon, according to the rule given by tess->windingRule. * Each interior region is guaranteed be monotone. */ int __gl_computeInterior( GLUtesselator *tess ); /* The following is here *only* for access by debugging routines */ #include "dict.h" /* For each pair of adjacent edges crossing the sweep line, there is * an ActiveRegion to represent the region between them. The active * regions are kept in sorted order in a dynamic dictionary. As the * sweep line crosses each vertex, we update the affected regions. */ struct ActiveRegion { GLUhalfEdge *eUp; /* upper edge, directed right to left */ DictNode *nodeUp; /* dictionary node corresponding to eUp */ int windingNumber; /* used to determine which regions are * inside the polygon */ GLboolean inside; /* is this region inside the polygon? */ GLboolean sentinel; /* marks fake edges at t = +/-infinity */ GLboolean dirty; /* marks regions where the upper or lower * edge has changed, but we haven't checked * whether they intersect yet */ GLboolean fixUpperEdge; /* marks temporary edges introduced when * we process a "right vertex" (one without * any edges leaving to the right) */ }; #define RegionBelow(r) ((ActiveRegion *) dictKey(dictPred((r)->nodeUp))) #define RegionAbove(r) ((ActiveRegion *) dictKey(dictSucc((r)->nodeUp))) #endif muffin-6.4.1/cogl/cogl-path/tesselator/tess.h0000664000175000017500000001442114723361714020062 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __tess_h_ #define __tess_h_ #include #include "mesh.h" #include "dict.h" #include "priorityq.h" /* The begin/end calls must be properly nested. We keep track of * the current state to enforce the ordering. */ enum TessState { T_DORMANT, T_IN_POLYGON, T_IN_CONTOUR }; /* We cache vertex data for single-contour polygons so that we can * try a quick-and-dirty decomposition first. */ #define TESS_MAX_CACHE 100 typedef struct CachedVertex { GLdouble coords[3]; void *data; } CachedVertex; struct GLUtesselator { /*** state needed for collecting the input data ***/ enum TessState state; /* what begin/end calls have we seen? */ GLUhalfEdge *lastEdge; /* lastEdge->Org is the most recent vertex */ GLUmesh *mesh; /* stores the input contours, and eventually the tessellation itself */ void (GLAPIENTRY *callError)( GLenum errnum ); /*** state needed for projecting onto the sweep plane ***/ GLdouble normal[3]; /* user-specified normal (if provided) */ GLdouble sUnit[3]; /* unit vector in s-direction (debugging) */ GLdouble tUnit[3]; /* unit vector in t-direction (debugging) */ /*** state needed for the line sweep ***/ GLdouble relTolerance; /* tolerance for merging features */ GLenum windingRule; /* rule for determining polygon interior */ GLboolean fatalError; /* fatal error: needed combine callback */ Dict *dict; /* edge dictionary for sweep line */ PriorityQ *pq; /* priority queue of vertex events */ GLUvertex *event; /* current sweep event being processed */ void (GLAPIENTRY *callCombine)( GLdouble coords[3], void *data[4], GLfloat weight[4], void **outData ); /*** state needed for rendering callbacks (see render.c) ***/ GLboolean flagBoundary; /* mark boundary edges (use EdgeFlag) */ GLboolean boundaryOnly; /* Extract contours, not triangles */ GLUface *lonelyTriList; /* list of triangles which could not be rendered as strips or fans */ void (GLAPIENTRY *callBegin)( GLenum type ); void (GLAPIENTRY *callEdgeFlag)( GLboolean boundaryEdge ); void (GLAPIENTRY *callVertex)( void *data ); void (GLAPIENTRY *callEnd)( void ); void (GLAPIENTRY *callMesh)( GLUmesh *mesh ); /*** state needed to cache single-contour polygons for renderCache() */ GLboolean emptyCache; /* empty cache on next vertex() call */ int cacheCount; /* number of cached vertices */ CachedVertex cache[TESS_MAX_CACHE]; /* the vertex data */ /*** rendering callbacks that also pass polygon data ***/ void (GLAPIENTRY *callBeginData)( GLenum type, void *polygonData ); void (GLAPIENTRY *callEdgeFlagData)( GLboolean boundaryEdge, void *polygonData ); void (GLAPIENTRY *callVertexData)( void *data, void *polygonData ); void (GLAPIENTRY *callEndData)( void *polygonData ); void (GLAPIENTRY *callErrorData)( GLenum errnum, void *polygonData ); void (GLAPIENTRY *callCombineData)( GLdouble coords[3], void *data[4], GLfloat weight[4], void **outData, void *polygonData ); jmp_buf env; /* place to jump to when memAllocs fail */ void *polygonData; /* client data for current polygon */ }; void GLAPIENTRY __gl_noBeginData( GLenum type, void *polygonData ); void GLAPIENTRY __gl_noEdgeFlagData( GLboolean boundaryEdge, void *polygonData ); void GLAPIENTRY __gl_noVertexData( void *data, void *polygonData ); void GLAPIENTRY __gl_noEndData( void *polygonData ); void GLAPIENTRY __gl_noErrorData( GLenum errnum, void *polygonData ); void GLAPIENTRY __gl_noCombineData( GLdouble coords[3], void *data[4], GLfloat weight[4], void **outData, void *polygonData ); #define CALL_BEGIN_OR_BEGIN_DATA(a) \ if (tess->callBeginData != &__gl_noBeginData) \ (*tess->callBeginData)((a),tess->polygonData); \ else (*tess->callBegin)((a)); #define CALL_VERTEX_OR_VERTEX_DATA(a) \ if (tess->callVertexData != &__gl_noVertexData) \ (*tess->callVertexData)((a),tess->polygonData); \ else (*tess->callVertex)((a)); #define CALL_EDGE_FLAG_OR_EDGE_FLAG_DATA(a) \ if (tess->callEdgeFlagData != &__gl_noEdgeFlagData) \ (*tess->callEdgeFlagData)((a),tess->polygonData); \ else (*tess->callEdgeFlag)((a)); #define CALL_END_OR_END_DATA() \ if (tess->callEndData != &__gl_noEndData) \ (*tess->callEndData)(tess->polygonData); \ else (*tess->callEnd)(); #define CALL_COMBINE_OR_COMBINE_DATA(a,b,c,d) \ if (tess->callCombineData != &__gl_noCombineData) \ (*tess->callCombineData)((a),(b),(c),(d),tess->polygonData); \ else (*tess->callCombine)((a),(b),(c),(d)); #define CALL_ERROR_OR_ERROR_DATA(a) \ if (tess->callErrorData != &__gl_noErrorData) \ (*tess->callErrorData)((a),tess->polygonData); \ else (*tess->callError)((a)); #endif muffin-6.4.1/cogl/cogl-path/tesselator/memalloc.h0000664000175000017500000000317114723361714020675 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2010 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * */ /* This is a simple replacement for memalloc from the SGI tesselator code to force it to use glib's allocation instead */ #ifndef __MEMALLOC_H__ #define __MEMALLOC_H__ #include #define memRealloc g_realloc #define memAlloc g_malloc #define memFree g_free #define memInit(x) 1 /* tess.c defines TRUE and FALSE itself unconditionally so we need to undefine it from the glib headers */ #undef TRUE #undef FALSE #endif /* __MEMALLOC_H__ */ muffin-6.4.1/cogl/cogl-path/tesselator/mesh.h0000664000175000017500000002735114723361714020046 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #ifndef __mesh_h_ #define __mesh_h_ #include typedef struct GLUmesh GLUmesh; typedef struct GLUvertex GLUvertex; typedef struct GLUface GLUface; typedef struct GLUhalfEdge GLUhalfEdge; typedef struct ActiveRegion ActiveRegion; /* Internal data */ /* The mesh structure is similar in spirit, notation, and operations * to the "quad-edge" structure (see L. Guibas and J. Stolfi, Primitives * for the manipulation of general subdivisions and the computation of * Voronoi diagrams, ACM Transactions on Graphics, 4(2):74-123, April 1985). * For a simplified description, see the course notes for CS348a, * "Mathematical Foundations of Computer Graphics", available at the * Stanford bookstore (and taught during the fall quarter). * The implementation also borrows a tiny subset of the graph-based approach * use in Mantyla's Geometric Work Bench (see M. Mantyla, An Introduction * to Sold Modeling, Computer Science Press, Rockville, Maryland, 1988). * * The fundamental data structure is the "half-edge". Two half-edges * go together to make an edge, but they point in opposite directions. * Each half-edge has a pointer to its mate (the "symmetric" half-edge Sym), * its origin vertex (Org), the face on its left side (Lface), and the * adjacent half-edges in the CCW direction around the origin vertex * (Onext) and around the left face (Lnext). There is also a "next" * pointer for the global edge list (see below). * * The notation used for mesh navigation: * Sym = the mate of a half-edge (same edge, but opposite direction) * Onext = edge CCW around origin vertex (keep same origin) * Dnext = edge CCW around destination vertex (keep same dest) * Lnext = edge CCW around left face (dest becomes new origin) * Rnext = edge CCW around right face (origin becomes new dest) * * "prev" means to substitute CW for CCW in the definitions above. * * The mesh keeps global lists of all vertices, faces, and edges, * stored as doubly-linked circular lists with a dummy header node. * The mesh stores pointers to these dummy headers (vHead, fHead, eHead). * * The circular edge list is special; since half-edges always occur * in pairs (e and e->Sym), each half-edge stores a pointer in only * one direction. Starting at eHead and following the e->next pointers * will visit each *edge* once (ie. e or e->Sym, but not both). * e->Sym stores a pointer in the opposite direction, thus it is * always true that e->Sym->next->Sym->next == e. * * Each vertex has a pointer to next and previous vertices in the * circular list, and a pointer to a half-edge with this vertex as * the origin (NULL if this is the dummy header). There is also a * field "data" for client data. * * Each face has a pointer to the next and previous faces in the * circular list, and a pointer to a half-edge with this face as * the left face (NULL if this is the dummy header). There is also * a field "data" for client data. * * Note that what we call a "face" is really a loop; faces may consist * of more than one loop (ie. not simply connected), but there is no * record of this in the data structure. The mesh may consist of * several disconnected regions, so it may not be possible to visit * the entire mesh by starting at a half-edge and traversing the edge * structure. * * The mesh does NOT support isolated vertices; a vertex is deleted along * with its last edge. Similarly when two faces are merged, one of the * faces is deleted (see __gl_meshDelete below). For mesh operations, * all face (loop) and vertex pointers must not be NULL. However, once * mesh manipulation is finished, __gl_MeshZapFace can be used to delete * faces of the mesh, one at a time. All external faces can be "zapped" * before the mesh is returned to the client; then a NULL face indicates * a region which is not part of the output polygon. */ struct GLUvertex { GLUvertex *next; /* next vertex (never NULL) */ GLUvertex *prev; /* previous vertex (never NULL) */ GLUhalfEdge *anEdge; /* a half-edge with this origin */ void *data; /* client's data */ /* Internal data (keep hidden) */ GLdouble coords[3]; /* vertex location in 3D */ GLdouble s, t; /* projection onto the sweep plane */ long pqHandle; /* to allow deletion from priority queue */ }; struct GLUface { GLUface *next; /* next face (never NULL) */ GLUface *prev; /* previous face (never NULL) */ GLUhalfEdge *anEdge; /* a half edge with this left face */ void *data; /* room for client's data */ /* Internal data (keep hidden) */ GLUface *trail; /* "stack" for conversion to strips */ GLboolean marked; /* flag for conversion to strips */ GLboolean inside; /* this face is in the polygon interior */ }; struct GLUhalfEdge { GLUhalfEdge *next; /* doubly-linked list (prev==Sym->next) */ GLUhalfEdge *Sym; /* same edge, opposite direction */ GLUhalfEdge *Onext; /* next edge CCW around origin */ GLUhalfEdge *Lnext; /* next edge CCW around left face */ GLUvertex *Org; /* origin vertex (Overtex too long) */ GLUface *Lface; /* left face */ /* Internal data (keep hidden) */ ActiveRegion *activeRegion; /* a region with this upper edge (sweep.c) */ int winding; /* change in winding number when crossing from the right face to the left face */ }; #define Rface Sym->Lface #define Dst Sym->Org #define Oprev Sym->Lnext #define Lprev Onext->Sym #define Dprev Lnext->Sym #define Rprev Sym->Onext #define Dnext Rprev->Sym /* 3 pointers */ #define Rnext Oprev->Sym /* 3 pointers */ struct GLUmesh { GLUvertex vHead; /* dummy header for vertex list */ GLUface fHead; /* dummy header for face list */ GLUhalfEdge eHead; /* dummy header for edge list */ GLUhalfEdge eHeadSym; /* and its symmetric counterpart */ }; /* The mesh operations below have three motivations: completeness, * convenience, and efficiency. The basic mesh operations are MakeEdge, * Splice, and Delete. All the other edge operations can be implemented * in terms of these. The other operations are provided for convenience * and/or efficiency. * * When a face is split or a vertex is added, they are inserted into the * global list *before* the existing vertex or face (ie. e->Org or e->Lface). * This makes it easier to process all vertices or faces in the global lists * without worrying about processing the same data twice. As a convenience, * when a face is split, the "inside" flag is copied from the old face. * Other internal data (v->data, v->activeRegion, f->data, f->marked, * f->trail, e->winding) is set to zero. * * ********************** Basic Edge Operations ************************** * * __gl_meshMakeEdge( mesh ) creates one edge, two vertices, and a loop. * The loop (face) consists of the two new half-edges. * * __gl_meshSplice( eOrg, eDst ) is the basic operation for changing the * mesh connectivity and topology. It changes the mesh so that * eOrg->Onext <- OLD( eDst->Onext ) * eDst->Onext <- OLD( eOrg->Onext ) * where OLD(...) means the value before the meshSplice operation. * * This can have two effects on the vertex structure: * - if eOrg->Org != eDst->Org, the two vertices are merged together * - if eOrg->Org == eDst->Org, the origin is split into two vertices * In both cases, eDst->Org is changed and eOrg->Org is untouched. * * Similarly (and independently) for the face structure, * - if eOrg->Lface == eDst->Lface, one loop is split into two * - if eOrg->Lface != eDst->Lface, two distinct loops are joined into one * In both cases, eDst->Lface is changed and eOrg->Lface is unaffected. * * __gl_meshDelete( eDel ) removes the edge eDel. There are several cases: * if (eDel->Lface != eDel->Rface), we join two loops into one; the loop * eDel->Lface is deleted. Otherwise, we are splitting one loop into two; * the newly created loop will contain eDel->Dst. If the deletion of eDel * would create isolated vertices, those are deleted as well. * * ********************** Other Edge Operations ************************** * * __gl_meshAddEdgeVertex( eOrg ) creates a new edge eNew such that * eNew == eOrg->Lnext, and eNew->Dst is a newly created vertex. * eOrg and eNew will have the same left face. * * __gl_meshSplitEdge( eOrg ) splits eOrg into two edges eOrg and eNew, * such that eNew == eOrg->Lnext. The new vertex is eOrg->Dst == eNew->Org. * eOrg and eNew will have the same left face. * * __gl_meshConnect( eOrg, eDst ) creates a new edge from eOrg->Dst * to eDst->Org, and returns the corresponding half-edge eNew. * If eOrg->Lface == eDst->Lface, this splits one loop into two, * and the newly created loop is eNew->Lface. Otherwise, two disjoint * loops are merged into one, and the loop eDst->Lface is destroyed. * * ************************ Other Operations ***************************** * * __gl_meshNewMesh() creates a new mesh with no edges, no vertices, * and no loops (what we usually call a "face"). * * __gl_meshUnion( mesh1, mesh2 ) forms the union of all structures in * both meshes, and returns the new mesh (the old meshes are destroyed). * * __gl_meshDeleteMesh( mesh ) will free all storage for any valid mesh. * * __gl_meshZapFace( fZap ) destroys a face and removes it from the * global face list. All edges of fZap will have a NULL pointer as their * left face. Any edges which also have a NULL pointer as their right face * are deleted entirely (along with any isolated vertices this produces). * An entire mesh can be deleted by zapping its faces, one at a time, * in any order. Zapped faces cannot be used in further mesh operations! * * __gl_meshCheckMesh( mesh ) checks a mesh for self-consistency. */ GLUhalfEdge *__gl_meshMakeEdge( GLUmesh *mesh ); int __gl_meshSplice( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); int __gl_meshDelete( GLUhalfEdge *eDel ); GLUhalfEdge *__gl_meshAddEdgeVertex( GLUhalfEdge *eOrg ); GLUhalfEdge *__gl_meshSplitEdge( GLUhalfEdge *eOrg ); GLUhalfEdge *__gl_meshConnect( GLUhalfEdge *eOrg, GLUhalfEdge *eDst ); GLUmesh *__gl_meshNewMesh( void ); GLUmesh *__gl_meshUnion( GLUmesh *mesh1, GLUmesh *mesh2 ); void __gl_meshDeleteMesh( GLUmesh *mesh ); void __gl_meshZapFace( GLUface *fZap ); #ifdef NDEBUG #define __gl_meshCheckMesh( mesh ) #else void __gl_meshCheckMesh( GLUmesh *mesh ); #endif #endif muffin-6.4.1/cogl/cogl-path/tesselator/dict.c0000664000175000017500000000620314723361714020021 0ustar fabiofabio/* * SGI FREE SOFTWARE LICENSE B (Version 2.0, Sept. 18, 2008) * Copyright (C) 1991-2000 Silicon Graphics, Inc. All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the * Software is furnished to do so, subject to the following conditions: * * The above copyright notice including the dates of first publication and * either this permission notice or a reference to * http://oss.sgi.com/projects/FreeB/ * shall be included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * SILICON GRAPHICS, INC. BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF * OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Except as contained in this notice, the name of Silicon Graphics, Inc. * shall not be used in advertising or otherwise to promote the sale, use or * other dealings in this Software without prior written authorization from * Silicon Graphics, Inc. */ /* ** Author: Eric Veach, July 1994. ** */ #include #include "dict-list.h" #include "memalloc.h" /* really __gl_dictListNewDict */ Dict *dictNewDict( void *frame, int (*leq)(void *frame, DictKey key1, DictKey key2) ) { Dict *dict = (Dict *) memAlloc( sizeof( Dict )); DictNode *head; if (dict == NULL) return NULL; head = &dict->head; head->key = NULL; head->next = head; head->prev = head; dict->frame = frame; dict->leq = leq; return dict; } /* really __gl_dictListDeleteDict */ void dictDeleteDict( Dict *dict ) { DictNode *node, *next; for( node = dict->head.next; node != &dict->head; node = next ) { next = node->next; memFree( node ); } memFree( dict ); } /* really __gl_dictListInsertBefore */ DictNode *dictInsertBefore( Dict *dict, DictNode *node, DictKey key ) { DictNode *newNode; do { node = node->prev; } while( node->key != NULL && ! (*dict->leq)(dict->frame, node->key, key)); newNode = (DictNode *) memAlloc( sizeof( DictNode )); if (newNode == NULL) return NULL; newNode->key = key; newNode->next = node->next; node->next->prev = newNode; newNode->prev = node; node->next = newNode; return newNode; } /* really __gl_dictListDelete */ void dictDelete( Dict *dict, DictNode *node ) /*ARGSUSED*/ { node->next->prev = node->prev; node->prev->next = node->next; memFree( node ); } /* really __gl_dictListSearch */ DictNode *dictSearch( Dict *dict, DictKey key ) { DictNode *node = &dict->head; do { node = node->next; } while( node->key != NULL && ! (*dict->leq)(dict->frame, key, node->key)); return node; } muffin-6.4.1/cogl/config-custom.h0000664000175000017500000000250514723361714015616 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /* The contents of this file get #included by config.h so it is intended for extra configuration that needs to be included by all Cogl source files. */ muffin-6.4.1/cogl/tests/0000775000175000017500000000000014723361714014030 5ustar fabiofabiomuffin-6.4.1/cogl/tests/unit/0000775000175000017500000000000014723361714015007 5ustar fabiofabiomuffin-6.4.1/cogl/tests/unit/meson.build0000664000175000017500000000202214723361714017145 0ustar fabiofabiocogl_test_unit_sources = [ 'test-unit-main.c', ] cogl_test_unit_includes = [ cogl_includepath, cogl_test_fixtures_includepath, ] libmutter_cogl_test_unit = executable('test-unit', sources: cogl_test_unit_sources, c_args: cogl_debug_c_args + [ '-DCOGL_DISABLE_DEPRECATED', '-DCOGL_COMPILATION', '-DTESTS_DATADIR="@0@/tests/data"'.format(cogl_srcdir), ], include_directories: cogl_test_unit_includes, dependencies: [ libmutter_cogl_dep, libmutter_cogl_path_dep, libmutter_cogl_test_fixtures_dep, ], install: false, ) cogl_unit_tests = run_command( find_program('meson/find-unit-tests.sh'), cogl_srcdir, '/dev/stdout', check: true, ).stdout().strip().split('\n') foreach test_target: cogl_unit_tests test_name = '-'.join(test_target.split('_')) test(test_name, cogl_run_tests, suite: ['cogl', 'cogl/unit'], env: ['RUN_TESTS_QUIET=1'], args: [ cogl_config_env, libmutter_cogl_test_unit, 'unit_test_' + test_target ], is_parallel: false, ) endforeach muffin-6.4.1/cogl/tests/unit/meson/0000775000175000017500000000000014723361714016130 5ustar fabiofabiomuffin-6.4.1/cogl/tests/unit/meson/find-unit-tests.sh0000775000175000017500000000030314723361714021520 0ustar fabiofabio#!/bin/sh inputdir="$1" outputfile="$2" echo > "$outputfile" grep -h -r --include \*.c UNIT_TEST "$inputdir" | \ sed -n -e 's/^UNIT_TEST *( *\([a-zA-Z0-9_]\{1,\}\).*/\1/p' > "$outputfile" muffin-6.4.1/cogl/tests/unit/test-unit-main.c0000664000175000017500000000173714723361714020041 0ustar fabiofabio#include "cogl-config.h" #include #include #include int main (int argc, char **argv) { GModule *main_module; const CoglUnitTest *unit_test; int i; if (argc != 2) { g_printerr ("usage %s UNIT_TEST\n", argv[0]); exit (1); } /* Just for convenience in case people try passing the wrapper * filenames for the UNIT_TEST argument we normalize '-' characters * to '_' characters... */ for (i = 0; argv[1][i]; i++) { if (argv[1][i] == '-') argv[1][i] = '_'; } main_module = g_module_open (NULL, /* use main module */ 0 /* flags */); if (!g_module_symbol (main_module, argv[1], (void **) &unit_test)) { g_printerr ("Unknown test name \"%s\"\n", argv[1]); return 1; } test_utils_init (unit_test->requirement_flags, unit_test->known_failure_flags); unit_test->run (); test_utils_fini (); return 0; } muffin-6.4.1/cogl/tests/config.env.in0000664000175000017500000000005214723361714016411 0ustar fabiofabioHAVE_GL=@HAVE_GL@ HAVE_GLES2=@HAVE_GLES2@ muffin-6.4.1/cogl/tests/meson.build0000664000175000017500000000112414723361714016170 0ustar fabiofabiocogl_run_tests = find_program('run-tests.sh') cdata = configuration_data() cdata.set('HAVE_GL', have_gl.to_int()) cdata.set('HAVE_GLES2', have_gles2.to_int()) cogl_installed_tests_libexecdir = join_paths( mutter_installed_tests_libexecdir, 'cogl', 'conform') if have_installed_tests install_data('run-tests.sh', install_dir: cogl_installed_tests_libexecdir) endif cogl_config_env = configure_file( input: 'config.env.in', output: 'config.env', configuration: cdata, install: have_installed_tests, install_dir: cogl_installed_tests_libexecdir, ) subdir('conform') subdir('unit') muffin-6.4.1/cogl/tests/README0000664000175000017500000000527314723361714014717 0ustar fabiofabioOutline of test categories: The conform/ tests: ------------------- These tests should be non-interactive unit-tests that verify a single feature is behaving as documented. See conform/ADDING_NEW_TESTS for more details. Although it may seem a bit awkward; all the tests are built into a single binary because it makes building the tests *much* faster by avoiding lots of linking. Each test has a wrapper script generated though so running the individual tests should be convenient enough. Running the wrapper script will also print out for convenience how you could run the test under gdb or valgrind like this for example: NOTE: For debugging purposes, you can run this single test as follows: $ libtool --mode=execute \ gdb --eval-command="b test_cogl_depth_test" \ --args ./test-conformance -p /conform/cogl/test_cogl_depth_test or: $ env G_SLICE=always-malloc \ libtool --mode=execute \ valgrind ./test-conformance -p /conform/cogl/test_cogl_depth_test By default the conformance tests are run offscreen. This makes the tests run much faster and they also don't interfere with other work you may want to do by constantly stealing focus. CoglOnscreen framebuffers obviously don't get tested this way so it's important that the tests also get run onscreen every once in a while, especially if changes are being made to CoglFramebuffer related code. Onscreen testing can be enabled by setting COGL_TEST_ONSCREEN=1 in your environment. The micro-bench/ tests: ----------------------- These should be focused performance tests, ideally testing a single metric. Please never forget that these tests are synthetic and if you are using them then you understand what metric is being tested. They probably don't reflect any real world application loads and the intention is that you use these tests once you have already determined the crux of your problem and need focused feedback that your changes are indeed improving matters. There is no exit status requirements for these tests, but they should give clear feedback as to their performance. If the framerate is the feedback metric, then the test should forcibly enable FPS debugging. The data/ directory: -------------------- This contains optional data (like images) that can be referenced by a test. Misc notes: ----------- • All tests should ideally include a detailed description in the source explaining exactly what the test is for, how the test was designed to work, and possibly a rationale for the approach taken for testing. • When running tests under Valgrind, you should follow the instructions available here: http://live.gnome.org/Valgrind and also use the suppression file available inside the data/ directory. muffin-6.4.1/cogl/tests/data/0000775000175000017500000000000014723361714014741 5ustar fabiofabiomuffin-6.4.1/cogl/tests/data/valgrind.suppressions0000664000175000017500000000366514723361714021260 0ustar fabiofabio{ ioctl_1 Memcheck:Param ioctl(generic) fun:ioctl fun:driDrawableInitVBlank fun:intelMakeCurrent fun:glXMakeContextCurrent } { ioctl_2 Memcheck:Param ioctl(generic) fun:ioctl fun:driDrawableGetMSC32 fun:clutter_backend_glx_redraw } { ioctl_3 Memcheck:Param ioctl(generic) fun:ioctl fun:driWaitForMSC32 fun:clutter_backend_glx_redraw } { mesa_init_context Memcheck:Leak fun:*alloc ... fun:glXCreateNewContext } { type_register Memcheck:Leak fun:*alloc ... fun:g_type_register_* } { type_ref Memcheck:Leak fun:*alloc ... fun:g_type_class_ref } { type_interface_prereq Memcheck:Leak fun:*alloc ... fun:g_type_interface_add_prerequisite } { get_charset Memcheck:Leak fun:*alloc ... fun:g_get_charset } { glx_query_version Memcheck:Leak fun:*alloc ... fun:glXQueryVersion } { glx_create_context Memcheck:Leak fun:*alloc ... fun:glXCreateNewContext } { glx_make_current Memcheck:Leak fun:*alloc ... fun:glXMakeContextCurrent } { gl_draw_arrays Memcheck:Leak fun:*malloc ... fun:glDrawArrays } { cogl_clear Memcheck:Leak fun:*alloc ... fun:cogl_clear } { default_font Memcheck:Leak fun:*alloc ... fun:clutter_backend_get_font_name } { id_pool Memcheck:Leak fun:*alloc ... fun:clutter_id_pool_new } { x_open_display Memcheck:Leak fun:*alloc ... fun:XOpenDisplay } # ... and font descriptions from every "sans 12" type string { pango_font_description_from_string Memcheck:Leak fun:*alloc ... fun:pango_font_description_from_string } # other lib init { fontconfig_init Memcheck:Leak fun:*alloc ... fun:FcConfigParseAndLoad } { freetype_init Memcheck:Leak fun:*alloc ... fun:FT_Open_Face } { x_init_ext Memcheck:Leak fun:*alloc ... fun:XInitExtension } muffin-6.4.1/cogl/tests/run-tests.sh0000775000175000017500000000615214723361714016337 0ustar fabiofabio#!/usr/bin/env bash if test -z "$G_DEBUG"; then G_DEBUG=fatal-warnings else G_DEBUG="$G_DEBUG,fatal-warnings" fi export G_DEBUG ENVIRONMENT_CONFIG=$1 shift TEST_BINARY=$1 shift UNIT_TESTS=$1 shift . "$ENVIRONMENT_CONFIG" set +m LOG=$(mktemp) trap "" ERR trap "" SIGABRT trap "" SIGFPE trap "" SIGSEGV EXIT=0 MISSING_FEATURE="WARNING: Missing required feature"; KNOWN_FAILURE="WARNING: Test is known to fail"; if [ -z "$RUN_TESTS_QUIET" ]; then echo "Key:" echo "ok = Test passed" echo "n/a = Driver is missing a feature required for the test" echo "FAIL = Unexpected failure" echo "FIXME = Test failed, but it was an expected failure" echo "PASS! = Unexpected pass" echo "" fi get_status() { case $1 in # Special value we use to indicate that the test failed # but it was an expected failure so don't fail the # overall test run as a result... 300) echo -n "FIXME";; # Special value we use to indicate that the test passed # but we weren't expecting it to pass‽ 400) echo -n 'PASS!';; # Special value to indicate the test is missing a required feature 500) echo -n "n/a";; 0) echo -n "ok";; *) echo -n "FAIL";; esac } run_test() { $("$TEST_BINARY" "$1" &> "$LOG") TMP=$? var_name=$2_result eval "$var_name=$TMP" if grep -q "$MISSING_FEATURE" "$LOG"; then if test "$TMP" -ne 0; then eval "$var_name=500" else eval "$var_name=400" fi elif grep -q "$KNOWN_FAILURE" "$LOG"; then if test $TMP -ne 0; then eval "$var_name=300" else eval "$var_name=400" fi else if test "$TMP" -ne 0; then EXIT=$TMP; fi fi } if [ -z "$UNIT_TESTS" ]; then echo Missing unit-tests file or names exit 1 fi TITLE_FORMAT="%35s" printf "$TITLE_FORMAT" "Test" if test "$HAVE_GL" -eq 1; then GL_FORMAT=" %6s %8s %7s %6s" printf "$GL_FORMAT" "GL+GLSL" "GL3" fi if test "$HAVE_GLES2" -eq 1; then GLES2_FORMAT=" %6s" printf "$GLES2_FORMAT" "ES2" fi echo "" if [ -f "$UNIT_TESTS" ]; then UNIT_TESTS="$(cat $UNIT_TESTS)" fi if [ -z "$RUN_TESTS_QUIET" ] || [ "$(echo "$UNIT_TESTS" | wc -w )" -gt 1 ]; then echo "" fi for test in $UNIT_TESTS do printf $TITLE_FORMAT "$test:" export COGL_DEBUG= if test "$HAVE_GL" -eq 1; then export COGL_DRIVER=gl # NB: we can't explicitly disable fixed + glsl in this case since # the arbfp code only supports fragment processing so we need either # the fixed or glsl vertends export COGL_DEBUG= run_test "$test" gl_arbfp export COGL_DRIVER=gl export COGL_DEBUG=disable-fixed,disable-arbfp run_test "$test" gl_glsl export COGL_DRIVER=gl3 export COGL_DEBUG= run_test "$test" gl3 fi if test "$HAVE_GLES2" -eq 1; then export COGL_DRIVER=gles2 export COGL_DEBUG= run_test "$test" gles2 fi if test "$HAVE_GL" -eq 1; then printf "$GL_FORMAT" \ "$(get_status "$gl_glsl_result")" \ "$(get_status "$gl3_result")" fi if test "$HAVE_GLES2" -eq 1; then printf "$GLES2_FORMAT" \ "$(get_status "$gles2_result")" fi echo "" done rm "$LOG" exit "$EXIT" muffin-6.4.1/cogl/tests/test-launcher.sh0000775000175000017500000000151314723361714017145 0ustar fabiofabio#!/bin/sh TEST_BINARY=$1 shift SYMBOL_PREFIX=$1 shift UNIT_TEST=$1 shift test -z "${UNIT_TEST}" && { echo "Usage: $0 UNIT_TEST" exit 1 } BINARY_NAME=$(basename "$TEST_BINARY") UNIT_TEST=$(echo "$UNIT_TEST"|sed 's/-/_/g') echo "Running: ./$BINARY_NAME ${UNIT_TEST} $*" echo "" COGL_TEST_VERBOSE=1 "$TEST_BINARY" "${UNIT_TEST}" "$@" exit_val=$? if test "$exit_val" -eq 0; then echo "OK" fi echo "" echo "NOTE: For debugging purposes, you can run this single test as follows:" echo "$ libtool --mode=execute \\" echo " gdb --eval-command=\"start\" --eval-command=\"b ${UNIT_TEST#${SYMBOL_PREFIX}}\" \\" echo " --args ./$BINARY_NAME ${UNIT_TEST}" echo "or:" echo "$ env G_SLICE=always-malloc \\" echo " libtool --mode=execute \\" echo " valgrind ./$BINARY_NAME ${UNIT_TEST}" exit "$exit_val" muffin-6.4.1/cogl/tests/conform/0000775000175000017500000000000014723361714015473 5ustar fabiofabiomuffin-6.4.1/cogl/tests/conform/test-pipeline-user-matrix.c0000664000175000017500000001122514723361714022700 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { int width; int height; } TestState; static void validate_result (TestState *state) { uint32_t *pixels, *p; char *screen_pixel; const char *intended_pixel = "#ffffff"; /* The textures are setup so that when added together with the correct matrices then all of the pixels should be white. We can verify this by reading back the entire stage */ pixels = g_malloc (state->width * state->height * 4); cogl_framebuffer_read_pixels (test_fb, 0, 0, state->width, state->height, COGL_PIXEL_FORMAT_RGBA_8888_PRE, (uint8_t *)pixels); for (p = pixels; p < pixels + state->width * state->height; p++) { screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8); g_assert_cmpstr (screen_pixel, ==, intended_pixel); g_free (screen_pixel); } } static void paint (TestState *state) { /* This texture is painted mirrored around the x-axis */ uint8_t data0[] = { 0xff, 0x00, 0x00, /* red -> becomes bottom left */ 0x00, 0xff, 0x00, /* green -> becomes bottom right */ 0x00, 0x00, 0xff, /* blue -> becomes top left */ 0xff, 0x00, 0xff /* magenta -> becomes top right */ }; /* This texture is painted mirrored about the y-axis */ uint8_t data1[] = { 0x00, 0xff, 0x00, /* green -> becomes top right */ 0xff, 0xff, 0x00, /* yellow -> becomes top left */ 0xff, 0x00, 0xff, /* magenta -> becomes bottom right */ 0x00, 0xff, 0xff /* cyan -> becomes bottom left */ }; CoglTexture *tex0, *tex1; CoglPipeline *pipeline; CoglMatrix matrix; GError *error = NULL; cogl_framebuffer_orthographic (test_fb, 0, 0, state->width, state->height, -1, 100); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); cogl_matrix_init_identity (&matrix); cogl_framebuffer_set_modelview_matrix (test_fb, &matrix); tex0 = cogl_texture_new_from_data (2, 2, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, 6, data0); tex1 = cogl_texture_new_from_data (2, 2, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, 6, data1); pipeline = cogl_pipeline_new (test_ctx); /* Set the two textures as layers */ cogl_pipeline_set_layer_texture (pipeline, 0, tex0); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_texture (pipeline, 1, tex1); cogl_pipeline_set_layer_filters (pipeline, 1, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); /* Set a combine mode so that the two textures get added together */ if (!cogl_pipeline_set_layer_combine (pipeline, 1, "RGBA=ADD(PREVIOUS, TEXTURE)", &error)) { g_warning ("Error setting blend string: %s", error->message); g_assert_not_reached (); } /* Set a matrix on the first layer so that it will mirror about the y-axis */ cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); cogl_matrix_scale (&matrix, 1.0f, -1.0f, 1.0f); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); /* Set a matrix on the second layer so that it will mirror about the x-axis */ cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 1.0f, 0.0f, 0.0f); cogl_matrix_scale (&matrix, -1.0f, 1.0f, 1.0f); cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, state->width, state->height); cogl_object_unref (tex1); cogl_object_unref (tex0); cogl_object_unref (pipeline); } void test_pipeline_user_matrix (void) { TestState state; state.width = cogl_framebuffer_get_width (test_fb); state.height = cogl_framebuffer_get_height (test_fb); paint (&state); validate_result (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-alpha-textures.c0000664000175000017500000001017014723361714021561 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" static void create_pipeline (CoglTexture **tex_out, CoglPipeline **pipeline_out) { CoglTexture2D *tex; CoglPipeline *pipeline; static const uint8_t tex_data[] = { 0x00, 0x44, 0x88, 0xcc }; tex = cogl_texture_2d_new_from_data (test_ctx, 2, 2, /* width/height */ COGL_PIXEL_FORMAT_A_8, /* format */ 2, /* rowstride */ tex_data, NULL); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_filters (pipeline, 0, /* layer */ COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, /* layer */ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); /* This is the layer combine used by cogl-pango */ cogl_pipeline_set_layer_combine (pipeline, 0, /* layer */ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", NULL); cogl_pipeline_set_layer_texture (pipeline, 0, /* layer */ tex); *pipeline_out = pipeline; *tex_out = tex; } void test_alpha_textures (void) { CoglTexture *tex1, *tex2; CoglPipeline *pipeline1, *pipeline2; int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); uint8_t replacement_data[1] = { 0xff }; create_pipeline (&tex1, &pipeline1); cogl_framebuffer_draw_rectangle (test_fb, pipeline1, -1.0f, 1.0f, /* x1/y1 */ 1.0f, 0.0f /* x2/y2 */); create_pipeline (&tex2, &pipeline2); cogl_texture_set_region (tex2, 0, 0, /* src_x/y */ 1, 1, /* dst_x/y */ 1, 1, /* dst_width / dst_height */ 1, 1, /* width / height */ COGL_PIXEL_FORMAT_A_8, 1, /* rowstride */ replacement_data); cogl_framebuffer_draw_rectangle (test_fb, pipeline2, -1.0f, 0.0f, /* x1/y1 */ 1.0f, -1.0f /* x2/y2 */); cogl_object_unref (tex1); cogl_object_unref (tex2); cogl_object_unref (pipeline1); cogl_object_unref (pipeline2); /* Unmodified texture */ test_utils_check_pixel (test_fb, fb_width / 4, fb_height / 8, 0x000000ff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height / 8, 0x444444ff); test_utils_check_pixel (test_fb, fb_width / 4, fb_height * 3 / 8, 0x888888ff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height * 3 / 8, 0xccccccff); /* Modified texture */ test_utils_check_pixel (test_fb, fb_width / 4, fb_height * 5 / 8, 0x000000ff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height * 5 / 8, 0x444444ff); test_utils_check_pixel (test_fb, fb_width / 4, fb_height * 7 / 8, 0x888888ff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height * 7 / 8, 0xffffffff); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-primitive-and-journal.c0000664000175000017500000000757614723361714023053 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" typedef CoglVertexP2C4 Vertex; static void setup_orthographic_modelview (void) { CoglMatrix matrix; int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); /* Set up a non-identity modelview matrix. When the journal is * flushed it will usually flush the identity matrix. Using the * non-default matrix ensures that we test that Cogl restores the * matrix we asked for. The matrix sets up an orthographic transform * in the modelview matrix */ cogl_matrix_init_identity (&matrix); cogl_matrix_orthographic (&matrix, 0.0f, 0.0f, /* x_1 y_1 */ fb_width, fb_height, -1.0f, /* nearval */ 1.0f /* farval */); cogl_framebuffer_set_modelview_matrix (test_fb, &matrix); } static void create_primitives (CoglPrimitive *primitives[2]) { static const Vertex vertex_data[8] = { /* triangle strip 1 */ { 0, 0, 255, 0, 0, 255 }, { 0, 100, 255, 0, 0, 255 }, { 100, 0, 255, 0, 0, 255 }, { 100, 100, 255, 0, 0, 255 }, /* triangle strip 2 */ { 200, 0, 0, 0, 255, 255 }, { 200, 100, 0, 0, 255, 255 }, { 300, 0, 0, 0, 255, 255 }, { 300, 100, 0, 0, 255, 255 }, }; primitives[0] = cogl_primitive_new_p2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLE_STRIP, G_N_ELEMENTS (vertex_data), vertex_data); cogl_primitive_set_n_vertices (primitives[0], 4); primitives[1] = cogl_primitive_copy (primitives[0]); cogl_primitive_set_first_vertex (primitives[1], 4); cogl_primitive_set_n_vertices (primitives[1], 4); } static CoglPipeline * create_pipeline (void) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255); return pipeline; } void test_primitive_and_journal (void) { CoglPrimitive *primitives[2]; CoglPipeline *pipeline; setup_orthographic_modelview (); create_primitives (primitives); pipeline = create_pipeline (); /* Set a clip to clip all three rectangles to just the bottom half. * The journal flushes its own clip state so this verifies that the * clip state is correctly restored for the second primitive. */ cogl_framebuffer_push_rectangle_clip (test_fb, 0, 50, 300, 100); cogl_primitive_draw (primitives[0], test_fb, pipeline); /* Draw a rectangle using the journal in-between the two primitives. * This should test that the journal gets flushed correctly and that * the modelview matrix is restored. Half of the rectangle should be * overriden by the second primitive */ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 100, 0, /* x1/y1 */ 300, 100 /* x2/y2 */); cogl_primitive_draw (primitives[1], test_fb, pipeline); /* Check the three rectangles */ test_utils_check_region (test_fb, 1, 51, 98, 48, 0xff0000ff); test_utils_check_region (test_fb, 101, 51, 98, 48, 0x00ff00ff); test_utils_check_region (test_fb, 201, 51, 98, 48, 0x0000ffff); /* Check that the top half of all of the rectangles was clipped */ test_utils_check_region (test_fb, 1, 1, 298, 48, 0x000000ff); cogl_framebuffer_pop_clip (test_fb); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-alpha-test.c0000664000175000017500000000470714723361714020666 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" static CoglTexture2D * create_texture (CoglContext *context) { static const uint8_t data[] = { 0xff, 0x00, 0x00, 0xff, 0x00, 0xfa, 0x00, 0xfa }; return cogl_texture_2d_new_from_data (context, 2, 1, /* width/height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ data, NULL /* error */); } void test_alpha_test (void) { CoglTexture *tex = create_texture (test_ctx); CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); CoglColor clear_color; cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_alpha_test_function (pipeline, COGL_PIPELINE_ALPHA_FUNC_GEQUAL, 254 / 255.0f /* alpha reference */); cogl_color_init_from_4ub (&clear_color, 0x00, 0x00, 0xff, 0xff); cogl_framebuffer_clear (test_fb, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, -1, 1, 1); cogl_object_unref (pipeline); cogl_object_unref (tex); /* The left side of the framebuffer should use the first pixel from * the texture which is red */ test_utils_check_region (test_fb, 2, 2, fb_width / 2 - 4, fb_height - 4, 0xff0000ff); /* The right side of the framebuffer should use the clear color * because the second pixel from the texture is clipped from the * alpha test */ test_utils_check_region (test_fb, fb_width / 2 + 2, 2, fb_width / 2 - 4, fb_height - 4, 0x0000ffff); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-euler.c0000664000175000017500000000457314723361714017741 0ustar fabiofabio#include #include #include #include "test-declarations.h" #include "test-utils.h" /* Macros are used here instead of functions so that the * g_assert_cmpfloat will give a more interesting message when it * fails */ #define COMPARE_FLOATS(a, b) \ do { \ if (fabsf ((a) - (b)) >= 0.0001f) \ g_assert_cmpfloat ((a), ==, (b)); \ } while (0) #define COMPARE_MATRICES(a, b) \ do { \ COMPARE_FLOATS ((a)->xx, (b)->xx); \ COMPARE_FLOATS ((a)->yx, (b)->yx); \ COMPARE_FLOATS ((a)->zx, (b)->zx); \ COMPARE_FLOATS ((a)->wx, (b)->wx); \ COMPARE_FLOATS ((a)->xy, (b)->xy); \ COMPARE_FLOATS ((a)->yy, (b)->yy); \ COMPARE_FLOATS ((a)->zy, (b)->zy); \ COMPARE_FLOATS ((a)->wy, (b)->wy); \ COMPARE_FLOATS ((a)->xz, (b)->xz); \ COMPARE_FLOATS ((a)->yz, (b)->yz); \ COMPARE_FLOATS ((a)->zz, (b)->zz); \ COMPARE_FLOATS ((a)->wz, (b)->wz); \ COMPARE_FLOATS ((a)->xw, (b)->xw); \ COMPARE_FLOATS ((a)->yw, (b)->yw); \ COMPARE_FLOATS ((a)->zw, (b)->zw); \ COMPARE_FLOATS ((a)->ww, (b)->ww); \ } while (0) void test_euler (void) { graphene_euler_t euler; CoglMatrix matrix_a, matrix_b; /* Try doing the rotation with three separate rotations */ cogl_matrix_init_identity (&matrix_a); cogl_matrix_rotate (&matrix_a, -30.0f, 0.0f, 1.0f, 0.0f); cogl_matrix_rotate (&matrix_a, 40.0f, 1.0f, 0.0f, 0.0f); cogl_matrix_rotate (&matrix_a, 50.0f, 0.0f, 0.0f, 1.0f); /* And try the same rotation with a euler */ graphene_euler_init_with_order (&euler, 40, -30, 50, GRAPHENE_EULER_ORDER_RYXZ); cogl_matrix_init_from_euler (&matrix_b, &euler); /* Verify that the matrices are approximately the same */ COMPARE_MATRICES (&matrix_a, &matrix_b); /* Try applying the rotation from a euler to a framebuffer */ cogl_framebuffer_identity_matrix (test_fb); cogl_framebuffer_rotate_euler (test_fb, &euler); memset (&matrix_b, 0, sizeof (matrix_b)); cogl_framebuffer_get_modelview_matrix (test_fb, &matrix_b); COMPARE_MATRICES (&matrix_a, &matrix_b); /* FIXME: This needs a lot more tests! */ if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/meson.build0000664000175000017500000000664214723361714017645 0ustar fabiofabiocogl_test_conformance_sources = [ 'test-conform-main.c', 'test-atlas-migration.c', 'test-blend-strings.c', 'test-blend.c', 'test-depth-test.c', 'test-color-hsl.c', 'test-backface-culling.c', 'test-just-vertex-shader.c', 'test-pipeline-user-matrix.c', 'test-pipeline-uniforms.c', 'test-pixel-buffer.c', 'test-premult.c', 'test-snippets.c', 'test-wrap-modes.c', 'test-sub-texture.c', 'test-custom-attributes.c', 'test-offscreen.c', 'test-primitive.c', 'test-sparse-pipeline.c', 'test-read-texture-formats.c', 'test-write-texture-formats.c', 'test-point-size.c', 'test-point-size-attribute.c', 'test-point-sprite.c', 'test-no-gl-header.c', 'test-version.c', 'test-euler.c', 'test-layer-remove.c', 'test-alpha-test.c', 'test-map-buffer-range.c', 'test-npot-texture.c', 'test-alpha-textures.c', 'test-texture-get-set-data.c', 'test-framebuffer-get-bits.c', 'test-primitive-and-journal.c', 'test-copy-replace-texture.c', 'test-pipeline-cache-unrefs-texture.c', 'test-texture-no-allocate.c', 'test-pipeline-shader-state.c', 'test-texture-rg.c', 'test-fence.c', 'test-path.c', 'test-path-clip.c', ] #unported = [ # "test-viewport.c", # "test-multitexture.c", # "test-npot-texture.c", # "test-object.c", # "test-readpixels.c", # "test-texture-mipmaps.c", # "test-texture-pixmap-x11.c",", #] cogl_test_conformance_includes = [ cogl_includepath, cogl_test_fixtures_includepath, ] if have_installed_tests cogl_installed_tests_cdata = configuration_data() cogl_installed_tests_cdata.set('libexecdir', libexecdir) cogl_installed_tests_cdata.set('apiversion', libmutter_api_version) configure_file( input: 'mutter-cogl.test.in', output: 'mutter-cogl.test', configuration: cogl_installed_tests_cdata, install: true, install_dir: mutter_installed_tests_datadir, ) endif libmutter_cogl_test_conformance = executable('test-conformance', sources: cogl_test_conformance_sources, c_args: cogl_debug_c_args + [ '-DCOGL_ENABLE_EXPERIMENTAL_API', '-DCOGL_DISABLE_DEPRECATED', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DTESTS_DATADIR="@0@/tests/data"'.format(cogl_srcdir), ], include_directories: cogl_test_conformance_includes, dependencies: [ libmutter_cogl_dep, libmutter_cogl_path_dep, libmutter_cogl_test_fixtures_dep, ], install: have_installed_tests, install_dir: cogl_installed_tests_libexecdir, install_rpath: pkglibdir, ) find_unit_tests = find_program('meson/find-conform-unit-tests.sh') test_conform_main = files(join_paths(meson.current_source_dir(), 'test-conform-main.c')) cogl_conform_unit_tests = custom_target('cogl-tests-conform-unit-tests', output: 'unit-tests', input: test_conform_main, command: [find_unit_tests, '@INPUT@', '@OUTPUT@'], install: have_installed_tests, install_dir: cogl_installed_tests_libexecdir, ) cogl_conformance_tests = run_command( find_unit_tests, test_conform_main, '/dev/stdout', check: true, ).stdout().strip().split('\n') foreach test_target: cogl_conformance_tests name_parts = [] foreach part: test_target.split('_') if part != 'test' name_parts += [part] endif endforeach test_name = '-'.join(name_parts) test(test_name, cogl_run_tests, suite: ['cogl', 'cogl/conform'], env: ['RUN_TESTS_QUIET=1'], args: [ cogl_config_env, libmutter_cogl_test_conformance, test_target ], is_parallel: false, ) endforeach muffin-6.4.1/cogl/tests/conform/test-readpixels.c0000664000175000017500000001302214723361714020752 0ustar fabiofabio #include #include #include "test-conform-common.h" #define RED 0 #define GREEN 1 #define BLUE 2 #define FRAMEBUFFER_WIDTH 640 #define FRAMEBUFFER_HEIGHT 480 static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, void *state) { float saved_viewport[4]; CoglMatrix saved_projection; CoglMatrix projection; CoglMatrix modelview; guchar *data; CoglHandle tex; CoglHandle offscreen; uint32_t *pixels; uint8_t *pixelsc; /* Save the Clutter viewport/matrices and load identity matrices */ cogl_get_viewport (saved_viewport); cogl_get_projection_matrix (&saved_projection); cogl_push_matrix (); cogl_matrix_init_identity (&projection); cogl_matrix_init_identity (&modelview); cogl_set_projection_matrix (&projection); cogl_set_modelview_matrix (&modelview); /* All offscreen rendering is done upside down so the first thing we * verify is reading back grid of colors from a CoglOffscreen framebuffer */ data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, TEST_UTILS_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ COGL_PIXEL_FORMAT_ANY, /* internal fmt */ FRAMEBUFFER_WIDTH * 4, /* rowstride */ data); g_free (data); offscreen = cogl_offscreen_new_with_texture (tex); cogl_push_framebuffer (offscreen); /* red, top left */ cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); cogl_rectangle (-1, 1, 0, 0); /* green, top right */ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); cogl_rectangle (0, 1, 1, 0); /* blue, bottom left */ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); cogl_rectangle (-1, 0, 0, -1); /* white, bottom right */ cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); cogl_rectangle (0, 0, 1, -1); pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, (guchar *)pixels); g_assert_cmpint (pixels[0], ==, 0xff0000ff); g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00); g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000); g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff); g_free (pixels); cogl_pop_framebuffer (); cogl_object_unref (offscreen); /* Now verify reading back from an onscreen framebuffer... */ cogl_set_source_texture (tex); cogl_rectangle (-1, 1, 1, -1); pixels = g_malloc0 (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, (guchar *)pixels); g_assert_cmpint (pixels[0], ==, 0xff0000ff); g_assert_cmpint (pixels[FRAMEBUFFER_WIDTH - 1], ==, 0xff00ff00); g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH], ==, 0xffff0000); g_assert_cmpint (pixels[(FRAMEBUFFER_HEIGHT - 1) * FRAMEBUFFER_WIDTH + FRAMEBUFFER_WIDTH - 1], ==, 0xffffffff); g_free (pixels); /* Verify using BGR format */ cogl_set_source_texture (tex); cogl_rectangle (-1, 1, 1, -1); pixelsc = g_malloc0 (FRAMEBUFFER_WIDTH * 3 * FRAMEBUFFER_HEIGHT); cogl_read_pixels (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_BGR_888, (guchar *)pixelsc); g_assert_cmpint (pixelsc[0], ==, 0x00); g_assert_cmpint (pixelsc[1], ==, 0x00); g_assert_cmpint (pixelsc[2], ==, 0xff); g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 0], ==, 0x00); g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 1], ==, 0xff); g_assert_cmpint (pixelsc[(FRAMEBUFFER_WIDTH - 1) * 3 + 2], ==, 0x00); g_free (pixelsc); cogl_object_unref (tex); /* Restore the viewport and matrices state */ cogl_set_viewport (saved_viewport[0], saved_viewport[1], saved_viewport[2], saved_viewport[3]); cogl_set_projection_matrix (&saved_projection); cogl_pop_matrix (); /* Comment this out if you want visual feedback of what this test * paints. */ clutter_main_quit (); } static gboolean queue_redraw (void *stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } void test_readpixels (TestUtilsGTestFixture *fixture, void *data) { unsigned int idle_source; ClutterActor *stage; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); clutter_actor_show (stage); clutter_main (); g_clear_handle_id (&idle_source, g_source_remove); /* Remove all of the actors from the stage */ clutter_actor_remove_all_children (stage); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-pipeline-cache-unrefs-texture.c0000664000175000017500000000545514723361714024471 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" /* Keep track of the number of textures that we've created and are * still alive */ static int destroyed_texture_count = 0; #define N_TEXTURES 3 static void free_texture_cb (void *user_data) { destroyed_texture_count++; } static CoglTexture * create_texture (void) { static const guint8 data[] = { 0xff, 0xff, 0xff, 0xff }; static CoglUserDataKey texture_data_key; CoglTexture2D *tex_2d; tex_2d = cogl_texture_2d_new_from_data (test_ctx, 1, 1, /* width / height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ data, NULL); /* Set some user data on the texture so we can track when it has * been destroyed */ cogl_object_set_user_data (COGL_OBJECT (tex_2d), &texture_data_key, GINT_TO_POINTER (1), free_texture_cb); return tex_2d; } void test_pipeline_cache_unrefs_texture (void) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglPipeline *simple_pipeline; int i; /* Create a pipeline with three texture layers. That way we can be * pretty sure the pipeline will cause a unique shader to be * generated in the cache */ for (i = 0; i < N_TEXTURES; i++) { CoglTexture *tex = create_texture (); cogl_pipeline_set_layer_texture (pipeline, i, tex); cogl_object_unref (tex); } /* Draw something with the pipeline to ensure it gets into the * pipeline cache */ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); cogl_framebuffer_finish (test_fb); /* Draw something else so that it is no longer the current flushed * pipeline, and the units have a different texture bound */ simple_pipeline = cogl_pipeline_new (test_ctx); for (i = 0; i < N_TEXTURES; i++) { CoglColor combine_constant; cogl_color_init_from_4ub (&combine_constant, i, 0, 0, 255); cogl_pipeline_set_layer_combine_constant (simple_pipeline, i, &combine_constant); } cogl_framebuffer_draw_rectangle (test_fb, simple_pipeline, 0, 0, 10, 10); cogl_framebuffer_finish (test_fb); cogl_object_unref (simple_pipeline); g_assert_cmpint (destroyed_texture_count, ==, 0); /* Destroy the pipeline. This should immediately cause the textures * to be freed */ cogl_object_unref (pipeline); g_assert_cmpint (destroyed_texture_count, ==, N_TEXTURES); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-texture-get-set-data.c0000664000175000017500000001036514723361714022576 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" static void check_texture (int width, int height, TestUtilsTextureFlags flags) { CoglTexture *tex; uint8_t *data, *p; int y, x; int rowstride; CoglBitmap *bmp; p = data = g_malloc (width * height * 4); for (y = 0; y < height; y++) for (x = 0; x < width; x++) { *(p++) = x; *(p++) = y; *(p++) = 128; *(p++) = (x ^ y); } bmp = cogl_bitmap_new_for_data (test_ctx, width, height, COGL_PIXEL_FORMAT_RGBA_8888, width * 4, data); tex = test_utils_texture_new_from_bitmap (bmp, flags, FALSE); /* Replace the bottom right quarter of the data with negated data to test set_region */ rowstride = width * 4; p = data + (height / 2) * rowstride + rowstride / 2; for (y = 0; y < height / 2; y++) { for (x = 0; x < width / 2; x++) { p[0] = ~p[0]; p[1] = ~p[1]; p[2] = ~p[2]; p[3] = ~p[3]; p += 4; } p += width * 2; } cogl_texture_set_region (tex, width / 2, height / 2, width / 2, /* dest x */ height / 2, /* dest y */ width / 2, /* region width */ height / 2, /* region height */ width, /* src width */ height, /* src height */ COGL_PIXEL_FORMAT_RGBA_8888, rowstride, data); /* Check passing a NULL pointer and a zero rowstride. The texture should calculate the needed data size and return it */ g_assert_cmpint (cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_ANY, 0, NULL), ==, width * height * 4); /* Try first receiving the data as RGB. This should cause a * conversion */ memset (data, 0, width * height * 4); cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGB_888, width * 3, data); p = data; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { if (x >= width / 2 && y >= height / 2) { g_assert_cmpint (p[0], ==, ~x & 0xff); g_assert_cmpint (p[1], ==, ~y & 0xff); g_assert_cmpint (p[2], ==, ~128 & 0xff); } else { g_assert_cmpint (p[0], ==, x & 0xff); g_assert_cmpint (p[1], ==, y & 0xff); g_assert_cmpint (p[2], ==, 128); } p += 3; } /* Now try receiving the data as RGBA. This should not cause a * conversion and no unpremultiplication because we explicitly set * the internal format when we created the texture */ memset (data, 0, width * height * 4); cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGBA_8888, width * 4, data); p = data; for (y = 0; y < height; y++) for (x = 0; x < width; x++) { if (x >= width / 2 && y >= height / 2) { g_assert_cmpint (p[0], ==, ~x & 0xff); g_assert_cmpint (p[1], ==, ~y & 0xff); g_assert_cmpint (p[2], ==, ~128 & 0xff); g_assert_cmpint (p[3], ==, ~(x ^ y) & 0xff); } else { g_assert_cmpint (p[0], ==, x & 0xff); g_assert_cmpint (p[1], ==, y & 0xff); g_assert_cmpint (p[2], ==, 128); g_assert_cmpint (p[3], ==, (x ^ y) & 0xff); } p += 4; } cogl_object_unref (tex); g_free (data); } void test_texture_get_set_data (void) { /* First try without atlasing */ check_texture (256, 256, TEST_UTILS_TEXTURE_NO_ATLAS); /* Try again with atlasing. This should end up testing the atlas backend and the sub texture backend */ check_texture (256, 256, 0); /* Try with a really big texture in the hope that it will end up sliced. */ check_texture (4, 5128, TEST_UTILS_TEXTURE_NO_ATLAS); /* And in the other direction. */ check_texture (5128, 4, TEST_UTILS_TEXTURE_NO_ATLAS); } muffin-6.4.1/cogl/tests/conform/test-snippets.c0000664000175000017500000006625614723361714020500 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { int fb_width, fb_height; } TestState; typedef void (* SnippetTestFunc) (TestState *state); static CoglPipeline * create_texture_pipeline (TestState *state) { CoglPipeline *pipeline; CoglTexture *tex; static const uint8_t tex_data[] = { 0xff, 0x00, 0x00, 0xff, /* red */ 0x00, 0xff, 0x00, 0xff, /* green */ 0x00, 0x00, 0xff, 0xff, /* blue */ 0xff, 0xff, 0x00, 0xff, /* yellow */ }; tex = test_utils_texture_new_from_data (test_ctx, 2, 2, /* width/height */ TEST_UTILS_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 8, /* rowstride */ tex_data); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_object_unref (tex); return pipeline; } static void simple_fragment_snippet (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Simple fragment snippet */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ "cogl_color_out.g += 1.0;"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); } static void simple_vertex_snippet (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Simple vertex snippet */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, NULL, "cogl_color_out.b += 1.0;"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 10, 0, 20, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 15, 5, 0xff00ffff); } static void shared_uniform (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; int location; /* Snippets sharing a uniform across the vertex and fragment hooks */ pipeline = cogl_pipeline_new (test_ctx); location = cogl_pipeline_get_uniform_location (pipeline, "a_value"); cogl_pipeline_set_uniform_1f (pipeline, location, 0.25f); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, "uniform float a_value;", "cogl_color_out.b += a_value;"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "uniform float a_value;", "cogl_color_out.b += a_value;"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 20, 0, 30, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 25, 5, 0xff0080ff); } static void lots_snippets (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; int location; int i; /* Lots of snippets on one pipeline */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); for (i = 0; i < 3; i++) { char letter = 'x' + i; char *uniform_name = g_strdup_printf ("%c_value", letter); char *declarations = g_strdup_printf ("uniform float %s;\n", uniform_name); char *code = g_strdup_printf ("cogl_color_out.%c = %s;\n", letter, uniform_name); location = cogl_pipeline_get_uniform_location (pipeline, uniform_name); cogl_pipeline_set_uniform_1f (pipeline, location, (i + 1) * 0.1f); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, declarations, code); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); g_free (code); g_free (uniform_name); g_free (declarations); } cogl_framebuffer_draw_rectangle (test_fb, pipeline, 30, 0, 40, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 35, 5, 0x19334cff); } static void shared_variable_pre_post (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Test that the pre string can declare variables used by the post string */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 255, 255, 255, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ "cogl_color_out = redvec;"); cogl_snippet_set_pre (snippet, "vec4 redvec = vec4 (1.0, 0.0, 0.0, 1.0);"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 40, 0, 50, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 45, 5, 0xff0000ff); } static void test_pipeline_caching (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Check that the pipeline caching works when unrelated pipelines share snippets state. It's too hard to actually assert this in the conformance test but at least it should be possible to see by setting COGL_DEBUG=show-source to check whether this shader gets generated twice */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "/* This comment should only be seen ONCE\n" " when COGL_DEBUG=show-source is TRUE\n" " even though it is used in two different\n" " unrelated pipelines */", "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);\n"); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_add_snippet (pipeline, snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 50, 0, 60, 10); cogl_object_unref (pipeline); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_add_snippet (pipeline, snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 60, 0, 70, 10); cogl_object_unref (pipeline); cogl_object_unref (snippet); test_utils_check_pixel (test_fb, 55, 5, 0x00ff00ff); test_utils_check_pixel (test_fb, 65, 5, 0x00ff00ff); } static void test_replace_string (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Check the replace string */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); cogl_snippet_set_pre (snippet, "cogl_color_out = vec4 (0.0, 0.5, 0.0, 1.0);"); /* Remove the generated output. If the replace string isn't working then the code from the pre string would get overwritten with white */ cogl_snippet_set_replace (snippet, "/* do nothing */"); cogl_snippet_set_post (snippet, "cogl_color_out += vec4 (0.5, 0.0, 0.0, 1.0);"); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_add_snippet (pipeline, snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 70, 0, 80, 10); cogl_object_unref (pipeline); cogl_object_unref (snippet); test_utils_check_pixel (test_fb, 75, 5, 0x808000ff); } static void test_texture_lookup_hook (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Check the texture lookup hook */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, "cogl_texel.b += 1.0;"); /* Flip the texture coordinates around the y axis so that it will get the green texel */ cogl_snippet_set_pre (snippet, "cogl_tex_coord.x = 1.0 - cogl_tex_coord.x;"); pipeline = create_texture_pipeline (state); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 80, 0, 90, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); cogl_object_unref (snippet); test_utils_check_pixel (test_fb, 85, 5, 0x00ffffff); } static void test_multiple_samples (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Check that we can use the passed in sampler in the texture lookup to sample multiple times */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_texel = " "texture2D (cogl_sampler, vec2 (0.25, 0.25)) + " "texture2D (cogl_sampler, vec2 (0.75, 0.25));"); pipeline = create_texture_pipeline (state); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); cogl_object_unref (pipeline); cogl_object_unref (snippet); test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); } static void test_replace_lookup_hook (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Check replacing the texture lookup hook */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_texel = vec4 (0.0, 0.0, 1.0, 0.0);"); pipeline = create_texture_pipeline (state); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 90, 0, 100, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); cogl_object_unref (snippet); test_utils_check_pixel (test_fb, 95, 5, 0x0000ffff); } static void test_replace_snippet (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Test replacing a previous snippet */ pipeline = create_texture_pipeline (state); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, "cogl_color_out = vec4 (0.5, 0.5, 0.5, 1.0);"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); cogl_snippet_set_pre (snippet, "cogl_color_out = vec4 (1.0, 1.0, 1.0, 1.0);"); cogl_snippet_set_replace (snippet, "cogl_color_out *= vec4 (1.0, 0.0, 0.0, 1.0);"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 100, 0, 110, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 105, 5, 0xff0000ff); } static void test_replace_fragment_layer (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Test replacing the fragment layer code */ pipeline = create_texture_pipeline (state); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_layer = vec4 (0.0, 0.0, 1.0, 1.0);"); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_object_unref (snippet); /* Add a second layer which samples from the texture in the first layer. The snippet override should cause the first layer not to generate the code for the texture lookup but this second layer should still be able to cause it to be generated */ cogl_pipeline_set_layer_combine (pipeline, 1, "RGB = ADD(TEXTURE_0, PREVIOUS)" "A = REPLACE(PREVIOUS)", NULL); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 110, 0, 120, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 115, 5, 0xff00ffff); } static void test_modify_fragment_layer (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Test modifying the fragment layer code */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_uniform_1f (pipeline, cogl_pipeline_get_uniform_location (pipeline, "a_value"), 0.5); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_LAYER_FRAGMENT, "uniform float a_value;", "cogl_layer.g = a_value;"); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 120, 0, 130, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 125, 5, 0xff80ffff); } static void test_modify_vertex_layer (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; CoglMatrix matrix; /* Test modifying the vertex layer code */ pipeline = create_texture_pipeline (state); cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM, NULL, "cogl_tex_coord.x = 1.0;"); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 130, 0, 140, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 135, 5, 0xffff00ff); } static void test_replace_vertex_layer (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; CoglMatrix matrix; /* Test replacing the vertex layer code */ pipeline = create_texture_pipeline (state); cogl_matrix_init_identity (&matrix); cogl_matrix_translate (&matrix, 0.0f, 1.0f, 0.0f); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_COORD_TRANSFORM, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_tex_coord.x = 1.0;\n"); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 140, 0, 150, 10, 0, 0, 0, 0); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 145, 5, 0x00ff00ff); } static void test_vertex_transform_hook (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; CoglMatrix identity_matrix; CoglMatrix matrix; int location; /* Test the vertex transform hook */ cogl_matrix_init_identity (&identity_matrix); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 255, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_TRANSFORM, "uniform mat4 pmat;", NULL); cogl_snippet_set_replace (snippet, "cogl_position_out = " "pmat * cogl_position_in;"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); /* Copy the current projection matrix to a uniform */ cogl_framebuffer_get_projection_matrix (test_fb, &matrix); location = cogl_pipeline_get_uniform_location (pipeline, "pmat"); cogl_pipeline_set_uniform_matrix (pipeline, location, 4, /* dimensions */ 1, /* count */ FALSE, /* don't transpose */ cogl_matrix_get_array (&matrix)); /* Replace the real projection matrix with the identity. This should mess up the drawing unless the snippet replacement is working */ cogl_framebuffer_set_projection_matrix (test_fb, &identity_matrix); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 150, 0, 160, 10); cogl_object_unref (pipeline); /* Restore the projection matrix */ cogl_framebuffer_set_projection_matrix (test_fb, &matrix); test_utils_check_pixel (test_fb, 155, 5, 0xff00ffff); } static void test_global_vertex_hook (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; pipeline = cogl_pipeline_new (test_ctx); /* Creates a function in the global declarations hook which is used * by a subsequent snippet. The subsequent snippets replace any * previous snippets but this shouldn't prevent the global * declarations from being generated */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX_GLOBALS, /* declarations */ "float\n" "multiply_by_two (float number)\n" "{\n" " return number * 2.0;\n" "}\n", /* post */ "This string shouldn't be used so " "we can safely put garbage in here."); cogl_snippet_set_pre (snippet, "This string shouldn't be used so " "we can safely put garbage in here."); cogl_snippet_set_replace (snippet, "This string shouldn't be used so " "we can safely put garbage in here."); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, NULL, /* declarations */ NULL /* replace */); cogl_snippet_set_replace (snippet, "cogl_color_out.r = multiply_by_two (0.5);\n" "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n" "cogl_position_out = cogl_position_in;\n"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, 1, 10.0f * 2.0f / state->fb_width - 1.0f, 10.0f * 2.0f / state->fb_height - 1.0f); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff); } static void test_global_fragment_hook (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; pipeline = cogl_pipeline_new (test_ctx); /* Creates a function in the global declarations hook which is used * by a subsequent snippet. The subsequent snippets replace any * previous snippets but this shouldn't prevent the global * declarations from being generated */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT_GLOBALS, /* declarations */ "float\n" "multiply_by_four (float number)\n" "{\n" " return number * 4.0;\n" "}\n", /* post */ "This string shouldn't be used so " "we can safely put garbage in here."); cogl_snippet_set_pre (snippet, "This string shouldn't be used so " "we can safely put garbage in here."); cogl_snippet_set_replace (snippet, "This string shouldn't be used so " "we can safely put garbage in here."); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ NULL /* replace */); cogl_snippet_set_replace (snippet, "cogl_color_out.r = multiply_by_four (0.25);\n" "cogl_color_out.gba = vec3 (0.0, 0.0, 1.0);\n"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 5, 5, 0xff0000ff); } static void test_snippet_order (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; /* Verify that the snippets are executed in the right order. We'll replace the r component of the color in the pre sections of the snippets and the g component in the post. The pre sections should be executed in the reverse order they were added and the post sections in the same order as they were added. Therefore the r component should be taken from the the second snippet and the g component from the first */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, "cogl_color_out.g = 0.5;\n"); cogl_snippet_set_pre (snippet, "cogl_color_out.r = 0.5;\n"); cogl_snippet_set_replace (snippet, "cogl_color_out.ba = vec2 (0.0, 1.0);"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, "cogl_color_out.g = 1.0;\n"); cogl_snippet_set_pre (snippet, "cogl_color_out.r = 1.0;\n"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 160, 0, 170, 10); cogl_object_unref (pipeline); test_utils_check_pixel (test_fb, 165, 5, 0x80ff00ff); } static void test_naming_texture_units (TestState *state) { CoglPipeline *pipeline; CoglSnippet *snippet; CoglTexture *tex1, *tex2; /* Test that we can sample from an arbitrary texture unit by naming its layer number */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, NULL); cogl_snippet_set_replace (snippet, "cogl_color_out = " "texture2D (cogl_sampler100, vec2 (0.0, 0.0)) + " "texture2D (cogl_sampler200, vec2 (0.0, 0.0));"); tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff); tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 100, tex1); cogl_pipeline_set_layer_texture (pipeline, 200, tex2); cogl_pipeline_add_snippet (pipeline, snippet); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 10, 10); cogl_object_unref (pipeline); cogl_object_unref (snippet); cogl_object_unref (tex1); cogl_object_unref (tex2); test_utils_check_pixel (test_fb, 5, 5, 0xffff00ff); } static void test_snippet_properties (TestState *state) { CoglSnippet *snippet; /* Sanity check modifying the snippet */ snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, "foo", "bar"); g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "foo"); g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar"); g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); cogl_snippet_set_declarations (snippet, "fu"); g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "bar"); g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); cogl_snippet_set_post (snippet, "ba"); g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, NULL); cogl_snippet_set_pre (snippet, "fuba"); g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, NULL); g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba"); cogl_snippet_set_replace (snippet, "baba"); g_assert_cmpstr (cogl_snippet_get_declarations (snippet), ==, "fu"); g_assert_cmpstr (cogl_snippet_get_post (snippet), ==, "ba"); g_assert_cmpstr (cogl_snippet_get_replace (snippet), ==, "baba"); g_assert_cmpstr (cogl_snippet_get_pre (snippet), ==, "fuba"); g_assert_cmpint (cogl_snippet_get_hook (snippet), ==, COGL_SNIPPET_HOOK_FRAGMENT); } static SnippetTestFunc tests[] = { simple_fragment_snippet, simple_vertex_snippet, shared_uniform, lots_snippets, shared_variable_pre_post, test_pipeline_caching, test_replace_string, test_texture_lookup_hook, test_multiple_samples, test_replace_lookup_hook, test_replace_snippet, test_replace_fragment_layer, test_modify_fragment_layer, test_modify_vertex_layer, test_replace_vertex_layer, test_vertex_transform_hook, test_global_fragment_hook, test_global_vertex_hook, test_snippet_order, test_naming_texture_units, test_snippet_properties }; static void run_tests (TestState *state) { int i; for (i = 0; i < G_N_ELEMENTS (tests); i++) { cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); tests[i] (state); } } void test_snippets (void) { TestState state; state.fb_width = cogl_framebuffer_get_width (test_fb); state.fb_height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, state.fb_width, state.fb_height, -1, 100); run_tests (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-object.c0000664000175000017500000000402314723361714020061 0ustar fabiofabio #include #include #include #include "test-conform-common.h" CoglUserDataKey private_key0; CoglUserDataKey private_key1; CoglUserDataKey private_key2; static int user_data0; static int user_data1; static int user_data2; static int destroy0_count = 0; static int destroy1_count = 0; static int destroy2_count = 0; static void destroy0_cb (void *user_data) { g_assert (user_data == &user_data0); destroy0_count++; } static void destroy1_cb (void *user_data) { g_assert (user_data == &user_data1); destroy1_count++; } static void destroy2_cb (void *user_data) { g_assert (user_data == &user_data2); destroy2_count++; } void test_object (TestUtilsGTestFixture *fixture, void *data) { CoglPath *path; /* Assuming that COGL_OBJECT_N_PRE_ALLOCATED_USER_DATA_ENTRIES == 2 * test associating 2 pointers to private data with an object */ cogl_path_new (); path = cogl_get_path (); cogl_object_set_user_data (COGL_OBJECT (path), &private_key0, &user_data0, destroy0_cb); cogl_object_set_user_data (COGL_OBJECT (path), &private_key1, &user_data1, destroy1_cb); cogl_object_set_user_data (COGL_OBJECT (path), &private_key2, &user_data2, destroy2_cb); cogl_object_set_user_data (COGL_OBJECT (path), &private_key1, NULL, destroy1_cb); cogl_object_set_user_data (COGL_OBJECT (path), &private_key1, &user_data1, destroy1_cb); cogl_object_unref (path); g_assert_cmpint (destroy0_count, ==, 1); g_assert_cmpint (destroy1_count, ==, 2); g_assert_cmpint (destroy2_count, ==, 1); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-path-clip.c0000664000175000017500000000415014723361714020475 0ustar fabiofabio#include #include #include #include "test-declarations.h" #include "test-utils.h" void test_path_clip (void) { CoglPath *path; CoglPipeline *pipeline; int fb_width, fb_height; fb_width = cogl_framebuffer_get_width (test_fb); fb_height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100); path = cogl_path_new (); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1.0f, 0.0f, 0.0f, 1.0f); /* Make an L-shape with the top right corner left untouched */ cogl_path_move_to (path, 0, fb_height); cogl_path_line_to (path, fb_width, fb_height); cogl_path_line_to (path, fb_width, fb_height / 2); cogl_path_line_to (path, fb_width / 2, fb_height / 2); cogl_path_line_to (path, fb_width / 2, 0); cogl_path_line_to (path, 0, 0); cogl_path_close (path); cogl_framebuffer_push_path_clip (test_fb, path); /* Try to fill the framebuffer with a blue rectangle. This should be * clipped to leave the top right quadrant as is */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0, 0, 255, 255); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, fb_width, fb_height); cogl_framebuffer_pop_clip (test_fb); cogl_object_unref (pipeline); cogl_object_unref (path); /* Check each of the four quadrants */ test_utils_check_pixel (test_fb, fb_width / 4, fb_height / 4, 0x0000ffff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height / 4, 0xff0000ff); test_utils_check_pixel (test_fb, fb_width / 4, fb_height * 3 / 4, 0x0000ffff); test_utils_check_pixel (test_fb, fb_width * 3 / 4, fb_height * 3 / 4, 0x0000ffff); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-no-gl-header.c0000664000175000017500000000044214723361714021056 0ustar fabiofabio#undef COGL_COMPILATION #include /* If you just include cogl/cogl.h, you shouldn't end up including any GL headers */ #ifdef GL_TRUE #error "Including cogl.h shouldn't be including any GL headers" #endif void test_no_gl_header (void); void test_no_gl_header (void) { } muffin-6.4.1/cogl/tests/conform/test-fence.c0000664000175000017500000000307514723361714017701 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" #include "cogl-config.h" /* I'm writing this on the train after having dinner at a churrascuria. */ #define MAGIC_CHUNK_O_DATA ((void *) 0xdeadbeef) static GMainLoop *loop; static gboolean timeout (void *user_data) { g_assert (!"timeout not reached"); return FALSE; } static void callback (CoglFence *fence, void *user_data) { int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); test_utils_check_pixel (test_fb, fb_width - 1, fb_height - 1, 0x00ff0000); g_assert (user_data == MAGIC_CHUNK_O_DATA && "callback data not mangled"); g_main_loop_quit (loop); } void test_fence (void) { GSource *cogl_source; int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); CoglFenceClosure *closure; cogl_source = cogl_glib_source_new (test_ctx, G_PRIORITY_DEFAULT); g_source_attach (cogl_source, NULL); loop = g_main_loop_new (NULL, TRUE); cogl_framebuffer_orthographic (test_fb, 0, 0, fb_width, fb_height, -1, 100); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0.0f, 1.0f, 0.0f, 0.0f); closure = cogl_framebuffer_add_fence_callback (test_fb, callback, MAGIC_CHUNK_O_DATA); g_assert (closure != NULL); g_timeout_add_seconds (5, timeout, NULL); g_main_loop_run (loop); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-framebuffer-get-bits.c0000664000175000017500000000271314723361714022617 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" void test_framebuffer_get_bits (void) { CoglTexture2D *tex_a = cogl_texture_2d_new_with_size (test_ctx, 16, 16); /* width/height */ CoglOffscreen *offscreen_a = cogl_offscreen_new_with_texture (tex_a); CoglFramebuffer *fb_a = offscreen_a; CoglTexture2D *tex_rgba = cogl_texture_2d_new_with_size (test_ctx, 16, 16); /* width/height */ CoglOffscreen *offscreen_rgba = cogl_offscreen_new_with_texture (tex_rgba); CoglFramebuffer *fb_rgba = offscreen_rgba; cogl_texture_set_components (tex_a, COGL_TEXTURE_COMPONENTS_A); cogl_framebuffer_allocate (fb_a, NULL); cogl_framebuffer_allocate (fb_rgba, NULL); g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_a), ==, 0); g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_a), ==, 0); g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_a), ==, 0); g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_a), >=, 1); g_assert_cmpint (cogl_framebuffer_get_red_bits (fb_rgba), >=, 1); g_assert_cmpint (cogl_framebuffer_get_green_bits (fb_rgba), >=, 1); g_assert_cmpint (cogl_framebuffer_get_blue_bits (fb_rgba), >=, 1); g_assert_cmpint (cogl_framebuffer_get_alpha_bits (fb_rgba), >=, 1); cogl_object_unref (fb_rgba); cogl_object_unref (tex_rgba); cogl_object_unref (fb_a); cogl_object_unref (tex_a); } muffin-6.4.1/cogl/tests/conform/test-primitive.c0000664000175000017500000002346214723361714020633 0ustar fabiofabio#include #include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { int fb_width; int fb_height; } TestState; #define PRIM_COLOR 0xff00ffff #define TEX_COLOR 0x0000ffff #define N_ATTRIBS 8 typedef CoglPrimitive * (* TestPrimFunc) (CoglContext *ctx, uint32_t *expected_color); static CoglPrimitive * test_prim_p2 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP2 verts[] = { { 0, 0 }, { 0, 10 }, { 10, 0 } }; return cogl_primitive_new_p2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p3 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP3 verts[] = { { 0, 0, 0 }, { 0, 10, 0 }, { 10, 0, 0 } }; return cogl_primitive_new_p3 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p2c4 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP2C4 verts[] = { { 0, 0, 255, 255, 0, 255 }, { 0, 10, 255, 255, 0, 255 }, { 10, 0, 255, 255, 0, 255 } }; *expected_color = 0xffff00ff; return cogl_primitive_new_p2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p3c4 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP3C4 verts[] = { { 0, 0, 0, 255, 255, 0, 255 }, { 0, 10, 0, 255, 255, 0, 255 }, { 10, 0, 0, 255, 255, 0, 255 } }; *expected_color = 0xffff00ff; return cogl_primitive_new_p3c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p2t2 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP2T2 verts[] = { { 0, 0, 1, 0 }, { 0, 10, 1, 0 }, { 10, 0, 1, 0 } }; *expected_color = TEX_COLOR; return cogl_primitive_new_p2t2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p3t2 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP3T2 verts[] = { { 0, 0, 0, 1, 0 }, { 0, 10, 0, 1, 0 }, { 10, 0, 0, 1, 0 } }; *expected_color = TEX_COLOR; return cogl_primitive_new_p3t2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p2t2c4 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP2T2C4 verts[] = { { 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, { 0, 10, 1, 0, 0xff, 0xff, 0xf0, 0xff }, { 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } }; /* The blue component of the texture color should be replaced with 0xf0 */ *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000; return cogl_primitive_new_p2t2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static CoglPrimitive * test_prim_p3t2c4 (CoglContext *ctx, uint32_t *expected_color) { static const CoglVertexP3T2C4 verts[] = { { 0, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, { 0, 10, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff }, { 10, 0, 0, 1, 0, 0xff, 0xff, 0xf0, 0xff } }; /* The blue component of the texture color should be replaced with 0xf0 */ *expected_color = (TEX_COLOR & 0xffff00ff) | 0x0000f000; return cogl_primitive_new_p3t2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ verts); } static const TestPrimFunc test_prim_funcs[] = { test_prim_p2, test_prim_p3, test_prim_p2c4, test_prim_p3c4, test_prim_p2t2, test_prim_p3t2, test_prim_p2t2c4, test_prim_p3t2c4 }; static void test_paint (TestState *state) { CoglPipeline *pipeline; CoglTexture *tex; uint8_t tex_data[6]; int i; /* Create a two pixel texture. The first pixel is white and the second pixel is tex_color. The assumption is that if no texture coordinates are specified then it will default to 0,0 and get white */ tex_data[0] = 255; tex_data[1] = 255; tex_data[2] = 255; tex_data[3] = (TEX_COLOR >> 24) & 0xff; tex_data[4] = (TEX_COLOR >> 16) & 0xff; tex_data[5] = (TEX_COLOR >> 8) & 0xff; tex = test_utils_texture_new_from_data (test_ctx, 2, 1, /* size */ TEST_UTILS_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGB_888, 6, /* rowstride */ tex_data); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, (PRIM_COLOR >> 24) & 0xff, (PRIM_COLOR >> 16) & 0xff, (PRIM_COLOR >> 8) & 0xff, (PRIM_COLOR >> 0) & 0xff); cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_object_unref (tex); for (i = 0; i < G_N_ELEMENTS (test_prim_funcs); i++) { CoglPrimitive *prim; uint32_t expected_color = PRIM_COLOR; prim = test_prim_funcs[i] (test_ctx, &expected_color); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, i * 10, 0, 0); cogl_primitive_draw (prim, test_fb, pipeline); cogl_framebuffer_pop_matrix (test_fb); test_utils_check_pixel (test_fb, i * 10 + 2, 2, expected_color); cogl_object_unref (prim); } cogl_object_unref (pipeline); } static gboolean get_attributes_cb (CoglPrimitive *prim, CoglAttribute *attrib, void *user_data) { CoglAttribute ***p = user_data; *((* p)++) = attrib; return TRUE; } static int compare_pointers (const void *a, const void *b) { CoglAttribute *pa = *(CoglAttribute **) a; CoglAttribute *pb = *(CoglAttribute **) b; if (pa < pb) return -1; else if (pa > pb) return 1; else return 0; } static void test_copy (TestState *state) { static const uint16_t indices_data[2] = { 1, 2 }; CoglAttributeBuffer *buffer = cogl_attribute_buffer_new (test_ctx, 100, NULL); CoglAttribute *attributes[N_ATTRIBS]; CoglAttribute *attributes_a[N_ATTRIBS], *attributes_b[N_ATTRIBS]; CoglAttribute **p; CoglPrimitive *prim_a, *prim_b; CoglIndices *indices; int i; for (i = 0; i < N_ATTRIBS; i++) { char *name = g_strdup_printf ("foo_%i", i); attributes[i] = cogl_attribute_new (buffer, name, 16, /* stride */ 16, /* offset */ 2, /* components */ COGL_ATTRIBUTE_TYPE_FLOAT); g_free (name); } prim_a = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 8, /* n_vertices */ attributes, N_ATTRIBS); indices = cogl_indices_new (test_ctx, COGL_INDICES_TYPE_UNSIGNED_SHORT, indices_data, 2 /* n_indices */); cogl_primitive_set_first_vertex (prim_a, 12); cogl_primitive_set_indices (prim_a, indices, 2); prim_b = cogl_primitive_copy (prim_a); p = attributes_a; cogl_primitive_foreach_attribute (prim_a, get_attributes_cb, &p); g_assert_cmpint (p - attributes_a, ==, N_ATTRIBS); p = attributes_b; cogl_primitive_foreach_attribute (prim_b, get_attributes_cb, &p); g_assert_cmpint (p - attributes_b, ==, N_ATTRIBS); qsort (attributes_a, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers); qsort (attributes_b, N_ATTRIBS, sizeof (CoglAttribute *), compare_pointers); g_assert (memcmp (attributes_a, attributes_b, sizeof (attributes_a)) == 0); g_assert_cmpint (cogl_primitive_get_first_vertex (prim_a), ==, cogl_primitive_get_first_vertex (prim_b)); g_assert_cmpint (cogl_primitive_get_n_vertices (prim_a), ==, cogl_primitive_get_n_vertices (prim_b)); g_assert_cmpint (cogl_primitive_get_mode (prim_a), ==, cogl_primitive_get_mode (prim_b)); g_assert (cogl_primitive_get_indices (prim_a) == cogl_primitive_get_indices (prim_b)); cogl_object_unref (prim_a); cogl_object_unref (prim_b); cogl_object_unref (indices); for (i = 0; i < N_ATTRIBS; i++) cogl_object_unref (attributes[i]); cogl_object_unref (buffer); } void test_primitive (void) { TestState state; state.fb_width = cogl_framebuffer_get_width (test_fb); state.fb_height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, state.fb_width, state.fb_height, -1, 100); test_paint (&state); test_copy (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-sub-texture.c0000664000175000017500000002507714723361714021116 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define SOURCE_SIZE 32 #define SOURCE_DIVISIONS_X 2 #define SOURCE_DIVISIONS_Y 2 #define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) #define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) #define TEST_INSET 1 static const uint32_t corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = { 0xff0000ff, /* red top left */ 0x00ff00ff, /* green top right */ 0x0000ffff, /* blue bottom left */ 0xff00ffff /* purple bottom right */ }; typedef struct _TestState { CoglTexture2D *tex; } TestState; static CoglTexture2D * create_source (TestState *state) { int dx, dy; uint8_t *data = g_malloc (SOURCE_SIZE * SOURCE_SIZE * 4); CoglTexture2D *tex; /* Create a texture with a different coloured rectangle at each corner */ for (dy = 0; dy < SOURCE_DIVISIONS_Y; dy++) for (dx = 0; dx < SOURCE_DIVISIONS_X; dx++) { uint8_t *p = (data + dy * DIVISION_HEIGHT * SOURCE_SIZE * 4 + dx * DIVISION_WIDTH * 4); int x, y; for (y = 0; y < DIVISION_HEIGHT; y++) { for (x = 0; x < DIVISION_WIDTH; x++) { uint32_t color = GUINT32_FROM_BE (corner_colors[dx + dy * SOURCE_DIVISIONS_X]); memcpy (p, &color, 4); p += 4; } p += SOURCE_SIZE * 4 - DIVISION_WIDTH * 4; } } tex = cogl_texture_2d_new_from_data (test_ctx, SOURCE_SIZE, SOURCE_SIZE, COGL_PIXEL_FORMAT_RGBA_8888, SOURCE_SIZE * 4, data, NULL); return tex; } static CoglTexture2D * create_test_texture (TestState *state) { CoglTexture2D *tex; uint8_t *data = g_malloc (256 * 256 * 4), *p = data; int x, y; /* Create a texture that is 256x256 where the red component ranges from 0->255 along the x axis and the green component ranges from 0->255 along the y axis. The blue and alpha components are all 255 */ for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { *(p++) = x; *(p++) = y; *(p++) = 255; *(p++) = 255; } tex = cogl_texture_2d_new_from_data (test_ctx, 256, 256, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4, data, NULL); g_free (data); return tex; } static void paint (TestState *state) { CoglTexture2D *full_texture; CoglSubTexture *sub_texture, *sub_sub_texture; CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); /* Create a sub texture of the bottom right quarter of the texture */ sub_texture = cogl_sub_texture_new (test_ctx, state->tex, DIVISION_WIDTH, DIVISION_HEIGHT, DIVISION_WIDTH, DIVISION_HEIGHT); /* Paint it */ cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture); cogl_object_unref (sub_texture); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0.0f, 0.0f, DIVISION_WIDTH, DIVISION_HEIGHT); /* Repeat a sub texture of the top half of the full texture. This is documented to be undefined so it doesn't technically have to work but it will with the current implementation */ sub_texture = cogl_sub_texture_new (test_ctx, state->tex, 0, 0, SOURCE_SIZE, DIVISION_HEIGHT); cogl_pipeline_set_layer_texture (pipeline, 0, sub_texture); cogl_object_unref (sub_texture); cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, 0.0f, SOURCE_SIZE, SOURCE_SIZE * 2.0f, SOURCE_SIZE * 1.5f, 0.0f, 0.0f, 2.0f, 1.0f); /* Create a sub texture of a sub texture */ full_texture = create_test_texture (state); sub_texture = cogl_sub_texture_new (test_ctx, full_texture, 20, 10, 30, 20); cogl_object_unref (full_texture); sub_sub_texture = cogl_sub_texture_new (test_ctx, sub_texture, 20, 10, 10, 10); cogl_object_unref (sub_texture); cogl_pipeline_set_layer_texture (pipeline, 0, sub_sub_texture); cogl_object_unref (sub_sub_texture); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0.0f, SOURCE_SIZE * 2.0f, 10.0f, SOURCE_SIZE * 2.0f + 10.0f); cogl_object_unref (pipeline); } static void validate_part (int xpos, int ypos, int width, int height, uint32_t color) { test_utils_check_region (test_fb, xpos + TEST_INSET, ypos + TEST_INSET, width - TEST_INSET - 2, height - TEST_INSET - 2, color); } static uint8_t * create_update_data (void) { uint8_t *data = g_malloc (256 * 256 * 4), *p = data; int x, y; /* Create some image data that is 256x256 where the blue component ranges from 0->255 along the x axis and the alpha component ranges from 0->255 along the y axis. The red and green components are all zero */ for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { *(p++) = 0; *(p++) = 0; *(p++) = x; *(p++) = y; } return data; } static void validate_result (TestState *state) { int i, division_num, x, y; CoglTexture2D *test_tex; CoglSubTexture *sub_texture; uint8_t *texture_data, *p; int tex_width, tex_height; /* Sub texture of the bottom right corner of the texture */ validate_part (0, 0, DIVISION_WIDTH, DIVISION_HEIGHT, corner_colors[ (SOURCE_DIVISIONS_Y - 1) * SOURCE_DIVISIONS_X + SOURCE_DIVISIONS_X - 1]); /* Sub texture of the top half repeated horizontally */ for (i = 0; i < 2; i++) for (division_num = 0; division_num < SOURCE_DIVISIONS_X; division_num++) validate_part (i * SOURCE_SIZE + division_num * DIVISION_WIDTH, SOURCE_SIZE, DIVISION_WIDTH, DIVISION_HEIGHT, corner_colors[division_num]); /* Sub sub texture */ p = texture_data = g_malloc (10 * 10 * 4); cogl_flush (); cogl_framebuffer_read_pixels (test_fb, 0, SOURCE_SIZE * 2, 10, 10, COGL_PIXEL_FORMAT_RGBA_8888, p); for (y = 0; y < 10; y++) for (x = 0; x < 10; x++) { g_assert (*(p++) == x + 40); g_assert (*(p++) == y + 20); p += 2; } g_free (texture_data); /* Try reading back the texture data */ sub_texture = cogl_sub_texture_new (test_ctx, state->tex, SOURCE_SIZE / 4, SOURCE_SIZE / 4, SOURCE_SIZE / 2, SOURCE_SIZE / 2); tex_width = cogl_texture_get_width (sub_texture); tex_height = cogl_texture_get_height (sub_texture); p = texture_data = g_malloc (tex_width * tex_height * 4); cogl_texture_get_data (sub_texture, COGL_PIXEL_FORMAT_RGBA_8888, tex_width * 4, texture_data); for (y = 0; y < tex_height; y++) for (x = 0; x < tex_width; x++) { int div_x = ((x * SOURCE_SIZE / 2 / tex_width + SOURCE_SIZE / 4) / DIVISION_WIDTH); int div_y = ((y * SOURCE_SIZE / 2 / tex_height + SOURCE_SIZE / 4) / DIVISION_HEIGHT); uint32_t reference = corner_colors[div_x + div_y * SOURCE_DIVISIONS_X] >> 8; uint32_t color = GUINT32_FROM_BE (*((uint32_t *)p)) >> 8; g_assert (color == reference); p += 4; } g_free (texture_data); cogl_object_unref (sub_texture); /* Create a 256x256 test texture */ test_tex = create_test_texture (state); /* Create a sub texture the views the center half of the texture */ sub_texture = cogl_sub_texture_new (test_ctx, test_tex, 64, 64, 128, 128); /* Update the center half of the sub texture */ texture_data = create_update_data (); cogl_texture_set_region (sub_texture, 0, 0, 32, 32, 64, 64, 256, 256, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4, texture_data); g_free (texture_data); cogl_object_unref (sub_texture); /* Get the texture data */ p = texture_data = g_malloc (256 * 256 * 4); cogl_texture_get_data (test_tex, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 256 * 4, texture_data); /* Verify the texture data */ for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { /* If we're in the center quarter */ if (x >= 96 && x < 160 && y >= 96 && y < 160) { g_assert ((*p++) == 0); g_assert ((*p++) == 0); g_assert ((*p++) == x - 96); g_assert ((*p++) == y - 96); } else { g_assert ((*p++) == x); g_assert ((*p++) == y); g_assert ((*p++) == 255); g_assert ((*p++) == 255); } } g_free (texture_data); cogl_object_unref (test_tex); } void test_sub_texture (void) { TestState state; state.tex = create_source (&state); cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); validate_result (&state); cogl_object_unref (state.tex); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-sparse-pipeline.c0000664000175000017500000000326114723361714021716 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { int fb_width; int fb_height; } TestState; static void test_sparse_layer_combine (TestState *state) { CoglPipeline *pipeline; CoglTexture *tex1, *tex2; cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); /* This tests that the TEXTURE_* numbers used in the layer combine string refer to the layer number rather than the unit numbers by creating a pipeline with very large layer numbers. This should end up being mapped to much smaller unit numbers */ tex1 = test_utils_create_color_texture (test_ctx, 0xff0000ff); tex2 = test_utils_create_color_texture (test_ctx, 0x00ff00ff); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 50, tex1); cogl_pipeline_set_layer_texture (pipeline, 100, tex2); cogl_pipeline_set_layer_combine (pipeline, 200, "RGBA = ADD(TEXTURE_50, TEXTURE_100)", NULL); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, -1, 1, 1); test_utils_check_pixel (test_fb, 2, 2, 0xffff00ff); cogl_object_unref (pipeline); cogl_object_unref (tex1); cogl_object_unref (tex2); } void test_sparse_pipeline (void) { TestState state; state.fb_width = cogl_framebuffer_get_width (test_fb); state.fb_height = cogl_framebuffer_get_height (test_fb); test_sparse_layer_combine (&state); /* FIXME: This should have a lot more tests, for example testing whether using an attribute with sparse texture coordinates will work */ if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-texture-rg.c0000664000175000017500000000422514723361714020725 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define TEX_WIDTH 8 #define TEX_HEIGHT 8 static CoglTexture2D * make_texture (void) { uint8_t tex_data[TEX_WIDTH * TEX_HEIGHT * 2], *p = tex_data; int x, y; for (y = 0; y < TEX_HEIGHT; y++) for (x = 0; x < TEX_WIDTH; x++) { *(p++) = x * 256 / TEX_WIDTH; *(p++) = y * 256 / TEX_HEIGHT; } return cogl_texture_2d_new_from_data (test_ctx, TEX_WIDTH, TEX_HEIGHT, COGL_PIXEL_FORMAT_RG_88, TEX_WIDTH * 2, tex_data, NULL); } void test_texture_rg (void) { CoglPipeline *pipeline; CoglTexture2D *tex; int fb_width, fb_height; int x, y; fb_width = cogl_framebuffer_get_width (test_fb); fb_height = cogl_framebuffer_get_height (test_fb); tex = make_texture (); g_assert (cogl_texture_get_components (tex) == COGL_TEXTURE_COMPONENTS_RG); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1.0f, 1.0f, 1.0f, -1.0f); for (y = 0; y < TEX_HEIGHT; y++) for (x = 0; x < TEX_WIDTH; x++) { test_utils_check_pixel_rgb (test_fb, x * fb_width / TEX_WIDTH + fb_width / (TEX_WIDTH * 2), y * fb_height / TEX_HEIGHT + fb_height / (TEX_HEIGHT * 2), x * 256 / TEX_WIDTH, y * 256 / TEX_HEIGHT, 0); } cogl_object_unref (pipeline); cogl_object_unref (tex); } muffin-6.4.1/cogl/tests/conform/test-texture-pixmap-x11.c0000664000175000017500000001554514723361714022231 0ustar fabiofabio#include #include "test-conform-common.h" static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; #ifdef COGL_HAS_XLIB #include #include #define PIXMAP_WIDTH 512 #define PIXMAP_HEIGHT 256 #define GRID_SQUARE_SIZE 16 /* Coordinates of a square that we'll update */ #define PIXMAP_CHANGE_X 1 #define PIXMAP_CHANGE_Y 1 typedef struct _TestState { ClutterActor *stage; CoglHandle tfp; Pixmap pixmap; unsigned int frame_count; Display *display; } TestState; static Pixmap create_pixmap (TestState *state) { Pixmap pixmap; XGCValues gc_values = { 0, }; GC black_gc, white_gc; int screen = DefaultScreen (state->display); int x, y; pixmap = XCreatePixmap (state->display, DefaultRootWindow (state->display), PIXMAP_WIDTH, PIXMAP_HEIGHT, DefaultDepth (state->display, screen)); gc_values.foreground = BlackPixel (state->display, screen); black_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values); gc_values.foreground = WhitePixel (state->display, screen); white_gc = XCreateGC (state->display, pixmap, GCForeground, &gc_values); /* Draw a grid of alternative black and white rectangles to the pixmap */ for (y = 0; y < PIXMAP_HEIGHT / GRID_SQUARE_SIZE; y++) for (x = 0; x < PIXMAP_WIDTH / GRID_SQUARE_SIZE; x++) XFillRectangle (state->display, pixmap, ((x ^ y) & 1) ? black_gc : white_gc, x * GRID_SQUARE_SIZE, y * GRID_SQUARE_SIZE, GRID_SQUARE_SIZE, GRID_SQUARE_SIZE); XFreeGC (state->display, black_gc); XFreeGC (state->display, white_gc); return pixmap; } static void update_pixmap (TestState *state) { XGCValues gc_values = { 0, }; GC black_gc; int screen = DefaultScreen (state->display); gc_values.foreground = BlackPixel (state->display, screen); black_gc = XCreateGC (state->display, state->pixmap, GCForeground, &gc_values); /* Fill in one the rectangles with black */ XFillRectangle (state->display, state->pixmap, black_gc, PIXMAP_CHANGE_X * GRID_SQUARE_SIZE, PIXMAP_CHANGE_Y * GRID_SQUARE_SIZE, GRID_SQUARE_SIZE, GRID_SQUARE_SIZE); XFreeGC (state->display, black_gc); } static gboolean check_paint (TestState *state, int x, int y, int scale) { uint8_t *data, *p, update_value = 0; p = data = g_malloc (PIXMAP_WIDTH * PIXMAP_HEIGHT * 4); cogl_read_pixels (x, y, PIXMAP_WIDTH / scale, PIXMAP_HEIGHT / scale, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); for (y = 0; y < PIXMAP_HEIGHT / scale; y++) for (x = 0; x < PIXMAP_WIDTH / scale; x++) { int grid_x = x * scale / GRID_SQUARE_SIZE; int grid_y = y * scale / GRID_SQUARE_SIZE; /* If this is the updatable square then we'll let it be either color but we'll return which one it was */ if (grid_x == PIXMAP_CHANGE_X && grid_y == PIXMAP_CHANGE_Y) { if (x % (GRID_SQUARE_SIZE / scale) == 0 && y % (GRID_SQUARE_SIZE / scale) == 0) update_value = *p; else g_assert_cmpint (p[0], ==, update_value); g_assert (p[1] == update_value); g_assert (p[2] == update_value); p += 4; } else { uint8_t value = ((grid_x ^ grid_y) & 1) ? 0x00 : 0xff; g_assert_cmpint (*(p++), ==, value); g_assert_cmpint (*(p++), ==, value); g_assert_cmpint (*(p++), ==, value); p++; } } g_free (data); return update_value == 0x00; } /* We skip these frames first */ #define FRAME_COUNT_BASE 5 /* First paint the tfp with no mipmaps */ #define FRAME_COUNT_NORMAL 6 /* Then use mipmaps */ #define FRAME_COUNT_MIPMAP 7 /* After this frame will start waiting for the pixmap to change */ #define FRAME_COUNT_UPDATED 8 static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, TestState *state) { CoglHandle material; material = cogl_material_new (); cogl_material_set_layer (material, 0, state->tfp); if (state->frame_count == FRAME_COUNT_MIPMAP) { const CoglMaterialFilter min_filter = COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST; cogl_material_set_layer_filters (material, 0, min_filter, COGL_MATERIAL_FILTER_NEAREST); } else cogl_material_set_layer_filters (material, 0, COGL_MATERIAL_FILTER_NEAREST, COGL_MATERIAL_FILTER_NEAREST); cogl_set_source (material); cogl_rectangle (0, 0, PIXMAP_WIDTH, PIXMAP_HEIGHT); cogl_rectangle (0, PIXMAP_HEIGHT, PIXMAP_WIDTH / 4, PIXMAP_HEIGHT * 5 / 4); if (state->frame_count >= 5) { gboolean big_updated, small_updated; big_updated = check_paint (state, 0, 0, 1); small_updated = check_paint (state, 0, PIXMAP_HEIGHT, 4); g_assert (big_updated == small_updated); if (state->frame_count < FRAME_COUNT_UPDATED) g_assert (big_updated == FALSE); else if (state->frame_count == FRAME_COUNT_UPDATED) /* Change the pixmap and keep drawing until it updates */ update_pixmap (state); else if (big_updated) /* If we successfully got the update then the test is over */ clutter_main_quit (); } state->frame_count++; } static gboolean queue_redraw (void *stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } #endif /* COGL_HAS_XLIB */ void test_texture_pixmap_x11 (TestUtilsGTestFixture *fixture, void *data) { #ifdef COGL_HAS_XLIB TestState state; unsigned int idle_handler; unsigned long paint_handler; state.frame_count = 0; state.stage = clutter_stage_get_default (); state.display = clutter_x11_get_default_display (); state.pixmap = create_pixmap (&state); state.tfp = cogl_texture_pixmap_x11_new (state.pixmap, TRUE); clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); paint_handler = g_signal_connect_after (state.stage, "paint", G_CALLBACK (on_paint), &state); idle_handler = g_idle_add (queue_redraw, state.stage); clutter_actor_show_all (state.stage); clutter_main (); g_clear_signal_handler (&paint_handler, state.stage); g_clear_handle_id (&idle_handler, g_source_remove); XFreePixmap (state.display, state.pixmap); if (cogl_test_verbose ()) g_print ("OK\n"); #else /* COGL_HAS_XLIB */ if (cogl_test_verbose ()) g_print ("Skipping\n"); #endif /* COGL_HAS_XLIB */ } muffin-6.4.1/cogl/tests/conform/test-point-size.c0000664000175000017500000000610014723361714020712 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" /* This test assumes the GL driver supports point sizes up to 16 pixels. Cogl should probably have some way of querying the size so we start from that instead */ #define MAX_POINT_SIZE 16 /* The size of the area that we'll paint each point in */ #define POINT_BOX_SIZE (MAX_POINT_SIZE * 2) static int calc_coord_offset (int pos, int pos_index, int point_size) { switch (pos_index) { case 0: return pos - point_size / 2 - 2; case 1: return pos - point_size / 2 + 2; case 2: return pos + point_size / 2 - 2; case 3: return pos + point_size / 2 + 2; } g_assert_not_reached (); return 0; } static void verify_point_size (CoglFramebuffer *test_fb, int x_pos, int y_pos, int point_size) { int y, x; for (y = 0; y < 4; y++) for (x = 0; x < 4; x++) { gboolean in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2; uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff; test_utils_check_pixel (test_fb, calc_coord_offset (x_pos, x, point_size), calc_coord_offset (y_pos, y, point_size), expected_pixel); } } void test_point_size (void) { int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); int point_size; int x_pos; cogl_framebuffer_orthographic (test_fb, 0, 0, /* x_1, y_1 */ fb_width, /* x_2 */ fb_height /* y_2 */, -1, 100 /* near/far */); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1.0f, 0.0f, 0.0f, 1.0f); /* Try a rendering a single point with a few different point sizes */ for (x_pos = 0, point_size = MAX_POINT_SIZE; point_size >= 4; x_pos += POINT_BOX_SIZE, point_size /= 2) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglVertexP2 point = { x_pos + POINT_BOX_SIZE / 2, POINT_BOX_SIZE / 2 }; CoglPrimitive *prim = cogl_primitive_new_p2 (test_ctx, COGL_VERTICES_MODE_POINTS, 1, /* n_vertices */ &point); cogl_pipeline_set_point_size (pipeline, point_size); cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 255); cogl_primitive_draw (prim, test_fb, pipeline); cogl_object_unref (prim); cogl_object_unref (pipeline); } /* Verify all of the points where drawn at the right size */ for (x_pos = 0, point_size = MAX_POINT_SIZE; point_size >= 4; x_pos += POINT_BOX_SIZE, point_size /= 2) verify_point_size (test_fb, x_pos + POINT_BOX_SIZE / 2, POINT_BOX_SIZE / 2, point_size); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-wrap-modes.c0000664000175000017500000001644414723361714020703 0ustar fabiofabio#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 #include #include #include "test-declarations.h" #include "test-utils.h" #define TEX_SIZE 4 typedef struct _TestState { int width; int height; CoglTexture *texture; } TestState; static CoglTexture * create_texture (TestUtilsTextureFlags flags) { uint8_t *data = g_malloc (TEX_SIZE * TEX_SIZE * 4), *p = data; CoglTexture *tex; int x, y; for (y = 0; y < TEX_SIZE; y++) for (x = 0; x < TEX_SIZE; x++) { *(p++) = 0; *(p++) = (x & 1) * 255; *(p++) = (y & 1) * 255; *(p++) = 255; } tex = test_utils_texture_new_from_data (test_ctx, TEX_SIZE, TEX_SIZE, flags, COGL_PIXEL_FORMAT_RGBA_8888_PRE, TEX_SIZE * 4, data); g_free (data); return tex; } static CoglPipeline * create_pipeline (TestState *state, CoglPipelineWrapMode wrap_mode_s, CoglPipelineWrapMode wrap_mode_t) { CoglPipeline *pipeline; pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, state->texture); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0, wrap_mode_s); cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0, wrap_mode_t); return pipeline; } static CoglPipelineWrapMode wrap_modes[] = { COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_REPEAT, }; static void draw_tests (TestState *state) { int i; for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) { CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; CoglPipeline *pipeline; /* Create a separate pipeline for each pair of wrap modes so that we can verify whether the batch splitting works */ wrap_mode_s = wrap_modes[i]; wrap_mode_t = wrap_modes[i + 1]; pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t); /* Render the pipeline at four times the size of the texture */ cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, i * TEX_SIZE, 0, (i + 2) * TEX_SIZE, TEX_SIZE * 2, 0, 0, 2, 2); cogl_object_unref (pipeline); } } static const CoglVertexP3T2 vertices[4] = { { .x = 0.0f, .y = 0.0f, .z = 0.0f, .s = 0.0f, .t = 0.0f }, { .x = 0.0f, .y = TEX_SIZE * 2, .z = 0.0f, .s = 0.0f, .t = 2.0f }, { .x = TEX_SIZE * 2, .y = TEX_SIZE * 2, .z = 0.0f, .s = 2.0f, .t = 2.0f }, { .x = TEX_SIZE * 2, .y = 0.0f, .z = 0.0f, .s = 2.0f, .t = 0.0f } }; static void draw_tests_polygon (TestState *state) { int i; for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) { CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; CoglPrimitive *primitive; CoglPipeline *pipeline; wrap_mode_s = wrap_modes[i]; wrap_mode_t = wrap_modes[i + 1]; pipeline = create_pipeline (state, wrap_mode_s, wrap_mode_t); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, TEX_SIZE * i, 0.0f, 0.0f); /* Render the pipeline at four times the size of the texture */ primitive = cogl_primitive_new_p3t2 (test_ctx, COGL_VERTICES_MODE_TRIANGLE_FAN, G_N_ELEMENTS (vertices), vertices); cogl_primitive_draw (primitive, test_fb, pipeline); cogl_object_unref (primitive); cogl_object_unref (pipeline); cogl_framebuffer_pop_matrix (test_fb); } } static void validate_set (TestState *state, int offset) { uint8_t data[TEX_SIZE * 2 * TEX_SIZE * 2 * 4], *p; int x, y, i; for (i = 0; i < G_N_ELEMENTS (wrap_modes); i += 2) { CoglPipelineWrapMode wrap_mode_s, wrap_mode_t; wrap_mode_s = wrap_modes[i]; wrap_mode_t = wrap_modes[i + 1]; cogl_framebuffer_read_pixels (test_fb, i * TEX_SIZE, offset * TEX_SIZE * 2, TEX_SIZE * 2, TEX_SIZE * 2, COGL_PIXEL_FORMAT_RGBA_8888, data); p = data; for (y = 0; y < TEX_SIZE * 2; y++) for (x = 0; x < TEX_SIZE * 2; x++) { uint8_t green, blue; if (x < TEX_SIZE || wrap_mode_s == COGL_PIPELINE_WRAP_MODE_REPEAT || wrap_mode_s == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) green = (x & 1) * 255; else green = ((TEX_SIZE - 1) & 1) * 255; if (y < TEX_SIZE || wrap_mode_t == COGL_PIPELINE_WRAP_MODE_REPEAT || wrap_mode_t == COGL_PIPELINE_WRAP_MODE_AUTOMATIC) blue = (y & 1) * 255; else blue = ((TEX_SIZE - 1) & 1) * 255; g_assert_cmpint (p[0], ==, 0); g_assert_cmpint (p[1], ==, green); g_assert_cmpint (p[2], ==, blue); p += 4; } } } static void validate_result (TestState *state) { validate_set (state, 0); /* non-atlased rectangle */ #if 0 /* this doesn't currently work */ validate_set (state, 1); /* atlased rectangle */ #endif validate_set (state, 2); /* CoglPrimitive */ } static void paint (TestState *state) { /* Draw the tests first with a non atlased texture */ state->texture = create_texture (TEST_UTILS_TEXTURE_NO_ATLAS); draw_tests (state); cogl_object_unref (state->texture); /* Draw the tests again with a possible atlased texture. This should end up testing software repeats */ state->texture = create_texture (TEST_UTILS_TEXTURE_NONE); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, 0.0f, TEX_SIZE * 2.0f, 0.0f); draw_tests (state); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (state->texture); /* Draw the tests using CoglPrimitive */ state->texture = create_texture (TEST_UTILS_TEXTURE_NO_ATLAS); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, 0.0f, TEX_SIZE * 4.0f, 0.0f); draw_tests_polygon (state); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (state->texture); validate_result (state); } void test_wrap_modes (void) { TestState state; state.width = cogl_framebuffer_get_width (test_fb); state.height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, state.width, state.height, -1, 100); paint (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-declarations.h0000664000175000017500000000332014723361714021267 0ustar fabiofabio#ifndef COGL_TEST_DECLARATIONS_H #define COGL_TEST_DECLARATIONS_H void test_pipeline_user_matrix (void); void test_blend_strings (void); void test_blend (void); void test_premult (void); void test_path (void); void test_path_clip (void); void test_depth_test (void); void test_backface_culling (void); void test_layer_remove (void); void test_sparse_pipeline (void); void test_npot_texture (void); void test_sub_texture (void); void test_pixel_buffer_map (void); void test_pixel_buffer_set_data (void); void test_pixel_buffer_sub_region (void); void test_wrap_modes (void); void test_texture_get_set_data (void); void test_atlas_migration (void); void test_read_texture_formats (void); void test_write_texture_formats (void); void test_alpha_textures (void); void test_primitive (void); void test_just_vertex_shader (void); void test_pipeline_uniforms (void); void test_snippets (void); void test_custom_attributes (void); void test_offscreen (void); void test_framebuffer_get_bits (void); void test_point_size (void); void test_point_size_attribute (void); void test_point_size_attribute_snippet (void); void test_point_sprite (void); void test_point_sprite_orientation (void); void test_point_sprite_glsl (void); void test_alpha_test (void); void test_map_buffer_range (void); void test_primitive_and_journal (void); void test_copy_replace_texture (void); void test_pipeline_cache_unrefs_texture (void); void test_pipeline_shader_state (void); void test_gles2_context (void); void test_gles2_context_fbo (void); void test_gles2_context_copy_tex_image (void); void test_euler (void); void test_color_hsl (void); void test_fence (void); void test_texture_no_allocate (void); void test_texture_rg (void); #endif /* COGL_TEST_DECLARATIONS_H */ muffin-6.4.1/cogl/tests/conform/test-version.c0000664000175000017500000000301714723361714020302 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" #include "cogl-config.h" /* So we can use _COGL_STATIC_ASSERT we include the internal * cogl-util.h header. Since internal headers explicitly guard against * applications including them directly instead of including * we define __COGL_H_INSIDE__ here to subvert those * guards in this case... */ #define __COGL_H_INSIDE__ #include #undef __COGL_H_INSIDE__ _COGL_STATIC_ASSERT (COGL_VERSION_GET_MAJOR (COGL_VERSION_ENCODE (100, 200, 300)) == 100, "Getting the major component out of a encoded version " "does not work"); _COGL_STATIC_ASSERT (COGL_VERSION_GET_MINOR (COGL_VERSION_ENCODE (100, 200, 300)) == 200, "Getting the minor component out of a encoded version " "does not work"); _COGL_STATIC_ASSERT (COGL_VERSION_GET_MICRO (COGL_VERSION_ENCODE (100, 200, 300)) == 300, "Getting the micro component out of a encoded version " "does not work"); muffin-6.4.1/cogl/tests/conform/test-custom-attributes.c0000664000175000017500000002520014723361714022311 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { CoglPipeline *pipeline; } TestState; typedef struct { int16_t x, y; float r, g, b, a; } FloatVert; typedef struct { int16_t x, y; uint8_t r, g, b, a; } ByteVert; typedef struct { int16_t x, y; int16_t r, g, b, a; } ShortVert; static void test_float_verts (TestState *state, int offset_x, int offset_y) { CoglAttribute *attributes[2]; CoglAttributeBuffer *buffer; CoglPrimitive *primitive; static const FloatVert float_verts[] = { { 0, 10, /**/ 1, 0, 0, 1 }, { 10, 10, /**/ 1, 0, 0, 1 }, { 5, 0, /**/ 1, 0, 0, 1 }, { 10, 10, /**/ 0, 1, 0, 1 }, { 20, 10, /**/ 0, 1, 0, 1 }, { 15, 0, /**/ 0, 1, 0, 1 } }; buffer = cogl_attribute_buffer_new (test_ctx, sizeof (float_verts), float_verts); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (FloatVert), G_STRUCT_OFFSET (FloatVert, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_SHORT); attributes[1] = cogl_attribute_new (buffer, "color", sizeof (FloatVert), G_STRUCT_OFFSET (FloatVert, r), 4, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 6, /* n_vertices */ attributes, 2); /* n_attributes */ cogl_primitive_draw (primitive, test_fb, state->pipeline); cogl_object_unref (primitive); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (attributes[1]); cogl_object_unref (attributes[0]); cogl_object_unref (buffer); test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); } static void test_byte_verts (TestState *state, int offset_x, int offset_y) { CoglAttribute *attributes[2]; CoglAttributeBuffer *buffer, *unnorm_buffer; CoglPrimitive *primitive; static const ByteVert norm_verts[] = { { 0, 10, /**/ 255, 0, 0, 255 }, { 10, 10, /**/ 255, 0, 0, 255 }, { 5, 0, /**/ 255, 0, 0, 255 }, { 10, 10, /**/ 0, 255, 0, 255 }, { 20, 10, /**/ 0, 255, 0, 255 }, { 15, 0, /**/ 0, 255, 0, 255 } }; static const ByteVert unnorm_verts[] = { { 0, 0, /**/ 0, 0, 1, 1 }, { 0, 0, /**/ 0, 0, 1, 1 }, { 0, 0, /**/ 0, 0, 1, 1 }, }; buffer = cogl_attribute_buffer_new (test_ctx, sizeof (norm_verts), norm_verts); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (ByteVert), G_STRUCT_OFFSET (ByteVert, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_SHORT); attributes[1] = cogl_attribute_new (buffer, "color", sizeof (ByteVert), G_STRUCT_OFFSET (ByteVert, r), 4, /* n_components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_BYTE); cogl_attribute_set_normalized (attributes[1], TRUE); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, offset_x, offset_y, 0.0f); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 6, /* n_vertices */ attributes, 2); /* n_attributes */ cogl_primitive_draw (primitive, test_fb, state->pipeline); cogl_object_unref (primitive); cogl_object_unref (attributes[1]); /* Test again with unnormalized attributes */ unnorm_buffer = cogl_attribute_buffer_new (test_ctx, sizeof (unnorm_verts), unnorm_verts); attributes[1] = cogl_attribute_new (unnorm_buffer, "color", sizeof (ByteVert), G_STRUCT_OFFSET (ByteVert, r), 4, /* n_components */ COGL_ATTRIBUTE_TYPE_BYTE); cogl_framebuffer_translate (test_fb, 20, 0, 0); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ attributes, 2); /* n_attributes */ cogl_primitive_draw (primitive, test_fb, state->pipeline); cogl_object_unref (primitive); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (attributes[0]); cogl_object_unref (attributes[1]); cogl_object_unref (buffer); cogl_object_unref (unnorm_buffer); test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); test_utils_check_pixel (test_fb, offset_x + 25, offset_y + 5, 0x0000ffff); } static void test_short_verts (TestState *state, int offset_x, int offset_y) { CoglAttribute *attributes[2]; CoglAttributeBuffer *buffer; CoglPipeline *pipeline, *pipeline2; CoglSnippet *snippet; CoglPrimitive *primitive; static const ShortVert short_verts[] = { { -10, -10, /**/ 0xffff, 0, 0, 0xffff }, { -1, -10, /**/ 0xffff, 0, 0, 0xffff }, { -5, -1, /**/ 0xffff, 0, 0, 0xffff } }; pipeline = cogl_pipeline_copy (state->pipeline); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); buffer = cogl_attribute_buffer_new (test_ctx, sizeof (short_verts), short_verts); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (ShortVert), G_STRUCT_OFFSET (ShortVert, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_SHORT); attributes[1] = cogl_attribute_new (buffer, "color", sizeof (ShortVert), G_STRUCT_OFFSET (ShortVert, r), 4, /* n_components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT); cogl_attribute_set_normalized (attributes[1], TRUE); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, offset_x + 10.0f, offset_y + 10.0f, 0.0f); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ attributes, 2); /* n_attributes */ cogl_primitive_draw (primitive, test_fb, pipeline); cogl_object_unref (primitive); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (attributes[0]); /* Test again treating the attribute as unsigned */ attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (ShortVert), G_STRUCT_OFFSET (ShortVert, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_UNSIGNED_SHORT); /* XXX: this is a hack to force the pipeline to use the glsl backend * because we know it's not possible to test short vertex position * components with the legacy GL backend since which might otherwise * be used internally... */ pipeline2 = cogl_pipeline_new (test_ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, "attribute vec4 color;", "cogl_color_out = vec4 (0.0, 1.0, 0.0, 1.0);"); cogl_pipeline_add_snippet (pipeline2, snippet); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, offset_x + 10.0f - 65525.0f, offset_y - 65525, 0.0f); primitive = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, 3, /* n_vertices */ attributes, 1); /* n_attributes */ cogl_primitive_draw (primitive, test_fb, pipeline2); cogl_object_unref (primitive); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (attributes[0]); cogl_object_unref (pipeline2); cogl_object_unref (pipeline); cogl_object_unref (buffer); test_utils_check_pixel (test_fb, offset_x + 5, offset_y + 5, 0xff0000ff); test_utils_check_pixel (test_fb, offset_x + 15, offset_y + 5, 0x00ff00ff); } static void paint (TestState *state) { cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); test_float_verts (state, 0, 0); test_byte_verts (state, 0, 10); test_short_verts (state, 0, 20); } void test_custom_attributes (void) { CoglSnippet *snippet; TestState state; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); state.pipeline = cogl_pipeline_new (test_ctx); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, "attribute vec4 color;", "cogl_color_out = color;"); cogl_pipeline_add_snippet (state.pipeline, snippet); paint (&state); cogl_object_unref (state.pipeline); cogl_object_unref (snippet); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-point-sprite.c0000664000175000017500000001465514723361714021264 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" #define POINT_SIZE 8 static const CoglVertexP2T2 point = { POINT_SIZE, POINT_SIZE, 0.0f, 0.0f }; static const uint8_t tex_data[3 * 2 * 2] = { 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0x00 }; static void do_test (gboolean check_orientation, gboolean use_glsl) { int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); CoglPrimitive *prim; GError *error = NULL; CoglTexture2D *tex_2d; CoglPipeline *pipeline, *solid_pipeline; int tex_height; cogl_framebuffer_orthographic (test_fb, 0, 0, /* x_1, y_1 */ fb_width, /* x_2 */ fb_height /* y_2 */, -1, 100 /* near/far */); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); /* If we're not checking the orientation of the point sprite then * we'll set the height of the texture to 1 so that the vertical * orientation does not matter */ if (check_orientation) tex_height = 2; else tex_height = 1; tex_2d = cogl_texture_2d_new_from_data (test_ctx, 2, tex_height, /* width/height */ COGL_PIXEL_FORMAT_RGB_888, 6, /* row stride */ tex_data, &error); g_assert (tex_2d != NULL); g_assert (error == NULL); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, tex_2d); cogl_pipeline_set_layer_filters (pipeline, 0, /* layer_index */ COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_point_size (pipeline, POINT_SIZE); /* If we're using GLSL then we don't need to enable point sprite * coords and we can just directly reference cogl_point_coord in the * snippet */ if (use_glsl) { CoglSnippet *snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, NULL, /* declarations */ NULL /* post */); static const char source[] = " cogl_texel = texture2D (cogl_sampler, cogl_point_coord);\n"; cogl_snippet_set_replace (snippet, source); /* Keep a reference to the original pipeline because there is no * way to remove a snippet in order to recreate the solid * pipeline */ solid_pipeline = cogl_pipeline_copy (pipeline); cogl_pipeline_add_layer_snippet (pipeline, 0, snippet); cogl_object_unref (snippet); } else { gboolean res = cogl_pipeline_set_layer_point_sprite_coords_enabled (pipeline, /* layer_index */ 0, /* enable */ TRUE, &error); g_assert (res == TRUE); g_assert (error == NULL); solid_pipeline = cogl_pipeline_copy (pipeline); res = cogl_pipeline_set_layer_point_sprite_coords_enabled (solid_pipeline, /* layer_index */ 0, /* enable */ FALSE, &error); g_assert (res == TRUE); g_assert (error == NULL); } prim = cogl_primitive_new_p2t2 (test_ctx, COGL_VERTICES_MODE_POINTS, 1, /* n_vertices */ &point); cogl_primitive_draw (prim, test_fb, pipeline); /* Render the primitive again without point sprites to make sure disabling it works */ cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, POINT_SIZE * 2, /* x */ 0.0f, /* y */ 0.0f /* z */); cogl_primitive_draw (prim, test_fb, solid_pipeline); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (prim); cogl_object_unref (solid_pipeline); cogl_object_unref (pipeline); cogl_object_unref (tex_2d); test_utils_check_pixel (test_fb, POINT_SIZE - POINT_SIZE / 4, POINT_SIZE - POINT_SIZE / 4, 0x0000ffff); test_utils_check_pixel (test_fb, POINT_SIZE + POINT_SIZE / 4, POINT_SIZE - POINT_SIZE / 4, 0x00ff00ff); test_utils_check_pixel (test_fb, POINT_SIZE - POINT_SIZE / 4, POINT_SIZE + POINT_SIZE / 4, check_orientation ? 0x00ffffff : 0x0000ffff); test_utils_check_pixel (test_fb, POINT_SIZE + POINT_SIZE / 4, POINT_SIZE + POINT_SIZE / 4, check_orientation ? 0xff0000ff : 0x00ff00ff); /* When rendering without the point sprites all of the texture coordinates should be 0,0 so it should get the top-left texel which is blue */ test_utils_check_region (test_fb, POINT_SIZE * 3 - POINT_SIZE / 2 + 1, POINT_SIZE - POINT_SIZE / 2 + 1, POINT_SIZE - 2, POINT_SIZE - 2, 0x0000ffff); if (cogl_test_verbose ()) g_print ("OK\n"); } void test_point_sprite (void) { do_test (FALSE /* don't check orientation */, FALSE /* don't use GLSL */); } void test_point_sprite_orientation (void) { do_test (TRUE /* check orientation */, FALSE /* don't use GLSL */); } void test_point_sprite_glsl (void) { do_test (FALSE /* don't check orientation */, TRUE /* use GLSL */); } muffin-6.4.1/cogl/tests/conform/test-fixtures.c0000664000175000017500000000036614723361714020472 0ustar fabiofabio #include #include void test_simple_rig (void) { ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); } muffin-6.4.1/cogl/tests/conform/test-multitexture.c0000664000175000017500000001340514723361714021372 0ustar fabiofabio#include #include #include #include "test-conform-common.h" static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; #define QUAD_WIDTH 20 #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 typedef struct _TestState { unsigned int padding; } TestState; static void assert_region_color (int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { uint8_t *data = g_malloc0 (width * height * 4); cogl_read_pixels (x, y, width, height, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); for (y = 0; y < height; y++) for (x = 0; x < width; x++) { uint8_t *pixel = &data[y * width * 4 + x * 4]; #if 1 g_assert (pixel[RED] == red && pixel[GREEN] == green && pixel[BLUE] == blue); #endif } g_free (data); } /* Creates a texture divided into 4 quads with colors arranged as follows: * (The same value are used in all channels for each texel) * * |-----------| * |0x11 |0x00 | * |+ref | | * |-----------| * |0x00 |0x33 | * | |+ref | * |-----------| * * */ static CoglHandle make_texture (guchar ref) { int x; int y; guchar *tex_data, *p; CoglHandle tex; guchar val; tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 16); for (y = 0; y < QUAD_WIDTH * 2; y++) for (x = 0; x < QUAD_WIDTH * 2; x++) { p = tex_data + (QUAD_WIDTH * 8 * y) + x * 4; if (x < QUAD_WIDTH && y < QUAD_WIDTH) val = 0x11 + ref; else if (x >= QUAD_WIDTH && y >= QUAD_WIDTH) val = 0x33 + ref; else val = 0x00; p[0] = p[1] = p[2] = p[3] = val; } /* Note: we don't use COGL_PIXEL_FORMAT_ANY for the internal format here * since we don't want to allow Cogl to premultiply our data. */ tex = test_utils_texture_new_from_data (QUAD_WIDTH * 2, QUAD_WIDTH * 2, TEST_UTILS_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGBA_8888, COGL_PIXEL_FORMAT_RGBA_8888, QUAD_WIDTH * 8, tex_data); g_free (tex_data); return tex; } static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, TestState *state) { CoglHandle tex0, tex1; CoglHandle material; gboolean status; GError *error = NULL; float tex_coords[] = { 0, 0, 0.5, 0.5, /* tex0 */ 0.5, 0.5, 1, 1 /* tex1 */ }; tex0 = make_texture (0x00); tex1 = make_texture (0x11); material = cogl_material_new (); /* An arbitrary color which should be replaced by the first texture layer */ cogl_material_set_color4ub (material, 0x80, 0x80, 0x80, 0x80); cogl_material_set_blend (material, "RGBA = ADD (SRC_COLOR, 0)", NULL); cogl_material_set_layer (material, 0, tex0); cogl_material_set_layer_combine (material, 0, "RGBA = REPLACE (TEXTURE)", NULL); /* We'll use nearest filtering mode on the textures, otherwise the edge of the quad can pull in texels from the neighbouring quarters of the texture due to imprecision */ cogl_material_set_layer_filters (material, 0, COGL_MATERIAL_FILTER_NEAREST, COGL_MATERIAL_FILTER_NEAREST); cogl_material_set_layer (material, 1, tex1); cogl_material_set_layer_filters (material, 1, COGL_MATERIAL_FILTER_NEAREST, COGL_MATERIAL_FILTER_NEAREST); status = cogl_material_set_layer_combine (material, 1, "RGBA = ADD (PREVIOUS, TEXTURE)", &error); if (!status) { /* It's not strictly a test failure; you need a more capable GPU or * driver to test this texture combine string. */ g_debug ("Failed to setup texture combine string " "RGBA = ADD (PREVIOUS, TEXTURE): %s", error->message); } cogl_set_source (material); cogl_rectangle_with_multitexture_coords (0, 0, QUAD_WIDTH, QUAD_WIDTH, tex_coords, 8); cogl_object_unref (material); cogl_object_unref (tex0); cogl_object_unref (tex1); /* See what we got... */ assert_region_color (0, 0, QUAD_WIDTH, QUAD_WIDTH, 0x55, 0x55, 0x55, 0x55); /* Comment this out if you want visual feedback for what this test paints */ #if 1 clutter_main_quit (); #endif } static gboolean queue_redraw (void *stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } void test_multitexture (TestUtilsGTestFixture *fixture, void *data) { TestState state; ClutterActor *stage; ClutterActor *group; unsigned int idle_source; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); group = clutter_group_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); /* We force continuous redrawing incase someone comments out the * clutter_main_quit and wants visual feedback for the test since we * wont be doing anything else that will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); clutter_actor_show_all (stage); clutter_main (); g_clear_handle_id (&idle_source, g_source_remove); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/mutter-cogl.test.in0000664000175000017500000000030314723361714021237 0ustar fabiofabio[Test] Type=session TestEnvironment=COGL_TEST_VERBOSE=1 Exec=sh -c "cd @libexecdir@/installed-tests/mutter-@apiversion@/cogl/conform; ./run-tests.sh ./config.env ./test-conformance ./unit-tests" muffin-6.4.1/cogl/tests/conform/test-layer-remove.c0000664000175000017500000001166314723361714021232 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" #define TEST_SQUARE_SIZE 10 static CoglPipeline * create_two_layer_pipeline (void) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglColor color; /* The pipeline is initially black */ cogl_pipeline_set_color4ub (pipeline, 0, 0, 0, 255); /* The first layer adds a full red component */ cogl_color_init_from_4ub (&color, 255, 0, 0, 255); cogl_pipeline_set_layer_combine_constant (pipeline, 0, &color); cogl_pipeline_set_layer_combine (pipeline, 0, /* layer_num */ "RGBA=ADD(PREVIOUS,CONSTANT)", NULL); /* The second layer adds a full green component */ cogl_color_init_from_4ub (&color, 0, 255, 0, 255); cogl_pipeline_set_layer_combine_constant (pipeline, 1, &color); cogl_pipeline_set_layer_combine (pipeline, 1, /* layer_num */ "RGBA=ADD(PREVIOUS,CONSTANT)", NULL); return pipeline; } static void test_color (CoglPipeline *pipeline, uint32_t color, int pos) { cogl_framebuffer_draw_rectangle (test_fb, pipeline, pos * TEST_SQUARE_SIZE, 0, pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE, TEST_SQUARE_SIZE); test_utils_check_pixel (test_fb, pos * TEST_SQUARE_SIZE + TEST_SQUARE_SIZE / 2, TEST_SQUARE_SIZE / 2, color); } void test_layer_remove (void) { CoglPipeline *pipeline0, *pipeline1; CoglColor color; int pos = 0; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); /** TEST 1 **/ /* Basic sanity check that the pipeline combines the two colors * together properly */ pipeline0 = create_two_layer_pipeline (); test_color (pipeline0, 0xffff00ff, pos++); cogl_object_unref (pipeline0); /** TEST 2 **/ /* Check that we can remove the second layer */ pipeline0 = create_two_layer_pipeline (); cogl_pipeline_remove_layer (pipeline0, 1); test_color (pipeline0, 0xff0000ff, pos++); cogl_object_unref (pipeline0); /** TEST 3 **/ /* Check that we can remove the first layer */ pipeline0 = create_two_layer_pipeline (); cogl_pipeline_remove_layer (pipeline0, 0); test_color (pipeline0, 0x00ff00ff, pos++); cogl_object_unref (pipeline0); /** TEST 4 **/ /* Check that we can make a copy and remove a layer from the * original pipeline */ pipeline0 = create_two_layer_pipeline (); pipeline1 = cogl_pipeline_copy (pipeline0); cogl_pipeline_remove_layer (pipeline0, 1); test_color (pipeline0, 0xff0000ff, pos++); test_color (pipeline1, 0xffff00ff, pos++); cogl_object_unref (pipeline0); cogl_object_unref (pipeline1); /** TEST 5 **/ /* Check that we can make a copy and remove the second layer from the * new pipeline */ pipeline0 = create_two_layer_pipeline (); pipeline1 = cogl_pipeline_copy (pipeline0); cogl_pipeline_remove_layer (pipeline1, 1); test_color (pipeline0, 0xffff00ff, pos++); test_color (pipeline1, 0xff0000ff, pos++); cogl_object_unref (pipeline0); cogl_object_unref (pipeline1); /** TEST 6 **/ /* Check that we can make a copy and remove the first layer from the * new pipeline */ pipeline0 = create_two_layer_pipeline (); pipeline1 = cogl_pipeline_copy (pipeline0); cogl_pipeline_remove_layer (pipeline1, 0); test_color (pipeline0, 0xffff00ff, pos++); test_color (pipeline1, 0x00ff00ff, pos++); cogl_object_unref (pipeline0); cogl_object_unref (pipeline1); /** TEST 7 **/ /* Check that we can modify a layer in a child pipeline */ pipeline0 = create_two_layer_pipeline (); pipeline1 = cogl_pipeline_copy (pipeline0); cogl_color_init_from_4ub (&color, 0, 0, 255, 255); cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color); test_color (pipeline0, 0xffff00ff, pos++); test_color (pipeline1, 0x00ffffff, pos++); cogl_object_unref (pipeline0); cogl_object_unref (pipeline1); /** TEST 8 **/ /* Check that we can modify a layer in a child pipeline but then remove it */ pipeline0 = create_two_layer_pipeline (); pipeline1 = cogl_pipeline_copy (pipeline0); cogl_color_init_from_4ub (&color, 0, 0, 255, 255); cogl_pipeline_set_layer_combine_constant (pipeline1, 0, &color); cogl_pipeline_remove_layer (pipeline1, 0); test_color (pipeline0, 0xffff00ff, pos++); test_color (pipeline1, 0x00ff00ff, pos++); cogl_object_unref (pipeline0); cogl_object_unref (pipeline1); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-color-hsl.c0000664000175000017500000000257714723361714020531 0ustar fabiofabio#include #include #include #include "test-declarations.h" #include "test-utils.h" #define cogl_assert_float(a, b) \ do { \ if (fabsf ((a) - (b)) >= 0.0001f) \ g_assert_cmpfloat ((a), ==, (b)); \ } while (0) void test_color_hsl (void) { CoglColor color; float hue, saturation, luminance; cogl_color_init_from_4ub(&color, 108, 198, 78, 255); cogl_color_to_hsl(&color, &hue, &saturation, &luminance); cogl_assert_float(hue, 105.f); cogl_assert_float(saturation, 0.512821); cogl_assert_float(luminance, 0.541176); memset(&color, 0, sizeof (CoglColor)); cogl_color_init_from_hsl(&color, hue, saturation, luminance); g_assert_cmpint (cogl_color_get_red_byte (&color), ==, 108); g_assert_cmpint (cogl_color_get_green_byte (&color), ==, 198); g_assert_cmpint (cogl_color_get_blue_byte (&color), ==, 78); g_assert_cmpint (cogl_color_get_alpha_byte (&color), ==, 255); memset(&color, 0, sizeof (CoglColor)); cogl_color_init_from_hsl(&color, hue, 0, luminance); cogl_assert_float (cogl_color_get_red_float (&color), luminance); cogl_assert_float (cogl_color_get_green_float (&color), luminance); cogl_assert_float (cogl_color_get_blue_float (&color), luminance); cogl_assert_float (cogl_color_get_alpha_float (&color), 1.0f); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-texture-no-allocate.c0000664000175000017500000000364214723361714022515 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" /* Tests that the various texture types can be freed without being * allocated */ /* Texture size that is probably to big to fit within the texture * limits */ #define BIG_TEX_WIDTH 16384 #define BIG_TEX_HEIGHT 128 void test_texture_no_allocate (void) { uint8_t *tex_data; CoglTexture *texture; CoglTexture2D *texture_2d; GError *error = NULL; tex_data = g_malloc (BIG_TEX_WIDTH * BIG_TEX_HEIGHT * 4); /* NB: if we make the atlas and sliced texture APIs public then this * could changed to explicitly use that instead of the magic texture * API */ /* Try to create an atlas texture that is too big so it will * internally be freed without allocating */ texture = cogl_atlas_texture_new_from_data (test_ctx, BIG_TEX_WIDTH, BIG_TEX_HEIGHT, /* format */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* rowstride */ BIG_TEX_WIDTH * 4, tex_data, &error); g_free (tex_data); /* It's ok if this causes an error, we just don't want it to * crash */ if (texture == NULL) g_error_free (error); else cogl_object_unref (texture); /* Try to create a sliced texture without allocating it */ texture = cogl_texture_2d_sliced_new_with_size (test_ctx, BIG_TEX_WIDTH, BIG_TEX_HEIGHT, COGL_TEXTURE_MAX_WASTE); cogl_object_unref (texture); /* 2D texture */ texture_2d = cogl_texture_2d_new_with_size (test_ctx, 64, 64); cogl_object_unref (texture_2d); } muffin-6.4.1/cogl/tests/conform/test-just-vertex-shader.c0000664000175000017500000000714314723361714022365 0ustar fabiofabio#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 #include #include #include "test-declarations.h" #include "test-utils.h" typedef struct _TestState { int paddiing; } TestState; static CoglTexture * create_dummy_texture (void) { /* Create a dummy 1x1 green texture to replace the color from the vertex shader */ static const uint8_t data[4] = { 0x00, 0xff, 0x00, 0xff }; return test_utils_texture_new_from_data (test_ctx, 1, 1, /* size */ TEST_UTILS_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGB_888, 4, /* rowstride */ data); } static void paint (TestState *state) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglTexture *tex; CoglColor color; GError *error = NULL; CoglHandle shader, program; cogl_color_init_from_4ub (&color, 0, 0, 0, 255); cogl_framebuffer_clear (test_fb, COGL_BUFFER_BIT_COLOR, &color); /* Set the primary vertex color as red */ cogl_color_init_from_4ub (&color, 0xff, 0x00, 0x00, 0xff); cogl_pipeline_set_color (pipeline, &color); /* Override the vertex color in the texture environment with a constant green color provided by a texture */ tex = create_dummy_texture (); cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_object_unref (tex); if (!cogl_pipeline_set_layer_combine (pipeline, 0, "RGBA=REPLACE(TEXTURE)", &error)) { g_warning ("Error setting layer combine: %s", error->message); g_assert_not_reached (); } /* Set up a dummy vertex shader that does nothing but the usual fixed function transform */ shader = cogl_create_shader (COGL_SHADER_TYPE_VERTEX); cogl_shader_source (shader, "void\n" "main ()\n" "{\n" " cogl_position_out = " "cogl_modelview_projection_matrix * " "cogl_position_in;\n" " cogl_color_out = cogl_color_in;\n" " cogl_tex_coord_out[0] = cogl_tex_coord_in;\n" "}\n"); program = cogl_create_program (); cogl_program_attach_shader (program, shader); cogl_program_link (program); cogl_object_unref (shader); /* Draw something without the program */ cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, 0, 50, 50); /* Draw it again using the program. It should look exactly the same */ cogl_pipeline_set_user_program (pipeline, program); cogl_object_unref (program); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 50, 0, 100, 50); cogl_pipeline_set_user_program (pipeline, NULL); cogl_object_unref (pipeline); } static void validate_result (CoglFramebuffer *framebuffer) { /* Non-shader version */ test_utils_check_pixel (framebuffer, 25, 25, 0x00ff0000); /* Shader version */ test_utils_check_pixel (framebuffer, 75, 25, 0x00ff0000); } void test_just_vertex_shader (void) { TestState state; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); validate_result (test_fb); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-read-texture-formats.c0000664000175000017500000001447014723361714022704 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" /* * This tests reading back an RGBA texture in all of the available * pixel formats */ static const uint8_t tex_data[4] = { 0x12, 0x34, 0x56, 0x78 }; static void test_read_byte (CoglTexture2D *tex_2d, CoglPixelFormat format, uint8_t expected_byte) { uint8_t received_byte; cogl_texture_get_data (tex_2d, format, 1, /* rowstride */ &received_byte); g_assert_cmpint (expected_byte, ==, received_byte); } static void test_read_short (CoglTexture2D *tex_2d, CoglPixelFormat format, ...) { va_list ap; int bits; uint16_t received_value; uint16_t expected_value = 0; char *received_value_str; char *expected_value_str; int bits_sum = 0; cogl_texture_get_data (tex_2d, format, 2, /* rowstride */ (uint8_t *) &received_value); va_start (ap, format); /* Convert the va args into a single 16-bit expected value */ while ((bits = va_arg (ap, int)) != -1) { int value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255; bits_sum += bits; expected_value |= value << (16 - bits_sum); } va_end (ap); received_value_str = g_strdup_printf ("0x%04x", received_value); expected_value_str = g_strdup_printf ("0x%04x", expected_value); g_assert_cmpstr (received_value_str, ==, expected_value_str); g_free (received_value_str); g_free (expected_value_str); } static void test_read_888 (CoglTexture2D *tex_2d, CoglPixelFormat format, uint32_t expected_pixel) { uint8_t pixel[4]; cogl_texture_get_data (tex_2d, format, 4, /* rowstride */ pixel); test_utils_compare_pixel (pixel, expected_pixel); } static void test_read_88 (CoglTexture2D *tex_2d, CoglPixelFormat format, uint32_t expected_pixel) { uint8_t pixel[4]; pixel[2] = 0x00; cogl_texture_get_data (tex_2d, format, 2, /* rowstride */ pixel); test_utils_compare_pixel (pixel, expected_pixel); } static void test_read_8888 (CoglTexture2D *tex_2d, CoglPixelFormat format, uint32_t expected_pixel) { uint32_t received_pixel; char *received_value_str; char *expected_value_str; cogl_texture_get_data (tex_2d, format, 4, /* rowstride */ (uint8_t *) &received_pixel); received_pixel = GUINT32_FROM_BE (received_pixel); received_value_str = g_strdup_printf ("0x%08x", received_pixel); expected_value_str = g_strdup_printf ("0x%08x", expected_pixel); g_assert_cmpstr (received_value_str, ==, expected_value_str); g_free (received_value_str); g_free (expected_value_str); } static void test_read_int (CoglTexture2D *tex_2d, CoglPixelFormat format, ...) { va_list ap; int bits; uint32_t received_value; uint32_t expected_value = 0; char *received_value_str; char *expected_value_str; int bits_sum = 0; cogl_texture_get_data (tex_2d, format, 4, /* rowstride */ (uint8_t *) &received_value); va_start (ap, format); /* Convert the va args into a single 32-bit expected value */ while ((bits = va_arg (ap, int)) != -1) { uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 128) / 255; bits_sum += bits; expected_value |= value << (32 - bits_sum); } va_end (ap); received_value_str = g_strdup_printf ("0x%08x", received_value); expected_value_str = g_strdup_printf ("0x%08x", expected_value); g_assert_cmpstr (received_value_str, ==, expected_value_str); g_free (received_value_str); g_free (expected_value_str); } void test_read_texture_formats (void) { CoglTexture2D *tex_2d; tex_2d = cogl_texture_2d_new_from_data (test_ctx, 1, 1, /* width / height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ tex_data, NULL); test_read_byte (tex_2d, COGL_PIXEL_FORMAT_A_8, 0x78); #if 0 /* I'm not sure what's the right value to put here because Nvidia and Mesa seem to behave differently so one of them must be wrong. */ test_read_byte (tex_2d, COGL_PIXEL_FORMAT_G_8, 0x9c); #endif /* We should always be able to read into an RG buffer regardless of * whether RG textures are supported because Cogl will do the * conversion for us */ test_read_88 (tex_2d, COGL_PIXEL_FORMAT_RG_88, 0x123400ff); test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGB_565, 5, 0x12, 6, 0x34, 5, 0x56, -1); test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_4444_PRE, 4, 0x12, 4, 0x34, 4, 0x56, 4, 0x78, -1); test_read_short (tex_2d, COGL_PIXEL_FORMAT_RGBA_5551_PRE, 5, 0x12, 5, 0x34, 5, 0x56, 1, 0x78, -1); test_read_888 (tex_2d, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff); test_read_888 (tex_2d, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff); test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 0x12345678); test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_BGRA_8888_PRE, 0x56341278); test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ARGB_8888_PRE, 0x78123456); test_read_8888 (tex_2d, COGL_PIXEL_FORMAT_ABGR_8888_PRE, 0x78563412); test_read_int (tex_2d, COGL_PIXEL_FORMAT_RGBA_1010102_PRE, 10, 0x12, 10, 0x34, 10, 0x56, 2, 0x78, -1); test_read_int (tex_2d, COGL_PIXEL_FORMAT_BGRA_1010102_PRE, 10, 0x56, 10, 0x34, 10, 0x12, 2, 0x78, -1); test_read_int (tex_2d, COGL_PIXEL_FORMAT_ARGB_2101010_PRE, 2, 0x78, 10, 0x12, 10, 0x34, 10, 0x56, -1); test_read_int (tex_2d, COGL_PIXEL_FORMAT_ABGR_2101010_PRE, 2, 0x78, 10, 0x56, 10, 0x34, 10, 0x12, -1); cogl_object_unref (tex_2d); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-write-texture-formats.c0000664000175000017500000001343114723361714023117 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" /* * This tests writing data to an RGBA texture in all of the available * pixel formats */ static void test_color (CoglTexture *texture, uint32_t expected_pixel) { uint8_t received_pixel[4]; cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ received_pixel); test_utils_compare_pixel_and_alpha (received_pixel, expected_pixel); } static void test_write_byte (CoglContext *context, CoglPixelFormat format, uint8_t byte, uint32_t expected_pixel) { CoglTexture *texture = test_utils_create_color_texture (context, 0); cogl_texture_set_region (texture, 0, 0, /* src_x / src_y */ 0, 0, /* dst_x / dst_y */ 1, 1, /* dst_w / dst_h */ 1, 1, /* width / height */ format, 1, /* rowstride */ &byte); test_color (texture, expected_pixel); cogl_object_unref (texture); } static void test_write_short (CoglContext *context, CoglPixelFormat format, uint16_t value, uint32_t expected_pixel) { CoglTexture *texture = test_utils_create_color_texture (context, 0); cogl_texture_set_region (texture, 0, 0, /* src_x / src_y */ 0, 0, /* dst_x / dst_y */ 1, 1, /* dst_w / dst_h */ 1, 1, /* width / height */ format, 2, /* rowstride */ (uint8_t *) &value); test_color (texture, expected_pixel); cogl_object_unref (texture); } static void test_write_bytes (CoglContext *context, CoglPixelFormat format, uint32_t value, uint32_t expected_pixel) { CoglTexture *texture = test_utils_create_color_texture (context, 0); value = GUINT32_TO_BE (value); cogl_texture_set_region (texture, 0, 0, /* src_x / src_y */ 0, 0, /* dst_x / dst_y */ 1, 1, /* dst_w / dst_h */ 1, 1, /* width / height */ format, 4, /* rowstride */ (uint8_t *) &value); test_color (texture, expected_pixel); cogl_object_unref (texture); } static void test_write_int (CoglContext *context, CoglPixelFormat format, uint32_t expected_pixel, ...) { va_list ap; int bits; uint32_t tex_data = 0; int bits_sum = 0; CoglTexture *texture = test_utils_create_color_texture (context, 0); va_start (ap, expected_pixel); /* Convert the va args into a single 32-bit value */ while ((bits = va_arg (ap, int)) != -1) { uint32_t value = (va_arg (ap, int) * ((1 << bits) - 1) + 127) / 255; bits_sum += bits; tex_data |= value << (32 - bits_sum); } va_end (ap); cogl_texture_set_region (texture, 0, 0, /* src_x / src_y */ 0, 0, /* dst_x / dst_y */ 1, 1, /* dst_w / dst_h */ 1, 1, /* width / height */ format, 4, /* rowstride */ (uint8_t *) &tex_data); test_color (texture, expected_pixel); cogl_object_unref (texture); } void test_write_texture_formats (void) { test_write_byte (test_ctx, COGL_PIXEL_FORMAT_A_8, 0x34, 0x00000034); #if 0 /* I'm not sure what's the right value to put here because Nvidia and Mesa seem to behave differently so one of them must be wrong. */ test_write_byte (test_ctx, COGL_PIXEL_FORMAT_G_8, 0x34, 0x340000ff); #endif /* We should always be able to read from an RG buffer regardless of * whether RG textures are supported because Cogl will do the * conversion for us */ test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RG_88, 0x123456ff, 0x123400ff); test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGB_565, 0x0843, 0x080819ff); test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_4444_PRE, 0x1234, 0x11223344); test_write_short (test_ctx, COGL_PIXEL_FORMAT_RGBA_5551_PRE, 0x0887, 0x081019ff); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGB_888, 0x123456ff, 0x123456ff); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGR_888, 0x563412ff, 0x123456ff); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 0x12345678, 0x12345678); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_BGRA_8888_PRE, 0x56341278, 0x12345678); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ARGB_8888_PRE, 0x78123456, 0x12345678); test_write_bytes (test_ctx, COGL_PIXEL_FORMAT_ABGR_8888_PRE, 0x78563412, 0x12345678); test_write_int (test_ctx, COGL_PIXEL_FORMAT_RGBA_1010102_PRE, 0x123456ff, 10, 0x12, 10, 0x34, 10, 0x56, 2, 0xff, -1); test_write_int (test_ctx, COGL_PIXEL_FORMAT_BGRA_1010102_PRE, 0x123456ff, 10, 0x56, 10, 0x34, 10, 0x12, 2, 0xff, -1); test_write_int (test_ctx, COGL_PIXEL_FORMAT_ARGB_2101010_PRE, 0x123456ff, 2, 0xff, 10, 0x12, 10, 0x34, 10, 0x56, -1); test_write_int (test_ctx, COGL_PIXEL_FORMAT_ABGR_2101010_PRE, 0x123456ff, 2, 0xff, 10, 0x56, 10, 0x34, 10, 0x12, -1); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-texture-mipmaps.c0000664000175000017500000000775214723361714021773 0ustar fabiofabio#include #include #include #include "test-conform-common.h" static const ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; #define TEX_SIZE 64 typedef struct _TestState { unsigned int padding; } TestState; /* Creates a texture where the pixels are evenly divided between selecting just one of the R,G and B components */ static CoglHandle make_texture (void) { guchar *tex_data = g_malloc (TEX_SIZE * TEX_SIZE * 3), *p = tex_data; CoglHandle tex; int x, y; for (y = 0; y < TEX_SIZE; y++) for (x = 0; x < TEX_SIZE; x++) { memset (p, 0, 3); /* Set one of the components to full. The components should be evenly represented so that each gets a third of the texture */ p[(p - tex_data) / (TEX_SIZE * TEX_SIZE * 3 / 3)] = 255; p += 3; } tex = test_utils_texture_new_from_data (TEX_SIZE, TEX_SIZE, TEST_UTILS_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGB_888, COGL_PIXEL_FORMAT_ANY, TEX_SIZE * 3, tex_data); g_free (tex_data); return tex; } static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, TestState *state) { CoglHandle tex; CoglHandle material; uint8_t pixels[8]; tex = make_texture (); material = cogl_material_new (); cogl_material_set_layer (material, 0, tex); cogl_object_unref (tex); /* Render a 1x1 pixel quad without mipmaps */ cogl_set_source (material); cogl_material_set_layer_filters (material, 0, COGL_MATERIAL_FILTER_NEAREST, COGL_MATERIAL_FILTER_NEAREST); cogl_rectangle (0, 0, 1, 1); /* Then with mipmaps */ cogl_material_set_layer_filters (material, 0, COGL_MATERIAL_FILTER_NEAREST_MIPMAP_NEAREST, COGL_MATERIAL_FILTER_NEAREST); cogl_rectangle (1, 0, 2, 1); cogl_object_unref (material); /* Read back the two pixels we rendered */ cogl_read_pixels (0, 0, 2, 1, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, pixels); /* The first pixel should be just one of the colors from the texture. It doesn't matter which one */ g_assert ((pixels[0] == 255 && pixels[1] == 0 && pixels[2] == 0) || (pixels[0] == 0 && pixels[1] == 255 && pixels[2] == 0) || (pixels[0] == 0 && pixels[1] == 0 && pixels[2] == 255)); /* The second pixel should be more or less the average of all of the pixels in the texture. Each component gets a third of the image so each component should be approximately 255/3 */ g_assert (ABS (pixels[4] - 255 / 3) <= 3 && ABS (pixels[5] - 255 / 3) <= 3 && ABS (pixels[6] - 255 / 3) <= 3); /* Comment this out if you want visual feedback for what this test paints */ #if 1 clutter_main_quit (); #endif } static gboolean queue_redraw (void *stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } void test_texture_mipmaps (TestUtilsGTestFixture *fixture, void *data) { TestState state; ClutterActor *stage; ClutterActor *group; unsigned int idle_source; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); group = clutter_group_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), &state); clutter_actor_show_all (stage); clutter_main (); g_clear_handle_id (&idle_source, g_source_remove); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-conform-main.c0000664000175000017500000001071014723361714021200 0ustar fabiofabio#include "cogl-config.h" #include #include #include #include #include #include "test-declarations.h" #include "test-utils.h" /* A bit of sugar for adding new conformance tests */ #define ADD_TEST(FUNC, REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS) \ G_STMT_START { \ if (strcmp (#FUNC, argv[1]) == 0) \ { \ test_utils_init (REQUIREMENTS, KNOWN_FAIL_REQUIREMENTS); \ FUNC (); \ test_utils_fini (); \ exit (0); \ } \ } G_STMT_END #define UNPORTED_TEST(FUNC) int main (int argc, char **argv) { int i; if (argc != 2) { g_printerr ("usage %s UNIT_TEST\n", argv[0]); exit (1); } /* Just for convenience in case people try passing the wrapper * filenames for the UNIT_TEST argument we normalize '-' characters * to '_' characters... */ for (i = 0; argv[1][i]; i++) { if (argv[1][i] == '-') argv[1][i] = '_'; } /* This file is run through a sed script during the make step so the * lines containing the tests need to be formatted on a single line * each. */ UNPORTED_TEST (test_object); UNPORTED_TEST (test_fixed); UNPORTED_TEST (test_materials); ADD_TEST (test_pipeline_user_matrix, 0, 0); ADD_TEST (test_blend_strings, 0, 0); ADD_TEST (test_blend, 0, 0); ADD_TEST (test_premult, 0, TEST_KNOWN_FAILURE); UNPORTED_TEST (test_readpixels); ADD_TEST (test_path, 0, 0); ADD_TEST (test_path_clip, 0, 0); ADD_TEST (test_depth_test, 0, 0); ADD_TEST (test_backface_culling, 0, TEST_REQUIREMENT_NPOT); ADD_TEST (test_layer_remove, 0, 0); ADD_TEST (test_sparse_pipeline, 0, 0); ADD_TEST (test_npot_texture, 0, 0); UNPORTED_TEST (test_multitexture); UNPORTED_TEST (test_texture_mipmaps); ADD_TEST (test_sub_texture, 0, 0); ADD_TEST (test_pixel_buffer_map, 0, 0); ADD_TEST (test_pixel_buffer_set_data, 0, 0); ADD_TEST (test_pixel_buffer_sub_region, 0, 0); UNPORTED_TEST (test_texture_rectangle); ADD_TEST (test_wrap_modes, 0, 0); UNPORTED_TEST (test_texture_pixmap_x11); ADD_TEST (test_texture_get_set_data, 0, 0); ADD_TEST (test_atlas_migration, 0, 0); ADD_TEST (test_read_texture_formats, 0, TEST_KNOWN_FAILURE); ADD_TEST (test_write_texture_formats, 0, 0); ADD_TEST (test_alpha_textures, 0, 0); UNPORTED_TEST (test_vertex_buffer_contiguous); UNPORTED_TEST (test_vertex_buffer_interleved); UNPORTED_TEST (test_vertex_buffer_mutability); ADD_TEST (test_primitive, 0, 0); ADD_TEST (test_just_vertex_shader, TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_pipeline_uniforms, TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_snippets, TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_custom_attributes, TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_offscreen, 0, 0); ADD_TEST (test_framebuffer_get_bits, TEST_REQUIREMENT_OFFSCREEN | TEST_REQUIREMENT_GL, 0); ADD_TEST (test_point_size, 0, 0); ADD_TEST (test_point_size_attribute, TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE, 0); ADD_TEST (test_point_size_attribute_snippet, TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE | TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_point_sprite, TEST_REQUIREMENT_POINT_SPRITE, 0); ADD_TEST (test_point_sprite_orientation, TEST_REQUIREMENT_POINT_SPRITE, TEST_KNOWN_FAILURE); ADD_TEST (test_point_sprite_glsl, TEST_REQUIREMENT_POINT_SPRITE | TEST_REQUIREMENT_GLSL, 0); ADD_TEST (test_alpha_test, 0, 0); ADD_TEST (test_map_buffer_range, TEST_REQUIREMENT_MAP_WRITE, 0); ADD_TEST (test_primitive_and_journal, 0, 0); ADD_TEST (test_copy_replace_texture, 0, 0); ADD_TEST (test_pipeline_cache_unrefs_texture, 0, 0); ADD_TEST (test_pipeline_shader_state, TEST_REQUIREMENT_GLSL, 0); UNPORTED_TEST (test_viewport); ADD_TEST (test_euler, 0, 0); ADD_TEST (test_color_hsl, 0, 0); ADD_TEST (test_fence, TEST_REQUIREMENT_FENCE, 0); ADD_TEST (test_texture_no_allocate, 0, 0); ADD_TEST (test_texture_rg, TEST_REQUIREMENT_TEXTURE_RG, 0); g_printerr ("Unknown test name \"%s\"\n", argv[1]); return 1; } muffin-6.4.1/cogl/tests/conform/test-path.c0000664000175000017500000001625214723361714017556 0ustar fabiofabio#include #include #include #include "test-declarations.h" #include "test-utils.h" #define BLOCK_SIZE 16 /* Number of pixels at the border of a block quadrant to skip when verifying */ #define TEST_INSET 1 typedef struct _TestState { int dummy; } TestState; static void draw_path_at (CoglPath *path, CoglPipeline *pipeline, int x, int y) { cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, x * BLOCK_SIZE, y * BLOCK_SIZE, 0.0f); cogl_framebuffer_fill_path (test_fb, pipeline, path); cogl_framebuffer_pop_matrix (test_fb); } static void check_block (int block_x, int block_y, int block_mask) { uint32_t data[BLOCK_SIZE * BLOCK_SIZE]; int qx, qy; /* Block mask represents which quarters of the block should be filled. The bits from 0->3 represent the top left, top right, bottom left and bottom right respectively */ cogl_framebuffer_read_pixels (test_fb, block_x * BLOCK_SIZE, block_y * BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE, COGL_PIXEL_FORMAT_RGBA_8888_PRE, (uint8_t *)data); for (qy = 0; qy < 2; qy++) for (qx = 0; qx < 2; qx++) { int bit = qx | (qy << 1); const char *intended_pixel = ((block_mask & (1 << bit)) ? "#ffffff" : "#000000"); int x, y; for (x = 0; x < BLOCK_SIZE / 2 - TEST_INSET * 2; x++) for (y = 0; y < BLOCK_SIZE / 2 - TEST_INSET * 2; y++) { const uint32_t *p = data + (qx * BLOCK_SIZE / 2 + qy * BLOCK_SIZE * BLOCK_SIZE / 2 + (x + TEST_INSET) + (y + TEST_INSET) * BLOCK_SIZE); char *screen_pixel = g_strdup_printf ("#%06x", GUINT32_FROM_BE (*p) >> 8); g_assert_cmpstr (screen_pixel, ==, intended_pixel); g_free (screen_pixel); } } } static void paint (TestState *state) { CoglPath *path_a, *path_b, *path_c; CoglPipeline *white = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4f (white, 1, 1, 1, 1); /* Create a path filling just a quarter of a block. It will use two rectangles so that we have a sub path in the path */ path_a = cogl_path_new (); cogl_path_rectangle (path_a, BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE * 3 / 4, BLOCK_SIZE); draw_path_at (path_a, white, 0, 0); /* Create another path filling the whole block */ path_b = cogl_path_new (); cogl_path_rectangle (path_b, 0, 0, BLOCK_SIZE, BLOCK_SIZE); draw_path_at (path_b, white, 1, 0); /* Draw the first path again */ draw_path_at (path_a, white, 2, 0); /* Draw a copy of path a */ path_c = cogl_path_copy (path_a); draw_path_at (path_c, white, 3, 0); /* Add another rectangle to path a. We'll use line_to's instead of cogl_rectangle so that we don't create another sub-path because that is more likely to break the copy */ cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); cogl_path_line_to (path_a, 0, 0); cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); draw_path_at (path_a, white, 4, 0); /* Draw the copy again. It should not have changed */ draw_path_at (path_c, white, 5, 0); /* Add another rectangle to path c. It will be added in two halves, one as an extension of the previous path and the other as a new sub path */ cogl_path_line_to (path_c, BLOCK_SIZE / 2, 0); cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, 0); cogl_path_line_to (path_c, BLOCK_SIZE * 3 / 4, BLOCK_SIZE / 2); cogl_path_line_to (path_c, BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_rectangle (path_c, BLOCK_SIZE * 3 / 4, 0, BLOCK_SIZE, BLOCK_SIZE / 2); draw_path_at (path_c, white, 6, 0); /* Draw the original path again. It should not have changed */ draw_path_at (path_a, white, 7, 0); cogl_object_unref (path_a); cogl_object_unref (path_b); cogl_object_unref (path_c); /* Draw a self-intersecting path. The part that intersects should be inverted */ path_a = cogl_path_new (); cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); cogl_path_close (path_a); draw_path_at (path_a, white, 8, 0); cogl_object_unref (path_a); /* Draw two sub paths. Where the paths intersect it should be inverted */ path_a = cogl_path_new (); cogl_path_rectangle (path_a, 0, 0, BLOCK_SIZE, BLOCK_SIZE); cogl_path_rectangle (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2, BLOCK_SIZE, BLOCK_SIZE); draw_path_at (path_a, white, 9, 0); cogl_object_unref (path_a); /* Draw a clockwise outer path */ path_a = cogl_path_new (); cogl_path_move_to (path_a, 0, 0); cogl_path_line_to (path_a, BLOCK_SIZE, 0); cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE); cogl_path_line_to (path_a, 0, BLOCK_SIZE); cogl_path_close (path_a); /* Add a clockwise sub path in the upper left quadrant */ cogl_path_move_to (path_a, 0, 0); cogl_path_line_to (path_a, BLOCK_SIZE / 2, 0); cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (path_a, 0, BLOCK_SIZE / 2); cogl_path_close (path_a); /* Add a counter-clockwise sub path in the upper right quadrant */ cogl_path_move_to (path_a, BLOCK_SIZE / 2, 0); cogl_path_line_to (path_a, BLOCK_SIZE / 2, BLOCK_SIZE / 2); cogl_path_line_to (path_a, BLOCK_SIZE, BLOCK_SIZE / 2); cogl_path_line_to (path_a, BLOCK_SIZE, 0); cogl_path_close (path_a); /* Retain the path for the next test */ draw_path_at (path_a, white, 10, 0); /* Draw the same path again with the other fill rule */ cogl_path_set_fill_rule (path_a, COGL_PATH_FILL_RULE_NON_ZERO); draw_path_at (path_a, white, 11, 0); cogl_object_unref (path_a); } static void validate_result (void) { check_block (0, 0, 0x8 /* bottom right */); check_block (1, 0, 0xf /* all of them */); check_block (2, 0, 0x8 /* bottom right */); check_block (3, 0, 0x8 /* bottom right */); check_block (4, 0, 0x9 /* top left and bottom right */); check_block (5, 0, 0x8 /* bottom right */); check_block (6, 0, 0xa /* bottom right and top right */); check_block (7, 0, 0x9 /* top_left and bottom right */); check_block (8, 0, 0xe /* all but top left */); check_block (9, 0, 0x7 /* all but bottom right */); check_block (10, 0, 0xc /* bottom two */); check_block (11, 0, 0xd /* all but top right */); } void test_path (void) { TestState state; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); validate_result (); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-point-size-attribute.c0000664000175000017500000001225114723361714022717 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" /* This test assumes the GL driver supports point sizes up to 16 pixels. Cogl should probably have some way of querying the size so we start from that instead */ #define MAX_POINT_SIZE 16 #define MIN_POINT_SIZE 4 #define N_POINTS (MAX_POINT_SIZE - MIN_POINT_SIZE + 1) /* The size of the area that we'll paint each point in */ #define POINT_BOX_SIZE (MAX_POINT_SIZE * 2) typedef struct { float x, y; float point_size; } PointVertex; static int calc_coord_offset (int pos, int pos_index, int point_size) { switch (pos_index) { case 0: return pos - point_size / 2 - 2; case 1: return pos - point_size / 2 + 2; case 2: return pos + point_size / 2 - 2; case 3: return pos + point_size / 2 + 2; } g_assert_not_reached (); return 0; } static void verify_point_size (CoglFramebuffer *test_fb, int x_pos, int y_pos, int point_size) { int y, x; for (y = 0; y < 4; y++) for (x = 0; x < 4; x++) { gboolean in_point = x >= 1 && x <= 2 && y >= 1 && y <= 2; uint32_t expected_pixel = in_point ? 0x00ff00ff : 0xff0000ff; test_utils_check_pixel (test_fb, calc_coord_offset (x_pos, x, point_size), calc_coord_offset (y_pos, y, point_size), expected_pixel); } } static CoglPrimitive * create_primitive (const char *attribute_name) { PointVertex vertices[N_POINTS]; CoglAttributeBuffer *buffer; CoglAttribute *attributes[2]; CoglPrimitive *prim; int i; for (i = 0; i < N_POINTS; i++) { vertices[i].x = i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2; vertices[i].y = POINT_BOX_SIZE / 2; vertices[i].point_size = MAX_POINT_SIZE - i; } buffer = cogl_attribute_buffer_new (test_ctx, sizeof (vertices), vertices); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (PointVertex), G_STRUCT_OFFSET (PointVertex, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (buffer, attribute_name, sizeof (PointVertex), G_STRUCT_OFFSET (PointVertex, point_size), 1, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_POINTS, N_POINTS, attributes, 2 /* n_attributes */); for (i = 0; i < 2; i++) cogl_object_unref (attributes[i]); return prim; } static void do_test (const char *attribute_name, void (* pipeline_setup_func) (CoglPipeline *pipeline)) { int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); CoglPrimitive *primitive; CoglPipeline *pipeline; int i; cogl_framebuffer_orthographic (test_fb, 0, 0, /* x_1, y_1 */ fb_width, /* x_2 */ fb_height /* y_2 */, -1, 100 /* near/far */); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1.0f, 0.0f, 0.0f, 1.0f); primitive = create_primitive (attribute_name); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0x00, 0xff, 0x00, 0xff); cogl_pipeline_set_per_vertex_point_size (pipeline, TRUE, NULL); if (pipeline_setup_func) pipeline_setup_func (pipeline); cogl_primitive_draw (primitive, test_fb, pipeline); cogl_object_unref (pipeline); cogl_object_unref (primitive); /* Verify all of the points where drawn at the right size */ for (i = 0; i < N_POINTS; i++) verify_point_size (test_fb, i * POINT_BOX_SIZE + POINT_BOX_SIZE / 2, /* x */ POINT_BOX_SIZE / 2, /* y */ MAX_POINT_SIZE - i /* point size */); if (cogl_test_verbose ()) g_print ("OK\n"); } void test_point_size_attribute (void) { do_test ("cogl_point_size_in", NULL); } static void setup_snippet (CoglPipeline *pipeline) { CoglSnippet *snippet; snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_POINT_SIZE, "attribute float " "my_super_duper_point_size_attrib;\n", NULL); cogl_snippet_set_replace (snippet, "cogl_point_size_out = " "my_super_duper_point_size_attrib;\n"); cogl_pipeline_add_snippet (pipeline, snippet); cogl_object_unref (snippet); } void test_point_size_attribute_snippet (void) { do_test ("my_super_duper_point_size_attrib", setup_snippet); } muffin-6.4.1/cogl/tests/conform/test-npot-texture.c0000664000175000017500000001160014723361714021270 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" /* Non-power-of-two sized texture that should cause slicing */ #define TEXTURE_SIZE 384 /* Number of times to split the texture up on each axis */ #define PARTS 2 /* The texture is split into four parts, each with a different colour */ #define PART_SIZE (TEXTURE_SIZE / PARTS) /* Amount of pixels to skip off the top, bottom, left and right of the texture when reading back the stage */ #define TEST_INSET 4 /* Size to actually render the texture at */ #define TEXTURE_RENDER_SIZE TEXTURE_SIZE /* The size of a part once rendered */ #define PART_RENDER_SIZE (TEXTURE_RENDER_SIZE / PARTS) static const uint32_t corner_colors[PARTS * PARTS] = { /* Top left - red */ 0xff0000ff, /* Top right - green */ 0x00ff00ff, /* Bottom left - blue */ 0x0000ffff, /* Bottom right - yellow */ 0xffff00ff }; static void validate_part (int xnum, int ynum, uint32_t color) { test_utils_check_region (test_fb, xnum * PART_RENDER_SIZE + TEST_INSET, ynum * PART_RENDER_SIZE + TEST_INSET, PART_RENDER_SIZE - TEST_INSET * 2, PART_RENDER_SIZE - TEST_INSET * 2, color); } static void validate_result (void) { /* Validate that all four corners of the texture are drawn in the right color */ validate_part (0, 0, corner_colors[0]); validate_part (1, 0, corner_colors[1]); validate_part (0, 1, corner_colors[2]); validate_part (1, 1, corner_colors[3]); } static CoglTexture * make_texture (void) { void *tex_data; uint32_t *p; CoglTexture *tex; int partx, party, width, height; p = tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); /* Make a texture with a different color for each part */ for (party = 0; party < PARTS; party++) { height = (party < PARTS - 1 ? PART_SIZE : TEXTURE_SIZE - PART_SIZE * (PARTS - 1)); for (partx = 0; partx < PARTS; partx++) { uint32_t color = corner_colors[party * PARTS + partx]; width = (partx < PARTS - 1 ? PART_SIZE : TEXTURE_SIZE - PART_SIZE * (PARTS - 1)); while (width-- > 0) *(p++) = GUINT32_TO_BE (color); } while (--height > 0) { memcpy (p, p - TEXTURE_SIZE, TEXTURE_SIZE * 4); p += TEXTURE_SIZE; } } tex = test_utils_texture_new_from_data (test_ctx, TEXTURE_SIZE, TEXTURE_SIZE, TEST_UTILS_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGBA_8888_PRE, TEXTURE_SIZE * 4, tex_data); g_free (tex_data); if (cogl_test_verbose ()) { if (cogl_texture_is_sliced (tex)) g_print ("Texture is sliced\n"); else g_print ("Texture is not sliced\n"); } /* The texture should be sliced unless NPOTs are supported, which they are */ g_assert (!cogl_texture_is_sliced (tex)); return tex; } static void paint (void) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); CoglTexture *texture = make_texture (); int y, x; cogl_pipeline_set_layer_texture (pipeline, 0, texture); /* Just render the texture in the top left corner */ /* Render the texture using four separate rectangles */ for (y = 0; y < 2; y++) for (x = 0; x < 2; x++) cogl_framebuffer_draw_textured_rectangle (test_fb, pipeline, x * TEXTURE_RENDER_SIZE / 2, y * TEXTURE_RENDER_SIZE / 2, (x + 1) * TEXTURE_RENDER_SIZE / 2, (y + 1) * TEXTURE_RENDER_SIZE / 2, x / 2.0f, y / 2.0f, (x + 1) / 2.0f, (y + 1) / 2.0f); cogl_object_unref (pipeline); cogl_object_unref (texture); } void test_npot_texture (void) { cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (); validate_result (); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-blend.c0000664000175000017500000000374714723361714017713 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" static void paint (void) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); int width = cogl_framebuffer_get_width (test_fb); int half_width = width / 2; int height = cogl_framebuffer_get_height (test_fb); CoglVertexP2 tri0_vertices[] = { { 0, 0 }, { 0, height }, { half_width, height }, }; CoglVertexP2C4 tri1_vertices[] = { { half_width, 0, 0x80, 0x80, 0x80, 0x80 }, { half_width, height, 0x80, 0x80, 0x80, 0x80 }, { width, height, 0x80, 0x80, 0x80, 0x80 }, }; CoglPrimitive *tri0; CoglPrimitive *tri1; cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 0); tri0 = cogl_primitive_new_p2 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, tri0_vertices); tri1 = cogl_primitive_new_p2c4 (test_ctx, COGL_VERTICES_MODE_TRIANGLES, 3, tri1_vertices); /* Check that cogl correctly handles the case where we draw * different primitives same pipeline and switch from using the * opaque color associated with the pipeline and using a colour * attribute with an alpha component which implies blending is * required. * * If Cogl gets this wrong then then in all likelyhood the second * primitive will be drawn with blending still disabled. */ cogl_primitive_draw (tri0, test_fb, pipeline); cogl_primitive_draw (tri1, test_fb, pipeline); test_utils_check_pixel_and_alpha (test_fb, half_width + 5, height - 5, 0x80808080); } void test_blend (void) { cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (); } muffin-6.4.1/cogl/tests/conform/test-pipeline-uniforms.c0000664000175000017500000002724514723361714022273 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define LONG_ARRAY_SIZE 128 typedef struct _TestState { CoglPipeline *pipeline_red; CoglPipeline *pipeline_green; CoglPipeline *pipeline_blue; CoglPipeline *matrix_pipeline; CoglPipeline *vector_pipeline; CoglPipeline *int_pipeline; CoglPipeline *long_pipeline; int long_uniform_locations[LONG_ARRAY_SIZE]; } TestState; static const char color_source[] = "uniform float red, green, blue;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = vec4 (red, green, blue, 1.0);\n" "}\n"; static const char matrix_source[] = "uniform mat4 matrix_array[4];\n" "\n" "void\n" "main ()\n" "{\n" " vec4 color = vec4 (0.0, 0.0, 0.0, 1.0);\n" " int i;\n" "\n" " for (i = 0; i < 4; i++)\n" " color = matrix_array[i] * color;\n" "\n" " cogl_color_out = color;\n" "}\n"; static const char vector_source[] = "uniform vec4 vector_array[2];\n" "uniform vec3 short_vector;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = (vector_array[0] +\n" " vector_array[1] +\n" " vec4 (short_vector, 1.0));\n" "}\n"; static const char int_source[] = "uniform ivec4 vector_array[2];\n" "uniform int single_value;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = (vec4 (vector_array[0]) +\n" " vec4 (vector_array[1]) +\n" " vec4 (float (single_value), 0.0, 0.0, 255.0)) / 255.0;\n" "}\n"; static const char long_source[] = "uniform int long_array[" G_STRINGIFY (LONG_ARRAY_SIZE) "];\n" "const int last_index = " G_STRINGIFY (LONG_ARRAY_SIZE) " - 1;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = vec4 (float (long_array[last_index]), 0.0, 0.0, 1.0);\n" "}\n"; static CoglPipeline * create_pipeline_for_shader (TestState *state, const char *shader_source) { CoglPipeline *pipeline; CoglHandle shader; CoglHandle program; pipeline = cogl_pipeline_new (test_ctx); shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); cogl_shader_source (shader, shader_source); program = cogl_create_program (); cogl_program_attach_shader (program, shader); cogl_pipeline_set_user_program (pipeline, program); cogl_object_unref (shader); cogl_object_unref (program); return pipeline; } static void init_state (TestState *state) { int uniform_location; state->pipeline_red = create_pipeline_for_shader (state, color_source); uniform_location = cogl_pipeline_get_uniform_location (state->pipeline_red, "red"); cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 1.0f); uniform_location = cogl_pipeline_get_uniform_location (state->pipeline_red, "green"); cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f); uniform_location = cogl_pipeline_get_uniform_location (state->pipeline_red, "blue"); cogl_pipeline_set_uniform_1f (state->pipeline_red, uniform_location, 0.0f); state->pipeline_green = cogl_pipeline_copy (state->pipeline_red); uniform_location = cogl_pipeline_get_uniform_location (state->pipeline_green, "green"); cogl_pipeline_set_uniform_1f (state->pipeline_green, uniform_location, 1.0f); state->pipeline_blue = cogl_pipeline_copy (state->pipeline_red); uniform_location = cogl_pipeline_get_uniform_location (state->pipeline_blue, "blue"); cogl_pipeline_set_uniform_1f (state->pipeline_blue, uniform_location, 1.0f); state->matrix_pipeline = create_pipeline_for_shader (state, matrix_source); state->vector_pipeline = create_pipeline_for_shader (state, vector_source); state->int_pipeline = create_pipeline_for_shader (state, int_source); state->long_pipeline = NULL; } static void init_long_pipeline_state (TestState *state) { int i; state->long_pipeline = create_pipeline_for_shader (state, long_source); /* This tries to lookup a large number of uniform names to make sure that the bitmask of overriden uniforms flows over the size of a single long so that it has to resort to allocating it */ for (i = 0; i < LONG_ARRAY_SIZE; i++) { char *uniform_name = g_strdup_printf ("long_array[%i]", i); state->long_uniform_locations[i] = cogl_pipeline_get_uniform_location (state->long_pipeline, uniform_name); g_free (uniform_name); } } static void destroy_state (TestState *state) { cogl_object_unref (state->pipeline_red); cogl_object_unref (state->pipeline_green); cogl_object_unref (state->pipeline_blue); cogl_object_unref (state->matrix_pipeline); cogl_object_unref (state->vector_pipeline); cogl_object_unref (state->int_pipeline); if (state->long_pipeline) cogl_object_unref (state->long_pipeline); } static void paint_pipeline (CoglPipeline *pipeline, int pos) { cogl_framebuffer_draw_rectangle (test_fb, pipeline, pos * 10, 0, pos * 10 + 10, 10); } static void paint_color_pipelines (TestState *state) { CoglPipeline *temp_pipeline; int uniform_location; int i; /* Paint with the first pipeline that sets the uniforms to bright red */ paint_pipeline (state->pipeline_red, 0); /* Paint with the two other pipelines. These inherit from the red pipeline and only override one other component. The values for the two other components should be inherited from the red pipeline. */ paint_pipeline (state->pipeline_green, 1); paint_pipeline (state->pipeline_blue, 2); /* Try modifying a single pipeline for multiple rectangles */ temp_pipeline = cogl_pipeline_copy (state->pipeline_green); uniform_location = cogl_pipeline_get_uniform_location (temp_pipeline, "green"); for (i = 0; i <= 8; i++) { cogl_pipeline_set_uniform_1f (temp_pipeline, uniform_location, i / 8.0f); paint_pipeline (temp_pipeline, i + 3); } cogl_object_unref (temp_pipeline); } static void paint_matrix_pipeline (CoglPipeline *pipeline) { CoglMatrix matrices[4]; float matrix_floats[16 * 4]; int uniform_location; int i; for (i = 0; i < 4; i++) cogl_matrix_init_identity (matrices + i); /* Use the first matrix to make the color red */ cogl_matrix_translate (matrices + 0, 1.0f, 0.0f, 0.0f); /* Rotate the vertex so that it ends up green */ cogl_matrix_rotate (matrices + 1, 90.0f, 0.0f, 0.0f, 1.0f); /* Scale the vertex so it ends up halved */ cogl_matrix_scale (matrices + 2, 0.5f, 0.5f, 0.5f); /* Add a blue component in the final matrix. The final matrix is uploaded as transposed so we need to transpose first to cancel that out */ cogl_matrix_translate (matrices + 3, 0.0f, 0.0f, 1.0f); cogl_matrix_transpose (matrices + 3); for (i = 0; i < 4; i++) memcpy (matrix_floats + i * 16, cogl_matrix_get_array (matrices + i), sizeof (float) * 16); /* Set the first three matrices as transposed */ uniform_location = cogl_pipeline_get_uniform_location (pipeline, "matrix_array"); cogl_pipeline_set_uniform_matrix (pipeline, uniform_location, 4, /* dimensions */ 3, /* count */ FALSE, /* not transposed */ matrix_floats); /* Set the last matrix as untransposed */ uniform_location = cogl_pipeline_get_uniform_location (pipeline, "matrix_array[3]"); cogl_pipeline_set_uniform_matrix (pipeline, uniform_location, 4, /* dimensions */ 1, /* count */ TRUE, /* transposed */ matrix_floats + 16 * 3); paint_pipeline (pipeline, 12); } static void paint_vector_pipeline (CoglPipeline *pipeline) { float vector_array_values[] = { 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f }; float short_vector_values[] = { 0.0f, 0.0f, 1.0f }; int uniform_location; uniform_location = cogl_pipeline_get_uniform_location (pipeline, "vector_array"); cogl_pipeline_set_uniform_float (pipeline, uniform_location, 4, /* n_components */ 2, /* count */ vector_array_values); uniform_location = cogl_pipeline_get_uniform_location (pipeline, "short_vector"); cogl_pipeline_set_uniform_float (pipeline, uniform_location, 3, /* n_components */ 1, /* count */ short_vector_values); paint_pipeline (pipeline, 13); } static void paint_int_pipeline (CoglPipeline *pipeline) { int vector_array_values[] = { 0x00, 0x00, 0xff, 0x00, 0x00, 0xff, 0x00, 0x00 }; int single_value = 0x80; int uniform_location; uniform_location = cogl_pipeline_get_uniform_location (pipeline, "vector_array"); cogl_pipeline_set_uniform_int (pipeline, uniform_location, 4, /* n_components */ 2, /* count */ vector_array_values); uniform_location = cogl_pipeline_get_uniform_location (pipeline, "single_value"); cogl_pipeline_set_uniform_1i (pipeline, uniform_location, single_value); paint_pipeline (pipeline, 14); } static void paint_long_pipeline (TestState *state) { int i; for (i = 0; i < LONG_ARRAY_SIZE; i++) { int location = state->long_uniform_locations[i]; cogl_pipeline_set_uniform_1i (state->long_pipeline, location, i == LONG_ARRAY_SIZE - 1); } paint_pipeline (state->long_pipeline, 15); } static void paint (TestState *state) { cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); paint_color_pipelines (state); paint_matrix_pipeline (state->matrix_pipeline); paint_vector_pipeline (state->vector_pipeline); paint_int_pipeline (state->int_pipeline); } static void check_pos (int pos, uint32_t color) { test_utils_check_pixel (test_fb, pos * 10 + 5, 5, color); } static void validate_result (void) { int i; check_pos (0, 0xff0000ff); check_pos (1, 0xffff00ff); check_pos (2, 0xff00ffff); for (i = 0; i <= 8; i++) { int green_value = i / 8.0f * 255.0f + 0.5f; check_pos (i + 3, 0xff0000ff + (green_value << 16)); } check_pos (12, 0x0080ffff); check_pos (13, 0xffffffff); check_pos (14, 0x80ffffff); } static void validate_long_pipeline_result (void) { check_pos (15, 0xff0000ff); } void test_pipeline_uniforms (void) { TestState state; init_state (&state); cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); validate_result (); /* Try the test again after querying the location of a large number of uniforms. This should verify that the bitmasks still work even if they have to allocate a separate array to store the bits */ init_long_pipeline_state (&state); paint (&state); paint_long_pipeline (&state); validate_result (); validate_long_pipeline_result (); destroy_state (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-blend-strings.c0000664000175000017500000003145714723361714021401 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define QUAD_WIDTH 20 #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 #define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) #define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) #define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) #define MASK_ALPHA(COLOR) (COLOR & 0xff) #define BLEND_CONSTANT_UNUSED 0xDEADBEEF #define TEX_CONSTANT_UNUSED 0xDEADBEEF typedef struct _TestState { CoglContext *ctx; } TestState; static void test_blend_paint (TestState *state, int x, int y, uint32_t src_color, uint32_t dst_color, const char *blend_string, uint32_t blend_constant, uint32_t expected_result) { /* src color */ uint8_t Sr = MASK_RED (src_color); uint8_t Sg = MASK_GREEN (src_color); uint8_t Sb = MASK_BLUE (src_color); uint8_t Sa = MASK_ALPHA (src_color); /* dest color */ uint8_t Dr = MASK_RED (dst_color); uint8_t Dg = MASK_GREEN (dst_color); uint8_t Db = MASK_BLUE (dst_color); uint8_t Da = MASK_ALPHA (dst_color); /* blend constant - when applicable */ uint8_t Br = MASK_RED (blend_constant); uint8_t Bg = MASK_GREEN (blend_constant); uint8_t Bb = MASK_BLUE (blend_constant); uint8_t Ba = MASK_ALPHA (blend_constant); CoglColor blend_const_color; CoglPipeline *pipeline; gboolean status; GError *error = NULL; int y_off; int x_off; /* First write out the destination color without any blending... */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, Dr, Dg, Db, Da); cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); cogl_object_unref (pipeline); /* * Now blend a rectangle over our well defined destination: */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, Sr, Sg, Sb, Sa); status = cogl_pipeline_set_blend (pipeline, blend_string, &error); if (!status) { /* It's not strictly a test failure; you need a more capable GPU or * driver to test this blend string. */ if (cogl_test_verbose ()) { g_debug ("Failed to test blend string %s: %s", blend_string, error->message); g_print ("Skipping\n"); } return; } cogl_color_init_from_4ub (&blend_const_color, Br, Bg, Bb, Ba); cogl_pipeline_set_blend_constant (pipeline, &blend_const_color); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); cogl_object_unref (pipeline); /* See what we got... */ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2); x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); if (cogl_test_verbose ()) { g_print ("test_blend (%d, %d):\n%s\n", x, y, blend_string); g_print (" src color = %02x, %02x, %02x, %02x\n", Sr, Sg, Sb, Sa); g_print (" dst color = %02x, %02x, %02x, %02x\n", Dr, Dg, Db, Da); if (blend_constant != BLEND_CONSTANT_UNUSED) g_print (" blend constant = %02x, %02x, %02x, %02x\n", Br, Bg, Bb, Ba); else g_print (" blend constant = UNUSED\n"); } test_utils_check_pixel (test_fb, x_off, y_off, expected_result); } static CoglTexture * make_texture (uint32_t color) { guchar *tex_data, *p; uint8_t r = MASK_RED (color); uint8_t g = MASK_GREEN (color); uint8_t b = MASK_BLUE (color); uint8_t a = MASK_ALPHA (color); CoglTexture *tex; tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4); for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;) { *(--p) = a; *(--p) = b; *(--p) = g; *(--p) = r; } /* Note: we claim that the data is premultiplied so that Cogl won't * premultiply the data on upload */ tex = test_utils_texture_new_from_data (test_ctx, QUAD_WIDTH, QUAD_WIDTH, TEST_UTILS_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGBA_8888_PRE, QUAD_WIDTH * 4, tex_data); g_free (tex_data); return tex; } static void test_tex_combine (TestState *state, int x, int y, uint32_t tex0_color, uint32_t tex1_color, uint32_t combine_constant, const char *combine_string, uint32_t expected_result) { CoglTexture *tex0, *tex1; /* combine constant - when applicable */ uint8_t Cr = MASK_RED (combine_constant); uint8_t Cg = MASK_GREEN (combine_constant); uint8_t Cb = MASK_BLUE (combine_constant); uint8_t Ca = MASK_ALPHA (combine_constant); CoglColor combine_const_color; CoglPipeline *pipeline; gboolean status; GError *error = NULL; int y_off; int x_off; tex0 = make_texture (tex0_color); tex1 = make_texture (tex1_color); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 0x80, 0x80, 0x80, 0x80); cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); cogl_pipeline_set_layer_texture (pipeline, 0, tex0); cogl_pipeline_set_layer_combine (pipeline, 0, "RGBA = REPLACE (TEXTURE)", NULL); cogl_pipeline_set_layer_texture (pipeline, 1, tex1); status = cogl_pipeline_set_layer_combine (pipeline, 1, combine_string, &error); if (!status) { /* It's not strictly a test failure; you need a more capable GPU or * driver to test this texture combine string. */ g_debug ("Failed to test texture combine string %s: %s", combine_string, error->message); } cogl_color_init_from_4ub (&combine_const_color, Cr, Cg, Cb, Ca); cogl_pipeline_set_layer_combine_constant (pipeline, 1, &combine_const_color); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); cogl_object_unref (pipeline); cogl_object_unref (tex0); cogl_object_unref (tex1); /* See what we got... */ y_off = y * QUAD_WIDTH + (QUAD_WIDTH / 2); x_off = x * QUAD_WIDTH + (QUAD_WIDTH / 2); if (cogl_test_verbose ()) { g_print ("test_tex_combine (%d, %d):\n%s\n", x, y, combine_string); g_print (" texture 0 color = 0x%08lX\n", (unsigned long)tex0_color); g_print (" texture 1 color = 0x%08lX\n", (unsigned long)tex1_color); if (combine_constant != TEX_CONSTANT_UNUSED) g_print (" combine constant = %02x, %02x, %02x, %02x\n", Cr, Cg, Cb, Ca); else g_print (" combine constant = UNUSED\n"); } test_utils_check_pixel (test_fb, x_off, y_off, expected_result); } static void paint (TestState *state) { test_blend_paint (state, 0, 0, /* position */ 0xff0000ff, /* src */ 0xffffffff, /* dst */ "RGBA = ADD (SRC_COLOR, 0)", BLEND_CONSTANT_UNUSED, 0xff0000ff); /* expected */ test_blend_paint (state, 1, 0, /* position */ 0x11223344, /* src */ 0x11223344, /* dst */ "RGBA = ADD (SRC_COLOR, DST_COLOR)", BLEND_CONSTANT_UNUSED, 0x22446688); /* expected */ test_blend_paint (state, 2, 0, /* position */ 0x80808080, /* src */ 0xffffffff, /* dst */ "RGBA = ADD (SRC_COLOR * (CONSTANT), 0)", 0x80808080, /* constant (RGBA all = 0.5 when normalized) */ 0x40404040); /* expected */ test_blend_paint (state, 3, 0, /* position */ 0x80000080, /* src (alpha = 0.5 when normalized) */ 0x40000000, /* dst */ "RGBA = ADD (SRC_COLOR * (SRC_COLOR[A])," " DST_COLOR * (1-SRC_COLOR[A]))", BLEND_CONSTANT_UNUSED, 0x60000040); /* expected */ /* XXX: * For all texture combine tests tex0 will use a combine mode of * "RGBA = REPLACE (TEXTURE)" */ test_tex_combine (state, 4, 0, /* position */ 0x11111111, /* texture 0 color */ 0x22222222, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = ADD (PREVIOUS, TEXTURE)", /* tex combine */ 0x33333333); /* expected */ test_tex_combine (state, 5, 0, /* position */ 0x40404040, /* texture 0 color */ 0x80808080, /* texture 1 color (RGBA all = 0.5) */ TEX_CONSTANT_UNUSED, "RGBA = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ 0x20202020); /* expected */ test_tex_combine (state, 6, 0, /* position */ 0xffffff80, /* texture 0 color (alpha = 0.5) */ 0xDEADBE40, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGB = REPLACE (PREVIOUS)" "A = MODULATE (PREVIOUS, TEXTURE)", /* tex combine */ 0xffffff20); /* expected */ /* XXX: we are assuming test_tex_combine creates a pipeline with * a color of 0x80808080 (i.e. the "PRIMARY" color) */ test_tex_combine (state, 7, 0, /* position */ 0xffffff80, /* texture 0 color (alpha = 0.5) */ 0xDEADBE20, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGB = REPLACE (PREVIOUS)" "A = MODULATE (PRIMARY, TEXTURE)", /* tex combine */ 0xffffff10); /* expected */ test_tex_combine (state, 8, 0, /* position */ 0x11111111, /* texture 0 color */ 0x22222222, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = ADD (PREVIOUS, 1-TEXTURE)", /* tex combine */ 0xeeeeeeee); /* expected */ /* this is again assuming a primary color of 0x80808080 */ test_tex_combine (state, 9, 0, /* position */ 0x10101010, /* texture 0 color */ 0x20202020, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = INTERPOLATE (PREVIOUS, TEXTURE, PRIMARY)", 0x18181818); /* expected */ #if 0 /* using TEXTURE_N appears to be broken in cogl-blend-string.c */ test_tex_combine (state, 0, 1, /* position */ 0xDEADBEEF, /* texture 0 color (not used) */ 0x11223344, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = ADD (TEXTURE_1, TEXTURE)", /* tex combine */ 0x22446688); /* expected */ #endif test_tex_combine (state, 1, 1, /* position */ 0x21314151, /* texture 0 color */ 0x99999999, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = ADD_SIGNED (PREVIOUS, TEXTURE)", /* tex combine */ 0x3a4a5a6a); /* expected */ test_tex_combine (state, 2, 1, /* position */ 0xfedcba98, /* texture 0 color */ 0x11111111, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGBA = SUBTRACT (PREVIOUS, TEXTURE)", /* tex combine */ 0xedcba987); /* expected */ test_tex_combine (state, 3, 1, /* position */ 0x8899aabb, /* texture 0 color */ 0xbbaa9988, /* texture 1 color */ TEX_CONSTANT_UNUSED, "RGB = DOT3_RGBA (PREVIOUS, TEXTURE)" "A = REPLACE (PREVIOUS)", 0x2a2a2abb); /* expected */ } void test_blend_strings (void) { TestState state; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-pixel-buffer.c0000664000175000017500000001677714723361714021226 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define BITMAP_SIZE 256 /* * Creates a 256 x 256 with image data split into four quadrants. The * colours of these in reading order will be: blue, green, cyan, * red */ static void generate_bitmap_data (uint8_t *data, int stride) { int y, x; for (y = 0; y < BITMAP_SIZE; y++) { for (x = 0; x < BITMAP_SIZE; x++) { int color_num = x / (BITMAP_SIZE / 2) + y / (BITMAP_SIZE / 2) * 2 + 1; *(data++) = (color_num & 4) ? 255 : 0; *(data++) = (color_num & 2) ? 255 : 0; *(data++) = (color_num & 1) ? 255 : 0; *(data++) = 255; } data += stride - BITMAP_SIZE * 4; } } static CoglBitmap * create_bitmap (void) { CoglBitmap *bitmap; CoglBuffer *buffer; bitmap = cogl_bitmap_new_with_size (test_ctx, BITMAP_SIZE, BITMAP_SIZE, COGL_PIXEL_FORMAT_RGBA_8888); buffer = cogl_bitmap_get_buffer (bitmap); g_assert (cogl_is_pixel_buffer (buffer)); g_assert (cogl_is_buffer (buffer)); cogl_buffer_set_update_hint (buffer, COGL_BUFFER_UPDATE_HINT_DYNAMIC); g_assert_cmpint (cogl_buffer_get_update_hint (buffer), ==, COGL_BUFFER_UPDATE_HINT_DYNAMIC); return bitmap; } static CoglBitmap * create_and_fill_bitmap (void) { CoglBitmap *bitmap = create_bitmap (); CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap); uint8_t *map; unsigned int stride; stride = cogl_bitmap_get_rowstride (bitmap); map = cogl_buffer_map (buffer, COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD); g_assert (map); generate_bitmap_data (map, stride); cogl_buffer_unmap (buffer); return bitmap; } static CoglTexture * create_texture_from_bitmap (CoglBitmap *bitmap) { CoglTexture2D *texture; texture = cogl_texture_2d_new_from_bitmap (bitmap); g_assert (texture != NULL); return texture; } static CoglPipeline * create_pipeline_from_texture (CoglTexture *texture) { CoglPipeline *pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, texture); cogl_pipeline_set_layer_filters (pipeline, 0, /* layer_num */ COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); return pipeline; } static void check_colours (uint32_t color0, uint32_t color1, uint32_t color2, uint32_t color3) { int fb_width = cogl_framebuffer_get_width (test_fb); int fb_height = cogl_framebuffer_get_height (test_fb); test_utils_check_region (test_fb, 1, 1, /* x/y */ fb_width / 2 - 2, /* width */ fb_height / 2 - 2, /* height */ color0); test_utils_check_region (test_fb, fb_width / 2 + 1, /* x */ 1, /* y */ fb_width / 2 - 2, /* width */ fb_height / 2 - 2, /* height */ color1); test_utils_check_region (test_fb, 1, /* x */ fb_height / 2 + 1, /* y */ fb_width / 2 - 2, /* width */ fb_height / 2 - 2, /* height */ color2); test_utils_check_region (test_fb, fb_width / 2 + 1, /* x */ fb_height / 2 + 1, /* y */ fb_width / 2 - 2, /* width */ fb_height / 2 - 2, /* height */ color3); } void test_pixel_buffer_map (void) { CoglBitmap *bitmap = create_and_fill_bitmap (); CoglPipeline *pipeline; CoglTexture *texture; texture = create_texture_from_bitmap (bitmap); pipeline = create_pipeline_from_texture (texture); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1.0f, 1.0f, 1.0f, -1.0f); cogl_object_unref (bitmap); cogl_object_unref (texture); cogl_object_unref (pipeline); check_colours (0x0000ffff, 0x00ff00ff, 0x00ffffff, 0xff0000ff); if (cogl_test_verbose ()) g_print ("OK\n"); } void test_pixel_buffer_set_data (void) { CoglBitmap *bitmap = create_bitmap (); CoglBuffer *buffer = cogl_bitmap_get_buffer (bitmap); CoglPipeline *pipeline; CoglTexture *texture; uint8_t *data; unsigned int stride; stride = cogl_bitmap_get_rowstride (bitmap); data = g_malloc (stride * BITMAP_SIZE); generate_bitmap_data (data, stride); cogl_buffer_set_data (buffer, 0, /* offset */ data, stride * (BITMAP_SIZE - 1) + BITMAP_SIZE * 4); g_free (data); texture = create_texture_from_bitmap (bitmap); pipeline = create_pipeline_from_texture (texture); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1.0f, 1.0f, 1.0f, -1.0f); cogl_object_unref (bitmap); cogl_object_unref (texture); cogl_object_unref (pipeline); check_colours (0x0000ffff, 0x00ff00ff, 0x00ffffff, 0xff0000ff); if (cogl_test_verbose ()) g_print ("OK\n"); } static CoglTexture * create_white_texture (void) { CoglTexture2D *texture; uint8_t *data = g_malloc (BITMAP_SIZE * BITMAP_SIZE * 4); memset (data, 255, BITMAP_SIZE * BITMAP_SIZE * 4); texture = cogl_texture_2d_new_from_data (test_ctx, BITMAP_SIZE, BITMAP_SIZE, COGL_PIXEL_FORMAT_RGBA_8888, BITMAP_SIZE * 4, /* rowstride */ data, NULL); /* don't catch errors */ g_free (data); return texture; } void test_pixel_buffer_sub_region (void) { CoglBitmap *bitmap = create_and_fill_bitmap (); CoglPipeline *pipeline; CoglTexture *texture; texture = create_white_texture (); /* Replace the top-right quadrant of the texture with the red part * of the bitmap */ cogl_texture_set_region_from_bitmap (texture, BITMAP_SIZE / 2, /* src_x */ BITMAP_SIZE / 2, /* src_y */ BITMAP_SIZE / 2, /* dst_x */ 0, /* dst_y */ BITMAP_SIZE / 2, /* width */ BITMAP_SIZE / 2, /* height */ bitmap); pipeline = create_pipeline_from_texture (texture); cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1.0f, 1.0f, 1.0f, -1.0f); cogl_object_unref (bitmap); cogl_object_unref (texture); cogl_object_unref (pipeline); check_colours (0xffffffff, 0xff0000ff, 0xffffffff, 0xffffffff); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-offscreen.c0000664000175000017500000001413714723361714020574 0ustar fabiofabio#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 #include #include "test-declarations.h" #include "test-utils.h" #define RED 0 #define GREEN 1 #define BLUE 2 typedef struct _TestState { int fb_width; int fb_height; } TestState; static void check_quadrant (TestState *state, int qx, int qy, uint32_t expected_rgba) { /* The quadrants are all stuffed into the top right corner of the framebuffer */ int x = state->fb_width * qx / 4 + state->fb_width / 2; int y = state->fb_height * qy / 4; int width = state->fb_width / 4; int height = state->fb_height / 4; /* Subtract a two-pixel gap around the edges to allow some rounding differences */ x += 2; y += 2; width -= 4; height -= 4; test_utils_check_region (test_fb, x, y, width, height, expected_rgba); } static void test_paint (TestState *state) { CoglTexture2D *tex_2d; CoglTexture *tex; CoglOffscreen *offscreen; CoglPipeline *opaque_pipeline; CoglPipeline *texture_pipeline; tex_2d = cogl_texture_2d_new_with_size (test_ctx, state->fb_width, state->fb_height); tex = tex_2d; offscreen = cogl_offscreen_new_with_texture (tex); /* Set a scale and translate transform on the window framebuffer * before switching to the offscreen framebuffer so we can verify it * gets restored when we switch back * * The test is going to draw a grid of 4 colors to a texture which * we subsequently draw to the window with a fullscreen rectangle. * This transform will flip the texture left to right, scale it to a * quarter of the window size and slide it to the top right of the * window. */ cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, 0.5, 0.5, 0); cogl_framebuffer_scale (test_fb, -0.5, 0.5, 1); /* Setup something other than the identity matrix for the modelview so we can * verify it gets restored when we call cogl_pop_framebuffer () */ cogl_framebuffer_scale (test_fb, 2, 2, 1); opaque_pipeline = cogl_pipeline_new (test_ctx); /* red, top left */ cogl_pipeline_set_color4ub (opaque_pipeline, 0xff, 0x00, 0x00, 0xff); cogl_framebuffer_draw_rectangle (offscreen, opaque_pipeline, -0.5, 0.5, 0, 0); /* green, top right */ cogl_pipeline_set_color4ub (opaque_pipeline, 0x00, 0xff, 0x00, 0xff); cogl_framebuffer_draw_rectangle (offscreen, opaque_pipeline, 0, 0.5, 0.5, 0); /* blue, bottom left */ cogl_pipeline_set_color4ub (opaque_pipeline, 0x00, 0x00, 0xff, 0xff); cogl_framebuffer_draw_rectangle (offscreen, opaque_pipeline, -0.5, 0, 0, -0.5); /* white, bottom right */ cogl_pipeline_set_color4ub (opaque_pipeline, 0xff, 0xff, 0xff, 0xff); cogl_framebuffer_draw_rectangle (offscreen, opaque_pipeline, 0, 0, 0.5, -0.5); /* Cogl should release the last reference when we call cogl_pop_framebuffer() */ cogl_object_unref (offscreen); texture_pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (texture_pipeline, 0, tex); cogl_framebuffer_draw_rectangle (test_fb, texture_pipeline, -1, 1, 1, -1); cogl_object_unref (opaque_pipeline); cogl_object_unref (texture_pipeline); cogl_object_unref (tex_2d); cogl_framebuffer_pop_matrix (test_fb); /* NB: The texture is drawn flipped horizontally and scaled to fit in the * top right corner of the window. */ /* red, top right */ check_quadrant (state, 1, 0, 0xff0000ff); /* green, top left */ check_quadrant (state, 0, 0, 0x00ff00ff); /* blue, bottom right */ check_quadrant (state, 1, 1, 0x0000ffff); /* white, bottom left */ check_quadrant (state, 0, 1, 0xffffffff); } static void test_flush (TestState *state) { CoglPipeline *pipeline; CoglTexture2D *tex_2d; CoglTexture *tex; CoglOffscreen *offscreen; CoglColor clear_color; int i; pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); for (i = 0; i < 3; i++) { /* This tests that rendering to a framebuffer and then reading back the contents of the texture will automatically flush the journal */ tex_2d = cogl_texture_2d_new_with_size (test_ctx, 16, 16); /* width/height */ tex = tex_2d; offscreen = cogl_offscreen_new_with_texture (tex); cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 255); cogl_framebuffer_clear (offscreen, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_draw_rectangle (offscreen, pipeline, -1, -1, 1, 1); if (i == 0) /* First time check using read pixels on the offscreen */ test_utils_check_region (offscreen, 1, 1, 15, 15, 0xff0000ff); else if (i == 1) { uint8_t data[16 * 4 * 16]; int x, y; /* Second time try reading back the texture contents */ cogl_texture_get_data (tex, COGL_PIXEL_FORMAT_RGBA_8888_PRE, 16 * 4, /* rowstride */ data); for (y = 1; y < 15; y++) for (x = 1; x < 15; x++) test_utils_compare_pixel (data + x * 4 + y * 16 * 4, 0xff0000ff); } if (i == 2) { /* Third time try drawing the texture to the screen */ cogl_framebuffer_draw_rectangle (test_fb, pipeline, -1, -1, 1, 1); test_utils_check_region (test_fb, 2, 2, /* x/y */ state->fb_width - 4, state->fb_height - 4, 0xff0000ff); } cogl_object_unref (tex_2d); cogl_object_unref (offscreen); } cogl_object_unref (pipeline); } void test_offscreen (void) { TestState state; state.fb_width = cogl_framebuffer_get_width (test_fb); state.fb_height = cogl_framebuffer_get_height (test_fb); test_paint (&state); test_flush (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-atlas-migration.c0000664000175000017500000000752514723361714021720 0ustar fabiofabio#include #include "test-declarations.h" #include "test-utils.h" #define N_TEXTURES 128 #define OPACITY_FOR_ROW(y) \ (0xff - ((y) & 0xf) * 0x10) #define COLOR_FOR_SIZE(size) \ (colors + (size) % 3) typedef struct { uint8_t red, green, blue, alpha; } TestColor; static const TestColor colors[] = { { 0xff, 0x00, 0x00, 0xff }, { 0x00, 0xff, 0x00, 0xff }, { 0x00, 0x00, 0xff, 0xff } }; static CoglTexture * create_texture (int size) { CoglTexture *texture; const TestColor *color; uint8_t *data, *p; int x, y; /* Create a red, green or blue texture depending on the size */ color = COLOR_FOR_SIZE (size); p = data = g_malloc (size * size * 4); /* Fill the data with the color but fade the opacity out with increasing y coordinates so that we can see the blending it the atlas migration accidentally blends with garbage in the texture */ for (y = 0; y < size; y++) { int opacity = OPACITY_FOR_ROW (y); for (x = 0; x < size; x++) { /* Store the colors premultiplied */ p[0] = color->red * opacity / 255; p[1] = color->green * opacity / 255; p[2] = color->blue * opacity / 255; p[3] = opacity; p += 4; } } texture = test_utils_texture_new_from_data (test_ctx, size, /* width */ size, /* height */ TEST_UTILS_TEXTURE_NONE, /* flags */ /* format */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* rowstride */ size * 4, data); g_free (data); return texture; } static void verify_texture (CoglTexture *texture, int size) { uint8_t *data, *p; int x, y; const TestColor *color; color = COLOR_FOR_SIZE (size); p = data = g_malloc (size * size * 4); cogl_texture_get_data (texture, COGL_PIXEL_FORMAT_RGBA_8888_PRE, size * 4, data); for (y = 0; y < size; y++) { int opacity = OPACITY_FOR_ROW (y); for (x = 0; x < size; x++) { TestColor real_color = { color->red * opacity / 255, color->green * opacity / 255, color->blue * opacity / 255 }; test_utils_compare_pixel (p, (real_color.red << 24) | (real_color.green << 16) | (real_color.blue << 8) | opacity); g_assert_cmpint (p[3], ==, opacity); p += 4; } } g_free (data); } void test_atlas_migration (void) { CoglTexture *textures[N_TEXTURES]; int i, tex_num; /* Create and destroy all of the textures a few times to increase the chances that we'll end up reusing the buffers for previously discarded atlases */ for (i = 0; i < 5; i++) { for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) textures[tex_num] = create_texture (tex_num + 1); for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) cogl_object_unref (textures[tex_num]); } /* Create all the textures again */ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) textures[tex_num] = create_texture (tex_num + 1); /* Verify that they all still have the right data */ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) verify_texture (textures[tex_num], tex_num + 1); /* Destroy them all */ for (tex_num = 0; tex_num < N_TEXTURES; tex_num++) cogl_object_unref (textures[tex_num]); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-backface-culling.c0000664000175000017500000002362414723361714021775 0ustar fabiofabio#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 #include #include #include "test-declarations.h" #include "test-utils.h" /* Size the texture so that it is just off a power of two to encourage it so use software tiling when NPOTs aren't available */ #define TEXTURE_SIZE 257 /* Amount of pixels to skip off the top, bottom, left and right of the texture when reading back the stage */ #define TEST_INSET 2 /* Size to actually render the texture at */ #define TEXTURE_RENDER_SIZE 8 typedef struct _TestState { CoglTexture *texture; CoglFramebuffer *offscreen; CoglTexture *offscreen_tex; int width, height; } TestState; static void validate_part (CoglFramebuffer *framebuffer, int xnum, int ynum, gboolean shown) { test_utils_check_region (framebuffer, xnum * TEXTURE_RENDER_SIZE + TEST_INSET, ynum * TEXTURE_RENDER_SIZE + TEST_INSET, TEXTURE_RENDER_SIZE - TEST_INSET * 2, TEXTURE_RENDER_SIZE - TEST_INSET * 2, shown ? 0xff0000ff : 0x000000ff); } /* We draw everything 8 times. The draw number is used as a bitmask to test all of the combinations of enabling both winding orders and all four culling modes */ #define FRONT_WINDING(draw_num) (((draw_num) & 0x01) >> 1) #define CULL_FACE_MODE(draw_num) (((draw_num) & 0x06) >> 2) static void paint_test_backface_culling (TestState *state, CoglFramebuffer *framebuffer) { int draw_num; CoglPipeline *base_pipeline = cogl_pipeline_new (test_ctx); cogl_framebuffer_orthographic (framebuffer, 0, 0, state->width, state->height, -1, 100); cogl_framebuffer_clear4f (framebuffer, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_STENCIL, 0, 0, 0, 1); cogl_pipeline_set_layer_texture (base_pipeline, 0, state->texture); cogl_pipeline_set_layer_filters (base_pipeline, 0, COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); /* Render the scene sixteen times to test all of the combinations of cull face mode, legacy state and winding orders */ for (draw_num = 0; draw_num < 8; draw_num++) { float x1 = 0, x2, y1 = 0, y2 = (float)(TEXTURE_RENDER_SIZE); CoglVertexP3T2 verts[4]; CoglPrimitive *primitive; CoglPipeline *pipeline; cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, 0, TEXTURE_RENDER_SIZE * draw_num, 0); pipeline = cogl_pipeline_copy (base_pipeline); cogl_pipeline_set_front_face_winding (pipeline, FRONT_WINDING (draw_num)); cogl_pipeline_set_cull_face_mode (pipeline, CULL_FACE_MODE (draw_num)); memset (verts, 0, sizeof (verts)); x2 = x1 + (float)(TEXTURE_RENDER_SIZE); /* Draw a front-facing texture */ cogl_framebuffer_draw_rectangle (framebuffer, pipeline, x1, y1, x2, y2); x1 = x2; x2 = x1 + (float)(TEXTURE_RENDER_SIZE); /* Draw a front-facing texture with flipped texcoords */ cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, x1, y1, x2, y2, 1.0, 0.0, 0.0, 1.0); x1 = x2; x2 = x1 + (float)(TEXTURE_RENDER_SIZE); /* Draw a back-facing texture */ cogl_framebuffer_draw_rectangle (framebuffer, pipeline, x2, y1, x1, y2); x1 = x2; x2 = x1 + (float)(TEXTURE_RENDER_SIZE); /* If the texture is sliced then cogl_polygon doesn't work so we'll just use a solid color instead */ if (cogl_texture_is_sliced (state->texture)) cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 255); /* Draw a front-facing polygon */ verts[0].x = x1; verts[0].y = y2; verts[1].x = x2; verts[1].y = y2; verts[2].x = x2; verts[2].y = y1; verts[3].x = x1; verts[3].y = y1; verts[0].s = 0; verts[0].t = 0; verts[1].s = 1.0; verts[1].t = 0; verts[2].s = 1.0; verts[2].t = 1.0; verts[3].s = 0; verts[3].t = 1.0; primitive = cogl_primitive_new_p3t2 (test_ctx, COGL_VERTICES_MODE_TRIANGLE_FAN, 4, verts); cogl_primitive_draw (primitive, framebuffer, pipeline); cogl_object_unref (primitive); x1 = x2; x2 = x1 + (float)(TEXTURE_RENDER_SIZE); /* Draw a back-facing polygon */ verts[0].x = x1; verts[0].y = y1; verts[1].x = x2; verts[1].y = y1; verts[2].x = x2; verts[2].y = y2; verts[3].x = x1; verts[3].y = y2; verts[0].s = 0; verts[0].t = 0; verts[1].s = 1.0; verts[1].t = 0; verts[2].s = 1.0; verts[2].t = 1.0; verts[3].s = 0; verts[3].t = 1.0; primitive = cogl_primitive_new_p3t2 (test_ctx, COGL_VERTICES_MODE_TRIANGLE_FAN, 4, verts); cogl_primitive_draw (primitive, framebuffer, pipeline); cogl_object_unref (primitive); x1 = x2; x2 = x1 + (float)(TEXTURE_RENDER_SIZE); cogl_framebuffer_pop_matrix (framebuffer); cogl_object_unref (pipeline); } cogl_object_unref (base_pipeline); } static void validate_result (CoglFramebuffer *framebuffer, int y_offset) { int draw_num; for (draw_num = 0; draw_num < 8; draw_num++) { gboolean cull_front, cull_back; CoglPipelineCullFaceMode cull_mode; cull_mode = CULL_FACE_MODE (draw_num); switch (cull_mode) { case COGL_PIPELINE_CULL_FACE_MODE_NONE: cull_front = FALSE; cull_back = FALSE; break; case COGL_PIPELINE_CULL_FACE_MODE_FRONT: cull_front = TRUE; cull_back = FALSE; break; case COGL_PIPELINE_CULL_FACE_MODE_BACK: cull_front = FALSE; cull_back = TRUE; break; case COGL_PIPELINE_CULL_FACE_MODE_BOTH: cull_front = TRUE; cull_back = TRUE; break; } if (FRONT_WINDING (draw_num) == COGL_WINDING_CLOCKWISE) { gboolean tmp = cull_front; cull_front = cull_back; cull_back = tmp; } /* Front-facing texture */ validate_part (framebuffer, 0, y_offset + draw_num, !cull_front); /* Front-facing texture with flipped tex coords */ validate_part (framebuffer, 1, y_offset + draw_num, !cull_front); /* Back-facing texture */ validate_part (framebuffer, 2, y_offset + draw_num, !cull_back); /* Front-facing texture polygon */ validate_part (framebuffer, 3, y_offset + draw_num, !cull_front); /* Back-facing texture polygon */ validate_part (framebuffer, 4, y_offset + draw_num, !cull_back); } } static void paint (TestState *state) { CoglPipeline *pipeline; paint_test_backface_culling (state, test_fb); /* * Now repeat the test but rendered to an offscreen * framebuffer. Note that by default the conformance tests are * always run to an offscreen buffer but we might as well have this * check anyway in case it is being run with COGL_TEST_ONSCREEN=1 */ paint_test_backface_culling (state, state->offscreen); /* Copy the result of the offscreen rendering for validation and * also so we can have visual feedback. */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, state->offscreen_tex); cogl_framebuffer_draw_rectangle (test_fb, pipeline, 0, TEXTURE_RENDER_SIZE * 8, state->width, state->height + TEXTURE_RENDER_SIZE * 8); cogl_object_unref (pipeline); validate_result (test_fb, 0); validate_result (test_fb, 8); } static CoglTexture * make_texture (void) { guchar *tex_data, *p; CoglTexture *tex; tex_data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); for (p = tex_data + TEXTURE_SIZE * TEXTURE_SIZE * 4; p > tex_data;) { *(--p) = 255; *(--p) = 0; *(--p) = 0; *(--p) = 255; } tex = test_utils_texture_new_from_data (test_ctx, TEXTURE_SIZE, TEXTURE_SIZE, TEST_UTILS_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_RGBA_8888, TEXTURE_SIZE * 4, tex_data); g_free (tex_data); return tex; } void test_backface_culling (void) { TestState state; CoglTexture *tex; state.width = cogl_framebuffer_get_width (test_fb); state.height = cogl_framebuffer_get_height (test_fb); state.offscreen = NULL; state.texture = make_texture (); tex = test_utils_texture_new_with_size (test_ctx, state.width, state.height, TEST_UTILS_TEXTURE_NO_SLICING, COGL_TEXTURE_COMPONENTS_RGBA); state.offscreen = cogl_offscreen_new_with_texture (tex); state.offscreen_tex = tex; paint (&state); cogl_object_unref (state.offscreen); cogl_object_unref (state.offscreen_tex); cogl_object_unref (state.texture); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/meson/0000775000175000017500000000000014723361714016614 5ustar fabiofabiomuffin-6.4.1/cogl/tests/conform/meson/find-conform-unit-tests.sh0000775000175000017500000000026714723361714023656 0ustar fabiofabio#!/bin/sh inputfile="$1" outputfile="$2" sed -n -e 's/^ \{1,\}ADD_TEST *( *\([a-zA-Z0-9_]\{1,\}\).*/\1/p' "$inputfile" | while read -r test; do echo "$test" >> "$outputfile" done muffin-6.4.1/cogl/tests/conform/test-map-buffer-range.c0000664000175000017500000001005014723361714021726 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" static uint8_t tex_data[2 * 2 * 4] = { 0xff, 0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0xff, 0xff, 0xff, 0x00, 0xff, 0xff }; /* Vertex data for a quad with all of the texture coordinates set to * the top left (red) pixel */ static CoglVertexP2T2 vertex_data[4] = { { -1, -1, 0, 0 }, { 1, -1, 0, 0 }, { -1, 1, 0, 0 }, { 1, 1, 0, 0 } }; void test_map_buffer_range (void) { CoglTexture2D *tex; CoglPipeline *pipeline; int fb_width, fb_height; CoglAttributeBuffer *buffer; CoglVertexP2T2 *data; CoglAttribute *pos_attribute; CoglAttribute *tex_coord_attribute; CoglPrimitive *primitive; tex = cogl_texture_2d_new_from_data (test_ctx, 2, 2, /* width/height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 2 * 4, /* rowstride */ tex_data, NULL /* error */); pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_pipeline_set_layer_filters (pipeline, 0, /* layer */ COGL_PIPELINE_FILTER_NEAREST, COGL_PIPELINE_FILTER_NEAREST); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, /* layer */ COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); fb_width = cogl_framebuffer_get_width (test_fb); fb_height = cogl_framebuffer_get_height (test_fb); buffer = cogl_attribute_buffer_new (test_ctx, sizeof (vertex_data), vertex_data); /* Replace the texture coordinates of the third vertex with the * coordinates for a green texel */ data = cogl_buffer_map_range (buffer, sizeof (vertex_data[0]) * 2, sizeof (vertex_data[0]), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD_RANGE, NULL); /* don't catch errors */ g_assert (data != NULL); data->x = vertex_data[2].x; data->y = vertex_data[2].y; data->s = 1.0f; data->t = 0.0f; cogl_buffer_unmap (buffer); pos_attribute = cogl_attribute_new (buffer, "cogl_position_in", sizeof (vertex_data[0]), offsetof (CoglVertexP2T2, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); tex_coord_attribute = cogl_attribute_new (buffer, "cogl_tex_coord_in", sizeof (vertex_data[0]), offsetof (CoglVertexP2T2, s), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); primitive = cogl_primitive_new (COGL_VERTICES_MODE_TRIANGLE_STRIP, 4, /* n_vertices */ pos_attribute, tex_coord_attribute, NULL); cogl_primitive_draw (primitive, test_fb, pipeline); cogl_object_unref (primitive); /* Top left pixel should be the one that is replaced to be green */ test_utils_check_pixel (test_fb, 1, 1, 0x00ff00ff); /* The other three corners should be left as red */ test_utils_check_pixel (test_fb, fb_width - 2, 1, 0xff0000ff); test_utils_check_pixel (test_fb, 1, fb_height - 2, 0xff0000ff); test_utils_check_pixel (test_fb, fb_width - 2, fb_height - 2, 0xff0000ff); cogl_object_unref (buffer); cogl_object_unref (pos_attribute); cogl_object_unref (tex_coord_attribute); cogl_object_unref (pipeline); cogl_object_unref (tex); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-viewport.c0000664000175000017500000003551014723361714020477 0ustar fabiofabio #include #include #include "test-conform-common.h" #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 #define FRAMEBUFFER_WIDTH 640 #define FRAMEBUFFER_HEIGHT 480 static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; static void assert_region_color (int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue, uint8_t alpha) { uint8_t *data = g_malloc0 (width * height * 4); cogl_read_pixels (x, y, width, height, COGL_READ_PIXELS_COLOR_BUFFER, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); for (y = 0; y < height; y++) for (x = 0; x < width; x++) { uint8_t *pixel = &data[y*width*4 + x*4]; #if 1 g_assert (pixel[RED] == red && pixel[GREEN] == green && pixel[BLUE] == blue && pixel[ALPHA] == alpha); #endif } g_free (data); } static void assert_rectangle_color_and_black_border (int x, int y, int width, int height, uint8_t red, uint8_t green, uint8_t blue) { /* check the rectangle itself... */ assert_region_color (x, y, width, height, red, green, blue, 0xff); /* black to left of the rectangle */ assert_region_color (x-10, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); /* black to right of the rectangle */ assert_region_color (x+width, y-10, 10, height+20, 0x00, 0x00, 0x00, 0xff); /* black above the rectangle */ assert_region_color (x-10, y-10, width+20, 10, 0x00, 0x00, 0x00, 0xff); /* and black below the rectangle */ assert_region_color (x-10, y+height, width+20, 10, 0x00, 0x00, 0x00, 0xff); } static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, void *state) { float saved_viewport[4]; CoglMatrix saved_projection; CoglMatrix projection; CoglMatrix modelview; guchar *data; CoglHandle tex; CoglHandle offscreen; CoglColor black; float x0; float y0; float width; float height; /* for clearing the offscreen framebuffer to black... */ cogl_color_init_from_4ub (&black, 0x00, 0x00, 0x00, 0xff); cogl_get_viewport (saved_viewport); cogl_get_projection_matrix (&saved_projection); cogl_push_matrix (); cogl_matrix_init_identity (&projection); cogl_matrix_init_identity (&modelview); cogl_set_projection_matrix (&projection); cogl_set_modelview_matrix (&modelview); /* - Create a 100x200 viewport (i.e. smaller than the onscreen framebuffer) * and position it a (20, 10) inside the framebuffer. * - Fill the whole viewport with a purple rectangle * - Verify that the framebuffer is black with a 100x200 purple rectangle at * (20, 10) */ cogl_set_viewport (20, /* x */ 10, /* y */ 100, /* width */ 200); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* fill the viewport with purple.. */ cogl_set_source_color4ub (0xff, 0x00, 0xff, 0xff); cogl_rectangle (-1, 1, 1, -1); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0xff, 0x00, 0xff); /* - Create a viewport twice the size of the onscreen framebuffer with * a negative offset positioning it at (-20, -10) relative to the * buffer itself. * - Draw a 100x200 green rectangle at (40, 20) within the viewport (which * is (20, 10) within the framebuffer) * - Verify that the framebuffer is black with a 100x200 green rectangle at * (20, 10) */ cogl_set_viewport (-20, /* x */ -10, /* y */ FRAMEBUFFER_WIDTH * 2, /* width */ FRAMEBUFFER_HEIGHT * 2); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* draw a 100x200 green rectangle offset into the viewport such that its * top left corner should be found at (20, 10) in the offscreen buffer */ /* (offset 40 pixels right from the left of the viewport) */ x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f; /* (offset 20 pixels down from the top of the viewport) */ y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f; width = (1.0f / FRAMEBUFFER_WIDTH) * 100; height = (1.0f / FRAMEBUFFER_HEIGHT) * 200; cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); cogl_rectangle (x0, y0, x0 + width, y0 - height); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0x00, 0xff, 0x00); /* - Create a 200x400 viewport and position it a (20, 10) inside the draw * buffer. * - Push a 100x200 window space clip rectangle at (20, 10) * - Fill the whole viewport with a blue rectangle * - Verify that the framebuffer is black with a 100x200 blue rectangle at * (20, 10) */ cogl_set_viewport (20, /* x */ 10, /* y */ 200, /* width */ 400); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); cogl_clip_push_window_rectangle (20, 10, 100, 200); /* fill the viewport with blue.. */ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); cogl_rectangle (-1, 1, 1, -1); cogl_clip_pop (); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0x00, 0x00, 0xff); /* - Create a 200x400 viewport and position it a (20, 10) inside the draw * buffer. * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport * (i.e. (40, 20) inside the framebuffer) * - Fill the whole viewport with a green rectangle * - Verify that the framebuffer is black with a 100x200 green rectangle at * (40, 20) */ cogl_set_viewport (20, /* x */ 10, /* y */ 200, /* width */ 400); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* figure out where to position our clip rectangle in model space * coordinates... */ /* (offset 40 pixels right from the left of the viewport) */ x0 = -1.0f + (2.0f / 200) * 20.f; /* (offset 20 pixels down from the top of the viewport) */ y0 = 1.0f - (2.0f / 400) * 10.0f; width = (2.0f / 200) * 100; height = (2.0f / 400) * 200; /* add the clip rectangle... */ cogl_push_matrix (); cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); /* XXX: Rotate just enough to stop Cogl from converting our model space * rectangle into a window space rectangle.. */ cogl_rotate (0.1, 0, 0, 1); cogl_clip_push_rectangle (-(width/2.0), -(height/2.0), width/2.0, height/2.0); cogl_pop_matrix (); /* fill the viewport with green.. */ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); cogl_rectangle (-1, 1, 1, -1); cogl_clip_pop (); assert_rectangle_color_and_black_border (40, 20, 100, 200, 0x00, 0xff, 0x00); /* Set the viewport to something specific so we can verify that it gets * restored after we are done testing with an offscreen framebuffer... */ cogl_set_viewport (20, 10, 100, 200); /* * Next test offscreen drawing... */ data = g_malloc (FRAMEBUFFER_WIDTH * 4 * FRAMEBUFFER_HEIGHT); tex = test_utils_texture_new_from_data (FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT, TEST_UTILS_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888, /* data fmt */ COGL_PIXEL_FORMAT_ANY, /* internal fmt */ FRAMEBUFFER_WIDTH * 4, /* rowstride */ data); g_free (data); offscreen = cogl_offscreen_new_with_texture (tex); cogl_push_framebuffer (offscreen); /* - Create a 100x200 viewport (i.e. smaller than the offscreen framebuffer) * and position it a (20, 10) inside the framebuffer. * - Fill the whole viewport with a blue rectangle * - Verify that the framebuffer is black with a 100x200 blue rectangle at * (20, 10) */ cogl_set_viewport (20, /* x */ 10, /* y */ 100, /* width */ 200); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* fill the viewport with blue.. */ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); cogl_rectangle (-1, 1, 1, -1); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0x00, 0x00, 0xff); /* - Create a viewport twice the size of the offscreen framebuffer with * a negative offset positioning it at (-20, -10) relative to the * buffer itself. * - Draw a 100x200 red rectangle at (40, 20) within the viewport (which * is (20, 10) within the framebuffer) * - Verify that the framebuffer is black with a 100x200 red rectangle at * (20, 10) */ cogl_set_viewport (-20, /* x */ -10, /* y */ FRAMEBUFFER_WIDTH * 2, /* width */ FRAMEBUFFER_HEIGHT * 2); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* draw a 100x200 red rectangle offset into the viewport such that its * top left corner should be found at (20, 10) in the offscreen buffer */ /* (offset 40 pixels right from the left of the viewport) */ x0 = -1.0f + (1.0f / FRAMEBUFFER_WIDTH) * 40.f; /* (offset 20 pixels down from the top of the viewport) */ y0 = 1.0f - (1.0f / FRAMEBUFFER_HEIGHT) * 20.0f; width = (1.0f / FRAMEBUFFER_WIDTH) * 100; height = (1.0f / FRAMEBUFFER_HEIGHT) * 200; cogl_set_source_color4ub (0xff, 0x00, 0x00, 0xff); cogl_rectangle (x0, y0, x0 + width, y0 - height); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0xff, 0x00, 0x00); /* - Create a 200x400 viewport and position it a (20, 10) inside the draw * buffer. * - Push a 100x200 window space clip rectangle at (20, 10) * - Fill the whole viewport with a blue rectangle * - Verify that the framebuffer is black with a 100x200 blue rectangle at * (20, 10) */ cogl_set_viewport (20, /* x */ 10, /* y */ 200, /* width */ 400); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); cogl_clip_push_window_rectangle (20, 10, 100, 200); /* fill the viewport with blue.. */ cogl_set_source_color4ub (0x00, 0x00, 0xff, 0xff); cogl_rectangle (-1, 1, 1, -1); cogl_clip_pop (); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0x00, 0x00, 0xff); /* - Create a 200x400 viewport and position it a (20, 10) inside the draw * buffer. * - Push a 100x200 model space clip rectangle at (20, 10) in the viewport * (i.e. (40, 20) inside the framebuffer) * - Fill the whole viewport with a green rectangle * - Verify that the framebuffer is black with a 100x200 green rectangle at * (40, 20) */ cogl_set_viewport (20, /* x */ 10, /* y */ 200, /* width */ 400); /* height */ /* clear everything... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); /* figure out where to position our clip rectangle in model space * coordinates... */ /* (offset 40 pixels right from the left of the viewport) */ x0 = -1.0f + (2.0f / 200) * 20.f; /* (offset 20 pixels down from the top of the viewport) */ y0 = 1.0f - (2.0f / 400) * 10.0f; width = (2.0f / 200) * 100; height = (2.0f / 400) * 200; /* add the clip rectangle... */ cogl_push_matrix (); cogl_translate (x0 + (width/2.0), y0 - (height/2.0), 0); /* XXX: Rotate just enough to stop Cogl from converting our model space * rectangle into a window space rectangle.. */ cogl_rotate (0.1, 0, 0, 1); cogl_clip_push_rectangle (-(width/2.0), -(height/2.0), width/2, height/2); cogl_pop_matrix (); /* fill the viewport with green.. */ cogl_set_source_color4ub (0x00, 0xff, 0x00, 0xff); cogl_rectangle (-1, 1, 1, -1); cogl_clip_pop (); assert_rectangle_color_and_black_border (40, 20, 100, 200, 0x00, 0xff, 0x00); /* Set the viewport to something obscure to verify that it gets * replace when we switch back to the onscreen framebuffer... */ cogl_set_viewport (0, 0, 10, 10); cogl_pop_framebuffer (); cogl_object_unref (offscreen); /* * Verify that the previous onscreen framebuffer's viewport was restored * by drawing a white rectangle across the whole viewport. This should * draw a 100x200 rectangle at (20,10) relative to the onscreen draw * buffer... */ cogl_clear (&black, COGL_BUFFER_BIT_COLOR); cogl_set_source_color4ub (0xff, 0xff, 0xff, 0xff); cogl_rectangle (-1, 1, 1, -1); assert_rectangle_color_and_black_border (20, 10, 100, 200, 0xff, 0xff, 0xff); /* Uncomment to display the last contents of the offscreen framebuffer */ #if 1 cogl_matrix_init_identity (&projection); cogl_matrix_init_identity (&modelview); cogl_set_viewport (0, 0, FRAMEBUFFER_WIDTH, FRAMEBUFFER_HEIGHT); cogl_set_projection_matrix (&projection); cogl_set_modelview_matrix (&modelview); cogl_set_source_texture (tex); cogl_rectangle (-1, 1, 1, -1); #endif cogl_object_unref (tex); /* Finally restore the stage's original state... */ cogl_pop_matrix (); cogl_set_projection_matrix (&saved_projection); cogl_set_viewport (saved_viewport[0], saved_viewport[1], saved_viewport[2], saved_viewport[3]); /* Comment this out if you want visual feedback of what this test * paints. */ clutter_main_quit (); } static gboolean queue_redraw (void *stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } void test_viewport (TestUtilsGTestFixture *fixture, void *data) { unsigned int idle_source; ClutterActor *stage; stage = clutter_stage_get_default (); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); /* We force continuous redrawing of the stage, since we need to skip * the first few frames, and we wont be doing anything else that * will trigger redrawing. */ idle_source = g_idle_add (queue_redraw, stage); g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), NULL); clutter_actor_show (stage); clutter_main (); g_clear_handle_id (&idle_source, g_source_remove); /* Remove all of the actors from the stage */ clutter_actor_remove_all_children (stage); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-copy-replace-texture.c0000664000175000017500000000711414723361714022700 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" /* Keep track of the number of textures that we've created and are * still alive */ static int alive_texture_mask = 0; #define N_LAYERS 3 #define N_PIPELINES 4 #define PIPELINE_LAYER_MASK(pipeline_num) \ (((1 << N_LAYERS) - 1) << (N_LAYERS * (pipeline_num) + 1)) #define LAST_PIPELINE_MASK PIPELINE_LAYER_MASK (N_PIPELINES - 1) #define FIRST_PIPELINE_MASK PIPELINE_LAYER_MASK (0) static void free_texture_cb (void *user_data) { int texture_num = GPOINTER_TO_INT (user_data); alive_texture_mask &= ~(1 << texture_num); } static CoglTexture * create_texture (void) { static const guint8 data[] = { 0xff, 0xff, 0xff, 0xff }; static CoglUserDataKey texture_data_key; CoglTexture2D *tex_2d; static int texture_num = 1; alive_texture_mask |= (1 << texture_num); tex_2d = cogl_texture_2d_new_from_data (test_ctx, 1, 1, /* width / height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ data, NULL); /* Set some user data on the texture so we can track when it has * been destroyed */ cogl_object_set_user_data (COGL_OBJECT (tex_2d), &texture_data_key, GINT_TO_POINTER (texture_num), free_texture_cb); texture_num++; return tex_2d; } void test_copy_replace_texture (void) { CoglPipeline *pipelines[N_PIPELINES]; int pipeline_num; /* Create a set of pipeline copies each with three of their own * replacement textures */ for (pipeline_num = 0; pipeline_num < N_PIPELINES; pipeline_num++) { int layer_num; if (pipeline_num == 0) pipelines[pipeline_num] = cogl_pipeline_new (test_ctx); else pipelines[pipeline_num] = cogl_pipeline_copy (pipelines[pipeline_num - 1]); for (layer_num = 0; layer_num < N_LAYERS; layer_num++) { CoglTexture *tex = create_texture (); cogl_pipeline_set_layer_texture (pipelines[pipeline_num], layer_num, tex); cogl_object_unref (tex); } } /* Unref everything but the last pipeline */ for (pipeline_num = 0; pipeline_num < N_PIPELINES - 1; pipeline_num++) cogl_object_unref (pipelines[pipeline_num]); if (alive_texture_mask && cogl_test_verbose ()) { int i; g_print ("Alive textures:"); for (i = 0; i < N_PIPELINES * N_LAYERS; i++) if ((alive_texture_mask & (1 << (i + 1)))) g_print (" %i", i); g_print ("\n"); } /* Ideally there should only be the textures from the last pipeline * left alive. We also let Cogl keep the textures from the first * texture alive because currently the child of the third layer in * the first pipeline will retain its authority on the unit index * state so that it can set it to 2. If there are more textures then * it means the pipeline isn't correctly pruning redundant * ancestors */ g_assert_cmpint (alive_texture_mask & ~FIRST_PIPELINE_MASK, ==, LAST_PIPELINE_MASK); /* Clean up the last pipeline */ cogl_object_unref (pipelines[N_PIPELINES - 1]); /* That should get rid of the last of the textures */ g_assert_cmpint (alive_texture_mask, ==, 0); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-premult.c0000664000175000017500000002270714723361714020314 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" #define QUAD_WIDTH 32 #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 #define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) #define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) #define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) #define MASK_ALPHA(COLOR) (COLOR & 0xff) typedef enum _MakeTextureFlags { TEXTURE_FLAG_SET_PREMULTIPLIED = 1, TEXTURE_FLAG_SET_UNPREMULTIPLIED = 1<<1, } MakeTextureFlags; static guchar * gen_tex_data (uint32_t color) { guchar *tex_data, *p; uint8_t r = MASK_RED (color); uint8_t g = MASK_GREEN (color); uint8_t b = MASK_BLUE (color); uint8_t a = MASK_ALPHA (color); tex_data = g_malloc (QUAD_WIDTH * QUAD_WIDTH * 4); for (p = tex_data + QUAD_WIDTH * QUAD_WIDTH * 4; p > tex_data;) { *(--p) = a; *(--p) = b; *(--p) = g; *(--p) = r; } return tex_data; } static CoglTexture * make_texture (uint32_t color, CoglPixelFormat src_format, MakeTextureFlags flags) { CoglTexture2D *tex_2d; guchar *tex_data = gen_tex_data (color); CoglBitmap *bmp = cogl_bitmap_new_for_data (test_ctx, QUAD_WIDTH, QUAD_WIDTH, src_format, QUAD_WIDTH * 4, tex_data); tex_2d = cogl_texture_2d_new_from_bitmap (bmp); if (flags & TEXTURE_FLAG_SET_PREMULTIPLIED) cogl_texture_set_premultiplied (tex_2d, TRUE); else if (flags & TEXTURE_FLAG_SET_UNPREMULTIPLIED) cogl_texture_set_premultiplied (tex_2d, FALSE); cogl_object_unref (bmp); g_free (tex_data); return tex_2d; } static void set_region (CoglTexture *tex, uint32_t color, CoglPixelFormat format) { guchar *tex_data = gen_tex_data (color); cogl_texture_set_region (tex, 0, 0, /* src x, y */ 0, 0, /* dst x, y */ QUAD_WIDTH, QUAD_WIDTH, /* dst width, height */ QUAD_WIDTH, QUAD_WIDTH, /* src width, height */ format, 0, /* auto compute row stride */ tex_data); } static void check_texture (CoglPipeline *pipeline, int x, int y, CoglTexture *tex, uint32_t expected_result) { /* New API */ cogl_pipeline_set_layer_texture (pipeline, 0, tex); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); test_utils_check_pixel (test_fb, x * QUAD_WIDTH + QUAD_WIDTH / 2, y * QUAD_WIDTH + QUAD_WIDTH / 2, expected_result); } void test_premult (void) { CoglPipeline *pipeline; CoglTexture *tex; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1.0f, 1.0f, 1.0f, 1.0f); /* New API */ pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_blend (pipeline, "RGBA = ADD (SRC_COLOR, 0)", NULL); cogl_pipeline_set_layer_combine (pipeline, 0, "RGBA = REPLACE (TEXTURE)", NULL); /* If the user explicitly specifies an unmultiplied internal format then * Cogl shouldn't automatically premultiply the given texture data... */ if (cogl_test_verbose ()) g_print ("make_texture (0xff00ff80, " "src = RGBA_8888, internal = RGBA_8888)\n"); tex = make_texture (0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ TEXTURE_FLAG_SET_UNPREMULTIPLIED); check_texture (pipeline, 0, 0, /* position */ tex, 0xff00ff80); /* expected */ /* If the user explicitly requests a premultiplied internal format and * gives unmultiplied src data then Cogl should always premultiply that * for us */ if (cogl_test_verbose ()) g_print ("make_texture (0xff00ff80, " "src = RGBA_8888, internal = RGBA_8888_PRE)\n"); tex = make_texture (0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ TEXTURE_FLAG_SET_PREMULTIPLIED); check_texture (pipeline, 1, 0, /* position */ tex, 0x80008080); /* expected */ /* If the user doesn't explicitly declare that the texture is premultiplied * then Cogl should assume it is by default should premultiply * unpremultiplied texture data... */ if (cogl_test_verbose ()) g_print ("make_texture (0xff00ff80, " "src = RGBA_8888, internal = ANY)\n"); tex = make_texture (0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ 0); /* default premultiplied status */ check_texture (pipeline, 2, 0, /* position */ tex, 0x80008080); /* expected */ /* If the user requests a premultiplied internal texture format and supplies * premultiplied source data, Cogl should never modify that source data... */ if (cogl_test_verbose ()) g_print ("make_texture (0x80008080, " "src = RGBA_8888_PRE, " "internal = RGBA_8888_PRE)\n"); tex = make_texture (0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ TEXTURE_FLAG_SET_PREMULTIPLIED); check_texture (pipeline, 3, 0, /* position */ tex, 0x80008080); /* expected */ /* If the user requests an unmultiplied internal texture format, but * supplies premultiplied source data, then Cogl should always * un-premultiply the source data... */ if (cogl_test_verbose ()) g_print ("make_texture (0x80008080, " "src = RGBA_8888_PRE, internal = RGBA_8888)\n"); tex = make_texture (0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ TEXTURE_FLAG_SET_UNPREMULTIPLIED); check_texture (pipeline, 4, 0, /* position */ tex, 0xff00ff80); /* expected */ /* If the user allows any internal texture format and provides premultipled * source data then by default Cogl shouldn't modify the source data... * (In the future there will be additional Cogl API to control this * behaviour) */ if (cogl_test_verbose ()) g_print ("make_texture (0x80008080, " "src = RGBA_8888_PRE, internal = ANY)\n"); tex = make_texture (0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ 0); /* default premultiplied status */ check_texture (pipeline, 5, 0, /* position */ tex, 0x80008080); /* expected */ /* * Test cogl_texture_set_region() .... */ if (cogl_test_verbose ()) g_print ("make_texture (0xDEADBEEF, " "src = RGBA_8888, internal = RGBA_8888)\n"); tex = make_texture (0xDEADBEEF, COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ TEXTURE_FLAG_SET_UNPREMULTIPLIED); if (cogl_test_verbose ()) g_print ("set_region (0xff00ff80, RGBA_8888)\n"); set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888); check_texture (pipeline, 6, 0, /* position */ tex, 0xff00ff80); /* expected */ /* Updating a texture region for an unmultiplied texture using premultiplied * region data should result in Cogl unmultiplying the given region data... */ if (cogl_test_verbose ()) g_print ("make_texture (0xDEADBEEF, " "src = RGBA_8888, internal = RGBA_8888)\n"); tex = make_texture (0xDEADBEEF, COGL_PIXEL_FORMAT_RGBA_8888, /* src format */ TEXTURE_FLAG_SET_UNPREMULTIPLIED); if (cogl_test_verbose ()) g_print ("set_region (0x80008080, RGBA_8888_PRE)\n"); set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE); check_texture (pipeline, 7, 0, /* position */ tex, 0xff00ff80); /* expected */ if (cogl_test_verbose ()) g_print ("make_texture (0xDEADBEEF, " "src = RGBA_8888_PRE, " "internal = RGBA_8888_PRE)\n"); tex = make_texture (0xDEADBEEF, COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ TEXTURE_FLAG_SET_PREMULTIPLIED); if (cogl_test_verbose ()) g_print ("set_region (0x80008080, RGBA_8888_PRE)\n"); set_region (tex, 0x80008080, COGL_PIXEL_FORMAT_RGBA_8888_PRE); check_texture (pipeline, 8, 0, /* position */ tex, 0x80008080); /* expected */ /* Updating a texture region for a premultiplied texture using unmultiplied * region data should result in Cogl premultiplying the given region data... */ if (cogl_test_verbose ()) g_print ("make_texture (0xDEADBEEF, " "src = RGBA_8888_PRE, " "internal = RGBA_8888_PRE)\n"); tex = make_texture (0xDEADBEEF, COGL_PIXEL_FORMAT_RGBA_8888_PRE, /* src format */ TEXTURE_FLAG_SET_PREMULTIPLIED); if (cogl_test_verbose ()) g_print ("set_region (0xff00ff80, RGBA_8888)\n"); set_region (tex, 0xff00ff80, COGL_PIXEL_FORMAT_RGBA_8888); check_texture (pipeline, 9, 0, /* position */ tex, 0x80008080); /* expected */ if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-depth-test.c0000664000175000017500000002050114723361714020673 0ustar fabiofabio#define COGL_VERSION_MIN_REQUIRED COGL_VERSION_1_0 #include #include #include "test-declarations.h" #include "test-utils.h" #define QUAD_WIDTH 20 #define RED 0 #define GREEN 1 #define BLUE 2 #define ALPHA 3 #define MASK_RED(COLOR) ((COLOR & 0xff000000) >> 24) #define MASK_GREEN(COLOR) ((COLOR & 0xff0000) >> 16) #define MASK_BLUE(COLOR) ((COLOR & 0xff00) >> 8) #define MASK_ALPHA(COLOR) (COLOR & 0xff) typedef struct _TestState { int padding; } TestState; typedef struct { uint32_t color; float depth; gboolean test_enable; CoglDepthTestFunction test_function; gboolean write_enable; gboolean fb_write_enable; float range_near; float range_far; } TestDepthState; static gboolean draw_rectangle (TestState *state, int x, int y, TestDepthState *rect_state, gboolean legacy_mode) { uint8_t Cr = MASK_RED (rect_state->color); uint8_t Cg = MASK_GREEN (rect_state->color); uint8_t Cb = MASK_BLUE (rect_state->color); uint8_t Ca = MASK_ALPHA (rect_state->color); CoglPipeline *pipeline; CoglDepthState depth_state; cogl_depth_state_init (&depth_state); cogl_depth_state_set_test_enabled (&depth_state, rect_state->test_enable); cogl_depth_state_set_test_function (&depth_state, rect_state->test_function); cogl_depth_state_set_write_enabled (&depth_state, rect_state->write_enable); cogl_depth_state_set_range (&depth_state, rect_state->range_near, rect_state->range_far); pipeline = cogl_pipeline_new (test_ctx); if (!cogl_pipeline_set_depth_state (pipeline, &depth_state, NULL)) { cogl_object_unref (pipeline); return FALSE; } if (!legacy_mode) { cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca); cogl_framebuffer_set_depth_write_enabled (test_fb, rect_state->fb_write_enable); cogl_framebuffer_push_matrix (test_fb); cogl_framebuffer_translate (test_fb, 0, 0, rect_state->depth); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); cogl_framebuffer_pop_matrix (test_fb); } else { CoglPipeline *legacy_pipeline; legacy_pipeline = cogl_pipeline_new (test_ctx); cogl_framebuffer_push_matrix (test_fb); cogl_pipeline_set_color4ub (pipeline, Cr, Cg, Cb, Ca); cogl_framebuffer_translate (test_fb, 0, 0, rect_state->depth); cogl_framebuffer_draw_rectangle (test_fb, pipeline, x * QUAD_WIDTH, y * QUAD_WIDTH, x * QUAD_WIDTH + QUAD_WIDTH, y * QUAD_WIDTH + QUAD_WIDTH); cogl_framebuffer_pop_matrix (test_fb); cogl_object_unref (legacy_pipeline); } cogl_object_unref (pipeline); return TRUE; } static void test_depth (TestState *state, int x, int y, TestDepthState *rect0_state, TestDepthState *rect1_state, TestDepthState *rect2_state, gboolean legacy_mode, uint32_t expected_result) { gboolean missing_feature = FALSE; if (rect0_state) missing_feature |= !draw_rectangle (state, x, y, rect0_state, legacy_mode); if (rect1_state) missing_feature |= !draw_rectangle (state, x, y, rect1_state, legacy_mode); if (rect2_state) missing_feature |= !draw_rectangle (state, x, y, rect2_state, legacy_mode); /* We don't consider it an error that we can't test something * the driver doesn't support. */ if (missing_feature) return; test_utils_check_pixel (test_fb, x * QUAD_WIDTH + (QUAD_WIDTH / 2), y * QUAD_WIDTH + (QUAD_WIDTH / 2), expected_result); } static void paint (TestState *state) { /* Sanity check a few of the different depth test functions * and that depth writing can be disabled... */ { /* Closest */ TestDepthState rect0_state = { 0xff0000ff, /* rgba color */ -10, /* depth */ FALSE, /* depth test enable */ COGL_DEPTH_TEST_FUNCTION_ALWAYS, TRUE, /* depth write enable */ TRUE, /* FB depth write enable */ 0, 1 /* depth range */ }; /* Furthest */ TestDepthState rect1_state = { 0x00ff00ff, /* rgba color */ -70, /* depth */ TRUE, /* depth test enable */ COGL_DEPTH_TEST_FUNCTION_ALWAYS, TRUE, /* depth write enable */ TRUE, /* FB depth write enable */ 0, 1 /* depth range */ }; /* In the middle */ TestDepthState rect2_state = { 0x0000ffff, /* rgba color */ -20, /* depth */ TRUE, /* depth test enable */ COGL_DEPTH_TEST_FUNCTION_NEVER, TRUE, /* depth write enable */ TRUE, /* FB depth write enable */ 0, 1 /* depth range */ }; test_depth (state, 0, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x00ff00ff); /* expected */ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_ALWAYS; test_depth (state, 1, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x0000ffff); /* expected */ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_LESS; test_depth (state, 2, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x0000ffff); /* expected */ rect2_state.test_function = COGL_DEPTH_TEST_FUNCTION_GREATER; test_depth (state, 3, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x00ff00ff); /* expected */ rect0_state.test_enable = TRUE; rect1_state.write_enable = FALSE; test_depth (state, 4, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x0000ffff); /* expected */ rect1_state.write_enable = TRUE; rect1_state.fb_write_enable = FALSE; test_depth (state, 4, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x0000ffff); /* expected */ /* Re-enable FB depth writing to verify state flush */ rect1_state.write_enable = TRUE; rect1_state.fb_write_enable = TRUE; test_depth (state, 4, 0, /* position */ &rect0_state, &rect1_state, &rect2_state, FALSE, /* legacy mode */ 0x00ff00ff); /* expected */ } /* Check that the depth buffer values can be mapped into different * ranges... */ { /* Closest by depth, furthest by depth range */ TestDepthState rect0_state = { 0xff0000ff, /* rgba color */ -10, /* depth */ TRUE, /* depth test enable */ COGL_DEPTH_TEST_FUNCTION_ALWAYS, TRUE, /* depth write enable */ TRUE, /* FB depth write enable */ 0.5, 1 /* depth range */ }; /* Furthest by depth, nearest by depth range */ TestDepthState rect1_state = { 0x00ff00ff, /* rgba color */ -70, /* depth */ TRUE, /* depth test enable */ COGL_DEPTH_TEST_FUNCTION_GREATER, TRUE, /* depth write enable */ TRUE, /* FB depth write enable */ 0, 0.5 /* depth range */ }; test_depth (state, 0, 1, /* position */ &rect0_state, &rect1_state, NULL, FALSE, /* legacy mode */ 0xff0000ff); /* expected */ } } void test_depth_test (void) { TestState state; cogl_framebuffer_orthographic (test_fb, 0, 0, cogl_framebuffer_get_width (test_fb), cogl_framebuffer_get_height (test_fb), -1, 100); paint (&state); if (cogl_test_verbose ()) g_print ("OK\n"); } muffin-6.4.1/cogl/tests/conform/test-pipeline-shader-state.c0000664000175000017500000000601214723361714023002 0ustar fabiofabio#include #include #include "test-declarations.h" #include "test-utils.h" void test_pipeline_shader_state (void) { CoglOffscreen *offscreen; CoglFramebuffer *fb; CoglPipeline *base_pipeline; CoglPipeline *draw_pipeline; CoglTexture2D *tex; CoglSnippet *snippet; float width = cogl_framebuffer_get_width (test_fb); float height = cogl_framebuffer_get_height (test_fb); cogl_framebuffer_orthographic (test_fb, 0, 0, width, height, -1, 100); tex = cogl_texture_2d_new_with_size (test_ctx, 128, 128); offscreen = cogl_offscreen_new_with_texture (tex); fb = offscreen; cogl_framebuffer_clear4f (fb, COGL_BUFFER_BIT_COLOR, 0, 0, 0, 1); cogl_object_unref (offscreen); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR, 1, 1, 0, 1); /* Setup a template pipeline... */ base_pipeline = cogl_pipeline_new (test_ctx); cogl_pipeline_set_layer_texture (base_pipeline, 1, tex); cogl_pipeline_set_color4f (base_pipeline, 1, 0, 0, 1); /* Derive a pipeline from the template, making a change that affects * fragment processing but making sure not to affect vertex * processing... */ draw_pipeline = cogl_pipeline_copy (base_pipeline); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ "cogl_color_out = vec4 (0.0, 1.0, 0.1, 1.1);"); cogl_pipeline_add_snippet (draw_pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline, 0, 0, width, height); cogl_object_unref (draw_pipeline); cogl_framebuffer_finish (test_fb); /* At this point we should have provoked cogl to cache some vertex * shader state for the draw_pipeline with the base_pipeline because * none of the changes made to the draw_pipeline affected vertex * processing. (NB: cogl will cache shader state with the oldest * ancestor that the state is still valid for to maximize the chance * that it can be used with other derived pipelines) * * Now we make a change to the base_pipeline to make sure that this * cached vertex shader gets invalidated. */ cogl_pipeline_set_layer_texture (base_pipeline, 0, tex); /* Now we derive another pipeline from base_pipeline to verify that * it doesn't end up re-using the old cached state */ draw_pipeline = cogl_pipeline_copy (base_pipeline); snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, NULL, /* declarations */ "cogl_color_out = vec4 (0.0, 0.0, 1.1, 1.1);"); cogl_pipeline_add_snippet (draw_pipeline, snippet); cogl_object_unref (snippet); cogl_framebuffer_draw_rectangle (test_fb, draw_pipeline, 0, 0, width, height); cogl_object_unref (draw_pipeline); test_utils_check_region (test_fb, 0, 0, width, height, 0x0000ffff); } muffin-6.4.1/cogl/cogl-pango/0000775000175000017500000000000014723361714014714 5ustar fabiofabiomuffin-6.4.1/cogl/cogl-pango/meson.build0000664000175000017500000000406414723361714017062 0ustar fabiofabiocogl_pango_sources = [ 'cogl-pango-display-list.c', 'cogl-pango-display-list.h', 'cogl-pango-fontmap.c', 'cogl-pango-glyph-cache.c', 'cogl-pango-glyph-cache.h', 'cogl-pango-pipeline-cache.c', 'cogl-pango-pipeline-cache.h', 'cogl-pango-private.h', 'cogl-pango-render.c', ] cogl_pango_public_headers = [ 'cogl-pango.h', ] cogl_pango_deps = [ pango_dep, pangocairo_dep, libmutter_cogl_dep, ] libmutter_cogl_pango = shared_library('muffin-cogl-pango-' + libmutter_api_version, sources: [cogl_pango_sources, cogl_pango_public_headers], version: '0.0.0', soversion: 0, c_args: cogl_c_args, include_directories: [cogl_includepath, cogl_path_includepath], gnu_symbol_visibility: 'hidden', dependencies: [cogl_pango_deps], install_rpath: pkglibdir, install_dir: pkglibdir, install: true, ) libmutter_cogl_pango_dep = declare_dependency( link_with: libmutter_cogl_pango, ) if have_introspection libmutter_cogl_pango_gir = gnome.generate_gir(libmutter_cogl_pango, sources: cogl_pango_public_headers, nsversion: libmutter_api_version, namespace: 'CoglPango', symbol_prefix: 'cogl_pango', header: 'cogl-pango.h', includes: [ libmutter_cogl_gir[0], 'Pango-1.0', 'PangoCairo-1.0' ], dependencies: [ cogl_deps, pango_dep, libmutter_cogl_pango_dep, ], extra_args: introspection_args + [ '-UCOGL_COMPILATION', '-DG_LOG_DOMAIN="CoglPango"', ], install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true ) endif cogl_pango_includesubdir = join_paths(cogl_includesubdir, 'cogl-pango') install_headers(cogl_pango_public_headers, subdir: cogl_pango_includesubdir) pkg.generate(libmutter_cogl_pango, name: 'CoglPango', filebase: 'muffin-cogl-pango-' + libmutter_api_version, description: 'A text rendering for Cogl in mutter', subdirs: join_paths(pkgname, 'cogl'), requires: [cogl_pkg_deps, libmutter_cogl_name], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, ], install_dir: pcdir, ) muffin-6.4.1/cogl/cogl-pango/cogl-pango-glyph-cache.c0000664000175000017500000003247714723361714021305 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "cogl-config.h" #include #include "cogl-pango-glyph-cache.h" #include "cogl-pango-private.h" #include "cogl/cogl-atlas.h" #include "cogl/cogl-atlas-texture-private.h" typedef struct _CoglPangoGlyphCacheKey CoglPangoGlyphCacheKey; struct _CoglPangoGlyphCache { CoglContext *ctx; /* Hash table to quickly check whether a particular glyph in a particular font is already cached */ GHashTable *hash_table; /* List of CoglAtlases */ GSList *atlases; /* List of callbacks to invoke when an atlas is reorganized */ GHookList reorganize_callbacks; /* TRUE if we've ever stored a texture in the global atlas. This is used to make sure we only register one callback to listen for global atlas reorganizations */ gboolean using_global_atlas; /* True if some of the glyphs are dirty. This is used as an optimization in _cogl_pango_glyph_cache_set_dirty_glyphs to avoid iterating the hash table if we know none of them are dirty */ gboolean has_dirty_glyphs; /* Whether mipmapping is being used for this cache. This only affects whether we decide to put the glyph in the global atlas */ gboolean use_mipmapping; }; struct _CoglPangoGlyphCacheKey { PangoFont *font; PangoGlyph glyph; }; static void cogl_pango_glyph_cache_value_free (CoglPangoGlyphCacheValue *value) { if (value->texture) cogl_object_unref (value->texture); g_slice_free (CoglPangoGlyphCacheValue, value); } static void cogl_pango_glyph_cache_key_free (CoglPangoGlyphCacheKey *key) { g_object_unref (key->font); g_slice_free (CoglPangoGlyphCacheKey, key); } static unsigned int cogl_pango_glyph_cache_hash_func (const void *key) { const CoglPangoGlyphCacheKey *cache_key = (const CoglPangoGlyphCacheKey *) key; /* Generate a number affected by both the font and the glyph number. We can safely directly compare the pointers because the key holds a reference to the font so it is not possible that a different font will have the same memory address */ return GPOINTER_TO_UINT (cache_key->font) ^ cache_key->glyph; } static gboolean cogl_pango_glyph_cache_equal_func (const void *a, const void *b) { const CoglPangoGlyphCacheKey *key_a = (const CoglPangoGlyphCacheKey *) a; const CoglPangoGlyphCacheKey *key_b = (const CoglPangoGlyphCacheKey *) b; /* We can safely directly compare the pointers for the fonts because the key holds a reference to the font so it is not possible that a different font will have the same memory address */ return key_a->font == key_b->font && key_a->glyph == key_b->glyph; } CoglPangoGlyphCache * cogl_pango_glyph_cache_new (CoglContext *ctx, gboolean use_mipmapping) { CoglPangoGlyphCache *cache; cache = g_malloc (sizeof (CoglPangoGlyphCache)); /* Note: as a rule we don't take references to a CoglContext * internally since */ cache->ctx = ctx; cache->hash_table = g_hash_table_new_full (cogl_pango_glyph_cache_hash_func, cogl_pango_glyph_cache_equal_func, (GDestroyNotify) cogl_pango_glyph_cache_key_free, (GDestroyNotify) cogl_pango_glyph_cache_value_free); cache->atlases = NULL; g_hook_list_init (&cache->reorganize_callbacks, sizeof (GHook)); cache->has_dirty_glyphs = FALSE; cache->using_global_atlas = FALSE; cache->use_mipmapping = use_mipmapping; return cache; } static void cogl_pango_glyph_cache_reorganize_cb (void *user_data) { CoglPangoGlyphCache *cache = user_data; g_hook_list_invoke (&cache->reorganize_callbacks, FALSE); } void cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache) { g_slist_foreach (cache->atlases, (GFunc) cogl_object_unref, NULL); g_slist_free (cache->atlases); cache->atlases = NULL; cache->has_dirty_glyphs = FALSE; g_hash_table_remove_all (cache->hash_table); } void cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache) { if (cache->using_global_atlas) { _cogl_atlas_texture_remove_reorganize_callback ( cache->ctx, cogl_pango_glyph_cache_reorganize_cb, cache); } cogl_pango_glyph_cache_clear (cache); g_hash_table_unref (cache->hash_table); g_hook_list_clear (&cache->reorganize_callbacks); g_free (cache); } static void cogl_pango_glyph_cache_update_position_cb (void *user_data, CoglTexture *new_texture, const CoglRectangleMapEntry *rect) { CoglPangoGlyphCacheValue *value = user_data; float tex_width, tex_height; if (value->texture) cogl_object_unref (value->texture); value->texture = cogl_object_ref (new_texture); tex_width = cogl_texture_get_width (new_texture); tex_height = cogl_texture_get_height (new_texture); value->tx1 = rect->x / tex_width; value->ty1 = rect->y / tex_height; value->tx2 = (rect->x + value->draw_width) / tex_width; value->ty2 = (rect->y + value->draw_height) / tex_height; value->tx_pixel = rect->x; value->ty_pixel = rect->y; /* The glyph has changed position so it will need to be redrawn */ value->dirty = TRUE; } static gboolean cogl_pango_glyph_cache_add_to_global_atlas (CoglPangoGlyphCache *cache, PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { CoglAtlasTexture *texture; GError *ignore_error = NULL; if (COGL_DEBUG_ENABLED (COGL_DEBUG_DISABLE_SHARED_ATLAS)) return FALSE; /* If the cache is using mipmapping then we can't use the global atlas because it would just get migrated back out */ if (cache->use_mipmapping) return FALSE; texture = cogl_atlas_texture_new_with_size (cache->ctx, value->draw_width, value->draw_height); if (!cogl_texture_allocate (COGL_TEXTURE (texture), &ignore_error)) { g_error_free (ignore_error); return FALSE; } value->texture = COGL_TEXTURE (texture); value->tx1 = 0; value->ty1 = 0; value->tx2 = 1; value->ty2 = 1; value->tx_pixel = 0; value->ty_pixel = 0; /* The first time we store a texture in the global atlas we'll register for notifications when the global atlas is reorganized so we can forward the notification on as a glyph reorganization */ if (!cache->using_global_atlas) { _cogl_atlas_texture_add_reorganize_callback (cache->ctx, cogl_pango_glyph_cache_reorganize_cb, cache); cache->using_global_atlas = TRUE; } return TRUE; } static gboolean cogl_pango_glyph_cache_add_to_local_atlas (CoglPangoGlyphCache *cache, PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { CoglAtlas *atlas = NULL; GSList *l; /* Look for an atlas that can reserve the space */ for (l = cache->atlases; l; l = l->next) if (_cogl_atlas_reserve_space (l->data, value->draw_width + 1, value->draw_height + 1, value)) { atlas = l->data; break; } /* If we couldn't find one then start a new atlas */ if (atlas == NULL) { atlas = _cogl_atlas_new (COGL_PIXEL_FORMAT_A_8, COGL_ATLAS_CLEAR_TEXTURE | COGL_ATLAS_DISABLE_MIGRATION, cogl_pango_glyph_cache_update_position_cb); COGL_NOTE (ATLAS, "Created new atlas for glyphs: %p", atlas); /* If we still can't reserve space then something has gone seriously wrong so we'll just give up */ if (!_cogl_atlas_reserve_space (atlas, value->draw_width + 1, value->draw_height + 1, value)) { cogl_object_unref (atlas); return FALSE; } _cogl_atlas_add_reorganize_callback (atlas, cogl_pango_glyph_cache_reorganize_cb, NULL, cache); cache->atlases = g_slist_prepend (cache->atlases, atlas); } return TRUE; } CoglPangoGlyphCacheValue * cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, gboolean create, PangoFont *font, PangoGlyph glyph) { CoglPangoGlyphCacheKey lookup_key; CoglPangoGlyphCacheValue *value; lookup_key.font = font; lookup_key.glyph = glyph; value = g_hash_table_lookup (cache->hash_table, &lookup_key); if (create && value == NULL) { CoglPangoGlyphCacheKey *key; PangoRectangle ink_rect; value = g_slice_new (CoglPangoGlyphCacheValue); value->texture = NULL; pango_font_get_glyph_extents (font, glyph, &ink_rect, NULL); pango_extents_to_pixels (&ink_rect, NULL); value->draw_x = ink_rect.x; value->draw_y = ink_rect.y; value->draw_width = ink_rect.width; value->draw_height = ink_rect.height; /* If the glyph is zero-sized then we don't need to reserve any space for it and we can just avoid painting anything */ if (ink_rect.width < 1 || ink_rect.height < 1) value->dirty = FALSE; else { /* Try adding the glyph to the global atlas... */ if (!cogl_pango_glyph_cache_add_to_global_atlas (cache, font, glyph, value) && /* If it fails try the local atlas */ !cogl_pango_glyph_cache_add_to_local_atlas (cache, font, glyph, value)) { cogl_pango_glyph_cache_value_free (value); return NULL; } value->dirty = TRUE; cache->has_dirty_glyphs = TRUE; } key = g_slice_new (CoglPangoGlyphCacheKey); key->font = g_object_ref (font); key->glyph = glyph; g_hash_table_insert (cache->hash_table, key, value); } return value; } static void _cogl_pango_glyph_cache_set_dirty_glyphs_cb (void *key_ptr, void *value_ptr, void *user_data) { CoglPangoGlyphCacheKey *key = key_ptr; CoglPangoGlyphCacheValue *value = value_ptr; CoglPangoGlyphCacheDirtyFunc func = user_data; if (value->dirty) { func (key->font, key->glyph, value); value->dirty = FALSE; } } void _cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache, CoglPangoGlyphCacheDirtyFunc func) { /* If we know that there are no dirty glyphs then we can shortcut out early */ if (!cache->has_dirty_glyphs) return; g_hash_table_foreach (cache->hash_table, _cogl_pango_glyph_cache_set_dirty_glyphs_cb, func); cache->has_dirty_glyphs = FALSE; } void _cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache, GHookFunc func, void *user_data) { GHook *hook = g_hook_alloc (&cache->reorganize_callbacks); hook->func = func; hook->data = user_data; g_hook_prepend (&cache->reorganize_callbacks, hook); } void _cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache, GHookFunc func, void *user_data) { GHook *hook = g_hook_find_func_data (&cache->reorganize_callbacks, FALSE, func, user_data); if (hook) g_hook_destroy_link (&cache->reorganize_callbacks, hook); } muffin-6.4.1/cogl/cogl-pango/cogl-pango-private.h0000664000175000017500000000376614723361714020577 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts * Robert Bragg * Matthew Allum */ #ifndef __COGL_PANGO_PRIVATE_H__ #define __COGL_PANGO_PRIVATE_H__ #include "cogl-pango.h" G_BEGIN_DECLS PangoRenderer * _cogl_pango_renderer_new (CoglContext *context); void _cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer); void _cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer, gboolean value); gboolean _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer); CoglContext * _cogl_pango_font_map_get_cogl_context (CoglPangoFontMap *fm); PangoRenderer * _cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm); G_END_DECLS #endif /* __COGL_PANGO_PRIVATE_H__ */ muffin-6.4.1/cogl/cogl-pango/cogl-pango.h0000664000175000017500000001674714723361714017132 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Neil Roberts * Robert Bragg * Matthew Allum */ #ifndef __COGL_PANGO_H__ #define __COGL_PANGO_H__ #include #include #include /* XXX: Currently this header may be included both as an internal * header (within the cogl-pango implementation) and as a public * header. * * Since should not be included for internal use we * determine the current context and switch between including cogl.h * or specific internal cogl headers here... */ #ifndef COGL_COMPILATION #include #else #include "cogl/cogl-context.h" #include "cogl/cogl-macros.h" #endif G_BEGIN_DECLS /* It's too difficult to actually subclass the pango cairo font * map. Instead we just make a fake set of macros that actually just * directly use the original type */ #define COGL_PANGO_TYPE_FONT_MAP PANGO_TYPE_CAIRO_FONT_MAP #define COGL_PANGO_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_FONT_MAP, CoglPangoFontMap)) #define COGL_PANGO_IS_FONT_MAP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_FONT_MAP)) typedef PangoCairoFontMap CoglPangoFontMap; /** * cogl_pango_font_map_new: * * Creates a new font map. * * Return value: (transfer full): the newly created #PangoFontMap * * Since: 1.14 */ COGL_EXPORT PangoFontMap * cogl_pango_font_map_new (void); /** * cogl_pango_font_map_create_context: * @font_map: a #CoglPangoFontMap * * Create a #PangoContext for the given @font_map. * * Returns: (transfer full): the newly created context: free with g_object_unref(). */ COGL_EXPORT PangoContext * cogl_pango_font_map_create_context (CoglPangoFontMap *font_map); /** * cogl_pango_font_map_set_resolution: * @font_map: a #CoglPangoFontMap * @dpi: The resolution in "dots per inch". (Physical inches aren't * actually involved; the terminology is conventional.) * * Sets the resolution for the @font_map. This is a scale factor * between points specified in a #PangoFontDescription and Cogl units. * The default value is %96, meaning that a 10 point font will be 13 * units high. (10 * 96. / 72. = 13.3). * * Since: 1.14 */ COGL_EXPORT void cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map, double dpi); /** * cogl_pango_font_map_clear_glyph_cache: * @font_map: a #CoglPangoFontMap * * Clears the glyph cache for @font_map. * * Since: 1.0 */ COGL_EXPORT void cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *font_map); /** * cogl_pango_ensure_glyph_cache_for_layout: * @layout: A #PangoLayout * * This updates any internal glyph cache textures as necessary to be * able to render the given @layout. * * This api should be used to avoid mid-scene modifications of * glyph-cache textures which can lead to undefined rendering results. * * Since: 1.0 */ COGL_EXPORT void cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout); /** * cogl_pango_font_map_set_use_mipmapping: * @font_map: a #CoglPangoFontMap * @value: %TRUE to enable the use of mipmapping * * Sets whether the renderer for the passed font map should use * mipmapping when rendering a #PangoLayout. * * Since: 1.0 */ COGL_EXPORT void cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *font_map, gboolean value); /** * cogl_pango_font_map_get_use_mipmapping: * @font_map: a #CoglPangoFontMap * * Retrieves whether the #CoglPangoRenderer used by @font_map will use * mipmapping when rendering the glyphs. * * Return value: %TRUE if mipmapping is used, %FALSE otherwise. * * Since: 1.0 */ COGL_EXPORT gboolean cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *font_map); /** * cogl_pango_font_map_get_renderer: * @font_map: a #CoglPangoFontMap * * Retrieves the #CoglPangoRenderer for the passed @font_map. * * Return value: (transfer none): a #PangoRenderer * * Since: 1.0 */ COGL_EXPORT PangoRenderer * cogl_pango_font_map_get_renderer (CoglPangoFontMap *font_map); /** * cogl_pango_show_layout: (skip) * @framebuffer: A #CoglFramebuffer to draw too. * @layout: a #PangoLayout * @x: X coordinate to render the layout at * @y: Y coordinate to render the layout at * @color: color to use when rendering the layout * * Draws a solidly coloured @layout on the given @framebuffer at (@x, * @y) within the @framebuffer's current model-view coordinate * space. * * Since: 1.14 */ COGL_EXPORT void cogl_pango_show_layout (CoglFramebuffer *framebuffer, PangoLayout *layout, float x, float y, const CoglColor *color); /** * cogl_pango_show_layout_line: (skip) * @framebuffer: A #CoglFramebuffer to draw too. * @line: a #PangoLayoutLine * @x: X coordinate to render the line at * @y: Y coordinate to render the line at * @color: color to use when rendering the line * * Draws a solidly coloured @line on the given @framebuffer at (@x, * @y) within the @framebuffer's current model-view coordinate * space. * * Since: 1.14 */ COGL_EXPORT void cogl_pango_show_layout_line (CoglFramebuffer *framebuffer, PangoLayoutLine *line, float x, float y, const CoglColor *color); #define COGL_PANGO_TYPE_RENDERER (cogl_pango_renderer_get_type ()) #define COGL_PANGO_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRenderer)) #define COGL_PANGO_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass)) #define COGL_PANGO_IS_RENDERER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COGL_PANGO_TYPE_RENDERER)) #define COGL_PANGO_IS_RENDERER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), COGL_PANGO_TYPE_RENDERER)) #define COGL_PANGO_RENDERER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), COGL_PANGO_TYPE_RENDERER, CoglPangoRendererClass)) /* opaque types */ typedef struct _CoglPangoRenderer CoglPangoRenderer; typedef struct _CoglPangoRendererClass CoglPangoRendererClass; COGL_EXPORT GType cogl_pango_renderer_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __COGL_PANGO_H__ */ muffin-6.4.1/cogl/cogl-pango/cogl-pango-glyph-cache.h0000664000175000017500000000641514723361714021303 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_PANGO_GLYPH_CACHE_H__ #define __COGL_PANGO_GLYPH_CACHE_H__ #include #include #include "cogl/cogl-texture.h" G_BEGIN_DECLS typedef struct _CoglPangoGlyphCache CoglPangoGlyphCache; typedef struct _CoglPangoGlyphCacheValue CoglPangoGlyphCacheValue; struct _CoglPangoGlyphCacheValue { CoglTexture *texture; float tx1; float ty1; float tx2; float ty2; int tx_pixel; int ty_pixel; int draw_x; int draw_y; int draw_width; int draw_height; /* This will be set to TRUE when the glyph atlas is reorganized which means the glyph will need to be redrawn */ guint dirty : 1; /* Set to TRUE if the glyph has colors (eg. emoji) */ guint has_color : 1; }; typedef void (* CoglPangoGlyphCacheDirtyFunc) (PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value); COGL_EXPORT CoglPangoGlyphCache * cogl_pango_glyph_cache_new (CoglContext *ctx, gboolean use_mipmapping); COGL_EXPORT void cogl_pango_glyph_cache_free (CoglPangoGlyphCache *cache); COGL_EXPORT CoglPangoGlyphCacheValue * cogl_pango_glyph_cache_lookup (CoglPangoGlyphCache *cache, gboolean create, PangoFont *font, PangoGlyph glyph); COGL_EXPORT void cogl_pango_glyph_cache_clear (CoglPangoGlyphCache *cache); void _cogl_pango_glyph_cache_add_reorganize_callback (CoglPangoGlyphCache *cache, GHookFunc func, void *user_data); void _cogl_pango_glyph_cache_remove_reorganize_callback (CoglPangoGlyphCache *cache, GHookFunc func, void *user_data); void _cogl_pango_glyph_cache_set_dirty_glyphs (CoglPangoGlyphCache *cache, CoglPangoGlyphCacheDirtyFunc func); G_END_DECLS #endif /* __COGL_PANGO_GLYPH_CACHE_H__ */ muffin-6.4.1/cogl/cogl-pango/cogl-pango.symbols0000664000175000017500000000047314723361714020360 0ustar fabiofabiocogl_pango_ensure_glyph_cache_for_layout cogl_pango_font_map_clear_glyph_cache cogl_pango_font_map_create_context cogl_pango_font_map_get_renderer cogl_pango_font_map_get_use_mipmapping cogl_pango_font_map_new cogl_pango_font_map_set_resolution cogl_pango_font_map_set_use_mipmapping cogl_pango_renderer_get_type muffin-6.4.1/cogl/cogl-pango/cogl-pango-pipeline-cache.h0000664000175000017500000000422414723361714021761 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #ifndef __COGL_PANGO_PIPELINE_CACHE_H__ #define __COGL_PANGO_PIPELINE_CACHE_H__ #include #include "cogl/cogl-context-private.h" G_BEGIN_DECLS typedef struct _CoglPangoPipelineCache { CoglContext *ctx; GHashTable *hash_table; CoglPipeline *base_texture_alpha_pipeline; CoglPipeline *base_texture_rgba_pipeline; gboolean use_mipmapping; } CoglPangoPipelineCache; CoglPangoPipelineCache * _cogl_pango_pipeline_cache_new (CoglContext *ctx, gboolean use_mipmapping); /* Returns a pipeline that can be used to render glyphs in the given texture. The pipeline has a new reference so it is up to the caller to unref it */ CoglPipeline * _cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, CoglTexture *texture); void _cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache); G_END_DECLS #endif /* __COGL_PANGO_PIPELINE_CACHE_H__ */ muffin-6.4.1/cogl/cogl-pango/cogl-pango-render.c0000664000175000017500000007352014723361714020372 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * Copyright (C) 2012 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * Authors: * Neil Roberts * Robert Bragg * Matthew Allum */ #include "cogl-config.h" #ifndef PANGO_ENABLE_BACKEND #define PANGO_ENABLE_BACKEND 1 #endif #ifndef PANGO_UNKNOWN_GLYPH_WIDTH #define PANGO_UNKNOWN_GLYPH_WIDTH 10 #endif #ifndef PANGO_UNKNOWN_GLYPH_HEIGHT #define PANGO_UNKNOWN_GLYPH_HEIGHT 14 #endif #include #include #include #include #include #include "cogl/cogl-debug.h" #include "cogl/cogl-context-private.h" #include "cogl/cogl-texture-private.h" #include "cogl-pango-private.h" #include "cogl-pango-glyph-cache.h" #include "cogl-pango-display-list.h" enum { PROP_0, PROP_COGL_CONTEXT, PROP_LAST }; typedef struct { CoglPangoGlyphCache *glyph_cache; CoglPangoPipelineCache *pipeline_cache; } CoglPangoRendererCaches; struct _CoglPangoRenderer { PangoRenderer parent_instance; CoglContext *ctx; /* Two caches of glyphs as textures and their corresponding pipeline caches, one with mipmapped textures and one without */ CoglPangoRendererCaches no_mipmap_caches; CoglPangoRendererCaches mipmap_caches; gboolean use_mipmapping; /* The current display list that is being built */ CoglPangoDisplayList *display_list; }; struct _CoglPangoRendererClass { PangoRendererClass class_instance; }; typedef struct _CoglPangoLayoutQdata CoglPangoLayoutQdata; /* An instance of this struct gets attached to each PangoLayout to cache the VBO and to detect changes to the layout */ struct _CoglPangoLayoutQdata { CoglPangoRenderer *renderer; /* The cache of the geometry for the layout */ CoglPangoDisplayList *display_list; /* A reference to the first line of the layout. This is just used to detect changes */ PangoLayoutLine *first_line; /* Whether mipmapping was previously used to render this layout. We need to regenerate the display list if the mipmapping value is changed because it will be using a different set of textures */ gboolean mipmapping_used; }; static void _cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line); typedef struct { CoglPangoDisplayList *display_list; float x1, y1, x2, y2; } CoglPangoRendererSliceCbData; PangoRenderer * _cogl_pango_renderer_new (CoglContext *context) { return PANGO_RENDERER (g_object_new (COGL_PANGO_TYPE_RENDERER, "context", context, NULL)); } static void cogl_pango_renderer_slice_cb (CoglTexture *texture, const float *slice_coords, const float *virtual_coords, void *user_data) { CoglPangoRendererSliceCbData *data = user_data; /* Note: this assumes that there is only one slice containing the whole texture and it doesn't attempt to split up the vertex coordinates based on the virtual_coords */ _cogl_pango_display_list_add_texture (data->display_list, texture, data->x1, data->y1, data->x2, data->y2, slice_coords[0], slice_coords[1], slice_coords[2], slice_coords[3]); } static void cogl_pango_renderer_draw_glyph (CoglPangoRenderer *priv, CoglPangoGlyphCacheValue *cache_value, float x1, float y1) { CoglPangoRendererSliceCbData data; g_return_if_fail (priv->display_list != NULL); data.display_list = priv->display_list; data.x1 = x1; data.y1 = y1; data.x2 = x1 + (float) cache_value->draw_width; data.y2 = y1 + (float) cache_value->draw_height; /* We iterate the internal sub textures of the texture so that we can get a pointer to the base texture even if the texture is in the global atlas. That way the display list can recognise that the neighbouring glyphs are coming from the same atlas and bundle them together into a single VBO */ cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (cache_value->texture), cache_value->tx1, cache_value->ty1, cache_value->tx2, cache_value->ty2, COGL_PIPELINE_WRAP_MODE_REPEAT, COGL_PIPELINE_WRAP_MODE_REPEAT, cogl_pango_renderer_slice_cb, &data); } static void cogl_pango_renderer_dispose (GObject *object); static void cogl_pango_renderer_finalize (GObject *object); static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, PangoFont *font, PangoGlyphString *glyphs, int x, int y); static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, PangoRenderPart part, int x, int y, int width, int height); static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, PangoRenderPart part, double y1, double x11, double x21, double y2, double x12, double x22); G_DEFINE_TYPE (CoglPangoRenderer, cogl_pango_renderer, PANGO_TYPE_RENDERER); static void cogl_pango_renderer_init (CoglPangoRenderer *priv) { } static void _cogl_pango_renderer_constructed (GObject *gobject) { CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (gobject); CoglContext *ctx = renderer->ctx; renderer->no_mipmap_caches.pipeline_cache = _cogl_pango_pipeline_cache_new (ctx, FALSE); renderer->mipmap_caches.pipeline_cache = _cogl_pango_pipeline_cache_new (ctx, TRUE); renderer->no_mipmap_caches.glyph_cache = cogl_pango_glyph_cache_new (ctx, FALSE); renderer->mipmap_caches.glyph_cache = cogl_pango_glyph_cache_new (ctx, TRUE); _cogl_pango_renderer_set_use_mipmapping (renderer, FALSE); if (G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed) G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->constructed (gobject); } static void cogl_pango_renderer_set_property (GObject *object, unsigned int prop_id, const GValue *value, GParamSpec *pspec) { CoglPangoRenderer *renderer = COGL_PANGO_RENDERER (object); switch (prop_id) { case PROP_COGL_CONTEXT: renderer->ctx = g_value_get_pointer (value); cogl_object_ref (renderer->ctx); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void cogl_pango_renderer_class_init (CoglPangoRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); PangoRendererClass *renderer_class = PANGO_RENDERER_CLASS (klass); GParamSpec *pspec; object_class->set_property = cogl_pango_renderer_set_property; object_class->constructed = _cogl_pango_renderer_constructed; object_class->dispose = cogl_pango_renderer_dispose; object_class->finalize = cogl_pango_renderer_finalize; pspec = g_param_spec_pointer ("context", "Context", "The Cogl Context", G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_COGL_CONTEXT, pspec); renderer_class->draw_glyphs = cogl_pango_renderer_draw_glyphs; renderer_class->draw_rectangle = cogl_pango_renderer_draw_rectangle; renderer_class->draw_trapezoid = cogl_pango_renderer_draw_trapezoid; } static void cogl_pango_renderer_dispose (GObject *object) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object); if (priv->ctx) { cogl_object_unref (priv->ctx); priv->ctx = NULL; } } static void cogl_pango_renderer_finalize (GObject *object) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (object); cogl_pango_glyph_cache_free (priv->no_mipmap_caches.glyph_cache); cogl_pango_glyph_cache_free (priv->mipmap_caches.glyph_cache); _cogl_pango_pipeline_cache_free (priv->no_mipmap_caches.pipeline_cache); _cogl_pango_pipeline_cache_free (priv->mipmap_caches.pipeline_cache); G_OBJECT_CLASS (cogl_pango_renderer_parent_class)->finalize (object); } static CoglPangoRenderer * cogl_pango_get_renderer_from_context (PangoContext *context) { PangoFontMap *font_map; CoglPangoFontMap *cogl_font_map; PangoRenderer *renderer; font_map = pango_context_get_font_map (context); g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (font_map), NULL); cogl_font_map = COGL_PANGO_FONT_MAP (font_map); renderer = _cogl_pango_font_map_get_renderer (cogl_font_map); g_return_val_if_fail (COGL_PANGO_IS_RENDERER (renderer), NULL); return COGL_PANGO_RENDERER (renderer); } static GQuark cogl_pango_layout_get_qdata_key (void) { static GQuark key = 0; if (G_UNLIKELY (key == 0)) key = g_quark_from_static_string ("CoglPangoDisplayList"); return key; } static void cogl_pango_layout_qdata_forget_display_list (CoglPangoLayoutQdata *qdata) { if (qdata->display_list) { CoglPangoRendererCaches *caches = qdata->mipmapping_used ? &qdata->renderer->mipmap_caches : &qdata->renderer->no_mipmap_caches; _cogl_pango_glyph_cache_remove_reorganize_callback (caches->glyph_cache, (GHookFunc) cogl_pango_layout_qdata_forget_display_list, qdata); _cogl_pango_display_list_free (qdata->display_list); qdata->display_list = NULL; } } static void cogl_pango_render_qdata_destroy (CoglPangoLayoutQdata *qdata) { cogl_pango_layout_qdata_forget_display_list (qdata); if (qdata->first_line) pango_layout_line_unref (qdata->first_line); g_slice_free (CoglPangoLayoutQdata, qdata); } void cogl_pango_show_layout (CoglFramebuffer *fb, PangoLayout *layout, float x, float y, const CoglColor *color) { PangoContext *context; CoglPangoRenderer *priv; CoglPangoLayoutQdata *qdata; context = pango_layout_get_context (layout); priv = cogl_pango_get_renderer_from_context (context); if (G_UNLIKELY (!priv)) return; qdata = g_object_get_qdata (G_OBJECT (layout), cogl_pango_layout_get_qdata_key ()); if (qdata == NULL) { qdata = g_slice_new0 (CoglPangoLayoutQdata); qdata->renderer = priv; g_object_set_qdata_full (G_OBJECT (layout), cogl_pango_layout_get_qdata_key (), qdata, (GDestroyNotify) cogl_pango_render_qdata_destroy); } /* Check if the layout has changed since the last build of the display list. This trick was suggested by Behdad Esfahbod here: http://mail.gnome.org/archives/gtk-i18n-list/2009-May/msg00019.html */ if (qdata->display_list && ((qdata->first_line && qdata->first_line->layout != layout) || qdata->mipmapping_used != priv->use_mipmapping)) cogl_pango_layout_qdata_forget_display_list (qdata); if (qdata->display_list == NULL) { CoglPangoRendererCaches *caches = priv->use_mipmapping ? &priv->mipmap_caches : &priv->no_mipmap_caches; cogl_pango_ensure_glyph_cache_for_layout (layout); qdata->display_list = _cogl_pango_display_list_new (caches->pipeline_cache); /* Register for notification of when the glyph cache changes so we can rebuild the display list */ _cogl_pango_glyph_cache_add_reorganize_callback (caches->glyph_cache, (GHookFunc) cogl_pango_layout_qdata_forget_display_list, qdata); priv->display_list = qdata->display_list; pango_renderer_draw_layout (PANGO_RENDERER (priv), layout, 0, 0); priv->display_list = NULL; qdata->mipmapping_used = priv->use_mipmapping; } cogl_framebuffer_push_matrix (fb); cogl_framebuffer_translate (fb, x, y, 0); _cogl_pango_display_list_render (fb, qdata->display_list, color); cogl_framebuffer_pop_matrix (fb); /* Keep a reference to the first line of the layout so we can detect changes */ if (qdata->first_line) { pango_layout_line_unref (qdata->first_line); qdata->first_line = NULL; } if (pango_layout_get_line_count (layout) > 0) { qdata->first_line = pango_layout_get_line (layout, 0); pango_layout_line_ref (qdata->first_line); } } void cogl_pango_show_layout_line (CoglFramebuffer *fb, PangoLayoutLine *line, float x, float y, const CoglColor *color) { PangoContext *context; CoglPangoRenderer *priv; CoglPangoRendererCaches *caches; int pango_x = x * PANGO_SCALE; int pango_y = y * PANGO_SCALE; context = pango_layout_get_context (line->layout); priv = cogl_pango_get_renderer_from_context (context); if (G_UNLIKELY (!priv)) return; caches = (priv->use_mipmapping ? &priv->mipmap_caches : &priv->no_mipmap_caches); priv->display_list = _cogl_pango_display_list_new (caches->pipeline_cache); _cogl_pango_ensure_glyph_cache_for_layout_line (line); pango_renderer_draw_layout_line (PANGO_RENDERER (priv), line, pango_x, pango_y); _cogl_pango_display_list_render (fb, priv->display_list, color); _cogl_pango_display_list_free (priv->display_list); priv->display_list = NULL; } void _cogl_pango_renderer_clear_glyph_cache (CoglPangoRenderer *renderer) { cogl_pango_glyph_cache_clear (renderer->mipmap_caches.glyph_cache); cogl_pango_glyph_cache_clear (renderer->no_mipmap_caches.glyph_cache); } void _cogl_pango_renderer_set_use_mipmapping (CoglPangoRenderer *renderer, gboolean value) { renderer->use_mipmapping = value; } gboolean _cogl_pango_renderer_get_use_mipmapping (CoglPangoRenderer *renderer) { return renderer->use_mipmapping; } static CoglPangoGlyphCacheValue * cogl_pango_renderer_get_cached_glyph (PangoRenderer *renderer, gboolean create, PangoFont *font, PangoGlyph glyph) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); CoglPangoRendererCaches *caches = (priv->use_mipmapping ? &priv->mipmap_caches : &priv->no_mipmap_caches); return cogl_pango_glyph_cache_lookup (caches->glyph_cache, create, font, glyph); } static gboolean font_has_color_glyphs (const PangoFont *font) { cairo_scaled_font_t *scaled_font; gboolean has_color = FALSE; scaled_font = pango_cairo_font_get_scaled_font ((PangoCairoFont *) font); if (cairo_scaled_font_get_type (scaled_font) == CAIRO_FONT_TYPE_FT) { FT_Face ft_face = cairo_ft_scaled_font_lock_face (scaled_font); has_color = (FT_HAS_COLOR (ft_face) != 0); cairo_ft_scaled_font_unlock_face (scaled_font); } return has_color; } static void cogl_pango_renderer_set_dirty_glyph (PangoFont *font, PangoGlyph glyph, CoglPangoGlyphCacheValue *value) { cairo_surface_t *surface; cairo_t *cr; cairo_scaled_font_t *scaled_font; cairo_glyph_t cairo_glyph; cairo_format_t format_cairo; CoglPixelFormat format_cogl; COGL_NOTE (PANGO, "redrawing glyph %i", glyph); /* Glyphs that don't take up any space will end up without a texture. These should never become dirty so they shouldn't end up here */ g_return_if_fail (value->texture != NULL); if (_cogl_texture_get_format (value->texture) == COGL_PIXEL_FORMAT_A_8) { format_cairo = CAIRO_FORMAT_A8; format_cogl = COGL_PIXEL_FORMAT_A_8; } else { format_cairo = CAIRO_FORMAT_ARGB32; /* Cairo stores the data in native byte order as ARGB but Cogl's pixel formats specify the actual byte order. Therefore we need to use a different format depending on the architecture */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN format_cogl = COGL_PIXEL_FORMAT_BGRA_8888_PRE; #else format_cogl = COGL_PIXEL_FORMAT_ARGB_8888_PRE; #endif } surface = cairo_image_surface_create (format_cairo, value->draw_width, value->draw_height); cr = cairo_create (surface); scaled_font = pango_cairo_font_get_scaled_font (PANGO_CAIRO_FONT (font)); cairo_set_scaled_font (cr, scaled_font); cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); cairo_glyph.x = -value->draw_x; cairo_glyph.y = -value->draw_y; /* The PangoCairo glyph numbers directly map to Cairo glyph numbers */ cairo_glyph.index = glyph; cairo_show_glyphs (cr, &cairo_glyph, 1); cairo_destroy (cr); cairo_surface_flush (surface); /* Copy the glyph to the texture */ cogl_texture_set_region (value->texture, 0, /* src_x */ 0, /* src_y */ value->tx_pixel, /* dst_x */ value->ty_pixel, /* dst_y */ value->draw_width, /* dst_width */ value->draw_height, /* dst_height */ value->draw_width, /* width */ value->draw_height, /* height */ format_cogl, cairo_image_surface_get_stride (surface), cairo_image_surface_get_data (surface)); cairo_surface_destroy (surface); value->has_color = font_has_color_glyphs (font); } static void _cogl_pango_ensure_glyph_cache_for_layout_line_internal (PangoLayoutLine *line) { PangoContext *context; PangoRenderer *renderer; GSList *l; context = pango_layout_get_context (line->layout); renderer = PANGO_RENDERER (cogl_pango_get_renderer_from_context (context)); for (l = line->runs; l; l = l->next) { PangoLayoutRun *run = l->data; PangoGlyphString *glyphs = run->glyphs; int i; for (i = 0; i < glyphs->num_glyphs; i++) { PangoGlyphInfo *gi = &glyphs->glyphs[i]; /* If the glyph isn't cached then this will reserve space for it now. We won't actually draw the glyph yet because reserving space could cause all of the other glyphs to be moved so we might as well redraw them all later once we know that the position is settled */ cogl_pango_renderer_get_cached_glyph (renderer, TRUE, run->item->analysis.font, gi->glyph); } } } static void _cogl_pango_set_dirty_glyphs (CoglPangoRenderer *priv) { _cogl_pango_glyph_cache_set_dirty_glyphs (priv->mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph); _cogl_pango_glyph_cache_set_dirty_glyphs (priv->no_mipmap_caches.glyph_cache, cogl_pango_renderer_set_dirty_glyph); } static void _cogl_pango_ensure_glyph_cache_for_layout_line (PangoLayoutLine *line) { PangoContext *context; CoglPangoRenderer *priv; context = pango_layout_get_context (line->layout); priv = cogl_pango_get_renderer_from_context (context); _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line); /* Now that we know all of the positions are settled we'll fill in any dirty glyphs */ _cogl_pango_set_dirty_glyphs (priv); } void cogl_pango_ensure_glyph_cache_for_layout (PangoLayout *layout) { PangoContext *context; CoglPangoRenderer *priv; PangoLayoutIter *iter; context = pango_layout_get_context (layout); priv = cogl_pango_get_renderer_from_context (context); g_return_if_fail (PANGO_IS_LAYOUT (layout)); if ((iter = pango_layout_get_iter (layout)) == NULL) return; do { PangoLayoutLine *line; line = pango_layout_iter_get_line_readonly (iter); _cogl_pango_ensure_glyph_cache_for_layout_line_internal (line); } while (pango_layout_iter_next_line (iter)); pango_layout_iter_free (iter); /* Now that we know all of the positions are settled we'll fill in any dirty glyphs */ _cogl_pango_set_dirty_glyphs (priv); } static void cogl_pango_renderer_set_color_for_part (PangoRenderer *renderer, PangoRenderPart part) { PangoColor *pango_color = pango_renderer_get_color (renderer, part); uint16_t alpha = pango_renderer_get_alpha (renderer, part); CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); if (pango_color) { CoglColor color; cogl_color_init_from_4ub (&color, pango_color->red >> 8, pango_color->green >> 8, pango_color->blue >> 8, alpha ? alpha >> 8 : 0xff); _cogl_pango_display_list_set_color_override (priv->display_list, &color); } else _cogl_pango_display_list_remove_color_override (priv->display_list); } static void cogl_pango_renderer_draw_box (PangoRenderer *renderer, int x, int y, int width, int height) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); g_return_if_fail (priv->display_list != NULL); _cogl_pango_display_list_add_rectangle (priv->display_list, x, y - height, x + width, y); } static void cogl_pango_renderer_get_device_units (PangoRenderer *renderer, int xin, int yin, float *xout, float *yout) { const PangoMatrix *matrix; if ((matrix = pango_renderer_get_matrix (renderer))) { /* Convert user-space coords to device coords */ *xout = ((xin * matrix->xx + yin * matrix->xy) / PANGO_SCALE + matrix->x0); *yout = ((yin * matrix->yy + xin * matrix->yx) / PANGO_SCALE + matrix->y0); } else { *xout = PANGO_PIXELS (xin); *yout = PANGO_PIXELS (yin); } } static void cogl_pango_renderer_draw_rectangle (PangoRenderer *renderer, PangoRenderPart part, int x, int y, int width, int height) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); float x1, x2, y1, y2; g_return_if_fail (priv->display_list != NULL); cogl_pango_renderer_set_color_for_part (renderer, part); cogl_pango_renderer_get_device_units (renderer, x, y, &x1, &y1); cogl_pango_renderer_get_device_units (renderer, x + width, y + height, &x2, &y2); _cogl_pango_display_list_add_rectangle (priv->display_list, x1, y1, x2, y2); } static void cogl_pango_renderer_draw_trapezoid (PangoRenderer *renderer, PangoRenderPart part, double y1, double x11, double x21, double y2, double x12, double x22) { CoglPangoRenderer *priv = COGL_PANGO_RENDERER (renderer); g_return_if_fail (priv->display_list != NULL); cogl_pango_renderer_set_color_for_part (renderer, part); _cogl_pango_display_list_add_trapezoid (priv->display_list, y1, x11, x21, y2, x12, x22); } static void cogl_pango_renderer_draw_glyphs (PangoRenderer *renderer, PangoFont *font, PangoGlyphString *glyphs, int xi, int yi) { CoglPangoRenderer *priv = (CoglPangoRenderer *) renderer; CoglPangoGlyphCacheValue *cache_value; int i; for (i = 0; i < glyphs->num_glyphs; i++) { PangoGlyphInfo *gi = glyphs->glyphs + i; float x, y; cogl_pango_renderer_set_color_for_part (renderer, PANGO_RENDER_PART_FOREGROUND); cogl_pango_renderer_get_device_units (renderer, xi + gi->geometry.x_offset, yi + gi->geometry.y_offset, &x, &y); if ((gi->glyph & PANGO_GLYPH_UNKNOWN_FLAG)) { if (font == NULL) { cogl_pango_renderer_draw_box (renderer, x, y, PANGO_UNKNOWN_GLYPH_WIDTH, PANGO_UNKNOWN_GLYPH_HEIGHT); } else { PangoRectangle ink_rect; pango_font_get_glyph_extents (font, gi->glyph, &ink_rect, NULL); pango_extents_to_pixels (&ink_rect, NULL); cogl_pango_renderer_draw_box (renderer, x + ink_rect.x, y + ink_rect.y + ink_rect.height, ink_rect.width, ink_rect.height); } } else { /* Get the texture containing the glyph */ cache_value = cogl_pango_renderer_get_cached_glyph (renderer, FALSE, font, gi->glyph); /* cogl_pango_ensure_glyph_cache_for_layout should always be called before rendering a layout so we should never have a dirty glyph here */ g_assert (cache_value == NULL || !cache_value->dirty); if (cache_value == NULL) { cogl_pango_renderer_draw_box (renderer, x, y, PANGO_UNKNOWN_GLYPH_WIDTH, PANGO_UNKNOWN_GLYPH_HEIGHT); } else if (cache_value->texture) { x += (float)(cache_value->draw_x); y += (float)(cache_value->draw_y); /* Do not override color if the glyph/font provide its own */ if (cache_value->has_color) { CoglColor color; uint16_t alpha; alpha = pango_renderer_get_alpha (renderer, PANGO_RENDER_PART_FOREGROUND); cogl_color_init_from_4ub (&color, 0xff, 0xff, 0xff, alpha ? alpha >> 8 : 0xff); _cogl_pango_display_list_set_color_override (priv->display_list, &color); } cogl_pango_renderer_draw_glyph (priv, cache_value, x, y); } } xi += gi->geometry.width; } } muffin-6.4.1/cogl/cogl-pango/cogl-pango-pipeline-cache.c0000664000175000017500000001612614723361714021760 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2011 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * * * Authors: * Neil Roberts */ #include "cogl-config.h" #include #include "cogl-pango-pipeline-cache.h" #include "cogl/cogl-context-private.h" #include "cogl/cogl-texture-private.h" typedef struct _CoglPangoPipelineCacheEntry CoglPangoPipelineCacheEntry; struct _CoglPangoPipelineCacheEntry { /* This will take a reference or it can be NULL to represent the pipeline used to render colors */ CoglTexture *texture; /* This will only take a weak reference */ CoglPipeline *pipeline; }; static void _cogl_pango_pipeline_cache_key_destroy (void *data) { if (data) cogl_object_unref (data); } static void _cogl_pango_pipeline_cache_value_destroy (void *data) { CoglPangoPipelineCacheEntry *cache_entry = data; if (cache_entry->texture) cogl_object_unref (cache_entry->texture); /* We don't need to unref the pipeline because it only takes a weak reference */ g_slice_free (CoglPangoPipelineCacheEntry, cache_entry); } CoglPangoPipelineCache * _cogl_pango_pipeline_cache_new (CoglContext *ctx, gboolean use_mipmapping) { CoglPangoPipelineCache *cache = g_new (CoglPangoPipelineCache, 1); cache->ctx = cogl_object_ref (ctx); /* The key is the pipeline pointer. A reference is taken when the pipeline is used as a key so we should unref it again in the destroy function */ cache->hash_table = g_hash_table_new_full (g_direct_hash, g_direct_equal, _cogl_pango_pipeline_cache_key_destroy, _cogl_pango_pipeline_cache_value_destroy); cache->base_texture_rgba_pipeline = NULL; cache->base_texture_alpha_pipeline = NULL; cache->use_mipmapping = use_mipmapping; return cache; } static CoglPipeline * get_base_texture_rgba_pipeline (CoglPangoPipelineCache *cache) { if (cache->base_texture_rgba_pipeline == NULL) { CoglPipeline *pipeline; pipeline = cache->base_texture_rgba_pipeline = cogl_pipeline_new (cache->ctx); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); if (cache->use_mipmapping) cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, COGL_PIPELINE_FILTER_LINEAR); } return cache->base_texture_rgba_pipeline; } static CoglPipeline * get_base_texture_alpha_pipeline (CoglPangoPipelineCache *cache) { if (cache->base_texture_alpha_pipeline == NULL) { CoglPipeline *pipeline; pipeline = cogl_pipeline_copy (get_base_texture_rgba_pipeline (cache)); cache->base_texture_alpha_pipeline = pipeline; /* The default combine mode of materials is to modulate (A x B) * the texture RGBA channels with the RGBA channels of the * previous layer (which in our case is just the font color) * * Since the RGB for an alpha texture is defined as 0, this gives us: * * result.rgb = color.rgb * 0 * result.a = color.a * texture.a * * What we want is premultiplied rgba values: * * result.rgba = color.rgb * texture.a * result.a = color.a * texture.a */ cogl_pipeline_set_layer_combine (pipeline, 0, /* layer */ "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", NULL); } return cache->base_texture_alpha_pipeline; } typedef struct { CoglPangoPipelineCache *cache; CoglTexture *texture; } PipelineDestroyNotifyData; static void pipeline_destroy_notify_cb (void *user_data) { PipelineDestroyNotifyData *data = user_data; g_hash_table_remove (data->cache->hash_table, data->texture); g_slice_free (PipelineDestroyNotifyData, data); } CoglPipeline * _cogl_pango_pipeline_cache_get (CoglPangoPipelineCache *cache, CoglTexture *texture) { CoglPangoPipelineCacheEntry *entry; PipelineDestroyNotifyData *destroy_data; static CoglUserDataKey pipeline_destroy_notify_key; /* Look for an existing entry */ entry = g_hash_table_lookup (cache->hash_table, texture); if (entry) return cogl_object_ref (entry->pipeline); /* No existing pipeline was found so let's create another */ entry = g_slice_new (CoglPangoPipelineCacheEntry); if (texture) { CoglPipeline *base; entry->texture = cogl_object_ref (texture); if (_cogl_texture_get_format (entry->texture) == COGL_PIXEL_FORMAT_A_8) base = get_base_texture_alpha_pipeline (cache); else base = get_base_texture_rgba_pipeline (cache); entry->pipeline = cogl_pipeline_copy (base); cogl_pipeline_set_layer_texture (entry->pipeline, 0 /* layer */, texture); } else { entry->texture = NULL; entry->pipeline = cogl_pipeline_new (cache->ctx); } /* Add a weak reference to the pipeline so we can remove it from the hash table when it is destroyed */ destroy_data = g_slice_new (PipelineDestroyNotifyData); destroy_data->cache = cache; destroy_data->texture = texture; cogl_object_set_user_data (COGL_OBJECT (entry->pipeline), &pipeline_destroy_notify_key, destroy_data, pipeline_destroy_notify_cb); g_hash_table_insert (cache->hash_table, texture ? cogl_object_ref (texture) : NULL, entry); /* This doesn't take a reference on the pipeline so that it will use the newly created reference */ return entry->pipeline; } void _cogl_pango_pipeline_cache_free (CoglPangoPipelineCache *cache) { if (cache->base_texture_rgba_pipeline) cogl_object_unref (cache->base_texture_rgba_pipeline); if (cache->base_texture_alpha_pipeline) cogl_object_unref (cache->base_texture_alpha_pipeline); g_hash_table_destroy (cache->hash_table); cogl_object_unref (cache->ctx); g_free (cache); } muffin-6.4.1/cogl/cogl-pango/cogl-pango-display-list.c0000664000175000017500000003750314723361714021532 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "cogl-config.h" #include #include #include "cogl-pango-display-list.h" #include "cogl-pango-pipeline-cache.h" #include "cogl/cogl-context-private.h" typedef enum { COGL_PANGO_DISPLAY_LIST_TEXTURE, COGL_PANGO_DISPLAY_LIST_RECTANGLE, COGL_PANGO_DISPLAY_LIST_TRAPEZOID } CoglPangoDisplayListNodeType; typedef struct _CoglPangoDisplayListNode CoglPangoDisplayListNode; typedef struct _CoglPangoDisplayListRectangle CoglPangoDisplayListRectangle; struct _CoglPangoDisplayList { gboolean color_override; CoglColor color; GSList *nodes; GSList *last_node; CoglPangoPipelineCache *pipeline_cache; }; /* This matches the format expected by cogl_rectangles_with_texture_coords */ struct _CoglPangoDisplayListRectangle { float x_1, y_1, x_2, y_2; float s_1, t_1, s_2, t_2; }; struct _CoglPangoDisplayListNode { CoglPangoDisplayListNodeType type; gboolean color_override; CoglColor color; CoglPipeline *pipeline; union { struct { /* The texture to render these coords from */ CoglTexture *texture; /* Array of rectangles in the format expected by cogl_rectangles_with_texture_coords */ GArray *rectangles; /* A primitive representing those vertices */ CoglPrimitive *primitive; guint has_color : 1; } texture; struct { float x_1, y_1; float x_2, y_2; } rectangle; struct { CoglPrimitive *primitive; } trapezoid; } d; }; CoglPangoDisplayList * _cogl_pango_display_list_new (CoglPangoPipelineCache *pipeline_cache) { CoglPangoDisplayList *dl = g_slice_new0 (CoglPangoDisplayList); dl->pipeline_cache = pipeline_cache; return dl; } static void _cogl_pango_display_list_append_node (CoglPangoDisplayList *dl, CoglPangoDisplayListNode *node) { if (dl->last_node) dl->last_node = dl->last_node->next = g_slist_prepend (NULL, node); else dl->last_node = dl->nodes = g_slist_prepend (NULL, node); } void _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl, const CoglColor *color) { dl->color_override = TRUE; dl->color = *color; } void _cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl) { dl->color_override = FALSE; } void _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, CoglTexture *texture, float x_1, float y_1, float x_2, float y_2, float tx_1, float ty_1, float tx_2, float ty_2) { CoglPangoDisplayListNode *node; CoglPangoDisplayListRectangle *rectangle; /* Add to the last node if it is a texture node with the same target texture */ if (dl->last_node && (node = dl->last_node->data)->type == COGL_PANGO_DISPLAY_LIST_TEXTURE && node->d.texture.texture == texture && (dl->color_override ? (node->color_override && cogl_color_equal (&dl->color, &node->color)) : !node->color_override)) { /* Get rid of the vertex buffer so that it will be recreated */ if (node->d.texture.primitive != NULL) { cogl_object_unref (node->d.texture.primitive); node->d.texture.primitive = NULL; } } else { /* Otherwise create a new node */ node = g_slice_new (CoglPangoDisplayListNode); node->type = COGL_PANGO_DISPLAY_LIST_TEXTURE; node->color_override = dl->color_override; node->color = dl->color; node->pipeline = NULL; node->d.texture.texture = cogl_object_ref (texture); node->d.texture.rectangles = g_array_new (FALSE, FALSE, sizeof (CoglPangoDisplayListRectangle)); node->d.texture.primitive = NULL; _cogl_pango_display_list_append_node (dl, node); } g_array_set_size (node->d.texture.rectangles, node->d.texture.rectangles->len + 1); rectangle = &g_array_index (node->d.texture.rectangles, CoglPangoDisplayListRectangle, node->d.texture.rectangles->len - 1); rectangle->x_1 = x_1; rectangle->y_1 = y_1; rectangle->x_2 = x_2; rectangle->y_2 = y_2; rectangle->s_1 = tx_1; rectangle->t_1 = ty_1; rectangle->s_2 = tx_2; rectangle->t_2 = ty_2; } void _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, float x_1, float y_1, float x_2, float y_2) { CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); node->type = COGL_PANGO_DISPLAY_LIST_RECTANGLE; node->color_override = dl->color_override; node->color = dl->color; node->d.rectangle.x_1 = x_1; node->d.rectangle.y_1 = y_1; node->d.rectangle.x_2 = x_2; node->d.rectangle.y_2 = y_2; node->pipeline = NULL; _cogl_pango_display_list_append_node (dl, node); } void _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, float y_1, float x_11, float x_21, float y_2, float x_12, float x_22) { CoglContext *ctx = dl->pipeline_cache->ctx; CoglPangoDisplayListNode *node = g_slice_new (CoglPangoDisplayListNode); CoglVertexP2 vertices[4] = { { x_11, y_1 }, { x_12, y_2 }, { x_22, y_2 }, { x_21, y_1 } }; node->type = COGL_PANGO_DISPLAY_LIST_TRAPEZOID; node->color_override = dl->color_override; node->color = dl->color; node->pipeline = NULL; node->d.trapezoid.primitive = cogl_primitive_new_p2 (ctx, COGL_VERTICES_MODE_TRIANGLE_FAN, 4, vertices); _cogl_pango_display_list_append_node (dl, node); } static void emit_rectangles_through_journal (CoglFramebuffer *fb, CoglPipeline *pipeline, CoglPangoDisplayListNode *node) { const float *rectangles = (const float *)node->d.texture.rectangles->data; cogl_framebuffer_draw_textured_rectangles (fb, pipeline, rectangles, node->d.texture.rectangles->len); } static void emit_vertex_buffer_geometry (CoglFramebuffer *fb, CoglPipeline *pipeline, CoglPangoDisplayListNode *node) { CoglContext *ctx = fb->context; /* It's expensive to go through the Cogl journal for large runs * of text in part because the journal transforms the quads in software * to avoid changing the modelview matrix. So for larger runs of text * we load the vertices into a VBO, and this has the added advantage * that if the text doesn't change from frame to frame the VBO can * be re-used avoiding the repeated cost of validating the data and * mapping it into the GPU... */ if (node->d.texture.primitive == NULL) { CoglAttributeBuffer *buffer; CoglVertexP2T2 *verts, *v; int n_verts; gboolean allocated = FALSE; CoglAttribute *attributes[2]; CoglPrimitive *prim; CoglIndices *indices; int i; n_verts = node->d.texture.rectangles->len * 4; buffer = cogl_attribute_buffer_new_with_size (ctx, n_verts * sizeof (CoglVertexP2T2)); if ((verts = cogl_buffer_map (COGL_BUFFER (buffer), COGL_BUFFER_ACCESS_WRITE, COGL_BUFFER_MAP_HINT_DISCARD)) == NULL) { verts = g_new (CoglVertexP2T2, n_verts); allocated = TRUE; } v = verts; /* Copy the rectangles into the buffer and expand into four vertices instead of just two */ for (i = 0; i < node->d.texture.rectangles->len; i++) { const CoglPangoDisplayListRectangle *rectangle = &g_array_index (node->d.texture.rectangles, CoglPangoDisplayListRectangle, i); v->x = rectangle->x_1; v->y = rectangle->y_1; v->s = rectangle->s_1; v->t = rectangle->t_1; v++; v->x = rectangle->x_1; v->y = rectangle->y_2; v->s = rectangle->s_1; v->t = rectangle->t_2; v++; v->x = rectangle->x_2; v->y = rectangle->y_2; v->s = rectangle->s_2; v->t = rectangle->t_2; v++; v->x = rectangle->x_2; v->y = rectangle->y_1; v->s = rectangle->s_2; v->t = rectangle->t_1; v++; } if (allocated) { cogl_buffer_set_data (COGL_BUFFER (buffer), 0, /* offset */ verts, sizeof (CoglVertexP2T2) * n_verts); g_free (verts); } else cogl_buffer_unmap (COGL_BUFFER (buffer)); attributes[0] = cogl_attribute_new (buffer, "cogl_position_in", sizeof (CoglVertexP2T2), G_STRUCT_OFFSET (CoglVertexP2T2, x), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); attributes[1] = cogl_attribute_new (buffer, "cogl_tex_coord0_in", sizeof (CoglVertexP2T2), G_STRUCT_OFFSET (CoglVertexP2T2, s), 2, /* n_components */ COGL_ATTRIBUTE_TYPE_FLOAT); prim = cogl_primitive_new_with_attributes (COGL_VERTICES_MODE_TRIANGLES, n_verts, attributes, 2 /* n_attributes */); indices = cogl_get_rectangle_indices (ctx, node->d.texture.rectangles->len); cogl_primitive_set_indices (prim, indices, node->d.texture.rectangles->len * 6); node->d.texture.primitive = prim; cogl_object_unref (buffer); cogl_object_unref (attributes[0]); cogl_object_unref (attributes[1]); } cogl_primitive_draw (node->d.texture.primitive, fb, pipeline); } static void _cogl_framebuffer_draw_display_list_texture (CoglFramebuffer *fb, CoglPipeline *pipeline, CoglPangoDisplayListNode *node) { /* For small runs of text like icon labels, we can get better performance * going through the Cogl journal since text may then be batched together * with other geometry. */ /* FIXME: 25 is a number I plucked out of thin air; it would be good * to determine this empirically! */ if (node->d.texture.rectangles->len < 25) emit_rectangles_through_journal (fb, pipeline, node); else emit_vertex_buffer_geometry (fb, pipeline, node); } void _cogl_pango_display_list_render (CoglFramebuffer *fb, CoglPangoDisplayList *dl, const CoglColor *color) { GSList *l; for (l = dl->nodes; l; l = l->next) { CoglPangoDisplayListNode *node = l->data; CoglColor draw_color; if (node->pipeline == NULL) { if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) node->pipeline = _cogl_pango_pipeline_cache_get (dl->pipeline_cache, node->d.texture.texture); else node->pipeline = _cogl_pango_pipeline_cache_get (dl->pipeline_cache, NULL); } if (node->color_override) /* Use the override color but preserve the alpha from the draw color */ cogl_color_init_from_4ub (&draw_color, cogl_color_get_red_byte (&node->color), cogl_color_get_green_byte (&node->color), cogl_color_get_blue_byte (&node->color), (cogl_color_get_alpha_byte (&node->color) * cogl_color_get_alpha_byte (color) / 255)); else draw_color = *color; cogl_color_premultiply (&draw_color); cogl_pipeline_set_color (node->pipeline, &draw_color); switch (node->type) { case COGL_PANGO_DISPLAY_LIST_TEXTURE: _cogl_framebuffer_draw_display_list_texture (fb, node->pipeline, node); break; case COGL_PANGO_DISPLAY_LIST_RECTANGLE: cogl_framebuffer_draw_rectangle (fb, node->pipeline, node->d.rectangle.x_1, node->d.rectangle.y_1, node->d.rectangle.x_2, node->d.rectangle.y_2); break; case COGL_PANGO_DISPLAY_LIST_TRAPEZOID: cogl_framebuffer_draw_primitive (fb, node->pipeline, node->d.trapezoid.primitive); break; } } } static void _cogl_pango_display_list_node_free (CoglPangoDisplayListNode *node) { if (node->type == COGL_PANGO_DISPLAY_LIST_TEXTURE) { g_array_free (node->d.texture.rectangles, TRUE); if (node->d.texture.texture != NULL) cogl_object_unref (node->d.texture.texture); if (node->d.texture.primitive != NULL) cogl_object_unref (node->d.texture.primitive); } else if (node->type == COGL_PANGO_DISPLAY_LIST_TRAPEZOID) cogl_object_unref (node->d.trapezoid.primitive); if (node->pipeline) cogl_object_unref (node->pipeline); g_slice_free (CoglPangoDisplayListNode, node); } void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl) { g_slist_free_full (dl->nodes, (GDestroyNotify) _cogl_pango_display_list_node_free); dl->nodes = NULL; dl->last_node = NULL; } void _cogl_pango_display_list_free (CoglPangoDisplayList *dl) { _cogl_pango_display_list_clear (dl); g_slice_free (CoglPangoDisplayList, dl); } muffin-6.4.1/cogl/cogl-pango/cogl-pango-fontmap.c0000664000175000017500000001162414723361714020554 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2008 OpenedHand * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ /** * SECTION:cogl-pango * @short_description: COGL-based text rendering using Pango * * FIXME * */ #include "cogl-config.h" /* This is needed to get the Pango headers to export stuff needed to subclass */ #ifndef PANGO_ENABLE_BACKEND #define PANGO_ENABLE_BACKEND 1 #endif #include #include #include #include "cogl-pango.h" #include "cogl-pango-private.h" #include "cogl-util.h" #include "cogl/cogl-context-private.h" static GQuark cogl_pango_font_map_get_priv_key (void) G_GNUC_CONST; typedef struct _CoglPangoFontMapPriv { CoglContext *ctx; PangoRenderer *renderer; } CoglPangoFontMapPriv; static void free_priv (gpointer data) { CoglPangoFontMapPriv *priv = data; cogl_object_unref (priv->ctx); cogl_object_unref (priv->renderer); g_free (priv); } PangoFontMap * cogl_pango_font_map_new (void) { PangoFontMap *fm = pango_cairo_font_map_new (); g_autofree CoglPangoFontMapPriv *priv = g_new0 (CoglPangoFontMapPriv, 1); _COGL_GET_CONTEXT (context, NULL); priv->ctx = cogl_object_ref (context); /* XXX: The public pango api doesn't let us sub-class * PangoCairoFontMap so we attach our own private data using qdata * for now. */ g_object_set_qdata_full (G_OBJECT (fm), cogl_pango_font_map_get_priv_key (), g_steal_pointer (&priv), free_priv); return fm; } PangoContext * cogl_pango_font_map_create_context (CoglPangoFontMap *fm) { g_return_val_if_fail (COGL_PANGO_IS_FONT_MAP (fm), NULL); #if PANGO_VERSION_CHECK (1, 22, 0) /* We can just directly use the pango context from the Cairo font map */ return pango_font_map_create_context (PANGO_FONT_MAP (fm)); #else return pango_cairo_font_map_create_context (PANGO_CAIRO_FONT_MAP (fm)); #endif } static CoglPangoFontMapPriv * _cogl_pango_font_map_get_priv (CoglPangoFontMap *fm) { return g_object_get_qdata (G_OBJECT (fm), cogl_pango_font_map_get_priv_key ()); } PangoRenderer * _cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm) { CoglPangoFontMapPriv *priv = _cogl_pango_font_map_get_priv (fm); if (G_UNLIKELY (!priv->renderer)) priv->renderer = _cogl_pango_renderer_new (priv->ctx); return priv->renderer; } PangoRenderer * cogl_pango_font_map_get_renderer (CoglPangoFontMap *fm) { return _cogl_pango_font_map_get_renderer (fm); } CoglContext * _cogl_pango_font_map_get_cogl_context (CoglPangoFontMap *fm) { CoglPangoFontMapPriv *priv = _cogl_pango_font_map_get_priv (fm); return priv->ctx; } void cogl_pango_font_map_set_resolution (CoglPangoFontMap *font_map, double dpi) { g_return_if_fail (COGL_PANGO_IS_FONT_MAP (font_map)); pango_cairo_font_map_set_resolution (PANGO_CAIRO_FONT_MAP (font_map), dpi); } void cogl_pango_font_map_clear_glyph_cache (CoglPangoFontMap *fm) { PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); _cogl_pango_renderer_clear_glyph_cache (COGL_PANGO_RENDERER (renderer)); } void cogl_pango_font_map_set_use_mipmapping (CoglPangoFontMap *fm, gboolean value) { PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); _cogl_pango_renderer_set_use_mipmapping (COGL_PANGO_RENDERER (renderer), value); } gboolean cogl_pango_font_map_get_use_mipmapping (CoglPangoFontMap *fm) { PangoRenderer *renderer = _cogl_pango_font_map_get_renderer (fm); return _cogl_pango_renderer_get_use_mipmapping (COGL_PANGO_RENDERER (renderer)); } static GQuark cogl_pango_font_map_get_priv_key (void) { static GQuark priv_key = 0; if (G_UNLIKELY (priv_key == 0)) priv_key = g_quark_from_static_string ("CoglPangoFontMap"); return priv_key; } muffin-6.4.1/cogl/cogl-pango/cogl-pango-display-list.h0000664000175000017500000000573414723361714021540 0ustar fabiofabio/* * Cogl * * A Low Level GPU Graphics and Utilities API * * Copyright (C) 2009 Intel Corporation. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef __COGL_PANGO_DISPLAY_LIST_H__ #define __COGL_PANGO_DISPLAY_LIST_H__ #include #include "cogl-pango-pipeline-cache.h" G_BEGIN_DECLS typedef struct _CoglPangoDisplayList CoglPangoDisplayList; CoglPangoDisplayList * _cogl_pango_display_list_new (CoglPangoPipelineCache *); void _cogl_pango_display_list_set_color_override (CoglPangoDisplayList *dl, const CoglColor *color); void _cogl_pango_display_list_remove_color_override (CoglPangoDisplayList *dl); void _cogl_pango_display_list_add_texture (CoglPangoDisplayList *dl, CoglTexture *texture, float x_1, float y_1, float x_2, float y_2, float tx_1, float ty_1, float tx_2, float ty_2); void _cogl_pango_display_list_add_rectangle (CoglPangoDisplayList *dl, float x_1, float y_1, float x_2, float y_2); void _cogl_pango_display_list_add_trapezoid (CoglPangoDisplayList *dl, float y_1, float x_11, float x_21, float y_2, float x_12, float x_22); void _cogl_pango_display_list_render (CoglFramebuffer *framebuffer, CoglPangoDisplayList *dl, const CoglColor *color); void _cogl_pango_display_list_clear (CoglPangoDisplayList *dl); void _cogl_pango_display_list_free (CoglPangoDisplayList *dl); G_END_DECLS #endif /* __COGL_PANGO_DISPLAY_LIST_H__ */ muffin-6.4.1/cogl/test-fixtures/0000775000175000017500000000000014723361714015514 5ustar fabiofabiomuffin-6.4.1/cogl/test-fixtures/test-utils.c0000664000175000017500000003300414723361714017775 0ustar fabiofabio#include "cogl-config.h" #include #include "test-unit.h" #include "test-utils.h" #define FB_WIDTH 512 #define FB_HEIGHT 512 static gboolean cogl_test_is_verbose; CoglContext *test_ctx; CoglFramebuffer *test_fb; static gboolean check_flags (TestFlags flags, CoglRenderer *renderer) { if (flags & TEST_REQUIREMENT_GL && cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL && cogl_renderer_get_driver (renderer) != COGL_DRIVER_GL3) { return FALSE; } if (flags & TEST_REQUIREMENT_TEXTURE_RG && !cogl_has_feature (test_ctx, COGL_FEATURE_ID_TEXTURE_RG)) { return FALSE; } if (flags & TEST_REQUIREMENT_MAP_WRITE && !cogl_has_feature (test_ctx, COGL_FEATURE_ID_MAP_BUFFER_FOR_WRITE)) { return FALSE; } if (flags & TEST_REQUIREMENT_FENCE && !cogl_has_feature (test_ctx, COGL_FEATURE_ID_FENCE)) { return FALSE; } if (flags & TEST_KNOWN_FAILURE) { return FALSE; } return TRUE; } static gboolean is_boolean_env_set (const char *variable) { char *val = getenv (variable); gboolean ret; if (!val) return FALSE; if (g_ascii_strcasecmp (val, "1") == 0 || g_ascii_strcasecmp (val, "on") == 0 || g_ascii_strcasecmp (val, "true") == 0) ret = TRUE; else if (g_ascii_strcasecmp (val, "0") == 0 || g_ascii_strcasecmp (val, "off") == 0 || g_ascii_strcasecmp (val, "false") == 0) ret = FALSE; else { g_critical ("Spurious boolean environment variable value (%s=%s)", variable, val); ret = TRUE; } return ret; } void test_utils_init (TestFlags requirement_flags, TestFlags known_failure_flags) { static int counter = 0; GError *error = NULL; CoglOnscreen *onscreen = NULL; CoglDisplay *display; CoglRenderer *renderer; gboolean missing_requirement; gboolean known_failure; if (counter != 0) g_critical ("We don't support running more than one test at a time\n" "in a single test run due to the state leakage that can\n" "cause subsequent tests to fail.\n" "\n" "If you want to run all the tests you should run\n" "$ make test-report"); counter++; if (is_boolean_env_set ("COGL_TEST_VERBOSE") || is_boolean_env_set ("V")) cogl_test_is_verbose = TRUE; /* NB: This doesn't have any effect since commit 47444dac of glib * because the environment variable is read in a magic constructor * so it is too late to set them here */ if (g_getenv ("G_DEBUG")) { char *debug = g_strconcat (g_getenv ("G_DEBUG"), ",fatal-warnings", NULL); g_setenv ("G_DEBUG", debug, TRUE); g_free (debug); } else g_setenv ("G_DEBUG", "fatal-warnings", TRUE); g_setenv ("COGL_X11_SYNC", "1", 0); test_ctx = cogl_context_new (NULL, &error); if (!test_ctx) g_critical ("Failed to create a CoglContext: %s", error->message); display = cogl_context_get_display (test_ctx); renderer = cogl_display_get_renderer (display); missing_requirement = !check_flags (requirement_flags, renderer); known_failure = !check_flags (known_failure_flags, renderer); if (is_boolean_env_set ("COGL_TEST_ONSCREEN")) { onscreen = cogl_onscreen_new (test_ctx, 640, 480); test_fb = COGL_FRAMEBUFFER (onscreen); } else { CoglOffscreen *offscreen; CoglTexture2D *tex = cogl_texture_2d_new_with_size (test_ctx, FB_WIDTH, FB_HEIGHT); offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (tex)); test_fb = COGL_FRAMEBUFFER (offscreen); } if (!cogl_framebuffer_allocate (test_fb, &error)) g_critical ("Failed to allocate framebuffer: %s", error->message); if (onscreen) cogl_onscreen_show (onscreen); cogl_framebuffer_clear4f (test_fb, COGL_BUFFER_BIT_COLOR | COGL_BUFFER_BIT_DEPTH | COGL_BUFFER_BIT_STENCIL, 0, 0, 0, 1); if (missing_requirement) g_print ("WARNING: Missing required feature[s] for this test\n"); else if (known_failure) g_print ("WARNING: Test is known to fail\n"); } void test_utils_fini (void) { if (test_fb) cogl_object_unref (test_fb); if (test_ctx) cogl_object_unref (test_ctx); } static gboolean compare_component (int a, int b) { return ABS (a - b) <= 1; } void test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, uint32_t expected_pixel) { /* Compare each component with a small fuzz factor */ if (!compare_component (screen_pixel[0], expected_pixel >> 24) || !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff) || !compare_component (screen_pixel[3], (expected_pixel >> 0) & 0xff)) { uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); char *screen_pixel_string = g_strdup_printf ("#%08x", screen_pixel_num); char *expected_pixel_string = g_strdup_printf ("#%08x", expected_pixel); g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); g_free (screen_pixel_string); g_free (expected_pixel_string); } } void test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel) { /* Compare each component with a small fuzz factor */ if (!compare_component (screen_pixel[0], expected_pixel >> 24) || !compare_component (screen_pixel[1], (expected_pixel >> 16) & 0xff) || !compare_component (screen_pixel[2], (expected_pixel >> 8) & 0xff)) { uint32_t screen_pixel_num = GUINT32_FROM_BE (*(uint32_t *) screen_pixel); char *screen_pixel_string = g_strdup_printf ("#%06x", screen_pixel_num >> 8); char *expected_pixel_string = g_strdup_printf ("#%06x", expected_pixel >> 8); g_assert_cmpstr (screen_pixel_string, ==, expected_pixel_string); g_free (screen_pixel_string); g_free (expected_pixel_string); } } void test_utils_check_pixel (CoglFramebuffer *test_fb, int x, int y, uint32_t expected_pixel) { uint8_t pixel[4]; cogl_framebuffer_read_pixels (test_fb, x, y, 1, 1, COGL_PIXEL_FORMAT_RGBA_8888_PRE, pixel); test_utils_compare_pixel (pixel, expected_pixel); } void test_utils_check_pixel_and_alpha (CoglFramebuffer *test_fb, int x, int y, uint32_t expected_pixel) { uint8_t pixel[4]; cogl_framebuffer_read_pixels (test_fb, x, y, 1, 1, COGL_PIXEL_FORMAT_RGBA_8888_PRE, pixel); test_utils_compare_pixel_and_alpha (pixel, expected_pixel); } void test_utils_check_pixel_rgb (CoglFramebuffer *test_fb, int x, int y, int r, int g, int b) { test_utils_check_pixel (test_fb, x, y, (r << 24) | (g << 16) | (b << 8)); } void test_utils_check_region (CoglFramebuffer *test_fb, int x, int y, int width, int height, uint32_t expected_rgba) { uint8_t *pixels, *p; pixels = p = g_malloc (width * height * 4); cogl_framebuffer_read_pixels (test_fb, x, y, width, height, COGL_PIXEL_FORMAT_RGBA_8888, p); /* Check whether the center of each division is the right color */ for (y = 0; y < height; y++) for (x = 0; x < width; x++) { test_utils_compare_pixel (p, expected_rgba); p += 4; } g_free (pixels); } CoglTexture * test_utils_create_color_texture (CoglContext *context, uint32_t color) { CoglTexture2D *tex_2d; color = GUINT32_TO_BE (color); tex_2d = cogl_texture_2d_new_from_data (context, 1, 1, /* width/height */ COGL_PIXEL_FORMAT_RGBA_8888_PRE, 4, /* rowstride */ (uint8_t *) &color, NULL); return COGL_TEXTURE (tex_2d); } gboolean cogl_test_verbose (void) { return cogl_test_is_verbose; } static void set_auto_mipmap_cb (CoglTexture *sub_texture, const float *sub_texture_coords, const float *meta_coords, void *user_data) { cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (sub_texture), FALSE); } CoglTexture * test_utils_texture_new_with_size (CoglContext *ctx, int width, int height, TestUtilsTextureFlags flags, CoglTextureComponents components) { CoglTexture *tex; GError *skip_error = NULL; /* First try creating a fast-path non-sliced texture */ tex = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); cogl_texture_set_components (tex, components); if (!cogl_texture_allocate (tex, &skip_error)) { g_error_free (skip_error); cogl_object_unref (tex); tex = NULL; } if (!tex) { /* If it fails resort to sliced textures */ int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; CoglTexture2DSliced *tex_2ds = cogl_texture_2d_sliced_new_with_size (ctx, width, height, max_waste); tex = COGL_TEXTURE (tex_2ds); cogl_texture_set_components (tex, components); } if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) { /* To be able to iterate the slices of a #CoglTexture2DSliced we * need to ensure the texture is allocated... */ cogl_texture_allocate (tex, NULL); /* don't catch exceptions */ cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, set_auto_mipmap_cb, NULL); /* don't catch exceptions */ } cogl_texture_allocate (tex, NULL); return tex; } CoglTexture * test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, TestUtilsTextureFlags flags, gboolean premultiplied) { CoglAtlasTexture *atlas_tex; CoglTexture *tex; GError *internal_error = NULL; if (!flags) { /* First try putting the texture in the atlas */ atlas_tex = cogl_atlas_texture_new_from_bitmap (bitmap); cogl_texture_set_premultiplied (COGL_TEXTURE (atlas_tex), premultiplied); if (cogl_texture_allocate (COGL_TEXTURE (atlas_tex), &internal_error)) return COGL_TEXTURE (atlas_tex); cogl_object_unref (atlas_tex); } g_clear_error (&internal_error); /* If that doesn't work try a fast path 2D texture */ tex = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); cogl_texture_set_premultiplied (tex, premultiplied); if (g_error_matches (internal_error, COGL_SYSTEM_ERROR, COGL_SYSTEM_ERROR_NO_MEMORY)) { g_assert_not_reached (); return NULL; } g_clear_error (&internal_error); if (!tex) { /* Otherwise create a sliced texture */ int max_waste = flags & TEST_UTILS_TEXTURE_NO_SLICING ? -1 : COGL_TEXTURE_MAX_WASTE; CoglTexture2DSliced *tex_2ds = cogl_texture_2d_sliced_new_from_bitmap (bitmap, max_waste); tex = COGL_TEXTURE (tex_2ds); cogl_texture_set_premultiplied (tex, premultiplied); } if (flags & TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP) { cogl_meta_texture_foreach_in_region (COGL_META_TEXTURE (tex), 0, 0, 1, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE, set_auto_mipmap_cb, NULL); /* don't catch exceptions */ } cogl_texture_allocate (tex, NULL); return tex; } CoglTexture * test_utils_texture_new_from_data (CoglContext *ctx, int width, int height, TestUtilsTextureFlags flags, CoglPixelFormat format, int rowstride, const uint8_t *data) { CoglBitmap *bmp; CoglTexture *tex; g_assert_cmpint (format, !=, COGL_PIXEL_FORMAT_ANY); g_assert (data != NULL); /* Wrap the data into a bitmap */ bmp = cogl_bitmap_new_for_data (ctx, width, height, format, rowstride, (uint8_t *) data); tex = test_utils_texture_new_from_bitmap (bmp, flags, TRUE); cogl_object_unref (bmp); return tex; } muffin-6.4.1/cogl/test-fixtures/meson.build0000664000175000017500000000123114723361714017653 0ustar fabiofabiocogl_test_fixtures_includepath = [include_directories('.')] cogl_test_fixtures_sources = [ 'test-unit.h', 'test-utils.h', 'test-utils.c', ] test_datadir = join_paths(cogl_srcdir, 'tests', 'data') libmutter_cogl_test_fixtures = static_library('mutter-cogl-test-fixtures', sources: cogl_test_fixtures_sources, c_args: [cogl_c_args, '-DTEST_DATADIR=@0@'.format(test_datadir)], link_args: ['-Wl,--no-undefined', '-Wl,--unresolved-symbols=ignore-in-object-files'], include_directories: cogl_includepath, dependencies: [cogl_deps], install: false, ) libmutter_cogl_test_fixtures_dep = declare_dependency( link_with: libmutter_cogl_test_fixtures ) muffin-6.4.1/cogl/test-fixtures/test-unit.h0000664000175000017500000000135014723361714017620 0ustar fabiofabio#ifndef _TEST_UNIT_H_ #define _TEST_UNIT_H_ #include #ifdef ENABLE_UNIT_TESTS typedef struct _CoglUnitTest { const char *name; TestFlags requirement_flags; TestFlags known_failure_flags; void (*run) (void); } CoglUnitTest; #define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \ static void NAME (void); \ \ COGL_EXPORT \ const CoglUnitTest unit_test_##NAME; \ const CoglUnitTest unit_test_##NAME = \ { #NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS, NAME }; \ \ static void NAME (void) #else /* ENABLE_UNIT_TESTS */ #define UNIT_TEST(NAME, REQUIREMENT_FLAGS, KNOWN_FAILURE_FLAGS) \ static inline void NAME (void) #endif /* ENABLE_UNIT_TESTS */ #endif /* _TEST_UNIT_H_ */ muffin-6.4.1/cogl/test-fixtures/test-utils.h0000664000175000017500000002416414723361714020011 0ustar fabiofabio#ifndef _TEST_UTILS_H_ #define _TEST_UTILS_H_ /* NB: This header is for private and public api testing and so * we need consider that if we are testing the public api we should * just include but since that will only provide * opaque typedefs we need to include the specific internal headers * for testing private apis... */ #ifdef COGL_COMPILATION #include #include #include #include #include #include #include #include #else #include #endif #include /* We don't really care about functions that are defined without a header for the unit tests so we can just disable it here */ #ifdef __GNUC__ #pragma GCC diagnostic ignored "-Wmissing-declarations" #endif typedef enum _TestFlags { TEST_KNOWN_FAILURE = 1<<0, TEST_REQUIREMENT_GL = 1<<1, TEST_REQUIREMENT_NPOT = 1<<2, TEST_REQUIREMENT_TEXTURE_RECTANGLE = 1<<4, TEST_REQUIREMENT_TEXTURE_RG = 1<<5, TEST_REQUIREMENT_POINT_SPRITE = 1<<6, TEST_REQUIREMENT_MAP_WRITE = 1<<8, TEST_REQUIREMENT_GLSL = 1<<9, TEST_REQUIREMENT_OFFSCREEN = 1<<10, TEST_REQUIREMENT_FENCE = 1<<11, TEST_REQUIREMENT_PER_VERTEX_POINT_SIZE = 1<<12 } TestFlags; /** * TestUtilsTextureFlags: * @TEST_UTILS_TEXTURE_NONE: No flags specified * @TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP: Disables the automatic generation of * the mipmap pyramid from the base level image whenever it is * updated. The mipmaps are only generated when the texture is * rendered with a mipmap filter so it should be free to leave out * this flag when using other filtering modes * @TEST_UTILS_TEXTURE_NO_SLICING: Disables the slicing of the texture * @TEST_UTILS_TEXTURE_NO_ATLAS: Disables the insertion of the texture inside * the texture atlas used by Cogl * * Flags to pass to the test_utils_texture_new_* family of functions. */ typedef enum { TEST_UTILS_TEXTURE_NONE = 0, TEST_UTILS_TEXTURE_NO_AUTO_MIPMAP = 1 << 0, TEST_UTILS_TEXTURE_NO_SLICING = 1 << 1, TEST_UTILS_TEXTURE_NO_ATLAS = 1 << 2 } TestUtilsTextureFlags; extern CoglContext *test_ctx; extern CoglFramebuffer *test_fb; void test_utils_init (TestFlags requirement_flags, TestFlags known_failure_flags); void test_utils_fini (void); /* * test_utils_texture_new_with_size: * @context: A #CoglContext * @width: width of texture in pixels. * @height: height of texture in pixels. * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE * @components: What texture components are required * * Creates a new #CoglTexture with the specified dimensions and pixel format. * * The storage for the texture is not necesarily created before this * function returns. The storage can be explicitly allocated using * cogl_texture_allocate() or preferably you can let Cogl * automatically allocate the storage lazily when uploading data when * Cogl may know more about how the texture will be used and can * optimize how it is allocated. * * Return value: A newly created #CoglTexture */ CoglTexture * test_utils_texture_new_with_size (CoglContext *ctx, int width, int height, TestUtilsTextureFlags flags, CoglTextureComponents components); /* * test_utils_texture_new_from_data: * @context: A #CoglContext * @width: width of texture in pixels * @height: height of texture in pixels * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE * @format: the #CoglPixelFormat the buffer is stored in in RAM * @rowstride: the memory offset in bytes between the starts of * scanlines in @data * @data: pointer the memory region where the source buffer resides * @error: A #GError to catch exceptional errors or %NULL * * Creates a new #CoglTexture based on data residing in memory. * * Note: If the given @format has an alpha channel then the data * will be loaded into a premultiplied internal format. If you want * to avoid having the source data be premultiplied then you can * either specify that the data is already premultiplied or use * test_utils_texture_new_from_bitmap which lets you explicitly * request whether the data should internally be premultipled or not. * * Return value: A newly created #CoglTexture or %NULL on failure */ CoglTexture * test_utils_texture_new_from_data (CoglContext *ctx, int width, int height, TestUtilsTextureFlags flags, CoglPixelFormat format, int rowstride, const uint8_t *data); /* * test_utils_texture_new_from_bitmap: * @bitmap: A #CoglBitmap pointer * @flags: Optional flags for the texture, or %TEST_UTILS_TEXTURE_NONE * @premultiplied: Whether the texture should hold premultipled data. * (if the bitmap already holds premultiplied data * and %TRUE is given then no premultiplication will * be done. The data will be premultipled while * uploading if the bitmap has an alpha channel but * does not already have a premultiplied format.) * * Creates a #CoglTexture from a #CoglBitmap. * * Return value: A newly created #CoglTexture or %NULL on failure */ CoglTexture * test_utils_texture_new_from_bitmap (CoglBitmap *bitmap, TestUtilsTextureFlags flags, gboolean premultiplied); /* * test_utils_check_pixel: * @framebuffer: The #CoglFramebuffer to read from * @x: x co-ordinate of the pixel to test * @y: y co-ordinate of the pixel to test * @pixel: An integer of the form 0xRRGGBBAA representing the expected * pixel value * * This performs reads a pixel on the given cogl @framebuffer and * asserts that it matches the given color. The alpha channel of the * color is ignored. The pixels are converted to a string and compared * with g_assert_cmpstr so that if the comparison fails then the * assert will display a meaningful message */ void test_utils_check_pixel (CoglFramebuffer *framebuffer, int x, int y, uint32_t expected_pixel); /** * @framebuffer: The #CoglFramebuffer to read from * @x: x co-ordinate of the pixel to test * @y: y co-ordinate of the pixel to test * @pixel: An integer of the form 0xRRGGBBAA representing the expected * pixel value * * This performs reads a pixel on the given cogl @framebuffer and * asserts that it matches the given color. The alpha channel is also * checked unlike with test_utils_check_pixel(). The pixels are * converted to a string and compared with g_assert_cmpstr so that if * the comparison fails then the assert will display a meaningful * message. */ void test_utils_check_pixel_and_alpha (CoglFramebuffer *fb, int x, int y, uint32_t expected_pixel); /* * test_utils_check_pixel: * @framebuffer: The #CoglFramebuffer to read from * @x: x co-ordinate of the pixel to test * @y: y co-ordinate of the pixel to test * @pixel: An integer of the form 0xrrggbb representing the expected pixel value * * This performs reads a pixel on the given cogl @framebuffer and * asserts that it matches the given color. The alpha channel of the * color is ignored. The pixels are converted to a string and compared * with g_assert_cmpstr so that if the comparison fails then the * assert will display a meaningful message */ void test_utils_check_pixel_rgb (CoglFramebuffer *framebuffer, int x, int y, int r, int g, int b); /* * test_utils_check_region: * @framebuffer: The #CoglFramebuffer to read from * @x: x co-ordinate of the region to test * @y: y co-ordinate of the region to test * @width: width of the region to test * @height: height of the region to test * @pixel: An integer of the form 0xrrggbb representing the expected region color * * Performs a read pixel on the specified region of the given cogl * @framebuffer and asserts that it matches the given color. The alpha * channel of the color is ignored. The pixels are converted to a * string and compared with g_assert_cmpstr so that if the comparison * fails then the assert will display a meaningful message */ void test_utils_check_region (CoglFramebuffer *framebuffer, int x, int y, int width, int height, uint32_t expected_rgba); /* * test_utils_compare_pixel: * @screen_pixel: A pixel stored in memory * @expected_pixel: The expected RGBA value * * Compares a pixel from a buffer to an expected value. The pixels are * converted to a string and compared with g_assert_cmpstr so that if * the comparison fails then the assert will display a meaningful * message. */ void test_utils_compare_pixel (const uint8_t *screen_pixel, uint32_t expected_pixel); /* * test_utils_compare_pixel_and_alpha: * @screen_pixel: A pixel stored in memory * @expected_pixel: The expected RGBA value * * Compares a pixel from a buffer to an expected value. This is * similar to test_utils_compare_pixel() except that it doesn't ignore * the alpha component. */ void test_utils_compare_pixel_and_alpha (const uint8_t *screen_pixel, uint32_t expected_pixel); /* * test_utils_create_color_texture: * @context: A #CoglContext * @color: A color to put in the texture * * Creates a 1x1-pixel RGBA texture filled with the given color. */ CoglTexture * test_utils_create_color_texture (CoglContext *context, uint32_t color); /* cogl_test_verbose: * * Queries if the user asked for verbose output or not. */ gboolean cogl_test_verbose (void); /* test_util_is_pot: * @number: A number to test * * Returns whether the given integer is a power of two */ static inline gboolean test_utils_is_pot (unsigned int number) { /* Make sure there is only one bit set */ return (number & (number - 1)) == 0; } #endif /* _TEST_UTILS_H_ */ muffin-6.4.1/README.md0000664000175000017500000000377414723361714013234 0ustar fabiofabioMuffin is a fork of Mutter, specifically adapted to work as the window manager for the Cinnamon desktop environment. # Mutter Mutter is a Wayland display server and X11 window manager and compositor library. When used as a Wayland display server, it runs on top of KMS and libinput. It implements the compositor side of the Wayland core protocol as well as various protocol extensions. It also has functionality related to running X11 applications using Xwayland. When used on top of Xorg it acts as a X11 window manager and compositing manager. It contains functionality related to, among other things, window management, window compositing, focus tracking, workspace management, keybindings and monitor configuration. Internally it uses a fork of Cogl, a hardware acceleration abstraction library used to simplify usage of OpenGL pipelines, as well as a fork af Clutter, a scene graph and user interface toolkit. Mutter is used by, for example, GNOME Shell, the GNOME core user interface, and by Gala, elementary OS's window manager. It can also be run standalone, using the command "mutter", but just running plain mutter is only intended for debugging purposes. ## Contributing To contribute, open merge requests at https://gitlab.gnome.org/GNOME/mutter. The coding style used is primarily the GNU flavor of the [GNOME coding style](https://developer.gnome.org/programming-guidelines/stable/c-coding-style.html.en) with some minor additions such as preferring `stdint.h` types over GLib fundamental types, and a soft 80 character line limit. However, in general, look at the file you're editing for inspiration. Commit messages should follow the [GNOME commit message guidelines](https://wiki.gnome.org/Git/CommitMessages). We require an URL to either an issue or a merge request in each commit. ## License Mutter is distributed under the terms of the GNU General Public License, version 2 or later. See the [COPYING][license] file for detalis. [bug-tracker]: https://gitlab.gnome.org/GNOME/mutter/issues [license]: COPYING muffin-6.4.1/NEWS0000664000175000017500000074527514723361714012465 0ustar fabiofabio3.36.9 ====== * Do not ping unmanaging windows [Florian; gnome-shell#2467] * Improve freezes when switching workspace [Jonas Å.; !1616] * Fix drag cancel animation when using geometry scaling [Robert; !1683] * Fix stuck icon in DND operation between X11 and wayland [Carlos; !1720] * Fix restoring focus to windows using globally active input [Olivier; !1716] * Fix order in which subsurface placement operations are handled [Robert; !1768] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Robert Mader, Florian Müllner 3.36.8 ====== * Fix _NET_WM_FRAME_DRAWN timestamps [Jonas; !1360] * Fix unwanted position changes on window resize [Jonas, Olivier, Robert; !1477, !1495] * Fix device configuration not being picked up on X11 [Carlos; !1553] * Fix size hints with CSD [Christian, Olivier; !1598] * Disable CRTCs if there is no monitor [Kai-Heng; !1561] * Fixed crashes [Olivier, Robert, Jonas; !1529, !1534, #1521, !1563, !1653] * Plugged memory leaks [Ray; !1225] * Misc. bug fixes and cleanups [Olivier, Robert, Daniel, Carlos; !1575, !1565, !1572, !1655] Contributors: Jonas Ådahl, Kai-Heng Feng, Olivier Fourdan, Carlos Garnacho, Robert Mader, Christian Rauch, Ray Strode, Daniel van Vugt 3.36.7 ====== * Fix Night Light updates after DPMS [Jonas, Benjamin; #1392] * Fix IM handling on X11 [Carlos; #1413] * Fix resizing of attached modal dialogs on wayland [Jonas; !1446] * Fix jumps when resizing windows using discrete steps [Jonas; #1447] * Fixed crashes [Marco; !1371, #1345] * Plugged Memory leaks [Ray; !1449, !1451] * Misc. bug fixes and cleanups [Jonas, Carlos, Robert; !1218, !1460, !1463] Contributors: Marco Trevisan (Treviño), Benjamin Berg, Carlos Garnacho, Robert Mader, Ray Strode, Jonas Ådahl Translators: Juliano de Souza Camargo [pt] 3.36.6 ====== * Fix stale cursor positions in remote desktop sessions [Georges; !1417] * Limit mipmap levels when rendering background [Daniel; !1003] * Improve support for Hangul input method [Carlos; !1423] * Fixed crashes [Robert, Benjamin; !1411, !1409, !1408, !1415, !1392] * Plugged memory leaks [Robert; #1292] * Misc. bug fixes and cleanups [Daniel; !1298, !983] Contributors: Benjamin Berg, Carlos Garnacho, Robert Mader, Georges Basile Stavracas Neto, Daniel van Vugt 3.36.5 ====== * Screencast fixes and improvements [Jonas; !1351, !1365] * Fix glitches when subsurfaces extend outside the toplevel [Robert; #1316] * Mipmap background texture rendering [Daniel; !1347] * Fix wine copy & paste [Sebastian; !1369] * Plugged memory leaks [Marco, Thomas; !1195] Contributors: Jonas Ådahl, Thomas Hindoe Paaboel Andersen, Sebastian Keller, Robert Mader, Marco Trevisan (Treviño), Daniel van Vugt Translators: Rafael Fontenelle [pt_BR] 3.36.4 ====== * Fix crash on area screenshots with fractional scaling [Sebastian; !1320] * Do not paint textures of fully obscured windows [Robert; !1326] * Turn off CRTCs as well when enabling DPMS [Michel; !1240] * Improve selection support [Robert, Carlos, Sebastian; !1330, !1193, !1253, !1255, !1293, !1350] * Use a more appropriate combine function on opaque areas [Daniel; !1331] * Fix remote desktop being broken without screencast session [Olivier; #1307] * Fix popovers disappearing on wayland and HiDPI [Robert; #1312] * Fixed crashes [Jonas Å.; !1317] * Plugged memory leaks [Jonas Å.; !1283] * Misc. bug fixes and cleanups [Corentin, Sebastian, Jonas Å., Jonas D.; !1314, !1321, !1295, !1333] Contributors: Jonas Dreßler, Michel Dänzer, Olivier Fourdan, Carlos Garnacho, Sebastian Keller, Robert Mader, Corentin Noël, Daniel van Vugt, Jonas Ådahl 3.36.3 ====== * Broadcast clipboard/primary offers [Carlos; !1262] * Fix monitor screen cast on X11 [Jonas Å.; !1251] * Implement touch-mode detecation for the X11 backend [Carlos; !1278] * Drop external keyboard detection from touch-mode heuristics [Carlos; !1277] * Fix leaked DMA buffers in screencasts [Jonas; !1283] * Fixed crashes [Daniel, Carlos, Jonas D.; !1256, !1258, !1280] Contributors: Carlos Garnacho, Daniel van Vugt, Jonas Ådahl 3.36.2 ====== * Sync timelines to hardware vsync [Daniel; !724] * Fix screencasting non-maximized windows [Jonas; !1174] * Make window-aliveness checks less aggressive [Jonas; !1182] * Fix stylus coordinates when using screen rotation [Jonas T.; #1118] * Preserve keyboard state on VT switch [Olivier; !1185] * Fix trackball button scrolling [Phillip; #1120] * Fix tiled monitor support [Jonas Å.; !1199] * Fix various clipboard issues [Carlos; !1198, !1203, !1204, !1186, !1206] * Synchronize shadows to server-side decorations [Olivier; !1214] * Fix overview key on X11 when using multiple keyboard layouts [Olivier; !1219] * Fix capturing with multiple stage views [Jonas Å.; !1222] * Fixed crashes [Jonas D., Carlos; !1173, !1183] * Misc. bug fixes and cleanups [Andre, Georges, Simon, Christian, Carlos, Marco, Pekka, Laurent, Jonas D.; !1169, !1170, !1172, !1168, !1184, !1200, !1209, #1074, !1208] Contributors: Marco Trevisan (Treviño), Laurent Bigonville, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Andre Moreira Magalhaes, Simon McVittie, Georges Basile Stavracas Neto, Pekka Paalanen, Christian Rauch, Jonas Troeger, Daniel van Vugt, Phillip Wood, Jonas Ådahl Translators: Dušan Kazik [sk], Christian Kirbach [de] 3.36.1 ====== * Fix hardware cursor on GPU hotplpug [Pekka; !1097] * Fix black areas around XWayland windows when resizing [Robert, Olivier; !1091] * Fix applying wrong scale to monitors on X11 [Jonas; !1118] * Fix moving/resizing windows via keyboard on wayland [Alynx; !997] * Fix locate-pointer feature interfering with keybindings [Carlos; !1014] * Add support for middle-click emulation [Andrew; !256] * Fix freeze when moving cursor between scaled monitors [Robert; !1125] * Fix popup misplacement with focus-follows-mouse [Jonas Å.; !1122] * Fix misplaced cursor in preedit strings [Carlos; !1132] * Support mirroring with proprietary Nvidia driver [Jonas Å.; !1098] * Support tablets with multiple mode switch buttons in a group [Carlos; !970] * Ignore foreground color for color glyphs (emojis) [Carlos; !1148] * Allow pad mode switches while showing OSD [Carlos; !975] * Fix positioning of OSD for display-attached tablets [Carlos; !971] * Respect configured RANDR panning on X11 [Jonas Å.; !1085] * Use correct texture filtering with scaled displays [Jonas; !1124] * Fix cursor hotspots in virtual machines [Jonas Å.; !1136] * Fix build with GLES and no GL [Georges; !1151] * Work around Firefox bug when copying images on wayland [Robert; !1141] * Fix wrong cursor rotation on rotated displays [Hans; !1153] * Fix glitches in window screencasts [Georges; !1129] * Fix IM support for deleting surrounding text [Takao, Carlos; #539] * Fix map animation of maximized windows [Robert; !1164] * Fixed crashes [Jonas Å., Carlos, Florian, Robert; !1120, !1121, #917, #1132, #1083, !1147, #1147] * Misc. bug fixes and cleanups [Jonas Å., Olivier, Mart, Sebastian, Corentin, Andre, Daniel, Robert, Carlos, Peter, Georges, Jonas D., Florian, Christian; !1115, !1102, !1104, !1106, !1117, !1119, !1101, !1123, #1124, !1130, !1131, !1133, #1065, !1108, !1144, !1145, !1109, !1059, !1107, !999, !1152, #1128, !1155, !1156, !1158, !1157, #1146, !1161, !1163] Contributors: Jonas Dreßler, Olivier Fourdan, Takao Fujiwara, Carlos Garnacho, Andrew Gaul, Hans de Goede, Peter Hutterer, Sebastian Keller, Robert Mader, Andre Moreira Magalhaes, Florian Müllner, Georges Basile Stavracas Neto, Corentin Noël, Pekka Paalanen, Christian Rauch, Mart Raudsepp, Daniel van Vugt, Alynx Zhou, Jonas Ådahl Translators: Марко Костић [sr], Daniel Șerbănescu [ro] 3.36.0 ====== * Fix placement of popup windows in multi-monitor setups [Jonas; !1110] * Fix invisible mouse cursor on some hardware [Jonas; !1079] Contributors: Jonas Ådahl Translators: Aurimas Černius [lt], Goran Vidović [hr], Anders Jonsson [sv], Guillaume Bernard [fr], Milo Casagrande [it], Daniel Korostil [uk], Andre Klapper [cy], Aman Alam [pa], Nathan Follens [nl] 3.35.92 ======= * Fix visibility of initially hidden windows [Jonas Å.; !1066] * Avoid flicker when (un)redirecting windows [Sebastian; #997] * Let BindConstraints update the preferred size [Emmanuele; !1070] * Learn about GLES3 [Adam; !882] * Ping windows on every window focus [Jonas D.; !891] * Remove overhead from hot code paths [Christian; #1056, !1081, !1083, !1071, !1087] * Allow remote desktop services to inhibit animations [Jonas Å.; !838] * Update screen-cast code to PipeWire 0.3 API [Wim; !1062] * Make check-alive timeouts configurable [Jonas Å.; !1080] * Make each stage view correspond to a single CRTC [Jonas Å.; !1042] * Implement scaled/transformed hardware cursors [Robert; !526] * Use DMA buffers for screencasting if possible [Georges; !1086] * Make Xwayland startup asynchronous [Carlos; !944] * Fix clipping glitches in long text entries [Jonas D.; !1096] * Add side channel for starting required X11 services [Carlos; !945] * Support synchronized wayland popup moving [Jonas Å.; !705] * Fixed crashes [Olivier, Jonas Å.; !1073, !1093] * Plugged memory leaks [Sebastian, Jonas Å.; !1089, !1095] * Misc. bug fixes and cleanups [Jonas Å, Olivier, Florian, Daniel, Jonas D., Robert, Sebastian, Christian, Arun, Carlos, worldofpeace; !1061, #1043, !1067, !1068, !1065, !835, !1058, !1069, !1075, #1060, !1077, !423, !1090, !1088, !1094, #1067, !1064, !1099, !957, !1000, !1082] Contributors: Emmanuele Bassi, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Christian Hergert, Adam Jackson, Sebastian Keller, Robert Mader, Florian Müllner, Georges Basile Stavracas Neto, Arun Raghavan, Wim Taymans, Daniel van Vugt, worldofpeace, Jonas Ådahl Translators: Yi-Jyun Pan [zh_TW], Asier Sarasua Garmendia [eu], Rafael Fontenelle [pt_BR], Emin Tufan Çetin [tr], Daniel Mustieles [es], Balázs Úr [hu], Gwan-gyeong Mun [ko], Marek Černocký [cs], Fran Dieguez [gl], Kukuh Syafaat [id], Alan Mortensen [da], Piotr Drąg [pl], sicklylife [ja], Matej Urbančič [sl] 3.35.91 ======= * Honor accelerometer orientation on monitor config changes [Hans; !959] * Enable culling for integer-scaled actors [Robert; !1036] * Add ClutterSeat::touch-mode property [Carlos; !1044] * Fix mis-scaling when streaming windows [Olivier; !1022] * Make the cursor renderer use the transactional KMS API [Jonas; !930] * Advertise MetaMonitor as wl_output [Olivier; !994] * Fix culling of XWayland windows [Robert; !1049] * Only consider enabled effects when disabling culling [Robert; !1052] * Misc. bug fixes and cleanups [Olivier, Sergio, Adam, Carlos, Björn; !1040, #985, !1024, !1039, !1051] Contributors: Sergio Costas, Björn Daase, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Adam Jackson, Robert Mader, Jonas Ådahl Translators: sicklylife [ja] 3.35.90 ======= * Cull out clip region [Robert; !985] * Always enable tap-to-click/drag on opaque Wacom tablets [Carlos; !968] * Fix visual glitches with offscreen effects applied [Georges; !992] * Fix "sticky corner" in multi-head setups [Jonas D.; #774] * Fix black shadows around XWayland windows during resizes [Ray, Olivier; #858] * Zero-copy path for GPU-less secondary GPUs [Pekka; !810] * Cancel DND on Esc [Carlos; #1020] * Sync XWayland window shadows to frame during resizes [Olivier; !1009] * Add support for per-monitor workareas [Alberts; !370] * Ensure newly mapped wayland windows receive ENTER event [Olivier; !1026] * Add ClutterSeat object [Carlos; !852] * Honour CLUTTER_ACTOR_NO_LAYOUT flag more efficiently [Daniel; !575] * Fix interoperation with wl_data_device_manager v1 [Carlos; #965] * Favor text over images in clipboard manager [Carlos; #919] * Apply monitor scale after background texture creation [Daniel; !1004] * Plugged memory leaks [Sebastian, Adam; !991, #1000, !1011, !1020, !1030, !1001, !1033] * Fixed crashes [Jonas Å., Florian, Olivier; !961, #1029, !1037] * Misc. bug fixes and cleanups [Björn, Jonas Å., Adam, Sebastian, Jonas D., Daniel, Carlos, Corentin, Sebastian, Robert, Daniel; #385, !998, !1007, !995, !1016, !1018, !1017, !1005, !1019, !1025, !1028, !1029, !1031, !1015, !1032, !1034, #1025] Contributors: Björn Daase, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Adam Jackson, Sebastian Keller, Robert Mader, Alberts Muktupāvels, Florian Müllner, Georges Basile Stavracas Neto, Corentin Noël, Pekka Paalanen, Ray Strode, Daniel van Vugt, Jonas Ådahl Translators: sicklylife [ja], Umarzuki Bin Mochlis Moktar [ms] 3.35.3 ====== * backends/native: Correct dy value in pinch gesture event [Yariv; !974] * Upload clipping rectangles in parallel [Daniel; !969] * More cogl API cleanups [Adam; !978, !977, !973] * Fix window recording on HiDPI [Pascal; !976] * Fix top-left pixel being insensitive to clicks [Sebastian; #893] * Misc. bug fixes and cleanups [Daniel, Adam; !979, !980] Contributors: Yariv Barkan, Adam Jackson, Sebastian Keller, Pascal Nowack, Daniel van Vugt Translators: Fran Dieguez [gl], Dz Chen [zh_CN] 3.35.2 ====== * Don't emit focus event after destruction [Marco; gnome-shell#1704, !860] * Add a notion of pixel format planes [Niels; !858] * Replace various Cogl/Clutter types with Graphene [Georges; !458] * Improve CoglJournal [Georges, Jasper; !402] * Split pick and paint [Georges; !865] * Remove deprecated/unused cogl/clutter APIs [Adam; !866, !878, !879, !880, !885, !900, !902, !904, !896, !913, !922, !883, !903, !921, !933, !819] * Fix hang when opening not-responding dialog on Xorg [Carlos; !876] * Allow changing Clutter debug flags at runtime [Georges; !862] * Fix frozen grabs on Xorg after weeks of inactivity [Jonas; !886] * Fix triggering popups from stylus devices o wayland [Carlos; #886] * Fix fallback to GLES2 [Adam; #635] * Fix buffer age checks on multiple monitors [Carlos; !906] * Adjust to Sysprof API change [Christian; !908] * Improve support for (X11) fullscreen games under wayland [Hans; !739] * Support shadow framebuffers for offscreen rendering [Olivier; !877] * Fix hang after interacting with desktop icons on X11 [Marco; !909] * Don't double scale when getting absolute surface coordinates [Xiang; !915] * Respect NET_WM_TRANSIENT_FOR for override-redirect windows [Marco; !920] * Kill window effects on destroy [Robert; !924] * Remove deprecated ClutterTexture [Jonas; !932] * Use regions instead of bounding box for clipping and culling [Carlos; !867] * Use partial damage for dma-buf and EGLImage buffers on wayland [Robert; #947] * Do not stack transients underneath their always-on-top parent [Florian; #587] * Add explicit paint/pick contexts [Jonas; !935] * Fix KMS freeze after pageflip fallback [Pekka; !953] * Fixed crashes [Robert, Carlos, Jonas, Marco, Hans, Tim; !856, !869, !912, !895, !928, #591, !823, !960] * Plugged memory leaks [Niels, Robert, Carlos, Marco; !847, !868, !873, #908] * Misc. bug fixes and cleanups [Niels, Robert, Jonas, Marco, Carlos, Daniel, Jan, Adam, Cosimo, Florian, Thomas, Georges, Hans, Corentin, Christian, Benjamin; !853, !822, !451, !854, !816, !857, !859, !734, !844, !851, #876, !874, !673, !692, !888, !889, !894, !901, !905, !872, !898, !911, !918, !863, #878, !811, !893, !925, !926, !890, !931, !927, !934, !938, !940, !947, !941, !929, !949, !952, !871, !955, !956, !958, !907, !965, !964, !966] Contributors: Marco Trevisan (Treviño), Jan Alexander Steffens (heftig), Thomas Hindoe Paaboel Andersen, Benjamin Berg, Cosimo Cecchi, Tim Crawford, Piotr Drąg, Xiang Fan, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Niels De Graef, Christian Hergert, Adam Jackson, Robert Mader, Florian Müllner, Georges Basile Stavracas Neto, Bastien Nocera, Corentin Noël, Pekka Paalanen, Jasper St. Pierre, Christian Rauch, Daniel van Vugt, Jonas Ådahl Translators: Bruce Cowan [en_GB] 3.35.1 ====== * Fix immediate screen blank after releaseing inhibitor [Tim; #573] * Respond to frame callbacks regardless of damage [Jonas; !839] * selection [Carlos; !842] * Fix Night Light on wayland [Jonas; !840] * Fix various copy+paste/DND regressions [Carlos; !848, #789, #842, #793, #845, #854] * Misc. bug fixes and cleanups [Daniel, Marco, Jonas, Georges; !841, !764, !837, !846] Contributors: Marco Trevisan (Treviño), Carlos Garnacho, Tim Klocke, Georges Basile Stavracas Neto, Daniel van Vugt, Jonas Ådahl 3.34.1 ====== * Fix startup of X11 session services on wayland [Carlos; #771] * Fix _NET_ACTIVE_WINDOW emission [Carlos; #751] * Fix initial view perspective [Marco; !803] * Fix screenshots and window animations when scaled [Robert; !758] * Re-enable coredumps when capabilities are set [Jonas; !811] * Fix scaling of DND surface actors [Robert; !780] * Optimize blitting of untransformed offscreen stage views [Olivier; !809, !820] * Fix freeze of pointer event delivery on X11 [Olivier; !821] * Fix scaling of stylus input coordinates with HiDPI [Dorian; !830] * Fix memory leak when using implicit animations [Jonas; !828] * Fix numlock state for native backend [Carlos; #769] * Fixed crashes [Marco, Olivier, Jonas Å.; !805, #823, !808, !825, #844, !826, #779] * Misc. bug fixes and cleanups [Jonas Å., Georges, Jonas D., Michal, Daniel, Iain, Adam, Marco, Carlos, Ting-Wei, Hans, Robert; !787, !795, !791, !797, !772, !775, !799, !778, !785, !782, !796, #819, !814, !769, !817, !783, !786, !829, !774, #822] Contributors: Marco Trevisan (Treviño), Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Adam Jackson, Ting-Wei Lan, Iain Lane, Michal Lazo, Robert Mader, Georges Basile Stavracas Neto, Dorian Stoll, Daniel van Vugt, Jonas Ådahl Translators: Milo Casagrande [it], Nathan Follens [nl], Matej Urbančič [sl], Ask Hjorth Larsen [da], Alan Mortensen [da], Jordi Mas [ca] 3.34.0 ====== * Fix xdg-output v3 support [Olivier; !771] * Fix crash when changing decoration state [Jonas; !773] * Add and remove connectors on hot-plug [Jonas; !743] Contributors: Olivier Fourdan, Jonas Ådahl Translators: Rafael Fontenelle [pt_BR], Gwan-gyeong Mun [ko], Christian Kirbach [de], Claude Paroz [fr], Milo Casagrande [it], Emin Tufan Çetin [tr], Ryuta Fujii [ja] 3.33.92 ======= * Turn MetaShapedTexture into a ClutterContent implementation [Georges; !409] * Restore inhibit shortcut for overlay key [Olivier; #734] * Misc. pointer a11y improvements [Jonas D., Olivier; !746, !747, !745, !761] * Fix position of drag surfaces [Robert; !684] * Implement subsurface.place_below() for parents [Robert; !664] * Add meta_window_actor_get_image() [Jonas Å.; !752] * Revert faulty optimization from !719 [Jonas Å.; #735] * Add additional sysprof trace points [Jonas Å.; !757, !765] * Remove GLX "threaded swap wait" used on Nvidia [Daniel; !602] * Implement geometric picking [Daniel; !189] * Fix lost keyboard focus after DND [Olivier; #747] * Misc. bug fixes and cleanups [Florian, Carlos, Piotr, Hans, Georges, Robert, Ray, Mart, Rémi; !740, !672, !749, !751, !753, !730, !755, !756, !750, !715, #738944, !657, !768] Contributors: Jonas Ådahl, Rémi Bernon, Piotr Drąg, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Robert Mader, Florian Müllner, Georges Basile Stavracas Neto, Mart Raudsepp, Ray Strode, Daniel van Vugt Translators: Piotr Drąg [pl], Марко Костић [sr], Rūdolfs Mazurs [lv], Matej Urbančič [sl], Balázs Úr [hu], Fran Dieguez [gl], Jordi Mas [ca], Anders Jonsson [sv], Trần Ngọc Quân [vi], Tim Sabsch [de], Fabio Tomat [fur], Goran Vidović [hr], Marek Černocký [cs] 3.33.91 ======= * Fix primary selection copy and paste between X11 and wayland [Hans; #702] * Improve monitor hotplug support [Hans; !713] * Remove a source of frame skips [Daniel; !719] * Fix windows being lowered after unmaximizing with double click [Olivier; #88] * Remove Clutter API for global grabs [Jonas D.; !536] * Improve processing of incompressible events [Daniel; !711] * Add xdg-output v3 support [Olivier; !704] * Misc. bug fixes and cleanups [Jonas Å., Marco, Carlos, Adam, Albert, Niels, Olivier, Florian; !722, !385, !728, !726, !500, !731, !727, !700, !735, !738] Contributors: Jonas Ådahl, Albert Vaca Cintora, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Niels De Graef, Adam Jackson, Florian Müllner, Marco Trevisan (Treviño), Daniel van Vugt Translators: Asier Sarasua Garmendia [eu], Kukuh Syafaat [id], Florentina Mușat [ro], Aurimas Černius [lt], Daniel Mustieles [es] 3.33.90 ======= * Fix visibility of clones with hidden source [Florian; #683] * Reduce freezes when opening some popup windows [Carlos; #556] * Be more thorough when excluding obscured areas from painting [Carlos; !698] * Make it possible to start Xwayland on demand [Carlos; !709] * clutter: Expose layout_manager to transitions [Florian; !716] * Misc. bug fixes and cleanups [Mark, Florian, Iain, Niels, Carlos, Ray; !671, !691, !694, !696, !703, !707, !697, !710, !708, !714, #719, !721] Contributors: Mark Blakeney, Carlos Garnacho, Niels De Graef, Iain Lane, Florian Müllner, Ray Strode Translators: Asier Sarasua Garmendia [eu], Rafael Fontenelle [pt_BR], Fabio Tomat [fur], Florentina Mușat [ro] 3.33.4 ====== * Discard page flip retries on hotplug [Jonas; !630] * Add xdg-output v2 support [Olivier; #645] * Restore DRM format fallbacks [Jonas; !662] * Don't emit ::size-changed when only position changed [Daniel; !568] * Expose workspace layout properties [Florian; !618] * Don't use grab modifiers when shortcuts are inhibited [Olivier; #642] * Fix stuttering due to unchanged power save mode notifications [Georges; !674] * Add API to reorder workspaces [Adam; !670] * Make picking a new focus window more reliable [Marco; !669] * Defer actor allocation till shown [Carlos; !677] * Try to use primary GPU for copy instead of glReadPixels [Pekka; !615] * Unset pointer focus when the cursor is hidden [Jonas D.; !448] * Fix modifier-drag on wayland subsurfaces [Robert; !604] * Fix background corruption on Nvidia after resuming from suspend [Daniel; !600] * Only grab the locate-pointer key when necessary [Olivier; !685, #647] * Misc. bug fixes and cleanups [Florian, Jonas, Daniel, Robert, Olivier, Georges, Marco, Carlos, Emmanuele; !648, !650, !647, !656, !658, !637, !663, !660, !659, !665, !666, !668, !667, #667, !676, !678, #672, !680, !683, !688, !689, !687] Contributors: Jonas Ådahl, Emmanuele Bassi, Adam Bieńkowski, Piotr Drąg, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Robert Mader, Florian Müllner, Georges Basile Stavracas Neto, Pekka Paalanen, Marco Trevisan (Treviño), Daniel van Vugt Translators: Fabio Tomat [fur], Kukuh Syafaat [id] 3.33.3 ====== * Prepare for running Xwayland on demand [Carlos; !420] * Fix text selection color rendering [Florian; #494] * Fix black shadows when using fractional scaling [Robert; #609] * Honor startup sequence workspace on wayland [Carlos; gnome-shell#674] * Only emit 'grab-op-end` signal after dropping grabs [Marco; !596] * Add a Sysprof-based profiler [Jonas, Georges; !197, !603] * Relax "xwayland-allow-grabs" setting [Olivier; #597] * Implement locate-pointer accessibility feature [Olivier; !453] * Implement mouse accessibility [Olivier; !512] * Consolidate frame throttling [Daniel, Georges; !363] * Fix setting blank cursor under wayland [Jonas; #630] * Pixel-align OpenGL cursors [Jonas; !610] * Handle returning from fullscreen/maximization better [Jonas; !621] * Improve screencast support on multi-monitor systems [Georges; !623] * Fix running X11 applications with sudo under wayland [Hans; #643] * Implement toggle-keys notification [Olivier; #637] * Add initial KMS transactional support [Jonas; !525] * Improve finding new focus window when the old one is closed [Marco; #308] * Misc. bug fixes and cleanups [Jonas, Carlos, Marco, Florian, Pekka, Robert, Douglas, Georges, Daniel, Emil, Niels, Hans, Olivier, Ting-Wei, Corentin; !591, #398, !592, !581, !597, !598, !593, !497, #591, !545, gtk#1675, !601, #568, !564, !605, !609, !115, !214, !611, !617, !616, !619, !624, !622, !627, !628, !629, !632, !633, !631, !636, !639, !638, !634, !640, !529, !644, !590] Contributors: Jonas Ådahl, Piotr Drąg, Olivier Fourdan, Carlos Garnacho, Hans de Goede, Niels De Graef, Ting-Wei Lan, Robert Mader, Florian Müllner, Georges Basile Stavracas Neto, Corentin Noël, Pekka Paalanen, Douglas R. Reno, Marco Trevisan (Treviño), Emil Velikov, Daniel van Vugt Translators: Balázs Úr [hu], Daniel Mustieles [es], Nathan Follens [nl], Goran Vidović [hr] 3.33.2 ====== * Fix rendering lag on Xorg [Daniel; !520, !281] * Misc. bug fixes and cleanups [Carlos, Marco, Jonas D., Florian, Niels, Daniel, Benjamin, Jonas Å., Ignacio, Vasilis; #598, !576, !547, !578, !583, !582, !469, !524, !119, !571, !584, !585, !586, #425] Contributors: Jonas Ådahl, Benjamin Berg, Jonas Dreßler, Carlos Garnacho, Niels De Graef, Vasilis Liaskovitis, Florian Müllner, Ignacio Casal Quinteiro, Marco Trevisan (Treviño), Daniel van Vugt Translators: Daniel Mustieles [es] 3.33.1 ====== * Remove unused APIs and outdated driver support [Adam; !481, !468, !489, !487, !546] * Enable EGL_IMG_context_priority [Adam; !454] * Disable mouse keys with Numlock on [Olivier; #530] * Fix crash when restarting on X11 [Marco; #576] * Implement clipboard manager [Carlos; !320] * Fix spurious idle signals that prevent session unblank [Jonas Å.; !543] * Fix mapping of touchscreens that don't report dimensions [Carlos; #581] * Fix propagating fractional scaling factor [Robert; !537] * Add experimental RT scheduling support [Carlos; !460] * Misc. bug fixes and cleanups [Robert, Carlos, Olivier, Ray, Marco, Jonas D., Georges, Daniel V., Daniel M; !467, !504, !551, !552, #575, #556, !557, !442, !562, !535, !548, #586, !567, !396, !422, !507] Contributors: Jonas Ådahl, Piotr Drąg, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Adam Jackson, Robert Mader, Daniel García Moreno, Florian Müllner, Georges Basile Stavracas Neto, Ray Strode, Marco Trevisan (Treviño), Daniel van Vugt Translators: Daniel Mustieles [es], Fabio Tomat [fur], Kukuh Syafaat [id] 3.32.1 ====== * Fix fallback app menu on wayland [Florian; #493] * Fix elogind support [Tom; !491] * Fix startup notifications not timing out [Carlos; #501] * Fix keyboard accessibility toggle from keys [Olivier, Carlos; !501, #529, !531] * Fix touchscreen input on rotated displays [Carlos; #514] * Work around hangul text input bug [Carlos; #1365] * Fix blurry wallpaper scaling [Daniel; !505] * Fix placement of window menu when using fractional scaling [Jan; #527] * Fix repaint issues of offscreen effects on secondary monitors [Daniel; !511] * Fix windows not getting focus after launch [Daniel; #505] * Properly advertise support for 'underscan' property [Jonas; !507] * Improve power-saving handling [Jonas; !506] * Fix moving windows by super+touch [Jonas D.; !495] * Misc. bug fixes and cleanups [Benjamin, Florian, Adam, Marco, Pablo, Erik, Jonas, Heiher, Pekka, Daniel, Olivier, Carlos; !478, !475, !480, !482, #490, !488, #491, #480, !477, !496, !492, !485, !515, !519, !521, !216, !538, #541, #523] Contributors: Jonas Ådahl, Pablo Barciela, Benjamin Berg, Tom Briden, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Jan Alexander Steffens (heftig), Heiher, Adam Jackson, Erik Kurzinger, Florian Müllner, Pekka Paalanen, Marco Trevisan (Treviño), Daniel van Vugt Translators: Khaled Hosny [ar], Goran Vidović [hr], Daniel Mustieles [es] 3.32.0 ====== * Fix deadlock when cancelling a theme sound [Andrea; !474] * Stop swizzling BGRA buffers (bye-bye inverted colors in screenshots and animations) [Carlos; !486] Contributors: Andrea Azzarone, Carlos Garnacho, Robert Mader 3.31.92 ======= * Fix flicker of apps that use multiple SHM buffers [Jonas Å.; #199] * Don't disable page flips after temporary failues [Jonas Å.; #460] * Improve redraw performance [Carlos; !196] * Add cursor-mode support to window screencasting [Jonas Å.; !413] * Add back support for system-wide monitor configurations [Jonas Å.; !253] * Add fractional scaling support [Marco, Jonas Å.; !3] * Consider remapped keys when guessing keycode from keysym [Andrea; #443] * Stop turning on-screen-keyboard off on focus changes [Carlos; !432] * Fix crashes [Robert, Carlos, Jonas D., Florian; !447, #361, !426, #479] * Misc. bug fixes and cleanups [Benjamin, Adam, Olivier, Niels, Piotr; !457, !452, !459, !380, !361, !461, !464, !471, !473, !463] Contributors: Jonas Ådahl, Andrea Azzarone, Benjamin Berg, Piotr Drąg, Jonas Dreßler, Olivier Fourdan, Carlos Garnacho, Niels De Graef, Adam Jackson, Robert Mader, Florian Müllner, Marco Trevisan (Treviño) Translators: Milo Casagrande [it], Tim Sabsch [de], Trần Ngọc Quân [vi], Gwan-gyeong Mun [ko], Марко Костић [sr], Daniel Mustieles [es], Rūdolfs Mazurs [lv], Nathan Follens [nl] 3.31.91 ======= * Fix infinite loop in EDID matching [Marco; #459] * wayland: Don't resetin text-input state prematurely [Carlos; !410] * wayland: Don't maximize windows if minimum size is too big [Olivier; #463] * Fix crash when using "restore shortcuts" without focus window [Olivier; #464] * Add flag parameter to grab accelerator API [Andrea; !169] * Reuse old CRTC if possible to avoid flicker on hotplug [Pekka, Emilio; #373] * Misc. bug fixes and cleanups [Marco, Jonas, Niels, Adam, Olivier; !436, !421, #462, !439, !440, !444, !321, !445, !456] Contributors: Jonas Ådahl, Andrea Azzarone, Olivier Fourdan, Carlos Garnacho, Niels De Graef, Adam Jackson, Emilio Pozuelo Monfort, Pekka Paalanen, Marco Trevisan (Treviño) Translators: Jiri Grönroos [fi], Charles Monzat [fr], Claude Paroz [fr], Fran Dieguez [gl], Emin Tufan Çetin [tr], Aurimas Černius [lt], Anders Jonsson [sv], Matej Urbančič [sl], Marek Cernocky [cs], Daniel Șerbănescu [ro], Alan Mortensen [da], Baurzhan Muftakhidinov [kk], Yi-Jyun Pan [zh_TW], Daniel Mustieles [es], Rafael Fontenelle [pt_BR] 3.31.90 ======= * Fix support of extended characters in on-screen keyboard [Andrea; #109] * Improve selection of the primary GPU [Pekka, Emilio; !271] * Screen-cast cursor updates as PipeWire stream metadata [Jonas; !357] * Fix rendering glitches in magnifier [Daniel; gnome-shell#387] * Fix monitor recording on HiDPI [Jonas; !415] * Honour secondary GPU supported pixel formats [Pekka; !341] * Fall back to CPU copy path when using a software renderer [Emilio; !325] * Remove fallback app menu [Florian; gnome-shell#624] * wayland: Add support for viewporter protocol [Robert; !323] * Misc. bug fixes and cleanups [Florian, Carlos, Olivier, Marco, Robert, Daniel, Pekka, Jonas, Ole, Georges; !391, #335, #442, !406, !395, #447, !375, gnome-shell#349, #451, !416, #784199, !408, !181, !405] Contributors: Jonas Ådahl, Andrea Azzarone, Ole Jørgen Brønner, Piotr Drąg, Olivier Fourdan, Dariusz Gadomski, Carlos Garnacho, Antoine Jacoutot, Iain Lane, Robert Mader, Emilio Pozuelo Monfort, Florian Müllner, Georges Basile Stavracas Neto, Pekka Paalanen, Marco Trevisan (Treviño), Josh Triplett, Daniel van Vugt Translators: Fabio Tomat [fur], Balázs Úr [hu], Daniel Mustieles [es], Kukuh Syafaat [id], Jordi Mas [ca], Piotr Drąg [pl] 3.31.4 ====== * keybindings: Limit corner move to current monitor [Jānis; #320] * xdg-output: Report rotated physical dimensions [Olivier; #369] * Add continuous integration pipeline [Jonas; #193] * Improve performance on secondary GPUs [Pekka; #323, !313] * Use the actual hardware refresh rate [Daniel; #781296] * Remove hide-titlebar-when-maximized support [Florian; !221] * wayland: Implement buffer transforms [Robert; !322] * Remove ability to externally set sync-to-vblank [Georges; !191] * Turn off touchscreens together with DPMS [Carlos; gnome-settings-daemon#29] * Mipmap the wallpaper when shrinking [Daniel; gnome-shell#254] * Implement RecordWindow method for screen-casts [Olivier; !306] * Fix EGLStream texture downloading [Jonas; !362] * Split out display-server-specific code from MetaWindowActor [Georges; !368] * Improve render performance on some KMS devices with software GL [Jonas; #106] * Fix damage area of transformed surfaces [Robert; !366] * Remove autotools support [George] * Misc. bug fixes and cleanups [Jonas, Alan, Olivier, Carlos, Javier, Peter, Daniel, Robert, Florian; !309, #790207, #272, #393, #276, #404, #104, !343, #765011, #786663, #342, !356, #414, #782344, #781034, #423, !374, !382, !383] Contributors: Jonas Ådahl, Nikita Churaev, Alan Coopersmith, Jānis Džeriņš, Olivier Fourdan, Carlos Garnacho, Niels De Graef, Peter Hutterer, Javier Jardón, Abderrahim Kitouni, Andre Klapper, Ting-Wei Lan, Robert Mader, Emilio Pozuelo Monfort, Florian Müllner, Georges Basile Stavracas Neto, Pekka Paalanen, Daniel Stone, Marco Trevisan (Treviño), Daniel van Vugt 3.31.2 ====== * Fix handling of non-UTF8 encodings [Florian; !227] * Fix memory leaks introduced in 3.30.1 [Jonas; #653] * Fix regression when overriding workspace layout [Ron; #270] * Fix crash when restarting window manager [Andrea; gnome-shell#595] * Add meson build support [Jonas; !167] * Freeze clock when headless [Jonas; !170] * Fix crash on monitor hotplug [Olivier; #189] * Misc. bug fixes [Jonas; #353, !132, #382] Contributors: Jonas Ådahl, Andrea Azzarone, Olivier Fourdan, Niels De Graef, Alexander Mikhaylenko, Florian Müllner, Akira Nakajima, Georges Basile Stavracas Neto, Pekka Paalanen, Peter Uithoven, Daniel van Vugt, Ron Yorston 3.30.1 ====== * Improve trackball detection [Tony; #258] * Fix clipping of scaled surfaces [Jonas; #300] * Improve tracking of monitor switch configuration [Daniel; !213] * Fix parent-relative positioning of constrained windows [Jonas; #332] * Add clutter_input_method_forward_key() method [Carlos; gnome-shell#531] * Various crash fixes [Olivier, Jonas; #194, #336] * Misc. bug fixes [Carlos, Florian, Olivier, Jonas; gnome-shell#540, #294, #221, !229, #30, #331] Contributors: Jonas Ådahl, Daniel Drake, Olivier Fourdan, Carlos Garnacho, Peter Hutterer, Ting-Wei Lan, Florian Müllner, Tony Novak, Pekka Paalanen, Sam Spilsbury Translators: Yuras Shumovich [be], Марко Костић [sr], Marek Cernocky [cs] 3.30.0 ====== Translators: Fran Dieguez [gl], Balázs Meskó [hu], Rūdolfs Mazurs [lv], Trần Ngọc Quân [vi], Ask Hjorth Larsen [da], gogo [hr] 3.29.92 ======= * Avoid crash when a cursor is not found [Sebastian; #254] * Fix screen rotation regression [Jonas; #216] * Handle requests to unmanaged windows gracefully [Jonas; #240] * Move popups together with their parent [Jonas; #274] * Fix non-lowercase letters on virtual key devices [Carlos; gnome-shell#135] * Misc. bug fixes [Iain, Jonas; #223, #192, #279] Contributors: Jonas Ådahl, Carlos Garnacho, Sebastian Keller, Iain Lane, Robert Mader, Daniel van Vugt Translators: Gwan-gyeong Mun [ko], Kukuh Syafaat [id], Milo Casagrande [it], Anders Jonsson [sv], Rafael Fontenelle [pt_BR], Marek Cernocky [cs] 3.29.91 ======= * Various crash fixes [Olivier, Iain; #255, #223] * Fix lock up with some DRI drivers [Alex; #127] * Send correct button codes from virtual evdev devices [Jonas; !190] * Improve grab-device clock updates on X11 [Jeff; !174] * Fix popups closing immediately on key down [Jonas; !180] * Prevent clients from modifying the shared keymap [Jonas; #784206] Contributors: Jonas Ådahl, Andrea Azzarone, Piotr Drąg, Olivier Fourdan, Carlos Garnacho, Jan Grulich, Iain Lane, Alex Villacís Lasso, Jeff Smith, Daniel van Vugt Translators: Matej Urbančič [sl], Mario Blättermann [de], Piotr Drąg [pl], Aurimas Černius [lt], Yi-Jyun Pan [zh_TW], Emin Tufan Çetin [tr], Fabio Tomat [fur], Bruce Cowan [en_GB] 3.29.90 ======= * Various crash fixes [Olivier, Jonas, Florian; #189, #70, #194, #15, #130] * Don't expose resolutions that are below the minimum [Andrea; #793223] * Remove support for preference overrides [Florian; #786496] * Misc. bug fixes and cleanups [Daniel, Jonas, Florian; #131, #245, !176] Contributors: Jonas Ådahl, Andrea Azzarone, Olivier Fourdan, Florian Müllner, Kevin Tamool, Daniel van Vugt Translators: Daniel Mustieles [es], Claude Paroz [fr] 3.29.4 ====== * Fix crash with parent-less modal dialogs [Olivier; #174] * Preserve paint volumes where possible to optimize CPU usage [Carlos; #782344] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Iain Lane, Bastien Nocera Translators: Daniel Șerbănescu [ro] 3.29.3 ====== * Fix Korean Hangul support on wayland [Changwoo; #152] * Improve support for proprietary Nvidia driver [Jonas; #790316] * Only upload HW cursor sprite to the GPU that will display them [Jonas; #77] * Improve EGLstream support [Miguel; #2, #782575] * Remove MetaScreen to prepare for non-mandatary X11 dependency [Armin, Jonas; #759538] * Misc. bug fixes [Olivier, Jonas, Sam; #160, !130, #786929, #788834] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Armin Krezović, Corentin Noël, Changwoo Ryu, Sam Spilsbury, Daniel Stone, Marco Trevisan (Treviño), Miguel A. Vico, Daniel van Vugt Translators: Yi-Jyun Pan [zh_TW], Jordi Mas [ca], Daniel Șerbănescu [ro], Fabio Tomat [fur] 3.29.2 ====== * Fix size change animations on wayland [Georges; #780292] * Handle touch events on server-side titlebars [Carlos; #770185] * Misc. bug fixes [Florian, Olivier, Jonas, Georges; #134, #124, !96, #138, !102, #781471, #150] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Florian Müllner, Georges Basile Stavracas Neto, Marco Trevisan (Treviño), Daniel van Vugt Translators: Daniel Șerbănescu [ro], Marcos Lans [gl], Dz Chen [zh_CN] 3.29.1 ====== * Fix various input-method regressions [Carlos, Olivier; #65, #74, #66, #112] * Fix wayland build on FreeBSD [Ting-Wei; #792280, #792717] * Fix swapped colors in screenshots (again) [Carlos; #72] * Allow building with elogind [Rasmus; !46] * Consider display rotation for cursor [Olivier; #85] * Fall back to non-modifier GBM surfaces [Daniel; #84] * Take inhibitors into account for monitoring idle [Bastien; #705942] * Misc. bug fixes [handsome-feng, Olivier, Mario, Jonas; !45, #83, #104, gnome-shell#157, #130, #21] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, handsome-feng, Yussuf Khalil, Ting-Wei Lan, Aleksandr Mezin, Alberts Muktupāvels, Georges Basile Stavracas Neto, Bastien Nocera, Benjamin Otte, Mario Sanchez Prada, Daniel Stone, Ray Strode, Rasmus Thomsen, Marco Trevisan (Treviño), Daniel van Vugt Translators: Emin Tufan Çetin [tr], Dušan Kazik [sk], Matej Urbančič [sl] 3.28.0 ====== * Fix xdg-foreign regression [Carlos; #63] Contributors: Carlos Garnacho, Georges Basile Stavracas Neto Translators: Marek Cernocky [cs], Ask Hjorth Larsen [da], Chao-Hsiung Liao [zh_TW], Anders Jonsson [sv], Mart Raudsepp [et] 3.27.92 ======= * Fix use of modifiers with multi-GPU systems [Louis-Francis; #18] * Add xdg-shell stable support [Jonas; #791938] * Fix scaling of icons in titlebar buttons [Egmont; #23] * Implement missing wacom functionality on X11 [Carlos; #48] * Force 8-bit RGB config [Jonas; #2] * Misc. bug fixes [Jonas, Olivier, Robert; #6, #27, #792203] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Egmont Koblinger, Robert Mader, Bastien Nocera, Louis-Francis Ratté-Boulianne Translators: Daniel Mustieles [es], Марко Костић [sr], Милош Поповић [sr@latin], Fran Dieguez [gl], Balázs Úr [hu], Gwan-gyeong Mun [ko], Rūdolfs Mazurs [lv], Milo Casagrande [it], Mario Blättermann [de], GNOME Translation Robot [gd, nl], Claude Paroz [fr], Aurimas Černius [lt] 3.27.91 ======= * Fix handling of trackball settings on wayland [Carlos; #787804] * Apply font settings on wayland [Daniel; #645433] * Fix keybindings getting mixed up with some layouts [Jonas; #789300] * Fix bluetooth mouse cursor disappearing after idle [Benoit; #761067] * Support platforms that export EGL_KHR_platform_gbm [memeka; #780668] * Add keyboard accessibility support on wayland [Olivier; #788564] * Fix missing cursor when using screen magnifier [Carlos; #754806] * Fix external monitor shutting off on wayland when lid closes [Jonas; #788915] * Add xdg-output support [Olivier; #787363] * Add Xwayland grab keyboard support [Olivier; #783342] * Allow shortcut inhibition of the super key [Olivier; #790627] * Take "panel orientation" drm_connector property into account [Hans; #782294] * Fix focus window ending up below other windows on wayland [Olivier; #780820] * Fix maximized windows restoring to a tiny size on wayland [Olivier; #783901] * Fix tap-and-drag setting on X11 [Jonas; #775755] * Fix handling of single-touch devices on wayland [Carlos; #792005] * Support tiled/compressed buffers [Daniel; #785779] * Port screencast support to pipewire 0.1.8 [Jonas; #792854] * Add support for third stylus button on newer tablets [Jason; #790033] * Fix background corruption regression on nvidia [Jonas; #739178] * Misc. bug fixes [Jonas, Rui, Michael, Marco, Carlos, Olivier, Philip, Piotr, Ting-Wei, Daniel, Jeremy, Hans, Florian, Ray, Jeff, George, Gwan-gyeong; #789153, #788493, #784314, #789227, #789223, #789277, #782344, #789552, #789553, #788695, #789984, #788764, #789386, #784545, #790336, #790358, #791022, #791006, #789070, #772218, #791383, #791809, #776220, #791916, #792281, #790309, #791371, #792527, #792599, #788834, #792765, #792062, #645460, #792853, !2, #792818, #8, #12, #789501, #10, #789961, #13, !15, #1, #26, #28, #35, #36, #38] Contributors: Jonas Ådahl, Jeremy Bicha, Michael Catanzaro, Piotr Drąg, Olivier Fourdan, Carlos Garnacho, Jason Gerecke, Hans de Goede, Benoit Gschwind, Peter Hutterer, George Kiagiadakis, Ting-Wei Lan, Rui Matos, memeka, Florian Müllner, Gwan-gyeong Mun, Jeremy Nickurak, Marc-Antoine Perennou, Jeff Smith, Daniel Stone, Ray Strode, Marco Trevisan (Treviño), Daniel van Vugt, Philip Withnall Translators: Khaled Hosny [ar], Kjartan Maraas [nb], Piotr Drąg [pl], Rafael Fontenelle [pt_BR], Christian Kirbach [de], Anders Jonsson [sv], Charles Monzat [fr], Marek Cernocky [cs], Muhammet Kara [tr], Milo Casagrande [it], Pawan Chitrakar [ne], Yosef Or Boczko [he], Kukuh Syafaat [id], Daniel Mustieles [es], Fabio Tomat [fur], Kristjan SCHMIDT [eo], Balázs Úr [hu], Andika Triwidada [id], Fran Dieguez [gl], gogo [hr] 3.27.1 ====== * Work with clients that require older linux_dmabuf protocol [Daniel; #788558] * Support hybrid GPU systems [Jonas; #785381] * Prevent crash when closing maximized windows [Jonni; #788666] * Use the correct monitor for HiDPI scaling of shell chrome [Jonas; #788820] * Fix unredirection of fullscreen windows [Rui, Jonas; #788493] * Fix list of supported monitor scales on X11 [Jonas; #788901] * Misc. bug fixes [Florian, Jonas, Marco; #788572, #788569, #788607, #788860, #788921] Contributors: Jonas Ådahl, Carlos Garnacho, Rui Matos, Florian Müllner, Daniel Stone, Marco Trevisan, Jonni Westphalen Translations: Xavi Ivars [ca@valencia] 3.26.1 ====== * Fix crash when respawning shortcut inhibitor dialog [Olivier; #787568] * Fix crash during monitor configuration migration [Carlos, Jonas; #787668] * Fix multihead regressions in X11 session [Jonas; #787477] * Fix screen rotation regressions [Hans; #787836] * Fix keybindings not being resolved with non-latin layouts [Jonas; #787016] * Support snap packages for sandboxed app IDs [Marco; #788217] * Fix crash when reconnecting tablet device [Jason; #787649] * Support running headless [Jonas; #730551, #787637] * Support _NET_RESTACK_WINDOW and ConfigureRequest siblings [Vasilis; #786365] * Fix monitor layout not being remembered across sessions [Jonas; #787629] * Make sure to export _NET_NUMBER_OF_DESKTOPS [Florian; #760651] * Allow resizing of tiled windows [Georges, Florian; #645153] * Export tiling information to clients [Georges; #751857] * Misc. bug fixes [Jonas, Florian, Jeremy, Rico; #787570, #787715, #787953, #788049, #788199, #788292, #788197] Contributors: Jonas Ådahl, Andrea Azzarone, Georges Basile Stavracas Neto, Hans de Goede, Olivier Fourdan, Carlos Garnacho, Jason Gerecke, Vasilis Liaskovitis, Rui Matos, Florian Müllner, Jeremy Soller, Marco Trevisan, Rico Tzschichholz Translations: Matej Urbančič [sl], gogo [hr], Cheng-Chia Tseng [zh_TW] 3.26.0 ====== Contributors: Florian Müllner Translations: Trần Ngọc Quân [vi], Inaki Larranaga Murgoitio [eu], Jordi Mas [ca], Anders Jonsson [sv], Alexander Shopov [bg], Ask Hjorth Larsen [da], Jean-Baptiste Holcroft [fr], A S Alam [pa] 3.25.92 ======= * Add screencast and remote desktop support [Jonas; #784199] * Support running with no attached monitors [Jonas; #730551] * Add a vertical gradient effect to background actor [Alessandro; #786618] * Misc. bug fixes [Mario, Daniel, Piotr, Jonas, Bastien; #786619, #786677, #772218, #786918, #760670] Contributors: Jonas Ådahl, Alessandro Bono, Piotr Drąg, Bastien Nocera, Mario Sanchez Prada, Daniel Stone Translations: Marek Cernocky [cs], Aurimas Černius [lt], Piotr Drąg [pl], Fran Dieguez [gl], gogo [hr], Dušan Kazik [sk], Milo Casagrande [it], Jordi Mas [ca], Cheng-Chia Tseng [zh_TW], Марко Костић [sr], Милош Поповић [sr@latin], Rūdolfs Mazurs [lv], Matej Urbančič [sl], Ask Hjorth Larsen [da], Piotr Drąg [it, lt], Jiri Grönroos [fi], Emin Tufan Çetin [tr], Wolfgang Stöggl [de], Kukuh Syafaat [id], Yuras Shumovich [be], Changwoo Ryu [ko], Alexander Shopov [bg], Rafael Fontenelle [pt_BR], Balázs Úr [hu] 3.25.91 ======= * Reduce memory use of suspended instances [Jonas; #786299] * Make supported scales determination saner [Rui; #786474] * Fix crash on inhibit-shortcuts dialog reponse [Jonas; #786385] * Support libinput's tag-and-drag setting [freeroot; #775755] * Avoid overlapping keybindings with multiple layouts [Jonas; #786408] * Fix non-transformed cursor on rotated monitors [Jonas; #786023] * Avoid unnecessary work during background painting [Alessandro; #783512] * Misc. bug fixes [Alberts, Jonas, Mario; #691611, #786300, #777732, #786568] Contributors: freeroot, Jonas Ådahl, Alessandro Bono, Carlos Garnacho, Rui Matos, Alberts Muktupāvels, Mario Sanchez Prada Translations: Muhammet Kara [tr], Claude Paroz [fr], Мирослав Николић [sr, sr@latin], Pawan Chitrakar [ne], Kukuh Syafaat [id] 3.25.90 ======= * Add zwp_linux_dmabuf_v1 support [Daniel; #785262] * Add (x)wayland shortcut inhibitor support [Olivier; #783342] * Misc. bug fixes [Daniel, Carlos, Cosimo; #785263, #785347, #767805] Contributors: Jonas Ådahl, Cosimo Cecchi, Olivier Fourdan, Carlos Garnacho, Daniel Stone Translations: Fabio Tomat [fur], Kukuh Syafaat [id], Aurimas Černius [lt], Daniel Mustieles [es], Baurzhan Muftakhidinov [kk], Jordi Mas [ca], Matej Urbančič [sl], Marek Cernocky [cs], gogo [hr], Fran Dieguez [gl], Balázs Meskó [hu] 3.25.4 ====== * Do not throttle motion events on tablet tools [Carlos; #783535] * Handle left-handed mode on pen/eraser devices [Carlos; #782027] * Add wl_surface.damage_buffer() support [Jonas; #784080] * Fix crash when moving across on-adjacent monitors [Jonas; #783630] * Fix window moving/resizing via tablet tools [Jason; #777333] * Support fractional monitor scaling [Jonas, Marco; #765011] * Keep override-redirect windows stacked on top [Rui; #780485] * Implement tablet rings/strips configuration [Carlos; #782033] * Support tablet wheel events on wayland [Jason; #783716] * Move g-s-d xrandr functionality into mutter [Rui; #781906] * Misc. bug fixes [Florian, Jason, Miguel, Carlos, Jonas; #783502, #784009, #784223, #784272, #784402, #784881, #762083, #784867, #781723] Contributors: Jonas Ådahl, Miguel A. Vico, Emmanuele Bassi, Carlos Garnacho, Jason Gerecke, Rui Matos, Florian Müllner, Marco Trevisan (Treviño) 3.25.3 ====== * Ignore hotplug-mode-update value on startup [Marco; #783073] * Implement configurable monitor scales on X11 [Jonas; #777732] * Fix handling of tiled monitors [Jonas; #781723] * Handle multiple keycodes for keysym [Christian; #781223] * Consider subsurfaces when grabbing [mindtree; #781811] * Fix logic for HiPDPI scaling of TV outputs [Christian; #777347] * Fix handling of left-handed mode on pen/eraser devices [Carlos; #782027] * Fix output cycling in non-display-attached tablets [Carlos; #782032] * Fix wacom cursor offset on wayland [Jason; #784009] * Handle EXIF orientation of backgrounds [Silvère; #783125] * Misc. bug fixes [Piotr, Tim, Bastien, Jonas, Florian, Benoit, Carlos; #772218, #783161, #780407, #783113, #783293, #783505, #781703] Contributors: mitchmindtree, Jonas Ådahl, Ikey Doherty, Piotr Drąg, Carlos Garnacho, Jason Gerecke, Benoit Gschwind, Christian Kellner, Silvère Latchurié, Tim Lunn, Florian Müllner, Bastien Nocera, Marco Trevisan (Treviño) Translations: Fabio Tomat [fur], Kukuh Syafaat [id], Khaled Hosny [ar], Daniel Mustieles [es] 3.25.2 ====== * Fix frame updates on hide-titlebar-when-maximized changes [Florian; #781862] * Fix accessible screen coordinates on X11 [Florian; #781902] * Use less CPU when rendering fast-updating windows [Carlos, Emmanuele; #782344] * Compute geometry of clients that don't set one explicitly [Olivier; #782213] * Fix copy+paste of UTF8 strings between X11 and wayland [Carlos; #782472] * Fix non-wayland builds [Chris; #780533] * Add plugin vfunc to implement a custom force-quit dialog [Carlos; #711619] * Fix swapped red and blue channels in CoglTexture data [Carlos; #779234 * Fix build where libtool's link_all_deplibs defaults to 'no' [Marco; #782821] * Fix glitches when opening a window maximized [Olivier; #781353, #782183] * Fix wrong cursor after window underneath the pointer changed [Carlos; #755164] * Implement support for disable-while-typing option [Evan; #764852] * Emit size-change signal when tiling [Alessandro; #782968] * Misc. bug fixes [Nigel, Matthias, Jonas; #759085, #780215, #782156, #782152] Contributors: Jonas Ådahl, Emmanuele Bassi, Alessandro Bono, Olivier Fourdan, Carlos Garnacho, Matthias Liertzer, Florian Müllner, Nigel Taylor, Marco Trevisan (Treviño), Chris Vine, Evan Welsh Translations: Fabio Tomat [fur], Jordi Mas [ca], Mario Blättermann [de], Emin Tufan Çetin [tr], Balázs Úr [hu] 3.25.1 ====== * Always sync window geometry on state changes [Jonas; #780292] * Use EGL instead of GLX when drawing using GLES [Jonas; #771636] * Fix HiDPI detection on vertical monitor layouts [Carlos; #777687] * Get double-click timing from desktop mouse settings [Armin; #771576] * Scale relative motion deltas with monitor scale [Jonas, Carlos; #778119] * Use texture fallback when setting hardware cursor fails [Jente; #770020] * Fix lock-up when using additional theme variants [Shantanu; #780254] * Rework low-level monitor configuration [Jonas; #777732] * Fix building with GLES2 instead of GL [Mario; #781398] * Misc. bug fixes [Jonas, Piotr, Philip; #780304, #772218, #781242, #781391] Contributors: Jonas Ådahl, Philip Chimento, Piotr Drąg, Carlos Garnacho, Shantanu Goel, Jente Hidskes, Armin Krezović, Rui Matos, Florian Müllner, Mario Sanchez Prada Translations: Yuras Shumovich [be], Yosef Or Boczko [he], Tom Tryfonidis [el], Fabio Tomat [fur], Kukuh Syafaat [id] 3.24.0 ====== Translations: Yuri Myasoedov [ru], Rūdolfs Mazurs [lv], Jordi Mas [ca] 3.23.92 ======= * Properly handle EGLOutput acquire errors [Jonas, Miguel; #779112] * Fix crash when a window closes during Alt+Tab [Rui; #779483] * Implement DnD handling code in wayland [Hyungwon; #765003] * Fix fallout from pixel conversion optimization in 3.23.91 [Carlos; #779234] * Fix mouse input stopping to work in applications [Carlos; #763246] * Fix DnD between QT5 and GTK3 applications on wayland [Carlos; #779757] * Make EDID reading less fragile [Jonas; #779837] * Add support for tablet grouping [Carlos; #779986] * Misc. bug fixes and cleanups [Rui, Jonas; #779436, #779001, #779745] Contributors: Jonas Ådahl, Miguel A. Vico, Olivier Fourdan, Carlos Garnacho, Hyungwon Hwang, Rui Matos Translations: Chao-Hsiung Liao [zh_TW], Sveinn í Felli [is], Ask Hjorth Larsen [da], Changwoo Ryu [ko], Aurimas Černius [lt], GNOME Translation Robot [gd], Marek Černocký [cs], Fran Dieguez [gl], Dušan Kazik [sk] 3.23.91 ======= * Give libinput read-only access to /sys [Carlos; #778472] * Allow edge-scrolling without 2-finger-scroll capable devices [Rui; #778554] * Fullscreen windows on the requested monitor on wayland [Rui; #772525] * Implement threaded swap_event fallback for NVIDIA driver [Owen; #779039] * Avoid pixel conversions when storing textures from cairo [Carlos; #779234] * Misc. bug fixes [Piotr, Rui, Florian; #772218, #776919, #778831, #642652] Contributors: Piotr Drąg, Carlos Garnacho, Rui Matos, Florian Müllner, Owen W. Taylor Translations: Inaki Larranaga Murgoitio [eu], Daniel Mustieles [es], Claude Paroz [fr], Mario Blättermann [de], Kjartan Maraas [nb], Piotr Drąg [pl], Andika Triwidada [id], Anders Jonsson [sv], Milo Casagrande [it], Fabio Tomat [fur], Rafael Fontenelle [pt_BR], Мирослав Николић [sr, sr@latin], Balázs Meskó [hu], Chao-Hsiung Liao [zh_TW] 3.23.90 ======= * Fix window menu placement with HiDPI [Jonas; #776055] * Improve EGLStream support [Jonas; #773629] * Start moving low-level monitor configuration into mutter [Jonas; #777732] * Fix erroneous key event repeats [Rui; #774989] * Don't hardcode seat ID in ClutterDeviceManager [Carlos; #778092] * Fix "ghost" cursors in multi-monitor setups [Jonas; #771056] * Use eglGetPlatformDisplay [Adam; #772422] * Fix erratic raise_or_lower behavior [Jose; #705200] * Fix coordinate mapping of absolute devices [Carlos; #774115] * Show OSD on tablet mode switches [Carlos; #771098] * Make mutter libs parallel installable [Jonas; #777317] * Only apply keymap when not running nested [Jonas; #777800] * Set right scale for tablet tool cursors on HiDPI [Carlos; #778474] * Adjust server-side shadows to match Adwaita [Juraj; #744667] * Misc. bug fixes [Jonas, Bastien, Carlos, Peter, Lionel, Jeremy, Florian; #774891, #777389, #777691, #778262, #776543, #778684, #778699, #744667] Contributors: Jonas Ådahl, Jeremy Bicha, Piotr Drąg, Juraj Fiala, Carlos Garnacho, Peter Hutterer, Adam Jackson, Lionel Landwerlin, Jose Marino, Rui Matos, Florian Müllner, Bastien Nocera Translations: Kjartan Maraas [nb], Mandy Wang [zh_CN], Marek Černocký [cs], Anders Jonsson [sv], Dušan Kazik [sk], Piotr Drąg [pl], Matej Urbančič [sl] 3.23.3 ====== * Fix frequent freezes in multihead setups on wayland [Rui; #774557] * Preserve root window mask on XSelectionRequest [Olivier; #776128] * Misc. bug fixes [Carlos, Florian, Rui, Olivier; #775478, #774891, #775986, #776036] Contributors: Olivier Fourdan, Carlos Garnacho, Rui Matos, Florian Müllner 3.23.2 ====== * Stack docks below other windows on fullscreen monitors [Rui; #772937] * Fix popup grabs blocking screen lock on wayland [Rui; #771235] * Handle touchpad pinch gestures with more than two fingers [Carlos; #765937] * Implement drawing tablet support on X11 [Carlos; #773779] * Fix some Wine games starting minimized [Carlos; #774333] * Fix switching between two finger- and edge scrolling on wayland [Rui; #771744] * Implement support for EGLStream/EGLDevice [Jonas; #773629] * Add size_changed vfunc to handle async client size changes [Rui; #770345] * Change focus window on clicks with any modifiers [Rui; #746642] * Misc. bug fixes and cleanups [Carlos, Daniel, Jonas, Rui; #771067, #774330, #774613, #771297, #774135, #774827, #774923] Contributors: Jonas Ådahl, Carlos Garnacho, Rui Matos, Florian Müllner, Daniel Stone Translations: Kjartan Maraas [nb] 3.23.1 ====== * Fix handling of Escape shortcut in force-quit dialog [Landry; #737109] * Improve pointer constraints support [Jonas; #771859] * Really fix framebuffer capture origin offset [Rui; #771502] * Fix session going into idle mode immediately on startup [Rui; #772839] * Fix mirror mode with stage views [Rui; #773115] * Fall back to X with connectors spread across multiple GPUs [Ray; #771442] * Fix various crashes on wayland [Jonas, Carlos; #771646, #771858, #772929] * Fix various placement issues on wayland [Olivier, Jonas, Sjoerd; #772729, #768039, #771841, #771841, #773141] * Misc. bug fixes [Rui, Jonas, Olivier; #771019, #773116, #772914, #773210] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Rui Matos, Landry MINOZA, Sjoerd Simons, Ray Strode Translations: Theppitak Karoonboonyanan [th], Kjartan Maraas [nb], Hannie Dumoleyn [nl], liushuyu [zh_CN] 3.22.1 ====== * Fix feedback loop between StClipboard and X11 bridge [Carlos; #760745] * Fall back gracefully if DRM plane rotation fails [Carlos; #772512] * Approximate native monitor backend behavior to X [Rui; #772176] * Fix crash on VT switch on wayland [Jonas; #771646] * Expose Flatpak ID for application matching [Florian; #772613, #772614] Contributors: Jonas Ådahl, Carlos Garnacho, Rui Matos, Florian Müllner, Olav Vitters Translations: Inaki Larranaga Murgoitio [eu], Milo Casagrande [it] 3.22.0 ====== * Fix wayland crashes [Jonas; #771305, #771345, #770940, #771495] * Fix display rotation on wayland [Jonas; #770672] * Fix framebuffer capture origin offset [Rui; #771502] * Misc. bug fixes [Jonas, Florian, Carlos; #770937, #771536, #771628, #771549] Contributors: Jonas Ådahl, Carlos Garnacho, Rui Matos, Florian Müllner Translations: Ask Hjorth Larsen [da], Charles Monzat [fr], Stas Solovey [ru], Tom Tryfonidis [el], David King [en_GB] 3.21.92 ======= * Fix absolute pointer motion events on wayland [Jonas; #770557] * Default to using stage views [Jonas; #770366] * Fix animated cursors on wayland [Rui; #749913] * Fix various crashes on wayland [Jonas; #757568, #770727, #770992] * Fix screen capture for stage views not at (0, 0) [Jonas; #770127] * Compress motion events instead of discarding them [Jonas; #771049] * Fix XWayland pointer warp emulation [Jonas; #771050] * Add common monitor modes in KMS backend [Rui; #744544] * Temporarily use g-s-d schemas for tablet configuration [Carlos; #771315] * Misc. bug fixes [Jonas, Carlos; #770402, #770647, #770991, #770994, #770929] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Rui Matos, Florian Müllner Translations: Changwoo Ryu [ko], Baurzhan Muftakhidinov [kk], Anders Jonsson [sv], Tiago Santos [pt], Rafael Fontenelle [pt_BR], Mario Blättermann [de], Alexander Shopov [bg], Rūdolfs Mazurs [lv], Fran Dieguez [gl], Trần Ngọc Quân [vi], Piotr Drąg [pl], Мирослав Николић [sr, sr@latin] 3.21.91 ======= * Add support for xdg-foreign protocol [Jonas; #769786] * Support monitor rotation on wayland [Carlos; #745079] * Port xdg-shell implementation to unstable v6 [Jonas; #769936] * Handle unsupported buffer sizes more gracefully [Olivier; #770387] * Use the same output naming logic as the X server on wayland [Rui; #770338] * Fix replies in gnome-shell's chat notifications on wayland [Florian; #758167] * Misc. bug fixes and cleanups [Bastien, Sjoerd, Jonas; #769276, #769636, #770131, #770324, #769731] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Rui Matos, Florian Müllner, Bastien Nocera, Sjoerd Simons Translations: Piotr Drąg [pl], Mario Blättermann [de], Andika Triwidada [id], Enrico Nicoletto [pt_BR], Мирослав Николић [sr, sr@latin] 3.21.90 ======= * Consider XDG_SESSION_TYPE when determining session type [Jouke; #759388] * Re-add support for edge scrolling on some touchpads [Bastien; #768245] * Support mouse and trackball acceleration profile [Jonas; #769179] * Draw monitor contentn to individual framebuffer [Jonas; #768976] * Support virtual input devices [Jonas, Carlos; #765009] * Set correct output scale on hotplug [Jonas; #769505] * Misc. bug fixes and cleanups [Florian, Jonas, Thomas, Bastien, Carlos; #769014, #769024, #769054, #769070, #769036, #769305, #769578, #769800, #769073] Contributors: Jonas Ådahl, Carlos Garnacho, Thomas Hindoe Paaboel Andersen, Simon McVittie, Alberts Muktupāvels, Florian Müllner, Bastien Nocera, Jouke Witteveen Translations: Daniel Mustieles [es], Aurimas Černius [lt], Dušan Kazik [sk], Fabio Tomat [fur], Balázs Úr [hu], Yosef Or Boczko [he], Marek Černocký [cs], Matej Urbančič [sl] 3.21.4 ====== * Fix missing frame border around GTK+ dialogs [Florian; #745060] * Improve X11 <-> wayland copy and paste interaction [Carlos; #768007] * Add support for NV_robustness_video_memory_purge extension [Rui; #739178] * Fix restoring the old focused window on restart [Owen; #766243] * Fix fullscreen windows on other monitors stealing focus after closing a window [Rui; #768221] * Draw monitor content to individual framebuffer [Jonas; #768976] * Provide screen capture API [Jonas; #768978] * Misc. bug fixes and cleanups [Rui, Owen, Luca, Olivier, Jonas, Carlos; #767969, #768243, #762407, #767997, #768039, #768977, #768977] Contributors: Jonas Ådahl, Luca Bruno, Olivier Fourdan, Carlos Garnacho, Rui Matos, Florian Müllner, Owen W. Taylor Translations: Andika Triwidada [id] 3.21.3 ====== * Don't create invalid UTF-8 window description strings [Rui; #765535] * Convert window titles and wm_class to UTF-8 [Rui; #752788] * Communicate tiled state to GTK+ on wayland [Olivier; #766860] * Use kill() to force-quit unresponsive wayland clients [Olivier; #767464] * Fix window position when unmaximizing via DND on wayland [Olivier; #764180] * Avoid full window redraws when using extended frame sync [Florian; #767798] Contributors: Olivier Fourdan, Rui Matos, Florian Müllner Translations: Cédric Valmary [oc] 3.21.2 ====== * Clean up surface <-> shell interaction [Jonas; #763431] * Fix grabbing random keys for disabled shortcuts [Rui; #766270] * Fix stacking of hidden windows on wayland [Rui; #764844] * Misc. bug fixes [Victor, Florian, Marek, Rui; #766306, #766326, #751847, #763832, #766528] Contributors: Jonas Ådahl, Emmanuele Bassi, Marek Chalupa, Matthias Clasen, Carlos Garnacho, Rui Matos, Florian Müllner, Victor Toso Translations: Tiago Santos [pt], Cédric Valmary [oc], Muhammet Kara [tr] 3.21.1 ====== * Notify clients of pending modifier state changes [Rui; #748526] * Add get_is_builtin_display_on() method [Florian; #765267] * Fix 2-finger titlebar taps on wayland [Carlos; #764519] * Merge clutter and cogl forks into mutter [Rui; #760439] * Misc. bug fixes [Florian, Victor, Jonas; #765058, #765252, #765062] Contributors: Jonas Ådahl, Emmanuele Bassi, Olivier Fourdan, Carlos Garnacho, Rui Matos, Florian Müllner, Victor Toso, Rico Tzschichholz Translations: GNOME Translation Robot [ja, gd] 3.20.1 ====== * Constrain window move/resizes on wayland as on X11 [Rui; #748819] * Don't crash with invalid previous monitor configurations [Rui; #764286] * Misc. bug fixes and cleanups [Jonas, Cosimo; #762828, #764807] Contributors: Jonas Ådahl, Cosimo Cecchi, Rui Matos, Jasper St. Pierre Translations: Inaki Larranaga Murgoitio [eu], Reinout van Schouwen [nl], Fabio Tomat [fur], Trần Ngọc Quân [vi] 3.20.0 ====== * Fix crash when using visual bell [Jonas; #763858] Contributors: Jonas Ådahl, Jasper St. Pierre Translations: Milo Casagrande [it], Ask Hjorth Larsen [da] 3.19.92 ======= * Add system bell support on wayland [Jonas; #763284] * Add gtk_surface.present to gtk-shell [Jonas; #763295] * Handle DND drops on the root window [Carlos; #762104] * Misc. bug fixes [Jonas, Carlos, Rui; #762828, #760745, #763125, #762763, #762661, #762639, #763159] Contributors: Jonas Ådahl, Carlos Garnacho, Rui Matos, Florian Müllner Translations: Rūdolfs Mazurs [lv], Balázs Úr [hu], Claude Paroz [fr], Matej Urbančič [sl], Мирослав Николић [sr, sr@latin], Sebastian Rasmussen [sv], Changwoo Ryu [ko], Gil Forcada [ca], Tom Tryfonidis [el] 3.19.91 ======= * Add --nested CLI argument to fix nested wayland session [Jonas; #758658] * Fix stack - scene graph stacking synchronization issues [Jonas; #755605] * Rate-limit last-device changes to fix freezes [Carlos; #753527] * Implement primary selection protocol [Carlos; #762560] * Misc. bug fixes [Carlos, Jonas; #762878, #762716] Contributors: Jonas Ådahl, Carlos Garnacho, Tim Lunn Translations: Piotr Drąg [pl], Artur de Aquino Morais [pt_BR], Marek Černocký [cs], Cédric Valmary [oc], Mario Blättermann [de], Dušan Kazik [sk], Fran Dieguez [gl], Aurimas Černius [lt], Daniel Mustieles [es], Stas Solovey [ru], Yosef Or Boczko [he] 3.19.90 ======= * Release buffer after processing commit [Ray; #761312, #761613] * Implement pointer motion, locks and confinement on wayland [Jonas; #744104] * Add basic startup notification support on wayland [Carlos; #762268] * Misc. bug fixes [Rui, Alberts, Florian; #760670, #761543, #752794, #761557] Contributors: Jonas Ådahl, Olivier Fourdan, Carlos Garnacho, Rui Matos, Alberts Muktupāvels, Florian Müllner, Jasper St. Pierre, Ray Strode 3.19.4 ====== * Fix updating stacking order when setting transient_for [Jonas; #755606] * Support screen rotation when supported by the driver [Carlos; #745079] * Protect against broken WM_CLASS property implementations [Sebastian; #759658] * Handle wl_pointer v5 events on wayland [Carlos; #760637] * Implement DND actions on wayland [Carlos; #760805] * Misc. bug fixes [Jonas, Rui, Ray, Marek; #754711, #756789, #759297, #758613, #760330, #760476, #759222, #760670] Contributors: Jonas Ådahl, Marek Chalupa, Carlos Garnacho, Sebastian Keller, Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode Translations: Aurimas Černius [lt] 3.19.3 ====== * Correct refresh rate units on KMS/Wayland [Daniel; #758653] * Fix crash when initial cursor position is not on a monitor [Marek; #756698] * Fix crash when more CRTs are enabled than outputs connected [Rui; #751638] * Fix touch pointer emulation on wayland [Carlos; #756754] * Allow minimizing windows that don't advertise supporting it [Jasper; #758186] * Force 2-finger scroll by default if available [Bastien; #759304] * Fix crash during XWayland initialization [Marek; #751845] * Ensure to send a ConfigureNotify to just mapped windows [Rui; #759492] * Misc. bug fixes and cleanups [Carlos, Jonas, Lionel; #758239, #758633, #755503, #759374] Contributors: Jonas Ådahl, Marek Chalupa, Carlos Garnacho, Lionel Landwerlin, Rui Matos, Bastien Nocera, Daniel Stone, Jasper St. Pierre 3.19.2 ====== * Fix crash on monitor unplug [Rui; #756796] * Exit cleanly on initialization errors [Owen; #757311] * Allow to determine backend setting from session type [Ray; #741666] * Fix DRM device detection for non-PCI devices [Alban; #754911] * Don't force placement of windows without buffer on wayland [Marek; #751887] * Fix initialization of bypass compositor hint [Rui; #758544] Contributors: Alban Browaeys, Marek Chalupa, Rui Matos, Florian Müllner, Ray Strode, Owen W. Taylor 3.19.1 ====== * wayland: Allow to trigger popups through keyboard/touch [Carlos; #756296] * Fix modifiers-only input source switching on Ubuntu [Alberts; #756543] * Misc. bug fixes [Jonas, Rui, Giovanni, Florian; #756675, #756660, #746420, #756548, #756796, #757101, #757148] Contributors: Jonas Ådahl, Giovanni Campagna, Carlos Garnacho, Rui Matos, Alberts Muktupāvels, Florian Müllner Translations: Daniel Șerbănescu [ro] 3.18.1 ====== * Misc. crash fixes [Jonas, Rui, Carlos, Owen, Florian; #755096, #754979, #755490, #754357, #745785, #756642] * Improve HiDPI support on wayland [Jonas; #755097] * Fix doubly-scaled cursor on XWayland HiDPI [Jonas; #755099] * Stop hiding titlebar buttons in dialogs [Florian; #641630] * Add support for fullscreen/unfullscreen animations [Cosimo; #707248] * Misc. bug fixes [Rui, Colin, Florian; #743339, #752047, #756074, #756649] Contributors: Jonas Ådahl, Cosimo Cecchi, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre, Colin Walters, Owen W. Taylor 3.18.0 ====== * Misc. fixes [Florian, Jonas; #753434] Contributors: Jonas Ådahl, Florian Müllner Translations: Rūdolfs Mazurs [lv] 3.17.92 ======= * Don't omit the background color for backgrounds that don't fill the screen [Ray; #754476] * Fix up key state on FocusIn when running nested [Owen; #753948] * Find the right DRM device instead of hardcoding card0 [Marek; #753434] * Scale cursor on HiDPI screens [Jonas; #744932] * Misc. fixes and cleanups [Lan, Jonas, Javier, Olivier; #754545, #754215, #754621, #754715] Contributors: Jonas Ådahl, Marek Chalupa, Olivier Fourdan, Javier Jardón, Ting-Wei Lan, Ray Strode, Owen W. Taylor 3.17.91 ======= * Send error on pointer-gesture protocol version mismatch [Jonas; #753855] * Misc. cleanups [Jonas; #744932] Contributors: Jonas Ådahl Translations: Chao-Hsiung Liao [zh_TW], Piotr Drąg [pl] 3.17.90 ======= * Fix glitch with some fullscreen apps [Rui; #753020] * Fix screen update issue with NVidia driver [Aaron, Rui; #728464] * Only call frame callbacks for surfaces that get drawn [Adel; #739163] * Misc. bug fixes and cleanups [Jonas, Rui, Ting-Wei; #753222, #752753, #753237, #753380, #744104, #744932] Contributors: Jonas Ådahl, Adel Gadllah, Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Aaron Plattner, Jasper St. Pierre Translations: Akom Chotiphantawanon [th] 3.17.4 ====== * nested: Allow basic configuration of dummy outputs [Jonas; #747089] * Send wl_surface.enter and wl_surface.leave on output changes [Jonas; #744453] * Improve HiDPI handling on wayland [Jonas; #745655, #744934] * Implement compositor-side animated cursors [Carlos; #752342] * Misc. bug fixes [Peter, Marek, Carlos, Matthias, Rui; #750816, #751884, #752248, #752551, #752552, #752673, #752674] Contributors: Jonas Ådahl, Marek Chalupa, Matthias Clasen, Carlos Garnacho, Peter Hutterer, Rui Matos, Florian Müllner, Jasper St. Pierre 3.17.3 ====== * Add X11/wayland clipboard interaction [Carlos; #738312] * Support VM monitor layout hints on wayland [Thomas; #750363] * Misc. bug fixes [Rui, Jonas, Olivier, Carlos, Ting-Wei, Peter, Florian; #749994, #750256, #749716, #748705, #750552, #751036, #750007, #751136, #750552, #751471, #751715, #750680] Contributors: Jonas Ådahl, Dave Airlie, Cosimo Cecchi, Olivier Fourdan, Carlos Garnacho, Thomas Hellstrom, Peter Hutterer, Ting-Wei Lan, Jasper Lievisse Adriaanse, Rui Matos, Florian Müllner, Jasper St. Pierre Translations: Marek Černocký [cs], Christian Kirbach [de], Pedro Albuquerque [pt] 3.17.2 ====== * Honor default value for click method setting [Rui; #746290] * Add X11/wayland clipboard interoperation [Carlos; #738312] * Misc. bug fixes [Rui; #749076, #749711] Contributors: Carlos Garnacho, Rui Matos, Jasper St. Pierre 3.17.1 ====== * Add public method to get neighboring monitor [Florian; #633994] * Apply the right settings to the right input devices [Carlos; #747886] * Fix scroll button setting [Ondrej; #747967] * Add support for modal hint on wayland [Jonas; #745720] * Don't reset idle time for non-hardware events [Rui; #748541] * Misc. bug fixes [Ray, Rui; #748380, #748478] Contributors: Jonas Ådahl, Carlos Garnacho, Ondrej Holy, Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode, Tomeu Vizoso 3.16.1 ====== * Add function to refresh all background instances [Rui; #739178] * Fix swapped scroll methods on wayland [Ondrej; #746870] * Manually activate stage to fix accessibility on wayland [Ray, Rui; #746670] * Center pointer on primary monitor on startup [Carlos; #746896] * wayland: Reword synchronized state application semantics [Jonas; #743617] * Ensure input settings are applied on startup [Rui; #747434] * Misc. bug fixes [Jonas, Giovanni, Calvin, Ray, Rui; #744932, #746509, #746692, #746510, #746545, #747263] Contributors: Jonas Ådahl, Giovanni Campagna, Carlos Garnacho, Ondrej Holy, Rui Matos, Jasper St. Pierre, Ray Strode, Calvin Walton Translations: Khaled Hosny [ar], Marek Černocký [cs] 3.16.0 ====== * wayland: Don't skip notifying about initial maximized state [Jonas; #745303] Contributors: Jonas Ådahl Translations: Kjartan Maraas [nb], Jiri Grönroos [fi], Andika Triwidada [id], Inaki Larranaga Murgoitio [eu], Ask H. Larsen [da], Muhammet Kara [tr] 3.15.92 ======= * Ensure pointer visibility on monitor changes [Rui, Marek; #745121, #745752] * Fix geometry of shaded windows [Florian; #746145] * Take over cursor visibility handling from gsd [Carlos; #712775] * Fix touch interaction on window decorations [Carlos; #745335] * Add options for libinput_config_click_method [Carlos; #746290] * Scale window decorations on HiDPI displays [Florian; #744354] * Misc. bug fixes [Carlos, Ray, Rui; #745163, #746295, #746098, #745734] Contributors: Marek Chalupa, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode Translations: Piotr Drąg [pl], Milo Casagrande [it], Changwoo Ryu [ko], Daniel Korostil [uk], Baurzhan Muftakhidinov [kk], Trần Ngọc Quân [vi], Alexander Shopov [bg], Jordi Mas [ca], Samir Ribic [bs], A S Alam [pa], Matej Urbančič [sl] 3.15.91 ======= * wayland: Fix nested compositor mode [Jonas; #745401] * wayland: Fix pointer constraining [Marek; #727337] * wayland: Fix input region on HiDPI [Jonas; #744933] * Allow themes to style buttons differently based on function [Horst; #745108] * Misc. bug fixes and cleanups [Ray, Rui, Alban; #745141, #745118, #745476, #745442] Contributors: Jonas Ådahl, Alban Browaeys, Marek Chalupa, Horst, Rui Matos, Jasper St. Pierre, Ray Strode Translations: Chao-Hsiung Liao [zh_TW], Efstathios Iosifidis [el], Dušan Kazik [sk], Balázs Úr [hu], Daniel Mustieles [es], Claude Paroz [fr], Stas Solovey [ru], Yosef Or Boczko [he], Rafael Ferreira [pt_BR], Aurimas Černius [lt], Fran Dieguez [gl], Anders Jonsson [sv], Мирослав Николић [sr, sr@latin] 3.15.90 ======= * Initialize MetaOutput even when we can't get the EDID [Rui; #743412] * Expose MetaMonitorManager to introspection [Rui; #743745] * Fix flash on unredirection [Chris; #743858] * Update xdg-shell implementation to v5 [Jonas; #744452] * Do not try to use seat devices that aren't (yet) present [Ray; #744640] * Add keybindings for switching to VT8-VT12 [Ray; #744800] * Misc bug fixes [Jonas, Cosimo; #743678, #744500] Contributors: Jonas Ådahl, Cosimo Cecchi, Carlos Garnacho, Rui Matos, Jasper St. Pierre, Ray Strode, Chris Wilson Translations: Yosef Or Boczko [he], Yuri Myasoedov [ru], Kristjan SCHMIDT [eo], Matej Urbančič [sl], Dušan Kazik [sk] 3.15.4 ====== * Use GTK+ theme for window decorations instead of metacity [Florian; #741917] * Export the same EDID information on X11 and wayland [Carlos; #742882] * Apply input device configuration on wayland [Carlos; #739397] * Implement pointer barriers on wayland [Jonas; #706655] * Misc. bug fixes (Ting-Wei, Rui, Ikey, Florian, Marek, Jonas; #741829, #738630, #737463, #698995, #727893, #742825, #742824, #742841, #743173, #743189, #743217, #743254] Contributors: Jonas Ådahl, Giovanni Campagna, Marek Chalupa, Ikey Doherty, Adel Gadllah, Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz Translations: Matej Urbančič [sl], Balázs Úr [hu], Marek Černocký [cs], Inaki Larranaga Murgoitio [eu], Rafael Ferreira [pt_BR], Daniel Mustieles [es], Fran Dieguez [gl] 3.15.3 ====== * Don't leave left-over frames queued [Owen; #738686] * Set CRTC configuration even if it might be redundant [Rui; #740838] Contributors: Rui Matos, Jasper St. Pierre, Rico Tzschichholz, Owen W. Taylor Translations: Trần Ngọc Quân [vi], Muhammet Kara [tr] 3.15.2 ====== * Don't enable hiDPI on monitors with broken EDID [Bastien; #734839] * Prevent crash applying monitor config for a closed lid [Rui; #739450] * Fix "flicker" during startup transition [Ray; #740377] * Misc. bug fixes [Lan, Florian, Carlos; #731521, #740133, #738890] Contributors: Emmanuele Bassi, Carlos Garnacho, Jonathon Jongsma, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera, Jasper St. Pierre, Ray Strode Translations: Kjartan Maraas [nb] 3.15.1 ====== * Use GResources for theme loading [Cosimo; #736936] * Fix headerbar drag getting stuck on xwayland [Carlos; #738411] * Fix wayland hiDPI regressions [Adel; #739161] * Misc bug fixes and cleanups [Jasper, Rui, Carlos; #662962, #738630, #738888, #738890] Contributors: Cosimo Cecchi, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre 3.14.1 ====== * Fix move-titlebar-onscreen function [Florian; #736915] * Fix stacking of the guard window [Owen; #737233] * Fix keycode lookup for non-default layouts [Rui; #737134] * Fix workspaces-only-on-primary handling [Florian; #737178] * Don't unstick sticky windows on workspace removal [Florian; #737625] * Do not auto-minimize fullscreen windows [Jasper; #705177] * Upload keymap to newly added keyboard devices [Rui; #737673] * Apply keyboard repeat settings [Rui; #728055] * Don't send pressed keys on enter [Rui; #727178] * Fix build without wayland/native [Rico; #738225] * Send modifiers after the key event [Rui; #738238] * Fix unredirect heuristic [Adel; #738271] * Do not show system chrome over fullscreen windows [Florian; #693991] * Misc. bug fixes [Florian, Adel, Tom; #737135, #737581, #738146, #738384] Contributors: Tom Beckmann, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz, Owen W. Taylor Translations: Krishnababu Krothapalli [te], Мирослав Николић [sr, sr@latin], Alexander Shopov [bg], Saibal Ray [bn_IN], Milo Casagrande [it], Rūdolfs Mazurs [lv] 3.14.0 ====== * Fix placement of popup windows on wayland [Jasper; #736812] * Only increment serial once per event [Jasper; #736840] * Fix window positioning regression with non-GTK+ toolkits [Owen; #736719] Contributors: Jasper St. Pierre, Owen W. Taylor Translations: Saibal Ray [bn_IN], Dušan Kazik [sk], Manoj Kumar Giri [or], Christian Kirbach [de], Ask H. Larsen [da], YunQiang Su [zh_CN], Bernd Homuth [de], Shankar Prasad [kn], Petr Kovar [cs], Rajesh Ranjan [hi] 3.13.92 ======= * Rewrite background code [Owen; #735637, #736568] * Fix size in nested mode [Owen; #736279] * Fix destroy animation of background windows [Florian; #735927] * Wire keymap changes up to the wayland frontend [Rui; #736433] * Add a test framework and stacking tests [Owen; #736505] * Simplify handling of the merged X and wayland stack [Owen; #736559] * Fix cursor size on HiDPI [Adel; #729337] * Misc. bug fixes [Owen; #735632, #736589, #736694] Contributors: Adel Gadllah, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen W. Taylor Translations: Andika Triwidada [id], Piotr Drąg [pl], Changwoo Ryu [ko], Kjartan Maraas [nb], Ville-Pekka Vainio [fi], Yuri Myasoedov [ru], Aurimas Černius [lt], Balázs Úr [hu], Sweta Kothari [gu], A S Alam [pa], Sandeep Sheshrao Shedmake [mr], Shantha kumar [ta], Gil Forcada [ca], Carles Ferrando [ca@valencia], Mattias Eriksson [sv] 3.13.91 ======= * Misc. bug fixes [Carlos; #735452] Contributors: Adel Gadllah, Carlos Garnacho, Rui Matos, Jasper St. Pierre, Rico Tzschichholz Translations: Chao-Hsiung Liao po/zh_HK, zh_TW.po, Enrico Nicoletto [pt_BR], Kjartan Maraas [nb], Fran Diéguez [gl], Yosef Or Boczko [he], Maria Mavridou [el], Claude Paroz [fr] 3.13.90 ======= * Only call XSync() once per frame [Rui; #728464] * Update capabilities on device list changes [Carlos; #733563] * Make use of GLSL optional [Adel; #733623] * Handle gestures and touch events on wayland [Carlos; #733631] * Add support for unminimize compositor effects [Cosimo; #733789] * Always set the frame background to None [Giovanni; #734054] * Add backend methods to handle keymaps [Rui; #734301] * Actually mark revalidated MetaTextureTower levels as valid [Owen; #734400] * Rely on explicit -backward switcher keybindings instead of -magic [Christophe; #732295, #732385] * Misc. bug fixes and cleanups [Rui, Adel, Christophe; #727178, #734852, #734960] Contributors: Emmanuele Bassi, Giovanni Campagna, Cosimo Cecchi, Piotr Drąg, Christophe Fergeau, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz, Olav Vitters, Owen W. Taylor Translations: Kjartan Maraas [nb], Inaki Larranaga Murgoitio [eu], Lasse Liehu [fi], ngoswami [as], Daniel Mustieles [es] 3.13.4 ====== * Fix move/resize operations for wayland clients [Marek; #731237] * Add ::first-frame signal to MetaWindowActor [Owen; #732343] * Handle keysyms without the XF86 prefix [Owen; #727993] * Add touch gesture support [Carlos] * Fix a deadlock when exiting [Owen; #733068] * Add framework for restarting the compositor with nice visuals [Owen; #733026] * Toggle seat capabilities on VT switch [Carlos; #733563] * Misc bug fixes [Florian, Owen; #732695, #732350] Contributors: Tom Beckmann, Giovanni Campagna, Marek Chalupa, Adel Gadllah, Carlos Garnacho, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz, Owen W. Taylor Translations: Yuri Myasoedov [ru], Fran Diéguez [gl], Aurimas Černius [lt], MarMav [el], Enrico Nicoletto [pt_BR] 3.13.3 ====== * Improve behavior of window buttons with compositor menus [Florian; #731058] * Implement touch support on wayland [Carlos; #724442] * Update window shadows [Nikita; #731866] * Keep windows on the preferred output [Florian; #731760] * Misc bug fixes [Jonas, Florian, Jasper; #729601, #730681, #731353, #731332, #730527, #662962] Contributors: Jonas Ådahl, Nikita Churaev, Carlos Garnacho, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz 3.13.2 ====== * Add basic HiDPI support on wayland [Adel; #728902] * Fix crash when monitors change during suspend [Giovanni; #725637] * Replace mutter-launch with logind integration [Jasper; #724604] * Move window menu into the compositor [Jasper; #726352] * Fix delayed focus-follows-mouse support [Florian; #730541] * Support fallback app menu in window decorations [Florian; #730752] * Misc. bug fixes and cleanups [Giovanni, Jonas, Jasper; #729732, #729602, #726714] Contributors: Jonas Ådahl, Giovanni Campagna, Adel Gadllah, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz Translations: Pau Iranzo [ca], Daniel Mustieles [es] 3.13.1 ====== * Fix opacity values from _NET_WM_WINDOW_OPACITY [Nirbheek; #727874] * Merge wayland branch [Jasper, Giovanni, Robert B., Neil, Adel, Rui, Jonas, Lionel, Tim, Owen, Florian, Colin W., Cosimo, Ray, Kalev, Pavel, Robert A., Magdalen, Marek, Matthias, Alban, Seán, Daniel, Stefano, Carlos, Colin G., Andreas, Alexander, Ryan, Marc-André, Asad, Alberto, Bastien, Hans, Debarshi, Sindhu, Andika, Rico, Olav] * Don't prevent workspace switches for present_with_time() [Florian; #728018] * Add shortcuts for switching to the last workspace [Elad; #659288] * Make move/resize menu items behave like the keybindings [Jasper; #728617] * Misc. bug fixes and cleanups [Jasper, Bastien, Florian, Adel; #720631, #727979, #728423, #728395, #729044] Contributors: Jonas Ådahl, Elad Alfassa, Robert Ancell, Magdalen Berns, Robert Bragg, Giovanni Campagna, Cosimo Cecchi, Marek Chalupa, Nirbheek Chauhan, Matthias Clasen, Alban Crequy, Seán de Búrca, Daniel Drake, Jason Ekstrand, Stefano Facchini, Adel Gadllah, Carlos Garnacho, Colin Guthrie, Andreas Heider, Lionel Landwerlin, Alexander Larsson, Kalev Lember, Ryan Lortie, Tim Lunn, Marc-André Lureau, Rui Matos, Asad Mehmood, Alberto Milone, Florian Müllner, Bastien Nocera, Hans Petter Jansson, Debarshi Ray, Neil Roberts, Sindhu S, Jasper St. Pierre, Ray Strode, Andika Triwidada, Rico Tzschichholz, Pavel Vasin, Olav Vitters, Colin Walters, A. Walton, Owen W. Taylor Translations: Inaki Larranaga Murgoitio [eu], marablack3 [el], Daniel Mustieles [es], Fran Diéguez [gl], Yosef Or Boczko [he], Dirgita [id] 3.12.0 ====== * Fix grab issue with SSD xwayland windows [Rui; #726123] * Misc. bug fixes [Jasper, Ray, Rui, Florian; #727011] Contributors: Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode 3.11.92 ======= * Fix identification of CSD windows [Owen; #723029] * Update keyboard state unconditionally [Rui; #722847] * Misc bug fixes and cleanups [Owen, Rui, Giovanni, Matthias, Adel, Ryan, Jasper, Marek, Florian; #723580, #726123, #726683] Contributors: Giovanni Campagna, Marek Chalupa, Matthias Clasen, Adel Gadllah, Ryan Lortie, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen W. Taylor 3.11.91 ======= * Don't use keysym to match keybindings [Rui; #678001] * Fix message tray icons showing up blank (again) [Adel; #725180] * Improve keybinding lookups [Rui; #725588] * Fix dynamic updates of titlebar style properties [Owen; #725751] * Fix positioning of manually positioned windows [Owen; #724049] * Misc bug fixes and cleanups [Jasper, Carlos, Adel, Giovanni, Florian; #720631, #724969, #725216, #724402, #722266, #725338, #725525] Contributors: Giovanni Campagna, Adel Gadllah, Carlos Garnacho, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen W. Taylor 3.11.90 ======= * Fix double-scaling on high DPI resolutions [Adel; #723931] * Make tile previews a compositor effect [Stefano, Florian; #665758] * Misc. bug fixes and cleanups [Ryan, Giovanni, Jasper, Adel; #722530, #724257, #724258, #720631, #724364, #724472] Contributors: Giovanni Campagna, Marek Chalupa, Stefano Facchini, Adel Gadllah, Ryan Lortie, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz 3.11.5 ====== * Fix CSD titlebars being placed off-screen [Jasper; #719772] * Add support for subsurfaces [Jonas; #705502] * Expose MetaWindow:skip-taskbar property [Florian; #723307] * Fix legacy tray icons showing up blank [Adel; #721596] * Fix configuration of cloned monitors [Adel; #710610] * Misc bug fixes and cleanups [Jasper, Adel, Marek, Jonas; #720631, #723468, #720818, #723563, #723564] Contributors: Jonas Ådahl, Marek Ch, Adel Gadllah, Florian Müllner, Jasper St. Pierre 3.11.4 ====== * Don't leave focus on windows that are being unmanaged [Owen; #711618] * Reduce server grabs [Daniel Drake; #721345, #721709] * Improve heuristic to determine display output name [Cosimo Cecchi; #721674] * Atomically unmaximize both directions [Jasper; #722108] * Misc bug fixes [Debarshi, Andika, Florian; #721517, #721674, #722347] Contributors: Cosimo Cecchi, Daniel Drake, Florian Müllner, Debarshi Ray, Jasper St. Pierre, Andika Triwidada, Owen W. Taylor 3.11.3 ====== * Fix focus issues with external OSKs[Jasper; #715030] * Add a MetaCullable interface [Jasper; #714706] * Fix window keybindings [Rui; #719724] * Fix settings keyboard/pointer focus for new clients [Rui; #719725] * Fix window group paint volume [Owen; #719669] * Fix frame extents problems [Owen; #714707] * Add shortcut to move windows between monitors [Florian; #671054] * Fix problems with focus tracking [Owen; #720558] * Misc. bug fixes and cleanups: [Rui, Colin, Lionel, Jasper, Owen; #712833, #719557, #719695, #719833, #678989, #720417, #720630] Contributors: Lionel Landwerlin, Rui Matos, Alberto Milone, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz, Owen W. Taylor, Colin Walters 3.11.2 ====== * Support setting a NULL opaque region [Andreas; #711518] * Sync keymap from X to wayland [Giovanni; #707446] * Implement support for subsurfaces [Jonas; #705502] * Don't focus the no-focus-window for globally active windows [Jasper; #710296] * Support "hotplug_mode_update" property [Marc-André; #711216] * Fix resize operations using mouse-button-modifier [Lionel; #710251] * Fix position of attached modals for CSD windows [Giovanni, Owen; #707194] * Misc. bug fixes [Rui, Jasper, Neil, Florian; #712247, #711731] Contributors: Giovanni Campagna, Andreas Heider, Lionel Landwerlin, Marc-André Lureau, Rui Matos, Florian Müllner, Neil Roberts, Sindhu S, Jasper St. Pierre, Rico Tzschichholz, Owen W. Taylor, Jonas Ådahl 3.11.1 ====== * Fix tile previews getting stuck on right click during drags [Lionel; #704759] * Use new UPower API [Bastien] * Set hot spot when cursor set from wl_buffer [Jonas; #709593] * Expose min-backlight-step [Asad; #710380] * Misc. bug fixes and cleanups [Jasper, Olav, Magdalen; #709776] Contributors: Magdalen Berns, Lionel Landwerlin, Asad Mehmood, Bastien Nocera, Jasper St. Pierre, Olav Vitters, Jonas Ådahl 3.10.1 ====== * Don't apply fullscreen workarounds to CSD windows [Giovanni; #708718] * Fix hangs during DND operations [Adel; #709340] * Misc bug fixes [Dan, Giovanni, Jasper; #708813, #708420] Contributors: Giovanni Campagna, Adel Gadllah, Dan Horák, Hans Petter Jansson, Jasper St. Pierre 3.10.0.1 ======== * Fix bug when a window changed size twice in a single frame - this can happen with GTK+ client-side decorations [Giovanni, Owen; #708367] Contributors: Giovanni Campagna, Owen Taylor 3.10.0 ====== * Update dependencies [Giovanni; #708210] 3.9.92 ====== * Constrain the pointer position onto visible monitors [Giovanni; #706655] * Fix keyboard state handling in face of event compression [Giovanni; #706963] * Extend the MetaCursorTracker API with query pointer and cursor visibility [Giovanni; #707474] * Be stricter in checking and exposing the wayland protocol version [#707851] * Don't require plugins to pass event to Clutter [Giovanni; #707482] * Move the --wayland option from the binary to the library [Giovanni; #707897] * Implement running from gnome-session (environment variable setting, process group handling, Clutter backend variables) [Giovanni; #706421] * Add support for more cursor types [Giovanni; #707919] * Drop man pages for removed utilities [Kalev; #706579] * Implement monitor configuration on KMS [Giovanni; #706308] * Implement HW cursors [Giovanni; #707573] * Implement minimal support for resizing and maximizing wayland clients [Giovanni; #707401] * Implement transient hints for wayland clients [Giovanni; #707401] * Implement popup menu surfaces and grabs [Giovanni; #707863] * Immediately fire idle watches that are already expired [Giovanni; #707302] * Remove holes generated by disabling the laptop lid [Giovanni; #707473] * Misc bug fixes [Giovanni, Pavel, Adel; #707649, #706124, #707584, #707851, #707929, #708070] Contributors: Adel Gadllah, Giovanni Campagna, Kalev Lember, Pavel Vasin Translations: Мирослав Николић po/sr, sr@latin.po, Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW], Yuri Myasoedov [ru], Ville-Pekka Vainio [fi], Changwoo Ryu [ko], A S Alam [pa], Mattias Põldaru [et], Rūdolfs Mazurs [lv], Ihar Hrachyshka [be], Nilamdyuti Goswami [as], Andika Triwidada [id], Baurzhan Muftakhidinov [kk], Benjamin Steinwender [de] 3.9.91 ====== * Drop man pages for removed utilities [Kalev; #706579] * Add support for idle tracking [Giovanni, Cosimo; #706005, #707250] * Skip CRTC reconfigurations that have no effect [Giovanni; #706672] * Ignore skip-taskbar hints on parentless dialogs [Giovanni; #673399] * Don't save pixbuf data in user data [Tim; #706777] * Don't queue redraws for obscured regions [Adel; #703332] * Suppor the opaque region hints for wayland clients [Jasper; #707019] * Turn blending off when drawing entirely opaque regions [Jasper; #707019] * Check event timestamps before reconfiguring [Giovanni; #706735] * Merge the DBus API for display configuration in the wayland branch [Giovanni] * Install an X IO error handler for XWayland [Giovanni; #706962] * Use the clutter xkbcommon integration for the wayland keyboard [Giovanni; #705862] * Add a setuid helper for running on KMS+evdev [Giovanni, Colin; #705861] * Add keybindings for switching VT [Giovanni; #705861] * Implement plugin modality when running as a wayland compositor [Giovanni; #705917] * Add support for the application menu for wayland clients [Giovanni; #707128] * Several Coverity spotted fixes [Jasper] * Don't create a dummy texture for the texture template [Neil; #707458] * Use a more conservative paint volume for obscured windows [Adel] * Misc bug fixes [Giovanni, Colin, Seán, Jasper, Cosimo; #706582, #706598, #706787, #706729, #706825, #707081, #707090, #707267, #706982, #706289] Contributors: Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Colin Guthrie, Kalev Lember, Tim Lunn, Jasper St. Pierre, Neil Roberts, Rico Tzschichholz, Seán de Búrca Translations: Piotr Drąg [pl], Alexandre Franke [fr], Kjartan Maraas [nb], Milo Casagrande [it], Balázs Úr [hu], Seán de Búrca [ga], Fran Diéguez [gl], Daniel Mustieles [es], Aurimas Černius [lt], Gil Forcada [ca] 3.9.90 ====== * First release from the wayland branch, includes basic support for running as a wayland compositor [Robert, Neil, Giovanni] * Add support for _GTK_FRAME_EXTENTS [Jasper; #705766] * Fix quick consecutive presses breaking keyboard input [Alban; #666101] * Work towards running as wayland compositor [Giovanni] - Add DBus API for display configuration [#705670, #706231, #706233, #706322, #706382] - Add abstraction layer for cursor tracking [#705911] - Add support for plugin modality under wayland [#705917] * Disable GTK+ scaling [Alexander; #706388] * Disable blending while updating tower [Robert] * Misc bug fixes and cleanups [Adel, Jasper, Giovanni, Colin, Rico, Florian; #703332, #704437, #706207] Contributors: Robert Bragg, Giovanni Campagna, Alban Crequy, Adel Gadllah, Alexander Larsson, Florian Müllner, Jasper St. Pierre, Neil Roberts, Rico Tzschichholz, Colin Walters Translations: Jiro Matsuzawa [ja], Kjartan Maraas [nb], Matej Urbančič [sl], Marek Černocký [cs], Daniel Mustieles [es], Rafael Ferreira [pt_BR], Yaron Shahrabani [he], Ján Kyselica [sk] 3.9.5 ===== * Don't select for touch events on the stage [Jasper; #697192] * Don't queue redraws for obscured regions [Adel; #703332] * Export timestamp of global keybinding events [Bastien; #704858] * Misc bug fixes and cleanups [Jasper, Rico; #703970] Contributors: Adel Gadllah, Bastien Nocera, Jasper St. Pierre, Rico Tzschichholz 3.9.4 ===== * Tweak window shadows [Allan; #702141] * Ignore our own focus events for focus prediction [Jasper; #701017] * Add API to query if the stage is focused [Jasper; #700735] * Add API to query the monitor for a given position [Adel] * Don't force attached dialogs to be border-only [Florian; #702764] * Allow slicing of backgrounds to avoid texture size limits [Ray; #702283] * Miscellaneous bug fixes and cleanups [Adel; #701224, #702564] Contributors: Allan Day, Adel Gadllah, Florian Müllner, Jasper St. Pierre, Ray Strode 3.9.3 ===== * Ensure events are always reported to the grab window [Rui; #701219] * Use new clutter_stage_set_paint_callback() function to prevent dropping frames with frame synced toolkits [Owen; #698794] Contributors: Rui Matos, Owen W. Taylor 3.9.2 ===== * Add meta_window_can_close() function [Jasper; #699269] * Add support for string-array preferences [Florian; #700223] * Fix a potential race condition with _NET_WM_MOVERESIZE [Jasper; #699777] * Fix shade window action [Stef; #693714] * Remove overlay_group [Giovanni; #700735] * Improve tracking of the focus window [Dan, Jasper; #647706] * Add API to freeze/unfreeze the keyboard [Rui; #697001] * Grab and emit a signal when XK_ISO_Next_Group is pressed [Rui; #697002] * Misc bug fixes and cleanups [Dieter, Jasper, Rui; #699636, #700735, #697000] Contributors: Giovanni Campagna, Rui Matos, Florian Müllner, Jasper St. Pierre, Dieter Verfaillie, Stef Walter, Dan Winship Translations: Kjartan Maraas [nb], Ján Kyselica [sk] 3.9.1 ===== * Fix miscellaneous memory leaks [Pavel; #698710] * Misc fixes and cleanups [Stef, Simon; #698179, #697758] Contributors: Simon McVittie, Pavel Vasin, Stef Walter 3.8.1 ===== * Fix crash when getting default font [Bastien; #696814] * Fix ungrabbing of keybindings [Rui; #697003] * Misc fixes and cleanups [Jasper, Simon; #697758] Contributors: Jasper Lievisse Adriaanse, Rui Matos, Simon McVittie, Bastien Nocera Translations: Guillaume Desmottes [fr], Shankar Prasad [kn], Bruce Cowan [en_GB], Andika Triwidada [id], Yaron Shahrabani [he], Kjartan Maraas [nb], Gheyret Kenji [ug] 3.8.0 ===== * Address major memory leak when changing backgrounds [Ray; #696157] Contributors: Ray Strode Translations: Sandeep Sheshrao Shedmake [mr], Victor Ibragimov [tg], Gabor Kelemen [hu], Ville-Pekka Vainio [fi], Rajesh Ranjan [hi], Dr.T.Vasudevan [ta], ManojKumar Giri [or], Yuri Myasoedov [ru], Petr Kovar [cs], Jiro Matsuzawa [ja], Krishnababu Krothapalli [te], Ani Peter [ml], Inaki Larranaga Murgoitio [eu] 3.7.92 ====== * Build and improve reference docs [Tomeu; #676856, #695641, #695935] * Add tracking of whether there are fullscreen windows [Owen; 649748] * Misc bug fixes and cleanups [Adel, Giovanni, Owen, Jasper, Florian; #695269, #695711, #694046, #695813, #695881, #676856, #696053, #682779, #696089, #696091, #696087] Contributors: Giovanni Campagna, Adel Gadllah, Florian Müllner, Jasper St. Pierre, Tomeu Vizoso, Owen W. Taylor Translations: Chao-Hsiung Liao [zh_HK, zh_TW], Rafael Ferreira [pt_BR], Ihar Hrachyshka [be], Nilamdyuti Goswami [as], Matej Urbančič [sl], Dimitris Spingos [el], Jan Kyselica [sk], Khaled Hosny [ar], Мирослав Николић [sr, sr@latin], Duarte Loreto [pt], Sweta Kothari [gu], Milo Casagrande [it], Changwoo Ryu [ko], Gil Forcada [ca], Carles Ferrando [ca@valencia], Mattias Põldaru [et], Alexandre Franke [fr], Ask H. Larsen [da], Rūdolfs Mazurs [lv], Nguyễn Thái Ngọc Duy [vi] 3.7.91 ====== * Fix windows being treated as remote after hostname changes [Ray; #688716] * Add meta_window_get_all_monitors() method [Adel; #646861] * Add grab API for externally defined accelerators [Florian; #643111] * Make session registration an explicit step [Ray; #694876] * Avoid unnecessary stage redraws [Adel; #694988, #695006] * Misc fixes [Giovanni, Ray, Jasper, Rui, Pavel, Owen; #694801, #694725, #694641, #694393, #678917, #695093, #694837, #695135, #694771, #694321] Contributors: Giovanni Campagna, Adel Gadllah, Rui Matos, Florian Müllner, Jasper St. Pierre, Ray Strode, Owen Taylor, Pavel Vasin Translations: Daniel Mustieles [es], Yaron Shahrabani [he], A S Alam [pa], Piotr Drąg [pl], Gheyret Kenji [ug], Alexandre Franke [fr], Milo Casagrande [it], Fran Diéguez [gl], Dimitris Spingos [el], Мирослав Николић [sr, sr@latin], Chao-Hsiung Liao [zh_HK, zh_TW], Nguyễn Thái Ngọc Duy [vi], Aurimas Černius [lt], Mario Blättermann [de], Kjartan Maraas [nb] 3.7.90 ====== * Support _NET_WM_OPAQUE_REGION [Jasper, Adel; #679901] * Add wrapper for XI2.3 pointer barriers [Jasper; #677215] * Update style of resize popups [Cosimo; #692741] * Implement compositor <-> application frame synchronization [Owen; #685463] * Handle animated backgrounds [Ray; #682427] * Add a new window group for override-redirect windows [Gayan; #633620] * Pass on pointer events on guard window to Clutter [Jasper; #681540] * Show correct shortcut in window menus [Giovanni; #694045] * Don't put minimized windows at the back of alt-tab [Jasper; #693991] * Misc bug fixes and cleanups [Jasper, Rico, Adel, Florian, Rui, Giovanni, Owen; #692679, #693354, #690581, #693439, #692718, #693475, #693482, #693540, #690580, #680990, #693833, #693922, #693854, #694224] Contributors: Giovanni Campagna, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner, Gayan Perera, Jasper St. Pierre, Ray Strode, Owen Taylor, Rico Tzschichholz Translations: Fran Diéguez [gl], A S Alam [pa], Alexandre Franke [fr], Aurimas Černius [lt], Мирослав Николић [sr, sr@latin], Fran Diéguez [gl], Piotr Drąg [pl], Luca Ferretti [it], Daniel Mustieles [es] 3.7.5 ===== * Don't allow multiline window titles [Jon; #683056] * Make meta_window_located_on_workspace() public [Jasper; #691744] * Request XI2.3 [Colin; #692877] * Add meta_window_set_icon_geometry() method [Florian; #692997] * Require XFixes 5.0 [Jasper; #677215] * Change unredirection hints to match spec changes [Adel; #693064] * Improve unredict heuristicts [Adel; #683786] * Misc bug fixes and cleanups [Florian, Jasper, Adel; #691874, #679901, #692952, #693042] Contributors: Adel Gadllah, William Jon McCann, Florian Müllner, Jasper St. Pierre, Colin Walters Translations: Daniel Mustieles [es], Ihar Hrachyshka [be], Nilamdyuti Goswami [as], Gheyret Kenji [ug], Kjartan Maraas [nb], Yaron Shahrabani [he], Piotr Drąg [pl], Chao-Hsiung Liao [zh_HK,zh_TW], Milo Casagrande [it] 3.7.4 ===== * Add support for bypass compositor hints [Adel; #683020] * Make automaximization optional [Adel; #680990] * Add method for checking if the application is responding [Giovanni; #684340] * Expose the xinput opcode [Jasper; #690590] * Rebrand "minimize" as "hide" [Florian; #682887] * Misc bug fixes and cleanups [Giovanni, Ray, Jasper, Matthias, Debarshi, Florian, Rui; #690454, #690573, #690593, #690956, #691363, #690609, #690317, #689263] Contributors: Giovanni Campagna, Matthias Clasen, Adel Gadllah, Rui Matos, Florian Müllner, Debarshi Ray, Jasper St. Pierre, Ray Strode Translations: Mattias Põldaru [et], Yaron Shahrabani [he], Daniel Mustieles [es], Khaled Hosny [ar], Fran Diéguez [gl], A S Alam [pa], Piotr Drąg [pl], Rafael Ferreira [pt_BR], Nilamdyuti Goswami [as], Alexander Shopov [bg], Matej Urbančič [sl] 3.7.3 ===== * Fix maximized windows jumping to other monitors [Alban; #556696] * Add 'switch-applications' keybinding [Florian; #688913] * Add a convenience method to focus the default window [Jasper; #689652] * Increase typical icon size to 96 [Jasper; #689651] * Port to XInput2 [Jasper; #688779] * Give dynamic keybindings a keybinding action [Florian; #682315] * Misc. fixes and cleanups [Jasper, Rui; #688777] Contributors: Alban Crequy, Rui Matos, Florian Müllner, Jasper St. Pierre Translations: Nilamdyuti Goswami [as], Piotr Drąg [pl], Yaron Shahrabani [he], Dr.T.Vasudevan [ta], ManojKumar Giri [or], Shankar Prasad [kn] 3.7.2 ===== * Fix spurious focus changes when showing desktop [Florian; #686928] * MetaPluginManager: don't send events to Clutter twice [Owen; #686406] * Add the ability to add shader hooks to MetaBackgroundActor [Giovanni; #669798] * Only process keyboard mapping events for the core X keyboard [Rui; #674859] * Import keybinding files from Metacity [Florian; #687672] * Add compositor hook to process keybindings selectively [Florian; #688202] * MetaBackgroundActor: add a setter for GLSL uniforms [Giovanni; #682536] * Misc. fixes and cleanups [Jasper, Rui, Florian, Rico; #688182] Contributors: Giovanni Campagna, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz Translations: Rafael Ferreira [pt_BR], Tobias Endrigkeit [de], Yaron Shahrabani [he] 3.7.1 ===== * screen: Ignore num-workspaces when using dynamic workspaces [Florian; #685439] Contributors: Florian Müllner Translations: Mattias Põldaru [et], Kjartan Maraas [nb], Мирослав Николић [sr, sr@latin], Marek Černocký [cs], Andika Triwidada [id], Daniel Mustieles [es], Fran Diéguez [gl], Matej Urbančič [sl] 3.6.1 ===== * Fix crash when opening large popup menus [Jasper; #681676] * window: Don't move the desktop window after monitor hotplug [Jasper; #681159] * Expose MetaPlugin to introspection [Evan; #671098] * Optionally delay focus changes in focus-follows-mouse mode [Florian; #678169] * Resize the guard window when the X screen is resized [Benjamin; #670396] * display: Only manage the default X screen [Jürg; #648156] * Misc cleanups: [Owen; #587255] Contributors: Benjamin Berg, Jürg Billeter, Evan Broder, Florian Müllner, Jasper St. Pierre, Owen Taylor Translations: Alexandre Franke [fr], Theppitak Karoonboonyanan [th], Sayak Sarkar [bn_IN], Sandeep Sheshrao Shedmake [mr], Ask H. Larsen [da], Shankar Prasad [kn], Alexander Shopov [bg], Aurimas Černius [lt], Ihar Hrachyshka [be], Kjartan Maraas [nb], Daniel Mustieles [es], Changwoo Ryu [ko], Yuri Myasoedov [ru], Tom Tryfonidis [el], Rūdolfs Mazurs [lv], Chris Leonard [en_GB], Piotr Drąg [pl], Fran Diéguez [gl], Gil Forcada [ca], Matej Urbančič [sl], Andika Triwidada [id], Carles Ferrando [ca] 3.6.0 ===== Translations: Alexander Shopov [bg], Daniel Korostil [uk], Rajesh Ranjan [hi], Krishnababu Krothapalli [te], Ani Peter [ml], Rūdolfs Mazurs [lv], Sweta Kothari [gu], Ihar Hrachyshka [be], Noriko Mizumoto [ja], Timo Jyrinki [fi], Mattias Põldaru [et] 3.5.92 ====== * screen: Allow NULL out arguments in meta_screen_get_size [Tomeu] * display: Add API to set wm_name / wm_keybindings [Florian; #671010] * Improve the not responding dialog [Jon, Florian; #684306] * Misc. bugfixes [Jasper] Contributors: William Jon McCann, Florian Müllner, Jasper St. Pierre, Tomeu Vizoso Translations: Gabor Kelemen [hu], Piotr Drąg [pl], Dr.T.Vasudevan [ta], Bruce Cowan [en_GB], Alexandre Franke [fr], Theppitak Karoonboonyanan [th], Gil Forcada [ca], Carles Ferrando [ca@valencia], Tobias Endrigkeit [de], Tom Tryfonidis [el], Nguyễn Thái Ngọc Duy [vi], Changwoo Ryu [ko], Ask H. Larsen [da], Rafael Ferreira [pt_BR], Marek Černocký [cs] 3.5.91 ====== * Do not include markup in app not responding dialog [Alex] * Fix subtracting unredirected windows from visible region [Jasper; #677116] * Minor improvements and bugfixes [Jasper, Florian; #682648, #682993] Contributors: Alexander Larsson, Florian Müllner, Jasper St. Pierre Translations: Dirgita [id], Piotr Drąg [pl], A S Alam [pa], Yuri Myasoedov [ru], Milo Casagrande [it], Nilamdyuti Goswami [as], Tom Tryfonidis [el], Duarte Loreto [pt], Fran Diéguez [gl], Nguyễn Thái Ngọc Duy [vi], Aurimas Černius [lt], Daniel Nylander [sv] 3.5.90 ====== * Fix logic for handling translations of the windows group [Owen; #681221] * Handle painting inside a Clutter clone [Owen; #681953] * Update overlay-key on settings changes [Florian; #681906] * Add keybinding for overlay-key [Florian; #665547] * Minor fixes and improvements [Javier, Florian] Contributors: Javier Jardón, Florian Müllner, Owen Taylor Translations: Sweta Kothari [gu], Muhammet Kara [tr], Khaled Hosny [ar], Sandeep Sheshrao Shedmake [mr] 3.5.5 ===== * Fix flickering around windows when using window group [Tom; #681221] Contributor(s): Tom Beckmann Translations: Chao-Hsiung Liao [zh_HK, zh_TW], Matej Urbančič [sl], Fran Diéguez [gl], Мирослав Николић [sr, sr@latin], Yaron Shahrabani [he], Kjartan Maraas [nb] 3.5.4 ===== * Make it possible to reimplement move-to-workspace keybindings from plugins [Giovanni; #674104] * Add a preference to ignore hide-titlebar-when-maximized hint [Rico; #678947] * window: Also use hide-titlebar-when-maximized when tiled [Florian; #679290] * Center modal dialogs on their parent instead [Florian; #674499] * Reduce amount of markup in translated messages [Matthias; #679660] * Fix focus problem after closing a window with focus-follows-mouse [Jasper; #675982] * Handle changes of the attach-modal-dialogs preference [Florian; #679904] * Do not restore tiling on unmaximize [Florian; #677565] * Misc. fixes and cleanups [Jasper Adriaanse, Jasper, Debarshi, Pavel; #679153, 673824] Contributors: Jasper Lievisse Adriaanse, Giovanni Campagna, Matthias Clasen, Florian Müllner, Debarshi Ray, Jasper St. Pierre, Rico Tzschichholz, Pavel Vasin Translations: Alexander Shopov [bg], Kjartan Maraas [nb], Yaron Shahrabani [he], Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Daniel Mustieles [es] 3.5.3 ===== * Simplify plugin system [Jasper; #676855] * meta-window-actor: Don't unredirect shaped windows [Jasper; #677657] * screen: Add new public meta_screen_get_current_monitor API [Tim; #642591] * frames: Increase the size of resize corners [Jasper; #677669] * window: Make some window methods public [Jasper; #678126] * Fix crash when running mutter stand-alone [Jasper; #678238] * meta-window-actor: Fix potential crash in shaping code [Jasper; #677977] * Misc. fixes [Jasper, Marc-Antoine, Rico] Contributors: Tim L, Marc-Antoine Perennou, Jasper St. Pierre, Rico Tzschichholz Translations: Daniel Mustieles [es], Matej Urbančič [sl], Khaled Hosny [ar], Bruno Brouard [fr], Fran Diéguez [gl] 3.5.2 ===== * keybindings: Remove 'toggle-recording' binding [Florian; #674376] * Switch to gtk-doc syntax [Jasper; #673752] * shaped-texture: never slice shape mask texture [Robert; #674731] * Make Mutter stop relying on Cogl including a GL header [Neil; #672711] * Make support for "XFree86" Xinerama mandatory [Owen; #674727] * meta_window_move_frame(): fix crash when frame is NULL [Owen; #675254] * Fix memory leaks [Pavel; #672640] * Code cleanups [Jasper; #671104 #674876 #676052] * Look for themes in XDG user data dir [Jasper; #675316] * Remove frame pixel caching [Jasper; #675111] * stack: Ignore keep-on-top property on maximized windows [Florian; #673581] * Misc. fixes [Javier, Jasper, Owen, Rico] Contributors: Robert Bragg, Javier Járdon, Florian Müllner, Neil Roberts, Jasper St. Pierre, Owen Taylor, Rico Tzschichholz, Pavel Vasin Translations: Praveen Illa [te], Luca Ferretti [it], Daniel Mustieles [es] 3.4.1 ===== * API change: the meta_display_add_keybinding() function added in 3.4 wasn't usable from a GNOME Shell extension, so has been changed to take a GSettings object rather than the name of a schema [Jasper; #673014] * Don't try to auto-maximize not-maximizable windows; this fixes the problem with the Nautilus desktop window being mis-positioned when enabled [Owen; #673566] * Fix a crash in the default plugin (not used in GNOME) [Giovanni; #673809] * Make the key work when set as the mouse button modifier [Florian; #662476] Contributors: Giovanni Campagna, Florian Muellner, Jasper St. Pierre, Owen Taylor Translations: Khaled Hosny [ar], Jordi Serratosa [ca], Carles Ferrando [ca@valencia], Christian Kirbach [de], Kristjan Schmidt [eo], Arash Mousavi [fa], Jiro Matsuzawa [ja], Shankar Prasad [kn], Aurimas Černius [lt], Yinghua Wang [zh_CN] 3.4.0 ===== * Fix crash when a full-screen window is opened [Jasper; #672797] * Fix memory leaks [Pavel; #672640] Contributors: Jasper St. Pierre, Pavel Vasin Translations: Marek Černocký, Petr Kovar [cz], Bruno Brouard [fr], Sweta Kothari [gu], Yaron Shahrabani [he], Changwoo Ryu [kr], Enrico Nicoletto [pt_BR], Yuri Myasoedov [ru], Muhammet Kara [tr], Nguyễn Thái Ngọc Duy [vi] 3.3.92 ====== * Automaximize large windows on map [Adel; #671677] * When unmaximizing windows, make sure the unminimized size is signficantly less than the maximized size [Adel; #671677] * Don't offer maximize option for windows larger than the screen [Jasper; #643606] * Always focus the window immediately underneath without restacking when closing a window [Jasper; #620744] * Avoid drawing shadows when two windows are tiled together [Rui; #643075] * Remove tooltips for window decorations [Florian; #645101] * Add org.gnome.mutter.dynamic-workspaces GSetting - when this is set to true, workspace counts are never saved to GSettings, avoiding pointless disk traffic for GNOME dynamic workspaces [Florian; #671568] * Add ::grab-op-begin, ::grab-op-end signals to MetaDisplay [Jasper; #670658] * Add meta_display_get_ignored_modifier_mask() [Florian; #665215] * Remove pointless wrapper methods on MetaPlugin [Jasper; #671103] * Fix frame drawing with 3.3.x GTK+ releases [Florian; #671796] * Build fixes [Jasper, Rico, Rui] * Misc bug fixes [Damien, Jasper, Lionel, Marius, Owen, Rui; #661256, #667437, #671601, #671087, #672374] Contributors: Stefano Facchini, Adel Gadllah, Lionel Landwerlin, Mariusz Libera, Rui Matos, Florian Müllner, Jasper St. Pierre, Damien Radtke, Owen Taylor, Rico Tzschichholz Translations: Nilamdyuti Goswami [as], Ihar Hrachyshka [be], Alexander Shopov [bg], David Planella [ca], Carles Ferrando [ca@valencia], Kenneth Nielsen [dk], Bruce Cowan [en_GB], Daniel Mustieles [es], Mattias Põldaru [et], Inaki Larranaga Murgoitio [eu], Timo Jyrinki [fi], Fran Diéguez [gl], Gabor Kelemen [hu], Changwoo Ryu [ko], Anita Reitere [lv], Kjartan Maraas [nb], Wouter Bolsterlee [nl], A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Yuri Myasoedov [ru], Daniel Nylander [se], Matej Urbančič [sl], Miroslav Nikolić [sr], Tirumurti Vasudevan [ta], Sasi Bhushan [te], Daniel Korostil [uk], Nguyễn Thái Ngọc Duy [vi], YunQiang Su [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW] 3.3.90 ====== * Update for Cogl API changes [Robert] * Bug fixes [Adel, Jasper; #659643] * Build fixes [Jasper, Owen] Contributors: Robert Bragg, Adel Gadllah, Jasper St. Pierre, Owen Taylor Translations: Ask H. Larsen [dk], Miroslav Nikolić [sr] 3.3.5 ===== * MetaShapedTexture no longer is a ClutterTexture subclass [Jasper; #660941] * Add meta_shaped_texture_get_image() [Jasper; #660941] * Cleanups [Rui, Jasper; #657639] * Depend on GTK+ 3.3.7 [Rico] Contributors: Rui Matos, Jasper St. Pierre, Rico Tzschichholz Translations: Kjartan Maraas [nb], Chao-Hsiung Liao [zh_HK, zh_TW] 3.3.4 ===== * Adapt to changes in GtkStateFlags [Owen] * Redo properties for applications menu corresponding to GTK+ changes - they are now _GTK_* not DBUS_*. [Ryan] * Fix crash on gnome-shell restart when a modal dialog is open [Owen; #668299] * Code cleanup [Florian; #666039] Contributors: Ryan Lortie, Florian Müllner, Owen Taylor Translations: Alexander Shopov [bg], Fran Diéguez [gl] 3.3.3 ===== * Add keybindings for tiling to left or right [Florian; #648700] * Support GTK+'s hide-titlebar-when-maximized hint [Florian; #665617] * Load _DBUS_APPLICATION_ID, _DBUS_UNIQUE_NAME, _DBUS_OBJECT_PATH property [Colin, Ryan; #664851] * Handle changes to workspaces-only-on-primary GSetting [Florian; #664853] * Don't use the Clutter default stage [Jasper; #664028] * Fix compilation with --disable-introspection [Lionel; #661871] * Fix problem where stage could end up mis-sized on startup with multiple monitors [Lionel] * Misc bug fixes [Adel, Lionel, Jasper; #666015] Contributors: Adel Gadllah, Lionel Landwerlin, Florian Müllner, Jasper St. Pierre Translations: Daniel Mustieles [es], Yaron Shahrabani [he], Kjartan Maraas [nb], Matej Urbančič [sk], Muhammet Kara [tr] 3.3.2 ===== * Move from GConf to GSettings for preferences [Florian; #635378] * Add meta_display_add_keybinding()/meta_display_remove_keybinding() to allow creating new keybindings at runtime [Florian; #663428] * Add suport for new _NET_WM_STATE_FOCUSED atom in _NET_WM_STATE to allow applications to draw unfocused windows differently [Rui; #661427] * Add meta_window_move_resize_frame() to allow specifying the size and position of a window via the outside dimensions of the window frame. * Don't activate window tiling when moving in snap mode [Rui; #662270] * Remove the ability to resize a window from the inner edge of the titlebar [Jasper; #660129] * Fix for deprecations in GTK+ [Jasper, Rico; #662574, #662895] * Misc bug fixes [Jasper, Rico, Rui; #662895, #642652, #660941, #662225] Contributors: Tim Cuthbertson, Rui Matos, Florian Müllner, Jasper St. Pierre, Rico Tzschichholz Translations: Jorge González (es), Kjartan Maraas (nb), Krishnababu Krothapalli (te), Nguyễn Thái Ngọc Duy (vi) 3.2.1 ===== * Allow keyboard window switching (alt-Tab) during drag-and-drop [Matthias, #660457] * Don't add invisible resize borders to fullscreen windows [Jasper, Owen; #659854] * Fix crash when toplevel windows were set to unexpected window types [Owen; #599988] * Correct problems with windows moving when restarting or switching window managers [Jasper; #660848] * Fix interaction of tiled windows with multiple monitors [Rui; #642580, #657519] * Make meta_display_unmanage_screen() public [Jasper; #660848] * Fix problem with turning off window decorations on the fly [Rui; #660773] * Fix spurious assertion failures with themes such as Nodoka [Sandro; #661286] * Misc bug fixes [Adel, Jasper, Rui; #660464, #660854, #662053] Contributors: Matthias Clasen, Sandro Mani, Rui Matos, Jasper St. Pierre, Owen Taylor Translations: Tommi Vainikainen [fi], Miroslav Nikolić [sr, sr@latin], Muhammet Kara [tr] 3.2.0 ===== * Fix _NET_WM_FRAME_EXTENTS not to include invisible borders [Jasper; #659848] * Fix application-specified window placement (-geometry) for invisible borders [Jasper; #659848] Contributors: Jasper St. Pierre Translations: Nilamdyuti Goswami [as], Carles Ferrando [ca@valencia], Petr Kovar [cz], Mario Blättermann [de], Inaki Larranaga [eu], Gabor Kelemen [hu], Takayoshi Okano [ja], Changwoo Ryu [ko], Djavan Fagundes [pt_BR] 3.1.92 ====== * Fix bug with unredirecting full-screen windows on multi-monitor - notably affected gnome-screensaver [Adel; #657869] * Disable top resizing of attached dialogs [Jasper; #657795] * Code cleanup [Jasper, Rui] * Misc bug fixes [Adel, Florian, Jasper, Rui; #658069, #659266, #659523, #659477] Contributors: Adel Gadllah, Rui Matos, Florian Müllner, Jasper St. Pierre Translations: Joan Duran [ca], Joe Hansen [dk], Jiro Matsuzawa [ja], Daniel Korostil [uk] 3.1.91.1 ======== * Fix problem where certain application updates would get lost [#657071, Owen] * Fix a problem where after resuming from the screensaver, things got slow [#658228, Jasper, Adel] * When a monitor is plugged or unplugged, keep existing windows on their current monitor [#645408, Alex] * Remove 'Mutter' title from alerts such as "The widow '%s' is not responding" [Matthias] * Remove pointless warning: Received a _NET_WM_MOVERESIZE message for %s; these messages lack timestamps and therefore suck. [Rui] * Misc bug fixes [Jasper] * Build fixes [Javier] Contributors: Matthias Clasen, Adel Gadllah, Javier Jardón, Alex Larsson, Rui Matos, Jasper St. Pierre, Owen Taylor Translations: Ihar Hrachyshka [be], Bruce Cowan [en_FB], Daniel Mustieles [es], Claude Paroz [fr], Andika Triwidada [id], Luca Ferretti [it], Rudolfs Mazurs [lt], Piotr Drąg [pl], Duarte Loreto [pt], Matej Urbančič [sl], Tirumurti Vasudevan [ta], Chao-Hsiung Liao [zh_KH, TW] 3.1.90.1 ======== * Fix crash when no windows are open [Adel; #657692] * Fix annotations for new strictness in gobject-introspection [Jasper, Owen] * Fix some errors with rounded frame drawing [Jasper; #657661] Contributors: Adel Gadllah, Jasper St. Pierre, Owen Taylor 3.1.90 ====== * Extend the draggable portion of window borders outside the visible frame for easy resizing with thin borders. (New draggable_border_width GConf key controls the total width of visible and invisible borders.) [Jasper; #644930] * Draw rounded window corners with antialising [Jasper; #628195] * Unredirect override-redirect fullscreen windows, such as full-screen 3D games to avoid any performance impact [Adel; #597014] * Add :resizable and :above properties to MetaWindow. [Tim; #653858] * Add MUTTER_DISABLE_FALLBACK_COLOR environment variable to allow visualizing places where a color is missing for gtk:custom() colors [Florian; #656112] * Don't attach modal dialogs to special windows like the desktop; add meta_window_is_attached_dialog() [Dan, #646761] * Make MetaBackgroundActor public, allow creating multiple instances (sharing a common texture), and add a :dim-factor property [Rui, Owen; #656433] * Fix attached dialogs to not be resizable from the top and to be position correctly [Jasper; #656619] * Misc bug fixes [Jasper, Rui; #656335, #657583] Contributors: Tim Cuthbertson, Adel Gadllah, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor, Dan Winship Translations: Alexander Shopov [bg], Jorge González [es], Fran Dieguez [gl], Yaron Shahrabani [he], Takeshi Aihana [ja], Aurimas Černius [lt], Kjartan Maraas [nb], A S Alam [pa], Yuri Kozlov [ru], Daniel Nylander [se], Theppitak Karoonboonyanan [th], Abduxukur Abdurixit [ug], Aron Xu [zh_CN] 3.1.4 ===== * Use better, much more subtle shadow definitions [Jakub; #649374] * Add the ability to use named GTK+ colors in theme files as gtk:custom(name,fallback) [Florian; #648709] * Port from GdkColor to GdkRGBA and from GtkStyle to GtkStyleContext [Florian; #650586] * Try to fix window bindings using the Super key [Owen; #624869] * Update to using more modern Cogl and Clutter APIs [Adel, Emmanuele, Neil; #654551 #654729 #654730 #655064] * Fix for srcdir != builddir builds [Thierry; #624910] * Make handling of focus appearance for attached dialogs more robust [Dan; #647712] * Misc bug fixes [Dan, Florian, Jasper, Owen, Rui; #642957 #649374 #650661 #654489 #654539] Contributors: Emmanuele Bassi, Adel Gadllah, Rui Matos, Florian Müllner, Neil Roberts, Jasper St. Pierre, Jakub Steiner, Owen Taylor Translations: Ihar Hrachyshka [be], Jorge González, Daniel Mustieles [es], Fran Dieguez [gl], Yaron Shahrabani [he], Takeshi Aihana [ja], Kjartan Maraas [nb], Rudolfs Mazurs [lv], Matej Urbančič [sl], Abduxukur Abdurixit [ug], Nguyễn Thái Ngọc Duy [vi] 3.1.3.1 ======= * Back API version down to "3.0" - the change to Meta-3.1.gir was unintentional [Owen] Translations: Yaron Shahrabani [he], Kjartan Maraas [nb], Muhammet Kara [tr] 3.1.3 ===== * Support dark window theme variants for windows with a dark widget theme; this is selected by the _GTK_THEME_VARIANT property [Florian, #645355] * Don't draw a shadow under windows with an alpha-channel - this fixes transparency for GNOME Terminal [Owen, Jasper; #635268] * Add a MetaWindow:wm-class property for notification [Jasper; #649315] * Add a MetaWindow:minimized property for notification [Florian] * Fix handing of unusual window shapes that Wine was setting causing some applications to draw wrong [Jasper; #627880] * Improve replacing another compositor and being replaced: release compositor selection in the right order and wait for compositors that get it wrong. [Colin, Owen; #653121] * Remove behavior where left clicking on a window border with the titlebar offscreen gave the window menu [Florian; #652369] * Don't set the global default textdomain, since Mutter is a library as well as an application [Dan; #649202] * Exit with the right (success or failure) exit status [Dan] * Code cleanup [Florian] * Miscellaneous bug fixes [Owen; #649114, #652507] Contributors: Florian Müllner, Jasper St. Pierre, Owen Taylor, Colin Walters, Dan Winship Translations: Ihar Hrachyshka [be], Daniel Mustieles [es], Yaron Shahrabani [he], Carles Ferrando [ca@valencia], Takeshi Aihana [ja], Fran Diéguez [gl], Matej Urbančič [sl], Miroslav Nikolic [sr], Muhammet Kara [tr], Daniel Korostil [uk] 3.0.2.1 ======= * When saving the session, use the "program name" rather than harcoding mutter, fixing session saving for gnome-shell [Matthias] https://bugzilla.gnome.org/show_bug.cgi?id=648828 Contributors: Matthias Clasen 3.0.2 ===== * Fix a crash when running without XKB support [Adam] https://bugzilla.gnome.org/show_bug.cgi?id=647777 * Fix smallish memory leaks [Colin] https://bugzilla.gnome.org/show_bug.cgi?id=649500 https://bugzilla.gnome.org/show_bug.cgi?id=649504 * Ignore mirrored monitors when listing monitors, fixing drag-and-drop problems in GNOME Shell [Owen] https://bugzilla.gnome.org/show_bug.cgi?id=649299 * Don't allow side-by-side tiling of non-maximizable windows like dialogs and utility windows [Dan] * Fix interaction of _NET_WM_WINDOW_OPACITY with window effects, making it work again with GNOME Shell https://bugzilla.gnome.org/show_bug.cgi?id=648613 Contributors: Adam Jackson, Colin Walters, Dan Winship Translations: Abduxukur Abdurixit [ug] 3.0.1 ===== * If WM_CLIENT_MACHINE isn't set, don't assume a window is remote; fixes behavior of Fox toolkit applications under GNOME Shell. https://bugzilla.gnome.org/show_bug.cgi?id=647662 [Colin] * Fix cases where windows could get stuck drawing as focused after an attached modal dialog was closed. [Dan] https://bugzilla.gnome.org/show_bug.cgi?id=647613 * Fix a bug where a window that is too big to be tiled side-by-side would behave strangely when using the gesture of dragging to the top to maximize. [Florian] Contributors: Florian Müllner, Colin Walters, Dan Winship Translations: Amitakhya Phukan [as], Kristjan Schmidt [eo], Muhammet Kara [tr] 3.0.0 ===== * Avoid crashing when you have a single window and try to move it between workspaces. [Dan] https://bugzilla.gnome.org/show_bug.cgi?id=642957 Contributors: Dan Winship Translations: Jordi Serratosa [ca], Petr Kovar [cz], Ask H. Larsen [da], Bruce Cowan [en_GB], Inaki Larranaga Murgoitio [eu], Gabor Kelemen [hu], Dirgita [id], Shankar Prasad [kn], Changwoo Ryu [ko], Wouter Bolsterlee [nl], Duarte Loreto [pt], Antonio Fernandes C. Neto, Rodrigo Padula de Oliveira [pt_BR], T. Vasudevan [ta], Nguyễn Thái Ngọc Duy [vi], Chao-Hsiung Liao [zh_HK, zh_TW] 2.91.93 ======= * Fix bug where, when a monitor was hot-plugged, all workspaces would collapse to a single workspace. (There are still issues when a secondary monitor is hot-plugged to the left of the primary monitor.) [Alex] https://bugzilla.gnome.org/show_bug.cgi?id=645408) * Fix a crash for the cycle_group action [Jasper] https://bugzilla.gnome.org/show_bug.cgi?id=645843 * Fix misdrawing of window shadows on some focus changes [Dan] https://bugzilla.gnome.org/show_bug.cgi?id=636904 * Export meta_get_replace_current_wm() to allow fixing a GNOME Shell bug with --replace [Colin] https://bugzilla.gnome.org/show_bug.cgi?id=645590 Contributors: Alexander Larsson, Jasper St. Pierre, Colin Walters, Dan Winship Translations: Alexander Shopov [bg], Christian Kirbach [de], Yaron Shahrabani [he], Rudolfs Mazurs [lv], A S Alam [pa], Yuri Myasoedov [ru], Daniel Nylander [se], Abduxukur Abdurixit [ug], Daniel Korostil [uj], Aron Xu [zh_CN] 2.91.92 ======= * Add a workspaces_only_on_primary preferences. When set, this makes workspaces switching only apply to windows on the primary monitor, while windows on other monitors are unaffected. * Export API for monitor handling [Alex] MetaScreen::monitors-changed signal meta_screen_get_primary_monitor() meta_window_is_on_primary_monitor() meta_window_get_monitor() MetaWindow::window-entered-monitor, Above_Tab from being a cycle_group binding to a switch_group binding [Rui] * Make plugin-loading failure fatal [Colin] * Add 'position-changed' signal to MetaWindowActor [Owen] * When 'live_hidden_previews' is enabled, position hidden windows to allow the creation of workspace previews [Owen] * Fix bug with opacity of MetaBackgroundActor Contributors: Rui Matos, Owen Taylor, Colin Walters Translations: Jorge González [es], Mattias Põldaru [et], Sweta Kothari [gu], Luca Ferretti [it], Changwoo Ryu [ko], Nguyễn Thái Ngọc Duy [vi] Bugs fixed: 641309 When live_hidden_previews is set, force placement for hidden windows 641310 MetaWindowActor: Add a 'positioned-changed' signal 641979 Visual glitch on workspace selector closing overview mode 641384 Make plugin loading failure fatal 642426 Don't pass handled key events to GTK+ 2.91.6 ====== * Add meta_screen_override_window_layout() to let a plugin set the workspace layout [Owen] * Add a 'size-changed' signal to MetaWindowActor [Florian] * Add meta_window_actor_is_destroyed() [Adel] * Fix problems with window tile previews when cancelling a move [Florian] * Port theme elements that use GTK+ drawing to use GtkStyleContext instead of the deprecated GtkStyle. [Florian] * Fix compiler warnings that were causing compilation failures [Jasper, Owen] * Misc bug fixes [Gabor, Jasper, Owen, Rui] Contributors: Adel Gadllah, Gabor Kelemen, Rui Matos, Florian Müllner, Jasper St. Pierre, Owen Taylor Translations: Khaled Hosny [ar], Alexander Shopov [bg], Petr Kovar [cz], Fran Diéguez [gl], Marios Zindilis [gr], Gabor Kelemen [hu], Kjartan Maraas [nb], A S Alam [pa], Daniel Nylander [se], Chao-Hsiung Liao [zh_HK, zh_TW] 2.91.5 ====== * Add a Above_Tab key symbol that can be used in key bindings to mean the key above the Tab key. This is now the default binding for cycle_group in both Mutter and Metacity. [Owen] * Add new frame states for tiled-on-the-left and tiled-on-the-right [Florian] * Add new background drawing functions that can be defined in a theme for single buttons. [Florian] * Draw the right button backgrounds for all custom button layouts [Florian] * Remove vestigal --composite/--no-composite command line options [Nickolas] * Fix building on GLES [Andreas] * Code cleanups [Adel, Owen] Contributors: Adel Gadllah, Nickolas Lloyd, Andreas Mueller, Florian Müllner, Owen Taylor Translations: Mattias Põldaru, Ivar Smolin [et], Gheyret T. Kenji [ug] Bugs fixed: 613124 Invalid visibility-related asserts in MutterWindow 626875 Fix handling of --composite and --no-composite command line options 629282 [PATCH] Fix errors building for gles-systems (clutter-eglx) 635569 Add an "Above_Tab" pseudo-keysym 635683 add specific button background for single button (per side) case 635686 button backgrounds broken with rtl locales 637330 [PATCH] theme: Add tiled_left/tiled_right frame states 2.91.4 ====== * Update for GTK+ 3 changes [Benjamin, Colin, Emmanuele, Florian] * Support maximizing a window by dragging to the top of the screen in the same way you can tile by dragging to the edge of the screen. [Ray, Florian] * Misc bug fixes [Milan, Owen] Contributors: Emmanuele Bassi, Milan Bouchet-Valat, Florian Müllner, Benjamin Otte, Ray Strode, Owen Taylor, Colin Walters Translations: Matej Urbančič [sl], Nguyễn Thái Ngọc Duy [vi] Bugs fixed: 630548 gnome-shell could auto-maximize windows when dragged to top edge of screen 636083 workspace: Consider text direction when switching 636301 Port testgradient example to GTK3 636302 Replace some GDK X11 calls with future-proof ones 636491 valgrind: meta_window_shape_new (meta-window-shape.c:79) 637802 ui: Adapt to GDK API changes 2.91.3 ====== * Better shadows: [Owen] - Shadows can be different for different window types and focus states - Shadows are larger by default, especially for the currently active window - Shadows for attached modal dialogs and menus are drawn not to overlap the attachment point. - Shadows follow the shape of shaped windows * Optimization: [Owen] - Avoid repainting in situations when windows are potentially restacked but aren't actually restacked. - Pay attention to partial stage repaints in obscured window calculations - Better optimization of painting obscured shadows; turn off shadows for maximized windows. - Move background repainting into Mutter; doing it here rather than in plugins allows not painting obscured parts of the background. * A new frame type 'attached' is added for attached modal dialogs and can be referenced in theme files with a theme version of 3.2. * Fix updating key bindings when the keyboard layout changes [Derek, Owen, Thomas] * Bug fixes [Adel, Florian] * Build fixes [Dan Williams, Diego, Javier, Owen] Contributors: Adel Gadllah, Javier Jardón, Florian Müllner, Derek Poon, Owen Taylor, Thomas Thurman, Diego Escalante Urrelo, Dan Williams Translations: Khaled Hosny [ar], Jorge González [es], Fran Diéguez [gl], Yaron Shahrabani [he], Kjartan Maraas [nb], Gheyret T. Kenji [ug] Bugs fixed: 634779 MetaWindowGroup: further optimize paints by using current scissor 634833 Draw the root window background 592382 improve shadow effect 628199 Add antialising to arc and line drawing operations 633002 meta-actor-window: Use G_UNLIKELY for TFP check 634771 MetaStackTracker: Avoid queueing resync for obvious no-ops 635421 Fix crash in check_needs_shadow 635493 configure.in: it's git, not Subversion 635528 configure.ac: move call to AM_GNU_GLIB_GETTEXT above cflags modification 635575 meta-window-actor: remove unused meta_window_actor_get_shadow_bounds 636083 workspace: Consider text direction when switching 2.91.2 ====== * Remove support for GTK+ 2 [Florian] * Adapt to deprecation of size_request deprecation in GTK+ [Matthias] * Include change from Metacity to fix confusion of mouse tracking when double-clicking on title bar [Owen] * Fix bug with the the window menu getting stuck when you alt-Tab [Owen] Contributors: Matthias Clasen, Florian Müllner, Owen Taylor Translations: Petr Kovar [cz] Bugs fixed: 633133 Remove compatibility for GTK+-2.0 633352 prepare for the demise of size_request 633398 Fix check for events on UI widgets 633401 Fix warning from synthesized events with GdkDevice 2.91.1 ====== * Default build is now GTK+ 3 build * Mutter namespace prefix is removed, in favor of consistent meta_ namespace prefixing [Owen]. Naming changes: MutterWindow => MetaWindowActor mutter_get_windows => meta_get_window_actors mutter_plugin_get_windows => meta_plugin_get_window_actors * Add missing values in MetaKeyBindingAction - this fixes a problem where key binding lookup wasn't working properly for some key bindings. [Dan] * Remove keysym parameter to meta_display_get_keybinding_action() - the function expected the default keysym for the keycode to always be passed [Dan] * Clean up installed header files - in particular, theme-parser.h is merged into a new public-only theme.h and private internals are moved to theme-private.h. * Fix problems with antialiased rendering of themes [Brandon, Owen, Nickolas] * Fix problem with parsing color constants in themes [Jon, Owen] * Build fixes [Colin] * Miscellaneous bug fixes [Giovanni, Rico] Contributors: Giovanni Campagna, Nickolas Lloyd, William Jon McCann, Owen Taylor, Rico Tzschichholz, Colin Walters, Dan Winship, Brandon Wright Translations: Fran Diéguez [gl], Yinghua Wang [zh_CN] Fixed bugs: 628401 tint and line draw ops rendering issues 628520 unfortunate namespacing 631487 Fix drawing of theme elements 632116 don't clobber gerrors 632149 Fill in missing MetaKeyBindingAction values 632155 meta_display_get_keybinding_action: remove keysym parameter 632474 Remove MetaRegion 632494 introspection: remove --allow-unprefixed 2.91.0 ====== * Enable side-by-side tiling via a gesture of dragging to the left or right edge of the screen. (enabled with an off-by-default GConf key) [Florian] * Allow breaking out of maximization/tiling using a alt-middle-button window resize [Owen, Florian] * Add the ability to have modal dialogs attached to their parent window (enabled with an off-by-default GConf key) [Maxim] * Draw with Cairo rather than GDK [Florian, Benjamin] * Add compatibility for changes in GTK+ 3 [Benjamin, Alban, Florian, Jasper, Matthias, Owen, Thierry] - libmutter-private is now only installed for GTK+ 3 builds - Theme parts of libmutter-private API are changed to take cairo_t rather than GdkDrawable * Update introspection build and annotations for new behavior of g-ir-scanner [Colin] * Fix bug that caused window menu options not to work [Owen] * Fix misbehavior of Wine windows [Owen, Alban] * Fix crashes from missing error traps [Adel] * Build fixes [Colin, Florian, Owen, Rob, Tomas] * Misc bug fixes [Adel, Jon, Owen, Nickolas, Tomas] * Cleanups [Adel, Benjamin, Florian] Contributors: Alban Browaeys, Matthias Clasen, Maxim Ermilov, Tomas Frydrych, Adel Gadllah, Nickolas Lloyd, William Jon McCann, Florian Muellner, Benjamin Otte, Thierry Reding, Rob Staudinger, Jasper St. Pierre, Owen Taylor, Colin Walters Translations: Alexander Shopov [bg], Mario Blättermann [de], Ask H. Larsen [dk], Michael Kotsarinis [el], Philip Withnall [en_UK], Jorge González [es], Fran Diéguez [gl], Bruno Brouard, Claude Paroz [fr], Yaron Shahrabani [he], Gabor Kelemen [hu], Luca Ferretti [it], Nils-Christoph Fiedler [nds], Kjartan Maraas [nb], A S Alam [pa], Piotr Drąg [pl], Duarte Loreto [pt], Antonio Fernandes C. Neto [pt_BR], Matej Urbančič [sl], Miloš Popović [sr, sr@latin], Tirumurti Vasudevan [ta], Aron Xu [zh_CN], Chao-Hsiung Liao [zh_HK, zh_TW] Fixed Bugs: 597763 With >2 workspaces, Window menu "Move to Another Workspace" menu doesn't work 598603 displays window size when moving terminal window 606158 "Always on top" triggers Window manager warning: Log level 8: meta_window_set_user_time: assertion `!window->override_redirect' failed 610575 make meta_screen_set_cursor public 613126 Do not cancel Alt+Tab grab due to Shift key events 623235 BadDamage error from XSubtractDamage 624757 Check for TFP usage after actually setting the pixmap 625712 [mutter-shaped-texture] Remove material_workaround 626583 Replace Gdk drawing API with cairo 627087 Mipmap emulation not working 627210 Crash with X error 628544 introspection: Build with --warn-fatal, drop fix-meta-rectangle.py hack 629127 build problem with recent gtk3 629232 Multiple syntax errors in file mutter-message.c when building Mutter for GNOME Shell dependencies 629350 [mutter-shaped-texture] Use a base material for all instances 629931 Allow breaking out from maximization/tiling during a mouse resize 630195 Use GDK error trapping straight-up 630203 Prepare mutter code for GTK3 rendering-cleanup 630671 prepare mutter for the demise of GtkObject 630843 gtk_window_set_visual was replaced by gtk_widget_set_visua 631147 Adapt to GTK API changes 631175 Mutter error compiling Gnome Shell 2.31.5 ====== * Support building with GTK+ 3.0 [Florian] * Remove deprecated usages for compatibility with GTK+ 3.0 [Claudio, Florian, Nickolas] * Export a boxed type for MetaRectangle [Owen] * Allow disabling -Werror with --enable-compile-warnings=yes [Nickolas] * Build fixes [Andreas, Florian, Owen] Contributors: Nickolas Lloyd, Andreas Mueller, Florian Müllner, Claudio Saavedra, Owen Taylor Translations: Petr Kovar [cz], Jorge González [es], Fran Diéguez [gl], Yaron Shahrabani [he], Matej Urbančič [sl] Fixed Bugs: 587991 - Remove deprecated GTK+ symbols 616275 - -Werror should not be enabled by default (or should be possible to disable) 622303 - Allow building with Gtk+-3.0 622800 - Make mutter more gtk+ 3.0 friendly 623335 - Make MetaRectangle a boxed type 623639 - Work around g-ir-scanner problem with Gdk.Rectangle 624166 - src/core/util.c: Fix warning in case WITH_VERBOSE_MODE is not set 2.31.4 ====== * Clean up MutterPlugin effect interface [Maxim] * Track damage as the bounding box, a significant optimizations for rapidly drawing clients [Robert] * Add meta_window_is_remote() [Colin] * Add meta_add_debug_topic() for turning on logging of specific topics [Colin] * Fix bug with window unmaximization [Owen] Contributors: Robert Bragg, Maxim Ermilov, Owen Taylor, Colin Walters Translations: Yaron Shahrabani (he), Fran Diéguez (gl), Kjartan Maraas (nb), A S Alam (pa) Fixed Bugs: 611838 - expose sub-stage redraws by streaming raw updates to ClutterX11TexturePixmap 620585 - Add meta_window_is_remote 620860 - function ‘meta_display_open’ 621082 - MutterPluginManager should call plugin->switch_workspace, when screen doesn't have any window. Or function should be renamed. 621413 - Maximize/Unmaximize not behaving properly for some non-gnome based programs 2.31.2 ====== * Theme enhancements [Owen] - Add a flexible version mechanism for themes - metacity-theme-3.xml is now supported, and can include version="> 3.2" type attributes on the root element or any subelement. - Add frame_x_center/frame_y_center variables - Allow a theme to turn on title ellipsization * Performance enhancements: - Stream raw damage updates to ClutterX11TexturePixmap to enable partial stage updates when windos change [Robert] - Don't trap XErrors in meta_compositor_process_event [Adel] * Add meta_prefs_override_preference_location(); this allows a plugin like GNOME Shell to redirect preferences to a plugin-specific location. [Owen] * Support a _MUTTER_HINTS window property; this is a string property holding key-value pairs with plugin-specific interpretation [Tomas] * Build with GSEAL_ENABLE [Florian, Javier] * Add meta_display_get_leader_window() [Tomas] * Add meta_display_sort_windows_by_stacking [Colin] * Export meta_display_get_last_user_time() meta_display_xserver_time_is_before() meta_window_foreach_ancestor(), meta_window_foreach_transient() meta_window_lower() meta_window_raise() meta_window_set_demands_attention() meta_window_unset_demands_attention() [Colin] * Bug fixes [Dan, Edward, Owen, Tomas] * Build fixes [Owen, Dominique, Vincent] Contributors: Robert Bragg, Adel Gadllah, Tomas Frydrych, Javier Jardón, Dominique Leuenberger, Florian Müllner, Edward Sheldrake, Owen Taylor, Vincent Untz, Colin Walters, Dan Winship Translations: Xandru Armesto Fernandez (ast), Khaled Hosny (ar), Petr Kovar (cz), Mario Blättermann, (de), Jorge González (es), Inaki Larranaga Murgoitio [eu), Claude Paroz (fr), Luca Ferretti (it), Gintautas Miliauskas (lt), Pavol Šimo (sk), Matej Urbančič (sl) Fixed Bugs: 591842 - ellipsize titles when oversize 592503 - Add a flexible version mechanism 595496 - Use accessor functions instead direct access (use GSEAL GnomeGoal) 596659 - Fix handling of grabbed key events 613123 - Framework for plugin-specific per-window hint 613125 - Add meta_display_get_leader_window() 613127 - Keep num_workspaces key in sync with the actual workspace number 613136 - remove over-restrictive assert from meta_prefs_get_workspace_name() 613398 - Don't trap XErrors in meta_compositor_process_event 615586 - Allow redirecting preferences to a different GConf key 615672 - cant' compile mutter error: dereferencing pointer ‘p’ does break strict-aliasing rules 616050 - alt-tab infrastructure patches 616274 - mutter from git fails with gcc 4.5 (on new warning) 616546 - On dual screen maximized windows dragged to the second screen no longer update their contents 618138 - Work around COGL bug causing flash for new windows 618613 - Fix crash with --sync option 2.29.1 ====== * Support and require Clutter 1.2 (Owen) * Add meta_display_get_keybinding_action() (Colin, Dan) * Add meta_window_get_wm_class_instance() (Tomas) * Remove workaround for bug fixed in intel driver Q2/2009 release (Robert) * Build fixes (Owen, Brian, Nguyễn Thái Ngọc Duy) Contributors: Robert Bragg, Brian Cameron, Tomas Frydrych, Nguyễn Thái Ngọc Duy, Owen Taylor, Colin Walters, Dan Winship Translations: Alexander Shopov (bg), Mario Blättermann (de), Bruno Brouard (fr), Nils-Christoph Fiedler (nds), Piotr Drąg (pl), Aron Xu (zh_CN) Fixed Bugs: 610862 Support and require Clutter 1.1 612506 mutter 2.29.0 fails to compile on Solaris 613100 [MetaDisplay] Expose meta_display_get_keybinding_action 613121 Remove workaround for multitexturing with old intel drivers 613128 [MetaWindow] Accessor for the instance part of WM_CLASS property 613278 meta_display_get_keybinding_action: strip out uninteresting modifiers 2.29.0 ====== * Improve appearance of scaled down windows using mipmap emulation (Owen) * Added signals: MetaDisplay::window-created, MetaDisplay::window-marked-urgent, MetaDisplay::window-demands-attention, MetaWindow::unmanaged (Colin, Tomas) * Added properties: MetaWindow:demands-attention, MetaWindow:urgent, MetaWindow:maximized-horizontally, MetaWindow:maximized-vertically (Florian, Tomas) * Fix nasty crash when workspace "struts" changed during a window move (Jon, Owen) * Bug fixes (Dan, Maxim, Neil, Owen, Tomas) * Build fixes (Colin, Emmanuele, Nickolas, Owen, Richard) * Merge Metacity changes since 2.26. Includes themable sound support via libcanberra (Owen) Contributors Emmanuele Bassi, Maxim Ermilov, Tomas Frydrych, Richard Hughes, Nickolas Lloyd, Florian Müllner, Jon Nettleton, Neil Roberts, Owen Taylor, Colin Walters, Dan Winship Additional Metacity contributors: Thomas Hindoe Paaboel Andersen, Peter Bloomfield, Matthias Clasen, Matt Kraai, Claude Paroz, Lennart Poettering, Ray Strode, Thomas Thurman, Vincent Untz, Tomislav Vujec, Tomeu Vizoso, Travis Watkins, 'alexisdm59' Translations: Khaled Hosny (ar), Petr Kovar (cz), Kjartan Maraas (nb), Djavan Fagundes (pt_BR), Nils-Christoph Fiedler (nds), Matej Urbančič (sl), Vincent Untz Fixed Bugs: 588065 Adds demands-attention signal to the window class 591913 Fails to skip current window on alt+tab when another window is asking for attention 592567 Dereferencing NULL in mutter_window_get_workspace() 597052 Add signal to MetaDisplay so we know when a window has demanded-attention 598289 Add "window-created" signal to MetaDisplay, "unmanaged" signal for MetaWindow 598473 "XXX specified twice for this theme" messages not in sync with metacity. 598600 "Visual Bell" option in Metacity causes Mutter to crash 600068 notifications for window urgency hint 601228 rdesktop does not get keypress signals 602349 [PATCH] trivial - fix compilation warning in mutter 602740 Remove XOR gc only used in removed reduced-resources mode 602870 Fix compilation with older libGL 604200 Compile issue: Use of deprecated clutter functions 606388 mutter fails to build when using ld with --no-add-needed 607125 Fails to build with latest introspection data 607398 Do not use CGL_* symbols 607746 reduce gconf roundtrips at startup 608800 alt-dragging gimp windows crashes gnome-shell 609350 Mutter does not support the COGL_DEBUG environment variable 609546 meta_workspace_set_builtin_struts(): optimize out non-changes 609585 Merge libcanberra usage from Metacity 609657 Use cogl multitexture API when drawing MutterShapedTexture 609665 Bug fixes from Fedora RPM 609710 screencast recording broke 610391 Fix crash on startup with list bindings 2.28.0 ====== * New exported API: meta_window_get_stable_sequence() [Colin] meta_window_get_transient_for_as_xid() [Tomas] MutterScreen::workareas-changed signal [Tomas] * Fix a problem where changes processed from a Clutter event callback wouldn't get handled before the screen was next repainted, causing flashing [Owen] * Remove MetaAltTabHandler as no longer needed [Dan] * Bug fixes [Colin, Owen] Contributors: Tomas Frydrych, Owen Taylor, Colin Walters, Dan Winship Translations: Christian Kirbach (de), Claude Paroz (fr) 2.27.5 ====== * Fix bug in GConf schemas where the overview activation key was specified as '' not 'Super_L'. Contributors: Colin Walters Translation: Denis Arnaud (br) 2.27.4 ====== * Big code cleanup: when talking about multiple monitors, call them "monitors", not "xineramas". [Dan] * Accessors added or made public: meta_screen_get_n_monitors(), meta_screen_get_monitor_geometry() meta_window_get_user_time() and MetaWindow:user-time property. [Colin, Dan] * Set _GNOME_WM_KEYBINDINGS=Metacity,Mutter on the _NET_SUPPORTING_WM_CHECK window so that gnome-keybinding-properties can figure out to show the Metacity keybindings when Mutter is running. [Owen] * Bug and build fixes [Colin, Owen] Contributors: Owen Taylor, Colin Walters, Dan Winship Translation: Jorge González (es), Inaki Larranaga Murgoitio (eu), Gabor Kelemen (hu) Bugs fixed: 592393 - Clicking on a minimized window in the overview doesn't focus the window 593399 - Add meta_display_get_grab_op() 593404 - Make MUTTER_DEBUG_XINERAMA override active Xinerama 593407 - Add 'skip-taskbar' accessor to MetaWindow. 593686 - Add meta_screen_get_monitors() 594067 - Export a _GNOME_WM_KEYBINDINGS property 2.27.3 ====== * Key handling improvements: - enforce that every key is handled no more than once. - mutter_plugin_begin_modal() and mutter_plugin_begin_modal() allow putting a plugin into a "modal" state where it has exclusive access to key and pointer events. - Add "tab_popup_select", "tab_pop_cancel" pseudo-keypress-handlers that plugins can use to get notification when Alt-Tab ends [Owen] * Accessors added or made public: meta_window_is_override_redirect(), meta_window_is_mapped(), meta_display_xwindow_is_a_no_focus_window(), meta_display_get_grab_op(), meta_window_is_skip_taskbar(), meta_window_is_modal(), all of errors.h [Colin, Owen, Michael, Steve, Tomas] * Fix for various GTK+ deprecations [Javier] * Bug fixes [Colin, Frédéric, Owen, Thomas, Tomas, Volker] Contributors: Javier Jardón, Steve Frécinaux, Tomas Frydrych, Michael Meeks, Frédéric Péters, Volker Sobek, Owen Taylor, Thomas Thurman, Colin Walters Translation: Fran Dieguez (gl), Gabor Kelemen (hu), Daniel Nylander (se) Bugs fixed: 589457 - Fix up window property notification for "title" 590911 - Do not run plugin effects on WM startup 590978 - API to query whether window is in modal state 591367 - Be silent by default 591566 - install errors.h header ... 591788 - Add meta_window_is_override_redirect 591836 - mutter mishandles opacity 591913 - Fails to skip current window on alt+tab when another window is asking for attention 592393 - Clicking on a minimized window in the overview doesn't focus the window 592699 - Remove deprecated Encoding key from desktop files 592742 - Avoid accessing freed memory when being replaced 593399 - Add meta_display_get_grab_op() 593404 - Make MUTTER_DEBUG_XINERAMA override active Xinerama 593407 - Add 'skip-taskbar' accessor to MetaWindow. ----------------------------- Older Metacity News ----------------------------- 2.26.0 ====== Thanks to Luca Ferretti, Matt Kraai, and Neil Jagdish Patel for improvements in this version. - queue frame resize on window undecorate (Neil) - fix description of desktop background (Luca) (#569649) - wrap g_error calls in braces (Matt) Translations Amitakhya Phukan (as), Mikel González (ast), Ihar Hrachyshka (be@latin), Runa Bhattacharjee (bn_IN), David Planella (ca), Petr Kovar (cs), Ask Hjorth Larsen (da), Christian Kirbach (de), Jennie Petoumenou (el), David Lodge (en_GB), Jorge González (es), Mattias Põldaru (et), Iñaki Larrañaga Murgoitio (eu), Ilkka Tuohela (fi), Claude Paroz (fr), Ankit Patel (gu), Mark Krapivner (he), Rajesh Ranjan (hi), Gabor Kelemen (hu), Luca Ferretti (it), Takeshi AIHANA (ja), Changwoo Ryu (ko), Gintautas Miliauskas (lt), Sangeeta Kumari (mai), Sandeep Shedmake (mr), Wouter Bolsterlee (nl), Manoj Kumar Giri (or), Duarte Loreto (pt), Leonardo Ferreira Fontenelle (pt_BR), Adi Roiban (ro), Yuriy Penkin (ru), Daniel Nylander (sv), I. Felix (ta), Krishna Babu K (te), Theppitak Karoonboonyanan (th), Clytie Siddall (vi), Chao-Hsiung Liao (zh_HK), Chao-Hsiung Liao (zh_TW) 2.25.144 ======== Thanks to Matthias Claesen, Matt Kraai, Elijah Newren, Owen Taylor, and Thomas Thurman for improvements in this version. - Optimise window property lookup (Thomas) (#549886) - Fix slip in the above (Matt) - Several memory leaks fixed (Matthias) (#552303, #552973, #552307) - Fix longstanding crasher about colourmaps (Owen) (#568365) - Alt+middle/right buttons can be switched (Thomas) (#437910) - Support _NET_WM_MOVERESIZE_CANCEL (Elijah) - minor fix paving the way for a theme editor (Thomas) Translations David Planella (ca), Jorge González (es), Mattias Põldaru (et), saudat mohammed (ha), Yuval Tanny\n (he), Gabor Kelemen (hu), Onye, Sylvester (ig), Changwoo Ryu (ko), Raivis Dejus (lv), Kjartan Maraas (nb), Daniel Nylander (sv), Fajuyitan, Sunday Ayo (yo), 甘露 (Gan Lu) (zh_CN) 2.25.89 ======= Thanks to Yanko Kaneti, Frederic Peters, Thomas Thurman, and Colin Walters for improvements in this version. - The maximisation key is a toggle. (Thomas) (#343824) - "Unmaximise" is now called "restore". (Thomas) (#343824) - New thread handling call for gconf (Frederic) (#565517) - Add screenshot commands back which had been removed (Yanko) (#565343) - move_to_corner_se keybinding fixed (Thomas) - Windows on other workspaces which attempt to present themselves are marked as needing attention (Colin) (#482354) - End the grab op when the user clicks the titlebar (Thomas) (#401028) Translations Jorge González (es) 2.25.55 ======= Thanks to Erwann Chenede for improvements in this version. - Fix build on Solaris (Erwann) (#564123) Translations Mattias Põldaru (et), Luca Ferretti (it) 2.25.34 ======= Thanks to Matt Kraai for improvements in this version. - Fixes to Thomas's earlier fixes (Matt) (#562939) Translations None 2.25.21 ======= Thanks to Thomas Thurman for improvements in this version. - Fixes to allow building without compositor again (Thomas) - Fixes for -Wall problems (Thomas) - Various tool updates (Thomas) Translations: none 2.25.13 ======= Thanks to Thomas Thurman for improvements in this version. - Add casts to fix failure to build from source on 64bit hosts (Thomas) (#562106) - Added script to produce annoucements (Thomas) Translations Jorge González (es) 2.25.8 ====== Thanks to Brian Cameron, Maxim Ermilov, Daniel Macks, Elijah Newren, Frederic Peters, Thomas Thurman, David Trowbridge, and Olav Vitters for improvements in this version. - Reorder compiler flags (Daniel) (#562033) - Fix compositor switch (Daniel) (#560990) - Remove spurious warnings about operations on window "none" (Thomas) - Fix _POSIX_C_SOURCE which was breaking OS X builds (Thomas) (#561962) - -Werror -Wall and -ansi are now standard compile flags (Thomas) - Merge screen and window keybindings files; fix minor alt-tab bug in the process (Thomas) (#528337) - Support _NET_WM_FULLSCREEN_MONITORS (David) - Remove some deprecated calls (Thomas) (#560445) - Clean up #includes (Maxim) (#560449) - Update description of raise_on_click (Elijah) - First dialogue delegated to zenity (Thomas) - fix theme-parser typo (Olav) - double-quote variable names in messages (Thomas) (#558309) - fix accidental renaming of run_command_terminal (Thomas) (#557943) - some null checks; problems exposed by new GDM (Brian) (#558058) - ignore mouse button modifier if it's missing (Thomas) (Launchpad 258054, Launchpad 266929) - fix docbook markup (Frederic) Translations Astur (ast), Jorge González (es), Thomas Thurman (la), Leonardo Ferreira Fontenelle (pt_BR), Daniel Nylander (sv) 2.25.5 ====== Thanks to Thomas Thurman for improvements in this version. - Allow third-party apps to decide whether a window appears on all workspaces (Thomas) (#557536) - Fixed keybindings script (again) (Thomas) Translations David Planella (ca), Robert Millan (ca@valencia) 2.25.3 ====== Brown paper bag release which fixes numerous build problems from last night's release of 2.25.2. Apologies. Thanks to Murray Cumming, Thomas Thurman, and Götz Waschk for improvements in this version. - Fix distcheck (Thomas) (#557356) - add libm reference (Götz) (#557357) - fix docbook tags (Murray) (#557337) Translations Yavor Doganov (bg), David Planella (ca), Robert Millan (ca@valencia), Kenneth Nielsen (da), Hendrik Richter (de), Ivar Smolin (et), Claude Paroz (fr), Seán de Búrca (ga), Launchpad Translations Administrators (hr), Gabor Kelemen (hu), Thomas Thurman (la), Žygimantas Beručka (lt), Kjartan Maraas (nb), Duarte Loreto (pt), Djavan Fagundes (pt_BR), Mugurel Tudor (ro), Pavol Šimo (sk), Laurent Dhima (sq), Горан Ракић (sr), Theppitak Karoonboonyanan (th), Funda Wang (zh_CN) 2.25.2 ====== Thanks to Joe Marcus Clarke, Murray Cumming, Tomas Frydrych, William Lachance, Matthew Martin, Christian Persch, Thomas Thurman, and Vincent Untz for improvements in this version. - Add handler for SIGTERM (Joe) (#553980) - Minimised windows are necessarily obscured (Matthew) (#528927) - Build fixes with the above (Christian, Tomas, Thomas) (#557335) (#557201) (#469361) - Changed keybindings to be in a single place (Thomas) (#469361) - Add new document about themes (Murray) - Remove obsolete support for fallback icons (Thomas) - Pass modified mouse events to panels (William) (#554428) - Change where desktop files should go (Vincent) (#549479) Translations Yavor Doganov (bg), David Planella (ca), Kenneth Nielsen (da), Hendrik Richter (de), Ivar Smolin (et), Claude Paroz (fr), Seán de Búrca (ga), Launchpad Translations Administrators (hr), Gabor Kelemen (hu), Thomas Thurman (la), Žygimantas Beručka (lt), Kjartan Maraas (nb), Duarte Loreto (pt), Djavan Fagundes (pt_BR), Mugurel Tudor (ro), Pavol Šimo (sk), Laurent Dhima (sq), Горан Ракић (sr), Theppitak Karoonboonyanan (th), Funda Wang (zh_CN) 2.25.1 ====== Thanks to Thomas Thurman for improvements in this version. - Fix small memory leak, found by Matthias Clasen (Thomas) (#549952) - Added move_to_center keybinding suggested by Khanh-Dang Nguyen Thu Lam (Thomas) (#549979) - Compositor can be turned on and off from the command line (#545323) (Thomas) Translations Khaled Hosny (ar), Petr Kovar (cs), Iñaki Larrañaga Murgoitio (eu), Ilkka Tuohela (fi), Žygimantas Beručka (lt), Duarte Loreto (pt), Djavan Fagundes (pt_BR), Laurent Dhima (sq) 2.25.0 ====== Thanks to Patrick Niklaus, Ted Percival, Eric Piel, Akira TAGOH, and Thomas Thurman for improvements in this version. - Fix memory allocation problem in struts (Eric) (probably #468075) - Ensure windows which start maximised know where to jump back to, so they don't warp to other screens (Ted) (#504692) - Added header comments to some files (Thomas) - Icons for windows which are uncooperative enough not to provide an icon are taken from the theme, not built in (Patrick) (#524343) - Added manual page for metacity-message (Akira, from Debian downstream) Translations Khaled Hosny (ar), Petr Kovar (cs), Ilkka Tuohela (fi), Duarte Loreto (pt), Djavan Fagundes (pt_BR) 2.23.89 ======= Thanks to Thomas Thurman for improvements in this version. - Added DOAP file. (Thomas) Translations Khaled Hosny (ar), Luca Ferretti (it), Takeshi AIHANA (ja), Wouter Bolsterlee (nl), Vladimir Melo (pt_BR), Daniel Nylander (sv) 2.23.55 ======= Thanks to Elijah Newren and Thomas Thurman for improvements in this version. Contrary to rumour, this release does not add tabbing to everything. - Display theme name in title bar of theme viewer (Thomas) (#430198) - Allow toggling of non-compositor effects (Thomas) (#92867) - Add some extra null checks (Thomas) (#422242) - Check for double-freeing at the time of workspace freeing (Elijah) (#361804) - Don't generate log messages unless we're logging (Thomas) - Two windows which don't belong to any application can't be considered to belong to the same application (Thomas) - Various tidyings (Thomas) Translations Yavor Doganov (bg), Gabor Kelemen (hu), Kjartan Maraas (nb), Matej Urbančič (sl), Daniel Nylander (sv), Theppitak Karoonboonyanan (th) 2.23.34 ======= Thanks to Thomas Thurman for improvements in this version. - Commenting and tidying (Thomas) - Fix possible compositor crash (Thomas) (#530702) Translations Khaled Hosny (ar), Yavor Doganov (bg), Jorge González (es), Kjartan Maraas (nb), Yannig Marchegay (Kokoyaya) (oc), Theppitak Karoonboonyanan (th), Clytie Siddall (vi) 2.23.34 ======= Thanks to Thomas Thurman for improvements in this version. - Commenting and tidying (Thomas) - Fix possible compositor crash (Thomas) (#530702) Translations Khaled Hosny (ar), Yavor Doganov (bg), Jorge González (es), Kjartan Maraas (nb), Yannig Marchegay (Kokoyaya) (oc), Theppitak Karoonboonyanan (th), Clytie Siddall (vi) 2.23.34 ======= Thanks to Thomas Thurman for improvements in this version. - Various commenting (Thomas) - Ensure you can turn off compositor with "configure" (Thomas) - Ensure you can turn off gconf with "configure" (Thomas) (#530870) Translations Clytie Siddall (vi) 2.23.21 ======= Thanks to Robert Escriva, Iain Holmes, Matt Krai, Thomas Thurman, and Chris Wang for improvements in this version. - Add shadow ability for menus and tooltips (Iain) (#517442) (#517524) - Fix possible crashes in compositor (Iain) (#534569) (#528787) - Major reorganisation of compositor code (Iain) - Initial version of XRender backend for the compositor (Iain) - New basic public API for compositor (Iain) - Window decoration updates colour when GTK theme changes (Robert) (#511826) - Minor code cleanup for pedantic compilers (Thomas) - Further code cleanup for pedantic compilers (Matt) (#526049) - The atom list appears only once in the code (Thomas) (#530843) - Don't attempt to read attributes of invalid windows (Chris) (#530485) Translations Khaled Hosny (ar), Gabor Kelemen (hu), Kjartan Maraas (nb), Tino Meinen (nl), Theppitak Karoonboonyanan (th) 2.23.13 ======= Thanks to Erwann Chenede and Carlos Garnacho for improvements in this version. - Re-enable cascading (Erwann) (#529925) - Propagate opacity to frame windows (spec compliance!) (Carlos) Translations - None this time! 2.23.8 ====== Thanks to Lucas Rocha, Iain Holmes, and Jens Granseuer for improvements in this version. * No need to symlink to .desktop files (Lucas) * Fixes to compositor's dealings with overlay windows (Iain) * C89 fixes (Jens) Translators: Khaled Hosny (ar), Amitakhya Phukan (as), Ihar Hrachyshka (be@latin), Petr Kovar (cs), Rhys Jones (cy), Kenneth Nielsen (da), Andre Klapper (de), Jorge González (es), Iñaki Larrañaga Murgoitio (eu), Ilkka Tuohela (fi), Claude Paroz (fr), Seán de Búrca (ga), Ignacio Casal Quinteiro (gl), Yuval Tanny (he), Gabor Kelemen (hu), Luca Ferretti (it), Takeshi AIHANA (ja), Shankar Prasad (kn), Changwoo Ryu (ko), Arangel Angov (mk), sandeep shedmake (mr), Kjartan Maraas (nb), Nabin Gautam (ne), Wouter Bolsterlee (nl), Eskild Hustvedt (nn), Yannig Marchegay (Kokoyaya) (oc), Tomasz Dominikowski (pl), Duarte Loreto (pt), Vasiliy Faronov (ru), Daniel Nylander (sv), Theppitak Karoonboonyanan (th), Baris Cicek (tr), Maxim Dziumanenko (uk), Clytie Siddall (vi), Woodman Tuen (zh_HK), Woodman Tuen (zh_TW) 2.23.5 ====== Thanks to Lucas Rocha, Owen Taylor, and Thomas Thurman for improvements in this version. - Updates of useless preferences don't crash (Thomas) (#526016) - Compliance with new gnome-session (Lucas) (#525051) - Preview widget doesn't crash on broken themes (Thomas) (Launchpad 199402) - Initially iconic windows don't unminimise (Owen) (#491090) - Move ~/.metacity to ~/.config/metacity (Thomas) (#518596) - Metacity doesn't stay around when replaced (Thomas) - Extra check for null return in a function (Thomas) - Displays are singletons, simplifying code (Thomas) (#499301) Translations Jorge González (es), Eskild Hustvedt (nn), Baris Cicek (tr), Clytie Siddall (vi) 2.23.3 ====== Thanks to Marco Pesenti Gritti, Iain Holmes, Josh Lee, Thomas Thurman, and Matthew Wilson for improvements in this version. - Workspaces whose name is the same as the standard name, plus some string, are not cut off. (Thomas) (#453678) - Improve compositor performance (Iain) (#522166) - Draw wallpaper correctly when we start up with compositor (Iain) (#522599) - Several other smaller compositor fixes (Iain) - Don't draw shadows on shaped windows unless they have frames (Iain) (#505333) - Newly-created keep-above windows get focus (Marco) (#519188) - Allow moving workspace when dragging with modifier key (Matthew) (#474195) Translations Kenneth Nielsen (da), Gabor Kelemen (hu), Vasiliy Faronov (ru), Daniel Nylander (sv), Maxim Dziumanenko (uk), Woodman Tuen (zh_HK) 2.23.2 ====== Removed some debug statements introduced in 2.23.1. Brown paper bag release. 2.23.1 ====== Thanks to Cosimo Cecchi, Jens Granseuer, Jim Huang, Andrea Del Signore, and Thomas Thurman for improvements in this version. (Cosimo's patch was very similar to another received from Jason Ribero.) - Allow horizontal and vertical maximisation using the mouse (Cosimo/Jason) (#358674) - Allow "spacer" as a value for buttons, for blank space (Andrea) (#509165) - Remove unused code (Jim) - refactor preferences handling (Thomas) - make sure we're valid C89 (Jens) (#518917) - some messing with tool scripts (Thomas) Translations Jorge González (es), Claude Paroz (fr), Woodman Tuen (zh_HK), Woodman Tuen (zh_TW) 2.23.0 ====== Thanks to Matthias Clasen, Mikkel Kamstrup Erlandsen, Jim Huang, Thomas Thurman, and Thomas Wood for improvements in this version. - the preview widget can draw shaped windows properly! (Thomas W, #460018) - refactored handling of boolean and enumerated gconf preferences; refactoring of string and integer preferences will follow shortly (Thomas T) - Applications asking to move and resize windows at the same time have both their requests granted (Mikkel) (#448183) - Windows marked "skip taskbar" don't appear in the ctrl-alt-tab list (Matthias) (#106249) - fix session management detection (Thomas T) (#328210) - when resizing with the keyboard, the cursor stays on a window edge if you escape, whichever direction you were going (Thomas T) (#436257) - fix major breakage when gconf was turned off in configure (Jim) (#515019) - fix major breakage when verbose was turned off in configure (Jim) (#515152) - fix name of verbose option in help (Thomas T) - various bits of messing around with release scripts (Thomas T) Translations Ihar Hrachyshka (be@latin), Ilkka Tuohela (fi), Ignacio Casal Quinteiro (gl), Shankar Prasad (kn), Changwoo Ryu (ko), Nabin Gautam (ne), Wouter Bolsterlee (nl) 2.21.13 ======= Thanks to Michael Meeks and Thomas Thurman for improvements in this version. - Only use compositor version if we have a compositor (Thomas) (#514453) - Remove workaround for a problem in an ancient GTK version (Thomas) (#513737) - Compositor efficiency fixes (Michael) - Various tools added (Thomas) Translations Amitakhya Phukan (as), Rhys Jones (cy), Andre Klapper (de), Takeshi AIHANA (ja), Arangel Angov (mk), Tomasz Dominikowski (pl), Duarte Loreto (pt) 2.21.8 ====== Thanks to Paolo Borelli, Iain Holmes, Havoc Pennington, Christian Persch, Thomas Thurman, and Alex R.M. Turner for improvements in this version. - Windows on other workspaces which need attention appear in the alt-tab list too (Alex) (#333548) - Remove deprecated function call (Christian) (#512561) - New release script (Thomas) - Made a start at improving the general number of comments (Thomas) - Updated copyright year to 2008, and some other tiny fixes (Thomas) - Don't do anything unusual when the compositor frees a window (Iain) - Mapping windows doesn't mark them as damaged (Iain) - Compositor uses the overlay window and not the root window (Iain) - Fixed several list leaks (Paolo) - Fixed warnings about printf formats (Havoc) - Move source files into subdirectories of the src directory (Havoc) Translations Khaled Hosny (ar), Ihar Hrachyshka (be@latin), Petr Kovar (cs), Andre Klapper (de), Jorge González (es), Iñaki Larrañaga Murgoitio (eu), Seán de Búrca (ga), Yuval Tanny (he), Luca Ferretti (it), Takeshi AIHANA (ja), Arangel Angov (mk), sandeep shedmake (mr), Kjartan Maraas (nb), Yannig Marchegay (Kokoyaya) (oc), Daniel Nylander (sv), Theppitak Karoonboonyanan (th), Baris Cicek (tr), Clytie Siddall (vi) 2.21.5 ====== Thanks to Iain Holmes and Thomas Thurman for improvements in this version. This contains the new compositor; downstream maintainers should note that its GConf key is initially turned off in src/metacity.schemas.in and consider whether to turn it on by default in their packages. - merge compositor branch! (Iain) (499081) - print "Subversion" and not "CVS" when building (Thomas) Translations Jorge González (es), Kjartan Maraas (nb), Daniel Nylander (sv) 2.21.3 ====== Thanks to Matthias Clasen, Martin Meyer, Kjartan Maraas, Thomas Thurman, and Lucas Rocha for improvements in this version. - remove dead code (pointed out by Kjartan) (501365) - rewrote long key binding description for the sake of the translators (Thomas) (474889) - check for null before adding menu (Matthias) (496054) - let keys which end a grab also begin a grab (Thomas) (112560) - check the right variable in theme sanity check (Martin) (501362) - get session ID from environment if it's not passed in on the command line (Lucas) (498033) Translations Ihar Hrachyshka (be@latin), Petr Kovar (cs), Jorge González (es), Ignacio Casal Quinteiro (gl), Rodrigo Flores (pt_BR), Pavol Šimo (sk), Matej Urbančič (sl) 2.21.2 ====== Thanks to Benjamin Gramlich, Thomas Thurman, and Peter Bloomfield for improvements in this release. - Theme parser is compliant to XDG Base Directory Specification in searching for theme files. (Benjamin) (#480026) - Some source files which didn't get used were removed (Thomas) (#496947) - Fullscreen and maximise windows don't try to save their position (Peter) (#461927) Translations Matej Urbančič (sl) 2.21.1 ====== Thanks to Elijah Newren, Alex R.M. Turner, Peter Bloomfield, Iain Holmes, Jans Granseuer, Federico Mena Quintero and Thomas Thurman for improvements in this release. - Add --sync option, like all other GTK apps (Iain) - Don't save window's position if it's maximised (Peter) (#461927) - Memory leak fix in preview (Jans) (#469682) - Truncate tab popup string correctly, and refactor function (Alex) - Windows which pop up under always-on-top windows don't get the focus, but do get the "needs attention" hint (Thomas) (#486445) - Fix error in function call which caused focus problems (Federico) (partial fix of #488468) Translations Djihed Afifi (ar), Metin Amiroff (az), Alexander Shopov (bg), Jordi Mallach (ca), David Lodge (en_GB), Jorge González (es), Iñaki Larrañaga Murgoitio (eu), Vincent Untz (fr), Alastair McKinstry (ga), Ankit Patel (gu), Rajesh Ranjan (hi), auto (hr), Changwoo Ryu (ko), Raivis Dejus (lv), Wouter Bolsterlee (nl), Gora Mohanty (or), ASB (pa), wadim dziedzic (pl), Duarte Loreto (pt), Og Maciel (pt_BR), Peter Tuhársky (sk), Matej Urbančič (sl), Daniel Nylander (sv), Maxim Dziumanenko (uk), Funda Wang (zh_CN) 2.20.0 ====== Thanks to Alexey Rusakov for the fix in this release. - prevent a crash on logout with metacity subsequently not being restored in future sessions (Alexey) [#433253] Translations Khaled Hosny (ar), Ihar Hrachyshka (be@latin), Ask Hjorth Larsen (da), Adam Weinberger (en_CA), Iñaki Larrañaga Murgoitio (eu), Ilkka Tuohela (fi), Vincent Untz (fr), Ankit Patel (gu), Gabor Kelemen (hu), Luca Ferretti (it), Takeshi AIHANA (ja), Žygimantas Beručka (lt), Jovan Naumovski (mk), Ani Peter (ml), Og Maciel (pt_BR), Duarte Loreto (pt), Mugurel Tudor (ro), Nickolay V. Shmyrev (ru), Peter Tuhársky (sk), Горан Ракић (sr), Daniel Nylander (sv), Dr.T.Vasudevan (ta), Maxim Dziumanenko (uk), Clytie Siddall (vi) 2.19.55 ======= Thanks to Frederic Crozat, Matthias Clasen, and Thomas Thurman for improvements in this release. - Noninteger auto-raise delay is not assumed to be zero (Thomas) (#377491) - Fix mangled window title in "Force Quit" (Frederic) (#462734) - "Close" can appear at any point in the window menu, and now appears at the bottom (Thomas) (#104026) - Windows which are always on top have "stick" insensitive (Thomas) (#460997) - All bitfields in window structure are together for optimisation (Thomas) (#450271) - Use the correct directory when installing keybindings (Matthias) (#454055) Translations Alexander Shopov (bg), Jorge González (es), Iñaki Larrañaga Murgoitio (eu), Ilkka Tuohela (fi), Theppitak Karoonboonyanan (th) 2.19.34 ======= Thanks to Rob Bradford, Cosimo Cecchi, Yair Hershkovitz and Thomas Thurman for improvements in this release. - Fix a bug where the window can be focused without being raised if the maximize is aborted. (Rob) [#459027] - Unset fullscreen is an allowed action where relevant. (Cosimo) [#449427] - Reverse window buttons and align them to the left for RTL locales. (Yair) [#92212] - Put all bitfields in window data together to help with optimisation. (Thomas) [#450271] Translations Jorge Gonzalez (es), Ilkka Tyohela (fi), Gabor Kelemen (hu), Takeshi AIHANA (ja), Kjartan Maraas (nb), Vincent van Adrighem (nl), Daniel Nylander (sv), Theppitak Karoonbooyana (th), Nguyễn Thái Ngọc Duy (vi) 2.19.21 ======= Thanks to Damien Carbery and Thomas Thurman for improvements in this release. - Fixed build on Solaris (Damien) [#397296, #446535] - Only activate windows which change their startup ID if the new ID differs from the old. (This fixes the bug where KDE apps gained the attention hint when switching workspaces.) (Thomas) [#400167] - Open new windows on the current xinerama. (Thomas) [#145503]. Translations Tshewang Norbu (dz), Jorge González (es), Funda Wang (zh_CN) 2.19.13 ======= Thanks to Elijah Newren and Thomas Thurman for improvements in this release. - Updated the description of raise_on_click (Elijah) [#445447, #389923] - Refactor queueing code in window.c (Thomas) [#376760] - Added switch_group to the keybindings file (Thomas) [#444879] - New window information accessor function (Thomas) [#377495] 2.19.8 ====== Thanks to Linus Torvalds, Yair Hershkovitz and Thomas Thurman for improvements in this release. - Lots of fixups for various alignments in RTL locales (Yair) [#387893] - Add code to configure what happens on right or middle click of titlebar (Linus) [#408904] - Fix layout for titlebars with mixed LTR/RTL scripts (Thomas) [#433400] - Fix window menu layout for RTL scripts (Thomas) [#433400] Translations Khaled Hosny (ar), Ihar Hrachyshka (be@latin), Jovan Naumovski (mk), Theppitak Karoonboonyanan (th) [ Apologies to these translators who didn't get credited in the version of 2.19.8 that shipped. ] 2.19.5 ====== - Prevent metacity from "forgetting" which machine a window is on (Elijah) [#418552] - Prevent nasty flickering an placement problem introduced in metacity 2.19.2 (Elijah) [fix side-effect of change in #426519] - Fix some uninitialized memory usage errors (Elijah) [#427385] Translations David Lodge (en_GB), Jorge González (es), Ignacio Casal Quinteiro (gl), Daniel Nylander (sv) 2.19.3 ====== Thanks to Magnus Therning, Elijah Newren, Thomas Thurman, and Bruno Boaventura for improvements in this release. - Add support for _NET_MOVERESIZE_WINDOW (Magnus, Elijah) [#344521] - EWMH compliance: set _NET_WM_ALLOWED_ACTIONS so that pagers know which actions we support (Elijah) [#115247] - Fix crash with apps trying to open an insanely huge window (Thomas) [#399529] - Fix temporary hang/pause with libXt by making sure apps get a ConfigureNotify on unmap (Elijah) [#399552] - do not auto-maximize windows larger than the workarea in only a single direction (Elijah) [#419810] - Don't show the current workspace as a possible workspace to switch to (Bruno) [#426791] - Preserve stacking order across restarts (Elijah) Translations Khaled Hosny (ar), Kjartan Maraas (nb) 2.19.2 ====== Thanks to Bastien Nocera, Thomas Thurman, and Elijah Newren for improvements in this release. - Add new control-center key bindings definitions (Bastien) [#420145] - Prevent metacity from crashing when trying to use invalid themes (Thomas) [#423855] - Fix invalid free causing crash on metacity close introduced in 2.19.1 (Elijah) [#427385] - Add special keybinding just for debugging spew marks, unbound and not even listed in schemas (Elijah) - Fix move/resize events in relation to combinations of ConfigureRequest and WM_NORMAL_HINTS change notifications (Elijah) [#426519] - Remove what we believe to be an ancient attempt at working around sloppy/mouse focus bugs that we believe have since been correctly fixed. May fix some ugly race conditions. May also cause nasty bugs in sloppy/mouse focus modes. Only one way to find out... (Elijah) [#304430] Translations Raivis Dejus (lv) 2.19.1 ====== Thanks to Jaap Haitsma, Linus Torvalds, Charlie Brej, Kjartan Maraas, Arthur Taylor, Elijah Newren, Josselin Mouette, Havoc Pennington, Benjamin Berg, and Carlo Wood for improvements in this release. - new icon for the force-quit dialog (Jaap) [#396655] - add configureable mouse click action abilities, and clean up lots of related code (Linus) [#408899, #408902, others] - add schemeas for middle and right click titlebar actions (Charlie) [#408903] - remove pango/pangox.h include since it's not needed and not installed anymore (Kjartan) - adjust rounded corners so that they fit nicely with the arcs around them (Arthur) [#399373] - fix session hang when metacity .sm file is missing (Josselin) [#407981] - add support for _NET_WM_USER_TIME_WINDOW in order to cut down on context switches (Elijah, Havoc) [#354213] - prevent nasty metacity/gdk interactions causing hangs with gtk trunk (Elijah) [offshoots of #354213] - fix button middle fallback and the prelight state (Benjamin) [#419043] - Lots of code cleanup for the strut lists (Elijah) - fix handling of unidirectional maximization and partial struts + some miscellaneous cleanups (Carlo) [#358311] - avoid some crashes when dragging windows partially offscreen (Elijah) [#353513] - avoid mousenav vs. keynav focus problems with the run application dialog in mouse/sloppy focus modes (Elijah) [#374752] - _NET_ACTIVE_WINDOW property on the root window should be a single xwindow id, not two (Elijah) - Fix unidirection unmaximization causing jumps (Elijah) [#355497] - fix unfullscreening and unmaximizing with size increment/size constraint windows (such as gnome-terminal) possibly not returning to their "original position" (Elijah) [#329152] - fix some issues with min/max and size increment constraints (Elijah) [#418395] - send synthetic configure notify events in response to appropriate MapRequest events too (Elijah) [#322840] Translations Ihar Hrachyshka (be@latin), Jordi Mallach (ca), Jakub Friedl (cs), norbu (dz), David Lodge (en_GB), Ivar Smolin (et), Gabor Kelemen (hu), Luca Ferretti (it), Takeshi AIHANA (ja), Erdal Ronahi (ku), Gintautas Miliauskas (lt), Jovan Naumovski (mk), Kjartan Maraas (nb), Reinout van Schouwen (nl), wadim dziedzic (pl), raulpereira (pt_BR), Nickolay V. Shmyrev (ru), Горан Ракић (sr), Woodman Tuen (zh_HK), Woodman Tuen (zh_TW) 2.17.5 ====== Thanks to Bruno Boaventura, Mad Alex, and Thomas Thurman for improvements in this release. - make window menu arrangement more sensible. (Bruno) [#382962] - unmaximise button keeps pressed appearance when moved off and back. (Alex) [#395560] - fix a couple of compositor crashes (Thomas) [#387761] - new environment variables checked if the compositor is enabled; see the new file doc/compositor-control.txt for details. (Thomas) Translations Djihed Afifi (ar), Ales Nyakhaychyk (be), Jordi Mallach (ca), Jakub Friedl (cs), David Lodge (en_GB), Raivis Dejus (lv), Kjartan Maraas (nb), Mugurel Tudor (ro), Daniel Nylander (sv), Theppitak Karoonboonyanan (th) 2.17.3 ====== Thanks to Christof Krüger, Federico Mena Quintero, Bruno Boaventura, and Björn Lindqvist for improvements in this release. - fix longstanding problem about windows flickering in and out of maximised state when dragging between xineramas (Christof) [#358715] - grab server when switching workspaces (Federico) [#381127] - replace changing text on window menu with pairs of radio buttons and checkboxes (Bruno, Björn) [#343108] Translations Kjartan Maraas (nb), Jakub Friedl (cs), Yuval Tanny (he), Ivar Smolin (et), Duarte Loreto (pt), Francisco Javier F. Serrador (es) 2.17.2 ====== Thanks to Priit Laes, Bruno Boaventura, Kjartan Maraas, Justin Mason, Elijah Newren and Dan Mick for improvements in this release. - implement handle_move_to_{side|corner}_* to allow the user to flip a window to the side or corner of the screen. (Justin) [#317884] - fix strict focus mode by picking up on res_class (Dan) [#361054] - remove deprecated gtk stuff (Priit, Bruno) - string fixes (Kjartan) [#363354, #363355] Translations Jakub Friedl (cs), Francisco Javier F. Serrador (es), Ilkka Tuohela (fi), Christophe Merlet (RedFox) (fr), Kjartan Maraas (nb) 2.17.1 ====== Thanks to Bruno Boaventura and Carlo Wood for improvements in this release. - sync metacity workspace previous with libwnck (Bruno) [#341893] - fix cases when titlebar is allowed offscreen and shouldn't be, and vice-versa (Carlo) [#333995] Translations Ilkka Tuohela (fi) 2.17.0 ====== Thanks to Elijah Newren, Jens Granseuer, Bruno Boaventura, Carlo Wood, and Thomas Thurman for changes in this release. - version 2 of theme format: stick, shade and above buttons on titlebar, variable rounding on corners, variable transparency on window backgrounds, stock icons in themes, can remove all titlebar buttons from certain classes of window, and more (Thomas) [#102547 and dependencies] - improve "Force Quit" dialog (Bruno) [#121936] - ignore edge resistance when resizing with keyboard (Elijah) [#346782] - maintain window size and placement across restarts (Carlo) [#358042] - prevent crash when closing certain remote apps (Elijah) [#358514] - longstanding mouse-focus bug fixed which affected firefox's autocompletion (Elijah) [#357695] - ignore maximum size constraints when maximising (Elijah) [#327543] - warn translators to keep translations in sync with libwnck (Bruno) [#355620] - fixes for compilation warnings, etc (Elijah, Jens) [#348067, #356631] Translators Ivar Smolin (et), Gabor Kelemen (hu), Luca Ferretti (it), Runa Bhattacharjee (bn_IN) 2.16.2 ====== Thanks to Eljah Newren, Maik Beckmann, Christian Hamar, Thomas Andersen, and Bruno Boaventura de Oliveira for changes in this release. - partial audit to fix timestamp usage (Elijah) [part of #355180] - remove compilation warnings (Maik) [#355876]; (Bruno) [#355490, #355489] - automatic detection of stable/unstable in configure script (Christian/Elijah) [#356122] - make windows be stacked correctly before showing them (Thomas) [#332385] - use guint32 for timestamps (Elijah) [#348305] Translators Wouter Bolsterlee (nl), Matic Žgur (sl), Francisco Javier F. Serrador (es), Vladimir Petkov (bg), Jordi Mallach (ca), Ilkka Tuohela (fi), Rajesh Ranjan (hi), Woodman Tuen (zh_HK, zh_TW), Ani Peter (ml), Felix (ta), Ankit Patel (gu), Mohammad DAMT (id) 2.16.1 ====== Thanks to Elijah Newren, Colin Watson, and Bruno Boaventura de Oliveira Lacerda for changes in this release. - fix stuck grab, letting focus be transferred between windows (Elijah) [#354422 partial] - windows returning from fullscreen are constrained to be onscreen (Elijah) [#353699] - Clear the transient_for flag of a window after emitting a warning (Colin) - Replace copy_of_gdk_x11_window_set_user_time() with the real thing (Bruno) [#352293] Translators David Lodge (en_GB), Ivar Smolin (et), Matic Žgur (sl), Vasiliy Faronov (ru) 2.16.0 == Thanks to Jens Granseuer for changes in this release. - Fix the build with c89/gcc 2.95. Translators Rahul Bhalerao (mr), Runa Bhattacharjee (bn_IN), Woodman Tuen (zh_HK, zh_TW), Kostas Papadimas (el), Ani Peter (ml), Jonathan Ernst (fr), Горан Ракић (sr, Gabor Kelemen (hu), Maxim Dziumanenko (uk), Duarte Loreto (pt), Jordi Mallach (ca), Gintautas Miliauskas (lt) 2.15.34 == Thanks to Stéphane Rosi, Vytautus Liuolia, Will Walker, Baptiste Mille-Mathias, Elijah Newren, Ed Catmur, and Thomas Andersen for fixes in this release. - allow moving maximized windows between xineramas again (Stéphane) [#338660] - fix an uninitialized-usage bug with net_wm_user_time that breaks focus with new windows (Vytautus) - re-fix accessibility events for the alt-tab popup (Will) [#350624] - update the close pixmap to fit better with the other pixmaps of the menu (Baptiste) [#345498] - fix several fullscreen handling bugs I introduced, causing fullscreen windows to not actually be shown fullscreen (Elijah) [#343115] - fix keybindings with hex-values, coming from special extended keyboard keys (Ed) [#140448] - fix metacity-dialog handling of arguments (Thomas) [#340690] Translators Vladimir Petkov (bg), Jordi Mallach (ca), Gabor Kelemen (hu), Mohammad DAMT (id), Wouter Bolsterlee (nl), Daniel Nylander (sv), Funda Wang (zh_CN) 2.15.21 == Thanks to Vincent Untz, Jens Granseuer, Björn Lindqvist, Dmitry Timoshkov, Thomas Thurman, Vytautas Liuolia, Thomas Andersen, Chris Ball, and Elijah Newren for fixes in this release. - kill usage of libegg (Vincent) [#348633] - fix another C89 vs. C99 issue (Jens) [#347621] - make it so maximized windows do not have rounded corners (Björn) [#336850] - fix the heuristic for determining if windows can be made fullscreen, needed for WINE and possible also some legacy applications (Dmitry) [#346927] - make sure window features get recalculated when the screen is resized via XRandR (Dmitry) [#346927] - fitts' law fixes for titlebar buttons on maximized windows (Thomas Thurman) [#97703] - react to _NET_STARTUP_ID changes, as proposed for the new startup-notification/EWMH spec (Vytautas) [#347515] - return the window to maximized state if the window was "shaken loose" from maximized state during a resize but the resize is later aborted (Thomas Andersen) [#346719] - fix button lighting with dragged clicks (Björn) [#321474] - don't minimize in response to double clicks on the titlebar when minimiziation should not be allowed (Chris) [#347377] - fix some titlebar-not-on-screen constraint issues (Elijah) [#333328, #345522] Translators Mahay Alam Khan (bn_IN), Jakub Friedl (cs), Iñaki Larrañaga Murgoitio (eu), Yuval Tanny (he), Rajesh Ranjan (hi), Jovan Naumovski (mk) Kjartan Maraas (nb), Leonid Kanter (ru) 2.15.13 == Thanks to Björn Lindqvist and Thomas Thurman for improvements in this release. - grab alt+shift+button1 when trying to snap-move windows (Björn) - avoid a case where memory is written after it's freed (Thomas) Translators Hendrik Richter (de), Kostas Papadimas (el), Jonathan Ernst (fr), Satoru SATOH (ja) 2.15.8 == Known as the "Elijah sucks for not reviewing a couple dozen patches" release. And for not getting on IRC soon enough to catch Marnanel and show him how to do the release. So, just translations this time. Translations Mahay Alam Khan (bn_IN), Rhys Jones (cy), Francisco Javier F. Serrador (es), Ilkka Tuohela (fi), Rajesh Ranjan (hi), Changwoo Ryu (ko), Fano Rajaonarisoa (mg), Sanlig Badral (mn), Слободан Д. Средојевић (sr), Funda Wang (zh_CN) 2.15.5 == Thanks to Björn Lindqvist, Søren Sandmann, Adam Jackson, Elijah Newren, and Aidan Delaney for improvements in this release. - code cleanup in resizepopup.c (Björn) [#341648] - fix a logic bug so that the whole titlebar becomes sensitive to mouse clicks (Björn) [#336320] - make mouse cursor when moving windows become a hand (Björn) [#337376] - lots and lots of compositor improvements -- beginning of a new layer to abstract transition effects, shrinking and minimizing and exploding effects, fading in and out, unminimize animation that reverses minimize one, translucent menus, bounce on window focus, and all kinds of stuff I don't understand and can't summarize well (Søren, Adam) - Fix a crash on exit/logout from assuming a compositor would always exist (Elijah) [#342166] - code cleanup in tabpopup.c (Aidan Delaney) [#166890] Translations Pema Geyleg (dz), Iñaki Larrañaga Murgoitio (eu), Theppitak Karoonboonyanan (th), Clytie Siddall (vi) 2.15.3 == Thanks to Søren Sandmann, Elijah Newren, Paolo Borelli, Björn Lindqvist, jylefort at FreeBSD org, - various code cleanups (Søren) - prevent long titles from "sticking" in the tasklist (Elijah) [#330671] - handle sync counter notifications in the compositor (Søren) - notes/documentation updates (Elijah) - plug a small leak (Paolo) - remove a lot of dead code obsoleted by the new edge-resistance stuff (Björn) [#341561] - prevent a crash when changing resolution (jylefort) [#340847] - revert an accessibility module loading workaround from Gnome 2.6 that has long since been fixed for us in gtk+ (Elijah) [#123372] Translations Francisco Javier F. Serrador (es), Ignacio Casal Quinteiro (gl), Raivis Dejus (lv), Kjartan Maraas (nb), Funda Wang (zh_CN), Woodman Tuen (zh_HK), Woodman Tuen (zh_TW) 2.15.2 == Here's hoping that "third time's a charm." ;-) This release just fixes the translations-not-included issue. See http://mail.gnome.org/archives/desktop-devel-list/2006-April/msg00483.html for more details. - Use gnome-autogen.sh like most other modules (Rodney) 2.15.1 == This release just fixes the control-center build (which depends upon libmetacity-private). Thanks to Vincent for catching the problem. - Include boxes.h in the includes dir (Elijah) [#339708] 2.15.0 == Thanks to Thomas Thurman, Elijah Newren, Havoc Pennington, Björn Lindqvist, Gora Mohanty, Alejandro Andres, Andy Morum, Dan Sanders, Thomas Andersen, Brian Pepple, and Søren Sandmann for improvements in this release. (Note that "Thomas" below refers to Thomas Thurman if last name isn't specified) - An endless array of compositor updates, not all of which are well explained in the ChangeLog. ;-) Includes an ability to enable and disable the compositor at runtime, fixed up wobbling effect and new explosion effect, special magnification handling, different opacity for different window types like menus, a way of scaling windows, handling of foreign displays, improved handling of window moving/resizing, various code restructuring, special runtime checks for correct extensions and other compositors, lots of bug fixes, and possibly other stuff I'm missing or not understanding (Søren) - Removed "move to another workspace" menu when there are exactly two workspaces (Thomas) [#151183] - fix type for compositing_manager schema entry (Elijah) [#335901] - Port more properties to our async system for code cleanliness and speed improvements (Havoc, Thomas) - Lots of code cleanup, even more code cleanup, wow our code was messy (Björn) [#335177, #337507, #336890, #338359] - Abstract out the functions for setting/unsetting demands attention hint and avoid doing it when the window isn't obscured (Thomas) [#305882] - Change strings to make them more readable, and more translatable (Gora) [#335720] - Reduce compiling warnings -- add a number of casts and change signedness on a number of variables (Björn) [#336032] - Fixed broken links in README (Alejandro) [#333303] - Add a tabbing function, bound to alt-f6 by default, to cycle through the windows of the current application (Thomas) [#94682] - Fix the build with --disable-xsync (Andy) [#336605] - Raise windows on maximize/unmaximize (Dan) [#335076] - Don't have confirmation windows make applications appear to be locked when closing via the window list (Dan) [#334899] - Allow any keybinding pref to be specified either with , a string, or _list, a list of strings, or both (Thomas) [#164831] - warn and ignore if transient_for is set to a non-top-level window (Thomas Andersen) [#335524] - Use po/LINGUAS for listing supported languages (Brian) [#337951] Translations Vladimir Petkov (bg), Jordi Mallach (ca), Miloslav Trmac (cs), Rhys Jones (cy), Lasse Bang Mikkelsen (da), Frank Arnold (de), Kostas Papadimas (el), Francisco Javier F. Serrador (es), Ivar Smolin (et), Iñaki Larrañaga (eu), Farzaneh Sarafraz (fa), Ilkka Tuohela (fi), Ignacio Casal Quinteiro (gl), Ankit Patel (gu), Rajesh Ranjan (hi), Gabor Kelemen (hu), Satoru SATOH (ja), Alexander Didebulidze (ka), Žygimantas Beručka (lt), Kjartan Maraas (nb), Michiel Sikkes (nl), Åsmund Skjæveland (nn), Gora Mohanty (or), Raphael Higino (pt_BR), Duarte Loreto (pt), Mugurel Tudor (ro), Leonid Kanter (ru), Steve Murphy (rw), Laurent Dhima (sq), Слободан Д. Средојевић (sr), Daniel Nylander (sv), Theppitak Karoonboonyanan (th), Maxim Dziumanenko (uk), Clytie Siddall (vi), Funda Wang (zh_CN) 2.14.3 == This release just reverts the widely hated new focus behavior of Metacity 2.14.x to the behavior found in 2.12.x. Patch came from Ron Yorston. See http://blogs.gnome.org/view/newren/2006/04/13/0 and http://mail.gnome.org/archives/release-team/2006-April/msg00025.html for more details. - Add a focus_new_windows gconf key, change the default to 'smart' (2.12 behavior) and add a 'strict' option to get 2.14 behavior. (Ron) [#326159] Translations Vladimir Petkov (bg), Miloslav Trmac (cs), Frank Arnold (de), Francisco Javier F. Serrador (es), Ilkka Tuohela (fi), Tino Meinen (nl), Åsmund Skjæveland (nn), Raphael Higino (pt_BR), Daniel Nylander (sv) 2.14.2 == Thanks to Thomas Thurman, Paolo Borelli, Björn Lindqvist, and Elijah Newren for fixes in this release. - Fix constraints bug causing negative width windows and crashes (Elijah) [#336651] - Fix window grouping with parent/child windows (Björn) [#336184] - use g_str_has_prefix instead of a local copy of the function (Paolo) [#334643] - Make sure pager can refresh when window is minimized on a different workspace (Thomas) [#315142] - Add debugging information for edge resistance (Elijah) Translations Vladimir Petkov (bg), tangi.bzh (br), Jordi Mallach (ca), Miloslav Trmac (cs), Rhys Jones (cy), Lasse Bang Mikkelsen (da), Frank Arnold (de), Mindu Dorji (dz), Kostas Papadimas (el), Francisco Javier F. Serrador (es), Ivar Smolin (et), Iñaki Larrañaga Murgoitio (eu), Elnaz Sarbar (fa), Ilkka Tuohela (fi), Ignacio Casal Quinteiro (gl), Ankit Patel (gu), Rajesh Ranjan (hi) Gabor Kelemen (hu), Luca Ferretti (it), Satoru SATOH (ja), Vladimer Sichinava (ka), Žygimantas Beručka (lt), Kjartan Maraas (nb), Tino Meinen (nl), Kjartan Maraas (no), Gora Mohanty (or), Gnome PL Team (pl), Evandro Fernandes Giovanini (pt_BR), Duarte Loreto (pt), Mugurel Tudor (ro), Leonid Kanter (ru), Laurent Dhima (sq), Слободан Д. Средојевић (sr), Daniel Nylander (sv), Theppitak Karoonboonyanan (th), Maxim Dziumanenko (uk), Clytie Siddall (vi), Funda Wang (zh_CN) 2.14.1 == The only change since 2.14.0 is to remove the "This is the UNSTABLE branch" warning from configure for those compiling Metacity from source. 2.14.0 == Thanks to Ryan Lortie and Thomas Thurman for fixes in this release. - Mark the app-running-remotely-window-title string for translation (Thomas) [#334332] - Only unmaximise window before freeing if the window is actually maximised (Ryan) [#333563] Translations Jordi Mallach (ca), Frank Arnold (de), Luca Ferretti (it), Evandro Fernandes Giovanini (pt_BR), Theppitak Karoonboonyanan (th) 2.13.144 == Thanks to Jens Granseuer, Kristian, Søren Sandmann, Sylvain Bertrand, and Thomas Thurman for improvements in this release. - Fix build with gcc 2.95 (Jens) [#331166] - Compositor improvements [remember, still off by default]: add unused wobbly (un)minimize animation (Kristian), add support for turning updates on and off (Søren), use sync counter to make composited resizing tear free (Søren), add ability to unmanage the screen (Søren), - Fix build issue with library search order (Sylvain) [#330695] - Work around buggy application grouping with transient windows (Thomas) [#328211] - Prevent setting cycle_windows to keybindings that won't work. (Thomas) [#329676] 2.13.89 == Thanks to Søren Sandmann, Thomas Thurman, Thom May, Akira Tagoh, Luke Morton, and Philip O'Brien for improvements in this release. - Compositor improvements [remember that the compositor is still disabled by default]: New fancy minimize animation that fades in and out, new debug stuff, various bug fixes (Søren) - When buggy apps create synthetic button or keypresses without a timestamp, produce a warning instead of failing an assertion (Thomas) [#313490] - Avoid a memory leak when checking which workspace(s) a window is on (Thomas) [#322059] - Add a man page for metacity (Thom, Akira, Luke, Philip) [#321279] - Disable alt-f7 if a window can't be moved, and alt-f8 if it can't be resized (Thomas) [#328920] - Allow alt-escape to cancel alt-tabbing, and vice versa (Thomas) [#141425] Translations Miloslav Trmac (cs), Kjartan Maraas (nb), Tino Meinen (nl), Kjartan Maraas (no), Слободан Д. Средојевић (sr), Funda Wang (zh_CN) 2.13.55 == Thanks to Dick Marinus, Christian Kirbach, and Elijah Newren for improvements in this release. - Add a minimize and none double-click-titlebar-action (Dick) [#300210] - Prevent a critical warning crasher when switching themes (Christian) [#327847] - Fix some uninitialized value problems (Elijah) - If the mouse enters a window on a different screen, activate the default window on the new screen (Elijah) [#319348] Translations Jordi Mallach (ca), Adam Weinberger (en_CA), Francisco Javier F. Serrador (es), Ilkka Tuohela (fi), Ignacio Casal Quinteiro (gl), Ankit Patel (gu), Kjartan Maraas (nb), Kjartan Maraas (no), Evandro Fernandes Giovanini (pt_BR), Theppitak Karoonboonyanan (th), Clytie Siddall (vi), Funda Wang (zh_CN) 2.13.34 == Thanks to Damien Carbery, Havoc Pennington, Søren Sandmann, Björn Lindqvist, Kjartan Maraas, Elijah Newren for improvements in this release. - manually define HOST_NAME_MAX if not already defined to fix Solaris compilation issue (Damien, Havoc) [#326745] - compositor improvements: port to changes in libcm, do it again, fix unrefing, make minimize animation update again (all done by Søren) - make sure an outline border is shown even if a window decoration's width is 0 (Björn) [#98340] - correctly handle window alt-tab outlines in showing desktop mode (Björn) [#98340] - fix lots of tiny issues spotted by the intel compiler (Kjartan) [#321439] - prevent rapidly repeated visual bells from hanging metacity (Elijah) [#322032] - more careful error handling of values returned by GConf (Elijah) [#326615] - fix various initialization and default issues, especially for running with --disable-gconf. Make --disable-gconf actually work. (Elijah) [#326661] - fix some reading-from-free'd-data errors (Søren) [#327575] - fix an unitialized value problem when in raise-on-click mode (Søren) [#327572] - avoid flashing original-sized window when closing a maximized window (Elijah) [#317254] - prevent windows from sometimes getting shoved and smashed by sliding (and possibly auto-hiding) panels (Elijah) [#327822] Translations Ilkka Tuohela (fi), Ignacio Casal Quinteiro (gl), Tino Meinen (nl), Funda Wang (zh_CN) 2.13.21 == Thanks to Damien Carbery, Ray Strode, Søren Sandmann, Elijah Newren, Jens Granseuer, and Kyle Ambroff for improvements in this release. - Fix Solaris compilation issues (Ray, Damien) [#326281, #326746] - Merge compositor work from branches to get the beginnings of an openGL based compositor. Still not ready and thus disabled by default. (Søren) - Composite fixes: Only update composite on damage events (Søren), get non-composite compilation working again (Elijah), Really turn off draw-in-a-loop (Søren) - Don't dereference a NULL string (Elijah) [#327013] - GCC 2.95 fix; remove more C99 style variable initiailizations (Jens) [#327050] - Fix accidental overzealous focus holding by the terminal (introduced in last release) so that windows launched from panel icons, the panel menu, or global keybindings should get focus now. (Elijah) [#326159] - If no valid window is found in the MRU list, then set focus to the desktop window. (Kyle) [#317405] Translations Adam Weinberger (en_CA), Francisco Javier F. Serrador (es), Ankit Patel (gu), Takeshi AIHANA (ja), Theppitak Karoonboonyanan (th), Clytie Siddall (vi) 2.13.13 == Thanks to Jens Granseuer, Björn Lindqvist, and Elijah Newren for improvements in this release. - Remove C99 style variable initiailization (Jens) [#322622] - Fix a logic error (Björn) [#322149] - Plug a few leaks (Elijah) [#309178] - Allow edge resistance at both sides of a window and also when edges don't overlap but are a single pixel away from doing so (Elijah) [part of #321905] - Remove the timeout resistance at screen/xinerama edges (Elijah) [part of #321905] - Revert to the old edge resistance behavior for keyboard movement/resizing based resistance (Elijah) [part of #321905] - Remove the "pull-away" edge resistance (Elijah) [part of #321905] - Avoid crashing when visual bell is in use and focus window is closed (Elijah) [#322031] - Be more strict about what is considered a valid region with partial struts (Elijah) [#322070] - Fix reduced resources resize handling for windows with sizing or resizing constraints (Elijah) [#325774] - Fix window outline for minimized windows when using alt-esc (Elijah) [#325092] - Make the taskbar less flash happy and fix up some related stacking issues (Elijah) [#326035] - More thorough handling of source indication (Elijah) [part of #326041] - Don't "steal" focus from terminal windows for new window mappings as the difference in usage between terminals and other apps seems to suggest this difference in treatment. See bug #326159 for details, feedback welcome (Elijah) [#326159] - Add a raise on click option, basically only because all the major distros are patching it in anyway (though each and every one of them has bugs in their implementations). (Elijah) [#326156] Translations Kjartan Maraas (nb), Kjartan Maraas (no) 2.13.8 == Thanks to Kang Jeong-Hee and Elijah Newren for improvements in this release. - Fix some compilation warnings and issues (Kang) - Escape the title since it is going to be treated as Markup (Elijah) [#324846] - Make the workspace switcher work with dual-head (non-xinerama) setups (Elijah) [#319423] Translations Ilkka Tuohela (fi), Ankit Patel (gu), Kang Joeng-Hee (ko) 2.13.5 == Thanks to Davyd Madeley, Kjartan Maraas, and Björn Lindqvist for improvements in this release. - Make a debugging message actually correspond to the code (Björn) [#322051] - Make the wireframe a bit slimmer (Kjartan) [#320051] - Display hostname in titlebar for remote X clients (Davyd) [#322202] Translations Miloslav Trmac (cs), Adam Weinberger (en_CA), Ankit Patel (gu), Kjartan Maraas (nb), Kjartan Maraas (no), Marcel Telka (sk) 2.13.3 == This is a special edition release just for gicmo, code-named 'elijah, please do a release so magic seb can bring it to me'. It fixes a number of issues due to the major constraints changes found since the last release. Thanks to Davyd Madeley and Elijah Newren for improvements in this release. - Differentiate between movement towards an edge and movement away from one for edge-resistance. Pick smaller constants for movement away from an edge (Elijah) - Use GPOINTER_TO_INT() macro instead of cast to allow compilation on 64-bit architectures without warning (Davyd) - compute the frame geometry due to maximization only after actually maximizing (Elijah) [#321902] - add some developer documentation on updating struts, workareas, regions, and edges (Elijah) - When updating the xinerama due to placement, update which maximal/spanning rect set to use as well (Elijah) [#322068] - Relax the partially onscreen constraint to allow the titlebar to touch the bottom panel in order to make the new constraints code function the same as the old version (Elijah) [#322071] - Don't allow removing a window from maximized or fullscreened state to place the titlebar under the top panel (Elijah) [#322075] Translations Vladimir Petkov (bg), Francisco Javier F. Serrador (es), Ignacio Casal Quinteiro (gl), Takeshi AIHANA (ja), Theppitak Karoonboonyanan (th) 2.13.2 == This release just contains a merge of all the changes on the constraints_experiments branch. Thanks to Havoc Pennington for reviewing the gargantuan patch and suggesting lots of little fixes for making it better, to Rob Adams and Soeren Sandmann for grilling me on how some of the difficult internals work -- allowing me to improve the documentation, to Olav Vitters for finding an easy-to-fix crasher bug in early testing and for repeatedly extending my deadline for switching from working on Metacity to Bugzilla, to Ray Strode for finding two crashers and fixing one of them in early testing, to Bryan Clark for usability advice, to Davyd Madeley and Christian Kellner for testing Xinerama stuff, to Sebastien Bacher for packaging an early version and finding some obscure bugs (that I unfortunately still can't duplicate and will probably still need to fix once I can), Bugs fixed: unfiled - constraints.c is overly complicated[1] unfiled - constraints.c is not robust when all constraints cannot simultaneously be met (constraints need to be prioritized) unfiled - keep-titlebar-onscreen constraint is decoration unaware (since get_outermost_onscreen_positions() forgets to include decorations) unfiled - keyboard snap-moving and snap-resizing snap to hidden edges 86644 - resize should have a shift option like move does 109553 - gravity w/ simultaneous move & resize doesn't work 113601 - maximize vertical and horizontal should toggle and be constrained 122196 - windows show up under vertical panels 122670 - jerky/random resizing of window via keyboard[2] 124582 - keyboard and mouse snap-resizing and snap-moving erroneously moves the window multidimensionally 136307 - don't allow apps to resize themselves off the screen (*cough* filechooser *cough*) 142016, 143784 - windows should not span multiple xineramas unless placed there by the user 143145 - clamp new windows to screensize and force them onscreen, if they'll fit 144126 - Handle pathological strut lists sanely[3] 149867 - fixed aspect ratio windows are difficult to resize[4] 152898 - make screen edges consistent; allow easy slamming of windows into the left, right, and bottom edges of the screen too. 154706 - bouncing weirdness at screen edge with keyboard moving or resizing 156699 - avoid struts when placing windows, if possible (nasty a11y blocker) 302456 - dragging offscreen too restrictive 304857 - wireframe moving off the top of the screen is misleading 308521 - make uni-directional resizing easier with alt-middle-drag and prevent the occasional super annoying resize-the-wrong-side(s) behavior 312007 - snap-resize moves windows with a minimum size constraint 312104 - resizing the top of a window can cause the bottom to grow 319351 - don't instantly snap on mouse-move-snapping, remove braindeadedness of having order of releasing shift and releasing button press matter so much [1] fixed in my opinion, anyway. [2] Actually, it's not totally fixed--it's just annoying instead of almost completely unusable. Matthias had a suggestion that may fix the remainder of the problems (see http://tinyurl.com/bwzuu). [3] This bug was originally about not-quite-so-pathological cases but was left open for the worse cases. The code from the branch handles the remainder of the cases mentioned in this bug. [4] Actually, although it's far better there's still some minor issues left: a slight drift that's only noticeable after lots of resizing, and potential problems with partially onscreen constraints due to not clearing any fixed_directions flags (aspect ratio windows get resized in both directions and thus aren't fixed in one of them) New feature: 81704 - edge resistance for user move and resize operations; in particular 3 different kinds of resistance are implemented: Pixel-Distance: window movement is resisted when it aligns with an edge unless the movement is greater than a threshold number of pixels Timeout: window movement past an edge is prevented until a certain amount of time has elapsed during the operation since the first request to move it past that edge Keyboard-Buildup: when moving or resizing with the keyboard, once a window is aligned with a certain edge it cannot move past until the correct direction has been pressed enough times (e.g. 2 or 3 times) Major code changes: - constraints.c has been rewritten; very few lines of code from the old version remain. There is a comment near the top of the function explaining the basics of how the new framework works. A more detailed explanation can be found in doc/how-constraints-works.txt - edge-resistance.[ch] are new files implementing edge-resistance. - boxes.[ch] are new files containing low-level error-prone functions used heavily in constraints.c and edge-resistance.c, among various places throughout the code. testboxes.c contains a thorough testsuite for the boxes.[ch] functions compiled into a program, testboxes. - meta_window_move_resize_internal() *must* be told the gravity of the associated operation (if it's just a move operation, the gravity will be ignored, but for resize and move+resize the correct value is needed) - the craziness of different values that meta_window_move_resize_internal() accepts has been documented in a large comment at the beginning of the function. It may be possible to clean this up some, but until then things will remain as they were before--caller beware. - screen and xinerama usable areas (i.e. places not covered by e.g. panels) are cached in the workspace now, as are the screen and xinerama edges. These get updated with the workarea in src/workspace.c:ensure_work_areas_validated() Translation Michiel Sikkes (nl) 2.13.1 == Thanks to Philip O'Brien, Kjartan Maraas, and Aidan Delaney for improvements in this release. - add handling for META_PREF_CURSOR_THEME and META_PREF_CURSOR_SIZE for more complete debug info (Philip) [#318976] - Remove possible g_source leak in #ifdef'd out code, in case anyone uses it in the future (Kjartan) [#320050] - Changed the 'minimized' field of the MetaTabEntry struct to 'hidden' (Aidan) [#168455] Translations Miloslav Trmac (cs), Francisco Javier F. Serrador (es), Takeshi AIHANA (ja), Erdal Ronahi (ku), Theppitak Karoonboonyanan (th) 2.13.0 == Thanks to Björn Lindqvist, Kjartan Maraas, Søren Sandmann, Elijah Newren, Ross Cohen, and Muktha for improvements in this release since 2.12.1. - Mave ancestors come along with the transient when moving the window from one workspace to another (Björn) [#314977] - Fix the workspace switcher tabpopup to display the right windows and to fix the pick-a-new-window-to-focus algorithm in order to not select windows that aren't showing (Björn) [#170475] - Fix a couple memory leaks (Kjartan, Søren, Elijah) [#313030] - Make alt-esc (the "switch between windows immediately" keybinding) actually show minimized windows too (Ross) [#107072] - Make alt-esc consistent with alt-tab by leaving stacking of unselected windows unchanged (Ross) [#314285] - Clarify the meaning of the auto_raise preference (Elijah) [#312421] - Fix a crash that occurs when removing some virtual desktops and windows happen to be on those desktops (Elijah) [#318306] - Make the unfocussed Simple window border visible with high contrast inverse theme (Muktha) [#121361] - Fix edge snapping for multi-screen (non-xinerama) setups (Elijah) [#319425] Translations Vladimir Petkov (bg), Kostas Papadimas (el), Adam Weinberger (en_CA), Ivar Smolin (et), Michiel Sikkes (nl), Marcel Telka (sk), Funda Wang (zh_CN) 2.12.1 == Thanks to Ray Strode, Havoc Pennington, and Elijah Newren for improvements in this release. - Truncate ridiculously long titles to avoid crashing or letting the pager crash (Ray, Havoc, Elijah) [#315070] - Get the tabbing window outline to work with gtk+ 2.8.4 again (Elijah) [#317528] Translations Mahay Alam Khan (bn), Francisco Javier F. Serrador (es), Ivar Smolin (et), Iñaki Larrañaga Murgoitio (eu), Luca Ferretti (it), Christian Rose (sv), Clytie Siddall (vi), Funda Wang (zh_CN) 2.12.0 == Thanks to Brent Smith for finding the crasher in the release candidate! - Fix an uninitialized variable problem causing crashes (Brent) [#315000] Translations Bryn Salisbury (cy), Hendrik Richter (de), Christophe Merlet (RedFox) (fr), Ignacio Casal Quinteiro (gl), Norayr Chilingaryan (hy), Young-Ho Cha (ko), Žygimantas Berucka (lt), Michiel Sikkes (nl), Leonid Kanter (ru), Danilo Šegan (sr), Baris Cicek (tr) 2.11.3 == Thanks to Björn Lindqvist and Elijah Newren for improvements in this release. - Check for the right versions of glib and gtk+ (Björn) [#314116] - Avoid obscuring centered-on-desktop windows which are denied focus (Elijah) [#313234] Translations Vladimir Petkov (bg), Jordi Mallach (ca), Kostas Papadimas (el), Ivar Smolin (et), Gabor Kelemen (hu), Mohammad DAMT (id), Duarte Loreto (pt), Mugurel Tudor (ro), Laurent Dhima (sq), Maxim Dziumanenko (uk) 2.11.2 == Thanks to Elijah Newren, Jaap Haitsma, Ray Strode, and Brent Smith for improvements in this release. - Fix an easy to trigger crasher in 2.11.1 caused by unneeded debugging spew (Elijah) [#311819] - Make sure that Metacity dialogs have icons (Jaap) [#309876] - Fix an infinite restacking flicker loop in sloppy and mouse focus with fullscreen windows (Elijah) [#311400] - Change default theme from Simple to Clearlooks (Elijah) - Vastly improve the behavior of keyboard move/resize and edge snapping (Ray) [#310888] - Remove a duplicate string (Brent) [#309774] Translations Yuval Tanny (he), Gnome PL Team (pl), Raphael Higino (pt_BR), Chao-Hsiung Liao (zh_TW) 2.11.1 == Thanks to Elijah Newren, Ken Harris, Matthias Clasen, Christian Persch, and Billy Biggs for improvements in this release. - Fix a miscoloring of parts of the titlebar introduced in the last unstable release (Elijah) [follow-up to #169982] - Provide a more lenient threshold for drawing rounded corners (Ken) [#122065] - Make the Xcursor changes in the last unstable release effective (Matthias) [follow-up to #308106] - Revert the _NET_ACTIVE_WINDOW behavior change made in the 2.9.x unstable series; activation includes changing a window to the current workspace again (Elijah) [reversion of #128380] - Restore original window size if the window was maximized upon withdrawing it (Elijah, Christian) [#137185] - Fix a raising bug with a window that has more than one child window (Elijah, Billy) [part of #307875] - Try to place windows denied focus near the focus window and fix a xinerama bug with the placement (Elijah) [part of #307875] - Avoid modal dialogs being obscured in somewhat pathologically strange circumstances that Eclipse seems to be good at triggering (Elijah) [part of #307875] Translations Miloslav Trmac (cs), Kostas Papadimas (el), Adam Weinberger (en_CA), Francisco Javier F. Serrador (es), Ilkka Tuohela (fi), Christophe Merlet (RedFox) (fr), Ignacio Casal Quinteiro (gl), Ankit Patel (gu), Yair Hershkovitz (he), Takeshi AIHANA (ja), Kjartan Maraas (nb), Kjartan Maraas (no), Marcel Telka (sk), Theppitak Karoonboonyanan (th), Clytie Siddall (vi), Funda Wang (zh_CN) 2.11.0 == This release contains all fixes up to Metacity 2.10.2 plus some new goodies. Thanks to Matthias Clasen, Aivars Kalvans, Björn Lindqvist, and Andrew Johnson for improvements in this release. - React to cursor theme changes (Matthias) [#308106] - Plug a small leak with xinerama information (Aivars) [#307884] - Split up main() into more manageable chunks and make use of GOpt (Björn) [#305331] - Speed up vertical gradients (Andrew) [#169982] Translations Hendrik Richter (de), Ivar Smolin (et), Ignacio Casal Quinteiro (gl), Clytie Siddall (vi) 2.10.2 == Thanks to Billy Biggs, Greg Hudson, Elijah Newren, Ray Strode, Ryan Lortie, and Soeren Sandmann for improvements in this release. - Makes metacity a bit faster when dragging windows around (Soeren) [#141813] - Fix simple memory error, using the address of a local variable as a hash key (Ryan) [#307209] - Fix a small leak in the case of a SYNC_COUNTER property value and HAVE_XSYNC not defined (Ryan) [#307214] - Cleanup font data when done with it (Ray) [#306720] - If the window has a modal transient which is being unmanaged, don't focus it (Elijah) [#305362] - Make sure window position is calculated correctly for reconfigure requests when part of the XWindowChanges structure is uninitialized (Greg) [#305257] - Add a resize popup when resizing constrained windows (Ray) [#305564] - Don't accidentally treat maximize vertically as maximize in both directions (Elijah) [#302204] - Put all transients of the new window, if any exist, in the calc_showing queue (Elijah, Billy) [#303284] Translations Kostas Papadimas (el), Priit Laes (et), Pauli Virtanen (fi), Ignacio Casal Quinteiro (gl), Theppitak Karoonboonyanan (th), Canonical Ltd (xh), Woodman Tuen (zh_TW) 2.10.1 == This is a stable release to coincide with the release of Gnome 2.10.0. Thanks to Dan Winship and Lex Hider for fixes in this release. - Make sure the "Close" button has the focus in the buggy-session-management-applications-warning dialog instead of the table (Dan) [#172703] - add doc/code-overview.txt and doc/how-to-get-focus-right.txt to the distributed files (Lex) [#170519] Translations Adam Weinberger (en_CA), Christopher Orr (en_GB), Elnaz Sarbar (fa), Gabor Kelemen (hu), Jyotsna Shrestha (ne), Steve Murphy (rw), Baris Cicek (tr), Canonical Ltd (xh) 2.10.0 == This is a stable release to coincide with the release of Gnome 2.10.0. The only difference between this version and 2.9.34 is some translation updates. Translations Vladimir Petkov (bg), Gabor Kelemen (hu), Žygimantas Berucka (lt), Reinout van Schouwen (nl), Mugurel Tudor (ro), Danilo Šegan (sr), Woodman Tuen (zh_TW) 2.9.34 == This is an unstable release to coincide with the release of Gnome 2.10.0 release candidate 1 (2.9.92). Thanks to Aidan Delaney, Elijah Newren, and Joe Marcus Clarke for fixes in this release. - Fix crash that occurs when stupid apps claim that a window is its own parent (Elijah, Joe) [#168207] - Prevent the visual bell from changing the focus window (Elijah) [#123366] - Make sure that icons in the alt-tab popup are dimmed for all hidden windows, not just minimized ones (Aidan) [#168455] Translations Elnaz Sarbar (fa), Ankit Patel (gu), Luca Ferretti (it), Reinout van Schouwen (nl), Gnome PL Team (pl), Alexandre Folle de Menezes (pt_BR) 2.9.21 == This is an unstable release heading towards Gnome 2.10. Since there have been an awful lot of fixes since Gnome 2.10 Beta 2, we are hoping to get an extra week of wider testing of all these changes before hard code freeze. Thanks to Aidan Delaney, Crispin Flowerday, Elijah Newren, and Joe Marcus Clarke for fixes in this release. - Make sure we get a valid timestamp if one doesn't come with the _NET_ACTIVE_WINDOW message (Elijah, Crispin) [#166728] - Avoid sending CurrentTime to our XSetInputFocus wrappers, but handle it better in case we miss any cases (Elijah) [#166732] - Remove useless function call (Aidan) [#166730] - Avoid new windows being obscured by the focus window and thus possibly lost (Elijah) [#166524] - Don't unconditionally place not-to-be-focused windows, such as splashscreens, below the focus window (Elijah) [#167042] - Raise the ancestor of a window instead of the window itself (Elijah) [#166894] - Cover half a dozen issues needed to fix a variety of rare timestamp bugs (Elijah) [#167358] - Fix a possible crash on logout (Joe) [#167935] - Fix an obscure xinerama placement bug with windows that are too large to fit in the workarea in both dimensions (Elijah) [#166757] - Ignore all focus and focus-stealing-prevention code in meta_window_show when not showing the window for the first time (Elijah) [#167199] - when receiving a _NET_ACTIVE_WINDOW message, switch to the desktop where the window is located before activating instead of moving the window to the current desktop (Elijah) [#128380] - Handle _NET_CURRENT_DESKTOP messages that come with timestamps (Elijah) [#161361] - Handle keynav vs. mousenav in mouse and sloppy focus modes (Elijah) [#167545] Translations Jordi Mallach (ca), Martin Willemoes Hansen (da), Kostas Papadimas (el), David Lodge (en_GB), Francisco Javier F. Serrador (es), Tõivo Leedjärv (et), Christophe Merlet (RedFox) (fr), Takeshi AIHANA (ja), Young-Ho, Cha (ko), Kjartan Maraas (nb), Michiel Sikkes (nl), Kjartan Maraas (no), Duarte Loreto (pt), Leonid Kanter (ru), Marcel Telka (sk), Laurent Dhima (sq), Maxim Dziumanenko (uk) 2.9.13 == This is an unstable release to coincide with the release of Gnome 2.10.0 Beta 2 (2.9.91). Thanks to Elijah Newren, Balamurali Viswanathan, Stephane Loeuillet, Benjamin Kahn, Garrett (LeSage?), Jose Moya, Dave Ahlswede, Arvind Samptur, John Paul Wallington, Tim Herold, Muktha Narayan, Sinisa Segvic, Owen Taylor, Crispin Flowerday, "RHEL-3", KWin, and Google for improvements in this release. - Refuse to focus a window with a modal transient, and focus the transient instead (Elijah) [#164716] - Make sure we get gconf notifications about the terminal command changing (Balamurali) [#160934] - Specify encoding of src/metacity.desktop.in (Stephane) [#151850] - New 48x48 default icon (Benjamin, Garrett) [#160660] - Add man pages for metacity-window-demo and metacity-theme-viewer (Jose, Dave) [#143513] - Fix minimized window display in workspace switcher after relogin with a saved session (Elijah) [#164677] - Ignore sticky windows for non-active workspaces (Elijah) [#165259] - Don't wireframe when accessibility is on, it apparently causes a desktop wide freeze. (Arvind) [#159538] - Keep tooltip on screen horizontally for xinerama (John) [#165261] - Stick and unstick transients with their parent automatically (Elijah) [#152283] - Shaded windows should not show up in pagers (Elijah) [#165377] - Treat splashscreens same as other windows for stacking (Elijah) [#165243] - Plug a pair of leaks (Elijah) [#165378] - Take into account the appropriate list of windows when placing a new one (Elijah) [#165381] - Correct misleading and inaccurate wording (Elijah) [#165380] - Handle xcomposite pkgconfig version regression (Tim) [#149368] - Make the warn-about-buggy-session-management-apps dialog be sticky (Elijah) [#164745] - Fix the problem with fullscreen windows on a different xinerama monitor not staying on top ("RHEL-3") [#156511] - Make the unfocussed title bar distinguishable in cases where it otherwise isn't for the Atlanta, Simple, and Bright themes (Muktha) [#125291] - Correct the stacking when returning from fullscreen mode (Elijah) [#165718] - Focus parents of dismissed transient windows in preference to the window that most recently had keyboard focus (Elijah) [#157360] - Make sure window->border_only is initialized so we don't get random windows without decorations (Elijah, Sinisa, Owen) [#145131] - Add period to the end of reduced_resources' description (Dave) [#165780] - If activation requests are too old, set the demands_attention hint instead of actually activating (Elijah, Crispin) [#166395] - Ignore xconfigurerequest events for stacking when it should be safe to do so (Elijah, Crispin, KWin, Google) [#166395] - Set a _METACITY_VERSION property (a utf8 string) on the WM check window (Elijah) [#165350] Translations Vladimir Petkov (bg), Miloslav Trmac (cs), Frank Arnold (de), Adam Weinberger (en_CA), David Lodge (en_GB), Francisco Javier F. Serrador (es), Pauli Virtanen (fi), Young-Ho, Changwoo Ryu (ko), Žygimantas Berucka (lt), Kjartan Maraas (nb), Kjartan Maraas (no), Duarte Loreto (pt), Marcel Telka (sk), Christian Rose (sv), Theppitak Karoonboonyanan (th) 2.9.8 == This is a brown paper bag release to cover up the crash I introduced in version 2.9.5. Thanks to Sebastien Bacher and the bleeding edge Ubuntu users for quickly catching the occasional crash that my fix in #123576 could cause, and for verifying that the patch I made fixed this issue (I couldn't duplicate). - Don't forget to initialize display->grab_old_window_stacking [#165093] 2.9.5 == This is an unstable release to coincide with the release of Gnome 2.10.0 Beta 1 (2.9.90). Thanks to Vincent Noel, Elijah Newren, and John Paul Wallington for fixes in this release. - Restore original stacking when aborting an alt-esc window switch operation (Elijah) [#123576] - Fix vertical maximization for second screen (John) [#163420] - Show labels in bold for windows that demand attention (Vincent) [#164590] - In the tab task switcher popup, dim the window icon and put its name between brackets when the window is minimized (Vincent) [#136666] - Correct highlighting of windows in workspace switcher popup (Elijah) [#163450] Translations zh_CN (Funda Wang), nb (Kjartan Maraas), nn (Kjartan Maraas), de (Frank Arnold) 2.9.3 == This is an unstable release to coincide with the release of Gnome 2.9.4. Thanks to Leena Gunda, Thomas Fitzsimmons, and mild7 users sourceforge net, and Elijah Newren for fixes in this release. - Don't focus the panel on click (Elijah) [#160470, and others] - Make sure the save session dialog appears focused (Elijah) [#162983] - Correctly restore size of window when double clicking the titlebar to unmaximize (Leena) [#161236] - Install schema data from builddir not srcdir (Thomas) [#161417] - Provide more documentation to make it easier for people to contribute to Metacity (Elijah) [#162646] - Allow users to move the window around immediately after double-clicking to shade (Elijah) [#90290] - Focus windows that manually position themselves too (Elijah) [#107347] - Don't show window menu if all options are invalid (Elijah) [#148915] - Exclude windows with skip_taskbar hint set from the alt-tab list; they'll appear in the ctrl-alt-tab list instead. (mild7 users sourceforge net) [#106249] - Wrap XSetInputFocus to make display->expected_focus_window more reliable (Elijah) [#154598] - Remove conflict between windows on multiple workspaces and hidden being a global quantity (Elijah) [#156182] Translations es (Francisco Javier F. Serrador), sv (Christian Rose), cs (Miloslav Trmac), ja (Takeshi AIHANA) 2.9.2 == This is an unstable release to coincide with the release of Gnome 2.9.3. Thanks to Alex Duggan, ash AT contact bg, Elijah Newren, and Baptiste Mille-Mathias for fixes in this release. - Add a missing period at the end of a sentence (Baptiste) [#158210] - When snap-moving don't snap to hidden windows, such as transients of minimized windows (Elijah) [#157180] - Focus the desktop when showing it (Elijah) [#159257] - Remove deprecated capplet (Alex, ash) [#160753] Translations da (Martin Willemoes Hansen), bg (Alexander Shopov), en_CA (Adam Weinberger) 2.9.1 == This is an unstable release heading towards Gnome 2.10, released a little late for Gnome 2.9.2 but there weren't many changes anyway this time... Thanks to Benjamin Kahn, Marco Pesenti Gritti, James Henstridge, and Vincent Untz for fixes/features in this release. - gnome-panel-screenshot was renamed to gnome-screenshot (Vincent) [#157529] - Update build stuff (use newer automake, etc.) (James) - Fix build out of src directory (Marco) [#158325] - Use a better default application icon (Benjamin) [#160373] Translations da (Martin Willemoes Hansen), fr(Christophe Merlet, Baptiste Mille-Mathias), lt(Žygimantas Berucka), ja(Takeshi AIHANA) 2.9.0 == This is an unstable release heading towards Gnome 2.10. Thanks to Rob Adams, Anders Carlsson, Elijah Newren, Soeren Sandmann, and Vincent Untz for fixes and features in this release. - Add a keybinding to launch a terminal (Vincent) [#154232] - Correct the requested number of keycodes (Rob) [#155247] - Add tracker bugs to rationales.txt file - Make the "showing desktop" mode be per-workspace instead of per-screen. (Elijah) [#142198] - Don't try to use an ARGB visual at all if the depth isn't 32-bit. This caused major slowdowns with Composite enabled. (Anders) - Fix the modifier key breakage introduced by an Xorg change. (Soeren) [#151554] - Update _NET_WM_STATE_HIDDEN so the pager on the panel will know whether to display windows as visible or hidden (Elijah) [#105665] - Fix the alt-tab order--if the most recently used window is not focused, start alt tabbing with that window instead of the one after it (Elijah) [#156251] - Don't lower newly mapped windows when they're denied focus if they are transients of the focused window. Instead, defocus the currently focused window (Elijah) [#151996] - Re-enable focus stealing prevention (Elijah) Translations es(Francisco Javier F. Serrador), sq(Laurent Dhima), sr(Danilo Šegan), cs(Miloslav Trmac), en_CA(Adam Weinberger), en_GB(David Lodge) 2.8.6 == This is a stable release for Gnome 2.8.1. Thanks to the Ken Harris, Kjartan Maraas, and the tireless efforts of Elijah Newren for fixes in this release. Fixes * Ensure the correct window is focused when minimizing (Elijah) * Fix keynav with mouse focus (Elijah) * Fix several race conditions in window focusing (Elijah) * Focus the top window when lowering by frame click (Ken) * Fix some compiler warnings (Kjartan) * Fix some valgrind-reported errors (Elijah) * Fix some potential issues with autoraising windows (Elijah) Translations * en_CA(Adam Weinberger), it(Luca Ferretti) 2.8.5 == This is a stable release for Gnome 2.8. Only translations and some new developer documentation were added since the last unstable release. This release boasts improved standards-compliance and a number of bug fixes since the last stable release. Translations * ar(Abdulaziz Al-Arfaj), cs(Miloslav Trmac), cy(Dafydd Harries), en_GB(David Lodge), fr(Christophe Merlet (RedFox)), nn(Åsmund Skjæveland), or(Gora Mohanty), pr_BR(Gustavo Noronha Silva), ro(Mugurel Tudor), th(Paisa Seeluangsawat), tr(Baris Cicek), zh_TW(Woodman Tuen) 2.8.4 == This release features a number of bug fixes, and also the disabling of the focus-stealing-prevention code (we're entering hard code freeze in Gnome so it's too late to fix the remaining issues, especially since it requires several patches to modules other than Metacity). Thanks to Havoc Pennington, Soeren Sandmann, Elijah Newren, and Rich Wareham for fixes in this release Fixes * track the last_xor_rect, for wireframe painting (Havoc) * Move wireframe code before grab is released to prevent endless loops with fullscreen windows. (Soeren) * Make dialogs that Metacity shows follow focus-stealing-prevention conventions. (Elijah; part of #149028) * add render extension check to the display, don't build the compositing manager by default, use an ARGB visual when available for the window frame (Rich Wareham; various tweaks added later by Havoc) * move the have_xrender variable initialization up in the file since it can be set as part of composite check (Havoc) * make argb stuff compile, add some code from xcompmgr (Havoc) * fix an assertion failure that would occur after increasing the number of workspaces; fix stacking order when a window is denied focus (Elijah; #150615) * disable some compositor code that wasn't working, don't grab the server during repaint, various set_background fixes and refactoring (Havoc) Translations * az(Metin Amiroff), bs(Kemal Sanjta), ca(Jordi Mallach), el(Kostas Papadimas), es(Francisco Javier F. Serrador), eu(Iñaki Larrañaga Murgoitio), fi(Pauli Virtanen), nb(Kjartan Maraas), sq(Laurent Dhima), uk(Maxim Dziumanenko) 2.8.3 == Some important bug fixes in this release, including somy a11y bugs, and a compile issue on Solaris. Thanks to Rob Adams, Bill Haneman, Peter O'Shea, Mike Castle, Soeren Sandman, Elijah Newren, and Havoc Pennington for fixes in this release. Fixes * Adjust the MRU list when preventing focus stealing (Elijah) * Ensure that we maintain a focus window when switching workspaces in mouse focus mode (Elijah) * Some improvements in the showing desktop mode, and window activation (Elijah) * Make sure cursors changes are handled correctly (Havoc, Soeren) * Some fixes to the window menu (Rob) * Fix a compile issue on Solaris (Peter, Mike) * Allow struts to go past the middle of the screen, provided there's a gap between them, fixing an issue with gnome magnifier (Bill) Translations * fi (Pauli Virtanen), ja (Takeshi AIHANA), ko (Young-Ho, Cha), pl (Gnome PL Team), ru (Dmitry G. Mastrukov), sr (Danilo Šegan), tk (Gurban M. Tewekgeli), zh_CN (Funda Wang) 2.8.2 === Many bugfixes and better support for the freedesktop.org EWMH spec. Thanks to Rob Adams, Anders Carlsson, Elijah Newren, Soeren Sandmann, Emil Soleyman-Zomalan, Michael Terry, and Jeff Waugh for fixes in this release. - set titlebar_uses_system_font = false (it was ugly) - make naming for "move a window"/"move the window"/"move window" more consistent (fixes #142235) - Add trailing quotes to keybinding explanation text. - support for EWMH update counter spec & add compensation events when events are ignored. (fixes #143333 and #109362) - Fix focus bugs: remove race condition on window close/minimize (#131582), make focus choice consistent for each focus mode (#135810), choose correct focus window when "un-showing the desktop (#144900), make sure correct window is focused when using the workspace switcher (#120100). - Use meta_topic instead of meta_warning when failing to connect to a session manager; reduces metacity verbosity. (fixes #136218) - Make meta_window_delete take a timestamp, and be sure to pass it one. - Add support for EWMH _NET_WM_USER_TIME spec. This enables part of preventing focus stealing. (bug #118372) Also fix bug with windows not being focused on unminimizing caused by original patch. (also bug #118372) - Fix some support for EWMH hints, and fix USER_TIME support to include the DEMANDS_ATTENTION hint. Also includes some code for implementing _NET_RESTACK_WINDOW and _NET_MOVERESIZE_WINDOW, but this is disabled pending feature thaw. 2.8.1 === Thanks to Olivier Crete, Jarrod Johnson, Neil Muller, Elijah Newren, Mark McLoughlin, Rob Adams, and foser AT gentoo.org for fixes in this release. - make the --enable-xinerama switch work properly - prevent unwanted grab op from occurring - don't down-size nitems from a gulong to an int - add a value type check for the visual/audible bell gconf settings - make the no sm support warning resizable - more translations 2.8.0 === No code changes in this release, but some new translations. 2.7.1 === Thanks to Rob Adams for fixes in this release. - bug #122016 - fix a focus race - Change move_to_workspace_left/right/up/down keybindings to arrow to avoid conflicting with new keybindings in spacial nautilus. - fix dialog stacking order issues so e.g. panel properties dialog is above the panel 2.7.0 === First unstable release tarball for GNOME 2.6. Thanks to Anders Carlsson, Elijah Newren, Rob Adams, James Cape, Thomas Fitzimmons, Calum Benson for fixes in this release. 2.6.2 === Thanks to Yukihiro Nakai, Rached Ben Mustapha, Gwenole Beauchesne, Padraig O'Briain, Laurent Vivier, Rob Adams for contributions to this release. - fix to repaint after resize always, so on maximize and theme changes we get things drawn properly - fix a compile issue on HPUX - fix translations of metacity-message output - fix to update window icons when they change - put a limit on number of characters displayed in window titles during Alt+tab - fix configure check for Xrandr - fix 64-bit bug in property reading that broke things badly on 64-bit - don't move focus when clicking close button on a window - fix a crash in getting pixmap icons - spawn dialogs and child processes on the proper screen in multihead situations - if the focus gets set to None, set it back to something sane - load accessibility modules and set accessibility roles - fix hang after displaying warning dialogs - fix a memory corruption when sticking/unsticking windows that lead to a frequent crash and windows appearing in Alt+tab improperly - fix some handling of partial-width panel struts - more translations 2.6.1 === - rebuild with fixed glib-gettext.m4 2.6.0 === - some additional translations 2.5.5 === Thanks to Rob Adams, Arvind Samptur, Andreas Volz, Ray Strode, John Paul Wallington, Soeren Sandmann for contributions to this release. And as always thanks to the translators. - fix aspect ratio handling - fix "shake loose" functionality for maximized windows - handle Xrandr size changes properly again - fix fullscreen window detection - fix workspace name handling - don't steal button press events on root window - nuke metacity.spec due to nonmaintenance - allow too-large-for-screen windows to move their titlebar offscreen - keep an MRU list of windows per-workspace and use it to focus the next window when the focused window disappears - fix cursor when moving - improve appearance of opaque resize - make BELOW window state work - fix a crash when gdk_pixmap_foreign_new() returned NULL 2.5.3 === Thanks to Jordi Mallach, Padraig O'Briain, Rob Adams, Julio Merino, Ben Jansens, Jurg Billeter, Ray Strode, marcus@freebsd.org, James Laska, for contributions to this release. Thanks also to all the tireless translators. - fixups to .desktop file - activate window prior to grab end, avoiding extra focus events - add support for partial-width panels (fixes corner panel and xinerama window position constraints) - added keybinding to toggle window as "always on top" - support --disable-schemas-install option to configure - destroy support for legacy GNOME 1.x hints; metacity no longer works with GNOME 1.x - disable raise-on-click for mouse focus modes - fix bug that broke many Javascript popup menus with mozilla - allow "shaking loose" maximized windows, to move them between Xinerama heads or whatever - honor desktop-wide double click timeout - handle window placement properly for windows that start out maximized - integrate Ximian patch to go ahead and log out after 4 minutes even if a dialog is open - fix a segfault - fix bug where window groups weren't always kept up to date - fix bug where focus got confused when switching workspaces with mouse focus mode - fix 64-bit crash on s390x - chdir to user's homedir on startup - keep window in fullscreen layer when its transients are focused - fix keybindings bug when you had ScrollLock enabled - many translation updates 2.5.2 === Thanks to David Santiago, Julien Olivier, Anders Carlsson, Rob Adams for fixes in this release. - improved wording/UI for some dialogs - while clicking a window button, if you move the mouse outside the button such that releasing the mouse button won't activate the window button, visually indicate by "popping out" the button. - fix some valgrind errors - change "show desktop mode" to convert to "everything is minimized mode" if you open a new window while showing desktop, rather than previous behavior of simply leaving show desktop mode. - fix a trivial memory leak - change "move to workspace N" so it doesn't switch workspaces, just moves the window. - translation updates 2.5.1 === Thanks to Rob Adams, Peter O'Shea, Dafydd Harries, Masahiro Sakai, Soeren Sandmann for fixes in this release. - fix bug where fullscreen windows were below top panels - build fix for Solaris - support diagonal window movement with numeric keypad - multihead fix - build fix for Cygwin - place on xinerama containing the pointer - fix totally hosed window placement/movement for frameless windows - improvement to smoothness of window move/resize 2.5.0 === Thanks to Rob Adams, Owen Taylor, Frederic Crozat, Arvind Samptur, Bill Haneman, Akira Tagoh for help with fixes in this release. - many new translations - fix an infinite loop while holding a server grab triggered by some recent Qt versions doing weird stuff - fix bug where Alt+rightclick repeatedly on titlebar resulted in zillions of menus - fix Alt+Tab to *actually* put minimized windows at the end, though this was always intended - rewrite size/positions constraint code (currently known to be quite buggy, e.g. xmms is hosed) - enforce size of at least 1x1 on windows - reduce latency of managing new windows still further by using async properties code in more places - don't grab keybindings on docks, so gnome-panel can handle them - suck in the panel's screenshot and run dialog global bindings - lots of improvements to window placement - sync max number of workspaces with pager applet - fix to keep focus when inside window frame in strict mouse focus mode - make it possible to start a reverse tab with shift+alt+tab (vs. alt+tab then shift) - fix a multihead issue with constraints between two windows on different heads - require GTK+ 2.2.0 and fontconfig - default theme is now Simple - add visual bell feature - incorporate many fixes from 2.4.34 - other stuff 2.4.13 === - we were making all dialogs skip the taskbar, even non-transient ones, though this was supposedly fixed a while ago. Now really fixed. - change back to Alt+click by default for the window drag feature. - assign Alt+F12 to shade window - fix not deleting enough workspaces when the number was reduced via the pager config dialog (readams@hmc.edu) - don't allow windows under the top panel ever, even if they are tall windows (Arvind) - fix up the window layout for directional workspace nav, so you always stop at the edges and always end up where you expect (hp, with tweaks from readams@hmc.edu) - focus new windows in mouse focus mode (readams@hmc.edu) - support xeyes, oclock, etc. by applying shape mask to the window manager frame (yeah it resizes slow, deal) - fix vertical/horizontal maximize - handle crossing events resizing for more opaque resize goodness (Soeren) - add wacky _METACITY_UPDATE_COUNTER experimental extension to do nice opaque resizing (does nothing without a GTK patch) - fix a crash setting workspace names - fix internationalized WM_NAME reading muffin-6.4.1/meson/0000775000175000017500000000000014723361714013063 5ustar fabiofabiomuffin-6.4.1/meson/meson-postinstall.sh0000775000175000017500000000046614723361714017123 0ustar fabiofabio#!/bin/sh # Package managers set this so we don't need to run if [ -z "$DESTDIR" ]; then echo Compiling GSettings schemas... glib-compile-schemas ${MESON_INSTALL_PREFIX}/share/glib-2.0/schemas echo Updating desktop database... update-desktop-database -q ${MESON_INSTALL_PREFIX}/share/applications fi muffin-6.4.1/doc/0000775000175000017500000000000014723361714012507 5ustar fabiofabiomuffin-6.4.1/doc/rationales.txt0000664000175000017500000000574614723361714015425 0ustar fabiofabio History ==== Focus issues: see doc/how-to-get-focus-right.txt Keep panel always on top: http://bugzilla.gnome.org/show_bug.cgi?id=81551 Edge flipping: http://bugzilla.gnome.org/show_bug.cgi?id=82917 Opaque resize: http://bugzilla.gnome.org/show_bug.cgi?id=92618 Alt+click to move/resize: http://bugzilla.gnome.org/show_bug.cgi?id=101151 https://bugzilla.redhat.com/bugzilla/show_bug.cgi?id=80918 minimized windows in Alt+tab: http://bugzilla.gnome.org/show_bug.cgi?id=89416 dialogs above entire app group: http://bugzilla.gnome.org/show_bug.cgi?id=88926 display window size/position: http://bugzilla.gnome.org/show_bug.cgi?id=85213 http://bugzilla.gnome.org/show_bug.cgi?id=106645 http://bugzilla.gnome.org/show_bug.cgi?id=130821 configure click actions, alt+click: http://bugzilla.gnome.org/show_bug.cgi?id=83210 system modal dialogs: http://bugzilla.gnome.org/show_bug.cgi?id=83357 workspace wrapping: http://bugzilla.gnome.org/show_bug.cgi?id=89315 raise windows on click: http://bugzilla.gnome.org/show_bug.cgi?id=326156 http://bugzilla.gnome.org/show_bug.cgi?id=86108 http://bugzilla.gnome.org/show_bug.cgi?id=115072 http://bugzilla.gnome.org/show_bug.cgi?id=115753 Pointer warping: http://bugzilla.gnome.org/show_bug.cgi?id=134353 http://bugzilla.gnome.org/show_bug.cgi?id=134352 (Think about tasklist & window selector too; this would be a very bad idea) Bugs for easy dupe-finding that seem to be hard to find otherwise: === Applications opening in wrong workspace: http://bugzilla.gnome.org/show_bug.cgi?id=160687 Tracking bugs ==== revise theme format: http://bugzilla.gnome.org/show_bug.cgi?id=102547 session management: http://bugzilla.gnome.org/show_bug.cgi?id=107063 focus-stealing-prevention: http://bugzilla.gnome.org/show_bug.cgi?id=149028 other focus bugs: http://bugzilla.gnome.org/show_bug.cgi?id=155450 drag-and-drop: http://bugzilla.gnome.org/show_bug.cgi?id=155451 raising/stacking: http://bugzilla.gnome.org/show_bug.cgi?id=155452 tasklist/workspace switcher: http://bugzilla.gnome.org/show_bug.cgi?id=155453 window/workspace selection: http://bugzilla.gnome.org/show_bug.cgi?id=155456 key/mouse-binding actions: http://bugzilla.gnome.org/show_bug.cgi?id=155457 moving/resizing (constraints): http://bugzilla.gnome.org/show_bug.cgi?id=155458 window placement: http://bugzilla.gnome.org/show_bug.cgi?id=155460 logout/system-monitor keys: http://bugzilla.gnome.org/show_bug.cgi?id=155462 modal dialogs: http://bugzilla.gnome.org/show_bug.cgi?id=164841 multi-head sans xinerama: http://bugzilla.gnome.org/show_bug.cgi?id=324772 xinerama: http://bugzilla.gnome.org/show_bug.cgi?id=324773 output-only windows: http://bugzilla.gnome.org/show_bug.cgi?id=340584 allowed actions/window-type: http://bugzilla.gnome.org/show_bug.cgi?id=340682 EWMH/ICCCM compliance: http://bugzilla.gnome.org/show_bug.cgi?id=340691 muffin-6.4.1/doc/code-overview.txt0000664000175000017500000001712014723361714016027 0ustar fabiofabioThis is not meant to be comprehensive by any means. Rather it is meant as just a brief overview of some of the bigger structures and files, with guides for a variety of task categories providing places to start looking in the code and things to look for. Overview Jobs of various files Major data structures and their relationships Getting started -- where to look Jobs of various files src/window.c is where all the guts of the window manager live. This is basically the only remotely scary file. src/frames.c is the GtkWidget that handles drawing window frames. src/core.h defines the interface used by the GTK portion of the window manager to talk to the other portions. There's some cruft in here that's unused, since nearly all window operations have moved out of this file so frameless apps can have window operations. src/ui.h defines the interface the plain Xlib portion of the window manager uses to talk to the GTK portion. src/theme.c and src/theme-parser.c have the theme system; this is well-modularized from the rest of the code, since the theme viewer app links to these files in addition to the WM itself. Major data structures and their relationships Major structs have a "Meta" prefix, thus MetaDisplay, MetaScreen, MetaWindow, etc. This serves as a way of namespacing in C. It also has the side effect of avoiding conflicts with common names that X already uses such as Display, Screen, Window, etc. Note that when I refer to a display below, I'm meaning a MetaDisplay and not a Display. Don't confuse displays and screens. While Metacity can run with multiple displays, it is kind of useless since you might as well just run two copies of Metacity. However, having multiple screens per display is useful and increasingly common (known as "multiscreen" and "xinerama" setups, where users make use of more than one monitor). You should basically think of a display as a combination of one or more monitors with a single keyboard (...and usually only one mouse). There is also a significant difference between multiscreen and xinerama as well. Basically, each MetaScreen is a root window (root node in the tree of windows). With Xinerama, a single root window appears to span multiple monitors, whereas with multiscreen a root window is confined to a single monitor. To re-emphasize the distinction between a display and a screen, the pointer and keyboard are shared between all root windows for a given display. The display keeps track of a lot of various global quantities, but in particular has a compositor and a list (GList) of screens. A compositor is an opaque structure (only defined in compositor.c), meaning that you'll only reference the API for it. It handles (or will handle) cool stuff with the new X extensions, such as smooth resizing and alpha transparency. A screen keeps track of a number of quantities as well, in particular a stack and a list of workspaces. A stack is basically a list of windows, and the depth order they have relative to each other (which thus determines which windows are on top and which are obscured). A workspace mostly contains a list of windows for the workspace, but also has a few other quantities as well (a list of struts which are areas where windows should not be placed and an mru_list or "most recently used window list"). A window has a huge list of quantities for keeping track of things about a window on the screen. (We want to avoid making this list larger because the memory for all these quantities is per window.) One item in particular that a window has, though, is a frame. A frame is the decorations that surround the window (i.e. the titlebar and the minimize and close buttons and the part that you can use to resize), and contains a handful of variables related to that, but no other major structures. Getting started -- where to look Getting started on developing free software projects can often be like being dropped off in a town that is unknown to you and being told to make a map, when various road and building signs are missing or fading. To try to alleviate that initial difficulty in orientation, below I list a variety of general task categories with file, function, variable, and x property names that may be useful to fixing bugs or writing features that fall within that category. First, though, it's useful to note that most event and message passing goes through display.c:event_callback(), so that's often a good place to start reading for general familiarity with the code (actually, I'd suggest skipping down to the first switch statement within that function). Of course, not all events go through that function, as there are a few other places that handle events too such as frames.c. Anyway, without further ado, here are the categories and (hopefully) useful things to look at for each: Focus issues (i.e. issues with which window is active): doc/how-to-get-focus-right.txt meta_workspace_focus_default_window _NET_ACTIVE_WINDOW _NET_WM_USER_TIME meta_window_focus meta_display_(set_input|focus_the_no)_focus_window XSetInputFocus (only for purposes of understanding how X focus/input works) CurrentTime (mostly, you should just think "Bad; don't use it") Compositor stuff (X extension for eye candy like transparency): compositor.c The luminocity module in CVS Window depth (i.e. stacking or lowering/raising) issues: stack.c _NET_CLIENT_LIST_STACKING transient_for WM_TRANSIENT_FOR meta_window_(raise|lower) _NET_WM_WINDOW_TYPE _NET_WM_MOUSE_ACTION/_NET_WM_TAKE_ACTIVITY? (aren't yet in EWMH) Window placement issues: place.c constraints.c _NET_WM_STRUT WM_SIZE_HINTS Moving and resizing issues: constraints.c update_move update_resize meta_window_handle_mouse_grab_op_event _NET_MOVERESIZE_WINDOW _NET_WM_STRUT Drag and drop issues: the XDND protocol (see http://www.newplanetsoftware.com/xdnd/ and http://freedesktop.org/Standards/XDND) _NET_WM_MOUSE_ACTION/_NET_WM_TAKE_ACTIVITY (aren't yet in EWMH) A general pointer: what causes the difficulty here is that when the application receives a mouse click to start a drag, it does a grab so that the window manager doesn't get any further events; thus correcting things require standards so that applications and window managers can collaborate correctly Theme issues: ??? doc/theme-format.txt theme.c theme-parser.c (ui.c, core.c, frames.c, frame.c? I dunno...) Session management issues: ??? session.c http://www.x.org/X11R6.8.1/doc/SM/xsmp.pdf ? http://www.x.org/X11R6.8.1/doc/SM/SMlib.pdf ? meta_window_apply_session_info Tasklist and Workspace switcher issues: window-props.c various functions in screen.c (especially ones using XChangeProperty) xprops.c The libwnck module in cvs meta_window_client_message Lots of the EWMH Window and workspace selection/changing issues: tabpopup.c keybindings.c, functions: *_workspace*, *_tab_* meta_screen_ensure_*_popup display.c, functions: *_tab* Key and mouse binding actions: keybindings.c meta_frames_button_(press|release)_event display.c: event_callback, but only the (Key|Button)_(Press|Release) cases Xinerama and multiscreen: ??? In general, just search for Xinerama, but in particular see screen.c window.c place.c constraints.c muffin-6.4.1/doc/strut-and-related-updating.txt0000664000175000017500000000530014723361714020416 0ustar fabiofabioHow updates happen for struts, workareas, and screen/xinerama regions/edges: One of three things causes meta_window_update_struts to be called (a) initial window map (window.c:meta_window_new_with_attrs()) (b) update of _net_wm_strut* properties (window.c:process_property_notify()) (c) screen resizes (e.g. via xrandr; from screen.c:meta_screen_resize_func()) meta_window_update_struts (MetaWindow *window) - Gets new list of struts from window properties - Makes sure window doesn't single-handedly fill the screen - records new struts if different and calls invalidate_work_areas() invalidate_work_areas () - Calls meta_workspace_invalidate_work_area() for each workspace it's on meta_workspace_invalidate_work_area() - Cleans out all strut lists - queues all windows for resizing - Calls meta_screen_queue_workarea_recalc (workspace->screen); meta_screen_queue_workarea_recalc() - Adds set_work_area_idle_func() as an idle handler set_work_area_idle_func() - Calls set_work_area_hint() set_work_area_hint() - Calls meta_workspace_get_work_area_all_xineramas() - Sets _NET_WORKAREA property meta_workspace_get_work_area_all_xineramas() - Calls ensure_work_areas_validated() ensure_work_areas_validated() - Loops over xineramas - Loops over windows, then struts: - Adds struts to list first time through xinerama loop - Find the amount of the strut on the given xinerama for _strut - Just max the amount of the strut with the all__strut - Makes sure there's a non-empty xinerama workarea - Record the xinerama workarea - Make sure there's a non-empty screen workarea - Record the screen workarea - Cache the spanning rects for the screen and xinerama regions - Cache the screen and xinerama edges Alternatively to all the above, if the idle function for the screen has not yet fired, constraints.c:setup_constraint_info() can call either workspace.c:meta_workspace_get_onscreen_region() or workspace.c:meta_workspace_get_onxinerama_region() which in turn call workspace.c:ensure_work_areas_validated(). Meaning of related functions that might be difficult to tell apart: screen.c:meta_screen_get_current_xinerama () - Finds out which xinerama the mouse is on with an XQueryPointer window.c:meta_window_get_work_area_current_xinerama() window.c:meta_window_get_work_area_for_xinerama() window.c:meta_window_get_work_area_all_xineramas () - All three are for finding the intersection of workareas across multiple workspaces so that placement of windows can be determined in such a way that they remain in the workarea for all workspaces that they are on. muffin-6.4.1/doc/man/0000775000175000017500000000000014723361714013262 5ustar fabiofabiomuffin-6.4.1/doc/man/meson.build0000664000175000017500000000003014723361714015415 0ustar fabiofabioinstall_man('muffin.1') muffin-6.4.1/doc/man/muffin.10000664000175000017500000000511314723361714014630 0ustar fabiofabio.\" Hey, EMACS: -*- nroff -*- .\" First parameter, NAME, should be all caps .\" Second parameter, SECTION, should be 1-8, maybe w/ subsection .\" other parameters are allowed: see man(7), man(1) .TH MUFFIN 1 "11 February 2006" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME MUFFIN \- Clutter based compositing GTK2 Window Manager .SH SYNOPSIS .B MUFFIN [\-\-display=\fIDISPLAY\fP] [\-\-replace] [\-\-sm\-client\-id=\fIID\fP] [\-\-sm\-disable] [\-\-sm\-save\-file=\fIFILENAME\fP] [\-\-version] [\-\-help] .SH DESCRIPTION This manual page documents briefly .B MUFFIN\fP. .PP .\" TeX users may be more comfortable with the \fB\fP and .\" \fI\fP escape sequences to invode bold face and italics, .\" respectively. \fBMUFFIN\fP is a minimal X window manager aimed at nontechnical users and is designed to integrate well with the Cinnamon desktop. \fBMUFFIN\fP lacks some features that may be expected by traditional UNIX or other technical users; these users may want to investigate other available window managers for use with Cinnamon or standalone. .SH OPTIONS .TP .B \-\-display=DISPLAY Connect to X display \fIDISPLAY\fP. .TP .B \-\-replace a window manager which is running is replaced by \fBMUFFIN\fP. Users are encouraged to change the Cinnamon window manager by running the new WM with the --replace or -replace option, and subsequently saving the session. .TP .B \-\-sm\-client\-id=ID Specify a session management \fIID\fP. .TP .B \-\-sm\-disable Disable the session management. .TP .B \-\-sm\-save\-file=FILENAME Load a session from \fIFILENAME\fP. .TP .B \-\-version Print the version number. .TP .B \-?, \-\-help Show summary of options. .SH CONFIGURATION \fBMUFFIN\fP configuration can be found under \fIPreferences\fP->\fIWindows\fP and \fIPreferences\fP->\fIKeyboard Shortcuts\fP on the menu-panel. Advanced configuration can be achieved directly through gsettings. .SH SEE ALSO .BR MUFFIN-message (1) .SH AUTHOR The original manual page was written by Thom May . It was updated by Akira TAGOH for the Debian GNU/Linux system (with permission to use by others), and then updated by Luke Morton and Philip O'Brien for inclusion in MUFFIN. muffin-6.4.1/doc/how-to-get-focus-right.txt0000664000175000017500000003414014723361714017474 0ustar fabiofabioTo make choice of focus window consistent for each focus method, a number of guidelines should be followed. (For purposes of discussion here, I'm excluding things like the panel and the desktop from "windows". It is technically incorrect to do this, but I'm lazy and "windows" is shorter than something like "normal windows". See the end of the discussion for how these special cases are handled.) The basics are easy: Focus method Behavior click When a user clicks on a window, focus it sloppy When an EnterNotify is received, focus the window mouse Same as sloppy, but also defocus when mouse enters DESKTOP window Note that these choices (along with the choice that clicking on a window raises it for the click focus method) introduces the following invariants for focus from mouse activity: Focus method Invariant click The window on top is focused sloppy If the mouse is in a window, then it is focused; if the mouse is not in a window, then the most recently used window is focused. mouse If the mouse is in a non-DESKTOP window, then it is focused; otherwise, the designated "no_focus_window" is focused However, there are a number of cases where the current focus window becomes invalid and another should be chosen. Some examples are when a focused window is closed or minimized, or when the user changes workspaces. In these cases, there needs to be a rule consistent with the above about the new window to choose. Focus method Behavior click Focus the window on top sloppy Focus the window containing the pointer if there is such a window, otherwise focus the most recently used window. mouse Focus the non-DESKTOP window containing the pointer if there is one, otherwise focus the designated "no_focus_window". Note that "most recently used window", as used here, has a slightly different connotation than "most recent to have keyboard focus". This is because when a user activates a window that is a transient, its ancestor(s) should be considered to be more recently used than other windows that have had the keyboard focus more recently. (See bug 157360; this may mean that the alt-tab order should also change simultaneously, although the current implementation does not do that.) Also, sometimes a new window will be mapped (e.g. unminimizing a window or launching a new application). Most users want to interact with new windows right away, so these should typically be focused. This does conflict with the invariants for sloppy and mouse focus modes, so this wouldn't be true for a strict-pointer-focus mode. For all other modes (non-strict-pointer-focus modes), there are only two cases in which a new window shouldn't be focused: 1) If the window takes a while to launch and the user starts interacting with a different application, the new window should not take focus. 2) If the window that will appear was not launched by the user (error dialogs, instant messaging windows, etc.), then the window should not take focus when it appears. To handle these cases, Metacity compares timestamps of the event that caused the launch and the timestamp of the last interaction with the focused window. (Case 2 is handled by the application providing a special timestamp of 0 for the launch time, which ensures that the window that appears doesn't get focus) If the newly launched window isn't focused, some things should be done to alert the user that there is a window to work with: 1) The _NET_WM_DEMANDS_ATTENTION hint should be set 2) If the new window isn't modal for the focused window, it should appear below the focused window so that it doesn't obscure the focused window that the user is interacting with. 3) If the new window is modal to the focused window, the currently focused window should lose focus but the modal window should appear on top. Additionally, the user may decide to use the keyboard instead of the mouse to navigate between windows (referred to as "keynav"). This poses no problems for click-to-focus (because the same invariant can be maintained), but for sloppy and mouse focus it requires extra work to attempt to handle the INHERENTLY CONFLICTING CONSTRAINTS. Metacity does this by having a mouse_mode boolean used to determine which of the two sets of invariants holds. This mode is set according to which method was most recently used to choose a focus window: 1) When receiving EnterNotify events from mouse movement, set mouse_mode to TRUE. 2) When using keynav to choose a focus window (e.g. alt-tab, alt-esc, alt-f2, move-window-to-workspace keybindings), set mouse_mode to FALSE. 3) When handling events that don't choose a focus window but rather need a focus_window chosen for them (e.g. switch-to-workspace keybindings), don't change the mouse_mode and just use the current value. Note that grabs present a special case since they can generate EnterNotify and LeaveNotify events without using the mouse, thus these events should be ignored when the crossing mode is NotifyGrab or NotifyUngrab. THIS MOUSENAV/KEYNAV MODERATION METHOD IS NOT PERFECT--there are corner cases when trying to mix-and-match between mousenav and keynav simultaneously that cause problems; but it appears to be the most reasonable tradeoff and works well in most cases, especially if the user sticks to just mousenav for a long time or just keynav for a long time. Finally, windows of type WM_DOCK or WM_DESKTOP (e.g. the desktop and the panel) present a special case, at least partially due to the lack of decorations. For WM_DESKTOP windows, we only focus them if the user explicitly requests it (e.g. clicks on the window, uses Ctrl-Alt-Tab to navigate to it, uses a keybinding to show the desktop, etc.). For WM_DOCK windows, we do not focus unless we receive a very explicit request (e.g. Ctrl-Alt-Tab or a _NET_ACTIVE_WINDOW message; not normal clicks). To read more about the bugs that inspired these choices: - When a focused window becomes invalid and another should be chosen http://bugzilla.gnome.org/show_bug.cgi?id=135810 - When a new window is mapped http://bugzilla.gnome.org/show_bug.cgi?id=118372 Also, the EWMH spec, especially the parts relating to _NET_WM_USER_TIME - Modal vs. non-modal dialogs that get denied focus when mapped http://bugzilla.gnome.org/show_bug.cgi?id=151996 - Mousenav vs. Keynav in mouse and sloppy focus modes http://bugzilla.gnome.org/show_bug.cgi?id=167545 http://bugzilla.gnome.org/show_bug.cgi?id=101190 http://bugzilla.gnome.org/show_bug.cgi?id=357695 - Not focusing panels http://bugzilla.gnome.org/show_bug.cgi?id=160470 http://bugzilla.gnome.org/show_bug.cgi?id=120100 There were many bugs which had to be fixed to get all the above working; they helped form these policies and/or show the difficulties in implementing this policy (my apologies in advance for producing a list heavily lopsided to what I've done; it's just that these bugs are the ones I'm the most familiar with): bug 72314 ignore LeaveNotify events from grabs bug 82921 focus windows on map bug 87531 only show focus for sticky windows on active workspace (pager) bug 94545 focus window on workspace switch is non-deterministic bug 95747 should ignore EnterNotify events with NotifyInferior detail set bug 97635 sticky windows always keep focus when switching workspaces bug 102665 a window unminimized from the tasklist should be focused bug 107347 focus windows that manually position themselves too bug 108643 focus in MRU order instead of stack order bug 110970 moving a window to another workspace loses focus bug 112031 closing a dialog can result in a strange focus window bug 115650 add _NET_WM_USER_TIME support to gtk+ (see also 150502) bug 120100 panel shouldn't be focused after workspace applet usage bug 123803 need final EnterNotify after workspace switch (see also 124798) bug 124981 focus clicked window in pager only if on current workspace bug 125492 catch the xserver unfocusing everything and fix its braindeadedness bug 128200 focus correct window on libwnck window minimize (see 107681 too) bug 131582 fix race condition on window minimize/close bug 133120 wrong window focused when changing workspaces bug 135024 _NET_ACTIVE_WINDOW messages need timestamps bug 135786 middle-clicking on focused window to lower it should defocus too bug 136581 window minimization vs. activation for mouse focus bug 144900 fix focus choice on "un-showing" the desktop bug 147475 don't lock keyboard on workspace change bug 148364 DEMANDS_ATTENTION support for metacity & libwnck (and other stuff) bug 149028 focus-stealing-prevention for metacity-dialog (and other stuff) bug 149366 windows denied focus on map occur in wrong order in alt-tab list bug 149543 consistent focus window when unshowing desktop bug 149589 race in focus choice from libwnck messages bug 150271 make sure "run application" dialog gets focused bug 150668 update gtk+ _NET_ACTIVE_WINDOW support bug 151245 application startup notification forwarding (partially rejected) bug 151984 Soeren's idea--backup timestamp when startup notification not used bug 151990 prevent focus inconsistencies by only providing one focus method bug 151996 modal dialogs denied focus should not be lowered bug 152000 fix race on window close followed by rapid mouse movement bug 152004 ways to handle new window versus mouse invariants bug 153220 catch the root window getting focus and reset to default window bug 157360 focus parents of dismissed transient windows in preference to the window that most recently had focus bug 159257 focus the desktop when showing it bug 160470 don't focus panels on click bug 163450 correct highlighting in workspace switcher popup bug 164716 refuse to focus a window with a modal transient, and focus the transient instead bug 166524 avoid new windows being obscured by the focus window bug 167545 mousenav vs. keynav in mouse and sloppy focus modes Addendum on sloppy and mouse focus You may occasionally hear people refer to sloppy or mouse focus modes as inherently buggy. This is what they mean by that: 1) Keynav doesn't maintain the same invariants as mouse navigation for these focus modes; switching back and forth between navigation methods, therefore, may have or appear to have inconsistencies. Examples: a) If the user uses Alt-Tab to change the window with focus, then starts to move the mouse, at that moment the window where the mouse is does not have focus. b) Users expect that a workspace they previously used will not change when the return to it. This means things like window position and stacking order, but also the focus window. Unfortunately, using the original focus window (which would be the most recently used window on that workspace) will sometimes conflict with the invariants for mouse and sloppy focus modes. Users are much more surprised by the invariant being broken than by having the focus window changed (see bug 94545 and probably others), so we maintain the invariant. This only matters when using Ctrl-Alt-Arrow to switch workspaces instead of clicking in the workspace switcher, so this really is a keynav vs mouse issue. Either that, or a windows-are-being-mapped exception. ;-) c) Opening a menu, then moving the mouse to a different window, and then pressing escape to dismiss the menu will result in the window containing the mouse not being focused. This is actually correct behavior (because pressing escape shows that the user is using key navigation to interact with the window containing the menu) but is one of those hard-to-get-right keynav and mouse focus mixture cases. (See bug 101190 for more details) d) Similar to (c), moving the mouse off the menu doesn't immediately focus the window that the mouse goes over, due to an application grab (we couldn't change this and wouldn't want to, but technically it does break the invariant). e) If mouse_mode is off and the user does something to cause focus to change (e.g. switch workspaces, close or minimize a window, etc.) and simultaneously tries to move the mouse, the choice of which window to focus is inherently race-y. (You probably can't satisfy both keynav and mousenav invariants simultaneously...) 2) The sloppy/mouse invariants are often not strictly maintained; for example, we provide an exception to the invariant for newly mapped windows. (Most find that not allowing this exception is confusing) 3) There are an awful lot of little cases to handle to get any focus mode right, even for click-to-focus. Since mouse and sloppy focus have sometimes been hard to even determine what correct behavior is, it is much harder to get them completely right. Plus mouse and sloppy focus users are a minority, decreasing the motivation of window manager implementors to get those focus modes right. 4) Because of -1-, -2-, and -3-, implementations are often buggy or inconsistent and people form their opinions from usage of these implementations. 5) Sloppy focus suffers from a bit of a discoverability problem (for example, I have seen a scientist sit down to a computer for which sloppy focus was in use and take a few minutes before figuring out how window activation worked; granted the layout of the windows in that situation was a bit unusual but it still illustrates that sloppy focus is harder than it should be to figure out). Mouse focus solves this problem; however, people that have experience with other computing environments are accustomed to being able to move their mouse outside the window they are working with and still continue interacting with that window, which conflicts with mouse focus. muffin-6.4.1/doc/dialogs.txt0000664000175000017500000000152614723361714014676 0ustar fabiofabioDialogs which have no transient parent or root window being their tranisent parent are the ones which will be visible in the tasklist. All such dialogs will be *always* on top of the window group i.e they would transients for the whole group. 1) Modal dialogs * If you wish to open another window from a modal dialog open *only* a modal dialog and set it's transient parent. 2) Normal dialog without transient parent * If you wish to open another window from a normal dialog open either a normal dialog or a modal dialog only. Set the transient parent for the child dialog if you do not want them to be transient for all the other windows in the group. with transient parent * If you wish to open another window from a normal dialog you could open any type of window. muffin-6.4.1/doc/compositor-control.txt0000664000175000017500000000434314723361714017130 0ustar fabiofabioThe compositor is the box of tricks inside the window manager which performs special effects on the windows on your screen. Metacity's compositor is under development. Your help is requested in finding and fixing bugs. This document tells you how to configure Metacity so that you can use compositing. To turn the compositor on initially, you need to pass --enable-compositor to the configure script. This will introduce a dependence on libcm, which you can get from . When Metacity is compiled, you will need to turn the compositor on in gconf for it to have any effect. You will find the boolean switch at /apps/metacity/general/compositing_manager When that's done, you can set some environment variables before you launch Metacity to influence how the compositor works. These will eventually become configuration options or gconf options when they grow up. Define them to any value to turn them on; leave them undefined to turn them off. Currently the options you can set are: LIBCM_DIRECT If this is set, the compositor will bypass the X server and do all its work directly with the hardware. I know of no reason you would want to do so, but perhaps you do. LIBCM_TFP If this is set ("tfp mode"), the compositor will feel free to use the texture_from_pixmap extension; if this is not set ("non-tfp mode"), the compositor will use a workaround. Many drivers require non-tfp mode in order to work, and will paint all windows clear blue or clear white without it. Thanks to Travis Watkins for suggesting this switch; he cautions that some games or video players may require tfp mode. METACITY_BLING This turns on several pretty but non-essential animations (dialogues fracturing and exploding, minimisations doing a shrinkydink effect, and so on). If it is not set, the standard non-GL animations are retained. This affects only window event animations; it doesn't change menus zooming, dialogues being semi-transparent, and so on. Try it and see whether you like it. If you have any problems, ask on mutter-devel-list@gnome.org, or #gnome-hackers on gimpnet, or come and find me (tthurman at gnome) and ask. muffin-6.4.1/doc/how-constraints-works.txt0000664000175000017500000003367714723361714017575 0ustar fabiofabioFile contents: Basic Ideas Important points to remember Explanation of fields in the ConstraintInfo struct Gory details of resize_gravity vs. fixed_directions IMPORTANT NOTE: There's a big comment at the top of constraints.c explaining how to add extra constraints or tweak others. Read it. I put that information there because it may be enough information by itself for people to hack on constraints.c. I won't duplicate that information in this file; this file is for deeper details. --------------------------------------------------------------------------- Basic Ideas --------------------------------------------------------------------------- There are a couple basic ideas behind how this constraints.c code works and why it works that way: 1) Split the low-level error-prone operations into a special file 2) Add robustness by prioritizing constraints 3) Make use of a minimal spanning set of rectangles for the "onscreen region" (screen minus struts). 4) Constraints can be user-action vs app-action oriented 5) Avoid over-complification ;-) Some more details explaining these basic ideas: 1) Split tedious operations out boxes.[ch] have been added which contain many common, tedious, and error-prone operations. I find that this separation helps a lot for managing the complexity and ensuring that things work correctly. Also, note that testboxes.c thoroughly tests all functionality in boxes.[ch] and a testboxes program is automatically compiled. Note that functions have also been added to this file to handle some of the tedium necessary for edge resistance as well. 2) Prioritize constraints In the old code, if each and every constraint could not be simultaneously satisfied, then it would result in some difficult-to-predict set of constraints being violated. This was because constraints were applied in order, with the possibility for each making changes that violated previous constraints, with no checking done at the end. Now, all constraints have an associated priority, defined in the ConstraintPriority enum near the top of constraints.c. The constraints are all applied, and then are all checked; if not all are satisfied then the least important constraints are dropped and the process is repeated. This ensures that the most important constraints are satisfied. A special note to make here is that if any one given constraint is impossible to satisfy even individually (e.g. if minimum size hints specify a larger window than the screen size, making the fully-onscreen constraint impossible to satisfy) then we treat the constraint as being satisfied. This sounds counter-intuitive, but the idea is that we want to satisfy as many constraints as possible and if we treat it as a violation then all constraints with a lesser priority also get dropped along with the impossible to satisfy one. 3) Using maximal/spanning rectangles The constraints rely heavily on something I call spanning rectangles (which Soeren referred to as maximal rectangles, a name which I think I like better but I don't want to go change all the code now). These spanning rectangles have the property that a window will fit on the screen if and only if it fits within at least one of the rectangles. Soeren had an alternative way of describing these rectangles, namely that they were rectangles with the property that if you made any of them larger in any direction, they would overlap with struts or be offscreen (with the implicit assumption that there are enough of these rectangles that combined they cover all relevant parts of the screen). Note that, by necessity, these spanning/maximal rectangles will often overlap each other. Such a list makes it relatively easy to define operations like window-is-onscreen or clamp-window-to-region or shove-window-into-region. Since we have a on-single-xinerama constraint in addition to the onscreen constraint(s), we cache number_xineramas + 1 of these lists in the workspace. These lists then only need to be updated whenever the workarea is (e.g. when strut list change or screen or xinerama size changes). 4) Constraints can be user-action vs app-action oriented Such differentiation requires special care for the constraints to be consistent; e.g. if the user does something and one constraint applies, then the app does something you have to be careful that the constraint on the app action doesn't result in some jarring motion. In particular, the constraints currently allow offscreen movement or resizing for user actions only. The way consistency is handled is that at the end of the constraints, update_onscreen_requirements() checks to see if the window is offscreen or split across xineramas and updates window->require_fully_onscreen and window->require_on_single_xinerama appropriately. 5) Avoid over-complification The previous code tried to reform the constraints into terms of a single variable. This made the code rather difficult to understand. ("This is a rather complicated fix for an obscure bug that happened when resizing a window and encountering a constraint such as the top edge of the screen.") It also failed, even on the very example for which it used as justification for the complexity (bug 312104 -- when keyboard resizing the top of the window, Metacity extends the bottom once the titlebar hits the top panel), though the reason why it failed is somewhat mysterious as it should have worked. Further, it didn't really reform the constraints in terms of a single variable -- there was both an x_move_delta and an x_resize_delta, and the existence of both caused bug 109553 (gravity with simultaneous move and resize doesn't work) --------------------------------------------------------------------------- Important points to remember --------------------------------------------------------------------------- - Inner vs Outer window Note that because of how configure requests work and meta_window_move_resize_internal() and friends are set up, that the rectangles passed to meta_window_constrain() are with respect to inner window positions instead of outer window positions (meaning that window manager decorations are not included in the position/size). For the constraints that need to be enforced with respect to outer window positions, you'll need to make use of the extend_by_frame() and unextend_by_frame() functions. - meta_window_move_resize_internal() accepts a really hairy set of inputs. See the huge comment at the beginning of that function. constraints gets screwed up if that function can't sanitize the input, so be very careful about that. It used to be pretty busted. --------------------------------------------------------------------------- Explanation of fields in the ConstraintInfo strut --------------------------------------------------------------------------- As of the time of this writing, ConstraintInfo had the following fields: orig current fgeom action_type is_user_action resize_gravity fixed_directions work_area_xinerama entire_xinerama usable_screen_region usable_xinerama_region A brief description of each and/or pointers to more information are found below: orig The previous position and size of the window, ignoring any window decorations current The requested position and size of the window, ignoring any window decorations. This rectangle gets modified by the various constraints to specify the allowed position closest to the requested position. fgeom The geometry of the window frame (i.e. "decorations"), if it exists. Otherwise, it's a dummy 0-size frame for convenience (i.e. this pointer is guaranteed to be non-NULL so you don't have to do the stupid check). action_type Whether the action being constrained is a move, resize, or a combined move and resize. Some constraints can run faster with this information (e.g. constraining size increment hints or min size hints don't need to do anything for pure move operations). This may also be used for providing slightly different behavior (e.g. clip-to-region instead of shove-into-region for resize vs. moving operations), but doesn't currently have a lot of use for this. is_user_action Used to determine whether the action being constrained is a user action. If so, certain parts of the constraint may be relaxed. Note that this requires care to get right; see item 4 of the basic ideas section for more details. resize_gravity The gravity used in the resize operation, used in order to make sure windows are resized correctly if constraints specify that their size must be modified. Explained further in the resize_gravity vs. fixed_directions section. fixed_directions There may be multiple solutions to shoving a window back onscreen. Typically, the shortest distance used is the solution picked, but if e.g. an application only moved its window in a single direction, it's more desirable that the window is shoved back in that direction than in a different one. fixed_directions facilitates that. Explained further in the resize_gravity vs. fixed_directions section. work_area_xinerama This region is defined in the workspace and just cached here for convenience. It is basically the area obtained by taking the current xinerama, treating all partial struts as full struts, and then subtracting all struts from the current xinerama region. Useful e.g. for enforcing maximization constraints. entire_xinerama Just a cache of the rectangle corresponding to the entire current xinerama, including struts. Useful e.g. for enforcing fullscreen constraints. usable_screen_region The set of maximal/spanning rectangles for the entire screen; this region doesn't overlap with any struts and helps to enforce e.g. onscreen constraints. usable_xinerama_region The set of maximal/spanning rectangles for the current xinerama; this region doesn't overlap with any struts on the xinerama and helps to enforce e.g. the on-single-xinerama constraint. --------------------------------------------------------------------------- Gory details of resize_gravity vs. fixed_directions --------------------------------------------------------------------------- Note that although resize_gravity and fixed_directions look similar, they are used for different purposes: - resize_gravity is only for resize operations and is used for constraints unrelated to keeping a window within a certain region - fixed_directions is for both move and resize operations and is specifically for keeping a window within a specified region. Examples of where each are used: - If a window is simultaneously moved and resized to the southeast corner with META_GRAVITY_SOUTH_EAST, but it turns out that the window was sized to something smaller than the minimum size hint, then the size_hints constraint should resize the window using the resize_gravity to ensure that the southeast corner doesn't move. - If an application resizes itself so that it grows downward only (which I note could be using any of three different gravities, most likely NorthWest), and happens to put the southeast part of the window under a partial strut, then the window needs to be forced back on screen. (Yes, shoved onscreen and not clipped; see bug 136307). It may be the case that moving the window to the left results in less movement of the window than moving the window up, which, in the absence of fixed directions would cause us to chose moving to the left. But since the user knows that only the height of the window is changing, they would find moving to the left weird (especially if this were a dialog that had been centered on its parent). It'd be better to shove the window upwards so we make sure to keep the left and right sides fixed in this case. Note that moving the window upwards (or leftwards) is probably totally against the gravity in this case; but that's okay because gravity typically assumes there's more than enough onscreen space for the resize and we only override the gravity when that assumption is wrong. For the paranoid, a fixed directions might give an impossible to fulfill constraint (I don't think that's true currently in the code, but I haven't thought it through in a while). If this ever becomes a problem, it should be relatively simple to throw out the fixed directions when this happens and rerun the constraint. Of course, it might be better to rethink things to just avoid such a problem. The nitty gritty of what gets fixed: User move: in x direction - y direction fixed in y direction - x direction fixed in both dirs. - neither direction fixed User resize: (note that for clipping, only 1 side ever changed) in x direction - y direction fixed (technically opposite x side fixed too) in y direction - x direction fixed (technically opposite y side fixed too) in both dirs. - neither direction fixed App move: in x direction - y direction fixed in y direction - x direction fixed in both dirs. - neither direction fixed App resize in x direction - y direction fixed in y direction - x direction fixed in 2 parallel directions (center side gravity) - other dir. fixed in 2 orthogonal directions (corner gravity) - neither dir. fixed in 3 or 4 directions (a center-like gravity) - neither dir. fixed Move & resize Treat like resize case though this will usually mean all four sides change and result in neither direction being fixed Note that in all cases, if neither direction moves it is likely do to a change in struts and thus neither direction should be fixed despite the lack of movement. muffin-6.4.1/meson_options.txt0000664000175000017500000000627514723361714015411 0ustar fabiofabiooption('opengl', type: 'boolean', value: true, description: 'Enable OpenGL' ) option('opengl_libname', type: 'string', value: 'libGL.so.1', description: 'OpenGL library file name' ) option('gles2_libname', type: 'string', value: 'libGLESv2.so.2', description: 'GLESv2 library file name' ) option('gles2', type: 'boolean', value: true, description: 'Enable GLES2 support' ) option('egl', type: 'boolean', value: true, description: 'Enable EGL support' ) option('glx', type: 'boolean', value: true, description: 'Enable GLX support' ) option('wayland', type: 'boolean', value: true, description: 'Enable Wayland support' ) option('native_backend', type: 'boolean', value: true, description: 'Enable the native backend' ) option('remote_desktop', type: 'boolean', value: true, description: 'Enable remote desktop and screen cast support' ) option('egl_device', type: 'boolean', value: false, description: 'Enable EGLDevice and EGLStream renderer support' ) option('wayland_eglstream', type: 'boolean', value: false, description: 'Enable Wayland EGLStream support client support' ) option('udev', type: 'boolean', value: true, description: 'Enable udev support when using the X11 backend' ) option('libwacom', type: 'boolean', value: true, description: 'Enable libwacom support' ) option('pango_ft2', type: 'boolean', value: true, description: 'Enable PangoFt2 support' ) option('startup_notification', type: 'boolean', value: true, description: 'Enable startup notification support' ) option('sm', type: 'boolean', value: true, description: 'Enable X11 session management support' ) option('introspection', type: 'boolean', value: true, description: 'Enable GObject introspection' ) option('cogl_tests', type: 'boolean', value: true, description: 'Enable cogl tests' ) option('clutter_tests', type: 'boolean', value: true, description: 'Enable clutter tests' ) option('core_tests', type: 'boolean', value: true, description: 'Enable mutter core tests' ) option('tests', type: 'boolean', value: false, description: 'Enable tests globally. Specific test suites can be controlled with core_tests, clutter_tests, and cogl_tests' ) option('profiler', type: 'boolean', value: false, description: 'Enable Sysprof tracing' ) option('installed_tests', type: 'boolean', value: false, description: 'Enable mutter installed tests' ) option('verbose', type: 'boolean', value: true, description: 'Enable verbose logging ability' ) option('default_driver', type: 'combo', choices: ['auto', 'gl', 'gl3', 'gles2', 'nop'], value: 'auto' ) option('xwayland_path', type: 'string', value: '', description: 'Path to Xwayland executable' ) option('xwayland_grab_default_access_rules', type: 'string', value: 'gnome-boxes,remote-viewer,virt-viewer,virt-manager,vinagre,vncviewer,Xephyr', description: 'Comma delimited list of applications ressources or class allowed to issue X11 grabs in Xwayland' ) option('xwayland_initfd', type: 'feature', value: 'auto', description: 'Whether -initfd argument is passed to Xwayland to guarantee services (e.g. gsd-xsettings) startup before applications' ) muffin-6.4.1/src/0000775000175000017500000000000014723361714012531 5ustar fabiofabiomuffin-6.4.1/src/org.freedesktop.login1.xml0000664000175000017500000000264214723361714017550 0ustar fabiofabio muffin-6.4.1/src/meson.build0000664000175000017500000007562514723361714014712 0ustar fabiofabiomutter_includesubdir = join_paths(pkgname, 'meta') mutter_includedir = join_paths(includedir, mutter_includesubdir) mutter_includes = [ include_directories('.'), top_includepath, clutter_includepath, cogl_includepath, ] mutter_lib_deps = [ m_dep, ] mutter_pkg_deps = [ cairo_dep, gio_unix_dep, glib_dep, cinnamon_desktop_dep, gtk3_dep, pango_dep, ] mutter_pkg_private_deps = [ gmodule_no_export_dep, cinnamon_desktop_dep, json_glib_dep, libcanberra_dep, xkbcommon_dep, ] if have_gl mutter_pkg_deps += [ gl_dep, ] endif if have_gles2 mutter_pkg_private_deps += [ gles2_dep, ] endif if have_egl mutter_pkg_deps += [ egl_dep, ] endif if have_libgudev mutter_pkg_private_deps += [ gudev_dep, libudev_dep, ] endif if have_startup_notification mutter_pkg_private_deps += [ libstartup_notification_dep, ] endif if have_libwacom mutter_pkg_private_deps += [ libwacom_dep, ] endif if have_remote_desktop mutter_pkg_private_deps += [ libpipewire_dep, ] endif if have_introspection mutter_pkg_private_deps += [ gobject_introspection_dep, ] endif if have_x11 mutter_pkg_deps += [ xfixes_dep, xi_dep, x11_dep, ] mutter_pkg_private_deps += [ xrandr_dep, xinerama_dep, xext_dep, ice_dep, xcomposite_dep, xcursor_dep, xdamage_dep, xkbfile_dep, xkeyboard_config_dep, xkbcommon_x11_dep, xrender_dep, x11_xcb_dep, xcb_randr_dep, xcb_res_dep, xau_dep, xtst_dep, ] if have_sm mutter_pkg_private_deps += [ sm_dep, ] endif endif if have_wayland mutter_pkg_deps += [ wayland_server_dep, ] endif if have_native_backend mutter_pkg_private_deps += [ libdrm_dep, libinput_dep, gudev_dep, libgbm_dep, logind_provider_dep, libudev_dep, xkbcommon_dep, ] endif if have_wayland_eglstream mutter_lib_deps += [ dl_dep, ] mutter_pkg_private_deps += [ wayland_eglstream_protocols_dep, ] endif mutter_deps = [ mutter_pkg_deps, mutter_pkg_private_deps, mutter_lib_deps, ] mutter_c_args = [ '-DCLUTTER_ENABLE_COMPOSITOR_API', '-DCLUTTER_ENABLE_EXPERIMENTAL_API', '-DCOGL_ENABLE_EXPERIMENTAL_API', '-DCOGL_ENABLE_EXPERIMENTAL_2_0_API', '-DCOGL_ENABLE_MUTTER_API', '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DG_LOG_DOMAIN="mutter"', '-DSN_API_NOT_YET_FROZEN=1', '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), ] if get_option('verbose') mutter_c_args += [ '-DWITH_VERBOSE_MODE' ] endif mutter_sources = [ 'backends/edid.h', 'backends/edid-parse.c', 'backends/gsm-inhibitor-flag.h', 'backends/meta-backend.c', 'backends/meta-backend-private.h', 'backends/meta-barrier.c', 'backends/meta-barrier-private.h', 'backends/meta-crtc.c', 'backends/meta-crtc.h', 'backends/meta-cursor.c', 'backends/meta-cursor.h', 'backends/meta-cursor-renderer.c', 'backends/meta-cursor-renderer.h', 'backends/meta-cursor-sprite-xcursor.c', 'backends/meta-cursor-sprite-xcursor.h', 'backends/meta-cursor-tracker.c', 'backends/meta-cursor-tracker-private.h', 'backends/meta-display-config-shared.h', 'backends/meta-dnd-private.h', 'backends/meta-gpu.c', 'backends/meta-gpu.h', 'backends/meta-idle-monitor.c', 'backends/meta-idle-monitor-dbus.c', 'backends/meta-idle-monitor-dbus.h', 'backends/meta-idle-monitor-private.h', 'backends/meta-input-device.c', 'backends/meta-input-mapper.c', 'backends/meta-input-mapper-private.h', 'backends/meta-input-settings.c', 'backends/meta-input-settings-private.h', 'backends/meta-logical-monitor.c', 'backends/meta-logical-monitor.h', 'backends/meta-monitor.c', 'backends/meta-monitor-config-manager.c', 'backends/meta-monitor-config-manager.h', 'backends/meta-monitor-config-migration.c', 'backends/meta-monitor-config-migration.h', 'backends/meta-monitor-config-store.c', 'backends/meta-monitor-config-store.h', 'backends/meta-monitor.h', 'backends/meta-monitor-manager.c', 'backends/meta-monitor-manager-dummy.c', 'backends/meta-monitor-manager-dummy.h', 'backends/meta-monitor-manager-private.h', 'backends/meta-monitor-transform.c', 'backends/meta-monitor-transform.h', 'backends/meta-orientation-manager.c', 'backends/meta-orientation-manager.h', 'backends/meta-output.c', 'backends/meta-output.h', 'backends/meta-pointer-constraint.c', 'backends/meta-pointer-constraint.h', 'backends/meta-remote-access-controller-private.h', 'backends/meta-remote-access-controller.c', 'backends/meta-renderer.c', 'backends/meta-renderer.h', 'backends/meta-renderer-view.c', 'backends/meta-renderer-view.h', 'backends/meta-screen-cast-window.c', 'backends/meta-screen-cast-window.h', 'backends/meta-settings.c', 'backends/meta-settings-private.h', 'backends/meta-stage.c', 'backends/meta-stage-private.h', 'backends/x11/cm/meta-backend-x11-cm.c', 'backends/x11/cm/meta-backend-x11-cm.h', 'backends/x11/cm/meta-cursor-sprite-xfixes.c', 'backends/x11/cm/meta-cursor-sprite-xfixes.h', 'backends/x11/cm/meta-renderer-x11-cm.c', 'backends/x11/cm/meta-renderer-x11-cm.h', 'backends/x11/meta-backend-x11.c', 'backends/x11/meta-backend-x11.h', 'backends/x11/meta-barrier-x11.c', 'backends/x11/meta-barrier-x11.h', 'backends/x11/meta-clutter-backend-x11.c', 'backends/x11/meta-clutter-backend-x11.h', 'backends/x11/meta-crtc-xrandr.c', 'backends/x11/meta-crtc-xrandr.h', 'backends/x11/meta-cursor-renderer-x11.c', 'backends/x11/meta-cursor-renderer-x11.h', 'backends/x11/meta-event-x11.c', 'backends/x11/meta-event-x11.h', 'backends/x11/meta-gpu-xrandr.c', 'backends/x11/meta-gpu-xrandr.h', 'backends/x11/meta-input-device-x11.c', 'backends/x11/meta-input-device-x11.h', 'backends/x11/meta-input-device-tool-x11.c', 'backends/x11/meta-input-device-tool-x11.h', 'backends/x11/meta-input-settings-x11.c', 'backends/x11/meta-input-settings-x11.h', 'backends/x11/meta-seat-x11.c', 'backends/x11/meta-seat-x11.h', 'backends/x11/meta-keymap-x11.c', 'backends/x11/meta-keymap-x11.h', 'backends/x11/meta-monitor-manager-xrandr.c', 'backends/x11/meta-monitor-manager-xrandr.h', 'backends/x11/meta-output-xrandr.c', 'backends/x11/meta-output-xrandr.h', 'backends/x11/meta-renderer-x11.c', 'backends/x11/meta-renderer-x11.h', 'backends/x11/meta-stage-x11.c', 'backends/x11/meta-stage-x11.h', 'backends/x11/meta-virtual-input-device-x11.c', 'backends/x11/meta-virtual-input-device-x11.h', 'backends/x11/meta-xkb-a11y-x11.c', 'backends/x11/meta-xkb-a11y-x11.h', 'backends/x11/nested/meta-backend-x11-nested.c', 'backends/x11/nested/meta-backend-x11-nested.h', 'backends/x11/nested/meta-cursor-renderer-x11-nested.c', 'backends/x11/nested/meta-cursor-renderer-x11-nested.h', 'backends/x11/nested/meta-stage-x11-nested.c', 'backends/x11/nested/meta-stage-x11-nested.h', 'backends/x11/nested/meta-renderer-x11-nested.c', 'backends/x11/nested/meta-renderer-x11-nested.h', 'compositor/clutter-utils.c', 'compositor/clutter-utils.h', 'compositor/cogl-utils.c', 'compositor/cogl-utils.h', 'compositor/compositor.c', 'compositor/compositor-private.h', 'compositor/meta-background-actor.c', 'compositor/meta-background-actor-private.h', 'compositor/meta-background.c', 'compositor/meta-background-group.c', 'compositor/meta-background-image.c', 'compositor/meta-background-private.h', 'compositor/meta-compositor-x11.c', 'compositor/meta-compositor-x11.h', 'compositor/meta-cullable.c', 'compositor/meta-cullable.h', 'compositor/meta-dnd-actor.c', 'compositor/meta-dnd-actor-private.h', 'compositor/meta-dnd.c', 'compositor/meta-feedback-actor.c', 'compositor/meta-feedback-actor-private.h', 'compositor/meta-module.c', 'compositor/meta-module.h', 'compositor/meta-plugin.c', 'compositor/meta-plugin-manager.c', 'compositor/meta-plugin-manager.h', 'compositor/meta-shadow-factory.c', 'compositor/meta-shaped-texture.c', 'compositor/meta-shaped-texture-private.h', 'compositor/meta-surface-actor.c', 'compositor/meta-surface-actor.h', 'compositor/meta-surface-actor-x11.c', 'compositor/meta-surface-actor-x11.h', 'compositor/meta-sync-ring.c', 'compositor/meta-sync-ring.h', 'compositor/meta-texture-tower.c', 'compositor/meta-texture-tower.h', 'compositor/meta-window-actor.c', 'compositor/meta-window-actor-private.h', 'compositor/meta-window-actor-x11.c', 'compositor/meta-window-actor-x11.h', 'compositor/meta-window-group.c', 'compositor/meta-window-group-private.h', 'compositor/meta-window-shape.c', 'compositor/region-utils.c', 'compositor/region-utils.h', 'core/bell.c', 'core/bell.h', 'core/boxes.c', 'core/boxes-private.h', 'core/constraints.c', 'core/constraints.h', 'core/delete.c', 'core/display.c', 'core/display-private.h', 'core/edge-resistance.c', 'core/edge-resistance.h', 'core/events.c', 'core/events.h', 'core/frame.c', 'core/frame.h', 'core/keybindings.c', 'core/keybindings-private.h', 'core/main.c', 'core/main-private.h', 'core/meta-accel-parse.c', 'core/meta-accel-parse.h', 'core/meta-border.c', 'core/meta-border.h', 'core/meta-clipboard-manager.c', 'core/meta-clipboard-manager.h', 'core/meta-close-dialog.c', 'core/meta-close-dialog-default.c', 'core/meta-close-dialog-default-private.h', 'core/meta-fraction.c', 'core/meta-fraction.h', 'core/meta-gesture-tracker.c', 'core/meta-gesture-tracker-private.h', 'core/meta-inhibit-shortcuts-dialog.c', 'core/meta-inhibit-shortcuts-dialog-default.c', 'core/meta-inhibit-shortcuts-dialog-default-private.h', 'core/meta-launch-context.c', 'core/meta-selection.c', 'core/meta-selection-source.c', 'core/meta-selection-source-memory.c', 'core/meta-sound-player.c', 'core/meta-workspace-manager.c', 'core/meta-workspace-manager-private.h', 'core/place.c', 'core/place.h', 'core/prefs.c', 'core/restart.c', 'core/stack.c', 'core/stack.h', 'core/stack-tracker.c', 'core/stack-tracker.h', 'core/startup-notification.c', 'core/startup-notification-private.h', 'core/util.c', 'core/util-private.h', 'core/window.c', 'core/window-private.h', 'core/workspace.c', 'core/workspace-private.h', 'ui/frames.c', 'ui/frames.h', 'ui/theme.c', 'ui/theme-private.h', 'ui/ui.c', 'ui/ui.h', 'x11/atomnames.h', 'x11/events.c', 'x11/events.h', 'x11/group.c', 'x11/group-private.h', 'x11/group-props.c', 'x11/group-props.h', 'x11/iconcache.c', 'x11/iconcache.h', 'x11/meta-selection-source-x11.c', 'x11/meta-selection-source-x11-private.h', 'x11/meta-startup-notification-x11.c', 'x11/meta-startup-notification-x11.h', 'x11/meta-x11-background-actor.c', 'x11/meta-x11-background-actor-private.h', 'x11/meta-x11-background.c', 'x11/meta-x11-background.h', 'x11/meta-x11-display.c', 'x11/meta-x11-display-private.h', 'x11/meta-x11-errors.c', 'x11/meta-x11-selection.c', 'x11/meta-x11-selection-private.h', 'x11/meta-x11-selection-input-stream.c', 'x11/meta-x11-selection-input-stream-private.h', 'x11/meta-x11-selection-output-stream.c', 'x11/meta-x11-selection-output-stream-private.h', 'x11/meta-x11-stack.c', 'x11/meta-x11-stack-private.h', 'x11/meta-x11-window-control.c', 'x11/meta-x11-window-control.h', 'x11/mutter-Xatomtype.h', 'x11/session.c', 'x11/session.h', 'x11/window-props.c', 'x11/window-props.h', 'x11/window-x11.c', 'x11/window-x11.h', 'x11/window-x11-private.h', 'x11/xprops.c', 'x11/xprops.h', ] if have_egl mutter_sources += [ 'backends/meta-egl.c', 'backends/meta-egl-ext.h', 'backends/meta-egl.h', ] endif if have_gles2 mutter_sources += [ 'backends/meta-gles3.c', 'backends/meta-gles3.h', 'backends/meta-gles3-table.h', ] endif if have_remote_desktop mutter_sources += [ 'backends/meta-dbus-session-watcher.c', 'backends/meta-dbus-session-watcher.h', 'backends/meta-remote-desktop.c', 'backends/meta-remote-desktop.h', 'backends/meta-remote-desktop-session.c', 'backends/meta-remote-desktop-session.h', 'backends/meta-screen-cast.c', 'backends/meta-screen-cast.h', 'backends/meta-screen-cast-monitor-stream.c', 'backends/meta-screen-cast-monitor-stream.h', 'backends/meta-screen-cast-monitor-stream-src.c', 'backends/meta-screen-cast-monitor-stream-src.h', 'backends/meta-screen-cast-window-stream-src.c', 'backends/meta-screen-cast-window-stream-src.h', 'backends/meta-screen-cast-window-stream.c', 'backends/meta-screen-cast-window-stream.h', 'backends/meta-screen-cast-session.c', 'backends/meta-screen-cast-session.h', 'backends/meta-screen-cast-stream.c', 'backends/meta-screen-cast-stream.h', 'backends/meta-screen-cast-stream-src.c', 'backends/meta-screen-cast-stream-src.h', ] endif if have_wayland mutter_sources += [ 'compositor/meta-surface-actor-wayland.c', 'compositor/meta-surface-actor-wayland.h', 'compositor/meta-window-actor-wayland.c', 'compositor/meta-window-actor-wayland.h', 'compositor/meta-compositor-server.c', 'compositor/meta-compositor-server.h', 'wayland/meta-cursor-sprite-wayland.c', 'wayland/meta-cursor-sprite-wayland.h', 'wayland/meta-pointer-confinement-wayland.c', 'wayland/meta-pointer-confinement-wayland.h', 'wayland/meta-pointer-lock-wayland.c', 'wayland/meta-pointer-lock-wayland.h', 'wayland/meta-selection-source-wayland.c', 'wayland/meta-selection-source-wayland-private.h', 'wayland/meta-wayland-actor-surface.c', 'wayland/meta-wayland-actor-surface.h', 'wayland/meta-wayland-buffer.c', 'wayland/meta-wayland-buffer.h', 'wayland/meta-wayland.c', 'wayland/meta-wayland-cursor-surface.c', 'wayland/meta-wayland-cursor-surface.h', 'wayland/meta-wayland-data-device.c', 'wayland/meta-wayland-data-device.h', 'wayland/meta-wayland-data-device-primary.c', 'wayland/meta-wayland-data-device-primary.h', 'wayland/meta-wayland-data-device-primary-legacy.c', 'wayland/meta-wayland-data-device-primary-legacy.h', 'wayland/meta-wayland-data-offer.c', 'wayland/meta-wayland-data-offer.h', 'wayland/meta-wayland-data-offer-primary.c', 'wayland/meta-wayland-data-offer-primary.h', 'wayland/meta-wayland-data-offer-primary-legacy.c', 'wayland/meta-wayland-data-offer-primary-legacy.h', 'wayland/meta-wayland-data-source.c', 'wayland/meta-wayland-data-source.h', 'wayland/meta-wayland-data-source-primary.c', 'wayland/meta-wayland-data-source-primary.h', 'wayland/meta-wayland-data-source-primary-legacy.c', 'wayland/meta-wayland-data-source-primary-legacy.h', 'wayland/meta-wayland-dma-buf.c', 'wayland/meta-wayland-dma-buf.h', 'wayland/meta-wayland-dnd-surface.c', 'wayland/meta-wayland-dnd-surface.h', 'wayland/meta-wayland-gtk-shell.c', 'wayland/meta-wayland-gtk-shell.h', 'wayland/meta-wayland.h', 'wayland/meta-wayland-inhibit-shortcuts.c', 'wayland/meta-wayland-inhibit-shortcuts-dialog.c', 'wayland/meta-wayland-inhibit-shortcuts-dialog.h', 'wayland/meta-wayland-inhibit-shortcuts.h', 'wayland/meta-wayland-input-device.c', 'wayland/meta-wayland-input-device.h', 'wayland/meta-wayland-keyboard.c', 'wayland/meta-wayland-keyboard.h', 'wayland/meta-wayland-legacy-xdg-shell.c', 'wayland/meta-wayland-legacy-xdg-shell.h', 'wayland/meta-wayland-outputs.c', 'wayland/meta-wayland-outputs.h', 'wayland/meta-wayland-pointer.c', 'wayland/meta-wayland-pointer-constraints.c', 'wayland/meta-wayland-pointer-constraints.h', 'wayland/meta-wayland-pointer-gesture-pinch.c', 'wayland/meta-wayland-pointer-gesture-pinch.h', 'wayland/meta-wayland-pointer-gestures.c', 'wayland/meta-wayland-pointer-gestures.h', 'wayland/meta-wayland-pointer-gesture-swipe.c', 'wayland/meta-wayland-pointer-gesture-swipe.h', 'wayland/meta-wayland-pointer.h', 'wayland/meta-wayland-popup.c', 'wayland/meta-wayland-popup.h', 'wayland/meta-wayland-private.h', 'wayland/meta-wayland-region.c', 'wayland/meta-wayland-region.h', 'wayland/meta-wayland-seat.c', 'wayland/meta-wayland-seat.h', 'wayland/meta-wayland-shell-surface.c', 'wayland/meta-wayland-shell-surface.h', 'wayland/meta-wayland-subsurface.c', 'wayland/meta-wayland-subsurface.h', 'wayland/meta-wayland-surface.c', 'wayland/meta-wayland-surface.h', 'wayland/meta-wayland-tablet.c', 'wayland/meta-wayland-tablet-cursor-surface.c', 'wayland/meta-wayland-tablet-cursor-surface.h', 'wayland/meta-wayland-tablet.h', 'wayland/meta-wayland-tablet-manager.c', 'wayland/meta-wayland-tablet-manager.h', 'wayland/meta-wayland-tablet-pad.c', 'wayland/meta-wayland-tablet-pad-group.c', 'wayland/meta-wayland-tablet-pad-group.h', 'wayland/meta-wayland-tablet-pad.h', 'wayland/meta-wayland-tablet-pad-ring.c', 'wayland/meta-wayland-tablet-pad-ring.h', 'wayland/meta-wayland-tablet-pad-strip.c', 'wayland/meta-wayland-tablet-pad-strip.h', 'wayland/meta-wayland-tablet-seat.c', 'wayland/meta-wayland-tablet-seat.h', 'wayland/meta-wayland-tablet-tool.c', 'wayland/meta-wayland-tablet-tool.h', 'wayland/meta-wayland-text-input.c', 'wayland/meta-wayland-text-input.h', 'wayland/meta-wayland-text-input-legacy.c', 'wayland/meta-wayland-text-input-legacy.h', 'wayland/meta-wayland-touch.c', 'wayland/meta-wayland-touch.h', 'wayland/meta-wayland-types.h', 'wayland/meta-wayland-versions.h', 'wayland/meta-wayland-viewporter.c', 'wayland/meta-wayland-viewporter.h', 'wayland/meta-wayland-window-configuration.c', 'wayland/meta-wayland-window-configuration.h', 'wayland/meta-wayland-wl-shell.c', 'wayland/meta-wayland-wl-shell.h', 'wayland/meta-wayland-xdg-foreign.c', 'wayland/meta-wayland-xdg-foreign.h', 'wayland/meta-wayland-xdg-shell.c', 'wayland/meta-wayland-xdg-shell.h', 'wayland/meta-window-wayland.c', 'wayland/meta-window-wayland.h', 'wayland/meta-window-xwayland.c', 'wayland/meta-window-xwayland.h', 'wayland/meta-xwayland.c', 'wayland/meta-xwayland-grab-keyboard.c', 'wayland/meta-xwayland-grab-keyboard.h', 'wayland/meta-xwayland.h', 'wayland/meta-xwayland-private.h', 'wayland/meta-xwayland-dnd.c', 'wayland/meta-xwayland-dnd-private.h', 'wayland/meta-xwayland-surface.c', 'wayland/meta-xwayland-surface.h', ] endif if have_native_backend mutter_sources += [ 'backends/native/dbus-utils.c', 'backends/native/dbus-utils.h', 'backends/native/meta-backend-native.c', 'backends/native/meta-backend-native.h', 'backends/native/meta-backend-native-private.h', 'backends/native/meta-backend-native-types.h', 'backends/native/meta-barrier-native.c', 'backends/native/meta-barrier-native.h', 'backends/native/meta-clutter-backend-native.c', 'backends/native/meta-clutter-backend-native.h', 'backends/native/meta-crtc-kms.c', 'backends/native/meta-crtc-kms.h', 'backends/native/meta-cursor-renderer-native.c', 'backends/native/meta-cursor-renderer-native.h', 'backends/native/meta-drm-buffer-dumb.c', 'backends/native/meta-drm-buffer-dumb.h', 'backends/native/meta-drm-buffer-gbm.c', 'backends/native/meta-drm-buffer-gbm.h', 'backends/native/meta-drm-buffer-import.c', 'backends/native/meta-drm-buffer-import.h', 'backends/native/meta-drm-buffer.c', 'backends/native/meta-drm-buffer.h', 'backends/native/meta-event-native.c', 'backends/native/meta-event-native.h', 'backends/native/meta-gpu-kms.c', 'backends/native/meta-gpu-kms.h', 'backends/native/meta-input-device-native.c', 'backends/native/meta-input-device-native.h', 'backends/native/meta-input-device-tool-native.c', 'backends/native/meta-input-device-tool-native.h', 'backends/native/meta-input-settings-native.c', 'backends/native/meta-input-settings-native.h', 'backends/native/meta-keymap-native.c', 'backends/native/meta-keymap-native.h', 'backends/native/meta-launcher.c', 'backends/native/meta-launcher.h', 'backends/native/meta-monitor-manager-kms.c', 'backends/native/meta-monitor-manager-kms.h', 'backends/native/meta-output-kms.c', 'backends/native/meta-output-kms.h', 'backends/native/meta-renderer-native.c', 'backends/native/meta-kms-connector-private.h', 'backends/native/meta-kms-connector.c', 'backends/native/meta-kms-connector.h', 'backends/native/meta-kms-crtc-private.h', 'backends/native/meta-kms-crtc.c', 'backends/native/meta-kms-crtc.h', 'backends/native/meta-kms-device-private.h', 'backends/native/meta-kms-device.c', 'backends/native/meta-kms-device.h', 'backends/native/meta-kms-impl-device.c', 'backends/native/meta-kms-impl-device.h', 'backends/native/meta-kms-impl-simple.c', 'backends/native/meta-kms-impl-simple.h', 'backends/native/meta-kms-impl.c', 'backends/native/meta-kms-impl.h', 'backends/native/meta-kms-page-flip.c', 'backends/native/meta-kms-page-flip-private.h', 'backends/native/meta-kms-plane.c', 'backends/native/meta-kms-plane-private.h', 'backends/native/meta-kms-plane.h', 'backends/native/meta-kms-private.h', 'backends/native/meta-kms-types.h', 'backends/native/meta-kms-update-private.h', 'backends/native/meta-kms-update.c', 'backends/native/meta-kms-update.h', 'backends/native/meta-kms-utils.c', 'backends/native/meta-kms-utils.h', 'backends/native/meta-kms.c', 'backends/native/meta-kms.h', 'backends/native/meta-renderer-native-gles3.c', 'backends/native/meta-renderer-native-gles3.h', 'backends/native/meta-renderer-native.h', 'backends/native/meta-seat-native.c', 'backends/native/meta-seat-native.h', 'backends/native/meta-stage-native.c', 'backends/native/meta-stage-native.h', 'backends/native/meta-udev.c', 'backends/native/meta-udev.h', 'backends/native/meta-virtual-input-device-native.c', 'backends/native/meta-virtual-input-device-native.h', 'backends/native/meta-xkb-utils.c', 'backends/native/meta-xkb-utils.h', ] endif if have_wayland_eglstream mutter_sources += [ 'wayland/meta-wayland-egl-stream.c', 'wayland/meta-wayland-egl-stream.h', ] endif mutter_built_sources = [] dbus_display_config_built_sources = gnome.gdbus_codegen('meta-dbus-display-config', 'org.cinnamon.Muffin.DisplayConfig.xml', interface_prefix: 'org.cinnamon.Muffin.', namespace: 'MetaDBus', ) mutter_built_sources += dbus_display_config_built_sources dbus_idle_monitor_built_sources = gnome.gdbus_codegen('meta-dbus-idle-monitor', 'org.cinnamon.Muffin.IdleMonitor.xml', interface_prefix: 'org.cinnamon.Muffin.', namespace: 'MetaDBus', object_manager: true, ) mutter_built_sources += dbus_idle_monitor_built_sources mutter_marshal = gnome.genmarshal('meta-marshal', sources: ['meta-marshal.list'], prefix: 'meta_marshal', internal: true, valist_marshallers: true, ) mutter_built_sources += mutter_marshal if have_profiler mutter_sources += [ 'backends/meta-profiler.c', 'backends/meta-profiler.h', ] sysprof_dbus_interfaces_dir = join_paths(sysprof_dep.get_variable(pkgconfig: 'datadir'), 'dbus-1', 'interfaces') sysprof3_dbus_file = join_paths(sysprof_dbus_interfaces_dir, 'org.gnome.Sysprof3.Profiler.xml') dbus_sysprof3_profiler_built_sources = gnome.gdbus_codegen('meta-dbus-sysprof3-profiler', sysprof3_dbus_file, interface_prefix: 'org.gnome.', namespace: 'MetaDBus', ) mutter_built_sources += dbus_sysprof3_profiler_built_sources endif if have_native_backend cvt = find_program('cvt') gen_default_modes = find_program('backends/native/gen-default-modes.py') default_modes_h = custom_target('meta-default-modes', output: 'meta-default-modes.h', command: [gen_default_modes, '@OUTPUT@'] ) mutter_built_sources += default_modes_h gdbus_codegen = find_program('gdbus-codegen') dbus_login1_built_sources = custom_target('meta-dbus-login1', input: 'org.freedesktop.login1.xml', output: [ 'meta-dbus-login1.c', 'meta-dbus-login1.h', ], command: [ gdbus_codegen, '--interface-prefix', 'org.freedesktop.login1', '--c-namespace', 'Login1', '--generate-c-code', 'meta-dbus-login1', '--output-directory', meson.current_build_dir(), '--c-generate-autocleanup', 'all', '@INPUT@', ] ) mutter_built_sources += dbus_login1_built_sources endif if have_remote_desktop dbus_remote_desktop_built_sources = gnome.gdbus_codegen('meta-dbus-remote-desktop', 'org.cinnamon.Muffin.RemoteDesktop.xml', interface_prefix: 'org.cinnamon.Muffin.', namespace: 'MetaDBus', ) mutter_built_sources += dbus_remote_desktop_built_sources dbus_screen_cast_built_sources = gnome.gdbus_codegen('meta-dbus-screen-cast', 'org.cinnamon.Muffin.ScreenCast.xml', interface_prefix: 'org.cinnamon.Muffin.', namespace: 'MetaDBus', ) mutter_built_sources += dbus_screen_cast_built_sources endif wayland_protocol_server_headers = [] wayland_protocol_client_headers = [] wayland_protocol_sources = [] if have_wayland # Format: # - protocol name # - protocol stability ('private', 'stable' or 'unstable') # - protocol version (if stability is 'unstable') wayland_protocols = [ ['gtk-primary-selection', 'private', ], ['gtk-shell', 'private', ], ['gtk-text-input', 'private', ], ['keyboard-shortcuts-inhibit', 'unstable', 'v1', ], ['linux-dmabuf', 'unstable', 'v1', ], ['pointer-constraints', 'unstable', 'v1', ], ['pointer-gestures', 'unstable', 'v1', ], ['primary-selection', 'unstable', 'v1', ], ['relative-pointer', 'unstable', 'v1', ], ['tablet', 'unstable', 'v2', ], ['text-input', 'unstable', 'v3', ], ['viewporter', 'stable', ], ['xdg-foreign', 'unstable', 'v1', ], ['xdg-output', 'unstable', 'v1', ], ['xdg-shell', 'unstable', 'v6', ], ['xdg-shell', 'stable', ], ['xwayland-keyboard-grab', 'unstable', 'v1', ], ] if have_wayland_eglstream wayland_eglstream_protocols_dir = wayland_eglstream_protocols_dep.get_variable(pkgconfig: 'pkgdatadir') wayland_protocols += [ ['wayland-eglstream-controller', 'third-party', wayland_eglstream_protocols_dir], ] endif wayland_scanner = find_program('wayland-scanner') protocols_dir = wayland_protocols_dep.get_variable(pkgconfig: 'pkgdatadir') assert(protocols_dir != '', 'Could not get pkgdatadir from wayland-protocols.pc') foreach p: wayland_protocols protocol_name = p.get(0) protocol_type = p.get(1) if protocol_type == 'stable' output_base = protocol_name input = join_paths(protocols_dir, '@0@/@1@/@2@.xml'.format(protocol_type, protocol_name, output_base)) elif protocol_type == 'private' output_base = protocol_name input = 'wayland/protocol/@0@.xml'.format(protocol_name) elif protocol_type == 'third-party' output_base = protocol_name protocol_dir = p.get(2) input = join_paths(protocol_dir, '@0@.xml'.format(protocol_name)) else protocol_version = p.get(2) output_base = '@0@-@1@-@2@'.format(protocol_name, protocol_type, protocol_version) input = join_paths(protocols_dir, '@0@/@1@/@2@.xml'.format(protocol_type, protocol_name, output_base)) endif wayland_protocol_server_headers += custom_target('@0@ server header'.format(output_base), input: input, output: '@0@-server-protocol.h'.format(output_base), command: [ wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@', ] ) # used by tests wayland_protocol_client_headers += custom_target('@0@ client header'.format(output_base), input: input, output: '@0@-client-protocol.h'.format(output_base), command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@', ] ) wayland_protocol_sources += custom_target('@0@ source'.format(output_base), input: input, output: '@0@-protocol.c'.format(output_base), command: [ wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@', ] ) endforeach endif mutter_built_sources += wayland_protocol_server_headers mutter_built_sources += wayland_protocol_sources subdir('meta') mutter_built_sources += mutter_enum_types mutter_built_sources += mutter_version libmutter = shared_library(libmutter_name, sources: [ mutter_sources, mutter_built_sources, ], version: '0.0.0', soversion: 0, gnu_symbol_visibility: 'hidden', include_directories: mutter_includes, c_args: mutter_c_args, dependencies: [ libmutter_cogl_dep, libmutter_clutter_dep, mutter_deps, ], install_rpath: pkglibdir, install_dir: libdir, install: true, ) libmutter_dep = declare_dependency( link_with: libmutter, include_directories: mutter_includes, sources: mutter_built_sources, dependencies: [ libmutter_cogl_dep, libmutter_clutter_dep, mutter_deps, ], ) executable('muffin', sources: [ files('core/mutter.c'), ], include_directories: mutter_includes, c_args: mutter_c_args, dependencies: [libmutter_dep], install_dir: bindir, install: true, ) executable('muffin-restart-helper', sources: [ files('core/restart-helper.c'), ], include_directories: [ top_includepath, ], c_args: mutter_c_args, dependencies: [ x11_dep, xcomposite_dep, ], install_dir: libexecdir, install: true, ) if have_introspection mutter_introspected_sources = [] foreach source : mutter_sources if source.endswith('.c') mutter_introspected_sources += source endif endforeach libmutter_gir = gnome.generate_gir(libmutter, sources: [ mutter_version, mutter_enum_types[1], mutter_introspected_sources, mutter_public_header_files ], nsversion: libmutter_api_version, namespace: 'Meta', symbol_prefix: 'meta', includes: [ 'GObject-2.0', 'CDesktopEnums-3.0', 'Gdk-3.0', 'Gtk-3.0', 'xlib-2.0', 'xfixes-4.0', libmutter_cogl_gir[0], libmutter_cogl_pango_gir[0], libmutter_clutter_gir[0], ], dependencies: [ mutter_deps, libmutter_dep, ], extra_args: mutter_c_args + introspection_args, install_dir_gir: pkglibdir, install_dir_typelib: pkglibdir, install: true ) endif pkg.generate(libmutter, name: 'Meta', filebase: 'libmuffin-' + libmutter_api_version, description: 'Muffin compositor and window manager library', subdirs: pkgname, requires: [mutter_pkg_deps, libmutter_clutter_name], version: meson.project_version(), variables: [ 'apiversion=' + libmutter_api_version, 'girdir=${libdir}/muffin', 'typelibdir=${libdir}/muffin', ], install_dir: pcdir, ) subdir('compositor/plugins') if have_core_tests subdir('tests') endif muffin-6.4.1/src/org.cinnamon.Muffin.RemoteDesktop.xml0000664000175000017500000001101714723361714021652 0ustar fabiofabio muffin-6.4.1/src/org.cinnamon.Muffin.IdleMonitor.xml0000664000175000017500000000175014723361714021315 0ustar fabiofabio muffin-6.4.1/src/meta-marshal.list0000664000175000017500000000002114723361714015772 0ustar fabiofabioVOID:FLOAT,FLOAT muffin-6.4.1/src/core/0000775000175000017500000000000014723361714013461 5ustar fabiofabiomuffin-6.4.1/src/core/workspace-private.h0000664000175000017500000000717714723361714017314 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file workspace.h Workspaces * * A workspace is a set of windows which all live on the same * screen. (You may also see the name "desktop" around the place, * which is the EWMH's name for the same thing.) Only one workspace * of a screen may be active at once; all windows on all other workspaces * are unmapped. */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2004, 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WORKSPACE_PRIVATE_H #define META_WORKSPACE_PRIVATE_H #include "core/window-private.h" #include "meta/workspace.h" struct _MetaWorkspace { GObject parent_instance; MetaDisplay *display; MetaWorkspaceManager *manager; GList *windows; /* The "MRU list", or "most recently used" list, is a list of * MetaWindows ordered based on the time the the user interacted * with the window most recently. * * For historical reasons, we keep an MRU list per workspace. * It used to be used to calculate the default focused window, * but isn't anymore, as the window next in the stacking order * can sometimes be not the window the user interacted with last, */ GList *mru_list; GList *list_containing_self; GHashTable *logical_monitor_data; MetaRectangle work_area_screen; GList *screen_region; GList *screen_edges; GList *monitor_edges; GSList *builtin_struts; GSList *all_struts; guint work_areas_invalid : 1; guint showing_desktop : 1; }; struct _MetaWorkspaceClass { GObjectClass parent_class; }; MetaWorkspace* meta_workspace_new (MetaWorkspaceManager *workspace_manager); void meta_workspace_remove (MetaWorkspace *workspace); void meta_workspace_add_window (MetaWorkspace *workspace, MetaWindow *window); void meta_workspace_remove_window (MetaWorkspace *workspace, MetaWindow *window); void meta_workspace_relocate_windows (MetaWorkspace *workspace, MetaWorkspace *new_home); void meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor, MetaRectangle *area); void meta_workspace_invalidate_work_area (MetaWorkspace *workspace); GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace); GList * meta_workspace_get_onmonitor_region (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor); void meta_workspace_focus_default_window (MetaWorkspace *workspace, MetaWindow *not_this_one, guint32 timestamp); const char* meta_workspace_get_name (MetaWorkspace *workspace); void meta_workspace_index_changed (MetaWorkspace *workspace); #endif muffin-6.4.1/src/core/meta-workspace-manager-private.h0000664000175000017500000000677414723361714021652 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WORKSPACE_MANAGER_PRIVATE_H #define META_WORKSPACE_MANAGER_PRIVATE_H #include #include "core/display-private.h" #include "meta/common.h" #include "meta/types.h" #include "meta/meta-workspace-manager.h" struct _MetaWorkspaceManager { GObject parent; MetaDisplay *display; MetaWorkspace *active_workspace; GList *workspaces; int rows_of_workspaces; int columns_of_workspaces; MetaDisplayCorner starting_corner; guint vertical_workspaces : 1; guint workspace_layout_overridden : 1; }; MetaWorkspaceManager *meta_workspace_manager_new (MetaDisplay *display); void meta_workspace_manager_init_workspaces (MetaWorkspaceManager *workspace_manager); void meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_manager, MetaDisplayCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns); void meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manager); typedef struct MetaWorkspaceLayout MetaWorkspaceLayout; struct MetaWorkspaceLayout { int rows; int cols; int *grid; int grid_area; int current_row; int current_col; }; void meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_manager, int num_workspaces, int current_space, MetaWorkspaceLayout *layout); void meta_workspace_manager_free_workspace_layout (MetaWorkspaceLayout *layout); void meta_workspace_manager_minimize_all_on_active_workspace_except (MetaWorkspaceManager *workspace_manager, MetaWindow *keep); void meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manager, int from, int to, MetaMotionDirection direction); void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager, guint32 timestamp, int new_num); #endif /* META_WORKSPACE_MANAGER_PRIVATE_H */ muffin-6.4.1/src/core/meta-gesture-tracker.c0000664000175000017500000004177514723361714017676 0ustar fabiofabio/* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ /** * SECTION:gesture-tracker * @Title: MetaGestureTracker * @Short_Description: Manages gestures on windows/desktop * * Forwards touch events to clutter actors, and accepts/rejects touch sequences * based on the outcome of those. */ #include "config.h" #include "core/meta-gesture-tracker-private.h" #include "compositor/meta-surface-actor.h" #define DISTANCE_THRESHOLD 30 typedef struct _MetaGestureTrackerPrivate MetaGestureTrackerPrivate; typedef struct _GestureActionData GestureActionData; typedef struct _MetaSequenceInfo MetaSequenceInfo; struct _MetaSequenceInfo { MetaGestureTracker *tracker; ClutterEventSequence *sequence; MetaSequenceState state; guint autodeny_timeout_id; gfloat start_x; gfloat start_y; }; struct _GestureActionData { ClutterGestureAction *gesture; MetaSequenceState state; gulong gesture_begin_id; gulong gesture_end_id; gulong gesture_cancel_id; }; struct _MetaGestureTrackerPrivate { GHashTable *sequences; /* Hashtable of ClutterEventSequence->MetaSequenceInfo */ MetaSequenceState stage_state; GArray *stage_gestures; /* Array of GestureActionData */ GList *listeners; /* List of ClutterGestureAction */ guint autodeny_timeout; }; enum { PROP_0, PROP_AUTODENY_TIMEOUT, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; enum { STATE_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; #define DEFAULT_AUTODENY_TIMEOUT 150 static void meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker); G_DEFINE_TYPE_WITH_PRIVATE (MetaGestureTracker, meta_gesture_tracker, G_TYPE_OBJECT) static void meta_gesture_tracker_finalize (GObject *object) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); g_hash_table_destroy (priv->sequences); g_array_free (priv->stage_gestures, TRUE); g_list_free (priv->listeners); G_OBJECT_CLASS (meta_gesture_tracker_parent_class)->finalize (object); } static void meta_gesture_tracker_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); switch (prop_id) { case PROP_AUTODENY_TIMEOUT: priv->autodeny_timeout = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_gesture_tracker_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (META_GESTURE_TRACKER (object)); switch (prop_id) { case PROP_AUTODENY_TIMEOUT: g_value_set_uint (value, priv->autodeny_timeout); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_gesture_tracker_class_init (MetaGestureTrackerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_gesture_tracker_finalize; object_class->set_property = meta_gesture_tracker_set_property; object_class->get_property = meta_gesture_tracker_get_property; obj_props[PROP_AUTODENY_TIMEOUT] = g_param_spec_uint ("autodeny-timeout", "Auto-deny timeout", "Auto-deny timeout", 0, G_MAXUINT, DEFAULT_AUTODENY_TIMEOUT, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, PROP_LAST, obj_props); signals[STATE_CHANGED] = g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MetaGestureTrackerClass, state_changed), NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_POINTER, G_TYPE_UINT); } static gboolean autodeny_sequence (gpointer user_data) { MetaSequenceInfo *info = user_data; /* Deny the sequence automatically after the given timeout */ if (info->state == META_SEQUENCE_NONE) meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, META_SEQUENCE_REJECTED); info->autodeny_timeout_id = 0; return G_SOURCE_REMOVE; } static MetaSequenceInfo * meta_sequence_info_new (MetaGestureTracker *tracker, const ClutterEvent *event) { MetaGestureTrackerPrivate *priv; MetaSequenceInfo *info; guint ms; priv = meta_gesture_tracker_get_instance_private (tracker); ms = priv->autodeny_timeout; info = g_slice_new0 (MetaSequenceInfo); info->tracker = tracker; info->sequence = event->touch.sequence; info->state = META_SEQUENCE_NONE; info->autodeny_timeout_id = g_timeout_add (ms, autodeny_sequence, info); clutter_event_get_coords (event, &info->start_x, &info->start_y); return info; } static void meta_sequence_info_free (MetaSequenceInfo *info) { g_clear_handle_id (&info->autodeny_timeout_id, g_source_remove); if (info->state == META_SEQUENCE_NONE) meta_gesture_tracker_set_sequence_state (info->tracker, info->sequence, META_SEQUENCE_REJECTED); g_slice_free (MetaSequenceInfo, info); } static gboolean state_is_applicable (MetaSequenceState prev_state, MetaSequenceState state) { if (prev_state == META_SEQUENCE_PENDING_END) return FALSE; /* Don't allow reverting to none */ if (state == META_SEQUENCE_NONE) return FALSE; /* PENDING_END state is final */ if (prev_state == META_SEQUENCE_PENDING_END) return FALSE; /* Sequences must be accepted/denied before PENDING_END */ if (prev_state == META_SEQUENCE_NONE && state == META_SEQUENCE_PENDING_END) return FALSE; /* Make sequences stick to their accepted/denied state */ if (state != META_SEQUENCE_PENDING_END && prev_state != META_SEQUENCE_NONE) return FALSE; return TRUE; } static gboolean meta_gesture_tracker_set_state (MetaGestureTracker *tracker, MetaSequenceState state) { MetaGestureTrackerPrivate *priv; ClutterEventSequence *sequence; GHashTableIter iter; priv = meta_gesture_tracker_get_instance_private (tracker); if (priv->stage_state != state && !state_is_applicable (priv->stage_state, state)) return FALSE; g_hash_table_iter_init (&iter, priv->sequences); priv->stage_state = state; while (g_hash_table_iter_next (&iter, (gpointer*) &sequence, NULL)) meta_gesture_tracker_set_sequence_state (tracker, sequence, state); return TRUE; } static gboolean gesture_begin_cb (ClutterGestureAction *gesture, ClutterActor *actor, MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (tracker); if (!g_list_find (priv->listeners, gesture) && meta_gesture_tracker_set_state (tracker, META_SEQUENCE_ACCEPTED)) priv->listeners = g_list_prepend (priv->listeners, gesture); return TRUE; } static void gesture_end_cb (ClutterGestureAction *gesture, ClutterActor *actor, MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (tracker); priv->listeners = g_list_remove (priv->listeners, gesture); if (!priv->listeners) meta_gesture_tracker_untrack_stage (tracker); } static void gesture_cancel_cb (ClutterGestureAction *gesture, ClutterActor *actor, MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (tracker); if (g_list_find (priv->listeners, gesture)) { priv->listeners = g_list_remove (priv->listeners, gesture); if (!priv->listeners) meta_gesture_tracker_set_state (tracker, META_SEQUENCE_PENDING_END); } } static gboolean cancel_and_unref_gesture_cb (ClutterGestureAction *action) { clutter_gesture_action_cancel (action); g_object_unref (action); return G_SOURCE_REMOVE; } static void clear_gesture_data (GestureActionData *data) { g_clear_signal_handler (&data->gesture_begin_id, data->gesture); g_clear_signal_handler (&data->gesture_end_id, data->gesture); g_clear_signal_handler (&data->gesture_cancel_id, data->gesture); /* Defer cancellation to an idle, as it may happen within event handling */ g_idle_add ((GSourceFunc) cancel_and_unref_gesture_cb, data->gesture); } static void meta_gesture_tracker_init (MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (tracker); priv->sequences = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_sequence_info_free); priv->stage_gestures = g_array_new (FALSE, FALSE, sizeof (GestureActionData)); g_array_set_clear_func (priv->stage_gestures, (GDestroyNotify) clear_gesture_data); } MetaGestureTracker * meta_gesture_tracker_new (void) { return g_object_new (META_TYPE_GESTURE_TRACKER, NULL); } static void meta_gesture_tracker_track_stage (MetaGestureTracker *tracker, ClutterActor *stage) { MetaGestureTrackerPrivate *priv; GList *actions, *l; priv = meta_gesture_tracker_get_instance_private (tracker); actions = clutter_actor_get_actions (stage); for (l = actions; l; l = l->next) { GestureActionData data; if (!CLUTTER_IS_GESTURE_ACTION (l->data)) continue; data.gesture = g_object_ref (l->data); data.state = META_SEQUENCE_NONE; data.gesture_begin_id = g_signal_connect (data.gesture, "gesture-begin", G_CALLBACK (gesture_begin_cb), tracker); data.gesture_end_id = g_signal_connect (data.gesture, "gesture-end", G_CALLBACK (gesture_end_cb), tracker); data.gesture_cancel_id = g_signal_connect (data.gesture, "gesture-cancel", G_CALLBACK (gesture_cancel_cb), tracker); g_array_append_val (priv->stage_gestures, data); } g_list_free (actions); } static void meta_gesture_tracker_untrack_stage (MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; priv = meta_gesture_tracker_get_instance_private (tracker); priv->stage_state = META_SEQUENCE_NONE; g_hash_table_remove_all (priv->sequences); if (priv->stage_gestures->len > 0) g_array_remove_range (priv->stage_gestures, 0, priv->stage_gestures->len); g_list_free (priv->listeners); priv->listeners = NULL; } gboolean meta_gesture_tracker_handle_event (MetaGestureTracker *tracker, const ClutterEvent *event) { MetaGestureTrackerPrivate *priv; ClutterEventSequence *sequence; MetaSequenceState state; MetaSequenceInfo *info; ClutterActor *stage; gfloat x, y; sequence = clutter_event_get_event_sequence (event); if (!sequence) return FALSE; priv = meta_gesture_tracker_get_instance_private (tracker); stage = CLUTTER_ACTOR (clutter_event_get_stage (event)); switch (event->type) { case CLUTTER_TOUCH_BEGIN: if (g_hash_table_size (priv->sequences) == 0) meta_gesture_tracker_track_stage (tracker, stage); info = meta_sequence_info_new (tracker, event); g_hash_table_insert (priv->sequences, sequence, info); if (priv->stage_gestures->len == 0) { /* If no gestures are attached, reject the sequence right away */ meta_gesture_tracker_set_sequence_state (tracker, sequence, META_SEQUENCE_REJECTED); } else if (priv->stage_state != META_SEQUENCE_NONE) { /* Make the sequence state match the general state */ meta_gesture_tracker_set_sequence_state (tracker, sequence, priv->stage_state); } state = info->state; break; case CLUTTER_TOUCH_END: info = g_hash_table_lookup (priv->sequences, sequence); if (!info) return FALSE; /* If nothing was done yet about the sequence, reject it so X11 * clients may see it */ if (info->state == META_SEQUENCE_NONE) meta_gesture_tracker_set_sequence_state (tracker, sequence, META_SEQUENCE_REJECTED); state = info->state; g_hash_table_remove (priv->sequences, sequence); if (g_hash_table_size (priv->sequences) == 0) meta_gesture_tracker_untrack_stage (tracker); break; case CLUTTER_TOUCH_UPDATE: info = g_hash_table_lookup (priv->sequences, sequence); if (!info) return FALSE; clutter_event_get_coords (event, &x, &y); if (info->state == META_SEQUENCE_NONE && (ABS (info->start_x - x) > DISTANCE_THRESHOLD || ABS (info->start_y - y) > DISTANCE_THRESHOLD)) meta_gesture_tracker_set_sequence_state (tracker, sequence, META_SEQUENCE_REJECTED); state = info->state; break; default: return FALSE; break; } /* As soon as a sequence is accepted, we replay it to * the stage as a captured event, and make sure it's never * propagated anywhere else. Since ClutterGestureAction does * all its event handling from a captured-event handler on * the stage, this effectively acts as a "sequence grab" on * gesture actions. * * Sequences that aren't (yet or never) in an accepted state * will go through, these events will get processed through * the compositor, and eventually through clutter, still * triggering the gestures capturing events on the stage, and * possibly resulting in MetaSequenceState changes. */ if (state == META_SEQUENCE_ACCEPTED) { clutter_actor_event (CLUTTER_ACTOR (clutter_event_get_stage (event)), event, TRUE); return TRUE; } return FALSE; } gboolean meta_gesture_tracker_set_sequence_state (MetaGestureTracker *tracker, ClutterEventSequence *sequence, MetaSequenceState state) { MetaGestureTrackerPrivate *priv; MetaSequenceInfo *info; g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), FALSE); priv = meta_gesture_tracker_get_instance_private (tracker); info = g_hash_table_lookup (priv->sequences, sequence); if (!info) return FALSE; else if (state == info->state) return TRUE; if (!state_is_applicable (info->state, state)) return FALSE; /* Unset autodeny timeout */ g_clear_handle_id (&info->autodeny_timeout_id, g_source_remove); info->state = state; g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); /* If the sequence was denied, set immediately to PENDING_END after emission */ if (state == META_SEQUENCE_REJECTED) { info->state = META_SEQUENCE_PENDING_END; g_signal_emit (tracker, signals[STATE_CHANGED], 0, sequence, info->state); } return TRUE; } MetaSequenceState meta_gesture_tracker_get_sequence_state (MetaGestureTracker *tracker, ClutterEventSequence *sequence) { MetaGestureTrackerPrivate *priv; MetaSequenceInfo *info; g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), META_SEQUENCE_PENDING_END); priv = meta_gesture_tracker_get_instance_private (tracker); info = g_hash_table_lookup (priv->sequences, sequence); if (!info) return META_SEQUENCE_PENDING_END; return info->state; } gint meta_gesture_tracker_get_n_current_touches (MetaGestureTracker *tracker) { MetaGestureTrackerPrivate *priv; g_return_val_if_fail (META_IS_GESTURE_TRACKER (tracker), 0); priv = meta_gesture_tracker_get_instance_private (tracker); return g_hash_table_size (priv->sequences); } muffin-6.4.1/src/core/meta-launch-context.c0000664000175000017500000002020014723361714017477 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "core/display-private.h" #include "meta/meta-launch-context.h" #include "x11/meta-startup-notification-x11.h" typedef struct _MetaLaunchContext MetaLaunchContext; struct _MetaLaunchContext { GAppLaunchContext parent_instance; MetaDisplay *display; MetaWorkspace *workspace; uint32_t timestamp; }; G_DEFINE_TYPE (MetaLaunchContext, meta_launch_context, G_TYPE_APP_LAUNCH_CONTEXT) enum { PROP_DISPLAY = 1, PROP_WORKSPACE, PROP_TIMESTAMP, N_PROPS }; static GParamSpec *props[N_PROPS] = { 0, }; static void meta_launch_context_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); switch (prop_id) { case PROP_DISPLAY: context->display = g_value_get_object (value); break; case PROP_WORKSPACE: meta_launch_context_set_workspace (context, g_value_get_object (value)); break; case PROP_TIMESTAMP: meta_launch_context_set_timestamp (context, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_launch_context_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, context->display); break; case PROP_WORKSPACE: g_value_set_object (value, context->workspace); break; case PROP_TIMESTAMP: g_value_set_uint (value, context->timestamp); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_launch_context_finalize (GObject *object) { G_OBJECT_CLASS (meta_launch_context_parent_class)->finalize (object); } static void meta_launch_context_constructed (GObject *object) { MetaLaunchContext *context = META_LAUNCH_CONTEXT (object); const char *x11_display, *wayland_display; G_OBJECT_CLASS (meta_launch_context_parent_class)->constructed (object); x11_display = getenv ("DISPLAY"); wayland_display = getenv ("WAYLAND_DISPLAY"); if (x11_display) { g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), "DISPLAY", x11_display); } if (wayland_display) { g_app_launch_context_setenv (G_APP_LAUNCH_CONTEXT (context), "WAYLAND_DISPLAY", wayland_display); } } static gchar * meta_launch_context_get_startup_notify_id (GAppLaunchContext *launch_context, GAppInfo *info, GList *files) { MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context); MetaDisplay *display = context->display; int workspace_idx = -1; char *startup_id = NULL; if (context->workspace) workspace_idx = meta_workspace_index (context->workspace); if (display->x11_display) { /* If there is a X11 display, we prefer going entirely through * libsn, as SnMonitor expects to keep a view of the full lifetime * of the startup sequence. We can't avoid it when launching and * expect that a "remove" message from a X11 client will be handled. */ startup_id = meta_x11_startup_notification_launch (display->x11_display, info, context->timestamp, workspace_idx); } if (!startup_id) { const char *application_id = NULL; MetaStartupNotification *sn; MetaStartupSequence *seq; startup_id = g_uuid_string_random (); /* Fallback through inserting our own startup sequence, this * will be enough for wayland clients. */ if (G_IS_DESKTOP_APP_INFO (info)) { application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (info)); } sn = meta_display_get_startup_notification (context->display); seq = g_object_new (META_TYPE_STARTUP_SEQUENCE, "id", startup_id, "application-id", application_id, "name", g_app_info_get_name (info), "workspace", workspace_idx, "timestamp", context->timestamp, NULL); meta_startup_notification_add_sequence (sn, seq); g_object_unref (seq); } return startup_id; } static void meta_launch_context_launch_failed (GAppLaunchContext *launch_context, const gchar *startup_notify_id) { MetaLaunchContext *context = META_LAUNCH_CONTEXT (launch_context); MetaStartupNotification *sn; MetaStartupSequence *seq; sn = meta_display_get_startup_notification (context->display); seq = meta_startup_notification_lookup_sequence (sn, startup_notify_id); if (seq) { meta_startup_sequence_complete (seq); meta_startup_notification_remove_sequence (sn, seq); } } static void meta_launch_context_class_init (MetaLaunchContextClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GAppLaunchContextClass *ctx_class = G_APP_LAUNCH_CONTEXT_CLASS (klass); object_class->finalize = meta_launch_context_finalize; object_class->constructed = meta_launch_context_constructed; object_class->set_property = meta_launch_context_set_property; object_class->get_property = meta_launch_context_get_property; ctx_class->get_startup_notify_id = meta_launch_context_get_startup_notify_id; ctx_class->launch_failed = meta_launch_context_launch_failed; props[PROP_DISPLAY] = g_param_spec_object ("display", "display", "Display", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); props[PROP_WORKSPACE] = g_param_spec_object ("workspace", "workspace", "Workspace", META_TYPE_WORKSPACE, G_PARAM_READWRITE); props[PROP_TIMESTAMP] = g_param_spec_uint ("timestamp", "timestamp", "Timestamp", 0, G_MAXUINT32, 0, G_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPS, props); } static void meta_launch_context_init (MetaLaunchContext *context) { } void meta_launch_context_set_workspace (MetaLaunchContext *context, MetaWorkspace *workspace) { g_return_if_fail (META_IS_LAUNCH_CONTEXT (context)); g_return_if_fail (META_IS_WORKSPACE (workspace)); g_set_object (&context->workspace, workspace); } void meta_launch_context_set_timestamp (MetaLaunchContext *context, uint32_t timestamp) { g_return_if_fail (META_IS_LAUNCH_CONTEXT (context)); context->timestamp = timestamp; } muffin-6.4.1/src/core/meta-close-dialog.c0000664000175000017500000000763214723361714017123 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #include "config.h" #include "core/window-private.h" #include "meta/meta-close-dialog.h" #include "meta/meta-enum-types.h" enum { RESPONSE, N_SIGNALS }; guint dialog_signals[N_SIGNALS] = { 0 }; static GQuark quark_visible = 0; G_DEFINE_INTERFACE (MetaCloseDialog, meta_close_dialog, G_TYPE_OBJECT) static void meta_close_dialog_default_init (MetaCloseDialogInterface *iface) { g_object_interface_install_property (iface, g_param_spec_object ("window", "Window", "Window", META_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); dialog_signals[RESPONSE] = g_signal_new ("response", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_CLOSE_DIALOG_RESPONSE); quark_visible = g_quark_from_static_string ("meta-close-dialog-visible"); } /** * meta_close_dialog_show: * @dialog: a #MetaCloseDialog * * Shows the close dialog. **/ void meta_close_dialog_show (MetaCloseDialog *dialog) { MetaCloseDialogInterface *iface; g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); iface = META_CLOSE_DIALOG_GET_IFACE (dialog); iface->show (dialog); g_object_set_qdata (G_OBJECT (dialog), quark_visible, GINT_TO_POINTER (TRUE)); } /** * meta_close_dialog_hide: * @dialog: a #MetaCloseDialog * * Hides the close dialog. **/ void meta_close_dialog_hide (MetaCloseDialog *dialog) { MetaCloseDialogInterface *iface; g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); iface = META_CLOSE_DIALOG_GET_IFACE (dialog); iface->hide (dialog); g_object_steal_qdata (G_OBJECT (dialog), quark_visible); } /** * meta_close_dialog_response: * @dialog: a #MetaCloseDialog * @response: a #MetaCloseDialogResponse * * Responds and closes the dialog. To be called by #MetaCloseDialog * implementations. **/ void meta_close_dialog_response (MetaCloseDialog *dialog, MetaCloseDialogResponse response) { g_signal_emit (dialog, dialog_signals[RESPONSE], 0, response); meta_close_dialog_hide (dialog); } /** * meta_close_dialog_is_visible: * @dialog: a #MetaCloseDialog * * Returns whether @dialog is currently visible. * * Returns: #TRUE if @dialog is visible. **/ gboolean meta_close_dialog_is_visible (MetaCloseDialog *dialog) { return GPOINTER_TO_INT (g_object_get_qdata (G_OBJECT (dialog), quark_visible)); } /** * meta_close_dialog_focus: * @dialog: a #MetaCloseDialog * * Call whenever @dialog should receive keyboard focus, * usually when the window would. **/ void meta_close_dialog_focus (MetaCloseDialog *dialog) { MetaCloseDialogInterface *iface; g_return_if_fail (META_IS_CLOSE_DIALOG (dialog)); iface = META_CLOSE_DIALOG_GET_IFACE (dialog); if (iface->focus) iface->focus (dialog); } muffin-6.4.1/src/core/display.c0000664000175000017500000035147214723361714015306 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:display * @title: MetaDisplay * @short_description: Mutter display representation * * The display is represented as a #MetaDisplay struct. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-monitor-dbus.h" #include "backends/meta-input-device-private.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-stage-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-event-x11.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #include "clutter/x11/clutter-x11.h" #include "compositor/compositor-private.h" #include "compositor/meta-compositor-x11.h" #include "cogl/cogl.h" #include "core/bell.h" #include "core/boxes-private.h" #include "core/display-private.h" #include "core/events.h" #include "core/frame.h" #include "core/keybindings-private.h" #include "core/main-private.h" #include "core/meta-clipboard-manager.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "core/window-private.h" #include "core/workspace-private.h" #include "meta/compositor-mutter.h" #include "meta/compositor.h" #include "meta/main.h" #include "meta/meta-backend.h" #include "meta/meta-enum-types.h" #include "meta/meta-sound-player.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "x11/meta-startup-notification-x11.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "compositor/meta-compositor-server.h" #include "wayland/meta-xwayland-private.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet-pad.h" #endif #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif /* * SECTION:pings * * Sometimes we want to see whether a window is responding, * so we send it a "ping" message and see whether it sends us back a "pong" * message within a reasonable time. Here we have a system which lets us * nominate one function to be called if we get the pong in time and another * function if we don't. The system is rather more complicated than it needs * to be, since we only ever use it to destroy windows which are asked to * close themselves and don't do so within a reasonable amount of time, and * therefore we always use the same callbacks. It's possible that we might * use it for other things in future, or on the other hand we might decide * that we're never going to do so and simplify it a bit. */ /** * MetaPingData: * * Describes a ping on a window. When we send a ping to a window, we build * one of these structs, and it eventually gets passed to the timeout function * or to the function which handles the response from the window. If the window * does or doesn't respond to the ping, we use this information to deal with * these facts; we have a handler function for each. */ typedef struct { MetaWindow *window; guint32 serial; guint ping_timeout_id; } MetaPingData; G_DEFINE_TYPE(MetaDisplay, meta_display, G_TYPE_OBJECT); /* Signals */ enum { CURSOR_UPDATED, X11_DISPLAY_SETUP, X11_DISPLAY_OPENED, X11_DISPLAY_CLOSING, OVERLAY_KEY, ACCELERATOR_ACTIVATED, MODIFIERS_ACCELERATOR_ACTIVATED, FOCUS_WINDOW, WINDOW_CREATED, WINDOW_DEMANDS_ATTENTION, WINDOW_MARKED_URGENT, GRAB_OP_BEGIN, GRAB_OP_END, SHOW_RESTART_MESSAGE, RESTART, SHOW_RESIZE_POPUP, GL_VIDEO_MEMORY_PURGED, SHOW_PAD_OSD, SHOW_OSD, PAD_MODE_SWITCH, WINDOW_ENTERED_MONITOR, WINDOW_LEFT_MONITOR, WINDOW_MONITOR_CHANGED, WINDOW_WORKSPACE_CHANGED, WINDOW_SKIP_TASKBAR_CHANGED, WORKSPACE_ADDED, WORKSPACE_REMOVED, WORKSPACE_SWITCHED, ACTIVE_WORKSPACE_CHANGED, IN_FULLSCREEN_CHANGED, SHOWING_DESKTOP_CHANGED, RESTACKED, WORKAREAS_CHANGED, CLOSING, INIT_XSERVER, ZOOM_SCROLL_IN, ZOOM_SCROLL_OUT, LAST_SIGNAL }; enum { PROP_0, PROP_FOCUS_WINDOW }; static guint display_signals [LAST_SIGNAL] = { 0 }; /* * The display we're managing. This is a singleton object. (Historically, * this was a list of displays, but there was never any way to add more * than one element to it.) The goofy name is because we don't want it * to shadow the parameter in its object methods. */ static MetaDisplay *the_display = NULL; static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager, MetaDisplay *display); static void prefs_changed_callback (MetaPreference pref, void *data); static int mru_cmp (gconstpointer a, gconstpointer b); static void meta_display_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaDisplay *display = META_DISPLAY (object); switch (prop_id) { case PROP_FOCUS_WINDOW: g_value_set_object (value, display->focus_window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_display_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_display_class_init (MetaDisplayClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = meta_display_get_property; object_class->set_property = meta_display_set_property; display_signals[CURSOR_UPDATED] = g_signal_new ("cursor-updated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[X11_DISPLAY_SETUP] = g_signal_new ("x11-display-setup", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[X11_DISPLAY_OPENED] = g_signal_new ("x11-display-opened", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[X11_DISPLAY_CLOSING] = g_signal_new ("x11-display-closing", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[OVERLAY_KEY] = g_signal_new ("overlay-key", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[ACCELERATOR_ACTIVATED] = g_signal_new ("accelerator-activated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_UINT, CLUTTER_TYPE_INPUT_DEVICE, G_TYPE_UINT); /** * MetaDisplay::modifiers-accelerator-activated: * @display: the #MetaDisplay instance * * The ::modifiers-accelerator-activated signal will be emitted when * a special modifiers-only keybinding is activated. * * Returns: %TRUE means that the keyboard device should remain * frozen and %FALSE for the default behavior of unfreezing the * keyboard. */ display_signals[MODIFIERS_ACCELERATOR_ACTIVATED] = g_signal_new ("modifiers-accelerator-activated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 0); display_signals[WINDOW_CREATED] = g_signal_new ("window-created", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); display_signals[WINDOW_DEMANDS_ATTENTION] = g_signal_new ("window-demands-attention", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); display_signals[WINDOW_MARKED_URGENT] = g_signal_new ("window-marked-urgent", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); display_signals[GRAB_OP_BEGIN] = g_signal_new ("grab-op-begin", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, META_TYPE_DISPLAY, META_TYPE_WINDOW, META_TYPE_GRAB_OP); display_signals[GRAB_OP_END] = g_signal_new ("grab-op-end", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, META_TYPE_DISPLAY, META_TYPE_WINDOW, META_TYPE_GRAB_OP); /** * MetaDisplay::show-restart-message: * @display: the #MetaDisplay instance * @message: (allow-none): The message to display, or %NULL * to clear a previous restart message. * * The ::show-restart-message signal will be emitted to indicate * that the compositor should show a message during restart. This is * emitted when meta_restart() is called, either by Mutter * internally or by the embedding compositor. The message should be * immediately added to the Clutter stage in its final form - * ::restart will be emitted to exit the application and leave the * stage contents frozen as soon as the the stage is painted again. * * On case of failure to restart, this signal will be emitted again * with %NULL for @message. * * Returns: %TRUE means the message was added to the stage; %FALSE * indicates that the compositor did not show the message. */ display_signals[SHOW_RESTART_MESSAGE] = g_signal_new ("show-restart-message", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_STRING); /** * MetaDisplay::restart: * @display: the #MetaDisplay instance * * The ::restart signal is emitted to indicate that compositor * should reexec the process. This is * emitted when meta_restart() is called, either by Mutter * internally or by the embedding compositor. See also * ::show-restart-message. * * Returns: %FALSE to indicate that the compositor could not * be restarted. When the compositor is restarted, the signal * should not return. */ display_signals[RESTART] = g_signal_new ("restart", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 0); display_signals[SHOW_RESIZE_POPUP] = g_signal_new ("show-resize-popup", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_true_handled, NULL, NULL, G_TYPE_BOOLEAN, 4, G_TYPE_BOOLEAN, META_TYPE_RECTANGLE, G_TYPE_INT, G_TYPE_INT); display_signals[GL_VIDEO_MEMORY_PURGED] = g_signal_new ("gl-video-memory-purged", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaDisplay::show-pad-osd: * @display: the #MetaDisplay instance * @pad: the pad device * @settings: the pad device settings * @layout_path: path to the layout image * @edition_mode: Whether the OSD should be shown in edition mode * @monitor_idx: Monitor to show the OSD on * * Requests the pad button mapping OSD to be shown. * * Returns: (transfer none) (nullable): The OSD actor */ display_signals[SHOW_PAD_OSD] = g_signal_new ("show-pad-osd", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, CLUTTER_TYPE_ACTOR, 5, CLUTTER_TYPE_INPUT_DEVICE, G_TYPE_SETTINGS, G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_INT); display_signals[SHOW_OSD] = g_signal_new ("show-osd", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_STRING, G_TYPE_STRING); display_signals[PAD_MODE_SWITCH] = g_signal_new ("pad-mode-switch", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, CLUTTER_TYPE_INPUT_DEVICE, G_TYPE_UINT, G_TYPE_UINT); display_signals[WINDOW_ENTERED_MONITOR] = g_signal_new ("window-entered-monitor", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, META_TYPE_WINDOW); display_signals[WINDOW_LEFT_MONITOR] = g_signal_new ("window-left-monitor", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, META_TYPE_WINDOW); display_signals[WINDOW_MONITOR_CHANGED] = g_signal_new ("window-monitor-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, META_TYPE_WINDOW, G_TYPE_INT); display_signals[WINDOW_WORKSPACE_CHANGED] = g_signal_new ("window-workspace-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, META_TYPE_WINDOW, META_TYPE_WORKSPACE); display_signals[WINDOW_SKIP_TASKBAR_CHANGED] = g_signal_new ("window-skip-taskbar-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); display_signals[IN_FULLSCREEN_CHANGED] = g_signal_new ("in-fullscreen-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[SHOWING_DESKTOP_CHANGED] = g_signal_new ("showing-desktop-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[RESTACKED] = g_signal_new ("restacked", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[WORKAREAS_CHANGED] = g_signal_new ("workareas-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[CLOSING] = g_signal_new ("closing", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[INIT_XSERVER] = g_signal_new ("init-xserver", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, g_signal_accumulator_first_wins, NULL, NULL, G_TYPE_BOOLEAN, 1, G_TYPE_TASK); display_signals[ZOOM_SCROLL_IN] = g_signal_new ("zoom-scroll-in", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); display_signals[ZOOM_SCROLL_OUT] = g_signal_new ("zoom-scroll-out", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_object_class_install_property (object_class, PROP_FOCUS_WINDOW, g_param_spec_object ("focus-window", "Focus window", "Currently focused window", META_TYPE_WINDOW, G_PARAM_READABLE)); } /** * ping_data_free: * * Destructor for #MetaPingData structs. Will destroy the * event source for the struct as well. */ static void ping_data_free (MetaPingData *ping_data) { /* Remove the timeout */ g_clear_handle_id (&ping_data->ping_timeout_id, g_source_remove); g_free (ping_data); } void meta_display_remove_pending_pings_for_window (MetaDisplay *display, MetaWindow *window) { GSList *tmp; GSList *dead; /* could obviously be more efficient, don't care */ /* build list to be removed */ dead = NULL; for (tmp = display->pending_pings; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; if (ping_data->window == window) dead = g_slist_prepend (dead, ping_data); } /* remove what we found */ for (tmp = dead; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; display->pending_pings = g_slist_remove (display->pending_pings, ping_data); ping_data_free (ping_data); } g_slist_free (dead); } static MetaCompositor * create_compositor (MetaDisplay *display) { #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) return META_COMPOSITOR (meta_compositor_server_new (display)); else #endif return META_COMPOSITOR (meta_compositor_x11_new (display)); } static void enable_compositor (MetaDisplay *display) { MetaX11Display *x11_display = display->x11_display; if (x11_display) { if (!META_X11_DISPLAY_HAS_COMPOSITE (x11_display) || !META_X11_DISPLAY_HAS_DAMAGE (x11_display)) { meta_fatal ("Missing %s extension required for compositing", !META_X11_DISPLAY_HAS_COMPOSITE (x11_display) ? "composite" : "damage"); return; } int version = (x11_display->composite_major_version * 10) + x11_display->composite_minor_version; if (version < 3) { meta_fatal ("Your version of COMPOSITE (%d.%d) is too old. Version 3.0 or later required.", x11_display->composite_major_version, x11_display->composite_minor_version); return; } } if (!display->compositor) display->compositor = create_compositor (display); meta_compositor_manage (display->compositor); } static void meta_display_init (MetaDisplay *disp) { /* Some stuff could go in here that's currently in _open, * but it doesn't really matter. */ } void meta_display_cancel_touch (MetaDisplay *display) { #ifdef HAVE_WAYLAND MetaWaylandCompositor *compositor; if (!meta_is_wayland_compositor ()) return; compositor = meta_wayland_compositor_get_default (); meta_wayland_touch_cancel (compositor->seat->touch); #endif } static void gesture_tracker_state_changed (MetaGestureTracker *tracker, ClutterEventSequence *sequence, MetaSequenceState state, MetaDisplay *display) { switch (state) { case META_SEQUENCE_NONE: case META_SEQUENCE_PENDING_END: return; case META_SEQUENCE_ACCEPTED: meta_display_cancel_touch (display); G_GNUC_FALLTHROUGH; case META_SEQUENCE_REJECTED: { MetaBackend *backend; backend = meta_get_backend (); meta_backend_finish_touch_sequence (backend, sequence, state); break; } } } static void on_ui_scaling_factor_changed (MetaSettings *settings, MetaDisplay *display) { meta_display_reload_cursor (display); } static gboolean meta_display_init_x11_display (MetaDisplay *display, GError **error) { MetaX11Display *x11_display; x11_display = meta_x11_display_new (display, error); if (!x11_display) return FALSE; display->x11_display = x11_display; g_signal_emit (display, display_signals[X11_DISPLAY_SETUP], 0); meta_x11_display_create_guard_window (x11_display); if (!display->display_opening) { g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0); meta_display_manage_all_xwindows (display); meta_compositor_redirect_x11_windows (display->compositor); } return TRUE; } #ifdef HAVE_WAYLAND gboolean meta_display_init_x11_finish (MetaDisplay *display, GAsyncResult *result, GError **error) { MetaX11Display *x11_display; g_assert (g_task_get_source_tag (G_TASK (result)) == meta_display_init_x11); if (!g_task_propagate_boolean (G_TASK (result), error)) { if (*error == NULL) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error"); return FALSE; } if (display->x11_display) return TRUE; x11_display = meta_x11_display_new (display, error); if (!x11_display) return FALSE; display->x11_display = x11_display; g_signal_emit (display, display_signals[X11_DISPLAY_SETUP], 0); meta_x11_display_create_guard_window (x11_display); if (!display->display_opening) { g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0); meta_x11_display_set_cm_selection (x11_display); meta_display_manage_all_xwindows (display); meta_compositor_redirect_x11_windows (display->compositor); } return TRUE; } static void on_xserver_started (MetaXWaylandManager *manager, GAsyncResult *result, gpointer user_data) { g_autoptr (GTask) task = user_data; MetaDisplay *display = g_task_get_source_object (task); GError *error = NULL; gboolean retval = FALSE; if (!meta_xwayland_start_xserver_finish (manager, result, &error)) { if (error) g_task_return_error (task, error); else g_task_return_boolean (task, FALSE); return; } g_signal_emit (display, display_signals[INIT_XSERVER], 0, task, &retval); if (!retval) { /* No handlers for this signal, proceed right away */ g_task_return_boolean (task, TRUE); } } void meta_display_init_x11 (MetaDisplay *display, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); g_autoptr (GTask) task = NULL; task = g_task_new (display, cancellable, callback, user_data); g_task_set_source_tag (task, meta_display_init_x11); meta_xwayland_start_xserver (&compositor->xwayland_manager, cancellable, (GAsyncReadyCallback) on_xserver_started, g_steal_pointer (&task)); } static void on_x11_initialized (MetaDisplay *display, GAsyncResult *result, gpointer user_data) { g_autoptr (GError) error = NULL; if (!meta_display_init_x11_finish (display, result, &error)) g_critical ("Failed to init X11 display: %s", error->message); } #endif void meta_display_shutdown_x11 (MetaDisplay *display) { if (!display->x11_display) return; g_signal_emit (display, display_signals[X11_DISPLAY_CLOSING], 0); g_object_run_dispose (G_OBJECT (display->x11_display)); g_clear_object (&display->x11_display); } /** * meta_display_open: * * Opens a new display, sets it up, initialises all the X extensions * we will need, and adds it to the list of displays. * * Returns: %TRUE if the display was opened successfully, and %FALSE * otherwise-- that is, if the display doesn't exist or it already * has a window manager. */ gboolean meta_display_open (void) { GError *error = NULL; MetaDisplay *display; int i; guint32 timestamp; Window old_active_xwindow = None; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager; MetaSettings *settings; g_assert (the_display == NULL); display = the_display = g_object_new (META_TYPE_DISPLAY, NULL); display->closing = 0; display->display_opening = TRUE; display->pending_pings = NULL; display->autoraise_timeout_id = 0; display->autoraise_window = NULL; display->focus_window = NULL; display->workspace_manager = NULL; display->x11_display = NULL; display->current_cursor = -1; /* invalid/unset */ display->tile_preview_timeout_id = 0; display->check_fullscreen_later = 0; display->work_area_later = 0; display->mouse_mode = TRUE; /* Only relevant for mouse or sloppy focus */ display->allow_terminal_deactivation = TRUE; /* Only relevant for when a terminal has the focus */ i = 0; while (i < N_IGNORED_CROSSING_SERIALS) { display->ignored_crossing_serials[i] = 0; ++i; } display->current_time = META_CURRENT_TIME; display->grab_resize_timeout_id = 0; display->grab_have_keyboard = FALSE; display->grab_op = META_GRAB_OP_NONE; display->grab_window = NULL; display->grab_tile_mode = META_TILE_NONE; display->grab_tile_monitor_number = -1; meta_display_cleanup_edges (display); meta_display_init_keys (display); meta_prefs_add_listener (prefs_changed_callback, display); /* Get events */ meta_display_init_events (display); display->stamps = g_hash_table_new (g_int64_hash, g_int64_equal); display->wayland_windows = g_hash_table_new (NULL, NULL); monitor_manager = meta_backend_get_monitor_manager (backend); g_signal_connect (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed_internal), display); settings = meta_backend_get_settings (backend); g_signal_connect (settings, "ui-scaling-factor-changed", G_CALLBACK (on_ui_scaling_factor_changed), display); meta_display_set_cursor (display, META_CURSOR_DEFAULT); display->stack = meta_stack_new (display); display->stack_tracker = meta_stack_tracker_new (display); display->workspace_manager = meta_workspace_manager_new (display); display->startup_notification = meta_startup_notification_new (display); display->bell = meta_bell_new (display); display->selection = meta_selection_new (display); meta_clipboard_manager_init (display); #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) { if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY) { meta_display_init_x11 (display, NULL, (GAsyncReadyCallback) on_x11_initialized, NULL); } timestamp = meta_display_get_current_time_roundtrip (display); } else #endif { if (!meta_display_init_x11_display (display, &error)) g_error ("Failed to init X11 display: %s", error->message); timestamp = display->x11_display->timestamp; } display->last_focus_time = timestamp; display->last_user_time = timestamp; display->compositor = NULL; if (!meta_is_wayland_compositor ()) meta_prop_get_window (display->x11_display, display->x11_display->xroot, display->x11_display->atom__NET_ACTIVE_WINDOW, &old_active_xwindow); enable_compositor (display); if (display->x11_display) { g_signal_emit (display, display_signals[X11_DISPLAY_OPENED], 0); meta_x11_display_restore_active_workspace (display->x11_display); meta_x11_display_create_guard_window (display->x11_display); } /* Set up touch support */ display->gesture_tracker = meta_gesture_tracker_new (); g_signal_connect (display->gesture_tracker, "state-changed", G_CALLBACK (gesture_tracker_state_changed), display); /* We know that if mutter is running as a Wayland compositor, * we start out with no windows. */ if (!meta_is_wayland_compositor ()) meta_display_manage_all_xwindows (display); if (old_active_xwindow != None) { MetaWindow *old_active_window; old_active_window = meta_x11_display_lookup_x_window (display->x11_display, old_active_xwindow); if (old_active_window) meta_window_focus (old_active_window, timestamp); else meta_display_unset_input_focus (display, timestamp); } else { meta_display_unset_input_focus (display, timestamp); } meta_idle_monitor_init_dbus (); display->sound_player = g_object_new (META_TYPE_SOUND_PLAYER, NULL); meta_input_settings_refresh (meta_backend_get_input_settings (backend)); /* Done opening new display */ display->display_opening = FALSE; return TRUE; } static gint ptrcmp (gconstpointer a, gconstpointer b) { if (a < b) return -1; else if (a > b) return 1; else return 0; } /** * meta_display_list_windows: * @display: a #MetaDisplay * @flags: options for listing * * Lists windows for the display, the @flags parameter for * now determines whether override-redirect windows will be * included. * * Return value: (transfer container) (element-type MetaWindow): the list of windows. */ GSList* meta_display_list_windows (MetaDisplay *display, MetaListWindowsFlags flags) { GSList *winlist; GSList *prev; GSList *tmp; GHashTableIter iter; gpointer key, value; winlist = NULL; if (display->x11_display) { g_hash_table_iter_init (&iter, display->x11_display->xids); while (g_hash_table_iter_next (&iter, &key, &value)) { MetaWindow *window = value; if (!META_IS_WINDOW (window) || window->unmanaging) continue; if (!window->override_redirect || (flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0) winlist = g_slist_prepend (winlist, window); } } g_hash_table_iter_init (&iter, display->wayland_windows); while (g_hash_table_iter_next (&iter, &key, &value)) { MetaWindow *window = value; if (!META_IS_WINDOW (window) || window->unmanaging) continue; if (!window->override_redirect || (flags & META_LIST_INCLUDE_OVERRIDE_REDIRECT) != 0) winlist = g_slist_prepend (winlist, window); } /* Uniquify the list, since both frame windows and plain * windows are in the hash */ winlist = g_slist_sort (winlist, ptrcmp); prev = NULL; tmp = winlist; while (tmp != NULL) { GSList *next; next = tmp->next; if (next && next->data == tmp->data) { /* Delete tmp from list */ if (prev) prev->next = next; if (tmp == winlist) winlist = next; g_slist_free_1 (tmp); /* leave prev unchanged */ } else { prev = tmp; } tmp = next; } if (flags & META_LIST_SORTED) winlist = g_slist_sort (winlist, mru_cmp); return winlist; } void meta_display_close (MetaDisplay *display, guint32 timestamp) { g_assert (display != NULL); g_assert (display == the_display); if (display->closing != 0) { /* The display's already been closed. */ return; } display->closing += 1; g_signal_emit (display, display_signals[CLOSING], 0); meta_compositor_unmanage (display->compositor); meta_display_unmanage_windows (display, timestamp); meta_prefs_remove_listener (prefs_changed_callback, display); meta_display_remove_autoraise_callback (display); g_clear_object (&display->gesture_tracker); g_clear_handle_id (&display->focus_timeout_id, g_source_remove); g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove); if (display->work_area_later != 0) meta_later_remove (display->work_area_later); if (display->check_fullscreen_later != 0) meta_later_remove (display->check_fullscreen_later); /* Stop caring about events */ meta_display_free_events (display); g_clear_pointer (&display->compositor, meta_compositor_destroy); meta_display_shutdown_x11 (display); g_clear_object (&display->stack); g_clear_pointer (&display->stack_tracker, meta_stack_tracker_free); /* Must be after all calls to meta_window_unmanage() since they * unregister windows */ g_hash_table_destroy (display->wayland_windows); g_hash_table_destroy (display->stamps); meta_display_shutdown_keys (display); g_clear_object (&display->bell); g_clear_object (&display->startup_notification); g_clear_object (&display->workspace_manager); g_clear_object (&display->sound_player); meta_clipboard_manager_shutdown (display); g_clear_object (&display->selection); g_object_unref (display); the_display = NULL; meta_quit (META_EXIT_SUCCESS); } /** * meta_display_for_x_display: * @xdisplay: An X display * * Returns the singleton MetaDisplay if @xdisplay matches the X display it's * managing; otherwise gives a warning and returns %NULL. When we were claiming * to be able to manage multiple displays, this was supposed to find the * display out of the list which matched that display. Now it's merely an * extra sanity check. * * Returns: The singleton X display, or %NULL if @xdisplay isn't the one * we're managing. */ MetaDisplay* meta_display_for_x_display (Display *xdisplay) { if (the_display->x11_display->xdisplay == xdisplay) return the_display; meta_warning ("Could not find display for X display %p, probably going to crash\n", xdisplay); return NULL; } /** * meta_get_display: * * Accessor for the singleton MetaDisplay. * * Returns: The only #MetaDisplay there is. This can be %NULL, but only * during startup. */ MetaDisplay* meta_get_display (void) { return the_display; } static inline gboolean grab_op_is_window (MetaGrabOp op) { return META_GRAB_OP_GET_BASE_TYPE (op) == META_GRAB_OP_WINDOW_BASE; } gboolean meta_grab_op_is_mouse (MetaGrabOp op) { if (!grab_op_is_window (op)) return FALSE; return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) == 0; } gboolean meta_grab_op_is_keyboard (MetaGrabOp op) { if (!grab_op_is_window (op)) return FALSE; return (op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) != 0; } gboolean meta_grab_op_is_resizing (MetaGrabOp op) { if (!grab_op_is_window (op)) return FALSE; return (op & META_GRAB_OP_WINDOW_DIR_MASK) != 0 || op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN; } gboolean meta_grab_op_is_moving (MetaGrabOp op) { if (!grab_op_is_window (op)) return FALSE; return !meta_grab_op_is_resizing (op); } /** * meta_display_windows_are_interactable: * @op: A #MetaGrabOp * * Whether windows can be interacted with. */ gboolean meta_display_windows_are_interactable (MetaDisplay *display) { switch (display->event_route) { case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: return TRUE; default: return FALSE; } } /** * meta_display_xserver_time_is_before: * @display: a #MetaDisplay * @time1: An event timestamp * @time2: An event timestamp * * Xserver time can wraparound, thus comparing two timestamps needs to take * this into account. If no wraparound has occurred, this is equivalent to * time1 < time2 * Otherwise, we need to account for the fact that wraparound can occur * and the fact that a timestamp of 0 must be special-cased since it * means "older than anything else". * * Note that this is NOT an equivalent for time1 <= time2; if that's what * you need then you'll need to swap the order of the arguments and negate * the result. */ gboolean meta_display_xserver_time_is_before (MetaDisplay *display, guint32 time1, guint32 time2) { return XSERVER_TIME_IS_BEFORE(time1, time2); } /** * meta_display_get_last_user_time: * @display: a #MetaDisplay * * Returns: Timestamp of the last user interaction event with a window */ guint32 meta_display_get_last_user_time (MetaDisplay *display) { return display->last_user_time; } /* Get time of current event, or CurrentTime if none. */ guint32 meta_display_get_current_time (MetaDisplay *display) { return display->current_time; } guint32 meta_display_get_current_time_roundtrip (MetaDisplay *display) { if (meta_is_wayland_compositor ()) /* Xwayland uses monotonic clock, so lets use it here as well */ return (guint32) (g_get_monotonic_time () / 1000); else return meta_x11_display_get_current_time_roundtrip (display->x11_display); } /** * meta_display_add_ignored_crossing_serial: * @display: a #MetaDisplay * @serial: the serial to ignore * * Save the specified serial and ignore crossing events with that * serial for the purpose of focus-follows-mouse. This can be used * for certain changes to the window hierarchy that we don't want * to change the focus window, even if they cause the pointer to * end up in a new window. */ void meta_display_add_ignored_crossing_serial (MetaDisplay *display, unsigned long serial) { int i; /* don't add the same serial more than once */ if (display->ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS-1] == serial) return; /* shift serials to the left */ i = 0; while (i < (N_IGNORED_CROSSING_SERIALS - 1)) { display->ignored_crossing_serials[i] = display->ignored_crossing_serials[i+1]; ++i; } /* put new one on the end */ display->ignored_crossing_serials[i] = serial; } static gboolean window_raise_with_delay_callback (void *data) { MetaWindow *window = data; window->display->autoraise_timeout_id = 0; window->display->autoraise_window = NULL; /* If we aren't already on top, check whether the pointer is inside * the window and raise the window if so. */ if (meta_stack_get_top (window->display->stack) != window) { if (meta_window_has_pointer (window)) meta_window_raise (window); else meta_topic (META_DEBUG_FOCUS, "Pointer not inside window, not raising %s\n", window->desc); } return G_SOURCE_REMOVE; } void meta_display_queue_autoraise_callback (MetaDisplay *display, MetaWindow *window) { meta_topic (META_DEBUG_FOCUS, "Queuing an autoraise timeout for %s with delay %d\n", window->desc, meta_prefs_get_auto_raise_delay ()); g_clear_handle_id (&display->autoraise_timeout_id, g_source_remove); display->autoraise_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, meta_prefs_get_auto_raise_delay (), window_raise_with_delay_callback, window, NULL); g_source_set_name_by_id (display->autoraise_timeout_id, "[mutter] window_raise_with_delay_callback"); display->autoraise_window = window; } void meta_display_sync_wayland_input_focus (MetaDisplay *display) { #ifdef HAVE_WAYLAND MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWindow *focus_window = NULL; MetaBackend *backend = meta_get_backend (); MetaStage *stage = META_STAGE (meta_backend_get_stage (backend)); gboolean is_no_focus_xwindow = FALSE; if (display->x11_display) is_no_focus_xwindow = meta_x11_display_xwindow_is_a_no_focus_window (display->x11_display, display->x11_display->focus_xwindow); if (!meta_display_windows_are_interactable (display)) focus_window = NULL; else if (is_no_focus_xwindow) focus_window = NULL; else if (display->focus_window && display->focus_window->surface) focus_window = display->focus_window; else meta_topic (META_DEBUG_FOCUS, "Focus change has no effect, because there is no matching wayland surface"); meta_stage_set_active (stage, focus_window == NULL); meta_wayland_compositor_set_input_focus (compositor, focus_window); meta_wayland_seat_repick (compositor->seat); #endif } void meta_display_update_focus_window (MetaDisplay *display, MetaWindow *window) { if (display->focus_window == window) return; if (display->focus_window) { MetaWindow *previous; meta_topic (META_DEBUG_FOCUS, "%s is now the previous focus window due to being focused out or unmapped\n", display->focus_window->desc); /* Make sure that signals handlers invoked by * meta_window_set_focused_internal() don't see * display->focus_window->has_focus == FALSE */ previous = display->focus_window; display->focus_window = NULL; meta_window_set_focused_internal (previous, FALSE); } display->focus_window = window; if (display->focus_window) { meta_topic (META_DEBUG_FOCUS, "* Focus --> %s\n", display->focus_window->desc); meta_window_set_focused_internal (display->focus_window, TRUE); } else meta_topic (META_DEBUG_FOCUS, "* Focus --> NULL\n"); if (meta_is_wayland_compositor ()) meta_display_sync_wayland_input_focus (display); g_object_notify (G_OBJECT (display), "focus-window"); } gboolean meta_display_timestamp_too_old (MetaDisplay *display, guint32 *timestamp) { /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow * us to sanity check the timestamp here and ensure it doesn't correspond to * a future time (though we would want to rename to * timestamp_too_old_or_in_future). */ if (*timestamp == META_CURRENT_TIME) { *timestamp = meta_display_get_current_time_roundtrip (display); return FALSE; } else if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_focus_time)) { if (XSERVER_TIME_IS_BEFORE (*timestamp, display->last_user_time)) return TRUE; else { *timestamp = display->last_focus_time; return FALSE; } } return FALSE; } void meta_display_set_input_focus (MetaDisplay *display, MetaWindow *window, gboolean focus_frame, guint32 timestamp) { if (meta_display_timestamp_too_old (display, ×tamp)) return; if (display->x11_display) { meta_x11_display_set_input_focus (display->x11_display, window, focus_frame, timestamp); } meta_display_update_focus_window (display, window); display->last_focus_time = timestamp; if (window == NULL || window != display->autoraise_window) meta_display_remove_autoraise_callback (display); } void meta_display_unset_input_focus (MetaDisplay *display, guint32 timestamp) { meta_display_set_input_focus (display, NULL, FALSE, timestamp); } void meta_display_register_wayland_window (MetaDisplay *display, MetaWindow *window) { g_hash_table_add (display->wayland_windows, window); } void meta_display_unregister_wayland_window (MetaDisplay *display, MetaWindow *window) { g_hash_table_remove (display->wayland_windows, window); } MetaWindow* meta_display_lookup_stamp (MetaDisplay *display, guint64 stamp) { return g_hash_table_lookup (display->stamps, &stamp); } void meta_display_register_stamp (MetaDisplay *display, guint64 *stampp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (display->stamps, stampp) == NULL); g_hash_table_insert (display->stamps, stampp, window); } void meta_display_unregister_stamp (MetaDisplay *display, guint64 stamp) { g_return_if_fail (g_hash_table_lookup (display->stamps, &stamp) != NULL); g_hash_table_remove (display->stamps, &stamp); } MetaWindow* meta_display_lookup_stack_id (MetaDisplay *display, guint64 stack_id) { if (META_STACK_ID_IS_X11 (stack_id)) { if (!display->x11_display) return NULL; return meta_x11_display_lookup_x_window (display->x11_display, (Window)stack_id); } else { return meta_display_lookup_stamp (display, stack_id); } } /* We return a pointer into a ring of static buffers. This is to make * using this function for debug-logging convenient and avoid tempory * strings that must be freed. */ const char * meta_display_describe_stack_id (MetaDisplay *display, guint64 stack_id) { /* 0x<64-bit: 16 characters> (<10 characters of title>)\0' */ static char buffer[5][32]; MetaWindow *window; static int pos = 0; char *result; result = buffer[pos]; pos = (pos + 1) % 5; window = meta_display_lookup_stack_id (display, stack_id); if (window && window->title) snprintf (result, sizeof(buffer[0]), "%#" G_GINT64_MODIFIER "x (%.10s)", stack_id, window->title); else snprintf (result, sizeof(buffer[0]), "%#" G_GINT64_MODIFIER "x", stack_id); return result; } void meta_display_notify_window_created (MetaDisplay *display, MetaWindow *window) { COGL_TRACE_BEGIN_SCOPED (MetaDisplayNotifyWindowCreated, "Display (notify window created)"); g_signal_emit (display, display_signals[WINDOW_CREATED], 0, window); } static MetaCursor meta_cursor_for_grab_op (MetaGrabOp op) { switch (op) { case META_GRAB_OP_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_SE: return META_CURSOR_SE_RESIZE; break; case META_GRAB_OP_RESIZING_S: case META_GRAB_OP_KEYBOARD_RESIZING_S: return META_CURSOR_SOUTH_RESIZE; break; case META_GRAB_OP_RESIZING_SW: case META_GRAB_OP_KEYBOARD_RESIZING_SW: return META_CURSOR_SW_RESIZE; break; case META_GRAB_OP_RESIZING_N: case META_GRAB_OP_KEYBOARD_RESIZING_N: return META_CURSOR_NORTH_RESIZE; break; case META_GRAB_OP_RESIZING_NE: case META_GRAB_OP_KEYBOARD_RESIZING_NE: return META_CURSOR_NE_RESIZE; break; case META_GRAB_OP_RESIZING_NW: case META_GRAB_OP_KEYBOARD_RESIZING_NW: return META_CURSOR_NW_RESIZE; break; case META_GRAB_OP_RESIZING_W: case META_GRAB_OP_KEYBOARD_RESIZING_W: return META_CURSOR_WEST_RESIZE; break; case META_GRAB_OP_RESIZING_E: case META_GRAB_OP_KEYBOARD_RESIZING_E: return META_CURSOR_EAST_RESIZE; break; case META_GRAB_OP_MOVING: case META_GRAB_OP_KEYBOARD_MOVING: case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: return META_CURSOR_MOVE_OR_RESIZE_WINDOW; break; default: break; } return META_CURSOR_DEFAULT; } static float find_highest_logical_monitor_scale (MetaBackend *backend, MetaCursorSprite *cursor_sprite) { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); graphene_rect_t cursor_rect; GList *logical_monitors; GList *l; float highest_scale = 0.0; cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer, cursor_sprite); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; graphene_rect_t logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor->rect); if (!graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; highest_scale = MAX (highest_scale, logical_monitor->scale); } return highest_scale; } static void root_cursor_prepare_at (MetaCursorSpriteXcursor *sprite_xcursor, int x, int y, MetaDisplay *display) { MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor); MetaBackend *backend = meta_get_backend (); if (meta_is_stage_views_scaled ()) { float scale; scale = find_highest_logical_monitor_scale (backend, cursor_sprite); if (scale != 0.0) { float ceiled_scale; ceiled_scale = ceilf (scale); meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor, (int) ceiled_scale); meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0 / ceiled_scale); } } else { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); /* Reload the cursor texture if the scale has changed. */ if (logical_monitor) { meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor, logical_monitor->scale); meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0); } } } static void manage_root_cursor_sprite_scale (MetaDisplay *display, MetaCursorSpriteXcursor *sprite_xcursor) { g_signal_connect_object (sprite_xcursor, "prepare-at", G_CALLBACK (root_cursor_prepare_at), display, 0); } void meta_display_reload_cursor (MetaDisplay *display) { MetaCursor cursor = display->current_cursor; MetaCursorSpriteXcursor *sprite_xcursor; MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); sprite_xcursor = meta_cursor_sprite_xcursor_new (cursor); if (meta_is_wayland_compositor ()) manage_root_cursor_sprite_scale (display, sprite_xcursor); meta_cursor_tracker_set_root_cursor (cursor_tracker, META_CURSOR_SPRITE (sprite_xcursor)); g_object_unref (sprite_xcursor); g_signal_emit (display, display_signals[CURSOR_UPDATED], 0, display); } void meta_display_set_cursor (MetaDisplay *display, MetaCursor cursor) { if (cursor == display->current_cursor) return; display->current_cursor = cursor; meta_display_reload_cursor (display); } void meta_display_update_cursor (MetaDisplay *display) { meta_display_set_cursor (display, meta_cursor_for_grab_op (display->grab_op)); } static MetaWindow * get_first_freefloating_window (MetaWindow *window) { while (meta_window_is_attached_dialog (window)) window = meta_window_get_transient_for (window); /* Attached dialogs should always have a non-NULL transient-for */ g_assert (window != NULL); return window; } static MetaEventRoute get_event_route_from_grab_op (MetaGrabOp op) { switch (META_GRAB_OP_GET_BASE_TYPE (op)) { case META_GRAB_OP_NONE: /* begin_grab_op shouldn't be called with META_GRAB_OP_NONE. */ g_assert_not_reached (); case META_GRAB_OP_WINDOW_BASE: return META_EVENT_ROUTE_WINDOW_OP; case META_GRAB_OP_COMPOSITOR: /* begin_grab_op shouldn't be called with META_GRAB_OP_COMPOSITOR. */ g_assert_not_reached (); case META_GRAB_OP_WAYLAND_POPUP: return META_EVENT_ROUTE_WAYLAND_POPUP; case META_GRAB_OP_FRAME_BUTTON: return META_EVENT_ROUTE_FRAME_BUTTON; default: g_assert_not_reached (); return 0; } } gboolean meta_display_begin_grab_op (MetaDisplay *display, MetaWindow *window, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, /* XXX - ignored */ guint32 timestamp, int root_x, int root_y) { MetaBackend *backend = meta_get_backend (); MetaWindow *grab_window = NULL; MetaEventRoute event_route; g_assert (window != NULL); meta_topic (META_DEBUG_WINDOW_OPS, "Doing grab op %u on window %s button %d pointer already grabbed: %d pointer pos %d,%d\n", op, window->desc, button, pointer_already_grabbed, root_x, root_y); if (display->grab_op != META_GRAB_OP_NONE) { meta_warning ("Attempt to perform window operation %u on window %s when operation %u on %s already in effect\n", op, window->desc, display->grab_op, display->grab_window ? display->grab_window->desc : "none"); return FALSE; } event_route = get_event_route_from_grab_op (op); if (event_route == META_EVENT_ROUTE_WINDOW_OP) { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); else { display->grab_initial_x = root_x; display->grab_initial_y = root_y; display->grab_threshold_movement_reached = FALSE; } } grab_window = window; /* If we're trying to move a window, move the first * non-attached dialog instead. */ if (meta_grab_op_is_moving (op)) grab_window = get_first_freefloating_window (window); g_assert (grab_window != NULL); g_assert (op != META_GRAB_OP_NONE); display->grab_have_pointer = FALSE; if (pointer_already_grabbed) display->grab_have_pointer = TRUE; if (META_IS_BACKEND_X11 (meta_get_backend ()) && display->x11_display) { /* Since grab operations often happen as a result of implicit * pointer operations on the display X11 connection, we need * to ungrab here to ensure that the backend's X11 can take * the device grab. */ XIUngrabDevice (display->x11_display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, timestamp); XSync (display->x11_display->xdisplay, False); } if (meta_backend_grab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp)) display->grab_have_pointer = TRUE; if (!display->grab_have_pointer && !meta_grab_op_is_keyboard (op)) { meta_topic (META_DEBUG_WINDOW_OPS, "XIGrabDevice() failed\n"); return FALSE; } /* Grab keys when beginning window ops; see #126497 */ if (event_route == META_EVENT_ROUTE_WINDOW_OP) { display->grab_have_keyboard = meta_window_grab_all_keys (grab_window, timestamp); if (!display->grab_have_keyboard) { meta_topic (META_DEBUG_WINDOW_OPS, "grabbing all keys failed, ungrabbing pointer\n"); meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp); display->grab_have_pointer = FALSE; return FALSE; } } display->event_route = event_route; display->grab_op = op; display->grab_window = grab_window; display->grab_button = button; display->grab_tile_mode = grab_window->tile_mode; display->grab_tile_monitor_number = grab_window->tile_monitor_number; display->grab_anchor_root_x = root_x; display->grab_anchor_root_y = root_y; display->grab_latest_motion_x = root_x; display->grab_latest_motion_y = root_y; display->grab_last_moveresize_time = 0; display->grab_last_user_action_was_snap = FALSE; display->grab_frame_action = frame_action; meta_display_update_cursor (display); g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove); meta_topic (META_DEBUG_WINDOW_OPS, "Grab op %u on window %s successful\n", display->grab_op, window ? window->desc : "(null)"); meta_window_get_frame_rect (display->grab_window, &display->grab_initial_window_pos); display->grab_anchor_window_pos = display->grab_initial_window_pos; if (meta_is_wayland_compositor ()) { meta_display_sync_wayland_input_focus (display); meta_display_cancel_touch (display); } g_signal_emit (display, display_signals[GRAB_OP_BEGIN], 0, display, display->grab_window, display->grab_op); if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) meta_window_grab_op_began (display->grab_window, display->grab_op); return TRUE; } void meta_display_end_grab_op (MetaDisplay *display, guint32 timestamp) { MetaWindow *grab_window = display->grab_window; MetaGrabOp grab_op = display->grab_op; meta_topic (META_DEBUG_WINDOW_OPS, "Ending grab op %u at time %u\n", grab_op, timestamp); if (display->event_route == META_EVENT_ROUTE_NORMAL || display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB) return; g_assert (grab_window != NULL); /* We need to reset this early, since the * meta_window_grab_op_ended callback relies on this being * up to date. */ display->grab_op = META_GRAB_OP_NONE; if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) { /* Clear out the edge cache */ meta_display_cleanup_edges (display); /* Only raise the window in orthogonal raise * ('do-not-raise-on-click') mode if the user didn't try to move * or resize the given window by at least a threshold amount. * For raise on click mode, the window was raised at the * beginning of the grab_op. */ if (!meta_prefs_get_raise_on_click () && !display->grab_threshold_movement_reached) meta_window_raise (display->grab_window); meta_window_grab_op_ended (grab_window, grab_op); } if (display->grab_have_pointer) { MetaBackend *backend = meta_get_backend (); meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp); } if (display->grab_have_keyboard) { meta_topic (META_DEBUG_WINDOW_OPS, "Ungrabbing all keys timestamp %u\n", timestamp); meta_window_ungrab_all_keys (grab_window, timestamp); } display->event_route = META_EVENT_ROUTE_NORMAL; display->grab_window = NULL; display->grab_tile_mode = META_TILE_NONE; display->grab_tile_monitor_number = -1; memset(&display->grab_initial_window_pos, 0, sizeof(MetaRectangle)); meta_display_update_cursor (display); g_clear_handle_id (&display->grab_resize_timeout_id, g_source_remove); if (meta_is_wayland_compositor ()) meta_display_sync_wayland_input_focus (display); g_signal_emit (display, display_signals[GRAB_OP_END], 0, display, grab_window, grab_op); } /** * meta_display_get_grab_op: * @display: The #MetaDisplay that the window is on * Gets the current grab operation, if any. * * Return value: the current grab operation, or %META_GRAB_OP_NONE if * Mutter doesn't currently have a grab. %META_GRAB_OP_COMPOSITOR will * be returned if a compositor-plugin modal operation is in effect * (See mutter_begin_modal_for_plugin()) */ MetaGrabOp meta_display_get_grab_op (MetaDisplay *display) { return display->grab_op; } void meta_display_check_threshold_reached (MetaDisplay *display, int x, int y) { /* Don't bother doing the check again if we've already reached the threshold */ if (meta_prefs_get_raise_on_click () || display->grab_threshold_movement_reached) return; if (ABS (display->grab_initial_x - x) >= 8 || ABS (display->grab_initial_y - y) >= 8) display->grab_threshold_movement_reached = TRUE; } void meta_display_queue_retheme_all_windows (MetaDisplay *display) { GSList* windows; GSList *tmp; windows = meta_display_list_windows (display, META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *window = tmp->data; meta_window_queue (window, META_QUEUE_MOVE_RESIZE); meta_window_frame_size_changed (window); if (window->frame) { meta_frame_queue_draw (window->frame); } tmp = tmp->next; } g_slist_free (windows); } /* * Stores whether syncing is currently enabled. */ static gboolean is_syncing = FALSE; /** * meta_is_syncing: * * Returns whether X synchronisation is currently enabled. * * FIXME: This is *only* called by meta_display_open(), but by that time * we have already turned syncing on or off on startup, and we don't * have any way to do so while Mutter is running, so it's rather * pointless. * * Returns: %TRUE if we must wait for events whenever we send X requests; * %FALSE otherwise. */ gboolean meta_is_syncing (void) { return is_syncing; } /** * meta_set_syncing: * @setting: whether to turn syncing on or off * * A handy way to turn on synchronisation on or off for every display. */ void meta_set_syncing (gboolean setting) { if (setting != is_syncing) { is_syncing = setting; if (meta_get_display ()) XSynchronize (meta_get_display ()->x11_display->xdisplay, is_syncing); } } /** * meta_display_ping_timeout: * @data: All the information about this ping. It is a #MetaPingData * cast to a #gpointer in order to be passable to a timeout function. * This function will also free this parameter. * * Does whatever it is we decided to do when a window didn't respond * to a ping. We also remove the ping from the display's list of * pending pings. This function is called by the event loop when the timeout * times out which we created at the start of the ping. * * Returns: Always returns %FALSE, because this function is called as a * timeout and we don't want to run the timer again. */ static gboolean meta_display_ping_timeout (gpointer data) { MetaPingData *ping_data = data; MetaWindow *window = ping_data->window; MetaDisplay *display = window->display; meta_window_set_alive (window, FALSE); ping_data->ping_timeout_id = 0; meta_topic (META_DEBUG_PING, "Ping %u on window %s timed out\n", ping_data->serial, ping_data->window->desc); display->pending_pings = g_slist_remove (display->pending_pings, ping_data); ping_data_free (ping_data); return FALSE; } /** * meta_display_ping_window: * @display: The #MetaDisplay that the window is on * @window: The #MetaWindow to send the ping to * @timestamp: The timestamp of the ping. Used for uniqueness. * Cannot be CurrentTime; use a real timestamp! * * Sends a ping request to a window. The window must respond to * the request within a certain amount of time. If it does, we * will call one callback; if the time passes and we haven't had * a response, we call a different callback. The window must have * the hint showing that it can respond to a ping; if it doesn't, * we call the "got a response" callback immediately and return. * This function returns straight away after setting things up; * the callbacks will be called from the event loop. */ void meta_display_ping_window (MetaWindow *window, guint32 serial) { GSList *l; MetaDisplay *display = window->display; MetaPingData *ping_data; unsigned int check_alive_timeout; check_alive_timeout = meta_prefs_get_check_alive_timeout (); if (check_alive_timeout == 0) return; if (serial == 0) { meta_warning ("Tried to ping window %s with a bad serial! Not allowed.\n", window->desc); return; } if (!meta_window_can_ping (window)) return; for (l = display->pending_pings; l; l = l->next) { MetaPingData *ping_data = l->data; if (window == ping_data->window) { meta_topic (META_DEBUG_PING, "Window %s already is being pinged with serial %u\n", window->desc, ping_data->serial); return; } if (serial == ping_data->serial) { meta_warning ("Ping serial %u was reused for window %s, " "previous use was for window %s.\n", serial, window->desc, ping_data->window->desc); return; } } ping_data = g_new (MetaPingData, 1); ping_data->window = window; ping_data->serial = serial; ping_data->ping_timeout_id = g_timeout_add (check_alive_timeout, meta_display_ping_timeout, ping_data); g_source_set_name_by_id (ping_data->ping_timeout_id, "[mutter] meta_display_ping_timeout"); display->pending_pings = g_slist_prepend (display->pending_pings, ping_data); meta_topic (META_DEBUG_PING, "Sending ping with serial %u to window %s\n", serial, window->desc); META_WINDOW_GET_CLASS (window)->ping (window, serial); } /** * meta_display_pong_for_serial: * @display: the display we got the pong from * @serial: the serial in the pong repsonse * * Process the pong (the response message) from the ping we sent * to the window. This involves removing the timeout, calling the * reply handler function, and freeing memory. */ void meta_display_pong_for_serial (MetaDisplay *display, guint32 serial) { GSList *tmp; meta_topic (META_DEBUG_PING, "Received a pong with serial %u\n", serial); for (tmp = display->pending_pings; tmp; tmp = tmp->next) { MetaPingData *ping_data = tmp->data; if (serial == ping_data->serial) { meta_topic (META_DEBUG_PING, "Matching ping found for pong %u\n", ping_data->serial); /* Remove the ping data from the list */ display->pending_pings = g_slist_remove (display->pending_pings, ping_data); /* Remove the timeout */ g_clear_handle_id (&ping_data->ping_timeout_id, g_source_remove); meta_window_set_alive (ping_data->window, TRUE); ping_data_free (ping_data); break; } } } static MetaGroup * get_focused_group (MetaDisplay *display) { if (display->focus_window) return display->focus_window->group; else return NULL; } #define IN_TAB_CHAIN(w,t) (((t) == META_TAB_LIST_NORMAL && META_WINDOW_IN_NORMAL_TAB_CHAIN (w)) \ || ((t) == META_TAB_LIST_DOCKS && META_WINDOW_IN_DOCK_TAB_CHAIN (w)) \ || ((t) == META_TAB_LIST_GROUP && META_WINDOW_IN_GROUP_TAB_CHAIN (w, get_focused_group (w->display))) \ || ((t) == META_TAB_LIST_NORMAL_ALL && META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w))) static MetaWindow* find_tab_forward (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, GList *start, gboolean skip_first) { GList *tmp; g_return_val_if_fail (start != NULL, NULL); g_return_val_if_fail (workspace != NULL, NULL); tmp = start; if (skip_first) tmp = tmp->next; while (tmp != NULL) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type)) return window; tmp = tmp->next; } tmp = workspace->mru_list; while (tmp != start) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type)) return window; tmp = tmp->next; } return NULL; } static MetaWindow* find_tab_backward (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, GList *start, gboolean skip_last) { GList *tmp; g_return_val_if_fail (start != NULL, NULL); g_return_val_if_fail (workspace != NULL, NULL); tmp = start; if (skip_last) tmp = tmp->prev; while (tmp != NULL) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type)) return window; tmp = tmp->prev; } tmp = g_list_last (workspace->mru_list); while (tmp != start) { MetaWindow *window = tmp->data; if (IN_TAB_CHAIN (window, type)) return window; tmp = tmp->prev; } return NULL; } static int mru_cmp (gconstpointer a, gconstpointer b) { guint32 time_a, time_b; time_a = meta_window_get_user_time ((MetaWindow *)a); time_b = meta_window_get_user_time ((MetaWindow *)b); if (time_a > time_b) return -1; else if (time_a < time_b) return 1; else return 0; } /** * meta_display_get_tab_list: * @display: a #MetaDisplay * @type: type of tab list * @workspace: (nullable): origin workspace * * Determine the list of windows that should be displayed for Alt-TAB * functionality. The windows are returned in most recently used order. * If @workspace is not %NULL, the list only conains windows that are on * @workspace or have the demands-attention hint set; otherwise it contains * all windows. * * Returns: (transfer container) (element-type Meta.Window): List of windows */ GList* meta_display_get_tab_list (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace) { GList *tab_list = NULL; GList *global_mru_list = NULL; GList *mru_list, *tmp; GSList *windows = meta_display_list_windows (display, META_LIST_DEFAULT); GSList *w; if (workspace == NULL) { /* Yay for mixing GList and GSList in the API */ for (w = windows; w; w = w->next) global_mru_list = g_list_prepend (global_mru_list, w->data); global_mru_list = g_list_sort (global_mru_list, mru_cmp); } mru_list = workspace ? workspace->mru_list : global_mru_list; /* Windows sellout mode - MRU order. Collect unminimized windows * then minimized so minimized windows aren't in the way so much. */ for (tmp = mru_list; tmp; tmp = tmp->next) { MetaWindow *window = tmp->data; if (!window->minimized && IN_TAB_CHAIN (window, type)) tab_list = g_list_prepend (tab_list, window); } for (tmp = mru_list; tmp; tmp = tmp->next) { MetaWindow *window = tmp->data; if (window->minimized && IN_TAB_CHAIN (window, type)) tab_list = g_list_prepend (tab_list, window); } tab_list = g_list_reverse (tab_list); /* If filtering by workspace, include windows from * other workspaces that demand attention */ if (workspace) for (w = windows; w; w = w->next) { MetaWindow *l_window = w->data; if (l_window->wm_state_demands_attention && !meta_window_located_on_workspace (l_window, workspace) && IN_TAB_CHAIN (l_window, type)) tab_list = g_list_prepend (tab_list, l_window); } g_list_free (global_mru_list); g_slist_free (windows); return tab_list; } /** * meta_display_get_tab_next: * @display: a #MetaDisplay * @type: type of tab list * @workspace: origin workspace * @window: (nullable): starting window * @backward: If %TRUE, look for the previous window. * * Determine the next window that should be displayed for Alt-TAB * functionality. * * Returns: (transfer none): Next window * */ MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, MetaWindow *window, gboolean backward) { gboolean skip; GList *tab_list; MetaWindow *ret; tab_list = meta_display_get_tab_list (display, type, workspace); if (tab_list == NULL) return NULL; if (window != NULL) { g_assert (window->display == display); if (backward) ret = find_tab_backward (display, type, workspace, g_list_find (tab_list, window), TRUE); else ret = find_tab_forward (display, type, workspace, g_list_find (tab_list, window), TRUE); } else { skip = display->focus_window != NULL && tab_list->data == display->focus_window; if (backward) ret = find_tab_backward (display, type, workspace, tab_list, skip); else ret = find_tab_forward (display, type, workspace, tab_list, skip); } g_list_free (tab_list); return ret; } /** * meta_display_get_tab_current: * @display: a #MetaDisplay * @type: type of tab list * @workspace: origin workspace * * Determine the active window that should be displayed for Alt-TAB. * * Returns: (transfer none): Current window * */ MetaWindow* meta_display_get_tab_current (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace) { MetaWindow *window; window = display->focus_window; if (window != NULL && IN_TAB_CHAIN (window, type) && (workspace == NULL || meta_window_located_on_workspace (window, workspace))) return window; else return NULL; } MetaGravity meta_resize_gravity_from_grab_op (MetaGrabOp op) { MetaGravity gravity; gravity = -1; switch (op) { case META_GRAB_OP_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_SE: gravity = META_GRAVITY_NORTH_WEST; break; case META_GRAB_OP_KEYBOARD_RESIZING_S: case META_GRAB_OP_RESIZING_S: gravity = META_GRAVITY_NORTH; break; case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_RESIZING_SW: gravity = META_GRAVITY_NORTH_EAST; break; case META_GRAB_OP_KEYBOARD_RESIZING_N: case META_GRAB_OP_RESIZING_N: gravity = META_GRAVITY_SOUTH; break; case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_RESIZING_NE: gravity = META_GRAVITY_SOUTH_WEST; break; case META_GRAB_OP_KEYBOARD_RESIZING_NW: case META_GRAB_OP_RESIZING_NW: gravity = META_GRAVITY_SOUTH_EAST; break; case META_GRAB_OP_KEYBOARD_RESIZING_E: case META_GRAB_OP_RESIZING_E: gravity = META_GRAVITY_WEST; break; case META_GRAB_OP_KEYBOARD_RESIZING_W: case META_GRAB_OP_RESIZING_W: gravity = META_GRAVITY_EAST; break; case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: gravity = META_GRAVITY_CENTER; break; default: break; } return gravity; } void meta_display_manage_all_xwindows (MetaDisplay *display) { guint64 *_children; guint64 *children; int n_children, i; meta_stack_freeze (display->stack); meta_stack_tracker_get_stack (display->stack_tracker, &_children, &n_children); /* Copy the stack as it will be modified as part of the loop */ children = g_memdup2 (_children, sizeof (uint64_t) * n_children); for (i = 0; i < n_children; ++i) { if (!META_STACK_ID_IS_X11 (children[i])) continue; meta_window_x11_new (display, children[i], TRUE, META_COMP_EFFECT_NONE); } g_free (children); meta_stack_thaw (display->stack); } void meta_display_unmanage_windows (MetaDisplay *display, guint32 timestamp) { GSList *tmp; GSList *winlist; winlist = meta_display_list_windows (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT); winlist = g_slist_sort (winlist, meta_display_stack_cmp); g_slist_foreach (winlist, (GFunc)g_object_ref, NULL); /* Unmanage all windows */ tmp = winlist; while (tmp != NULL) { MetaWindow *window = tmp->data; /* Check if already unmanaged for safety - in particular, catch * the case where unmanaging a parent window can cause attached * dialogs to be (temporarily) unmanaged. */ if (!window->unmanaging) meta_window_unmanage (window, timestamp); g_object_unref (window); tmp = tmp->next; } g_slist_free (winlist); } int meta_display_stack_cmp (const void *a, const void *b) { MetaWindow *aw = (void*) a; MetaWindow *bw = (void*) b; return meta_stack_windows_cmp (aw->display->stack, aw, bw); } /** * meta_display_sort_windows_by_stacking: * @display: a #MetaDisplay * @windows: (element-type MetaWindow): Set of windows * * Sorts a set of windows according to their current stacking order. If windows * from multiple screens are present in the set of input windows, then all the * windows on screen 0 are sorted below all the windows on screen 1, and so forth. * Since the stacking order of override-redirect windows isn't controlled by * Metacity, if override-redirect windows are in the input, the result may not * correspond to the actual stacking order in the X server. * * An example of using this would be to sort the list of transient dialogs for a * window into their current stacking order. * * Returns: (transfer container) (element-type MetaWindow): Input windows sorted by stacking order, from lowest to highest */ GSList * meta_display_sort_windows_by_stacking (MetaDisplay *display, GSList *windows) { GSList *copy = g_slist_copy (windows); copy = g_slist_sort (copy, meta_display_stack_cmp); return copy; } static void prefs_changed_callback (MetaPreference pref, void *data) { MetaDisplay *display = data; if (pref == META_PREF_CURSOR_THEME || pref == META_PREF_CURSOR_SIZE) { meta_display_reload_cursor (display); } } void meta_display_sanity_check_timestamps (MetaDisplay *display, guint32 timestamp) { if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_focus_time)) { meta_warning ("last_focus_time (%u) is greater than comparison " "timestamp (%u). This most likely represents a buggy " "client sending inaccurate timestamps in messages such as " "_NET_ACTIVE_WINDOW. Trying to work around...\n", display->last_focus_time, timestamp); display->last_focus_time = timestamp; } if (XSERVER_TIME_IS_BEFORE (timestamp, display->last_user_time)) { GSList *windows; GSList *tmp; meta_warning ("last_user_time (%u) is greater than comparison " "timestamp (%u). This most likely represents a buggy " "client sending inaccurate timestamps in messages such as " "_NET_ACTIVE_WINDOW. Trying to work around...\n", display->last_user_time, timestamp); display->last_user_time = timestamp; windows = meta_display_list_windows (display, META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *window = tmp->data; if (XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time)) { meta_warning ("%s appears to be one of the offending windows " "with a timestamp of %u. Working around...\n", window->desc, window->net_wm_user_time); meta_window_set_user_time (window, timestamp); } tmp = tmp->next; } g_slist_free (windows); } } void meta_display_remove_autoraise_callback (MetaDisplay *display) { g_clear_handle_id (&display->autoraise_timeout_id, g_source_remove); display->autoraise_window = NULL; } void meta_display_overlay_key_activate (MetaDisplay *display) { g_signal_emit (display, display_signals[OVERLAY_KEY], 0); } void meta_display_accelerator_activate (MetaDisplay *display, guint action, ClutterKeyEvent *event) { g_signal_emit (display, display_signals[ACCELERATOR_ACTIVATED], 0, action, clutter_event_get_source_device ((ClutterEvent *) event), event->time); } gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display) { gboolean freeze; g_signal_emit (display, display_signals[MODIFIERS_ACCELERATOR_ACTIVATED], 0, &freeze); return freeze; } /** * meta_display_supports_extended_barriers: * @display: a #MetaDisplay * * Returns: whether pointer barriers can be supported. * * When running as an X compositor the X server needs XInput 2 * version 2.3. When running as a display server it is supported * when running on the native backend. * * Clients should use this method to determine whether their * interfaces should depend on new barrier features. */ gboolean meta_display_supports_extended_barriers (MetaDisplay *display) { #ifdef HAVE_NATIVE_BACKEND if (META_IS_BACKEND_NATIVE (meta_get_backend ())) return TRUE; #endif if (META_IS_BACKEND_X11_CM (meta_get_backend ())) { if (meta_is_wayland_compositor()) return FALSE; return META_X11_DISPLAY_HAS_XINPUT_23 (display->x11_display); } return FALSE; } /** * meta_display_get_compositor: (skip) * @display: a #MetaDisplay * */ MetaCompositor * meta_display_get_compositor (MetaDisplay *display) { return display->compositor; } /** * meta_display_get_x11_display: (skip) * @display: a #MetaDisplay * */ MetaX11Display * meta_display_get_x11_display (MetaDisplay *display) { return display->x11_display; } /** * meta_display_get_size: * @display: A #MetaDisplay * @width: (out): The width of the screen * @height: (out): The height of the screen * * Retrieve the size of the display. */ void meta_display_get_size (MetaDisplay *display, int *width, int *height) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); int display_width, display_height; meta_monitor_manager_get_screen_size (monitor_manager, &display_width, &display_height); if (width != NULL) *width = display_width; if (height != NULL) *height = display_height; } /** * meta_display_get_focus_window: * @display: a #MetaDisplay * * Get our best guess as to the "currently" focused window (that is, * the window that we expect will be focused at the point when the X * server processes our next request). * * Return Value: (transfer none): The current focus window */ MetaWindow * meta_display_get_focus_window (MetaDisplay *display) { return display->focus_window; } /** * meta_display_clear_mouse_mode: * @display: a #MetaDisplay * * Sets the mouse-mode flag to %FALSE, which means that motion events are * no longer ignored in mouse or sloppy focus. * This is an internal function. It should be used only for reimplementing * keybindings, and only in a manner compatible with core code. */ void meta_display_clear_mouse_mode (MetaDisplay *display) { display->mouse_mode = FALSE; } MetaGestureTracker * meta_display_get_gesture_tracker (MetaDisplay *display) { return display->gesture_tracker; } gboolean meta_display_show_restart_message (MetaDisplay *display, const char *message) { gboolean result = FALSE; g_signal_emit (display, display_signals[SHOW_RESTART_MESSAGE], 0, message, &result); return result; } gboolean meta_display_request_restart (MetaDisplay *display) { gboolean result = FALSE; g_signal_emit (display, display_signals[RESTART], 0, &result); return result; } gboolean meta_display_show_resize_popup (MetaDisplay *display, gboolean show, MetaRectangle *rect, int display_w, int display_h) { gboolean result = FALSE; g_signal_emit (display, display_signals[SHOW_RESIZE_POPUP], 0, show, rect, display_w, display_h, &result); return result; } /** * meta_display_is_pointer_emulating_sequence: * @display: the display * @sequence: (nullable): a #ClutterEventSequence * * Tells whether the event sequence is the used for pointer emulation * and single-touch interaction. * * Returns: #TRUE if the sequence emulates pointer behavior **/ gboolean meta_display_is_pointer_emulating_sequence (MetaDisplay *display, ClutterEventSequence *sequence) { if (!sequence) return FALSE; return display->pointer_emulating_sequence == sequence; } void meta_display_request_pad_osd (MetaDisplay *display, ClutterInputDevice *pad, gboolean edition_mode) { MetaBackend *backend = meta_get_backend (); MetaInputSettings *input_settings; const gchar *layout_path = NULL; ClutterActor *osd; MetaLogicalMonitor *logical_monitor; GSettings *settings; #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; #endif /* Avoid emitting the signal while there is an OSD being currently * displayed, the first OSD will have to be dismissed before showing * any other one. */ if (display->current_pad_osd) return; input_settings = meta_backend_get_input_settings (meta_get_backend ()); if (input_settings) { settings = meta_input_settings_get_tablet_settings (input_settings, pad); logical_monitor = meta_input_settings_get_tablet_logical_monitor (input_settings, pad); #ifdef HAVE_LIBWACOM wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (pad)); layout_path = libwacom_get_layout_filename (wacom_device); #endif } if (!layout_path || !settings) return; if (!logical_monitor) logical_monitor = meta_backend_get_current_logical_monitor (backend); g_signal_emit (display, display_signals[SHOW_PAD_OSD], 0, pad, settings, layout_path, edition_mode, logical_monitor->number, &osd); if (osd) { display->current_pad_osd = osd; g_object_add_weak_pointer (G_OBJECT (display->current_pad_osd), (gpointer *) &display->current_pad_osd); } g_object_unref (settings); } gchar * meta_display_get_pad_action_label (MetaDisplay *display, ClutterInputDevice *pad, MetaPadActionType action_type, guint action_number) { MetaInputSettings *settings; gchar *label; /* First, lookup the action, as imposed by settings */ settings = meta_backend_get_input_settings (meta_get_backend ()); label = meta_input_settings_get_pad_action_label (settings, pad, action_type, action_number); if (label) return label; #ifdef HAVE_WAYLAND /* Second, if this wayland, lookup the actions set by the clients */ if (meta_is_wayland_compositor ()) { MetaWaylandCompositor *compositor; MetaWaylandTabletSeat *tablet_seat; MetaWaylandTabletPad *tablet_pad = NULL; compositor = meta_wayland_compositor_get_default (); tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, compositor->seat); if (tablet_seat) tablet_pad = meta_wayland_tablet_seat_lookup_pad (tablet_seat, pad); if (tablet_pad) { label = meta_wayland_tablet_pad_get_label (tablet_pad, action_type, action_number); } if (label) return label; } #endif return NULL; } static void meta_display_show_osd (MetaDisplay *display, gint monitor_idx, const gchar *icon_name, const gchar *message) { g_signal_emit (display, display_signals[SHOW_OSD], 0, monitor_idx, icon_name, message); } static gint lookup_tablet_monitor (MetaDisplay *display, ClutterInputDevice *device) { MetaInputSettings *input_settings; MetaLogicalMonitor *monitor; gint monitor_idx = -1; input_settings = meta_backend_get_input_settings (meta_get_backend ()); if (!input_settings) return -1; monitor = meta_input_settings_get_tablet_logical_monitor (input_settings, device); if (monitor) { monitor_idx = meta_display_get_monitor_index_for_rect (display, &monitor->rect); } return monitor_idx; } void meta_display_show_tablet_mapping_notification (MetaDisplay *display, ClutterInputDevice *pad, const gchar *pretty_name) { if (!pretty_name) pretty_name = clutter_input_device_get_device_name (pad); meta_display_show_osd (display, lookup_tablet_monitor (display, pad), "input-tablet-symbolic", pretty_name); } void meta_display_notify_pad_group_switch (MetaDisplay *display, ClutterInputDevice *pad, const gchar *pretty_name, guint n_group, guint n_mode, guint n_modes) { GString *message; guint i; if (!pretty_name) pretty_name = clutter_input_device_get_device_name (pad); message = g_string_new (pretty_name); g_string_append_c (message, '\n'); for (i = 0; i < n_modes; i++) g_string_append (message, (i == n_mode) ? "⚫" : "⚪"); meta_display_show_osd (display, lookup_tablet_monitor (display, pad), "input-tablet-symbolic", message->str); g_signal_emit (display, display_signals[PAD_MODE_SWITCH], 0, pad, n_group, n_mode); g_string_free (message, TRUE); } void meta_display_foreach_window (MetaDisplay *display, MetaListWindowsFlags flags, MetaDisplayWindowFunc func, gpointer data) { GSList *windows; /* If we end up doing this often, just keeping a list * of windows might be sensible. */ windows = meta_display_list_windows (display, flags); g_slist_foreach (windows, (GFunc) func, data); g_slist_free (windows); } static void meta_display_resize_func (MetaWindow *window, gpointer user_data) { if (window->struts) { meta_window_update_struts (window); } meta_window_queue (window, META_QUEUE_MOVE_RESIZE); meta_window_recalc_features (window); } static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager, MetaDisplay *display) { MetaBackend *backend; MetaCursorRenderer *cursor_renderer; meta_workspace_manager_reload_work_areas (display->workspace_manager); /* Fix up monitor for all windows on this display */ meta_display_foreach_window (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT, (MetaDisplayWindowFunc) meta_window_update_for_monitors_changed, 0); /* Queue a resize on all the windows */ meta_display_foreach_window (display, META_LIST_DEFAULT, meta_display_resize_func, 0); meta_display_queue_check_fullscreen (display); backend = meta_get_backend (); cursor_renderer = meta_backend_get_cursor_renderer (backend); meta_cursor_renderer_force_update (cursor_renderer); } void meta_display_restacked (MetaDisplay *display) { g_signal_emit (display, display_signals[RESTACKED], 0); } static gboolean meta_display_update_tile_preview_timeout (gpointer data) { MetaDisplay *display = data; MetaWindow *window = display->grab_window; gboolean needs_preview = FALSE; display->tile_preview_timeout_id = 0; if (window) { switch (display->preview_tile_mode) { case META_TILE_LEFT: case META_TILE_RIGHT: case META_TILE_TOP: case META_TILE_BOTTOM: case META_TILE_ULC: case META_TILE_URC: case META_TILE_LRC: case META_TILE_LLC: if (!META_WINDOW_TILED (window)) needs_preview = TRUE; break; case META_TILE_MAXIMIZED: if (!META_WINDOW_MAXIMIZED (window)) needs_preview = TRUE; break; default: needs_preview = FALSE; break; } } if (needs_preview) { MetaRectangle tile_rect; int monitor; monitor = meta_window_get_current_tile_monitor_number (window); meta_window_get_tile_area (window, display->preview_tile_mode, &tile_rect); meta_compositor_show_tile_preview (display->compositor, window, &tile_rect, monitor); } else meta_compositor_hide_tile_preview (display->compositor); return FALSE; } #define TILE_PREVIEW_TIMEOUT_MS 200 void meta_display_update_tile_preview (MetaDisplay *display, gboolean delay) { if (delay) { if (display->tile_preview_timeout_id > 0) return; display->tile_preview_timeout_id = g_timeout_add (TILE_PREVIEW_TIMEOUT_MS, meta_display_update_tile_preview_timeout, display); g_source_set_name_by_id (display->tile_preview_timeout_id, "[mutter] meta_display_update_tile_preview_timeout"); } else { g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove); meta_display_update_tile_preview_timeout ((gpointer)display); } } void meta_display_hide_tile_preview (MetaDisplay *display) { g_clear_handle_id (&display->tile_preview_timeout_id, g_source_remove); display->preview_tile_mode = META_TILE_NONE; meta_compositor_hide_tile_preview (display->compositor); } static MetaStartupSequence * find_startup_sequence_by_wmclass (MetaDisplay *display, MetaWindow *window) { GSList *startup_sequences, *l; startup_sequences = meta_startup_notification_get_sequences (display->startup_notification); for (l = startup_sequences; l; l = l->next) { MetaStartupSequence *sequence = l->data; const char *wmclass; wmclass = meta_startup_sequence_get_wmclass (sequence); if (wmclass != NULL && ((window->res_class && strcmp (wmclass, window->res_class) == 0) || (window->res_name && strcmp (wmclass, window->res_name) == 0))) return sequence; } return NULL; } /* Sets the initial_timestamp and initial_workspace properties * of a window according to information given us by the * startup-notification library. * * Returns TRUE if startup properties have been applied, and * FALSE if they have not (for example, if they had already * been applied.) */ gboolean meta_display_apply_startup_properties (MetaDisplay *display, MetaWindow *window) { const char *startup_id; MetaStartupSequence *sequence = NULL; /* Does the window have a startup ID stored? */ startup_id = meta_window_get_startup_id (window); meta_topic (META_DEBUG_STARTUP, "Applying startup props to %s id \"%s\"\n", window->desc, startup_id ? startup_id : "(none)"); if (!startup_id) { /* No startup ID stored for the window. Let's ask the * startup-notification library whether there's anything * stored for the resource name or resource class hints. */ sequence = find_startup_sequence_by_wmclass (display, window); if (sequence) { g_assert (window->startup_id == NULL); window->startup_id = g_strdup (meta_startup_sequence_get_id (sequence)); startup_id = window->startup_id; meta_topic (META_DEBUG_STARTUP, "Ending legacy sequence %s due to window %s\n", meta_startup_sequence_get_id (sequence), window->desc); meta_startup_sequence_complete (sequence); } } /* Still no startup ID? Bail. */ if (!startup_id) return FALSE; /* We might get this far and not know the sequence ID (if the window * already had a startup ID stored), so let's look for one if we don't * already know it. */ if (sequence == NULL) { sequence = meta_startup_notification_lookup_sequence (display->startup_notification, startup_id); } if (sequence != NULL) { gboolean changed_something = FALSE; meta_topic (META_DEBUG_STARTUP, "Found startup sequence for window %s ID \"%s\"\n", window->desc, startup_id); if (!window->initial_workspace_set) { int space = meta_startup_sequence_get_workspace (sequence); if (space >= 0) { meta_topic (META_DEBUG_STARTUP, "Setting initial window workspace to %d based on startup info\n", space); window->initial_workspace_set = TRUE; window->initial_workspace = space; changed_something = TRUE; } } if (!window->initial_timestamp_set) { guint32 timestamp = meta_startup_sequence_get_timestamp (sequence); meta_topic (META_DEBUG_STARTUP, "Setting initial window timestamp to %u based on startup info\n", timestamp); window->initial_timestamp_set = TRUE; window->initial_timestamp = timestamp; changed_something = TRUE; } return changed_something; } else { meta_topic (META_DEBUG_STARTUP, "Did not find startup sequence for window %s ID \"%s\"\n", window->desc, startup_id); } return FALSE; } static gboolean set_work_area_later_func (MetaDisplay *display) { meta_topic (META_DEBUG_WORKAREA, "Running work area hint computation function\n"); display->work_area_later = 0; g_signal_emit (display, display_signals[WORKAREAS_CHANGED], 0); return FALSE; } void meta_display_queue_workarea_recalc (MetaDisplay *display) { /* Recompute work area later before redrawing */ if (display->work_area_later == 0) { meta_topic (META_DEBUG_WORKAREA, "Adding work area hint computation function\n"); display->work_area_later = meta_later_add (META_LATER_BEFORE_REDRAW, (GSourceFunc) set_work_area_later_func, display, NULL); } } static gboolean check_fullscreen_func (gpointer data) { MetaDisplay *display = data; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; MetaWindow *window; GSList *fullscreen_monitors = NULL; GSList *obscured_monitors = NULL; gboolean in_fullscreen_changed = FALSE; display->check_fullscreen_later = 0; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); /* We consider a monitor in fullscreen if it contains a fullscreen window; * however we make an exception for maximized windows above the fullscreen * one, as in that case window+chrome fully obscure the fullscreen window. */ for (window = meta_stack_get_top (display->stack); window; window = meta_stack_get_below (display->stack, window, FALSE)) { gboolean covers_monitors = FALSE; if (window->hidden) continue; if (window->fullscreen) { covers_monitors = TRUE; } else if (window->override_redirect) { /* We want to handle the case where an application is creating an * override-redirect window the size of the screen (monitor) and treat * it similarly to a fullscreen window, though it doesn't have fullscreen * window management behavior. (Being O-R, it's not managed at all.) */ if (meta_window_is_monitor_sized (window)) covers_monitors = TRUE; } else if (window->maximized_horizontally && window->maximized_vertically) { MetaLogicalMonitor *logical_monitor; logical_monitor = meta_window_get_main_logical_monitor (window); if (!g_slist_find (obscured_monitors, logical_monitor)) obscured_monitors = g_slist_prepend (obscured_monitors, logical_monitor); } if (covers_monitors) { MetaRectangle window_rect; meta_window_get_frame_rect (window, &window_rect); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (meta_rectangle_overlap (&window_rect, &logical_monitor->rect) && !g_slist_find (fullscreen_monitors, logical_monitor) && !g_slist_find (obscured_monitors, logical_monitor)) fullscreen_monitors = g_slist_prepend (fullscreen_monitors, logical_monitor); } } } g_slist_free (obscured_monitors); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; gboolean in_fullscreen; in_fullscreen = g_slist_find (fullscreen_monitors, logical_monitor) != NULL; if (in_fullscreen != logical_monitor->in_fullscreen) { logical_monitor->in_fullscreen = in_fullscreen; in_fullscreen_changed = TRUE; } } g_slist_free (fullscreen_monitors); if (in_fullscreen_changed) { /* DOCK window stacking depends on the monitor's fullscreen status so we need to trigger a re-layering. */ MetaWindow *window = meta_stack_get_top (display->stack); if (window) meta_stack_update_layer (display->stack, window); g_signal_emit (display, display_signals[IN_FULLSCREEN_CHANGED], 0, NULL); } return FALSE; } void meta_display_queue_check_fullscreen (MetaDisplay *display) { if (!display->check_fullscreen_later) display->check_fullscreen_later = meta_later_add (META_LATER_CHECK_FULLSCREEN, check_fullscreen_func, display, NULL); } int meta_display_get_monitor_index_for_rect (MetaDisplay *display, MetaRectangle *rect) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; logical_monitor = meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, rect); if (!logical_monitor) return -1; return logical_monitor->number; } int meta_display_get_monitor_neighbor_index (MetaDisplay *display, int which_monitor, MetaDisplayDirection direction) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; MetaLogicalMonitor *neighbor; logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, which_monitor); neighbor = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, direction); return neighbor ? neighbor->number : -1; } /** * meta_display_get_current_monitor: * @display: a #MetaDisplay * * Gets the index of the monitor that currently has the mouse pointer. * * Return value: a monitor index */ int meta_display_get_current_monitor (MetaDisplay *display) { MetaBackend *backend = meta_get_backend (); MetaLogicalMonitor *logical_monitor; logical_monitor = meta_backend_get_current_logical_monitor (backend); /* Pretend its the first when there is no actual current monitor. */ if (!logical_monitor) return 0; return logical_monitor->number; } /** * meta_display_get_n_monitors: * @display: a #MetaDisplay * * Gets the number of monitors that are joined together to form @display. * * Return value: the number of monitors */ int meta_display_get_n_monitors (MetaDisplay *display) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); g_return_val_if_fail (META_IS_DISPLAY (display), 0); return meta_monitor_manager_get_num_logical_monitors (monitor_manager); } /** * meta_display_get_primary_monitor: * @display: a #MetaDisplay * * Gets the index of the primary monitor on this @display. * * Return value: a monitor index */ int meta_display_get_primary_monitor (MetaDisplay *display) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; g_return_val_if_fail (META_IS_DISPLAY (display), 0); logical_monitor = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); if (logical_monitor) return logical_monitor->number; else return 0; } /** * meta_display_get_monitor_name: * @display: a #MetaDisplay * @monitor: the monitor number * * Return value: (transfer none): the monitor vendor name */ const gchar* meta_display_get_monitor_name (MetaDisplay *display, int monitor) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); #ifndef G_DISABLE_CHECKS int n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); #endif g_return_val_if_fail (META_IS_DISPLAY (display), NULL); g_return_val_if_fail (monitor >= 0 && monitor < n_logical_monitors, NULL); GList *l; for (l = monitor_manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *other = l->data; if (other->number != monitor) { continue; } GList *m; for (m = other->monitors; m; m = m->next) { MetaMonitor *monitor = m->data; const char* name = meta_monitor_get_display_name (monitor); if (name != NULL) { return name; } } } return NULL; } /** * meta_display_get_monitor_geometry: * @display: a #MetaDisplay * @monitor: the monitor number * @geometry: (out): location to store the monitor geometry * * Stores the location and size of the indicated @monitor in @geometry. */ void meta_display_get_monitor_geometry (MetaDisplay *display, int monitor, MetaRectangle *geometry) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; #ifndef G_DISABLE_CHECKS int n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); #endif g_return_if_fail (META_IS_DISPLAY (display)); g_return_if_fail (monitor >= 0 && monitor < n_logical_monitors); g_return_if_fail (geometry != NULL); logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, monitor); *geometry = logical_monitor->rect; } /** * meta_display_get_monitor_scale: * @display: a #MetaDisplay * @monitor: the monitor number * * Gets the monitor scaling value for the given @monitor. * * Return value: the monitor scaling value */ float meta_display_get_monitor_scale (MetaDisplay *display, int monitor) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; #ifndef G_DISABLE_CHECKS int n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); #endif g_return_val_if_fail (META_IS_DISPLAY (display), 1.0f); g_return_val_if_fail (monitor >= 0 && monitor < n_logical_monitors, 1.0f); logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, monitor); return logical_monitor->scale; } /** * meta_display_get_monitor_in_fullscreen: * @display: a #MetaDisplay * @monitor: the monitor number * * Determines whether there is a fullscreen window obscuring the specified * monitor. If there is a fullscreen window, the desktop environment will * typically hide any controls that might obscure the fullscreen window. * * You can get notification when this changes by connecting to * MetaDisplay::in-fullscreen-changed. * * Returns: %TRUE if there is a fullscreen window covering the specified monitor. */ gboolean meta_display_get_monitor_in_fullscreen (MetaDisplay *display, int monitor) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; #ifndef G_DISABLE_CHECKS int n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); #endif g_return_val_if_fail (META_IS_DISPLAY (display), FALSE); g_return_val_if_fail (monitor >= 0 && monitor < n_logical_monitors, FALSE); logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, monitor); /* We use -1 as a flag to mean "not known yet" for notification purposes */ return logical_monitor->in_fullscreen == TRUE; } /** * meta_display_get_pointer_window: * @display: the #MetaDisplay. * @not_this_one: (nullable): window to be excluded * * Gets the #MetaWindow pointed by the mouse * * Return value: (transfer none): the #MetaWindow pointed by the mouse * %NULL when window not found */ MetaWindow * meta_display_get_pointer_window (MetaDisplay *display, MetaWindow *not_this_one) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); MetaWindow *window; int x, y; if (not_this_one) meta_topic (META_DEBUG_FOCUS, "Focusing mouse window excluding %s\n", not_this_one->desc); meta_cursor_tracker_get_pointer (cursor_tracker, &x, &y, NULL); window = meta_stack_get_default_focus_window_at_point (display->stack, workspace_manager->active_workspace, not_this_one, x, y); return window; } void meta_display_focus_default_window (MetaDisplay *display, guint32 timestamp) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, timestamp); } /** * meta_display_get_workspace_manager: * @display: a #MetaDisplay * * Returns: (transfer none): The workspace manager of the display */ MetaWorkspaceManager * meta_display_get_workspace_manager (MetaDisplay *display) { return display->workspace_manager; } MetaStartupNotification * meta_display_get_startup_notification (MetaDisplay *display) { return display->startup_notification; } MetaWindow * meta_display_get_window_from_id (MetaDisplay *display, uint64_t window_id) { g_autoptr (GSList) windows = NULL; GSList *l; windows = meta_display_list_windows (display, META_LIST_DEFAULT); for (l = windows; l; l = l->next) { MetaWindow *window = l->data; if (window->id == window_id) return window; } return NULL; } uint64_t meta_display_generate_window_id (MetaDisplay *display) { static uint64_t base_window_id; static uint64_t last_window_id; if (!base_window_id) base_window_id = g_random_int () + 1; /* We can overflow here, that's fine */ return (base_window_id + last_window_id++); } /** * meta_display_get_sound_player: * @display: a #MetaDisplay * * Returns: (transfer none): The sound player of the display */ MetaSoundPlayer * meta_display_get_sound_player (MetaDisplay *display) { return display->sound_player; } /** * meta_display_get_selection: * @display: a #MetaDisplay * * Returns: (transfer none): The selection manager of the display */ MetaSelection * meta_display_get_selection (MetaDisplay *display) { return display->selection; } void meta_display_a11y_zoom (MetaDisplay *display, gboolean in) { g_signal_emit (display, display_signals[in ? ZOOM_SCROLL_IN : ZOOM_SCROLL_OUT], 0); } /** * meta_display_set_desklets_above: * @display: a #MetaDisplay * @above: Whether to set above or not * * Raises the desklet container to the top of the stage temporatily. */ void meta_display_set_desklets_above (MetaDisplay *display, gboolean above) { display->desklets_above = above; meta_update_desklet_stacking (display->compositor); } /** * meta_display_get_desklets_above: * @display: a #MetaDisplay * * Returns: Whether or not desklets are currently raised to the top. */ gboolean meta_display_get_desklets_above (MetaDisplay *display) { return display->desklets_above; } gint meta_display_xinerama_index_to_logical_index (MetaDisplay *display, gint x_index) { MetaLogicalMonitor *monitor; monitor = meta_x11_display_xinerama_index_to_logical_monitor (display->x11_display, x_index); if (monitor == NULL) { return 0; } return monitor->number; } gint meta_display_logical_index_to_xinerama_index (MetaDisplay *display, gint log_index) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *manager; MetaLogicalMonitor *monitor; manager = meta_backend_get_monitor_manager (backend); monitor = meta_monitor_manager_get_logical_monitor_from_number (manager, log_index); if (monitor == NULL) { return 0; } return meta_x11_display_logical_monitor_to_xinerama_index (display->x11_display, monitor); } const gchar * meta_display_get_tile_mode_str (MetaTileMode mode) { switch (mode) { case META_TILE_NONE: return "META_TILE_NONE "; case META_TILE_LEFT: return "META_TILE_LEFT "; case META_TILE_RIGHT: return "META_TILE_RIGHT "; case META_TILE_ULC: return "META_TILE_ULC "; case META_TILE_LLC: return "META_TILE_LLC "; case META_TILE_URC: return "META_TILE_URC "; case META_TILE_LRC: return "META_TILE_LRC "; case META_TILE_TOP: return "META_TILE_TOP "; case META_TILE_BOTTOM: return "META_TILE_BOTTOM "; case META_TILE_MAXIMIZED: return "META_TILE_MAXIMIZED"; default: return "(unknown) "; } } muffin-6.4.1/src/core/meta-gesture-tracker-private.h0000664000175000017500000000610414723361714021336 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_GESTURE_TRACKER_PRIVATE_H #define META_GESTURE_TRACKER_PRIVATE_H #include #include "backends/meta-backend-private.h" #include "clutter/clutter.h" #include "meta/window.h" #define META_TYPE_GESTURE_TRACKER (meta_gesture_tracker_get_type ()) #define META_GESTURE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_GESTURE_TRACKER, MetaGestureTracker)) #define META_GESTURE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) #define META_IS_GESTURE_TRACKER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_GESTURE_TRACKER)) #define META_IS_GESTURE_TRACKER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_GESTURE_TRACKER)) #define META_GESTURE_TRACKER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_GESTURE_TRACKER, MetaGestureTrackerClass)) typedef struct _MetaGestureTracker MetaGestureTracker; typedef struct _MetaGestureTrackerClass MetaGestureTrackerClass; struct _MetaGestureTracker { GObject parent_instance; }; struct _MetaGestureTrackerClass { GObjectClass parent_class; void (* state_changed) (MetaGestureTracker *tracker, ClutterEventSequence *sequence, MetaSequenceState state); }; GType meta_gesture_tracker_get_type (void) G_GNUC_CONST; MetaGestureTracker * meta_gesture_tracker_new (void); gboolean meta_gesture_tracker_handle_event (MetaGestureTracker *tracker, const ClutterEvent *event); gboolean meta_gesture_tracker_set_sequence_state (MetaGestureTracker *tracker, ClutterEventSequence *sequence, MetaSequenceState state); MetaSequenceState meta_gesture_tracker_get_sequence_state (MetaGestureTracker *tracker, ClutterEventSequence *sequence); gint meta_gesture_tracker_get_n_current_touches (MetaGestureTracker *tracker); #endif /* META_GESTURE_TRACKER_PRIVATE_H */ muffin-6.4.1/src/core/stack-tracker.h0000664000175000017500000000740014723361714016371 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file stack-tracker.h Track stacking order for compositor * * MetaStackTracker maintains the most accurate view we have at a * given point of time of the ordering of the children of the root * window (including override-redirect windows.) This is used to order * the windows when the compositor draws them. * * By contrast, MetaStack is responsible for keeping track of how we * think that windows *should* be ordered. For windows we manage * (non-override-redirect windows), the two stacking orders will be * the same. */ /* * Copyright (C) 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_STACK_TRACKER_H #define META_STACK_TRACKER_H #include "core/util-private.h" #include "meta/display.h" #include "meta/window.h" typedef struct _MetaStackTracker MetaStackTracker; MetaStackTracker *meta_stack_tracker_new (MetaDisplay *display); void meta_stack_tracker_free (MetaStackTracker *tracker); /* These functions are called when we make an X call that changes the * stacking order; this allows MetaStackTracker to predict stacking * order before it receives events back from the X server */ void meta_stack_tracker_record_add (MetaStackTracker *tracker, guint64 window, gulong serial); void meta_stack_tracker_record_remove (MetaStackTracker *tracker, guint64 window, gulong serial); /* We also have functions that also go ahead and do the work */ void meta_stack_tracker_lower (MetaStackTracker *tracker, guint64 window); void meta_stack_tracker_restack_managed (MetaStackTracker *tracker, const guint64 *windows, int n_windows); void meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, const guint64 *new_order, int n_new_order); /* These functions are used to update the stack when we get events * reflecting changes to the stacking order */ void meta_stack_tracker_create_event (MetaStackTracker *tracker, XCreateWindowEvent *event); void meta_stack_tracker_destroy_event (MetaStackTracker *tracker, XDestroyWindowEvent *event); void meta_stack_tracker_reparent_event (MetaStackTracker *tracker, XReparentEvent *event); void meta_stack_tracker_configure_event (MetaStackTracker *tracker, XConfigureEvent *event); META_EXPORT_TEST void meta_stack_tracker_get_stack (MetaStackTracker *tracker, guint64 **windows, int *n_entries); void meta_stack_tracker_sync_stack (MetaStackTracker *tracker); void meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker); #endif /* META_STACK_TRACKER_H */ muffin-6.4.1/src/core/events.h0000664000175000017500000000206614723361714015142 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_EVENTS_H #define META_EVENTS_H #include "meta/display.h" void meta_display_init_events (MetaDisplay *display); void meta_display_free_events (MetaDisplay *display); #endif muffin-6.4.1/src/core/meta-selection-private.h0000664000175000017500000000213714723361714020216 0ustar fabiofabio/* * Copyright (C) 2020 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Carlos Garnacho */ #ifndef META_SELECTION_PRIVATE_H #define META_SELECTION_PRIVATE_H #include "meta/meta-selection.h" MetaSelectionSource * meta_selection_get_current_owner (MetaSelection *selection, MetaSelectionType selection_type); #endif /* META_SELECTION_PRIVATE_H */ muffin-6.4.1/src/core/stack.h0000664000175000017500000003422014723361714014740 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_STACK_H #define META_STACK_H /** * SECTION:stack * @short_description: Which windows cover which other windows * * There are two factors that determine window position. * * One is window->stack_position, which is a unique integer * indicating how windows are ordered with respect to one * another. The ordering here transcends layers; it isn't changed * as the window is moved among layers. This allows us to move several * windows from one layer to another, while preserving the relative * order of the moved windows. Also, it allows us to restore * the stacking order from a saved session. * * However when actually stacking windows on the screen, the * layer overrides the stack_position; windows are first sorted * by layer, then by stack_position within each layer. */ #include "core/display-private.h" /** * A sorted list of windows bearing some level of resemblance to the stack of * windows on the X server. * * (This is only used as a field within a MetaScreen; we treat it as a separate * class for simplicity.) */ struct _MetaStack { GObject parent; /** The MetaDisplay containing this stack. */ MetaDisplay *display; /** The MetaWindows of the windows we manage, sorted in order. */ GList *sorted; /** * If this is zero, the local stack oughtn't to be brought up to date with * the X server's stack, because it is in the middle of being updated. * If it is positive, the local stack is said to be "frozen", and will need * to be thawed that many times before the stack can be brought up to date * again. You may freeze the stack with meta_stack_freeze() and thaw it * with meta_stack_thaw(). */ int freeze_count; /** * The last-known stack of all windows, bottom to top. We cache it here * so that subsequent times we'll be able to do incremental moves. */ GArray *last_all_root_children_stacked; /** * Number of stack positions; same as the length of added, but * kept for quick reference. */ gint n_positions; /** Is the stack in need of re-sorting? */ unsigned int need_resort : 1; /** * Are the windows in the stack in need of having their * layers recalculated? */ unsigned int need_relayer : 1; /** * Are the windows in the stack in need of having their positions * recalculated with respect to transiency (parent and child windows)? */ unsigned int need_constrain : 1; }; #define META_TYPE_STACK (meta_stack_get_type ()) G_DECLARE_FINAL_TYPE (MetaStack, meta_stack, META, STACK, GObject) /** * meta_stack_new: * @display: The MetaDisplay which will be the parent of this stack. * * Creates and initialises a MetaStack. * * Returns: The new stack. */ MetaStack * meta_stack_new (MetaDisplay *display); /** * meta_stack_add: * @stack: The stack to add it to * @window: The window to add * * Adds a window to the local stack. It is a fatal error to call this * function on a window which already exists on the stack of any screen. */ void meta_stack_add (MetaStack *stack, MetaWindow *window); /** * meta_stack_remove: * @stack: The stack to remove it from * @window: The window to remove * * Removes a window from the local stack. It is a fatal error to call this * function on a window which exists on the stack of any screen. */ void meta_stack_remove (MetaStack *stack, MetaWindow *window); /** * meta_stack_update_layer: * @stack: The stack to recalculate * @window: Dummy parameter * * Recalculates the correct layer for all windows in the stack, * and moves them about accordingly. * */ void meta_stack_update_layer (MetaStack *stack, MetaWindow *window); /** * meta_stack_update_transient: * @stack: The stack to recalculate * @window: Dummy parameter * * Recalculates the correct stacking order for all windows in the stack * according to their transience, and moves them about accordingly. * * FIXME: What's with the dummy parameter? */ void meta_stack_update_transient (MetaStack *stack, MetaWindow *window); /** * meta_stack_raise: * @stack: The stack to modify. * @window: The window that's making an ascension. * (Amulet of Yendor not required.) * * Move a window to the top of its layer. */ void meta_stack_raise (MetaStack *stack, MetaWindow *window); /** * meta_stack_lower: * @stack: The stack to modify. * @window: The window that's on the way downwards. * * Move a window to the bottom of its layer. */ void meta_stack_lower (MetaStack *stack, MetaWindow *window); /** * meta_stack_freeze: * @stack: The stack to freeze. * * Prevent syncing to server until the next call of meta_stack_thaw(), * so that we can carry out multiple operations in one go without having * everything halfway reflected on the X server. * * (Calls to meta_stack_freeze() nest, so that multiple calls to * meta_stack_freeze will require multiple calls to meta_stack_thaw().) */ void meta_stack_freeze (MetaStack *stack); /** * meta_stack_thaw: * @stack: The stack to thaw. * * Undoes a meta_stack_freeze(), and processes anything which has become * necessary during the freeze. It is an error to call this function if * the stack has not been frozen. */ void meta_stack_thaw (MetaStack *stack); /** * meta_stack_get_top: * @stack: The stack to examine. * * Finds the top window on the stack. * * Returns: The top window on the stack, or %NULL in the vanishingly unlikely * event that you have no windows on your screen whatsoever. */ MetaWindow * meta_stack_get_top (MetaStack *stack); /** * meta_stack_get_bottom: * @stack: The stack to search * * Finds the window at the bottom of the stack. Since that's pretty much * always the desktop, this isn't the most useful of functions, and nobody * actually calls it. We should probably get rid of it. */ MetaWindow * meta_stack_get_bottom (MetaStack *stack); /** * meta_stack_get_above: * @stack: The stack to search. * @window: The window to look above. * @only_within_layer: If %TRUE, will return %NULL if @window is the * top window in its layer. * * Finds the window above a given window in the stack. * It is not an error to pass in a window which does not exist in * the stack; the function will merely return %NULL. * * Returns: %NULL if there is no such window; * the window above @window otherwise. */ MetaWindow * meta_stack_get_above (MetaStack *stack, MetaWindow *window, gboolean only_within_layer); /** * meta_stack_get_below: * @stack: The stack to search. * @window: The window to look below. * @only_within_layer: If %TRUE, will return %NULL if window is the * bottom window in its layer. * * Finds the window below a given window in the stack. * It is not an error to pass in a window which does not exist in * the stack; the function will merely return %NULL. * * * Returns: %NULL if there is no such window; * the window below @window otherwise. */ MetaWindow * meta_stack_get_below (MetaStack *stack, MetaWindow *window, gboolean only_within_layer); /** * meta_stack_get_default_focus_window: * @stack: The stack to search. * @workspace: %NULL to search all workspaces; otherwise only windows * from that workspace will be returned. * @not_this_one: Window to ignore because it's being unfocussed or * going away. * * Find the topmost, focusable, mapped, window in a stack. If you supply * a window as @not_this_one, we won't return that one (presumably * because it's going to be going away). But if you do supply @not_this_one * and we find its parent, we'll return that; and if @not_this_one is in * a group, we'll return the top window of that group. * * Also, we are prejudiced against dock windows. Every kind of window, even * the desktop, will be returned in preference to a dock window. * * Returns: The window matching all these constraints or %NULL if none does. */ MetaWindow * meta_stack_get_default_focus_window (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one); /** * meta_stack_get_default_focus_window_at_point: * @stack: The stack to search. * @workspace: %NULL to search all workspaces; otherwise only windows * from that workspace will be returned. * @not_this_one: Window to ignore because it's being unfocussed or * going away. * @root_x: The returned window must contain this point, * unless it's a dock. * @root_y: See root_x. * * Find the topmost, focusable, mapped, window in a stack. If you supply * a window as @not_this_one, we won't return that one (presumably * because it's going to be going away). But if you do supply @not_this_one * and we find its parent, we'll return that; and if @not_this_one is in * a group, we'll return the top window of that group. * * Also, we are prejudiced against dock windows. Every kind of window, even * the desktop, will be returned in preference to a dock window. * * Returns: The window matching all these constraints or %NULL if none does. */ MetaWindow * meta_stack_get_default_focus_window_at_point (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one, int root_x, int root_y); /** * meta_stack_get_default_focus_candidates: * @stack: The stack to examine. * @workspace: If not %NULL, only windows on this workspace will be * returned; otherwise all windows in the stack will be * returned. * * Returns all the focus candidate windows in the stack, in order. * * Returns: (transfer container) (element-type Meta.Window): * A #GList of #MetaWindow, in stacking order, honouring layers. */ GList * meta_stack_get_default_focus_candidates (MetaStack *stack, MetaWorkspace *workspace); /** * meta_stack_list_windows: * @stack: The stack to examine. * @workspace: If not %NULL, only windows on this workspace will be * returned; otherwise all windows in the stack will be * returned. * * Finds all the windows in the stack, in order. * * Returns: (transfer container) (element-type Meta.Window): * A list of windows, in stacking order, honouring layers. */ GList * meta_stack_list_windows (MetaStack *stack, MetaWorkspace *workspace); /** * meta_stack_windows_cmp: * @stack: A stack containing both window_a and window_b * @window_a: A window * @window_b Another window * * Comparison function for windows within a stack. This is not directly * suitable for use within a standard comparison routine, because it takes * an extra parameter; you will need to wrap it. * * (FIXME: We could remove the stack parameter and use the stack of * the screen of window A, and complain if the stack of the screen of * window B differed; then this would be a usable general comparison function.) * * (FIXME: Apparently identical to compare_window_position(). Merge them.) * * \return -1 if window_a is below window_b, honouring layers; 1 if it's * above it; 0 if you passed in the same window twice! */ int meta_stack_windows_cmp (MetaStack *stack, MetaWindow *window_a, MetaWindow *window_b); /** * meta_window_set_stack_position: * @window: The window which is moving. * @position: Where it should move to (0 is the bottom). * * Sets the position of a window within the stack. This will only move it * up or down within its layer. It is an error to attempt to move this * below position zero or above the last position in the stack (however, since * we don't provide a simple way to tell the number of windows in the stack, * this requirement may not be easy to fulfil). */ void meta_window_set_stack_position (MetaWindow *window, int position); /** * meta_stack_get_positions: * @stack: The stack to examine. * * Returns the current stack state, allowing rudimentary transactions. * * Returns: (transfer container) (element-type Meta.Window): * An opaque #GList representing the current stack sort order; * it is the caller's responsibility to free it. * Pass this to meta_stack_set_positions() later if you want to restore * the state to where it was when you called this function. */ GList * meta_stack_get_positions (MetaStack *stack); /** * meta_stack_set_positions: * @stack: The stack to roll back. * @windows: The list returned from meta_stack_get_positions(). * * Rolls back a transaction, given the list returned from * meta_stack_get_positions(). * */ void meta_stack_set_positions (MetaStack *stack, GList *windows); void meta_stack_update_window_tile_matches (MetaStack *stack, MetaWorkspace *workspace); #endif muffin-6.4.1/src/core/meta-close-dialog-default-private.h0000664000175000017500000000242614723361714022216 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_CLOSE_DIALOG_DEFAULT_H #define META_CLOSE_DIALOG_DEFAULT_H #include #include "meta/meta-plugin.h" #define META_TYPE_CLOSE_DIALOG_DEFAULT (meta_close_dialog_default_get_type ()) G_DECLARE_FINAL_TYPE (MetaCloseDialogDefault, meta_close_dialog_default, META, CLOSE_DIALOG_DEFAULT, GObject) MetaCloseDialog * meta_close_dialog_default_new (MetaWindow *window); #endif /* META_CLOSE_DIALOG_DEFAULT_H */ muffin-6.4.1/src/core/constraints.c0000664000175000017500000022027414723361714016203 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter size/position constraints */ /* * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/constraints.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "core/boxes-private.h" #include "core/meta-workspace-manager-private.h" #include "core/place.h" #include "core/workspace-private.h" #include "meta/prefs.h" #if 0 // This is the short and sweet version of how to hack on this file; see // doc/how-constraints-works.txt for the gory details. The basics of // understanding this file can be shown by the steps needed to add a new // constraint, which are: // 1) Add a new entry in the ConstraintPriority enum; higher values // have higher priority // 2) Write a new function following the format of the example below, // "constrain_whatever". // 3) Add your function to the all_constraints and all_constraint_names // arrays (the latter of which is for debugging purposes) // // An example constraint function, constrain_whatever: // // /* constrain_whatever does the following: // * Quits (returning true) if priority is higher than PRIORITY_WHATEVER // * If check_only is TRUE // * Returns whether the constraint is satisfied or not // * otherwise // * Enforces the constraint // * Note that the value of PRIORITY_WHATEVER is centralized with the // * priorities of other constraints in the definition of ConstrainPriority // * for easier maintenance and shuffling of priorities. // */ // static gboolean // constrain_whatever (MetaWindow *window, // ConstraintInfo *info, // ConstraintPriority priority, // gboolean check_only) // { // if (priority > PRIORITY_WHATEVER) // return TRUE; // // /* Determine whether constraint applies; note that if the constraint // * cannot possibly be satisfied, constraint_applies should be set to // * false. If we don't do this, all constraints with a lesser priority // * will be dropped along with this one, and we'd rather apply as many as // * possible. // */ // if (!constraint_applies) // return TRUE; // // /* Determine whether constraint is already satisfied; if we're only // * checking the status of whether the constraint is satisfied, we end // * here. // */ // if (check_only || constraint_already_satisfied) // return constraint_already_satisfied; // // /* Enforce constraints */ // return TRUE; /* Note that we exited early if check_only is FALSE; also, // * we know we can return TRUE here because we exited early // * if the constraint could not be satisfied; not that the // * return value is heeded in this case... // */ // } #endif typedef enum { PRIORITY_MINIMUM = 0, /* Dummy value used for loop start = min(all priorities) */ PRIORITY_ASPECT_RATIO = 0, PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR = 0, PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA = 1, PRIORITY_SIZE_HINTS_INCREMENTS = 1, PRIORITY_MAXIMIZATION = 2, PRIORITY_TILING = 2, PRIORITY_FULLSCREEN = 2, PRIORITY_SIZE_HINTS_LIMITS = 3, PRIORITY_TITLEBAR_VISIBLE = 4, PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA = 4, PRIORITY_CUSTOM_RULE = 4, PRIORITY_MAXIMUM = 4 /* Dummy value used for loop end = max(all priorities) */ } ConstraintPriority; typedef enum { ACTION_MOVE, ACTION_RESIZE, ACTION_MOVE_AND_RESIZE } ActionType; typedef struct { MetaRectangle orig; MetaRectangle current; MetaRectangle temporary; int rel_x; int rel_y; ActionType action_type; gboolean is_user_action; /* I know that these two things probably look similar at first, but they * have much different uses. See doc/how-constraints-works.txt for for * explanation of the differences and similarity between resize_gravity * and fixed_directions */ MetaGravity resize_gravity; FixedDirections fixed_directions; /* work_area_monitor - current monitor region minus struts * entire_monitor - current monitor, including strut regions */ MetaRectangle work_area_monitor; MetaRectangle entire_monitor; /* Spanning rectangles for the non-covered (by struts) region of the * screen and also for just the current monitor */ GList *usable_screen_region; GList *usable_monitor_region; MetaMoveResizeFlags flags; } ConstraintInfo; static gboolean do_screen_and_monitor_relative_constraints (MetaWindow *window, GList *region_spanning_rectangles, ConstraintInfo *info, gboolean check_only); static gboolean constrain_custom_rule (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_modal_dialog (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_maximization (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_tiling (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_fullscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_size_increments (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_size_limits (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_aspect_ratio (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_to_single_monitor (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_fully_onscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_titlebar_visible (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static gboolean constrain_partially_onscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); static void setup_constraint_info (ConstraintInfo *info, MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new); static void place_window_if_needed (MetaWindow *window, ConstraintInfo *info); static void update_onscreen_requirements (MetaWindow *window, ConstraintInfo *info); typedef gboolean (* ConstraintFunc) (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only); typedef struct { ConstraintFunc func; const char* name; } Constraint; static const Constraint all_constraints[] = { {constrain_custom_rule, "constrain_custom_rule"}, {constrain_modal_dialog, "constrain_modal_dialog"}, {constrain_maximization, "constrain_maximization"}, {constrain_tiling, "constrain_tiling"}, {constrain_fullscreen, "constrain_fullscreen"}, {constrain_size_increments, "constrain_size_increments"}, {constrain_size_limits, "constrain_size_limits"}, {constrain_aspect_ratio, "constrain_aspect_ratio"}, {constrain_to_single_monitor, "constrain_to_single_monitor"}, {constrain_fully_onscreen, "constrain_fully_onscreen"}, {constrain_titlebar_visible, "constrain_titlebar_visible"}, {constrain_partially_onscreen, "constrain_partially_onscreen"}, {NULL, NULL} }; static gboolean do_all_constraints (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { const Constraint *constraint; gboolean satisfied; constraint = &all_constraints[0]; satisfied = TRUE; while (constraint->func != NULL) { satisfied = satisfied && (*constraint->func) (window, info, priority, check_only); if (!check_only) { /* Log how the constraint modified the position */ meta_topic (META_DEBUG_GEOMETRY, "info->current is %d,%d +%d,%d after %s\n", info->current.x, info->current.y, info->current.width, info->current.height, constraint->name); } else if (!satisfied) { /* Log which constraint was not satisfied */ meta_topic (META_DEBUG_GEOMETRY, "constraint %s not satisfied.\n", constraint->name); return FALSE; } ++constraint; } return TRUE; } void meta_window_constrain (MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new, MetaRectangle *temporary, int *rel_x, int *rel_y) { ConstraintInfo info; ConstraintPriority priority = PRIORITY_MINIMUM; gboolean satisfied = FALSE; meta_topic (META_DEBUG_GEOMETRY, "Constraining %s in move from %d,%d %dx%d to %d,%d %dx%d\n", window->desc, orig->x, orig->y, orig->width, orig->height, new->x, new->y, new->width, new->height); setup_constraint_info (&info, window, flags, resize_gravity, orig, new); place_window_if_needed (window, &info); while (!satisfied && priority <= PRIORITY_MAXIMUM) { gboolean check_only = TRUE; /* Individually enforce all the high-enough priority constraints */ do_all_constraints (window, &info, priority, !check_only); /* Check if all high-enough priority constraints are simultaneously * satisfied */ satisfied = do_all_constraints (window, &info, priority, check_only); /* Drop the least important constraints if we can't satisfy them all */ priority++; } /* Make sure we use the constrained position */ *new = info.current; *temporary = info.temporary; *rel_x = info.rel_x; *rel_y = info.rel_y; /* We may need to update window->require_fully_onscreen, * window->require_on_single_monitor, and perhaps other quantities * if this was a user move or user move-and-resize operation. */ update_onscreen_requirements (window, &info); } static void setup_constraint_info (ConstraintInfo *info, MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; MetaWorkspace *cur_workspace; info->orig = *orig; info->current = *new; info->temporary = *orig; info->rel_x = 0; info->rel_y = 0; info->flags = flags; if (info->current.width < 1) info->current.width = 1; if (info->current.height < 1) info->current.height = 1; if (flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_RESIZE_ACTION) info->action_type = ACTION_MOVE_AND_RESIZE; else if (flags & META_MOVE_RESIZE_RESIZE_ACTION) info->action_type = ACTION_RESIZE; else if (flags & META_MOVE_RESIZE_MOVE_ACTION) info->action_type = ACTION_MOVE; else g_error ("BAD, BAD developer! No treat for you! (Fix your calls to " "meta_window_move_resize_internal()).\n"); info->is_user_action = (flags & META_MOVE_RESIZE_USER_ACTION); info->resize_gravity = resize_gravity; /* FIXME: fixed_directions might be more sane if we (a) made it * depend on the grab_op type instead of current amount of movement * (thus implying that it only has effect when user_action is true, * and (b) ignored it for aspect ratio windows -- at least in those * cases where both directions do actually change size. */ info->fixed_directions = FIXED_DIRECTION_NONE; /* If x directions don't change but either y direction does */ if ( orig->x == new->x && orig->x + orig->width == new->x + new->width && (orig->y != new->y || orig->y + orig->height != new->y + new->height)) { info->fixed_directions = FIXED_DIRECTION_X; } /* If y directions don't change but either x direction does */ if ( orig->y == new->y && orig->y + orig->height == new->y + new->height && (orig->x != new->x || orig->x + orig->width != new->x + new->width )) { info->fixed_directions = FIXED_DIRECTION_Y; } /* The point of fixed directions is just that "move to nearest valid * position" is sometimes a poorer choice than "move to nearest * valid position but only change this coordinate" for windows the * user is explicitly moving. This isn't ever true for things that * aren't explicit user interaction, though, so just clear it out. */ if (!info->is_user_action) info->fixed_directions = FIXED_DIRECTION_NONE; logical_monitor = meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, &info->current); meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &info->work_area_monitor); if (window->fullscreen && meta_window_has_fullscreen_monitors (window)) { info->entire_monitor = window->fullscreen_monitors.top->rect; meta_rectangle_union (&info->entire_monitor, &window->fullscreen_monitors.bottom->rect, &info->entire_monitor); meta_rectangle_union (&info->entire_monitor, &window->fullscreen_monitors.left->rect, &info->entire_monitor); meta_rectangle_union (&info->entire_monitor, &window->fullscreen_monitors.right->rect, &info->entire_monitor); } else { info->entire_monitor = logical_monitor->rect; if (window->fullscreen) meta_window_adjust_fullscreen_monitor_rect (window, &info->entire_monitor); } cur_workspace = window->display->workspace_manager->active_workspace; info->usable_screen_region = meta_workspace_get_onscreen_region (cur_workspace); info->usable_monitor_region = meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); /* Log all this information for debugging */ meta_topic (META_DEBUG_GEOMETRY, "Setting up constraint info:\n" " orig: %d,%d +%d,%d\n" " new : %d,%d +%d,%d\n" " action_type : %s\n" " is_user_action : %s\n" " resize_gravity : %s\n" " fixed_directions: %s\n" " work_area_monitor: %d,%d +%d,%d\n" " entire_monitor : %d,%d +%d,%d\n", info->orig.x, info->orig.y, info->orig.width, info->orig.height, info->current.x, info->current.y, info->current.width, info->current.height, (info->action_type == ACTION_MOVE) ? "Move" : (info->action_type == ACTION_RESIZE) ? "Resize" : (info->action_type == ACTION_MOVE_AND_RESIZE) ? "Move&Resize" : "Freakin' Invalid Stupid", (info->is_user_action) ? "true" : "false", meta_gravity_to_string (info->resize_gravity), (info->fixed_directions == FIXED_DIRECTION_NONE) ? "None" : (info->fixed_directions == FIXED_DIRECTION_X) ? "X fixed" : (info->fixed_directions == FIXED_DIRECTION_Y) ? "Y fixed" : "Freakin' Invalid Stupid", info->work_area_monitor.x, info->work_area_monitor.y, info->work_area_monitor.width, info->work_area_monitor.height, info->entire_monitor.x, info->entire_monitor.y, info->entire_monitor.width, info->entire_monitor.height); } static MetaRectangle * get_start_rect_for_resize (MetaWindow *window, ConstraintInfo *info) { if (!info->is_user_action && info->action_type == ACTION_MOVE_AND_RESIZE) return &info->current; else return &info->orig; } static void place_window_if_needed(MetaWindow *window, ConstraintInfo *info) { gboolean did_placement; /* Do placement if any, so we go ahead and apply position * constraints in a move-only context. Don't place * maximized/minimized/fullscreen windows until they are * unmaximized, unminimized and unfullscreened. */ did_placement = FALSE; if (!window->placed && window->calc_placement && !(window->maximized_horizontally || window->maximized_vertically) && !window->minimized && !window->fullscreen) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaRectangle orig_rect; MetaRectangle placed_rect; MetaWorkspace *cur_workspace; MetaLogicalMonitor *logical_monitor; placed_rect = (MetaRectangle) { .x = window->rect.x, .y = window->rect.y, .width = info->current.width, .height = info->current.height }; orig_rect = info->orig; if (window->placement.rule) { meta_window_process_placement (window, window->placement.rule, &info->rel_x, &info->rel_y); placed_rect.x = window->placement.rule->parent_rect.x + info->rel_x; placed_rect.y = window->placement.rule->parent_rect.y + info->rel_y; } else { meta_window_place (window, orig_rect.x, orig_rect.y, &placed_rect.x, &placed_rect.y); } did_placement = TRUE; /* placing the window may have changed the monitor. Find the * new monitor and update the ConstraintInfo */ logical_monitor = meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, &placed_rect); info->entire_monitor = logical_monitor->rect; meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &info->work_area_monitor); cur_workspace = window->display->workspace_manager->active_workspace; info->usable_monitor_region = meta_workspace_get_onmonitor_region (cur_workspace, logical_monitor); info->current.x = placed_rect.x; info->current.y = placed_rect.y; /* Since we just barely placed the window, there's no reason to * consider any of the directions fixed. */ info->fixed_directions = FIXED_DIRECTION_NONE; } if (window->placed || did_placement) { if (window->maximize_horizontally_after_placement || window->maximize_vertically_after_placement) { /* define a sane saved_rect so that the user can unmaximize to * something reasonable. */ if (info->current.width >= info->work_area_monitor.width) { info->current.width = .75 * info->work_area_monitor.width; info->current.x = info->work_area_monitor.x + .125 * info->work_area_monitor.width; } if (info->current.height >= info->work_area_monitor.height) { info->current.height = .75 * info->work_area_monitor.height; info->current.y = info->work_area_monitor.y + .083 * info->work_area_monitor.height; } /* idle_move_resize() uses the unconstrained_rect, so make sure it * uses the placed coordinates (bug #556696). */ window->unconstrained_rect = info->current; if (window->maximize_horizontally_after_placement || window->maximize_vertically_after_placement) meta_window_maximize_internal (window, (window->maximize_horizontally_after_placement ? META_MAXIMIZE_HORIZONTAL : 0 ) | (window->maximize_vertically_after_placement ? META_MAXIMIZE_VERTICAL : 0), &info->current); window->maximize_horizontally_after_placement = FALSE; window->maximize_vertically_after_placement = FALSE; } if (window->minimize_after_placement) { meta_window_minimize (window); window->minimize_after_placement = FALSE; } } } static void update_onscreen_requirements (MetaWindow *window, ConstraintInfo *info) { gboolean old; /* We only apply the various onscreen requirements to normal windows */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK) return; /* We don't want to update the requirements for fullscreen windows; * fullscreen windows are specially handled anyway, and it updating * the requirements when windows enter fullscreen mode mess up the * handling of the window when it leaves that mode (especially when * the application sends a bunch of configurerequest events). See * #353699. */ if (window->fullscreen) return; /* USABILITY NOTE: Naturally, I only want the require_fully_onscreen, * require_on_single_monitor, and require_titlebar_visible flags to * *become false* due to user interactions (which is allowed since * certain constraints are ignored for user interactions regardless of * the setting of these flags). However, whether to make these flags * *become true* due to just an application interaction is a little * trickier. It's possible that users may find not doing that strange * since two application interactions that resize in opposite ways don't * necessarily end up cancelling--but it may also be strange for the user * to have an application resize the window so that it's onscreen, the * user forgets about it, and then later the app is able to resize itself * off the screen. Anyway, for now, I think the latter is the more * problematic case but this may need to be revisited. */ /* Update whether we want future constraint runs to require the * window to be on fully onscreen. */ old = window->require_fully_onscreen; window->require_fully_onscreen = meta_rectangle_contained_in_region (info->usable_screen_region, &info->current); if (old != window->require_fully_onscreen) meta_topic (META_DEBUG_GEOMETRY, "require_fully_onscreen for %s toggled to %s\n", window->desc, window->require_fully_onscreen ? "TRUE" : "FALSE"); /* Update whether we want future constraint runs to require the * window to be on a single monitor. */ old = window->require_on_single_monitor; window->require_on_single_monitor = meta_rectangle_contained_in_region (info->usable_monitor_region, &info->current); if (old != window->require_on_single_monitor) meta_topic (META_DEBUG_GEOMETRY, "require_on_single_monitor for %s toggled to %s\n", window->desc, window->require_on_single_monitor ? "TRUE" : "FALSE"); /* Update whether we want future constraint runs to require the * titlebar to be visible. */ if (window->frame && window->decorated) { MetaRectangle titlebar_rect, frame_rect; meta_window_get_titlebar_rect (window, &titlebar_rect); meta_window_get_frame_rect (window, &frame_rect); /* translate into screen coordinates */ titlebar_rect.x = frame_rect.x; titlebar_rect.y = frame_rect.y; old = window->require_titlebar_visible; window->require_titlebar_visible = meta_rectangle_overlaps_with_region (info->usable_screen_region, &titlebar_rect); if (old != window->require_titlebar_visible) meta_topic (META_DEBUG_GEOMETRY, "require_titlebar_visible for %s toggled to %s\n", window->desc, window->require_titlebar_visible ? "TRUE" : "FALSE"); } } static inline void get_size_limits (MetaWindow *window, MetaRectangle *min_size, MetaRectangle *max_size) { /* We pack the results into MetaRectangle structs just for convienience; we * don't actually use the position of those rects. */ min_size->x = min_size->y = max_size->x = max_size->y = 0; min_size->width = window->size_hints.min_width; min_size->height = window->size_hints.min_height; max_size->width = window->size_hints.max_width; max_size->height = window->size_hints.max_height; meta_window_client_rect_to_frame_rect (window, min_size, min_size); meta_window_client_rect_to_frame_rect (window, max_size, max_size); } static void placement_rule_flip_horizontally (MetaPlacementRule *placement_rule) { if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT) { placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_LEFT; placement_rule->anchor |= META_PLACEMENT_ANCHOR_RIGHT; } else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT) { placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_RIGHT; placement_rule->anchor |= META_PLACEMENT_ANCHOR_LEFT; } if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT) { placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_LEFT; placement_rule->gravity |= META_PLACEMENT_GRAVITY_RIGHT; } else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT) { placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_RIGHT; placement_rule->gravity |= META_PLACEMENT_GRAVITY_LEFT; } } static void placement_rule_flip_vertically (MetaPlacementRule *placement_rule) { if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP) { placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_TOP; placement_rule->anchor |= META_PLACEMENT_ANCHOR_BOTTOM; } else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM) { placement_rule->anchor &= ~META_PLACEMENT_ANCHOR_BOTTOM; placement_rule->anchor |= META_PLACEMENT_ANCHOR_TOP; } if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP) { placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_TOP; placement_rule->gravity |= META_PLACEMENT_GRAVITY_BOTTOM; } else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM) { placement_rule->gravity &= ~META_PLACEMENT_GRAVITY_BOTTOM; placement_rule->gravity |= META_PLACEMENT_GRAVITY_TOP; } } static void try_flip_window_position (MetaWindow *window, ConstraintInfo *info, MetaPlacementRule *placement_rule, MetaPlacementConstraintAdjustment constraint_adjustment, int parent_x, int parent_y, MetaRectangle *rect, int *rel_x, int *rel_y, MetaRectangle *intersection) { MetaPlacementRule flipped_rule = *placement_rule;; MetaRectangle flipped_rect; MetaRectangle flipped_intersection; int flipped_rel_x; int flipped_rel_y; switch (constraint_adjustment) { case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X: placement_rule_flip_horizontally (&flipped_rule); break; case META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y: placement_rule_flip_vertically (&flipped_rule); break; default: g_assert_not_reached (); } flipped_rect = info->current; meta_window_process_placement (window, &flipped_rule, &flipped_rel_x, &flipped_rel_y); flipped_rect.x = parent_x + flipped_rel_x; flipped_rect.y = parent_y + flipped_rel_y; meta_rectangle_intersect (&flipped_rect, &info->work_area_monitor, &flipped_intersection); if ((constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X && flipped_intersection.width == flipped_rect.width) || (constraint_adjustment == META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y && flipped_intersection.height == flipped_rect.height)) { *placement_rule = flipped_rule; *rect = flipped_rect; *rel_x = flipped_rel_x; *rel_y = flipped_rel_y; *intersection = flipped_intersection; } } static gboolean is_custom_rule_satisfied (MetaRectangle *rect, MetaPlacementRule *placement_rule, MetaRectangle *intersection) { uint32_t x_constrain_actions, y_constrain_actions; x_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X | META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X); y_constrain_actions = (META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y | META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y); if ((placement_rule->constraint_adjustment & x_constrain_actions && rect->width != intersection->width) || (placement_rule->constraint_adjustment & y_constrain_actions && rect->height != intersection->height)) return FALSE; else return TRUE; } static gboolean constrain_custom_rule (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaPlacementRule *placement_rule; MetaRectangle intersection; gboolean constraint_satisfied; MetaRectangle temporary_rect; MetaRectangle adjusted_unconstrained; int adjusted_rel_x; int adjusted_rel_y; MetaPlacementRule current_rule; MetaWindow *parent; int parent_x, parent_y; if (priority > PRIORITY_CUSTOM_RULE) return TRUE; placement_rule = meta_window_get_placement_rule (window); if (!placement_rule) return TRUE; parent = meta_window_get_transient_for (window); if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) { placement_rule->parent_rect.x = parent->rect.x; placement_rule->parent_rect.y = parent->rect.y; } parent_x = placement_rule->parent_rect.x; parent_y = placement_rule->parent_rect.y; /* * Calculate the temporary position, meaning a position that will be * applied if the new constrained position requires asynchronous * configuration of the window. This happens for example when the parent * moves, causing this window to change relative position, meaning it can * only have its newly constrained position applied when the configuration is * acknowledged. */ switch (window->placement.state) { case META_PLACEMENT_STATE_UNCONSTRAINED: temporary_rect = info->current; break; case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: case META_PLACEMENT_STATE_CONSTRAINED_PENDING: case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: case META_PLACEMENT_STATE_INVALIDATED: temporary_rect = (MetaRectangle) { .x = parent->rect.x + window->placement.current.rel_x, .y = parent->rect.y + window->placement.current.rel_y, .width = info->current.width, .height = info->current.height, }; break; } /* * Calculate an adjusted current position. Depending on the rule * configuration and placement state, this may result in window being * reconstrained. */ adjusted_unconstrained = temporary_rect; if (window->placement.state == META_PLACEMENT_STATE_INVALIDATED || window->placement.state == META_PLACEMENT_STATE_UNCONSTRAINED || (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED && placement_rule->is_reactive)) { meta_window_process_placement (window, placement_rule, &adjusted_rel_x, &adjusted_rel_y); adjusted_unconstrained.x = parent_x + adjusted_rel_x; adjusted_unconstrained.y = parent_y + adjusted_rel_y; } else if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_PENDING) { adjusted_rel_x = window->placement.pending.rel_x; adjusted_rel_y = window->placement.pending.rel_y; adjusted_unconstrained.x = window->placement.pending.x; adjusted_unconstrained.y = window->placement.pending.y; } else { adjusted_rel_x = window->placement.current.rel_x; adjusted_rel_y = window->placement.current.rel_y; } meta_rectangle_intersect (&adjusted_unconstrained, &info->work_area_monitor, &intersection); constraint_satisfied = (meta_rectangle_equal (&info->current, &adjusted_unconstrained) && is_custom_rule_satisfied (&adjusted_unconstrained, placement_rule, &intersection)); if (check_only) return constraint_satisfied; info->current = adjusted_unconstrained; info->rel_x = adjusted_rel_x; info->rel_y = adjusted_rel_y; info->temporary = temporary_rect; switch (window->placement.state) { case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: if (!placement_rule->is_reactive) return TRUE; break; case META_PLACEMENT_STATE_CONSTRAINED_PENDING: case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: return TRUE; case META_PLACEMENT_STATE_UNCONSTRAINED: case META_PLACEMENT_STATE_INVALIDATED: break; } if (constraint_satisfied) goto done; /* * Process the placement rule in order either until constraints are * satisfied, or there are no more rules to process. */ current_rule = *placement_rule; if (info->current.width != intersection.width && (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X)) { try_flip_window_position (window, info, ¤t_rule, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X, parent_x, parent_y, &info->current, &info->rel_x, &info->rel_y, &intersection); } if (info->current.height != intersection.height && (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y)) { try_flip_window_position (window, info, ¤t_rule, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y, parent_x, parent_y, &info->current, &info->rel_x, &info->rel_y, &intersection); } meta_rectangle_intersect (&info->current, &info->work_area_monitor, &intersection); constraint_satisfied = is_custom_rule_satisfied (&info->current, placement_rule, &intersection); if (constraint_satisfied) goto done; if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X) { int current_x2; int work_area_monitor_x2; int new_x; current_x2 = info->current.x + info->current.width; work_area_monitor_x2 = (info->work_area_monitor.x + info->work_area_monitor.width); if (current_x2 > work_area_monitor_x2) { new_x = MAX (info->work_area_monitor.x, work_area_monitor_x2 - info->current.width); } else if (info->current.x < info->work_area_monitor.x) { new_x = info->work_area_monitor.x; } else { new_x = info->current.x; } info->rel_x += new_x - info->current.x; info->current.x = new_x; } if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y) { int current_y2; int work_area_monitor_y2; int new_y; current_y2 = info->current.y + info->current.height; work_area_monitor_y2 = (info->work_area_monitor.y + info->work_area_monitor.height); if (current_y2 > work_area_monitor_y2) { new_y = MAX (info->work_area_monitor.y, work_area_monitor_y2 - info->current.height); } else if (info->current.y < info->work_area_monitor.y) { new_y = info->work_area_monitor.y; } else { new_y = info->current.y; } info->rel_y += new_y - info->current.y; info->current.y = new_y; } meta_rectangle_intersect (&info->current, &info->work_area_monitor, &intersection); constraint_satisfied = is_custom_rule_satisfied (&info->current, placement_rule, &intersection); if (constraint_satisfied) goto done; if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X) { int new_x; new_x = intersection.x; info->current.width = intersection.width; info->rel_x += new_x - info->current.x; info->current.x = new_x; } if (current_rule.constraint_adjustment & META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y) { int new_y; new_y = intersection.y; info->current.height = intersection.height; info->rel_y += new_y - info->current.y; info->current.y = new_y; } done: window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_PENDING; window->placement.pending.rel_x = info->rel_x; window->placement.pending.rel_y = info->rel_y; window->placement.pending.x = info->current.x; window->placement.pending.y = info->current.y; return TRUE; } static gboolean constrain_modal_dialog (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { int x, y; MetaWindow *parent = meta_window_get_transient_for (window); MetaRectangle child_rect, parent_rect; gboolean constraint_already_satisfied; if (!parent || !meta_window_is_attached_dialog (window) || meta_window_get_placement_rule (window)) return TRUE; /* We want to center the dialog on the parent, including the decorations for both of them. info->current is in client X window coordinates, so we need to convert them to frame coordinates, apply the centering and then convert back to client. */ child_rect = info->current; meta_window_get_frame_rect (parent, &parent_rect); child_rect.x = parent_rect.x + (parent_rect.width / 2 - child_rect.width / 2); child_rect.y = parent_rect.y + (parent_rect.height / 2 - child_rect.height / 2); x = child_rect.x; y = child_rect.y; constraint_already_satisfied = (x == info->current.x) && (y == info->current.y); if (check_only || constraint_already_satisfied) return constraint_already_satisfied; info->current.y = y; info->current.x = x; /* The calculated position above may need adjustment to make sure the * dialog does not end up partially off-screen */ return do_screen_and_monitor_relative_constraints (window, info->usable_screen_region, info, check_only); } static gboolean constrain_maximization (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaRectangle target_size; MetaRectangle min_size, max_size; gboolean hminbad, vminbad; gboolean horiz_equal, vert_equal; gboolean constraint_already_satisfied; if (priority > PRIORITY_MAXIMIZATION) return TRUE; /* Determine whether constraint applies; exit if it doesn't */ if ((!window->maximized_horizontally && !window->maximized_vertically) || META_WINDOW_TILED (window)) return TRUE; /* Calculate target_size = maximized size of (window + frame) */ if (META_WINDOW_TILED_MAXIMIZED (window)) { meta_window_get_tile_area (window, window->tile_mode, &target_size); } else if (META_WINDOW_MAXIMIZED (window)) { target_size = info->work_area_monitor; } else { /* Amount of maximization possible in a single direction depends * on which struts could occlude the window given its current * position. For example, a vertical partial strut on the right * is only relevant for a horizontally maximized window when the * window is at a vertical position where it could be occluded * by that partial strut. */ MetaDirection direction; GSList *active_workspace_struts; if (window->maximized_horizontally) direction = META_DIRECTION_HORIZONTAL; else direction = META_DIRECTION_VERTICAL; active_workspace_struts = workspace_manager->active_workspace->all_struts; target_size = info->current; meta_rectangle_expand_to_avoiding_struts (&target_size, &info->entire_monitor, direction, active_workspace_struts); } /* Check min size constraints; max size constraints are ignored for maximized * windows, as per bug 327543. */ get_size_limits (window, &min_size, &max_size); hminbad = target_size.width < min_size.width && window->maximized_horizontally; vminbad = target_size.height < min_size.height && window->maximized_vertically; if (hminbad || vminbad) return TRUE; /* Determine whether constraint is already satisfied; exit if it is */ horiz_equal = target_size.x == info->current.x && target_size.width == info->current.width; vert_equal = target_size.y == info->current.y && target_size.height == info->current.height; constraint_already_satisfied = (horiz_equal || !window->maximized_horizontally) && (vert_equal || !window->maximized_vertically); if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ if (window->maximized_horizontally) { info->current.x = target_size.x; info->current.width = target_size.width; } if (window->maximized_vertically) { info->current.y = target_size.y; info->current.height = target_size.height; } return TRUE; } static gboolean constrain_tiling (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaRectangle target_size; MetaRectangle min_size, max_size; gboolean hminbad, vminbad; gboolean horiz_equal, vert_equal; gboolean constraint_already_satisfied; if (priority > PRIORITY_TILING) return TRUE; /* Determine whether constraint applies; exit if it doesn't */ if (!META_WINDOW_TILED (window)) return TRUE; /* Calculate target_size - as the tile previews need this as well, we * use an external function for the actual calculation */ meta_window_get_tile_area (window, window->tile_mode, &target_size); /* Check min size constraints; max size constraints are ignored as for * maximized windows. */ get_size_limits (window, &min_size, &max_size); hminbad = target_size.width < min_size.width; vminbad = target_size.height < min_size.height; if (hminbad || vminbad) return TRUE; /* Determine whether constraint is already satisfied; exit if it is */ horiz_equal = target_size.x == info->current.x && target_size.width == info->current.width; vert_equal = target_size.y == info->current.y && target_size.height == info->current.height; constraint_already_satisfied = horiz_equal && vert_equal; if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ info->current.x = target_size.x; info->current.width = target_size.width; info->current.y = target_size.y; info->current.height = target_size.height; return TRUE; } static gboolean constrain_fullscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaRectangle min_size, max_size, monitor; gboolean too_big, too_small, constraint_already_satisfied; if (priority > PRIORITY_FULLSCREEN) return TRUE; /* Determine whether constraint applies; exit if it doesn't */ if (!window->fullscreen) return TRUE; monitor = info->entire_monitor; get_size_limits (window, &min_size, &max_size); too_big = !meta_rectangle_could_fit_rect (&monitor, &min_size); too_small = !meta_rectangle_could_fit_rect (&max_size, &monitor); if (too_big || too_small) return TRUE; /* Determine whether constraint is already satisfied; exit if it is */ constraint_already_satisfied = meta_rectangle_equal (&info->current, &monitor); if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ info->current = monitor; return TRUE; } static gboolean constrain_size_increments (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { int bh, hi, bw, wi, extra_height, extra_width; int new_width, new_height; gboolean constraint_already_satisfied; MetaRectangle *start_rect; MetaRectangle client_rect; if (priority > PRIORITY_SIZE_HINTS_INCREMENTS) return TRUE; /* Determine whether constraint applies; exit if it doesn't */ if (META_WINDOW_MAXIMIZED (window) || window->fullscreen || META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE) return TRUE; meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); /* Determine whether constraint is already satisfied; exit if it is */ bh = window->size_hints.base_height; hi = window->size_hints.height_inc; bw = window->size_hints.base_width; wi = window->size_hints.width_inc; extra_height = (client_rect.height - bh) % hi; extra_width = (client_rect.width - bw) % wi; /* ignore size increments for maximized windows */ if (window->maximized_horizontally) extra_width *= 0; if (window->maximized_vertically) extra_height *= 0; /* constraint is satisfied iff there is no extra height or width */ constraint_already_satisfied = (extra_height == 0 && extra_width == 0); if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ new_width = client_rect.width - extra_width; new_height = client_rect.height - extra_height; /* Adjusting down instead of up (as done in the above two lines) may * violate minimum size constraints; fix the adjustment if this * happens. */ if (new_width < window->size_hints.min_width) new_width += ((window->size_hints.min_width - new_width)/wi + 1)*wi; if (new_height < window->size_hints.min_height) new_height += ((window->size_hints.min_height - new_height)/hi + 1)*hi; { client_rect.width = new_width; client_rect.height = new_height; meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); new_width = client_rect.width; new_height = client_rect.height; } start_rect = get_start_rect_for_resize (window, info); /* Resize to the new size */ meta_rectangle_resize_with_gravity (start_rect, &info->current, info->resize_gravity, new_width, new_height); return TRUE; } static gboolean constrain_size_limits (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaRectangle min_size, max_size; gboolean too_big, too_small, constraint_already_satisfied; int new_width, new_height; MetaRectangle *start_rect; if (priority > PRIORITY_SIZE_HINTS_LIMITS) return TRUE; /* Determine whether constraint applies; exit if it doesn't. * * Note: The old code didn't apply this constraint for fullscreen or * maximized windows--but that seems odd to me. *shrug* */ if (info->action_type == ACTION_MOVE) return TRUE; /* Determine whether constraint is already satisfied; exit if it is */ get_size_limits (window, &min_size, &max_size); /* We ignore max-size limits for maximized windows; see #327543 */ if (window->maximized_horizontally) max_size.width = MAX (max_size.width, info->current.width); if (window->maximized_vertically) max_size.height = MAX (max_size.height, info->current.height); too_small = !meta_rectangle_could_fit_rect (&info->current, &min_size); too_big = !meta_rectangle_could_fit_rect (&max_size, &info->current); constraint_already_satisfied = !too_big && !too_small; if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ new_width = CLAMP (info->current.width, min_size.width, max_size.width); new_height = CLAMP (info->current.height, min_size.height, max_size.height); start_rect = get_start_rect_for_resize (window, info); meta_rectangle_resize_with_gravity (start_rect, &info->current, info->resize_gravity, new_width, new_height); return TRUE; } static gboolean constrain_aspect_ratio (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { double minr, maxr; gboolean constraints_are_inconsistent, constraint_already_satisfied; int fudge, new_width, new_height; double best_width, best_height; double alt_width, alt_height; MetaRectangle *start_rect; MetaRectangle client_rect; if (priority > PRIORITY_ASPECT_RATIO) return TRUE; /* Determine whether constraint applies; exit if it doesn't. */ minr = window->size_hints.min_aspect.x / (double)window->size_hints.min_aspect.y; maxr = window->size_hints.max_aspect.x / (double)window->size_hints.max_aspect.y; constraints_are_inconsistent = minr > maxr; if (constraints_are_inconsistent || META_WINDOW_MAXIMIZED (window) || window->fullscreen || META_WINDOW_TILED (window) || info->action_type == ACTION_MOVE) return TRUE; /* Determine whether constraint is already satisfied; exit if it is. We * need the following to hold: * * width * minr <= ------ <= maxr * height * * But we need to allow for some slight fudging since width and height * are integers instead of floating point numbers (this is particularly * important when minr == maxr), so we allow width and height to be off * a little bit from strictly satisfying these equations. For just one * sided resizing, we have to make the fudge factor a little bigger * because of how meta_rectangle_resize_with_gravity treats those as * being a resize increment (FIXME: I should handle real resize * increments better here...) */ switch (info->resize_gravity) { case META_GRAVITY_WEST: case META_GRAVITY_NORTH: case META_GRAVITY_SOUTH: case META_GRAVITY_EAST: fudge = 2; break; case META_GRAVITY_NORTH_WEST: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_NORTH_EAST: case META_GRAVITY_SOUTH_EAST: case META_GRAVITY_STATIC: default: fudge = 1; break; } meta_window_frame_rect_to_client_rect (window, &info->current, &client_rect); constraint_already_satisfied = client_rect.width - (client_rect.height * minr ) > -minr*fudge && client_rect.width - (client_rect.height * maxr ) < maxr*fudge; if (check_only || constraint_already_satisfied) return constraint_already_satisfied; /*** Enforce constraint ***/ new_width = client_rect.width; new_height = client_rect.height; switch (info->resize_gravity) { case META_GRAVITY_WEST: case META_GRAVITY_EAST: /* Yeah, I suck for doing implicit rounding -- sue me */ new_height = CLAMP (new_height, new_width / maxr, new_width / minr); break; case META_GRAVITY_NORTH: case META_GRAVITY_SOUTH: /* Yeah, I suck for doing implicit rounding -- sue me */ new_width = CLAMP (new_width, new_height * minr, new_height * maxr); break; case META_GRAVITY_NORTH_WEST: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_NORTH_EAST: case META_GRAVITY_SOUTH_EAST: case META_GRAVITY_STATIC: default: /* Find what width would correspond to new_height, and what height would * correspond to new_width */ alt_width = CLAMP (new_width, new_height * minr, new_height * maxr); alt_height = CLAMP (new_height, new_width / maxr, new_width / minr); /* The line connecting the points (alt_width, new_height) and * (new_width, alt_height) provide a range of * valid-for-the-aspect-ratio-constraint sizes. We want the * size in that range closest to the value requested, i.e. the * point on the line which is closest to the point (new_width, * new_height) */ meta_rectangle_find_linepoint_closest_to_point (alt_width, new_height, new_width, alt_height, new_width, new_height, &best_width, &best_height); /* Yeah, I suck for doing implicit rounding -- sue me */ new_width = best_width; new_height = best_height; break; } { client_rect.width = new_width; client_rect.height = new_height; meta_window_client_rect_to_frame_rect (window, &client_rect, &client_rect); new_width = client_rect.width; new_height = client_rect.height; } start_rect = get_start_rect_for_resize (window, info); meta_rectangle_resize_with_gravity (start_rect, &info->current, info->resize_gravity, new_width, new_height); return TRUE; } static gboolean do_screen_and_monitor_relative_constraints ( MetaWindow *window, GList *region_spanning_rectangles, ConstraintInfo *info, gboolean check_only) { gboolean exit_early = FALSE, constraint_satisfied; MetaRectangle how_far_it_can_be_smushed, min_size, max_size; #ifdef WITH_VERBOSE_MODE if (meta_is_verbose ()) { /* First, log some debugging information */ char spanning_region[1 + 28 * g_list_length (region_spanning_rectangles)]; meta_topic (META_DEBUG_GEOMETRY, "screen/monitor constraint; region_spanning_rectangles: %s\n", meta_rectangle_region_to_string (region_spanning_rectangles, ", ", spanning_region)); } #endif /* Determine whether constraint applies; exit if it doesn't */ how_far_it_can_be_smushed = info->current; get_size_limits (window, &min_size, &max_size); if (info->action_type != ACTION_MOVE) { if (!(info->fixed_directions & FIXED_DIRECTION_X)) how_far_it_can_be_smushed.width = min_size.width; if (!(info->fixed_directions & FIXED_DIRECTION_Y)) how_far_it_can_be_smushed.height = min_size.height; } if (!meta_rectangle_could_fit_in_region (region_spanning_rectangles, &how_far_it_can_be_smushed)) exit_early = TRUE; /* Determine whether constraint is already satisfied; exit if it is */ constraint_satisfied = meta_rectangle_contained_in_region (region_spanning_rectangles, &info->current); if (exit_early || constraint_satisfied || check_only) return constraint_satisfied; /* Enforce constraint */ /* Clamp rectangle size for resize or move+resize actions */ if (info->action_type != ACTION_MOVE) meta_rectangle_clamp_to_fit_into_region (region_spanning_rectangles, info->fixed_directions, &info->current, &min_size); if (info->is_user_action && info->action_type == ACTION_RESIZE) /* For user resize, clip to the relevant region */ meta_rectangle_clip_to_region (region_spanning_rectangles, info->fixed_directions, &info->current); else /* For everything else, shove the rectangle into the relevant region */ meta_rectangle_shove_into_region (region_spanning_rectangles, info->fixed_directions, &info->current); return TRUE; } static gboolean constrain_to_single_monitor (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_SINGLE_MONITOR) return TRUE; /* Exit early if we know the constraint won't apply--note that this constraint * is only meant for normal windows (e.g. we don't want docks to be shoved * "onscreen" by their own strut) and we can't apply it to frameless windows * or else users will be unable to move windows such as XMMS across monitors. */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || meta_monitor_manager_get_num_logical_monitors (monitor_manager) == 1 || !window->require_on_single_monitor || !window->frame || info->is_user_action || meta_window_get_placement_rule (window)) return TRUE; /* Have a helper function handle the constraint for us */ return do_screen_and_monitor_relative_constraints (window, info->usable_monitor_region, info, check_only); } static gboolean constrain_fully_onscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { MetaWindow *transient_for; if (priority > PRIORITY_ENTIRELY_VISIBLE_ON_WORKAREA) return TRUE; transient_for = meta_window_get_transient_for(window); /* Exit early if we know the constraint won't apply--note that this constraint * is only meant for normal windows (e.g. we don't want docks to be shoved * "onscreen" by their own strut). */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->fullscreen || (transient_for && transient_for->fullscreen) || !window->require_fully_onscreen || info->is_user_action || meta_window_get_placement_rule (window)) return TRUE; /* Have a helper function handle the constraint for us */ return do_screen_and_monitor_relative_constraints (window, info->usable_screen_region, info, check_only); } static gboolean constrain_titlebar_visible (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { gboolean unconstrained_user_action; gboolean user_nonnorthern_resize; gboolean retval; int bottom_amount; int horiz_amount_offscreen, vert_amount_offscreen; int horiz_amount_onscreen, vert_amount_onscreen; if (priority > PRIORITY_TITLEBAR_VISIBLE) return TRUE; /* Allow the titlebar beyond the top of the screen only if the user wasn't * clicking on the frame to start the move. */ unconstrained_user_action = info->is_user_action && !window->display->grab_frame_action; /* If the user is resizing anything other than the top, then don't check if * the titlebar is beyond the top of the screen. This resize might be * immediately following an unconstrained move or unconstrained resize that * placed the titlebar above the top of the screen, in which case we don't * want the titlebar immediately popping back below the screen or other * glitching (https://gitlab.gnome.org/GNOME/mutter/-/issues/1206). */ user_nonnorthern_resize = info->is_user_action && meta_grab_op_is_resizing (window->display->grab_op) && info->orig.y == info->current.y; /* Exit early if we know the constraint won't apply--note that this constraint * is only meant for normal windows (e.g. we don't want docks to be shoved * "onscreen" by their own strut). */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->fullscreen || !window->require_titlebar_visible || unconstrained_user_action || user_nonnorthern_resize || meta_window_get_placement_rule (window)) return TRUE; /* Determine how much offscreen things are allowed. We first need to * figure out how much must remain on the screen. For that, we use 25% * window width/height but clamp to the range of (10,75) pixels. This is * somewhat of a seat of my pants random guess at what might look good. * Then, the amount that is allowed off is just the window size minus * this amount (but no less than 0 for tiny windows). */ horiz_amount_onscreen = info->current.width / 4; vert_amount_onscreen = info->current.height / 4; horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; vert_amount_offscreen = info->current.height - vert_amount_onscreen; horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); vert_amount_offscreen = MAX (vert_amount_offscreen, 0); /* Allow the titlebar to touch the bottom panel; If there is no titlebar, * require vert_amount to remain on the screen. */ if (window->frame) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); bottom_amount = info->current.height - borders.visible.top; vert_amount_onscreen = borders.visible.top; } else bottom_amount = vert_amount_offscreen; /* Extend the region, have a helper function handle the constraint, * then return the region to its original size. */ meta_rectangle_expand_region_conditionally (info->usable_screen_region, horiz_amount_offscreen, horiz_amount_offscreen, 0, /* Don't let titlebar off */ bottom_amount, horiz_amount_onscreen, vert_amount_onscreen); retval = do_screen_and_monitor_relative_constraints (window, info->usable_screen_region, info, check_only); meta_rectangle_expand_region_conditionally (info->usable_screen_region, -horiz_amount_offscreen, -horiz_amount_offscreen, 0, /* Don't let titlebar off */ -bottom_amount, horiz_amount_onscreen, vert_amount_onscreen); return retval; } static gboolean constrain_partially_onscreen (MetaWindow *window, ConstraintInfo *info, ConstraintPriority priority, gboolean check_only) { gboolean retval; int top_amount, bottom_amount; int horiz_amount_offscreen, vert_amount_offscreen; int horiz_amount_onscreen, vert_amount_onscreen; if (priority > PRIORITY_PARTIALLY_VISIBLE_ON_WORKAREA) return TRUE; /* Exit early if we know the constraint won't apply--note that this constraint * is only meant for normal windows (e.g. we don't want docks to be shoved * "onscreen" by their own strut). */ if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || meta_window_get_placement_rule (window)) return TRUE; /* Determine how much offscreen things are allowed. We first need to * figure out how much must remain on the screen. For that, we use 25% * window width/height but clamp to the range of (10,75) pixels. This is * somewhat of a seat of my pants random guess at what might look good. * Then, the amount that is allowed off is just the window size minus * this amount (but no less than 0 for tiny windows). */ horiz_amount_onscreen = info->current.width / 4; vert_amount_onscreen = info->current.height / 4; horiz_amount_onscreen = CLAMP (horiz_amount_onscreen, 10, 75); vert_amount_onscreen = CLAMP (vert_amount_onscreen, 10, 75); horiz_amount_offscreen = info->current.width - horiz_amount_onscreen; vert_amount_offscreen = info->current.height - vert_amount_onscreen; horiz_amount_offscreen = MAX (horiz_amount_offscreen, 0); vert_amount_offscreen = MAX (vert_amount_offscreen, 0); top_amount = vert_amount_offscreen; /* Allow the titlebar to touch the bottom panel; If there is no titlebar, * require vert_amount to remain on the screen. */ if (window->frame) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); bottom_amount = info->current.height - borders.visible.top; vert_amount_onscreen = borders.visible.top; } else bottom_amount = vert_amount_offscreen; /* Extend the region, have a helper function handle the constraint, * then return the region to its original size. */ meta_rectangle_expand_region_conditionally (info->usable_screen_region, horiz_amount_offscreen, horiz_amount_offscreen, top_amount, bottom_amount, horiz_amount_onscreen, vert_amount_onscreen); retval = do_screen_and_monitor_relative_constraints (window, info->usable_screen_region, info, check_only); meta_rectangle_expand_region_conditionally (info->usable_screen_region, -horiz_amount_offscreen, -horiz_amount_offscreen, -top_amount, -bottom_amount, horiz_amount_onscreen, vert_amount_onscreen); return retval; } muffin-6.4.1/src/core/main.c0000664000175000017500000004617714723361714014570 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter main() */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:main * @title: Main * @short_description: Program startup. * * Functions which parse the command-line arguments, create the display, * kick everything off and then close down Mutter when it's time to go. * * * * Mutter - a boring window manager for the adult in you * * Many window managers are like Marshmallow Froot Loops; Mutter * is like Frosted Flakes: it's still plain old corn, but dusted * with some sugar. * * The best way to get a handle on how the whole system fits together * is discussed in doc/code-overview.txt; if you're looking for functions * to investigate, read main(), meta_display_open(), and event_callback(). */ #define _XOPEN_SOURCE /* for putenv() and some signal-related functions */ #include "config.h" #include "meta/main.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_INTROSPECTION #include #endif #if defined(HAVE_NATIVE_BACKEND) && defined(HAVE_WAYLAND) #include #endif /* HAVE_WAYLAND && HAVE_NATIVE_BACKEND */ #ifdef HAVE_SYS_PRCTL #include #endif #include "backends/meta-backend-private.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #include "backends/x11/meta-backend-x11.h" #include "clutter/clutter.h" #include "core/display-private.h" #include "core/main-private.h" #include "core/util-private.h" #include "meta/compositor.h" #include "meta/meta-backend.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "ui/ui.h" #include "x11/session.h" #ifdef HAVE_WAYLAND #include "backends/x11/nested/meta-backend-x11-nested.h" #include "wayland/meta-wayland.h" #include "wayland/meta-xwayland.h" #endif #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif /* * The exit code we'll return to our parent process when we eventually die. */ static MetaExitCode meta_exit_code = META_EXIT_SUCCESS; /* * Handle on the main loop, so that we have an easy way of shutting Mutter * down. */ static GMainLoop *meta_main_loop = NULL; static void prefs_changed_callback (MetaPreference pref, gpointer data); /** * meta_print_compilation_info: * * Prints a list of which configure script options were used to * build this copy of Mutter. This is actually always called * on startup, but it's all no-op unless we're in verbose mode * (see meta_set_verbose()). */ static void meta_print_compilation_info (void) { #ifdef HAVE_STARTUP_NOTIFICATION meta_verbose ("Compiled with startup notification\n"); #else meta_verbose ("Compiled without startup notification\n"); #endif } /** * meta_print_self_identity: * * Prints the version number, the current timestamp (not the * build date), the locale, the character encoding, and a list * of configure script options that were used to build this * copy of Mutter. This is actually always called * on startup, but it's all no-op unless we're in verbose mode * (see meta_set_verbose()). */ static void meta_print_self_identity (void) { char buf[256]; GDate d; const char *charset; /* Version and current date. */ g_date_clear (&d, 1); g_date_set_time_t (&d, time (NULL)); g_date_strftime (buf, sizeof (buf), "%x", &d); meta_verbose ("Mutter version %s running on %s\n", VERSION, buf); /* Locale and encoding. */ g_get_charset (&charset); meta_verbose ("Running in locale \"%s\" with encoding \"%s\"\n", setlocale (LC_ALL, NULL), charset); /* Compilation settings. */ meta_print_compilation_info (); } /* * The set of possible options that can be set on Mutter's * command line. */ static gchar *opt_save_file; static gchar *opt_display_name; static gchar *opt_client_id; static gboolean opt_replace_wm; static gboolean opt_disable_sm; static gboolean opt_sync; #ifdef HAVE_WAYLAND static gboolean opt_wayland; static gboolean opt_nested; static gboolean opt_no_x11; #endif #ifdef HAVE_NATIVE_BACKEND static gboolean opt_display_server; #endif static gboolean opt_x11; static GOptionEntry meta_options[] = { { "sm-disable", 0, 0, G_OPTION_ARG_NONE, &opt_disable_sm, N_("Disable connection to session manager"), NULL }, { "replace", 'r', 0, G_OPTION_ARG_NONE, &opt_replace_wm, N_("Replace the running window manager"), NULL }, { "sm-client-id", 0, 0, G_OPTION_ARG_STRING, &opt_client_id, N_("Specify session management ID"), "ID" }, { "display", 'd', 0, G_OPTION_ARG_STRING, &opt_display_name, N_("X Display to use"), "DISPLAY" }, { "sm-save-file", 0, 0, G_OPTION_ARG_FILENAME, &opt_save_file, N_("Initialize session from savefile"), "FILE" }, { "sync", 0, 0, G_OPTION_ARG_NONE, &opt_sync, N_("Make X calls synchronous"), NULL }, #ifdef HAVE_WAYLAND { "wayland", 0, 0, G_OPTION_ARG_NONE, &opt_wayland, N_("Run as a wayland compositor"), NULL }, { "nested", 0, 0, G_OPTION_ARG_NONE, &opt_nested, N_("Run as a nested compositor"), NULL }, { "no-x11", 0, 0, G_OPTION_ARG_NONE, &opt_no_x11, N_("Run wayland compositor without starting Xwayland"), NULL }, #endif #ifdef HAVE_NATIVE_BACKEND { "display-server", 0, 0, G_OPTION_ARG_NONE, &opt_display_server, N_("Run as a full display server, rather than nested") }, #endif { "x11", 0, 0, G_OPTION_ARG_NONE, &opt_x11, N_("Run with X11 backend") }, {NULL} }; /** * meta_get_option_context: (skip) * * Returns a #GOptionContext initialized with mutter-related options. * Parse the command-line args with this before calling meta_init(). * * Return value: the #GOptionContext */ GOptionContext * meta_get_option_context (void) { GOptionContext *ctx; if (setlocale (LC_ALL, "") == NULL) meta_warning ("Locale not understood by C library, internationalization will not work\n"); bindtextdomain (GETTEXT_PACKAGE, MUTTER_LOCALEDIR); bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); ctx = g_option_context_new (NULL); g_option_context_add_main_entries (ctx, meta_options, GETTEXT_PACKAGE); return ctx; } /** * meta_select_display: * * Selects which display Mutter should use. It first tries to use * @display_name as the display. If @display_name is %NULL then * try to use the environment variable MUTTER_DISPLAY. If that * also is %NULL, use the default - :0.0 */ static void meta_select_display (char *display_arg) { const char *display_name; if (display_arg) display_name = (const char *) display_arg; else display_name = g_getenv ("MUTTER_DISPLAY"); if (display_name) g_setenv ("DISPLAY", display_name, TRUE); } static void meta_finalize (void) { MetaDisplay *display = meta_get_display (); if (display) meta_display_close (display, META_CURRENT_TIME); /* I doubt correct timestamps matter here */ #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_wayland_finalize (); #endif } static gboolean on_sigterm (gpointer user_data) { meta_quit (EXIT_SUCCESS); return G_SOURCE_REMOVE; } #if defined(HAVE_WAYLAND) && defined(HAVE_NATIVE_BACKEND) static gboolean session_type_is_supported (const char *session_type) { return (g_strcmp0 (session_type, "x11") == 0) || (g_strcmp0 (session_type, "wayland") == 0); } static char * find_session_type (void) { char **sessions = NULL; char *session_id; char *session_type; const char *session_type_env; gboolean is_tty = FALSE; int ret, i; ret = sd_pid_get_session (0, &session_id); if (ret == 0 && session_id != NULL) { ret = sd_session_get_type (session_id, &session_type); free (session_id); if (ret == 0) { if (session_type_is_supported (session_type)) goto out; else is_tty = g_strcmp0 (session_type, "tty") == 0; free (session_type); } } else if (sd_uid_get_sessions (getuid (), 1, &sessions) > 0) { for (i = 0; sessions[i] != NULL; i++) { ret = sd_session_get_type (sessions[i], &session_type); if (ret < 0) continue; if (session_type_is_supported (session_type)) { g_strfreev (sessions); goto out; } free (session_type); } } g_strfreev (sessions); session_type_env = g_getenv ("XDG_SESSION_TYPE"); if (session_type_is_supported (session_type_env)) { /* The string should be freeable */ session_type = strdup (session_type_env); goto out; } /* Legacy support for starting through xinit */ if (is_tty && (g_getenv ("MUTTER_DISPLAY") || g_getenv ("DISPLAY"))) { session_type = strdup ("x11"); goto out; } meta_warning ("Unsupported session type\n"); meta_exit (META_EXIT_ERROR); out: return session_type; } static gboolean check_for_wayland_session_type (void) { char *session_type; gboolean is_wayland; session_type = find_session_type (); is_wayland = g_strcmp0 (session_type, "wayland") == 0; free (session_type); return is_wayland; } #endif /* * Determine the compositor configuration, i.e. whether to run as a Wayland * compositor, as well as what backend to use. * * There are various different flags affecting this: * * --nested always forces the use of the nested X11 backend * --display-server always forces the use of the native backend * --wayland always forces the compositor type to be a Wayland compositor * * If no flag is passed that forces the compositor type, the compositor type * is determined first from the logind session type, or if that fails, from the * XDG_SESSION_TYPE environment variable. * * If no flag is passed that forces the backend type, the backend type is * determined given the compositor type. If the compositor is a Wayland * compositor, then the native backend is used, or the nested backend, would * the native backend not be enabled at build time. If the compositor is not a * Wayland compositor, then the X11 Compositing Manager backend is used. */ static void calculate_compositor_configuration (MetaCompositorType *compositor_type, GType *backend_gtype) { #ifdef HAVE_WAYLAND gboolean run_as_wayland_compositor = opt_wayland && !opt_x11; #ifdef HAVE_NATIVE_BACKEND if ((opt_wayland || opt_nested || opt_display_server) && opt_x11) #else if ((opt_wayland || opt_nested) && opt_x11) #endif { meta_warning ("Can't run both as Wayland compositor and X11 compositing manager\n"); meta_exit (META_EXIT_ERROR); } #ifdef HAVE_NATIVE_BACKEND if (opt_nested && opt_display_server) { meta_warning ("Can't run both as nested and as a display server\n"); meta_exit (META_EXIT_ERROR); } if (!run_as_wayland_compositor && !opt_x11) run_as_wayland_compositor = check_for_wayland_session_type (); #endif /* HAVE_NATIVE_BACKEND */ if (!run_as_wayland_compositor && opt_no_x11) { meta_warning ("Can't disable X11 support on X11 compositor\n"); meta_exit (META_EXIT_ERROR); } if (run_as_wayland_compositor) *compositor_type = META_COMPOSITOR_TYPE_WAYLAND; else #endif /* HAVE_WAYLAND */ *compositor_type = META_COMPOSITOR_TYPE_X11; #ifdef HAVE_WAYLAND if (opt_nested) { *backend_gtype = META_TYPE_BACKEND_X11_NESTED; return; } #endif /* HAVE_WAYLAND */ #ifdef HAVE_NATIVE_BACKEND if (opt_display_server) { *backend_gtype = META_TYPE_BACKEND_NATIVE; return; } #ifdef HAVE_WAYLAND if (run_as_wayland_compositor) { *backend_gtype = META_TYPE_BACKEND_NATIVE; return; } #endif /* HAVE_WAYLAND */ #endif /* HAVE_NATIVE_BACKEND */ #ifdef HAVE_WAYLAND if (run_as_wayland_compositor) { *backend_gtype = META_TYPE_BACKEND_X11_NESTED; return; } else #endif /* HAVE_WAYLAND */ { *backend_gtype = META_TYPE_BACKEND_X11_CM; return; } } static gboolean _compositor_configuration_overridden = FALSE; static MetaCompositorType _compositor_type_override; static GType _backend_gtype_override; void meta_override_compositor_configuration (MetaCompositorType compositor_type, GType backend_gtype) { _compositor_configuration_overridden = TRUE; _compositor_type_override = compositor_type; _backend_gtype_override = backend_gtype; } /** * meta_init: (skip) * * Initialize mutter. Call this after meta_get_option_context() and * meta_plugin_manager_set_plugin_type(), and before meta_run(). */ void meta_init (void) { struct sigaction act; sigset_t empty_mask; MetaCompositorType compositor_type; GType backend_gtype; #ifdef HAVE_SYS_PRCTL prctl (PR_SET_DUMPABLE, 1); #endif sigemptyset (&empty_mask); act.sa_handler = SIG_IGN; act.sa_mask = empty_mask; act.sa_flags = 0; if (sigaction (SIGPIPE, &act, NULL) < 0) g_printerr ("Failed to register SIGPIPE handler: %s\n", g_strerror (errno)); #ifdef SIGXFSZ if (sigaction (SIGXFSZ, &act, NULL) < 0) g_printerr ("Failed to register SIGXFSZ handler: %s\n", g_strerror (errno)); #endif g_unix_signal_add (SIGTERM, on_sigterm, NULL); if (g_getenv ("MUTTER_VERBOSE")) meta_set_verbose (TRUE); if (g_getenv ("MUTTER_DEBUG")) meta_set_debugging (TRUE); if (_compositor_configuration_overridden) { compositor_type = _compositor_type_override; backend_gtype = _backend_gtype_override; } else { calculate_compositor_configuration (&compositor_type, &backend_gtype); } #ifdef HAVE_WAYLAND if (compositor_type == META_COMPOSITOR_TYPE_WAYLAND) meta_set_is_wayland_compositor (TRUE); #endif if (g_get_home_dir ()) if (chdir (g_get_home_dir ()) < 0) meta_warning ("Could not change to home directory %s.\n", g_get_home_dir ()); meta_print_self_identity (); #ifdef HAVE_INTROSPECTION g_irepository_prepend_search_path (MUTTER_PKGLIBDIR); #endif /* NB: When running as a hybrid wayland compositor we run our own headless X * server so the user can't control the X display to connect too. */ if (!meta_is_wayland_compositor ()) meta_select_display (opt_display_name); meta_init_backend (backend_gtype); meta_set_syncing (opt_sync || (g_getenv ("MUTTER_SYNC") != NULL)); if (opt_replace_wm) meta_set_replace_current_wm (TRUE); if (opt_save_file && opt_client_id) meta_fatal ("Can't specify both SM save file and SM client id\n"); meta_main_loop = g_main_loop_new (NULL, FALSE); } /** * meta_register_with_session: * * Registers mutter with the session manager. Call this after completing your own * initialization. * * This should be called when the session manager can safely continue to the * next phase of startup and potentially display windows. */ void meta_register_with_session (void) { if (!opt_disable_sm) { if (opt_client_id == NULL) { const gchar *desktop_autostart_id; desktop_autostart_id = g_getenv ("DESKTOP_AUTOSTART_ID"); if (desktop_autostart_id != NULL) opt_client_id = g_strdup (desktop_autostart_id); } /* Unset DESKTOP_AUTOSTART_ID in order to avoid child processes to * use the same client id. */ g_unsetenv ("DESKTOP_AUTOSTART_ID"); meta_session_init (opt_client_id, opt_save_file); } /* Free memory possibly allocated by the argument parsing which are * no longer needed. */ g_free (opt_save_file); g_free (opt_display_name); g_free (opt_client_id); } /** * meta_run: (skip) * * Runs mutter. Call this after completing initialization that doesn't require * an event loop. * * Return value: mutter's exit status */ int meta_run (void) { /* Load prefs */ meta_prefs_init (); meta_prefs_add_listener (prefs_changed_callback, NULL); if (!meta_display_open ()) meta_exit (META_EXIT_ERROR); g_main_loop_run (meta_main_loop); meta_finalize (); return meta_exit_code; } /** * meta_quit: * @code: The success or failure code to return to the calling process. * * Stops Mutter. This tells the event loop to stop processing; it is * rather dangerous to use this because this will leave the user with * no window manager. We generally do this only if, for example, the * session manager asks us to; we assume the session manager knows * what it's talking about. */ void meta_quit (MetaExitCode code) { if (g_main_loop_is_running (meta_main_loop)) { meta_exit_code = code; g_main_loop_quit (meta_main_loop); } } /** * prefs_changed_callback: * @pref: Which preference has changed * @data: Arbitrary data (which we ignore) * * Called on pref changes. (One of several functions of its kind and purpose.) * * FIXME: Why are these particular prefs handled in main.c and not others? * Should they be? */ static void prefs_changed_callback (MetaPreference pref, gpointer data) { switch (pref) { case META_PREF_DRAGGABLE_BORDER_WIDTH: meta_display_queue_retheme_all_windows (meta_get_display ()); break; default: /* handled elsewhere or otherwise */ break; } } MetaDisplayPolicy meta_get_x11_display_policy (void) { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11_CM (backend)) return META_DISPLAY_POLICY_MANDATORY; #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) { MetaSettings *settings = meta_backend_get_settings (backend); if (opt_no_x11) return META_DISPLAY_POLICY_DISABLED; if (meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND)) return META_DISPLAY_POLICY_ON_DEMAND; } #endif return META_DISPLAY_POLICY_MANDATORY; } void meta_test_init (void) { #if defined(HAVE_WAYLAND) g_autofree char *display_name = g_strdup ("mutter-test-display-XXXXXX"); int fd = g_mkstemp (display_name); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_X11_NESTED); meta_wayland_override_display_name (display_name); meta_xwayland_override_display_number (512 + rand() % 512); meta_init (); close (fd); #else g_warning ("Tests require wayland support"); #endif } muffin-6.4.1/src/core/boxes.c0000664000175000017500000021611514723361714014753 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:boxes * @Title: MetaRectangle * @Short_Description: Simple box operations */ /* * Copyright (C) 2005, 2006 Elijah Newren * [meta_rectangle_intersect() is copyright the GTK+ Team according to Havoc, * see gdkrectangle.c. As far as Havoc knows, he probably wrote * meta_rectangle_equal(), and I'm guessing it's (C) Red Hat. So...] * Copyright (C) 1995-2000 GTK+ Team * Copyright (C) 2002 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-monitor-transform.h" #include "core/boxes-private.h" #include #include #include "meta/util.h" /* It would make sense to use GSlice here, but until we clean up the * rest of this file and the internal API to use these functions, we * leave it using g_malloc()/g_free() for consistency. */ MetaRectangle * meta_rectangle_copy (const MetaRectangle *rect) { return g_memdup2 (rect, sizeof (MetaRectangle)); } void meta_rectangle_free (MetaRectangle *rect) { g_free (rect); } G_DEFINE_BOXED_TYPE (MetaRectangle, meta_rectangle, meta_rectangle_copy, meta_rectangle_free); char* meta_rectangle_to_string (const MetaRectangle *rect, char *output) { /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit. * Should be more than enough space. Note that of this space, the * trailing \0 will be overwritten for all but the last rectangle. */ g_snprintf (output, RECT_LENGTH, "%d,%d +%d,%d", rect->x, rect->y, rect->width, rect->height); return output; } char* meta_rectangle_region_to_string (GList *region, const char *separator_string, char *output) { /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 * for each digit. Should be more than enough space. Note that of this * space, the trailing \0 will be overwritten for all but the last * rectangle. */ char rect_string[RECT_LENGTH]; GList *tmp = region; char *cur = output; if (region == NULL) g_snprintf (output, 10, "(EMPTY)"); while (tmp) { MetaRectangle *rect = tmp->data; g_snprintf (rect_string, RECT_LENGTH, "[%d,%d +%d,%d]", rect->x, rect->y, rect->width, rect->height); cur = g_stpcpy (cur, rect_string); tmp = tmp->next; if (tmp) cur = g_stpcpy (cur, separator_string); } return output; } char* meta_rectangle_edge_to_string (const MetaEdge *edge, char *output) { /* 25 chars: 2 commas, space, plus, trailing \0 + 5 for each digit. * Should be more than enough space. Note that of this space, the * trailing \0 will be overwritten for all but the last rectangle. * * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and * 2 more spaces, for a total of 10 more. */ g_snprintf (output, EDGE_LENGTH, "[%d,%d +%d,%d], %2d, %2d", edge->rect.x, edge->rect.y, edge->rect.width, edge->rect.height, edge->side_type, edge->edge_type); return output; } char* meta_rectangle_edge_list_to_string (GList *edge_list, const char *separator_string, char *output) { /* 27 chars: 2 commas, 2 square brackets, space, plus, trailing \0 + 5 for * each digit. Should be more than enough space. Note that of this * space, the trailing \0 will be overwritten for all but the last * rectangle. * * Plus 2 for parenthesis, 4 for 2 more numbers, 2 more commas, and * 2 more spaces, for a total of 10 more. */ char rect_string[EDGE_LENGTH]; char *cur = output; GList *tmp = edge_list; if (edge_list == NULL) g_snprintf (output, 10, "(EMPTY)"); while (tmp) { MetaEdge *edge = tmp->data; MetaRectangle *rect = &edge->rect; g_snprintf (rect_string, EDGE_LENGTH, "([%d,%d +%d,%d], %2d, %2d)", rect->x, rect->y, rect->width, rect->height, edge->side_type, edge->edge_type); cur = g_stpcpy (cur, rect_string); tmp = tmp->next; if (tmp) cur = g_stpcpy (cur, separator_string); } return output; } MetaRectangle meta_rect (int x, int y, int width, int height) { MetaRectangle temporary; temporary.x = x; temporary.y = y; temporary.width = width; temporary.height = height; return temporary; } int meta_rectangle_area (const MetaRectangle *rect) { g_return_val_if_fail (rect != NULL, 0); return rect->width * rect->height; } /** * meta_rectangle_intersect: * @src1: a #MetaRectangle * @src2: another #MetaRectangle * @dest: (out caller-allocates): an empty #MetaRectangle, to be filled * with the coordinates of the intersection. * * Returns: TRUE is some intersection exists and is not degenerate, FALSE * otherwise. */ gboolean meta_rectangle_intersect (const MetaRectangle *src1, const MetaRectangle *src2, MetaRectangle *dest) { int dest_x, dest_y; int dest_w, dest_h; int return_val; g_return_val_if_fail (src1 != NULL, FALSE); g_return_val_if_fail (src2 != NULL, FALSE); g_return_val_if_fail (dest != NULL, FALSE); return_val = FALSE; dest_x = MAX (src1->x, src2->x); dest_y = MAX (src1->y, src2->y); dest_w = MIN (src1->x + src1->width, src2->x + src2->width) - dest_x; dest_h = MIN (src1->y + src1->height, src2->y + src2->height) - dest_y; if (dest_w > 0 && dest_h > 0) { dest->x = dest_x; dest->y = dest_y; dest->width = dest_w; dest->height = dest_h; return_val = TRUE; } else { dest->width = 0; dest->height = 0; } return return_val; } gboolean meta_rectangle_equal (const MetaRectangle *src1, const MetaRectangle *src2) { return ((src1->x == src2->x) && (src1->y == src2->y) && (src1->width == src2->width) && (src1->height == src2->height)); } /** * meta_rectangle_union: * @rect1: a #MetaRectangle * @rect2: another #MetaRectangle * @dest: (out caller-allocates): an empty #MetaRectangle, to be filled * with the coordinates of the bounding box. */ void meta_rectangle_union (const MetaRectangle *rect1, const MetaRectangle *rect2, MetaRectangle *dest) { int dest_x, dest_y; int dest_w, dest_h; dest_x = rect1->x; dest_y = rect1->y; dest_w = rect1->width; dest_h = rect1->height; if (rect2->x < dest_x) { dest_w += dest_x - rect2->x; dest_x = rect2->x; } if (rect2->y < dest_y) { dest_h += dest_y - rect2->y; dest_y = rect2->y; } if (rect2->x + rect2->width > dest_x + dest_w) dest_w = rect2->x + rect2->width - dest_x; if (rect2->y + rect2->height > dest_y + dest_h) dest_h = rect2->y + rect2->height - dest_y; dest->x = dest_x; dest->y = dest_y; dest->width = dest_w; dest->height = dest_h; } gboolean meta_rectangle_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2) { g_return_val_if_fail (rect1 != NULL, FALSE); g_return_val_if_fail (rect2 != NULL, FALSE); return !((rect1->x + rect1->width <= rect2->x) || (rect2->x + rect2->width <= rect1->x) || (rect1->y + rect1->height <= rect2->y) || (rect2->y + rect2->height <= rect1->y)); } gboolean meta_rectangle_vert_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2) { return (rect1->y < rect2->y + rect2->height && rect2->y < rect1->y + rect1->height); } gboolean meta_rectangle_horiz_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2) { return (rect1->x < rect2->x + rect2->width && rect2->x < rect1->x + rect1->width); } gboolean meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect) { return (outer_rect->width >= inner_rect->width && outer_rect->height >= inner_rect->height); } gboolean meta_rectangle_contains_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect) { return inner_rect->x >= outer_rect->x && inner_rect->y >= outer_rect->y && inner_rect->x + inner_rect->width <= outer_rect->x + outer_rect->width && inner_rect->y + inner_rect->height <= outer_rect->y + outer_rect->height; } void meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect, MetaRectangle *rect, MetaGravity gravity, int new_width, int new_height) { /* FIXME: I'm too deep into this to know whether the below comment is * still clear or not now that I've moved it out of constraints.c. * boxes.h has a good comment, but I'm not sure if the below info is also * helpful on top of that (or whether it has superfluous info). */ /* These formulas may look overly simplistic at first but you can work * everything out with a left_frame_with, right_frame_width, * border_width, and old and new client area widths (instead of old total * width and new total width) and you come up with the same formulas. * * Also, note that the reason we can treat META_GRAVITY_NORTH_WEST and * META_GRAVITY_STATIC the same is because we're not given a location at * which to place the window--the window was already placed * appropriately before. So, META_GRAVITY_NORTH_WEST for this function * means to just leave the upper left corner of the outer window * where it already is, and META_GRAVITY_STATIC for this function means to * just leave the upper left corner of the inner window where it * already is. But leaving either of those two corners where they * already are will ensure that the other corner is fixed as well * (since frame size doesn't change)--thus making the two * equivalent. */ /* First, the x direction */ switch (gravity) { case META_GRAVITY_NORTH_WEST: case META_GRAVITY_WEST: case META_GRAVITY_SOUTH_WEST: rect->x = old_rect->x; break; case META_GRAVITY_NORTH: case META_GRAVITY_CENTER: case META_GRAVITY_SOUTH: /* FIXME: Needing to adjust new_width kind of sucks, but not doing so * would cause drift. */ new_width -= (old_rect->width - new_width) % 2; rect->x = old_rect->x + (old_rect->width - new_width)/2; break; case META_GRAVITY_NORTH_EAST: case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: rect->x = old_rect->x + (old_rect->width - new_width); break; case META_GRAVITY_STATIC: default: rect->x = old_rect->x; break; } rect->width = new_width; /* Next, the y direction */ switch (gravity) { case META_GRAVITY_NORTH_WEST: case META_GRAVITY_NORTH: case META_GRAVITY_NORTH_EAST: rect->y = old_rect->y; break; case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_EAST: /* FIXME: Needing to adjust new_height kind of sucks, but not doing so * would cause drift. */ new_height -= (old_rect->height - new_height) % 2; rect->y = old_rect->y + (old_rect->height - new_height)/2; break; case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_EAST: rect->y = old_rect->y + (old_rect->height - new_height); break; case META_GRAVITY_STATIC: default: rect->y = old_rect->y; break; } rect->height = new_height; } /* Not so simple helper function for get_minimal_spanning_set_for_region() */ static GList* merge_spanning_rects_in_region (GList *region) { /* NOTE FOR ANY OPTIMIZATION PEOPLE OUT THERE: Please see the * documentation of get_minimal_spanning_set_for_region() for performance * considerations that also apply to this function. */ GList* compare; compare = region; if (region == NULL) { g_warning ("Region to merge was empty! Either you have a some " "pathological STRUT list or there's a bug somewhere!\n"); return NULL; } while (compare && compare->next) { MetaRectangle *a = compare->data; GList *other = compare->next; g_assert (a->width > 0 && a->height > 0); while (other) { MetaRectangle *b = other->data; GList *delete_me = NULL; g_assert (b->width > 0 && b->height > 0); /* If a contains b, just remove b */ if (meta_rectangle_contains_rect (a, b)) { delete_me = other; } /* If b contains a, just remove a */ else if (meta_rectangle_contains_rect (b, a)) { delete_me = compare; } /* If a and b might be mergeable horizontally */ else if (a->y == b->y && a->height == b->height) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; } /* If a and b are adjacent */ else if (a->x + a->width == b->x || a->x == b->x + b->width) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; } } /* If a and b might be mergeable vertically */ else if (a->x == b->x && a->width == b->width) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; } /* If a and b are adjacent */ else if (a->y + a->height == b->y || a->y == b->y + b->height) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; } } other = other->next; /* Delete any rectangle in the list that is no longer wanted */ if (delete_me != NULL) { /* Deleting the rect we compare others to is a little tricker */ if (compare == delete_me) { compare = compare->next; other = compare->next; a = compare->data; } /* Okay, we can free it now */ g_free (delete_me->data); region = g_list_delete_link (region, delete_me); } } compare = compare->next; } return region; } /* Simple helper function for get_minimal_spanning_set_for_region()... */ static gint compare_rect_areas (gconstpointer a, gconstpointer b) { const MetaRectangle *a_rect = (gconstpointer) a; const MetaRectangle *b_rect = (gconstpointer) b; int a_area = meta_rectangle_area (a_rect); int b_area = meta_rectangle_area (b_rect); return b_area - a_area; /* positive ret value denotes b > a, ... */ } /* ... and another helper for get_minimal_spanning_set_for_region()... */ static gboolean check_strut_align (MetaStrut *strut, const MetaRectangle *rect) { /* Check whether @strut actually aligns to the side of @rect it claims */ switch (strut->side) { case META_SIDE_TOP: return BOX_TOP (strut->rect) <= BOX_TOP (*rect); case META_SIDE_BOTTOM: return BOX_BOTTOM (strut->rect) >= BOX_BOTTOM (*rect); case META_SIDE_LEFT: return BOX_LEFT (strut->rect) <= BOX_LEFT (*rect); case META_SIDE_RIGHT: return BOX_RIGHT (strut->rect) >= BOX_RIGHT (*rect); default: return FALSE; } } /** * meta_rectangle_get_minimal_spanning_set_for_region: * @basic_rect: Input rectangle * @all_struts: (element-type Meta.Rectangle): List of struts * * This function is trying to find a "minimal spanning set (of rectangles)" * for a given region. * * The region is given by taking basic_rect, then removing the areas * covered by all the rectangles in the all_struts list, and then expanding * the resulting region by the given number of pixels in each direction. * * A "minimal spanning set (of rectangles)" is the best name I could come * up with for the concept I had in mind. Basically, for a given region, I * want a set of rectangles with the property that a window is contained in * the region if and only if it is contained within at least one of the * rectangles. * * Returns: (transfer full) (element-type Meta.Rectangle): Minimal spanning set */ GList* meta_rectangle_get_minimal_spanning_set_for_region ( const MetaRectangle *basic_rect, const GSList *all_struts) { /* NOTE FOR OPTIMIZERS: This function *might* be somewhat slow, * especially due to the call to merge_spanning_rects_in_region() (which * is O(n^2) where n is the size of the list generated in this function). * This is made more onerous due to the fact that it involves a fair * number of memory allocation and deallocation calls. However, n is 1 * for default installations of Gnome (because partial struts aren't used * by default and only partial struts increase the size of the spanning * set generated). With one partial strut, n will be 2 or 3. With 2 * partial struts, n will probably be 4 or 5. So, n probably isn't large * enough to make this worth bothering. Further, it is only called from * workspace.c:ensure_work_areas_validated (at least as of the time of * writing this comment), which in turn should only be called if the * strut list changes or the screen or monitor size changes. If it ever * does show up on profiles (most likely because people start using * ridiculously huge numbers of partial struts), possible optimizations * include: * * (1) rewrite merge_spanning_rects_in_region() to be O(n) or O(nlogn). * I'm not totally sure it's possible, but with a couple copies of * the list and sorting them appropriately, I believe it might be. * (2) only call merge_spanning_rects_in_region() with a subset of the * full list of rectangles. I believe from some of my preliminary * debugging and thinking about it that it is possible to figure out * apriori groups of rectangles which are only merge candidates with * each other. (See testboxes.c:get_screen_region() when which==2 * and track the steps of this function carefully to see what gave * me the hint that this might work) * (3) figure out how to avoid merge_spanning_rects_in_region(). I think * it might be possible to modify this function to make that * possible, and I spent just a little while thinking about it, but n * wasn't large enough to convince me to care yet. * (4) Some of the stuff Rob mentioned at http://mail.gnome.org/archives\ * /metacity-devel-list/2005-November/msg00028.html. (Sorry for the * URL splitting.) */ GList *ret; GList *tmp_list; const GSList *strut_iter; MetaRectangle *temp_rect; /* The algorithm is basically as follows: * Initialize rectangle_set to basic_rect * Foreach strut: * Foreach rectangle in rectangle_set: * - Split the rectangle into new rectangles that don't overlap the * strut (but which are as big as possible otherwise) * - Remove the old (pre-split) rectangle from the rectangle_set, * and replace it with the new rectangles generated from the * splitting */ temp_rect = g_new (MetaRectangle, 1); *temp_rect = *basic_rect; ret = g_list_prepend (NULL, temp_rect); for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next) { GList *rect_iter; MetaStrut *strut = (MetaStrut*)strut_iter->data; MetaRectangle *strut_rect = &strut->rect; tmp_list = ret; ret = NULL; rect_iter = tmp_list; while (rect_iter) { MetaRectangle *rect = (MetaRectangle*) rect_iter->data; if (!meta_rectangle_overlap (strut_rect, rect) || !check_strut_align (strut, basic_rect)) ret = g_list_prepend (ret, rect); else { /* If there is area in rect left of strut */ if (BOX_LEFT (*rect) < BOX_LEFT (*strut_rect)) { temp_rect = g_new (MetaRectangle, 1); *temp_rect = *rect; temp_rect->width = BOX_LEFT (*strut_rect) - BOX_LEFT (*rect); ret = g_list_prepend (ret, temp_rect); } /* If there is area in rect right of strut */ if (BOX_RIGHT (*rect) > BOX_RIGHT (*strut_rect)) { int new_x; temp_rect = g_new (MetaRectangle, 1); *temp_rect = *rect; new_x = BOX_RIGHT (*strut_rect); temp_rect->width = BOX_RIGHT(*rect) - new_x; temp_rect->x = new_x; ret = g_list_prepend (ret, temp_rect); } /* If there is area in rect above strut */ if (BOX_TOP (*rect) < BOX_TOP (*strut_rect)) { temp_rect = g_new (MetaRectangle, 1); *temp_rect = *rect; temp_rect->height = BOX_TOP (*strut_rect) - BOX_TOP (*rect); ret = g_list_prepend (ret, temp_rect); } /* If there is area in rect below strut */ if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*strut_rect)) { int new_y; temp_rect = g_new (MetaRectangle, 1); *temp_rect = *rect; new_y = BOX_BOTTOM (*strut_rect); temp_rect->height = BOX_BOTTOM (*rect) - new_y; temp_rect->y = new_y; ret = g_list_prepend (ret, temp_rect); } g_free (rect); } rect_iter = rect_iter->next; } g_list_free (tmp_list); } /* Sort by maximal area, just because I feel like it... */ ret = g_list_sort (ret, compare_rect_areas); /* Merge rectangles if possible so that the list really is minimal */ ret = merge_spanning_rects_in_region (ret); return ret; } /** * meta_rectangle_expand_region: (skip) * */ GList* meta_rectangle_expand_region (GList *region, const int left_expand, const int right_expand, const int top_expand, const int bottom_expand) { return meta_rectangle_expand_region_conditionally (region, left_expand, right_expand, top_expand, bottom_expand, 0, 0); } /** * meta_rectangle_expand_region_conditionally: (skip) * */ GList* meta_rectangle_expand_region_conditionally (GList *region, const int left_expand, const int right_expand, const int top_expand, const int bottom_expand, const int min_x, const int min_y) { GList *tmp_list = region; while (tmp_list) { MetaRectangle *rect = (MetaRectangle*) tmp_list->data; if (rect->width >= min_x) { rect->x -= left_expand; rect->width += (left_expand + right_expand); } if (rect->height >= min_y) { rect->y -= top_expand; rect->height += (top_expand + bottom_expand); } tmp_list = tmp_list->next; } return region; } void meta_rectangle_expand_to_avoiding_struts (MetaRectangle *rect, const MetaRectangle *expand_to, const MetaDirection direction, const GSList *all_struts) { const GSList *strut_iter; /* If someone wants this function to handle more fine-grained * direction expanding in the future (e.g. only left, or fully * horizontal plus upward), feel free. But I'm hard-coding for both * horizontal directions (exclusive-)or both vertical directions. */ g_assert ((direction == META_DIRECTION_HORIZONTAL) ^ (direction == META_DIRECTION_VERTICAL )); if (direction == META_DIRECTION_HORIZONTAL) { rect->x = expand_to->x; rect->width = expand_to->width; } else { rect->y = expand_to->y; rect->height = expand_to->height; } /* Run over all struts */ for (strut_iter = all_struts; strut_iter; strut_iter = strut_iter->next) { MetaStrut *strut = (MetaStrut*) strut_iter->data; /* Skip struts that don't overlap */ if (!meta_rectangle_overlap (&strut->rect, rect)) continue; if (direction == META_DIRECTION_HORIZONTAL) { if (strut->side == META_SIDE_LEFT) { int offset = BOX_RIGHT(strut->rect) - BOX_LEFT(*rect); rect->x += offset; rect->width -= offset; } else if (strut->side == META_SIDE_RIGHT) { int offset = BOX_RIGHT (*rect) - BOX_LEFT(strut->rect); rect->width -= offset; } /* else ignore the strut */ } else /* direction == META_DIRECTION_VERTICAL */ { if (strut->side == META_SIDE_TOP) { int offset = BOX_BOTTOM(strut->rect) - BOX_TOP(*rect); rect->y += offset; rect->height -= offset; } else if (strut->side == META_SIDE_BOTTOM) { int offset = BOX_BOTTOM(*rect) - BOX_TOP(strut->rect); rect->height -= offset; } /* else ignore the strut */ } } /* end loop over struts */ } /* end meta_rectangle_expand_to_avoiding_struts */ void meta_rectangle_free_list_and_elements (GList *filled_list) { g_list_free_full (filled_list, g_free); } gboolean meta_rectangle_could_fit_in_region (const GList *spanning_rects, const MetaRectangle *rect) { const GList *temp; gboolean could_fit; temp = spanning_rects; could_fit = FALSE; while (!could_fit && temp != NULL) { could_fit = could_fit || meta_rectangle_could_fit_rect (temp->data, rect); temp = temp->next; } return could_fit; } gboolean meta_rectangle_contained_in_region (const GList *spanning_rects, const MetaRectangle *rect) { const GList *temp; gboolean contained; temp = spanning_rects; contained = FALSE; while (!contained && temp != NULL) { contained = contained || meta_rectangle_contains_rect (temp->data, rect); temp = temp->next; } return contained; } gboolean meta_rectangle_overlaps_with_region (const GList *spanning_rects, const MetaRectangle *rect) { const GList *temp; gboolean overlaps; temp = spanning_rects; overlaps = FALSE; while (!overlaps && temp != NULL) { overlaps = overlaps || meta_rectangle_overlap (temp->data, rect); temp = temp->next; } return overlaps; } gboolean meta_rectangle_has_adjacent_in_region (const GList *spanning_rects, const MetaRectangle *rect) { const GList *l; for (l = spanning_rects; l; l = l->next) { MetaRectangle *other = (MetaRectangle *) l->data; if (rect == other || meta_rectangle_equal (rect, other)) continue; if (meta_rectangle_is_adjacent_to ((MetaRectangle *) rect, other)) { return TRUE; } } return FALSE; } void meta_rectangle_clamp_to_fit_into_region (const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect, const MetaRectangle *min_size) { const GList *temp; const MetaRectangle *best_rect = NULL; int best_overlap = 0; /* First, find best rectangle from spanning_rects to which we can clamp * rect to fit into. */ for (temp = spanning_rects; temp; temp = temp->next) { MetaRectangle *compare_rect = temp->data; int maximal_overlap_amount_for_compare; /* If x is fixed and the entire width of rect doesn't fit in compare, * skip this rectangle. */ if ((fixed_directions & FIXED_DIRECTION_X) && (compare_rect->x > rect->x || compare_rect->x + compare_rect->width < rect->x + rect->width)) continue; /* If y is fixed and the entire height of rect doesn't fit in compare, * skip this rectangle. */ if ((fixed_directions & FIXED_DIRECTION_Y) && (compare_rect->y > rect->y || compare_rect->y + compare_rect->height < rect->y + rect->height)) continue; /* If compare can't hold the min_size window, skip this rectangle. */ if (compare_rect->width < min_size->width || compare_rect->height < min_size->height) continue; /* Determine maximal overlap amount */ maximal_overlap_amount_for_compare = MIN (rect->width, compare_rect->width) * MIN (rect->height, compare_rect->height); /* See if this is the best rect so far */ if (maximal_overlap_amount_for_compare > best_overlap) { best_rect = compare_rect; best_overlap = maximal_overlap_amount_for_compare; } } /* Clamp rect appropriately */ if (best_rect == NULL) { g_warning ("No rect whose size to clamp to found!\n"); /* If it doesn't fit, at least make it no bigger than it has to be */ if (!(fixed_directions & FIXED_DIRECTION_X)) rect->width = min_size->width; if (!(fixed_directions & FIXED_DIRECTION_Y)) rect->height = min_size->height; } else { rect->width = MIN (rect->width, best_rect->width); rect->height = MIN (rect->height, best_rect->height); } } void meta_rectangle_clip_to_region (const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect) { const GList *temp; const MetaRectangle *best_rect = NULL; int best_overlap = 0; /* First, find best rectangle from spanning_rects to which we will clip * rect into. */ for (temp = spanning_rects; temp; temp = temp->next) { MetaRectangle *compare_rect = temp->data; MetaRectangle overlap; int maximal_overlap_amount_for_compare; /* If x is fixed and the entire width of rect doesn't fit in compare, * skip the rectangle. */ if ((fixed_directions & FIXED_DIRECTION_X) && (compare_rect->x > rect->x || compare_rect->x + compare_rect->width < rect->x + rect->width)) continue; /* If y is fixed and the entire height of rect doesn't fit in compare, * skip the rectangle. */ if ((fixed_directions & FIXED_DIRECTION_Y) && (compare_rect->y > rect->y || compare_rect->y + compare_rect->height < rect->y + rect->height)) continue; /* Determine maximal overlap amount */ meta_rectangle_intersect (rect, compare_rect, &overlap); maximal_overlap_amount_for_compare = meta_rectangle_area (&overlap); /* See if this is the best rect so far */ if (maximal_overlap_amount_for_compare > best_overlap) { best_rect = compare_rect; best_overlap = maximal_overlap_amount_for_compare; } } /* Clip rect appropriately */ if (best_rect == NULL) { g_warning ("No rect to clip to found!\n"); } else { /* Extra precaution with checking fixed direction shouldn't be needed * due to logic above, but it shouldn't hurt either. */ if (!(fixed_directions & FIXED_DIRECTION_X)) { /* Find the new left and right */ int new_x = MAX (rect->x, best_rect->x); rect->width = MIN ((rect->x + rect->width) - new_x, (best_rect->x + best_rect->width) - new_x); rect->x = new_x; } /* Extra precaution with checking fixed direction shouldn't be needed * due to logic above, but it shouldn't hurt either. */ if (!(fixed_directions & FIXED_DIRECTION_Y)) { /* Clip the top, if needed */ int new_y = MAX (rect->y, best_rect->y); rect->height = MIN ((rect->y + rect->height) - new_y, (best_rect->y + best_rect->height) - new_y); rect->y = new_y; } } } void meta_rectangle_shove_into_region (const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect) { const GList *temp; const MetaRectangle *best_rect = NULL; int best_overlap = 0; int shortest_distance = G_MAXINT; /* First, find best rectangle from spanning_rects to which we will shove * rect into. */ for (temp = spanning_rects; temp; temp = temp->next) { MetaRectangle *compare_rect = temp->data; int maximal_overlap_amount_for_compare; int dist_to_compare; /* If x is fixed and the entire width of rect doesn't fit in compare, * skip this rectangle. */ if ((fixed_directions & FIXED_DIRECTION_X) && (compare_rect->x > rect->x || compare_rect->x + compare_rect->width < rect->x + rect->width)) continue; /* If y is fixed and the entire height of rect doesn't fit in compare, * skip this rectangle. */ if ((fixed_directions & FIXED_DIRECTION_Y) && (compare_rect->y > rect->y || compare_rect->y + compare_rect->height < rect->y + rect->height)) continue; /* Determine maximal overlap amount between rect & compare_rect */ maximal_overlap_amount_for_compare = MIN (rect->width, compare_rect->width) * MIN (rect->height, compare_rect->height); /* Determine distance necessary to put rect into compare_rect */ dist_to_compare = 0; if (compare_rect->x > rect->x) dist_to_compare += compare_rect->x - rect->x; if (compare_rect->x + compare_rect->width < rect->x + rect->width) dist_to_compare += (rect->x + rect->width) - (compare_rect->x + compare_rect->width); if (compare_rect->y > rect->y) dist_to_compare += compare_rect->y - rect->y; if (compare_rect->y + compare_rect->height < rect->y + rect->height) dist_to_compare += (rect->y + rect->height) - (compare_rect->y + compare_rect->height); /* See if this is the best rect so far */ if ((maximal_overlap_amount_for_compare > best_overlap) || (maximal_overlap_amount_for_compare == best_overlap && dist_to_compare < shortest_distance)) { best_rect = compare_rect; best_overlap = maximal_overlap_amount_for_compare; shortest_distance = dist_to_compare; } } /* Shove rect appropriately */ if (best_rect == NULL) { g_warning ("No rect to shove into found!\n"); } else { /* Extra precaution with checking fixed direction shouldn't be needed * due to logic above, but it shouldn't hurt either. */ if (!(fixed_directions & FIXED_DIRECTION_X)) { /* Shove to the right, if needed */ if (best_rect->x > rect->x) rect->x = best_rect->x; /* Shove to the left, if needed */ if (best_rect->x + best_rect->width < rect->x + rect->width) rect->x = (best_rect->x + best_rect->width) - rect->width; } /* Extra precaution with checking fixed direction shouldn't be needed * due to logic above, but it shouldn't hurt either. */ if (!(fixed_directions & FIXED_DIRECTION_Y)) { /* Shove down, if needed */ if (best_rect->y > rect->y) rect->y = best_rect->y; /* Shove up, if needed */ if (best_rect->y + best_rect->height < rect->y + rect->height) rect->y = (best_rect->y + best_rect->height) - rect->height; } } } void meta_rectangle_find_linepoint_closest_to_point (double x1, double y1, double x2, double y2, double px, double py, double *valx, double *valy) { /* I'll use the shorthand rx, ry for the return values, valx & valy. * Now, we need (rx,ry) to be on the line between (x1,y1) and (x2,y2). * For that to happen, we first need the slope of the line from (x1,y1) * to (rx,ry) must match the slope of (x1,y1) to (x2,y2), i.e.: * (ry-y1) (y2-y1) * ------- = ------- * (rx-x1) (x2-x1) * If x1==x2, though, this gives divide by zero errors, so we want to * rewrite the equation by multiplying both sides by (rx-x1)*(x2-x1): * (ry-y1)(x2-x1) = (y2-y1)(rx-x1) * This is a valid requirement even when x1==x2 (when x1==x2, this latter * equation will basically just mean that rx must be equal to both x1 and * x2) * * The other requirement that we have is that the line from (rx,ry) to * (px,py) must be perpendicular to the line from (x1,y1) to (x2,y2). So * we just need to get a vector in the direction of each line, take the * dot product of the two, and ensure that the result is 0: * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0. * * This gives us two equations and two unknowns: * * (ry-y1)(x2-x1) = (y2-y1)(rx-x1) * (rx-px)*(x2-x1) + (ry-py)*(y2-y1) = 0. * * This particular pair of equations is always solvable so long as * (x1,y1) and (x2,y2) are not the same point (and note that anyone who * calls this function that way is braindead because it means that they * really didn't specify a line after all). However, the caller should * be careful to avoid making (x1,y1) and (x2,y2) too close (e.g. like * 10^{-8} apart in each coordinate), otherwise roundoff error could * cause issues. Solving these equations by hand (or using Maple(TM) or * Mathematica(TM) or whatever) results in slightly messy expressions, * but that's all the below few lines do. */ double diffx, diffy, den; diffx = x2 - x1; diffy = y2 - y1; den = diffx * diffx + diffy * diffy; *valx = (py * diffx * diffy + px * diffx * diffx + y2 * x1 * diffy - y1 * x2 * diffy) / den; *valy = (px * diffx * diffy + py * diffy * diffy + x2 * y1 * diffx - x1 * y2 * diffx) / den; } /***************************************************************************/ /* */ /* Switching gears to code for edges instead of just rectangles */ /* */ /***************************************************************************/ gboolean meta_rectangle_edge_aligns (const MetaRectangle *rect, const MetaEdge *edge) { /* The reason for the usage of <= below instead of < is because we are * interested in in-the-way-or-adject'ness. So, a left (i.e. vertical * edge) occupying y positions 0-9 (which has a y of 0 and a height of * 10) and a rectangle with top at y=10 would be considered to "align" by * this function. */ switch (edge->side_type) { case META_SIDE_LEFT: case META_SIDE_RIGHT: return BOX_TOP (*rect) <= BOX_BOTTOM (edge->rect) && BOX_TOP (edge->rect) <= BOX_BOTTOM (*rect); case META_SIDE_TOP: case META_SIDE_BOTTOM: return BOX_LEFT (*rect) <= BOX_RIGHT (edge->rect) && BOX_LEFT (edge->rect) <= BOX_RIGHT (*rect); default: g_assert_not_reached (); return FALSE; } } static GList* get_rect_minus_overlap (const GList *rect_in_list, MetaRectangle *overlap) { MetaRectangle *temp; MetaRectangle *rect = rect_in_list->data; GList *ret = NULL; if (BOX_LEFT (*rect) < BOX_LEFT (*overlap)) { temp = g_new (MetaRectangle, 1); *temp = *rect; temp->width = BOX_LEFT (*overlap) - BOX_LEFT (*rect); ret = g_list_prepend (ret, temp); } if (BOX_RIGHT (*rect) > BOX_RIGHT (*overlap)) { temp = g_new (MetaRectangle, 1); *temp = *rect; temp->x = BOX_RIGHT (*overlap); temp->width = BOX_RIGHT (*rect) - BOX_RIGHT (*overlap); ret = g_list_prepend (ret, temp); } if (BOX_TOP (*rect) < BOX_TOP (*overlap)) { temp = g_new (MetaRectangle, 1); temp->x = overlap->x; temp->width = overlap->width; temp->y = BOX_TOP (*rect); temp->height = BOX_TOP (*overlap) - BOX_TOP (*rect); ret = g_list_prepend (ret, temp); } if (BOX_BOTTOM (*rect) > BOX_BOTTOM (*overlap)) { temp = g_new (MetaRectangle, 1); temp->x = overlap->x; temp->width = overlap->width; temp->y = BOX_BOTTOM (*overlap); temp->height = BOX_BOTTOM (*rect) - BOX_BOTTOM (*overlap); ret = g_list_prepend (ret, temp); } return ret; } static GList* replace_rect_with_list (GList *old_element, GList *new_list) { GList *ret; g_assert (old_element != NULL); if (!new_list) { /* If there is no new list, just remove the old_element */ ret = g_list_remove_link (old_element, old_element); } else { /* Fix up the prev and next pointers everywhere */ ret = new_list; if (old_element->prev) { old_element->prev->next = new_list; new_list->prev = old_element->prev; } if (old_element->next) { GList *tmp = g_list_last (new_list); old_element->next->prev = tmp; tmp->next = old_element->next; } } /* Free the old_element and return the appropriate "next" point */ g_free (old_element->data); g_list_free_1 (old_element); return ret; } /* Make a copy of the strut list, make sure that copy only contains parts * of the old_struts that intersect with the region rect, and then do some * magic to make all the new struts disjoint (okay, we we break up struts * that aren't disjoint in a way that the overlapping part is only included * once, so it's not really magic...). */ static GList* get_disjoint_strut_rect_list_in_region (const GSList *old_struts, const MetaRectangle *region) { GList *strut_rects; GList *tmp; /* First, copy the list */ strut_rects = NULL; while (old_struts) { MetaRectangle *cur = &((MetaStrut*)old_struts->data)->rect; MetaRectangle *copy = g_new (MetaRectangle, 1); *copy = *cur; if (meta_rectangle_intersect (copy, region, copy)) strut_rects = g_list_prepend (strut_rects, copy); else g_free (copy); old_struts = old_struts->next; } /* Now, loop over the list and check for intersections, fixing things up * where they do intersect. */ tmp = strut_rects; while (tmp) { GList *compare; MetaRectangle *cur = tmp->data; compare = tmp->next; while (compare) { MetaRectangle *comp = compare->data; MetaRectangle overlap; if (meta_rectangle_intersect (cur, comp, &overlap)) { /* Get a list of rectangles for each strut that don't overlap * the intersection region. */ GList *cur_leftover = get_rect_minus_overlap (tmp, &overlap); GList *comp_leftover = get_rect_minus_overlap (compare, &overlap); /* Add the intersection region to cur_leftover */ MetaRectangle *overlap_allocated = g_new (MetaRectangle, 1); *overlap_allocated = overlap; cur_leftover = g_list_prepend (cur_leftover, overlap_allocated); /* Fix up tmp, compare, and cur -- maybe struts too */ if (strut_rects == tmp) { strut_rects = replace_rect_with_list (tmp, cur_leftover); tmp = strut_rects; } else tmp = replace_rect_with_list (tmp, cur_leftover); compare = replace_rect_with_list (compare, comp_leftover); if (compare == NULL) break; cur = tmp->data; } compare = compare->next; } tmp = tmp->next; } return strut_rects; } gint meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b) { const MetaEdge *a_edge_rect = (gconstpointer) a; const MetaEdge *b_edge_rect = (gconstpointer) b; int a_compare, b_compare; /* Edges must be both vertical or both horizontal, or it doesn't make * sense to compare them. */ g_assert ((a_edge_rect->rect.width == 0 && b_edge_rect->rect.width == 0) || (a_edge_rect->rect.height == 0 && b_edge_rect->rect.height == 0)); a_compare = b_compare = 0; /* gcc-3.4.2 sucks at figuring initialized'ness */ if (a_edge_rect->side_type == META_SIDE_LEFT || a_edge_rect->side_type == META_SIDE_RIGHT) { a_compare = a_edge_rect->rect.x; b_compare = b_edge_rect->rect.x; if (a_compare == b_compare) { a_compare = a_edge_rect->rect.y; b_compare = b_edge_rect->rect.y; } } else if (a_edge_rect->side_type == META_SIDE_TOP || a_edge_rect->side_type == META_SIDE_BOTTOM) { a_compare = a_edge_rect->rect.y; b_compare = b_edge_rect->rect.y; if (a_compare == b_compare) { a_compare = a_edge_rect->rect.x; b_compare = b_edge_rect->rect.x; } } else g_assert ("Some idiot wanted to sort sides of different types.\n"); return a_compare - b_compare; /* positive value denotes a > b ... */ } /* To make things easily testable, provide a nice way of sorting edges */ gint meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b) { const MetaEdge *a_edge_rect = (gconstpointer) a; const MetaEdge *b_edge_rect = (gconstpointer) b; int a_compare, b_compare; a_compare = a_edge_rect->side_type; b_compare = b_edge_rect->side_type; if (a_compare == b_compare) return meta_rectangle_edge_cmp_ignore_type (a, b); return a_compare - b_compare; /* positive value denotes a > b ... */ } /* Determine whether two given edges overlap */ static gboolean edges_overlap (const MetaEdge *edge1, const MetaEdge *edge2) { if (edge1->rect.width == 0 && edge2->rect.width == 0) { return meta_rectangle_vert_overlap (&edge1->rect, &edge2->rect) && edge1->rect.x == edge2->rect.x; } else if (edge1->rect.height == 0 && edge2->rect.height == 0) { return meta_rectangle_horiz_overlap (&edge1->rect, &edge2->rect) && edge1->rect.y == edge2->rect.y; } else { return FALSE; } } static gboolean rectangle_and_edge_intersection (const MetaRectangle *rect, const MetaEdge *edge, MetaEdge *overlap, int *handle_type) { const MetaRectangle *rect2 = &edge->rect; MetaRectangle *result = &overlap->rect; gboolean intersect = TRUE; /* We don't know how to set these, so set them to invalid values */ overlap->edge_type = -1; overlap->side_type = -1; /* Figure out what the intersection is */ result->x = MAX (rect->x, rect2->x); result->y = MAX (rect->y, rect2->y); result->width = MIN (BOX_RIGHT (*rect), BOX_RIGHT (*rect2)) - result->x; result->height = MIN (BOX_BOTTOM (*rect), BOX_BOTTOM (*rect2)) - result->y; /* Find out if the intersection is empty; have to do it this way since * edges have a thickness of 0 */ if ((result->width < 0 || result->height < 0) || (result->width == 0 && result->height == 0)) { result->width = 0; result->height = 0; intersect = FALSE; } else { /* Need to figure out the handle_type, a somewhat weird quantity: * 0 - overlap is in middle of rect * -1 - overlap is at the side of rect, and is on the opposite side * of rect than the edge->side_type side * 1 - overlap is at the side of rect, and the side of rect it is * on is the edge->side_type side */ switch (edge->side_type) { case META_SIDE_LEFT: if (result->x == rect->x) *handle_type = 1; else if (result->x == BOX_RIGHT (*rect)) *handle_type = -1; else *handle_type = 0; break; case META_SIDE_RIGHT: if (result->x == rect->x) *handle_type = -1; else if (result->x == BOX_RIGHT (*rect)) *handle_type = 1; else *handle_type = 0; break; case META_SIDE_TOP: if (result->y == rect->y) *handle_type = 1; else if (result->y == BOX_BOTTOM (*rect)) *handle_type = -1; else *handle_type = 0; break; case META_SIDE_BOTTOM: if (result->y == rect->y) *handle_type = -1; else if (result->y == BOX_BOTTOM (*rect)) *handle_type = 1; else *handle_type = 0; break; default: g_assert_not_reached (); } } return intersect; } /* Add all edges of the given rect to cur_edges and return the result. If * rect_is_internal is false, the side types are switched (LEFT<->RIGHT and * TOP<->BOTTOM). */ static GList* add_edges (GList *cur_edges, const MetaRectangle *rect, gboolean rect_is_internal) { MetaEdge *temp_edge; int i; for (i=0; i<4; i++) { temp_edge = g_new (MetaEdge, 1); temp_edge->rect = *rect; switch (i) { case 0: temp_edge->side_type = rect_is_internal ? META_SIDE_LEFT : META_SIDE_RIGHT; temp_edge->rect.width = 0; break; case 1: temp_edge->side_type = rect_is_internal ? META_SIDE_RIGHT : META_SIDE_LEFT; temp_edge->rect.x += temp_edge->rect.width; temp_edge->rect.width = 0; break; case 2: temp_edge->side_type = rect_is_internal ? META_SIDE_TOP : META_SIDE_BOTTOM; temp_edge->rect.height = 0; break; case 3: temp_edge->side_type = rect_is_internal ? META_SIDE_BOTTOM : META_SIDE_TOP; temp_edge->rect.y += temp_edge->rect.height; temp_edge->rect.height = 0; break; } temp_edge->edge_type = META_EDGE_SCREEN; cur_edges = g_list_prepend (cur_edges, temp_edge); } return cur_edges; } /* Remove any part of old_edge that intersects remove and add any resulting * edges to cur_list. Return cur_list when finished. */ static GList* split_edge (GList *cur_list, const MetaEdge *old_edge, const MetaEdge *remove) { MetaEdge *temp_edge; switch (old_edge->side_type) { case META_SIDE_LEFT: case META_SIDE_RIGHT: g_assert (meta_rectangle_vert_overlap (&old_edge->rect, &remove->rect)); if (BOX_TOP (old_edge->rect) < BOX_TOP (remove->rect)) { temp_edge = g_new (MetaEdge, 1); *temp_edge = *old_edge; temp_edge->rect.height = BOX_TOP (remove->rect) - BOX_TOP (old_edge->rect); cur_list = g_list_prepend (cur_list, temp_edge); } if (BOX_BOTTOM (old_edge->rect) > BOX_BOTTOM (remove->rect)) { temp_edge = g_new (MetaEdge, 1); *temp_edge = *old_edge; temp_edge->rect.y = BOX_BOTTOM (remove->rect); temp_edge->rect.height = BOX_BOTTOM (old_edge->rect) - BOX_BOTTOM (remove->rect); cur_list = g_list_prepend (cur_list, temp_edge); } break; case META_SIDE_TOP: case META_SIDE_BOTTOM: g_assert (meta_rectangle_horiz_overlap (&old_edge->rect, &remove->rect)); if (BOX_LEFT (old_edge->rect) < BOX_LEFT (remove->rect)) { temp_edge = g_new (MetaEdge, 1); *temp_edge = *old_edge; temp_edge->rect.width = BOX_LEFT (remove->rect) - BOX_LEFT (old_edge->rect); cur_list = g_list_prepend (cur_list, temp_edge); } if (BOX_RIGHT (old_edge->rect) > BOX_RIGHT (remove->rect)) { temp_edge = g_new (MetaEdge, 1); *temp_edge = *old_edge; temp_edge->rect.x = BOX_RIGHT (remove->rect); temp_edge->rect.width = BOX_RIGHT (old_edge->rect) - BOX_RIGHT (remove->rect); cur_list = g_list_prepend (cur_list, temp_edge); } break; default: g_assert_not_reached (); } return cur_list; } /* Split up edge and remove preliminary edges from strut_edges depending on * if and how rect and edge intersect. */ static void fix_up_edges (MetaRectangle *rect, MetaEdge *edge, GList **strut_edges, GList **edge_splits, gboolean *edge_needs_removal) { MetaEdge overlap; int handle_type; if (!rectangle_and_edge_intersection (rect, edge, &overlap, &handle_type)) return; if (handle_type == 0 || handle_type == 1) { /* Put the result of removing overlap from edge into edge_splits */ *edge_splits = split_edge (*edge_splits, edge, &overlap); *edge_needs_removal = TRUE; } if (handle_type == -1 || handle_type == 1) { /* Remove the overlap from strut_edges */ /* First, loop over the edges of the strut */ GList *tmp = *strut_edges; while (tmp) { MetaEdge *cur = tmp->data; /* If this is the edge that overlaps, then we need to split it */ if (edges_overlap (cur, &overlap)) { GList *delete_me = tmp; /* Split this edge into some new ones */ *strut_edges = split_edge (*strut_edges, cur, &overlap); /* Delete the old one */ tmp = tmp->next; g_free (cur); *strut_edges = g_list_delete_link (*strut_edges, delete_me); } else tmp = tmp->next; } } } /** * meta_rectangle_remove_intersections_with_boxes_from_edges: (skip) * * This function removes intersections of edges with the rectangles from the * list of edges. */ GList* meta_rectangle_remove_intersections_with_boxes_from_edges ( GList *edges, const GSList *rectangles) { const GSList *rect_iter; const int opposing = 1; /* Now remove all intersections of rectangles with the edge list */ rect_iter = rectangles; while (rect_iter) { MetaRectangle *rect = rect_iter->data; GList *edge_iter = edges; while (edge_iter) { MetaEdge *edge = edge_iter->data; MetaEdge overlap; int handle; gboolean edge_iter_advanced = FALSE; /* If this edge overlaps with this rect... */ if (rectangle_and_edge_intersection (rect, edge, &overlap, &handle)) { /* "Intersections" where the edges touch but are opposite * sides (e.g. a left edge against the right edge) should not * be split. Note that the comments in * rectangle_and_edge_intersection() say that opposing edges * occur when handle is -1, BUT you need to remember that we * treat the left side of a window as a right edge because * it's what the right side of the window being moved should * be-resisted-by/snap-to. So opposing is really 1. Anyway, * we just keep track of it in the opposing constant set up * above and if handle isn't equal to that, then we know the * edge should be split. */ if (handle != opposing) { /* Keep track of this edge so we can delete it below */ GList *delete_me = edge_iter; edge_iter = edge_iter->next; edge_iter_advanced = TRUE; /* Split the edge and add the result to beginning of edges */ edges = split_edge (edges, edge, &overlap); /* Now free the edge... */ g_free (edge); edges = g_list_delete_link (edges, delete_me); } } if (!edge_iter_advanced) edge_iter = edge_iter->next; } rect_iter = rect_iter->next; } return edges; } /** * meta_rectangle_find_onscreen_edges: (skip) * * This function is trying to find all the edges of an onscreen region. */ GList* meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect, const GSList *all_struts) { GList *ret; GList *fixed_strut_rects; GList *edge_iter; const GList *strut_rect_iter; /* The algorithm is basically as follows: * Make sure the struts are disjoint * Initialize the edge_set to the edges of basic_rect * Foreach strut: * Put together a preliminary new edge from the edges of the strut * Foreach edge in edge_set: * - Split the edge if it is partially contained inside the strut * - If the edge matches an edge of the strut (i.e. a strut just * against the edge of the screen or a not-next-to-edge-of-screen * strut adjacent to another), then both the edge from the * edge_set and the preliminary edge for the strut will need to * be split * Add any remaining "preliminary" strut edges to the edge_set */ /* Make sure the struts are disjoint */ fixed_strut_rects = get_disjoint_strut_rect_list_in_region (all_struts, basic_rect); /* Start off the list with the edges of basic_rect */ ret = add_edges (NULL, basic_rect, TRUE); strut_rect_iter = fixed_strut_rects; while (strut_rect_iter) { MetaRectangle *strut_rect = (MetaRectangle*) strut_rect_iter->data; /* Get the new possible edges we may need to add from the strut */ GList *new_strut_edges = add_edges (NULL, strut_rect, FALSE); edge_iter = ret; while (edge_iter) { MetaEdge *cur_edge = edge_iter->data; GList *splits_of_cur_edge = NULL; gboolean edge_needs_removal = FALSE; fix_up_edges (strut_rect, cur_edge, &new_strut_edges, &splits_of_cur_edge, &edge_needs_removal); if (edge_needs_removal) { /* Delete the old edge */ GList *delete_me = edge_iter; edge_iter = edge_iter->next; g_free (cur_edge); ret = g_list_delete_link (ret, delete_me); /* Add the new split parts of the edge */ ret = g_list_concat (splits_of_cur_edge, ret); } else { edge_iter = edge_iter->next; } /* edge_iter was already advanced above */ } ret = g_list_concat (new_strut_edges, ret); strut_rect_iter = strut_rect_iter->next; } /* Sort the list */ ret = g_list_sort (ret, meta_rectangle_edge_cmp); /* Free the fixed struts list */ meta_rectangle_free_list_and_elements (fixed_strut_rects); return ret; } /** * meta_rectangle_find_nonintersected_monitor_edges: (skip) * */ GList* meta_rectangle_find_nonintersected_monitor_edges ( const GList *monitor_rects, const GSList *all_struts) { /* This function cannot easily be merged with * meta_rectangle_find_onscreen_edges() because real screen edges * and strut edges both are of the type "there ain't anything * immediately on the other side"; monitor edges are different. */ GList *ret; const GList *cur; GSList *temp_rects; /* Initialize the return list to be empty */ ret = NULL; /* start of ret with all the edges of monitors that are adjacent to * another monitor. */ cur = monitor_rects; while (cur) { MetaRectangle *cur_rect = cur->data; const GList *compare = monitor_rects; while (compare) { MetaRectangle *compare_rect = compare->data; /* Check if cur might be horizontally adjacent to compare */ if (meta_rectangle_vert_overlap(cur_rect, compare_rect)) { MetaSide side_type; int y = MAX (cur_rect->y, compare_rect->y); int height = MIN (BOX_BOTTOM (*cur_rect) - y, BOX_BOTTOM (*compare_rect) - y); int width = 0; int x; if (BOX_LEFT (*cur_rect) == BOX_RIGHT (*compare_rect)) { /* compare_rect is to the left of cur_rect */ x = BOX_LEFT (*cur_rect); side_type = META_SIDE_LEFT; } else if (BOX_RIGHT (*cur_rect) == BOX_LEFT (*compare_rect)) { /* compare_rect is to the right of cur_rect */ x = BOX_RIGHT (*cur_rect); side_type = META_SIDE_RIGHT; } else /* These rectangles aren't adjacent after all */ x = INT_MIN; /* If the rectangles really are adjacent */ if (x != INT_MIN) { /* We need a left edge for the monitor on the right, and * a right edge for the monitor on the left. Just fill * up the edges and stick 'em on the list. */ MetaEdge *new_edge = g_new (MetaEdge, 1); new_edge->rect = meta_rect (x, y, width, height); new_edge->side_type = side_type; new_edge->edge_type = META_EDGE_MONITOR; ret = g_list_prepend (ret, new_edge); } } /* Check if cur might be vertically adjacent to compare */ if (meta_rectangle_horiz_overlap(cur_rect, compare_rect)) { MetaSide side_type; int x = MAX (cur_rect->x, compare_rect->x); int width = MIN (BOX_RIGHT (*cur_rect) - x, BOX_RIGHT (*compare_rect) - x); int height = 0; int y; if (BOX_TOP (*cur_rect) == BOX_BOTTOM (*compare_rect)) { /* compare_rect is to the top of cur_rect */ y = BOX_TOP (*cur_rect); side_type = META_SIDE_TOP; } else if (BOX_BOTTOM (*cur_rect) == BOX_TOP (*compare_rect)) { /* compare_rect is to the bottom of cur_rect */ y = BOX_BOTTOM (*cur_rect); side_type = META_SIDE_BOTTOM; } else /* These rectangles aren't adjacent after all */ y = INT_MIN; /* If the rectangles really are adjacent */ if (y != INT_MIN) { /* We need a top edge for the monitor on the bottom, and * a bottom edge for the monitor on the top. Just fill * up the edges and stick 'em on the list. */ MetaEdge *new_edge = g_new (MetaEdge, 1); new_edge->rect = meta_rect (x, y, width, height); new_edge->side_type = side_type; new_edge->edge_type = META_EDGE_MONITOR; ret = g_list_prepend (ret, new_edge); } } compare = compare->next; } cur = cur->next; } temp_rects = NULL; for (; all_struts; all_struts = all_struts->next) temp_rects = g_slist_prepend (temp_rects, &((MetaStrut*)all_struts->data)->rect); ret = meta_rectangle_remove_intersections_with_boxes_from_edges (ret, temp_rects); g_slist_free (temp_rects); /* Sort the list */ ret = g_list_sort (ret, meta_rectangle_edge_cmp); return ret; } gboolean meta_rectangle_is_adjacent_to (MetaRectangle *rect, MetaRectangle *other) { int rect_x1 = rect->x; int rect_y1 = rect->y; int rect_x2 = rect->x + rect->width; int rect_y2 = rect->y + rect->height; int other_x1 = other->x; int other_y1 = other->y; int other_x2 = other->x + other->width; int other_y2 = other->y + other->height; if ((rect_x1 == other_x2 || rect_x2 == other_x1) && !(rect_y2 <= other_y1 || rect_y1 >= other_y2)) return TRUE; else if ((rect_y1 == other_y2 || rect_y2 == other_y1) && !(rect_x2 <= other_x1 || rect_x1 >= other_x2)) return TRUE; else return FALSE; } void meta_rectangle_scale_double (const MetaRectangle *rect, double scale, MetaRoundingStrategy rounding_strategy, MetaRectangle *dest) { graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y, rect->width, rect->height); graphene_rect_scale (&tmp, scale, scale, &tmp); meta_rectangle_from_graphene_rect (&tmp, rounding_strategy, dest); } void meta_rectangle_transform (const MetaRectangle *rect, MetaMonitorTransform transform, int width, int height, MetaRectangle *dest) { switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: *dest = *rect; break; case META_MONITOR_TRANSFORM_90: *dest = (MetaRectangle) { .x = width - (rect->y + rect->height), .y = rect->x, .width = rect->height, .height = rect->width, }; break; case META_MONITOR_TRANSFORM_180: *dest = (MetaRectangle) { .x = width - (rect->x + rect->width), .y = height - (rect->y + rect->height), .width = rect->width, .height = rect->height, }; break; case META_MONITOR_TRANSFORM_270: *dest = (MetaRectangle) { .x = rect->y, .y = height - (rect->x + rect->width), .width = rect->height, .height = rect->width, }; break; case META_MONITOR_TRANSFORM_FLIPPED: *dest = (MetaRectangle) { .x = width - (rect->x + rect->width), .y = rect->y, .width = rect->width, .height = rect->height, }; break; case META_MONITOR_TRANSFORM_FLIPPED_90: *dest = (MetaRectangle) { .x = width - (rect->y + rect->height), .y = height - (rect->x + rect->width), .width = rect->height, .height = rect->width, }; break; case META_MONITOR_TRANSFORM_FLIPPED_180: *dest = (MetaRectangle) { .x = rect->x, .y = height - (rect->y + rect->height), .width = rect->width, .height = rect->height, }; break; case META_MONITOR_TRANSFORM_FLIPPED_270: *dest = (MetaRectangle) { .x = rect->y, .y = rect->x, .width = rect->height, .height = rect->width, }; break; } } void meta_rectangle_from_graphene_rect (const graphene_rect_t *rect, MetaRoundingStrategy rounding_strategy, MetaRectangle *dest) { switch (rounding_strategy) { case META_ROUNDING_STRATEGY_SHRINK: { *dest = (MetaRectangle) { .x = ceilf (rect->origin.x), .y = ceilf (rect->origin.y), .width = floorf (rect->size.width), .height = floorf (rect->size.height), }; } break; case META_ROUNDING_STRATEGY_GROW: { graphene_rect_t clamped = *rect; graphene_rect_round_extents (&clamped, &clamped); *dest = (MetaRectangle) { .x = clamped.origin.x, .y = clamped.origin.y, .width = clamped.size.width, .height = clamped.size.height, }; } break; case META_ROUNDING_STRATEGY_ROUND: { *dest = (MetaRectangle) { .x = roundf (rect->origin.x), .y = roundf (rect->origin.y), .width = roundf (rect->size.width), .height = roundf (rect->size.height), }; } } } void meta_rectangle_crop_and_scale (const MetaRectangle *rect, graphene_rect_t *src_rect, int dst_width, int dst_height, MetaRectangle *dest) { graphene_rect_t tmp = GRAPHENE_RECT_INIT (rect->x, rect->y, rect->width, rect->height); graphene_rect_scale (&tmp, src_rect->size.width / dst_width, src_rect->size.height / dst_height, &tmp); graphene_rect_offset (&tmp, src_rect->origin.x, src_rect->origin.y); meta_rectangle_from_graphene_rect (&tmp, META_ROUNDING_STRATEGY_GROW, dest); } muffin-6.4.1/src/core/edge-resistance.h0000664000175000017500000000360614723361714016701 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Edge resistance for move/resize operations */ /* * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_EDGE_RESISTANCE_H #define META_EDGE_RESISTANCE_H #include "core/window-private.h" void meta_window_edge_resistance_for_move (MetaWindow *window, int *new_x, int *new_y, GSourceFunc timeout_func, gboolean snap, gboolean is_keyboard_op); void meta_window_edge_resistance_for_resize (MetaWindow *window, int *new_width, int *new_height, MetaGravity gravity, GSourceFunc timeout_func, gboolean snap, gboolean is_keyboard_op); #endif /* META_EDGE_RESISTANCE_H */ muffin-6.4.1/src/core/window-private.h0000664000175000017500000010213414723361714016612 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file window-private.h Windows which Mutter manages * * Managing X windows. * This file contains methods on this class which are available to * routines in core but not outside it. (See window.h for the routines * which the rest of the world is allowed to use.) */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_PRIVATE_H #define META_WINDOW_PRIVATE_H #include #include #include #include "backends/meta-logical-monitor.h" #include "clutter/clutter.h" #include "core/stack.h" #include "meta/compositor.h" #include "meta/meta-close-dialog.h" #include "meta/util.h" #include "meta/window.h" #include "wayland/meta-wayland-types.h" #include "x11/group-private.h" typedef struct _MetaWindowQueue MetaWindowQueue; typedef enum { META_CLIENT_TYPE_UNKNOWN = 0, META_CLIENT_TYPE_APPLICATION = 1, META_CLIENT_TYPE_PAGER = 2, META_CLIENT_TYPE_MAX_RECOGNIZED = 2 } MetaClientType; typedef enum { META_QUEUE_CALC_SHOWING = 1 << 0, META_QUEUE_MOVE_RESIZE = 1 << 1, META_QUEUE_UPDATE_ICON = 1 << 2, } MetaQueueType; #define NUMBER_OF_QUEUES 3 typedef enum { _NET_WM_BYPASS_COMPOSITOR_HINT_AUTO = 0, _NET_WM_BYPASS_COMPOSITOR_HINT_ON = 1, _NET_WM_BYPASS_COMPOSITOR_HINT_OFF = 2, } MetaBypassCompositorHintValue; typedef enum { META_MOVE_RESIZE_CONFIGURE_REQUEST = 1 << 0, META_MOVE_RESIZE_USER_ACTION = 1 << 1, META_MOVE_RESIZE_MOVE_ACTION = 1 << 2, META_MOVE_RESIZE_RESIZE_ACTION = 1 << 3, META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE = 1 << 4, META_MOVE_RESIZE_STATE_CHANGED = 1 << 5, META_MOVE_RESIZE_UNMAXIMIZE = 1 << 6, META_MOVE_RESIZE_UNFULLSCREEN = 1 << 7, META_MOVE_RESIZE_FORCE_MOVE = 1 << 8, META_MOVE_RESIZE_WAYLAND_STATE_CHANGED = 1 << 9, META_MOVE_RESIZE_FORCE_UPDATE_MONITOR = 1 << 10, META_MOVE_RESIZE_PLACEMENT_CHANGED = 1 << 11, } MetaMoveResizeFlags; typedef enum { META_MOVE_RESIZE_RESULT_MOVED = 1 << 0, META_MOVE_RESIZE_RESULT_RESIZED = 1 << 1, META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED = 1 << 2, META_MOVE_RESIZE_RESULT_STATE_CHANGED = 1 << 3, } MetaMoveResizeResultFlags; typedef enum { META_PLACEMENT_GRAVITY_NONE = 0, META_PLACEMENT_GRAVITY_TOP = 1 << 0, META_PLACEMENT_GRAVITY_BOTTOM = 1 << 1, META_PLACEMENT_GRAVITY_LEFT = 1 << 2, META_PLACEMENT_GRAVITY_RIGHT = 1 << 3, } MetaPlacementGravity; typedef enum { META_PLACEMENT_ANCHOR_NONE = 0, META_PLACEMENT_ANCHOR_TOP = 1 << 0, META_PLACEMENT_ANCHOR_BOTTOM = 1 << 1, META_PLACEMENT_ANCHOR_LEFT = 1 << 2, META_PLACEMENT_ANCHOR_RIGHT = 1 << 3, } MetaPlacementAnchor; typedef enum { META_PLACEMENT_CONSTRAINT_ADJUSTMENT_NONE = 0, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_X = 1 << 0, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_SLIDE_Y = 1 << 1, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_X = 1 << 2, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_FLIP_Y = 1 << 3, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_X = 1 << 4, META_PLACEMENT_CONSTRAINT_ADJUSTMENT_RESIZE_Y = 1 << 5, } MetaPlacementConstraintAdjustment; typedef enum _MetaWindowUpdateMonitorFlags { META_WINDOW_UPDATE_MONITOR_FLAGS_NONE = 0, META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP = 1 << 0, META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE = 1 << 1, } MetaWindowUpdateMonitorFlags; typedef struct _MetaPlacementRule { MetaRectangle anchor_rect; MetaPlacementGravity gravity; MetaPlacementAnchor anchor; MetaPlacementConstraintAdjustment constraint_adjustment; int offset_x; int offset_y; int width; int height; gboolean is_reactive; MetaRectangle parent_rect; } MetaPlacementRule; typedef enum _MetaPlacementState { META_PLACEMENT_STATE_UNCONSTRAINED, META_PLACEMENT_STATE_CONSTRAINED_PENDING, META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED, META_PLACEMENT_STATE_CONSTRAINED_FINISHED, META_PLACEMENT_STATE_INVALIDATED, } MetaPlacementState; typedef enum { META_EDGE_CONSTRAINT_NONE = 0, META_EDGE_CONSTRAINT_WINDOW = 1, META_EDGE_CONSTRAINT_MONITOR = 2, } MetaEdgeConstraint; struct _MetaWindow { GObject parent_instance; MetaDisplay *display; uint64_t id; guint64 stamp; MetaLogicalMonitor *monitor; MetaWorkspace *workspace; MetaWindowClientType client_type; MetaWaylandSurface *surface; Window xwindow; /* may be NULL! not all windows get decorated */ MetaFrame *frame; int depth; Visual *xvisual; char *desc; /* used in debug spew */ char *title; cairo_surface_t *icon; cairo_surface_t *mini_icon; /* XAppGtkWindow */ char *theme_icon_name; guint progress; guint progress_pulse : 1; /* /XappGtkWindow */ MetaWindowType type; /* NOTE these five are not in UTF-8, we just treat them as random * binary data */ char *res_class; char *res_name; char *role; char *sm_client_id; char *wm_client_machine; char *startup_id; char *mutter_hints; char *sandboxed_app_id; char *gtk_theme_variant; char *gtk_application_id; char *gtk_unique_bus_name; char *gtk_application_object_path; char *gtk_window_object_path; char *gtk_app_menu_object_path; char *gtk_menubar_object_path; int net_wm_pid; Window xtransient_for; Window xgroup_leader; Window xclient_leader; MetaWindow *transient_for; /* Initial workspace property */ int initial_workspace; /* Initial timestamp property */ guint32 initial_timestamp; /* Whether this is an override redirect window or not */ guint override_redirect : 1; /* Whether we're maximized */ guint maximized_horizontally : 1; guint maximized_vertically : 1; /* Whether we have to maximize/minimize after placement */ guint maximize_horizontally_after_placement : 1; guint maximize_vertically_after_placement : 1; guint minimize_after_placement : 1; guint move_after_placement : 1; /* The current tile mode */ MetaTileMode tile_mode; /* The last "full" maximized/unmaximized state. We need to keep track of * that to toggle between normal/tiled or maximized/tiled states. */ MetaTileMode saved_tile_mode; int tile_monitor_number; struct { MetaEdgeConstraint top; MetaEdgeConstraint right; MetaEdgeConstraint bottom; MetaEdgeConstraint left; } edge_constraints; double tile_hfraction; double tile_vfraction; uint64_t preferred_output_winsys_id; /* Whether we're shaded */ guint shaded : 1; /* Whether we're fullscreen */ guint fullscreen : 1; /* Whether the window is marked as urgent */ guint urgent : 1; /* Area to cover when in fullscreen mode. If _NET_WM_FULLSCREEN_MONITORS has * been overridden (via a client message), the window will cover the union of * these monitors. If not, this is the single monitor which the window's * origin is on. */ struct { MetaLogicalMonitor *top; MetaLogicalMonitor *bottom; MetaLogicalMonitor *left; MetaLogicalMonitor *right; } fullscreen_monitors; /* Whether we're trying to constrain the window to be fully onscreen */ guint require_fully_onscreen : 1; /* Whether we're trying to constrain the window to be on a single monitor */ guint require_on_single_monitor : 1; /* Whether we're trying to constrain the window's titlebar to be onscreen */ guint require_titlebar_visible : 1; /* Whether we're sticky in the multi-workspace sense * (vs. the not-scroll-with-viewport sense, we don't * have no stupid viewports) */ guint on_all_workspaces : 1; /* This is true if the client requested sticky, and implies on_all_workspaces == TRUE, * however on_all_workspaces can be set TRUE for other internal reasons too, such as * being override_redirect or being on the non-primary monitor. */ guint on_all_workspaces_requested : 1; /* Minimize is the state controlled by the minimize button */ guint minimized : 1; guint tab_unminimized : 1; /* Whether the window is mapped; actual server-side state * see also unmaps_pending */ guint mapped : 1; /* Whether window has been hidden from view by lowering it to the bottom * of window stack. */ guint hidden : 1; /* Whether the compositor thinks the window is visible. * This should match up with calls to meta_compositor_show_window / * meta_compositor_hide_window. */ guint visible_to_compositor : 1; /* Whether the compositor knows about the window. * This should match up with calls to meta_compositor_add_window / * meta_compositor_remove_window. */ guint known_to_compositor : 1; /* When we next show or hide the window, what effect we should * tell the compositor to perform. */ guint pending_compositor_effect : 4; /* MetaCompEffect */ /* Iconic is the state in WM_STATE; happens for workspaces/shading * in addition to minimize */ guint iconic : 1; /* initially_iconic is the WM_HINTS setting when we first manage * the window. It's taken to mean initially minimized. */ guint initially_iconic : 1; /* whether an initial workspace was explicitly set */ guint initial_workspace_set : 1; /* whether an initial timestamp was explicitly set */ guint initial_timestamp_set : 1; /* whether net_wm_user_time has been set yet */ guint net_wm_user_time_set : 1; /* whether net_wm_icon_geometry has been set */ guint icon_geometry_set : 1; /* Globally active / No input */ guint input : 1; /* MWM hints about features of window */ guint mwm_decorated : 1; guint mwm_border_only : 1; guint mwm_has_close_func : 1; guint mwm_has_minimize_func : 1; guint mwm_has_maximize_func : 1; guint mwm_has_move_func : 1; guint mwm_has_resize_func : 1; /* Computed features of window */ guint decorated : 1; guint border_only : 1; guint always_sticky : 1; guint has_close_func : 1; guint has_minimize_func : 1; guint has_maximize_func : 1; guint has_shade_func : 1; guint has_move_func : 1; guint has_resize_func : 1; guint has_fullscreen_func : 1; /* Computed whether to skip taskbar or not */ guint skip_taskbar : 1; guint skip_pager : 1; /* TRUE if client set these */ guint wm_state_above : 1; guint wm_state_below : 1; /* EWHH demands attention flag */ guint wm_state_demands_attention : 1; /* TRUE iff window == window->display->focus_window */ guint has_focus : 1; /* Have we placed this window? */ guint placed : 1; /* Is this not a transient of the focus window which is being denied focus? */ guint denied_focus_and_not_transient : 1; /* Has this window not ever been shown yet? */ guint showing_for_first_time : 1; /* Are we in meta_window_unmanage()? */ guint unmanaging : 1; /* Are we in meta_window_new()? */ guint constructing : 1; /* Are we in the various queues? (Bitfield: see META_WINDOW_IS_IN_QUEUE) */ guint is_in_queues : NUMBER_OF_QUEUES; /* Used by keybindings.c */ guint keys_grabbed : 1; /* normal keybindings grabbed */ guint grab_on_frame : 1; /* grabs are on the frame */ guint all_keys_grabbed : 1; /* AnyKey grabbed */ /* Set if the reason for unmanaging the window is that * it was withdrawn */ guint withdrawn : 1; /* TRUE if constrain_position should calc placement. * only relevant if !window->placed */ guint calc_placement : 1; /* if TRUE, window was maximized at start of current grab op */ guint shaken_loose : 1; /* if TRUE we have a grab on the focus click buttons */ guint have_focus_click_grab : 1; /* if TRUE, application is buggy and SYNC resizing is turned off */ guint disable_sync : 1; /* if TRUE, window is attached to its parent */ guint attached : 1; /* whether or not the window is from a program running on another machine */ guint is_remote : 1; /* whether focus should be restored on map */ guint restore_focus_on_map : 1; /* if non-NULL, the bounds of the window frame */ cairo_region_t *frame_bounds; /* if non-NULL, the bounding shape region of the window. Relative to * the server-side client window. */ cairo_region_t *shape_region; /* if non-NULL, the opaque region _NET_WM_OPAQUE_REGION */ cairo_region_t *opaque_region; /* the input shape region for picking */ cairo_region_t *input_region; /* _NET_WM_WINDOW_OPACITY rescaled to 0xFF */ guint8 opacity; /* if TRUE, the we have the new form of sync request counter which * also handles application frames */ guint extended_sync_request_counter : 1; /* Note: can be NULL */ GSList *struts; /* XSync update counter */ XSyncCounter sync_request_counter; gint64 sync_request_serial; gint64 sync_request_wait_serial; guint sync_request_timeout_id; /* alarm monitoring client's _NET_WM_SYNC_REQUEST_COUNTER */ XSyncAlarm sync_request_alarm; /* Number of UnmapNotify that are caused by us, if * we get UnmapNotify with none pending then the client * is withdrawing the window. */ int unmaps_pending; /* Number of XReparentWindow requests that we have queued. */ int reparents_pending; /* See docs for meta_window_get_stable_sequence() */ guint32 stable_sequence; /* set to the most recent user-interaction event timestamp that we know about for this window */ guint32 net_wm_user_time; /* window that gets updated net_wm_user_time values */ Window user_time_window; gboolean has_custom_frame_extents; GtkBorder custom_frame_extents; /* The rectangles here are in "frame rect" coordinates. See the * comment at the top of meta_window_move_resize_internal() for more * information. */ /* The current window geometry of the window. */ MetaRectangle rect; /* The geometry to restore when we unmaximize. */ MetaRectangle saved_rect; /* This is the geometry the window will have if no constraints have * applied. We use this whenever we are moving implicitly (for example, * if we move to avoid a panel, we can snap back to this position if * the panel moves again). */ MetaRectangle unconstrained_rect; /* The rectangle of the "server-side" geometry of the buffer, * in root coordinates. * * For X11 windows, this matches XGetGeometry of the toplevel. * * For Wayland windows, the position matches the position of the * surface associated with shell surface (wl_shell_surface, xdg_surface * etc). The size matches the size surface size as displayed in the stage. */ MetaRectangle buffer_rect; /* Cached net_wm_icon_geometry */ MetaRectangle icon_geometry; /* x/y/w/h here get filled with ConfigureRequest values */ XSizeHints size_hints; /* Managed by stack.c */ MetaStackLayer layer; int stack_position; /* see comment in stack.h */ /* Managed by delete.c */ MetaCloseDialog *close_dialog; /* maintained by group.c */ MetaGroup *group; GObject *compositor_private; /* Focused window that is (directly or indirectly) attached to this one */ MetaWindow *attached_focus_window; /* The currently complementary tiled windows, if any. */ MetaWindow *vtile_match; MetaWindow *htile_match; // MetaWindow *tile_match; /* Bypass compositor hints */ guint bypass_compositor; struct { MetaPlacementRule *rule; MetaPlacementState state; struct { int x; int y; int rel_x; int rel_y; } pending; struct { int rel_x; int rel_y; } current; } placement; guint unmanage_idle_id; }; struct _MetaWindowClass { GObjectClass parent_class; void (*manage) (MetaWindow *window); void (*unmanage) (MetaWindow *window); void (*ping) (MetaWindow *window, guint32 serial); void (*delete) (MetaWindow *window, guint32 timestamp); void (*kill) (MetaWindow *window); void (*focus) (MetaWindow *window, guint32 timestamp); void (*grab_op_began) (MetaWindow *window, MetaGrabOp op); void (*grab_op_ended) (MetaWindow *window, MetaGrabOp op); void (*current_workspace_changed) (MetaWindow *window); void (*move_resize_internal) (MetaWindow *window, MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, MetaRectangle temporary_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags, MetaMoveResizeResultFlags *result); gboolean (*update_struts) (MetaWindow *window); void (*get_default_skip_hints) (MetaWindow *window, gboolean *skip_taskbar_out, gboolean *skip_pager_out); gboolean (*update_icon) (MetaWindow *window, cairo_surface_t **icon, cairo_surface_t **mini_icon); uint32_t (*get_client_pid) (MetaWindow *window); void (*update_main_monitor) (MetaWindow *window, MetaWindowUpdateMonitorFlags flags); void (*main_monitor_changed) (MetaWindow *window, const MetaLogicalMonitor *old); void (*adjust_fullscreen_monitor_rect) (MetaWindow *window, MetaRectangle *monitor_rect); void (*force_restore_shortcuts) (MetaWindow *window, ClutterInputDevice *source); gboolean (*shortcuts_inhibited) (MetaWindow *window, ClutterInputDevice *source); gboolean (*is_focusable) (MetaWindow *window); gboolean (*is_stackable) (MetaWindow *window); gboolean (*can_ping) (MetaWindow *window); gboolean (*are_updates_frozen) (MetaWindow *window); gboolean (*is_focus_async) (MetaWindow *window); MetaStackLayer (*calculate_layer) (MetaWindow *window); void (* map) (MetaWindow *window); void (* unmap) (MetaWindow *window); }; /* These differ from window->has_foo_func in that they consider * the dynamic window state such as "maximized", not just the * window's type */ #define META_WINDOW_MAXIMIZED(w) ((w)->maximized_horizontally && \ (w)->maximized_vertically) #define META_WINDOW_MAXIMIZED_VERTICALLY(w) ((w)->maximized_vertically) #define META_WINDOW_MAXIMIZED_HORIZONTALLY(w) ((w)->maximized_horizontally) #define META_WINDOW_TILED(w) ((w)->maximized_vertically && \ !(w)->maximized_horizontally && \ (w)->tile_mode != META_TILE_NONE) #define META_WINDOW_TILED_LEFT(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_LEFT) #define META_WINDOW_TILED_RIGHT(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_RIGHT) #define META_WINDOW_TILED_LEFT_RIGHT(w) (META_WINDOW_TILED_LEFT(w) || META_WINDOW_TILED_RIGHT(w)) #define META_WINDOW_TILED_TOP(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_TOP) #define META_WINDOW_TILED_BOTTOM(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_BOTTOM) #define META_WINDOW_TILED_TOP_BOTTOM(w) (META_WINDOW_TILED_TOP(w) || META_WINDOW_TILED_BOTTOM(w)) #define META_WINDOW_TILED_ULC(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_ULC) #define META_WINDOW_TILED_URC(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_URC) #define META_WINDOW_TILED_LRC(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_LRC) #define META_WINDOW_TILED_LLC(w) (META_WINDOW_TILED(w) && \ (w)->tile_mode == META_TILE_LLC) #define META_WINDOW_TILED_CORNER(w) (META_WINDOW_TILED_ULC(w) || META_WINDOW_TILED_URC(w) || \ META_WINDOW_TILED_LRC(w) || META_WINDOW_TILED_LLC(w)) #define META_WINDOW_TILED_MAXIMIZED(w)(META_WINDOW_MAXIMIZED(w) && \ (w)->tile_mode == META_TILE_MAXIMIZED) #define META_WINDOW_ALLOWS_MOVE(w) ((w)->has_move_func && !(w)->fullscreen) #define META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS(w) ((w)->has_resize_func && !META_WINDOW_MAXIMIZED (w) && !(w)->fullscreen && !(w)->shaded) #define META_WINDOW_ALLOWS_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && \ (((w)->size_hints.min_width < (w)->size_hints.max_width) || \ ((w)->size_hints.min_height < (w)->size_hints.max_height))) #define META_WINDOW_ALLOWS_HORIZONTAL_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && (w)->size_hints.min_width < (w)->size_hints.max_width) #define META_WINDOW_ALLOWS_VERTICAL_RESIZE(w) (META_WINDOW_ALLOWS_RESIZE_EXCEPT_HINTS (w) && (w)->size_hints.min_height < (w)->size_hints.max_height) MetaWindow * _meta_window_shared_new (MetaDisplay *display, MetaWindowClientType client_type, MetaWaylandSurface *surface, Window xwindow, gulong existing_wm_state, MetaCompEffect effect, XWindowAttributes *attrs); void meta_window_unmanage (MetaWindow *window, guint32 timestamp); void meta_window_unmanage_on_idle (MetaWindow *window); void meta_window_queue (MetaWindow *window, guint queuebits); META_EXPORT_TEST void meta_window_tile (MetaWindow *window, MetaTileMode mode); MetaTileMode meta_window_get_tile_mode (MetaWindow *window); void meta_window_restore_tile (MetaWindow *window, MetaTileMode mode, int width, int height); void meta_window_maximize_internal (MetaWindow *window, MetaMaximizeFlags directions, MetaRectangle *saved_rect); void meta_window_make_fullscreen_internal (MetaWindow *window); void meta_window_update_fullscreen_monitors (MetaWindow *window, MetaLogicalMonitor *top, MetaLogicalMonitor *bottom, MetaLogicalMonitor *left, MetaLogicalMonitor *right); gboolean meta_window_has_fullscreen_monitors (MetaWindow *window); void meta_window_adjust_fullscreen_monitor_rect (MetaWindow *window, MetaRectangle *monitor_rect); void meta_window_resize_frame_with_gravity (MetaWindow *window, gboolean user_op, int w, int h, MetaGravity gravity); /* Return whether the window should be currently mapped */ gboolean meta_window_should_be_showing (MetaWindow *window); void meta_window_update_struts (MetaWindow *window); /* gets position we need to set to stay in current position, * assuming position will be gravity-compensated. i.e. * this is the position a client would send in a configure * request. */ void meta_window_get_gravity_position (MetaWindow *window, MetaGravity gravity, int *x, int *y); /* Get geometry for saving in the session; x/y are gravity * position, and w/h are in resize inc above the base size. */ void meta_window_get_session_geometry (MetaWindow *window, int *x, int *y, int *width, int *height); void meta_window_update_unfocused_button_grabs (MetaWindow *window); void meta_window_set_focused_internal (MetaWindow *window, gboolean focused); gboolean meta_window_is_focusable (MetaWindow *window); gboolean meta_window_can_ping (MetaWindow *window); MetaStackLayer meta_window_calculate_layer (MetaWindow *window); void meta_window_current_workspace_changed (MetaWindow *window); void meta_window_show_menu (MetaWindow *window, MetaWindowMenuType menu, int x, int y); void meta_window_show_menu_for_rect (MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect); gboolean meta_window_handle_mouse_grab_op_event (MetaWindow *window, const ClutterEvent *event); GList* meta_window_get_workspaces (MetaWindow *window); void meta_window_get_work_area_for_logical_monitor (MetaWindow *window, MetaLogicalMonitor *logical_monitor, MetaRectangle *area); int meta_window_get_current_tile_monitor_number (MetaWindow *window); void meta_window_get_tile_area (MetaWindow *window, MetaTileMode mode, MetaRectangle *tile_area); gboolean meta_window_same_application (MetaWindow *window, MetaWindow *other_window); #define META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE(w) \ ((w)->type != META_WINDOW_DOCK && (w)->type != META_WINDOW_DESKTOP) #define META_WINDOW_IN_NORMAL_TAB_CHAIN(w) \ (meta_window_is_focusable (w) && META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) && (!(w)->skip_taskbar)) #define META_WINDOW_IN_DOCK_TAB_CHAIN(w) \ (meta_window_is_focusable (w) && (! META_WINDOW_IN_NORMAL_TAB_CHAIN_TYPE (w) || (w)->skip_taskbar)) #define META_WINDOW_IN_GROUP_TAB_CHAIN(w, g) \ (meta_window_is_focusable (w) && (!g || meta_window_get_group(w)==g)) void meta_window_free_delete_dialog (MetaWindow *window); void meta_window_update_keyboard_resize (MetaWindow *window, gboolean update_cursor); void meta_window_update_keyboard_move (MetaWindow *window); MetaStackLayer meta_window_get_default_layer (MetaWindow *window); void meta_window_update_layer (MetaWindow *window); void meta_window_recalc_features (MetaWindow *window); void meta_window_set_type (MetaWindow *window, MetaWindowType type); void meta_window_frame_size_changed (MetaWindow *window); gboolean meta_window_is_in_stack (MetaWindow *window); void meta_window_stack_just_below (MetaWindow *window, MetaWindow *below_this_one); void meta_window_stack_just_above (MetaWindow *window, MetaWindow *above_this_one); void meta_window_set_user_time (MetaWindow *window, guint32 timestamp); void meta_window_update_for_monitors_changed (MetaWindow *window); void meta_window_on_all_workspaces_changed (MetaWindow *window); gboolean meta_window_should_attach_to_parent (MetaWindow *window); gboolean meta_window_can_tile_left_right (MetaWindow *window); gboolean meta_window_can_tile_top_bottom (MetaWindow *window); gboolean meta_window_can_tile_corner (MetaWindow *window); void meta_window_compute_tile_matches (MetaWindow *window); gboolean meta_window_updates_are_frozen (MetaWindow *window); void meta_window_set_title (MetaWindow *window, const char *title); void meta_window_set_wm_class (MetaWindow *window, const char *wm_class, const char *wm_instance); void meta_window_set_gtk_dbus_properties (MetaWindow *window, const char *application_id, const char *unique_bus_name, const char *appmenu_path, const char *menubar_path, const char *application_object_path, const char *window_object_path); gboolean meta_window_has_transient_type (MetaWindow *window); void meta_window_set_transient_for (MetaWindow *window, MetaWindow *parent); void meta_window_increase_opacity (MetaWindow *window); void meta_window_decrease_opacity (MetaWindow *window); void meta_window_handle_enter (MetaWindow *window, guint32 timestamp, guint root_x, guint root_y); void meta_window_handle_leave (MetaWindow *window); gboolean meta_window_handle_ui_frame_event (MetaWindow *window, const ClutterEvent *event); void meta_window_handle_ungrabbed_event (MetaWindow *window, const ClutterEvent *event); void meta_window_get_client_area_rect (const MetaWindow *window, cairo_rectangle_int_t *rect); void meta_window_get_titlebar_rect (MetaWindow *window, MetaRectangle *titlebar_rect); void meta_window_activate_full (MetaWindow *window, guint32 timestamp, MetaClientType source_indication, MetaWorkspace *workspace); MetaLogicalMonitor * meta_window_calculate_main_logical_monitor (MetaWindow *window); MetaLogicalMonitor * meta_window_get_main_logical_monitor (MetaWindow *window); void meta_window_update_monitor (MetaWindow *window, MetaWindowUpdateMonitorFlags flags); void meta_window_set_urgent (MetaWindow *window, gboolean urgent); void meta_window_update_resize (MetaWindow *window, gboolean snap, int x, int y, gboolean force); void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity gravity, MetaRectangle frame_rect); void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op); void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op); void meta_window_set_alive (MetaWindow *window, gboolean is_alive); gboolean meta_window_has_pointer (MetaWindow *window); void meta_window_emit_size_changed (MetaWindow *window); MetaPlacementRule *meta_window_get_placement_rule (MetaWindow *window); void meta_window_force_placement (MetaWindow *window, gboolean force_move); void meta_window_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source); gboolean meta_window_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source); gboolean meta_window_is_stackable (MetaWindow *window); gboolean meta_window_is_focus_async (MetaWindow *window); #endif muffin-6.4.1/src/core/place.c0000664000175000017500000007014414723361714014717 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window placement */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/place.h" #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "core/boxes-private.h" #include "meta/meta-backend.h" #include "meta/prefs.h" #include "meta/workspace.h" #include "meta/meta-cursor-tracker.h" typedef enum { META_LEFT, META_RIGHT, META_TOP, META_BOTTOM } MetaWindowDirection; static gint northwestcmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; MetaRectangle a_frame; MetaRectangle b_frame; int from_origin_a; int from_origin_b; int ax, ay, bx, by; meta_window_get_frame_rect (aw, &a_frame); meta_window_get_frame_rect (bw, &b_frame); ax = a_frame.x; ay = a_frame.y; bx = b_frame.x; by = b_frame.y; /* probably there's a fast good-enough-guess we could use here. */ from_origin_a = sqrt (ax * ax + ay * ay); from_origin_b = sqrt (bx * bx + by * by); if (from_origin_a < from_origin_b) return -1; else if (from_origin_a > from_origin_b) return 1; else return 0; } static gboolean place_by_pointer(MetaWindow *window, MetaPlacementMode placement_mode, int *new_x, int *new_y) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); int root_x, root_y; meta_cursor_tracker_get_pointer (cursor_tracker, &root_x, &root_y, NULL); MetaRectangle frame_rect; meta_window_get_frame_rect (window, &frame_rect); *new_x = root_x - frame_rect.width / 2; *new_y = root_y - frame_rect.height / 2; if (placement_mode == META_PLACEMENT_MODE_MANUAL) window->move_after_placement = TRUE; return TRUE; } static void find_next_cascade (MetaWindow *window, /* visible windows on relevant workspaces */ GList *windows, int x, int y, int *new_x, int *new_y) { MetaBackend *backend = meta_get_backend (); GList *tmp; GList *sorted; int cascade_x, cascade_y; MetaRectangle titlebar_rect; int x_threshold, y_threshold; MetaRectangle frame_rect; int window_width, window_height; int cascade_stage; MetaRectangle work_area; MetaLogicalMonitor *current; sorted = g_list_copy (windows); sorted = g_list_sort (sorted, northwestcmp); /* This is a "fuzzy" cascade algorithm. * For each window in the list, we find where we'd cascade a * new window after it. If a window is already nearly at that * position, we move on. */ /* arbitrary-ish threshold, honors user attempts to * manually cascade. */ #define CASCADE_FUZZ 15 meta_window_get_titlebar_rect (window, &titlebar_rect); x_threshold = MAX (titlebar_rect.x, CASCADE_FUZZ); y_threshold = MAX (titlebar_rect.y, CASCADE_FUZZ); /* Find furthest-SE origin of all workspaces. * cascade_x, cascade_y are the target position * of NW corner of window frame. */ current = meta_backend_get_current_logical_monitor (backend); meta_window_get_work_area_for_logical_monitor (window, current, &work_area); cascade_x = MAX (0, work_area.x); cascade_y = MAX (0, work_area.y); /* Find first cascade position that's not used. */ meta_window_get_frame_rect (window, &frame_rect); window_width = frame_rect.width; window_height = frame_rect.height; cascade_stage = 0; tmp = sorted; while (tmp != NULL) { MetaWindow *w; MetaRectangle w_frame_rect; int wx, wy; w = tmp->data; /* we want frame position, not window position */ meta_window_get_frame_rect (w, &w_frame_rect); wx = w_frame_rect.x; wy = w_frame_rect.y; if (ABS (wx - cascade_x) < x_threshold && ABS (wy - cascade_y) < y_threshold) { meta_window_get_titlebar_rect (w, &titlebar_rect); /* Cascade the window evenly by the titlebar height; this isn't a typo. */ cascade_x = wx + titlebar_rect.height; cascade_y = wy + titlebar_rect.height; /* If we go off the screen, start over with a new cascade */ if (((cascade_x + window_width) > (work_area.x + work_area.width)) || ((cascade_y + window_height) > (work_area.y + work_area.height))) { cascade_x = MAX (0, work_area.x); cascade_y = MAX (0, work_area.y); #define CASCADE_INTERVAL 50 /* space between top-left corners of cascades */ cascade_stage += 1; cascade_x += CASCADE_INTERVAL * cascade_stage; /* start over with a new cascade translated to the right, unless * we are out of space */ if ((cascade_x + window_width) < (work_area.x + work_area.width)) { tmp = sorted; continue; } else { /* All out of space, this cascade_x won't work */ cascade_x = MAX (0, work_area.x); break; } } } else { /* Keep searching for a further-down-the-diagonal window. */ } tmp = tmp->next; } /* cascade_x and cascade_y will match the last window in the list * that was "in the way" (in the approximate cascade diagonal) */ g_list_free (sorted); *new_x = cascade_x; *new_y = cascade_y; } static void find_most_freespace (MetaWindow *window, /* visible windows on relevant workspaces */ MetaWindow *focus_window, int x, int y, int *new_x, int *new_y) { MetaWindowDirection side; int max_area; int max_width, max_height, left, right, top, bottom; int left_space, right_space, top_space, bottom_space; MetaRectangle work_area; MetaRectangle avoid; MetaRectangle frame_rect; meta_window_get_work_area_current_monitor (focus_window, &work_area); meta_window_get_frame_rect (focus_window, &avoid); meta_window_get_frame_rect (window, &frame_rect); /* Find the areas of choosing the various sides of the focus window */ max_width = MIN (avoid.width, frame_rect.width); max_height = MIN (avoid.height, frame_rect.height); left_space = avoid.x - work_area.x; right_space = work_area.width - (avoid.x + avoid.width - work_area.x); top_space = avoid.y - work_area.y; bottom_space = work_area.height - (avoid.y + avoid.height - work_area.y); left = MIN (left_space, frame_rect.width); right = MIN (right_space, frame_rect.width); top = MIN (top_space, frame_rect.height); bottom = MIN (bottom_space, frame_rect.height); /* Find out which side of the focus_window can show the most of the window */ side = META_LEFT; max_area = left*max_height; if (right*max_height > max_area) { side = META_RIGHT; max_area = right*max_height; } if (top*max_width > max_area) { side = META_TOP; max_area = top*max_width; } if (bottom*max_width > max_area) { side = META_BOTTOM; max_area = bottom*max_width; } /* Give up if there's no where to put it (i.e. focus window is maximized) */ if (max_area == 0) return; /* Place the window on the relevant side; if the whole window fits, * make it adjacent to the focus window; if not, make sure the * window doesn't go off the edge of the screen. */ switch (side) { case META_LEFT: *new_y = avoid.y; if (left_space > frame_rect.width) *new_x = avoid.x - frame_rect.width; else *new_x = work_area.x; break; case META_RIGHT: *new_y = avoid.y; if (right_space > frame_rect.width) *new_x = avoid.x + avoid.width; else *new_x = work_area.x + work_area.width - frame_rect.width; break; case META_TOP: *new_x = avoid.x; if (top_space > frame_rect.height) *new_y = avoid.y - frame_rect.height; else *new_y = work_area.y; break; case META_BOTTOM: *new_x = avoid.x; if (bottom_space > frame_rect.height) *new_y = avoid.y + avoid.height; else *new_y = work_area.y + work_area.height - frame_rect.height; break; } } static gboolean window_overlaps_focus_window (MetaWindow *window) { MetaWindow *focus_window; MetaRectangle window_frame, focus_frame, overlap; focus_window = window->display->focus_window; if (focus_window == NULL) return FALSE; meta_window_get_frame_rect (window, &window_frame); meta_window_get_frame_rect (focus_window, &focus_frame); return meta_rectangle_intersect (&window_frame, &focus_frame, &overlap); } static gboolean window_place_centered (MetaWindow *window) { MetaWindowType type; type = window->type; return (type == META_WINDOW_DIALOG || type == META_WINDOW_MODAL_DIALOG || type == META_WINDOW_SPLASHSCREEN || (type == META_WINDOW_NORMAL && meta_prefs_get_new_window_placement_mode () == META_PLACEMENT_MODE_CENTER)); } static void avoid_being_obscured_as_second_modal_dialog (MetaWindow *window, int *x, int *y) { /* We can't center this dialog if it was denied focus and it * overlaps with the focus window and this dialog is modal and this * dialog is in the same app as the focus window (*phew*...please * don't make me say that ten times fast). See bug 307875 comment 11 * and 12 for details, but basically it means this is probably a * second modal dialog for some app while the focus window is the * first modal dialog. We should probably make them simultaneously * visible in general, but it becomes mandatory to do so due to * buggy apps (e.g. those using gtk+ *sigh*) because in those cases * this second modal dialog also happens to be modal to the first * dialog in addition to the main window, while it has only let us * know about the modal-to-the-main-window part. */ MetaWindow *focus_window; focus_window = window->display->focus_window; /* denied_focus_and_not_transient is only set when focus_window != NULL */ if (window->denied_focus_and_not_transient && window->type == META_WINDOW_MODAL_DIALOG && meta_window_same_application (window, focus_window) && window_overlaps_focus_window (window)) { find_most_freespace (window, focus_window, *x, *y, x, y); meta_topic (META_DEBUG_PLACEMENT, "Dialog window %s was denied focus but may be modal " "to the focus window; had to move it to avoid the " "focus window\n", window->desc); } } static gboolean rectangle_overlaps_some_window (MetaRectangle *rect, GList *windows) { GList *tmp; MetaRectangle dest; tmp = windows; while (tmp != NULL) { MetaWindow *other = tmp->data; MetaRectangle other_rect; switch (other->type) { case META_WINDOW_DOCK: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_DESKTOP: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: /* override redirect window types: */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: break; case META_WINDOW_NORMAL: case META_WINDOW_UTILITY: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: meta_window_get_frame_rect (other, &other_rect); if (meta_rectangle_intersect (rect, &other_rect, &dest)) return TRUE; break; } tmp = tmp->next; } return FALSE; } static gint leftmost_cmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; MetaRectangle a_frame; MetaRectangle b_frame; int ax, bx; meta_window_get_frame_rect (aw, &a_frame); meta_window_get_frame_rect (bw, &b_frame); ax = a_frame.x; bx = b_frame.x; if (ax < bx) return -1; else if (ax > bx) return 1; else return 0; } static gint topmost_cmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; MetaRectangle a_frame; MetaRectangle b_frame; int ay, by; meta_window_get_frame_rect (aw, &a_frame); meta_window_get_frame_rect (bw, &b_frame); ay = a_frame.y; by = b_frame.y; if (ay < by) return -1; else if (ay > by) return 1; else return 0; } static void center_tile_rect_in_area (MetaRectangle *rect, MetaRectangle *work_area) { int fluff; /* The point here is to tile a window such that "extra" * space is equal on either side (i.e. so a full screen * of windows tiled this way would center the windows * as a group) */ fluff = (work_area->width % (rect->width+1)) / 2; rect->x = work_area->x + fluff; fluff = (work_area->height % (rect->height+1)) / 3; rect->y = work_area->y + fluff; } /* Find the leftmost, then topmost, empty area on the workspace * that can contain the new window. * * Cool feature to have: if we can't fit the current window size, * try shrinking the window (within geometry constraints). But * beware windows such as Emacs with no sane minimum size, we * don't want to create a 1x1 Emacs. */ static gboolean find_first_fit (MetaWindow *window, /* visible windows on relevant workspaces */ GList *windows, MetaLogicalMonitor *logical_monitor, int x, int y, int *new_x, int *new_y) { /* This algorithm is limited - it just brute-force tries * to fit the window in a small number of locations that are aligned * with existing windows. It tries to place the window on * the bottom of each existing window, and then to the right * of each existing window, aligned with the left/top of the * existing window in each of those cases. */ int retval; GList *below_sorted; GList *right_sorted; GList *tmp; MetaRectangle rect; MetaRectangle work_area; retval = FALSE; /* Below each window */ below_sorted = g_list_copy (windows); below_sorted = g_list_sort (below_sorted, leftmost_cmp); below_sorted = g_list_sort (below_sorted, topmost_cmp); /* To the right of each window */ right_sorted = g_list_copy (windows); right_sorted = g_list_sort (right_sorted, topmost_cmp); right_sorted = g_list_sort (right_sorted, leftmost_cmp); meta_window_get_frame_rect (window, &rect); #ifdef WITH_VERBOSE_MODE { char monitor_location_string[RECT_LENGTH]; meta_rectangle_to_string (&logical_monitor->rect, monitor_location_string); meta_topic (META_DEBUG_XINERAMA, "Natural monitor is %s\n", monitor_location_string); } #endif meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &work_area); center_tile_rect_in_area (&rect, &work_area); if (meta_rectangle_contains_rect (&work_area, &rect) && !rectangle_overlaps_some_window (&rect, windows)) { *new_x = rect.x; *new_y = rect.y; retval = TRUE; goto out; } /* try below each window */ tmp = below_sorted; while (tmp != NULL) { MetaWindow *w = tmp->data; MetaRectangle frame_rect; meta_window_get_frame_rect (w, &frame_rect); rect.x = frame_rect.x; rect.y = frame_rect.y + frame_rect.height; if (meta_rectangle_contains_rect (&work_area, &rect) && !rectangle_overlaps_some_window (&rect, below_sorted)) { *new_x = rect.x; *new_y = rect.y; retval = TRUE; goto out; } tmp = tmp->next; } /* try to the right of each window */ tmp = right_sorted; while (tmp != NULL) { MetaWindow *w = tmp->data; MetaRectangle frame_rect; meta_window_get_frame_rect (w, &frame_rect); rect.x = frame_rect.x + frame_rect.width; rect.y = frame_rect.y; if (meta_rectangle_contains_rect (&work_area, &rect) && !rectangle_overlaps_some_window (&rect, right_sorted)) { *new_x = rect.x; *new_y = rect.y; retval = TRUE; goto out; } tmp = tmp->next; } out: g_list_free (below_sorted); g_list_free (right_sorted); return retval; } void meta_window_process_placement (MetaWindow *window, MetaPlacementRule *placement_rule, int *rel_x, int *rel_y) { MetaRectangle anchor_rect; int window_width, window_height; int x, y; window_width = placement_rule->width; window_height = placement_rule->height; anchor_rect = placement_rule->anchor_rect; /* Place at anchor point. */ if (placement_rule->anchor & META_PLACEMENT_ANCHOR_LEFT) x = anchor_rect.x; else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_RIGHT) x = anchor_rect.x + anchor_rect.width; else x = anchor_rect.x + (anchor_rect.width / 2); if (placement_rule->anchor & META_PLACEMENT_ANCHOR_TOP) y = anchor_rect.y; else if (placement_rule->anchor & META_PLACEMENT_ANCHOR_BOTTOM) y = anchor_rect.y + anchor_rect.height; else y = anchor_rect.y + (anchor_rect.height / 2); /* Shift according to gravity. */ if (placement_rule->gravity & META_PLACEMENT_GRAVITY_LEFT) x -= window_width; else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_RIGHT) x = x; else x -= window_width / 2; if (placement_rule->gravity & META_PLACEMENT_GRAVITY_TOP) y -= window_height; else if (placement_rule->gravity & META_PLACEMENT_GRAVITY_BOTTOM) y = y; else y -= window_height / 2; /* Offset according to offset. */ x += placement_rule->offset_x; y += placement_rule->offset_y; *rel_x = x; *rel_y = y; } void meta_window_place (MetaWindow *window, int x, int y, int *new_x, int *new_y) { MetaBackend *backend = meta_get_backend (); GList *windows = NULL; MetaLogicalMonitor *logical_monitor; MetaPlacementMode placement_mode; meta_topic (META_DEBUG_PLACEMENT, "Placing window %s\n", window->desc); g_return_if_fail (!window->placement.rule); switch (window->type) { /* Run placement algorithm on these. */ case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_SPLASHSCREEN: break; /* Assume the app knows best how to place these, no placement * algorithm ever (other than "leave them as-is") */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: case META_WINDOW_UTILITY: /* override redirect window types: */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: goto done; } if (meta_prefs_get_disable_workarounds ()) { switch (window->type) { /* Only accept USPosition on normal windows because the app is full * of shit claiming the user set -geometry for a dialog or dock */ case META_WINDOW_NORMAL: if (window->size_hints.flags & USPosition) { /* don't constrain with placement algorithm */ meta_topic (META_DEBUG_PLACEMENT, "Honoring USPosition for %s instead of using placement algorithm\n", window->desc); goto done; } break; /* Ignore even USPosition on dialogs, splashscreen */ case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_SPLASHSCREEN: break; /* Assume the app knows best how to place these. */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: case META_WINDOW_UTILITY: /* override redirect window types: */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: if (window->size_hints.flags & PPosition) { meta_topic (META_DEBUG_PLACEMENT, "Not placing non-normal non-dialog window with PPosition set\n"); goto done; } break; } } else { /* workarounds enabled */ if ((window->size_hints.flags & PPosition) || (window->size_hints.flags & USPosition)) { meta_topic (META_DEBUG_PLACEMENT, "Not placing window with PPosition or USPosition set\n"); avoid_being_obscured_as_second_modal_dialog (window, &x, &y); goto done; } } if (window->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG) { MetaWindow *parent = meta_window_get_transient_for (window); if (parent) { MetaRectangle frame_rect, parent_frame_rect; meta_window_get_frame_rect (window, &frame_rect); meta_window_get_frame_rect (parent, &parent_frame_rect); y = parent_frame_rect.y; /* center of parent */ x = parent_frame_rect.x + parent_frame_rect.width / 2; /* center of child over center of parent */ x -= frame_rect.width / 2; /* "visually" center window over parent, leaving twice as * much space below as on top. */ y += (parent_frame_rect.height - frame_rect.height)/3; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s over transient parent\n", window->desc); avoid_being_obscured_as_second_modal_dialog (window, &x, &y); goto done; } } /* FIXME UTILITY with transient set should be stacked up * on the sides of the parent window or something. */ if (window_place_centered (window)) { /* Center on current monitor */ int w, h; MetaRectangle frame_rect; meta_window_get_frame_rect (window, &frame_rect); /* Warning, this function is a round trip! */ logical_monitor = meta_backend_get_current_logical_monitor (backend); w = logical_monitor->rect.width; h = logical_monitor->rect.height; x = (w - frame_rect.width) / 2; y = (h - frame_rect.height) / 2; x += logical_monitor->rect.x; y += logical_monitor->rect.y; meta_topic (META_DEBUG_PLACEMENT, "Centered window %s on monitor %d\n", window->desc, logical_monitor->number); goto done_check_denied_focus; } /* Find windows that matter (not minimized, on same workspace * as placed window, may be shaded - if shaded we pretend it isn't * for placement purposes) */ { GSList *all_windows; GSList *tmp; all_windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); tmp = all_windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (w != window && meta_window_showing_on_its_workspace (w) && (window->on_all_workspaces || meta_window_located_on_workspace (w, window->workspace))) windows = g_list_prepend (windows, w); tmp = tmp->next; } g_slist_free (all_windows); } /* Warning, on X11 this might be a round trip! */ logical_monitor = meta_backend_get_current_logical_monitor (backend); /* Maximize windows if they are too big for their work area (bit of * a hack here). Assume undecorated windows probably don't intend to * be maximized. */ if (window->has_maximize_func && window->decorated && !window->fullscreen) { MetaRectangle workarea; MetaRectangle frame_rect; meta_window_get_work_area_for_logical_monitor (window, logical_monitor, &workarea); meta_window_get_frame_rect (window, &frame_rect); /* If the window is bigger than the screen, then automaximize. Do NOT * auto-maximize the directions independently. See #419810. */ if (frame_rect.width >= workarea.width && frame_rect.height >= workarea.height) { window->maximize_horizontally_after_placement = TRUE; window->maximize_vertically_after_placement = TRUE; } } /* "Origin" placement algorithm */ x = logical_monitor->rect.x; y = logical_monitor->rect.y; /* Placement based on pointer position */ placement_mode = meta_prefs_get_new_window_placement_mode(); if (placement_mode == META_PLACEMENT_MODE_POINTER || placement_mode == META_PLACEMENT_MODE_MANUAL) { if (place_by_pointer (window, placement_mode, &x, &y)) goto done_check_denied_focus; } if (find_first_fit (window, windows, logical_monitor, x, y, &x, &y)) goto done_check_denied_focus; /* No good fit? Fall back to cascading... */ find_next_cascade (window, windows, x, y, &x, &y); done_check_denied_focus: /* If the window is being denied focus and isn't a transient of the * focus window, we do NOT want it to overlap with the focus window * if at all possible. This is guaranteed to only be called if the * focus_window is non-NULL, and we try to avoid that window. */ if (window->denied_focus_and_not_transient) { MetaWindow *focus_window; gboolean found_fit; focus_window = window->display->focus_window; g_assert (focus_window != NULL); /* No need to do anything if the window doesn't overlap at all */ found_fit = !window_overlaps_focus_window (window); /* Try to do a first fit again, this time only taking into account the * focus window. */ if (!found_fit) { GList *focus_window_list; focus_window_list = g_list_prepend (NULL, focus_window); /* Reset x and y ("origin" placement algorithm) */ x = logical_monitor->rect.x; y = logical_monitor->rect.y; found_fit = find_first_fit (window, focus_window_list, logical_monitor, x, y, &x, &y); g_list_free (focus_window_list); } /* If that still didn't work, just place it where we can see as much * as possible. */ if (!found_fit) find_most_freespace (window, focus_window, x, y, &x, &y); } done: if (windows) g_list_free (windows); *new_x = x; *new_y = y; } muffin-6.4.1/src/core/startup-notification.c0000664000175000017500000004445414723361714020026 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include "core/display-private.h" #include "core/startup-notification-private.h" #include "core/util-private.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" /* This should be fairly long, as it should never be required unless * apps or .desktop files are buggy, and it's confusing if * OpenOffice or whatever seems to stop launching - people * might decide they need to launch it again. */ #define STARTUP_TIMEOUT_MS 15000 enum { PROP_0, PROP_DISPLAY, N_PROPS }; enum { PROP_SEQ_0, PROP_SEQ_ID, PROP_SEQ_TIMESTAMP, PROP_SEQ_ICON_NAME, PROP_SEQ_APPLICATION_ID, PROP_SEQ_WMCLASS, PROP_SEQ_WORKSPACE, PROP_SEQ_NAME, N_SEQ_PROPS }; enum { SEQ_COMPLETE, N_SEQ_SIGNALS }; enum { CHANGED, N_SIGNALS }; static guint sn_signals[N_SIGNALS]; static GParamSpec *sn_props[N_PROPS]; static guint seq_signals[N_SEQ_SIGNALS]; static GParamSpec *seq_props[N_SEQ_PROPS]; typedef struct { GSList *list; gint64 now; } CollectTimedOutData; struct _MetaStartupNotification { GObject parent_instance; MetaDisplay *display; GSList *startup_sequences; guint startup_sequence_timeout; }; typedef struct { char *wmclass; char *name; char *application_id; char *icon_name; char *id; uint64_t timestamp; int workspace; uint completed : 1; } MetaStartupSequencePrivate; G_DEFINE_TYPE (MetaStartupNotification, meta_startup_notification, G_TYPE_OBJECT) G_DEFINE_TYPE_WITH_PRIVATE (MetaStartupSequence, meta_startup_sequence, G_TYPE_OBJECT) static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn); static gboolean meta_startup_notification_has_pending_sequences (MetaStartupNotification *sn) { GSList *l; for (l = sn->startup_sequences; l; l = l->next) { if (!meta_startup_sequence_get_completed (l->data)) return TRUE; } return FALSE; } static void meta_startup_notification_update_feedback (MetaStartupNotification *sn) { MetaDisplay *display = sn->display; if (meta_startup_notification_has_pending_sequences (sn)) { meta_topic (META_DEBUG_STARTUP, "Setting busy cursor\n"); meta_display_set_cursor (display, META_CURSOR_BUSY); } else { meta_topic (META_DEBUG_STARTUP, "Setting default cursor\n"); meta_display_set_cursor (display, META_CURSOR_DEFAULT); } } static void meta_startup_sequence_init (MetaStartupSequence *seq) { } static void meta_startup_sequence_finalize (GObject *object) { MetaStartupSequence *seq; MetaStartupSequencePrivate *priv; seq = META_STARTUP_SEQUENCE (object); priv = meta_startup_sequence_get_instance_private (seq); g_free (priv->id); g_free (priv->wmclass); g_free (priv->name); g_free (priv->application_id); g_free (priv->icon_name); G_OBJECT_CLASS (meta_startup_sequence_parent_class)->finalize (object); } static void meta_startup_sequence_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStartupSequence *seq; MetaStartupSequencePrivate *priv; seq = META_STARTUP_SEQUENCE (object); priv = meta_startup_sequence_get_instance_private (seq); switch (prop_id) { case PROP_SEQ_ID: priv->id = g_value_dup_string (value); break; case PROP_SEQ_TIMESTAMP: priv->timestamp = g_value_get_uint64 (value); break; case PROP_SEQ_ICON_NAME: priv->icon_name = g_value_dup_string (value); break; case PROP_SEQ_APPLICATION_ID: priv->application_id = g_value_dup_string (value); break; case PROP_SEQ_WMCLASS: priv->wmclass = g_value_dup_string (value); break; case PROP_SEQ_WORKSPACE: priv->workspace = g_value_get_int (value); break; case PROP_SEQ_NAME: priv->name = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_sequence_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStartupSequence *seq; MetaStartupSequencePrivate *priv; seq = META_STARTUP_SEQUENCE (object); priv = meta_startup_sequence_get_instance_private (seq); switch (prop_id) { case PROP_SEQ_ID: g_value_set_string (value, priv->id); break; case PROP_SEQ_TIMESTAMP: g_value_set_uint64 (value, priv->timestamp); break; case PROP_SEQ_ICON_NAME: g_value_set_string (value, priv->icon_name); break; case PROP_SEQ_APPLICATION_ID: g_value_set_string (value, priv->application_id); break; case PROP_SEQ_WMCLASS: g_value_set_string (value, priv->wmclass); break; case PROP_SEQ_WORKSPACE: g_value_set_int (value, priv->workspace); break; case PROP_SEQ_NAME: g_value_set_string (value, priv->name); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_sequence_class_init (MetaStartupSequenceClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_startup_sequence_finalize; object_class->set_property = meta_startup_sequence_set_property; object_class->get_property = meta_startup_sequence_get_property; seq_signals[SEQ_COMPLETE] = g_signal_new ("complete", META_TYPE_STARTUP_SEQUENCE, G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (MetaStartupSequenceClass, complete), NULL, NULL, NULL, G_TYPE_NONE, 0); seq_props[PROP_SEQ_ID] = g_param_spec_string ("id", "ID", "ID", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_TIMESTAMP] = g_param_spec_uint64 ("timestamp", "Timestamp", "Timestamp", 0, G_MAXUINT64, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_ICON_NAME] = g_param_spec_string ("icon-name", "Icon name", "Icon name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_APPLICATION_ID] = g_param_spec_string ("application-id", "Application ID", "Application ID", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_WMCLASS] = g_param_spec_string ("wmclass", "WM class", "WM class", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_WORKSPACE] = g_param_spec_int ("workspace", "Workspace", "Workspace", G_MININT, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); seq_props[PROP_SEQ_NAME] = g_param_spec_string ("name", "Name", "Name", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_SEQ_PROPS, seq_props); } const char * meta_startup_sequence_get_id (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); priv = meta_startup_sequence_get_instance_private (seq); return priv->id; } uint64_t meta_startup_sequence_get_timestamp (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), 0); priv = meta_startup_sequence_get_instance_private (seq); return priv->timestamp; } void meta_startup_sequence_complete (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_if_fail (META_IS_STARTUP_SEQUENCE (seq)); priv = meta_startup_sequence_get_instance_private (seq); if (priv->completed) return; priv->completed = TRUE; g_signal_emit (seq, seq_signals[SEQ_COMPLETE], 0); } gboolean meta_startup_sequence_get_completed (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), FALSE); priv = meta_startup_sequence_get_instance_private (seq); return priv->completed; } const char * meta_startup_sequence_get_name (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); priv = meta_startup_sequence_get_instance_private (seq); return priv->name; } int meta_startup_sequence_get_workspace (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), 0); priv = meta_startup_sequence_get_instance_private (seq); return priv->workspace; } const char * meta_startup_sequence_get_icon_name (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); priv = meta_startup_sequence_get_instance_private (seq); return priv->icon_name; } const char * meta_startup_sequence_get_application_id (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); priv = meta_startup_sequence_get_instance_private (seq); return priv->application_id; } const char * meta_startup_sequence_get_wmclass (MetaStartupSequence *seq) { MetaStartupSequencePrivate *priv; g_return_val_if_fail (META_IS_STARTUP_SEQUENCE (seq), NULL); priv = meta_startup_sequence_get_instance_private (seq); return priv->wmclass; } static void on_sequence_completed (MetaStartupSequence *seq, MetaStartupNotification *sn) { meta_startup_notification_update_feedback (sn); g_signal_emit (sn, sn_signals[CHANGED], 0, seq); } void meta_startup_notification_add_sequence (MetaStartupNotification *sn, MetaStartupSequence *seq) { sn->startup_sequences = g_slist_prepend (sn->startup_sequences, g_object_ref (seq)); g_signal_connect (seq, "complete", G_CALLBACK (on_sequence_completed), sn); meta_startup_notification_ensure_timeout (sn); meta_startup_notification_update_feedback (sn); g_signal_emit (sn, sn_signals[CHANGED], 0, seq); } static void collect_timed_out_foreach (void *element, void *data) { MetaStartupSequence *sequence = element; CollectTimedOutData *ctod = data; gint64 elapsed, timestamp; timestamp = meta_startup_sequence_get_timestamp (sequence); elapsed = ctod->now - timestamp; meta_topic (META_DEBUG_STARTUP, "Sequence used %" G_GINT64_FORMAT " ms vs. %d max: %s\n", elapsed, STARTUP_TIMEOUT_MS, meta_startup_sequence_get_id (sequence)); if (elapsed > STARTUP_TIMEOUT_MS) ctod->list = g_slist_prepend (ctod->list, sequence); } static gboolean startup_sequence_timeout (void *data) { MetaStartupNotification *sn = data; CollectTimedOutData ctod; GSList *l; ctod.list = NULL; ctod.now = g_get_monotonic_time () / 1000; g_slist_foreach (sn->startup_sequences, collect_timed_out_foreach, &ctod); for (l = ctod.list; l != NULL; l = l->next) { MetaStartupSequence *sequence = l->data; meta_topic (META_DEBUG_STARTUP, "Timed out sequence %s\n", meta_startup_sequence_get_id (sequence)); meta_startup_sequence_complete (sequence); meta_startup_notification_remove_sequence (sn, sequence); } g_slist_free (ctod.list); if (sn->startup_sequences != NULL) { return TRUE; } else { /* remove */ sn->startup_sequence_timeout = 0; return FALSE; } } static void meta_startup_notification_ensure_timeout (MetaStartupNotification *sn) { if (sn->startup_sequence_timeout != 0) return; /* our timeout just polls every second, instead of bothering * to compute exactly when we may next time out */ sn->startup_sequence_timeout = g_timeout_add_seconds (1, startup_sequence_timeout, sn); g_source_set_name_by_id (sn->startup_sequence_timeout, "[mutter] startup_sequence_timeout"); } void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, MetaStartupSequence *seq) { sn->startup_sequences = g_slist_remove (sn->startup_sequences, seq); meta_startup_notification_update_feedback (sn); g_signal_handlers_disconnect_by_func (seq, on_sequence_completed, sn); if (sn->startup_sequences == NULL) g_clear_handle_id (&sn->startup_sequence_timeout, g_source_remove); g_signal_emit (sn, sn_signals[CHANGED], 0, seq); g_object_unref (seq); } MetaStartupSequence * meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, const gchar *id) { MetaStartupSequence *seq; const gchar *seq_id; GSList *l; for (l = sn->startup_sequences; l; l = l->next) { seq = l->data; seq_id = meta_startup_sequence_get_id (seq); if (g_str_equal (seq_id, id)) return l->data; } return NULL; } static void meta_startup_notification_init (MetaStartupNotification *sn) { } static void meta_startup_notification_finalize (GObject *object) { MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); g_clear_handle_id (&sn->startup_sequence_timeout, g_source_remove); g_slist_free_full (sn->startup_sequences, g_object_unref); sn->startup_sequences = NULL; G_OBJECT_CLASS (meta_startup_notification_parent_class)->finalize (object); } static void meta_startup_notification_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); switch (prop_id) { case PROP_DISPLAY: sn->display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, sn->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_notification_constructed (GObject *object) { MetaStartupNotification *sn = META_STARTUP_NOTIFICATION (object); g_assert (sn->display != NULL); sn->startup_sequences = NULL; sn->startup_sequence_timeout = 0; } static void meta_startup_notification_class_init (MetaStartupNotificationClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_startup_notification_constructed; object_class->finalize = meta_startup_notification_finalize; object_class->set_property = meta_startup_notification_set_property; object_class->get_property = meta_startup_notification_get_property; sn_props[PROP_DISPLAY] = g_param_spec_object ("display", "Display", "Display", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); sn_signals[CHANGED] = g_signal_new ("changed", META_TYPE_STARTUP_NOTIFICATION, G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); g_object_class_install_properties (object_class, N_PROPS, sn_props); } MetaStartupNotification * meta_startup_notification_new (MetaDisplay *display) { return g_object_new (META_TYPE_STARTUP_NOTIFICATION, "display", display, NULL); } GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn) { return sn->startup_sequences; } /** * meta_startup_notification_create_launcher: * @sn: a #MetaStartupNotification * * Creates an app launch context. * * Returns: (transfer full): a launch context. **/ MetaLaunchContext * meta_startup_notification_create_launcher (MetaStartupNotification *sn) { return g_object_new (META_TYPE_LAUNCH_CONTEXT, "display", sn->display, NULL); } muffin-6.4.1/src/core/meta-workspace-manager.c0000664000175000017500000010046314723361714020163 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/meta-workspace-manager-private.h" #include #include #include #include "core/window-private.h" #include "core/workspace-private.h" #include "meta/meta-enum-types.h" #include "meta/prefs.h" #include "meta/util.h" G_DEFINE_TYPE (MetaWorkspaceManager, meta_workspace_manager, G_TYPE_OBJECT) enum { WORKSPACE_ADDED, WORKSPACE_REMOVED, WORKSPACE_SWITCHED, WORKSPACES_REORDERED, ACTIVE_WORKSPACE_CHANGED, SHOWING_DESKTOP_CHANGED, LAST_SIGNAL }; enum { PROP_0, PROP_LAYOUT_COLUMNS, PROP_LAYOUT_ROWS, PROP_N_WORKSPACES }; static guint workspace_manager_signals [LAST_SIGNAL] = { 0 }; static void prefs_changed_callback (MetaPreference pref, gpointer data); static void meta_workspace_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object); switch (prop_id) { case PROP_LAYOUT_COLUMNS: g_value_set_int (value, workspace_manager->columns_of_workspaces); break; case PROP_LAYOUT_ROWS: g_value_set_int (value, workspace_manager->rows_of_workspaces); break; case PROP_N_WORKSPACES: g_value_set_int (value, meta_workspace_manager_get_n_workspaces (workspace_manager)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_workspace_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_workspace_manager_finalize (GObject *object) { MetaWorkspaceManager *workspace_manager = META_WORKSPACE_MANAGER (object); meta_prefs_remove_listener (prefs_changed_callback, workspace_manager); G_OBJECT_CLASS (meta_workspace_manager_parent_class)->finalize (object); } static void meta_workspace_manager_class_init (MetaWorkspaceManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = meta_workspace_manager_get_property; object_class->set_property = meta_workspace_manager_set_property; object_class->finalize = meta_workspace_manager_finalize; workspace_manager_signals[WORKSPACE_ADDED] = g_signal_new ("workspace-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); workspace_manager_signals[WORKSPACE_REMOVED] = g_signal_new ("workspace-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); workspace_manager_signals[WORKSPACE_SWITCHED] = g_signal_new ("workspace-switched", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, G_TYPE_INT, G_TYPE_INT, META_TYPE_MOTION_DIRECTION); /* Emitted when calling meta_workspace_manager_reorder_workspace. * * This signal is emitted when a workspace has been reordered to * a different index. Note that other workspaces can change * their index too when reordering happens. */ workspace_manager_signals[WORKSPACES_REORDERED] = g_signal_new ("workspaces-reordered", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED] = g_signal_new ("active-workspace-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); workspace_manager_signals[SHOWING_DESKTOP_CHANGED] = g_signal_new ("showing-desktop-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); g_object_class_install_property (object_class, PROP_LAYOUT_COLUMNS, g_param_spec_int ("layout-columns", "Layout columns", "Number of columns in layout", -1, G_MAXINT, 1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_LAYOUT_ROWS, g_param_spec_int ("layout-rows", "Layout rows", "Number of rows in layout", -1, G_MAXINT, -1, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_N_WORKSPACES, g_param_spec_int ("n-workspaces", "N Workspaces", "Number of workspaces", 1, G_MAXINT, 1, G_PARAM_READABLE)); } static void meta_workspace_manager_init (MetaWorkspaceManager *workspace_manager) { } void meta_workspace_manager_reload_work_areas (MetaWorkspaceManager *workspace_manager) { GList *l; for (l = workspace_manager->workspaces; l; l = l->next) { MetaWorkspace *workspace = l->data; meta_workspace_invalidate_work_area (workspace); } } MetaWorkspaceManager * meta_workspace_manager_new (MetaDisplay *display) { MetaWorkspaceManager *workspace_manager; workspace_manager = g_object_new (META_TYPE_WORKSPACE_MANAGER, NULL); workspace_manager->display = display; workspace_manager->active_workspace = NULL; workspace_manager->workspaces = NULL; workspace_manager->rows_of_workspaces = 1; workspace_manager->columns_of_workspaces = -1; workspace_manager->vertical_workspaces = FALSE; workspace_manager->starting_corner = META_DISPLAY_TOPLEFT; /* This is the default layout extracted from default * variable values in update_num_workspaces () * This can be overriden using _NET_DESKTOP_LAYOUT in * meta_x11_display_new (), if it's specified */ meta_workspace_manager_update_workspace_layout (workspace_manager, META_DISPLAY_TOPLEFT, FALSE, -1, 1); /* There must be at least one workspace at all times, * so create that required workspace. */ meta_workspace_new (workspace_manager); meta_workspace_manager_init_workspaces (workspace_manager); meta_prefs_add_listener (prefs_changed_callback, workspace_manager); return workspace_manager; } void meta_workspace_manager_init_workspaces (MetaWorkspaceManager *workspace_manager) { int num; g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); if (meta_prefs_get_dynamic_workspaces ()) /* This will be properly updated using _NET_NUMBER_OF_DESKTOPS * (if set) in meta_x11_display_new () */ num = 1; else num = meta_prefs_get_num_workspaces (); meta_workspace_manager_update_num_workspaces (workspace_manager, META_CURRENT_TIME, num); meta_workspace_activate (workspace_manager->workspaces->data, META_CURRENT_TIME); meta_workspace_manager_reload_work_areas (workspace_manager); } int meta_workspace_manager_get_n_workspaces (MetaWorkspaceManager *workspace_manager) { return g_list_length (workspace_manager->workspaces); } /** * meta_workspace_manager_get_workspace_by_index: * @workspace_manager: a #MetaWorkspaceManager * @index: index of one of the display's workspaces * * Gets the workspace object for one of a workspace manager's workspaces given the workspace * index. It's valid to call this function with an out-of-range index and it * will robustly return %NULL. * * Return value: (transfer none) (nullable): the workspace object with specified * index, or %NULL if the index is out of range. */ MetaWorkspace * meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager, int idx) { return g_list_nth_data (workspace_manager->workspaces, idx); } void meta_workspace_manager_remove_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace, guint32 timestamp) { GList *l; GList *next; MetaWorkspace *neighbour = NULL; int index; int active_index; gboolean active_index_changed; int new_num; l = g_list_find (workspace_manager->workspaces, workspace); if (!l) return; next = l->next; if (l->prev) neighbour = l->prev->data; else if (l->next) neighbour = l->next->data; else { /* Cannot remove the only workspace! */ return; } meta_workspace_relocate_windows (workspace, neighbour); if (workspace == workspace_manager->active_workspace) meta_workspace_activate (neighbour, timestamp); /* To emit the signal after removing the workspace */ index = meta_workspace_index (workspace); active_index = meta_workspace_manager_get_active_workspace_index (workspace_manager); active_index_changed = index < active_index; /* This also removes the workspace from the displays list */ meta_workspace_remove (workspace); new_num = g_list_length (workspace_manager->workspaces); if (!meta_prefs_get_dynamic_workspaces ()) meta_prefs_set_num_workspaces (new_num); /* If deleting a workspace before the current workspace, the active * workspace index changes, so we need to update that hint */ if (active_index_changed) g_signal_emit (workspace_manager, workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED], 0, NULL); for (l = next; l; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_index_changed (w); } meta_display_queue_workarea_recalc (workspace_manager->display); g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_REMOVED], 0, index); g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); } /** * meta_workspace_manager_append_new_workspace: * @workspace_manager: a #MetaWorkspaceManager * @activate: %TRUE if the workspace should be switched to after creation * @timestamp: if switching to a new workspace, timestamp to be used when * focusing a window on the new workspace. (Doesn't hurt to pass a valid * timestamp when available even if not switching workspaces.) * * Append a new workspace to the workspace manager and (optionally) switch to that * display. * * Return value: (transfer none): the newly appended workspace. */ MetaWorkspace * meta_workspace_manager_append_new_workspace (MetaWorkspaceManager *workspace_manager, gboolean activate, guint32 timestamp) { MetaWorkspace *w; int new_num; /* This also adds the workspace to the workspace manager list */ w = meta_workspace_new (workspace_manager); if (!w) return NULL; if (activate) meta_workspace_activate (w, timestamp); new_num = g_list_length (workspace_manager->workspaces); if (!meta_prefs_get_dynamic_workspaces ()) meta_prefs_set_num_workspaces (new_num); meta_display_queue_workarea_recalc (workspace_manager->display); g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_ADDED], 0, meta_workspace_index (w)); g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); return w; } void meta_workspace_manager_update_num_workspaces (MetaWorkspaceManager *workspace_manager, guint32 timestamp, int new_num) { int old_num; GList *l; int i = 0; GList *extras = NULL; MetaWorkspace *last_remaining = NULL; gboolean need_change_space = FALSE; g_assert (new_num > 0); if (g_list_length (workspace_manager->workspaces) == (guint) new_num) return; for (l = workspace_manager->workspaces; l; l = l->next) { MetaWorkspace *w = l->data; if (i >= new_num) extras = g_list_prepend (extras, w); else last_remaining = w; ++i; } old_num = i; g_assert (last_remaining); /* Get rid of the extra workspaces by moving all their windows * to last_remaining, then activating last_remaining if * one of the removed workspaces was active. This will be a bit * wacky if the config tool for changing number of workspaces * is on a removed workspace ;-) */ for (l = extras; l; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_relocate_windows (w, last_remaining); if (w == workspace_manager->active_workspace) need_change_space = TRUE; } if (need_change_space) meta_workspace_activate (last_remaining, timestamp); /* Should now be safe to free the workspaces */ for (l = extras; l; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_remove (w); } g_list_free (extras); for (i = old_num; i < new_num; i++) meta_workspace_new (workspace_manager); meta_display_queue_workarea_recalc (workspace_manager->display); for (i = old_num; i < new_num; i++) g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_ADDED], 0, i); g_object_notify (G_OBJECT (workspace_manager), "n-workspaces"); } /** * meta_workspace_manager_reorder_workspace: * @workspace_manager: a #MetaWorkspaceManager * @workspace: a #MetaWorkspace to reorder * @new_index: the new index of the passed workspace * * Reorder a workspace to a new index. If the workspace is currently active * the "active-workspace-changed" signal will be emited. * If the workspace's index is the same as @new_index or the workspace * will not be found in the list, this function will return. * * Calling this function will also emit the "workspaces-reordered" signal. */ void meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace, int new_index) { GList *l; GList *from, *to; int index; int active_index, new_active_index; g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); g_return_if_fail (new_index >= 0 && new_index < g_list_length (workspace_manager->workspaces)); l = g_list_find (workspace_manager->workspaces, workspace); g_return_if_fail (l); index = meta_workspace_index (workspace); if (new_index == index) return; active_index = meta_workspace_manager_get_active_workspace_index (workspace_manager); workspace_manager->workspaces = g_list_remove_link (workspace_manager->workspaces, l); workspace_manager->workspaces = g_list_insert (workspace_manager->workspaces, l->data, new_index); g_list_free (l); new_active_index = meta_workspace_manager_get_active_workspace_index (workspace_manager); if (active_index != new_active_index) g_signal_emit (workspace_manager, workspace_manager_signals[ACTIVE_WORKSPACE_CHANGED], 0, NULL); from = g_list_nth (workspace_manager->workspaces, MIN (new_index, index)); to = g_list_nth (workspace_manager->workspaces, MAX (new_index, index)); for (l = from; l != to->next; l = l->next) { MetaWorkspace *w = l->data; meta_workspace_index_changed (w); } meta_display_queue_workarea_recalc (workspace_manager->display); g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACES_REORDERED], 0, NULL); } void meta_workspace_manager_update_workspace_layout (MetaWorkspaceManager *workspace_manager, MetaDisplayCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns) { g_return_if_fail (META_IS_WORKSPACE_MANAGER (workspace_manager)); g_return_if_fail (n_rows > 0 || n_columns > 0); g_return_if_fail (n_rows != 0 && n_columns != 0); if (workspace_manager->workspace_layout_overridden) return; workspace_manager->vertical_workspaces = vertical_layout != FALSE; workspace_manager->starting_corner = starting_corner; workspace_manager->rows_of_workspaces = n_rows; workspace_manager->columns_of_workspaces = n_columns; meta_verbose ("Workspace layout rows = %d cols = %d orientation = %d starting corner = %u\n", workspace_manager->rows_of_workspaces, workspace_manager->columns_of_workspaces, workspace_manager->vertical_workspaces, workspace_manager->starting_corner); g_object_notify (G_OBJECT (workspace_manager), "layout-columns"); g_object_notify (G_OBJECT (workspace_manager), "layout-rows"); } /** * meta_workspace_manager_override_workspace_layout: * @workspace_manager: a #MetaWorkspaceManager * @starting_corner: the corner at which the first workspace is found * @vertical_layout: if %TRUE the workspaces are laid out in columns rather than rows * @n_rows: number of rows of workspaces, or -1 to determine the number of rows from * @n_columns and the total number of workspaces * @n_columns: number of columns of workspaces, or -1 to determine the number of columns from * @n_rows and the total number of workspaces * * Explicitly set the layout of workspaces. Once this has been called, the contents of the * _NET_DESKTOP_LAYOUT property on the root window are completely ignored. */ void meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *workspace_manager, MetaDisplayCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns) { workspace_manager->workspace_layout_overridden = FALSE; meta_workspace_manager_update_workspace_layout (workspace_manager, starting_corner, vertical_layout, n_rows, n_columns); workspace_manager->workspace_layout_overridden = TRUE; } #ifdef WITH_VERBOSE_MODE static const char * meta_workspace_manager_corner_to_string (MetaDisplayCorner corner) { switch (corner) { case META_DISPLAY_TOPLEFT: return "TopLeft"; case META_DISPLAY_TOPRIGHT: return "TopRight"; case META_DISPLAY_BOTTOMLEFT: return "BottomLeft"; case META_DISPLAY_BOTTOMRIGHT: return "BottomRight"; } return "Unknown"; } #endif /* WITH_VERBOSE_MODE */ void meta_workspace_manager_calc_workspace_layout (MetaWorkspaceManager *workspace_manager, int num_workspaces, int current_space, MetaWorkspaceLayout *layout) { int rows, cols; int grid_area; int *grid; int i, r, c; int current_row, current_col; rows = workspace_manager->rows_of_workspaces; cols = workspace_manager->columns_of_workspaces; if (rows <= 0 && cols <= 0) cols = num_workspaces; if (rows <= 0) rows = num_workspaces / cols + ((num_workspaces % cols) > 0 ? 1 : 0); if (cols <= 0) cols = num_workspaces / rows + ((num_workspaces % rows) > 0 ? 1 : 0); /* paranoia */ if (rows < 1) rows = 1; if (cols < 1) cols = 1; g_assert (rows != 0 && cols != 0); grid_area = rows * cols; meta_verbose ("Getting layout rows = %d cols = %d current = %d " "num_spaces = %d vertical = %s corner = %s\n", rows, cols, current_space, num_workspaces, workspace_manager->vertical_workspaces ? "(true)" : "(false)", meta_workspace_manager_corner_to_string (workspace_manager->starting_corner)); /* ok, we want to setup the distances in the workspace array to go * in each direction. Remember, there are many ways that a workspace * array can be setup. * see http://www.freedesktop.org/standards/wm-spec/1.2/html/x109.html * and look at the _NET_DESKTOP_LAYOUT section for details. * For instance: */ /* starting_corner = META_DISPLAY_TOPLEFT * vertical_workspaces = 0 vertical_workspaces=1 * 1234 1357 * 5678 2468 * * starting_corner = META_DISPLAY_TOPRIGHT * vertical_workspaces = 0 vertical_workspaces=1 * 4321 7531 * 8765 8642 * * starting_corner = META_DISPLAY_BOTTOMLEFT * vertical_workspaces = 0 vertical_workspaces=1 * 5678 2468 * 1234 1357 * * starting_corner = META_DISPLAY_BOTTOMRIGHT * vertical_workspaces = 0 vertical_workspaces=1 * 8765 8642 * 4321 7531 * */ /* keep in mind that we could have a ragged layout, e.g. the "8" * in the above grids could be missing */ grid = g_new (int, grid_area); i = 0; switch (workspace_manager->starting_corner) { case META_DISPLAY_TOPLEFT: if (workspace_manager->vertical_workspaces) { c = 0; while (c < cols) { r = 0; while (r < rows) { grid[r*cols+c] = i; ++i; ++r; } ++c; } } else { r = 0; while (r < rows) { c = 0; while (c < cols) { grid[r*cols+c] = i; ++i; ++c; } ++r; } } break; case META_DISPLAY_TOPRIGHT: if (workspace_manager->vertical_workspaces) { c = cols - 1; while (c >= 0) { r = 0; while (r < rows) { grid[r*cols+c] = i; ++i; ++r; } --c; } } else { r = 0; while (r < rows) { c = cols - 1; while (c >= 0) { grid[r*cols+c] = i; ++i; --c; } ++r; } } break; case META_DISPLAY_BOTTOMLEFT: if (workspace_manager->vertical_workspaces) { c = 0; while (c < cols) { r = rows - 1; while (r >= 0) { grid[r*cols+c] = i; ++i; --r; } ++c; } } else { r = rows - 1; while (r >= 0) { c = 0; while (c < cols) { grid[r*cols+c] = i; ++i; ++c; } --r; } } break; case META_DISPLAY_BOTTOMRIGHT: if (workspace_manager->vertical_workspaces) { c = cols - 1; while (c >= 0) { r = rows - 1; while (r >= 0) { grid[r*cols+c] = i; ++i; --r; } --c; } } else { r = rows - 1; while (r >= 0) { c = cols - 1; while (c >= 0) { grid[r*cols+c] = i; ++i; --c; } --r; } } break; } if (i != grid_area) meta_bug ("did not fill in the whole workspace grid in %s (%d filled)\n", G_STRFUNC, i); current_row = 0; current_col = 0; r = 0; while (r < rows) { c = 0; while (c < cols) { if (grid[r*cols+c] == current_space) { current_row = r; current_col = c; } else if (grid[r*cols+c] >= num_workspaces) { /* flag nonexistent spaces with -1 */ grid[r*cols+c] = -1; } ++c; } ++r; } layout->rows = rows; layout->cols = cols; layout->grid = grid; layout->grid_area = grid_area; layout->current_row = current_row; layout->current_col = current_col; #ifdef WITH_VERBOSE_MODE if (meta_is_verbose ()) { r = 0; while (r < layout->rows) { meta_verbose (" "); meta_push_no_msg_prefix (); c = 0; while (c < layout->cols) { if (r == layout->current_row && c == layout->current_col) meta_verbose ("*%2d ", layout->grid[r*layout->cols+c]); else meta_verbose ("%3d ", layout->grid[r*layout->cols+c]); ++c; } meta_verbose ("\n"); meta_pop_no_msg_prefix (); ++r; } } #endif /* WITH_VERBOSE_MODE */ } void meta_workspace_manager_free_workspace_layout (MetaWorkspaceLayout *layout) { g_free (layout->grid); } static void queue_windows_showing (MetaWorkspaceManager *workspace_manager) { GSList *windows, *l; /* Must operate on all windows on display instead of just on the * active_workspace's window list, because the active_workspace's * window list may not contain the on_all_workspace windows. */ windows = meta_display_list_windows (workspace_manager->display, META_LIST_DEFAULT); for (l = windows; l; l = l->next) { MetaWindow *w = l->data; meta_window_queue (w, META_QUEUE_CALC_SHOWING); } g_slist_free (windows); } void meta_workspace_manager_minimize_all_on_active_workspace_except (MetaWorkspaceManager *workspace_manager, MetaWindow *keep) { GList *l; for (l = workspace_manager->active_workspace->windows; l; l = l->next) { MetaWindow *w = l->data; if (w->has_minimize_func && w != keep) meta_window_minimize (w); } } void meta_workspace_manager_show_desktop (MetaWorkspaceManager *workspace_manager, guint32 timestamp) { GList *l; if (workspace_manager->active_workspace->showing_desktop) return; workspace_manager->active_workspace->showing_desktop = TRUE; queue_windows_showing (workspace_manager); /* Focus the most recently used META_WINDOW_DESKTOP window, if there is one; * see bug 159257. */ for (l = workspace_manager->active_workspace->mru_list; l; l = l->next) { MetaWindow *w = l->data; if (w->type == META_WINDOW_DESKTOP) { meta_window_focus (w, timestamp); break; } } g_signal_emit (workspace_manager, workspace_manager_signals[SHOWING_DESKTOP_CHANGED], 0, NULL); } void meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager) { if (!workspace_manager->active_workspace->showing_desktop) return; workspace_manager->active_workspace->showing_desktop = FALSE; queue_windows_showing (workspace_manager); g_signal_emit (workspace_manager, workspace_manager_signals[SHOWING_DESKTOP_CHANGED], 0, NULL); } void meta_workspace_manager_toggle_desktop (MetaWorkspaceManager *workspace_manager, guint32 timestamp) { if (workspace_manager->active_workspace->showing_desktop) { meta_workspace_manager_unshow_desktop (workspace_manager); meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, timestamp); } else { meta_workspace_manager_show_desktop (workspace_manager, timestamp); } } /** * meta_workspace_manager_get_workspaces: (skip) * @workspace_manager: a #MetaWorkspaceManager * * Returns: (transfer none) (element-type Meta.Workspace): The workspaces for @display */ GList * meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager) { return workspace_manager->workspaces; } int meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager) { MetaWorkspace *active = workspace_manager->active_workspace; if (!active) return -1; return meta_workspace_index (active); } /** * meta_workspace_manager_get_active_workspace: * @workspace_manager: A #MetaWorkspaceManager * * Returns: (transfer none): The current workspace */ MetaWorkspace * meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager) { return workspace_manager->active_workspace; } void meta_workspace_manager_workspace_switched (MetaWorkspaceManager *workspace_manager, int from, int to, MetaMotionDirection direction) { g_signal_emit (workspace_manager, workspace_manager_signals[WORKSPACE_SWITCHED], 0, from, to, direction); } static void prefs_changed_callback (MetaPreference pref, gpointer data) { MetaWorkspaceManager *workspace_manager = data; if ((pref == META_PREF_NUM_WORKSPACES || pref == META_PREF_DYNAMIC_WORKSPACES) && !meta_prefs_get_dynamic_workspaces ()) { guint32 timestamp; int new_num; timestamp = meta_display_get_current_time_roundtrip (workspace_manager->display); new_num = meta_prefs_get_num_workspaces (); meta_workspace_manager_update_num_workspaces (workspace_manager, timestamp, new_num); } } muffin-6.4.1/src/core/meta-fraction.c0000664000175000017500000000601614723361714016361 0ustar fabiofabio/* * Copyright (C) 1999,2000 Erik Walthinsen * 2000 Wim Taymans * 2002 Thomas Vander Stichele * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301, USA. * * Fraction utility functions in this file comes from gstutils.c in gstreamer. */ #include "config.h" #include "core/meta-fraction.h" #include #include #define MAX_TERMS 30 #define MIN_DIVISOR 1.0e-10 #define MAX_ERROR 1.0e-20 static int greatest_common_divisor (int a, int b) { while (b != 0) { int temp = a; a = b; b = temp % b; } return ABS (a); } MetaFraction meta_fraction_from_double (double src) { double V, F; /* double being converted */ int N, D; /* will contain the result */ int A; /* current term in continued fraction */ int64_t N1, D1; /* numerator, denominator of last approx */ int64_t N2, D2; /* numerator, denominator of previous approx */ int i; int gcd; gboolean negative = FALSE; /* initialize fraction being converted */ F = src; if (F < 0.0) { F = -F; negative = TRUE; } V = F; /* initialize fractions with 1/0, 0/1 */ N1 = 1; D1 = 0; N2 = 0; D2 = 1; N = 1; D = 1; for (i = 0; i < MAX_TERMS; i++) { /* get next term */ A = (gint) F; /* no floor() needed, F is always >= 0 */ /* get new divisor */ F = F - A; /* calculate new fraction in temp */ N2 = N1 * A + N2; D2 = D1 * A + D2; /* guard against overflow */ if (N2 > G_MAXINT || D2 > G_MAXINT) break; N = N2; D = D2; /* save last two fractions */ N2 = N1; D2 = D1; N1 = N; D1 = D; /* quit if dividing by zero or close enough to target */ if (F < MIN_DIVISOR || fabs (V - ((gdouble) N) / D) < MAX_ERROR) break; /* Take reciprocal */ F = 1 / F; } /* fix for overflow */ if (D == 0) { N = G_MAXINT; D = 1; } /* fix for negative */ if (negative) N = -N; /* simplify */ gcd = greatest_common_divisor (N, D); if (gcd) { N /= gcd; D /= gcd; } return (MetaFraction) { .num = N, .denom = D }; } muffin-6.4.1/src/core/meta-inhibit-shortcuts-dialog.c0000664000175000017500000000646714723361714021505 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "core/window-private.h" #include "meta/meta-inhibit-shortcuts-dialog.h" #include "meta/meta-enum-types.h" enum { RESPONSE, LAST_SIGNAL }; static guint inhibit_dialog_signals[LAST_SIGNAL] = { 0, }; G_DEFINE_INTERFACE (MetaInhibitShortcutsDialog, meta_inhibit_shortcuts_dialog, G_TYPE_OBJECT) static void meta_inhibit_shortcuts_dialog_default_init (MetaInhibitShortcutsDialogInterface *iface) { g_object_interface_install_property (iface, g_param_spec_object ("window", "Window", "Window", META_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); inhibit_dialog_signals[RESPONSE] = g_signal_new ("response", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_INHIBIT_SHORTCUTS_DIALOG_RESPONSE); } /** * meta_inhibit_shortcuts_dialog_show: * @dialog: a #MetaInhibitShortcutsDialog * * Shows the inhibit shortcuts dialog. **/ void meta_inhibit_shortcuts_dialog_show (MetaInhibitShortcutsDialog *dialog) { MetaInhibitShortcutsDialogInterface *iface; g_return_if_fail (META_IS_INHIBIT_SHORTCUTS_DIALOG (dialog)); iface = META_INHIBIT_SHORTCUTS_DIALOG_GET_IFACE (dialog); iface->show (dialog); } /** * meta_inhibit_shortcuts_dialog_hide: * @dialog: a #MetaInhibitShortcutsDialog * * Hides the inhibit shortcuts dialog. **/ void meta_inhibit_shortcuts_dialog_hide (MetaInhibitShortcutsDialog *dialog) { MetaInhibitShortcutsDialogInterface *iface; g_return_if_fail (META_IS_INHIBIT_SHORTCUTS_DIALOG (dialog)); iface = META_INHIBIT_SHORTCUTS_DIALOG_GET_IFACE (dialog); iface->hide (dialog); } /** * meta_inhibit_shortcuts_dialog_response: * @dialog: a #MetaInhibitShortcutsDialog * @response: a #MetaInhibitShortcutsDialogResponse * * Responds and closes the dialog. To be called by #MetaInhibitShortcutsDialog * implementations. **/ void meta_inhibit_shortcuts_dialog_response (MetaInhibitShortcutsDialog *dialog, MetaInhibitShortcutsDialogResponse response) { g_signal_emit (dialog, inhibit_dialog_signals[RESPONSE], 0, response); meta_inhibit_shortcuts_dialog_hide (dialog); } muffin-6.4.1/src/core/boxes-private.h0000664000175000017500000003063514723361714016431 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Simple box operations */ /* * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BOXES_PRIVATE_H #define META_BOXES_PRIVATE_H #include #include "backends/meta-backend-types.h" #include "core/util-private.h" #include "meta/boxes.h" #include "meta/common.h" #define BOX_LEFT(box) ((box).x) /* Leftmost pixel of rect */ #define BOX_RIGHT(box) ((box).x + (box).width) /* One pixel past right */ #define BOX_TOP(box) ((box).y) /* Topmost pixel of rect */ #define BOX_BOTTOM(box) ((box).y + (box).height) /* One pixel past bottom */ typedef enum { FIXED_DIRECTION_NONE = 0, FIXED_DIRECTION_X = 1 << 0, FIXED_DIRECTION_Y = 1 << 1, } FixedDirections; typedef enum _MetaRoundingStrategy { META_ROUNDING_STRATEGY_SHRINK, META_ROUNDING_STRATEGY_GROW, META_ROUNDING_STRATEGY_ROUND, } MetaRoundingStrategy; /* Output functions -- note that the output buffer had better be big enough: * rect_to_string: RECT_LENGTH * region_to_string: (RECT_LENGTH+strlen(separator_string)) * * g_list_length (region) * edge_to_string: EDGE_LENGTH * edge_list_to_...: (EDGE_LENGTH+strlen(separator_string)) * * g_list_length (edge_list) */ #define RECT_LENGTH 27 #define EDGE_LENGTH 37 char* meta_rectangle_to_string (const MetaRectangle *rect, char *output); char* meta_rectangle_region_to_string (GList *region, const char *separator_string, char *output); char* meta_rectangle_edge_to_string (const MetaEdge *edge, char *output); char* meta_rectangle_edge_list_to_string ( GList *edge_list, const char *separator_string, char *output); /* Resize old_rect to the given new_width and new_height, but store the * result in rect. NOTE THAT THIS IS RESIZE ONLY SO IT CANNOT BE USED FOR * A MOVERESIZE OPERATION (that simplies the routine a little bit as it * means there's no difference between META_GRAVITY_NORTH_WEST and * META_GRAVITY_STATIC. Also, I lied a little bit--technically, you could use * it in a MoveResize operation if you muck with old_rect just right). */ META_EXPORT_TEST void meta_rectangle_resize_with_gravity (const MetaRectangle *old_rect, MetaRectangle *rect, MetaGravity gravity, int new_width, int new_height); /* find a list of rectangles with the property that a window is contained * in the given region if and only if it is contained in one of the * rectangles in the list. * * In this case, the region is given by taking basic_rect, removing from * it the intersections with all the rectangles in the all_struts list, * then expanding all the rectangles in the resulting list by the given * amounts on each side. * * See boxes.c for more details. */ META_EXPORT_TEST GList* meta_rectangle_get_minimal_spanning_set_for_region ( const MetaRectangle *basic_rect, const GSList *all_struts); /* Expand all rectangles in region by the given amount on each side */ GList* meta_rectangle_expand_region (GList *region, const int left_expand, const int right_expand, const int top_expand, const int bottom_expand); /* Same as for meta_rectangle_expand_region except that rectangles not at * least min_x or min_y in size are not expanded in that direction */ GList* meta_rectangle_expand_region_conditionally ( GList *region, const int left_expand, const int right_expand, const int top_expand, const int bottom_expand, const int min_x, const int min_y); /* Expand rect in direction to the size of expand_to, and then clip out any * overlapping struts oriented orthognal to the expansion direction. (Think * horizontal or vertical maximization) */ META_EXPORT_TEST void meta_rectangle_expand_to_avoiding_struts ( MetaRectangle *rect, const MetaRectangle *expand_to, const MetaDirection direction, const GSList *all_struts); /* Free the list created by * meta_rectangle_get_minimal_spanning_set_for_region() * or * meta_rectangle_find_onscreen_edges () * or * meta_rectangle_find_nonintersected_monitor_edges() */ META_EXPORT_TEST void meta_rectangle_free_list_and_elements (GList *filled_list); /* could_fit_in_region determines whether one of the spanning_rects is * big enough to contain rect. contained_in_region checks whether one * actually contains it. */ META_EXPORT_TEST gboolean meta_rectangle_could_fit_in_region ( const GList *spanning_rects, const MetaRectangle *rect); META_EXPORT_TEST gboolean meta_rectangle_contained_in_region ( const GList *spanning_rects, const MetaRectangle *rect); META_EXPORT_TEST gboolean meta_rectangle_overlaps_with_region ( const GList *spanning_rects, const MetaRectangle *rect); gboolean meta_rectangle_has_adjacent_in_region ( const GList *spanning_rects, const MetaRectangle *rect); /* Make the rectangle small enough to fit into one of the spanning_rects, * but make it no smaller than min_size. */ META_EXPORT_TEST void meta_rectangle_clamp_to_fit_into_region ( const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect, const MetaRectangle *min_size); /* Clip the rectangle so that it fits into one of the spanning_rects, assuming * it overlaps with at least one of them */ META_EXPORT_TEST void meta_rectangle_clip_to_region (const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect); /* Shove the rectangle into one of the spanning_rects, assuming it fits in * one of them. */ META_EXPORT_TEST void meta_rectangle_shove_into_region( const GList *spanning_rects, FixedDirections fixed_directions, MetaRectangle *rect); /* Finds the point on the line connecting (x1,y1) to (x2,y2) which is closest * to (px, py). Useful for finding an optimal rectangle size when given a * range between two sizes that are all candidates. */ META_EXPORT_TEST void meta_rectangle_find_linepoint_closest_to_point (double x1, double y1, double x2, double y2, double px, double py, double *valx, double *valy); /***************************************************************************/ /* */ /* Switching gears to code for edges instead of just rectangles */ /* */ /***************************************************************************/ /* Return whether an edge overlaps or is adjacent to the rectangle in the * nonzero-width dimension of the edge. */ META_EXPORT_TEST gboolean meta_rectangle_edge_aligns (const MetaRectangle *rect, const MetaEdge *edge); /* Compare two edges, so that sorting functions can put a list of edges in * canonical order. */ META_EXPORT_TEST gint meta_rectangle_edge_cmp (gconstpointer a, gconstpointer b); /* Compare two edges, so that sorting functions can put a list of edges in * order. This function doesn't separate left edges first, then right edges, * etc., but rather compares only upon location. */ META_EXPORT_TEST gint meta_rectangle_edge_cmp_ignore_type (gconstpointer a, gconstpointer b); /* Removes an parts of edges in the given list that intersect any box in the * given rectangle list. Returns the result. */ GList* meta_rectangle_remove_intersections_with_boxes_from_edges ( GList *edges, const GSList *rectangles); /* Finds all the edges of an onscreen region, returning a GList* of * MetaEdgeRect's. */ META_EXPORT_TEST GList* meta_rectangle_find_onscreen_edges (const MetaRectangle *basic_rect, const GSList *all_struts); /* Finds edges between adjacent monitors which are not covered by the given * struts. */ META_EXPORT_TEST GList* meta_rectangle_find_nonintersected_monitor_edges ( const GList *monitor_rects, const GSList *all_struts); META_EXPORT_TEST gboolean meta_rectangle_is_adjacent_to (MetaRectangle *rect, MetaRectangle *other); META_EXPORT_TEST void meta_rectangle_scale_double (const MetaRectangle *rect, double scale, MetaRoundingStrategy rounding_strategy, MetaRectangle *dest); static inline graphene_rect_t meta_rectangle_to_graphene_rect (MetaRectangle *rect) { return (graphene_rect_t) { .origin = { .x = rect->x, .y = rect->y }, .size = { .width = rect->width, .height = rect->height } }; } META_EXPORT_TEST void meta_rectangle_transform (const MetaRectangle *rect, MetaMonitorTransform transform, int width, int height, MetaRectangle *dest); void meta_rectangle_from_graphene_rect (const graphene_rect_t *rect, MetaRoundingStrategy rounding_strategy, MetaRectangle *dest); void meta_rectangle_crop_and_scale (const MetaRectangle *rect, graphene_rect_t *src_rect, int dst_width, int dst_height, MetaRectangle *dest); #endif /* META_BOXES_PRIVATE_H */ muffin-6.4.1/src/core/startup-notification-private.h0000664000175000017500000000413714723361714021475 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_STARTUP_NOTIFICATION_PRIVATE_H #define META_STARTUP_NOTIFICATION_PRIVATE_H #include "core/display-private.h" #include "meta/meta-startup-notification.h" struct _MetaStartupSequenceClass { GObjectClass parent_class; void (* complete) (MetaStartupSequence *sequence); }; MetaStartupNotification * meta_startup_notification_new (MetaDisplay *display); gboolean meta_startup_notification_handle_xevent (MetaStartupNotification *sn, XEvent *xevent); void meta_startup_notification_add_sequence (MetaStartupNotification *sn, MetaStartupSequence *seq); void meta_startup_notification_remove_sequence (MetaStartupNotification *sn, MetaStartupSequence *seq); MetaStartupSequence * meta_startup_notification_lookup_sequence (MetaStartupNotification *sn, const gchar *id); #endif /* META_STARTUP_NOTIFICATION_PRIVATE_H */ muffin-6.4.1/src/core/keybindings.c0000664000175000017500000047717614723361714016161 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter Keybindings */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:keybindings * @Title: MetaKeybinding * @Short_Description: Key bindings */ #include "config.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/x11/meta-backend-x11.h" #include "compositor/compositor-private.h" #include "core/edge-resistance.h" #include "core/frame.h" #include "core/keybindings-private.h" #include "core/meta-accel-parse.h" #include "core/meta-workspace-manager-private.h" #include "core/workspace-private.h" #include "meta/compositor.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #ifdef __linux__ #include #elif !defined KEY_GRAVE #define KEY_GRAVE 0x29 /* assume the use of xf86-input-keyboard */ #endif #define SCHEMA_COMMON_KEYBINDINGS "org.cinnamon.desktop.keybindings.wm" #define SCHEMA_MUTTER_KEYBINDINGS "org.cinnamon.muffin.keybindings" #define SCHEMA_MUTTER_WAYLAND_KEYBINDINGS "org.cinnamon.muffin.wayland.keybindings" #define META_KEY_BINDING_PRIMARY_LAYOUT 0 #define META_KEY_BINDING_SECONDARY_LAYOUT 1 /* Only for special modifier keys */ #define IGNORED_MODIFIERS (CLUTTER_LOCK_MASK | \ CLUTTER_MOD2_MASK | \ CLUTTER_BUTTON1_MASK | \ CLUTTER_BUTTON2_MASK | \ CLUTTER_BUTTON3_MASK | \ CLUTTER_BUTTON4_MASK | \ CLUTTER_BUTTON5_MASK) static gboolean modifier_key_only_pressed = FALSE; static gboolean add_builtin_keybinding (MetaDisplay *display, const char *name, GSettings *settings, MetaKeyBindingFlags flags, MetaKeyBindingAction action, MetaKeyHandlerFunc handler, int handler_arg); static void resolved_key_combo_reset (MetaResolvedKeyCombo *resolved_combo) { g_free (resolved_combo->keycodes); resolved_combo->len = 0; resolved_combo->keycodes = NULL; } static void resolved_key_combo_copy (MetaResolvedKeyCombo *from, MetaResolvedKeyCombo *to) { to->len = from->len; to->keycodes = g_memdup2 (from->keycodes, from->len * sizeof (xkb_keycode_t)); } static gboolean resolved_key_combo_has_keycode (MetaResolvedKeyCombo *resolved_combo, int keycode) { int i; for (i = 0; i < resolved_combo->len; i++) if ((int) resolved_combo->keycodes[i] == keycode) return TRUE; return FALSE; } static gboolean resolved_key_combo_intersect (MetaResolvedKeyCombo *a, MetaResolvedKeyCombo *b) { int i; for (i = 0; i < a->len; i++) if (resolved_key_combo_has_keycode (b, a->keycodes[i])) return TRUE; return FALSE; } static void meta_key_binding_free (MetaKeyBinding *binding) { resolved_key_combo_reset (&binding->resolved_combo); g_slice_free (MetaKeyBinding, binding); } static MetaKeyBinding * meta_key_binding_copy (MetaKeyBinding *binding) { MetaKeyBinding *clone = g_slice_dup (MetaKeyBinding, binding); resolved_key_combo_copy (&binding->resolved_combo, &clone->resolved_combo); return clone; } G_DEFINE_BOXED_TYPE(MetaKeyBinding, meta_key_binding, meta_key_binding_copy, meta_key_binding_free) const char * meta_key_binding_get_name (MetaKeyBinding *binding) { return binding->name; } MetaVirtualModifier meta_key_binding_get_modifiers (MetaKeyBinding *binding) { return binding->combo.modifiers; } gboolean meta_key_binding_is_reversed (MetaKeyBinding *binding) { return (binding->handler->flags & META_KEY_BINDING_IS_REVERSED) != 0; } guint meta_key_binding_get_mask (MetaKeyBinding *binding) { return binding->resolved_combo.mask; } gboolean meta_key_binding_is_builtin (MetaKeyBinding *binding) { return binding->handler->flags & META_KEY_BINDING_BUILTIN; } /* These can't be bound to anything, but they are used to handle * various other events. TODO: Possibly we should include them as event * handler functions and have some kind of flag to say they're unbindable. */ static gboolean process_mouse_move_resize_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event); static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event); static gboolean process_keyboard_resize_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event); static GHashTable *key_handlers; static GHashTable *external_grabs; #define HANDLER(name) g_hash_table_lookup (key_handlers, (name)) static void key_handler_free (MetaKeyHandler *handler) { g_free (handler->name); if (handler->user_data_free_func && handler->user_data) handler->user_data_free_func (handler->user_data); g_free (handler); } typedef struct _MetaKeyGrab MetaKeyGrab; struct _MetaKeyGrab { char *name; guint action; MetaKeyCombo combo; gint flags; }; static void meta_key_grab_free (MetaKeyGrab *grab) { g_free (grab->name); g_free (grab); } static guint32 key_combo_key (MetaResolvedKeyCombo *resolved_combo, int i) { /* On X, keycodes are only 8 bits while libxkbcommon supports 32 bit keycodes, but since we're using the same XKB keymaps that X uses, we won't find keycodes bigger than 8 bits in practice. The bits that mutter cares about in the modifier mask are also all in the lower 8 bits both on X and clutter key events. This means that we can use a 32 bit integer to safely concatenate both keycode and mask and thus making it easy to use them as an index in a GHashTable. */ guint32 key = resolved_combo->keycodes[i] & 0xffff; return (key << 16) | (resolved_combo->mask & 0xffff); } static void reload_modmap (MetaKeyBindingManager *keys) { struct xkb_keymap *keymap = meta_backend_get_keymap (keys->backend); struct xkb_state *scratch_state; xkb_mod_mask_t scroll_lock_mask; xkb_mod_mask_t dummy_mask; /* Modifiers to find. */ struct { const char *name; xkb_mod_mask_t *mask_p; xkb_mod_mask_t *virtual_mask_p; } mods[] = { { "ScrollLock", &scroll_lock_mask, &dummy_mask }, { "Meta", &keys->meta_mask, &keys->virtual_meta_mask }, { "Hyper", &keys->hyper_mask, &keys->virtual_hyper_mask }, { "Super", &keys->super_mask, &keys->virtual_super_mask }, }; scratch_state = xkb_state_new (keymap); gsize i; for (i = 0; i < G_N_ELEMENTS (mods); i++) { xkb_mod_mask_t *mask_p = mods[i].mask_p; xkb_mod_mask_t *virtual_mask_p = mods[i].virtual_mask_p; xkb_mod_index_t idx = xkb_keymap_mod_get_index (keymap, mods[i].name); if (idx != XKB_MOD_INVALID) { xkb_mod_mask_t vmodmask = (1 << idx); xkb_state_update_mask (scratch_state, vmodmask, 0, 0, 0, 0, 0); *mask_p = xkb_state_serialize_mods (scratch_state, XKB_STATE_MODS_DEPRESSED) & ~vmodmask; *virtual_mask_p = vmodmask; } else { *mask_p = 0; *virtual_mask_p = 0; } } xkb_state_unref (scratch_state); keys->ignored_modifier_mask = (scroll_lock_mask | Mod2Mask | LockMask); meta_topic (META_DEBUG_KEYBINDINGS, "Ignoring modmask 0x%x scroll lock 0x%x hyper 0x%x super 0x%x meta 0x%x\n", keys->ignored_modifier_mask, scroll_lock_mask, keys->hyper_mask, keys->super_mask, keys->meta_mask); } static gboolean is_keycode_for_keysym (struct xkb_keymap *keymap, xkb_layout_index_t layout, xkb_level_index_t level, xkb_keycode_t keycode, xkb_keysym_t keysym) { const xkb_keysym_t *syms; int num_syms, k; num_syms = xkb_keymap_key_get_syms_by_level (keymap, keycode, layout, level, &syms); for (k = 0; k < num_syms; k++) { if (syms[k] == keysym) return TRUE; } return FALSE; } typedef struct { GArray *keycodes; xkb_keysym_t keysym; xkb_layout_index_t layout; xkb_level_index_t level; } FindKeysymData; static void get_keycodes_for_keysym_iter (struct xkb_keymap *keymap, xkb_keycode_t keycode, void *data) { FindKeysymData *search_data = data; GArray *keycodes = search_data->keycodes; xkb_keysym_t keysym = search_data->keysym; xkb_layout_index_t layout = search_data->layout; xkb_level_index_t level = search_data->level; if (is_keycode_for_keysym (keymap, layout, level, keycode, keysym)) { guint i; gboolean missing = TRUE; /* duplicate keycode detection */ for (i = 0; i < keycodes->len; i++) if (g_array_index (keycodes, xkb_keysym_t, i) == keycode) { missing = FALSE; break; } if (missing) g_array_append_val (keycodes, keycode); } } static void add_keysym_keycodes_from_layout (int keysym, MetaKeyBindingKeyboardLayout *layout, GArray *keycodes) { xkb_level_index_t layout_level; for (layout_level = 0; layout_level < layout->n_levels && keycodes->len == 0; layout_level++) { FindKeysymData search_data = (FindKeysymData) { .keycodes = keycodes, .keysym = keysym, .layout = layout->index, .level = layout_level }; xkb_keymap_key_for_each (layout->keymap, get_keycodes_for_keysym_iter, &search_data); } } /* Original code from gdk_x11_keymap_get_entries_for_keyval() in * gdkkeys-x11.c */ static void get_keycodes_for_keysym (MetaKeyBindingManager *keys, int keysym, MetaResolvedKeyCombo *resolved_combo) { unsigned int i; GArray *keycodes; int keycode; keycodes = g_array_new (FALSE, FALSE, sizeof (xkb_keysym_t)); /* Special-case: Fake mutter keysym */ if (keysym == META_KEY_ABOVE_TAB) { keycode = KEY_GRAVE + 8; g_array_append_val (keycodes, keycode); goto out; } for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++) { MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i]; if (!layout->keymap) continue; add_keysym_keycodes_from_layout (keysym, layout, keycodes); } out: resolved_combo->len = keycodes->len; resolved_combo->keycodes = (xkb_keycode_t *) g_array_free (keycodes, keycodes->len == 0 ? TRUE : FALSE); } typedef struct _CalculateLayoutLevelsState { struct xkb_keymap *keymap; xkb_layout_index_t layout_index; xkb_level_index_t out_n_levels; } CalculateLayoutLevelState; static void calculate_n_layout_levels_iter (struct xkb_keymap *keymap, xkb_keycode_t keycode, void *data) { CalculateLayoutLevelState *state = data; xkb_level_index_t n_levels; n_levels = xkb_keymap_num_levels_for_key (keymap, keycode, state->layout_index); state->out_n_levels = MAX (n_levels, state->out_n_levels); } static xkb_level_index_t calculate_n_layout_levels (struct xkb_keymap *keymap, xkb_layout_index_t layout_index) { CalculateLayoutLevelState state = { .keymap = keymap, .layout_index = layout_index, .out_n_levels = 0 }; xkb_keymap_key_for_each (keymap, calculate_n_layout_levels_iter, &state); return state.out_n_levels; } static void reload_iso_next_group_combos (MetaKeyBindingManager *keys) { const char *iso_next_group_option; int i; for (i = 0; i < keys->n_iso_next_group_combos; i++) resolved_key_combo_reset (&keys->iso_next_group_combo[i]); keys->n_iso_next_group_combos = 0; iso_next_group_option = meta_prefs_get_iso_next_group_option (); if (iso_next_group_option == NULL) return; get_keycodes_for_keysym (keys, XKB_KEY_ISO_Next_Group, keys->iso_next_group_combo); if (keys->iso_next_group_combo[0].len == 0) return; keys->n_iso_next_group_combos = 1; if (g_str_equal (iso_next_group_option, "toggle") || g_str_equal (iso_next_group_option, "lalt_toggle") || g_str_equal (iso_next_group_option, "lwin_toggle") || g_str_equal (iso_next_group_option, "rwin_toggle") || g_str_equal (iso_next_group_option, "lshift_toggle") || g_str_equal (iso_next_group_option, "rshift_toggle") || g_str_equal (iso_next_group_option, "lctrl_toggle") || g_str_equal (iso_next_group_option, "rctrl_toggle") || g_str_equal (iso_next_group_option, "sclk_toggle") || g_str_equal (iso_next_group_option, "menu_toggle") || g_str_equal (iso_next_group_option, "caps_toggle")) { keys->iso_next_group_combo[0].mask = 0; } else if (g_str_equal (iso_next_group_option, "shift_caps_toggle") || g_str_equal (iso_next_group_option, "shifts_toggle")) { keys->iso_next_group_combo[0].mask = ShiftMask; } else if (g_str_equal (iso_next_group_option, "alt_caps_toggle") || g_str_equal (iso_next_group_option, "alt_space_toggle")) { keys->iso_next_group_combo[0].mask = Mod1Mask; } else if (g_str_equal (iso_next_group_option, "ctrl_shift_toggle") || g_str_equal (iso_next_group_option, "lctrl_lshift_toggle") || g_str_equal (iso_next_group_option, "rctrl_rshift_toggle")) { resolved_key_combo_copy (&keys->iso_next_group_combo[0], &keys->iso_next_group_combo[1]); keys->iso_next_group_combo[0].mask = ShiftMask; keys->iso_next_group_combo[1].mask = ControlMask; keys->n_iso_next_group_combos = 2; } else if (g_str_equal (iso_next_group_option, "ctrl_alt_toggle")) { resolved_key_combo_copy (&keys->iso_next_group_combo[0], &keys->iso_next_group_combo[1]); keys->iso_next_group_combo[0].mask = Mod1Mask; keys->iso_next_group_combo[1].mask = ControlMask; keys->n_iso_next_group_combos = 2; } else if (g_str_equal (iso_next_group_option, "alt_shift_toggle") || g_str_equal (iso_next_group_option, "lalt_lshift_toggle")) { resolved_key_combo_copy (&keys->iso_next_group_combo[0], &keys->iso_next_group_combo[1]); keys->iso_next_group_combo[0].mask = Mod1Mask; keys->iso_next_group_combo[1].mask = ShiftMask; keys->n_iso_next_group_combos = 2; } else { resolved_key_combo_reset (keys->iso_next_group_combo); keys->n_iso_next_group_combos = 0; } } static void devirtualize_modifiers (MetaKeyBindingManager *keys, MetaVirtualModifier modifiers, unsigned int *mask) { *mask = 0; if (modifiers & META_VIRTUAL_SHIFT_MASK) *mask |= ShiftMask; if (modifiers & META_VIRTUAL_CONTROL_MASK) *mask |= ControlMask; if (modifiers & META_VIRTUAL_ALT_MASK) *mask |= Mod1Mask; if (modifiers & META_VIRTUAL_META_MASK) *mask |= keys->meta_mask; if (modifiers & META_VIRTUAL_HYPER_MASK) *mask |= keys->hyper_mask; if (modifiers & META_VIRTUAL_SUPER_MASK) *mask |= keys->super_mask; if (modifiers & META_VIRTUAL_MOD2_MASK) *mask |= Mod2Mask; if (modifiers & META_VIRTUAL_MOD3_MASK) *mask |= Mod3Mask; if (modifiers & META_VIRTUAL_MOD4_MASK) *mask |= Mod4Mask; if (modifiers & META_VIRTUAL_MOD5_MASK) *mask |= Mod5Mask; } static void index_binding (MetaKeyBindingManager *keys, MetaKeyBinding *binding) { int i; for (i = 0; i < binding->resolved_combo.len; i++) { MetaKeyBinding *existing; guint32 index_key; index_key = key_combo_key (&binding->resolved_combo, i); existing = g_hash_table_lookup (keys->key_bindings_index, GINT_TO_POINTER (index_key)); if (existing != NULL) { /* Overwrite already indexed keycodes only for the first * keycode, i.e. we give those primary keycodes precedence * over non-first ones. */ if (i > 0) continue; meta_warning ("%s - Overwriting existing binding of keysym %x" " with keysym %x (keycode %x).\n", binding->name, binding->combo.keysym, existing->combo.keysym, binding->resolved_combo.keycodes[i]); } g_hash_table_replace (keys->key_bindings_index, GINT_TO_POINTER (index_key), binding); } } static void resolve_key_combo (MetaKeyBindingManager *keys, MetaKeyCombo *combo, MetaResolvedKeyCombo *resolved_combo) { resolved_key_combo_reset (resolved_combo); if (combo->keysym != 0) { get_keycodes_for_keysym (keys, combo->keysym, resolved_combo); } else if (combo->keycode != 0) { resolved_combo->keycodes = g_new0 (xkb_keycode_t, 1); resolved_combo->keycodes[0] = combo->keycode; resolved_combo->len = 1; } devirtualize_modifiers (keys, combo->modifiers, &resolved_combo->mask); } static void binding_reload_combos_foreach (gpointer key, gpointer value, gpointer data) { MetaKeyBindingManager *keys = data; MetaKeyBinding *binding = value; resolve_key_combo (keys, &binding->combo, &binding->resolved_combo); index_binding (keys, binding); } typedef struct _FindLatinKeysymsState { MetaKeyBindingKeyboardLayout *layout; gboolean *required_keysyms_found; int n_required_keysyms; } FindLatinKeysymsState; static void find_latin_keysym (struct xkb_keymap *keymap, xkb_keycode_t key, void *data) { FindLatinKeysymsState *state = data; int n_keysyms, i; const xkb_keysym_t *keysyms; n_keysyms = xkb_keymap_key_get_syms_by_level (state->layout->keymap, key, state->layout->index, 0, &keysyms); for (i = 0; i < n_keysyms; i++) { xkb_keysym_t keysym = keysyms[i]; if (keysym >= XKB_KEY_a && keysym <= XKB_KEY_z) { unsigned int keysym_index = keysym - XKB_KEY_a; if (!state->required_keysyms_found[keysym_index]) { state->required_keysyms_found[keysym_index] = TRUE; state->n_required_keysyms--; } } } } static gboolean needs_secondary_layout (MetaKeyBindingKeyboardLayout *layout) { gboolean required_keysyms_found[] = { FALSE, /* XKB_KEY_a */ FALSE, /* XKB_KEY_b */ FALSE, /* XKB_KEY_c */ FALSE, /* XKB_KEY_d */ FALSE, /* XKB_KEY_e */ FALSE, /* XKB_KEY_f */ FALSE, /* XKB_KEY_g */ FALSE, /* XKB_KEY_h */ FALSE, /* XKB_KEY_i */ FALSE, /* XKB_KEY_j */ FALSE, /* XKB_KEY_k */ FALSE, /* XKB_KEY_l */ FALSE, /* XKB_KEY_m */ FALSE, /* XKB_KEY_n */ FALSE, /* XKB_KEY_o */ FALSE, /* XKB_KEY_p */ FALSE, /* XKB_KEY_q */ FALSE, /* XKB_KEY_r */ FALSE, /* XKB_KEY_s */ FALSE, /* XKB_KEY_t */ FALSE, /* XKB_KEY_u */ FALSE, /* XKB_KEY_v */ FALSE, /* XKB_KEY_w */ FALSE, /* XKB_KEY_x */ FALSE, /* XKB_KEY_y */ FALSE, /* XKB_KEY_z */ }; FindLatinKeysymsState state = { .layout = layout, .required_keysyms_found = required_keysyms_found, .n_required_keysyms = G_N_ELEMENTS (required_keysyms_found), }; xkb_keymap_key_for_each (layout->keymap, find_latin_keysym, &state); return state.n_required_keysyms != 0; } static void clear_active_keyboard_layouts (MetaKeyBindingManager *keys) { unsigned int i; for (i = 0; i < G_N_ELEMENTS (keys->active_layouts); i++) { MetaKeyBindingKeyboardLayout *layout = &keys->active_layouts[i]; g_clear_pointer (&layout->keymap, xkb_keymap_unref); *layout = (MetaKeyBindingKeyboardLayout) { 0 }; } } static MetaKeyBindingKeyboardLayout create_us_layout (void) { struct xkb_rule_names names; struct xkb_keymap *keymap; struct xkb_context *context; names.rules = DEFAULT_XKB_RULES_FILE; names.model = DEFAULT_XKB_MODEL; names.layout = "us"; names.variant = ""; names.options = ""; context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); xkb_context_unref (context); return (MetaKeyBindingKeyboardLayout) { .keymap = keymap, .n_levels = calculate_n_layout_levels (keymap, 0), }; } static void reload_active_keyboard_layouts (MetaKeyBindingManager *keys) { struct xkb_keymap *keymap; xkb_layout_index_t layout_index; MetaKeyBindingKeyboardLayout primary_layout; clear_active_keyboard_layouts (keys); keymap = meta_backend_get_keymap (keys->backend); layout_index = meta_backend_get_keymap_layout_group (keys->backend); primary_layout = (MetaKeyBindingKeyboardLayout) { .keymap = xkb_keymap_ref (keymap), .index = layout_index, .n_levels = calculate_n_layout_levels (keymap, layout_index), }; keys->active_layouts[META_KEY_BINDING_PRIMARY_LAYOUT] = primary_layout; if (needs_secondary_layout (&primary_layout)) { MetaKeyBindingKeyboardLayout us_layout; us_layout = create_us_layout (); keys->active_layouts[META_KEY_BINDING_SECONDARY_LAYOUT] = us_layout; } } static void reload_combos (MetaKeyBindingManager *keys) { g_hash_table_remove_all (keys->key_bindings_index); reload_active_keyboard_layouts (keys); resolve_key_combo (keys, &keys->overlay_key_combo, &keys->overlay_resolved_key_combo); resolve_key_combo (keys, &keys->locate_pointer_key_combo, &keys->locate_pointer_resolved_key_combo); reload_iso_next_group_combos (keys); g_hash_table_foreach (keys->key_bindings, binding_reload_combos_foreach, keys); } static void rebuild_binding_table (MetaKeyBindingManager *keys, GList *prefs, GList *grabs) { MetaKeyBinding *b; GList *p, *g; g_hash_table_remove_all (keys->key_bindings); p = prefs; while (p) { MetaKeyPref *pref = (MetaKeyPref*)p->data; GSList *tmp = pref->combos; while (tmp) { MetaKeyCombo *combo = tmp->data; if (combo && (combo->keysym != None || combo->keycode != 0)) { MetaKeyHandler *handler = HANDLER (pref->name); b = g_slice_new0 (MetaKeyBinding); b->name = pref->name; b->handler = handler; b->flags = handler->flags; b->combo = *combo; g_hash_table_add (keys->key_bindings, b); } tmp = tmp->next; } p = p->next; } g = grabs; while (g) { MetaKeyGrab *grab = (MetaKeyGrab*)g->data; if (grab->combo.keysym != None || grab->combo.keycode != 0) { MetaKeyHandler *handler = HANDLER ("external-grab"); b = g_slice_new0 (MetaKeyBinding); b->name = grab->name; b->handler = handler; b->flags = grab->flags; b->combo = grab->combo; g_hash_table_add (keys->key_bindings, b); } g = g->next; } meta_topic (META_DEBUG_KEYBINDINGS, " %d bindings in table\n", g_hash_table_size (keys->key_bindings)); } static void rebuild_key_binding_table (MetaKeyBindingManager *keys) { GList *prefs, *grabs; meta_topic (META_DEBUG_KEYBINDINGS, "Rebuilding key binding table from preferences\n"); prefs = meta_prefs_get_keybindings (); grabs = g_hash_table_get_values (external_grabs); rebuild_binding_table (keys, prefs, grabs); g_list_free (prefs); g_list_free (grabs); } static void rebuild_special_bindings (MetaKeyBindingManager *keys) { MetaKeyCombo combo; meta_prefs_get_overlay_binding (&combo); keys->overlay_key_combo = combo; meta_prefs_get_locate_pointer_binding (&combo); keys->locate_pointer_key_combo = combo; } static void ungrab_key_bindings (MetaDisplay *display) { GSList *windows, *l; if (display->x11_display) meta_x11_display_ungrab_keys (display->x11_display); windows = meta_display_list_windows (display, META_LIST_DEFAULT); for (l = windows; l; l = l->next) { MetaWindow *w = l->data; meta_window_ungrab_keys (w); } g_slist_free (windows); } static void grab_key_bindings (MetaDisplay *display) { GSList *windows, *l; if (display->x11_display) meta_x11_display_grab_keys (display->x11_display); windows = meta_display_list_windows (display, META_LIST_DEFAULT); for (l = windows; l; l = l->next) { MetaWindow *w = l->data; meta_window_grab_keys (w); } g_slist_free (windows); } static MetaKeyBinding * get_keybinding (MetaKeyBindingManager *keys, MetaResolvedKeyCombo *resolved_combo) { MetaKeyBinding *binding = NULL; int i; for (i = 0; i < resolved_combo->len; i++) { guint32 key; key = key_combo_key (resolved_combo, i); binding = g_hash_table_lookup (keys->key_bindings_index, GINT_TO_POINTER (key)); if (binding != NULL) break; } return binding; } static guint next_dynamic_keybinding_action (void) { static guint num_dynamic_bindings = 0; return META_KEYBINDING_ACTION_LAST + (++num_dynamic_bindings); } static gboolean add_keybinding_internal (MetaDisplay *display, const char *name, GSettings *settings, const char **bindings, MetaKeyBindingFlags flags, MetaKeyBindingAction action, MetaKeyHandlerFunc func, int data, gpointer user_data, GDestroyNotify free_data) { MetaKeyHandler *handler; if (!meta_prefs_add_keybinding (name, settings, bindings, action, flags)) return FALSE; handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup (name); handler->func = func; handler->default_func = func; handler->data = data; handler->flags = flags; handler->user_data = user_data; handler->user_data_free_func = free_data; g_hash_table_insert (key_handlers, g_strdup (name), handler); return TRUE; } static gboolean add_builtin_keybinding (MetaDisplay *display, const char *name, GSettings *settings, MetaKeyBindingFlags flags, MetaKeyBindingAction action, MetaKeyHandlerFunc handler, int handler_arg) { return add_keybinding_internal (display, name, settings, NULL, flags | META_KEY_BINDING_BUILTIN, action, handler, handler_arg, NULL, NULL); } /** * meta_display_add_keybinding: * @display: a #MetaDisplay * @name: the binding's name * @settings: the #GSettings object where @name is stored * @flags: flags to specify binding details * @handler: function to run when the keybinding is invoked * @user_data: the data to pass to @handler * @free_data: function to free @user_data * * Add a keybinding at runtime. The key @name in @schema needs to be of * type %G_VARIANT_TYPE_STRING_ARRAY, with each string describing a * keybinding in the form of "<Control>a" or "<Shift><Alt>F1". The parser * is fairly liberal and allows lower or upper case, and also abbreviations * such as "<Ctl>" and "<Ctrl>". If the key is set to the empty list or a * list with a single element of either "" or "disabled", the keybinding is * disabled. * * Use meta_display_remove_keybinding() to remove the binding. * * Returns: the corresponding keybinding action if the keybinding was * added successfully, otherwise %META_KEYBINDING_ACTION_NONE */ guint meta_display_add_keybinding (MetaDisplay *display, const char *name, GSettings *settings, MetaKeyBindingFlags flags, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data) { guint new_action = next_dynamic_keybinding_action (); if (!add_keybinding_internal (display, name, settings, NULL, flags, new_action, handler, 0, user_data, free_data)) return META_KEYBINDING_ACTION_NONE; return new_action; } /** * meta_display_add_custom_keybinding: * @display: a #MetaDisplay * @name: the binding's unique name * @bindings: (allow-none) (array zero-terminated=1): array of parseable keystrokes * @callback: function to run when the keybinding is invoked * @user_data: the data to pass to @handler * @free_data: function to free @user_data * * * Use meta_display_remove_custom_keybinding() to remove the binding. * * Returns: %TRUE if the keybinding was added successfully, * otherwise %FALSE */ guint meta_display_add_custom_keybinding (MetaDisplay *display, const char *name, const char **bindings, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data) { guint new_action = next_dynamic_keybinding_action (); if (!add_keybinding_internal (display, name, NULL, bindings, META_KEY_BINDING_NONE, new_action, handler, 0, user_data, free_data)) return META_KEYBINDING_ACTION_NONE; return new_action; } /** * meta_display_remove_keybinding: * @display: the #MetaDisplay * @name: name of the keybinding to remove * * Remove keybinding @name; the function will fail if @name is not a known * keybinding or has not been added with meta_display_add_keybinding(). * * Returns: %TRUE if the binding has been removed sucessfully, * otherwise %FALSE */ gboolean meta_display_remove_keybinding (MetaDisplay *display, const char *name) { if (!meta_prefs_remove_keybinding (name)) return FALSE; g_hash_table_remove (key_handlers, name); return TRUE; } guint get_keybinding_action (MetaKeyBindingManager *keys, MetaResolvedKeyCombo *resolved_combo) { MetaKeyBinding *binding; binding = get_keybinding (keys, resolved_combo); if (binding) { MetaKeyGrab *grab = g_hash_table_lookup (external_grabs, binding->name); if (grab) return grab->action; else return (guint) meta_prefs_get_keybinding_action (binding->name); } else { return META_KEYBINDING_ACTION_NONE; } } static xkb_mod_mask_t mask_from_event_params (MetaKeyBindingManager *keys, unsigned long mask) { return mask & 0xff & ~keys->ignored_modifier_mask; } /** * meta_display_get_keybinding_action: * @display: A #MetaDisplay * @keycode: Raw keycode * @mask: Event mask * * Get the keybinding action bound to @keycode. Builtin keybindings * have a fixed associated #MetaKeyBindingAction, for bindings added * dynamically the function will return the keybinding action * meta_display_add_keybinding() returns on registration. * * Returns: The action that should be taken for the given key, or * %META_KEYBINDING_ACTION_NONE. */ guint meta_display_get_keybinding_action (MetaDisplay *display, unsigned int keycode, unsigned long mask) { MetaKeyBindingManager *keys = &display->key_binding_manager; xkb_keycode_t code = (xkb_keycode_t) keycode; MetaResolvedKeyCombo resolved_combo = { &code, 1 }; resolved_combo.mask = mask_from_event_params (keys, mask); return get_keybinding_action (keys, &resolved_combo); } static void reload_keybindings (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; ungrab_key_bindings (display); /* Deciphering the modmap depends on the loaded keysyms to find out * what modifiers is Super and so forth, so we need to reload it * even when only the keymap changes */ reload_modmap (keys); reload_combos (keys); grab_key_bindings (display); } static GArray * calc_grab_modifiers (MetaKeyBindingManager *keys, unsigned int modmask) { unsigned int ignored_mask; XIGrabModifiers mods; GArray *mods_array = g_array_new (FALSE, TRUE, sizeof (XIGrabModifiers)); /* The X server crashes if XIAnyModifier gets passed in with any other bits. It doesn't make sense to ask for a grab of XIAnyModifier plus other bits anyway so we avoid that. */ if (modmask & XIAnyModifier) { mods = (XIGrabModifiers) { XIAnyModifier, 0 }; g_array_append_val (mods_array, mods); return mods_array; } mods = (XIGrabModifiers) { modmask, 0 }; g_array_append_val (mods_array, mods); for (ignored_mask = 1; ignored_mask <= keys->ignored_modifier_mask; ++ignored_mask) { if (ignored_mask & keys->ignored_modifier_mask) { mods = (XIGrabModifiers) { modmask | ignored_mask, 0 }; g_array_append_val (mods_array, mods); } } return mods_array; } static void meta_change_button_grab (MetaKeyBindingManager *keys, Window xwindow, gboolean grab, gboolean sync, int button, int modmask) { if (meta_is_wayland_compositor ()) return; MetaBackendX11 *backend = META_BACKEND_X11 (keys->backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; GArray *mods; XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Motion); mods = calc_grab_modifiers (keys, modmask); /* GrabModeSync means freeze until XAllowEvents */ if (grab) XIGrabButton (xdisplay, META_VIRTUAL_CORE_POINTER_ID, button, xwindow, None, sync ? XIGrabModeSync : XIGrabModeAsync, XIGrabModeAsync, False, &mask, mods->len, (XIGrabModifiers *)mods->data); else XIUngrabButton (xdisplay, META_VIRTUAL_CORE_POINTER_ID, button, xwindow, mods->len, (XIGrabModifiers *)mods->data); g_array_free (mods, TRUE); } ClutterModifierType meta_display_get_window_grab_modifiers (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; return keys->window_grab_modifiers; } static void meta_change_buttons_grab (MetaKeyBindingManager *keys, Window xwindow, gboolean grab, gboolean sync, int modmask) { #define MAX_BUTTON 3 int i; for (i = 1; i <= MAX_BUTTON; i++) meta_change_button_grab (keys, xwindow, grab, sync, i, modmask); } void meta_display_grab_window_buttons (MetaDisplay *display, Window xwindow) { MetaKeyBindingManager *keys = &display->key_binding_manager; /* Grab Alt + button1 for moving window. * Grab Alt + button2 for resizing window. * Grab Alt + button3 for popping up window menu. * Grab Alt + Shift + button1 for snap-moving window. * Grab Alt + button4 for scrolling in * Grab Alt + button5 for scrolling out */ meta_verbose ("Grabbing window buttons for 0x%lx\n", xwindow); /* FIXME If we ignored errors here instead of spewing, we could * put one big error trap around the loop and avoid a bunch of * XSync() */ if (keys->window_grab_modifiers != 0) { meta_change_buttons_grab (keys, xwindow, TRUE, FALSE, keys->window_grab_modifiers); /* In addition to grabbing Alt+Button1 for moving the window, * grab Alt+Shift+Button1 for snap-moving the window. See bug * 112478. Unfortunately, this doesn't work with * Shift+Alt+Button1 for some reason; so at least part of the * order still matters, which sucks (please FIXME). */ meta_change_button_grab (keys, xwindow, TRUE, FALSE, 1, keys->window_grab_modifiers | ShiftMask); } if (meta_prefs_get_mouse_zoom_enabled () && keys->mouse_zoom_modifiers != 0) { int i; for (i = 4; i < 6; i++) { meta_change_button_grab (keys, xwindow, TRUE, FALSE, i, keys->mouse_zoom_modifiers); } } } void meta_display_ungrab_window_buttons (MetaDisplay *display, Window xwindow) { MetaKeyBindingManager *keys = &display->key_binding_manager; if (keys->window_grab_modifiers != 0) { meta_change_buttons_grab (keys, xwindow, FALSE, FALSE, keys->window_grab_modifiers); } int i = 4; while (i < 6) { meta_change_button_grab (keys, xwindow, FALSE, FALSE, i, keys->mouse_zoom_modifiers); ++i; } } static void update_window_grab_modifiers (MetaKeyBindingManager *keys) { MetaVirtualModifier virtual_mods; unsigned int mods; virtual_mods = meta_prefs_get_mouse_button_mods (); devirtualize_modifiers (keys, virtual_mods, &mods); keys->window_grab_modifiers = mods; } static void update_mouse_zoom_modifiers (MetaKeyBindingManager *keys) { MetaVirtualModifier virtual_mods; unsigned int mods; virtual_mods = meta_prefs_get_mouse_button_zoom_mods (); devirtualize_modifiers (keys, virtual_mods, &mods); keys->mouse_zoom_modifiers = mods; } void meta_display_grab_focus_window_button (MetaDisplay *display, MetaWindow *window) { MetaKeyBindingManager *keys = &display->key_binding_manager; /* Grab button 1 for activating unfocused windows */ meta_verbose ("Grabbing unfocused window buttons for %s\n", window->desc); if (window->have_focus_click_grab) { meta_verbose (" (well, not grabbing since we already have the grab)\n"); return; } /* FIXME If we ignored errors here instead of spewing, we could * put one big error trap around the loop and avoid a bunch of * XSync() */ meta_change_buttons_grab (keys, window->xwindow, TRUE, TRUE, XIAnyModifier); window->have_focus_click_grab = TRUE; } void meta_display_ungrab_focus_window_button (MetaDisplay *display, MetaWindow *window) { MetaKeyBindingManager *keys = &display->key_binding_manager; meta_verbose ("Ungrabbing unfocused window buttons for %s\n", window->desc); if (!window->have_focus_click_grab) return; meta_change_buttons_grab (keys, window->xwindow, FALSE, FALSE, XIAnyModifier); window->have_focus_click_grab = FALSE; } static void prefs_changed_callback (MetaPreference pref, void *data) { MetaDisplay *display = data; MetaKeyBindingManager *keys = &display->key_binding_manager; switch (pref) { case META_PREF_LOCATE_POINTER: break; case META_PREF_KEYBINDINGS: ungrab_key_bindings (display); rebuild_key_binding_table (keys); rebuild_special_bindings (keys); reload_combos (keys); grab_key_bindings (display); break; case META_PREF_MOUSE_BUTTON_MODS: case META_PREF_MOUSE_BUTTON_ZOOM_MODS: case META_PREF_MOUSE_ZOOM_ENABLED: { GSList *windows, *l; windows = meta_display_list_windows (display, META_LIST_DEFAULT); for (l = windows; l; l = l->next) { MetaWindow *w = l->data; meta_display_ungrab_window_buttons (display, w->xwindow); } update_window_grab_modifiers (keys); update_mouse_zoom_modifiers (keys); for (l = windows; l; l = l->next) { MetaWindow *w = l->data; if (w->type != META_WINDOW_DOCK) meta_display_grab_window_buttons (display, w->xwindow); } g_slist_free (windows); } default: break; } } void meta_display_shutdown_keys (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; meta_prefs_remove_listener (prefs_changed_callback, display); g_hash_table_destroy (keys->key_bindings_index); g_hash_table_destroy (keys->key_bindings); clear_active_keyboard_layouts (keys); } /* Grab/ungrab, ignoring all annoying modifiers like NumLock etc. */ static void meta_change_keygrab (MetaKeyBindingManager *keys, Window xwindow, gboolean grab, MetaResolvedKeyCombo *resolved_combo) { unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); if (meta_is_wayland_compositor ()) return; MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); GArray *mods; int i; /* Grab keycode/modmask, together with * all combinations of ignored modifiers. * X provides no better way to do this. */ mods = calc_grab_modifiers (keys, resolved_combo->mask); for (i = 0; i < resolved_combo->len; i++) { xkb_keycode_t keycode = resolved_combo->keycodes[i]; meta_topic (META_DEBUG_KEYBINDINGS, "%s keybinding keycode %d mask 0x%x on 0x%lx\n", grab ? "Grabbing" : "Ungrabbing", keycode, resolved_combo->mask, xwindow); if (grab) XIGrabKeycode (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, keycode, xwindow, XIGrabModeSync, XIGrabModeAsync, False, &mask, mods->len, (XIGrabModifiers *)mods->data); else XIUngrabKeycode (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, keycode, xwindow, mods->len, (XIGrabModifiers *)mods->data); } g_array_free (mods, TRUE); } typedef struct { MetaKeyBindingManager *keys; Window xwindow; gboolean only_per_window; gboolean grab; } ChangeKeygrabData; static void change_keygrab_foreach (gpointer key, gpointer value, gpointer user_data) { ChangeKeygrabData *data = user_data; MetaKeyBinding *binding = value; gboolean binding_is_per_window = (binding->flags & META_KEY_BINDING_PER_WINDOW) != 0; if (data->only_per_window != binding_is_per_window) return; /* Ignore the key bindings marked as META_KEY_BINDING_NO_AUTO_GRAB, * those are handled separately */ if (binding->flags & META_KEY_BINDING_NO_AUTO_GRAB) return; if (binding->resolved_combo.len == 0) return; meta_change_keygrab (data->keys, data->xwindow, data->grab, &binding->resolved_combo); } static void change_binding_keygrabs (MetaKeyBindingManager *keys, Window xwindow, gboolean only_per_window, gboolean grab) { ChangeKeygrabData data; data.keys = keys; data.xwindow = xwindow; data.only_per_window = only_per_window; data.grab = grab; g_hash_table_foreach (keys->key_bindings, change_keygrab_foreach, &data); } static void maybe_update_locate_pointer_keygrab (MetaDisplay *display, gboolean grab) { MetaKeyBindingManager *keys = &display->key_binding_manager; if (!display->x11_display) return; if (keys->locate_pointer_resolved_key_combo.len != 0) meta_change_keygrab (keys, display->x11_display->xroot, (!!grab & !!meta_prefs_is_locate_pointer_enabled()), &keys->locate_pointer_resolved_key_combo); } static void meta_x11_display_change_keygrabs (MetaX11Display *x11_display, gboolean grab) { MetaKeyBindingManager *keys = &x11_display->display->key_binding_manager; int i; for (i = 0; i < keys->n_iso_next_group_combos; i++) meta_change_keygrab (keys, x11_display->xroot, grab, &keys->iso_next_group_combo[i]); change_binding_keygrabs (keys, x11_display->xroot, FALSE, grab); } void meta_x11_display_grab_keys (MetaX11Display *x11_display) { if (x11_display->keys_grabbed) return; meta_x11_display_change_keygrabs (x11_display, TRUE); x11_display->keys_grabbed = TRUE; } void meta_x11_display_ungrab_keys (MetaX11Display *x11_display) { if (!x11_display->keys_grabbed) return; meta_x11_display_change_keygrabs (x11_display, FALSE); x11_display->keys_grabbed = FALSE; } static void change_window_keygrabs (MetaKeyBindingManager *keys, Window xwindow, gboolean grab) { change_binding_keygrabs (keys, xwindow, TRUE, grab); } void meta_window_grab_keys (MetaWindow *window) { MetaDisplay *display = window->display; MetaKeyBindingManager *keys = &display->key_binding_manager; if (meta_is_wayland_compositor ()) return; if (window->all_keys_grabbed) return; if (window->type == META_WINDOW_DOCK || window->override_redirect) { if (window->keys_grabbed) change_window_keygrabs (keys, window->xwindow, FALSE); window->keys_grabbed = FALSE; return; } if (window->keys_grabbed) { if (window->frame && !window->grab_on_frame) change_window_keygrabs (keys, window->xwindow, FALSE); else if (window->frame == NULL && window->grab_on_frame) ; /* continue to regrab on client window */ else return; /* already all good */ } change_window_keygrabs (keys, meta_window_x11_get_toplevel_xwindow (window), TRUE); window->keys_grabbed = TRUE; window->grab_on_frame = window->frame != NULL; } void meta_window_ungrab_keys (MetaWindow *window) { if (!meta_is_wayland_compositor () && window->keys_grabbed) { MetaDisplay *display = window->display; MetaKeyBindingManager *keys = &display->key_binding_manager; if (window->grab_on_frame && window->frame != NULL) change_window_keygrabs (keys, window->frame->xwindow, FALSE); else if (!window->grab_on_frame) change_window_keygrabs (keys, window->xwindow, FALSE); window->keys_grabbed = FALSE; } } static void handle_external_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer user_data) { MetaKeyBindingManager *keys = &display->key_binding_manager; guint action = get_keybinding_action (keys, &binding->resolved_combo); meta_display_accelerator_activate (display, action, event); } guint meta_display_grab_accelerator (MetaDisplay *display, const char *accelerator, MetaKeyBindingFlags flags) { MetaKeyBindingManager *keys = &display->key_binding_manager; MetaKeyBinding *binding; MetaKeyGrab *grab; MetaKeyCombo combo = { 0 }; MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; if (!meta_parse_accelerator (accelerator, &combo)) { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse accelerator\n"); meta_warning ("\"%s\" is not a valid accelerator\n", accelerator); return META_KEYBINDING_ACTION_NONE; } resolve_key_combo (keys, &combo, &resolved_combo); if (resolved_combo.len == 0) return META_KEYBINDING_ACTION_NONE; if (get_keybinding (keys, &resolved_combo)) { resolved_key_combo_reset (&resolved_combo); return META_KEYBINDING_ACTION_NONE; } if (!meta_is_wayland_compositor ()) { meta_change_keygrab (keys, display->x11_display->xroot, TRUE, &resolved_combo); } grab = g_new0 (MetaKeyGrab, 1); grab->action = next_dynamic_keybinding_action (); grab->name = meta_external_binding_name_for_action (grab->action); grab->combo = combo; grab->flags = flags; g_hash_table_insert (external_grabs, grab->name, grab); binding = g_slice_new0 (MetaKeyBinding); binding->name = grab->name; binding->handler = HANDLER ("external-grab"); binding->combo = combo; binding->resolved_combo = resolved_combo; binding->flags = flags; g_hash_table_add (keys->key_bindings, binding); index_binding (keys, binding); return grab->action; } gboolean meta_display_ungrab_accelerator (MetaDisplay *display, guint action) { MetaKeyBindingManager *keys = &display->key_binding_manager; MetaKeyBinding *binding; MetaKeyGrab *grab; g_autofree char *key = NULL; MetaResolvedKeyCombo resolved_combo = { NULL, 0 }; g_return_val_if_fail (action != META_KEYBINDING_ACTION_NONE, FALSE); key = meta_external_binding_name_for_action (action); grab = g_hash_table_lookup (external_grabs, key); if (!grab) return FALSE; resolve_key_combo (keys, &grab->combo, &resolved_combo); binding = get_keybinding (keys, &resolved_combo); if (binding) { int i; if (!meta_is_wayland_compositor ()) { meta_change_keygrab (keys, display->x11_display->xroot, FALSE, &binding->resolved_combo); } for (i = 0; i < binding->resolved_combo.len; i++) { guint32 index_key = key_combo_key (&binding->resolved_combo, i); g_hash_table_remove (keys->key_bindings_index, GINT_TO_POINTER (index_key)); } g_hash_table_remove (keys->key_bindings, binding); } g_hash_table_remove (external_grabs, key); resolved_key_combo_reset (&resolved_combo); return TRUE; } static gboolean grab_keyboard (Window xwindow, guint32 timestamp, int grab_mode) { int grab_status; unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); if (meta_is_wayland_compositor ()) return TRUE; /* Grab the keyboard, so we get key releases and all key * presses */ MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); /* Strictly, we only need to set grab_mode on the keyboard device * while the pointer should always be XIGrabModeAsync. Unfortunately * there is a bug in the X server, only fixed (link below) in 1.15, * which swaps these arguments for keyboard devices. As such, we set * both the device and the paired device mode which works around * that bug and also works on fixed X servers. * * http://cgit.freedesktop.org/xorg/xserver/commit/?id=9003399708936481083424b4ff8f18a16b88b7b3 */ grab_status = XIGrabDevice (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, xwindow, timestamp, None, grab_mode, grab_mode, False, /* owner_events */ &mask); return (grab_status == Success); } static void ungrab_keyboard (guint32 timestamp) { if (meta_is_wayland_compositor ()) return; MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); XIUngrabDevice (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp); } gboolean meta_window_grab_all_keys (MetaWindow *window, guint32 timestamp) { Window grabwindow; gboolean retval = TRUE; if (window->all_keys_grabbed) return FALSE; if (window->keys_grabbed) meta_window_ungrab_keys (window); /* Make sure the window is focused, otherwise the grab * won't do a lot of good. */ meta_topic (META_DEBUG_FOCUS, "Focusing %s because we're grabbing all its keys\n", window->desc); meta_window_focus (window, timestamp); if (!meta_is_wayland_compositor ()) { grabwindow = meta_window_x11_get_toplevel_xwindow (window); meta_topic (META_DEBUG_KEYBINDINGS, "Grabbing all keys on window %s\n", window->desc); retval = grab_keyboard (grabwindow, timestamp, XIGrabModeAsync); } if (retval) { window->keys_grabbed = FALSE; window->all_keys_grabbed = TRUE; window->grab_on_frame = window->frame != NULL; } return retval; } void meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp) { if (window->all_keys_grabbed) { if (!meta_is_wayland_compositor()) ungrab_keyboard (timestamp); window->grab_on_frame = FALSE; window->all_keys_grabbed = FALSE; window->keys_grabbed = FALSE; /* Re-establish our standard bindings */ meta_window_grab_keys (window); } } void meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp) { MetaBackend *backend = meta_get_backend (); if (!META_IS_BACKEND_X11 (backend)) return; Window window = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)); grab_keyboard (window, timestamp, XIGrabModeSync); } void meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp) { ungrab_keyboard (timestamp); } void meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp) { MetaBackend *backend = meta_get_backend (); if (!META_IS_BACKEND_X11 (backend)) return; Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XIAllowEvents (xdisplay, META_VIRTUAL_CORE_KEYBOARD_ID, XIAsyncDevice, timestamp); /* We shouldn't need to unfreeze the pointer device here, however we * have to, due to the workaround we do in grab_keyboard(). */ XIAllowEvents (xdisplay, META_VIRTUAL_CORE_POINTER_ID, XIAsyncDevice, timestamp); } static gboolean is_modifier (xkb_keysym_t keysym) { switch (keysym) { case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: case XKB_KEY_Control_L: case XKB_KEY_Control_R: case XKB_KEY_Caps_Lock: case XKB_KEY_Shift_Lock: case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: case XKB_KEY_Super_L: case XKB_KEY_Super_R: case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: return TRUE; default: return FALSE; } } static void invoke_handler (MetaDisplay *display, MetaKeyHandler *handler, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding) { if (handler->func) (* handler->func) (display, handler->flags & META_KEY_BINDING_PER_WINDOW ? window : NULL, event, binding, handler->user_data); else (* handler->default_func) (display, handler->flags & META_KEY_BINDING_PER_WINDOW ? window: NULL, event, binding, NULL); } static gboolean meta_key_binding_has_handler_func (MetaKeyBinding *binding) { return (!!binding->handler->func || !!binding->handler->default_func); } static void strip_self_mod (guint keyval, ClutterModifierType event_state, ClutterModifierType *stripped_mod) { unsigned long mod = 0; switch (keyval) { case CLUTTER_KEY_Super_L: case CLUTTER_KEY_Super_R: mod = CLUTTER_MOD4_MASK; break; case CLUTTER_KEY_Control_L: case CLUTTER_KEY_Control_R: mod = CLUTTER_CONTROL_MASK; break; case CLUTTER_KEY_Alt_L: case CLUTTER_KEY_Alt_R: mod = CLUTTER_MOD1_MASK; break; case CLUTTER_KEY_Shift_L: case CLUTTER_KEY_Shift_R: mod = CLUTTER_SHIFT_MASK; break; default: mod = 0; break; } *stripped_mod = event_state & ~mod; } static gboolean is_workspace_action (gint action) { if (action == META_MOTION_LEFT || action == META_MOTION_RIGHT) { return TRUE; } switch (action) { case META_MOTION_LEFT: case META_MOTION_RIGHT: case META_KEYBINDING_ACTION_WORKSPACE_1: case META_KEYBINDING_ACTION_WORKSPACE_2: case META_KEYBINDING_ACTION_WORKSPACE_3: case META_KEYBINDING_ACTION_WORKSPACE_4: case META_KEYBINDING_ACTION_WORKSPACE_5: case META_KEYBINDING_ACTION_WORKSPACE_6: case META_KEYBINDING_ACTION_WORKSPACE_7: case META_KEYBINDING_ACTION_WORKSPACE_8: case META_KEYBINDING_ACTION_WORKSPACE_9: case META_KEYBINDING_ACTION_WORKSPACE_10: case META_KEYBINDING_ACTION_WORKSPACE_11: case META_KEYBINDING_ACTION_WORKSPACE_12: return TRUE; } return FALSE; } static gboolean process_event (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, gboolean allow_release, gboolean mouse_grab_move) { MetaKeyBindingManager *keys = &display->key_binding_manager; xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; MetaKeyBinding *binding; MetaKeyBindingAction action; /* we used to have release-based bindings but no longer. */ if (event->type == CLUTTER_KEY_RELEASE && !allow_release) return FALSE; resolved_combo.mask = mask_from_event_params (keys, event->modifier_state); strip_self_mod (event->keyval, mask_from_event_params (keys, event->modifier_state), &resolved_combo.mask); binding = get_keybinding (keys, &resolved_combo); if (!binding || (!window && binding->flags & META_KEY_BINDING_PER_WINDOW)) goto not_found; action = binding->handler->data; if (mouse_grab_move && !is_workspace_action (action)) goto not_found; if (binding->handler == NULL) meta_bug ("Binding %s has no handler\n", binding->name); if (!meta_key_binding_has_handler_func (binding)) goto not_found; if (display->focus_window && !(binding->handler->flags & META_KEY_BINDING_NON_MASKABLE)) { ClutterInputDevice *source; source = clutter_event_get_source_device ((ClutterEvent *) event); if (meta_window_shortcuts_inhibited (display->focus_window, source)) goto not_found; } /* If the compositor filtered out the keybindings, that * means they don't want the binding to trigger, so we do * the same thing as if the binding didn't exist. */ if (meta_compositor_filter_keybinding (display->compositor, binding)) goto not_found; if (event->flags & CLUTTER_EVENT_FLAG_REPEATED && binding->flags & META_KEY_BINDING_IGNORE_AUTOREPEAT) { meta_topic (META_DEBUG_KEYBINDINGS, "Ignore autorepeat for handler %s\n", binding->name); return TRUE; } meta_topic (META_DEBUG_KEYBINDINGS, "Running handler for %s\n", binding->name); /* Global keybindings count as a let-the-terminal-lose-focus * due to new window mapping until the user starts * interacting with the terminal again. */ display->allow_terminal_deactivation = TRUE; invoke_handler (display, binding->handler, window, event, binding); return TRUE; not_found: meta_topic (META_DEBUG_KEYBINDINGS, "No handler found for this event in this binding table\n"); return mouse_grab_move; } static gboolean modifier_only_keyval (guint keyval) { return keyval == CLUTTER_KEY_Super_L || keyval == CLUTTER_KEY_Super_R || keyval == CLUTTER_KEY_Control_L || keyval == CLUTTER_KEY_Control_R || keyval == CLUTTER_KEY_Alt_L || keyval == CLUTTER_KEY_Alt_R || keyval == CLUTTER_KEY_Shift_L || keyval == CLUTTER_KEY_Shift_R; } static void get_combo (MetaKeyBindingManager *keys, unsigned int code, unsigned long mask, MetaResolvedKeyCombo *combo) { xkb_keycode_t keycode = (xkb_keycode_t) code; MetaResolvedKeyCombo resolved_combo = { &keycode, 1 }; resolved_combo.mask = mask_from_event_params (keys, mask); *combo = resolved_combo; } static gboolean process_special_modifier_key (MetaDisplay *display, ClutterKeyEvent *event, MetaWindow *window, gboolean *allow_key_up, GFunc trigger_callback) { MetaKeyBindingManager *keys = &display->key_binding_manager; MetaBackend *backend = keys->backend; Display *xdisplay; if (META_IS_BACKEND_X11 (backend)) xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); else xdisplay = NULL; if (event->type == CLUTTER_KEY_PRESS) { /* check if we're just Pressing a captured modifier at this time */ if (modifier_only_keyval (event->keyval)) { /* remember this state, because we'll need to know it for subsequent events */ modifier_key_only_pressed = TRUE; /* We keep the keyboard frozen - this allows us to use ReplayKeyboard * on the next event if it's not the release of this modifier key */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XISyncDevice, event->time); return TRUE; } else { modifier_key_only_pressed = FALSE; if (process_event (display, window, event, FALSE, FALSE)) { /* As normally, after we've handled a global key * binding, we unfreeze the keyboard but keep the grab * (this is important for something like cycling * windows */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XIAsyncDevice, event->time); return TRUE; } else { /* Replay the event so it gets delivered to our * per-window key bindings or to the application */ if (xdisplay) XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XIReplayDevice, event->time); return FALSE; } } } else { /* We only care about key releases for modifier-only bindings - * and only when that release directly follows a press. When this happens, * negate modifier_only_is_down, and allow the binding handler to accept * a key release. */ if (modifier_only_keyval (event->keyval)) { if (modifier_key_only_pressed) *allow_key_up = TRUE; modifier_key_only_pressed = FALSE; } } return FALSE; } static gboolean process_modifier_only (MetaDisplay *display, ClutterKeyEvent *event, MetaWindow *window, gboolean *allow_key_up) { MetaKeyBindingManager *keys = &display->key_binding_manager; if (display->focus_window && !modifier_key_only_pressed) { ClutterInputDevice *source; source = clutter_event_get_source_device ((ClutterEvent *) event); if (meta_window_shortcuts_inhibited (display->focus_window, source)) return FALSE; } return process_special_modifier_key (display, event, window, allow_key_up, (GFunc) meta_display_overlay_key_activate); } static void handle_locate_pointer (MetaDisplay *display) { meta_compositor_locate_pointer (display->compositor); } static gboolean process_iso_next_group (MetaDisplay *display, ClutterKeyEvent *event) { MetaKeyBindingManager *keys = &display->key_binding_manager; gboolean activate; xkb_keycode_t keycode = (xkb_keycode_t) event->hardware_keycode; xkb_mod_mask_t mask; int i, j; if (event->type == CLUTTER_KEY_RELEASE) return FALSE; activate = FALSE; mask = mask_from_event_params (keys, event->modifier_state); for (i = 0; i < keys->n_iso_next_group_combos; ++i) { for (j = 0; j < keys->iso_next_group_combo[i].len; ++j) { if (keycode == keys->iso_next_group_combo[i].keycodes[j] && mask == keys->iso_next_group_combo[i].mask) { /* If the signal handler returns TRUE the keyboard will remain frozen. It's the signal handler's responsibility to unfreeze it. */ if (!meta_display_modifiers_accelerator_activate (display)) meta_display_unfreeze_keyboard (display, event->time); activate = TRUE; break; } } } return activate; } static gboolean process_key_event (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event) { gboolean keep_grab, mouse_grab_move; gboolean all_keys_grabbed; gboolean allow_key_up = FALSE; all_keys_grabbed = window ? window->all_keys_grabbed : FALSE; if (!all_keys_grabbed) { if (process_modifier_only (display, event, window, &allow_key_up)) return TRUE; if (process_iso_next_group (display, event)) return TRUE; } { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XIAllowEvents (xdisplay, clutter_input_device_get_device_id (event->device), XIAsyncDevice, event->time); } } keep_grab = TRUE; mouse_grab_move = FALSE; if (all_keys_grabbed) { if (display->grab_op == META_GRAB_OP_NONE) return TRUE; /* If we get here we have a global grab, because * we're in some special keyboard mode such as window move * mode. */ if (window == display->grab_window) { if (display->grab_op & META_GRAB_OP_WINDOW_FLAG_KEYBOARD) { if (display->grab_op == META_GRAB_OP_KEYBOARD_MOVING) { meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard move\n"); keep_grab = process_keyboard_move_grab (display, window, event); } else { meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for keyboard resize\n"); keep_grab = process_keyboard_resize_grab (display, window, event); } } else { meta_topic (META_DEBUG_KEYBINDINGS, "Processing event for mouse-only move/resize\n"); keep_grab = process_mouse_move_resize_grab (display, window, event); mouse_grab_move = display->grab_op == META_GRAB_OP_MOVING; } } if (!keep_grab) meta_display_end_grab_op (display, event->time); if (!mouse_grab_move) { return TRUE; } } /* Do the normal keybindings */ return process_event (display, window, event, allow_key_up, mouse_grab_move); } /* Handle a key event. May be called recursively: some key events cause * grabs to be ended and then need to be processed again in their own * right. This cannot cause infinite recursion because we never call * ourselves when there wasn't a grab, and we always clear the grab * first; the invariant is enforced using an assertion. See #112560. * * The return value is whether we handled the key event. * * FIXME: We need to prove there are no race conditions here. * FIXME: Does it correctly handle alt-Tab being followed by another * grabbing keypress without letting go of alt? * FIXME: An iterative solution would probably be simpler to understand * (and help us solve the other fixmes). */ gboolean meta_keybindings_process_event (MetaDisplay *display, MetaWindow *window, const ClutterEvent *event) { MetaKeyBindingManager *keys = &display->key_binding_manager; switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_END: modifier_key_only_pressed = FALSE; return FALSE; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: return process_key_event (display, window, (ClutterKeyEvent *) event); default: return FALSE; } } static void handle_workspace_shift (MetaWindow *window, ClutterKeyEvent *event, guint keyval) { MetaWorkspace *target_workspace; guint motion = META_MOTION_LEFT; gboolean should_handle = FALSE; if (keyval == CLUTTER_KEY_Left || keyval == CLUTTER_KEY_KP_Left) { motion = meta_prefs_get_invert_flip_direction () ? META_MOTION_RIGHT : META_MOTION_LEFT; should_handle = TRUE; } else if (keyval == CLUTTER_KEY_Right || keyval == CLUTTER_KEY_KP_Right) { motion = meta_prefs_get_invert_flip_direction () ? META_MOTION_LEFT : META_MOTION_RIGHT; should_handle = TRUE; } if (!should_handle) { return; } target_workspace = meta_workspace_get_neighbor (meta_workspace_manager_get_active_workspace (window->display->workspace_manager), motion); if (target_workspace) { meta_workspace_activate (target_workspace, event->time); } } static gboolean process_mouse_move_resize_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event) { /* don't care about releases, but eat them, don't end grab */ if (event->type == CLUTTER_KEY_RELEASE) return TRUE; if (event->keyval == CLUTTER_KEY_Escape) { MetaTileMode tile_mode; /* Hide the tiling preview if necessary */ if (display->preview_tile_mode != META_TILE_NONE) meta_display_hide_tile_preview (display); /* Restore the original tile mode */ tile_mode = display->grab_tile_mode; window->tile_monitor_number = display->grab_tile_monitor_number; /* End move or resize and restore to original state. If the * window was a maximized window that had been "shaken loose" we * need to remaximize it. In normal cases, we need to do a * moveresize now to get the position back to the original. */ if (window->saved_tile_mode == META_TILE_MAXIMIZED) meta_window_maximize (window, META_MAXIMIZE_BOTH); else if (tile_mode != META_TILE_NONE) meta_window_restore_tile (window, tile_mode, display->grab_initial_window_pos.width, display->grab_initial_window_pos.height); else meta_window_move_resize_frame (display->grab_window, TRUE, display->grab_initial_window_pos.x, display->grab_initial_window_pos.y, display->grab_initial_window_pos.width, display->grab_initial_window_pos.height); /* End grab */ return FALSE; } MetaWorkspace *target_workspace; gulong mask; /* Only proceed if no mod or only numlock */ mask = event->modifier_state & ~(CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); if (mask != CLUTTER_BUTTON1_MASK) return TRUE; gint index = -1; switch (event->keyval) { case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: handle_workspace_shift (window, event, event->keyval); return TRUE; case CLUTTER_KEY_KP_1: case CLUTTER_KEY_1: index = 0; break; case CLUTTER_KEY_KP_2: case CLUTTER_KEY_2: index = 1; break; case CLUTTER_KEY_KP_3: case CLUTTER_KEY_3: index = 2; break; case CLUTTER_KEY_KP_4: case CLUTTER_KEY_4: index = 3; break; case CLUTTER_KEY_KP_5: case CLUTTER_KEY_5: index = 4; break; case CLUTTER_KEY_KP_6: case CLUTTER_KEY_6: index = 5; break; case CLUTTER_KEY_KP_7: case CLUTTER_KEY_7: index = 6; break; case CLUTTER_KEY_KP_8: case CLUTTER_KEY_8: index = 7; break; case CLUTTER_KEY_KP_9: case CLUTTER_KEY_9: index = 8; break; case CLUTTER_KEY_KP_0: case CLUTTER_KEY_0: index = 9; break; default: break; } if (index >= 0) { target_workspace = meta_workspace_manager_get_workspace_by_index (window->display->workspace_manager, index); if (target_workspace) { meta_workspace_activate (target_workspace, event->time); } } return TRUE; } static gboolean process_keyboard_move_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event) { gboolean handled; MetaRectangle frame_rect; int x, y; int incr; gboolean smart_snap; handled = FALSE; /* don't care about releases, but eat them, don't end grab */ if (event->type == CLUTTER_KEY_RELEASE) return TRUE; /* don't end grab on modifier key presses */ if (is_modifier (event->keyval)) return TRUE; meta_window_get_frame_rect (window, &frame_rect); x = frame_rect.x; y = frame_rect.y; smart_snap = (event->modifier_state & CLUTTER_SHIFT_MASK) != 0; #define SMALL_INCREMENT 1 #define NORMAL_INCREMENT 10 if (smart_snap) incr = 1; else if (event->modifier_state & CLUTTER_CONTROL_MASK) incr = SMALL_INCREMENT; else incr = NORMAL_INCREMENT; if (event->keyval == CLUTTER_KEY_Escape) { MetaTileMode tile_mode; /* Hide the tiling preview if necessary */ if (display->preview_tile_mode != META_TILE_NONE) meta_display_hide_tile_preview (display); /* Restore the original tile mode */ tile_mode = display->grab_tile_mode; window->tile_monitor_number = display->grab_tile_monitor_number; /* End move and restore to original state. If the window was a * maximized window that had been "shaken loose" we need to * remaximize it. In normal cases, we need to do a moveresize * now to get the position back to the original. */ if (window->saved_tile_mode == META_TILE_MAXIMIZED) meta_window_maximize (window, META_MAXIMIZE_BOTH); else if (tile_mode != META_TILE_NONE) meta_window_restore_tile (window, tile_mode, display->grab_initial_window_pos.width, display->grab_initial_window_pos.height); else meta_window_move_resize_frame (display->grab_window, TRUE, display->grab_initial_window_pos.x, display->grab_initial_window_pos.y, display->grab_initial_window_pos.width, display->grab_initial_window_pos.height); } /* When moving by increments, we still snap to edges if the move * to the edge is smaller than the increment. This is because * Shift + arrow to snap is sort of a hidden feature. This way * people using just arrows shouldn't get too frustrated. */ switch (event->keyval) { case CLUTTER_KEY_KP_Home: case CLUTTER_KEY_KP_Prior: case CLUTTER_KEY_Up: case CLUTTER_KEY_KP_Up: y -= incr; handled = TRUE; break; case CLUTTER_KEY_KP_End: case CLUTTER_KEY_KP_Next: case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: y += incr; handled = TRUE; break; } switch (event->keyval) { case CLUTTER_KEY_KP_Home: case CLUTTER_KEY_KP_End: case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: x -= incr; handled = TRUE; break; case CLUTTER_KEY_KP_Prior: case CLUTTER_KEY_KP_Next: case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: x += incr; handled = TRUE; break; } if (handled) { meta_topic (META_DEBUG_KEYBINDINGS, "Computed new window location %d,%d due to keypress\n", x, y); meta_window_edge_resistance_for_move (window, &x, &y, NULL, smart_snap, TRUE); meta_window_move_frame (window, TRUE, x, y); meta_window_update_keyboard_move (window); } return handled; } static gboolean process_keyboard_resize_grab_op_change (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event) { gboolean handled; handled = FALSE; switch (display->grab_op) { case META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: switch (event->keyval) { case CLUTTER_KEY_Up: case CLUTTER_KEY_KP_Up: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; handled = TRUE; break; case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; handled = TRUE; break; case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; handled = TRUE; break; case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; handled = TRUE; break; } break; case META_GRAB_OP_KEYBOARD_RESIZING_S: switch (event->keyval) { case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; handled = TRUE; break; case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; handled = TRUE; break; } break; case META_GRAB_OP_KEYBOARD_RESIZING_N: switch (event->keyval) { case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_W; handled = TRUE; break; case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_E; handled = TRUE; break; } break; case META_GRAB_OP_KEYBOARD_RESIZING_W: switch (event->keyval) { case CLUTTER_KEY_Up: case CLUTTER_KEY_KP_Up: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; handled = TRUE; break; case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; handled = TRUE; break; } break; case META_GRAB_OP_KEYBOARD_RESIZING_E: switch (event->keyval) { case CLUTTER_KEY_Up: case CLUTTER_KEY_KP_Up: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_N; handled = TRUE; break; case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: display->grab_op = META_GRAB_OP_KEYBOARD_RESIZING_S; handled = TRUE; break; } break; case META_GRAB_OP_KEYBOARD_RESIZING_SE: case META_GRAB_OP_KEYBOARD_RESIZING_NE: case META_GRAB_OP_KEYBOARD_RESIZING_SW: case META_GRAB_OP_KEYBOARD_RESIZING_NW: break; default: g_assert_not_reached (); break; } if (handled) { meta_window_update_keyboard_resize (window, TRUE); return TRUE; } return FALSE; } static gboolean process_keyboard_resize_grab (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event) { MetaRectangle frame_rect; gboolean handled; int height_inc; int width_inc; int width, height; gboolean smart_snap; MetaGravity gravity; handled = FALSE; /* don't care about releases, but eat them, don't end grab */ if (event->type == CLUTTER_KEY_RELEASE) return TRUE; /* don't end grab on modifier key presses */ if (is_modifier (event->keyval)) return TRUE; if (event->keyval == CLUTTER_KEY_Escape) { /* End resize and restore to original state. */ meta_window_move_resize_frame (display->grab_window, TRUE, display->grab_initial_window_pos.x, display->grab_initial_window_pos.y, display->grab_initial_window_pos.width, display->grab_initial_window_pos.height); return FALSE; } if (process_keyboard_resize_grab_op_change (display, window, event)) return TRUE; width = window->rect.width; height = window->rect.height; meta_window_get_frame_rect (window, &frame_rect); width = frame_rect.width; height = frame_rect.height; gravity = meta_resize_gravity_from_grab_op (display->grab_op); smart_snap = (event->modifier_state & CLUTTER_SHIFT_MASK) != 0; #define SMALL_INCREMENT 1 #define NORMAL_INCREMENT 10 if (smart_snap) { height_inc = 1; width_inc = 1; } else if (event->modifier_state & CLUTTER_CONTROL_MASK) { width_inc = SMALL_INCREMENT; height_inc = SMALL_INCREMENT; } else { width_inc = NORMAL_INCREMENT; height_inc = NORMAL_INCREMENT; } /* If this is a resize increment window, make the amount we resize * the window by match that amount (well, unless snap resizing...) */ if (window->size_hints.width_inc > 1) width_inc = window->size_hints.width_inc; if (window->size_hints.height_inc > 1) height_inc = window->size_hints.height_inc; switch (event->keyval) { case CLUTTER_KEY_Up: case CLUTTER_KEY_KP_Up: switch (gravity) { case META_GRAVITY_NORTH: case META_GRAVITY_NORTH_WEST: case META_GRAVITY_NORTH_EAST: /* Move bottom edge up */ height -= height_inc; break; case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH_EAST: /* Move top edge up */ height += height_inc; break; case META_GRAVITY_EAST: case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_NONE: case META_GRAVITY_STATIC: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Down: case CLUTTER_KEY_KP_Down: switch (gravity) { case META_GRAVITY_NORTH: case META_GRAVITY_NORTH_WEST: case META_GRAVITY_NORTH_EAST: /* Move bottom edge down */ height += height_inc; break; case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH_EAST: /* Move top edge down */ height -= height_inc; break; case META_GRAVITY_EAST: case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_NONE: case META_GRAVITY_STATIC: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Left: case CLUTTER_KEY_KP_Left: switch (gravity) { case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: case META_GRAVITY_NORTH_EAST: /* Move left edge left */ width += width_inc; break; case META_GRAVITY_WEST: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_NORTH_WEST: /* Move right edge left */ width -= width_inc; break; case META_GRAVITY_NORTH: case META_GRAVITY_SOUTH: case META_GRAVITY_CENTER: case META_GRAVITY_NONE: case META_GRAVITY_STATIC: g_assert_not_reached (); break; } handled = TRUE; break; case CLUTTER_KEY_Right: case CLUTTER_KEY_KP_Right: switch (gravity) { case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: case META_GRAVITY_NORTH_EAST: /* Move left edge right */ width -= width_inc; break; case META_GRAVITY_WEST: case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_NORTH_WEST: /* Move right edge right */ width += width_inc; break; case META_GRAVITY_NORTH: case META_GRAVITY_SOUTH: case META_GRAVITY_CENTER: case META_GRAVITY_NONE: case META_GRAVITY_STATIC: g_assert_not_reached (); break; } handled = TRUE; break; default: break; } /* fixup hack (just paranoia, not sure it's required) */ if (height < 1) height = 1; if (width < 1) width = 1; if (handled) { meta_topic (META_DEBUG_KEYBINDINGS, "Computed new window size due to keypress: " "%dx%d, gravity %s\n", width, height, meta_gravity_to_string (gravity)); /* Do any edge resistance/snapping */ meta_window_edge_resistance_for_resize (window, &width, &height, gravity, NULL, smart_snap, TRUE); meta_window_resize_frame_with_gravity (window, TRUE, width, height, gravity); meta_window_update_keyboard_resize (window, FALSE); } return handled; } static void handle_switch_to_last_workspace (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint target = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1; MetaWorkspace *workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, target); meta_workspace_activate (workspace, event->time); } static void handle_switch_to_workspace (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gint which = binding->handler->data; MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaWorkspace *workspace; if (which < 0) { /* Negative workspace numbers are directions with respect to the * current workspace. */ workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace, which); } else { workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); } if (workspace) { meta_workspace_activate (workspace, event->time); } else { /* We could offer to create it I suppose */ } } static void handle_maximize_vertically (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_resize_func) { if (window->maximized_vertically) meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL); else meta_window_maximize (window, META_MAXIMIZE_VERTICAL); } } static void handle_maximize_horizontally (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_resize_func) { if (window->maximized_horizontally) meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL); else meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL); } } static void handle_always_on_top (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->wm_state_above == FALSE) meta_window_make_above (window); else meta_window_unmake_above (window); } static void handle_move_to_corner_backend (MetaDisplay *display, MetaWindow *window, MetaGravity gravity) { MetaRectangle work_area; MetaRectangle frame_rect; int new_x, new_y; if (!window->monitor) return; meta_window_get_work_area_current_monitor (window, &work_area); meta_window_get_frame_rect (window, &frame_rect); switch (gravity) { case META_GRAVITY_NORTH_WEST: case META_GRAVITY_WEST: case META_GRAVITY_SOUTH_WEST: new_x = work_area.x; break; case META_GRAVITY_NORTH: case META_GRAVITY_SOUTH: new_x = frame_rect.x; break; case META_GRAVITY_NORTH_EAST: case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: new_x = work_area.x + work_area.width - frame_rect.width; break; default: g_assert_not_reached (); } switch (gravity) { case META_GRAVITY_NORTH_WEST: case META_GRAVITY_NORTH: case META_GRAVITY_NORTH_EAST: new_y = work_area.y; break; case META_GRAVITY_WEST: case META_GRAVITY_EAST: new_y = frame_rect.y; break; case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_EAST: new_y = work_area.y + work_area.height - frame_rect.height; break; default: g_assert_not_reached (); } meta_window_move_frame (window, TRUE, new_x, new_y); } static void handle_move_to_corner_nw (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_WEST); } static void handle_move_to_corner_ne (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH_EAST); } static void handle_move_to_corner_sw (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_WEST); } static void handle_move_to_corner_se (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH_EAST); } static void handle_move_to_side_n (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_NORTH); } static void handle_move_to_side_s (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_SOUTH); } static void handle_move_to_side_e (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_EAST); } static void handle_move_to_side_w (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { handle_move_to_corner_backend (display, window, META_GRAVITY_WEST); } static void handle_move_to_center (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaRectangle work_area; MetaRectangle frame_rect; meta_window_get_work_area_current_monitor (window, &work_area); meta_window_get_frame_rect (window, &frame_rect); meta_window_move_frame (window, TRUE, work_area.x + (work_area.width - frame_rect.width ) / 2, work_area.y + (work_area.height - frame_rect.height) / 2); } static void handle_show_desktop (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; if (workspace_manager->active_workspace->showing_desktop) { meta_workspace_manager_unshow_desktop (workspace_manager); meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, event->time); } else meta_workspace_manager_show_desktop (workspace_manager, event->time); } static void handle_panel (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaKeyBindingAction action = binding->handler->data; MetaX11Display *x11_display = display->x11_display; Atom action_atom; XClientMessageEvent ev; action_atom = None; switch (action) { /* FIXME: The numbers are wrong */ case META_KEYBINDING_ACTION_PANEL_MAIN_MENU: action_atom = x11_display->atom__GNOME_PANEL_ACTION_MAIN_MENU; break; case META_KEYBINDING_ACTION_PANEL_RUN_DIALOG: action_atom = x11_display->atom__GNOME_PANEL_ACTION_RUN_DIALOG; break; default: return; } ev.type = ClientMessage; ev.window = x11_display->xroot; ev.message_type = x11_display->atom__GNOME_PANEL_ACTION; ev.format = 32; ev.data.l[0] = action_atom; ev.data.l[1] = event->time; meta_topic (META_DEBUG_KEYBINDINGS, "Sending panel message with timestamp %u, and turning mouse_mode " "off due to keybinding press\n", event->time); display->mouse_mode = FALSE; meta_x11_error_trap_push (x11_display); /* Release the grab for the panel before sending the event */ XUngrabKeyboard (x11_display->xdisplay, event->time); XSendEvent (x11_display->xdisplay, x11_display->xroot, False, StructureNotifyMask, (XEvent*) &ev); meta_x11_error_trap_pop (x11_display); } static void handle_activate_window_menu (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (display->focus_window) { int x, y; MetaRectangle frame_rect; cairo_rectangle_int_t child_rect; meta_window_get_frame_rect (display->focus_window, &frame_rect); meta_window_get_client_area_rect (display->focus_window, &child_rect); x = frame_rect.x + child_rect.x; if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) x += child_rect.width; y = frame_rect.y + child_rect.y; meta_window_show_menu (display->focus_window, META_WINDOW_MENU_WM, x, y); } } static void do_choose_window (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gboolean backward) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaTabList type = binding->handler->data; MetaWindow *window; meta_topic (META_DEBUG_KEYBINDINGS, "Tab list = %u\n", type); window = meta_display_get_tab_next (display, type, workspace_manager->active_workspace, NULL, backward); if (window) meta_window_activate (window, event->time); } static void handle_switch (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, event_window, event, binding, backwards); } static void handle_cycle (MetaDisplay *display, MetaWindow *event_window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gboolean backwards = meta_key_binding_is_reversed (binding); do_choose_window (display, event_window, event, binding, backwards); } static void handle_toggle_fullscreen (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->fullscreen) meta_window_unmake_fullscreen (window); else if (window->has_fullscreen_func) meta_window_make_fullscreen (window); } static void handle_toggle_above (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->wm_state_above) meta_window_unmake_above (window); else meta_window_make_above (window); } static MetaTileMode get_new_tile_mode (MetaTileMode direction, MetaTileMode current) { MetaTileMode ret = META_TILE_NONE; switch (current) { case META_TILE_NONE: ret = direction; break; case META_TILE_MAXIMIZED: if (direction == META_TILE_LEFT) ret = META_TILE_LEFT; else if (direction == META_TILE_RIGHT) ret = META_TILE_RIGHT; else if (direction == META_TILE_TOP) ret = direction; else if (direction == META_TILE_BOTTOM) ret = META_TILE_TOP; else ret = META_TILE_NONE; break; case META_TILE_LEFT: if (direction == META_TILE_LEFT) ret = META_TILE_LEFT; else if (direction == META_TILE_RIGHT) ret = META_TILE_NONE; else if (direction == META_TILE_TOP) ret = META_TILE_ULC; else ret = META_TILE_LLC; break; case META_TILE_RIGHT: if (direction == META_TILE_LEFT) ret = META_TILE_NONE; else if (direction == META_TILE_RIGHT) ret = META_TILE_RIGHT; else if (direction == META_TILE_TOP) ret = META_TILE_URC; else ret = META_TILE_LRC; break; case META_TILE_TOP: if (direction == META_TILE_LEFT) ret = META_TILE_ULC; else if (direction == META_TILE_RIGHT) ret = META_TILE_URC; else if (direction == META_TILE_TOP) ret = META_TILE_MAXIMIZED; else ret = META_TILE_NONE; break; case META_TILE_BOTTOM: if (direction == META_TILE_LEFT) ret = META_TILE_LLC; else if (direction == META_TILE_RIGHT) ret = META_TILE_LRC; else if (direction == META_TILE_TOP) ret = META_TILE_NONE; else ret = META_TILE_BOTTOM; break; case META_TILE_ULC: if (direction == META_TILE_LEFT) ret = META_TILE_ULC; else if (direction == META_TILE_RIGHT) ret = META_TILE_TOP; else if (direction == META_TILE_TOP) ret = META_TILE_ULC; else ret = META_TILE_LEFT; break; case META_TILE_LLC: if (direction == META_TILE_LEFT) ret = META_TILE_LLC; else if (direction == META_TILE_RIGHT) ret = META_TILE_BOTTOM; else if (direction == META_TILE_TOP) ret = META_TILE_LEFT; else ret = META_TILE_LLC; break; case META_TILE_URC: if (direction == META_TILE_LEFT) ret = META_TILE_TOP; else if (direction == META_TILE_RIGHT) ret = META_TILE_URC; else if (direction == META_TILE_TOP) ret = META_TILE_URC; else ret = META_TILE_RIGHT; break; case META_TILE_LRC: if (direction == META_TILE_LEFT) ret = META_TILE_BOTTOM; else if (direction == META_TILE_RIGHT) ret = META_TILE_LRC; else if (direction == META_TILE_TOP) ret = META_TILE_RIGHT; else ret = META_TILE_LRC; break; default: ret = current; break; } return ret; } static gboolean can_tile (MetaWindow *window, MetaTileMode mode) { switch (mode) { case META_TILE_LEFT: case META_TILE_RIGHT: return meta_window_can_tile_left_right (window); case META_TILE_TOP: case META_TILE_BOTTOM: return meta_window_can_tile_top_bottom (window); case META_TILE_ULC: case META_TILE_LLC: case META_TILE_URC: case META_TILE_LRC: return meta_window_can_tile_corner (window); case META_TILE_MAXIMIZED: case META_TILE_NONE: return TRUE; default: return FALSE; } } static void do_tile_move (MetaWindow *window, MetaTileMode mode) { MetaTileMode new_mode; new_mode = get_new_tile_mode (mode, META_WINDOW_MAXIMIZED (window) ? META_TILE_MAXIMIZED : window->tile_mode); if (new_mode == window->tile_mode) return; if (new_mode == META_TILE_NONE) { meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } else if (can_tile (window, new_mode)) { window->tile_monitor_number = window->monitor->number; window->tile_mode = META_TILE_NONE; meta_window_tile (window, new_mode); } } static void handle_tile_action (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaTileMode mode = binding->handler->data; do_tile_move (window, mode); } /** * meta_display_push_tile: * @display: the #MetaDisplay * @window: the #MetaWindow to act upon * @direction: the #MetaMotionDirection to push the window in. * * If the window is not yet tiled, tile the window to the portion * of the monitor indicated by @direction. If already tiled, perform * navigation around the possible tile positions, or untile the * window, according to @direction. * * For example, if @window is left-tiled, and the @direction is * #META_MOTION_UP, the window will be upper-left corner tiled. If the * @direction is #META_MOTION_RIGHT, the window will be untiled. * * This logic is identical to the behavior of the tiling keyboard * shortcuts. **/ void meta_display_push_tile (MetaDisplay *display, MetaWindow *window, MetaMotionDirection direction) { g_return_if_fail (META_IS_DISPLAY (display) && META_IS_WINDOW (window)); MetaTileMode mode; switch (direction) { case META_MOTION_LEFT: mode = META_TILE_LEFT; break; case META_MOTION_RIGHT: mode = META_TILE_RIGHT; break; case META_MOTION_UP: mode = META_TILE_TOP; break; case META_MOTION_DOWN: mode = META_TILE_BOTTOM; break; default: return; } do_tile_move (window, mode); } static void handle_toggle_maximized (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (META_WINDOW_MAXIMIZED (window)) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); else if (window->has_maximize_func) meta_window_maximize (window, META_MAXIMIZE_BOTH); } static void handle_maximize (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_maximize_func) meta_window_maximize (window, META_MAXIMIZE_BOTH); } static void handle_unmaximize (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->maximized_vertically || window->maximized_horizontally) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } static void handle_toggle_shaded (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->shaded) meta_window_unshade (window, event->time); else if (window->has_shade_func) meta_window_shade (window, event->time); } static void handle_close (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_close_func) meta_window_delete (window, event->time); } static void handle_minimize (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_minimize_func) meta_window_minimize (window); } static void handle_begin_move (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_move_func) { meta_window_begin_grab_op (window, META_GRAB_OP_KEYBOARD_MOVING, FALSE, event->time); } } static void handle_begin_resize (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->has_resize_func) { meta_window_begin_grab_op (window, META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN, FALSE, event->time); } } static void handle_toggle_on_all_workspaces (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { if (window->on_all_workspaces_requested) meta_window_unstick (window); else meta_window_stick (window); } static void handle_move_to_workspace_last (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint which; MetaWorkspace *workspace; if (window->always_sticky) return; which = meta_workspace_manager_get_n_workspaces (workspace_manager) - 1; workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); meta_window_change_workspace (window, workspace); } static void handle_move_to_workspace (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; gint which = binding->handler->data; gboolean flip = (which < 0); MetaWorkspace *workspace; /* If which is zero or positive, it's a workspace number, and the window * should move to the workspace with that number. * * However, if it's negative, it's a direction with respect to the current * position; it's expressed as a member of the MetaMotionDirection enum, * all of whose members are negative. Such a change is called a flip. */ if (window->always_sticky) return; workspace = NULL; if (flip) { workspace = meta_workspace_get_neighbor (workspace_manager->active_workspace, which); } else { workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, which); } if (workspace) { /* Activate second, so the window is never unmapped */ meta_window_change_workspace (window, workspace); if (flip) { meta_topic (META_DEBUG_FOCUS, "Resetting mouse_mode to FALSE due to " "handle_move_to_workspace() call with flip set.\n"); meta_display_clear_mouse_mode (workspace->display); meta_workspace_activate_with_focus (workspace, window, event->time); } } else { /* We could offer to create it I suppose */ } } static void handle_move_to_monitor (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); gint which = binding->handler->data; MetaLogicalMonitor *current, *new; current = window->monitor; new = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, current, which); if (new == NULL) return; meta_window_move_to_monitor (window, new->number); } static void handle_raise_or_lower (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { /* Get window at pointer */ MetaWindow *above = NULL; /* Check if top */ if (meta_stack_get_top (window->display->stack) == window) { meta_window_lower (window); return; } /* else check if windows in same layer are intersecting it */ above = meta_stack_get_above (window->display->stack, window, TRUE); while (above) { MetaRectangle tmp, win_rect, above_rect; if (above->mapped && meta_window_should_be_showing (above)) { meta_window_get_frame_rect (window, &win_rect); meta_window_get_frame_rect (above, &above_rect); /* Check if obscured */ if (meta_rectangle_intersect (&win_rect, &above_rect, &tmp)) { meta_window_raise (window); return; } } above = meta_stack_get_above (window->display->stack, above, TRUE); } /* window is not obscured */ meta_window_lower (window); } static void handle_raise (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { meta_window_raise (window); } static void handle_lower (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { meta_window_lower (window); } static void handle_set_spew_mark (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { meta_verbose ("-- MARK MARK MARK MARK --\n"); } #ifdef HAVE_NATIVE_BACKEND static void handle_switch_vt (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { gint vt = binding->handler->data; GError *error = NULL; if (!meta_activate_vt (vt, &error)) { g_warning ("Failed to switch VT: %s", error->message); g_error_free (error); } } #endif /* HAVE_NATIVE_BACKEND */ static void handle_switch_monitor (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorSwitchConfigType config_type = meta_monitor_manager_get_switch_config (monitor_manager); if (!meta_monitor_manager_can_switch_config (monitor_manager)) return; config_type = (config_type + 1) % (META_MONITOR_SWITCH_CONFIG_UNKNOWN); meta_monitor_manager_switch_config (monitor_manager, config_type); } static void handle_rotate_monitor (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); meta_monitor_manager_rotate_monitor (monitor_manager); } static void handle_opacity (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { MetaKeyBindingAction action = meta_prefs_get_keybinding_action (binding->name); if (action == META_KEYBINDING_ACTION_DECREASE_OPACITY) { meta_window_decrease_opacity (window); } else { meta_window_increase_opacity (window); } } static void handle_restore_shortcuts (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer dummy) { ClutterInputDevice *source; if (!display->focus_window) return; source = clutter_event_get_source_device ((ClutterEvent *) event); meta_topic (META_DEBUG_KEYBINDINGS, "Restoring normal keyboard shortcuts\n"); meta_window_force_restore_shortcuts (display->focus_window, source); } /** * meta_keybindings_set_custom_handler: * @name: The name of the keybinding to set * @handler: (nullable): The new handler function * @user_data: User data to pass to the callback * @free_data: Will be called when this handler is overridden. * * Allows users to register a custom handler for a * builtin key binding. * * Returns: %TRUE if the binding known as @name was found, * %FALSE otherwise. */ gboolean meta_keybindings_set_custom_handler (const gchar *name, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data) { MetaKeyHandler *key_handler = HANDLER (name); if (!key_handler) return FALSE; if (key_handler->user_data_free_func && key_handler->user_data) key_handler->user_data_free_func (key_handler->user_data); key_handler->func = handler; key_handler->user_data = user_data; key_handler->user_data_free_func = free_data; return TRUE; } int meta_keybindings_get_mouse_zoom_modifiers (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; return keys->mouse_zoom_modifiers; } uint meta_keybindings_get_ignored_modifier_mask (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; return keys->ignored_modifier_mask; } static void init_builtin_key_bindings (MetaDisplay *display) { GSettings *common_keybindings = g_settings_new (SCHEMA_COMMON_KEYBINDINGS); GSettings *mutter_keybindings = g_settings_new (SCHEMA_MUTTER_KEYBINDINGS); GSettings *mutter_wayland_keybindings = g_settings_new (SCHEMA_MUTTER_WAYLAND_KEYBINDINGS); add_builtin_keybinding (display, "switch-to-workspace-1", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_1, handle_switch_to_workspace, 0); add_builtin_keybinding (display, "switch-to-workspace-2", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_2, handle_switch_to_workspace, 1); add_builtin_keybinding (display, "switch-to-workspace-3", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_3, handle_switch_to_workspace, 2); add_builtin_keybinding (display, "switch-to-workspace-4", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_4, handle_switch_to_workspace, 3); add_builtin_keybinding (display, "switch-to-workspace-5", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_5, handle_switch_to_workspace, 4); add_builtin_keybinding (display, "switch-to-workspace-6", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_6, handle_switch_to_workspace, 5); add_builtin_keybinding (display, "switch-to-workspace-7", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_7, handle_switch_to_workspace, 6); add_builtin_keybinding (display, "switch-to-workspace-8", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_8, handle_switch_to_workspace, 7); add_builtin_keybinding (display, "switch-to-workspace-9", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_9, handle_switch_to_workspace, 8); add_builtin_keybinding (display, "switch-to-workspace-10", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_10, handle_switch_to_workspace, 9); add_builtin_keybinding (display, "switch-to-workspace-11", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_11, handle_switch_to_workspace, 10); add_builtin_keybinding (display, "switch-to-workspace-12", common_keybindings, META_KEY_BINDING_NONE | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_WORKSPACE_12, handle_switch_to_workspace, 11); add_builtin_keybinding (display, "switch-to-workspace-left", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_WORKSPACE_LEFT, handle_switch_to_workspace, META_MOTION_LEFT); add_builtin_keybinding (display, "switch-to-workspace-right", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_WORKSPACE_RIGHT, handle_switch_to_workspace, META_MOTION_RIGHT); add_builtin_keybinding (display, "switch-to-workspace-up", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_WORKSPACE_UP, handle_switch_to_workspace, META_MOTION_UP); add_builtin_keybinding (display, "switch-to-workspace-down", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_WORKSPACE_DOWN, handle_switch_to_workspace, META_MOTION_DOWN); /* The ones which have inverses. These can't be bound to any keystroke * containing Shift because Shift will invert their "backward" state. * * TODO: "NORMAL" and "DOCKS" should be renamed to the same name as their * action, for obviousness. * * TODO: handle_switch and handle_cycle should probably really be the * same function checking a bit in the parameter for difference. */ add_builtin_keybinding (display, "switch-group", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SWITCH_GROUP, handle_switch, META_TAB_LIST_GROUP); add_builtin_keybinding (display, "switch-group-backward", common_keybindings, META_KEY_BINDING_IS_REVERSED, META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD, handle_switch, META_TAB_LIST_GROUP); // add_builtin_keybinding (display, // "switch-applications", // common_keybindings, // META_KEY_BINDING_NONE, // META_KEYBINDING_ACTION_SWITCH_APPLICATIONS, // handle_switch, META_TAB_LIST_NORMAL); // add_builtin_keybinding (display, // "switch-applications-backward", // common_keybindings, // META_KEY_BINDING_IS_REVERSED, // META_KEYBINDING_ACTION_SWITCH_APPLICATIONS_BACKWARD, // handle_switch, META_TAB_LIST_NORMAL); add_builtin_keybinding (display, "switch-windows", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SWITCH_WINDOWS, handle_switch, META_TAB_LIST_NORMAL); add_builtin_keybinding (display, "switch-windows-backward", common_keybindings, META_KEY_BINDING_IS_REVERSED, META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD, handle_switch, META_TAB_LIST_NORMAL); add_builtin_keybinding (display, "switch-panels", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SWITCH_PANELS, handle_switch, META_TAB_LIST_DOCKS); add_builtin_keybinding (display, "switch-panels-backward", common_keybindings, META_KEY_BINDING_IS_REVERSED, META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD, handle_switch, META_TAB_LIST_DOCKS); // add_builtin_keybinding (display, // "cycle-group", // common_keybindings, // META_KEY_BINDING_NONE, // META_KEYBINDING_ACTION_CYCLE_GROUP, // handle_cycle, META_TAB_LIST_GROUP); // add_builtin_keybinding (display, // "cycle-group-backward", // common_keybindings, // META_KEY_BINDING_IS_REVERSED, // META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD, // handle_cycle, META_TAB_LIST_GROUP); // add_builtin_keybinding (display, // "cycle-windows", // common_keybindings, // META_KEY_BINDING_NONE, // META_KEYBINDING_ACTION_CYCLE_WINDOWS, // handle_cycle, META_TAB_LIST_NORMAL); // add_builtin_keybinding (display, // "cycle-windows-backward", // common_keybindings, // META_KEY_BINDING_IS_REVERSED, // META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD, // handle_cycle, META_TAB_LIST_NORMAL); // add_builtin_keybinding (display, // "cycle-panels", // common_keybindings, // META_KEY_BINDING_NONE, // META_KEYBINDING_ACTION_CYCLE_PANELS, // handle_cycle, META_TAB_LIST_DOCKS); // add_builtin_keybinding (display, // "cycle-panels-backward", // common_keybindings, // META_KEY_BINDING_IS_REVERSED, // META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD, // handle_cycle, META_TAB_LIST_DOCKS); /***********************************/ add_builtin_keybinding (display, "show-desktop", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SHOW_DESKTOP, handle_show_desktop, 0); // add_builtin_keybinding (display, // "panel-main-menu", // common_keybindings, // META_KEY_BINDING_NONE, // META_KEYBINDING_ACTION_PANEL_MAIN_MENU, // handle_panel, META_KEYBINDING_ACTION_PANEL_MAIN_MENU); add_builtin_keybinding (display, "panel-run-dialog", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_PANEL_RUN_DIALOG, handle_panel, META_KEYBINDING_ACTION_PANEL_RUN_DIALOG); add_builtin_keybinding (display, "set-spew-mark", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SET_SPEW_MARK, handle_set_spew_mark, 0); add_builtin_keybinding (display, "toggle-recording", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_TOGGLE_RECORDING, NULL, 0); add_builtin_keybinding (display, "switch-monitor", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_SWITCH_MONITOR, handle_switch_monitor, 0); add_builtin_keybinding (display, "rotate-monitor", common_keybindings, META_KEY_BINDING_NONE, META_KEYBINDING_ACTION_ROTATE_MONITOR, handle_rotate_monitor, 0); #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { add_builtin_keybinding (display, "switch-to-session-1", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 1); add_builtin_keybinding (display, "switch-to-session-2", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 2); add_builtin_keybinding (display, "switch-to-session-3", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 3); add_builtin_keybinding (display, "switch-to-session-4", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 4); add_builtin_keybinding (display, "switch-to-session-5", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 5); add_builtin_keybinding (display, "switch-to-session-6", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 6); add_builtin_keybinding (display, "switch-to-session-7", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 7); add_builtin_keybinding (display, "switch-to-session-8", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 8); add_builtin_keybinding (display, "switch-to-session-9", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 9); add_builtin_keybinding (display, "switch-to-session-10", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 10); add_builtin_keybinding (display, "switch-to-session-11", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 11); add_builtin_keybinding (display, "switch-to-session-12", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_switch_vt, 12); } #endif /* HAVE_NATIVE_BACKEND */ add_builtin_keybinding (display, "restore-shortcuts", mutter_wayland_keybindings, META_KEY_BINDING_NON_MASKABLE, META_KEYBINDING_ACTION_NONE, handle_restore_shortcuts, 0); /************************ PER WINDOW BINDINGS ************************/ /* These take a window as an extra parameter; they have no effect * if no window is active. */ add_builtin_keybinding (display, "activate-window-menu", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU, handle_activate_window_menu, 0); add_builtin_keybinding (display, "toggle-fullscreen", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN, handle_toggle_fullscreen, 0); add_builtin_keybinding (display, "toggle-maximized", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED, handle_toggle_maximized, 0); add_builtin_keybinding (display, "push-tile-left", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_PUSH_TILE_LEFT, handle_tile_action, META_TILE_LEFT); add_builtin_keybinding (display, "push-tile-right", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_PUSH_TILE_RIGHT, handle_tile_action, META_TILE_RIGHT); add_builtin_keybinding (display, "push-tile-up", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_PUSH_TILE_UP, handle_tile_action, META_TILE_TOP); add_builtin_keybinding (display, "push-tile-down", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_PUSH_TILE_DOWN, handle_tile_action, META_TILE_BOTTOM); add_builtin_keybinding (display, "toggle-above", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_TOGGLE_ABOVE, handle_toggle_above, 0); add_builtin_keybinding (display, "maximize", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MAXIMIZE, handle_maximize, 0); add_builtin_keybinding (display, "unmaximize", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_UNMAXIMIZE, handle_unmaximize, 0); add_builtin_keybinding (display, "toggle-shaded", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_TOGGLE_SHADED, handle_toggle_shaded, 0); add_builtin_keybinding (display, "minimize", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MINIMIZE, handle_minimize, 0); add_builtin_keybinding (display, "close", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_CLOSE, handle_close, 0); add_builtin_keybinding (display, "begin-move", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_BEGIN_MOVE, handle_begin_move, 0); add_builtin_keybinding (display, "begin-resize", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_BEGIN_RESIZE, handle_begin_resize, 0); add_builtin_keybinding (display, "toggle-on-all-workspaces", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_TOGGLE_ON_ALL_WORKSPACES, handle_toggle_on_all_workspaces, 0); add_builtin_keybinding (display, "move-to-workspace-1", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_1, handle_move_to_workspace, 0); add_builtin_keybinding (display, "move-to-workspace-2", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_2, handle_move_to_workspace, 1); add_builtin_keybinding (display, "move-to-workspace-3", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_3, handle_move_to_workspace, 2); add_builtin_keybinding (display, "move-to-workspace-4", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_4, handle_move_to_workspace, 3); add_builtin_keybinding (display, "move-to-workspace-5", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_5, handle_move_to_workspace, 4); add_builtin_keybinding (display, "move-to-workspace-6", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_6, handle_move_to_workspace, 5); add_builtin_keybinding (display, "move-to-workspace-7", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_7, handle_move_to_workspace, 6); add_builtin_keybinding (display, "move-to-workspace-8", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_8, handle_move_to_workspace, 7); add_builtin_keybinding (display, "move-to-workspace-9", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_9, handle_move_to_workspace, 8); add_builtin_keybinding (display, "move-to-workspace-10", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_10, handle_move_to_workspace, 9); add_builtin_keybinding (display, "move-to-workspace-11", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_11, handle_move_to_workspace, 10); add_builtin_keybinding (display, "move-to-workspace-12", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12, handle_move_to_workspace, 11); add_builtin_keybinding (display, "move-to-workspace-left", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LEFT, handle_move_to_workspace, META_MOTION_LEFT); add_builtin_keybinding (display, "move-to-workspace-right", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_RIGHT, handle_move_to_workspace, META_MOTION_RIGHT); add_builtin_keybinding (display, "move-to-workspace-up", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_UP, handle_move_to_workspace, META_MOTION_UP); add_builtin_keybinding (display, "move-to-workspace-down", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_DOWN, handle_move_to_workspace, META_MOTION_DOWN); add_builtin_keybinding (display, "move-to-monitor-left", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_LEFT, handle_move_to_monitor, META_DISPLAY_LEFT); add_builtin_keybinding (display, "move-to-monitor-right", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_RIGHT, handle_move_to_monitor, META_DISPLAY_RIGHT); add_builtin_keybinding (display, "move-to-monitor-down", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_DOWN, handle_move_to_monitor, META_DISPLAY_DOWN); add_builtin_keybinding (display, "move-to-monitor-up", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_UP, handle_move_to_monitor, META_DISPLAY_UP); add_builtin_keybinding (display, "raise-or-lower", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_RAISE_OR_LOWER, handle_raise_or_lower, 0); add_builtin_keybinding (display, "raise", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_RAISE, handle_raise, 0); add_builtin_keybinding (display, "lower", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_LOWER, handle_lower, 0); add_builtin_keybinding (display, "maximize-vertically", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY, handle_maximize_vertically, 0); add_builtin_keybinding (display, "maximize-horizontally", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY, handle_maximize_horizontally, 0); /* Handled already by toggle_above */ // add_builtin_keybinding (display, // "always-on-top", // common_keybindings, // META_KEY_BINDING_PER_WINDOW | // META_KEY_BINDING_IGNORE_AUTOREPEAT, // META_KEYBINDING_ACTION_ALWAYS_ON_TOP, // handle_always_on_top, 0); add_builtin_keybinding (display, "move-to-corner-nw", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_CORNER_NW, handle_move_to_corner_nw, 0); add_builtin_keybinding (display, "move-to-corner-ne", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_CORNER_NE, handle_move_to_corner_ne, 0); add_builtin_keybinding (display, "move-to-corner-sw", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_CORNER_SW, handle_move_to_corner_sw, 0); add_builtin_keybinding (display, "move-to-corner-se", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_CORNER_SE, handle_move_to_corner_se, 0); add_builtin_keybinding (display, "move-to-side-n", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_SIDE_N, handle_move_to_side_n, 0); add_builtin_keybinding (display, "move-to-side-s", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_SIDE_S, handle_move_to_side_s, 0); add_builtin_keybinding (display, "move-to-side-e", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_SIDE_E, handle_move_to_side_e, 0); add_builtin_keybinding (display, "move-to-side-w", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_SIDE_W, handle_move_to_side_w, 0); add_builtin_keybinding (display, "move-to-center", common_keybindings, META_KEY_BINDING_PER_WINDOW | META_KEY_BINDING_IGNORE_AUTOREPEAT, META_KEYBINDING_ACTION_MOVE_TO_CENTER, handle_move_to_center, 0); add_builtin_keybinding (display, "increase-opacity", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_INCREASE_OPACITY, handle_opacity, 0); add_builtin_keybinding (display, "decrease-opacity", common_keybindings, META_KEY_BINDING_PER_WINDOW, META_KEYBINDING_ACTION_DECREASE_OPACITY, handle_opacity, 0); g_object_unref (common_keybindings); g_object_unref (mutter_keybindings); g_object_unref (mutter_wayland_keybindings); } void meta_display_init_keys (MetaDisplay *display) { MetaKeyBindingManager *keys = &display->key_binding_manager; MetaBackend *backend = meta_get_backend (); MetaKeyHandler *handler; keys->backend = backend; /* Keybindings */ keys->ignored_modifier_mask = 0; keys->hyper_mask = 0; keys->super_mask = 0; keys->meta_mask = 0; keys->key_bindings = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_key_binding_free); keys->key_bindings_index = g_hash_table_new (NULL, NULL); reload_modmap (keys); key_handlers = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) key_handler_free); handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup ("overlay-key"); handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB; g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup ("locate-pointer-key"); handler->flags = META_KEY_BINDING_BUILTIN | META_KEY_BINDING_NO_AUTO_GRAB; g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup ("iso-next-group"); handler->flags = META_KEY_BINDING_BUILTIN; g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); handler = g_new0 (MetaKeyHandler, 1); handler->name = g_strdup ("external-grab"); handler->func = handle_external_grab; handler->default_func = handle_external_grab; g_hash_table_insert (key_handlers, g_strdup (handler->name), handler); external_grabs = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)meta_key_grab_free); init_builtin_key_bindings (display); rebuild_key_binding_table (keys); rebuild_special_bindings (keys); reload_combos (keys); update_window_grab_modifiers (keys); update_mouse_zoom_modifiers (keys); /* Keys are actually grabbed in meta_screen_grab_keys() */ meta_prefs_add_listener (prefs_changed_callback, display); g_signal_connect_swapped (backend, "keymap-changed", G_CALLBACK (reload_keybindings), display); g_signal_connect_swapped (backend, "keymap-layout-group-changed", G_CALLBACK (reload_keybindings), display); } muffin-6.4.1/src/core/meta-clipboard-manager.h0000664000175000017500000000205714723361714020131 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_CLIPBOARD_MANAGER_H #define META_CLIPBOARD_MANAGER_H #include "core/display-private.h" void meta_clipboard_manager_init (MetaDisplay *display); void meta_clipboard_manager_shutdown (MetaDisplay *display); #endif /* META_CLIPBOARD_MANAGER_H */ muffin-6.4.1/src/core/util-private.h0000664000175000017500000000272414723361714016264 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter utilities */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_UTIL_PRIVATE_H #define META_UTIL_PRIVATE_H #include #include "meta/util.h" #include "meta/common.h" /* META_EXPORT_TEST should be used to export symbols that are exported only * for testability purposes */ #define META_EXPORT_TEST META_EXPORT void meta_set_debugging (gboolean setting); void meta_set_syncing (gboolean setting); void meta_set_replace_current_wm (gboolean setting); void meta_set_is_wayland_compositor (gboolean setting); char * meta_generate_random_id (GRand *rand, int length); #endif muffin-6.4.1/src/core/util.c0000664000175000017500000005470014723361714014610 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:util * @title: Utility functions * @short_description: Miscellaneous utility functions */ #define _POSIX_C_SOURCE 200112L /* for fdopen() */ #include "config.h" #include "core/util-private.h" #include #include #include #include #include #include /* must explicitly be included for Solaris; #326746 */ #include /* Just for the definition of the various gravities */ #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/common.h" #include "meta/main.h" #ifdef WITH_VERBOSE_MODE static void meta_topic_real_valist (MetaDebugTopic topic, const char *format, va_list args) G_GNUC_PRINTF(2, 0); #endif static gboolean meta_later_remove_from_list (guint later_id, GSList **laters_list); static gint verbose_topics = 0; static gboolean is_debugging = FALSE; static gboolean replace_current = FALSE; static int no_prefix = 0; static gboolean is_wayland_compositor = FALSE; #ifdef WITH_VERBOSE_MODE static FILE* logfile = NULL; static void ensure_logfile (void) { if (logfile == NULL && g_getenv ("MUTTER_USE_LOGFILE")) { char *filename = NULL; char *tmpl; int fd; GError *err; tmpl = g_strdup_printf ("mutter-%d-debug-log-XXXXXX", (int) getpid ()); err = NULL; fd = g_file_open_tmp (tmpl, &filename, &err); g_free (tmpl); if (err != NULL) { meta_warning ("Failed to open debug log: %s\n", err->message); g_error_free (err); return; } logfile = fdopen (fd, "w"); if (logfile == NULL) { meta_warning ("Failed to fdopen() log file %s: %s\n", filename, strerror (errno)); close (fd); } else { g_printerr ("Opened log file %s\n", filename); } g_free (filename); } } #endif gboolean meta_is_verbose (void) { return verbose_topics != 0; } void meta_set_verbose (gboolean setting) { #ifndef WITH_VERBOSE_MODE if (setting) meta_fatal (_("Mutter was compiled without support for verbose mode\n")); #else if (setting) ensure_logfile (); #endif if (setting) meta_add_verbose_topic (META_DEBUG_VERBOSE); else meta_remove_verbose_topic (META_DEBUG_VERBOSE); } /** * meta_add_verbose_topic: * @topic: Topic for which logging will be started * * Ensure log messages for the given topic @topic * will be printed. */ void meta_add_verbose_topic (MetaDebugTopic topic) { if (verbose_topics == META_DEBUG_VERBOSE) return; if (topic == META_DEBUG_VERBOSE) verbose_topics = META_DEBUG_VERBOSE; else verbose_topics |= topic; } /** * meta_remove_verbose_topic: * @topic: Topic for which logging will be stopped * * Stop printing log messages for the given topic @topic. Note * that this method does not stack with meta_add_verbose_topic(); * i.e. if two calls to meta_add_verbose_topic() for the same * topic are made, one call to meta_remove_verbose_topic() will * remove it. */ void meta_remove_verbose_topic (MetaDebugTopic topic) { if (topic == META_DEBUG_VERBOSE) verbose_topics = 0; else verbose_topics &= ~topic; } gboolean meta_is_debugging (void) { return is_debugging; } void meta_set_debugging (gboolean setting) { #ifdef WITH_VERBOSE_MODE if (setting) ensure_logfile (); #endif is_debugging = setting; } gboolean meta_get_replace_current_wm (void) { return replace_current; } void meta_set_replace_current_wm (gboolean setting) { replace_current = setting; } gboolean meta_is_wayland_compositor (void) { return is_wayland_compositor; } void meta_set_is_wayland_compositor (gboolean value) { is_wayland_compositor = value; } char * meta_g_utf8_strndup (const gchar *src, gsize n) { const gchar *s = src; while (n && *s) { s = g_utf8_next_char (s); n--; } return g_strndup (src, s - src); } static int utf8_fputs (const char *str, FILE *f) { char *l; int retval; l = g_locale_from_utf8 (str, -1, NULL, NULL, NULL); if (l == NULL) retval = fputs (str, f); /* just print it anyway, better than nothing */ else retval = fputs (l, f); g_free (l); return retval; } #ifdef WITH_VERBOSE_MODE void meta_debug_spew_real (const char *format, ...) { va_list args; gchar *str; FILE *out; g_return_if_fail (format != NULL); if (!is_debugging) return; va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); out = logfile ? logfile : stderr; if (no_prefix == 0) utf8_fputs ("Window manager: ", out); utf8_fputs (str, out); fflush (out); g_free (str); } #endif /* WITH_VERBOSE_MODE */ #ifdef WITH_VERBOSE_MODE void meta_verbose_real (const char *format, ...) { va_list args; va_start (args, format); meta_topic_real_valist (META_DEBUG_VERBOSE, format, args); va_end (args); } #endif /* WITH_VERBOSE_MODE */ #ifdef WITH_VERBOSE_MODE static const char* topic_name (MetaDebugTopic topic) { switch (topic) { case META_DEBUG_FOCUS: return "FOCUS"; case META_DEBUG_WORKAREA: return "WORKAREA"; case META_DEBUG_STACK: return "STACK"; case META_DEBUG_THEMES: return "THEMES"; case META_DEBUG_SM: return "SM"; case META_DEBUG_EVENTS: return "EVENTS"; case META_DEBUG_WINDOW_STATE: return "WINDOW_STATE"; case META_DEBUG_WINDOW_OPS: return "WINDOW_OPS"; case META_DEBUG_PLACEMENT: return "PLACEMENT"; case META_DEBUG_GEOMETRY: return "GEOMETRY"; case META_DEBUG_PING: return "PING"; case META_DEBUG_XINERAMA: return "XINERAMA"; case META_DEBUG_KEYBINDINGS: return "KEYBINDINGS"; case META_DEBUG_SYNC: return "SYNC"; case META_DEBUG_ERRORS: return "ERRORS"; case META_DEBUG_STARTUP: return "STARTUP"; case META_DEBUG_PREFS: return "PREFS"; case META_DEBUG_GROUPS: return "GROUPS"; case META_DEBUG_RESIZING: return "RESIZING"; case META_DEBUG_SHAPES: return "SHAPES"; case META_DEBUG_COMPOSITOR: return "COMPOSITOR"; case META_DEBUG_EDGE_RESISTANCE: return "EDGE_RESISTANCE"; case META_DEBUG_DBUS: return "DBUS"; case META_DEBUG_INPUT: return "INPUT"; case META_DEBUG_VERBOSE: return "VERBOSE"; } return "WM"; } static int sync_count = 0; static void meta_topic_real_valist (MetaDebugTopic topic, const char *format, va_list args) { gchar *str; FILE *out; g_return_if_fail (format != NULL); if (verbose_topics == 0 || (topic == META_DEBUG_VERBOSE && verbose_topics != META_DEBUG_VERBOSE) || (!(verbose_topics & topic))) return; str = g_strdup_vprintf (format, args); out = logfile ? logfile : stderr; if (no_prefix == 0) fprintf (out, "%s: ", topic_name (topic)); if (topic == META_DEBUG_SYNC) { ++sync_count; fprintf (out, "%d: ", sync_count); } utf8_fputs (str, out); fflush (out); g_free (str); } void meta_topic_real (MetaDebugTopic topic, const char *format, ...) { va_list args; va_start (args, format); meta_topic_real_valist (topic, format, args); va_end (args); } #endif /* WITH_VERBOSE_MODE */ void meta_bug (const char *format, ...) { va_list args; gchar *str; FILE *out; g_return_if_fail (format != NULL); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif if (no_prefix == 0) utf8_fputs ("Bug in window manager: ", out); utf8_fputs (str, out); fflush (out); g_free (str); /* stop us in a debugger */ abort (); } void meta_warning (const char *format, ...) { va_list args; gchar *str; FILE *out; g_return_if_fail (format != NULL); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif if (no_prefix == 0) utf8_fputs ("Window manager warning: ", out); utf8_fputs (str, out); fflush (out); g_free (str); } void meta_fatal (const char *format, ...) { va_list args; gchar *str; FILE *out; g_warn_if_fail (format); if (!format) meta_exit (META_EXIT_ERROR); va_start (args, format); str = g_strdup_vprintf (format, args); va_end (args); #ifdef WITH_VERBOSE_MODE out = logfile ? logfile : stderr; #else out = stderr; #endif if (no_prefix == 0) utf8_fputs ("Window manager error: ", out); utf8_fputs (str, out); fflush (out); g_free (str); meta_exit (META_EXIT_ERROR); } void meta_push_no_msg_prefix (void) { ++no_prefix; } void meta_pop_no_msg_prefix (void) { g_return_if_fail (no_prefix > 0); --no_prefix; } void meta_exit (MetaExitCode code) { exit (code); } gint meta_unsigned_long_equal (gconstpointer v1, gconstpointer v2) { return *((const gulong*) v1) == *((const gulong*) v2); } guint meta_unsigned_long_hash (gconstpointer v) { gulong val = * (const gulong *) v; /* I'm not sure this works so well. */ #if GLIB_SIZEOF_LONG > 4 return (guint) (val ^ (val >> 32)); #else return val; #endif } const char* meta_gravity_to_string (MetaGravity gravity) { switch (gravity) { case META_GRAVITY_NORTH_WEST: return "META_GRAVITY_NORTH_WEST"; break; case META_GRAVITY_NORTH: return "META_GRAVITY_NORTH"; break; case META_GRAVITY_NORTH_EAST: return "META_GRAVITY_NORTH_EAST"; break; case META_GRAVITY_WEST: return "META_GRAVITY_WEST"; break; case META_GRAVITY_CENTER: return "META_GRAVITY_CENTER"; break; case META_GRAVITY_EAST: return "META_GRAVITY_EAST"; break; case META_GRAVITY_SOUTH_WEST: return "META_GRAVITY_SOUTH_WEST"; break; case META_GRAVITY_SOUTH: return "META_GRAVITY_SOUTH"; break; case META_GRAVITY_SOUTH_EAST: return "META_GRAVITY_SOUTH_EAST"; break; case META_GRAVITY_STATIC: return "META_GRAVITY_STATIC"; break; default: return "META_GRAVITY_NORTH_WEST"; break; } } char* meta_external_binding_name_for_action (guint keybinding_action) { return g_strdup_printf ("external-grab-%u", keybinding_action); } /* Command line arguments are passed in the locale encoding; in almost * all cases, we'd hope that is UTF-8 and no conversion is necessary. * If it's not UTF-8, then it's possible that the message isn't * representable in the locale encoding. */ static void append_argument (GPtrArray *args, const char *arg) { char *locale_arg = g_locale_from_utf8 (arg, -1, NULL, NULL, NULL); /* This is cheesy, but it's better to have a few ???'s in the dialog * for an unresponsive application than no dialog at all appear */ if (!locale_arg) locale_arg = g_strdup ("???"); g_ptr_array_add (args, locale_arg); } /** * meta_show_dialog: (skip) * @message: message * @timeout: timeout * @display: display * @ok_text: text for Ok button * @cancel_text: text for Cancel button * @transient_for: window XID of parent * @columns: columns * @entries: entries * */ GPid meta_show_dialog (const char *type, const char *message, const char *timeout, const char *display, const char *ok_text, const char *cancel_text, const int transient_for, GSList *columns, GSList *entries) { GError *error = NULL; GSList *tmp; GPid child_pid; GPtrArray *args; args = g_ptr_array_new (); append_argument (args, "zenity"); append_argument (args, type); if (display) { append_argument (args, "--display"); append_argument (args, display); } append_argument (args, "--title"); append_argument (args, ""); append_argument (args, "--text"); append_argument (args, message); if (timeout) { append_argument (args, "--timeout"); append_argument (args, timeout); } if (ok_text) { append_argument (args, "--ok-label"); append_argument (args, ok_text); } if (cancel_text) { append_argument (args, "--cancel-label"); append_argument (args, cancel_text); } tmp = columns; while (tmp) { append_argument (args, "--column"); append_argument (args, tmp->data); tmp = tmp->next; } tmp = entries; while (tmp) { append_argument (args, tmp->data); tmp = tmp->next; } if (transient_for) { gchar *env = g_strdup_printf("%d", transient_for); setenv ("WINDOWID", env, 1); g_free (env); append_argument (args, "--modal"); } g_ptr_array_add (args, NULL); /* NULL-terminate */ g_spawn_async ( "/", (gchar**) args->pdata, NULL, G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &child_pid, &error ); if (transient_for) unsetenv ("WINDOWID"); g_ptr_array_free (args, TRUE); if (error) { meta_warning ("%s\n", error->message); g_error_free (error); } return child_pid; } /*************************************************************************** * Later functions: like idles but integrated with the Clutter repaint loop ***************************************************************************/ static guint last_later_id = 0; typedef struct { guint id; guint ref_count; MetaLaterType when; GSourceFunc func; gpointer data; GDestroyNotify notify; int source; gboolean run_once; } MetaLater; static GSList *laters[] = { NULL, /* META_LATER_RESIZE */ NULL, /* META_LATER_CALC_SHOWING */ NULL, /* META_LATER_CHECK_FULLSCREEN */ NULL, /* META_LATER_SYNC_STACK */ NULL, /* META_LATER_BEFORE_REDRAW */ NULL, /* META_LATER_IDLE */ }; /* This is a dummy timeline used to get the Clutter master clock running */ static ClutterTimeline *later_timeline; static guint later_repaint_func = 0; static void ensure_later_repaint_func (void); static void unref_later (MetaLater *later) { if (--later->ref_count == 0) { if (later->notify) { later->notify (later->data); later->notify = NULL; } g_slice_free (MetaLater, later); } } static void destroy_later (MetaLater *later) { g_clear_handle_id (&later->source, g_source_remove); later->func = NULL; unref_later (later); } #ifdef COGL_HAS_TRACING static const char * later_type_to_string (MetaLaterType when) { switch (when) { case META_LATER_RESIZE: return "Later (resize)"; case META_LATER_CALC_SHOWING: return "Later (calc-showing)"; case META_LATER_CHECK_FULLSCREEN: return "Later (check-fullscreen)"; case META_LATER_SYNC_STACK: return "Later (sync-stack)"; case META_LATER_BEFORE_REDRAW: return "Later (before-redraw)"; case META_LATER_IDLE: return "Later (idle)"; } return "unknown"; } #endif static gboolean call_later_func (MetaLater *later) { COGL_TRACE_BEGIN_SCOPED (later, later_type_to_string (later->when)); return later->func (later->data); } static void run_repaint_laters (GSList **laters_list) { GSList *laters_copy; GSList *l; laters_copy = NULL; for (l = *laters_list; l; l = l->next) { MetaLater *later = l->data; if (later->source == 0 || (later->when <= META_LATER_BEFORE_REDRAW && !later->run_once)) { later->ref_count++; laters_copy = g_slist_prepend (laters_copy, later); } } laters_copy = g_slist_reverse (laters_copy); for (l = laters_copy; l; l = l->next) { MetaLater *later = l->data; if (!later->func || !call_later_func (later)) meta_later_remove_from_list (later->id, laters_list); unref_later (later); } g_slist_free (laters_copy); } static gboolean run_all_repaint_laters (gpointer data) { guint i; GSList *l; gboolean keep_timeline_running = FALSE; for (i = 0; i < G_N_ELEMENTS (laters); i++) { run_repaint_laters (&laters[i]); } for (i = 0; i < G_N_ELEMENTS (laters); i++) { for (l = laters[i]; l; l = l->next) { MetaLater *later = l->data; if (later->source == 0) keep_timeline_running = TRUE; } } if (!keep_timeline_running) clutter_timeline_stop (later_timeline); /* Just keep the repaint func around - it's cheap if the lists are empty */ return TRUE; } static void ensure_later_repaint_func (void) { if (!later_timeline) later_timeline = clutter_timeline_new (G_MAXUINT); if (later_repaint_func == 0) later_repaint_func = clutter_threads_add_repaint_func (run_all_repaint_laters, NULL, NULL); /* Make sure the repaint function gets run */ clutter_timeline_start (later_timeline); } static gboolean call_idle_later (gpointer data) { MetaLater *later = data; if (!later->func (later->data)) { meta_later_remove (later->id); return FALSE; } else { later->run_once = TRUE; return TRUE; } } /** * meta_later_add: * @when: enumeration value determining the phase at which to run the callback * @func: callback to run later * @data: data to pass to the callback * @notify: function to call to destroy @data when it is no longer in use, or %NULL * * Sets up a callback to be called at some later time. @when determines the * particular later occasion at which it is called. This is much like g_idle_add(), * except that the functions interact properly with clutter event handling. * If a "later" function is added from a clutter event handler, and is supposed * to be run before the stage is redrawn, it will be run before that redraw * of the stage, not the next one. * * Return value: an integer ID (guaranteed to be non-zero) that can be used * to cancel the callback and prevent it from being run. */ guint meta_later_add (MetaLaterType when, GSourceFunc func, gpointer data, GDestroyNotify notify) { MetaLater *later = g_slice_new0 (MetaLater); later->id = ++last_later_id; later->ref_count = 1; later->when = when; later->func = func; later->data = data; later->notify = notify; laters[when] = g_slist_prepend (laters[when], later); switch (when) { case META_LATER_RESIZE: /* We add this one two ways - as a high-priority idle and as a * repaint func. If we are in a clutter event callback, the repaint * handler will get hit first, and we'll take care of this function * there so it gets called before the stage is redrawn, even if * we haven't gotten back to the main loop. Otherwise, the idle * handler will get hit first and we want to call this function * there so it will happen before GTK+ repaints. */ later->source = g_idle_add_full (META_PRIORITY_RESIZE, call_idle_later, later, NULL); g_source_set_name_by_id (later->source, "[mutter] call_idle_later"); ensure_later_repaint_func (); break; case META_LATER_CALC_SHOWING: case META_LATER_CHECK_FULLSCREEN: case META_LATER_SYNC_STACK: case META_LATER_BEFORE_REDRAW: ensure_later_repaint_func (); break; case META_LATER_IDLE: later->source = g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, call_idle_later, later, NULL); g_source_set_name_by_id (later->source, "[mutter] call_idle_later"); break; } return later->id; } static gboolean meta_later_remove_from_list (guint later_id, GSList **laters_list) { GSList *l; for (l = *laters_list; l; l = l->next) { MetaLater *later = l->data; if (later->id == later_id) { *laters_list = g_slist_delete_link (*laters_list, l); /* If this was a "repaint func" later, we just let the * repaint func run and get removed */ destroy_later (later); return TRUE; } } return FALSE; } /** * meta_later_remove: * @later_id: the integer ID returned from meta_later_add() * * Removes a callback added with meta_later_add() */ void meta_later_remove (guint later_id) { guint i; for (i = 0; i < G_N_ELEMENTS (laters); i++) { if (meta_later_remove_from_list (later_id, &laters[i])) return; } } MetaLocaleDirection meta_get_locale_direction (void) { switch (gtk_get_locale_direction ()) { case GTK_TEXT_DIR_LTR: return META_LOCALE_DIRECTION_LTR; case GTK_TEXT_DIR_RTL: return META_LOCALE_DIRECTION_RTL; default: g_assert_not_reached (); return 0; } } char * meta_generate_random_id (GRand *rand, int length) { char *id; int i; /* Generate a random string of printable ASCII characters. */ id = g_new0 (char, length + 1); for (i = 0; i < length; i++) id[i] = (char) g_rand_int_range (rand, 32, 127); return id; } void meta_add_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_add_debug_flags (debug_flags, draw_flags, pick_flags); } void meta_remove_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags) { clutter_remove_debug_flags (debug_flags, draw_flags, pick_flags); } /* eof util.c */ muffin-6.4.1/src/core/display-private.h0000664000175000017500000003635214723361714016760 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X display handler */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_DISPLAY_PRIVATE_H #define META_DISPLAY_PRIVATE_H #include "meta/display.h" #include #include #include #ifdef HAVE_STARTUP_NOTIFICATION #include #endif #include "clutter/clutter.h" #include "core/keybindings-private.h" #include "core/meta-gesture-tracker-private.h" #include "core/stack-tracker.h" #include "core/startup-notification-private.h" #include "meta/barrier.h" #include "meta/boxes.h" #include "meta/common.h" #include "meta/meta-selection.h" #include "meta/prefs.h" typedef struct _MetaBell MetaBell; typedef struct _MetaStack MetaStack; typedef struct _MetaUISlave MetaUISlave; typedef struct MetaEdgeResistanceData MetaEdgeResistanceData; #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ /* This is basically a bogus number, just has to be large enough * to handle the expected case of the alt+tab operation, where * we want to ignore serials from UnmapNotify on the tab popup, * and the LeaveNotify/EnterNotify from the pointer ungrab. It * also has to be big enough to hold ignored serials from the point * where we reshape the stage to the point where we get events back. */ #define N_IGNORED_CROSSING_SERIALS 10 typedef enum { /* Normal interaction where you're interacting with windows. * Events go to windows normally. */ META_EVENT_ROUTE_NORMAL, /* In a window operation like moving or resizing. All events * goes to MetaWindow, but not to the actual client window. */ META_EVENT_ROUTE_WINDOW_OP, /* In a compositor grab operation. All events go to the * compositor plugin. */ META_EVENT_ROUTE_COMPOSITOR_GRAB, /* A Wayland application has a popup open. All events go to * the Wayland application. */ META_EVENT_ROUTE_WAYLAND_POPUP, /* The user is clicking on a window button. */ META_EVENT_ROUTE_FRAME_BUTTON, } MetaEventRoute; typedef void (* MetaDisplayWindowFunc) (MetaWindow *window, gpointer user_data); struct _MetaDisplay { GObject parent_instance; MetaX11Display *x11_display; int clutter_event_filter; /* Our best guess as to the "currently" focused window (that is, the * window that we expect will be focused at the point when the X * server processes our next request), and the serial of the request * or event that caused this. */ MetaWindow *focus_window; /* last timestamp passed to XSetInputFocus */ guint32 last_focus_time; /* last user interaction time in any app */ guint32 last_user_time; /* whether we're using mousenav (only relevant for sloppy&mouse focus modes; * !mouse_mode means "keynav mode") */ guint mouse_mode : 1; /* Helper var used when focus_new_windows setting is 'strict'; only * relevant in 'strict' mode and if the focus window is a terminal. * In that case, we don't allow new windows to take focus away from * a terminal, but if the user explicitly did something that should * allow a different window to gain focus (e.g. global keybinding or * clicking on a dock), then we will allow the transfer. */ guint allow_terminal_deactivation : 1; /*< private-ish >*/ GHashTable *stamps; GHashTable *wayland_windows; /* serials of leave/unmap events that may * correspond to an enter event we should * ignore */ unsigned long ignored_crossing_serials[N_IGNORED_CROSSING_SERIALS]; guint32 current_time; /* We maintain a sequence counter, incremented for each #MetaWindow * created. This is exposed by meta_window_get_stable_sequence() * but is otherwise not used inside mutter. * * It can be useful to plugins which want to sort windows in a * stable fashion. */ guint32 window_sequence_counter; /* Pings which we're waiting for a reply from */ GSList *pending_pings; /* Pending focus change */ guint focus_timeout_id; /* Pending autoraise */ guint autoraise_timeout_id; MetaWindow* autoraise_window; /* Event routing */ MetaEventRoute event_route; /* current window operation */ MetaGrabOp grab_op; MetaWindow *grab_window; int grab_button; int grab_anchor_root_x; int grab_anchor_root_y; MetaRectangle grab_anchor_window_pos; MetaTileMode grab_tile_mode; int grab_tile_monitor_number; int grab_latest_motion_x; int grab_latest_motion_y; guint grab_have_pointer : 1; guint grab_have_keyboard : 1; guint grab_frame_action : 1; MetaRectangle grab_initial_window_pos; int grab_initial_x, grab_initial_y; /* These are only relevant for */ gboolean grab_threshold_movement_reached; /* raise_on_click == FALSE. */ int64_t grab_last_moveresize_time; MetaEdgeResistanceData *grab_edge_resistance_data; unsigned int grab_last_user_action_was_snap; int grab_resize_timeout_id; MetaKeyBindingManager key_binding_manager; /* Monitor cache */ unsigned int monitor_cache_invalidated : 1; /* Opening the display */ unsigned int display_opening : 1; /* Closing down the display */ int closing; /* Managed by compositor.c */ MetaCompositor *compositor; MetaGestureTracker *gesture_tracker; ClutterEventSequence *pointer_emulating_sequence; ClutterActor *current_pad_osd; MetaStartupNotification *startup_notification; MetaCursor current_cursor; MetaStack *stack; MetaStackTracker *stack_tracker; guint tile_preview_timeout_id; guint preview_tile_mode : 4; GSList *startup_sequences; guint work_area_later; guint check_fullscreen_later; MetaBell *bell; MetaWorkspaceManager *workspace_manager; MetaSoundPlayer *sound_player; MetaSelectionSource *selection_source; GBytes *saved_clipboard; gchar *saved_clipboard_mimetype; MetaSelection *selection; gboolean desklets_above; }; struct _MetaDisplayClass { GObjectClass parent_class; }; #define XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) \ ( (( (time1) < (time2) ) && ( (time2) - (time1) < ((guint32)-1)/2 )) || \ (( (time1) > (time2) ) && ( (time1) - (time2) > ((guint32)-1)/2 )) \ ) /** * XSERVER_TIME_IS_BEFORE: * * See the docs for meta_display_xserver_time_is_before(). */ #define XSERVER_TIME_IS_BEFORE(time1, time2) \ ( (time1) == 0 || \ (XSERVER_TIME_IS_BEFORE_ASSUMING_REAL_TIMESTAMPS(time1, time2) && \ (time2) != 0) \ ) gboolean meta_display_open (void); void meta_display_manage_all_xwindows (MetaDisplay *display); void meta_display_unmanage_windows (MetaDisplay *display, guint32 timestamp); /* Utility function to compare the stacking of two windows */ int meta_display_stack_cmp (const void *a, const void *b); /* Each MetaWindow is uniquely identified by a 64-bit "stamp"; unlike a * a MetaWindow *, a stamp will never be recycled */ MetaWindow* meta_display_lookup_stamp (MetaDisplay *display, guint64 stamp); void meta_display_register_stamp (MetaDisplay *display, guint64 *stampp, MetaWindow *window); void meta_display_unregister_stamp (MetaDisplay *display, guint64 stamp); /* A "stack id" is a XID or a stamp */ #define META_STACK_ID_IS_X11(id) ((id) < G_GUINT64_CONSTANT(0x100000000)) META_EXPORT_TEST MetaWindow* meta_display_lookup_stack_id (MetaDisplay *display, guint64 stack_id); /* for debug logging only; returns a human-description of the stack * ID - a small number of buffers are recycled, so the result must * be used immediately or copied */ const char *meta_display_describe_stack_id (MetaDisplay *display, guint64 stack_id); void meta_display_register_wayland_window (MetaDisplay *display, MetaWindow *window); void meta_display_unregister_wayland_window (MetaDisplay *display, MetaWindow *window); void meta_display_notify_window_created (MetaDisplay *display, MetaWindow *window); MetaDisplay* meta_display_for_x_display (Display *xdisplay); META_EXPORT_TEST MetaDisplay* meta_get_display (void); void meta_display_reload_cursor (MetaDisplay *display); void meta_display_update_cursor (MetaDisplay *display); void meta_display_check_threshold_reached (MetaDisplay *display, int x, int y); void meta_display_grab_window_buttons (MetaDisplay *display, Window xwindow); void meta_display_ungrab_window_buttons (MetaDisplay *display, Window xwindow); void meta_display_grab_focus_window_button (MetaDisplay *display, MetaWindow *window); void meta_display_ungrab_focus_window_button (MetaDisplay *display, MetaWindow *window); /* Next function is defined in edge-resistance.c */ void meta_display_cleanup_edges (MetaDisplay *display); /* utility goo */ const char* meta_event_mode_to_string (int m); const char* meta_event_detail_to_string (int d); void meta_display_queue_retheme_all_windows (MetaDisplay *display); void meta_display_ping_window (MetaWindow *window, guint32 serial); void meta_display_pong_for_serial (MetaDisplay *display, guint32 serial); MetaGravity meta_resize_gravity_from_grab_op (MetaGrabOp op); gboolean meta_grab_op_is_moving (MetaGrabOp op); gboolean meta_grab_op_is_resizing (MetaGrabOp op); gboolean meta_grab_op_is_mouse (MetaGrabOp op); gboolean meta_grab_op_is_keyboard (MetaGrabOp op); void meta_display_queue_autoraise_callback (MetaDisplay *display, MetaWindow *window); void meta_display_remove_autoraise_callback (MetaDisplay *display); void meta_display_overlay_key_activate (MetaDisplay *display); void meta_display_accelerator_activate (MetaDisplay *display, guint action, ClutterKeyEvent *event); gboolean meta_display_modifiers_accelerator_activate (MetaDisplay *display); void meta_display_sync_wayland_input_focus (MetaDisplay *display); void meta_display_update_focus_window (MetaDisplay *display, MetaWindow *window); void meta_display_sanity_check_timestamps (MetaDisplay *display, guint32 timestamp); gboolean meta_display_timestamp_too_old (MetaDisplay *display, guint32 *timestamp); void meta_display_remove_pending_pings_for_window (MetaDisplay *display, MetaWindow *window); MetaGestureTracker * meta_display_get_gesture_tracker (MetaDisplay *display); gboolean meta_display_show_restart_message (MetaDisplay *display, const char *message); gboolean meta_display_request_restart (MetaDisplay *display); gboolean meta_display_show_resize_popup (MetaDisplay *display, gboolean show, MetaRectangle *rect, int display_w, int display_h); void meta_set_is_restart (gboolean whether); void meta_display_cancel_touch (MetaDisplay *display); gboolean meta_display_windows_are_interactable (MetaDisplay *display); void meta_display_show_tablet_mapping_notification (MetaDisplay *display, ClutterInputDevice *pad, const gchar *pretty_name); void meta_display_notify_pad_group_switch (MetaDisplay *display, ClutterInputDevice *pad, const gchar *pretty_name, guint n_group, guint n_mode, guint n_modes); void meta_display_foreach_window (MetaDisplay *display, MetaListWindowsFlags flags, MetaDisplayWindowFunc func, gpointer data); void meta_display_restacked (MetaDisplay *display); void meta_display_update_tile_preview (MetaDisplay *display, gboolean delay); void meta_display_hide_tile_preview (MetaDisplay *display); gboolean meta_display_apply_startup_properties (MetaDisplay *display, MetaWindow *window); void meta_display_queue_workarea_recalc (MetaDisplay *display); void meta_display_queue_check_fullscreen (MetaDisplay *display); MetaWindow *meta_display_get_window_from_id (MetaDisplay *display, uint64_t window_id); uint64_t meta_display_generate_window_id (MetaDisplay *display); void meta_display_init_x11 (MetaDisplay *display, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean meta_display_init_x11_finish (MetaDisplay *display, GAsyncResult *result, GError **error); void meta_display_shutdown_x11 (MetaDisplay *display); void meta_display_a11y_zoom (MetaDisplay *display, gboolean in); const gchar *meta_display_get_tile_mode_str (MetaTileMode mode); #endif muffin-6.4.1/src/core/constraints.h0000664000175000017500000000267414723361714016212 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter size/position constraints */ /* * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_CONSTRAINTS_H #define META_CONSTRAINTS_H #include "core/frame.h" #include "core/window-private.h" #include "meta/util.h" void meta_window_constrain (MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity resize_gravity, const MetaRectangle *orig, MetaRectangle *new, MetaRectangle *intermediate, int *rel_x, int *rel_y); #endif /* META_CONSTRAINTS_H */ muffin-6.4.1/src/core/keybindings-private.h0000664000175000017500000001213714723361714017614 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file keybindings.h Grab and ungrab keys, and process the key events * * Performs global X grabs on the keys we need to be told about, like * the one to close a window. It also deals with incoming key events. */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_KEYBINDINGS_PRIVATE_H #define META_KEYBINDINGS_PRIVATE_H #include #include #include "core/meta-accel-parse.h" #include "meta/keybindings.h" typedef struct _MetaKeyHandler MetaKeyHandler; struct _MetaKeyHandler { char *name; MetaKeyHandlerFunc func; MetaKeyHandlerFunc default_func; gint data, flags; gpointer user_data; GDestroyNotify user_data_free_func; }; typedef struct _MetaResolvedKeyCombo { xkb_keycode_t *keycodes; int len; xkb_mod_mask_t mask; } MetaResolvedKeyCombo; /** * MetaKeyCombo: * @keysym: keysym * @keycode: keycode * @modifiers: modifiers */ struct _MetaKeyCombo { unsigned int keysym; unsigned int keycode; MetaVirtualModifier modifiers; }; struct _MetaKeyBinding { const char *name; MetaKeyCombo combo; MetaResolvedKeyCombo resolved_combo; gint flags; MetaKeyHandler *handler; }; typedef struct { char *name; GSettings *settings; MetaKeyBindingAction action; /* * A list of MetaKeyCombos. Each of them is bound to * this keypref. If one has keysym==modifiers==0, it is * ignored. */ GSList *combos; /* for keybindings not added with meta_display_add_keybinding() */ gboolean builtin:1; } MetaKeyPref; typedef struct _MetaKeyBindingKeyboardLayout { struct xkb_keymap *keymap; xkb_layout_index_t index; xkb_level_index_t n_levels; } MetaKeyBindingKeyboardLayout; typedef struct { MetaBackend *backend; GHashTable *key_bindings; GHashTable *key_bindings_index; xkb_mod_mask_t ignored_modifier_mask; xkb_mod_mask_t hyper_mask; xkb_mod_mask_t virtual_hyper_mask; xkb_mod_mask_t super_mask; xkb_mod_mask_t virtual_super_mask; xkb_mod_mask_t meta_mask; xkb_mod_mask_t virtual_meta_mask; MetaKeyCombo overlay_key_combo; MetaResolvedKeyCombo overlay_resolved_key_combo; MetaKeyCombo locate_pointer_key_combo; MetaResolvedKeyCombo locate_pointer_resolved_key_combo; MetaResolvedKeyCombo iso_next_group_combo[2]; int n_iso_next_group_combos; /* * A primary layout, and an optional secondary layout for when the * primary layout does not use the latin alphabet. */ MetaKeyBindingKeyboardLayout active_layouts[2]; /* Alt+click button grabs */ ClutterModifierType window_grab_modifiers; ClutterModifierType mouse_zoom_modifiers; } MetaKeyBindingManager; void meta_display_init_keys (MetaDisplay *display); void meta_display_shutdown_keys (MetaDisplay *display); void meta_window_grab_keys (MetaWindow *window); void meta_window_ungrab_keys (MetaWindow *window); gboolean meta_window_grab_all_keys (MetaWindow *window, guint32 timestamp); void meta_window_ungrab_all_keys (MetaWindow *window, guint32 timestamp); gboolean meta_keybindings_process_event (MetaDisplay *display, MetaWindow *window, const ClutterEvent *event); int meta_keybindings_get_mouse_zoom_modifiers (MetaDisplay *display); ClutterModifierType meta_display_get_window_grab_modifiers (MetaDisplay *display); uint meta_keybindings_get_ignored_modifier_mask (MetaDisplay *display); gboolean meta_prefs_add_keybinding (const char *name, GSettings *settings, const gchar **bindings, MetaKeyBindingAction action, MetaKeyBindingFlags flags); gboolean meta_prefs_remove_keybinding (const char *name); GList *meta_prefs_get_keybindings (void); void meta_prefs_get_overlay_binding (MetaKeyCombo *combo); void meta_prefs_get_locate_pointer_binding (MetaKeyCombo *combo); const char *meta_prefs_get_iso_next_group_option (void); gboolean meta_prefs_is_locate_pointer_enabled (void); void meta_x11_display_grab_keys (MetaX11Display *x11_display); void meta_x11_display_ungrab_keys (MetaX11Display *x11_display); #endif muffin-6.4.1/src/core/meta-accel-parse.h0000664000175000017500000000302014723361714016730 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_ACCEL_PARSE_H #define META_ACCEL_PARSE_H #include #include "meta/common.h" typedef struct _MetaKeyCombo MetaKeyCombo; /* Not a real key symbol but means "key above the tab key"; this is * used as the default keybinding for cycle_group. * 0x2xxxxxxx is a range not used by GDK or X. the remaining digits are * randomly chosen */ #define META_KEY_ABOVE_TAB 0x2f7259c9 gboolean meta_parse_accelerator (const char *accel, MetaKeyCombo *combo); gboolean meta_parse_modifier (const char *accel, MetaVirtualModifier *mask); #endif /* META_ACCEL_PARSE_H */ muffin-6.4.1/src/core/meta-fraction.h0000664000175000017500000000170414723361714016365 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_FRACTION_H #define META_FRACTION_H typedef struct _MetaFraction { int num; int denom; } MetaFraction; MetaFraction meta_fraction_from_double (double src); #endif /* META_FRACTION_H */ muffin-6.4.1/src/core/edge-resistance.c0000664000175000017500000013275414723361714016703 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Edge resistance for move/resize operations */ /* * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/edge-resistance.h" #include "core/boxes-private.h" #include "core/display-private.h" #include "core/meta-workspace-manager-private.h" #include "core/workspace-private.h" /* A simple macro for whether a given window's edges are potentially * relevant for resistance/snapping during a move/resize operation */ #define WINDOW_EDGES_RELEVANT(window, display) \ meta_window_should_be_showing (window) && \ window != display->grab_window && \ window->type != META_WINDOW_DESKTOP && \ window->type != META_WINDOW_MENU && \ window->type != META_WINDOW_SPLASHSCREEN struct ResistanceDataForAnEdge { gboolean timeout_setup; guint timeout_id; int timeout_edge_pos; gboolean timeout_over; GSourceFunc timeout_func; MetaWindow *window; int keyboard_buildup; }; typedef struct ResistanceDataForAnEdge ResistanceDataForAnEdge; struct MetaEdgeResistanceData { GArray *left_edges; GArray *right_edges; GArray *top_edges; GArray *bottom_edges; ResistanceDataForAnEdge left_data; ResistanceDataForAnEdge right_data; ResistanceDataForAnEdge top_data; ResistanceDataForAnEdge bottom_data; }; static void compute_resistance_and_snapping_edges (MetaDisplay *display); /* !WARNING!: this function can return invalid indices (namely, either -1 or * edges->len); this is by design, but you need to remember this. */ static int find_index_of_edge_near_position (const GArray *edges, int position, gboolean want_interval_min, gboolean horizontal) { /* This is basically like a binary search, except that we're trying to * find a range instead of an exact value. So, if we have in our array * Value: 3 27 316 316 316 505 522 800 1213 * Index: 0 1 2 3 4 5 6 7 8 * and we call this function with position=500 & want_interval_min=TRUE * then we should get 5 (because 505 is the first value bigger than 500). * If we call this function with position=805 and want_interval_min=FALSE * then we should get 7 (because 800 is the last value smaller than 800). * A couple more, to make things clear: * position want_interval_min correct_answer * 316 TRUE 2 * 316 FALSE 4 * 2 FALSE -1 * 2000 TRUE 9 */ int low, high, mid; int compare; MetaEdge *edge; /* Initialize mid, edge, & compare in the off change that the array only * has one element. */ mid = 0; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; /* Begin the search... */ low = 0; high = edges->len - 1; while (low < high) { mid = low + (high - low)/2; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; if (compare == position) break; if (compare > position) high = mid - 1; else low = mid + 1; } /* mid should now be _really_ close to the index we want, so we start * linearly searching. However, note that we don't know if mid is less * than or greater than what we need and it's possible that there are * several equal values equal to what we were searching for and we ended * up in the middle of them instead of at the end. So we may need to * move mid multiple locations over. */ if (want_interval_min) { while (compare >= position && mid > 0) { mid--; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; } while (compare < position && mid < (int)edges->len - 1) { mid++; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; } /* Special case for no values in array big enough */ if (compare < position) return edges->len; /* Return the found value */ return mid; } else { while (compare <= position && mid < (int)edges->len - 1) { mid++; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; } while (compare > position && mid > 0) { mid--; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; } /* Special case for no values in array small enough */ if (compare > position) return -1; /* Return the found value */ return mid; } } static gboolean points_on_same_side (int ref, int pt1, int pt2) { return (pt1 - ref) * (pt2 - ref) > 0; } static int find_nearest_position (const GArray *edges, int position, int old_position, const MetaRectangle *new_rect, gboolean horizontal, gboolean only_forward) { /* This is basically just a binary search except that we're looking * for the value closest to position, rather than finding that * actual value. Also, we ignore any edges that aren't relevant * given the horizontal/vertical position of new_rect. */ int low, high, mid; int compare; MetaEdge *edge; int best, best_dist, i; gboolean edges_align; /* Initialize mid, edge, & compare in the off change that the array only * has one element. */ mid = 0; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; /* Begin the search... */ low = 0; high = edges->len - 1; while (low < high) { mid = low + (high - low)/2; edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; if (compare == position) break; if (compare > position) high = mid - 1; else low = mid + 1; } /* mid should now be _really_ close to the index we want, so we * start searching nearby for something that overlaps and is closer * than the original position. */ best = old_position; best_dist = INT_MAX; /* Start the search at mid */ edge = g_array_index (edges, MetaEdge*, mid); compare = horizontal ? edge->rect.x : edge->rect.y; edges_align = meta_rectangle_edge_aligns (new_rect, edge); if (edges_align && (!only_forward || !points_on_same_side (position, compare, old_position))) { int dist = ABS (compare - position); if (dist < best_dist) { best = compare; best_dist = dist; } } /* Now start searching higher than mid */ for (i = mid + 1; i < (int)edges->len; i++) { edge = g_array_index (edges, MetaEdge*, i); compare = horizontal ? edge->rect.x : edge->rect.y; edges_align = horizontal ? meta_rectangle_vert_overlap (&edge->rect, new_rect) : meta_rectangle_horiz_overlap (&edge->rect, new_rect); if (edges_align && (!only_forward || !points_on_same_side (position, compare, old_position))) { int dist = ABS (compare - position); if (dist < best_dist) { best = compare; best_dist = dist; } break; } } /* Now start searching lower than mid */ for (i = mid-1; i >= 0; i--) { edge = g_array_index (edges, MetaEdge*, i); compare = horizontal ? edge->rect.x : edge->rect.y; edges_align = horizontal ? meta_rectangle_vert_overlap (&edge->rect, new_rect) : meta_rectangle_horiz_overlap (&edge->rect, new_rect); if (edges_align && (!only_forward || !points_on_same_side (position, compare, old_position))) { int dist = ABS (compare - position); if (dist < best_dist) { best = compare; best_dist = dist; } break; } } /* Return the best one found */ return best; } static gboolean movement_towards_edge (MetaSide side, int increment) { switch (side) { case META_SIDE_LEFT: case META_SIDE_TOP: return increment < 0; case META_SIDE_RIGHT: case META_SIDE_BOTTOM: return increment > 0; default: g_assert_not_reached (); return FALSE; } } static gboolean edge_resistance_timeout (gpointer data) { ResistanceDataForAnEdge *resistance_data = data; resistance_data->timeout_over = TRUE; resistance_data->timeout_id = 0; (*resistance_data->timeout_func)(resistance_data->window); return FALSE; } static int apply_edge_resistance (MetaWindow *window, int old_pos, int new_pos, const MetaRectangle *old_rect, const MetaRectangle *new_rect, GArray *edges, ResistanceDataForAnEdge *resistance_data, GSourceFunc timeout_func, gboolean xdir, gboolean keyboard_op) { int i, begin, end; int last_edge; gboolean increasing = new_pos > old_pos; int increment = increasing ? 1 : -1; const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW = 16; const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW = 0; const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR = 32; const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR = 0; const int PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN = 32; const int PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN = 0; const int TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW = 0; const int TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR = 0; const int TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN = 0; /* Quit if no movement was specified */ if (old_pos == new_pos) return new_pos; /* Remove the old timeout if it's no longer relevant */ if (resistance_data->timeout_setup && ((resistance_data->timeout_edge_pos > old_pos && resistance_data->timeout_edge_pos > new_pos) || (resistance_data->timeout_edge_pos < old_pos && resistance_data->timeout_edge_pos < new_pos))) { resistance_data->timeout_setup = FALSE; g_clear_handle_id (&resistance_data->timeout_id, g_source_remove); } /* Get the range of indices in the edge array that we move past/to. */ begin = find_index_of_edge_near_position (edges, old_pos, increasing, xdir); end = find_index_of_edge_near_position (edges, new_pos, !increasing, xdir); /* begin and end can be outside the array index, if the window is partially * off the screen */ last_edge = edges->len - 1; begin = CLAMP (begin, 0, last_edge); end = CLAMP (end, 0, last_edge); /* Loop over all these edges we're moving past/to. */ i = begin; while ((increasing && i <= end) || (!increasing && i >= end)) { gboolean edges_align; MetaEdge *edge = g_array_index (edges, MetaEdge*, i); int compare = xdir ? edge->rect.x : edge->rect.y; /* Find out if this edge is relevant */ edges_align = meta_rectangle_edge_aligns (new_rect, edge) || meta_rectangle_edge_aligns (old_rect, edge); /* Nothing to do unless the edges align */ if (!edges_align) { /* Go to the next edge in the range */ i += increment; continue; } /* Rest is easier to read if we split on keyboard vs. mouse op */ if (keyboard_op) { if ((old_pos < compare && compare < new_pos) || (old_pos > compare && compare > new_pos)) return compare; } else /* mouse op */ { int threshold; /* TIMEOUT RESISTANCE: If the edge is relevant and we're moving * towards it, then we may want to have some kind of time delay * before the user can move past this edge. */ if (movement_towards_edge (edge->side_type, increment)) { /* First, determine the length of time for the resistance */ int timeout_length_ms = 0; switch (edge->edge_type) { case META_EDGE_WINDOW: timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_WINDOW; break; case META_EDGE_MONITOR: timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_MONITOR; break; case META_EDGE_SCREEN: timeout_length_ms = TIMEOUT_RESISTANCE_LENGTH_MS_SCREEN; break; } if (!resistance_data->timeout_setup && timeout_length_ms != 0) { resistance_data->timeout_id = g_timeout_add (timeout_length_ms, edge_resistance_timeout, resistance_data); g_source_set_name_by_id (resistance_data->timeout_id, "[mutter] edge_resistance_timeout"); resistance_data->timeout_setup = TRUE; resistance_data->timeout_edge_pos = compare; resistance_data->timeout_over = FALSE; resistance_data->timeout_func = timeout_func; resistance_data->window = window; } if (!resistance_data->timeout_over && timeout_length_ms != 0) return compare; } /* PIXEL DISTANCE MOUSE RESISTANCE: If the edge matters and the * user hasn't moved at least threshold pixels past this edge, * stop movement at this edge. (Note that this is different from * keyboard resistance precisely because keyboard move ops are * relative to previous positions, whereas mouse move ops are * relative to differences in mouse position and mouse position * is an absolute quantity rather than a relative quantity) */ /* First, determine the threshold */ threshold = 0; switch (edge->edge_type) { case META_EDGE_WINDOW: if (movement_towards_edge (edge->side_type, increment)) threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_WINDOW; else threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_WINDOW; break; case META_EDGE_MONITOR: if (movement_towards_edge (edge->side_type, increment)) threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_MONITOR; else threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_MONITOR; break; case META_EDGE_SCREEN: if (movement_towards_edge (edge->side_type, increment)) threshold = PIXEL_DISTANCE_THRESHOLD_TOWARDS_SCREEN; else threshold = PIXEL_DISTANCE_THRESHOLD_AWAYFROM_SCREEN; break; } if (ABS (compare - new_pos) < threshold) return compare; } /* Go to the next edge in the range */ i += increment; } return new_pos; } static int apply_edge_snapping (int old_pos, int new_pos, const MetaRectangle *new_rect, GArray *edges, gboolean xdir, gboolean keyboard_op) { int snap_to; if (old_pos == new_pos) return new_pos; snap_to = find_nearest_position (edges, new_pos, old_pos, new_rect, xdir, keyboard_op); /* If mouse snap-moving, the user could easily accidentally move just a * couple pixels in a direction they didn't mean to move; so ignore snap * movement in those cases unless it's only a small number of pixels * anyway. */ if (!keyboard_op && ABS (snap_to - old_pos) >= 8 && ABS (new_pos - old_pos) < 8) return old_pos; else /* Otherwise, return the snapping position found */ return snap_to; } /* This function takes the position (including any frame) of the window and * a proposed new position (ignoring edge resistance/snapping), and then * applies edge resistance to EACH edge (separately) updating new_outer. * It returns true if new_outer is modified, false otherwise. * * display->grab_edge_resistance_data MUST already be setup or calling this * function will cause a crash. */ static gboolean apply_edge_resistance_to_each_side (MetaDisplay *display, MetaWindow *window, const MetaRectangle *old_outer, MetaRectangle *new_outer, GSourceFunc timeout_func, gboolean auto_snap, gboolean keyboard_op, gboolean is_resize) { MetaEdgeResistanceData *edge_data; MetaRectangle modified_rect; gboolean modified; int new_left, new_right, new_top, new_bottom; if (display->grab_edge_resistance_data == NULL) compute_resistance_and_snapping_edges (display); edge_data = display->grab_edge_resistance_data; if (auto_snap && !META_WINDOW_TILED (window)) { /* Do the auto snapping instead of normal edge resistance; in all * cases, we allow snapping to opposite kinds of edges (e.g. left * sides of windows to both left and right edges. */ new_left = apply_edge_snapping (BOX_LEFT (*old_outer), BOX_LEFT (*new_outer), new_outer, edge_data->left_edges, TRUE, keyboard_op); new_right = apply_edge_snapping (BOX_RIGHT (*old_outer), BOX_RIGHT (*new_outer), new_outer, edge_data->right_edges, TRUE, keyboard_op); new_top = apply_edge_snapping (BOX_TOP (*old_outer), BOX_TOP (*new_outer), new_outer, edge_data->top_edges, FALSE, keyboard_op); new_bottom = apply_edge_snapping (BOX_BOTTOM (*old_outer), BOX_BOTTOM (*new_outer), new_outer, edge_data->bottom_edges, FALSE, keyboard_op); } else if (auto_snap && META_WINDOW_TILED (window)) { MetaRectangle workarea; guint i; const gfloat tile_edges[] = { 1./4., 1./3., 1./2., 2./3., 3./4., }; meta_window_get_work_area_current_monitor (window, &workarea); new_left = new_outer->x; new_top = new_outer->y; new_right = new_outer->x + new_outer->width; new_bottom = new_outer->y + new_outer->height; /* When snapping tiled windows, we don't really care about the * x and y position, only about the width and height. Also, it * is special-cased (instead of relying on edge_data) because * we don't really care for other windows when calculating the * snapping points of tiled windows - we only care about the * work area and the target position. */ for (i = 0; i < G_N_ELEMENTS (tile_edges); i++) { guint horizontal_point = workarea.x + floor (workarea.width * tile_edges[i]); guint vertical_point = workarea.y + floor (workarea.height * tile_edges[i]); if (ABS (horizontal_point - new_left) < 16) { new_left = horizontal_point; new_right = workarea.x + workarea.width; } else if (ABS (horizontal_point - new_right) < 16) { new_left = workarea.x; new_right = horizontal_point; } if (ABS (vertical_point - new_left) < 16) { new_top = vertical_point; new_bottom = workarea.y + workarea.height; } else if (ABS (vertical_point - new_bottom) < 16) { new_top = workarea.y; new_bottom = vertical_point; } } } else { /* Disable edge resistance for resizes when windows have size * increment hints; see #346782. For all other cases, apply * them. */ if (!is_resize || window->size_hints.width_inc == 1) { /* Now, apply the normal horizontal edge resistance */ new_left = apply_edge_resistance (window, BOX_LEFT (*old_outer), BOX_LEFT (*new_outer), old_outer, new_outer, edge_data->left_edges, &edge_data->left_data, timeout_func, TRUE, keyboard_op); new_right = apply_edge_resistance (window, BOX_RIGHT (*old_outer), BOX_RIGHT (*new_outer), old_outer, new_outer, edge_data->right_edges, &edge_data->right_data, timeout_func, TRUE, keyboard_op); } else { new_left = new_outer->x; new_right = new_outer->x + new_outer->width; } /* Same for vertical resizes... */ if (!is_resize || window->size_hints.height_inc == 1) { new_top = apply_edge_resistance (window, BOX_TOP (*old_outer), BOX_TOP (*new_outer), old_outer, new_outer, edge_data->top_edges, &edge_data->top_data, timeout_func, FALSE, keyboard_op); new_bottom = apply_edge_resistance (window, BOX_BOTTOM (*old_outer), BOX_BOTTOM (*new_outer), old_outer, new_outer, edge_data->bottom_edges, &edge_data->bottom_data, timeout_func, FALSE, keyboard_op); } else { new_top = new_outer->y; new_bottom = new_outer->y + new_outer->height; } } /* Determine whether anything changed, and save the changes */ modified_rect = meta_rect (new_left, new_top, new_right - new_left, new_bottom - new_top); modified = !meta_rectangle_equal (new_outer, &modified_rect); *new_outer = modified_rect; return modified; } void meta_display_cleanup_edges (MetaDisplay *display) { guint i,j; MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data; GHashTable *edges_to_be_freed; if (edge_data == NULL) /* Not currently cached */ return; /* We first need to clean out any window edges */ edges_to_be_freed = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_free, NULL); for (i = 0; i < 4; i++) { GArray *tmp = NULL; MetaSide side; switch (i) { case 0: tmp = edge_data->left_edges; side = META_SIDE_LEFT; break; case 1: tmp = edge_data->right_edges; side = META_SIDE_RIGHT; break; case 2: tmp = edge_data->top_edges; side = META_SIDE_TOP; break; case 3: tmp = edge_data->bottom_edges; side = META_SIDE_BOTTOM; break; default: g_assert_not_reached (); } for (j = 0; j < tmp->len; j++) { MetaEdge *edge = g_array_index (tmp, MetaEdge*, j); if (edge->edge_type == META_EDGE_WINDOW && edge->side_type == side) { /* The same edge will appear in two arrays, and we can't free * it yet we still need to compare edge->side_type for the other * array that it is in. So store it in a hash table for later * freeing. Could also do this in a simple linked list. */ g_hash_table_insert (edges_to_be_freed, edge, edge); } } } /* Now free all the window edges (the key destroy function is g_free) */ g_hash_table_destroy (edges_to_be_freed); /* Now free the arrays and data */ g_array_free (edge_data->left_edges, TRUE); g_array_free (edge_data->right_edges, TRUE); g_array_free (edge_data->top_edges, TRUE); g_array_free (edge_data->bottom_edges, TRUE); edge_data->left_edges = NULL; edge_data->right_edges = NULL; edge_data->top_edges = NULL; edge_data->bottom_edges = NULL; /* Cleanup the timeouts */ if (edge_data->left_data.timeout_setup) g_clear_handle_id (&edge_data->left_data.timeout_id, g_source_remove); if (edge_data->right_data.timeout_setup) g_clear_handle_id (&edge_data->right_data.timeout_id, g_source_remove); if (edge_data->top_data.timeout_setup) g_clear_handle_id (&edge_data->top_data.timeout_id, g_source_remove); if (edge_data->bottom_data.timeout_setup) g_clear_handle_id (&edge_data->bottom_data.timeout_id, g_source_remove); g_free (display->grab_edge_resistance_data); display->grab_edge_resistance_data = NULL; } static int stupid_sort_requiring_extra_pointer_dereference (gconstpointer a, gconstpointer b) { const MetaEdge * const *a_edge = a; const MetaEdge * const *b_edge = b; return meta_rectangle_edge_cmp_ignore_type (*a_edge, *b_edge); } static void cache_edges (MetaDisplay *display, GList *window_edges, GList *monitor_edges, GList *screen_edges) { MetaEdgeResistanceData *edge_data; GList *tmp; int num_left, num_right, num_top, num_bottom; int i; /* * 0th: Print debugging information to the log about the edges */ #ifdef WITH_VERBOSE_MODE if (meta_is_verbose()) { int max_edges = MAX (MAX( g_list_length (window_edges), g_list_length (monitor_edges)), g_list_length (screen_edges)); char big_buffer[(EDGE_LENGTH+2)*max_edges]; meta_rectangle_edge_list_to_string (window_edges, ", ", big_buffer); meta_topic (META_DEBUG_EDGE_RESISTANCE, "Window edges for resistance : %s\n", big_buffer); meta_rectangle_edge_list_to_string (monitor_edges, ", ", big_buffer); meta_topic (META_DEBUG_EDGE_RESISTANCE, "Monitor edges for resistance: %s\n", big_buffer); meta_rectangle_edge_list_to_string (screen_edges, ", ", big_buffer); meta_topic (META_DEBUG_EDGE_RESISTANCE, "Screen edges for resistance : %s\n", big_buffer); } #endif /* * 1st: Get the total number of each kind of edge */ num_left = num_right = num_top = num_bottom = 0; for (i = 0; i < 3; i++) { tmp = NULL; switch (i) { case 0: tmp = window_edges; break; case 1: tmp = monitor_edges; break; case 2: tmp = screen_edges; break; default: g_assert_not_reached (); } while (tmp) { MetaEdge *edge = tmp->data; switch (edge->side_type) { case META_SIDE_LEFT: num_left++; break; case META_SIDE_RIGHT: num_right++; break; case META_SIDE_TOP: num_top++; break; case META_SIDE_BOTTOM: num_bottom++; break; default: g_assert_not_reached (); } tmp = tmp->next; } } /* * 2nd: Allocate the edges */ g_assert (display->grab_edge_resistance_data == NULL); display->grab_edge_resistance_data = g_new0 (MetaEdgeResistanceData, 1); edge_data = display->grab_edge_resistance_data; edge_data->left_edges = g_array_sized_new (FALSE, FALSE, sizeof(MetaEdge*), num_left + num_right); edge_data->right_edges = g_array_sized_new (FALSE, FALSE, sizeof(MetaEdge*), num_left + num_right); edge_data->top_edges = g_array_sized_new (FALSE, FALSE, sizeof(MetaEdge*), num_top + num_bottom); edge_data->bottom_edges = g_array_sized_new (FALSE, FALSE, sizeof(MetaEdge*), num_top + num_bottom); /* * 3rd: Add the edges to the arrays */ for (i = 0; i < 3; i++) { tmp = NULL; switch (i) { case 0: tmp = window_edges; break; case 1: tmp = monitor_edges; break; case 2: tmp = screen_edges; break; default: g_assert_not_reached (); } while (tmp) { MetaEdge *edge = tmp->data; switch (edge->side_type) { case META_SIDE_LEFT: case META_SIDE_RIGHT: g_array_append_val (edge_data->left_edges, edge); g_array_append_val (edge_data->right_edges, edge); break; case META_SIDE_TOP: case META_SIDE_BOTTOM: g_array_append_val (edge_data->top_edges, edge); g_array_append_val (edge_data->bottom_edges, edge); break; default: g_assert_not_reached (); } tmp = tmp->next; } } /* * 4th: Sort the arrays (FIXME: This is kinda dumb since the arrays were * individually sorted earlier and we could have done this faster and * avoided this sort by sticking them into the array with some simple * merging of the lists). */ g_array_sort (display->grab_edge_resistance_data->left_edges, stupid_sort_requiring_extra_pointer_dereference); g_array_sort (display->grab_edge_resistance_data->right_edges, stupid_sort_requiring_extra_pointer_dereference); g_array_sort (display->grab_edge_resistance_data->top_edges, stupid_sort_requiring_extra_pointer_dereference); g_array_sort (display->grab_edge_resistance_data->bottom_edges, stupid_sort_requiring_extra_pointer_dereference); } static void initialize_grab_edge_resistance_data (MetaDisplay *display) { MetaEdgeResistanceData *edge_data = display->grab_edge_resistance_data; edge_data->left_data.timeout_setup = FALSE; edge_data->right_data.timeout_setup = FALSE; edge_data->top_data.timeout_setup = FALSE; edge_data->bottom_data.timeout_setup = FALSE; edge_data->left_data.keyboard_buildup = 0; edge_data->right_data.keyboard_buildup = 0; edge_data->top_data.keyboard_buildup = 0; edge_data->bottom_data.keyboard_buildup = 0; } static void compute_resistance_and_snapping_edges (MetaDisplay *display) { GList *stacked_windows; GList *cur_window_iter; GList *edges; /* Lists of window positions (rects) and their relative stacking positions */ int stack_position; GSList *obscuring_windows, *window_stacking; /* The portions of the above lists that still remain at the stacking position * in the layer that we are working on */ GSList *rem_windows, *rem_win_stacking; MetaWorkspaceManager *workspace_manager = display->workspace_manager; g_assert (display->grab_window != NULL); meta_topic (META_DEBUG_WINDOW_OPS, "Computing edges to resist-movement or snap-to for %s.\n", display->grab_window->desc); /* * 1st: Get the list of relevant windows, from bottom to top */ stacked_windows = meta_stack_list_windows (display->stack, workspace_manager->active_workspace); /* * 2nd: we need to separate that stacked list into a list of windows that * can obscure other edges. To make sure we only have windows obscuring * those below it instead of going both ways, we also need to keep a * counter list. Messy, I know. */ obscuring_windows = window_stacking = NULL; cur_window_iter = stacked_windows; stack_position = 0; while (cur_window_iter != NULL) { MetaWindow *cur_window = cur_window_iter->data; if (WINDOW_EDGES_RELEVANT (cur_window, display)) { MetaRectangle *new_rect; new_rect = g_new (MetaRectangle, 1); meta_window_get_frame_rect (cur_window, new_rect); obscuring_windows = g_slist_prepend (obscuring_windows, new_rect); window_stacking = g_slist_prepend (window_stacking, GINT_TO_POINTER (stack_position)); } stack_position++; cur_window_iter = cur_window_iter->next; } /* Put 'em in bottom to top order */ rem_windows = obscuring_windows = g_slist_reverse (obscuring_windows); rem_win_stacking = window_stacking = g_slist_reverse (window_stacking); /* * 3rd: loop over the windows again, this time getting the edges from * them and removing intersections with the relevant obscuring_windows & * obscuring_docks. */ edges = NULL; stack_position = 0; cur_window_iter = stacked_windows; while (cur_window_iter != NULL) { MetaRectangle cur_rect; MetaWindow *cur_window = cur_window_iter->data; meta_window_get_frame_rect (cur_window, &cur_rect); /* Check if we want to use this window's edges for edge * resistance (note that dock edges are considered screen edges * which are handled separately */ if (WINDOW_EDGES_RELEVANT (cur_window, display) && cur_window->type != META_WINDOW_DOCK) { GList *new_edges; MetaEdge *new_edge; MetaRectangle display_rect = { 0 }; MetaRectangle reduced; meta_display_get_size (display, &display_rect.width, &display_rect.height); /* We don't care about snapping to any portion of the window that * is offscreen (we also don't care about parts of edges covered * by other windows or DOCKS, but that's handled below). */ meta_rectangle_intersect (&cur_rect, &display_rect, &reduced); new_edges = NULL; /* Left side of this window is resistance for the right edge of * the window being moved. */ new_edge = g_new (MetaEdge, 1); new_edge->rect = reduced; new_edge->rect.width = 0; new_edge->side_type = META_SIDE_RIGHT; new_edge->edge_type = META_EDGE_WINDOW; new_edges = g_list_prepend (new_edges, new_edge); /* Right side of this window is resistance for the left edge of * the window being moved. */ new_edge = g_new (MetaEdge, 1); new_edge->rect = reduced; new_edge->rect.x += new_edge->rect.width; new_edge->rect.width = 0; new_edge->side_type = META_SIDE_LEFT; new_edge->edge_type = META_EDGE_WINDOW; new_edges = g_list_prepend (new_edges, new_edge); /* Top side of this window is resistance for the bottom edge of * the window being moved. */ new_edge = g_new (MetaEdge, 1); new_edge->rect = reduced; new_edge->rect.height = 0; new_edge->side_type = META_SIDE_BOTTOM; new_edge->edge_type = META_EDGE_WINDOW; new_edges = g_list_prepend (new_edges, new_edge); /* Top side of this window is resistance for the bottom edge of * the window being moved. */ new_edge = g_new (MetaEdge, 1); new_edge->rect = reduced; new_edge->rect.y += new_edge->rect.height; new_edge->rect.height = 0; new_edge->side_type = META_SIDE_TOP; new_edge->edge_type = META_EDGE_WINDOW; new_edges = g_list_prepend (new_edges, new_edge); /* Update the remaining windows to only those at a higher * stacking position than this one. */ while (rem_win_stacking && stack_position >= GPOINTER_TO_INT (rem_win_stacking->data)) { rem_windows = rem_windows->next; rem_win_stacking = rem_win_stacking->next; } /* Remove edge portions overlapped by rem_windows and rem_docks */ new_edges = meta_rectangle_remove_intersections_with_boxes_from_edges ( new_edges, rem_windows); /* Save the new edges */ edges = g_list_concat (new_edges, edges); } stack_position++; cur_window_iter = cur_window_iter->next; } /* * 4th: Free the extra memory not needed and sort the list */ g_list_free (stacked_windows); /* Free the memory used by the obscuring windows/docks lists */ g_slist_free (window_stacking); g_slist_free_full (obscuring_windows, g_free); /* Sort the list. FIXME: Should I bother with this sorting? I just * sort again later in cache_edges() anyway... */ edges = g_list_sort (edges, meta_rectangle_edge_cmp); /* * 5th: Cache the combination of these edges with the onscreen and * monitor edges in an array for quick access. Free the edges since * they've been cached elsewhere. */ cache_edges (display, edges, workspace_manager->active_workspace->monitor_edges, workspace_manager->active_workspace->screen_edges); g_list_free (edges); /* * 6th: Initialize the resistance timeouts and buildups */ initialize_grab_edge_resistance_data (display); } void meta_window_edge_resistance_for_move (MetaWindow *window, int *new_x, int *new_y, GSourceFunc timeout_func, gboolean snap, gboolean is_keyboard_op) { MetaRectangle old_outer, proposed_outer, new_outer; gboolean is_resize; meta_window_get_frame_rect (window, &old_outer); proposed_outer = old_outer; proposed_outer.x = *new_x; proposed_outer.y = *new_y; new_outer = proposed_outer; window->display->grab_last_user_action_was_snap = snap; is_resize = FALSE; if (apply_edge_resistance_to_each_side (window->display, window, &old_outer, &new_outer, timeout_func, snap, is_keyboard_op, is_resize)) { /* apply_edge_resistance_to_each_side independently applies * resistance to both the right and left edges of new_outer as both * could meet areas of resistance. But we don't want a resize, so we * just have both edges move according to the stricter of the * resistances. Same thing goes for top & bottom edges. */ MetaRectangle *reference; int left_change, right_change, smaller_x_change; int top_change, bottom_change, smaller_y_change; if (snap && !is_keyboard_op) reference = &proposed_outer; else reference = &old_outer; left_change = BOX_LEFT (new_outer) - BOX_LEFT (*reference); right_change = BOX_RIGHT (new_outer) - BOX_RIGHT (*reference); if ( snap && is_keyboard_op && left_change == 0) smaller_x_change = right_change; else if (snap && is_keyboard_op && right_change == 0) smaller_x_change = left_change; else if (ABS (left_change) < ABS (right_change)) smaller_x_change = left_change; else smaller_x_change = right_change; top_change = BOX_TOP (new_outer) - BOX_TOP (*reference); bottom_change = BOX_BOTTOM (new_outer) - BOX_BOTTOM (*reference); if ( snap && is_keyboard_op && top_change == 0) smaller_y_change = bottom_change; else if (snap && is_keyboard_op && bottom_change == 0) smaller_y_change = top_change; else if (ABS (top_change) < ABS (bottom_change)) smaller_y_change = top_change; else smaller_y_change = bottom_change; *new_x = old_outer.x + smaller_x_change + (BOX_LEFT (*reference) - BOX_LEFT (old_outer)); *new_y = old_outer.y + smaller_y_change + (BOX_TOP (*reference) - BOX_TOP (old_outer)); meta_topic (META_DEBUG_EDGE_RESISTANCE, "outer x & y move-to coordinate changed from %d,%d to %d,%d\n", proposed_outer.x, proposed_outer.y, *new_x, *new_y); } } void meta_window_edge_resistance_for_resize (MetaWindow *window, int *new_width, int *new_height, MetaGravity gravity, GSourceFunc timeout_func, gboolean snap, gboolean is_keyboard_op) { MetaRectangle old_outer, new_outer; int proposed_outer_width, proposed_outer_height; meta_window_get_frame_rect (window, &old_outer); proposed_outer_width = *new_width; proposed_outer_height = *new_height; meta_rectangle_resize_with_gravity (&old_outer, &new_outer, gravity, proposed_outer_width, proposed_outer_height); window->display->grab_last_user_action_was_snap = snap; if (apply_edge_resistance_to_each_side (window->display, window, &old_outer, &new_outer, timeout_func, snap, is_keyboard_op, TRUE)) { *new_width = new_outer.width; *new_height = new_outer.height; meta_topic (META_DEBUG_EDGE_RESISTANCE, "outer width & height got changed from %d,%d to %d,%d\n", proposed_outer_width, proposed_outer_height, new_outer.width, new_outer.height); } } muffin-6.4.1/src/core/place.h0000664000175000017500000000253414723361714014722 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window placement */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_PLACE_H #define META_PLACE_H #include "core/frame.h" #include "core/window-private.h" void meta_window_process_placement (MetaWindow *window, MetaPlacementRule *placement_rule, int *rel_x, int *rel_y); void meta_window_place (MetaWindow *window, int x, int y, int *new_x, int *new_y); #endif muffin-6.4.1/src/core/prefs.c0000664000175000017500000017101614723361714014752 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington, Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2006 Elijah Newren * Copyright (C) 2008 Thomas Thurman * Copyright (C) 2010 Milan Bouchet-Valat, Copyright (C) 2011 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:prefs * @title: Preferences * @short_description: Mutter preferences */ #include "config.h" #include #include #include #include #include "compositor/meta-plugin-manager.h" #include "core/keybindings-private.h" #include "core/meta-accel-parse.h" #include "core/util-private.h" #include "meta/prefs.h" #include "x11/meta-x11-display-private.h" /* If you add a key, it needs updating in init() and in the gsettings * notify listener and of course in the .schemas file. * * Keys which are handled by one of the unified handlers below are * not given a name here, because the purpose of the unified handlers * is that keys should be referred to exactly once. */ #define KEY_TITLEBAR_FONT "titlebar-font" #define KEY_NUM_WORKSPACES "num-workspaces" #define KEY_WORKSPACE_NAMES "workspace-names" /* Keys from "foreign" schemas */ #define KEY_GNOME_ACCESSIBILITY "toolkit-accessibility" #define KEY_GNOME_ANIMATIONS "enable-animations" #define KEY_GNOME_CURSOR_THEME "cursor-theme" #define KEY_GNOME_CURSOR_SIZE "cursor-size" #define KEY_XKB_OPTIONS "xkb-options" #define KEY_MOUSEWHEEL_ZOOM_ENABLED "screen-magnifier-enabled" #define KEY_BRING_ACTIVATED_WINDOWS_TO_CURRENT_WORKSPACE "bring-windows-to-current-workspace" #define KEY_OVERLAY_KEY "overlay-key" #define KEY_WORKSPACES_ONLY_ON_PRIMARY "workspaces-only-on-primary" #define KEY_LOCATE_POINTER "locate-pointer" #define KEY_GTK_THEME "gtk-theme" /* These are the different schemas we are keeping * a GSettings instance for */ #define SCHEMA_GENERAL "org.cinnamon.desktop.wm.preferences" #define SCHEMA_MUFFIN "org.cinnamon.muffin" #define SCHEMA_INTERFACE "org.cinnamon.desktop.interface" #define SCHEMA_INPUT_SOURCES "org.cinnamon.desktop.input-sources" #define SCHEMA_MOUSE "org.cinnamon.desktop.peripherals.mouse" #define SCHEMA_A11Y_APPLICATIONS "org.cinnamon.desktop.a11y.applications" #define SETTINGS(s) g_hash_table_lookup (settings_schemas, (s)) static GList *changes = NULL; static guint changed_idle; static GList *listeners = NULL; static GHashTable *settings_schemas; static gboolean use_system_font = FALSE; static PangoFontDescription *titlebar_font = NULL; static MetaVirtualModifier mouse_button_mods = Mod1Mask; static MetaVirtualModifier mouse_button_zoom_mods = Mod1Mask; static gboolean mouse_zoom_enabled = FALSE; static MetaKeyCombo overlay_key_combo = { 0, 0, 0 }; static MetaKeyCombo locate_pointer_key_combo = { 0, 0, 0 }; static CDesktopFocusMode focus_mode = C_DESKTOP_FOCUS_MODE_CLICK; static CDesktopFocusNewWindows focus_new_windows = C_DESKTOP_FOCUS_NEW_WINDOWS_SMART; static gboolean raise_on_click = TRUE; static MetaPlacementMode new_window_placement_mode = META_PLACEMENT_MODE_AUTOMATIC; static gboolean attach_modal_dialogs = FALSE; static int num_workspaces = 4; static gboolean workspace_cycle = FALSE; static CDesktopTitlebarAction action_double_click_titlebar = C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE; static CDesktopTitlebarAction action_middle_click_titlebar = C_DESKTOP_TITLEBAR_ACTION_LOWER; static CDesktopTitlebarAction action_right_click_titlebar = C_DESKTOP_TITLEBAR_ACTION_MENU; static CDesktopTitlebarScrollAction action_scroll_titlebar = C_DESKTOP_TITLEBAR_SCROLL_ACTION_NONE; static int min_window_opacity = 0; static gboolean dynamic_workspaces = FALSE; static gboolean disable_workarounds = FALSE; static gboolean auto_raise = FALSE; static gboolean auto_raise_delay = 500; static gboolean focus_change_on_pointer_rest = FALSE; static gboolean bell_is_visible = FALSE; static gboolean bell_is_audible = TRUE; static gboolean gnome_accessibility = FALSE; static gboolean gnome_animations = TRUE; static gboolean locate_pointer_is_enabled = FALSE; static unsigned int check_alive_timeout = 5000; static char *cursor_theme = NULL; /* cursor_size will, when running as an X11 compositing window manager, be the * actual cursor size, multiplied with the global window scaling factor. On * Wayland, it will be the actual cursor size retrieved from gsettings. */ static int cursor_size = 24; static int draggable_border_width = 10; static int drag_threshold; static gboolean resize_with_right_button = FALSE; static gboolean edge_tiling = FALSE; static gboolean force_fullscreen = TRUE; static gboolean auto_maximize = TRUE; static gboolean show_fallback_app_menu = TRUE; static CDesktopVisualBellType visual_bell_type = C_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH; static MetaButtonLayout button_layout; /* NULL-terminated array */ static char **workspace_names = NULL; static gboolean workspaces_only_on_primary = FALSE; static char *iso_next_group_option = NULL; static MetaX11BackgroundTransition background_transition = META_X11_BACKGROUND_TRANSITION_BLEND; static gboolean unredirect_fullscreen_windows = FALSE; static gboolean tile_maximize = FALSE; static gboolean invert_workspace_flip = FALSE; static char *gtk_theme = NULL; static char *bell_sound = NULL; static gboolean bring_user_activated_windows_to_current_workspace = FALSE; static void handle_preference_update_enum (GSettings *settings, gchar *key); static gboolean update_binding (MetaKeyPref *binding, gchar **strokes); static gboolean update_key_binding (const char *key, gchar **strokes); static void update_min_win_opacity (void); static void settings_changed (GSettings *settings, gchar *key, gpointer data); static void bindings_changed (GSettings *settings, gchar *key, gpointer data); static void queue_changed (MetaPreference pref); static void maybe_give_disable_workarounds_warning (void); static gboolean titlebar_handler (GVariant*, gpointer*, gpointer); static gboolean mouse_button_mods_handler (GVariant*, gpointer*, gpointer); static gboolean mouse_button_mods_zoom_handler (GVariant*, gpointer*, gpointer); static gboolean button_layout_handler (GVariant*, gpointer*, gpointer); static gboolean overlay_key_handler (GVariant*, gpointer*, gpointer); static gboolean locate_pointer_key_handler (GVariant*, gpointer*, gpointer); static gboolean iso_next_group_handler (GVariant*, gpointer*, gpointer); static void init_bindings (void); typedef struct { MetaPrefsChangedFunc func; gpointer data; } MetaPrefsListener; typedef struct { const char *key; const char *schema; MetaPreference pref; } MetaBasePreference; typedef struct { MetaBasePreference base; gpointer target; } MetaEnumPreference; typedef struct { MetaBasePreference base; gboolean *target; } MetaBoolPreference; /** * MetaStringPreference: * @handler: (nullable): A handler. Many of the string preferences * aren't stored as strings and need parsing; others of them have * default values which can't be solved in the general case. If you * include a function pointer here, it will be called instead of writing * the string value out to the target variable. * The function will be passed to g_settings_get_mapped() and should * return %TRUE if the mapping was successful and %FALSE otherwise. * In the former case the function is expected to handle the result * of the conversion itself and call queue_changed() appropriately; * in particular the @result (out) parameter as returned by * g_settings_get_mapped() will be ignored in all cases. * This may be %NULL. If it is, see "target", below. * @target: (nullable): Where to write the incoming string. * This must be %NULL if the handler is non-%NULL. * If the incoming string is %NULL, no change will be made. */ typedef struct { MetaBasePreference base; GSettingsGetMapping handler; gchar **target; } MetaStringPreference; typedef struct { MetaBasePreference base; GSettingsGetMapping handler; gchar ***target; } MetaStringArrayPreference; typedef struct { MetaBasePreference base; gint *target; } MetaIntPreference; typedef struct { MetaBasePreference base; unsigned int *target; } MetaUintPreference; /* All preferences that are not keybindings must be listed here, * plus in the GSettings schemas and the MetaPreference enum. */ /* FIXMEs: */ /* @@@ Don't use NULL lines at the end; glib can tell you how big it is */ static MetaEnumPreference preferences_enum[] = { { { "focus-new-windows", SCHEMA_GENERAL, META_PREF_FOCUS_NEW_WINDOWS, }, &focus_new_windows, }, { { "focus-mode", SCHEMA_GENERAL, META_PREF_FOCUS_MODE, }, &focus_mode, }, { { "placement-mode", SCHEMA_MUFFIN, META_PREF_NEW_WINDOW_PLACEMENT_MODE, }, &new_window_placement_mode, }, { { "visual-bell-type", SCHEMA_GENERAL, META_PREF_VISUAL_BELL_TYPE, }, &visual_bell_type, }, { { "action-double-click-titlebar", SCHEMA_GENERAL, META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR, }, &action_double_click_titlebar, }, { { "action-middle-click-titlebar", SCHEMA_GENERAL, META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR, }, &action_middle_click_titlebar, }, { { "action-right-click-titlebar", SCHEMA_GENERAL, META_PREF_ACTION_RIGHT_CLICK_TITLEBAR, }, &action_right_click_titlebar, }, { { "action-scroll-titlebar", SCHEMA_GENERAL, META_PREF_ACTION_SCROLL_WHEEL_TITLEBAR, }, &action_scroll_titlebar, }, { { "background-transition", SCHEMA_MUFFIN, META_PREF_BACKGROUND_TRANSITION, }, &background_transition, }, { { NULL, 0, 0 }, NULL }, }; static MetaBoolPreference preferences_bool[] = { { { "attach-modal-dialogs", SCHEMA_MUFFIN, META_PREF_ATTACH_MODAL_DIALOGS, }, &attach_modal_dialogs, }, { { "raise-on-click", SCHEMA_GENERAL, META_PREF_RAISE_ON_CLICK, }, &raise_on_click, }, { { "titlebar-uses-system-font", SCHEMA_GENERAL, META_PREF_TITLEBAR_FONT, /* note! shares a pref */ }, &use_system_font, }, { { "dynamic-workspaces", SCHEMA_MUFFIN, META_PREF_DYNAMIC_WORKSPACES, }, &dynamic_workspaces, }, { { "disable-workarounds", SCHEMA_GENERAL, META_PREF_DISABLE_WORKAROUNDS, }, &disable_workarounds, }, { { "auto-raise", SCHEMA_GENERAL, META_PREF_AUTO_RAISE, }, &auto_raise, }, { { "focus-change-on-pointer-rest", SCHEMA_MUFFIN, META_PREF_FOCUS_CHANGE_ON_POINTER_REST, }, &focus_change_on_pointer_rest }, { { "visual-bell", SCHEMA_GENERAL, META_PREF_VISUAL_BELL, }, &bell_is_visible, /* FIXME: change the name: it's confusing */ }, { { "audible-bell", SCHEMA_GENERAL, META_PREF_AUDIBLE_BELL, }, &bell_is_audible, /* FIXME: change the name: it's confusing */ }, { { KEY_GNOME_ACCESSIBILITY, SCHEMA_INTERFACE, META_PREF_GNOME_ACCESSIBILITY, }, &gnome_accessibility, }, { { KEY_GNOME_ANIMATIONS, SCHEMA_INTERFACE, META_PREF_GNOME_ANIMATIONS, }, &gnome_animations, }, { { "resize-with-right-button", SCHEMA_GENERAL, META_PREF_RESIZE_WITH_RIGHT_BUTTON, }, &resize_with_right_button, }, { { "edge-tiling", SCHEMA_MUFFIN, META_PREF_EDGE_TILING, }, &edge_tiling, }, { { "workspaces-only-on-primary", SCHEMA_MUFFIN, META_PREF_WORKSPACES_ONLY_ON_PRIMARY, }, &workspaces_only_on_primary, }, { { "invert-workspace-flip-direction", SCHEMA_MUFFIN, META_PREF_INVERT_WORKSPACE_FLIP_DIRECTION, }, &invert_workspace_flip, }, { { "auto-maximize", SCHEMA_MUFFIN, META_PREF_AUTO_MAXIMIZE, }, &auto_maximize, }, { { KEY_LOCATE_POINTER, SCHEMA_MOUSE, META_PREF_LOCATE_POINTER, }, &locate_pointer_is_enabled, }, { { "unredirect-fullscreen-windows", SCHEMA_MUFFIN, META_PREF_UNREDIRECT_FULLSCREEN_WINDOWS, }, &unredirect_fullscreen_windows, }, { { "workspace-cycle", SCHEMA_MUFFIN, META_PREF_WORKSPACE_CYCLE, }, &workspace_cycle, }, { { KEY_MOUSEWHEEL_ZOOM_ENABLED, SCHEMA_A11Y_APPLICATIONS, META_PREF_MOUSE_ZOOM_ENABLED, }, &mouse_zoom_enabled, }, { { "tile-maximize", SCHEMA_MUFFIN, META_PREF_TILE_MAXIMIZE, }, &tile_maximize, }, { { "bring-windows-to-current-workspace", SCHEMA_MUFFIN, META_PREF_BRING_WINDOWS_TO_CURRENT_WORKSPACE, }, &bring_user_activated_windows_to_current_workspace, }, { { NULL, 0, 0 }, NULL }, }; static MetaStringPreference preferences_string[] = { { { "mouse-button-modifier", SCHEMA_GENERAL, META_PREF_MOUSE_BUTTON_MODS, }, mouse_button_mods_handler, NULL, }, { { "mouse-button-zoom-modifier", SCHEMA_GENERAL, META_PREF_MOUSE_BUTTON_ZOOM_MODS, }, mouse_button_mods_zoom_handler, NULL, }, { { KEY_TITLEBAR_FONT, SCHEMA_GENERAL, META_PREF_TITLEBAR_FONT, }, titlebar_handler, NULL, }, { { "button-layout", SCHEMA_GENERAL, META_PREF_BUTTON_LAYOUT, }, button_layout_handler, NULL, }, { { "cursor-theme", SCHEMA_INTERFACE, META_PREF_CURSOR_THEME, }, NULL, &cursor_theme, }, { { "overlay-key", SCHEMA_MUFFIN, META_PREF_KEYBINDINGS, }, overlay_key_handler, NULL, }, { { "locate-pointer-key", SCHEMA_MUFFIN, META_PREF_KEYBINDINGS, }, locate_pointer_key_handler, NULL, }, { { "gtk-theme", SCHEMA_INTERFACE, META_PREF_GTK_THEME, }, NULL, >k_theme, }, { { "bell-sound", SCHEMA_GENERAL, META_PREF_BELL_SOUND, }, NULL, &bell_sound, }, { { NULL, 0, 0 }, NULL }, }; static MetaStringArrayPreference preferences_string_array[] = { { { KEY_WORKSPACE_NAMES, SCHEMA_GENERAL, META_PREF_WORKSPACE_NAMES, }, NULL, &workspace_names, }, { { KEY_XKB_OPTIONS, SCHEMA_INPUT_SOURCES, META_PREF_KEYBINDINGS, }, iso_next_group_handler, NULL, }, { { NULL, 0, 0 }, NULL }, }; static MetaIntPreference preferences_int[] = { { { KEY_NUM_WORKSPACES, SCHEMA_GENERAL, META_PREF_NUM_WORKSPACES, }, &num_workspaces }, { { "auto-raise-delay", SCHEMA_GENERAL, META_PREF_AUTO_RAISE_DELAY, }, &auto_raise_delay }, { { "draggable-border-width", SCHEMA_MUFFIN, META_PREF_DRAGGABLE_BORDER_WIDTH, }, &draggable_border_width }, { { "drag-threshold", SCHEMA_MOUSE, META_PREF_DRAG_THRESHOLD, }, &drag_threshold }, { { "cursor-size", SCHEMA_INTERFACE, META_PREF_CURSOR_SIZE, }, &cursor_size }, { { NULL, 0, 0 }, NULL }, }; static MetaUintPreference preferences_uint[] = { { { "check-alive-timeout", SCHEMA_MUFFIN, META_PREF_CHECK_ALIVE_TIMEOUT, }, &check_alive_timeout, }, { { NULL, 0, 0 }, NULL }, }; static void handle_preference_init_enum (void) { MetaEnumPreference *cursor = preferences_enum; while (cursor->base.key != NULL) { if (cursor->target==NULL) continue; *((gint *) cursor->target) = g_settings_get_enum (SETTINGS (cursor->base.schema), cursor->base.key); ++cursor; } } static void handle_preference_init_bool (void) { MetaBoolPreference *cursor = preferences_bool; while (cursor->base.key != NULL) { if (cursor->target!=NULL) *cursor->target = g_settings_get_boolean (SETTINGS (cursor->base.schema), cursor->base.key); ++cursor; } maybe_give_disable_workarounds_warning (); } static void handle_preference_init_string (void) { MetaStringPreference *cursor = preferences_string; while (cursor->base.key != NULL) { char *value; /* Complex keys have a mapping function to check validity */ if (cursor->handler) { if (cursor->target) meta_bug ("%s has both a target and a handler\n", cursor->base.key); g_settings_get_mapped (SETTINGS (cursor->base.schema), cursor->base.key, cursor->handler, NULL); } else { if (!cursor->target) meta_bug ("%s must have handler or target\n", cursor->base.key); g_free (*(cursor->target)); value = g_settings_get_string (SETTINGS (cursor->base.schema), cursor->base.key); *(cursor->target) = value; } ++cursor; } } static void handle_preference_init_string_array (void) { MetaStringArrayPreference *cursor = preferences_string_array; while (cursor->base.key != NULL) { char **value; /* Complex keys have a mapping function to check validity */ if (cursor->handler) { if (cursor->target) meta_bug ("%s has both a target and a handler\n", cursor->base.key); g_settings_get_mapped (SETTINGS (cursor->base.schema), cursor->base.key, cursor->handler, NULL); } else { if (!cursor->target) meta_bug ("%s must have handler or target\n", cursor->base.key); if (*(cursor->target)) g_strfreev (*(cursor->target)); value = g_settings_get_strv (SETTINGS (cursor->base.schema), cursor->base.key); *(cursor->target) = value; } ++cursor; } } static void handle_preference_init_int (void) { MetaIntPreference *cursor = preferences_int; while (cursor->base.key != NULL) { if (cursor->target) *cursor->target = g_settings_get_int (SETTINGS (cursor->base.schema), cursor->base.key); ++cursor; } } static void handle_preference_init_uint (void) { MetaUintPreference *cursor = preferences_uint; while (cursor->base.key != NULL) { if (cursor->target) *cursor->target = g_settings_get_uint (SETTINGS (cursor->base.schema), cursor->base.key); ++cursor; } } static void handle_preference_update_enum (GSettings *settings, gchar *key) { MetaEnumPreference *cursor = preferences_enum; gint old_value; while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) ++cursor; if (cursor->base.key == NULL) /* Didn't recognise that key. */ return; /* We need to know whether the value changes, so * store the current value away. */ old_value = * ((gint *)cursor->target); *((gint *)cursor->target) = g_settings_get_enum (SETTINGS (cursor->base.schema), key); /* Did it change? If so, tell the listeners about it. */ if (old_value != *((gint *)cursor->target)) queue_changed (cursor->base.pref); } static void handle_preference_update_bool (GSettings *settings, gchar *key) { MetaBoolPreference *cursor = preferences_bool; gboolean old_value; while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) ++cursor; if (cursor->base.key == NULL || cursor->target == NULL) /* Unknown key or no work for us to do. */ return; /* We need to know whether the value changes, so * store the current value away. */ old_value = *((gboolean *) cursor->target); /* Now look it up... */ *((gboolean *) cursor->target) = g_settings_get_boolean (SETTINGS (cursor->base.schema), key); /* Did it change? If so, tell the listeners about it. */ if (old_value != *((gboolean *)cursor->target)) queue_changed (cursor->base.pref); if (cursor->base.pref==META_PREF_DISABLE_WORKAROUNDS) maybe_give_disable_workarounds_warning (); } static void handle_preference_update_string (GSettings *settings, gchar *key) { MetaStringPreference *cursor = preferences_string; char *value; gboolean inform_listeners = FALSE; while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) ++cursor; if (cursor->base.key==NULL) /* Didn't recognise that key. */ return; /* Complex keys have a mapping function to check validity */ if (cursor->handler) { if (cursor->target) meta_bug ("%s has both a target and a handler\n", cursor->base.key); g_settings_get_mapped (SETTINGS (cursor->base.schema), cursor->base.key, cursor->handler, NULL); } else { if (!cursor->target) meta_bug ("%s must have handler or target\n", cursor->base.key); value = g_settings_get_string (SETTINGS (cursor->base.schema), cursor->base.key); inform_listeners = (g_strcmp0 (value, *(cursor->target)) != 0); g_free(*(cursor->target)); *(cursor->target) = value; } if (inform_listeners) queue_changed (cursor->base.pref); } static void handle_preference_update_string_array (GSettings *settings, gchar *key) { MetaStringArrayPreference *cursor = preferences_string_array; gboolean inform_listeners = FALSE; while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) ++cursor; if (cursor->base.key==NULL) /* Didn't recognise that key. */ return; /* Complex keys have a mapping function to check validity */ if (cursor->handler) { if (cursor->target) meta_bug ("%s has both a target and a handler\n", cursor->base.key); g_settings_get_mapped (SETTINGS (cursor->base.schema), cursor->base.key, cursor->handler, NULL); } else { char **values, **previous; int n_values, n_previous, i; if (!cursor->target) meta_bug ("%s must have handler or target\n", cursor->base.key); values = g_settings_get_strv (SETTINGS (cursor->base.schema), cursor->base.key); n_values = g_strv_length (values); previous = *(cursor->target); n_previous = previous ? g_strv_length (previous) : 0; inform_listeners = n_previous != n_values; for (i = 0; i < n_values && !inform_listeners; i++) inform_listeners = g_strcmp0 (values[i], previous[i]) != 0; if (*(cursor->target)) g_strfreev (*(cursor->target)); *(cursor->target) = values; } if (inform_listeners) queue_changed (cursor->base.pref); } static void handle_preference_update_int (GSettings *settings, gchar *key) { MetaIntPreference *cursor = preferences_int; gint new_value; while (cursor->base.key != NULL && strcmp (key, cursor->base.key) != 0) ++cursor; if (cursor->base.key == NULL || cursor->target == NULL) /* Unknown key or no work for us to do. */ return; new_value = g_settings_get_int (SETTINGS (cursor->base.schema), key); /* Did it change? If so, tell the listeners about it. */ if (*cursor->target != new_value) { *cursor->target = new_value; queue_changed (cursor->base.pref); } } static void handle_preference_update_uint (GSettings *settings, char *key) { MetaUintPreference *cursor = preferences_uint; unsigned int new_value; while (cursor->base.key && strcmp (key, cursor->base.key) != 0) ++cursor; if (!cursor->base.key || !cursor->target) return; new_value = g_settings_get_uint (SETTINGS (cursor->base.schema), key); if (*cursor->target != new_value) { *cursor->target = new_value; queue_changed (cursor->base.pref); } } /****************************************************************************/ /* Listeners. */ /****************************************************************************/ /** * meta_prefs_add_listener: (skip) * @func: a #MetaPrefsChangedFunc * @user_data: data passed to the function * */ void meta_prefs_add_listener (MetaPrefsChangedFunc func, gpointer user_data) { MetaPrefsListener *l; l = g_new (MetaPrefsListener, 1); l->func = func; l->data = user_data; listeners = g_list_prepend (listeners, l); } /** * meta_prefs_remove_listener: (skip) * @func: a #MetaPrefsChangedFunc * @user_data: data passed to the function * */ void meta_prefs_remove_listener (MetaPrefsChangedFunc func, gpointer user_data) { GList *tmp; tmp = listeners; while (tmp != NULL) { MetaPrefsListener *l = tmp->data; if (l->func == func && l->data == user_data) { g_free (l); listeners = g_list_delete_link (listeners, tmp); return; } tmp = tmp->next; } } static void emit_changed (MetaPreference pref) { GList *tmp; GList *copy; meta_topic (META_DEBUG_PREFS, "Notifying listeners that pref %s changed\n", meta_preference_to_string (pref)); copy = g_list_copy (listeners); tmp = copy; while (tmp != NULL) { MetaPrefsListener *l = tmp->data; (* l->func) (pref, l->data); tmp = tmp->next; } g_list_free (copy); } static gboolean changed_idle_handler (gpointer data) { GList *tmp; GList *copy; changed_idle = 0; copy = g_list_copy (changes); /* reentrancy paranoia */ g_list_free (changes); changes = NULL; tmp = copy; while (tmp != NULL) { MetaPreference pref = GPOINTER_TO_INT (tmp->data); emit_changed (pref); tmp = tmp->next; } g_list_free (copy); return FALSE; } static void queue_changed (MetaPreference pref) { meta_topic (META_DEBUG_PREFS, "Queueing change of pref %s\n", meta_preference_to_string (pref)); if (g_list_find (changes, GINT_TO_POINTER (pref)) == NULL) changes = g_list_prepend (changes, GINT_TO_POINTER (pref)); else meta_topic (META_DEBUG_PREFS, "Change of pref %s was already pending\n", meta_preference_to_string (pref)); if (changed_idle == 0) { changed_idle = g_idle_add_full (META_PRIORITY_PREFS_NOTIFY, changed_idle_handler, NULL, NULL); g_source_set_name_by_id (changed_idle, "[mutter] changed_idle_handler"); } } /****************************************************************************/ /* Initialisation. */ /****************************************************************************/ void meta_prefs_init (void) { GSettings *settings; settings_schemas = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref); settings = g_settings_new (SCHEMA_GENERAL); g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_GENERAL), settings); settings = g_settings_new (SCHEMA_MUFFIN); g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MUFFIN), settings); settings = g_settings_new (SCHEMA_MOUSE); g_signal_connect (settings, "changed", G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_MOUSE), settings); settings = g_settings_new (SCHEMA_A11Y_APPLICATIONS); g_signal_connect (settings, "changed::" KEY_MOUSEWHEEL_ZOOM_ENABLED, G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_A11Y_APPLICATIONS), settings); /* Individual keys we watch outside of our schemas */ settings = g_settings_new (SCHEMA_INTERFACE); g_signal_connect (settings, "changed::" KEY_GNOME_ACCESSIBILITY, G_CALLBACK (settings_changed), NULL); g_signal_connect (settings, "changed::" KEY_GNOME_ANIMATIONS, G_CALLBACK (settings_changed), NULL); g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_THEME, G_CALLBACK (settings_changed), NULL); g_signal_connect (settings, "changed::" KEY_GNOME_CURSOR_SIZE, G_CALLBACK (settings_changed), NULL); g_signal_connect (settings, "changed::" KEY_LOCATE_POINTER, G_CALLBACK (settings_changed), NULL); g_signal_connect (settings, "changed::" KEY_GTK_THEME, G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INTERFACE), settings); settings = g_settings_new (SCHEMA_INPUT_SOURCES); g_signal_connect (settings, "changed::" KEY_XKB_OPTIONS, G_CALLBACK (settings_changed), NULL); g_hash_table_insert (settings_schemas, g_strdup (SCHEMA_INPUT_SOURCES), settings); /* Pick up initial values. */ handle_preference_init_enum (); handle_preference_init_bool (); handle_preference_init_string (); handle_preference_init_string_array (); handle_preference_init_int (); handle_preference_init_uint (); update_min_win_opacity (); init_bindings (); } static gboolean find_pref (void *prefs, size_t pref_size, const char *search_key, MetaBasePreference **pref) { void *p = prefs; while (TRUE) { char **key = p; if (*key == NULL) break; if (strcmp (*key, search_key) == 0) { *pref = p; return TRUE; } p = (guchar *)p + pref_size; } return FALSE; } /****************************************************************************/ /* Updates. */ /****************************************************************************/ static void settings_changed (GSettings *settings, gchar *key, gpointer data) { GVariant *value; const GVariantType *type; MetaEnumPreference *cursor; gboolean found_enum; value = g_settings_get_value (settings, key); type = g_variant_get_type (value); if (g_variant_type_equal (type, G_VARIANT_TYPE_BOOLEAN)) handle_preference_update_bool (settings, key); else if (g_variant_type_equal (type, G_VARIANT_TYPE_INT32)) { if (strcmp (key, "min-window-opacity") == 0) { update_min_win_opacity (); queue_changed (META_PREF_MIN_WIN_OPACITY); } else { handle_preference_update_int (settings, key); } } else if (g_variant_type_equal (type, G_VARIANT_TYPE_UINT32)) handle_preference_update_uint (settings, key); else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING_ARRAY)) handle_preference_update_string_array (settings, key); else if (g_variant_type_equal (type, G_VARIANT_TYPE_STRING)) { cursor = preferences_enum; found_enum = FALSE; while (cursor->base.key != NULL) { if (strcmp (key, cursor->base.key) == 0) found_enum = TRUE; cursor++; } if (found_enum) handle_preference_update_enum (settings, key); else handle_preference_update_string (settings, key); } else { /* Unknown preference type. This quite likely simply isn't * a preference we track changes to. */ } g_variant_unref (value); } static void bindings_changed (GSettings *settings, gchar *key, gpointer data) { gchar **strokes; strokes = g_settings_get_strv (settings, key); if (update_key_binding (key, strokes)) queue_changed (META_PREF_KEYBINDINGS); g_strfreev (strokes); } /** * maybe_give_disable_workaround_warning: * * Special case: give a warning the first time disable_workarounds * is turned on. */ static void maybe_give_disable_workarounds_warning (void) { static gboolean first_disable = TRUE; if (first_disable && disable_workarounds) { first_disable = FALSE; meta_warning ("Workarounds for broken applications disabled. " "Some applications may not behave properly.\n"); } } MetaVirtualModifier meta_prefs_get_mouse_button_mods (void) { return mouse_button_mods; } MetaVirtualModifier meta_prefs_get_mouse_button_zoom_mods (void) { return mouse_button_zoom_mods; } gboolean meta_prefs_get_mouse_zoom_enabled (void) { return mouse_zoom_enabled; } CDesktopFocusMode meta_prefs_get_focus_mode (void) { return focus_mode; } CDesktopFocusNewWindows meta_prefs_get_focus_new_windows (void) { return focus_new_windows; } MetaPlacementMode meta_prefs_get_new_window_placement_mode (void) { return new_window_placement_mode; } gboolean meta_prefs_get_attach_modal_dialogs (void) { return attach_modal_dialogs; } gboolean meta_prefs_get_raise_on_click (void) { return raise_on_click; } gboolean meta_prefs_get_bring_windows_to_current_workspace (void) { /* Windows that the user activates (with a current timestamp) will * be brought to the currently active workspace if necessary. Normally * the user is brought to the window's workspace instead. */ return bring_user_activated_windows_to_current_workspace; } gboolean meta_prefs_get_show_fallback_app_menu (void) { return show_fallback_app_menu; } void meta_prefs_set_show_fallback_app_menu (gboolean whether) { gboolean changed = FALSE; changed = (show_fallback_app_menu == !whether); show_fallback_app_menu = whether; if (changed) queue_changed (META_PREF_BUTTON_LAYOUT); } const char* meta_prefs_get_cursor_theme (void) { return cursor_theme; } int meta_prefs_get_cursor_size (void) { return cursor_size; } /****************************************************************************/ /* Handlers for string preferences. */ /****************************************************************************/ static gboolean titlebar_handler (GVariant *value, gpointer *result, gpointer data) { PangoFontDescription *desc; const gchar *string_value; *result = NULL; /* ignored */ string_value = g_variant_get_string (value, NULL); desc = pango_font_description_from_string (string_value); if (desc == NULL) { meta_warning ("Could not parse font description " "\"%s\" from GSettings key %s\n", string_value ? string_value : "(null)", KEY_TITLEBAR_FONT); return FALSE; } /* Is the new description the same as the old? */ if (titlebar_font && pango_font_description_equal (desc, titlebar_font)) { pango_font_description_free (desc); } else { if (titlebar_font) pango_font_description_free (titlebar_font); titlebar_font = desc; queue_changed (META_PREF_TITLEBAR_FONT); } return TRUE; } static gboolean real_button_mods_handler (GVariant *value, gpointer *result, gpointer data, MetaVirtualModifier *modifier, MetaPreference pref_id) { MetaVirtualModifier mods; const gchar *string_value; *result = NULL; /* ignored */ string_value = g_variant_get_string (value, NULL); if (!string_value || !meta_parse_modifier (string_value, &mods)) { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse new GSettings value\n"); meta_warning ("\"%s\" found in configuration database is " "not a valid value for mouse button modifier\n", string_value); return FALSE; } meta_topic (META_DEBUG_KEYBINDINGS, "Mouse button modifier has new GSettings value \"%s\"\n", string_value); if (mods != *modifier) { *modifier = mods; queue_changed (pref_id); } return TRUE; } static gboolean mouse_button_mods_handler (GVariant *value, gpointer *result, gpointer data) { return real_button_mods_handler (value, result, data, &mouse_button_mods, META_PREF_MOUSE_BUTTON_MODS); } static gboolean mouse_button_mods_zoom_handler (GVariant *value, gpointer *result, gpointer data) { return real_button_mods_handler (value, result, data, &mouse_button_zoom_mods, META_PREF_MOUSE_BUTTON_ZOOM_MODS); } static gboolean button_layout_equal (const MetaButtonLayout *a, const MetaButtonLayout *b) { int i; i = 0; while (i < MAX_BUTTONS_PER_CORNER) { if (a->left_buttons[i] != b->left_buttons[i]) return FALSE; if (a->right_buttons[i] != b->right_buttons[i]) return FALSE; if (a->left_buttons_has_spacer[i] != b->left_buttons_has_spacer[i]) return FALSE; if (a->right_buttons_has_spacer[i] != b->right_buttons_has_spacer[i]) return FALSE; ++i; } return TRUE; } /* * This conversion cannot be handled by GSettings since * several values are stored in the same key (as a string). */ static MetaButtonFunction button_function_from_string (const char *str) { if (strcmp (str, "menu") == 0) return META_BUTTON_FUNCTION_MENU; else if (strcmp (str, "minimize") == 0) return META_BUTTON_FUNCTION_MINIMIZE; else if (strcmp (str, "maximize") == 0) return META_BUTTON_FUNCTION_MAXIMIZE; else if (strcmp (str, "close") == 0) return META_BUTTON_FUNCTION_CLOSE; else /* don't know; give up */ return META_BUTTON_FUNCTION_LAST; } static gboolean button_layout_handler (GVariant *value, gpointer *result, gpointer data) { MetaButtonLayout new_layout; const gchar *string_value; char **sides = NULL; int i; /* We need to ignore unknown button functions, for * compat with future versions */ *result = NULL; /* ignored */ string_value = g_variant_get_string (value, NULL); if (string_value) sides = g_strsplit (string_value, ":", 2); i = 0; if (sides != NULL && sides[0] != NULL) { char **buttons; int b; gboolean used[META_BUTTON_FUNCTION_LAST]; while (i < META_BUTTON_FUNCTION_LAST) { used[i] = FALSE; new_layout.left_buttons_has_spacer[i] = FALSE; ++i; } buttons = g_strsplit (sides[0], ",", -1); i = 0; b = 0; while (buttons[b] != NULL) { MetaButtonFunction f = button_function_from_string (buttons[b]); if (i > 0 && strcmp("spacer", buttons[b]) == 0) { new_layout.left_buttons_has_spacer[i-1] = TRUE; } else { if (f != META_BUTTON_FUNCTION_LAST && !used[f]) { new_layout.left_buttons[i] = f; used[f] = TRUE; ++i; } else { meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n", buttons[b]); } } ++b; } g_strfreev (buttons); } for (; i < MAX_BUTTONS_PER_CORNER; i++) { new_layout.left_buttons[i] = META_BUTTON_FUNCTION_LAST; new_layout.left_buttons_has_spacer[i] = FALSE; } i = 0; if (sides != NULL && sides[0] != NULL && sides[1] != NULL) { char **buttons; int b; gboolean used[META_BUTTON_FUNCTION_LAST]; while (i < META_BUTTON_FUNCTION_LAST) { used[i] = FALSE; new_layout.right_buttons_has_spacer[i] = FALSE; ++i; } buttons = g_strsplit (sides[1], ",", -1); i = 0; b = 0; while (buttons[b] != NULL) { MetaButtonFunction f = button_function_from_string (buttons[b]); if (i > 0 && strcmp("spacer", buttons[b]) == 0) { new_layout.right_buttons_has_spacer[i-1] = TRUE; } else { if (f != META_BUTTON_FUNCTION_LAST && !used[f]) { new_layout.right_buttons[i] = f; used[f] = TRUE; ++i; } else { meta_topic (META_DEBUG_PREFS, "Ignoring unknown or already-used button name \"%s\"\n", buttons[b]); } } ++b; } g_strfreev (buttons); } for (; i < MAX_BUTTONS_PER_CORNER; i++) { new_layout.right_buttons[i] = META_BUTTON_FUNCTION_LAST; new_layout.right_buttons_has_spacer[i] = FALSE; } g_strfreev (sides); /* Invert the button layout for RTL languages */ if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL) { MetaButtonLayout rtl_layout; int j; for (i = 0; new_layout.left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); for (j = 0; j < i; j++) { rtl_layout.right_buttons[j] = new_layout.left_buttons[i - j - 1]; if (j == 0) rtl_layout.right_buttons_has_spacer[i - 1] = new_layout.left_buttons_has_spacer[i - j - 1]; else rtl_layout.right_buttons_has_spacer[j - 1] = new_layout.left_buttons_has_spacer[i - j - 1]; } for (; j < MAX_BUTTONS_PER_CORNER; j++) { rtl_layout.right_buttons[j] = META_BUTTON_FUNCTION_LAST; rtl_layout.right_buttons_has_spacer[j] = FALSE; } for (i = 0; new_layout.right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++); for (j = 0; j < i; j++) { rtl_layout.left_buttons[j] = new_layout.right_buttons[i - j - 1]; if (j == 0) rtl_layout.left_buttons_has_spacer[i - 1] = new_layout.right_buttons_has_spacer[i - j - 1]; else rtl_layout.left_buttons_has_spacer[j - 1] = new_layout.right_buttons_has_spacer[i - j - 1]; } for (; j < MAX_BUTTONS_PER_CORNER; j++) { rtl_layout.left_buttons[j] = META_BUTTON_FUNCTION_LAST; rtl_layout.left_buttons_has_spacer[j] = FALSE; } new_layout = rtl_layout; } if (!button_layout_equal (&button_layout, &new_layout)) { button_layout = new_layout; emit_changed (META_PREF_BUTTON_LAYOUT); } return TRUE; } static gboolean overlay_key_handler (GVariant *value, gpointer *result, gpointer data) { MetaKeyCombo combo; const gchar *string_value; *result = NULL; /* ignored */ string_value = g_variant_get_string (value, NULL); if (string_value && meta_parse_accelerator (string_value, &combo)) ; else { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse value for overlay-key\n"); return FALSE; } combo.modifiers = 0; if (overlay_key_combo.keysym != combo.keysym || overlay_key_combo.keycode != combo.keycode) { overlay_key_combo = combo; queue_changed (META_PREF_KEYBINDINGS); } return TRUE; } static gboolean locate_pointer_key_handler (GVariant *value, gpointer *result, gpointer data) { MetaKeyCombo *combo; const gchar **string_values; gboolean output; int i; *result = NULL; /* ignored */ string_values = g_variant_get_strv (value, NULL); output = TRUE; for (i = 0; string_values && string_values[i]; i++) { combo = g_malloc0 (sizeof(MetaKeyCombo)); if (!meta_parse_accelerator (string_values[i], combo)) { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse keybinding value \"%s\" for locate-pointer-key\n", string_values[i]); g_free (combo); output = FALSE; } combo->modifiers = 0; } if (locate_pointer_key_combo.keysym != combo->keysym || locate_pointer_key_combo.keycode != combo->keycode) { locate_pointer_key_combo = *combo; queue_changed (META_PREF_KEYBINDINGS); } g_free (string_values); return output; } static gboolean iso_next_group_handler (GVariant *value, gpointer *result, gpointer data) { const char **xkb_options, **p; const char *option = NULL; gboolean changed = FALSE; *result = NULL; /* ignored */ xkb_options = g_variant_get_strv (value, NULL); for (p = xkb_options; p && *p; ++p) if (g_str_has_prefix (*p, "grp:")) { option = (*p + 4); break; } changed = (g_strcmp0 (option, iso_next_group_option) != 0); if (changed) { g_free (iso_next_group_option); iso_next_group_option = g_strdup (option); queue_changed (META_PREF_KEYBINDINGS); } g_free (xkb_options); return TRUE; } const PangoFontDescription* meta_prefs_get_titlebar_font (void) { if (use_system_font) return NULL; else return titlebar_font; } int meta_prefs_get_num_workspaces (void) { return num_workspaces; } gboolean meta_prefs_get_dynamic_workspaces (void) { return dynamic_workspaces; } gboolean meta_prefs_get_disable_workarounds (void) { return disable_workarounds; } gboolean meta_prefs_get_unredirect_fullscreen_windows (void) { return unredirect_fullscreen_windows; } gboolean meta_prefs_get_workspace_cycle (void) { return workspace_cycle; } #ifdef WITH_VERBOSE_MODE const char* meta_preference_to_string (MetaPreference pref) { /* TODO: better handled via GLib enum nicknames */ switch (pref) { case META_PREF_MOUSE_BUTTON_MODS: return "MOUSE_BUTTON_MODS"; case META_PREF_MOUSE_BUTTON_ZOOM_MODS: return "MOUSE_BUTTON_ZOOM_MODS"; case META_PREF_FOCUS_MODE: return "FOCUS_MODE"; case META_PREF_FOCUS_NEW_WINDOWS: return "FOCUS_NEW_WINDOWS"; case META_PREF_NEW_WINDOW_PLACEMENT_MODE: return "NEW_WINDOW_PLACEMENT_MODE"; case META_PREF_ATTACH_MODAL_DIALOGS: return "ATTACH_MODAL_DIALOGS"; case META_PREF_RAISE_ON_CLICK: return "RAISE_ON_CLICK"; case META_PREF_TITLEBAR_FONT: return "TITLEBAR_FONT"; case META_PREF_NUM_WORKSPACES: return "NUM_WORKSPACES"; case META_PREF_KEYBINDINGS: return "KEYBINDINGS"; case META_PREF_DISABLE_WORKAROUNDS: return "DISABLE_WORKAROUNDS"; case META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR: return "ACTION_DOUBLE_CLICK_TITLEBAR"; case META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR: return "ACTION_MIDDLE_CLICK_TITLEBAR"; case META_PREF_ACTION_RIGHT_CLICK_TITLEBAR: return "ACTION_RIGHT_CLICK_TITLEBAR"; case META_PREF_ACTION_SCROLL_WHEEL_TITLEBAR: return "ACTION_SCROLL_WHEEL_TITLEBAR"; case META_PREF_AUTO_RAISE: return "AUTO_RAISE"; case META_PREF_AUTO_RAISE_DELAY: return "AUTO_RAISE_DELAY"; case META_PREF_FOCUS_CHANGE_ON_POINTER_REST: return "FOCUS_CHANGE_ON_POINTER_REST"; case META_PREF_BUTTON_LAYOUT: return "BUTTON_LAYOUT"; case META_PREF_WORKSPACE_NAMES: return "WORKSPACE_NAMES"; case META_PREF_VISUAL_BELL: return "VISUAL_BELL"; case META_PREF_AUDIBLE_BELL: return "AUDIBLE_BELL"; case META_PREF_VISUAL_BELL_TYPE: return "VISUAL_BELL_TYPE"; case META_PREF_GNOME_ACCESSIBILITY: return "GNOME_ACCESSIBILTY"; case META_PREF_GNOME_ANIMATIONS: return "GNOME_ANIMATIONS"; case META_PREF_CURSOR_THEME: return "CURSOR_THEME"; case META_PREF_CURSOR_SIZE: return "CURSOR_SIZE"; case META_PREF_RESIZE_WITH_RIGHT_BUTTON: return "RESIZE_WITH_RIGHT_BUTTON"; case META_PREF_EDGE_TILING: return "EDGE_TILING"; case META_PREF_FORCE_FULLSCREEN: return "FORCE_FULLSCREEN"; case META_PREF_WORKSPACES_ONLY_ON_PRIMARY: return "WORKSPACES_ONLY_ON_PRIMARY"; case META_PREF_DRAGGABLE_BORDER_WIDTH: return "DRAGGABLE_BORDER_WIDTH"; case META_PREF_DRAG_THRESHOLD: return "DRAG_THRESHOLD"; case META_PREF_DYNAMIC_WORKSPACES: return "DYNAMIC_WORKSPACES"; case META_PREF_AUTO_MAXIMIZE: return "AUTO_MAXIMIZE"; case META_PREF_LOCATE_POINTER: return "LOCATE_POINTER"; case META_PREF_CHECK_ALIVE_TIMEOUT: return "CHECK_ALIVE_TIMEOUT"; case META_PREF_UNREDIRECT_FULLSCREEN_WINDOWS: return "UNREDIRECT_FULLSCREEN_WINDOWS"; case META_PREF_WORKSPACE_CYCLE: return "WORKSPACE_CYCLE"; case META_PREF_MIN_WIN_OPACITY: return "MIN_WIN_OPACITY"; case META_PREF_MOUSE_ZOOM_ENABLED: return "MOUSE_ZOOM_ENABLED"; case META_PREF_TILE_MAXIMIZE: return "TILE_MAXIMIZE"; case META_PREF_GTK_THEME: return "GTK_THEME"; case META_PREF_BELL_SOUND: return "BELL_SOUND"; case META_PREF_BRING_WINDOWS_TO_CURRENT_WORKSPACE: return "BRING_WINDOWS_TO_CURRENT_WORKSPACE"; } return "(unknown)"; } #endif /* WITH_VERBOSE_MODE */ void meta_prefs_set_num_workspaces (int n_workspaces) { MetaBasePreference *pref; if (find_pref (preferences_int, sizeof(MetaIntPreference), KEY_NUM_WORKSPACES, &pref)) { g_settings_set_int (SETTINGS (pref->schema), KEY_NUM_WORKSPACES, n_workspaces); } } static GHashTable *key_bindings; static void meta_key_pref_free (MetaKeyPref *pref) { update_binding (pref, NULL); g_free (pref->name); g_clear_object (&pref->settings); g_free (pref); } static void init_bindings (void) { MetaKeyPref *pref; key_bindings = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)meta_key_pref_free); } static gboolean update_binding (MetaKeyPref *binding, gchar **strokes) { GSList *old_combos, *a, *b; gboolean changed; int i; meta_topic (META_DEBUG_KEYBINDINGS, "Binding \"%s\" has new GSettings value\n", binding->name); old_combos = binding->combos; binding->combos = NULL; for (i = 0; strokes && strokes[i]; i++) { MetaKeyCombo *combo; combo = g_malloc0 (sizeof (MetaKeyCombo)); if (!meta_parse_accelerator (strokes[i], combo)) { meta_topic (META_DEBUG_KEYBINDINGS, "Failed to parse new GSettings value\n"); meta_warning ("\"%s\" found in configuration database is not a valid value for keybinding \"%s\"\n", strokes[i], binding->name); g_free (combo); /* Value is kept and will thus be removed next time we save the key. * Changing the key in response to a modification could lead to cyclic calls. */ continue; } binding->combos = g_slist_prepend (binding->combos, combo); } binding->combos = g_slist_reverse (binding->combos); a = old_combos; b = binding->combos; while (TRUE) { if ((!a && b) || (a && !b)) { changed = TRUE; break; } else if (!a && !b) { changed = FALSE; break; } else if (memcmp (a->data, b->data, sizeof (MetaKeyCombo)) != 0) { changed = TRUE; break; } else { a = a->next; b = b->next; } } g_slist_free_full (old_combos, g_free); return changed; } static gboolean update_key_binding (const char *key, gchar **strokes) { MetaKeyPref *pref = g_hash_table_lookup (key_bindings, key); if (pref) return update_binding (pref, strokes); else return FALSE; } static void update_min_win_opacity (void) { int pct; int mapped; pct = g_settings_get_int (SETTINGS (SCHEMA_GENERAL), "min-window-opacity"); mapped = (int)(((double)pct / 100.0) * 255.0); min_window_opacity = CLAMP (mapped, 0, 255); } const char* meta_prefs_get_workspace_name (int i) { const char *name; if (!workspace_names || g_strv_length (workspace_names) < (guint)i + 1 || !*workspace_names[i]) { char *generated_name = g_strdup_printf (_("Workspace %d"), i + 1); name = g_intern_string (generated_name); g_free (generated_name); } else name = workspace_names[i]; meta_topic (META_DEBUG_PREFS, "Getting name of workspace %d: \"%s\"\n", i, name); return name; } void meta_prefs_change_workspace_name (int num, const char *name) { GVariantBuilder builder; int n_workspace_names, i; g_return_if_fail (num >= 0); meta_topic (META_DEBUG_PREFS, "Changing name of workspace %d to %s\n", num, name ? name : "none"); /* NULL and empty string both mean "default" here, * and we also need to match the name against its default value * to avoid saving it literally. */ if (g_strcmp0 (name, meta_prefs_get_workspace_name (num)) == 0) { if (!name || !*name) meta_topic (META_DEBUG_PREFS, "Workspace %d already uses default name\n", num); else meta_topic (META_DEBUG_PREFS, "Workspace %d already has name %s\n", num, name); return; } g_variant_builder_init (&builder, G_VARIANT_TYPE_STRING_ARRAY); n_workspace_names = workspace_names ? g_strv_length (workspace_names) : 0; for (i = 0; i < MAX (num + 1, n_workspace_names); i++) { const char *value; if (i == num) value = name ? name : ""; else if (i < n_workspace_names) value = workspace_names[i] ? workspace_names[i] : ""; else value = ""; g_variant_builder_add (&builder, "s", value); } g_settings_set_value (SETTINGS (SCHEMA_GENERAL), KEY_WORKSPACE_NAMES, g_variant_builder_end (&builder)); } /** * meta_prefs_get_button_layout: * @button_layout: (out): */ void meta_prefs_get_button_layout (MetaButtonLayout *button_layout_p) { *button_layout_p = button_layout; } gboolean meta_prefs_get_visual_bell (void) { return bell_is_visible; } gboolean meta_prefs_bell_is_audible (void) { return bell_is_audible; } CDesktopVisualBellType meta_prefs_get_visual_bell_type (void) { return visual_bell_type; } gboolean meta_prefs_add_keybinding (const char *name, GSettings *settings, const gchar **bindings, MetaKeyBindingAction action, MetaKeyBindingFlags flags) { MetaKeyPref *pref; char **strokes = NULL; guint id; if (g_hash_table_lookup (key_bindings, name)) { meta_warning ("Trying to re-add keybinding \"%s\".\n", name); return FALSE; } pref = g_new0 (MetaKeyPref, 1); pref->name = g_strdup (name); pref->settings = settings != NULL ? g_object_ref (settings) : NULL; pref->action = action; pref->combos = NULL; pref->builtin = (flags & META_KEY_BINDING_BUILTIN) != 0; if (pref->builtin) { if (g_object_get_data (G_OBJECT (settings), "changed-signal") == NULL) { id = g_signal_connect (settings, "changed", G_CALLBACK (bindings_changed), NULL); g_object_set_data (G_OBJECT (settings), "changed-signal", GUINT_TO_POINTER (id)); } strokes = g_settings_get_strv (settings, name); } else { if (pref->settings != NULL) { char *changed_signal = g_strdup_printf ("changed::%s", name); id = g_signal_connect (settings, changed_signal, G_CALLBACK (bindings_changed), NULL); g_free (changed_signal); g_object_set_data (G_OBJECT (settings), name, GUINT_TO_POINTER (id)); strokes = g_settings_get_strv (settings, name); } else { strokes = g_strdupv((gchar **)bindings); } queue_changed (META_PREF_KEYBINDINGS); } update_binding (pref, strokes); g_strfreev (strokes); g_hash_table_insert (key_bindings, g_strdup (name), pref); return TRUE; } gboolean meta_prefs_remove_keybinding (const char *name) { MetaKeyPref *pref; gulong id; pref = g_hash_table_lookup (key_bindings, name); if (!pref) { meta_warning ("Trying to remove non-existent keybinding \"%s\".\n", name); return FALSE; } if (pref->builtin) { meta_warning ("Trying to remove builtin keybinding \"%s\".\n", name); return FALSE; } if (pref->settings != NULL) { id = GPOINTER_TO_UINT (g_object_steal_data (G_OBJECT (pref->settings), name)); g_clear_signal_handler (&id, pref->settings); } g_hash_table_remove (key_bindings, name); queue_changed (META_PREF_KEYBINDINGS); return TRUE; } GList * meta_prefs_get_keybindings (void) { return g_hash_table_get_values (key_bindings); } void meta_prefs_get_overlay_binding (MetaKeyCombo *combo) { *combo = overlay_key_combo; } void meta_prefs_get_locate_pointer_binding (MetaKeyCombo *combo) { *combo = locate_pointer_key_combo; } gboolean meta_prefs_is_locate_pointer_enabled (void) { return locate_pointer_is_enabled; } unsigned int meta_prefs_get_check_alive_timeout (void) { return check_alive_timeout; } const char * meta_prefs_get_iso_next_group_option (void) { return iso_next_group_option; } CDesktopTitlebarAction meta_prefs_get_action_double_click_titlebar (void) { return action_double_click_titlebar; } CDesktopTitlebarAction meta_prefs_get_action_middle_click_titlebar (void) { return action_middle_click_titlebar; } CDesktopTitlebarAction meta_prefs_get_action_right_click_titlebar (void) { return action_right_click_titlebar; } CDesktopTitlebarScrollAction meta_prefs_get_action_scroll_wheel_titlebar (void) { return action_scroll_titlebar; } gboolean meta_prefs_get_auto_raise (void) { return auto_raise; } int meta_prefs_get_auto_raise_delay (void) { return auto_raise_delay; } gboolean meta_prefs_get_focus_change_on_pointer_rest (void) { return focus_change_on_pointer_rest; } gboolean meta_prefs_get_gnome_accessibility (void) { return gnome_accessibility; } gboolean meta_prefs_get_gnome_animations (void) { return gnome_animations; } gboolean meta_prefs_get_edge_tiling (void) { return edge_tiling; } gboolean meta_prefs_get_tile_maximize (void) { return tile_maximize; } gboolean meta_prefs_get_auto_maximize (void) { return auto_maximize; } gboolean meta_prefs_get_invert_flip_direction (void) { return invert_workspace_flip; } MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name) { MetaKeyPref *pref = g_hash_table_lookup (key_bindings, name); return pref ? pref->action : META_KEYBINDING_ACTION_NONE; } gint meta_prefs_get_mouse_button_resize (void) { return resize_with_right_button ? 3: 2; } gint meta_prefs_get_mouse_button_menu (void) { return resize_with_right_button ? 2: 3; } gboolean meta_prefs_get_force_fullscreen (void) { return force_fullscreen; } gboolean meta_prefs_get_workspaces_only_on_primary (void) { return workspaces_only_on_primary; } int meta_prefs_get_draggable_border_width (void) { return draggable_border_width; } int meta_prefs_get_drag_threshold (void) { return drag_threshold; } void meta_prefs_set_force_fullscreen (gboolean whether) { force_fullscreen = whether; } MetaX11BackgroundTransition meta_prefs_get_background_transition (void) { return background_transition; } gint meta_prefs_get_min_win_opacity (void) { return min_window_opacity; } const char * meta_prefs_get_bell_sound (void) { return bell_sound; } muffin-6.4.1/src/core/frame.c0000664000175000017500000003154114723361714014723 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X window decorations */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003, 2004 Red Hat, Inc. * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/frame.h" #include "backends/x11/meta-backend-x11.h" #include "core/bell.h" #include "core/keybindings-private.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" #define EVENT_MASK (SubstructureRedirectMask | \ StructureNotifyMask | SubstructureNotifyMask | \ ExposureMask | FocusChangeMask) void meta_window_ensure_frame (MetaWindow *window) { MetaFrame *frame; XSetWindowAttributes attrs; gulong create_serial; MetaX11Display *x11_display; if (window->frame) return; x11_display = window->display->x11_display; frame = g_new (MetaFrame, 1); frame->window = window; frame->xwindow = None; frame->rect = window->rect; frame->child_x = 0; frame->child_y = 0; frame->bottom_height = 0; frame->right_width = 0; frame->current_cursor = 0; frame->borders_cached = FALSE; meta_verbose ("Frame geometry %d,%d %dx%d\n", frame->rect.x, frame->rect.y, frame->rect.width, frame->rect.height); frame->ui_frame = meta_ui_create_frame (x11_display->ui, x11_display->xdisplay, frame->window, window->xvisual, frame->rect.x, frame->rect.y, frame->rect.width, frame->rect.height, &create_serial); frame->xwindow = frame->ui_frame->xwindow; meta_stack_tracker_record_add (window->display->stack_tracker, frame->xwindow, create_serial); meta_verbose ("Frame for %s is 0x%lx\n", frame->window->desc, frame->xwindow); attrs.event_mask = EVENT_MASK; XChangeWindowAttributes (x11_display->xdisplay, frame->xwindow, CWEventMask, &attrs); meta_x11_display_register_x_window (x11_display, &frame->xwindow, window); meta_x11_error_trap_push (x11_display); if (window->mapped) { window->mapped = FALSE; /* the reparent will unmap the window, * we don't want to take that as a withdraw */ meta_topic (META_DEBUG_WINDOW_STATE, "Incrementing unmaps_pending on %s for reparent\n", window->desc); window->unmaps_pending += 1; } meta_stack_tracker_record_remove (window->display->stack_tracker, window->xwindow, XNextRequest (x11_display->xdisplay)); XReparentWindow (x11_display->xdisplay, window->xwindow, frame->xwindow, frame->child_x, frame->child_y); window->reparents_pending += 1; /* FIXME handle this error */ meta_x11_error_trap_pop (x11_display); /* Ensure focus is restored after the unmap/map events triggered * by XReparentWindow(). */ if (meta_window_has_focus (window)) window->restore_focus_on_map = TRUE; /* stick frame to the window */ window->frame = frame; /* Now that frame->xwindow is registered with window, we can set its * style and background. */ meta_frame_update_style (frame); meta_frame_update_title (frame); meta_ui_map_frame (x11_display->ui, frame->xwindow); { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); /* Since the backend selects for events on another connection, * make sure to sync the GTK+ connection to ensure that the * frame window has been created on the server at this point. */ XSync (x11_display->xdisplay, False); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISelectEvents (GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()), frame->xwindow, &mask, 1); XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Motion); XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISelectEvents (xdisplay, frame->xwindow, &mask, 1); } } /* Move keybindings to frame instead of window */ meta_window_grab_keys (window); } void meta_window_destroy_frame (MetaWindow *window) { MetaFrame *frame; MetaFrameBorders borders; MetaX11Display *x11_display; if (window->frame == NULL) return; x11_display = window->display->x11_display; meta_verbose ("Unframing window %s\n", window->desc); frame = window->frame; meta_frame_calc_borders (frame, &borders); /* Unparent the client window; it may be destroyed, * thus the error trap. */ meta_x11_error_trap_push (x11_display); if (window->mapped) { window->mapped = FALSE; /* Keep track of unmapping it, so we * can identify a withdraw initiated * by the client. */ meta_topic (META_DEBUG_WINDOW_STATE, "Incrementing unmaps_pending on %s for reparent back to root\n", window->desc); window->unmaps_pending += 1; } if (!x11_display->closing) { meta_stack_tracker_record_add (window->display->stack_tracker, window->xwindow, XNextRequest (x11_display->xdisplay)); XReparentWindow (x11_display->xdisplay, window->xwindow, x11_display->xroot, /* Using anything other than client root window coordinates * coordinates here means we'll need to ensure a configure * notify event is sent; see bug 399552. */ window->frame->rect.x + borders.invisible.left, window->frame->rect.y + borders.invisible.top); window->reparents_pending += 1; } meta_x11_error_trap_pop (x11_display); meta_ui_frame_unmanage (frame->ui_frame); /* Ensure focus is restored after the unmap/map events triggered * by XReparentWindow(). */ if (meta_window_has_focus (window)) window->restore_focus_on_map = TRUE; meta_x11_display_unregister_x_window (x11_display, frame->xwindow); window->frame = NULL; if (window->frame_bounds) { cairo_region_destroy (window->frame_bounds); window->frame_bounds = NULL; } /* Move keybindings to window instead of frame */ meta_window_grab_keys (window); g_free (frame); /* Put our state back where it should be */ meta_window_queue (window, META_QUEUE_CALC_SHOWING); meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } MetaFrameFlags meta_frame_get_flags (MetaFrame *frame) { MetaFrameFlags flags; flags = 0; if (frame->window->border_only) { ; /* FIXME this may disable the _function_ as well as decor * in some cases, which is sort of wrong. */ } else { flags |= META_FRAME_ALLOWS_MENU; if (frame->window->has_close_func) flags |= META_FRAME_ALLOWS_DELETE; if (frame->window->has_maximize_func) flags |= META_FRAME_ALLOWS_MAXIMIZE; if (frame->window->has_minimize_func) flags |= META_FRAME_ALLOWS_MINIMIZE; if (frame->window->has_shade_func) flags |= META_FRAME_ALLOWS_SHADE; } if (META_WINDOW_ALLOWS_MOVE (frame->window)) flags |= META_FRAME_ALLOWS_MOVE; if (META_WINDOW_ALLOWS_HORIZONTAL_RESIZE (frame->window)) flags |= META_FRAME_ALLOWS_HORIZONTAL_RESIZE; if (META_WINDOW_ALLOWS_VERTICAL_RESIZE (frame->window)) flags |= META_FRAME_ALLOWS_VERTICAL_RESIZE; if (meta_window_appears_focused (frame->window)) flags |= META_FRAME_HAS_FOCUS; if (frame->window->shaded) flags |= META_FRAME_SHADED; if (frame->window->on_all_workspaces_requested) flags |= META_FRAME_STUCK; /* FIXME: Should we have some kind of UI for windows that are just vertically * maximized or just horizontally maximized? */ if (META_WINDOW_MAXIMIZED (frame->window)) flags |= META_FRAME_MAXIMIZED; if (META_WINDOW_TILED_TOP (frame->window)) flags |= META_FRAME_TILED_TOP; if (META_WINDOW_TILED_RIGHT (frame->window)) flags |= META_FRAME_TILED_RIGHT; if (META_WINDOW_TILED_BOTTOM (frame->window)) flags |= META_FRAME_TILED_BOTTOM; if (META_WINDOW_TILED_LEFT (frame->window)) flags |= META_FRAME_TILED_LEFT; if (META_WINDOW_TILED_ULC (frame->window)) flags |= META_FRAME_TILED_ULC; if (META_WINDOW_TILED_URC (frame->window)) flags |= META_FRAME_TILED_URC; if (META_WINDOW_TILED_LRC (frame->window)) flags |= META_FRAME_TILED_LRC; if (META_WINDOW_TILED_LLC (frame->window)) flags |= META_FRAME_TILED_LLC; if (frame->window->fullscreen) flags |= META_FRAME_FULLSCREEN; if (frame->window->wm_state_above) flags |= META_FRAME_ABOVE; return flags; } void meta_frame_borders_clear (MetaFrameBorders *self) { self->visible.top = self->invisible.top = self->total.top = 0; self->visible.bottom = self->invisible.bottom = self->total.bottom = 0; self->visible.left = self->invisible.left = self->total.left = 0; self->visible.right = self->invisible.right = self->total.right = 0; } void meta_frame_calc_borders (MetaFrame *frame, MetaFrameBorders *borders) { /* Save on if statements and potential uninitialized values * in callers -- if there's no frame, then zero the borders. */ if (frame == NULL) meta_frame_borders_clear (borders); else { if (!frame->borders_cached) { meta_ui_frame_get_borders (frame->ui_frame, &frame->cached_borders); frame->borders_cached = TRUE; } *borders = frame->cached_borders; } } void meta_frame_clear_cached_borders (MetaFrame *frame) { frame->borders_cached = FALSE; } gboolean meta_frame_sync_to_window (MetaFrame *frame, gboolean need_resize) { meta_topic (META_DEBUG_GEOMETRY, "Syncing frame geometry %d,%d %dx%d (SE: %d,%d)\n", frame->rect.x, frame->rect.y, frame->rect.width, frame->rect.height, frame->rect.x + frame->rect.width, frame->rect.y + frame->rect.height); meta_ui_frame_move_resize (frame->ui_frame, frame->rect.x, frame->rect.y, frame->rect.width, frame->rect.height); return need_resize; } cairo_region_t * meta_frame_get_frame_bounds (MetaFrame *frame) { return meta_ui_frame_get_bounds (frame->ui_frame); } void meta_frame_get_mask (MetaFrame *frame, cairo_rectangle_int_t *frame_rect, cairo_t *cr) { meta_ui_frame_get_mask (frame->ui_frame, frame_rect, cr); } void meta_frame_queue_draw (MetaFrame *frame) { meta_ui_frame_queue_draw (frame->ui_frame); } void meta_frame_set_screen_cursor (MetaFrame *frame, MetaCursor cursor) { MetaX11Display *x11_display; Cursor xcursor; if (cursor == frame->current_cursor) return; frame->current_cursor = cursor; x11_display = frame->window->display->x11_display; if (cursor == META_CURSOR_DEFAULT) XUndefineCursor (x11_display->xdisplay, frame->xwindow); else { xcursor = meta_x11_display_create_x_cursor (x11_display, cursor); XDefineCursor (x11_display->xdisplay, frame->xwindow, xcursor); XFlush (x11_display->xdisplay); XFreeCursor (x11_display->xdisplay, xcursor); } } Window meta_frame_get_xwindow (MetaFrame *frame) { return frame->xwindow; } void meta_frame_update_style (MetaFrame *frame) { meta_ui_frame_update_style (frame->ui_frame); } void meta_frame_update_title (MetaFrame *frame) { if (frame->window->title) meta_ui_frame_set_title (frame->ui_frame, frame->window->title); } muffin-6.4.1/src/core/window.c0000664000175000017500000107733114723361714015150 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington, Anders Carlsson * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-window * @title: MetaWindow * @short_description: A display-agnostic abstraction for a window. * * #MetaWindow is the core abstraction in Mutter of a window. It has the * properties you'd expect, such as a title, an icon, whether it's fullscreen, * has decorations, etc. * * Since a lot of different kinds of windows exist, each window also a * #MetaWindowType which denotes which kind of window we're exactly dealing * with. For example, one expects slightly different behaviour from a dialog * than a "normal" window. The type of a window can be queried with * meta_window_get_type(). * * Common API for windows include: * - Minimizing: meta_window_minimize() / meta_window_unminimize() * - Maximizing: meta_window_maximize() / meta_window_unmaximize() * - Fullscreen: meta_window_make_fullscreen() / meta_window_unmake_fullscreen() * / meta_window_is_fullscreen() * * Each #MetaWindow is part of either one or all #MetaWorkspaces of the * desktop. You can activate a window on a certain workspace using * meta_window_activate_with_workspace(), and query on which workspace it is * located using meta_window_located_on_workspace(). The workspace it is part * of can be obtained using meta_window_get_workspace(). * * Each display protocol should make a subclass to be compatible with that * protocols' specifics, for example #MetaWindowX11 and #MetaWindowWayland. * This is independent of the protocol that the client uses, which is modeled * using the #MetaWindowClientType enum. * * To integrate within the Clutter scene graph, which deals with the actual * rendering, each #MetaWindow will be part of a #MetaWindowActor. */ #include "config.h" #include "core/window-private.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "cogl/cogl.h" #include "core/boxes-private.h" #include "core/constraints.h" #include "core/edge-resistance.h" #include "core/frame.h" #include "core/keybindings-private.h" #include "core/meta-workspace-manager-private.h" #include "core/place.h" #include "core/stack.h" #include "core/util-private.h" #include "core/workspace-private.h" #include "meta/compositor-mutter.h" #include "meta/group.h" #include "meta/meta-cursor-tracker.h" #include "meta/meta-enum-types.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "ui/ui.h" #include "x11/meta-x11-display-private.h" #include "x11/window-props.h" #include "x11/window-x11.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-window-wayland.h" #include "wayland/meta-window-xwayland.h" #endif /* Windows that unmaximize to a size bigger than that fraction of the workarea * will be scaled down to that size (while maintaining aspect ratio). * Windows that cover an area greater then this size are automaximized on map. */ #define MAX_UNMAXIMIZED_WINDOW_AREA .8 #define SNAP_SECURITY_LABEL_PREFIX "snap." static int destroying_windows_disallowed = 0; /* Each window has a "stamp" which is a non-recycled 64-bit ID. They * start after the end of the XID space so that, for stacking * we can keep a guint64 that represents one or the other */ static guint64 next_window_stamp = G_GUINT64_CONSTANT(0x100000000); static void invalidate_work_areas (MetaWindow *window); static void set_wm_state (MetaWindow *window); static void set_net_wm_state (MetaWindow *window); static void meta_window_set_above (MetaWindow *window, gboolean new_value); static void meta_window_show (MetaWindow *window); static void meta_window_hide (MetaWindow *window); static void meta_window_save_rect (MetaWindow *window); static void ensure_mru_position_after (MetaWindow *window, MetaWindow *after_this_one); static void meta_window_move_resize_now (MetaWindow *window); static void meta_window_unqueue (MetaWindow *window, guint queuebits); static void update_move (MetaWindow *window, gboolean snap, int x, int y); static gboolean update_move_timeout (gpointer data); static void update_resize (MetaWindow *window, gboolean snap, int x, int y, gboolean force); static gboolean update_resize_timeout (gpointer data); static gboolean should_be_on_all_workspaces (MetaWindow *window); static void meta_window_flush_calc_showing (MetaWindow *window); static gboolean queue_calc_showing_func (MetaWindow *window, void *data); static void meta_window_move_between_rects (MetaWindow *window, MetaMoveResizeFlags move_resize_flags, const MetaRectangle *old_area, const MetaRectangle *new_area); static void unmaximize_window_before_freeing (MetaWindow *window); static void unminimize_window_and_all_transient_parents (MetaWindow *window); static void meta_window_propagate_focus_appearance (MetaWindow *window, gboolean focused); static void meta_window_update_icon_now (MetaWindow *window, gboolean force); static void set_workspace_state (MetaWindow *window, gboolean on_all_workspaces, MetaWorkspace *workspace); static MetaWindow *meta_window_find_tile_match (MetaWindow *window, MetaTileMode mode, gboolean vertical); static void update_edge_constraints (MetaWindow *window); static void notify_tile_mode (MetaWindow *window); /* Idle handlers for the three queues (run with meta_later_add()). The * "data" parameter in each case will be a GINT_TO_POINTER of the * index into the queue arrays to use. * * TODO: Possibly there is still some code duplication among these, which we * need to sort out at some point. */ static gboolean idle_calc_showing (gpointer data); static gboolean idle_move_resize (gpointer data); static gboolean idle_update_icon (gpointer data); G_DEFINE_ABSTRACT_TYPE (MetaWindow, meta_window, G_TYPE_OBJECT); enum { PROP_0, PROP_TITLE, PROP_ICON, PROP_MINI_ICON, PROP_DECORATED, PROP_FULLSCREEN, PROP_MAXIMIZED_HORIZONTALLY, PROP_MAXIMIZED_VERTICALLY, PROP_MINIMIZED, PROP_WINDOW_TYPE, PROP_USER_TIME, PROP_DEMANDS_ATTENTION, PROP_URGENT, PROP_SKIP_TASKBAR, PROP_MUTTER_HINTS, PROP_APPEARS_FOCUSED, PROP_RESIZEABLE, PROP_ABOVE, PROP_WM_CLASS, PROP_GTK_APPLICATION_ID, PROP_GTK_UNIQUE_BUS_NAME, PROP_GTK_APPLICATION_OBJECT_PATH, PROP_GTK_WINDOW_OBJECT_PATH, PROP_GTK_APP_MENU_OBJECT_PATH, PROP_GTK_MENUBAR_OBJECT_PATH, PROP_ON_ALL_WORKSPACES, PROP_PROGRESS, PROP_PROGRESS_PULSE, PROP_TILE_MODE, PROP_OPACITY, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; enum { WORKSPACE_CHANGED, FOCUS, RAISED, UNMANAGING, UNMANAGED, SIZE_CHANGED, POSITION_CHANGED, MONITOR_CHANGED, SHOWN, LAST_SIGNAL }; static guint window_signals[LAST_SIGNAL] = { 0 }; static void prefs_changed_callback (MetaPreference pref, gpointer data) { MetaWindow *window = data; if (pref == META_PREF_WORKSPACES_ONLY_ON_PRIMARY) { meta_window_on_all_workspaces_changed (window); } else if (pref == META_PREF_ATTACH_MODAL_DIALOGS && window->type == META_WINDOW_MODAL_DIALOG) { window->attached = meta_window_should_attach_to_parent (window); meta_window_recalc_features (window); meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } } static void meta_window_real_grab_op_began (MetaWindow *window, MetaGrabOp op) { } static void meta_window_real_grab_op_ended (MetaWindow *window, MetaGrabOp op) { window->shaken_loose = FALSE; } static void meta_window_real_current_workspace_changed (MetaWindow *window) { } static gboolean meta_window_real_update_struts (MetaWindow *window) { return FALSE; } static void meta_window_real_get_default_skip_hints (MetaWindow *window, gboolean *skip_taskbar_out, gboolean *skip_pager_out) { *skip_taskbar_out = FALSE; *skip_pager_out = FALSE; } static gboolean meta_window_real_update_icon (MetaWindow *window, cairo_surface_t **icon, cairo_surface_t **mini_icon) { *icon = NULL; *mini_icon = NULL; return FALSE; } static uint32_t meta_window_real_get_client_pid (MetaWindow *window) { return 0; } static void meta_window_finalize (GObject *object) { MetaWindow *window = META_WINDOW (object); if (window->icon) cairo_surface_destroy (window->icon); if (window->mini_icon) cairo_surface_destroy (window->mini_icon); if (window->frame_bounds) cairo_region_destroy (window->frame_bounds); if (window->shape_region) cairo_region_destroy (window->shape_region); if (window->opaque_region) cairo_region_destroy (window->opaque_region); if (window->input_region) cairo_region_destroy (window->input_region); if (window->transient_for) g_object_unref (window->transient_for); g_free (window->sm_client_id); g_free (window->wm_client_machine); g_free (window->startup_id); g_free (window->role); g_free (window->res_class); g_free (window->res_name); g_free (window->title); g_free (window->desc); g_free (window->sandboxed_app_id); g_free (window->gtk_theme_variant); g_free (window->gtk_application_id); g_free (window->gtk_unique_bus_name); g_free (window->gtk_application_object_path); g_free (window->gtk_window_object_path); g_free (window->gtk_app_menu_object_path); g_free (window->gtk_menubar_object_path); g_free (window->placement.rule); G_OBJECT_CLASS (meta_window_parent_class)->finalize (object); } static void meta_window_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWindow *win = META_WINDOW (object); switch (prop_id) { case PROP_TITLE: g_value_set_string (value, win->title); break; case PROP_ICON: g_value_set_pointer (value, win->icon); break; case PROP_MINI_ICON: g_value_set_pointer (value, win->mini_icon); break; case PROP_DECORATED: g_value_set_boolean (value, win->decorated); break; case PROP_FULLSCREEN: g_value_set_boolean (value, win->fullscreen); break; case PROP_MAXIMIZED_HORIZONTALLY: g_value_set_boolean (value, win->maximized_horizontally); break; case PROP_MAXIMIZED_VERTICALLY: g_value_set_boolean (value, win->maximized_vertically); break; case PROP_MINIMIZED: g_value_set_boolean (value, win->minimized); break; case PROP_WINDOW_TYPE: g_value_set_enum (value, win->type); break; case PROP_USER_TIME: g_value_set_uint (value, win->net_wm_user_time); break; case PROP_DEMANDS_ATTENTION: g_value_set_boolean (value, win->wm_state_demands_attention); break; case PROP_URGENT: g_value_set_boolean (value, win->urgent); break; case PROP_SKIP_TASKBAR: g_value_set_boolean (value, win->skip_taskbar); break; case PROP_MUTTER_HINTS: g_value_set_string (value, win->mutter_hints); break; case PROP_APPEARS_FOCUSED: g_value_set_boolean (value, meta_window_appears_focused (win)); break; case PROP_WM_CLASS: g_value_set_string (value, win->res_class); break; case PROP_RESIZEABLE: g_value_set_boolean (value, win->has_resize_func); break; case PROP_ABOVE: g_value_set_boolean (value, win->wm_state_above); break; case PROP_GTK_APPLICATION_ID: g_value_set_string (value, win->gtk_application_id); break; case PROP_GTK_UNIQUE_BUS_NAME: g_value_set_string (value, win->gtk_unique_bus_name); break; case PROP_GTK_APPLICATION_OBJECT_PATH: g_value_set_string (value, win->gtk_application_object_path); break; case PROP_GTK_WINDOW_OBJECT_PATH: g_value_set_string (value, win->gtk_window_object_path); break; case PROP_GTK_APP_MENU_OBJECT_PATH: g_value_set_string (value, win->gtk_app_menu_object_path); break; case PROP_GTK_MENUBAR_OBJECT_PATH: g_value_set_string (value, win->gtk_menubar_object_path); break; case PROP_ON_ALL_WORKSPACES: g_value_set_boolean (value, win->on_all_workspaces); break; case PROP_PROGRESS: g_value_set_uint (value, win->progress); break; case PROP_PROGRESS_PULSE: g_value_set_boolean (value, win->progress_pulse); break; case PROP_TILE_MODE: g_value_set_enum (value, win->tile_mode); break; case PROP_OPACITY: g_value_set_uint (value, win->opacity); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_class_init (MetaWindowClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_window_finalize; object_class->get_property = meta_window_get_property; object_class->set_property = meta_window_set_property; klass->grab_op_began = meta_window_real_grab_op_began; klass->grab_op_ended = meta_window_real_grab_op_ended; klass->current_workspace_changed = meta_window_real_current_workspace_changed; klass->update_struts = meta_window_real_update_struts; klass->get_default_skip_hints = meta_window_real_get_default_skip_hints; klass->update_icon = meta_window_real_update_icon; klass->get_client_pid = meta_window_real_get_client_pid; obj_props[PROP_TITLE] = g_param_spec_string ("title", "Title", "The title of the window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_ICON] = g_param_spec_pointer ("icon", "Icon", "Normal icon, usually 96x96 pixels", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MINI_ICON] = g_param_spec_pointer ("mini-icon", "Mini Icon", "Mini icon, usually 16x16 pixels", G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_DECORATED] = g_param_spec_boolean ("decorated", "Decorated", "Whether window is decorated", TRUE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_FULLSCREEN] = g_param_spec_boolean ("fullscreen", "Fullscreen", "Whether window is fullscreened", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MAXIMIZED_HORIZONTALLY] = g_param_spec_boolean ("maximized-horizontally", "Maximized horizontally", "Whether window is maximized horizontally", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MAXIMIZED_VERTICALLY] = g_param_spec_boolean ("maximized-vertically", "Maximizing vertically", "Whether window is maximized vertically", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MINIMIZED] = g_param_spec_boolean ("minimized", "Minimizing", "Whether window is minimized", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_WINDOW_TYPE] = g_param_spec_enum ("window-type", "Window Type", "The type of the window", META_TYPE_WINDOW_TYPE, META_WINDOW_NORMAL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_USER_TIME] = g_param_spec_uint ("user-time", "User time", "Timestamp of last user interaction", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_DEMANDS_ATTENTION] = g_param_spec_boolean ("demands-attention", "Demands Attention", "Whether the window has _NET_WM_STATE_DEMANDS_ATTENTION set", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_URGENT] = g_param_spec_boolean ("urgent", "Urgent", "Whether the urgent flag of WM_HINTS is set", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_SKIP_TASKBAR] = g_param_spec_boolean ("skip-taskbar", "Skip taskbar", "Whether the skip-taskbar flag of WM_HINTS is set", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_MUTTER_HINTS] = g_param_spec_string ("mutter-hints", "_MUTTER_HINTS", "Contents of the _MUTTER_HINTS property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_APPEARS_FOCUSED] = g_param_spec_boolean ("appears-focused", "Appears focused", "Whether the window is drawn as being focused", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_RESIZEABLE] = g_param_spec_boolean ("resizeable", "Resizeable", "Whether the window can be resized", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_ABOVE] = g_param_spec_boolean ("above", "Above", "Whether the window is shown as always-on-top", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_WM_CLASS] = g_param_spec_string ("wm-class", "WM_CLASS", "Contents of the WM_CLASS property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_APPLICATION_ID] = g_param_spec_string ("gtk-application-id", "_GTK_APPLICATION_ID", "Contents of the _GTK_APPLICATION_ID property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_UNIQUE_BUS_NAME] = g_param_spec_string ("gtk-unique-bus-name", "_GTK_UNIQUE_BUS_NAME", "Contents of the _GTK_UNIQUE_BUS_NAME property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_APPLICATION_OBJECT_PATH] = g_param_spec_string ("gtk-application-object-path", "_GTK_APPLICATION_OBJECT_PATH", "Contents of the _GTK_APPLICATION_OBJECT_PATH property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_WINDOW_OBJECT_PATH] = g_param_spec_string ("gtk-window-object-path", "_GTK_WINDOW_OBJECT_PATH", "Contents of the _GTK_WINDOW_OBJECT_PATH property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_APP_MENU_OBJECT_PATH] = g_param_spec_string ("gtk-app-menu-object-path", "_GTK_APP_MENU_OBJECT_PATH", "Contents of the _GTK_APP_MENU_OBJECT_PATH property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_GTK_MENUBAR_OBJECT_PATH] = g_param_spec_string ("gtk-menubar-object-path", "_GTK_MENUBAR_OBJECT_PATH", "Contents of the _GTK_MENUBAR_OBJECT_PATH property of this window", NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_ON_ALL_WORKSPACES] = g_param_spec_boolean ("on-all-workspaces", "On all workspaces", "Whether the window is set to appear on all workspaces", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_PROGRESS] = g_param_spec_uint ("progress", "Progress", "The progress of some long-running operation", 0, 100, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_PROGRESS_PULSE] = g_param_spec_boolean ("progress-pulse", "Indeterminate progress", "Show indeterminate or ongoing progress of an operation.", FALSE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_TILE_MODE] = g_param_spec_enum ("tile-mode", "Window Tile Mode", "The tile state of the window", META_TYPE_TILE_MODE, META_TILE_NONE, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_OPACITY] = g_param_spec_uint ("opacity", "Opacity", "The window's 'real' opacity (not the current opacity of the window actor", 0, 0xFF, 0xFF, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); window_signals[WORKSPACE_CHANGED] = g_signal_new ("workspace-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); window_signals[FOCUS] = g_signal_new ("focus", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); window_signals[RAISED] = g_signal_new ("raised", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); window_signals[UNMANAGING] = g_signal_new ("unmanaging", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); window_signals[UNMANAGED] = g_signal_new ("unmanaged", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindow::position-changed: * @window: a #MetaWindow * * This is emitted when the position of a window might * have changed. Specifically, this is emitted when the * position of the toplevel window has changed, or when * the position of the client window has changed. */ window_signals[POSITION_CHANGED] = g_signal_new ("position-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindow::monitor-changed: * @window: a #MetaWindow * @old_monitor: the old monitor index or -1 if not known * * This is emitted when the window has changed monitor */ window_signals[MONITOR_CHANGED] = g_signal_new ("monitor-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT); /** * MetaWindow::shown: * @window: a #MetaWindow * * This is emitted after a window has been shown. */ window_signals[SHOWN] = g_signal_new ("shown", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindow::size-changed: * @window: a #MetaWindow * * This is emitted when the size of a window might * have changed. Specifically, this is emitted when the * size of the toplevel window has changed, or when the * size of the client window has changed. */ window_signals[SIZE_CHANGED] = g_signal_new ("size-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_window_init (MetaWindow *self) { self->stamp = next_window_stamp++; meta_prefs_add_listener (prefs_changed_callback, self); } static gboolean is_desktop_or_dock_foreach (MetaWindow *window, void *data) { gboolean *result = data; *result = window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK; if (*result) return FALSE; /* stop as soon as we find one */ else return TRUE; } /* window is the window that's newly mapped provoking * the possible change */ static void maybe_leave_show_desktop_mode (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean is_desktop_or_dock; if (!workspace_manager->active_workspace->showing_desktop) return; /* If the window is a transient for the dock or desktop, don't * leave show desktop mode when the window opens. That's * so you can e.g. hide all windows, manipulate a file on * the desktop via a dialog, then unshow windows again. */ is_desktop_or_dock = FALSE; is_desktop_or_dock_foreach (window, &is_desktop_or_dock); meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach, &is_desktop_or_dock); if (!is_desktop_or_dock) { meta_workspace_manager_minimize_all_on_active_workspace_except (workspace_manager, window); meta_workspace_manager_unshow_desktop (workspace_manager); } } gboolean meta_window_should_attach_to_parent (MetaWindow *window) { MetaWindow *parent; if (!meta_prefs_get_attach_modal_dialogs () || window->type != META_WINDOW_MODAL_DIALOG) return FALSE; parent = meta_window_get_transient_for (window); if (!parent) return FALSE; switch (parent->type) { case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: return TRUE; default: return FALSE; } } static gboolean client_window_should_be_mapped (MetaWindow *window) { #ifdef HAVE_WAYLAND if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND && !meta_wayland_surface_get_buffer (window->surface)) return FALSE; #endif return !window->shaded; } static void sync_client_window_mapped (MetaWindow *window) { gboolean should_be_mapped = client_window_should_be_mapped (window); g_return_if_fail (!window->override_redirect); if (window->mapped == should_be_mapped) return; window->mapped = should_be_mapped; if (window->mapped) META_WINDOW_GET_CLASS (window)->map (window); else META_WINDOW_GET_CLASS (window)->unmap (window); } static gboolean meta_window_update_flatpak_id (MetaWindow *window, uint32_t pid) { g_autoptr(GKeyFile) key_file = NULL; g_autofree char *info_filename = NULL; g_return_val_if_fail (pid != 0, FALSE); g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE); key_file = g_key_file_new (); info_filename = g_strdup_printf ("/proc/%u/root/.flatpak-info", pid); if (!g_key_file_load_from_file (key_file, info_filename, G_KEY_FILE_NONE, NULL)) return FALSE; window->sandboxed_app_id = g_key_file_get_string (key_file, "Application", "name", NULL); return TRUE; } static gboolean meta_window_update_snap_id (MetaWindow *window, uint32_t pid) { g_autofree char *security_label_filename = NULL; g_autofree char *security_label_contents = NULL; gsize i, security_label_contents_size = 0; char *contents_start; char *contents_end; char *sandboxed_app_id; g_return_val_if_fail (pid != 0, FALSE); g_return_val_if_fail (window->sandboxed_app_id == NULL, FALSE); security_label_filename = g_strdup_printf ("/proc/%u/attr/current", pid); if (!g_file_get_contents (security_label_filename, &security_label_contents, &security_label_contents_size, NULL)) return FALSE; if (!g_str_has_prefix (security_label_contents, SNAP_SECURITY_LABEL_PREFIX)) return FALSE; /* We need to translate the security profile into the desktop-id. * The profile is in the form of 'snap.name-space.binary-name (current)' * while the desktop id will be name-space_binary-name. */ security_label_contents_size -= sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1; contents_start = security_label_contents + sizeof (SNAP_SECURITY_LABEL_PREFIX) - 1; contents_end = strchr (contents_start, ' '); if (contents_end) security_label_contents_size = contents_end - contents_start; for (i = 0; i < security_label_contents_size; ++i) { if (contents_start[i] == '.') contents_start[i] = '_'; } sandboxed_app_id = g_malloc0 (security_label_contents_size + 1); memcpy (sandboxed_app_id, contents_start, security_label_contents_size); window->sandboxed_app_id = sandboxed_app_id; return TRUE; } static void meta_window_update_sandboxed_app_id (MetaWindow *window) { uint32_t pid; g_clear_pointer (&window->sandboxed_app_id, g_free); pid = meta_window_get_client_pid (window); if (pid == 0) return; if (meta_window_update_flatpak_id (window, pid)) return; if (meta_window_update_snap_id (window, pid)) return; } static void meta_window_update_desc (MetaWindow *window) { g_clear_pointer (&window->desc, g_free); if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) window->desc = g_strdup_printf ("0x%lx", window->xwindow); else { guint64 small_stamp = window->stamp - G_GUINT64_CONSTANT(0x100000000); window->desc = g_strdup_printf ("W%" G_GUINT64_FORMAT , small_stamp); } } static void meta_window_main_monitor_changed (MetaWindow *window, const MetaLogicalMonitor *old) { META_WINDOW_GET_CLASS (window)->main_monitor_changed (window, old); g_signal_emit (window, window_signals[MONITOR_CHANGED], 0, old ? old->number : -1); if (old) g_signal_emit_by_name (window->display, "window-left-monitor", old->number, window); if (window->monitor) { g_signal_emit_by_name (window->display, "window-entered-monitor", window->monitor->number, window); g_signal_emit_by_name (window->display, "window-monitor-changed", window, window->monitor->number); } } MetaLogicalMonitor * meta_window_calculate_main_logical_monitor (MetaWindow *window) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaRectangle window_rect; meta_window_get_frame_rect (window, &window_rect); return meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, &window_rect); } static void meta_window_manage (MetaWindow *window) { COGL_TRACE_BEGIN_SCOPED (MetaWindowManage, "Window (manage)"); META_WINDOW_GET_CLASS (window)->manage (window); } MetaWindow * _meta_window_shared_new (MetaDisplay *display, MetaWindowClientType client_type, MetaWaylandSurface *surface, Window xwindow, gulong existing_wm_state, MetaCompEffect effect, XWindowAttributes *attrs) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; MetaWindow *window; COGL_TRACE_BEGIN_SCOPED (MetaWindowSharedNew, "Window (new)"); g_assert (attrs != NULL); meta_verbose ("attrs->map_state = %d (%s)\n", attrs->map_state, (attrs->map_state == IsUnmapped) ? "IsUnmapped" : (attrs->map_state == IsViewable) ? "IsViewable" : (attrs->map_state == IsUnviewable) ? "IsUnviewable" : "(unknown)"); if (client_type == META_WINDOW_CLIENT_TYPE_X11 && !meta_is_wayland_compositor ()) window = g_object_new (META_TYPE_WINDOW_X11, NULL); #ifdef HAVE_WAYLAND else if (client_type == META_WINDOW_CLIENT_TYPE_X11) window = g_object_new (META_TYPE_WINDOW_XWAYLAND, NULL); else if (client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) window = g_object_new (META_TYPE_WINDOW_WAYLAND, NULL); #endif else g_assert_not_reached (); window->constructing = TRUE; window->client_type = client_type; window->surface = surface; window->xwindow = xwindow; window->display = display; meta_display_register_stamp (window->display, &window->stamp, window); window->workspace = NULL; window->sync_request_counter = None; window->sync_request_serial = 0; window->sync_request_timeout_id = 0; window->sync_request_alarm = None; meta_window_update_sandboxed_app_id (window); meta_window_update_desc (window); window->override_redirect = attrs->override_redirect; /* avoid tons of stack updates */ meta_stack_freeze (window->display->stack); window->rect.x = attrs->x; window->rect.y = attrs->y; window->rect.width = attrs->width; window->rect.height = attrs->height; /* size_hints are the "request" */ window->size_hints.x = attrs->x; window->size_hints.y = attrs->y; window->size_hints.width = attrs->width; window->size_hints.height = attrs->height; /* initialize the remaining size_hints as if size_hints.flags were zero */ meta_set_normal_hints (window, NULL); /* And this is our unmaximized size */ window->saved_rect = window->rect; window->unconstrained_rect = window->rect; window->depth = attrs->depth; window->xvisual = attrs->visual; window->title = NULL; window->icon = NULL; window->mini_icon = NULL; window->theme_icon_name = NULL; window->frame = NULL; window->has_focus = FALSE; window->attached_focus_window = NULL; window->maximized_horizontally = FALSE; window->maximized_vertically = FALSE; window->maximize_horizontally_after_placement = FALSE; window->maximize_vertically_after_placement = FALSE; window->minimize_after_placement = FALSE; window->move_after_placement = FALSE; window->fullscreen = FALSE; window->require_fully_onscreen = TRUE; window->require_on_single_monitor = TRUE; window->require_titlebar_visible = TRUE; window->on_all_workspaces = FALSE; window->on_all_workspaces_requested = FALSE; window->tile_mode = META_TILE_NONE; window->tile_monitor_number = -1; window->tile_vfraction = -1.; window->tile_hfraction = -1.; window->shaded = FALSE; window->initially_iconic = FALSE; window->minimized = FALSE; window->tab_unminimized = FALSE; window->iconic = FALSE; window->mapped = attrs->map_state != IsUnmapped; window->known_to_compositor = FALSE; window->visible_to_compositor = FALSE; window->pending_compositor_effect = effect; /* if already mapped, no need to worry about focus-on-first-time-showing */ window->showing_for_first_time = !window->mapped; /* if already mapped we don't want to do the placement thing; * override-redirect windows are placed by the app */ window->placed = ((window->mapped && !window->hidden) || window->override_redirect); window->denied_focus_and_not_transient = FALSE; window->unmanaging = FALSE; window->is_in_queues = 0; window->keys_grabbed = FALSE; window->grab_on_frame = FALSE; window->all_keys_grabbed = FALSE; window->withdrawn = FALSE; window->initial_workspace_set = FALSE; window->initial_timestamp_set = FALSE; window->net_wm_user_time_set = FALSE; window->user_time_window = None; window->input = TRUE; window->calc_placement = FALSE; window->shaken_loose = FALSE; window->have_focus_click_grab = FALSE; window->disable_sync = FALSE; window->unmaps_pending = 0; window->reparents_pending = 0; window->mwm_decorated = TRUE; window->mwm_border_only = FALSE; window->mwm_has_close_func = TRUE; window->mwm_has_minimize_func = TRUE; window->mwm_has_maximize_func = TRUE; window->mwm_has_move_func = TRUE; window->mwm_has_resize_func = TRUE; switch (client_type) { case META_WINDOW_CLIENT_TYPE_X11: window->decorated = TRUE; window->hidden = FALSE; break; case META_WINDOW_CLIENT_TYPE_WAYLAND: window->decorated = FALSE; window->hidden = TRUE; break; } window->has_close_func = TRUE; window->has_minimize_func = TRUE; window->has_maximize_func = TRUE; window->has_move_func = TRUE; window->has_resize_func = TRUE; window->has_shade_func = TRUE; window->has_fullscreen_func = TRUE; window->always_sticky = FALSE; window->skip_taskbar = FALSE; window->skip_pager = FALSE; window->wm_state_above = FALSE; window->wm_state_below = FALSE; window->wm_state_demands_attention = FALSE; window->res_class = NULL; window->res_name = NULL; window->role = NULL; window->sm_client_id = NULL; window->wm_client_machine = NULL; window->is_remote = FALSE; window->startup_id = NULL; window->net_wm_pid = -1; window->xtransient_for = None; window->xclient_leader = None; window->type = META_WINDOW_NORMAL; window->struts = NULL; window->layer = META_LAYER_LAST; /* invalid value */ window->stack_position = -1; window->initial_workspace = 0; /* not used */ window->initial_timestamp = 0; /* not used */ window->compositor_private = NULL; window->monitor = meta_window_calculate_main_logical_monitor (window); if (window->monitor) window->preferred_output_winsys_id = window->monitor->winsys_id; else window->preferred_output_winsys_id = UINT_MAX; window->vtile_match = NULL; window->htile_match = NULL; /* Assign this #MetaWindow a sequence number which can be used * for sorting. */ window->stable_sequence = ++display->window_sequence_counter; window->opacity = 0xFF; if (window->override_redirect) { window->decorated = FALSE; window->always_sticky = TRUE; window->has_close_func = FALSE; window->has_shade_func = FALSE; window->has_move_func = FALSE; window->has_resize_func = FALSE; } window->id = meta_display_generate_window_id (display); meta_window_manage (window); if (!window->override_redirect) meta_window_update_icon_now (window, TRUE); if (window->initially_iconic) { /* WM_HINTS said minimized */ window->minimized = TRUE; meta_verbose ("Window %s asked to start out minimized\n", window->desc); } if (existing_wm_state == IconicState) { /* WM_STATE said minimized */ window->minimized = TRUE; meta_verbose ("Window %s had preexisting WM_STATE = IconicState, minimizing\n", window->desc); /* Assume window was previously placed, though perhaps it's * been iconic its whole life, we have no way of knowing. */ window->placed = TRUE; } /* Apply any window attributes such as initial workspace * based on startup notification */ meta_display_apply_startup_properties (window->display, window); /* Try to get a "launch timestamp" for the window. If the window is * a transient, we'd like to be able to get a last-usage timestamp * from the parent window. If the window has no parent, there isn't * much we can do...except record the current time so that any children * can use this time as a fallback. */ if (!window->override_redirect && !window->net_wm_user_time_set) { /* First, maybe the app was launched with startup notification using an * obsolete version of the spec; use that timestamp if it exists. */ if (window->initial_timestamp_set) /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just * being recorded as a fallback for potential transients */ window->net_wm_user_time = window->initial_timestamp; else if (window->transient_for != NULL) meta_window_set_user_time (window, window->transient_for->net_wm_user_time); else /* NOTE: Do NOT toggle net_wm_user_time_set to true; this is just * being recorded as a fallback for potential transients */ window->net_wm_user_time = meta_display_get_current_time_roundtrip (window->display); } window->attached = meta_window_should_attach_to_parent (window); if (window->attached) meta_window_recalc_features (window); if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK) { /* Change the default, but don't enforce this if the user * focuses the dock/desktop and unsticks it using key shortcuts. * Need to set this before adding to the workspaces so the MRU * lists will be updated. */ window->on_all_workspaces_requested = TRUE; } window->on_all_workspaces = should_be_on_all_workspaces (window); /* For the workspace, first honor hints, * if that fails put transients with parents, * otherwise put window on active space */ if (window->initial_workspace_set) { gboolean on_all_workspaces = window->on_all_workspaces; MetaWorkspace *workspace = NULL; if (window->initial_workspace == (int) 0xFFFFFFFF) { meta_topic (META_DEBUG_PLACEMENT, "Window %s is initially on all spaces\n", window->desc); /* need to set on_all_workspaces first so that it will be * added to all the MRU lists */ window->on_all_workspaces_requested = TRUE; on_all_workspaces = TRUE; } else if (!on_all_workspaces) { meta_topic (META_DEBUG_PLACEMENT, "Window %s is initially on space %d\n", window->desc, window->initial_workspace); workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, window->initial_workspace); } /* Ignore when a window requests to be placed on a non-existent workspace */ if (on_all_workspaces || workspace != NULL) set_workspace_state (window, on_all_workspaces, workspace); } /* override-redirect windows are subtly different from other windows * with window->on_all_workspaces == TRUE. Other windows are part of * some workspace (so they can return to that if the flag is turned off), * but appear on other workspaces. override-redirect windows are part * of no workspace. */ if (!window->override_redirect && window->workspace == NULL) { if (window->transient_for != NULL) { meta_topic (META_DEBUG_PLACEMENT, "Putting window %s on same workspace as parent %s\n", window->desc, window->transient_for->desc); g_warn_if_fail (!window->transient_for->override_redirect); set_workspace_state (window, window->transient_for->on_all_workspaces, window->transient_for->workspace); } else if (window->on_all_workspaces) { meta_topic (META_DEBUG_PLACEMENT, "Putting window %s on all workspaces\n", window->desc); set_workspace_state (window, TRUE, NULL); } else { meta_topic (META_DEBUG_PLACEMENT, "Putting window %s on active workspace\n", window->desc); set_workspace_state (window, FALSE, workspace_manager->active_workspace); } meta_window_update_struts (window); } meta_window_main_monitor_changed (window, NULL); /* Must add window to stack before doing move/resize, since the * window might have fullscreen size (i.e. should have been * fullscreen'd; acrobat is one such braindead case; it withdraws * and remaps its window whenever trying to become fullscreen...) * and thus constraints may try to auto-fullscreen it which also * means restacking it. */ if (meta_window_is_stackable (window)) meta_stack_add (window->display->stack, window); else if (window->override_redirect) window->layer = META_LAYER_OVERRIDE_REDIRECT; /* otherwise set by MetaStack */ if (!window->override_redirect) { /* FIXME we have a tendency to set this then immediately * change it again. */ set_wm_state (window); set_net_wm_state (window); } meta_compositor_add_window (window->display->compositor, window); window->known_to_compositor = TRUE; /* Sync stack changes */ meta_stack_thaw (window->display->stack); /* Usually the we'll have queued a stack sync anyways, because we've * added a new frame window or restacked. But if an undecorated * window is mapped, already stacked in the right place, then we * might need to do this explicitly. */ meta_stack_tracker_queue_sync_stack (window->display->stack_tracker); /* disable show desktop mode unless we're a desktop component */ maybe_leave_show_desktop_mode (window); meta_window_queue (window, META_QUEUE_CALC_SHOWING); /* See bug 303284; a transient of the given window can already exist, in which * case we think it should probably be shown. */ meta_window_foreach_transient (window, queue_calc_showing_func, NULL); /* See bug 334899; the window may have minimized ancestors * which need to be shown. * * However, we shouldn't unminimize windows here when opening * a new display because that breaks passing _NET_WM_STATE_HIDDEN * between window managers when replacing them; see bug 358042. * * And we shouldn't unminimize windows if they were initially * iconic. */ if (!window->override_redirect && !display->display_opening && !window->initially_iconic) unminimize_window_and_all_transient_parents (window); window->constructing = FALSE; meta_display_notify_window_created (display, window); if (window->wm_state_demands_attention) g_signal_emit_by_name (window->display, "window-demands-attention", window); return window; } static gboolean detach_foreach_func (MetaWindow *window, void *data) { GList **children = data; MetaWindow *parent; if (window->attached) { /* Only return the immediate children of the window being unmanaged */ parent = meta_window_get_transient_for (window); if (parent->unmanaging) *children = g_list_prepend (*children, window); } return TRUE; } void meta_window_unmanage (MetaWindow *window, guint32 timestamp) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; GList *tmp; meta_verbose ("Unmanaging %s\n", window->desc); window->unmanaging = TRUE; g_clear_handle_id (&window->unmanage_idle_id, g_source_remove); g_signal_emit (window, window_signals[UNMANAGING], 0); meta_window_free_delete_dialog (window); if (window->visible_to_compositor) { window->visible_to_compositor = FALSE; meta_compositor_hide_window (window->display->compositor, window, META_COMP_EFFECT_DESTROY); } meta_compositor_remove_window (window->display->compositor, window); window->known_to_compositor = FALSE; if (destroying_windows_disallowed > 0) meta_bug ("Tried to destroy window %s while destruction was not allowed\n", window->desc); meta_display_unregister_stamp (window->display, window->stamp); if (meta_prefs_get_attach_modal_dialogs ()) { GList *attached_children = NULL, *iter; /* Detach any attached dialogs by unmapping and letting them * be remapped after @window is destroyed. */ meta_window_foreach_transient (window, detach_foreach_func, &attached_children); for (iter = attached_children; iter; iter = iter->next) meta_window_unmanage (iter->data, timestamp); g_list_free (attached_children); } /* Make sure to only show window on all workspaces if requested, to * not confuse other window managers that may take over */ if (meta_prefs_get_workspaces_only_on_primary ()) meta_window_on_all_workspaces_changed (window); if (window->fullscreen) { MetaGroup *group; /* If the window is fullscreen, it may be forcing * other windows in its group to a higher layer */ meta_stack_freeze (window->display->stack); group = meta_window_get_group (window); if (group) meta_group_update_layers (group); meta_stack_thaw (window->display->stack); } meta_display_remove_pending_pings_for_window (window->display, window); /* safe to do this early as group.c won't re-add to the * group if window->unmanaging */ meta_window_shutdown_group (window); /* If we have the focus, focus some other window. * This is done first, so that if the unmap causes * an EnterNotify the EnterNotify will have final say * on what gets focused, maintaining sloppy focus * invariants. */ if (meta_window_appears_focused (window)) meta_window_propagate_focus_appearance (window, FALSE); if (window->has_focus) { meta_topic (META_DEBUG_FOCUS, "Focusing default window since we're unmanaging %s\n", window->desc); meta_workspace_focus_default_window (workspace_manager->active_workspace, window, timestamp); } else { meta_topic (META_DEBUG_FOCUS, "Unmanaging window %s which doesn't currently have focus\n", window->desc); } g_assert (window->display->focus_window != window); if (window->struts) { g_slist_free_full (window->struts, g_free); window->struts = NULL; meta_topic (META_DEBUG_WORKAREA, "Unmanaging window %s which has struts, so invalidating work areas\n", window->desc); invalidate_work_areas (window); } g_clear_handle_id (&window->sync_request_timeout_id, g_source_remove); if (window->display->grab_window == window) meta_display_end_grab_op (window->display, timestamp); g_assert (window->display->grab_window != window); if (window->maximized_horizontally || window->maximized_vertically) unmaximize_window_before_freeing (window); meta_window_unqueue (window, META_QUEUE_CALC_SHOWING | META_QUEUE_MOVE_RESIZE | META_QUEUE_UPDATE_ICON); set_workspace_state (window, FALSE, NULL); g_assert (window->workspace == NULL); #ifndef G_DISABLE_CHECKS tmp = workspace_manager->workspaces; while (tmp != NULL) { MetaWorkspace *workspace = tmp->data; g_assert (g_list_find (workspace->windows, window) == NULL); g_assert (g_list_find (workspace->mru_list, window) == NULL); tmp = tmp->next; } #endif if (window->monitor) { const MetaLogicalMonitor *old = window->monitor; window->monitor = NULL; meta_window_main_monitor_changed (window, old); } if (meta_window_is_in_stack (window)) meta_stack_remove (window->display->stack, window); /* If an undecorated window is being withdrawn, that will change the * stack as presented to the compositing manager, without actually * changing the stacking order of X windows. */ meta_stack_tracker_queue_sync_stack (window->display->stack_tracker); if (window->display->autoraise_window == window) meta_display_remove_autoraise_callback (window->display); META_WINDOW_GET_CLASS (window)->unmanage (window); meta_prefs_remove_listener (prefs_changed_callback, window); meta_display_queue_check_fullscreen (window->display); g_signal_emit (window, window_signals[UNMANAGED], 0); g_object_unref (window); } static void set_wm_state (MetaWindow *window) { if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) meta_window_x11_set_wm_state (window); } static void set_net_wm_state (MetaWindow *window) { if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) meta_window_x11_set_net_wm_state (window); } static void set_allowed_actions_hint (MetaWindow *window) { if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) meta_window_x11_set_allowed_actions_hint (window); } /** * meta_window_located_on_workspace: * @window: a #MetaWindow * @workspace: a #MetaWorkspace * * Returns: whether @window is displayed on @workspace, or whether it * will be displayed on all workspaces. */ gboolean meta_window_located_on_workspace (MetaWindow *window, MetaWorkspace *workspace) { return (window->on_all_workspaces) || (window->workspace == workspace); } static gboolean is_minimized_foreach (MetaWindow *window, void *data) { gboolean *result = data; *result = window->minimized; if (*result) return FALSE; /* stop as soon as we find one */ else return TRUE; } static gboolean ancestor_is_minimized (MetaWindow *window) { gboolean is_minimized; is_minimized = FALSE; meta_window_foreach_ancestor (window, is_minimized_foreach, &is_minimized); return is_minimized; } /** * meta_window_showing_on_its_workspace: * @window: A #MetaWindow * * Returns: %TRUE if window would be visible, if its workspace was current */ gboolean meta_window_showing_on_its_workspace (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean showing; gboolean is_desktop_or_dock; MetaWorkspace* workspace_of_window; showing = TRUE; /* 1. See if we're minimized */ if (window->minimized) showing = FALSE; /* 2. See if we're in "show desktop" mode */ is_desktop_or_dock = FALSE; is_desktop_or_dock_foreach (window, &is_desktop_or_dock); meta_window_foreach_ancestor (window, is_desktop_or_dock_foreach, &is_desktop_or_dock); if (window->on_all_workspaces) workspace_of_window = workspace_manager->active_workspace; else if (window->workspace) workspace_of_window = window->workspace; else /* This only seems to be needed for startup */ workspace_of_window = NULL; if (showing && workspace_of_window && workspace_of_window->showing_desktop && !is_desktop_or_dock) { meta_verbose ("We're showing the desktop on the workspace(s) that window %s is on\n", window->desc); showing = FALSE; } /* 3. See if an ancestor is minimized (note that * ancestor's "mapped" field may not be up to date * since it's being computed in this same idle queue) */ if (showing) { if (ancestor_is_minimized (window)) showing = FALSE; } return showing; } gboolean meta_window_should_be_showing (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; #ifdef HAVE_WAYLAND if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND && !meta_wayland_surface_get_buffer (window->surface)) return FALSE; #endif /* Windows should be showing if they're located on the * active workspace and they're showing on their own workspace. */ return (meta_window_located_on_workspace (window, workspace_manager->active_workspace) && meta_window_showing_on_its_workspace (window)); } static void implement_showing (MetaWindow *window, gboolean showing) { /* Actually show/hide the window */ meta_verbose ("Implement showing = %d for window %s\n", showing, window->desc); /* Some windows are not stackable until being showed, so add those now. */ if (meta_window_is_stackable (window) && !meta_window_is_in_stack (window)) meta_stack_add (window->display->stack, window); if (!showing) { /* When we manage a new window, we normally delay placing it * until it is is first shown, but if we're previewing hidden * windows we might want to know where they are on the screen, * so we should place the window even if we're hiding it rather * than showing it. * Force placing windows only when they should be already mapped, * see #751887 */ if (!window->placed && client_window_should_be_mapped (window)) meta_window_force_placement (window, FALSE); meta_window_hide (window); } else meta_window_show (window); if (!window->override_redirect) sync_client_window_mapped (window); } static void meta_window_calc_showing (MetaWindow *window) { implement_showing (window, meta_window_should_be_showing (window)); } static guint queue_later[NUMBER_OF_QUEUES] = {0, 0, 0}; static GSList *queue_pending[NUMBER_OF_QUEUES] = {NULL, NULL, NULL}; static int stackcmp (gconstpointer a, gconstpointer b) { MetaWindow *aw = (gpointer) a; MetaWindow *bw = (gpointer) b; return meta_stack_windows_cmp (aw->display->stack, aw, bw); } static gboolean idle_calc_showing (gpointer data) { MetaDisplay *display = meta_get_display (); GSList *tmp; GSList *copy; GSList *should_show; GSList *should_hide; GSList *unplaced; GSList *displays; guint queue_index = GPOINTER_TO_INT (data); g_return_val_if_fail (queue_pending[queue_index] != NULL, FALSE); meta_topic (META_DEBUG_WINDOW_STATE, "Clearing the calc_showing queue\n"); /* Work with a copy, for reentrancy. The allowed reentrancy isn't * complete; destroying a window while we're in here would result in * badness. But it's OK to queue/unqueue calc_showings. */ copy = g_slist_copy (queue_pending[queue_index]); g_slist_free (queue_pending[queue_index]); queue_pending[queue_index] = NULL; queue_later[queue_index] = 0; destroying_windows_disallowed += 1; /* We map windows from top to bottom and unmap from bottom to * top, to avoid extra expose events. The exception is * for unplaced windows, which have to be mapped from bottom to * top so placement works. */ should_show = NULL; should_hide = NULL; unplaced = NULL; displays = NULL; tmp = copy; while (tmp != NULL) { MetaWindow *window; window = tmp->data; if (!window->placed) unplaced = g_slist_prepend (unplaced, window); else if (meta_window_should_be_showing (window)) should_show = g_slist_prepend (should_show, window); else should_hide = g_slist_prepend (should_hide, window); tmp = tmp->next; } /* bottom to top */ unplaced = g_slist_sort (unplaced, stackcmp); should_hide = g_slist_sort (should_hide, stackcmp); /* top to bottom */ should_show = g_slist_sort (should_show, stackcmp); should_show = g_slist_reverse (should_show); tmp = unplaced; while (tmp != NULL) { MetaWindow *window; window = tmp->data; meta_window_calc_showing (window); tmp = tmp->next; } meta_stack_freeze (display->stack); tmp = should_show; while (tmp != NULL) { MetaWindow *window; window = tmp->data; implement_showing (window, TRUE); tmp = tmp->next; } tmp = should_hide; while (tmp != NULL) { MetaWindow *window; window = tmp->data; implement_showing (window, FALSE); tmp = tmp->next; } meta_stack_thaw (display->stack); tmp = copy; while (tmp != NULL) { MetaWindow *window; window = tmp->data; /* important to set this here for reentrancy - * if we queue a window again while it's in "copy", * then queue_calc_showing will just return since * we are still in the calc_showing queue */ window->is_in_queues &= ~META_QUEUE_CALC_SHOWING; tmp = tmp->next; } if (meta_prefs_get_focus_mode () != C_DESKTOP_FOCUS_MODE_CLICK) { /* When display->mouse_mode is false, we want to ignore * EnterNotify events unless they come from mouse motion. To do * that, we set a sentinel property on the root window if we're * not in mouse_mode. */ tmp = should_show; while (tmp != NULL) { MetaWindow *window = tmp->data; MetaDisplay *display = window->display; if (display->x11_display && !display->mouse_mode) meta_x11_display_increment_focus_sentinel (display->x11_display); tmp = tmp->next; } } g_slist_free (copy); g_slist_free (unplaced); g_slist_free (should_show); g_slist_free (should_hide); g_slist_free (displays); destroying_windows_disallowed -= 1; return FALSE; } #ifdef WITH_VERBOSE_MODE static const gchar* meta_window_queue_names[NUMBER_OF_QUEUES] = {"calc_showing", "move_resize", "update_icon"}; #endif static void meta_window_unqueue (MetaWindow *window, guint queuebits) { gint queuenum; for (queuenum=0; queuenumis_in_queues & 1<desc, meta_window_queue_names[queuenum]); /* Note that window may not actually be in the queue * because it may have been in "copy" inside the idle handler */ queue_pending[queuenum] = g_slist_remove (queue_pending[queuenum], window); window->is_in_queues &= ~(1<is_in_queues & META_QUEUE_CALC_SHOWING) { meta_window_unqueue (window, META_QUEUE_CALC_SHOWING); meta_window_calc_showing (window); } } void meta_window_queue (MetaWindow *window, guint queuebits) { guint queuenum; /* Easier to debug by checking here rather than in the idle */ g_return_if_fail (!window->override_redirect || (queuebits & META_QUEUE_MOVE_RESIZE) == 0); for (queuenum=0; queuenumunmanaging) break; /* If the window already claims to be in that queue, there's no * point putting it in the queue. */ if (window->is_in_queues & 1<desc, meta_window_queue_names[queuenum]); /* So, mark it as being in this queue. */ window->is_in_queues |= 1<display->focus_window; meta_topic (META_DEBUG_STARTUP, "COMPARISON:\n" " net_wm_user_time_set : %d\n" " net_wm_user_time : %u\n" " initial_timestamp_set: %d\n" " initial_timestamp : %u\n", window->net_wm_user_time_set, window->net_wm_user_time, window->initial_timestamp_set, window->initial_timestamp); if (focus_window != NULL) { meta_topic (META_DEBUG_STARTUP, "COMPARISON (continued):\n" " focus_window : %s\n" " fw->net_wm_user_time_set : %d\n" " fw->net_wm_user_time : %u\n", focus_window->desc, focus_window->net_wm_user_time_set, focus_window->net_wm_user_time); } /* We expect the most common case for not focusing a new window * to be when a hint to not focus it has been set. Since we can * deal with that case rapidly, we use special case it--this is * merely a preliminary optimization. :) */ if ( ((window->net_wm_user_time_set == TRUE) && (window->net_wm_user_time == 0)) || ((window->initial_timestamp_set == TRUE) && (window->initial_timestamp == 0))) { meta_topic (META_DEBUG_STARTUP, "window %s explicitly requested no focus\n", window->desc); return TRUE; } if (!(window->net_wm_user_time_set) && !(window->initial_timestamp_set)) { meta_topic (META_DEBUG_STARTUP, "no information about window %s found\n", window->desc); return FALSE; } if (focus_window != NULL && !focus_window->net_wm_user_time_set) { meta_topic (META_DEBUG_STARTUP, "focus window, %s, doesn't have a user time set yet!\n", window->desc); return FALSE; } /* To determine the "launch" time of an application, * startup-notification can set the TIMESTAMP and the * application (usually via its toolkit such as gtk or qt) can * set the _NET_WM_USER_TIME. If both are set, we need to be * using the newer of the two values. * * See http://bugzilla.gnome.org/show_bug.cgi?id=573922 */ compare = 0; if (window->net_wm_user_time_set && window->initial_timestamp_set) compare = XSERVER_TIME_IS_BEFORE (window->net_wm_user_time, window->initial_timestamp) ? window->initial_timestamp : window->net_wm_user_time; else if (window->net_wm_user_time_set) compare = window->net_wm_user_time; else if (window->initial_timestamp_set) compare = window->initial_timestamp; if ((focus_window != NULL) && XSERVER_TIME_IS_BEFORE (compare, focus_window->net_wm_user_time)) { meta_topic (META_DEBUG_STARTUP, "window %s focus prevented by other activity; %u < %u\n", window->desc, compare, focus_window->net_wm_user_time); return TRUE; } else { meta_topic (META_DEBUG_STARTUP, "new window %s with no intervening events\n", window->desc); return FALSE; } } /* This function is an ugly hack. It's experimental in nature and ought to be * replaced by a real hint from the app to the WM if we decide the experimental * behavior is worthwhile. The basic idea is to get more feedback about how * usage scenarios of "strict" focus users and what they expect. See #326159. */ static gboolean window_is_terminal (MetaWindow *window) { if (window == NULL || window->res_class == NULL) return FALSE; /* * Compare res_class, which is not user-settable, and thus theoretically * a more-reliable indication of term-ness. */ /* gnome-terminal -- if you couldn't guess */ if (strcmp (window->res_class, "Gnome-terminal") == 0) return TRUE; /* xterm, rxvt, aterm */ else if (strcmp (window->res_class, "XTerm") == 0) return TRUE; /* konsole, KDE's terminal program */ else if (strcmp (window->res_class, "Konsole") == 0) return TRUE; /* rxvt-unicode */ else if (strcmp (window->res_class, "URxvt") == 0) return TRUE; /* eterm */ else if (strcmp (window->res_class, "Eterm") == 0) return TRUE; /* KTerm -- some terminal not KDE based; so not like Konsole */ else if (strcmp (window->res_class, "KTerm") == 0) return TRUE; /* Multi-gnome-terminal */ else if (strcmp (window->res_class, "Multi-gnome-terminal") == 0) return TRUE; /* mlterm ("multi lingual terminal emulator on X") */ else if (strcmp (window->res_class, "mlterm") == 0) return TRUE; /* Terminal -- XFCE Terminal */ else if (strcmp (window->res_class, "Terminal") == 0) return TRUE; else if (strcmp (window->res_class, "Tilix") == 0) return TRUE; else if (strcmp (window->res_class, "qterminal") == 0) return TRUE; return FALSE; } /* This function determines what state the window should have assuming that it * and the focus_window have no relation */ static void window_state_on_map (MetaWindow *window, gboolean *takes_focus, gboolean *places_on_top) { gboolean intervening_events; intervening_events = intervening_user_event_occurred (window); *takes_focus = !intervening_events; *places_on_top = *takes_focus; /* don't initially focus windows that are intended to not accept * focus */ if (!meta_window_is_focusable (window)) { *takes_focus = FALSE; return; } /* Terminal usage may be different; some users intend to launch * many apps in quick succession or to just view things in the new * window while still interacting with the terminal. In that case, * apps launched from the terminal should not take focus. This * isn't quite the same as not allowing focus to transfer from * terminals due to new window map, but the latter is a much easier * approximation to enforce so we do that. */ if (*takes_focus && meta_prefs_get_focus_new_windows () == C_DESKTOP_FOCUS_NEW_WINDOWS_STRICT && !window->display->allow_terminal_deactivation && window_is_terminal (window->display->focus_window) && !meta_window_is_ancestor_of_transient (window->display->focus_window, window)) { meta_topic (META_DEBUG_FOCUS, "focus_window is terminal; not focusing new window.\n"); *takes_focus = FALSE; *places_on_top = FALSE; } switch (window->type) { case META_WINDOW_UTILITY: case META_WINDOW_TOOLBAR: *takes_focus = FALSE; *places_on_top = FALSE; break; case META_WINDOW_DOCK: case META_WINDOW_DESKTOP: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_MENU: /* override redirect types: */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: /* don't focus any of these; places_on_top may be irrelevant for some of * these (e.g. dock)--but you never know--the focus window might also be * of the same type in some weird situation... */ *takes_focus = FALSE; break; case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: /* The default is correct for these */ break; } } static gboolean windows_overlap (const MetaWindow *w1, const MetaWindow *w2) { MetaRectangle w1rect, w2rect; meta_window_get_frame_rect (w1, &w1rect); meta_window_get_frame_rect (w2, &w2rect); return meta_rectangle_overlap (&w1rect, &w2rect); } /* Returns whether a new window would be covered by any * existing window on the same workspace that is set * to be "above" ("always on top"). A window that is not * set "above" would be underneath the new window anyway. * * We take "covered" to mean even partially covered, but * some people might prefer entirely covered. I think it * is more useful to behave this way if any part of the * window is covered, because a partial coverage could be * (say) ninety per cent and almost indistinguishable from total. */ static gboolean window_may_be_covered_by_above (const MetaWindow *newbie) { MetaBackend *backend = meta_get_backend (); MetaLogicalMonitor *monitor; MetaWorkspace *workspace = meta_window_get_workspace ((MetaWindow *)newbie); GList *tmp, *windows; monitor = meta_backend_get_current_logical_monitor (backend); windows = meta_workspace_list_windows (workspace); gboolean overlaps = FALSE; tmp = windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (w->wm_state_above && w != newbie) { /* We have found a window that is "above". Do some tests. This is run before * newbie is actually placed, so its position and monitor are not yet accurate, * but it's not feasible to do it after either (because then it's too late). These * tests can eliminate a lot of silly false positives, however. */ // Minimized - no overlap if (w->minimized) { tmp = tmp->next; continue; } // Maximized, same monitor, overlap. if (w->monitor->number == monitor->number && meta_window_get_maximized (w)) { overlaps = TRUE; break; } MetaRectangle w_rect, w_in_area_rect, work_area; meta_window_get_frame_rect (w, &w_rect); meta_workspace_get_work_area_for_monitor (workspace, monitor->number, &work_area); // If the above window is at least partially on the target monitor, check if the new window // would theoretically have enough room leftover to be completely uncovered. This is no // guarantee it *will* be uncovered, but it's the best we can do without an accurate position // for the new window. if (meta_rectangle_intersect (&work_area, &w_rect, &w_in_area_rect)) { if ((newbie->rect.width + w_in_area_rect.width > work_area.width) && (newbie->rect.height + w_in_area_rect.height > work_area.height)) { overlaps = TRUE; break; } } } tmp = tmp->next; } g_list_free (windows); return overlaps; } void meta_window_force_placement (MetaWindow *window, gboolean force_move) { MetaMoveResizeFlags flags; if (window->placed) return; /* We have to recalc the placement here since other windows may * have been mapped/placed since we last did constrain_position */ /* calc_placement is an efficiency hack to avoid * multiple placement calculations before we finally * show the window. */ window->calc_placement = TRUE; flags = META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; if (force_move) flags |= META_MOVE_RESIZE_FORCE_MOVE; meta_window_move_resize_internal (window, flags, META_GRAVITY_NORTH_WEST, window->unconstrained_rect); window->calc_placement = FALSE; /* don't ever do the initial position constraint thing again. * This is toggled here so that initially-iconified windows * still get placed when they are ultimately shown. */ window->placed = TRUE; /* Don't want to accidentally reuse the fact that we had been denied * focus in any future constraints unless we're denied focus again. */ window->denied_focus_and_not_transient = FALSE; } static void meta_window_show (MetaWindow *window) { gboolean did_show; gboolean takes_focus_on_map; gboolean place_on_top_on_map; gboolean needs_stacking_adjustment; MetaWindow *focus_window; gboolean notify_demands_attention = FALSE; MetaDisplay *display = window->display; meta_topic (META_DEBUG_WINDOW_STATE, "Showing window %s, shaded: %d iconic: %d placed: %d\n", window->desc, window->shaded, window->iconic, window->placed); focus_window = window->display->focus_window; /* May be NULL! */ did_show = FALSE; window_state_on_map (window, &takes_focus_on_map, &place_on_top_on_map); needs_stacking_adjustment = FALSE; meta_topic (META_DEBUG_WINDOW_STATE, "Window %s %s focus on map, and %s place on top on map.\n", window->desc, takes_focus_on_map ? "does" : "does not", place_on_top_on_map ? "does" : "does not"); /* Now, in some rare cases we should *not* put a new window on top. * These cases include certain types of windows showing for the first * time, and any window which would be covered because of another window * being set "above" ("always on top"). * * FIXME: Although "place_on_top_on_map" and "takes_focus_on_map" are * generally based on the window type, there is a special case when the * focus window is a terminal for them both to be false; this should * probably rather be a term in the "if" condition below. */ if ( focus_window != NULL && window->showing_for_first_time && ( (!place_on_top_on_map && !takes_focus_on_map) || window_may_be_covered_by_above (window) ) ) { if (!meta_window_is_ancestor_of_transient (focus_window, window)) { needs_stacking_adjustment = TRUE; if (!window->placed) window->denied_focus_and_not_transient = TRUE; } } if (!window->placed) { if (window->monitor && meta_prefs_get_auto_maximize() && window->showing_for_first_time && window->has_maximize_func) { MetaRectangle work_area; meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area); /* Automaximize windows that map with a size > MAX_UNMAXIMIZED_WINDOW_AREA of the work area */ if (window->rect.width * window->rect.height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) { window->maximize_horizontally_after_placement = TRUE; window->maximize_vertically_after_placement = TRUE; } } meta_window_force_placement (window, FALSE); } if (needs_stacking_adjustment) { gboolean overlap; /* This window isn't getting focus on map. We may need to do some * special handing with it in regards to * - the stacking of the window * - the MRU position of the window * - the demands attention setting of the window * * Firstly, set the flag so we don't give the window focus anyway * and confuse people. */ takes_focus_on_map = FALSE; overlap = windows_overlap (window, focus_window); /* We want alt tab to go to the denied-focus window */ ensure_mru_position_after (window, focus_window); /* We don't want the denied-focus window to obscure the focus * window, and if we're in both click-to-focus mode and * raise-on-click mode then we want to maintain the invariant * that MRU order == stacking order. The need for this if * comes from the fact that in sloppy/mouse focus the focus * window may not overlap other windows and also can be * considered "below" them; this combination means that * placing the denied-focus window "below" the focus window * in the stack when it doesn't overlap it confusingly places * that new window below a lot of other windows. */ if (overlap || (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK && meta_prefs_get_raise_on_click ())) meta_window_stack_just_below (window, focus_window); /* If the window will be obscured by the focus window, then the * user might not notice the window appearing so set the * demands attention hint. * * We set the hint ourselves rather than calling * meta_window_set_demands_attention() because that would cause * a recalculation of overlap, and a call to set_net_wm_state() * which we are going to call ourselves here a few lines down. */ if (overlap) { if (!window->wm_state_demands_attention) { window->wm_state_demands_attention = TRUE; notify_demands_attention = TRUE; } } } if (window->hidden) { meta_stack_freeze (window->display->stack); window->hidden = FALSE; meta_stack_thaw (window->display->stack); did_show = TRUE; } if (window->iconic) { window->iconic = FALSE; set_wm_state (window); } if (!window->visible_to_compositor) { MetaCompEffect effect = META_COMP_EFFECT_NONE; window->visible_to_compositor = TRUE; switch (window->pending_compositor_effect) { case META_COMP_EFFECT_CREATE: case META_COMP_EFFECT_UNMINIMIZE: effect = window->pending_compositor_effect; break; case META_COMP_EFFECT_NONE: case META_COMP_EFFECT_DESTROY: case META_COMP_EFFECT_MINIMIZE: break; } meta_compositor_show_window (window->display->compositor, window, effect); window->pending_compositor_effect = META_COMP_EFFECT_NONE; } /* We don't want to worry about all cases from inside * implement_showing(); we only want to worry about focus if this * window has not been shown before. */ if (window->showing_for_first_time) { window->showing_for_first_time = FALSE; if (takes_focus_on_map) { guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_focus (window, timestamp); if (window->move_after_placement) { timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_begin_grab_op(window, META_GRAB_OP_KEYBOARD_MOVING, FALSE, timestamp); window->move_after_placement = FALSE; } } else if (display->x11_display) { /* Prevent EnterNotify events in sloppy/mouse focus from * erroneously focusing the window that had been denied * focus. FIXME: This introduces a race; I have a couple * ideas for a better way to accomplish the same thing, but * they're more involved so do it this way for now. */ meta_x11_display_increment_focus_sentinel (display->x11_display); } } set_net_wm_state (window); if (did_show && window->struts) { meta_topic (META_DEBUG_WORKAREA, "Mapped window %s with struts, so invalidating work areas\n", window->desc); invalidate_work_areas (window); } if (did_show) meta_display_queue_check_fullscreen (window->display); /* * Now that we have shown the window, we no longer want to consider the * initial timestamp in any subsequent deliberations whether to focus this * window or not, so clear the flag. * * See http://bugzilla.gnome.org/show_bug.cgi?id=573922 */ window->initial_timestamp_set = FALSE; if (notify_demands_attention) { g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); g_signal_emit_by_name (window->display, "window-demands-attention", window); } if (did_show) g_signal_emit (window, window_signals[SHOWN], 0); } static void meta_window_hide (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean did_hide; meta_topic (META_DEBUG_WINDOW_STATE, "Hiding window %s\n", window->desc); if (window->visible_to_compositor) { MetaCompEffect effect = META_COMP_EFFECT_NONE; window->visible_to_compositor = FALSE; switch (window->pending_compositor_effect) { case META_COMP_EFFECT_CREATE: case META_COMP_EFFECT_UNMINIMIZE: case META_COMP_EFFECT_NONE: break; case META_COMP_EFFECT_DESTROY: case META_COMP_EFFECT_MINIMIZE: effect = window->pending_compositor_effect; break; } meta_compositor_hide_window (window->display->compositor, window, effect); window->pending_compositor_effect = META_COMP_EFFECT_NONE; } did_hide = FALSE; if (!window->hidden) { meta_stack_freeze (window->display->stack); window->hidden = TRUE; meta_stack_thaw (window->display->stack); did_hide = TRUE; } if (!window->iconic) { window->iconic = TRUE; set_wm_state (window); } set_net_wm_state (window); if (did_hide && window->struts) { meta_topic (META_DEBUG_WORKAREA, "Unmapped window %s with struts, so invalidating work areas\n", window->desc); invalidate_work_areas (window); } if (window->has_focus) { MetaWindow *not_this_one = NULL; MetaWorkspace *my_workspace = meta_window_get_workspace (window); guint32 timestamp = meta_display_get_current_time_roundtrip (window->display); /* * If this window is modal, passing the not_this_one window to * _focus_default_window() makes the focus to be given to this window's * ancestor. This can only be the case if the window is on the currently * active workspace; when it is not, we need to pass in NULL, so as to * focus the default window for the active workspace (this scenario * arises when we are switching workspaces). * We also pass in NULL if we are in the process of hiding all non-desktop * windows to avoid unexpected changes to the stacking order. */ if (my_workspace == workspace_manager->active_workspace && !my_workspace->showing_desktop) not_this_one = window; meta_workspace_focus_default_window (workspace_manager->active_workspace, not_this_one, timestamp); } if (did_hide) meta_display_queue_check_fullscreen (window->display); } static gboolean queue_calc_showing_func (MetaWindow *window, void *data) { meta_window_queue(window, META_QUEUE_CALC_SHOWING); return TRUE; } void meta_window_minimize (MetaWindow *window) { g_return_if_fail (!window->override_redirect); if (!window->minimized) { window->minimized = TRUE; window->pending_compositor_effect = META_COMP_EFFECT_MINIMIZE; meta_window_queue(window, META_QUEUE_CALC_SHOWING); meta_window_foreach_transient (window, queue_calc_showing_func, NULL); if (window->has_focus) { meta_topic (META_DEBUG_FOCUS, "Focusing default window due to minimization of focus window %s\n", window->desc); } else { meta_topic (META_DEBUG_FOCUS, "Minimizing window %s which doesn't have the focus\n", window->desc); } g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINIMIZED]); } } void meta_window_unminimize (MetaWindow *window) { g_return_if_fail (!window->override_redirect); if (window->minimized) { window->minimized = FALSE; window->pending_compositor_effect = META_COMP_EFFECT_UNMINIMIZE; meta_window_queue(window, META_QUEUE_CALC_SHOWING); meta_window_foreach_transient (window, queue_calc_showing_func, NULL); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINIMIZED]); } } static void ensure_size_hints_satisfied (MetaRectangle *rect, const XSizeHints *size_hints) { int minw, minh, maxw, maxh; /* min/max width/height */ int basew, baseh, winc, hinc; /* base width/height, width/height increment */ int extra_width, extra_height; minw = size_hints->min_width; minh = size_hints->min_height; maxw = size_hints->max_width; maxh = size_hints->max_height; basew = size_hints->base_width; baseh = size_hints->base_height; winc = size_hints->width_inc; hinc = size_hints->height_inc; /* First, enforce min/max size constraints */ rect->width = CLAMP (rect->width, minw, maxw); rect->height = CLAMP (rect->height, minh, maxh); /* Now, verify size increment constraints are satisfied, or make them be */ extra_width = (rect->width - basew) % winc; extra_height = (rect->height - baseh) % hinc; rect->width -= extra_width; rect->height -= extra_height; /* Adjusting width/height down, as done above, may violate minimum size * constraints, so one last fix. */ if (rect->width < minw) rect->width += ((minw - rect->width)/winc + 1)*winc; if (rect->height < minh) rect->height += ((minh - rect->height)/hinc + 1)*hinc; } static void meta_window_save_rect (MetaWindow *window) { if (!(META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window) || window->fullscreen)) { /* save size/pos as appropriate args for move_resize */ if (!window->maximized_horizontally) { window->saved_rect.x = window->rect.x; window->saved_rect.width = window->rect.width; } if (!window->maximized_vertically) { window->saved_rect.y = window->rect.y; window->saved_rect.height = window->rect.height; } } } void meta_window_maximize_internal (MetaWindow *window, MetaMaximizeFlags directions, MetaRectangle *saved_rect) { /* At least one of the two directions ought to be set */ gboolean maximize_horizontally, maximize_vertically; maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; maximize_vertically = directions & META_MAXIMIZE_VERTICAL; g_assert (maximize_horizontally || maximize_vertically); meta_topic (META_DEBUG_WINDOW_OPS, "Maximizing %s%s\n", window->desc, maximize_horizontally && maximize_vertically ? "" : maximize_horizontally ? " horizontally" : maximize_vertically ? " vertically" : "BUGGGGG"); if (saved_rect != NULL) window->saved_rect = *saved_rect; else meta_window_save_rect (window); if (maximize_horizontally && maximize_vertically) window->saved_tile_mode = META_TILE_MAXIMIZED; else window->saved_tile_mode = window->tile_mode; window->maximized_horizontally = window->maximized_horizontally || maximize_horizontally; window->maximized_vertically = window->maximized_vertically || maximize_vertically; /* Update the edge constraints */ update_edge_constraints (window);; meta_window_recalc_features (window); set_net_wm_state (window); if (window->monitor && window->monitor->in_fullscreen) meta_display_queue_check_fullscreen (window->display); g_object_freeze_notify (G_OBJECT (window)); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_HORIZONTALLY]); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_VERTICALLY]); g_object_thaw_notify (G_OBJECT (window)); } void meta_window_maximize (MetaWindow *window, MetaMaximizeFlags directions) { MetaRectangle *saved_rect = NULL; gboolean maximize_horizontally, maximize_vertically; g_return_if_fail (!window->override_redirect); /* At least one of the two directions ought to be set */ maximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; maximize_vertically = directions & META_MAXIMIZE_VERTICAL; g_assert (maximize_horizontally || maximize_vertically); /* Only do something if the window isn't already maximized in the * given direction(s). */ if ((maximize_horizontally && !window->maximized_horizontally) || (maximize_vertically && !window->maximized_vertically)) { if (window->shaded && maximize_vertically) { /* Shading sucks anyway; I'm not adding a timestamp argument * to this function just for this niche usage & corner case. */ guint32 timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_unshade (window, timestamp); } /* if the window hasn't been placed yet, we'll maximize it then */ if (!window->placed) { window->maximize_horizontally_after_placement = window->maximize_horizontally_after_placement || maximize_horizontally; window->maximize_vertically_after_placement = window->maximize_vertically_after_placement || maximize_vertically; return; } gboolean from_tiled = FALSE; MetaRectangle resize_rect; if (META_WINDOW_MAXIMIZED (window) || META_WINDOW_TILED (window)) { saved_rect = &window->saved_rect; window->maximized_vertically = FALSE; window->tile_mode = META_TILE_NONE; from_tiled = TRUE; notify_tile_mode (window); } meta_window_maximize_internal (window, directions, saved_rect); MetaRectangle old_frame_rect, old_buffer_rect; meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); meta_compositor_size_change_window (window->display->compositor, window, META_SIZE_CHANGE_MAXIMIZE, &old_frame_rect, &old_buffer_rect); resize_rect = window->unconstrained_rect; if (from_tiled) { resize_rect.x = old_frame_rect.x; resize_rect.y = old_frame_rect.y; } meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED), META_GRAVITY_NORTH_WEST, resize_rect); } } /** * meta_window_get_maximized: * @window: a #MetaWindow * * Gets the current maximization state of the window, as combination * of the %META_MAXIMIZE_HORIZONTAL and %META_MAXIMIZE_VERTICAL flags; * * Return value: current maximization state */ MetaMaximizeFlags meta_window_get_maximized (MetaWindow *window) { return ((window->maximized_horizontally ? META_MAXIMIZE_HORIZONTAL : 0) | (window->maximized_vertically ? META_MAXIMIZE_VERTICAL : 0)); } /** * meta_window_is_fullscreen: * @window: a #MetaWindow * * Return value: %TRUE if the window is currently fullscreen */ gboolean meta_window_is_fullscreen (MetaWindow *window) { return window->fullscreen; } /** * meta_window_is_screen_sized: * @window: A #MetaWindow * * Return value: %TRUE if the window is occupies the * the whole screen (all monitors). */ gboolean meta_window_is_screen_sized (MetaWindow *window) { MetaRectangle window_rect; int screen_width, screen_height; meta_display_get_size (window->display, &screen_width, &screen_height); meta_window_get_frame_rect (window, &window_rect); if (window_rect.x == 0 && window_rect.y == 0 && window_rect.width == screen_width && window_rect.height == screen_height) return TRUE; return FALSE; } /** * meta_window_is_monitor_sized: * @window: a #MetaWindow * * Return value: %TRUE if the window is occupies an entire monitor or * the whole screen. */ gboolean meta_window_is_monitor_sized (MetaWindow *window) { if (!window->monitor) return FALSE; if (window->fullscreen) return TRUE; if (meta_window_is_screen_sized (window)) return TRUE; if (window->override_redirect) { MetaRectangle window_rect, monitor_rect; meta_window_get_frame_rect (window, &window_rect); meta_display_get_monitor_geometry (window->display, window->monitor->number, &monitor_rect); if (meta_rectangle_equal (&window_rect, &monitor_rect)) return TRUE; } return FALSE; } /** * meta_window_is_on_primary_monitor: * @window: a #MetaWindow * * Return value: %TRUE if the window is on the primary monitor */ gboolean meta_window_is_on_primary_monitor (MetaWindow *window) { g_return_val_if_fail (window->monitor, FALSE); return window->monitor->is_primary; } /** * meta_window_requested_bypass_compositor: * @window: a #MetaWindow * * Return value: %TRUE if the window requested to bypass the compositor */ gboolean meta_window_requested_bypass_compositor (MetaWindow *window) { return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_ON; } /** * meta_window_requested_dont_bypass_compositor: * @window: a #MetaWindow * * Return value: %TRUE if the window requested to opt out of unredirecting */ gboolean meta_window_requested_dont_bypass_compositor (MetaWindow *window) { return window->bypass_compositor == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF; } static void get_default_tile_fractions (MetaTileMode for_mode, double *hfraction, double *vfraction) { switch (for_mode) { case META_TILE_LEFT: case META_TILE_RIGHT: { *hfraction = 0.5; *vfraction = 1.0; break; } case META_TILE_TOP: case META_TILE_BOTTOM: { *hfraction = 1.0; *vfraction = 0.5; break; } case META_TILE_ULC: case META_TILE_URC: case META_TILE_LRC: case META_TILE_LLC: default: { *hfraction = 0.5; *vfraction = 0.5; break; } } } static void meta_window_get_tile_fractions (MetaWindow *window, MetaTileMode tile_mode, double *hfraction, double *vfraction, gboolean *is_valid) { MetaWindow *htile_match, *vtile_match; MetaRectangle client_rect, tile_area, work_area; double new_hfraction, new_vfraction; gint monitor; monitor = meta_display_get_current_monitor (window->display); meta_window_get_work_area_for_monitor (window, monitor, &work_area); /* Make sure the tile match is up-to-date and matches the * passed in mode rather than the current state */ if (tile_mode == META_TILE_NONE) { *hfraction = -1.; *vfraction = -1.; return; } else if (tile_mode == META_TILE_MAXIMIZED) { *hfraction = 1.; *vfraction = 1.; return; } // FIXME: should we fill both or just vtile if htile fails - both i think. htile_match = meta_window_find_tile_match (window, tile_mode, FALSE); vtile_match = meta_window_find_tile_match (window, tile_mode, TRUE); #if 0 g_printerr ("htile: %p (%s), vtile: %p (%s)\n", htile_match, htile_match ? htile_match->title : "", vtile_match, vtile_match ? vtile_match->title: ""); #endif get_default_tile_fractions (tile_mode, &new_hfraction, &new_vfraction); if (vtile_match) { new_vfraction = 1. - vtile_match->tile_vfraction; } if (htile_match) { new_hfraction = 1. - htile_match->tile_hfraction; } if (META_WINDOW_TILED (window)) { if (window->tile_mode != tile_mode) { new_hfraction = 1. - window->tile_hfraction; new_vfraction = 1. - window->tile_vfraction; } else { if (!htile_match) { new_hfraction = window->tile_hfraction; } if (!vtile_match) { new_vfraction = window->tile_vfraction; } } } tile_area = work_area; tile_area.width = work_area.width * new_hfraction; tile_area.height = work_area.height * new_vfraction; meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect); if (client_rect.width >= window->size_hints.min_width && client_rect.height >= window->size_hints.min_height) { *hfraction = new_hfraction; *vfraction = new_vfraction; if (is_valid) *is_valid = TRUE; return; } if (meta_grab_op_is_resizing (window->display->grab_op)) return; get_default_tile_fractions (tile_mode, &new_hfraction, &new_vfraction); tile_area = work_area; tile_area.width = work_area.width * new_hfraction; tile_area.height = work_area.height * new_vfraction; meta_window_frame_rect_to_client_rect (window, &tile_area, &client_rect); if (client_rect.width >= window->size_hints.min_width && client_rect.height >= window->size_hints.min_height) { *hfraction = new_hfraction; *vfraction = new_vfraction; if (is_valid) *is_valid = TRUE; } } static void meta_window_update_tile_fractions (MetaWindow *window, int new_w, int new_h) { MetaWindow *vtile_match = window->vtile_match; MetaWindow *htile_match = window->htile_match; MetaRectangle work_area; if (!META_WINDOW_TILED (window)) return; meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, &work_area); window->tile_hfraction = (double)new_w / work_area.width; window->tile_vfraction = (double)new_h / work_area.height; if (htile_match && window->display->grab_window == window) meta_window_tile (htile_match, htile_match->tile_mode); if (vtile_match && window->display->grab_window == window) meta_window_tile (vtile_match, vtile_match->tile_mode); } static void update_edge_constraints (MetaWindow *window) { switch (window->tile_mode) { case META_TILE_NONE: window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; break; case META_TILE_MAXIMIZED: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_LEFT: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; if (window->htile_match) window->edge_constraints.right = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_RIGHT: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; if (window->htile_match) window->edge_constraints.left = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; break; case META_TILE_TOP: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; if (window->vtile_match) window->edge_constraints.bottom = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.bottom = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_BOTTOM: if (window->vtile_match) window->edge_constraints.top = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_ULC: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; if (window->htile_match) window->edge_constraints.right = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; if (window->vtile_match) window->edge_constraints.bottom = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.bottom = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_LLC: if (window->vtile_match) window->edge_constraints.top = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; if (window->htile_match) window->edge_constraints.right = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.right = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; break; case META_TILE_URC: window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; if (window->vtile_match) window->edge_constraints.bottom = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.bottom = META_EDGE_CONSTRAINT_NONE; if (window->htile_match) window->edge_constraints.left = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; break; case META_TILE_LRC: if (window->vtile_match) window->edge_constraints.top = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.top = META_EDGE_CONSTRAINT_NONE; window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; if (window->htile_match) window->edge_constraints.left = META_EDGE_CONSTRAINT_WINDOW; else window->edge_constraints.left = META_EDGE_CONSTRAINT_NONE; break; } if (window->tile_mode != META_TILE_NONE) return; /* h/vmaximize also modify the edge constraints */ if (window->maximized_vertically) { window->edge_constraints.top = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.bottom = META_EDGE_CONSTRAINT_MONITOR; } if (window->maximized_horizontally) { window->edge_constraints.right = META_EDGE_CONSTRAINT_MONITOR; window->edge_constraints.left = META_EDGE_CONSTRAINT_MONITOR; } } static void notify_tile_mode (MetaWindow *window) { g_object_freeze_notify (G_OBJECT (window)); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_TILE_MODE]); g_object_thaw_notify (G_OBJECT (window)); } void meta_window_tile (MetaWindow *window, MetaTileMode tile_mode) { MetaMaximizeFlags directions; gboolean changed = FALSE; /* Maximization constraints beat tiling constraints, so if the window * is maximized, tiling won't have any effect unless we unmaximize it * horizontally first; rather than calling meta_window_unmaximize(), * we just set the flag and rely on meta_window_tile() syncing it to * save an additional roundtrip. */ meta_window_get_tile_fractions (window, tile_mode, &window->tile_hfraction, &window->tile_vfraction, NULL); window->maximized_horizontally = FALSE; window->maximized_vertically = FALSE; changed = tile_mode != window->tile_mode; window->tile_mode = tile_mode; /* Don't do anything if no tiling is requested */ if (window->tile_mode == META_TILE_NONE) { window->tile_monitor_number = -1; return; } if (window->tile_mode == META_TILE_MAXIMIZED) directions = META_MAXIMIZE_BOTH; else directions = META_MAXIMIZE_VERTICAL; GdkRectangle *maybe_saved_rect = NULL; if (window->saved_tile_mode == META_TILE_NONE && window->display->grab_op != META_GRAB_OP_NONE) { // If we let it save the current rect when initially tiling, the rect position // may end up on a different monitor (depending on where the grab anchor is positioned). maybe_saved_rect = &window->display->grab_initial_window_pos; } else if (window->saved_tile_mode != META_TILE_NONE) { maybe_saved_rect = &window->saved_rect; } meta_window_maximize_internal (window, directions, maybe_saved_rect); meta_display_update_tile_preview (window->display, FALSE); if ((!window->htile_match || window->htile_match != window->display->grab_window) && (!window->vtile_match || window->vtile_match != window->display->grab_window)) { MetaRectangle old_frame_rect, old_buffer_rect; meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); meta_compositor_size_change_window (window->display->compositor, window, META_SIZE_CHANGE_TILE, &old_frame_rect, &old_buffer_rect); } meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED), META_GRAVITY_NORTH_WEST, window->unconstrained_rect); if (window->frame) meta_frame_queue_draw (window->frame); if (changed) notify_tile_mode (window); } MetaTileMode meta_window_get_tile_mode (MetaWindow *window) { return window->tile_mode; } void meta_window_restore_tile (MetaWindow *window, MetaTileMode mode, int width, int height) { meta_window_update_tile_fractions (window, width, height); meta_window_tile (window, mode); } static gboolean meta_window_can_tile_maximized (MetaWindow *window) { return window->has_maximize_func; } static gboolean is_valid_tile_mode (MetaWindow *window, MetaTileMode mode) { double hfraction, vfraction; gboolean is_valid = FALSE; if (!meta_window_can_tile_maximized (window)) return FALSE; meta_window_get_tile_fractions (window, mode, &hfraction, &vfraction, &is_valid); return is_valid; } gboolean meta_window_can_tile_left_right (MetaWindow *window) { return is_valid_tile_mode (window, META_TILE_LEFT); } gboolean meta_window_can_tile_top_bottom (MetaWindow *window) { return is_valid_tile_mode (window, META_TILE_TOP); } gboolean meta_window_can_tile_corner (MetaWindow *window) { return is_valid_tile_mode (window, META_TILE_ULC); } static void unmaximize_window_before_freeing (MetaWindow *window) { meta_topic (META_DEBUG_WINDOW_OPS, "Unmaximizing %s just before freeing\n", window->desc); window->maximized_horizontally = FALSE; window->maximized_vertically = FALSE; if (window->withdrawn) /* See bug #137185 */ { window->rect = window->saved_rect; set_net_wm_state (window); } #ifdef HAVE_WAYLAND else if (!meta_is_wayland_compositor ()) { /* Do NOT update net_wm_state: this screen is closing, * it likely will be managed by another window manager * that will need the current _NET_WM_STATE atoms. * Moreover, it will need to know the unmaximized geometry, * therefore move_resize the window to saved_rect here * before closing it. */ meta_window_move_resize_frame (window, FALSE, window->saved_rect.x, window->saved_rect.y, window->saved_rect.width, window->saved_rect.height); } #endif } static void meta_window_maybe_apply_size_hints (MetaWindow *window, MetaRectangle *target_rect) { meta_window_frame_rect_to_client_rect (window, target_rect, target_rect); ensure_size_hints_satisfied (target_rect, &window->size_hints); meta_window_client_rect_to_frame_rect (window, target_rect, target_rect); } void meta_window_unmaximize (MetaWindow *window, MetaMaximizeFlags directions) { gboolean unmaximize_horizontally, unmaximize_vertically; g_return_if_fail (!window->override_redirect); /* At least one of the two directions ought to be set */ unmaximize_horizontally = directions & META_MAXIMIZE_HORIZONTAL; unmaximize_vertically = directions & META_MAXIMIZE_VERTICAL; g_assert (unmaximize_horizontally || unmaximize_vertically); if (unmaximize_vertically && window->display->grab_op == META_GRAB_OP_NONE) window->saved_tile_mode = META_TILE_NONE; /* Only do something if the window isn't already maximized in the * given direction(s). */ if ((unmaximize_horizontally && window->maximized_horizontally) || (unmaximize_vertically && window->maximized_vertically)) { MetaRectangle *desired_rect; MetaRectangle target_rect; MetaRectangle work_area; MetaRectangle old_frame_rect, old_buffer_rect; meta_window_get_work_area_for_monitor (window, window->monitor->number, &work_area); meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); if (unmaximize_vertically) { window->tile_mode = META_TILE_NONE; notify_tile_mode (window); } meta_topic (META_DEBUG_WINDOW_OPS, "Unmaximizing %s%s\n", window->desc, unmaximize_horizontally && unmaximize_vertically ? "" : unmaximize_horizontally ? " horizontally" : unmaximize_vertically ? " vertically" : "BUGGGGG"); window->maximized_horizontally = window->maximized_horizontally && !unmaximize_horizontally; window->maximized_vertically = window->maximized_vertically && !unmaximize_vertically; /* Update the edge constraints */ update_edge_constraints (window); /* recalc_features() will eventually clear the cached frame * extents, but we need the correct frame extents in the code below, * so invalidate the old frame extents manually up front. */ meta_window_frame_size_changed (window); desired_rect = &window->saved_rect; /* Unmaximize to the saved_rect position in the direction(s) * being unmaximized. */ target_rect = old_frame_rect; /* Avoid unmaximizing to "almost maximized" size when the previous size * is greater then 80% of the work area use MAX_UNMAXIMIZED_WINDOW_AREA of the work area as upper limit * while maintaining the aspect ratio. */ if (unmaximize_horizontally && unmaximize_vertically && desired_rect->width * desired_rect->height > work_area.width * work_area.height * MAX_UNMAXIMIZED_WINDOW_AREA) { if (desired_rect->width > desired_rect->height) { float aspect = (float)desired_rect->height / (float)desired_rect->width; desired_rect->width = MAX (work_area.width * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_width); desired_rect->height = MAX (desired_rect->width * aspect, window->size_hints.min_height); } else { float aspect = (float)desired_rect->width / (float)desired_rect->height; desired_rect->height = MAX (work_area.height * sqrt (MAX_UNMAXIMIZED_WINDOW_AREA), window->size_hints.min_height); desired_rect->width = MAX (desired_rect->height * aspect, window->size_hints.min_width); } } if (unmaximize_horizontally) { target_rect.x = desired_rect->x; target_rect.width = desired_rect->width; } if (unmaximize_vertically) { target_rect.y = desired_rect->y; target_rect.height = desired_rect->height; } /* Window's size hints may have changed while maximized, making * saved_rect invalid. #329152 */ meta_window_maybe_apply_size_hints (window, &target_rect); meta_compositor_size_change_window (window->display->compositor, window, META_SIZE_CHANGE_UNMAXIMIZE, &old_frame_rect, &old_buffer_rect); meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED | META_MOVE_RESIZE_UNMAXIMIZE), META_GRAVITY_NORTH_WEST, target_rect); /* When we unmaximize, if we're doing a mouse move also we could * get the window suddenly jumping to the upper left corner of * the workspace, since that's where it was when the grab op * started. So we need to update the grab anchor position. */ if (meta_grab_op_is_moving (window->display->grab_op) && window->display->grab_window == window) { window->display->grab_anchor_window_pos = target_rect; } meta_window_recalc_features (window); set_net_wm_state (window); if (!window->monitor->in_fullscreen) meta_display_queue_check_fullscreen (window->display); } g_object_freeze_notify (G_OBJECT (window)); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_HORIZONTALLY]); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MAXIMIZED_VERTICALLY]); g_object_thaw_notify (G_OBJECT (window)); } void meta_window_make_above (MetaWindow *window) { g_return_if_fail (!window->override_redirect); meta_window_set_above (window, TRUE); meta_window_raise (window); } void meta_window_unmake_above (MetaWindow *window) { g_return_if_fail (!window->override_redirect); meta_window_set_above (window, FALSE); meta_window_raise (window); } static void meta_window_set_above (MetaWindow *window, gboolean new_value) { new_value = new_value != FALSE; if (new_value == window->wm_state_above) return; window->wm_state_above = new_value; meta_window_update_layer (window); set_net_wm_state (window); meta_window_frame_size_changed (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ABOVE]); } void meta_window_make_fullscreen_internal (MetaWindow *window) { if (!window->fullscreen) { meta_topic (META_DEBUG_WINDOW_OPS, "Fullscreening %s\n", window->desc); if (window->shaded) { /* Shading sucks anyway; I'm not adding a timestamp argument * to this function just for this niche usage & corner case. */ guint32 timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_unshade (window, timestamp); } meta_window_save_rect (window); window->fullscreen = TRUE; meta_stack_freeze (window->display->stack); meta_window_raise (window); meta_stack_thaw (window->display->stack); meta_window_recalc_features (window); set_net_wm_state (window); /* For the auto-minimize feature, if we fail to get focus */ meta_display_queue_check_fullscreen (window->display); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_FULLSCREEN]); } } void meta_window_make_fullscreen (MetaWindow *window) { g_return_if_fail (!window->override_redirect); if (!window->fullscreen) { MetaRectangle old_frame_rect, old_buffer_rect; meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); meta_compositor_size_change_window (window->display->compositor, window, META_SIZE_CHANGE_FULLSCREEN, &old_frame_rect, &old_buffer_rect); meta_window_make_fullscreen_internal (window); meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED), META_GRAVITY_NORTH_WEST, window->unconstrained_rect); } } void meta_window_unmake_fullscreen (MetaWindow *window) { g_return_if_fail (!window->override_redirect); if (window->fullscreen) { MetaRectangle old_frame_rect, old_buffer_rect, target_rect; meta_topic (META_DEBUG_WINDOW_OPS, "Unfullscreening %s\n", window->desc); window->fullscreen = FALSE; target_rect = window->saved_rect; meta_window_frame_size_changed (window); meta_window_get_frame_rect (window, &old_frame_rect); meta_window_get_buffer_rect (window, &old_buffer_rect); /* Window's size hints may have changed while maximized, making * saved_rect invalid. #329152 */ meta_window_maybe_apply_size_hints (window, &target_rect); /* Need to update window->has_resize_func before we move_resize() */ meta_window_recalc_features (window); set_net_wm_state (window); meta_compositor_size_change_window (window->display->compositor, window, META_SIZE_CHANGE_UNFULLSCREEN, &old_frame_rect, &old_buffer_rect); meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_STATE_CHANGED | META_MOVE_RESIZE_UNFULLSCREEN), META_GRAVITY_NORTH_WEST, target_rect); meta_display_queue_check_fullscreen (window->display); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_FULLSCREEN]); } } static void meta_window_clear_fullscreen_monitors (MetaWindow *window) { window->fullscreen_monitors.top = NULL; window->fullscreen_monitors.bottom = NULL; window->fullscreen_monitors.left = NULL; window->fullscreen_monitors.right = NULL; } void meta_window_update_fullscreen_monitors (MetaWindow *window, MetaLogicalMonitor *top, MetaLogicalMonitor *bottom, MetaLogicalMonitor *left, MetaLogicalMonitor *right) { if (top && bottom && left && right) { window->fullscreen_monitors.top = top; window->fullscreen_monitors.bottom = bottom; window->fullscreen_monitors.left = left; window->fullscreen_monitors.right = right; } else { meta_window_clear_fullscreen_monitors (window); } if (window->fullscreen) { meta_window_queue(window, META_QUEUE_MOVE_RESIZE); } } gboolean meta_window_has_fullscreen_monitors (MetaWindow *window) { return window->fullscreen_monitors.top != NULL; } void meta_window_adjust_fullscreen_monitor_rect (MetaWindow *window, MetaRectangle *monitor_rect) { MetaWindowClass *window_class = META_WINDOW_GET_CLASS (window); if (window_class->adjust_fullscreen_monitor_rect) window_class->adjust_fullscreen_monitor_rect (window, monitor_rect); } void meta_window_shade (MetaWindow *window, guint32 timestamp) { g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_WINDOW_OPS, "Shading %s\n", window->desc); if (!window->shaded) { window->shaded = TRUE; meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); meta_window_frame_size_changed (window); /* After queuing the calc showing, since _focus flushes it, * and we need to focus the frame */ meta_topic (META_DEBUG_FOCUS, "Re-focusing window %s after shading it\n", window->desc); meta_window_focus (window, timestamp); set_net_wm_state (window); } } void meta_window_unshade (MetaWindow *window, guint32 timestamp) { g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_WINDOW_OPS, "Unshading %s\n", window->desc); if (window->shaded) { window->shaded = FALSE; meta_window_queue(window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); meta_window_frame_size_changed (window); /* focus the window */ meta_topic (META_DEBUG_FOCUS, "Focusing window %s after unshading it\n", window->desc); meta_window_focus (window, timestamp); set_net_wm_state (window); } } static gboolean unminimize_func (MetaWindow *window, void *data) { meta_window_unminimize (window); return TRUE; } static void unminimize_window_and_all_transient_parents (MetaWindow *window) { meta_window_unminimize (window); meta_window_foreach_ancestor (window, unminimize_func, NULL); } void meta_window_activate_full (MetaWindow *window, guint32 timestamp, MetaClientType source_indication, MetaWorkspace *workspace) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean allow_workspace_switch; if (window->unmanaging) { g_warning ("Trying to activate unmanaged window '%s'", window->desc); return; } meta_topic (META_DEBUG_FOCUS, "_NET_ACTIVE_WINDOW message sent for %s at time %u " "by client type %u.\n", window->desc, timestamp, source_indication); allow_workspace_switch = (timestamp != 0); if (timestamp != 0 && XSERVER_TIME_IS_BEFORE (timestamp, window->display->last_user_time)) { // If the window is modal its parent is currently the focused window, always give // focus, even if its timestamp is wrong. Check also for an xdg-portal dialog, which // probably won't have the correct timestamp either. // // Otherwise, these windows will either start below their parent window, or they may // start above the parent, but with the parent retaining focus. if (window->type == META_WINDOW_MODAL_DIALOG && ((window->transient_for && window->display->focus_window == window->transient_for) || g_strcmp0 (window->res_class, "Xdg-desktop-portal-gtk") == 0)) { meta_topic (META_DEBUG_FOCUS, "Window is a modal dialog and and its parent is currently focused, or it's an xdg-desktop-portal-gtk dialog), " "adjusting its timestamp so it will be focused and brought forward.\n"); timestamp = meta_display_get_current_time_roundtrip (window->display); } else { meta_topic (META_DEBUG_FOCUS, "last_user_time (%u) is more recent; ignoring " " _NET_ACTIVE_WINDOW message.\n", window->display->last_user_time); meta_window_set_demands_attention(window); return; } } if (timestamp == 0) timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_set_user_time (window, timestamp); /* disable show desktop mode unless we're a desktop component */ maybe_leave_show_desktop_mode (window); /* Get window on current or given workspace */ if (workspace == NULL) workspace = workspace_manager->active_workspace; // allow_workspace_switch == "user action" if (window->transient_for == NULL) { if (!allow_workspace_switch && !meta_window_located_on_workspace (window, workspace)) { meta_window_set_demands_attention (window); return; } else { if (!meta_window_located_on_workspace (window, workspace)) { if (meta_prefs_get_bring_windows_to_current_workspace ()) { meta_window_change_workspace (window, workspace); } else { meta_workspace_activate (window->workspace, timestamp); } } } } else if (window->transient_for != NULL) { /* Move transients to current workspace - preference dialogs should appear over the source window. */ meta_window_change_workspace (window, workspace); } if (window->shaded) meta_window_unshade (window, timestamp); unminimize_window_and_all_transient_parents (window); if (meta_prefs_get_raise_on_click () || source_indication == META_CLIENT_TYPE_PAGER) meta_window_raise (window); meta_topic (META_DEBUG_FOCUS, "Focusing window %s due to activation\n", window->desc); if (meta_window_located_on_workspace (window, workspace)) meta_window_focus (window, timestamp); else meta_workspace_activate_with_focus (window->workspace, window, timestamp); meta_window_check_alive (window, timestamp); } /* This function exists since most of the functionality in window_activate * is useful for Mutter, but Mutter shouldn't need to specify a client * type for itself. ;-) */ void meta_window_activate (MetaWindow *window, guint32 timestamp) { g_return_if_fail (!window->override_redirect); /* We're not really a pager, but the behavior we want is the same as if * we were such. If we change the pager behavior later, we could revisit * this and just add extra flags to window_activate. */ meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_PAGER, NULL); } void meta_window_activate_with_workspace (MetaWindow *window, guint32 timestamp, MetaWorkspace *workspace) { g_return_if_fail (!window->override_redirect); meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_APPLICATION, workspace); } /** * meta_window_updates_are_frozen: * @window: a #MetaWindow * * Gets whether the compositor should be updating the window contents; * window content updates may be frozen at client request by setting * an odd value in the extended _NET_WM_SYNC_REQUEST_COUNTER counter * by the window manager during a resize operation while waiting for * the client to redraw. * * Return value: %TRUE if updates are currently frozen */ gboolean meta_window_updates_are_frozen (MetaWindow *window) { return META_WINDOW_GET_CLASS (window)->are_updates_frozen (window); } static void meta_window_reposition (MetaWindow *window) { meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION), META_GRAVITY_NORTH_WEST, window->rect); } static gboolean maybe_move_attached_window (MetaWindow *window, void *data) { if (window->hidden) return G_SOURCE_CONTINUE; if (meta_window_is_attached_dialog (window) || meta_window_get_placement_rule (window)) meta_window_reposition (window); return G_SOURCE_CONTINUE; } /** * meta_window_get_monitor: * @window: a #MetaWindow * * Gets index of the monitor that this window is on. * * Return Value: The index of the monitor in the screens monitor list, or -1 * if the window has been recently unmanaged and does not have a monitor. */ int meta_window_get_monitor (MetaWindow *window) { if (!window->monitor) return -1; return window->monitor->number; } MetaLogicalMonitor * meta_window_get_main_logical_monitor (MetaWindow *window) { return window->monitor; } static MetaLogicalMonitor * find_monitor_by_winsys_id (MetaWindow *window, uint64_t winsys_id) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (logical_monitor->winsys_id == winsys_id) return logical_monitor; } return NULL; } /* This is called when the monitor setup has changed. The window->monitor * reference is still "valid", but refer to the previous monitor setup */ void meta_window_update_for_monitors_changed (MetaWindow *window) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); const MetaLogicalMonitor *old, *new; if (meta_window_has_fullscreen_monitors (window)) meta_window_clear_fullscreen_monitors (window); if (window->override_redirect || window->type == META_WINDOW_DESKTOP) { meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE); goto out; } old = window->monitor; /* Try the preferred output first */ new = find_monitor_by_winsys_id (window, window->preferred_output_winsys_id); /* Otherwise, try to find the old output on a new monitor */ if (old && !new) new = find_monitor_by_winsys_id (window, old->winsys_id); /* Fall back to primary if everything else failed */ if (!new) new = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); if (window->tile_mode != META_TILE_NONE) { if (new) window->tile_monitor_number = new->number; else window->tile_monitor_number = -1; } if (new && old) { /* This will eventually reach meta_window_update_monitor that * will send leave/enter-monitor events. The old != new monitor * check will always fail (due to the new logical_monitors set) so * we will always send the events, even if the new and old monitor * index is the same. That is right, since the enumeration of the * monitors changed and the same index could be refereing * to a different monitor. */ meta_window_move_between_rects (window, META_MOVE_RESIZE_FORCE_UPDATE_MONITOR, &old->rect, &new->rect); } else { meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE); } out: g_assert (!window->monitor || g_list_find (meta_monitor_manager_get_logical_monitors (monitor_manager), window->monitor)); } void meta_window_update_monitor (MetaWindow *window, MetaWindowUpdateMonitorFlags flags) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; const MetaLogicalMonitor *old; old = window->monitor; META_WINDOW_GET_CLASS (window)->update_main_monitor (window, flags); if (old != window->monitor) { meta_window_on_all_workspaces_changed (window); /* If workspaces only on primary and we moved back to primary due to a user action, * ensure that the window is now in that workspace. We do this because while * the window is on a non-primary monitor it is always visible, so it would be * very jarring if it disappeared when it crossed the monitor border. * The one time we want it to both change to the primary monitor and a non-active * workspace is when dropping the window on some other workspace thumbnail directly. * That should be handled by explicitly moving the window before changing the * workspace. */ if (meta_prefs_get_workspaces_only_on_primary () && flags & META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP && meta_window_is_on_primary_monitor (window) && workspace_manager->active_workspace != window->workspace) meta_window_change_workspace (window, workspace_manager->active_workspace); meta_window_main_monitor_changed (window, old); /* If we're changing monitors, we need to update the has_maximize_func flag, * as the working area has changed. */ meta_window_recalc_features (window); } } void meta_window_move_resize_internal (MetaWindow *window, MetaMoveResizeFlags flags, MetaGravity gravity, MetaRectangle frame_rect) { /* The rectangle here that's passed in *always* in "frame rect" * coordinates. That means the position of the frame's visible bounds, * with x and y being absolute (root window) coordinates. * * For an X11 framed window, the client window's server rectangle is * inset from this rectangle by the frame's visible borders, and the * frame window's server rectangle is outset by the invisible borders. * * For an X11 unframed window, the rectangle here directly matches * the server's rectangle, since the visible and invisible borders * are both 0. * * For an X11 CSD window, the client window's server rectangle is * outset from this rectagle by the client-specified frame extents. * * For a Wayland window, this rectangle can simply be sent directly * to the client. */ MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean did_placement; MetaRectangle unconstrained_rect; MetaRectangle constrained_rect; MetaRectangle temporary_rect; int rel_x = 0; int rel_y = 0; MetaMoveResizeResultFlags result = 0; gboolean moved_or_resized = FALSE; MetaWindowUpdateMonitorFlags update_monitor_flags; if (window->type != META_WINDOW_TOOLTIP) { g_return_if_fail (!window->override_redirect); } /* The action has to be a move, a resize or the wayland client * acking our choice of size. */ g_assert (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)); did_placement = !window->placed && window->calc_placement; /* We don't need it in the idle queue anymore. */ meta_window_unqueue (window, META_QUEUE_MOVE_RESIZE); if ((flags & META_MOVE_RESIZE_RESIZE_ACTION) && (flags & META_MOVE_RESIZE_MOVE_ACTION)) { /* We're both moving and resizing. Just use the passed in rect. */ unconstrained_rect = frame_rect; } else if ((flags & META_MOVE_RESIZE_RESIZE_ACTION)) { /* If this is only a resize, then ignore the position given in * the parameters and instead calculate the new position from * resizing the old rectangle with the given gravity. */ meta_rectangle_resize_with_gravity (&window->rect, &unconstrained_rect, gravity, frame_rect.width, frame_rect.height); } else if ((flags & META_MOVE_RESIZE_MOVE_ACTION)) { /* If this is only a move, then ignore the passed in size and * just use the existing size of the window. */ unconstrained_rect.x = frame_rect.x; unconstrained_rect.y = frame_rect.y; unconstrained_rect.width = window->rect.width; unconstrained_rect.height = window->rect.height; } else if ((flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE)) { /* This is a Wayland buffer acking our size. The new rect is * just the existing one we have. Ignore the passed-in rect * completely. */ unconstrained_rect = window->rect; } else g_assert_not_reached (); constrained_rect = unconstrained_rect; temporary_rect = window->rect; if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION) && !(flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) && window->monitor) { MetaRectangle old_rect; meta_window_get_frame_rect (window, &old_rect); meta_window_constrain (window, flags, gravity, &old_rect, &constrained_rect, &temporary_rect, &rel_x, &rel_y); } else if (window->placement.rule) { rel_x = window->placement.pending.rel_x; rel_y = window->placement.pending.rel_y; } /* If we did placement, then we need to save the position that the window * was placed at to make sure that meta_window_move_resize_now places the * window correctly. */ if (did_placement) { unconstrained_rect.x = constrained_rect.x; unconstrained_rect.y = constrained_rect.y; } /* Do the protocol-specific move/resize logic */ META_WINDOW_GET_CLASS (window)->move_resize_internal (window, gravity, unconstrained_rect, constrained_rect, temporary_rect, rel_x, rel_y, flags, &result); if (result & META_MOVE_RESIZE_RESULT_MOVED) { moved_or_resized = TRUE; g_signal_emit (window, window_signals[POSITION_CHANGED], 0); } if (result & META_MOVE_RESIZE_RESULT_RESIZED) { moved_or_resized = TRUE; g_signal_emit (window, window_signals[SIZE_CHANGED], 0); } if (moved_or_resized || did_placement) window->unconstrained_rect = unconstrained_rect; if ((moved_or_resized || did_placement || (result & META_MOVE_RESIZE_RESULT_STATE_CHANGED) != 0) && window->known_to_compositor) { meta_compositor_sync_window_geometry (window->display->compositor, window, did_placement); } update_monitor_flags = META_WINDOW_UPDATE_MONITOR_FLAGS_NONE; if (flags & META_MOVE_RESIZE_USER_ACTION) update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP; if (flags & META_MOVE_RESIZE_FORCE_UPDATE_MONITOR) update_monitor_flags |= META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE; if (window->monitor) { uint64_t old_output_winsys_id; old_output_winsys_id = window->monitor->winsys_id; meta_window_update_monitor (window, update_monitor_flags); if (old_output_winsys_id != window->monitor->winsys_id && flags & META_MOVE_RESIZE_MOVE_ACTION && flags & META_MOVE_RESIZE_USER_ACTION) window->preferred_output_winsys_id = window->monitor->winsys_id; } else { meta_window_update_monitor (window, update_monitor_flags); } if ((result & META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED) && window->frame_bounds) { cairo_region_destroy (window->frame_bounds); window->frame_bounds = NULL; } meta_window_foreach_transient (window, maybe_move_attached_window, NULL); meta_stack_update_window_tile_matches (window->display->stack, workspace_manager->active_workspace); } /** * meta_window_move_frame: * @window: a #MetaWindow * @user_op: bool to indicate whether or not this is a user operation * @root_x_nw: desired x pos * @root_y_nw: desired y pos * * Moves the window to the desired location on window's assigned * workspace, using the northwest edge of the frame as the reference, * instead of the actual window's origin, but only if a frame is present. * Otherwise, acts identically to meta_window_move(). */ void meta_window_move_frame (MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw) { MetaMoveResizeFlags flags; MetaRectangle rect = { root_x_nw, root_y_nw, 0, 0 }; if (window->type != META_WINDOW_TOOLTIP) { g_return_if_fail (!window->override_redirect); } flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_MOVE_ACTION; meta_window_move_resize_internal (window, flags, META_GRAVITY_NORTH_WEST, rect); } static void meta_window_move_between_rects (MetaWindow *window, MetaMoveResizeFlags move_resize_flags, const MetaRectangle *old_area, const MetaRectangle *new_area) { int rel_x, rel_y; double scale_x, scale_y; if (old_area) { rel_x = window->unconstrained_rect.x - old_area->x; rel_y = window->unconstrained_rect.y - old_area->y; scale_x = (double)new_area->width / old_area->width; scale_y = (double)new_area->height / old_area->height; } else { rel_x = rel_y = scale_x = scale_y = 0; } window->unconstrained_rect.x = new_area->x + rel_x * scale_x; window->unconstrained_rect.y = new_area->y + rel_y * scale_y; window->saved_rect.x = window->unconstrained_rect.x; window->saved_rect.y = window->unconstrained_rect.y; meta_window_move_resize_internal (window, move_resize_flags | META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION, META_GRAVITY_NORTH_WEST, window->unconstrained_rect); } /** * meta_window_move_resize_frame: * @window: a #MetaWindow * @user_op: bool to indicate whether or not this is a user operation * @root_x_nw: new x * @root_y_nw: new y * @w: desired width * @h: desired height * * Resizes the window so that its outer bounds (including frame) * fit within the given rect */ void meta_window_move_resize_frame (MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw, int w, int h) { MetaMoveResizeFlags flags; MetaRectangle rect = { root_x_nw, root_y_nw, w, h }; g_return_if_fail (!window->override_redirect); flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; meta_window_move_resize_internal (window, flags, META_GRAVITY_NORTH_WEST, rect); } /** * meta_window_move_to_monitor: * @window: a #MetaWindow * @monitor: desired monitor index * * Moves the window to the monitor with index @monitor, keeping * the relative position of the window's top left corner. */ void meta_window_move_to_monitor (MetaWindow *window, int monitor) { MetaRectangle old_area, new_area; if (window->tile_mode != META_TILE_NONE) window->tile_monitor_number = monitor; meta_window_get_work_area_for_monitor (window, window->monitor->number, &old_area); meta_window_get_work_area_for_monitor (window, monitor, &new_area); if (window->unconstrained_rect.width == 0 || window->unconstrained_rect.height == 0 || !meta_rectangle_overlap (&window->unconstrained_rect, &old_area)) { meta_window_move_between_rects (window, 0, NULL, &new_area); } else { if (monitor == window->monitor->number) return; meta_window_move_between_rects (window, 0, &old_area, &new_area); } window->preferred_output_winsys_id = window->monitor->winsys_id; if (window->fullscreen || window->override_redirect) meta_display_queue_check_fullscreen (window->display); } static void adjust_size_for_tile_match (MetaWindow *window, int *new_w, int *new_h) { MetaRectangle work_area, rect, match_rect; MetaWindow *vtile_match = window->vtile_match; MetaWindow *htile_match = window->htile_match; if (!META_WINDOW_TILED (window) || (!vtile_match && !htile_match)) return; meta_window_get_work_area_for_monitor (window, window->tile_monitor_number, &work_area); /* Make sure the resize does not break minimum sizes */ rect = work_area; rect.width = *new_w; rect.height = *new_h; meta_window_frame_rect_to_client_rect (window, &rect, &rect); *new_w += MAX(0, window->size_hints.min_width - rect.width); *new_h += MAX(0, window->size_hints.min_height - rect.height); /* Make sure we're not resizing the tile match below its min width */ rect = work_area; rect.width = work_area.width - *new_w; rect.height = work_area.height - *new_h; if (vtile_match) { meta_window_frame_rect_to_client_rect (vtile_match, &rect, &match_rect); *new_h -= MAX(0, vtile_match->size_hints.min_height - match_rect.height); } if (htile_match) { meta_window_frame_rect_to_client_rect (htile_match, &rect, &match_rect); *new_w -= MAX(0, htile_match->size_hints.min_width - match_rect.width); } } void meta_window_resize_frame_with_gravity (MetaWindow *window, gboolean user_op, int w, int h, MetaGravity gravity) { MetaMoveResizeFlags flags; MetaRectangle rect; rect.width = w; rect.height = h; if (user_op) { /* When resizing in-tandem with a tile match, we need to respect * its minimum width */ if (window->display->grab_window == window) adjust_size_for_tile_match (window, &w, &h); meta_window_update_tile_fractions (window, w, h); } flags = (user_op ? META_MOVE_RESIZE_USER_ACTION : 0) | META_MOVE_RESIZE_RESIZE_ACTION; meta_window_move_resize_internal (window, flags, gravity, rect); } static void meta_window_move_resize_now (MetaWindow *window) { meta_window_move_resize_frame (window, FALSE, window->unconstrained_rect.x, window->unconstrained_rect.y, window->unconstrained_rect.width, window->unconstrained_rect.height); } static gboolean idle_move_resize (gpointer data) { GSList *tmp; GSList *copy; guint queue_index = GPOINTER_TO_INT (data); meta_topic (META_DEBUG_GEOMETRY, "Clearing the move_resize queue\n"); /* Work with a copy, for reentrancy. The allowed reentrancy isn't * complete; destroying a window while we're in here would result in * badness. But it's OK to queue/unqueue move_resizes. */ copy = g_slist_copy (queue_pending[queue_index]); g_slist_free (queue_pending[queue_index]); queue_pending[queue_index] = NULL; queue_later[queue_index] = 0; destroying_windows_disallowed += 1; tmp = copy; while (tmp != NULL) { MetaWindow *window; window = tmp->data; /* As a side effect, sets window->move_resize_queued = FALSE */ meta_window_move_resize_now (window); tmp = tmp->next; } g_slist_free (copy); destroying_windows_disallowed -= 1; return FALSE; } void meta_window_get_gravity_position (MetaWindow *window, MetaGravity gravity, int *root_x, int *root_y) { MetaRectangle frame_extents; int w, h; int x, y; w = window->rect.width; h = window->rect.height; if (gravity == META_GRAVITY_STATIC) { frame_extents = window->rect; if (window->frame) { frame_extents.x = window->frame->rect.x + window->frame->child_x; frame_extents.y = window->frame->rect.y + window->frame->child_y; } } else { if (window->frame == NULL) frame_extents = window->rect; else frame_extents = window->frame->rect; } x = frame_extents.x; y = frame_extents.y; switch (gravity) { case META_GRAVITY_NORTH: case META_GRAVITY_CENTER: case META_GRAVITY_SOUTH: /* Find center of frame. */ x += frame_extents.width / 2; /* Center client window on that point. */ x -= w / 2; break; case META_GRAVITY_SOUTH_EAST: case META_GRAVITY_EAST: case META_GRAVITY_NORTH_EAST: /* Find right edge of frame */ x += frame_extents.width; /* Align left edge of client at that point. */ x -= w; break; default: break; } switch (gravity) { case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_EAST: /* Find center of frame. */ y += frame_extents.height / 2; /* Center client window there. */ y -= h / 2; break; case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_EAST: /* Find south edge of frame */ y += frame_extents.height; /* Place bottom edge of client there */ y -= h; break; default: break; } if (root_x) *root_x = x; if (root_y) *root_y = y; } void meta_window_get_session_geometry (MetaWindow *window, int *x, int *y, int *width, int *height) { meta_window_get_gravity_position (window, window->size_hints.win_gravity, x, y); *width = (window->rect.width - window->size_hints.base_width) / window->size_hints.width_inc; *height = (window->rect.height - window->size_hints.base_height) / window->size_hints.height_inc; } /** * meta_window_get_buffer_rect: * @window: a #MetaWindow * @rect: (out): pointer to an allocated #MetaRectangle * * Gets the rectangle that the pixmap or buffer of @window occupies. * * For X11 windows, this is the server-side geometry of the toplevel * window. * * For Wayland windows, this is the bounding rectangle of the attached * buffer. */ void meta_window_get_buffer_rect (const MetaWindow *window, MetaRectangle *rect) { *rect = window->buffer_rect; } /** * meta_window_client_rect_to_frame_rect: * @window: a #MetaWindow * @client_rect: client rectangle in root coordinates * @frame_rect: (out): location to store the computed corresponding frame bounds. * * Converts a desired bounds of the client window into the corresponding bounds * of the window frame (excluding invisible borders and client side shadows.) */ void meta_window_client_rect_to_frame_rect (MetaWindow *window, MetaRectangle *client_rect, MetaRectangle *frame_rect) { if (!frame_rect) return; *frame_rect = *client_rect; /* The support for G_MAXINT here to mean infinity is a convenience for * constraints.c:get_size_limits() and not something that we provide * in other locations or document. */ if (window->frame) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); frame_rect->x -= borders.visible.left; frame_rect->y -= borders.visible.top; if (frame_rect->width != G_MAXINT) frame_rect->width += borders.visible.left + borders.visible.right; if (frame_rect->height != G_MAXINT) frame_rect->height += borders.visible.top + borders.visible.bottom; } else { const GtkBorder *extents = &window->custom_frame_extents; frame_rect->x += extents->left; frame_rect->y += extents->top; if (frame_rect->width != G_MAXINT) frame_rect->width -= extents->left + extents->right; if (frame_rect->height != G_MAXINT) frame_rect->height -= extents->top + extents->bottom; } } /** * meta_window_frame_rect_to_client_rect: * @window: a #MetaWindow * @frame_rect: desired frame bounds for the window * @client_rect: (out): location to store the computed corresponding client rectangle. * * Converts a desired frame bounds for a window into the bounds of the client * window. */ void meta_window_frame_rect_to_client_rect (MetaWindow *window, MetaRectangle *frame_rect, MetaRectangle *client_rect) { if (!client_rect) return; *client_rect = *frame_rect; if (window->frame) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); client_rect->x += borders.visible.left; client_rect->y += borders.visible.top; client_rect->width -= borders.visible.left + borders.visible.right; client_rect->height -= borders.visible.top + borders.visible.bottom; } else { const GtkBorder *extents = &window->custom_frame_extents; client_rect->x -= extents->left; client_rect->y -= extents->top; client_rect->width += extents->left + extents->right; client_rect->height += extents->top + extents->bottom; } } /** * meta_window_get_frame_rect: * @window: a #MetaWindow * @rect: (out): pointer to an allocated #MetaRectangle * * Gets the rectangle that bounds @window that is what the user thinks of * as the edge of the window. This doesn't include any extra reactive * area that we or the client adds to the window, or any area that the * client adds to draw a client-side shadow. */ void meta_window_get_frame_rect (const MetaWindow *window, MetaRectangle *rect) { *rect = window->rect; } /** * meta_window_get_client_area_rect: * @window: a #MetaWindow * @rect: (out): pointer to a cairo rectangle * * Gets the rectangle for the boundaries of the client area, relative * to the buffer rect. If the window is shaded, the height of the * rectangle is 0. */ void meta_window_get_client_area_rect (const MetaWindow *window, cairo_rectangle_int_t *rect) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); rect->x = borders.total.left; rect->y = borders.total.top; rect->width = window->buffer_rect.width - borders.total.left - borders.total.right; if (window->shaded) rect->height = 0; else rect->height = window->buffer_rect.height - borders.total.top - borders.total.bottom; } void meta_window_get_titlebar_rect (MetaWindow *window, MetaRectangle *rect) { meta_window_get_frame_rect (window, rect); /* The returned rectangle is relative to the frame rect. */ rect->x = 0; rect->y = 0; if (window->frame) { rect->height = window->frame->child_y; } else { /* Pick an arbitrary height for a titlebar. We might want to * eventually have CSD windows expose their borders to us. */ rect->height = 50; } } const char* meta_window_get_startup_id (MetaWindow *window) { if (window->startup_id == NULL) { MetaGroup *group; group = meta_window_get_group (window); if (group != NULL) return meta_group_get_startup_id (group); } return window->startup_id; } static MetaWindow* get_modal_transient (MetaWindow *window) { GSList *windows; GSList *tmp; MetaWindow *modal_transient; /* A window can't be the transient of itself, but this is just for * convenience in the loop below; we manually fix things up at the * end if no real modal transient was found. */ modal_transient = window; windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *transient = tmp->data; if (transient->transient_for == modal_transient && transient->type == META_WINDOW_MODAL_DIALOG) { modal_transient = transient; tmp = windows; continue; } tmp = tmp->next; } g_slist_free (windows); if (window == modal_transient) modal_transient = NULL; return modal_transient; } static gboolean meta_window_transient_can_focus (MetaWindow *window) { #ifdef HAVE_WAYLAND if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) return meta_wayland_surface_get_buffer (window->surface) != NULL; #endif return TRUE; } /* XXX META_EFFECT_FOCUS */ void meta_window_focus (MetaWindow *window, guint32 timestamp) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaWindow *modal_transient; g_return_if_fail (!window->override_redirect); /* This is a oneshot flag */ window->restore_focus_on_map = FALSE; meta_topic (META_DEBUG_FOCUS, "Setting input focus to window %s, input: %d focusable: %d\n", window->desc, window->input, meta_window_is_focusable (window)); if (window->display->grab_window && window->display->grab_window != window && window->display->grab_window->all_keys_grabbed && !window->display->grab_window->unmanaging) { meta_topic (META_DEBUG_FOCUS, "Current focus window %s has global keygrab, not focusing window %s after all\n", window->display->grab_window->desc, window->desc); return; } modal_transient = get_modal_transient (window); if (modal_transient != NULL && !modal_transient->unmanaging && meta_window_transient_can_focus (modal_transient)) { meta_topic (META_DEBUG_FOCUS, "%s has %s as a modal transient, so focusing it instead.\n", window->desc, modal_transient->desc); if (!meta_window_located_on_workspace (modal_transient, workspace_manager->active_workspace)) meta_window_change_workspace (modal_transient, workspace_manager->active_workspace); window = modal_transient; } meta_window_flush_calc_showing (window); if ((!window->mapped || window->hidden) && !window->shaded) { meta_topic (META_DEBUG_FOCUS, "Window %s is not showing, not focusing after all\n", window->desc); return; } META_WINDOW_GET_CLASS (window)->focus (window, timestamp); if (window->display->event_route == META_EVENT_ROUTE_NORMAL) { MetaBackend *backend = meta_get_backend (); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); clutter_stage_set_key_focus (stage, NULL); } if (window->close_dialog && meta_close_dialog_is_visible (window->close_dialog)) meta_close_dialog_focus (window->close_dialog); if (window->wm_state_demands_attention) meta_window_unset_demands_attention(window); /* meta_effect_run_focus(window, NULL, NULL); */ } /* Workspace management. Invariants: * * - window->workspace describes the workspace the window is on. * * - workspace->windows is a list of windows that is located on * that workspace. * * - If the window is on_all_workspaces, then * window->workspace == NULL, but workspace->windows contains * the window. */ static void set_workspace_state (MetaWindow *window, gboolean on_all_workspaces, MetaWorkspace *workspace) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; /* If we're on all workspaces, then our new workspace must be NULL, * otherwise it must be set, unless we're unmanaging. */ if (on_all_workspaces) g_assert_null (workspace); else g_assert_true (window->unmanaging || workspace != NULL); /* If this is an override-redirect window, ensure that the only * times we're setting the workspace state is either during construction * to mark as on_all_workspaces, or when unmanaging to remove all the * workspaces. */ if (window->override_redirect) g_return_if_fail ((window->constructing && on_all_workspaces) || window->unmanaging); if (on_all_workspaces == window->on_all_workspaces && workspace == window->workspace && !window->constructing) return; if (window->workspace) meta_workspace_remove_window (window->workspace, window); else if (window->on_all_workspaces) { GList *l; for (l = workspace_manager->workspaces; l != NULL; l = l->next) { MetaWorkspace *ws = l->data; meta_workspace_remove_window (ws, window); } } window->on_all_workspaces = on_all_workspaces; window->workspace = workspace; if (window->workspace) meta_workspace_add_window (window->workspace, window); else if (window->on_all_workspaces) { GList *l; for (l = workspace_manager->workspaces; l != NULL; l = l->next) { MetaWorkspace *ws = l->data; meta_workspace_add_window (ws, window); } } /* queue a move_resize since changing workspaces may change * the relevant struts */ if (!window->override_redirect) meta_window_queue (window, META_QUEUE_MOVE_RESIZE); meta_window_queue (window, META_QUEUE_CALC_SHOWING); meta_window_current_workspace_changed (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ON_ALL_WORKSPACES]); g_signal_emit (window, window_signals[WORKSPACE_CHANGED], 0); g_signal_emit_by_name (window->display, "window-workspace-changed", window, window->workspace); } static gboolean should_be_on_all_workspaces (MetaWindow *window) { if (window->always_sticky) return TRUE; if (window->on_all_workspaces_requested) return TRUE; if (window->override_redirect) return TRUE; if (meta_prefs_get_workspaces_only_on_primary () && !window->unmanaging && window->monitor && !meta_window_is_on_primary_monitor (window)) return TRUE; return FALSE; } void meta_window_on_all_workspaces_changed (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; gboolean on_all_workspaces = should_be_on_all_workspaces (window); if (window->on_all_workspaces == on_all_workspaces) return; MetaWorkspace *workspace; if (on_all_workspaces) { workspace = NULL; } else { /* We're coming out of the sticky state. Put the window on * the currently active workspace. */ workspace = workspace_manager->active_workspace; } set_workspace_state (window, on_all_workspaces, workspace); } static void meta_window_change_workspace_without_transients (MetaWindow *window, MetaWorkspace *workspace) { /* Try to unstick the window if it's stuck. This doesn't * have any guarantee that we'll actually unstick the * window, since it could be stuck for other reasons. */ if (window->on_all_workspaces_requested) meta_window_unstick (window); /* We failed to unstick the window. */ if (window->on_all_workspaces) return; if (window->workspace == workspace) return; set_workspace_state (window, FALSE, workspace); } static gboolean change_workspace_foreach (MetaWindow *window, void *data) { meta_window_change_workspace_without_transients (window, data); return TRUE; } void meta_window_change_workspace (MetaWindow *window, MetaWorkspace *workspace) { g_return_if_fail (!window->override_redirect); meta_window_change_workspace_without_transients (window, workspace); meta_window_foreach_transient (window, change_workspace_foreach, workspace); meta_window_foreach_ancestor (window, change_workspace_foreach, workspace); } static void window_stick_impl (MetaWindow *window) { meta_verbose ("Sticking window %s current on_all_workspaces = %d\n", window->desc, window->on_all_workspaces); if (window->on_all_workspaces_requested) return; /* We don't change window->workspaces, because we revert * to that original workspace list if on_all_workspaces is * toggled back off. */ window->on_all_workspaces_requested = TRUE; meta_window_on_all_workspaces_changed (window); } static void window_unstick_impl (MetaWindow *window) { if (!window->on_all_workspaces_requested) return; /* Revert to window->workspaces */ window->on_all_workspaces_requested = FALSE; meta_window_on_all_workspaces_changed (window); } static gboolean stick_foreach_func (MetaWindow *window, void *data) { gboolean stick; stick = *(gboolean*)data; if (stick) window_stick_impl (window); else window_unstick_impl (window); return TRUE; } void meta_window_stick (MetaWindow *window) { gboolean stick = TRUE; g_return_if_fail (!window->override_redirect); window_stick_impl (window); meta_window_foreach_transient (window, stick_foreach_func, &stick); } void meta_window_unstick (MetaWindow *window) { gboolean stick = FALSE; g_return_if_fail (!window->override_redirect); window_unstick_impl (window); meta_window_foreach_transient (window, stick_foreach_func, &stick); } void meta_window_current_workspace_changed (MetaWindow *window) { META_WINDOW_GET_CLASS (window)->current_workspace_changed (window); } static gboolean find_root_ancestor (MetaWindow *window, void *data) { MetaWindow **ancestor = data; /* Overwrite the previously "most-root" ancestor with the new one found */ *ancestor = window; /* We want this to continue until meta_window_foreach_ancestor quits because * there are no more valid ancestors. */ return TRUE; } /** * meta_window_find_root_ancestor: * @window: a #MetaWindow * * Follow the chain of parents of @window, skipping transient windows, * and return the "root" window which has no non-transient parent. * * Returns: (transfer none): The root ancestor window */ MetaWindow * meta_window_find_root_ancestor (MetaWindow *window) { MetaWindow *ancestor; ancestor = window; meta_window_foreach_ancestor (window, find_root_ancestor, &ancestor); return ancestor; } void meta_window_raise (MetaWindow *window) { MetaWindow *ancestor; g_return_if_fail (!window->override_redirect); ancestor = meta_window_find_root_ancestor (window); meta_topic (META_DEBUG_WINDOW_OPS, "Raising window %s, ancestor of %s\n", ancestor->desc, window->desc); /* Raise the ancestor of the window (if the window has no ancestor, * then ancestor will be set to the window itself); do this because * it's weird to see windows from other apps stacked between a child * and parent window of the currently active app. The stacking * constraints in stack.c then magically take care of raising all * the child windows appropriately. */ if (window->display->stack == ancestor->display->stack) { meta_stack_raise (window->display->stack, ancestor); } else { meta_warning ( "Either stacks aren't per screen or some window has a weird " "transient_for hint; window->display->stack != " "ancestor->screen->stack. window = %s, ancestor = %s.\n", window->desc, ancestor->desc); /* We could raise the window here, but don't want to do that twice and * so we let the case below handle that. */ } /* Okay, so stacking constraints misses one case: If a window has * two children and we want to raise one of those children, then * raising the ancestor isn't enough; we need to also raise the * correct child. See bug 307875. */ if (window != ancestor) meta_stack_raise (window->display->stack, window); g_signal_emit (window, window_signals[RAISED], 0); } void meta_window_lower (MetaWindow *window) { g_return_if_fail (!window->override_redirect); meta_topic (META_DEBUG_WINDOW_OPS, "Lowering window %s\n", window->desc); meta_stack_lower (window->display->stack, window); } /* * Move window to the requested workspace; append controls whether new WS * should be created if one does not exist. */ void meta_window_change_workspace_by_index (MetaWindow *window, gint space_index, gboolean append) { MetaWorkspaceManager *workspace_manager; MetaWorkspace *workspace; MetaDisplay *display; g_return_if_fail (!window->override_redirect); if (space_index == -1) { meta_window_stick (window); return; } display = window->display; workspace_manager = display->workspace_manager; workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, space_index); if (!workspace && append) workspace = meta_workspace_manager_append_new_workspace (workspace_manager, FALSE, META_CURRENT_TIME); if (workspace) meta_window_change_workspace (window, workspace); } static void meta_window_appears_focused_changed (MetaWindow *window) { set_net_wm_state (window); meta_window_frame_size_changed (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_APPEARS_FOCUSED]); if (window->frame) meta_frame_queue_draw (window->frame); } static gboolean should_propagate_focus_appearance (MetaWindow *window) { /* Parents of attached modal dialogs should appear focused. */ if (meta_window_is_attached_dialog (window)) return TRUE; /* Parents of these sorts of override-redirect windows should * appear focused. */ switch (window->type) { case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_COMBO: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: return TRUE; default: break; } return FALSE; } /** * meta_window_propagate_focus_appearance: * @window: the window to start propagating from * @focused: %TRUE if @window's ancestors should appear focused, * %FALSE if they should not. * * Adjusts the value of #MetaWindow:appears-focused on @window's * ancestors (but not @window itself). If @focused is %TRUE, each of * @window's ancestors will have its %attached_focus_window field set * to the current %focus_window. If @focused if %FALSE, each of * @window's ancestors will have its %attached_focus_window field * cleared if it is currently %focus_window. */ static void meta_window_propagate_focus_appearance (MetaWindow *window, gboolean focused) { MetaWindow *child, *parent, *focus_window; focus_window = window->display->focus_window; child = window; parent = meta_window_get_transient_for (child); while (parent && (!focused || should_propagate_focus_appearance (child))) { gboolean child_focus_state_changed; if (focused) { if (parent->attached_focus_window == focus_window) break; child_focus_state_changed = (parent->attached_focus_window == NULL); parent->attached_focus_window = focus_window; } else { if (parent->attached_focus_window != focus_window) break; child_focus_state_changed = (parent->attached_focus_window != NULL); parent->attached_focus_window = NULL; } if (child_focus_state_changed && !parent->has_focus) { meta_window_appears_focused_changed (parent); } child = parent; parent = meta_window_get_transient_for (child); } } void meta_window_set_focused_internal (MetaWindow *window, gboolean focused) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; if (focused) { window->has_focus = TRUE; if (window->override_redirect) return; /* Move to the front of the focusing workspace's MRU list. * We should only be "removing" it from the MRU list if it's * not already there. Note that it's possible that we might * be processing this FocusIn after we've changed to a * different workspace; we should therefore update the MRU * list only if the window is actually on the active * workspace. */ if (workspace_manager->active_workspace && meta_window_located_on_workspace (window, workspace_manager->active_workspace)) { GList* link; link = g_list_find (workspace_manager->active_workspace->mru_list, window); g_assert (link); workspace_manager->active_workspace->mru_list = g_list_remove_link (workspace_manager->active_workspace->mru_list, link); g_list_free (link); workspace_manager->active_workspace->mru_list = g_list_prepend (workspace_manager->active_workspace->mru_list, window); } if (window->frame) meta_frame_queue_draw (window->frame); /* Ungrab click to focus button since the sync grab can interfere * with some things you might do inside the focused window, by * causing the client to get funky enter/leave events. * * The reason we usually have a passive grab on the window is * so that we can intercept clicks and raise the window in * response. For click-to-focus we don't need that since the * focused window is already raised. When raise_on_click is * FALSE we also don't need that since we don't do anything * when the window is clicked. * * There is dicussion in bugs 102209, 115072, and 461577 */ if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK || !meta_prefs_get_raise_on_click()) { meta_display_ungrab_focus_window_button (window->display, window); /* Since we ungrab with XIAnyModifier above, all button grabs go way so we need to re-grab the window buttons. */ meta_display_grab_window_buttons (window->display, window->xwindow); } g_signal_emit (window, window_signals[FOCUS], 0); if (!window->attached_focus_window) meta_window_appears_focused_changed (window); meta_window_propagate_focus_appearance (window, TRUE); } else { window->has_focus = FALSE; if (window->override_redirect) return; meta_window_propagate_focus_appearance (window, FALSE); if (!window->attached_focus_window) meta_window_appears_focused_changed (window); /* Re-grab for click to focus and raise-on-click, if necessary */ if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK || !meta_prefs_get_raise_on_click ()) meta_display_grab_focus_window_button (window->display, window); } } /** * meta_window_get_icon_geometry: * @window: a #MetaWindow * @rect: (out): rectangle into which to store the returned geometry. * * Gets the location of the icon corresponding to the window. The location * will be provided set by the task bar or other user interface element * displaying the icon, and is relative to the root window. * * Return value: %TRUE if the icon geometry was succesfully retrieved. */ gboolean meta_window_get_icon_geometry (MetaWindow *window, MetaRectangle *rect) { g_return_val_if_fail (!window->override_redirect, FALSE); if (window->icon_geometry_set) { if (rect) *rect = window->icon_geometry; return TRUE; } return FALSE; } /** * meta_window_set_icon_geometry: * @window: a #MetaWindow * @rect: (nullable): rectangle with the desired geometry or %NULL. * * Sets or unsets the location of the icon corresponding to the window. If * set, the location should correspond to a dock, task bar or other user * interface element displaying the icon, and is relative to the root window. */ void meta_window_set_icon_geometry (MetaWindow *window, MetaRectangle *rect) { if (rect) { window->icon_geometry = *rect; window->icon_geometry_set = TRUE; } else { window->icon_geometry_set = FALSE; } } static void redraw_icon (MetaWindow *window) { /* We could probably be smart and just redraw the icon here, * instead of the whole frame. */ if (window->frame) meta_frame_queue_draw (window->frame); } static void meta_window_update_icon_now (MetaWindow *window, gboolean force) { gboolean changed; cairo_surface_t *icon = NULL; cairo_surface_t *mini_icon; g_return_if_fail (!window->override_redirect); changed = META_WINDOW_GET_CLASS (window)->update_icon (window, &icon, &mini_icon); if (changed || force) { if (window->icon) cairo_surface_destroy (window->icon); window->icon = icon; if (window->mini_icon) cairo_surface_destroy (window->mini_icon); window->mini_icon = mini_icon; g_object_freeze_notify (G_OBJECT (window)); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_ICON]); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_MINI_ICON]); g_object_thaw_notify (G_OBJECT (window)); redraw_icon (window); } } static gboolean idle_update_icon (gpointer data) { GSList *tmp; GSList *copy; guint queue_index = GPOINTER_TO_INT (data); meta_topic (META_DEBUG_GEOMETRY, "Clearing the update_icon queue\n"); /* Work with a copy, for reentrancy. The allowed reentrancy isn't * complete; destroying a window while we're in here would result in * badness. But it's OK to queue/unqueue update_icons. */ copy = g_slist_copy (queue_pending[queue_index]); g_slist_free (queue_pending[queue_index]); queue_pending[queue_index] = NULL; queue_later[queue_index] = 0; destroying_windows_disallowed += 1; tmp = copy; while (tmp != NULL) { MetaWindow *window; window = tmp->data; meta_window_update_icon_now (window, FALSE); window->is_in_queues &= ~META_QUEUE_UPDATE_ICON; tmp = tmp->next; } g_slist_free (copy); destroying_windows_disallowed -= 1; return FALSE; } GList* meta_window_get_workspaces (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; if (window->on_all_workspaces) return workspace_manager->workspaces; else if (window->workspace != NULL) return window->workspace->list_containing_self; else if (window->constructing) return NULL; else g_assert_not_reached (); return NULL; } static void invalidate_work_areas (MetaWindow *window) { GList *tmp; tmp = meta_window_get_workspaces (window); while (tmp != NULL) { meta_workspace_invalidate_work_area (tmp->data); tmp = tmp->next; } } void meta_window_update_struts (MetaWindow *window) { if (META_WINDOW_GET_CLASS (window)->update_struts (window)) invalidate_work_areas (window); } static void meta_window_type_changed (MetaWindow *window) { gboolean old_decorated = window->decorated; GObject *object = G_OBJECT (window); window->attached = meta_window_should_attach_to_parent (window); meta_window_recalc_features (window); if (!window->override_redirect) set_net_wm_state (window); /* Update frame */ if (window->decorated) meta_window_ensure_frame (window); else meta_window_destroy_frame (window); /* update stacking constraints */ meta_window_update_layer (window); meta_window_grab_keys (window); g_object_freeze_notify (object); if (old_decorated != window->decorated) g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DECORATED]); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_WINDOW_TYPE]); g_object_thaw_notify (object); } void meta_window_set_type (MetaWindow *window, MetaWindowType type) { if (window->type == type) return; window->type = type; meta_window_type_changed (window); } void meta_window_frame_size_changed (MetaWindow *window) { if (window->frame) meta_frame_clear_cached_borders (window->frame); } static void meta_window_get_default_skip_hints (MetaWindow *window, gboolean *skip_taskbar_out, gboolean *skip_pager_out) { META_WINDOW_GET_CLASS (window)->get_default_skip_hints (window, skip_taskbar_out, skip_pager_out); } static void meta_window_recalc_skip_features (MetaWindow *window) { switch (window->type) { /* Force skip taskbar/pager on these window types */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_MENU: case META_WINDOW_UTILITY: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: window->skip_taskbar = TRUE; window->skip_pager = TRUE; break; case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: /* only skip taskbar if we have a real transient parent (and ignore the application hints) */ if (window->transient_for != NULL) window->skip_taskbar = TRUE; else window->skip_taskbar = FALSE; break; case META_WINDOW_NORMAL: { gboolean skip_taskbar_hint, skip_pager_hint; meta_window_get_default_skip_hints (window, &skip_taskbar_hint, &skip_pager_hint); window->skip_taskbar = skip_taskbar_hint; window->skip_pager = skip_pager_hint; } break; } } void meta_window_recalc_features (MetaWindow *window) { gboolean old_has_close_func; gboolean old_has_minimize_func; gboolean old_has_move_func; gboolean old_has_resize_func; gboolean old_has_shade_func; gboolean old_always_sticky; gboolean old_skip_taskbar; old_has_close_func = window->has_close_func; old_has_minimize_func = window->has_minimize_func; old_has_move_func = window->has_move_func; old_has_resize_func = window->has_resize_func; old_has_shade_func = window->has_shade_func; old_always_sticky = window->always_sticky; old_skip_taskbar = window->skip_taskbar; /* Use MWM hints initially */ if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) window->decorated = window->mwm_decorated; else window->decorated = FALSE; window->border_only = window->mwm_border_only; window->has_close_func = window->mwm_has_close_func; window->has_minimize_func = window->mwm_has_minimize_func; window->has_maximize_func = window->mwm_has_maximize_func; window->has_move_func = window->mwm_has_move_func; window->has_resize_func = TRUE; /* If min_size == max_size, then don't allow resize */ if (window->size_hints.min_width == window->size_hints.max_width && window->size_hints.min_height == window->size_hints.max_height) window->has_resize_func = FALSE; else if (!window->mwm_has_resize_func) { /* We ignore mwm_has_resize_func because WM_NORMAL_HINTS is the * authoritative source for that info. Some apps such as mplayer or * xine disable resize via MWM but not WM_NORMAL_HINTS, but that * leads to e.g. us not fullscreening their windows. Apps that set * MWM but not WM_NORMAL_HINTS are basically broken. We complain * about these apps but make them work. */ meta_warning ("Window %s sets an MWM hint indicating it isn't resizable, but sets min size %d x %d and max size %d x %d; this doesn't make much sense.\n", window->desc, window->size_hints.min_width, window->size_hints.min_height, window->size_hints.max_width, window->size_hints.max_height); } window->has_shade_func = TRUE; window->has_fullscreen_func = TRUE; window->always_sticky = FALSE; /* Semantic category overrides the MWM hints */ if (window->type == META_WINDOW_TOOLBAR) window->decorated = FALSE; if (window->type == META_WINDOW_DESKTOP || window->type == META_WINDOW_DOCK || window->override_redirect) window->always_sticky = TRUE; if (window->override_redirect || meta_window_get_frame_type (window) == META_FRAME_TYPE_LAST) { window->decorated = FALSE; window->has_close_func = FALSE; window->has_shade_func = FALSE; /* FIXME this keeps panels and things from using * NET_WM_MOVERESIZE; the problem is that some * panels (edge panels) have fixed possible locations, * and others ("floating panels") do not. * * Perhaps we should require edge panels to explicitly * disable movement? */ window->has_move_func = FALSE; window->has_resize_func = FALSE; } if (window->type != META_WINDOW_NORMAL) { window->has_minimize_func = FALSE; window->has_maximize_func = FALSE; window->has_fullscreen_func = FALSE; } if (!window->has_resize_func) { window->has_maximize_func = FALSE; MetaRectangle display_rect = { 0 }; meta_display_get_size (window->display, &display_rect.width, &display_rect.height); /* don't allow fullscreen if we can't resize, unless the size * is entire screen size (kind of broken, because we * actually fullscreen to monitor size not screen size) */ if (window->size_hints.min_width == display_rect.width && window->size_hints.min_height == display_rect.height) ; /* leave fullscreen available */ else window->has_fullscreen_func = FALSE; } /* We leave fullscreen windows decorated, just push the frame outside * the screen. This avoids flickering to unparent them. * * Note that setting has_resize_func = FALSE here must come after the * above code that may disable fullscreen, because if the window * is not resizable purely due to fullscreen, we don't want to * disable fullscreen mode. */ if (window->fullscreen) { window->has_shade_func = FALSE; window->has_move_func = FALSE; window->has_resize_func = FALSE; window->has_maximize_func = FALSE; } if (window->has_maximize_func && window->monitor) { MetaRectangle work_area, client_rect; meta_window_get_work_area_current_monitor (window, &work_area); meta_window_frame_rect_to_client_rect (window, &work_area, &client_rect); if (window->size_hints.min_width >= client_rect.width || window->size_hints.min_height >= client_rect.height) window->has_maximize_func = FALSE; } meta_topic (META_DEBUG_WINDOW_OPS, "Window %s fullscreen = %d not resizable, maximizable = %d fullscreenable = %d min size %dx%d max size %dx%d\n", window->desc, window->fullscreen, window->has_maximize_func, window->has_fullscreen_func, window->size_hints.min_width, window->size_hints.min_height, window->size_hints.max_width, window->size_hints.max_height); /* no shading if not decorated */ if (!window->decorated || window->border_only) window->has_shade_func = FALSE; meta_window_recalc_skip_features (window); /* To prevent users from losing windows, let's prevent users from * minimizing skip-taskbar windows through the window decorations. */ if (window->skip_taskbar) window->has_minimize_func = FALSE; meta_topic (META_DEBUG_WINDOW_OPS, "Window %s decorated = %d border_only = %d has_close = %d has_minimize = %d has_maximize = %d has_move = %d has_shade = %d skip_taskbar = %d skip_pager = %d\n", window->desc, window->decorated, window->border_only, window->has_close_func, window->has_minimize_func, window->has_maximize_func, window->has_move_func, window->has_shade_func, window->skip_taskbar, window->skip_pager); if (old_skip_taskbar != window->skip_taskbar) { g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_SKIP_TASKBAR]); g_signal_emit_by_name (window->display, "window-skip-taskbar-changed", window); } /* FIXME: * Lame workaround for recalc_features being used overzealously. * The fix is to only recalc_features when something has * actually changed. */ if (window->constructing || old_has_close_func != window->has_close_func || old_has_minimize_func != window->has_minimize_func || old_has_move_func != window->has_move_func || old_has_resize_func != window->has_resize_func || old_has_shade_func != window->has_shade_func || old_always_sticky != window->always_sticky) set_allowed_actions_hint (window); if (window->has_resize_func != old_has_resize_func) g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_RESIZEABLE]); meta_window_frame_size_changed (window); /* FIXME perhaps should ensure if we don't have a shade func, * we aren't shaded, etc. */ } void meta_window_show_menu (MetaWindow *window, MetaWindowMenuType menu, int x, int y) { g_return_if_fail (!window->override_redirect); meta_compositor_show_window_menu (window->display->compositor, window, menu, x, y); } void meta_window_show_menu_for_rect (MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect) { g_return_if_fail (!window->override_redirect); meta_compositor_show_window_menu_for_rect (window->display->compositor, window, menu, rect); } void meta_window_shove_titlebar_onscreen (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaRectangle frame_rect; GList *onscreen_region; int horiz_amount, vert_amount; g_return_if_fail (!window->override_redirect); /* If there's no titlebar, don't bother */ if (!window->frame) return; /* Get the basic info we need */ meta_window_get_frame_rect (window, &frame_rect); onscreen_region = workspace_manager->active_workspace->screen_region; /* Extend the region (just in case the window is too big to fit on the * screen), then shove the window on screen, then return the region to * normal. */ horiz_amount = frame_rect.width; vert_amount = frame_rect.height; meta_rectangle_expand_region (onscreen_region, horiz_amount, horiz_amount, 0, vert_amount); meta_rectangle_shove_into_region(onscreen_region, FIXED_DIRECTION_X, &frame_rect); meta_rectangle_expand_region (onscreen_region, -horiz_amount, -horiz_amount, 0, -vert_amount); meta_window_move_frame (window, FALSE, frame_rect.x, frame_rect.y); } gboolean meta_window_titlebar_is_onscreen (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaRectangle titlebar_rect, frame_rect; GList *onscreen_region; gboolean is_onscreen; const int min_height_needed = 8; const float min_width_percent = 0.5; const int min_width_absolute = 50; /* Titlebar can't be offscreen if there is no titlebar... */ if (!window->frame) return TRUE; /* Get the rectangle corresponding to the titlebar */ meta_window_get_titlebar_rect (window, &titlebar_rect); /* Translate into screen coordinates */ meta_window_get_frame_rect (window, &frame_rect); titlebar_rect.x = frame_rect.x; titlebar_rect.y = frame_rect.y; /* Run through the spanning rectangles for the screen and see if one of * them overlaps with the titlebar sufficiently to consider it onscreen. */ is_onscreen = FALSE; onscreen_region = workspace_manager->active_workspace->screen_region; while (onscreen_region) { MetaRectangle *spanning_rect = onscreen_region->data; MetaRectangle overlap; meta_rectangle_intersect (&titlebar_rect, spanning_rect, &overlap); if (overlap.height > MIN (titlebar_rect.height, min_height_needed) && overlap.width > MIN (titlebar_rect.width * min_width_percent, min_width_absolute)) { is_onscreen = TRUE; break; } onscreen_region = onscreen_region->next; } return is_onscreen; } static gboolean check_moveresize_frequency (MetaWindow *window, gdouble *remaining) { int64_t current_time; const double max_resizes_per_second = 25.0; const double ms_between_resizes = 1000.0 / max_resizes_per_second; double elapsed; current_time = g_get_real_time (); /* If we are throttling via _NET_WM_SYNC_REQUEST, we don't need * an artificial timeout-based throttled */ if (!window->disable_sync && window->sync_request_alarm != None) return TRUE; elapsed = (current_time - window->display->grab_last_moveresize_time) / 1000; if (elapsed >= 0.0 && elapsed < ms_between_resizes) { meta_topic (META_DEBUG_RESIZING, "Delaying move/resize as only %g of %g ms elapsed\n", elapsed, ms_between_resizes); if (remaining) *remaining = (ms_between_resizes - elapsed); return FALSE; } meta_topic (META_DEBUG_RESIZING, " Checked moveresize freq, allowing move/resize now (%g of %g seconds elapsed)\n", elapsed / 1000.0, 1.0 / max_resizes_per_second); return TRUE; } static gboolean update_move_timeout (gpointer data) { MetaWindow *window = data; update_move (window, window->display->grab_last_user_action_was_snap, window->display->grab_latest_motion_x, window->display->grab_latest_motion_y); return FALSE; } enum { ZONE_NONE = 0, ZONE_TOP = 1 << 0, ZONE_RIGHT = 1 << 1, ZONE_BOTTOM = 1 << 2, ZONE_LEFT = 1 << 3 }; #define BOX_CENTER_X(box) ((box).x + (box).width / 2) /* Center in X */ #define BOX_CENTER_Y(box) ((box).y + (box).height / 2) /* Center in Y */ #define ORIGIN_CONSTANT 1 #define EXTREME_CONSTANT 2 #define COMMON_EDGE_PADDING 5 static void get_extra_padding_for_common_monitor_edges (MetaWindow *window, gint monitor_num, MetaRectangle cur_rect, gint *left_shift, gint *right_shift, gint *up_shift, gint *down_shift) { gint num_mons; gint i; MetaRectangle other_rect; num_mons = meta_display_get_n_monitors (window->display); if (num_mons == 1) return; for (i = 0; i < num_mons; i++) { if (i == monitor_num) continue; meta_display_get_monitor_geometry (window->display, i, &other_rect); if (BOX_CENTER_X (other_rect) < BOX_LEFT (cur_rect)) { *left_shift = COMMON_EDGE_PADDING; continue; } if (BOX_CENTER_X (other_rect) > BOX_RIGHT (cur_rect)) { *right_shift = COMMON_EDGE_PADDING; continue; } if (BOX_CENTER_Y (other_rect) < BOX_TOP (cur_rect)) { *up_shift = COMMON_EDGE_PADDING; continue; } if (BOX_CENTER_Y (other_rect) > BOX_BOTTOM (cur_rect)) { *down_shift = COMMON_EDGE_PADDING; continue; } } } static gint get_tile_zone_at_pointer (MetaWindow *window, MetaLogicalMonitor *logical_monitor, MetaRectangle work_area, int x, int y) { gint zones = ZONE_NONE; gint left_shift, right_shift, up_shift, down_shift; left_shift = right_shift = up_shift = down_shift = 0; MetaRectangle lm_rect = logical_monitor->rect; get_extra_padding_for_common_monitor_edges (window, logical_monitor->number, work_area, &left_shift, &right_shift, &up_shift, &down_shift); if (x >= BOX_LEFT (lm_rect) && x <= (BOX_LEFT (work_area) + ORIGIN_CONSTANT + left_shift)) zones |= ZONE_LEFT; if ((x >= BOX_RIGHT (work_area) - EXTREME_CONSTANT - right_shift) && x < BOX_RIGHT (lm_rect)) zones |= ZONE_RIGHT; if (y >= BOX_TOP (lm_rect) && y <= (BOX_TOP (work_area) + ORIGIN_CONSTANT + up_shift)) zones |= ZONE_TOP; if ((y >= BOX_BOTTOM (work_area) - EXTREME_CONSTANT - down_shift) && y < BOX_BOTTOM (lm_rect)) zones |= ZONE_BOTTOM; return zones; } static void update_move_maybe_tile (MetaWindow *window, int shake_threshold, int x, int y) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; MetaDisplay *display = window->display; MetaRectangle work_area; /* For side-by-side tiling we are interested in the inside vertical * edges of the work area of the monitor where the pointer is located, * and in the outside top edge for maximized tiling. * * For maximized tiling we use the outside edge instead of the * inside edge, because we don't want to force users to maximize * windows they are placing near the top of their screens. * * The "current" idea of meta_window_get_work_area_current_monitor() and * meta_screen_get_current_monitor() is slightly different: the former * refers to the monitor which contains the largest part of the window, * the latter to the one where the pointer is located. */ logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); if (!logical_monitor) return; meta_window_get_work_area_for_monitor (window, logical_monitor->number, &work_area); /* Check if the cursor is in a position which triggers tiling * and set tile_mode accordingly. */ if (meta_window_can_tile_left_right (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == ZONE_LEFT) display->preview_tile_mode = META_TILE_LEFT; else if (meta_window_can_tile_left_right (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == ZONE_RIGHT) display->preview_tile_mode = META_TILE_RIGHT; else if (get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == ZONE_TOP) { if (meta_window_can_maximize (window) && (meta_prefs_get_tile_maximize () || window->saved_tile_mode == META_TILE_MAXIMIZED)) { display->preview_tile_mode = META_TILE_MAXIMIZED; } else if (meta_window_can_tile_top_bottom (window)) { display->preview_tile_mode = META_TILE_TOP; } } else if (meta_window_can_tile_top_bottom (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == ZONE_BOTTOM) display->preview_tile_mode = META_TILE_BOTTOM; else if (meta_window_can_tile_corner (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == (ZONE_LEFT | ZONE_TOP)) display->preview_tile_mode = META_TILE_ULC; else if (meta_window_can_tile_corner (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == (ZONE_RIGHT | ZONE_TOP)) display->preview_tile_mode = META_TILE_URC; else if (meta_window_can_tile_corner (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == (ZONE_RIGHT | ZONE_BOTTOM)) display->preview_tile_mode = META_TILE_LRC; else if (meta_window_can_tile_corner (window) && get_tile_zone_at_pointer (window, logical_monitor, work_area, x, y) == (ZONE_LEFT | ZONE_BOTTOM)) display->preview_tile_mode = META_TILE_LLC; else display->preview_tile_mode = META_TILE_NONE; if (display->preview_tile_mode != META_TILE_NONE) window->tile_monitor_number = logical_monitor->number; } static void update_move (MetaWindow *window, gboolean snap, int x, int y) { int dx, dy; int new_x, new_y; MetaRectangle old; int shake_threshold; MetaDisplay *display = window->display; display->grab_latest_motion_x = x; display->grab_latest_motion_y = y; dx = x - display->grab_anchor_root_x; dy = y - display->grab_anchor_root_y; new_x = display->grab_anchor_window_pos.x + dx; new_y = display->grab_anchor_window_pos.y + dy; meta_verbose ("x,y = %d,%d anchor ptr %d,%d anchor pos %d,%d dx,dy %d,%d\n", x, y, display->grab_anchor_root_x, display->grab_anchor_root_y, display->grab_anchor_window_pos.x, display->grab_anchor_window_pos.y, dx, dy); /* Don't bother doing anything if no move has been specified. (This * happens often, even in keyboard moving, due to the warping of the * pointer. */ if (dx == 0 && dy == 0) return; /* Originally for detaching maximized windows, but we use this * for the zones at the sides of the monitor where trigger tiling * because it's about the right size */ #define DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR 6 shake_threshold = meta_prefs_get_drag_threshold () * DRAG_THRESHOLD_TO_SHAKE_THRESHOLD_FACTOR; if (snap) { /* We don't want to tile while snapping. Also, clear any previous tile request. */ display->preview_tile_mode = META_TILE_NONE; window->tile_monitor_number = -1; } else if (meta_prefs_get_edge_tiling () && !META_WINDOW_MAXIMIZED (window) && !META_WINDOW_TILED (window)) { update_move_maybe_tile (window, shake_threshold, x, y); } /* shake loose (unmaximize) maximized or tiled window if dragged beyond * the threshold in the Y direction. Tiled windows can also be pulled * loose via X motion. The fact you can 'slide' one-way-maximized * windows along their maximized edge is useful but it's easy to accidentally * pull them loose while doing so. Double their thresholds to make this less * likely to happen. */ if ((META_WINDOW_MAXIMIZED (window) && ABS (dy) >= shake_threshold) || (META_WINDOW_MAXIMIZED_HORIZONTALLY (window) && ABS (dx) >= (shake_threshold * 2)) || (META_WINDOW_MAXIMIZED_VERTICALLY (window) && ABS (dy) >= (shake_threshold * 2)) || (META_WINDOW_TILED (window) && (MAX (ABS (dx), ABS (dy)) >= shake_threshold))) { double prop; /* Shake loose, so that the window snaps back to maximized * when dragged near the top; do not snap back if tiling * is enabled, as top edge tiling can be used in that case */ window->shaken_loose = !meta_prefs_get_edge_tiling (); if (window->tile_mode != META_TILE_NONE) { window->tile_mode = META_TILE_NONE; notify_tile_mode (window); } /* move the unmaximized window to the cursor */ prop = ((double)(x - display->grab_initial_window_pos.x)) / ((double)display->grab_initial_window_pos.width); display->grab_initial_window_pos.x = x - window->saved_rect.width * prop; /* If we started dragging the window from above the top of the window, * pretend like we started dragging from the middle of the titlebar * instead, as the "correct" anchoring looks wrong. */ if (display->grab_anchor_root_y < display->grab_initial_window_pos.y) { MetaRectangle titlebar_rect; meta_window_get_titlebar_rect (window, &titlebar_rect); display->grab_anchor_root_y = display->grab_initial_window_pos.y + titlebar_rect.height / 2; } window->saved_rect.x = display->grab_initial_window_pos.x; window->saved_rect.y = display->grab_initial_window_pos.y; meta_window_unmaximize (window, META_MAXIMIZE_BOTH); return; } /* remaximize window on another monitor if window has been shaken * loose or it is still maximized (then move straight) */ else if (window->shaken_loose && (window->tile_mode == META_TILE_NONE || window->tile_mode == META_TILE_MAXIMIZED)) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); int n_logical_monitors; const MetaLogicalMonitor *wmonitor; MetaRectangle work_area; int monitor; if (window->tile_mode != META_TILE_NONE) { window->tile_mode = META_TILE_NONE; notify_tile_mode (window); } wmonitor = window->monitor; n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); for (monitor = 0; monitor < n_logical_monitors; monitor++) { meta_window_get_work_area_for_monitor (window, monitor, &work_area); /* check if cursor is near the top of a monitor work area */ if (x >= work_area.x && x < (work_area.x + work_area.width) && y <= work_area.y) { window->saved_rect.x = work_area.x; window->saved_rect.y = work_area.y; if (window->frame) { window->saved_rect.x += window->frame->child_x; window->saved_rect.y += window->frame->child_y; } window->unconstrained_rect.x = window->saved_rect.x; window->unconstrained_rect.y = window->saved_rect.y; if (monitor != wmonitor->number && window->saved_tile_mode == META_TILE_NONE) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); display->grab_initial_window_pos = work_area; display->grab_anchor_root_x = x; display->grab_anchor_root_y = y; window->shaken_loose = FALSE; meta_window_maximize (window, META_MAXIMIZE_BOTH); return; } } } /* Delay showing the tile preview slightly to make it more unlikely to * trigger it unwittingly, e.g. when shaking loose the window or moving * it to another monitor. */ meta_display_update_tile_preview (window->display, window->tile_mode != META_TILE_NONE); meta_window_get_frame_rect (window, &old); /* Don't allow movement in the maximized directions or while tiled */ if (window->maximized_horizontally || META_WINDOW_TILED (window)) new_x = old.x; if (window->maximized_vertically || META_WINDOW_TILED (window)) new_y = old.y; /* Do any edge resistance/snapping */ meta_window_edge_resistance_for_move (window, &new_x, &new_y, update_move_timeout, snap, FALSE); meta_window_move_frame (window, TRUE, new_x, new_y); } static gboolean update_resize_timeout (gpointer data) { MetaWindow *window = data; update_resize (window, window->display->grab_last_user_action_was_snap, window->display->grab_latest_motion_x, window->display->grab_latest_motion_y, TRUE); return FALSE; } static void update_resize (MetaWindow *window, gboolean snap, int x, int y, gboolean force) { int dx, dy; MetaGravity gravity; MetaRectangle new_rect; MetaRectangle old_rect; double remaining = 0; window->display->grab_latest_motion_x = x; window->display->grab_latest_motion_y = y; dx = x - window->display->grab_anchor_root_x; dy = y - window->display->grab_anchor_root_y; /* Attached modal dialogs are special in that size * changes apply to both sides, so that the dialog * remains centered to the parent. */ if (meta_window_is_attached_dialog (window)) { dx *= 2; dy *= 2; } new_rect.width = window->display->grab_anchor_window_pos.width; new_rect.height = window->display->grab_anchor_window_pos.height; /* Don't bother doing anything if no move has been specified. (This * happens often, even in keyboard resizing, due to the warping of the * pointer. */ if (dx == 0 && dy == 0) return; if (window->display->grab_op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN) { MetaGrabOp op = META_GRAB_OP_WINDOW_BASE | META_GRAB_OP_WINDOW_FLAG_KEYBOARD; if (dx > 0) op |= META_GRAB_OP_WINDOW_DIR_EAST; else if (dx < 0) op |= META_GRAB_OP_WINDOW_DIR_WEST; if (dy > 0) op |= META_GRAB_OP_WINDOW_DIR_SOUTH; else if (dy < 0) op |= META_GRAB_OP_WINDOW_DIR_NORTH; window->display->grab_op = op; meta_window_update_keyboard_resize (window, TRUE); } if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_EAST) new_rect.width += dx; else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_WEST) new_rect.width -= dx; if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_SOUTH) new_rect.height += dy; else if (window->display->grab_op & META_GRAB_OP_WINDOW_DIR_NORTH) new_rect.height -= dy; meta_window_maybe_apply_size_hints (window, &new_rect); /* If we're waiting for a request for _NET_WM_SYNC_REQUEST, we'll * resize the window when the window responds, or when we time * the response out. */ if (window->sync_request_timeout_id != 0) return; if (!check_moveresize_frequency (window, &remaining) && !force) { /* we are ignoring an event here, so we schedule a * compensation event when we would otherwise not ignore * an event. Otherwise we can become stuck if the user never * generates another event. */ if (!window->display->grab_resize_timeout_id) { window->display->grab_resize_timeout_id = g_timeout_add ((int)remaining, update_resize_timeout, window); g_source_set_name_by_id (window->display->grab_resize_timeout_id, "[mutter] update_resize_timeout"); } return; } /* Remove any scheduled compensation events */ g_clear_handle_id (&window->display->grab_resize_timeout_id, g_source_remove); meta_window_get_frame_rect (window, &old_rect); /* One sided resizing ought to actually be one-sided, despite the fact that * aspect ratio windows don't interact nicely with the above stuff. So, * to avoid some nasty flicker, we enforce that. */ if ((window->display->grab_op & (META_GRAB_OP_WINDOW_DIR_WEST | META_GRAB_OP_WINDOW_DIR_EAST)) == 0) new_rect.width = old_rect.width; if ((window->display->grab_op & (META_GRAB_OP_WINDOW_DIR_NORTH | META_GRAB_OP_WINDOW_DIR_SOUTH)) == 0) new_rect.height = old_rect.height; /* compute gravity of client during operation */ gravity = meta_resize_gravity_from_grab_op (window->display->grab_op); g_assert (gravity >= 0); /* Do any edge resistance/snapping */ meta_window_edge_resistance_for_resize (window, &new_rect.width, &new_rect.height, gravity, update_resize_timeout, snap, FALSE); meta_window_resize_frame_with_gravity (window, TRUE, new_rect.width, new_rect.height, gravity); /* Store the latest resize time, if we actually resized. */ if (window->rect.width != old_rect.width || window->rect.height != old_rect.height) window->display->grab_last_moveresize_time = g_get_real_time (); } void meta_window_update_resize (MetaWindow *window, gboolean snap, int x, int y, gboolean force) { update_resize (window, snap, x, y, force); } static void end_grab_op (MetaWindow *window, const ClutterEvent *event) { ClutterModifierType modifiers; gfloat x, y; clutter_event_get_coords (event, &x, &y); modifiers = clutter_event_get_state (event); meta_display_check_threshold_reached (window->display, x, y); /* If the user was snap moving then ignore the button * release because they may have let go of shift before * releasing the mouse button and they almost certainly do * not want a non-snapped movement to occur from the button * release. */ if (!window->display->grab_last_user_action_was_snap) { if (meta_grab_op_is_moving (window->display->grab_op)) { if (window->display->preview_tile_mode != META_TILE_NONE) meta_window_tile (window, window->display->preview_tile_mode); else update_move (window, modifiers & CLUTTER_SHIFT_MASK, x, y); } else if (meta_grab_op_is_resizing (window->display->grab_op)) { update_resize (window, modifiers & CLUTTER_SHIFT_MASK || (window->htile_match != NULL || window->vtile_match != NULL), x, y, TRUE); } } window->display->preview_tile_mode = META_TILE_NONE; meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_USER_OP); if (!META_WINDOW_MAXIMIZED (window) && !META_WINDOW_TILED (window)) window->saved_tile_mode = META_TILE_NONE; meta_display_end_grab_op (window->display, clutter_event_get_time (event)); } gboolean meta_window_handle_mouse_grab_op_event (MetaWindow *window, const ClutterEvent *event) { ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); ClutterModifierType modifier_state; gfloat x, y; switch (event->type) { case CLUTTER_BUTTON_PRESS: { ClutterModifierType grab_mods = meta_display_get_window_grab_modifiers (window->display); /* This is the keybinding or menu case where we've * been dragging around the window without the button * pressed. */ if ((meta_grab_op_is_mouse (window->display->grab_op) && (event->button.modifier_state & grab_mods) == grab_mods && window->display->grab_button != (int) event->button.button) || meta_grab_op_is_keyboard (window->display->grab_op)) { end_grab_op (window, event); return FALSE; } return TRUE; } case CLUTTER_TOUCH_END: if (meta_display_is_pointer_emulating_sequence (window->display, sequence)) end_grab_op (window, event); return TRUE; case CLUTTER_BUTTON_RELEASE: if (event->button.button == 1 || event->button.button == (unsigned int) meta_prefs_get_mouse_button_resize ()) end_grab_op (window, event); return TRUE; case CLUTTER_TOUCH_BEGIN: /* This will only catch the keybinding and menu cases, just deal with this * like a CLUTTER_TOUCH_UPDATE rather than a CLUTTER_BUTTON_PRESS, and * wait until CLUTTER_TOUCH_END to undo the grab, just so the window * doesn't warp below the finger and remain there. */ case CLUTTER_TOUCH_UPDATE: if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) return FALSE; /* Fall through */ case CLUTTER_MOTION: modifier_state = clutter_event_get_state (event); clutter_event_get_coords (event, &x, &y); meta_display_check_threshold_reached (window->display, x, y); if (meta_grab_op_is_moving (window->display->grab_op)) { update_move (window, modifier_state & CLUTTER_SHIFT_MASK, x, y); } else if (meta_grab_op_is_resizing (window->display->grab_op)) { update_resize (window, modifier_state & CLUTTER_SHIFT_MASK || (window->htile_match != NULL || window->vtile_match != NULL), x, y, FALSE); } return TRUE; default: return FALSE; } } void meta_window_get_work_area_for_logical_monitor (MetaWindow *window, MetaLogicalMonitor *logical_monitor, MetaRectangle *area) { GList *tmp; g_assert (logical_monitor); /* Initialize to the whole monitor */ *area = logical_monitor->rect; tmp = meta_window_get_workspaces (window); while (tmp != NULL) { MetaRectangle workspace_work_area; meta_workspace_get_work_area_for_logical_monitor (tmp->data, logical_monitor, &workspace_work_area); meta_rectangle_intersect (area, &workspace_work_area, area); tmp = tmp->next; } meta_topic (META_DEBUG_WORKAREA, "Window %s monitor %d has work area %d,%d %d x %d\n", window->desc, logical_monitor->number, area->x, area->y, area->width, area->height); } /** * meta_window_get_work_area_current_monitor: * @window: a #MetaWindow * @area: (out): a location to store the work area * * Get the work area for the monitor @window is currently on. */ void meta_window_get_work_area_current_monitor (MetaWindow *window, MetaRectangle *area) { meta_window_get_work_area_for_monitor (window, window->monitor->number, area); } /** * meta_window_get_work_area_for_monitor: * @window: a #MetaWindow * @which_monitor: a moniotr to get the work area for * @area: (out): a location to store the work area * * Get the work area for @window, given the monitor index * @which_monitor. */ void meta_window_get_work_area_for_monitor (MetaWindow *window, int which_monitor, MetaRectangle *area) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; g_return_if_fail (which_monitor >= 0); logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, which_monitor); meta_window_get_work_area_for_logical_monitor (window, logical_monitor, area); } /** * meta_window_get_work_area_all_monitors: * @window: a #MetaWindow * @area: (out): a location to store the work area * * Get the work area for all monitors for @window. */ void meta_window_get_work_area_all_monitors (MetaWindow *window, MetaRectangle *area) { GList *tmp; MetaRectangle display_rect = { 0 }; meta_display_get_size (window->display, &display_rect.width, &display_rect.height); /* Initialize to the whole display */ *area = display_rect; tmp = meta_window_get_workspaces (window); while (tmp != NULL) { MetaRectangle workspace_work_area; meta_workspace_get_work_area_all_monitors (tmp->data, &workspace_work_area); meta_rectangle_intersect (area, &workspace_work_area, area); tmp = tmp->next; } meta_topic (META_DEBUG_WORKAREA, "Window %s has whole-screen work area %d,%d %d x %d\n", window->desc, area->x, area->y, area->width, area->height); } int meta_window_get_current_tile_monitor_number (MetaWindow *window) { int tile_monitor_number = window->tile_monitor_number; if (tile_monitor_number < 0) { meta_warning ("%s called with an invalid monitor number; using 0 instead\n", G_STRFUNC); tile_monitor_number = 0; } return tile_monitor_number; } void meta_window_get_tile_area (MetaWindow *window, MetaTileMode tile_mode, MetaRectangle *tile_area) { MetaRectangle work_area; int tile_monitor_number; double vfraction, hfraction; g_return_if_fail (tile_mode != META_TILE_NONE); tile_monitor_number = meta_window_get_current_tile_monitor_number (window); meta_window_get_work_area_for_monitor (window, tile_monitor_number, &work_area); meta_window_get_tile_fractions (window, tile_mode, &hfraction, &vfraction, NULL); *tile_area = work_area; tile_area->width = round (tile_area->width * hfraction); tile_area->height = round (tile_area->height * vfraction); switch (tile_mode) { case META_TILE_RIGHT: case META_TILE_URC: tile_area->x += work_area.width - tile_area->width; break; case META_TILE_BOTTOM: case META_TILE_LLC: tile_area->y += work_area.height - tile_area->height; break; case META_TILE_LRC: tile_area->x += work_area.width - tile_area->width; tile_area->y += work_area.height - tile_area->height; break; default: break; } } gboolean meta_window_same_application (MetaWindow *window, MetaWindow *other_window) { MetaGroup *group = meta_window_get_group (window); MetaGroup *other_group = meta_window_get_group (other_window); return group!=NULL && other_group!=NULL && group==other_group; } /** * meta_window_is_client_decorated: * * Check if if the window has decorations drawn by the client. * (window->decorated refers only to whether we should add decorations) */ gboolean meta_window_is_client_decorated (MetaWindow *window) { if (window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) { /* Assume all Wayland clients draw decorations - not strictly * true but good enough for current purposes. */ return TRUE; } else { /* Currently the implementation here is hackish - * has_custom_frame_extents() is set if _GTK_FRAME_EXTENTS is set * to any value even 0. GTK+ always sets _GTK_FRAME_EXTENTS for * client-side-decorated window, even if the value is 0 because * the window is maxized and has no invisible borders or shadows. */ return window->has_custom_frame_extents; } } /** * meta_window_foreach_transient: * @window: a #MetaWindow * @func: (scope call) (closure user_data): Called for each window which is a transient of @window (transitively) * @user_data: User data * * Call @func for every window which is either transient for @window, or is * a transient of a window which is in turn transient for @window. * The order of window enumeration is not defined. * * Iteration will stop if @func at any point returns %FALSE. */ void meta_window_foreach_transient (MetaWindow *window, MetaWindowForeachFunc func, void *user_data) { GSList *windows; GSList *tmp; windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *transient = tmp->data; if (meta_window_is_ancestor_of_transient (window, transient)) { if (!(* func) (transient, user_data)) break; } tmp = tmp->next; } g_slist_free (windows); } /** * meta_window_foreach_ancestor: * @window: a #MetaWindow * @func: (scope call) (closure user_data): Called for each window which is a transient parent of @window * @user_data: User data * * If @window is transient, call @func with the window for which it's transient, * repeatedly until either we find a non-transient window, or @func returns %FALSE. */ void meta_window_foreach_ancestor (MetaWindow *window, MetaWindowForeachFunc func, void *user_data) { MetaWindow *w; w = window; do { if (w->transient_for == NULL) break; w = w->transient_for; } while (w && (* func) (w, user_data)); } typedef struct { MetaWindow *ancestor; gboolean found; } FindAncestorData; static gboolean find_ancestor_func (MetaWindow *window, void *data) { FindAncestorData *d = data; if (window == d->ancestor) { d->found = TRUE; return FALSE; } return TRUE; } /** * meta_window_is_ancestor_of_transient: * @window: a #MetaWindow * @transient: a #MetaWindow * * The function determines whether @window is an ancestor of @transient; it does * so by traversing the @transient's ancestors until it either locates @window * or reaches an ancestor that is not transient. * * Return Value: %TRUE if window is an ancestor of transient. */ gboolean meta_window_is_ancestor_of_transient (MetaWindow *window, MetaWindow *transient) { FindAncestorData d; d.ancestor = window; d.found = FALSE; meta_window_foreach_ancestor (transient, find_ancestor_func, &d); return d.found; } /* Warp pointer to location appropriate for grab, * return root coordinates where pointer ended up. */ static gboolean warp_grab_pointer (MetaWindow *window, MetaGrabOp grab_op, int *x, int *y) { MetaRectangle rect; MetaRectangle display_rect = { 0 }; MetaDisplay *display; display = window->display; meta_display_get_size (display, &display_rect.width, &display_rect.height); /* We may not have done begin_grab_op yet, i.e. may not be in a grab */ meta_window_get_frame_rect (window, &rect); if (grab_op & META_GRAB_OP_WINDOW_DIR_WEST) *x = 0; else if (grab_op & META_GRAB_OP_WINDOW_DIR_EAST) *x = rect.width - 1; else *x = rect.width / 2; if (grab_op & META_GRAB_OP_WINDOW_DIR_NORTH) *y = 0; else if (grab_op & META_GRAB_OP_WINDOW_DIR_SOUTH) *y = rect.height - 1; else *y = rect.height / 2; *x += rect.x; *y += rect.y; /* Avoid weird bouncing at the screen edge; see bug 154706 */ *x = CLAMP (*x, 0, display_rect.width - 1); *y = CLAMP (*y, 0, display_rect.height - 1); meta_x11_error_trap_push (display->x11_display); meta_topic (META_DEBUG_WINDOW_OPS, "Warping pointer to %d,%d with window at %d,%d\n", *x, *y, rect.x, rect.y); /* Need to update the grab positions so that the MotionNotify and other * events generated by the XWarpPointer() call below don't cause complete * funkiness. See bug 124582 and bug 122670. */ display->grab_anchor_root_x = *x; display->grab_anchor_root_y = *y; display->grab_latest_motion_x = *x; display->grab_latest_motion_y = *y; meta_window_get_frame_rect (window, &display->grab_anchor_window_pos); { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); clutter_seat_warp_pointer (seat, *x, *y); } if (meta_x11_error_trap_pop_with_return (display->x11_display) != Success) { meta_verbose ("Failed to warp pointer for window %s\n", window->desc); return FALSE; } return TRUE; } void meta_window_begin_grab_op (MetaWindow *window, MetaGrabOp op, gboolean frame_action, guint32 timestamp) { int x, y; warp_grab_pointer (window, op, &x, &y); meta_display_begin_grab_op (window->display, window, op, FALSE, frame_action, 0 /* button */, 0, timestamp, x, y); } void meta_window_update_keyboard_resize (MetaWindow *window, gboolean update_cursor) { int x, y; warp_grab_pointer (window, window->display->grab_op, &x, &y); if (update_cursor) meta_display_update_cursor (window->display); } void meta_window_update_keyboard_move (MetaWindow *window) { int x, y; warp_grab_pointer (window, window->display->grab_op, &x, &y); } MetaStackLayer meta_window_get_default_layer (MetaWindow *window) { if (window->wm_state_below) return META_LAYER_BOTTOM; else if (window->wm_state_above && !META_WINDOW_MAXIMIZED (window)) return META_LAYER_TOP; else return META_LAYER_NORMAL; } void meta_window_update_layer (MetaWindow *window) { MetaGroup *group; meta_stack_freeze (window->display->stack); group = meta_window_get_group (window); if (group) meta_group_update_layers (group); else meta_stack_update_layer (window->display->stack, window); meta_stack_thaw (window->display->stack); } /* ensure_mru_position_after ensures that window appears after * below_this_one in the active_workspace's mru_list (i.e. it treats * window as having been less recently used than below_this_one) */ static void ensure_mru_position_after (MetaWindow *window, MetaWindow *after_this_one) { /* This is sort of slow since it runs through the entire list more * than once (especially considering the fact that we expect the * windows of interest to be the first two elements in the list), * but it doesn't matter while we're only using it on new window * map. */ MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; GList* active_mru_list; GList* window_position; GList* after_this_one_position; active_mru_list = workspace_manager->active_workspace->mru_list; window_position = g_list_find (active_mru_list, window); after_this_one_position = g_list_find (active_mru_list, after_this_one); /* after_this_one_position is NULL when we switch workspaces, but in * that case we don't need to do any MRU shuffling so we can simply * return. */ if (after_this_one_position == NULL) return; if (g_list_length (window_position) > g_list_length (after_this_one_position)) { workspace_manager->active_workspace->mru_list = g_list_delete_link (workspace_manager->active_workspace->mru_list, window_position); workspace_manager->active_workspace->mru_list = g_list_insert_before (workspace_manager->active_workspace->mru_list, after_this_one_position->next, window); } } gboolean meta_window_is_in_stack (MetaWindow *window) { return window->stack_position >= 0; } void meta_window_stack_just_below (MetaWindow *window, MetaWindow *below_this_one) { g_return_if_fail (window != NULL); g_return_if_fail (below_this_one != NULL); if (window->stack_position > below_this_one->stack_position) { meta_topic (META_DEBUG_STACK, "Setting stack position of window %s to %d (making it below window %s).\n", window->desc, below_this_one->stack_position, below_this_one->desc); meta_window_set_stack_position (window, below_this_one->stack_position); } else { meta_topic (META_DEBUG_STACK, "Window %s was already below window %s.\n", window->desc, below_this_one->desc); } } void meta_window_stack_just_above (MetaWindow *window, MetaWindow *above_this_one) { g_return_if_fail (window != NULL); g_return_if_fail (above_this_one != NULL); if (window->stack_position < above_this_one->stack_position) { meta_topic (META_DEBUG_STACK, "Setting stack position of window %s to %d (making it above window %s).\n", window->desc, above_this_one->stack_position, above_this_one->desc); meta_window_set_stack_position (window, above_this_one->stack_position); } else { meta_topic (META_DEBUG_STACK, "Window %s was already above window %s.\n", window->desc, above_this_one->desc); } } /** * meta_window_get_user_time: * @window: a #MetaWindow * * The user time represents a timestamp for the last time the user * interacted with this window. Note this property is only available * for non-override-redirect windows. * * The property is set by Mutter initially upon window creation, * and updated thereafter on input events (key and button presses) seen by Mutter, * client updates to the _NET_WM_USER_TIME property (if later than the current time) * and when focusing the window. * * Returns: The last time the user interacted with this window. */ guint32 meta_window_get_user_time (MetaWindow *window) { return window->net_wm_user_time; } void meta_window_set_user_time (MetaWindow *window, guint32 timestamp) { /* FIXME: If Soeren's suggestion in bug 151984 is implemented, it will allow * us to sanity check the timestamp here and ensure it doesn't correspond to * a future time. */ g_return_if_fail (!window->override_redirect); /* Only update the time if this timestamp is newer... */ if (window->net_wm_user_time_set && XSERVER_TIME_IS_BEFORE (timestamp, window->net_wm_user_time)) { meta_topic (META_DEBUG_STARTUP, "Window %s _NET_WM_USER_TIME not updated to %u, because it " "is less than %u\n", window->desc, timestamp, window->net_wm_user_time); } else { meta_topic (META_DEBUG_STARTUP, "Window %s has _NET_WM_USER_TIME of %u\n", window->desc, timestamp); window->net_wm_user_time_set = TRUE; window->net_wm_user_time = timestamp; if (XSERVER_TIME_IS_BEFORE (window->display->last_user_time, timestamp)) window->display->last_user_time = timestamp; /* If this is a terminal, user interaction with it means the user likely * doesn't want to have focus transferred for now due to new windows. */ if (meta_prefs_get_focus_new_windows () == C_DESKTOP_FOCUS_NEW_WINDOWS_STRICT && window_is_terminal (window)) window->display->allow_terminal_deactivation = FALSE; g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_USER_TIME]); } } /** * meta_window_get_stable_sequence: * @window: A #MetaWindow * * The stable sequence number is a monotonicially increasing * unique integer assigned to each #MetaWindow upon creation. * * This number can be useful for sorting windows in a stable * fashion. * * Returns: Internal sequence number for this window */ guint32 meta_window_get_stable_sequence (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), 0); return window->stable_sequence; } /* Sets the demands_attention hint on a window, but only * if it's at least partially obscured (see #305882). */ void meta_window_set_demands_attention (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaRectangle candidate_rect, other_rect; GList *stack = window->display->stack->sorted; MetaWindow *other_window; gboolean obscured = FALSE; MetaWorkspace *workspace = workspace_manager->active_workspace; if (window->wm_state_demands_attention) return; if (!meta_window_located_on_workspace (window, workspace)) { /* windows on other workspaces are necessarily obscured */ obscured = TRUE; } else if (window->minimized) { obscured = TRUE; } else { meta_window_get_frame_rect (window, &candidate_rect); /* The stack is sorted with the top windows first. */ while (stack != NULL && stack->data != window) { other_window = stack->data; stack = stack->next; if (meta_window_located_on_workspace (other_window, workspace)) { meta_window_get_frame_rect (other_window, &other_rect); if (meta_rectangle_overlap (&candidate_rect, &other_rect)) { obscured = TRUE; break; } } } } if (obscured) { meta_topic (META_DEBUG_WINDOW_OPS, "Marking %s as needing attention\n", window->desc); window->wm_state_demands_attention = TRUE; set_net_wm_state (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); g_signal_emit_by_name (window->display, "window-demands-attention", window); } else { /* If the window's in full view, there's no point setting the flag. */ meta_topic (META_DEBUG_WINDOW_OPS, "Not marking %s as needing attention because " "it's in full view\n", window->desc); } } void meta_window_unset_demands_attention (MetaWindow *window) { meta_topic (META_DEBUG_WINDOW_OPS, "Marking %s as not needing attention\n", window->desc); if (window->wm_state_demands_attention) { window->wm_state_demands_attention = FALSE; set_net_wm_state (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_DEMANDS_ATTENTION]); } } /** * meta_window_get_frame: (skip) * @window: a #MetaWindow * */ MetaFrame * meta_window_get_frame (MetaWindow *window) { return window->frame; } /** * meta_window_appears_focused: * @window: a #MetaWindow * * Determines if the window should be drawn with a focused appearance. This is * true for focused windows but also true for windows with a focused modal * dialog attached. * * Return value: %TRUE if the window should be drawn with a focused frame */ gboolean meta_window_appears_focused (MetaWindow *window) { return window->has_focus || (window->attached_focus_window != NULL); } gboolean meta_window_has_focus (MetaWindow *window) { return window->has_focus; } gboolean meta_window_is_shaded (MetaWindow *window) { return window->shaded; } /** * meta_window_is_override_redirect: * @window: A #MetaWindow * * Returns: %TRUE if this window isn't managed by mutter; it will * control its own positioning and mutter won't draw decorations * among other things. In X terminology this is "override redirect". */ gboolean meta_window_is_override_redirect (MetaWindow *window) { return window->override_redirect; } /** * meta_window_is_skip_taskbar: * @window: A #MetaWindow * * Gets whether this window should be ignored by task lists. * * Return value: %TRUE if the skip bar hint is set. */ gboolean meta_window_is_skip_taskbar (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), FALSE); return window->skip_taskbar; } /** * meta_window_get_display: * @window: A #MetaWindow * * Returns: (transfer none): The display for @window */ MetaDisplay * meta_window_get_display (MetaWindow *window) { return window->display; } /** * meta_window_get_xwindow: * @window: a #MetaWindow * * Returns: (type gulong): The Window (XID) of @window */ Window meta_window_get_xwindow (MetaWindow *window) { return window->xwindow; } MetaWindowType meta_window_get_window_type (MetaWindow *window) { return window->type; } /** * meta_window_get_workspace: * @window: a #MetaWindow * * Gets the #MetaWorkspace that the window is currently displayed on. * If the window is on all workspaces, returns the currently active * workspace. * * Return value: (transfer none): the #MetaWorkspace for the window */ MetaWorkspace * meta_window_get_workspace (MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; if (window->on_all_workspaces) return workspace_manager->active_workspace; else return window->workspace; } gboolean meta_window_is_on_all_workspaces (MetaWindow *window) { return window->on_all_workspaces; } gboolean meta_window_is_hidden (MetaWindow *window) { return window->hidden; } const char * meta_window_get_description (MetaWindow *window) { if (!window) return NULL; return window->desc; } /** * meta_window_get_wm_class: * @window: a #MetaWindow * * Return the current value of the name part of WM_CLASS X property. */ const char * meta_window_get_wm_class (MetaWindow *window) { if (!window) return NULL; return window->res_class; } /** * meta_window_get_wm_class_instance: * @window: a #MetaWindow * * Return the current value of the instance part of WM_CLASS X property. */ const char * meta_window_get_wm_class_instance (MetaWindow *window) { if (!window) return NULL; return window->res_name; } /** * meta_window_get_sandboxed_app_id: * @window: a #MetaWindow * * Gets an unique id for a sandboxed app (currently flatpaks and snaps are * supported). * * Return value: (transfer none): the sandboxed application ID or %NULL **/ const char * meta_window_get_sandboxed_app_id (MetaWindow *window) { /* We're abusing this API here not to break the gnome shell assumptions * or adding a new function, to be renamed to generic names in new versions */ return window->sandboxed_app_id; } /** * meta_window_get_gtk_theme_variant: * @window: a #MetaWindow * * Return value: (transfer none): the theme variant or %NULL **/ const char * meta_window_get_gtk_theme_variant (MetaWindow *window) { return window->gtk_theme_variant; } /** * meta_window_get_gtk_application_id: * @window: a #MetaWindow * * Return value: (transfer none): the application ID **/ const char * meta_window_get_gtk_application_id (MetaWindow *window) { return window->gtk_application_id; } /** * meta_window_get_gtk_unique_bus_name: * @window: a #MetaWindow * * Return value: (transfer none): the unique name **/ const char * meta_window_get_gtk_unique_bus_name (MetaWindow *window) { return window->gtk_unique_bus_name; } /** * meta_window_get_gtk_application_object_path: * @window: a #MetaWindow * * Return value: (transfer none): the object path **/ const char * meta_window_get_gtk_application_object_path (MetaWindow *window) { return window->gtk_application_object_path; } /** * meta_window_get_gtk_window_object_path: * @window: a #MetaWindow * * Return value: (transfer none): the object path **/ const char * meta_window_get_gtk_window_object_path (MetaWindow *window) { return window->gtk_window_object_path; } /** * meta_window_get_gtk_app_menu_object_path: * @window: a #MetaWindow * * Return value: (transfer none): the object path **/ const char * meta_window_get_gtk_app_menu_object_path (MetaWindow *window) { return window->gtk_app_menu_object_path; } /** * meta_window_get_gtk_menubar_object_path: * @window: a #MetaWindow * * Return value: (transfer none): the object path **/ const char * meta_window_get_gtk_menubar_object_path (MetaWindow *window) { return window->gtk_menubar_object_path; } /** * meta_window_get_compositor_private: * @window: a #MetaWindow * * Gets the compositor's wrapper object for @window. * * Return value: (transfer none): the wrapper object. **/ GObject * meta_window_get_compositor_private (MetaWindow *window) { if (!window) return NULL; return window->compositor_private; } void meta_window_set_compositor_private (MetaWindow *window, GObject *priv) { if (!window) return; window->compositor_private = priv; } const char * meta_window_get_role (MetaWindow *window) { if (!window) return NULL; return window->role; } /** * meta_window_get_title: * @window: a #MetaWindow * * Returns: the current title of the window. */ const char * meta_window_get_title (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); return window->title; } MetaStackLayer meta_window_get_layer (MetaWindow *window) { return window->layer; } /** * meta_window_get_transient_for: * @window: a #MetaWindow * * Returns the #MetaWindow for the window that is pointed to by the * WM_TRANSIENT_FOR hint on this window (see XGetTransientForHint() * or XSetTransientForHint()). Metacity keeps transient windows above their * parents. A typical usage of this hint is for a dialog that wants to stay * above its associated window. * * Return value: (transfer none): the window this window is transient for, or * %NULL if the WM_TRANSIENT_FOR hint is unset or does not point to a toplevel * window that Metacity knows about. */ MetaWindow * meta_window_get_transient_for (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); if (window->transient_for) return window->transient_for; else if (window->xtransient_for) return meta_x11_display_lookup_x_window (window->display->x11_display, window->xtransient_for); else return NULL; } /** * meta_window_get_client_pid: * @window: a #MetaWindow * * Returns the pid of the process that created this window, if available * to the windowing system. * * Return value: the pid, or 0 if not known. */ uint32_t meta_window_get_client_pid (MetaWindow *window) { return META_WINDOW_GET_CLASS (window)->get_client_pid (window); } /** * meta_window_get_pid: * @window: a #MetaWindow * * Returns pid of the process that created this window, if known (obtained from * the _NET_WM_PID property). * * Return value: the pid, or -1 if not known. */ int meta_window_get_pid (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), -1); return window->net_wm_pid; } /** * meta_window_get_client_machine: * @window: a #MetaWindow * * Returns name of the client machine from which this windows was created, * if known (obtained from the WM_CLIENT_MACHINE property). * * Return value: (transfer none): the machine name, or NULL; the string is * owned by the window manager and should not be freed or modified by the * caller. */ const char * meta_window_get_client_machine (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); return window->wm_client_machine; } /** * meta_window_is_remote: * @window: a #MetaWindow * * Returns: %TRUE if this window originates from a host * different from the one running mutter. */ gboolean meta_window_is_remote (MetaWindow *window) { return window->is_remote; } /** * meta_window_get_mutter_hints: * @window: a #MetaWindow * * Gets the current value of the _MUTTER_HINTS property. * * The purpose of the hints is to allow fine-tuning of the Window Manager and * Compositor behaviour on per-window basis, and is intended primarily for * hints that are plugin-specific. * * The property is a list of colon-separated key=value pairs. The key names for * any plugin-specific hints must be suitably namespaced to allow for shared * use; 'mutter-' key prefix is reserved for internal use, and must not be used * by plugins. * * Return value: (transfer none): the _MUTTER_HINTS string, or %NULL if no hints * are set. */ const char * meta_window_get_mutter_hints (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); return window->mutter_hints; } /** * meta_window_get_frame_type: * @window: a #MetaWindow * * Gets the type of window decorations that should be used for this window. * * Return value: the frame type */ MetaFrameType meta_window_get_frame_type (MetaWindow *window) { MetaFrameType base_type = META_FRAME_TYPE_LAST; switch (window->type) { case META_WINDOW_NORMAL: base_type = META_FRAME_TYPE_NORMAL; break; case META_WINDOW_DIALOG: base_type = META_FRAME_TYPE_DIALOG; break; case META_WINDOW_MODAL_DIALOG: if (meta_window_is_attached_dialog (window)) base_type = META_FRAME_TYPE_ATTACHED; else base_type = META_FRAME_TYPE_MODAL_DIALOG; break; case META_WINDOW_MENU: base_type = META_FRAME_TYPE_MENU; break; case META_WINDOW_UTILITY: base_type = META_FRAME_TYPE_UTILITY; break; case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_SPLASHSCREEN: case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: case META_WINDOW_OVERRIDE_OTHER: /* No frame */ base_type = META_FRAME_TYPE_LAST; break; } if (base_type == META_FRAME_TYPE_LAST) { /* can't add border if undecorated */ return META_FRAME_TYPE_LAST; } else if (window->border_only) { /* override base frame type */ return META_FRAME_TYPE_BORDER; } else { return base_type; } } /** * meta_window_get_frame_bounds: * @window: a #MetaWindow * * Gets a region representing the outer bounds of the window's frame. * * Return value: (transfer none) (nullable): a #cairo_region_t * holding the outer bounds of the window, or %NULL if the window * doesn't have a frame. */ cairo_region_t * meta_window_get_frame_bounds (MetaWindow *window) { if (!window->frame_bounds) { if (window->frame) window->frame_bounds = meta_frame_get_frame_bounds (window->frame); } return window->frame_bounds; } /** * meta_window_is_attached_dialog: * @window: a #MetaWindow * * Tests if @window is should be attached to its parent window. * (If the "attach_modal_dialogs" option is not enabled, this will * always return %FALSE.) * * Return value: whether @window should be attached to its parent */ gboolean meta_window_is_attached_dialog (MetaWindow *window) { return window->attached; } /** * meta_window_get_tile_match: * @window: a #MetaWindow * * Returns the matching tiled window on the same monitor as @window. This is * the topmost tiled window in a complementary tile mode that is: * * - on the same monitor; * - on the same workspace; * - spanning the remaining monitor width; * - there is no 3rd window stacked between both tiled windows that's * partially visible in the common edge. * * Return value: (transfer none) (nullable): An array of matching tiled windows or * %NULL if none exist. */ MetaWindow * meta_window_get_tile_match (MetaWindow *window, gboolean vertical) { return vertical ? window->vtile_match : window->htile_match; } void meta_window_compute_tile_matches (MetaWindow *window) { window->vtile_match = meta_window_find_tile_match (window, window->tile_mode, TRUE); window->htile_match = meta_window_find_tile_match (window, window->tile_mode, FALSE); } static gint LEFT_VMATCHES[] = { META_TILE_NONE, -1 }; static gint LEFT_HMATCHES[] = { META_TILE_RIGHT, META_TILE_URC, META_TILE_LRC, -1 }; static gint RIGHT_VMATCHES[] = { META_TILE_NONE, -1 }; static gint RIGHT_HMATCHES[] = { META_TILE_LEFT, META_TILE_ULC, META_TILE_LLC, -1 }; static gint TOP_VMATCHES[] = { META_TILE_BOTTOM, META_TILE_LLC, META_TILE_LRC, -1 }; static gint TOP_HMATCHES[] = { META_TILE_NONE, -1 }; static gint BOTTOM_VMATCHES[] = { META_TILE_TOP, META_TILE_ULC, META_TILE_URC, -1 }; static gint BOTTOM_HMATCHES[] = { META_TILE_NONE, -1 }; static gint ULC_VMATCHES[] = { META_TILE_LLC, META_TILE_LRC, META_TILE_BOTTOM, -1 }; static gint ULC_HMATCHES[] = { META_TILE_URC, META_TILE_LRC, META_TILE_RIGHT, -1 }; static gint URC_VMATCHES[] = { META_TILE_LLC, META_TILE_LRC, META_TILE_BOTTOM, -1 }; static gint URC_HMATCHES[] = { META_TILE_LLC, META_TILE_ULC, META_TILE_LEFT, -1 }; static gint LRC_VMATCHES[] = { META_TILE_ULC, META_TILE_URC, META_TILE_TOP, -1 }; static gint LRC_HMATCHES[] = { META_TILE_ULC, META_TILE_LLC, META_TILE_LEFT, -1 }; static gint LLC_VMATCHES[] = { META_TILE_ULC, META_TILE_URC, META_TILE_TOP, -1 }; static gint LLC_HMATCHES[] = { META_TILE_URC, META_TILE_LRC, META_TILE_RIGHT, -1 }; static gboolean can_match_mode (MetaWindow *match, gint *modes) { gint i = 0; while (modes[i] != -1) { if (modes[i++] == match->tile_mode) return TRUE; } return FALSE; } static gboolean vert_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2) { return (rect1->y < rect2->y + rect2->height - meta_prefs_get_drag_threshold () && rect2->y < rect1->y + rect1->height - meta_prefs_get_drag_threshold ()); } static gboolean horiz_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2) { return (rect1->x < rect2->x + rect2->width - meta_prefs_get_drag_threshold () && rect2->x < rect1->x + rect1->width - meta_prefs_get_drag_threshold ()); } static MetaWindow * meta_window_find_tile_match (MetaWindow *window, MetaTileMode current_mode, gboolean vertical) { MetaWindow *match; MetaStack *stack; gint *match_tile_modes; if (window->shaded || window->minimized) return NULL; if (vertical) { switch (current_mode) { case META_TILE_LEFT: match_tile_modes = LEFT_VMATCHES; break; case META_TILE_RIGHT: match_tile_modes = RIGHT_VMATCHES; break; case META_TILE_TOP: match_tile_modes = TOP_VMATCHES; break; case META_TILE_BOTTOM: match_tile_modes = BOTTOM_VMATCHES; break; case META_TILE_ULC: match_tile_modes = ULC_VMATCHES; break; case META_TILE_LLC: match_tile_modes = LLC_VMATCHES; break; case META_TILE_URC: match_tile_modes = URC_VMATCHES; break; case META_TILE_LRC: match_tile_modes = LRC_VMATCHES; break; default: return NULL; } } else { switch (current_mode) { case META_TILE_LEFT: match_tile_modes = LEFT_HMATCHES; break; case META_TILE_RIGHT: match_tile_modes = RIGHT_HMATCHES; break; case META_TILE_TOP: match_tile_modes = TOP_HMATCHES; break; case META_TILE_BOTTOM: match_tile_modes = BOTTOM_HMATCHES; break; case META_TILE_ULC: match_tile_modes = ULC_HMATCHES; break; case META_TILE_LLC: match_tile_modes = LLC_HMATCHES; break; case META_TILE_URC: match_tile_modes = URC_HMATCHES; break; case META_TILE_LRC: match_tile_modes = LRC_HMATCHES; break; default: return NULL; } } #if 0 if (match_tile_modes) { g_printerr ("%s matches: ", vertical ? "vertical " : "horizontal"); for (int i = 0; match_tile_modes[i] > -1; i++) { g_printerr ("%s ", meta_display_get_tile_mode_str (match_tile_modes[i])); } g_printerr ("\n"); } #endif stack = window->display->stack; for (match = meta_stack_get_top (stack); match; match = meta_stack_get_below (stack, match, FALSE)) { if (!match->shaded && !match->minimized && META_WINDOW_TILED (match) && can_match_mode (match, match_tile_modes) && (window != match) && match->tile_monitor_number == window->tile_monitor_number && meta_window_get_workspace (match) == meta_window_get_workspace (window)) break; } if (match) { MetaWindow *above, *bottommost, *topmost; MetaRectangle above_rect, bottommost_rect, topmost_rect; if (meta_stack_windows_cmp (window->display->stack, match, window) > 0) { topmost = match; bottommost = window; } else { topmost = window; bottommost = match; } meta_window_get_frame_rect (bottommost, &bottommost_rect); meta_window_get_frame_rect (topmost, &topmost_rect); /* * Don't extend window edges to infinity when looking for matches. Tiling * in a given direction requires some amount of overlap in the other. */ if (!vert_overlap (&bottommost_rect, &topmost_rect) && !horiz_overlap (&bottommost_rect, &topmost_rect)) return NULL; /* * If we are looking for a tile match while actually being tiled, * rather than a match for a potential tile mode, then discard * windows with too much gap or overlap */ gboolean have_match = vertical ? window->vtile_match != NULL : window->htile_match != NULL; if (window->tile_mode == current_mode && !(meta_grab_op_is_resizing (window->display->grab_op) && window->display->grab_window == window && have_match)) { int threshold = meta_prefs_get_drag_threshold (); if (!vertical) { if (ABS (topmost_rect.x - bottommost_rect.x - bottommost_rect.width) > threshold && ABS (bottommost_rect.x - topmost_rect.x - topmost_rect.width) > threshold) { return NULL; } } else { if (ABS (topmost_rect.y - bottommost_rect.y - bottommost_rect.height) > threshold && ABS (bottommost_rect.y - topmost_rect.y - topmost_rect.height) > threshold) return NULL; } } /* * If there's a window stacked in between which is partially visible * behind the topmost tile we don't consider the tiles to match. */ for (above = meta_stack_get_above (stack, bottommost, FALSE); above && above != topmost; above = meta_stack_get_above (stack, above, FALSE)) { if (above->minimized || above->monitor != window->monitor || meta_window_get_workspace (above) != meta_window_get_workspace (window)) continue; meta_window_get_frame_rect (above, &above_rect); if (meta_rectangle_overlap (&above_rect, &bottommost_rect) && meta_rectangle_overlap (&above_rect, &topmost_rect)) return NULL; } } return match; } void meta_window_set_title (MetaWindow *window, const char *title) { g_free (window->title); window->title = g_strdup (title); if (window->frame) meta_frame_update_title (window->frame); meta_window_update_desc (window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_TITLE]); } void meta_window_set_wm_class (MetaWindow *window, const char *wm_class, const char *wm_instance) { g_free (window->res_class); g_free (window->res_name); window->res_name = g_strdup (wm_instance); window->res_class = g_strdup (wm_class); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_WM_CLASS]); } void meta_window_set_gtk_dbus_properties (MetaWindow *window, const char *application_id, const char *unique_bus_name, const char *appmenu_path, const char *menubar_path, const char *application_object_path, const char *window_object_path) { g_object_freeze_notify (G_OBJECT (window)); g_free (window->gtk_application_id); window->gtk_application_id = g_strdup (application_id); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APPLICATION_ID]); g_free (window->gtk_unique_bus_name); window->gtk_unique_bus_name = g_strdup (unique_bus_name); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_UNIQUE_BUS_NAME]); g_free (window->gtk_app_menu_object_path); window->gtk_app_menu_object_path = g_strdup (appmenu_path); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APP_MENU_OBJECT_PATH]); g_free (window->gtk_menubar_object_path); window->gtk_menubar_object_path = g_strdup (menubar_path); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_MENUBAR_OBJECT_PATH]); g_free (window->gtk_application_object_path); window->gtk_application_object_path = g_strdup (application_object_path); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_APPLICATION_OBJECT_PATH]); g_free (window->gtk_window_object_path); window->gtk_window_object_path = g_strdup (window_object_path); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_GTK_WINDOW_OBJECT_PATH]); g_object_thaw_notify (G_OBJECT (window)); } static gboolean check_transient_for_loop (MetaWindow *window, MetaWindow *parent) { while (parent) { if (parent == window) return TRUE; parent = parent->transient_for; } return FALSE; } gboolean meta_window_has_transient_type (MetaWindow *window) { return (window->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG || window->type == META_WINDOW_TOOLBAR || window->type == META_WINDOW_MENU || window->type == META_WINDOW_UTILITY); } void meta_window_set_transient_for (MetaWindow *window, MetaWindow *parent) { if (check_transient_for_loop (window, parent)) { meta_warning ("Setting %s transient for %s would create a loop.\n", window->desc, parent->desc); return; } if (meta_window_appears_focused (window) && window->transient_for != NULL) meta_window_propagate_focus_appearance (window, FALSE); /* may now be a dialog */ if (window->client_type == META_WINDOW_CLIENT_TYPE_X11) { meta_window_x11_recalc_window_type (window); if (!window->constructing) { /* If the window attaches, detaches, or changes attached * parents, we need to destroy the MetaWindow and let a new one * be created (which happens as a side effect of * meta_window_unmanage()). The condition below is correct * because we know window->transient_for has changed. */ if (window->attached || meta_window_should_attach_to_parent (window)) { guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_unmanage (window, timestamp); return; } } } else if (window->attached && parent == NULL) { guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (window->display); meta_window_unmanage (window, timestamp); return; } /* We know this won't create a reference cycle because we check for loops */ g_clear_object (&window->transient_for); window->transient_for = parent ? g_object_ref (parent) : NULL; /* update stacking constraints */ if (!window->override_redirect) meta_stack_update_transient (window->display->stack, window); /* possibly change its group. We treat being a window's transient as * equivalent to making it your group leader, to work around shortcomings * in programs such as xmms-- see #328211. */ if (window->xtransient_for != None && window->xgroup_leader != None && window->xtransient_for != window->xgroup_leader) meta_window_group_leader_changed (window); if (!window->constructing && !window->override_redirect) meta_window_queue (window, META_QUEUE_MOVE_RESIZE | META_QUEUE_CALC_SHOWING); if (meta_window_appears_focused (window) && window->transient_for != NULL) meta_window_propagate_focus_appearance (window, TRUE); } void meta_window_set_opacity (MetaWindow *window, guint8 opacity) { g_return_if_fail (META_IS_WINDOW (window)); window->opacity = opacity; meta_compositor_window_opacity_changed (window->display->compositor, window); g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_OPACITY]); } guint8 meta_window_get_opacity (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), 0xFF); return window->opacity; } #define OPACITY_STEP 32 void meta_window_increase_opacity (MetaWindow *window) { window->opacity = MIN (255, window->opacity + OPACITY_STEP); meta_compositor_window_opacity_changed (window->display->compositor, window); } void meta_window_decrease_opacity (MetaWindow *window) { window->opacity = MAX (meta_prefs_get_min_win_opacity (), window->opacity - OPACITY_STEP); meta_compositor_window_opacity_changed (window->display->compositor, window); } static void reset_ignored_crossing_serials (MetaDisplay *display) { int i; i = 0; while (i < N_IGNORED_CROSSING_SERIALS) { display->ignored_crossing_serials[i] = 0; ++i; } } typedef struct { MetaWindow *window; int pointer_x; int pointer_y; } MetaFocusData; static void mouse_mode_focus (MetaWindow *window, guint32 timestamp) { MetaDisplay *display = window->display; if (window->override_redirect) return; if (window->type != META_WINDOW_DESKTOP) { meta_topic (META_DEBUG_FOCUS, "Focusing %s at time %u.\n", window->desc, timestamp); meta_window_focus (window, timestamp); if (meta_prefs_get_auto_raise ()) meta_display_queue_autoraise_callback (display, window); else meta_topic (META_DEBUG_FOCUS, "Auto raise is disabled\n"); } else { /* In mouse focus mode, we defocus when the mouse *enters* * the DESKTOP window, instead of defocusing on LeaveNotify. * This is because having the mouse enter override-redirect * child windows unfortunately causes LeaveNotify events that * we can't distinguish from the mouse actually leaving the * toplevel window as we expect. But, since we filter out * EnterNotify events on override-redirect windows, this * alternative mechanism works great. */ if (meta_prefs_get_focus_mode() == C_DESKTOP_FOCUS_MODE_MOUSE && display->focus_window != NULL) { meta_topic (META_DEBUG_FOCUS, "Unsetting focus from %s due to mouse entering " "the DESKTOP window\n", display->focus_window->desc); meta_display_unset_input_focus (display, timestamp); } } } static gboolean window_has_pointer_wayland (MetaWindow *window) { ClutterSeat *seat; ClutterInputDevice *dev; ClutterActor *pointer_actor, *window_actor; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); dev = clutter_seat_get_pointer (seat); pointer_actor = clutter_input_device_get_pointer_actor (dev); window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); return pointer_actor && clutter_actor_contains (window_actor, pointer_actor); } static gboolean window_has_pointer_x11 (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; Window root, child; double root_x, root_y, x, y; XIButtonState buttons; XIModifierState mods; XIGroupState group; meta_x11_error_trap_push (x11_display); XIQueryPointer (x11_display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, x11_display->xroot, &root, &child, &root_x, &root_y, &x, &y, &buttons, &mods, &group); meta_x11_error_trap_pop (x11_display); free (buttons.mask); return meta_x11_display_lookup_x_window (x11_display, child) == window; } gboolean meta_window_has_pointer (MetaWindow *window) { if (meta_is_wayland_compositor ()) return window_has_pointer_wayland (window); else return window_has_pointer_x11 (window); } static gboolean window_focus_on_pointer_rest_callback (gpointer data) { MetaFocusData *focus_data = data; MetaWindow *window = focus_data->window; MetaDisplay *display = window->display; MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); int root_x, root_y; guint32 timestamp; if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK) goto out; meta_cursor_tracker_get_pointer (cursor_tracker, &root_x, &root_y, NULL); if (root_x != focus_data->pointer_x || root_y != focus_data->pointer_y) { focus_data->pointer_x = root_x; focus_data->pointer_y = root_y; return G_SOURCE_CONTINUE; } if (!meta_window_has_pointer (window)) goto out; timestamp = meta_display_get_current_time_roundtrip (display); mouse_mode_focus (window, timestamp); out: display->focus_timeout_id = 0; return G_SOURCE_REMOVE; } /* The interval, in milliseconds, we use in focus-follows-mouse * mode to check whether the pointer has stopped moving after a * crossing event. */ #define FOCUS_TIMEOUT_DELAY 25 static void queue_focus_callback (MetaDisplay *display, MetaWindow *window, int pointer_x, int pointer_y) { MetaFocusData *focus_data; focus_data = g_new (MetaFocusData, 1); focus_data->window = window; focus_data->pointer_x = pointer_x; focus_data->pointer_y = pointer_y; g_clear_handle_id (&display->focus_timeout_id, g_source_remove); display->focus_timeout_id = g_timeout_add_full (G_PRIORITY_DEFAULT, FOCUS_TIMEOUT_DELAY, window_focus_on_pointer_rest_callback, focus_data, g_free); g_source_set_name_by_id (display->focus_timeout_id, "[mutter] window_focus_on_pointer_rest_callback"); } void meta_window_handle_enter (MetaWindow *window, guint32 timestamp, guint root_x, guint root_y) { MetaDisplay *display = window->display; switch (meta_prefs_get_focus_mode ()) { case C_DESKTOP_FOCUS_MODE_SLOPPY: case C_DESKTOP_FOCUS_MODE_MOUSE: display->mouse_mode = TRUE; if (window->type != META_WINDOW_DOCK) { if (meta_prefs_get_focus_change_on_pointer_rest()) queue_focus_callback (display, window, root_x, root_y); else mouse_mode_focus (window, timestamp); /* stop ignoring stuff */ reset_ignored_crossing_serials (display); } break; case C_DESKTOP_FOCUS_MODE_CLICK: break; } if (window->type == META_WINDOW_DOCK) meta_window_raise (window); } void meta_window_handle_leave (MetaWindow *window) { if (window->type == META_WINDOW_DOCK && !window->has_focus) meta_window_lower (window); } gboolean meta_window_handle_ui_frame_event (MetaWindow *window, const ClutterEvent *event) { if (!window->frame) return FALSE; return meta_ui_frame_handle_event (window->frame->ui_frame, event); } void meta_window_handle_ungrabbed_event (MetaWindow *window, const ClutterEvent *event) { MetaDisplay *display = window->display; gboolean unmodified; gboolean is_window_grab; gboolean is_window_button_grab_allowed; ClutterModifierType grab_mods, event_mods; ClutterInputDevice *source; gfloat x, y; guint button; if (window->unmanaging) return; if (event->type != CLUTTER_BUTTON_PRESS && event->type != CLUTTER_TOUCH_BEGIN) return; if (event->type == CLUTTER_TOUCH_BEGIN) { ClutterEventSequence *sequence; button = 1; sequence = clutter_event_get_event_sequence (event); if (!meta_display_is_pointer_emulating_sequence (window->display, sequence)) return; } else button = clutter_event_get_button (event); if (display->grab_op != META_GRAB_OP_NONE) return; /* Some windows might not ask for input, in which case we might be here * because we selected for ButtonPress on the root window. In that case, * we have to take special care not to act for an override-redirect window. */ if (window->override_redirect) return; /* Don't focus panels--they must explicitly request focus. * See bug 160470 */ if (window->type != META_WINDOW_DOCK) { meta_topic (META_DEBUG_FOCUS, "Focusing %s due to button %u press (display.c)\n", window->desc, button); meta_window_focus (window, event->any.time); meta_window_check_alive (window, event->any.time); } else /* However, do allow terminals to lose focus due to new * window mappings after the user clicks on a panel. */ display->allow_terminal_deactivation = TRUE; /* We have three passive button grabs: * - on any button, without modifiers => focuses and maybe raises the window * - on resize button, with modifiers => start an interactive resizing * (normally middle) * - on move button, with modifiers => start an interactive move * (normally left) * - on menu button, with modifiers => show the window menu * (normally right) * * We may get here because we actually have a button * grab on the window, or because we're a wayland * compositor and thus we see all the events, so we * need to check if the event is interesting. * We want an event that is not modified for a window. * * We may have other events on the window, for example * a click on a frame button, but that's not for us to * care about. Just let the event through. */ grab_mods = meta_display_get_window_grab_modifiers (display); event_mods = clutter_event_get_state (event); unmodified = (event_mods & grab_mods) == 0; source = clutter_event_get_source_device (event); is_window_button_grab_allowed = !display->focus_window || !meta_window_shortcuts_inhibited (display->focus_window, source); is_window_grab = (is_window_button_grab_allowed && ((event_mods & grab_mods) == grab_mods)); clutter_event_get_coords (event, &x, &y); if (unmodified) { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); else meta_topic (META_DEBUG_FOCUS, "Not raising window on click due to don't-raise-on-click option\n"); } else if (is_window_grab && (int) button == meta_prefs_get_mouse_button_resize ()) { if (window->has_resize_func) { gboolean north, south; gboolean west, east; MetaRectangle frame_rect; MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; meta_window_get_frame_rect (window, &frame_rect); west = x < (frame_rect.x + 1 * frame_rect.width / 3); east = x > (frame_rect.x + 2 * frame_rect.width / 3); north = y < (frame_rect.y + 1 * frame_rect.height / 3); south = y > (frame_rect.y + 2 * frame_rect.height / 3); if (west) op |= META_GRAB_OP_WINDOW_DIR_WEST; if (east) op |= META_GRAB_OP_WINDOW_DIR_EAST; if (north) op |= META_GRAB_OP_WINDOW_DIR_NORTH; if (south) op |= META_GRAB_OP_WINDOW_DIR_SOUTH; if (op != META_GRAB_OP_WINDOW_BASE) meta_display_begin_grab_op (display, window, op, TRUE, FALSE, button, 0, event->any.time, x, y); } } else if (is_window_grab && (int) button == meta_prefs_get_mouse_button_menu ()) { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y); } else if (is_window_grab && (int) button == 1) { if (window->has_move_func) { meta_display_begin_grab_op (display, window, META_GRAB_OP_MOVING, TRUE, FALSE, button, 0, event->any.time, x, y); } } } gboolean meta_window_can_maximize (MetaWindow *window) { return window->has_maximize_func; } gboolean meta_window_can_minimize (MetaWindow *window) { return window->has_minimize_func; } gboolean meta_window_can_shade (MetaWindow *window) { return window->has_shade_func; } gboolean meta_window_can_close (MetaWindow *window) { return window->has_close_func; } gboolean meta_window_is_always_on_all_workspaces (MetaWindow *window) { return window->always_sticky; } gboolean meta_window_is_above (MetaWindow *window) { return window->wm_state_above; } gboolean meta_window_allows_move (MetaWindow *window) { return META_WINDOW_ALLOWS_MOVE (window); } gboolean meta_window_allows_resize (MetaWindow *window) { return META_WINDOW_ALLOWS_RESIZE (window); } void meta_window_set_urgent (MetaWindow *window, gboolean urgent) { if (window->urgent == urgent) return; window->urgent = urgent; g_object_notify_by_pspec (G_OBJECT (window), obj_props[PROP_URGENT]); if (urgent) g_signal_emit_by_name (window->display, "window-marked-urgent", window); } void meta_window_grab_op_began (MetaWindow *window, MetaGrabOp op) { META_WINDOW_GET_CLASS (window)->grab_op_began (window, op); } void meta_window_grab_op_ended (MetaWindow *window, MetaGrabOp op) { META_WINDOW_GET_CLASS (window)->grab_op_ended (window, op); } void meta_window_emit_size_changed (MetaWindow *window) { g_signal_emit (window, window_signals[SIZE_CHANGED], 0); } MetaPlacementRule * meta_window_get_placement_rule (MetaWindow *window) { return window->placement.rule; } void meta_window_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source) { META_WINDOW_GET_CLASS (window)->force_restore_shortcuts (window, source); } gboolean meta_window_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source) { return META_WINDOW_GET_CLASS (window)->shortcuts_inhibited (window, source); } gboolean meta_window_is_focusable (MetaWindow *window) { g_return_val_if_fail (!window->unmanaging, FALSE); return META_WINDOW_GET_CLASS (window)->is_focusable (window); } gboolean meta_window_can_ping (MetaWindow *window) { g_return_val_if_fail (!window->unmanaging, FALSE); return META_WINDOW_GET_CLASS (window)->can_ping (window); } gboolean meta_window_is_stackable (MetaWindow *window) { return META_WINDOW_GET_CLASS (window)->is_stackable (window); } gboolean meta_window_is_focus_async (MetaWindow *window) { return META_WINDOW_GET_CLASS (window)->is_focus_async (window); } MetaStackLayer meta_window_calculate_layer (MetaWindow *window) { return META_WINDOW_GET_CLASS (window)->calculate_layer (window); } /** * meta_window_get_id: * @window: a #MetaWindow * * Returns the window id associated with window. * * Returns: The window id */ uint64_t meta_window_get_id (MetaWindow *window) { return window->id; } /** * meta_window_get_client_type: * @window: a #MetaWindow * * Returns the #MetaWindowClientType of the window. * * Returns: The root ancestor window */ MetaWindowClientType meta_window_get_client_type (MetaWindow *window) { return window->client_type; } /** * meta_window_get_icon_name: * @window: a #MetaWindow * * Returns the currently set icon name or icon path for the window. * * Note: * * This will currently only be non-NULL for programs that use XAppGtkWindow * in place of GtkWindow and use xapp_gtk_window_set_icon_name() or * set_icon_from_file(). These methods will need to be used explicitly in * C programs, but for introspection use you should not need to treat it any * differently (except for using the correct window class.) */ const char * meta_window_get_icon_name (MetaWindow *window) { g_return_val_if_fail (META_IS_WINDOW (window), NULL); return window->theme_icon_name; } muffin-6.4.1/src/core/restart-helper.c0000664000175000017500000000457314723361714016577 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * SECTION:restart-helper * @short_description: helper program during a restart * * To smoothly restart Mutter, we want to keep the composite * overlay window enabled during the restart. This is done by * spawning this program, which keeps a reference to the the composite * overlay window until Mutter picks it back up. */ /* * Copyright (C) 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include int main (int argc, char **argv) { Display *display = XOpenDisplay (NULL); Window selection_window; XSetWindowAttributes xwa; unsigned long mask = 0; xwa.override_redirect = True; mask |= CWOverrideRedirect; XCompositeGetOverlayWindow (display, DefaultRootWindow (display)); selection_window = XCreateWindow (display, DefaultRootWindow (display), -100, -100, 1, 1, 0, 0, InputOnly, DefaultVisual (display, DefaultScreen (display)), mask, &xwa); XSetSelectionOwner (display, XInternAtom (display, "_MUFFIN_RESTART_HELPER", False), selection_window, CurrentTime); /* Mutter looks for an (arbitrary) line printed to stdout to know that * we have started and have a reference to the COW. XSync() so that * everything is set on the X server before Mutter starts restarting. */ XSync (display, False); printf ("STARTED\n"); fflush (stdout); while (True) { XEvent xev; XNextEvent (display, &xev); /* Mutter restarted and unset the selection to indicate that * it has a reference on the COW again */ if (xev.xany.type == SelectionClear) return 0; } } muffin-6.4.1/src/core/meta-border.h0000664000175000017500000000424714723361714016042 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_BORDER_H #define META_BORDER_H #include typedef enum { META_BORDER_MOTION_DIRECTION_POSITIVE_X = 1 << 0, META_BORDER_MOTION_DIRECTION_POSITIVE_Y = 1 << 1, META_BORDER_MOTION_DIRECTION_NEGATIVE_X = 1 << 2, META_BORDER_MOTION_DIRECTION_NEGATIVE_Y = 1 << 3, } MetaBorderMotionDirection; typedef struct _MetaVector2 { float x; float y; } MetaVector2; typedef struct _MetaLine2 { MetaVector2 a; MetaVector2 b; } MetaLine2; typedef struct _MetaBorder { MetaLine2 line; MetaBorderMotionDirection blocking_directions; } MetaBorder; static inline MetaVector2 meta_vector2_subtract (const MetaVector2 a, const MetaVector2 b) { return (MetaVector2) { .x = a.x - b.x, .y = a.y - b.y, }; } gboolean meta_line2_intersects_with (const MetaLine2 *line1, const MetaLine2 *line2, MetaVector2 *intersection); gboolean meta_border_is_horizontal (MetaBorder *border); gboolean meta_border_is_blocking_directions (MetaBorder *border, MetaBorderMotionDirection directions); unsigned int meta_border_get_allows_directions (MetaBorder *border); void meta_border_set_allows_directions (MetaBorder *border, unsigned int directions); #endif /* META_BORDER_H */ muffin-6.4.1/src/core/main-private.h0000664000175000017500000000324514723361714016232 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MAIN_PRIVATE_H #define META_MAIN_PRIVATE_H #include "core/util-private.h" typedef enum _MetaCompositorType { #ifdef HAVE_WAYLAND META_COMPOSITOR_TYPE_WAYLAND, #endif META_COMPOSITOR_TYPE_X11, } MetaCompositorType; typedef enum _MetaDisplayPolicy { META_DISPLAY_POLICY_MANDATORY, META_DISPLAY_POLICY_ON_DEMAND, META_DISPLAY_POLICY_DISABLED, } MetaDisplayPolicy; #define META_POINT_IN_RECT(xcoord, ycoord, rect) \ ((xcoord) >= (rect).x && \ (xcoord) < ((rect).x + (rect).width) && \ (ycoord) >= (rect).y && \ (ycoord) < ((rect).y + (rect).height)) #define META_GRAB_OP_GET_BASE_TYPE(op) (op & 0x00FF) META_EXPORT_TEST void meta_override_compositor_configuration (MetaCompositorType compositor_type, GType backend_gtype); MetaDisplayPolicy meta_get_x11_display_policy (void); #endif /* META_MAIN_PRIVATE_H */ muffin-6.4.1/src/core/restart.c0000664000175000017500000001402414723361714015312 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* * SECTION:restart * @short_description: Smoothly restart the compositor * * There are some cases where we need to restart Mutter in order * to deal with changes in state - the particular case inspiring * this is enabling or disabling stereo output. To make this * fairly smooth for the user, we need to do two things: * * - Display a message to the user and make sure that it is * actually painted before we exit. * - Use a helper program so that the Composite Overlay Window * isn't unmapped and mapped. * * This handles both of these. */ #include "config.h" #include #include "clutter/clutter.h" #include "core/display-private.h" #include "core/util-private.h" #include "meta/main.h" #include "ui/ui.h" #include "x11/meta-x11-display-private.h" static gboolean restart_helper_started = FALSE; static gboolean restart_message_shown = FALSE; static gboolean is_restart = FALSE; void meta_set_is_restart (gboolean whether) { is_restart = whether; } static void restart_check_ready (void) { if (restart_helper_started && restart_message_shown) meta_display_request_restart (meta_get_display ()); } static void restart_helper_read_line_callback (GObject *source_object, GAsyncResult *res, gpointer user_data) { GError *error = NULL; gsize length; char *line = g_data_input_stream_read_line_finish_utf8 (G_DATA_INPUT_STREAM (source_object), res, &length, &error); if (line == NULL) { meta_warning ("Failed to read output from restart helper%s%s\n", error ? ": " : NULL, error ? error->message : NULL); } else g_free (line); /* We don't actually care what the restart helper outputs */ g_object_unref (source_object); restart_helper_started = TRUE; restart_check_ready (); } static gboolean restart_message_painted (gpointer data) { restart_message_shown = TRUE; restart_check_ready (); return FALSE; } /** * meta_restart: * @message: (allow-none): message to display to the user, or %NULL * * Starts the process of restarting the compositor. Note that Mutter's * involvement here is to make the restart visually smooth for the * user - it cannot itself safely reexec a program that embeds libmuttter. * So in order for this to work, the compositor must handle two * signals - MetaDisplay::show-restart-message, to display the * message passed here on the Clutter stage, and ::restart to actually * reexec the compositor. */ void meta_restart (const char *message) { MetaDisplay *display = meta_get_display(); GInputStream *unix_stream; GDataInputStream *data_stream; GError *error = NULL; int helper_out_fd; static const char * const helper_argv[] = { MUTTER_LIBEXECDIR "/muffin-restart-helper", NULL }; if (message && meta_display_show_restart_message (display, message)) { /* Wait until the stage was painted */ clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, restart_message_painted, NULL, NULL); } else { /* Can't show the message, show the message as soon as the * restart helper starts */ restart_message_painted (NULL); } /* We also need to wait for the restart helper to get its * reference to the Composite Overlay Window. */ if (!g_spawn_async_with_pipes (NULL, /* working directory */ (char **)helper_argv, NULL, /* envp */ G_SPAWN_DEFAULT, NULL, NULL, /* child_setup */ NULL, /* child_pid */ NULL, /* standard_input */ &helper_out_fd, NULL, /* standard_error */ &error)) { meta_warning ("Failed to start restart helper: %s\n", error->message); goto error; } unix_stream = g_unix_input_stream_new (helper_out_fd, TRUE); data_stream = g_data_input_stream_new (unix_stream); g_object_unref (unix_stream); g_data_input_stream_read_line_async (data_stream, G_PRIORITY_DEFAULT, NULL, restart_helper_read_line_callback, &error); if (error != NULL) { meta_warning ("Failed to read from restart helper: %s\n", error->message); g_object_unref (data_stream); goto error; } return; error: /* If starting the restart helper fails, then we just go ahead and restart * immediately. We won't get a smooth transition, since the overlay window * will be destroyed and recreated, but otherwise it will work fine. */ restart_helper_started = TRUE; restart_check_ready (); return; } /** * meta_is_restart: * * Returns %TRUE if this instance of Mutter comes from Mutter * restarting itself (for example to enable/disable stereo.) * See meta_restart(). If this is the case, any startup visuals * or animations should be suppressed. */ gboolean meta_is_restart (void) { return is_restart; } muffin-6.4.1/src/core/meta-sound-player.c0000664000175000017500000001743614723361714017206 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "meta/meta-sound-player.h" #define EVENT_SOUNDS_KEY "event-sounds" #define THEME_NAME_KEY "theme-name" typedef struct _MetaPlayRequest MetaPlayRequest; struct _MetaSoundPlayer { GObject parent; GThreadPool *queue; GSettings *settings; ca_context *context; uint32_t id_pool; }; struct _MetaPlayRequest { ca_proplist *props; uint32_t id; gulong cancel_id; GCancellable *cancellable; MetaSoundPlayer *player; }; const char * const cache_whitelist[] = { "bell-window-system", "desktop-switch-left", "desktop-switch-right", "desktop-switch-up", "desktop-switch-down", NULL }; G_DEFINE_TYPE (MetaSoundPlayer, meta_sound_player, G_TYPE_OBJECT) static MetaPlayRequest * meta_play_request_new (MetaSoundPlayer *player, ca_proplist *props, GCancellable *cancellable) { MetaPlayRequest *req; req = g_new0 (MetaPlayRequest, 1); req->props = props; req->player = player; g_set_object (&req->cancellable, cancellable); return req; } static void meta_play_request_free (MetaPlayRequest *req) { g_clear_object (&req->cancellable); ca_proplist_destroy (req->props); g_free (req); } static void meta_sound_player_finalize (GObject *object) { MetaSoundPlayer *player = META_SOUND_PLAYER (object); g_object_unref (player->settings); g_thread_pool_free (player->queue, FALSE, TRUE); ca_context_destroy (player->context); G_OBJECT_CLASS (meta_sound_player_parent_class)->finalize (object); } static void meta_sound_player_class_init (MetaSoundPlayerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_sound_player_finalize; } static void cancelled_cb (GCancellable *cancellable, MetaPlayRequest *req) { ca_context_cancel (req->player->context, req->id); } static void finish_cb (ca_context *context, uint32_t id, int error_code, gpointer user_data) { MetaPlayRequest *req = user_data; if (error_code != CA_ERROR_CANCELED) g_cancellable_disconnect (req->cancellable, req->cancel_id); else if (req->cancellable != NULL) g_clear_signal_handler (&req->cancel_id, req->cancellable); meta_play_request_free (req); } static void play_sound (MetaPlayRequest *req, MetaSoundPlayer *player) { req->id = player->id_pool++; if (ca_context_play_full (player->context, req->id, req->props, finish_cb, req) != CA_SUCCESS) { meta_play_request_free (req); return; } if (req->cancellable) { gulong cancel_id = g_cancellable_connect (req->cancellable, G_CALLBACK (cancelled_cb), req, NULL); if (cancel_id) req->cancel_id = cancel_id; } } static void settings_changed_cb (GSettings *settings, const char *key, MetaSoundPlayer *player) { if (strcmp (key, EVENT_SOUNDS_KEY) == 0) { gboolean enabled; enabled = g_settings_get_boolean (settings, EVENT_SOUNDS_KEY); ca_context_change_props (player->context, CA_PROP_CANBERRA_ENABLE, enabled ? "1" : "0", NULL); } else if (strcmp (key, THEME_NAME_KEY) == 0) { char *theme_name; theme_name = g_settings_get_string (settings, THEME_NAME_KEY); ca_context_change_props (player->context, CA_PROP_CANBERRA_XDG_THEME_NAME, theme_name, NULL); g_free (theme_name); } } static ca_context * create_context (MetaSoundPlayer *player) { ca_context *context; ca_proplist *props; if (ca_context_create (&context) != CA_SUCCESS) return NULL; if (ca_proplist_create (&props) != CA_SUCCESS) { ca_context_destroy (context); return NULL; } ca_proplist_sets (props, CA_PROP_APPLICATION_NAME, "Muffin"); ca_proplist_sets (props, CA_PROP_CANBERRA_ENABLE, "1"); settings_changed_cb (player->settings, EVENT_SOUNDS_KEY, player); settings_changed_cb (player->settings, THEME_NAME_KEY, player); ca_context_change_props_full (context, props); ca_proplist_destroy (props); return context; } static void meta_sound_player_init (MetaSoundPlayer *player) { player->queue = g_thread_pool_new ((GFunc) play_sound, player, 1, FALSE, NULL); player->settings = g_settings_new ("org.cinnamon.desktop.sound"); player->context = create_context (player); g_signal_connect (player->settings, "changed", G_CALLBACK (settings_changed_cb), player); } static void build_ca_proplist (ca_proplist *props, const char *event_property, const char *event_id, const char *event_description) { ca_proplist_sets (props, event_property, event_id); ca_proplist_sets (props, CA_PROP_EVENT_DESCRIPTION, event_description); } /** * meta_sound_player_play_from_theme: * @player: a #MetaSoundPlayer * @name: sound theme name of the event * @description: description of the event * @cancellable: cancellable for the request * * Plays a sound from the sound theme. **/ void meta_sound_player_play_from_theme (MetaSoundPlayer *player, const char *name, const char *description, GCancellable *cancellable) { MetaPlayRequest *req; ca_proplist *props; g_return_if_fail (META_IS_SOUND_PLAYER (player)); g_return_if_fail (name != NULL); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); ca_proplist_create (&props); build_ca_proplist (props, CA_PROP_EVENT_ID, name, description); if (g_strv_contains (cache_whitelist, name)) ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "permanent"); else ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile"); req = meta_play_request_new (player, props, cancellable); g_thread_pool_push (player->queue, req, NULL); } /** * meta_sound_player_play_from_file: * @player: a #MetaSoundPlayer * @file: file to play * @description: description of the played sound * @cancellable: cancellable for the request * * Plays a sound from a file. **/ void meta_sound_player_play_from_file (MetaSoundPlayer *player, GFile *file, const char *description, GCancellable *cancellable) { MetaPlayRequest *req; ca_proplist *props; char *path; g_return_if_fail (META_IS_SOUND_PLAYER (player)); g_return_if_fail (G_IS_FILE (file)); g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable)); path = g_file_get_path (file); g_return_if_fail (path != NULL); ca_proplist_create (&props); build_ca_proplist (props, CA_PROP_MEDIA_FILENAME, path, description); ca_proplist_sets (props, CA_PROP_CANBERRA_CACHE_CONTROL, "volatile"); g_free (path); req = meta_play_request_new (player, props, cancellable); g_thread_pool_push (player->queue, req, NULL); } muffin-6.4.1/src/core/meta-inhibit-shortcuts-dialog-default-private.h0000664000175000017500000000243114723361714024567 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H #define META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H #include #include "meta/meta-plugin.h" #define META_TYPE_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (meta_inhibit_shortcuts_dialog_default_get_type ()) G_DECLARE_FINAL_TYPE (MetaInhibitShortcutsDialogDefault, meta_inhibit_shortcuts_dialog_default, META, INHIBIT_SHORTCUTS_DIALOG_DEFAULT, GObject) MetaInhibitShortcutsDialog * meta_inhibit_shortcuts_dialog_default_new (MetaWindow *window); #endif /* META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT_H */ muffin-6.4.1/src/core/meta-accel-parse.c0000664000175000017500000002365314723361714016741 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "core/meta-accel-parse.h" #include #include #include #include "core/keybindings-private.h" /* This is copied from GTK+ and modified to work with mutter's * internal structures. Originating code comes from gtk/gtkaccelgroup.c */ static inline gboolean is_alt (const gchar *string) { return ((string[0] == '<') && (string[1] == 'a' || string[1] == 'A') && (string[2] == 'l' || string[2] == 'L') && (string[3] == 't' || string[3] == 'T') && (string[4] == '>')); } static inline gboolean is_ctl (const gchar *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'l' || string[3] == 'L') && (string[4] == '>')); } static inline gboolean is_modx (const gchar *string) { return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'd' || string[3] == 'D') && (string[4] >= '1' && string[4] <= '5') && (string[5] == '>')); } static inline gboolean is_ctrl (const gchar *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 't' || string[2] == 'T') && (string[3] == 'r' || string[3] == 'R') && (string[4] == 'l' || string[4] == 'L') && (string[5] == '>')); } static inline gboolean is_shft (const gchar *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'f' || string[3] == 'F') && (string[4] == 't' || string[4] == 'T') && (string[5] == '>')); } static inline gboolean is_shift (const gchar *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'h' || string[2] == 'H') && (string[3] == 'i' || string[3] == 'I') && (string[4] == 'f' || string[4] == 'F') && (string[5] == 't' || string[5] == 'T') && (string[6] == '>')); } static inline gboolean is_control (const gchar *string) { return ((string[0] == '<') && (string[1] == 'c' || string[1] == 'C') && (string[2] == 'o' || string[2] == 'O') && (string[3] == 'n' || string[3] == 'N') && (string[4] == 't' || string[4] == 'T') && (string[5] == 'r' || string[5] == 'R') && (string[6] == 'o' || string[6] == 'O') && (string[7] == 'l' || string[7] == 'L') && (string[8] == '>')); } static inline gboolean is_meta (const gchar *string) { return ((string[0] == '<') && (string[1] == 'm' || string[1] == 'M') && (string[2] == 'e' || string[2] == 'E') && (string[3] == 't' || string[3] == 'T') && (string[4] == 'a' || string[4] == 'A') && (string[5] == '>')); } static inline gboolean is_super (const gchar *string) { return ((string[0] == '<') && (string[1] == 's' || string[1] == 'S') && (string[2] == 'u' || string[2] == 'U') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>')); } static inline gboolean is_hyper (const gchar *string) { return ((string[0] == '<') && (string[1] == 'h' || string[1] == 'H') && (string[2] == 'y' || string[2] == 'Y') && (string[3] == 'p' || string[3] == 'P') && (string[4] == 'e' || string[4] == 'E') && (string[5] == 'r' || string[5] == 'R') && (string[6] == '>')); } static inline gboolean is_primary (const gchar *string) { return ((string[0] == '<') && (string[1] == 'p' || string[1] == 'P') && (string[2] == 'r' || string[2] == 'R') && (string[3] == 'i' || string[3] == 'I') && (string[4] == 'm' || string[4] == 'M') && (string[5] == 'a' || string[5] == 'A') && (string[6] == 'r' || string[6] == 'R') && (string[7] == 'y' || string[7] == 'Y') && (string[8] == '>')); } static inline gboolean is_keycode (const gchar *string) { return (string[0] == '0' && string[1] == 'x' && g_ascii_isxdigit (string[2]) && g_ascii_isxdigit (string[3])); } static gboolean accelerator_parse (const gchar *accelerator, MetaKeyCombo *combo) { guint keyval, keycode; MetaVirtualModifier mods; gint len; combo->keysym = 0; combo->keycode = 0; combo->modifiers = 0; if (accelerator == NULL) return FALSE; keyval = 0; keycode = 0; mods = 0; len = strlen (accelerator); while (len) { if (*accelerator == '<') { if (len >= 9 && is_primary (accelerator)) { /* Primary is treated the same as Control */ accelerator += 9; len -= 9; mods |= META_VIRTUAL_CONTROL_MASK; } else if (len >= 9 && is_control (accelerator)) { accelerator += 9; len -= 9; mods |= META_VIRTUAL_CONTROL_MASK; } else if (len >= 7 && is_shift (accelerator)) { accelerator += 7; len -= 7; mods |= META_VIRTUAL_SHIFT_MASK; } else if (len >= 6 && is_shft (accelerator)) { accelerator += 6; len -= 6; mods |= META_VIRTUAL_SHIFT_MASK; } else if (len >= 6 && is_ctrl (accelerator)) { accelerator += 6; len -= 6; mods |= META_VIRTUAL_CONTROL_MASK; } else if (len >= 6 && is_modx (accelerator)) { static const guint mod_vals[] = { META_VIRTUAL_ALT_MASK, META_VIRTUAL_MOD2_MASK, META_VIRTUAL_MOD3_MASK, META_VIRTUAL_MOD4_MASK, META_VIRTUAL_MOD5_MASK, }; len -= 6; accelerator += 4; mods |= mod_vals[*accelerator - '1']; accelerator += 2; } else if (len >= 5 && is_ctl (accelerator)) { accelerator += 5; len -= 5; mods |= META_VIRTUAL_CONTROL_MASK; } else if (len >= 5 && is_alt (accelerator)) { accelerator += 5; len -= 5; mods |= META_VIRTUAL_ALT_MASK; } else if (len >= 6 && is_meta (accelerator)) { accelerator += 6; len -= 6; mods |= META_VIRTUAL_META_MASK; } else if (len >= 7 && is_hyper (accelerator)) { accelerator += 7; len -= 7; mods |= META_VIRTUAL_HYPER_MASK; } else if (len >= 7 && is_super (accelerator)) { accelerator += 7; len -= 7; mods |= META_VIRTUAL_SUPER_MASK; } else { gchar last_ch; last_ch = *accelerator; while (last_ch && last_ch != '>') { last_ch = *accelerator; accelerator += 1; len -= 1; } } } else { if (len >= 4 && is_keycode (accelerator)) { keycode = strtoul (accelerator, NULL, 16); goto out; } else if (strcmp (accelerator, "Above_Tab") == 0) { keyval = META_KEY_ABOVE_TAB; goto out; } else { keyval = xkb_keysym_from_name (accelerator, XKB_KEYSYM_CASE_INSENSITIVE); if (keyval == XKB_KEY_NoSymbol) { char *with_xf86 = g_strconcat ("XF86", accelerator, NULL); keyval = xkb_keysym_from_name (with_xf86, XKB_KEYSYM_CASE_INSENSITIVE); g_free (with_xf86); if (keyval == XKB_KEY_NoSymbol) return FALSE; } } accelerator += len; len -= len; } } out: combo->keysym = keyval; combo->keycode = keycode; combo->modifiers = mods; return TRUE; } gboolean meta_parse_accelerator (const char *accel, MetaKeyCombo *combo) { g_return_val_if_fail (combo != NULL, FALSE); *combo = (MetaKeyCombo) { 0 }; if (!accel[0] || strcmp (accel, "disabled") == 0) return TRUE; return accelerator_parse (accel, combo); } gboolean meta_parse_modifier (const char *accel, MetaVirtualModifier *mask) { MetaKeyCombo combo = { 0 }; g_return_val_if_fail (mask != NULL, FALSE); *mask = 0; if (accel == NULL || !accel[0] || strcmp (accel, "disabled") == 0) return TRUE; if (!accelerator_parse (accel, &combo)) return FALSE; *mask = combo.modifiers; return TRUE; } muffin-6.4.1/src/core/meta-selection-source.c0000664000175000017500000001164614723361714020044 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "meta/meta-selection.h" #include "meta/meta-selection-source.h" typedef struct MetaSelectionSourcePrivate MetaSelectionSourcePrivate; struct MetaSelectionSourcePrivate { guint active : 1; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaSelectionSource, meta_selection_source, G_TYPE_OBJECT) enum { ACTIVE, INACTIVE, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; static void meta_selection_source_activated (MetaSelectionSource *source) { MetaSelectionSourcePrivate *priv = meta_selection_source_get_instance_private (source); priv->active = TRUE; } static void meta_selection_source_deactivated (MetaSelectionSource *source) { MetaSelectionSourcePrivate *priv = meta_selection_source_get_instance_private (source); priv->active = FALSE; } static void meta_selection_source_class_init (MetaSelectionSourceClass *klass) { klass->activated = meta_selection_source_activated; klass->deactivated = meta_selection_source_deactivated; signals[ACTIVE] = g_signal_new ("activated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MetaSelectionSourceClass, activated), NULL, NULL, NULL, G_TYPE_NONE, 0); signals[INACTIVE] = g_signal_new ("deactivated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, G_STRUCT_OFFSET (MetaSelectionSourceClass, deactivated), NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_selection_source_init (MetaSelectionSource *source) { } void meta_selection_source_read_async (MetaSelectionSource *source, const gchar *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { g_return_if_fail (META_IS_SELECTION_SOURCE (source)); g_return_if_fail (mimetype != NULL); g_return_if_fail (callback != NULL); META_SELECTION_SOURCE_GET_CLASS (source)->read_async (source, mimetype, cancellable, callback, user_data); } /** * meta_selection_source_read_finish: * @source: The selection source * @result: The async result * @error: Location for returned error * * Finishes a read from the selection source. * * Returns: (transfer full): The resulting #GInputStream */ GInputStream * meta_selection_source_read_finish (MetaSelectionSource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL); g_return_val_if_fail (g_task_is_valid (result, source), NULL); return META_SELECTION_SOURCE_GET_CLASS (source)->read_finish (source, result, error); } /** * meta_selection_source_get_mimetypes: * @source: The selection source * * Returns the list of supported mimetypes. * * Returns: (element-type utf8) (transfer full): The supported mimetypes */ GList * meta_selection_source_get_mimetypes (MetaSelectionSource *source) { g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), NULL); return META_SELECTION_SOURCE_GET_CLASS (source)->get_mimetypes (source); } /** * meta_selection_source_is_active: * @source: the selection source * * Returns #TRUE if the source is active on a selection. * * Returns: #TRUE if the source owns a selection. **/ gboolean meta_selection_source_is_active (MetaSelectionSource *source) { MetaSelectionSourcePrivate *priv = meta_selection_source_get_instance_private (source); g_return_val_if_fail (META_IS_SELECTION_SOURCE (source), FALSE); return priv->active; } muffin-6.4.1/src/core/meta-selection.c0000664000175000017500000002720114723361714016540 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "core/meta-selection-private.h" #include "meta/meta-selection.h" typedef struct TransferRequest TransferRequest; struct _MetaSelection { GObject parent_instance; MetaDisplay *display; MetaSelectionSource *owners[META_N_SELECTION_TYPES]; }; struct TransferRequest { MetaSelectionType selection_type; GInputStream *istream; GOutputStream *ostream; gssize len; }; enum { OWNER_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0 }; G_DEFINE_TYPE (MetaSelection, meta_selection, G_TYPE_OBJECT) static void read_selection_source_async (GTask *task, TransferRequest *request); static void meta_selection_dispose (GObject *object) { MetaSelection *selection = META_SELECTION (object); guint i; for (i = 0; i < META_N_SELECTION_TYPES; i++) { g_clear_object (&selection->owners[i]); } G_OBJECT_CLASS (meta_selection_parent_class)->dispose (object); } static void meta_selection_class_init (MetaSelectionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_selection_dispose; signals[OWNER_CHANGED] = g_signal_new ("owner-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_UINT, META_TYPE_SELECTION_SOURCE); } static void meta_selection_init (MetaSelection *selection) { } MetaSelection * meta_selection_new (MetaDisplay *display) { return g_object_new (META_TYPE_SELECTION, NULL); } /** * meta_selection_set_owner: * @selection: The selection manager * @selection_type: Selection type * @owner: New selection owner * * Sets @owner as the owner of the selection given by @selection_type, * unsets any previous owner there was. **/ void meta_selection_set_owner (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *owner) { g_return_if_fail (META_IS_SELECTION (selection)); g_return_if_fail (selection_type < META_N_SELECTION_TYPES); if (selection->owners[selection_type] == owner) return; if (selection->owners[selection_type]) g_signal_emit_by_name (selection->owners[selection_type], "deactivated"); g_set_object (&selection->owners[selection_type], owner); g_signal_emit_by_name (owner, "activated"); g_signal_emit (selection, signals[OWNER_CHANGED], 0, selection_type, owner); } /** * meta_selection_unset_owner: * @selection: The selection manager * @selection_type: Selection type * @owner: Owner to unset * * Unsets @owner as the owner the selection given by @selection_type. If * @owner does not own the selection, nothing is done. **/ void meta_selection_unset_owner (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *owner) { g_return_if_fail (META_IS_SELECTION (selection)); g_return_if_fail (selection_type < META_N_SELECTION_TYPES); if (selection->owners[selection_type] == owner) { g_signal_emit_by_name (owner, "deactivated"); g_clear_object (&selection->owners[selection_type]); g_signal_emit (selection, signals[OWNER_CHANGED], 0, selection_type, NULL); } } /** * meta_selection_get_mimetypes: * @selection: The selection manager * @selection_type: Selection to query * * Returns the list of supported mimetypes for the given selection type. * * Returns: (element-type utf8) (transfer full): The supported mimetypes */ GList * meta_selection_get_mimetypes (MetaSelection *selection, MetaSelectionType selection_type) { g_return_val_if_fail (META_IS_SELECTION (selection), NULL); g_return_val_if_fail (selection_type < META_N_SELECTION_TYPES, NULL); if (!selection->owners[selection_type]) return NULL; return meta_selection_source_get_mimetypes (selection->owners[selection_type]); } static TransferRequest * transfer_request_new (GOutputStream *ostream, MetaSelectionType selection_type, gssize len) { TransferRequest *request; request = g_new0 (TransferRequest, 1); request->ostream = g_object_ref (ostream); request->selection_type = selection_type; request->len = len; return request; } static void transfer_request_free (TransferRequest *request) { g_clear_object (&request->istream); g_clear_object (&request->ostream); g_free (request); } static void splice_cb (GOutputStream *stream, GAsyncResult *result, GTask *task) { GError *error = NULL; g_output_stream_splice_finish (stream, result, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_task_return_boolean (task, TRUE); g_object_unref (task); } static void write_cb (GOutputStream *stream, GAsyncResult *result, GTask *task) { TransferRequest *request; GError *error = NULL; g_output_stream_write_bytes_finish (stream, result, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } request = g_task_get_task_data (task); if (request->len > 0) { read_selection_source_async (task, request); } else { g_task_return_boolean (task, TRUE); g_object_unref (task); } } static void read_cb (GInputStream *stream, GAsyncResult *result, GTask *task) { TransferRequest *request; GError *error = NULL; GBytes *bytes; bytes = g_input_stream_read_bytes_finish (stream, result, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } else if (g_bytes_get_size (bytes) == 0) { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } request = g_task_get_task_data (task); if (request->len < g_bytes_get_size (bytes)) { GBytes *copy; /* Trim content */ copy = g_bytes_new_from_bytes (bytes, 0, request->len); g_bytes_unref (bytes); bytes = copy; } request->len -= g_bytes_get_size (bytes); g_output_stream_write_bytes_async (request->ostream, bytes, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback) write_cb, task); g_bytes_unref (bytes); } static void read_selection_source_async (GTask *task, TransferRequest *request) { g_input_stream_read_bytes_async (request->istream, (gsize) request->len, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback) read_cb, task); } static void source_read_cb (MetaSelectionSource *source, GAsyncResult *result, GTask *task) { TransferRequest *request; GInputStream *stream; GError *error = NULL; stream = meta_selection_source_read_finish (source, result, &error); if (!stream) { g_task_return_error (task, error); g_object_unref (task); return; } request = g_task_get_task_data (task); request->istream = stream; if (request->len < 0) { g_output_stream_splice_async (request->ostream, request->istream, G_OUTPUT_STREAM_SPLICE_CLOSE_SOURCE | G_OUTPUT_STREAM_SPLICE_CLOSE_TARGET, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback) splice_cb, task); } else { read_selection_source_async (task, request); } } /** * meta_selection_transfer_async: * @selection: The selection manager * @selection_type: Selection type * @mimetype: Mimetype to transfer * @size: Maximum size to transfer, -1 for unlimited * @output: Output stream to write contents to * @cancellable: Cancellable * @callback: User callback * @user_data: User data * * Requests a transfer of @mimetype on the selection given by * @selection_type. **/ void meta_selection_transfer_async (MetaSelection *selection, MetaSelectionType selection_type, const char *mimetype, gssize size, GOutputStream *output, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; g_return_if_fail (META_IS_SELECTION (selection)); g_return_if_fail (selection_type < META_N_SELECTION_TYPES); g_return_if_fail (G_IS_OUTPUT_STREAM (output)); g_return_if_fail (mimetype != NULL); task = g_task_new (selection, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_transfer_async); g_task_set_task_data (task, transfer_request_new (output, selection_type, size), (GDestroyNotify) transfer_request_free); meta_selection_source_read_async (selection->owners[selection_type], mimetype, cancellable, (GAsyncReadyCallback) source_read_cb, task); } /** * meta_selection_transfer_finish: * @selection: The selection manager * @result: The async result * @error: Location for returned error, or %NULL * * Finishes the transfer of a queried mimetype. * * Returns: #TRUE if the transfer was successful. **/ gboolean meta_selection_transfer_finish (MetaSelection *selection, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, selection), FALSE); g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == meta_selection_transfer_async, FALSE); return g_task_propagate_boolean (G_TASK (result), error); } MetaSelectionSource * meta_selection_get_current_owner (MetaSelection *selection, MetaSelectionType selection_type) { g_return_val_if_fail (META_IS_SELECTION (selection), NULL); g_return_val_if_fail (selection_type < META_N_SELECTION_TYPES, NULL); return selection->owners[selection_type]; } muffin-6.4.1/src/core/stack.c0000664000175000017500000010105414723361714014733 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * SECTION:stack * @short_description: Which windows cover which other windows */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2004 Rob Adams * Copyright (C) 2004, 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/stack.h" #include "backends/meta-logical-monitor.h" #include "cogl/cogl.h" #include "core/frame.h" #include "core/main-private.h" #include "core/meta-workspace-manager-private.h" #include "core/window-private.h" #include "meta/group.h" #include "meta/prefs.h" #include "meta/workspace.h" #include "x11/meta-x11-display-private.h" #define WINDOW_TRANSIENT_FOR_WHOLE_GROUP(w) \ (meta_window_has_transient_type (w) && w->transient_for == NULL) static void meta_window_set_stack_position_no_sync (MetaWindow *window, int position); static void stack_do_relayer (MetaStack *stack); static void stack_do_constrain (MetaStack *stack); static void stack_do_resort (MetaStack *stack); static void stack_ensure_sorted (MetaStack *stack); enum { PROP_DISPLAY = 1, N_PROPS }; enum { CHANGED, WINDOW_ADDED, WINDOW_REMOVED, N_SIGNALS }; static GParamSpec *pspecs[N_PROPS] = { 0 }; static guint signals[N_SIGNALS] = { 0 }; G_DEFINE_TYPE (MetaStack, meta_stack, G_TYPE_OBJECT) static void on_stack_changed (MetaStack *stack) { MetaDisplay *display = stack->display; GArray *all_root_children_stacked; GList *l; GArray *hidden_stack_ids; GList *sorted; meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); all_root_children_stacked = g_array_new (FALSE, FALSE, sizeof (uint64_t)); hidden_stack_ids = g_array_new (FALSE, FALSE, sizeof (uint64_t)); meta_topic (META_DEBUG_STACK, "Bottom to top: "); meta_push_no_msg_prefix (); sorted = meta_stack_list_windows (stack, NULL); for (l = sorted; l; l = l->next) { MetaWindow *w = l->data; uint64_t top_level_window; uint64_t stack_id; if (w->unmanaging) continue; meta_topic (META_DEBUG_STACK, "%u:%d - %s ", w->layer, w->stack_position, w->desc); if (w->frame) top_level_window = w->frame->xwindow; else top_level_window = w->xwindow; if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) stack_id = top_level_window; else stack_id = w->stamp; /* We don't restack hidden windows along with the rest, though they are * reflected in the _NET hints. Hidden windows all get pushed below * the screens fullscreen guard_window. */ if (w->hidden) { g_array_append_val (hidden_stack_ids, stack_id); continue; } g_array_append_val (all_root_children_stacked, stack_id); } meta_topic (META_DEBUG_STACK, "\n"); meta_pop_no_msg_prefix (); if (display->x11_display) { uint64_t guard_window_id; /* The screen guard window sits above all hidden windows and acts as * a barrier to input reaching these windows. */ guard_window_id = display->x11_display->guard_window; g_array_append_val (hidden_stack_ids, guard_window_id); } /* Sync to server */ meta_topic (META_DEBUG_STACK, "Restacking %u windows\n", all_root_children_stacked->len); meta_stack_tracker_restack_managed (display->stack_tracker, (uint64_t *)all_root_children_stacked->data, all_root_children_stacked->len); meta_stack_tracker_restack_at_bottom (display->stack_tracker, (uint64_t *)hidden_stack_ids->data, hidden_stack_ids->len); g_array_free (hidden_stack_ids, TRUE); g_array_free (all_root_children_stacked, TRUE); g_list_free (sorted); } static void meta_stack_init (MetaStack *stack) { g_signal_connect (stack, "changed", G_CALLBACK (on_stack_changed), NULL); } static void meta_stack_finalize (GObject *object) { MetaStack *stack = META_STACK (object); g_list_free (stack->sorted); G_OBJECT_CLASS (meta_stack_parent_class)->finalize (object); } static void meta_stack_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStack *stack = META_STACK (object); switch (prop_id) { case PROP_DISPLAY: stack->display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_stack_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStack *stack = META_STACK (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, stack->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_stack_class_init (MetaStackClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_stack_set_property; object_class->get_property = meta_stack_get_property; object_class->finalize = meta_stack_finalize; signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[WINDOW_ADDED] = g_signal_new ("window-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW); signals[WINDOW_REMOVED] = g_signal_new ("window-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, META_TYPE_WINDOW); pspecs[PROP_DISPLAY] = g_param_spec_object ("display", "Display", "Display", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, pspecs); } MetaStack * meta_stack_new (MetaDisplay *display) { return g_object_new (META_TYPE_STACK, "display", display, NULL); } static void meta_stack_changed (MetaStack *stack) { /* Bail out if frozen */ if (stack->freeze_count > 0) return; stack_ensure_sorted (stack); g_signal_emit (stack, signals[CHANGED], 0); } void meta_stack_add (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; COGL_TRACE_BEGIN_SCOPED (MetaStackAdd, "Stack (add window)"); g_return_if_fail (meta_window_is_stackable (window)); meta_topic (META_DEBUG_STACK, "Adding window %s to the stack\n", window->desc); if (meta_window_is_in_stack (window)) meta_bug ("Window %s had stack position already\n", window->desc); stack->sorted = g_list_prepend (stack->sorted, window); stack->need_resort = TRUE; /* may not be needed as we add to top */ stack->need_constrain = TRUE; stack->need_relayer = TRUE; g_signal_emit (stack, signals[WINDOW_ADDED], 0, window); window->stack_position = stack->n_positions; stack->n_positions += 1; meta_topic (META_DEBUG_STACK, "Window %s has stack_position initialized to %d\n", window->desc, window->stack_position); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } void meta_stack_remove (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; COGL_TRACE_BEGIN_SCOPED (MetaStackRemove, "Stack (remove window)"); meta_topic (META_DEBUG_STACK, "Removing window %s from the stack\n", window->desc); /* Set window to top position, so removing it will not leave gaps * in the set of positions */ meta_window_set_stack_position_no_sync (window, stack->n_positions - 1); window->stack_position = -1; stack->n_positions -= 1; stack->sorted = g_list_remove (stack->sorted, window); g_signal_emit (stack, signals[WINDOW_REMOVED], 0, window); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } void meta_stack_update_layer (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; stack->need_relayer = TRUE; meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } void meta_stack_update_transient (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; stack->need_constrain = TRUE; meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } /* raise/lower within a layer */ void meta_stack_raise (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; GList *l; int max_stack_position = window->stack_position; MetaWorkspace *workspace; stack_ensure_sorted (stack); workspace = meta_window_get_workspace (window); for (l = stack->sorted; l; l = l->next) { MetaWindow *w = (MetaWindow *) l->data; if (meta_window_located_on_workspace (w, workspace) && w->stack_position > max_stack_position) max_stack_position = w->stack_position; } if (max_stack_position == window->stack_position) return; meta_window_set_stack_position_no_sync (window, max_stack_position); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } void meta_stack_lower (MetaStack *stack, MetaWindow *window) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; GList *l; int min_stack_position = window->stack_position; MetaWorkspace *workspace; stack_ensure_sorted (stack); workspace = meta_window_get_workspace (window); for (l = stack->sorted; l; l = l->next) { MetaWindow *w = (MetaWindow *) l->data; if (meta_window_located_on_workspace (w, workspace) && w->stack_position < min_stack_position) min_stack_position = w->stack_position; } if (min_stack_position == window->stack_position) return; meta_window_set_stack_position_no_sync (window, min_stack_position); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, workspace_manager->active_workspace); } void meta_stack_freeze (MetaStack *stack) { stack->freeze_count += 1; } void meta_stack_thaw (MetaStack *stack) { g_return_if_fail (stack->freeze_count > 0); stack->freeze_count -= 1; meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, NULL); } void meta_stack_update_window_tile_matches (MetaStack *stack, MetaWorkspace *workspace) { GList *windows, *tmp; if (stack->freeze_count > 0) return; windows = meta_stack_list_windows (stack, workspace); tmp = windows; while (tmp) { meta_window_compute_tile_matches ((MetaWindow *) tmp->data); tmp = tmp->next; } g_list_free (windows); } /* Front of the layer list is the topmost window, * so the lower stack position is later in the list */ static int compare_window_position (void *a, void *b) { MetaWindow *window_a = a; MetaWindow *window_b = b; /* Go by layer, then stack_position */ if (window_a->layer < window_b->layer) return 1; /* move window_a later in list */ else if (window_a->layer > window_b->layer) return -1; else if (window_a->stack_position < window_b->stack_position) return 1; /* move window_a later in list */ else if (window_a->stack_position > window_b->stack_position) return -1; else return 0; /* not reached */ } /* * Stacking constraints * * Assume constraints of the form "AB" meaning "window A must be * below window B" * * If we have windows stacked from bottom to top * "ABC" then raise A we get "BCA". Say C is * transient for B is transient for A. So * we have constraints AB and BC. * * After raising A, we need to reapply the constraints. * If we do this by raising one window at a time - * * start: BCA * apply AB: CAB * apply BC: ABC * * but apply constraints in the wrong order and it breaks: * * start: BCA * apply BC: BCA * apply AB: CAB * * We make a directed graph of the constraints by linking * from "above windows" to "below windows as follows: * * AB -> BC -> CD * \ * CE * * If we then walk that graph and apply the constraints in the order * that they appear, we will apply them correctly. Note that the * graph MAY have cycles, so we have to guard against that. * */ typedef struct Constraint Constraint; struct Constraint { MetaWindow *above; MetaWindow *below; /* used to keep the constraint in the * list of constraints for window "below" */ Constraint *next; /* used to create the graph. */ GSList *next_nodes; /* constraint has been applied, used * to detect cycles. */ unsigned int applied : 1; /* constraint has a previous node in the graph, * used to find places to start in the graph. * (I think this also has the side effect * of preventing cycles, since cycles will * have no starting point - so maybe * the "applied" flag isn't needed.) */ unsigned int has_prev : 1; }; /* We index the array of constraints by window * stack positions, just because the stack * positions are a convenient index. */ static void add_constraint (Constraint **constraints, MetaWindow *above, MetaWindow *below) { Constraint *c; /* check if constraint is a duplicate */ c = constraints[below->stack_position]; while (c != NULL) { if (c->above == above) return; c = c->next; } /* if not, add the constraint */ c = g_new (Constraint, 1); c->above = above; c->below = below; c->next = constraints[below->stack_position]; c->next_nodes = NULL; c->applied = FALSE; c->has_prev = FALSE; constraints[below->stack_position] = c; } static void create_constraints (Constraint **constraints, GList *windows) { GList *tmp; tmp = windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (!meta_window_is_in_stack (w)) { meta_topic (META_DEBUG_STACK, "Window %s not in the stack, not constraining it\n", w->desc); tmp = tmp->next; continue; } if (WINDOW_TRANSIENT_FOR_WHOLE_GROUP (w)) { GSList *group_windows; GSList *tmp2; MetaGroup *group; group = meta_window_get_group (w); if (group != NULL) group_windows = meta_group_list_windows (group); else group_windows = NULL; tmp2 = group_windows; while (tmp2 != NULL) { MetaWindow *group_window = tmp2->data; if (!meta_window_is_in_stack (group_window) || group_window->override_redirect) { tmp2 = tmp2->next; continue; } #if 0 /* old way of doing it */ if (!(meta_window_is_ancestor_of_transient (w, group_window)) && !WINDOW_TRANSIENT_FOR_WHOLE_GROUP (group_window)) /* note */;/*note*/ #else /* better way I think, so transient-for-group are constrained * only above non-transient-type windows in their group */ if (!meta_window_has_transient_type (group_window)) #endif { meta_topic (META_DEBUG_STACK, "Constraining %s above %s as it's transient for its group\n", w->desc, group_window->desc); add_constraint (constraints, w, group_window); } tmp2 = tmp2->next; } g_slist_free (group_windows); } else if (w->transient_for != NULL) { MetaWindow *parent; parent = w->transient_for; if (parent && meta_window_is_in_stack (parent)) { meta_topic (META_DEBUG_STACK, "Constraining %s above %s due to transiency\n", w->desc, parent->desc); add_constraint (constraints, w, parent); } } tmp = tmp->next; } } static void graph_constraints (Constraint **constraints, int n_constraints) { int i; i = 0; while (i < n_constraints) { Constraint *c; /* If we have "A below B" and "B below C" then AB -> BC so we * add BC to next_nodes in AB. */ c = constraints[i]; while (c != NULL) { Constraint *n; g_assert (c->below->stack_position == i); /* Constraints where ->above is below are our * next_nodes and we are their previous */ n = constraints[c->above->stack_position]; while (n != NULL) { c->next_nodes = g_slist_prepend (c->next_nodes, n); /* c is a previous node of n */ n->has_prev = TRUE; n = n->next; } c = c->next; } ++i; } } static void free_constraints (Constraint **constraints, int n_constraints) { int i; i = 0; while (i < n_constraints) { Constraint *c; c = constraints[i]; while (c != NULL) { Constraint *next = c->next; g_slist_free (c->next_nodes); g_free (c); c = next; } ++i; } } static void ensure_above (MetaWindow *above, MetaWindow *below) { gboolean is_transient; is_transient = meta_window_has_transient_type (above) || above->transient_for == below; if (is_transient && above->layer < below->layer) { meta_topic (META_DEBUG_STACK, "Promoting window %s from layer %u to %u due to contraint\n", above->desc, above->layer, below->layer); above->layer = below->layer; } if (above->stack_position < below->stack_position) { /* move above to below->stack_position bumping below down the stack */ meta_window_set_stack_position_no_sync (above, below->stack_position); g_assert (below->stack_position + 1 == above->stack_position); } meta_topic (META_DEBUG_STACK, "%s above at %d > %s below at %d\n", above->desc, above->stack_position, below->desc, below->stack_position); } static void traverse_constraint (Constraint *c) { GSList *tmp; if (c->applied) return; ensure_above (c->above, c->below); c->applied = TRUE; tmp = c->next_nodes; while (tmp != NULL) { traverse_constraint (tmp->data); tmp = tmp->next; } } static void apply_constraints (Constraint **constraints, int n_constraints) { GSList *heads; GSList *tmp; int i; /* List all heads in an ordered constraint chain */ heads = NULL; i = 0; while (i < n_constraints) { Constraint *c; c = constraints[i]; while (c != NULL) { if (!c->has_prev) heads = g_slist_prepend (heads, c); c = c->next; } ++i; } /* Now traverse the chain and apply constraints */ tmp = heads; while (tmp != NULL) { Constraint *c = tmp->data; traverse_constraint (c); tmp = tmp->next; } g_slist_free (heads); } /** * stack_do_relayer: * * Update the layers that windows are in */ static void stack_do_relayer (MetaStack *stack) { GList *tmp; if (!stack->need_relayer) return; meta_topic (META_DEBUG_STACK, "Recomputing layers\n"); tmp = stack->sorted; while (tmp != NULL) { MetaWindow *w; MetaStackLayer old_layer; w = tmp->data; old_layer = w->layer; w->layer = meta_window_calculate_layer (w); if (w->layer != old_layer) { meta_topic (META_DEBUG_STACK, "Window %s moved from layer %u to %u\n", w->desc, old_layer, w->layer); stack->need_resort = TRUE; stack->need_constrain = TRUE; /* don't need to constrain as constraining * purely operates in terms of stack_position * not layer */ } tmp = tmp->next; } stack->need_relayer = FALSE; } /** * stack_do_constrain: * * Update stack_position and layer to reflect transiency * constraints */ static void stack_do_constrain (MetaStack *stack) { Constraint **constraints; /* It'd be nice if this were all faster, probably */ if (!stack->need_constrain) return; meta_topic (META_DEBUG_STACK, "Reapplying constraints\n"); constraints = g_new0 (Constraint*, stack->n_positions); create_constraints (constraints, stack->sorted); graph_constraints (constraints, stack->n_positions); apply_constraints (constraints, stack->n_positions); free_constraints (constraints, stack->n_positions); g_free (constraints); stack->need_constrain = FALSE; } /** * stack_do_resort: * * Sort stack->sorted with layers having priority over stack_position. */ static void stack_do_resort (MetaStack *stack) { if (!stack->need_resort) return; meta_topic (META_DEBUG_STACK, "Sorting stack list\n"); stack->sorted = g_list_sort (stack->sorted, (GCompareFunc) compare_window_position); meta_display_queue_check_fullscreen (stack->display); stack->need_resort = FALSE; } /** * stack_ensure_sorted: * * Puts the stack into canonical form. * * Honour the removed and added lists of the stack, and then recalculate * all the layers (if the flag is set), re-run all the constraint calculations * (if the flag is set), and finally re-sort the stack (if the flag is set, * and if it wasn't already it might have become so during all the previous * activity). */ static void stack_ensure_sorted (MetaStack *stack) { stack_do_relayer (stack); stack_do_constrain (stack); stack_do_resort (stack); } MetaWindow * meta_stack_get_top (MetaStack *stack) { stack_ensure_sorted (stack); if (stack->sorted) return stack->sorted->data; else return NULL; } MetaWindow * meta_stack_get_bottom (MetaStack *stack) { GList *link; stack_ensure_sorted (stack); link = g_list_last (stack->sorted); if (link != NULL) return link->data; else return NULL; } MetaWindow * meta_stack_get_above (MetaStack *stack, MetaWindow *window, gboolean only_within_layer) { GList *link; MetaWindow *above; stack_ensure_sorted (stack); link = g_list_find (stack->sorted, window); if (link == NULL) return NULL; if (link->prev == NULL) return NULL; above = link->prev->data; if (only_within_layer && above->layer != window->layer) return NULL; else return above; } MetaWindow * meta_stack_get_below (MetaStack *stack, MetaWindow *window, gboolean only_within_layer) { GList *link; MetaWindow *below; stack_ensure_sorted (stack); link = g_list_find (stack->sorted, window); if (link == NULL) return NULL; if (link->next == NULL) return NULL; below = link->next->data; if (only_within_layer && below->layer != window->layer) return NULL; else return below; } static gboolean window_contains_point (MetaWindow *window, int root_x, int root_y) { MetaRectangle rect; meta_window_get_frame_rect (window, &rect); return META_POINT_IN_RECT (root_x, root_y, rect); } static gboolean window_can_get_default_focus (MetaWindow *window) { if (window->unmaps_pending > 0) return FALSE; if (window->unmanaging) return FALSE; if (!meta_window_is_focusable (window)) return FALSE; if (!meta_window_should_be_showing (window)) return FALSE; if (window->type == META_WINDOW_DOCK) return FALSE; return TRUE; } static MetaWindow * get_default_focus_window (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one, gboolean must_be_at_point, int root_x, int root_y) { /* Find the topmost, focusable, mapped, window. * not_this_one is being unfocused or going away, so exclude it. */ GList *l; stack_ensure_sorted (stack); /* top of this layer is at the front of the list */ for (l = stack->sorted; l != NULL; l = l->next) { MetaWindow *window = l->data; if (!window) continue; if (window == not_this_one) continue; if (!window_can_get_default_focus (window)) continue; if (must_be_at_point && !window_contains_point (window, root_x, root_y)) continue; return window; } return NULL; } MetaWindow * meta_stack_get_default_focus_window_at_point (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one, int root_x, int root_y) { return get_default_focus_window (stack, workspace, not_this_one, TRUE, root_x, root_y); } MetaWindow * meta_stack_get_default_focus_window (MetaStack *stack, MetaWorkspace *workspace, MetaWindow *not_this_one) { return get_default_focus_window (stack, workspace, not_this_one, FALSE, 0, 0); } GList * meta_stack_list_windows (MetaStack *stack, MetaWorkspace *workspace) { GList *workspace_windows = NULL; GList *link; stack_ensure_sorted (stack); /* do adds/removes */ link = stack->sorted; while (link) { MetaWindow *window = link->data; if (window && (workspace == NULL || meta_window_located_on_workspace (window, workspace))) { workspace_windows = g_list_prepend (workspace_windows, window); } link = link->next; } return workspace_windows; } GList * meta_stack_get_default_focus_candidates (MetaStack *stack, MetaWorkspace *workspace) { GList *windows = meta_stack_list_windows (stack, workspace); GList *l; for (l = windows; l;) { GList *next = l->next; if (!window_can_get_default_focus (l->data)) windows = g_list_delete_link (windows, l); l = next; } return windows; } int meta_stack_windows_cmp (MetaStack *stack, MetaWindow *window_a, MetaWindow *window_b) { /* -1 means a below b */ stack_ensure_sorted (stack); /* update constraints, layers */ if (window_a->layer < window_b->layer) return -1; else if (window_a->layer > window_b->layer) return 1; else if (window_a->stack_position < window_b->stack_position) return -1; else if (window_a->stack_position > window_b->stack_position) return 1; else return 0; /* not reached */ } static int compare_just_window_stack_position (void *a, void *b) { MetaWindow *window_a = a; MetaWindow *window_b = b; if (window_a->stack_position < window_b->stack_position) return -1; /* move window_a earlier in list */ else if (window_a->stack_position > window_b->stack_position) return 1; else return 0; /* not reached */ } GList * meta_stack_get_positions (MetaStack *stack) { GList *tmp; /* Make sure to handle any adds or removes */ stack_ensure_sorted (stack); tmp = g_list_copy (stack->sorted); tmp = g_list_sort (tmp, (GCompareFunc) compare_just_window_stack_position); return tmp; } static gint compare_pointers (gconstpointer a, gconstpointer b) { if (a > b) return 1; else if (a < b) return -1; else return 0; } static gboolean lists_contain_same_windows (GList *a, GList *b) { GList *copy1, *copy2; GList *tmp1, *tmp2; if (g_list_length (a) != g_list_length (b)) return FALSE; tmp1 = copy1 = g_list_sort (g_list_copy (a), compare_pointers); tmp2 = copy2 = g_list_sort (g_list_copy (b), compare_pointers); while (tmp1 && tmp1->data == tmp2->data) /* tmp2 is non-NULL if tmp1 is */ { tmp1 = tmp1->next; tmp2 = tmp2->next; } g_list_free (copy1); g_list_free (copy2); return (tmp1 == NULL); /* tmp2 is non-NULL if tmp1 is */ } void meta_stack_set_positions (MetaStack *stack, GList *windows) { int i; GList *tmp; /* Make sure any adds or removes aren't in limbo -- is this needed? */ stack_ensure_sorted (stack); if (!lists_contain_same_windows (windows, stack->sorted)) { meta_warning ("This list of windows has somehow changed; not resetting " "positions of the windows.\n"); return; } g_list_free (stack->sorted); stack->sorted = g_list_copy (windows); stack->need_resort = TRUE; stack->need_constrain = TRUE; i = 0; tmp = windows; while (tmp != NULL) { MetaWindow *w = tmp->data; w->stack_position = i++; tmp = tmp->next; } meta_topic (META_DEBUG_STACK, "Reset the stack positions of (nearly) all windows\n"); meta_stack_changed (stack); meta_stack_update_window_tile_matches (stack, NULL); } void meta_window_set_stack_position_no_sync (MetaWindow *window, int position) { int low, high, delta; GList *tmp; g_return_if_fail (window->display->stack != NULL); g_return_if_fail (window->stack_position >= 0); g_return_if_fail (position >= 0); g_return_if_fail (position < window->display->stack->n_positions); if (position == window->stack_position) { meta_topic (META_DEBUG_STACK, "Window %s already has position %d\n", window->desc, position); return; } window->display->stack->need_resort = TRUE; window->display->stack->need_constrain = TRUE; if (position < window->stack_position) { low = position; high = window->stack_position - 1; delta = 1; } else { low = window->stack_position + 1; high = position; delta = -1; } tmp = window->display->stack->sorted; while (tmp != NULL) { MetaWindow *w = tmp->data; if (w->stack_position >= low && w->stack_position <= high) w->stack_position += delta; tmp = tmp->next; } window->stack_position = position; meta_topic (META_DEBUG_STACK, "Window %s had stack_position set to %d\n", window->desc, window->stack_position); } void meta_window_set_stack_position (MetaWindow *window, int position) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; meta_window_set_stack_position_no_sync (window, position); meta_stack_changed (window->display->stack); meta_stack_update_window_tile_matches (window->display->stack, workspace_manager->active_workspace); } muffin-6.4.1/src/core/meta-selection-source-memory.c0000664000175000017500000001036714723361714021351 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "meta/meta-selection-source-memory.h" struct _MetaSelectionSourceMemory { MetaSelectionSource parent_instance; char *mimetype; GBytes *content; }; G_DEFINE_TYPE (MetaSelectionSourceMemory, meta_selection_source_memory, META_TYPE_SELECTION_SOURCE) static void meta_selection_source_memory_read_async (MetaSelectionSource *source, const char *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (source); GInputStream *stream; g_autoptr (GTask) task = NULL; if (g_strcmp0 (mimetype, source_mem->mimetype) != 0) { g_task_report_new_error (source, callback, user_data, meta_selection_source_memory_read_async, G_IO_ERROR, G_IO_ERROR_FAILED, "Mimetype not in selection"); return; } task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_source_memory_read_async); stream = g_memory_input_stream_new_from_bytes (source_mem->content); g_task_return_pointer (task, stream, g_object_unref); } static GInputStream * meta_selection_source_memory_read_finish (MetaSelectionSource *source, GAsyncResult *result, GError **error) { g_assert (g_task_get_source_tag (G_TASK (result)) == meta_selection_source_memory_read_async); return g_task_propagate_pointer (G_TASK (result), error); } static GList * meta_selection_source_memory_get_mimetypes (MetaSelectionSource *source) { MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (source); if (!source_mem->mimetype) return NULL; return g_list_prepend (NULL, g_strdup (source_mem->mimetype)); } static void meta_selection_source_memory_finalize (GObject *object) { MetaSelectionSourceMemory *source_mem = META_SELECTION_SOURCE_MEMORY (object); g_clear_pointer (&source_mem->content, g_bytes_unref); g_free (source_mem->mimetype); G_OBJECT_CLASS (meta_selection_source_memory_parent_class)->finalize (object); } static void meta_selection_source_memory_class_init (MetaSelectionSourceMemoryClass *klass) { MetaSelectionSourceClass *source_class = META_SELECTION_SOURCE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_selection_source_memory_finalize; source_class->read_async = meta_selection_source_memory_read_async; source_class->read_finish = meta_selection_source_memory_read_finish; source_class->get_mimetypes = meta_selection_source_memory_get_mimetypes; } static void meta_selection_source_memory_init (MetaSelectionSourceMemory *source) { } MetaSelectionSource * meta_selection_source_memory_new (const char *mimetype, GBytes *content) { MetaSelectionSourceMemory *source; g_return_val_if_fail (mimetype != NULL, NULL); g_return_val_if_fail (content != NULL, NULL); source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); source->mimetype = g_strdup (mimetype); source->content = g_bytes_ref (content); return META_SELECTION_SOURCE (source); } muffin-6.4.1/src/core/bell.c0000664000175000017500000001413414723361714014546 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter visual bell */ /* * Copyright (C) 2002 Sun Microsystems Inc. * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* * SECTION:bell * @short_description: Ring the bell or flash the screen * * Sometimes, X programs "ring the bell", whatever that means. Mutter lets * the user configure the bell to be audible or visible (aka visual), and * if it's visual it can be configured to be frame-flash or fullscreen-flash. * We never get told about audible bells; X handles them just fine by itself. * * Visual bells come in at meta_bell_notify(), which checks we are actually * in visual mode and calls through to bell_visual_notify(). That * function then checks what kind of visual flash you like, and calls either * bell_flash_fullscreen()-- which calls bell_flash_screen() to do * its work-- or bell_flash_frame(), which flashes the focussed window * using bell_flash_window(), unless there is no such window, in * which case it flashes the screen instead. * * The visual bell was the result of a discussion in Bugzilla here: * . * * Several of the functions in this file are ifdeffed out entirely if we are * found not to have the XKB extension, which is required to do these clever * things with bells; some others are entirely no-ops in that case. */ #include "config.h" #include "core/bell.h" #include "compositor/compositor-private.h" #include "core/util-private.h" #include "core/window-private.h" #include "meta/compositor.h" G_DEFINE_TYPE (MetaBell, meta_bell, G_TYPE_OBJECT) enum { IS_AUDIBLE_CHANGED, LAST_SIGNAL }; static guint bell_signals [LAST_SIGNAL] = { 0 }; static void prefs_changed_callback (MetaPreference pref, gpointer data) { MetaBell *bell = data; if (pref == META_PREF_AUDIBLE_BELL) { g_signal_emit (bell, bell_signals[IS_AUDIBLE_CHANGED], 0, meta_prefs_bell_is_audible ()); } else if (pref == META_PREF_BELL_SOUND) { g_clear_object (&bell->bell_sound_file); bell->bell_sound_file = g_file_new_for_path (meta_prefs_get_bell_sound ()); } } static void meta_bell_finalize (GObject *object) { MetaBell *bell = META_BELL (object); meta_prefs_remove_listener (prefs_changed_callback, bell); g_clear_object (&bell->bell_sound_file); G_OBJECT_CLASS (meta_bell_parent_class)->finalize (object); } static void meta_bell_class_init (MetaBellClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_bell_finalize; bell_signals[IS_AUDIBLE_CHANGED] = g_signal_new ("is-audible-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } static void meta_bell_init (MetaBell *bell) { meta_prefs_add_listener (prefs_changed_callback, bell); bell->bell_sound_file = g_file_new_for_path (meta_prefs_get_bell_sound ()); } MetaBell * meta_bell_new (MetaDisplay *display) { return g_object_new (META_TYPE_BELL, NULL); } /** * bell_flash_fullscreen: * @display: The display the event came in on * @xkb_ev: The bell event * * Flashes one screen, or all screens, in response to a bell event. * If the event is on a particular window, flash the screen that * window is on. Otherwise, flash every screen on this display. * * If the configure script found we had no XKB, this does not exist. */ static void bell_flash_fullscreen (MetaDisplay *display) { meta_compositor_flash_display (display->compositor, display); } static void bell_flash_window (MetaWindow *window) { meta_compositor_flash_window (window->display->compositor, window); } /** * bell_flash_frame: * @display: The display the bell event came in on * @xkb_ev: The bell event we just received * * Flashes the frame of the focused window. If there is no focused window, * flashes the screen. */ static void bell_flash_frame (MetaDisplay *display, MetaWindow *window) { if (window) bell_flash_window (window); else bell_flash_fullscreen (display); } /** * bell_visual_notify: * @display: The display the bell event came in on * @xkb_ev: The bell event we just received * * Gives the user some kind of visual bell substitute, in response to a * bell event. What this is depends on the "visual bell type" pref. */ static void bell_visual_notify (MetaDisplay *display, MetaWindow *window) { switch (meta_prefs_get_visual_bell_type ()) { case C_DESKTOP_VISUAL_BELL_FULLSCREEN_FLASH: bell_flash_fullscreen (display); break; case C_DESKTOP_VISUAL_BELL_FRAME_FLASH: bell_flash_frame (display, window); break; } } static gboolean bell_audible_notify (MetaDisplay *display, MetaWindow *window) { MetaSoundPlayer *player; player = meta_display_get_sound_player (display); meta_sound_player_play_from_file (player, display->bell->bell_sound_file, _("Bell event"), NULL); return TRUE; } gboolean meta_bell_notify (MetaDisplay *display, MetaWindow *window) { /* flash something */ if (meta_prefs_get_visual_bell ()) bell_visual_notify (display, window); if (meta_prefs_bell_is_audible ()) return bell_audible_notify (display, window); return TRUE; } muffin-6.4.1/src/core/frame.h0000664000175000017500000000503514723361714014727 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X window decorations */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_FRAME_PRIVATE_H #define META_FRAME_PRIVATE_H #include "core/window-private.h" #include "ui/frames.h" struct _MetaFrame { /* window we frame */ MetaWindow *window; /* reparent window */ Window xwindow; MetaCursor current_cursor; /* This rect is trusted info from where we put the * frame, not the result of ConfigureNotify */ MetaRectangle rect; MetaFrameBorders cached_borders; /* valid if borders_cached is set */ /* position of client, size of frame */ int child_x; int child_y; int right_width; int bottom_height; guint need_reapply_frame_shape : 1; guint borders_cached : 1; MetaUIFrame *ui_frame; }; void meta_window_ensure_frame (MetaWindow *window); void meta_window_destroy_frame (MetaWindow *window); void meta_frame_queue_draw (MetaFrame *frame); MetaFrameFlags meta_frame_get_flags (MetaFrame *frame); Window meta_frame_get_xwindow (MetaFrame *frame); /* These should ONLY be called from meta_window_move_resize_internal */ void meta_frame_calc_borders (MetaFrame *frame, MetaFrameBorders *borders); gboolean meta_frame_sync_to_window (MetaFrame *frame, gboolean need_resize); void meta_frame_clear_cached_borders (MetaFrame *frame); cairo_region_t *meta_frame_get_frame_bounds (MetaFrame *frame); void meta_frame_get_mask (MetaFrame *frame, cairo_rectangle_int_t *frame_rect, cairo_t *cr); void meta_frame_set_screen_cursor (MetaFrame *frame, MetaCursor cursor); void meta_frame_update_style (MetaFrame *frame); void meta_frame_update_title (MetaFrame *frame); #endif muffin-6.4.1/src/core/bell.h0000664000175000017500000000275014723361714014554 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2002 Sun Microsystems Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "core/display-private.h" #include "core/frame.h" struct _MetaBell { GObject parent; GFile *bell_sound_file; }; #define META_TYPE_BELL (meta_bell_get_type ()) G_DECLARE_FINAL_TYPE (MetaBell, meta_bell, META, BELL, GObject) MetaBell * meta_bell_new (MetaDisplay *display); /** * meta_bell_notify: * @display: The display the bell event came in on * @window: The window the bell event was received on * * Gives the user some kind of aural or visual feedback, such as a bell sound * or flash. What type of feedback is invoked depends on the configuration. * If the aural feedback could not be invoked, FALSE is returned. */ gboolean meta_bell_notify (MetaDisplay *display, MetaWindow *window); muffin-6.4.1/src/core/meta-clipboard-manager.c0000664000175000017500000001317014723361714020122 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "core/meta-clipboard-manager.h" #include "meta/meta-selection-source-memory.h" #define MAX_TEXT_SIZE (4 * 1024 * 1024) /* 4MB */ #define MAX_IMAGE_SIZE (200 * 1024 * 1024) /* 200MB */ /* Supported mimetype globs, from least to most preferred */ static struct { const char *mimetype_glob; ssize_t max_transfer_size; } supported_mimetypes[] = { { "image/tiff", MAX_IMAGE_SIZE }, { "image/bmp", MAX_IMAGE_SIZE }, { "image/gif", MAX_IMAGE_SIZE }, { "image/jpeg", MAX_IMAGE_SIZE }, { "image/webp", MAX_IMAGE_SIZE }, { "image/png", MAX_IMAGE_SIZE }, { "image/svg+xml", MAX_IMAGE_SIZE }, { "text/plain", MAX_TEXT_SIZE }, { "text/plain;charset=utf-8", MAX_TEXT_SIZE }, }; static gboolean mimetype_match (const char *mimetype, int *idx, gssize *max_transfer_size) { int i; for (i = 0; i < G_N_ELEMENTS (supported_mimetypes); i++) { if (g_pattern_match_simple (supported_mimetypes[i].mimetype_glob, mimetype)) { *max_transfer_size = supported_mimetypes[i].max_transfer_size; *idx = i; return TRUE; } } return FALSE; } static void transfer_cb (MetaSelection *selection, GAsyncResult *result, GOutputStream *output) { MetaDisplay *display = meta_get_display (); GError *error = NULL; if (!meta_selection_transfer_finish (selection, result, &error)) { g_warning ("Failed to store clipboard: %s", error->message); g_error_free (error); g_object_unref (output); return; } g_output_stream_close (output, NULL, NULL); display->saved_clipboard = g_memory_output_stream_steal_as_bytes (G_MEMORY_OUTPUT_STREAM (output)); g_object_unref (output); } static void owner_changed_cb (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *new_owner, MetaDisplay *display) { if (selection_type != META_SELECTION_CLIPBOARD) return; if (new_owner && new_owner != display->selection_source) { GOutputStream *output; GList *mimetypes, *l; int best_idx = -1; const char *best = NULL; ssize_t transfer_size = -1; /* New selection source, find the best mimetype in order to * keep a copy of it. */ g_clear_object (&display->selection_source); g_clear_pointer (&display->saved_clipboard_mimetype, g_free); g_clear_pointer (&display->saved_clipboard, g_bytes_unref); mimetypes = meta_selection_get_mimetypes (selection, selection_type); for (l = mimetypes; l; l = l->next) { gssize max_transfer_size; int idx; if (!mimetype_match (l->data, &idx, &max_transfer_size)) continue; if (best_idx < idx) { best_idx = idx; best = l->data; transfer_size = max_transfer_size; } } if (best_idx < 0) { g_list_free_full (mimetypes, g_free); return; } display->saved_clipboard_mimetype = g_strdup (best); g_list_free_full (mimetypes, g_free); output = g_memory_output_stream_new_resizable (); meta_selection_transfer_async (selection, META_SELECTION_CLIPBOARD, display->saved_clipboard_mimetype, transfer_size, output, NULL, (GAsyncReadyCallback) transfer_cb, output); } else if (!new_owner && display->saved_clipboard) { /* Old owner is gone, time to take over */ new_owner = meta_selection_source_memory_new (display->saved_clipboard_mimetype, display->saved_clipboard); g_set_object (&display->selection_source, new_owner); meta_selection_set_owner (selection, selection_type, new_owner); g_object_unref (new_owner); } } void meta_clipboard_manager_init (MetaDisplay *display) { MetaSelection *selection; selection = meta_display_get_selection (display); g_signal_connect_after (selection, "owner-changed", G_CALLBACK (owner_changed_cb), display); } void meta_clipboard_manager_shutdown (MetaDisplay *display) { MetaSelection *selection; g_clear_object (&display->selection_source); g_clear_pointer (&display->saved_clipboard, g_bytes_unref); g_clear_pointer (&display->saved_clipboard_mimetype, g_free); selection = meta_display_get_selection (display); g_signal_handlers_disconnect_by_func (selection, owner_changed_cb, display); } muffin-6.4.1/src/core/stack-tracker.c0000664000175000017500000010675514723361714016401 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * SECTION:stack-tracker * @short_description: Track stacking order for compositor * * #MetaStackTracker maintains the most accurate view we have at a * given point of time of the ordering of the children of the root * window (including override-redirect windows.) This is used to order * the windows when the compositor draws them. * * By contrast, #MetaStack is responsible for keeping track of how we * think that windows *should* be ordered. For windows we manage * (non-override-redirect windows), the two stacking orders will be * the same. */ /* * Copyright (C) 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/stack-tracker.h" #include #include "core/display-private.h" #include "core/frame.h" #include "meta/compositor.h" #include "meta/meta-x11-errors.h" #include "meta/util.h" #include "x11/meta-x11-display-private.h" /* The complexity here comes from resolving two competing factors: * * - We need to have a view of the stacking order that takes into * account everything we have done without waiting for events * back from the X server; we don't want to draw intermediate * partially-stacked stack states just because we haven't received * some notification yet. * * - Only the X server has an accurate view of the complete stacking; * when we make a request to restack windows, we don't know how * it will affect override-redirect windows, because at any point * applications may restack these windows without our involvement. * * The technique we use is that we keep three sets of information: * * - The stacking order on the server as known from the last * event we received. * - A queue of stacking requests that *we* made subsequent to * that last event. * - A predicted stacking order, derived from applying the queued * requests to the last state from the server. * * When we receive a new event: a) we compare the serial in the event to * the serial of the queued requests and remove any that are now * no longer pending b) if necessary, drop the predicted stacking * order to recompute it at the next opportunity. * * Possible optimizations: * Keep the stacks as an array + reverse-mapping hash table to avoid * linear lookups. * Keep the stacks as a GList + reverse-mapping hash table to avoid * linear lookups and to make restacking constant-time. */ typedef union _MetaStackOp MetaStackOp; typedef enum { STACK_OP_ADD, STACK_OP_REMOVE, STACK_OP_RAISE_ABOVE, STACK_OP_LOWER_BELOW } MetaStackOpType; typedef enum { APPLY_DEFAULT = 0, /* Only do restacking that we can do locally without changing * the order of X windows. After we've received any stack * events from the X server, we apply the locally cached * ops in this mode to handle the non-X parts */ NO_RESTACK_X_WINDOWS = 1 << 0, /* If the stacking operation wouldn't change the order of X * windows, ignore it. We use this when applying events received * from X so that a spontaneous ConfigureNotify (for a move, say) * doesn't change the stacking of X windows with respect to * Wayland windows. */ IGNORE_NOOP_X_RESTACK = 1 << 1 } ApplyFlags; /* MetaStackOp represents a "stacking operation" - a change to * apply to a window stack. Depending on the context, it could * either reflect a request we have sent to the server, or a * notification event we received from the X server. */ union _MetaStackOp { struct { MetaStackOpType type; gulong serial; guint64 window; } any; struct { MetaStackOpType type; gulong serial; guint64 window; } add; struct { MetaStackOpType type; gulong serial; guint64 window; } remove; struct { MetaStackOpType type; gulong serial; guint64 window; guint64 sibling; } raise_above; struct { MetaStackOpType type; gulong serial; guint64 window; guint64 sibling; } lower_below; }; struct _MetaStackTracker { MetaDisplay *display; /* This is the serial of the last request we made that was reflected * in xserver_stack */ gulong xserver_serial; /* A combined stack containing X and Wayland windows but without * any unverified operations applied. */ GArray *verified_stack; /* This is a queue of requests we've made to change the stacking order, * where we haven't yet gotten a reply back from the server. */ GQueue *unverified_predictions; /* This is how we think the stack is, based on verified_stack, and * on the unverified_predictions we've made subsequent to * verified_stack. */ GArray *predicted_stack; /* Idle function used to sync the compositor's view of the window * stack up with our best guess before a frame is drawn. */ guint sync_stack_later; }; static void meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker); static inline const char * get_window_desc (MetaStackTracker *tracker, guint64 window) { return meta_display_describe_stack_id (tracker->display, window); } static void meta_stack_op_dump (MetaStackTracker *tracker, MetaStackOp *op, const char *prefix, const char *suffix) { #ifdef WITH_VERBOSE_MODE const char *window_desc = get_window_desc (tracker, op->any.window); #endif switch (op->any.type) { case STACK_OP_ADD: meta_topic (META_DEBUG_STACK, "%sADD(%s; %ld)%s", prefix, window_desc, op->any.serial, suffix); break; case STACK_OP_REMOVE: meta_topic (META_DEBUG_STACK, "%sREMOVE(%s; %ld)%s", prefix, window_desc, op->any.serial, suffix); break; case STACK_OP_RAISE_ABOVE: { meta_topic (META_DEBUG_STACK, "%sRAISE_ABOVE(%s, %s; %ld)%s", prefix, window_desc, get_window_desc (tracker, op->raise_above.sibling), op->any.serial, suffix); break; } case STACK_OP_LOWER_BELOW: { meta_topic (META_DEBUG_STACK, "%sLOWER_BELOW(%s, %s; %ld)%s", prefix, window_desc, get_window_desc (tracker, op->lower_below.sibling), op->any.serial, suffix); break; } } } #ifdef WITH_VERBOSE_MODE static void stack_dump (MetaStackTracker *tracker, GArray *stack) { guint i; meta_push_no_msg_prefix (); for (i = 0; i < stack->len; i++) { guint64 window = g_array_index (stack, guint64, i); meta_topic (META_DEBUG_STACK, " %s", get_window_desc (tracker, window)); } meta_topic (META_DEBUG_STACK, "\n"); meta_pop_no_msg_prefix (); } #endif /* WITH_VERBOSE_MODE */ static void meta_stack_tracker_dump (MetaStackTracker *tracker) { #ifdef WITH_VERBOSE_MODE GList *l; meta_topic (META_DEBUG_STACK, "MetaStackTracker state\n"); meta_push_no_msg_prefix (); meta_topic (META_DEBUG_STACK, " xserver_serial: %ld\n", tracker->xserver_serial); meta_topic (META_DEBUG_STACK, " verified_stack: "); stack_dump (tracker, tracker->verified_stack); meta_topic (META_DEBUG_STACK, " unverified_predictions: ["); for (l = tracker->unverified_predictions->head; l; l = l->next) { MetaStackOp *op = l->data; meta_stack_op_dump (tracker, op, "", l->next ? ", " : ""); } meta_topic (META_DEBUG_STACK, "]\n"); if (tracker->predicted_stack) { meta_topic (META_DEBUG_STACK, "\n predicted_stack: "); stack_dump (tracker, tracker->predicted_stack); } meta_pop_no_msg_prefix (); #endif /* WITH_VERBOSE_MODE */ } static void meta_stack_op_free (MetaStackOp *op) { g_slice_free (MetaStackOp, op); } static int find_window (GArray *window_stack, guint64 window) { guint i; for (i = 0; i < window_stack->len; i++) { guint64 current = g_array_index (window_stack, guint64, i); if (current == window) return i; } return -1; } /* Returns TRUE if stack was changed */ static gboolean move_window_above (GArray *stack, guint64 window, int old_pos, int above_pos, ApplyFlags apply_flags) { int i; gboolean can_restack_this_window = (apply_flags & NO_RESTACK_X_WINDOWS) == 0 || !META_STACK_ID_IS_X11 (window); if (old_pos < above_pos) { if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) { gboolean found_x_window = FALSE; for (i = old_pos + 1; i <= above_pos; i++) if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) found_x_window = TRUE; if (!found_x_window) return FALSE; } for (i = old_pos; i < above_pos; i++) { if (!can_restack_this_window && META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i + 1))) break; g_array_index (stack, guint64, i) = g_array_index (stack, guint64, i + 1); } g_array_index (stack, guint64, i) = window; return i != old_pos; } else if (old_pos > above_pos + 1) { if ((apply_flags & IGNORE_NOOP_X_RESTACK) != 0) { gboolean found_x_window = FALSE; for (i = above_pos + 1; i < old_pos; i++) if (META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i))) found_x_window = TRUE; if (!found_x_window) return FALSE; } for (i = old_pos; i > above_pos + 1; i--) { if (!can_restack_this_window && META_STACK_ID_IS_X11 (g_array_index (stack, guint64, i - 1))) break; g_array_index (stack, guint64, i) = g_array_index (stack, guint64, i - 1); } g_array_index (stack, guint64, i) = window; return i != old_pos; } else return FALSE; } /* Returns TRUE if stack was changed */ static gboolean meta_stack_op_apply (MetaStackTracker *tracker, MetaStackOp *op, GArray *stack, ApplyFlags apply_flags) { switch (op->any.type) { case STACK_OP_ADD: { if (META_STACK_ID_IS_X11 (op->add.window) && (apply_flags & NO_RESTACK_X_WINDOWS) != 0) return FALSE; int old_pos = find_window (stack, op->add.window); if (old_pos >= 0) { g_warning ("STACK_OP_ADD: window %s already in stack", get_window_desc (tracker, op->add.window)); return FALSE; } g_array_append_val (stack, op->add.window); return TRUE; } case STACK_OP_REMOVE: { if (META_STACK_ID_IS_X11 (op->remove.window) && (apply_flags & NO_RESTACK_X_WINDOWS) != 0) return FALSE; int old_pos = find_window (stack, op->remove.window); if (old_pos < 0) { g_warning ("STACK_OP_REMOVE: window %s not in stack", get_window_desc (tracker, op->remove.window)); return FALSE; } g_array_remove_index (stack, old_pos); return TRUE; } case STACK_OP_RAISE_ABOVE: { int old_pos = find_window (stack, op->raise_above.window); int above_pos; if (old_pos < 0) { g_warning ("STACK_OP_RAISE_ABOVE: window %s not in stack", get_window_desc (tracker, op->raise_above.window)); return FALSE; } if (op->raise_above.sibling) { above_pos = find_window (stack, op->raise_above.sibling); if (above_pos < 0) { g_warning ("STACK_OP_RAISE_ABOVE: sibling window %s not in stack", get_window_desc (tracker, op->raise_above.sibling)); return FALSE; } } else { above_pos = -1; } return move_window_above (stack, op->raise_above.window, old_pos, above_pos, apply_flags); } case STACK_OP_LOWER_BELOW: { int old_pos = find_window (stack, op->lower_below.window); int above_pos; if (old_pos < 0) { g_warning ("STACK_OP_LOWER_BELOW: window %s not in stack", get_window_desc (tracker, op->lower_below.window)); return FALSE; } if (op->lower_below.sibling) { int below_pos = find_window (stack, op->lower_below.sibling); if (below_pos < 0) { g_warning ("STACK_OP_LOWER_BELOW: sibling window %s not in stack", get_window_desc (tracker, op->lower_below.sibling)); return FALSE; } above_pos = below_pos - 1; } else { above_pos = stack->len - 1; } return move_window_above (stack, op->lower_below.window, old_pos, above_pos, apply_flags); } } g_assert_not_reached (); return FALSE; } static GArray * copy_stack (GArray *stack) { GArray *copy = g_array_sized_new (FALSE, FALSE, sizeof (guint64), stack->len); g_array_set_size (copy, stack->len); memcpy (copy->data, stack->data, sizeof (guint64) * stack->len); return copy; } static void query_xserver_stack (MetaDisplay *display, MetaStackTracker *tracker) { MetaX11Display *x11_display = display->x11_display; Window ignored1, ignored2; Window *children; guint n_children; guint i, old_len; tracker->xserver_serial = XNextRequest (x11_display->xdisplay); XQueryTree (x11_display->xdisplay, x11_display->xroot, &ignored1, &ignored2, &children, &n_children); old_len = tracker->verified_stack->len; g_array_set_size (tracker->verified_stack, old_len + n_children); for (i = 0; i < n_children; i++) g_array_index (tracker->verified_stack, guint64, old_len + i) = children[i]; XFree (children); } static void drop_x11_windows (MetaDisplay *display, MetaStackTracker *tracker) { GArray *new_stack; GList *l; int i; tracker->xserver_serial = 0; new_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); for (i = 0; i < tracker->verified_stack->len; i++) { guint64 window = g_array_index (tracker->verified_stack, guint64, i); if (!META_STACK_ID_IS_X11 (window)) g_array_append_val (new_stack, window); } g_array_unref (tracker->verified_stack); tracker->verified_stack = new_stack; l = tracker->unverified_predictions->head; while (l) { MetaStackOp *op = l->data; GList *next = l->next; if (META_STACK_ID_IS_X11 (op->any.window)) g_queue_remove (tracker->unverified_predictions, op); l = next; } } MetaStackTracker * meta_stack_tracker_new (MetaDisplay *display) { MetaStackTracker *tracker; tracker = g_new0 (MetaStackTracker, 1); tracker->display = display; tracker->verified_stack = g_array_new (FALSE, FALSE, sizeof (guint64)); tracker->unverified_predictions = g_queue_new (); g_signal_connect (display, "x11-display-setup", G_CALLBACK (query_xserver_stack), tracker); g_signal_connect (display, "x11-display-closing", G_CALLBACK (drop_x11_windows), tracker); meta_stack_tracker_dump (tracker); return tracker; } void meta_stack_tracker_free (MetaStackTracker *tracker) { if (tracker->sync_stack_later) meta_later_remove (tracker->sync_stack_later); g_array_free (tracker->verified_stack, TRUE); if (tracker->predicted_stack) g_array_free (tracker->predicted_stack, TRUE); g_queue_foreach (tracker->unverified_predictions, (GFunc)meta_stack_op_free, NULL); g_queue_free (tracker->unverified_predictions); tracker->unverified_predictions = NULL; g_signal_handlers_disconnect_by_func (tracker->display, (gpointer)query_xserver_stack, tracker); g_signal_handlers_disconnect_by_func (tracker->display, drop_x11_windows, tracker); g_free (tracker); } static void stack_tracker_apply_prediction (MetaStackTracker *tracker, MetaStackOp *op) { gboolean free_at_end = FALSE; /* If this operation doesn't involve restacking X windows then it's * implicitly verified. We can apply it immediately unless there * are outstanding X restacks that haven't yet been confirmed. */ if (op->any.serial == 0 && tracker->unverified_predictions->length == 0) { if (meta_stack_op_apply (tracker, op, tracker->verified_stack, APPLY_DEFAULT)) meta_stack_tracker_queue_sync_stack (tracker); free_at_end = TRUE; } else { meta_stack_op_dump (tracker, op, "Predicting: ", "\n"); g_queue_push_tail (tracker->unverified_predictions, op); } if (!tracker->predicted_stack || meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT)) meta_stack_tracker_queue_sync_stack (tracker); if (free_at_end) meta_stack_op_free (op); meta_stack_tracker_dump (tracker); } void meta_stack_tracker_record_add (MetaStackTracker *tracker, guint64 window, gulong serial) { MetaStackOp *op = g_slice_new (MetaStackOp); op->any.type = STACK_OP_ADD; op->any.serial = serial; op->any.window = window; stack_tracker_apply_prediction (tracker, op); } void meta_stack_tracker_record_remove (MetaStackTracker *tracker, guint64 window, gulong serial) { MetaStackOp *op = g_slice_new (MetaStackOp); op->any.type = STACK_OP_REMOVE; op->any.serial = serial; op->any.window = window; stack_tracker_apply_prediction (tracker, op); } static void meta_stack_tracker_record_raise_above (MetaStackTracker *tracker, guint64 window, guint64 sibling, gulong serial) { MetaStackOp *op = g_slice_new (MetaStackOp); op->any.type = STACK_OP_RAISE_ABOVE; op->any.serial = serial; op->any.window = window; op->raise_above.sibling = sibling; stack_tracker_apply_prediction (tracker, op); } static void meta_stack_tracker_record_lower_below (MetaStackTracker *tracker, guint64 window, guint64 sibling, gulong serial) { MetaStackOp *op = g_slice_new (MetaStackOp); op->any.type = STACK_OP_LOWER_BELOW; op->any.serial = serial; op->any.window = window; op->lower_below.sibling = sibling; stack_tracker_apply_prediction (tracker, op); } static void stack_tracker_event_received (MetaStackTracker *tracker, MetaStackOp *op) { gboolean need_sync = FALSE; /* If the event is older than our initial query, then it's * already included in our tree. Just ignore it. */ if (op->any.serial < tracker->xserver_serial) return; meta_stack_op_dump (tracker, op, "Stack op event received: ", "\n"); /* First we apply any operations that we have queued up that depended * on X operations *older* than what we received .. those operations * must have been ignored by the X server, so we just apply the * operations we have as best as possible while not moving windows. */ while (tracker->unverified_predictions->head) { MetaStackOp *queued_op = tracker->unverified_predictions->head->data; if (queued_op->any.serial >= op->any.serial) break; meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, NO_RESTACK_X_WINDOWS); g_queue_pop_head (tracker->unverified_predictions); meta_stack_op_free (queued_op); need_sync = TRUE; } /* Then we apply the received event. If it's a spontaneous event * based on stacking we didn't trigger, this is the only handling. If we * triggered it, we do the X restacking here, and then any residual * local-only Wayland stacking below. */ if (meta_stack_op_apply (tracker, op, tracker->verified_stack, IGNORE_NOOP_X_RESTACK)) need_sync = TRUE; /* What is left to process is the prediction corresponding to the event * (if any), and then any subsequent Wayland-only events we can just * go ahead and do now. */ while (tracker->unverified_predictions->head) { MetaStackOp *queued_op = tracker->unverified_predictions->head->data; if (queued_op->any.serial > op->any.serial) break; meta_stack_op_apply (tracker, queued_op, tracker->verified_stack, NO_RESTACK_X_WINDOWS); g_queue_pop_head (tracker->unverified_predictions); meta_stack_op_free (queued_op); need_sync = TRUE; } if (need_sync) { if (tracker->predicted_stack) { g_array_free (tracker->predicted_stack, TRUE); tracker->predicted_stack = NULL; } meta_stack_tracker_queue_sync_stack (tracker); } meta_stack_tracker_dump (tracker); } void meta_stack_tracker_create_event (MetaStackTracker *tracker, XCreateWindowEvent *event) { MetaStackOp op; op.any.type = STACK_OP_ADD; op.any.serial = event->serial; op.add.window = event->window; stack_tracker_event_received (tracker, &op); } void meta_stack_tracker_destroy_event (MetaStackTracker *tracker, XDestroyWindowEvent *event) { MetaStackOp op; op.any.type = STACK_OP_REMOVE; op.any.serial = event->serial; op.remove.window = event->window; stack_tracker_event_received (tracker, &op); } void meta_stack_tracker_reparent_event (MetaStackTracker *tracker, XReparentEvent *event) { if (event->parent == event->event) { MetaStackOp op; op.any.type = STACK_OP_ADD; op.any.serial = event->serial; op.add.window = event->window; stack_tracker_event_received (tracker, &op); } else { MetaStackOp op; op.any.type = STACK_OP_REMOVE; op.any.serial = event->serial; op.remove.window = event->window; stack_tracker_event_received (tracker, &op); } } void meta_stack_tracker_configure_event (MetaStackTracker *tracker, XConfigureEvent *event) { MetaStackOp op; op.any.type = STACK_OP_RAISE_ABOVE; op.any.serial = event->serial; op.raise_above.window = event->window; op.raise_above.sibling = event->above; stack_tracker_event_received (tracker, &op); } static gboolean meta_stack_tracker_is_guard_window (MetaStackTracker *tracker, uint64_t stack_id) { MetaX11Display *x11_display = tracker->display->x11_display; if (!x11_display) return FALSE; return stack_id == x11_display->guard_window; } /** * meta_stack_tracker_get_stack: * @tracker: a #MetaStackTracker * @windows: location to store list of windows, or %NULL * @n_windows: location to store count of windows, or %NULL * * @windows will contain the most current view we have of the stacking order * of the children of the root window. The returned array contains * everything: InputOnly windows, override-redirect windows, * hidden windows, etc. Some of these will correspond to MetaWindow * objects, others won't. * * Assuming that no other clients have made requests that change * the stacking order since we last received a notification, the * returned list of windows is exactly that you'd get as the * children when calling XQueryTree() on the root window. */ void meta_stack_tracker_get_stack (MetaStackTracker *tracker, guint64 **windows, int *n_windows) { GArray *stack; if (tracker->unverified_predictions->length == 0) { stack = tracker->verified_stack; } else { if (tracker->predicted_stack == NULL) { GList *l; tracker->predicted_stack = copy_stack (tracker->verified_stack); for (l = tracker->unverified_predictions->head; l; l = l->next) { MetaStackOp *op = l->data; meta_stack_op_apply (tracker, op, tracker->predicted_stack, APPLY_DEFAULT); } } stack = tracker->predicted_stack; } if (windows) *windows = (guint64 *)stack->data; if (n_windows) *n_windows = stack->len; } /** * meta_stack_tracker_sync_stack: * @tracker: a #MetaStackTracker * * Informs the compositor of the current stacking order of windows, * based on the predicted view maintained by the #MetaStackTracker. */ void meta_stack_tracker_sync_stack (MetaStackTracker *tracker) { guint64 *windows; GList *meta_windows; int n_windows; int i; if (tracker->sync_stack_later) { meta_later_remove (tracker->sync_stack_later); tracker->sync_stack_later = 0; } meta_stack_tracker_keep_override_redirect_on_top (tracker); meta_stack_tracker_get_stack (tracker, &windows, &n_windows); meta_windows = NULL; for (i = 0; i < n_windows; i++) { guint64 window = windows[i]; if (META_STACK_ID_IS_X11 (window)) { MetaX11Display *x11_display = tracker->display->x11_display; MetaWindow *meta_window = NULL; if (x11_display) meta_window = meta_x11_display_lookup_x_window (x11_display, (Window) window); /* When mapping back from xwindow to MetaWindow we have to be a bit careful; * children of the root could include unmapped windows created by toolkits * for internal purposes, including ones that we have registered in our * XID => window table. (Wine uses a toplevel for _NET_WM_USER_TIME_WINDOW; * see window-prop.c:reload_net_wm_user_time_window() for registration.) */ if (meta_window && ((Window)window == meta_window->xwindow || (meta_window->frame && (Window)window == meta_window->frame->xwindow))) meta_windows = g_list_prepend (meta_windows, meta_window); } else meta_windows = g_list_prepend (meta_windows, meta_display_lookup_stamp (tracker->display, window)); } meta_compositor_sync_stack (tracker->display->compositor, meta_windows); g_list_free (meta_windows); meta_display_restacked (tracker->display); } static gboolean stack_tracker_sync_stack_later (gpointer data) { meta_stack_tracker_sync_stack (data); return FALSE; } /** * meta_stack_tracker_queue_sync_stack: * @tracker: a #MetaStackTracker * * Queue informing the compositor of the new stacking order before the * next redraw. (See meta_stack_tracker_sync_stack()). This is called * internally when the stack of X windows changes, but also needs be * called directly when we an undecorated window is first shown or * withdrawn since the compositor's stacking order (which contains only * the windows that have a corresponding MetaWindow) will change without * any change to the stacking order of the X windows, if we are creating * or destroying MetaWindows. */ void meta_stack_tracker_queue_sync_stack (MetaStackTracker *tracker) { if (tracker->sync_stack_later == 0) { tracker->sync_stack_later = meta_later_add (META_LATER_SYNC_STACK, stack_tracker_sync_stack_later, tracker, NULL); } } /* When moving an X window we sometimes need an X based sibling. * * If the given sibling is X based this function returns it back * otherwise it searches downwards looking for the nearest X window. * * If no X based sibling could be found return NULL. */ static Window find_x11_sibling_downwards (MetaStackTracker *tracker, guint64 sibling) { guint64 *windows; int n_windows; int i; if (META_STACK_ID_IS_X11 (sibling)) return (Window)sibling; meta_stack_tracker_get_stack (tracker, &windows, &n_windows); /* NB: Children are in order from bottom to top and we * want to search downwards for the nearest X window. */ for (i = n_windows - 1; i >= 0; i--) if (windows[i] == sibling) break; for (; i >= 0; i--) { if (META_STACK_ID_IS_X11 (windows[i])) return (Window)windows[i]; } return None; } static Window find_x11_sibling_upwards (MetaStackTracker *tracker, guint64 sibling) { guint64 *windows; int n_windows; int i; if (META_STACK_ID_IS_X11 (sibling)) return (Window)sibling; meta_stack_tracker_get_stack (tracker, &windows, &n_windows); for (i = 0; i < n_windows; i++) if (windows[i] == sibling) break; for (; i < n_windows; i++) { if (META_STACK_ID_IS_X11 (windows[i])) return (Window)windows[i]; } return None; } static void meta_stack_tracker_lower_below (MetaStackTracker *tracker, guint64 window, guint64 sibling) { gulong serial = 0; MetaX11Display *x11_display = tracker->display->x11_display; if (META_STACK_ID_IS_X11 (window)) { XWindowChanges changes; changes.sibling = sibling ? find_x11_sibling_upwards (tracker, sibling) : None; if (changes.sibling != find_x11_sibling_upwards (tracker, window)) { serial = XNextRequest (x11_display->xdisplay); meta_x11_error_trap_push (x11_display); changes.stack_mode = changes.sibling ? Below : Above; XConfigureWindow (x11_display->xdisplay, window, (changes.sibling ? CWSibling : 0) | CWStackMode, &changes); meta_x11_error_trap_pop (x11_display); } } meta_stack_tracker_record_lower_below (tracker, window, sibling, serial); } static void meta_stack_tracker_raise_above (MetaStackTracker *tracker, guint64 window, guint64 sibling) { gulong serial = 0; MetaX11Display *x11_display = tracker->display->x11_display; if (META_STACK_ID_IS_X11 (window)) { XWindowChanges changes; changes.sibling = sibling ? find_x11_sibling_downwards (tracker, sibling) : None; if (changes.sibling != find_x11_sibling_downwards (tracker, window)) { serial = XNextRequest (x11_display->xdisplay); meta_x11_error_trap_push (x11_display); changes.stack_mode = changes.sibling ? Above : Below; XConfigureWindow (x11_display->xdisplay, (Window)window, (changes.sibling ? CWSibling : 0) | CWStackMode, &changes); meta_x11_error_trap_pop (x11_display); } } meta_stack_tracker_record_raise_above (tracker, window, sibling, serial); } void meta_stack_tracker_lower (MetaStackTracker *tracker, guint64 window) { meta_stack_tracker_raise_above (tracker, window, None); } static void meta_stack_tracker_keep_override_redirect_on_top (MetaStackTracker *tracker) { guint64 *stack; int n_windows, i; int topmost_non_or; meta_stack_tracker_get_stack (tracker, &stack, &n_windows); for (i = n_windows - 1; i >= 0; i--) { MetaWindow *window; window = meta_display_lookup_stack_id (tracker->display, stack[i]); if (window && window->layer != META_LAYER_OVERRIDE_REDIRECT) break; } topmost_non_or = i; for (i -= 1; i >= 0; i--) { MetaWindow *window; if (meta_stack_tracker_is_guard_window (tracker, stack[i])) break; window = meta_display_lookup_stack_id (tracker->display, stack[i]); if (window && window->layer == META_LAYER_OVERRIDE_REDIRECT) { meta_stack_tracker_raise_above (tracker, stack[i], stack[topmost_non_or]); meta_stack_tracker_get_stack (tracker, &stack, &n_windows); topmost_non_or -= 1; } } } void meta_stack_tracker_restack_managed (MetaStackTracker *tracker, const guint64 *managed, int n_managed) { guint64 *windows; int n_windows; int old_pos, new_pos; if (n_managed == 0) return; meta_stack_tracker_get_stack (tracker, &windows, &n_windows); /* If the top window has to be restacked, we don't want to move it to the very * top of the stack, since apps expect override-redirect windows to stay near * the top of the X stack; we instead move it above all managed windows (or * above the guard window if there are no non-hidden managed windows.) */ old_pos = n_windows - 1; for (old_pos = n_windows - 1; old_pos >= 0; old_pos--) { MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]); if ((old_window && !old_window->override_redirect && !old_window->unmanaging) || meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) break; } g_assert (old_pos >= 0); new_pos = n_managed - 1; if (managed[new_pos] != windows[old_pos]) { /* Move the first managed window in the new stack above all managed windows */ meta_stack_tracker_raise_above (tracker, managed[new_pos], windows[old_pos]); meta_stack_tracker_get_stack (tracker, &windows, &n_windows); /* Moving managed[new_pos] above windows[old_pos], moves the window at old_pos down by one */ } old_pos--; new_pos--; while (old_pos >= 0 && new_pos >= 0) { if (meta_stack_tracker_is_guard_window (tracker, windows[old_pos])) break; if (windows[old_pos] == managed[new_pos]) { old_pos--; new_pos--; continue; } MetaWindow *old_window = meta_display_lookup_stack_id (tracker->display, windows[old_pos]); if (!old_window || old_window->override_redirect || old_window->unmanaging) { old_pos--; continue; } meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos + 1]); meta_stack_tracker_get_stack (tracker, &windows, &n_windows); /* Moving managed[new_pos] above windows[old_pos] moves the window at old_pos down by one, * we'll examine it again to see if it matches the next new window */ old_pos--; new_pos--; } while (new_pos > 0) { meta_stack_tracker_lower_below (tracker, managed[new_pos], managed[new_pos - 1]); new_pos--; } } void meta_stack_tracker_restack_at_bottom (MetaStackTracker *tracker, const guint64 *new_order, int n_new_order) { guint64 *windows; int n_windows; int pos; meta_stack_tracker_get_stack (tracker, &windows, &n_windows); for (pos = 0; pos < n_new_order; pos++) { if (pos >= n_windows || windows[pos] != new_order[pos]) { if (pos == 0) meta_stack_tracker_lower (tracker, new_order[pos]); else meta_stack_tracker_raise_above (tracker, new_order[pos], new_order[pos - 1]); meta_stack_tracker_get_stack (tracker, &windows, &n_windows); } } } muffin-6.4.1/src/core/meta-close-dialog-default.c0000664000175000017500000001657314723361714020551 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2004 Elijah Newren * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #define _XOPEN_SOURCE /* for kill() */ #include "config.h" #include "core/meta-close-dialog-default-private.h" #include "meta/meta-close-dialog.h" #include #include #include #include "core/util-private.h" #include "core/window-private.h" #include "x11/meta-x11-display-private.h" typedef struct _MetaCloseDialogDefaultPrivate MetaCloseDialogDefaultPrivate; struct _MetaCloseDialogDefault { GObject parent_instance; MetaWindow *window; int dialog_pid; guint child_watch_id; }; enum { PROP_0, PROP_WINDOW, N_PROPS }; GParamSpec *pspecs[N_PROPS] = { NULL }; static void meta_close_dialog_iface_init (MetaCloseDialogInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaCloseDialogDefault, meta_close_dialog_default, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (META_TYPE_CLOSE_DIALOG, meta_close_dialog_iface_init)) static void dialog_exited (GPid pid, int status, gpointer user_data) { MetaCloseDialogDefault *dialog = user_data; dialog->dialog_pid = -1; /* exit status of 0 means the user pressed "Force Quit" */ if (WIFEXITED (status) && WEXITSTATUS (status) == 0) g_signal_emit_by_name (dialog, "response", META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE); } static void present_existing_delete_dialog (MetaCloseDialogDefault *dialog) { MetaWindow *window; GSList *windows; GSList *tmp; window = dialog->window; if (dialog->dialog_pid < 0) return; meta_topic (META_DEBUG_PING, "Presenting existing ping dialog for %s\n", window->desc); /* Activate transient for window that belongs to * mutter-dialog */ windows = meta_display_list_windows (window->display, META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *w = tmp->data; if (w->transient_for == window && w->res_class && g_ascii_strcasecmp (w->res_class, "mutter-dialog") == 0) { meta_window_activate (w, CLUTTER_CURRENT_TIME); break; } tmp = tmp->next; } g_slist_free (windows); } static void meta_close_dialog_default_show (MetaCloseDialog *dialog) { MetaCloseDialogDefault *dialog_default = META_CLOSE_DIALOG_DEFAULT (dialog); MetaWindow *window = dialog_default->window; gchar *window_title, *window_content, *tmp; GPid dialog_pid; if (dialog_default->dialog_pid >= 0) { present_existing_delete_dialog (dialog_default); return; } /* This is to get a better string if the title isn't representable * in the locale encoding; actual conversion to UTF-8 is done inside * meta_show_dialog */ if (window->title && window->title[0]) { tmp = g_locale_from_utf8 (window->title, -1, NULL, NULL, NULL); if (tmp == NULL) window_title = NULL; else window_title = window->title; g_free (tmp); } else { window_title = NULL; } if (window_title) /* Translators: %s is a window title */ tmp = g_strdup_printf (_("“%s” is not responding."), window_title); else tmp = g_strdup (_("Application is not responding.")); window_content = g_strdup_printf ( "%s\n\n%s", tmp, _("You may choose to wait a short while for it to " "continue or force the application to quit entirely.")); dialog_pid = meta_show_dialog ("--question", window_content, NULL, window->display->x11_display->screen_name, _("_Force Quit"), _("_Wait"), window->xwindow, NULL, NULL); g_free (window_content); g_free (tmp); dialog_default->dialog_pid = dialog_pid; g_child_watch_add (dialog_pid, dialog_exited, dialog); } static void meta_close_dialog_default_hide (MetaCloseDialog *dialog) { MetaCloseDialogDefault *dialog_default; dialog_default = META_CLOSE_DIALOG_DEFAULT (dialog); g_clear_handle_id (&dialog_default->child_watch_id, g_source_remove); if (dialog_default->dialog_pid > -1) { kill (dialog_default->dialog_pid, SIGTERM); dialog_default->dialog_pid = -1; } } static void meta_close_dialog_iface_init (MetaCloseDialogInterface *iface) { iface->show = meta_close_dialog_default_show; iface->hide = meta_close_dialog_default_hide; } static void meta_close_dialog_default_finalize (GObject *object) { MetaCloseDialogDefault *dialog; dialog = META_CLOSE_DIALOG_DEFAULT (object); g_clear_handle_id (&dialog->child_watch_id, g_source_remove); if (dialog->dialog_pid > -1) { kill (dialog->dialog_pid, SIGKILL); dialog->dialog_pid = -1; } G_OBJECT_CLASS (meta_close_dialog_default_parent_class)->finalize (object); } static void meta_close_dialog_default_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaCloseDialogDefault *dialog; dialog = META_CLOSE_DIALOG_DEFAULT (object); switch (prop_id) { case PROP_WINDOW: dialog->window = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_close_dialog_default_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaCloseDialogDefault *dialog; dialog = META_CLOSE_DIALOG_DEFAULT (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, dialog->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_close_dialog_default_class_init (MetaCloseDialogDefaultClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_close_dialog_default_finalize; object_class->set_property = meta_close_dialog_default_set_property; object_class->get_property = meta_close_dialog_default_get_property; g_object_class_override_property (object_class, PROP_WINDOW, "window"); } static void meta_close_dialog_default_init (MetaCloseDialogDefault *dialog) { dialog->dialog_pid = -1; } MetaCloseDialog * meta_close_dialog_default_new (MetaWindow *window) { return g_object_new (META_TYPE_CLOSE_DIALOG_DEFAULT, "window", window, NULL); } muffin-6.4.1/src/core/meta-border.c0000664000175000017500000001033714723361714016032 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "core/meta-border.h" #include static inline float meta_vector2_cross_product (const MetaVector2 a, const MetaVector2 b) { return a.x * b.y - a.y * b.x; } static inline MetaVector2 meta_vector2_add (const MetaVector2 a, const MetaVector2 b) { return (MetaVector2) { .x = a.x + b.x, .y = a.y + b.y, }; } static inline MetaVector2 meta_vector2_multiply_constant (const float c, const MetaVector2 a) { return (MetaVector2) { .x = c * a.x, .y = c * a.y, }; } gboolean meta_line2_intersects_with (const MetaLine2 *line1, const MetaLine2 *line2, MetaVector2 *intersection) { MetaVector2 p = line1->a; MetaVector2 r = meta_vector2_subtract (line1->b, line1->a); MetaVector2 q = line2->a; MetaVector2 s = meta_vector2_subtract (line2->b, line2->a); float rxs; float sxr; float t; float u; /* * The line (p, r) and (q, s) intersects where * * p + t r = q + u s * * Calculate t: * * (p + t r) × s = (q + u s) × s * p × s + t (r × s) = q × s + u (s × s) * p × s + t (r × s) = q × s * t (r × s) = q × s - p × s * t (r × s) = (q - p) × s * t = ((q - p) × s) / (r × s) * * Using the same method, for u we get: * * u = ((p - q) × r) / (s × r) */ rxs = meta_vector2_cross_product (r, s); sxr = meta_vector2_cross_product (s, r); /* If r × s = 0 then the lines are either parallel or collinear. */ if (fabsf (rxs) < FLT_MIN) return FALSE; t = meta_vector2_cross_product (meta_vector2_subtract (q, p), s) / rxs; u = meta_vector2_cross_product (meta_vector2_subtract (p, q), r) / sxr; /* The lines only intersect if 0 ≤ t ≤ 1 and 0 ≤ u ≤ 1. */ if (t < 0.0 || t > 1.0 || u < 0.0 || u > 1.0) return FALSE; *intersection = meta_vector2_add (p, meta_vector2_multiply_constant (t, r)); return TRUE; } gboolean meta_border_is_horizontal (MetaBorder *border) { return border->line.a.y == border->line.b.y; } gboolean meta_border_is_blocking_directions (MetaBorder *border, MetaBorderMotionDirection directions) { if (meta_border_is_horizontal (border)) { if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_Y | META_BORDER_MOTION_DIRECTION_NEGATIVE_Y)) == 0) return FALSE; } else { if ((directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | META_BORDER_MOTION_DIRECTION_NEGATIVE_X)) == 0) return FALSE; } return (~border->blocking_directions & directions) != directions; } unsigned int meta_border_get_allows_directions (MetaBorder *border) { return ~border->blocking_directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | META_BORDER_MOTION_DIRECTION_POSITIVE_Y | META_BORDER_MOTION_DIRECTION_NEGATIVE_X | META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); } void meta_border_set_allows_directions (MetaBorder *border, unsigned int directions) { border->blocking_directions = ~directions & (META_BORDER_MOTION_DIRECTION_POSITIVE_X | META_BORDER_MOTION_DIRECTION_POSITIVE_Y | META_BORDER_MOTION_DIRECTION_NEGATIVE_X | META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); } muffin-6.4.1/src/core/mutter.c0000664000175000017500000000441014723361714015144 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2011 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include "compositor/meta-plugin-manager.h" #include "meta/main.h" #include "meta/util.h" static gboolean print_version (const gchar *option_name, const gchar *value, gpointer data, GError **error) { const int latest_year = 2011; g_print (_("mutter %s\n" "Copyright © 2001-%d Havoc Pennington, Red Hat, Inc., and others\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n"), VERSION, latest_year); exit (0); } static const char *plugin = "libdefault"; GOptionEntry mutter_options[] = { { "version", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, print_version, N_("Print version"), NULL }, { "mutter-plugin", 0, 0, G_OPTION_ARG_STRING, &plugin, N_("Mutter plugin to use"), "PLUGIN", }, { NULL } }; int main (int argc, char **argv) { GOptionContext *ctx; GError *error = NULL; ctx = meta_get_option_context (); g_option_context_add_main_entries (ctx, mutter_options, GETTEXT_PACKAGE); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("mutter: %s\n", error->message); exit (1); } g_option_context_free (ctx); if (plugin) meta_plugin_manager_load (plugin); meta_init (); meta_register_with_session (); return meta_run (); } muffin-6.4.1/src/core/events.c0000664000175000017500000004072314723361714015137 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/events.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-monitor-private.h" #include "backends/x11/meta-backend-x11.h" #include "compositor/meta-window-actor-private.h" #include "core/display-private.h" #include "core/window-private.h" #include "meta/meta-backend.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #endif #define IS_GESTURE_EVENT(e) ((e)->type == CLUTTER_TOUCHPAD_SWIPE || \ (e)->type == CLUTTER_TOUCHPAD_PINCH || \ (e)->type == CLUTTER_TOUCH_BEGIN || \ (e)->type == CLUTTER_TOUCH_UPDATE || \ (e)->type == CLUTTER_TOUCH_END || \ (e)->type == CLUTTER_TOUCH_CANCEL) #define IS_KEY_EVENT(e) ((e)->type == CLUTTER_KEY_PRESS || \ (e)->type == CLUTTER_KEY_RELEASE) typedef enum { EVENTS_UNFREEZE_SYNC, EVENTS_UNFREEZE_REPLAY, } EventsUnfreezeMethod; static gboolean stage_has_key_focus (void) { MetaBackend *backend = meta_get_backend (); ClutterActor *stage = meta_backend_get_stage (backend); return clutter_stage_get_key_focus (CLUTTER_STAGE (stage)) == stage; } static MetaWindow * get_window_for_event (MetaDisplay *display, const ClutterEvent *event) { switch (display->event_route) { case META_EVENT_ROUTE_NORMAL: { ClutterActor *source; MetaWindowActor *window_actor; /* Always use the key focused window for key events. */ if (IS_KEY_EVENT (event)) return stage_has_key_focus () ? display->focus_window : NULL; source = clutter_event_get_source (event); window_actor = meta_window_actor_from_actor (source); if (window_actor) return meta_window_actor_get_meta_window (window_actor); else return NULL; } case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_WAYLAND_POPUP: case META_EVENT_ROUTE_FRAME_BUTTON: return display->grab_window; default: g_assert_not_reached (); return NULL; } } static void handle_idletime_for_event (const ClutterEvent *event) { MetaIdleMonitor *core_monitor; if (clutter_event_get_device (event) == NULL) return; if (event->any.flags & CLUTTER_EVENT_FLAG_SYNTHETIC || event->type == CLUTTER_ENTER || event->type == CLUTTER_LEAVE || event->type == CLUTTER_STAGE_STATE || event->type == CLUTTER_DESTROY_NOTIFY || event->type == CLUTTER_CLIENT_MESSAGE || event->type == CLUTTER_DELETE) return; core_monitor = meta_idle_monitor_get_core (); meta_idle_monitor_reset_idletime (core_monitor); } static gboolean sequence_is_pointer_emulated (MetaDisplay *display, const ClutterEvent *event) { ClutterEventSequence *sequence; sequence = clutter_event_get_event_sequence (event); if (!sequence) return FALSE; if (clutter_event_is_pointer_emulated (event)) return TRUE; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); /* When using Clutter's native input backend there is no concept of * pointer emulating sequence, we still must make up our own to be * able to implement single-touch (hence pointer alike) behavior. * * This is implemented similarly to X11, where only the first touch * on screen gets the "pointer emulated" flag, and it won't get assigned * to another sequence until the next first touch on an idle touchscreen. */ if (META_IS_BACKEND_NATIVE (backend)) { MetaGestureTracker *tracker; tracker = meta_display_get_gesture_tracker (display); if (event->type == CLUTTER_TOUCH_BEGIN && meta_gesture_tracker_get_n_current_touches (tracker) == 0) return TRUE; } #endif /* HAVE_NATIVE_BACKEND */ return FALSE; } static void maybe_unfreeze_pointer_events (MetaBackend *backend, const ClutterEvent *event, EventsUnfreezeMethod unfreeze_method) { Display *xdisplay; int event_mode; int device_id; if (event->type != CLUTTER_BUTTON_PRESS) return; if (!META_IS_BACKEND_X11 (backend)) return; device_id = clutter_event_get_device_id (event); switch (unfreeze_method) { case EVENTS_UNFREEZE_SYNC: event_mode = XISyncDevice; meta_verbose ("Syncing events time %u device %i\n", (unsigned int) event->button.time, device_id); break; case EVENTS_UNFREEZE_REPLAY: event_mode = XIReplayDevice; meta_verbose ("Replaying events time %u device %i\n", (unsigned int) event->button.time, device_id); break; default: g_assert_not_reached (); return; } xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XIAllowEvents (xdisplay, device_id, event_mode, event->button.time); } static gboolean meta_display_handle_event (MetaDisplay *display, const ClutterEvent *event) { MetaBackend *backend = meta_get_backend (); MetaWindow *window; gboolean bypass_clutter = FALSE; G_GNUC_UNUSED gboolean bypass_wayland = FALSE; MetaGestureTracker *gesture_tracker; ClutterEventSequence *sequence; sequence = clutter_event_get_event_sequence (event); /* Set the pointer emulating sequence on touch begin, if eligible */ if (event->type == CLUTTER_TOUCH_BEGIN) { if (sequence_is_pointer_emulated (display, event)) { /* This is the new pointer emulating sequence */ display->pointer_emulating_sequence = sequence; } else if (display->pointer_emulating_sequence == sequence) { /* This sequence was "pointer emulating" in a prior incarnation, * but now it isn't. We unset the pointer emulating sequence at * this point so the current sequence is not mistaken as pointer * emulating, while we've ensured that it's been deemed * "pointer emulating" throughout all of the event processing * of the previous incarnation. */ display->pointer_emulating_sequence = NULL; } } #ifdef HAVE_WAYLAND MetaWaylandCompositor *compositor = NULL; if (meta_is_wayland_compositor ()) { compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_update (compositor, event); } #endif if (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE || event->type == CLUTTER_PAD_RING || event->type == CLUTTER_PAD_STRIP) { gboolean handle_pad_event; gboolean is_mode_switch = FALSE; if (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE) { ClutterInputDevice *pad; uint32_t button; pad = clutter_event_get_source_device (event); button = clutter_event_get_button (event); is_mode_switch = clutter_input_device_get_mode_switch_button_group (pad, button) >= 0; } handle_pad_event = !display->current_pad_osd || is_mode_switch; if (handle_pad_event && meta_input_settings_handle_pad_event (meta_backend_get_input_settings (backend), event)) { bypass_wayland = bypass_clutter = TRUE; goto out; } } if (event->type == CLUTTER_SCROLL) { if (meta_prefs_get_mouse_zoom_enabled () && ((event->scroll.modifier_state & ~meta_keybindings_get_ignored_modifier_mask (display)) == meta_keybindings_get_mouse_zoom_modifiers (display))) { if (clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_UP) { meta_display_a11y_zoom (display, TRUE); } else if (clutter_event_get_scroll_direction (event) == CLUTTER_SCROLL_DOWN) { meta_display_a11y_zoom (display, FALSE); } bypass_wayland = bypass_clutter = TRUE; goto out; } } if (event->type != CLUTTER_DEVICE_ADDED && event->type != CLUTTER_DEVICE_REMOVED) { ClutterInputDevice *source; handle_idletime_for_event (event); source = clutter_event_get_source_device (event); if (source) meta_backend_update_last_device (backend, source); } #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () && event->type == CLUTTER_MOTION) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); if (meta_wayland_tablet_manager_consumes_event (compositor->tablet_manager, event)) { meta_wayland_tablet_manager_update_cursor_position (compositor->tablet_manager, event); } else { MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); meta_cursor_tracker_update_position (cursor_tracker, event->motion.x, event->motion.y); } display->monitor_cache_invalidated = TRUE; } #endif window = get_window_for_event (display, event); display->current_time = event->any.time; if (window && !window->override_redirect && (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN)) { if (META_CURRENT_TIME == display->current_time) { /* We can't use missing (i.e. invalid) timestamps to set user time, * nor do we want to use them to sanity check other timestamps. * See bug 313490 for more details. */ meta_warning ("Event has no timestamp! You may be using a broken " "program such as xse. Please ask the authors of that " "program to fix it.\n"); } else { meta_window_set_user_time (window, display->current_time); meta_display_sanity_check_timestamps (display, display->current_time); } } gesture_tracker = meta_display_get_gesture_tracker (display); if (meta_gesture_tracker_handle_event (gesture_tracker, event)) { bypass_wayland = bypass_clutter = TRUE; goto out; } if (display->event_route == META_EVENT_ROUTE_WINDOW_OP) { if (meta_window_handle_mouse_grab_op_event (window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } } /* For key events, it's important to enforce single-handling, or * we can get into a confused state. So if a keybinding is * handled (because it's one of our hot-keys, or because we are * in a keyboard-grabbed mode like moving a window, we don't * want to pass the key event to the compositor or Wayland at all. */ if (display->event_route != META_EVENT_ROUTE_COMPOSITOR_GRAB && meta_keybindings_process_event (display, window, event)) { bypass_clutter = TRUE; bypass_wayland = TRUE; goto out; } /* Do not pass keyboard events to Wayland if key focus is not on the * stage in normal mode (e.g. during keynav in the panel) */ if (display->event_route == META_EVENT_ROUTE_NORMAL) { if (IS_KEY_EVENT (event) && !stage_has_key_focus ()) { bypass_wayland = TRUE; goto out; } } if (display->current_pad_osd) { bypass_wayland = TRUE; goto out; } if (window) { /* Events that are likely to trigger compositor gestures should * be known to clutter so they can propagate along the hierarchy. * Gesture-wise, there's two groups of events we should be getting * here: * - CLUTTER_TOUCH_* with a touch sequence that's not yet accepted * by the gesture tracker, these might trigger gesture actions * into recognition. Already accepted touch sequences are handled * directly by meta_gesture_tracker_handle_event(). * - CLUTTER_TOUCHPAD_* events over windows. These can likewise * trigger ::captured-event handlers along the way. */ bypass_clutter = !IS_GESTURE_EVENT (event); /* When double clicking to un-maximize an X11 window under Wayland, * there is a race between X11 and Wayland protocols and the X11 * XConfigureWindow may be processed by Xwayland before the button * press event is forwarded via the Wayland protocol. * As a result, the second click may reach another X11 window placed * immediately underneath in the X11 stack. * The following is to make sure we do not forward the button press * event to Wayland if it was handled by the frame UI. * See: https://gitlab.gnome.org/GNOME/mutter/issues/88 */ if (meta_window_handle_ui_frame_event (window, event)) bypass_wayland = (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN); else meta_window_handle_ungrabbed_event (window, event); /* This might start a grab op. If it does, then filter out the * event, and if it doesn't, replay the event to release our * own sync grab. */ if (display->event_route == META_EVENT_ROUTE_WINDOW_OP || display->event_route == META_EVENT_ROUTE_FRAME_BUTTON) { bypass_clutter = TRUE; bypass_wayland = TRUE; } else { /* Only replay button press events, since that's where we * have the synchronous grab. */ maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_REPLAY); /* If the focus window has an active close dialog let clutter * events go through, so fancy clutter dialogs can get to handle * all events. */ if (window->close_dialog && meta_close_dialog_is_visible (window->close_dialog)) { bypass_wayland = TRUE; bypass_clutter = FALSE; } } goto out; } else { /* We could not match the event with a window, make sure we sync * the pointer to discard the sequence and don't keep events frozen. */ maybe_unfreeze_pointer_events (backend, event, EVENTS_UNFREEZE_SYNC); } out: /* If the compositor has a grab, don't pass that through to Wayland */ if (display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB) bypass_wayland = TRUE; /* If a Wayland client has a grab, don't pass that through to Clutter */ if (display->event_route == META_EVENT_ROUTE_WAYLAND_POPUP) bypass_clutter = TRUE; #ifdef HAVE_WAYLAND if (compositor && !bypass_wayland) { if (meta_wayland_compositor_handle_event (compositor, event)) bypass_clutter = TRUE; } #endif display->current_time = META_CURRENT_TIME; return bypass_clutter; } static gboolean event_callback (const ClutterEvent *event, gpointer data) { MetaDisplay *display = data; return meta_display_handle_event (display, event); } void meta_display_init_events (MetaDisplay *display) { display->clutter_event_filter = clutter_event_add_filter (NULL, event_callback, NULL, display); } void meta_display_free_events (MetaDisplay *display) { clutter_event_remove_filter (display->clutter_event_filter); display->clutter_event_filter = 0; } muffin-6.4.1/src/core/workspace.c0000664000175000017500000013304314723361714015627 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004, 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:workspace * @title:MetaWorkspace * @short_description:Workspaces * * A workspace is a set of windows which all live on the same * screen. (You may also see the name "desktop" around the place, * which is the EWMH's name for the same thing.) Only one workspace * of a screen may be active at once; all windows on all other workspaces * are unmapped. */ #include "config.h" #include "meta/workspace.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "cogl/cogl.h" #include "core/boxes-private.h" #include "core/meta-workspace-manager-private.h" #include "core/workspace-private.h" #include "meta/compositor.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "x11/meta-x11-display-private.h" void meta_workspace_queue_calc_showing (MetaWorkspace *workspace); static void focus_ancestor_or_top_window (MetaWorkspace *workspace, MetaWindow *not_this_one, guint32 timestamp); G_DEFINE_TYPE (MetaWorkspace, meta_workspace, G_TYPE_OBJECT); enum { PROP_0, PROP_N_WINDOWS, PROP_WORKSPACE_INDEX, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; enum { WINDOW_ADDED, WINDOW_REMOVED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef struct _MetaWorkspaceLogicalMonitorData { GList *logical_monitor_region; MetaRectangle logical_monitor_work_area; } MetaWorkspaceLogicalMonitorData; typedef struct _MetaWorkspaceFocusableAncestorData { MetaWorkspace *workspace; MetaWindow *out_window; } MetaWorkspaceFocusableAncestorData; static MetaWorkspaceLogicalMonitorData * meta_workspace_get_logical_monitor_data (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor) { if (!workspace->logical_monitor_data) return NULL; return g_hash_table_lookup (workspace->logical_monitor_data, logical_monitor); } static void workspace_logical_monitor_data_free (MetaWorkspaceLogicalMonitorData *data) { g_clear_pointer (&data->logical_monitor_region, meta_rectangle_free_list_and_elements); g_free (data); } static MetaWorkspaceLogicalMonitorData * meta_workspace_ensure_logical_monitor_data (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor) { MetaWorkspaceLogicalMonitorData *data; data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); if (data) return data; if (!workspace->logical_monitor_data) { workspace->logical_monitor_data = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) workspace_logical_monitor_data_free); } data = g_new0 (MetaWorkspaceLogicalMonitorData, 1); g_hash_table_insert (workspace->logical_monitor_data, logical_monitor, data); return data; } static void meta_workspace_clear_logical_monitor_data (MetaWorkspace *workspace) { g_clear_pointer (&workspace->logical_monitor_data, g_hash_table_destroy); } static void meta_workspace_finalize (GObject *object) { /* Actual freeing done in meta_workspace_remove() for now */ G_OBJECT_CLASS (meta_workspace_parent_class)->finalize (object); } static void meta_workspace_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_workspace_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWorkspace *ws = META_WORKSPACE (object); switch (prop_id) { case PROP_N_WINDOWS: /* * This is reliable, but not very efficient; should we store * the list lenth ? */ g_value_set_uint (value, g_list_length (ws->windows)); break; case PROP_WORKSPACE_INDEX: g_value_set_uint (value, meta_workspace_index (ws)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_workspace_class_init (MetaWorkspaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_workspace_finalize; object_class->get_property = meta_workspace_get_property; object_class->set_property = meta_workspace_set_property; signals[WINDOW_ADDED] = g_signal_new ("window-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); signals[WINDOW_REMOVED] = g_signal_new ("window-removed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_WINDOW); obj_props[PROP_N_WINDOWS] = g_param_spec_uint ("n-windows", "N Windows", "Number of windows", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); obj_props[PROP_WORKSPACE_INDEX] = g_param_spec_uint ("workspace-index", "Workspace index", "The workspace's index", 0, G_MAXUINT, 0, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } static void meta_workspace_init (MetaWorkspace *workspace) { } MetaWorkspace * meta_workspace_new (MetaWorkspaceManager *workspace_manager) { MetaDisplay *display = workspace_manager->display; MetaWorkspace *workspace; GSList *windows, *l; workspace = g_object_new (META_TYPE_WORKSPACE, NULL); workspace->display = display; workspace->manager = workspace_manager; workspace_manager->workspaces = g_list_append (workspace_manager->workspaces, workspace); workspace->windows = NULL; workspace->mru_list = NULL; workspace->work_areas_invalid = TRUE; workspace->work_area_screen.x = 0; workspace->work_area_screen.y = 0; workspace->work_area_screen.width = 0; workspace->work_area_screen.height = 0; workspace->screen_region = NULL; workspace->screen_edges = NULL; workspace->monitor_edges = NULL; workspace->list_containing_self = g_list_prepend (NULL, workspace); workspace->builtin_struts = NULL; workspace->all_struts = NULL; workspace->showing_desktop = FALSE; /* make sure sticky windows are in our mru_list */ windows = meta_display_list_windows (display, META_LIST_SORTED); for (l = windows; l; l = l->next) if (meta_window_located_on_workspace (l->data, workspace)) meta_workspace_add_window (workspace, l->data); g_slist_free (windows); return workspace; } /** * workspace_free_all_struts: * @workspace: The workspace. * * Frees the combined struts list of a workspace. */ static void workspace_free_all_struts (MetaWorkspace *workspace) { if (workspace->all_struts == NULL) return; g_slist_free_full (workspace->all_struts, g_free); workspace->all_struts = NULL; } /** * workspace_free_builtin_struts: * @workspace: The workspace. * * Frees the struts list set with meta_workspace_set_builtin_struts */ static void workspace_free_builtin_struts (MetaWorkspace *workspace) { if (workspace->builtin_struts == NULL) return; g_slist_free_full (workspace->builtin_struts, g_free); workspace->builtin_struts = NULL; } /* Ensure that the workspace is empty by making sure that * all of our windows are on-all-workspaces. */ static void assert_workspace_empty (MetaWorkspace *workspace) { GList *l; for (l = workspace->windows; l != NULL; l = l->next) { MetaWindow *window = l->data; g_assert (window->on_all_workspaces); } } void meta_workspace_remove (MetaWorkspace *workspace) { MetaWorkspaceManager *manager = workspace->display->workspace_manager; g_return_if_fail (workspace != manager->active_workspace); assert_workspace_empty (workspace); manager->workspaces = g_list_remove (manager->workspaces, workspace); meta_workspace_clear_logical_monitor_data (workspace); g_list_free (workspace->mru_list); g_list_free (workspace->list_containing_self); workspace_free_builtin_struts (workspace); /* screen.c:update_num_workspaces(), which calls us, removes windows from * workspaces first, which can cause the workareas on the workspace to be * invalidated (and hence for struts/regions/edges to be freed). * So, no point trying to double free it; that causes a crash * anyway. #361804. */ if (!workspace->work_areas_invalid) { workspace_free_all_struts (workspace); meta_rectangle_free_list_and_elements (workspace->screen_region); meta_rectangle_free_list_and_elements (workspace->screen_edges); meta_rectangle_free_list_and_elements (workspace->monitor_edges); } g_object_unref (workspace); /* don't bother to reset names, pagers can just ignore * extra ones */ } void meta_workspace_add_window (MetaWorkspace *workspace, MetaWindow *window) { COGL_TRACE_BEGIN_SCOPED (MetaWorkspaceAddWindow, "Workspace (add window)"); g_assert (g_list_find (workspace->mru_list, window) == NULL); workspace->mru_list = g_list_prepend (workspace->mru_list, window); workspace->windows = g_list_prepend (workspace->windows, window); if (window->struts) { meta_topic (META_DEBUG_WORKAREA, "Invalidating work area of workspace %d since we're adding window %s to it\n", meta_workspace_index (workspace), window->desc); meta_workspace_invalidate_work_area (workspace); } g_signal_emit (workspace, signals[WINDOW_ADDED], 0, window); g_object_notify_by_pspec (G_OBJECT (workspace), obj_props[PROP_N_WINDOWS]); } void meta_workspace_remove_window (MetaWorkspace *workspace, MetaWindow *window) { COGL_TRACE_BEGIN_SCOPED (MetaWorkspaceRemoveWindow, "Workspace (remove window)"); workspace->windows = g_list_remove (workspace->windows, window); workspace->mru_list = g_list_remove (workspace->mru_list, window); g_assert (g_list_find (workspace->mru_list, window) == NULL); if (window->struts) { meta_topic (META_DEBUG_WORKAREA, "Invalidating work area of workspace %d since we're removing window %s from it\n", meta_workspace_index (workspace), window->desc); meta_workspace_invalidate_work_area (workspace); } g_signal_emit (workspace, signals[WINDOW_REMOVED], 0, window); g_object_notify (G_OBJECT (workspace), "n-windows"); } void meta_workspace_relocate_windows (MetaWorkspace *workspace, MetaWorkspace *new_home) { GList *copy, *l; g_return_if_fail (workspace != new_home); /* can't modify list we're iterating over */ copy = g_list_copy (workspace->windows); for (l = copy; l != NULL; l = l->next) { MetaWindow *window = l->data; if (!window->on_all_workspaces) meta_window_change_workspace (window, new_home); } g_list_free (copy); assert_workspace_empty (workspace); } void meta_workspace_queue_calc_showing (MetaWorkspace *workspace) { GList *l; for (l = workspace->windows; l != NULL; l = l->next) meta_window_queue (l->data, META_QUEUE_CALC_SHOWING); } static MetaMotionDirection get_wrapped_horizontal_direction (gint from, gint to, MetaMotionDirection suggested_dir, gint num_workspaces) { MetaMotionDirection ret = 0; gboolean wrap = meta_prefs_get_workspace_cycle(); if (suggested_dir != 0 && wrap) { if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) { if (suggested_dir == META_MOTION_LEFT) suggested_dir = META_MOTION_RIGHT; else if (suggested_dir == META_MOTION_RIGHT) suggested_dir = META_MOTION_LEFT; } return suggested_dir; } if (meta_get_locale_direction () == META_LOCALE_DIRECTION_RTL) { if (from < to) { if (wrap) ret = (to - from) <= ((num_workspaces - to) + from) ? META_MOTION_LEFT : META_MOTION_RIGHT; else ret = META_MOTION_LEFT; } else if (from > to) { if (wrap) ret = (from - to) <= ((num_workspaces - from) + to) ? META_MOTION_RIGHT : META_MOTION_LEFT; else ret = META_MOTION_RIGHT; } } else { if (from < to) { if (wrap) ret = (to - from) <= ((num_workspaces - to) + from) ? META_MOTION_RIGHT : META_MOTION_LEFT; else ret = META_MOTION_RIGHT; } else if (from > to) { if (wrap) ret = (from - to) <= ((num_workspaces - from) + to) ? META_MOTION_LEFT : META_MOTION_RIGHT; else ret = META_MOTION_LEFT; } } return ret; } static void meta_workspace_activate_internal (MetaWorkspace *workspace, MetaWindow *focus_this, MetaMotionDirection suggested_dir, guint32 timestamp) { MetaWorkspace *old; MetaWindow *move_window; MetaCompositor *comp; MetaWorkspaceLayout layout1, layout2; gint num_workspaces, current_space, new_space; MetaMotionDirection direction; meta_verbose ("Activating workspace %d\n", meta_workspace_index (workspace)); if (workspace->manager->active_workspace == workspace) { if (focus_this) meta_window_activate (focus_this, timestamp); return; } /* Free any cached pointers to the workspaces's edges from * a current resize or move operation */ meta_display_cleanup_edges (workspace->display); /* Note that old can be NULL; e.g. when starting up */ old = workspace->manager->active_workspace; workspace->manager->active_workspace = workspace; g_signal_emit_by_name (workspace->manager, "active-workspace-changed"); if (old == NULL) return; /* If the "show desktop" mode is active for either the old workspace * or the new one *but not both*, then update the * _net_showing_desktop hint */ if (old->showing_desktop != workspace->showing_desktop) g_signal_emit_by_name (workspace->manager, "showing-desktop-changed"); move_window = NULL; if (meta_grab_op_is_moving (workspace->display->grab_op)) move_window = workspace->display->grab_window; if (move_window != NULL) { /* We put the window on the new workspace, flip spaces, * then remove from old workspace, so the window * never gets unmapped and we maintain the button grab * on it. * * \bug This comment appears to be the reverse of what happens */ if (!meta_window_located_on_workspace (move_window, workspace)) meta_window_change_workspace (move_window, workspace); } meta_workspace_queue_calc_showing (old); meta_workspace_queue_calc_showing (workspace); /* * Notify the compositor that the active workspace is changing. */ comp = meta_display_get_compositor (workspace->display); direction = 0; current_space = meta_workspace_index (old); new_space = meta_workspace_index (workspace); num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, current_space, &layout1); meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, new_space, &layout2); direction = get_wrapped_horizontal_direction (layout1.current_col, layout2.current_col, suggested_dir, num_workspaces); if (layout1.current_row < layout2.current_row) { if (!direction) direction = META_MOTION_DOWN; else if (direction == META_MOTION_RIGHT) direction = META_MOTION_DOWN_RIGHT; else direction = META_MOTION_DOWN_LEFT; } if (layout1.current_row > layout2.current_row) { if (!direction) direction = META_MOTION_UP; else if (direction == META_MOTION_RIGHT) direction = META_MOTION_UP_RIGHT; else direction = META_MOTION_UP_LEFT; } meta_workspace_manager_free_workspace_layout (&layout1); meta_workspace_manager_free_workspace_layout (&layout2); meta_compositor_switch_workspace (comp, old, workspace, direction); /* This needs to be done after telling the compositor we are switching * workspaces since focusing a window will cause it to be immediately * shown and that would confuse the compositor if it didn't know we * were in a workspace switch. */ if (focus_this) { meta_window_activate (focus_this, timestamp); } else if (move_window) { meta_window_raise (move_window); } else { meta_topic (META_DEBUG_FOCUS, "Focusing default window on new workspace\n"); meta_workspace_focus_default_window (workspace, NULL, timestamp); } meta_workspace_manager_workspace_switched (workspace->manager, current_space, new_space, direction); } /** * meta_workspace_activate_with_focus: * @workspace: a #MetaWorkspace * @focus_this: the #MetaWindow to be focused, or %NULL * @timestamp: timestamp for @focus_this * * Switches to @workspace and possibly activates the window @focus_this. * * The window @focus_this is activated by calling meta_window_activate() * which will unminimize it and transient parents, raise it and give it * the focus. * * If a window is currently being moved by the user, it will be * moved to @workspace. * * The advantage of calling this function instead of meta_workspace_activate() * followed by meta_window_activate() is that it happens as a unit, so * no other window gets focused first before @focus_this. */ void meta_workspace_activate_with_focus (MetaWorkspace *workspace, MetaWindow *focus_this, guint32 timestamp) { meta_workspace_activate_internal (workspace, focus_this, 0, timestamp); } void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp) { meta_workspace_activate_internal (workspace, NULL, 0, timestamp); } /** * meta_workspace_activate_with_direction_hint: * @workspace: a #MetaWorkspace * @direction: the suggested #MetaMotionDirection * @timestamp: timestamp for @focus_this * * Switches to @workspace in the specified @direction (if possible) */ void meta_workspace_activate_with_direction_hint (MetaWorkspace *workspace, MetaMotionDirection direction, guint32 timestamp) { g_return_if_fail (META_IS_WORKSPACE (workspace)); meta_workspace_activate_internal (workspace, NULL, direction, timestamp); } int meta_workspace_index (MetaWorkspace *workspace) { int ret; ret = g_list_index (workspace->manager->workspaces, workspace); /* return value is negative if the workspace is invalid */ return ret; } void meta_workspace_index_changed (MetaWorkspace *workspace) { GList *l; for (l = workspace->windows; l != NULL; l = l->next) { MetaWindow *win = l->data; meta_window_current_workspace_changed (win); } g_object_notify_by_pspec (G_OBJECT (workspace), obj_props[PROP_WORKSPACE_INDEX]); } /** * meta_workspace_list_windows: * @workspace: a #MetaWorkspace * * Gets windows contained on the workspace, including workspace->windows * and also sticky windows. Override-redirect windows are not included. * * Return value: (transfer container) (element-type MetaWindow): the list of windows. */ GList* meta_workspace_list_windows (MetaWorkspace *workspace) { GSList *display_windows, *l; GList *workspace_windows; display_windows = meta_display_list_windows (workspace->display, META_LIST_DEFAULT); workspace_windows = NULL; for (l = display_windows; l != NULL; l = l->next) { MetaWindow *window = l->data; if (meta_window_located_on_workspace (window, workspace)) workspace_windows = g_list_prepend (workspace_windows, window); } g_slist_free (display_windows); return workspace_windows; } void meta_workspace_invalidate_work_area (MetaWorkspace *workspace) { GList *windows, *l; if (workspace->work_areas_invalid) { meta_topic (META_DEBUG_WORKAREA, "Work area for workspace %d is already invalid\n", meta_workspace_index (workspace)); return; } meta_topic (META_DEBUG_WORKAREA, "Invalidating work area for workspace %d\n", meta_workspace_index (workspace)); /* If we are in the middle of a resize or move operation, we * might have cached pointers to the workspace's edges */ if (workspace == workspace->manager->active_workspace) meta_display_cleanup_edges (workspace->display); meta_workspace_clear_logical_monitor_data (workspace); workspace_free_all_struts (workspace); meta_rectangle_free_list_and_elements (workspace->screen_region); meta_rectangle_free_list_and_elements (workspace->screen_edges); meta_rectangle_free_list_and_elements (workspace->monitor_edges); workspace->screen_region = NULL; workspace->screen_edges = NULL; workspace->monitor_edges = NULL; workspace->work_areas_invalid = TRUE; /* redo the size/position constraints on all windows */ windows = meta_workspace_list_windows (workspace); for (l = windows; l != NULL; l = l->next) { MetaWindow *w = l->data; meta_window_queue (w, META_QUEUE_MOVE_RESIZE); } g_list_free (windows); meta_display_queue_workarea_recalc (workspace->display); } static MetaStrut * copy_strut(MetaStrut *original) { return g_memdup2 (original, sizeof (MetaStrut)); } static GSList * copy_strut_list(GSList *original) { GSList *result = NULL; for (; original != NULL; original = original->next) result = g_slist_prepend (result, copy_strut (original->data)); return g_slist_reverse (result); } static void ensure_work_areas_validated (MetaWorkspace *workspace) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *windows; GList *tmp; GList *logical_monitors, *l; MetaRectangle display_rect = { 0 }; MetaRectangle work_area; if (!workspace->work_areas_invalid) return; g_assert (workspace->all_struts == NULL); g_assert (workspace->screen_region == NULL); g_assert (workspace->screen_edges == NULL); g_assert (workspace->monitor_edges == NULL); meta_display_get_size (workspace->display, &display_rect.width, &display_rect.height); /* STEP 1: Get the list of struts */ workspace->all_struts = copy_strut_list (workspace->builtin_struts); windows = meta_workspace_list_windows (workspace); for (tmp = windows; tmp != NULL; tmp = tmp->next) { MetaWindow *win = tmp->data; GSList *s_iter; for (s_iter = win->struts; s_iter != NULL; s_iter = s_iter->next) { workspace->all_struts = g_slist_prepend (workspace->all_struts, copy_strut(s_iter->data)); } } g_list_free (windows); /* STEP 2: Get the maximal/spanning rects for the onscreen and * on-single-monitor regions */ g_assert (workspace->screen_region == NULL); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaWorkspaceLogicalMonitorData *data; g_assert (!meta_workspace_get_logical_monitor_data (workspace, logical_monitor)); data = meta_workspace_ensure_logical_monitor_data (workspace, logical_monitor); data->logical_monitor_region = meta_rectangle_get_minimal_spanning_set_for_region ( &logical_monitor->rect, workspace->all_struts); } workspace->screen_region = meta_rectangle_get_minimal_spanning_set_for_region ( &display_rect, workspace->all_struts); /* STEP 3: Get the work areas (region-to-maximize-to) for the screen and * monitors. */ work_area = display_rect; /* start with the screen */ if (workspace->screen_region == NULL) work_area = meta_rect (0, 0, -1, -1); else meta_rectangle_clip_to_region (workspace->screen_region, FIXED_DIRECTION_NONE, &work_area); /* Lots of paranoia checks, forcing work_area_screen to be sane */ #define MIN_SANE_AREA 100 if (work_area.width < MIN_SANE_AREA) { meta_warning ("struts occupy an unusually large percentage of the screen; " "available remaining width = %d < %d", work_area.width, MIN_SANE_AREA); if (work_area.width < 1) { work_area.x = (display_rect.width - MIN_SANE_AREA)/2; work_area.width = MIN_SANE_AREA; } else { int amount = (MIN_SANE_AREA - work_area.width)/2; work_area.x -= amount; work_area.width += 2*amount; } } if (work_area.height < MIN_SANE_AREA) { meta_warning ("struts occupy an unusually large percentage of the screen; " "available remaining height = %d < %d", work_area.height, MIN_SANE_AREA); if (work_area.height < 1) { work_area.y = (display_rect.height - MIN_SANE_AREA)/2; work_area.height = MIN_SANE_AREA; } else { int amount = (MIN_SANE_AREA - work_area.height)/2; work_area.y -= amount; work_area.height += 2*amount; } } workspace->work_area_screen = work_area; meta_topic (META_DEBUG_WORKAREA, "Computed work area for workspace %d: %d,%d %d x %d\n", meta_workspace_index (workspace), workspace->work_area_screen.x, workspace->work_area_screen.y, workspace->work_area_screen.width, workspace->work_area_screen.height); /* Now find the work areas for each monitor */ for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaWorkspaceLogicalMonitorData *data; data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); work_area = logical_monitor->rect; if (!data->logical_monitor_region) /* FIXME: constraints.c untested with this, but it might be nice for * a screen reader or magnifier. */ work_area = meta_rect (work_area.x, work_area.y, -1, -1); else meta_rectangle_clip_to_region (data->logical_monitor_region, FIXED_DIRECTION_NONE, &work_area); data->logical_monitor_work_area = work_area; meta_topic (META_DEBUG_WORKAREA, "Computed work area for workspace %d " "monitor %d: %d,%d %d x %d\n", meta_workspace_index (workspace), logical_monitor->number, data->logical_monitor_work_area.x, data->logical_monitor_work_area.y, data->logical_monitor_work_area.width, data->logical_monitor_work_area.height); } /* STEP 4: Make sure the screen_region is nonempty (separate from step 2 * since it relies on step 3). */ if (workspace->screen_region == NULL) { MetaRectangle *nonempty_region; nonempty_region = g_new (MetaRectangle, 1); *nonempty_region = workspace->work_area_screen; workspace->screen_region = g_list_prepend (NULL, nonempty_region); } /* STEP 5: Cache screen and monitor edges for edge resistance and snapping */ g_assert (workspace->screen_edges == NULL); g_assert (workspace->monitor_edges == NULL); workspace->screen_edges = meta_rectangle_find_onscreen_edges (&display_rect, workspace->all_struts); tmp = NULL; for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; tmp = g_list_prepend (tmp, &logical_monitor->rect); } workspace->monitor_edges = meta_rectangle_find_nonintersected_monitor_edges (tmp, workspace->all_struts); g_list_free (tmp); /* We're all done, YAAY! Record that everything has been validated. */ workspace->work_areas_invalid = FALSE; } static gboolean strut_lists_equal (GSList *l, GSList *m) { for (; l && m; l = l->next, m = m->next) { MetaStrut *a = l->data; MetaStrut *b = m->data; if (a->side != b->side || !meta_rectangle_equal (&a->rect, &b->rect)) return FALSE; } return l == NULL && m == NULL; } /** * meta_workspace_set_builtin_struts: * @workspace: a #MetaWorkspace * @struts: (element-type Meta.Strut) (transfer none): list of #MetaStrut * * Sets a list of struts that will be used in addition to the struts * of the windows in the workspace when computing the work area of * the workspace. */ void meta_workspace_set_builtin_struts (MetaWorkspace *workspace, GSList *struts) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaDisplay *display = workspace->display; MetaRectangle display_rect = { 0 }; GSList *l; meta_display_get_size (display, &display_rect.width, &display_rect.height); for (l = struts; l; l = l->next) { MetaStrut *strut = l->data; MetaLogicalMonitor *logical_monitor; logical_monitor = meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, &strut->rect); switch (strut->side) { case META_SIDE_TOP: if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, META_DISPLAY_UP)) continue; strut->rect.height += strut->rect.y; strut->rect.y = 0; break; case META_SIDE_BOTTOM: if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, META_DISPLAY_DOWN)) continue; strut->rect.height = display_rect.height - strut->rect.y; break; case META_SIDE_LEFT: if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, META_DISPLAY_LEFT)) continue; strut->rect.width += strut->rect.x; strut->rect.x = 0; break; case META_SIDE_RIGHT: if (meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, logical_monitor, META_DISPLAY_RIGHT)) continue; strut->rect.width = display_rect.width - strut->rect.x; break; } } /* Reordering doesn't actually matter, so we don't catch all * no-impact changes, but this is just a (possibly unnecessary * anyways) optimization */ if (strut_lists_equal (struts, workspace->builtin_struts)) return; workspace_free_builtin_struts (workspace); workspace->builtin_struts = copy_strut_list (struts); meta_workspace_invalidate_work_area (workspace); } void meta_workspace_get_work_area_for_logical_monitor (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor, MetaRectangle *area) { meta_workspace_get_work_area_for_monitor (workspace, logical_monitor->number, area); } /** * meta_workspace_get_work_area_for_monitor: * @workspace: a #MetaWorkspace * @which_monitor: a monitor index * @area: (out): location to store the work area * * Stores the work area for @which_monitor on @workspace * in @area. */ void meta_workspace_get_work_area_for_monitor (MetaWorkspace *workspace, int which_monitor, MetaRectangle *area) { MetaBackend *backend = meta_get_backend(); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; MetaWorkspaceLogicalMonitorData *data; logical_monitor = meta_monitor_manager_get_logical_monitor_from_number (monitor_manager, which_monitor); g_return_if_fail (logical_monitor != NULL); ensure_work_areas_validated (workspace); data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); g_return_if_fail (data != NULL); *area = data->logical_monitor_work_area; } /** * meta_workspace_get_work_area_all_monitors: * @workspace: a #MetaWorkspace * @area: (out): location to store the work area * * Stores the work area in @area. */ void meta_workspace_get_work_area_all_monitors (MetaWorkspace *workspace, MetaRectangle *area) { ensure_work_areas_validated (workspace); *area = workspace->work_area_screen; } GList* meta_workspace_get_onscreen_region (MetaWorkspace *workspace) { ensure_work_areas_validated (workspace); return workspace->screen_region; } GList * meta_workspace_get_onmonitor_region (MetaWorkspace *workspace, MetaLogicalMonitor *logical_monitor) { MetaWorkspaceLogicalMonitorData *data; ensure_work_areas_validated (workspace); data = meta_workspace_get_logical_monitor_data (workspace, logical_monitor); return data->logical_monitor_region; } #ifdef WITH_VERBOSE_MODE static const char * meta_motion_direction_to_string (MetaMotionDirection direction) { switch (direction) { case META_MOTION_UP: return "Up"; case META_MOTION_DOWN: return "Down"; case META_MOTION_LEFT: return "Left"; case META_MOTION_RIGHT: return "Right"; case META_MOTION_UP_RIGHT: return "Up-Right"; case META_MOTION_DOWN_RIGHT: return "Down-Right"; case META_MOTION_UP_LEFT: return "Up-Left"; case META_MOTION_DOWN_LEFT: return "Down-Left"; } return "Unknown"; } #endif /* WITH_VERBOSE_MODE */ /** * meta_workspace_get_neighbor: * @workspace: a #MetaWorkspace * @direction: a #MetaMotionDirection, relative to @workspace * * Calculate and retrive the workspace that is next to @workspace, * according to @direction and the current workspace layout, as set * by meta_screen_override_workspace_layout(). * * Returns: (transfer none): the workspace next to @workspace, or * @workspace itself if the neighbor would be outside the layout */ MetaWorkspace* meta_workspace_get_neighbor (MetaWorkspace *workspace, MetaMotionDirection direction) { MetaWorkspaceLayout layout; int i, current_space, num_workspaces; gboolean ltr, cycle; current_space = meta_workspace_index (workspace); num_workspaces = meta_workspace_manager_get_n_workspaces (workspace->manager); meta_workspace_manager_calc_workspace_layout (workspace->manager, num_workspaces, current_space, &layout); cycle = meta_prefs_get_workspace_cycle(); meta_verbose ("Getting neighbor of %d in direction %s\n", current_space, meta_motion_direction_to_string (direction)); ltr = (meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR); switch (direction) { case META_MOTION_LEFT: layout.current_col -= ltr ? 1 : -1; break; case META_MOTION_RIGHT: layout.current_col += ltr ? 1 : -1; break; case META_MOTION_UP: layout.current_row -= 1; break; case META_MOTION_DOWN: layout.current_row += 1; break; default:; } if (layout.current_col < 0) layout.current_col = (cycle == 1) ? layout.cols - 1 : 0; if (layout.current_col >= layout.cols) layout.current_col = (cycle == 1) ? 0 : layout.cols - 1; if (layout.current_row < 0) layout.current_row = 0; if (layout.current_row >= layout.rows) layout.current_row = layout.rows - 1; i = layout.grid[layout.current_row * layout.cols + layout.current_col]; if (i < 0) i = current_space; if (i >= num_workspaces) meta_bug ("calc_workspace_layout left an invalid (too-high) workspace number %d in the grid\n", i); meta_verbose ("Neighbor workspace is %d at row %d col %d\n", i, layout.current_row, layout.current_col); meta_workspace_manager_free_workspace_layout (&layout); return meta_workspace_manager_get_workspace_by_index (workspace->manager, i); } const char* meta_workspace_get_name (MetaWorkspace *workspace) { return meta_prefs_get_workspace_name (meta_workspace_index (workspace)); } void meta_workspace_focus_default_window (MetaWorkspace *workspace, MetaWindow *not_this_one, guint32 timestamp) { if (timestamp == META_CURRENT_TIME) meta_warning ("META_CURRENT_TIME used to choose focus window; " "focus window may not be correct.\n"); if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK || !workspace->display->mouse_mode) focus_ancestor_or_top_window (workspace, not_this_one, timestamp); else { MetaWindow * window; window = meta_display_get_pointer_window (workspace->display, not_this_one); if (window && window->type != META_WINDOW_DOCK && window->type != META_WINDOW_DESKTOP) { if (timestamp == META_CURRENT_TIME) { /* We would like for this to never happen. However, if * it does happen then we kludge since using META_CURRENT_TIME * can mean ugly race conditions--and we can avoid these * by allowing EnterNotify events (which come with * timestamps) to handle focus. */ meta_topic (META_DEBUG_FOCUS, "Not focusing mouse window %s because EnterNotify events should handle that\n", window->desc); } else { meta_topic (META_DEBUG_FOCUS, "Focusing mouse window %s\n", window->desc); meta_window_focus (window, timestamp); } if (workspace->display->autoraise_window != window && meta_prefs_get_auto_raise ()) { meta_display_queue_autoraise_callback (workspace->display, window); } } else if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_SLOPPY) focus_ancestor_or_top_window (workspace, not_this_one, timestamp); else if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_MOUSE) { meta_topic (META_DEBUG_FOCUS, "Setting focus to no_focus_window, since no valid " "window to focus found.\n"); meta_display_unset_input_focus (workspace->display, timestamp); } } } static gboolean find_focusable_ancestor (MetaWindow *window, gpointer user_data) { MetaWorkspaceFocusableAncestorData *data = user_data; if (!window->unmanaging && window->mapped && !window->hidden && meta_window_is_focusable (window) && meta_window_located_on_workspace (window, data->workspace) && meta_window_showing_on_its_workspace (window)) { data->out_window = window; return FALSE; } return TRUE; } static gboolean try_to_set_focus_and_check (MetaWindow *window, MetaWindow *not_this_one, uint32_t timestamp) { meta_window_focus (window, timestamp); /* meta_focus_window() will not change focus for clients using the * "globally active input" model of input handling, hence defeating * the assumption that focus should be changed for such windows. * See https://tronche.com/gui/x/icccm/sec-4.html#s-4.1.7 */ if (meta_window_is_focus_async (window)) return TRUE; /* meta_window_focus() does not guarantee that focus will end up * where we expect, it can fail for various reasons, better check * it did not actually changed or even left focus to the window we * explicitly want to avoid. */ if (not_this_one && meta_display_get_focus_window (window->display) == not_this_one) { meta_warning ("Failed to focus window %s while avoiding %s", window->desc, not_this_one->desc); return FALSE; } return TRUE; } /* Focus ancestor of not_this_one if there is one */ static void focus_ancestor_or_top_window (MetaWorkspace *workspace, MetaWindow *not_this_one, guint32 timestamp) { MetaWindow *window = NULL; if (not_this_one) meta_topic (META_DEBUG_FOCUS, "Focusing MRU window excluding %s\n", not_this_one->desc); else meta_topic (META_DEBUG_FOCUS, "Focusing MRU window\n"); /* First, check to see if we need to focus an ancestor of a window */ if (not_this_one) { MetaWindow *ancestor; MetaWorkspaceFocusableAncestorData data; data = (MetaWorkspaceFocusableAncestorData) { .workspace = workspace, }; meta_window_foreach_ancestor (not_this_one, find_focusable_ancestor, &data); ancestor = data.out_window; if (ancestor) { meta_topic (META_DEBUG_FOCUS, "Focusing %s, ancestor of %s\n", ancestor->desc, not_this_one->desc); if (try_to_set_focus_and_check (ancestor, not_this_one, timestamp)) { /* Also raise the window if in click-to-focus */ if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK) meta_window_raise (ancestor); return; } } } window = meta_stack_get_default_focus_window (workspace->display->stack, workspace, not_this_one); if (window) { meta_topic (META_DEBUG_FOCUS, "Focusing workspace MRU window %s\n", window->desc); if (try_to_set_focus_and_check (window, not_this_one, timestamp)) { /* Also raise the window if in click-to-focus */ if (meta_prefs_get_focus_mode () == C_DESKTOP_FOCUS_MODE_CLICK) meta_window_raise (window); return; } } meta_topic (META_DEBUG_FOCUS, "No MRU window to focus found; focusing no_focus_window."); meta_display_unset_input_focus (workspace->display, timestamp); } /** * meta_workspace_get_display: * @workspace: a #MetaWorkspace * * Gets the #MetaDisplay that the workspace is part of. * * Return value: (transfer none): the #MetaDisplay for the workspace */ MetaDisplay * meta_workspace_get_display (MetaWorkspace *workspace) { return workspace->display; } muffin-6.4.1/src/core/meta-inhibit-shortcuts-dialog-default.c0000664000175000017500000001027414723361714023116 0ustar fabiofabio/* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2004 Elijah Newren * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "core/meta-inhibit-shortcuts-dialog-default-private.h" #include "core/util-private.h" #include "core/window-private.h" #include "meta/meta-inhibit-shortcuts-dialog.h" typedef struct _MetaInhibitShortcutsDialogDefaultPrivate MetaInhibitShortcutsDialogDefaultPrivate; struct _MetaInhibitShortcutsDialogDefault { GObject parent_instance; MetaWindow *window; }; enum { PROP_0, PROP_WINDOW, N_PROPS }; static void meta_inhibit_shortcuts_dialog_iface_init (MetaInhibitShortcutsDialogInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaInhibitShortcutsDialogDefault, meta_inhibit_shortcuts_dialog_default, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (META_TYPE_INHIBIT_SHORTCUTS_DIALOG, meta_inhibit_shortcuts_dialog_iface_init)) static void meta_inhibit_shortcuts_dialog_default_show (MetaInhibitShortcutsDialog *dialog) { /* Default to allow shortcuts inhibitor, but complain that no dialog is implemented */ g_warning ("No MetaInhibitShortcutDialog implementation, falling back on allowing"); meta_inhibit_shortcuts_dialog_response (dialog, META_INHIBIT_SHORTCUTS_DIALOG_RESPONSE_ALLOW); } static void meta_inhibit_shortcuts_dialog_default_hide (MetaInhibitShortcutsDialog *dialog) { } static void meta_inhibit_shortcuts_dialog_iface_init (MetaInhibitShortcutsDialogInterface *iface) { iface->show = meta_inhibit_shortcuts_dialog_default_show; iface->hide = meta_inhibit_shortcuts_dialog_default_hide; } static void meta_inhibit_shortcuts_dialog_default_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaInhibitShortcutsDialogDefault *dialog; dialog = META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (object); switch (prop_id) { case PROP_WINDOW: dialog->window = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_inhibit_shortcuts_dialog_default_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaInhibitShortcutsDialogDefault *dialog; dialog = META_INHIBIT_SHORTCUTS_DIALOG_DEFAULT (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, dialog->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_inhibit_shortcuts_dialog_default_class_init (MetaInhibitShortcutsDialogDefaultClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_inhibit_shortcuts_dialog_default_set_property; object_class->get_property = meta_inhibit_shortcuts_dialog_default_get_property; g_object_class_override_property (object_class, PROP_WINDOW, "window"); } static void meta_inhibit_shortcuts_dialog_default_init (MetaInhibitShortcutsDialogDefault *dialog) { } MetaInhibitShortcutsDialog * meta_inhibit_shortcuts_dialog_default_new (MetaWindow *window) { return g_object_new (META_TYPE_INHIBIT_SHORTCUTS_DIALOG_DEFAULT, "window", window, NULL); } muffin-6.4.1/src/core/delete.c0000664000175000017500000000655714723361714015104 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window deletion */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2004 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #define _XOPEN_SOURCE /* for kill() */ #include "config.h" #include #include "compositor/compositor-private.h" #include "core/util-private.h" #include "core/window-private.h" #include "meta/meta-x11-errors.h" #include "meta/workspace.h" static void close_dialog_response_cb (MetaCloseDialog *dialog, MetaCloseDialogResponse response, MetaWindow *window) { if (response == META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE) meta_window_kill (window); } static void meta_window_ensure_close_dialog (MetaWindow *window) { MetaDisplay *display; if (window->close_dialog) return; display = window->display; window->close_dialog = meta_compositor_create_close_dialog (display->compositor, window); g_signal_connect (window->close_dialog, "response", G_CALLBACK (close_dialog_response_cb), window); } void meta_window_set_alive (MetaWindow *window, gboolean is_alive) { if (is_alive && window->close_dialog) { meta_close_dialog_hide (window->close_dialog); } else if (!is_alive) { meta_window_ensure_close_dialog (window); meta_close_dialog_show (window->close_dialog); if (window->display && window->display->event_route == META_EVENT_ROUTE_NORMAL && window == window->display->focus_window) meta_close_dialog_focus (window->close_dialog); } } void meta_window_check_alive (MetaWindow *window, guint32 timestamp) { meta_display_ping_window (window, timestamp); } void meta_window_delete (MetaWindow *window, guint32 timestamp) { META_WINDOW_GET_CLASS (window)->delete (window, timestamp); meta_window_check_alive (window, timestamp); } void meta_window_kill (MetaWindow *window) { pid_t pid = meta_window_get_client_pid (window); if (pid > 0) { meta_topic (META_DEBUG_WINDOW_OPS, "Killing %s with kill()\n", window->desc); if (kill (pid, 9) == 0) return; meta_topic (META_DEBUG_WINDOW_OPS, "Failed to signal %s: %s\n", window->desc, strerror (errno)); } META_WINDOW_GET_CLASS (window)->kill (window); } void meta_window_free_delete_dialog (MetaWindow *window) { if (window->close_dialog && meta_close_dialog_is_visible (window->close_dialog)) meta_close_dialog_hide (window->close_dialog); g_clear_object (&window->close_dialog); } muffin-6.4.1/src/backends/0000775000175000017500000000000014723361714014303 5ustar fabiofabiomuffin-6.4.1/src/backends/meta-dnd-private.h0000664000175000017500000000267714723361714017631 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Hyungwon Hwang * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_DND_PRIVATE__H #define META_DND_PRIVATE__H #include #include #include "backends/meta-backend-private.h" #include "compositor/meta-compositor-x11.h" gboolean meta_dnd_handle_xdnd_event (MetaBackend *backend, MetaCompositorX11 *compositor_x11, Display *xdisplay, XEvent *xev); void meta_dnd_init_xdnd (MetaX11Display *x11_display); #ifdef HAVE_WAYLAND void meta_dnd_wayland_handle_begin_modal (MetaCompositor *compositor); void meta_dnd_wayland_handle_end_modal (MetaCompositor *compositor); #endif #endif /* META_DND_PRIVATE_H */ muffin-6.4.1/src/backends/meta-pointer-constraint.h0000664000175000017500000000432514723361714021246 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_POINTER_CONSTRAINT_H #define META_POINTER_CONSTRAINT_H #include #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_POINTER_CONSTRAINT (meta_pointer_constraint_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaPointerConstraint, meta_pointer_constraint, META, POINTER_CONSTRAINT, GObject); /** * MetaPointerConstraintClass: * @constrain: the virtual function pointer for * meta_pointer_constraint_constrain(). */ struct _MetaPointerConstraintClass { GObjectClass parent_class; void (*constrain) (MetaPointerConstraint *constraint, ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y); }; void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint, ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y); G_END_DECLS #endif /* META_POINTER_CONSTRAINT_H */ muffin-6.4.1/src/backends/meta-output.h0000664000175000017500000000723714723361714016751 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_OUTPUT_H #define META_OUTPUT_H #include #include "backends/meta-backend-types.h" #include "backends/meta-gpu.h" #include "core/util-private.h" struct _MetaTileInfo { guint32 group_id; guint32 flags; guint32 max_h_tiles; guint32 max_v_tiles; guint32 loc_h_tile; guint32 loc_v_tile; guint32 tile_w; guint32 tile_h; }; /* This matches the values in drm_mode.h */ typedef enum { META_CONNECTOR_TYPE_Unknown = 0, META_CONNECTOR_TYPE_VGA = 1, META_CONNECTOR_TYPE_DVII = 2, META_CONNECTOR_TYPE_DVID = 3, META_CONNECTOR_TYPE_DVIA = 4, META_CONNECTOR_TYPE_Composite = 5, META_CONNECTOR_TYPE_SVIDEO = 6, META_CONNECTOR_TYPE_LVDS = 7, META_CONNECTOR_TYPE_Component = 8, META_CONNECTOR_TYPE_9PinDIN = 9, META_CONNECTOR_TYPE_DisplayPort = 10, META_CONNECTOR_TYPE_HDMIA = 11, META_CONNECTOR_TYPE_HDMIB = 12, META_CONNECTOR_TYPE_TV = 13, META_CONNECTOR_TYPE_eDP = 14, META_CONNECTOR_TYPE_VIRTUAL = 15, META_CONNECTOR_TYPE_DSI = 16, } MetaConnectorType; struct _MetaOutput { GObject parent; MetaGpu *gpu; /* The low-level ID of this output, used to apply back configuration */ uint64_t winsys_id; char *name; char *vendor; char *product; char *serial; int width_mm; int height_mm; CoglSubpixelOrder subpixel_order; MetaConnectorType connector_type; MetaMonitorTransform panel_orientation_transform; MetaCrtcMode *preferred_mode; MetaCrtcMode **modes; unsigned int n_modes; MetaCrtc **possible_crtcs; unsigned int n_possible_crtcs; MetaOutput **possible_clones; unsigned int n_possible_clones; int backlight; int backlight_min; int backlight_max; /* Used when changing configuration */ gboolean is_dirty; gboolean is_primary; gboolean is_presentation; gboolean is_underscanning; gboolean supports_underscanning; gpointer driver_private; GDestroyNotify driver_notify; /* * Get a new preferred mode on hotplug events, to handle dynamic guest * resizing. */ gboolean hotplug_mode_update; gint suggested_x; gint suggested_y; MetaTileInfo tile_info; }; #define META_TYPE_OUTPUT (meta_output_get_type ()) META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaOutput, meta_output, META, OUTPUT, GObject) META_EXPORT_TEST MetaGpu * meta_output_get_gpu (MetaOutput *output); META_EXPORT_TEST void meta_output_assign_crtc (MetaOutput *output, MetaCrtc *crtc); META_EXPORT_TEST void meta_output_unassign_crtc (MetaOutput *output); META_EXPORT_TEST MetaCrtc * meta_output_get_assigned_crtc (MetaOutput *output); MetaMonitorTransform meta_output_logical_to_crtc_transform (MetaOutput *output, MetaMonitorTransform transform); MetaMonitorTransform meta_output_crtc_to_logical_transform (MetaOutput *output, MetaMonitorTransform transform); #endif /* META_OUTPUT_H */ muffin-6.4.1/src/backends/edid-parse.c0000664000175000017500000003250414723361714016470 0ustar fabiofabio/* * Copyright 2007 Red Hat, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /* Author: Soren Sandmann */ #include "config.h" #include #include #include #include #include "backends/edid.h" static int get_bit (int in, int bit) { return (in & (1 << bit)) >> bit; } static int get_bits (int in, int begin, int end) { int mask = (1 << (end - begin + 1)) - 1; return (in >> begin) & mask; } static int decode_header (const uchar *edid) { if (memcmp (edid, "\x00\xff\xff\xff\xff\xff\xff\x00", 8) == 0) return TRUE; return FALSE; } static int decode_vendor_and_product_identification (const uchar *edid, MonitorInfo *info) { int is_model_year; /* Manufacturer Code */ info->manufacturer_code[0] = get_bits (edid[0x08], 2, 6); info->manufacturer_code[1] = get_bits (edid[0x08], 0, 1) << 3; info->manufacturer_code[1] |= get_bits (edid[0x09], 5, 7); info->manufacturer_code[2] = get_bits (edid[0x09], 0, 4); info->manufacturer_code[3] = '\0'; info->manufacturer_code[0] += 'A' - 1; info->manufacturer_code[1] += 'A' - 1; info->manufacturer_code[2] += 'A' - 1; /* Product Code */ info->product_code = edid[0x0b] << 8 | edid[0x0a]; /* Serial Number */ info->serial_number = edid[0x0c] | edid[0x0d] << 8 | edid[0x0e] << 16 | edid[0x0f] << 24; /* Week and Year */ is_model_year = FALSE; switch (edid[0x10]) { case 0x00: info->production_week = -1; break; case 0xff: info->production_week = -1; is_model_year = TRUE; break; default: info->production_week = edid[0x10]; break; } if (is_model_year) { info->production_year = -1; info->model_year = 1990 + edid[0x11]; } else { info->production_year = 1990 + edid[0x11]; info->model_year = -1; } return TRUE; } static int decode_edid_version (const uchar *edid, MonitorInfo *info) { info->major_version = edid[0x12]; info->minor_version = edid[0x13]; return TRUE; } static int decode_display_parameters (const uchar *edid, MonitorInfo *info) { /* Digital vs Analog */ info->is_digital = get_bit (edid[0x14], 7); if (info->is_digital) { int bits; static const int bit_depth[8] = { -1, 6, 8, 10, 12, 14, 16, -1 }; static const Interface interfaces[6] = { UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT }; bits = get_bits (edid[0x14], 4, 6); info->connector.digital.bits_per_primary = bit_depth[bits]; bits = get_bits (edid[0x14], 0, 3); if (bits <= 5) info->connector.digital.interface = interfaces[bits]; else info->connector.digital.interface = UNDEFINED; } else { int bits = get_bits (edid[0x14], 5, 6); static const double levels[][3] = { { 0.7, 0.3, 1.0 }, { 0.714, 0.286, 1.0 }, { 1.0, 0.4, 1.4 }, { 0.7, 0.0, 0.7 }, }; info->connector.analog.video_signal_level = levels[bits][0]; info->connector.analog.sync_signal_level = levels[bits][1]; info->connector.analog.total_signal_level = levels[bits][2]; info->connector.analog.blank_to_black = get_bit (edid[0x14], 4); info->connector.analog.separate_hv_sync = get_bit (edid[0x14], 3); info->connector.analog.composite_sync_on_h = get_bit (edid[0x14], 2); info->connector.analog.composite_sync_on_green = get_bit (edid[0x14], 1); info->connector.analog.serration_on_vsync = get_bit (edid[0x14], 0); } /* Screen Size / Aspect Ratio */ if (edid[0x15] == 0 && edid[0x16] == 0) { info->width_mm = -1; info->height_mm = -1; info->aspect_ratio = -1.0; } else if (edid[0x16] == 0) { info->width_mm = -1; info->height_mm = -1; info->aspect_ratio = 100.0 / (edid[0x15] + 99); } else if (edid[0x15] == 0) { info->width_mm = -1; info->height_mm = -1; info->aspect_ratio = 100.0 / (edid[0x16] + 99); info->aspect_ratio = 1/info->aspect_ratio; /* portrait */ } else { info->width_mm = 10 * edid[0x15]; info->height_mm = 10 * edid[0x16]; } /* Gamma */ if (edid[0x17] == 0xFF) info->gamma = -1.0; else info->gamma = (edid[0x17] + 100.0) / 100.0; /* Features */ info->standby = get_bit (edid[0x18], 7); info->suspend = get_bit (edid[0x18], 6); info->active_off = get_bit (edid[0x18], 5); if (info->is_digital) { info->connector.digital.rgb444 = TRUE; if (get_bit (edid[0x18], 3)) info->connector.digital.ycrcb444 = 1; if (get_bit (edid[0x18], 4)) info->connector.digital.ycrcb422 = 1; } else { int bits = get_bits (edid[0x18], 3, 4); ColorType color_type[4] = { MONOCHROME, RGB, OTHER_COLOR, UNDEFINED_COLOR }; info->connector.analog.color_type = color_type[bits]; } info->srgb_is_standard = get_bit (edid[0x18], 2); /* In 1.3 this is called "has preferred timing" */ info->preferred_timing_includes_native = get_bit (edid[0x18], 1); /* FIXME: In 1.3 this indicates whether the monitor accepts GTF */ info->continuous_frequency = get_bit (edid[0x18], 0); return TRUE; } static double decode_fraction (int high, int low) { double result = 0.0; int i; high = (high << 2) | low; for (i = 0; i < 10; ++i) result += get_bit (high, i) * pow (2, i - 10); return result; } static int decode_color_characteristics (const uchar *edid, MonitorInfo *info) { info->red_x = decode_fraction (edid[0x1b], get_bits (edid[0x19], 6, 7)); info->red_y = decode_fraction (edid[0x1c], get_bits (edid[0x19], 5, 4)); info->green_x = decode_fraction (edid[0x1d], get_bits (edid[0x19], 2, 3)); info->green_y = decode_fraction (edid[0x1e], get_bits (edid[0x19], 0, 1)); info->blue_x = decode_fraction (edid[0x1f], get_bits (edid[0x1a], 6, 7)); info->blue_y = decode_fraction (edid[0x20], get_bits (edid[0x1a], 4, 5)); info->white_x = decode_fraction (edid[0x21], get_bits (edid[0x1a], 2, 3)); info->white_y = decode_fraction (edid[0x22], get_bits (edid[0x1a], 0, 1)); return TRUE; } static int decode_established_timings (const uchar *edid, MonitorInfo *info) { static const Timing established[][8] = { { { 800, 600, 60 }, { 800, 600, 56 }, { 640, 480, 75 }, { 640, 480, 72 }, { 640, 480, 67 }, { 640, 480, 60 }, { 720, 400, 88 }, { 720, 400, 70 } }, { { 1280, 1024, 75 }, { 1024, 768, 75 }, { 1024, 768, 70 }, { 1024, 768, 60 }, { 1024, 768, 87 }, { 832, 624, 75 }, { 800, 600, 75 }, { 800, 600, 72 } }, { { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 0, 0, 0 }, { 1152, 870, 75 } }, }; int i, j, idx; idx = 0; for (i = 0; i < 3; ++i) { for (j = 0; j < 8; ++j) { int byte = edid[0x23 + i]; if (get_bit (byte, j) && established[i][j].frequency != 0) info->established[idx++] = established[i][j]; } } return TRUE; } static int decode_standard_timings (const uchar *edid, MonitorInfo *info) { int i; for (i = 0; i < 8; i++) { int first = edid[0x26 + 2 * i]; int second = edid[0x27 + 2 * i]; if (first != 0x01 && second != 0x01) { int w = 8 * (first + 31); int h = 0; switch (get_bits (second, 6, 7)) { case 0x00: h = (w / 16) * 10; break; case 0x01: h = (w / 4) * 3; break; case 0x02: h = (w / 5) * 4; break; case 0x03: h = (w / 16) * 9; break; } info->standard[i].width = w; info->standard[i].height = h; info->standard[i].frequency = get_bits (second, 0, 5) + 60; } } return TRUE; } static void decode_lf_string (const uchar *s, int n_chars, char *result) { int i; for (i = 0; i < n_chars; ++i) { if (s[i] == 0x0a) { *result++ = '\0'; break; } else if (s[i] == 0x00) { /* Convert embedded 0's to spaces */ *result++ = ' '; } else { *result++ = s[i]; } } } static void decode_display_descriptor (const uchar *desc, MonitorInfo *info) { switch (desc[0x03]) { case 0xFC: decode_lf_string (desc + 5, 13, info->dsc_product_name); break; case 0xFF: decode_lf_string (desc + 5, 13, info->dsc_serial_number); break; case 0xFE: decode_lf_string (desc + 5, 13, info->dsc_string); break; case 0xFD: /* Range Limits */ break; case 0xFB: /* Color Point */ break; case 0xFA: /* Timing Identifications */ break; case 0xF9: /* Color Management */ break; case 0xF8: /* Timing Codes */ break; case 0xF7: /* Established Timings */ break; case 0x10: break; } } static void decode_detailed_timing (const uchar *timing, DetailedTiming *detailed) { int bits; StereoType stereo[] = { NO_STEREO, NO_STEREO, FIELD_RIGHT, FIELD_LEFT, TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE }; detailed->pixel_clock = (timing[0x00] | timing[0x01] << 8) * 10000; detailed->h_addr = timing[0x02] | ((timing[0x04] & 0xf0) << 4); detailed->h_blank = timing[0x03] | ((timing[0x04] & 0x0f) << 8); detailed->v_addr = timing[0x05] | ((timing[0x07] & 0xf0) << 4); detailed->v_blank = timing[0x06] | ((timing[0x07] & 0x0f) << 8); detailed->h_front_porch = timing[0x08] | get_bits (timing[0x0b], 6, 7) << 8; detailed->h_sync = timing[0x09] | get_bits (timing[0x0b], 4, 5) << 8; detailed->v_front_porch = get_bits (timing[0x0a], 4, 7) | get_bits (timing[0x0b], 2, 3) << 4; detailed->v_sync = get_bits (timing[0x0a], 0, 3) | get_bits (timing[0x0b], 0, 1) << 4; detailed->width_mm = timing[0x0c] | get_bits (timing[0x0e], 4, 7) << 8; detailed->height_mm = timing[0x0d] | get_bits (timing[0x0e], 0, 3) << 8; detailed->right_border = timing[0x0f]; detailed->top_border = timing[0x10]; detailed->interlaced = get_bit (timing[0x11], 7); /* Stereo */ bits = get_bits (timing[0x11], 5, 6) << 1 | get_bit (timing[0x11], 0); detailed->stereo = stereo[bits]; /* Sync */ bits = timing[0x11]; detailed->digital_sync = get_bit (bits, 4); if (detailed->digital_sync) { detailed->connector.digital.composite = !get_bit (bits, 3); if (detailed->connector.digital.composite) { detailed->connector.digital.serrations = get_bit (bits, 2); detailed->connector.digital.negative_vsync = FALSE; } else { detailed->connector.digital.serrations = FALSE; detailed->connector.digital.negative_vsync = !get_bit (bits, 2); } detailed->connector.digital.negative_hsync = !get_bit (bits, 0); } else { detailed->connector.analog.bipolar = get_bit (bits, 3); detailed->connector.analog.serrations = get_bit (bits, 2); detailed->connector.analog.sync_on_green = !get_bit (bits, 1); } } static int decode_descriptors (const uchar *edid, MonitorInfo *info) { int i; int timing_idx; timing_idx = 0; for (i = 0; i < 4; ++i) { int index = 0x36 + i * 18; if (edid[index + 0] == 0x00 && edid[index + 1] == 0x00) { decode_display_descriptor (edid + index, info); } else { decode_detailed_timing (edid + index, &(info->detailed_timings[timing_idx++])); } } info->n_detailed_timings = timing_idx; return TRUE; } static void decode_check_sum (const uchar *edid, MonitorInfo *info) { int i; uchar check = 0; for (i = 0; i < 128; ++i) check += edid[i]; info->checksum = check; } MonitorInfo * decode_edid (const uchar *edid) { MonitorInfo *info = g_new0 (MonitorInfo, 1); decode_check_sum (edid, info); if (decode_header (edid) && decode_vendor_and_product_identification (edid, info) && decode_edid_version (edid, info) && decode_display_parameters (edid, info) && decode_color_characteristics (edid, info) && decode_established_timings (edid, info) && decode_standard_timings (edid, info) && decode_descriptors (edid, info)) { return info; } else { g_free (info); return NULL; } } muffin-6.4.1/src/backends/meta-screen-cast-monitor-stream.c0000664000175000017500000002333414723361714022565 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-screen-cast-monitor-stream-src.h" enum { PROP_0, PROP_MONITOR, }; struct _MetaScreenCastMonitorStream { MetaScreenCastStream parent; ClutterStage *stage; MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; }; G_DEFINE_TYPE (MetaScreenCastMonitorStream, meta_screen_cast_monitor_stream, META_TYPE_SCREEN_CAST_STREAM) static gboolean update_monitor (MetaScreenCastMonitorStream *monitor_stream, MetaMonitor *new_monitor) { MetaLogicalMonitor *new_logical_monitor; new_logical_monitor = meta_monitor_get_logical_monitor (new_monitor); if (!new_logical_monitor) return FALSE; if (!meta_rectangle_equal (&new_logical_monitor->rect, &monitor_stream->logical_monitor->rect)) return FALSE; g_set_object (&monitor_stream->monitor, new_monitor); g_set_object (&monitor_stream->logical_monitor, new_logical_monitor); return TRUE; } static void on_monitors_changed (MetaMonitorManager *monitor_manager, MetaScreenCastMonitorStream *monitor_stream) { MetaMonitor *new_monitor = NULL; GList *monitors; GList *l; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *other_monitor = l->data; if (meta_monitor_is_same_as (monitor_stream->monitor, other_monitor)) { new_monitor = other_monitor; break; } } if (!new_monitor || !update_monitor (monitor_stream, new_monitor)) meta_screen_cast_stream_close (META_SCREEN_CAST_STREAM (monitor_stream)); } ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream) { return monitor_stream->stage; } MetaMonitor * meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monitor_stream) { return monitor_stream->monitor; } MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, GDBusConnection *connection, MetaMonitor *monitor, ClutterStage *stage, MetaScreenCastCursorMode cursor_mode, GError **error) { MetaGpu *gpu = meta_monitor_get_gpu (monitor); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaScreenCastMonitorStream *monitor_stream; if (!meta_monitor_is_active (monitor)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor not active"); return NULL; } monitor_stream = g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM, NULL, error, "session", session, "connection", connection, "cursor-mode", cursor_mode, "monitor", monitor, NULL); if (!monitor_stream) return NULL; monitor_stream->stage = stage; g_signal_connect_object (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), monitor_stream, 0); return monitor_stream; } static MetaScreenCastStreamSrc * meta_screen_cast_monitor_stream_create_src (MetaScreenCastStream *stream, GError **error) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (stream); MetaScreenCastMonitorStreamSrc *monitor_stream_src; monitor_stream_src = meta_screen_cast_monitor_stream_src_new (monitor_stream, error); if (!monitor_stream_src) return NULL; return META_SCREEN_CAST_STREAM_SRC (monitor_stream_src); } static void meta_screen_cast_monitor_stream_set_parameters (MetaScreenCastStream *stream, GVariantBuilder *parameters_builder) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (stream); MetaRectangle logical_monitor_layout; logical_monitor_layout = meta_logical_monitor_get_layout (monitor_stream->logical_monitor); g_variant_builder_add (parameters_builder, "{sv}", "position", g_variant_new ("(ii)", logical_monitor_layout.x, logical_monitor_layout.y)); g_variant_builder_add (parameters_builder, "{sv}", "size", g_variant_new ("(ii)", logical_monitor_layout.width, logical_monitor_layout.height)); } static void meta_screen_cast_monitor_stream_transform_position (MetaScreenCastStream *stream, double stream_x, double stream_y, double *x, double *y) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (stream); MetaRectangle logical_monitor_layout; logical_monitor_layout = meta_logical_monitor_get_layout (monitor_stream->logical_monitor); *x = logical_monitor_layout.x + stream_x; *y = logical_monitor_layout.y + stream_y; } static void meta_screen_cast_monitor_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (object); MetaLogicalMonitor *logical_monitor; switch (prop_id) { case PROP_MONITOR: g_set_object (&monitor_stream->monitor, g_value_get_object (value)); logical_monitor = meta_monitor_get_logical_monitor (monitor_stream->monitor); g_set_object (&monitor_stream->logical_monitor, logical_monitor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_monitor_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (object); switch (prop_id) { case PROP_MONITOR: g_value_set_object (value, monitor_stream->monitor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_monitor_stream_finalize (GObject *object) { MetaScreenCastMonitorStream *monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (object); g_clear_object (&monitor_stream->monitor); g_clear_object (&monitor_stream->logical_monitor); G_OBJECT_CLASS (meta_screen_cast_monitor_stream_parent_class)->finalize (object); } static void meta_screen_cast_monitor_stream_init (MetaScreenCastMonitorStream *monitor_stream) { } static void meta_screen_cast_monitor_stream_class_init (MetaScreenCastMonitorStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaScreenCastStreamClass *stream_class = META_SCREEN_CAST_STREAM_CLASS (klass); object_class->set_property = meta_screen_cast_monitor_stream_set_property; object_class->get_property = meta_screen_cast_monitor_stream_get_property; object_class->finalize = meta_screen_cast_monitor_stream_finalize; stream_class->create_src = meta_screen_cast_monitor_stream_create_src; stream_class->set_parameters = meta_screen_cast_monitor_stream_set_parameters; stream_class->transform_position = meta_screen_cast_monitor_stream_transform_position; g_object_class_install_property (object_class, PROP_MONITOR, g_param_spec_object ("monitor", "monitor", "MetaMonitor", META_TYPE_MONITOR, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } muffin-6.4.1/src/backends/meta-monitor.c0000664000175000017500000015265614723361714017101 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-monitor.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-gpu.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-settings-private.h" #include "backends/meta-output.h" #include "core/boxes-private.h" #define SCALE_FACTORS_PER_INTEGER 4 #define SCALE_FACTORS_STEPS (1.0 / (float) SCALE_FACTORS_PER_INTEGER) #define MINIMUM_SCALE_FACTOR 0.74f #define MAXIMUM_SCALE_FACTOR 4.0f #define MINIMUM_LOGICAL_AREA (800 * 480) #define MAXIMUM_REFRESH_RATE_DIFF 0.001 typedef struct _MetaMonitorMode { MetaMonitor *monitor; char *id; MetaMonitorModeSpec spec; MetaMonitorCrtcMode *crtc_modes; } MetaMonitorMode; typedef struct _MetaMonitorModeTiled { MetaMonitorMode parent; gboolean is_tiled; } MetaMonitorModeTiled; typedef struct _MetaMonitorPrivate { MetaGpu *gpu; GList *outputs; GList *modes; GHashTable *mode_ids; MetaMonitorMode *preferred_mode; MetaMonitorMode *current_mode; MetaMonitorSpec *spec; MetaLogicalMonitor *logical_monitor; /* * The primary or first output for this monitor, 0 if we can't figure out. * It can be matched to a winsys_id of a MetaOutput. * * This is used as an opaque token on reconfiguration when switching from * clone to extened, to decide on what output the windows should go next * (it's an attempt to keep windows on the same monitor, and preferably on * the primary one). */ uint64_t winsys_id; char *display_name; } MetaMonitorPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaMonitor, meta_monitor, G_TYPE_OBJECT) struct _MetaMonitorNormal { MetaMonitor parent; }; G_DEFINE_TYPE (MetaMonitorNormal, meta_monitor_normal, META_TYPE_MONITOR) struct _MetaMonitorTiled { MetaMonitor parent; MetaMonitorManager *monitor_manager; uint32_t tile_group_id; /* The tile (0, 0) output. */ MetaOutput *origin_output; /* The output enabled even when a non-tiled mode is used. */ MetaOutput *main_output; }; G_DEFINE_TYPE (MetaMonitorTiled, meta_monitor_tiled, META_TYPE_MONITOR) static void meta_monitor_mode_free (MetaMonitorMode *mode); MetaMonitorSpec * meta_monitor_spec_clone (MetaMonitorSpec *monitor_spec) { MetaMonitorSpec *new_monitor_spec; new_monitor_spec = g_new0 (MetaMonitorSpec, 1); *new_monitor_spec = (MetaMonitorSpec) { .connector = g_strdup (monitor_spec->connector), .vendor = g_strdup (monitor_spec->vendor), .product = g_strdup (monitor_spec->product), .serial = g_strdup (monitor_spec->serial), }; return new_monitor_spec; } gboolean meta_monitor_spec_equals (MetaMonitorSpec *monitor_spec, MetaMonitorSpec *other_monitor_spec) { return (g_str_equal (monitor_spec->connector, other_monitor_spec->connector) && g_str_equal (monitor_spec->vendor, other_monitor_spec->vendor) && g_str_equal (monitor_spec->product, other_monitor_spec->product) && g_str_equal (monitor_spec->serial, other_monitor_spec->serial)); } int meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a, MetaMonitorSpec *monitor_spec_b) { int ret; ret = strcmp (monitor_spec_a->connector, monitor_spec_b->connector); if (ret != 0) return ret; ret = strcmp (monitor_spec_a->vendor, monitor_spec_b->vendor); if (ret != 0) return ret; ret = strcmp (monitor_spec_a->product, monitor_spec_b->product); if (ret != 0) return ret; return strcmp (monitor_spec_a->serial, monitor_spec_b->serial); } void meta_monitor_spec_free (MetaMonitorSpec *monitor_spec) { g_free (monitor_spec->connector); g_free (monitor_spec->vendor); g_free (monitor_spec->product); g_free (monitor_spec->serial); g_free (monitor_spec); } static void meta_monitor_generate_spec (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); MetaOutput *output = meta_monitor_get_main_output (monitor); MetaMonitorSpec *monitor_spec; monitor_spec = g_new0 (MetaMonitorSpec, 1); *monitor_spec = (MetaMonitorSpec) { .connector = g_strdup (output->name), .vendor = g_strdup (output->vendor), .product = g_strdup (output->product), .serial = g_strdup (output->serial), }; priv->spec = monitor_spec; } static const double known_diagonals[] = { 12.1, 13.3, 15.6 }; static char * diagonal_to_str (double d) { unsigned int i; for (i = 0; i < G_N_ELEMENTS (known_diagonals); i++) { double delta; delta = fabs(known_diagonals[i] - d); if (delta < 0.1) return g_strdup_printf ("%0.1lf\"", known_diagonals[i]); } return g_strdup_printf ("%d\"", (int) (d + 0.5)); } static char * meta_monitor_make_display_name (MetaMonitor *monitor, MetaMonitorManager *monitor_manager) { g_autofree char *inches = NULL; g_autofree char *vendor_name = NULL; const char *vendor = NULL; const char *product_name = NULL; int width_mm; int height_mm; meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm); if (meta_monitor_is_laptop_panel (monitor)) return g_strdup (_("Built-in display")); if (width_mm > 0 && height_mm > 0) { if (!meta_monitor_has_aspect_as_size (monitor)) { double d = sqrt (width_mm * width_mm + height_mm * height_mm); inches = diagonal_to_str (d / 25.4); } else { product_name = meta_monitor_get_product (monitor); } } vendor = meta_monitor_get_vendor (monitor); if (g_strcmp0 (vendor, "unknown") != 0) { vendor_name = meta_monitor_manager_get_vendor_name (monitor_manager, vendor); if (!vendor_name) vendor_name = g_strdup (vendor); } else { if (inches != NULL) vendor_name = g_strdup (_("Unknown")); else vendor_name = g_strdup (_("Unknown Display")); } if (inches != NULL) { /**/ return g_strdup_printf (C_("This is a monitor vendor name, followed by a " "size in inches, like 'Dell 15\"'", "%s %s"), vendor_name, inches); } else if (product_name != NULL) { return g_strdup_printf (C_("This is a monitor vendor name followed by " "product/model name where size in inches " "could not be calculated, e.g. Dell U2414H", "%s %s"), vendor_name, product_name); } else { return g_strdup (vendor_name); } } MetaGpu * meta_monitor_get_gpu (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->gpu; } GList * meta_monitor_get_outputs (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->outputs; } MetaOutput * meta_monitor_get_main_output (MetaMonitor *monitor) { return META_MONITOR_GET_CLASS (monitor)->get_main_output (monitor); } gboolean meta_monitor_is_active (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return !!priv->current_mode; } gboolean meta_monitor_is_primary (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->is_primary; } gboolean meta_monitor_supports_underscanning (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->supports_underscanning; } gboolean meta_monitor_is_underscanning (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->is_underscanning; } gboolean meta_monitor_is_laptop_panel (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); switch (output->connector_type) { case META_CONNECTOR_TYPE_eDP: case META_CONNECTOR_TYPE_LVDS: case META_CONNECTOR_TYPE_DSI: return TRUE; default: return FALSE; } } gboolean meta_monitor_is_same_as (MetaMonitor *monitor, MetaMonitor *other_monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); MetaMonitorPrivate *other_priv = meta_monitor_get_instance_private (other_monitor); return priv->winsys_id == other_priv->winsys_id; } void meta_monitor_get_current_resolution (MetaMonitor *monitor, int *width, int *height) { MetaMonitorMode *mode = meta_monitor_get_current_mode (monitor); *width = mode->spec.width; *height = mode->spec.height; } void meta_monitor_derive_layout (MetaMonitor *monitor, MetaRectangle *layout) { META_MONITOR_GET_CLASS (monitor)->derive_layout (monitor, layout); } void meta_monitor_get_physical_dimensions (MetaMonitor *monitor, int *width_mm, int *height_mm) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); *width_mm = output->width_mm; *height_mm = output->height_mm; } CoglSubpixelOrder meta_monitor_get_subpixel_order (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->subpixel_order; } const char * meta_monitor_get_connector (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->name; } const char * meta_monitor_get_vendor (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->vendor; } const char * meta_monitor_get_product (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->product; } const char * meta_monitor_get_serial (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->serial; } MetaConnectorType meta_monitor_get_connector_type (MetaMonitor *monitor) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); return output->connector_type; } MetaMonitorTransform meta_monitor_logical_to_crtc_transform (MetaMonitor *monitor, MetaMonitorTransform transform) { MetaOutput *output = meta_monitor_get_main_output (monitor); return meta_output_logical_to_crtc_transform (output, transform); } MetaMonitorTransform meta_monitor_crtc_to_logical_transform (MetaMonitor *monitor, MetaMonitorTransform transform) { MetaOutput *output = meta_monitor_get_main_output (monitor); return meta_output_crtc_to_logical_transform (output, transform); } static void meta_monitor_dispose (GObject *object) { MetaMonitor *monitor = META_MONITOR (object); MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); if (priv->outputs) { g_list_free_full (priv->outputs, g_object_unref); priv->outputs = NULL; } G_OBJECT_CLASS (meta_monitor_parent_class)->dispose (object); } static void meta_monitor_finalize (GObject *object) { MetaMonitor *monitor = META_MONITOR (object); MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); g_hash_table_destroy (priv->mode_ids); g_list_free_full (priv->modes, (GDestroyNotify) meta_monitor_mode_free); meta_monitor_spec_free (priv->spec); g_free (priv->display_name); G_OBJECT_CLASS (meta_monitor_parent_class)->finalize (object); } static void meta_monitor_init (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); priv->mode_ids = g_hash_table_new (g_str_hash, g_str_equal); } static void meta_monitor_class_init (MetaMonitorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_monitor_dispose; object_class->finalize = meta_monitor_finalize; } static char * generate_mode_id (MetaMonitorModeSpec *monitor_mode_spec) { gboolean is_interlaced; char refresh_rate_str[G_ASCII_DTOSTR_BUF_SIZE]; is_interlaced = !!(monitor_mode_spec->flags & META_CRTC_MODE_FLAG_INTERLACE); g_ascii_dtostr (refresh_rate_str, G_ASCII_DTOSTR_BUF_SIZE, monitor_mode_spec->refresh_rate); return g_strdup_printf ("%dx%d%s@%s", monitor_mode_spec->width, monitor_mode_spec->height, is_interlaced ? "i" : "", refresh_rate_str); } static gboolean meta_monitor_add_mode (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, gboolean replace) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); MetaMonitorMode *existing_mode; existing_mode = g_hash_table_lookup (priv->mode_ids, meta_monitor_mode_get_id (monitor_mode)); if (existing_mode && !replace) return FALSE; if (existing_mode) priv->modes = g_list_remove (priv->modes, existing_mode); priv->modes = g_list_append (priv->modes, monitor_mode); g_hash_table_replace (priv->mode_ids, monitor_mode->id, monitor_mode); return TRUE; } static MetaMonitorModeSpec meta_monitor_create_spec (MetaMonitor *monitor, int width, int height, MetaCrtcMode *crtc_mode) { MetaOutput *output = meta_monitor_get_main_output (monitor); if (meta_monitor_transform_is_rotated (output->panel_orientation_transform)) { int temp = width; width = height; height = temp; } return (MetaMonitorModeSpec) { .width = width, .height = height, .refresh_rate = crtc_mode->refresh_rate, .flags = crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS }; } static void meta_monitor_normal_generate_modes (MetaMonitorNormal *monitor_normal) { MetaMonitor *monitor = META_MONITOR (monitor_normal); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaOutput *output; MetaCrtcModeFlag preferred_mode_flags; unsigned int i; output = meta_monitor_get_main_output (monitor); preferred_mode_flags = output->preferred_mode->flags; for (i = 0; i < output->n_modes; i++) { MetaCrtcMode *crtc_mode = output->modes[i]; MetaCrtc *crtc; MetaMonitorMode *mode; gboolean replace; mode = g_new0 (MetaMonitorMode, 1); mode->monitor = monitor; mode->spec = meta_monitor_create_spec (monitor, crtc_mode->width, crtc_mode->height, crtc_mode); mode->id = generate_mode_id (&mode->spec); mode->crtc_modes = g_new (MetaMonitorCrtcMode, 1); mode->crtc_modes[0] = (MetaMonitorCrtcMode) { .output = output, .crtc_mode = crtc_mode }; /* * We don't distinguish between all available mode flags, just the ones * that are configurable. We still need to pick some mode though, so * prefer ones that has the same set of flags as the preferred mode; * otherwise take the first one in the list. This guarantees that the * preferred mode is always added. */ replace = crtc_mode->flags == preferred_mode_flags; if (!meta_monitor_add_mode (monitor, mode, replace)) { g_assert (crtc_mode != output->preferred_mode); meta_monitor_mode_free (mode); continue; } if (crtc_mode == output->preferred_mode) monitor_priv->preferred_mode = mode; crtc = meta_output_get_assigned_crtc (output); if (crtc && crtc->config && crtc_mode == crtc->config->mode) monitor_priv->current_mode = mode; } } MetaMonitorNormal * meta_monitor_normal_new (MetaGpu *gpu, MetaMonitorManager *monitor_manager, MetaOutput *output) { MetaMonitorNormal *monitor_normal; MetaMonitor *monitor; MetaMonitorPrivate *monitor_priv; monitor_normal = g_object_new (META_TYPE_MONITOR_NORMAL, NULL); monitor = META_MONITOR (monitor_normal); monitor_priv = meta_monitor_get_instance_private (monitor); monitor_priv->gpu = gpu; monitor_priv->outputs = g_list_append (NULL, g_object_ref (output)); monitor_priv->winsys_id = output->winsys_id; meta_monitor_generate_spec (monitor); meta_monitor_normal_generate_modes (monitor_normal); monitor_priv->display_name = meta_monitor_make_display_name (monitor, monitor_manager); return monitor_normal; } static MetaOutput * meta_monitor_normal_get_main_output (MetaMonitor *monitor) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); return monitor_priv->outputs->data; } static void meta_monitor_normal_derive_layout (MetaMonitor *monitor, MetaRectangle *layout) { MetaOutput *output; MetaCrtc *crtc; MetaCrtcConfig *crtc_config; output = meta_monitor_get_main_output (monitor); crtc = meta_output_get_assigned_crtc (output); crtc_config = crtc->config; g_return_if_fail (crtc_config); meta_rectangle_from_graphene_rect (&crtc_config->layout, META_ROUNDING_STRATEGY_ROUND, layout); } static gboolean meta_monitor_normal_get_suggested_position (MetaMonitor *monitor, int *x, int *y) { MetaOutput *output; output = meta_monitor_get_main_output (monitor); if (output->suggested_x < 0 && output->suggested_y < 0) return FALSE; if (x) *x = output->suggested_x; if (y) *y = output->suggested_y; return TRUE; } static void meta_monitor_normal_calculate_crtc_pos (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y) { *out_x = 0; *out_y = 0; } static void meta_monitor_normal_init (MetaMonitorNormal *monitor) { } static void meta_monitor_normal_class_init (MetaMonitorNormalClass *klass) { MetaMonitorClass *monitor_class = META_MONITOR_CLASS (klass); monitor_class->get_main_output = meta_monitor_normal_get_main_output; monitor_class->derive_layout = meta_monitor_normal_derive_layout; monitor_class->calculate_crtc_pos = meta_monitor_normal_calculate_crtc_pos; monitor_class->get_suggested_position = meta_monitor_normal_get_suggested_position; } uint32_t meta_monitor_tiled_get_tile_group_id (MetaMonitorTiled *monitor_tiled) { return monitor_tiled->tile_group_id; } gboolean meta_monitor_get_suggested_position (MetaMonitor *monitor, int *x, int *y) { return META_MONITOR_GET_CLASS (monitor)->get_suggested_position (monitor, x, y); } static void add_tiled_monitor_outputs (MetaGpu *gpu, MetaMonitorTiled *monitor_tiled) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (META_MONITOR (monitor_tiled)); GList *outputs; GList *l; outputs = meta_gpu_get_outputs (gpu); for (l = outputs; l; l = l->next) { MetaOutput *output = l->data; if (output->tile_info.group_id != monitor_tiled->tile_group_id) continue; g_warn_if_fail (output->subpixel_order == monitor_tiled->origin_output->subpixel_order); monitor_priv->outputs = g_list_append (monitor_priv->outputs, g_object_ref (output)); } } static void calculate_tile_coordinate (MetaMonitor *monitor, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; int x = 0; int y = 0; for (l = monitor_priv->outputs; l; l = l->next) { MetaOutput *other_output = l->data; switch (crtc_transform) { case META_MONITOR_TRANSFORM_NORMAL: case META_MONITOR_TRANSFORM_FLIPPED: if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile && other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile) x += other_output->tile_info.tile_w; if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile && other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile) y += other_output->tile_info.tile_h; break; case META_MONITOR_TRANSFORM_180: case META_MONITOR_TRANSFORM_FLIPPED_180: if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile && other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile) x += other_output->tile_info.tile_w; if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile && other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile) y += other_output->tile_info.tile_h; break; case META_MONITOR_TRANSFORM_270: case META_MONITOR_TRANSFORM_FLIPPED_270: if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile && other_output->tile_info.loc_h_tile > output->tile_info.loc_h_tile) y += other_output->tile_info.tile_w; if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile && other_output->tile_info.loc_v_tile > output->tile_info.loc_v_tile) x += other_output->tile_info.tile_h; break; case META_MONITOR_TRANSFORM_90: case META_MONITOR_TRANSFORM_FLIPPED_90: if (other_output->tile_info.loc_v_tile == output->tile_info.loc_v_tile && other_output->tile_info.loc_h_tile < output->tile_info.loc_h_tile) y += other_output->tile_info.tile_w; if (other_output->tile_info.loc_h_tile == output->tile_info.loc_h_tile && other_output->tile_info.loc_v_tile < output->tile_info.loc_v_tile) x += other_output->tile_info.tile_h; break; } } *out_x = x; *out_y = y; } static void meta_monitor_tiled_calculate_tiled_size (MetaMonitor *monitor, int *out_width, int *out_height) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; int width; int height; width = 0; height = 0; for (l = monitor_priv->outputs; l; l = l->next) { MetaOutput *output = l->data; if (output->tile_info.loc_v_tile == 0) width += output->tile_info.tile_w; if (output->tile_info.loc_h_tile == 0) height += output->tile_info.tile_h; } *out_width = width; *out_height = height; } static gboolean is_monitor_mode_assigned (MetaMonitor *monitor, MetaMonitorMode *mode) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); GList *l; int i; for (l = priv->outputs, i = 0; l; l = l->next, i++) { MetaOutput *output = l->data; MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i]; MetaCrtc *crtc; crtc = meta_output_get_assigned_crtc (output); if (monitor_crtc_mode->crtc_mode && (!crtc || !crtc->config || crtc->config->mode != monitor_crtc_mode->crtc_mode)) return FALSE; else if (!monitor_crtc_mode->crtc_mode && crtc) return FALSE; } return TRUE; } static gboolean is_crtc_mode_tiled (MetaOutput *output, MetaCrtcMode *crtc_mode) { return (crtc_mode->width == (int) output->tile_info.tile_w && crtc_mode->height == (int) output->tile_info.tile_h); } static MetaCrtcMode * find_tiled_crtc_mode (MetaOutput *output, MetaCrtcMode *reference_crtc_mode) { MetaCrtcMode *crtc_mode; unsigned int i; crtc_mode = output->preferred_mode; if (is_crtc_mode_tiled (output, crtc_mode)) return crtc_mode; for (i = 0; i < output->n_modes; i++) { crtc_mode = output->modes[i]; if (!is_crtc_mode_tiled (output, crtc_mode)) continue; if (crtc_mode->refresh_rate != reference_crtc_mode->refresh_rate) continue; if (crtc_mode->flags != reference_crtc_mode->flags) continue; return crtc_mode; } return NULL; } static MetaMonitorMode * create_tiled_monitor_mode (MetaMonitorTiled *monitor_tiled, MetaCrtcMode *reference_crtc_mode, gboolean *out_is_preferred) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaMonitorModeTiled *mode; int width, height; GList *l; unsigned int i; gboolean is_preferred = TRUE; mode = g_new0 (MetaMonitorModeTiled, 1); mode->is_tiled = TRUE; meta_monitor_tiled_calculate_tiled_size (monitor, &width, &height); mode->parent.monitor = monitor; mode->parent.spec = meta_monitor_create_spec (monitor, width, height, reference_crtc_mode); mode->parent.id = generate_mode_id (&mode->parent.spec); mode->parent.crtc_modes = g_new0 (MetaMonitorCrtcMode, g_list_length (monitor_priv->outputs)); for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++) { MetaOutput *output = l->data; MetaCrtcMode *tiled_crtc_mode; tiled_crtc_mode = find_tiled_crtc_mode (output, reference_crtc_mode); if (!tiled_crtc_mode) { g_warning ("No tiled mode found on %s", output->name); meta_monitor_mode_free ((MetaMonitorMode *) mode); return NULL; } mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) { .output = output, .crtc_mode = tiled_crtc_mode }; is_preferred = is_preferred && tiled_crtc_mode == output->preferred_mode; } *out_is_preferred = is_preferred; return (MetaMonitorMode *) mode; } static void generate_tiled_monitor_modes (MetaMonitorTiled *monitor_tiled) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaOutput *main_output; GList *tiled_modes = NULL; unsigned int i; MetaMonitorMode *best_mode = NULL; GList *l; main_output = meta_monitor_get_main_output (META_MONITOR (monitor_tiled)); for (i = 0; i < main_output->n_modes; i++) { MetaCrtcMode *reference_crtc_mode = main_output->modes[i]; MetaMonitorMode *mode; gboolean is_preferred; if (!is_crtc_mode_tiled (main_output, reference_crtc_mode)) continue; mode = create_tiled_monitor_mode (monitor_tiled, reference_crtc_mode, &is_preferred); if (!mode) continue; tiled_modes = g_list_append (tiled_modes, mode); if (is_monitor_mode_assigned (monitor, mode)) monitor_priv->current_mode = mode; if (is_preferred) monitor_priv->preferred_mode = mode; } while ((l = tiled_modes)) { MetaMonitorMode *mode = l->data; tiled_modes = g_list_remove_link (tiled_modes, l); if (!meta_monitor_add_mode (monitor, mode, FALSE)) { meta_monitor_mode_free (mode); continue; } if (!monitor_priv->preferred_mode) { if (!best_mode || mode->spec.refresh_rate > best_mode->spec.refresh_rate) best_mode = mode; } } if (best_mode) monitor_priv->preferred_mode = best_mode; } static MetaMonitorMode * create_untiled_monitor_mode (MetaMonitorTiled *monitor_tiled, MetaOutput *main_output, MetaCrtcMode *crtc_mode) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaMonitorModeTiled *mode; GList *l; int i; if (is_crtc_mode_tiled (main_output, crtc_mode)) return NULL; mode = g_new0 (MetaMonitorModeTiled, 1); mode->is_tiled = FALSE; mode->parent.monitor = monitor; mode->parent.spec = meta_monitor_create_spec (monitor, crtc_mode->width, crtc_mode->height, crtc_mode); mode->parent.id = generate_mode_id (&mode->parent.spec); mode->parent.crtc_modes = g_new0 (MetaMonitorCrtcMode, g_list_length (monitor_priv->outputs)); for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++) { MetaOutput *output = l->data; if (output == main_output) { mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) { .output = output, .crtc_mode = crtc_mode }; } else { mode->parent.crtc_modes[i] = (MetaMonitorCrtcMode) { .output = output, .crtc_mode = NULL }; } } return &mode->parent; } static int count_untiled_crtc_modes (MetaOutput *output) { int count; unsigned int i; count = 0; for (i = 0; i < output->n_modes; i++) { MetaCrtcMode *crtc_mode = output->modes[i]; if (!is_crtc_mode_tiled (output, crtc_mode)) count++; } return count; } static MetaOutput * find_untiled_output (MetaMonitorTiled *monitor_tiled) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaOutput *best_output; int best_untiled_crtc_mode_count; GList *l; best_output = monitor_tiled->origin_output; best_untiled_crtc_mode_count = count_untiled_crtc_modes (monitor_tiled->origin_output); for (l = monitor_priv->outputs; l; l = l->next) { MetaOutput *output = l->data; int untiled_crtc_mode_count; if (output == monitor_tiled->origin_output) continue; untiled_crtc_mode_count = count_untiled_crtc_modes (output); if (untiled_crtc_mode_count > best_untiled_crtc_mode_count) { best_untiled_crtc_mode_count = untiled_crtc_mode_count; best_output = output; } } return best_output; } static void generate_untiled_monitor_modes (MetaMonitorTiled *monitor_tiled) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaOutput *main_output; unsigned int i; main_output = meta_monitor_get_main_output (monitor); for (i = 0; i < main_output->n_modes; i++) { MetaCrtcMode *crtc_mode = main_output->modes[i]; MetaMonitorMode *mode; mode = create_untiled_monitor_mode (monitor_tiled, main_output, crtc_mode); if (!mode) continue; if (!meta_monitor_add_mode (monitor, mode, FALSE)) { meta_monitor_mode_free (mode); continue; } if (is_monitor_mode_assigned (monitor, mode)) { g_assert (!monitor_priv->current_mode); monitor_priv->current_mode = mode; } if (!monitor_priv->preferred_mode && crtc_mode == main_output->preferred_mode) monitor_priv->preferred_mode = mode; } } static MetaMonitorMode * find_best_mode (MetaMonitor *monitor) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); MetaMonitorMode *best_mode = NULL; GList *l; for (l = monitor_priv->modes; l; l = l->next) { MetaMonitorMode *mode = l->data; int area, best_area; if (!best_mode) { best_mode = mode; continue; } area = mode->spec.width * mode->spec.height; best_area = best_mode->spec.width * best_mode->spec.height; if (area > best_area) { best_mode = mode; continue; } if (mode->spec.refresh_rate > best_mode->spec.refresh_rate) { best_mode = mode; continue; } } return best_mode; } static void meta_monitor_tiled_generate_modes (MetaMonitorTiled *monitor_tiled) { MetaMonitor *monitor = META_MONITOR (monitor_tiled); MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); /* * Tiled monitors may look a bit different from each other, depending on the * monitor itself, the driver, etc. * * On some, the tiled modes will be the preferred CRTC modes, and running * untiled is done by only enabling (0, 0) tile. In this case, things are * pretty straight forward. * * Other times a monitor may have some bogus mode preferred on the main tile, * and an untiled mode preferred on the non-main tile, and there seems to be * no guarantee that the (0, 0) tile is the one that should drive the * non-tiled mode. * * To handle both these cases, the following hueristics are implemented: * * 1) Find all the tiled CRTC modes of the (0, 0) tile, and create tiled * monitor modes for all tiles based on these. * 2) If there is any tiled monitor mode combination where all CRTC modes * are the preferred ones, that one is marked as preferred. * 3) If there is no preferred mode determined so far, assume the tiled * monitor mode with the highest refresh rate is preferred. * 4) Find the tile with highest number of untiled CRTC modes available, * assume this is the one driving the monitor in untiled mode, and * create monitor modes for all untiled CRTC modes of that tile. If * there is still no preferred mode, set any untiled mode as preferred * if the CRTC mode is marked as such. * 5) If at this point there is still no preferred mode, just pick the one * with the highest number of pixels and highest refresh rate. * * Note that this ignores the preference if the preference is a non-tiled * mode. This seems to be the case on some systems, where the user tends to * manually set up the tiled mode anyway. */ generate_tiled_monitor_modes (monitor_tiled); if (!monitor_priv->preferred_mode) g_warning ("Tiled monitor on %s didn't have any tiled modes", monitor_priv->spec->connector); generate_untiled_monitor_modes (monitor_tiled); if (!monitor_priv->preferred_mode) { g_warning ("Tiled monitor on %s didn't have a valid preferred mode", monitor_priv->spec->connector); monitor_priv->preferred_mode = find_best_mode (monitor); } } MetaMonitorTiled * meta_monitor_tiled_new (MetaGpu *gpu, MetaMonitorManager *monitor_manager, MetaOutput *output) { MetaMonitorTiled *monitor_tiled; MetaMonitor *monitor; MetaMonitorPrivate *monitor_priv; monitor_tiled = g_object_new (META_TYPE_MONITOR_TILED, NULL); monitor = META_MONITOR (monitor_tiled); monitor_priv = meta_monitor_get_instance_private (monitor); monitor_priv->gpu = gpu; monitor_tiled->tile_group_id = output->tile_info.group_id; monitor_priv->winsys_id = output->winsys_id; monitor_tiled->origin_output = output; add_tiled_monitor_outputs (gpu, monitor_tiled); monitor_tiled->main_output = find_untiled_output (monitor_tiled); meta_monitor_generate_spec (monitor); monitor_tiled->monitor_manager = monitor_manager; meta_monitor_manager_tiled_monitor_added (monitor_manager, META_MONITOR (monitor_tiled)); meta_monitor_tiled_generate_modes (monitor_tiled); monitor_priv->display_name = meta_monitor_make_display_name (monitor, monitor_manager); return monitor_tiled; } static MetaOutput * meta_monitor_tiled_get_main_output (MetaMonitor *monitor) { MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (monitor); return monitor_tiled->main_output; } static void meta_monitor_tiled_derive_layout (MetaMonitor *monitor, MetaRectangle *layout) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; float min_x, min_y, max_x, max_y; min_x = FLT_MAX; min_y = FLT_MAX; max_x = 0.0; max_y = 0.0; for (l = monitor_priv->outputs; l; l = l->next) { MetaOutput *output = l->data; MetaCrtc *crtc; MetaCrtcConfig *crtc_config; graphene_rect_t *crtc_layout; crtc = meta_output_get_assigned_crtc (output); if (!crtc) continue; crtc_config = crtc->config; g_return_if_fail (crtc_config); crtc_layout = &crtc_config->layout; min_x = MIN (crtc_layout->origin.x, min_x); min_y = MIN (crtc_layout->origin.y, min_y); max_x = MAX (crtc_layout->origin.x + crtc_layout->size.width, max_x); max_y = MAX (crtc_layout->origin.y + crtc_layout->size.height, max_y); } *layout = (MetaRectangle) { .x = roundf (min_x), .y = roundf (min_y), .width = roundf (max_x - min_x), .height = roundf (max_y - min_y) }; } static gboolean meta_monitor_tiled_get_suggested_position (MetaMonitor *monitor, int *x, int *y) { return FALSE; } static void meta_monitor_tiled_calculate_crtc_pos (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y) { MetaMonitorModeTiled *mode_tiled = (MetaMonitorModeTiled *) monitor_mode; if (mode_tiled->is_tiled) { calculate_tile_coordinate (monitor, output, crtc_transform, out_x, out_y); } else { *out_x = 0; *out_y = 0; } } static void meta_monitor_tiled_finalize (GObject *object) { MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (object); meta_monitor_manager_tiled_monitor_removed (monitor_tiled->monitor_manager, META_MONITOR (monitor_tiled)); G_OBJECT_CLASS (meta_monitor_tiled_parent_class)->finalize (object); } static void meta_monitor_tiled_init (MetaMonitorTiled *monitor) { } static void meta_monitor_tiled_class_init (MetaMonitorTiledClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaMonitorClass *monitor_class = META_MONITOR_CLASS (klass); object_class->finalize = meta_monitor_tiled_finalize; monitor_class->get_main_output = meta_monitor_tiled_get_main_output; monitor_class->derive_layout = meta_monitor_tiled_derive_layout; monitor_class->calculate_crtc_pos = meta_monitor_tiled_calculate_crtc_pos; monitor_class->get_suggested_position = meta_monitor_tiled_get_suggested_position; } static void meta_monitor_mode_free (MetaMonitorMode *monitor_mode) { g_free (monitor_mode->id); g_free (monitor_mode->crtc_modes); g_free (monitor_mode); } MetaMonitorSpec * meta_monitor_get_spec (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->spec; } MetaLogicalMonitor * meta_monitor_get_logical_monitor (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->logical_monitor; } MetaMonitorMode * meta_monitor_get_mode_from_id (MetaMonitor *monitor, const char *monitor_mode_id) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return g_hash_table_lookup (priv->mode_ids, monitor_mode_id); } static gboolean meta_monitor_mode_spec_equals (MetaMonitorModeSpec *monitor_mode_spec, MetaMonitorModeSpec *other_monitor_mode_spec) { return (monitor_mode_spec->width == other_monitor_mode_spec->width && monitor_mode_spec->height == other_monitor_mode_spec->height && ABS (monitor_mode_spec->refresh_rate - other_monitor_mode_spec->refresh_rate) < MAXIMUM_REFRESH_RATE_DIFF && monitor_mode_spec->flags == other_monitor_mode_spec->flags); } MetaMonitorMode * meta_monitor_get_mode_from_spec (MetaMonitor *monitor, MetaMonitorModeSpec *monitor_mode_spec) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); GList *l; for (l = priv->modes; l; l = l->next) { MetaMonitorMode *monitor_mode = l->data; if (meta_monitor_mode_spec_equals (monitor_mode_spec, &monitor_mode->spec)) return monitor_mode; } return NULL; } MetaMonitorMode * meta_monitor_get_preferred_mode (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->preferred_mode; } MetaMonitorMode * meta_monitor_get_current_mode (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->current_mode; } static gboolean is_current_mode_known (MetaMonitor *monitor) { MetaOutput *output; MetaCrtc *crtc; output = meta_monitor_get_main_output (monitor); crtc = meta_output_get_assigned_crtc (output); return meta_monitor_is_active (monitor) == (crtc && crtc->config); } void meta_monitor_derive_current_mode (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); MetaMonitorMode *current_mode = NULL; GList *l; for (l = priv->modes; l; l = l->next) { MetaMonitorMode *mode = l->data; if (is_monitor_mode_assigned (monitor, mode)) { current_mode = mode; break; } } priv->current_mode = current_mode; g_warn_if_fail (is_current_mode_known (monitor)); } void meta_monitor_set_current_mode (MetaMonitor *monitor, MetaMonitorMode *mode) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); priv->current_mode = mode; } GList * meta_monitor_get_modes (MetaMonitor *monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); return priv->modes; } void meta_monitor_calculate_crtc_pos (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y) { META_MONITOR_GET_CLASS (monitor)->calculate_crtc_pos (monitor, monitor_mode, output, crtc_transform, out_x, out_y); } /* The minimum resolution at which we turn on a window-scale of 2 */ #define HIDPI_LIMIT 192 /* * The minimum screen height at which we turn on a window-scale of 2; * below this there just isn't enough vertical real estate for GNOME * apps to work, and it's better to just be tiny */ #define HIDPI_MIN_HEIGHT 1200 /* From http://en.wikipedia.org/wiki/4K_resolution#Resolutions_of_common_formats */ #define SMALLEST_4K_WIDTH 3656 static float calculate_scale (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints) { int resolution_width, resolution_height; int width_mm, height_mm; int scale; scale = 1.0; meta_monitor_mode_get_resolution (monitor_mode, &resolution_width, &resolution_height); if (resolution_height < HIDPI_MIN_HEIGHT) goto out; /* 4K TV */ switch (meta_monitor_get_connector_type (monitor)) { case META_CONNECTOR_TYPE_HDMIA: case META_CONNECTOR_TYPE_HDMIB: if (resolution_width < SMALLEST_4K_WIDTH) goto out; break; default: break; } meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm); /* * Somebody encoded the aspect ratio (16/9 or 16/10) instead of the physical * size. */ if (meta_monitor_has_aspect_as_size (monitor)) goto out; if (width_mm > 0 && height_mm > 0) { double dpi_x, dpi_y; dpi_x = (double) resolution_width / (width_mm / 25.4); dpi_y = (double) resolution_height / (height_mm / 25.4); /* * We don't completely trust these values so both must be high, and never * pick higher ratio than 2 automatically. */ if (dpi_x > HIDPI_LIMIT && dpi_y > HIDPI_LIMIT) scale = 2.0; } out: return scale; } float meta_monitor_calculate_mode_scale (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); int global_scaling_factor; if (meta_settings_get_global_scaling_factor (settings, &global_scaling_factor)) return global_scaling_factor; return calculate_scale (monitor, monitor_mode, constraints); } static gboolean is_logical_size_large_enough (int width, int height) { return width * height >= MINIMUM_LOGICAL_AREA; } static gboolean is_scale_valid_for_size (float width, float height, float scale) { return scale >= MINIMUM_SCALE_FACTOR && scale <= MAXIMUM_SCALE_FACTOR && is_logical_size_large_enough (floorf (width/scale), floorf (width/scale)); } gboolean meta_monitor_mode_should_be_advertised (MetaMonitorMode *monitor_mode) { MetaMonitorMode *preferred_mode; g_return_val_if_fail (monitor_mode != NULL, FALSE); preferred_mode = meta_monitor_get_preferred_mode (monitor_mode->monitor); if (monitor_mode->spec.width == preferred_mode->spec.width && monitor_mode->spec.height == preferred_mode->spec.height) return TRUE; return is_logical_size_large_enough (monitor_mode->spec.width, monitor_mode->spec.height); } static float get_closest_scale_factor_for_resolution (float width, float height, float scale) { unsigned int i, j; float scaled_h; float scaled_w; float best_scale; int base_scaled_w; gboolean found_one; best_scale = 0; if (!is_scale_valid_for_size (width, height, scale)) goto out; if (fmodf (width, scale) == 0.0 && fmodf (height, scale) == 0.0) return scale; i = 0; found_one = FALSE; base_scaled_w = floorf (width / scale); do { for (j = 0; j < 2; j++) { float current_scale; int offset = i * (j ? 1 : -1); scaled_w = base_scaled_w + offset; current_scale = width / scaled_w; scaled_h = height / current_scale; if (current_scale >= scale + SCALE_FACTORS_STEPS || current_scale <= scale - SCALE_FACTORS_STEPS || current_scale < MINIMUM_SCALE_FACTOR || current_scale > MAXIMUM_SCALE_FACTOR) { goto out; } if (floorf (scaled_h) == scaled_h) { found_one = TRUE; if (fabsf (current_scale - scale) < fabsf (best_scale - scale)) best_scale = current_scale; } } i++; } while (!found_one); out: return best_scale; } float * meta_monitor_calculate_supported_scales (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints, int *n_supported_scales) { unsigned int i, j; int width, height; GArray *supported_scales; supported_scales = g_array_new (FALSE, FALSE, sizeof (float)); meta_monitor_mode_get_resolution (monitor_mode, &width, &height); for (i = floorf (MINIMUM_SCALE_FACTOR); i <= ceilf (MAXIMUM_SCALE_FACTOR); i++) { for (j = 0; j < SCALE_FACTORS_PER_INTEGER; j++) { float scale; float scale_value = i + j * SCALE_FACTORS_STEPS; if (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) { if (fmodf (scale_value, 1.0) != 0.0) continue; } if ((constraints & META_MONITOR_SCALES_CONSTRAINT_NO_FRAC) || (constraints & META_MONITOR_SCALES_CONSTRAINT_NO_LOGICAL)) { if (!is_scale_valid_for_size (width, height, scale_value)) continue; scale = scale_value; } else scale = get_closest_scale_factor_for_resolution (width, height, scale_value); if (scale > 0.0f) g_array_append_val (supported_scales, scale); } } if (supported_scales->len == 0) { float fallback_scale; fallback_scale = 1.0; g_array_append_val (supported_scales, fallback_scale); } *n_supported_scales = supported_scales->len; return (float *) g_array_free (supported_scales, FALSE); } MetaMonitorModeSpec * meta_monitor_mode_get_spec (MetaMonitorMode *monitor_mode) { return &monitor_mode->spec; } const char * meta_monitor_mode_get_id (MetaMonitorMode *monitor_mode) { return monitor_mode->id; } void meta_monitor_mode_get_resolution (MetaMonitorMode *monitor_mode, int *width, int *height) { *width = monitor_mode->spec.width; *height = monitor_mode->spec.height; } float meta_monitor_mode_get_refresh_rate (MetaMonitorMode *monitor_mode) { return monitor_mode->spec.refresh_rate; } MetaCrtcModeFlag meta_monitor_mode_get_flags (MetaMonitorMode *monitor_mode) { return monitor_mode->spec.flags; } gboolean meta_monitor_mode_foreach_crtc (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; int i; for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++) { MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i]; if (!monitor_crtc_mode->crtc_mode) continue; if (!func (monitor, mode, monitor_crtc_mode, user_data, error)) return FALSE; } return TRUE; } gboolean meta_monitor_mode_foreach_output (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); GList *l; int i; for (l = monitor_priv->outputs, i = 0; l; l = l->next, i++) { MetaMonitorCrtcMode *monitor_crtc_mode = &mode->crtc_modes[i]; if (!func (monitor, mode, monitor_crtc_mode, user_data, error)) return FALSE; } return TRUE; } const char * meta_monitor_get_display_name (MetaMonitor *monitor) { MetaMonitorPrivate *monitor_priv = meta_monitor_get_instance_private (monitor); return monitor_priv->display_name; } void meta_monitor_set_logical_monitor (MetaMonitor *monitor, MetaLogicalMonitor *logical_monitor) { MetaMonitorPrivate *priv = meta_monitor_get_instance_private (monitor); priv->logical_monitor = logical_monitor; } muffin-6.4.1/src/backends/meta-renderer.c0000664000175000017500000002103014723361714017175 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:meta-renderer * @title: MetaRenderer * @short_description: Keeps track of the different renderer views. * * A MetaRenderer object has 2 functions: * * 1) Keeping a list of #MetaRendererViews, each responsible for * rendering a part of the stage, corresponding to each #MetaLogicalMonitor. It * keeps track of this list by querying the list of logical monitors in the * #MetaBackend's #MetaMonitorManager, and creating a renderer view for each * logical monitor it encounters. * * 2) Creating and setting up an appropriate #CoglRenderer. For example, a * #MetaRenderer might call cogl_renderer_set_custom_winsys() to tie the * backend-specific mechanisms into Cogl. */ #include "config.h" #include "backends/meta-renderer.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" enum { PROP_0, PROP_BACKEND, N_PROPS }; static GParamSpec *obj_props[N_PROPS]; typedef struct _MetaRendererPrivate { MetaBackend *backend; GList *views; } MetaRendererPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaRenderer, meta_renderer, G_TYPE_OBJECT) MetaBackend * meta_renderer_get_backend (MetaRenderer *renderer) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); return priv->backend; } /** * meta_renderer_create_cogl_renderer: * @renderer: a #MetaRenderer object * * Creates a #CoglRenderer that is appropriate for a certain backend. For * example, a #MetaRenderer might call cogl_renderer_set_custom_winsys() to tie * the backend-specific mechanisms (such as swapBuffers and vsync) into Cogl. * * Returns: (transfer full): a newly made #CoglRenderer. */ CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer) { return META_RENDERER_GET_CLASS (renderer)->create_cogl_renderer (renderer); } static MetaRendererView * meta_renderer_create_view (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor, MetaOutput *output, MetaCrtc *crtc) { return META_RENDERER_GET_CLASS (renderer)->create_view (renderer, logical_monitor, output, crtc); } /** * meta_renderer_rebuild_views: * @renderer: a #MetaRenderer object * * Rebuilds the internal list of #MetaRendererView objects by querying the * current #MetaBackend's #MetaMonitorManager. * * This also leads to the original list of monitors being unconditionally freed. */ void meta_renderer_rebuild_views (MetaRenderer *renderer) { return META_RENDERER_GET_CLASS (renderer)->rebuild_views (renderer); } static void create_crtc_view (MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor, MetaOutput *output, MetaCrtc *crtc, gpointer user_data) { MetaRenderer *renderer = user_data; MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); MetaRendererView *view; view = meta_renderer_create_view (renderer, logical_monitor, output, crtc); priv->views = g_list_append (priv->views, view); } static void meta_renderer_real_rebuild_views (MetaRenderer *renderer) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; g_list_free_full (priv->views, g_object_unref); priv->views = NULL; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; meta_logical_monitor_foreach_crtc (logical_monitor, create_crtc_view, renderer); } } void meta_renderer_add_view (MetaRenderer *renderer, MetaRendererView *view) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); priv->views = g_list_append (priv->views, view); } /** * meta_renderer_get_views: * @renderer: a #MetaRenderer object * * Returns a list of #MetaRendererView objects, each dealing with a part of the * stage. * * Returns: (transfer none) (element-type MetaRendererView): a list of * #MetaRendererView objects. */ GList * meta_renderer_get_views (MetaRenderer *renderer) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); return priv->views; } gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer) { MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); MetaBackend *backend = priv->backend; ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglGpuInfo *info = &cogl_context->gpu; switch (info->architecture) { case COGL_GPU_INFO_ARCHITECTURE_UNKNOWN: case COGL_GPU_INFO_ARCHITECTURE_SANDYBRIDGE: case COGL_GPU_INFO_ARCHITECTURE_SGX: case COGL_GPU_INFO_ARCHITECTURE_MALI: return TRUE; case COGL_GPU_INFO_ARCHITECTURE_LLVMPIPE: case COGL_GPU_INFO_ARCHITECTURE_SOFTPIPE: case COGL_GPU_INFO_ARCHITECTURE_SWRAST: return FALSE; } g_assert_not_reached (); return FALSE; } static void meta_renderer_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaRenderer *renderer = META_RENDERER (object); MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); switch (prop_id) { case PROP_BACKEND: g_value_set_object (value, priv->backend); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaRenderer *renderer = META_RENDERER (object); MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); switch (prop_id) { case PROP_BACKEND: priv->backend = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_finalize (GObject *object) { MetaRenderer *renderer = META_RENDERER (object); MetaRendererPrivate *priv = meta_renderer_get_instance_private (renderer); g_list_free_full (priv->views, g_object_unref); priv->views = NULL; G_OBJECT_CLASS (meta_renderer_parent_class)->finalize (object); } static void meta_renderer_init (MetaRenderer *renderer) { } static void meta_renderer_class_init (MetaRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = meta_renderer_get_property; object_class->set_property = meta_renderer_set_property; object_class->finalize = meta_renderer_finalize; klass->rebuild_views = meta_renderer_real_rebuild_views; obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "backend", "MetaBackend", META_TYPE_BACKEND, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); } muffin-6.4.1/src/backends/meta-monitor.h0000664000175000017500000002515614723361714017100 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_MONITOR_H #define META_MONITOR_H #include #include "backends/meta-backend-types.h" #include "backends/meta-crtc.h" #include "backends/meta-output.h" typedef struct _MetaMonitorSpec { char *connector; char *vendor; char *product; char *serial; } MetaMonitorSpec; typedef struct _MetaMonitorModeSpec { int width; int height; float refresh_rate; MetaCrtcModeFlag flags; } MetaMonitorModeSpec; typedef struct _MetaMonitorCrtcMode { MetaOutput *output; MetaCrtcMode *crtc_mode; } MetaMonitorCrtcMode; #define HANDLED_CRTC_MODE_FLAGS (META_CRTC_MODE_FLAG_INTERLACE) typedef gboolean (* MetaMonitorModeFunc) (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error); typedef enum _MetaMonitorScalesConstraint { META_MONITOR_SCALES_CONSTRAINT_NONE = 0, META_MONITOR_SCALES_CONSTRAINT_NO_FRAC = (1 << 0), META_MONITOR_SCALES_CONSTRAINT_NO_LOGICAL = (1 << 1), } MetaMonitorScalesConstraint; #define META_TYPE_MONITOR (meta_monitor_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaMonitor, meta_monitor, META, MONITOR, GObject) struct _MetaMonitorClass { GObjectClass parent_class; MetaOutput * (* get_main_output) (MetaMonitor *monitor); void (* derive_layout) (MetaMonitor *monitor, MetaRectangle *layout); void (* calculate_crtc_pos) (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y); gboolean (* get_suggested_position) (MetaMonitor *monitor, int *width, int *height); }; #define META_TYPE_MONITOR_NORMAL (meta_monitor_normal_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorNormal, meta_monitor_normal, META, MONITOR_NORMAL, MetaMonitor) #define META_TYPE_MONITOR_TILED (meta_monitor_tiled_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorTiled, meta_monitor_tiled, META, MONITOR_TILED, MetaMonitor) META_EXPORT_TEST MetaMonitorTiled * meta_monitor_tiled_new (MetaGpu *gpu, MetaMonitorManager *monitor_manager, MetaOutput *output); META_EXPORT_TEST MetaMonitorNormal * meta_monitor_normal_new (MetaGpu *gpu, MetaMonitorManager *monitor_manager, MetaOutput *output); META_EXPORT_TEST MetaMonitorSpec * meta_monitor_get_spec (MetaMonitor *monitor); META_EXPORT_TEST MetaGpu * meta_monitor_get_gpu (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_is_active (MetaMonitor *monitor); META_EXPORT_TEST MetaOutput * meta_monitor_get_main_output (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_is_primary (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_supports_underscanning (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_is_underscanning (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_is_laptop_panel (MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_monitor_is_same_as (MetaMonitor *monitor, MetaMonitor *other_monitor); META_EXPORT_TEST GList * meta_monitor_get_outputs (MetaMonitor *monitor); META_EXPORT_TEST void meta_monitor_get_current_resolution (MetaMonitor *monitor, int *width, int *height); META_EXPORT_TEST void meta_monitor_derive_layout (MetaMonitor *monitor, MetaRectangle *layout); META_EXPORT_TEST void meta_monitor_get_physical_dimensions (MetaMonitor *monitor, int *width_mm, int *height_mm); META_EXPORT_TEST CoglSubpixelOrder meta_monitor_get_subpixel_order (MetaMonitor *monitor); META_EXPORT_TEST const char * meta_monitor_get_connector (MetaMonitor *monitor); META_EXPORT_TEST const char * meta_monitor_get_vendor (MetaMonitor *monitor); META_EXPORT_TEST const char * meta_monitor_get_product (MetaMonitor *monitor); META_EXPORT_TEST const char * meta_monitor_get_serial (MetaMonitor *monitor); META_EXPORT_TEST MetaConnectorType meta_monitor_get_connector_type (MetaMonitor *monitor); /* This function returns the transform corrected for the panel orientation */ META_EXPORT_TEST MetaMonitorTransform meta_monitor_logical_to_crtc_transform (MetaMonitor *monitor, MetaMonitorTransform transform); /* * This function converts a transform corrected for the panel orientation * to its logical (user-visible) transform. */ META_EXPORT_TEST MetaMonitorTransform meta_monitor_crtc_to_logical_transform (MetaMonitor *monitor, MetaMonitorTransform transform); META_EXPORT_TEST uint32_t meta_monitor_tiled_get_tile_group_id (MetaMonitorTiled *monitor_tiled); META_EXPORT_TEST gboolean meta_monitor_get_suggested_position (MetaMonitor *monitor, int *x, int *y); META_EXPORT_TEST MetaLogicalMonitor * meta_monitor_get_logical_monitor (MetaMonitor *monitor); META_EXPORT_TEST MetaMonitorMode * meta_monitor_get_mode_from_id (MetaMonitor *monitor, const char *monitor_mode_id); META_EXPORT_TEST MetaMonitorMode * meta_monitor_get_mode_from_spec (MetaMonitor *monitor, MetaMonitorModeSpec *monitor_mode_spec); META_EXPORT_TEST MetaMonitorMode * meta_monitor_get_preferred_mode (MetaMonitor *monitor); META_EXPORT_TEST MetaMonitorMode * meta_monitor_get_current_mode (MetaMonitor *monitor); META_EXPORT_TEST void meta_monitor_derive_current_mode (MetaMonitor *monitor); META_EXPORT_TEST void meta_monitor_set_current_mode (MetaMonitor *monitor, MetaMonitorMode *mode); META_EXPORT_TEST GList * meta_monitor_get_modes (MetaMonitor *monitor); META_EXPORT_TEST void meta_monitor_calculate_crtc_pos (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaOutput *output, MetaMonitorTransform crtc_transform, int *out_x, int *out_y); META_EXPORT_TEST float meta_monitor_calculate_mode_scale (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints); META_EXPORT_TEST float * meta_monitor_calculate_supported_scales (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorScalesConstraint constraints, int *n_supported_scales); META_EXPORT_TEST const char * meta_monitor_mode_get_id (MetaMonitorMode *monitor_mode); META_EXPORT_TEST MetaMonitorModeSpec * meta_monitor_mode_get_spec (MetaMonitorMode *monitor_mode); META_EXPORT_TEST void meta_monitor_mode_get_resolution (MetaMonitorMode *monitor_mode, int *width, int *height); META_EXPORT_TEST float meta_monitor_mode_get_refresh_rate (MetaMonitorMode *monitor_mode); META_EXPORT_TEST MetaCrtcModeFlag meta_monitor_mode_get_flags (MetaMonitorMode *monitor_mode); META_EXPORT_TEST gboolean meta_monitor_mode_foreach_crtc (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error); META_EXPORT_TEST gboolean meta_monitor_mode_foreach_output (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorModeFunc func, gpointer user_data, GError **error); META_EXPORT_TEST gboolean meta_monitor_mode_should_be_advertised (MetaMonitorMode *monitor_mode); META_EXPORT_TEST MetaMonitorSpec * meta_monitor_spec_clone (MetaMonitorSpec *monitor_id); META_EXPORT_TEST gboolean meta_monitor_spec_equals (MetaMonitorSpec *monitor_id, MetaMonitorSpec *other_monitor_id); META_EXPORT_TEST int meta_monitor_spec_compare (MetaMonitorSpec *monitor_spec_a, MetaMonitorSpec *monitor_spec_b); META_EXPORT_TEST void meta_monitor_spec_free (MetaMonitorSpec *monitor_id); const char * meta_monitor_get_display_name (MetaMonitor *monitor); void meta_monitor_set_logical_monitor (MetaMonitor *monitor, MetaLogicalMonitor *logical_monitor); #endif /* META_MONITOR_H */ muffin-6.4.1/src/backends/meta-renderer-view.h0000664000175000017500000000224314723361714020157 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef META_RENDERER_VIEW_H #define META_RENDERER_VIEW_H #include "backends/meta-monitor-manager-private.h" #include "clutter/clutter-mutter.h" #define META_TYPE_RENDERER_VIEW (meta_renderer_view_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererView, meta_renderer_view, META, RENDERER_VIEW, ClutterStageViewCogl) MetaMonitorTransform meta_renderer_view_get_transform (MetaRendererView *view); #endif /* META_RENDERER_VIEW_H */ muffin-6.4.1/src/backends/meta-remote-desktop.h0000664000175000017500000000325714723361714020351 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_REMOTE_DESKTOP_H #define META_REMOTE_DESKTOP_H #include #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-remote-desktop.h" typedef struct _MetaRemoteDesktopSession MetaRemoteDesktopSession; #define META_TYPE_REMOTE_DESKTOP (meta_remote_desktop_get_type ()) G_DECLARE_FINAL_TYPE (MetaRemoteDesktop, meta_remote_desktop, META, REMOTE_DESKTOP, MetaDBusRemoteDesktopSkeleton) MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, const char *session_id); GDBusConnection * meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop); MetaRemoteDesktop * meta_remote_desktop_new (MetaDbusSessionWatcher *session_watcher); #endif /* META_REMOTE_DESKTOP_H */ muffin-6.4.1/src/backends/meta-input-device.c0000664000175000017500000000747714723361714020006 0ustar fabiofabio/* * Copyright © 2020 Red Hat Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "config.h" #include "backends/meta-backend-private.h" #include "meta-input-device-private.h" typedef struct _MetaInputDevicePrivate MetaInputDevicePrivate; struct _MetaInputDevicePrivate { #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; #else /* Just something to have non-zero sized struct otherwise */ gpointer wacom_device; #endif }; enum { PROP_WACOM_DEVICE = 1, N_PROPS }; static GParamSpec *props[N_PROPS] = { 0 }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaInputDevice, meta_input_device, CLUTTER_TYPE_INPUT_DEVICE) static void meta_input_device_init (MetaInputDevice *input_device) { } static void meta_input_device_constructed (GObject *object) { MetaInputDevice *input_device = META_INPUT_DEVICE (object); #ifdef HAVE_LIBWACOM WacomDeviceDatabase *wacom_db; MetaInputDevicePrivate *priv; const char *node; #endif G_OBJECT_CLASS (meta_input_device_parent_class)->constructed (object); #ifdef HAVE_LIBWACOM priv = meta_input_device_get_instance_private (input_device); wacom_db = meta_backend_get_wacom_database (meta_get_backend ()); node = clutter_input_device_get_device_node (CLUTTER_INPUT_DEVICE (input_device)); priv->wacom_device = libwacom_new_from_path (wacom_db, node, WFALLBACK_NONE, NULL); #endif /* HAVE_LIBWACOM */ } static void meta_input_device_finalize (GObject *object) { #ifdef HAVE_LIBWACOM MetaInputDevicePrivate *priv; priv = meta_input_device_get_instance_private (META_INPUT_DEVICE (object)); g_clear_pointer (&priv->wacom_device, libwacom_destroy); #endif /* HAVE_LIBWACOM */ G_OBJECT_CLASS (meta_input_device_parent_class)->finalize (object); } static void meta_input_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaInputDevicePrivate *priv; priv = meta_input_device_get_instance_private (META_INPUT_DEVICE (object)); switch (prop_id) { case PROP_WACOM_DEVICE: g_value_set_pointer (value, priv->wacom_device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_input_device_class_init (MetaInputDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_input_device_constructed; object_class->finalize = meta_input_device_finalize; object_class->get_property = meta_input_device_get_property; props[PROP_WACOM_DEVICE] = g_param_spec_pointer ("wacom-device", "Wacom device", "Wacom device", G_PARAM_READABLE); g_object_class_install_properties (object_class, N_PROPS, props); } #ifdef HAVE_LIBWACOM WacomDevice * meta_input_device_get_wacom_device (MetaInputDevice *input_device) { MetaInputDevicePrivate *priv; priv = meta_input_device_get_instance_private (input_device); return priv->wacom_device; } #endif /* HAVE_LIBWACOM */ muffin-6.4.1/src/backends/meta-egl.c0000664000175000017500000007641214723361714016154 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016, 2017 Red Hat Inc. * Copyright (C) 2018, 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-egl.h" #include "backends/meta-egl-ext.h" #include "meta/util.h" struct _MetaEgl { GObject parent; PFNEGLGETPLATFORMDISPLAYEXTPROC eglGetPlatformDisplayEXT; PFNEGLCREATEIMAGEKHRPROC eglCreateImageKHR; PFNEGLDESTROYIMAGEKHRPROC eglDestroyImageKHR; PFNEGLQUERYWAYLANDBUFFERWL eglQueryWaylandBufferWL; PFNEGLQUERYDEVICESEXTPROC eglQueryDevicesEXT; PFNEGLQUERYDEVICESTRINGEXTPROC eglQueryDeviceStringEXT; PFNEGLGETOUTPUTLAYERSEXTPROC eglGetOutputLayersEXT; PFNEGLQUERYOUTPUTLAYERATTRIBEXTPROC eglQueryOutputLayerAttribEXT; PFNEGLCREATESTREAMKHRPROC eglCreateStreamKHR; PFNEGLDESTROYSTREAMKHRPROC eglDestroyStreamKHR; PFNEGLQUERYSTREAMKHRPROC eglQueryStreamKHR; PFNEGLCREATESTREAMATTRIBNVPROC eglCreateStreamAttribNV; PFNEGLCREATESTREAMPRODUCERSURFACEKHRPROC eglCreateStreamProducerSurfaceKHR; PFNEGLSTREAMCONSUMEROUTPUTEXTPROC eglStreamConsumerOutputEXT; PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALKHRPROC eglStreamConsumerGLTextureExternalKHR; PFNEGLSTREAMCONSUMERACQUIREKHRPROC eglStreamConsumerAcquireKHR; PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC eglStreamConsumerAcquireAttribNV; PFNEGLQUERYDMABUFFORMATSEXTPROC eglQueryDmaBufFormatsEXT; PFNEGLQUERYDMABUFMODIFIERSEXTPROC eglQueryDmaBufModifiersEXT; }; G_DEFINE_TYPE (MetaEgl, meta_egl, G_TYPE_OBJECT) G_DEFINE_QUARK (-meta-egl-error-quark, meta_egl_error) static const char * get_egl_error_str (EGLint error_number) { switch (error_number) { case EGL_SUCCESS: return "The last function succeeded without error."; break; case EGL_NOT_INITIALIZED: return "EGL is not initialized, or could not be initialized, for the specified EGL display connection."; break; case EGL_BAD_ACCESS: return "EGL cannot access a requested resource (for example a context is bound in another thread)."; break; case EGL_BAD_ALLOC: return "EGL failed to allocate resources for the requested operation."; break; case EGL_BAD_ATTRIBUTE: return "An unrecognized attribute or attribute value was passed in the attribute list."; break; case EGL_BAD_CONTEXT: return "An EGLContext argument does not name a valid EGL rendering context."; break; case EGL_BAD_CONFIG: return "An EGLConfig argument does not name a valid EGL frame buffer configuration."; break; case EGL_BAD_CURRENT_SURFACE: return "The current surface of the calling thread is a window, pixel buffer or pixmap that is no longer valid."; break; case EGL_BAD_DISPLAY: return "An EGLDisplay argument does not name a valid EGL display connection."; break; case EGL_BAD_SURFACE: return "An EGLSurface argument does not name a valid surface (window, pixel buffer or pixmap) configured for GL rendering."; break; case EGL_BAD_MATCH: return "Arguments are inconsistent (for example, a valid context requires buffers not supplied by a valid surface)."; break; case EGL_BAD_PARAMETER: return "One or more argument values are invalid."; break; case EGL_BAD_NATIVE_PIXMAP: return "A NativePixmapType argument does not refer to a valid native pixmap."; break; case EGL_BAD_NATIVE_WINDOW: return "A NativeWindowType argument does not refer to a valid native window."; break; case EGL_CONTEXT_LOST: return "A power management event has occurred. The application must destroy all contexts and reinitialise OpenGL ES state and objects to continue rendering. "; break; case EGL_BAD_STREAM_KHR: return "An EGLStreamKHR argument does not name a valid EGL stream."; break; case EGL_BAD_STATE_KHR: return "An EGLStreamKHR argument is not in a valid state"; break; case EGL_BAD_DEVICE_EXT: return "An EGLDeviceEXT argument does not name a valid EGL device."; break; case EGL_BAD_OUTPUT_LAYER_EXT: return "An EGLOutputLayerEXT argument does not name a valid EGL output layer."; case EGL_RESOURCE_BUSY_EXT: return "The operation could not be completed on the requested resource because it is temporary unavailable."; default: return "Unknown error"; break; } } static void set_egl_error (GError **error) { EGLint error_number; const char *error_str; if (!error) return; error_number = eglGetError (); error_str = get_egl_error_str (error_number); g_set_error_literal (error, META_EGL_ERROR, error_number, error_str); } gboolean meta_extensions_string_has_extensions_valist (const char *extensions_str, const char ***missing_extensions, const char *first_extension, va_list var_args) { char **extensions; const char *extension; size_t num_missing_extensions = 0; if (missing_extensions) *missing_extensions = NULL; extensions = g_strsplit (extensions_str, " ", -1); extension = first_extension; while (extension) { if (!g_strv_contains ((const char * const *) extensions, extension)) { num_missing_extensions++; if (missing_extensions) { *missing_extensions = g_realloc_n (*missing_extensions, num_missing_extensions + 1, sizeof (const char *)); (*missing_extensions)[num_missing_extensions - 1] = extension; (*missing_extensions)[num_missing_extensions] = NULL; } else { break; } } extension = va_arg (var_args, char *); } g_strfreev (extensions); return num_missing_extensions == 0; } gboolean meta_egl_has_extensions (MetaEgl *egl, EGLDisplay display, const char ***missing_extensions, const char *first_extension, ...) { va_list var_args; const char *extensions_str; gboolean has_extensions; extensions_str = (const char *) eglQueryString (display, EGL_EXTENSIONS); if (!extensions_str) { g_warning ("Failed to query string: %s", get_egl_error_str (eglGetError ())); return FALSE; } va_start (var_args, first_extension); has_extensions = meta_extensions_string_has_extensions_valist (extensions_str, missing_extensions, first_extension, var_args); va_end (var_args); return has_extensions; } gboolean meta_egl_initialize (MetaEgl *egl, EGLDisplay display, GError **error) { if (!eglInitialize (display, NULL, NULL)) { set_egl_error (error); return FALSE; } return TRUE; } gpointer meta_egl_get_proc_address (MetaEgl *egl, const char *procname, GError **error) { gpointer func; func = (gpointer) eglGetProcAddress (procname); if (!func) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not load symbol '%s': Not found", procname); return NULL; } return func; } gboolean meta_egl_get_config_attrib (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value, GError **error) { if (!eglGetConfigAttrib (display, config, attribute, value)) { set_egl_error (error); return FALSE; } return TRUE; } EGLConfig * meta_egl_choose_all_configs (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, EGLint *out_num_configs, GError **error) { EGLint num_configs; EGLConfig *configs; EGLint num_matches; if (!eglGetConfigs (display, NULL, 0, &num_configs)) { set_egl_error (error); return FALSE; } if (num_configs < 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No EGL configurations available"); return FALSE; } configs = g_new0 (EGLConfig, num_configs); if (!eglChooseConfig (display, attrib_list, configs, num_configs, &num_matches)) { g_free (configs); set_egl_error (error); return FALSE; } if (num_matches == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No matching EGL configs"); g_free (configs); return NULL; } *out_num_configs = num_configs; return configs; } gboolean meta_egl_choose_first_config (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, EGLConfig *chosen_config, GError **error) { EGLint num_configs; EGLConfig *configs; EGLint num_matches; if (!eglGetConfigs (display, NULL, 0, &num_configs)) { set_egl_error (error); return FALSE; } if (num_configs < 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No EGL configurations available"); return FALSE; } configs = g_new0 (EGLConfig, num_configs); if (!eglChooseConfig (display, attrib_list, configs, num_configs, &num_matches)) { g_free (configs); set_egl_error (error); return FALSE; } if (num_matches == 0) { g_free (configs); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No matching EGLConfig found"); return FALSE; } /* * We don't have any preference specified yet, so lets choose the first one. */ *chosen_config = configs[0]; g_free (configs); return TRUE; } EGLSurface meta_egl_create_window_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLNativeWindowType native_window_type, const EGLint *attrib_list, GError **error) { EGLSurface surface; surface = eglCreateWindowSurface (display, config, native_window_type, attrib_list); if (surface == EGL_NO_SURFACE) { set_egl_error (error); return EGL_NO_SURFACE; } return surface; } EGLSurface meta_egl_create_pbuffer_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, const EGLint *attrib_list, GError **error) { EGLSurface surface; surface = eglCreatePbufferSurface (display, config, attrib_list); if (surface == EGL_NO_SURFACE) { set_egl_error (error); return EGL_NO_SURFACE; } return surface; } gboolean meta_egl_destroy_surface (MetaEgl *egl, EGLDisplay display, EGLSurface surface, GError **error) { if (!eglDestroySurface (display, surface)) { set_egl_error (error); return FALSE; } return TRUE; } static gboolean is_egl_proc_valid_real (void *proc, const char *proc_name, GError **error) { if (!proc) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EGL proc '%s' not resolved", proc_name); return FALSE; } return TRUE; } #define is_egl_proc_valid(proc, error) \ is_egl_proc_valid_real (proc, #proc, error) EGLDisplay meta_egl_get_platform_display (MetaEgl *egl, EGLenum platform, void *native_display, const EGLint *attrib_list, GError **error) { EGLDisplay display; if (!is_egl_proc_valid (egl->eglGetPlatformDisplayEXT, error)) return EGL_NO_DISPLAY; display = egl->eglGetPlatformDisplayEXT (platform, native_display, attrib_list); if (display == EGL_NO_DISPLAY) { set_egl_error (error); return EGL_NO_DISPLAY; } return display; } gboolean meta_egl_terminate (MetaEgl *egl, EGLDisplay display, GError **error) { if (!eglTerminate (display)) { set_egl_error (error); return FALSE; } return TRUE; } EGLContext meta_egl_create_context (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list, GError **error) { EGLContext context; context = eglCreateContext (display, config, share_context, attrib_list); if (context == EGL_NO_CONTEXT) { set_egl_error (error); return EGL_NO_CONTEXT; } return context; } gboolean meta_egl_destroy_context (MetaEgl *egl, EGLDisplay display, EGLContext context, GError **error) { if (!eglDestroyContext (display, context)) { set_egl_error (error); return FALSE; } return TRUE; } EGLImageKHR meta_egl_create_image (MetaEgl *egl, EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list, GError **error) { EGLImageKHR image; if (!is_egl_proc_valid (egl->eglCreateImageKHR, error)) return EGL_NO_IMAGE_KHR; image = egl->eglCreateImageKHR (display, context, target, buffer, attrib_list); if (image == EGL_NO_IMAGE_KHR) { set_egl_error (error); return EGL_NO_IMAGE_KHR; } return image; } gboolean meta_egl_destroy_image (MetaEgl *egl, EGLDisplay display, EGLImageKHR image, GError **error) { if (!is_egl_proc_valid (egl->eglDestroyImageKHR, error)) return FALSE; if (!egl->eglDestroyImageKHR (display, image)) { set_egl_error (error); return FALSE; } return TRUE; } EGLImageKHR meta_egl_create_dmabuf_image (MetaEgl *egl, EGLDisplay egl_display, unsigned int width, unsigned int height, uint32_t drm_format, uint32_t n_planes, const int *fds, const uint32_t *strides, const uint32_t *offsets, const uint64_t *modifiers, GError **error) { EGLint attribs[37]; int atti = 0; /* This requires the Mesa commit in * Mesa 10.3 (08264e5dad4df448e7718e782ad9077902089a07) or * Mesa 10.2.7 (55d28925e6109a4afd61f109e845a8a51bd17652). * Otherwise Mesa closes the fd behind our back and re-importing * will fail. * https://bugs.freedesktop.org/show_bug.cgi?id=76188 */ attribs[atti++] = EGL_WIDTH; attribs[atti++] = width; attribs[atti++] = EGL_HEIGHT; attribs[atti++] = height; attribs[atti++] = EGL_LINUX_DRM_FOURCC_EXT; attribs[atti++] = drm_format; if (n_planes > 0) { attribs[atti++] = EGL_DMA_BUF_PLANE0_FD_EXT; attribs[atti++] = fds[0]; attribs[atti++] = EGL_DMA_BUF_PLANE0_OFFSET_EXT; attribs[atti++] = offsets[0]; attribs[atti++] = EGL_DMA_BUF_PLANE0_PITCH_EXT; attribs[atti++] = strides[0]; if (modifiers) { attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_LO_EXT; attribs[atti++] = modifiers[0] & 0xFFFFFFFF; attribs[atti++] = EGL_DMA_BUF_PLANE0_MODIFIER_HI_EXT; attribs[atti++] = modifiers[0] >> 32; } } if (n_planes > 1) { attribs[atti++] = EGL_DMA_BUF_PLANE1_FD_EXT; attribs[atti++] = fds[1]; attribs[atti++] = EGL_DMA_BUF_PLANE1_OFFSET_EXT; attribs[atti++] = offsets[1]; attribs[atti++] = EGL_DMA_BUF_PLANE1_PITCH_EXT; attribs[atti++] = strides[1]; if (modifiers) { attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_LO_EXT; attribs[atti++] = modifiers[1] & 0xFFFFFFFF; attribs[atti++] = EGL_DMA_BUF_PLANE1_MODIFIER_HI_EXT; attribs[atti++] = modifiers[1] >> 32; } } if (n_planes > 2) { attribs[atti++] = EGL_DMA_BUF_PLANE2_FD_EXT; attribs[atti++] = fds[2]; attribs[atti++] = EGL_DMA_BUF_PLANE2_OFFSET_EXT; attribs[atti++] = offsets[2]; attribs[atti++] = EGL_DMA_BUF_PLANE2_PITCH_EXT; attribs[atti++] = strides[2]; if (modifiers) { attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_LO_EXT; attribs[atti++] = modifiers[2] & 0xFFFFFFFF; attribs[atti++] = EGL_DMA_BUF_PLANE2_MODIFIER_HI_EXT; attribs[atti++] = modifiers[2] >> 32; } } attribs[atti++] = EGL_NONE; g_assert (atti <= G_N_ELEMENTS (attribs)); return meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, EGL_LINUX_DMA_BUF_EXT, NULL, attribs, error); } gboolean meta_egl_make_current (MetaEgl *egl, EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context, GError **error) { if (!eglMakeCurrent (display, draw, read, context)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_swap_buffers (MetaEgl *egl, EGLDisplay display, EGLSurface surface, GError **error) { if (!eglSwapBuffers (display, surface)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_wayland_buffer (MetaEgl *egl, EGLDisplay display, struct wl_resource *buffer, EGLint attribute, EGLint *value, GError **error) { if (!is_egl_proc_valid (egl->eglQueryWaylandBufferWL, error)) return FALSE; if (!egl->eglQueryWaylandBufferWL (display, buffer, attribute, value)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_devices (MetaEgl *egl, EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices, GError **error) { if (!is_egl_proc_valid (egl->eglQueryDevicesEXT, error)) return FALSE; if (!egl->eglQueryDevicesEXT (max_devices, devices, num_devices)) { set_egl_error (error); return FALSE; } return TRUE; } const char * meta_egl_query_device_string (MetaEgl *egl, EGLDeviceEXT device, EGLint name, GError **error) { const char *device_string; if (!is_egl_proc_valid (egl->eglQueryDeviceStringEXT, error)) return NULL; device_string = egl->eglQueryDeviceStringEXT (device, name); if (!device_string) { set_egl_error (error); return NULL; } return device_string; } gboolean meta_egl_egl_device_has_extensions (MetaEgl *egl, EGLDeviceEXT device, const char ***missing_extensions, const char *first_extension, ...) { va_list var_args; const char *extensions_str; gboolean has_extensions; GError *error = NULL; extensions_str = meta_egl_query_device_string (egl, device, EGL_EXTENSIONS, &error); if (!extensions_str) { g_warning ("Failed to query device string: %s", error->message); g_error_free (error); return FALSE; } va_start (var_args, first_extension); has_extensions = meta_extensions_string_has_extensions_valist (extensions_str, missing_extensions, first_extension, var_args); va_end (var_args); return has_extensions; } gboolean meta_egl_get_output_layers (MetaEgl *egl, EGLDisplay display, const EGLAttrib *attrib_list, EGLOutputLayerEXT *layers, EGLint max_layers, EGLint *num_layers, GError **error) { if (!is_egl_proc_valid (egl->eglGetOutputLayersEXT, error)) return FALSE; if (!egl->eglGetOutputLayersEXT (display, attrib_list, layers, max_layers, num_layers)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_output_layer_attrib (MetaEgl *egl, EGLDisplay display, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib *value, GError **error) { if (!is_egl_proc_valid (egl->eglQueryOutputLayerAttribEXT, error)) return FALSE; if (!egl->eglQueryOutputLayerAttribEXT (display, layer, attribute, value)) { set_egl_error (error); return FALSE; } return TRUE; } EGLStreamKHR meta_egl_create_stream (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, GError **error) { EGLStreamKHR stream; if (!is_egl_proc_valid (egl->eglCreateStreamKHR, error)) return EGL_NO_STREAM_KHR; stream = egl->eglCreateStreamKHR (display, attrib_list); if (stream == EGL_NO_STREAM_KHR) { set_egl_error (error); return EGL_NO_STREAM_KHR; } return stream; } gboolean meta_egl_destroy_stream (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error) { if (!is_egl_proc_valid (egl->eglDestroyStreamKHR, error)) return FALSE; if (!egl->eglDestroyStreamKHR (display, stream)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_stream (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLenum attribute, EGLint *value, GError **error) { if (!is_egl_proc_valid (egl->eglQueryStreamKHR, error)) return FALSE; if (!egl->eglQueryStreamKHR (display, stream, attribute, value)) { set_egl_error (error); return FALSE; } return TRUE; } EGLStreamKHR meta_egl_create_stream_attrib (MetaEgl *egl, EGLDisplay display, const EGLAttrib *attrib_list, GError **error) { EGLStreamKHR stream; if (!is_egl_proc_valid (egl->eglCreateStreamAttribNV, error)) return FALSE; stream = egl->eglCreateStreamAttribNV (display, attrib_list); if (stream == EGL_NO_STREAM_KHR) { set_egl_error (error); return EGL_NO_STREAM_KHR; } return stream; } EGLSurface meta_egl_create_stream_producer_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLStreamKHR stream, const EGLint *attrib_list, GError **error) { EGLSurface surface; if (!is_egl_proc_valid (egl->eglCreateStreamProducerSurfaceKHR, error)) return EGL_NO_SURFACE; surface = egl->eglCreateStreamProducerSurfaceKHR (display, config, stream, attrib_list); if (surface == EGL_NO_SURFACE) { set_egl_error (error); return EGL_NO_SURFACE; } return surface; } gboolean meta_egl_stream_consumer_output (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLOutputLayerEXT layer, GError **error) { if (!is_egl_proc_valid (egl->eglStreamConsumerOutputEXT, error)) return FALSE; if (!egl->eglStreamConsumerOutputEXT (display, stream, layer)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_stream_consumer_acquire_attrib (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLAttrib *attrib_list, GError **error) { if (!is_egl_proc_valid (egl->eglStreamConsumerAcquireAttribNV, error)) return FALSE; if (!egl->eglStreamConsumerAcquireAttribNV (display, stream, attrib_list)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_stream_consumer_gl_texture_external (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error) { if (!is_egl_proc_valid (egl->eglStreamConsumerGLTextureExternalKHR, error)) return FALSE; if (!egl->eglStreamConsumerGLTextureExternalKHR (display, stream)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_stream_consumer_acquire (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error) { if (!is_egl_proc_valid (egl->eglStreamConsumerAcquireKHR, error)) return FALSE; if (!egl->eglStreamConsumerAcquireKHR (display, stream)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_dma_buf_formats (MetaEgl *egl, EGLDisplay display, EGLint max_formats, EGLint *formats, EGLint *num_formats, GError **error) { if (!is_egl_proc_valid (egl->eglQueryDmaBufFormatsEXT, error)) return FALSE; if (!egl->eglQueryDmaBufFormatsEXT (display, max_formats, formats, num_formats)) { set_egl_error (error); return FALSE; } return TRUE; } gboolean meta_egl_query_dma_buf_modifiers (MetaEgl *egl, EGLDisplay display, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_modifiers, GError **error) { if (!is_egl_proc_valid (egl->eglQueryDmaBufModifiersEXT, error)) return FALSE; if (!egl->eglQueryDmaBufModifiersEXT (display, format, max_modifiers, modifiers, external_only, num_modifiers)) { set_egl_error (error); return FALSE; } return TRUE; } #define GET_EGL_PROC_ADDR(proc) \ egl->proc = (void *) eglGetProcAddress (#proc); static void meta_egl_constructed (GObject *object) { MetaEgl *egl = META_EGL (object); GET_EGL_PROC_ADDR (eglGetPlatformDisplayEXT); GET_EGL_PROC_ADDR (eglCreateImageKHR); GET_EGL_PROC_ADDR (eglDestroyImageKHR); GET_EGL_PROC_ADDR (eglQueryWaylandBufferWL); GET_EGL_PROC_ADDR (eglQueryDevicesEXT); GET_EGL_PROC_ADDR (eglQueryDeviceStringEXT); GET_EGL_PROC_ADDR (eglGetOutputLayersEXT); GET_EGL_PROC_ADDR (eglQueryOutputLayerAttribEXT); GET_EGL_PROC_ADDR (eglCreateStreamKHR); GET_EGL_PROC_ADDR (eglDestroyStreamKHR); GET_EGL_PROC_ADDR (eglQueryStreamKHR); GET_EGL_PROC_ADDR (eglCreateStreamAttribNV); GET_EGL_PROC_ADDR (eglCreateStreamProducerSurfaceKHR); GET_EGL_PROC_ADDR (eglStreamConsumerOutputEXT); GET_EGL_PROC_ADDR (eglStreamConsumerGLTextureExternalKHR); GET_EGL_PROC_ADDR (eglStreamConsumerAcquireKHR); GET_EGL_PROC_ADDR (eglStreamConsumerAcquireAttribNV); GET_EGL_PROC_ADDR (eglQueryDmaBufFormatsEXT); GET_EGL_PROC_ADDR (eglQueryDmaBufModifiersEXT); } #undef GET_EGL_PROC_ADDR static void meta_egl_init (MetaEgl *egl) { } static void meta_egl_class_init (MetaEglClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_egl_constructed; } muffin-6.4.1/src/backends/meta-cursor-tracker.c0000664000175000017500000003077314723361714020353 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Giovanni Campagna */ /** * SECTION:cursor-tracker * @title: MetaCursorTracker * @short_description: Mutter cursor tracking helper. Originally only * tracking the cursor image, now more of a "core * pointer abstraction" */ #include "config.h" #include "backends/meta-cursor-tracker-private.h" #include #include #include #include "backends/meta-backend-private.h" #include "backends/x11/cm/meta-cursor-sprite-xfixes.h" #include "cogl/cogl.h" #include "clutter/clutter.h" #include "meta-marshal.h" #include "meta/main.h" #include "meta/meta-x11-errors.h" #include "meta/util.h" #include "x11/meta-x11-display-private.h" G_DEFINE_TYPE (MetaCursorTracker, meta_cursor_tracker, G_TYPE_OBJECT); enum { CURSOR_CHANGED, CURSOR_MOVED, VISIBILITY_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; static void cursor_texture_updated (MetaCursorSprite *cursor, MetaCursorTracker *tracker) { g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); } static gboolean update_displayed_cursor (MetaCursorTracker *tracker) { MetaDisplay *display = meta_get_display (); MetaCursorSprite *cursor = NULL; if (display && meta_display_windows_are_interactable (display) && tracker->has_window_cursor) cursor = tracker->window_cursor; else cursor = tracker->root_cursor; if (tracker->displayed_cursor == cursor) return FALSE; if (tracker->displayed_cursor) { g_signal_handlers_disconnect_by_func (tracker->displayed_cursor, cursor_texture_updated, tracker); } g_set_object (&tracker->displayed_cursor, cursor); if (cursor) { g_signal_connect (cursor, "texture-changed", G_CALLBACK (cursor_texture_updated), tracker); } return TRUE; } static gboolean update_effective_cursor (MetaCursorTracker *tracker) { MetaCursorSprite *cursor = NULL; if (tracker->is_showing) cursor = tracker->displayed_cursor; return g_set_object (&tracker->effective_cursor, cursor); } static void change_cursor_renderer (MetaCursorTracker *tracker) { MetaBackend *backend = meta_get_backend (); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); meta_cursor_renderer_set_cursor (cursor_renderer, tracker->effective_cursor); } static void sync_cursor (MetaCursorTracker *tracker) { gboolean cursor_changed = FALSE; cursor_changed = update_displayed_cursor (tracker); if (update_effective_cursor (tracker)) change_cursor_renderer (tracker); if (cursor_changed) g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); } static void meta_cursor_tracker_init (MetaCursorTracker *self) { self->is_showing = TRUE; } static void meta_cursor_tracker_finalize (GObject *object) { MetaCursorTracker *self = META_CURSOR_TRACKER (object); if (self->effective_cursor) g_object_unref (self->effective_cursor); if (self->displayed_cursor) g_object_unref (self->displayed_cursor); if (self->root_cursor) g_object_unref (self->root_cursor); G_OBJECT_CLASS (meta_cursor_tracker_parent_class)->finalize (object); } static void meta_cursor_tracker_class_init (MetaCursorTrackerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_cursor_tracker_finalize; signals[CURSOR_CHANGED] = g_signal_new ("cursor-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaCursorTracker::cursor-moved: * @cursor: The #MetaCursorTracker * @x: The new X coordinate of the cursor * @y: The new Y coordinate of the cursor * * Notifies when the cursor has moved to a new location. */ signals[CURSOR_MOVED] = g_signal_new ("cursor-moved", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, meta_marshal_VOID__FLOAT_FLOAT, G_TYPE_NONE, 2, G_TYPE_FLOAT, G_TYPE_FLOAT); g_signal_set_va_marshaller (signals[CURSOR_MOVED], G_TYPE_FROM_CLASS (klass), meta_marshal_VOID__FLOAT_FLOATv); signals[VISIBILITY_CHANGED] = g_signal_new ("visibility-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /** * meta_cursor_tracker_get_for_display: * @display: the #MetaDisplay * * Retrieves the cursor tracker object for @display. * * Returns: (transfer none): */ MetaCursorTracker * meta_cursor_tracker_get_for_display (MetaDisplay *display) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *tracker = meta_backend_get_cursor_tracker (backend); g_assert (tracker); return tracker; } static void set_window_cursor (MetaCursorTracker *tracker, gboolean has_cursor, MetaCursorSprite *cursor_sprite) { g_clear_object (&tracker->window_cursor); if (cursor_sprite) tracker->window_cursor = g_object_ref (cursor_sprite); tracker->has_window_cursor = has_cursor; sync_cursor (tracker); } gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker, XEvent *xevent) { MetaX11Display *x11_display = meta_get_display ()->x11_display; XFixesCursorNotifyEvent *notify_event; if (meta_is_wayland_compositor ()) return FALSE; if (xevent->xany.type != x11_display->xfixes_event_base + XFixesCursorNotify) return FALSE; notify_event = (XFixesCursorNotifyEvent *)xevent; if (notify_event->subtype != XFixesDisplayCursorNotify) return FALSE; g_clear_object (&tracker->xfixes_cursor); g_signal_emit (tracker, signals[CURSOR_CHANGED], 0); return TRUE; } static void ensure_xfixes_cursor (MetaCursorTracker *tracker) { MetaDisplay *display = meta_get_display (); g_autoptr (GError) error = NULL; if (tracker->xfixes_cursor) return; tracker->xfixes_cursor = meta_cursor_sprite_xfixes_new (display, &error); if (!tracker->xfixes_cursor) g_warning ("Failed to create XFIXES cursor: %s", error->message); } /** * meta_cursor_tracker_get_sprite: * * Returns: (transfer none): */ CoglTexture * meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker) { MetaCursorSprite *cursor_sprite; g_return_val_if_fail (META_IS_CURSOR_TRACKER (tracker), NULL); if (meta_is_wayland_compositor ()) { cursor_sprite = tracker->displayed_cursor; } else { ensure_xfixes_cursor (tracker); cursor_sprite = META_CURSOR_SPRITE (tracker->xfixes_cursor); } if (cursor_sprite) { meta_cursor_sprite_realize_texture (cursor_sprite); return meta_cursor_sprite_get_cogl_texture (cursor_sprite); } else { return NULL; } } /** * meta_cursor_tracker_get_hot: * @tracker: * @x: (out): * @y: (out): * */ void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker, int *x, int *y) { MetaCursorSprite *cursor_sprite; g_return_if_fail (META_IS_CURSOR_TRACKER (tracker)); if (meta_is_wayland_compositor ()) { cursor_sprite = tracker->displayed_cursor; } else { ensure_xfixes_cursor (tracker); cursor_sprite = META_CURSOR_SPRITE (tracker->xfixes_cursor); } if (cursor_sprite) meta_cursor_sprite_get_hotspot (cursor_sprite, x, y); else { if (x) *x = 0; if (y) *y = 0; } } void meta_cursor_tracker_set_window_cursor (MetaCursorTracker *tracker, MetaCursorSprite *cursor_sprite) { set_window_cursor (tracker, TRUE, cursor_sprite); } void meta_cursor_tracker_unset_window_cursor (MetaCursorTracker *tracker) { set_window_cursor (tracker, FALSE, NULL); } /** * meta_cursor_tracker_set_root_cursor: * @tracker: a #MetaCursorTracker object. * @cursor_sprite: (transfer none): the new root cursor * * Sets the root cursor (the cursor that is shown if not modified by a window). * The #MetaCursorTracker will take a strong reference to the sprite. */ void meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker, MetaCursorSprite *cursor_sprite) { g_clear_object (&tracker->root_cursor); if (cursor_sprite) tracker->root_cursor = g_object_ref (cursor_sprite); sync_cursor (tracker); } void meta_cursor_tracker_update_position (MetaCursorTracker *tracker, float new_x, float new_y) { MetaBackend *backend = meta_get_backend (); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); g_assert (meta_is_wayland_compositor ()); meta_cursor_renderer_set_position (cursor_renderer, new_x, new_y); g_signal_emit (tracker, signals[CURSOR_MOVED], 0, new_x, new_y); } static void get_pointer_position_gdk (int *x, int *y, int *mods) { GdkSeat *gseat; GdkDevice *gdevice; GdkScreen *gscreen; gseat = gdk_display_get_default_seat (gdk_display_get_default ()); gdevice = gdk_seat_get_pointer (gseat); gdk_device_get_position (gdevice, &gscreen, x, y); if (mods) gdk_device_get_state (gdevice, gdk_screen_get_root_window (gscreen), NULL, (GdkModifierType*)mods); } static void get_pointer_position_clutter (int *x, int *y, int *mods) { ClutterSeat *seat; ClutterInputDevice *cdevice; graphene_point_t point; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); cdevice = clutter_seat_get_pointer (seat); clutter_input_device_get_coords (cdevice, NULL, &point); if (x) *x = point.x; if (y) *y = point.y; if (mods) *mods = clutter_input_device_get_modifier_state (cdevice); } void meta_cursor_tracker_get_pointer (MetaCursorTracker *tracker, int *x, int *y, ClutterModifierType *mods) { /* We can't use the clutter interface when not running as a wayland compositor, because we need to query the server, rather than using the last cached value. OTOH, on wayland we can't use GDK, because that only sees the events we forward to xwayland. */ if (meta_is_wayland_compositor ()) get_pointer_position_clutter (x, y, (int*)mods); else get_pointer_position_gdk (x, y, (int*)mods); } gboolean meta_cursor_tracker_get_pointer_visible (MetaCursorTracker *tracker) { return tracker->is_showing; } void meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker, gboolean visible) { if (visible == tracker->is_showing) return; tracker->is_showing = visible; sync_cursor (tracker); g_signal_emit (tracker, signals[VISIBILITY_CHANGED], 0); } MetaCursorSprite * meta_cursor_tracker_get_displayed_cursor (MetaCursorTracker *tracker) { return tracker->displayed_cursor; } muffin-6.4.1/src/backends/meta-gpu.h0000664000175000017500000000371514723361714016201 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_GPU_H #define META_GPU_H #include #include "backends/meta-monitor-manager-private.h" #define META_TYPE_GPU (meta_gpu_get_type ()) META_EXPORT_TEST G_DECLARE_DERIVABLE_TYPE (MetaGpu, meta_gpu, META, GPU, GObject) struct _MetaGpuClass { GObjectClass parent_class; gboolean (* read_current) (MetaGpu *gpu, GError **error); }; META_EXPORT_TEST gboolean meta_gpu_read_current (MetaGpu *gpu, GError **error); META_EXPORT_TEST gboolean meta_gpu_has_hotplug_mode_update (MetaGpu *gpu); META_EXPORT_TEST MetaBackend * meta_gpu_get_backend (MetaGpu *gpu); META_EXPORT_TEST GList * meta_gpu_get_outputs (MetaGpu *gpu); META_EXPORT_TEST GList * meta_gpu_get_crtcs (MetaGpu *gpu); META_EXPORT_TEST GList * meta_gpu_get_modes (MetaGpu *gpu); META_EXPORT_TEST void meta_gpu_take_outputs (MetaGpu *gpu, GList *outputs); META_EXPORT_TEST void meta_gpu_take_crtcs (MetaGpu *gpu, GList *crtcs); META_EXPORT_TEST void meta_gpu_take_modes (MetaGpu *gpu, GList *modes); #endif /* META_GPU_H */ muffin-6.4.1/src/backends/meta-renderer.h0000664000175000017500000000426314723361714017213 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_RENDERER_H #define META_RENDERER_H #include #include "backends/meta-monitor-manager-private.h" #include "backends/meta-renderer-view.h" #include "core/util-private.h" #include "clutter/clutter-mutter.h" #include "cogl/cogl.h" #define META_TYPE_RENDERER (meta_renderer_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaRenderer, meta_renderer, META, RENDERER, GObject) struct _MetaRendererClass { GObjectClass parent_class; CoglRenderer * (* create_cogl_renderer) (MetaRenderer *renderer); MetaRendererView * (* create_view) (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor, MetaOutput *output, MetaCrtc *crtc); void (* rebuild_views) (MetaRenderer *renderer); }; MetaBackend * meta_renderer_get_backend (MetaRenderer *renderer); CoglRenderer * meta_renderer_create_cogl_renderer (MetaRenderer *renderer); void meta_renderer_rebuild_views (MetaRenderer *renderer); void meta_renderer_add_view (MetaRenderer *renderer, MetaRendererView *view); META_EXPORT_TEST GList * meta_renderer_get_views (MetaRenderer *renderer); gboolean meta_renderer_is_hardware_accelerated (MetaRenderer *renderer); #endif /* META_RENDERER_H */ muffin-6.4.1/src/backends/native/0000775000175000017500000000000014723361714015571 5ustar fabiofabiomuffin-6.4.1/src/backends/native/meta-drm-buffer-dumb.c0000664000175000017500000000332214723361714021637 0ustar fabiofabio/* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2016 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * Copyright (C) 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/native/meta-drm-buffer-dumb.h" struct _MetaDrmBufferDumb { MetaDrmBuffer parent; uint32_t fb_id; }; G_DEFINE_TYPE (MetaDrmBufferDumb, meta_drm_buffer_dumb, META_TYPE_DRM_BUFFER) MetaDrmBufferDumb * meta_drm_buffer_dumb_new (uint32_t dumb_fb_id) { MetaDrmBufferDumb *buffer_dumb; buffer_dumb = g_object_new (META_TYPE_DRM_BUFFER_DUMB, NULL); buffer_dumb->fb_id = dumb_fb_id; return buffer_dumb; } static uint32_t meta_drm_buffer_dumb_get_fb_id (MetaDrmBuffer *buffer) { return META_DRM_BUFFER_DUMB (buffer)->fb_id; } static void meta_drm_buffer_dumb_init (MetaDrmBufferDumb *buffer_dumb) { } static void meta_drm_buffer_dumb_class_init (MetaDrmBufferDumbClass *klass) { MetaDrmBufferClass *buffer_class = META_DRM_BUFFER_CLASS (klass); buffer_class->get_fb_id = meta_drm_buffer_dumb_get_fb_id; } muffin-6.4.1/src/backends/native/meta-drm-buffer-gbm.h0000664000175000017500000000312314723361714021461 0ustar fabiofabio/* * Copyright (C) 2018 Canonical Ltd. * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_DRM_BUFFER_GBM_H #define META_DRM_BUFFER_GBM_H #include #include "backends/native/meta-drm-buffer.h" #include "backends/native/meta-gpu-kms.h" #define META_TYPE_DRM_BUFFER_GBM (meta_drm_buffer_gbm_get_type ()) G_DECLARE_FINAL_TYPE (MetaDrmBufferGbm, meta_drm_buffer_gbm, META, DRM_BUFFER_GBM, MetaDrmBuffer) MetaDrmBufferGbm * meta_drm_buffer_gbm_new (MetaGpuKms *gpu_kms, struct gbm_surface *gbm_surface, gboolean use_modifiers, GError **error); struct gbm_bo * meta_drm_buffer_gbm_get_bo (MetaDrmBufferGbm *buffer_gbm); #endif /* META_DRM_BUFFER_GBM_H */ muffin-6.4.1/src/backends/native/meta-input-settings-native.h0000664000175000017500000000411514723361714023150 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_SETTINGS_NATIVE_H #define META_INPUT_SETTINGS_NATIVE_H #include "backends/meta-input-settings-private.h" #define META_TYPE_INPUT_SETTINGS_NATIVE (meta_input_settings_native_get_type ()) #define META_INPUT_SETTINGS_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_INPUT_SETTINGS_NATIVE, MetaInputSettingsNative)) #define META_INPUT_SETTINGS_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_INPUT_SETTINGS_NATIVE, MetaInputSettingsNativeClass)) #define META_IS_INPUT_SETTINGS_NATIVE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_INPUT_SETTINGS_NATIVE)) #define META_IS_INPUT_SETTINGS_NATIVE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_INPUT_SETTINGS_NATIVE)) #define META_INPUT_SETTINGS_NATIVE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_INPUT_SETTINGS_NATIVE, MetaInputSettingsNativeClass)) typedef struct _MetaInputSettingsNative MetaInputSettingsNative; typedef struct _MetaInputSettingsNativeClass MetaInputSettingsNativeClass; struct _MetaInputSettingsNative { MetaInputSettings parent_instance; }; struct _MetaInputSettingsNativeClass { MetaInputSettingsClass parent_class; }; GType meta_input_settings_native_get_type (void) G_GNUC_CONST; #endif /* META_INPUT_SETTINGS_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-xkb-utils.h0000664000175000017500000000337714723361714020622 0ustar fabiofabio/* * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * Authors: * Damien Lespiau */ #ifndef META_XKB_UTILS_H #define META_XKB_UTILS_H #include #include "clutter/clutter.h" ClutterEvent * meta_key_event_new_from_evdev (ClutterInputDevice *device, ClutterInputDevice *core_keyboard, ClutterStage *stage, struct xkb_state *xkb_state, uint32_t button_state, uint32_t _time, uint32_t key, uint32_t state); void meta_xkb_translate_state (ClutterEvent *event, struct xkb_state *xkb_state, uint32_t button_state); #endif /* META_XKB_UTILS_H */ muffin-6.4.1/src/backends/native/meta-kms-types.h0000664000175000017500000000353214723361714020625 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_IMPL_TYPES_H #define META_KMS_IMPL_TYPES_H #include typedef struct _MetaKms MetaKms; typedef struct _MetaKmsDevice MetaKmsDevice; typedef struct _MetaKmsPlane MetaKmsPlane; typedef struct _MetaKmsCrtc MetaKmsCrtc; typedef struct _MetaKmsConnector MetaKmsConnector; typedef struct _MetaKmsUpdate MetaKmsUpdate; typedef struct _MetaKmsPlaneAssignment MetaKmsPlaneAssignment; typedef struct _MetaKmsModeSet MetaKmsModeSet; typedef struct _MetaKmsFeedback MetaKmsFeedback; typedef struct _MetaKmsPageFlipFeedback MetaKmsPageFlipFeedback; typedef struct _MetaKmsImpl MetaKmsImpl; typedef struct _MetaKmsImplDevice MetaKmsImplDevice; /* 16:16 fixed point */ typedef int32_t MetaFixed16; typedef struct _MetaFixed16Rectangle { MetaFixed16 x; MetaFixed16 y; MetaFixed16 width; MetaFixed16 height; } MetaFixed16Rectangle; typedef enum _MetaKmsDeviceFlag { META_KMS_DEVICE_FLAG_NONE = 0, META_KMS_DEVICE_FLAG_BOOT_VGA = 1 << 0, META_KMS_DEVICE_FLAG_PLATFORM_DEVICE = 1 << 1, } MetaKmsDeviceFlag; typedef enum _MetaKmsPlaneType MetaKmsPlaneType; #endif /* META_KMS_IMPL_TYPES_H */ muffin-6.4.1/src/backends/native/meta-kms-impl-device.h0000664000175000017500000000627414723361714021665 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_IMPL_DEVICE_H #define META_KMS_IMPL_DEVICE_H #include #include #include #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-types.h" #include "backends/native/meta-kms-update.h" typedef struct _MetaKmsDeviceCaps { gboolean has_cursor_size; uint64_t cursor_width; uint64_t cursor_height; } MetaKmsDeviceCaps; #define META_TYPE_KMS_IMPL_DEVICE (meta_kms_impl_device_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsImplDevice, meta_kms_impl_device, META, KMS_IMPL_DEVICE, GObject) MetaKmsDevice * meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_copy_connectors (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device); GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device); const MetaKmsDeviceCaps * meta_kms_impl_device_get_caps (MetaKmsImplDevice *impl_device); gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device, GError **error); drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props, const char *prop_name, int *idx); int meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device); int meta_kms_impl_device_leak_fd (MetaKmsImplDevice *impl_device); void meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device); void meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update); MetaKmsPlane * meta_kms_impl_device_add_fake_plane (MetaKmsImplDevice *impl_device, MetaKmsPlaneType plane_type, MetaKmsCrtc *crtc); int meta_kms_impl_device_close (MetaKmsImplDevice *impl_device); MetaKmsImplDevice * meta_kms_impl_device_new (MetaKmsDevice *device, MetaKmsImpl *kms_impl, int fd, GError **error); #endif /* META_KMS_IMPL_DEVICE_H */ muffin-6.4.1/src/backends/native/meta-kms-impl.c0000664000175000017500000000762414723361714020423 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-impl.h" enum { PROP_0, PROP_KMS, }; typedef struct _MetaKmsImplPrivate { MetaKms *kms; } MetaKmsImplPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaKmsImpl, meta_kms_impl, G_TYPE_OBJECT) MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl) { MetaKmsImplPrivate *priv = meta_kms_impl_get_instance_private (impl); return priv->kms; } MetaKmsFeedback * meta_kms_impl_process_update (MetaKmsImpl *impl, MetaKmsUpdate *update) { return META_KMS_IMPL_GET_CLASS (impl)->process_update (impl, update); } void meta_kms_impl_handle_page_flip_callback (MetaKmsImpl *impl, MetaKmsPageFlipData *page_flip_data) { META_KMS_IMPL_GET_CLASS (impl)->handle_page_flip_callback (impl, page_flip_data); } void meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl) { META_KMS_IMPL_GET_CLASS (impl)->discard_pending_page_flips (impl); } void meta_kms_impl_dispatch_idle (MetaKmsImpl *impl) { META_KMS_IMPL_GET_CLASS (impl)->dispatch_idle (impl); } void meta_kms_impl_notify_device_created (MetaKmsImpl *impl, MetaKmsDevice *device) { META_KMS_IMPL_GET_CLASS (impl)->notify_device_created (impl, device); } static void meta_kms_impl_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaKmsImpl *impl = META_KMS_IMPL (object); MetaKmsImplPrivate *priv = meta_kms_impl_get_instance_private (impl); switch (prop_id) { case PROP_KMS: priv->kms = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_kms_impl_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaKmsImpl *impl = META_KMS_IMPL (object); MetaKmsImplPrivate *priv = meta_kms_impl_get_instance_private (impl); switch (prop_id) { case PROP_KMS: g_value_set_object (value, priv->kms); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_kms_impl_init (MetaKmsImpl *kms_impl) { } static void meta_kms_impl_class_init (MetaKmsImplClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->set_property = meta_kms_impl_set_property; object_class->get_property = meta_kms_impl_get_property; pspec = g_param_spec_object ("kms", "kms", "MetaKms", META_TYPE_KMS, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_KMS, pspec); } muffin-6.4.1/src/backends/native/meta-input-settings-native.c0000664000175000017500000006110514723361714023145 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include "backends/meta-logical-monitor.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-input-device-native.h" #include "backends/native/meta-input-device-tool-native.h" #include "backends/native/meta-input-settings-native.h" G_DEFINE_TYPE (MetaInputSettingsNative, meta_input_settings_native, META_TYPE_INPUT_SETTINGS) static void meta_input_settings_native_set_send_events (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopDeviceSendEvents mode) { enum libinput_config_send_events_mode libinput_mode; struct libinput_device *libinput_device; switch (mode) { case C_DESKTOP_DEVICE_SEND_EVENTS_DISABLED: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED; break; case C_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE; break; case C_DESKTOP_DEVICE_SEND_EVENTS_ENABLED: libinput_mode = LIBINPUT_CONFIG_SEND_EVENTS_ENABLED; break; default: g_assert_not_reached (); } libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; libinput_device_config_send_events_set_mode (libinput_device, libinput_mode); } static void meta_input_settings_native_set_matrix (MetaInputSettings *settings, ClutterInputDevice *device, gfloat matrix[6]) { cairo_matrix_t dev_matrix; cairo_matrix_init (&dev_matrix, matrix[0], matrix[3], matrix[1], matrix[4], matrix[2], matrix[5]); g_object_set (device, "device-matrix", &dev_matrix, NULL); } static void meta_input_settings_native_set_speed (MetaInputSettings *settings, ClutterInputDevice *device, gdouble speed) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; libinput_device_config_accel_set_speed (libinput_device, CLAMP (speed, -1, 1)); } static void meta_input_settings_native_set_left_handed (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_left_handed_is_available (libinput_device)) libinput_device_config_left_handed_set (libinput_device, enabled); } static void meta_input_settings_native_set_tap_enabled (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_tap_get_finger_count (libinput_device) > 0) libinput_device_config_tap_set_enabled (libinput_device, enabled ? LIBINPUT_CONFIG_TAP_ENABLED : LIBINPUT_CONFIG_TAP_DISABLED); } static void meta_input_settings_native_set_tap_and_drag_enabled (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_tap_get_finger_count (libinput_device) > 0) libinput_device_config_tap_set_drag_enabled (libinput_device, enabled ? LIBINPUT_CONFIG_DRAG_ENABLED : LIBINPUT_CONFIG_DRAG_DISABLED); } static void meta_input_settings_native_set_disable_while_typing (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_dwt_is_available (libinput_device)) libinput_device_config_dwt_set_enabled (libinput_device, enabled ? LIBINPUT_CONFIG_DWT_ENABLED : LIBINPUT_CONFIG_DWT_DISABLED); } static void meta_input_settings_native_set_invert_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean inverted) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_scroll_has_natural_scroll (libinput_device)) libinput_device_config_scroll_set_natural_scroll_enabled (libinput_device, inverted); } static gboolean device_set_scroll_method (struct libinput_device *libinput_device, enum libinput_config_scroll_method method) { enum libinput_config_status status = libinput_device_config_scroll_set_method (libinput_device, method); return status == LIBINPUT_CONFIG_STATUS_SUCCESS; } static gboolean device_set_click_method (struct libinput_device *libinput_device, enum libinput_config_click_method method) { enum libinput_config_status status = libinput_device_config_click_set_method (libinput_device, method); return status == LIBINPUT_CONFIG_STATUS_SUCCESS; } static void meta_input_settings_native_set_edge_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean edge_scrolling_enabled) { struct libinput_device *libinput_device; enum libinput_config_scroll_method current, method; libinput_device = meta_input_device_native_get_libinput_device (device); method = edge_scrolling_enabled ? LIBINPUT_CONFIG_SCROLL_EDGE : LIBINPUT_CONFIG_SCROLL_NO_SCROLL; current = libinput_device_config_scroll_get_method (libinput_device); current &= ~LIBINPUT_CONFIG_SCROLL_EDGE; device_set_scroll_method (libinput_device, current | method); } static void meta_input_settings_native_set_two_finger_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean two_finger_scroll_enabled) { struct libinput_device *libinput_device; enum libinput_config_scroll_method current, method; libinput_device = meta_input_device_native_get_libinput_device (device); method = two_finger_scroll_enabled ? LIBINPUT_CONFIG_SCROLL_2FG : LIBINPUT_CONFIG_SCROLL_NO_SCROLL; current = libinput_device_config_scroll_get_method (libinput_device); current &= ~LIBINPUT_CONFIG_SCROLL_2FG; device_set_scroll_method (libinput_device, current | method); } static gboolean meta_input_settings_native_has_two_finger_scroll (MetaInputSettings *settings, ClutterInputDevice *device) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return FALSE; return libinput_device_config_scroll_get_methods (libinput_device) & LIBINPUT_CONFIG_SCROLL_2FG; } static void meta_input_settings_native_set_scroll_button (MetaInputSettings *settings, ClutterInputDevice *device, guint button) { struct libinput_device *libinput_device; enum libinput_config_scroll_method method; guint evcode; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (button == 0) { method = LIBINPUT_CONFIG_SCROLL_NO_SCROLL; evcode = 0; } else { /* Compensate for X11 scroll buttons */ if (button > 7) button -= 4; /* Button is 1-indexed */ evcode = (BTN_LEFT - 1) + button; method = LIBINPUT_CONFIG_SCROLL_ON_BUTTON_DOWN; } if (!device_set_scroll_method (libinput_device, method)) return; libinput_device_config_scroll_set_button (libinput_device, evcode); } static void meta_input_settings_native_set_click_method (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTouchpadClickMethod mode) { enum libinput_config_click_method click_method = 0; struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; switch (mode) { case C_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT: click_method = libinput_device_config_click_get_default_method (libinput_device); break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE: click_method = LIBINPUT_CONFIG_CLICK_METHOD_NONE; break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS: click_method = LIBINPUT_CONFIG_CLICK_METHOD_BUTTON_AREAS; break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_FINGERS: click_method = LIBINPUT_CONFIG_CLICK_METHOD_CLICKFINGER; break; default: g_assert_not_reached (); return; } device_set_click_method (libinput_device, click_method); } static void meta_input_settings_native_set_keyboard_repeat (MetaInputSettings *settings, gboolean enabled, guint delay, guint interval) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); meta_seat_native_set_keyboard_repeat (META_SEAT_NATIVE (seat), enabled, delay, interval); } static void set_device_accel_profile (ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { struct libinput_device *libinput_device; enum libinput_config_accel_profile libinput_profile; uint32_t profiles; libinput_device = meta_input_device_native_get_libinput_device (device); switch (profile) { case C_DESKTOP_POINTER_ACCEL_PROFILE_FLAT: libinput_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_FLAT; break; case C_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE: libinput_profile = LIBINPUT_CONFIG_ACCEL_PROFILE_ADAPTIVE; break; default: g_warn_if_reached (); case C_DESKTOP_POINTER_ACCEL_PROFILE_DEFAULT: libinput_profile = libinput_device_config_accel_get_default_profile (libinput_device); } profiles = libinput_device_config_accel_get_profiles (libinput_device); if ((profiles & libinput_profile) == 0) { libinput_profile = libinput_device_config_accel_get_default_profile (libinput_device); } libinput_device_config_accel_set_profile (libinput_device, libinput_profile); } static gboolean has_udev_property (ClutterInputDevice *device, const char *property) { struct libinput_device *libinput_device; struct udev_device *udev_device; struct udev_device *parent_udev_device; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return FALSE; udev_device = libinput_device_get_udev_device (libinput_device); if (!udev_device) return FALSE; if (NULL != udev_device_get_property_value (udev_device, property)) { udev_device_unref (udev_device); return TRUE; } parent_udev_device = udev_device_get_parent (udev_device); udev_device_unref (udev_device); if (!parent_udev_device) return FALSE; if (NULL != udev_device_get_property_value (parent_udev_device, property)) return TRUE; return FALSE; } static gboolean is_mouse_device (ClutterInputDevice *device) { return (has_udev_property (device, "ID_INPUT_MOUSE") && !has_udev_property (device, "ID_INPUT_POINTINGSTICK")); } static gboolean meta_input_settings_native_is_touchpad_device (MetaInputSettings *settings, ClutterInputDevice *device) { return has_udev_property (device, "ID_INPUT_TOUCHPAD"); } static gboolean meta_input_settings_native_is_trackball_device (MetaInputSettings *settings, ClutterInputDevice *device) { return has_udev_property (device, "ID_INPUT_TRACKBALL"); } static void meta_input_settings_native_set_mouse_accel_profile (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { if (!is_mouse_device (device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_native_set_trackball_accel_profile (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { if (!meta_input_settings_native_is_trackball_device (settings, device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_native_set_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTabletMapping mapping) { ClutterInputDeviceMapping dev_mapping; if (mapping == C_DESKTOP_TABLET_MAPPING_ABSOLUTE) dev_mapping = CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE; else if (mapping == C_DESKTOP_TABLET_MAPPING_RELATIVE) dev_mapping = CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE; else return; clutter_input_device_set_mapping_mode (device, dev_mapping); } static void meta_input_settings_native_set_tablet_keep_aspect (MetaInputSettings *settings, ClutterInputDevice *device, MetaLogicalMonitor *logical_monitor, gboolean keep_aspect) { double aspect_ratio = 0; if (keep_aspect) { int width, height; if (logical_monitor) { width = logical_monitor->rect.width; height = logical_monitor->rect.height; } else { MetaMonitorManager *monitor_manager; MetaBackend *backend; backend = meta_get_backend (); monitor_manager = meta_backend_get_monitor_manager (backend); meta_monitor_manager_get_screen_size (monitor_manager, &width, &height); } aspect_ratio = (double) width / height; } g_object_set (device, "output-aspect-ratio", aspect_ratio, NULL); } static void meta_input_settings_native_set_tablet_area (MetaInputSettings *settings, ClutterInputDevice *device, gdouble padding_left, gdouble padding_right, gdouble padding_top, gdouble padding_bottom) { struct libinput_device *libinput_device; gfloat scale_x; gfloat scale_y; gfloat offset_x; gfloat offset_y; scale_x = 1. / (1. - (padding_left + padding_right)); scale_y = 1. / (1. - (padding_top + padding_bottom)); offset_x = -padding_left * scale_x; offset_y = -padding_top * scale_y; gfloat matrix[6] = { scale_x, 0., offset_x, 0., scale_y, offset_y }; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device || !libinput_device_config_calibration_has_matrix (libinput_device)) return; libinput_device_config_calibration_set_matrix (libinput_device, matrix); } static void meta_input_settings_native_set_stylus_pressure (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, const gint curve[4]) { gdouble pressure_curve[4]; pressure_curve[0] = (gdouble) curve[0] / 100; pressure_curve[1] = (gdouble) curve[1] / 100; pressure_curve[2] = (gdouble) curve[2] / 100; pressure_curve[3] = (gdouble) curve[3] / 100; meta_input_device_tool_native_set_pressure_curve (tool, pressure_curve); } static guint action_to_evcode (CDesktopStylusButtonAction action) { switch (action) { case C_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE: return BTN_STYLUS; case C_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT: return BTN_STYLUS2; case C_DESKTOP_STYLUS_BUTTON_ACTION_BACK: return BTN_BACK; case C_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD: return BTN_FORWARD; case C_DESKTOP_STYLUS_BUTTON_ACTION_DISABLED: return BTN_DEAD; // This needs testing under wayland? case C_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT: default: return 0; } } static void meta_input_settings_native_set_stylus_button_map (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, CDesktopStylusButtonAction primary, CDesktopStylusButtonAction secondary, CDesktopStylusButtonAction tertiary) { meta_input_device_tool_native_set_button_code (tool, CLUTTER_BUTTON_MIDDLE, action_to_evcode (primary)); meta_input_device_tool_native_set_button_code (tool, CLUTTER_BUTTON_SECONDARY, action_to_evcode (secondary)); meta_input_device_tool_native_set_button_code (tool, 8, /* Back */ action_to_evcode (tertiary)); } static void meta_input_settings_native_set_mouse_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; if (!is_mouse_device (device)) return; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_middle_emulation_is_available (libinput_device)) libinput_device_config_middle_emulation_set_enabled (libinput_device, enabled); } static void meta_input_settings_native_set_touchpad_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; if (!meta_input_settings_native_is_touchpad_device (settings, device)) return; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_middle_emulation_is_available (libinput_device)) libinput_device_config_middle_emulation_set_enabled (libinput_device, enabled); } static void meta_input_settings_native_set_trackball_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { struct libinput_device *libinput_device; if (!meta_input_settings_native_is_trackball_device (settings, device)) return; libinput_device = meta_input_device_native_get_libinput_device (device); if (!libinput_device) return; if (libinput_device_config_middle_emulation_is_available (libinput_device)) libinput_device_config_middle_emulation_set_enabled (libinput_device, enabled); } static void meta_input_settings_native_class_init (MetaInputSettingsNativeClass *klass) { MetaInputSettingsClass *input_settings_class = META_INPUT_SETTINGS_CLASS (klass); input_settings_class->set_send_events = meta_input_settings_native_set_send_events; input_settings_class->set_matrix = meta_input_settings_native_set_matrix; input_settings_class->set_speed = meta_input_settings_native_set_speed; input_settings_class->set_left_handed = meta_input_settings_native_set_left_handed; input_settings_class->set_tap_enabled = meta_input_settings_native_set_tap_enabled; input_settings_class->set_tap_and_drag_enabled = meta_input_settings_native_set_tap_and_drag_enabled; input_settings_class->set_invert_scroll = meta_input_settings_native_set_invert_scroll; input_settings_class->set_edge_scroll = meta_input_settings_native_set_edge_scroll; input_settings_class->set_two_finger_scroll = meta_input_settings_native_set_two_finger_scroll; input_settings_class->set_scroll_button = meta_input_settings_native_set_scroll_button; input_settings_class->set_click_method = meta_input_settings_native_set_click_method; input_settings_class->set_keyboard_repeat = meta_input_settings_native_set_keyboard_repeat; input_settings_class->set_disable_while_typing = meta_input_settings_native_set_disable_while_typing; input_settings_class->set_tablet_mapping = meta_input_settings_native_set_tablet_mapping; input_settings_class->set_tablet_keep_aspect = meta_input_settings_native_set_tablet_keep_aspect; input_settings_class->set_tablet_area = meta_input_settings_native_set_tablet_area; input_settings_class->set_mouse_accel_profile = meta_input_settings_native_set_mouse_accel_profile; input_settings_class->set_trackball_accel_profile = meta_input_settings_native_set_trackball_accel_profile; input_settings_class->set_stylus_pressure = meta_input_settings_native_set_stylus_pressure; input_settings_class->set_stylus_button_map = meta_input_settings_native_set_stylus_button_map; input_settings_class->set_mouse_middle_click_emulation = meta_input_settings_native_set_mouse_middle_click_emulation; input_settings_class->set_touchpad_middle_click_emulation = meta_input_settings_native_set_touchpad_middle_click_emulation; input_settings_class->set_trackball_middle_click_emulation = meta_input_settings_native_set_trackball_middle_click_emulation; input_settings_class->has_two_finger_scroll = meta_input_settings_native_has_two_finger_scroll; input_settings_class->is_trackball_device = meta_input_settings_native_is_trackball_device; } static void meta_input_settings_native_init (MetaInputSettingsNative *settings) { } muffin-6.4.1/src/backends/native/meta-kms.h0000664000175000017500000000325314723361714017463 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_H #define META_KMS_H #include #include "backends/meta-backend-private.h" #include "backends/native/meta-kms-types.h" #define META_TYPE_KMS (meta_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaKms, meta_kms, META, KMS, GObject) MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms); MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms); MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms); void meta_kms_discard_pending_page_flips (MetaKms *kms); MetaBackend * meta_kms_get_backend (MetaKms *kms); MetaKmsDevice * meta_kms_create_device (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, GError **error); MetaKms * meta_kms_new (MetaBackend *backend, GError **error); #endif /* META_KMS_H */ muffin-6.4.1/src/backends/native/meta-kms-device.c0000664000175000017500000002377714723361714020730 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" struct _MetaKmsDevice { GObject parent; MetaKms *kms; MetaKmsImplDevice *impl_device; MetaKmsDeviceFlag flags; char *path; GList *crtcs; GList *connectors; GList *planes; MetaKmsDeviceCaps caps; }; G_DEFINE_TYPE (MetaKmsDevice, meta_kms_device, G_TYPE_OBJECT); MetaKmsImplDevice * meta_kms_device_get_impl_device (MetaKmsDevice *device) { return device->impl_device; } int meta_kms_device_leak_fd (MetaKmsDevice *device) { return meta_kms_impl_device_leak_fd (device->impl_device); } const char * meta_kms_device_get_path (MetaKmsDevice *device) { return device->path; } MetaKmsDeviceFlag meta_kms_device_get_flags (MetaKmsDevice *device) { return device->flags; } gboolean meta_kms_device_get_cursor_size (MetaKmsDevice *device, uint64_t *out_cursor_width, uint64_t *out_cursor_height) { if (device->caps.has_cursor_size) { *out_cursor_width = device->caps.cursor_width; *out_cursor_height = device->caps.cursor_height; return TRUE; } else { return FALSE; } } GList * meta_kms_device_get_connectors (MetaKmsDevice *device) { return device->connectors; } GList * meta_kms_device_get_crtcs (MetaKmsDevice *device) { return device->crtcs; } static GList * meta_kms_device_get_planes (MetaKmsDevice *device) { return device->planes; } static MetaKmsPlane * get_plane_with_type_for (MetaKmsDevice *device, MetaKmsCrtc *crtc, MetaKmsPlaneType type) { GList *l; for (l = meta_kms_device_get_planes (device); l; l = l->next) { MetaKmsPlane *plane = l->data; if (meta_kms_plane_get_plane_type (plane) != type) continue; if (meta_kms_plane_is_usable_with (plane, crtc)) return plane; } return NULL; } MetaKmsPlane * meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, MetaKmsCrtc *crtc) { return get_plane_with_type_for (device, crtc, META_KMS_PLANE_TYPE_PRIMARY); } MetaKmsPlane * meta_kms_device_get_cursor_plane_for (MetaKmsDevice *device, MetaKmsCrtc *crtc) { return get_plane_with_type_for (device, crtc, META_KMS_PLANE_TYPE_CURSOR); } void meta_kms_device_update_states_in_impl (MetaKmsDevice *device) { MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); meta_assert_in_kms_impl (device->kms); meta_assert_is_waiting_for_kms_impl_task (device->kms); meta_kms_impl_device_update_states (impl_device); g_list_free (device->crtcs); device->crtcs = meta_kms_impl_device_copy_crtcs (impl_device); g_list_free (device->connectors); device->connectors = meta_kms_impl_device_copy_connectors (impl_device); g_list_free (device->planes); device->planes = meta_kms_impl_device_copy_planes (impl_device); } void meta_kms_device_predict_states_in_impl (MetaKmsDevice *device, MetaKmsUpdate *update) { MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); meta_assert_in_kms_impl (device->kms); meta_kms_impl_device_predict_states (impl_device, update); } static gpointer dispatch_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (user_data); gboolean ret; ret = meta_kms_impl_device_dispatch (impl_device, error); return GINT_TO_POINTER (ret); } static gpointer dispatch_idle_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { meta_kms_impl_dispatch_idle (impl); return GINT_TO_POINTER (TRUE); } int meta_kms_device_dispatch_sync (MetaKmsDevice *device, GError **error) { int callback_count; if (!meta_kms_run_impl_task_sync (device->kms, dispatch_idle_in_impl, device->impl_device, error)) return -1; callback_count = meta_kms_flush_callbacks (device->kms); if (callback_count > 0) return TRUE; if (!meta_kms_run_impl_task_sync (device->kms, dispatch_in_impl, device->impl_device, error)) return -1; return meta_kms_flush_callbacks (device->kms); } void meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device, MetaKmsPlaneType plane_type, MetaKmsCrtc *crtc) { MetaKmsImplDevice *impl_device = device->impl_device; MetaKmsPlane *plane; meta_assert_in_kms_impl (device->kms); plane = meta_kms_impl_device_add_fake_plane (impl_device, plane_type, crtc); device->planes = g_list_append (device->planes, plane); } typedef struct _CreateImplDeviceData { MetaKmsDevice *device; int fd; MetaKmsImplDevice *out_impl_device; GList *out_crtcs; GList *out_connectors; GList *out_planes; MetaKmsDeviceCaps out_caps; } CreateImplDeviceData; static gpointer create_impl_device_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { CreateImplDeviceData *data = user_data; MetaKmsImplDevice *impl_device; impl_device = meta_kms_impl_device_new (data->device, impl, data->fd, error); if (!impl_device) return FALSE; data->out_impl_device = impl_device; data->out_crtcs = meta_kms_impl_device_copy_crtcs (impl_device); data->out_connectors = meta_kms_impl_device_copy_connectors (impl_device); data->out_planes = meta_kms_impl_device_copy_planes (impl_device); data->out_caps = *meta_kms_impl_device_get_caps (impl_device); return GINT_TO_POINTER (TRUE); } MetaKmsDevice * meta_kms_device_new (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, GError **error) { MetaBackend *backend = meta_kms_get_backend (kms); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaLauncher *launcher = meta_backend_native_get_launcher (backend_native); MetaKmsDevice *device; CreateImplDeviceData data; int fd; fd = meta_launcher_open_restricted (launcher, path, error); if (fd == -1) return NULL; device = g_object_new (META_TYPE_KMS_DEVICE, NULL); device->kms = kms; data = (CreateImplDeviceData) { .device = device, .fd = fd, }; if (!meta_kms_run_impl_task_sync (kms, create_impl_device_in_impl, &data, error)) { meta_launcher_close_restricted (launcher, fd); g_object_unref (device); return NULL; } device->impl_device = data.out_impl_device; device->flags = flags; device->path = g_strdup (path); device->crtcs = data.out_crtcs; device->connectors = data.out_connectors; device->planes = data.out_planes; device->caps = data.out_caps; return device; } typedef struct _FreeImplDeviceData { MetaKmsImplDevice *impl_device; int out_fd; } FreeImplDeviceData; static gpointer free_impl_device_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { FreeImplDeviceData *data = user_data; MetaKmsImplDevice *impl_device = data->impl_device; int fd; fd = meta_kms_impl_device_close (impl_device); g_object_unref (impl_device); data->out_fd = fd; return GINT_TO_POINTER (TRUE); } static void meta_kms_device_finalize (GObject *object) { MetaKmsDevice *device = META_KMS_DEVICE (object); MetaBackend *backend = meta_kms_get_backend (device->kms); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaLauncher *launcher = meta_backend_native_get_launcher (backend_native); g_free (device->path); g_list_free (device->crtcs); g_list_free (device->connectors); g_list_free (device->planes); if (device->impl_device) { FreeImplDeviceData data; GError *error = NULL; data = (FreeImplDeviceData) { .impl_device = device->impl_device, }; if (!meta_kms_run_impl_task_sync (device->kms, free_impl_device_in_impl, &data, &error)) { g_warning ("Failed to close KMS impl device: %s", error->message); g_error_free (error); } else { meta_launcher_close_restricted (launcher, data.out_fd); } } G_OBJECT_CLASS (meta_kms_device_parent_class)->finalize (object); } static void meta_kms_device_init (MetaKmsDevice *device) { } static void meta_kms_device_class_init (MetaKmsDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_device_finalize; } muffin-6.4.1/src/backends/native/meta-kms-crtc-private.h0000664000175000017500000000240514723361714022062 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_CRTC_PRIVATE_H #define META_KMS_CRTC_PRIVATE_H #include #include "backends/native/meta-kms-types.h" MetaKmsCrtc * meta_kms_crtc_new (MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc, int idx); void meta_kms_crtc_update_state (MetaKmsCrtc *crtc); void meta_kms_crtc_predict_state (MetaKmsCrtc *crtc, MetaKmsUpdate *update); #endif /* META_KMS_CRTC_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-kms-connector-private.h0000664000175000017500000000304214723361714023117 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_CONNECTOR_PRIVATE_H #define META_KMS_CONNECTOR_PRIVATE_H #include "backends/native/meta-kms-types.h" void meta_kms_connector_update_state (MetaKmsConnector *connector, drmModeRes *drm_resources); void meta_kms_connector_predict_state (MetaKmsConnector *connector, MetaKmsUpdate *update); MetaKmsConnector * meta_kms_connector_new (MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector, drmModeRes *drm_resources); gboolean meta_kms_connector_is_same_as (MetaKmsConnector *connector, drmModeConnector *drm_connector); #endif /* META_KMS_CONNECTOR_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-kms-impl-simple.h0000664000175000017500000000230014723361714021701 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_IMPL_SIMPLE_H #define META_KMS_IMPL_SIMPLE_H #include "backends/native/meta-kms-impl.h" #define META_TYPE_KMS_IMPL_SIMPLE meta_kms_impl_simple_get_type () G_DECLARE_FINAL_TYPE (MetaKmsImplSimple, meta_kms_impl_simple, META, KMS_IMPL_SIMPLE, MetaKmsImpl) MetaKmsImplSimple * meta_kms_impl_simple_new (MetaKms *kms, GError **error); #endif /* META_KMS_IMPL_SIMPLE_H */ muffin-6.4.1/src/backends/native/meta-barrier-native.h0000664000175000017500000000354314723361714021605 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_BARRIER_NATIVE_H #define META_BARRIER_NATIVE_H #include "backends/meta-barrier-private.h" G_BEGIN_DECLS #define META_TYPE_BARRIER_IMPL_NATIVE (meta_barrier_impl_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaBarrierImplNative, meta_barrier_impl_native, META, BARRIER_IMPL_NATIVE, MetaBarrierImpl) typedef struct _MetaBarrierManagerNative MetaBarrierManagerNative; MetaBarrierImpl *meta_barrier_impl_native_new (MetaBarrier *barrier); MetaBarrierManagerNative *meta_barrier_manager_native_new (void); void meta_barrier_manager_native_process (MetaBarrierManagerNative *manager, ClutterInputDevice *device, guint32 time, float *x, float *y); G_END_DECLS #endif /* META_BARRIER_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-udev.h0000664000175000017500000000263214723361714017634 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_UDEV_H #define META_UDEV_H #include #include "backends/native/meta-backend-native-types.h" #define META_TYPE_UDEV (meta_udev_get_type ()) G_DECLARE_FINAL_TYPE (MetaUdev, meta_udev, META, UDEV, GObject) gboolean meta_is_udev_device_platform_device (GUdevDevice *device); gboolean meta_is_udev_device_boot_vga (GUdevDevice *device); gboolean meta_udev_is_drm_device (MetaUdev *udev, GUdevDevice *device); GList * meta_udev_list_drm_devices (MetaUdev *udev, GError **error); MetaUdev * meta_udev_new (MetaBackendNative *backend_native); #endif /* META_UDEV_H */ muffin-6.4.1/src/backends/native/meta-kms-private.h0000664000175000017500000000521714723361714021135 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_PRIVATE_H #define META_KMS_PRIVATE_H #include "backends/native/meta-kms.h" #include "backends/native/meta-kms-types.h" typedef void (* MetaKmsCallback) (MetaKms *kms, gpointer user_data); typedef gpointer (* MetaKmsImplTaskFunc) (MetaKmsImpl *impl, gpointer user_data, GError **error); void meta_kms_queue_callback (MetaKms *kms, MetaKmsCallback callback, gpointer user_data, GDestroyNotify user_data_destroy); int meta_kms_flush_callbacks (MetaKms *kms); gpointer meta_kms_run_impl_task_sync (MetaKms *kms, MetaKmsImplTaskFunc func, gpointer user_data, GError **error); GSource * meta_kms_add_source_in_impl (MetaKms *kms, GSourceFunc func, gpointer user_data, GDestroyNotify user_data_destroy); GSource * meta_kms_register_fd_in_impl (MetaKms *kms, int fd, MetaKmsImplTaskFunc dispatch, gpointer user_data); gboolean meta_kms_in_impl_task (MetaKms *kms); gboolean meta_kms_is_waiting_for_impl_task (MetaKms *kms); #define meta_assert_in_kms_impl(kms) \ g_assert (meta_kms_in_impl_task (kms)) #define meta_assert_not_in_kms_impl(kms) \ g_assert (!meta_kms_in_impl_task (kms)) #define meta_assert_is_waiting_for_kms_impl_task(kms) \ g_assert (meta_kms_is_waiting_for_impl_task (kms)) #endif /* META_KMS_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-drm-buffer-import.h0000664000175000017500000000405514723361714022233 0ustar fabiofabio/* * Copyright (C) 2018 Canonical Ltd. * Copyright (C) 2019 Red Hat Inc. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_DRM_BUFFER_IMPORT_H #define META_DRM_BUFFER_IMPORT_H #include #include "backends/native/meta-drm-buffer.h" #include "backends/native/meta-drm-buffer-gbm.h" #include "backends/native/meta-gpu-kms.h" #define META_TYPE_DRM_BUFFER_IMPORT (meta_drm_buffer_import_get_type ()) G_DECLARE_FINAL_TYPE (MetaDrmBufferImport, meta_drm_buffer_import, META, DRM_BUFFER_IMPORT, MetaDrmBuffer) /* * MetaDrmBufferImport is a buffer that refers to the storage of a * MetaDrmBufferGbm buffer on another MetaGpuKms. * * When creating an imported buffer, the given GBM buffer is exported * as a dma_buf and then imported to the given MetaGpuKms. A reference * is kept to the GBM buffer so that it won't disappear while the * imported buffer exists. * * The import has a high chance of failing under normal operating * conditions and needs to be handled with fallbacks to something else. */ MetaDrmBufferImport * meta_drm_buffer_import_new (MetaGpuKms *gpu_kms, MetaDrmBufferGbm *buffer_gbm, GError **error); #endif /* META_DRM_BUFFER_IMPORT_H */ muffin-6.4.1/src/backends/native/meta-kms-crtc.h0000664000175000017500000000362714723361714020421 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_CRTC_H #define META_KMS_CRTC_H #include #include #include #include "backends/native/meta-kms-types.h" #include "meta/boxes.h" typedef struct _MetaKmsCrtcState { MetaRectangle rect; gboolean is_drm_mode_valid; drmModeModeInfo drm_mode; struct { uint16_t *red; uint16_t *green; uint16_t *blue; int size; } gamma; } MetaKmsCrtcState; #define META_TYPE_KMS_CRTC (meta_kms_crtc_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsCrtc, meta_kms_crtc, META, KMS_CRTC, GObject) void meta_kms_crtc_set_gamma (MetaKmsCrtc *crtc, MetaKmsUpdate *update, int size, const uint16_t *red, const uint16_t *green, const uint16_t *blue); MetaKmsDevice * meta_kms_crtc_get_device (MetaKmsCrtc *crtc); const MetaKmsCrtcState * meta_kms_crtc_get_current_state (MetaKmsCrtc *crtc); uint32_t meta_kms_crtc_get_id (MetaKmsCrtc *crtc); int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc); #endif /* META_KMS_CRTC_H */ muffin-6.4.1/src/backends/native/meta-renderer-native-gles3.c0000664000175000017500000001301514723361714022766 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (c) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #define GL_GLEXT_PROTOTYPES #include "backends/native/meta-renderer-native-gles3.h" #include #include #include #include #include #include "backends/meta-egl-ext.h" #include "backends/meta-gles3.h" #include "backends/meta-gles3-table.h" /* * GL/gl.h being included may conflit with gl3.h on some architectures. * Make sure that hasn't happened on any architecture. */ #ifdef GL_VERSION_1_1 #error "Somehow included OpenGL headers when we shouldn't have" #endif static void paint_egl_image (MetaGles3 *gles3, EGLImageKHR egl_image, int width, int height) { GLuint texture; GLuint framebuffer; meta_gles3_clear_error (gles3); GLBAS (gles3, glGenFramebuffers, (1, &framebuffer)); GLBAS (gles3, glBindFramebuffer, (GL_READ_FRAMEBUFFER, framebuffer)); GLBAS (gles3, glActiveTexture, (GL_TEXTURE0)); GLBAS (gles3, glGenTextures, (1, &texture)); GLBAS (gles3, glBindTexture, (GL_TEXTURE_2D, texture)); GLEXT (gles3, glEGLImageTargetTexture2DOES, (GL_TEXTURE_2D, egl_image)); GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST)); GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST)); GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE)); GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE)); GLBAS (gles3, glTexParameteri, (GL_TEXTURE_2D, GL_TEXTURE_WRAP_R_OES, GL_CLAMP_TO_EDGE)); GLBAS (gles3, glFramebufferTexture2D, (GL_READ_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0)); GLBAS (gles3, glBindFramebuffer, (GL_READ_FRAMEBUFFER, framebuffer)); GLBAS (gles3, glBlitFramebuffer, (0, height, width, 0, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST)); GLBAS (gles3, glDeleteTextures, (1, &texture)); GLBAS (gles3, glDeleteFramebuffers, (1, &framebuffer)); } gboolean meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, MetaGles3 *gles3, EGLDisplay egl_display, EGLContext egl_context, EGLSurface egl_surface, struct gbm_bo *shared_bo, GError **error) { int shared_bo_fd; unsigned int width; unsigned int height; uint32_t i, n_planes; uint32_t strides[4] = { 0 }; uint32_t offsets[4] = { 0 }; uint64_t modifiers[4] = { 0 }; int fds[4] = { -1, -1, -1, -1 }; uint32_t format; EGLImageKHR egl_image; gboolean use_modifiers; shared_bo_fd = gbm_bo_get_fd (shared_bo); if (shared_bo_fd < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to export gbm_bo: %s", strerror (errno)); return FALSE; } width = gbm_bo_get_width (shared_bo); height = gbm_bo_get_height (shared_bo); format = gbm_bo_get_format (shared_bo); n_planes = gbm_bo_get_plane_count (shared_bo); for (i = 0; i < n_planes; i++) { strides[i] = gbm_bo_get_stride_for_plane (shared_bo, i); offsets[i] = gbm_bo_get_offset (shared_bo, i); modifiers[i] = gbm_bo_get_modifier (shared_bo); fds[i] = shared_bo_fd; } /* Workaround for https://gitlab.gnome.org/GNOME/mutter/issues/18 */ if (modifiers[0] == DRM_FORMAT_MOD_LINEAR || modifiers[0] == DRM_FORMAT_MOD_INVALID) use_modifiers = FALSE; else use_modifiers = TRUE; egl_image = meta_egl_create_dmabuf_image (egl, egl_display, width, height, format, n_planes, fds, strides, offsets, use_modifiers ? modifiers : NULL, error); close (shared_bo_fd); if (!egl_image) return FALSE; paint_egl_image (gles3, egl_image, width, height); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); return TRUE; } muffin-6.4.1/src/backends/native/meta-input-device-native.c0000664000175000017500000013563714723361714022560 0ustar fabiofabio/* * Copyright (C) 2010 Intel Corp. * Copyright (C) 2014 Jonas Ådahl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Damien Lespiau * Author: Jonas Ådahl */ #include "config.h" #include #include #include "backends/native/meta-input-device-tool-native.h" #include "backends/native/meta-input-device-native.h" #include "backends/native/meta-seat-native.h" #include "clutter/clutter-mutter.h" G_DEFINE_TYPE (MetaInputDeviceNative, meta_input_device_native, META_TYPE_INPUT_DEVICE) enum { PROP_0, PROP_DEVICE_MATRIX, PROP_OUTPUT_ASPECT_RATIO, N_PROPS }; static GParamSpec *obj_props[N_PROPS] = { 0 }; typedef struct _SlowKeysEventPending { MetaInputDeviceNative *device; ClutterEvent *event; ClutterEmitInputDeviceEvent emit_event_func; guint timer; } SlowKeysEventPending; static void clear_slow_keys (MetaInputDeviceNative *device); static void stop_bounce_keys (MetaInputDeviceNative *device); static void stop_toggle_slowkeys (MetaInputDeviceNative *device); static void stop_mousekeys_move (MetaInputDeviceNative *device); static void meta_input_device_native_finalize (GObject *object) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (object); MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (object); ClutterBackend *backend; ClutterSeat *seat; if (device_evdev->libinput_device) libinput_device_unref (device_evdev->libinput_device); meta_input_device_native_release_touch_slots (device_evdev, g_get_monotonic_time ()); backend = clutter_get_default_backend (); seat = clutter_backend_get_default_seat (backend); meta_seat_native_release_device_id (META_SEAT_NATIVE (seat), device); clear_slow_keys (device_evdev); stop_bounce_keys (device_evdev); stop_toggle_slowkeys (device_evdev); stop_mousekeys_move (device_evdev); G_OBJECT_CLASS (meta_input_device_native_parent_class)->finalize (object); } static void meta_input_device_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaInputDeviceNative *device = META_INPUT_DEVICE_NATIVE (object); switch (prop_id) { case PROP_DEVICE_MATRIX: { const cairo_matrix_t *matrix = g_value_get_boxed (value); cairo_matrix_init_identity (&device->device_matrix); cairo_matrix_multiply (&device->device_matrix, &device->device_matrix, matrix); break; } case PROP_OUTPUT_ASPECT_RATIO: device->output_ratio = g_value_get_double (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_input_device_native_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaInputDeviceNative *device = META_INPUT_DEVICE_NATIVE (object); switch (prop_id) { case PROP_DEVICE_MATRIX: g_value_set_boxed (value, &device->device_matrix); break; case PROP_OUTPUT_ASPECT_RATIO: g_value_set_double (value, device->output_ratio); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static gboolean meta_input_device_native_keycode_to_evdev (ClutterInputDevice *device, uint32_t hardware_keycode, uint32_t *evdev_keycode) { /* The hardware keycodes from the evdev backend are almost evdev keycodes: we use the evdev keycode file, but xkb rules have an offset by 8. See the comment in _clutter_key_event_new_from_evdev() */ *evdev_keycode = hardware_keycode - 8; return TRUE; } static void meta_input_device_native_update_from_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool) { MetaInputDeviceToolNative *evdev_tool; evdev_tool = META_INPUT_DEVICE_TOOL_NATIVE (tool); g_object_freeze_notify (G_OBJECT (device)); _clutter_input_device_reset_axes (device); _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_X, 0, 0, 0); _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_Y, 0, 0, 0); if (libinput_tablet_tool_has_distance (evdev_tool->tool)) _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_DISTANCE, 0, 1, 0); if (libinput_tablet_tool_has_pressure (evdev_tool->tool)) _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_PRESSURE, 0, 1, 0); if (libinput_tablet_tool_has_tilt (evdev_tool->tool)) { _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_XTILT, -90, 90, 0); _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_YTILT, -90, 90, 0); } if (libinput_tablet_tool_has_rotation (evdev_tool->tool)) _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_ROTATION, 0, 360, 0); if (libinput_tablet_tool_has_slider (evdev_tool->tool)) _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_SLIDER, -1, 1, 0); if (libinput_tablet_tool_has_wheel (evdev_tool->tool)) _clutter_input_device_add_axis (device, CLUTTER_INPUT_AXIS_WHEEL, -180, 180, 0); g_object_thaw_notify (G_OBJECT (device)); } static gboolean meta_input_device_native_is_mode_switch_button (ClutterInputDevice *device, uint32_t group, uint32_t button) { struct libinput_device *libinput_device; struct libinput_tablet_pad_mode_group *mode_group; libinput_device = meta_input_device_native_get_libinput_device (device); mode_group = libinput_device_tablet_pad_get_mode_group (libinput_device, group); return libinput_tablet_pad_mode_group_button_is_toggle (mode_group, button) != 0; } static int meta_input_device_native_get_group_n_modes (ClutterInputDevice *device, int group) { struct libinput_device *libinput_device; struct libinput_tablet_pad_mode_group *mode_group; libinput_device = meta_input_device_native_get_libinput_device (device); mode_group = libinput_device_tablet_pad_get_mode_group (libinput_device, group); return libinput_tablet_pad_mode_group_get_num_modes (mode_group); } static gboolean meta_input_device_native_is_grouped (ClutterInputDevice *device, ClutterInputDevice *other_device) { struct libinput_device *libinput_device, *other_libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); other_libinput_device = meta_input_device_native_get_libinput_device (other_device); return libinput_device_get_device_group (libinput_device) == libinput_device_get_device_group (other_libinput_device); } static void meta_input_device_native_bell_notify (MetaInputDeviceNative *device) { clutter_seat_bell_notify (CLUTTER_SEAT (device->seat)); } static void meta_input_device_native_free_pending_slow_key (gpointer data) { SlowKeysEventPending *slow_keys_event = data; clutter_event_free (slow_keys_event->event); g_clear_handle_id (&slow_keys_event->timer, g_source_remove); g_free (slow_keys_event); } static void clear_slow_keys (MetaInputDeviceNative *device) { g_list_free_full (device->slow_keys_list, meta_input_device_native_free_pending_slow_key); g_list_free (device->slow_keys_list); device->slow_keys_list = NULL; } static guint get_slow_keys_delay (ClutterInputDevice *device) { MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); ClutterKbdA11ySettings a11y_settings; clutter_seat_get_kbd_a11y_settings (CLUTTER_SEAT (device_native->seat), &a11y_settings); /* Settings use int, we use uint, make sure we dont go negative */ return MAX (0, a11y_settings.slowkeys_delay); } static gboolean trigger_slow_keys (gpointer data) { SlowKeysEventPending *slow_keys_event = data; MetaInputDeviceNative *device = slow_keys_event->device; ClutterKeyEvent *key_event = (ClutterKeyEvent *) slow_keys_event->event; /* Alter timestamp and emit the event */ key_event->time = us2ms (g_get_monotonic_time ()); slow_keys_event->emit_event_func (slow_keys_event->event, CLUTTER_INPUT_DEVICE (device)); /* Then remote the pending event */ device->slow_keys_list = g_list_remove (device->slow_keys_list, slow_keys_event); meta_input_device_native_free_pending_slow_key (slow_keys_event); if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT) meta_input_device_native_bell_notify (device); return G_SOURCE_REMOVE; } static int find_pending_event_by_keycode (gconstpointer a, gconstpointer b) { const SlowKeysEventPending *pa = a; const ClutterKeyEvent *ka = (ClutterKeyEvent *) pa->event; const ClutterKeyEvent *kb = b; return kb->hardware_keycode - ka->hardware_keycode; } static void start_slow_keys (ClutterEvent *event, MetaInputDeviceNative *device, ClutterEmitInputDeviceEvent emit_event_func) { SlowKeysEventPending *slow_keys_event; ClutterKeyEvent *key_event = (ClutterKeyEvent *) event; if (key_event->flags & CLUTTER_EVENT_FLAG_REPEATED) return; slow_keys_event = g_new0 (SlowKeysEventPending, 1); slow_keys_event->device = device; slow_keys_event->event = clutter_event_copy (event); slow_keys_event->emit_event_func = emit_event_func; slow_keys_event->timer = clutter_threads_add_timeout (get_slow_keys_delay (CLUTTER_INPUT_DEVICE (device)), trigger_slow_keys, slow_keys_event); device->slow_keys_list = g_list_append (device->slow_keys_list, slow_keys_event); if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS) meta_input_device_native_bell_notify (device); } static void stop_slow_keys (ClutterEvent *event, MetaInputDeviceNative *device, ClutterEmitInputDeviceEvent emit_event_func) { GList *item; /* Check if we have a slow key event queued for this key event */ item = g_list_find_custom (device->slow_keys_list, event, find_pending_event_by_keycode); if (item) { SlowKeysEventPending *slow_keys_event = item->data; device->slow_keys_list = g_list_delete_link (device->slow_keys_list, item); meta_input_device_native_free_pending_slow_key (slow_keys_event); if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT) meta_input_device_native_bell_notify (device); return; } /* If no key press event was pending, just emit the key release as-is */ emit_event_func (event, CLUTTER_INPUT_DEVICE (device)); } static guint get_debounce_delay (ClutterInputDevice *device) { MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); ClutterKbdA11ySettings a11y_settings; clutter_seat_get_kbd_a11y_settings (CLUTTER_SEAT (device_native->seat), &a11y_settings); /* Settings use int, we use uint, make sure we dont go negative */ return MAX (0, a11y_settings.debounce_delay); } static gboolean clear_bounce_keys (gpointer data) { MetaInputDeviceNative *device = data; device->debounce_key = 0; device->debounce_timer = 0; return G_SOURCE_REMOVE; } static void start_bounce_keys (ClutterEvent *event, MetaInputDeviceNative *device) { stop_bounce_keys (device); device->debounce_key = ((ClutterKeyEvent *) event)->hardware_keycode; device->debounce_timer = clutter_threads_add_timeout (get_debounce_delay (CLUTTER_INPUT_DEVICE (device)), clear_bounce_keys, device); } static void stop_bounce_keys (MetaInputDeviceNative *device) { g_clear_handle_id (&device->debounce_timer, g_source_remove); } static void notify_bounce_keys_reject (MetaInputDeviceNative *device) { if (device->a11y_flags & CLUTTER_A11Y_BOUNCE_KEYS_BEEP_REJECT) meta_input_device_native_bell_notify (device); } static gboolean debounce_key (ClutterEvent *event, MetaInputDeviceNative *device) { return (device->debounce_key == ((ClutterKeyEvent *) event)->hardware_keycode); } static gboolean key_event_is_modifier (ClutterEvent *event) { switch (event->key.keyval) { case XKB_KEY_Shift_L: case XKB_KEY_Shift_R: case XKB_KEY_Control_L: case XKB_KEY_Control_R: case XKB_KEY_Alt_L: case XKB_KEY_Alt_R: case XKB_KEY_Meta_L: case XKB_KEY_Meta_R: case XKB_KEY_Super_L: case XKB_KEY_Super_R: case XKB_KEY_Hyper_L: case XKB_KEY_Hyper_R: case XKB_KEY_Caps_Lock: case XKB_KEY_Shift_Lock: return TRUE; default: return FALSE; } } static void notify_stickykeys_mask (MetaInputDeviceNative *device) { g_signal_emit_by_name (device->seat, "kbd-a11y-mods-state-changed", device->stickykeys_latched_mask, device->stickykeys_locked_mask); } static void update_internal_xkb_state (MetaInputDeviceNative *device, xkb_mod_mask_t new_latched_mask, xkb_mod_mask_t new_locked_mask) { MetaSeatNative *seat = device->seat; xkb_mod_mask_t depressed_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; xkb_mod_mask_t group_mods; depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); latched_mods &= ~device->stickykeys_latched_mask; locked_mods &= ~device->stickykeys_locked_mask; device->stickykeys_latched_mask = new_latched_mask; device->stickykeys_locked_mask = new_locked_mask; latched_mods |= device->stickykeys_latched_mask; locked_mods |= device->stickykeys_locked_mask; group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE); xkb_state_update_mask (seat->xkb, depressed_mods, latched_mods, locked_mods, 0, 0, group_mods); notify_stickykeys_mask (device); } static void update_stickykeys_event (ClutterEvent *event, MetaInputDeviceNative *device, xkb_mod_mask_t new_latched_mask, xkb_mod_mask_t new_locked_mask) { MetaSeatNative *seat = device->seat; xkb_mod_mask_t effective_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; update_internal_xkb_state (device, new_latched_mask, new_locked_mask); effective_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_EFFECTIVE); latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); _clutter_event_set_state_full (event, seat->button_state, device->stickykeys_depressed_mask, latched_mods, locked_mods, effective_mods | seat->button_state); } static void notify_stickykeys_change (MetaInputDeviceNative *device) { /* Everytime sticky keys setting is changed, clear the masks */ device->stickykeys_depressed_mask = 0; update_internal_xkb_state (device, 0, 0); g_signal_emit_by_name (CLUTTER_INPUT_DEVICE (device)->seat, "kbd-a11y-flags-changed", device->a11y_flags, CLUTTER_A11Y_STICKY_KEYS_ENABLED); } static void set_stickykeys_off (MetaInputDeviceNative *device) { device->a11y_flags &= ~CLUTTER_A11Y_STICKY_KEYS_ENABLED; notify_stickykeys_change (device); } static void set_stickykeys_on (MetaInputDeviceNative *device) { device->a11y_flags |= CLUTTER_A11Y_STICKY_KEYS_ENABLED; notify_stickykeys_change (device); } static void clear_stickykeys_event (ClutterEvent *event, MetaInputDeviceNative *device) { set_stickykeys_off (device); update_stickykeys_event (event, device, 0, 0); } static void set_slowkeys_off (MetaInputDeviceNative *device) { device->a11y_flags &= ~CLUTTER_A11Y_SLOW_KEYS_ENABLED; g_signal_emit_by_name (CLUTTER_INPUT_DEVICE (device)->seat, "kbd-a11y-flags-changed", device->a11y_flags, CLUTTER_A11Y_SLOW_KEYS_ENABLED); } static void set_slowkeys_on (MetaInputDeviceNative *device) { device->a11y_flags |= CLUTTER_A11Y_SLOW_KEYS_ENABLED; g_signal_emit_by_name (CLUTTER_INPUT_DEVICE (device)->seat, "kbd-a11y-flags-changed", device->a11y_flags, CLUTTER_A11Y_SLOW_KEYS_ENABLED); } static void handle_stickykeys_press (ClutterEvent *event, MetaInputDeviceNative *device) { MetaSeatNative *seat = device->seat; xkb_mod_mask_t depressed_mods; xkb_mod_mask_t new_latched_mask; xkb_mod_mask_t new_locked_mask; if (!key_event_is_modifier (event)) return; if (device->stickykeys_depressed_mask && (device->a11y_flags & CLUTTER_A11Y_STICKY_KEYS_TWO_KEY_OFF)) { clear_stickykeys_event (event, device); return; } depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); /* Ignore the lock modifier mask, that one cannot be sticky, yet the * CAPS_LOCK key itself counts as a modifier as it might be remapped * to some other modifier which can be sticky. */ depressed_mods &= ~CLUTTER_LOCK_MASK; new_latched_mask = device->stickykeys_latched_mask; new_locked_mask = device->stickykeys_locked_mask; device->stickykeys_depressed_mask = depressed_mods; if (new_locked_mask & depressed_mods) { new_locked_mask &= ~depressed_mods; } else if (new_latched_mask & depressed_mods) { new_locked_mask |= depressed_mods; new_latched_mask &= ~depressed_mods; } else { new_latched_mask |= depressed_mods; } update_stickykeys_event (event, device, new_latched_mask, new_locked_mask); } static void handle_stickykeys_release (ClutterEvent *event, MetaInputDeviceNative *device) { MetaSeatNative *seat = device->seat; device->stickykeys_depressed_mask = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); if (key_event_is_modifier (event)) { if (device->a11y_flags & CLUTTER_A11Y_STICKY_KEYS_BEEP) meta_input_device_native_bell_notify (device); return; } if (device->stickykeys_latched_mask == 0) return; update_stickykeys_event (event, device, 0, device->stickykeys_locked_mask); } static gboolean trigger_toggle_slowkeys (gpointer data) { MetaInputDeviceNative *device = data; device->toggle_slowkeys_timer = 0; if (device->a11y_flags & CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP) meta_input_device_native_bell_notify (device); if (device->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_ENABLED) set_slowkeys_off (device); else set_slowkeys_on (device); return G_SOURCE_REMOVE; } static void start_toggle_slowkeys (MetaInputDeviceNative *device) { if (device->toggle_slowkeys_timer != 0) return; device->toggle_slowkeys_timer = clutter_threads_add_timeout (8 * 1000 /* 8 secs */, trigger_toggle_slowkeys, device); } static void stop_toggle_slowkeys (MetaInputDeviceNative *device) { g_clear_handle_id (&device->toggle_slowkeys_timer, g_source_remove); } static void handle_enablekeys_press (ClutterEvent *event, MetaInputDeviceNative *device) { if (event->key.keyval == XKB_KEY_Shift_L || event->key.keyval == XKB_KEY_Shift_R) { start_toggle_slowkeys (device); if (event->key.time > device->last_shift_time + 15 * 1000 /* 15 secs */) device->shift_count = 1; else device->shift_count++; device->last_shift_time = event->key.time; } else { device->shift_count = 0; stop_toggle_slowkeys (device); } } static void handle_enablekeys_release (ClutterEvent *event, MetaInputDeviceNative *device) { if (event->key.keyval == XKB_KEY_Shift_L || event->key.keyval == XKB_KEY_Shift_R) { stop_toggle_slowkeys (device); if (device->shift_count >= 5) { device->shift_count = 0; if (device->a11y_flags & CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP) meta_input_device_native_bell_notify (device); if (device->a11y_flags & CLUTTER_A11Y_STICKY_KEYS_ENABLED) set_stickykeys_off (device); else set_stickykeys_on (device); } } } static int get_button_index (int button) { switch (button) { case CLUTTER_BUTTON_PRIMARY: return 0; case CLUTTER_BUTTON_MIDDLE: return 1; case CLUTTER_BUTTON_SECONDARY: return 2; default: break; } g_warn_if_reached (); return 0; } static void emulate_button_press (MetaInputDeviceNative *device_evdev) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_evdev); int btn = device_evdev->mousekeys_btn; if (device_evdev->mousekeys_btn_states[get_button_index (btn)]) return; clutter_virtual_input_device_notify_button (device->accessibility_virtual_device, g_get_monotonic_time (), btn, CLUTTER_BUTTON_STATE_PRESSED); device_evdev->mousekeys_btn_states[get_button_index (btn)] = CLUTTER_BUTTON_STATE_PRESSED; } static void emulate_button_release (MetaInputDeviceNative *device_evdev) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_evdev); int btn = device_evdev->mousekeys_btn; if (device_evdev->mousekeys_btn_states[get_button_index (btn)] == CLUTTER_BUTTON_STATE_RELEASED) return; clutter_virtual_input_device_notify_button (device->accessibility_virtual_device, g_get_monotonic_time (), btn, CLUTTER_BUTTON_STATE_RELEASED); device_evdev->mousekeys_btn_states[get_button_index (btn)] = CLUTTER_BUTTON_STATE_RELEASED; } static void emulate_button_click (MetaInputDeviceNative *device) { emulate_button_press (device); emulate_button_release (device); } #define MOUSEKEYS_CURVE (1.0 + (((double) 50.0) * 0.001)) static void update_mousekeys_params (MetaInputDeviceNative *device, ClutterKbdA11ySettings *settings) { /* Prevent us from broken settings values */ device->mousekeys_max_speed = MAX (1, settings->mousekeys_max_speed); device->mousekeys_accel_time = MAX (1, settings->mousekeys_accel_time); device->mousekeys_init_delay = MAX (0, settings->mousekeys_init_delay); device->mousekeys_curve_factor = (((double) device->mousekeys_max_speed) / pow ((double) device->mousekeys_accel_time, MOUSEKEYS_CURVE)); } static double mousekeys_get_speed_factor (MetaInputDeviceNative *device, uint64_t time_us) { uint32_t time; int64_t delta_t; int64_t init_time; double speed; time = us2ms (time_us); if (device->mousekeys_first_motion_time == 0) { /* Start acceleration _after_ the first move, so take * mousekeys_init_delay into account for t0 */ device->mousekeys_first_motion_time = time + device->mousekeys_init_delay; device->mousekeys_last_motion_time = device->mousekeys_first_motion_time; return 1.0; } init_time = time - device->mousekeys_first_motion_time; delta_t = time - device->mousekeys_last_motion_time; if (delta_t < 0) return 0.0; if (init_time < device->mousekeys_accel_time) speed = (double) (device->mousekeys_curve_factor * pow ((double) init_time, MOUSEKEYS_CURVE) * delta_t / 1000.0); else speed = (double) (device->mousekeys_max_speed * delta_t / 1000.0); device->mousekeys_last_motion_time = time; return speed; } #undef MOUSEKEYS_CURVE static void emulate_pointer_motion (MetaInputDeviceNative *device_evdev, int dx, int dy) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_evdev); double dx_motion; double dy_motion; double speed; int64_t time_us; time_us = g_get_monotonic_time (); speed = mousekeys_get_speed_factor (device_evdev, time_us); if (dx < 0) dx_motion = floor (((double) dx) * speed); else dx_motion = ceil (((double) dx) * speed); if (dy < 0) dy_motion = floor (((double) dy) * speed); else dy_motion = ceil (((double) dy) * speed); clutter_virtual_input_device_notify_relative_motion (device->accessibility_virtual_device, time_us, dx_motion, dy_motion); } static gboolean is_numlock_active (MetaInputDeviceNative *device) { MetaSeatNative *seat = device->seat; return xkb_state_mod_name_is_active (seat->xkb, "Mod2", XKB_STATE_MODS_LOCKED); } static void enable_mousekeys (MetaInputDeviceNative *device_evdev) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_evdev); device_evdev->mousekeys_btn = CLUTTER_BUTTON_PRIMARY; device_evdev->move_mousekeys_timer = 0; device_evdev->mousekeys_first_motion_time = 0; device_evdev->mousekeys_last_motion_time = 0; device_evdev->last_mousekeys_key = 0; if (device->accessibility_virtual_device) return; device->accessibility_virtual_device = clutter_seat_create_virtual_device (CLUTTER_SEAT (device_evdev->seat), CLUTTER_POINTER_DEVICE); } static void disable_mousekeys (MetaInputDeviceNative *device_evdev) { ClutterInputDevice *device = CLUTTER_INPUT_DEVICE (device_evdev); stop_mousekeys_move (device_evdev); /* Make sure we don't leave button pressed behind... */ if (device_evdev->mousekeys_btn_states[get_button_index (CLUTTER_BUTTON_PRIMARY)]) { device_evdev->mousekeys_btn = CLUTTER_BUTTON_PRIMARY; emulate_button_release (device_evdev); } if (device_evdev->mousekeys_btn_states[get_button_index (CLUTTER_BUTTON_MIDDLE)]) { device_evdev->mousekeys_btn = CLUTTER_BUTTON_MIDDLE; emulate_button_release (device_evdev); } if (device_evdev->mousekeys_btn_states[get_button_index (CLUTTER_BUTTON_SECONDARY)]) { device_evdev->mousekeys_btn = CLUTTER_BUTTON_SECONDARY; emulate_button_release (device_evdev); } if (device->accessibility_virtual_device) g_clear_object (&device->accessibility_virtual_device); } static gboolean trigger_mousekeys_move (gpointer data) { MetaInputDeviceNative *device = data; int dx = 0; int dy = 0; if (device->mousekeys_first_motion_time == 0) { /* This is the first move, Secdule at mk_init_delay */ device->move_mousekeys_timer = clutter_threads_add_timeout (device->mousekeys_init_delay, trigger_mousekeys_move, device); } else { /* More moves, reschedule at mk_interval */ device->move_mousekeys_timer = clutter_threads_add_timeout (100, /* msec between mousekey events */ trigger_mousekeys_move, device); } /* Pointer motion */ switch (device->last_mousekeys_key) { case XKB_KEY_KP_Home: case XKB_KEY_KP_7: case XKB_KEY_KP_Up: case XKB_KEY_KP_8: case XKB_KEY_KP_Page_Up: case XKB_KEY_KP_9: dy = -1; break; case XKB_KEY_KP_End: case XKB_KEY_KP_1: case XKB_KEY_KP_Down: case XKB_KEY_KP_2: case XKB_KEY_KP_Page_Down: case XKB_KEY_KP_3: dy = 1; break; default: break; } switch (device->last_mousekeys_key) { case XKB_KEY_KP_Home: case XKB_KEY_KP_7: case XKB_KEY_KP_Left: case XKB_KEY_KP_4: case XKB_KEY_KP_End: case XKB_KEY_KP_1: dx = -1; break; case XKB_KEY_KP_Page_Up: case XKB_KEY_KP_9: case XKB_KEY_KP_Right: case XKB_KEY_KP_6: case XKB_KEY_KP_Page_Down: case XKB_KEY_KP_3: dx = 1; break; default: break; } if (dx != 0 || dy != 0) emulate_pointer_motion (device, dx, dy); /* We reschedule each time */ return G_SOURCE_REMOVE; } static void stop_mousekeys_move (MetaInputDeviceNative *device) { device->mousekeys_first_motion_time = 0; device->mousekeys_last_motion_time = 0; g_clear_handle_id (&device->move_mousekeys_timer, g_source_remove); } static void start_mousekeys_move (ClutterEvent *event, MetaInputDeviceNative *device) { device->last_mousekeys_key = event->key.keyval; if (device->move_mousekeys_timer != 0) return; trigger_mousekeys_move (device); } static gboolean handle_mousekeys_press (ClutterEvent *event, MetaInputDeviceNative *device) { if (!(event->key.flags & CLUTTER_EVENT_FLAG_SYNTHETIC)) stop_mousekeys_move (device); /* Do not handle mousekeys if NumLock is ON */ if (is_numlock_active (device)) return FALSE; /* Button selection */ switch (event->key.keyval) { case XKB_KEY_KP_Divide: device->mousekeys_btn = CLUTTER_BUTTON_PRIMARY; return TRUE; case XKB_KEY_KP_Multiply: device->mousekeys_btn = CLUTTER_BUTTON_MIDDLE; return TRUE; case XKB_KEY_KP_Subtract: device->mousekeys_btn = CLUTTER_BUTTON_SECONDARY; return TRUE; default: break; } /* Button events */ switch (event->key.keyval) { case XKB_KEY_KP_Begin: case XKB_KEY_KP_5: emulate_button_click (device); return TRUE; case XKB_KEY_KP_Insert: case XKB_KEY_KP_0: emulate_button_press (device); return TRUE; case XKB_KEY_KP_Decimal: case XKB_KEY_KP_Delete: emulate_button_release (device); return TRUE; case XKB_KEY_KP_Add: emulate_button_click (device); emulate_button_click (device); return TRUE; default: break; } /* Pointer motion */ switch (event->key.keyval) { case XKB_KEY_KP_1: case XKB_KEY_KP_2: case XKB_KEY_KP_3: case XKB_KEY_KP_4: case XKB_KEY_KP_6: case XKB_KEY_KP_7: case XKB_KEY_KP_8: case XKB_KEY_KP_9: case XKB_KEY_KP_Down: case XKB_KEY_KP_End: case XKB_KEY_KP_Home: case XKB_KEY_KP_Left: case XKB_KEY_KP_Page_Down: case XKB_KEY_KP_Page_Up: case XKB_KEY_KP_Right: case XKB_KEY_KP_Up: start_mousekeys_move (event, device); return TRUE; default: break; } return FALSE; } static gboolean handle_mousekeys_release (ClutterEvent *event, MetaInputDeviceNative *device) { /* Do not handle mousekeys if NumLock is ON */ if (is_numlock_active (device)) return FALSE; switch (event->key.keyval) { case XKB_KEY_KP_0: case XKB_KEY_KP_1: case XKB_KEY_KP_2: case XKB_KEY_KP_3: case XKB_KEY_KP_4: case XKB_KEY_KP_5: case XKB_KEY_KP_6: case XKB_KEY_KP_7: case XKB_KEY_KP_8: case XKB_KEY_KP_9: case XKB_KEY_KP_Add: case XKB_KEY_KP_Begin: case XKB_KEY_KP_Decimal: case XKB_KEY_KP_Delete: case XKB_KEY_KP_Divide: case XKB_KEY_KP_Down: case XKB_KEY_KP_End: case XKB_KEY_KP_Home: case XKB_KEY_KP_Insert: case XKB_KEY_KP_Left: case XKB_KEY_KP_Multiply: case XKB_KEY_KP_Page_Down: case XKB_KEY_KP_Page_Up: case XKB_KEY_KP_Right: case XKB_KEY_KP_Subtract: case XKB_KEY_KP_Up: stop_mousekeys_move (device); return TRUE; default: break; } return FALSE; } static void meta_input_device_native_process_kbd_a11y_event (ClutterEvent *event, ClutterInputDevice *device, ClutterEmitInputDeviceEvent emit_event_func) { MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device); /* Ignore key events injected from IM */ if (event->key.flags & CLUTTER_EVENT_FLAG_INPUT_METHOD) goto emit_event; if (device_evdev->a11y_flags & CLUTTER_A11Y_KEYBOARD_ENABLED) { if (event->type == CLUTTER_KEY_PRESS) handle_enablekeys_press (event, device_evdev); else handle_enablekeys_release (event, device_evdev); } if (device_evdev->a11y_flags & CLUTTER_A11Y_MOUSE_KEYS_ENABLED) { if (event->type == CLUTTER_KEY_PRESS && handle_mousekeys_press (event, device_evdev)) return; /* swallow event */ if (event->type == CLUTTER_KEY_RELEASE && handle_mousekeys_release (event, device_evdev)) return; /* swallow event */ } if ((device_evdev->a11y_flags & CLUTTER_A11Y_BOUNCE_KEYS_ENABLED) && (get_debounce_delay (device) != 0)) { if ((event->type == CLUTTER_KEY_PRESS) && debounce_key (event, device_evdev)) { notify_bounce_keys_reject (device_evdev); return; } else if (event->type == CLUTTER_KEY_RELEASE) start_bounce_keys (event, device_evdev); } if ((device_evdev->a11y_flags & CLUTTER_A11Y_SLOW_KEYS_ENABLED) && (get_slow_keys_delay (device) != 0)) { if (event->type == CLUTTER_KEY_PRESS) start_slow_keys (event, device_evdev, emit_event_func); else if (event->type == CLUTTER_KEY_RELEASE) stop_slow_keys (event, device_evdev, emit_event_func); return; } if (device_evdev->a11y_flags & CLUTTER_A11Y_STICKY_KEYS_ENABLED) { if (event->type == CLUTTER_KEY_PRESS) handle_stickykeys_press (event, device_evdev); else if (event->type == CLUTTER_KEY_RELEASE) handle_stickykeys_release (event, device_evdev); } emit_event: emit_event_func (event, device); } void meta_input_device_native_apply_kbd_a11y_settings (MetaInputDeviceNative *device, ClutterKbdA11ySettings *settings) { ClutterKeyboardA11yFlags changed_flags = (device->a11y_flags ^ settings->controls); if (changed_flags & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_SLOW_KEYS_ENABLED)) clear_slow_keys (device); if (changed_flags & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_BOUNCE_KEYS_ENABLED)) device->debounce_key = 0; if (changed_flags & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_STICKY_KEYS_ENABLED)) { device->stickykeys_depressed_mask = 0; update_internal_xkb_state (device, 0, 0); } if (changed_flags & CLUTTER_A11Y_KEYBOARD_ENABLED) { device->toggle_slowkeys_timer = 0; device->shift_count = 0; device->last_shift_time = 0; } if (changed_flags & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_MOUSE_KEYS_ENABLED)) { if (settings->controls & (CLUTTER_A11Y_KEYBOARD_ENABLED | CLUTTER_A11Y_MOUSE_KEYS_ENABLED)) enable_mousekeys (device); else disable_mousekeys (device); } update_mousekeys_params (device, settings); /* Keep our own copy of keyboard a11y features flags to see what changes */ device->a11y_flags = settings->controls; } void meta_input_device_native_a11y_maybe_notify_toggle_keys (MetaInputDeviceNative *device) { if (device->a11y_flags & CLUTTER_A11Y_TOGGLE_KEYS_ENABLED) meta_input_device_native_bell_notify (device); } static void release_device_touch_slot (gpointer value) { MetaTouchState *touch_state = value; meta_seat_native_release_touch_state (touch_state->seat, touch_state); } MetaTouchState * meta_input_device_native_acquire_touch_state (MetaInputDeviceNative *device, int device_slot) { MetaTouchState *touch_state; touch_state = meta_seat_native_acquire_touch_state (device->seat, device_slot); g_hash_table_insert (device->touches, GINT_TO_POINTER (device_slot), touch_state); return touch_state; } MetaTouchState * meta_input_device_native_lookup_touch_state (MetaInputDeviceNative *device, int device_slot) { return g_hash_table_lookup (device->touches, GINT_TO_POINTER (device_slot)); } void meta_input_device_native_release_touch_state (MetaInputDeviceNative *device, MetaTouchState *touch_state) { g_hash_table_remove (device->touches, GINT_TO_POINTER (touch_state->device_slot)); } static void meta_input_device_native_class_init (MetaInputDeviceNativeClass *klass) { ClutterInputDeviceClass *device_manager_class = CLUTTER_INPUT_DEVICE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_input_device_native_finalize; object_class->set_property = meta_input_device_native_set_property; object_class->get_property = meta_input_device_native_get_property; device_manager_class->keycode_to_evdev = meta_input_device_native_keycode_to_evdev; device_manager_class->update_from_tool = meta_input_device_native_update_from_tool; device_manager_class->is_mode_switch_button = meta_input_device_native_is_mode_switch_button; device_manager_class->get_group_n_modes = meta_input_device_native_get_group_n_modes; device_manager_class->is_grouped = meta_input_device_native_is_grouped; device_manager_class->process_kbd_a11y_event = meta_input_device_native_process_kbd_a11y_event; obj_props[PROP_DEVICE_MATRIX] = g_param_spec_boxed ("device-matrix", "Device input matrix", "Device input matrix", CAIRO_GOBJECT_TYPE_MATRIX, CLUTTER_PARAM_READWRITE); obj_props[PROP_OUTPUT_ASPECT_RATIO] = g_param_spec_double ("output-aspect-ratio", "Output aspect ratio", "Output aspect ratio", 0, G_MAXDOUBLE, 0, CLUTTER_PARAM_READWRITE); g_object_class_install_properties (object_class, N_PROPS, obj_props); } static void meta_input_device_native_init (MetaInputDeviceNative *self) { cairo_matrix_init_identity (&self->device_matrix); self->device_aspect_ratio = 0; self->output_ratio = 0; self->touches = g_hash_table_new_full (NULL, NULL, NULL, release_device_touch_slot); } /* * meta_input_device_native_new: * @manager: the device manager * @seat: the seat the device will belong to * @libinput_device: the libinput device * * Create a new ClutterInputDevice given a libinput device and associate * it with the provided seat. */ ClutterInputDevice * meta_input_device_native_new (MetaSeatNative *seat, struct libinput_device *libinput_device) { MetaInputDeviceNative *device; ClutterInputDeviceType type; char *vendor, *product; int device_id, n_rings = 0, n_strips = 0, n_groups = 1; char *node_path; double width, height; type = meta_input_device_native_determine_type (libinput_device); vendor = g_strdup_printf ("%.4x", libinput_device_get_id_vendor (libinput_device)); product = g_strdup_printf ("%.4x", libinput_device_get_id_product (libinput_device)); device_id = meta_seat_native_acquire_device_id (seat); node_path = g_strdup_printf ("/dev/input/%s", libinput_device_get_sysname (libinput_device)); if (libinput_device_has_capability (libinput_device, LIBINPUT_DEVICE_CAP_TABLET_PAD)) { n_rings = libinput_device_tablet_pad_get_num_rings (libinput_device); n_strips = libinput_device_tablet_pad_get_num_strips (libinput_device); n_groups = libinput_device_tablet_pad_get_num_mode_groups (libinput_device); } device = g_object_new (META_TYPE_INPUT_DEVICE_NATIVE, "id", device_id, "name", libinput_device_get_name (libinput_device), "device-type", type, "device-mode", CLUTTER_INPUT_MODE_SLAVE, "enabled", TRUE, "vendor-id", vendor, "product-id", product, "n-rings", n_rings, "n-strips", n_strips, "n-mode-groups", n_groups, "device-node", node_path, "seat", seat, NULL); device->seat = seat; device->libinput_device = libinput_device; libinput_device_set_user_data (libinput_device, device); libinput_device_ref (libinput_device); g_free (vendor); g_free (product); g_free (node_path); if (libinput_device_get_size (libinput_device, &width, &height) == 0) device->device_aspect_ratio = width / height; return CLUTTER_INPUT_DEVICE (device); } /* * meta_input_device_native_new_virtual: * @seat: the seat the device will belong to * @type: the input device type * * Create a new virtual ClutterInputDevice of the given type. */ ClutterInputDevice * meta_input_device_native_new_virtual (MetaSeatNative *seat, ClutterInputDeviceType type, ClutterInputMode mode) { MetaInputDeviceNative *device; const char *name; int device_id; switch (type) { case CLUTTER_KEYBOARD_DEVICE: name = "Virtual keyboard device for seat"; break; case CLUTTER_POINTER_DEVICE: name = "Virtual pointer device for seat"; break; case CLUTTER_TOUCHSCREEN_DEVICE: name = "Virtual touchscreen device for seat"; break; default: name = "Virtual device for seat"; break; }; device_id = meta_seat_native_acquire_device_id (seat); device = g_object_new (META_TYPE_INPUT_DEVICE_NATIVE, "id", device_id, "name", name, "device-type", type, "device-mode", mode, "enabled", TRUE, "seat", seat, NULL); device->seat = seat; return CLUTTER_INPUT_DEVICE (device); } MetaSeatNative * meta_input_device_native_get_seat (MetaInputDeviceNative *device) { return device->seat; } void meta_input_device_native_update_leds (MetaInputDeviceNative *device, enum libinput_led leds) { if (!device->libinput_device) return; libinput_device_led_update (device->libinput_device, leds); } ClutterInputDeviceType meta_input_device_native_determine_type (struct libinput_device *ldev) { /* This setting is specific to touchpads and alike, only in these * devices there is this additional layer of touch event interpretation. */ if (libinput_device_config_tap_get_finger_count (ldev) > 0) return CLUTTER_TOUCHPAD_DEVICE; else if (libinput_device_has_capability (ldev, LIBINPUT_DEVICE_CAP_TABLET_TOOL)) return CLUTTER_TABLET_DEVICE; else if (libinput_device_has_capability (ldev, LIBINPUT_DEVICE_CAP_TABLET_PAD)) return CLUTTER_PAD_DEVICE; else if (libinput_device_has_capability (ldev, LIBINPUT_DEVICE_CAP_POINTER)) return CLUTTER_POINTER_DEVICE; else if (libinput_device_has_capability (ldev, LIBINPUT_DEVICE_CAP_TOUCH)) return CLUTTER_TOUCHSCREEN_DEVICE; else if (libinput_device_has_capability (ldev, LIBINPUT_DEVICE_CAP_KEYBOARD)) return CLUTTER_KEYBOARD_DEVICE; else return CLUTTER_EXTENSION_DEVICE; } /** * meta_input_device_native_get_libinput_device: * @device: a #ClutterInputDevice * * Retrieves the libinput_device struct held in @device. * * Returns: The libinput_device struct * * Since: 1.20 * Stability: unstable **/ struct libinput_device * meta_input_device_native_get_libinput_device (ClutterInputDevice *device) { MetaInputDeviceNative *device_evdev; g_return_val_if_fail (META_IS_INPUT_DEVICE_NATIVE (device), NULL); device_evdev = META_INPUT_DEVICE_NATIVE (device); return device_evdev->libinput_device; } void meta_input_device_native_translate_coordinates (ClutterInputDevice *device, ClutterStage *stage, float *x, float *y) { MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device); double min_x = 0, min_y = 0, max_x = 1, max_y = 1; double stage_width, stage_height; double x_d, y_d; stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); x_d = *x / stage_width; y_d = *y / stage_height; /* Apply aspect ratio */ if (device_evdev->output_ratio > 0 && device_evdev->device_aspect_ratio > 0) { double ratio = device_evdev->device_aspect_ratio / device_evdev->output_ratio; if (ratio > 1) x_d *= ratio; else if (ratio < 1) y_d *= 1 / ratio; } cairo_matrix_transform_point (&device_evdev->device_matrix, &min_x, &min_y); cairo_matrix_transform_point (&device_evdev->device_matrix, &max_x, &max_y); cairo_matrix_transform_point (&device_evdev->device_matrix, &x_d, &y_d); *x = CLAMP (x_d, MIN (min_x, max_x), MAX (min_x, max_x)) * stage_width; *y = CLAMP (y_d, MIN (min_y, max_y), MAX (min_y, max_y)) * stage_height; } void meta_input_device_native_release_touch_slots (MetaInputDeviceNative *device_evdev, uint64_t time_us) { GHashTableIter iter; MetaTouchState *touch_state; g_hash_table_iter_init (&iter, device_evdev->touches); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &touch_state)) { meta_seat_native_notify_touch_event (touch_state->seat, CLUTTER_INPUT_DEVICE (device_evdev), CLUTTER_TOUCH_CANCEL, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); g_hash_table_iter_remove (&iter); } } muffin-6.4.1/src/backends/native/meta-clutter-backend-native.c0000664000175000017500000001170414723361714023217 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:meta-clutter-backend-native * @title: MetaClutterBackendNatve * @short_description: A native backend which renders using EGL. * * MetaClutterBackendNative is the #ClutterBackend which is used by the native * (as opposed to the X) backend. It creates a stage with #MetaStageNative and * renders using the #CoglRenderer. * * Note that MetaClutterBackendNative is something different than a * #MetaBackendNative. The former is a #ClutterBackend implementation, while * the latter is a #MetaBackend implementation. */ #include "config.h" #include "backends/native/meta-clutter-backend-native.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-renderer.h" #include "backends/native/meta-seat-native.h" #include "backends/native/meta-stage-native.h" #include "clutter/clutter.h" #include "core/bell.h" #include "meta/meta-backend.h" struct _MetaClutterBackendNative { ClutterBackendEglNative parent; MetaSeatNative *main_seat; MetaStageNative *stage_native; }; static gchar *evdev_seat_id; G_DEFINE_TYPE (MetaClutterBackendNative, meta_clutter_backend_native, CLUTTER_TYPE_BACKEND_EGL_NATIVE) MetaStageNative * meta_clutter_backend_native_get_stage_native (ClutterBackend *backend) { MetaClutterBackendNative *clutter_backend_native = META_CLUTTER_BACKEND_NATIVE (backend); return clutter_backend_native->stage_native; } static CoglRenderer * meta_clutter_backend_native_get_renderer (ClutterBackend *clutter_backend, GError **error) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); return meta_renderer_create_cogl_renderer (renderer); } static ClutterStageWindow * meta_clutter_backend_native_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error) { MetaClutterBackendNative *clutter_backend_native = META_CLUTTER_BACKEND_NATIVE (backend); g_assert (!clutter_backend_native->stage_native); clutter_backend_native->stage_native = g_object_new (META_TYPE_STAGE_NATIVE, "backend", backend, "wrapper", wrapper, NULL); return CLUTTER_STAGE_WINDOW (clutter_backend_native->stage_native); } static void meta_clutter_backend_native_init_events (ClutterBackend *backend) { MetaClutterBackendNative *backend_native = META_CLUTTER_BACKEND_NATIVE (backend); const gchar *seat_id = evdev_seat_id ? evdev_seat_id : "seat0"; backend_native->main_seat = g_object_new (META_TYPE_SEAT_NATIVE, "backend", backend, "seat-id", seat_id, NULL); } static ClutterSeat * meta_clutter_backend_native_get_default_seat (ClutterBackend *backend) { MetaClutterBackendNative *backend_native = META_CLUTTER_BACKEND_NATIVE (backend); return CLUTTER_SEAT (backend_native->main_seat); } static void meta_clutter_backend_native_init (MetaClutterBackendNative *clutter_backend_nativen) { } static void meta_clutter_backend_native_class_init (MetaClutterBackendNativeClass *klass) { ClutterBackendClass *clutter_backend_class = CLUTTER_BACKEND_CLASS (klass); clutter_backend_class->get_renderer = meta_clutter_backend_native_get_renderer; clutter_backend_class->create_stage = meta_clutter_backend_native_create_stage; clutter_backend_class->init_events = meta_clutter_backend_native_init_events; clutter_backend_class->get_default_seat = meta_clutter_backend_native_get_default_seat; } /** * meta_cluter_backend_native_set_seat_id: * @seat_id: The seat ID * * Sets the seat to assign to the libinput context. * * For reliable effects, this function must be called before clutter_init(). */ void meta_clutter_backend_native_set_seat_id (const gchar *seat_id) { g_free (evdev_seat_id); evdev_seat_id = g_strdup (seat_id); } muffin-6.4.1/src/backends/native/meta-drm-buffer.h0000664000175000017500000000262014723361714020717 0ustar fabiofabio/* * Copyright (C) 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Daniel van Vugt */ #ifndef META_DRM_BUFFER_H #define META_DRM_BUFFER_H #include #include #define META_TYPE_DRM_BUFFER (meta_drm_buffer_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaDrmBuffer, meta_drm_buffer, META, DRM_BUFFER, GObject) struct _MetaDrmBufferClass { GObjectClass parent_class; uint32_t (* get_fb_id) (MetaDrmBuffer *buffer); }; MetaDrmBuffer * meta_drm_buffer_new_from_dumb (uint32_t dumb_fb_id); uint32_t meta_drm_buffer_get_fb_id (MetaDrmBuffer *buffer); #endif /* META_DRM_BUFFER_H */ muffin-6.4.1/src/backends/native/meta-kms-update-private.h0000664000175000017500000001050514723361714022411 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_UPDATE_PRIVATE_H #define META_KMS_UPDATE_PRIVATE_H #include #include #include "backends/native/meta-kms-types.h" #include "backends/native/meta-kms-update.h" typedef struct _MetaKmsFeedback { MetaKmsFeedbackResult result; GList *failed_planes; GError *error; } MetaKmsFeedback; typedef struct _MetaKmsProperty { uint32_t prop_id; uint64_t value; } MetaKmsProperty; typedef struct _MetaKmsPlaneAssignment { MetaKmsUpdate *update; MetaKmsCrtc *crtc; MetaKmsPlane *plane; uint32_t fb_id; MetaFixed16Rectangle src_rect; MetaFixed16Rectangle dst_rect; MetaKmsAssignPlaneFlag flags; GList *plane_properties; struct { gboolean is_valid; int x; int y; } cursor_hotspot; } MetaKmsPlaneAssignment; typedef struct _MetaKmsModeSet { MetaKmsCrtc *crtc; GList *connectors; drmModeModeInfo *drm_mode; } MetaKmsModeSet; typedef struct _MetaKmsConnectorProperty { MetaKmsDevice *device; MetaKmsConnector *connector; uint32_t prop_id; uint64_t value; } MetaKmsConnectorProperty; typedef struct _MetaKmsCrtcGamma { MetaKmsCrtc *crtc; int size; uint16_t *red; uint16_t *green; uint16_t *blue; } MetaKmsCrtcGamma; typedef struct _MetaKmsPageFlip { MetaKmsCrtc *crtc; const MetaKmsPageFlipFeedback *feedback; gpointer user_data; MetaKmsCustomPageFlipFunc custom_page_flip_func; gpointer custom_page_flip_user_data; } MetaKmsPageFlip; void meta_kms_plane_feedback_free (MetaKmsPlaneFeedback *plane_feedback); MetaKmsPlaneFeedback * meta_kms_plane_feedback_new_take_error (MetaKmsPlane *plane, MetaKmsCrtc *crtc, GError *error); MetaKmsFeedback * meta_kms_feedback_new_passed (void); MetaKmsFeedback * meta_kms_feedback_new_failed (GList *failed_planes, GError *error); void meta_kms_update_seal (MetaKmsUpdate *update); gboolean meta_kms_update_is_sealed (MetaKmsUpdate *update); void meta_kms_update_set_connector_property (MetaKmsUpdate *update, MetaKmsConnector *connector, uint32_t prop_id, uint64_t value); void meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update, MetaKmsCrtc *crtc, int size, const uint16_t *red, const uint16_t *green, const uint16_t *blue); void meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment, uint32_t prop_id, uint64_t value); MetaKmsPlaneAssignment * meta_kms_update_get_primary_plane_assignment (MetaKmsUpdate *update, MetaKmsCrtc *crtc); GList * meta_kms_update_get_plane_assignments (MetaKmsUpdate *update); GList * meta_kms_update_get_mode_sets (MetaKmsUpdate *update); GList * meta_kms_update_get_page_flips (MetaKmsUpdate *update); GList * meta_kms_update_get_connector_properties (MetaKmsUpdate *update); GList * meta_kms_update_get_crtc_gammas (MetaKmsUpdate *update); G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsPlaneFeedback, meta_kms_plane_feedback_free) #endif /* META_KMS_UPDATE_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-barrier-native.c0000664000175000017500000004126314723361714021601 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:barrier-native * @Title: MetaBarrierImplNative * @Short_Description: Pointer barriers implementation for the native backend */ #include "config.h" #include "backends/native/meta-barrier-native.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-barrier-private.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-backend-native-private.h" #include "meta/barrier.h" #include "meta/util.h" struct _MetaBarrierManagerNative { GHashTable *barriers; }; typedef enum { /* The barrier is active and responsive to pointer motion. */ META_BARRIER_STATE_ACTIVE, /* An intermediate state after a pointer hit the pointer barrier. */ META_BARRIER_STATE_HIT, /* The barrier was hit by a pointer and is still within the hit box and * has not been released.*/ META_BARRIER_STATE_HELD, /* The pointer was released by the user. If the following motion hits * the barrier, it will pass through. */ META_BARRIER_STATE_RELEASE, /* An intermediate state when the pointer has left the barrier. */ META_BARRIER_STATE_LEFT, } MetaBarrierState; struct _MetaBarrierImplNative { MetaBarrierImpl parent; MetaBarrier *barrier; MetaBarrierManagerNative *manager; gboolean is_active; MetaBarrierState state; int trigger_serial; guint32 last_event_time; MetaBarrierDirection blocked_dir; }; G_DEFINE_TYPE (MetaBarrierImplNative, meta_barrier_impl_native, META_TYPE_BARRIER_IMPL) static int next_serial (void) { static int barrier_serial = 1; barrier_serial++; /* If it wraps, avoid 0 as it's not a valid serial. */ if (barrier_serial == 0) barrier_serial++; return barrier_serial; } static gboolean is_barrier_horizontal (MetaBarrier *barrier) { return meta_border_is_horizontal (&barrier->priv->border); } static gboolean is_barrier_blocking_directions (MetaBarrier *barrier, MetaBarrierDirection directions) { return meta_border_is_blocking_directions (&barrier->priv->border, directions); } static void dismiss_pointer (MetaBarrierImplNative *self) { self->state = META_BARRIER_STATE_LEFT; } /* * Calculate the hit box for a held motion. The hit box is a 2 px wide region * in the opposite direction of every direction the barrier blocks. The purpose * of this is to allow small movements without receiving a "left" signal. This * heuristic comes from the X.org pointer barrier implementation. */ static MetaLine2 calculate_barrier_hit_box (MetaBarrier *barrier) { MetaLine2 hit_box = barrier->priv->border.line; if (is_barrier_horizontal (barrier)) { if (is_barrier_blocking_directions (barrier, META_BARRIER_DIRECTION_POSITIVE_Y)) hit_box.a.y -= 2.0f; if (is_barrier_blocking_directions (barrier, META_BARRIER_DIRECTION_NEGATIVE_Y)) hit_box.b.y += 2.0f; } else { if (is_barrier_blocking_directions (barrier, META_BARRIER_DIRECTION_POSITIVE_X)) hit_box.a.x -= 2.0f; if (is_barrier_blocking_directions (barrier, META_BARRIER_DIRECTION_NEGATIVE_X)) hit_box.b.x += 2.0f; } return hit_box; } static gboolean is_within_box (MetaLine2 box, MetaVector2 point) { return (point.x >= box.a.x && point.x < box.b.x && point.y >= box.a.y && point.y < box.b.y); } static void maybe_release_barrier (gpointer key, gpointer value, gpointer user_data) { MetaBarrierImplNative *self = key; MetaBarrier *barrier = self->barrier; MetaLine2 *motion = user_data; MetaLine2 hit_box; if (self->state != META_BARRIER_STATE_HELD) return; /* Release if we end up outside barrier end points. */ if (is_barrier_horizontal (barrier)) { if (motion->b.x > MAX (barrier->priv->border.line.a.x, barrier->priv->border.line.b.x) || motion->b.x < MIN (barrier->priv->border.line.a.x, barrier->priv->border.line.b.x)) { dismiss_pointer (self); return; } } else { if (motion->b.y > MAX (barrier->priv->border.line.a.y, barrier->priv->border.line.b.y) || motion->b.y < MIN (barrier->priv->border.line.a.y, barrier->priv->border.line.b.y)) { dismiss_pointer (self); return; } } /* Release if we don't intersect and end up outside of hit box. */ hit_box = calculate_barrier_hit_box (barrier); if (!is_within_box (hit_box, motion->b)) { dismiss_pointer (self); return; } } static void maybe_release_barriers (MetaBarrierManagerNative *manager, float prev_x, float prev_y, float x, float y) { MetaLine2 motion = { .a = { .x = prev_x, .y = prev_y, }, .b = { .x = x, .y = y, }, }; g_hash_table_foreach (manager->barriers, maybe_release_barrier, &motion); } typedef struct _MetaClosestBarrierData { struct { MetaLine2 motion; MetaBarrierDirection directions; } in; struct { float closest_distance_2; MetaBarrierImplNative *barrier_impl; } out; } MetaClosestBarrierData; static void update_closest_barrier (gpointer key, gpointer value, gpointer user_data) { MetaBarrierImplNative *self = key; MetaBarrier *barrier = self->barrier; MetaClosestBarrierData *data = user_data; MetaVector2 intersection; float dx, dy; float distance_2; /* Ignore if the barrier is not blocking in any of the motions directions. */ if (!is_barrier_blocking_directions (barrier, data->in.directions)) return; /* Ignore if the barrier released the pointer. */ if (self->state == META_BARRIER_STATE_RELEASE) return; /* Ignore if we are moving away from barrier. */ if (self->state == META_BARRIER_STATE_HELD && (data->in.directions & self->blocked_dir) == 0) return; /* Check if the motion intersects with the barrier, and retrieve the * intersection point if any. */ if (!meta_line2_intersects_with (&barrier->priv->border.line, &data->in.motion, &intersection)) return; /* Calculate the distance to the barrier and keep track of the closest * barrier. */ dx = intersection.x - data->in.motion.a.x; dy = intersection.y - data->in.motion.a.y; distance_2 = dx*dx + dy*dy; if (data->out.barrier_impl == NULL || distance_2 < data->out.closest_distance_2) { data->out.barrier_impl = self; data->out.closest_distance_2 = distance_2; } } static gboolean get_closest_barrier (MetaBarrierManagerNative *manager, float prev_x, float prev_y, float x, float y, MetaBarrierDirection motion_dir, MetaBarrierImplNative **barrier_impl) { MetaClosestBarrierData closest_barrier_data; closest_barrier_data = (MetaClosestBarrierData) { .in = { .motion = { .a = { .x = prev_x, .y = prev_y, }, .b = { .x = x, .y = y, }, }, .directions = motion_dir, }, }; g_hash_table_foreach (manager->barriers, update_closest_barrier, &closest_barrier_data); if (closest_barrier_data.out.barrier_impl != NULL) { *barrier_impl = closest_barrier_data.out.barrier_impl; return TRUE; } else { return FALSE; } } typedef struct _MetaBarrierEventData { guint32 time; float prev_x; float prev_y; float x; float y; float dx; float dy; } MetaBarrierEventData; static void emit_barrier_event (MetaBarrierImplNative *self, guint32 time, float prev_x, float prev_y, float x, float y, float dx, float dy) { MetaBarrier *barrier = self->barrier; MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent); MetaBarrierState old_state = self->state; switch (self->state) { case META_BARRIER_STATE_HIT: self->state = META_BARRIER_STATE_HELD; self->trigger_serial = next_serial (); event->dt = 0; break; case META_BARRIER_STATE_RELEASE: case META_BARRIER_STATE_LEFT: self->state = META_BARRIER_STATE_ACTIVE; G_GNUC_FALLTHROUGH; case META_BARRIER_STATE_HELD: event->dt = time - self->last_event_time; break; case META_BARRIER_STATE_ACTIVE: g_assert_not_reached (); /* Invalid state. */ } event->ref_count = 1; event->event_id = self->trigger_serial; event->time = time; event->x = x; event->y = y; event->dx = dx; event->dy = dy; event->grabbed = self->state == META_BARRIER_STATE_HELD; event->released = old_state == META_BARRIER_STATE_RELEASE; self->last_event_time = time; if (self->state == META_BARRIER_STATE_HELD) _meta_barrier_emit_hit_signal (barrier, event); else _meta_barrier_emit_left_signal (barrier, event); meta_barrier_event_unref (event); } static void maybe_emit_barrier_event (gpointer key, gpointer value, gpointer user_data) { MetaBarrierImplNative *self = key; MetaBarrierEventData *data = user_data; switch (self->state) { case META_BARRIER_STATE_ACTIVE: break; case META_BARRIER_STATE_HIT: case META_BARRIER_STATE_HELD: case META_BARRIER_STATE_RELEASE: case META_BARRIER_STATE_LEFT: emit_barrier_event (self, data->time, data->prev_x, data->prev_y, data->x, data->y, data->dx, data->dy); break; } } /* Clamp (x, y) to the barrier and remove clamped direction from motion_dir. */ static void clamp_to_barrier (MetaBarrierImplNative *self, MetaBarrierDirection *motion_dir, float *x, float *y) { MetaBarrier *barrier = self->barrier; if (is_barrier_horizontal (barrier)) { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_Y) *y = barrier->priv->border.line.a.y; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_Y) *y = barrier->priv->border.line.a.y; self->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_Y | META_BARRIER_DIRECTION_NEGATIVE_Y); *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_Y | META_BARRIER_DIRECTION_NEGATIVE_Y); } else { if (*motion_dir & META_BARRIER_DIRECTION_POSITIVE_X) *x = barrier->priv->border.line.a.x; else if (*motion_dir & META_BARRIER_DIRECTION_NEGATIVE_X) *x = barrier->priv->border.line.a.x; self->blocked_dir = *motion_dir & (META_BARRIER_DIRECTION_POSITIVE_X | META_BARRIER_DIRECTION_NEGATIVE_X); *motion_dir &= ~(META_BARRIER_DIRECTION_POSITIVE_X | META_BARRIER_DIRECTION_NEGATIVE_X); } self->state = META_BARRIER_STATE_HIT; } void meta_barrier_manager_native_process (MetaBarrierManagerNative *manager, ClutterInputDevice *device, guint32 time, float *x, float *y) { graphene_point_t prev_pos; float prev_x; float prev_y; float orig_x = *x; float orig_y = *y; MetaBarrierDirection motion_dir = 0; MetaBarrierEventData barrier_event_data; MetaBarrierImplNative *barrier_impl; if (!clutter_input_device_get_coords (device, NULL, &prev_pos)) return; prev_x = prev_pos.x; prev_y = prev_pos.y; /* Get the direction of the motion vector. */ if (prev_x < *x) motion_dir |= META_BARRIER_DIRECTION_POSITIVE_X; else if (prev_x > *x) motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_X; if (prev_y < *y) motion_dir |= META_BARRIER_DIRECTION_POSITIVE_Y; else if (prev_y > *y) motion_dir |= META_BARRIER_DIRECTION_NEGATIVE_Y; /* Clamp to the closest barrier in any direction until either there are no * more barriers to clamp to or all directions have been clamped. */ while (motion_dir != 0) { if (get_closest_barrier (manager, prev_x, prev_y, *x, *y, motion_dir, &barrier_impl)) clamp_to_barrier (barrier_impl, &motion_dir, x, y); else break; } /* Potentially release active barrier movements. */ maybe_release_barriers (manager, prev_x, prev_y, *x, *y); /* Initiate or continue barrier interaction. */ barrier_event_data = (MetaBarrierEventData) { .time = time, .prev_x = prev_x, .prev_y = prev_y, .x = *x, .y = *y, .dx = orig_x - prev_x, .dy = orig_y - prev_y, }; g_hash_table_foreach (manager->barriers, maybe_emit_barrier_event, &barrier_event_data); } static gboolean _meta_barrier_impl_native_is_active (MetaBarrierImpl *impl) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); return self->is_active; } static void _meta_barrier_impl_native_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); if (self->state == META_BARRIER_STATE_HELD && event->event_id == self->trigger_serial) self->state = META_BARRIER_STATE_RELEASE; } static void _meta_barrier_impl_native_destroy (MetaBarrierImpl *impl) { MetaBarrierImplNative *self = META_BARRIER_IMPL_NATIVE (impl); g_hash_table_remove (self->manager->barriers, self); self->is_active = FALSE; } MetaBarrierImpl * meta_barrier_impl_native_new (MetaBarrier *barrier) { MetaBarrierImplNative *self; MetaBackendNative *native; MetaBarrierManagerNative *manager; self = g_object_new (META_TYPE_BARRIER_IMPL_NATIVE, NULL); self->barrier = barrier; self->is_active = TRUE; native = META_BACKEND_NATIVE (meta_get_backend ()); manager = meta_backend_native_get_barrier_manager (native); self->manager = manager; g_hash_table_add (manager->barriers, self); return META_BARRIER_IMPL (self); } static void meta_barrier_impl_native_class_init (MetaBarrierImplNativeClass *klass) { MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass); impl_class->is_active = _meta_barrier_impl_native_is_active; impl_class->release = _meta_barrier_impl_native_release; impl_class->destroy = _meta_barrier_impl_native_destroy; } static void meta_barrier_impl_native_init (MetaBarrierImplNative *self) { } MetaBarrierManagerNative * meta_barrier_manager_native_new (void) { MetaBarrierManagerNative *manager; manager = g_new0 (MetaBarrierManagerNative, 1); manager->barriers = g_hash_table_new (NULL, NULL); return manager; } muffin-6.4.1/src/backends/native/meta-kms.c0000664000175000017500000004152114723361714017456 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * Copyright 2020 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-impl-simple.h" #include "backends/native/meta-kms-update-private.h" #include "backends/native/meta-udev.h" #include "cogl/cogl.h" /** * SECTION:kms * @short description: KMS abstraction * @title: KMS abstraction * * The KMS abstraction consists of various building blocks for helping out with * interacting with the various drm API's, enabling users to use a * transactional API, aiming to hide all interaction with the underlying APIs. * * The subsystem defines two separate contexts, the "main" context, and the * "impl" context. The main context is the context of which mutter as a whole * runs in. It uses the main GLib main loop and main context and always runs in * the main thread. * * The impl context is where all underlying API is being executed. While in the * current state, it always runs in the main thread, the aim is to be able to * execute the impl context in a dedicated thread. * * The public facing MetaKms API is always assumed to be executed from the main * context. * * The KMS abstraction consists of the following public components: * * #MetaKms: * * Main entry point; used by the native backend to create devices, post updates * etc. * * #MetaKmsDevice: * * A device (usually /dev/dri/cardN, where N being a number). Used to get KMS * objects, such as connectors, CRTCs, planes, as well as basic meta data such * as device path etc. * * #MetaKmsCrtc: * * Represents a CRTC. It manages a representation of the current CRTC state, * including current mode, coordinates, possible clones. * * #MetaKmsConnector: * * Represents a connector, e.g. a display port connection. It also manages a * representation of the current state, including meta data such as physical * dimension of the connected, available modes, EDID, tile info etc. It also * contains helper functions for configuration, as well as methods for adding * configuration to a transaction (See #MetaKmsUpdate). * * #MetaKmsPlane: * * Represents a hardware plane. A plane is used to define the content of what * should be presented on a CRTC. Planes can either be primary planes, used as * a backdrop for CRTCs, overlay planes, and cursor planes. * * #MetaKmsUpdate: * * A KMS transaction object, meant to be processed potentially atomically when * posted. An update consists of plane assignments, mode sets and KMS object * property entries. The user adds updates to the object, and then posts it via * MetaKms. It will then be processed by the MetaKms backend (See * #MetaKmsImpl), potentially atomically. * * * There are also these private objects, without public facing API: * * #MetaKmsImpl: * * The KMS backend implementation, running in the impl context. #MetaKmsImpl * itself is an abstract object, with potentially multiple implementations. * Currently only #MetaKmsImplSimple exists. * * #MetaKmsImplSimple: * * A KMS backend implementation using the non-atomic drmMode* API. While it's * interacted with using the transactional API, the #MetaKmsUpdate is processed * non-atomically. * * #MetaKmsImplDevice: * * An object linked to a #MetaKmsDevice, but where it is executed in the impl * context. It takes care of the updating of the various KMS object (CRTC, * connector, ..) states. * * #MetaKmsPageFlip: * * A object representing a page flip. It's created when a page flip is queued, * and contains information necessary to provide feedback to the one requesting * the page flip. * */ enum { RESOURCES_CHANGED, N_SIGNALS }; static int signals[N_SIGNALS]; typedef struct _MetaKmsCallbackData { MetaKmsCallback callback; gpointer user_data; GDestroyNotify user_data_destroy; } MetaKmsCallbackData; typedef struct _MetaKmsSimpleImplSource { GSource source; MetaKms *kms; } MetaKmsSimpleImplSource; typedef struct _MetaKmsFdImplSource { GSource source; gpointer fd_tag; MetaKms *kms; MetaKmsImplTaskFunc dispatch; gpointer user_data; } MetaKmsFdImplSource; struct _MetaKms { GObject parent; MetaBackend *backend; gulong hotplug_handler_id; gulong removed_handler_id; MetaKmsImpl *impl; gboolean in_impl_task; gboolean waiting_for_impl_task; GList *devices; MetaKmsUpdate *pending_update; GList *pending_callbacks; guint callback_source_id; }; G_DEFINE_TYPE (MetaKms, meta_kms, G_TYPE_OBJECT) MetaKmsUpdate * meta_kms_ensure_pending_update (MetaKms *kms) { if (!kms->pending_update) kms->pending_update = meta_kms_update_new (); return meta_kms_get_pending_update (kms); } MetaKmsUpdate * meta_kms_get_pending_update (MetaKms *kms) { return kms->pending_update; } static void meta_kms_predict_states_in_impl (MetaKms *kms, MetaKmsUpdate *update) { meta_assert_in_kms_impl (kms); g_list_foreach (kms->devices, (GFunc) meta_kms_device_predict_states_in_impl, update); } static gpointer meta_kms_process_update_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { g_autoptr (MetaKmsUpdate) update = user_data; MetaKmsFeedback *feedback; feedback = meta_kms_impl_process_update (impl, update); meta_kms_predict_states_in_impl (meta_kms_impl_get_kms (impl), update); return feedback; } static MetaKmsFeedback * meta_kms_post_update_sync (MetaKms *kms, MetaKmsUpdate *update) { meta_kms_update_seal (update); COGL_TRACE_BEGIN_SCOPED (MetaKmsPostUpdateSync, "KMS (post update)"); return meta_kms_run_impl_task_sync (kms, meta_kms_process_update_in_impl, update, NULL); } MetaKmsFeedback * meta_kms_post_pending_update_sync (MetaKms *kms) { return meta_kms_post_update_sync (kms, g_steal_pointer (&kms->pending_update)); } static gpointer meta_kms_discard_pending_page_flips_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { meta_kms_impl_discard_pending_page_flips (impl); return GINT_TO_POINTER (TRUE); } void meta_kms_discard_pending_page_flips (MetaKms *kms) { meta_kms_run_impl_task_sync (kms, meta_kms_discard_pending_page_flips_in_impl, NULL, NULL); } static void meta_kms_callback_data_free (MetaKmsCallbackData *callback_data) { if (callback_data->user_data_destroy) callback_data->user_data_destroy (callback_data->user_data); g_slice_free (MetaKmsCallbackData, callback_data); } static int flush_callbacks (MetaKms *kms) { GList *l; int callback_count = 0; meta_assert_not_in_kms_impl (kms); for (l = kms->pending_callbacks; l; l = l->next) { MetaKmsCallbackData *callback_data = l->data; callback_data->callback (kms, callback_data->user_data); meta_kms_callback_data_free (callback_data); callback_count++; } g_list_free (kms->pending_callbacks); kms->pending_callbacks = NULL; return callback_count; } static gboolean callback_idle (gpointer user_data) { MetaKms *kms = user_data; flush_callbacks (kms); kms->callback_source_id = 0; return G_SOURCE_REMOVE; } void meta_kms_queue_callback (MetaKms *kms, MetaKmsCallback callback, gpointer user_data, GDestroyNotify user_data_destroy) { MetaKmsCallbackData *callback_data; callback_data = g_slice_new0 (MetaKmsCallbackData); *callback_data = (MetaKmsCallbackData) { .callback = callback, .user_data = user_data, .user_data_destroy = user_data_destroy, }; kms->pending_callbacks = g_list_append (kms->pending_callbacks, callback_data); if (!kms->callback_source_id) kms->callback_source_id = g_idle_add (callback_idle, kms); } int meta_kms_flush_callbacks (MetaKms *kms) { int callback_count; callback_count = flush_callbacks (kms); g_clear_handle_id (&kms->callback_source_id, g_source_remove); return callback_count; } gpointer meta_kms_run_impl_task_sync (MetaKms *kms, MetaKmsImplTaskFunc func, gpointer user_data, GError **error) { gpointer ret; kms->in_impl_task = TRUE; kms->waiting_for_impl_task = TRUE; ret = func (kms->impl, user_data, error); kms->waiting_for_impl_task = FALSE; kms->in_impl_task = FALSE; return ret; } static gboolean simple_impl_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { MetaKmsSimpleImplSource *simple_impl_source = (MetaKmsSimpleImplSource *) source; MetaKms *kms = simple_impl_source->kms; gboolean ret; kms->in_impl_task = TRUE; ret = callback (user_data); kms->in_impl_task = FALSE; return ret; } static GSourceFuncs simple_impl_source_funcs = { .dispatch = simple_impl_source_dispatch, }; GSource * meta_kms_add_source_in_impl (MetaKms *kms, GSourceFunc func, gpointer user_data, GDestroyNotify user_data_destroy) { GSource *source; MetaKmsSimpleImplSource *simple_impl_source; meta_assert_in_kms_impl (kms); source = g_source_new (&simple_impl_source_funcs, sizeof (MetaKmsSimpleImplSource)); simple_impl_source = (MetaKmsSimpleImplSource *) source; simple_impl_source->kms = kms; g_source_set_callback (source, func, user_data, user_data_destroy); g_source_set_ready_time (source, 0); g_source_attach (source, g_main_context_get_thread_default ()); return source; } static gboolean meta_kms_fd_impl_source_check (GSource *source) { MetaKmsFdImplSource *fd_impl_source = (MetaKmsFdImplSource *) source; return g_source_query_unix_fd (source, fd_impl_source->fd_tag) & G_IO_IN; } static gboolean meta_kms_fd_impl_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { MetaKmsFdImplSource *fd_impl_source = (MetaKmsFdImplSource *) source; MetaKms *kms = fd_impl_source->kms; gpointer ret; GError *error = NULL; kms->in_impl_task = TRUE; ret = fd_impl_source->dispatch (kms->impl, fd_impl_source->user_data, &error); kms->in_impl_task = FALSE; if (!GPOINTER_TO_INT (ret)) { g_warning ("Failed to dispatch fd source: %s", error->message); g_error_free (error); } return G_SOURCE_CONTINUE; } static GSourceFuncs fd_impl_source_funcs = { NULL, meta_kms_fd_impl_source_check, meta_kms_fd_impl_source_dispatch }; GSource * meta_kms_register_fd_in_impl (MetaKms *kms, int fd, MetaKmsImplTaskFunc dispatch, gpointer user_data) { GSource *source; MetaKmsFdImplSource *fd_impl_source; meta_assert_in_kms_impl (kms); source = g_source_new (&fd_impl_source_funcs, sizeof (MetaKmsFdImplSource)); fd_impl_source = (MetaKmsFdImplSource *) source; fd_impl_source->dispatch = dispatch; fd_impl_source->user_data = user_data; fd_impl_source->kms = kms; fd_impl_source->fd_tag = g_source_add_unix_fd (source, fd, G_IO_IN | G_IO_ERR); g_source_attach (source, g_main_context_get_thread_default ()); return source; } gboolean meta_kms_in_impl_task (MetaKms *kms) { return kms->in_impl_task; } gboolean meta_kms_is_waiting_for_impl_task (MetaKms *kms) { return kms->waiting_for_impl_task; } static void meta_kms_update_states_in_impl (MetaKms *kms) { COGL_TRACE_BEGIN_SCOPED (MetaKmsUpdateStates, "KMS (update states)"); meta_assert_in_kms_impl (kms); g_list_foreach (kms->devices, (GFunc) meta_kms_device_update_states_in_impl, NULL); } static gpointer update_states_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { MetaKms *kms = meta_kms_impl_get_kms (impl);; meta_kms_update_states_in_impl (kms); return GINT_TO_POINTER (TRUE); } static gboolean meta_kms_update_states_sync (MetaKms *kms, GError **error) { gpointer ret; ret = meta_kms_run_impl_task_sync (kms, update_states_in_impl, NULL, error); return GPOINTER_TO_INT (ret); } static void handle_hotplug_event (MetaKms *kms) { g_autoptr (GError) error = NULL; if (!meta_kms_update_states_sync (kms, &error)) g_warning ("Updating KMS state failed: %s", error->message); g_signal_emit (kms, signals[RESOURCES_CHANGED], 0); } static void on_udev_hotplug (MetaUdev *udev, MetaKms *kms) { handle_hotplug_event (kms); } static void on_udev_device_removed (MetaUdev *udev, GUdevDevice *device, MetaKms *kms) { handle_hotplug_event (kms); } MetaBackend * meta_kms_get_backend (MetaKms *kms) { return kms->backend; } static gpointer notify_device_created_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { MetaKmsDevice *device = user_data; meta_kms_impl_notify_device_created (impl, device); return GINT_TO_POINTER (TRUE); } MetaKmsDevice * meta_kms_create_device (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, GError **error) { MetaKmsDevice *device; device = meta_kms_device_new (kms, path, flags, error); if (!device) return NULL; meta_kms_run_impl_task_sync (kms, notify_device_created_in_impl, device, NULL); kms->devices = g_list_append (kms->devices, device); return device; } MetaKms * meta_kms_new (MetaBackend *backend, GError **error) { MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaUdev *udev = meta_backend_native_get_udev (backend_native); MetaKms *kms; kms = g_object_new (META_TYPE_KMS, NULL); kms->backend = backend; kms->impl = META_KMS_IMPL (meta_kms_impl_simple_new (kms, error)); if (!kms->impl) { g_object_unref (kms); return NULL; } kms->hotplug_handler_id = g_signal_connect (udev, "hotplug", G_CALLBACK (on_udev_hotplug), kms); kms->removed_handler_id = g_signal_connect (udev, "device-removed", G_CALLBACK (on_udev_device_removed), kms); return kms; } static void meta_kms_finalize (GObject *object) { MetaKms *kms = META_KMS (object); MetaBackendNative *backend_native = META_BACKEND_NATIVE (kms->backend); MetaUdev *udev = meta_backend_native_get_udev (backend_native); GList *l; for (l = kms->pending_callbacks; l; l = l->next) meta_kms_callback_data_free (l->data); g_list_free (kms->pending_callbacks); g_clear_handle_id (&kms->callback_source_id, g_source_remove); g_list_free_full (kms->devices, g_object_unref); g_clear_signal_handler (&kms->hotplug_handler_id, udev); g_clear_signal_handler (&kms->removed_handler_id, udev); G_OBJECT_CLASS (meta_kms_parent_class)->finalize (object); } static void meta_kms_init (MetaKms *kms) { } static void meta_kms_class_init (MetaKmsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_finalize; signals[RESOURCES_CHANGED] = g_signal_new ("resources-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/backends/native/meta-keymap-native.c0000664000175000017500000000760214723361714021440 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-seat-native.h" static const char *option_xkb_layout = "us"; static const char *option_xkb_variant = ""; static const char *option_xkb_options = ""; typedef struct _MetaKeymapNative MetaKeymapNative; struct _MetaKeymapNative { ClutterKeymap parent_instance; struct xkb_keymap *keymap; }; G_DEFINE_TYPE (MetaKeymapNative, meta_keymap_native, CLUTTER_TYPE_KEYMAP) static void meta_keymap_native_finalize (GObject *object) { MetaKeymapNative *keymap = META_KEYMAP_NATIVE (object); xkb_keymap_unref (keymap->keymap); G_OBJECT_CLASS (meta_keymap_native_parent_class)->finalize (object); } static gboolean meta_keymap_native_get_num_lock_state (ClutterKeymap *keymap) { struct xkb_state *xkb_state; ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); xkb_state = meta_seat_native_get_xkb_state (META_SEAT_NATIVE (seat)); return xkb_state_mod_name_is_active (xkb_state, XKB_MOD_NAME_NUM, XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED); } static gboolean meta_keymap_native_get_caps_lock_state (ClutterKeymap *keymap) { struct xkb_state *xkb_state; ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); xkb_state = meta_seat_native_get_xkb_state (META_SEAT_NATIVE (seat)); return xkb_state_mod_name_is_active (xkb_state, XKB_MOD_NAME_CAPS, XKB_STATE_MODS_LATCHED | XKB_STATE_MODS_LOCKED); } static PangoDirection meta_keymap_native_get_direction (ClutterKeymap *keymap) { return PANGO_DIRECTION_NEUTRAL; } static void meta_keymap_native_class_init (MetaKeymapNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterKeymapClass *keymap_class = CLUTTER_KEYMAP_CLASS (klass); object_class->finalize = meta_keymap_native_finalize; keymap_class->get_num_lock_state = meta_keymap_native_get_num_lock_state; keymap_class->get_caps_lock_state = meta_keymap_native_get_caps_lock_state; keymap_class->get_direction = meta_keymap_native_get_direction; } static void meta_keymap_native_init (MetaKeymapNative *keymap) { struct xkb_context *ctx; struct xkb_rule_names names; names.rules = "evdev"; names.model = "pc105"; names.layout = option_xkb_layout; names.variant = option_xkb_variant; names.options = option_xkb_options; ctx = xkb_context_new (XKB_CONTEXT_NO_FLAGS); g_assert (ctx); keymap->keymap = xkb_keymap_new_from_names (ctx, &names, 0); xkb_context_unref (ctx); } void meta_keymap_native_set_keyboard_map (MetaKeymapNative *keymap, struct xkb_keymap *xkb_keymap) { if (keymap->keymap) xkb_keymap_unref (keymap->keymap); keymap->keymap = xkb_keymap_ref (xkb_keymap); } struct xkb_keymap * meta_keymap_native_get_keyboard_map (MetaKeymapNative *keymap) { return keymap->keymap; } muffin-6.4.1/src/backends/native/gen-default-modes.py0000775000175000017500000000702514723361714021452 0ustar fabiofabio#!/usr/bin/env python3 # Copyright (C) 2016 Red Hat Inc. # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License as # published by the Free Software Foundation; either version 2 of the # License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, but # WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA # 02111-1307, USA. import os import sys if len(sys.argv) != 2: print("Usage: %s [output file]"%sys.argv[0]) exit(1) common_resolutions = [ # 4:3 (800, 600), (1024, 768), (1152, 864), (1280, 960), (1400, 1050), (1440, 1080), (1600, 1200), (1920, 1440), (2048, 1536), # 16:10 (1280, 800), (1440, 900), (1680, 1050), (1920, 1200), (2560, 1600), # 16:9 (1280, 720), (1366, 768), (1600, 900), (1920, 1080), (2048, 1152), (2560, 1440), (2880, 1620), (3200, 1800), (3840, 2160), (4096, 2304), (5120, 2880), ] output_lines = [ "/* Generated by gen-default-modes.py */\n", "static const drmModeModeInfo meta_default_landscape_drm_mode_infos[] = {", ] def sync_flags(hsync, vsync): flags = "DRM_MODE_FLAG_" flags += "NHSYNC" if hsync[0] == '-' else "PHSYNC" flags += " | DRM_MODE_FLAG_" flags += "NVSYNC" if vsync[0] == '-' else "PVSYNC" return flags def drm_mode_info_from_modeline(line): sline = line.split() return "{ %d, %d, %d, %d, %d, 0, %d, %d, %d, %d, 0, 0, %s, DRM_MODE_TYPE_DEFAULT, %s }," % \ (int(float(sline[2]) * 1000), int(sline[3]), int(sline[4]), int(sline[5]), int(sline[6]), int(sline[7]), int(sline[8]), int(sline[9]), int(sline[10]), sync_flags(sline[11], sline[12]), sline[1]) def portrait_drm_mode_info_from_modeline(line): sline = line.split() return "{ %d, %d, %d, %d, %d, 0, %d, %d, %d, %d, 0, 0, %s, DRM_MODE_TYPE_DEFAULT, \"%dx%d_60.00\" }," % \ (int(float(sline[2]) * 1000), int(sline[7]), int(sline[8]), int(sline[9]), int(sline[10]), int(sline[3]), int(sline[4]), int(sline[5]), int(sline[6]), sync_flags(sline[12], sline[11]), int(sline[7]), int(sline[3])) for resolution in common_resolutions: cvt = os.popen("%s %s %s" % ('cvt', resolution[0], resolution[1])) cvt.readline() # discard comment line line = cvt.readline() output_lines.append(drm_mode_info_from_modeline(line)) cvt.close() output_lines.append("};") output_lines.append("") output_lines.append("static const drmModeModeInfo meta_default_portrait_drm_mode_infos[] = {") for resolution in common_resolutions: cvt = os.popen("%s %s %s" % ('cvt', resolution[0], resolution[1])) cvt.readline() # discard comment line line = cvt.readline() output_lines.append(portrait_drm_mode_info_from_modeline(line)) cvt.close() output_lines.append("};") try: output_file = open(sys.argv[1], 'w') for line in output_lines: output_file.write(line + "\n") output_file.flush() output_file.close() except: print("Failed to generate modelines:", sys.exc_info()[0]) exit(1) muffin-6.4.1/src/backends/native/meta-cursor-renderer-native.h0000664000175000017500000000262314723361714023276 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_NATIVE_H #define META_CURSOR_RENDERER_NATIVE_H #include "backends/meta-cursor-renderer.h" #include "meta/meta-backend.h" #define META_TYPE_CURSOR_RENDERER_NATIVE (meta_cursor_renderer_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaCursorRendererNative, meta_cursor_renderer_native, META, CURSOR_RENDERER_NATIVE, MetaCursorRenderer) MetaCursorRendererNative * meta_cursor_renderer_native_new (MetaBackend *backend); #endif /* META_CURSOR_RENDERER_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-crtc-kms.c0000664000175000017500000002203614723361714020407 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013-2017 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-crtc-kms.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-output-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-update.h" #define ALL_TRANSFORMS_MASK ((1 << META_MONITOR_N_TRANSFORMS) - 1) typedef struct _MetaCrtcKms { MetaKmsCrtc *kms_crtc; MetaKmsPlane *primary_plane; } MetaCrtcKms; static GQuark kms_crtc_crtc_kms_quark; gboolean meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, MetaMonitorTransform transform) { MetaCrtcKms *crtc_kms = crtc->driver_private; if (!crtc_kms->primary_plane) return FALSE; return meta_kms_plane_is_transform_handled (crtc_kms->primary_plane, transform); } void meta_crtc_kms_apply_transform (MetaCrtc *crtc, MetaKmsPlaneAssignment *kms_plane_assignment) { MetaCrtcKms *crtc_kms = crtc->driver_private; MetaMonitorTransform hw_transform; hw_transform = crtc->config->transform; if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) hw_transform = META_MONITOR_TRANSFORM_NORMAL; if (!meta_crtc_kms_is_transform_handled (crtc, hw_transform)) return; meta_kms_plane_update_set_rotation (crtc_kms->primary_plane, kms_plane_assignment, hw_transform); } void meta_crtc_kms_assign_primary_plane (MetaCrtc *crtc, uint32_t fb_id, MetaKmsUpdate *kms_update) { MetaCrtcConfig *crtc_config; MetaFixed16Rectangle src_rect; MetaFixed16Rectangle dst_rect; MetaKmsAssignPlaneFlag flags; MetaKmsCrtc *kms_crtc; MetaKmsDevice *kms_device; MetaKmsPlane *primary_kms_plane; MetaKmsPlaneAssignment *plane_assignment; crtc_config = crtc->config; src_rect = (MetaFixed16Rectangle) { .x = meta_fixed_16_from_int (0), .y = meta_fixed_16_from_int (0), .width = meta_fixed_16_from_int (crtc_config->mode->width), .height = meta_fixed_16_from_int (crtc_config->mode->height), }; dst_rect = (MetaFixed16Rectangle) { .x = meta_fixed_16_from_int (0), .y = meta_fixed_16_from_int (0), .width = meta_fixed_16_from_int (crtc_config->mode->width), .height = meta_fixed_16_from_int (crtc_config->mode->height), }; flags = META_KMS_ASSIGN_PLANE_FLAG_NONE; kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); kms_device = meta_kms_crtc_get_device (kms_crtc); primary_kms_plane = meta_kms_device_get_primary_plane_for (kms_device, kms_crtc); plane_assignment = meta_kms_update_assign_plane (kms_update, kms_crtc, primary_kms_plane, fb_id, src_rect, dst_rect, flags); meta_crtc_kms_apply_transform (crtc, plane_assignment); } static GList * generate_crtc_connector_list (MetaGpu *gpu, MetaCrtc *crtc) { GList *connectors = NULL; GList *l; for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; MetaCrtc *assigned_crtc; assigned_crtc = meta_output_get_assigned_crtc (output); if (assigned_crtc == crtc) { MetaKmsConnector *kms_connector = meta_output_kms_get_kms_connector (output); connectors = g_list_prepend (connectors, kms_connector); } } return connectors; } void meta_crtc_kms_set_mode (MetaCrtc *crtc, MetaKmsUpdate *kms_update) { MetaCrtcConfig *crtc_config = crtc->config; MetaGpu *gpu = meta_crtc_get_gpu (crtc); GList *connectors; drmModeModeInfo *mode; connectors = generate_crtc_connector_list (gpu, crtc); if (connectors) { mode = crtc_config->mode->driver_private; g_debug ("Setting CRTC (%ld) mode to %s", crtc->crtc_id, mode->name); } else { mode = NULL; g_debug ("Unsetting CRTC (%ld) mode", crtc->crtc_id); } meta_kms_update_mode_set (kms_update, meta_crtc_kms_get_kms_crtc (crtc), g_steal_pointer (&connectors), mode); } void meta_crtc_kms_page_flip (MetaCrtc *crtc, const MetaKmsPageFlipFeedback *page_flip_feedback, gpointer user_data, MetaKmsUpdate *kms_update) { meta_kms_update_page_flip (kms_update, meta_crtc_kms_get_kms_crtc (crtc), page_flip_feedback, user_data); } MetaKmsCrtc * meta_crtc_kms_get_kms_crtc (MetaCrtc *crtc) { MetaCrtcKms *crtc_kms = crtc->driver_private; return crtc_kms->kms_crtc; } /** * meta_crtc_kms_get_modifiers: * @crtc: a #MetaCrtc object that has to be a #MetaCrtcKms * @format: a DRM pixel format * * Returns a pointer to a #GArray containing all the supported * modifiers for the given DRM pixel format on the CRTC's primary * plane. The array element type is uint64_t. * * The caller must not modify or destroy the array or its contents. * * Returns NULL if the modifiers are not known or the format is not * supported. */ GArray * meta_crtc_kms_get_modifiers (MetaCrtc *crtc, uint32_t format) { MetaCrtcKms *crtc_kms = crtc->driver_private; return meta_kms_plane_get_modifiers_for_format (crtc_kms->primary_plane, format); } /** * meta_crtc_kms_copy_drm_format_list: * @crtc: a #MetaCrtc object that has to be a #MetaCrtcKms * * Returns a new #GArray that the caller must destroy. The array * contains all the DRM pixel formats the CRTC supports on * its primary plane. The array element type is uint32_t. */ GArray * meta_crtc_kms_copy_drm_format_list (MetaCrtc *crtc) { MetaCrtcKms *crtc_kms = crtc->driver_private; return meta_kms_plane_copy_drm_format_list (crtc_kms->primary_plane); } /** * meta_crtc_kms_supports_format: * @crtc: a #MetaCrtc object that has to be a #MetaCrtcKms * @drm_format: a DRM pixel format * * Returns true if the CRTC supports the format on its primary plane. */ gboolean meta_crtc_kms_supports_format (MetaCrtc *crtc, uint32_t drm_format) { MetaCrtcKms *crtc_kms = crtc->driver_private; return meta_kms_plane_is_format_supported (crtc_kms->primary_plane, drm_format); } MetaCrtc * meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc) { return g_object_get_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark); } static void meta_crtc_destroy_notify (MetaCrtc *crtc) { g_free (crtc->driver_private); } MetaCrtc * meta_create_kms_crtc (MetaGpuKms *gpu_kms, MetaKmsCrtc *kms_crtc) { MetaGpu *gpu = META_GPU (gpu_kms); MetaKmsDevice *kms_device; MetaCrtc *crtc; MetaCrtcKms *crtc_kms; MetaKmsPlane *primary_plane; kms_device = meta_gpu_kms_get_kms_device (gpu_kms); primary_plane = meta_kms_device_get_primary_plane_for (kms_device, kms_crtc); crtc = g_object_new (META_TYPE_CRTC, NULL); crtc->gpu = gpu; crtc->crtc_id = meta_kms_crtc_get_id (kms_crtc); crtc->is_dirty = FALSE; crtc->all_transforms = ALL_TRANSFORMS_MASK; crtc_kms = g_new0 (MetaCrtcKms, 1); crtc_kms->kms_crtc = kms_crtc; crtc_kms->primary_plane = primary_plane; crtc->driver_private = crtc_kms; crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify; if (!kms_crtc_crtc_kms_quark) { kms_crtc_crtc_kms_quark = g_quark_from_static_string ("meta-kms-crtc-crtc-kms-quark"); } g_object_set_qdata (G_OBJECT (kms_crtc), kms_crtc_crtc_kms_quark, crtc); return crtc; } muffin-6.4.1/src/backends/native/meta-event-native.c0000664000175000017500000001224314723361714021270 0ustar fabiofabio/* * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authored by: * Carlos Garnacho */ #include "config.h" #include "backends/native/meta-event-native.h" #include "backends/native/meta-input-device-native.h" #include "clutter/clutter-mutter.h" typedef struct _MetaEventNative MetaEventNative; struct _MetaEventNative { uint32_t evcode; uint64_t time_usec; gboolean has_relative_motion; double dx; double dy; double dx_unaccel; double dy_unaccel; }; static MetaEventNative * meta_event_native_new (void) { return g_slice_new0 (MetaEventNative); } MetaEventNative * meta_event_native_copy (MetaEventNative *event_evdev) { if (event_evdev != NULL) return g_slice_dup (MetaEventNative, event_evdev); return NULL; } void meta_event_native_free (MetaEventNative *event_evdev) { if (event_evdev != NULL) g_slice_free (MetaEventNative, event_evdev); } static MetaEventNative * meta_event_native_ensure_platform_data (ClutterEvent *event) { MetaEventNative *event_evdev = _clutter_event_get_platform_data (event); if (!event_evdev) { event_evdev = meta_event_native_new (); _clutter_event_set_platform_data (event, event_evdev); } return event_evdev; } void meta_event_native_set_event_code (ClutterEvent *event, uint32_t evcode) { MetaEventNative *event_evdev; event_evdev = meta_event_native_ensure_platform_data (event); event_evdev->evcode = evcode; } void meta_event_native_set_time_usec (ClutterEvent *event, uint64_t time_usec) { MetaEventNative *event_evdev; event_evdev = meta_event_native_ensure_platform_data (event); event_evdev->time_usec = time_usec; } void meta_event_native_set_relative_motion (ClutterEvent *event, double dx, double dy, double dx_unaccel, double dy_unaccel) { MetaEventNative *event_evdev; event_evdev = meta_event_native_ensure_platform_data (event); event_evdev->dx = dx; event_evdev->dy = dy; event_evdev->dx_unaccel = dx_unaccel; event_evdev->dy_unaccel = dy_unaccel; event_evdev->has_relative_motion = TRUE; } /** * meta_event_native_get_event_code: * @event: a #ClutterEvent * * Returns the event code of the original event. See linux/input.h for more * information. * * Returns: The event code. **/ uint32_t meta_event_native_get_event_code (const ClutterEvent *event) { MetaEventNative *event_evdev = _clutter_event_get_platform_data (event); if (event_evdev) return event_evdev->evcode; return 0; } /** * meta_event_native_get_time_usec: * @event: a #ClutterEvent * * Returns the time in microsecond granularity, or 0 if unavailable. * * Returns: The time in microsecond granularity, or 0 if unavailable. */ uint64_t meta_event_native_get_time_usec (const ClutterEvent *event) { MetaEventNative *event_evdev = _clutter_event_get_platform_data (event); if (event_evdev) return event_evdev->time_usec; return 0; } /** * meta_event_get_pointer_motion * @event: a #ClutterEvent * * If available, the normal and unaccelerated motion deltas are written * to the dx, dy, dx_unaccel and dy_unaccel and TRUE is returned. * * If unavailable, FALSE is returned. * * Returns: TRUE on success, otherwise FALSE. **/ gboolean meta_event_native_get_relative_motion (const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel) { MetaEventNative *event_evdev = _clutter_event_get_platform_data (event); if (event_evdev && event_evdev->has_relative_motion) { if (dx) *dx = event_evdev->dx; if (dy) *dy = event_evdev->dy; if (dx_unaccel) *dx_unaccel = event_evdev->dx_unaccel; if (dy_unaccel) *dy_unaccel = event_evdev->dy_unaccel; return TRUE; } else return FALSE; } /** * meta_event_native_sequence_get_slot: * @sequence: a #ClutterEventSequence * * Retrieves the touch slot triggered by this @sequence * * Returns: the libinput touch slot. * * Since: 1.20 * Stability: unstable **/ int32_t meta_event_native_sequence_get_slot (const ClutterEventSequence *sequence) { if (!sequence) return -1; return GPOINTER_TO_INT (sequence) - 1; } muffin-6.4.1/src/backends/native/meta-cursor-renderer-native.c0000664000175000017500000016321714723361714023300 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * Copyright 2020 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "backends/native/meta-cursor-renderer-native.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-output.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-renderer-native.h" #include "core/boxes-private.h" #include "meta/boxes.h" #include "meta/meta-backend.h" #include "meta/util.h" #ifdef HAVE_WAYLAND #include "wayland/meta-cursor-sprite-wayland.h" #include "wayland/meta-wayland-buffer.h" #endif #ifndef DRM_CAP_CURSOR_WIDTH #define DRM_CAP_CURSOR_WIDTH 0x8 #endif #ifndef DRM_CAP_CURSOR_HEIGHT #define DRM_CAP_CURSOR_HEIGHT 0x9 #endif /* When animating a cursor, we usually call drmModeSetCursor2 once per frame. * Though, testing shows that we need to triple buffer the cursor buffer in * order to avoid glitches when animating the cursor, at least when running on * Intel. The reason for this might be (but is not confirmed to be) due to * the user space gbm_bo cache, making us reuse and overwrite the kernel side * buffer content before it was scanned out. To avoid this, we keep a user space * reference to each buffer we set until at least one frame after it was drawn. * In effect, this means we three active cursor gbm_bo's: one that that just has * been set, one that was previously set and may or may not have been scanned * out, and one pending that will be replaced if the cursor sprite changes. */ #define HW_CURSOR_BUFFER_COUNT 3 static GQuark quark_cursor_sprite = 0; struct _MetaCursorRendererNative { MetaCursorRenderer parent; }; struct _MetaCursorRendererNativePrivate { MetaBackend *backend; gboolean hw_state_invalidated; gboolean has_hw_cursor; MetaCursorSprite *last_cursor; guint animation_timeout_id; }; typedef struct _MetaCursorRendererNativePrivate MetaCursorRendererNativePrivate; typedef struct _MetaCursorRendererNativeGpuData { gboolean hw_cursor_broken; uint64_t cursor_width; uint64_t cursor_height; } MetaCursorRendererNativeGpuData; typedef enum _MetaCursorGbmBoState { META_CURSOR_GBM_BO_STATE_NONE, META_CURSOR_GBM_BO_STATE_SET, META_CURSOR_GBM_BO_STATE_INVALIDATED, } MetaCursorGbmBoState; typedef struct _MetaCursorNativeGpuState { MetaGpu *gpu; guint active_bo; MetaCursorGbmBoState pending_bo_state; struct gbm_bo *bos[HW_CURSOR_BUFFER_COUNT]; } MetaCursorNativeGpuState; typedef struct _MetaCursorNativePrivate { GHashTable *gpu_states; struct { gboolean can_preprocess; float current_relative_scale; MetaMonitorTransform current_relative_transform; } preprocess_state; } MetaCursorNativePrivate; static GQuark quark_cursor_renderer_native_gpu_data = 0; G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererNative, meta_cursor_renderer_native, META_TYPE_CURSOR_RENDERER); static void realize_cursor_sprite (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, GList *gpus); static MetaCursorNativeGpuState * get_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv, MetaGpuKms *gpu_kms); static MetaCursorNativeGpuState * ensure_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv, MetaGpuKms *gpu_kms); static void invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite); static MetaCursorNativePrivate * ensure_cursor_priv (MetaCursorSprite *cursor_sprite); static MetaCursorNativePrivate * get_cursor_priv (MetaCursorSprite *cursor_sprite); static MetaCursorRendererNativeGpuData * meta_cursor_renderer_native_gpu_data_from_gpu (MetaGpuKms *gpu_kms) { return g_object_get_qdata (G_OBJECT (gpu_kms), quark_cursor_renderer_native_gpu_data); } static MetaCursorRendererNativeGpuData * meta_create_cursor_renderer_native_gpu_data (MetaGpuKms *gpu_kms) { MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; cursor_renderer_gpu_data = g_new0 (MetaCursorRendererNativeGpuData, 1); g_object_set_qdata_full (G_OBJECT (gpu_kms), quark_cursor_renderer_native_gpu_data, cursor_renderer_gpu_data, g_free); return cursor_renderer_gpu_data; } static void meta_cursor_renderer_native_finalize (GObject *object) { MetaCursorRendererNative *renderer = META_CURSOR_RENDERER_NATIVE (object); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (renderer); g_clear_handle_id (&priv->animation_timeout_id, g_source_remove); G_OBJECT_CLASS (meta_cursor_renderer_native_parent_class)->finalize (object); } static guint get_pending_cursor_sprite_gbm_bo_index (MetaCursorNativeGpuState *cursor_gpu_state) { return (cursor_gpu_state->active_bo + 1) % HW_CURSOR_BUFFER_COUNT; } static struct gbm_bo * get_pending_cursor_sprite_gbm_bo (MetaCursorNativeGpuState *cursor_gpu_state) { guint pending_bo; pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); return cursor_gpu_state->bos[pending_bo]; } static struct gbm_bo * get_active_cursor_sprite_gbm_bo (MetaCursorNativeGpuState *cursor_gpu_state) { return cursor_gpu_state->bos[cursor_gpu_state->active_bo]; } static void set_pending_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, MetaGpuKms *gpu_kms, struct gbm_bo *bo) { MetaCursorNativePrivate *cursor_priv; MetaCursorNativeGpuState *cursor_gpu_state; guint pending_bo; cursor_priv = ensure_cursor_priv (cursor_sprite); cursor_gpu_state = ensure_cursor_gpu_state (cursor_priv, gpu_kms); pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); cursor_gpu_state->bos[pending_bo] = bo; cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_SET; } static void calculate_crtc_cursor_hotspot (MetaCursorSprite *cursor_sprite, int *cursor_hotspot_x, int *cursor_hotspot_y) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); int hot_x, hot_y; int width, height; float scale; MetaMonitorTransform transform; scale = cursor_priv->preprocess_state.current_relative_scale; transform = cursor_priv->preprocess_state.current_relative_transform; meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y); width = meta_cursor_sprite_get_width (cursor_sprite); height = meta_cursor_sprite_get_height (cursor_sprite); meta_monitor_transform_transform_point (transform, width, height, hot_x, hot_y, &hot_x, &hot_y); *cursor_hotspot_x = (int) roundf (hot_x * scale); *cursor_hotspot_y = (int) roundf (hot_y * scale); } static void set_crtc_cursor (MetaCursorRendererNative *native, MetaKmsUpdate *kms_update, MetaCrtc *crtc, int x, int y, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); MetaCursorNativeGpuState *cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); MetaKmsCrtc *kms_crtc; MetaKmsDevice *kms_device; MetaKmsPlane *cursor_plane; struct gbm_bo *bo; union gbm_bo_handle handle; int cursor_width, cursor_height; MetaFixed16Rectangle src_rect; MetaFixed16Rectangle dst_rect; MetaKmsAssignPlaneFlag flags; int cursor_hotspot_x; int cursor_hotspot_y; MetaKmsPlaneAssignment *plane_assignment; if (cursor_gpu_state->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET) bo = get_pending_cursor_sprite_gbm_bo (cursor_gpu_state); else bo = get_active_cursor_sprite_gbm_bo (cursor_gpu_state); kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); kms_device = meta_kms_crtc_get_device (kms_crtc); cursor_plane = meta_kms_device_get_cursor_plane_for (kms_device, kms_crtc); g_return_if_fail (cursor_plane); handle = gbm_bo_get_handle (bo); cursor_width = cursor_renderer_gpu_data->cursor_width; cursor_height = cursor_renderer_gpu_data->cursor_height; src_rect = (MetaFixed16Rectangle) { .x = meta_fixed_16_from_int (0), .y = meta_fixed_16_from_int (0), .width = meta_fixed_16_from_int (cursor_width), .height = meta_fixed_16_from_int (cursor_height), }; dst_rect = (MetaFixed16Rectangle) { .x = meta_fixed_16_from_int (x), .y = meta_fixed_16_from_int (y), .width = meta_fixed_16_from_int (cursor_width), .height = meta_fixed_16_from_int (cursor_height), }; flags = META_KMS_ASSIGN_PLANE_FLAG_NONE; if (!priv->hw_state_invalidated && bo == crtc->cursor_renderer_private) flags |= META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED; plane_assignment = meta_kms_update_assign_plane (kms_update, kms_crtc, cursor_plane, handle.u32, src_rect, dst_rect, flags); calculate_crtc_cursor_hotspot (cursor_sprite, &cursor_hotspot_x, &cursor_hotspot_y); meta_kms_plane_assignment_set_cursor_hotspot (plane_assignment, cursor_hotspot_x, cursor_hotspot_y); crtc->cursor_renderer_private = bo; if (cursor_gpu_state->pending_bo_state == META_CURSOR_GBM_BO_STATE_SET) { cursor_gpu_state->active_bo = (cursor_gpu_state->active_bo + 1) % HW_CURSOR_BUFFER_COUNT; cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_NONE; } } static void unset_crtc_cursor (MetaCursorRendererNative *native, MetaKmsUpdate *kms_update, MetaCrtc *crtc) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaKmsCrtc *kms_crtc; MetaKmsDevice *kms_device; MetaKmsPlane *cursor_plane; if (!priv->hw_state_invalidated && !crtc->cursor_renderer_private) return; kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); kms_device = meta_kms_crtc_get_device (kms_crtc); cursor_plane = meta_kms_device_get_cursor_plane_for (kms_device, kms_crtc); if (cursor_plane) meta_kms_update_unassign_plane (kms_update, kms_crtc, cursor_plane); crtc->cursor_renderer_private = NULL; } static float calculate_cursor_crtc_sprite_scale (MetaCursorSprite *cursor_sprite, MetaLogicalMonitor *logical_monitor) { if (meta_is_stage_views_scaled ()) { return (meta_logical_monitor_get_scale (logical_monitor) * meta_cursor_sprite_get_texture_scale (cursor_sprite)); } else { return 1.0; } } typedef struct { MetaCursorRendererNative *in_cursor_renderer_native; MetaLogicalMonitor *in_logical_monitor; graphene_rect_t in_local_cursor_rect; MetaCursorSprite *in_cursor_sprite; MetaKmsUpdate *in_kms_update; gboolean out_painted; } UpdateCrtcCursorData; static gboolean update_monitor_crtc_cursor (MetaMonitor *monitor, MetaMonitorMode *monitor_mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { UpdateCrtcCursorData *data = user_data; MetaCursorRendererNative *cursor_renderer_native = data->in_cursor_renderer_native; MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); MetaCrtc *crtc; MetaMonitorTransform transform; graphene_rect_t scaled_crtc_rect; float scale; int crtc_x, crtc_y; int crtc_width, crtc_height; if (meta_is_stage_views_scaled ()) scale = meta_logical_monitor_get_scale (data->in_logical_monitor); else scale = 1.0; transform = meta_logical_monitor_get_transform (data->in_logical_monitor); transform = meta_monitor_logical_to_crtc_transform (monitor, transform); meta_monitor_calculate_crtc_pos (monitor, monitor_mode, monitor_crtc_mode->output, transform, &crtc_x, &crtc_y); if (meta_monitor_transform_is_rotated (transform)) { crtc_width = monitor_crtc_mode->crtc_mode->height; crtc_height = monitor_crtc_mode->crtc_mode->width; } else { crtc_width = monitor_crtc_mode->crtc_mode->width; crtc_height = monitor_crtc_mode->crtc_mode->height; } scaled_crtc_rect = (graphene_rect_t) { .origin = { .x = crtc_x / scale, .y = crtc_y / scale }, .size = { .width = crtc_width / scale, .height = crtc_height / scale }, }; crtc = meta_output_get_assigned_crtc (monitor_crtc_mode->output); if (priv->has_hw_cursor && graphene_rect_intersection (&scaled_crtc_rect, &data->in_local_cursor_rect, NULL)) { MetaMonitorTransform inverted_transform; MetaRectangle cursor_rect; CoglTexture *texture; float crtc_cursor_x, crtc_cursor_y; float cursor_crtc_scale; int tex_width, tex_height; crtc_cursor_x = (data->in_local_cursor_rect.origin.x - scaled_crtc_rect.origin.x) * scale; crtc_cursor_y = (data->in_local_cursor_rect.origin.y - scaled_crtc_rect.origin.y) * scale; texture = meta_cursor_sprite_get_cogl_texture (data->in_cursor_sprite); tex_width = cogl_texture_get_width (texture); tex_height = cogl_texture_get_height (texture); cursor_crtc_scale = calculate_cursor_crtc_sprite_scale (data->in_cursor_sprite, data->in_logical_monitor); cursor_rect = (MetaRectangle) { .x = floorf (crtc_cursor_x), .y = floorf (crtc_cursor_y), .width = roundf (tex_width * cursor_crtc_scale), .height = roundf (tex_height * cursor_crtc_scale) }; inverted_transform = meta_monitor_transform_invert (transform); meta_rectangle_transform (&cursor_rect, inverted_transform, monitor_crtc_mode->crtc_mode->width, monitor_crtc_mode->crtc_mode->height, &cursor_rect); set_crtc_cursor (data->in_cursor_renderer_native, data->in_kms_update, crtc, cursor_rect.x, cursor_rect.y, data->in_cursor_sprite); data->out_painted = data->out_painted || TRUE; } else { unset_crtc_cursor (data->in_cursor_renderer_native, data->in_kms_update, crtc); } return TRUE; } static void disable_hw_cursor_for_crtc (MetaKmsCrtc *kms_crtc, const GError *error) { MetaCrtc *crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); MetaGpuKms *gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); g_warning ("Failed to set hardware cursor (%s), " "using OpenGL from now on", error->message); cursor_renderer_gpu_data->hw_cursor_broken = TRUE; } static void update_hw_cursor (MetaCursorRendererNative *native, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native); MetaBackend *backend = priv->backend; MetaBackendNative *backend_native = META_BACKEND_NATIVE (priv->backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaKmsUpdate *kms_update; GList *logical_monitors; GList *l; graphene_rect_t rect; gboolean painted = FALSE; g_autoptr (MetaKmsFeedback) feedback = NULL; kms_update = meta_kms_ensure_pending_update (kms); if (cursor_sprite) rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); else rect = GRAPHENE_RECT_INIT_ZERO; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; UpdateCrtcCursorData data; GList *monitors; GList *k; data = (UpdateCrtcCursorData) { .in_cursor_renderer_native = native, .in_logical_monitor = logical_monitor, .in_local_cursor_rect = (graphene_rect_t) { .origin = { .x = rect.origin.x - logical_monitor->rect.x, .y = rect.origin.y - logical_monitor->rect.y }, .size = rect.size }, .in_cursor_sprite = cursor_sprite, .in_kms_update = kms_update, }; monitors = meta_logical_monitor_get_monitors (logical_monitor); for (k = monitors; k; k = k->next) { MetaMonitor *monitor = k->data; MetaMonitorMode *monitor_mode; monitor_mode = meta_monitor_get_current_mode (monitor); meta_monitor_mode_foreach_crtc (monitor, monitor_mode, update_monitor_crtc_cursor, &data, NULL); } painted = painted || data.out_painted; } feedback = meta_kms_post_pending_update_sync (kms); if (meta_kms_feedback_get_result (feedback) != META_KMS_FEEDBACK_PASSED) { for (l = meta_kms_feedback_get_failed_planes (feedback); l; l = l->next) { MetaKmsPlaneFeedback *plane_feedback = l->data; if (!g_error_matches (plane_feedback->error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) { disable_hw_cursor_for_crtc (plane_feedback->crtc, plane_feedback->error); } } priv->has_hw_cursor = FALSE; } priv->hw_state_invalidated = FALSE; if (painted) meta_cursor_renderer_emit_painted (renderer, cursor_sprite); } static gboolean has_valid_cursor_sprite_gbm_bo (MetaCursorSprite *cursor_sprite, MetaGpuKms *gpu_kms) { MetaCursorNativePrivate *cursor_priv; MetaCursorNativeGpuState *cursor_gpu_state; cursor_priv = get_cursor_priv (cursor_sprite); if (!cursor_priv) return FALSE; cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); if (!cursor_gpu_state) return FALSE; switch (cursor_gpu_state->pending_bo_state) { case META_CURSOR_GBM_BO_STATE_NONE: return get_active_cursor_sprite_gbm_bo (cursor_gpu_state) != NULL; case META_CURSOR_GBM_BO_STATE_SET: return TRUE; case META_CURSOR_GBM_BO_STATE_INVALIDATED: return FALSE; } g_assert_not_reached (); return FALSE; } static void set_can_preprocess (MetaCursorSprite *cursor_sprite, float scale, MetaMonitorTransform transform) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); cursor_priv->preprocess_state.current_relative_scale = scale; cursor_priv->preprocess_state.current_relative_transform = transform; cursor_priv->preprocess_state.can_preprocess = TRUE; invalidate_cursor_gpu_state (cursor_sprite); } static void unset_can_preprocess (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); memset (&cursor_priv->preprocess_state, 0, sizeof (cursor_priv->preprocess_state)); cursor_priv->preprocess_state.can_preprocess = FALSE; invalidate_cursor_gpu_state (cursor_sprite); } static gboolean get_can_preprocess (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); return cursor_priv->preprocess_state.can_preprocess; } static float get_current_relative_scale (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); return cursor_priv->preprocess_state.current_relative_scale; } static MetaMonitorTransform get_current_relative_transform (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); return cursor_priv->preprocess_state.current_relative_transform; } static void has_cursor_plane (MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor, MetaOutput *output, MetaCrtc *crtc, gpointer user_data) { gboolean *has_cursor_planes = user_data; MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); MetaKmsDevice *kms_device = meta_kms_crtc_get_device (kms_crtc); *has_cursor_planes &= !!meta_kms_device_get_cursor_plane_for (kms_device, kms_crtc); } static gboolean crtcs_has_cursor_planes (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNative *cursor_renderer_native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); MetaBackend *backend = priv->backend; MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors; GList *l; graphene_rect_t cursor_rect; cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaRectangle logical_monitor_layout; graphene_rect_t logical_monitor_rect; gboolean has_cursor_planes; logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor_layout); if (!graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; has_cursor_planes = TRUE; meta_logical_monitor_foreach_crtc (logical_monitor, has_cursor_plane, &has_cursor_planes); if (!has_cursor_planes) return FALSE; } return TRUE; } static gboolean get_common_crtc_sprite_scale_for_logical_monitors (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, float *out_scale) { MetaCursorRendererNative *cursor_renderer_native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); MetaBackend *backend = priv->backend; MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); graphene_rect_t cursor_rect; float scale = 1.0; gboolean has_visible_crtc_sprite = FALSE; GList *logical_monitors; GList *l; cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; graphene_rect_t logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor->rect); float tmp_scale; if (!graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; tmp_scale = calculate_cursor_crtc_sprite_scale (cursor_sprite, logical_monitor); if (has_visible_crtc_sprite && scale != tmp_scale) return FALSE; has_visible_crtc_sprite = TRUE; scale = tmp_scale; } if (!has_visible_crtc_sprite) return FALSE; *out_scale = scale; return TRUE; } static gboolean get_common_crtc_sprite_transform_for_logical_monitors (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, MetaMonitorTransform *out_transform) { MetaCursorRendererNative *cursor_renderer_native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); MetaBackend *backend = priv->backend; MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); graphene_rect_t cursor_rect; MetaMonitorTransform transform = META_MONITOR_TRANSFORM_NORMAL; gboolean has_visible_crtc_sprite = FALSE; GList *logical_monitors; GList *l; cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; graphene_rect_t logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor->rect); MetaMonitorTransform logical_transform, tmp_transform; GList *monitors, *l_mon; if (!graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; logical_transform = meta_logical_monitor_get_transform (logical_monitor); monitors = meta_logical_monitor_get_monitors (logical_monitor); for (l_mon = monitors; l_mon; l_mon = l_mon->next) { MetaMonitor *monitor = l_mon->data; tmp_transform = meta_monitor_transform_relative_transform ( meta_cursor_sprite_get_texture_transform (cursor_sprite), meta_monitor_logical_to_crtc_transform (monitor, logical_transform)); if (has_visible_crtc_sprite && transform != tmp_transform) return FALSE; has_visible_crtc_sprite = TRUE; transform = tmp_transform; } } if (!has_visible_crtc_sprite) return FALSE; *out_transform = transform; return TRUE; } static gboolean should_have_hw_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, GList *gpus) { CoglTexture *texture; MetaMonitorTransform transform; float scale; GList *l; if (!cursor_sprite) return FALSE; if (meta_cursor_renderer_is_hw_cursors_inhibited (renderer, cursor_sprite)) return FALSE; for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = l->data; MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); if (!cursor_renderer_gpu_data) return FALSE; if (cursor_renderer_gpu_data->hw_cursor_broken) return FALSE; if (!has_valid_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms)) return FALSE; } if (!crtcs_has_cursor_planes (renderer, cursor_sprite)) return FALSE; texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!texture) return FALSE; if (!get_common_crtc_sprite_scale_for_logical_monitors (renderer, cursor_sprite, &scale)) return FALSE; if (!get_common_crtc_sprite_transform_for_logical_monitors (renderer, cursor_sprite, &transform)) return FALSE; if (G_APPROX_VALUE (scale, 1.f, FLT_EPSILON) && transform == META_MONITOR_TRANSFORM_NORMAL) return TRUE; else return get_can_preprocess (cursor_sprite); return TRUE; } static gboolean meta_cursor_renderer_native_update_animation (MetaCursorRendererNative *native) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native); MetaCursorSprite *cursor_sprite = meta_cursor_renderer_get_cursor (renderer); priv->animation_timeout_id = 0; meta_cursor_sprite_tick_frame (cursor_sprite); meta_cursor_renderer_force_update (renderer); return G_SOURCE_REMOVE; } static void maybe_schedule_cursor_sprite_animation_frame (MetaCursorRendererNative *native, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); gboolean cursor_change; guint delay; cursor_change = cursor_sprite != priv->last_cursor; priv->last_cursor = cursor_sprite; if (!cursor_change && priv->animation_timeout_id) return; g_clear_handle_id (&priv->animation_timeout_id, g_source_remove); if (cursor_sprite && meta_cursor_sprite_is_animated (cursor_sprite)) { delay = meta_cursor_sprite_get_current_frame_time (cursor_sprite); if (delay == 0) return; priv->animation_timeout_id = g_timeout_add (delay, (GSourceFunc) meta_cursor_renderer_native_update_animation, native); g_source_set_name_by_id (priv->animation_timeout_id, "[mutter] meta_cursor_renderer_native_update_animation"); } } static GList * calculate_cursor_sprite_gpus (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); MetaBackend *backend = priv->backend; MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *gpus = NULL; GList *logical_monitors; GList *l; graphene_rect_t cursor_rect; cursor_rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaRectangle logical_monitor_layout; graphene_rect_t logical_monitor_rect; GList *monitors, *l_mon; logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor_layout); if (!graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL)) continue; monitors = meta_logical_monitor_get_monitors (logical_monitor); for (l_mon = monitors; l_mon; l_mon = l_mon->next) { MetaMonitor *monitor = l_mon->data; MetaGpu *gpu; gpu = meta_monitor_get_gpu (monitor); if (!g_list_find (gpus, gpu)) gpus = g_list_prepend (gpus, gpu); } } return gpus; } static gboolean meta_cursor_renderer_native_update_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); g_autoptr (GList) gpus = NULL; if (cursor_sprite) { meta_cursor_sprite_realize_texture (cursor_sprite); gpus = calculate_cursor_sprite_gpus (renderer, cursor_sprite); realize_cursor_sprite (renderer, cursor_sprite, gpus); } maybe_schedule_cursor_sprite_animation_frame (native, cursor_sprite); priv->has_hw_cursor = should_have_hw_cursor (renderer, cursor_sprite, gpus); update_hw_cursor (native, cursor_sprite); return (priv->has_hw_cursor || !cursor_sprite || !meta_cursor_sprite_get_cogl_texture (cursor_sprite)); } static void unset_crtc_cursor_renderer_privates (MetaGpu *gpu, struct gbm_bo *bo) { GList *l; for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if (bo == crtc->cursor_renderer_private) crtc->cursor_renderer_private = NULL; } } static void cursor_gpu_state_free (MetaCursorNativeGpuState *cursor_gpu_state) { int i; struct gbm_bo *active_bo; active_bo = get_active_cursor_sprite_gbm_bo (cursor_gpu_state); if (active_bo) unset_crtc_cursor_renderer_privates (cursor_gpu_state->gpu, active_bo); for (i = 0; i < HW_CURSOR_BUFFER_COUNT; i++) g_clear_pointer (&cursor_gpu_state->bos[i], gbm_bo_destroy); g_free (cursor_gpu_state); } static MetaCursorNativeGpuState * get_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv, MetaGpuKms *gpu_kms) { return g_hash_table_lookup (cursor_priv->gpu_states, gpu_kms); } static MetaCursorNativeGpuState * ensure_cursor_gpu_state (MetaCursorNativePrivate *cursor_priv, MetaGpuKms *gpu_kms) { MetaCursorNativeGpuState *cursor_gpu_state; cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); if (cursor_gpu_state) return cursor_gpu_state; cursor_gpu_state = g_new0 (MetaCursorNativeGpuState, 1); cursor_gpu_state->gpu = META_GPU (gpu_kms); g_hash_table_insert (cursor_priv->gpu_states, gpu_kms, cursor_gpu_state); return cursor_gpu_state; } static void invalidate_cursor_gpu_state (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv = get_cursor_priv (cursor_sprite); GHashTableIter iter; MetaCursorNativeGpuState *cursor_gpu_state; g_hash_table_iter_init (&iter, cursor_priv->gpu_states); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &cursor_gpu_state)) { guint pending_bo; pending_bo = get_pending_cursor_sprite_gbm_bo_index (cursor_gpu_state); g_clear_pointer (&cursor_gpu_state->bos[pending_bo], gbm_bo_destroy); cursor_gpu_state->pending_bo_state = META_CURSOR_GBM_BO_STATE_INVALIDATED; } } static void on_cursor_sprite_texture_changed (MetaCursorSprite *cursor_sprite) { invalidate_cursor_gpu_state (cursor_sprite); } static void cursor_priv_free (MetaCursorNativePrivate *cursor_priv) { g_hash_table_destroy (cursor_priv->gpu_states); g_free (cursor_priv); } static MetaCursorNativePrivate * get_cursor_priv (MetaCursorSprite *cursor_sprite) { return g_object_get_qdata (G_OBJECT (cursor_sprite), quark_cursor_sprite); } static MetaCursorNativePrivate * ensure_cursor_priv (MetaCursorSprite *cursor_sprite) { MetaCursorNativePrivate *cursor_priv; cursor_priv = get_cursor_priv (cursor_sprite); if (cursor_priv) return cursor_priv; cursor_priv = g_new0 (MetaCursorNativePrivate, 1); cursor_priv->gpu_states = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) cursor_gpu_state_free); g_object_set_qdata_full (G_OBJECT (cursor_sprite), quark_cursor_sprite, cursor_priv, (GDestroyNotify) cursor_priv_free); g_signal_connect (cursor_sprite, "texture-changed", G_CALLBACK (on_cursor_sprite_texture_changed), NULL); unset_can_preprocess (cursor_sprite); return cursor_priv; } static void load_cursor_sprite_gbm_buffer_for_gpu (MetaCursorRendererNative *native, MetaGpuKms *gpu_kms, MetaCursorSprite *cursor_sprite, uint8_t *pixels, uint width, uint height, int rowstride, uint32_t gbm_format) { uint64_t cursor_width, cursor_height; MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; struct gbm_device *gbm_device; cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); if (!cursor_renderer_gpu_data) return; cursor_width = (uint64_t) cursor_renderer_gpu_data->cursor_width; cursor_height = (uint64_t) cursor_renderer_gpu_data->cursor_height; if (width > cursor_width || height > cursor_height) { meta_warning ("Invalid theme cursor size (must be at most %ux%u)\n", (unsigned int)cursor_width, (unsigned int)cursor_height); return; } gbm_device = meta_gbm_device_from_gpu (gpu_kms); if (gbm_device_is_format_supported (gbm_device, gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE)) { struct gbm_bo *bo; uint8_t buf[4 * cursor_width * cursor_height]; uint i; bo = gbm_bo_create (gbm_device, cursor_width, cursor_height, gbm_format, GBM_BO_USE_CURSOR | GBM_BO_USE_WRITE); if (!bo) { meta_warning ("Failed to allocate HW cursor buffer\n"); return; } memset (buf, 0, sizeof(buf)); for (i = 0; i < height; i++) memcpy (buf + i * 4 * cursor_width, pixels + i * rowstride, width * 4); if (gbm_bo_write (bo, buf, cursor_width * cursor_height * 4) != 0) { meta_warning ("Failed to write cursors buffer data: %s", g_strerror (errno)); gbm_bo_destroy (bo); return; } set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo); } else { meta_warning ("HW cursor for format %d not supported\n", gbm_format); } } static gboolean is_cursor_hw_state_valid (MetaCursorSprite *cursor_sprite, MetaGpuKms *gpu_kms) { MetaCursorNativePrivate *cursor_priv; MetaCursorNativeGpuState *cursor_gpu_state; cursor_priv = get_cursor_priv (cursor_sprite); if (!cursor_priv) return FALSE; cursor_gpu_state = get_cursor_gpu_state (cursor_priv, gpu_kms); if (!cursor_gpu_state) return FALSE; switch (cursor_gpu_state->pending_bo_state) { case META_CURSOR_GBM_BO_STATE_SET: case META_CURSOR_GBM_BO_STATE_NONE: return TRUE; case META_CURSOR_GBM_BO_STATE_INVALIDATED: return FALSE; } g_assert_not_reached (); return FALSE; } static gboolean is_cursor_scale_and_transform_valid (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaMonitorTransform transform; float scale; if (!get_common_crtc_sprite_scale_for_logical_monitors (renderer, cursor_sprite, &scale)) return FALSE; if (!get_common_crtc_sprite_transform_for_logical_monitors (renderer, cursor_sprite, &transform)) return FALSE; return (scale == get_current_relative_scale (cursor_sprite) && transform == get_current_relative_transform (cursor_sprite)); } static cairo_surface_t * scale_and_transform_cursor_sprite_cpu (uint8_t *pixels, int width, int height, int rowstride, float scale, MetaMonitorTransform transform) { cairo_t *cr; cairo_surface_t *source_surface; cairo_surface_t *target_surface; int image_width; int image_height; image_width = ceilf (width * scale); image_height = ceilf (height * scale); target_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, image_width, image_height); cr = cairo_create (target_surface); if (transform != META_MONITOR_TRANSFORM_NORMAL) { cairo_translate (cr, 0.5 * image_width, 0.5 * image_height); switch (transform) { case META_MONITOR_TRANSFORM_90: cairo_rotate (cr, M_PI * 1.5); break; case META_MONITOR_TRANSFORM_180: cairo_rotate (cr, M_PI); break; case META_MONITOR_TRANSFORM_270: cairo_rotate (cr, M_PI * 0.5); break; case META_MONITOR_TRANSFORM_FLIPPED: cairo_scale (cr, 1, -1); break; case META_MONITOR_TRANSFORM_FLIPPED_90: cairo_rotate (cr, M_PI * 1.5); cairo_scale (cr, -1, 1); break; case META_MONITOR_TRANSFORM_FLIPPED_180: cairo_rotate (cr, M_PI); cairo_scale (cr, 1, -1); break; case META_MONITOR_TRANSFORM_FLIPPED_270: cairo_rotate (cr, M_PI * 0.5); cairo_scale (cr, -1, 1); break; case META_MONITOR_TRANSFORM_NORMAL: g_assert_not_reached (); } cairo_translate (cr, -0.5 * image_width, -0.5 * image_height); } cairo_scale (cr, scale, scale); source_surface = cairo_image_surface_create_for_data (pixels, CAIRO_FORMAT_ARGB32, width, height, rowstride); cairo_set_source_surface (cr, source_surface, 0, 0); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (source_surface); return target_surface; } static void load_scaled_and_transformed_cursor_sprite (MetaCursorRendererNative *native, MetaGpuKms *gpu_kms, MetaCursorSprite *cursor_sprite, float relative_scale, MetaMonitorTransform relative_transform, uint8_t *data, int width, int height, int rowstride, uint32_t gbm_format) { if (!G_APPROX_VALUE (relative_scale, 1.f, FLT_EPSILON) || relative_transform != META_MONITOR_TRANSFORM_NORMAL) { cairo_surface_t *surface; surface = scale_and_transform_cursor_sprite_cpu (data, width, height, rowstride, relative_scale, relative_transform); load_cursor_sprite_gbm_buffer_for_gpu (native, gpu_kms, cursor_sprite, cairo_image_surface_get_data (surface), cairo_image_surface_get_width (surface), cairo_image_surface_get_width (surface), cairo_image_surface_get_stride (surface), gbm_format); cairo_surface_destroy (surface); } else { load_cursor_sprite_gbm_buffer_for_gpu (native, gpu_kms, cursor_sprite, data, width, height, rowstride, gbm_format); } } #ifdef HAVE_WAYLAND static void realize_cursor_sprite_from_wl_buffer_for_gpu (MetaCursorRenderer *renderer, MetaGpuKms *gpu_kms, MetaCursorSpriteWayland *sprite_wayland) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_wayland); MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; uint64_t cursor_width, cursor_height; CoglTexture *texture; uint width, height; MetaWaylandBuffer *buffer; struct wl_resource *buffer_resource; struct wl_shm_buffer *shm_buffer; cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken) return; if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) && is_cursor_scale_and_transform_valid (renderer, cursor_sprite)) return; buffer = meta_cursor_sprite_wayland_get_buffer (sprite_wayland); if (!buffer) return; buffer_resource = meta_wayland_buffer_get_resource (buffer); if (!buffer_resource) return; ensure_cursor_priv (cursor_sprite); shm_buffer = wl_shm_buffer_get (buffer_resource); if (shm_buffer) { int rowstride = wl_shm_buffer_get_stride (shm_buffer); uint8_t *buffer_data; float relative_scale; MetaMonitorTransform relative_transform; uint32_t gbm_format; if (!get_common_crtc_sprite_scale_for_logical_monitors (renderer, cursor_sprite, &relative_scale)) { unset_can_preprocess (cursor_sprite); return; } if (!get_common_crtc_sprite_transform_for_logical_monitors (renderer, cursor_sprite, &relative_transform)) { unset_can_preprocess (cursor_sprite); return; } set_can_preprocess (cursor_sprite, relative_scale, relative_transform); wl_shm_buffer_begin_access (shm_buffer); buffer_data = wl_shm_buffer_get_data (shm_buffer); width = wl_shm_buffer_get_width (shm_buffer); height = wl_shm_buffer_get_height (shm_buffer); switch (wl_shm_buffer_get_format (shm_buffer)) { case WL_SHM_FORMAT_ARGB8888: gbm_format = GBM_FORMAT_ARGB8888; break; case WL_SHM_FORMAT_XRGB8888: gbm_format = GBM_FORMAT_XRGB8888; break; default: g_warn_if_reached (); gbm_format = GBM_FORMAT_ARGB8888; } load_scaled_and_transformed_cursor_sprite (native, gpu_kms, cursor_sprite, relative_scale, relative_transform, buffer_data, width, height, rowstride, gbm_format); wl_shm_buffer_end_access (shm_buffer); } else { struct gbm_device *gbm_device; struct gbm_bo *bo; /* HW cursors have a predefined size (at least 64x64), which usually is * bigger than cursor theme size, so themed cursors must be padded with * transparent pixels to fill the overlay. This is trivial if we have CPU * access to the data, but it's not possible if the buffer is in GPU * memory (and possibly tiled too), so if we don't get the right size, we * fallback to GL. */ cursor_width = (uint64_t) cursor_renderer_gpu_data->cursor_width; cursor_height = (uint64_t) cursor_renderer_gpu_data->cursor_height; texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); if (width != cursor_width || height != cursor_height) { meta_warning ("Invalid cursor size (must be 64x64), falling back to software (GL) cursors\n"); return; } gbm_device = meta_gbm_device_from_gpu (gpu_kms); bo = gbm_bo_import (gbm_device, GBM_BO_IMPORT_WL_BUFFER, buffer, GBM_BO_USE_CURSOR); if (!bo) { meta_warning ("Importing HW cursor from wl_buffer failed\n"); return; } unset_can_preprocess (cursor_sprite); set_pending_cursor_sprite_gbm_bo (cursor_sprite, gpu_kms, bo); } } #endif static void realize_cursor_sprite_from_xcursor_for_gpu (MetaCursorRenderer *renderer, MetaGpuKms *gpu_kms, MetaCursorSpriteXcursor *sprite_xcursor) { MetaCursorRendererNative *native = META_CURSOR_RENDERER_NATIVE (renderer); MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor); XcursorImage *xc_image; float relative_scale; MetaMonitorTransform relative_transform; ensure_cursor_priv (cursor_sprite); cursor_renderer_gpu_data = meta_cursor_renderer_native_gpu_data_from_gpu (gpu_kms); if (!cursor_renderer_gpu_data || cursor_renderer_gpu_data->hw_cursor_broken) return; if (is_cursor_hw_state_valid (cursor_sprite, gpu_kms) && is_cursor_scale_and_transform_valid (renderer, cursor_sprite)) return; if (!get_common_crtc_sprite_scale_for_logical_monitors (renderer, cursor_sprite, &relative_scale)) { unset_can_preprocess (cursor_sprite); return; } if (!get_common_crtc_sprite_transform_for_logical_monitors (renderer, cursor_sprite, &relative_transform)) { unset_can_preprocess (cursor_sprite); return; } set_can_preprocess (cursor_sprite, relative_scale, relative_transform); xc_image = meta_cursor_sprite_xcursor_get_current_image (sprite_xcursor); load_scaled_and_transformed_cursor_sprite (native, gpu_kms, cursor_sprite, relative_scale, relative_transform, (uint8_t *) xc_image->pixels, xc_image->width, xc_image->height, xc_image->width * 4, GBM_FORMAT_ARGB8888); } static void realize_cursor_sprite_for_gpu (MetaCursorRenderer *renderer, MetaGpuKms *gpu_kms, MetaCursorSprite *cursor_sprite) { if (META_IS_CURSOR_SPRITE_XCURSOR (cursor_sprite)) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (cursor_sprite); realize_cursor_sprite_from_xcursor_for_gpu (renderer, gpu_kms, sprite_xcursor); } #ifdef HAVE_WAYLAND else if (META_IS_CURSOR_SPRITE_WAYLAND (cursor_sprite)) { MetaCursorSpriteWayland *sprite_wayland = META_CURSOR_SPRITE_WAYLAND (cursor_sprite); realize_cursor_sprite_from_wl_buffer_for_gpu (renderer, gpu_kms, sprite_wayland); } #endif } static void realize_cursor_sprite (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite, GList *gpus) { GList *l; for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = l->data; realize_cursor_sprite_for_gpu (renderer, gpu_kms, cursor_sprite); } } static void meta_cursor_renderer_native_class_init (MetaCursorRendererNativeClass *klass) { MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_cursor_renderer_native_finalize; renderer_class->update_cursor = meta_cursor_renderer_native_update_cursor; quark_cursor_sprite = g_quark_from_static_string ("-meta-cursor-native"); quark_cursor_renderer_native_gpu_data = g_quark_from_static_string ("-meta-cursor-renderer-native-gpu-data"); } static void force_update_hw_cursor (MetaCursorRendererNative *native) { MetaCursorRenderer *renderer = META_CURSOR_RENDERER (native); MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (native); priv->hw_state_invalidated = TRUE; meta_cursor_renderer_force_update (renderer); } static void on_monitors_changed (MetaMonitorManager *monitors, MetaCursorRendererNative *native) { force_update_hw_cursor (native); } static void init_hw_cursor_support_for_gpu (MetaGpuKms *gpu_kms) { MetaKmsDevice *kms_device = meta_gpu_kms_get_kms_device (gpu_kms); MetaCursorRendererNativeGpuData *cursor_renderer_gpu_data; struct gbm_device *gbm_device; uint64_t width, height; gbm_device = meta_gbm_device_from_gpu (gpu_kms); if (!gbm_device) return; cursor_renderer_gpu_data = meta_create_cursor_renderer_native_gpu_data (gpu_kms); if (!meta_kms_device_get_cursor_size (kms_device, &width, &height)) { width = 64; height = 64; } cursor_renderer_gpu_data->cursor_width = width; cursor_renderer_gpu_data->cursor_height = height; } static void on_gpu_added_for_cursor (MetaBackend *backend, MetaGpuKms *gpu_kms) { init_hw_cursor_support_for_gpu (gpu_kms); } static void init_hw_cursor_support (MetaCursorRendererNative *cursor_renderer_native) { MetaCursorRendererNativePrivate *priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); GList *gpus; GList *l; gpus = meta_backend_get_gpus (priv->backend); for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = l->data; init_hw_cursor_support_for_gpu (gpu_kms); } } MetaCursorRendererNative * meta_cursor_renderer_native_new (MetaBackend *backend) { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaCursorRendererNative *cursor_renderer_native; MetaCursorRendererNativePrivate *priv; cursor_renderer_native = g_object_new (META_TYPE_CURSOR_RENDERER_NATIVE, NULL); priv = meta_cursor_renderer_native_get_instance_private (cursor_renderer_native); g_signal_connect_object (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), cursor_renderer_native, 0); g_signal_connect (backend, "gpu-added", G_CALLBACK (on_gpu_added_for_cursor), NULL); priv->backend = backend; priv->hw_state_invalidated = TRUE; init_hw_cursor_support (cursor_renderer_native); return cursor_renderer_native; } static void meta_cursor_renderer_native_init (MetaCursorRendererNative *native) { } muffin-6.4.1/src/backends/native/dbus-utils.h0000664000175000017500000000204414723361714020035 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef DBUS_UTILS_H #define DBUS_UTILS_H char * get_escaped_dbus_path (const char *prefix, const char *component); #endif /* DBUS_UTILS_H */ muffin-6.4.1/src/backends/native/meta-virtual-input-device-native.h0000664000175000017500000000234514723361714024236 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #ifndef META_VIRTUAL_INPUT_DEVICE_NATIVE_H #define META_VIRTUAL_INPUT_DEVICE_NATIVE_H #include "clutter/clutter-virtual-input-device.h" #define META_TYPE_VIRTUAL_INPUT_DEVICE_NATIVE (meta_virtual_input_device_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaVirtualInputDeviceNative, meta_virtual_input_device_native, META, VIRTUAL_INPUT_DEVICE_NATIVE, ClutterVirtualInputDevice) #endif /* META_VIRTUAL_INPUT_DEVICE_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-stage-native.h0000664000175000017500000000236414723361714021262 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_STAGE_NATIVE_H #define META_STAGE_NATIVE_H #include "clutter/clutter-mutter.h" #define META_TYPE_STAGE_NATIVE (meta_stage_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaStageNative, meta_stage_native, META, STAGE_NATIVE, ClutterStageCogl) void meta_stage_native_rebuild_views (MetaStageNative *stage_native); #endif /* META_STAGE_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-monitor-manager-kms.h0000664000175000017500000000275014723361714022561 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_MANAGER_KMS_H #define META_MONITOR_MANAGER_KMS_H #include #include #include "backends/meta-monitor-manager-private.h" typedef struct _MetaGpuKms MetaGpuKms; #define META_TYPE_MONITOR_MANAGER_KMS (meta_monitor_manager_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorManagerKms, meta_monitor_manager_kms, META, MONITOR_MANAGER_KMS, MetaMonitorManager) void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms); void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms); #endif /* META_MONITOR_MANAGER_KMS_H */ muffin-6.4.1/src/backends/native/meta-seat-native.c0000664000175000017500000032354114723361714021111 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corp. * Copyright (C) 2014 Jonas Ådahl * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Damien Lespiau * Author: Jonas Ådahl */ #include "config.h" #include #include #include #include #include #include "backends/meta-cursor-tracker-private.h" #include "backends/native/meta-seat-native.h" #include "backends/native/meta-event-native.h" #include "backends/native/meta-input-device-native.h" #include "backends/native/meta-input-device-tool-native.h" #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-virtual-input-device-native.h" #include "clutter/clutter-mutter.h" #include "core/bell.h" /* * Clutter makes the assumption that two core devices have ID's 2 and 3 (core * pointer and core keyboard). * * Since the two first devices that will ever be created will be the virtual * pointer and virtual keyboard of the first seat, we fulfill the made * assumptions by having the first device having ID 2 and following 3. */ #define INITIAL_DEVICE_ID 2 /* Try to keep the pointer inside the stage. Hopefully no one is using * this backend with stages smaller than this. */ #define INITIAL_POINTER_X 16 #define INITIAL_POINTER_Y 16 #define AUTOREPEAT_VALUE 2 #define DISCRETE_SCROLL_STEP 10.0 #ifndef BTN_STYLUS3 #define BTN_STYLUS3 0x149 /* Linux 4.15 */ #endif typedef struct _MetaEventFilter MetaEventFilter; struct _MetaEventFilter { MetaEvdevFilterFunc func; gpointer data; GDestroyNotify destroy_notify; }; struct _MetaEventSource { GSource source; MetaSeatNative *seat; GPollFD event_poll_fd; }; static MetaOpenDeviceCallback device_open_callback; static MetaCloseDeviceCallback device_close_callback; static gpointer device_callback_data; #ifdef CLUTTER_ENABLE_DEBUG static const char *device_type_str[] = { "pointer", /* CLUTTER_POINTER_DEVICE */ "keyboard", /* CLUTTER_KEYBOARD_DEVICE */ "extension", /* CLUTTER_EXTENSION_DEVICE */ "joystick", /* CLUTTER_JOYSTICK_DEVICE */ "tablet", /* CLUTTER_TABLET_DEVICE */ "touchpad", /* CLUTTER_TOUCHPAD_DEVICE */ "touchscreen", /* CLUTTER_TOUCHSCREEN_DEVICE */ "pen", /* CLUTTER_PEN_DEVICE */ "eraser", /* CLUTTER_ERASER_DEVICE */ "cursor", /* CLUTTER_CURSOR_DEVICE */ "pad", /* CLUTTER_PAD_DEVICE */ }; #endif /* CLUTTER_ENABLE_DEBUG */ enum { PROP_0, PROP_SEAT_ID, N_PROPS, /* This property is overridden */ PROP_TOUCH_MODE, }; GParamSpec *props[N_PROPS] = { NULL }; G_DEFINE_TYPE (MetaSeatNative, meta_seat_native, CLUTTER_TYPE_SEAT) static void process_events (MetaSeatNative *seat); void meta_seat_native_set_libinput_seat (MetaSeatNative *seat, struct libinput_seat *libinput_seat) { g_assert (seat->libinput_seat == NULL); libinput_seat_ref (libinput_seat); libinput_seat_set_user_data (libinput_seat, seat); seat->libinput_seat = libinput_seat; } void meta_seat_native_sync_leds (MetaSeatNative *seat) { GSList *iter; MetaInputDeviceNative *device_evdev; int caps_lock, num_lock, scroll_lock; enum libinput_led leds = 0; caps_lock = xkb_state_led_index_is_active (seat->xkb, seat->caps_lock_led); num_lock = xkb_state_led_index_is_active (seat->xkb, seat->num_lock_led); scroll_lock = xkb_state_led_index_is_active (seat->xkb, seat->scroll_lock_led); if (caps_lock) leds |= LIBINPUT_LED_CAPS_LOCK; if (num_lock) leds |= LIBINPUT_LED_NUM_LOCK; if (scroll_lock) leds |= LIBINPUT_LED_SCROLL_LOCK; for (iter = seat->devices; iter; iter = iter->next) { device_evdev = iter->data; meta_input_device_native_update_leds (device_evdev, leds); } } static void clutter_touch_state_free (MetaTouchState *touch_state) { g_slice_free (MetaTouchState, touch_state); } static void ensure_seat_slot_allocated (MetaSeatNative *seat, int seat_slot) { if (seat_slot >= seat->n_alloc_touch_states) { const int size_increase = 5; int i; seat->n_alloc_touch_states += size_increase; seat->touch_states = g_realloc_n (seat->touch_states, seat->n_alloc_touch_states, sizeof (MetaTouchState *)); for (i = 0; i < size_increase; i++) seat->touch_states[seat->n_alloc_touch_states - (i + 1)] = NULL; } } MetaTouchState * meta_seat_native_acquire_touch_state (MetaSeatNative *seat, int device_slot) { MetaTouchState *touch_state; int seat_slot; for (seat_slot = 0; seat_slot < seat->n_alloc_touch_states; seat_slot++) { if (!seat->touch_states[seat_slot]) break; } ensure_seat_slot_allocated (seat, seat_slot); touch_state = g_slice_new0 (MetaTouchState); *touch_state = (MetaTouchState) { .seat = seat, .seat_slot = seat_slot, .device_slot = device_slot, }; seat->touch_states[seat_slot] = touch_state; return touch_state; } void meta_seat_native_release_touch_state (MetaSeatNative *seat, MetaTouchState *touch_state) { g_clear_pointer (&seat->touch_states[touch_state->seat_slot], clutter_touch_state_free); } void meta_seat_native_clear_repeat_timer (MetaSeatNative *seat) { if (seat->repeat_timer) { g_clear_handle_id (&seat->repeat_timer, g_source_remove); g_clear_object (&seat->repeat_device); } } static void dispatch_libinput (MetaSeatNative *seat) { libinput_dispatch (seat->libinput); process_events (seat); } static gboolean keyboard_repeat (gpointer data) { MetaSeatNative *seat = data; GSource *source; /* There might be events queued in libinput that could cancel the repeat timer. */ dispatch_libinput (seat); if (!seat->repeat_timer) return G_SOURCE_REMOVE; g_return_val_if_fail (seat->repeat_device != NULL, G_SOURCE_REMOVE); source = g_main_context_find_source_by_id (NULL, seat->repeat_timer); meta_seat_native_notify_key (seat, seat->repeat_device, g_source_get_time (source), seat->repeat_key, AUTOREPEAT_VALUE, FALSE); return G_SOURCE_CONTINUE; } static void queue_event (ClutterEvent *event) { _clutter_event_push (event, FALSE); } static int update_button_count (MetaSeatNative *seat, uint32_t button, uint32_t state) { if (state) { return ++seat->button_count[button]; } else { /* Handle cases where we newer saw the initial pressed event. */ if (seat->button_count[button] == 0) { meta_topic (META_DEBUG_INPUT, "Counting release of key 0x%x and count is already 0\n", button); return 0; } return --seat->button_count[button]; } } void meta_seat_native_notify_key (MetaSeatNative *seat, ClutterInputDevice *device, uint64_t time_us, uint32_t key, uint32_t state, gboolean update_keys) { ClutterStage *stage; ClutterEvent *event = NULL; enum xkb_state_component changed_state; if (state != AUTOREPEAT_VALUE) { /* Drop any repeated button press (for example from virtual devices. */ int count = update_button_count (seat, key, state); if ((state && count > 1) || (!state && count != 0)) { meta_topic (META_DEBUG_INPUT, "Dropping repeated %s of key 0x%x, count %d, state %d\n", state ? "press" : "release", key, count, state); return; } } /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (device); if (stage == NULL) { meta_seat_native_clear_repeat_timer (seat); return; } event = meta_key_event_new_from_evdev (device, seat->core_keyboard, stage, seat->xkb, seat->button_state, us2ms (time_us), key, state); meta_event_native_set_event_code (event, key); /* We must be careful and not pass multiple releases to xkb, otherwise it gets confused and locks the modifiers */ if (state != AUTOREPEAT_VALUE) { changed_state = xkb_state_update_key (seat->xkb, event->key.hardware_keycode, state ? XKB_KEY_DOWN : XKB_KEY_UP); } else { changed_state = 0; clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED); } queue_event (event); if (update_keys && (changed_state & XKB_STATE_LEDS)) { g_signal_emit_by_name (seat->keymap, "state-changed"); meta_seat_native_sync_leds (seat); meta_input_device_native_a11y_maybe_notify_toggle_keys (META_INPUT_DEVICE_NATIVE (seat->core_keyboard)); } if (state == 0 || /* key release */ !seat->repeat || !xkb_keymap_key_repeats (xkb_state_get_keymap (seat->xkb), event->key.hardware_keycode)) { meta_seat_native_clear_repeat_timer (seat); return; } if (state == 1) /* key press */ seat->repeat_count = 0; seat->repeat_count += 1; seat->repeat_key = key; switch (seat->repeat_count) { case 1: case 2: { uint32_t interval; meta_seat_native_clear_repeat_timer (seat); seat->repeat_device = g_object_ref (device); if (seat->repeat_count == 1) interval = seat->repeat_delay; else interval = seat->repeat_interval; seat->repeat_timer = clutter_threads_add_timeout_full (CLUTTER_PRIORITY_EVENTS, interval, keyboard_repeat, seat, NULL); return; } default: return; } } static ClutterEvent * new_absolute_motion_event (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, float x, float y, double *axes) { ClutterStage *stage = _clutter_input_device_get_stage (input_device); ClutterEvent *event; event = clutter_event_new (CLUTTER_MOTION); if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) { meta_seat_native_constrain_pointer (seat, seat->core_pointer, time_us, seat->pointer_x, seat->pointer_y, &x, &y); } meta_event_native_set_time_usec (event, time_us); event->motion.time = us2ms (time_us); event->motion.stage = stage; meta_xkb_translate_state (event, seat->xkb, seat->button_state); event->motion.x = x; event->motion.y = y; meta_input_device_native_translate_coordinates (input_device, stage, &event->motion.x, &event->motion.y); event->motion.axes = axes; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (input_device); clutter_event_set_device_tool (event, device_evdev->last_tool); clutter_event_set_device (event, input_device); } else { clutter_event_set_device (event, seat->core_pointer); } _clutter_input_device_set_stage (seat->core_pointer, stage); if (clutter_input_device_get_device_type (input_device) != CLUTTER_TABLET_DEVICE) { seat->pointer_x = x; seat->pointer_y = y; } return event; } void meta_seat_native_notify_relative_motion (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, float dx, float dy, float dx_unaccel, float dy_unaccel) { float new_x, new_y; ClutterEvent *event; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ if (!_clutter_input_device_get_stage (input_device)) return; meta_seat_native_filter_relative_motion (seat, input_device, seat->pointer_x, seat->pointer_y, &dx, &dy); new_x = seat->pointer_x + dx; new_y = seat->pointer_y + dy; event = new_absolute_motion_event (seat, input_device, time_us, new_x, new_y, NULL); meta_event_native_set_relative_motion (event, dx, dy, dx_unaccel, dy_unaccel); queue_event (event); } void meta_seat_native_notify_absolute_motion (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, float x, float y, double *axes) { ClutterEvent *event; event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); queue_event (event); } void meta_seat_native_notify_button (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, uint32_t button, uint32_t state) { MetaInputDeviceNative *device_evdev = (MetaInputDeviceNative *) input_device; ClutterStage *stage; ClutterEvent *event = NULL; int button_nr; static int maskmap[8] = { CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON2_MASK, CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK, 0, 0, 0 }; int button_count; /* Drop any repeated button press (for example from virtual devices. */ button_count = update_button_count (seat, button, state); if ((state && button_count > 1) || (!state && button_count != 0)) { meta_topic (META_DEBUG_INPUT, "Dropping repeated %s of button 0x%x, count %d\n", state ? "press" : "release", button, button_count); return; } /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; /* The evdev button numbers don't map sequentially to clutter button * numbers (the right and middle mouse buttons are in the opposite * order) so we'll map them directly with a switch statement */ switch (button) { case BTN_LEFT: case BTN_TOUCH: button_nr = CLUTTER_BUTTON_PRIMARY; break; case BTN_RIGHT: case BTN_STYLUS: button_nr = CLUTTER_BUTTON_SECONDARY; break; case BTN_MIDDLE: case BTN_STYLUS2: button_nr = CLUTTER_BUTTON_MIDDLE; break; case 0x149: /* BTN_STYLUS3 */ button_nr = 8; break; default: /* For compatibility reasons, all additional buttons go after the old 4-7 scroll ones */ if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) button_nr = button - BTN_TOOL_PEN + 4; else button_nr = button - (BTN_LEFT - 1) + 4; break; } if (button_nr < 1 || button_nr > 12) { g_warning ("Unhandled button event 0x%x", button); return; } if (state) event = clutter_event_new (CLUTTER_BUTTON_PRESS); else event = clutter_event_new (CLUTTER_BUTTON_RELEASE); if (button_nr < G_N_ELEMENTS (maskmap)) { /* Update the modifiers */ if (state) seat->button_state |= maskmap[button_nr - 1]; else seat->button_state &= ~maskmap[button_nr - 1]; } meta_event_native_set_time_usec (event, time_us); event->button.time = us2ms (time_us); event->button.stage = CLUTTER_STAGE (stage); meta_xkb_translate_state (event, seat->xkb, seat->button_state); event->button.button = button_nr; if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { graphene_point_t point; clutter_input_device_get_coords (input_device, NULL, &point); event->button.x = point.x; event->button.y = point.y; } else { event->button.x = seat->pointer_x; event->button.y = seat->pointer_y; } clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); if (device_evdev->last_tool) { /* Apply the button event code as per the tool mapping */ uint32_t mapped_button; mapped_button = meta_input_device_tool_native_get_button_code (device_evdev->last_tool, button_nr); if (mapped_button != 0) button = mapped_button; } meta_event_native_set_event_code (event, button); if (clutter_input_device_get_device_type (input_device) == CLUTTER_TABLET_DEVICE) { clutter_event_set_device_tool (event, device_evdev->last_tool); clutter_event_set_device (event, input_device); } else { clutter_event_set_device (event, seat->core_pointer); } _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); } static void notify_scroll (ClutterInputDevice *input_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags flags, gboolean emulated) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event = NULL; double scroll_factor; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_SCROLL); meta_event_native_set_time_usec (event, time_us); event->scroll.time = us2ms (time_us); event->scroll.stage = CLUTTER_STAGE (stage); meta_xkb_translate_state (event, seat->xkb, seat->button_state); /* libinput pointer axis events are in pointer motion coordinate space. * To convert to Xi2 discrete step coordinate space, multiply the factor * 1/10. */ event->scroll.direction = CLUTTER_SCROLL_SMOOTH; scroll_factor = 1.0 / DISCRETE_SCROLL_STEP; clutter_event_set_scroll_delta (event, scroll_factor * dx, scroll_factor * dy); event->scroll.x = seat->pointer_x; event->scroll.y = seat->pointer_y; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); event->scroll.scroll_source = scroll_source; event->scroll.finish_flags = flags; _clutter_event_set_pointer_emulated (event, emulated); queue_event (event); } static void notify_discrete_scroll (ClutterInputDevice *input_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source, gboolean emulated) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event = NULL; if (direction == CLUTTER_SCROLL_SMOOTH) return; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_SCROLL); meta_event_native_set_time_usec (event, time_us); event->scroll.time = us2ms (time_us); event->scroll.stage = CLUTTER_STAGE (stage); meta_xkb_translate_state (event, seat->xkb, seat->button_state); event->scroll.direction = direction; event->scroll.x = seat->pointer_x; event->scroll.y = seat->pointer_y; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); event->scroll.scroll_source = scroll_source; _clutter_event_set_pointer_emulated (event, emulated); queue_event (event); } static void check_notify_discrete_scroll (MetaSeatNative *seat, ClutterInputDevice *device, uint64_t time_us, ClutterScrollSource scroll_source) { int i, n_xscrolls, n_yscrolls; n_xscrolls = floor (fabs (seat->accum_scroll_dx) / DISCRETE_SCROLL_STEP); n_yscrolls = floor (fabs (seat->accum_scroll_dy) / DISCRETE_SCROLL_STEP); for (i = 0; i < n_xscrolls; i++) { notify_discrete_scroll (device, time_us, seat->accum_scroll_dx > 0 ? CLUTTER_SCROLL_RIGHT : CLUTTER_SCROLL_LEFT, scroll_source, TRUE); } for (i = 0; i < n_yscrolls; i++) { notify_discrete_scroll (device, time_us, seat->accum_scroll_dy > 0 ? CLUTTER_SCROLL_DOWN : CLUTTER_SCROLL_UP, scroll_source, TRUE); } seat->accum_scroll_dx = fmodf (seat->accum_scroll_dx, DISCRETE_SCROLL_STEP); seat->accum_scroll_dy = fmodf (seat->accum_scroll_dy, DISCRETE_SCROLL_STEP); } void meta_seat_native_notify_scroll_continuous (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags) { if (finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) seat->accum_scroll_dx = 0; else seat->accum_scroll_dx += dx; if (finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) seat->accum_scroll_dy = 0; else seat->accum_scroll_dy += dy; notify_scroll (input_device, time_us, dx, dy, scroll_source, finish_flags, FALSE); check_notify_discrete_scroll (seat, input_device, time_us, scroll_source); } static ClutterScrollDirection discrete_to_direction (double discrete_dx, double discrete_dy) { if (discrete_dx > 0) return CLUTTER_SCROLL_RIGHT; else if (discrete_dx < 0) return CLUTTER_SCROLL_LEFT; else if (discrete_dy > 0) return CLUTTER_SCROLL_DOWN; else if (discrete_dy < 0) return CLUTTER_SCROLL_UP; else g_assert_not_reached (); return 0; } void meta_seat_native_notify_discrete_scroll (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, double discrete_dx, double discrete_dy, ClutterScrollSource scroll_source) { notify_scroll (input_device, time_us, discrete_dx * DISCRETE_SCROLL_STEP, discrete_dy * DISCRETE_SCROLL_STEP, scroll_source, CLUTTER_SCROLL_FINISHED_NONE, TRUE); notify_discrete_scroll (input_device, time_us, discrete_to_direction (discrete_dx, discrete_dy), scroll_source, FALSE); } void meta_seat_native_notify_touch_event (MetaSeatNative *seat, ClutterInputDevice *input_device, ClutterEventType evtype, uint64_t time_us, int slot, double x, double y) { ClutterStage *stage; ClutterEvent *event = NULL; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; event = clutter_event_new (evtype); meta_event_native_set_time_usec (event, time_us); event->touch.time = us2ms (time_us); event->touch.stage = CLUTTER_STAGE (stage); event->touch.x = x; event->touch.y = y; meta_input_device_native_translate_coordinates (input_device, stage, &event->touch.x, &event->touch.y); /* "NULL" sequences are special cased in clutter */ event->touch.sequence = GINT_TO_POINTER (MAX (1, slot + 1)); meta_xkb_translate_state (event, seat->xkb, seat->button_state); if (evtype == CLUTTER_TOUCH_BEGIN || evtype == CLUTTER_TOUCH_UPDATE) event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); queue_event (event); } /* * MetaEventSource for reading input devices */ static gboolean meta_event_prepare (GSource *source, gint *timeout) { gboolean retval; _clutter_threads_acquire_lock (); *timeout = -1; retval = clutter_events_pending (); _clutter_threads_release_lock (); return retval; } static gboolean meta_event_check (GSource *source) { MetaEventSource *event_source = (MetaEventSource *) source; gboolean retval; _clutter_threads_acquire_lock (); retval = ((event_source->event_poll_fd.revents & G_IO_IN) || clutter_events_pending ()); _clutter_threads_release_lock (); return retval; } void meta_seat_native_constrain_pointer (MetaSeatNative *seat, ClutterInputDevice *core_pointer, uint64_t time_us, float x, float y, float *new_x, float *new_y) { if (seat->constrain_callback) { seat->constrain_callback (core_pointer, us2ms (time_us), x, y, new_x, new_y, seat->constrain_data); } else { ClutterActor *stage = CLUTTER_ACTOR (meta_seat_native_get_stage (seat)); float stage_width = clutter_actor_get_width (stage); float stage_height = clutter_actor_get_height (stage); *new_x = CLAMP (*new_x, 0.f, stage_width - 1); *new_y = CLAMP (*new_y, 0.f, stage_height - 1); } } void meta_seat_native_filter_relative_motion (MetaSeatNative *seat, ClutterInputDevice *device, float x, float y, float *dx, float *dy) { if (!seat->relative_motion_filter) return; seat->relative_motion_filter (device, x, y, dx, dy, seat->relative_motion_filter_user_data); } static void notify_absolute_motion (ClutterInputDevice *input_device, uint64_t time_us, float x, float y, double *axes) { MetaSeatNative *seat; ClutterEvent *event; seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (input_device)); event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); queue_event (event); } static void notify_relative_tool_motion (ClutterInputDevice *input_device, uint64_t time_us, float dx, float dy, double *axes) { MetaInputDeviceNative *device_evdev; ClutterEvent *event; MetaSeatNative *seat; gfloat x, y; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); x = input_device->current_x + dx; y = input_device->current_y + dy; meta_seat_native_filter_relative_motion (seat, input_device, seat->pointer_x, seat->pointer_y, &dx, &dy); event = new_absolute_motion_event (seat, input_device, time_us, x, y, axes); meta_event_native_set_relative_motion (event, dx, dy, 0, 0); queue_event (event); } static void notify_pinch_gesture_event (ClutterInputDevice *input_device, ClutterTouchpadGesturePhase phase, uint64_t time_us, double dx, double dy, double angle_delta, double scale, uint32_t n_fingers) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event = NULL; graphene_point_t pos; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_TOUCHPAD_PINCH); clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); meta_event_native_set_time_usec (event, time_us); event->touchpad_pinch.phase = phase; event->touchpad_pinch.time = us2ms (time_us); event->touchpad_pinch.stage = CLUTTER_STAGE (stage); event->touchpad_pinch.x = pos.x; event->touchpad_pinch.y = pos.y; event->touchpad_pinch.dx = dx; event->touchpad_pinch.dy = dy; event->touchpad_pinch.angle_delta = angle_delta; event->touchpad_pinch.scale = scale; event->touchpad_pinch.n_fingers = n_fingers; meta_xkb_translate_state (event, seat->xkb, seat->button_state); clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); queue_event (event); } static void notify_swipe_gesture_event (ClutterInputDevice *input_device, ClutterTouchpadGesturePhase phase, uint64_t time_us, uint32_t n_fingers, double dx, double dy) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event = NULL; graphene_point_t pos; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_TOUCHPAD_SWIPE); meta_event_native_set_time_usec (event, time_us); event->touchpad_swipe.phase = phase; event->touchpad_swipe.time = us2ms (time_us); event->touchpad_swipe.stage = CLUTTER_STAGE (stage); clutter_input_device_get_coords (seat->core_pointer, NULL, &pos); event->touchpad_swipe.x = pos.x; event->touchpad_swipe.y = pos.y; event->touchpad_swipe.dx = dx; event->touchpad_swipe.dy = dy; event->touchpad_swipe.n_fingers = n_fingers; meta_xkb_translate_state (event, seat->xkb, seat->button_state); clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); queue_event (event); } static void notify_proximity (ClutterInputDevice *input_device, uint64_t time_us, gboolean in) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event = NULL; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); if (in) event = clutter_event_new (CLUTTER_PROXIMITY_IN); else event = clutter_event_new (CLUTTER_PROXIMITY_OUT); meta_event_native_set_time_usec (event, time_us); event->proximity.time = us2ms (time_us); event->proximity.stage = CLUTTER_STAGE (stage); clutter_event_set_device_tool (event, device_evdev->last_tool); clutter_event_set_device (event, seat->core_pointer); clutter_event_set_source_device (event, input_device); _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); } static void notify_pad_button (ClutterInputDevice *input_device, uint64_t time_us, uint32_t button, uint32_t mode_group, uint32_t mode, uint32_t pressed) { MetaInputDeviceNative *device_evdev; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; if (pressed) event = clutter_event_new (CLUTTER_PAD_BUTTON_PRESS); else event = clutter_event_new (CLUTTER_PAD_BUTTON_RELEASE); device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); meta_event_native_set_time_usec (event, time_us); event->pad_button.stage = stage; event->pad_button.button = button; event->pad_button.group = mode_group; event->pad_button.mode = mode; clutter_event_set_device (event, input_device); clutter_event_set_source_device (event, input_device); clutter_event_set_time (event, us2ms (time_us)); _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); } static void notify_pad_strip (ClutterInputDevice *input_device, uint64_t time_us, uint32_t strip_number, uint32_t strip_source, uint32_t mode_group, uint32_t mode, double value) { MetaInputDeviceNative *device_evdev; ClutterInputDevicePadSource source; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; if (strip_source == LIBINPUT_TABLET_PAD_STRIP_SOURCE_FINGER) source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; else source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_PAD_STRIP); meta_event_native_set_time_usec (event, time_us); event->pad_strip.strip_source = source; event->pad_strip.stage = stage; event->pad_strip.strip_number = strip_number; event->pad_strip.value = value; event->pad_strip.group = mode_group; event->pad_strip.mode = mode; clutter_event_set_device (event, input_device); clutter_event_set_source_device (event, input_device); clutter_event_set_time (event, us2ms (time_us)); _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); } static void notify_pad_ring (ClutterInputDevice *input_device, uint64_t time_us, uint32_t ring_number, uint32_t ring_source, uint32_t mode_group, uint32_t mode, double angle) { MetaInputDeviceNative *device_evdev; ClutterInputDevicePadSource source; MetaSeatNative *seat; ClutterStage *stage; ClutterEvent *event; /* We can drop the event on the floor if no stage has been * associated with the device yet. */ stage = _clutter_input_device_get_stage (input_device); if (stage == NULL) return; if (ring_source == LIBINPUT_TABLET_PAD_RING_SOURCE_FINGER) source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER; else source = CLUTTER_INPUT_DEVICE_PAD_SOURCE_UNKNOWN; device_evdev = META_INPUT_DEVICE_NATIVE (input_device); seat = meta_input_device_native_get_seat (device_evdev); event = clutter_event_new (CLUTTER_PAD_RING); meta_event_native_set_time_usec (event, time_us); event->pad_ring.ring_source = source; event->pad_ring.stage = stage; event->pad_ring.ring_number = ring_number; event->pad_ring.angle = angle; event->pad_ring.group = mode_group; event->pad_ring.mode = mode; clutter_event_set_device (event, input_device); clutter_event_set_source_device (event, input_device); clutter_event_set_time (event, us2ms (time_us)); _clutter_input_device_set_stage (seat->core_pointer, stage); queue_event (event); } static gboolean meta_event_dispatch (GSource *g_source, GSourceFunc callback, gpointer user_data) { MetaEventSource *source = (MetaEventSource *) g_source; MetaSeatNative *seat; ClutterEvent *event; _clutter_threads_acquire_lock (); seat = source->seat; /* Don't queue more events if we haven't finished handling the previous batch */ if (clutter_events_pending ()) goto queue_event; dispatch_libinput (seat); queue_event: event = clutter_event_get (); if (event) { ClutterModifierType event_state; ClutterInputDevice *input_device = clutter_event_get_source_device (event); MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (input_device); MetaSeatNative *seat = meta_input_device_native_get_seat (device_evdev); /* Drop events if we don't have any stage to forward them to */ if (!_clutter_input_device_get_stage (input_device)) goto out; /* update the device states *before* the event */ event_state = seat->button_state | xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_EFFECTIVE); _clutter_input_device_set_state (seat->core_pointer, event_state); _clutter_input_device_set_state (seat->core_keyboard, event_state); /* forward the event into clutter for emission etc. */ _clutter_stage_queue_event (event->any.stage, event, FALSE); } out: _clutter_threads_release_lock (); return TRUE; } static GSourceFuncs event_funcs = { meta_event_prepare, meta_event_check, meta_event_dispatch, NULL }; static MetaEventSource * meta_event_source_new (MetaSeatNative *seat) { GSource *source; MetaEventSource *event_source; gint fd; source = g_source_new (&event_funcs, sizeof (MetaEventSource)); event_source = (MetaEventSource *) source; /* setup the source */ event_source->seat = seat; fd = libinput_get_fd (seat->libinput); event_source->event_poll_fd.fd = fd; event_source->event_poll_fd.events = G_IO_IN; /* and finally configure and attach the GSource */ g_source_set_priority (source, CLUTTER_PRIORITY_EVENTS); g_source_add_poll (source, &event_source->event_poll_fd); g_source_set_can_recurse (source, TRUE); g_source_attach (source, NULL); return event_source; } static void meta_event_source_free (MetaEventSource *source) { GSource *g_source = (GSource *) source; /* ignore the return value of close, it's not like we can do something * about it */ close (source->event_poll_fd.fd); g_source_destroy (g_source); g_source_unref (g_source); } static gboolean has_touchscreen (MetaSeatNative *seat) { GSList *l; for (l = seat->devices; l; l = l->next) { ClutterInputDeviceType device_type; device_type = clutter_input_device_get_device_type (l->data); if (device_type == CLUTTER_TOUCHSCREEN_DEVICE) return TRUE; } return FALSE; } static void update_touch_mode (MetaSeatNative *seat) { gboolean touch_mode; /* No touch mode if we don't have a touchscreen, easy */ if (!seat->has_touchscreen) touch_mode = FALSE; /* If we have a tablet mode switch, honor it being unset */ else if (seat->has_tablet_switch && !seat->tablet_mode_switch_state) touch_mode = FALSE; /* If tablet mode is enabled, or if there is no tablet mode switch * (eg. kiosk machines), assume touch-mode. */ else touch_mode = TRUE; if (seat->touch_mode != touch_mode) { seat->touch_mode = touch_mode; g_object_notify (G_OBJECT (seat), "touch-mode"); } } static ClutterInputDevice * evdev_add_device (MetaSeatNative *seat, struct libinput_device *libinput_device) { ClutterInputDeviceType type; ClutterInputDevice *device, *master = NULL; ClutterActor *stage; device = meta_input_device_native_new (seat, libinput_device); stage = CLUTTER_ACTOR (meta_seat_native_get_stage (seat)); _clutter_input_device_set_stage (device, CLUTTER_STAGE (stage)); seat->devices = g_slist_prepend (seat->devices, device); /* Clutter assumes that device types are exclusive in the * ClutterInputDevice API */ type = meta_input_device_native_determine_type (libinput_device); if (type == CLUTTER_KEYBOARD_DEVICE) master = seat->core_keyboard; else if (type == CLUTTER_POINTER_DEVICE) master = seat->core_pointer; if (master) { _clutter_input_device_set_associated_device (device, master); _clutter_input_device_add_slave (master, device); } return device; } static void evdev_remove_device (MetaSeatNative *seat, MetaInputDeviceNative *device_evdev) { ClutterInputDevice *device; device = CLUTTER_INPUT_DEVICE (device_evdev); seat->devices = g_slist_remove (seat->devices, device); g_object_unref (device); } static gboolean meta_seat_native_handle_device_event (ClutterSeat *seat, ClutterEvent *event) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); ClutterInputDevice *device = event->device.device; MetaInputDeviceNative *device_native = META_INPUT_DEVICE_NATIVE (device); gboolean check_touch_mode; check_touch_mode = clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE; switch (event->type) { case CLUTTER_DEVICE_ADDED: seat_native->has_touchscreen = check_touch_mode; if (libinput_device_has_capability (device_native->libinput_device, LIBINPUT_DEVICE_CAP_SWITCH) && libinput_device_switch_has_switch (device_native->libinput_device, LIBINPUT_SWITCH_TABLET_MODE)) { seat_native->has_tablet_switch = TRUE; check_touch_mode = TRUE; } break; case CLUTTER_DEVICE_REMOVED: if (check_touch_mode) seat_native->has_touchscreen = has_touchscreen (seat_native); if (seat_native->repeat_timer && seat_native->repeat_device == device) meta_seat_native_clear_repeat_timer (seat_native); break; default: break; } if (check_touch_mode) update_touch_mode (seat_native); return TRUE; } static gboolean process_base_event (MetaSeatNative *seat, struct libinput_event *event) { ClutterInputDevice *device = NULL; ClutterEvent *device_event; struct libinput_device *libinput_device; switch (libinput_event_get_type (event)) { case LIBINPUT_EVENT_DEVICE_ADDED: libinput_device = libinput_event_get_device (event); device = evdev_add_device (seat, libinput_device); device_event = clutter_event_new (CLUTTER_DEVICE_ADDED); clutter_event_set_device (device_event, device); break; case LIBINPUT_EVENT_DEVICE_REMOVED: libinput_device = libinput_event_get_device (event); device = libinput_device_get_user_data (libinput_device); device_event = clutter_event_new (CLUTTER_DEVICE_REMOVED); clutter_event_set_device (device_event, device); evdev_remove_device (seat, META_INPUT_DEVICE_NATIVE (device)); break; default: device_event = NULL; } if (device_event) { device_event->device.stage = _clutter_input_device_get_stage (device); queue_event (device_event); return TRUE; } return FALSE; } static ClutterScrollSource translate_scroll_source (enum libinput_pointer_axis_source source) { switch (source) { case LIBINPUT_POINTER_AXIS_SOURCE_WHEEL: return CLUTTER_SCROLL_SOURCE_WHEEL; case LIBINPUT_POINTER_AXIS_SOURCE_FINGER: return CLUTTER_SCROLL_SOURCE_FINGER; case LIBINPUT_POINTER_AXIS_SOURCE_CONTINUOUS: return CLUTTER_SCROLL_SOURCE_CONTINUOUS; default: return CLUTTER_SCROLL_SOURCE_UNKNOWN; } } static ClutterInputDeviceToolType translate_tool_type (struct libinput_tablet_tool *libinput_tool) { enum libinput_tablet_tool_type tool; tool = libinput_tablet_tool_get_type (libinput_tool); switch (tool) { case LIBINPUT_TABLET_TOOL_TYPE_PEN: return CLUTTER_INPUT_DEVICE_TOOL_PEN; case LIBINPUT_TABLET_TOOL_TYPE_ERASER: return CLUTTER_INPUT_DEVICE_TOOL_ERASER; case LIBINPUT_TABLET_TOOL_TYPE_BRUSH: return CLUTTER_INPUT_DEVICE_TOOL_BRUSH; case LIBINPUT_TABLET_TOOL_TYPE_PENCIL: return CLUTTER_INPUT_DEVICE_TOOL_PENCIL; case LIBINPUT_TABLET_TOOL_TYPE_AIRBRUSH: return CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH; case LIBINPUT_TABLET_TOOL_TYPE_MOUSE: return CLUTTER_INPUT_DEVICE_TOOL_MOUSE; case LIBINPUT_TABLET_TOOL_TYPE_LENS: return CLUTTER_INPUT_DEVICE_TOOL_LENS; default: return CLUTTER_INPUT_DEVICE_TOOL_NONE; } } static void input_device_update_tool (ClutterInputDevice *input_device, struct libinput_tablet_tool *libinput_tool) { MetaInputDeviceNative *evdev_device = META_INPUT_DEVICE_NATIVE (input_device); MetaSeatNative *seat = meta_input_device_native_get_seat (evdev_device); ClutterInputDeviceTool *tool = NULL; ClutterInputDeviceToolType tool_type; uint64_t tool_serial; if (libinput_tool) { tool_serial = libinput_tablet_tool_get_serial (libinput_tool); tool_type = translate_tool_type (libinput_tool); tool = clutter_input_device_lookup_tool (input_device, tool_serial, tool_type); if (!tool) { tool = meta_input_device_tool_native_new (libinput_tool, tool_serial, tool_type); clutter_input_device_add_tool (input_device, tool); } } if (evdev_device->last_tool != tool) { evdev_device->last_tool = tool; g_signal_emit_by_name (seat, "tool-changed", input_device, tool); } } static gdouble * translate_tablet_axes (struct libinput_event_tablet_tool *tablet_event, ClutterInputDeviceTool *tool) { GArray *axes = g_array_new (FALSE, FALSE, sizeof (gdouble)); struct libinput_tablet_tool *libinput_tool; gdouble value; libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); value = libinput_event_tablet_tool_get_x (tablet_event); g_array_append_val (axes, value); value = libinput_event_tablet_tool_get_y (tablet_event); g_array_append_val (axes, value); if (libinput_tablet_tool_has_distance (libinput_tool)) { value = libinput_event_tablet_tool_get_distance (tablet_event); g_array_append_val (axes, value); } if (libinput_tablet_tool_has_pressure (libinput_tool)) { value = libinput_event_tablet_tool_get_pressure (tablet_event); value = meta_input_device_tool_native_translate_pressure (tool, value); g_array_append_val (axes, value); } if (libinput_tablet_tool_has_tilt (libinput_tool)) { value = libinput_event_tablet_tool_get_tilt_x (tablet_event); g_array_append_val (axes, value); value = libinput_event_tablet_tool_get_tilt_y (tablet_event); g_array_append_val (axes, value); } if (libinput_tablet_tool_has_rotation (libinput_tool)) { value = libinput_event_tablet_tool_get_rotation (tablet_event); g_array_append_val (axes, value); } if (libinput_tablet_tool_has_slider (libinput_tool)) { value = libinput_event_tablet_tool_get_slider_position (tablet_event); g_array_append_val (axes, value); } if (libinput_tablet_tool_has_wheel (libinput_tool)) { value = libinput_event_tablet_tool_get_wheel_delta (tablet_event); g_array_append_val (axes, value); } if (axes->len == 0) { g_array_free (axes, TRUE); return NULL; } else return (gdouble *) g_array_free (axes, FALSE); } static MetaSeatNative * seat_from_device (ClutterInputDevice *device) { MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (device); return meta_input_device_native_get_seat (device_evdev); } static void notify_continuous_axis (MetaSeatNative *seat, ClutterInputDevice *device, uint64_t time_us, ClutterScrollSource scroll_source, struct libinput_event_pointer *axis_event) { gdouble dx = 0.0, dy = 0.0; ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; if (libinput_event_pointer_has_axis (axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) { dx = libinput_event_pointer_get_axis_value ( axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); if (fabs (dx) < DBL_EPSILON) finish_flags |= CLUTTER_SCROLL_FINISHED_HORIZONTAL; } if (libinput_event_pointer_has_axis (axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { dy = libinput_event_pointer_get_axis_value ( axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); if (fabs (dy) < DBL_EPSILON) finish_flags |= CLUTTER_SCROLL_FINISHED_VERTICAL; } meta_seat_native_notify_scroll_continuous (seat, device, time_us, dx, dy, scroll_source, finish_flags); } static void notify_discrete_axis (MetaSeatNative *seat, ClutterInputDevice *device, uint64_t time_us, ClutterScrollSource scroll_source, struct libinput_event_pointer *axis_event) { gdouble discrete_dx = 0.0, discrete_dy = 0.0; if (libinput_event_pointer_has_axis (axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL)) { discrete_dx = libinput_event_pointer_get_axis_value_discrete ( axis_event, LIBINPUT_POINTER_AXIS_SCROLL_HORIZONTAL); } if (libinput_event_pointer_has_axis (axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL)) { discrete_dy = libinput_event_pointer_get_axis_value_discrete ( axis_event, LIBINPUT_POINTER_AXIS_SCROLL_VERTICAL); } meta_seat_native_notify_discrete_scroll (seat, device, time_us, discrete_dx, discrete_dy, scroll_source); } static void process_tablet_axis (MetaSeatNative *seat, struct libinput_event *event) { struct libinput_device *libinput_device = libinput_event_get_device (event); uint64_t time; double x, y, dx, dy, *axes; float stage_width, stage_height; ClutterStage *stage; ClutterInputDevice *device; struct libinput_event_tablet_tool *tablet_event = libinput_event_get_tablet_tool_event (event); MetaInputDeviceNative *evdev_device; device = libinput_device_get_user_data (libinput_device); evdev_device = META_INPUT_DEVICE_NATIVE (device); stage = _clutter_input_device_get_stage (device); if (!stage) return; axes = translate_tablet_axes (tablet_event, evdev_device->last_tool); if (!axes) return; stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); time = libinput_event_tablet_tool_get_time_usec (tablet_event); if (clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE || clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_MOUSE || clutter_input_device_tool_get_tool_type (evdev_device->last_tool) == CLUTTER_INPUT_DEVICE_TOOL_LENS) { dx = libinput_event_tablet_tool_get_dx (tablet_event); dy = libinput_event_tablet_tool_get_dy (tablet_event); notify_relative_tool_motion (device, time, dx, dy, axes); } else { x = libinput_event_tablet_tool_get_x_transformed (tablet_event, stage_width); y = libinput_event_tablet_tool_get_y_transformed (tablet_event, stage_height); notify_absolute_motion (device, time, x, y, axes); } } static gboolean process_device_event (MetaSeatNative *seat, struct libinput_event *event) { gboolean handled = TRUE; struct libinput_device *libinput_device = libinput_event_get_device(event); ClutterInputDevice *device; MetaInputDeviceNative *device_evdev; switch (libinput_event_get_type (event)) { case LIBINPUT_EVENT_KEYBOARD_KEY: { uint32_t key, key_state, seat_key_count; uint64_t time_us; struct libinput_event_keyboard *key_event = libinput_event_get_keyboard_event (event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_keyboard_get_time_usec (key_event); key = libinput_event_keyboard_get_key (key_event); key_state = libinput_event_keyboard_get_key_state (key_event) == LIBINPUT_KEY_STATE_PRESSED; seat_key_count = libinput_event_keyboard_get_seat_key_count (key_event); /* Ignore key events that are not seat wide state changes. */ if ((key_state == LIBINPUT_KEY_STATE_PRESSED && seat_key_count != 1) || (key_state == LIBINPUT_KEY_STATE_RELEASED && seat_key_count != 0)) { meta_topic (META_DEBUG_INPUT, "Dropping key-%s of key 0x%x because seat-wide " "key count is %d\n", key_state == LIBINPUT_KEY_STATE_PRESSED ? "press" : "release", key, seat_key_count); break; } meta_seat_native_notify_key (seat_from_device (device), device, time_us, key, key_state, TRUE); break; } case LIBINPUT_EVENT_POINTER_MOTION: { struct libinput_event_pointer *pointer_event = libinput_event_get_pointer_event (event); uint64_t time_us; double dx; double dy; double dx_unaccel; double dy_unaccel; device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_pointer_get_time_usec (pointer_event); dx = libinput_event_pointer_get_dx (pointer_event); dy = libinput_event_pointer_get_dy (pointer_event); dx_unaccel = libinput_event_pointer_get_dx_unaccelerated (pointer_event); dy_unaccel = libinput_event_pointer_get_dy_unaccelerated (pointer_event); meta_seat_native_notify_relative_motion (seat_from_device (device), device, time_us, dx, dy, dx_unaccel, dy_unaccel); break; } case LIBINPUT_EVENT_POINTER_MOTION_ABSOLUTE: { uint64_t time_us; double x, y; float stage_width, stage_height; ClutterStage *stage; struct libinput_event_pointer *motion_event = libinput_event_get_pointer_event (event); device = libinput_device_get_user_data (libinput_device); stage = _clutter_input_device_get_stage (device); if (stage == NULL) break; stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); time_us = libinput_event_pointer_get_time_usec (motion_event); x = libinput_event_pointer_get_absolute_x_transformed (motion_event, stage_width); y = libinput_event_pointer_get_absolute_y_transformed (motion_event, stage_height); meta_seat_native_notify_absolute_motion (seat_from_device (device), device, time_us, x, y, NULL); break; } case LIBINPUT_EVENT_POINTER_BUTTON: { uint32_t button, button_state, seat_button_count; uint64_t time_us; struct libinput_event_pointer *button_event = libinput_event_get_pointer_event (event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_pointer_get_time_usec (button_event); button = libinput_event_pointer_get_button (button_event); button_state = libinput_event_pointer_get_button_state (button_event) == LIBINPUT_BUTTON_STATE_PRESSED; seat_button_count = libinput_event_pointer_get_seat_button_count (button_event); /* Ignore button events that are not seat wide state changes. */ if ((button_state == LIBINPUT_BUTTON_STATE_PRESSED && seat_button_count != 1) || (button_state == LIBINPUT_BUTTON_STATE_RELEASED && seat_button_count != 0)) { meta_topic (META_DEBUG_INPUT, "Dropping button-%s of button 0x%x because seat-wide " "button count is %d\n", button_state == LIBINPUT_BUTTON_STATE_PRESSED ? "press" : "release", button, seat_button_count); break; } meta_seat_native_notify_button (seat_from_device (device), device, time_us, button, button_state); break; } case LIBINPUT_EVENT_POINTER_AXIS: { uint64_t time_us; enum libinput_pointer_axis_source source; struct libinput_event_pointer *axis_event = libinput_event_get_pointer_event (event); MetaSeatNative *seat; ClutterScrollSource scroll_source; device = libinput_device_get_user_data (libinput_device); seat = meta_input_device_native_get_seat (META_INPUT_DEVICE_NATIVE (device)); time_us = libinput_event_pointer_get_time_usec (axis_event); source = libinput_event_pointer_get_axis_source (axis_event); scroll_source = translate_scroll_source (source); /* libinput < 0.8 sent wheel click events with value 10. Since 0.8 the value is the angle of the click in degrees. To keep backwards-compat with existing clients, we just send multiples of the click count. */ switch (scroll_source) { case CLUTTER_SCROLL_SOURCE_WHEEL: notify_discrete_axis (seat, device, time_us, scroll_source, axis_event); break; case CLUTTER_SCROLL_SOURCE_FINGER: case CLUTTER_SCROLL_SOURCE_CONTINUOUS: case CLUTTER_SCROLL_SOURCE_UNKNOWN: notify_continuous_axis (seat, device, time_us, scroll_source, axis_event); break; } break; } case LIBINPUT_EVENT_TOUCH_DOWN: { int device_slot; uint64_t time_us; double x, y; float stage_width, stage_height; MetaSeatNative *seat; ClutterStage *stage; MetaTouchState *touch_state; struct libinput_event_touch *touch_event = libinput_event_get_touch_event (event); device = libinput_device_get_user_data (libinput_device); device_evdev = META_INPUT_DEVICE_NATIVE (device); seat = meta_input_device_native_get_seat (device_evdev); stage = _clutter_input_device_get_stage (device); if (stage == NULL) break; stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); device_slot = libinput_event_touch_get_slot (touch_event); time_us = libinput_event_touch_get_time_usec (touch_event); x = libinput_event_touch_get_x_transformed (touch_event, stage_width); y = libinput_event_touch_get_y_transformed (touch_event, stage_height); touch_state = meta_input_device_native_acquire_touch_state (device_evdev, device_slot); touch_state->coords.x = x; touch_state->coords.y = y; meta_seat_native_notify_touch_event (seat, device, CLUTTER_TOUCH_BEGIN, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); break; } case LIBINPUT_EVENT_TOUCH_UP: { int device_slot; uint64_t time_us; MetaSeatNative *seat; MetaTouchState *touch_state; struct libinput_event_touch *touch_event = libinput_event_get_touch_event (event); device = libinput_device_get_user_data (libinput_device); device_evdev = META_INPUT_DEVICE_NATIVE (device); seat = meta_input_device_native_get_seat (device_evdev); device_slot = libinput_event_touch_get_slot (touch_event); time_us = libinput_event_touch_get_time_usec (touch_event); touch_state = meta_input_device_native_lookup_touch_state (device_evdev, device_slot); if (!touch_state) break; meta_seat_native_notify_touch_event (seat, device, CLUTTER_TOUCH_END, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); meta_input_device_native_release_touch_state (device_evdev, touch_state); break; } case LIBINPUT_EVENT_TOUCH_MOTION: { int device_slot; uint64_t time_us; double x, y; float stage_width, stage_height; MetaSeatNative *seat; ClutterStage *stage; MetaTouchState *touch_state; struct libinput_event_touch *touch_event = libinput_event_get_touch_event (event); device = libinput_device_get_user_data (libinput_device); device_evdev = META_INPUT_DEVICE_NATIVE (device); seat = meta_input_device_native_get_seat (device_evdev); stage = _clutter_input_device_get_stage (device); if (stage == NULL) break; stage_width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); stage_height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); device_slot = libinput_event_touch_get_slot (touch_event); time_us = libinput_event_touch_get_time_usec (touch_event); x = libinput_event_touch_get_x_transformed (touch_event, stage_width); y = libinput_event_touch_get_y_transformed (touch_event, stage_height); touch_state = meta_input_device_native_lookup_touch_state (device_evdev, device_slot); if (!touch_state) break; touch_state->coords.x = x; touch_state->coords.y = y; meta_seat_native_notify_touch_event (seat, device, CLUTTER_TOUCH_UPDATE, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); break; } case LIBINPUT_EVENT_TOUCH_CANCEL: { uint64_t time_us; struct libinput_event_touch *touch_event = libinput_event_get_touch_event (event); device = libinput_device_get_user_data (libinput_device); device_evdev = META_INPUT_DEVICE_NATIVE (device); time_us = libinput_event_touch_get_time_usec (touch_event); meta_input_device_native_release_touch_slots (device_evdev, time_us); break; } case LIBINPUT_EVENT_GESTURE_PINCH_BEGIN: case LIBINPUT_EVENT_GESTURE_PINCH_END: { struct libinput_event_gesture *gesture_event = libinput_event_get_gesture_event (event); ClutterTouchpadGesturePhase phase; uint32_t n_fingers; uint64_t time_us; if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_PINCH_BEGIN) phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; else phase = libinput_event_gesture_get_cancelled (gesture_event) ? CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; n_fingers = libinput_event_gesture_get_finger_count (gesture_event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_gesture_get_time_usec (gesture_event); notify_pinch_gesture_event (device, phase, time_us, 0, 0, 0, 0, n_fingers); break; } case LIBINPUT_EVENT_GESTURE_PINCH_UPDATE: { struct libinput_event_gesture *gesture_event = libinput_event_get_gesture_event (event); gdouble angle_delta, scale, dx, dy; uint32_t n_fingers; uint64_t time_us; n_fingers = libinput_event_gesture_get_finger_count (gesture_event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_gesture_get_time_usec (gesture_event); angle_delta = libinput_event_gesture_get_angle_delta (gesture_event); scale = libinput_event_gesture_get_scale (gesture_event); dx = libinput_event_gesture_get_dx (gesture_event); dy = libinput_event_gesture_get_dy (gesture_event); notify_pinch_gesture_event (device, CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, time_us, dx, dy, angle_delta, scale, n_fingers); break; } case LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN: case LIBINPUT_EVENT_GESTURE_SWIPE_END: { struct libinput_event_gesture *gesture_event = libinput_event_get_gesture_event (event); ClutterTouchpadGesturePhase phase; uint32_t n_fingers; uint64_t time_us; device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_gesture_get_time_usec (gesture_event); n_fingers = libinput_event_gesture_get_finger_count (gesture_event); if (libinput_event_get_type (event) == LIBINPUT_EVENT_GESTURE_SWIPE_BEGIN) phase = CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN; else phase = libinput_event_gesture_get_cancelled (gesture_event) ? CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL : CLUTTER_TOUCHPAD_GESTURE_PHASE_END; notify_swipe_gesture_event (device, phase, time_us, n_fingers, 0, 0); break; } case LIBINPUT_EVENT_GESTURE_SWIPE_UPDATE: { struct libinput_event_gesture *gesture_event = libinput_event_get_gesture_event (event); uint32_t n_fingers; uint64_t time_us; double dx, dy; device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_gesture_get_time_usec (gesture_event); n_fingers = libinput_event_gesture_get_finger_count (gesture_event); dx = libinput_event_gesture_get_dx (gesture_event); dy = libinput_event_gesture_get_dy (gesture_event); notify_swipe_gesture_event (device, CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE, time_us, n_fingers, dx, dy); break; } case LIBINPUT_EVENT_TABLET_TOOL_AXIS: { process_tablet_axis (seat, event); break; } case LIBINPUT_EVENT_TABLET_TOOL_PROXIMITY: { uint64_t time; struct libinput_event_tablet_tool *tablet_event = libinput_event_get_tablet_tool_event (event); struct libinput_tablet_tool *libinput_tool = NULL; enum libinput_tablet_tool_proximity_state state; state = libinput_event_tablet_tool_get_proximity_state (tablet_event); time = libinput_event_tablet_tool_get_time_usec (tablet_event); device = libinput_device_get_user_data (libinput_device); libinput_tool = libinput_event_tablet_tool_get_tool (tablet_event); if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN) input_device_update_tool (device, libinput_tool); notify_proximity (device, time, state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_IN); if (state == LIBINPUT_TABLET_TOOL_PROXIMITY_STATE_OUT) input_device_update_tool (device, NULL); break; } case LIBINPUT_EVENT_TABLET_TOOL_BUTTON: { uint64_t time_us; uint32_t button_state; struct libinput_event_tablet_tool *tablet_event = libinput_event_get_tablet_tool_event (event); uint32_t tablet_button; process_tablet_axis (seat, event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); tablet_button = libinput_event_tablet_tool_get_button (tablet_event); button_state = libinput_event_tablet_tool_get_button_state (tablet_event) == LIBINPUT_BUTTON_STATE_PRESSED; meta_seat_native_notify_button (seat_from_device (device), device, time_us, tablet_button, button_state); break; } case LIBINPUT_EVENT_TABLET_TOOL_TIP: { uint64_t time_us; uint32_t button_state; struct libinput_event_tablet_tool *tablet_event = libinput_event_get_tablet_tool_event (event); device = libinput_device_get_user_data (libinput_device); time_us = libinput_event_tablet_tool_get_time_usec (tablet_event); button_state = libinput_event_tablet_tool_get_tip_state (tablet_event) == LIBINPUT_TABLET_TOOL_TIP_DOWN; /* To avoid jumps on tip, notify axes before the tip down event but after the tip up event */ if (button_state) process_tablet_axis (seat, event); meta_seat_native_notify_button (seat_from_device (device), device, time_us, BTN_TOUCH, button_state); if (!button_state) process_tablet_axis (seat, event); break; } case LIBINPUT_EVENT_TABLET_PAD_BUTTON: { uint64_t time; uint32_t button_state, button, group, mode; struct libinput_tablet_pad_mode_group *mode_group; struct libinput_event_tablet_pad *pad_event = libinput_event_get_tablet_pad_event (event); device = libinput_device_get_user_data (libinput_device); time = libinput_event_tablet_pad_get_time_usec (pad_event); mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); group = libinput_tablet_pad_mode_group_get_index (mode_group); mode = libinput_event_tablet_pad_get_mode (pad_event); button = libinput_event_tablet_pad_get_button_number (pad_event); button_state = libinput_event_tablet_pad_get_button_state (pad_event) == LIBINPUT_BUTTON_STATE_PRESSED; notify_pad_button (device, time, button, group, mode, button_state); break; } case LIBINPUT_EVENT_TABLET_PAD_STRIP: { uint64_t time; uint32_t number, source, group, mode; struct libinput_tablet_pad_mode_group *mode_group; struct libinput_event_tablet_pad *pad_event = libinput_event_get_tablet_pad_event (event); double value; device = libinput_device_get_user_data (libinput_device); time = libinput_event_tablet_pad_get_time_usec (pad_event); number = libinput_event_tablet_pad_get_strip_number (pad_event); value = libinput_event_tablet_pad_get_strip_position (pad_event); source = libinput_event_tablet_pad_get_strip_source (pad_event); mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); group = libinput_tablet_pad_mode_group_get_index (mode_group); mode = libinput_event_tablet_pad_get_mode (pad_event); notify_pad_strip (device, time, number, source, group, mode, value); break; } case LIBINPUT_EVENT_TABLET_PAD_RING: { uint64_t time; uint32_t number, source, group, mode; struct libinput_tablet_pad_mode_group *mode_group; struct libinput_event_tablet_pad *pad_event = libinput_event_get_tablet_pad_event (event); double angle; device = libinput_device_get_user_data (libinput_device); time = libinput_event_tablet_pad_get_time_usec (pad_event); number = libinput_event_tablet_pad_get_ring_number (pad_event); angle = libinput_event_tablet_pad_get_ring_position (pad_event); source = libinput_event_tablet_pad_get_ring_source (pad_event); mode_group = libinput_event_tablet_pad_get_mode_group (pad_event); group = libinput_tablet_pad_mode_group_get_index (mode_group); mode = libinput_event_tablet_pad_get_mode (pad_event); notify_pad_ring (device, time, number, source, group, mode, angle); break; } case LIBINPUT_EVENT_SWITCH_TOGGLE: { struct libinput_event_switch *switch_event = libinput_event_get_switch_event (event); enum libinput_switch sw = libinput_event_switch_get_switch (switch_event); enum libinput_switch_state state = libinput_event_switch_get_switch_state (switch_event); if (sw == LIBINPUT_SWITCH_TABLET_MODE) { seat->tablet_mode_switch_state = (state == LIBINPUT_SWITCH_STATE_ON); update_touch_mode (seat); } break; } default: handled = FALSE; } return handled; } static gboolean filter_event (MetaSeatNative *seat, struct libinput_event *event) { gboolean retval = CLUTTER_EVENT_PROPAGATE; MetaEventFilter *filter; GSList *tmp_list; tmp_list = seat->event_filters; while (tmp_list) { filter = tmp_list->data; retval = filter->func (event, filter->data); tmp_list = tmp_list->next; if (retval != CLUTTER_EVENT_PROPAGATE) break; } return retval; } static void process_event (MetaSeatNative *seat, struct libinput_event *event) { gboolean retval; retval = filter_event (seat, event); if (retval != CLUTTER_EVENT_PROPAGATE) return; if (process_base_event (seat, event)) return; if (process_device_event (seat, event)) return; } static void process_events (MetaSeatNative *seat) { struct libinput_event *event; while ((event = libinput_get_event (seat->libinput))) { process_event(seat, event); libinput_event_destroy(event); } } static int open_restricted (const char *path, int flags, void *user_data) { gint fd; if (device_open_callback) { GError *error = NULL; fd = device_open_callback (path, flags, device_callback_data, &error); if (fd < 0) { g_warning ("Could not open device %s: %s", path, error->message); g_error_free (error); } } else { fd = open (path, O_RDWR | O_NONBLOCK); if (fd < 0) { g_warning ("Could not open device %s: %s", path, strerror (errno)); } } return fd; } static void close_restricted (int fd, void *user_data) { if (device_close_callback) device_close_callback (fd, device_callback_data); else close (fd); } static const struct libinput_interface libinput_interface = { open_restricted, close_restricted }; static void meta_seat_native_constructed (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); ClutterInputDevice *device; ClutterStage *stage; MetaEventSource *source; struct udev *udev; struct xkb_keymap *xkb_keymap; device = meta_input_device_native_new_virtual ( seat, CLUTTER_POINTER_DEVICE, CLUTTER_INPUT_MODE_MASTER); stage = meta_seat_native_get_stage (seat); _clutter_input_device_set_stage (device, stage); seat->pointer_x = INITIAL_POINTER_X; seat->pointer_y = INITIAL_POINTER_Y; _clutter_input_device_set_coords (device, NULL, seat->pointer_x, seat->pointer_y, NULL); seat->core_pointer = device; device = meta_input_device_native_new_virtual ( seat, CLUTTER_KEYBOARD_DEVICE, CLUTTER_INPUT_MODE_MASTER); _clutter_input_device_set_stage (device, stage); seat->core_keyboard = device; udev = udev_new (); if (G_UNLIKELY (udev == NULL)) { g_warning ("Failed to create udev object"); return; } seat->libinput = libinput_udev_create_context (&libinput_interface, seat, udev); if (seat->libinput == NULL) { g_critical ("Failed to create the libinput object."); return; } if (libinput_udev_assign_seat (seat->libinput, seat->seat_id) == -1) { g_critical ("Failed to assign a seat to the libinput object."); libinput_unref (seat->libinput); seat->libinput = NULL; return; } udev_unref (udev); seat->udev_client = g_udev_client_new ((const gchar *[]) { "input", NULL }); source = meta_event_source_new (seat); seat->event_source = source; seat->keymap = g_object_new (META_TYPE_KEYMAP_NATIVE, NULL); xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); if (xkb_keymap) { seat->xkb = xkb_state_new (xkb_keymap); seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); } seat->has_touchscreen = has_touchscreen (seat); update_touch_mode (seat); if (G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed) G_OBJECT_CLASS (meta_seat_native_parent_class)->constructed (object); } static void meta_seat_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaSeatNative *seat_native = META_SEAT_NATIVE (object); switch (prop_id) { case PROP_SEAT_ID: seat_native->seat_id = g_value_dup_string (value); break; case PROP_TOUCH_MODE: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_seat_native_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaSeatNative *seat_native = META_SEAT_NATIVE (object); switch (prop_id) { case PROP_SEAT_ID: g_value_set_string (value, seat_native->seat_id); break; case PROP_TOUCH_MODE: g_value_set_boolean (value, seat_native->touch_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_seat_native_dispose (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); g_clear_signal_handler (&seat->stage_added_handler, seat->stage_manager); g_clear_signal_handler (&seat->stage_removed_handler, seat->stage_manager); if (seat->stage_manager) { g_object_unref (seat->stage_manager); seat->stage_manager = NULL; } if (seat->libinput) { libinput_unref (seat->libinput); seat->libinput = NULL; } G_OBJECT_CLASS (meta_seat_native_parent_class)->dispose (object); } static void meta_seat_native_finalize (GObject *object) { MetaSeatNative *seat = META_SEAT_NATIVE (object); GSList *iter; for (iter = seat->devices; iter; iter = g_slist_next (iter)) { ClutterInputDevice *device = iter->data; g_object_unref (device); } g_slist_free (seat->devices); g_free (seat->touch_states); g_object_unref (seat->udev_client); meta_event_source_free (seat->event_source); xkb_state_unref (seat->xkb); meta_seat_native_clear_repeat_timer (seat); if (seat->libinput_seat) libinput_seat_unref (seat->libinput_seat); g_list_free (seat->free_device_ids); if (seat->constrain_data_notify != NULL) seat->constrain_data_notify (seat->constrain_data); g_free (seat->seat_id); G_OBJECT_CLASS (meta_seat_native_parent_class)->finalize (object); } static ClutterInputDevice * meta_seat_native_get_pointer (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return seat_native->core_pointer; } static ClutterInputDevice * meta_seat_native_get_keyboard (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return seat_native->core_keyboard; } static GList * meta_seat_native_list_devices (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); GList *devices = NULL; GSList *l; for (l = seat_native->devices; l; l = l->next) devices = g_list_prepend (devices, l->data); return devices; } static void meta_seat_native_bell_notify (ClutterSeat *seat) { MetaDisplay *display = meta_get_display (); meta_bell_notify (display, NULL); } static ClutterKeymap * meta_seat_native_get_keymap (ClutterSeat *seat) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); return CLUTTER_KEYMAP (seat_native->keymap); } static void meta_seat_native_copy_event_data (ClutterSeat *seat, const ClutterEvent *src, ClutterEvent *dest) { MetaEventNative *event_evdev; event_evdev = _clutter_event_get_platform_data (src); if (event_evdev != NULL) _clutter_event_set_platform_data (dest, meta_event_native_copy (event_evdev)); } static void meta_seat_native_free_event_data (ClutterSeat *seat, ClutterEvent *event) { MetaEventNative *event_evdev; event_evdev = _clutter_event_get_platform_data (event); if (event_evdev != NULL) meta_event_native_free (event_evdev); } static void meta_seat_native_apply_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *settings) { ClutterInputDevice *device; device = clutter_seat_get_keyboard (seat); if (device) meta_input_device_native_apply_kbd_a11y_settings (META_INPUT_DEVICE_NATIVE (device), settings); } static ClutterVirtualInputDevice * meta_seat_native_create_virtual_device (ClutterSeat *seat, ClutterInputDeviceType device_type) { return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_NATIVE, "seat", seat, "device-type", device_type, NULL); } static ClutterVirtualDeviceType meta_seat_native_get_supported_virtual_device_types (ClutterSeat *seat) { return (CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD | CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER | CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN); } static void meta_seat_native_compress_motion (ClutterSeat *seat, ClutterEvent *event, const ClutterEvent *to_discard) { double dx, dy; double dx_unaccel, dy_unaccel; double dst_dx = 0.0, dst_dy = 0.0; double dst_dx_unaccel = 0.0, dst_dy_unaccel = 0.0; if (!meta_event_native_get_relative_motion (to_discard, &dx, &dy, &dx_unaccel, &dy_unaccel)) return; meta_event_native_get_relative_motion (event, &dst_dx, &dst_dy, &dst_dx_unaccel, &dst_dy_unaccel); meta_event_native_set_relative_motion (event, dx + dst_dx, dy + dst_dy, dx_unaccel + dst_dx_unaccel, dy_unaccel + dst_dy_unaccel); } static void meta_seat_native_warp_pointer (ClutterSeat *seat, int x, int y) { MetaSeatNative *seat_native = META_SEAT_NATIVE (seat); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); notify_absolute_motion (seat_native->core_pointer, 0, x, y, NULL); meta_cursor_tracker_update_position (cursor_tracker, x, y); } static void meta_seat_native_class_init (MetaSeatNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass); object_class->constructed = meta_seat_native_constructed; object_class->set_property = meta_seat_native_set_property; object_class->get_property = meta_seat_native_get_property; object_class->dispose = meta_seat_native_dispose; object_class->finalize = meta_seat_native_finalize; seat_class->get_pointer = meta_seat_native_get_pointer; seat_class->get_keyboard = meta_seat_native_get_keyboard; seat_class->list_devices = meta_seat_native_list_devices; seat_class->bell_notify = meta_seat_native_bell_notify; seat_class->get_keymap = meta_seat_native_get_keymap; seat_class->copy_event_data = meta_seat_native_copy_event_data; seat_class->free_event_data = meta_seat_native_free_event_data; seat_class->apply_kbd_a11y_settings = meta_seat_native_apply_kbd_a11y_settings; seat_class->create_virtual_device = meta_seat_native_create_virtual_device; seat_class->get_supported_virtual_device_types = meta_seat_native_get_supported_virtual_device_types; seat_class->compress_motion = meta_seat_native_compress_motion; seat_class->warp_pointer = meta_seat_native_warp_pointer; seat_class->handle_device_event = meta_seat_native_handle_device_event; props[PROP_SEAT_ID] = g_param_spec_string ("seat-id", "Seat ID", "Seat ID", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, props); g_object_class_override_property (object_class, PROP_TOUCH_MODE, "touch-mode"); } static void meta_seat_native_stage_added_cb (ClutterStageManager *manager, ClutterStage *stage, MetaSeatNative *seat) { /* NB: Currently we can only associate a single stage with all evdev * devices. * * We save a pointer to the stage so if we release/reclaim input * devices due to switching virtual terminals then we know what * stage to re associate the devices with. */ meta_seat_native_set_stage (seat, stage); /* We only want to do this once so we can catch the default stage. If the application has multiple stages then it will need to manage the stage of the input devices itself */ g_clear_signal_handler (&seat->stage_added_handler, seat->stage_manager); } static void meta_seat_native_stage_removed_cb (ClutterStageManager *manager, ClutterStage *stage, MetaSeatNative *seat) { meta_seat_native_set_stage (seat, NULL); } static void meta_seat_native_init (MetaSeatNative *seat) { seat->stage_manager = clutter_stage_manager_get_default (); g_object_ref (seat->stage_manager); /* evdev doesn't have any way to link an event to a particular stage so we'll have to leave it up to applications to set the corresponding stage for an input device. However to make it easier for applications that are only using one fullscreen stage (which is probably the most frequent use-case for the evdev backend) we'll associate any input devices that don't have a stage with the first stage created. */ seat->stage_added_handler = g_signal_connect (seat->stage_manager, "stage-added", G_CALLBACK (meta_seat_native_stage_added_cb), seat); seat->stage_removed_handler = g_signal_connect (seat->stage_manager, "stage-removed", G_CALLBACK (meta_seat_native_stage_removed_cb), seat); seat->device_id_next = INITIAL_DEVICE_ID; seat->repeat = TRUE; seat->repeat_delay = 250; /* ms */ seat->repeat_interval = 33; /* ms */ } ClutterInputDevice * meta_seat_native_get_device (MetaSeatNative *seat, int id) { ClutterInputDevice *device; GSList *l; for (l = seat->devices; l; l = l->next) { device = l->data; if (clutter_input_device_get_device_id (device) == id) return device; } return NULL; } void meta_seat_native_set_stage (MetaSeatNative *seat, ClutterStage *stage) { GSList *l; if (seat->stage == stage) return; seat->stage = stage; _clutter_input_device_set_stage (seat->core_pointer, stage); _clutter_input_device_set_stage (seat->core_keyboard, stage); for (l = seat->devices; l; l = l->next) { ClutterInputDevice *device = l->data; _clutter_input_device_set_stage (device, stage); } } ClutterStage * meta_seat_native_get_stage (MetaSeatNative *seat) { return seat->stage; } /** * meta_seat_native_set_device_callbacks: (skip) * @open_callback: the user replacement for open() * @close_callback: the user replacement for close() * @user_data: user data for @callback * * Through this function, the application can set a custom callback * to be invoked when Clutter is about to open an evdev device. It can do * so if special handling is needed, for example to circumvent permission * problems. * * Setting @callback to %NULL will reset the default behavior. * * For reliable effects, this function must be called before clutter_init(). */ void meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback open_callback, MetaCloseDeviceCallback close_callback, gpointer user_data) { device_open_callback = open_callback; device_close_callback = close_callback; device_callback_data = user_data; } /** * meta_seat_native_set_pointer_constrain_callback: * @seat: the #ClutterSeat created by the evdev backend * @callback: the callback * @user_data: data to pass to the callback * @user_data_notify: function to be called when removing the callback * * Sets a callback to be invoked for every pointer motion. The callback * can then modify the new pointer coordinates to constrain movement within * a specific region. */ void meta_seat_native_set_pointer_constrain_callback (MetaSeatNative *seat, MetaPointerConstrainCallback callback, gpointer user_data, GDestroyNotify user_data_notify) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); if (seat->constrain_data_notify) seat->constrain_data_notify (seat->constrain_data); seat->constrain_callback = callback; seat->constrain_data = user_data; seat->constrain_data_notify = user_data_notify; } void meta_seat_native_set_relative_motion_filter (MetaSeatNative *seat, MetaRelativeMotionFilter filter, gpointer user_data) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); seat->relative_motion_filter = filter; seat->relative_motion_filter_user_data = user_data; } /** * meta_seat_native_add_filter: (skip) * @func: (closure data): a filter function * @data: (allow-none): user data to be passed to the filter function, or %NULL * @destroy_notify: (allow-none): function to call on @data when the filter is removed, or %NULL * * Adds an event filter function. */ void meta_seat_native_add_filter (MetaSeatNative *seat, MetaEvdevFilterFunc func, gpointer data, GDestroyNotify destroy_notify) { MetaEventFilter *filter; g_return_if_fail (func != NULL); filter = g_new0 (MetaEventFilter, 1); filter->func = func; filter->data = data; filter->destroy_notify = destroy_notify; seat->event_filters = g_slist_append (seat->event_filters, filter); } /** * meta_seat_native_remove_filter: (skip) * @func: a filter function * @data: (allow-none): user data to be passed to the filter function, or %NULL * * Removes the given filter function. */ void meta_seat_native_remove_filter (MetaSeatNative *seat, MetaEvdevFilterFunc func, gpointer data) { MetaEventFilter *filter; GSList *tmp_list; g_return_if_fail (func != NULL); tmp_list = seat->event_filters; while (tmp_list) { filter = tmp_list->data; if (filter->func == func && filter->data == data) { if (filter->destroy_notify) filter->destroy_notify (filter->data); g_free (filter); seat->event_filters = g_slist_delete_link (seat->event_filters, tmp_list); return; } tmp_list = tmp_list->next; } } void meta_seat_native_update_xkb_state (MetaSeatNative *seat) { xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; struct xkb_keymap *xkb_keymap; xkb_keymap = meta_keymap_native_get_keyboard_map (seat->keymap); latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); xkb_state_unref (seat->xkb); seat->xkb = xkb_state_new (xkb_keymap); xkb_state_update_mask (seat->xkb, 0, /* depressed */ latched_mods, locked_mods, 0, 0, seat->layout_idx); seat->caps_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_CAPS); seat->num_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_NUM); seat->scroll_lock_led = xkb_keymap_led_get_index (xkb_keymap, XKB_LED_NAME_SCROLL); meta_seat_native_sync_leds (seat); } gint meta_seat_native_acquire_device_id (MetaSeatNative *seat) { GList *first; gint next_id; if (seat->free_device_ids == NULL) { gint i; /* We ran out of free ID's, so append 10 new ones. */ for (i = 0; i < 10; i++) seat->free_device_ids = g_list_append (seat->free_device_ids, GINT_TO_POINTER (seat->device_id_next++)); } first = g_list_first (seat->free_device_ids); next_id = GPOINTER_TO_INT (first->data); seat->free_device_ids = g_list_delete_link (seat->free_device_ids, first); return next_id; } static int compare_ids (gconstpointer a, gconstpointer b) { return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b); } void meta_seat_native_release_device_id (MetaSeatNative *seat, ClutterInputDevice *device) { gint device_id; device_id = clutter_input_device_get_device_id (device); seat->free_device_ids = g_list_insert_sorted (seat->free_device_ids, GINT_TO_POINTER (device_id), compare_ids); } /** * meta_seat_native_release_devices: * * Releases all the evdev devices that Clutter is currently managing. This api * is typically used when switching away from the Clutter application when * switching tty. The devices can be reclaimed later with a call to * meta_seat_native_reclaim_devices(). * * This function should only be called after clutter has been initialized. */ void meta_seat_native_release_devices (MetaSeatNative *seat) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); if (seat->released) { g_warning ("meta_seat_native_release_devices() shouldn't be called " "multiple times without a corresponding call to " "meta_seat_native_reclaim_devices() first"); return; } libinput_suspend (seat->libinput); process_events (seat); seat->released = TRUE; } /** * meta_seat_native_reclaim_devices: * * This causes Clutter to re-probe for evdev devices. This is must only be * called after a corresponding call to meta_seat_native_release_devices() * was previously used to release all evdev devices. This API is typically * used when a clutter application using evdev has regained focus due to * switching ttys. * * This function should only be called after clutter has been initialized. */ void meta_seat_native_reclaim_devices (MetaSeatNative *seat) { if (!seat->released) { g_warning ("Spurious call to meta_seat_native_reclaim_devices() without " "previous call to meta_seat_native_release_devices"); return; } libinput_resume (seat->libinput); meta_seat_native_update_xkb_state (seat); process_events (seat); seat->released = FALSE; } /** * meta_seat_native_set_keyboard_map: (skip) * @seat: the #ClutterSeat created by the evdev backend * @keymap: the new keymap * * Instructs @evdev to use the speficied keyboard map. This will cause * the backend to drop the state and create a new one with the new * map. To avoid state being lost, callers should ensure that no key * is pressed when calling this function. */ void meta_seat_native_set_keyboard_map (MetaSeatNative *seat, struct xkb_keymap *xkb_keymap) { ClutterKeymap *keymap; g_return_if_fail (META_IS_SEAT_NATIVE (seat)); keymap = clutter_seat_get_keymap (CLUTTER_SEAT (seat)); meta_keymap_native_set_keyboard_map (META_KEYMAP_NATIVE (keymap), xkb_keymap); meta_seat_native_update_xkb_state (seat); } /** * meta_seat_native_get_keyboard_map: (skip) * @seat: the #ClutterSeat created by the evdev backend * * Retrieves the #xkb_keymap in use by the evdev backend. * * Return value: the #xkb_keymap. */ struct xkb_keymap * meta_seat_native_get_keyboard_map (MetaSeatNative *seat) { g_return_val_if_fail (META_IS_SEAT_NATIVE (seat), NULL); return xkb_state_get_keymap (seat->xkb); } /** * meta_seat_native_set_keyboard_layout_index: (skip) * @seat: the #ClutterSeat created by the evdev backend * @idx: the xkb layout index to set * * Sets the xkb layout index on the backend's #xkb_state . */ void meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat, xkb_layout_index_t idx) { xkb_mod_mask_t depressed_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; struct xkb_state *state; g_return_if_fail (META_IS_SEAT_NATIVE (seat)); state = seat->xkb; depressed_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED); latched_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED); xkb_state_update_mask (state, depressed_mods, latched_mods, locked_mods, 0, 0, idx); seat->layout_idx = idx; } /** * meta_seat_native_get_keyboard_layout_index: (skip) */ xkb_layout_index_t meta_seat_native_get_keyboard_layout_index (MetaSeatNative *seat) { return seat->layout_idx; } /** * meta_seat_native_set_keyboard_numlock: (skip) * @seat: the #ClutterSeat created by the evdev backend * @numlock_set: TRUE to set NumLock ON, FALSE otherwise. * * Sets the NumLock state on the backend's #xkb_state . */ void meta_seat_native_set_keyboard_numlock (MetaSeatNative *seat, gboolean numlock_state) { xkb_mod_mask_t depressed_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; xkb_mod_mask_t group_mods; xkb_mod_mask_t numlock; struct xkb_keymap *xkb_keymap; ClutterKeymap *keymap; g_return_if_fail (META_IS_SEAT_NATIVE (seat)); keymap = clutter_seat_get_keymap (CLUTTER_SEAT (seat)); xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap)); numlock = (1 << xkb_keymap_mod_get_index (xkb_keymap, "Mod2")); depressed_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_DEPRESSED); latched_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (seat->xkb, XKB_STATE_MODS_LOCKED); group_mods = xkb_state_serialize_layout (seat->xkb, XKB_STATE_LAYOUT_EFFECTIVE); if (numlock_state) locked_mods |= numlock; else locked_mods &= ~numlock; xkb_state_update_mask (seat->xkb, depressed_mods, latched_mods, locked_mods, 0, 0, group_mods); meta_seat_native_sync_leds (seat); } /** * meta_seat_native_set_keyboard_repeat: * @seat: the #ClutterSeat created by the evdev backend * @repeat: whether to enable or disable keyboard repeat events * @delay: the delay in ms between the hardware key press event and * the first synthetic event * @interval: the period in ms between consecutive synthetic key * press events * * Enables or disables sythetic key press events, allowing for initial * delay and interval period to be specified. */ void meta_seat_native_set_keyboard_repeat (MetaSeatNative *seat, gboolean repeat, uint32_t delay, uint32_t interval) { g_return_if_fail (META_IS_SEAT_NATIVE (seat)); seat->repeat = repeat; seat->repeat_delay = delay; seat->repeat_interval = interval; } struct xkb_state * meta_seat_native_get_xkb_state (MetaSeatNative *seat) { return seat->xkb; } muffin-6.4.1/src/backends/native/dbus-utils.c0000664000175000017500000000545414723361714020040 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "backends/native/dbus-utils.h" #include /* Stolen from tp_escape_as_identifier, from tp-glib, * which follows the same escaping convention as systemd. */ static inline gboolean _esc_ident_bad (gchar c, gboolean is_first) { return ((c < 'a' || c > 'z') && (c < 'A' || c > 'Z') && (c < '0' || c > '9' || is_first)); } static gchar * escape_dbus_component (const gchar *name) { gboolean bad = FALSE; size_t len = 0; GString *op; const gchar *ptr, *first_ok; g_return_val_if_fail (name != NULL, NULL); /* fast path for empty name */ if (name[0] == '\0') return g_strdup ("_"); for (ptr = name; *ptr; ptr++) { if (_esc_ident_bad (*ptr, ptr == name)) { bad = TRUE; len += 3; } else len++; } /* fast path if it's clean */ if (!bad) return g_strdup (name); /* If strictly less than ptr, first_ok is the first uncopied safe character. */ first_ok = name; op = g_string_sized_new (len); for (ptr = name; *ptr; ptr++) { if (_esc_ident_bad (*ptr, ptr == name)) { /* copy preceding safe characters if any */ if (first_ok < ptr) { g_string_append_len (op, first_ok, ptr - first_ok); } /* escape the unsafe character */ g_string_append_printf (op, "_%02x", (unsigned char)(*ptr)); /* restart after it */ first_ok = ptr + 1; } } /* copy trailing safe characters if any */ if (first_ok < ptr) { g_string_append_len (op, first_ok, ptr - first_ok); } return g_string_free (op, FALSE); } char * get_escaped_dbus_path (const char *prefix, const char *component) { char *escaped_component = escape_dbus_component (component); char *path = g_strconcat (prefix, "/", escaped_component, NULL); g_free (escaped_component); return path; } muffin-6.4.1/src/backends/native/meta-kms-connector.h0000664000175000017500000000565014723361714021456 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_CONNECTOR_H #define META_KMS_CONNECTOR_H #include #include #include #include "backends/meta-output.h" #include "backends/native/meta-kms-types.h" #define META_TYPE_KMS_CONNECTOR (meta_kms_connector_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsConnector, meta_kms_connector, META, KMS_CONNECTOR, GObject) typedef struct _MetaKmsConnectorState { uint32_t current_crtc_id; uint32_t common_possible_crtcs; uint32_t common_possible_clones; uint32_t encoder_device_idxs; drmModeModeInfo *modes; int n_modes; uint32_t width_mm; uint32_t height_mm; MetaTileInfo tile_info; GBytes *edid_data; gboolean has_scaling; CoglSubpixelOrder subpixel_order; int suggested_x; int suggested_y; gboolean hotplug_mode_update; MetaMonitorTransform panel_orientation_transform; } MetaKmsConnectorState; MetaKmsDevice * meta_kms_connector_get_device (MetaKmsConnector *connector); void meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector, MetaKmsUpdate *update, uint64_t state); void meta_kms_connector_set_underscanning (MetaKmsConnector *connector, MetaKmsUpdate *update, uint64_t hborder, uint64_t vborder); void meta_kms_connector_unset_underscanning (MetaKmsConnector *connector, MetaKmsUpdate *update); MetaConnectorType meta_kms_connector_get_connector_type (MetaKmsConnector *connector); uint32_t meta_kms_connector_get_id (MetaKmsConnector *connector); const char * meta_kms_connector_get_name (MetaKmsConnector *connector); gboolean meta_kms_connector_can_clone (MetaKmsConnector *connector, MetaKmsConnector *other_connector); const MetaKmsConnectorState * meta_kms_connector_get_current_state (MetaKmsConnector *connector); gboolean meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector); #endif /* META_KMS_CONNECTOR_H */ muffin-6.4.1/src/backends/native/meta-gpu-kms.h0000664000175000017500000000705414723361714020257 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_GPU_KMS_H #define META_GPU_KMS_H #include #include #include #include "backends/meta-gpu.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-kms-types.h" #define META_TYPE_GPU_KMS (meta_gpu_kms_get_type ()) G_DECLARE_FINAL_TYPE (MetaGpuKms, meta_gpu_kms, META, GPU_KMS, MetaGpu) typedef struct _MetaGpuKmsFlipClosureContainer MetaGpuKmsFlipClosureContainer; MetaGpuKms * meta_gpu_kms_new (MetaBackendNative *backend_native, MetaKmsDevice *kms_device, GError **error); gboolean meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, MetaCrtc *crtc); gboolean meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms); gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, GError **error); MetaKmsDevice * meta_gpu_kms_get_kms_device (MetaGpuKms *gpu_kms); int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms); uint32_t meta_gpu_kms_get_id (MetaGpuKms *gpu_kms); const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms); int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms); void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, uint64_t state, MetaKmsUpdate *kms_update); MetaCrtcMode * meta_gpu_kms_get_mode_from_drm_mode (MetaGpuKms *gpu_kms, const drmModeModeInfo *drm_mode); gboolean meta_drm_mode_equal (const drmModeModeInfo *one, const drmModeModeInfo *two); MetaGpuKmsFlipClosureContainer * meta_gpu_kms_wrap_flip_closure (MetaGpuKms *gpu_kms, MetaCrtc *crtc, GClosure *flip_closure); void meta_gpu_kms_flip_closure_container_free (MetaGpuKmsFlipClosureContainer *closure_container); typedef struct _MetaGpuKmsFBArgs { uint32_t width; uint32_t height; uint32_t format; uint32_t handles[4]; uint32_t offsets[4]; uint32_t strides[4]; uint64_t modifiers[4]; } MetaGpuKmsFBArgs; gboolean meta_gpu_kms_add_fb (MetaGpuKms *gpu_kms, gboolean use_modifiers, const MetaGpuKmsFBArgs *args, uint32_t *fb_id_out, GError **error); #endif /* META_GPU_KMS_H */ muffin-6.4.1/src/backends/native/meta-virtual-input-device-native.c0000664000175000017500000006607214723361714024240 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #include "config.h" #include #include #include "backends/native/meta-input-device-native.h" #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-seat-native.h" #include "backends/native/meta-virtual-input-device-native.h" #include "clutter/clutter-mutter.h" #include "meta/util.h" enum { PROP_0, PROP_SEAT, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _MetaVirtualInputDeviceNative { ClutterVirtualInputDevice parent; ClutterInputDevice *device; MetaSeatNative *seat; int button_count[KEY_CNT]; }; G_DEFINE_TYPE (MetaVirtualInputDeviceNative, meta_virtual_input_device_native, CLUTTER_TYPE_VIRTUAL_INPUT_DEVICE) typedef enum _EvdevButtonType { EVDEV_BUTTON_TYPE_NONE, EVDEV_BUTTON_TYPE_KEY, EVDEV_BUTTON_TYPE_BUTTON, } EvdevButtonType; static int update_button_count (MetaVirtualInputDeviceNative *virtual_evdev, uint32_t button, uint32_t state) { if (state) return ++virtual_evdev->button_count[button]; else return --virtual_evdev->button_count[button]; } static EvdevButtonType get_button_type (uint16_t code) { switch (code) { case BTN_TOOL_PEN: case BTN_TOOL_RUBBER: case BTN_TOOL_BRUSH: case BTN_TOOL_PENCIL: case BTN_TOOL_AIRBRUSH: case BTN_TOOL_MOUSE: case BTN_TOOL_LENS: case BTN_TOOL_QUINTTAP: case BTN_TOOL_DOUBLETAP: case BTN_TOOL_TRIPLETAP: case BTN_TOOL_QUADTAP: case BTN_TOOL_FINGER: case BTN_TOUCH: return EVDEV_BUTTON_TYPE_NONE; } if (code >= KEY_ESC && code <= KEY_MICMUTE) return EVDEV_BUTTON_TYPE_KEY; if (code >= BTN_MISC && code <= BTN_GEAR_UP) return EVDEV_BUTTON_TYPE_BUTTON; if (code >= KEY_OK && code <= KEY_LIGHTS_TOGGLE) return EVDEV_BUTTON_TYPE_KEY; if (code >= BTN_DPAD_UP && code <= BTN_DPAD_RIGHT) return EVDEV_BUTTON_TYPE_BUTTON; if (code >= KEY_ALS_TOGGLE && code <= KEY_KBDINPUTASSIST_CANCEL) return EVDEV_BUTTON_TYPE_KEY; if (code >= BTN_TRIGGER_HAPPY && code <= BTN_TRIGGER_HAPPY40) return EVDEV_BUTTON_TYPE_BUTTON; return EVDEV_BUTTON_TYPE_NONE; } static void release_pressed_buttons (ClutterVirtualInputDevice *virtual_device) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); int code; uint64_t time_us; time_us = g_get_monotonic_time (); meta_topic (META_DEBUG_INPUT, "Releasing pressed buttons while destroying virtual input device " "(device %p)\n", virtual_device); for (code = 0; code < G_N_ELEMENTS (virtual_evdev->button_count); code++) { if (virtual_evdev->button_count[code] == 0) continue; switch (get_button_type (code)) { case EVDEV_BUTTON_TYPE_KEY: clutter_virtual_input_device_notify_key (virtual_device, time_us, code, CLUTTER_KEY_STATE_RELEASED); break; case EVDEV_BUTTON_TYPE_BUTTON: clutter_virtual_input_device_notify_button (virtual_device, time_us, code, CLUTTER_BUTTON_STATE_RELEASED); break; case EVDEV_BUTTON_TYPE_NONE: g_assert_not_reached (); } } } static void meta_virtual_input_device_native_notify_relative_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); meta_seat_native_notify_relative_motion (virtual_evdev->seat, virtual_evdev->device, time_us, dx, dy, dx, dy); } static void meta_virtual_input_device_native_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double x, double y) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); meta_seat_native_notify_absolute_motion (virtual_evdev->seat, virtual_evdev->device, time_us, x, y, NULL); } static int translate_to_evdev_button (int clutter_button) { switch (clutter_button) { case CLUTTER_BUTTON_PRIMARY: return BTN_LEFT; case CLUTTER_BUTTON_SECONDARY: return BTN_RIGHT; case CLUTTER_BUTTON_MIDDLE: return BTN_MIDDLE; default: /* * For compatibility reasons, all additional buttons go after the old * 4-7 scroll ones. */ return clutter_button + (BTN_LEFT - 1) - 4; } } static void meta_virtual_input_device_native_notify_button (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t button, ClutterButtonState button_state) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); int button_count; int evdev_button; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); evdev_button = translate_to_evdev_button (button); if (get_button_type (evdev_button) != EVDEV_BUTTON_TYPE_BUTTON) { g_warning ("Unknown/invalid virtual device button 0x%x pressed", evdev_button); return; } button_count = update_button_count (virtual_evdev, evdev_button, button_state); if (button_count < 0 || button_count > 1) { g_warning ("Received multiple virtual 0x%x button %s (ignoring)", evdev_button, button_state == CLUTTER_BUTTON_STATE_PRESSED ? "presses" : "releases"); update_button_count (virtual_evdev, evdev_button, 1 - button_state); return; } meta_topic (META_DEBUG_INPUT, "Emitting virtual button-%s of button 0x%x (device %p)\n", button_state == CLUTTER_BUTTON_STATE_PRESSED ? "press" : "release", evdev_button, virtual_device); meta_seat_native_notify_button (virtual_evdev->seat, virtual_evdev->device, time_us, evdev_button, button_state); } static void meta_virtual_input_device_native_notify_key (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t key, ClutterKeyState key_state) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); int key_count; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); if (get_button_type (key) != EVDEV_BUTTON_TYPE_KEY) { g_warning ("Unknown/invalid virtual device key 0x%x pressed\n", key); return; } key_count = update_button_count (virtual_evdev, key, key_state); if (key_count < 0 || key_count > 1) { g_warning ("Received multiple virtual 0x%x key %s (ignoring)", key, key_state == CLUTTER_KEY_STATE_PRESSED ? "presses" : "releases"); update_button_count (virtual_evdev, key, 1 - key_state); return; } meta_topic (META_DEBUG_INPUT, "Emitting virtual key-%s of key 0x%x (device %p)\n", key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release", key, virtual_device); meta_seat_native_notify_key (virtual_evdev->seat, virtual_evdev->device, time_us, key, key_state, TRUE); } static gboolean pick_keycode_for_keyval_in_current_group (ClutterVirtualInputDevice *virtual_device, guint keyval, guint *keycode_out, guint *level_out) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); ClutterBackend *backend; ClutterKeymap *keymap; struct xkb_keymap *xkb_keymap; struct xkb_state *state; guint keycode, layout; xkb_keycode_t min_keycode, max_keycode; backend = clutter_get_default_backend (); keymap = clutter_seat_get_keymap (clutter_backend_get_default_seat (backend)); xkb_keymap = meta_keymap_native_get_keyboard_map (META_KEYMAP_NATIVE (keymap)); state = virtual_evdev->seat->xkb; layout = xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE); min_keycode = xkb_keymap_min_keycode (xkb_keymap); max_keycode = xkb_keymap_max_keycode (xkb_keymap); for (keycode = min_keycode; keycode < max_keycode; keycode++) { gint num_levels, level; num_levels = xkb_keymap_num_levels_for_key (xkb_keymap, keycode, layout); for (level = 0; level < num_levels; level++) { const xkb_keysym_t *syms; gint num_syms, sym; num_syms = xkb_keymap_key_get_syms_by_level (xkb_keymap, keycode, layout, level, &syms); for (sym = 0; sym < num_syms; sym++) { if (syms[sym] == keyval) { *keycode_out = keycode; if (level_out) *level_out = level; return TRUE; } } } } return FALSE; } static void apply_level_modifiers (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t level, uint32_t key_state) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); guint keysym, keycode, evcode; if (level == 0) return; if (level == 1) { keysym = XKB_KEY_Shift_L; } else if (level == 2) { keysym = XKB_KEY_ISO_Level3_Shift; } else { g_warning ("Unhandled level: %d\n", level); return; } if (!pick_keycode_for_keyval_in_current_group (virtual_device, keysym, &keycode, NULL)) return; clutter_input_device_keycode_to_evdev (virtual_evdev->device, keycode, &evcode); meta_topic (META_DEBUG_INPUT, "Emitting virtual key-%s of modifier key 0x%x (device %p)\n", key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release", evcode, virtual_device); meta_seat_native_notify_key (virtual_evdev->seat, virtual_evdev->device, time_us, evcode, key_state, TRUE); } static void meta_virtual_input_device_native_notify_keyval (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t keyval, ClutterKeyState key_state) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); int key_count; guint keycode = 0, level = 0, evcode = 0; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); if (!pick_keycode_for_keyval_in_current_group (virtual_device, keyval, &keycode, &level)) { g_warning ("No keycode found for keyval %x in current group", keyval); return; } clutter_input_device_keycode_to_evdev (virtual_evdev->device, keycode, &evcode); if (get_button_type (evcode) != EVDEV_BUTTON_TYPE_KEY) { g_warning ("Unknown/invalid virtual device key 0x%x pressed\n", evcode); return; } key_count = update_button_count (virtual_evdev, evcode, key_state); if (key_count < 0 || key_count > 1) { g_warning ("Received multiple virtual 0x%x key %s (ignoring)", evcode, key_state == CLUTTER_KEY_STATE_PRESSED ? "presses" : "releases"); update_button_count (virtual_evdev, evcode, 1 - key_state); return; } meta_topic (META_DEBUG_INPUT, "Emitting virtual key-%s of key 0x%x with modifier level %d, " "press count %d (device %p)\n", key_state == CLUTTER_KEY_STATE_PRESSED ? "press" : "release", evcode, level, key_count, virtual_device); if (key_state) apply_level_modifiers (virtual_device, time_us, level, key_state); meta_seat_native_notify_key (virtual_evdev->seat, virtual_evdev->device, time_us, evcode, key_state, TRUE); if (!key_state) apply_level_modifiers (virtual_device, time_us, level, key_state); } static void direction_to_discrete (ClutterScrollDirection direction, double *discrete_dx, double *discrete_dy) { switch (direction) { case CLUTTER_SCROLL_UP: *discrete_dx = 0.0; *discrete_dy = -1.0; break; case CLUTTER_SCROLL_DOWN: *discrete_dx = 0.0; *discrete_dy = 1.0; break; case CLUTTER_SCROLL_LEFT: *discrete_dx = -1.0; *discrete_dy = 0.0; break; case CLUTTER_SCROLL_RIGHT: *discrete_dx = 1.0; *discrete_dy = 0.0; break; case CLUTTER_SCROLL_SMOOTH: g_assert_not_reached (); break; } } static void meta_virtual_input_device_native_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); double discrete_dx = 0.0, discrete_dy = 0.0; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); direction_to_discrete (direction, &discrete_dx, &discrete_dy); meta_seat_native_notify_discrete_scroll (virtual_evdev->seat, virtual_evdev->device, time_us, discrete_dx, discrete_dy, scroll_source); } static void meta_virtual_input_device_native_notify_scroll_continuous (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); meta_seat_native_notify_scroll_continuous (virtual_evdev->seat, virtual_evdev->device, time_us, dx, dy, scroll_source, CLUTTER_SCROLL_FINISHED_NONE); } static void meta_virtual_input_device_native_notify_touch_down (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot, double x, double y) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (virtual_evdev->device); MetaTouchState *touch_state; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); touch_state = meta_input_device_native_acquire_touch_state (device_evdev, device_slot); if (!touch_state) return; touch_state->coords.x = x; touch_state->coords.y = y; meta_seat_native_notify_touch_event (virtual_evdev->seat, virtual_evdev->device, CLUTTER_TOUCH_BEGIN, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); } static void meta_virtual_input_device_native_notify_touch_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot, double x, double y) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (virtual_evdev->device); MetaTouchState *touch_state; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); touch_state = meta_input_device_native_lookup_touch_state (device_evdev, device_slot); if (!touch_state) return; touch_state->coords.x = x; touch_state->coords.y = y; meta_seat_native_notify_touch_event (virtual_evdev->seat, virtual_evdev->device, CLUTTER_TOUCH_BEGIN, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); } static void meta_virtual_input_device_native_notify_touch_up (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (virtual_device); MetaInputDeviceNative *device_evdev = META_INPUT_DEVICE_NATIVE (virtual_evdev->device); MetaTouchState *touch_state; g_return_if_fail (virtual_evdev->device != NULL); if (time_us == CLUTTER_CURRENT_TIME) time_us = g_get_monotonic_time (); touch_state = meta_input_device_native_lookup_touch_state (device_evdev, device_slot); if (!touch_state) return; meta_seat_native_notify_touch_event (virtual_evdev->seat, virtual_evdev->device, CLUTTER_TOUCH_BEGIN, time_us, touch_state->seat_slot, touch_state->coords.x, touch_state->coords.y); meta_input_device_native_release_touch_state (device_evdev, touch_state); } static void meta_virtual_input_device_native_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (object); switch (prop_id) { case PROP_SEAT: g_value_set_pointer (value, virtual_evdev->seat); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_virtual_input_device_native_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (object); switch (prop_id) { case PROP_SEAT: virtual_evdev->seat = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_virtual_input_device_native_constructed (GObject *object) { ClutterVirtualInputDevice *virtual_device = CLUTTER_VIRTUAL_INPUT_DEVICE (object); MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (object); ClutterInputDeviceType device_type; ClutterStage *stage; device_type = clutter_virtual_input_device_get_device_type (virtual_device); meta_topic (META_DEBUG_INPUT, "Creating new virtual input device of type %d (%p)\n", device_type, virtual_device); virtual_evdev->device = meta_input_device_native_new_virtual (virtual_evdev->seat, device_type, CLUTTER_INPUT_MODE_SLAVE); stage = meta_seat_native_get_stage (virtual_evdev->seat); _clutter_input_device_set_stage (virtual_evdev->device, stage); g_signal_emit_by_name (virtual_evdev->seat, "device-added", virtual_evdev->device); } static void meta_virtual_input_device_native_dispose (GObject *object) { ClutterVirtualInputDevice *virtual_device = CLUTTER_VIRTUAL_INPUT_DEVICE (object); MetaVirtualInputDeviceNative *virtual_evdev = META_VIRTUAL_INPUT_DEVICE_NATIVE (object); GObjectClass *object_class = G_OBJECT_CLASS (meta_virtual_input_device_native_parent_class); if (virtual_evdev->device) { release_pressed_buttons (virtual_device); g_signal_emit_by_name (virtual_evdev->seat, "device-removed", virtual_evdev->device); g_clear_object (&virtual_evdev->device); } object_class->dispose (object); } static void meta_virtual_input_device_native_init (MetaVirtualInputDeviceNative *virtual_device_evdev) { } static void meta_virtual_input_device_native_class_init (MetaVirtualInputDeviceNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterVirtualInputDeviceClass *virtual_input_device_class = CLUTTER_VIRTUAL_INPUT_DEVICE_CLASS (klass); object_class->get_property = meta_virtual_input_device_native_get_property; object_class->set_property = meta_virtual_input_device_native_set_property; object_class->constructed = meta_virtual_input_device_native_constructed; object_class->dispose = meta_virtual_input_device_native_dispose; virtual_input_device_class->notify_relative_motion = meta_virtual_input_device_native_notify_relative_motion; virtual_input_device_class->notify_absolute_motion = meta_virtual_input_device_native_notify_absolute_motion; virtual_input_device_class->notify_button = meta_virtual_input_device_native_notify_button; virtual_input_device_class->notify_key = meta_virtual_input_device_native_notify_key; virtual_input_device_class->notify_keyval = meta_virtual_input_device_native_notify_keyval; virtual_input_device_class->notify_discrete_scroll = meta_virtual_input_device_native_notify_discrete_scroll; virtual_input_device_class->notify_scroll_continuous = meta_virtual_input_device_native_notify_scroll_continuous; virtual_input_device_class->notify_touch_down = meta_virtual_input_device_native_notify_touch_down; virtual_input_device_class->notify_touch_motion = meta_virtual_input_device_native_notify_touch_motion; virtual_input_device_class->notify_touch_up = meta_virtual_input_device_native_notify_touch_up; obj_props[PROP_SEAT] = g_param_spec_pointer ("seat", "Seat", "Seat", CLUTTER_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/src/backends/native/meta-kms-page-flip-private.h0000664000175000017500000000467114723361714023002 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_PAGE_FLIP_H #define META_KMS_PAGE_FLIP_H #include #include "backends/native/meta-kms-types.h" typedef struct _MetaKmsPageFlipData MetaKmsPageFlipData; typedef void (* MetaPageFlipDataFeedbackFunc) (MetaKmsPageFlipData *page_flip_data); MetaKmsPageFlipData * meta_kms_page_flip_data_new (MetaKmsImpl *impl, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data); MetaKmsPageFlipData * meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data); void meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data); MetaKmsImpl * meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data); void meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data, unsigned int sequence, unsigned int sec, unsigned int usec); void meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data); void meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data); void meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data, const GError *error); void meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data, GError *error); #endif /* META_KMS_PAGE_FLIP_H */ muffin-6.4.1/src/backends/native/meta-drm-buffer-import.c0000664000175000017500000001242214723361714022223 0ustar fabiofabio/* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2016,2017 Red Hat * Copyright (C) 2018,2019 DisplayLink (UK) Ltd. * Copyright (C) 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/native/meta-drm-buffer-import.h" #include #include #include #include #include "backends/native/meta-drm-buffer-gbm.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-renderer-native.h" #define INVALID_FB_ID 0U struct _MetaDrmBufferImport { MetaDrmBuffer parent; MetaGpuKms *gpu_kms; MetaDrmBufferGbm *importee; uint32_t fb_id; }; G_DEFINE_TYPE (MetaDrmBufferImport, meta_drm_buffer_import, META_TYPE_DRM_BUFFER) static struct gbm_bo * dmabuf_to_gbm_bo (struct gbm_device *importer, int dmabuf_fd, uint32_t width, uint32_t height, uint32_t stride, uint32_t format) { struct gbm_import_fd_data data = { .fd = dmabuf_fd, .width = width, .height = height, .stride = stride, .format = format }; return gbm_bo_import (importer, GBM_BO_IMPORT_FD, &data, GBM_BO_USE_SCANOUT); } static gboolean import_gbm_buffer (MetaDrmBufferImport *buffer_import, GError **error) { MetaGpuKmsFBArgs fb_args = { 0, }; struct gbm_bo *primary_bo; struct gbm_device *importer; struct gbm_bo *imported_bo; int dmabuf_fd; gboolean ret; g_assert (buffer_import->fb_id == INVALID_FB_ID); importer = meta_gbm_device_from_gpu (buffer_import->gpu_kms); primary_bo = meta_drm_buffer_gbm_get_bo (buffer_import->importee); dmabuf_fd = gbm_bo_get_fd (primary_bo); if (dmabuf_fd == -1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "getting dmabuf fd failed"); return FALSE; } fb_args.strides[0] = gbm_bo_get_stride (primary_bo); fb_args.width = gbm_bo_get_width (primary_bo); fb_args.height = gbm_bo_get_height (primary_bo); fb_args.format = gbm_bo_get_format (primary_bo); imported_bo = dmabuf_to_gbm_bo (importer, dmabuf_fd, fb_args.width, fb_args.height, fb_args.strides[0], fb_args.format); if (!imported_bo) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "importing dmabuf fd failed"); ret = FALSE; goto out_close; } fb_args.handles[0] = gbm_bo_get_handle (imported_bo).u32; ret = meta_gpu_kms_add_fb (buffer_import->gpu_kms, FALSE /* use_modifiers */, &fb_args, &buffer_import->fb_id, error); gbm_bo_destroy (imported_bo); out_close: close (dmabuf_fd); return ret; } MetaDrmBufferImport * meta_drm_buffer_import_new (MetaGpuKms *gpu_kms, MetaDrmBufferGbm *buffer_gbm, GError **error) { MetaDrmBufferImport *buffer_import; buffer_import = g_object_new (META_TYPE_DRM_BUFFER_IMPORT, NULL); buffer_import->gpu_kms = gpu_kms; g_set_object (&buffer_import->importee, buffer_gbm); if (!import_gbm_buffer (buffer_import, error)) { g_object_unref (buffer_import); return NULL; } return buffer_import; } static uint32_t meta_drm_buffer_import_get_fb_id (MetaDrmBuffer *buffer) { return META_DRM_BUFFER_IMPORT (buffer)->fb_id; } static void meta_drm_buffer_import_finalize (GObject *object) { MetaDrmBufferImport *buffer_import = META_DRM_BUFFER_IMPORT (object); if (buffer_import->fb_id != INVALID_FB_ID) { int kms_fd; kms_fd = meta_gpu_kms_get_fd (buffer_import->gpu_kms); drmModeRmFB (kms_fd, buffer_import->fb_id); } g_clear_object (&buffer_import->importee); G_OBJECT_CLASS (meta_drm_buffer_import_parent_class)->finalize (object); } static void meta_drm_buffer_import_init (MetaDrmBufferImport *buffer_import) { } static void meta_drm_buffer_import_class_init (MetaDrmBufferImportClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaDrmBufferClass *buffer_class = META_DRM_BUFFER_CLASS (klass); object_class->finalize = meta_drm_buffer_import_finalize; buffer_class->get_fb_id = meta_drm_buffer_import_get_fb_id; } muffin-6.4.1/src/backends/native/meta-launcher.h0000664000175000017500000000363214723361714020473 0ustar fabiofabio/* * Copyright (C) 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_LAUNCHER_H #define META_LAUNCHER_H #include typedef struct _MetaLauncher MetaLauncher; MetaLauncher *meta_launcher_new (GError **error); void meta_launcher_free (MetaLauncher *self); gboolean meta_launcher_activate_session (MetaLauncher *self, GError **error); gboolean meta_launcher_activate_vt (MetaLauncher *self, signed char vt, GError **error); const char * meta_launcher_get_seat_id (MetaLauncher *launcher); int meta_launcher_open_restricted (MetaLauncher *launcher, const char *path, GError **error); void meta_launcher_close_restricted (MetaLauncher *launcher, int fd); #endif /* META_LAUNCHER_H */ muffin-6.4.1/src/backends/native/meta-kms-impl.h0000664000175000017500000000445414723361714020426 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_IMPL_H #define META_KMS_IMPL_H #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms.h" #define META_TYPE_KMS_IMPL (meta_kms_impl_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaKmsImpl, meta_kms_impl, META, KMS_IMPL, GObject) struct _MetaKmsImplClass { GObjectClass parent_class; MetaKmsFeedback * (* process_update) (MetaKmsImpl *impl, MetaKmsUpdate *update); void (* handle_page_flip_callback) (MetaKmsImpl *impl, MetaKmsPageFlipData *page_flip_data); void (* discard_pending_page_flips) (MetaKmsImpl *impl); void (* dispatch_idle) (MetaKmsImpl *impl); void (* notify_device_created) (MetaKmsImpl *impl, MetaKmsDevice *impl_device); }; MetaKms * meta_kms_impl_get_kms (MetaKmsImpl *impl); MetaKmsFeedback * meta_kms_impl_process_update (MetaKmsImpl *impl, MetaKmsUpdate *update); void meta_kms_impl_handle_page_flip_callback (MetaKmsImpl *impl, MetaKmsPageFlipData *page_flip_data); void meta_kms_impl_discard_pending_page_flips (MetaKmsImpl *impl); void meta_kms_impl_dispatch_idle (MetaKmsImpl *impl); void meta_kms_impl_notify_device_created (MetaKmsImpl *impl, MetaKmsDevice *impl_device); #endif /* META_KMS_IMPL_H */ muffin-6.4.1/src/backends/native/meta-kms-utils.h0000664000175000017500000000226214723361714020620 0ustar fabiofabio/* * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_UTILS_H #define META_KMS_UTILS_H #include #include #include typedef struct _MetaDrmFormatBuf { char s[5]; } MetaDrmFormatBuf; float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *drm_mode); const char * meta_drm_format_to_string (MetaDrmFormatBuf *tmp, uint32_t drm_format); #endif /* META_KMS_UTILS_H */ muffin-6.4.1/src/backends/native/meta-kms-crtc.c0000664000175000017500000001556114723361714020414 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-update-private.h" struct _MetaKmsCrtc { GObject parent; MetaKmsDevice *device; uint32_t id; int idx; MetaKmsCrtcState current_state; }; G_DEFINE_TYPE (MetaKmsCrtc, meta_kms_crtc, G_TYPE_OBJECT) void meta_kms_crtc_set_gamma (MetaKmsCrtc *crtc, MetaKmsUpdate *update, int size, const uint16_t *red, const uint16_t *green, const uint16_t *blue) { meta_kms_update_set_crtc_gamma (update, crtc, size, red, green, blue); } MetaKmsDevice * meta_kms_crtc_get_device (MetaKmsCrtc *crtc) { return crtc->device; } const MetaKmsCrtcState * meta_kms_crtc_get_current_state (MetaKmsCrtc *crtc) { return &crtc->current_state; } uint32_t meta_kms_crtc_get_id (MetaKmsCrtc *crtc) { return crtc->id; } int meta_kms_crtc_get_idx (MetaKmsCrtc *crtc) { return crtc->idx; } static void read_gamma_state (MetaKmsCrtc *crtc, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { MetaKmsCrtcState *current_state = &crtc->current_state; if (current_state->gamma.size != drm_crtc->gamma_size) { current_state->gamma.size = drm_crtc->gamma_size; current_state->gamma.red = g_realloc_n (current_state->gamma.red, drm_crtc->gamma_size, sizeof (uint16_t)); current_state->gamma.green = g_realloc_n (current_state->gamma.green, drm_crtc->gamma_size, sizeof (uint16_t)); current_state->gamma.blue = g_realloc_n (current_state->gamma.blue, drm_crtc->gamma_size, sizeof (uint16_t)); } drmModeCrtcGetGamma (meta_kms_impl_device_get_fd (impl_device), crtc->id, current_state->gamma.size, current_state->gamma.red, current_state->gamma.green, current_state->gamma.blue); } static void meta_kms_crtc_read_state (MetaKmsCrtc *crtc, MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc) { crtc->current_state.rect = (MetaRectangle) { .x = drm_crtc->x, .y = drm_crtc->y, .width = drm_crtc->width, .height = drm_crtc->height, }; crtc->current_state.is_drm_mode_valid = drm_crtc->mode_valid; crtc->current_state.drm_mode = drm_crtc->mode; read_gamma_state (crtc, impl_device, drm_crtc); } void meta_kms_crtc_update_state (MetaKmsCrtc *crtc) { MetaKmsImplDevice *impl_device; drmModeCrtc *drm_crtc; impl_device = meta_kms_device_get_impl_device (crtc->device); drm_crtc = drmModeGetCrtc (meta_kms_impl_device_get_fd (impl_device), crtc->id); if (!drm_crtc) { crtc->current_state.rect = (MetaRectangle) { }; crtc->current_state.is_drm_mode_valid = FALSE; return; } meta_kms_crtc_read_state (crtc, impl_device, drm_crtc); drmModeFreeCrtc (drm_crtc); } static void clear_gamma_state (MetaKmsCrtc *crtc) { crtc->current_state.gamma.size = 0; g_clear_pointer (&crtc->current_state.gamma.red, g_free); g_clear_pointer (&crtc->current_state.gamma.green, g_free); g_clear_pointer (&crtc->current_state.gamma.blue, g_free); } void meta_kms_crtc_predict_state (MetaKmsCrtc *crtc, MetaKmsUpdate *update) { GList *mode_sets; GList *crtc_gammas; GList *l; mode_sets = meta_kms_update_get_mode_sets (update); for (l = mode_sets; l; l = l->next) { MetaKmsModeSet *mode_set = l->data; if (mode_set->crtc != crtc) continue; if (mode_set->drm_mode) { MetaKmsPlaneAssignment *plane_assignment; plane_assignment = meta_kms_update_get_primary_plane_assignment (update, crtc); crtc->current_state.rect = meta_fixed_16_rectangle_to_rectangle (plane_assignment->src_rect); crtc->current_state.is_drm_mode_valid = TRUE; crtc->current_state.drm_mode = *mode_set->drm_mode; } else { crtc->current_state.rect = (MetaRectangle) { 0 }; crtc->current_state.is_drm_mode_valid = FALSE; crtc->current_state.drm_mode = (drmModeModeInfo) { 0 }; } break; } crtc_gammas = meta_kms_update_get_crtc_gammas (update); for (l = crtc_gammas; l; l = l->next) { MetaKmsCrtcGamma *gamma = l->data; if (gamma->crtc != crtc) continue; clear_gamma_state (crtc); crtc->current_state.gamma.size = gamma->size; crtc->current_state.gamma.red = g_memdup2 (gamma->red, gamma->size * sizeof (uint16_t)); crtc->current_state.gamma.green = g_memdup2 (gamma->green, gamma->size * sizeof (uint16_t)); crtc->current_state.gamma.blue = g_memdup2 (gamma->blue, gamma->size * sizeof (uint16_t)); break; } } MetaKmsCrtc * meta_kms_crtc_new (MetaKmsImplDevice *impl_device, drmModeCrtc *drm_crtc, int idx) { MetaKmsCrtc *crtc; crtc = g_object_new (META_TYPE_KMS_CRTC, NULL); crtc->device = meta_kms_impl_device_get_device (impl_device); crtc->id = drm_crtc->crtc_id; crtc->idx = idx; meta_kms_crtc_read_state (crtc, impl_device, drm_crtc); return crtc; } static void meta_kms_crtc_finalize (GObject *object) { MetaKmsCrtc *crtc = META_KMS_CRTC (object); clear_gamma_state (crtc); G_OBJECT_CLASS (meta_kms_crtc_parent_class)->finalize (object); } static void meta_kms_crtc_init (MetaKmsCrtc *crtc) { } static void meta_kms_crtc_class_init (MetaKmsCrtcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_crtc_finalize; } muffin-6.4.1/src/backends/native/meta-kms-utils.c0000664000175000017500000000437314723361714020620 0ustar fabiofabio/* * Copyright (C) 2013-2019 Red Hat * Copyright (c) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-utils.h" #include #include /* added in libdrm 2.4.95 */ #ifndef DRM_FORMAT_INVALID #define DRM_FORMAT_INVALID 0 #endif float meta_calculate_drm_mode_refresh_rate (const drmModeModeInfo *drm_mode) { float refresh = 0.0; if (drm_mode->htotal > 0 && drm_mode->vtotal > 0) { /* Calculate refresh rate in milliHz first for extra precision. */ refresh = (drm_mode->clock * 1000000LL) / drm_mode->htotal; refresh += (drm_mode->vtotal / 2); refresh /= drm_mode->vtotal; if (drm_mode->vscan > 1) refresh /= drm_mode->vscan; refresh /= 1000.0; } return refresh; } /** * meta_drm_format_to_string: * @tmp: temporary buffer * @drm_format: DRM fourcc pixel format * * Returns a pointer to a string naming the given pixel format, * usually a pointer to the temporary buffer but not always. * Invalid formats may return nonsense names. * * When calling this, allocate one MetaDrmFormatBuf on the stack to * be used as the temporary buffer. */ const char * meta_drm_format_to_string (MetaDrmFormatBuf *tmp, uint32_t drm_format) { int i; if (drm_format == DRM_FORMAT_INVALID) return "INVALID"; G_STATIC_ASSERT (sizeof (tmp->s) == 5); for (i = 0; i < 4; i++) { char c = (drm_format >> (i * 8)) & 0xff; tmp->s[i] = g_ascii_isgraph (c) ? c : '.'; } tmp->s[i] = 0; return tmp->s; } muffin-6.4.1/src/backends/native/meta-input-device-native.h0000664000175000017500000001333614723361714022554 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corp. * Copyright (C) 2014 Jonas Ådahl * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Damien Lespiau * Author: Jonas Ådahl */ #ifndef META_INPUT_DEVICE_NATIVE_H #define META_INPUT_DEVICE_NATIVE_H #include #include "backends/meta-input-device-private.h" #include "backends/native/meta-seat-native.h" #include "clutter/clutter-mutter.h" #define META_TYPE_INPUT_DEVICE_NATIVE meta_input_device_native_get_type() #define META_INPUT_DEVICE_NATIVE(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ META_TYPE_INPUT_DEVICE_NATIVE, MetaInputDeviceNative)) #define META_INPUT_DEVICE_NATIVE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ META_TYPE_INPUT_DEVICE_NATIVE, MetaInputDeviceNativeClass)) #define META_IS_INPUT_DEVICE_NATIVE(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ META_TYPE_INPUT_DEVICE_NATIVE)) #define META_IS_INPUT_DEVICE_NATIVE_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ META_TYPE_INPUT_DEVICE_NATIVE)) #define META_INPUT_DEVICE_NATIVE_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ META_TYPE_INPUT_DEVICE_NATIVE, MetaInputDeviceNativeClass)) typedef struct _MetaInputDeviceNative MetaInputDeviceNative; typedef struct _MetaInputDeviceNativeClass MetaInputDeviceNativeClass; struct _MetaInputDeviceNative { ClutterInputDevice parent; struct libinput_device *libinput_device; MetaSeatNative *seat; ClutterInputDeviceTool *last_tool; cairo_matrix_t device_matrix; double device_aspect_ratio; /* w:h */ double output_ratio; /* w:h */ GHashTable *touches; /* Keyboard a11y */ ClutterKeyboardA11yFlags a11y_flags; GList *slow_keys_list; guint debounce_timer; uint16_t debounce_key; xkb_mod_mask_t stickykeys_depressed_mask; xkb_mod_mask_t stickykeys_latched_mask; xkb_mod_mask_t stickykeys_locked_mask; guint toggle_slowkeys_timer; uint16_t shift_count; uint32_t last_shift_time; int mousekeys_btn; gboolean mousekeys_btn_states[3]; uint32_t mousekeys_first_motion_time; /* ms */ uint32_t mousekeys_last_motion_time; /* ms */ guint mousekeys_init_delay; guint mousekeys_accel_time; guint mousekeys_max_speed; double mousekeys_curve_factor; guint move_mousekeys_timer; uint16_t last_mousekeys_key; }; struct _MetaInputDeviceNativeClass { ClutterInputDeviceClass parent_class; }; GType meta_input_device_native_get_type (void) G_GNUC_CONST; ClutterInputDevice * meta_input_device_native_new (MetaSeatNative *seat, struct libinput_device *libinput_device); ClutterInputDevice * meta_input_device_native_new_virtual (MetaSeatNative *seat, ClutterInputDeviceType type, ClutterInputMode mode); MetaSeatNative * meta_input_device_native_get_seat (MetaInputDeviceNative *device); void meta_input_device_native_update_leds (MetaInputDeviceNative *device, enum libinput_led leds); ClutterInputDeviceType meta_input_device_native_determine_type (struct libinput_device *libinput_device); void meta_input_device_native_translate_coordinates (ClutterInputDevice *device, ClutterStage *stage, float *x, float *y); void meta_input_device_native_apply_kbd_a11y_settings (MetaInputDeviceNative *device, ClutterKbdA11ySettings *settings); MetaTouchState * meta_input_device_native_acquire_touch_state (MetaInputDeviceNative *device, int device_slot); MetaTouchState * meta_input_device_native_lookup_touch_state (MetaInputDeviceNative *device, int device_slot); void meta_input_device_native_release_touch_state (MetaInputDeviceNative *device, MetaTouchState *touch_state); void meta_input_device_native_release_touch_slots (MetaInputDeviceNative *device_evdev, uint64_t time_us); void meta_input_device_native_a11y_maybe_notify_toggle_keys (MetaInputDeviceNative *device_evdev); struct libinput_device * meta_input_device_native_get_libinput_device (ClutterInputDevice *device); #endif /* META_INPUT_DEVICE_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-output-kms.h0000664000175000017500000000371014723361714021017 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_OUTPUT_KMS_H #define META_OUTPUT_KMS_H #include "backends/meta-output.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-types.h" void meta_output_kms_set_power_save_mode (MetaOutput *output, uint64_t dpms_state, MetaKmsUpdate *kms_update); void meta_output_kms_set_underscan (MetaOutput *output, MetaKmsUpdate *kms_update); gboolean meta_output_kms_can_clone (MetaOutput *output, MetaOutput *other_output); MetaKmsConnector * meta_output_kms_get_kms_connector (MetaOutput *output); uint32_t meta_output_kms_get_connector_id (MetaOutput *output); GBytes * meta_output_kms_read_edid (MetaOutput *output); MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms, MetaKmsConnector *kms_connector, MetaOutput *old_output, GError **error); #endif /* META_OUTPUT_KMS_H */ muffin-6.4.1/src/backends/native/meta-udev.c0000664000175000017500000001405014723361714017624 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/native/meta-udev.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-launcher.h" #define DRM_CARD_UDEV_DEVICE_TYPE "drm_minor" enum { HOTPLUG, DEVICE_ADDED, DEVICE_REMOVED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaUdev { GObject parent; MetaBackendNative *backend_native; GUdevClient *gudev_client; gulong uevent_handler_id; }; G_DEFINE_TYPE (MetaUdev, meta_udev, G_TYPE_OBJECT) gboolean meta_is_udev_device_platform_device (GUdevDevice *device) { g_autoptr (GUdevDevice) platform_device = NULL; platform_device = g_udev_device_get_parent_with_subsystem (device, "platform", NULL); return !!platform_device; } gboolean meta_is_udev_device_boot_vga (GUdevDevice *device) { g_autoptr (GUdevDevice) pci_device = NULL; pci_device = g_udev_device_get_parent_with_subsystem (device, "pci", NULL); if (!pci_device) return FALSE; return g_udev_device_get_sysfs_attr_as_int (pci_device, "boot_vga") == 1; } gboolean meta_udev_is_drm_device (MetaUdev *udev, GUdevDevice *device) { MetaLauncher *launcher = meta_backend_native_get_launcher (udev->backend_native); const char *seat_id; const char *device_type; const char *device_seat; /* Filter out devices that are not character device, like card0-VGA-1. */ if (g_udev_device_get_device_type (device) != G_UDEV_DEVICE_TYPE_CHAR) return FALSE; device_type = g_udev_device_get_property (device, "DEVTYPE"); if (g_strcmp0 (device_type, DRM_CARD_UDEV_DEVICE_TYPE) != 0) return FALSE; device_seat = g_udev_device_get_property (device, "ID_SEAT"); if (!device_seat) { /* When ID_SEAT is not set, it means seat0. */ device_seat = "seat0"; } /* Skip devices that do not belong to our seat. */ seat_id = meta_launcher_get_seat_id (launcher); if (g_strcmp0 (seat_id, device_seat)) return FALSE; return TRUE; } GList * meta_udev_list_drm_devices (MetaUdev *udev, GError **error) { g_autoptr (GUdevEnumerator) enumerator = NULL; GList *devices; GList *l; enumerator = g_udev_enumerator_new (udev->gudev_client); g_udev_enumerator_add_match_name (enumerator, "card*"); g_udev_enumerator_add_match_tag (enumerator, "seat"); /* * We need to explicitly match the subsystem for now. * https://bugzilla.gnome.org/show_bug.cgi?id=773224 */ g_udev_enumerator_add_match_subsystem (enumerator, "drm"); devices = g_udev_enumerator_execute (enumerator); if (!devices) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No drm devices found"); return FALSE; } for (l = devices; l;) { GUdevDevice *device = l->data; GList *l_next = l->next; if (!meta_udev_is_drm_device (udev, device)) { g_object_unref (device); devices = g_list_delete_link (devices, l); } l = l_next; } return devices; } static void on_uevent (GUdevClient *client, const char *action, GUdevDevice *device, gpointer user_data) { MetaUdev *udev = META_UDEV (user_data); if (!g_udev_device_get_device_file (device)) return; if (g_str_equal (action, "add")) g_signal_emit (udev, signals[DEVICE_ADDED], 0, device); else if (g_str_equal (action, "remove")) g_signal_emit (udev, signals[DEVICE_REMOVED], 0, device); if (g_udev_device_get_property_as_boolean (device, "HOTPLUG")) g_signal_emit (udev, signals[HOTPLUG], 0); } MetaUdev * meta_udev_new (MetaBackendNative *backend_native) { MetaUdev *udev; udev = g_object_new (META_TYPE_UDEV, NULL); udev->backend_native = backend_native; return udev; } static void meta_udev_finalize (GObject *object) { MetaUdev *udev = META_UDEV (object); g_clear_signal_handler (&udev->uevent_handler_id, udev->gudev_client); g_clear_object (&udev->gudev_client); G_OBJECT_CLASS (meta_udev_parent_class)->finalize (object); } static void meta_udev_init (MetaUdev *udev) { const char *subsystems[] = { "drm", NULL }; udev->gudev_client = g_udev_client_new (subsystems); udev->uevent_handler_id = g_signal_connect (udev->gudev_client, "uevent", G_CALLBACK (on_uevent), udev); } static void meta_udev_class_init (MetaUdevClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_udev_finalize; signals[HOTPLUG] = g_signal_new ("hotplug", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[DEVICE_ADDED] = g_signal_new ("device-added", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE); signals[DEVICE_REMOVED] = g_signal_new ("device-removed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_UDEV_TYPE_DEVICE); } muffin-6.4.1/src/backends/native/meta-kms-device-private.h0000664000175000017500000000260514723361714022370 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_DEVICE_PRIVATE_H #define META_KMS_DEVICE_PRIVATE_H #include "backends/native/meta-kms-types.h" MetaKmsImplDevice * meta_kms_device_get_impl_device (MetaKmsDevice *device); void meta_kms_device_update_states_in_impl (MetaKmsDevice *device); void meta_kms_device_predict_states_in_impl (MetaKmsDevice *device, MetaKmsUpdate *update); void meta_kms_device_add_fake_plane_in_impl (MetaKmsDevice *device, MetaKmsPlaneType plane_type, MetaKmsCrtc *crtc); #endif /* META_KMS_DEVICE_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-xkb-utils.c0000664000175000017500000000740214723361714020606 0ustar fabiofabio/* * Copyright (C) 2010 Intel Corporation. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * Authors: * Kristian Høgsberg * Damien Lespiau */ #include "config.h" #include "backends/native/meta-xkb-utils.h" #include "clutter/clutter-keysyms.h" #include "clutter/clutter-mutter.h" /* * _clutter_event_new_from_evdev: Create a new Clutter ClutterKeyEvent * @device: a ClutterInputDevice * @stage: the stage the event should be delivered to * @xkb: XKB rules to translate the event * @_time: timestamp of the event * @key: a key code coming from a Linux input device * @state: TRUE if a press event, FALSE if a release event * @modifer_state: in/out * * Translate @key to a #ClutterKeyEvent using rules from xbbcommon. * * Return value: the new #ClutterEvent */ ClutterEvent * meta_key_event_new_from_evdev (ClutterInputDevice *device, ClutterInputDevice *core_device, ClutterStage *stage, struct xkb_state *xkb_state, uint32_t button_state, uint32_t _time, xkb_keycode_t key, uint32_t state) { ClutterEvent *event; xkb_keysym_t sym; const xkb_keysym_t *syms; char buffer[8]; int n; if (state) event = clutter_event_new (CLUTTER_KEY_PRESS); else event = clutter_event_new (CLUTTER_KEY_RELEASE); /* We use a fixed offset of 8 because evdev starts KEY_* numbering from * 0, whereas X11's minimum keycode, for really stupid reasons, is 8. * So the evdev XKB rules are based on the keycodes all being shifted * upwards by 8. */ key += 8; n = xkb_key_get_syms (xkb_state, key, &syms); if (n == 1) sym = syms[0]; else sym = XKB_KEY_NoSymbol; event->key.stage = stage; event->key.time = _time; meta_xkb_translate_state (event, xkb_state, button_state); event->key.hardware_keycode = key; event->key.keyval = sym; clutter_event_set_device (event, core_device); clutter_event_set_source_device (event, device); n = xkb_keysym_to_utf8 (sym, buffer, sizeof (buffer)); if (n == 0) { /* not printable */ event->key.unicode_value = (gunichar) '\0'; } else { event->key.unicode_value = g_utf8_get_char_validated (buffer, n); if (event->key.unicode_value == -1 || event->key.unicode_value == -2) event->key.unicode_value = (gunichar) '\0'; } return event; } void meta_xkb_translate_state (ClutterEvent *event, struct xkb_state *state, uint32_t button_state) { _clutter_event_set_state_full (event, button_state, xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED), xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED), xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED), xkb_state_serialize_mods (state, XKB_STATE_MODS_EFFECTIVE) | button_state); } muffin-6.4.1/src/backends/native/meta-renderer-native.c0000664000175000017500000040760314723361714021765 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2016 Red Hat * Copyright (c) 2018,2019 DisplayLink (UK) Ltd. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, sublicense, and/or sell copies * of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. * * Authors: * Rob Bradford (from cogl-winsys-egl-kms.c) * Kristian Høgsberg (from eglkms.c) * Benjamin Franzke (from eglkms.c) * Robert Bragg (from cogl-winsys-egl-kms.c) * Neil Roberts (from cogl-winsys-egl-kms.c) * Jonas Ådahl * */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-egl-ext.h" #include "backends/meta-egl.h" #include "backends/meta-gles3.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-output.h" #include "backends/meta-renderer-view.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-drm-buffer-dumb.h" #include "backends/native/meta-drm-buffer-gbm.h" #include "backends/native/meta-drm-buffer-import.h" #include "backends/native/meta-drm-buffer.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-output-kms.h" #include "backends/native/meta-renderer-native-gles3.h" #include "backends/native/meta-renderer-native.h" //#include "cogl/cogl-framebuffer.h" #include "cogl/cogl.h" #include "core/boxes-private.h" #ifndef EGL_DRM_MASTER_FD_EXT #define EGL_DRM_MASTER_FD_EXT 0x333C #endif /* added in libdrm 2.4.95 */ #ifndef DRM_FORMAT_INVALID #define DRM_FORMAT_INVALID 0 #endif typedef enum _MetaSharedFramebufferCopyMode { /* Zero-copy: primary GPU exports, secondary GPU imports as KMS FB */ META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO, /* the secondary GPU will make the copy */ META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU, /* * The copy is made in the primary GPU rendering context, either * as a CPU copy through Cogl read-pixels or as primary GPU copy * using glBlitFramebuffer. */ META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY } MetaSharedFramebufferCopyMode; typedef struct _MetaRendererNativeGpuData { MetaRendererNative *renderer_native; struct { struct gbm_device *device; } gbm; #ifdef HAVE_EGL_DEVICE struct { EGLDeviceEXT device; } egl; #endif MetaRendererNativeMode mode; EGLDisplay egl_display; /* * Fields used for blitting iGPU framebuffer content onto dGPU framebuffers. */ struct { MetaSharedFramebufferCopyMode copy_mode; gboolean is_hardware_rendering; gboolean has_EGL_EXT_image_dma_buf_import_modifiers; /* For GPU blit mode */ EGLContext egl_context; EGLConfig egl_config; } secondary; } MetaRendererNativeGpuData; typedef struct _MetaDumbBuffer { uint32_t fb_id; uint32_t handle; void *map; uint64_t map_size; int width; int height; int stride_bytes; uint32_t drm_format; int dmabuf_fd; } MetaDumbBuffer; typedef enum _MetaSharedFramebufferImportStatus { /* Not tried importing yet. */ META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE, /* Tried before and failed. */ META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED, /* Tried before and succeeded. */ META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK } MetaSharedFramebufferImportStatus; typedef struct _MetaOnscreenNativeSecondaryGpuState { MetaGpuKms *gpu_kms; MetaRendererNativeGpuData *renderer_gpu_data; EGLSurface egl_surface; struct { struct gbm_surface *surface; MetaDrmBuffer *current_fb; MetaDrmBuffer *next_fb; } gbm; struct { MetaDumbBuffer *dumb_fb; MetaDumbBuffer dumb_fbs[2]; } cpu; int pending_flips; gboolean noted_primary_gpu_copy_ok; gboolean noted_primary_gpu_copy_failed; MetaSharedFramebufferImportStatus import_status; } MetaOnscreenNativeSecondaryGpuState; typedef struct _MetaOnscreenNative { MetaRendererNative *renderer_native; MetaGpuKms *render_gpu; MetaOutput *output; MetaCrtc *crtc; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; struct { struct gbm_surface *surface; MetaDrmBuffer *current_fb; MetaDrmBuffer *next_fb; } gbm; #ifdef HAVE_EGL_DEVICE struct { EGLStreamKHR stream; MetaDumbBuffer dumb_fb; } egl; #endif gboolean pending_swap_notify; gboolean pending_set_crtc; int64_t pending_queue_swap_notify_frame_count; int64_t pending_swap_notify_frame_count; MetaRendererView *view; int total_pending_flips; } MetaOnscreenNative; struct _MetaRendererNative { MetaRenderer parent; MetaGpuKms *primary_gpu_kms; MetaGles3 *gles3; gboolean use_modifiers; GHashTable *gpu_datas; CoglClosure *swap_notify_idle; int64_t frame_counter; gboolean pending_unset_disabled_crtcs; GList *power_save_page_flip_onscreens; guint power_save_page_flip_source_id; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaRendererNative, meta_renderer_native, META_TYPE_RENDERER, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable; static const CoglWinsysVtable *parent_vtable; static void release_dumb_fb (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms); static gboolean init_dumb_fb (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms, int width, int height, uint32_t format, GError **error); static int meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms); static MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native); static void free_current_secondary_bo (CoglOnscreen *onscreen); static gboolean cogl_pixel_format_from_drm_format (uint32_t drm_format, CoglPixelFormat *out_format, CoglTextureComponents *out_components); static void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native); static void meta_renderer_native_gpu_data_free (MetaRendererNativeGpuData *renderer_gpu_data) { MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); if (renderer_gpu_data->egl_display != EGL_NO_DISPLAY) meta_egl_terminate (egl, renderer_gpu_data->egl_display, NULL); g_clear_pointer (&renderer_gpu_data->gbm.device, gbm_device_destroy); g_free (renderer_gpu_data); } static MetaRendererNativeGpuData * meta_renderer_native_get_gpu_data (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms) { return g_hash_table_lookup (renderer_native->gpu_datas, gpu_kms); } static MetaRendererNative * meta_renderer_native_from_gpu (MetaGpuKms *gpu_kms) { MetaBackend *backend = meta_gpu_get_backend (META_GPU (gpu_kms)); return META_RENDERER_NATIVE (meta_backend_get_renderer (backend)); } struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms) { MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms); MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, gpu_kms); return renderer_gpu_data->gbm.device; } static MetaRendererNativeGpuData * meta_create_renderer_native_gpu_data (MetaGpuKms *gpu_kms) { return g_new0 (MetaRendererNativeGpuData, 1); } static MetaEgl * meta_renderer_native_get_egl (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); return meta_backend_get_egl (meta_renderer_get_backend (renderer)); } static MetaEgl * meta_onscreen_native_get_egl (MetaOnscreenNative *onscreen_native) { return meta_renderer_native_get_egl (onscreen_native->renderer_native); } static GArray * get_supported_kms_modifiers (MetaCrtc *crtc, uint32_t format) { GArray *modifiers; GArray *crtc_mods; unsigned int i; crtc_mods = meta_crtc_kms_get_modifiers (crtc, format); if (!crtc_mods) return NULL; modifiers = g_array_new (FALSE, FALSE, sizeof (uint64_t)); /* * For each modifier from base_crtc, check if it's available on all other * CRTCs. */ for (i = 0; i < crtc_mods->len; i++) { uint64_t modifier = g_array_index (crtc_mods, uint64_t, i); g_array_append_val (modifiers, modifier); } if (modifiers->len == 0) { g_array_free (modifiers, TRUE); return NULL; } return modifiers; } static GArray * get_supported_egl_modifiers (CoglOnscreen *onscreen, MetaCrtc *crtc, uint32_t format) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); MetaGpu *gpu; MetaRendererNativeGpuData *renderer_gpu_data; EGLint num_modifiers; GArray *modifiers; GError *error = NULL; gboolean ret; gpu = meta_crtc_get_gpu (crtc); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, META_GPU_KMS (gpu)); if (!meta_egl_has_extensions (egl, renderer_gpu_data->egl_display, NULL, "EGL_EXT_image_dma_buf_import_modifiers", NULL)) return NULL; ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display, format, 0, NULL, NULL, &num_modifiers, NULL); if (!ret || num_modifiers == 0) return NULL; modifiers = g_array_sized_new (FALSE, FALSE, sizeof (uint64_t), num_modifiers); ret = meta_egl_query_dma_buf_modifiers (egl, renderer_gpu_data->egl_display, format, num_modifiers, (EGLuint64KHR *) modifiers->data, NULL, &num_modifiers, &error); if (!ret) { g_warning ("Failed to query DMABUF modifiers: %s", error->message); g_error_free (error); g_array_free (modifiers, TRUE); return NULL; } return modifiers; } static GArray * get_supported_modifiers (CoglOnscreen *onscreen, uint32_t format) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaCrtc *crtc = onscreen_native->crtc; MetaGpu *gpu; g_autoptr (GArray) modifiers = NULL; gpu = meta_crtc_get_gpu (crtc); if (gpu == META_GPU (onscreen_native->render_gpu)) modifiers = get_supported_kms_modifiers (crtc, format); else modifiers = get_supported_egl_modifiers (onscreen, crtc, format); return g_steal_pointer (&modifiers); } static GArray * get_supported_kms_formats (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaCrtc *crtc = onscreen_native->crtc; return meta_crtc_kms_copy_drm_format_list (crtc); } static gboolean init_secondary_gpu_state_gpu_copy_mode (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, MetaRendererNativeGpuData *renderer_gpu_data, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); int width, height; EGLNativeWindowType egl_native_window; struct gbm_surface *gbm_surface; EGLSurface egl_surface; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; MetaGpuKms *gpu_kms; width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device, width, height, GBM_FORMAT_XRGB8888, GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING); if (!gbm_surface) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create gbm_surface: %s", strerror (errno)); return FALSE; } egl_native_window = (EGLNativeWindowType) gbm_surface; egl_surface = meta_egl_create_window_surface (egl, renderer_gpu_data->egl_display, renderer_gpu_data->secondary.egl_config, egl_native_window, NULL, error); if (egl_surface == EGL_NO_SURFACE) { gbm_surface_destroy (gbm_surface); return FALSE; } secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1); gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); secondary_gpu_state->gpu_kms = gpu_kms; secondary_gpu_state->renderer_gpu_data = renderer_gpu_data; secondary_gpu_state->gbm.surface = gbm_surface; secondary_gpu_state->egl_surface = egl_surface; onscreen_native->secondary_gpu_state = secondary_gpu_state; return TRUE; } static void secondary_gpu_release_dumb (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaGpuKms *gpu_kms = secondary_gpu_state->gpu_kms; unsigned i; for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) release_dumb_fb (&secondary_gpu_state->cpu.dumb_fbs[i], gpu_kms); } static void secondary_gpu_state_free (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); if (secondary_gpu_state->egl_surface != EGL_NO_SURFACE) { MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = secondary_gpu_state->renderer_gpu_data; meta_egl_destroy_surface (egl, renderer_gpu_data->egl_display, secondary_gpu_state->egl_surface, NULL); } g_clear_object (&secondary_gpu_state->gbm.current_fb); g_clear_object (&secondary_gpu_state->gbm.next_fb); g_clear_pointer (&secondary_gpu_state->gbm.surface, gbm_surface_destroy); secondary_gpu_release_dumb (secondary_gpu_state); g_free (secondary_gpu_state); } static uint32_t pick_secondary_gpu_framebuffer_format_for_cpu (CoglOnscreen *onscreen) { /* * cogl_framebuffer_read_pixels_into_bitmap () supported formats in * preference order. Ideally these should depend on the render buffer * format copy_shared_framebuffer_cpu () will be reading from but * alpha channel ignored. */ static const uint32_t preferred_formats[] = { /* * DRM_FORMAT_XBGR8888 a.k.a GL_RGBA, GL_UNSIGNED_BYTE on * little-endian is possibly the most optimized glReadPixels * output format. glReadPixels cannot avoid manufacturing an alpha * channel if the render buffer does not have one and converting * to ABGR8888 may be more optimized than ARGB8888. */ DRM_FORMAT_XBGR8888, /* The rest are other fairly commonly used formats in OpenGL. */ DRM_FORMAT_XRGB8888, }; g_autoptr (GArray) formats = NULL; size_t k; unsigned int i; uint32_t drm_format; formats = get_supported_kms_formats (onscreen); /* Check if any of our preferred formats are supported. */ for (k = 0; k < G_N_ELEMENTS (preferred_formats); k++) { g_assert (cogl_pixel_format_from_drm_format (preferred_formats[k], NULL, NULL)); for (i = 0; i < formats->len; i++) { drm_format = g_array_index (formats, uint32_t, i); if (drm_format == preferred_formats[k]) return drm_format; } } /* * Otherwise just pick an arbitrary format we recognize. The formats * list is not in any specific order and we don't know any better * either. */ for (i = 0; i < formats->len; i++) { drm_format = g_array_index (formats, uint32_t, i); if (cogl_pixel_format_from_drm_format (drm_format, NULL, NULL)) return drm_format; } return DRM_FORMAT_INVALID; } static gboolean init_secondary_gpu_state_cpu_copy_mode (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, MetaRendererNativeGpuData *renderer_gpu_data, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; MetaGpuKms *gpu_kms; int width, height; unsigned int i; uint32_t drm_format; MetaDrmFormatBuf tmp; drm_format = pick_secondary_gpu_framebuffer_format_for_cpu (onscreen); if (drm_format == DRM_FORMAT_INVALID) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not find a suitable pixel format in CPU copy mode"); return FALSE; } width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)); g_debug ("Secondary GPU %s using DRM format '%s' (0x%x) for a %dx%d output.", meta_gpu_kms_get_file_path (gpu_kms), meta_drm_format_to_string (&tmp, drm_format), drm_format, width, height); secondary_gpu_state = g_new0 (MetaOnscreenNativeSecondaryGpuState, 1); secondary_gpu_state->renderer_gpu_data = renderer_gpu_data; secondary_gpu_state->gpu_kms = gpu_kms; secondary_gpu_state->egl_surface = EGL_NO_SURFACE; for (i = 0; i < G_N_ELEMENTS (secondary_gpu_state->cpu.dumb_fbs); i++) { MetaDumbBuffer *dumb_fb = &secondary_gpu_state->cpu.dumb_fbs[i]; if (!init_dumb_fb (dumb_fb, gpu_kms, width, height, drm_format, error)) { secondary_gpu_state_free (secondary_gpu_state); return FALSE; } } /* * This function initializes everything needed for * META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO as well. */ secondary_gpu_state->import_status = META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE; onscreen_native->secondary_gpu_state = secondary_gpu_state; return TRUE; } static gboolean init_secondary_gpu_state (MetaRendererNative *renderer_native, CoglOnscreen *onscreen, GError **error) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaGpu *gpu = meta_crtc_get_gpu (onscreen_native->crtc); MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, META_GPU_KMS (gpu)); switch (renderer_gpu_data->secondary.copy_mode) { case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: if (!init_secondary_gpu_state_gpu_copy_mode (renderer_native, onscreen, renderer_gpu_data, error)) return FALSE; break; case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: /* * Initialize also the primary copy mode, so that if zero-copy * path fails, which is quite likely, we can simply continue * with the primary copy path on the very first frame. */ G_GNUC_FALLTHROUGH; case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: if (!init_secondary_gpu_state_cpu_copy_mode (renderer_native, onscreen, renderer_gpu_data, error)) return FALSE; break; } return TRUE; } static void meta_renderer_native_disconnect (CoglRenderer *cogl_renderer) { CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; g_slice_free (CoglRendererEGL, cogl_renderer_egl); } static void flush_pending_swap_notify (CoglFramebuffer *framebuffer) { if (framebuffer->type == COGL_FRAMEBUFFER_TYPE_ONSCREEN) { CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; if (onscreen_native->pending_swap_notify) { CoglFrameInfo *info; while ((info = g_queue_peek_head (&onscreen->pending_frame_infos)) && info->global_frame_counter <= onscreen_native->pending_swap_notify_frame_count) { _cogl_onscreen_notify_frame_sync (onscreen, info); _cogl_onscreen_notify_complete (onscreen, info); cogl_object_unref (info); g_queue_pop_head (&onscreen->pending_frame_infos); } onscreen_native->pending_swap_notify = FALSE; cogl_object_unref (onscreen); } } } static void flush_pending_swap_notify_idle (void *user_data) { CoglContext *cogl_context = user_data; CoglRendererEGL *cogl_renderer_egl = cogl_context->display->renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; GList *l; /* This needs to be disconnected before invoking the callbacks in * case the callbacks cause it to be queued again */ _cogl_closure_disconnect (renderer_native->swap_notify_idle); renderer_native->swap_notify_idle = NULL; l = cogl_context->framebuffers; while (l) { GList *next = l->next; CoglFramebuffer *framebuffer = l->data; flush_pending_swap_notify (framebuffer); l = next; } } static void free_current_secondary_bo (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; secondary_gpu_state = onscreen_native->secondary_gpu_state; if (!secondary_gpu_state) return; g_clear_object (&secondary_gpu_state->gbm.current_fb); } static void free_current_bo (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; g_clear_object (&onscreen_native->gbm.current_fb); free_current_secondary_bo (onscreen); } static void meta_onscreen_native_queue_swap_notify (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; onscreen_native->pending_swap_notify_frame_count = onscreen_native->pending_queue_swap_notify_frame_count; if (onscreen_native->pending_swap_notify) return; /* We only want to notify that the swap is complete when the * application calls cogl_context_dispatch so instead of * immediately notifying we queue an idle callback */ if (!renderer_native->swap_notify_idle) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; renderer_native->swap_notify_idle = _cogl_poll_renderer_add_idle (cogl_renderer, flush_pending_swap_notify_idle, cogl_context, NULL); } /* * The framebuffer will have its own referenc while the swap notify is * pending. Otherwise when destroying the view would drop the pending * notification with if the destruction happens before the idle callback * is invoked. */ cogl_object_ref (onscreen); onscreen_native->pending_swap_notify = TRUE; } static gboolean meta_renderer_native_connect (CoglRenderer *cogl_renderer, GError **error) { CoglRendererEGL *cogl_renderer_egl; MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data; MetaRendererNative *renderer_native = meta_renderer_native_from_gpu (gpu_kms); MetaRendererNativeGpuData *renderer_gpu_data; cogl_renderer->winsys = g_slice_new0 (CoglRendererEGL); cogl_renderer_egl = cogl_renderer->winsys; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, gpu_kms); cogl_renderer_egl->platform_vtable = &_cogl_winsys_egl_vtable; cogl_renderer_egl->platform = renderer_gpu_data; cogl_renderer_egl->edpy = renderer_gpu_data->egl_display; if (!_cogl_winsys_egl_renderer_connect_common (cogl_renderer, error)) goto fail; return TRUE; fail: meta_renderer_native_disconnect (cogl_renderer); return FALSE; } static int meta_renderer_native_add_egl_config_attributes (CoglDisplay *cogl_display, CoglFramebufferConfig *config, EGLint *attributes) { CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; int i = 0; switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: attributes[i++] = EGL_SURFACE_TYPE; attributes[i++] = EGL_WINDOW_BIT; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: attributes[i++] = EGL_SURFACE_TYPE; attributes[i++] = EGL_STREAM_BIT_KHR; break; #endif } return i; } static gboolean choose_egl_config_from_gbm_format (MetaEgl *egl, EGLDisplay egl_display, const EGLint *attributes, uint32_t gbm_format, EGLConfig *out_config, GError **error) { EGLConfig *egl_configs; EGLint n_configs; EGLint i; egl_configs = meta_egl_choose_all_configs (egl, egl_display, attributes, &n_configs, error); if (!egl_configs) return FALSE; for (i = 0; i < n_configs; i++) { EGLint visual_id; if (!meta_egl_get_config_attrib (egl, egl_display, egl_configs[i], EGL_NATIVE_VISUAL_ID, &visual_id, error)) { g_free (egl_configs); return FALSE; } if ((uint32_t) visual_id == gbm_format) { *out_config = egl_configs[i]; g_free (egl_configs); return TRUE; } } g_free (egl_configs); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No EGL config matching supported GBM format found"); return FALSE; } static gboolean meta_renderer_native_choose_egl_config (CoglDisplay *cogl_display, EGLint *attributes, EGLConfig *out_config, GError **error) { CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; EGLDisplay egl_display = cogl_renderer_egl->edpy; switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: return choose_egl_config_from_gbm_format (egl, egl_display, attributes, GBM_FORMAT_XRGB8888, out_config, error); #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: return meta_egl_choose_first_config (egl, egl_display, attributes, out_config, error); #endif } return FALSE; } static gboolean meta_renderer_native_setup_egl_display (CoglDisplay *cogl_display, GError **error) { CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglRendererEGL *cogl_renderer_egl = cogl_display->renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; cogl_display_egl->platform = renderer_native; /* Force a full modeset / drmModeSetCrtc on * the first swap buffers call. */ meta_renderer_native_queue_modes_reset (renderer_native); return TRUE; } static void meta_renderer_native_destroy_egl_display (CoglDisplay *cogl_display) { } static EGLSurface create_dummy_pbuffer_surface (EGLDisplay egl_display, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); EGLConfig pbuffer_config; static const EGLint pbuffer_config_attribs[] = { EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, 0, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE }; static const EGLint pbuffer_attribs[] = { EGL_WIDTH, 16, EGL_HEIGHT, 16, EGL_NONE }; if (!meta_egl_choose_first_config (egl, egl_display, pbuffer_config_attribs, &pbuffer_config, error)) return EGL_NO_SURFACE; return meta_egl_create_pbuffer_surface (egl, egl_display, pbuffer_config, pbuffer_attribs, error); } static gboolean meta_renderer_native_egl_context_created (CoglDisplay *cogl_display, GError **error) { CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; if ((cogl_renderer_egl->private_features & COGL_EGL_WINSYS_FEATURE_SURFACELESS_CONTEXT) == 0) { cogl_display_egl->dummy_surface = create_dummy_pbuffer_surface (cogl_renderer_egl->edpy, error); if (cogl_display_egl->dummy_surface == EGL_NO_SURFACE) return FALSE; } if (!_cogl_winsys_egl_make_current (cogl_display, cogl_display_egl->dummy_surface, cogl_display_egl->dummy_surface, cogl_display_egl->egl_context)) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_CONTEXT, "Failed to make context current"); return FALSE; } return TRUE; } static void meta_renderer_native_egl_cleanup_context (CoglDisplay *cogl_display) { CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); if (cogl_display_egl->dummy_surface != EGL_NO_SURFACE) { meta_egl_destroy_surface (egl, cogl_renderer_egl->edpy, cogl_display_egl->dummy_surface, NULL); cogl_display_egl->dummy_surface = EGL_NO_SURFACE; } } static void swap_secondary_drm_fb (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; secondary_gpu_state = onscreen_native->secondary_gpu_state; if (!secondary_gpu_state) return; g_set_object (&secondary_gpu_state->gbm.current_fb, secondary_gpu_state->gbm.next_fb); g_clear_object (&secondary_gpu_state->gbm.next_fb); } static void meta_onscreen_native_swap_drm_fb (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; free_current_bo (onscreen); g_set_object (&onscreen_native->gbm.current_fb, onscreen_native->gbm.next_fb); g_clear_object (&onscreen_native->gbm.next_fb); swap_secondary_drm_fb (onscreen); } static void notify_view_crtc_presented (MetaRendererView *view, MetaKmsCrtc *kms_crtc, int64_t time_ns) { ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (view); CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaGpuKms *render_gpu = onscreen_native->render_gpu; CoglFrameInfo *frame_info; MetaCrtc *crtc; float refresh_rate; MetaGpuKms *gpu_kms; /* Only keep the frame info for the fastest CRTC in use, which may not be * the first one to complete a flip. By only telling the compositor about the * fastest monitor(s) we direct it to produce new frames fast enough to * satisfy all monitors. */ frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); refresh_rate = crtc && crtc->config ? crtc->config->mode->refresh_rate : 0.0f; if (refresh_rate >= frame_info->refresh_rate) { frame_info->presentation_time = time_ns; frame_info->refresh_rate = refresh_rate; } gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); if (gpu_kms != render_gpu) { MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = onscreen_native->secondary_gpu_state; secondary_gpu_state->pending_flips--; } onscreen_native->total_pending_flips--; if (onscreen_native->total_pending_flips == 0) { MetaRendererNativeGpuData *renderer_gpu_data; meta_onscreen_native_queue_swap_notify (onscreen); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, onscreen_native->render_gpu); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: meta_onscreen_native_swap_drm_fb (onscreen); break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } } } static int64_t timeval_to_nanoseconds (const struct timeval *tv) { int64_t usec = ((int64_t) tv->tv_sec) * G_USEC_PER_SEC + tv->tv_usec; int64_t nsec = usec * 1000; return nsec; } static void page_flip_feedback_flipped (MetaKmsCrtc *kms_crtc, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, gpointer user_data) { MetaRendererView *view = user_data; struct timeval page_flip_time; page_flip_time = (struct timeval) { .tv_sec = tv_sec, .tv_usec = tv_usec, }; notify_view_crtc_presented (view, kms_crtc, timeval_to_nanoseconds (&page_flip_time)); g_object_unref (view); } static void page_flip_feedback_mode_set_fallback (MetaKmsCrtc *kms_crtc, gpointer user_data) { MetaRendererView *view = user_data; MetaCrtc *crtc; MetaGpuKms *gpu_kms; int64_t now_ns; /* * We ended up not page flipping, thus we don't have a presentation time to * use. Lets use the next best thing: the current time. */ crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); notify_view_crtc_presented (view, kms_crtc, now_ns); g_object_unref (view); } static void page_flip_feedback_discarded (MetaKmsCrtc *kms_crtc, gpointer user_data, const GError *error) { MetaRendererView *view = user_data; MetaCrtc *crtc; MetaGpuKms *gpu_kms; int64_t now_ns; /* * Page flipping failed, but we want to fail gracefully, so to avoid freezing * the frame clack, pretend we flipped. */ if (error) g_warning ("Page flip discarded: %s", error->message); crtc = meta_crtc_kms_from_kms_crtc (kms_crtc); gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); now_ns = meta_gpu_kms_get_current_time_ns (gpu_kms); notify_view_crtc_presented (view, kms_crtc, now_ns); g_object_unref (view); } static const MetaKmsPageFlipFeedback page_flip_feedback = { .flipped = page_flip_feedback_flipped, .mode_set_fallback = page_flip_feedback_mode_set_fallback, .discarded = page_flip_feedback_discarded, }; #ifdef HAVE_EGL_DEVICE static int custom_egl_stream_page_flip (gpointer custom_page_flip_data, gpointer user_data) { MetaOnscreenNative *onscreen_native = custom_page_flip_data; MetaRendererView *view = user_data; MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); MetaRendererNativeGpuData *renderer_gpu_data; EGLDisplay *egl_display; EGLAttrib *acquire_attribs; g_autoptr (GError) error = NULL; acquire_attribs = (EGLAttrib[]) { EGL_DRM_FLIP_EVENT_DATA_NV, (EGLAttrib) view, EGL_NONE }; renderer_gpu_data = meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, onscreen_native->render_gpu); egl_display = renderer_gpu_data->egl_display; if (!meta_egl_stream_consumer_acquire_attrib (egl, egl_display, onscreen_native->egl.stream, acquire_attribs, &error)) { if (g_error_matches (error, META_EGL_ERROR, EGL_RESOURCE_BUSY_EXT)) return -EBUSY; else return -EINVAL; } return 0; } #endif /* HAVE_EGL_DEVICE */ static void dummy_power_save_page_flip (CoglOnscreen *onscreen) { meta_onscreen_native_swap_drm_fb (onscreen); meta_onscreen_native_queue_swap_notify (onscreen); } static gboolean dummy_power_save_page_flip_cb (gpointer user_data) { MetaRendererNative *renderer_native = user_data; g_list_foreach (renderer_native->power_save_page_flip_onscreens, (GFunc) dummy_power_save_page_flip, NULL); g_list_free_full (renderer_native->power_save_page_flip_onscreens, (GDestroyNotify) cogl_object_unref); renderer_native->power_save_page_flip_onscreens = NULL; renderer_native->power_save_page_flip_source_id = 0; return G_SOURCE_REMOVE; } static void queue_dummy_power_save_page_flip (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; const unsigned int timeout_ms = 100; if (!renderer_native->power_save_page_flip_source_id) { renderer_native->power_save_page_flip_source_id = g_timeout_add (timeout_ms, dummy_power_save_page_flip_cb, renderer_native); } renderer_native->power_save_page_flip_onscreens = g_list_prepend (renderer_native->power_save_page_flip_onscreens, cogl_object_ref (onscreen)); } static void meta_onscreen_native_flip_crtc (CoglOnscreen *onscreen, MetaRendererView *view, MetaCrtc *crtc, MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaGpuKms *render_gpu = onscreen_native->render_gpu; MetaRendererNativeGpuData *renderer_gpu_data; MetaGpuKms *gpu_kms; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state = NULL; uint32_t fb_id; gpu_kms = META_GPU_KMS (meta_crtc_get_gpu (crtc)); g_assert (meta_gpu_kms_is_crtc_active (gpu_kms, crtc)); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: if (gpu_kms == render_gpu) { fb_id = meta_drm_buffer_get_fb_id (onscreen_native->gbm.next_fb); } else { secondary_gpu_state = onscreen_native->secondary_gpu_state; fb_id = meta_drm_buffer_get_fb_id (secondary_gpu_state->gbm.next_fb); } meta_crtc_kms_assign_primary_plane (crtc, fb_id, kms_update); meta_crtc_kms_page_flip (crtc, &page_flip_feedback, g_object_ref (view), kms_update); onscreen_native->total_pending_flips++; if (secondary_gpu_state) secondary_gpu_state->pending_flips++; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: meta_kms_update_custom_page_flip (kms_update, meta_crtc_kms_get_kms_crtc (crtc), &page_flip_feedback, g_object_ref (view), custom_egl_stream_page_flip, onscreen_native); onscreen_native->total_pending_flips++; break; #endif } } static void meta_onscreen_native_set_crtc_mode (CoglOnscreen *onscreen, MetaRendererNativeGpuData *renderer_gpu_data, MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeSetCrtcModes, "Onscreen (set CRTC modes)"); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: { uint32_t fb_id; fb_id = onscreen_native->egl.dumb_fb.fb_id; meta_crtc_kms_assign_primary_plane (onscreen_native->crtc, fb_id, kms_update); break; } #endif } meta_crtc_kms_set_mode (onscreen_native->crtc, kms_update); meta_output_kms_set_underscan (onscreen_native->output, kms_update); } static void meta_onscreen_native_flip_crtcs (CoglOnscreen *onscreen, MetaKmsUpdate *kms_update) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererView *view = onscreen_native->view; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaRenderer *renderer = META_RENDERER (renderer_native); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (meta_renderer_get_backend (renderer)); MetaPowerSave power_save_mode; COGL_TRACE_BEGIN_SCOPED (MetaOnscreenNativeFlipCrtcs, "Onscreen (flip CRTCs)"); power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) { meta_onscreen_native_flip_crtc (onscreen, view, onscreen_native->crtc, kms_update); } else { queue_dummy_power_save_page_flip (onscreen); } } static void wait_for_pending_flips (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; GError *error = NULL; secondary_gpu_state = onscreen_native->secondary_gpu_state; if (secondary_gpu_state) { while (secondary_gpu_state->pending_flips) { if (!meta_gpu_kms_wait_for_flip (secondary_gpu_state->gpu_kms, &error)) { g_warning ("Failed to wait for flip on secondary GPU: %s", error->message); g_clear_error (&error); break; } } } while (onscreen_native->total_pending_flips) { if (!meta_gpu_kms_wait_for_flip (onscreen_native->render_gpu, &error)) { g_warning ("Failed to wait for flip: %s", error->message); g_clear_error (&error); break; } } } static gboolean import_shared_framebuffer (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaDrmBufferGbm *buffer_gbm; MetaDrmBufferImport *buffer_import; g_autoptr (GError) error = NULL; buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); buffer_import = meta_drm_buffer_import_new (secondary_gpu_state->gpu_kms, buffer_gbm, &error); if (!buffer_import) { g_debug ("Zero-copy disabled for %s, meta_drm_buffer_import_new failed: %s", meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms), error->message); g_warn_if_fail (secondary_gpu_state->import_status == META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE); /* * Fall back. If META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE is * in effect, we have COPY_MODE_PRIMARY prepared already, so we * simply retry with that path. Import status cannot be FAILED, * because we should not retry if failed once. * * If import status is OK, that is unexpected and we do not * have the fallback path prepared which means this output cannot * work anymore. */ secondary_gpu_state->renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY; secondary_gpu_state->import_status = META_SHARED_FRAMEBUFFER_IMPORT_STATUS_FAILED; return FALSE; } /* * next_fb may already contain a fallback buffer, so clear it only * when we are sure to succeed. */ g_clear_object (&secondary_gpu_state->gbm.next_fb); secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_import); if (secondary_gpu_state->import_status == META_SHARED_FRAMEBUFFER_IMPORT_STATUS_NONE) { /* * Clean up the cpu-copy part of * init_secondary_gpu_state_cpu_copy_mode () */ secondary_gpu_release_dumb (secondary_gpu_state); g_debug ("Using zero-copy for %s succeeded once.", meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); } secondary_gpu_state->import_status = META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK; return TRUE; } static void copy_shared_framebuffer_gpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, MetaRendererNativeGpuData *renderer_gpu_data, gboolean *egl_context_changed) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); GError *error = NULL; MetaDrmBufferGbm *buffer_gbm; struct gbm_bo *bo; COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferSecondaryGpu, "FB Copy (secondary GPU)"); g_warn_if_fail (secondary_gpu_state->gbm.next_fb == NULL); g_clear_object (&secondary_gpu_state->gbm.next_fb); if (!meta_egl_make_current (egl, renderer_gpu_data->egl_display, secondary_gpu_state->egl_surface, secondary_gpu_state->egl_surface, renderer_gpu_data->secondary.egl_context, &error)) { g_warning ("Failed to make current: %s", error->message); g_error_free (error); return; } *egl_context_changed = TRUE; buffer_gbm = META_DRM_BUFFER_GBM (onscreen_native->gbm.next_fb); bo = meta_drm_buffer_gbm_get_bo (buffer_gbm); if (!meta_renderer_native_gles3_blit_shared_bo (egl, renderer_native->gles3, renderer_gpu_data->egl_display, renderer_gpu_data->secondary.egl_context, secondary_gpu_state->egl_surface, bo, &error)) { g_warning ("Failed to blit shared framebuffer: %s", error->message); g_error_free (error); return; } if (!meta_egl_swap_buffers (egl, renderer_gpu_data->egl_display, secondary_gpu_state->egl_surface, &error)) { g_warning ("Failed to swap buffers: %s", error->message); g_error_free (error); return; } buffer_gbm = meta_drm_buffer_gbm_new (secondary_gpu_state->gpu_kms, secondary_gpu_state->gbm.surface, renderer_native->use_modifiers, &error); if (!buffer_gbm) { g_warning ("meta_drm_buffer_gbm_new failed: %s", error->message); g_error_free (error); return; } secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); } static MetaDumbBuffer * secondary_gpu_get_next_dumb_buffer (MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { MetaDumbBuffer *current_dumb_fb; current_dumb_fb = secondary_gpu_state->cpu.dumb_fb; if (current_dumb_fb == &secondary_gpu_state->cpu.dumb_fbs[0]) return &secondary_gpu_state->cpu.dumb_fbs[1]; else return &secondary_gpu_state->cpu.dumb_fbs[0]; } static CoglContext * cogl_context_from_renderer_native (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); return clutter_backend_get_cogl_context (clutter_backend); } static CoglFramebuffer * create_dma_buf_framebuffer (MetaRendererNative *renderer_native, int dmabuf_fd, uint32_t width, uint32_t height, uint32_t stride, uint32_t offset, uint64_t modifier, uint32_t drm_format, GError **error) { CoglContext *cogl_context = cogl_context_from_renderer_native (renderer_native); CoglDisplay *cogl_display = cogl_context->display; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; EGLDisplay egl_display = cogl_renderer_egl->edpy; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); EGLImageKHR egl_image; uint32_t strides[1]; uint32_t offsets[1]; uint64_t modifiers[1]; CoglPixelFormat cogl_format; CoglEglImageFlags flags; CoglTexture2D *cogl_tex; CoglOffscreen *cogl_fbo; int ret; ret = cogl_pixel_format_from_drm_format (drm_format, &cogl_format, NULL); g_assert (ret); strides[0] = stride; offsets[0] = offset; modifiers[0] = modifier; egl_image = meta_egl_create_dmabuf_image (egl, egl_display, width, height, drm_format, 1 /* n_planes */, &dmabuf_fd, strides, offsets, modifiers, error); if (egl_image == EGL_NO_IMAGE_KHR) return NULL; flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; cogl_tex = cogl_egl_texture_2d_new_from_image (cogl_context, width, height, cogl_format, egl_image, flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!cogl_tex) return NULL; cogl_fbo = cogl_offscreen_new_with_texture (COGL_TEXTURE (cogl_tex)); cogl_object_unref (cogl_tex); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (cogl_fbo), error)) { cogl_object_unref (cogl_fbo); return NULL; } return COGL_FRAMEBUFFER (cogl_fbo); } static gboolean copy_shared_framebuffer_primary_gpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaRendererNativeGpuData *primary_gpu_data; MetaDrmBufferDumb *buffer_dumb; MetaDumbBuffer *dumb_fb; CoglFramebuffer *dmabuf_fb; int dmabuf_fd; g_autoptr (GError) error = NULL; CoglPixelFormat cogl_format; int ret; COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferPrimaryGpu, "FB Copy (primary GPU)"); primary_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, renderer_native->primary_gpu_kms); if (!primary_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers) return FALSE; dumb_fb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); g_assert (cogl_framebuffer_get_width (framebuffer) == dumb_fb->width); g_assert (cogl_framebuffer_get_height (framebuffer) == dumb_fb->height); ret = cogl_pixel_format_from_drm_format (dumb_fb->drm_format, &cogl_format, NULL); g_assert (ret); dmabuf_fd = meta_dumb_buffer_ensure_dmabuf_fd (dumb_fb, secondary_gpu_state->gpu_kms); if (dmabuf_fd == -1) return FALSE; dmabuf_fb = create_dma_buf_framebuffer (renderer_native, dmabuf_fd, dumb_fb->width, dumb_fb->height, dumb_fb->stride_bytes, 0, DRM_FORMAT_MOD_LINEAR, dumb_fb->drm_format, &error); if (error) { g_debug ("%s: Failed to blit DMA buffer image: %s", G_STRFUNC, error->message); return FALSE; } if (!cogl_blit_framebuffer (framebuffer, COGL_FRAMEBUFFER (dmabuf_fb), 0, 0, 0, 0, dumb_fb->width, dumb_fb->height, &error)) { cogl_object_unref (dmabuf_fb); return FALSE; } cogl_object_unref (dmabuf_fb); g_clear_object (&secondary_gpu_state->gbm.next_fb); buffer_dumb = meta_drm_buffer_dumb_new (dumb_fb->fb_id); secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_dumb); secondary_gpu_state->cpu.dumb_fb = dumb_fb; return TRUE; } typedef struct _PixelFormatMap { uint32_t drm_format; CoglPixelFormat cogl_format; CoglTextureComponents cogl_components; } PixelFormatMap; static const PixelFormatMap pixel_format_map[] = { /* DRM formats are defined as little-endian, not machine endian. */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN { DRM_FORMAT_RGB565, COGL_PIXEL_FORMAT_RGB_565, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_ABGR8888, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_XBGR8888, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_ARGB8888, COGL_PIXEL_FORMAT_BGRA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_XRGB8888, COGL_PIXEL_FORMAT_BGRA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_BGRA8888, COGL_PIXEL_FORMAT_ARGB_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_BGRX8888, COGL_PIXEL_FORMAT_ARGB_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_RGBA8888, COGL_PIXEL_FORMAT_ABGR_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_RGBX8888, COGL_PIXEL_FORMAT_ABGR_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, #elif G_BYTE_ORDER == G_BIG_ENDIAN /* DRM_FORMAT_RGB565 cannot be expressed. */ { DRM_FORMAT_ABGR8888, COGL_PIXEL_FORMAT_ABGR_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_XBGR8888, COGL_PIXEL_FORMAT_ABGR_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_ARGB8888, COGL_PIXEL_FORMAT_ARGB_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_XRGB8888, COGL_PIXEL_FORMAT_ARGB_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_BGRA8888, COGL_PIXEL_FORMAT_BGRA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_BGRX8888, COGL_PIXEL_FORMAT_BGRA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, { DRM_FORMAT_RGBA8888, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGBA }, { DRM_FORMAT_RGBX8888, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_TEXTURE_COMPONENTS_RGB }, #else #error "unexpected G_BYTE_ORDER" #endif }; static gboolean cogl_pixel_format_from_drm_format (uint32_t drm_format, CoglPixelFormat *out_format, CoglTextureComponents *out_components) { const size_t n = G_N_ELEMENTS (pixel_format_map); size_t i; for (i = 0; i < n; i++) { if (pixel_format_map[i].drm_format == drm_format) break; } if (i == n) return FALSE; if (out_format) *out_format = pixel_format_map[i].cogl_format; if (out_components) *out_components = pixel_format_map[i].cogl_components; return TRUE; } static void copy_shared_framebuffer_cpu (CoglOnscreen *onscreen, MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state, MetaRendererNativeGpuData *renderer_gpu_data) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; MetaDumbBuffer *dumb_fb; CoglBitmap *dumb_bitmap; CoglPixelFormat cogl_format; gboolean ret; MetaDrmBufferDumb *buffer_dumb; COGL_TRACE_BEGIN_SCOPED (CopySharedFramebufferCpu, "FB Copy (CPU)"); dumb_fb = secondary_gpu_get_next_dumb_buffer (secondary_gpu_state); g_assert (cogl_framebuffer_get_width (framebuffer) == dumb_fb->width); g_assert (cogl_framebuffer_get_height (framebuffer) == dumb_fb->height); ret = cogl_pixel_format_from_drm_format (dumb_fb->drm_format, &cogl_format, NULL); g_assert (ret); dumb_bitmap = cogl_bitmap_new_for_data (cogl_context, dumb_fb->width, dumb_fb->height, cogl_format, dumb_fb->stride_bytes, dumb_fb->map); if (!cogl_framebuffer_read_pixels_into_bitmap (framebuffer, 0 /* x */, 0 /* y */, COGL_READ_PIXELS_COLOR_BUFFER, dumb_bitmap)) g_warning ("Failed to CPU-copy to a secondary GPU output"); cogl_object_unref (dumb_bitmap); g_clear_object (&secondary_gpu_state->gbm.next_fb); buffer_dumb = meta_drm_buffer_dumb_new (dumb_fb->fb_id); secondary_gpu_state->gbm.next_fb = META_DRM_BUFFER (buffer_dumb); secondary_gpu_state->cpu.dumb_fb = dumb_fb; } static void update_secondary_gpu_state_pre_swap_buffers (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePreSwapBuffers, "Onscreen (secondary gpu pre-swap-buffers)"); secondary_gpu_state = onscreen_native->secondary_gpu_state; if (secondary_gpu_state) { MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = secondary_gpu_state->renderer_gpu_data; switch (renderer_gpu_data->secondary.copy_mode) { case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: /* Done after eglSwapBuffers. */ break; case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: /* Done after eglSwapBuffers. */ if (secondary_gpu_state->import_status == META_SHARED_FRAMEBUFFER_IMPORT_STATUS_OK) break; /* prepare fallback */ G_GNUC_FALLTHROUGH; case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: if (!copy_shared_framebuffer_primary_gpu (onscreen, secondary_gpu_state)) { if (!secondary_gpu_state->noted_primary_gpu_copy_failed) { g_debug ("Using primary GPU to copy for %s failed once.", meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); secondary_gpu_state->noted_primary_gpu_copy_failed = TRUE; } copy_shared_framebuffer_cpu (onscreen, secondary_gpu_state, renderer_gpu_data); } else if (!secondary_gpu_state->noted_primary_gpu_copy_ok) { g_debug ("Using primary GPU to copy for %s succeeded once.", meta_gpu_kms_get_file_path (secondary_gpu_state->gpu_kms)); secondary_gpu_state->noted_primary_gpu_copy_ok = TRUE; } break; } } } static void update_secondary_gpu_state_post_swap_buffers (CoglOnscreen *onscreen, gboolean *egl_context_changed) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaOnscreenNativeSecondaryGpuState *secondary_gpu_state; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeGpuStatePostSwapBuffers, "Onscreen (secondary gpu post-swap-buffers)"); secondary_gpu_state = onscreen_native->secondary_gpu_state; if (secondary_gpu_state) { MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, secondary_gpu_state->gpu_kms); retry: switch (renderer_gpu_data->secondary.copy_mode) { case META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO: if (!import_shared_framebuffer (onscreen, secondary_gpu_state)) goto retry; break; case META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU: copy_shared_framebuffer_gpu (onscreen, secondary_gpu_state, renderer_gpu_data, egl_context_changed); break; case META_SHARED_FRAMEBUFFER_COPY_MODE_PRIMARY: /* Done before eglSwapBuffers. */ break; } } } static MetaKmsUpdate * unset_disabled_crtcs (MetaBackend *backend, MetaKms *kms) { MetaKmsUpdate *kms_update = NULL; GList *l; for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpu *gpu = l->data; GList *k; for (k = meta_gpu_get_crtcs (gpu); k; k = k->next) { MetaCrtc *crtc = k->data; if (crtc->config) continue; kms_update = meta_kms_ensure_pending_update (kms); meta_crtc_kms_set_mode (crtc, kms_update); } } return kms_update; } static void post_pending_update (MetaKms *kms) { g_autoptr (MetaKmsFeedback) kms_feedback = NULL; kms_feedback = meta_kms_post_pending_update_sync (kms); if (meta_kms_feedback_get_result (kms_feedback) != META_KMS_FEEDBACK_PASSED) { const GError *error = meta_kms_feedback_get_error (kms_feedback); if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) g_warning ("Failed to post KMS update: %s", error->message); } } static void meta_onscreen_native_swap_buffers_with_damage (CoglOnscreen *onscreen, const int *rectangles, int n_rectangles) { CoglContext *cogl_context = COGL_FRAMEBUFFER (onscreen)->context; CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaGpuKms *render_gpu = onscreen_native->render_gpu; CoglFrameInfo *frame_info; gboolean egl_context_changed = FALSE; MetaKmsUpdate *kms_update; MetaPowerSave power_save_mode; g_autoptr (GError) error = NULL; MetaDrmBufferGbm *buffer_gbm; g_autoptr (MetaKmsFeedback) kms_feedback = NULL; COGL_TRACE_BEGIN_SCOPED (MetaRendererNativeSwapBuffers, "Onscreen (swap-buffers)"); kms_update = meta_kms_ensure_pending_update (kms); /* * Wait for the flip callback before continuing, as we might have started the * animation earlier due to the animation being driven by some other monitor. */ COGL_TRACE_BEGIN (MetaRendererNativeSwapBuffersWait, "Onscreen (waiting for page flips)"); wait_for_pending_flips (onscreen); COGL_TRACE_END (MetaRendererNativeSwapBuffersWait); frame_info = g_queue_peek_tail (&onscreen->pending_frame_infos); frame_info->global_frame_counter = renderer_native->frame_counter; update_secondary_gpu_state_pre_swap_buffers (onscreen); parent_vtable->onscreen_swap_buffers_with_damage (onscreen, rectangles, n_rectangles); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, render_gpu); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: g_warn_if_fail (onscreen_native->gbm.next_fb == NULL); g_clear_object (&onscreen_native->gbm.next_fb); buffer_gbm = meta_drm_buffer_gbm_new (render_gpu, onscreen_native->gbm.surface, renderer_native->use_modifiers, &error); if (!buffer_gbm) { g_warning ("meta_drm_buffer_gbm_new failed: %s", error->message); return; } onscreen_native->gbm.next_fb = META_DRM_BUFFER (buffer_gbm); break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } update_secondary_gpu_state_post_swap_buffers (onscreen, &egl_context_changed); /* If this is the first framebuffer to be presented then we now setup the * crtc modes, else we flip from the previous buffer */ power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (onscreen_native->pending_set_crtc && power_save_mode == META_POWER_SAVE_ON) { meta_onscreen_native_set_crtc_mode (onscreen, renderer_gpu_data, kms_update); onscreen_native->pending_set_crtc = FALSE; } onscreen_native->pending_queue_swap_notify_frame_count = renderer_native->frame_counter; meta_onscreen_native_flip_crtcs (onscreen, kms_update); /* * If we changed EGL context, cogl will have the wrong idea about what is * current, making it fail to set it when it needs to. Avoid that by making * EGL_NO_CONTEXT current now, making cogl eventually set the correct * context. */ if (egl_context_changed) _cogl_winsys_egl_ensure_current (cogl_display); COGL_TRACE_BEGIN (MetaRendererNativePostKmsUpdate, "Onscreen (post pending update)"); post_pending_update (kms); COGL_TRACE_END (MetaRendererNativePostKmsUpdate); } static CoglDmaBufHandle * meta_renderer_native_create_dma_buf (CoglRenderer *cogl_renderer, int width, int height, GError **error) { CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: { CoglFramebuffer *dmabuf_fb; CoglDmaBufHandle *dmabuf_handle; struct gbm_bo *new_bo; int dmabuf_fd = -1; new_bo = gbm_bo_create (renderer_gpu_data->gbm.device, width, height, DRM_FORMAT_XRGB8888, GBM_BO_USE_RENDERING | GBM_BO_USE_LINEAR); if (!new_bo) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to allocate buffer"); return NULL; } dmabuf_fd = gbm_bo_get_fd (new_bo); if (dmabuf_fd == -1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_EXISTS, "Failed to export buffer's DMA fd: %s", g_strerror (errno)); return NULL; } dmabuf_fb = create_dma_buf_framebuffer (renderer_native, dmabuf_fd, width, height, gbm_bo_get_stride (new_bo), gbm_bo_get_offset (new_bo, 0), DRM_FORMAT_MOD_LINEAR, DRM_FORMAT_XRGB8888, error); if (!dmabuf_fb) return NULL; dmabuf_handle = cogl_dma_buf_handle_new (dmabuf_fb, dmabuf_fd, new_bo, (GDestroyNotify) gbm_bo_destroy); cogl_object_unref (dmabuf_fb); return dmabuf_handle; } break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: break; #endif } g_set_error (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, "Current mode does not support exporting DMA buffers"); return NULL; } static gboolean meta_renderer_native_init_egl_context (CoglContext *cogl_context, GError **error) { #ifdef HAVE_EGL_DEVICE CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; #endif COGL_FLAGS_SET (cogl_context->features, COGL_FEATURE_ID_PRESENTATION_TIME, TRUE); COGL_FLAGS_SET (cogl_context->features, COGL_FEATURE_ID_SWAP_BUFFERS_EVENT, TRUE); /* TODO: remove this deprecated feature */ COGL_FLAGS_SET (cogl_context->winsys_features, COGL_WINSYS_FEATURE_SWAP_BUFFERS_EVENT, TRUE); COGL_FLAGS_SET (cogl_context->winsys_features, COGL_WINSYS_FEATURE_SYNC_AND_COMPLETE_EVENT, TRUE); COGL_FLAGS_SET (cogl_context->winsys_features, COGL_WINSYS_FEATURE_MULTIPLE_ONSCREEN, TRUE); /* COGL_WINSYS_FEATURE_SWAP_THROTTLE is always true for this renderer * because we have the call to wait_for_pending_flips on every frame. */ COGL_FLAGS_SET (cogl_context->winsys_features, COGL_WINSYS_FEATURE_SWAP_THROTTLE, TRUE); #ifdef HAVE_EGL_DEVICE if (renderer_gpu_data->mode == META_RENDERER_NATIVE_MODE_EGL_DEVICE) COGL_FLAGS_SET (cogl_context->features, COGL_FEATURE_ID_TEXTURE_EGL_IMAGE_EXTERNAL, TRUE); #endif return TRUE; } static gboolean should_surface_be_sharable (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; if (META_GPU_KMS (meta_crtc_get_gpu (onscreen_native->crtc)) == onscreen_native->render_gpu) return FALSE; else return TRUE; } static gboolean meta_renderer_native_create_surface_gbm (CoglOnscreen *onscreen, int width, int height, struct gbm_surface **gbm_surface, EGLSurface *egl_surface, GError **error) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNative *renderer_native = onscreen_native->renderer_native; MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglDisplay *cogl_display = cogl_context->display; CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; struct gbm_surface *new_gbm_surface = NULL; EGLNativeWindowType egl_native_window; EGLSurface new_egl_surface; uint32_t format = GBM_FORMAT_XRGB8888; GArray *modifiers; renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, onscreen_native->render_gpu); if (renderer_native->use_modifiers) modifiers = get_supported_modifiers (onscreen, format); else modifiers = NULL; if (modifiers) { new_gbm_surface = gbm_surface_create_with_modifiers (renderer_gpu_data->gbm.device, width, height, format, (uint64_t *) modifiers->data, modifiers->len); g_array_free (modifiers, TRUE); } if (!new_gbm_surface) { uint32_t flags = GBM_BO_USE_SCANOUT | GBM_BO_USE_RENDERING; if (should_surface_be_sharable (onscreen)) flags |= GBM_BO_USE_LINEAR; new_gbm_surface = gbm_surface_create (renderer_gpu_data->gbm.device, width, height, format, flags); } if (!new_gbm_surface) { g_set_error (error, COGL_WINSYS_ERROR, COGL_WINSYS_ERROR_CREATE_ONSCREEN, "Failed to allocate surface"); return FALSE; } egl_native_window = (EGLNativeWindowType) new_gbm_surface; new_egl_surface = meta_egl_create_window_surface (egl, cogl_renderer_egl->edpy, cogl_display_egl->egl_config, egl_native_window, NULL, error); if (new_egl_surface == EGL_NO_SURFACE) { gbm_surface_destroy (new_gbm_surface); return FALSE; } *gbm_surface = new_gbm_surface; *egl_surface = new_egl_surface; return TRUE; } #ifdef HAVE_EGL_DEVICE static gboolean meta_renderer_native_create_surface_egl_device (CoglOnscreen *onscreen, int width, int height, EGLStreamKHR *out_egl_stream, EGLSurface *out_egl_surface, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; CoglContext *cogl_context = framebuffer->context; CoglDisplay *cogl_display = cogl_context->display; CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglRenderer *cogl_renderer = cogl_display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; MetaRendererNativeGpuData *renderer_gpu_data = cogl_renderer_egl->platform; MetaEgl *egl = meta_renderer_native_get_egl (renderer_gpu_data->renderer_native); EGLDisplay egl_display = renderer_gpu_data->egl_display; EGLConfig egl_config; EGLStreamKHR egl_stream; EGLSurface egl_surface; EGLint num_layers; EGLOutputLayerEXT output_layer; EGLAttrib output_attribs[3]; EGLint stream_attribs[] = { EGL_STREAM_FIFO_LENGTH_KHR, 0, EGL_CONSUMER_AUTO_ACQUIRE_EXT, EGL_FALSE, EGL_NONE }; EGLint stream_producer_attribs[] = { EGL_WIDTH, width, EGL_HEIGHT, height, EGL_NONE }; egl_stream = meta_egl_create_stream (egl, egl_display, stream_attribs, error); if (egl_stream == EGL_NO_STREAM_KHR) return FALSE; output_attribs[0] = EGL_DRM_CRTC_EXT; output_attribs[1] = onscreen_native->crtc->crtc_id; output_attribs[2] = EGL_NONE; if (!meta_egl_get_output_layers (egl, egl_display, output_attribs, &output_layer, 1, &num_layers, error)) { meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL); return FALSE; } if (num_layers < 1) { meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to find output layers."); return FALSE; } if (!meta_egl_stream_consumer_output (egl, egl_display, egl_stream, output_layer, error)) { meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL); return FALSE; } egl_config = cogl_display_egl->egl_config; egl_surface = meta_egl_create_stream_producer_surface (egl, egl_display, egl_config, egl_stream, stream_producer_attribs, error); if (egl_surface == EGL_NO_SURFACE) { meta_egl_destroy_stream (egl, egl_display, egl_stream, NULL); return FALSE; } *out_egl_stream = egl_stream; *out_egl_surface = egl_surface; return TRUE; } #endif /* HAVE_EGL_DEVICE */ static gboolean init_dumb_fb (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms, int width, int height, uint32_t format, GError **error) { struct drm_mode_create_dumb create_arg; struct drm_mode_destroy_dumb destroy_arg; struct drm_mode_map_dumb map_arg; uint32_t fb_id = 0; void *map; int kms_fd; MetaGpuKmsFBArgs fb_args = { .width = width, .height = height, .format = format, }; kms_fd = meta_gpu_kms_get_fd (gpu_kms); create_arg = (struct drm_mode_create_dumb) { .bpp = 32, /* RGBX8888 */ .width = width, .height = height }; if (drmIoctl (kms_fd, DRM_IOCTL_MODE_CREATE_DUMB, &create_arg) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create dumb drm buffer: %s", g_strerror (errno)); goto err_ioctl; } fb_args.handles[0] = create_arg.handle; fb_args.strides[0] = create_arg.pitch; if (!meta_gpu_kms_add_fb (gpu_kms, FALSE, &fb_args, &fb_id, error)) goto err_add_fb; map_arg = (struct drm_mode_map_dumb) { .handle = create_arg.handle }; if (drmIoctl (kms_fd, DRM_IOCTL_MODE_MAP_DUMB, &map_arg) != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to map dumb drm buffer: %s", g_strerror (errno)); goto err_map_dumb; } map = mmap (NULL, create_arg.size, PROT_WRITE, MAP_SHARED, kms_fd, map_arg.offset); if (map == MAP_FAILED) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to mmap dumb drm buffer memory: %s", g_strerror (errno)); goto err_mmap; } dumb_fb->fb_id = fb_id; dumb_fb->handle = create_arg.handle; dumb_fb->map = map; dumb_fb->map_size = create_arg.size; dumb_fb->width = width; dumb_fb->height = height; dumb_fb->stride_bytes = create_arg.pitch; dumb_fb->drm_format = format; dumb_fb->dmabuf_fd = -1; return TRUE; err_mmap: err_map_dumb: drmModeRmFB (kms_fd, fb_id); err_add_fb: destroy_arg = (struct drm_mode_destroy_dumb) { .handle = create_arg.handle }; drmIoctl (kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); err_ioctl: return FALSE; } static int meta_dumb_buffer_ensure_dmabuf_fd (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms) { int ret; int kms_fd; int dmabuf_fd; if (dumb_fb->dmabuf_fd != -1) return dumb_fb->dmabuf_fd; kms_fd = meta_gpu_kms_get_fd (gpu_kms); ret = drmPrimeHandleToFD (kms_fd, dumb_fb->handle, DRM_CLOEXEC, &dmabuf_fd); if (ret) { g_debug ("Failed to export dumb drm buffer: %s", g_strerror (errno)); return -1; } dumb_fb->dmabuf_fd = dmabuf_fd; return dumb_fb->dmabuf_fd; } static void release_dumb_fb (MetaDumbBuffer *dumb_fb, MetaGpuKms *gpu_kms) { struct drm_mode_destroy_dumb destroy_arg; int kms_fd; if (!dumb_fb->map) return; if (dumb_fb->dmabuf_fd != -1) close (dumb_fb->dmabuf_fd); munmap (dumb_fb->map, dumb_fb->map_size); kms_fd = meta_gpu_kms_get_fd (gpu_kms); drmModeRmFB (kms_fd, dumb_fb->fb_id); destroy_arg = (struct drm_mode_destroy_dumb) { .handle = dumb_fb->handle }; drmIoctl (kms_fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy_arg); *dumb_fb = (MetaDumbBuffer) { .dmabuf_fd = -1, }; } static gboolean meta_renderer_native_init_onscreen (CoglOnscreen *onscreen, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglDisplay *cogl_display = cogl_context->display; CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglOnscreenEGL *onscreen_egl; MetaOnscreenNative *onscreen_native; g_return_val_if_fail (cogl_display_egl->egl_context, FALSE); onscreen->winsys = g_slice_new0 (CoglOnscreenEGL); onscreen_egl = onscreen->winsys; onscreen_native = g_slice_new0 (MetaOnscreenNative); onscreen_egl->platform = onscreen_native; /* * Don't actually initialize anything here, since we may not have the * information available yet, and there is no way to pass it at this stage. * To properly allocate a MetaOnscreenNative, the caller must call * meta_onscreen_native_allocate() after cogl_framebuffer_allocate(). * * TODO: Turn CoglFramebuffer/CoglOnscreen into GObjects, so it's possible * to add backend specific properties. */ return TRUE; } static gboolean meta_onscreen_native_allocate (CoglOnscreen *onscreen, GError **error) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaRendererNativeGpuData *renderer_gpu_data; struct gbm_surface *gbm_surface; EGLSurface egl_surface; int width; int height; #ifdef HAVE_EGL_DEVICE EGLStreamKHR egl_stream; #endif onscreen_native->pending_set_crtc = TRUE; /* If a kms_fd is set then the display width and height * won't be available until meta_renderer_native_set_layout * is called. In that case, defer creating the surface * until then. */ width = cogl_framebuffer_get_width (framebuffer); height = cogl_framebuffer_get_height (framebuffer); if (width == 0 || height == 0) return TRUE; renderer_gpu_data = meta_renderer_native_get_gpu_data (onscreen_native->renderer_native, onscreen_native->render_gpu); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: if (!meta_renderer_native_create_surface_gbm (onscreen, width, height, &gbm_surface, &egl_surface, error)) return FALSE; onscreen_native->gbm.surface = gbm_surface; onscreen_egl->egl_surface = egl_surface; break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: if (!init_dumb_fb (&onscreen_native->egl.dumb_fb, onscreen_native->render_gpu, width, height, DRM_FORMAT_XRGB8888, error)) return FALSE; if (!meta_renderer_native_create_surface_egl_device (onscreen, width, height, &egl_stream, &egl_surface, error)) return FALSE; onscreen_native->egl.stream = egl_stream; onscreen_egl->egl_surface = egl_surface; break; #endif /* HAVE_EGL_DEVICE */ } return TRUE; } static void destroy_egl_surface (CoglOnscreen *onscreen) { CoglOnscreenEGL *onscreen_egl = onscreen->winsys; if (onscreen_egl->egl_surface != EGL_NO_SURFACE) { MetaOnscreenNative *onscreen_native = onscreen_egl->platform; MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; meta_egl_destroy_surface (egl, cogl_renderer_egl->edpy, onscreen_egl->egl_surface, NULL); onscreen_egl->egl_surface = EGL_NO_SURFACE; } } static void meta_renderer_native_release_onscreen (CoglOnscreen *onscreen) { CoglFramebuffer *framebuffer = COGL_FRAMEBUFFER (onscreen); CoglContext *cogl_context = framebuffer->context; CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglDisplayEGL *cogl_display_egl = cogl_display->winsys; CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native; MetaRendererNative *renderer_native; MetaRendererNativeGpuData *renderer_gpu_data; /* If we never successfully allocated then there's nothing to do */ if (onscreen_egl == NULL) return; onscreen_native = onscreen_egl->platform; renderer_native = onscreen_native->renderer_native; if (onscreen_egl->egl_surface != EGL_NO_SURFACE && (cogl_display_egl->current_draw_surface == onscreen_egl->egl_surface || cogl_display_egl->current_read_surface == onscreen_egl->egl_surface)) { if (!_cogl_winsys_egl_make_current (cogl_display, cogl_display_egl->dummy_surface, cogl_display_egl->dummy_surface, cogl_display_egl->egl_context)) g_warning ("Failed to clear current context"); } renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, onscreen_native->render_gpu); switch (renderer_gpu_data->mode) { case META_RENDERER_NATIVE_MODE_GBM: /* flip state takes a reference on the onscreen so there should * never be outstanding flips when we reach here. */ g_return_if_fail (onscreen_native->gbm.next_fb == NULL); free_current_bo (onscreen); destroy_egl_surface (onscreen); if (onscreen_native->gbm.surface) { gbm_surface_destroy (onscreen_native->gbm.surface); onscreen_native->gbm.surface = NULL; } break; #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: release_dumb_fb (&onscreen_native->egl.dumb_fb, onscreen_native->render_gpu); destroy_egl_surface (onscreen); if (onscreen_native->egl.stream != EGL_NO_STREAM_KHR) { MetaEgl *egl = meta_onscreen_native_get_egl (onscreen_native); CoglRenderer *cogl_renderer = cogl_context->display->renderer; CoglRendererEGL *cogl_renderer_egl = cogl_renderer->winsys; meta_egl_destroy_stream (egl, cogl_renderer_egl->edpy, onscreen_native->egl.stream, NULL); onscreen_native->egl.stream = EGL_NO_STREAM_KHR; } break; #endif /* HAVE_EGL_DEVICE */ } g_clear_pointer (&onscreen_native->secondary_gpu_state, secondary_gpu_state_free); g_slice_free (MetaOnscreenNative, onscreen_native); g_slice_free (CoglOnscreenEGL, onscreen->winsys); onscreen->winsys = NULL; } static const CoglWinsysEGLVtable _cogl_winsys_egl_vtable = { .add_config_attributes = meta_renderer_native_add_egl_config_attributes, .choose_config = meta_renderer_native_choose_egl_config, .display_setup = meta_renderer_native_setup_egl_display, .display_destroy = meta_renderer_native_destroy_egl_display, .context_created = meta_renderer_native_egl_context_created, .cleanup_context = meta_renderer_native_egl_cleanup_context, .context_init = meta_renderer_native_init_egl_context }; static void meta_renderer_native_queue_modes_reset (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); GList *l; for (l = meta_renderer_get_views (renderer); l; l = l->next) { ClutterStageView *stage_view = l->data; CoglFramebuffer *framebuffer = clutter_stage_view_get_onscreen (stage_view); CoglOnscreen *onscreen = COGL_ONSCREEN (framebuffer); CoglOnscreenEGL *onscreen_egl = onscreen->winsys; MetaOnscreenNative *onscreen_native = onscreen_egl->platform; onscreen_native->pending_set_crtc = TRUE; } renderer_native->pending_unset_disabled_crtcs = TRUE; } static CoglOnscreen * meta_renderer_native_create_onscreen (MetaRendererNative *renderer_native, MetaGpuKms *render_gpu, MetaOutput *output, MetaCrtc *crtc, CoglContext *context, int width, int height, GError **error) { CoglOnscreen *onscreen; CoglOnscreenEGL *onscreen_egl; MetaOnscreenNative *onscreen_native; onscreen = cogl_onscreen_new (context, width, height); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (onscreen), error)) { cogl_object_unref (onscreen); return NULL; } onscreen_egl = onscreen->winsys; onscreen_native = onscreen_egl->platform; onscreen_native->renderer_native = renderer_native; onscreen_native->render_gpu = render_gpu; onscreen_native->output = output; onscreen_native->crtc = crtc; if (META_GPU_KMS (meta_crtc_get_gpu (crtc)) != render_gpu) { if (!init_secondary_gpu_state (renderer_native, onscreen, error)) { cogl_object_unref (onscreen); return NULL; } } return onscreen; } static CoglOffscreen * meta_renderer_native_create_offscreen (MetaRendererNative *renderer, CoglContext *context, gint view_width, gint view_height, GError **error) { CoglOffscreen *fb; CoglTexture2D *tex; tex = cogl_texture_2d_new_with_size (context, view_width, view_height); cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (tex), FALSE); if (!cogl_texture_allocate (COGL_TEXTURE (tex), error)) { cogl_object_unref (tex); return FALSE; } fb = cogl_offscreen_new_with_texture (COGL_TEXTURE (tex)); cogl_object_unref (tex); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (fb), error)) { cogl_object_unref (fb); return FALSE; } return fb; } static int64_t meta_renderer_native_get_clock_time (CoglContext *context) { CoglRenderer *cogl_renderer = cogl_context_get_renderer (context); MetaGpuKms *gpu_kms = cogl_renderer->custom_winsys_user_data; return meta_gpu_kms_get_current_time_ns (gpu_kms); } static const CoglWinsysVtable * get_native_cogl_winsys_vtable (CoglRenderer *cogl_renderer) { static gboolean vtable_inited = FALSE; static CoglWinsysVtable vtable; if (!vtable_inited) { /* The this winsys is a subclass of the EGL winsys so we start by copying its vtable */ parent_vtable = _cogl_winsys_egl_get_vtable (); vtable = *parent_vtable; vtable.id = COGL_WINSYS_ID_CUSTOM; vtable.name = "EGL_KMS"; vtable.renderer_connect = meta_renderer_native_connect; vtable.renderer_disconnect = meta_renderer_native_disconnect; vtable.renderer_create_dma_buf = meta_renderer_native_create_dma_buf; vtable.onscreen_init = meta_renderer_native_init_onscreen; vtable.onscreen_deinit = meta_renderer_native_release_onscreen; /* The KMS winsys doesn't support swap region */ vtable.onscreen_swap_region = NULL; vtable.onscreen_swap_buffers_with_damage = meta_onscreen_native_swap_buffers_with_damage; vtable.context_get_clock_time = meta_renderer_native_get_clock_time; vtable_inited = TRUE; } return &vtable; } static CoglRenderer * create_cogl_renderer_for_gpu (MetaGpuKms *gpu_kms) { CoglRenderer *cogl_renderer; cogl_renderer = cogl_renderer_new (); cogl_renderer_set_custom_winsys (cogl_renderer, get_native_cogl_winsys_vtable, gpu_kms); return cogl_renderer; } static CoglRenderer * meta_renderer_native_create_cogl_renderer (MetaRenderer *renderer) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); return create_cogl_renderer_for_gpu (renderer_native->primary_gpu_kms); } static void meta_onscreen_native_set_view (CoglOnscreen *onscreen, MetaRendererView *view) { CoglOnscreenEGL *onscreen_egl; MetaOnscreenNative *onscreen_native; onscreen_egl = onscreen->winsys; onscreen_native = onscreen_egl->platform; onscreen_native->view = view; } static MetaMonitorTransform calculate_view_transform (MetaMonitorManager *monitor_manager, MetaLogicalMonitor *logical_monitor, MetaOutput *output, MetaCrtc *crtc) { MetaMonitorTransform crtc_transform; crtc = meta_output_get_assigned_crtc (output); crtc_transform = meta_output_logical_to_crtc_transform (output, logical_monitor->transform); if (meta_monitor_manager_is_transform_handled (monitor_manager, crtc, crtc_transform)) return META_MONITOR_TRANSFORM_NORMAL; else return crtc_transform; } static gboolean should_force_shadow_fb (MetaRendererNative *renderer_native, MetaGpuKms *primary_gpu) { MetaRenderer *renderer = META_RENDERER (renderer_native); int kms_fd; uint64_t prefer_shadow = 0; if (meta_renderer_is_hardware_accelerated (renderer)) return FALSE; kms_fd = meta_gpu_kms_get_fd (primary_gpu); if (drmGetCap (kms_fd, DRM_CAP_DUMB_PREFER_SHADOW, &prefer_shadow) == 0) { if (prefer_shadow) { static gboolean logged_once = FALSE; if (!logged_once) { g_message ("Forcing shadow framebuffer"); logged_once = TRUE; } return TRUE; } } return FALSE; } static MetaRendererView * meta_renderer_native_create_view (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor, MetaOutput *output, MetaCrtc *crtc) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); CoglContext *cogl_context = cogl_context_from_renderer_native (renderer_native); CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); CoglDisplayEGL *cogl_display_egl; CoglOnscreenEGL *onscreen_egl; MetaCrtcConfig *crtc_config; MetaMonitorTransform view_transform; CoglOnscreen *onscreen = NULL; CoglOffscreen *offscreen = NULL; CoglOffscreen *shadowfb = NULL; float scale; int onscreen_width; int onscreen_height; MetaRectangle view_layout; MetaRendererView *view; GError *error = NULL; crtc_config = crtc->config; onscreen_width = crtc_config->mode->width; onscreen_height = crtc_config->mode->height; onscreen = meta_renderer_native_create_onscreen (renderer_native, renderer_native->primary_gpu_kms, output, crtc, cogl_context, onscreen_width, onscreen_height, &error); if (!onscreen) g_error ("Failed to allocate onscreen framebuffer: %s", error->message); view_transform = calculate_view_transform (monitor_manager, logical_monitor, output, crtc); if (view_transform != META_MONITOR_TRANSFORM_NORMAL) { int offscreen_width; int offscreen_height; if (meta_monitor_transform_is_rotated (view_transform)) { offscreen_width = onscreen_height; offscreen_height = onscreen_width; } else { offscreen_width = onscreen_width; offscreen_height = onscreen_height; } offscreen = meta_renderer_native_create_offscreen (renderer_native, cogl_context, offscreen_width, offscreen_height, &error); if (!offscreen) g_error ("Failed to allocate back buffer texture: %s", error->message); } if (should_force_shadow_fb (renderer_native, renderer_native->primary_gpu_kms)) { int shadow_width; int shadow_height; /* The shadowfb must be the same size as the on-screen framebuffer */ shadow_width = cogl_framebuffer_get_width (COGL_FRAMEBUFFER (onscreen)); shadow_height = cogl_framebuffer_get_height (COGL_FRAMEBUFFER (onscreen)); shadowfb = meta_renderer_native_create_offscreen (renderer_native, cogl_context, shadow_width, shadow_height, &error); if (!shadowfb) g_error ("Failed to allocate shadow buffer texture: %s", error->message); } if (meta_is_stage_views_scaled ()) scale = meta_logical_monitor_get_scale (logical_monitor); else scale = 1.0; meta_rectangle_from_graphene_rect (&crtc->config->layout, META_ROUNDING_STRATEGY_ROUND, &view_layout); view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &view_layout, "scale", scale, "framebuffer", onscreen, "offscreen", offscreen, "shadowfb", shadowfb, "transform", view_transform, NULL); g_clear_pointer (&offscreen, cogl_object_unref); g_clear_pointer (&shadowfb, cogl_object_unref); meta_onscreen_native_set_view (onscreen, view); if (!meta_onscreen_native_allocate (onscreen, &error)) { g_warning ("Could not create onscreen: %s", error->message); cogl_object_unref (onscreen); g_object_unref (view); g_error_free (error); return NULL; } cogl_object_unref (onscreen); /* Ensure we don't point to stale surfaces when creating the offscreen */ onscreen_egl = onscreen->winsys; cogl_display_egl = cogl_display->winsys; _cogl_winsys_egl_make_current (cogl_display, onscreen_egl->egl_surface, onscreen_egl->egl_surface, cogl_display_egl->egl_context); return view; } static void meta_renderer_native_rebuild_views (MetaRenderer *renderer) { MetaBackend *backend = meta_renderer_get_backend (renderer); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaRendererClass *parent_renderer_class = META_RENDERER_CLASS (meta_renderer_native_parent_class); meta_kms_discard_pending_page_flips (kms); parent_renderer_class->rebuild_views (renderer); meta_renderer_native_queue_modes_reset (META_RENDERER_NATIVE (renderer)); } void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKmsUpdate *kms_update = NULL; renderer_native->frame_counter++; if (renderer_native->pending_unset_disabled_crtcs) { kms_update = unset_disabled_crtcs (backend, kms); renderer_native->pending_unset_disabled_crtcs = FALSE; } if (kms_update) post_pending_update (kms); } int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native) { return renderer_native->frame_counter; } static gboolean create_secondary_egl_config (MetaEgl *egl, MetaRendererNativeMode mode, EGLDisplay egl_display, EGLConfig *egl_config, GError **error) { EGLint attributes[] = { EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, EGL_ALPHA_SIZE, EGL_DONT_CARE, EGL_BUFFER_SIZE, EGL_DONT_CARE, EGL_RENDERABLE_TYPE, EGL_OPENGL_ES3_BIT, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; switch (mode) { case META_RENDERER_NATIVE_MODE_GBM: return choose_egl_config_from_gbm_format (egl, egl_display, attributes, GBM_FORMAT_XRGB8888, egl_config, error); #ifdef HAVE_EGL_DEVICE case META_RENDERER_NATIVE_MODE_EGL_DEVICE: return meta_egl_choose_first_config (egl, egl_display, attributes, egl_config, error); #endif } return FALSE; } static EGLContext create_secondary_egl_context (MetaEgl *egl, EGLDisplay egl_display, EGLConfig egl_config, GError **error) { EGLint attributes[] = { EGL_CONTEXT_CLIENT_VERSION, 3, EGL_NONE }; return meta_egl_create_context (egl, egl_display, egl_config, EGL_NO_CONTEXT, attributes, error); } static void meta_renderer_native_ensure_gles3 (MetaRendererNative *renderer_native) { MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); if (renderer_native->gles3) return; renderer_native->gles3 = meta_gles3_new (egl); } static gboolean init_secondary_gpu_data_gpu (MetaRendererNativeGpuData *renderer_gpu_data, GError **error) { MetaRendererNative *renderer_native = renderer_gpu_data->renderer_native; MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); EGLDisplay egl_display = renderer_gpu_data->egl_display; EGLConfig egl_config; EGLContext egl_context; const char **missing_gl_extensions; const char *renderer_str; if (!create_secondary_egl_config (egl, renderer_gpu_data->mode, egl_display, &egl_config, error)) return FALSE; egl_context = create_secondary_egl_context (egl, egl_display, egl_config, error); if (egl_context == EGL_NO_CONTEXT) return FALSE; meta_renderer_native_ensure_gles3 (renderer_native); if (!meta_egl_make_current (egl, egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, egl_context, error)) { meta_egl_destroy_context (egl, egl_display, egl_context, NULL); return FALSE; } renderer_str = (const char *) glGetString (GL_RENDERER); if (g_str_has_prefix (renderer_str, "llvmpipe") || g_str_has_prefix (renderer_str, "softpipe") || g_str_has_prefix (renderer_str, "swrast")) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Do not want to use software renderer (%s), falling back to CPU copy path", renderer_str); goto out_fail_with_context; } if (!meta_gles3_has_extensions (renderer_native->gles3, &missing_gl_extensions, "GL_OES_EGL_image_external", NULL)) { char *missing_gl_extensions_str; missing_gl_extensions_str = g_strjoinv (", ", (char **) missing_gl_extensions); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing OpenGL ES extensions: %s", missing_gl_extensions_str); g_free (missing_gl_extensions_str); g_free (missing_gl_extensions); goto out_fail_with_context; } renderer_gpu_data->secondary.is_hardware_rendering = TRUE; renderer_gpu_data->secondary.egl_context = egl_context; renderer_gpu_data->secondary.egl_config = egl_config; renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_SECONDARY_GPU; renderer_gpu_data->secondary.has_EGL_EXT_image_dma_buf_import_modifiers = meta_egl_has_extensions (egl, egl_display, NULL, "EGL_EXT_image_dma_buf_import_modifiers", NULL); return TRUE; out_fail_with_context: meta_egl_make_current (egl, egl_display, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT, NULL); meta_egl_destroy_context (egl, egl_display, egl_context, NULL); return FALSE; } static void init_secondary_gpu_data_cpu (MetaRendererNativeGpuData *renderer_gpu_data) { renderer_gpu_data->secondary.is_hardware_rendering = FALSE; /* First try ZERO, it automatically falls back to PRIMARY as needed */ renderer_gpu_data->secondary.copy_mode = META_SHARED_FRAMEBUFFER_COPY_MODE_ZERO; } static void init_secondary_gpu_data (MetaRendererNativeGpuData *renderer_gpu_data) { GError *error = NULL; if (init_secondary_gpu_data_gpu (renderer_gpu_data, &error)) return; g_warning ("Failed to initialize accelerated iGPU/dGPU framebuffer sharing: %s", error->message); g_error_free (error); init_secondary_gpu_data_cpu (renderer_gpu_data); } static gboolean gpu_kms_is_hardware_rendering (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms) { MetaRendererNativeGpuData *data; data = meta_renderer_native_get_gpu_data (renderer_native, gpu_kms); return data->secondary.is_hardware_rendering; } static EGLDisplay init_gbm_egl_display (MetaRendererNative *renderer_native, struct gbm_device *gbm_device, GError **error) { MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); EGLDisplay egl_display; if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL, "EGL_MESA_platform_gbm", NULL) && !meta_egl_has_extensions (egl, EGL_NO_DISPLAY, NULL, "EGL_KHR_platform_gbm", NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing extension for GBM renderer: EGL_KHR_platform_gbm"); return EGL_NO_DISPLAY; } egl_display = meta_egl_get_platform_display (egl, EGL_PLATFORM_GBM_KHR, gbm_device, NULL, error); if (egl_display == EGL_NO_DISPLAY) return EGL_NO_DISPLAY; if (!meta_egl_initialize (egl, egl_display, error)) return EGL_NO_DISPLAY; return egl_display; } static MetaRendererNativeGpuData * create_renderer_gpu_data_gbm (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, GError **error) { struct gbm_device *gbm_device; int kms_fd; MetaRendererNativeGpuData *renderer_gpu_data; g_autoptr (GError) local_error = NULL; kms_fd = meta_gpu_kms_get_fd (gpu_kms); gbm_device = gbm_create_device (kms_fd); if (!gbm_device) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create gbm device: %s", g_strerror (errno)); return NULL; } renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms); renderer_gpu_data->renderer_native = renderer_native; renderer_gpu_data->gbm.device = gbm_device; renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_GBM; renderer_gpu_data->egl_display = init_gbm_egl_display (renderer_native, gbm_device, &local_error); if (renderer_gpu_data->egl_display == EGL_NO_DISPLAY) { g_debug ("GBM EGL init for %s failed: %s", meta_gpu_kms_get_file_path (gpu_kms), local_error->message); init_secondary_gpu_data_cpu (renderer_gpu_data); return renderer_gpu_data; } init_secondary_gpu_data (renderer_gpu_data); return renderer_gpu_data; } #ifdef HAVE_EGL_DEVICE static const char * get_drm_device_file (MetaEgl *egl, EGLDeviceEXT device, GError **error) { if (!meta_egl_egl_device_has_extensions (egl, device, NULL, "EGL_EXT_device_drm", NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing required EGLDevice extension EGL_EXT_device_drm"); return NULL; } return meta_egl_query_device_string (egl, device, EGL_DRM_DEVICE_FILE_EXT, error); } static EGLDeviceEXT find_egl_device (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, GError **error) { MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); const char **missing_extensions; EGLint num_devices; EGLDeviceEXT *devices; const char *kms_file_path; EGLDeviceEXT device; EGLint i; if (!meta_egl_has_extensions (egl, EGL_NO_DISPLAY, &missing_extensions, "EGL_EXT_device_base", NULL)) { char *missing_extensions_str; missing_extensions_str = g_strjoinv (", ", (char **) missing_extensions); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing EGL extensions required for EGLDevice renderer: %s", missing_extensions_str); g_free (missing_extensions_str); g_free (missing_extensions); return EGL_NO_DEVICE_EXT; } if (!meta_egl_query_devices (egl, 0, NULL, &num_devices, error)) return EGL_NO_DEVICE_EXT; devices = g_new0 (EGLDeviceEXT, num_devices); if (!meta_egl_query_devices (egl, num_devices, devices, &num_devices, error)) { g_free (devices); return EGL_NO_DEVICE_EXT; } kms_file_path = meta_gpu_kms_get_file_path (gpu_kms); device = EGL_NO_DEVICE_EXT; for (i = 0; i < num_devices; i++) { const char *egl_device_drm_path; g_clear_error (error); egl_device_drm_path = get_drm_device_file (egl, devices[i], error); if (!egl_device_drm_path) continue; if (g_str_equal (egl_device_drm_path, kms_file_path)) { device = devices[i]; break; } } g_free (devices); if (device == EGL_NO_DEVICE_EXT) { if (!*error) g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to find matching EGLDeviceEXT"); return EGL_NO_DEVICE_EXT; } return device; } static EGLDisplay get_egl_device_display (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, EGLDeviceEXT egl_device, GError **error) { MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); int kms_fd = meta_gpu_kms_get_fd (gpu_kms); EGLint platform_attribs[] = { EGL_DRM_MASTER_FD_EXT, kms_fd, EGL_NONE }; return meta_egl_get_platform_display (egl, EGL_PLATFORM_DEVICE_EXT, (void *) egl_device, platform_attribs, error); } static int count_drm_devices (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); return g_list_length (meta_backend_get_gpus (backend)); } static MetaRendererNativeGpuData * create_renderer_gpu_data_egl_device (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, GError **error) { MetaEgl *egl = meta_renderer_native_get_egl (renderer_native); const char **missing_extensions; EGLDeviceEXT egl_device; EGLDisplay egl_display; MetaRendererNativeGpuData *renderer_gpu_data; if (count_drm_devices (renderer_native) != 1) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "EGLDevice currently only works with single GPU systems"); return NULL; } egl_device = find_egl_device (renderer_native, gpu_kms, error); if (egl_device == EGL_NO_DEVICE_EXT) return NULL; egl_display = get_egl_device_display (renderer_native, gpu_kms, egl_device, error); if (egl_display == EGL_NO_DISPLAY) return NULL; if (!meta_egl_initialize (egl, egl_display, error)) return NULL; if (!meta_egl_has_extensions (egl, egl_display, &missing_extensions, "EGL_NV_output_drm_flip_event", "EGL_EXT_output_base", "EGL_EXT_output_drm", "EGL_KHR_stream", "EGL_KHR_stream_producer_eglsurface", "EGL_EXT_stream_consumer_egloutput", "EGL_EXT_stream_acquire_mode", NULL)) { char *missing_extensions_str; missing_extensions_str = g_strjoinv (", ", (char **) missing_extensions); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing EGL extensions required for EGLDevice renderer: %s", missing_extensions_str); meta_egl_terminate (egl, egl_display, NULL); g_free (missing_extensions_str); g_free (missing_extensions); return NULL; } renderer_gpu_data = meta_create_renderer_native_gpu_data (gpu_kms); renderer_gpu_data->renderer_native = renderer_native; renderer_gpu_data->egl.device = egl_device; renderer_gpu_data->mode = META_RENDERER_NATIVE_MODE_EGL_DEVICE; renderer_gpu_data->egl_display = egl_display; return renderer_gpu_data; } #endif /* HAVE_EGL_DEVICE */ static MetaRendererNativeGpuData * meta_renderer_native_create_renderer_gpu_data (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, GError **error) { MetaRendererNativeGpuData *renderer_gpu_data; GError *gbm_error = NULL; #ifdef HAVE_EGL_DEVICE GError *egl_device_error = NULL; #endif #ifdef HAVE_EGL_DEVICE /* Try to initialize the EGLDevice backend first. Whenever we use a * non-NVIDIA GPU, the EGLDevice enumeration function won't find a match, and * we'll fall back to GBM (which will always succeed as it has a software * rendering fallback) */ renderer_gpu_data = create_renderer_gpu_data_egl_device (renderer_native, gpu_kms, &egl_device_error); if (renderer_gpu_data) return renderer_gpu_data; #endif renderer_gpu_data = create_renderer_gpu_data_gbm (renderer_native, gpu_kms, &gbm_error); if (renderer_gpu_data) { #ifdef HAVE_EGL_DEVICE g_error_free (egl_device_error); #endif return renderer_gpu_data; } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to initialize renderer: " "%s" #ifdef HAVE_EGL_DEVICE ", %s" #endif , gbm_error->message #ifdef HAVE_EGL_DEVICE , egl_device_error->message #endif ); g_error_free (gbm_error); #ifdef HAVE_EGL_DEVICE g_error_free (egl_device_error); #endif return NULL; } static gboolean create_renderer_gpu_data (MetaRendererNative *renderer_native, MetaGpuKms *gpu_kms, GError **error) { MetaRendererNativeGpuData *renderer_gpu_data; renderer_gpu_data = meta_renderer_native_create_renderer_gpu_data (renderer_native, gpu_kms, error); if (!renderer_gpu_data) return FALSE; g_hash_table_insert (renderer_native->gpu_datas, gpu_kms, renderer_gpu_data); return TRUE; } static void on_gpu_added (MetaBackendNative *backend_native, MetaGpuKms *gpu_kms, MetaRendererNative *renderer_native) { MetaBackend *backend = META_BACKEND (backend_native); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglDisplay *cogl_display = cogl_context_get_display (cogl_context); GError *error = NULL; if (!create_renderer_gpu_data (renderer_native, gpu_kms, &error)) { g_warning ("on_gpu_added: could not create gpu_data for gpu %s: %s", meta_gpu_kms_get_file_path (gpu_kms), error->message); g_clear_error (&error); } _cogl_winsys_egl_ensure_current (cogl_display); } static void on_power_save_mode_changed (MetaMonitorManager *monitor_manager, MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaPowerSave power_save_mode; power_save_mode = meta_monitor_manager_get_power_save_mode (monitor_manager); if (power_save_mode == META_POWER_SAVE_ON) meta_renderer_native_queue_modes_reset (renderer_native); else meta_kms_discard_pending_page_flips (kms); } void meta_renderer_native_reset_modes (MetaRendererNative *renderer_native) { MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKmsUpdate *kms_update; kms_update = unset_disabled_crtcs (backend, kms); if (kms_update) post_pending_update (kms); } static MetaGpuKms * choose_primary_gpu_unchecked (MetaBackend *backend, MetaRendererNative *renderer_native) { GList *gpus = meta_backend_get_gpus (backend); GList *l; int allow_sw; /* * Check first hardware rendering devices, and if none found, * then software rendering devices. */ for (allow_sw = 0; allow_sw < 2; allow_sw++) { /* Prefer a platform device */ for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = META_GPU_KMS (l->data); if (meta_gpu_kms_is_platform_device (gpu_kms) && (allow_sw == 1 || gpu_kms_is_hardware_rendering (renderer_native, gpu_kms))) return gpu_kms; } /* Otherwise a device we booted with */ for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = META_GPU_KMS (l->data); if (meta_gpu_kms_is_boot_vga (gpu_kms) && (allow_sw == 1 || gpu_kms_is_hardware_rendering (renderer_native, gpu_kms))) return gpu_kms; } /* Fall back to any device */ for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = META_GPU_KMS (l->data); if (allow_sw == 1 || gpu_kms_is_hardware_rendering (renderer_native, gpu_kms)) return gpu_kms; } } g_assert_not_reached (); return NULL; } static MetaGpuKms * choose_primary_gpu (MetaBackend *backend, MetaRendererNative *renderer_native, GError **error) { MetaGpuKms *gpu_kms; MetaRendererNativeGpuData *renderer_gpu_data; gpu_kms = choose_primary_gpu_unchecked (backend, renderer_native); renderer_gpu_data = meta_renderer_native_get_gpu_data (renderer_native, gpu_kms); if (renderer_gpu_data->egl_display == EGL_NO_DISPLAY) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "The GPU %s chosen as primary is not supported by EGL.", meta_gpu_kms_get_file_path (gpu_kms)); return NULL; } return gpu_kms; } static gboolean meta_renderer_native_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (initable); MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); GList *gpus; GList *l; gpus = meta_backend_get_gpus (backend); for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = META_GPU_KMS (l->data); if (!create_renderer_gpu_data (renderer_native, gpu_kms, error)) return FALSE; } renderer_native->primary_gpu_kms = choose_primary_gpu (backend, renderer_native, error); if (!renderer_native->primary_gpu_kms) return FALSE; return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = meta_renderer_native_initable_init; } static void meta_renderer_native_finalize (GObject *object) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); if (renderer_native->power_save_page_flip_onscreens) { g_list_free_full (renderer_native->power_save_page_flip_onscreens, (GDestroyNotify) cogl_object_unref); g_clear_handle_id (&renderer_native->power_save_page_flip_source_id, g_source_remove); } g_hash_table_destroy (renderer_native->gpu_datas); g_clear_object (&renderer_native->gles3); G_OBJECT_CLASS (meta_renderer_native_parent_class)->finalize (object); } static void meta_renderer_native_constructed (GObject *object) { MetaRendererNative *renderer_native = META_RENDERER_NATIVE (object); MetaRenderer *renderer = META_RENDERER (renderer_native); MetaBackend *backend = meta_renderer_get_backend (renderer); MetaSettings *settings = meta_backend_get_settings (backend); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS)) renderer_native->use_modifiers = TRUE; g_signal_connect (backend, "gpu-added", G_CALLBACK (on_gpu_added), renderer_native); g_signal_connect (monitor_manager, "power-save-mode-changed", G_CALLBACK (on_power_save_mode_changed), renderer_native); G_OBJECT_CLASS (meta_renderer_native_parent_class)->constructed (object); } static void meta_renderer_native_init (MetaRendererNative *renderer_native) { renderer_native->gpu_datas = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_renderer_native_gpu_data_free); } static void meta_renderer_native_class_init (MetaRendererNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass); object_class->finalize = meta_renderer_native_finalize; object_class->constructed = meta_renderer_native_constructed; renderer_class->create_cogl_renderer = meta_renderer_native_create_cogl_renderer; renderer_class->create_view = meta_renderer_native_create_view; renderer_class->rebuild_views = meta_renderer_native_rebuild_views; } MetaRendererNative * meta_renderer_native_new (MetaBackendNative *backend_native, GError **error) { return g_initable_new (META_TYPE_RENDERER_NATIVE, NULL, error, "backend", backend_native, NULL); } muffin-6.4.1/src/backends/native/meta-kms-device.h0000664000175000017500000000440514723361714020720 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_DEVICE_H #define META_KMS_DEVICE_H #include #include "backends/native/meta-kms-types.h" #define META_TYPE_KMS_DEVICE (meta_kms_device_get_type ()) G_DECLARE_FINAL_TYPE (MetaKmsDevice, meta_kms_device, META, KMS_DEVICE, GObject) int meta_kms_device_leak_fd (MetaKmsDevice *device); const char * meta_kms_device_get_path (MetaKmsDevice *device); MetaKmsDeviceFlag meta_kms_device_get_flags (MetaKmsDevice *device); gboolean meta_kms_device_get_cursor_size (MetaKmsDevice *device, uint64_t *out_cursor_width, uint64_t *out_cursor_height); GList * meta_kms_device_get_connectors (MetaKmsDevice *device); GList * meta_kms_device_get_crtcs (MetaKmsDevice *device); MetaKmsPlane * meta_kms_device_get_primary_plane_for (MetaKmsDevice *device, MetaKmsCrtc *crtc); MetaKmsPlane * meta_kms_device_get_cursor_plane_for (MetaKmsDevice *device, MetaKmsCrtc *crtc); int meta_kms_device_dispatch_sync (MetaKmsDevice *device, GError **error); MetaKmsDevice * meta_kms_device_new (MetaKms *kms, const char *path, MetaKmsDeviceFlag flags, GError **error); #endif /* META_KMS_DEVICE_H */ muffin-6.4.1/src/backends/native/meta-keymap-native.h0000664000175000017500000000263614723361714021447 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_KEYMAP_NATIVE_H #define META_KEYMAP_NATIVE_H #include "backends/native/meta-xkb-utils.h" #include "clutter/clutter.h" #define META_TYPE_KEYMAP_NATIVE (meta_keymap_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaKeymapNative, meta_keymap_native, META, KEYMAP_NATIVE, ClutterKeymap) void meta_keymap_native_set_keyboard_map (MetaKeymapNative *keymap, struct xkb_keymap *xkb_keymap); struct xkb_keymap * meta_keymap_native_get_keyboard_map (MetaKeymapNative *keymap); #endif /* META_KEYMAP_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-kms-update.c0000664000175000017500000002632514723361714020743 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms-update-private.h" #include "backends/meta-display-config-shared.h" #include "backends/native/meta-kms-plane.h" struct _MetaKmsUpdate { gboolean is_sealed; MetaPowerSave power_save; GList *mode_sets; GList *plane_assignments; GList *page_flips; GList *connector_properties; GList *crtc_gammas; }; void meta_kms_plane_feedback_free (MetaKmsPlaneFeedback *plane_feedback) { g_error_free (plane_feedback->error); g_free (plane_feedback); } MetaKmsPlaneFeedback * meta_kms_plane_feedback_new_take_error (MetaKmsPlane *plane, MetaKmsCrtc *crtc, GError *error) { MetaKmsPlaneFeedback *plane_feedback; plane_feedback = g_new0 (MetaKmsPlaneFeedback, 1); *plane_feedback = (MetaKmsPlaneFeedback) { .plane = plane, .crtc = crtc, .error = error, }; return plane_feedback; } MetaKmsFeedback * meta_kms_feedback_new_passed (void) { MetaKmsFeedback *feedback; feedback = g_new0 (MetaKmsFeedback, 1); *feedback = (MetaKmsFeedback) { .result = META_KMS_FEEDBACK_PASSED, }; return feedback; } MetaKmsFeedback * meta_kms_feedback_new_failed (GList *failed_planes, GError *error) { MetaKmsFeedback *feedback; feedback = g_new0 (MetaKmsFeedback, 1); *feedback = (MetaKmsFeedback) { .result = META_KMS_FEEDBACK_FAILED, .error = error, .failed_planes = failed_planes, }; return feedback; } void meta_kms_feedback_free (MetaKmsFeedback *feedback) { g_list_free_full (feedback->failed_planes, (GDestroyNotify) meta_kms_plane_feedback_free); g_clear_error (&feedback->error); g_free (feedback); } MetaKmsFeedbackResult meta_kms_feedback_get_result (MetaKmsFeedback *feedback) { return feedback->result; } GList * meta_kms_feedback_get_failed_planes (MetaKmsFeedback *feedback) { return feedback->failed_planes; } const GError * meta_kms_feedback_get_error (MetaKmsFeedback *feedback) { return feedback->error; } static MetaKmsProperty * meta_kms_property_new (uint32_t prop_id, uint64_t value) { MetaKmsProperty *prop; prop = g_new0 (MetaKmsProperty, 1); *prop = (MetaKmsProperty) { .prop_id = prop_id, .value = value, }; return prop; } static void meta_kms_property_free (MetaKmsProperty *prop) { g_free (prop); } static void meta_kms_plane_assignment_free (MetaKmsPlaneAssignment *plane_assignment) { g_list_free_full (plane_assignment->plane_properties, (GDestroyNotify) meta_kms_property_free); g_free (plane_assignment); } static void meta_kms_mode_set_free (MetaKmsModeSet *mode_set) { g_free (mode_set->drm_mode); g_list_free (mode_set->connectors); g_free (mode_set); } MetaKmsPlaneAssignment * meta_kms_update_assign_plane (MetaKmsUpdate *update, MetaKmsCrtc *crtc, MetaKmsPlane *plane, uint32_t fb_id, MetaFixed16Rectangle src_rect, MetaFixed16Rectangle dst_rect, MetaKmsAssignPlaneFlag flags) { MetaKmsPlaneAssignment *plane_assignment; g_assert (!meta_kms_update_is_sealed (update)); plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1); *plane_assignment = (MetaKmsPlaneAssignment) { .update = update, .crtc = crtc, .plane = plane, .fb_id = fb_id, .src_rect = src_rect, .dst_rect = dst_rect, .flags = flags, }; update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); return plane_assignment; } MetaKmsPlaneAssignment * meta_kms_update_unassign_plane (MetaKmsUpdate *update, MetaKmsCrtc *crtc, MetaKmsPlane *plane) { MetaKmsPlaneAssignment *plane_assignment; g_assert (!meta_kms_update_is_sealed (update)); plane_assignment = g_new0 (MetaKmsPlaneAssignment, 1); *plane_assignment = (MetaKmsPlaneAssignment) { .update = update, .crtc = crtc, .plane = plane, .fb_id = 0, }; update->plane_assignments = g_list_prepend (update->plane_assignments, plane_assignment); return plane_assignment; } void meta_kms_update_mode_set (MetaKmsUpdate *update, MetaKmsCrtc *crtc, GList *connectors, drmModeModeInfo *drm_mode) { MetaKmsModeSet *mode_set; g_assert (!meta_kms_update_is_sealed (update)); mode_set = g_new0 (MetaKmsModeSet, 1); *mode_set = (MetaKmsModeSet) { .crtc = crtc, .connectors = connectors, .drm_mode = drm_mode ? g_memdup2 (drm_mode, sizeof *drm_mode) : NULL, }; update->mode_sets = g_list_prepend (update->mode_sets, mode_set); } void meta_kms_update_set_connector_property (MetaKmsUpdate *update, MetaKmsConnector *connector, uint32_t prop_id, uint64_t value) { MetaKmsConnectorProperty *prop; g_assert (!meta_kms_update_is_sealed (update)); prop = g_new0 (MetaKmsConnectorProperty, 1); *prop = (MetaKmsConnectorProperty) { .connector = connector, .prop_id = prop_id, .value = value, }; update->connector_properties = g_list_prepend (update->connector_properties, prop); } static void meta_kms_crtc_gamma_free (MetaKmsCrtcGamma *gamma) { g_free (gamma->red); g_free (gamma->green); g_free (gamma->blue); g_free (gamma); } void meta_kms_update_set_crtc_gamma (MetaKmsUpdate *update, MetaKmsCrtc *crtc, int size, const uint16_t *red, const uint16_t *green, const uint16_t *blue) { MetaKmsCrtcGamma *gamma; g_assert (!meta_kms_update_is_sealed (update)); gamma = g_new0 (MetaKmsCrtcGamma, 1); *gamma = (MetaKmsCrtcGamma) { .crtc = crtc, .size = size, .red = g_memdup2 (red, size * sizeof *red), .green = g_memdup2 (green, size * sizeof *green), .blue = g_memdup2 (blue, size * sizeof *blue), }; update->crtc_gammas = g_list_prepend (update->crtc_gammas, gamma); } void meta_kms_update_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data) { MetaKmsPageFlip *page_flip; g_assert (!meta_kms_update_is_sealed (update)); page_flip = g_new0 (MetaKmsPageFlip, 1); *page_flip = (MetaKmsPageFlip) { .crtc = crtc, .feedback = feedback, .user_data = user_data, }; update->page_flips = g_list_prepend (update->page_flips, page_flip); } void meta_kms_update_custom_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data, MetaKmsCustomPageFlipFunc custom_page_flip_func, gpointer custom_page_flip_user_data) { MetaKmsPageFlip *page_flip; g_assert (!meta_kms_update_is_sealed (update)); page_flip = g_new0 (MetaKmsPageFlip, 1); *page_flip = (MetaKmsPageFlip) { .crtc = crtc, .feedback = feedback, .user_data = user_data, .custom_page_flip_func = custom_page_flip_func, .custom_page_flip_user_data = custom_page_flip_user_data, }; update->page_flips = g_list_prepend (update->page_flips, page_flip); } void meta_kms_plane_assignment_set_plane_property (MetaKmsPlaneAssignment *plane_assignment, uint32_t prop_id, uint64_t value) { MetaKmsProperty *plane_prop; g_assert (!meta_kms_update_is_sealed (plane_assignment->update)); plane_prop = meta_kms_property_new (prop_id, value); plane_assignment->plane_properties = g_list_prepend (plane_assignment->plane_properties, plane_prop); } void meta_kms_plane_assignment_set_cursor_hotspot (MetaKmsPlaneAssignment *plane_assignment, int x, int y) { plane_assignment->cursor_hotspot.is_valid = TRUE; plane_assignment->cursor_hotspot.x = x; plane_assignment->cursor_hotspot.y = y; } MetaKmsPlaneAssignment * meta_kms_update_get_primary_plane_assignment (MetaKmsUpdate *update, MetaKmsCrtc *crtc) { GList *l; for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next) { MetaKmsPlaneAssignment *plane_assignment = l->data; if (plane_assignment->crtc == crtc) return plane_assignment; } return NULL; } GList * meta_kms_update_get_plane_assignments (MetaKmsUpdate *update) { return update->plane_assignments; } GList * meta_kms_update_get_mode_sets (MetaKmsUpdate *update) { return update->mode_sets; } GList * meta_kms_update_get_page_flips (MetaKmsUpdate *update) { return update->page_flips; } GList * meta_kms_update_get_connector_properties (MetaKmsUpdate *update) { return update->connector_properties; } GList * meta_kms_update_get_crtc_gammas (MetaKmsUpdate *update) { return update->crtc_gammas; } void meta_kms_update_seal (MetaKmsUpdate *update) { update->is_sealed = TRUE; } gboolean meta_kms_update_is_sealed (MetaKmsUpdate *update) { return update->is_sealed; } MetaKmsUpdate * meta_kms_update_new (void) { return g_new0 (MetaKmsUpdate, 1); } void meta_kms_update_free (MetaKmsUpdate *update) { g_list_free_full (update->plane_assignments, (GDestroyNotify) meta_kms_plane_assignment_free); g_list_free_full (update->mode_sets, (GDestroyNotify) meta_kms_mode_set_free); g_list_free_full (update->page_flips, g_free); g_list_free_full (update->connector_properties, g_free); g_list_free_full (update->crtc_gammas, (GDestroyNotify) meta_kms_crtc_gamma_free); g_free (update); } muffin-6.4.1/src/backends/native/meta-backend-native.h0000664000175000017500000000352114723361714021542 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_NATIVE_H #define META_BACKEND_NATIVE_H #include "backends/meta-backend-private.h" #include "backends/native/meta-clutter-backend-native.h" #include "backends/native/meta-kms-types.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-udev.h" #define META_TYPE_BACKEND_NATIVE (meta_backend_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaBackendNative, meta_backend_native, META, BACKEND_NATIVE, MetaBackend) gboolean meta_activate_vt (int vt, GError **error); void meta_backend_native_pause (MetaBackendNative *backend_native); void meta_backend_native_resume (MetaBackendNative *backend_native); MetaLauncher * meta_backend_native_get_launcher (MetaBackendNative *native); MetaUdev * meta_backend_native_get_udev (MetaBackendNative *native); MetaKms * meta_backend_native_get_kms (MetaBackendNative *native); void meta_backend_native_set_seat_id (const gchar *seat_id); #endif /* META_BACKEND_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-backend-native-private.h0000664000175000017500000000217514723361714023216 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_BACKEND_NATIVE_PRIVATE_H #define META_BACKEND_NATIVE_PRIVATE_H #include "backends/native/meta-barrier-native.h" MetaBarrierManagerNative *meta_backend_native_get_barrier_manager (MetaBackendNative *native); #endif /* META_BACKEND_NATIVE_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-clutter-backend-native.h0000664000175000017500000000305214723361714023221 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_CLUTTER_BACKEND_NATIVE_H #define META_CLUTTER_BACKEND_NATIVE_H #include #include "backends/native/meta-stage-native.h" #include "clutter/clutter.h" #include "clutter/egl/clutter-backend-eglnative.h" #define META_TYPE_CLUTTER_BACKEND_NATIVE (meta_clutter_backend_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaClutterBackendNative, meta_clutter_backend_native, META, CLUTTER_BACKEND_NATIVE, ClutterBackendEglNative) MetaStageNative * meta_clutter_backend_native_get_stage_native (ClutterBackend *backend); void meta_clutter_backend_native_set_seat_id (const gchar *seat_id); #endif /* META_CLUTTER_BACKEND_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-seat-native.h0000664000175000017500000003245114723361714021113 0ustar fabiofabio/* * Copyright (C) 2010 Intel Corp. * Copyright (C) 2014 Jonas Ådahl * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Damien Lespiau * Author: Jonas Ådahl */ #ifndef META_SEAT_NATIVE_H #define META_SEAT_NATIVE_H #include #include #include #include "backends/native/meta-keymap-native.h" #include "backends/native/meta-xkb-utils.h" #include "clutter/clutter.h" typedef struct _MetaTouchState MetaTouchState; typedef struct _MetaSeatNative MetaSeatNative; typedef struct _MetaEventSource MetaEventSource; /** * MetaPointerConstrainCallback: * @device: the core pointer device * @time: the event time in milliseconds * @x: (inout): the new X coordinate * @y: (inout): the new Y coordinate * @user_data: user data passed to this function * * This callback will be called for all pointer motion events, and should * update (@x, @y) to constrain the pointer position appropriately. * The subsequent motion event will use the updated values as the new coordinates. * Note that the coordinates are not clamped to the stage size, and the callback * must make sure that this happens before it returns. * Also note that the event will be emitted even if the pointer is constrained * to be in the same position. */ typedef void (* MetaPointerConstrainCallback) (ClutterInputDevice *device, uint32_t time, float prev_x, float prev_y, float *x, float *y, gpointer user_data); typedef void (* MetaRelativeMotionFilter) (ClutterInputDevice *device, float x, float y, float *dx, float *dy, gpointer user_data); struct _MetaTouchState { MetaSeatNative *seat; int device_slot; int seat_slot; graphene_point_t coords; }; struct _MetaSeatNative { ClutterSeat parent_instance; char *seat_id; MetaEventSource *event_source; struct libinput *libinput; struct libinput_seat *libinput_seat; GSList *devices; ClutterInputDevice *core_pointer; ClutterInputDevice *core_keyboard; MetaTouchState **touch_states; int n_alloc_touch_states; struct xkb_state *xkb; xkb_led_index_t caps_lock_led; xkb_led_index_t num_lock_led; xkb_led_index_t scroll_lock_led; xkb_layout_index_t layout_idx; uint32_t button_state; int button_count[KEY_CNT]; ClutterStage *stage; ClutterStageManager *stage_manager; gulong stage_added_handler; gulong stage_removed_handler; int device_id_next; GList *free_device_ids; MetaPointerConstrainCallback constrain_callback; gpointer constrain_data; GDestroyNotify constrain_data_notify; MetaRelativeMotionFilter relative_motion_filter; gpointer relative_motion_filter_user_data; GSList *event_filters; MetaKeymapNative *keymap; GUdevClient *udev_client; guint tablet_mode_switch_state : 1; guint has_touchscreen : 1; guint has_tablet_switch : 1; guint touch_mode : 1; /* keyboard repeat */ gboolean repeat; uint32_t repeat_delay; uint32_t repeat_interval; uint32_t repeat_key; uint32_t repeat_count; uint32_t repeat_timer; ClutterInputDevice *repeat_device; float pointer_x; float pointer_y; /* Emulation of discrete scroll events out of smooth ones */ float accum_scroll_dx; float accum_scroll_dy; gboolean released; }; #define META_TYPE_SEAT_NATIVE meta_seat_native_get_type () G_DECLARE_FINAL_TYPE (MetaSeatNative, meta_seat_native, META, SEAT_NATIVE, ClutterSeat) static inline uint64_t us (uint64_t us) { return us; } static inline uint64_t ms2us (uint64_t ms) { return us (ms * 1000); } static inline uint32_t us2ms (uint64_t us) { return (uint32_t) (us / 1000); } void meta_seat_native_notify_key (MetaSeatNative *seat, ClutterInputDevice *device, uint64_t time_us, uint32_t key, uint32_t state, gboolean update_keys); void meta_seat_native_notify_relative_motion (MetaSeatNative *seat_evdev, ClutterInputDevice *input_device, uint64_t time_us, float dx, float dy, float dx_unaccel, float dy_unaccel); void meta_seat_native_notify_absolute_motion (MetaSeatNative *seat_evdev, ClutterInputDevice *input_device, uint64_t time_us, float x, float y, double *axes); void meta_seat_native_notify_button (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, uint32_t button, uint32_t state); void meta_seat_native_notify_scroll_continuous (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, double dx, double dy, ClutterScrollSource source, ClutterScrollFinishFlags flags); void meta_seat_native_notify_discrete_scroll (MetaSeatNative *seat, ClutterInputDevice *input_device, uint64_t time_us, double discrete_dx, double discrete_dy, ClutterScrollSource source); void meta_seat_native_notify_touch_event (MetaSeatNative *seat, ClutterInputDevice *input_device, ClutterEventType evtype, uint64_t time_us, int slot, double x, double y); void meta_seat_native_set_libinput_seat (MetaSeatNative *seat, struct libinput_seat *libinput_seat); void meta_seat_native_sync_leds (MetaSeatNative *seat); ClutterInputDevice * meta_seat_native_get_device (MetaSeatNative *seat, int id); MetaTouchState * meta_seat_native_acquire_touch_state (MetaSeatNative *seat, int device_slot); void meta_seat_native_release_touch_state (MetaSeatNative *seat, MetaTouchState *touch_state); void meta_seat_native_set_stage (MetaSeatNative *seat, ClutterStage *stage); ClutterStage * meta_seat_native_get_stage (MetaSeatNative *seat); void meta_seat_native_clear_repeat_timer (MetaSeatNative *seat); gint meta_seat_native_acquire_device_id (MetaSeatNative *seat); void meta_seat_native_release_device_id (MetaSeatNative *seat, ClutterInputDevice *device); void meta_seat_native_update_xkb_state (MetaSeatNative *seat); void meta_seat_native_constrain_pointer (MetaSeatNative *seat, ClutterInputDevice *core_pointer, uint64_t time_us, float x, float y, float *new_x, float *new_y); void meta_seat_native_filter_relative_motion (MetaSeatNative *seat, ClutterInputDevice *device, float x, float y, float *dx, float *dy); void meta_seat_native_dispatch (MetaSeatNative *seat); /** * MetaOpenDeviceCallback: * @path: the device path * @flags: flags to be passed to open * * This callback will be called when Clutter needs to access an input * device. It should return an open file descriptor for the file at @path, * or -1 if opening failed. */ typedef int (* MetaOpenDeviceCallback) (const char *path, int flags, gpointer user_data, GError **error); typedef void (* MetaCloseDeviceCallback) (int fd, gpointer user_data); void meta_seat_native_set_device_callbacks (MetaOpenDeviceCallback open_callback, MetaCloseDeviceCallback close_callback, gpointer user_data); void meta_seat_native_release_devices (MetaSeatNative *seat); void meta_seat_native_reclaim_devices (MetaSeatNative *seat); void meta_seat_native_set_pointer_constrain_callback (MetaSeatNative *seat, MetaPointerConstrainCallback callback, gpointer user_data, GDestroyNotify user_data_notify); void meta_seat_native_set_relative_motion_filter (MetaSeatNative *seat, MetaRelativeMotionFilter filter, gpointer user_data); typedef gboolean (* MetaEvdevFilterFunc) (struct libinput_event *event, gpointer data); void meta_seat_native_add_filter (MetaSeatNative *seat, MetaEvdevFilterFunc func, gpointer data, GDestroyNotify destroy_notify); void meta_seat_native_remove_filter (MetaSeatNative *seat, MetaEvdevFilterFunc func, gpointer data); struct xkb_state * meta_seat_native_get_xkb_state (MetaSeatNative *seat); void meta_seat_native_set_keyboard_map (MetaSeatNative *seat, struct xkb_keymap *keymap); struct xkb_keymap * meta_seat_native_get_keyboard_map (MetaSeatNative *seat); void meta_seat_native_set_keyboard_layout_index (MetaSeatNative *seat, xkb_layout_index_t idx); xkb_layout_index_t meta_seat_native_get_keyboard_layout_index (MetaSeatNative *seat); void meta_seat_native_set_keyboard_numlock (MetaSeatNative *seat, gboolean numlock_state); void meta_seat_native_set_keyboard_repeat (MetaSeatNative *seat, gboolean repeat, uint32_t delay, uint32_t interval); #endif /* META_SEAT_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-stage-native.c0000664000175000017500000001543314723361714021256 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "backends/native/meta-stage-native.h" #include "backends/meta-backend-private.h" #include "backends/native/meta-renderer-native.h" #include "meta/meta-backend.h" #include "meta/meta-monitor-manager.h" #include "meta/util.h" static GQuark quark_view_frame_closure = 0; struct _MetaStageNative { ClutterStageCogl parent; CoglClosure *frame_closure; int64_t presented_frame_counter_sync; int64_t presented_frame_counter_complete; }; static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaStageNative, meta_stage_native, CLUTTER_TYPE_STAGE_COGL, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init)) static void frame_cb (CoglOnscreen *onscreen, CoglFrameEvent frame_event, CoglFrameInfo *frame_info, void *user_data) { MetaStageNative *stage_native = user_data; ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_native); int64_t global_frame_counter; int64_t presented_frame_counter; ClutterFrameInfo clutter_frame_info; global_frame_counter = cogl_frame_info_get_global_frame_counter (frame_info); switch (frame_event) { case COGL_FRAME_EVENT_SYNC: presented_frame_counter = stage_native->presented_frame_counter_sync; stage_native->presented_frame_counter_sync = global_frame_counter; break; case COGL_FRAME_EVENT_COMPLETE: presented_frame_counter = stage_native->presented_frame_counter_complete; stage_native->presented_frame_counter_complete = global_frame_counter; break; default: g_assert_not_reached (); } if (global_frame_counter <= presented_frame_counter) return; clutter_frame_info = (ClutterFrameInfo) { .frame_counter = global_frame_counter, .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info), .presentation_time = cogl_frame_info_get_presentation_time (frame_info) }; _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info); } static void ensure_frame_callback (MetaStageNative *stage_native, ClutterStageView *stage_view) { CoglFramebuffer *framebuffer; CoglOnscreen *onscreen; CoglClosure *closure; closure = g_object_get_qdata (G_OBJECT (stage_view), quark_view_frame_closure); if (closure) return; framebuffer = clutter_stage_view_get_onscreen (stage_view); onscreen = COGL_ONSCREEN (framebuffer); closure = cogl_onscreen_add_frame_callback (onscreen, frame_cb, stage_native, NULL); g_object_set_qdata (G_OBJECT (stage_view), quark_view_frame_closure, closure); } static void ensure_frame_callbacks (MetaStageNative *stage_native) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); GList *l; for (l = meta_renderer_get_views (renderer); l; l = l->next) { ClutterStageView *stage_view = l->data; ensure_frame_callback (stage_native, stage_view); } } void meta_stage_native_rebuild_views (MetaStageNative *stage_native) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); ClutterActor *stage = meta_backend_get_stage (backend); meta_renderer_rebuild_views (renderer); clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); ensure_frame_callbacks (stage_native); } static gboolean meta_stage_native_can_clip_redraws (ClutterStageWindow *stage_window) { return TRUE; } static void meta_stage_native_get_geometry (ClutterStageWindow *stage_window, cairo_rectangle_int_t *geometry) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); if (monitor_manager) { int width, height; meta_monitor_manager_get_screen_size (monitor_manager, &width, &height); *geometry = (cairo_rectangle_int_t) { .width = width, .height = height, }; } else { *geometry = (cairo_rectangle_int_t) { .width = 1, .height = 1, }; } } static GList * meta_stage_native_get_views (ClutterStageWindow *stage_window) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); return meta_renderer_get_views (renderer); } static int64_t meta_stage_native_get_frame_counter (ClutterStageWindow *stage_window) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererNative *renderer_native = META_RENDERER_NATIVE (renderer); return meta_renderer_native_get_frame_counter (renderer_native); } static void meta_stage_native_finish_frame (ClutterStageWindow *stage_window) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); meta_renderer_native_finish_frame (META_RENDERER_NATIVE (renderer)); } static void meta_stage_native_init (MetaStageNative *stage_native) { stage_native->presented_frame_counter_sync = -1; stage_native->presented_frame_counter_complete = -1; } static void meta_stage_native_class_init (MetaStageNativeClass *klass) { quark_view_frame_closure = g_quark_from_static_string ("-meta-native-stage-view-frame-closure"); } static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) { iface->can_clip_redraws = meta_stage_native_can_clip_redraws; iface->get_geometry = meta_stage_native_get_geometry; iface->get_views = meta_stage_native_get_views; iface->get_frame_counter = meta_stage_native_get_frame_counter; iface->finish_frame = meta_stage_native_finish_frame; } muffin-6.4.1/src/backends/native/meta-kms-impl-device.c0000664000175000017500000003244214723361714021654 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-impl-device.h" #include #include #include "backends/native/meta-kms-connector-private.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-crtc-private.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-plane-private.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update.h" struct _MetaKmsImplDevice { GObject parent; MetaKmsDevice *device; MetaKmsImpl *impl; int fd; GSource *fd_source; GList *crtcs; GList *connectors; GList *planes; MetaKmsDeviceCaps caps; }; G_DEFINE_TYPE (MetaKmsImplDevice, meta_kms_impl_device, G_TYPE_OBJECT) MetaKmsDevice * meta_kms_impl_device_get_device (MetaKmsImplDevice *impl_device) { return impl_device->device; } GList * meta_kms_impl_device_copy_connectors (MetaKmsImplDevice *impl_device) { return g_list_copy (impl_device->connectors); } GList * meta_kms_impl_device_copy_crtcs (MetaKmsImplDevice *impl_device) { return g_list_copy (impl_device->crtcs); } GList * meta_kms_impl_device_copy_planes (MetaKmsImplDevice *impl_device) { return g_list_copy (impl_device->planes); } const MetaKmsDeviceCaps * meta_kms_impl_device_get_caps (MetaKmsImplDevice *impl_device) { return &impl_device->caps; } static void page_flip_handler (int fd, unsigned int sequence, unsigned int sec, unsigned int usec, void *user_data) { MetaKmsPageFlipData *page_flip_data = user_data; MetaKmsImpl *impl; meta_kms_page_flip_data_set_timings_in_impl (page_flip_data, sequence, sec, usec); impl = meta_kms_page_flip_data_get_kms_impl (page_flip_data); meta_kms_impl_handle_page_flip_callback (impl, page_flip_data); } gboolean meta_kms_impl_device_dispatch (MetaKmsImplDevice *impl_device, GError **error) { drmEventContext drm_event_context; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); drm_event_context = (drmEventContext) { 0 }; drm_event_context.version = 2; drm_event_context.page_flip_handler = page_flip_handler; while (TRUE) { if (drmHandleEvent (impl_device->fd, &drm_event_context) != 0) { struct pollfd pfd; int ret; if (errno != EAGAIN) { g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (errno), strerror (errno)); return FALSE; } pfd.fd = impl_device->fd; pfd.events = POLL_IN | POLL_ERR; do { ret = poll (&pfd, 1, -1); } while (ret == -1 && errno == EINTR); } else { break; } } return TRUE; } static gpointer kms_event_dispatch_in_impl (MetaKmsImpl *impl, gpointer user_data, GError **error) { MetaKmsImplDevice *impl_device = user_data; gboolean ret; ret = meta_kms_impl_device_dispatch (impl_device, error); return GINT_TO_POINTER (ret); } drmModePropertyPtr meta_kms_impl_device_find_property (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props, const char *prop_name, int *out_idx) { unsigned int i; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); for (i = 0; i < props->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (impl_device->fd, props->props[i]); if (!prop) continue; if (strcmp (prop->name, prop_name) == 0) { *out_idx = i; return prop; } drmModeFreeProperty (prop); } return NULL; } static void init_caps (MetaKmsImplDevice *impl_device) { int fd = impl_device->fd; uint64_t cursor_width, cursor_height; if (drmGetCap (fd, DRM_CAP_CURSOR_WIDTH, &cursor_width) == 0 && drmGetCap (fd, DRM_CAP_CURSOR_HEIGHT, &cursor_height) == 0) { impl_device->caps.has_cursor_size = TRUE; impl_device->caps.cursor_width = cursor_width; impl_device->caps.cursor_height = cursor_height; } } static void init_crtcs (MetaKmsImplDevice *impl_device, drmModeRes *drm_resources) { int idx; for (idx = 0; idx < drm_resources->count_crtcs; idx++) { drmModeCrtc *drm_crtc; MetaKmsCrtc *crtc; drm_crtc = drmModeGetCrtc (impl_device->fd, drm_resources->crtcs[idx]); crtc = meta_kms_crtc_new (impl_device, drm_crtc, idx); drmModeFreeCrtc (drm_crtc); impl_device->crtcs = g_list_prepend (impl_device->crtcs, crtc); } impl_device->crtcs = g_list_reverse (impl_device->crtcs); } static MetaKmsConnector * find_existing_connector (MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector) { GList *l; for (l = impl_device->connectors; l; l = l->next) { MetaKmsConnector *connector = l->data; if (meta_kms_connector_is_same_as (connector, drm_connector)) return connector; } return NULL; } static void update_connectors (MetaKmsImplDevice *impl_device, drmModeRes *drm_resources) { GList *connectors = NULL; unsigned int i; for (i = 0; i < drm_resources->count_connectors; i++) { drmModeConnector *drm_connector; MetaKmsConnector *connector; drm_connector = drmModeGetConnector (impl_device->fd, drm_resources->connectors[i]); if (!drm_connector) continue; connector = find_existing_connector (impl_device, drm_connector); if (connector) connector = g_object_ref (connector); else connector = meta_kms_connector_new (impl_device, drm_connector, drm_resources); drmModeFreeConnector (drm_connector); connectors = g_list_prepend (connectors, connector); } g_list_free_full (impl_device->connectors, g_object_unref); impl_device->connectors = g_list_reverse (connectors); } static MetaKmsPlaneType get_plane_type (MetaKmsImplDevice *impl_device, drmModeObjectProperties *props) { drmModePropertyPtr prop; int idx; prop = meta_kms_impl_device_find_property (impl_device, props, "type", &idx); if (!prop) return FALSE; drmModeFreeProperty (prop); switch (props->prop_values[idx]) { case DRM_PLANE_TYPE_PRIMARY: return META_KMS_PLANE_TYPE_PRIMARY; case DRM_PLANE_TYPE_CURSOR: return META_KMS_PLANE_TYPE_CURSOR; case DRM_PLANE_TYPE_OVERLAY: return META_KMS_PLANE_TYPE_OVERLAY; default: g_warning ("Unhandled plane type %" G_GUINT64_FORMAT, props->prop_values[idx]); return -1; } } MetaKmsPlane * meta_kms_impl_device_add_fake_plane (MetaKmsImplDevice *impl_device, MetaKmsPlaneType plane_type, MetaKmsCrtc *crtc) { MetaKmsPlane *plane; plane = meta_kms_plane_new_fake (plane_type, crtc); impl_device->planes = g_list_append (impl_device->planes, plane); return plane; } static void init_planes (MetaKmsImplDevice *impl_device) { int fd = impl_device->fd; drmModePlaneRes *drm_planes; unsigned int i; drm_planes = drmModeGetPlaneResources (fd); if (!drm_planes) return; for (i = 0; i < drm_planes->count_planes; i++) { drmModePlane *drm_plane; drmModeObjectProperties *props; drm_plane = drmModeGetPlane (fd, drm_planes->planes[i]); if (!drm_plane) continue; props = drmModeObjectGetProperties (fd, drm_plane->plane_id, DRM_MODE_OBJECT_PLANE); if (props) { MetaKmsPlaneType plane_type; plane_type = get_plane_type (impl_device, props); if (plane_type != -1) { MetaKmsPlane *plane; plane = meta_kms_plane_new (plane_type, impl_device, drm_plane, props); impl_device->planes = g_list_prepend (impl_device->planes, plane); } } g_clear_pointer (&props, drmModeFreeObjectProperties); drmModeFreePlane (drm_plane); } impl_device->planes = g_list_reverse (impl_device->planes); } void meta_kms_impl_device_update_states (MetaKmsImplDevice *impl_device) { drmModeRes *drm_resources; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); drm_resources = drmModeGetResources (impl_device->fd); if (!drm_resources) { g_list_free_full (impl_device->planes, g_object_unref); g_list_free_full (impl_device->crtcs, g_object_unref); g_list_free_full (impl_device->connectors, g_object_unref); impl_device->planes = NULL; impl_device->crtcs = NULL; impl_device->connectors = NULL; return; } update_connectors (impl_device, drm_resources); g_list_foreach (impl_device->crtcs, (GFunc) meta_kms_crtc_update_state, NULL); g_list_foreach (impl_device->connectors, (GFunc) meta_kms_connector_update_state, drm_resources); drmModeFreeResources (drm_resources); } void meta_kms_impl_device_predict_states (MetaKmsImplDevice *impl_device, MetaKmsUpdate *update) { g_list_foreach (impl_device->crtcs, (GFunc) meta_kms_crtc_predict_state, update); g_list_foreach (impl_device->connectors, (GFunc) meta_kms_connector_predict_state, update); } MetaKmsImplDevice * meta_kms_impl_device_new (MetaKmsDevice *device, MetaKmsImpl *impl, int fd, GError **error) { MetaKms *kms = meta_kms_impl_get_kms (impl); MetaKmsImplDevice *impl_device; int ret; drmModeRes *drm_resources; meta_assert_in_kms_impl (kms); ret = drmSetClientCap (fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "Failed to activate universal planes: %s", g_strerror (-ret)); return NULL; } drm_resources = drmModeGetResources (fd); if (!drm_resources) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "Failed to activate universal planes: %s", g_strerror (errno)); return NULL; } impl_device = g_object_new (META_TYPE_KMS_IMPL_DEVICE, NULL); impl_device->device = device; impl_device->impl = impl; impl_device->fd = fd; init_caps (impl_device); init_crtcs (impl_device, drm_resources); init_planes (impl_device); update_connectors (impl_device, drm_resources); drmModeFreeResources (drm_resources); impl_device->fd_source = meta_kms_register_fd_in_impl (kms, fd, kms_event_dispatch_in_impl, impl_device); return impl_device; } int meta_kms_impl_device_get_fd (MetaKmsImplDevice *impl_device) { meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); return impl_device->fd; } int meta_kms_impl_device_leak_fd (MetaKmsImplDevice *impl_device) { return impl_device->fd; } int meta_kms_impl_device_close (MetaKmsImplDevice *impl_device) { int fd; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl_device->impl)); g_clear_pointer (&impl_device->fd_source, g_source_destroy); fd = impl_device->fd; impl_device->fd = -1; return fd; } static void meta_kms_impl_device_finalize (GObject *object) { MetaKmsImplDevice *impl_device = META_KMS_IMPL_DEVICE (object); g_list_free_full (impl_device->planes, g_object_unref); g_list_free_full (impl_device->crtcs, g_object_unref); g_list_free_full (impl_device->connectors, g_object_unref); G_OBJECT_CLASS (meta_kms_impl_device_parent_class)->finalize (object); } static void meta_kms_impl_device_init (MetaKmsImplDevice *device) { } static void meta_kms_impl_device_class_init (MetaKmsImplDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_impl_device_finalize; } muffin-6.4.1/src/backends/native/meta-kms-plane-private.h0000664000175000017500000000261714723361714022233 0ustar fabiofabio/* * Copyright (C) 2018-2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_PLANE_PRIVATE_H #define META_KMS_PLANE_PRIVATE_H #include #include #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-types.h" MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType type, MetaKmsImplDevice *impl_device, drmModePlane *drm_plane, drmModeObjectProperties *drm_plane_props); MetaKmsPlane * meta_kms_plane_new_fake (MetaKmsPlaneType type, MetaKmsCrtc *crtc); #endif /* META_KMS_PLANE_PRIVATE_H */ muffin-6.4.1/src/backends/native/meta-drm-buffer-dumb.h0000664000175000017500000000234614723361714021651 0ustar fabiofabio/* * Copyright (C) 2018 Canonical Ltd. * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_DRM_BUFFER_DUMB_H #define META_DRM_BUFFER_DUMB_H #include "backends/native/meta-drm-buffer.h" #define META_TYPE_DRM_BUFFER_DUMB (meta_drm_buffer_dumb_get_type ()) G_DECLARE_FINAL_TYPE (MetaDrmBufferDumb, meta_drm_buffer_dumb, META, DRM_BUFFER_DUMB, MetaDrmBuffer) MetaDrmBufferDumb * meta_drm_buffer_dumb_new (uint32_t dumb_fb_id); #endif /* META_DRM_BUFFER_DUMB_H */ muffin-6.4.1/src/backends/native/meta-input-device-tool-native.c0000664000175000017500000001240714723361714023520 0ustar fabiofabio/* * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "config.h" #include "backends/native/meta-input-device-tool-native.h" G_DEFINE_TYPE (MetaInputDeviceToolNative, meta_input_device_tool_native, CLUTTER_TYPE_INPUT_DEVICE_TOOL) static void meta_input_device_tool_native_finalize (GObject *object) { MetaInputDeviceToolNative *tool = META_INPUT_DEVICE_TOOL_NATIVE (object); g_hash_table_unref (tool->button_map); libinput_tablet_tool_unref (tool->tool); G_OBJECT_CLASS (meta_input_device_tool_native_parent_class)->finalize (object); } static void meta_input_device_tool_native_class_init (MetaInputDeviceToolNativeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_input_device_tool_native_finalize; } static void meta_input_device_tool_native_init (MetaInputDeviceToolNative *tool) { tool->button_map = g_hash_table_new (NULL, NULL); } ClutterInputDeviceTool * meta_input_device_tool_native_new (struct libinput_tablet_tool *tool, uint64_t serial, ClutterInputDeviceToolType type) { MetaInputDeviceToolNative *evdev_tool; evdev_tool = g_object_new (META_TYPE_INPUT_DEVICE_TOOL_NATIVE, "type", type, "serial", serial, "id", libinput_tablet_tool_get_tool_id (tool), NULL); evdev_tool->tool = libinput_tablet_tool_ref (tool); return CLUTTER_INPUT_DEVICE_TOOL (evdev_tool); } void meta_input_device_tool_native_set_pressure_curve (ClutterInputDeviceTool *tool, double curve[4]) { MetaInputDeviceToolNative *evdev_tool; g_return_if_fail (META_IS_INPUT_DEVICE_TOOL_NATIVE (tool)); g_return_if_fail (curve[0] >= 0 && curve[0] <= 1 && curve[1] >= 0 && curve[1] <= 1 && curve[2] >= 0 && curve[2] <= 1 && curve[3] >= 0 && curve[3] <= 1); evdev_tool = META_INPUT_DEVICE_TOOL_NATIVE (tool); evdev_tool->pressure_curve[0] = curve[0]; evdev_tool->pressure_curve[1] = curve[1]; evdev_tool->pressure_curve[2] = curve[2]; evdev_tool->pressure_curve[3] = curve[3]; } void meta_input_device_tool_native_set_button_code (ClutterInputDeviceTool *tool, uint32_t button, uint32_t evcode) { MetaInputDeviceToolNative *evdev_tool; g_return_if_fail (META_IS_INPUT_DEVICE_TOOL_NATIVE (tool)); evdev_tool = META_INPUT_DEVICE_TOOL_NATIVE (tool); if (evcode == 0) { g_hash_table_remove (evdev_tool->button_map, GUINT_TO_POINTER (button)); } else { g_hash_table_insert (evdev_tool->button_map, GUINT_TO_POINTER (button), GUINT_TO_POINTER (evcode)); } } static double calculate_bezier_position (double pos, double x1, double y1, double x2, double y2) { double int1_y, int2_y; pos = CLAMP (pos, 0, 1); /* Intersection between 0,0 and x1,y1 */ int1_y = pos * y1; /* Intersection between x2,y2 and 1,1 */ int2_y = (pos * (1 - y2)) + y2; /* Find the new position in the line traced by the previous points */ return (pos * (int2_y - int1_y)) + int1_y; } double meta_input_device_tool_native_translate_pressure (ClutterInputDeviceTool *tool, double pressure) { MetaInputDeviceToolNative *evdev_tool; g_return_val_if_fail (META_IS_INPUT_DEVICE_TOOL_NATIVE (tool), pressure); evdev_tool = META_INPUT_DEVICE_TOOL_NATIVE (tool); return calculate_bezier_position (CLAMP (pressure, 0, 1), evdev_tool->pressure_curve[0], evdev_tool->pressure_curve[1], evdev_tool->pressure_curve[2], evdev_tool->pressure_curve[3]); } uint32_t meta_input_device_tool_native_get_button_code (ClutterInputDeviceTool *tool, uint32_t button) { MetaInputDeviceToolNative *evdev_tool; g_return_val_if_fail (META_IS_INPUT_DEVICE_TOOL_NATIVE (tool), 0); evdev_tool = META_INPUT_DEVICE_TOOL_NATIVE (tool); return GPOINTER_TO_UINT (g_hash_table_lookup (evdev_tool->button_map, GUINT_TO_POINTER (button))); } muffin-6.4.1/src/backends/native/meta-drm-buffer-gbm.c0000664000175000017500000001077114723361714021463 0ustar fabiofabio/* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2016 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * Copyright (C) 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/native/meta-drm-buffer-gbm.h" #include #include #include #include #define INVALID_FB_ID 0U struct _MetaDrmBufferGbm { MetaDrmBuffer parent; MetaGpuKms *gpu_kms; struct gbm_surface *surface; struct gbm_bo *bo; uint32_t fb_id; }; G_DEFINE_TYPE (MetaDrmBufferGbm, meta_drm_buffer_gbm, META_TYPE_DRM_BUFFER) struct gbm_bo * meta_drm_buffer_gbm_get_bo (MetaDrmBufferGbm *buffer_gbm) { return buffer_gbm->bo; } static gboolean acquire_swapped_buffer (MetaDrmBufferGbm *buffer_gbm, gboolean use_modifiers, GError **error) { MetaGpuKmsFBArgs fb_args = { 0, }; struct gbm_bo *bo; bo = gbm_surface_lock_front_buffer (buffer_gbm->surface); if (!bo) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "gbm_surface_lock_front_buffer failed"); return FALSE; } if (gbm_bo_get_handle_for_plane (bo, 0).s32 == -1) { /* Failed to fetch handle to plane, falling back to old method */ fb_args.strides[0] = gbm_bo_get_stride (bo); fb_args.handles[0] = gbm_bo_get_handle (bo).u32; fb_args.offsets[0] = 0; fb_args.modifiers[0] = DRM_FORMAT_MOD_INVALID; } else { int i; for (i = 0; i < gbm_bo_get_plane_count (bo); i++) { fb_args.strides[i] = gbm_bo_get_stride_for_plane (bo, i); fb_args.handles[i] = gbm_bo_get_handle_for_plane (bo, i).u32; fb_args.offsets[i] = gbm_bo_get_offset (bo, i); fb_args.modifiers[i] = gbm_bo_get_modifier (bo); } } fb_args.width = gbm_bo_get_width (bo); fb_args.height = gbm_bo_get_height (bo); fb_args.format = gbm_bo_get_format (bo); if (!meta_gpu_kms_add_fb (buffer_gbm->gpu_kms, use_modifiers, &fb_args, &buffer_gbm->fb_id, error)) { gbm_surface_release_buffer (buffer_gbm->surface, bo); return FALSE; } buffer_gbm->bo = bo; return TRUE; } MetaDrmBufferGbm * meta_drm_buffer_gbm_new (MetaGpuKms *gpu_kms, struct gbm_surface *gbm_surface, gboolean use_modifiers, GError **error) { MetaDrmBufferGbm *buffer_gbm; buffer_gbm = g_object_new (META_TYPE_DRM_BUFFER_GBM, NULL); buffer_gbm->gpu_kms = gpu_kms; buffer_gbm->surface = gbm_surface; if (!acquire_swapped_buffer (buffer_gbm, use_modifiers, error)) { g_object_unref (buffer_gbm); return NULL; } return buffer_gbm; } static uint32_t meta_drm_buffer_gbm_get_fb_id (MetaDrmBuffer *buffer) { return META_DRM_BUFFER_GBM (buffer)->fb_id; } static void meta_drm_buffer_gbm_finalize (GObject *object) { MetaDrmBufferGbm *buffer_gbm = META_DRM_BUFFER_GBM (object); if (buffer_gbm->fb_id != INVALID_FB_ID) { int kms_fd; kms_fd = meta_gpu_kms_get_fd (buffer_gbm->gpu_kms); drmModeRmFB (kms_fd, buffer_gbm->fb_id); } if (buffer_gbm->bo) gbm_surface_release_buffer (buffer_gbm->surface, buffer_gbm->bo); G_OBJECT_CLASS (meta_drm_buffer_gbm_parent_class)->finalize (object); } static void meta_drm_buffer_gbm_init (MetaDrmBufferGbm *buffer_gbm) { } static void meta_drm_buffer_gbm_class_init (MetaDrmBufferGbmClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaDrmBufferClass *buffer_class = META_DRM_BUFFER_CLASS (klass); object_class->finalize = meta_drm_buffer_gbm_finalize; buffer_class->get_fb_id = meta_drm_buffer_gbm_get_fb_id; } muffin-6.4.1/src/backends/native/meta-input-device-tool-native.h0000664000175000017500000000673614723361714023535 0ustar fabiofabio/* * Copyright © 2009, 2010, 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_DEVICE_NATIVE_TOOL_H #define META_INPUT_DEVICE_NATIVE_TOOL_H #include #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_INPUT_DEVICE_TOOL_NATIVE (meta_input_device_tool_native_get_type ()) #define META_INPUT_DEVICE_TOOL_NATIVE(o) \ (G_TYPE_CHECK_INSTANCE_CAST ((o), \ META_TYPE_INPUT_DEVICE_TOOL_NATIVE, MetaInputDeviceToolNative)) #define META_IS_INPUT_DEVICE_TOOL_NATIVE(o) \ (G_TYPE_CHECK_INSTANCE_TYPE ((o), \ META_TYPE_INPUT_DEVICE_TOOL_NATIVE)) #define META_INPUT_DEVICE_TOOL_NATIVE_CLASS(c) \ (G_TYPE_CHECK_CLASS_CAST ((c), \ META_TYPE_INPUT_DEVICE_TOOL_EVDEV, MetaInputDeviceToolNativeClass)) #define META_IS_INPUT_DEVICE_TOOL_NATIVE_CLASS(c) \ (G_TYPE_CHECK_CLASS_TYPE ((c), \ META_TYPE_INPUT_DEVICE_TOOL_NATIVE)) #define META_INPUT_DEVICE_TOOL_NATIVE_GET_CLASS(o) \ (G_TYPE_INSTANCE_GET_CLASS ((o), \ META_TYPE_INPUT_DEVICE_TOOL_NATIVE, MetaInputDeviceToolNativeClass)) typedef struct _MetaInputDeviceToolNative MetaInputDeviceToolNative; typedef struct _MetaInputDeviceToolNativeClass MetaInputDeviceToolNativeClass; struct _MetaInputDeviceToolNative { ClutterInputDeviceTool parent_instance; struct libinput_tablet_tool *tool; GHashTable *button_map; double pressure_curve[4]; }; struct _MetaInputDeviceToolNativeClass { ClutterInputDeviceToolClass parent_class; }; GType meta_input_device_tool_native_get_type (void) G_GNUC_CONST; ClutterInputDeviceTool * meta_input_device_tool_native_new (struct libinput_tablet_tool *tool, uint64_t serial, ClutterInputDeviceToolType type); gdouble meta_input_device_tool_native_translate_pressure (ClutterInputDeviceTool *tool, double pressure); uint32_t meta_input_device_tool_native_get_button_code (ClutterInputDeviceTool *tool, uint32_t button); void meta_input_device_tool_native_set_pressure_curve (ClutterInputDeviceTool *tool, double curve[4]); void meta_input_device_tool_native_set_button_code (ClutterInputDeviceTool *tool, uint32_t button, uint32_t evcode); G_END_DECLS #endif /* META_INPUT_DEVICE_NATIVE_TOOL_H */ muffin-6.4.1/src/backends/native/meta-backend-native.c0000664000175000017500000006311714723361714021544 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ /** * SECTION:meta-backend-native * @title: MetaBackendNative * @short_description: A native (KMS/evdev) MetaBackend * * MetaBackendNative is an implementation of #MetaBackend that uses "native" * technologies like DRM/KMS and libinput/evdev to perform the necessary * functions. */ #include "config.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-backend-native-private.h" #include #include #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-monitor-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-pointer-constraint.h" #include "backends/meta-settings-private.h" #include "backends/meta-stage-private.h" #include "backends/native/meta-barrier-native.h" #include "backends/native/meta-clutter-backend-native.h" #include "backends/native/meta-cursor-renderer-native.h" #include "backends/native/meta-event-native.h" #include "backends/native/meta-input-settings-native.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-monitor-manager-kms.h" #include "backends/native/meta-renderer-native.h" #include "backends/native/meta-seat-native.h" #include "backends/native/meta-stage-native.h" #include "cogl/cogl.h" #include "core/meta-border.h" #include "meta/main.h" struct _MetaBackendNative { MetaBackend parent; MetaLauncher *launcher; MetaUdev *udev; MetaKms *kms; MetaBarrierManagerNative *barrier_manager; gulong udev_device_added_handler_id; }; static GInitableIface *initable_parent_iface; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaBackendNative, meta_backend_native, META_TYPE_BACKEND, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) static void disconnect_udev_device_added_handler (MetaBackendNative *native); static void meta_backend_native_finalize (GObject *object) { MetaBackendNative *native = META_BACKEND_NATIVE (object); if (native->udev_device_added_handler_id) disconnect_udev_device_added_handler (native); g_clear_object (&native->udev); g_clear_object (&native->kms); meta_launcher_free (native->launcher); G_OBJECT_CLASS (meta_backend_native_parent_class)->finalize (object); } static void constrain_to_barriers (ClutterInputDevice *device, guint32 time, float *new_x, float *new_y) { MetaBackendNative *native = META_BACKEND_NATIVE (meta_get_backend ()); meta_barrier_manager_native_process (native->barrier_manager, device, time, new_x, new_y); } static void constrain_to_client_constraint (ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y) { MetaBackend *backend = meta_get_backend (); MetaPointerConstraint *constraint = meta_backend_get_client_pointer_constraint (backend); if (!constraint) return; meta_pointer_constraint_constrain (constraint, device, time, prev_x, prev_y, x, y); } /* * The pointer constrain code is mostly a rip-off of the XRandR code from Xorg. * (from xserver/randr/rrcrtc.c, RRConstrainCursorHarder) * * Copyright © 2006 Keith Packard * Copyright 2010 Red Hat, Inc * */ static void constrain_all_screen_monitors (ClutterInputDevice *device, MetaMonitorManager *monitor_manager, float *x, float *y) { graphene_point_t current; float cx, cy; GList *logical_monitors, *l; clutter_input_device_get_coords (device, NULL, ¤t); cx = current.x; cy = current.y; /* if we're trying to escape, clamp to the CRTC we're coming from */ logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; int left, right, top, bottom; left = logical_monitor->rect.x; right = left + logical_monitor->rect.width; top = logical_monitor->rect.y; bottom = top + logical_monitor->rect.height; if ((cx >= left) && (cx < right) && (cy >= top) && (cy < bottom)) { if (*x < left) *x = left; if (*x >= right) *x = right - 1; if (*y < top) *y = top; if (*y >= bottom) *y = bottom - 1; return; } } } static void pointer_constrain_callback (ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *new_x, float *new_y, gpointer user_data) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); /* Constrain to barriers */ constrain_to_barriers (device, time, new_x, new_y); /* Constrain to pointer lock */ constrain_to_client_constraint (device, time, prev_x, prev_y, new_x, new_y); /* if we're moving inside a monitor, we're fine */ if (meta_monitor_manager_get_logical_monitor_at (monitor_manager, *new_x, *new_y)) return; /* if we're trying to escape, clamp to the CRTC we're coming from */ constrain_all_screen_monitors (device, monitor_manager, new_x, new_y); } static void relative_motion_across_outputs (MetaMonitorManager *monitor_manager, MetaLogicalMonitor *current, float cur_x, float cur_y, float *dx_inout, float *dy_inout) { MetaLogicalMonitor *cur = current; float x = cur_x, y = cur_y; float target_x = cur_x, target_y = cur_y; float dx = *dx_inout, dy = *dy_inout; MetaDisplayDirection direction = -1; while (cur) { MetaLine2 left, right, top, bottom, motion; MetaVector2 intersection; motion = (MetaLine2) { .a = { x, y }, .b = { x + (dx * cur->scale), y + (dy * cur->scale) } }; left = (MetaLine2) { { cur->rect.x, cur->rect.y }, { cur->rect.x, cur->rect.y + cur->rect.height } }; right = (MetaLine2) { { cur->rect.x + cur->rect.width, cur->rect.y }, { cur->rect.x + cur->rect.width, cur->rect.y + cur->rect.height } }; top = (MetaLine2) { { cur->rect.x, cur->rect.y }, { cur->rect.x + cur->rect.width, cur->rect.y } }; bottom = (MetaLine2) { { cur->rect.x, cur->rect.y + cur->rect.height }, { cur->rect.x + cur->rect.width, cur->rect.y + cur->rect.height } }; target_x = motion.b.x; target_y = motion.b.y; if (direction != META_DISPLAY_RIGHT && meta_line2_intersects_with (&motion, &left, &intersection)) direction = META_DISPLAY_LEFT; else if (direction != META_DISPLAY_LEFT && meta_line2_intersects_with (&motion, &right, &intersection)) direction = META_DISPLAY_RIGHT; else if (direction != META_DISPLAY_DOWN && meta_line2_intersects_with (&motion, &top, &intersection)) direction = META_DISPLAY_UP; else if (direction != META_DISPLAY_UP && meta_line2_intersects_with (&motion, &bottom, &intersection)) direction = META_DISPLAY_DOWN; else /* We reached the dest logical monitor */ break; x = intersection.x; y = intersection.y; dx -= intersection.x - motion.a.x; dy -= intersection.y - motion.a.y; cur = meta_monitor_manager_get_logical_monitor_neighbor (monitor_manager, cur, direction); } *dx_inout = target_x - cur_x; *dy_inout = target_y - cur_y; } static void relative_motion_filter (ClutterInputDevice *device, float x, float y, float *dx, float *dy, gpointer user_data) { MetaMonitorManager *monitor_manager = user_data; MetaLogicalMonitor *logical_monitor, *dest_logical_monitor; float new_dx, new_dy; if (meta_is_stage_views_scaled ()) return; logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); if (!logical_monitor) return; new_dx = (*dx) * logical_monitor->scale; new_dy = (*dy) * logical_monitor->scale; dest_logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x + new_dx, y + new_dy); if (dest_logical_monitor && dest_logical_monitor != logical_monitor) { /* If we are crossing monitors, attempt to bisect the distance on each * axis and apply the relative scale for each of them. */ new_dx = *dx; new_dy = *dy; relative_motion_across_outputs (monitor_manager, logical_monitor, x, y, &new_dx, &new_dy); } *dx = new_dx; *dy = new_dy; } static ClutterBackend * meta_backend_native_create_clutter_backend (MetaBackend *backend) { return g_object_new (META_TYPE_CLUTTER_BACKEND_NATIVE, NULL); } static void meta_backend_native_post_init (MetaBackend *backend) { ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend); MetaSettings *settings = meta_backend_get_settings (backend); meta_seat_native_set_pointer_constrain_callback (META_SEAT_NATIVE (seat), pointer_constrain_callback, NULL, NULL); meta_seat_native_set_relative_motion_filter (META_SEAT_NATIVE (seat), relative_motion_filter, meta_backend_get_monitor_manager (backend)); META_BACKEND_CLASS (meta_backend_native_parent_class)->post_init (backend); if (meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_RT_SCHEDULER)) { int retval; struct sched_param sp = { .sched_priority = sched_get_priority_min (SCHED_RR) }; retval = sched_setscheduler (0, SCHED_RR | SCHED_RESET_ON_FORK, &sp); if (retval != 0) g_warning ("Failed to set RT scheduler: %m"); } #ifdef HAVE_WAYLAND meta_backend_init_wayland (backend); #endif } static MetaMonitorManager * meta_backend_native_create_monitor_manager (MetaBackend *backend, GError **error) { return g_initable_new (META_TYPE_MONITOR_MANAGER_KMS, NULL, error, "backend", backend, NULL); } static MetaCursorRenderer * meta_backend_native_create_cursor_renderer (MetaBackend *backend) { return META_CURSOR_RENDERER (meta_cursor_renderer_native_new (backend)); } static MetaRenderer * meta_backend_native_create_renderer (MetaBackend *backend, GError **error) { MetaBackendNative *native = META_BACKEND_NATIVE (backend); MetaRendererNative *renderer_native; renderer_native = meta_renderer_native_new (native, error); if (!renderer_native) return NULL; return META_RENDERER (renderer_native); } static MetaInputSettings * meta_backend_native_create_input_settings (MetaBackend *backend) { return g_object_new (META_TYPE_INPUT_SETTINGS_NATIVE, NULL); } static MetaLogicalMonitor * meta_backend_native_get_current_logical_monitor (MetaBackend *backend) { MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); int x, y; meta_cursor_tracker_get_pointer (cursor_tracker, &x, &y, NULL); return meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); } static void meta_backend_native_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options) { struct xkb_rule_names names; struct xkb_keymap *keymap; struct xkb_context *context; ClutterSeat *seat; names.rules = DEFAULT_XKB_RULES_FILE; names.model = DEFAULT_XKB_MODEL; names.layout = layouts; names.variant = variants; names.options = options; context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); keymap = xkb_keymap_new_from_names (context, &names, XKB_KEYMAP_COMPILE_NO_FLAGS); xkb_context_unref (context); seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); meta_seat_native_set_keyboard_map (META_SEAT_NATIVE (seat), keymap); meta_backend_notify_keymap_changed (backend); xkb_keymap_unref (keymap); } static struct xkb_keymap * meta_backend_native_get_keymap (MetaBackend *backend) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); return meta_seat_native_get_keyboard_map (META_SEAT_NATIVE (seat)); } static xkb_layout_index_t meta_backend_native_get_keymap_layout_group (MetaBackend *backend) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); return meta_seat_native_get_keyboard_layout_index (META_SEAT_NATIVE (seat)); } static void meta_backend_native_lock_layout_group (MetaBackend *backend, guint idx) { xkb_layout_index_t old_idx; ClutterSeat *seat; old_idx = meta_backend_native_get_keymap_layout_group (backend); if (old_idx == idx) return; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); meta_seat_native_set_keyboard_layout_index (META_SEAT_NATIVE (seat), idx); meta_backend_notify_keymap_layout_group_changed (backend, idx); } static void meta_backend_native_set_numlock (MetaBackend *backend, gboolean numlock_state) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); meta_seat_native_set_keyboard_numlock (META_SEAT_NATIVE (seat), numlock_state); } static void meta_backend_native_update_screen_size (MetaBackend *backend, int width, int height) { ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); MetaStageNative *stage_native; ClutterActor *stage = meta_backend_get_stage (backend); stage_native = meta_clutter_backend_native_get_stage_native (clutter_backend); meta_stage_native_rebuild_views (stage_native); clutter_actor_set_size (stage, width, height); } static MetaGpuKms * create_gpu_from_udev_device (MetaBackendNative *native, GUdevDevice *device, GError **error) { MetaKmsDeviceFlag flags = META_KMS_DEVICE_FLAG_NONE; const char *device_path; MetaKmsDevice *kms_device; if (meta_is_udev_device_platform_device (device)) flags |= META_KMS_DEVICE_FLAG_PLATFORM_DEVICE; if (meta_is_udev_device_boot_vga (device)) flags |= META_KMS_DEVICE_FLAG_BOOT_VGA; device_path = g_udev_device_get_device_file (device); kms_device = meta_kms_create_device (native->kms, device_path, flags, error); if (!kms_device) return NULL; return meta_gpu_kms_new (native, kms_device, error); } static void on_udev_device_added (MetaUdev *udev, GUdevDevice *device, MetaBackendNative *native) { MetaBackend *backend = META_BACKEND (native); g_autoptr (GError) error = NULL; const char *device_path; MetaGpuKms *new_gpu_kms; GList *gpus, *l; if (!meta_udev_is_drm_device (udev, device)) return; device_path = g_udev_device_get_device_file (device); gpus = meta_backend_get_gpus (backend);; for (l = gpus; l; l = l->next) { MetaGpuKms *gpu_kms = l->data; if (!g_strcmp0 (device_path, meta_gpu_kms_get_file_path (gpu_kms))) { g_warning ("Failed to hotplug secondary gpu '%s': %s", device_path, "device already present"); return; } } new_gpu_kms = create_gpu_from_udev_device (native, device, &error); if (!new_gpu_kms) { g_warning ("Failed to hotplug secondary gpu '%s': %s", device_path, error->message); return; } meta_backend_add_gpu (backend, META_GPU (new_gpu_kms)); } static void connect_udev_device_added_handler (MetaBackendNative *native) { native->udev_device_added_handler_id = g_signal_connect (native->udev, "device-added", G_CALLBACK (on_udev_device_added), native); } static void disconnect_udev_device_added_handler (MetaBackendNative *native) { g_clear_signal_handler (&native->udev_device_added_handler_id, native->udev); } static gboolean init_gpus (MetaBackendNative *native, GError **error) { MetaBackend *backend = META_BACKEND (native); MetaUdev *udev = meta_backend_native_get_udev (native); GList *devices; GList *l; devices = meta_udev_list_drm_devices (udev, error); if (!devices) return FALSE; for (l = devices; l; l = l->next) { GUdevDevice *device = l->data; MetaGpuKms *gpu_kms; GError *local_error = NULL; gpu_kms = create_gpu_from_udev_device (native, device, &local_error); if (!gpu_kms) { g_warning ("Failed to open gpu '%s': %s", g_udev_device_get_device_file (device), local_error->message); g_clear_error (&local_error); continue; } meta_backend_add_gpu (backend, META_GPU (gpu_kms)); } g_list_free_full (devices, g_object_unref); if (g_list_length (meta_backend_get_gpus (backend)) == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No GPUs found"); return FALSE; } connect_udev_device_added_handler (native); return TRUE; } static gboolean meta_backend_native_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaBackendNative *native = META_BACKEND_NATIVE (initable); if (!meta_is_stage_views_enabled ()) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "The native backend requires stage views"); return FALSE; } native->launcher = meta_launcher_new (error); if (!native->launcher) return FALSE; #ifdef HAVE_WAYLAND meta_backend_init_wayland_display (META_BACKEND (native)); #endif native->udev = meta_udev_new (native); native->barrier_manager = meta_barrier_manager_native_new (); native->kms = meta_kms_new (META_BACKEND (native), error); if (!native->kms) return FALSE; if (!init_gpus (native, error)) return FALSE; return initable_parent_iface->init (initable, cancellable, error); } static void initable_iface_init (GInitableIface *initable_iface) { initable_parent_iface = g_type_interface_peek_parent (initable_iface); initable_iface->init = meta_backend_native_initable_init; } static void meta_backend_native_class_init (MetaBackendNativeClass *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_backend_native_finalize; backend_class->create_clutter_backend = meta_backend_native_create_clutter_backend; backend_class->post_init = meta_backend_native_post_init; backend_class->create_monitor_manager = meta_backend_native_create_monitor_manager; backend_class->create_cursor_renderer = meta_backend_native_create_cursor_renderer; backend_class->create_renderer = meta_backend_native_create_renderer; backend_class->create_input_settings = meta_backend_native_create_input_settings; backend_class->get_current_logical_monitor = meta_backend_native_get_current_logical_monitor; backend_class->set_keymap = meta_backend_native_set_keymap; backend_class->get_keymap = meta_backend_native_get_keymap; backend_class->get_keymap_layout_group = meta_backend_native_get_keymap_layout_group; backend_class->lock_layout_group = meta_backend_native_lock_layout_group; backend_class->update_screen_size = meta_backend_native_update_screen_size; backend_class->set_numlock = meta_backend_native_set_numlock; } static void meta_backend_native_init (MetaBackendNative *native) { } MetaLauncher * meta_backend_native_get_launcher (MetaBackendNative *native) { return native->launcher; } MetaUdev * meta_backend_native_get_udev (MetaBackendNative *native) { return native->udev; } MetaKms * meta_backend_native_get_kms (MetaBackendNative *native) { return native->kms; } gboolean meta_activate_vt (int vt, GError **error) { MetaBackend *backend = meta_get_backend (); MetaBackendNative *native = META_BACKEND_NATIVE (backend); MetaLauncher *launcher = meta_backend_native_get_launcher (native); return meta_launcher_activate_vt (launcher, vt, error); } MetaBarrierManagerNative * meta_backend_native_get_barrier_manager (MetaBackendNative *native) { return native->barrier_manager; } /** * meta_activate_session: * * Tells mutter to activate the session. When mutter is a * display server, this tells logind to switch over to * the new session. */ gboolean meta_activate_session (void) { GError *error = NULL; MetaBackend *backend = meta_get_backend (); /* Do nothing. */ if (!META_IS_BACKEND_NATIVE (backend)) return TRUE; MetaBackendNative *native = META_BACKEND_NATIVE (backend); if (!meta_launcher_activate_session (native->launcher, &error)) { g_warning ("Could not activate session: %s\n", error->message); g_error_free (error); return FALSE; } return TRUE; } void meta_backend_native_pause (MetaBackendNative *native) { MetaBackend *backend = META_BACKEND (native); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); ClutterBackend *clutter_backend = clutter_get_default_backend (); MetaSeatNative *seat = META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend)); COGL_TRACE_BEGIN_SCOPED (MetaBackendNativePause, "Backend (pause)"); meta_seat_native_release_devices (seat); clutter_stage_freeze_updates (stage); disconnect_udev_device_added_handler (native); meta_monitor_manager_kms_pause (monitor_manager_kms); } void meta_backend_native_resume (MetaBackendNative *native) { MetaBackend *backend = META_BACKEND (native); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerKms *monitor_manager_kms = META_MONITOR_MANAGER_KMS (monitor_manager); MetaInputSettings *input_settings; MetaIdleMonitor *idle_monitor; ClutterBackend *clutter_backend = clutter_get_default_backend (); MetaSeatNative *seat = META_SEAT_NATIVE (clutter_backend_get_default_seat (clutter_backend)); COGL_TRACE_BEGIN_SCOPED (MetaBackendNativeResume, "Backend (resume)"); meta_monitor_manager_kms_resume (monitor_manager_kms); connect_udev_device_added_handler (native); meta_seat_native_reclaim_devices (seat); clutter_stage_thaw_updates (stage); clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); idle_monitor = meta_idle_monitor_get_core (); meta_idle_monitor_reset_idletime (idle_monitor); input_settings = meta_backend_get_input_settings (backend); meta_input_settings_maybe_restore_numlock_state (input_settings); clutter_seat_ensure_a11y_state (CLUTTER_SEAT (seat)); } muffin-6.4.1/src/backends/native/meta-backend-native-types.h0000664000175000017500000000164114723361714022705 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_BACKEND_NATIVE_TYPES_H #define META_BACKEND_NATIVE_TYPES_H typedef struct _MetaBackendNative MetaBackendNative; #endif /* META_BACKEND_NATIVE_TYPES_H */ muffin-6.4.1/src/backends/native/meta-renderer-native-gles3.h0000664000175000017500000000311514723361714022773 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (c) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_RENDERER_NATIVE_GLES3_H #define META_RENDERER_NATIVE_GLES3_H #include #include "backends/meta-egl.h" #include "backends/meta-gles3.h" gboolean meta_renderer_native_gles3_blit_shared_bo (MetaEgl *egl, MetaGles3 *gles3, EGLDisplay egl_display, EGLContext egl_context, EGLSurface egl_surface, struct gbm_bo *shared_bo, GError **error); #endif /* META_RENDERER_NATIVE_GLES3_H */ muffin-6.4.1/src/backends/native/meta-kms-plane.c0000664000175000017500000003035014723361714020551 0ustar fabiofabio/* * Copyright (C) 2013-2019 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-plane-private.h" #include #include #include "backends/meta-monitor-transform.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-update-private.h" struct _MetaKmsPlane { GObject parent; MetaKmsPlaneType type; gboolean is_fake; uint32_t id; uint32_t possible_crtcs; uint32_t rotation_prop_id; uint32_t rotation_map[META_MONITOR_N_TRANSFORMS]; uint32_t all_hw_transforms; /* * primary plane's supported formats and maybe modifiers * key: GUINT_TO_POINTER (format) * value: owned GArray* (uint64_t modifier), or NULL */ GHashTable *formats_modifiers; MetaKmsDevice *device; }; G_DEFINE_TYPE (MetaKmsPlane, meta_kms_plane, G_TYPE_OBJECT) MetaKmsDevice * meta_kms_plane_get_device (MetaKmsPlane *plane) { return plane->device; } uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane) { g_return_val_if_fail (!plane->is_fake, 0); return plane->id; } MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane) { return plane->type; } void meta_kms_plane_update_set_rotation (MetaKmsPlane *plane, MetaKmsPlaneAssignment *plane_assignment, MetaMonitorTransform transform) { g_return_if_fail (meta_kms_plane_is_transform_handled (plane, transform)); meta_kms_plane_assignment_set_plane_property (plane_assignment, plane->rotation_prop_id, plane->rotation_map[transform]); } gboolean meta_kms_plane_is_transform_handled (MetaKmsPlane *plane, MetaMonitorTransform transform) { switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: case META_MONITOR_TRANSFORM_180: case META_MONITOR_TRANSFORM_FLIPPED: case META_MONITOR_TRANSFORM_FLIPPED_180: break; case META_MONITOR_TRANSFORM_90: case META_MONITOR_TRANSFORM_270: case META_MONITOR_TRANSFORM_FLIPPED_90: case META_MONITOR_TRANSFORM_FLIPPED_270: /* * Blacklist these transforms as testing shows that they don't work * anyway, e.g. due to the wrong buffer modifiers. They might as well be * less optimal due to the complexity dealing with rotation at scan-out, * potentially resulting in higher power consumption. */ return FALSE; } return plane->all_hw_transforms & (1 << transform); } GArray * meta_kms_plane_get_modifiers_for_format (MetaKmsPlane *plane, uint32_t format) { return g_hash_table_lookup (plane->formats_modifiers, GUINT_TO_POINTER (format)); } GArray * meta_kms_plane_copy_drm_format_list (MetaKmsPlane *plane) { GArray *formats; GHashTableIter it; gpointer key; unsigned int n_formats_modifiers; n_formats_modifiers = g_hash_table_size (plane->formats_modifiers); formats = g_array_sized_new (FALSE, FALSE, sizeof (uint32_t), n_formats_modifiers); g_hash_table_iter_init (&it, plane->formats_modifiers); while (g_hash_table_iter_next (&it, &key, NULL)) { uint32_t drm_format = GPOINTER_TO_UINT (key); g_array_append_val (formats, drm_format); } return formats; } gboolean meta_kms_plane_is_format_supported (MetaKmsPlane *plane, uint32_t drm_format) { return g_hash_table_lookup_extended (plane->formats_modifiers, GUINT_TO_POINTER (drm_format), NULL, NULL); } gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane, MetaKmsCrtc *crtc) { return !!(plane->possible_crtcs & (1 << meta_kms_crtc_get_idx (crtc))); } static void parse_rotations (MetaKmsPlane *plane, MetaKmsImplDevice *impl_device, drmModePropertyPtr prop) { int i; for (i = 0; i < prop->count_enums; i++) { MetaMonitorTransform transform = -1; if (strcmp (prop->enums[i].name, "rotate-0") == 0) transform = META_MONITOR_TRANSFORM_NORMAL; else if (strcmp (prop->enums[i].name, "rotate-90") == 0) transform = META_MONITOR_TRANSFORM_90; else if (strcmp (prop->enums[i].name, "rotate-180") == 0) transform = META_MONITOR_TRANSFORM_180; else if (strcmp (prop->enums[i].name, "rotate-270") == 0) transform = META_MONITOR_TRANSFORM_270; if (transform != -1) { plane->all_hw_transforms |= 1 << transform; plane->rotation_map[transform] = 1 << prop->enums[i].value; } } } static void init_rotations (MetaKmsPlane *plane, MetaKmsImplDevice *impl_device, drmModeObjectProperties *drm_plane_props) { drmModePropertyPtr prop; int idx; prop = meta_kms_impl_device_find_property (impl_device, drm_plane_props, "rotation", &idx); if (prop) { plane->rotation_prop_id = drm_plane_props->props[idx]; parse_rotations (plane, impl_device, prop); drmModeFreeProperty (prop); } } static inline uint32_t * drm_formats_ptr (struct drm_format_modifier_blob *blob) { return (uint32_t *) (((char *) blob) + blob->formats_offset); } static inline struct drm_format_modifier * drm_modifiers_ptr (struct drm_format_modifier_blob *blob) { return (struct drm_format_modifier *) (((char *) blob) + blob->modifiers_offset); } static void free_modifier_array (GArray *array) { if (!array) return; g_array_free (array, TRUE); } static void parse_formats (MetaKmsPlane *plane, MetaKmsImplDevice *impl_device, uint32_t blob_id) { int fd; drmModePropertyBlobPtr blob; struct drm_format_modifier_blob *blob_fmt; uint32_t *formats; struct drm_format_modifier *drm_modifiers; unsigned int fmt_i, mod_i; g_return_if_fail (g_hash_table_size (plane->formats_modifiers) == 0); if (blob_id == 0) return; fd = meta_kms_impl_device_get_fd (impl_device); blob = drmModeGetPropertyBlob (fd, blob_id); if (!blob) return; if (blob->length < sizeof (struct drm_format_modifier_blob)) { drmModeFreePropertyBlob (blob); return; } blob_fmt = blob->data; formats = drm_formats_ptr (blob_fmt); drm_modifiers = drm_modifiers_ptr (blob_fmt); for (fmt_i = 0; fmt_i < blob_fmt->count_formats; fmt_i++) { GArray *modifiers = g_array_new (FALSE, FALSE, sizeof (uint64_t)); for (mod_i = 0; mod_i < blob_fmt->count_modifiers; mod_i++) { struct drm_format_modifier *drm_modifier = &drm_modifiers[mod_i]; /* * The modifier advertisement blob is partitioned into groups of * 64 formats. */ if (fmt_i < drm_modifier->offset || fmt_i > drm_modifier->offset + 63) continue; if (!(drm_modifier->formats & (1 << (fmt_i - drm_modifier->offset)))) continue; g_array_append_val (modifiers, drm_modifier->modifier); } if (modifiers->len == 0) { free_modifier_array (modifiers); modifiers = NULL; } g_hash_table_insert (plane->formats_modifiers, GUINT_TO_POINTER (formats[fmt_i]), modifiers); } drmModeFreePropertyBlob (blob); } static void set_formats_from_array (MetaKmsPlane *plane, const uint32_t *formats, size_t n_formats) { size_t i; for (i = 0; i < n_formats; i++) { g_hash_table_insert (plane->formats_modifiers, GUINT_TO_POINTER (formats[i]), NULL); } } /* * In case the DRM driver does not expose a format list for the * primary plane (does not support universal planes nor * IN_FORMATS property), hardcode something that is probably supported. */ static const uint32_t drm_default_formats[] = { /* The format everything should always support by convention */ DRM_FORMAT_XRGB8888, #if G_BYTE_ORDER == G_LITTLE_ENDIAN /* OpenGL GL_RGBA, GL_UNSIGNED_BYTE format, hopefully supported */ DRM_FORMAT_XBGR8888 #endif }; static void init_formats (MetaKmsPlane *plane, MetaKmsImplDevice *impl_device, drmModePlane *drm_plane, drmModeObjectProperties *drm_plane_props) { drmModePropertyPtr prop; int idx; prop = meta_kms_impl_device_find_property (impl_device, drm_plane_props, "IN_FORMATS", &idx); if (prop) { uint32_t blob_id; blob_id = drm_plane_props->prop_values[idx]; parse_formats (plane, impl_device, blob_id); drmModeFreeProperty (prop); } if (g_hash_table_size (plane->formats_modifiers) == 0) { set_formats_from_array (plane, drm_plane->formats, drm_plane->count_formats); } /* final formats fallback to something hardcoded */ if (g_hash_table_size (plane->formats_modifiers) == 0) { set_formats_from_array (plane, drm_default_formats, G_N_ELEMENTS (drm_default_formats)); } } MetaKmsPlane * meta_kms_plane_new (MetaKmsPlaneType type, MetaKmsImplDevice *impl_device, drmModePlane *drm_plane, drmModeObjectProperties *drm_plane_props) { MetaKmsPlane *plane; plane = g_object_new (META_TYPE_KMS_PLANE, NULL); plane->type = type; plane->id = drm_plane->plane_id; plane->possible_crtcs = drm_plane->possible_crtcs; plane->device = meta_kms_impl_device_get_device (impl_device); init_rotations (plane, impl_device, drm_plane_props); init_formats (plane, impl_device, drm_plane, drm_plane_props); return plane; } MetaKmsPlane * meta_kms_plane_new_fake (MetaKmsPlaneType type, MetaKmsCrtc *crtc) { MetaKmsPlane *plane; static const uint32_t fake_plane_drm_formats[] = { DRM_FORMAT_XRGB8888, DRM_FORMAT_ARGB8888, #if G_BYTE_ORDER == G_LITTLE_ENDIAN /* OpenGL GL_RGBA, GL_UNSIGNED_BYTE format, hopefully supported */ DRM_FORMAT_XBGR8888, DRM_FORMAT_ABGR8888 #endif }; plane = g_object_new (META_TYPE_KMS_PLANE, NULL); plane->type = type; plane->is_fake = TRUE; plane->possible_crtcs = 1 << meta_kms_crtc_get_idx (crtc); plane->device = meta_kms_crtc_get_device (crtc); set_formats_from_array (plane, fake_plane_drm_formats, G_N_ELEMENTS (fake_plane_drm_formats)); return plane; } static void meta_kms_plane_finalize (GObject *object) { MetaKmsPlane *plane = META_KMS_PLANE (object); g_hash_table_destroy (plane->formats_modifiers); G_OBJECT_CLASS (meta_kms_plane_parent_class)->finalize (object); } static void meta_kms_plane_init (MetaKmsPlane *plane) { plane->formats_modifiers = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, (GDestroyNotify) free_modifier_array); } static void meta_kms_plane_class_init (MetaKmsPlaneClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_plane_finalize; } muffin-6.4.1/src/backends/native/meta-gpu-kms.c0000664000175000017500000004053214723361714020250 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (c) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-gpu-kms.h" #include #include #include #include #include #include #include #include #include "backends/meta-crtc.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-output.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-device.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-output-kms.h" #include "meta-default-modes.h" struct _MetaGpuKms { MetaGpu parent; MetaKmsDevice *kms_device; uint32_t id; int fd; clockid_t clock_id; gboolean resources_init_failed_before; }; G_DEFINE_TYPE (MetaGpuKms, meta_gpu_kms, META_TYPE_GPU) gboolean meta_gpu_kms_add_fb (MetaGpuKms *gpu_kms, gboolean use_modifiers, const MetaGpuKmsFBArgs *args, uint32_t *fb_id_out, GError **error) { MetaDrmFormatBuf tmp; uint32_t fb_id; if (use_modifiers && args->modifiers[0] != DRM_FORMAT_MOD_INVALID) { if (drmModeAddFB2WithModifiers (gpu_kms->fd, args->width, args->height, args->format, args->handles, args->strides, args->offsets, args->modifiers, &fb_id, DRM_MODE_FB_MODIFIERS)) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "drmModeAddFB2WithModifiers failed: %s", g_strerror (errno)); return FALSE; } } else if (drmModeAddFB2 (gpu_kms->fd, args->width, args->height, args->format, args->handles, args->strides, args->offsets, &fb_id, 0)) { if (args->format != DRM_FORMAT_XRGB8888) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "drmModeAddFB does not support format '%s' (0x%x)", meta_drm_format_to_string (&tmp, args->format), args->format); return FALSE; } if (drmModeAddFB (gpu_kms->fd, args->width, args->height, 24, 32, args->strides[0], args->handles[0], &fb_id)) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (errno), "drmModeAddFB failed: %s", g_strerror (errno)); return FALSE; } } *fb_id_out = fb_id; return TRUE; } gboolean meta_gpu_kms_is_crtc_active (MetaGpuKms *gpu_kms, MetaCrtc *crtc) { MetaGpu *gpu = META_GPU (gpu_kms); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *l; gboolean connected_crtc_found; g_assert (meta_crtc_get_gpu (crtc) == META_GPU (gpu_kms)); if (meta_monitor_manager_get_power_save_mode (monitor_manager)) return FALSE; connected_crtc_found = FALSE; for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; MetaCrtc *assigned_crtc; assigned_crtc = meta_output_get_assigned_crtc (output); if (assigned_crtc == crtc) { connected_crtc_found = TRUE; break; } } if (!connected_crtc_found) return FALSE; return TRUE; } static int64_t timespec_to_nanoseconds (const struct timespec *ts) { const int64_t one_billion = 1000000000; return ((int64_t) ts->tv_sec) * one_billion + ts->tv_nsec; } gboolean meta_gpu_kms_wait_for_flip (MetaGpuKms *gpu_kms, GError **error) { if (meta_kms_device_dispatch_sync (gpu_kms->kms_device, error) < 0) return FALSE; return TRUE; } MetaKmsDevice * meta_gpu_kms_get_kms_device (MetaGpuKms *gpu_kms) { return gpu_kms->kms_device; } int meta_gpu_kms_get_fd (MetaGpuKms *gpu_kms) { return gpu_kms->fd; } uint32_t meta_gpu_kms_get_id (MetaGpuKms *gpu_kms) { return gpu_kms->id; } const char * meta_gpu_kms_get_file_path (MetaGpuKms *gpu_kms) { return meta_kms_device_get_path (gpu_kms->kms_device); } int64_t meta_gpu_kms_get_current_time_ns (MetaGpuKms *gpu_kms) { struct timespec ts; if (clock_gettime (gpu_kms->clock_id, &ts)) return 0; return timespec_to_nanoseconds (&ts); } void meta_gpu_kms_set_power_save_mode (MetaGpuKms *gpu_kms, uint64_t state, MetaKmsUpdate *kms_update) { MetaGpu *gpu = META_GPU (gpu_kms); GList *l; for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; meta_output_kms_set_power_save_mode (output, state, kms_update); } if (state != META_POWER_SAVE_ON) { /* Turn off CRTCs for DPMS */ for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = META_CRTC (l->data); meta_kms_update_mode_set (kms_update, meta_crtc_kms_get_kms_crtc (crtc), NULL, NULL); } } } gboolean meta_gpu_kms_is_boot_vga (MetaGpuKms *gpu_kms) { MetaKmsDeviceFlag flags; flags = meta_kms_device_get_flags (gpu_kms->kms_device); return !!(flags & META_KMS_DEVICE_FLAG_BOOT_VGA); } gboolean meta_gpu_kms_is_platform_device (MetaGpuKms *gpu_kms) { MetaKmsDeviceFlag flags; flags = meta_kms_device_get_flags (gpu_kms->kms_device); return !!(flags & META_KMS_DEVICE_FLAG_PLATFORM_DEVICE); } static int compare_outputs (gconstpointer one, gconstpointer two) { const MetaOutput *o_one = one, *o_two = two; return strcmp (o_one->name, o_two->name); } static void meta_crtc_mode_destroy_notify (MetaCrtcMode *mode) { g_slice_free (drmModeModeInfo, mode->driver_private); } gboolean meta_drm_mode_equal (const drmModeModeInfo *one, const drmModeModeInfo *two) { return (one->clock == two->clock && one->hdisplay == two->hdisplay && one->hsync_start == two->hsync_start && one->hsync_end == two->hsync_end && one->htotal == two->htotal && one->hskew == two->hskew && one->vdisplay == two->vdisplay && one->vsync_start == two->vsync_start && one->vsync_end == two->vsync_end && one->vtotal == two->vtotal && one->vscan == two->vscan && one->vrefresh == two->vrefresh && one->flags == two->flags && one->type == two->type && strncmp (one->name, two->name, DRM_DISPLAY_MODE_LEN) == 0); } static guint drm_mode_hash (gconstpointer ptr) { const drmModeModeInfo *mode = ptr; guint hash = 0; /* * We don't include the name in the hash because it's generally * derived from the other fields (hdisplay, vdisplay and flags) */ hash ^= mode->clock; hash ^= mode->hdisplay ^ mode->hsync_start ^ mode->hsync_end; hash ^= mode->vdisplay ^ mode->vsync_start ^ mode->vsync_end; hash ^= mode->vrefresh; hash ^= mode->flags ^ mode->type; return hash; } MetaCrtcMode * meta_gpu_kms_get_mode_from_drm_mode (MetaGpuKms *gpu_kms, const drmModeModeInfo *drm_mode) { MetaGpu *gpu = META_GPU (gpu_kms); GList *l; for (l = meta_gpu_get_modes (gpu); l; l = l->next) { MetaCrtcMode *mode = l->data; if (meta_drm_mode_equal (drm_mode, mode->driver_private)) return mode; } g_assert_not_reached (); return NULL; } static MetaCrtcMode * create_mode (const drmModeModeInfo *drm_mode, long mode_id) { MetaCrtcMode *mode; mode = g_object_new (META_TYPE_CRTC_MODE, NULL); mode->mode_id = mode_id; mode->name = g_strndup (drm_mode->name, DRM_DISPLAY_MODE_LEN); mode->width = drm_mode->hdisplay; mode->height = drm_mode->vdisplay; mode->flags = drm_mode->flags; mode->refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); mode->driver_private = g_slice_dup (drmModeModeInfo, drm_mode); mode->driver_notify = (GDestroyNotify) meta_crtc_mode_destroy_notify; return mode; } static MetaOutput * find_output_by_connector_id (GList *outputs, uint32_t connector_id) { GList *l; for (l = outputs; l; l = l->next) { MetaOutput *output = l->data; if (meta_output_kms_get_connector_id (output) == connector_id) return output; } return NULL; } static void setup_output_clones (MetaGpu *gpu) { GList *l; for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; GList *k; for (k = meta_gpu_get_outputs (gpu); k; k = k->next) { MetaOutput *other_output = k->data; if (other_output == output) continue; if (meta_output_kms_can_clone (output, other_output)) { output->n_possible_clones++; output->possible_clones = g_renew (MetaOutput *, output->possible_clones, output->n_possible_clones); output->possible_clones[output->n_possible_clones - 1] = other_output; } } } } static void init_modes (MetaGpuKms *gpu_kms) { MetaGpu *gpu = META_GPU (gpu_kms); GHashTable *modes_table; GList *l; GList *modes; GHashTableIter iter; drmModeModeInfo *drm_mode; int i; long mode_id; /* * Gather all modes on all connected connectors. */ modes_table = g_hash_table_new (drm_mode_hash, (GEqualFunc) meta_drm_mode_equal); for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next) { MetaKmsConnector *kms_connector = l->data; const MetaKmsConnectorState *state; state = meta_kms_connector_get_current_state (kms_connector); if (!state) continue; for (i = 0; i < state->n_modes; i++) g_hash_table_add (modes_table, &state->modes[i]); } modes = NULL; g_hash_table_iter_init (&iter, modes_table); mode_id = 0; while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &drm_mode)) { MetaCrtcMode *mode; mode = create_mode (drm_mode, (long) mode_id); modes = g_list_append (modes, mode); mode_id++; } g_hash_table_destroy (modes_table); for (i = 0; i < G_N_ELEMENTS (meta_default_landscape_drm_mode_infos); i++) { MetaCrtcMode *mode; mode = create_mode (&meta_default_landscape_drm_mode_infos[i], mode_id); modes = g_list_append (modes, mode); mode_id++; } for (i = 0; i < G_N_ELEMENTS (meta_default_portrait_drm_mode_infos); i++) { MetaCrtcMode *mode; mode = create_mode (&meta_default_portrait_drm_mode_infos[i], mode_id); modes = g_list_append (modes, mode); mode_id++; } meta_gpu_take_modes (gpu, modes); } static void init_crtcs (MetaGpuKms *gpu_kms) { MetaGpu *gpu = META_GPU (gpu_kms); MetaKmsDevice *kms_device = gpu_kms->kms_device; GList *l; GList *crtcs; crtcs = NULL; for (l = meta_kms_device_get_crtcs (kms_device); l; l = l->next) { MetaKmsCrtc *kms_crtc = l->data; MetaCrtc *crtc; crtc = meta_create_kms_crtc (gpu_kms, kms_crtc); crtcs = g_list_append (crtcs, crtc); } meta_gpu_take_crtcs (gpu, crtcs); } static void init_frame_clock (MetaGpuKms *gpu_kms) { uint64_t uses_monotonic; if (drmGetCap (gpu_kms->fd, DRM_CAP_TIMESTAMP_MONOTONIC, &uses_monotonic) != 0) uses_monotonic = 0; gpu_kms->clock_id = uses_monotonic ? CLOCK_MONOTONIC : CLOCK_REALTIME; } static void init_outputs (MetaGpuKms *gpu_kms) { MetaGpu *gpu = META_GPU (gpu_kms); GList *old_outputs; GList *outputs; GList *l; old_outputs = meta_gpu_get_outputs (gpu); outputs = NULL; for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next) { MetaKmsConnector *kms_connector = l->data; const MetaKmsConnectorState *connector_state; MetaOutput *output; MetaOutput *old_output; GError *error = NULL; connector_state = meta_kms_connector_get_current_state (kms_connector); if (!connector_state) continue; old_output = find_output_by_connector_id (old_outputs, meta_kms_connector_get_id (kms_connector)); output = meta_create_kms_output (gpu_kms, kms_connector, old_output, &error); if (!output) { g_warning ("Failed to create KMS output: %s", error->message); g_error_free (error); } else { outputs = g_list_prepend (outputs, output); } } /* Sort the outputs for easier handling in MetaMonitorConfig */ outputs = g_list_sort (outputs, compare_outputs); meta_gpu_take_outputs (gpu, outputs); setup_output_clones (gpu); } static gboolean meta_gpu_kms_read_current (MetaGpu *gpu, GError **error) { MetaGpuKms *gpu_kms = META_GPU_KMS (gpu); /* Note: we must not free the public structures (output, crtc, monitor mode and monitor info) here, they must be kept alive until the API users are done with them after we emit monitors-changed, and thus are freed by the platform-independent layer. */ init_modes (gpu_kms); init_crtcs (gpu_kms); init_outputs (gpu_kms); init_frame_clock (gpu_kms); return TRUE; } gboolean meta_gpu_kms_can_have_outputs (MetaGpuKms *gpu_kms) { GList *l; int n_connected_connectors = 0; for (l = meta_kms_device_get_connectors (gpu_kms->kms_device); l; l = l->next) { MetaKmsConnector *kms_connector = l->data; if (meta_kms_connector_get_current_state (kms_connector)) n_connected_connectors++; } return n_connected_connectors > 0; } MetaGpuKms * meta_gpu_kms_new (MetaBackendNative *backend_native, MetaKmsDevice *kms_device, GError **error) { MetaGpuKms *gpu_kms; int kms_fd; kms_fd = meta_kms_device_leak_fd (kms_device); gpu_kms = g_object_new (META_TYPE_GPU_KMS, "backend", backend_native, NULL); gpu_kms->kms_device = kms_device; gpu_kms->fd = kms_fd; meta_gpu_kms_read_current (META_GPU (gpu_kms), NULL); return gpu_kms; } static void meta_gpu_kms_init (MetaGpuKms *gpu_kms) { static uint32_t id = 0; gpu_kms->fd = -1; gpu_kms->id = ++id; } static void meta_gpu_kms_class_init (MetaGpuKmsClass *klass) { MetaGpuClass *gpu_class = META_GPU_CLASS (klass); gpu_class->read_current = meta_gpu_kms_read_current; } muffin-6.4.1/src/backends/native/meta-output-kms.c0000664000175000017500000003046514723361714021021 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013-2017 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-output-kms.h" #include #include #include #include "backends/meta-crtc.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-utils.h" #include "backends/native/meta-crtc-kms.h" #include "meta-default-modes.h" #define SYNC_TOLERANCE 0.01 /* 1 percent */ typedef struct _MetaOutputKms { MetaOutput parent; MetaKmsConnector *kms_connector; } MetaOutputKms; MetaKmsConnector * meta_output_kms_get_kms_connector (MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; return output_kms->kms_connector; } void meta_output_kms_set_underscan (MetaOutput *output, MetaKmsUpdate *kms_update) { MetaOutputKms *output_kms = output->driver_private; if (!output->supports_underscanning) return; if (output->is_underscanning) { MetaCrtc *crtc; MetaCrtcConfig *crtc_config; uint64_t hborder, vborder; crtc = meta_output_get_assigned_crtc (output); crtc_config = crtc->config; hborder = MIN (128, (uint64_t) round (crtc_config->mode->width * 0.05)); vborder = MIN (128, (uint64_t) round (crtc_config->mode->height * 0.05)); g_debug ("Setting underscan of connector %s to %" G_GUINT64_FORMAT " x %" G_GUINT64_FORMAT, meta_kms_connector_get_name (output_kms->kms_connector), hborder, vborder); meta_kms_connector_set_underscanning (output_kms->kms_connector, kms_update, hborder, vborder); } else { g_debug ("Unsetting underscan of connector %s", meta_kms_connector_get_name (output_kms->kms_connector)); meta_kms_connector_unset_underscanning (output_kms->kms_connector, kms_update); } } uint32_t meta_output_kms_get_connector_id (MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; return meta_kms_connector_get_id (output_kms->kms_connector); } void meta_output_kms_set_power_save_mode (MetaOutput *output, uint64_t dpms_state, MetaKmsUpdate *kms_update) { MetaOutputKms *output_kms = output->driver_private; g_debug ("Setting DPMS state of connector %s to %" G_GUINT64_FORMAT, meta_kms_connector_get_name (output_kms->kms_connector), dpms_state); meta_kms_connector_update_set_dpms_state (output_kms->kms_connector, kms_update, dpms_state); } gboolean meta_output_kms_can_clone (MetaOutput *output, MetaOutput *other_output) { MetaOutputKms *output_kms = output->driver_private; MetaOutputKms *other_output_kms = other_output->driver_private; return meta_kms_connector_can_clone (output_kms->kms_connector, other_output_kms->kms_connector); } GBytes * meta_output_kms_read_edid (MetaOutput *output) { MetaOutputKms *output_kms = output->driver_private; const MetaKmsConnectorState *connector_state; GBytes *edid_data; connector_state = meta_kms_connector_get_current_state (output_kms->kms_connector); edid_data = connector_state->edid_data; if (!edid_data) return NULL; return g_bytes_new_from_bytes (edid_data, 0, g_bytes_get_size (edid_data)); } static void meta_output_destroy_notify (MetaOutput *output) { MetaOutputKms *output_kms; output_kms = output->driver_private; g_slice_free (MetaOutputKms, output_kms); } static void add_common_modes (MetaOutput *output, MetaGpuKms *gpu_kms) { const drmModeModeInfo *drm_mode; MetaCrtcMode *crtc_mode; GPtrArray *array; float refresh_rate; unsigned i; unsigned max_hdisplay = 0; unsigned max_vdisplay = 0; float max_refresh_rate = 0.0; for (i = 0; i < output->n_modes; i++) { drm_mode = output->modes[i]->driver_private; refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); max_hdisplay = MAX (max_hdisplay, drm_mode->hdisplay); max_vdisplay = MAX (max_vdisplay, drm_mode->vdisplay); max_refresh_rate = MAX (max_refresh_rate, refresh_rate); } max_refresh_rate = MAX (max_refresh_rate, 60.0); max_refresh_rate *= (1 + SYNC_TOLERANCE); array = g_ptr_array_new (); if (max_hdisplay > max_vdisplay) { for (i = 0; i < G_N_ELEMENTS (meta_default_landscape_drm_mode_infos); i++) { drm_mode = &meta_default_landscape_drm_mode_infos[i]; refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); if (drm_mode->hdisplay > max_hdisplay || drm_mode->vdisplay > max_vdisplay || refresh_rate > max_refresh_rate) continue; crtc_mode = meta_gpu_kms_get_mode_from_drm_mode (gpu_kms, drm_mode); g_ptr_array_add (array, crtc_mode); } } else { for (i = 0; i < G_N_ELEMENTS (meta_default_portrait_drm_mode_infos); i++) { drm_mode = &meta_default_portrait_drm_mode_infos[i]; refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); if (drm_mode->hdisplay > max_hdisplay || drm_mode->vdisplay > max_vdisplay || refresh_rate > max_refresh_rate) continue; crtc_mode = meta_gpu_kms_get_mode_from_drm_mode (gpu_kms, drm_mode); g_ptr_array_add (array, crtc_mode); } } output->modes = g_renew (MetaCrtcMode *, output->modes, output->n_modes + array->len); memcpy (output->modes + output->n_modes, array->pdata, array->len * sizeof (MetaCrtcMode *)); output->n_modes += array->len; g_ptr_array_free (array, TRUE); } static int compare_modes (const void *one, const void *two) { MetaCrtcMode *a = *(MetaCrtcMode **) one; MetaCrtcMode *b = *(MetaCrtcMode **) two; if (a->width != b->width) return a->width > b->width ? -1 : 1; if (a->height != b->height) return a->height > b->height ? -1 : 1; if (a->refresh_rate != b->refresh_rate) return a->refresh_rate > b->refresh_rate ? -1 : 1; return g_strcmp0 (b->name, a->name); } static gboolean init_output_modes (MetaOutput *output, MetaGpuKms *gpu_kms, GError **error) { MetaOutputKms *output_kms = output->driver_private; const MetaKmsConnectorState *connector_state; int i; connector_state = meta_kms_connector_get_current_state (output_kms->kms_connector); output->preferred_mode = NULL; output->n_modes = connector_state->n_modes; output->modes = g_new0 (MetaCrtcMode *, output->n_modes); for (i = 0; i < connector_state->n_modes; i++) { drmModeModeInfo *drm_mode = &connector_state->modes[i]; MetaCrtcMode *crtc_mode; crtc_mode = meta_gpu_kms_get_mode_from_drm_mode (gpu_kms, drm_mode); output->modes[i] = crtc_mode; if (drm_mode->type & DRM_MODE_TYPE_PREFERRED) output->preferred_mode = output->modes[i]; } /* FIXME: MSC feature bit? */ /* Presume that if the output supports scaling, then we have * a panel fitter capable of adjusting any mode to suit. */ if (connector_state->has_scaling) add_common_modes (output, gpu_kms); if (!output->modes) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No modes available"); return FALSE; } qsort (output->modes, output->n_modes, sizeof (MetaCrtcMode *), compare_modes); if (!output->preferred_mode) output->preferred_mode = output->modes[0]; return TRUE; } MetaOutput * meta_create_kms_output (MetaGpuKms *gpu_kms, MetaKmsConnector *kms_connector, MetaOutput *old_output, GError **error) { MetaGpu *gpu = META_GPU (gpu_kms); MetaOutput *output; MetaOutputKms *output_kms; const MetaKmsConnectorState *connector_state; uint32_t connector_id; GArray *crtcs; GList *l; uint32_t gpu_id; output = g_object_new (META_TYPE_OUTPUT, NULL); output_kms = g_slice_new0 (MetaOutputKms); output->driver_private = output_kms; output->driver_notify = (GDestroyNotify) meta_output_destroy_notify; output->gpu = gpu; output->name = g_strdup (meta_kms_connector_get_name (kms_connector)); gpu_id = meta_gpu_kms_get_id (gpu_kms); connector_id = meta_kms_connector_get_id (kms_connector); output->winsys_id = ((uint64_t) gpu_id << 32) | connector_id; output_kms->kms_connector = kms_connector; connector_state = meta_kms_connector_get_current_state (kms_connector); output->panel_orientation_transform = connector_state->panel_orientation_transform; if (meta_monitor_transform_is_rotated (output->panel_orientation_transform)) { output->width_mm = connector_state->height_mm; output->height_mm = connector_state->width_mm; } else { output->width_mm = connector_state->width_mm; output->height_mm = connector_state->height_mm; } if (!init_output_modes (output, gpu_kms, error)) { g_object_unref (output); return NULL; } crtcs = g_array_new (FALSE, FALSE, sizeof (MetaCrtc *)); for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; MetaKmsCrtc *kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); uint32_t crtc_idx; crtc_idx = meta_kms_crtc_get_idx (kms_crtc); if (connector_state->common_possible_crtcs & (1 << crtc_idx)) g_array_append_val (crtcs, crtc); } output->n_possible_crtcs = crtcs->len; output->possible_crtcs = (MetaCrtc **) g_array_free (crtcs, FALSE); if (connector_state->current_crtc_id) { for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if (crtc->crtc_id == connector_state->current_crtc_id) { meta_output_assign_crtc (output, crtc); break; } } } else { meta_output_unassign_crtc (output); } if (old_output) { output->is_primary = old_output->is_primary; output->is_presentation = old_output->is_presentation; } else { output->is_primary = FALSE; output->is_presentation = FALSE; } output->suggested_x = connector_state->suggested_x; output->suggested_y = connector_state->suggested_y; output->hotplug_mode_update = connector_state->hotplug_mode_update; output->supports_underscanning = meta_kms_connector_is_underscanning_supported (kms_connector); meta_output_parse_edid (output, connector_state->edid_data); output->connector_type = meta_kms_connector_get_connector_type (kms_connector); output->tile_info = connector_state->tile_info; /* FIXME: backlight is a very driver specific thing unfortunately, every DDX does its own thing, and the dumb KMS API does not include it. For example, xf86-video-intel has a list of paths to probe in /sys/class/backlight (one for each major HW maker, and then some). We can't do the same because we're not root. It might be best to leave backlight out of the story and rely on the setuid helper in gnome-settings-daemon. */ output->backlight_min = 0; output->backlight_max = 0; output->backlight = -1; return output; } muffin-6.4.1/src/backends/native/meta-event-native.h0000664000175000017500000000463614723361714021304 0ustar fabiofabio/* * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Authored by: * Carlos Garnacho */ #ifndef META_EVENT_NATIVE_H #define META_EVENT_NATIVE_H #include "clutter/clutter.h" typedef struct _MetaEventNative MetaEventNative; MetaEventNative * meta_event_native_copy (MetaEventNative *event_evdev); void meta_event_native_free (MetaEventNative *event_evdev); uint32_t meta_event_native_get_event_code (const ClutterEvent *event); void meta_event_native_set_event_code (ClutterEvent *event, uint32_t evcode); uint64_t meta_event_native_get_time_usec (const ClutterEvent *event); void meta_event_native_set_time_usec (ClutterEvent *event, uint64_t time_usec); void meta_event_native_set_relative_motion (ClutterEvent *event, double dx, double dy, double dx_unaccel, double dy_unaccel); gboolean meta_event_native_get_relative_motion (const ClutterEvent *event, double *dx, double *dy, double *dx_unaccel, double *dy_unaccel); int32_t meta_event_native_sequence_get_slot (const ClutterEventSequence *sequence); #endif /* META_EVENT_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-kms-page-flip.c0000664000175000017500000001322314723361714021316 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-impl.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update.h" struct _MetaKmsPageFlipData { int ref_count; MetaKmsImpl *impl; MetaKmsCrtc *crtc; const MetaKmsPageFlipFeedback *feedback; gpointer user_data; unsigned int sequence; unsigned int sec; unsigned int usec; GError *error; }; MetaKmsPageFlipData * meta_kms_page_flip_data_new (MetaKmsImpl *impl, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data) { MetaKmsPageFlipData *page_flip_data; page_flip_data = g_new0 (MetaKmsPageFlipData , 1); *page_flip_data = (MetaKmsPageFlipData) { .ref_count = 1, .impl = impl, .crtc = crtc, .feedback = feedback, .user_data = user_data, }; return page_flip_data; } MetaKmsPageFlipData * meta_kms_page_flip_data_ref (MetaKmsPageFlipData *page_flip_data) { page_flip_data->ref_count++; return page_flip_data; } void meta_kms_page_flip_data_unref (MetaKmsPageFlipData *page_flip_data) { page_flip_data->ref_count--; if (page_flip_data->ref_count == 0) { g_clear_error (&page_flip_data->error); g_free (page_flip_data); } } MetaKmsImpl * meta_kms_page_flip_data_get_kms_impl (MetaKmsPageFlipData *page_flip_data) { return page_flip_data->impl; } static void meta_kms_page_flip_data_flipped (MetaKms *kms, gpointer user_data) { MetaKmsPageFlipData *page_flip_data = user_data; meta_assert_not_in_kms_impl (kms); page_flip_data->feedback->flipped (page_flip_data->crtc, page_flip_data->sequence, page_flip_data->sec, page_flip_data->usec, page_flip_data->user_data); } void meta_kms_page_flip_data_set_timings_in_impl (MetaKmsPageFlipData *page_flip_data, unsigned int sequence, unsigned int sec, unsigned int usec) { MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); meta_assert_in_kms_impl (kms); page_flip_data->sequence = sequence; page_flip_data->sec = sec; page_flip_data->usec = usec; } void meta_kms_page_flip_data_flipped_in_impl (MetaKmsPageFlipData *page_flip_data) { MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); meta_assert_in_kms_impl (kms); meta_kms_queue_callback (kms, meta_kms_page_flip_data_flipped, meta_kms_page_flip_data_ref (page_flip_data), (GDestroyNotify) meta_kms_page_flip_data_unref); } static void meta_kms_page_flip_data_mode_set_fallback (MetaKms *kms, gpointer user_data) { MetaKmsPageFlipData *page_flip_data = user_data; meta_assert_not_in_kms_impl (kms); page_flip_data->feedback->mode_set_fallback (page_flip_data->crtc, page_flip_data->user_data); } void meta_kms_page_flip_data_mode_set_fallback_in_impl (MetaKmsPageFlipData *page_flip_data) { MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); meta_assert_in_kms_impl (kms); meta_kms_queue_callback (kms, meta_kms_page_flip_data_mode_set_fallback, meta_kms_page_flip_data_ref (page_flip_data), (GDestroyNotify) meta_kms_page_flip_data_unref); } static void meta_kms_page_flip_data_discard (MetaKms *kms, gpointer user_data) { MetaKmsPageFlipData *page_flip_data = user_data; meta_assert_not_in_kms_impl (kms); page_flip_data->feedback->discarded (page_flip_data->crtc, page_flip_data->user_data, page_flip_data->error); } void meta_kms_page_flip_data_take_error (MetaKmsPageFlipData *page_flip_data, GError *error) { g_assert (!page_flip_data->error); page_flip_data->error = error; } void meta_kms_page_flip_data_discard_in_impl (MetaKmsPageFlipData *page_flip_data, const GError *error) { MetaKms *kms = meta_kms_impl_get_kms (page_flip_data->impl); meta_assert_in_kms_impl (kms); if (error) meta_kms_page_flip_data_take_error (page_flip_data, g_error_copy (error)); meta_kms_queue_callback (kms, meta_kms_page_flip_data_discard, meta_kms_page_flip_data_ref (page_flip_data), (GDestroyNotify) meta_kms_page_flip_data_unref); } muffin-6.4.1/src/backends/native/meta-kms-impl-simple.c0000664000175000017500000010526714723361714021714 0ustar fabiofabio/* * Copyright (C) 2018-2019 Red Hat * Copyright (C) 2019-2020 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-impl-simple.h" #include #include #include #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-page-flip-private.h" #include "backends/native/meta-kms-plane.h" #include "backends/native/meta-kms-private.h" #include "backends/native/meta-kms-update-private.h" #include "backends/native/meta-kms-utils.h" typedef struct _CachedModeSet { GList *connectors; drmModeModeInfo *drm_mode; } CachedModeSet; struct _MetaKmsImplSimple { MetaKmsImpl parent; GSource *mode_set_fallback_feedback_source; GList *mode_set_fallback_page_flip_datas; GList *pending_page_flip_retries; GSource *retry_page_flips_source; GList *postponed_page_flip_datas; GList *postponed_mode_set_fallback_datas; GHashTable *cached_mode_sets; }; G_DEFINE_TYPE (MetaKmsImplSimple, meta_kms_impl_simple, META_TYPE_KMS_IMPL) static void flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple); MetaKmsImplSimple * meta_kms_impl_simple_new (MetaKms *kms, GError **error) { return g_object_new (META_TYPE_KMS_IMPL_SIMPLE, "kms", kms, NULL); } static gboolean process_connector_property (MetaKmsImpl *impl, MetaKmsUpdate *update, gpointer update_entry, GError **error) { MetaKmsConnectorProperty *connector_property = update_entry; MetaKmsConnector *connector = connector_property->connector; MetaKmsDevice *device = meta_kms_connector_get_device (connector); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); int fd; int ret; fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModeObjectSetProperty (fd, meta_kms_connector_get_id (connector), DRM_MODE_OBJECT_CONNECTOR, connector_property->prop_id, connector_property->value); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "Failed to set connector %u property %u: %s", meta_kms_connector_get_id (connector), connector_property->prop_id, g_strerror (-ret)); return FALSE; } return TRUE; } static gboolean process_plane_property (MetaKmsImpl *impl, MetaKmsPlane *plane, MetaKmsProperty *prop, GError **error) { MetaKmsDevice *device = meta_kms_plane_get_device (plane); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); int fd; int ret; fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModeObjectSetProperty (fd, meta_kms_plane_get_id (plane), DRM_MODE_OBJECT_PLANE, prop->prop_id, prop->value); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "Failed to set plane %u property %u: %s", meta_kms_plane_get_id (plane), prop->prop_id, g_strerror (-ret)); return FALSE; } return TRUE; } static CachedModeSet * cached_mode_set_new (GList *connectors, const drmModeModeInfo *drm_mode) { CachedModeSet *cached_mode_set; cached_mode_set = g_new0 (CachedModeSet, 1); *cached_mode_set = (CachedModeSet) { .connectors = g_list_copy (connectors), .drm_mode = g_memdup2 (drm_mode, sizeof *drm_mode), }; return cached_mode_set; } static void cached_mode_set_free (CachedModeSet *cached_mode_set) { g_list_free (cached_mode_set->connectors); g_free (cached_mode_set->drm_mode); g_free (cached_mode_set); } static void fill_connector_ids_array (GList *connectors, uint32_t **out_connectors, int *out_n_connectors) { GList *l; int i; *out_n_connectors = g_list_length (connectors); *out_connectors = g_new0 (uint32_t, *out_n_connectors); i = 0; for (l = connectors; l; l = l->next) { MetaKmsConnector *connector = l->data; (*out_connectors)[i++] = meta_kms_connector_get_id (connector); } } static gboolean process_mode_set (MetaKmsImpl *impl, MetaKmsUpdate *update, gpointer update_entry, GError **error) { MetaKmsModeSet *mode_set = update_entry; MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); MetaKmsCrtc *crtc = mode_set->crtc; MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); g_autofree uint32_t *connectors = NULL; int n_connectors; MetaKmsPlaneAssignment *plane_assignment; uint32_t x, y; uint32_t fb_id; int fd; int ret; crtc = mode_set->crtc; if (mode_set->drm_mode) { GList *l; fill_connector_ids_array (mode_set->connectors, &connectors, &n_connectors); plane_assignment = meta_kms_update_get_primary_plane_assignment (update, crtc); if (!plane_assignment) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing primary plane assignment for legacy mode set on CRTC %u", meta_kms_crtc_get_id (crtc)); return FALSE; } x = meta_fixed_16_to_int (plane_assignment->src_rect.x); y = meta_fixed_16_to_int (plane_assignment->src_rect.y); for (l = plane_assignment->plane_properties; l; l = l->next) { MetaKmsProperty *prop = l->data; if (!process_plane_property (impl, plane_assignment->plane, prop, error)) return FALSE; } fb_id = plane_assignment->fb_id; } else { x = y = 0; n_connectors = 0; connectors = NULL; fb_id = 0; } fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModeSetCrtc (fd, meta_kms_crtc_get_id (crtc), fb_id, x, y, connectors, n_connectors, mode_set->drm_mode); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "Failed to set mode %s on CRTC %u: %s", mode_set->drm_mode ? mode_set->drm_mode->name : "off", meta_kms_crtc_get_id (crtc), g_strerror (-ret)); return FALSE; } if (mode_set->drm_mode) { g_hash_table_replace (impl_simple->cached_mode_sets, crtc, cached_mode_set_new (mode_set->connectors, mode_set->drm_mode)); } else { g_hash_table_remove (impl_simple->cached_mode_sets, crtc); } return TRUE; } static gboolean process_crtc_gamma (MetaKmsImpl *impl, MetaKmsUpdate *update, gpointer update_entry, GError **error) { MetaKmsCrtcGamma *gamma = update_entry; MetaKmsCrtc *crtc = gamma->crtc; MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); int fd; int ret; fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModeCrtcSetGamma (fd, meta_kms_crtc_get_id (crtc), gamma->size, gamma->red, gamma->green, gamma->blue); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "drmModeCrtcSetGamma on CRTC %u failed: %s", meta_kms_crtc_get_id (crtc), g_strerror (-ret)); return FALSE; } return TRUE; } static gboolean is_timestamp_earlier_than (uint64_t ts1, uint64_t ts2) { if (ts1 == ts2) return FALSE; else return ts2 - ts1 < UINT64_MAX / 2; } typedef struct _RetryPageFlipData { MetaKmsCrtc *crtc; uint32_t fb_id; MetaKmsPageFlipData *page_flip_data; float refresh_rate; uint64_t retry_time_us; } RetryPageFlipData; static void retry_page_flip_data_free (RetryPageFlipData *retry_page_flip_data) { g_assert (!retry_page_flip_data->page_flip_data); g_free (retry_page_flip_data); } static CachedModeSet * get_cached_mode_set (MetaKmsImplSimple *impl_simple, MetaKmsCrtc *crtc) { return g_hash_table_lookup (impl_simple->cached_mode_sets, crtc); } static float get_cached_crtc_refresh_rate (MetaKmsImplSimple *impl_simple, MetaKmsCrtc *crtc) { CachedModeSet *cached_mode_set; cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets, crtc); g_assert (cached_mode_set); return meta_calculate_drm_mode_refresh_rate (cached_mode_set->drm_mode); } static gboolean retry_page_flips (gpointer user_data) { MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (user_data); uint64_t now_us; GList *l; meta_assert_in_kms_impl (meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple))); now_us = g_source_get_time (impl_simple->retry_page_flips_source); l = impl_simple->pending_page_flip_retries; while (l) { RetryPageFlipData *retry_page_flip_data = l->data; MetaKmsCrtc *crtc = retry_page_flip_data->crtc; MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); GList *l_next = l->next; int fd; int ret; MetaKmsPageFlipData *page_flip_data; if (is_timestamp_earlier_than (now_us, retry_page_flip_data->retry_time_us)) { l = l_next; continue; } fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModePageFlip (fd, meta_kms_crtc_get_id (crtc), retry_page_flip_data->fb_id, DRM_MODE_PAGE_FLIP_EVENT, retry_page_flip_data->page_flip_data); if (ret == -EBUSY) { float refresh_rate; refresh_rate = get_cached_crtc_refresh_rate (impl_simple, crtc); retry_page_flip_data->retry_time_us += (uint64_t) (G_USEC_PER_SEC / refresh_rate); l = l_next; continue; } impl_simple->pending_page_flip_retries = g_list_remove_link (impl_simple->pending_page_flip_retries, l); page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data); if (ret != 0) { g_autoptr (GError) error = NULL; g_set_error (&error, G_IO_ERROR, g_io_error_from_errno (-ret), "drmModePageFlip on CRTC %u failed: %s", meta_kms_crtc_get_id (crtc), g_strerror (-ret)); if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED)) g_critical ("Failed to page flip: %s", error->message); meta_kms_page_flip_data_discard_in_impl (page_flip_data, error); } retry_page_flip_data_free (retry_page_flip_data); l = l_next; } if (impl_simple->pending_page_flip_retries) { GList *l; uint64_t earliest_retry_time_us = 0; for (l = impl_simple->pending_page_flip_retries; l; l = l->next) { RetryPageFlipData *retry_page_flip_data = l->data; if (l == impl_simple->pending_page_flip_retries || is_timestamp_earlier_than (retry_page_flip_data->retry_time_us, earliest_retry_time_us)) earliest_retry_time_us = retry_page_flip_data->retry_time_us; } g_source_set_ready_time (impl_simple->retry_page_flips_source, earliest_retry_time_us); return G_SOURCE_CONTINUE; } else { g_clear_pointer (&impl_simple->retry_page_flips_source, g_source_unref); flush_postponed_page_flip_datas (impl_simple); return G_SOURCE_REMOVE; } } static void schedule_retry_page_flip (MetaKmsImplSimple *impl_simple, MetaKmsCrtc *crtc, uint32_t fb_id, float refresh_rate, MetaKmsPageFlipData *page_flip_data) { RetryPageFlipData *retry_page_flip_data; uint64_t now_us; uint64_t retry_time_us; now_us = g_get_monotonic_time (); retry_time_us = now_us + (uint64_t) (G_USEC_PER_SEC / refresh_rate); retry_page_flip_data = g_new0 (RetryPageFlipData, 1); *retry_page_flip_data = (RetryPageFlipData) { .crtc = crtc, .fb_id = fb_id, .page_flip_data = meta_kms_page_flip_data_ref (page_flip_data), .refresh_rate = refresh_rate, .retry_time_us = retry_time_us, }; if (!impl_simple->retry_page_flips_source) { MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)); GSource *source; source = meta_kms_add_source_in_impl (kms, retry_page_flips, impl_simple, NULL); g_source_set_ready_time (source, retry_time_us); impl_simple->retry_page_flips_source = source; } else { GList *l; for (l = impl_simple->pending_page_flip_retries; l; l = l->next) { RetryPageFlipData *pending_retry_page_flip_data = l->data; uint64_t pending_retry_time_us = pending_retry_page_flip_data->retry_time_us; if (is_timestamp_earlier_than (retry_time_us, pending_retry_time_us)) { g_source_set_ready_time (impl_simple->retry_page_flips_source, retry_time_us); break; } } } impl_simple->pending_page_flip_retries = g_list_append (impl_simple->pending_page_flip_retries, retry_page_flip_data); } static void invoke_page_flip_datas (GList *page_flip_datas, MetaPageFlipDataFeedbackFunc func) { g_list_foreach (page_flip_datas, (GFunc) func, NULL); } static void clear_page_flip_datas (GList **page_flip_datas) { g_list_free_full (*page_flip_datas, (GDestroyNotify) meta_kms_page_flip_data_unref); *page_flip_datas = NULL; } static gboolean mode_set_fallback_feedback_idle (gpointer user_data) { MetaKmsImplSimple *impl_simple = user_data; g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source, g_source_unref); if (impl_simple->pending_page_flip_retries) { impl_simple->postponed_mode_set_fallback_datas = g_steal_pointer (&impl_simple->mode_set_fallback_page_flip_datas); } else { invoke_page_flip_datas (impl_simple->mode_set_fallback_page_flip_datas, meta_kms_page_flip_data_mode_set_fallback_in_impl); clear_page_flip_datas (&impl_simple->mode_set_fallback_page_flip_datas); } return G_SOURCE_REMOVE; } static gboolean mode_set_fallback (MetaKmsImplSimple *impl_simple, MetaKmsUpdate *update, MetaKmsPageFlip *page_flip, MetaKmsPlaneAssignment *plane_assignment, MetaKmsPageFlipData *page_flip_data, GError **error) { MetaKms *kms = meta_kms_impl_get_kms (META_KMS_IMPL (impl_simple)); MetaKmsCrtc *crtc = page_flip->crtc; MetaKmsDevice *device = meta_kms_crtc_get_device (crtc); MetaKmsImplDevice *impl_device = meta_kms_device_get_impl_device (device); CachedModeSet *cached_mode_set; g_autofree uint32_t *connectors = NULL; int n_connectors; uint32_t x, y; int fd; int ret; cached_mode_set = g_hash_table_lookup (impl_simple->cached_mode_sets, crtc); if (!cached_mode_set) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Missing mode set for page flip fallback"); return FALSE; } fill_connector_ids_array (cached_mode_set->connectors, &connectors, &n_connectors); x = meta_fixed_16_to_int (plane_assignment->src_rect.x); y = meta_fixed_16_to_int (plane_assignment->src_rect.y); fd = meta_kms_impl_device_get_fd (impl_device); ret = drmModeSetCrtc (fd, meta_kms_crtc_get_id (crtc), plane_assignment->fb_id, x, y, connectors, n_connectors, cached_mode_set->drm_mode); if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "drmModeSetCrtc mode '%s' on CRTC %u failed: %s", cached_mode_set->drm_mode->name, meta_kms_crtc_get_id (crtc), g_strerror (-ret)); return FALSE; } if (!impl_simple->mode_set_fallback_feedback_source) { GSource *source; source = meta_kms_add_source_in_impl (kms, mode_set_fallback_feedback_idle, impl_simple, NULL); impl_simple->mode_set_fallback_feedback_source = source; } impl_simple->mode_set_fallback_page_flip_datas = g_list_prepend (impl_simple->mode_set_fallback_page_flip_datas, meta_kms_page_flip_data_ref (page_flip_data)); return TRUE; } static gboolean process_page_flip (MetaKmsImpl *impl, MetaKmsUpdate *update, gpointer update_entry, GError **error) { MetaKmsPageFlip *page_flip = update_entry; MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); MetaKmsCrtc *crtc; MetaKmsDevice *device; MetaKmsImplDevice *impl_device; MetaKmsPlaneAssignment *plane_assignment; MetaKmsPageFlipData *page_flip_data; MetaKmsCustomPageFlipFunc custom_page_flip_func; int fd; int ret; crtc = page_flip->crtc; plane_assignment = meta_kms_update_get_primary_plane_assignment (update, crtc); page_flip_data = meta_kms_page_flip_data_new (impl, crtc, page_flip->feedback, page_flip->user_data); device = meta_kms_crtc_get_device (crtc); impl_device = meta_kms_device_get_impl_device (device); fd = meta_kms_impl_device_get_fd (impl_device); custom_page_flip_func = page_flip->custom_page_flip_func; if (custom_page_flip_func) { ret = custom_page_flip_func (page_flip->custom_page_flip_user_data, meta_kms_page_flip_data_ref (page_flip_data)); } else { ret = drmModePageFlip (fd, meta_kms_crtc_get_id (crtc), plane_assignment->fb_id, DRM_MODE_PAGE_FLIP_EVENT, meta_kms_page_flip_data_ref (page_flip_data)); } if (ret != 0) meta_kms_page_flip_data_unref (page_flip_data); if (ret == -EBUSY) { CachedModeSet *cached_mode_set; cached_mode_set = get_cached_mode_set (impl_simple, crtc); if (cached_mode_set) { drmModeModeInfo *drm_mode; float refresh_rate; drm_mode = cached_mode_set->drm_mode; refresh_rate = meta_calculate_drm_mode_refresh_rate (drm_mode); schedule_retry_page_flip (impl_simple, crtc, plane_assignment->fb_id, refresh_rate, page_flip_data); } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Page flip of %u failed, and no mode set available", meta_kms_crtc_get_id (crtc)); meta_kms_page_flip_data_unref (page_flip_data); return FALSE; } } else if (ret == -EINVAL) { if (!mode_set_fallback (impl_simple, update, page_flip, plane_assignment, page_flip_data, error)) { meta_kms_page_flip_data_unref (page_flip_data); return FALSE; } } else if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "drmModePageFlip on CRTC %u failed: %s", meta_kms_crtc_get_id (crtc), g_strerror (-ret)); meta_kms_page_flip_data_unref (page_flip_data); return FALSE; } meta_kms_page_flip_data_unref (page_flip_data); return TRUE; } static void discard_page_flip (MetaKmsImpl *impl, MetaKmsUpdate *update, MetaKmsPageFlip *page_flip) { MetaKmsCrtc *crtc; MetaKmsPageFlipData *page_flip_data; crtc = page_flip->crtc; page_flip_data = meta_kms_page_flip_data_new (impl, crtc, page_flip->feedback, page_flip->user_data); meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL); meta_kms_page_flip_data_unref (page_flip_data); } static gboolean process_entries (MetaKmsImpl *impl, MetaKmsUpdate *update, GList *entries, gboolean (* func) (MetaKmsImpl *impl, MetaKmsUpdate *update, gpointer entry_data, GError **error), GError **error) { GList *l; for (l = entries; l; l = l->next) { if (!func (impl, update, l->data, error)) return FALSE; } return TRUE; } static gboolean process_cursor_plane_assignment (MetaKmsImpl *impl, MetaKmsUpdate *update, MetaKmsPlaneAssignment *plane_assignment, GError **error) { MetaKmsPlane *plane; MetaKmsDevice *device; MetaKmsImplDevice *impl_device; int fd; plane = plane_assignment->plane; device = meta_kms_plane_get_device (plane); impl_device = meta_kms_device_get_impl_device (device); fd = meta_kms_impl_device_get_fd (impl_device); if (!(plane_assignment->flags & META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED)) { int width, height; int ret = -1; width = meta_fixed_16_to_int (plane_assignment->dst_rect.width); height = meta_fixed_16_to_int (plane_assignment->dst_rect.height); if (plane_assignment->cursor_hotspot.is_valid) { ret = drmModeSetCursor2 (fd, meta_kms_crtc_get_id (plane_assignment->crtc), plane_assignment->fb_id, width, height, plane_assignment->cursor_hotspot.x, plane_assignment->cursor_hotspot.y); } if (ret != 0) { ret = drmModeSetCursor (fd, meta_kms_crtc_get_id (plane_assignment->crtc), plane_assignment->fb_id, width, height); } if (ret != 0) { g_set_error (error, G_IO_ERROR, g_io_error_from_errno (-ret), "drmModeSetCursor failed: %s", g_strerror (-ret)); return FALSE; } } drmModeMoveCursor (fd, meta_kms_crtc_get_id (plane_assignment->crtc), meta_fixed_16_to_int (plane_assignment->dst_rect.x), meta_fixed_16_to_int (plane_assignment->dst_rect.y)); return TRUE; } static gboolean process_plane_assignment (MetaKmsImpl *impl, MetaKmsUpdate *update, MetaKmsPlaneAssignment *plane_assignment, MetaKmsPlaneFeedback **plane_feedback) { MetaKmsPlane *plane; MetaKmsPlaneType plane_type; GError *error = NULL; plane = plane_assignment->plane; plane_type = meta_kms_plane_get_plane_type (plane); switch (plane_type) { case META_KMS_PLANE_TYPE_PRIMARY: /* Handled as part of the mode-set and page flip. */ return TRUE; case META_KMS_PLANE_TYPE_CURSOR: if (!process_cursor_plane_assignment (impl, update, plane_assignment, &error)) { *plane_feedback = meta_kms_plane_feedback_new_take_error (plane, plane_assignment->crtc, g_steal_pointer (&error)); return FALSE; } else { return TRUE; } case META_KMS_PLANE_TYPE_OVERLAY: error = g_error_new_literal (G_IO_ERROR, G_IO_ERROR_FAILED, "Overlay planes cannot be assigned"); *plane_feedback = meta_kms_plane_feedback_new_take_error (plane, plane_assignment->crtc, g_steal_pointer (&error)); return TRUE; } g_assert_not_reached (); } static GList * process_plane_assignments (MetaKmsImpl *impl, MetaKmsUpdate *update) { GList *failed_planes = NULL; GList *l; for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next) { MetaKmsPlaneAssignment *plane_assignment = l->data; MetaKmsPlaneFeedback *plane_feedback; if (!process_plane_assignment (impl, update, plane_assignment, &plane_feedback)) failed_planes = g_list_prepend (failed_planes, plane_feedback); } return failed_planes; } static GList * generate_all_failed_feedbacks (MetaKmsUpdate *update) { GList *failed_planes = NULL; GList *l; for (l = meta_kms_update_get_plane_assignments (update); l; l = l->next) { MetaKmsPlaneAssignment *plane_assignment = l->data; MetaKmsPlane *plane; MetaKmsPlaneType plane_type; MetaKmsPlaneFeedback *plane_feedback; plane = plane_assignment->plane; plane_type = meta_kms_plane_get_plane_type (plane); switch (plane_type) { case META_KMS_PLANE_TYPE_PRIMARY: continue; case META_KMS_PLANE_TYPE_CURSOR: case META_KMS_PLANE_TYPE_OVERLAY: break; } plane_feedback = meta_kms_plane_feedback_new_take_error (plane_assignment->plane, plane_assignment->crtc, g_error_new (G_IO_ERROR, G_IO_ERROR_FAILED, "Discarded")); failed_planes = g_list_prepend (failed_planes, plane_feedback); } return failed_planes; } static MetaKmsFeedback * meta_kms_impl_simple_process_update (MetaKmsImpl *impl, MetaKmsUpdate *update) { GError *error = NULL; GList *failed_planes; GList *l; meta_assert_in_kms_impl (meta_kms_impl_get_kms (impl)); if (!process_entries (impl, update, meta_kms_update_get_connector_properties (update), process_connector_property, &error)) goto err_planes_not_assigned; if (!process_entries (impl, update, meta_kms_update_get_mode_sets (update), process_mode_set, &error)) goto err_planes_not_assigned; if (!process_entries (impl, update, meta_kms_update_get_crtc_gammas (update), process_crtc_gamma, &error)) goto err_planes_not_assigned; failed_planes = process_plane_assignments (impl, update); if (failed_planes) { g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to assign one or more planes"); goto err_planes_assigned; } if (!process_entries (impl, update, meta_kms_update_get_page_flips (update), process_page_flip, &error)) goto err_planes_assigned; return meta_kms_feedback_new_passed (); err_planes_not_assigned: failed_planes = generate_all_failed_feedbacks (update); err_planes_assigned: for (l = meta_kms_update_get_page_flips (update); l; l = l->next) { MetaKmsPageFlip *page_flip = l->data; discard_page_flip (impl, update, page_flip); } return meta_kms_feedback_new_failed (failed_planes, error); } static void flush_postponed_page_flip_datas (MetaKmsImplSimple *impl_simple) { invoke_page_flip_datas (impl_simple->postponed_page_flip_datas, meta_kms_page_flip_data_flipped_in_impl); clear_page_flip_datas (&impl_simple->postponed_page_flip_datas); invoke_page_flip_datas (impl_simple->postponed_mode_set_fallback_datas, meta_kms_page_flip_data_mode_set_fallback_in_impl); clear_page_flip_datas (&impl_simple->postponed_mode_set_fallback_datas); } static void meta_kms_impl_simple_handle_page_flip_callback (MetaKmsImpl *impl, MetaKmsPageFlipData *page_flip_data) { MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); if (impl_simple->pending_page_flip_retries) { impl_simple->postponed_page_flip_datas = g_list_append (impl_simple->postponed_page_flip_datas, meta_kms_page_flip_data_ref (page_flip_data)); } else { meta_kms_page_flip_data_flipped_in_impl (page_flip_data); } meta_kms_page_flip_data_unref (page_flip_data); } static void meta_kms_impl_simple_discard_pending_page_flips (MetaKmsImpl *impl) { MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); GList *l; if (!impl_simple->pending_page_flip_retries) return; for (l = impl_simple->pending_page_flip_retries; l; l = l->next) { RetryPageFlipData *retry_page_flip_data = l->data; MetaKmsPageFlipData *page_flip_data; page_flip_data = g_steal_pointer (&retry_page_flip_data->page_flip_data); meta_kms_page_flip_data_discard_in_impl (page_flip_data, NULL); retry_page_flip_data_free (retry_page_flip_data); } g_clear_pointer (&impl_simple->pending_page_flip_retries, g_list_free); g_clear_pointer (&impl_simple->retry_page_flips_source, g_source_destroy); } static void meta_kms_impl_simple_dispatch_idle (MetaKmsImpl *impl) { MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (impl); if (impl_simple->mode_set_fallback_feedback_source) mode_set_fallback_feedback_idle (impl_simple); } static void meta_kms_impl_simple_notify_device_created (MetaKmsImpl *impl, MetaKmsDevice *device) { GList *l; for (l = meta_kms_device_get_crtcs (device); l; l = l->next) { MetaKmsCrtc *crtc = l->data; MetaKmsPlane *plane; plane = meta_kms_device_get_cursor_plane_for (device, crtc); if (plane) continue; meta_kms_device_add_fake_plane_in_impl (device, META_KMS_PLANE_TYPE_CURSOR, crtc); } } static void meta_kms_impl_simple_finalize (GObject *object) { MetaKmsImplSimple *impl_simple = META_KMS_IMPL_SIMPLE (object); g_list_free_full (impl_simple->pending_page_flip_retries, (GDestroyNotify) retry_page_flip_data_free); g_list_free_full (impl_simple->postponed_page_flip_datas, (GDestroyNotify) meta_kms_page_flip_data_unref); g_list_free_full (impl_simple->postponed_mode_set_fallback_datas, (GDestroyNotify) meta_kms_page_flip_data_unref); g_clear_pointer (&impl_simple->mode_set_fallback_feedback_source, g_source_destroy); g_hash_table_destroy (impl_simple->cached_mode_sets); G_OBJECT_CLASS (meta_kms_impl_simple_parent_class)->finalize (object); } static void meta_kms_impl_simple_init (MetaKmsImplSimple *impl_simple) { impl_simple->cached_mode_sets = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) cached_mode_set_free); } static void meta_kms_impl_simple_class_init (MetaKmsImplSimpleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaKmsImplClass *impl_class = META_KMS_IMPL_CLASS (klass); object_class->finalize = meta_kms_impl_simple_finalize; impl_class->process_update = meta_kms_impl_simple_process_update; impl_class->handle_page_flip_callback = meta_kms_impl_simple_handle_page_flip_callback; impl_class->discard_pending_page_flips = meta_kms_impl_simple_discard_pending_page_flips; impl_class->dispatch_idle = meta_kms_impl_simple_dispatch_idle; impl_class->notify_device_created = meta_kms_impl_simple_notify_device_created; } muffin-6.4.1/src/backends/native/meta-crtc-kms.h0000664000175000017500000000522314723361714020413 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_CRTC_KMS_H #define META_CRTC_KMS_H #include #include #include "backends/meta-backend-types.h" #include "backends/meta-crtc.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-crtc.h" gboolean meta_crtc_kms_is_transform_handled (MetaCrtc *crtc, MetaMonitorTransform transform); void meta_crtc_kms_apply_transform (MetaCrtc *crtc, MetaKmsPlaneAssignment *kms_plane_assignment); void meta_crtc_kms_assign_primary_plane (MetaCrtc *crtc, uint32_t fb_id, MetaKmsUpdate *kms_update); void meta_crtc_kms_set_mode (MetaCrtc *crtc, MetaKmsUpdate *kms_update); void meta_crtc_kms_page_flip (MetaCrtc *crtc, const MetaKmsPageFlipFeedback *page_flip_feedback, gpointer user_data, MetaKmsUpdate *kms_update); void meta_crtc_kms_set_is_underscanning (MetaCrtc *crtc, gboolean is_underscanning); MetaKmsCrtc * meta_crtc_kms_get_kms_crtc (MetaCrtc *crtc); GArray * meta_crtc_kms_get_modifiers (MetaCrtc *crtc, uint32_t format); GArray * meta_crtc_kms_copy_drm_format_list (MetaCrtc *crtc); gboolean meta_crtc_kms_supports_format (MetaCrtc *crtc, uint32_t drm_format); MetaCrtc * meta_crtc_kms_from_kms_crtc (MetaKmsCrtc *kms_crtc); MetaCrtc * meta_create_kms_crtc (MetaGpuKms *gpu_kms, MetaKmsCrtc *kms_crtc); #endif /* META_CRTC_KMS_H */ muffin-6.4.1/src/backends/native/meta-renderer-native.h0000664000175000017500000000376614723361714021774 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_RENDERER_NATIVE_H #define META_RENDERER_NATIVE_H #include #include #include #include "backends/meta-renderer.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-monitor-manager-kms.h" #define META_TYPE_RENDERER_NATIVE (meta_renderer_native_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererNative, meta_renderer_native, META, RENDERER_NATIVE, MetaRenderer) typedef enum _MetaRendererNativeMode { META_RENDERER_NATIVE_MODE_GBM, #ifdef HAVE_EGL_DEVICE META_RENDERER_NATIVE_MODE_EGL_DEVICE #endif } MetaRendererNativeMode; MetaRendererNative * meta_renderer_native_new (MetaBackendNative *backend_native, GError **error); struct gbm_device * meta_gbm_device_from_gpu (MetaGpuKms *gpu_kms); void meta_renderer_native_finish_frame (MetaRendererNative *renderer_native); int64_t meta_renderer_native_get_frame_counter (MetaRendererNative *renderer_native); void meta_renderer_native_reset_modes (MetaRendererNative *renderer_native); #endif /* META_RENDERER_NATIVE_H */ muffin-6.4.1/src/backends/native/meta-kms-plane.h0000664000175000017500000000443214723361714020560 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_PLANE_H #define META_KMS_PLANE_H #include #include #include #include "backends/native/meta-kms-types.h" #include "backends/meta-monitor-transform.h" enum _MetaKmsPlaneType { META_KMS_PLANE_TYPE_PRIMARY, META_KMS_PLANE_TYPE_CURSOR, META_KMS_PLANE_TYPE_OVERLAY, }; #define META_TYPE_KMS_PLANE meta_kms_plane_get_type () G_DECLARE_FINAL_TYPE (MetaKmsPlane, meta_kms_plane, META, KMS_PLANE, GObject) MetaKmsDevice * meta_kms_plane_get_device (MetaKmsPlane *plane); uint32_t meta_kms_plane_get_id (MetaKmsPlane *plane); MetaKmsPlaneType meta_kms_plane_get_plane_type (MetaKmsPlane *plane); gboolean meta_kms_plane_is_transform_handled (MetaKmsPlane *plane, MetaMonitorTransform transform); GArray * meta_kms_plane_get_modifiers_for_format (MetaKmsPlane *plane, uint32_t format); GArray * meta_kms_plane_copy_drm_format_list (MetaKmsPlane *plane); gboolean meta_kms_plane_is_format_supported (MetaKmsPlane *plane, uint32_t format); gboolean meta_kms_plane_is_usable_with (MetaKmsPlane *plane, MetaKmsCrtc *crtc); void meta_kms_plane_update_set_rotation (MetaKmsPlane *plane, MetaKmsPlaneAssignment *plane_assignment, MetaMonitorTransform transform); #endif /* META_KMS_PLANE_H */ muffin-6.4.1/src/backends/native/meta-kms-connector.c0000664000175000017500000004556414723361714021461 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-kms-connector.h" #include "backends/native/meta-kms-connector-private.h" #include #include "backends/native/meta-kms-crtc.h" #include "backends/native/meta-kms-device-private.h" #include "backends/native/meta-kms-impl-device.h" #include "backends/native/meta-kms-update-private.h" struct _MetaKmsConnector { GObject parent; MetaKmsDevice *device; uint32_t id; MetaConnectorType type; uint32_t type_id; char *name; MetaKmsConnectorState *current_state; uint32_t dpms_prop_id; uint32_t underscan_prop_id; uint32_t underscan_hborder_prop_id; uint32_t underscan_vborder_prop_id; uint32_t edid_blob_id; uint32_t tile_blob_id; }; G_DEFINE_TYPE (MetaKmsConnector, meta_kms_connector, G_TYPE_OBJECT) MetaKmsDevice * meta_kms_connector_get_device (MetaKmsConnector *connector) { return connector->device; } void meta_kms_connector_update_set_dpms_state (MetaKmsConnector *connector, MetaKmsUpdate *update, uint64_t state) { meta_kms_update_set_connector_property (update, connector, connector->dpms_prop_id, state); } void meta_kms_connector_set_underscanning (MetaKmsConnector *connector, MetaKmsUpdate *update, uint64_t hborder, uint64_t vborder) { meta_kms_update_set_connector_property (update, connector, connector->underscan_prop_id, 1); meta_kms_update_set_connector_property (update, connector, connector->underscan_hborder_prop_id, hborder); meta_kms_update_set_connector_property (update, connector, connector->underscan_vborder_prop_id, vborder); } void meta_kms_connector_unset_underscanning (MetaKmsConnector *connector, MetaKmsUpdate *update) { meta_kms_update_set_connector_property (update, connector, connector->underscan_prop_id, 0); } MetaConnectorType meta_kms_connector_get_connector_type (MetaKmsConnector *connector) { return connector->type; } uint32_t meta_kms_connector_get_id (MetaKmsConnector *connector) { return connector->id; } const char * meta_kms_connector_get_name (MetaKmsConnector *connector) { return connector->name; } gboolean meta_kms_connector_can_clone (MetaKmsConnector *connector, MetaKmsConnector *other_connector) { MetaKmsConnectorState *state = connector->current_state; MetaKmsConnectorState *other_state = other_connector->current_state; if (state->common_possible_clones == 0 || other_state->common_possible_clones == 0) return FALSE; if (state->encoder_device_idxs != other_state->encoder_device_idxs) return FALSE; return TRUE; } const MetaKmsConnectorState * meta_kms_connector_get_current_state (MetaKmsConnector *connector) { return connector->current_state; } gboolean meta_kms_connector_is_underscanning_supported (MetaKmsConnector *connector) { return connector->underscan_prop_id != 0; } static void set_panel_orientation (MetaKmsConnectorState *state, drmModePropertyPtr prop, uint64_t orientation) { const char *name; name = prop->enums[orientation].name; if (strcmp (name, "Upside Down") == 0) { state->panel_orientation_transform = META_MONITOR_TRANSFORM_180; } else if (strcmp (name, "Left Side Up") == 0) { /* Left side up, rotate 90 degrees counter clockwise to correct */ state->panel_orientation_transform = META_MONITOR_TRANSFORM_90; } else if (strcmp (name, "Right Side Up") == 0) { /* Right side up, rotate 270 degrees counter clockwise to correct */ state->panel_orientation_transform = META_MONITOR_TRANSFORM_270; } else { state->panel_orientation_transform = META_MONITOR_TRANSFORM_NORMAL; } } static void state_set_properties (MetaKmsConnectorState *state, MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector) { int fd; int i; fd = meta_kms_impl_device_get_fd (impl_device); for (i = 0; i < drm_connector->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (fd, drm_connector->props[i]); if (!prop) continue; if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "suggested X") == 0) state->suggested_x = drm_connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "suggested Y") == 0) state->suggested_y = drm_connector->prop_values[i]; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "hotplug_mode_update") == 0) state->hotplug_mode_update = drm_connector->prop_values[i]; else if (strcmp (prop->name, "scaling mode") == 0) state->has_scaling = TRUE; else if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "panel orientation") == 0) set_panel_orientation (state, prop, drm_connector->prop_values[i]); drmModeFreeProperty (prop); } } static CoglSubpixelOrder drm_subpixel_order_to_cogl_subpixel_order (drmModeSubPixel subpixel) { switch (subpixel) { case DRM_MODE_SUBPIXEL_NONE: return COGL_SUBPIXEL_ORDER_NONE; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB: return COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB; break; case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR: return COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR; break; case DRM_MODE_SUBPIXEL_VERTICAL_RGB: return COGL_SUBPIXEL_ORDER_VERTICAL_RGB; break; case DRM_MODE_SUBPIXEL_VERTICAL_BGR: return COGL_SUBPIXEL_ORDER_VERTICAL_BGR; break; case DRM_MODE_SUBPIXEL_UNKNOWN: return COGL_SUBPIXEL_ORDER_UNKNOWN; } return COGL_SUBPIXEL_ORDER_UNKNOWN; } static void state_set_edid (MetaKmsConnectorState *state, MetaKmsConnector *connector, MetaKmsImplDevice *impl_device, uint32_t blob_id) { int fd; drmModePropertyBlobPtr edid_blob; GBytes *edid_data; fd = meta_kms_impl_device_get_fd (impl_device); edid_blob = drmModeGetPropertyBlob (fd, blob_id); if (!edid_blob) { g_warning ("Failed to read EDID of connector %s: %s", connector->name, g_strerror (errno)); return; } edid_data = g_bytes_new (edid_blob->data, edid_blob->length); drmModeFreePropertyBlob (edid_blob); state->edid_data = edid_data; } static void state_set_tile_info (MetaKmsConnectorState *state, MetaKmsConnector *connector, MetaKmsImplDevice *impl_device, uint32_t blob_id) { int fd; drmModePropertyBlobPtr tile_blob; state->tile_info = (MetaTileInfo) { 0 }; fd = meta_kms_impl_device_get_fd (impl_device); tile_blob = drmModeGetPropertyBlob (fd, blob_id); if (!tile_blob) { g_warning ("Failed to read TILE of connector %s: %s", connector->name, strerror (errno)); return; } if (tile_blob->length > 0) { if (sscanf ((char *) tile_blob->data, "%d:%d:%d:%d:%d:%d:%d:%d", &state->tile_info.group_id, &state->tile_info.flags, &state->tile_info.max_h_tiles, &state->tile_info.max_v_tiles, &state->tile_info.loc_h_tile, &state->tile_info.loc_v_tile, &state->tile_info.tile_w, &state->tile_info.tile_h) != 8) { g_warning ("Couldn't understand TILE property blob of connector %s", connector->name); state->tile_info = (MetaTileInfo) { 0 }; } } drmModeFreePropertyBlob (tile_blob); } static void state_set_blobs (MetaKmsConnectorState *state, MetaKmsConnector *connector, MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector) { int fd; int i; fd = meta_kms_impl_device_get_fd (impl_device); for (i = 0; i < drm_connector->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (fd, drm_connector->props[i]); if (!prop) continue; if (prop->flags & DRM_MODE_PROP_BLOB) { uint32_t blob_id; blob_id = drm_connector->prop_values[i]; if (blob_id) { if (strcmp (prop->name, "EDID") == 0) state_set_edid (state, connector, impl_device, blob_id); else if (strcmp (prop->name, "TILE") == 0) state_set_tile_info (state, connector, impl_device, blob_id); } } drmModeFreeProperty (prop); } } static void state_set_physical_dimensions (MetaKmsConnectorState *state, drmModeConnector *drm_connector) { state->width_mm = drm_connector->mmWidth; state->height_mm = drm_connector->mmHeight; } static void state_set_modes (MetaKmsConnectorState *state, drmModeConnector *drm_connector) { state->modes = g_memdup2 (drm_connector->modes, drm_connector->count_modes * sizeof (drmModeModeInfo)); state->n_modes = drm_connector->count_modes; } static void set_encoder_device_idx_bit (uint32_t *encoder_device_idxs, uint32_t encoder_id, MetaKmsImplDevice *impl_device, drmModeRes *drm_resources) { int fd; int i; fd = meta_kms_impl_device_get_fd (impl_device); for (i = 0; i < drm_resources->count_encoders; i++) { drmModeEncoder *drm_encoder; drm_encoder = drmModeGetEncoder (fd, drm_resources->encoders[i]); if (!drm_encoder) continue; if (drm_encoder->encoder_id == encoder_id) { *encoder_device_idxs |= (1 << i); drmModeFreeEncoder (drm_encoder); break; } drmModeFreeEncoder (drm_encoder); } } static void state_set_crtc_state (MetaKmsConnectorState *state, drmModeConnector *drm_connector, MetaKmsImplDevice *impl_device, drmModeRes *drm_resources) { int fd; int i; uint32_t common_possible_crtcs; uint32_t common_possible_clones; uint32_t encoder_device_idxs; fd = meta_kms_impl_device_get_fd (impl_device); common_possible_crtcs = UINT32_MAX; common_possible_clones = UINT32_MAX; encoder_device_idxs = 0; for (i = 0; i < drm_connector->count_encoders; i++) { drmModeEncoder *drm_encoder; drm_encoder = drmModeGetEncoder (fd, drm_connector->encoders[i]); if (!drm_encoder) continue; common_possible_crtcs &= drm_encoder->possible_crtcs; common_possible_clones &= drm_encoder->possible_clones; set_encoder_device_idx_bit (&encoder_device_idxs, drm_encoder->encoder_id, impl_device, drm_resources); if (drm_connector->encoder_id == drm_encoder->encoder_id) state->current_crtc_id = drm_encoder->crtc_id; drmModeFreeEncoder (drm_encoder); } state->common_possible_crtcs = common_possible_crtcs; state->common_possible_clones = common_possible_clones; state->encoder_device_idxs = encoder_device_idxs; } static MetaKmsConnectorState * meta_kms_connector_state_new (void) { MetaKmsConnectorState *state; state = g_new0 (MetaKmsConnectorState, 1); state->suggested_x = -1; state->suggested_y = -1; return state; } static void meta_kms_connector_state_free (MetaKmsConnectorState *state) { g_clear_pointer (&state->edid_data, g_bytes_unref); g_free (state->modes); g_free (state); } static void meta_kms_connector_read_state (MetaKmsConnector *connector, MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector, drmModeRes *drm_resources) { MetaKmsConnectorState *state; g_clear_pointer (&connector->current_state, meta_kms_connector_state_free); if (!drm_connector || drm_connector->connection != DRM_MODE_CONNECTED) return; state = meta_kms_connector_state_new (); state_set_blobs (state, connector, impl_device, drm_connector); state_set_properties (state, impl_device, drm_connector); state->subpixel_order = drm_subpixel_order_to_cogl_subpixel_order (drm_connector->subpixel); state_set_physical_dimensions (state, drm_connector); state_set_modes (state, drm_connector); state_set_crtc_state (state, drm_connector, impl_device, drm_resources); connector->current_state = state; } void meta_kms_connector_update_state (MetaKmsConnector *connector, drmModeRes *drm_resources) { MetaKmsImplDevice *impl_device; drmModeConnector *drm_connector; impl_device = meta_kms_device_get_impl_device (connector->device); drm_connector = drmModeGetConnector (meta_kms_impl_device_get_fd (impl_device), connector->id); meta_kms_connector_read_state (connector, impl_device, drm_connector, drm_resources); if (drm_connector) drmModeFreeConnector (drm_connector); } void meta_kms_connector_predict_state (MetaKmsConnector *connector, MetaKmsUpdate *update) { GList *mode_sets; GList *l; if (!connector->current_state) return; mode_sets = meta_kms_update_get_mode_sets (update); for (l = mode_sets; l; l = l->next) { MetaKmsModeSet *mode_set = l->data; MetaKmsCrtc *crtc; if (!g_list_find (mode_set->connectors, connector)) continue; crtc = mode_set->crtc; if (crtc) connector->current_state->current_crtc_id = meta_kms_crtc_get_id (crtc); else connector->current_state->current_crtc_id = 0; break; } } static void find_property_ids (MetaKmsConnector *connector, MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector) { int fd; int i; fd = meta_kms_impl_device_get_fd (impl_device); for (i = 0; i < drm_connector->count_props; i++) { drmModePropertyPtr prop; prop = drmModeGetProperty (fd, drm_connector->props[i]); if (!prop) continue; if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "DPMS") == 0) connector->dpms_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_ENUM) && strcmp (prop->name, "underscan") == 0) connector->underscan_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "underscan hborder") == 0) connector->underscan_hborder_prop_id = prop->prop_id; else if ((prop->flags & DRM_MODE_PROP_RANGE) && strcmp (prop->name, "underscan vborder") == 0) connector->underscan_vborder_prop_id = prop->prop_id; drmModeFreeProperty (prop); } } static char * make_connector_name (drmModeConnector *drm_connector) { static const char * const connector_type_names[] = { "None", "VGA", "DVI-I", "DVI-D", "DVI-A", "Composite", "SVIDEO", "LVDS", "Component", "DIN", "DP", "HDMI", "HDMI-B", "TV", "eDP", "Virtual", "DSI", }; if (drm_connector->connector_type < G_N_ELEMENTS (connector_type_names)) return g_strdup_printf ("%s-%d", connector_type_names[drm_connector->connector_type], drm_connector->connector_type_id); else return g_strdup_printf ("Unknown%d-%d", drm_connector->connector_type, drm_connector->connector_type_id); } gboolean meta_kms_connector_is_same_as (MetaKmsConnector *connector, drmModeConnector *drm_connector) { return (connector->id == drm_connector->connector_id && connector->type == drm_connector->connector_type && connector->type_id == drm_connector->connector_type_id); } MetaKmsConnector * meta_kms_connector_new (MetaKmsImplDevice *impl_device, drmModeConnector *drm_connector, drmModeRes *drm_resources) { MetaKmsConnector *connector; connector = g_object_new (META_TYPE_KMS_CONNECTOR, NULL); connector->device = meta_kms_impl_device_get_device (impl_device); connector->id = drm_connector->connector_id; connector->type = (MetaConnectorType) drm_connector->connector_type; connector->type_id = drm_connector->connector_type_id; connector->name = make_connector_name (drm_connector); find_property_ids (connector, impl_device, drm_connector); meta_kms_connector_read_state (connector, impl_device, drm_connector, drm_resources); return connector; } static void meta_kms_connector_finalize (GObject *object) { MetaKmsConnector *connector = META_KMS_CONNECTOR (object); g_clear_pointer (&connector->current_state, meta_kms_connector_state_free); g_free (connector->name); G_OBJECT_CLASS (meta_kms_connector_parent_class)->finalize (object); } static void meta_kms_connector_init (MetaKmsConnector *connector) { } static void meta_kms_connector_class_init (MetaKmsConnectorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_kms_connector_finalize; } muffin-6.4.1/src/backends/native/meta-kms-update.h0000664000175000017500000001217614723361714020747 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_KMS_UPDATE_H #define META_KMS_UPDATE_H #include #include #include #include #include "backends/meta-monitor-transform.h" #include "backends/native/meta-kms-types.h" #include "meta/boxes.h" typedef enum _MetaKmsFeedbackResult { META_KMS_FEEDBACK_PASSED, META_KMS_FEEDBACK_FAILED, } MetaKmsFeedbackResult; typedef enum _MetaKmsAssignPlaneFlag { META_KMS_ASSIGN_PLANE_FLAG_NONE = 0, META_KMS_ASSIGN_PLANE_FLAG_FB_UNCHANGED = 1 << 0, } MetaKmsAssignPlaneFlag; struct _MetaKmsPageFlipFeedback { void (* flipped) (MetaKmsCrtc *crtc, unsigned int sequence, unsigned int tv_sec, unsigned int tv_usec, gpointer user_data); void (* mode_set_fallback) (MetaKmsCrtc *crtc, gpointer user_data); void (* discarded) (MetaKmsCrtc *crtc, gpointer user_data, const GError *error); }; typedef int (* MetaKmsCustomPageFlipFunc) (gpointer custom_page_flip_data, gpointer user_data); typedef struct _MetaKmsPlaneFeedback { MetaKmsPlane *plane; MetaKmsCrtc *crtc; GError *error; } MetaKmsPlaneFeedback; void meta_kms_feedback_free (MetaKmsFeedback *feedback); MetaKmsFeedbackResult meta_kms_feedback_get_result (MetaKmsFeedback *feedback); GList * meta_kms_feedback_get_failed_planes (MetaKmsFeedback *feedback); const GError * meta_kms_feedback_get_error (MetaKmsFeedback *feedback); MetaKmsUpdate * meta_kms_update_new (void); void meta_kms_update_free (MetaKmsUpdate *update); void meta_kms_update_mode_set (MetaKmsUpdate *update, MetaKmsCrtc *crtc, GList *connectors, drmModeModeInfo *drm_mode); MetaKmsPlaneAssignment * meta_kms_update_assign_plane (MetaKmsUpdate *update, MetaKmsCrtc *crtc, MetaKmsPlane *plane, uint32_t fb_id, MetaFixed16Rectangle src_rect, MetaFixed16Rectangle dst_rect, MetaKmsAssignPlaneFlag flags); MetaKmsPlaneAssignment * meta_kms_update_unassign_plane (MetaKmsUpdate *update, MetaKmsCrtc *crtc, MetaKmsPlane *plane); void meta_kms_update_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data); void meta_kms_update_custom_page_flip (MetaKmsUpdate *update, MetaKmsCrtc *crtc, const MetaKmsPageFlipFeedback *feedback, gpointer user_data, MetaKmsCustomPageFlipFunc custom_page_flip_func, gpointer custom_page_flip_user_data); void meta_kms_plane_assignment_set_cursor_hotspot (MetaKmsPlaneAssignment *plane_assignment, int x, int y); static inline MetaFixed16 meta_fixed_16_from_int (int16_t d) { return d * 65536; } static inline int16_t meta_fixed_16_to_int (MetaFixed16 fixed) { return fixed / 65536; } static inline MetaRectangle meta_fixed_16_rectangle_to_rectangle (MetaFixed16Rectangle fixed_rect) { return (MetaRectangle) { .x = meta_fixed_16_to_int (fixed_rect.x), .y = meta_fixed_16_to_int (fixed_rect.y), .width = meta_fixed_16_to_int (fixed_rect.width), .height = meta_fixed_16_to_int (fixed_rect.height), }; } G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsFeedback, meta_kms_feedback_free) G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaKmsUpdate, meta_kms_update_free) #endif /* META_KMS_UPDATE_H */ muffin-6.4.1/src/backends/native/meta-drm-buffer.c0000664000175000017500000000247714723361714020724 0ustar fabiofabio/* * Copyright (C) 2011 Intel Corporation. * Copyright (C) 2016 Red Hat * Copyright (C) 2018 DisplayLink (UK) Ltd. * Copyright (C) 2018 Canonical Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Daniel van Vugt */ #include "config.h" #include "backends/native/meta-drm-buffer.h" G_DEFINE_ABSTRACT_TYPE (MetaDrmBuffer, meta_drm_buffer, G_TYPE_OBJECT) uint32_t meta_drm_buffer_get_fb_id (MetaDrmBuffer *buffer) { return META_DRM_BUFFER_GET_CLASS (buffer)->get_fb_id (buffer); } static void meta_drm_buffer_init (MetaDrmBuffer *buffer) { } static void meta_drm_buffer_class_init (MetaDrmBufferClass *klass) { } muffin-6.4.1/src/backends/native/meta-monitor-manager-kms.c0000664000175000017500000005201514723361714022553 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat Inc. * Copyright (C) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Giovanni Campagna */ /** * SECTION:meta-monitor-manager-kms * @title: MetaMonitorManagerKms * @short_description: A subclass of #MetaMonitorManager using Linux DRM * * #MetaMonitorManagerKms is a subclass of #MetaMonitorManager which * implements its functionality "natively": it uses the appropriate * functions of the Linux DRM kernel module and using a udev client. * * See also #MetaMonitorManagerXrandr for an implementation using XRandR. */ #include "config.h" #include "backends/native/meta-monitor-manager-kms.h" #include #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-output.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-crtc-kms.h" #include "backends/native/meta-gpu-kms.h" #include "backends/native/meta-kms-update.h" #include "backends/native/meta-kms.h" #include "backends/native/meta-launcher.h" #include "backends/native/meta-output-kms.h" #include "backends/native/meta-renderer-native.h" #include "clutter/clutter.h" #include "meta/main.h" #include "meta/meta-x11-errors.h" typedef struct { GSource source; gpointer fd_tag; MetaMonitorManagerKms *manager_kms; } MetaKmsSource; struct _MetaMonitorManagerKms { MetaMonitorManager parent_instance; gulong kms_resources_changed_handler_id; }; struct _MetaMonitorManagerKmsClass { MetaMonitorManagerClass parent_class; }; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaMonitorManagerKms, meta_monitor_manager_kms, META_TYPE_MONITOR_MANAGER, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)) static GBytes * meta_monitor_manager_kms_read_edid (MetaMonitorManager *manager, MetaOutput *output) { return meta_output_kms_read_edid (output); } static void meta_monitor_manager_kms_read_current_state (MetaMonitorManager *manager) { MetaMonitorManagerClass *parent_class = META_MONITOR_MANAGER_CLASS (meta_monitor_manager_kms_parent_class); MetaPowerSave power_save_mode; power_save_mode = meta_monitor_manager_get_power_save_mode (manager); if (power_save_mode != META_POWER_SAVE_ON) meta_monitor_manager_power_save_mode_changed (manager, META_POWER_SAVE_ON); parent_class->read_current_state (manager); } static void meta_monitor_manager_kms_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKmsUpdate *kms_update; uint64_t state; GList *l; g_autoptr (MetaKmsFeedback) kms_feedback = NULL; switch (mode) { case META_POWER_SAVE_ON: state = DRM_MODE_DPMS_ON; break; case META_POWER_SAVE_STANDBY: state = DRM_MODE_DPMS_STANDBY; break; case META_POWER_SAVE_SUSPEND: state = DRM_MODE_DPMS_SUSPEND; break; case META_POWER_SAVE_OFF: state = DRM_MODE_DPMS_OFF; break; default: return; } kms_update = meta_kms_ensure_pending_update (kms); for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpuKms *gpu_kms = l->data; meta_gpu_kms_set_power_save_mode (gpu_kms, state, kms_update); } kms_feedback = meta_kms_post_pending_update_sync (kms); if (meta_kms_feedback_get_result (kms_feedback) != META_KMS_FEEDBACK_PASSED) { g_warning ("Failed to set DPMS: %s", meta_kms_feedback_get_error (kms_feedback)->message); } } static void meta_monitor_manager_kms_ensure_initial_config (MetaMonitorManager *manager) { MetaMonitorsConfig *config; config = meta_monitor_manager_ensure_configured (manager); meta_monitor_manager_update_logical_state (manager, config); } static void apply_crtc_assignments (MetaMonitorManager *manager, MetaCrtcInfo **crtcs, unsigned int n_crtcs, MetaOutputInfo **outputs, unsigned int n_outputs) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); unsigned i; GList *gpus; GList *l; for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; crtc->is_dirty = TRUE; if (crtc_info->mode == NULL) { meta_crtc_unset_config (crtc); } else { unsigned int j; meta_crtc_set_config (crtc, &crtc_info->layout, crtc_info->mode, crtc_info->transform); for (j = 0; j < crtc_info->outputs->len; j++) { MetaOutput *output = g_ptr_array_index (crtc_info->outputs, j); output->is_dirty = TRUE; meta_output_assign_crtc (output, crtc); } } } /* Disable CRTCs not mentioned in the list (they have is_dirty == FALSE, because they weren't seen in the first loop) */ gpus = meta_backend_get_gpus (backend); for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; GList *k; for (k = meta_gpu_get_crtcs (gpu); k; k = k->next) { MetaCrtc *crtc = k->data; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } meta_crtc_unset_config (crtc); } } for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = outputs[i]; MetaOutput *output = output_info->output; output->is_primary = output_info->is_primary; output->is_presentation = output_info->is_presentation; output->is_underscanning = output_info->is_underscanning; } /* Disable outputs not mentioned in the list */ for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; GList *k; for (k = meta_gpu_get_outputs (gpu); k; k = k->next) { MetaOutput *output = k->data; if (output->is_dirty) { output->is_dirty = FALSE; continue; } meta_output_unassign_crtc (output); output->is_primary = FALSE; } } } static void update_screen_size (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; int screen_width = 0; int screen_height = 0; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; int right_edge; int bottom_edge; right_edge = (logical_monitor_config->layout.width + logical_monitor_config->layout.x); if (right_edge > screen_width) screen_width = right_edge; bottom_edge = (logical_monitor_config->layout.height + logical_monitor_config->layout.y); if (bottom_edge > screen_height) screen_height = bottom_edge; } manager->screen_width = screen_width; manager->screen_height = screen_height; } static gboolean meta_monitor_manager_kms_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; if (!config) { if (!manager->in_init) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); meta_renderer_native_reset_modes (META_RENDERER_NATIVE (renderer)); } manager->screen_width = META_MONITOR_MANAGER_MIN_SCREEN_WIDTH; manager->screen_height = META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT; meta_monitor_manager_rebuild (manager, NULL); return TRUE; } if (!meta_monitor_config_manager_assign (manager, config, &crtc_infos, &output_infos, error)) return FALSE; if (method == META_MONITORS_CONFIG_METHOD_VERIFY) { g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); return TRUE; } apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len); g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); update_screen_size (manager, config); meta_monitor_manager_rebuild (manager, config); return TRUE; } static void meta_monitor_manager_kms_get_crtc_gamma (MetaMonitorManager *manager, MetaCrtc *crtc, gsize *size, unsigned short **red, unsigned short **green, unsigned short **blue) { MetaKmsCrtc *kms_crtc; const MetaKmsCrtcState *crtc_state; kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); crtc_state = meta_kms_crtc_get_current_state (kms_crtc); *size = crtc_state->gamma.size; *red = g_memdup2 (crtc_state->gamma.red, *size * sizeof **red); *green = g_memdup2 (crtc_state->gamma.green, *size * sizeof **green); *blue = g_memdup2 (crtc_state->gamma.blue, *size * sizeof **blue); } static char * generate_gamma_ramp_string (size_t size, unsigned short *red, unsigned short *green, unsigned short *blue) { GString *string; int color; string = g_string_new ("["); for (color = 0; color < 3; color++) { unsigned short **color_ptr; char color_char; size_t i; switch (color) { case 0: color_ptr = &red; color_char = 'r'; break; case 1: color_ptr = &green; color_char = 'g'; break; case 2: color_ptr = &blue; color_char = 'b'; break; } g_string_append_printf (string, " %c: ", color_char); for (i = 0; i < MIN (4, size); i++) { int j; if (size > 4) { if (i == 2) g_string_append (string, ",..."); if (i >= 2) j = i + (size - 4); else j = i; } else { j = i; } g_string_append_printf (string, "%s%hu", j == 0 ? "" : ",", (*color_ptr)[i]); } } g_string_append (string, " ]"); return g_string_free (string, FALSE); } static void meta_monitor_manager_kms_set_crtc_gamma (MetaMonitorManager *manager, MetaCrtc *crtc, gsize size, unsigned short *red, unsigned short *green, unsigned short *blue) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); MetaKmsCrtc *kms_crtc; g_autofree char *gamma_ramp_string = NULL; MetaKmsUpdate *kms_update; g_autoptr (MetaKmsFeedback) kms_feedback = NULL; gamma_ramp_string = generate_gamma_ramp_string (size, red, green, blue); g_debug ("Setting CRTC (%ld) gamma to %s", crtc->crtc_id, gamma_ramp_string); kms_update = meta_kms_ensure_pending_update (kms); kms_crtc = meta_crtc_kms_get_kms_crtc (crtc); meta_kms_crtc_set_gamma (kms_crtc, kms_update, size, red, green, blue); kms_feedback = meta_kms_post_pending_update_sync (kms); if (meta_kms_feedback_get_result (kms_feedback) != META_KMS_FEEDBACK_PASSED) { g_warning ("Failed to set CRTC gamma: %s", meta_kms_feedback_get_error (kms_feedback)->message); } } static void handle_hotplug_event (MetaMonitorManager *manager) { meta_monitor_manager_read_current_state (manager); meta_monitor_manager_on_hotplug (manager); } static void on_kms_resources_changed (MetaKms *kms, MetaMonitorManager *manager) { handle_hotplug_event (manager); } static void meta_monitor_manager_kms_connect_hotplug_handler (MetaMonitorManagerKms *manager_kms) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); manager_kms->kms_resources_changed_handler_id = g_signal_connect (kms, "resources-changed", G_CALLBACK (on_kms_resources_changed), manager); } static void meta_monitor_manager_kms_disconnect_hotplug_handler (MetaMonitorManagerKms *manager_kms) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); MetaKms *kms = meta_backend_native_get_kms (backend_native); g_clear_signal_handler (&manager_kms->kms_resources_changed_handler_id, kms); } void meta_monitor_manager_kms_pause (MetaMonitorManagerKms *manager_kms) { meta_monitor_manager_kms_disconnect_hotplug_handler (manager_kms); } void meta_monitor_manager_kms_resume (MetaMonitorManagerKms *manager_kms) { meta_monitor_manager_kms_connect_hotplug_handler (manager_kms); handle_hotplug_event (META_MONITOR_MANAGER (manager_kms)); } static gboolean meta_monitor_manager_kms_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { return meta_crtc_kms_is_transform_handled (crtc, transform); } static MetaMonitorScalesConstraint get_monitor_scale_constraints_per_layout_mode (MetaLogicalMonitorLayoutMode layout_mode) { MetaMonitorScalesConstraint constraints = META_MONITOR_SCALES_CONSTRAINT_NONE; switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; break; } return constraints; } static float meta_monitor_manager_kms_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { MetaMonitorScalesConstraint constraints = get_monitor_scale_constraints_per_layout_mode (layout_mode); return meta_monitor_calculate_mode_scale (monitor, monitor_mode, constraints); } static float * meta_monitor_manager_kms_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorScalesConstraint constraints = get_monitor_scale_constraints_per_layout_mode (layout_mode); return meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, n_supported_scales); } static MetaMonitorManagerCapability meta_monitor_manager_kms_get_capabilities (MetaMonitorManager *manager) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); MetaMonitorManagerCapability capabilities = META_MONITOR_MANAGER_CAPABILITY_TILING; if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; return capabilities; } static gboolean meta_monitor_manager_kms_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { return FALSE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_kms_get_default_layout_mode (MetaMonitorManager *manager) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; else return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static gboolean meta_monitor_manager_kms_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaMonitorManagerKms *manager_kms = META_MONITOR_MANAGER_KMS (initable); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_kms); MetaBackend *backend = meta_monitor_manager_get_backend (manager); gboolean can_have_outputs; GList *l; meta_monitor_manager_kms_connect_hotplug_handler (manager_kms); can_have_outputs = FALSE; for (l = meta_backend_get_gpus (backend); l; l = l->next) { MetaGpuKms *gpu_kms = l->data; if (meta_gpu_kms_can_have_outputs (gpu_kms)) { can_have_outputs = TRUE; break; } } if (!can_have_outputs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "No GPUs with outputs found"); return FALSE; } return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = meta_monitor_manager_kms_initable_init; } static void meta_monitor_manager_kms_init (MetaMonitorManagerKms *manager_kms) { } static void meta_monitor_manager_kms_class_init (MetaMonitorManagerKmsClass *klass) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); manager_class->read_edid = meta_monitor_manager_kms_read_edid; manager_class->read_current_state = meta_monitor_manager_kms_read_current_state; manager_class->ensure_initial_config = meta_monitor_manager_kms_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_kms_apply_monitors_config; manager_class->set_power_save_mode = meta_monitor_manager_kms_set_power_save_mode; manager_class->get_crtc_gamma = meta_monitor_manager_kms_get_crtc_gamma; manager_class->set_crtc_gamma = meta_monitor_manager_kms_set_crtc_gamma; manager_class->is_transform_handled = meta_monitor_manager_kms_is_transform_handled; manager_class->calculate_monitor_mode_scale = meta_monitor_manager_kms_calculate_monitor_mode_scale; manager_class->calculate_supported_scales = meta_monitor_manager_kms_calculate_supported_scales; manager_class->get_capabilities = meta_monitor_manager_kms_get_capabilities; manager_class->get_max_screen_size = meta_monitor_manager_kms_get_max_screen_size; manager_class->get_default_layout_mode = meta_monitor_manager_kms_get_default_layout_mode; } muffin-6.4.1/src/backends/native/meta-launcher.c0000664000175000017500000003777214723361714020502 0ustar fabiofabio/* * Copyright (C) 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/native/meta-launcher.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/native/dbus-utils.h" #include "backends/native/meta-backend-native.h" #include "backends/native/meta-clutter-backend-native.h" #include "backends/native/meta-cursor-renderer-native.h" #include "backends/native/meta-renderer-native.h" #include "backends/native/meta-seat-native.h" #include "clutter/clutter.h" #include "meta-dbus-login1.h" struct _MetaLauncher { Login1Session *session_proxy; Login1Seat *seat_proxy; char *seat_id; GHashTable *sysfs_fds; gboolean session_active; }; const char * meta_launcher_get_seat_id (MetaLauncher *launcher) { return launcher->seat_id; } static gboolean find_systemd_session (gchar **session_id, GError **error) { const gchar * const graphical_session_types[] = { "wayland", "x11", "mir", NULL }; const gchar * const active_states[] = { "active", "online", NULL }; g_autofree gchar *class = NULL; g_autofree gchar *local_session_id = NULL; g_autofree gchar *type = NULL; g_autofree gchar *state = NULL; g_auto (GStrv) sessions = NULL; int n_sessions; int saved_errno; g_assert (session_id != NULL); g_assert (error == NULL || *error == NULL); /* if we are in a logind session, we can trust that value, so use it. This * happens for example when you run mutter directly from a VT but when * systemd starts us we will not be in a logind session. */ saved_errno = sd_pid_get_session (0, &local_session_id); if (saved_errno < 0) { if (saved_errno != -ENODATA) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Failed to get session by pid for user %d (%s)", getuid (), g_strerror (-saved_errno)); return FALSE; } } else { *session_id = g_steal_pointer (&local_session_id); return TRUE; } saved_errno = sd_uid_get_display (getuid (), &local_session_id); if (saved_errno < 0) { /* no session, maybe there's a greeter session */ if (saved_errno == -ENODATA) { n_sessions = sd_uid_get_sessions (getuid (), 1, &sessions); if (n_sessions < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Failed to get all sessions for user %d (%m)", getuid ()); return FALSE; } if (n_sessions == 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "User %d has no sessions", getuid ()); return FALSE; } for (int i = 0; i < n_sessions; ++i) { saved_errno = sd_session_get_class (sessions[i], &class); if (saved_errno < 0) { g_warning ("Couldn't get class for session '%d': %s", i, g_strerror (-saved_errno)); continue; } if (g_strcmp0 (class, "greeter") == 0) { local_session_id = g_strdup (sessions[i]); break; } } if (!local_session_id) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't find a session or a greeter session for user %d", getuid ()); return FALSE; } } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't get display for user %d: %s", getuid (), g_strerror (-saved_errno)); return FALSE; } } /* sd_uid_get_display will return any session if there is no graphical * one, so let's check it really is graphical. */ saved_errno = sd_session_get_type (local_session_id, &type); if (saved_errno < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't get type for session '%s': %s", local_session_id, g_strerror (-saved_errno)); return FALSE; } if (!g_strv_contains (graphical_session_types, type)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Session '%s' is not a graphical session (type: '%s')", local_session_id, type); return FALSE; } /* and display sessions can be 'closing' if they are logged out but * some processes are lingering; we shouldn't consider these */ saved_errno = sd_session_get_state (local_session_id, &state); if (saved_errno < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Couldn't get state for session '%s': %s", local_session_id, g_strerror (-saved_errno)); return FALSE; } if (!g_strv_contains (active_states, state)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Session '%s' is not active", local_session_id); return FALSE; } *session_id = g_steal_pointer (&local_session_id); return TRUE; } static Login1Session * get_session_proxy (GCancellable *cancellable, GError **error) { g_autofree char *proxy_path = NULL; g_autofree char *session_id = NULL; g_autoptr (GError) local_error = NULL; Login1Session *session_proxy; if (!find_systemd_session (&session_id, &local_error)) { g_propagate_prefixed_error (error, g_steal_pointer (&local_error), "Could not get session ID: "); return NULL; } proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/session", session_id); session_proxy = login1_session_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "org.freedesktop.login1", proxy_path, cancellable, error); if (!session_proxy) g_prefix_error(error, "Could not get session proxy: "); return session_proxy; } static Login1Seat * get_seat_proxy (gchar *seat_id, GCancellable *cancellable, GError **error) { g_autofree char *seat_proxy_path = get_escaped_dbus_path ("/org/freedesktop/login1/seat", seat_id); Login1Seat *seat = login1_seat_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, "org.freedesktop.login1", seat_proxy_path, cancellable, error); if (!seat) g_prefix_error(error, "Could not get seat proxy: "); return seat; } static gboolean take_device (Login1Session *session_proxy, int dev_major, int dev_minor, int *out_fd, GCancellable *cancellable, GError **error) { g_autoptr (GVariant) fd_variant = NULL; g_autoptr (GUnixFDList) fd_list = NULL; int fd = -1; if (!login1_session_call_take_device_sync (session_proxy, dev_major, dev_minor, NULL, &fd_variant, NULL, /* paused */ &fd_list, cancellable, error)) return FALSE; fd = g_unix_fd_list_get (fd_list, g_variant_get_handle (fd_variant), error); if (fd == -1) return FALSE; *out_fd = fd; return TRUE; } static gboolean get_device_info_from_path (const char *path, int *out_major, int *out_minor) { int r; struct stat st; r = stat (path, &st); if (r < 0 || !S_ISCHR (st.st_mode)) return FALSE; *out_major = major (st.st_rdev); *out_minor = minor (st.st_rdev); return TRUE; } static gboolean get_device_info_from_fd (int fd, int *out_major, int *out_minor) { int r; struct stat st; r = fstat (fd, &st); if (r < 0 || !S_ISCHR (st.st_mode)) return FALSE; *out_major = major (st.st_rdev); *out_minor = minor (st.st_rdev); return TRUE; } int meta_launcher_open_restricted (MetaLauncher *launcher, const char *path, GError **error) { int fd; int major, minor; if (!get_device_info_from_path (path, &major, &minor)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not get device info for path %s: %m", path); return -1; } if (!take_device (launcher->session_proxy, major, minor, &fd, NULL, error)) return -1; return fd; } void meta_launcher_close_restricted (MetaLauncher *launcher, int fd) { int major, minor; GError *error = NULL; if (!get_device_info_from_fd (fd, &major, &minor)) { g_warning ("Could not get device info for fd %d: %m", fd); goto out; } if (!login1_session_call_release_device_sync (launcher->session_proxy, major, minor, NULL, &error)) { g_warning ("Could not release device (%d,%d): %s", major, minor, error->message); } out: close (fd); } static int on_evdev_device_open (const char *path, int flags, gpointer user_data, GError **error) { MetaLauncher *self = user_data; /* Allow readonly access to sysfs */ if (g_str_has_prefix (path, "/sys/")) { int fd; do { fd = open (path, flags); } while (fd < 0 && errno == EINTR); if (fd < 0) { g_set_error (error, G_FILE_ERROR, g_file_error_from_errno (errno), "Could not open /sys file: %s: %m", path); return -1; } g_hash_table_add (self->sysfs_fds, GINT_TO_POINTER (fd)); return fd; } return meta_launcher_open_restricted (self, path, error); } static void on_evdev_device_close (int fd, gpointer user_data) { MetaLauncher *self = user_data; if (g_hash_table_lookup (self->sysfs_fds, GINT_TO_POINTER (fd))) { /* /sys/ paths just need close() here */ g_hash_table_remove (self->sysfs_fds, GINT_TO_POINTER (fd)); close (fd); return; } meta_launcher_close_restricted (self, fd); } static void sync_active (MetaLauncher *self) { MetaBackend *backend = meta_get_backend (); MetaBackendNative *backend_native = META_BACKEND_NATIVE (backend); gboolean active = login1_session_get_active (LOGIN1_SESSION (self->session_proxy)); if (active == self->session_active) return; self->session_active = active; if (active) meta_backend_native_resume (backend_native); else meta_backend_native_pause (backend_native); } static void on_active_changed (Login1Session *session, GParamSpec *pspec, gpointer user_data) { MetaLauncher *self = user_data; sync_active (self); } static gchar * get_seat_id (GError **error) { g_autoptr (GError) local_error = NULL; g_autofree char *session_id = NULL; char *seat_id = NULL; int r; if (!find_systemd_session (&session_id, &local_error)) { g_propagate_prefixed_error (error, g_steal_pointer (&local_error), "Could not get session ID: "); return NULL; } r = sd_session_get_seat (session_id, &seat_id); if (r < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Could not get seat for session: %s", g_strerror (-r)); return NULL; } return seat_id; } MetaLauncher * meta_launcher_new (GError **error) { MetaLauncher *self = NULL; g_autoptr (Login1Session) session_proxy = NULL; g_autoptr (Login1Seat) seat_proxy = NULL; g_autofree char *seat_id = NULL; gboolean have_control = FALSE; session_proxy = get_session_proxy (NULL, error); if (!session_proxy) goto fail; if (!login1_session_call_take_control_sync (session_proxy, FALSE, NULL, error)) { g_prefix_error (error, "Could not take control: "); goto fail; } have_control = TRUE; seat_id = get_seat_id (error); if (!seat_id) goto fail; seat_proxy = get_seat_proxy (seat_id, NULL, error); if (!seat_proxy) goto fail; self = g_slice_new0 (MetaLauncher); self->session_proxy = g_object_ref (session_proxy); self->seat_proxy = g_object_ref (seat_proxy); self->seat_id = g_steal_pointer (&seat_id); self->sysfs_fds = g_hash_table_new (NULL, NULL); self->session_active = TRUE; meta_clutter_backend_native_set_seat_id (self->seat_id); meta_seat_native_set_device_callbacks (on_evdev_device_open, on_evdev_device_close, self); g_signal_connect (self->session_proxy, "notify::active", G_CALLBACK (on_active_changed), self); return self; fail: if (have_control) login1_session_call_release_control_sync (session_proxy, NULL, NULL); return NULL; } void meta_launcher_free (MetaLauncher *self) { g_free (self->seat_id); g_object_unref (self->seat_proxy); g_object_unref (self->session_proxy); g_hash_table_destroy (self->sysfs_fds); g_slice_free (MetaLauncher, self); } gboolean meta_launcher_activate_session (MetaLauncher *launcher, GError **error) { if (!login1_session_call_activate_sync (launcher->session_proxy, NULL, error)) return FALSE; sync_active (launcher); return TRUE; } gboolean meta_launcher_activate_vt (MetaLauncher *launcher, signed char vt, GError **error) { return login1_seat_call_switch_to_sync (launcher->seat_proxy, vt, NULL, error); } muffin-6.4.1/src/backends/meta-screen-cast-window.c0000664000175000017500000001033114723361714021105 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-window.h" G_DEFINE_INTERFACE (MetaScreenCastWindow, meta_screen_cast_window, G_TYPE_OBJECT) static void meta_screen_cast_window_default_init (MetaScreenCastWindowInterface *iface) { } void meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds) { META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->get_buffer_bounds (screen_cast_window, bounds); } void meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window, double x, double y, double *x_out, double *y_out) { META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->transform_relative_position (screen_cast_window, x, y, x_out, y_out); } gboolean meta_screen_cast_window_transform_cursor_position (MetaScreenCastWindow *screen_cast_window, MetaCursorSprite *cursor_sprite, graphene_point_t *cursor_position, float *out_cursor_scale, graphene_point_t *out_relative_cursor_position) { MetaScreenCastWindowInterface *iface = META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window); return iface->transform_cursor_position (screen_cast_window, cursor_sprite, cursor_position, out_cursor_scale, out_relative_cursor_position); } void meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, uint8_t *data) { META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window)->capture_into (screen_cast_window, bounds, data); } gboolean meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, CoglFramebuffer *framebuffer) { MetaScreenCastWindowInterface *iface = META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window); return iface->blit_to_framebuffer (screen_cast_window, bounds, framebuffer); } gboolean meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window) { MetaScreenCastWindowInterface *iface = META_SCREEN_CAST_WINDOW_GET_IFACE (screen_cast_window); return iface->has_damage (screen_cast_window); } muffin-6.4.1/src/backends/edid.h0000664000175000017500000001003014723361714015353 0ustar fabiofabio/* edid.h * * Copyright 2007, 2008, Red Hat, Inc. * * This file is part of the Gnome Library. * * The Gnome Library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * The Gnome Library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with the Gnome Library; see the file COPYING.LIB. If not, * write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. * * Author: Soren Sandmann */ #ifndef EDID_H #define EDID_H typedef unsigned char uchar; typedef struct MonitorInfo MonitorInfo; typedef struct Timing Timing; typedef struct DetailedTiming DetailedTiming; typedef enum { UNDEFINED, DVI, HDMI_A, HDMI_B, MDDI, DISPLAY_PORT } Interface; typedef enum { UNDEFINED_COLOR, MONOCHROME, RGB, OTHER_COLOR } ColorType; typedef enum { NO_STEREO, FIELD_RIGHT, FIELD_LEFT, TWO_WAY_RIGHT_ON_EVEN, TWO_WAY_LEFT_ON_EVEN, FOUR_WAY_INTERLEAVED, SIDE_BY_SIDE } StereoType; struct Timing { int width; int height; int frequency; }; struct DetailedTiming { int pixel_clock; int h_addr; int h_blank; int h_sync; int h_front_porch; int v_addr; int v_blank; int v_sync; int v_front_porch; int width_mm; int height_mm; int right_border; int top_border; int interlaced; StereoType stereo; int digital_sync; union { struct { int bipolar; int serrations; int sync_on_green; } analog; struct { int composite; int serrations; int negative_vsync; int negative_hsync; } digital; } connector; }; struct MonitorInfo { int checksum; char manufacturer_code[4]; int product_code; unsigned int serial_number; int production_week; /* -1 if not specified */ int production_year; /* -1 if not specified */ int model_year; /* -1 if not specified */ int major_version; int minor_version; int is_digital; union { struct { int bits_per_primary; Interface interface; int rgb444; int ycrcb444; int ycrcb422; } digital; struct { double video_signal_level; double sync_signal_level; double total_signal_level; int blank_to_black; int separate_hv_sync; int composite_sync_on_h; int composite_sync_on_green; int serration_on_vsync; ColorType color_type; } analog; } connector; int width_mm; /* -1 if not specified */ int height_mm; /* -1 if not specified */ double aspect_ratio; /* -1.0 if not specififed */ double gamma; /* -1.0 if not specified */ int standby; int suspend; int active_off; int srgb_is_standard; int preferred_timing_includes_native; int continuous_frequency; double red_x; double red_y; double green_x; double green_y; double blue_x; double blue_y; double white_x; double white_y; Timing established[24]; /* Terminated by 0x0x0 */ Timing standard[8]; int n_detailed_timings; DetailedTiming detailed_timings[4]; /* If monitor has a preferred * mode, it is the first one * (whether it has, is * determined by the * preferred_timing_includes * bit. */ /* Optional product description */ char dsc_serial_number[14]; char dsc_product_name[14]; char dsc_string[14]; /* Unspecified ASCII data */ }; MonitorInfo *decode_edid (const uchar *data); #endif muffin-6.4.1/src/backends/meta-logical-monitor.c0000664000175000017500000002461514723361714020502 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ /** * SECTION:meta-logical-monitor * @title: MetaLogicalMonitor * @short_description: An abstraction for a monitor(set) and its configuration. * * A logical monitor is a group of one or more physical monitors that * must behave and be treated as single one. This happens, for example, * when 2 monitors are mirrored. Each physical monitor is represented * by a #MetaMonitor. * * #MetaLogicalMonitor has a single viewport, with its owns transformations * (such as scaling), that are applied to all the #MetaMonitors that * are grouped by it. * * #MetaLogicalMonitor provides an abstraction that makes it easy to handle * the specifics of setting up different #MetaMonitors. It then can * be used more easily by #MetaRendererView. */ #include "config.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-output.h" G_DEFINE_TYPE (MetaLogicalMonitor, meta_logical_monitor, G_TYPE_OBJECT) static MetaMonitor * get_first_monitor (MetaMonitorManager *monitor_manager, GList *monitor_configs) { MetaMonitorConfig *first_monitor_config; MetaMonitorSpec *first_monitor_spec; first_monitor_config = g_list_first (monitor_configs)->data; first_monitor_spec = first_monitor_config->monitor_spec; return meta_monitor_manager_get_monitor_from_spec (monitor_manager, first_monitor_spec); } typedef struct { MetaMonitorManager *monitor_manager; MetaLogicalMonitor *logical_monitor; } AddMonitorFromConfigData; static void add_monitor_from_config (MetaMonitorConfig *monitor_config, AddMonitorFromConfigData *data) { MetaMonitorSpec *monitor_spec; MetaMonitor *monitor; monitor_spec = monitor_config->monitor_spec; monitor = meta_monitor_manager_get_monitor_from_spec (data->monitor_manager, monitor_spec); meta_logical_monitor_add_monitor (data->logical_monitor, monitor); } MetaLogicalMonitor * meta_logical_monitor_new (MetaMonitorManager *monitor_manager, MetaLogicalMonitorConfig *logical_monitor_config, int monitor_number) { MetaLogicalMonitor *logical_monitor; GList *monitor_configs; MetaMonitor *first_monitor; MetaOutput *main_output; logical_monitor = g_object_new (META_TYPE_LOGICAL_MONITOR, NULL); monitor_configs = logical_monitor_config->monitor_configs; first_monitor = get_first_monitor (monitor_manager, monitor_configs); main_output = meta_monitor_get_main_output (first_monitor); logical_monitor->number = monitor_number; logical_monitor->winsys_id = main_output->winsys_id; logical_monitor->scale = logical_monitor_config->scale; logical_monitor->transform = logical_monitor_config->transform; logical_monitor->in_fullscreen = -1; logical_monitor->rect = logical_monitor_config->layout; logical_monitor->is_presentation = TRUE; g_list_foreach (monitor_configs, (GFunc) add_monitor_from_config, &(AddMonitorFromConfigData) { .monitor_manager = monitor_manager, .logical_monitor = logical_monitor }); return logical_monitor; } static MetaMonitorTransform derive_monitor_transform (MetaMonitor *monitor) { MetaOutput *main_output; MetaMonitorTransform transform; main_output = meta_monitor_get_main_output (monitor); transform = meta_output_get_assigned_crtc (main_output)->config->transform; return meta_monitor_crtc_to_logical_transform (monitor, transform); } MetaLogicalMonitor * meta_logical_monitor_new_derived (MetaMonitorManager *monitor_manager, MetaMonitor *monitor, MetaRectangle *layout, float scale, int monitor_number) { MetaLogicalMonitor *logical_monitor; MetaOutput *main_output; MetaMonitorTransform transform; logical_monitor = g_object_new (META_TYPE_LOGICAL_MONITOR, NULL); transform = derive_monitor_transform (monitor); main_output = meta_monitor_get_main_output (monitor); logical_monitor->number = monitor_number; logical_monitor->winsys_id = main_output->winsys_id; logical_monitor->scale = scale; logical_monitor->transform = transform; logical_monitor->in_fullscreen = -1; logical_monitor->rect = *layout; logical_monitor->is_presentation = TRUE; meta_logical_monitor_add_monitor (logical_monitor, monitor); return logical_monitor; } void meta_logical_monitor_add_monitor (MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor) { GList *l; gboolean is_presentation; is_presentation = logical_monitor->is_presentation; logical_monitor->monitors = g_list_append (logical_monitor->monitors, g_object_ref (monitor)); for (l = logical_monitor->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; GList *outputs; GList *l_output; outputs = meta_monitor_get_outputs (monitor); for (l_output = outputs; l_output; l_output = l_output->next) { MetaOutput *output = l_output->data; is_presentation = is_presentation && output->is_presentation; } } logical_monitor->is_presentation = is_presentation; meta_monitor_set_logical_monitor (monitor, logical_monitor); } gboolean meta_logical_monitor_is_primary (MetaLogicalMonitor *logical_monitor) { return logical_monitor->is_primary; } void meta_logical_monitor_make_primary (MetaLogicalMonitor *logical_monitor) { logical_monitor->is_primary = TRUE; } float meta_logical_monitor_get_scale (MetaLogicalMonitor *logical_monitor) { return logical_monitor->scale; } MetaMonitorTransform meta_logical_monitor_get_transform (MetaLogicalMonitor *logical_monitor) { return logical_monitor->transform; } MetaRectangle meta_logical_monitor_get_layout (MetaLogicalMonitor *logical_monitor) { return logical_monitor->rect; } GList * meta_logical_monitor_get_monitors (MetaLogicalMonitor *logical_monitor) { return logical_monitor->monitors; } typedef struct _ForeachCrtcData { MetaLogicalMonitor *logical_monitor; MetaLogicalMonitorCrtcFunc func; gpointer user_data; } ForeachCrtcData; static gboolean foreach_crtc (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { ForeachCrtcData *data = user_data; data->func (data->logical_monitor, monitor, monitor_crtc_mode->output, meta_output_get_assigned_crtc (monitor_crtc_mode->output), data->user_data); return TRUE; } void meta_logical_monitor_foreach_crtc (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitorCrtcFunc func, gpointer user_data) { GList *l; for (l = logical_monitor->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorMode *mode; ForeachCrtcData data = { .logical_monitor = logical_monitor, .func = func, .user_data = user_data }; mode = meta_monitor_get_current_mode (monitor); meta_monitor_mode_foreach_crtc (monitor, mode, foreach_crtc, &data, NULL); } } static void meta_logical_monitor_init (MetaLogicalMonitor *logical_monitor) { } static void meta_logical_monitor_dispose (GObject *object) { MetaLogicalMonitor *logical_monitor = META_LOGICAL_MONITOR (object); if (logical_monitor->monitors) { g_list_free_full (logical_monitor->monitors, g_object_unref); logical_monitor->monitors = NULL; } G_OBJECT_CLASS (meta_logical_monitor_parent_class)->dispose (object); } static void meta_logical_monitor_class_init (MetaLogicalMonitorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_logical_monitor_dispose; } gboolean meta_logical_monitor_has_neighbor (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitor *neighbor, MetaDisplayDirection neighbor_direction) { switch (neighbor_direction) { case META_DISPLAY_RIGHT: if (neighbor->rect.x == (logical_monitor->rect.x + logical_monitor->rect.width) && meta_rectangle_vert_overlap (&neighbor->rect, &logical_monitor->rect)) return TRUE; break; case META_DISPLAY_LEFT: if (logical_monitor->rect.x == (neighbor->rect.x + neighbor->rect.width) && meta_rectangle_vert_overlap (&neighbor->rect, &logical_monitor->rect)) return TRUE; break; case META_DISPLAY_UP: if (logical_monitor->rect.y == (neighbor->rect.y + neighbor->rect.height) && meta_rectangle_horiz_overlap (&neighbor->rect, &logical_monitor->rect)) return TRUE; break; case META_DISPLAY_DOWN: if (neighbor->rect.y == (logical_monitor->rect.y + logical_monitor->rect.height) && meta_rectangle_horiz_overlap (&neighbor->rect, &logical_monitor->rect)) return TRUE; break; } return FALSE; } muffin-6.4.1/src/backends/meta-screen-cast-window-stream-src.h0000664000175000017500000000304314723361714023172 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_WINDOW_STREAM_SRC_H #define META_SCREEN_CAST_WINDOW_STREAM_SRC_H #include "backends/meta-screen-cast-stream-src.h" typedef struct _MetaScreenCastWindowStream MetaScreenCastWindowStream; #define META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC (meta_screen_cast_window_stream_src_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStreamSrc, meta_screen_cast_window_stream_src, META, SCREEN_CAST_WINDOW_STREAM_SRC, MetaScreenCastStreamSrc) MetaScreenCastWindowStreamSrc * meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stream, GError **error); #endif /* META_SCREEN_CAST_WINDOW_STREAM_SRC_H */ muffin-6.4.1/src/backends/meta-egl-ext.h0000664000175000017500000001176014723361714016752 0ustar fabiofabio/* * Copyright 2008 Tungsten Graphics, Inc., Cedar Park, Texas. * All Rights Reserved. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #ifndef META_EGL_EXT_H #define META_EGL_EXT_H #include #include #include /* * This is a little different to the tests shipped with EGL implementations, * which wrap the entire thing in #ifndef EGL_WL_bind_wayland_display, then go * on to define both BindWaylandDisplay and QueryWaylandBuffer. * * Unfortunately, some implementations (particularly the version of Mesa shipped * in Ubuntu 12.04) define EGL_WL_bind_wayland_display, but then only provide * prototypes for (Un)BindWaylandDisplay, completely omitting * QueryWaylandBuffer. * * Detect this, and provide our own definitions if necessary. */ #ifndef EGL_WAYLAND_BUFFER_WL #define EGL_WAYLAND_BUFFER_WL 0x31D5 /* eglCreateImageKHR target */ #define EGL_WAYLAND_PLANE_WL 0x31D6 /* eglCreateImageKHR target */ #define EGL_TEXTURE_Y_U_V_WL 0x31D7 #define EGL_TEXTURE_Y_UV_WL 0x31D8 #define EGL_TEXTURE_Y_XUXV_WL 0x31D9 #define EGL_TEXTURE_EXTERNAL_WL 0x31DA struct wl_resource; #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglQueryWaylandBufferWL(EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); #endif typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYWAYLANDBUFFERWL) (EGLDisplay dpy, struct wl_resource *buffer, EGLint attribute, EGLint *value); #endif /* * FIXME: Remove both EGL_EXT_stream_acquire_mode and * EGL_NV_output_drm_flip_event definitions below once both extensions * get published by Khronos and incorportated into Khronos' header files */ #ifndef EGL_EXT_stream_acquire_mode #define EGL_EXT_stream_acquire_mode 1 #define EGL_CONSUMER_AUTO_ACQUIRE_EXT 0x332B #define EGL_RESOURCE_BUSY_EXT 0x3353 typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBEXTPROC) (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireAttribEXT (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); #endif #endif /* EGL_EXT_stream_acquire_mode */ #ifndef EGL_NV_output_drm_flip_event #define EGL_NV_output_drm_flip_event 1 #define EGL_DRM_FLIP_EVENT_DATA_NV 0x333E #endif /* EGL_NV_output_drm_flip_event */ #ifndef EGL_NV_stream_attrib #define EGL_NV_stream_attrib 1 #ifdef EGL_EGLEXT_PROTOTYPES EGLAPI EGLStreamKHR EGLAPIENTRY eglCreateStreamAttribNV(EGLDisplay dpy, const EGLAttrib *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglSetStreamAttribNV(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); EGLAPI EGLBoolean EGLAPIENTRY eglQueryStreamAttribNV(EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerAcquireAttribNV(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); EGLAPI EGLBoolean EGLAPIENTRY eglStreamConsumerReleaseAttribNV(EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); #endif typedef EGLStreamKHR (EGLAPIENTRYP PFNEGLCREATESTREAMATTRIBNVPROC) (EGLDisplay dpy, const EGLAttrib *attrib_list); typedef EGLBoolean (EGLAPIENTRYP PFNEGLSETSTREAMATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib value); typedef EGLBoolean (EGLAPIENTRYP PFNEGLQUERYSTREAMATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, EGLenum attribute, EGLAttrib *value); typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERACQUIREATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); typedef EGLBoolean (EGLAPIENTRYP PFNEGLSTREAMCONSUMERRELEASEATTRIBNVPROC) (EGLDisplay dpy, EGLStreamKHR stream, const EGLAttrib *attrib_list); #endif /* EGL_NV_stream_attrib */ #ifndef EGL_WL_wayland_eglstream #define EGL_WL_wayland_eglstream 1 #define EGL_WAYLAND_EGLSTREAM_WL 0x334B #endif /* EGL_WL_wayland_eglstream */ #endif /* META_EGL_EXT_H */ muffin-6.4.1/src/backends/meta-monitor-manager-dummy.c0000664000175000017500000005772514723361714021643 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-monitor-manager-dummy.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-output.h" #include "meta/util.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) #define MAX_MONITORS 5 #define MAX_OUTPUTS (MAX_MONITORS * 2) #define MAX_CRTCS (MAX_MONITORS * 2) #define MAX_MODES (MAX_MONITORS * 4) struct _MetaMonitorManagerDummy { MetaMonitorManager parent_instance; gboolean is_transform_handled; }; struct _MetaMonitorManagerDummyClass { MetaMonitorManagerClass parent_class; }; typedef struct _MetaOutputDummy { float scale; } MetaOutputDummy; G_DEFINE_TYPE (MetaMonitorManagerDummy, meta_monitor_manager_dummy, META_TYPE_MONITOR_MANAGER); struct _MetaGpuDummy { MetaGpu parent; }; G_DEFINE_TYPE (MetaGpuDummy, meta_gpu_dummy, META_TYPE_GPU) static void meta_output_dummy_notify_destroy (MetaOutput *output); typedef struct _CrtcModeSpec { int width; int height; float refresh_rate; } CrtcModeSpec; G_DEFINE_AUTOPTR_CLEANUP_FUNC(CrtcModeSpec, g_free); static MetaCrtcMode * create_mode (CrtcModeSpec *spec, long mode_id) { MetaCrtcMode *mode; mode = g_object_new (META_TYPE_CRTC_MODE, NULL); mode->mode_id = mode_id; mode->width = spec->width; mode->height = spec->height; mode->refresh_rate = spec->refresh_rate; return mode; } static MetaGpu * get_gpu (MetaMonitorManager *manager) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); return META_GPU (meta_backend_get_gpus (backend)->data); } static void append_monitor (MetaMonitorManager *manager, GList **modes, GList **crtcs, GList **outputs, float scale) { MetaGpu *gpu = get_gpu (manager); CrtcModeSpec default_specs[] = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 }, { .width = 1440, .height = 900, .refresh_rate = 60.0 }, { .width = 1600, .height = 920, .refresh_rate = 60.0 }, }; g_autolist (CrtcModeSpec) mode_specs = NULL; unsigned int n_mode_specs = 0; GList *new_modes = NULL; MetaCrtc *crtc; MetaOutputDummy *output_dummy; MetaOutput *output; unsigned int i; unsigned int number; const char *mode_specs_str; GList *l; mode_specs_str = getenv ("MUTTER_DEBUG_DUMMY_MODE_SPECS"); if (mode_specs_str && *mode_specs_str != '\0') { g_auto (GStrv) specs = g_strsplit (mode_specs_str, ":", -1); for (i = 0; specs[i]; ++i) { int width, height; float refresh_rate = 60.0; if (sscanf (specs[i], "%dx%d@%f", &width, &height, &refresh_rate) == 3 || sscanf (specs[i], "%dx%d", &width, &height) == 2) { CrtcModeSpec *spec; if (width < META_MONITOR_MANAGER_MIN_SCREEN_WIDTH || height < META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT) continue; spec = g_new0 (CrtcModeSpec, 1); spec->width = width; spec->height = height; spec->refresh_rate = refresh_rate; mode_specs = g_list_prepend (mode_specs, spec); } } } else { for (i = 0; i < G_N_ELEMENTS (default_specs); i++) { CrtcModeSpec *spec; spec = g_memdup2 (&default_specs[i], sizeof (CrtcModeSpec)); mode_specs = g_list_prepend (mode_specs, spec); } } for (l = mode_specs; l; l = l->next) { CrtcModeSpec *spec = l->data; long mode_id; MetaCrtcMode *mode; mode_id = g_list_length (*modes) + n_mode_specs + 1; mode = create_mode (spec, mode_id); new_modes = g_list_append (new_modes, mode); n_mode_specs++; } *modes = g_list_concat (*modes, new_modes); crtc = g_object_new (META_TYPE_CRTC, NULL); crtc->crtc_id = g_list_length (*crtcs) + 1; crtc->all_transforms = ALL_TRANSFORMS; *crtcs = g_list_append (*crtcs, crtc); output = g_object_new (META_TYPE_OUTPUT, NULL); output_dummy = g_new0 (MetaOutputDummy, 1); *output_dummy = (MetaOutputDummy) { .scale = scale }; number = g_list_length (*outputs) + 1; output->gpu = gpu; output->winsys_id = number; output->name = g_strdup_printf ("LVDS%d", number); output->vendor = g_strdup ("MetaProducts Inc."); output->product = g_strdup ("MetaMonitor"); output->serial = g_strdup_printf ("0xC0FFEE-%d", number); output->suggested_x = -1; output->suggested_y = -1; output->width_mm = 222; output->height_mm = 125; output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; output->preferred_mode = g_list_last (*modes)->data; output->n_possible_clones = 0; output->backlight = -1; output->connector_type = META_CONNECTOR_TYPE_LVDS; output->driver_private = output_dummy; output->driver_notify = (GDestroyNotify) meta_output_dummy_notify_destroy; output->modes = g_new0 (MetaCrtcMode *, n_mode_specs); for (l = new_modes, i = 0; l; l = l->next, i++) { MetaCrtcMode *mode = l->data; output->modes[i] = mode; } output->n_modes = n_mode_specs; output->possible_crtcs = g_new0 (MetaCrtc *, 1); output->possible_crtcs[0] = g_list_last (*crtcs)->data; output->n_possible_crtcs = 1; *outputs = g_list_append (*outputs, output); } static void append_tiled_monitor (MetaMonitorManager *manager, GList **modes, GList **crtcs, GList **outputs, int scale) { MetaGpu *gpu = get_gpu (manager); CrtcModeSpec mode_specs[] = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 512, .height = 768, .refresh_rate = 60.0 } }; unsigned int n_tiles = 2; GList *new_modes = NULL; GList *new_crtcs = NULL; MetaOutput *output; unsigned int i; uint32_t tile_group_id; for (i = 0; i < G_N_ELEMENTS (mode_specs); i++) { long mode_id; MetaCrtcMode *mode; mode_id = g_list_length (*modes) + i + 1; mode = create_mode (&mode_specs[i], mode_id); new_modes = g_list_append (new_modes, mode); } *modes = g_list_concat (*modes, new_modes); for (i = 0; i < n_tiles; i++) { MetaCrtc *crtc; crtc = g_object_new (META_TYPE_CRTC, NULL); crtc->gpu = gpu; crtc->crtc_id = g_list_length (*crtcs) + i + 1; crtc->all_transforms = ALL_TRANSFORMS; new_crtcs = g_list_append (new_crtcs, crtc); } *crtcs = g_list_concat (*crtcs, new_crtcs); tile_group_id = g_list_length (*outputs) + 1; for (i = 0; i < n_tiles; i++) { MetaOutputDummy *output_dummy; MetaCrtcMode *preferred_mode; unsigned int j; unsigned int number; GList *l; output_dummy = g_new0 (MetaOutputDummy, 1); *output_dummy = (MetaOutputDummy) { .scale = scale }; /* Arbitrary ID unique for this output */ number = g_list_length (*outputs) + 1; preferred_mode = g_list_last (*modes)->data; output = g_object_new (META_TYPE_OUTPUT, NULL); output->gpu = gpu; output->winsys_id = number; output->name = g_strdup_printf ("LVDS%d", number); output->vendor = g_strdup ("MetaProducts Inc."); output->product = g_strdup ("MetaMonitor"); output->serial = g_strdup_printf ("0xC0FFEE-%d", number); output->suggested_x = -1; output->suggested_y = -1; output->width_mm = 222; output->height_mm = 125; output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; output->preferred_mode = preferred_mode; output->n_possible_clones = 0; output->backlight = -1; output->connector_type = META_CONNECTOR_TYPE_LVDS; output->tile_info = (MetaTileInfo) { .group_id = tile_group_id, .max_h_tiles = n_tiles, .max_v_tiles = 1, .loc_h_tile = i, .loc_v_tile = 0, .tile_w = preferred_mode->width, .tile_h = preferred_mode->height }, output->driver_private = output_dummy; output->driver_notify = (GDestroyNotify) meta_output_dummy_notify_destroy; output->modes = g_new0 (MetaCrtcMode *, G_N_ELEMENTS (mode_specs)); for (l = new_modes, j = 0; l; l = l->next, j++) { MetaCrtcMode *mode = l->data; output->modes[j] = mode; } output->n_modes = G_N_ELEMENTS (mode_specs); output->possible_crtcs = g_new0 (MetaCrtc *, n_tiles); for (l = new_crtcs, j = 0; l; l = l->next, j++) { MetaCrtc *crtc = l->data; output->possible_crtcs[j] = crtc; } output->n_possible_crtcs = n_tiles; *outputs = g_list_append (*outputs, output); } } static void meta_output_dummy_notify_destroy (MetaOutput *output) { g_clear_pointer (&output->driver_private, g_free); } static gboolean has_tiled_monitors (void) { const char *tiled_monitors_str; tiled_monitors_str = g_getenv ("MUTTER_DEBUG_TILED_DUMMY_MONITORS"); return g_strcmp0 (tiled_monitors_str, "1") == 0; } static void meta_monitor_manager_dummy_read_current (MetaMonitorManager *manager) { MetaGpu *gpu = get_gpu (manager); unsigned int num_monitors = 1; float *monitor_scales = NULL; const char *num_monitors_str; const char *monitor_scales_str; gboolean tiled_monitors; unsigned int i; GList *outputs; GList *crtcs; GList *modes; /* To control what monitor configuration is generated, there are two available * environmental variables that can be used: * * MUTTER_DEBUG_NUM_DUMMY_MONITORS * * Specifies the number of dummy monitors to include in the stage. Every * monitor is 1024x786 pixels and they are placed on a horizontal row. * * MUTTER_DEBUG_DUMMY_MODE_SPECS * * A colon separated list of mode specifications that can be used to * configure the monitor via dbus API. Setting this environment variable * overrides the default set of modes available. * Format should be WWxHH:WWxHH@RR * * MUTTER_DEBUG_DUMMY_MONITOR_SCALES * * A comma separated list that specifies the scales of the dummy monitors. * * MUTTER_DEBUG_TILED_DUMMY_MONITORS * * If set to "1" the dummy monitors will emulate being tiled, i.e. each have a * unique tile group id, made up of multiple outputs and CRTCs. * * For example the following configuration results in two monitors, where the * first one has the monitor scale 1, and the other the monitor scale 2. * * MUTTER_DEBUG_NUM_DUMMY_MONITORS=2 * MUTTER_DEBUG_DUMMY_MONITOR_SCALES=1,2 * MUTTER_DEBUG_TILED_DUMMY_MONITORS=1 */ num_monitors_str = getenv ("MUTTER_DEBUG_NUM_DUMMY_MONITORS"); if (num_monitors_str) { num_monitors = g_ascii_strtoll (num_monitors_str, NULL, 10); if (num_monitors <= 0) { meta_warning ("Invalid number of dummy monitors"); num_monitors = 1; } if (num_monitors > MAX_MONITORS) { meta_warning ("Clamping monitor count to max (%d)", MAX_MONITORS); num_monitors = MAX_MONITORS; } } monitor_scales = g_newa (typeof (*monitor_scales), num_monitors); for (i = 0; i < num_monitors; i++) monitor_scales[i] = 1.0; monitor_scales_str = getenv ("MUTTER_DEBUG_DUMMY_MONITOR_SCALES"); if (monitor_scales_str) { gchar **scales_str_list; scales_str_list = g_strsplit (monitor_scales_str, ",", -1); if (g_strv_length (scales_str_list) != num_monitors) meta_warning ("Number of specified monitor scales differ from number " "of monitors (defaults to 1).\n"); for (i = 0; i < num_monitors && scales_str_list[i]; i++) { float scale = g_ascii_strtod (scales_str_list[i], NULL); monitor_scales[i] = scale; } g_strfreev (scales_str_list); } tiled_monitors = has_tiled_monitors (); modes = NULL; crtcs = NULL; outputs = NULL; for (i = 0; i < num_monitors; i++) { if (tiled_monitors) append_tiled_monitor (manager, &modes, &crtcs, &outputs, monitor_scales[i]); else append_monitor (manager, &modes, &crtcs, &outputs, monitor_scales[i]); } meta_gpu_take_modes (gpu, modes); meta_gpu_take_crtcs (gpu, crtcs); meta_gpu_take_outputs (gpu, outputs); } static void meta_monitor_manager_dummy_ensure_initial_config (MetaMonitorManager *manager) { MetaMonitorsConfig *config; config = meta_monitor_manager_ensure_configured (manager); if (meta_is_stage_views_enabled ()) meta_monitor_manager_update_logical_state (manager, config); else meta_monitor_manager_update_logical_state_derived (manager, NULL); } static void apply_crtc_assignments (MetaMonitorManager *manager, MetaCrtcInfo **crtcs, unsigned int n_crtcs, MetaOutputInfo **outputs, unsigned int n_outputs) { GList *l; unsigned i; for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; crtc->is_dirty = TRUE; if (crtc_info->mode == NULL) { meta_crtc_unset_config (crtc); } else { MetaOutput *output; unsigned int j; meta_crtc_set_config (crtc, &crtc_info->layout, crtc_info->mode, crtc_info->transform); for (j = 0; j < crtc_info->outputs->len; j++) { output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; meta_output_assign_crtc (output, crtc); } } } for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = outputs[i]; MetaOutput *output = output_info->output; output->is_primary = output_info->is_primary; output->is_presentation = output_info->is_presentation; } /* Disable CRTCs not mentioned in the list */ for (l = meta_gpu_get_crtcs (get_gpu (manager)); l; l = l->next) { MetaCrtc *crtc = l->data; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } meta_crtc_unset_config (crtc); } /* Disable outputs not mentioned in the list */ for (l = meta_gpu_get_outputs (get_gpu (manager)); l; l = l->next) { MetaOutput *output = l->data; if (output->is_dirty) { output->is_dirty = FALSE; continue; } meta_output_unassign_crtc (output); output->is_primary = FALSE; } } static void update_screen_size (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; int screen_width = 0; int screen_height = 0; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; int right_edge; int bottom_edge; right_edge = (logical_monitor_config->layout.width + logical_monitor_config->layout.x); if (right_edge > screen_width) screen_width = right_edge; bottom_edge = (logical_monitor_config->layout.height + logical_monitor_config->layout.y); if (bottom_edge > screen_height) screen_height = bottom_edge; } manager->screen_width = screen_width; manager->screen_height = screen_height; } static gboolean meta_monitor_manager_dummy_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; if (!config) { manager->screen_width = META_MONITOR_MANAGER_MIN_SCREEN_WIDTH; manager->screen_height = META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT; meta_monitor_manager_rebuild (manager, NULL); return TRUE; } if (!meta_monitor_config_manager_assign (manager, config, &crtc_infos, &output_infos, error)) return FALSE; if (method == META_MONITORS_CONFIG_METHOD_VERIFY) { g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); return TRUE; } apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len); g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); update_screen_size (manager, config); meta_monitor_manager_rebuild (manager, config); return TRUE; } static gboolean meta_monitor_manager_dummy_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { MetaMonitorManagerDummy *manager_dummy = META_MONITOR_MANAGER_DUMMY (manager); return manager_dummy->is_transform_handled; } static float meta_monitor_manager_dummy_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { MetaOutput *output; MetaOutputDummy *output_dummy; output = meta_monitor_get_main_output (monitor); output_dummy = output->driver_private; return output_dummy->scale; } static float * meta_monitor_manager_dummy_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorScalesConstraint constraints = META_MONITOR_SCALES_CONSTRAINT_NONE; switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; break; } return meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, n_supported_scales); } static gboolean is_monitor_framebuffers_scaled (void) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); return meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); } static MetaMonitorManagerCapability meta_monitor_manager_dummy_get_capabilities (MetaMonitorManager *manager) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); MetaMonitorManagerCapability capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; if (has_tiled_monitors ()) capabilities |= META_MONITOR_MANAGER_CAPABILITY_TILING; if (meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER)) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; return capabilities; } static gboolean meta_monitor_manager_dummy_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { if (meta_is_stage_views_enabled ()) return FALSE; *max_width = 65535; *max_height = 65535; return TRUE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_dummy_get_default_layout_mode (MetaMonitorManager *manager) { if (!meta_is_stage_views_enabled ()) return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; if (is_monitor_framebuffers_scaled ()) return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; else return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static void meta_monitor_manager_dummy_constructed (GObject *object) { MetaMonitorManagerDummy *manager_dummy = META_MONITOR_MANAGER_DUMMY (object); const char *nested_offscreen_transform; GObjectClass *parent_object_class = G_OBJECT_CLASS (meta_monitor_manager_dummy_parent_class); parent_object_class->constructed (object); nested_offscreen_transform = g_getenv ("MUTTER_DEBUG_NESTED_OFFSCREEN_TRANSFORM"); if (g_strcmp0 (nested_offscreen_transform, "1") == 0) manager_dummy->is_transform_handled = FALSE; else manager_dummy->is_transform_handled = TRUE; } static void meta_monitor_manager_dummy_class_init (MetaMonitorManagerDummyClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); object_class->constructed = meta_monitor_manager_dummy_constructed; manager_class->ensure_initial_config = meta_monitor_manager_dummy_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_dummy_apply_monitors_config; manager_class->is_transform_handled = meta_monitor_manager_dummy_is_transform_handled; manager_class->calculate_monitor_mode_scale = meta_monitor_manager_dummy_calculate_monitor_mode_scale; manager_class->calculate_supported_scales = meta_monitor_manager_dummy_calculate_supported_scales; manager_class->get_capabilities = meta_monitor_manager_dummy_get_capabilities; manager_class->get_max_screen_size = meta_monitor_manager_dummy_get_max_screen_size; manager_class->get_default_layout_mode = meta_monitor_manager_dummy_get_default_layout_mode; } static void meta_monitor_manager_dummy_init (MetaMonitorManagerDummy *manager_dummy) { } static gboolean meta_gpu_dummy_read_current (MetaGpu *gpu, GError **error) { MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *manager = meta_backend_get_monitor_manager (backend); meta_monitor_manager_dummy_read_current (manager); return TRUE; } static void meta_gpu_dummy_init (MetaGpuDummy *gpu_dummy) { } static void meta_gpu_dummy_class_init (MetaGpuDummyClass *klass) { MetaGpuClass *gpu_class = META_GPU_CLASS (klass); gpu_class->read_current = meta_gpu_dummy_read_current; } muffin-6.4.1/src/backends/meta-input-settings.c0000664000175000017500000027273114723361714020404 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ /** * SECTION:input-settings * @title: MetaInputSettings * @short_description: Mutter input device configuration */ #include "config.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-input-device-private.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-input-mapper-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "core/display-private.h" #include "meta/util.h" static GQuark quark_tool_settings = 0; typedef struct _MetaInputSettingsPrivate MetaInputSettingsPrivate; typedef struct _DeviceMappingInfo DeviceMappingInfo; typedef struct _CurrentToolInfo CurrentToolInfo; struct _CurrentToolInfo { MetaInputSettings *input_settings; ClutterInputDevice *device; ClutterInputDeviceTool *tool; GSettings *settings; gulong changed_id; }; struct _DeviceMappingInfo { MetaInputSettings *input_settings; ClutterInputDevice *device; GSettings *settings; gulong changed_id; guint *group_modes; }; struct _MetaInputSettingsPrivate { ClutterSeat *seat; MetaMonitorManager *monitor_manager; gulong monitors_changed_id; GSettings *mouse_settings; GSettings *touchpad_settings; GSettings *trackball_settings; GSettings *keyboard_settings; GSettings *keyboard_a11y_settings; GSettings *mouse_a11y_settings; GHashTable *mappable_devices; GHashTable *current_tools; ClutterVirtualInputDevice *virtual_pad_keyboard; GHashTable *two_finger_devices; /* Pad ring/strip emission */ struct { ClutterInputDevice *pad; MetaPadActionType action; guint number; gdouble value; } last_pad_action_info; /* For absolute devices with no mapping in settings */ MetaInputMapper *input_mapper; }; typedef gboolean (* ConfigBoolMappingFunc) (MetaInputSettings *input_settings, ClutterInputDevice *device, gboolean value); typedef void (*ConfigBoolFunc) (MetaInputSettings *input_settings, ClutterInputDevice *device, gboolean setting); typedef void (*ConfigDoubleFunc) (MetaInputSettings *input_settings, ClutterInputDevice *device, gdouble value); typedef void (*ConfigUintFunc) (MetaInputSettings *input_settings, ClutterInputDevice *device, guint value); typedef enum { META_PAD_DIRECTION_NONE = -1, META_PAD_DIRECTION_UP = 0, META_PAD_DIRECTION_DOWN, META_PAD_DIRECTION_CW, META_PAD_DIRECTION_CCW, } MetaPadDirection; G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettings, meta_input_settings, G_TYPE_OBJECT) static GSList * meta_input_settings_get_devices (MetaInputSettings *settings, ClutterInputDeviceType type) { MetaInputSettingsPrivate *priv; GList *l, *devices; GSList *list = NULL; priv = meta_input_settings_get_instance_private (settings); devices = clutter_seat_list_devices (priv->seat); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; if (clutter_input_device_get_device_type (device) == type && clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER) list = g_slist_prepend (list, device); } g_list_free (devices); return list; } static void meta_input_settings_dispose (GObject *object) { MetaInputSettings *settings = META_INPUT_SETTINGS (object); MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (settings); g_clear_object (&priv->virtual_pad_keyboard); g_clear_object (&priv->mouse_settings); g_clear_object (&priv->touchpad_settings); g_clear_object (&priv->trackball_settings); g_clear_object (&priv->keyboard_settings); g_clear_object (&priv->keyboard_a11y_settings); g_clear_object (&priv->mouse_a11y_settings); g_clear_object (&priv->input_mapper); g_clear_pointer (&priv->mappable_devices, g_hash_table_unref); g_clear_pointer (&priv->current_tools, g_hash_table_unref); if (priv->monitor_manager) g_clear_signal_handler (&priv->monitors_changed_id, priv->monitor_manager); g_clear_object (&priv->monitor_manager); g_clear_pointer (&priv->two_finger_devices, g_hash_table_destroy); G_OBJECT_CLASS (meta_input_settings_parent_class)->dispose (object); } static void settings_device_set_bool_setting (MetaInputSettings *input_settings, ClutterInputDevice *device, ConfigBoolFunc func, gboolean enabled) { func (input_settings, device, enabled); } static void settings_set_bool_setting (MetaInputSettings *input_settings, ClutterInputDeviceType type, ConfigBoolMappingFunc mapping_func, ConfigBoolFunc func, gboolean enabled) { GSList *devices, *l; devices = meta_input_settings_get_devices (input_settings, type); for (l = devices; l; l = l->next) { gboolean value = enabled; if (mapping_func) value = mapping_func (input_settings, l->data, value); settings_device_set_bool_setting (input_settings, l->data, func, value); } g_slist_free (devices); } static void settings_device_set_double_setting (MetaInputSettings *input_settings, ClutterInputDevice *device, ConfigDoubleFunc func, gdouble value) { func (input_settings, device, value); } static void settings_set_double_setting (MetaInputSettings *input_settings, ClutterInputDeviceType type, ConfigDoubleFunc func, gdouble value) { GSList *devices, *d; devices = meta_input_settings_get_devices (input_settings, type); for (d = devices; d; d = d->next) settings_device_set_double_setting (input_settings, d->data, func, value); g_slist_free (devices); } static void settings_device_set_uint_setting (MetaInputSettings *input_settings, ClutterInputDevice *device, ConfigUintFunc func, guint value) { (func) (input_settings, device, value); } static void settings_set_uint_setting (MetaInputSettings *input_settings, ClutterInputDeviceType type, ConfigUintFunc func, guint value) { GSList *devices, *d; devices = meta_input_settings_get_devices (input_settings, type); for (d = devices; d; d = d->next) settings_device_set_uint_setting (input_settings, d->data, func, value); g_slist_free (devices); } static void update_touchpad_left_handed (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; CDesktopTouchpadHandedness handedness; MetaInputSettingsPrivate *priv; gboolean enabled = FALSE; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); handedness = g_settings_get_enum (priv->touchpad_settings, "left-handed"); switch (handedness) { case C_DESKTOP_TOUCHPAD_HANDEDNESS_RIGHT: enabled = FALSE; break; case C_DESKTOP_TOUCHPAD_HANDEDNESS_LEFT: enabled = TRUE; break; case C_DESKTOP_TOUCHPAD_HANDEDNESS_MOUSE: enabled = g_settings_get_boolean (priv->mouse_settings, "left-handed"); break; default: g_assert_not_reached (); } if (device) { settings_device_set_bool_setting (input_settings, device, input_settings_class->set_left_handed, enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, NULL, input_settings_class->set_left_handed, enabled); } } static void update_mouse_left_handed (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; gboolean enabled; if (device && clutter_input_device_get_device_type (device) != CLUTTER_POINTER_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); enabled = g_settings_get_boolean (priv->mouse_settings, "left-handed"); if (device) { settings_device_set_bool_setting (input_settings, device, input_settings_class->set_left_handed, enabled); } else { CDesktopTouchpadHandedness touchpad_handedness; settings_set_bool_setting (input_settings, CLUTTER_POINTER_DEVICE, NULL, input_settings_class->set_left_handed, enabled); touchpad_handedness = g_settings_get_enum (priv->touchpad_settings, "left-handed"); /* Also update touchpads if they're following mouse settings */ if (touchpad_handedness == C_DESKTOP_TOUCHPAD_HANDEDNESS_MOUSE) update_touchpad_left_handed (input_settings, NULL); } } static void do_update_pointer_accel_profile (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); MetaInputSettingsClass *input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); if (settings == priv->mouse_settings) input_settings_class->set_mouse_accel_profile (input_settings, device, profile); else if (settings == priv->trackball_settings) input_settings_class->set_trackball_accel_profile (input_settings, device, profile); } static void update_pointer_accel_profile (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { CDesktopPointerAccelProfile profile; profile = g_settings_get_enum (settings, "accel-profile"); if (device) { do_update_pointer_accel_profile (input_settings, settings, device, profile); } else { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); GList *l, *devices; devices = clutter_seat_list_devices (priv->seat); for (l = devices; l; l = l->next) { device = l->data; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) continue; do_update_pointer_accel_profile (input_settings, settings, device, profile); } g_list_free (devices); } } static GSettings * get_settings_for_device_type (MetaInputSettings *input_settings, ClutterInputDeviceType type) { MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (input_settings); switch (type) { case CLUTTER_POINTER_DEVICE: return priv->mouse_settings; case CLUTTER_TOUCHPAD_DEVICE: return priv->touchpad_settings; default: return NULL; } } static void update_middle_click_emulation (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { ConfigBoolFunc func; const gchar *key = "middle-click-emulation"; MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); if (!settings) return; if (settings == priv->mouse_settings) func = META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_mouse_middle_click_emulation; else if (settings == priv->touchpad_settings) func = META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_touchpad_middle_click_emulation; else if (settings == priv->trackball_settings) func = META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_trackball_middle_click_emulation; else return; if (device) { settings_device_set_bool_setting (input_settings, device, func, g_settings_get_boolean (settings, key)); } else { settings_set_bool_setting (input_settings, CLUTTER_POINTER_DEVICE, NULL, func, g_settings_get_boolean (settings, key)); } } static void update_device_speed (MetaInputSettings *input_settings, ClutterInputDevice *device) { GSettings *settings; ConfigDoubleFunc func; const gchar *key = "speed"; func = META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_speed; if (device) { settings = get_settings_for_device_type (input_settings, clutter_input_device_get_device_type (device)); if (!settings) return; settings_device_set_double_setting (input_settings, device, func, g_settings_get_double (settings, key)); } else { settings = get_settings_for_device_type (input_settings, CLUTTER_POINTER_DEVICE); settings_set_double_setting (input_settings, CLUTTER_POINTER_DEVICE, func, g_settings_get_double (settings, key)); settings = get_settings_for_device_type (input_settings, CLUTTER_TOUCHPAD_DEVICE); settings_set_double_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, func, g_settings_get_double (settings, key)); } } static void update_device_natural_scroll (MetaInputSettings *input_settings, ClutterInputDevice *device) { GSettings *settings; ConfigBoolFunc func; const gchar *key = "natural-scroll"; func = META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_invert_scroll; if (device) { settings = get_settings_for_device_type (input_settings, clutter_input_device_get_device_type (device)); if (!settings) return; settings_device_set_bool_setting (input_settings, device, func, g_settings_get_boolean (settings, key)); } else { settings = get_settings_for_device_type (input_settings, CLUTTER_POINTER_DEVICE); settings_set_bool_setting (input_settings, CLUTTER_POINTER_DEVICE, NULL, func, g_settings_get_boolean (settings, key)); settings = get_settings_for_device_type (input_settings, CLUTTER_TOUCHPAD_DEVICE); settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, NULL, func, g_settings_get_boolean (settings, key)); } } static void update_touchpad_disable_while_typing (MetaInputSettings *input_settings, ClutterInputDevice *device) { GSettings *settings; MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; gboolean enabled; const gchar *key = "disable-while-typing"; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); enabled = g_settings_get_boolean (priv->touchpad_settings, key); if (device) { settings = get_settings_for_device_type (input_settings, clutter_input_device_get_device_type (device)); if (!settings) return; settings_device_set_bool_setting (input_settings, device, input_settings_class->set_disable_while_typing, enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, NULL, input_settings_class->set_disable_while_typing, enabled); } } static gboolean device_is_tablet_touchpad (MetaInputSettings *input_settings, ClutterInputDevice *device) { #ifdef HAVE_LIBWACOM WacomIntegrationFlags flags = 0; WacomDevice *wacom_device; if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return FALSE; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); if (wacom_device) { flags = libwacom_get_integration_flags (wacom_device); if ((flags & (WACOM_DEVICE_INTEGRATED_SYSTEM | WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) return TRUE; } #endif return FALSE; } static gboolean force_enable_on_tablet (MetaInputSettings *input_settings, ClutterInputDevice *device, gboolean value) { return device_is_tablet_touchpad (input_settings, device) || value; } static void update_touchpad_tap_enabled (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; gboolean enabled; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); enabled = g_settings_get_boolean (priv->touchpad_settings, "tap-to-click"); if (device) { enabled = force_enable_on_tablet (input_settings, device, enabled); settings_device_set_bool_setting (input_settings, device, input_settings_class->set_tap_enabled, enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, force_enable_on_tablet, input_settings_class->set_tap_enabled, enabled); } } static void update_touchpad_tap_and_drag_enabled (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; gboolean enabled; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); enabled = g_settings_get_boolean (priv->touchpad_settings, "tap-and-drag"); if (device) { enabled = force_enable_on_tablet (input_settings, device, enabled); settings_device_set_bool_setting (input_settings, device, input_settings_class->set_tap_and_drag_enabled, enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, force_enable_on_tablet, input_settings_class->set_tap_and_drag_enabled, enabled); } } static void update_touchpad_edge_scroll (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; gboolean edge_scroll_enabled; gboolean two_finger_scroll_enabled; gboolean two_finger_scroll_available; MetaInputSettingsPrivate *priv; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); edge_scroll_enabled = g_settings_get_boolean (priv->touchpad_settings, "edge-scrolling-enabled"); two_finger_scroll_enabled = g_settings_get_boolean (priv->touchpad_settings, "two-finger-scrolling-enabled"); two_finger_scroll_available = g_hash_table_size (priv->two_finger_devices) > 0; /* If both are enabled we prefer two finger. */ if (edge_scroll_enabled && two_finger_scroll_enabled && two_finger_scroll_available) edge_scroll_enabled = FALSE; if (device) { settings_device_set_bool_setting (input_settings, device, input_settings_class->set_edge_scroll, edge_scroll_enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, NULL, (ConfigBoolFunc) input_settings_class->set_edge_scroll, edge_scroll_enabled); } } static void update_touchpad_two_finger_scroll (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; gboolean two_finger_scroll_enabled; MetaInputSettingsPrivate *priv; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); two_finger_scroll_enabled = g_settings_get_boolean (priv->touchpad_settings, "two-finger-scrolling-enabled"); /* Disable edge since they can't both be set. */ if (two_finger_scroll_enabled) update_touchpad_edge_scroll (input_settings, device); if (device) { settings_device_set_bool_setting (input_settings, device, input_settings_class->set_two_finger_scroll, two_finger_scroll_enabled); } else { settings_set_bool_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, NULL, (ConfigBoolFunc) input_settings_class->set_two_finger_scroll, two_finger_scroll_enabled); } /* Edge might have been disabled because two finger was on. */ if (!two_finger_scroll_enabled) update_touchpad_edge_scroll (input_settings, device); } static void update_touchpad_click_method (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; CDesktopTouchpadClickMethod method; MetaInputSettingsPrivate *priv; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); method = g_settings_get_enum (priv->touchpad_settings, "click-method"); if (device) { settings_device_set_uint_setting (input_settings, device, input_settings_class->set_click_method, method); } else { settings_set_uint_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, (ConfigUintFunc) input_settings_class->set_click_method, method); } } static void update_touchpad_send_events (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; CDesktopDeviceSendEvents mode; if (device && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); mode = g_settings_get_enum (priv->touchpad_settings, "send-events"); if (device) { settings_device_set_uint_setting (input_settings, device, input_settings_class->set_send_events, mode); } else { settings_set_uint_setting (input_settings, CLUTTER_TOUCHPAD_DEVICE, input_settings_class->set_send_events, mode); } } static void update_trackball_scroll_button (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; guint button; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); if (device && !input_settings_class->is_trackball_device (input_settings, device)) return; /* This key is 'i' in the schema but it also specifies a minimum * range of 0 so the cast here is safe. */ button = (guint) g_settings_get_int (priv->trackball_settings, "scroll-wheel-emulation-button"); if (device) { input_settings_class->set_scroll_button (input_settings, device, button); } else if (!device) { GList *l, *devices; devices = clutter_seat_list_devices (priv->seat); for (l = devices; l; l = l->next) { device = l->data; if (input_settings_class->is_trackball_device (input_settings, device)) input_settings_class->set_scroll_button (input_settings, device, button); } g_list_free (devices); } } static void update_keyboard_repeat (MetaInputSettings *input_settings) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; guint delay, interval; gboolean repeat; priv = meta_input_settings_get_instance_private (input_settings); repeat = g_settings_get_boolean (priv->keyboard_settings, "repeat"); delay = g_settings_get_uint (priv->keyboard_settings, "delay"); interval = g_settings_get_uint (priv->keyboard_settings, "repeat-interval"); delay = MAX (1, delay); interval = MAX (1, interval); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); input_settings_class->set_keyboard_repeat (input_settings, repeat, delay, interval); } static MetaMonitor * logical_monitor_find_monitor (MetaLogicalMonitor *logical_monitor, const char *vendor, const char *product, const char *serial) { GList *monitors; GList *l; monitors = meta_logical_monitor_get_monitors (logical_monitor); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (g_strcmp0 (meta_monitor_get_vendor (monitor), vendor) == 0 && g_strcmp0 (meta_monitor_get_product (monitor), product) == 0 && g_strcmp0 (meta_monitor_get_serial (monitor), serial) == 0) return monitor; } return NULL; } static void meta_input_settings_find_monitor (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device, MetaMonitor **out_monitor, MetaLogicalMonitor **out_logical_monitor) { MetaInputSettingsPrivate *priv; MetaMonitorManager *monitor_manager; MetaMonitor *monitor; guint n_values; GList *logical_monitors; GList *l; gchar **edid; priv = meta_input_settings_get_instance_private (input_settings); edid = g_settings_get_strv (settings, "output"); n_values = g_strv_length (edid); if (n_values != 3) { g_warning ("EDID configuration for device '%s' " "is incorrect, must have 3 values", clutter_input_device_get_device_name (device)); goto out; } if (!*edid[0] && !*edid[1] && !*edid[2]) goto out; monitor_manager = priv->monitor_manager; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; monitor = logical_monitor_find_monitor (logical_monitor, edid[0], edid[1], edid[2]); if (monitor) { if (out_monitor) *out_monitor = monitor; if (out_logical_monitor) *out_logical_monitor = logical_monitor; break; } } out: g_strfreev (edid); } static gboolean meta_input_settings_delegate_on_mapper (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; gboolean builtin = FALSE; priv = meta_input_settings_get_instance_private (input_settings); #ifdef HAVE_LIBWACOM if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHSCREEN_DEVICE) { WacomDevice *wacom_device; WacomIntegrationFlags flags = 0; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); if (wacom_device) { flags = libwacom_get_integration_flags (wacom_device); if ((flags & (WACOM_DEVICE_INTEGRATED_SYSTEM | WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) return FALSE; builtin = (flags & WACOM_DEVICE_INTEGRATED_SYSTEM) != 0; } } #endif meta_input_mapper_add_device (priv->input_mapper, device, builtin); return TRUE; } static void update_tablet_keep_aspect (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaLogicalMonitor *logical_monitor = NULL; gboolean keep_aspect; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE) return; #ifdef HAVE_LIBWACOM { WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); /* Keep aspect only makes sense in external tablets */ if (wacom_device && libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE) return; } #endif input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); if (clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE) { keep_aspect = g_settings_get_boolean (settings, "keep-aspect"); meta_input_settings_find_monitor (input_settings, settings, device, NULL, &logical_monitor); } else { keep_aspect = FALSE; } input_settings_class->set_tablet_keep_aspect (input_settings, device, logical_monitor, keep_aspect); } static void update_device_display (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; MetaInputSettingsPrivate *priv; gfloat matrix[6] = { 1, 0, 0, 0, 1, 0 }; MetaMonitor *monitor = NULL; MetaLogicalMonitor *logical_monitor = NULL; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_TOUCHSCREEN_DEVICE) return; priv = meta_input_settings_get_instance_private (input_settings); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); /* If mapping is relative, the device can move on all displays */ if (clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE || clutter_input_device_get_mapping_mode (device) == CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE) { meta_input_settings_find_monitor (input_settings, settings, device, &monitor, &logical_monitor); if (monitor) { meta_input_mapper_remove_device (priv->input_mapper, device); meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, monitor, logical_monitor, matrix); } else { if (meta_input_settings_delegate_on_mapper (input_settings, device)) return; } } input_settings_class->set_matrix (input_settings, device, matrix); /* Ensure the keep-aspect mapping is updated */ update_tablet_keep_aspect (input_settings, settings, device); } static void update_tablet_mapping (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; CDesktopTabletMapping mapping; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE) return; #ifdef HAVE_LIBWACOM { WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); /* Tablet mapping only makes sense on external tablets */ if (wacom_device && (libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE)) return; } #endif input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); mapping = g_settings_get_enum (settings, "mapping"); settings_device_set_uint_setting (input_settings, device, input_settings_class->set_tablet_mapping, mapping); /* Relative mapping disables keep-aspect/display */ update_tablet_keep_aspect (input_settings, settings, device); update_device_display (input_settings, settings, device); } static void update_tablet_area (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; GVariant *variant; const gdouble *area; gsize n_elems; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE) return; #ifdef HAVE_LIBWACOM { WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); /* Tablet area only makes sense on system/display integrated tablets */ if (wacom_device && (libwacom_get_integration_flags (wacom_device) & (WACOM_DEVICE_INTEGRATED_SYSTEM | WACOM_DEVICE_INTEGRATED_DISPLAY)) == 0) return; } #endif input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); variant = g_settings_get_value (settings, "area"); area = g_variant_get_fixed_array (variant, &n_elems, sizeof (gdouble)); if (n_elems == 4) { input_settings_class->set_tablet_area (input_settings, device, area[0], area[1], area[2], area[3]); } g_variant_unref (variant); } static void update_tablet_left_handed (MetaInputSettings *input_settings, GSettings *settings, ClutterInputDevice *device) { MetaInputSettingsClass *input_settings_class; gboolean enabled; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PAD_DEVICE) return; #ifdef HAVE_LIBWACOM { WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); /* Left handed mode only makes sense on external tablets */ if (wacom_device && (libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE)) return; } #endif input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); enabled = g_settings_get_boolean (settings, "left-handed"); settings_device_set_bool_setting (input_settings, device, input_settings_class->set_left_handed, enabled); } static void meta_input_settings_changed_cb (GSettings *settings, const char *key, gpointer user_data) { MetaInputSettings *input_settings = META_INPUT_SETTINGS (user_data); MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); if (settings == priv->mouse_settings) { if (strcmp (key, "left-handed") == 0) update_mouse_left_handed (input_settings, NULL); else if (strcmp (key, "speed") == 0) update_device_speed (input_settings, NULL); else if (strcmp (key, "natural-scroll") == 0) update_device_natural_scroll (input_settings, NULL); else if (strcmp (key, "accel-profile") == 0) update_pointer_accel_profile (input_settings, settings, NULL); else if (strcmp (key, "middle-click-emulation") == 0) update_middle_click_emulation (input_settings, settings, NULL); } else if (settings == priv->touchpad_settings) { if (strcmp (key, "left-handed") == 0) update_touchpad_left_handed (input_settings, NULL); else if (strcmp (key, "speed") == 0) update_device_speed (input_settings, NULL); else if (strcmp (key, "natural-scroll") == 0) update_device_natural_scroll (input_settings, NULL); else if (strcmp (key, "tap-to-click") == 0) update_touchpad_tap_enabled (input_settings, NULL); else if (strcmp (key, "tap-and-drag") == 0) update_touchpad_tap_and_drag_enabled (input_settings, NULL); else if (strcmp(key, "disable-while-typing") == 0) update_touchpad_disable_while_typing (input_settings, NULL); else if (strcmp (key, "send-events") == 0) update_touchpad_send_events (input_settings, NULL); else if (strcmp (key, "edge-scrolling-enabled") == 0) update_touchpad_edge_scroll (input_settings, NULL); else if (strcmp (key, "two-finger-scrolling-enabled") == 0) update_touchpad_two_finger_scroll (input_settings, NULL); else if (strcmp (key, "click-method") == 0) update_touchpad_click_method (input_settings, NULL); else if (strcmp (key, "middle-click-emulation") == 0) update_middle_click_emulation (input_settings, settings, NULL); } else if (settings == priv->trackball_settings) { if (strcmp (key, "scroll-wheel-emulation-button") == 0) update_trackball_scroll_button (input_settings, NULL); else if (strcmp (key, "accel-profile") == 0) update_pointer_accel_profile (input_settings, settings, NULL); else if (strcmp (key, "middle-click-emulation") == 0) update_middle_click_emulation (input_settings, settings, NULL); } else if (settings == priv->keyboard_settings) { if (strcmp (key, "repeat") == 0 || strcmp (key, "repeat-interval") == 0 || strcmp (key, "delay") == 0) update_keyboard_repeat (input_settings); else if (strcmp (key, "remember-numlock-state") == 0) meta_input_settings_maybe_save_numlock_state (input_settings); } } static void mapped_device_changed_cb (GSettings *settings, const gchar *key, DeviceMappingInfo *info) { if (strcmp (key, "output") == 0) update_device_display (info->input_settings, settings, info->device); else if (strcmp (key, "mapping") == 0) update_tablet_mapping (info->input_settings, settings, info->device); else if (strcmp (key, "area") == 0) update_tablet_area (info->input_settings, settings, info->device); else if (strcmp (key, "keep-aspect") == 0) update_tablet_keep_aspect (info->input_settings, settings, info->device); else if (strcmp (key, "left-handed") == 0) update_tablet_left_handed (info->input_settings, settings, info->device); } static void apply_mappable_device_settings (MetaInputSettings *input_settings, DeviceMappingInfo *info) { ClutterInputDeviceType device_type; update_device_display (input_settings, info->settings, info->device); device_type = clutter_input_device_get_device_type (info->device); if (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE || device_type == CLUTTER_PAD_DEVICE) { update_tablet_mapping (input_settings, info->settings, info->device); update_tablet_area (input_settings, info->settings, info->device); update_tablet_keep_aspect (input_settings, info->settings, info->device); update_tablet_left_handed (input_settings, info->settings, info->device); } } struct _keyboard_a11y_settings_flags_pair { const char *name; ClutterKeyboardA11yFlags flag; } keyboard_a11y_settings_flags_pair[] = { { "enable", CLUTTER_A11Y_KEYBOARD_ENABLED }, { "timeout-enable", CLUTTER_A11Y_TIMEOUT_ENABLED }, { "mousekeys-enable", CLUTTER_A11Y_MOUSE_KEYS_ENABLED }, { "slowkeys-enable", CLUTTER_A11Y_SLOW_KEYS_ENABLED }, { "slowkeys-beep-press", CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS }, { "slowkeys-beep-accept", CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT }, { "slowkeys-beep-reject", CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT }, { "bouncekeys-enable", CLUTTER_A11Y_BOUNCE_KEYS_ENABLED }, { "bouncekeys-beep-reject", CLUTTER_A11Y_BOUNCE_KEYS_BEEP_REJECT }, { "stickykeys-enable", CLUTTER_A11Y_STICKY_KEYS_ENABLED }, { "stickykeys-modifier-beep", CLUTTER_A11Y_STICKY_KEYS_BEEP }, { "stickykeys-two-key-off", CLUTTER_A11Y_STICKY_KEYS_TWO_KEY_OFF }, { "feature-state-change-beep", CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP }, }; static void load_keyboard_a11y_settings (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); ClutterKbdA11ySettings kbd_a11y_settings = { 0 }; ClutterInputDevice *core_keyboard; ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); guint i; core_keyboard = clutter_seat_get_keyboard (priv->seat); if (device && device != core_keyboard) return; kbd_a11y_settings.controls = 0; for (i = 0; i < G_N_ELEMENTS (keyboard_a11y_settings_flags_pair); i++) { if (g_settings_get_boolean (priv->keyboard_a11y_settings, keyboard_a11y_settings_flags_pair[i].name)) kbd_a11y_settings.controls |= keyboard_a11y_settings_flags_pair[i].flag; } kbd_a11y_settings.timeout_delay = g_settings_get_int (priv->keyboard_a11y_settings, "disable-timeout"); kbd_a11y_settings.slowkeys_delay = g_settings_get_int (priv->keyboard_a11y_settings, "slowkeys-delay"); kbd_a11y_settings.debounce_delay = g_settings_get_int (priv->keyboard_a11y_settings, "bouncekeys-delay"); kbd_a11y_settings.mousekeys_init_delay = g_settings_get_int (priv->keyboard_a11y_settings, "mousekeys-init-delay"); kbd_a11y_settings.mousekeys_max_speed = g_settings_get_int (priv->keyboard_a11y_settings, "mousekeys-max-speed"); kbd_a11y_settings.mousekeys_accel_time = g_settings_get_int (priv->keyboard_a11y_settings, "mousekeys-accel-time"); clutter_seat_set_kbd_a11y_settings (seat, &kbd_a11y_settings); } static void on_keyboard_a11y_settings_changed (ClutterSeat *seat, ClutterKeyboardA11yFlags new_flags, ClutterKeyboardA11yFlags what_changed, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); guint i; for (i = 0; i < G_N_ELEMENTS (keyboard_a11y_settings_flags_pair); i++) { if (keyboard_a11y_settings_flags_pair[i].flag & what_changed) g_settings_set_boolean (priv->keyboard_a11y_settings, keyboard_a11y_settings_flags_pair[i].name, (new_flags & keyboard_a11y_settings_flags_pair[i].flag) ? TRUE : FALSE); } } static void meta_input_keyboard_a11y_settings_changed (GSettings *settings, const char *key, gpointer user_data) { MetaInputSettings *input_settings = META_INPUT_SETTINGS (user_data); load_keyboard_a11y_settings (input_settings, NULL); } struct _pointer_a11y_settings_flags_pair { const char *name; ClutterPointerA11yFlags flag; } pointer_a11y_settings_flags_pair[] = { { "secondary-click-enabled", CLUTTER_A11Y_SECONDARY_CLICK_ENABLED }, { "dwell-click-enabled", CLUTTER_A11Y_DWELL_ENABLED }, }; static ClutterPointerA11yDwellDirection pointer_a11y_dwell_direction_from_setting (MetaInputSettings *input_settings, const char *key) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); CDesktopMouseDwellDirection dwell_gesture_direction; dwell_gesture_direction = g_settings_get_enum (priv->mouse_a11y_settings, key); switch (dwell_gesture_direction) { case C_DESKTOP_MOUSE_DWELL_DIRECTION_LEFT: return CLUTTER_A11Y_DWELL_DIRECTION_LEFT; break; case C_DESKTOP_MOUSE_DWELL_DIRECTION_RIGHT: return CLUTTER_A11Y_DWELL_DIRECTION_RIGHT; break; case C_DESKTOP_MOUSE_DWELL_DIRECTION_UP: return CLUTTER_A11Y_DWELL_DIRECTION_UP; break; case C_DESKTOP_MOUSE_DWELL_DIRECTION_DOWN: return CLUTTER_A11Y_DWELL_DIRECTION_DOWN; break; default: break; } return CLUTTER_A11Y_DWELL_DIRECTION_NONE; } static void load_pointer_a11y_settings (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); ClutterPointerA11ySettings pointer_a11y_settings; ClutterInputDevice *core_pointer; CDesktopMouseDwellMode dwell_mode; guint i; core_pointer = clutter_seat_get_pointer (priv->seat); if (device && device != core_pointer) return; clutter_seat_get_pointer_a11y_settings (CLUTTER_SEAT (priv->seat), &pointer_a11y_settings); pointer_a11y_settings.controls = 0; for (i = 0; i < G_N_ELEMENTS (pointer_a11y_settings_flags_pair); i++) { if (g_settings_get_boolean (priv->mouse_a11y_settings, pointer_a11y_settings_flags_pair[i].name)) pointer_a11y_settings.controls |= pointer_a11y_settings_flags_pair[i].flag; } /* "secondary-click-time" is expressed in seconds */ pointer_a11y_settings.secondary_click_delay = (1000 * g_settings_get_double (priv->mouse_a11y_settings, "secondary-click-time")); /* "dwell-time" is expressed in seconds */ pointer_a11y_settings.dwell_delay = (1000 * g_settings_get_double (priv->mouse_a11y_settings, "dwell-time")); pointer_a11y_settings.dwell_threshold = g_settings_get_int (priv->mouse_a11y_settings, "dwell-threshold"); dwell_mode = g_settings_get_enum (priv->mouse_a11y_settings, "dwell-mode"); if (dwell_mode == C_DESKTOP_MOUSE_DWELL_MODE_WINDOW) pointer_a11y_settings.dwell_mode = CLUTTER_A11Y_DWELL_MODE_WINDOW; else pointer_a11y_settings.dwell_mode = CLUTTER_A11Y_DWELL_MODE_GESTURE; pointer_a11y_settings.dwell_gesture_single = pointer_a11y_dwell_direction_from_setting (input_settings, "dwell-gesture-single"); pointer_a11y_settings.dwell_gesture_double = pointer_a11y_dwell_direction_from_setting (input_settings, "dwell-gesture-double"); pointer_a11y_settings.dwell_gesture_drag = pointer_a11y_dwell_direction_from_setting (input_settings, "dwell-gesture-drag"); pointer_a11y_settings.dwell_gesture_secondary = pointer_a11y_dwell_direction_from_setting (input_settings, "dwell-gesture-secondary"); clutter_seat_set_pointer_a11y_settings (CLUTTER_SEAT (priv->seat), &pointer_a11y_settings); } static void meta_input_mouse_a11y_settings_changed (GSettings *settings, const char *key, gpointer user_data) { MetaInputSettings *input_settings = META_INPUT_SETTINGS (user_data); load_pointer_a11y_settings (input_settings, NULL); } static GSettings * lookup_device_settings (ClutterInputDevice *device) { const gchar *group, *schema, *vendor, *product; ClutterInputDeviceType type; GSettings *settings; gchar *path; type = clutter_input_device_get_device_type (device); if (type == CLUTTER_TOUCHSCREEN_DEVICE) { group = "touchscreens"; schema = "org.cinnamon.desktop.peripherals.touchscreen"; } else if (type == CLUTTER_TABLET_DEVICE || type == CLUTTER_PEN_DEVICE || type == CLUTTER_ERASER_DEVICE || type == CLUTTER_CURSOR_DEVICE || type == CLUTTER_PAD_DEVICE) { group = "tablets"; schema = "org.cinnamon.desktop.peripherals.tablet"; } else return NULL; vendor = clutter_input_device_get_vendor_id (device); product = clutter_input_device_get_product_id (device); path = g_strdup_printf ("/org/cinnamon/desktop/peripherals/%s/%s:%s/", group, vendor, product); settings = g_settings_new_with_path (schema, path); g_free (path); return settings; } static GSettings * lookup_tool_settings (ClutterInputDeviceTool *tool, ClutterInputDevice *device) { GSettings *tool_settings; guint64 serial; gchar *path; tool_settings = g_object_get_qdata (G_OBJECT (tool), quark_tool_settings); if (tool_settings) return tool_settings; serial = clutter_input_device_tool_get_serial (tool); /* The Wacom driver uses serial 1 for serial-less devices but 1 is not a * real serial, so let's custom-case this */ if (serial == 0 || serial == 1) { path = g_strdup_printf ("/org/cinnamon/desktop/peripherals/stylus/default-%s:%s/", clutter_input_device_get_vendor_id (device), clutter_input_device_get_product_id (device)); } else { path = g_strdup_printf ("/org/cinnamon/desktop/peripherals/stylus/%" G_GINT64_MODIFIER "x/", serial); } tool_settings = g_settings_new_with_path ("org.cinnamon.desktop.peripherals.tablet.stylus", path); g_object_set_qdata_full (G_OBJECT (tool), quark_tool_settings, tool_settings, (GDestroyNotify) g_object_unref); g_free (path); return tool_settings; } static GSettings * lookup_pad_action_settings (ClutterInputDevice *device, MetaPadActionType action, guint number, MetaPadDirection direction, gint mode) { const gchar *vendor, *product, *action_type, *detail_type = NULL; GSettings *settings; GString *path; gchar action_label; vendor = clutter_input_device_get_vendor_id (device); product = clutter_input_device_get_product_id (device); action_label = 'A' + number; switch (action) { case META_PAD_ACTION_BUTTON: action_type = "button"; break; case META_PAD_ACTION_RING: g_assert (direction == META_PAD_DIRECTION_CW || direction == META_PAD_DIRECTION_CCW); action_type = "ring"; detail_type = (direction == META_PAD_DIRECTION_CW) ? "cw" : "ccw"; break; case META_PAD_ACTION_STRIP: g_assert (direction == META_PAD_DIRECTION_UP || direction == META_PAD_DIRECTION_DOWN); action_type = "strip"; detail_type = (direction == META_PAD_DIRECTION_UP) ? "up" : "down"; break; default: return NULL; } path = g_string_new (NULL); g_string_append_printf (path, "/org/cinnamon/desktop/peripherals/tablets/%s:%s/%s%c", vendor, product, action_type, action_label); if (detail_type) g_string_append_printf (path, "-%s", detail_type); if (mode >= 0) g_string_append_printf (path, "-mode-%d", mode); g_string_append_c (path, '/'); settings = g_settings_new_with_path ("org.cinnamon.desktop.peripherals.tablet.pad-button", path->str); g_string_free (path, TRUE); return settings; } static void monitors_changed_cb (MetaMonitorManager *monitor_manager, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; ClutterInputDevice *device; DeviceMappingInfo *info; GHashTableIter iter; priv = meta_input_settings_get_instance_private (input_settings); g_hash_table_iter_init (&iter, priv->mappable_devices); while (g_hash_table_iter_next (&iter, (gpointer *) &device, (gpointer *) &info)) update_device_display (input_settings, info->settings, device); } static void input_mapper_device_mapped_cb (MetaInputMapper *mapper, ClutterInputDevice *device, MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; float matrix[6] = { 1, 0, 0, 0, 1, 0 }; priv = meta_input_settings_get_instance_private (input_settings); if (monitor && logical_monitor) { meta_monitor_manager_get_monitor_matrix (priv->monitor_manager, monitor, logical_monitor, matrix); } META_INPUT_SETTINGS_GET_CLASS (input_settings)->set_matrix (input_settings, device, matrix); } static void device_mapping_info_free (DeviceMappingInfo *info) { g_clear_signal_handler (&info->changed_id, info->settings); g_object_unref (info->settings); g_free (info->group_modes); g_slice_free (DeviceMappingInfo, info); } static gboolean check_add_mappable_device (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; ClutterInputDeviceType device_type; GSettings *settings; device_type = clutter_input_device_get_device_type (device); if ((device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE || device_type == CLUTTER_PAD_DEVICE) && g_getenv ("MUTTER_DISABLE_WACOM_CONFIGURATION") != NULL) return FALSE; settings = lookup_device_settings (device); if (!settings) return FALSE; priv = meta_input_settings_get_instance_private (input_settings); info = g_slice_new0 (DeviceMappingInfo); info->input_settings = input_settings; info->device = device; info->settings = settings; if (device_type == CLUTTER_PAD_DEVICE) { info->group_modes = g_new0 (guint, clutter_input_device_get_n_mode_groups (device)); } info->changed_id = g_signal_connect (settings, "changed", G_CALLBACK (mapped_device_changed_cb), info); g_hash_table_insert (priv->mappable_devices, device, info); apply_mappable_device_settings (input_settings, info); return TRUE; } static void apply_device_settings (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (input_settings); update_device_speed (input_settings, device); update_device_natural_scroll (input_settings, device); update_mouse_left_handed (input_settings, device); update_pointer_accel_profile (input_settings, priv->mouse_settings, device); update_touchpad_left_handed (input_settings, device); update_touchpad_tap_enabled (input_settings, device); update_touchpad_tap_and_drag_enabled (input_settings, device); update_touchpad_disable_while_typing (input_settings, device); update_touchpad_send_events (input_settings, device); update_touchpad_two_finger_scroll (input_settings, device); update_touchpad_edge_scroll (input_settings, device); update_touchpad_click_method (input_settings, device); update_trackball_scroll_button (input_settings, device); update_pointer_accel_profile (input_settings, priv->trackball_settings, device); load_keyboard_a11y_settings (input_settings, device); load_pointer_a11y_settings (input_settings, device); update_middle_click_emulation (input_settings, priv->mouse_settings, device); update_middle_click_emulation (input_settings, priv->touchpad_settings, device); update_middle_click_emulation (input_settings, priv->trackball_settings, device); } static void update_stylus_pressure (MetaInputSettings *input_settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool) { MetaInputSettingsClass *input_settings_class; GSettings *tool_settings; const gint32 *curve; GVariant *variant; gsize n_elems; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE) return; if (!tool) return; tool_settings = lookup_tool_settings (tool, device); if (clutter_input_device_tool_get_tool_type (tool) == CLUTTER_INPUT_DEVICE_TOOL_ERASER) variant = g_settings_get_value (tool_settings, "eraser-pressure-curve"); else variant = g_settings_get_value (tool_settings, "pressure-curve"); curve = g_variant_get_fixed_array (variant, &n_elems, sizeof (gint32)); if (n_elems != 4) return; input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); input_settings_class->set_stylus_pressure (input_settings, device, tool, curve); } static void update_stylus_buttonmap (MetaInputSettings *input_settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool) { MetaInputSettingsClass *input_settings_class; CDesktopStylusButtonAction primary, secondary, tertiary; GSettings *tool_settings; if (clutter_input_device_get_device_type (device) != CLUTTER_TABLET_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_PEN_DEVICE && clutter_input_device_get_device_type (device) != CLUTTER_ERASER_DEVICE) return; if (!tool) return; tool_settings = lookup_tool_settings (tool, device); primary = g_settings_get_enum (tool_settings, "button-action"); secondary = g_settings_get_enum (tool_settings, "secondary-button-action"); tertiary = g_settings_get_enum (tool_settings, "tertiary-button-action"); input_settings_class = META_INPUT_SETTINGS_GET_CLASS (input_settings); input_settings_class->set_stylus_button_map (input_settings, device, tool, primary, secondary, tertiary); } static void apply_stylus_settings (MetaInputSettings *input_settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool) { update_stylus_pressure (input_settings, device, tool); update_stylus_buttonmap (input_settings, device, tool); } static void evaluate_two_finger_scrolling (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsClass *klass; MetaInputSettingsPrivate *priv; if (clutter_input_device_get_device_type (device) != CLUTTER_TOUCHPAD_DEVICE) return; klass = META_INPUT_SETTINGS_GET_CLASS (input_settings); priv = meta_input_settings_get_instance_private (input_settings); if (klass->has_two_finger_scroll (input_settings, device)) g_hash_table_add (priv->two_finger_devices, device); } static void meta_input_settings_device_added (ClutterSeat *seat, ClutterInputDevice *device, MetaInputSettings *input_settings) { if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return; evaluate_two_finger_scrolling (input_settings, device); apply_device_settings (input_settings, device); check_add_mappable_device (input_settings, device); } static void meta_input_settings_device_removed (ClutterSeat *seat, ClutterInputDevice *device, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (input_settings); meta_input_mapper_remove_device (priv->input_mapper, device); g_hash_table_remove (priv->mappable_devices, device); g_hash_table_remove (priv->current_tools, device); if (g_hash_table_remove (priv->two_finger_devices, device) && g_hash_table_size (priv->two_finger_devices) == 0) apply_device_settings (input_settings, NULL); } static void current_tool_changed_cb (GSettings *settings, const char *key, gpointer user_data) { CurrentToolInfo *info = user_data; apply_stylus_settings (info->input_settings, info->device, info->tool); } static CurrentToolInfo * current_tool_info_new (MetaInputSettings *input_settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool) { CurrentToolInfo *info; info = g_new0 (CurrentToolInfo, 1); info->input_settings = input_settings; info->device = device; info->tool = tool; info->settings = lookup_tool_settings (tool, device); info->changed_id = g_signal_connect (info->settings, "changed", G_CALLBACK (current_tool_changed_cb), info); return info; } static void current_tool_info_free (CurrentToolInfo *info) { g_clear_signal_handler (&info->changed_id, info->settings); g_free (info); } static void meta_input_settings_tool_changed (ClutterSeat *seat, ClutterInputDevice *device, ClutterInputDeviceTool *tool, MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (input_settings); if (tool) { CurrentToolInfo *current_tool; current_tool = current_tool_info_new (input_settings, device, tool); g_hash_table_insert (priv->current_tools, device, current_tool); apply_stylus_settings (input_settings, device, tool); } else { g_hash_table_remove (priv->current_tools, device); } } static void check_mappable_devices (MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; GList *l, *devices; priv = meta_input_settings_get_instance_private (input_settings); devices = clutter_seat_list_devices (priv->seat); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) continue; check_add_mappable_device (input_settings, device); } g_list_free (devices); } static void power_save_mode_changed_cb (MetaMonitorManager *manager, gpointer user_data) { MetaInputSettingsPrivate *priv; ClutterInputDevice *device; MetaLogicalMonitor *logical_monitor; MetaMonitor *builtin; MetaPowerSave power_save_mode; gboolean on; power_save_mode = meta_monitor_manager_get_power_save_mode (manager); on = power_save_mode == META_POWER_SAVE_ON; priv = meta_input_settings_get_instance_private (user_data); builtin = meta_monitor_manager_get_laptop_panel (manager); if (!builtin) return; logical_monitor = meta_monitor_get_logical_monitor (builtin); if (!logical_monitor) return; device = meta_input_mapper_get_logical_monitor_device (priv->input_mapper, logical_monitor, CLUTTER_TOUCHSCREEN_DEVICE); if (!device) return; clutter_input_device_set_enabled (device, on); } static void refresh_input_settings (MetaInputSettings *input_settings) { GSList *devices, *d; devices = meta_input_settings_get_devices (input_settings, CLUTTER_TOUCHPAD_DEVICE); for (d = devices; d; d = d->next) evaluate_two_finger_scrolling (input_settings, d->data); g_slist_free (devices); apply_device_settings (input_settings, NULL); update_keyboard_repeat (input_settings); check_mappable_devices (input_settings); } static void meta_input_settings_constructed (GObject *object) { MetaInputSettings *input_settings = META_INPUT_SETTINGS (object); refresh_input_settings (input_settings); } static void meta_input_settings_class_init (MetaInputSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_input_settings_dispose; object_class->constructed = meta_input_settings_constructed; quark_tool_settings = g_quark_from_static_string ("meta-input-settings-tool-settings"); } static void meta_input_settings_init (MetaInputSettings *settings) { MetaInputSettingsPrivate *priv; priv = meta_input_settings_get_instance_private (settings); priv->seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_connect (priv->seat, "device-added", G_CALLBACK (meta_input_settings_device_added), settings); g_signal_connect (priv->seat, "device-removed", G_CALLBACK (meta_input_settings_device_removed), settings); g_signal_connect (priv->seat, "tool-changed", G_CALLBACK (meta_input_settings_tool_changed), settings); priv->mouse_settings = g_settings_new ("org.cinnamon.desktop.peripherals.mouse"); g_signal_connect (priv->mouse_settings, "changed", G_CALLBACK (meta_input_settings_changed_cb), settings); priv->touchpad_settings = g_settings_new ("org.cinnamon.desktop.peripherals.touchpad"); g_signal_connect (priv->touchpad_settings, "changed", G_CALLBACK (meta_input_settings_changed_cb), settings); priv->trackball_settings = g_settings_new ("org.cinnamon.desktop.peripherals.trackball"); g_signal_connect (priv->trackball_settings, "changed", G_CALLBACK (meta_input_settings_changed_cb), settings); priv->keyboard_settings = g_settings_new ("org.cinnamon.desktop.peripherals.keyboard"); g_signal_connect (priv->keyboard_settings, "changed", G_CALLBACK (meta_input_settings_changed_cb), settings); priv->keyboard_a11y_settings = g_settings_new ("org.cinnamon.desktop.a11y.keyboard"); g_signal_connect (priv->keyboard_a11y_settings, "changed", G_CALLBACK (meta_input_keyboard_a11y_settings_changed), settings); g_signal_connect (priv->seat, "kbd-a11y-flags-changed", G_CALLBACK (on_keyboard_a11y_settings_changed), settings); priv->mouse_a11y_settings = g_settings_new ("org.cinnamon.desktop.a11y.mouse"); g_signal_connect (priv->mouse_a11y_settings, "changed", G_CALLBACK (meta_input_mouse_a11y_settings_changed), settings); priv->mappable_devices = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) device_mapping_info_free); priv->current_tools = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) current_tool_info_free); priv->monitor_manager = g_object_ref (meta_monitor_manager_get ()); g_signal_connect (priv->monitor_manager, "monitors-changed-internal", G_CALLBACK (monitors_changed_cb), settings); g_signal_connect (priv->monitor_manager, "power-save-mode-changed", G_CALLBACK (power_save_mode_changed_cb), settings); priv->two_finger_devices = g_hash_table_new (NULL, NULL); priv->input_mapper = meta_input_mapper_new (); g_signal_connect (priv->input_mapper, "device-mapped", G_CALLBACK (input_mapper_device_mapped_cb), settings); } GSettings * meta_input_settings_get_tablet_settings (MetaInputSettings *settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; g_return_val_if_fail (META_IS_INPUT_SETTINGS (settings), NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); priv = meta_input_settings_get_instance_private (settings); info = g_hash_table_lookup (priv->mappable_devices, device); return info ? g_object_ref (info->settings) : NULL; } static ClutterInputDevice * find_grouped_pen (MetaInputSettings *settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; GList *l, *devices; ClutterInputDeviceType device_type; ClutterInputDevice *pen = NULL; device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE) return device; priv = meta_input_settings_get_instance_private (settings); devices = clutter_seat_list_devices (priv->seat); for (l = devices; l; l = l->next) { ClutterInputDevice *other_device = l->data; device_type = clutter_input_device_get_device_type (other_device); if ((device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE) && clutter_input_device_is_grouped (device, other_device)) { pen = other_device; break; } } g_list_free (devices); return pen; } MetaLogicalMonitor * meta_input_settings_get_tablet_logical_monitor (MetaInputSettings *settings, ClutterInputDevice *device) { MetaLogicalMonitor *logical_monitor = NULL; MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; g_return_val_if_fail (META_IS_INPUT_SETTINGS (settings), NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), NULL); if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE) { device = find_grouped_pen (settings, device); if (!device) return NULL; } priv = meta_input_settings_get_instance_private (settings); info = g_hash_table_lookup (priv->mappable_devices, device); if (!info) return NULL; logical_monitor = meta_input_mapper_get_device_logical_monitor (priv->input_mapper, device); if (!logical_monitor) { meta_input_settings_find_monitor (settings, info->settings, device, NULL, &logical_monitor); } return logical_monitor; } CDesktopTabletMapping meta_input_settings_get_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; g_return_val_if_fail (META_IS_INPUT_SETTINGS (settings), C_DESKTOP_TABLET_MAPPING_ABSOLUTE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (device), C_DESKTOP_TABLET_MAPPING_ABSOLUTE); priv = meta_input_settings_get_instance_private (settings); info = g_hash_table_lookup (priv->mappable_devices, device); g_return_val_if_fail (info != NULL, C_DESKTOP_TABLET_MAPPING_ABSOLUTE); return g_settings_get_enum (info->settings, "mapping"); } static CDesktopPadButtonAction meta_input_settings_get_pad_button_action (MetaInputSettings *input_settings, ClutterInputDevice *pad, guint button) { CDesktopPadButtonAction action; GSettings *settings; g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), C_DESKTOP_PAD_BUTTON_ACTION_NONE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), C_DESKTOP_PAD_BUTTON_ACTION_NONE); settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, button, META_PAD_DIRECTION_NONE, -1); action = g_settings_get_enum (settings, "action"); g_object_unref (settings); return action; } static gboolean cycle_logical_monitors (MetaInputSettings *settings, MetaLogicalMonitor *current_logical_monitor, MetaLogicalMonitor **next_logical_monitor) { MetaInputSettingsPrivate *priv = meta_input_settings_get_instance_private (settings); MetaMonitorManager *monitor_manager = priv->monitor_manager; GList *logical_monitors; /* We cycle between: * - the span of all monitors (current_output = NULL) * - each monitor individually. */ logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); if (!current_logical_monitor) { *next_logical_monitor = logical_monitors->data; } else { GList *l; l = g_list_find (logical_monitors, current_logical_monitor); if (l->next) *next_logical_monitor = l->next->data; else *next_logical_monitor = NULL; } return TRUE; } static void meta_input_settings_cycle_tablet_output (MetaInputSettings *input_settings, ClutterInputDevice *device) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; MetaLogicalMonitor *logical_monitor = NULL; const gchar *edid[4] = { 0 }, *pretty_name = NULL; #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; #endif g_return_if_fail (META_IS_INPUT_SETTINGS (input_settings)); g_return_if_fail (CLUTTER_IS_INPUT_DEVICE (device)); g_return_if_fail (clutter_input_device_get_device_type (device) == CLUTTER_TABLET_DEVICE || clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE); priv = meta_input_settings_get_instance_private (input_settings); info = g_hash_table_lookup (priv->mappable_devices, device); g_return_if_fail (info != NULL); #ifdef HAVE_LIBWACOM wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); if (wacom_device) { /* Output rotation only makes sense on external tablets */ if (libwacom_get_integration_flags (wacom_device) != WACOM_DEVICE_INTEGRATED_NONE) return; pretty_name = libwacom_get_name (wacom_device); } #endif meta_input_settings_find_monitor (input_settings, info->settings, device, NULL, &logical_monitor); if (!cycle_logical_monitors (input_settings, logical_monitor, &logical_monitor)) return; if (logical_monitor) { MetaMonitor *monitor; /* Pick an arbitrary monitor in the logical monitor to represent it. */ monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; edid[0] = meta_monitor_get_vendor (monitor); edid[1] = meta_monitor_get_product (monitor); edid[2] = meta_monitor_get_serial (monitor); } else { edid[0] = ""; edid[1] = ""; edid[2] = ""; } g_settings_set_strv (info->settings, "output", edid); meta_display_show_tablet_mapping_notification (meta_get_display (), device, pretty_name); } static void emulate_modifiers (ClutterVirtualInputDevice *device, ClutterModifierType mods, ClutterKeyState state) { guint i; struct { ClutterModifierType mod; guint keyval; } mod_map[] = { { CLUTTER_SHIFT_MASK, CLUTTER_KEY_Shift_L }, { CLUTTER_CONTROL_MASK, CLUTTER_KEY_Control_L }, { CLUTTER_MOD1_MASK, CLUTTER_KEY_Meta_L } }; for (i = 0; i < G_N_ELEMENTS (mod_map); i++) { if ((mods & mod_map[i].mod) == 0) continue; clutter_virtual_input_device_notify_keyval (device, clutter_get_current_event_time (), mod_map[i].keyval, state); } } static void meta_input_settings_emulate_keybinding (MetaInputSettings *input_settings, const gchar *accel, gboolean is_press) { MetaInputSettingsPrivate *priv; ClutterKeyState state; guint key, mods; if (!accel || !*accel) return; priv = meta_input_settings_get_instance_private (input_settings); /* FIXME: This is appalling */ gtk_accelerator_parse (accel, &key, &mods); if (!priv->virtual_pad_keyboard) { ClutterBackend *backend; ClutterSeat *seat; backend = clutter_get_default_backend (); seat = clutter_backend_get_default_seat (backend); priv->virtual_pad_keyboard = clutter_seat_create_virtual_device (seat, CLUTTER_KEYBOARD_DEVICE); } state = is_press ? CLUTTER_KEY_STATE_PRESSED : CLUTTER_KEY_STATE_RELEASED; if (is_press) emulate_modifiers (priv->virtual_pad_keyboard, mods, state); clutter_virtual_input_device_notify_keyval (priv->virtual_pad_keyboard, clutter_get_current_event_time (), key, state); if (!is_press) emulate_modifiers (priv->virtual_pad_keyboard, mods, state); } gboolean meta_input_settings_is_pad_button_grabbed (MetaInputSettings *input_settings, ClutterInputDevice *pad, guint button) { g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), FALSE); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), FALSE); g_return_val_if_fail (clutter_input_device_get_device_type (pad) == CLUTTER_PAD_DEVICE, FALSE); return (meta_input_settings_get_pad_button_action (input_settings, pad, button) != C_DESKTOP_PAD_BUTTON_ACTION_NONE); } static gboolean meta_input_settings_handle_pad_button (MetaInputSettings *input_settings, ClutterInputDevice *pad, const ClutterPadButtonEvent *event) { CDesktopPadButtonAction action; gint button, group, mode; gboolean is_press; GSettings *settings; gchar *accel; g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), FALSE); g_return_val_if_fail (event->type == CLUTTER_PAD_BUTTON_PRESS || event->type == CLUTTER_PAD_BUTTON_RELEASE, FALSE); button = event->button; mode = event->mode; group = clutter_input_device_get_mode_switch_button_group (pad, button); is_press = event->type == CLUTTER_PAD_BUTTON_PRESS; if (is_press && group >= 0) { guint n_modes = clutter_input_device_get_group_n_modes (pad, group); const gchar *pretty_name = NULL; MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; #endif priv = meta_input_settings_get_instance_private (input_settings); info = g_hash_table_lookup (priv->mappable_devices, pad); #ifdef HAVE_LIBWACOM wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (pad)); if (wacom_device) pretty_name = libwacom_get_name (wacom_device); #endif meta_display_notify_pad_group_switch (meta_get_display (), pad, pretty_name, group, mode, n_modes); info->group_modes[group] = mode; } action = meta_input_settings_get_pad_button_action (input_settings, pad, button); switch (action) { case C_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR: if (is_press) meta_input_settings_cycle_tablet_output (input_settings, pad); return TRUE; case C_DESKTOP_PAD_BUTTON_ACTION_HELP: if (is_press) meta_display_request_pad_osd (meta_get_display (), pad, FALSE); return TRUE; case C_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING: settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, button, META_PAD_DIRECTION_NONE, -1); accel = g_settings_get_string (settings, "keybinding"); meta_input_settings_emulate_keybinding (input_settings, accel, is_press); g_object_unref (settings); g_free (accel); return TRUE; case C_DESKTOP_PAD_BUTTON_ACTION_NONE: default: return FALSE; } } static gboolean meta_input_settings_handle_pad_action (MetaInputSettings *input_settings, ClutterInputDevice *pad, MetaPadActionType action, guint number, MetaPadDirection direction, guint mode) { GSettings *settings; gboolean handled = FALSE; gchar *accel; settings = lookup_pad_action_settings (pad, action, number, direction, mode); accel = g_settings_get_string (settings, "keybinding"); if (accel && *accel) { meta_input_settings_emulate_keybinding (input_settings, accel, TRUE); meta_input_settings_emulate_keybinding (input_settings, accel, FALSE); handled = TRUE; } g_object_unref (settings); g_free (accel); return handled; } static gboolean meta_input_settings_get_pad_action_direction (MetaInputSettings *input_settings, const ClutterEvent *event, MetaPadDirection *direction) { MetaInputSettingsPrivate *priv; ClutterInputDevice *pad = clutter_event_get_device (event); MetaPadActionType pad_action; gboolean has_direction = FALSE; MetaPadDirection inc_dir, dec_dir; guint number; gdouble value; priv = meta_input_settings_get_instance_private (input_settings); *direction = META_PAD_DIRECTION_NONE; switch (event->type) { case CLUTTER_PAD_RING: pad_action = META_PAD_ACTION_RING; number = event->pad_ring.ring_number; value = event->pad_ring.angle; inc_dir = META_PAD_DIRECTION_CW; dec_dir = META_PAD_DIRECTION_CCW; break; case CLUTTER_PAD_STRIP: pad_action = META_PAD_ACTION_STRIP; number = event->pad_strip.strip_number; value = event->pad_strip.value; inc_dir = META_PAD_DIRECTION_DOWN; dec_dir = META_PAD_DIRECTION_UP; break; default: return FALSE; } if (priv->last_pad_action_info.pad == pad && priv->last_pad_action_info.action == pad_action && priv->last_pad_action_info.number == number && value >= 0 && priv->last_pad_action_info.value >= 0) { *direction = (value - priv->last_pad_action_info.value) > 0 ? inc_dir : dec_dir; has_direction = TRUE; } priv->last_pad_action_info.pad = pad; priv->last_pad_action_info.action = pad_action; priv->last_pad_action_info.number = number; priv->last_pad_action_info.value = value; return has_direction; } gboolean meta_input_settings_handle_pad_event (MetaInputSettings *input_settings, const ClutterEvent *event) { ClutterInputDevice *pad; MetaPadDirection direction = META_PAD_DIRECTION_NONE; pad = clutter_event_get_source_device ((ClutterEvent *) event); switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: return meta_input_settings_handle_pad_button (input_settings, pad, &event->pad_button); case CLUTTER_PAD_RING: if (!meta_input_settings_get_pad_action_direction (input_settings, event, &direction)) return FALSE; return meta_input_settings_handle_pad_action (input_settings, pad, META_PAD_ACTION_RING, event->pad_ring.ring_number, direction, event->pad_ring.mode); case CLUTTER_PAD_STRIP: if (!meta_input_settings_get_pad_action_direction (input_settings, event, &direction)) return FALSE; return meta_input_settings_handle_pad_action (input_settings, pad, META_PAD_ACTION_STRIP, event->pad_strip.strip_number, direction, event->pad_strip.mode); default: return FALSE; } } static gchar * compose_directional_action_label (GSettings *direction1, GSettings *direction2) { gchar *accel1, *accel2, *str = NULL; accel1 = g_settings_get_string (direction1, "keybinding"); accel2 = g_settings_get_string (direction2, "keybinding"); if (accel1 && *accel1 && accel2 && *accel2) str = g_strdup_printf ("%s / %s", accel1, accel2); g_free (accel1); g_free (accel2); return str; } static gchar * meta_input_settings_get_ring_label (MetaInputSettings *settings, ClutterInputDevice *pad, guint number, guint mode) { GSettings *settings1, *settings2; gchar *label; /* We only allow keybinding actions with those */ settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number, META_PAD_DIRECTION_CW, mode); settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_RING, number, META_PAD_DIRECTION_CCW, mode); label = compose_directional_action_label (settings1, settings2); g_object_unref (settings1); g_object_unref (settings2); return label; } static gchar * meta_input_settings_get_strip_label (MetaInputSettings *settings, ClutterInputDevice *pad, guint number, guint mode) { GSettings *settings1, *settings2; gchar *label; /* We only allow keybinding actions with those */ settings1 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number, META_PAD_DIRECTION_UP, mode); settings2 = lookup_pad_action_settings (pad, META_PAD_ACTION_STRIP, number, META_PAD_DIRECTION_DOWN, mode); label = compose_directional_action_label (settings1, settings2); g_object_unref (settings1); g_object_unref (settings2); return label; } static gchar * meta_input_settings_get_button_label (MetaInputSettings *input_settings, ClutterInputDevice *pad, guint button) { CDesktopPadButtonAction action; gint group; g_return_val_if_fail (META_IS_INPUT_SETTINGS (input_settings), NULL); g_return_val_if_fail (CLUTTER_IS_INPUT_DEVICE (pad), NULL); g_return_val_if_fail (clutter_input_device_get_device_type (pad) == CLUTTER_PAD_DEVICE, NULL); group = clutter_input_device_get_mode_switch_button_group (pad, button); if (group >= 0) { /* TRANSLATORS: This string refers to a button that switches between * different modes. */ return g_strdup_printf (_("Mode Switch (Group %d)"), group); } action = meta_input_settings_get_pad_button_action (input_settings, pad, button); switch (action) { case C_DESKTOP_PAD_BUTTON_ACTION_KEYBINDING: { GSettings *settings; gchar *accel; settings = lookup_pad_action_settings (pad, META_PAD_ACTION_BUTTON, button, META_PAD_DIRECTION_NONE, -1); accel = g_settings_get_string (settings, "keybinding"); g_object_unref (settings); return accel; } case C_DESKTOP_PAD_BUTTON_ACTION_SWITCH_MONITOR: /* TRANSLATORS: This string refers to an action, cycles drawing tablets' * mapping through the available outputs. */ return g_strdup (_("Switch monitor")); case C_DESKTOP_PAD_BUTTON_ACTION_HELP: return g_strdup (_("Show on-screen help")); case C_DESKTOP_PAD_BUTTON_ACTION_NONE: default: return NULL; } } static guint get_current_pad_mode (MetaInputSettings *input_settings, ClutterInputDevice *pad, MetaPadActionType action_type, guint number) { MetaInputSettingsPrivate *priv; DeviceMappingInfo *info; guint group = 0, n_groups; priv = meta_input_settings_get_instance_private (input_settings); info = g_hash_table_lookup (priv->mappable_devices, pad); n_groups = clutter_input_device_get_n_mode_groups (pad); if (!info->group_modes || n_groups == 0) return 0; if (action_type == META_PAD_ACTION_RING || action_type == META_PAD_ACTION_STRIP) { /* Assume features are evenly distributed in groups */ group = number % n_groups; } return info->group_modes[group]; } gchar * meta_input_settings_get_pad_action_label (MetaInputSettings *input_settings, ClutterInputDevice *pad, MetaPadActionType action_type, guint number) { guint mode; switch (action_type) { case META_PAD_ACTION_BUTTON: return meta_input_settings_get_button_label (input_settings, pad, number); case META_PAD_ACTION_RING: mode = get_current_pad_mode (input_settings, pad, action_type, number); return meta_input_settings_get_ring_label (input_settings, pad, number, mode); case META_PAD_ACTION_STRIP: mode = get_current_pad_mode (input_settings, pad, action_type, number); return meta_input_settings_get_strip_label (input_settings, pad, number, mode); } return NULL; } void meta_input_settings_maybe_save_numlock_state (MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; ClutterSeat *seat; ClutterKeymap *keymap; gboolean numlock_state; priv = meta_input_settings_get_instance_private (input_settings); if (!g_settings_get_boolean (priv->keyboard_settings, "remember-numlock-state")) return; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); keymap = clutter_seat_get_keymap (seat); numlock_state = clutter_keymap_get_num_lock_state (keymap); if (numlock_state == g_settings_get_boolean (priv->keyboard_settings, "numlock-state")) return; g_settings_set_boolean (priv->keyboard_settings, "numlock-state", numlock_state); } void meta_input_settings_maybe_restore_numlock_state (MetaInputSettings *input_settings) { MetaInputSettingsPrivate *priv; gboolean numlock_state; priv = meta_input_settings_get_instance_private (input_settings); if (!g_settings_get_boolean (priv->keyboard_settings, "remember-numlock-state")) return; numlock_state = g_settings_get_boolean (priv->keyboard_settings, "numlock-state"); meta_backend_set_numlock (meta_get_backend (), numlock_state); } void meta_input_settings_refresh (MetaInputSettings *input_settings) { g_return_if_fail (META_IS_INPUT_SETTINGS (input_settings)); refresh_input_settings (input_settings); }muffin-6.4.1/src/backends/meta-screen-cast-stream.c0000664000175000017500000002751014723361714021100 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-stream.h" #include "backends/meta-screen-cast-session.h" #define META_SCREEN_CAST_STREAM_DBUS_IFACE "org.cinnamon.Muffin.ScreenCast.Stream" #define META_SCREEN_CAST_STREAM_DBUS_PATH "/org/cinnamon/Muffin/ScreenCast/Stream" enum { PROP_0, PROP_SESSION, PROP_CONNECTION, PROP_CURSOR_MODE, }; enum { CLOSED, N_SIGNALS }; static guint signals[N_SIGNALS]; typedef struct _MetaScreenCastStreamPrivate { MetaScreenCastSession *session; GDBusConnection *connection; char *object_path; MetaScreenCastCursorMode cursor_mode; MetaScreenCastStreamSrc *src; } MetaScreenCastStreamPrivate; static void meta_screen_cast_stream_init_initable_iface (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStream, meta_screen_cast_stream, META_DBUS_TYPE_SCREEN_CAST_STREAM_SKELETON, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, meta_screen_cast_stream_init_initable_iface) G_ADD_PRIVATE (MetaScreenCastStream)) static MetaScreenCastStreamSrc * meta_screen_cast_stream_create_src (MetaScreenCastStream *stream, GError **error) { return META_SCREEN_CAST_STREAM_GET_CLASS (stream)->create_src (stream, error); } static void meta_screen_cast_stream_set_parameters (MetaScreenCastStream *stream, GVariantBuilder *parameters_builder) { META_SCREEN_CAST_STREAM_GET_CLASS (stream)->set_parameters (stream, parameters_builder); } static void on_stream_src_closed (MetaScreenCastStreamSrc *src, MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); if (priv->src) meta_screen_cast_stream_close (stream); } static void on_stream_src_ready (MetaScreenCastStreamSrc *src, uint32_t node_id, MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); GDBusConnection *connection = priv->connection; char *peer_name; peer_name = meta_screen_cast_session_get_peer_name (priv->session); g_dbus_connection_emit_signal (connection, peer_name, priv->object_path, META_SCREEN_CAST_STREAM_DBUS_IFACE, "PipeWireStreamAdded", g_variant_new ("(u)", node_id), NULL); } MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); return priv->session; } gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream, GError **error) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); MetaScreenCastStreamSrc *src; src = meta_screen_cast_stream_create_src (stream, error); if (!src) return FALSE; priv->src = src; g_signal_connect (src, "ready", G_CALLBACK (on_stream_src_ready), stream); g_signal_connect (src, "closed", G_CALLBACK (on_stream_src_closed), stream); return TRUE; } void meta_screen_cast_stream_close (MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); g_clear_object (&priv->src); g_signal_emit (stream, signals[CLOSED], 0); } char * meta_screen_cast_stream_get_object_path (MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); return priv->object_path; } void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream, double stream_x, double stream_y, double *x, double *y) { META_SCREEN_CAST_STREAM_GET_CLASS (stream)->transform_position (stream, stream_x, stream_y, x, y); } MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream) { MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); return priv->cursor_mode; } static void meta_screen_cast_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaScreenCastStream *stream = META_SCREEN_CAST_STREAM (object); MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); switch (prop_id) { case PROP_SESSION: priv->session = g_value_get_object (value); break; case PROP_CONNECTION: priv->connection = g_value_get_object (value); break; case PROP_CURSOR_MODE: priv->cursor_mode = g_value_get_uint (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaScreenCastStream *stream = META_SCREEN_CAST_STREAM (object); MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); switch (prop_id) { case PROP_SESSION: g_value_set_object (value, priv->session); break; case PROP_CONNECTION: g_value_set_object (value, priv->connection); break; case PROP_CURSOR_MODE: g_value_set_uint (value, priv->cursor_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_stream_finalize (GObject *object) { MetaScreenCastStream *stream = META_SCREEN_CAST_STREAM (object); MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); if (priv->src) meta_screen_cast_stream_close (stream); g_clear_pointer (&priv->object_path, g_free); G_OBJECT_CLASS (meta_screen_cast_stream_parent_class)->finalize (object); } static gboolean meta_screen_cast_stream_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaScreenCastStream *stream = META_SCREEN_CAST_STREAM (initable); MetaDBusScreenCastStream *skeleton = META_DBUS_SCREEN_CAST_STREAM (stream); MetaScreenCastStreamPrivate *priv = meta_screen_cast_stream_get_instance_private (stream); GVariantBuilder parameters_builder; GVariant *parameters_variant; static unsigned int global_stream_number = 0; g_variant_builder_init (¶meters_builder, G_VARIANT_TYPE_VARDICT); meta_screen_cast_stream_set_parameters (stream, ¶meters_builder); parameters_variant = g_variant_builder_end (¶meters_builder); meta_dbus_screen_cast_stream_set_parameters (skeleton, parameters_variant); priv->object_path = g_strdup_printf (META_SCREEN_CAST_STREAM_DBUS_PATH "/u%u", ++global_stream_number); if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (stream), priv->connection, priv->object_path, error)) return FALSE; return TRUE; } static void meta_screen_cast_stream_init_initable_iface (GInitableIface *iface) { iface->init = meta_screen_cast_stream_initable_init; } static void meta_screen_cast_stream_init (MetaScreenCastStream *stream) { } static void meta_screen_cast_stream_class_init (MetaScreenCastStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_screen_cast_stream_finalize; object_class->set_property = meta_screen_cast_stream_set_property; object_class->get_property = meta_screen_cast_stream_get_property; g_object_class_install_property (object_class, PROP_SESSION, g_param_spec_object ("session", "session", "MetaScreenSession", META_TYPE_SCREEN_CAST_SESSION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CONNECTION, g_param_spec_object ("connection", "connection", "GDBus connection", G_TYPE_DBUS_CONNECTION, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_CURSOR_MODE, g_param_spec_uint ("cursor-mode", "cursor-mode", "Cursor mode", META_SCREEN_CAST_CURSOR_MODE_HIDDEN, META_SCREEN_CAST_CURSOR_MODE_METADATA, META_SCREEN_CAST_CURSOR_MODE_HIDDEN, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/backends/meta-screen-cast.h0000664000175000017500000000341314723361714017610 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_H #define META_SCREEN_CAST_H #include #include "backends/meta-backend-private.h" #include "backends/meta-dbus-session-watcher.h" #include "meta-dbus-screen-cast.h" typedef enum _MetaScreenCastCursorMode { META_SCREEN_CAST_CURSOR_MODE_HIDDEN = 0, META_SCREEN_CAST_CURSOR_MODE_EMBEDDED = 1, META_SCREEN_CAST_CURSOR_MODE_METADATA = 2, } MetaScreenCastCursorMode; #define META_TYPE_SCREEN_CAST (meta_screen_cast_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCast, meta_screen_cast, META, SCREEN_CAST, MetaDBusScreenCastSkeleton) GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast); MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast); MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, MetaDbusSessionWatcher *session_watcher); #endif /* META_SCREEN_CAST_H */ muffin-6.4.1/src/backends/meta-cursor-sprite-xcursor.h0000664000175000017500000000322314723361714021724 0ustar fabiofabio/* * Copyright 2013, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_CURSOR_SPRITE_XCURSOR_H #define META_CURSOR_SPRITE_XCURSOR_H #include #include #include "backends/meta-cursor.h" #define META_TYPE_CURSOR_SPRITE_XCURSOR meta_cursor_sprite_xcursor_get_type () G_DECLARE_FINAL_TYPE (MetaCursorSpriteXcursor, meta_cursor_sprite_xcursor, META, CURSOR_SPRITE_XCURSOR, MetaCursorSprite) MetaCursorSpriteXcursor * meta_cursor_sprite_xcursor_new (MetaCursor cursor); void meta_cursor_sprite_xcursor_set_theme_scale (MetaCursorSpriteXcursor *sprite_xcursor, int scale); MetaCursor meta_cursor_sprite_xcursor_get_cursor (MetaCursorSpriteXcursor *sprite_xcusror); XcursorImage * meta_cursor_sprite_xcursor_get_current_image (MetaCursorSpriteXcursor *sprite_xcursor); Cursor meta_create_x_cursor (Display *xdisplay, MetaCursor cursor); #endif /* META_CURSOR_SPRITE_XCURSOR_H */ muffin-6.4.1/src/backends/meta-dbus-session-watcher.c0000664000175000017500000001531114723361714021445 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-dbus-session-watcher.h" #include enum { SESSION_SIGNAL_SESSION_CLOSED, N_SESSION_SIGNALS }; static guint session_signals[N_SESSION_SIGNALS]; G_DEFINE_INTERFACE (MetaDbusSession, meta_dbus_session, G_TYPE_OBJECT) struct _MetaDbusSessionWatcher { GObject parent; GHashTable *clients; }; G_DEFINE_TYPE (MetaDbusSessionWatcher, meta_dbus_session_watcher, G_TYPE_OBJECT) typedef struct _MetaDbusSessionClient { MetaDbusSessionWatcher *session_watcher; MetaDbusSession *session; char *dbus_name; guint name_watcher_id; GList *sessions; } MetaDbusSessionClient; static void meta_dbus_session_client_vanished (MetaDbusSession *session) { META_DBUS_SESSION_GET_IFACE (session)->client_vanished (session); } static void meta_dbus_session_client_destroy (MetaDbusSessionClient *client) { while (TRUE) { GList *l; MetaDbusSession *session; l = client->sessions; if (!l) break; session = l->data; /* * This will invoke on_session_closed which removes the session from the * list. */ meta_dbus_session_client_vanished (session); } if (client->name_watcher_id) g_bus_unwatch_name (client->name_watcher_id); g_free (client->dbus_name); g_free (client); } static void meta_dbus_session_watcher_destroy_client (MetaDbusSessionWatcher *session_watcher, MetaDbusSessionClient *client) { g_hash_table_remove (session_watcher->clients, client->dbus_name); } static void name_vanished_callback (GDBusConnection *connection, const char *name, gpointer user_data) { MetaDbusSessionClient *client = user_data; g_warning ("D-Bus client with active sessions vanished"); client->name_watcher_id = 0; meta_dbus_session_watcher_destroy_client (client->session_watcher, client); } static MetaDbusSessionClient * meta_dbus_session_client_new (MetaDbusSessionWatcher *session_watcher, MetaDbusSession *session, const char *dbus_name) { GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); GDBusConnection *connection = g_dbus_interface_skeleton_get_connection (interface_skeleton); MetaDbusSessionClient *client; client = g_new0 (MetaDbusSessionClient, 1); client->session_watcher = session_watcher; client->session = session; client->dbus_name = g_strdup (dbus_name); client->name_watcher_id = g_bus_watch_name_on_connection (connection, dbus_name, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, name_vanished_callback, client, NULL); return client; } static void on_session_closed (MetaDbusSession *session, MetaDbusSessionClient *client) { client->sessions = g_list_remove (client->sessions, session); if (!client->sessions) meta_dbus_session_watcher_destroy_client (client->session_watcher, client); } static void meta_dbus_session_client_add_session (MetaDbusSessionClient *client, MetaDbusSession *session) { client->sessions = g_list_append (client->sessions, session); g_signal_connect (session, "session-closed", G_CALLBACK (on_session_closed), client); } static MetaDbusSessionClient * meta_dbus_session_watcher_get_client (MetaDbusSessionWatcher *session_watcher, const char *dbus_name) { return g_hash_table_lookup (session_watcher->clients, dbus_name); } void meta_dbus_session_watcher_watch_session (MetaDbusSessionWatcher *session_watcher, const char *client_dbus_name, MetaDbusSession *session) { MetaDbusSessionClient *client; client = meta_dbus_session_watcher_get_client (session_watcher, client_dbus_name); if (!client) { client = meta_dbus_session_client_new (session_watcher, session, client_dbus_name); g_hash_table_insert (session_watcher->clients, g_strdup (client_dbus_name), client); } meta_dbus_session_client_add_session (client, session); } void meta_dbus_session_notify_closed (MetaDbusSession *session) { g_signal_emit (session, session_signals[SESSION_SIGNAL_SESSION_CLOSED], 0); } static void meta_dbus_session_default_init (MetaDbusSessionInterface *iface) { session_signals[SESSION_SIGNAL_SESSION_CLOSED] = g_signal_new ("session-closed", G_TYPE_FROM_INTERFACE (iface), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_dbus_session_watcher_finalize (GObject *object) { MetaDbusSessionWatcher *session_watcher = META_DBUS_SESSION_WATCHER (object); g_hash_table_destroy (session_watcher->clients); G_OBJECT_CLASS (meta_dbus_session_watcher_parent_class)->finalize (object); } static void meta_dbus_session_watcher_init (MetaDbusSessionWatcher *session_watcher) { session_watcher->clients = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify) meta_dbus_session_client_destroy); } static void meta_dbus_session_watcher_class_init (MetaDbusSessionWatcherClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_dbus_session_watcher_finalize; } muffin-6.4.1/src/backends/meta-output.c0000664000175000017500000000670714723361714016745 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-output.h" typedef struct _MetaOutputPrivate { /* The CRTC driving this output, NULL if the output is not enabled */ MetaCrtc *crtc; } MetaOutputPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaOutput, meta_output, G_TYPE_OBJECT) MetaGpu * meta_output_get_gpu (MetaOutput *output) { return output->gpu; } void meta_output_assign_crtc (MetaOutput *output, MetaCrtc *crtc) { MetaOutputPrivate *priv = meta_output_get_instance_private (output); g_assert (crtc); g_set_object (&priv->crtc, crtc); } void meta_output_unassign_crtc (MetaOutput *output) { MetaOutputPrivate *priv = meta_output_get_instance_private (output); g_clear_object (&priv->crtc); } MetaCrtc * meta_output_get_assigned_crtc (MetaOutput *output) { MetaOutputPrivate *priv = meta_output_get_instance_private (output); return priv->crtc; } MetaMonitorTransform meta_output_logical_to_crtc_transform (MetaOutput *output, MetaMonitorTransform transform) { MetaMonitorTransform panel_orientation_transform; panel_orientation_transform = output->panel_orientation_transform; return meta_monitor_transform_transform (transform, panel_orientation_transform); } MetaMonitorTransform meta_output_crtc_to_logical_transform (MetaOutput *output, MetaMonitorTransform transform) { MetaMonitorTransform inverted_panel_orientation_transform; inverted_panel_orientation_transform = meta_monitor_transform_invert (output->panel_orientation_transform); return meta_monitor_transform_transform (transform, inverted_panel_orientation_transform); } static void meta_output_dispose (GObject *object) { MetaOutput *output = META_OUTPUT (object); MetaOutputPrivate *priv = meta_output_get_instance_private (output); g_clear_object (&priv->crtc); G_OBJECT_CLASS (meta_output_parent_class)->dispose (object); } static void meta_output_finalize (GObject *object) { MetaOutput *output = META_OUTPUT (object); g_free (output->name); g_free (output->vendor); g_free (output->product); g_free (output->serial); g_free (output->modes); g_free (output->possible_crtcs); g_free (output->possible_clones); if (output->driver_notify) output->driver_notify (output); G_OBJECT_CLASS (meta_output_parent_class)->finalize (object); } static void meta_output_init (MetaOutput *output) { } static void meta_output_class_init (MetaOutputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_output_dispose; object_class->finalize = meta_output_finalize; } muffin-6.4.1/src/backends/meta-egl.h0000664000175000017500000003126114723361714016152 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_EGL_H #define META_EGL_H #include #include #include #include #define META_EGL_ERROR meta_egl_error_quark () #define META_TYPE_EGL (meta_egl_get_type ()) G_DECLARE_FINAL_TYPE (MetaEgl, meta_egl, META, EGL, GObject) GQuark meta_egl_error_quark (void); gboolean meta_extensions_string_has_extensions_valist (const char *extensions_str, const char ***missing_extensions, const char *first_extension, va_list var_args); gboolean meta_egl_has_extensions (MetaEgl *egl, EGLDisplay display, const char ***missing_extensions, const char *first_extension, ...); gboolean meta_egl_initialize (MetaEgl *egl, EGLDisplay display, GError **error); gpointer meta_egl_get_proc_address (MetaEgl *egl, const char *procname, GError **error); gboolean meta_egl_choose_first_config (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, EGLConfig *chosen_config, GError **error); gboolean meta_egl_get_config_attrib (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLint attribute, EGLint *value, GError **error); EGLConfig * meta_egl_choose_all_configs (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, EGLint *out_num_configs, GError **error); EGLContext meta_egl_create_context (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLContext share_context, const EGLint *attrib_list, GError **error); gboolean meta_egl_destroy_context (MetaEgl *egl, EGLDisplay display, EGLContext context, GError **error); EGLImageKHR meta_egl_create_image (MetaEgl *egl, EGLDisplay display, EGLContext context, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list, GError **error); gboolean meta_egl_destroy_image (MetaEgl *egl, EGLDisplay display, EGLImageKHR image, GError **error); EGLImageKHR meta_egl_create_dmabuf_image (MetaEgl *egl, EGLDisplay egl_display, unsigned int width, unsigned int height, uint32_t drm_format, uint32_t n_planes, const int *fds, const uint32_t *strides, const uint32_t *offsets, const uint64_t *modifiers, GError **error); EGLSurface meta_egl_create_window_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLNativeWindowType native_window_type, const EGLint *attrib_list, GError **error); EGLSurface meta_egl_create_pbuffer_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, const EGLint *attrib_list, GError **error); gboolean meta_egl_destroy_surface (MetaEgl *egl, EGLDisplay display, EGLSurface surface, GError **error); EGLDisplay meta_egl_get_platform_display (MetaEgl *egl, EGLenum platform, void *native_display, const EGLint *attrib_list, GError **error); gboolean meta_egl_terminate (MetaEgl *egl, EGLDisplay display, GError **error); gboolean meta_egl_make_current (MetaEgl *egl, EGLDisplay display, EGLSurface draw, EGLSurface read, EGLContext context, GError **error); gboolean meta_egl_swap_buffers (MetaEgl *egl, EGLDisplay display, EGLSurface surface, GError **error); gboolean meta_egl_query_wayland_buffer (MetaEgl *egl, EGLDisplay display, struct wl_resource *buffer, EGLint attribute, EGLint *value, GError **error); gboolean meta_egl_query_devices (MetaEgl *egl, EGLint max_devices, EGLDeviceEXT *devices, EGLint *num_devices, GError **error); const char * meta_egl_query_device_string (MetaEgl *egl, EGLDeviceEXT device, EGLint name, GError **error); gboolean meta_egl_egl_device_has_extensions (MetaEgl *egl, EGLDeviceEXT device, const char ***missing_extensions, const char *first_extension, ...); gboolean meta_egl_get_output_layers (MetaEgl *egl, EGLDisplay display, const EGLAttrib *attrib_list, EGLOutputLayerEXT *layers, EGLint max_layers, EGLint *num_layers, GError **error); gboolean meta_egl_query_output_layer_attrib (MetaEgl *egl, EGLDisplay display, EGLOutputLayerEXT layer, EGLint attribute, EGLAttrib *value, GError **error); EGLStreamKHR meta_egl_create_stream (MetaEgl *egl, EGLDisplay display, const EGLint *attrib_list, GError **error); gboolean meta_egl_destroy_stream (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error); gboolean meta_egl_query_stream (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLenum attribute, EGLint *value, GError **error); EGLStreamKHR meta_egl_create_stream_attrib (MetaEgl *egl, EGLDisplay display, const EGLAttrib *attrib_list, GError **error); EGLSurface meta_egl_create_stream_producer_surface (MetaEgl *egl, EGLDisplay display, EGLConfig config, EGLStreamKHR stream, const EGLint *attrib_list, GError **error); gboolean meta_egl_stream_consumer_output (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLOutputLayerEXT layer, GError **error); gboolean meta_egl_stream_consumer_acquire_attrib (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, EGLAttrib *attrib_list, GError **error); gboolean meta_egl_stream_consumer_acquire (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error); gboolean meta_egl_stream_consumer_gl_texture_external (MetaEgl *egl, EGLDisplay display, EGLStreamKHR stream, GError **error); gboolean meta_egl_query_dma_buf_formats (MetaEgl *egl, EGLDisplay display, EGLint max_formats, EGLint *formats, EGLint *num_formats, GError **error); gboolean meta_egl_query_dma_buf_modifiers (MetaEgl *egl, EGLDisplay display, EGLint format, EGLint max_modifiers, EGLuint64KHR *modifiers, EGLBoolean *external_only, EGLint *num_formats, GError **error); #endif /* META_EGL_H */ muffin-6.4.1/src/backends/meta-monitor-config-manager.h0000664000175000017500000002030414723361714021741 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_MONITOR_CONFIG_MANAGER_H #define META_MONITOR_CONFIG_MANAGER_H #include "backends/meta-monitor.h" #include "backends/meta-monitor-manager-private.h" #define META_TYPE_MONITOR_CONFIG_MANAGER (meta_monitor_config_manager_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorConfigManager, meta_monitor_config_manager, META, MONITOR_CONFIG_MANAGER, GObject) typedef struct _MetaMonitorConfig { MetaMonitorSpec *monitor_spec; MetaMonitorModeSpec *mode_spec; gboolean enable_underscanning; } MetaMonitorConfig; typedef struct _MetaLogicalMonitorConfig { MetaRectangle layout; GList *monitor_configs; MetaMonitorTransform transform; float scale; gboolean is_primary; gboolean is_presentation; } MetaLogicalMonitorConfig; typedef struct _MetaMonitorsConfigKey { GList *monitor_specs; } MetaMonitorsConfigKey; typedef enum _MetaMonitorsConfigFlag { META_MONITORS_CONFIG_FLAG_NONE = 0, META_MONITORS_CONFIG_FLAG_MIGRATED = (1 << 0), META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG = (1 << 1), } MetaMonitorsConfigFlag; struct _MetaMonitorsConfig { GObject parent; MetaMonitorsConfigKey *key; GList *logical_monitor_configs; GList *disabled_monitor_specs; MetaMonitorsConfigFlag flags; MetaLogicalMonitorLayoutMode layout_mode; MetaMonitorSwitchConfigType switch_config; }; #define META_TYPE_MONITORS_CONFIG (meta_monitors_config_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorsConfig, meta_monitors_config, META, MONITORS_CONFIG, GObject) META_EXPORT_TEST MetaMonitorConfigManager * meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager); META_EXPORT_TEST MetaMonitorConfigStore * meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager, MetaMonitorsConfig *config, GPtrArray **crtc_infos, GPtrArray **output_infos, GError **error); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_for_orientation (MetaMonitorConfigManager *config_manager, MetaMonitorTransform transform); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager *config_manager); MetaMonitorsConfig * meta_monitor_config_manager_create_for_layout (MetaMonitorConfigManager *config_manager, MetaMonitorsConfig *config, MetaLogicalMonitorLayoutMode layout_mode); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager *config_manager, MetaMonitorSwitchConfigType config_type); META_EXPORT_TEST void meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager, MetaMonitorsConfig *config); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_get_current (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_pop_previous (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_manager_get_previous (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST void meta_monitor_config_manager_clear_history (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST void meta_monitor_config_manager_save_current (MetaMonitorConfigManager *config_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitors_config_new_full (GList *logical_monitor_configs, GList *disabled_monitors, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags); META_EXPORT_TEST MetaMonitorsConfig * meta_monitors_config_new (MetaMonitorManager *monitor_manager, GList *logical_monitor_configs, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags); META_EXPORT_TEST MetaMonitorSwitchConfigType meta_monitors_config_get_switch_config (MetaMonitorsConfig *config); META_EXPORT_TEST void meta_monitors_config_set_switch_config (MetaMonitorsConfig *config, MetaMonitorSwitchConfigType switch_config); META_EXPORT_TEST unsigned int meta_monitors_config_key_hash (gconstpointer config_key); META_EXPORT_TEST gboolean meta_monitors_config_key_equal (gconstpointer config_key_a, gconstpointer config_key_b); META_EXPORT_TEST void meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key); META_EXPORT_TEST void meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config); META_EXPORT_TEST void meta_monitor_config_free (MetaMonitorConfig *monitor_config); META_EXPORT_TEST MetaMonitorsConfigKey * meta_create_monitors_config_key_for_current_state (MetaMonitorManager *monitor_manager); META_EXPORT_TEST gboolean meta_logical_monitor_configs_have_monitor (GList *logical_monitor_configs, MetaMonitorSpec *monitor_spec); META_EXPORT_TEST gboolean meta_verify_monitor_mode_spec (MetaMonitorModeSpec *monitor_mode_spec, GError **error); META_EXPORT_TEST gboolean meta_verify_monitor_spec (MetaMonitorSpec *monitor_spec, GError **error); META_EXPORT_TEST gboolean meta_verify_monitor_config (MetaMonitorConfig *monitor_config, GError **error); META_EXPORT_TEST gboolean meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorManager *monitor_manager, float max_scale, GError **error); META_EXPORT_TEST gboolean meta_verify_monitors_config (MetaMonitorsConfig *config, MetaMonitorManager *monitor_manager, GError **error); #endif /* META_MONITOR_CONFIG_MANAGER_H */ muffin-6.4.1/src/backends/meta-input-device-private.h0000664000175000017500000000265214723361714021451 0ustar fabiofabio/* * Copyright © 2020 Red Hat Ltd. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_DEVICE_H #define META_INPUT_DEVICE_H #include #ifdef HAVE_LIBWACOM #include #endif #include "clutter/clutter-mutter.h" typedef struct _MetaInputDeviceClass MetaInputDeviceClass; typedef struct _MetaInputDevice MetaInputDevice; struct _MetaInputDeviceClass { ClutterInputDeviceClass parent_class; }; #define META_TYPE_INPUT_DEVICE (meta_input_device_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaInputDevice, meta_input_device, META, INPUT_DEVICE, ClutterInputDevice) #ifdef HAVE_LIBWACOM WacomDevice * meta_input_device_get_wacom_device (MetaInputDevice *input_device); #endif #endif /* META_INPUT_DEVICE_H */ muffin-6.4.1/src/backends/meta-stage.c0000664000175000017500000003067514723361714016511 0ustar fabiofabio/* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "backends/meta-stage-private.h" #include "backends/meta-backend-private.h" #include "clutter/clutter-mutter.h" #include "meta/meta-backend.h" #include "meta/meta-monitor-manager.h" #include "meta/util.h" #define N_WATCH_MODES 4 enum { ACTORS_PAINTED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaStageWatch { ClutterStageView *view; MetaStageWatchFunc callback; gpointer user_data; }; struct _MetaOverlay { gboolean enabled; CoglPipeline *pipeline; CoglTexture *texture; graphene_rect_t current_rect; graphene_rect_t previous_rect; gboolean previous_is_valid; }; struct _MetaStage { ClutterStage parent; GPtrArray *watchers[N_WATCH_MODES]; GList *overlays; gboolean is_active; }; G_DEFINE_TYPE (MetaStage, meta_stage, CLUTTER_TYPE_STAGE); static MetaOverlay * meta_overlay_new (void) { MetaOverlay *overlay; CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); overlay = g_slice_new0 (MetaOverlay); overlay->pipeline = cogl_pipeline_new (ctx); return overlay; } static void meta_overlay_free (MetaOverlay *overlay) { if (overlay->pipeline) cogl_object_unref (overlay->pipeline); g_slice_free (MetaOverlay, overlay); } static void meta_overlay_set (MetaOverlay *overlay, CoglTexture *texture, graphene_rect_t *rect) { if (overlay->texture != texture) { overlay->texture = texture; if (texture) { cogl_pipeline_set_layer_texture (overlay->pipeline, 0, texture); overlay->enabled = TRUE; } else { cogl_pipeline_set_layer_texture (overlay->pipeline, 0, NULL); overlay->enabled = FALSE; } } overlay->current_rect = *rect; } static void meta_overlay_paint (MetaOverlay *overlay, ClutterPaintContext *paint_context) { CoglFramebuffer *framebuffer; if (!overlay->enabled) return; g_assert (meta_is_wayland_compositor ()); framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_draw_rectangle (framebuffer, overlay->pipeline, overlay->current_rect.origin.x, overlay->current_rect.origin.y, (overlay->current_rect.origin.x + overlay->current_rect.size.width), (overlay->current_rect.origin.y + overlay->current_rect.size.height)); if (!graphene_rect_equal (&overlay->previous_rect, &overlay->current_rect)) { overlay->previous_rect = overlay->current_rect; overlay->previous_is_valid = TRUE; } } static void meta_stage_finalize (GObject *object) { MetaStage *stage = META_STAGE (object); GList *l; int i; l = stage->overlays; while (l) { meta_overlay_free (l->data); l = g_list_delete_link (l, l); } for (i = 0; i < N_WATCH_MODES; i++) g_clear_pointer (&stage->watchers[i], g_ptr_array_unref); G_OBJECT_CLASS (meta_stage_parent_class)->finalize (object); } static void notify_watchers_for_mode (MetaStage *stage, ClutterStageView *view, ClutterPaintContext *paint_context, MetaStageWatchPhase watch_phase) { GPtrArray *watchers; int i; watchers = stage->watchers[watch_phase]; for (i = 0; i < watchers->len; i++) { MetaStageWatch *watch = g_ptr_array_index (watchers, i); if (watch->view && view != watch->view) continue; watch->callback (stage, view, paint_context, watch->user_data); } } static void meta_stage_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { MetaStage *stage = META_STAGE (actor); ClutterStageView *view; GList *l; CLUTTER_ACTOR_CLASS (meta_stage_parent_class)->paint (actor, paint_context); view = clutter_paint_context_get_stage_view (paint_context); if (view) { notify_watchers_for_mode (stage, view, paint_context, META_STAGE_WATCH_AFTER_ACTOR_PAINT); } if (!(clutter_paint_context_get_paint_flags (paint_context) & CLUTTER_PAINT_FLAG_NO_PAINT_SIGNAL)) g_signal_emit (stage, signals[ACTORS_PAINTED], 0); if (!(clutter_paint_context_get_paint_flags (paint_context) & CLUTTER_PAINT_FLAG_NO_CURSORS)) { for (l = stage->overlays; l; l = l->next) meta_overlay_paint (l->data, paint_context); } if (view) { notify_watchers_for_mode (stage, view, paint_context, META_STAGE_WATCH_AFTER_OVERLAY_PAINT); } } static void meta_stage_paint_view (ClutterStage *stage, ClutterStageView *view, const cairo_region_t *redraw_clip) { MetaStage *meta_stage = META_STAGE (stage); notify_watchers_for_mode (meta_stage, view, NULL, META_STAGE_WATCH_BEFORE_PAINT); CLUTTER_STAGE_CLASS (meta_stage_parent_class)->paint_view (stage, view, redraw_clip); notify_watchers_for_mode (meta_stage, view, NULL, META_STAGE_WATCH_AFTER_PAINT); } static void meta_stage_activate (ClutterStage *actor) { MetaStage *stage = META_STAGE (actor); CLUTTER_STAGE_CLASS (meta_stage_parent_class)->activate (actor); stage->is_active = TRUE; } static void meta_stage_deactivate (ClutterStage *actor) { MetaStage *stage = META_STAGE (actor); CLUTTER_STAGE_CLASS (meta_stage_parent_class)->deactivate (actor); stage->is_active = FALSE; } static void on_power_save_changed (MetaMonitorManager *monitor_manager, MetaStage *stage) { if (meta_monitor_manager_get_power_save_mode (monitor_manager) == META_POWER_SAVE_ON) clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } static void meta_stage_class_init (MetaStageClass *klass) { ClutterStageClass *stage_class = (ClutterStageClass *) klass; ClutterActorClass *actor_class = (ClutterActorClass *) klass; GObjectClass *object_class = (GObjectClass *) klass; object_class->finalize = meta_stage_finalize; actor_class->paint = meta_stage_paint; stage_class->activate = meta_stage_activate; stage_class->deactivate = meta_stage_deactivate; stage_class->paint_view = meta_stage_paint_view; signals[ACTORS_PAINTED] = g_signal_new ("actors-painted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_stage_init (MetaStage *stage) { int i; for (i = 0; i < N_WATCH_MODES; i++) stage->watchers[i] = g_ptr_array_new_with_free_func (g_free); } ClutterActor * meta_stage_new (MetaBackend *backend) { MetaStage *stage; MetaMonitorManager *monitor_manager; stage = g_object_new (META_TYPE_STAGE, "cursor-visible", FALSE, NULL); monitor_manager = meta_backend_get_monitor_manager (backend); g_signal_connect (monitor_manager, "power-save-mode-changed", G_CALLBACK (on_power_save_changed), stage); return CLUTTER_ACTOR (stage); } static void queue_redraw_clutter_rect (MetaStage *stage, MetaOverlay *overlay, graphene_rect_t *rect) { cairo_rectangle_int_t clip = { .x = floorf (rect->origin.x), .y = floorf (rect->origin.y), .width = ceilf (rect->size.width), .height = ceilf (rect->size.height) }; /* Since we're flooring the coordinates, we need to enlarge the clip by the * difference between the actual coordinate and the floored value */ clip.width += ceilf (rect->origin.x - clip.x) * 2; clip.height += ceilf (rect->origin.y - clip.y) * 2; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); } static void queue_redraw_for_overlay (MetaStage *stage, MetaOverlay *overlay) { /* Clear the location the overlay was at before, if we need to. */ if (overlay->previous_is_valid) { queue_redraw_clutter_rect (stage, overlay, &overlay->previous_rect); overlay->previous_is_valid = FALSE; } /* Draw the overlay at the new position */ if (overlay->enabled) queue_redraw_clutter_rect (stage, overlay, &overlay->current_rect); } MetaOverlay * meta_stage_create_cursor_overlay (MetaStage *stage) { MetaOverlay *overlay; overlay = meta_overlay_new (); stage->overlays = g_list_prepend (stage->overlays, overlay); return overlay; } void meta_stage_remove_cursor_overlay (MetaStage *stage, MetaOverlay *overlay) { GList *link; link = g_list_find (stage->overlays, overlay); if (!link) return; stage->overlays = g_list_delete_link (stage->overlays, link); meta_overlay_free (overlay); } void meta_stage_update_cursor_overlay (MetaStage *stage, MetaOverlay *overlay, CoglTexture *texture, graphene_rect_t *rect) { g_assert (meta_is_wayland_compositor () || texture == NULL); meta_overlay_set (overlay, texture, rect); queue_redraw_for_overlay (stage, overlay); } void meta_stage_set_active (MetaStage *stage, gboolean is_active) { ClutterEvent *event; /* Used by the native backend to inform accessibility technologies * about when the stage loses and gains input focus. * * For the X11 backend, clutter transparently takes care of this * for us. */ if (stage->is_active == is_active) return; event = clutter_event_new (CLUTTER_STAGE_STATE); clutter_event_set_stage (event, CLUTTER_STAGE (stage)); event->stage_state.changed_mask = CLUTTER_STAGE_STATE_ACTIVATED; if (is_active) event->stage_state.new_state = CLUTTER_STAGE_STATE_ACTIVATED; /* Emitting this StageState event will result in the stage getting * activated or deactivated (with the activated or deactivated signal * getting emitted from the stage) * * FIXME: This won't update ClutterStage's own notion of its * activeness. For that we would need to somehow trigger a * _clutter_stage_update_state call, which will probably * require new API in clutter. In practice, nothing relies * on the ClutterStage's own notion of activeness when using * the EGL backend. * * See http://bugzilla.gnome.org/746670 */ clutter_stage_event (CLUTTER_STAGE (stage), event); clutter_event_free (event); } MetaStageWatch * meta_stage_watch_view (MetaStage *stage, ClutterStageView *view, MetaStageWatchPhase watch_phase, MetaStageWatchFunc callback, gpointer user_data) { MetaStageWatch *watch; GPtrArray *watchers; watch = g_new0 (MetaStageWatch, 1); watch->view = view; watch->callback = callback; watch->user_data = user_data; watchers = stage->watchers[watch_phase]; g_ptr_array_add (watchers, watch); return watch; } void meta_stage_remove_watch (MetaStage *stage, MetaStageWatch *watch) { GPtrArray *watchers; gboolean removed = FALSE; int i; for (i = 0; i < N_WATCH_MODES; i++) { watchers = stage->watchers[i]; removed = g_ptr_array_remove_fast (watchers, watch); if (removed) break; } g_assert (removed); } muffin-6.4.1/src/backends/meta-screen-cast-window-stream-src.c0000664000175000017500000005107614723361714023176 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-window-stream-src.h" #include "backends/meta-backend-private.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-screen-cast-window.h" #include "backends/meta-screen-cast-window-stream.h" #include "compositor/meta-window-actor-private.h" struct _MetaScreenCastWindowStreamSrc { MetaScreenCastStreamSrc parent; MetaScreenCastWindow *screen_cast_window; unsigned long screen_cast_window_damaged_handler_id; unsigned long screen_cast_window_destroyed_handler_id; unsigned long cursor_moved_handler_id; unsigned long cursor_changed_handler_id; gboolean cursor_bitmap_invalid; }; G_DEFINE_TYPE (MetaScreenCastWindowStreamSrc, meta_screen_cast_window_stream_src, META_TYPE_SCREEN_CAST_STREAM_SRC) static MetaBackend * get_backend (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); return meta_screen_cast_get_backend (screen_cast); } static MetaScreenCastWindowStream * get_window_stream (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastStreamSrc *src; MetaScreenCastStream *stream; src = META_SCREEN_CAST_STREAM_SRC (window_src); stream = meta_screen_cast_stream_src_get_stream (src); return META_SCREEN_CAST_WINDOW_STREAM (stream); } static MetaWindow * get_window (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastWindowStream *window_stream; window_stream = get_window_stream (window_src); return meta_screen_cast_window_stream_get_window (window_stream); } static int get_stream_width (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastWindowStream *window_stream; window_stream = get_window_stream (window_src); return meta_screen_cast_window_stream_get_width (window_stream); } static int get_stream_height (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastWindowStream *window_stream; window_stream = get_window_stream (window_src); return meta_screen_cast_window_stream_get_height (window_stream); } static void maybe_draw_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, uint8_t *data, MetaRectangle *stream_rect) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); MetaBackend *backend = get_backend (window_src); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaCursorSprite *cursor_sprite; CoglTexture *cursor_texture; MetaScreenCastWindow *screen_cast_window; graphene_point_t cursor_position; graphene_point_t relative_cursor_position; cairo_surface_t *cursor_surface; uint8_t *cursor_surface_data; GError *error = NULL; cairo_surface_t *stream_surface; int width, height; float scale; int hotspot_x, hotspot_y; cairo_t *cr; cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); if (!cursor_sprite) return; cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!cursor_texture) return; screen_cast_window = window_src->screen_cast_window; cursor_position = meta_cursor_renderer_get_position (cursor_renderer); if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window, cursor_sprite, &cursor_position, &scale, &relative_cursor_position)) return; meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); width = cogl_texture_get_width (cursor_texture) * scale; height = cogl_texture_get_height (cursor_texture) * scale; cursor_surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, width, height); cursor_surface_data = cairo_image_surface_get_data (cursor_surface); if (!meta_screen_cast_stream_src_draw_cursor_into (src, cursor_texture, scale, cursor_surface_data, &error)) { g_warning ("Failed to draw cursor: %s", error->message); g_error_free (error); cairo_surface_destroy (cursor_surface); return; } stream_surface = cairo_image_surface_create_for_data (data, CAIRO_FORMAT_ARGB32, stream_rect->width, stream_rect->height, stream_rect->width * 4); cr = cairo_create (stream_surface); cairo_surface_mark_dirty (cursor_surface); cairo_surface_flush (cursor_surface); cairo_set_source_surface (cr, cursor_surface, relative_cursor_position.x - hotspot_x * scale, relative_cursor_position.y - hotspot_y * scale); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (stream_surface); cairo_surface_destroy (cursor_surface); } static void maybe_blit_cursor_sprite (MetaScreenCastWindowStreamSrc *window_src, CoglFramebuffer *framebuffer, MetaRectangle *stream_rect) { MetaBackend *backend = get_backend (window_src); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_get_default_backend ()); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaScreenCastWindow *screen_cast_window; MetaCursorSprite *cursor_sprite; graphene_point_t relative_cursor_position; graphene_point_t cursor_position; CoglTexture *cursor_texture; CoglPipeline *pipeline; int width, height; float scale; int hotspot_x, hotspot_y; float x, y; cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); if (!cursor_sprite) return; cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!cursor_texture) return; screen_cast_window = window_src->screen_cast_window; cursor_position = meta_cursor_renderer_get_position (cursor_renderer); if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window, cursor_sprite, &cursor_position, &scale, &relative_cursor_position)) return; meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); x = (relative_cursor_position.x - hotspot_x) * scale; y = (relative_cursor_position.y - hotspot_y) * scale; width = cogl_texture_get_width (cursor_texture); height = cogl_texture_get_height (cursor_texture); pipeline = cogl_pipeline_new (cogl_context); cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, x, y, x + width, y + height); cogl_object_unref (pipeline); } static gboolean capture_into (MetaScreenCastWindowStreamSrc *window_src, uint8_t *data) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); MetaRectangle stream_rect; MetaScreenCastStream *stream; stream_rect.x = 0; stream_rect.y = 0; stream_rect.width = get_stream_width (window_src); stream_rect.height = get_stream_height (window_src); meta_screen_cast_window_capture_into (window_src->screen_cast_window, &stream_rect, data); stream = meta_screen_cast_stream_src_get_stream (src); switch (meta_screen_cast_stream_get_cursor_mode (stream)) { case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: maybe_draw_cursor_sprite (window_src, data, &stream_rect); break; case META_SCREEN_CAST_CURSOR_MODE_METADATA: case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: break; } return TRUE; } static void meta_screen_cast_window_stream_src_get_specs (MetaScreenCastStreamSrc *src, int *width, int *height, float *frame_rate) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); *width = get_stream_width (window_src); *height = get_stream_height (window_src); *frame_rate = 60.0f; } static gboolean meta_screen_cast_window_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, MetaRectangle *crop_rect) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); MetaRectangle stream_rect; meta_screen_cast_window_get_buffer_bounds (window_src->screen_cast_window, crop_rect); stream_rect.x = 0; stream_rect.y = 0; stream_rect.width = get_stream_width (window_src); stream_rect.height = get_stream_height (window_src); meta_rectangle_intersect (crop_rect, &stream_rect, crop_rect); return TRUE; } static void meta_screen_cast_window_stream_src_stop (MetaScreenCastWindowStreamSrc *window_src) { MetaBackend *backend = get_backend (window_src); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); if (!window_src->screen_cast_window) return; g_clear_signal_handler (&window_src->screen_cast_window_damaged_handler_id, window_src->screen_cast_window); g_clear_signal_handler (&window_src->screen_cast_window_destroyed_handler_id, window_src->screen_cast_window); g_clear_signal_handler (&window_src->cursor_moved_handler_id, cursor_tracker); g_clear_signal_handler (&window_src->cursor_changed_handler_id, cursor_tracker); } static void screen_cast_window_damaged (MetaWindowActor *actor, MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); MetaScreenCastRecordFlag flags; flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static void screen_cast_window_destroyed (MetaWindowActor *actor, MetaScreenCastWindowStreamSrc *window_src) { meta_screen_cast_window_stream_src_stop (window_src); window_src->screen_cast_window = NULL; } static void sync_cursor_state (MetaScreenCastWindowStreamSrc *window_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (window_src); MetaScreenCastRecordFlag flags; if (meta_screen_cast_window_has_damage (window_src->screen_cast_window)) return; flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static void cursor_moved (MetaCursorTracker *cursor_tracker, float x, float y, MetaScreenCastWindowStreamSrc *window_src) { sync_cursor_state (window_src); } static void cursor_changed (MetaCursorTracker *cursor_tracker, MetaScreenCastWindowStreamSrc *window_src) { window_src->cursor_bitmap_invalid = TRUE; sync_cursor_state (window_src); } static void meta_screen_cast_window_stream_src_enable (MetaScreenCastStreamSrc *src) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); MetaBackend *backend = get_backend (window_src); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); MetaWindowActor *window_actor; MetaScreenCastStream *stream; MetaScreenCastRecordFlag flags; window_actor = meta_window_actor_from_window (get_window (window_src)); if (!window_actor) return; window_src->screen_cast_window = META_SCREEN_CAST_WINDOW (window_actor); window_src->screen_cast_window_damaged_handler_id = g_signal_connect (window_src->screen_cast_window, "damaged", G_CALLBACK (screen_cast_window_damaged), window_src); window_src->screen_cast_window_destroyed_handler_id = g_signal_connect (window_src->screen_cast_window, "destroy", G_CALLBACK (screen_cast_window_destroyed), window_src); stream = meta_screen_cast_stream_src_get_stream (src); switch (meta_screen_cast_stream_get_cursor_mode (stream)) { case META_SCREEN_CAST_CURSOR_MODE_METADATA: case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: window_src->cursor_moved_handler_id = g_signal_connect_after (cursor_tracker, "cursor-moved", G_CALLBACK (cursor_moved), window_src); window_src->cursor_changed_handler_id = g_signal_connect_after (cursor_tracker, "cursor-changed", G_CALLBACK (cursor_changed), window_src); break; case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: break; } flags = META_SCREEN_CAST_RECORD_FLAG_NONE; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static void meta_screen_cast_window_stream_src_disable (MetaScreenCastStreamSrc *src) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); meta_screen_cast_window_stream_src_stop (window_src); } static gboolean meta_screen_cast_window_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, uint8_t *data, GError **error) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); capture_into (window_src, data); return TRUE; } static gboolean meta_screen_cast_window_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, CoglFramebuffer *framebuffer, GError **error) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); MetaScreenCastStream *stream; MetaRectangle stream_rect; stream_rect.x = 0; stream_rect.y = 0; stream_rect.width = get_stream_width (window_src); stream_rect.height = get_stream_height (window_src); if (!meta_screen_cast_window_blit_to_framebuffer (window_src->screen_cast_window, &stream_rect, framebuffer)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to blit window content to framebuffer"); return FALSE; } stream = meta_screen_cast_stream_src_get_stream (src); switch (meta_screen_cast_stream_get_cursor_mode (stream)) { case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: maybe_blit_cursor_sprite (window_src, framebuffer, &stream_rect); break; case META_SCREEN_CAST_CURSOR_MODE_METADATA: case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: break; } cogl_framebuffer_finish (framebuffer); return TRUE; } static void meta_screen_cast_window_stream_record_follow_up (MetaScreenCastStreamSrc *src) { MetaScreenCastRecordFlag flags; flags = META_SCREEN_CAST_RECORD_FLAG_NONE; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static void meta_screen_cast_window_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) { MetaScreenCastWindowStreamSrc *window_src = META_SCREEN_CAST_WINDOW_STREAM_SRC (src); MetaBackend *backend = get_backend (window_src); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaScreenCastWindow *screen_cast_window = window_src->screen_cast_window; MetaCursorSprite *cursor_sprite; graphene_point_t cursor_position; float scale; graphene_point_t relative_cursor_position; int x, y; cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); cursor_position = meta_cursor_renderer_get_position (cursor_renderer); if (!meta_screen_cast_window_transform_cursor_position (screen_cast_window, cursor_sprite, &cursor_position, &scale, &relative_cursor_position)) { meta_screen_cast_stream_src_unset_cursor_metadata (src, spa_meta_cursor); return; } x = (int) roundf (relative_cursor_position.x); y = (int) roundf (relative_cursor_position.y); if (window_src->cursor_bitmap_invalid) { if (cursor_sprite) { meta_screen_cast_stream_src_set_cursor_sprite_metadata (src, spa_meta_cursor, cursor_sprite, x, y, scale); } else { meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src, spa_meta_cursor, x, y); } window_src->cursor_bitmap_invalid = FALSE; } else { meta_screen_cast_stream_src_set_cursor_position_metadata (src, spa_meta_cursor, x, y); } } MetaScreenCastWindowStreamSrc * meta_screen_cast_window_stream_src_new (MetaScreenCastWindowStream *window_stream, GError **error) { return g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM_SRC, NULL, error, "stream", window_stream, NULL); } static void meta_screen_cast_window_stream_src_init (MetaScreenCastWindowStreamSrc *window_src) { window_src->cursor_bitmap_invalid = TRUE; } static void meta_screen_cast_window_stream_src_class_init (MetaScreenCastWindowStreamSrcClass *klass) { MetaScreenCastStreamSrcClass *src_class = META_SCREEN_CAST_STREAM_SRC_CLASS (klass); src_class->get_specs = meta_screen_cast_window_stream_src_get_specs; src_class->enable = meta_screen_cast_window_stream_src_enable; src_class->disable = meta_screen_cast_window_stream_src_disable; src_class->record_to_buffer = meta_screen_cast_window_stream_src_record_to_buffer; src_class->record_to_framebuffer = meta_screen_cast_window_stream_src_record_to_framebuffer; src_class->record_follow_up = meta_screen_cast_window_stream_record_follow_up; src_class->get_videocrop = meta_screen_cast_window_stream_src_get_videocrop; src_class->set_cursor_metadata = meta_screen_cast_window_stream_src_set_cursor_metadata; } muffin-6.4.1/src/backends/meta-profiler.h0000664000175000017500000000231014723361714017216 0ustar fabiofabio/* * Copyright (C) 2019 Endless, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_PROFILER_H #define META_PROFILER_H #include #include "meta-dbus-sysprof3-profiler.h" G_BEGIN_DECLS #define META_TYPE_PROFILER (meta_profiler_get_type()) G_DECLARE_FINAL_TYPE (MetaProfiler, meta_profiler, META, PROFILER, MetaDBusSysprof3ProfilerSkeleton) MetaProfiler * meta_profiler_new (void); G_END_DECLS #endif /* META_PROFILER_H */ muffin-6.4.1/src/backends/meta-monitor-config-store.c0000664000175000017500000014263714723361714021474 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-monitor-config-store.h" #include #include #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-config-migration.h" #define MONITORS_CONFIG_XML_FORMAT_VERSION 2 #define QUOTE1(a) #a #define QUOTE(a) QUOTE1(a) /* * Example configuration: * * * * * 0 * 0 * 1 * * * LVDS1 * Vendor A * Product A * Serial A * * * 1920 * 1080 * 60.049972534179688 * interlace * * * * right * no * * yes * no * * * 1920 * 1080 * * * LVDS2 * Vendor B * Product B * Serial B * * * 1920 * 1080 * 60.049972534179688 * * yes * * yes * * * * LVDS3 * Vendor C * Product C * Serial C * * * * * */ enum { PROP_0, PROP_MONITOR_MANAGER, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _MetaMonitorConfigStore { GObject parent; MetaMonitorManager *monitor_manager; GHashTable *configs; GCancellable *save_cancellable; GFile *user_file; GFile *custom_read_file; GFile *custom_write_file; }; #define META_MONITOR_CONFIG_STORE_ERROR (meta_monitor_config_store_error_quark ()) static GQuark meta_monitor_config_store_error_quark (void); enum { META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION }; G_DEFINE_QUARK (meta-monitor-config-store-error-quark, meta_monitor_config_store_error) typedef enum { STATE_INITIAL, STATE_MONITORS, STATE_CONFIGURATION, STATE_MIGRATED, STATE_LOGICAL_MONITOR, STATE_LOGICAL_MONITOR_X, STATE_LOGICAL_MONITOR_Y, STATE_LOGICAL_MONITOR_PRIMARY, STATE_LOGICAL_MONITOR_PRESENTATION, STATE_LOGICAL_MONITOR_SCALE, STATE_TRANSFORM, STATE_TRANSFORM_ROTATION, STATE_TRANSFORM_FLIPPED, STATE_MONITOR, STATE_MONITOR_SPEC, STATE_MONITOR_SPEC_CONNECTOR, STATE_MONITOR_SPEC_VENDOR, STATE_MONITOR_SPEC_PRODUCT, STATE_MONITOR_SPEC_SERIAL, STATE_MONITOR_MODE, STATE_MONITOR_MODE_WIDTH, STATE_MONITOR_MODE_HEIGHT, STATE_MONITOR_MODE_RATE, STATE_MONITOR_MODE_FLAG, STATE_MONITOR_UNDERSCANNING, STATE_DISABLED, } ParserState; typedef struct { ParserState state; MetaMonitorConfigStore *config_store; ParserState monitor_spec_parent_state; gboolean current_was_migrated; GList *current_logical_monitor_configs; MetaMonitorSpec *current_monitor_spec; gboolean current_transform_flipped; MetaMonitorTransform current_transform; MetaMonitorModeSpec *current_monitor_mode_spec; MetaMonitorConfig *current_monitor_config; MetaLogicalMonitorConfig *current_logical_monitor_config; GList *current_disabled_monitor_specs; MetaMonitorsConfigFlag extra_config_flags; } ConfigParser; G_DEFINE_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, G_TYPE_OBJECT) static void handle_start_element (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_INITIAL: { char *version; if (!g_str_equal (element_name, "monitors")) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid document element '%s'", element_name); return; } if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version, G_MARKUP_COLLECT_INVALID)) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Missing config file format version"); } if (g_str_equal (version, "1")) { g_set_error_literal (error, META_MONITOR_CONFIG_STORE_ERROR, META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION, "monitors.xml has the old format"); return; } if (!g_str_equal (version, QUOTE (MONITORS_CONFIG_XML_FORMAT_VERSION))) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid or unsupported version '%s'", version); return; } parser->state = STATE_MONITORS; return; } case STATE_MONITORS: { if (!g_str_equal (element_name, "configuration")) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid toplevel element '%s'", element_name); return; } parser->state = STATE_CONFIGURATION; parser->current_was_migrated = FALSE; return; } case STATE_CONFIGURATION: { if (g_str_equal (element_name, "logicalmonitor")) { parser->current_logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); parser->state = STATE_LOGICAL_MONITOR; } else if (g_str_equal (element_name, "migrated")) { parser->current_was_migrated = TRUE; parser->state = STATE_MIGRATED; } else if (g_str_equal (element_name, "disabled")) { parser->state = STATE_DISABLED; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid configuration element '%s'", element_name); return; } return; } case STATE_MIGRATED: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Unexpected element '%s'", element_name); return; } case STATE_LOGICAL_MONITOR: { if (g_str_equal (element_name, "x")) { parser->state = STATE_LOGICAL_MONITOR_X; } else if (g_str_equal (element_name, "y")) { parser->state = STATE_LOGICAL_MONITOR_Y; } else if (g_str_equal (element_name, "scale")) { parser->state = STATE_LOGICAL_MONITOR_SCALE; } else if (g_str_equal (element_name, "primary")) { parser->state = STATE_LOGICAL_MONITOR_PRIMARY; } else if (g_str_equal (element_name, "presentation")) { parser->state = STATE_LOGICAL_MONITOR_PRESENTATION; } else if (g_str_equal (element_name, "transform")) { parser->state = STATE_TRANSFORM; } else if (g_str_equal (element_name, "monitor")) { parser->current_monitor_config = g_new0 (MetaMonitorConfig, 1);; parser->state = STATE_MONITOR; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid monitor logicalmonitor element '%s'", element_name); return; } return; } case STATE_LOGICAL_MONITOR_X: case STATE_LOGICAL_MONITOR_Y: case STATE_LOGICAL_MONITOR_SCALE: case STATE_LOGICAL_MONITOR_PRIMARY: case STATE_LOGICAL_MONITOR_PRESENTATION: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid logical monitor element '%s'", element_name); return; } case STATE_TRANSFORM: { if (g_str_equal (element_name, "rotation")) { parser->state = STATE_TRANSFORM_ROTATION; } else if (g_str_equal (element_name, "flipped")) { parser->state = STATE_TRANSFORM_FLIPPED; } return; } case STATE_TRANSFORM_ROTATION: case STATE_TRANSFORM_FLIPPED: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid transform element '%s'", element_name); return; } case STATE_MONITOR: { if (g_str_equal (element_name, "monitorspec")) { parser->current_monitor_spec = g_new0 (MetaMonitorSpec, 1); parser->monitor_spec_parent_state = STATE_MONITOR; parser->state = STATE_MONITOR_SPEC; } else if (g_str_equal (element_name, "mode")) { parser->current_monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1); parser->state = STATE_MONITOR_MODE; } else if (g_str_equal (element_name, "underscanning")) { parser->state = STATE_MONITOR_UNDERSCANNING; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid monitor element '%s'", element_name); return; } return; } case STATE_MONITOR_SPEC: { if (g_str_equal (element_name, "connector")) { parser->state = STATE_MONITOR_SPEC_CONNECTOR; } else if (g_str_equal (element_name, "vendor")) { parser->state = STATE_MONITOR_SPEC_VENDOR; } else if (g_str_equal (element_name, "product")) { parser->state = STATE_MONITOR_SPEC_PRODUCT; } else if (g_str_equal (element_name, "serial")) { parser->state = STATE_MONITOR_SPEC_SERIAL; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid monitor spec element '%s'", element_name); return; } return; } case STATE_MONITOR_SPEC_CONNECTOR: case STATE_MONITOR_SPEC_VENDOR: case STATE_MONITOR_SPEC_PRODUCT: case STATE_MONITOR_SPEC_SERIAL: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid monitor spec element '%s'", element_name); return; } case STATE_MONITOR_MODE: { if (g_str_equal (element_name, "width")) { parser->state = STATE_MONITOR_MODE_WIDTH; } else if (g_str_equal (element_name, "height")) { parser->state = STATE_MONITOR_MODE_HEIGHT; } else if (g_str_equal (element_name, "rate")) { parser->state = STATE_MONITOR_MODE_RATE; } else if (g_str_equal (element_name, "flag")) { parser->state = STATE_MONITOR_MODE_FLAG; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid mode element '%s'", element_name); return; } return; } case STATE_MONITOR_MODE_WIDTH: case STATE_MONITOR_MODE_HEIGHT: case STATE_MONITOR_MODE_RATE: case STATE_MONITOR_MODE_FLAG: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid mode sub element '%s'", element_name); return; } case STATE_MONITOR_UNDERSCANNING: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid element '%s' under underscanning", element_name); return; } case STATE_DISABLED: { if (!g_str_equal (element_name, "monitorspec")) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid element '%s' under disabled", element_name); return; } parser->current_monitor_spec = g_new0 (MetaMonitorSpec, 1); parser->monitor_spec_parent_state = STATE_DISABLED; parser->state = STATE_MONITOR_SPEC; return; } } } static gboolean derive_logical_monitor_layout (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, float max_scale, GError **error) { MetaMonitorConfig *monitor_config; int mode_width, mode_height; int width = 0, height = 0; GList *l; monitor_config = logical_monitor_config->monitor_configs->data; mode_width = monitor_config->mode_spec->width; mode_height = monitor_config->mode_spec->height; for (l = logical_monitor_config->monitor_configs->next; l; l = l->next) { monitor_config = l->data; if (monitor_config->mode_spec->width != mode_width || monitor_config->mode_spec->height != mode_height) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitors in logical monitor incompatible"); return FALSE; } } if (meta_monitor_transform_is_rotated (logical_monitor_config->transform)) { width = mode_height; height = mode_width; } else { width = mode_width; height = mode_height; } switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: width *= ceilf (max_scale); height *= ceilf (max_scale); /* fall through! */ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: width = roundf (width / logical_monitor_config->scale); height = roundf (height / logical_monitor_config->scale); break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } logical_monitor_config->layout.width = width; logical_monitor_config->layout.height = height; return TRUE; } static void finish_monitor_spec (ConfigParser *parser) { switch (parser->monitor_spec_parent_state) { case STATE_MONITOR: { parser->current_monitor_config->monitor_spec = parser->current_monitor_spec; parser->current_monitor_spec = NULL; return; } case STATE_DISABLED: { parser->current_disabled_monitor_specs = g_list_prepend (parser->current_disabled_monitor_specs, parser->current_monitor_spec); parser->current_monitor_spec = NULL; return; } default: g_assert_not_reached (); } } static void handle_end_element (GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_LOGICAL_MONITOR_X: case STATE_LOGICAL_MONITOR_Y: case STATE_LOGICAL_MONITOR_SCALE: case STATE_LOGICAL_MONITOR_PRIMARY: case STATE_LOGICAL_MONITOR_PRESENTATION: { parser->state = STATE_LOGICAL_MONITOR; return; } case STATE_TRANSFORM: { g_assert (g_str_equal (element_name, "transform")); parser->current_logical_monitor_config->transform = parser->current_transform; if (parser->current_transform_flipped) { parser->current_logical_monitor_config->transform += META_MONITOR_TRANSFORM_FLIPPED; } parser->current_transform = META_MONITOR_TRANSFORM_NORMAL; parser->current_transform_flipped = FALSE; parser->state = STATE_LOGICAL_MONITOR; return; } case STATE_TRANSFORM_ROTATION: case STATE_TRANSFORM_FLIPPED: { parser->state = STATE_TRANSFORM; return; } case STATE_MONITOR_SPEC_CONNECTOR: case STATE_MONITOR_SPEC_VENDOR: case STATE_MONITOR_SPEC_PRODUCT: case STATE_MONITOR_SPEC_SERIAL: { parser->state = STATE_MONITOR_SPEC; return; } case STATE_MONITOR_SPEC: { g_assert (g_str_equal (element_name, "monitorspec")); if (!meta_verify_monitor_spec (parser->current_monitor_spec, error)) return; finish_monitor_spec (parser); parser->state = parser->monitor_spec_parent_state; return; } case STATE_MONITOR_MODE_WIDTH: case STATE_MONITOR_MODE_HEIGHT: case STATE_MONITOR_MODE_RATE: case STATE_MONITOR_MODE_FLAG: { parser->state = STATE_MONITOR_MODE; return; } case STATE_MONITOR_MODE: { g_assert (g_str_equal (element_name, "mode")); if (!meta_verify_monitor_mode_spec (parser->current_monitor_mode_spec, error)) return; parser->current_monitor_config->mode_spec = parser->current_monitor_mode_spec; parser->current_monitor_mode_spec = NULL; parser->state = STATE_MONITOR; return; } case STATE_MONITOR_UNDERSCANNING: { g_assert (g_str_equal (element_name, "underscanning")); parser->state = STATE_MONITOR; return; } case STATE_MONITOR: { MetaLogicalMonitorConfig *logical_monitor_config; g_assert (g_str_equal (element_name, "monitor")); if (!meta_verify_monitor_config (parser->current_monitor_config, error)) return; logical_monitor_config = parser->current_logical_monitor_config; logical_monitor_config->monitor_configs = g_list_append (logical_monitor_config->monitor_configs, parser->current_monitor_config); parser->current_monitor_config = NULL; parser->state = STATE_LOGICAL_MONITOR; return; } case STATE_LOGICAL_MONITOR: { MetaLogicalMonitorConfig *logical_monitor_config = parser->current_logical_monitor_config; g_assert (g_str_equal (element_name, "logicalmonitor")); if (parser->current_was_migrated) logical_monitor_config->scale = -1; else if (logical_monitor_config->scale == 0) logical_monitor_config->scale = 1; parser->current_logical_monitor_configs = g_list_append (parser->current_logical_monitor_configs, logical_monitor_config); parser->current_logical_monitor_config = NULL; parser->state = STATE_CONFIGURATION; return; } case STATE_MIGRATED: { g_assert (g_str_equal (element_name, "migrated")); parser->state = STATE_CONFIGURATION; return; } case STATE_DISABLED: { g_assert (g_str_equal (element_name, "disabled")); parser->state = STATE_CONFIGURATION; return; } case STATE_CONFIGURATION: { MetaMonitorConfigStore *store = parser->config_store; MetaMonitorsConfig *config; GList *l; MetaLogicalMonitorLayoutMode layout_mode; MetaMonitorsConfigFlag config_flags = META_MONITORS_CONFIG_FLAG_NONE; float max_scale = 1.0f; g_assert (g_str_equal (element_name, "configuration")); if (parser->current_was_migrated) layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; else layout_mode = meta_monitor_manager_get_default_layout_mode (store->monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { for (l = parser->current_logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; max_scale = MAX (max_scale, logical_monitor_config->scale); } } for (l = parser->current_logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (!derive_logical_monitor_layout (logical_monitor_config, layout_mode, max_scale, error)) return; if (!meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, store->monitor_manager, max_scale, error)) return; } if (parser->current_was_migrated) config_flags |= META_MONITORS_CONFIG_FLAG_MIGRATED; config_flags |= parser->extra_config_flags; config = meta_monitors_config_new_full (parser->current_logical_monitor_configs, parser->current_disabled_monitor_specs, layout_mode, config_flags); parser->current_logical_monitor_configs = NULL; parser->current_disabled_monitor_specs = NULL; if (!meta_verify_monitors_config (config, store->monitor_manager, error)) { g_object_unref (config); return; } g_hash_table_replace (parser->config_store->configs, config->key, config); parser->state = STATE_MONITORS; return; } case STATE_MONITORS: { g_assert (g_str_equal (element_name, "monitors")); parser->state = STATE_INITIAL; return; } case STATE_INITIAL: { g_assert_not_reached (); } } } static gboolean read_int (const char *text, gsize text_len, gint *out_value, GError **error) { char buf[64]; int64_t value; char *end; strncpy (buf, text, text_len); buf[MIN (63, text_len)] = 0; value = g_ascii_strtoll (buf, &end, 10); if (*end || value < 0 || value > G_MAXINT16) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Expected a number, got %s", buf); return FALSE; } else { *out_value = value; return TRUE; } } static gboolean read_float (const char *text, gsize text_len, float *out_value, GError **error) { char buf[64]; float value; char *end; strncpy (buf, text, text_len); buf[MIN (63, text_len)] = 0; value = g_ascii_strtod (buf, &end); if (*end) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Expected a number, got %s", buf); return FALSE; } else { *out_value = value; return TRUE; } } static gboolean read_bool (const char *text, gsize text_len, gboolean *out_value, GError **error) { if (strncmp (text, "no", text_len) == 0) { *out_value = FALSE; return TRUE; } else if (strncmp (text, "yes", text_len) == 0) { *out_value = TRUE; return TRUE; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid boolean value '%.*s'", (int) text_len, text); return FALSE; } } static gboolean is_all_whitespace (const char *text, gsize text_len) { gsize i; for (i = 0; i < text_len; i++) if (!g_ascii_isspace (text[i])) return FALSE; return TRUE; } static void handle_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_INITIAL: case STATE_MONITORS: case STATE_CONFIGURATION: case STATE_MIGRATED: case STATE_LOGICAL_MONITOR: case STATE_MONITOR: case STATE_MONITOR_SPEC: case STATE_MONITOR_MODE: case STATE_TRANSFORM: case STATE_DISABLED: { if (!is_all_whitespace (text, text_len)) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unexpected content at this point"); return; } case STATE_MONITOR_SPEC_CONNECTOR: { parser->current_monitor_spec->connector = g_strndup (text, text_len); return; } case STATE_MONITOR_SPEC_VENDOR: { parser->current_monitor_spec->vendor = g_strndup (text, text_len); return; } case STATE_MONITOR_SPEC_PRODUCT: { parser->current_monitor_spec->product = g_strndup (text, text_len); return; } case STATE_MONITOR_SPEC_SERIAL: { parser->current_monitor_spec->serial = g_strndup (text, text_len); return; } case STATE_LOGICAL_MONITOR_X: { read_int (text, text_len, &parser->current_logical_monitor_config->layout.x, error); return; } case STATE_LOGICAL_MONITOR_Y: { read_int (text, text_len, &parser->current_logical_monitor_config->layout.y, error); return; } case STATE_LOGICAL_MONITOR_SCALE: { if (!read_float (text, text_len, &parser->current_logical_monitor_config->scale, error)) return; if (parser->current_logical_monitor_config->scale <= 0.0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitor scale '%g' invalid", parser->current_logical_monitor_config->scale); return; } return; } case STATE_LOGICAL_MONITOR_PRIMARY: { read_bool (text, text_len, &parser->current_logical_monitor_config->is_primary, error); return; } case STATE_LOGICAL_MONITOR_PRESENTATION: { read_bool (text, text_len, &parser->current_logical_monitor_config->is_presentation, error); return; } case STATE_TRANSFORM_ROTATION: { if (strncmp (text, "normal", text_len) == 0) parser->current_transform = META_MONITOR_TRANSFORM_NORMAL; else if (strncmp (text, "left", text_len) == 0) parser->current_transform = META_MONITOR_TRANSFORM_90; else if (strncmp (text, "upside_down", text_len) == 0) parser->current_transform = META_MONITOR_TRANSFORM_180; else if (strncmp (text, "right", text_len) == 0) parser->current_transform = META_MONITOR_TRANSFORM_270; else g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid rotation type %.*s", (int)text_len, text); return; } case STATE_TRANSFORM_FLIPPED: { read_bool (text, text_len, &parser->current_transform_flipped, error); return; } case STATE_MONITOR_MODE_WIDTH: { read_int (text, text_len, &parser->current_monitor_mode_spec->width, error); return; } case STATE_MONITOR_MODE_HEIGHT: { read_int (text, text_len, &parser->current_monitor_mode_spec->height, error); return; } case STATE_MONITOR_MODE_RATE: { read_float (text, text_len, &parser->current_monitor_mode_spec->refresh_rate, error); return; } case STATE_MONITOR_MODE_FLAG: { if (strncmp (text, "interlace", text_len) == 0) { parser->current_monitor_mode_spec->flags |= META_CRTC_MODE_FLAG_INTERLACE; } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid mode flag %.*s", (int) text_len, text); } return; } case STATE_MONITOR_UNDERSCANNING: { read_bool (text, text_len, &parser->current_monitor_config->enable_underscanning, error); return; } } } static const GMarkupParser config_parser = { .start_element = handle_start_element, .end_element = handle_end_element, .text = handle_text }; static gboolean read_config_file (MetaMonitorConfigStore *config_store, GFile *file, MetaMonitorsConfigFlag extra_config_flags, GError **error) { char *buffer; gsize size; ConfigParser parser; GMarkupParseContext *parse_context; if (!g_file_load_contents (file, NULL, &buffer, &size, NULL, error)) return FALSE; parser = (ConfigParser) { .state = STATE_INITIAL, .config_store = config_store, .extra_config_flags = extra_config_flags, }; parse_context = g_markup_parse_context_new (&config_parser, G_MARKUP_TREAT_CDATA_AS_TEXT | G_MARKUP_PREFIX_ERROR_POSITION, &parser, NULL); if (!g_markup_parse_context_parse (parse_context, buffer, size, error)) { g_list_free_full (parser.current_logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); g_clear_pointer (&parser.current_monitor_spec, meta_monitor_spec_free); g_free (parser.current_monitor_mode_spec); g_clear_pointer (&parser.current_monitor_config, meta_monitor_config_free); g_clear_pointer (&parser.current_logical_monitor_config, meta_logical_monitor_config_free); return FALSE; } g_markup_parse_context_free (parse_context); g_free (buffer); return TRUE; } MetaMonitorsConfig * meta_monitor_config_store_lookup (MetaMonitorConfigStore *config_store, MetaMonitorsConfigKey *key) { return META_MONITORS_CONFIG (g_hash_table_lookup (config_store->configs, key)); } static void append_monitor_spec (GString *buffer, MetaMonitorSpec *monitor_spec, const char *indentation) { char *escaped; g_string_append_printf (buffer, "%s\n", indentation); escaped = g_markup_escape_text (monitor_spec->connector, -1); g_string_append_printf (buffer, "%s %s\n", indentation, escaped); g_free (escaped); escaped = g_markup_escape_text (monitor_spec->vendor, -1); g_string_append_printf (buffer, "%s %s\n", indentation, escaped); g_free (escaped); escaped = g_markup_escape_text (monitor_spec->product, -1); g_string_append_printf (buffer, "%s %s\n", indentation, escaped); g_free (escaped); escaped = g_markup_escape_text (monitor_spec->serial, -1); g_string_append_printf (buffer, "%s %s\n", indentation, escaped); g_free (escaped); g_string_append_printf (buffer, "%s\n", indentation); } static void append_monitors (GString *buffer, GList *monitor_configs) { GList *l; for (l = monitor_configs; l; l = l->next) { MetaMonitorConfig *monitor_config = l->data; char rate_str[G_ASCII_DTOSTR_BUF_SIZE]; g_ascii_dtostr (rate_str, sizeof (rate_str), monitor_config->mode_spec->refresh_rate); g_string_append (buffer, " \n"); append_monitor_spec (buffer, monitor_config->monitor_spec, " "); g_string_append (buffer, " \n"); g_string_append_printf (buffer, " %d\n", monitor_config->mode_spec->width); g_string_append_printf (buffer, " %d\n", monitor_config->mode_spec->height); g_string_append_printf (buffer, " %s\n", rate_str); if (monitor_config->mode_spec->flags & META_CRTC_MODE_FLAG_INTERLACE) g_string_append_printf (buffer, " interlace\n"); g_string_append (buffer, " \n"); if (monitor_config->enable_underscanning) g_string_append (buffer, " yes\n"); g_string_append (buffer, " \n"); } } static const char * bool_to_string (gboolean value) { return value ? "yes" : "no"; } static void append_transform (GString *buffer, MetaMonitorTransform transform) { const char *rotation = NULL; gboolean flipped = FALSE; switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: return; case META_MONITOR_TRANSFORM_90: rotation = "left"; break; case META_MONITOR_TRANSFORM_180: rotation = "upside_down"; break; case META_MONITOR_TRANSFORM_270: rotation = "right"; break; case META_MONITOR_TRANSFORM_FLIPPED: rotation = "normal"; flipped = TRUE; break; case META_MONITOR_TRANSFORM_FLIPPED_90: rotation = "left"; flipped = TRUE; break; case META_MONITOR_TRANSFORM_FLIPPED_180: rotation = "upside_down"; flipped = TRUE; break; case META_MONITOR_TRANSFORM_FLIPPED_270: rotation = "right"; flipped = TRUE; break; } g_string_append (buffer, " \n"); g_string_append_printf (buffer, " %s\n", rotation); g_string_append_printf (buffer, " %s\n", bool_to_string (flipped)); g_string_append (buffer, " \n"); } static void append_logical_monitor_xml (GString *buffer, MetaMonitorsConfig *config, MetaLogicalMonitorConfig *logical_monitor_config) { char scale_str[G_ASCII_DTOSTR_BUF_SIZE]; g_string_append (buffer, " \n"); g_string_append_printf (buffer, " %d\n", logical_monitor_config->layout.x); g_string_append_printf (buffer, " %d\n", logical_monitor_config->layout.y); g_ascii_dtostr (scale_str, G_ASCII_DTOSTR_BUF_SIZE, logical_monitor_config->scale); if ((config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED) == 0) g_string_append_printf (buffer, " %s\n", scale_str); if (logical_monitor_config->is_primary) g_string_append (buffer, " yes\n"); if (logical_monitor_config->is_presentation) g_string_append (buffer, " yes\n"); append_transform (buffer, logical_monitor_config->transform); append_monitors (buffer, logical_monitor_config->monitor_configs); g_string_append (buffer, " \n"); } static GString * generate_config_xml (MetaMonitorConfigStore *config_store) { GString *buffer; GHashTableIter iter; MetaMonitorsConfig *config; buffer = g_string_new (""); g_string_append_printf (buffer, "\n", MONITORS_CONFIG_XML_FORMAT_VERSION); g_hash_table_iter_init (&iter, config_store->configs); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &config)) { GList *l; if (config->flags & META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG) continue; g_string_append (buffer, " \n"); if (config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED) g_string_append (buffer, " \n"); for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; append_logical_monitor_xml (buffer, config, logical_monitor_config); } if (config->disabled_monitor_specs) { g_string_append (buffer, " \n"); for (l = config->disabled_monitor_specs; l; l = l->next) { MetaMonitorSpec *monitor_spec = l->data; append_monitor_spec (buffer, monitor_spec, " "); } g_string_append (buffer, " \n"); } g_string_append (buffer, " \n"); } g_string_append (buffer, "\n"); return buffer; } typedef struct _SaveData { MetaMonitorConfigStore *config_store; GString *buffer; } SaveData; static void saved_cb (GObject *object, GAsyncResult *result, gpointer user_data) { SaveData *data = user_data; GError *error = NULL; if (!g_file_replace_contents_finish (G_FILE (object), result, NULL, &error)) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning ("Saving monitor configuration failed: %s\n", error->message); g_clear_object (&data->config_store->save_cancellable); } g_error_free (error); } else { g_clear_object (&data->config_store->save_cancellable); } g_clear_object (&data->config_store); g_string_free (data->buffer, TRUE); g_free (data); } static void meta_monitor_config_store_save_sync (MetaMonitorConfigStore *config_store) { GError *error = NULL; GFile *file; GString *buffer; if (config_store->custom_write_file) file = config_store->custom_write_file; else file = config_store->user_file; buffer = generate_config_xml (config_store); if (!g_file_replace_contents (file, buffer->str, buffer->len, NULL, FALSE, G_FILE_CREATE_REPLACE_DESTINATION, NULL, NULL, &error)) { g_warning ("Saving monitor configuration failed: %s\n", error->message); g_error_free (error); } g_string_free (buffer, TRUE); } static void meta_monitor_config_store_save (MetaMonitorConfigStore *config_store) { GString *buffer; SaveData *data; if (config_store->save_cancellable) { g_cancellable_cancel (config_store->save_cancellable); g_clear_object (&config_store->save_cancellable); } /* * Custom write file is only ever used by the test suite, and the test suite * will want to have be able to read back the content immediately, so for * custom write files, do the content replacement synchronously. */ if (config_store->custom_write_file) { meta_monitor_config_store_save_sync (config_store); return; } config_store->save_cancellable = g_cancellable_new (); buffer = generate_config_xml (config_store); data = g_new0 (SaveData, 1); *data = (SaveData) { .config_store = g_object_ref (config_store), .buffer = buffer }; g_file_replace_contents_async (config_store->user_file, buffer->str, buffer->len, NULL, TRUE, G_FILE_CREATE_REPLACE_DESTINATION, config_store->save_cancellable, saved_cb, data); } static void maybe_save_configs (MetaMonitorConfigStore *config_store) { /* * If a custom file is used, it means we are run by the test suite. When this * is done, avoid replacing the user configuration file with test data, * except if a custom write file is set as well. */ if (!config_store->custom_read_file || config_store->custom_write_file) meta_monitor_config_store_save (config_store); } static gboolean is_system_config (MetaMonitorsConfig *config) { return !!(config->flags & META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG); } void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config) { g_hash_table_replace (config_store->configs, config->key, g_object_ref (config)); if (!is_system_config (config)) maybe_save_configs (config_store); } void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config) { g_hash_table_remove (config_store->configs, config->key); if (!is_system_config (config)) maybe_save_configs (config_store); } gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, const char *read_path, const char *write_path, GError **error) { g_clear_object (&config_store->custom_read_file); g_clear_object (&config_store->custom_write_file); g_hash_table_remove_all (config_store->configs); config_store->custom_read_file = g_file_new_for_path (read_path); if (write_path) config_store->custom_write_file = g_file_new_for_path (write_path); return read_config_file (config_store, config_store->custom_read_file, META_MONITORS_CONFIG_FLAG_NONE, error); } int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store) { return (int) g_hash_table_size (config_store->configs); } MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store) { return config_store->monitor_manager; } MetaMonitorConfigStore * meta_monitor_config_store_new (MetaMonitorManager *monitor_manager) { return g_object_new (META_TYPE_MONITOR_CONFIG_STORE, "monitor-manager", monitor_manager, NULL); } static void meta_monitor_config_store_constructed (GObject *object) { MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object); const char * const *system_dirs; char *user_file_path; GError *error = NULL; for (system_dirs = g_get_system_config_dirs (); system_dirs && *system_dirs; system_dirs++) { g_autofree char *system_file_path = NULL; system_file_path = g_build_filename (*system_dirs, "monitors.xml", NULL); if (g_file_test (system_file_path, G_FILE_TEST_EXISTS)) { g_autoptr (GFile) system_file = NULL; system_file = g_file_new_for_path (system_file_path); if (!read_config_file (config_store, system_file, META_MONITORS_CONFIG_FLAG_SYSTEM_CONFIG, &error)) { if (g_error_matches (error, META_MONITOR_CONFIG_STORE_ERROR, META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION)) g_warning ("System monitor configuration file (%s) is " "incompatible; ask your administrator to migrate " "the system monitor configuration.", system_file_path); else g_warning ("Failed to read monitors config file '%s': %s", system_file_path, error->message); g_clear_error (&error); } } } user_file_path = g_build_filename (g_get_user_config_dir (), "cinnamon-monitors.xml", NULL); config_store->user_file = g_file_new_for_path (user_file_path); if (g_file_test (user_file_path, G_FILE_TEST_EXISTS)) { if (!read_config_file (config_store, config_store->user_file, META_MONITORS_CONFIG_FLAG_NONE, &error)) { if (error->domain == META_MONITOR_CONFIG_STORE_ERROR && error->code == META_MONITOR_CONFIG_STORE_ERROR_NEEDS_MIGRATION) { g_clear_error (&error); if (!meta_migrate_old_user_monitors_config (config_store, user_file_path, &error)) { g_warning ("Failed to migrate old monitors config file: %s", error->message); g_error_free (error); } } else { g_warning ("Failed to read monitors config file '%s': %s", user_file_path, error->message); g_error_free (error); } } } g_free (user_file_path); G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->constructed (object); } static void meta_monitor_config_store_dispose (GObject *object) { MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object); if (config_store->save_cancellable) { g_cancellable_cancel (config_store->save_cancellable); g_clear_object (&config_store->save_cancellable); meta_monitor_config_store_save_sync (config_store); } g_clear_pointer (&config_store->configs, g_hash_table_destroy); g_clear_object (&config_store->user_file); g_clear_object (&config_store->custom_read_file); g_clear_object (&config_store->custom_write_file); G_OBJECT_CLASS (meta_monitor_config_store_parent_class)->dispose (object); } static void meta_monitor_config_store_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object); switch (prop_id) { case PROP_MONITOR_MANAGER: g_value_set_object (value, &config_store->monitor_manager); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_monitor_config_store_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaMonitorConfigStore *config_store = META_MONITOR_CONFIG_STORE (object); switch (prop_id) { case PROP_MONITOR_MANAGER: config_store->monitor_manager = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_monitor_config_store_init (MetaMonitorConfigStore *config_store) { config_store->configs = g_hash_table_new_full (meta_monitors_config_key_hash, meta_monitors_config_key_equal, NULL, g_object_unref); } static void meta_monitor_config_store_class_init (MetaMonitorConfigStoreClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_monitor_config_store_constructed; object_class->dispose = meta_monitor_config_store_dispose; object_class->get_property = meta_monitor_config_store_get_property; object_class->set_property = meta_monitor_config_store_set_property; obj_props[PROP_MONITOR_MANAGER] = g_param_spec_object ("monitor-manager", "MetaMonitorManager", "MetaMonitorManager", META_TYPE_MONITOR_MANAGER, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/src/backends/meta-gles3.c0000664000175000017500000000745314723361714016421 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include #include "config.h" #include "backends/meta-gles3.h" #include #include #include "backends/meta-gles3-table.h" struct _MetaGles3 { GObject parent; MetaEgl *egl; MetaGles3Table table; }; G_DEFINE_TYPE (MetaGles3, meta_gles3, G_TYPE_OBJECT) MetaGles3Table * meta_gles3_get_table (MetaGles3 *gles3) { return &gles3->table; } void meta_gles3_ensure_loaded (MetaGles3 *gles3, gpointer *func, const char *name) { GError *error = NULL; if (*func) return; *func = meta_egl_get_proc_address (gles3->egl, name, &error); if (!*func) g_error ("Failed to load GLES3 symbol: %s", error->message); } static const char * get_gl_error_str (GLenum gl_error) { switch (gl_error) { case GL_NO_ERROR: return "No error has been recorded."; case GL_INVALID_ENUM: return "An unacceptable value is specified for an enumerated argument."; case GL_INVALID_VALUE: return "A numeric argument is out of range."; case GL_INVALID_OPERATION: return "The specified operation is not allowed in the current state."; case GL_INVALID_FRAMEBUFFER_OPERATION: return "The framebuffer object is not complete."; case GL_OUT_OF_MEMORY: return "There is not enough memory left to execute the command."; default: return "Unknown error"; } } void meta_gles3_clear_error (MetaGles3 *gles3) { while (TRUE) { GLenum gl_error = glGetError (); if (gl_error == GL_NO_ERROR) break; } } gboolean meta_gles3_validate (MetaGles3 *gles3, GError **error) { GLenum gl_error; gl_error = glGetError (); if (gl_error != GL_NO_ERROR) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, get_gl_error_str (gl_error)); return FALSE; } return TRUE; } gboolean meta_gles3_has_extensions (MetaGles3 *gles3, const char ***missing_extensions, const char *first_extension, ...) { va_list var_args; const char *extensions_str; gboolean has_extensions; extensions_str = (const char *) glGetString (GL_EXTENSIONS); if (!extensions_str) { g_warning ("Failed to get string: %s", get_gl_error_str (glGetError ())); return FALSE; } va_start (var_args, first_extension); has_extensions = meta_extensions_string_has_extensions_valist (extensions_str, missing_extensions, first_extension, var_args); va_end (var_args); return has_extensions; } MetaGles3 * meta_gles3_new (MetaEgl *egl) { MetaGles3 *gles3; gles3 = g_object_new (META_TYPE_GLES3, NULL); gles3->egl = egl; return gles3; } static void meta_gles3_init (MetaGles3 *gles3) { } static void meta_gles3_class_init (MetaGles3Class *klass) { } muffin-6.4.1/src/backends/meta-input-settings-private.h0000664000175000017500000002100714723361714022045 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_SETTINGS_PRIVATE_H #define META_INPUT_SETTINGS_PRIVATE_H #include #ifdef HAVE_LIBWACOM #include #endif #include "backends/meta-backend-types.h" #include "clutter/clutter.h" #include "meta/display.h" #define META_TYPE_INPUT_SETTINGS (meta_input_settings_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaInputSettings, meta_input_settings, META, INPUT_SETTINGS, GObject) struct _MetaInputSettingsClass { GObjectClass parent_class; void (* set_send_events) (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopDeviceSendEvents mode); void (* set_matrix) (MetaInputSettings *settings, ClutterInputDevice *device, gfloat matrix[6]); void (* set_speed) (MetaInputSettings *settings, ClutterInputDevice *device, gdouble speed); void (* set_left_handed) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_tap_enabled) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_tap_and_drag_enabled) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_disable_while_typing) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_invert_scroll) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean inverted); void (* set_edge_scroll) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_two_finger_scroll) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_scroll_button) (MetaInputSettings *settings, ClutterInputDevice *device, guint button); void (* set_click_method) (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTouchpadClickMethod mode); void (* set_keyboard_repeat) (MetaInputSettings *settings, gboolean repeat, guint delay, guint interval); void (* set_tablet_mapping) (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTabletMapping mapping); void (* set_tablet_keep_aspect) (MetaInputSettings *settings, ClutterInputDevice *device, MetaLogicalMonitor *logical_monitor, gboolean keep_aspect); void (* set_tablet_area) (MetaInputSettings *settings, ClutterInputDevice *device, gdouble padding_left, gdouble padding_right, gdouble padding_top, gdouble padding_bottom); void (* set_mouse_accel_profile) (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile); void (* set_trackball_accel_profile) (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile); void (* set_stylus_pressure) (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, const gint32 curve[4]); void (* set_stylus_button_map) (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, CDesktopStylusButtonAction primary, CDesktopStylusButtonAction secondary, CDesktopStylusButtonAction tertiary); void (* set_mouse_middle_click_emulation) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_touchpad_middle_click_emulation) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); void (* set_trackball_middle_click_emulation) (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled); gboolean (* has_two_finger_scroll) (MetaInputSettings *settings, ClutterInputDevice *device); gboolean (* is_trackball_device) (MetaInputSettings *settings, ClutterInputDevice *device); }; GSettings * meta_input_settings_get_tablet_settings (MetaInputSettings *settings, ClutterInputDevice *device); MetaLogicalMonitor * meta_input_settings_get_tablet_logical_monitor (MetaInputSettings *settings, ClutterInputDevice *device); CDesktopTabletMapping meta_input_settings_get_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device); gboolean meta_input_settings_is_pad_button_grabbed (MetaInputSettings *input_settings, ClutterInputDevice *pad, guint button); gboolean meta_input_settings_handle_pad_event (MetaInputSettings *input_settings, const ClutterEvent *event); gchar * meta_input_settings_get_pad_action_label (MetaInputSettings *input_settings, ClutterInputDevice *pad, MetaPadActionType action, guint number); void meta_input_settings_maybe_save_numlock_state (MetaInputSettings *input_settings); void meta_input_settings_maybe_restore_numlock_state (MetaInputSettings *input_settings); void meta_input_settings_refresh (MetaInputSettings *input_settings); #endif /* META_INPUT_SETTINGS_PRIVATE_H */ muffin-6.4.1/src/backends/meta-crtc.h0000664000175000017500000000611414723361714016335 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_CRTC_H #define META_CRTC_H #include #include "backends/meta-backend-types.h" #include "backends/meta-monitor-transform.h" #include "core/util-private.h" #include "meta/boxes.h" /* Same as KMS mode flags and X11 randr flags */ typedef enum _MetaCrtcModeFlag { META_CRTC_MODE_FLAG_NONE = 0, META_CRTC_MODE_FLAG_PHSYNC = (1 << 0), META_CRTC_MODE_FLAG_NHSYNC = (1 << 1), META_CRTC_MODE_FLAG_PVSYNC = (1 << 2), META_CRTC_MODE_FLAG_NVSYNC = (1 << 3), META_CRTC_MODE_FLAG_INTERLACE = (1 << 4), META_CRTC_MODE_FLAG_DBLSCAN = (1 << 5), META_CRTC_MODE_FLAG_CSYNC = (1 << 6), META_CRTC_MODE_FLAG_PCSYNC = (1 << 7), META_CRTC_MODE_FLAG_NCSYNC = (1 << 8), META_CRTC_MODE_FLAG_HSKEW = (1 << 9), META_CRTC_MODE_FLAG_BCAST = (1 << 10), META_CRTC_MODE_FLAG_PIXMUX = (1 << 11), META_CRTC_MODE_FLAG_DBLCLK = (1 << 12), META_CRTC_MODE_FLAG_CLKDIV2 = (1 << 13), META_CRTC_MODE_FLAG_MASK = 0x3fff } MetaCrtcModeFlag; typedef struct _MetaCrtcConfig { graphene_rect_t layout; MetaMonitorTransform transform; MetaCrtcMode *mode; } MetaCrtcConfig; struct _MetaCrtc { GObject parent; MetaGpu *gpu; glong crtc_id; unsigned int all_transforms; float scale; MetaCrtcConfig *config; /* Used when changing configuration */ gboolean is_dirty; /* Used by cursor renderer backend */ void *cursor_renderer_private; gpointer driver_private; GDestroyNotify driver_notify; }; struct _MetaCrtcMode { GObject parent; /* The low-level ID of this mode, used to apply back configuration */ glong mode_id; char *name; int width; int height; float refresh_rate; MetaCrtcModeFlag flags; gpointer driver_private; GDestroyNotify driver_notify; }; #define META_TYPE_CRTC (meta_crtc_get_type ()) META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaCrtc, meta_crtc, META, CRTC, GObject) #define META_TYPE_CRTC_MODE (meta_crtc_mode_get_type ()) META_EXPORT_TEST G_DECLARE_FINAL_TYPE (MetaCrtcMode, meta_crtc_mode, META, CRTC_MODE, GObject) MetaGpu * meta_crtc_get_gpu (MetaCrtc *crtc); META_EXPORT_TEST void meta_crtc_set_config (MetaCrtc *crtc, graphene_rect_t *layout, MetaCrtcMode *mode, MetaMonitorTransform transform); META_EXPORT_TEST void meta_crtc_unset_config (MetaCrtc *crtc); #endif /* META_CRTC_H */ muffin-6.4.1/src/backends/meta-backend-types.h0000664000175000017500000000407414723361714020136 0ustar fabiofabio/* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013-2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BACKEND_TYPE_H #define META_BACKEND_TYPE_H typedef struct _MetaMonitorManager MetaMonitorManager; typedef struct _MetaMonitorConfigManager MetaMonitorConfigManager; typedef struct _MetaMonitorConfigStore MetaMonitorConfigStore; typedef struct _MetaMonitorsConfig MetaMonitorsConfig; typedef struct _MetaMonitor MetaMonitor; typedef struct _MetaMonitorNormal MetaMonitorNormal; typedef struct _MetaMonitorTiled MetaMonitorTiled; typedef struct _MetaMonitorSpec MetaMonitorSpec; typedef struct _MetaLogicalMonitor MetaLogicalMonitor; typedef enum _MetaMonitorTransform MetaMonitorTransform; typedef struct _MetaMonitorMode MetaMonitorMode; typedef struct _MetaGpu MetaGpu; typedef struct _MetaCrtc MetaCrtc; typedef struct _MetaOutput MetaOutput; typedef struct _MetaCrtcMode MetaCrtcMode; typedef struct _MetaCrtcInfo MetaCrtcInfo; typedef struct _MetaOutputInfo MetaOutputInfo; typedef struct _MetaTileInfo MetaTileInfo; typedef struct _MetaRenderer MetaRenderer; typedef struct _MetaRendererView MetaRendererView; typedef struct _MetaScreenCast MetaScreenCast; typedef struct _MetaScreenCastSession MetaScreenCastSession; typedef struct _MetaScreenCastStream MetaScreenCastStream; typedef struct _MetaWaylandCompositor MetaWaylandCompositor; #endif /* META_BACKEND_TYPE_H */ muffin-6.4.1/src/backends/meta-cursor.h0000664000175000017500000000611014723361714016713 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Giovanni Campagna */ #ifndef META_CURSOR_H #define META_CURSOR_H #include "backends/meta-backend-types.h" #include "meta/common.h" #include "meta/boxes.h" #define META_TYPE_CURSOR_SPRITE (meta_cursor_sprite_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaCursorSprite, meta_cursor_sprite, META, CURSOR_SPRITE, GObject) struct _MetaCursorSpriteClass { GObjectClass parent_class; void (* realize_texture) (MetaCursorSprite *sprite); gboolean (* is_animated) (MetaCursorSprite *sprite); void (* tick_frame) (MetaCursorSprite *sprite); unsigned int (* get_current_frame_time) (MetaCursorSprite *sprite); }; void meta_cursor_sprite_prepare_at (MetaCursorSprite *sprite, int x, int y); void meta_cursor_sprite_realize_texture (MetaCursorSprite *sprite); void meta_cursor_sprite_clear_texture (MetaCursorSprite *sprite); void meta_cursor_sprite_set_texture (MetaCursorSprite *sprite, CoglTexture *texture, int hot_x, int hot_y); void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *sprite, float scale); void meta_cursor_sprite_set_texture_transform (MetaCursorSprite *sprite, MetaMonitorTransform transform); CoglTexture *meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *sprite); void meta_cursor_sprite_get_hotspot (MetaCursorSprite *sprite, int *hot_x, int *hot_y); int meta_cursor_sprite_get_width (MetaCursorSprite *sprite); int meta_cursor_sprite_get_height (MetaCursorSprite *sprite); float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *sprite); MetaMonitorTransform meta_cursor_sprite_get_texture_transform (MetaCursorSprite *sprite); gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *sprite); void meta_cursor_sprite_tick_frame (MetaCursorSprite *sprite); unsigned int meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *sprite); #endif /* META_CURSOR_H */ muffin-6.4.1/src/backends/meta-remote-access-controller-private.h0000664000175000017500000000261414723361714023766 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H #define META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H #include "meta/meta-remote-access-controller.h" void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller, MetaRemoteAccessHandle *handle); void meta_remote_access_handle_notify_stopped (MetaRemoteAccessHandle *handle); void meta_remote_access_handle_set_disable_animations (MetaRemoteAccessHandle *handle, gboolean disable_animations); #endif /* META_REMOTE_ACCESS_CONTROLLER_PRIVATE_H */ muffin-6.4.1/src/backends/meta-monitor-config-store.h0000664000175000017500000000461214723361714021467 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_MONITOR_CONFIG_STORE_H #define META_MONITOR_CONFIG_STORE_H #include #include "backends/meta-monitor-config-manager.h" #define META_TYPE_MONITOR_CONFIG_STORE (meta_monitor_config_store_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorConfigStore, meta_monitor_config_store, META, MONITOR_CONFIG_STORE, GObject) META_EXPORT_TEST MetaMonitorConfigStore * meta_monitor_config_store_new (MetaMonitorManager *monitor_manager); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_config_store_lookup (MetaMonitorConfigStore *config_store, MetaMonitorsConfigKey *key); META_EXPORT_TEST void meta_monitor_config_store_add (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config); META_EXPORT_TEST void meta_monitor_config_store_remove (MetaMonitorConfigStore *config_store, MetaMonitorsConfig *config); META_EXPORT_TEST gboolean meta_monitor_config_store_set_custom (MetaMonitorConfigStore *config_store, const char *read_path, const char *write_path, GError **error); META_EXPORT_TEST int meta_monitor_config_store_get_config_count (MetaMonitorConfigStore *config_store); META_EXPORT_TEST MetaMonitorManager * meta_monitor_config_store_get_monitor_manager (MetaMonitorConfigStore *config_store); #endif /* META_MONITOR_CONFIG_STORE_H */ muffin-6.4.1/src/backends/meta-crtc.c0000664000175000017500000000474214723361714016335 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-crtc.h" G_DEFINE_TYPE (MetaCrtc, meta_crtc, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaCrtcMode, meta_crtc_mode, G_TYPE_OBJECT) MetaGpu * meta_crtc_get_gpu (MetaCrtc *crtc) { return crtc->gpu; } void meta_crtc_set_config (MetaCrtc *crtc, graphene_rect_t *layout, MetaCrtcMode *mode, MetaMonitorTransform transform) { MetaCrtcConfig *config; meta_crtc_unset_config (crtc); config = g_new0 (MetaCrtcConfig, 1); config->layout = *layout; config->mode = mode; config->transform = transform; crtc->config = config; } void meta_crtc_unset_config (MetaCrtc *crtc) { g_clear_pointer (&crtc->config, g_free); } static void meta_crtc_finalize (GObject *object) { MetaCrtc *crtc = META_CRTC (object); if (crtc->driver_notify) crtc->driver_notify (crtc); g_clear_pointer (&crtc->config, g_free); G_OBJECT_CLASS (meta_crtc_parent_class)->finalize (object); } static void meta_crtc_init (MetaCrtc *crtc) { } static void meta_crtc_class_init (MetaCrtcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_crtc_finalize; } static void meta_crtc_mode_finalize (GObject *object) { MetaCrtcMode *crtc_mode = META_CRTC_MODE (object); if (crtc_mode->driver_notify) crtc_mode->driver_notify (crtc_mode); g_clear_pointer (&crtc_mode->name, g_free); G_OBJECT_CLASS (meta_crtc_mode_parent_class)->finalize (object); } static void meta_crtc_mode_init (MetaCrtcMode *crtc_mode) { } static void meta_crtc_mode_class_init (MetaCrtcModeClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_crtc_mode_finalize; } muffin-6.4.1/src/backends/meta-screen-cast-monitor-stream-src.c0000664000175000017500000005124114723361714023350 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-monitor-stream-src.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-stage-private.h" #include "clutter/clutter.h" #include "clutter/clutter-mutter.h" #include "core/boxes-private.h" struct _MetaScreenCastMonitorStreamSrc { MetaScreenCastStreamSrc parent; gboolean cursor_bitmap_invalid; gboolean hw_cursor_inhibited; GList *watches; gulong cursor_moved_handler_id; gulong cursor_changed_handler_id; }; static void hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastMonitorStreamSrc, meta_screen_cast_monitor_stream_src, META_TYPE_SCREEN_CAST_STREAM_SRC, G_IMPLEMENT_INTERFACE (META_TYPE_HW_CURSOR_INHIBITOR, hw_cursor_inhibitor_iface_init)) static ClutterStage * get_stage (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src; MetaScreenCastStream *stream; MetaScreenCastMonitorStream *monitor_stream; src = META_SCREEN_CAST_STREAM_SRC (monitor_src); stream = meta_screen_cast_stream_src_get_stream (src); monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (stream); return meta_screen_cast_monitor_stream_get_stage (monitor_stream); } static MetaMonitor * get_monitor (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src; MetaScreenCastStream *stream; MetaScreenCastMonitorStream *monitor_stream; src = META_SCREEN_CAST_STREAM_SRC (monitor_src); stream = meta_screen_cast_stream_src_get_stream (src); monitor_stream = META_SCREEN_CAST_MONITOR_STREAM (stream); return meta_screen_cast_monitor_stream_get_monitor (monitor_stream); } static void meta_screen_cast_monitor_stream_src_get_specs (MetaScreenCastStreamSrc *src, int *width, int *height, float *frame_rate) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; float scale; MetaMonitorMode *mode; monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); mode = meta_monitor_get_current_mode (monitor); if (meta_is_stage_views_scaled ()) scale = logical_monitor->scale; else scale = 1.0; *width = (int) roundf (logical_monitor->rect.width * scale); *height = (int) roundf (logical_monitor->rect.height * scale); *frame_rate = meta_monitor_mode_get_refresh_rate (mode); } static void stage_painted (MetaStage *stage, ClutterStageView *view, ClutterPaintContext *paint_context, gpointer user_data) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (user_data); MetaScreenCastRecordFlag flags; flags = META_SCREEN_CAST_RECORD_FLAG_NONE; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static MetaBackend * get_backend (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); return meta_screen_cast_get_backend (screen_cast); } static gboolean is_cursor_in_stream (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaBackend *backend = get_backend (monitor_src); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; graphene_rect_t logical_monitor_rect; MetaCursorSprite *cursor_sprite; monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor_layout); cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); if (cursor_sprite) { graphene_rect_t cursor_rect; cursor_rect = meta_cursor_renderer_calculate_rect (cursor_renderer, cursor_sprite); return graphene_rect_intersection (&cursor_rect, &logical_monitor_rect, NULL); } else { graphene_point_t cursor_position; cursor_position = meta_cursor_renderer_get_position (cursor_renderer); return graphene_rect_contains_point (&logical_monitor_rect, &cursor_position); } } static void sync_cursor_state (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); ClutterStage *stage = get_stage (monitor_src); MetaScreenCastRecordFlag flags; if (clutter_stage_is_redraw_queued (stage)) return; if (meta_screen_cast_stream_src_pending_follow_up_frame (src)) return; flags = META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY; meta_screen_cast_stream_src_maybe_record_frame (src, flags); } static void cursor_moved (MetaCursorTracker *cursor_tracker, float x, float y, MetaScreenCastMonitorStreamSrc *monitor_src) { sync_cursor_state (monitor_src); } static void cursor_changed (MetaCursorTracker *cursor_tracker, MetaScreenCastMonitorStreamSrc *monitor_src) { monitor_src->cursor_bitmap_invalid = TRUE; sync_cursor_state (monitor_src); } static MetaCursorRenderer * get_cursor_renderer (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (monitor_src); MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); return meta_backend_get_cursor_renderer (backend); } static void inhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaCursorRenderer *cursor_renderer; MetaHwCursorInhibitor *inhibitor; g_return_if_fail (!monitor_src->hw_cursor_inhibited); cursor_renderer = get_cursor_renderer (monitor_src); inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src); meta_cursor_renderer_add_hw_cursor_inhibitor (cursor_renderer, inhibitor); monitor_src->hw_cursor_inhibited = TRUE; } static void uninhibit_hw_cursor (MetaScreenCastMonitorStreamSrc *monitor_src) { MetaCursorRenderer *cursor_renderer; MetaHwCursorInhibitor *inhibitor; g_return_if_fail (monitor_src->hw_cursor_inhibited); cursor_renderer = get_cursor_renderer (monitor_src); inhibitor = META_HW_CURSOR_INHIBITOR (monitor_src); meta_cursor_renderer_remove_hw_cursor_inhibitor (cursor_renderer, inhibitor); monitor_src->hw_cursor_inhibited = FALSE; } static void add_view_painted_watches (MetaScreenCastMonitorStreamSrc *monitor_src, MetaStageWatchPhase watch_phase) { MetaBackend *backend = get_backend (monitor_src); MetaRenderer *renderer = meta_backend_get_renderer (backend); ClutterStage *stage; MetaStage *meta_stage; MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; GList *l; stage = get_stage (monitor_src); meta_stage = META_STAGE (stage); monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); for (l = meta_renderer_get_views (renderer); l; l = l->next) { MetaRendererView *view = l->data; MetaRectangle view_layout; clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout); if (meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) { MetaStageWatch *watch; watch = meta_stage_watch_view (meta_stage, CLUTTER_STAGE_VIEW (view), watch_phase, stage_painted, monitor_src); monitor_src->watches = g_list_prepend (monitor_src->watches, watch); } } } static void meta_screen_cast_monitor_stream_src_enable (MetaScreenCastStreamSrc *src) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterStage *stage; MetaScreenCastStream *stream; stream = meta_screen_cast_stream_src_get_stream (src); stage = get_stage (monitor_src); switch (meta_screen_cast_stream_get_cursor_mode (stream)) { case META_SCREEN_CAST_CURSOR_MODE_METADATA: monitor_src->cursor_moved_handler_id = g_signal_connect_after (cursor_tracker, "cursor-moved", G_CALLBACK (cursor_moved), monitor_src); monitor_src->cursor_changed_handler_id = g_signal_connect_after (cursor_tracker, "cursor-changed", G_CALLBACK (cursor_changed), monitor_src); G_GNUC_FALLTHROUGH; case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: add_view_painted_watches (monitor_src, META_STAGE_WATCH_AFTER_ACTOR_PAINT); break; case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: inhibit_hw_cursor (monitor_src); add_view_painted_watches (monitor_src, META_STAGE_WATCH_AFTER_PAINT); break; } clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); } static void meta_screen_cast_monitor_stream_src_disable (MetaScreenCastStreamSrc *src) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterStage *stage; MetaStage *meta_stage; GList *l; stage = get_stage (monitor_src); meta_stage = META_STAGE (stage); for (l = monitor_src->watches; l; l = l->next) { MetaStageWatch *watch = l->data; meta_stage_remove_watch (meta_stage, watch); } g_clear_pointer (&monitor_src->watches, g_list_free); if (monitor_src->hw_cursor_inhibited) uninhibit_hw_cursor (monitor_src); g_clear_signal_handler (&monitor_src->cursor_moved_handler_id, cursor_tracker); g_clear_signal_handler (&monitor_src->cursor_changed_handler_id, cursor_tracker); } static gboolean meta_screen_cast_monitor_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, uint8_t *data, GError **error) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); ClutterStage *stage; MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; stage = get_stage (monitor_src); monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); clutter_stage_capture_into (stage, FALSE, &logical_monitor->rect, data); return TRUE; } static gboolean meta_screen_cast_monitor_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, CoglFramebuffer *framebuffer, GError **error) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; GList *l; float view_scale; monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); if (meta_is_stage_views_scaled ()) view_scale = meta_logical_monitor_get_scale (logical_monitor); else view_scale = 1.0; for (l = meta_renderer_get_views (renderer); l; l = l->next) { ClutterStageView *view = CLUTTER_STAGE_VIEW (l->data); CoglFramebuffer *view_framebuffer; MetaRectangle view_layout; int x, y; clutter_stage_view_get_layout (view, &view_layout); if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) continue; view_framebuffer = clutter_stage_view_get_framebuffer (view); x = (int) roundf ((view_layout.x - logical_monitor_layout.x) * view_scale); y = (int) roundf ((view_layout.y - logical_monitor_layout.y) * view_scale); if (!cogl_blit_framebuffer (view_framebuffer, framebuffer, 0, 0, x, y, cogl_framebuffer_get_width (view_framebuffer), cogl_framebuffer_get_height (view_framebuffer), error)) return FALSE; } cogl_framebuffer_finish (framebuffer); return TRUE; } static void meta_screen_cast_monitor_stream_record_follow_up (MetaScreenCastStreamSrc *src) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); MetaRenderer *renderer = meta_backend_get_renderer (backend); ClutterStage *stage = get_stage (monitor_src); MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; GList *l; monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); for (l = meta_renderer_get_views (renderer); l; l = l->next) { MetaRendererView *view = l->data; MetaRectangle view_layout; MetaRectangle damage; clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (view), &view_layout); if (!meta_rectangle_overlap (&logical_monitor_layout, &view_layout)) continue; damage = (cairo_rectangle_int_t) { .x = view_layout.x, .y = view_layout.y, .width = 1, .height = 1, }; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &damage); } } static void meta_screen_cast_monitor_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (src); MetaBackend *backend = get_backend (monitor_src); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaCursorSprite *cursor_sprite; MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; MetaRectangle logical_monitor_layout; graphene_rect_t logical_monitor_rect; float view_scale; graphene_point_t cursor_position; int x, y; cursor_sprite = meta_cursor_renderer_get_cursor (cursor_renderer); if (!is_cursor_in_stream (monitor_src)) { meta_screen_cast_stream_src_unset_cursor_metadata (src, spa_meta_cursor); return; } monitor = get_monitor (monitor_src); logical_monitor = meta_monitor_get_logical_monitor (monitor); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor_layout); if (meta_is_stage_views_scaled ()) view_scale = meta_logical_monitor_get_scale (logical_monitor); else view_scale = 1.0; cursor_position = meta_cursor_renderer_get_position (cursor_renderer); cursor_position.x -= logical_monitor_rect.origin.x; cursor_position.y -= logical_monitor_rect.origin.y; cursor_position.x *= view_scale; cursor_position.y *= view_scale; x = (int) roundf (cursor_position.x); y = (int) roundf (cursor_position.y); if (monitor_src->cursor_bitmap_invalid) { if (cursor_sprite) { float cursor_scale; float scale; cursor_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite); scale = view_scale * cursor_scale; meta_screen_cast_stream_src_set_cursor_sprite_metadata (src, spa_meta_cursor, cursor_sprite, x, y, scale); } else { meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src, spa_meta_cursor, x, y); } monitor_src->cursor_bitmap_invalid = FALSE; } else { meta_screen_cast_stream_src_set_cursor_position_metadata (src, spa_meta_cursor, x, y); } } static gboolean meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor, MetaCursorSprite *cursor_sprite) { MetaScreenCastMonitorStreamSrc *monitor_src = META_SCREEN_CAST_MONITOR_STREAM_SRC (inhibitor); return is_cursor_in_stream (monitor_src); } static void hw_cursor_inhibitor_iface_init (MetaHwCursorInhibitorInterface *iface) { iface->is_cursor_sprite_inhibited = meta_screen_cast_monitor_stream_src_is_cursor_sprite_inhibited; } MetaScreenCastMonitorStreamSrc * meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream *monitor_stream, GError **error) { return g_initable_new (META_TYPE_SCREEN_CAST_MONITOR_STREAM_SRC, NULL, error, "stream", monitor_stream, NULL); } static void meta_screen_cast_monitor_stream_src_init (MetaScreenCastMonitorStreamSrc *monitor_src) { monitor_src->cursor_bitmap_invalid = TRUE; } static void meta_screen_cast_monitor_stream_src_class_init (MetaScreenCastMonitorStreamSrcClass *klass) { MetaScreenCastStreamSrcClass *src_class = META_SCREEN_CAST_STREAM_SRC_CLASS (klass); src_class->get_specs = meta_screen_cast_monitor_stream_src_get_specs; src_class->enable = meta_screen_cast_monitor_stream_src_enable; src_class->disable = meta_screen_cast_monitor_stream_src_disable; src_class->record_to_buffer = meta_screen_cast_monitor_stream_src_record_to_buffer; src_class->record_to_framebuffer = meta_screen_cast_monitor_stream_src_record_to_framebuffer; src_class->record_follow_up = meta_screen_cast_monitor_stream_record_follow_up; src_class->set_cursor_metadata = meta_screen_cast_monitor_stream_src_set_cursor_metadata; } muffin-6.4.1/src/backends/gsm-inhibitor-flag.h0000664000175000017500000000227414723361714020143 0ustar fabiofabio/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright (C) 2008 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __GSM_INHIBITOR_FLAG_H__ #define __GSM_INHIBITOR_FLAG_H__ #include G_BEGIN_DECLS typedef enum { GSM_INHIBITOR_FLAG_LOGOUT = 1 << 0, GSM_INHIBITOR_FLAG_SWITCH_USER = 1 << 1, GSM_INHIBITOR_FLAG_SUSPEND = 1 << 2, GSM_INHIBITOR_FLAG_IDLE = 1 << 3, GSM_INHIBITOR_FLAG_AUTOMOUNT = 1 << 4 } GsmInhibitorFlag; G_END_DECLS #endif /* __GSM_INHIBITOR_FLAG_H__ */ muffin-6.4.1/src/backends/meta-idle-monitor.c0000664000175000017500000003473314723361714020007 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Adapted from gnome-session/gnome-session/gs-idle-monitor.c and * from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c */ /** * SECTION:idle-monitor * @title: MetaIdleMonitor * @short_description: Mutter idle counter (similar to X's IDLETIME) */ #include "config.h" #include #include #include #include "backends/gsm-inhibitor-flag.h" #include "backends/meta-backend-private.h" #include "backends/meta-idle-monitor-private.h" #include "backends/meta-idle-monitor-dbus.h" #include "clutter/clutter.h" #include "meta/main.h" #include "meta/meta-idle-monitor.h" #include "meta/util.h" G_STATIC_ASSERT(sizeof(unsigned long) == sizeof(gpointer)); enum { PROP_0, PROP_DEVICE, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE (MetaIdleMonitor, meta_idle_monitor, G_TYPE_OBJECT) static void meta_idle_monitor_watch_fire (MetaIdleMonitorWatch *watch) { MetaIdleMonitor *monitor; guint id; gboolean is_user_active_watch; monitor = watch->monitor; g_object_ref (monitor); g_clear_handle_id (&watch->idle_source_id, g_source_remove); id = watch->id; is_user_active_watch = (watch->timeout_msec == 0); if (watch->callback) watch->callback (monitor, id, watch->user_data); if (is_user_active_watch) meta_idle_monitor_remove_watch (monitor, id); g_object_unref (monitor); } static void meta_idle_monitor_dispose (GObject *object) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (object); g_clear_pointer (&monitor->watches, g_hash_table_destroy); g_clear_object (&monitor->session_proxy); G_OBJECT_CLASS (meta_idle_monitor_parent_class)->dispose (object); } static void meta_idle_monitor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (object); switch (prop_id) { case PROP_DEVICE: g_value_set_object (value, monitor->device); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_idle_monitor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaIdleMonitor *monitor = META_IDLE_MONITOR (object); switch (prop_id) { case PROP_DEVICE: monitor->device = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_idle_monitor_class_init (MetaIdleMonitorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_idle_monitor_dispose; object_class->get_property = meta_idle_monitor_get_property; object_class->set_property = meta_idle_monitor_set_property; /** * MetaIdleMonitor:device: * * The device to listen to idletime on. */ obj_props[PROP_DEVICE] = g_param_spec_object ("device", "Device", "The device to listen to idletime on", CLUTTER_TYPE_INPUT_DEVICE, G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DEVICE, obj_props[PROP_DEVICE]); } static void free_watch (gpointer data) { MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) data; MetaIdleMonitor *monitor = watch->monitor; g_object_ref (monitor); g_clear_handle_id (&watch->idle_source_id, g_source_remove); if (watch->notify != NULL) watch->notify (watch->user_data); if (watch->timeout_source != NULL) g_source_destroy (watch->timeout_source); g_object_unref (monitor); g_slice_free (MetaIdleMonitorWatch, watch); } static void update_inhibited_watch (gpointer key, gpointer value, gpointer user_data) { MetaIdleMonitor *monitor = user_data; MetaIdleMonitorWatch *watch = value; if (!watch->timeout_source) return; if (monitor->inhibited) { g_source_set_ready_time (watch->timeout_source, -1); } else { g_source_set_ready_time (watch->timeout_source, monitor->last_event_time + watch->timeout_msec * 1000); } } static void update_inhibited (MetaIdleMonitor *monitor, gboolean inhibited) { if (inhibited == monitor->inhibited) return; monitor->inhibited = inhibited; g_hash_table_foreach (monitor->watches, update_inhibited_watch, monitor); } static void meta_idle_monitor_inhibited_actions_changed (GDBusProxy *session, GVariant *changed, char **invalidated, gpointer user_data) { MetaIdleMonitor *monitor = user_data; GVariant *v; v = g_variant_lookup_value (changed, "InhibitedActions", G_VARIANT_TYPE_UINT32); if (v) { gboolean inhibited; inhibited = !!(g_variant_get_uint32 (v) & GSM_INHIBITOR_FLAG_IDLE); g_variant_unref (v); if (!inhibited) monitor->last_event_time = g_get_monotonic_time (); update_inhibited (monitor, inhibited); } } static void meta_idle_monitor_init (MetaIdleMonitor *monitor) { GVariant *v; monitor->watches = g_hash_table_new_full (NULL, NULL, NULL, free_watch); monitor->last_event_time = g_get_monotonic_time (); /* Monitor inhibitors */ monitor->session_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS | G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START, NULL, "org.gnome.SessionManager", "/org/gnome/SessionManager", "org.gnome.SessionManager", NULL, NULL); if (!monitor->session_proxy) return; g_signal_connect (monitor->session_proxy, "g-properties-changed", G_CALLBACK (meta_idle_monitor_inhibited_actions_changed), monitor); v = g_dbus_proxy_get_cached_property (monitor->session_proxy, "InhibitedActions"); if (v) { monitor->inhibited = !!(g_variant_get_uint32 (v) & GSM_INHIBITOR_FLAG_IDLE); g_variant_unref (v); } } /** * meta_idle_monitor_get_core: * * Returns: (transfer none): the #MetaIdleMonitor that tracks the server-global * idletime for all devices. */ MetaIdleMonitor * meta_idle_monitor_get_core (void) { MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); ClutterSeat *seat = clutter_backend_get_default_seat (clutter_backend); return meta_backend_get_idle_monitor (backend, clutter_seat_get_pointer (seat)); } static guint32 get_next_watch_serial (void) { static guint32 serial = 0; g_atomic_int_inc (&serial); return serial; } static gboolean idle_monitor_dispatch_timeout (GSource *source, GSourceFunc callback, gpointer user_data) { MetaIdleMonitorWatch *watch = (MetaIdleMonitorWatch *) user_data; int64_t now; int64_t ready_time; now = g_source_get_time (source); ready_time = g_source_get_ready_time (source); if (ready_time > now) return G_SOURCE_CONTINUE; g_source_set_ready_time (watch->timeout_source, -1); meta_idle_monitor_watch_fire (watch); return G_SOURCE_CONTINUE; } static GSourceFuncs idle_monitor_source_funcs = { .prepare = NULL, .check = NULL, .dispatch = idle_monitor_dispatch_timeout, .finalize = NULL, }; static MetaIdleMonitorWatch * make_watch (MetaIdleMonitor *monitor, guint64 timeout_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorWatch *watch; watch = g_slice_new0 (MetaIdleMonitorWatch); watch->monitor = monitor; watch->id = get_next_watch_serial (); watch->callback = callback; watch->user_data = user_data; watch->notify = notify; watch->timeout_msec = timeout_msec; if (timeout_msec != 0) { GSource *source = g_source_new (&idle_monitor_source_funcs, sizeof (GSource)); g_source_set_callback (source, NULL, watch, NULL); if (!monitor->inhibited) { g_source_set_ready_time (source, monitor->last_event_time + timeout_msec * 1000); } g_source_attach (source, NULL); g_source_unref (source); watch->timeout_source = source; } g_hash_table_insert (monitor->watches, GUINT_TO_POINTER (watch->id), watch); return watch; } /** * meta_idle_monitor_add_idle_watch: * @monitor: A #MetaIdleMonitor * @interval_msec: The idletime interval, in milliseconds * @callback: (nullable): The callback to call when the user has * accumulated @interval_msec milliseconds of idle time. * @user_data: (nullable): The user data to pass to the callback * @notify: A #GDestroyNotify * * Returns: a watch id * * Adds a watch for a specific idle time. The callback will be called * when the user has accumulated @interval_msec milliseconds of idle time. * This function will return an ID that can either be passed to * meta_idle_monitor_remove_watch(), or can be used to tell idle time * watches apart if you have more than one. * * Also note that this function will only care about positive transitions * (user's idle time exceeding a certain time). If you want to know about * when the user has become active, use * meta_idle_monitor_add_user_active_watch(). */ guint meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor, guint64 interval_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorWatch *watch; g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0); g_return_val_if_fail (interval_msec > 0, 0); watch = make_watch (monitor, interval_msec, callback, user_data, notify); return watch->id; } /** * meta_idle_monitor_add_user_active_watch: * @monitor: A #MetaIdleMonitor * @callback: (nullable): The callback to call when the user is * active again. * @user_data: (nullable): The user data to pass to the callback * @notify: A #GDestroyNotify * * Returns: a watch id * * Add a one-time watch to know when the user is active again. * Note that this watch is one-time and will de-activate after the * function is called, for efficiency purposes. It's most convenient * to call this when an idle watch, as added by * meta_idle_monitor_add_idle_watch(), has triggered. */ guint meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify) { MetaIdleMonitorWatch *watch; g_return_val_if_fail (META_IS_IDLE_MONITOR (monitor), 0); watch = make_watch (monitor, 0, callback, user_data, notify); return watch->id; } /** * meta_idle_monitor_remove_watch: * @monitor: A #MetaIdleMonitor * @id: A watch ID * * Removes an idle time watcher, previously added by * meta_idle_monitor_add_idle_watch() or * meta_idle_monitor_add_user_active_watch(). */ void meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor, guint id) { g_return_if_fail (META_IS_IDLE_MONITOR (monitor)); g_object_ref (monitor); g_hash_table_remove (monitor->watches, GUINT_TO_POINTER (id)); g_object_unref (monitor); } /** * meta_idle_monitor_get_idletime: * @monitor: A #MetaIdleMonitor * * Returns: The current idle time, in milliseconds, or -1 for not supported */ gint64 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor) { return (g_get_monotonic_time () - monitor->last_event_time) / 1000; } void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor) { GList *node, *watch_ids; monitor->last_event_time = g_get_monotonic_time (); watch_ids = g_hash_table_get_keys (monitor->watches); for (node = watch_ids; node != NULL; node = node->next) { guint watch_id = GPOINTER_TO_UINT (node->data); MetaIdleMonitorWatch *watch; watch = g_hash_table_lookup (monitor->watches, GUINT_TO_POINTER (watch_id)); if (!watch) continue; if (watch->timeout_msec == 0) { meta_idle_monitor_watch_fire (watch); } else { if (monitor->inhibited) { g_source_set_ready_time (watch->timeout_source, -1); } else { g_source_set_ready_time (watch->timeout_source, monitor->last_event_time + watch->timeout_msec * 1000); } } } g_list_free (watch_ids); } muffin-6.4.1/src/backends/meta-screen-cast.c0000664000175000017500000002252014723361714017603 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-remote-desktop-session.h" #include "backends/meta-screen-cast-session.h" #define META_SCREEN_CAST_DBUS_SERVICE "org.cinnamon.Muffin.ScreenCast" #define META_SCREEN_CAST_DBUS_PATH "/org/cinnamon/Muffin/ScreenCast" #define META_SCREEN_CAST_API_VERSION 3 struct _MetaScreenCast { MetaDBusScreenCastSkeleton parent; int dbus_name_id; GList *sessions; MetaDbusSessionWatcher *session_watcher; MetaBackend *backend; }; static void meta_screen_cast_init_iface (MetaDBusScreenCastIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCast, meta_screen_cast, META_DBUS_TYPE_SCREEN_CAST_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST, meta_screen_cast_init_iface)) GDBusConnection * meta_screen_cast_get_connection (MetaScreenCast *screen_cast) { GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (screen_cast); return g_dbus_interface_skeleton_get_connection (interface_skeleton); } MetaBackend * meta_screen_cast_get_backend (MetaScreenCast *screen_cast) { return screen_cast->backend; } static gboolean register_remote_desktop_screen_cast_session (MetaScreenCastSession *session, const char *remote_desktop_session_id, GError **error) { MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); MetaRemoteDesktop *remote_desktop = meta_backend_get_remote_desktop (backend); MetaRemoteDesktopSession *remote_desktop_session; remote_desktop_session = meta_remote_desktop_get_session (remote_desktop, remote_desktop_session_id); if (!remote_desktop_session) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No remote desktop session found"); return FALSE; } if (!meta_remote_desktop_session_register_screen_cast (remote_desktop_session, session, error)) return FALSE; return TRUE; } static void on_session_closed (MetaScreenCastSession *session, MetaScreenCast *screen_cast) { screen_cast->sessions = g_list_remove (screen_cast->sessions, session); } static gboolean handle_create_session (MetaDBusScreenCast *skeleton, GDBusMethodInvocation *invocation, GVariant *properties) { MetaScreenCast *screen_cast = META_SCREEN_CAST (skeleton); const char *peer_name; MetaScreenCastSession *session; GError *error = NULL; const char *session_path; const char *client_dbus_name; char *remote_desktop_session_id = NULL; gboolean disable_animations; MetaScreenCastSessionType session_type; g_variant_lookup (properties, "remote-desktop-session-id", "s", &remote_desktop_session_id); if (remote_desktop_session_id) session_type = META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP; else session_type = META_SCREEN_CAST_SESSION_TYPE_NORMAL; peer_name = g_dbus_method_invocation_get_sender (invocation); session = meta_screen_cast_session_new (screen_cast, session_type, peer_name, &error); if (!session) { g_warning ("Failed to create screen cast session: %s", error->message); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to create session: %s", error->message); g_error_free (error); return TRUE; } if (remote_desktop_session_id) { if (!register_remote_desktop_screen_cast_session (session, remote_desktop_session_id, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "%s", error->message); g_error_free (error); g_object_unref (session); return TRUE; } } if (g_variant_lookup (properties, "disable-animations", "b", &disable_animations)) { meta_screen_cast_session_set_disable_animations (session, disable_animations); } client_dbus_name = g_dbus_method_invocation_get_sender (invocation); meta_dbus_session_watcher_watch_session (screen_cast->session_watcher, client_dbus_name, META_DBUS_SESSION (session)); session_path = meta_screen_cast_session_get_object_path (session); meta_dbus_screen_cast_complete_create_session (skeleton, invocation, session_path); screen_cast->sessions = g_list_append (screen_cast->sessions, session); g_signal_connect (session, "session-closed", G_CALLBACK (on_session_closed), screen_cast); return TRUE; } static void meta_screen_cast_init_iface (MetaDBusScreenCastIface *iface) { iface->handle_create_session = handle_create_session; } static void on_bus_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { MetaScreenCast *screen_cast = user_data; GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (screen_cast); GError *error = NULL; if (!g_dbus_interface_skeleton_export (interface_skeleton, connection, META_SCREEN_CAST_DBUS_PATH, &error)) g_warning ("Failed to export remote desktop object: %s\n", error->message); } static void on_name_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { g_info ("Acquired name %s\n", name); } static void on_name_lost (GDBusConnection *connection, const char *name, gpointer user_data) { g_warning ("Lost or failed to acquire name %s\n", name); } static void meta_screen_cast_constructed (GObject *object) { MetaScreenCast *screen_cast = META_SCREEN_CAST (object); screen_cast->dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, META_SCREEN_CAST_DBUS_SERVICE, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, screen_cast, NULL); } static void meta_screen_cast_finalize (GObject *object) { MetaScreenCast *screen_cast = META_SCREEN_CAST (object); if (screen_cast->dbus_name_id) g_bus_unown_name (screen_cast->dbus_name_id); while (screen_cast->sessions) { MetaScreenCastSession *session = screen_cast->sessions->data; meta_screen_cast_session_close (session); } G_OBJECT_CLASS (meta_screen_cast_parent_class)->finalize (object); } MetaScreenCast * meta_screen_cast_new (MetaBackend *backend, MetaDbusSessionWatcher *session_watcher) { MetaScreenCast *screen_cast; screen_cast = g_object_new (META_TYPE_SCREEN_CAST, NULL); screen_cast->backend = backend; screen_cast->session_watcher = session_watcher; return screen_cast; } static void meta_screen_cast_init (MetaScreenCast *screen_cast) { static gboolean is_pipewire_initialized = FALSE; if (!is_pipewire_initialized) { pw_init (NULL, NULL); is_pipewire_initialized = TRUE; } meta_dbus_screen_cast_set_version (META_DBUS_SCREEN_CAST (screen_cast), META_SCREEN_CAST_API_VERSION); } static void meta_screen_cast_class_init (MetaScreenCastClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_screen_cast_constructed; object_class->finalize = meta_screen_cast_finalize; } muffin-6.4.1/src/backends/meta-backend-private.h0000664000175000017500000001606214723361714020444 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_PRIVATE_H #define META_BACKEND_PRIVATE_H #include #include #include "meta/meta-backend.h" #include "meta/meta-idle-monitor.h" #include "backends/meta-backend-types.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-egl.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-orientation-manager.h" #include "backends/meta-pointer-constraint.h" #include "backends/meta-renderer.h" #include "backends/meta-settings-private.h" #include "core/util-private.h" #ifdef HAVE_REMOTE_DESKTOP #include "backends/meta-remote-desktop.h" #endif #define DEFAULT_XKB_RULES_FILE "evdev" #define DEFAULT_XKB_MODEL "pc105+inet" typedef enum { META_SEQUENCE_NONE, META_SEQUENCE_ACCEPTED, META_SEQUENCE_REJECTED, META_SEQUENCE_PENDING_END } MetaSequenceState; struct _MetaBackendClass { GObjectClass parent_class; ClutterBackend * (* create_clutter_backend) (MetaBackend *backend); void (* post_init) (MetaBackend *backend); MetaMonitorManager * (* create_monitor_manager) (MetaBackend *backend, GError **error); MetaCursorRenderer * (* create_cursor_renderer) (MetaBackend *backend); MetaRenderer * (* create_renderer) (MetaBackend *backend, GError **error); MetaInputSettings * (* create_input_settings) (MetaBackend *backend); gboolean (* grab_device) (MetaBackend *backend, int device_id, uint32_t timestamp); gboolean (* ungrab_device) (MetaBackend *backend, int device_id, uint32_t timestamp); void (* finish_touch_sequence) (MetaBackend *backend, ClutterEventSequence *sequence, MetaSequenceState state); MetaLogicalMonitor * (* get_current_logical_monitor) (MetaBackend *backend); void (* set_keymap) (MetaBackend *backend, const char *layouts, const char *variants, const char *options); gboolean (* is_lid_closed) (MetaBackend *backend); struct xkb_keymap * (* get_keymap) (MetaBackend *backend); xkb_layout_index_t (* get_keymap_layout_group) (MetaBackend *backend); void (* lock_layout_group) (MetaBackend *backend, guint idx); void (* update_screen_size) (MetaBackend *backend, int width, int height); void (* select_stage_events) (MetaBackend *backend); void (* set_numlock) (MetaBackend *backend, gboolean numlock_state); }; void meta_init_backend (GType backend_gtype); #ifdef HAVE_WAYLAND MetaWaylandCompositor * meta_backend_get_wayland_compositor (MetaBackend *backend); void meta_backend_init_wayland_display (MetaBackend *backend); void meta_backend_init_wayland (MetaBackend *backend); #endif ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend); MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend, ClutterInputDevice *device); void meta_backend_foreach_device_monitor (MetaBackend *backend, GFunc func, gpointer user_data); META_EXPORT_TEST MetaMonitorManager * meta_backend_get_monitor_manager (MetaBackend *backend); MetaOrientationManager * meta_backend_get_orientation_manager (MetaBackend *backend); MetaCursorTracker * meta_backend_get_cursor_tracker (MetaBackend *backend); MetaCursorRenderer * meta_backend_get_cursor_renderer (MetaBackend *backend); META_EXPORT_TEST MetaRenderer * meta_backend_get_renderer (MetaBackend *backend); MetaEgl * meta_backend_get_egl (MetaBackend *backend); #ifdef HAVE_REMOTE_DESKTOP MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend); #endif gboolean meta_backend_grab_device (MetaBackend *backend, int device_id, uint32_t timestamp); gboolean meta_backend_ungrab_device (MetaBackend *backend, int device_id, uint32_t timestamp); void meta_backend_finish_touch_sequence (MetaBackend *backend, ClutterEventSequence *sequence, MetaSequenceState state); MetaLogicalMonitor * meta_backend_get_current_logical_monitor (MetaBackend *backend); struct xkb_keymap * meta_backend_get_keymap (MetaBackend *backend); xkb_layout_index_t meta_backend_get_keymap_layout_group (MetaBackend *backend); gboolean meta_backend_is_lid_closed (MetaBackend *backend); void meta_backend_freeze_updates (MetaBackend *backend); void meta_backend_thaw_updates (MetaBackend *backend); void meta_backend_update_last_device (MetaBackend *backend, ClutterInputDevice *device); MetaPointerConstraint * meta_backend_get_client_pointer_constraint (MetaBackend *backend); void meta_backend_set_client_pointer_constraint (MetaBackend *backend, MetaPointerConstraint *constraint); void meta_backend_monitors_changed (MetaBackend *backend); META_EXPORT_TEST gboolean meta_is_stage_views_enabled (void); gboolean meta_is_stage_views_scaled (void); MetaInputSettings *meta_backend_get_input_settings (MetaBackend *backend); void meta_backend_notify_keymap_changed (MetaBackend *backend); void meta_backend_notify_keymap_layout_group_changed (MetaBackend *backend, unsigned int locked_group); void meta_backend_notify_ui_scaling_factor_changed (MetaBackend *backend); META_EXPORT_TEST void meta_backend_add_gpu (MetaBackend *backend, MetaGpu *gpu); META_EXPORT_TEST GList * meta_backend_get_gpus (MetaBackend *backend); #ifdef HAVE_LIBWACOM WacomDeviceDatabase * meta_backend_get_wacom_database (MetaBackend *backend); #endif #endif /* META_BACKEND_PRIVATE_H */ muffin-6.4.1/src/backends/meta-dbus-session-watcher.h0000664000175000017500000000351614723361714021456 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_DBUS_SESSION_WATCHER_H #define META_DBUS_SESSION_WATCHER_H #include #define META_TYPE_DBUS_SESSION (meta_dbus_session_get_type ()) G_DECLARE_INTERFACE (MetaDbusSession, meta_dbus_session, META, DBUS_SESSION, GObject) struct _MetaDbusSessionInterface { GTypeInterface parent_iface; void (* client_vanished) (MetaDbusSession *session); }; #define META_TYPE_DBUS_SESSION_WATCHER (meta_dbus_session_watcher_get_type ()) G_DECLARE_FINAL_TYPE (MetaDbusSessionWatcher, meta_dbus_session_watcher, META, DBUS_SESSION_WATCHER, GObject) void meta_dbus_session_watcher_watch_session (MetaDbusSessionWatcher *session_watcher, const char *client_dbus_name, MetaDbusSession *session); void meta_dbus_session_notify_closed (MetaDbusSession *session); #endif /* META_DBUS_SESSION_WATCHER_H */ muffin-6.4.1/src/backends/meta-input-mapper.c0000664000175000017500000004517614723361714020031 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #include "config.h" #ifdef HAVE_LIBGUDEV #include #endif #include "meta-input-mapper-private.h" #include "meta-monitor-manager-private.h" #include "meta-logical-monitor.h" #include "meta-backend-private.h" #define MAX_SIZE_MATCH_DIFF 0.05 typedef struct _MetaMapperInputInfo MetaMapperInputInfo; typedef struct _MetaMapperOutputInfo MetaMapperOutputInfo; typedef struct _MappingHelper MappingHelper; typedef struct _DeviceCandidates DeviceCandidates; struct _MetaInputMapper { GObject parent_instance; MetaMonitorManager *monitor_manager; ClutterSeat *seat; GHashTable *input_devices; /* ClutterInputDevice -> MetaMapperInputInfo */ GHashTable *output_devices; /* MetaLogicalMonitor -> MetaMapperOutputInfo */ #ifdef HAVE_LIBGUDEV GUdevClient *udev_client; #endif }; typedef enum { META_INPUT_CAP_TOUCH = 1 << 0, /* touch device, either touchscreen or tablet */ META_INPUT_CAP_STYLUS = 1 << 1, /* tablet pen */ META_INPUT_CAP_ERASER = 1 << 2, /* tablet eraser */ META_INPUT_CAP_PAD = 1 << 3, /* pad device, most usually in tablets */ META_INPUT_CAP_CURSOR = 1 << 4 /* pointer-like device in tablets */ } MetaInputCapabilityFlags; typedef enum { META_MATCH_IS_BUILTIN, /* Output is builtin, applies mainly to system-integrated devices */ META_MATCH_SIZE, /* Size from input device and output match */ META_MATCH_EDID_FULL, /* Full EDID model match, eg. "Cintiq 12WX" */ META_MATCH_EDID_PARTIAL, /* Partial EDID model match, eg. "Cintiq" */ META_MATCH_EDID_VENDOR, /* EDID vendor match, eg. "WAC" for Wacom */ N_OUTPUT_MATCHES } MetaOutputMatchType; struct _MetaMapperInputInfo { ClutterInputDevice *device; MetaInputMapper *mapper; MetaMapperOutputInfo *output; guint builtin : 1; }; struct _MetaMapperOutputInfo { MetaLogicalMonitor *logical_monitor; GList *input_devices; MetaInputCapabilityFlags attached_caps; }; struct _MappingHelper { GArray *device_maps; }; struct _DeviceCandidates { MetaMapperInputInfo *input; MetaMonitor *candidates[N_OUTPUT_MATCHES]; MetaOutputMatchType best; }; enum { DEVICE_MAPPED, N_SIGNALS }; static guint signals[N_SIGNALS] = { 0, }; G_DEFINE_TYPE (MetaInputMapper, meta_input_mapper, G_TYPE_OBJECT) static MetaMapperInputInfo * mapper_input_info_new (ClutterInputDevice *device, MetaInputMapper *mapper, gboolean builtin) { MetaMapperInputInfo *info; info = g_new0 (MetaMapperInputInfo, 1); info->mapper = mapper; info->device = device; info->builtin = builtin; return info; } static void mapper_input_info_free (MetaMapperInputInfo *info) { g_free (info); } static MetaMapperOutputInfo * mapper_output_info_new (MetaLogicalMonitor *logical_monitor) { MetaMapperOutputInfo *info; info = g_new0 (MetaMapperOutputInfo, 1); info->logical_monitor = logical_monitor; return info; } static void mapper_output_info_free (MetaMapperOutputInfo *info) { g_free (info); } static MetaInputCapabilityFlags mapper_input_info_get_caps (MetaMapperInputInfo *info) { ClutterInputDeviceType type; type = clutter_input_device_get_device_type (info->device); switch (type) { case CLUTTER_TOUCHSCREEN_DEVICE: return META_INPUT_CAP_TOUCH; case CLUTTER_TABLET_DEVICE: case CLUTTER_PEN_DEVICE: return META_INPUT_CAP_STYLUS; case CLUTTER_ERASER_DEVICE: return META_INPUT_CAP_ERASER; case CLUTTER_CURSOR_DEVICE: return META_INPUT_CAP_CURSOR; case CLUTTER_PAD_DEVICE: return META_INPUT_CAP_PAD; default: return 0; } } static void mapper_input_info_set_output (MetaMapperInputInfo *input, MetaMapperOutputInfo *output, MetaMonitor *monitor) { if (input->output == output) return; input->output = output; g_signal_emit (input->mapper, signals[DEVICE_MAPPED], 0, input->device, output ? output->logical_monitor : NULL, monitor); } static void mapper_output_info_add_input (MetaMapperOutputInfo *output, MetaMapperInputInfo *input, MetaMonitor *monitor) { g_assert (input->output == NULL); output->input_devices = g_list_prepend (output->input_devices, input); output->attached_caps |= mapper_input_info_get_caps (input); mapper_input_info_set_output (input, output, monitor); } static void mapper_output_info_remove_input (MetaMapperOutputInfo *output, MetaMapperInputInfo *input) { GList *l; g_assert (input->output == output); output->input_devices = g_list_remove (output->input_devices, input); output->attached_caps = 0; for (l = output->input_devices; l; l = l->next) output->attached_caps |= mapper_input_info_get_caps (l->data); mapper_input_info_set_output (input, NULL, NULL); } static void mapper_output_info_clear_inputs (MetaMapperOutputInfo *output) { while (output->input_devices) { MetaMapperInputInfo *input = output->input_devices->data; mapper_input_info_set_output (input, NULL, NULL); output->input_devices = g_list_remove (output->input_devices, input); } output->attached_caps = 0; } static void mapping_helper_init (MappingHelper *helper) { helper->device_maps = g_array_new (FALSE, FALSE, sizeof (DeviceCandidates)); } static void mapping_helper_release (MappingHelper *helper) { g_array_unref (helper->device_maps); } static gboolean match_edid (MetaMapperInputInfo *input, MetaMonitor *monitor, MetaOutputMatchType *match_type) { const gchar *dev_name; dev_name = clutter_input_device_get_device_name (input->device); if (strcasestr (dev_name, meta_monitor_get_vendor (monitor)) == NULL) return FALSE; *match_type = META_MATCH_EDID_VENDOR; if (strcasestr (dev_name, meta_monitor_get_product (monitor)) != NULL) { *match_type = META_MATCH_EDID_FULL; } else { int i; g_auto (GStrv) split = NULL; split = g_strsplit (meta_monitor_get_product (monitor), " ", -1); for (i = 0; split[i]; i++) { if (strcasestr (dev_name, split[i]) != NULL) { *match_type = META_MATCH_EDID_PARTIAL; break; } } } return TRUE; } static gboolean input_device_get_physical_size (MetaInputMapper *mapper, ClutterInputDevice *device, double *width, double *height) { #ifdef HAVE_LIBGUDEV g_autoptr (GUdevDevice) udev_device = NULL; const char *node; node = clutter_input_device_get_device_node (device); udev_device = g_udev_client_query_by_device_file (mapper->udev_client, node); if (udev_device && g_udev_device_has_property (udev_device, "ID_INPUT_WIDTH_MM")) { *width = g_udev_device_get_property_as_double (udev_device, "ID_INPUT_WIDTH_MM"); *height = g_udev_device_get_property_as_double (udev_device, "ID_INPUT_HEIGHT_MM"); return TRUE; } #endif return FALSE; } static gboolean find_size_match (MetaMapperInputInfo *input, GList *monitors, MetaMonitor **matched_monitor) { double min_w_diff, min_h_diff; double i_width, i_height; gboolean found = FALSE; GList *l; min_w_diff = min_h_diff = MAX_SIZE_MATCH_DIFF; if (!input_device_get_physical_size (input->mapper, input->device, &i_width, &i_height)) return FALSE; for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; double w_diff, h_diff; int o_width, o_height; meta_monitor_get_physical_dimensions (monitor, &o_width, &o_height); w_diff = ABS (1 - ((double) o_width / i_width)); h_diff = ABS (1 - ((double) o_height / i_height)); if (w_diff >= min_w_diff || h_diff >= min_h_diff) continue; *matched_monitor = monitor; min_w_diff = w_diff; min_h_diff = h_diff; found = TRUE; } return found; } static gboolean find_builtin_output (MetaInputMapper *mapper, MetaMonitor **matched_monitor) { MetaMonitor *panel; panel = meta_monitor_manager_get_laptop_panel (mapper->monitor_manager); *matched_monitor = panel; return panel != NULL; } static void guess_candidates (MetaInputMapper *mapper, MetaMapperInputInfo *input, DeviceCandidates *info) { MetaOutputMatchType best = N_OUTPUT_MATCHES; GList *monitors, *l; MetaMonitor *matched_monitor = NULL; monitors = meta_monitor_manager_get_monitors (mapper->monitor_manager); for (l = monitors; l; l = l->next) { MetaOutputMatchType edid_match; if (match_edid (input, l->data, &edid_match)) { best = MIN (best, edid_match); info->candidates[edid_match] = l->data; } } if (find_size_match (input, monitors, &matched_monitor)) { best = MIN (best, META_MATCH_SIZE); info->candidates[META_MATCH_SIZE] = matched_monitor; } if (input->builtin || best == N_OUTPUT_MATCHES) { best = MIN (best, META_MATCH_IS_BUILTIN); find_builtin_output (mapper, &info->candidates[META_MATCH_IS_BUILTIN]); } info->best = best; } static void mapping_helper_add (MappingHelper *helper, MetaMapperInputInfo *input, MetaInputMapper *mapper) { DeviceCandidates info = { 0, }; guint i, pos = 0; info.input = input; guess_candidates (mapper, input, &info); for (i = 0; i < helper->device_maps->len; i++) { DeviceCandidates *elem; elem = &g_array_index (helper->device_maps, DeviceCandidates, i); if (elem->best < info.best) pos = i; } if (pos >= helper->device_maps->len) g_array_append_val (helper->device_maps, info); else g_array_insert_val (helper->device_maps, pos, info); } static void mapping_helper_apply (MappingHelper *helper, MetaInputMapper *mapper) { guint i; /* Now, decide which input claims which output */ for (i = 0; i < helper->device_maps->len; i++) { MetaMapperOutputInfo *output; DeviceCandidates *info; MetaOutputMatchType j; info = &g_array_index (helper->device_maps, DeviceCandidates, i); for (j = 0; j < N_OUTPUT_MATCHES; j++) { MetaLogicalMonitor *logical_monitor; if (!info->candidates[j]) continue; logical_monitor = meta_monitor_get_logical_monitor (info->candidates[j]); output = g_hash_table_lookup (mapper->output_devices, logical_monitor); if (!output) continue; if (output->attached_caps & mapper_input_info_get_caps (info->input)) continue; mapper_output_info_add_input (output, info->input, info->candidates[j]); break; } } } static void mapper_recalculate_candidates (MetaInputMapper *mapper) { MetaMapperInputInfo *input; MappingHelper helper; GHashTableIter iter; mapping_helper_init (&helper); g_hash_table_iter_init (&iter, mapper->input_devices); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &input)) mapping_helper_add (&helper, input, mapper); mapping_helper_apply (&helper, mapper); mapping_helper_release (&helper); } static void mapper_recalculate_input (MetaInputMapper *mapper, MetaMapperInputInfo *input) { MappingHelper helper; mapping_helper_init (&helper); mapping_helper_add (&helper, input, mapper); mapping_helper_apply (&helper, mapper); mapping_helper_release (&helper); } static void mapper_update_outputs (MetaInputMapper *mapper) { MetaMapperOutputInfo *output; GList *logical_monitors, *l; GHashTableIter iter; g_hash_table_iter_init (&iter, mapper->output_devices); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &output)) { mapper_output_info_clear_inputs (output); g_hash_table_iter_remove (&iter); } logical_monitors = meta_monitor_manager_get_logical_monitors (mapper->monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaMapperOutputInfo *info; info = mapper_output_info_new (logical_monitor); g_hash_table_insert (mapper->output_devices, logical_monitor, info); } mapper_recalculate_candidates (mapper); } static void input_mapper_monitors_changed_cb (MetaMonitorManager *monitor_manager, MetaInputMapper *mapper) { mapper_update_outputs (mapper); } static void input_mapper_device_removed_cb (ClutterSeat *seat, ClutterInputDevice *device, MetaInputMapper *mapper) { meta_input_mapper_remove_device (mapper, device); } static void meta_input_mapper_finalize (GObject *object) { MetaInputMapper *mapper = META_INPUT_MAPPER (object); g_signal_handlers_disconnect_by_func (mapper->monitor_manager, input_mapper_monitors_changed_cb, mapper); g_signal_handlers_disconnect_by_func (mapper->seat, input_mapper_device_removed_cb, mapper); g_hash_table_unref (mapper->input_devices); g_hash_table_unref (mapper->output_devices); #ifdef HAVE_LIBGUDEV g_clear_object (&mapper->udev_client); #endif G_OBJECT_CLASS (meta_input_mapper_parent_class)->finalize (object); } static void meta_input_mapper_constructed (GObject *object) { #ifdef HAVE_LIBGUDEV const char *udev_subsystems[] = { "input", NULL }; #endif MetaInputMapper *mapper = META_INPUT_MAPPER (object); MetaBackend *backend; G_OBJECT_CLASS (meta_input_mapper_parent_class)->constructed (object); #ifdef HAVE_LIBGUDEV mapper->udev_client = g_udev_client_new (udev_subsystems); #endif mapper->seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_connect (mapper->seat, "device-removed", G_CALLBACK (input_mapper_device_removed_cb), mapper); backend = meta_get_backend (); mapper->monitor_manager = meta_backend_get_monitor_manager (backend); g_signal_connect (mapper->monitor_manager, "monitors-changed-internal", G_CALLBACK (input_mapper_monitors_changed_cb), mapper); mapper_update_outputs (mapper); } static void meta_input_mapper_class_init (MetaInputMapperClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_input_mapper_constructed; object_class->finalize = meta_input_mapper_finalize; signals[DEVICE_MAPPED] = g_signal_new ("device-mapped", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 3, CLUTTER_TYPE_INPUT_DEVICE, G_TYPE_POINTER, G_TYPE_POINTER); } static void meta_input_mapper_init (MetaInputMapper *mapper) { mapper->input_devices = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) mapper_input_info_free); mapper->output_devices = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) mapper_output_info_free); } MetaInputMapper * meta_input_mapper_new (void) { return g_object_new (META_TYPE_INPUT_MAPPER, NULL); } void meta_input_mapper_add_device (MetaInputMapper *mapper, ClutterInputDevice *device, gboolean builtin) { MetaMapperInputInfo *info; g_return_if_fail (mapper != NULL); g_return_if_fail (device != NULL); if (g_hash_table_contains (mapper->input_devices, device)) return; info = mapper_input_info_new (device, mapper, builtin); g_hash_table_insert (mapper->input_devices, device, info); mapper_recalculate_input (mapper, info); } void meta_input_mapper_remove_device (MetaInputMapper *mapper, ClutterInputDevice *device) { MetaMapperInputInfo *input; g_return_if_fail (mapper != NULL); g_return_if_fail (device != NULL); input = g_hash_table_lookup (mapper->input_devices, device); if (input) { if (input->output) mapper_output_info_remove_input (input->output, input); g_hash_table_remove (mapper->input_devices, device); } } ClutterInputDevice * meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, MetaLogicalMonitor *logical_monitor, ClutterInputDeviceType device_type) { MetaMapperOutputInfo *output; GList *l; output = g_hash_table_lookup (mapper->output_devices, logical_monitor); if (!output) return NULL; for (l = output->input_devices; l; l = l->next) { MetaMapperInputInfo *input = l->data; if (clutter_input_device_get_device_type (input->device) == device_type) return input->device; } return NULL; } MetaLogicalMonitor * meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper, ClutterInputDevice *device) { MetaMapperOutputInfo *output; MetaLogicalMonitor *logical_monitor; GHashTableIter iter; GList *l; g_hash_table_iter_init (&iter, mapper->output_devices); while (g_hash_table_iter_next (&iter, (gpointer *) &logical_monitor, (gpointer *) &output)) { for (l = output->input_devices; l; l = l->next) { MetaMapperInputInfo *input = l->data; if (input->device == device) return logical_monitor; } } return NULL; } muffin-6.4.1/src/backends/meta-settings.c0000664000175000017500000004730514723361714017244 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-settings-private.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "ui/theme-private.h" #ifndef XWAYLAND_GRAB_DEFAULT_ACCESS_RULES # warning "XWAYLAND_GRAB_DEFAULT_ACCESS_RULES is not set" # define XWAYLAND_GRAB_DEFAULT_ACCESS_RULES "" #endif enum { UI_SCALING_FACTOR_CHANGED, GLOBAL_SCALING_FACTOR_CHANGED, FONT_DPI_CHANGED, X11_SCALE_MODE_CHANGED, EXPERIMENTAL_FEATURES_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaSettings { GObject parent; MetaBackend *backend; GSettings *interface_settings; GSettings *mutter_settings; GSettings *wayland_settings; GSettings *x11_settings; int ui_scaling_factor; int global_scaling_factor; int font_dpi; MetaExperimentalFeature experimental_features; gboolean experimental_features_overridden; gboolean xwayland_allow_grabs; GPtrArray *xwayland_grab_whitelist_patterns; GPtrArray *xwayland_grab_blacklist_patterns; MetaX11ScaleMode x11_scale_mode; }; G_DEFINE_TYPE (MetaSettings, meta_settings, G_TYPE_OBJECT) static int calculate_ui_scaling_factor (MetaSettings *settings) { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (settings->backend); if (!meta_is_wayland_compositor () && monitor_manager && (meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) { MetaLogicalMonitorLayoutMode layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { return ceilf (meta_monitor_manager_get_maximum_crtc_scale (monitor_manager)); } else if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL) { return 1.0f; } } if (monitor_manager) { MetaLogicalMonitor *primary_logical_monitor; primary_logical_monitor = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); if (!primary_logical_monitor) return 1; return (int) meta_logical_monitor_get_scale (primary_logical_monitor); } return 1; } static gboolean update_ui_scaling_factor (MetaSettings *settings) { int ui_scaling_factor; if (meta_is_stage_views_scaled ()) ui_scaling_factor = 1; else ui_scaling_factor = calculate_ui_scaling_factor (settings); if (settings->ui_scaling_factor != ui_scaling_factor) { settings->ui_scaling_factor = ui_scaling_factor; return TRUE; } else { return FALSE; } } void meta_settings_update_ui_scaling_factor (MetaSettings *settings) { if (update_ui_scaling_factor (settings)) g_signal_emit (settings, signals[UI_SCALING_FACTOR_CHANGED], 0); } int meta_settings_get_ui_scaling_factor (MetaSettings *settings) { g_assert (settings->ui_scaling_factor != 0); return settings->ui_scaling_factor; } static gboolean update_global_scaling_factor (MetaSettings *settings) { int global_scaling_factor; global_scaling_factor = (int) g_settings_get_uint (settings->interface_settings, "scaling-factor"); if (settings->global_scaling_factor != global_scaling_factor) { settings->global_scaling_factor = global_scaling_factor; return TRUE; } else { return FALSE; } } gboolean meta_settings_get_global_scaling_factor (MetaSettings *settings, int *out_scaling_factor) { if (settings->global_scaling_factor == 0) return FALSE; *out_scaling_factor = settings->global_scaling_factor; return TRUE; } static gboolean update_font_dpi (MetaSettings *settings) { double text_scaling_factor; /* Number of logical pixels on an inch when unscaled */ const double dots_per_inch = 96; /* Being based on Xft, API users expect the DPI to be 1/1024th of an inch. */ const double xft_factor = 1024; int font_dpi; text_scaling_factor = g_settings_get_double (settings->interface_settings, "text-scaling-factor"); font_dpi = (int) (text_scaling_factor * dots_per_inch * xft_factor * settings->ui_scaling_factor); if (font_dpi != settings->font_dpi) { settings->font_dpi = font_dpi; g_object_set (clutter_settings_get_default (), "font-dpi", font_dpi, NULL); return TRUE; } else { return FALSE; } } static void meta_settings_update_font_dpi (MetaSettings *settings) { if (update_font_dpi (settings)) g_signal_emit (settings, signals[FONT_DPI_CHANGED], 0); } double meta_settings_get_font_scaling_factor(MetaSettings *settings) { return g_settings_get_double (settings->interface_settings, "text-scaling-factor"); } int meta_settings_get_font_dpi (MetaSettings *settings) { g_assert (settings->font_dpi != 0); return settings->font_dpi; } static void interface_settings_changed (GSettings *interface_settings, const char *key, MetaSettings *settings) { if (g_str_equal (key, "scaling-factor")) { if (update_global_scaling_factor (settings)) g_signal_emit (settings, signals[GLOBAL_SCALING_FACTOR_CHANGED], 0); } else if (g_str_equal (key, "text-scaling-factor")) { meta_settings_update_font_dpi (settings); } } gboolean meta_settings_is_experimental_feature_enabled (MetaSettings *settings, MetaExperimentalFeature feature) { return !!(settings->experimental_features & feature); } void meta_settings_override_experimental_features (MetaSettings *settings) { settings->experimental_features = META_EXPERIMENTAL_FEATURE_NONE; settings->experimental_features_overridden = TRUE; } static gboolean update_x11_scale_mode (MetaSettings *settings) { MetaX11ScaleMode scale_mode; if (!(settings->experimental_features & META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) { scale_mode = META_X11_SCALE_MODE_NONE; } else { scale_mode = g_settings_get_enum (settings->x11_settings, "fractional-scale-mode"); } if (settings->x11_scale_mode != scale_mode) { settings->x11_scale_mode = scale_mode; return TRUE; } return FALSE; } void meta_settings_enable_x11_fractional_scaling (MetaSettings *settings, gboolean enable) { g_auto(GStrv) existing_features = NULL; gboolean have_fractional_scaling = FALSE; g_autoptr(GVariantBuilder) builder = NULL; MetaExperimentalFeature old_experimental_features; if (enable == meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) return; /* Change the internal value now, as we don't want to wait for gsettings */ old_experimental_features = settings->experimental_features; settings->experimental_features |= META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING; update_x11_scale_mode (settings); g_signal_emit (settings, signals[EXPERIMENTAL_FEATURES_CHANGED], 0, (unsigned int) old_experimental_features); /* Add or remove the fractional scaling feature from mutter */ existing_features = g_settings_get_strv (settings->mutter_settings, "experimental-features"); builder = g_variant_builder_new (G_VARIANT_TYPE ("as")); for (int i = 0; existing_features[i] != NULL; i++) { if (g_strcmp0 (existing_features[i], "x11-randr-fractional-scaling") == 0) { if (enable) have_fractional_scaling = TRUE; else continue; } g_variant_builder_add (builder, "s", existing_features[i]); } if (enable && !have_fractional_scaling) g_variant_builder_add (builder, "s", "x11-randr-fractional-scaling"); g_settings_set_value (settings->mutter_settings, "experimental-features", g_variant_builder_end (builder)); } void meta_settings_enable_experimental_feature (MetaSettings *settings, MetaExperimentalFeature feature) { g_assert (settings->experimental_features_overridden); settings->experimental_features |= feature; if (update_x11_scale_mode (settings)) g_signal_emit (settings, signals[X11_SCALE_MODE_CHANGED], 0, NULL); } static gboolean experimental_features_handler (GVariant *features_variant, gpointer *result, gpointer data) { MetaSettings *settings = data; GVariantIter features_iter; char *feature_str; MetaExperimentalFeature features = META_EXPERIMENTAL_FEATURE_NONE; if (settings->experimental_features_overridden) { *result = GINT_TO_POINTER (FALSE); return TRUE; } g_variant_iter_init (&features_iter, features_variant); while (g_variant_iter_loop (&features_iter, "s", &feature_str)) { MetaExperimentalFeature feature = META_EXPERIMENTAL_FEATURE_NONE; if (g_str_equal (feature_str, "scale-monitor-framebuffer")) feature = META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER; else if (g_str_equal (feature_str, "kms-modifiers")) feature = META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS; else if (g_str_equal (feature_str, "rt-scheduler")) feature = META_EXPERIMENTAL_FEATURE_RT_SCHEDULER; else if (g_str_equal (feature_str, "autostart-xwayland")) feature = META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND; else if (g_str_equal (feature_str, "x11-randr-fractional-scaling")) feature = META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING; if (feature) g_message ("Enabling experimental feature '%s'", feature_str); else g_warning ("Unknown experimental feature '%s'", feature_str); features |= feature; } if (features != settings->experimental_features) { settings->experimental_features = features; update_x11_scale_mode (settings); *result = GINT_TO_POINTER (TRUE); } else { *result = GINT_TO_POINTER (FALSE); } return TRUE; } static gboolean update_experimental_features (MetaSettings *settings) { return GPOINTER_TO_INT (g_settings_get_mapped (settings->mutter_settings, "experimental-features", experimental_features_handler, settings)); } static void mutter_settings_changed (GSettings *mutter_settings, gchar *key, MetaSettings *settings) { MetaExperimentalFeature old_experimental_features; if (!g_str_equal (key, "experimental-features")) return; old_experimental_features = settings->experimental_features; if (update_experimental_features (settings)) g_signal_emit (settings, signals[EXPERIMENTAL_FEATURES_CHANGED], 0, (unsigned int) old_experimental_features); } static void xwayland_grab_list_add_item (MetaSettings *settings, char *item) { /* If first character is '!', it's a blacklisted item */ if (item[0] != '!') g_ptr_array_add (settings->xwayland_grab_whitelist_patterns, g_pattern_spec_new (item)); else if (item[1] != 0) g_ptr_array_add (settings->xwayland_grab_blacklist_patterns, g_pattern_spec_new (&item[1])); } static gboolean xwayland_grab_access_rules_handler (GVariant *variant, gpointer *result, gpointer data) { MetaSettings *settings = data; GVariantIter iter; char *item; /* Create a GPatternSpec for each element */ g_variant_iter_init (&iter, variant); while (g_variant_iter_loop (&iter, "s", &item)) xwayland_grab_list_add_item (settings, item); *result = GINT_TO_POINTER (TRUE); return TRUE; } static void update_xwayland_grab_access_rules (MetaSettings *settings) { gchar **system_defaults; int i; /* Free previous patterns and create new arrays */ g_clear_pointer (&settings->xwayland_grab_whitelist_patterns, g_ptr_array_unref); settings->xwayland_grab_whitelist_patterns = g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free); g_clear_pointer (&settings->xwayland_grab_blacklist_patterns, g_ptr_array_unref); settings->xwayland_grab_blacklist_patterns = g_ptr_array_new_with_free_func ((GDestroyNotify) g_pattern_spec_free); /* Add system defaults values */ system_defaults = g_strsplit (XWAYLAND_GRAB_DEFAULT_ACCESS_RULES, ",", -1); for (i = 0; system_defaults[i]; i++) xwayland_grab_list_add_item (settings, system_defaults[i]); g_strfreev (system_defaults); /* Then add gsettings values */ g_settings_get_mapped (settings->wayland_settings, "xwayland-grab-access-rules", xwayland_grab_access_rules_handler, settings); } static void update_xwayland_allow_grabs (MetaSettings *settings) { settings->xwayland_allow_grabs = g_settings_get_boolean (settings->wayland_settings, "xwayland-allow-grabs"); } static void wayland_settings_changed (GSettings *wayland_settings, gchar *key, MetaSettings *settings) { if (g_str_equal (key, "xwayland-allow-grabs")) { update_xwayland_allow_grabs (settings); } else if (g_str_equal (key, "xwayland-grab-access-rules")) { update_xwayland_grab_access_rules (settings); } } static void x11_settings_changed (GSettings *wayland_settings, gchar *key, MetaSettings *settings) { if (g_str_equal (key, "fractional-scale-mode")) { if (update_x11_scale_mode (settings)) g_signal_emit (settings, signals[X11_SCALE_MODE_CHANGED], 0, NULL); } } void meta_settings_get_xwayland_grab_patterns (MetaSettings *settings, GPtrArray **whitelist_patterns, GPtrArray **blacklist_patterns) { *whitelist_patterns = settings->xwayland_grab_whitelist_patterns; *blacklist_patterns = settings->xwayland_grab_blacklist_patterns; } gboolean meta_settings_are_xwayland_grabs_allowed (MetaSettings *settings) { return (settings->xwayland_allow_grabs); } MetaX11ScaleMode meta_settings_get_x11_scale_mode (MetaSettings *settings) { return settings->x11_scale_mode; } MetaSettings * meta_settings_new (MetaBackend *backend) { MetaSettings *settings; settings = g_object_new (META_TYPE_SETTINGS, NULL); settings->backend = backend; return settings; } static void meta_settings_dispose (GObject *object) { MetaSettings *settings = META_SETTINGS (object); g_clear_object (&settings->mutter_settings); g_clear_object (&settings->interface_settings); g_clear_object (&settings->wayland_settings); g_clear_object (&settings->x11_settings); g_clear_pointer (&settings->xwayland_grab_whitelist_patterns, g_ptr_array_unref); g_clear_pointer (&settings->xwayland_grab_blacklist_patterns, g_ptr_array_unref); G_OBJECT_CLASS (meta_settings_parent_class)->dispose (object); } static void meta_settings_init (MetaSettings *settings) { settings->interface_settings = g_settings_new ("org.cinnamon.desktop.interface"); g_signal_connect (settings->interface_settings, "changed", G_CALLBACK (interface_settings_changed), settings); settings->mutter_settings = g_settings_new ("org.cinnamon.muffin"); g_signal_connect (settings->mutter_settings, "changed", G_CALLBACK (mutter_settings_changed), settings); settings->wayland_settings = g_settings_new ("org.cinnamon.muffin.wayland"); g_signal_connect (settings->wayland_settings, "changed", G_CALLBACK (wayland_settings_changed), settings); settings->x11_settings = g_settings_new ("org.cinnamon.muffin.x11"); g_signal_connect (settings->x11_settings, "changed", G_CALLBACK (x11_settings_changed), settings); /* Chain up inter-dependent settings. */ g_signal_connect (settings, "global-scaling-factor-changed", G_CALLBACK (meta_settings_update_ui_scaling_factor), NULL); g_signal_connect (settings, "ui-scaling-factor-changed", G_CALLBACK (meta_settings_update_font_dpi), NULL); update_global_scaling_factor (settings); update_experimental_features (settings); update_xwayland_grab_access_rules (settings); update_xwayland_allow_grabs (settings); } static void on_monitors_changed (MetaMonitorManager *monitor_manager, MetaSettings *settings) { meta_settings_update_ui_scaling_factor (settings); } void meta_settings_post_init (MetaSettings *settings) { MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (settings->backend); update_ui_scaling_factor (settings); update_font_dpi (settings); g_signal_connect_object (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), settings, G_CONNECT_AFTER); } static void meta_settings_class_init (MetaSettingsClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_settings_dispose; signals[UI_SCALING_FACTOR_CHANGED] = g_signal_new ("ui-scaling-factor-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[GLOBAL_SCALING_FACTOR_CHANGED] = g_signal_new ("global-scaling-factor-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[FONT_DPI_CHANGED] = g_signal_new ("font-dpi-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[X11_SCALE_MODE_CHANGED] = g_signal_new ("x11-scale-mode-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[EXPERIMENTAL_FEATURES_CHANGED] = g_signal_new ("experimental-features-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); } muffin-6.4.1/src/backends/meta-gles3-table.h0000664000175000017500000000221414723361714017501 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_GLES3_TABLE_H #define META_GLES3_TABLE_H #include #include #include typedef struct _MetaGles3Table { void (* glEGLImageTargetTexture2DOES) (GLenum target, GLeglImageOES image); } MetaGles3Table; #endif /* META_GLES3_TABLE */ muffin-6.4.1/src/backends/meta-gles3.h0000664000175000017500000000743514723361714016426 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_GLES3_H #define META_GLES3_H #include #include "backends/meta-egl.h" typedef struct _MetaGles3Table MetaGles3Table; #define META_TYPE_GLES3 (meta_gles3_get_type ()) G_DECLARE_FINAL_TYPE (MetaGles3, meta_gles3, META, GLES3, GObject) MetaGles3Table * meta_gles3_get_table (MetaGles3 *gles3); void meta_gles3_clear_error (MetaGles3 *gles3); gboolean meta_gles3_validate (MetaGles3 *gles3, GError **error); void meta_gles3_ensure_loaded (MetaGles3 *gles, gpointer *func, const char *name); gboolean meta_gles3_has_extensions (MetaGles3 *gles3, const char ***missing_extensions, const char *first_extension, ...); MetaGles3 * meta_gles3_new (MetaEgl *egl); #define GLBAS(gles3, func, args) \ { \ GError *_error = NULL; \ \ func args; \ \ if (!meta_gles3_validate (gles3, &_error)) \ { \ g_warning ("%s %s failed: %s", #func, #args, _error->message); \ g_error_free (_error); \ } \ } #define GLEXT(gles3, func, args) \ { \ GError *_error = NULL; \ MetaGles3Table *table; \ \ table = meta_gles3_get_table (gles3); \ meta_gles3_ensure_loaded (gles3, (gpointer *) &table->func, #func); \ \ table->func args; \ \ if (!meta_gles3_validate (gles3, &_error)) \ { \ g_warning ("%s %s failed: %s", #func, #args, _error->message); \ g_error_free (_error); \ } \ } #endif /* META_GLES3_H */ muffin-6.4.1/src/backends/meta-screen-cast-window.h0000664000175000017500000001037714723361714021124 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_WINDOW_H #define META_SCREEN_CAST_WINDOW_H #include #include #include "backends/meta-cursor.h" #include "meta/boxes.h" G_BEGIN_DECLS #define META_TYPE_SCREEN_CAST_WINDOW (meta_screen_cast_window_get_type ()) G_DECLARE_INTERFACE (MetaScreenCastWindow, meta_screen_cast_window, META, SCREEN_CAST_WINDOW, GObject) struct _MetaScreenCastWindowInterface { GTypeInterface parent_iface; void (*get_buffer_bounds) (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds); void (*transform_relative_position) (MetaScreenCastWindow *screen_cast_window, double x, double y, double *x_out, double *y_out); gboolean (*transform_cursor_position) (MetaScreenCastWindow *screen_cast_window, MetaCursorSprite *cursor_sprite, graphene_point_t *cursor_position, float *out_cursor_scale, graphene_point_t *out_relative_cursor_position); void (*capture_into) (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, uint8_t *data); gboolean (*blit_to_framebuffer) (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, CoglFramebuffer *framebuffer); gboolean (*has_damage) (MetaScreenCastWindow *screen_cast_window); }; void meta_screen_cast_window_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds); void meta_screen_cast_window_transform_relative_position (MetaScreenCastWindow *screen_cast_window, double x, double y, double *x_out, double *y_out); gboolean meta_screen_cast_window_transform_cursor_position (MetaScreenCastWindow *screen_cast_window, MetaCursorSprite *cursor_sprite, graphene_point_t *cursor_position, float *out_cursor_scale, graphene_point_t *out_relative_cursor_position); void meta_screen_cast_window_capture_into (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, uint8_t *data); gboolean meta_screen_cast_window_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, CoglFramebuffer *framebuffer); gboolean meta_screen_cast_window_has_damage (MetaScreenCastWindow *screen_cast_window); G_END_DECLS #endif /* META_SCREEN_CAST_WINDOW_H */ muffin-6.4.1/src/backends/meta-remote-desktop.c0000664000175000017500000002145214723361714020341 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-remote-desktop.h" #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-remote-desktop-session.h" #include "backends/native/meta-cursor-renderer-native.h" #include "meta/meta-backend.h" #include "meta-dbus-remote-desktop.h" #define META_REMOTE_DESKTOP_DBUS_SERVICE "org.cinnamon.Muffin.RemoteDesktop" #define META_REMOTE_DESKTOP_DBUS_PATH "/org/cinnamon/Muffin/RemoteDesktop" #define META_REMOTE_DESKTOP_API_VERSION 1 typedef enum _MetaRemoteDesktopDeviceTypes { META_REMOTE_DESKTOP_DEVICE_TYPE_NONE = 0, META_REMOTE_DESKTOP_DEVICE_TYPE_KEYBOARD = 1 << 0, META_REMOTE_DESKTOP_DEVICE_TYPE_POINTER = 1 << 1, META_REMOTE_DESKTOP_DEVICE_TYPE_TOUCHSCREEN = 1 << 2, } MetaRemoteDesktopDeviceTypes; struct _MetaRemoteDesktop { MetaDBusRemoteDesktopSkeleton parent; int dbus_name_id; GHashTable *sessions; MetaDbusSessionWatcher *session_watcher; }; static void meta_remote_desktop_init_iface (MetaDBusRemoteDesktopIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktop, meta_remote_desktop, META_DBUS_TYPE_REMOTE_DESKTOP_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP, meta_remote_desktop_init_iface)); GDBusConnection * meta_remote_desktop_get_connection (MetaRemoteDesktop *remote_desktop) { GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (remote_desktop); return g_dbus_interface_skeleton_get_connection (interface_skeleton); } MetaRemoteDesktopSession * meta_remote_desktop_get_session (MetaRemoteDesktop *remote_desktop, const char *session_id) { return g_hash_table_lookup (remote_desktop->sessions, session_id); } static void on_session_closed (MetaRemoteDesktopSession *session, MetaRemoteDesktop *remote_desktop) { char *session_id; session_id = meta_remote_desktop_session_get_session_id (session); g_hash_table_remove (remote_desktop->sessions, session_id); } static gboolean handle_create_session (MetaDBusRemoteDesktop *skeleton, GDBusMethodInvocation *invocation) { MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (skeleton); const char *peer_name; MetaRemoteDesktopSession *session; GError *error = NULL; char *session_id; char *session_path; const char *client_dbus_name; peer_name = g_dbus_method_invocation_get_sender (invocation); session = meta_remote_desktop_session_new (remote_desktop, peer_name, &error); if (!session) { g_warning ("Failed to create remote desktop session: %s", error->message); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to create session: %s", error->message); g_error_free (error); return TRUE; } session_id = meta_remote_desktop_session_get_session_id (session); g_hash_table_insert (remote_desktop->sessions, session_id, session); client_dbus_name = g_dbus_method_invocation_get_sender (invocation); meta_dbus_session_watcher_watch_session (remote_desktop->session_watcher, client_dbus_name, META_DBUS_SESSION (session)); session_path = meta_remote_desktop_session_get_object_path (session); meta_dbus_remote_desktop_complete_create_session (skeleton, invocation, session_path); g_signal_connect (session, "session-closed", G_CALLBACK (on_session_closed), remote_desktop); return TRUE; } static void meta_remote_desktop_init_iface (MetaDBusRemoteDesktopIface *iface) { iface->handle_create_session = handle_create_session; } static void on_bus_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { MetaRemoteDesktop *remote_desktop = user_data; GDBusInterfaceSkeleton *interface_skeleton = G_DBUS_INTERFACE_SKELETON (remote_desktop); GError *error = NULL; if (!g_dbus_interface_skeleton_export (interface_skeleton, connection, META_REMOTE_DESKTOP_DBUS_PATH, &error)) g_warning ("Failed to export remote desktop object: %s\n", error->message); } static void on_name_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { g_info ("Acquired name %s\n", name); } static void on_name_lost (GDBusConnection *connection, const char *name, gpointer user_data) { g_warning ("Lost or failed to acquire name %s\n", name); } static void meta_remote_desktop_constructed (GObject *object) { MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (object); remote_desktop->dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, META_REMOTE_DESKTOP_DBUS_SERVICE, G_BUS_NAME_OWNER_FLAGS_NONE, on_bus_acquired, on_name_acquired, on_name_lost, remote_desktop, NULL); } static void meta_remote_desktop_finalize (GObject *object) { MetaRemoteDesktop *remote_desktop = META_REMOTE_DESKTOP (object); GList *sessions; if (remote_desktop->dbus_name_id != 0) g_bus_unown_name (remote_desktop->dbus_name_id); sessions = g_list_copy (g_hash_table_get_values (remote_desktop->sessions)); g_list_free_full (sessions, (GDestroyNotify) meta_remote_desktop_session_close); g_hash_table_destroy (remote_desktop->sessions); G_OBJECT_CLASS (meta_remote_desktop_parent_class)->finalize (object); } MetaRemoteDesktop * meta_remote_desktop_new (MetaDbusSessionWatcher *session_watcher) { MetaRemoteDesktop *remote_desktop; remote_desktop = g_object_new (META_TYPE_REMOTE_DESKTOP, NULL); remote_desktop->session_watcher = session_watcher; return remote_desktop; } static MetaRemoteDesktopDeviceTypes calculate_supported_device_types (void) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); ClutterVirtualDeviceType device_types; MetaRemoteDesktopDeviceTypes supported_devices = META_REMOTE_DESKTOP_DEVICE_TYPE_NONE; device_types = clutter_seat_get_supported_virtual_device_types (seat); if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD) supported_devices |= META_REMOTE_DESKTOP_DEVICE_TYPE_KEYBOARD; if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER) supported_devices |= META_REMOTE_DESKTOP_DEVICE_TYPE_POINTER; if (device_types & CLUTTER_VIRTUAL_DEVICE_TYPE_TOUCHSCREEN) supported_devices |= META_REMOTE_DESKTOP_DEVICE_TYPE_TOUCHSCREEN; return supported_devices; } static void meta_remote_desktop_init (MetaRemoteDesktop *remote_desktop) { remote_desktop->sessions = g_hash_table_new (g_str_hash, g_str_equal); meta_dbus_remote_desktop_set_supported_device_types ( META_DBUS_REMOTE_DESKTOP (remote_desktop), calculate_supported_device_types ()); meta_dbus_remote_desktop_set_version ( META_DBUS_REMOTE_DESKTOP (remote_desktop), META_REMOTE_DESKTOP_API_VERSION); } static void meta_remote_desktop_class_init (MetaRemoteDesktopClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_remote_desktop_constructed; object_class->finalize = meta_remote_desktop_finalize; } muffin-6.4.1/src/backends/meta-display-config-shared.h0000664000175000017500000000241014723361714021551 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* This file is shared between mutter (src/core/meta-display-config-shared.h) and gnome-desktop (libgnome-desktop/meta-xrandr-shared.h). The canonical place for all changes is mutter. There should be no includes in this file. */ #ifndef META_DISPLAY_CONFIG_SHARED_H #define META_DISPLAY_CONFIG_SHARED_H typedef enum { META_POWER_SAVE_UNSUPPORTED = -1, META_POWER_SAVE_ON = 0, META_POWER_SAVE_STANDBY, META_POWER_SAVE_SUSPEND, META_POWER_SAVE_OFF, } MetaPowerSave; #endif /* META_DISPLAY_CONFIG_SHARED_H */ muffin-6.4.1/src/backends/meta-orientation-manager.c0000664000175000017500000001705314723361714021344 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-orientation-manager.h" #include enum { ORIENTATION_CHANGED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaOrientationManager { GObject parent_instance; GCancellable *cancellable; guint iio_watch_id; GDBusProxy *iio_proxy; MetaOrientation prev_orientation; MetaOrientation curr_orientation; GSettings *settings; }; G_DEFINE_TYPE (MetaOrientationManager, meta_orientation_manager, G_TYPE_OBJECT) #define CONF_SCHEMA "org.cinnamon.settings-daemon.peripherals.touchscreen" #define ORIENTATION_LOCK_KEY "orientation-lock" static MetaOrientation orientation_from_string (const char *orientation) { if (g_strcmp0 (orientation, "normal") == 0) return META_ORIENTATION_NORMAL; if (g_strcmp0 (orientation, "bottom-up") == 0) return META_ORIENTATION_BOTTOM_UP; if (g_strcmp0 (orientation, "left-up") == 0) return META_ORIENTATION_LEFT_UP; if (g_strcmp0 (orientation, "right-up") == 0) return META_ORIENTATION_RIGHT_UP; return META_ORIENTATION_UNDEFINED; } static void read_iio_proxy (MetaOrientationManager *self) { gboolean has_accel = FALSE; GVariant *v; self->curr_orientation = META_ORIENTATION_UNDEFINED; if (!self->iio_proxy) return; v = g_dbus_proxy_get_cached_property (self->iio_proxy, "HasAccelerometer"); if (v) { has_accel = g_variant_get_boolean (v); g_variant_unref (v); } if (has_accel) { v = g_dbus_proxy_get_cached_property (self->iio_proxy, "AccelerometerOrientation"); if (v) { self->curr_orientation = orientation_from_string (g_variant_get_string (v, NULL)); g_variant_unref (v); } } } static void sync_state (MetaOrientationManager *self) { if (g_settings_get_boolean (self->settings, ORIENTATION_LOCK_KEY)) return; read_iio_proxy (self); if (self->prev_orientation == self->curr_orientation) return; self->prev_orientation = self->curr_orientation; if (self->curr_orientation == META_ORIENTATION_UNDEFINED) return; g_signal_emit (self, signals[ORIENTATION_CHANGED], 0); } static void orientation_lock_changed (GSettings *settings, gchar *key, gpointer user_data) { MetaOrientationManager *self = user_data; sync_state (self); } static void iio_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data) { MetaOrientationManager *self = user_data; sync_state (self); } static void accelerometer_claimed (GObject *source, GAsyncResult *res, gpointer user_data) { MetaOrientationManager *self = user_data; GVariant *v; GError *error = NULL; v = g_dbus_proxy_call_finish (G_DBUS_PROXY (source), res, &error); if (!v) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to claim accelerometer: %s", error->message); g_error_free (error); return; } g_variant_unref (v); sync_state (self); } static void iio_proxy_ready (GObject *source, GAsyncResult *res, gpointer user_data) { MetaOrientationManager *self = user_data; GDBusProxy *proxy; GError *error = NULL; proxy = g_dbus_proxy_new_finish (res, &error); if (!proxy) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to obtain IIO DBus proxy: %s", error->message); g_error_free (error); return; } self->iio_proxy = proxy; g_signal_connect_object (self->iio_proxy, "g-properties-changed", G_CALLBACK (iio_properties_changed), self, 0); g_dbus_proxy_call (self->iio_proxy, "ClaimAccelerometer", NULL, G_DBUS_CALL_FLAGS_NONE, -1, self->cancellable, accelerometer_claimed, self); } static void iio_sensor_appeared_cb (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { MetaOrientationManager *self = user_data; self->cancellable = g_cancellable_new (); g_dbus_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, "net.hadess.SensorProxy", "/net/hadess/SensorProxy", "net.hadess.SensorProxy", self->cancellable, iio_proxy_ready, self); } static void iio_sensor_vanished_cb (GDBusConnection *connection, const gchar *name, gpointer user_data) { MetaOrientationManager *self = user_data; g_cancellable_cancel (self->cancellable); g_clear_object (&self->cancellable); g_clear_object (&self->iio_proxy); sync_state (self); } static void meta_orientation_manager_init (MetaOrientationManager *self) { self->iio_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "net.hadess.SensorProxy", G_BUS_NAME_WATCHER_FLAGS_NONE, iio_sensor_appeared_cb, iio_sensor_vanished_cb, self, NULL); self->settings = g_settings_new (CONF_SCHEMA); g_signal_connect_object (self->settings, "changed::"ORIENTATION_LOCK_KEY, G_CALLBACK (orientation_lock_changed), self, 0); sync_state (self); } static void meta_orientation_manager_finalize (GObject *object) { MetaOrientationManager *self = META_ORIENTATION_MANAGER (object); g_cancellable_cancel (self->cancellable); g_clear_object (&self->cancellable); g_bus_unwatch_name (self->iio_watch_id); g_clear_object (&self->iio_proxy); g_clear_object (&self->settings); G_OBJECT_CLASS (meta_orientation_manager_parent_class)->finalize (object); } static void meta_orientation_manager_class_init (MetaOrientationManagerClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = meta_orientation_manager_finalize; signals[ORIENTATION_CHANGED] = g_signal_new ("orientation-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } MetaOrientation meta_orientation_manager_get_orientation (MetaOrientationManager *self) { return self->curr_orientation; } muffin-6.4.1/src/backends/meta-barrier.c0000664000175000017500000002321614723361714017025 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ /** * SECTION:barrier * @Title: MetaBarrier * @Short_Description: Pointer barriers */ #include "config.h" #include "meta/barrier.h" #include "backends/meta-barrier-private.h" #include #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-barrier-x11.h" #include "meta/meta-enum-types.h" #include "meta/util.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include "backends/native/meta-barrier-native.h" #endif G_DEFINE_TYPE_WITH_PRIVATE (MetaBarrier, meta_barrier, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaBarrierImpl, meta_barrier_impl, G_TYPE_OBJECT) enum { PROP_0, PROP_DISPLAY, PROP_X1, PROP_Y1, PROP_X2, PROP_Y2, PROP_DIRECTIONS, PROP_LAST, }; static GParamSpec *obj_props[PROP_LAST]; enum { HIT, LEFT, LAST_SIGNAL, }; static guint obj_signals[LAST_SIGNAL]; static void meta_barrier_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, priv->display); break; case PROP_X1: g_value_set_int (value, priv->border.line.a.x); break; case PROP_Y1: g_value_set_int (value, priv->border.line.a.y); break; case PROP_X2: g_value_set_int (value, priv->border.line.b.x); break; case PROP_Y2: g_value_set_int (value, priv->border.line.b.y); break; case PROP_DIRECTIONS: g_value_set_flags (value, meta_border_get_allows_directions (&priv->border)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_barrier_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; switch (prop_id) { case PROP_DISPLAY: priv->display = g_value_get_object (value); break; case PROP_X1: priv->border.line.a.x = g_value_get_int (value); break; case PROP_Y1: priv->border.line.a.y = g_value_get_int (value); break; case PROP_X2: priv->border.line.b.x = g_value_get_int (value); break; case PROP_Y2: priv->border.line.b.y = g_value_get_int (value); break; case PROP_DIRECTIONS: meta_border_set_allows_directions (&priv->border, g_value_get_flags (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_barrier_dispose (GObject *object) { MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; if (meta_barrier_is_active (barrier)) { meta_bug ("MetaBarrier %p was destroyed while it was still active.", barrier); } g_clear_object (&priv->impl); G_OBJECT_CLASS (meta_barrier_parent_class)->dispose (object); } gboolean meta_barrier_is_active (MetaBarrier *barrier) { MetaBarrierImpl *impl = barrier->priv->impl; if (impl) return META_BARRIER_IMPL_GET_CLASS (impl)->is_active (impl); else return FALSE; } /** * meta_barrier_release: * @barrier: The barrier to release * @event: The event to release the pointer for * * In XI2.3, pointer barriers provide a feature where they can * be temporarily released so that the pointer goes through * them. Pass a #MetaBarrierEvent to release the barrier for * this event sequence. */ void meta_barrier_release (MetaBarrier *barrier, MetaBarrierEvent *event) { MetaBarrierImpl *impl = barrier->priv->impl; if (impl) META_BARRIER_IMPL_GET_CLASS (impl)->release (impl, event); } static void meta_barrier_constructed (GObject *object) { MetaBarrier *barrier = META_BARRIER (object); MetaBarrierPrivate *priv = barrier->priv; g_return_if_fail (priv->border.line.a.x == priv->border.line.b.x || priv->border.line.a.y == priv->border.line.b.y); #if defined(HAVE_NATIVE_BACKEND) if (META_IS_BACKEND_NATIVE (meta_get_backend ())) priv->impl = meta_barrier_impl_native_new (barrier); #endif if (META_IS_BACKEND_X11 (meta_get_backend ()) && !meta_is_wayland_compositor ()) priv->impl = meta_barrier_impl_x11_new (barrier); if (priv->impl == NULL) g_warning ("Created a non-working barrier"); /* Take a ref that we'll release in destroy() so that the object stays * alive while active. */ g_object_ref (barrier); G_OBJECT_CLASS (meta_barrier_parent_class)->constructed (object); } static void meta_barrier_class_init (MetaBarrierClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->get_property = meta_barrier_get_property; object_class->set_property = meta_barrier_set_property; object_class->dispose = meta_barrier_dispose; object_class->constructed = meta_barrier_constructed; obj_props[PROP_DISPLAY] = g_param_spec_object ("display", "Display", "The display to construct the pointer barrier on", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_X1] = g_param_spec_int ("x1", "X1", "The first X coordinate of the barrier", 0, G_MAXSHORT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_Y1] = g_param_spec_int ("y1", "Y1", "The first Y coordinate of the barrier", 0, G_MAXSHORT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_X2] = g_param_spec_int ("x2", "X2", "The second X coordinate of the barrier", 0, G_MAXSHORT, G_MAXSHORT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_Y2] = g_param_spec_int ("y2", "Y2", "The second Y coordinate of the barrier", 0, G_MAXSHORT, G_MAXSHORT, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); obj_props[PROP_DIRECTIONS] = g_param_spec_flags ("directions", "Directions", "A set of directions to let the pointer through", META_TYPE_BARRIER_DIRECTION, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); /** * MetaBarrier::hit: * @barrier: The #MetaBarrier that was hit * @event: A #MetaBarrierEvent that has the details of how * the barrier was hit. * * When a pointer barrier is hit, this will trigger. This * requires an XI2-enabled server. */ obj_signals[HIT] = g_signal_new ("hit", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_BARRIER_EVENT); /** * MetaBarrier::left: * @barrier: The #MetaBarrier that was left * @event: A #MetaBarrierEvent that has the details of how * the barrier was left. * * When a pointer barrier hitbox was left, this will trigger. * This requires an XI2-enabled server. */ obj_signals[LEFT] = g_signal_new ("left", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_BARRIER_EVENT); } void meta_barrier_destroy (MetaBarrier *barrier) { MetaBarrierImpl *impl = barrier->priv->impl; if (impl) META_BARRIER_IMPL_GET_CLASS (impl)->destroy (impl); g_object_unref (barrier); } static void meta_barrier_init (MetaBarrier *barrier) { barrier->priv = meta_barrier_get_instance_private (barrier); } void _meta_barrier_emit_hit_signal (MetaBarrier *barrier, MetaBarrierEvent *event) { g_signal_emit (barrier, obj_signals[HIT], 0, event); } void _meta_barrier_emit_left_signal (MetaBarrier *barrier, MetaBarrierEvent *event) { g_signal_emit (barrier, obj_signals[LEFT], 0, event); } static void meta_barrier_impl_class_init (MetaBarrierImplClass *klass) { klass->is_active = NULL; klass->release = NULL; klass->destroy = NULL; } static void meta_barrier_impl_init (MetaBarrierImpl *impl) { } static MetaBarrierEvent * meta_barrier_event_ref (MetaBarrierEvent *event) { g_return_val_if_fail (event != NULL, NULL); g_return_val_if_fail (event->ref_count > 0, NULL); g_atomic_int_inc ((volatile int *)&event->ref_count); return event; } void meta_barrier_event_unref (MetaBarrierEvent *event) { g_return_if_fail (event != NULL); g_return_if_fail (event->ref_count > 0); if (g_atomic_int_dec_and_test ((volatile int *)&event->ref_count)) g_slice_free (MetaBarrierEvent, event); } G_DEFINE_BOXED_TYPE (MetaBarrierEvent, meta_barrier_event, meta_barrier_event_ref, meta_barrier_event_unref) muffin-6.4.1/src/backends/meta-gpu.c0000664000175000017500000001223214723361714016166 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-gpu.h" #include "backends/meta-backend-private.h" #include "backends/meta-output.h" enum { PROP_0, PROP_BACKEND, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; typedef struct _MetaGpuPrivate { MetaBackend *backend; GList *outputs; GList *crtcs; GList *modes; } MetaGpuPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaGpu, meta_gpu, G_TYPE_OBJECT) gboolean meta_gpu_has_hotplug_mode_update (MetaGpu *gpu) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); GList *l; for (l = priv->outputs; l; l = l->next) { MetaOutput *output = l->data; if (output->hotplug_mode_update) return TRUE; } return FALSE; } gboolean meta_gpu_read_current (MetaGpu *gpu, GError **error) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); gboolean ret; GList *old_outputs; GList *old_crtcs; GList *old_modes; /* TODO: Get rid of this when objects incref:s what they need instead */ old_outputs = priv->outputs; old_crtcs = priv->crtcs; old_modes = priv->modes; ret = META_GPU_GET_CLASS (gpu)->read_current (gpu, error); g_list_free_full (old_outputs, g_object_unref); g_list_free_full (old_modes, g_object_unref); g_list_free_full (old_crtcs, g_object_unref); return ret; } MetaBackend * meta_gpu_get_backend (MetaGpu *gpu) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); return priv->backend; } GList * meta_gpu_get_outputs (MetaGpu *gpu) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); return priv->outputs; } GList * meta_gpu_get_crtcs (MetaGpu *gpu) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); return priv->crtcs; } GList * meta_gpu_get_modes (MetaGpu *gpu) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); return priv->modes; } void meta_gpu_take_outputs (MetaGpu *gpu, GList *outputs) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); priv->outputs = outputs; } void meta_gpu_take_crtcs (MetaGpu *gpu, GList *crtcs) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); priv->crtcs = crtcs; } void meta_gpu_take_modes (MetaGpu *gpu, GList *modes) { MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); priv->modes = modes; } static void meta_gpu_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaGpu *gpu = META_GPU (object); MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); switch (prop_id) { case PROP_BACKEND: priv->backend = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_gpu_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaGpu *gpu = META_GPU (object); MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); switch (prop_id) { case PROP_BACKEND: g_value_set_object (value, priv->backend); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_gpu_finalize (GObject *object) { MetaGpu *gpu = META_GPU (object); MetaGpuPrivate *priv = meta_gpu_get_instance_private (gpu); g_list_free_full (priv->outputs, g_object_unref); g_list_free_full (priv->modes, g_object_unref); g_list_free_full (priv->crtcs, g_object_unref); G_OBJECT_CLASS (meta_gpu_parent_class)->finalize (object); } static void meta_gpu_init (MetaGpu *gpu) { } static void meta_gpu_class_init (MetaGpuClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_gpu_set_property; object_class->get_property = meta_gpu_get_property; object_class->finalize = meta_gpu_finalize; obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "backend", "MetaBackend", META_TYPE_BACKEND, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/src/backends/meta-cursor-sprite-xcursor.c0000664000175000017500000002264714723361714021732 0ustar fabiofabio/* * Copyright 2013, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-cursor.h" #include "backends/meta-cursor-renderer.h" #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/prefs.h" #include "meta/util.h" struct _MetaCursorSpriteXcursor { MetaCursorSprite parent; MetaCursor cursor; int current_frame; XcursorImages *xcursor_images; int theme_scale; gboolean theme_dirty; }; G_DEFINE_TYPE (MetaCursorSpriteXcursor, meta_cursor_sprite_xcursor, META_TYPE_CURSOR_SPRITE) static const char * translate_meta_cursor (MetaCursor cursor) { switch (cursor) { case META_CURSOR_DEFAULT: return "left_ptr"; case META_CURSOR_NORTH_RESIZE: return "top_side"; case META_CURSOR_SOUTH_RESIZE: return "bottom_side"; case META_CURSOR_WEST_RESIZE: return "left_side"; case META_CURSOR_EAST_RESIZE: return "right_side"; case META_CURSOR_SE_RESIZE: return "bottom_right_corner"; case META_CURSOR_SW_RESIZE: return "bottom_left_corner"; case META_CURSOR_NE_RESIZE: return "top_right_corner"; case META_CURSOR_NW_RESIZE: return "top_left_corner"; case META_CURSOR_MOVE_OR_RESIZE_WINDOW: return "fleur"; case META_CURSOR_BUSY: return "watch"; case META_CURSOR_DND_IN_DRAG: return "dnd-none"; case META_CURSOR_DND_MOVE: return "dnd-move"; case META_CURSOR_DND_COPY: return "dnd-copy"; case META_CURSOR_DND_UNSUPPORTED_TARGET: return "dnd-none"; case META_CURSOR_POINTING_HAND: return "hand2"; case META_CURSOR_CROSSHAIR: return "crosshair"; case META_CURSOR_IBEAM: return "xterm"; case META_CURSOR_NONE: case META_CURSOR_LAST: break; } g_assert_not_reached (); return NULL; } MetaCursor meta_cursor_sprite_xcursor_get_cursor (MetaCursorSpriteXcursor *sprite_xcursor) { return sprite_xcursor->cursor; } Cursor meta_create_x_cursor (Display *xdisplay, MetaCursor cursor) { return XcursorLibraryLoadCursor (xdisplay, translate_meta_cursor (cursor)); } static XcursorImages * load_cursor_on_client (MetaCursor cursor, int scale) { XcursorImages *xcursor_images; int fallback_size; xcursor_images = XcursorLibraryLoadImages (translate_meta_cursor (cursor), meta_prefs_get_cursor_theme (), meta_prefs_get_cursor_size () * scale); if (xcursor_images) return xcursor_images; g_warning ("No cursor theme available, please install a cursor theme"); fallback_size = 24 * scale; xcursor_images = XcursorImagesCreate (1); xcursor_images->images[0] = XcursorImageCreate (fallback_size, fallback_size); xcursor_images->images[0]->xhot = 0; xcursor_images->images[0]->yhot = 0; memset (xcursor_images->images[0]->pixels, 0xc0, fallback_size * fallback_size * sizeof (int32_t)); return xcursor_images; } static void load_from_current_xcursor_image (MetaCursorSpriteXcursor *sprite_xcursor) { MetaCursorSprite *sprite = META_CURSOR_SPRITE (sprite_xcursor); XcursorImage *xc_image; int width, height, rowstride; CoglPixelFormat cogl_format; ClutterBackend *clutter_backend; CoglContext *cogl_context; CoglTexture2D *texture; GError *error = NULL; int hotspot_x, hotspot_y; g_assert (!meta_cursor_sprite_get_cogl_texture (sprite)); xc_image = meta_cursor_sprite_xcursor_get_current_image (sprite_xcursor); width = (int) xc_image->width; height = (int) xc_image->height; rowstride = width * 4; #if G_BYTE_ORDER == G_LITTLE_ENDIAN cogl_format = COGL_PIXEL_FORMAT_BGRA_8888; #else cogl_format = COGL_PIXEL_FORMAT_ARGB_8888; #endif clutter_backend = clutter_get_default_backend (); cogl_context = clutter_backend_get_cogl_context (clutter_backend); texture = cogl_texture_2d_new_from_data (cogl_context, width, height, cogl_format, rowstride, (uint8_t *) xc_image->pixels, &error); if (!texture) { g_warning ("Failed to allocate cursor texture: %s\n", error->message); g_error_free (error); } if (meta_is_wayland_compositor ()) { hotspot_x = ((int) (xc_image->xhot / sprite_xcursor->theme_scale) * sprite_xcursor->theme_scale); hotspot_y = ((int) (xc_image->yhot / sprite_xcursor->theme_scale) * sprite_xcursor->theme_scale); } else { hotspot_x = xc_image->xhot; hotspot_y = xc_image->yhot; } meta_cursor_sprite_set_texture (sprite, COGL_TEXTURE (texture), hotspot_x, hotspot_y); g_clear_pointer (&texture, cogl_object_unref); } void meta_cursor_sprite_xcursor_set_theme_scale (MetaCursorSpriteXcursor *sprite_xcursor, int theme_scale) { if (sprite_xcursor->theme_scale != theme_scale) sprite_xcursor->theme_dirty = TRUE; sprite_xcursor->theme_scale = theme_scale; } static gboolean meta_cursor_sprite_xcursor_is_animated (MetaCursorSprite *sprite) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite); return (sprite_xcursor->xcursor_images && sprite_xcursor->xcursor_images->nimage > 1); } XcursorImage * meta_cursor_sprite_xcursor_get_current_image (MetaCursorSpriteXcursor *sprite_xcursor) { return sprite_xcursor->xcursor_images->images[sprite_xcursor->current_frame]; } static void meta_cursor_sprite_xcursor_tick_frame (MetaCursorSprite *sprite) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite); if (!meta_cursor_sprite_is_animated (sprite)) return; sprite_xcursor->current_frame++; if (sprite_xcursor->current_frame >= sprite_xcursor->xcursor_images->nimage) sprite_xcursor->current_frame = 0; meta_cursor_sprite_clear_texture (sprite); load_from_current_xcursor_image (sprite_xcursor); } static unsigned int meta_cursor_sprite_xcursor_get_current_frame_time (MetaCursorSprite *sprite) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite); XcursorImages *xcursor_images; g_return_val_if_fail (meta_cursor_sprite_is_animated (sprite), 0); xcursor_images = sprite_xcursor->xcursor_images; return xcursor_images->images[sprite_xcursor->current_frame]->delay; } static void load_cursor_from_theme (MetaCursorSprite *sprite) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite); g_assert (sprite_xcursor->cursor != META_CURSOR_NONE); sprite_xcursor->theme_dirty = FALSE; /* We might be reloading with a different scale. If so clear the old data. */ if (sprite_xcursor->xcursor_images) { meta_cursor_sprite_clear_texture (sprite); XcursorImagesDestroy (sprite_xcursor->xcursor_images); } sprite_xcursor->current_frame = 0; sprite_xcursor->xcursor_images = load_cursor_on_client (sprite_xcursor->cursor, sprite_xcursor->theme_scale); load_from_current_xcursor_image (sprite_xcursor); } static void meta_cursor_sprite_xcursor_realize_texture (MetaCursorSprite *sprite) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (sprite); if (sprite_xcursor->theme_dirty) load_cursor_from_theme (sprite); } MetaCursorSpriteXcursor * meta_cursor_sprite_xcursor_new (MetaCursor cursor) { MetaCursorSpriteXcursor *sprite_xcursor; sprite_xcursor = g_object_new (META_TYPE_CURSOR_SPRITE_XCURSOR, NULL); sprite_xcursor->cursor = cursor; return sprite_xcursor; } static void meta_cursor_sprite_xcursor_finalize (GObject *object) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (object); g_clear_pointer (&sprite_xcursor->xcursor_images, XcursorImagesDestroy); G_OBJECT_CLASS (meta_cursor_sprite_xcursor_parent_class)->finalize (object); } static void meta_cursor_sprite_xcursor_init (MetaCursorSpriteXcursor *sprite_xcursor) { sprite_xcursor->theme_scale = 1; sprite_xcursor->theme_dirty = TRUE; } static void meta_cursor_sprite_xcursor_class_init (MetaCursorSpriteXcursorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass); object_class->finalize = meta_cursor_sprite_xcursor_finalize; cursor_sprite_class->realize_texture = meta_cursor_sprite_xcursor_realize_texture; cursor_sprite_class->is_animated = meta_cursor_sprite_xcursor_is_animated; cursor_sprite_class->tick_frame = meta_cursor_sprite_xcursor_tick_frame; cursor_sprite_class->get_current_frame_time = meta_cursor_sprite_xcursor_get_current_frame_time; } muffin-6.4.1/src/backends/meta-cursor.c0000664000175000017500000001471014723361714016713 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Giovanni Campagna */ #include "config.h" #include "backends/meta-cursor.h" #include "backends/meta-backend-private.h" #include "cogl/cogl.h" #include "meta/common.h" enum { PREPARE_AT, TEXTURE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; typedef struct _MetaCursorSpritePrivate { GObject parent; CoglTexture2D *texture; float texture_scale; MetaMonitorTransform texture_transform; int hot_x, hot_y; } MetaCursorSpritePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaCursorSprite, meta_cursor_sprite, G_TYPE_OBJECT) gboolean meta_cursor_sprite_is_animated (MetaCursorSprite *sprite) { MetaCursorSpriteClass *klass = META_CURSOR_SPRITE_GET_CLASS (sprite); if (klass->is_animated) return klass->is_animated (sprite); else return FALSE; } void meta_cursor_sprite_tick_frame (MetaCursorSprite *sprite) { return META_CURSOR_SPRITE_GET_CLASS (sprite)->tick_frame (sprite); } unsigned int meta_cursor_sprite_get_current_frame_time (MetaCursorSprite *sprite) { return META_CURSOR_SPRITE_GET_CLASS (sprite)->get_current_frame_time (sprite); } void meta_cursor_sprite_clear_texture (MetaCursorSprite *sprite) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); g_clear_pointer (&priv->texture, cogl_object_unref); } void meta_cursor_sprite_set_texture (MetaCursorSprite *sprite, CoglTexture *texture, int hot_x, int hot_y) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); g_clear_pointer (&priv->texture, cogl_object_unref); if (texture) priv->texture = cogl_object_ref (texture); priv->hot_x = hot_x; priv->hot_y = hot_y; g_signal_emit (sprite, signals[TEXTURE_CHANGED], 0); } void meta_cursor_sprite_set_texture_scale (MetaCursorSprite *sprite, float scale) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); priv->texture_scale = scale; } void meta_cursor_sprite_set_texture_transform (MetaCursorSprite *sprite, MetaMonitorTransform transform) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); priv->texture_transform = transform; } CoglTexture * meta_cursor_sprite_get_cogl_texture (MetaCursorSprite *sprite) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); return COGL_TEXTURE (priv->texture); } void meta_cursor_sprite_get_hotspot (MetaCursorSprite *sprite, int *hot_x, int *hot_y) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); *hot_x = priv->hot_x; *hot_y = priv->hot_y; } int meta_cursor_sprite_get_width (MetaCursorSprite *sprite) { CoglTexture *texture; texture = meta_cursor_sprite_get_cogl_texture (sprite); return cogl_texture_get_width (texture); } int meta_cursor_sprite_get_height (MetaCursorSprite *sprite) { CoglTexture *texture; texture = meta_cursor_sprite_get_cogl_texture (sprite); return cogl_texture_get_height (texture); } float meta_cursor_sprite_get_texture_scale (MetaCursorSprite *sprite) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); return priv->texture_scale; } MetaMonitorTransform meta_cursor_sprite_get_texture_transform (MetaCursorSprite *sprite) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); return priv->texture_transform; } void meta_cursor_sprite_prepare_at (MetaCursorSprite *sprite, int x, int y) { g_signal_emit (sprite, signals[PREPARE_AT], 0, x, y); } void meta_cursor_sprite_realize_texture (MetaCursorSprite *sprite) { MetaCursorSpriteClass *klass = META_CURSOR_SPRITE_GET_CLASS (sprite); if (klass->realize_texture) klass->realize_texture (sprite); } static void meta_cursor_sprite_init (MetaCursorSprite *sprite) { MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); priv->texture_scale = 1.0f; priv->texture_transform = META_MONITOR_TRANSFORM_NORMAL; } static void meta_cursor_sprite_finalize (GObject *object) { MetaCursorSprite *sprite = META_CURSOR_SPRITE (object); MetaCursorSpritePrivate *priv = meta_cursor_sprite_get_instance_private (sprite); g_clear_pointer (&priv->texture, cogl_object_unref); G_OBJECT_CLASS (meta_cursor_sprite_parent_class)->finalize (object); } static void meta_cursor_sprite_class_init (MetaCursorSpriteClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_cursor_sprite_finalize; signals[PREPARE_AT] = g_signal_new ("prepare-at", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); signals[TEXTURE_CHANGED] = g_signal_new ("texture-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/backends/meta-remote-access-controller.c0000664000175000017500000001006414723361714022307 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-remote-access-controller-private.h" enum { HANDLE_STOPPED, N_HANDLE_SIGNALS }; static int handle_signals[N_HANDLE_SIGNALS]; enum { CONTROLLER_NEW_HANDLE, N_CONTROLLER_SIGNALS }; static int controller_signals[N_CONTROLLER_SIGNALS]; typedef struct _MetaRemoteAccessHandlePrivate { gboolean has_stopped; gboolean disable_animations; } MetaRemoteAccessHandlePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaRemoteAccessHandle, meta_remote_access_handle, G_TYPE_OBJECT) struct _MetaRemoteAccessController { GObject parent; }; G_DEFINE_TYPE (MetaRemoteAccessController, meta_remote_access_controller, G_TYPE_OBJECT) /** * meta_remote_access_handle_stop: * @handle: A #MetaRemoteAccessHandle * * Stop the associated remote access session. */ void meta_remote_access_handle_stop (MetaRemoteAccessHandle *handle) { MetaRemoteAccessHandlePrivate *priv = meta_remote_access_handle_get_instance_private (handle); if (priv->has_stopped) return; META_REMOTE_ACCESS_HANDLE_GET_CLASS (handle)->stop (handle); } /** * meta_remote_access_get_disable_animations: * @handle: A #MetaRemoteAccessHandle * * Returns: %TRUE if the remote access requested that animations should be * disabled. */ gboolean meta_remote_access_handle_get_disable_animations (MetaRemoteAccessHandle *handle) { MetaRemoteAccessHandlePrivate *priv = meta_remote_access_handle_get_instance_private (handle); return priv->disable_animations; } void meta_remote_access_handle_set_disable_animations (MetaRemoteAccessHandle *handle, gboolean disable_animations) { MetaRemoteAccessHandlePrivate *priv = meta_remote_access_handle_get_instance_private (handle); priv->disable_animations = disable_animations; } void meta_remote_access_handle_notify_stopped (MetaRemoteAccessHandle *handle) { MetaRemoteAccessHandlePrivate *priv = meta_remote_access_handle_get_instance_private (handle); priv->has_stopped = TRUE; g_signal_emit (handle, handle_signals[HANDLE_STOPPED], 0); } void meta_remote_access_controller_notify_new_handle (MetaRemoteAccessController *controller, MetaRemoteAccessHandle *handle) { g_signal_emit (controller, controller_signals[CONTROLLER_NEW_HANDLE], 0, handle); } static void meta_remote_access_handle_init (MetaRemoteAccessHandle *handle) { } static void meta_remote_access_handle_class_init (MetaRemoteAccessHandleClass *klass) { handle_signals[HANDLE_STOPPED] = g_signal_new ("stopped", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_remote_access_controller_init (MetaRemoteAccessController *controller) { } static void meta_remote_access_controller_class_init (MetaRemoteAccessControllerClass *klass) { controller_signals[CONTROLLER_NEW_HANDLE] = g_signal_new ("new-handle", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_REMOTE_ACCESS_HANDLE); } muffin-6.4.1/src/backends/meta-monitor-manager.c0000664000175000017500000036126714723361714020511 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-monitor-manager * @title: MetaMonitorManager * @short_description: A manager for multiple monitors * * #MetaMonitorManager is an abstract class which contains methods to handle * multiple monitors (both #MetaMonitor and #MetaLogicalMonitor) and GPU's * (#MetaGpu). Its functions include reading and/or changing the current * configuration and available capabiliies. * * The #MetaMonitorManager also provides the "org.cinnamon.Muffin.DisplayConfig" * DBus service, so apps like GNOME Settings can use this functionality. */ #include "config.h" #include "backends/meta-monitor-manager-private.h" #include #include #include #include "backends/edid.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-orientation-manager.h" #include "backends/meta-output.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include "clutter/clutter.h" #include "core/main-private.h" #include "core/util-private.h" #include "meta/main.h" #include "meta/meta-x11-errors.h" #define DEFAULT_DISPLAY_CONFIGURATION_TIMEOUT 20 // Fractional scales in cinnamon-monitors.xml are only stored to 6 digits, // So migrating from 1.503759 to 1.5037590265274048 will fail because FLT_EPSILON // is 0.00000011920928955078 which is less than the difference of those two scales. #define CINNAMON_SCALE_EPSILON 0.000001f enum { PROP_0, PROP_BACKEND, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; enum { MONITORS_CHANGED, MONITORS_CHANGED_INTERNAL, POWER_SAVE_MODE_CHANGED, CONFIRM_DISPLAY_CHANGE, SIGNALS_LAST }; /* Array index matches MetaMonitorTransform */ static gfloat transform_matrices[][6] = { { 1, 0, 0, 0, 1, 0 }, /* normal */ { 0, -1, 1, 1, 0, 0 }, /* 90° */ { -1, 0, 1, 0, -1, 1 }, /* 180° */ { 0, 1, 0, -1, 0, 1 }, /* 270° */ { -1, 0, 1, 0, 1, 0 }, /* normal flipped */ { 0, 1, 0, 1, 0, 0 }, /* 90° flipped */ { 1, 0, 0, 0, -1, 1 }, /* 180° flipped */ { 0, -1, 1, -1, 0, 1 }, /* 270° flipped */ }; static int signals[SIGNALS_LAST]; typedef struct _MetaMonitorManagerPrivate { MetaPowerSave power_save_mode; } MetaMonitorManagerPrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaMonitorManager, meta_monitor_manager, G_TYPE_OBJECT) static void initialize_dbus_interface (MetaMonitorManager *manager); static void monitor_manager_setup_dbus_config_handlers (MetaMonitorManager *manager); static gboolean meta_monitor_manager_is_config_complete (MetaMonitorManager *manager, MetaMonitorsConfig *config); static gboolean is_global_scale_matching_in_config (MetaMonitorsConfig *config, float scale); static gboolean meta_monitor_manager_is_scale_supported_with_threshold (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float scale, float threshold, float *out_scale); static void meta_monitor_manager_real_read_current_state (MetaMonitorManager *manager); MetaBackend * meta_monitor_manager_get_backend (MetaMonitorManager *manager) { return manager->backend; } static void meta_monitor_manager_init (MetaMonitorManager *manager) { } static void meta_monitor_manager_set_primary_logical_monitor (MetaMonitorManager *manager, MetaLogicalMonitor *logical_monitor) { manager->primary_logical_monitor = logical_monitor; if (logical_monitor) meta_logical_monitor_make_primary (logical_monitor); } static gboolean is_main_tiled_monitor_output (MetaOutput *output) { return output->tile_info.loc_h_tile == 0 && output->tile_info.loc_v_tile == 0; } static MetaLogicalMonitor * logical_monitor_from_layout (MetaMonitorManager *manager, GList *logical_monitors, MetaRectangle *layout) { GList *l; for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (meta_rectangle_equal (layout, &logical_monitor->rect)) return logical_monitor; } return NULL; } static void meta_monitor_manager_rebuild_logical_monitors (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *logical_monitor_configs; GList *logical_monitors = NULL; GList *l; int monitor_number = 0; MetaLogicalMonitor *primary_logical_monitor = NULL; logical_monitor_configs = config ? config->logical_monitor_configs : NULL; for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; MetaLogicalMonitor *logical_monitor; logical_monitor = meta_logical_monitor_new (manager, logical_monitor_config, monitor_number); monitor_number++; if (logical_monitor_config->is_primary) primary_logical_monitor = logical_monitor; logical_monitors = g_list_append (logical_monitors, logical_monitor); } /* * If no monitor was marked as primary, fall back on marking the first * logical monitor the primary one. */ if (!primary_logical_monitor && logical_monitors) primary_logical_monitor = g_list_first (logical_monitors)->data; manager->logical_monitors = logical_monitors; meta_monitor_manager_set_primary_logical_monitor (manager, primary_logical_monitor); } float meta_monitor_manager_get_maximum_crtc_scale (MetaMonitorManager *manager) { GList *l; float scale; scale = 1.0f; for (l = manager->monitors; l != NULL; l = l->next) { MetaMonitor *monitor = l->data; MetaOutput *output = meta_monitor_get_main_output (monitor); MetaCrtc *crtc = meta_output_get_assigned_crtc (output); if (crtc) scale = MAX (scale, crtc->scale); } return scale; } static float derive_configured_global_scale (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *monitor_config = l->data; if (is_global_scale_matching_in_config (config, monitor_config->scale)) return monitor_config->scale; } return 1.0f; } static float calculate_monitor_scale (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorMode *monitor_mode; monitor_mode = meta_monitor_get_current_mode (monitor); return meta_monitor_manager_calculate_monitor_mode_scale (manager, manager->layout_mode, monitor, monitor_mode); } static gboolean meta_monitor_manager_is_scale_supported_by_other_monitors (MetaMonitorManager *manager, MetaMonitor *not_this_one, float scale) { GList *l; for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorMode *mode; if (monitor == not_this_one || !meta_monitor_is_active (monitor)) continue; mode = meta_monitor_get_current_mode (monitor); if (!meta_monitor_manager_is_scale_supported (manager, manager->layout_mode, monitor, mode, scale)) return FALSE; } return TRUE; } static float derive_calculated_global_scale (MetaMonitorManager *manager) { MetaMonitor *monitor = NULL; float scale; GList *l; scale = 1.0f; monitor = meta_monitor_manager_get_primary_monitor (manager); if (monitor && meta_monitor_is_active (monitor)) { scale = calculate_monitor_scale (manager, monitor); if (meta_monitor_manager_is_scale_supported_by_other_monitors (manager, monitor, scale)) return scale; } for (l = manager->monitors; l; l = l->next) { MetaMonitor *other_monitor = l->data; float monitor_scale; if (other_monitor == monitor || !meta_monitor_is_active (other_monitor)) continue; monitor_scale = calculate_monitor_scale (manager, other_monitor); if (meta_monitor_manager_is_scale_supported_by_other_monitors (manager, other_monitor, monitor_scale)) scale = MAX (scale, monitor_scale); } return scale; } static float derive_scale_from_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaRectangle *layout) { GList *l; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (meta_rectangle_equal (layout, &logical_monitor_config->layout)) return logical_monitor_config->scale; } g_warning ("Missing logical monitor, using scale 1"); return 1.0; } static gboolean derive_scale_from_crtc (MetaMonitorManager *manager, MetaMonitor *monitor, float *scale) { MetaMonitorManagerCapability capabilities; MetaMonitorMode *monitor_mode; float threshold; MetaOutput *output; MetaCrtc *crtc; capabilities = meta_monitor_manager_get_capabilities (manager); if (!(capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) return FALSE; if (!(capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) return FALSE; output = meta_monitor_get_main_output (monitor); crtc = meta_output_get_assigned_crtc (output); if (!crtc) return FALSE; /* Due to integer and possibly inverse scaling applied to the output the * result could not match exactly, so we apply a more relaxed threshold * in this case. */ threshold = 0.001f; monitor_mode = meta_monitor_get_current_mode (monitor); if (meta_monitor_manager_is_scale_supported_with_threshold (manager, manager->layout_mode, monitor, monitor_mode, crtc->scale, threshold, scale)) return TRUE; return FALSE; } static void meta_monitor_manager_rebuild_logical_monitors_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *logical_monitors = NULL; GList *l; int monitor_number; MetaLogicalMonitor *primary_logical_monitor = NULL; gboolean use_global_scale; float global_scale = 0.0; MetaMonitorManagerCapability capabilities; monitor_number = 0; capabilities = meta_monitor_manager_get_capabilities (manager); use_global_scale = !!(capabilities & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED); if (use_global_scale) { if (config) global_scale = derive_configured_global_scale (manager, config); else global_scale = derive_calculated_global_scale (manager); } for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaLogicalMonitor *logical_monitor; MetaRectangle layout; if (!meta_monitor_is_active (monitor)) continue; meta_monitor_derive_layout (monitor, &layout); logical_monitor = logical_monitor_from_layout (manager, logical_monitors, &layout); if (logical_monitor) { meta_logical_monitor_add_monitor (logical_monitor, monitor); } else { float scale; if (use_global_scale) scale = roundf (global_scale); else { if (!derive_scale_from_crtc (manager, monitor, &scale)) { if (config) scale = derive_scale_from_config (manager, config, &layout); else scale = calculate_monitor_scale (manager, monitor); } } g_assert (scale > 0); logical_monitor = meta_logical_monitor_new_derived (manager, monitor, &layout, scale, monitor_number); logical_monitors = g_list_append (logical_monitors, logical_monitor); monitor_number++; } if (meta_monitor_is_primary (monitor)) primary_logical_monitor = logical_monitor; } manager->logical_monitors = logical_monitors; /* * If no monitor was marked as primary, fall back on marking the first * logical monitor the primary one. */ if (!primary_logical_monitor && manager->logical_monitors) primary_logical_monitor = g_list_first (manager->logical_monitors)->data; meta_monitor_manager_set_primary_logical_monitor (manager, primary_logical_monitor); } void meta_monitor_manager_power_save_mode_changed (MetaMonitorManager *manager, MetaPowerSave mode) { MetaMonitorManagerPrivate *priv = meta_monitor_manager_get_instance_private (manager); if (priv->power_save_mode == mode) return; priv->power_save_mode = mode; g_signal_emit (manager, signals[POWER_SAVE_MODE_CHANGED], 0); } static void power_save_mode_changed (MetaMonitorManager *manager, GParamSpec *pspec, gpointer user_data) { MetaMonitorManagerPrivate *priv = meta_monitor_manager_get_instance_private (manager); MetaMonitorManagerClass *klass; int mode = meta_dbus_display_config_get_power_save_mode (manager->display_config); if (mode == META_POWER_SAVE_UNSUPPORTED) return; /* If DPMS is unsupported, force the property back. */ if (priv->power_save_mode == META_POWER_SAVE_UNSUPPORTED) { meta_dbus_display_config_set_power_save_mode (manager->display_config, META_POWER_SAVE_UNSUPPORTED); return; } klass = META_MONITOR_MANAGER_GET_CLASS (manager); if (klass->set_power_save_mode) klass->set_power_save_mode (manager, mode); meta_monitor_manager_power_save_mode_changed (manager, mode); } void meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager) { meta_monitor_manager_ensure_configured (manager); } static void lid_is_closed_changed (MetaBackend *backend, gboolean lid_is_closed, gpointer user_data) { MetaMonitorManager *manager = user_data; meta_monitor_manager_lid_is_closed_changed (manager); } /** * meta_monitor_manager_is_headless: * @manager: A #MetaMonitorManager object * * Returns whether the monitor manager is headless, i.e. without * any #MetaLogicalMonitors attached to it. * * Returns: %TRUE if no monitors are attached, %FALSE otherwise. */ gboolean meta_monitor_manager_is_headless (MetaMonitorManager *manager) { return !manager->logical_monitors; } float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { float scale; MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); scale = manager_class->calculate_monitor_mode_scale (manager, layout_mode, monitor, monitor_mode); return scale; } float * meta_monitor_manager_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->calculate_supported_scales (manager, layout_mode, monitor, monitor_mode, n_supported_scales); } /** * meta_monitor_manager_get_capabilities: * @manager: A #MetaMonitorManager object * * Queries the capabilities of the monitor manager. * * Returns: #MetaMonitorManagerCapability flags representing the capabilities. */ MetaMonitorManagerCapability meta_monitor_manager_get_capabilities (MetaMonitorManager *manager) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->get_capabilities (manager); } gboolean meta_monitor_manager_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->get_max_screen_size (manager, max_width, max_height); } MetaLogicalMonitorLayoutMode meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->get_default_layout_mode (manager); } static void meta_monitor_manager_ensure_initial_config (MetaMonitorManager *manager) { META_MONITOR_MANAGER_GET_CLASS (manager)->ensure_initial_config (manager); } static gboolean meta_monitor_manager_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); g_assert (!config || !(config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED)); if (!manager_class->apply_monitors_config (manager, config, method, error)) return FALSE; switch (method) { case META_MONITORS_CONFIG_METHOD_TEMPORARY: case META_MONITORS_CONFIG_METHOD_PERSISTENT: meta_monitor_config_manager_set_current (manager->config_manager, config); break; case META_MONITORS_CONFIG_METHOD_VERIFY: break; } return TRUE; } gboolean meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager) { GList *gpus; GList *l; gpus = meta_backend_get_gpus (manager->backend); for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; if (meta_gpu_has_hotplug_mode_update (gpu)) return TRUE; } return FALSE; } static gboolean should_use_stored_config (MetaMonitorManager *manager) { return (manager->in_init || !meta_monitor_manager_has_hotplug_mode_update (manager)); } MetaMonitorsConfig * meta_monitor_manager_ensure_configured (MetaMonitorManager *manager) { MetaMonitorsConfig *config = NULL; GError *error = NULL; gboolean use_stored_config; MetaMonitorsConfigMethod method; MetaMonitorsConfigMethod fallback_method = META_MONITORS_CONFIG_METHOD_TEMPORARY; MetaLogicalMonitorLayoutMode layout_mode = meta_monitor_manager_get_default_layout_mode (manager); use_stored_config = should_use_stored_config (manager); if (use_stored_config) method = META_MONITORS_CONFIG_METHOD_PERSISTENT; else method = META_MONITORS_CONFIG_METHOD_TEMPORARY; if (use_stored_config) { g_autoptr(MetaMonitorsConfig) new_config = NULL; config = meta_monitor_config_manager_get_stored (manager->config_manager); if (config && config->layout_mode != layout_mode) { new_config = meta_monitor_config_manager_create_for_layout (manager->config_manager, config, layout_mode); config = new_config; } if (config) { if (!meta_monitor_manager_apply_monitors_config (manager, config, method, &error)) { config = NULL; g_warning ("Failed to use stored monitor configuration: %s", error->message); g_clear_error (&error); } else { g_object_ref (config); goto done; } } } config = meta_monitor_config_manager_create_suggested (manager->config_manager); if (config) { if (!meta_monitor_manager_apply_monitors_config (manager, config, method, &error)) { g_clear_object (&config); g_warning ("Failed to use suggested monitor configuration: %s", error->message); g_clear_error (&error); } else { goto done; } } config = meta_monitor_config_manager_get_previous (manager->config_manager); if (config) { config = g_object_ref (config); if (config && config->layout_mode != layout_mode) { MetaMonitorsConfig *new_config = meta_monitor_config_manager_create_for_layout (manager->config_manager, config, layout_mode); g_object_unref (config); config = new_config; } if (meta_monitor_manager_is_config_complete (manager, config)) { if (!meta_monitor_manager_apply_monitors_config (manager, config, method, &error)) { g_warning ("Failed to use suggested monitor configuration: %s", error->message); g_clear_error (&error); } else { goto done; } } g_clear_object (&config); } config = meta_monitor_config_manager_create_linear (manager->config_manager); if (config) { if (!meta_monitor_manager_apply_monitors_config (manager, config, method, &error)) { g_clear_object (&config); g_warning ("Failed to use linear monitor configuration: %s", error->message); g_clear_error (&error); } else { goto done; } } config = meta_monitor_config_manager_create_fallback (manager->config_manager); if (config) { if (!meta_monitor_manager_apply_monitors_config (manager, config, fallback_method, &error)) { g_clear_object (&config); g_warning ("Failed to use fallback monitor configuration: %s", error->message); g_clear_error (&error); } else { goto done; } } done: if (!config) { meta_monitor_manager_apply_monitors_config (manager, NULL, fallback_method, &error); return NULL; } g_object_unref (config); return config; } static void orientation_changed (MetaOrientationManager *orientation_manager, MetaMonitorManager *manager) { MetaMonitorTransform transform; GError *error = NULL; MetaMonitorsConfig *config; switch (meta_orientation_manager_get_orientation (orientation_manager)) { case META_ORIENTATION_NORMAL: transform = META_MONITOR_TRANSFORM_NORMAL; break; case META_ORIENTATION_BOTTOM_UP: transform = META_MONITOR_TRANSFORM_180; break; case META_ORIENTATION_LEFT_UP: transform = META_MONITOR_TRANSFORM_90; break; case META_ORIENTATION_RIGHT_UP: transform = META_MONITOR_TRANSFORM_270; break; case META_ORIENTATION_UNDEFINED: default: return; } config = meta_monitor_config_manager_create_for_orientation (manager->config_manager, transform); if (!config) return; if (!meta_monitor_manager_apply_monitors_config (manager, config, META_MONITORS_CONFIG_METHOD_TEMPORARY, &error)) { g_warning ("Failed to use orientation monitor configuration: %s", error->message); g_error_free (error); } g_object_unref (config); } static gboolean apply_x11_fractional_scaling_config (MetaMonitorManager *manager) { g_autoptr(GError) error = NULL; g_autoptr(MetaMonitorsConfig) config = NULL; MetaMonitorsConfig *applied_config; MetaLogicalMonitorLayoutMode layout_mode = meta_monitor_manager_get_default_layout_mode (manager); if (!META_IS_MONITOR_MANAGER_XRANDR (manager)) return TRUE; applied_config = meta_monitor_config_manager_get_current (manager->config_manager); config = meta_monitor_config_manager_create_for_layout (manager->config_manager, applied_config, layout_mode); if (!config) return FALSE; if (meta_monitor_manager_apply_monitors_config (manager, config, META_MONITORS_CONFIG_METHOD_PERSISTENT, &error)) { if (config != applied_config && manager->persistent_timeout_id) { if (G_UNLIKELY (applied_config != meta_monitor_config_manager_get_previous (manager->config_manager))) { g_warning ("The removed configuration doesn't match the " "previously applied one, reverting may not work"); } else { g_autoptr(MetaMonitorsConfig) previous_config = NULL; /* The previous config we applied was just a temporary one that * GNOME control center passed us while toggling the fractional * scaling. So, in such case, once the configuration with the * correct layout has been applied, we need to ignore the * temporary one. */ previous_config = meta_monitor_config_manager_pop_previous (manager->config_manager); g_assert_true (applied_config == previous_config); } } } else { g_warning ("Impossible to apply the layout config %s\n", error->message); return FALSE; } return TRUE; } static void experimental_features_changed (MetaSettings *settings, MetaExperimentalFeature old_experimental_features, MetaMonitorManager *manager) { gboolean was_stage_views_scaled; gboolean is_stage_views_scaled; gboolean was_x11_scaling; gboolean x11_scaling; gboolean should_reconfigure = FALSE; was_stage_views_scaled = !!(old_experimental_features & META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); is_stage_views_scaled = meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); was_x11_scaling = !!(old_experimental_features & META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING); x11_scaling = meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING); if (is_stage_views_scaled != was_stage_views_scaled) should_reconfigure = TRUE; if (was_x11_scaling != x11_scaling) { if (!apply_x11_fractional_scaling_config (manager)) should_reconfigure = TRUE; } if (should_reconfigure) meta_monitor_manager_on_hotplug (manager); meta_settings_update_ui_scaling_factor (settings); } void meta_monitor_manager_setup (MetaMonitorManager *manager) { manager->in_init = TRUE; manager->config_manager = meta_monitor_config_manager_new (manager); meta_monitor_manager_read_current_state (manager); meta_monitor_manager_ensure_initial_config (manager); manager->in_init = FALSE; } static void meta_monitor_manager_constructed (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); MetaBackend *backend = manager->backend; MetaSettings *settings = meta_backend_get_settings (backend); manager->display_config = meta_dbus_display_config_skeleton_new (); manager->experimental_features_changed_handler_id = g_signal_connect (settings, "experimental-features-changed", G_CALLBACK (experimental_features_changed), manager); monitor_manager_setup_dbus_config_handlers (manager); g_signal_connect_object (manager->display_config, "notify::power-save-mode", G_CALLBACK (power_save_mode_changed), manager, G_CONNECT_SWAPPED); g_signal_connect_object (meta_backend_get_orientation_manager (backend), "orientation-changed", G_CALLBACK (orientation_changed), manager, 0); g_signal_connect_object (backend, "lid-is-closed-changed", G_CALLBACK (lid_is_closed_changed), manager, 0); manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; initialize_dbus_interface (manager); } static void meta_monitor_manager_finalize (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); g_list_free_full (manager->logical_monitors, g_object_unref); g_clear_signal_handler (&manager->experimental_features_changed_handler_id, manager->backend); G_OBJECT_CLASS (meta_monitor_manager_parent_class)->finalize (object); } static void meta_monitor_manager_dispose (GObject *object) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); if (manager->dbus_name_id != 0) { g_bus_unown_name (manager->dbus_name_id); manager->dbus_name_id = 0; } g_clear_object (&manager->display_config); g_clear_object (&manager->config_manager); G_OBJECT_CLASS (meta_monitor_manager_parent_class)->dispose (object); } static GBytes * meta_monitor_manager_real_read_edid (MetaMonitorManager *manager, MetaOutput *output) { return NULL; } static void meta_monitor_manager_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); switch (prop_id) { case PROP_BACKEND: manager->backend = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_monitor_manager_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaMonitorManager *manager = META_MONITOR_MANAGER (object); switch (prop_id) { case PROP_BACKEND: g_value_set_object (value, manager->backend); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_monitor_manager_class_init (MetaMonitorManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_monitor_manager_constructed; object_class->dispose = meta_monitor_manager_dispose; object_class->finalize = meta_monitor_manager_finalize; object_class->get_property = meta_monitor_manager_get_property; object_class->set_property = meta_monitor_manager_set_property; klass->read_edid = meta_monitor_manager_real_read_edid; klass->read_current_state = meta_monitor_manager_real_read_current_state; signals[MONITORS_CHANGED] = g_signal_new ("monitors-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[MONITORS_CHANGED_INTERNAL] = g_signal_new ("monitors-changed-internal", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[POWER_SAVE_MODE_CHANGED] = g_signal_new ("power-save-mode-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[CONFIRM_DISPLAY_CHANGE] = g_signal_new ("confirm-display-change", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "backend", "MetaBackend", META_TYPE_BACKEND, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } gboolean meta_monitor_has_aspect_as_size (MetaMonitor *monitor) { int width_mm; int height_mm; meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm); return (width_mm == 1600 && height_mm == 900) || (width_mm == 1600 && height_mm == 1000) || (width_mm == 160 && height_mm == 90) || (width_mm == 160 && height_mm == 100) || (width_mm == 16 && height_mm == 9) || (width_mm == 16 && height_mm == 10); } static const char * get_connector_type_name (MetaConnectorType connector_type) { switch (connector_type) { case META_CONNECTOR_TYPE_Unknown: return "Unknown"; case META_CONNECTOR_TYPE_VGA: return "VGA"; case META_CONNECTOR_TYPE_DVII: return "DVII"; case META_CONNECTOR_TYPE_DVID: return "DVID"; case META_CONNECTOR_TYPE_DVIA: return "DVIA"; case META_CONNECTOR_TYPE_Composite: return "Composite"; case META_CONNECTOR_TYPE_SVIDEO: return "SVIDEO"; case META_CONNECTOR_TYPE_LVDS: return "LVDS"; case META_CONNECTOR_TYPE_Component: return "Component"; case META_CONNECTOR_TYPE_9PinDIN: return "9PinDIN"; case META_CONNECTOR_TYPE_DisplayPort: return "DisplayPort"; case META_CONNECTOR_TYPE_HDMIA: return "HDMIA"; case META_CONNECTOR_TYPE_HDMIB: return "HDMIB"; case META_CONNECTOR_TYPE_TV: return "TV"; case META_CONNECTOR_TYPE_eDP: return "eDP"; case META_CONNECTOR_TYPE_VIRTUAL: return "VIRTUAL"; case META_CONNECTOR_TYPE_DSI: return "DSI"; default: g_assert_not_reached (); } return NULL; } static GList * combine_gpu_lists (MetaMonitorManager *manager, GList * (*list_getter) (MetaGpu *gpu)) { GList *gpus; GList *list = NULL; GList *l; gpus = meta_backend_get_gpus (manager->backend); for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; list = g_list_concat (list, g_list_copy (list_getter (gpu))); } return list; } static gboolean meta_monitor_manager_handle_get_resources (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, MetaMonitorManager *manager) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); GList *combined_modes; GList *combined_outputs; GList *combined_crtcs; GVariantBuilder crtc_builder, output_builder, mode_builder; GList *l; unsigned int i, j; int max_screen_width; int max_screen_height; combined_modes = combine_gpu_lists (manager, meta_gpu_get_modes); combined_outputs = combine_gpu_lists (manager, meta_gpu_get_outputs); combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs); g_variant_builder_init (&crtc_builder, G_VARIANT_TYPE ("a(uxiiiiiuaua{sv})")); g_variant_builder_init (&output_builder, G_VARIANT_TYPE ("a(uxiausauaua{sv})")); g_variant_builder_init (&mode_builder, G_VARIANT_TYPE ("a(uxuudu)")); for (l = combined_crtcs, i = 0; l; l = l->next, i++) { MetaCrtc *crtc = l->data; GVariantBuilder transforms; MetaCrtcConfig *crtc_config; g_variant_builder_init (&transforms, G_VARIANT_TYPE ("au")); for (j = 0; j <= META_MONITOR_TRANSFORM_FLIPPED_270; j++) if (crtc->all_transforms & (1 << j)) g_variant_builder_add (&transforms, "u", j); crtc_config = crtc->config; if (crtc_config) { int current_mode_index; current_mode_index = g_list_index (combined_modes, crtc_config->mode); g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})", i, /* ID */ (int64_t) crtc->crtc_id, (int) roundf (crtc_config->layout.origin.x), (int) roundf (crtc_config->layout.origin.y), (int) roundf (crtc_config->layout.size.width), (int) roundf (crtc_config->layout.size.height), current_mode_index, (uint32_t) crtc_config->transform, &transforms, NULL /* properties */); } else { g_variant_builder_add (&crtc_builder, "(uxiiiiiuaua{sv})", i, /* ID */ (int64_t) crtc->crtc_id, 0, 0, 0, 0, -1, (uint32_t) META_MONITOR_TRANSFORM_NORMAL, &transforms, NULL /* properties */); } } for (l = combined_outputs, i = 0; l; l = l->next, i++) { MetaOutput *output = l->data; GVariantBuilder crtcs, modes, clones, properties; GBytes *edid; MetaCrtc *crtc; int crtc_index; g_variant_builder_init (&crtcs, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_possible_crtcs; j++) { MetaCrtc *possible_crtc = output->possible_crtcs[j]; unsigned possible_crtc_index; possible_crtc_index = g_list_index (combined_crtcs, possible_crtc); g_variant_builder_add (&crtcs, "u", possible_crtc_index); } g_variant_builder_init (&modes, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_modes; j++) { unsigned mode_index; mode_index = g_list_index (combined_modes, output->modes[j]); g_variant_builder_add (&modes, "u", mode_index); } g_variant_builder_init (&clones, G_VARIANT_TYPE ("au")); for (j = 0; j < output->n_possible_clones; j++) { unsigned int possible_clone_index; possible_clone_index = g_list_index (combined_outputs, output->possible_clones[j]); g_variant_builder_add (&clones, "u", possible_clone_index); } g_variant_builder_init (&properties, G_VARIANT_TYPE ("a{sv}")); g_variant_builder_add (&properties, "{sv}", "vendor", g_variant_new_string (output->vendor)); g_variant_builder_add (&properties, "{sv}", "product", g_variant_new_string (output->product)); g_variant_builder_add (&properties, "{sv}", "serial", g_variant_new_string (output->serial)); g_variant_builder_add (&properties, "{sv}", "width-mm", g_variant_new_int32 (output->width_mm)); g_variant_builder_add (&properties, "{sv}", "height-mm", g_variant_new_int32 (output->height_mm)); g_variant_builder_add (&properties, "{sv}", "display-name", g_variant_new_string (output->name)); g_variant_builder_add (&properties, "{sv}", "backlight", g_variant_new_int32 (output->backlight)); g_variant_builder_add (&properties, "{sv}", "min-backlight-step", g_variant_new_int32 ((output->backlight_max - output->backlight_min) ? 100 / (output->backlight_max - output->backlight_min) : -1)); g_variant_builder_add (&properties, "{sv}", "primary", g_variant_new_boolean (output->is_primary)); g_variant_builder_add (&properties, "{sv}", "presentation", g_variant_new_boolean (output->is_presentation)); g_variant_builder_add (&properties, "{sv}", "connector-type", g_variant_new_string (get_connector_type_name (output->connector_type))); g_variant_builder_add (&properties, "{sv}", "underscanning", g_variant_new_boolean (output->is_underscanning)); g_variant_builder_add (&properties, "{sv}", "supports-underscanning", g_variant_new_boolean (output->supports_underscanning)); edid = manager_class->read_edid (manager, output); if (edid) { g_variant_builder_add (&properties, "{sv}", "edid", g_variant_new_from_bytes (G_VARIANT_TYPE ("ay"), edid, TRUE)); g_bytes_unref (edid); } if (output->tile_info.group_id) { g_variant_builder_add (&properties, "{sv}", "tile", g_variant_new ("(uuuuuuuu)", output->tile_info.group_id, output->tile_info.flags, output->tile_info.max_h_tiles, output->tile_info.max_v_tiles, output->tile_info.loc_h_tile, output->tile_info.loc_v_tile, output->tile_info.tile_w, output->tile_info.tile_h)); } crtc = meta_output_get_assigned_crtc (output); crtc_index = crtc ? g_list_index (combined_crtcs, crtc) : -1; g_variant_builder_add (&output_builder, "(uxiausauaua{sv})", i, /* ID */ (gint64)output->winsys_id, crtc_index, &crtcs, output->name, &modes, &clones, &properties); } for (l = combined_modes, i = 0; l; l = l->next, i++) { MetaCrtcMode *mode = l->data; g_variant_builder_add (&mode_builder, "(uxuudu)", i, /* ID */ (gint64)mode->mode_id, (guint32)mode->width, (guint32)mode->height, (double)mode->refresh_rate, (guint32)mode->flags); } if (!meta_monitor_manager_get_max_screen_size (manager, &max_screen_width, &max_screen_height)) { /* No max screen size, just send something large */ max_screen_width = 65535; max_screen_height = 65535; } meta_dbus_display_config_complete_get_resources (skeleton, invocation, manager->serial, g_variant_builder_end (&crtc_builder), g_variant_builder_end (&output_builder), g_variant_builder_end (&mode_builder), max_screen_width, max_screen_height); g_list_free (combined_modes); g_list_free (combined_outputs); g_list_free (combined_crtcs); return TRUE; } static void restore_previous_experimental_config (MetaMonitorManager *manager, MetaMonitorsConfig *previous_config) { MetaBackend *backend = manager->backend; MetaSettings *settings = meta_backend_get_settings (backend); gboolean was_fractional; if (!META_IS_MONITOR_MANAGER_XRANDR (manager)) return; was_fractional = previous_config->layout_mode != META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; if (meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING) == was_fractional) return; g_signal_handler_block (settings, manager->experimental_features_changed_handler_id); meta_settings_enable_x11_fractional_scaling (settings, was_fractional); g_signal_handler_unblock (settings, manager->experimental_features_changed_handler_id); } static void restore_previous_config (MetaMonitorManager *manager) { MetaMonitorsConfig *previous_config; GError *error = NULL; previous_config = meta_monitor_config_manager_pop_previous (manager->config_manager); if (previous_config) { MetaMonitorsConfigMethod method; restore_previous_experimental_config (manager, previous_config); method = META_MONITORS_CONFIG_METHOD_TEMPORARY; if (meta_monitor_manager_apply_monitors_config (manager, previous_config, method, &error)) { g_object_unref (previous_config); return; } else { g_object_unref (previous_config); g_warning ("Failed to restore previous configuration: %s", error->message); g_error_free (error); } } meta_monitor_manager_ensure_configured (manager); } gint meta_monitor_manager_get_display_configuration_timeout (void) { return DEFAULT_DISPLAY_CONFIGURATION_TIMEOUT; } static gboolean save_config_timeout (gpointer user_data) { MetaMonitorManager *manager = user_data; restore_previous_config (manager); manager->persistent_timeout_id = 0; return G_SOURCE_REMOVE; } static void cancel_persistent_confirmation (MetaMonitorManager *manager) { g_clear_handle_id (&manager->persistent_timeout_id, g_source_remove); } static void request_persistent_confirmation (MetaMonitorManager *manager) { manager->persistent_timeout_id = g_timeout_add_seconds (meta_monitor_manager_get_display_configuration_timeout (), save_config_timeout, manager); g_source_set_name_by_id (manager->persistent_timeout_id, "[mutter] save_config_timeout"); g_signal_emit (manager, signals[CONFIRM_DISPLAY_CHANGE], 0); } #define META_DISPLAY_CONFIG_MODE_FLAGS_PREFERRED (1 << 0) #define META_DISPLAY_CONFIG_MODE_FLAGS_CURRENT (1 << 1) #define MODE_FORMAT "(siiddada{sv})" #define MODES_FORMAT "a" MODE_FORMAT #define MONITOR_SPEC_FORMAT "(ssss)" #define MONITOR_FORMAT "(" MONITOR_SPEC_FORMAT MODES_FORMAT "a{sv})" #define MONITORS_FORMAT "a" MONITOR_FORMAT #define LOGICAL_MONITOR_MONITORS_FORMAT "a" MONITOR_SPEC_FORMAT #define LOGICAL_MONITOR_FORMAT "(iidub" LOGICAL_MONITOR_MONITORS_FORMAT "a{sv})" #define LOGICAL_MONITORS_FORMAT "a" LOGICAL_MONITOR_FORMAT static gboolean meta_monitor_manager_handle_get_current_state (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, MetaMonitorManager *manager) { MetaSettings *settings = meta_backend_get_settings (manager->backend); GVariantBuilder monitors_builder; GVariantBuilder logical_monitors_builder; GVariantBuilder properties_builder; GList *l; int i; MetaMonitorManagerCapability capabilities; int ui_scaling_factor; int max_screen_width, max_screen_height; char *renderer; g_variant_builder_init (&monitors_builder, G_VARIANT_TYPE (MONITORS_FORMAT)); g_variant_builder_init (&logical_monitors_builder, G_VARIANT_TYPE (LOGICAL_MONITORS_FORMAT)); for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor); MetaMonitorMode *current_mode; MetaMonitorMode *preferred_mode; GVariantBuilder modes_builder; GVariantBuilder monitor_properties_builder; GList *k; gboolean is_builtin; const char *display_name; current_mode = meta_monitor_get_current_mode (monitor); preferred_mode = meta_monitor_get_preferred_mode (monitor); g_variant_builder_init (&modes_builder, G_VARIANT_TYPE (MODES_FORMAT)); for (k = meta_monitor_get_modes (monitor); k; k = k->next) { MetaMonitorMode *monitor_mode = k->data; GVariantBuilder supported_scales_builder; const char *mode_id; int mode_width, mode_height; float refresh_rate; float preferred_scale; float *supported_scales; int n_supported_scales; GVariantBuilder mode_properties_builder; MetaCrtcModeFlag mode_flags; if (!meta_monitor_mode_should_be_advertised (monitor_mode)) continue; mode_id = meta_monitor_mode_get_id (monitor_mode); meta_monitor_mode_get_resolution (monitor_mode, &mode_width, &mode_height); refresh_rate = meta_monitor_mode_get_refresh_rate (monitor_mode); preferred_scale = meta_monitor_manager_calculate_monitor_mode_scale (manager, manager->layout_mode, monitor, monitor_mode); g_variant_builder_init (&supported_scales_builder, G_VARIANT_TYPE ("ad")); supported_scales = meta_monitor_manager_calculate_supported_scales (manager, manager->layout_mode, monitor, monitor_mode, &n_supported_scales); for (i = 0; i < n_supported_scales; i++) g_variant_builder_add (&supported_scales_builder, "d", (double) supported_scales[i]); g_free (supported_scales); mode_flags = meta_monitor_mode_get_flags (monitor_mode); g_variant_builder_init (&mode_properties_builder, G_VARIANT_TYPE ("a{sv}")); if (monitor_mode == current_mode) g_variant_builder_add (&mode_properties_builder, "{sv}", "is-current", g_variant_new_boolean (TRUE)); if (monitor_mode == preferred_mode) g_variant_builder_add (&mode_properties_builder, "{sv}", "is-preferred", g_variant_new_boolean (TRUE)); if (mode_flags & META_CRTC_MODE_FLAG_INTERLACE) g_variant_builder_add (&mode_properties_builder, "{sv}", "is-interlaced", g_variant_new_boolean (TRUE)); g_variant_builder_add (&modes_builder, MODE_FORMAT, mode_id, mode_width, mode_height, refresh_rate, (double) preferred_scale, &supported_scales_builder, &mode_properties_builder); } g_variant_builder_init (&monitor_properties_builder, G_VARIANT_TYPE ("a{sv}")); if (meta_monitor_supports_underscanning (monitor)) { gboolean is_underscanning = meta_monitor_is_underscanning (monitor); g_variant_builder_add (&monitor_properties_builder, "{sv}", "is-underscanning", g_variant_new_boolean (is_underscanning)); } is_builtin = meta_monitor_is_laptop_panel (monitor); g_variant_builder_add (&monitor_properties_builder, "{sv}", "is-builtin", g_variant_new_boolean (is_builtin)); display_name = meta_monitor_get_display_name (monitor); g_variant_builder_add (&monitor_properties_builder, "{sv}", "display-name", g_variant_new_string (display_name)); g_variant_builder_add (&monitors_builder, MONITOR_FORMAT, monitor_spec->connector, monitor_spec->vendor, monitor_spec->product, monitor_spec->serial, &modes_builder, &monitor_properties_builder); } for (l = manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; GVariantBuilder logical_monitor_monitors_builder; GList *k; g_variant_builder_init (&logical_monitor_monitors_builder, G_VARIANT_TYPE (LOGICAL_MONITOR_MONITORS_FORMAT)); for (k = logical_monitor->monitors; k; k = k->next) { MetaMonitor *monitor = k->data; MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor); g_variant_builder_add (&logical_monitor_monitors_builder, MONITOR_SPEC_FORMAT, monitor_spec->connector, monitor_spec->vendor, monitor_spec->product, monitor_spec->serial); } g_variant_builder_add (&logical_monitors_builder, LOGICAL_MONITOR_FORMAT, logical_monitor->rect.x, logical_monitor->rect.y, (double) logical_monitor->scale, logical_monitor->transform, logical_monitor->is_primary, &logical_monitor_monitors_builder, NULL); } g_variant_builder_init (&properties_builder, G_VARIANT_TYPE ("a{sv}")); renderer = g_ascii_strdown (G_OBJECT_TYPE_NAME (manager) + strlen (g_type_name (g_type_parent (G_OBJECT_TYPE (manager)))), -1); g_variant_builder_add (&properties_builder, "{sv}", "renderer", g_variant_new_take_string (renderer)); capabilities = meta_monitor_manager_get_capabilities (manager); g_variant_builder_add (&properties_builder, "{sv}", "layout-mode", g_variant_new_uint32 (manager->layout_mode)); if (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE) { g_variant_builder_add (&properties_builder, "{sv}", "supports-changing-layout-mode", g_variant_new_boolean (TRUE)); } if (capabilities & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) { g_variant_builder_add (&properties_builder, "{sv}", "global-scale-required", g_variant_new_boolean (TRUE)); } else if (META_IS_MONITOR_MANAGER_XRANDR (manager) && (capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING) && (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) { g_variant_builder_add (&properties_builder, "{sv}", "x11-fractional-scaling", g_variant_new_boolean (TRUE)); } ui_scaling_factor = meta_settings_get_ui_scaling_factor (settings); g_variant_builder_add (&properties_builder, "{sv}", "legacy-ui-scaling-factor", g_variant_new_int32 (ui_scaling_factor)); if (meta_monitor_manager_get_max_screen_size (manager, &max_screen_width, &max_screen_height)) { GVariantBuilder max_screen_size_builder; g_variant_builder_init (&max_screen_size_builder, G_VARIANT_TYPE ("(ii)")); g_variant_builder_add (&max_screen_size_builder, "i", max_screen_width); g_variant_builder_add (&max_screen_size_builder, "i", max_screen_height); g_variant_builder_add (&properties_builder, "{sv}", "max-screen-size", g_variant_builder_end (&max_screen_size_builder)); } meta_dbus_display_config_complete_get_current_state ( skeleton, invocation, manager->serial, g_variant_builder_end (&monitors_builder), g_variant_builder_end (&logical_monitors_builder), g_variant_builder_end (&properties_builder)); return TRUE; } #undef MODE_FORMAT #undef MODES_FORMAT #undef MONITOR_SPEC_FORMAT #undef MONITOR_FORMAT #undef MONITORS_FORMAT #undef LOGICAL_MONITOR_MONITORS_FORMAT #undef LOGICAL_MONITOR_FORMAT #undef LOGICAL_MONITORS_FORMAT static gboolean meta_monitor_manager_is_scale_supported_with_threshold (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float scale, float threshold, float *out_scale) { g_autofree float *supported_scales = NULL; int n_supported_scales; int i; supported_scales = meta_monitor_manager_calculate_supported_scales (manager, layout_mode, monitor, monitor_mode, &n_supported_scales); for (i = 0; i < n_supported_scales; i++) { if (fabs (supported_scales[i] - scale) < threshold) { if (out_scale) *out_scale = supported_scales[i]; return TRUE; } } return FALSE; } gboolean meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float scale) { return meta_monitor_manager_is_scale_supported_with_threshold (manager, layout_mode, monitor, monitor_mode, scale, CINNAMON_SCALE_EPSILON, NULL); } static gboolean is_global_scale_matching_in_config (MetaMonitorsConfig *config, float scale) { GList *l; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (fabs (logical_monitor_config->scale - scale) > FLT_EPSILON) return FALSE; } return TRUE; } static gboolean meta_monitor_manager_is_scale_supported_for_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float scale) { if (meta_monitor_manager_is_scale_supported (manager, config->layout_mode, monitor, monitor_mode, scale)) { if (meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) return is_global_scale_matching_in_config (config, scale); return TRUE; } return FALSE; } static gboolean meta_monitor_manager_is_config_applicable (MetaMonitorManager *manager, MetaMonitorsConfig *config, GError **error) { GList *l; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; float scale = logical_monitor_config->scale; GList *k; for (k = logical_monitor_config->monitor_configs; k; k = k->next) { MetaMonitorConfig *monitor_config = k->data; MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec; MetaMonitorModeSpec *mode_spec = monitor_config->mode_spec; MetaMonitor *monitor; MetaMonitorMode *monitor_mode; monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec); if (!monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Specified monitor not found"); return FALSE; } monitor_mode = meta_monitor_get_mode_from_spec (monitor, mode_spec); if (!monitor_mode) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Specified monitor mode not available"); return FALSE; } if (!meta_monitor_manager_is_scale_supported_for_config (manager, config, monitor, monitor_mode, scale)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Scale not supported by backend"); return FALSE; } if (meta_monitor_is_laptop_panel (monitor) && meta_backend_is_lid_closed (manager->backend)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Refusing to activate a closed laptop panel"); return FALSE; } } } return TRUE; } static gboolean meta_monitor_manager_is_config_complete (MetaMonitorManager *manager, MetaMonitorsConfig *config) { MetaMonitorsConfigKey *current_state_key; gboolean is_config_complete; current_state_key = meta_create_monitors_config_key_for_current_state (manager); if (!current_state_key) return FALSE; is_config_complete = meta_monitors_config_key_equal (current_state_key, config->key); meta_monitors_config_key_free (current_state_key); if (!is_config_complete) return FALSE; return meta_monitor_manager_is_config_applicable (manager, config, NULL); } static MetaMonitor * find_monitor_from_connector (MetaMonitorManager *manager, char *connector) { GList *monitors; GList *l; if (!connector) return NULL; monitors = meta_monitor_manager_get_monitors (manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor); if (g_str_equal (connector, monitor_spec->connector)) return monitor; } return NULL; } #define MONITOR_CONFIG_FORMAT "(ssa{sv})" #define MONITOR_CONFIGS_FORMAT "a" MONITOR_CONFIG_FORMAT #define LOGICAL_MONITOR_CONFIG_FORMAT "(iidub" MONITOR_CONFIGS_FORMAT ")" static MetaMonitorConfig * create_monitor_config_from_variant (MetaMonitorManager *manager, GVariant *monitor_config_variant, GError **error) { MetaMonitorConfig *monitor_config = NULL; g_autofree char *connector = NULL; g_autofree char *mode_id = NULL; MetaMonitorMode *monitor_mode; MetaMonitor *monitor; MetaMonitorSpec *monitor_spec; MetaMonitorModeSpec *monitor_mode_spec; g_autoptr (GVariant) properties_variant = NULL; gboolean enable_underscanning = FALSE; gboolean set_underscanning = FALSE; g_variant_get (monitor_config_variant, "(ss@a{sv})", &connector, &mode_id, &properties_variant); monitor = find_monitor_from_connector (manager, connector); if (!monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid connector '%s' specified", connector); return NULL; } monitor_mode = meta_monitor_get_mode_from_id (monitor, mode_id); if (!monitor_mode) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid mode '%s' specified", mode_id); return NULL; } set_underscanning = g_variant_lookup (properties_variant, "underscanning", "b", &enable_underscanning); if (set_underscanning) { if (enable_underscanning && !meta_monitor_supports_underscanning (monitor)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Underscanning requested but unsupported"); return NULL; } } monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor)); monitor_mode_spec = g_new0 (MetaMonitorModeSpec, 1); *monitor_mode_spec = *meta_monitor_mode_get_spec (monitor_mode); monitor_config = g_new0 (MetaMonitorConfig, 1); *monitor_config = (MetaMonitorConfig) { .monitor_spec = monitor_spec, .mode_spec = monitor_mode_spec, .enable_underscanning = enable_underscanning }; return monitor_config; } static gboolean find_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorConfig *monitor_config, float scale, float *out_scale, GError **error) { MetaMonitorSpec *monitor_spec; MetaMonitor *monitor; MetaMonitorModeSpec *monitor_mode_spec; MetaMonitorMode *monitor_mode; g_autofree float *supported_scales = NULL; int n_supported_scales; int i; monitor_spec = monitor_config->monitor_spec; monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec); if (!monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor not found"); return FALSE; } monitor_mode_spec = monitor_config->mode_spec; monitor_mode = meta_monitor_get_mode_from_spec (monitor, monitor_mode_spec); if (!monitor_mode) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor mode not found"); return FALSE; } supported_scales = meta_monitor_manager_calculate_supported_scales (manager, layout_mode, monitor, monitor_mode, &n_supported_scales); for (i = 0; i < n_supported_scales; i++) { float supported_scale = supported_scales[i]; if (fabsf (supported_scale - scale) < FLT_EPSILON) { *out_scale = supported_scale; return TRUE; } } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Scale %g not valid for resolution %dx%d", scale, monitor_mode_spec->width, monitor_mode_spec->height); return FALSE; } static gboolean derive_logical_monitor_size (MetaMonitorConfig *monitor_config, int *out_width, int *out_height, float scale, MetaMonitorTransform transform, MetaLogicalMonitorLayoutMode layout_mode, GError **error) { int width, height; if (meta_monitor_transform_is_rotated (transform)) { width = monitor_config->mode_spec->height; height = monitor_config->mode_spec->width; } else { width = monitor_config->mode_spec->width; height = monitor_config->mode_spec->height; } switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: width = roundf (width / scale); height = roundf (height / scale); break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } *out_width = width; *out_height = height; return TRUE; } static MetaLogicalMonitorConfig * create_logical_monitor_config_from_variant (MetaMonitorManager *manager, GVariant *logical_monitor_config_variant, MetaLogicalMonitorLayoutMode layout_mode, GError **error) { MetaLogicalMonitorConfig *logical_monitor_config; int x, y, width, height; double scale_d; float scale; MetaMonitorTransform transform; gboolean is_primary; GVariantIter *monitor_configs_iter; GList *monitor_configs = NULL; MetaMonitorConfig *monitor_config; g_variant_get (logical_monitor_config_variant, LOGICAL_MONITOR_CONFIG_FORMAT, &x, &y, &scale_d, &transform, &is_primary, &monitor_configs_iter); scale = (float) scale_d; while (TRUE) { GVariant *monitor_config_variant = g_variant_iter_next_value (monitor_configs_iter); MetaMonitorConfig *monitor_config; if (!monitor_config_variant) break; monitor_config = create_monitor_config_from_variant (manager, monitor_config_variant, error); g_variant_unref (monitor_config_variant); if (!monitor_config) goto err; if (!meta_verify_monitor_config (monitor_config, error)) { meta_monitor_config_free (monitor_config); goto err; } monitor_configs = g_list_append (monitor_configs, monitor_config); } g_variant_iter_free (monitor_configs_iter); if (!monitor_configs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Empty logical monitor"); goto err; } monitor_config = monitor_configs->data; if (!find_monitor_mode_scale (manager, layout_mode, monitor_config, scale, &scale, error)) goto err; if (!derive_logical_monitor_size (monitor_config, &width, &height, scale, transform, layout_mode, error)) goto err; logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); *logical_monitor_config = (MetaLogicalMonitorConfig) { .layout = { .x = x, .y = y, .width = width, .height = height }, .transform = transform, .scale = scale, .is_primary = is_primary, .monitor_configs = monitor_configs }; if (layout_mode != META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL && !meta_verify_logical_monitor_config (logical_monitor_config, layout_mode, manager, 1.0f, error)) { meta_logical_monitor_config_free (logical_monitor_config); return NULL; } return logical_monitor_config; err: g_list_free_full (monitor_configs, (GDestroyNotify) meta_monitor_config_free); return NULL; } static gboolean is_valid_layout_mode (MetaLogicalMonitorLayoutMode layout_mode) { switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: return TRUE; } return FALSE; } static gboolean meta_monitor_manager_handle_apply_monitors_config (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, guint serial, guint method, GVariant *logical_monitor_configs_variant, GVariant *properties_variant, MetaMonitorManager *manager) { MetaMonitorManagerCapability capabilities; GVariant *layout_mode_variant = NULL; MetaLogicalMonitorLayoutMode layout_mode; GVariantIter logical_monitor_configs_iter; MetaMonitorsConfig *config; GList *logical_monitor_configs = NULL; GError *error = NULL; float max_scale = 1.0f; if (serial != manager->serial) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "The requested configuration is based on stale information"); return TRUE; } capabilities = meta_monitor_manager_get_capabilities (manager); if (properties_variant) layout_mode_variant = g_variant_lookup_value (properties_variant, "layout-mode", G_VARIANT_TYPE ("u")); if (layout_mode_variant && capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE) { g_variant_get (layout_mode_variant, "u", &layout_mode); } else if (!layout_mode_variant) { layout_mode = meta_monitor_manager_get_default_layout_mode (manager); } else { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Can't set layout mode"); return TRUE; } if (!is_valid_layout_mode (layout_mode)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Invalid layout mode specified"); return TRUE; } g_variant_iter_init (&logical_monitor_configs_iter, logical_monitor_configs_variant); while (TRUE) { GVariant *logical_monitor_config_variant = g_variant_iter_next_value (&logical_monitor_configs_iter); MetaLogicalMonitorConfig *logical_monitor_config; if (!logical_monitor_config_variant) break; logical_monitor_config = create_logical_monitor_config_from_variant (manager, logical_monitor_config_variant, layout_mode, &error); g_variant_unref (logical_monitor_config_variant); if (!logical_monitor_config) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "%s", error->message); g_error_free (error); g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return TRUE; } max_scale = MAX (max_scale, logical_monitor_config->scale); logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); } if (manager->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { GList *l; int ui_scale = ceilf (max_scale); for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; logical_monitor_config->layout.width = roundf (logical_monitor_config->layout.width * ui_scale); logical_monitor_config->layout.height = roundf (logical_monitor_config->layout.height * ui_scale); if (!meta_verify_logical_monitor_config (logical_monitor_config, manager->layout_mode, manager, ui_scale, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "%s", error->message); g_error_free (error); g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return TRUE; } } } config = meta_monitors_config_new (manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); if (!meta_verify_monitors_config (config, manager, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "%s", error->message); g_error_free (error); g_object_unref (config); return TRUE; } if (!meta_monitor_manager_is_config_applicable (manager, config, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "%s", error->message); g_error_free (error); g_object_unref (config); return TRUE; } if (manager->persistent_timeout_id && method != META_MONITORS_CONFIG_METHOD_VERIFY) cancel_persistent_confirmation (manager); if (!meta_monitor_manager_apply_monitors_config (manager, config, method, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "%s", error->message); g_error_free (error); g_object_unref (config); return TRUE; } if (method == META_MONITORS_CONFIG_METHOD_PERSISTENT) request_persistent_confirmation (manager); meta_dbus_display_config_complete_apply_monitors_config (skeleton, invocation); return TRUE; } #undef MONITOR_MODE_SPEC_FORMAT #undef MONITOR_CONFIG_FORMAT #undef MONITOR_CONFIGS_FORMAT #undef LOGICAL_MONITOR_CONFIG_FORMAT static void confirm_configuration (MetaMonitorManager *manager, gboolean confirmed) { if (confirmed) meta_monitor_config_manager_save_current (manager->config_manager); else restore_previous_config (manager); } void meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager, gboolean ok) { if (!manager->persistent_timeout_id) { /* too late */ return; } cancel_persistent_confirmation (manager); confirm_configuration (manager, ok); } static gboolean meta_monitor_manager_handle_change_backlight (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, guint serial, guint output_index, gint value, MetaMonitorManager *manager) { GList *combined_outputs; MetaOutput *output; if (serial != manager->serial) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "The requested configuration is based on stale information"); return TRUE; } combined_outputs = combine_gpu_lists (manager, meta_gpu_get_outputs); if (output_index >= g_list_length (combined_outputs)) { g_list_free (combined_outputs); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid output id"); return TRUE; } output = g_list_nth_data (combined_outputs, output_index); g_list_free (combined_outputs); if (value < 0 || value > 100) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid backlight value"); return TRUE; } if (output->backlight == -1 || (output->backlight_min == 0 && output->backlight_max == 0)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Output does not support changing backlight"); return TRUE; } META_MONITOR_MANAGER_GET_CLASS (manager)->change_backlight (manager, output, value); meta_dbus_display_config_complete_change_backlight (skeleton, invocation, output->backlight); return TRUE; } static gboolean meta_monitor_manager_handle_get_crtc_gamma (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, guint serial, guint crtc_id, MetaMonitorManager *manager) { MetaMonitorManagerClass *klass; GList *combined_crtcs; MetaCrtc *crtc; gsize size; unsigned short *red; unsigned short *green; unsigned short *blue; GBytes *red_bytes, *green_bytes, *blue_bytes; GVariant *red_v, *green_v, *blue_v; if (serial != manager->serial) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "The requested configuration is based on stale information"); return TRUE; } combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs); if (crtc_id >= g_list_length (combined_crtcs)) { g_list_free (combined_crtcs); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid crtc id"); return TRUE; } crtc = g_list_nth_data (combined_crtcs, crtc_id); g_list_free (combined_crtcs); klass = META_MONITOR_MANAGER_GET_CLASS (manager); if (klass->get_crtc_gamma) klass->get_crtc_gamma (manager, crtc, &size, &red, &green, &blue); else { size = 0; red = green = blue = NULL; } red_bytes = g_bytes_new_take (red, size * sizeof (unsigned short)); green_bytes = g_bytes_new_take (green, size * sizeof (unsigned short)); blue_bytes = g_bytes_new_take (blue, size * sizeof (unsigned short)); red_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), red_bytes, TRUE); green_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), green_bytes, TRUE); blue_v = g_variant_new_from_bytes (G_VARIANT_TYPE ("aq"), blue_bytes, TRUE); meta_dbus_display_config_complete_get_crtc_gamma (skeleton, invocation, red_v, green_v, blue_v); g_bytes_unref (red_bytes); g_bytes_unref (green_bytes); g_bytes_unref (blue_bytes); return TRUE; } static gboolean meta_monitor_manager_handle_set_crtc_gamma (MetaDBusDisplayConfig *skeleton, GDBusMethodInvocation *invocation, guint serial, guint crtc_id, GVariant *red_v, GVariant *green_v, GVariant *blue_v, MetaMonitorManager *manager) { MetaMonitorManagerClass *klass; GList *combined_crtcs; MetaCrtc *crtc; gsize size, dummy; unsigned short *red; unsigned short *green; unsigned short *blue; GBytes *red_bytes, *green_bytes, *blue_bytes; if (serial != manager->serial) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "The requested configuration is based on stale information"); return TRUE; } combined_crtcs = combine_gpu_lists (manager, meta_gpu_get_crtcs); if (crtc_id >= g_list_length (combined_crtcs)) { g_list_free (combined_crtcs); g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_INVALID_ARGS, "Invalid crtc id"); return TRUE; } crtc = g_list_nth_data (combined_crtcs, crtc_id); g_list_free (combined_crtcs); red_bytes = g_variant_get_data_as_bytes (red_v); green_bytes = g_variant_get_data_as_bytes (green_v); blue_bytes = g_variant_get_data_as_bytes (blue_v); size = g_bytes_get_size (red_bytes) / sizeof (unsigned short); red = (unsigned short*) g_bytes_get_data (red_bytes, &dummy); green = (unsigned short*) g_bytes_get_data (green_bytes, &dummy); blue = (unsigned short*) g_bytes_get_data (blue_bytes, &dummy); klass = META_MONITOR_MANAGER_GET_CLASS (manager); if (klass->set_crtc_gamma) klass->set_crtc_gamma (manager, crtc, size, red, green, blue); meta_dbus_display_config_complete_set_crtc_gamma (skeleton, invocation); g_bytes_unref (red_bytes); g_bytes_unref (green_bytes); g_bytes_unref (blue_bytes); return TRUE; } static void monitor_manager_setup_dbus_config_handlers (MetaMonitorManager *manager) { g_signal_connect_object (manager->display_config, "handle-get-resources", G_CALLBACK (meta_monitor_manager_handle_get_resources), manager, 0); g_signal_connect_object (manager->display_config, "handle-change-backlight", G_CALLBACK (meta_monitor_manager_handle_change_backlight), manager, 0); g_signal_connect_object (manager->display_config, "handle-get-crtc-gamma", G_CALLBACK (meta_monitor_manager_handle_get_crtc_gamma), manager, 0); g_signal_connect_object (manager->display_config, "handle-set-crtc-gamma", G_CALLBACK (meta_monitor_manager_handle_set_crtc_gamma), manager, 0); g_signal_connect_object (manager->display_config, "handle-get-current-state", G_CALLBACK (meta_monitor_manager_handle_get_current_state), manager, 0); g_signal_connect_object (manager->display_config, "handle-apply-monitors-config", G_CALLBACK (meta_monitor_manager_handle_apply_monitors_config), manager, 0); } static void on_bus_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { MetaMonitorManager *manager = user_data; g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (manager->display_config), connection, "/org/cinnamon/Muffin/DisplayConfig", NULL); } static void on_name_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { meta_topic (META_DEBUG_DBUS, "Acquired name %s\n", name); } static void on_name_lost (GDBusConnection *connection, const char *name, gpointer user_data) { meta_topic (META_DEBUG_DBUS, "Lost or failed to acquire name %s\n", name); } static void initialize_dbus_interface (MetaMonitorManager *manager) { manager->dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, "org.cinnamon.Muffin.DisplayConfig", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | (meta_get_replace_current_wm () ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), on_bus_acquired, on_name_acquired, on_name_lost, g_object_ref (manager), g_object_unref); } /** * meta_monitor_manager_get: * * Accessor for the singleton MetaMonitorManager. * * Returns: (transfer none): The only #MetaMonitorManager there is. */ MetaMonitorManager * meta_monitor_manager_get (void) { MetaBackend *backend = meta_get_backend (); return meta_backend_get_monitor_manager (backend); } /** * meta_monitor_manager_get_num_logical_monitors: * @manager: A #MetaMonitorManager object * * Returns the number of #MetaLogicalMonitors (can be 0 in case of a * headless setup). * * Returns: the total number of #MetaLogicalMonitors. */ int meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager) { return g_list_length (manager->logical_monitors); } /** * meta_monitor_manager_get_logical_monitors: * @manager: A #MetaMonitorManager object * * Returns the list of #MetaLogicalMonitors that is handled. See also * meta_monitor_manager_get_num_logical_monitors() if you only need the size of * the list. * * Returns: (transfer none) (nullable): the list of logical monitors. */ GList * meta_monitor_manager_get_logical_monitors (MetaMonitorManager *manager) { return manager->logical_monitors; } MetaLogicalMonitor * meta_monitor_manager_get_logical_monitor_from_number (MetaMonitorManager *manager, int number) { g_return_val_if_fail ((unsigned int) number < g_list_length (manager->logical_monitors), NULL); return g_list_nth (manager->logical_monitors, number)->data; } MetaLogicalMonitor * meta_monitor_manager_get_primary_logical_monitor (MetaMonitorManager *manager) { return manager->primary_logical_monitor; } static MetaMonitor * find_monitor (MetaMonitorManager *monitor_manager, gboolean (*match_func) (MetaMonitor *monitor)) { GList *monitors; GList *l; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (match_func (monitor)) return monitor; } return NULL; } /** * meta_monitor_manager_get_primary_monitor: * @manager: A #MetaMonitorManager object * * Returns the primary monitor. This can be %NULL (e.g. when running headless). * * Returns: (transfer none) (nullable): The primary #MetaMonitor, or %NULL if * none. */ MetaMonitor * meta_monitor_manager_get_primary_monitor (MetaMonitorManager *manager) { return find_monitor (manager, meta_monitor_is_primary); } /** * meta_monitor_manager_get_laptop_panel: * @manager: A #MetaMonitorManager object * * Returns the #MetaMonitor that represents the built-in laptop panel (if * applicable). * * Returns: (transfer none) (nullable): The laptop panel, or %NULL if none. */ MetaMonitor * meta_monitor_manager_get_laptop_panel (MetaMonitorManager *manager) { return find_monitor (manager, meta_monitor_is_laptop_panel); } MetaMonitor * meta_monitor_manager_get_monitor_from_connector (MetaMonitorManager *manager, const char *connector) { GList *l; for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (g_str_equal (meta_monitor_get_connector (monitor), connector)) return monitor; } return NULL; } MetaMonitor * meta_monitor_manager_get_monitor_from_spec (MetaMonitorManager *manager, MetaMonitorSpec *monitor_spec) { GList *l; for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (meta_monitor_spec_equals (meta_monitor_get_spec (monitor), monitor_spec)) return monitor; } return NULL; } /** * meta_monitor_manager_get_logical_monitor_at: * @manager: A #MetaMonitorManager object * @x: The x-coordinate * @y: The y-coordinate * * Finds the #MetaLogicalMonitor at the given @x and @y coordinates in the * total layout. * * Returns: (transfer none) (nullable): The #MetaLogicalMonitor at the given * point, or %NULL if none. */ MetaLogicalMonitor * meta_monitor_manager_get_logical_monitor_at (MetaMonitorManager *manager, float x, float y) { GList *l; for (l = manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (META_POINT_IN_RECT (x, y, logical_monitor->rect)) return logical_monitor; } return NULL; } /** * meta_monitor_manager_get_logical_monitor_from_rect: * @manager: A #MetaMonitorManager object * @rect: The rectangle * * Finds the #MetaLogicalMonitor which has the largest area in common with the * given @rect in the total layout. * * Returns: (transfer none) (nullable): The #MetaLogicalMonitor which * corresponds the most to the given @rect, or %NULL if none. */ MetaLogicalMonitor * meta_monitor_manager_get_logical_monitor_from_rect (MetaMonitorManager *manager, MetaRectangle *rect) { MetaLogicalMonitor *best_logical_monitor; int best_logical_monitor_area; GList *l; best_logical_monitor = NULL; best_logical_monitor_area = 0; for (l = manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaRectangle intersection; int intersection_area; if (!meta_rectangle_intersect (&logical_monitor->rect, rect, &intersection)) continue; intersection_area = meta_rectangle_area (&intersection); if (intersection_area > best_logical_monitor_area) { best_logical_monitor = logical_monitor; best_logical_monitor_area = intersection_area; } } if (!best_logical_monitor && (rect->width == 0 || rect->height == 0)) best_logical_monitor = meta_monitor_manager_get_logical_monitor_at (manager, rect->x, rect->y); if (!best_logical_monitor) best_logical_monitor = manager->primary_logical_monitor; return best_logical_monitor; } MetaLogicalMonitor * meta_monitor_manager_get_logical_monitor_neighbor (MetaMonitorManager *manager, MetaLogicalMonitor *logical_monitor, MetaDisplayDirection direction) { GList *l; for (l = manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *other = l->data; if (meta_logical_monitor_has_neighbor (logical_monitor, other, direction)) return other; } return NULL; } /** * meta_monitor_manager_get_monitors: * @manager: A #MetaMonitorManager object * * Returns the list of #MetaMonitors. See also * meta_monitor_manager_get_logical_monitors() for a list of * #MetaLogicalMonitors. * * Returns: (transfer none) (nullable): the list of #MetaMonitors. */ GList * meta_monitor_manager_get_monitors (MetaMonitorManager *manager) { return manager->monitors; } void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager, int *width, int *height) { *width = manager->screen_width; *height = manager->screen_height; } MetaPowerSave meta_monitor_manager_get_power_save_mode (MetaMonitorManager *manager) { MetaMonitorManagerPrivate *priv = meta_monitor_manager_get_instance_private (manager); return priv->power_save_mode; } static void rebuild_monitors (MetaMonitorManager *manager) { GList *gpus; GList *l; gboolean has_tiling; has_tiling = meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_TILING; if (manager->monitors) { g_list_free_full (manager->monitors, g_object_unref); manager->monitors = NULL; } gpus = meta_backend_get_gpus (manager->backend); for (l = gpus; l; l = l->next) { MetaGpu *gpu = l->data; GList *k; for (k = meta_gpu_get_outputs (gpu); k; k = k->next) { MetaOutput *output = k->data; if (has_tiling && output->tile_info.group_id) { if (is_main_tiled_monitor_output (output)) { MetaMonitorTiled *monitor_tiled; monitor_tiled = meta_monitor_tiled_new (gpu, manager, output); manager->monitors = g_list_append (manager->monitors, monitor_tiled); } } else { MetaMonitorNormal *monitor_normal; monitor_normal = meta_monitor_normal_new (gpu, manager, output); manager->monitors = g_list_append (manager->monitors, monitor_normal); } } } } void meta_monitor_manager_tiled_monitor_added (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); if (manager_class->tiled_monitor_added) manager_class->tiled_monitor_added (manager, monitor); } void meta_monitor_manager_tiled_monitor_removed (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); if (manager_class->tiled_monitor_removed) manager_class->tiled_monitor_removed (manager, monitor); } gboolean meta_monitor_manager_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); return manager_class->is_transform_handled (manager, crtc, transform); } static void meta_monitor_manager_real_read_current_state (MetaMonitorManager *manager) { GList *l; manager->serial++; for (l = meta_backend_get_gpus (manager->backend); l; l = l->next) { MetaGpu *gpu = l->data; GError *error = NULL; if (!meta_gpu_read_current (gpu, &error)) { g_warning ("Failed to read current KMS state: %s", error->message); g_clear_error (&error); } } rebuild_monitors (manager); } void meta_monitor_manager_read_current_state (MetaMonitorManager *manager) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_GET_CLASS (manager); manager_class->read_current_state (manager); } static void meta_monitor_manager_notify_monitors_changed (MetaMonitorManager *manager) { meta_backend_monitors_changed (manager->backend); g_signal_emit (manager, signals[MONITORS_CHANGED_INTERNAL], 0); g_signal_emit (manager, signals[MONITORS_CHANGED], 0); meta_dbus_display_config_emit_monitors_changed (manager->display_config); } static void set_logical_monitor_modes (MetaMonitorManager *manager, MetaLogicalMonitorConfig *logical_monitor_config) { GList *l; for (l = logical_monitor_config->monitor_configs; l; l = l->next) { MetaMonitorConfig *monitor_config = l->data; MetaMonitorSpec *monitor_spec; MetaMonitor *monitor; MetaMonitorModeSpec *monitor_mode_spec; MetaMonitorMode *monitor_mode; monitor_spec = monitor_config->monitor_spec; monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec); monitor_mode_spec = monitor_config->mode_spec; monitor_mode = meta_monitor_get_mode_from_spec (monitor, monitor_mode_spec); meta_monitor_set_current_mode (monitor, monitor_mode); } } static void meta_monitor_manager_update_monitor_modes (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *logical_monitor_configs; GList *l; g_list_foreach (manager->monitors, (GFunc) meta_monitor_set_current_mode, NULL); logical_monitor_configs = config ? config->logical_monitor_configs : NULL; for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; set_logical_monitor_modes (manager, logical_monitor_config); } } void meta_monitor_manager_update_logical_state (MetaMonitorManager *manager, MetaMonitorsConfig *config) { if (config) { manager->layout_mode = config->layout_mode; manager->current_switch_config = meta_monitors_config_get_switch_config (config); } else { manager->layout_mode = meta_monitor_manager_get_default_layout_mode (manager); manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; } meta_monitor_manager_rebuild_logical_monitors (manager, config); } void meta_monitor_manager_rebuild (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *old_logical_monitors; meta_monitor_manager_update_monitor_modes (manager, config); if (manager->in_init) return; old_logical_monitors = manager->logical_monitors; meta_monitor_manager_update_logical_state (manager, config); meta_monitor_manager_notify_monitors_changed (manager); g_list_free_full (old_logical_monitors, g_object_unref); } static void meta_monitor_manager_update_monitor_modes_derived (MetaMonitorManager *manager) { GList *l; for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; meta_monitor_derive_current_mode (monitor); } } void meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { if (config) manager->current_switch_config = meta_monitors_config_get_switch_config (config); else manager->current_switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; manager->layout_mode = meta_monitor_manager_get_default_layout_mode (manager); meta_monitor_manager_rebuild_logical_monitors_derived (manager, config); } void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { MetaMonitorManagerClass *klass = META_MONITOR_MANAGER_GET_CLASS (manager); GList *old_logical_monitors; meta_monitor_manager_update_monitor_modes_derived (manager); if (klass->update_screen_size_derived) klass->update_screen_size_derived (manager, config); if (manager->in_init) return; old_logical_monitors = manager->logical_monitors; meta_monitor_manager_update_logical_state_derived (manager, config); meta_monitor_manager_notify_monitors_changed (manager); g_list_free_full (old_logical_monitors, g_object_unref); } void meta_output_parse_edid (MetaOutput *output, GBytes *edid) { MonitorInfo *parsed_edid; gsize len; if (!edid) goto out; parsed_edid = decode_edid (g_bytes_get_data (edid, &len)); if (parsed_edid) { output->vendor = g_strndup (parsed_edid->manufacturer_code, 4); if (!g_utf8_validate (output->vendor, -1, NULL)) g_clear_pointer (&output->vendor, g_free); output->product = g_strndup (parsed_edid->dsc_product_name, 14); if (!g_utf8_validate (output->product, -1, NULL) || output->product[0] == '\0') { g_clear_pointer (&output->product, g_free); output->product = g_strdup_printf ("0x%04x", (unsigned) parsed_edid->product_code); } output->serial = g_strndup (parsed_edid->dsc_serial_number, 14); if (!g_utf8_validate (output->serial, -1, NULL) || output->serial[0] == '\0') { g_clear_pointer (&output->serial, g_free); output->serial = g_strdup_printf ("0x%08x", parsed_edid->serial_number); } g_free (parsed_edid); } out: if (!output->vendor) output->vendor = g_strdup ("unknown"); if (!output->product) output->product = g_strdup ("unknown"); if (!output->serial) output->serial = g_strdup ("unknown"); } gboolean meta_output_is_laptop (MetaOutput *output) { /* FIXME: extend with better heuristics */ switch (output->connector_type) { case META_CONNECTOR_TYPE_eDP: case META_CONNECTOR_TYPE_LVDS: case META_CONNECTOR_TYPE_DSI: return TRUE; default: return FALSE; } } void meta_monitor_manager_on_hotplug (MetaMonitorManager *manager) { meta_monitor_manager_ensure_configured (manager); } static gboolean calculate_viewport_matrix (MetaMonitorManager *manager, MetaLogicalMonitor *logical_monitor, gfloat viewport[6]) { gfloat x, y, width, height; x = (float) logical_monitor->rect.x / manager->screen_width; y = (float) logical_monitor->rect.y / manager->screen_height; width = (float) logical_monitor->rect.width / manager->screen_width; height = (float) logical_monitor->rect.height / manager->screen_height; viewport[0] = width; viewport[1] = 0.0f; viewport[2] = x; viewport[3] = 0.0f; viewport[4] = height; viewport[5] = y; return TRUE; } static inline void multiply_matrix (float a[6], float b[6], float res[6]) { res[0] = a[0] * b[0] + a[1] * b[3]; res[1] = a[0] * b[1] + a[1] * b[4]; res[2] = a[0] * b[2] + a[1] * b[5] + a[2]; res[3] = a[3] * b[0] + a[4] * b[3]; res[4] = a[3] * b[1] + a[4] * b[4]; res[5] = a[3] * b[2] + a[4] * b[5] + a[5]; } gboolean meta_monitor_manager_get_monitor_matrix (MetaMonitorManager *manager, MetaMonitor *monitor, MetaLogicalMonitor *logical_monitor, gfloat matrix[6]) { MetaMonitorTransform transform; gfloat viewport[9]; if (!calculate_viewport_matrix (manager, logical_monitor, viewport)) return FALSE; /* Get transform corrected for LCD panel-orientation. */ transform = logical_monitor->transform; transform = meta_monitor_logical_to_crtc_transform (monitor, transform); multiply_matrix (viewport, transform_matrices[transform], matrix); return TRUE; } /** * meta_monitor_manager_get_monitor_for_connector: * @manager: A #MetaMonitorManager * @connector: A valid connector name * * Returns: The monitor index or -1 if @id isn't valid or the connector * isn't associated with a logical monitor. */ gint meta_monitor_manager_get_monitor_for_connector (MetaMonitorManager *manager, const char *connector) { GList *l; for (l = manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; if (meta_monitor_is_active (monitor) && g_str_equal (connector, meta_monitor_get_connector (monitor))) return meta_monitor_get_logical_monitor (monitor)->number; } return -1; } /** * meta_monitor_manager_get_is_builtin_display_on: * @manager: A #MetaMonitorManager object * * Returns whether the built-in display (i.e. a laptop panel) is turned on. */ gboolean meta_monitor_manager_get_is_builtin_display_on (MetaMonitorManager *manager) { MetaMonitor *laptop_panel; g_return_val_if_fail (META_IS_MONITOR_MANAGER (manager), FALSE); laptop_panel = meta_monitor_manager_get_laptop_panel (manager); if (!laptop_panel) return FALSE; return meta_monitor_is_active (laptop_panel); } void meta_monitor_manager_rotate_monitor (MetaMonitorManager *manager) { GError *error = NULL; MetaMonitorsConfig *config = meta_monitor_config_manager_create_for_rotate_monitor (manager->config_manager); if (!config) return; if (!meta_monitor_manager_apply_monitors_config (manager, config, META_MONITORS_CONFIG_METHOD_TEMPORARY, &error)) { g_warning ("Failed to use rotate monitor configuration: %s", error->message); g_error_free (error); } g_object_unref (config); } void meta_monitor_manager_switch_config (MetaMonitorManager *manager, MetaMonitorSwitchConfigType config_type) { GError *error = NULL; MetaMonitorsConfig *config; g_return_if_fail (config_type != META_MONITOR_SWITCH_CONFIG_UNKNOWN); config = meta_monitor_config_manager_create_for_switch_config (manager->config_manager, config_type); if (!config) return; if (!meta_monitor_manager_apply_monitors_config (manager, config, META_MONITORS_CONFIG_METHOD_TEMPORARY, &error)) { g_warning ("Failed to use switch monitor configuration: %s", error->message); g_error_free (error); } else { manager->current_switch_config = config_type; } g_object_unref (config); } gboolean meta_monitor_manager_can_switch_config (MetaMonitorManager *manager) { return (!meta_backend_is_lid_closed (manager->backend) && g_list_length (manager->monitors) > 1); } MetaMonitorSwitchConfigType meta_monitor_manager_get_switch_config (MetaMonitorManager *manager) { return manager->current_switch_config; } MetaMonitorConfigManager * meta_monitor_manager_get_config_manager (MetaMonitorManager *manager) { return manager->config_manager; } /** * meta_monitor_manager_get_vendor_name: * @manager: A #MetaMonitorManager object * @vendor: the PNP ID of the monitor * * Find the full vendor name from the given monitor PNP ID. * * Returns: (transfer full): A string containing the vendor name, * or NULL when not found. */ char * meta_monitor_manager_get_vendor_name (MetaMonitorManager *manager, const char *vendor) { if (!manager->pnp_ids) manager->pnp_ids = gnome_pnp_ids_new (); return gnome_pnp_ids_get_pnp_id (manager->pnp_ids, vendor); } #define META_MONITOR_TRANSFORM_INVALID -1 MetaMonitorTransform xrandr_to_monitor_transform (MetaXrandrRotation rotation) { switch (rotation) { case META_XRANDR_ROTATION_NORMAL: return META_MONITOR_TRANSFORM_NORMAL; case META_XRANDR_ROTATION_LEFT: return META_MONITOR_TRANSFORM_90; case META_XRANDR_ROTATION_FLIPPED: return META_MONITOR_TRANSFORM_180; case META_XRANDR_ROTATION_RIGHT: return META_MONITOR_TRANSFORM_270; default: g_return_val_if_reached (META_MONITOR_TRANSFORM_INVALID); } } gboolean meta_monitor_manager_can_apply_rotation (MetaMonitorManager *manager, MetaXrandrRotation rotation) { gboolean ret = FALSE; if (!meta_monitor_manager_get_is_builtin_display_on (manager)) return FALSE; MetaOrientation orientation = xrandr_to_monitor_transform (rotation); if (orientation == META_MONITOR_TRANSFORM_INVALID) { meta_warning ("Invalid orientation requested."); return ret; } MetaMonitorsConfig *config = meta_monitor_config_manager_create_for_orientation (manager->config_manager, orientation); ret = config != NULL; g_clear_object (&config); if (ret) return TRUE; MetaXrandrRotation current_rotation; meta_monitor_manager_get_current_rotation (manager, ¤t_rotation); if (current_rotation == rotation) return TRUE; return FALSE; } gboolean meta_monitor_manager_apply_temporary_rotation (MetaMonitorManager *manager, MetaXrandrRotation rotation) { MetaOrientation orientation = xrandr_to_monitor_transform (rotation); if (orientation == META_MONITOR_TRANSFORM_INVALID) { meta_warning ("Invalid orientation requested."); return FALSE; } GError *error = NULL; MetaMonitorsConfig *config = meta_monitor_config_manager_create_for_orientation (manager->config_manager, orientation); if (!config) { meta_warning ("Could not create config for rotation."); return FALSE; } if (!meta_monitor_manager_apply_monitors_config (manager, config, META_MONITORS_CONFIG_METHOD_TEMPORARY, &error)) { g_warning ("Failed to use rotate monitor configuration: %s", error->message); g_error_free (error); g_object_unref (config); return FALSE; } g_object_unref (config); return TRUE; } MetaXrandrRotation monitor_transform_to_xrandr_rotation (MetaMonitorTransform transform) { switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: return META_XRANDR_ROTATION_NORMAL; case META_MONITOR_TRANSFORM_90: return META_XRANDR_ROTATION_LEFT; case META_MONITOR_TRANSFORM_180: return META_XRANDR_ROTATION_FLIPPED; case META_MONITOR_TRANSFORM_270: return META_XRANDR_ROTATION_RIGHT; default: g_return_val_if_reached (META_XRANDR_ROTATION_NORMAL); } } /** * meta_monitor_manager_get_current_rotation: * @manager: A #MetaMonitorManager object * @rotation: (out): The current #MetaXrandrRotation applied to the laptop panel. * * Returns %TRUE if there is a laptop panel and its current rotation * can be determined, otherwise %FALSE */ gboolean meta_monitor_manager_get_current_rotation (MetaMonitorManager *manager, MetaXrandrRotation *rotation) { MetaMonitor *monitor; MetaLogicalMonitor *logical_monitor; *rotation = META_XRANDR_ROTATION_NORMAL; monitor = meta_monitor_manager_get_laptop_panel (manager); if (monitor == NULL || !meta_monitor_is_active (monitor)) { return FALSE; } logical_monitor = meta_monitor_get_logical_monitor (monitor); if (logical_monitor != NULL) { MetaMonitorTransform transform = meta_logical_monitor_get_transform (logical_monitor); *rotation = monitor_transform_to_xrandr_rotation (transform); return TRUE; } return FALSE; } muffin-6.4.1/src/backends/meta-monitor-manager-dummy.h0000664000175000017500000000263314723361714021634 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_MANAGER_DUMMY_H #define META_MONITOR_MANAGER_DUMMY_H #include "backends/meta-gpu.h" #include "backends/meta-monitor-manager-private.h" #define META_TYPE_MONITOR_MANAGER_DUMMY (meta_monitor_manager_dummy_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorManagerDummy, meta_monitor_manager_dummy, META, MONITOR_MANAGER_DUMMY, MetaMonitorManager) #define META_TYPE_GPU_DUMMY (meta_gpu_dummy_get_type ()) G_DECLARE_FINAL_TYPE (MetaGpuDummy, meta_gpu_dummy, META, GPU_DUMMY, MetaGpu) #endif /* META_MONITOR_MANAGER_DUMMY_H */ muffin-6.4.1/src/backends/meta-screen-cast-session.h0000664000175000017500000000571314723361714021276 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_SESSION_H #define META_SCREEN_CAST_SESSION_H #include "backends/meta-screen-cast.h" #include "backends/meta-screen-cast-stream.h" #include "meta/meta-remote-access-controller.h" typedef enum _MetaScreenCastSessionType { META_SCREEN_CAST_SESSION_TYPE_NORMAL, META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP, } MetaScreenCastSessionType; #define META_TYPE_SCREEN_CAST_SESSION (meta_screen_cast_session_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastSession, meta_screen_cast_session, META, SCREEN_CAST_SESSION, MetaDBusScreenCastSessionSkeleton) #define META_TYPE_SCREEN_CAST_SESSION_HANDLE (meta_screen_cast_session_handle_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastSessionHandle, meta_screen_cast_session_handle, META, SCREEN_CAST_SESSION_HANDLE, MetaRemoteAccessHandle) char * meta_screen_cast_session_get_object_path (MetaScreenCastSession *session); char * meta_screen_cast_session_get_peer_name (MetaScreenCastSession *session); MetaScreenCastSession * meta_screen_cast_session_new (MetaScreenCast *screen_cast, MetaScreenCastSessionType session_type, const char *peer_name, GError **error); gboolean meta_screen_cast_session_start (MetaScreenCastSession *session, GError **error); void meta_screen_cast_session_close (MetaScreenCastSession *session); MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path); MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session); void meta_screen_cast_session_set_disable_animations (MetaScreenCastSession *session, gboolean disable_animations); #endif /* META_SCREEN_CAST_SESSION_H */ muffin-6.4.1/src/backends/meta-backend.c0000664000175000017500000012272114723361714016767 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ /** * SECTION:meta-backend * @title: MetaBackend * @short_description: Handles monitor config, modesetting, cursor sprites, ... * * MetaBackend is the abstraction that deals with several things like: * - Modesetting (depending on the backend, this can be done either by X or KMS) * - Initializing the #MetaSettings * - Setting up Monitor configuration * - Input device configuration (using the #ClutterDeviceManager) * - Creating the #MetaRenderer * - Setting up the stage of the scene graph (using #MetaStage) * - Creating the object that deals wih the cursor (using #MetaCursorTracker) * and its possible pointer constraint (using #MetaPointerConstraint) * - Setting the cursor sprite (using #MetaCursorRenderer) * - Interacting with logind (using the appropriate D-Bus interface) * - Querying UPower (over D-Bus) to know when the lid is closed * - Setup Remote Desktop / Screencasting (#MetaRemoteDesktop) * - Setup the #MetaEgl object * * Note that the #MetaBackend is not a subclass of #ClutterBackend. It is * responsible for creating the correct one, based on the backend that is * used (#MetaBackendNative or #MetaBackendX11). */ #include "config.h" #include "backends/meta-backend-private.h" #include #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-idle-monitor-private.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-dummy.h" #include "backends/meta-settings-private.h" #include "backends/meta-stage-private.h" #include "backends/x11/meta-backend-x11.h" #include "clutter/clutter-mutter.h" #include "meta/main.h" #include "meta/meta-backend.h" #include "meta/util.h" #ifdef HAVE_PROFILER #include "backends/meta-profiler.h" #endif #ifdef HAVE_REMOTE_DESKTOP #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-remote-access-controller-private.h" #include "backends/meta-remote-desktop.h" #include "backends/meta-screen-cast.h" #endif #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #ifdef HAVE_WAYLAND #include "wayland/meta-wayland.h" #endif enum { KEYMAP_CHANGED, KEYMAP_LAYOUT_GROUP_CHANGED, LAST_DEVICE_CHANGED, LID_IS_CLOSED_CHANGED, GPU_ADDED, N_SIGNALS }; static guint signals[N_SIGNALS]; static MetaBackend *_backend; static gboolean stage_views_disabled = FALSE; /** * meta_get_backend: * * Accessor for the singleton MetaBackend. * * Returns: (transfer none): The only #MetaBackend there is. */ MetaBackend * meta_get_backend (void) { return _backend; } struct _MetaBackendPrivate { MetaMonitorManager *monitor_manager; MetaOrientationManager *orientation_manager; MetaCursorTracker *cursor_tracker; MetaCursorRenderer *cursor_renderer; MetaInputSettings *input_settings; MetaRenderer *renderer; #ifdef HAVE_EGL MetaEgl *egl; #endif MetaSettings *settings; #ifdef HAVE_REMOTE_DESKTOP MetaRemoteAccessController *remote_access_controller; MetaDbusSessionWatcher *dbus_session_watcher; MetaScreenCast *screen_cast; MetaRemoteDesktop *remote_desktop; #endif #ifdef HAVE_WAYLAND MetaWaylandCompositor *wayland_compositor; #endif #ifdef HAVE_PROFILER MetaProfiler *profiler; #endif #ifdef HAVE_LIBWACOM WacomDeviceDatabase *wacom_db; #endif ClutterBackend *clutter_backend; ClutterActor *stage; GList *gpus; gboolean is_pointer_position_initialized; guint device_update_idle_id; gulong keymap_state_changed_id; GHashTable *device_monitors; ClutterInputDevice *current_device; MetaPointerConstraint *client_pointer_constraint; MetaDnd *dnd; guint upower_watch_id; GDBusProxy *upower_proxy; gboolean lid_is_closed; guint sleep_signal_id; GCancellable *cancellable; GDBusConnection *system_bus; gboolean was_headless; }; typedef struct _MetaBackendPrivate MetaBackendPrivate; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaBackend, meta_backend, G_TYPE_OBJECT, G_ADD_PRIVATE (MetaBackend) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); static void meta_backend_finalize (GObject *object) { MetaBackend *backend = META_BACKEND (object); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); if (priv->keymap_state_changed_id) { ClutterSeat *seat; ClutterKeymap *keymap; seat = clutter_backend_get_default_seat (priv->clutter_backend); keymap = clutter_seat_get_keymap (seat); g_clear_signal_handler (&priv->keymap_state_changed_id, keymap); } g_list_free_full (priv->gpus, g_object_unref); g_clear_object (&priv->current_device); g_clear_object (&priv->monitor_manager); g_clear_object (&priv->orientation_manager); g_clear_object (&priv->input_settings); #ifdef HAVE_REMOTE_DESKTOP g_clear_object (&priv->remote_desktop); g_clear_object (&priv->screen_cast); g_clear_object (&priv->dbus_session_watcher); g_clear_object (&priv->remote_access_controller); #endif #ifdef HAVE_LIBWACOM g_clear_pointer (&priv->wacom_db, libwacom_database_destroy); #endif if (priv->sleep_signal_id) g_dbus_connection_signal_unsubscribe (priv->system_bus, priv->sleep_signal_id); if (priv->upower_watch_id) g_bus_unwatch_name (priv->upower_watch_id); g_cancellable_cancel (priv->cancellable); g_clear_object (&priv->cancellable); g_clear_object (&priv->system_bus); g_clear_object (&priv->upower_proxy); g_clear_handle_id (&priv->device_update_idle_id, g_source_remove); g_hash_table_destroy (priv->device_monitors); g_clear_object (&priv->settings); #ifdef HAVE_PROFILER g_clear_object (&priv->profiler); #endif G_OBJECT_CLASS (meta_backend_parent_class)->finalize (object); } static void meta_backend_sync_screen_size (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); int width, height; meta_monitor_manager_get_screen_size (priv->monitor_manager, &width, &height); META_BACKEND_GET_CLASS (backend)->update_screen_size (backend, width, height); } static void reset_pointer_position (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaMonitorManager *monitor_manager = priv->monitor_manager; ClutterSeat *seat = clutter_backend_get_default_seat (priv->clutter_backend); MetaLogicalMonitor *primary; primary = meta_monitor_manager_get_primary_logical_monitor (monitor_manager); /* Move the pointer out of the way to avoid hovering over reactive * elements (e.g. users list at login) causing undesired behaviour. */ clutter_seat_warp_pointer (seat, primary->rect.x + primary->rect.width * 0.9, primary->rect.y + primary->rect.height * 0.9); } void meta_backend_monitors_changed (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); ClutterSeat *seat = clutter_backend_get_default_seat (priv->clutter_backend); ClutterInputDevice *device = clutter_seat_get_pointer (seat); graphene_point_t point; meta_backend_sync_screen_size (backend); if (clutter_input_device_get_coords (device, NULL, &point)) { /* If we're outside all monitors, warp the pointer back inside */ if ((!meta_monitor_manager_get_logical_monitor_at (monitor_manager, point.x, point.y) || !priv->is_pointer_position_initialized) && !meta_monitor_manager_is_headless (monitor_manager)) { reset_pointer_position (backend); priv->is_pointer_position_initialized = TRUE; } } meta_cursor_renderer_force_update (priv->cursor_renderer); if (meta_monitor_manager_is_headless (priv->monitor_manager) && !priv->was_headless) { clutter_stage_freeze_updates (CLUTTER_STAGE (priv->stage)); priv->was_headless = TRUE; } else if (!meta_monitor_manager_is_headless (priv->monitor_manager) && priv->was_headless) { clutter_stage_thaw_updates (CLUTTER_STAGE (priv->stage)); priv->was_headless = FALSE; } } void meta_backend_foreach_device_monitor (MetaBackend *backend, GFunc func, gpointer user_data) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); GHashTableIter iter; gpointer value; g_hash_table_iter_init (&iter, priv->device_monitors); while (g_hash_table_iter_next (&iter, NULL, &value)) { MetaIdleMonitor *device_monitor = META_IDLE_MONITOR (value); func (device_monitor, user_data); } } static MetaIdleMonitor * meta_backend_create_idle_monitor (MetaBackend *backend, ClutterInputDevice *device) { return g_object_new (META_TYPE_IDLE_MONITOR, "device", device, NULL); } static void create_device_monitor (MetaBackend *backend, ClutterInputDevice *device) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaIdleMonitor *idle_monitor; if (g_hash_table_contains (priv->device_monitors, device)) return; idle_monitor = meta_backend_create_idle_monitor (backend, device); g_hash_table_insert (priv->device_monitors, device, idle_monitor); } static void destroy_device_monitor (MetaBackend *backend, ClutterInputDevice *device) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); g_hash_table_remove (priv->device_monitors, device); } static void meta_backend_monitor_device (MetaBackend *backend, ClutterInputDevice *device) { create_device_monitor (backend, device); } static inline gboolean device_is_slave_touchscreen (ClutterInputDevice *device) { return (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER && clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE); } static inline gboolean check_has_pointing_device (ClutterSeat *seat) { GList *l, *devices; gboolean found = FALSE; devices = clutter_seat_list_devices (seat); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) continue; if (clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE || clutter_input_device_get_device_type (device) == CLUTTER_KEYBOARD_DEVICE) continue; found = TRUE; break; } g_list_free (devices); return found; } static inline gboolean check_has_slave_touchscreen (ClutterSeat *seat) { GList *l, *devices; gboolean found = FALSE; devices = clutter_seat_list_devices (seat); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; if (clutter_input_device_get_device_mode (device) != CLUTTER_INPUT_MODE_MASTER && clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE) { found = TRUE; break; } } g_list_free (devices); return found; } static void on_device_added (ClutterSeat *seat, ClutterInputDevice *device, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); create_device_monitor (backend, device); if (device_is_slave_touchscreen (device)) meta_cursor_tracker_set_pointer_visible (priv->cursor_tracker, FALSE); } static void on_device_removed (ClutterSeat *seat, ClutterInputDevice *device, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); destroy_device_monitor (backend, device); /* If the device the user last interacted goes away, check again pointer * visibility. */ if (priv->current_device == device) { MetaCursorTracker *cursor_tracker = priv->cursor_tracker; gboolean has_touchscreen, has_pointing_device; ClutterInputDeviceType device_type; g_clear_object (&priv->current_device); g_clear_handle_id (&priv->device_update_idle_id, g_source_remove); device_type = clutter_input_device_get_device_type (device); has_touchscreen = check_has_slave_touchscreen (seat); if (device_type == CLUTTER_TOUCHSCREEN_DEVICE && has_touchscreen) { /* There's more touchscreens left, keep the pointer hidden */ meta_cursor_tracker_set_pointer_visible (cursor_tracker, FALSE); } else if (device_type != CLUTTER_KEYBOARD_DEVICE) { has_pointing_device = check_has_pointing_device (seat); meta_cursor_tracker_set_pointer_visible (cursor_tracker, has_pointing_device && !has_touchscreen); } } if (priv->current_device == device) meta_backend_update_last_device (backend, NULL); } static void create_device_monitors (MetaBackend *backend, ClutterSeat *seat) { GList *l, *devices; create_device_monitor (backend, clutter_seat_get_pointer (seat)); create_device_monitor (backend, clutter_seat_get_keyboard (seat)); devices = clutter_seat_list_devices (seat); for (l = devices; l; l = l->next) { ClutterInputDevice *device = l->data; meta_backend_monitor_device (backend, device); } g_list_free (devices); } static MetaInputSettings * meta_backend_create_input_settings (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->create_input_settings (backend); } static void meta_backend_real_post_init (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); ClutterSeat *seat = clutter_backend_get_default_seat (priv->clutter_backend); ClutterKeymap *keymap = clutter_seat_get_keymap (seat); priv->stage = meta_stage_new (backend); clutter_actor_realize (priv->stage); META_BACKEND_GET_CLASS (backend)->select_stage_events (backend); meta_monitor_manager_setup (priv->monitor_manager); meta_backend_sync_screen_size (backend); priv->cursor_renderer = META_BACKEND_GET_CLASS (backend)->create_cursor_renderer (backend); priv->device_monitors = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); create_device_monitors (backend, seat); g_signal_connect_object (seat, "device-added", G_CALLBACK (on_device_added), backend, 0); g_signal_connect_object (seat, "device-removed", G_CALLBACK (on_device_removed), backend, G_CONNECT_AFTER); priv->input_settings = meta_backend_create_input_settings (backend); if (priv->input_settings) { priv->keymap_state_changed_id = g_signal_connect_swapped (keymap, "state-changed", G_CALLBACK (meta_input_settings_maybe_save_numlock_state), priv->input_settings); meta_input_settings_maybe_restore_numlock_state (priv->input_settings); } #ifdef HAVE_REMOTE_DESKTOP priv->remote_access_controller = g_object_new (META_TYPE_REMOTE_ACCESS_CONTROLLER, NULL); priv->dbus_session_watcher = g_object_new (META_TYPE_DBUS_SESSION_WATCHER, NULL); priv->screen_cast = meta_screen_cast_new (backend, priv->dbus_session_watcher); priv->remote_desktop = meta_remote_desktop_new (priv->dbus_session_watcher); #endif /* HAVE_REMOTE_DESKTOP */ if (!meta_monitor_manager_is_headless (priv->monitor_manager)) { reset_pointer_position (backend); priv->is_pointer_position_initialized = TRUE; } } static MetaCursorRenderer * meta_backend_real_create_cursor_renderer (MetaBackend *backend) { return meta_cursor_renderer_new (); } static gboolean meta_backend_real_grab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { /* Do nothing */ return TRUE; } static gboolean meta_backend_real_ungrab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { /* Do nothing */ return TRUE; } static void meta_backend_real_select_stage_events (MetaBackend *backend) { /* Do nothing */ } static gboolean meta_backend_real_is_lid_closed (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->lid_is_closed; } gboolean meta_backend_is_lid_closed (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->is_lid_closed (backend); } static void upower_properties_changed (GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, gpointer user_data) { MetaBackend *backend = user_data; MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); GVariant *v; gboolean lid_is_closed; v = g_variant_lookup_value (changed_properties, "LidIsClosed", G_VARIANT_TYPE_BOOLEAN); if (!v) return; lid_is_closed = g_variant_get_boolean (v); g_variant_unref (v); if (lid_is_closed == priv->lid_is_closed) return; priv->lid_is_closed = lid_is_closed; g_signal_emit (backend, signals[LID_IS_CLOSED_CHANGED], 0, priv->lid_is_closed); if (lid_is_closed) return; meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ()); } static void upower_ready_cb (GObject *source_object, GAsyncResult *res, gpointer user_data) { MetaBackend *backend; MetaBackendPrivate *priv; GDBusProxy *proxy; GError *error = NULL; GVariant *v; proxy = g_dbus_proxy_new_finish (res, &error); if (!proxy) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to create UPower proxy: %s", error->message); g_error_free (error); return; } backend = META_BACKEND (user_data); priv = meta_backend_get_instance_private (backend); priv->upower_proxy = proxy; g_signal_connect (proxy, "g-properties-changed", G_CALLBACK (upower_properties_changed), backend); v = g_dbus_proxy_get_cached_property (proxy, "LidIsClosed"); if (!v) return; priv->lid_is_closed = g_variant_get_boolean (v); g_variant_unref (v); if (priv->lid_is_closed) { g_signal_emit (backend, signals[LID_IS_CLOSED_CHANGED], 0, priv->lid_is_closed); } } static void upower_appeared (GDBusConnection *connection, const gchar *name, const gchar *name_owner, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); g_dbus_proxy_new (connection, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.UPower", "/org/freedesktop/UPower", "org.freedesktop.UPower", priv->cancellable, upower_ready_cb, backend); } static void upower_vanished (GDBusConnection *connection, const gchar *name, gpointer user_data) { MetaBackend *backend = META_BACKEND (user_data); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); g_clear_object (&priv->upower_proxy); } static void meta_backend_constructed (GObject *object) { MetaBackend *backend = META_BACKEND (object); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaBackendClass *backend_class = META_BACKEND_GET_CLASS (backend); #ifdef HAVE_LIBWACOM priv->wacom_db = libwacom_database_new (); if (!priv->wacom_db) { g_warning ("Could not create database of Wacom devices, " "expect tablets to misbehave"); } #endif if (backend_class->is_lid_closed != meta_backend_real_is_lid_closed) return; priv->upower_watch_id = g_bus_watch_name (G_BUS_TYPE_SYSTEM, "org.freedesktop.UPower", G_BUS_NAME_WATCHER_FLAGS_NONE, upower_appeared, upower_vanished, backend, NULL); } static void meta_backend_class_init (MetaBackendClass *klass) { const gchar *mutter_stage_views; GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_backend_finalize; object_class->constructed = meta_backend_constructed; klass->post_init = meta_backend_real_post_init; klass->create_cursor_renderer = meta_backend_real_create_cursor_renderer; klass->grab_device = meta_backend_real_grab_device; klass->ungrab_device = meta_backend_real_ungrab_device; klass->select_stage_events = meta_backend_real_select_stage_events; klass->is_lid_closed = meta_backend_real_is_lid_closed; signals[KEYMAP_CHANGED] = g_signal_new ("keymap-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[KEYMAP_LAYOUT_GROUP_CHANGED] = g_signal_new ("keymap-layout-group-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); signals[LAST_DEVICE_CHANGED] = g_signal_new ("last-device-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, CLUTTER_TYPE_INPUT_DEVICE); signals[LID_IS_CLOSED_CHANGED] = g_signal_new ("lid-is-closed-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); /** * MetaBackend::gpu-added: (skip) * @backend: the #MetaBackend * @gpu: the #MetaGpu */ signals[GPU_ADDED] = g_signal_new ("gpu-added", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, META_TYPE_GPU); mutter_stage_views = g_getenv ("MUTTER_STAGE_VIEWS"); stage_views_disabled = g_strcmp0 (mutter_stage_views, "0") == 0; } static MetaMonitorManager * meta_backend_create_monitor_manager (MetaBackend *backend, GError **error) { if (g_getenv ("META_DUMMY_MONITORS")) return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, NULL); return META_BACKEND_GET_CLASS (backend)->create_monitor_manager (backend, error); } static MetaRenderer * meta_backend_create_renderer (MetaBackend *backend, GError **error) { return META_BACKEND_GET_CLASS (backend)->create_renderer (backend, error); } static void prepare_for_sleep_cb (GDBusConnection *connection, const gchar *sender_name, const gchar *object_path, const gchar *interface_name, const gchar *signal_name, GVariant *parameters, gpointer user_data) { gboolean suspending; g_variant_get (parameters, "(b)", &suspending); if (suspending) return; meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ()); } static void system_bus_gotten_cb (GObject *object, GAsyncResult *res, gpointer user_data) { MetaBackendPrivate *priv; GDBusConnection *bus; bus = g_bus_get_finish (res, NULL); if (!bus) return; priv = meta_backend_get_instance_private (user_data); priv->system_bus = bus; priv->sleep_signal_id = g_dbus_connection_signal_subscribe (priv->system_bus, "org.freedesktop.login1", "org.freedesktop.login1.Manager", "PrepareForSleep", "/org/freedesktop/login1", NULL, G_DBUS_SIGNAL_FLAGS_NONE, prepare_for_sleep_cb, NULL, NULL); } #ifdef HAVE_WAYLAND MetaWaylandCompositor * meta_backend_get_wayland_compositor (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->wayland_compositor; } void meta_backend_init_wayland_display (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); priv->wayland_compositor = meta_wayland_compositor_new (backend); } void meta_backend_init_wayland (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); meta_wayland_compositor_setup (priv->wayland_compositor); } #endif /* Mutter is responsible for pulling events off the X queue, so Clutter * doesn't need (and shouldn't) run its normal event source which polls * the X fd, but we do have to deal with dispatching events that accumulate * in the clutter queue. This happens, for example, when clutter generate * enter/leave events on mouse motion - several events are queued in the * clutter queue but only one dispatched. It could also happen because of * explicit calls to clutter_event_put(). We add a very simple custom * event loop source which is simply responsible for pulling events off * of the queue and dispatching them before we block for new events. */ static gboolean clutter_source_prepare (GSource *source, int *timeout) { *timeout = -1; return clutter_events_pending (); } static gboolean clutter_source_check (GSource *source) { return clutter_events_pending (); } static gboolean clutter_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { ClutterEvent *event = clutter_event_get (); if (event) { clutter_do_event (event); clutter_event_free (event); } return TRUE; } static GSourceFuncs clutter_source_funcs = { clutter_source_prepare, clutter_source_check, clutter_source_dispatch }; static ClutterBackend * meta_get_clutter_backend (void) { MetaBackend *backend = meta_get_backend (); return meta_backend_get_clutter_backend (backend); } static gboolean init_clutter (MetaBackend *backend, GError **error) { GSource *source; clutter_set_custom_backend_func (meta_get_clutter_backend); if (clutter_init (NULL, NULL) != CLUTTER_INIT_SUCCESS) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to initialize Clutter"); return FALSE; } source = g_source_new (&clutter_source_funcs, sizeof (GSource)); g_source_attach (source, NULL); g_source_unref (source); return TRUE; } static void meta_backend_post_init (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); META_BACKEND_GET_CLASS (backend)->post_init (backend); meta_settings_post_init (priv->settings); } static gboolean meta_backend_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaBackend *backend = META_BACKEND (initable); MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); priv->settings = meta_settings_new (backend); #ifdef HAVE_EGL priv->egl = g_object_new (META_TYPE_EGL, NULL); #endif priv->orientation_manager = g_object_new (META_TYPE_ORIENTATION_MANAGER, NULL); priv->monitor_manager = meta_backend_create_monitor_manager (backend, error); if (!priv->monitor_manager) return FALSE; priv->renderer = meta_backend_create_renderer (backend, error); if (!priv->renderer) return FALSE; priv->cursor_tracker = g_object_new (META_TYPE_CURSOR_TRACKER, NULL); priv->dnd = g_object_new (META_TYPE_DND, NULL); priv->cancellable = g_cancellable_new (); g_bus_get (G_BUS_TYPE_SYSTEM, priv->cancellable, system_bus_gotten_cb, backend); #ifdef HAVE_PROFILER priv->profiler = meta_profiler_new (); #endif if (!init_clutter (backend, error)) return FALSE; meta_backend_post_init (backend); return TRUE; } static void initable_iface_init (GInitableIface *initable_iface) { initable_iface->init = meta_backend_initable_init; } static void meta_backend_init (MetaBackend *backend) { _backend = backend; } /** * meta_backend_get_idle_monitor: (skip) */ MetaIdleMonitor * meta_backend_get_idle_monitor (MetaBackend *backend, ClutterInputDevice *device) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return g_hash_table_lookup (priv->device_monitors, device); } /** * meta_backend_get_monitor_manager: (skip) */ MetaMonitorManager * meta_backend_get_monitor_manager (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->monitor_manager; } /** * meta_backend_get_orientation_manager: (skip) */ MetaOrientationManager * meta_backend_get_orientation_manager (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->orientation_manager; } MetaCursorTracker * meta_backend_get_cursor_tracker (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->cursor_tracker; } /** * meta_backend_get_cursor_renderer: (skip) */ MetaCursorRenderer * meta_backend_get_cursor_renderer (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->cursor_renderer; } /** * meta_backend_get_renderer: (skip) */ MetaRenderer * meta_backend_get_renderer (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->renderer; } #ifdef HAVE_EGL /** * meta_backend_get_egl: (skip) */ MetaEgl * meta_backend_get_egl (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->egl; } #endif /* HAVE_EGL */ /** * meta_backend_get_settings: (skip) */ MetaSettings * meta_backend_get_settings (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->settings; } #ifdef HAVE_REMOTE_DESKTOP /** * meta_backend_get_remote_desktop: (skip) */ MetaRemoteDesktop * meta_backend_get_remote_desktop (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->remote_desktop; } #endif /* HAVE_REMOTE_DESKTOP */ /** * meta_backend_get_remote_access_controller: * @backend: A #MetaBackend * * Return Value: (transfer none): The #MetaRemoteAccessController */ MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend) { #ifdef HAVE_REMOTE_DESKTOP MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->remote_access_controller; #else return NULL; #endif } /** * meta_backend_is_rendering_hardware_accelerated: * @backend: A #MetaBackend * * Returns: %TRUE if the rendering is hardware accelerated, otherwise * %FALSE. */ gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend) { MetaRenderer *renderer = meta_backend_get_renderer (backend); return meta_renderer_is_hardware_accelerated (renderer); } /** * meta_backend_grab_device: (skip) */ gboolean meta_backend_grab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { return META_BACKEND_GET_CLASS (backend)->grab_device (backend, device_id, timestamp); } /** * meta_backend_ungrab_device: (skip) */ gboolean meta_backend_ungrab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { return META_BACKEND_GET_CLASS (backend)->ungrab_device (backend, device_id, timestamp); } /** * meta_backend_finish_touch_sequence: (skip) */ void meta_backend_finish_touch_sequence (MetaBackend *backend, ClutterEventSequence *sequence, MetaSequenceState state) { if (META_BACKEND_GET_CLASS (backend)->finish_touch_sequence) META_BACKEND_GET_CLASS (backend)->finish_touch_sequence (backend, sequence, state); } MetaLogicalMonitor * meta_backend_get_current_logical_monitor (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->get_current_logical_monitor (backend); } void meta_backend_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options) { META_BACKEND_GET_CLASS (backend)->set_keymap (backend, layouts, variants, options); } /** * meta_backend_get_keymap: (skip) */ struct xkb_keymap * meta_backend_get_keymap (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->get_keymap (backend); } xkb_layout_index_t meta_backend_get_keymap_layout_group (MetaBackend *backend) { return META_BACKEND_GET_CLASS (backend)->get_keymap_layout_group (backend); } void meta_backend_lock_layout_group (MetaBackend *backend, guint idx) { META_BACKEND_GET_CLASS (backend)->lock_layout_group (backend, idx); } void meta_backend_set_numlock (MetaBackend *backend, gboolean numlock_state) { META_BACKEND_GET_CLASS (backend)->set_numlock (backend, numlock_state); } /** * meta_backend_get_stage: * @backend: A #MetaBackend * * Gets the global #ClutterStage that's managed by this backend. * * Returns: (transfer none): the #ClutterStage */ ClutterActor * meta_backend_get_stage (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->stage; } void meta_backend_freeze_updates (MetaBackend *backend) { ClutterStage *stage; stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); clutter_stage_freeze_updates (stage); } void meta_backend_thaw_updates (MetaBackend *backend) { ClutterStage *stage; stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); clutter_stage_thaw_updates (stage); } static gboolean update_last_device (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); MetaCursorTracker *cursor_tracker = priv->cursor_tracker; ClutterInputDeviceType device_type; priv->device_update_idle_id = 0; device_type = clutter_input_device_get_device_type (priv->current_device); g_signal_emit (backend, signals[LAST_DEVICE_CHANGED], 0, priv->current_device); switch (device_type) { case CLUTTER_KEYBOARD_DEVICE: break; case CLUTTER_TOUCHSCREEN_DEVICE: meta_cursor_tracker_set_pointer_visible (cursor_tracker, FALSE); break; default: meta_cursor_tracker_set_pointer_visible (cursor_tracker, TRUE); break; } return G_SOURCE_REMOVE; } void meta_backend_update_last_device (MetaBackend *backend, ClutterInputDevice *device) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); if (priv->current_device == device) return; if (!device || clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return; g_set_object (&priv->current_device, device); if (priv->device_update_idle_id == 0) { priv->device_update_idle_id = g_idle_add ((GSourceFunc) update_last_device, backend); g_source_set_name_by_id (priv->device_update_idle_id, "[mutter] update_last_device"); } } MetaPointerConstraint * meta_backend_get_client_pointer_constraint (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->client_pointer_constraint; } /** * meta_backend_set_client_pointer_constraint: * @backend: a #MetaBackend object. * @constraint: (nullable): the client constraint to follow. * * Sets the current pointer constraint and removes (and unrefs) the previous * one. If @constrant is %NULL, this means that there is no * #MetaPointerConstraint active. */ void meta_backend_set_client_pointer_constraint (MetaBackend *backend, MetaPointerConstraint *constraint) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); g_assert (!constraint || !priv->client_pointer_constraint); g_clear_object (&priv->client_pointer_constraint); if (constraint) priv->client_pointer_constraint = g_object_ref (constraint); } ClutterBackend * meta_backend_get_clutter_backend (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); if (!priv->clutter_backend) { priv->clutter_backend = META_BACKEND_GET_CLASS (backend)->create_clutter_backend (backend); } return priv->clutter_backend; } void meta_init_backend (GType backend_gtype) { MetaBackend *backend; GError *error = NULL; /* meta_backend_init() above install the backend globally so * so meta_get_backend() works even during initialization. */ backend = g_object_new (backend_gtype, NULL); if (!g_initable_init (G_INITABLE (backend), NULL, &error)) { g_warning ("Failed to create backend: %s", error->message); meta_exit (META_EXIT_ERROR); } } /** * meta_is_stage_views_enabled: * * Returns whether the #ClutterStage can be rendered using multiple stage views. * In practice, this means we can define a separate framebuffer for each * #MetaLogicalMonitor, rather than rendering everything into a single * framebuffer. For example: in X11, onle one single framebuffer is allowed. */ gboolean meta_is_stage_views_enabled (void) { if (!meta_is_wayland_compositor ()) return FALSE; return !stage_views_disabled; } gboolean meta_is_stage_views_scaled (void) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitorLayoutMode layout_mode; if (!meta_is_stage_views_enabled ()) return FALSE; layout_mode = monitor_manager->layout_mode; return layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; } MetaInputSettings * meta_backend_get_input_settings (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->input_settings; } /** * meta_backend_get_dnd: * @backend: A #MetaDnd * * Gets the global #MetaDnd that's managed by this backend. * * Returns: (transfer none): the #MetaDnd */ MetaDnd * meta_backend_get_dnd (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->dnd; } void meta_backend_notify_keymap_changed (MetaBackend *backend) { g_signal_emit (backend, signals[KEYMAP_CHANGED], 0); } void meta_backend_notify_keymap_layout_group_changed (MetaBackend *backend, unsigned int locked_group) { g_signal_emit (backend, signals[KEYMAP_LAYOUT_GROUP_CHANGED], 0, locked_group); } void meta_backend_add_gpu (MetaBackend *backend, MetaGpu *gpu) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); priv->gpus = g_list_append (priv->gpus, gpu); g_signal_emit (backend, signals[GPU_ADDED], 0, gpu); } GList * meta_backend_get_gpus (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->gpus; } #ifdef HAVE_LIBWACOM WacomDeviceDatabase * meta_backend_get_wacom_database (MetaBackend *backend) { MetaBackendPrivate *priv = meta_backend_get_instance_private (backend); return priv->wacom_db; } #endif muffin-6.4.1/src/backends/meta-monitor-manager-private.h0000664000175000017500000004164614723361714022162 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_MANAGER_PRIVATE_H #define META_MONITOR_MANAGER_PRIVATE_H #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor.h" #include "backends/meta-display-config-shared.h" #include "backends/meta-monitor-transform.h" #include "core/util-private.h" #include "meta/display.h" #include "meta/meta-monitor-manager.h" #include "meta-dbus-display-config.h" #define META_MONITOR_MANAGER_MIN_SCREEN_WIDTH 640 #define META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT 480 typedef enum _MetaMonitorManagerCapability { META_MONITOR_MANAGER_CAPABILITY_NONE = 0, META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE = (1 << 0), META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED = (1 << 1), META_MONITOR_MANAGER_CAPABILITY_TILING = (1 << 2), META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING = (1 << 3), } MetaMonitorManagerCapability; /* Equivalent to the 'method' enum in org.cinnamon.Muffin.DisplayConfig */ typedef enum _MetaMonitorsConfigMethod { META_MONITORS_CONFIG_METHOD_VERIFY = 0, META_MONITORS_CONFIG_METHOD_TEMPORARY = 1, META_MONITORS_CONFIG_METHOD_PERSISTENT = 2 } MetaMonitorsConfigMethod; /* Equivalent to the 'layout-mode' enum in org.cinnamon.Muffin.DisplayConfig */ typedef enum _MetaLogicalMonitorLayoutMode { META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL = 1, META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL = 2, META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL = 3 } MetaLogicalMonitorLayoutMode; /* * MetaCrtcInfo: * * A representation of a CRTC configuration, generated by * MetaMonitorConfigManager. */ struct _MetaCrtcInfo { MetaCrtc *crtc; MetaCrtcMode *mode; graphene_rect_t layout; float scale; MetaMonitorTransform transform; GPtrArray *outputs; }; /* * MetaOutputInfo: * * A representation of a connector configuration, generated by * MetaMonitorConfigManager. */ struct _MetaOutputInfo { MetaOutput *output; gboolean is_primary; gboolean is_presentation; gboolean is_underscanning; }; #define META_TYPE_MONITOR_MANAGER (meta_monitor_manager_get_type ()) #define META_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManager)) #define META_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass)) #define META_IS_MONITOR_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_MONITOR_MANAGER)) #define META_IS_MONITOR_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MONITOR_MANAGER)) #define META_MONITOR_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MONITOR_MANAGER, MetaMonitorManagerClass)) G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaMonitorManager, g_object_unref) struct _MetaMonitorManager { GObject parent_instance; MetaDBusDisplayConfig *display_config; MetaBackend *backend; /* XXX: this structure is very badly packed, but I like the logical organization of fields */ gboolean in_init; unsigned int serial; MetaLogicalMonitorLayoutMode layout_mode; int screen_width; int screen_height; GList *monitors; GList *logical_monitors; MetaLogicalMonitor *primary_logical_monitor; int dbus_name_id; int persistent_timeout_id; MetaMonitorConfigManager *config_manager; GnomePnpIds *pnp_ids; gulong experimental_features_changed_handler_id; MetaMonitorSwitchConfigType current_switch_config; }; /** * MetaMonitorManagerClass: * * @read_edid: Returns the raw Extended Display Identification Data (EDID) * for the given #MetaOutput object. Use meta_output_parse_edid() to parse * afterwards. * * @ensure_initial_config: Called on setup. Makes sure an initial config * is loaded. * * @apply_monitors_config: Tries to apply the given config using the given * method. Throws an error if something went wrong. * * @update_screen_size_derived: Computes the screen size for derived * configuration. * * @set_power_save_mode: Sets the #MetaPowerSave mode (for all displays). * * @change_backlight: Changes the backlight intensity to the given value (in * percent). * * @get_crtc_gamma: Queries and returns the gamma rampQueries and returns the * gamma ramp. * * @set_crtc_gamma: Sets custom display LUT (look up table) for each primary * color. Each table is indexed by a value that represents input intensity, * and yields a value that represents output intensity. * * @tiled_monitor_added: Should be called by a #MetaMonitor when it is created. * * @tiled_monitor_removed: Should be called by a #MetaMonitor when it is * destroyed. * * @is_transform_handled: vfunc for * meta_monitor_manager_is_transform_handled(). * @calculate_monitor_mode_scale: vfunc for * meta_monitor_manager_calculate_monitor_mode_scale(). * @calculate_supported_scales: vfunc for * meta_monitor_manager_calculate_supported_scales(). * @get_capabilities: vfunc for meta_monitor_manager_get_capabilities(). * @get_max_screen_size: vfunc for meta_monitor_manager_get_max_screen_size(). * @get_default_layout_mode: vfunc for meta_monitor_manager_get_default_layout_mode(). * * The base class for a #MetaMonitorManager. */ struct _MetaMonitorManagerClass { MetaDBusDisplayConfigSkeletonClass parent_class; GBytes* (*read_edid) (MetaMonitorManager *, MetaOutput *); void (*read_current_state) (MetaMonitorManager *); void (*ensure_initial_config) (MetaMonitorManager *); gboolean (*apply_monitors_config) (MetaMonitorManager *, MetaMonitorsConfig *, MetaMonitorsConfigMethod , GError **); void (*update_screen_size_derived) (MetaMonitorManager *, MetaMonitorsConfig *); void (*set_power_save_mode) (MetaMonitorManager *, MetaPowerSave); void (*change_backlight) (MetaMonitorManager *, MetaOutput *, int); void (*get_crtc_gamma) (MetaMonitorManager *, MetaCrtc *, gsize *, unsigned short **, unsigned short **, unsigned short **); void (*set_crtc_gamma) (MetaMonitorManager *, MetaCrtc *, gsize , unsigned short *, unsigned short *, unsigned short *); void (*tiled_monitor_added) (MetaMonitorManager *, MetaMonitor *); void (*tiled_monitor_removed) (MetaMonitorManager *, MetaMonitor *); gboolean (*is_transform_handled) (MetaMonitorManager *, MetaCrtc *, MetaMonitorTransform); float (*calculate_monitor_mode_scale) (MetaMonitorManager *, MetaLogicalMonitorLayoutMode , MetaMonitor *, MetaMonitorMode *); float * (*calculate_supported_scales) (MetaMonitorManager *, MetaLogicalMonitorLayoutMode , MetaMonitor *, MetaMonitorMode *, int *); MetaMonitorManagerCapability (*get_capabilities) (MetaMonitorManager *); gboolean (*get_max_screen_size) (MetaMonitorManager *, int *, int *); MetaLogicalMonitorLayoutMode (*get_default_layout_mode) (MetaMonitorManager *); }; META_EXPORT_TEST MetaBackend * meta_monitor_manager_get_backend (MetaMonitorManager *manager); void meta_monitor_manager_setup (MetaMonitorManager *manager); META_EXPORT_TEST void meta_monitor_manager_rebuild (MetaMonitorManager *manager, MetaMonitorsConfig *config); META_EXPORT_TEST void meta_monitor_manager_rebuild_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config); META_EXPORT_TEST int meta_monitor_manager_get_num_logical_monitors (MetaMonitorManager *manager); META_EXPORT_TEST GList * meta_monitor_manager_get_logical_monitors (MetaMonitorManager *manager); MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_from_number (MetaMonitorManager *manager, int number); MetaLogicalMonitor *meta_monitor_manager_get_primary_logical_monitor (MetaMonitorManager *manager); MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_at (MetaMonitorManager *manager, float x, float y); MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_from_rect (MetaMonitorManager *manager, MetaRectangle *rect); MetaLogicalMonitor *meta_monitor_manager_get_logical_monitor_neighbor (MetaMonitorManager *manager, MetaLogicalMonitor *logical_monitor, MetaDisplayDirection direction); MetaMonitor * meta_monitor_manager_get_primary_monitor (MetaMonitorManager *manager); MetaMonitor * meta_monitor_manager_get_laptop_panel (MetaMonitorManager *manager); MetaMonitor * meta_monitor_manager_get_monitor_from_spec (MetaMonitorManager *manager, MetaMonitorSpec *monitor_spec); MetaMonitor * meta_monitor_manager_get_monitor_from_connector (MetaMonitorManager *manager, const char *connector); META_EXPORT_TEST GList * meta_monitor_manager_get_monitors (MetaMonitorManager *manager); void meta_monitor_manager_get_screen_size (MetaMonitorManager *manager, int *width, int *height); MetaPowerSave meta_monitor_manager_get_power_save_mode (MetaMonitorManager *manager); void meta_monitor_manager_power_save_mode_changed (MetaMonitorManager *manager, MetaPowerSave mode); void meta_monitor_manager_confirm_configuration (MetaMonitorManager *manager, gboolean ok); void meta_output_parse_edid (MetaOutput *output, GBytes *edid); gboolean meta_output_is_laptop (MetaOutput *output); gboolean meta_monitor_manager_has_hotplug_mode_update (MetaMonitorManager *manager); META_EXPORT_TEST void meta_monitor_manager_read_current_state (MetaMonitorManager *manager); META_EXPORT_TEST void meta_monitor_manager_on_hotplug (MetaMonitorManager *manager); gboolean meta_monitor_manager_get_monitor_matrix (MetaMonitorManager *manager, MetaMonitor *monitor, MetaLogicalMonitor *logical_monitor, gfloat matrix[6]); void meta_monitor_manager_tiled_monitor_added (MetaMonitorManager *manager, MetaMonitor *monitor); void meta_monitor_manager_tiled_monitor_removed (MetaMonitorManager *manager, MetaMonitor *monitor); gboolean meta_monitor_manager_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform); META_EXPORT_TEST MetaMonitorsConfig * meta_monitor_manager_ensure_configured (MetaMonitorManager *manager); META_EXPORT_TEST void meta_monitor_manager_update_logical_state (MetaMonitorManager *manager, MetaMonitorsConfig *config); META_EXPORT_TEST void meta_monitor_manager_update_logical_state_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config); META_EXPORT_TEST void meta_monitor_manager_lid_is_closed_changed (MetaMonitorManager *manager); gboolean meta_monitor_manager_is_headless (MetaMonitorManager *manager); float meta_monitor_manager_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode); float * meta_monitor_manager_calculate_supported_scales (MetaMonitorManager *, MetaLogicalMonitorLayoutMode , MetaMonitor *, MetaMonitorMode *, int *); gboolean meta_monitor_manager_is_scale_supported (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, float scale); float meta_monitor_manager_get_maximum_crtc_scale (MetaMonitorManager *manager); MetaMonitorManagerCapability meta_monitor_manager_get_capabilities (MetaMonitorManager *manager); gboolean meta_monitor_manager_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height); MetaLogicalMonitorLayoutMode meta_monitor_manager_get_default_layout_mode (MetaMonitorManager *manager); MetaMonitorConfigManager * meta_monitor_manager_get_config_manager (MetaMonitorManager *manager); void meta_monitor_manager_rotate_monitor (MetaMonitorManager *manager); void meta_monitor_manager_clear_output (MetaOutput *output); void meta_monitor_manager_clear_mode (MetaCrtcMode *mode); void meta_monitor_manager_clear_crtc (MetaCrtc *crtc); gboolean meta_monitor_has_aspect_as_size (MetaMonitor *monitor); char * meta_monitor_manager_get_vendor_name (MetaMonitorManager *manager, const char *vendor); #endif /* META_MONITOR_MANAGER_PRIVATE_H */ muffin-6.4.1/src/backends/meta-screen-cast-monitor-stream-src.h0000664000175000017500000000325114723361714023353 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_MONITOR_STREAM_SRC_H #define META_SCREEN_CAST_MONITOR_STREAM_SRC_H #include "backends/meta-monitor-manager-private.h" #include "backends/meta-screen-cast-stream-src.h" typedef struct _MetaScreenCastMonitorStream MetaScreenCastMonitorStream; #define META_TYPE_SCREEN_CAST_MONITOR_STREAM_SRC (meta_screen_cast_monitor_stream_src_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStreamSrc, meta_screen_cast_monitor_stream_src, META, SCREEN_CAST_MONITOR_STREAM_SRC, MetaScreenCastStreamSrc) MetaScreenCastMonitorStreamSrc * meta_screen_cast_monitor_stream_src_new (MetaScreenCastMonitorStream *monitor_stream, GError **error); #endif /* META_SCREEN_CAST_MONITOR_STREAM_SRC_H */ muffin-6.4.1/src/backends/meta-monitor-config-migration.h0000664000175000017500000000336514723361714022330 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_MONITOR_CONFIG_MIGRATION_H #define META_MONITOR_CONFIG_MIGRATION_H #include "backends/meta-monitor-manager-private.h" META_EXPORT_TEST gboolean meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store, GFile *in_file, GError **error); META_EXPORT_TEST gboolean meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store, const gchar *user_file_path, GError **error); META_EXPORT_TEST gboolean meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, MetaMonitorsConfig *config, GError **error); #endif /* META_MONITOR_CONFIG_MIGRATION_H */ muffin-6.4.1/src/backends/meta-monitor-transform.c0000664000175000017500000000772014723361714021101 0ustar fabiofabio/* * Copyright (C) 2018 Robert Mader * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-monitor-transform.h" MetaMonitorTransform meta_monitor_transform_invert (MetaMonitorTransform transform) { switch (transform) { case META_MONITOR_TRANSFORM_90: return META_MONITOR_TRANSFORM_270; case META_MONITOR_TRANSFORM_270: return META_MONITOR_TRANSFORM_90; case META_MONITOR_TRANSFORM_NORMAL: case META_MONITOR_TRANSFORM_180: case META_MONITOR_TRANSFORM_FLIPPED: case META_MONITOR_TRANSFORM_FLIPPED_90: case META_MONITOR_TRANSFORM_FLIPPED_180: case META_MONITOR_TRANSFORM_FLIPPED_270: return transform; } g_assert_not_reached (); return 0; } MetaMonitorTransform meta_monitor_transform_transform (MetaMonitorTransform transform, MetaMonitorTransform other) { MetaMonitorTransform new_transform; new_transform = (transform + other) % META_MONITOR_TRANSFORM_FLIPPED; if (meta_monitor_transform_is_flipped (transform) != meta_monitor_transform_is_flipped (other)) new_transform += META_MONITOR_TRANSFORM_FLIPPED; return new_transform; } /** * meta_monitor_transform_relative_transform: * @transform: The transform to start from * @other: The transform to go to * * Return value: a transform to get from @transform to @other */ MetaMonitorTransform meta_monitor_transform_relative_transform (MetaMonitorTransform transform, MetaMonitorTransform other) { MetaMonitorTransform relative_transform; relative_transform = ((other % META_MONITOR_TRANSFORM_FLIPPED - transform % META_MONITOR_TRANSFORM_FLIPPED) % META_MONITOR_TRANSFORM_FLIPPED); if (meta_monitor_transform_is_flipped (transform) != meta_monitor_transform_is_flipped (other)) { relative_transform = (meta_monitor_transform_invert (relative_transform) + META_MONITOR_TRANSFORM_FLIPPED); } return relative_transform; } void meta_monitor_transform_transform_point (MetaMonitorTransform transform, int area_width, int area_height, int x, int y, int *out_x, int *out_y) { switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: *out_x = x; *out_y = y; break; case META_MONITOR_TRANSFORM_90: *out_x = area_width - y; *out_y = x; break; case META_MONITOR_TRANSFORM_180: *out_x = area_width - x; *out_y = area_height - y; break; case META_MONITOR_TRANSFORM_270: *out_x = y, *out_y = area_height - x; break; case META_MONITOR_TRANSFORM_FLIPPED: *out_x = area_width - x; *out_y = y; break; case META_MONITOR_TRANSFORM_FLIPPED_90: *out_x = area_width - y; *out_y = area_height - x; break; case META_MONITOR_TRANSFORM_FLIPPED_180: *out_x = x; *out_y = area_height - y; break; case META_MONITOR_TRANSFORM_FLIPPED_270: *out_x = y; *out_y = x; break; } } muffin-6.4.1/src/backends/meta-remote-desktop-session.h0000664000175000017500000000467214723361714022034 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_REMOTE_DESKTOP_SESSION_H #define META_REMOTE_DESKTOP_SESSION_H #include #include "backends/meta-remote-desktop.h" #include "backends/meta-screen-cast-session.h" #define META_TYPE_REMOTE_DESKTOP_SESSION (meta_remote_desktop_session_get_type ()) G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSession, meta_remote_desktop_session, META, REMOTE_DESKTOP_SESSION, MetaDBusRemoteDesktopSessionSkeleton) #define META_TYPE_REMOTE_DESKTOP_SESSION_HANDLE (meta_remote_desktop_session_handle_get_type ()) G_DECLARE_FINAL_TYPE (MetaRemoteDesktopSessionHandle, meta_remote_desktop_session_handle, META, REMOTE_DESKTOP_SESSION_HANDLE, MetaRemoteAccessHandle) char * meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session); char * meta_remote_desktop_session_get_session_id (MetaRemoteDesktopSession *session); gboolean meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSession *session, MetaScreenCastSession *screen_cast_session, GError **error); void meta_remote_desktop_session_close (MetaRemoteDesktopSession *session); MetaRemoteDesktopSession * meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop, const char *peer_name, GError **error); #endif /* META_REMOTE_DESKTOP_SESSION_H */ muffin-6.4.1/src/backends/meta-idle-monitor-dbus.h0000664000175000017500000000176114723361714020742 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Adapted from gnome-session/gnome-session/gs-idle-monitor.c and * from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c */ #ifndef META_IDLE_MONITOR_DBUS_H #define META_IDLE_MONITOR_DBUS_H void meta_idle_monitor_init_dbus (void); #endif muffin-6.4.1/src/backends/meta-pointer-constraint.c0000664000175000017500000000577014723361714021246 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:meta-pointer-constraint * @title: MetaPointerConstraint * @short_description: Pointer client constraints. * * A MetaPointerConstraint can be used to implement any kind of pointer * constraint as requested by a client, such as cursor lock. * * Examples of pointer constraints are "pointer confinement" and "pointer * locking" (as defined in the wayland pointer constraint protocol extension), * which restrict movement in relation to a given client. */ #include "config.h" #include "backends/meta-pointer-constraint.h" #include G_DEFINE_TYPE (MetaPointerConstraint, meta_pointer_constraint, G_TYPE_OBJECT); static void meta_pointer_constraint_init (MetaPointerConstraint *constraint) { } static void meta_pointer_constraint_class_init (MetaPointerConstraintClass *klass) { } /** * meta_pointer_constraint_constrain: * @constraint: a #MetaPointerConstraint. * @device; the device of the pointer. * @time: the timestamp (in ms) of the event. * @prev_x: X-coordinate of the previous pointer position. * @prev_y: Y-coordinate of the previous pointer position. * @x: The modifiable X-coordinate to which the pointer would like to go to. * @y: The modifiable Y-coordinate to which the pointer would like to go to. * * Constrains the pointer movement from point (@prev_x, @prev_y) to (@x, @y), * if needed. */ void meta_pointer_constraint_constrain (MetaPointerConstraint *constraint, ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y) { META_POINTER_CONSTRAINT_GET_CLASS (constraint)->constrain (constraint, device, time, prev_x, prev_y, x, y); } muffin-6.4.1/src/backends/meta-barrier-private.h0000664000175000017500000000374614723361714020510 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014-2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre * Jonas Ådahl */ #ifndef META_BARRIER_PRIVATE_H #define META_BARRIER_PRIVATE_H #include "core/meta-border.h" #include "meta/barrier.h" G_BEGIN_DECLS #define META_TYPE_BARRIER_IMPL (meta_barrier_impl_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaBarrierImpl, meta_barrier_impl, META, BARRIER_IMPL, GObject) struct _MetaBarrierImplClass { GObjectClass parent_class; gboolean (*is_active) (MetaBarrierImpl *barrier); void (*release) (MetaBarrierImpl *barrier, MetaBarrierEvent *event); void (*destroy) (MetaBarrierImpl *barrier); }; void _meta_barrier_emit_hit_signal (MetaBarrier *barrier, MetaBarrierEvent *event); void _meta_barrier_emit_left_signal (MetaBarrier *barrier, MetaBarrierEvent *event); void meta_barrier_event_unref (MetaBarrierEvent *event); G_END_DECLS struct _MetaBarrierPrivate { MetaDisplay *display; MetaBorder border; MetaBarrierImpl *impl; }; #endif /* META_BARRIER_PRIVATE_H */ muffin-6.4.1/src/backends/meta-cursor-renderer.c0000664000175000017500000002666214723361714020530 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "backends/meta-cursor-renderer.h" #include #include "backends/meta-stage-private.h" #include "clutter/clutter.h" #include "clutter/clutter-mutter.h" #include "cogl/cogl.h" #include "meta/meta-backend.h" #include "meta/util.h" G_DEFINE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor, G_TYPE_OBJECT) struct _MetaCursorRendererPrivate { float current_x; float current_y; MetaCursorSprite *displayed_cursor; MetaOverlay *stage_overlay; gboolean handled_by_backend; guint post_paint_func_id; GList *hw_cursor_inhibitors; }; typedef struct _MetaCursorRendererPrivate MetaCursorRendererPrivate; enum { CURSOR_PAINTED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRenderer, meta_cursor_renderer, G_TYPE_OBJECT); static gboolean meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (MetaHwCursorInhibitor *inhibitor, MetaCursorSprite *cursor_sprite) { MetaHwCursorInhibitorInterface *iface = META_HW_CURSOR_INHIBITOR_GET_IFACE (inhibitor); return iface->is_cursor_sprite_inhibited (inhibitor, cursor_sprite); } static void meta_hw_cursor_inhibitor_default_init (MetaHwCursorInhibitorInterface *iface) { } void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { g_signal_emit (renderer, signals[CURSOR_PAINTED], 0, cursor_sprite); } static void align_cursor_position (MetaCursorRenderer *renderer, graphene_rect_t *rect) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); MetaBackend *backend = meta_get_backend (); ClutterActor *stage = meta_backend_get_stage (backend); ClutterStageView *view; cairo_rectangle_int_t view_layout; float view_scale; view = clutter_stage_get_view_at (CLUTTER_STAGE (stage), priv->current_x, priv->current_y); if (!view) return; clutter_stage_view_get_layout (view, &view_layout); view_scale = clutter_stage_view_get_scale (view); graphene_rect_offset (rect, -view_layout.x, -view_layout.y); rect->origin.x = floorf (rect->origin.x * view_scale) / view_scale; rect->origin.y = floorf (rect->origin.y * view_scale) / view_scale; graphene_rect_offset (rect, view_layout.x, view_layout.y); } static void queue_redraw (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); MetaBackend *backend = meta_get_backend (); ClutterActor *stage = meta_backend_get_stage (backend); CoglTexture *texture; graphene_rect_t rect = GRAPHENE_RECT_INIT_ZERO; /* During early initialization, we can have no stage */ if (!stage) return; if (cursor_sprite) { rect = meta_cursor_renderer_calculate_rect (renderer, cursor_sprite); align_cursor_position (renderer, &rect); } if (!priv->stage_overlay) priv->stage_overlay = meta_stage_create_cursor_overlay (META_STAGE (stage)); if (cursor_sprite && !priv->handled_by_backend) texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); else texture = NULL; meta_stage_update_cursor_overlay (META_STAGE (stage), priv->stage_overlay, texture, &rect); } static gboolean meta_cursor_renderer_post_paint (gpointer data) { MetaCursorRenderer *renderer = META_CURSOR_RENDERER (data); MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); if (priv->displayed_cursor && !priv->handled_by_backend) meta_cursor_renderer_emit_painted (renderer, priv->displayed_cursor); return TRUE; } static gboolean meta_cursor_renderer_real_update_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { if (cursor_sprite) meta_cursor_sprite_realize_texture (cursor_sprite); return FALSE; } static void meta_cursor_renderer_finalize (GObject *object) { MetaCursorRenderer *renderer = META_CURSOR_RENDERER (object); MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); MetaBackend *backend = meta_get_backend (); ClutterActor *stage = meta_backend_get_stage (backend); if (priv->stage_overlay) meta_stage_remove_cursor_overlay (META_STAGE (stage), priv->stage_overlay); clutter_threads_remove_repaint_func (priv->post_paint_func_id); G_OBJECT_CLASS (meta_cursor_renderer_parent_class)->finalize (object); } static void meta_cursor_renderer_class_init (MetaCursorRendererClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_cursor_renderer_finalize; klass->update_cursor = meta_cursor_renderer_real_update_cursor; signals[CURSOR_PAINTED] = g_signal_new ("cursor-painted", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_POINTER); } static void meta_cursor_renderer_init (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); priv->post_paint_func_id = clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, meta_cursor_renderer_post_paint, renderer, NULL); } graphene_rect_t meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); CoglTexture *texture; int hot_x, hot_y; int width, height; float texture_scale; texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!texture) return (graphene_rect_t) GRAPHENE_RECT_INIT_ZERO; meta_cursor_sprite_get_hotspot (cursor_sprite, &hot_x, &hot_y); texture_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite); width = cogl_texture_get_width (texture); height = cogl_texture_get_height (texture); return (graphene_rect_t) { .origin = { .x = priv->current_x - (hot_x * texture_scale), .y = priv->current_y - (hot_y * texture_scale) }, .size = { .width = width * texture_scale, .height = height * texture_scale } }; } static void meta_cursor_renderer_update_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); gboolean handled_by_backend; gboolean should_redraw = FALSE; if (cursor_sprite) meta_cursor_sprite_prepare_at (cursor_sprite, (int) priv->current_x, (int) priv->current_y); handled_by_backend = META_CURSOR_RENDERER_GET_CLASS (renderer)->update_cursor (renderer, cursor_sprite); if (handled_by_backend != priv->handled_by_backend) { priv->handled_by_backend = handled_by_backend; should_redraw = TRUE; } if (!handled_by_backend) should_redraw = TRUE; if (should_redraw) queue_redraw (renderer, cursor_sprite); } MetaCursorRenderer * meta_cursor_renderer_new (void) { return g_object_new (META_TYPE_CURSOR_RENDERER, NULL); } void meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); if (priv->displayed_cursor == cursor_sprite) return; priv->displayed_cursor = cursor_sprite; meta_cursor_renderer_update_cursor (renderer, cursor_sprite); } void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); meta_cursor_renderer_update_cursor (renderer, priv->displayed_cursor); } void meta_cursor_renderer_set_position (MetaCursorRenderer *renderer, float x, float y) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); g_assert (meta_is_wayland_compositor ()); priv->current_x = x; priv->current_y = y; meta_cursor_renderer_update_cursor (renderer, priv->displayed_cursor); } graphene_point_t meta_cursor_renderer_get_position (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); return (graphene_point_t) { .x = priv->current_x, .y = priv->current_y }; } MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); return priv->displayed_cursor; } void meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer *renderer, MetaHwCursorInhibitor *inhibitor) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); priv->hw_cursor_inhibitors = g_list_prepend (priv->hw_cursor_inhibitors, inhibitor); } void meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer *renderer, MetaHwCursorInhibitor *inhibitor) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); priv->hw_cursor_inhibitors = g_list_remove (priv->hw_cursor_inhibitors, inhibitor); } gboolean meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererPrivate *priv = meta_cursor_renderer_get_instance_private (renderer); GList *l; for (l = priv->hw_cursor_inhibitors; l; l = l->next) { MetaHwCursorInhibitor *inhibitor = l->data; if (meta_hw_cursor_inhibitor_is_cursor_sprite_inhibited (inhibitor, cursor_sprite)) return TRUE; } return FALSE; } muffin-6.4.1/src/backends/meta-monitor-config-manager.c0000664000175000017500000020063614723361714021744 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * Copyright (c) 2018 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-backend-private.h" #include "backends/meta-monitor-config-migration.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-output.h" #include "core/boxes-private.h" #define CONFIG_HISTORY_MAX_SIZE 3 struct _MetaMonitorConfigManager { GObject parent; MetaMonitorManager *monitor_manager; MetaMonitorConfigStore *config_store; MetaMonitorsConfig *current_config; GQueue config_history; }; G_DEFINE_TYPE (MetaMonitorConfigManager, meta_monitor_config_manager, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaMonitorsConfig, meta_monitors_config, G_TYPE_OBJECT) static void meta_crtc_info_free (MetaCrtcInfo *info); static void meta_output_info_free (MetaOutputInfo *info); MetaMonitorConfigManager * meta_monitor_config_manager_new (MetaMonitorManager *monitor_manager) { MetaMonitorConfigManager *config_manager; config_manager = g_object_new (META_TYPE_MONITOR_CONFIG_MANAGER, NULL); config_manager->monitor_manager = monitor_manager; config_manager->config_store = meta_monitor_config_store_new (monitor_manager); return config_manager; } MetaMonitorConfigStore * meta_monitor_config_manager_get_store (MetaMonitorConfigManager *config_manager) { return config_manager->config_store; } static gboolean is_crtc_reserved (MetaCrtc *crtc, GArray *reserved_crtcs) { unsigned int i; for (i = 0; i < reserved_crtcs->len; i++) { glong id = g_array_index (reserved_crtcs, glong, i); if (id == crtc->crtc_id) return TRUE; } return FALSE; } static gboolean is_crtc_assigned (MetaCrtc *crtc, GPtrArray *crtc_infos) { unsigned int i; for (i = 0; i < crtc_infos->len; i++) { MetaCrtcInfo *assigned_crtc_info = g_ptr_array_index (crtc_infos, i); if (assigned_crtc_info->crtc == crtc) return TRUE; } return FALSE; } static MetaCrtc * find_unassigned_crtc (MetaOutput *output, GPtrArray *crtc_infos, GArray *reserved_crtcs) { MetaCrtc *crtc; unsigned int i; crtc = meta_output_get_assigned_crtc (output); if (crtc && !is_crtc_assigned (crtc, crtc_infos)) return crtc; /* then try to assign a CRTC that wasn't used */ for (i = 0; i < output->n_possible_crtcs; i++) { crtc = output->possible_crtcs[i]; if (is_crtc_assigned (crtc, crtc_infos)) continue; if (is_crtc_reserved (crtc, reserved_crtcs)) continue; return crtc; } /* finally just give a CRTC that we haven't assigned */ for (i = 0; i < output->n_possible_crtcs; i++) { crtc = output->possible_crtcs[i]; if (is_crtc_assigned (crtc, crtc_infos)) continue; return crtc; } return NULL; } typedef struct { MetaMonitorManager *monitor_manager; MetaMonitorsConfig *config; MetaLogicalMonitorConfig *logical_monitor_config; MetaMonitorConfig *monitor_config; GPtrArray *crtc_infos; GPtrArray *output_infos; GArray *reserved_crtcs; } MonitorAssignmentData; static gboolean assign_monitor_crtc (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { MonitorAssignmentData *data = user_data; MetaOutput *output; MetaCrtc *crtc; MetaMonitorTransform transform; MetaMonitorTransform crtc_transform; MetaMonitorTransform crtc_hw_transform; int crtc_x, crtc_y; float x_offset, y_offset; float scale = 0.0; float width, height; MetaCrtcMode *crtc_mode; graphene_rect_t crtc_layout; MetaCrtcInfo *crtc_info; MetaOutputInfo *output_info; MetaMonitorConfig *first_monitor_config; gboolean assign_output_as_primary; gboolean assign_output_as_presentation; output = monitor_crtc_mode->output; crtc = find_unassigned_crtc (output, data->crtc_infos, data->reserved_crtcs); if (!crtc) { MetaMonitorSpec *monitor_spec = meta_monitor_get_spec (monitor); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No available CRTC for monitor '%s %s' not found", monitor_spec->vendor, monitor_spec->product); return FALSE; } transform = data->logical_monitor_config->transform; crtc_transform = meta_monitor_logical_to_crtc_transform (monitor, transform); if (meta_monitor_manager_is_transform_handled (data->monitor_manager, crtc, crtc_transform)) crtc_hw_transform = crtc_transform; else crtc_hw_transform = META_MONITOR_TRANSFORM_NORMAL; scale = data->logical_monitor_config->scale; if (!meta_monitor_manager_is_scale_supported (data->monitor_manager, data->config->layout_mode, monitor, mode, scale)) { scale = roundf (scale); if (!meta_monitor_manager_is_scale_supported (data->monitor_manager, data->config->layout_mode, monitor, mode, scale)) scale = 1.0f; } meta_monitor_calculate_crtc_pos (monitor, mode, output, crtc_transform, &crtc_x, &crtc_y); x_offset = data->logical_monitor_config->layout.x; y_offset = data->logical_monitor_config->layout.y; switch (data->config->layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: scale = data->logical_monitor_config->scale; break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: scale = 1.0; break; case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; } crtc_mode = monitor_crtc_mode->crtc_mode; if (meta_monitor_transform_is_rotated (crtc_transform)) { width = crtc_mode->height / scale; height = crtc_mode->width / scale; } else { width = crtc_mode->width / scale; height = crtc_mode->height / scale; } crtc_layout = GRAPHENE_RECT_INIT (x_offset + (crtc_x / scale), y_offset + (crtc_y / scale), width, height); crtc_info = g_slice_new0 (MetaCrtcInfo); *crtc_info = (MetaCrtcInfo) { .crtc = crtc, .mode = crtc_mode, .layout = crtc_layout, .transform = crtc_hw_transform, .scale = scale, .outputs = g_ptr_array_new () }; g_ptr_array_add (crtc_info->outputs, output); /* * Only one output can be marked as primary (due to Xrandr limitation), * so only mark the main output of the first monitor in the logical monitor * as such. */ first_monitor_config = data->logical_monitor_config->monitor_configs->data; if (data->logical_monitor_config->is_primary && data->monitor_config == first_monitor_config && meta_monitor_get_main_output (monitor) == output) assign_output_as_primary = TRUE; else assign_output_as_primary = FALSE; if (data->logical_monitor_config->is_presentation) assign_output_as_presentation = TRUE; else assign_output_as_presentation = FALSE; output_info = g_slice_new0 (MetaOutputInfo); *output_info = (MetaOutputInfo) { .output = output, .is_primary = assign_output_as_primary, .is_presentation = assign_output_as_presentation, .is_underscanning = data->monitor_config->enable_underscanning }; g_ptr_array_add (data->crtc_infos, crtc_info); g_ptr_array_add (data->output_infos, output_info); return TRUE; } static gboolean assign_monitor_crtcs (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaLogicalMonitorConfig *logical_monitor_config, MetaMonitorConfig *monitor_config, GPtrArray *crtc_infos, GPtrArray *output_infos, GArray *reserved_crtcs, GError **error) { MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec; MetaMonitorModeSpec *monitor_mode_spec = monitor_config->mode_spec; MetaMonitor *monitor; MetaMonitorMode *monitor_mode; MonitorAssignmentData data; monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec); if (!monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Configured monitor '%s %s' not found", monitor_spec->vendor, monitor_spec->product); return FALSE; } monitor_mode = meta_monitor_get_mode_from_spec (monitor, monitor_mode_spec); if (!monitor_mode) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid mode %dx%d (%f) for monitor '%s %s'", monitor_mode_spec->width, monitor_mode_spec->height, monitor_mode_spec->refresh_rate, monitor_spec->vendor, monitor_spec->product); return FALSE; } data = (MonitorAssignmentData) { .monitor_manager = manager, .config = config, .logical_monitor_config = logical_monitor_config, .monitor_config = monitor_config, .crtc_infos = crtc_infos, .output_infos = output_infos, .reserved_crtcs = reserved_crtcs }; if (!meta_monitor_mode_foreach_crtc (monitor, monitor_mode, assign_monitor_crtc, &data, error)) return FALSE; return TRUE; } static gboolean assign_logical_monitor_crtcs (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaLogicalMonitorConfig *logical_monitor_config, GPtrArray *crtc_infos, GPtrArray *output_infos, GArray *reserved_crtcs, GError **error) { GList *l; for (l = logical_monitor_config->monitor_configs; l; l = l->next) { MetaMonitorConfig *monitor_config = l->data; if (!assign_monitor_crtcs (manager, config, logical_monitor_config, monitor_config, crtc_infos, output_infos, reserved_crtcs, error)) return FALSE; } return TRUE; } gboolean meta_monitor_config_manager_assign (MetaMonitorManager *manager, MetaMonitorsConfig *config, GPtrArray **out_crtc_infos, GPtrArray **out_output_infos, GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; GArray *reserved_crtcs; GList *l; crtc_infos = g_ptr_array_new_with_free_func ((GDestroyNotify) meta_crtc_info_free); output_infos = g_ptr_array_new_with_free_func ((GDestroyNotify) meta_output_info_free); reserved_crtcs = g_array_new (FALSE, FALSE, sizeof (glong)); for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; GList *k; for (k = logical_monitor_config->monitor_configs; k; k = k->next) { MetaMonitorConfig *monitor_config = k->data; MetaMonitorSpec *monitor_spec = monitor_config->monitor_spec; MetaMonitor *monitor; GList *o; monitor = meta_monitor_manager_get_monitor_from_spec (manager, monitor_spec); for (o = meta_monitor_get_outputs (monitor); o; o = o->next) { MetaOutput *output = o->data; MetaCrtc *crtc; crtc = meta_output_get_assigned_crtc (output); if (crtc) g_array_append_val (reserved_crtcs, crtc->crtc_id); } } } for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (!assign_logical_monitor_crtcs (manager, config, logical_monitor_config, crtc_infos, output_infos, reserved_crtcs, error)) { g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); g_array_free (reserved_crtcs, TRUE); return FALSE; } } g_array_free (reserved_crtcs, TRUE); *out_crtc_infos = crtc_infos; *out_output_infos = output_infos; return TRUE; } static gboolean is_lid_closed (MetaMonitorManager *monitor_manager) { MetaBackend *backend; backend = meta_monitor_manager_get_backend (monitor_manager); return meta_backend_is_lid_closed (backend); } MetaMonitorsConfigKey * meta_create_monitors_config_key_for_current_state (MetaMonitorManager *monitor_manager) { MetaMonitorsConfigKey *config_key; MetaMonitorSpec *laptop_monitor_spec; GList *l; GList *monitor_specs; laptop_monitor_spec = NULL; monitor_specs = NULL; for (l = monitor_manager->monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorSpec *monitor_spec; if (meta_monitor_is_laptop_panel (monitor)) { laptop_monitor_spec = meta_monitor_get_spec (monitor); if (is_lid_closed (monitor_manager)) continue; } monitor_spec = meta_monitor_spec_clone (meta_monitor_get_spec (monitor)); monitor_specs = g_list_prepend (monitor_specs, monitor_spec); } if (!monitor_specs && laptop_monitor_spec) { monitor_specs = g_list_prepend (NULL, meta_monitor_spec_clone (laptop_monitor_spec)); } if (!monitor_specs) return NULL; monitor_specs = g_list_sort (monitor_specs, (GCompareFunc) meta_monitor_spec_compare); config_key = g_new0 (MetaMonitorsConfigKey, 1); *config_key = (MetaMonitorsConfigKey) { .monitor_specs = monitor_specs }; return config_key; } MetaMonitorsConfig * meta_monitor_config_manager_get_stored (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaMonitorsConfigKey *config_key; MetaMonitorsConfig *config; GError *error = NULL; config_key = meta_create_monitors_config_key_for_current_state (monitor_manager); if (!config_key) return NULL; config = meta_monitor_config_store_lookup (config_manager->config_store, config_key); meta_monitors_config_key_free (config_key); if (!config) return NULL; if (config->flags & META_MONITORS_CONFIG_FLAG_MIGRATED) { if (!meta_finish_monitors_config_migration (monitor_manager, config, &error)) { g_warning ("Failed to finish monitors config migration: %s", error->message); g_error_free (error); meta_monitor_config_store_remove (config_manager->config_store, config); return NULL; } } return config; } typedef enum _MonitorMatchRule { MONITOR_MATCH_ALL = 0, MONITOR_MATCH_EXTERNAL = (1 << 0), MONITOR_MATCH_BUILTIN = (1 << 1), MONITOR_MATCH_PRIMARY = (1 << 2), MONITOR_MATCH_VISIBLE = (1 << 3), MONITOR_MATCH_WITH_POSITION = (1 << 4), } MonitorMatchRule; static MetaMonitor * find_monitor_with_highest_preferred_resolution (MetaMonitorManager *monitor_manager, MonitorMatchRule match_rule) { GList *monitors; GList *l; int largest_area = 0; MetaMonitor *largest_monitor = NULL; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorMode *mode; int width, height; int area; if (match_rule & MONITOR_MATCH_EXTERNAL) { if (meta_monitor_is_laptop_panel (monitor)) continue; } mode = meta_monitor_get_preferred_mode (monitor); meta_monitor_mode_get_resolution (mode, &width, &height); area = width * height; if (area > largest_area) { largest_area = area; largest_monitor = monitor; } } return largest_monitor; } /* * Try to find the primary monitor. The priority of classification is: * * 1. Find the primary monitor as reported by the underlying system, * 2. Find the laptop panel * 3. Find the external monitor with highest resolution * * If the laptop lid is closed, exclude the laptop panel from possible * alternatives, except if no other alternatives exist. */ static MetaMonitor * find_primary_monitor (MetaMonitorManager *monitor_manager) { MetaMonitor *monitor; if (is_lid_closed (monitor_manager)) { monitor = meta_monitor_manager_get_primary_monitor (monitor_manager); if (monitor && !meta_monitor_is_laptop_panel (monitor)) return monitor; monitor = find_monitor_with_highest_preferred_resolution (monitor_manager, MONITOR_MATCH_EXTERNAL); if (monitor) return monitor; return find_monitor_with_highest_preferred_resolution (monitor_manager, MONITOR_MATCH_ALL); } else { monitor = meta_monitor_manager_get_primary_monitor (monitor_manager); if (monitor) return monitor; monitor = meta_monitor_manager_get_laptop_panel (monitor_manager); if (monitor) return monitor; return find_monitor_with_highest_preferred_resolution (monitor_manager, MONITOR_MATCH_ALL); } } static MetaMonitorConfig * create_monitor_config (MetaMonitor *monitor, MetaMonitorMode *mode) { MetaMonitorSpec *monitor_spec; MetaMonitorModeSpec *mode_spec; MetaMonitorConfig *monitor_config; monitor_spec = meta_monitor_get_spec (monitor); mode_spec = meta_monitor_mode_get_spec (mode); monitor_config = g_new0 (MetaMonitorConfig, 1); *monitor_config = (MetaMonitorConfig) { .monitor_spec = meta_monitor_spec_clone (monitor_spec), .mode_spec = g_memdup2 (mode_spec, sizeof (MetaMonitorModeSpec)), .enable_underscanning = meta_monitor_is_underscanning (monitor) }; return monitor_config; } static MetaMonitorTransform get_monitor_transform (MetaMonitorManager *monitor_manager, MetaMonitor *monitor) { MetaOrientationManager *orientation_manager; MetaBackend *backend; if (!meta_monitor_is_laptop_panel (monitor)) return META_MONITOR_TRANSFORM_NORMAL; backend = meta_monitor_manager_get_backend (monitor_manager); orientation_manager = meta_backend_get_orientation_manager (backend); switch (meta_orientation_manager_get_orientation (orientation_manager)) { case META_ORIENTATION_BOTTOM_UP: return META_MONITOR_TRANSFORM_180; case META_ORIENTATION_LEFT_UP: return META_MONITOR_TRANSFORM_90; case META_ORIENTATION_RIGHT_UP: return META_MONITOR_TRANSFORM_270; case META_ORIENTATION_UNDEFINED: case META_ORIENTATION_NORMAL: default: return META_MONITOR_TRANSFORM_NORMAL; } } static float get_preferred_preferred_max_scale (MetaMonitorManager *monitor_manager, MetaLogicalMonitorLayoutMode layout_mode, MonitorMatchRule match_rule) { float scale = 1.0f; GList *monitors, *l; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { float s; MetaMonitor *monitor = l->data; MetaMonitorMode *mode = meta_monitor_get_preferred_mode (monitor); if (match_rule & MONITOR_MATCH_PRIMARY) { if (!meta_monitor_is_primary (monitor)) continue; } if (match_rule & MONITOR_MATCH_BUILTIN) { if (!meta_monitor_is_laptop_panel (monitor)) continue; } else if (match_rule & MONITOR_MATCH_EXTERNAL) { if (meta_monitor_is_laptop_panel (monitor)) continue; } if (match_rule & MONITOR_MATCH_VISIBLE) { if (meta_monitor_is_laptop_panel (monitor) && is_lid_closed (monitor_manager)) continue; } if (match_rule & MONITOR_MATCH_WITH_POSITION) { if (!meta_monitor_get_suggested_position (monitor, NULL, NULL)) continue; } s = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, layout_mode, monitor, mode); scale = MAX (scale, s); } return scale; } static MetaLogicalMonitorConfig * create_preferred_logical_monitor_config (MetaMonitorManager *monitor_manager, MetaMonitor *monitor, int x, int y, float max_scale, MetaLogicalMonitorConfig *primary_logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode) { MetaMonitorMode *mode; int width, height; float scale; MetaMonitorTransform transform; MetaMonitorConfig *monitor_config; MetaLogicalMonitorConfig *logical_monitor_config; mode = meta_monitor_get_preferred_mode (monitor); meta_monitor_mode_get_resolution (mode, &width, &height); if ((meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) && primary_logical_monitor_config) scale = primary_logical_monitor_config->scale; else scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, monitor_manager->layout_mode, monitor, mode); switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: width = (int) roundf (width / scale); height = (int) roundf (height / scale); break; case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: { float ui_scale = scale / ceilf (max_scale); width = (int) roundf (width / ui_scale); height = (int) roundf (height / ui_scale); } break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } monitor_config = create_monitor_config (monitor, mode); transform = get_monitor_transform (monitor_manager, monitor); if (meta_monitor_transform_is_rotated (transform)) { int temp = width; width = height; height = temp; } logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); *logical_monitor_config = (MetaLogicalMonitorConfig) { .layout = (MetaRectangle) { .x = x, .y = y, .width = width, .height = height }, .transform = transform, .scale = scale, .monitor_configs = g_list_append (NULL, monitor_config) }; return logical_monitor_config; } MetaMonitorsConfig * meta_monitor_config_manager_create_linear (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; GList *logical_monitor_configs; MetaMonitor *primary_monitor; MetaLogicalMonitorLayoutMode layout_mode; MetaLogicalMonitorConfig *primary_logical_monitor_config; float max_scale = 1.0f; int x; GList *monitors; GList *l; primary_monitor = find_primary_monitor (monitor_manager); if (!primary_monitor) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) max_scale = get_preferred_preferred_max_scale (monitor_manager, layout_mode, MONITOR_MATCH_VISIBLE); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; logical_monitor_configs = g_list_append (NULL, primary_logical_monitor_config); x = primary_logical_monitor_config->layout.width; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaLogicalMonitorConfig *logical_monitor_config; if (monitor == primary_monitor) continue; if (meta_monitor_is_laptop_panel (monitor) && is_lid_closed (monitor_manager)) continue; logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, x, 0, max_scale, primary_logical_monitor_config, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); x += logical_monitor_config->layout.width; } return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } MetaMonitorsConfig * meta_monitor_config_manager_create_fallback (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaMonitor *primary_monitor; GList *logical_monitor_configs; MetaLogicalMonitorLayoutMode layout_mode; MetaLogicalMonitorConfig *primary_logical_monitor_config; float max_scale = 1.0f; primary_monitor = find_primary_monitor (monitor_manager); if (!primary_monitor) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) max_scale = get_preferred_preferred_max_scale (monitor_manager, layout_mode, MONITOR_MATCH_PRIMARY); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, 0, 0, max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; logical_monitor_configs = g_list_append (NULL, primary_logical_monitor_config); return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } MetaMonitorsConfig * meta_monitor_config_manager_create_suggested (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaLogicalMonitorConfig *primary_logical_monitor_config = NULL; MetaMonitor *primary_monitor; MetaLogicalMonitorLayoutMode layout_mode; GList *logical_monitor_configs; GList *region; int x, y; float max_scale = 1; GList *monitors; GList *l; primary_monitor = find_primary_monitor (monitor_manager); if (!primary_monitor) return NULL; if (!meta_monitor_get_suggested_position (primary_monitor, &x, &y)) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) max_scale = get_preferred_preferred_max_scale (monitor_manager, layout_mode, MONITOR_MATCH_WITH_POSITION); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, primary_monitor, x, y, max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; logical_monitor_configs = g_list_append (NULL, primary_logical_monitor_config); region = g_list_prepend (NULL, &primary_logical_monitor_config->layout); monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaLogicalMonitorConfig *logical_monitor_config; if (monitor == primary_monitor) continue; if (!meta_monitor_get_suggested_position (monitor, &x, &y)) continue; logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, x, y, max_scale, primary_logical_monitor_config, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); if (meta_rectangle_overlaps_with_region (region, &logical_monitor_config->layout)) { g_warning ("Suggested monitor config has overlapping region, rejecting"); g_list_free (region); g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return NULL; } region = g_list_prepend (region, &logical_monitor_config->layout); } for (l = region; region->next && l; l = l->next) { MetaRectangle *rect = l->data; if (!meta_rectangle_has_adjacent_in_region (region, rect)) { g_warning ("Suggested monitor config has monitors with no neighbors, " "rejecting"); g_list_free (region); g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return NULL; } } g_list_free (region); if (!logical_monitor_configs) return NULL; return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } static GList * clone_monitor_config_list (GList *monitor_configs_in) { MetaMonitorConfig *monitor_config_in; MetaMonitorConfig *monitor_config_out; GList *monitor_configs_out = NULL; GList *l; for (l = monitor_configs_in; l; l = l->next) { monitor_config_in = l->data; monitor_config_out = g_new0 (MetaMonitorConfig, 1); *monitor_config_out = (MetaMonitorConfig) { .monitor_spec = meta_monitor_spec_clone (monitor_config_in->monitor_spec), .mode_spec = g_memdup2 (monitor_config_in->mode_spec, sizeof (MetaMonitorModeSpec)), .enable_underscanning = monitor_config_in->enable_underscanning }; monitor_configs_out = g_list_append (monitor_configs_out, monitor_config_out); } return monitor_configs_out; } static GList * clone_logical_monitor_config_list (GList *logical_monitor_configs_in) { MetaLogicalMonitorConfig *logical_monitor_config_in; MetaLogicalMonitorConfig *logical_monitor_config_out; GList *logical_monitor_configs_out = NULL; GList *l; for (l = logical_monitor_configs_in; l; l = l->next) { logical_monitor_config_in = l->data; logical_monitor_config_out = g_memdup2 (logical_monitor_config_in, sizeof (MetaLogicalMonitorConfig)); logical_monitor_config_out->monitor_configs = clone_monitor_config_list (logical_monitor_config_in->monitor_configs); logical_monitor_configs_out = g_list_append (logical_monitor_configs_out, logical_monitor_config_out); } return logical_monitor_configs_out; } static MetaLogicalMonitorConfig * find_logical_config_for_builtin_display_rotation (MetaMonitorConfigManager *config_manager, GList *logical_monitor_configs) { MetaLogicalMonitorConfig *logical_monitor_config; MetaMonitorConfig *monitor_config; MetaMonitor *panel; GList *l; panel = meta_monitor_manager_get_laptop_panel (config_manager->monitor_manager); if (panel && meta_monitor_is_active (panel)) { for (l = logical_monitor_configs; l; l = l->next) { logical_monitor_config = l->data; /* * We only want to return the config for the panel if it is * configured on its own, so we skip configs which contain clones. */ if (g_list_length (logical_monitor_config->monitor_configs) != 1) continue; monitor_config = logical_monitor_config->monitor_configs->data; if (meta_monitor_spec_equals (meta_monitor_get_spec (panel), monitor_config->monitor_spec)) return logical_monitor_config; } } return NULL; } static MetaMonitorsConfig * create_for_builtin_display_rotation (MetaMonitorConfigManager *config_manager, gboolean rotate, MetaMonitorTransform transform) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaLogicalMonitorConfig *logical_monitor_config; MetaLogicalMonitorConfig *current_logical_monitor_config; GList *logical_monitor_configs, *current_configs; MetaLogicalMonitorLayoutMode layout_mode; if (!config_manager->current_config) return NULL; current_configs = config_manager->current_config->logical_monitor_configs; current_logical_monitor_config = find_logical_config_for_builtin_display_rotation (config_manager, current_configs); if (!current_logical_monitor_config) return NULL; if (rotate) transform = (current_logical_monitor_config->transform + 1) % META_MONITOR_TRANSFORM_FLIPPED; else { /* * The transform coming from the accelerometer should be applied to * the crtc as is, without taking panel-orientation into account, this * is done so that non panel-orientation aware desktop environments do the * right thing. Mutter corrects for panel-orientation when applying the * transform from a logical-monitor-config, so we must convert here. */ MetaMonitor *panel = meta_monitor_manager_get_laptop_panel (config_manager->monitor_manager); transform = meta_monitor_crtc_to_logical_transform (panel, transform); } if (current_logical_monitor_config->transform == transform) return NULL; logical_monitor_configs = clone_logical_monitor_config_list (config_manager->current_config->logical_monitor_configs); logical_monitor_config = find_logical_config_for_builtin_display_rotation (config_manager, logical_monitor_configs); logical_monitor_config->transform = transform; if (meta_monitor_transform_is_rotated (current_logical_monitor_config->transform) != meta_monitor_transform_is_rotated (logical_monitor_config->transform)) { int temp = logical_monitor_config->layout.width; logical_monitor_config->layout.width = logical_monitor_config->layout.height; logical_monitor_config->layout.height = temp; } layout_mode = config_manager->current_config->layout_mode; return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } MetaMonitorsConfig * meta_monitor_config_manager_create_for_orientation (MetaMonitorConfigManager *config_manager, MetaMonitorTransform transform) { return create_for_builtin_display_rotation (config_manager, FALSE, transform); } MetaMonitorsConfig * meta_monitor_config_manager_create_for_rotate_monitor (MetaMonitorConfigManager *config_manager) { return create_for_builtin_display_rotation (config_manager, TRUE, META_MONITOR_TRANSFORM_NORMAL); } MetaMonitorsConfig * meta_monitor_config_manager_create_for_layout (MetaMonitorConfigManager *config_manager, MetaMonitorsConfig *config, MetaLogicalMonitorLayoutMode layout_mode) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; GList *logical_monitor_configs; GList *l; if (!config) return NULL; if (config->layout_mode == layout_mode) return g_object_ref (config); logical_monitor_configs = clone_logical_monitor_config_list (config->logical_monitor_configs); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL) { for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *monitor_config = l->data; monitor_config->scale = roundf (monitor_config->scale); } } return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } static MetaMonitorsConfig * create_for_switch_config_all_mirror (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaLogicalMonitorLayoutMode layout_mode; MetaLogicalMonitorConfig *logical_monitor_config = NULL; GList *logical_monitor_configs; GList *monitor_configs = NULL; gint common_mode_w = 0, common_mode_h = 0; float best_scale = 1.0; MetaMonitor *monitor; GList *modes; GList *monitors; GList *l; monitors = meta_monitor_manager_get_monitors (monitor_manager); monitor = monitors->data; modes = meta_monitor_get_modes (monitor); for (l = modes; l; l = l->next) { MetaMonitorMode *mode = l->data; gboolean common_mode_size = TRUE; gint mode_w, mode_h; GList *ll; meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h); for (ll = monitors->next; ll; ll = ll->next) { MetaMonitor *monitor_b = ll->data; gboolean have_same_mode_size = FALSE; GList *mm; for (mm = meta_monitor_get_modes (monitor_b); mm; mm = mm->next) { MetaMonitorMode *mode_b = mm->data; gint mode_b_w, mode_b_h; meta_monitor_mode_get_resolution (mode_b, &mode_b_w, &mode_b_h); if (mode_w == mode_b_w && mode_h == mode_b_h) { have_same_mode_size = TRUE; break; } } if (!have_same_mode_size) { common_mode_size = FALSE; break; } } if (common_mode_size && common_mode_w * common_mode_h < mode_w * mode_h) { common_mode_w = mode_w; common_mode_h = mode_h; } } if (common_mode_w == 0 || common_mode_h == 0) return NULL; for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorMode *mode = NULL; GList *ll; float scale; for (ll = meta_monitor_get_modes (monitor); ll; ll = ll->next) { gint mode_w, mode_h; mode = ll->data; meta_monitor_mode_get_resolution (mode, &mode_w, &mode_h); if (mode_w == common_mode_w && mode_h == common_mode_h) break; } if (!mode) continue; scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, monitor_manager->layout_mode, monitor, mode); best_scale = MAX (best_scale, scale); monitor_configs = g_list_prepend (monitor_configs, create_monitor_config (monitor, mode)); } logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); *logical_monitor_config = (MetaLogicalMonitorConfig) { .layout = (MetaRectangle) { .x = 0, .y = 0, .width = common_mode_w, .height = common_mode_h }, .scale = best_scale, .monitor_configs = monitor_configs }; logical_monitor_configs = g_list_append (NULL, logical_monitor_config); layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } static MetaMonitorsConfig * create_for_switch_config_external (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; GList *logical_monitor_configs = NULL; int x = 0; float max_scale = 1.0f; MetaLogicalMonitorLayoutMode layout_mode; GList *monitors; GList *l; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) max_scale = get_preferred_preferred_max_scale (monitor_manager, layout_mode, MONITOR_MATCH_EXTERNAL); monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaLogicalMonitorConfig *logical_monitor_config; if (meta_monitor_is_laptop_panel (monitor)) continue; logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, x, 0, max_scale, NULL, layout_mode); logical_monitor_configs = g_list_append (logical_monitor_configs, logical_monitor_config); if (x == 0) logical_monitor_config->is_primary = TRUE; x += logical_monitor_config->layout.width; } return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } static MetaMonitorsConfig * create_for_switch_config_builtin (MetaMonitorConfigManager *config_manager) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaLogicalMonitorLayoutMode layout_mode; GList *logical_monitor_configs; MetaLogicalMonitorConfig *primary_logical_monitor_config; MetaMonitor *monitor; float max_scale = 1.0f; monitor = meta_monitor_manager_get_laptop_panel (monitor_manager); if (!monitor) return NULL; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) max_scale = get_preferred_preferred_max_scale (monitor_manager, layout_mode, MONITOR_MATCH_BUILTIN); primary_logical_monitor_config = create_preferred_logical_monitor_config (monitor_manager, monitor, 0, 0, max_scale, NULL, layout_mode); primary_logical_monitor_config->is_primary = TRUE; logical_monitor_configs = g_list_append (NULL, primary_logical_monitor_config); return meta_monitors_config_new (monitor_manager, logical_monitor_configs, layout_mode, META_MONITORS_CONFIG_FLAG_NONE); } MetaMonitorsConfig * meta_monitor_config_manager_create_for_switch_config (MetaMonitorConfigManager *config_manager, MetaMonitorSwitchConfigType config_type) { MetaMonitorManager *monitor_manager = config_manager->monitor_manager; MetaMonitorsConfig *config; if (!meta_monitor_manager_can_switch_config (monitor_manager)) return NULL; switch (config_type) { case META_MONITOR_SWITCH_CONFIG_ALL_MIRROR: config = create_for_switch_config_all_mirror (config_manager); break; case META_MONITOR_SWITCH_CONFIG_ALL_LINEAR: config = meta_monitor_config_manager_create_linear (config_manager); break; case META_MONITOR_SWITCH_CONFIG_EXTERNAL: config = create_for_switch_config_external (config_manager); break; case META_MONITOR_SWITCH_CONFIG_BUILTIN: config = create_for_switch_config_builtin (config_manager); break; case META_MONITOR_SWITCH_CONFIG_UNKNOWN: default: g_warn_if_reached (); return NULL; } if (config) meta_monitors_config_set_switch_config (config, config_type); return config; } void meta_monitor_config_manager_set_current (MetaMonitorConfigManager *config_manager, MetaMonitorsConfig *config) { if (config_manager->current_config) { g_queue_push_head (&config_manager->config_history, g_object_ref (config_manager->current_config)); if (g_queue_get_length (&config_manager->config_history) > CONFIG_HISTORY_MAX_SIZE) g_object_unref (g_queue_pop_tail (&config_manager->config_history)); } g_set_object (&config_manager->current_config, config); } void meta_monitor_config_manager_save_current (MetaMonitorConfigManager *config_manager) { g_return_if_fail (config_manager->current_config); meta_monitor_config_store_add (config_manager->config_store, config_manager->current_config); } MetaMonitorsConfig * meta_monitor_config_manager_get_current (MetaMonitorConfigManager *config_manager) { return config_manager->current_config; } MetaMonitorsConfig * meta_monitor_config_manager_pop_previous (MetaMonitorConfigManager *config_manager) { return g_queue_pop_head (&config_manager->config_history); } MetaMonitorsConfig * meta_monitor_config_manager_get_previous (MetaMonitorConfigManager *config_manager) { return g_queue_peek_head (&config_manager->config_history); } void meta_monitor_config_manager_clear_history (MetaMonitorConfigManager *config_manager) { g_queue_foreach (&config_manager->config_history, (GFunc) g_object_unref, NULL); g_queue_clear (&config_manager->config_history); } static void meta_monitor_config_manager_dispose (GObject *object) { MetaMonitorConfigManager *config_manager = META_MONITOR_CONFIG_MANAGER (object); g_clear_object (&config_manager->current_config); meta_monitor_config_manager_clear_history (config_manager); G_OBJECT_CLASS (meta_monitor_config_manager_parent_class)->dispose (object); } static void meta_monitor_config_manager_init (MetaMonitorConfigManager *config_manager) { g_queue_init (&config_manager->config_history); } static void meta_monitor_config_manager_class_init (MetaMonitorConfigManagerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_monitor_config_manager_dispose; } void meta_monitor_config_free (MetaMonitorConfig *monitor_config) { if (monitor_config->monitor_spec) meta_monitor_spec_free (monitor_config->monitor_spec); g_free (monitor_config->mode_spec); g_free (monitor_config); } void meta_logical_monitor_config_free (MetaLogicalMonitorConfig *logical_monitor_config) { g_list_free_full (logical_monitor_config->monitor_configs, (GDestroyNotify) meta_monitor_config_free); g_free (logical_monitor_config); } static MetaMonitorsConfigKey * meta_monitors_config_key_new (GList *logical_monitor_configs, GList *disabled_monitor_specs) { MetaMonitorsConfigKey *config_key; GList *monitor_specs; GList *l; monitor_specs = NULL; for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; GList *k; for (k = logical_monitor_config->monitor_configs; k; k = k->next) { MetaMonitorConfig *monitor_config = k->data; MetaMonitorSpec *monitor_spec; monitor_spec = meta_monitor_spec_clone (monitor_config->monitor_spec); monitor_specs = g_list_prepend (monitor_specs, monitor_spec); } } for (l = disabled_monitor_specs; l; l = l->next) { MetaMonitorSpec *monitor_spec = l->data; monitor_spec = meta_monitor_spec_clone (monitor_spec); monitor_specs = g_list_prepend (monitor_specs, monitor_spec); } monitor_specs = g_list_sort (monitor_specs, (GCompareFunc) meta_monitor_spec_compare); config_key = g_new0 (MetaMonitorsConfigKey, 1); *config_key = (MetaMonitorsConfigKey) { .monitor_specs = monitor_specs }; return config_key; } void meta_monitors_config_key_free (MetaMonitorsConfigKey *config_key) { g_list_free_full (config_key->monitor_specs, (GDestroyNotify) meta_monitor_spec_free); g_free (config_key); } unsigned int meta_monitors_config_key_hash (gconstpointer data) { const MetaMonitorsConfigKey *config_key = data; GList *l; unsigned long hash; hash = 0; for (l = config_key->monitor_specs; l; l = l->next) { MetaMonitorSpec *monitor_spec = l->data; hash ^= (g_str_hash (monitor_spec->connector) ^ g_str_hash (monitor_spec->vendor) ^ g_str_hash (monitor_spec->product) ^ g_str_hash (monitor_spec->serial)); } return hash; } gboolean meta_monitors_config_key_equal (gconstpointer data_a, gconstpointer data_b) { const MetaMonitorsConfigKey *config_key_a = data_a; const MetaMonitorsConfigKey *config_key_b = data_b; GList *l_a, *l_b; for (l_a = config_key_a->monitor_specs, l_b = config_key_b->monitor_specs; l_a && l_b; l_a = l_a->next, l_b = l_b->next) { MetaMonitorSpec *monitor_spec_a = l_a->data; MetaMonitorSpec *monitor_spec_b = l_b->data; if (!meta_monitor_spec_equals (monitor_spec_a, monitor_spec_b)) return FALSE; } if (l_a || l_b) return FALSE; return TRUE; } MetaMonitorSwitchConfigType meta_monitors_config_get_switch_config (MetaMonitorsConfig *config) { return config->switch_config; } void meta_monitors_config_set_switch_config (MetaMonitorsConfig *config, MetaMonitorSwitchConfigType switch_config) { config->switch_config = switch_config; } MetaMonitorsConfig * meta_monitors_config_new_full (GList *logical_monitor_configs, GList *disabled_monitor_specs, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags) { MetaMonitorsConfig *config; config = g_object_new (META_TYPE_MONITORS_CONFIG, NULL); config->logical_monitor_configs = logical_monitor_configs; config->disabled_monitor_specs = disabled_monitor_specs; config->key = meta_monitors_config_key_new (logical_monitor_configs, disabled_monitor_specs); config->layout_mode = layout_mode; config->flags = flags; config->switch_config = META_MONITOR_SWITCH_CONFIG_UNKNOWN; return config; } MetaMonitorsConfig * meta_monitors_config_new (MetaMonitorManager *monitor_manager, GList *logical_monitor_configs, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorsConfigFlag flags) { GList *disabled_monitor_specs = NULL; GList *monitors; GList *l; monitors = meta_monitor_manager_get_monitors (monitor_manager); for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; MetaMonitorSpec *monitor_spec; if (is_lid_closed (monitor_manager) && meta_monitor_is_laptop_panel (monitor)) continue; monitor_spec = meta_monitor_get_spec (monitor); if (meta_logical_monitor_configs_have_monitor (logical_monitor_configs, monitor_spec)) continue; disabled_monitor_specs = g_list_prepend (disabled_monitor_specs, meta_monitor_spec_clone (monitor_spec)); } return meta_monitors_config_new_full (logical_monitor_configs, disabled_monitor_specs, layout_mode, flags); } static void meta_monitors_config_finalize (GObject *object) { MetaMonitorsConfig *config = META_MONITORS_CONFIG (object); meta_monitors_config_key_free (config->key); g_list_free_full (config->logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); g_list_free_full (config->disabled_monitor_specs, (GDestroyNotify) meta_monitor_spec_free); G_OBJECT_CLASS (meta_monitors_config_parent_class)->finalize (object); } static void meta_monitors_config_init (MetaMonitorsConfig *config) { } static void meta_monitors_config_class_init (MetaMonitorsConfigClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_monitors_config_finalize; } static void meta_crtc_info_free (MetaCrtcInfo *info) { g_ptr_array_free (info->outputs, TRUE); g_slice_free (MetaCrtcInfo, info); } static void meta_output_info_free (MetaOutputInfo *info) { g_slice_free (MetaOutputInfo, info); } gboolean meta_verify_monitor_mode_spec (MetaMonitorModeSpec *monitor_mode_spec, GError **error) { if (monitor_mode_spec->width > 0 && monitor_mode_spec->height > 0 && monitor_mode_spec->refresh_rate > 0.0f) { return TRUE; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor mode invalid"); return FALSE; } } gboolean meta_verify_monitor_spec (MetaMonitorSpec *monitor_spec, GError **error) { if (monitor_spec->connector && monitor_spec->vendor && monitor_spec->product && monitor_spec->serial) { return TRUE; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor spec incomplete"); return FALSE; } } gboolean meta_verify_monitor_config (MetaMonitorConfig *monitor_config, GError **error) { if (monitor_config->monitor_spec && monitor_config->mode_spec) { return TRUE; } else { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor config incomplete"); return FALSE; } } gboolean meta_verify_logical_monitor_config (MetaLogicalMonitorConfig *logical_monitor_config, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitorManager *monitor_manager, float max_scale, GError **error) { GList *l; int expected_mode_width = 0; int expected_mode_height = 0; if (logical_monitor_config->layout.x < 0 || logical_monitor_config->layout.y < 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Invalid logical monitor position (%d, %d)", logical_monitor_config->layout.x, logical_monitor_config->layout.y); return FALSE; } if (!logical_monitor_config->monitor_configs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitor is empty"); return FALSE; } if (meta_monitor_transform_is_rotated (logical_monitor_config->transform)) { expected_mode_width = logical_monitor_config->layout.height; expected_mode_height = logical_monitor_config->layout.width; } else { expected_mode_width = logical_monitor_config->layout.width; expected_mode_height = logical_monitor_config->layout.height; } switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: expected_mode_width /= ceilf (max_scale); expected_mode_height /= ceilf (max_scale); /* fall through! */ case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: expected_mode_width = roundf (expected_mode_width * logical_monitor_config->scale); expected_mode_height = roundf (expected_mode_height * logical_monitor_config->scale); break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: break; } for (l = logical_monitor_config->monitor_configs; l; l = l->next) { MetaMonitorConfig *monitor_config = l->data; if (monitor_config->mode_spec->width != expected_mode_width || monitor_config->mode_spec->height != expected_mode_height) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitor modes in logical monitor conflict"); return FALSE; } } return TRUE; } static gboolean has_adjacent_neighbour (MetaMonitorsConfig *config, MetaLogicalMonitorConfig *logical_monitor_config) { GList *l; if (!config->logical_monitor_configs->next) { g_assert (config->logical_monitor_configs->data == logical_monitor_config); return TRUE; } for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *other_logical_monitor_config = l->data; if (logical_monitor_config == other_logical_monitor_config) continue; if (meta_rectangle_is_adjacent_to (&logical_monitor_config->layout, &other_logical_monitor_config->layout)) return TRUE; } return FALSE; } gboolean meta_logical_monitor_configs_have_monitor (GList *logical_monitor_configs, MetaMonitorSpec *monitor_spec) { GList *l; for (l = logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; GList *k; for (k = logical_monitor_config->monitor_configs; k; k = k->next) { MetaMonitorConfig *monitor_config = k->data; if (meta_monitor_spec_equals (monitor_spec, monitor_config->monitor_spec)) return TRUE; } } return FALSE; } static gboolean meta_monitors_config_is_monitor_enabled (MetaMonitorsConfig *config, MetaMonitorSpec *monitor_spec) { return meta_logical_monitor_configs_have_monitor (config->logical_monitor_configs, monitor_spec); } gboolean meta_verify_monitors_config (MetaMonitorsConfig *config, MetaMonitorManager *monitor_manager, GError **error) { int min_x, min_y; gboolean has_primary; GList *region; GList *l; gboolean global_scale_required; if (!config->logical_monitor_configs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Monitors config incomplete"); return FALSE; } global_scale_required = !!(meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED); min_x = INT_MAX; min_y = INT_MAX; region = NULL; has_primary = FALSE; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (global_scale_required) { MetaLogicalMonitorConfig *prev_logical_monitor_config = l->prev ? l->prev->data : NULL; if (prev_logical_monitor_config && (prev_logical_monitor_config->scale != logical_monitor_config->scale)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitor scales must be identical"); return FALSE; } } if (meta_rectangle_overlaps_with_region (region, &logical_monitor_config->layout)) { g_list_free (region); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitors overlap"); return FALSE; } if (has_primary && logical_monitor_config->is_primary) { g_list_free (region); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Config contains multiple primary logical monitors"); return FALSE; } else if (logical_monitor_config->is_primary) { has_primary = TRUE; } if (!has_adjacent_neighbour (config, logical_monitor_config)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitors not adjacent"); return FALSE; } min_x = MIN (logical_monitor_config->layout.x, min_x); min_y = MIN (logical_monitor_config->layout.y, min_y); region = g_list_prepend (region, &logical_monitor_config->layout); } g_list_free (region); for (l = config->disabled_monitor_specs; l; l = l->next) { MetaMonitorSpec *monitor_spec = l->data; if (meta_monitors_config_is_monitor_enabled (config, monitor_spec)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Assigned monitor explicitly disabled"); return FALSE; } } if (min_x != 0 || min_y != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Logical monitors positions are offset"); return FALSE; } if (!has_primary) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Config is missing primary logical"); return FALSE; } return TRUE; } muffin-6.4.1/src/backends/meta-monitor-transform.h0000664000175000017500000000527414723361714021110 0ustar fabiofabio/* * Copyright (C) 2013 Red Hat Inc. * Copyright (C) 2018 Robert Mader * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_TRANSFORM_H #define META_MONITOR_TRANSFORM_H #include #include "backends/meta-backend-types.h" #include "core/util-private.h" enum _MetaMonitorTransform { META_MONITOR_TRANSFORM_NORMAL, META_MONITOR_TRANSFORM_90, META_MONITOR_TRANSFORM_180, META_MONITOR_TRANSFORM_270, META_MONITOR_TRANSFORM_FLIPPED, META_MONITOR_TRANSFORM_FLIPPED_90, META_MONITOR_TRANSFORM_FLIPPED_180, META_MONITOR_TRANSFORM_FLIPPED_270, }; #define META_MONITOR_N_TRANSFORMS (META_MONITOR_TRANSFORM_FLIPPED_270 + 1) /* Returns true if transform causes width and height to be inverted This is true for the odd transforms in the enum */ static inline gboolean meta_monitor_transform_is_rotated (MetaMonitorTransform transform) { return (transform % 2); } /* Returns true if transform involves flipping */ static inline gboolean meta_monitor_transform_is_flipped (MetaMonitorTransform transform) { return (abs(transform) >= META_MONITOR_TRANSFORM_FLIPPED); } MetaMonitorTransform meta_monitor_transform_invert (MetaMonitorTransform transform); META_EXPORT_TEST MetaMonitorTransform meta_monitor_transform_transform (MetaMonitorTransform transform, MetaMonitorTransform other); MetaMonitorTransform meta_monitor_transform_relative_transform (MetaMonitorTransform transform, MetaMonitorTransform other); void meta_monitor_transform_transform_point (MetaMonitorTransform transform, int area_width, int area_height, int x, int y, int *out_x, int *out_y); #endif /* META_MONITOR_TRANSFORM_H */ muffin-6.4.1/src/backends/meta-screen-cast-stream.h0000664000175000017500000000552014723361714021102 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_STREAM_H #define META_SCREEN_CAST_STREAM_H #include #include "backends/meta-screen-cast-stream-src.h" #include "backends/meta-screen-cast.h" #include "meta-dbus-screen-cast.h" #define META_TYPE_SCREEN_CAST_STREAM (meta_screen_cast_stream_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStream, meta_screen_cast_stream, META, SCREEN_CAST_STREAM, MetaDBusScreenCastStreamSkeleton) struct _MetaScreenCastStreamClass { MetaDBusScreenCastStreamSkeletonClass parent_class; MetaScreenCastStreamSrc * (* create_src) (MetaScreenCastStream *stream, GError **error); void (* set_parameters) (MetaScreenCastStream *stream, GVariantBuilder *parameters_builder); void (* transform_position) (MetaScreenCastStream *stream, double stream_x, double stream_y, double *x, double *y); }; MetaScreenCastSession * meta_screen_cast_stream_get_session (MetaScreenCastStream *stream); gboolean meta_screen_cast_stream_start (MetaScreenCastStream *stream, GError **error); void meta_screen_cast_stream_close (MetaScreenCastStream *stream); char * meta_screen_cast_stream_get_object_path (MetaScreenCastStream *stream); void meta_screen_cast_stream_transform_position (MetaScreenCastStream *stream, double stream_x, double stream_y, double *x, double *y); MetaScreenCastCursorMode meta_screen_cast_stream_get_cursor_mode (MetaScreenCastStream *stream); #endif /* META_SCREEN_CAST_STREAM_H */ muffin-6.4.1/src/backends/meta-orientation-manager.h0000664000175000017500000000264014723361714021345 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_ORIENTATION_MANAGER_H #define META_ORIENTATION_MANAGER_H #include typedef enum { META_ORIENTATION_UNDEFINED, META_ORIENTATION_NORMAL, META_ORIENTATION_BOTTOM_UP, META_ORIENTATION_LEFT_UP, META_ORIENTATION_RIGHT_UP } MetaOrientation; #define META_TYPE_ORIENTATION_MANAGER (meta_orientation_manager_get_type ()) G_DECLARE_FINAL_TYPE (MetaOrientationManager, meta_orientation_manager, META, ORIENTATION_MANAGER, GObject) MetaOrientation meta_orientation_manager_get_orientation (MetaOrientationManager *self); #endif /* META_ORIENTATION_MANAGER_H */ muffin-6.4.1/src/backends/meta-profiler.c0000664000175000017500000001323614723361714017222 0ustar fabiofabio/* * Copyright (C) 2019 Endless, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "src/backends/meta-profiler.h" #include #include #include #include "cogl/cogl.h" #define META_SYSPROF_PROFILER_DBUS_PATH "/org/gnome/Sysprof3/Profiler" struct _MetaProfiler { MetaDBusSysprof3ProfilerSkeleton parent_instance; GDBusConnection *connection; GCancellable *cancellable; gboolean running; }; static void meta_sysprof_capturer_init_iface (MetaDBusSysprof3ProfilerIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaProfiler, meta_profiler, META_DBUS_TYPE_SYSPROF3_PROFILER_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SYSPROF3_PROFILER, meta_sysprof_capturer_init_iface)) static gboolean handle_start (MetaDBusSysprof3Profiler *dbus_profiler, GDBusMethodInvocation *invocation, GUnixFDList *fd_list, GVariant *options, GVariant *fd_variant) { MetaProfiler *profiler = META_PROFILER (dbus_profiler); GMainContext *main_context = g_main_context_default (); const char *group_name; int position; int fd = -1; if (profiler->running) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Profiler already running"); return TRUE; } g_variant_get (fd_variant, "h", &position); if (fd_list) fd = g_unix_fd_list_get (fd_list, position, NULL); /* Translators: this string will appear in Sysprof */ group_name = _("Compositor"); if (fd != -1) { cogl_set_tracing_enabled_on_thread_with_fd (main_context, group_name, fd); } else { cogl_set_tracing_enabled_on_thread (main_context, group_name, "mutter-profile.syscap"); } profiler->running = TRUE; g_debug ("Profiler running"); meta_dbus_sysprof3_profiler_complete_start (dbus_profiler, invocation, NULL); return TRUE; } static gboolean handle_stop (MetaDBusSysprof3Profiler *dbus_profiler, GDBusMethodInvocation *invocation) { MetaProfiler *profiler = META_PROFILER (dbus_profiler); if (!profiler->running) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Profiler not running"); return TRUE; } cogl_set_tracing_disabled_on_thread (g_main_context_default ()); profiler->running = FALSE; g_debug ("Stopping profiler"); meta_dbus_sysprof3_profiler_complete_stop (dbus_profiler, invocation); return TRUE; } static void meta_sysprof_capturer_init_iface (MetaDBusSysprof3ProfilerIface *iface) { iface->handle_start = handle_start; iface->handle_stop = handle_stop; } static void on_bus_acquired_cb (GObject *source, GAsyncResult *result, gpointer user_data) { g_autoptr (GDBusConnection) connection = NULL; GDBusInterfaceSkeleton *interface_skeleton; g_autoptr (GError) error = NULL; MetaProfiler *profiler; connection = g_bus_get_finish (result, &error); if (error) { if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) g_warning ("Failed to get session bus: %s\n", error->message); return; } profiler = META_PROFILER (user_data); interface_skeleton = G_DBUS_INTERFACE_SKELETON (profiler); if (!g_dbus_interface_skeleton_export (interface_skeleton, connection, META_SYSPROF_PROFILER_DBUS_PATH, &error)) { g_warning ("Failed to export profiler object: %s\n", error->message); return; } profiler->connection = g_steal_pointer (&connection); } static void meta_profiler_finalize (GObject *object) { MetaProfiler *self = (MetaProfiler *)object; g_cancellable_cancel (self->cancellable); g_clear_object (&self->cancellable); g_clear_object (&self->connection); G_OBJECT_CLASS (meta_profiler_parent_class)->finalize (object); } static void meta_profiler_class_init (MetaProfilerClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_profiler_finalize; } static void meta_profiler_init (MetaProfiler *self) { self->cancellable = g_cancellable_new (); g_bus_get (G_BUS_TYPE_SESSION, self->cancellable, on_bus_acquired_cb, self); } MetaProfiler * meta_profiler_new (void) { return g_object_new (META_TYPE_PROFILER, NULL); } muffin-6.4.1/src/backends/x11/0000775000175000017500000000000014723361714014714 5ustar fabiofabiomuffin-6.4.1/src/backends/x11/meta-clutter-backend-x11.h0000664000175000017500000000246614723361714021477 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_CLUTTER_BACKEND_X11_H #define META_CLUTTER_BACKEND_X11_H #include #include "clutter/clutter.h" #include "clutter/x11/clutter-backend-x11.h" #define META_TYPE_CLUTTER_BACKEND_X11 (meta_clutter_backend_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaClutterBackendX11, meta_clutter_backend_x11, META, CLUTTER_BACKEND_X11, ClutterBackendX11) #endif /* META_CLUTTER_BACKEND_X11_H */ muffin-6.4.1/src/backends/x11/meta-backend-x11.h0000664000175000017500000000407314723361714020013 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_X11_H #define META_BACKEND_X11_H #include #include #include "backends/meta-backend-private.h" #include "backends/x11/meta-clutter-backend-x11.h" #define META_TYPE_BACKEND_X11 (meta_backend_x11_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaBackendX11, meta_backend_x11, META, BACKEND_X11, MetaBackend) struct _MetaBackendX11Class { MetaBackendClass parent_class; gboolean (* handle_host_xevent) (MetaBackendX11 *x11, XEvent *event); void (* translate_device_event) (MetaBackendX11 *x11, XIDeviceEvent *device_event); void (* translate_crossing_event) (MetaBackendX11 *x11, XIEnterEvent *enter_event); }; Display * meta_backend_x11_get_xdisplay (MetaBackendX11 *backend); Window meta_backend_x11_get_xwindow (MetaBackendX11 *backend); void meta_backend_x11_handle_event (MetaBackendX11 *x11, XEvent *xevent); uint8_t meta_backend_x11_get_xkb_event_base (MetaBackendX11 *x11); void meta_backend_x11_reload_cursor (MetaBackendX11 *x11); #endif /* META_BACKEND_X11_H */ muffin-6.4.1/src/backends/x11/meta-stage-x11.c0000664000175000017500000007016214723361714017524 0ustar fabiofabio/* * Authored By Matthew Allum * Copyright (C) 2006-2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * */ #include "config.h" #include #include #ifdef HAVE_UNISTD_H #include #endif #include "backends/x11/cm/meta-backend-x11-cm.h" #include "backends/x11/cm/meta-renderer-x11-cm.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-seat-x11.h" #include "backends/x11/meta-stage-x11.h" #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" #include "clutter/x11/clutter-backend-x11.h" #include "cogl/cogl.h" #include "core/display-private.h" #include "meta/meta-x11-errors.h" #define STAGE_X11_IS_MAPPED(s) ((((MetaStageX11 *) (s))->wm_state & STAGE_X11_WITHDRAWN) == 0) static ClutterStageWindowInterface *clutter_stage_window_parent_iface = NULL; static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface); static ClutterStageCogl *meta_x11_get_stage_window_from_window (Window win); static GHashTable *clutter_stages_by_xid = NULL; G_DEFINE_TYPE_WITH_CODE (MetaStageX11, meta_stage_x11, CLUTTER_TYPE_STAGE_COGL, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init)); #define _NET_WM_STATE_REMOVE 0 /* remove/unset property */ #define _NET_WM_STATE_ADD 1 /* add/set property */ #define _NET_WM_STATE_TOGGLE 2 /* toggle property */ #define META_STAGE_X11_EVENT_MASK \ StructureNotifyMask | \ FocusChangeMask | \ ExposureMask | \ PropertyChangeMask | \ EnterWindowMask | \ LeaveWindowMask | \ KeyPressMask | \ KeyReleaseMask | \ ButtonPressMask | \ ButtonReleaseMask | \ PointerMotionMask static void meta_stage_x11_fix_window_size (MetaStageX11 *stage_x11, int new_width, int new_height) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); if (stage_x11->xwin != None) { Display *xdisplay = clutter_x11_get_default_display (); uint32_t min_width, min_height; XSizeHints *size_hints; size_hints = XAllocSizeHints(); clutter_stage_get_minimum_size (stage_cogl->wrapper, &min_width, &min_height); if (new_width <= 0) new_width = min_width; if (new_height <= 0) new_height = min_height; size_hints->min_width = new_width; size_hints->min_height = new_height; size_hints->max_width = new_width; size_hints->max_height = new_height; size_hints->flags = PMinSize | PMaxSize; XSetWMNormalHints (xdisplay, stage_x11->xwin, size_hints); XFree(size_hints); } } static void meta_stage_x11_set_wm_protocols (MetaStageX11 *stage_x11) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); Display *xdisplay = clutter_x11_get_default_display (); Atom protocols[2]; int n = 0; protocols[n++] = backend_x11->atom_WM_DELETE_WINDOW; protocols[n++] = backend_x11->atom_NET_WM_PING; XSetWMProtocols (xdisplay, stage_x11->xwin, protocols, n); } static void meta_stage_x11_get_geometry (ClutterStageWindow *stage_window, cairo_rectangle_int_t *geometry) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); geometry->x = geometry->y = 0; geometry->width = stage_x11->xwin_width; geometry->height = stage_x11->xwin_height; } static void meta_stage_x11_resize (ClutterStageWindow *stage_window, int width, int height) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); if (width == 0 || height == 0) { /* Should not happen, if this turns up we need to debug it and * determine the cleanest way to fix. */ g_warning ("X11 stage not allowed to have 0 width or height"); width = 1; height = 1; } if (stage_x11->xwin != None) { meta_stage_x11_fix_window_size (stage_x11, width, height); if (width != stage_x11->xwin_width || height != stage_x11->xwin_height) { Display *xdisplay = clutter_x11_get_default_display (); /* XXX: in this case we can rely on a subsequent * ConfigureNotify that will result in the stage * being reallocated so we don't actively do anything * to affect the stage allocation here. */ XResizeWindow (xdisplay, stage_x11->xwin, width, height); } } else { /* if the backing window hasn't been created yet, we just * need to store the new window size */ stage_x11->xwin_width = width; stage_x11->xwin_height = height; } } static inline void set_wm_pid (MetaStageX11 *stage_x11) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); Display *xdisplay = clutter_x11_get_default_display (); long pid; if (stage_x11->xwin == None) return; /* this will take care of WM_CLIENT_MACHINE and WM_LOCALE_NAME */ XSetWMProperties (xdisplay, stage_x11->xwin, NULL, NULL, NULL, 0, NULL, NULL, NULL); pid = getpid (); XChangeProperty (xdisplay, stage_x11->xwin, backend_x11->atom_NET_WM_PID, XA_CARDINAL, 32, PropModeReplace, (guchar *) &pid, 1); } static inline void set_wm_title (MetaStageX11 *stage_x11) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); Display *xdisplay = clutter_x11_get_default_display (); if (stage_x11->xwin == None) return; if (stage_x11->title == NULL) { XDeleteProperty (xdisplay, stage_x11->xwin, backend_x11->atom_NET_WM_NAME); } else { XChangeProperty (xdisplay, stage_x11->xwin, backend_x11->atom_NET_WM_NAME, backend_x11->atom_UTF8_STRING, 8, PropModeReplace, (unsigned char *) stage_x11->title, (int) strlen (stage_x11->title)); } } static inline void set_cursor_visible (MetaStageX11 *stage_x11) { Display *xdisplay = clutter_x11_get_default_display (); if (stage_x11->xwin == None) return; g_debug ("setting cursor state ('%s') over stage window (%u)", stage_x11->is_cursor_visible ? "visible" : "invisible", (unsigned int) stage_x11->xwin); if (stage_x11->is_cursor_visible) { XUndefineCursor (xdisplay, stage_x11->xwin); } else { XColor col; Pixmap pix; Cursor curs; pix = XCreatePixmap (xdisplay, stage_x11->xwin, 1, 1, 1); memset (&col, 0, sizeof (col)); curs = XCreatePixmapCursor (xdisplay, pix, pix, &col, &col, 1, 1); XFreePixmap (xdisplay, pix); XDefineCursor (xdisplay, stage_x11->xwin, curs); } } static void meta_stage_x11_unrealize (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); if (clutter_stages_by_xid != NULL) { g_hash_table_remove (clutter_stages_by_xid, GINT_TO_POINTER (stage_x11->xwin)); } if (stage_x11->frame_closure) { cogl_onscreen_remove_frame_callback (stage_x11->onscreen, stage_x11->frame_closure); stage_x11->frame_closure = NULL; } clutter_stage_window_parent_iface->unrealize (stage_window); g_clear_pointer (&stage_x11->onscreen, cogl_object_unref); } static void frame_cb (CoglOnscreen *onscreen, CoglFrameEvent frame_event, CoglFrameInfo *frame_info, void *user_data) { ClutterStageCogl *stage_cogl = user_data; ClutterFrameInfo clutter_frame_info = { .frame_counter = cogl_frame_info_get_frame_counter (frame_info), .presentation_time = cogl_frame_info_get_presentation_time (frame_info), .refresh_rate = cogl_frame_info_get_refresh_rate (frame_info) }; _clutter_stage_cogl_presented (stage_cogl, frame_event, &clutter_frame_info); } static gboolean meta_stage_x11_realize (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_window); ClutterBackend *backend = CLUTTER_BACKEND (stage_cogl->backend); MetaSeatX11 *seat_x11 = META_SEAT_X11 (clutter_backend_get_default_seat (backend)); Display *xdisplay = clutter_x11_get_default_display (); float width, height; GError *error = NULL; clutter_actor_get_size (CLUTTER_ACTOR (stage_cogl->wrapper), &width, &height); stage_x11->onscreen = cogl_onscreen_new (backend->cogl_context, width, height); stage_x11->frame_closure = cogl_onscreen_add_frame_callback (stage_x11->onscreen, frame_cb, stage_cogl, NULL); if (META_IS_BACKEND_X11_CM (stage_x11->backend)) { MetaRenderer *renderer = meta_backend_get_renderer (stage_x11->backend); MetaRendererX11Cm *renderer_x11_cm = META_RENDERER_X11_CM (renderer); meta_renderer_x11_cm_set_onscreen (renderer_x11_cm, stage_x11->onscreen); } /* We just created a window of the size of the actor. No need to fix the size of the stage, just update it. */ stage_x11->xwin_width = width; stage_x11->xwin_height = height; if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (stage_x11->onscreen), &error)) { g_warning ("Failed to allocate stage: %s", error->message); g_error_free (error); cogl_object_unref (stage_x11->onscreen); abort(); } if (!(clutter_stage_window_parent_iface->realize (stage_window))) return FALSE; stage_x11->xwin = cogl_x11_onscreen_get_window_xid (stage_x11->onscreen); if (clutter_stages_by_xid == NULL) clutter_stages_by_xid = g_hash_table_new (NULL, NULL); g_hash_table_insert (clutter_stages_by_xid, GINT_TO_POINTER (stage_x11->xwin), stage_x11); set_wm_pid (stage_x11); set_wm_title (stage_x11); set_cursor_visible (stage_x11); /* we unconditionally select input events even with event retrieval * disabled because we need to guarantee that the Clutter internal * state is maintained when calling clutter_x11_handle_event() without * requiring applications or embedding toolkits to select events * themselves. if we did that, we'd have to document the events to be * selected, and also update applications and embedding toolkits each * time we added a new mask, or a new class of events. * * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=998 * for the rationale of why we did conditional selection. it is now * clear that a compositor should clear out the input region, since * it cannot assume a perfectly clean slate coming from us. * * see: http://bugzilla.clutter-project.org/show_bug.cgi?id=2228 * for an example of things that break if we do conditional event * selection. */ XSelectInput (xdisplay, stage_x11->xwin, META_STAGE_X11_EVENT_MASK); meta_seat_x11_select_stage_events (seat_x11, stage_cogl->wrapper); meta_stage_x11_fix_window_size (stage_x11, stage_x11->xwin_width, stage_x11->xwin_height); meta_stage_x11_set_wm_protocols (stage_x11); return TRUE; } static void meta_stage_x11_set_cursor_visible (ClutterStageWindow *stage_window, gboolean cursor_visible) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); stage_x11->is_cursor_visible = !!cursor_visible; set_cursor_visible (stage_x11); } static void meta_stage_x11_set_title (ClutterStageWindow *stage_window, const char *title) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); g_free (stage_x11->title); stage_x11->title = g_strdup (title); set_wm_title (stage_x11); } static inline void update_wm_hints (MetaStageX11 *stage_x11) { Display *xdisplay = clutter_x11_get_default_display (); XWMHints wm_hints; if (stage_x11->wm_state & STAGE_X11_WITHDRAWN) return; wm_hints.flags = StateHint | InputHint; wm_hints.initial_state = NormalState; wm_hints.input = stage_x11->accept_focus ? True : False; XSetWMHints (xdisplay, stage_x11->xwin, &wm_hints); } static void meta_stage_x11_set_accept_focus (ClutterStageWindow *stage_window, gboolean accept_focus) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); stage_x11->accept_focus = !!accept_focus; update_wm_hints (stage_x11); } static void set_stage_x11_state (MetaStageX11 *stage_x11, MetaStageX11State unset_flags, MetaStageX11State set_flags) { MetaStageX11State new_stage_state, old_stage_state; old_stage_state = stage_x11->wm_state; new_stage_state = old_stage_state; new_stage_state |= set_flags; new_stage_state &= ~unset_flags; if (new_stage_state == old_stage_state) return; stage_x11->wm_state = new_stage_state; } static void meta_stage_x11_show (ClutterStageWindow *stage_window, gboolean do_raise) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); if (stage_x11->xwin != None) { Display *xdisplay = clutter_x11_get_default_display (); if (do_raise) { XRaiseWindow (xdisplay, stage_x11->xwin); } if (!STAGE_X11_IS_MAPPED (stage_x11)) { set_stage_x11_state (stage_x11, STAGE_X11_WITHDRAWN, 0); update_wm_hints (stage_x11); } g_assert (STAGE_X11_IS_MAPPED (stage_x11)); clutter_actor_map (CLUTTER_ACTOR (stage_cogl->wrapper)); XMapWindow (xdisplay, stage_x11->xwin); } } static void meta_stage_x11_hide (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); if (stage_x11->xwin != None) { Display *xdisplay = clutter_x11_get_default_display (); if (STAGE_X11_IS_MAPPED (stage_x11)) set_stage_x11_state (stage_x11, 0, STAGE_X11_WITHDRAWN); g_assert (!STAGE_X11_IS_MAPPED (stage_x11)); clutter_actor_unmap (CLUTTER_ACTOR (stage_cogl->wrapper)); XWithdrawWindow (xdisplay, stage_x11->xwin, 0); } } static gboolean meta_stage_x11_can_clip_redraws (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); /* while resizing a window, clipped redraws are disabled in order to * avoid artefacts. */ return stage_x11->clipped_redraws_cool_off == 0; } static GList * meta_stage_x11_get_views (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); MetaRenderer *renderer = meta_backend_get_renderer (stage_x11->backend); return meta_renderer_get_views (renderer); } static int64_t meta_stage_x11_get_frame_counter (ClutterStageWindow *stage_window) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); return cogl_onscreen_get_frame_counter (stage_x11->onscreen); } static void meta_stage_x11_finalize (GObject *object) { MetaStageX11 *stage_x11 = META_STAGE_X11 (object); g_free (stage_x11->title); G_OBJECT_CLASS (meta_stage_x11_parent_class)->finalize (object); } static void meta_stage_x11_class_init (MetaStageX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); gobject_class->finalize = meta_stage_x11_finalize; } static void meta_stage_x11_init (MetaStageX11 *stage) { MetaRenderer *renderer; MetaRendererX11Cm *renderer_x11_cm; stage->xwin = None; stage->xwin_width = 640; stage->xwin_height = 480; stage->wm_state = STAGE_X11_WITHDRAWN; stage->is_cursor_visible = TRUE; stage->accept_focus = TRUE; stage->title = NULL; stage->backend = meta_get_backend (); g_assert (stage->backend); if (META_IS_BACKEND_X11_CM (stage->backend)) { renderer = meta_backend_get_renderer (stage->backend); renderer_x11_cm = META_RENDERER_X11_CM (renderer); meta_renderer_x11_cm_ensure_screen_view (renderer_x11_cm, stage->xwin_width, stage->xwin_height); } } static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) { clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); iface->set_title = meta_stage_x11_set_title; iface->set_cursor_visible = meta_stage_x11_set_cursor_visible; iface->set_accept_focus = meta_stage_x11_set_accept_focus; iface->show = meta_stage_x11_show; iface->hide = meta_stage_x11_hide; iface->resize = meta_stage_x11_resize; iface->get_geometry = meta_stage_x11_get_geometry; iface->realize = meta_stage_x11_realize; iface->unrealize = meta_stage_x11_unrealize; iface->can_clip_redraws = meta_stage_x11_can_clip_redraws; iface->get_views = meta_stage_x11_get_views; iface->get_frame_counter = meta_stage_x11_get_frame_counter; } static inline void set_user_time (ClutterBackendX11 *backend_x11, MetaStageX11 *stage_x11, long timestamp) { if (timestamp != CLUTTER_CURRENT_TIME) { Display *xdisplay = clutter_x11_get_default_display (); XChangeProperty (xdisplay, stage_x11->xwin, backend_x11->atom_NET_WM_USER_TIME, XA_CARDINAL, 32, PropModeReplace, (unsigned char *) ×tamp, 1); } } static gboolean handle_wm_protocols_event (ClutterBackendX11 *backend_x11, MetaStageX11 *stage_x11, XEvent *xevent) { Atom atom = (Atom) xevent->xclient.data.l[0]; if (atom == backend_x11->atom_WM_DELETE_WINDOW && xevent->xany.window == stage_x11->xwin) { set_user_time (backend_x11, stage_x11, xevent->xclient.data.l[1]); return TRUE; } else if (atom == backend_x11->atom_NET_WM_PING && xevent->xany.window == stage_x11->xwin) { XClientMessageEvent xclient = xevent->xclient; Display *xdisplay = clutter_x11_get_default_display (); xclient.window = backend_x11->xwin_root; XSendEvent (xdisplay, xclient.window, False, SubstructureRedirectMask | SubstructureNotifyMask, (XEvent *) &xclient); return FALSE; } /* do not send any of the WM_PROTOCOLS events to the queue */ return FALSE; } static gboolean clipped_redraws_cool_off_cb (void *data) { MetaStageX11 *stage_x11 = data; stage_x11->clipped_redraws_cool_off = 0; return G_SOURCE_REMOVE; } gboolean meta_stage_x11_translate_event (MetaStageX11 *stage_x11, XEvent *xevent, ClutterEvent *event) { ClutterStageCogl *stage_cogl; gboolean res = FALSE; ClutterBackendX11 *backend_x11; ClutterStage *stage; stage_cogl = meta_x11_get_stage_window_from_window (xevent->xany.window); if (stage_cogl == NULL) return FALSE; stage = stage_cogl->wrapper; backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); switch (xevent->type) { case ConfigureNotify: { gboolean size_changed = FALSE; int stage_width; int stage_height; g_debug ("ConfigureNotify[%x] (%d, %d)", (unsigned int) stage_x11->xwin, xevent->xconfigure.width, xevent->xconfigure.height); if ((stage_x11->xwin_width != xevent->xconfigure.width) || (stage_x11->xwin_height != xevent->xconfigure.height)) { size_changed = TRUE; stage_x11->xwin_width = xevent->xconfigure.width; stage_x11->xwin_height = xevent->xconfigure.height; } stage_width = xevent->xconfigure.width; stage_height = xevent->xconfigure.height; clutter_actor_set_size (CLUTTER_ACTOR (stage), stage_width, stage_height); if (size_changed) { /* XXX: This is a workaround for a race condition when * resizing windows while there are in-flight * glXCopySubBuffer blits happening. * * The problem stems from the fact that rectangles for the * blits are described relative to the bottom left of the * window and because we can't guarantee control over the X * window gravity used when resizing so the gravity is * typically NorthWest not SouthWest. * * This means if you grow a window vertically the server * will make sure to place the old contents of the window * at the top-left/north-west of your new larger window, but * that may happen asynchronous to GLX preparing to do a * blit specified relative to the bottom-left/south-west of * the window (based on the old smaller window geometry). * * When the GLX issued blit finally happens relative to the * new bottom of your window, the destination will have * shifted relative to the top-left where all the pixels you * care about are so it will result in a nasty artefact * making resizing look very ugly! * * We can't currently fix this completely, in-part because * the window manager tends to trample any gravity we might * set. This workaround instead simply disables blits for a * while if we are notified of any resizes happening so if * the user is resizing a window via the window manager then * they may see an artefact for one frame but then we will * fallback to redrawing the full stage until the cooling * off period is over. */ g_clear_handle_id (&stage_x11->clipped_redraws_cool_off, g_source_remove); stage_x11->clipped_redraws_cool_off = clutter_threads_add_timeout (1000, clipped_redraws_cool_off_cb, stage_x11); /* Queue a relayout - we want glViewport to be called * with the correct values, and this is done in ClutterStage * via cogl_onscreen_clutter_backend_set_size (). * * We queue a relayout, because if this ConfigureNotify is * in response to a size we set in the application, the * set_size() call above is essentially a null-op. * * Make sure we do this only when the size has changed, * otherwise we end up relayouting on window moves. */ clutter_actor_queue_relayout (CLUTTER_ACTOR (stage)); /* the resize process is complete, so we can ask the stage * to set up the GL viewport with the new size */ clutter_stage_ensure_viewport (stage); /* If this was a result of the Xrandr change when running as a * X11 compositing manager, we need to reset the legacy * stage view, now that it has a new size. */ if (META_IS_BACKEND_X11_CM (stage_x11->backend)) { MetaBackend *backend = stage_x11->backend; MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererX11Cm *renderer_x11_cm = META_RENDERER_X11_CM (renderer); meta_renderer_x11_cm_resize (renderer_x11_cm, stage_width, stage_height); } } } break; case FocusIn: if (!_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, 0, CLUTTER_STAGE_STATE_ACTIVATED); } break; case FocusOut: if (_clutter_stage_is_activated (stage_cogl->wrapper)) { _clutter_stage_update_state (stage_cogl->wrapper, CLUTTER_STAGE_STATE_ACTIVATED, 0); } break; case Expose: { XExposeEvent *expose = (XExposeEvent *) xevent; cairo_rectangle_int_t clip; g_debug ("expose for stage: win:0x%x - " "redrawing area (x: %d, y: %d, width: %d, height: %d)", (unsigned int) xevent->xany.window, expose->x, expose->y, expose->width, expose->height); clip.x = expose->x; clip.y = expose->y; clip.width = expose->width; clip.height = expose->height; clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (stage), &clip); } break; case DestroyNotify: g_debug ("Destroy notification received for stage, win:0x%x", (unsigned int) xevent->xany.window); event->any.type = CLUTTER_DESTROY_NOTIFY; event->any.stage = stage; res = TRUE; break; case ClientMessage: g_debug ("Client message for stage, win:0x%x", (unsigned int) xevent->xany.window); if (xevent->xclient.message_type == backend_x11->atom_WM_PROTOCOLS) { if (handle_wm_protocols_event (backend_x11, stage_x11, xevent)) { event->any.type = CLUTTER_DELETE; event->any.stage = stage; res = TRUE; } } break; default: res = FALSE; break; } return res; } Window meta_x11_get_stage_window (ClutterStage *stage) { ClutterStageWindow *impl; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), None); impl = _clutter_stage_get_window (stage); g_assert (META_IS_STAGE_X11 (impl)); return META_STAGE_X11 (impl)->xwin; } static ClutterStageCogl * meta_x11_get_stage_window_from_window (Window win) { if (clutter_stages_by_xid == NULL) return NULL; return g_hash_table_lookup (clutter_stages_by_xid, GINT_TO_POINTER (win)); } ClutterStage * meta_x11_get_stage_from_window (Window win) { ClutterStageCogl *stage_cogl; stage_cogl = meta_x11_get_stage_window_from_window (win); if (stage_cogl != NULL) return stage_cogl->wrapper; return NULL; } void meta_stage_x11_set_user_time (MetaStageX11 *stage_x11, uint32_t user_time) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); ClutterBackendX11 *backend_x11 = CLUTTER_BACKEND_X11 (stage_cogl->backend); set_user_time (backend_x11, stage_x11, user_time); } muffin-6.4.1/src/backends/x11/meta-input-settings-x11.c0000664000175000017500000010507614723361714021421 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "backends/x11/meta-input-settings-x11.h" #include #include #include #include #include #ifdef HAVE_LIBGUDEV #include #endif #include "backends/meta-logical-monitor.h" #include "backends/x11/meta-backend-x11.h" #include "core/display-private.h" #include "meta/meta-x11-errors.h" typedef struct _MetaInputSettingsX11Private { #ifdef HAVE_LIBGUDEV GUdevClient *udev_client; #endif } MetaInputSettingsX11Private; G_DEFINE_TYPE_WITH_PRIVATE (MetaInputSettingsX11, meta_input_settings_x11, META_TYPE_INPUT_SETTINGS) enum { SCROLL_METHOD_FIELD_2FG, SCROLL_METHOD_FIELD_EDGE, SCROLL_METHOD_FIELD_BUTTON, SCROLL_METHOD_NUM_FIELDS }; static void device_free_xdevice (gpointer user_data) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XDevice *xdev = user_data; meta_x11_error_trap_push (display->x11_display); XCloseDevice (xdisplay, xdev); meta_x11_error_trap_pop (display->x11_display); } static XDevice * device_ensure_xdevice (ClutterInputDevice *device) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); int device_id = clutter_input_device_get_device_id (device); XDevice *xdev = NULL; xdev = g_object_get_data (G_OBJECT (device), "meta-input-settings-xdevice"); if (xdev) return xdev; meta_x11_error_trap_push (display->x11_display); xdev = XOpenDevice (xdisplay, device_id); meta_x11_error_trap_pop (display->x11_display); if (xdev) { g_object_set_data_full (G_OBJECT (device), "meta-input-settings-xdevice", xdev, device_free_xdevice); } return xdev; } static void * get_property (ClutterInputDevice *device, const gchar *property, Atom type, int format, gulong nitems) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); gulong nitems_ret, bytes_after_ret; int rc, device_id, format_ret; Atom property_atom, type_ret; guchar *data_ret = NULL; property_atom = XInternAtom (xdisplay, property, True); if (!property_atom) return NULL; device_id = clutter_input_device_get_device_id (device); clutter_x11_trap_x_errors (); rc = XIGetProperty (xdisplay, device_id, property_atom, 0, 10, False, type, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, &data_ret); clutter_x11_untrap_x_errors (); if (rc == Success && type_ret == type && format_ret == format && nitems_ret >= nitems) { return data_ret; } meta_XFree (data_ret); return NULL; } static void change_property (ClutterInputDevice *device, const gchar *property, Atom type, int format, void *data, gulong nitems) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); int device_id; Atom property_atom; guchar *data_ret; property_atom = XInternAtom (xdisplay, property, True); if (!property_atom) return; device_id = clutter_input_device_get_device_id (device); data_ret = get_property (device, property, type, format, nitems); if (!data_ret) return; XIChangeProperty (xdisplay, device_id, property_atom, type, format, XIPropModeReplace, data, nitems); meta_XFree (data_ret); } static void meta_input_settings_x11_set_send_events (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopDeviceSendEvents mode) { guchar values[2] = { 0 }; /* disabled, disabled-on-external-mouse */ guchar *available; available = get_property (device, "libinput Send Events Modes Available", XA_INTEGER, 8, 2); switch (mode) { case C_DESKTOP_DEVICE_SEND_EVENTS_DISABLED: values[0] = 1; break; case C_DESKTOP_DEVICE_SEND_EVENTS_DISABLED_ON_EXTERNAL_MOUSE: values[1] = 1; break; default: break; } change_property (device, "Synaptics Off", XA_INTEGER, 8, &values, 1); if (!available) return; if ((values[0] && !available[0]) || (values[1] && !available[1])) g_warning ("Device '%s' does not support sendevents mode %d\n", clutter_input_device_get_device_name (device), mode); else change_property (device, "libinput Send Events Mode Enabled", XA_INTEGER, 8, &values, 2); meta_XFree (available); } static void meta_input_settings_x11_set_matrix (MetaInputSettings *settings, ClutterInputDevice *device, gfloat matrix[6]) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); gfloat full_matrix[9] = { matrix[0], matrix[1], matrix[2], matrix[3], matrix[4], matrix[5], 0, 0, 1 }; change_property (device, "Coordinate Transformation Matrix", XInternAtom (xdisplay, "FLOAT", False), 32, &full_matrix, 9); } static void meta_input_settings_x11_set_speed (MetaInputSettings *settings, ClutterInputDevice *device, gdouble speed) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); gfloat *accel = get_property (device, "libinput Accel Speed", XInternAtom (xdisplay, "FLOAT", False), 32, 1); if (accel) { *accel = speed; /* Sounds confused, but libinput is confused. */ change_property (device, "libinput Accel Speed", XInternAtom (xdisplay, "FLOAT", False), 32, accel, 1); meta_XFree (accel); } else { gfloat scale = (speed <= -1.0) ? 1.0 : (speed + 1.0) * 12.5; change_property (device, "Device Accel Velocity Scaling", XInternAtom (xdisplay, "FLOAT", False), 32, &scale, 1); } } static void meta_input_settings_x11_set_left_handed (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { ClutterInputDeviceType device_type; guchar value; device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE) { value = enabled ? 3 : 0; change_property (device, "Wacom Rotation", XA_INTEGER, 8, &value, 1); } else { value = enabled ? 1 : 0; change_property (device, "libinput Left Handed Enabled", XA_INTEGER, 8, &value, 1); } } static void meta_input_settings_x11_set_disable_while_typing (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = (enabled) ? 1 : 0; change_property (device, "libinput Disable While Typing Enabled", XA_INTEGER, 8, &value, 1); change_property (device, "Synaptics Palm Detection", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_tap_enabled (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = (enabled) ? 1 : 0; gint32 tap_time = (enabled) ? 180 : 0; change_property (device, "libinput Tapping Enabled", XA_INTEGER, 8, &value, 1); change_property (device, "Synaptics Tap Time", XA_INTEGER, 32, &tap_time, 1); } static void meta_input_settings_x11_set_tap_and_drag_enabled (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = (enabled) ? 1 : 0; change_property (device, "libinput Tapping Drag Enabled", XA_INTEGER, 8, &value, 1); change_property (device, "Synaptics Gestures", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_invert_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean inverted) { gint32 *scroll_dist = get_property (device, "Synaptics Scrolling Distance", XA_INTEGER, 32, 2); if (scroll_dist) { scroll_dist[0] = inverted ? -ABS (scroll_dist[0]) : ABS (scroll_dist[0]); change_property (device, "Synaptics Scrolling Distance", XA_INTEGER, 32, scroll_dist, 2); meta_XFree (scroll_dist); } else { guchar value = (inverted) ? 1 : 0; change_property (device, "libinput Natural Scrolling Enabled", XA_INTEGER, 8, &value, 1); } } static void meta_input_settings_x11_set_edge_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean edge_scroll_enabled) { guchar values[SCROLL_METHOD_NUM_FIELDS] = { 0 }; /* 2fg, edge, button. The last value is unused */ guchar *current = NULL; guchar *available = NULL; current = get_property (device, "Synaptics Edge Scrolling", XA_INTEGER, 8, 3); if (current) { current[0] = current[1] = !!edge_scroll_enabled; change_property (device, "Synaptics Edge Scrolling", XA_INTEGER, 8, current, 3); goto out; } available = get_property (device, "libinput Scroll Methods Available", XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); if (!available || !available[SCROLL_METHOD_FIELD_EDGE]) goto out; current = get_property (device, "libinput Scroll Method Enabled", XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); if (!current) goto out; memcpy (values, current, SCROLL_METHOD_NUM_FIELDS); values[SCROLL_METHOD_FIELD_EDGE] = !!edge_scroll_enabled; change_property (device, "libinput Scroll Method Enabled", XA_INTEGER, 8, &values, SCROLL_METHOD_NUM_FIELDS); out: meta_XFree (current); meta_XFree (available); } static void meta_input_settings_x11_set_two_finger_scroll (MetaInputSettings *settings, ClutterInputDevice *device, gboolean two_finger_scroll_enabled) { guchar values[SCROLL_METHOD_NUM_FIELDS] = { 0 }; /* 2fg, edge, button. The last value is unused */ guchar *current = NULL; guchar *available = NULL; current = get_property (device, "Synaptics Two-Finger Scrolling", XA_INTEGER, 8, 2); if (current) { current[0] = current[1] = !!two_finger_scroll_enabled; change_property (device, "Synaptics Two-Finger Scrolling", XA_INTEGER, 8, current, 2); goto out; } available = get_property (device, "libinput Scroll Methods Available", XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); if (!available || !available[SCROLL_METHOD_FIELD_2FG]) goto out; current = get_property (device, "libinput Scroll Method Enabled", XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); if (!current) goto out; memcpy (values, current, SCROLL_METHOD_NUM_FIELDS); values[SCROLL_METHOD_FIELD_2FG] = !!two_finger_scroll_enabled; change_property (device, "libinput Scroll Method Enabled", XA_INTEGER, 8, &values, SCROLL_METHOD_NUM_FIELDS); out: meta_XFree (current); meta_XFree (available); } static gboolean meta_input_settings_x11_has_two_finger_scroll (MetaInputSettings *settings, ClutterInputDevice *device) { guchar *available = NULL; gboolean has_two_finger = TRUE; available = get_property (device, "Synaptics Two-Finger Scrolling", XA_INTEGER, 8, 2); if (available) { has_two_finger = available[0] || available[1]; } else { available = get_property (device, "libinput Scroll Methods Available", XA_INTEGER, 8, SCROLL_METHOD_NUM_FIELDS); if (!available || !available[SCROLL_METHOD_FIELD_2FG]) has_two_finger = FALSE; } meta_XFree (available); return has_two_finger; } static void meta_input_settings_x11_set_scroll_button (MetaInputSettings *settings, ClutterInputDevice *device, guint button) { change_property (device, "libinput Button Scrolling Button", XA_INTEGER, 32, &button, 1); } static void meta_input_settings_x11_set_click_method_synaptics (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTouchpadClickMethod mode) { /* { RT corner, RB, LT, LB, 1 finger, 2 fingers, 3 fingers } */ guchar tap_action_default[7] = { 2, 3, 0, 0, 1, 3, 0 }; guchar tap_action_areas[7] = { 2, 3, 0, 0, 1, 0, 0 }; guchar tap_action_fingers[7] = { 0, 0, 0, 0, 1, 3, 2 }; guchar tap_action_none[7] = { 0, 0, 0, 0, 1, 0, 0 }; guchar *tap_action = tap_action_default; /* TODO On startup save the default value of 'Synaptics Soft Button Areas', but save it where? */ gint32 zero_button_areas[8] = { 0, 0, 0, 0, 0, 0, 0, 0 }; gint32 *button_areas_known = NULL; switch (mode) { case C_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT: tap_action = tap_action_default; /* Doing nothing right now will give the correct default, unless changed during the session */ break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE: tap_action = tap_action_none; button_areas_known = zero_button_areas; break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS: tap_action = tap_action_areas; /* Doing nothing right now will give the correct default, unless changed during the session */ break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_FINGERS: tap_action = tap_action_fingers; button_areas_known = zero_button_areas; break; default: g_assert_not_reached (); return; } change_property (device, "Synaptics Tap Action", XA_INTEGER, 8, tap_action, 7); change_property (device, "Synaptics Click Action", XA_INTEGER, 8, tap_action + 4, 3); if (button_areas_known) change_property (device, "Synaptics Soft Button Areas", XA_INTEGER, 32, button_areas_known, 8); } static void meta_input_settings_x11_set_click_method (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTouchpadClickMethod mode) { guchar values[2] = { 0 }; /* buttonareas, clickfinger */ guchar *defaults, *available; available = get_property (device, "libinput Click Methods Available", XA_INTEGER, 8, 2); if (!available) { meta_input_settings_x11_set_click_method_synaptics (settings, device, mode); return; } switch (mode) { case C_DESKTOP_TOUCHPAD_CLICK_METHOD_DEFAULT: defaults = get_property (device, "libinput Click Method Enabled Default", XA_INTEGER, 8, 2); if (!defaults) break; memcpy (values, defaults, 2); meta_XFree (defaults); break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_NONE: break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_AREAS: values[0] = 1; break; case C_DESKTOP_TOUCHPAD_CLICK_METHOD_FINGERS: values[1] = 1; break; default: g_assert_not_reached (); return; } if ((values[0] && !available[0]) || (values[1] && !available[1])) g_warning ("Device '%s' does not support click method %d\n", clutter_input_device_get_device_name (device), mode); else change_property (device, "libinput Click Method Enabled", XA_INTEGER, 8, &values, 2); meta_XFree(available); } static void meta_input_settings_x11_set_keyboard_repeat (MetaInputSettings *settings, gboolean enabled, guint delay, guint interval) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); if (enabled) { XAutoRepeatOn (xdisplay); XkbSetAutoRepeatRate (xdisplay, XkbUseCoreKbd, delay, interval); } else { XAutoRepeatOff (xdisplay); } } static gboolean has_udev_property (MetaInputSettings *settings, ClutterInputDevice *device, const char *property_name) { #ifdef HAVE_LIBGUDEV MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (settings); MetaInputSettingsX11Private *priv = meta_input_settings_x11_get_instance_private (settings_x11); const char *device_node; GUdevDevice *udev_device = NULL; GUdevDevice *parent_udev_device = NULL; device_node = clutter_input_device_get_device_node (device); if (!device_node) return FALSE; udev_device = g_udev_client_query_by_device_file (priv->udev_client, device_node); if (!udev_device) return FALSE; if (NULL != g_udev_device_get_property (udev_device, property_name)) { g_object_unref (udev_device); return TRUE; } parent_udev_device = g_udev_device_get_parent (udev_device); g_object_unref (udev_device); if (!parent_udev_device) return FALSE; if (NULL != g_udev_device_get_property (parent_udev_device, property_name)) { g_object_unref (parent_udev_device); return TRUE; } g_object_unref (parent_udev_device); return FALSE; #else static gboolean warned_once = FALSE; if (!warned_once) { g_warning ("Failed to query property: no udev support"); warned_once = TRUE; } return FALSE; #endif } static gboolean is_mouse (MetaInputSettings *settings, ClutterInputDevice *device) { return (has_udev_property (settings, device, "ID_INPUT_MOUSE") && !has_udev_property (settings, device, "ID_INPUT_POINTINGSTICK")); } static gboolean meta_input_settings_x11_is_touchpad_device (MetaInputSettings *settings, ClutterInputDevice *device) { return has_udev_property (settings, device, "ID_INPUT_TOUCHPAD"); } static gboolean meta_input_settings_x11_is_trackball_device (MetaInputSettings *settings, ClutterInputDevice *device) { return has_udev_property (settings, device, "ID_INPUT_TRACKBALL"); } static void set_device_accel_profile (ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { guchar *defaults, *available; guchar values[2] = { 0 }; /* adaptive, flat */ defaults = get_property (device, "libinput Accel Profile Enabled Default", XA_INTEGER, 8, 2); if (!defaults) return; available = get_property (device, "libinput Accel Profiles Available", XA_INTEGER, 8, 2); if (!available) goto err_available; switch (profile) { case C_DESKTOP_POINTER_ACCEL_PROFILE_FLAT: values[0] = 0; values[1] = 1; break; case C_DESKTOP_POINTER_ACCEL_PROFILE_ADAPTIVE: values[0] = 1; values[1] = 0; break; default: g_warn_if_reached (); case C_DESKTOP_POINTER_ACCEL_PROFILE_DEFAULT: values[0] = defaults[0]; values[1] = defaults[1]; break; } change_property (device, "libinput Accel Profile Enabled", XA_INTEGER, 8, &values, 2); meta_XFree (available); err_available: meta_XFree (defaults); } static void meta_input_settings_x11_set_mouse_accel_profile (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { if (!is_mouse (settings, device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_x11_set_trackball_accel_profile (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopPointerAccelProfile profile) { if (!meta_input_settings_x11_is_trackball_device (settings, device)) return; set_device_accel_profile (device, profile); } static void meta_input_settings_x11_set_tablet_mapping (MetaInputSettings *settings, ClutterInputDevice *device, CDesktopTabletMapping mapping) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XDevice *xdev; if (!display) return; /* Grab the puke bucket! */ meta_x11_error_trap_push (display->x11_display); xdev = device_ensure_xdevice (device); if (xdev) { XSetDeviceMode (xdisplay, xdev, mapping == C_DESKTOP_TABLET_MAPPING_ABSOLUTE ? Absolute : Relative); } if (meta_x11_error_trap_pop_with_return (display->x11_display)) { g_warning ("Could not set tablet mapping for %s", clutter_input_device_get_device_name (device)); } else { ClutterInputDeviceMapping dev_mapping; dev_mapping = (mapping == C_DESKTOP_TABLET_MAPPING_ABSOLUTE) ? CLUTTER_INPUT_DEVICE_MAPPING_ABSOLUTE : CLUTTER_INPUT_DEVICE_MAPPING_RELATIVE; clutter_input_device_set_mapping_mode (device, dev_mapping); } } static gboolean device_query_area (ClutterInputDevice *device, gint *x, gint *y, gint *width, gint *height) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); gint device_id, n_devices, i; XIDeviceInfo *info; Atom abs_x, abs_y; *width = *height = 0; device_id = clutter_input_device_get_device_id (device); info = XIQueryDevice (xdisplay, device_id, &n_devices); if (n_devices <= 0 || !info) return FALSE; abs_x = XInternAtom (xdisplay, "Abs X", True); abs_y = XInternAtom (xdisplay, "Abs Y", True); for (i = 0; i < info->num_classes; i++) { XIValuatorClassInfo *valuator = (XIValuatorClassInfo *) info->classes[i]; if (valuator->type != XIValuatorClass) continue; if (valuator->label == abs_x) { *x = valuator->min; *width = valuator->max - valuator->min; } else if (valuator->label == abs_y) { *y = valuator->min; *height = valuator->max - valuator->min; } } XIFreeDeviceInfo (info); return TRUE; } static void update_tablet_area (MetaInputSettings *settings, ClutterInputDevice *device, gint32 *area) { change_property (device, "Wacom Tablet Area", XA_INTEGER, 32, area, 4); } static void meta_input_settings_x11_set_tablet_area (MetaInputSettings *settings, ClutterInputDevice *device, gdouble padding_left, gdouble padding_right, gdouble padding_top, gdouble padding_bottom) { gint32 x, y, width, height, area[4] = { 0 }; if (!device_query_area (device, &x, &y, &width, &height)) return; area[0] = (width * padding_left) + x; area[1] = (height * padding_top) + y; area[2] = width - (width * padding_right) + x; area[3] = height - (height * padding_bottom) + y; update_tablet_area (settings, device, area); } static void meta_input_settings_x11_set_tablet_keep_aspect (MetaInputSettings *settings, ClutterInputDevice *device, MetaLogicalMonitor *logical_monitor, gboolean keep_aspect) { gint32 width, height, dev_x, dev_y, dev_width, dev_height, area[4] = { 0 }; if (!device_query_area (device, &dev_x, &dev_y, &dev_width, &dev_height)) return; if (keep_aspect) { double aspect_ratio, dev_aspect; if (logical_monitor) { width = logical_monitor->rect.width; height = logical_monitor->rect.height; } else { MetaMonitorManager *monitor_manager; MetaBackend *backend; backend = meta_get_backend (); monitor_manager = meta_backend_get_monitor_manager (backend); meta_monitor_manager_get_screen_size (monitor_manager, &width, &height); } aspect_ratio = (double) width / height; dev_aspect = (double) dev_width / dev_height; if (dev_aspect > aspect_ratio) dev_width = dev_height * aspect_ratio; else if (dev_aspect < aspect_ratio) dev_height = dev_width / aspect_ratio; } area[0] = dev_x; area[1] = dev_y; area[2] = dev_width + dev_x; area[3] = dev_height + dev_y; update_tablet_area (settings, device, area); } static void meta_input_settings_x11_dispose (GObject *object) { #ifdef HAVE_LIBGUDEV MetaInputSettingsX11 *settings_x11 = META_INPUT_SETTINGS_X11 (object); MetaInputSettingsX11Private *priv = meta_input_settings_x11_get_instance_private (settings_x11); g_clear_object (&priv->udev_client); #endif G_OBJECT_CLASS (meta_input_settings_x11_parent_class)->dispose (object); } static guint action_to_button (CDesktopStylusButtonAction action, guint button) { switch (action) { case C_DESKTOP_STYLUS_BUTTON_ACTION_MIDDLE: return CLUTTER_BUTTON_MIDDLE; case C_DESKTOP_STYLUS_BUTTON_ACTION_RIGHT: return CLUTTER_BUTTON_SECONDARY; case C_DESKTOP_STYLUS_BUTTON_ACTION_BACK: return 8; case C_DESKTOP_STYLUS_BUTTON_ACTION_FORWARD: return 9; case C_DESKTOP_STYLUS_BUTTON_ACTION_DISABLED: return 0; case C_DESKTOP_STYLUS_BUTTON_ACTION_DEFAULT: default: return button; } } static void meta_input_settings_x11_set_stylus_button_map (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, CDesktopStylusButtonAction primary, CDesktopStylusButtonAction secondary, CDesktopStylusButtonAction tertiary) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); Display *xdisplay = meta_backend_x11_get_xdisplay (META_BACKEND_X11 (backend)); XDevice *xdev; if (!display) return; /* Grab the puke bucket! */ meta_x11_error_trap_push (display->x11_display); xdev = device_ensure_xdevice (device); if (xdev) { guchar map[8] = { CLUTTER_BUTTON_PRIMARY, action_to_button (primary, CLUTTER_BUTTON_MIDDLE), action_to_button (secondary, CLUTTER_BUTTON_SECONDARY), 4, 5, 6, 7, action_to_button (tertiary, 8), /* "Back" */ }; XSetDeviceButtonMapping (xdisplay, xdev, map, G_N_ELEMENTS (map)); } if (meta_x11_error_trap_pop_with_return (display->x11_display)) { g_warning ("Could not set stylus button map for %s", clutter_input_device_get_device_name (device)); } } static void meta_input_settings_x11_set_mouse_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = enabled ? 1 : 0; if (!is_mouse (settings, device)) return; change_property (device, "libinput Middle Emulation Enabled", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_touchpad_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = enabled ? 1 : 0; if (!meta_input_settings_x11_is_touchpad_device (settings, device)) return; change_property (device, "libinput Middle Emulation Enabled", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_trackball_middle_click_emulation (MetaInputSettings *settings, ClutterInputDevice *device, gboolean enabled) { guchar value = enabled ? 1 : 0; if (!meta_input_settings_x11_is_trackball_device (settings, device)) return; change_property (device, "libinput Middle Emulation Enabled", XA_INTEGER, 8, &value, 1); } static void meta_input_settings_x11_set_stylus_pressure (MetaInputSettings *settings, ClutterInputDevice *device, ClutterInputDeviceTool *tool, const gint32 pressure[4]) { guint32 values[4] = { pressure[0], pressure[1], pressure[2], pressure[3] }; change_property (device, "Wacom Pressurecurve", XA_INTEGER, 32, &values, G_N_ELEMENTS (values)); } static void meta_input_settings_x11_class_init (MetaInputSettingsX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaInputSettingsClass *input_settings_class = META_INPUT_SETTINGS_CLASS (klass); object_class->dispose = meta_input_settings_x11_dispose; input_settings_class->set_send_events = meta_input_settings_x11_set_send_events; input_settings_class->set_matrix = meta_input_settings_x11_set_matrix; input_settings_class->set_speed = meta_input_settings_x11_set_speed; input_settings_class->set_left_handed = meta_input_settings_x11_set_left_handed; input_settings_class->set_tap_enabled = meta_input_settings_x11_set_tap_enabled; input_settings_class->set_tap_and_drag_enabled = meta_input_settings_x11_set_tap_and_drag_enabled; input_settings_class->set_disable_while_typing = meta_input_settings_x11_set_disable_while_typing; input_settings_class->set_invert_scroll = meta_input_settings_x11_set_invert_scroll; input_settings_class->set_edge_scroll = meta_input_settings_x11_set_edge_scroll; input_settings_class->set_two_finger_scroll = meta_input_settings_x11_set_two_finger_scroll; input_settings_class->set_scroll_button = meta_input_settings_x11_set_scroll_button; input_settings_class->set_click_method = meta_input_settings_x11_set_click_method; input_settings_class->set_keyboard_repeat = meta_input_settings_x11_set_keyboard_repeat; input_settings_class->set_tablet_mapping = meta_input_settings_x11_set_tablet_mapping; input_settings_class->set_tablet_keep_aspect = meta_input_settings_x11_set_tablet_keep_aspect; input_settings_class->set_tablet_area = meta_input_settings_x11_set_tablet_area; input_settings_class->set_mouse_accel_profile = meta_input_settings_x11_set_mouse_accel_profile; input_settings_class->set_trackball_accel_profile = meta_input_settings_x11_set_trackball_accel_profile; input_settings_class->set_stylus_pressure = meta_input_settings_x11_set_stylus_pressure; input_settings_class->set_stylus_button_map = meta_input_settings_x11_set_stylus_button_map; input_settings_class->set_mouse_middle_click_emulation = meta_input_settings_x11_set_mouse_middle_click_emulation; input_settings_class->set_touchpad_middle_click_emulation = meta_input_settings_x11_set_touchpad_middle_click_emulation; input_settings_class->set_trackball_middle_click_emulation = meta_input_settings_x11_set_trackball_middle_click_emulation; input_settings_class->has_two_finger_scroll = meta_input_settings_x11_has_two_finger_scroll; input_settings_class->is_trackball_device = meta_input_settings_x11_is_trackball_device; } static void meta_input_settings_x11_init (MetaInputSettingsX11 *settings) { #ifdef HAVE_LIBGUDEV MetaInputSettingsX11Private *priv = meta_input_settings_x11_get_instance_private (settings); const char *subsystems[] = { NULL }; priv->udev_client = g_udev_client_new (subsystems); #endif } muffin-6.4.1/src/backends/x11/meta-crtc-xrandr.h0000664000175000017500000000505314723361714020243 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_CRTC_XRANDR_H #define META_CRTC_XRANDR_H #include #include #include "backends/meta-crtc.h" #include "backends/x11/meta-gpu-xrandr.h" gboolean meta_crtc_xrandr_set_config (MetaCrtc *crtc, xcb_randr_crtc_t xrandr_crtc, xcb_timestamp_t timestamp, int x, int y, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, xcb_randr_output_t *outputs, int n_outputs, xcb_timestamp_t *out_timestamp); gboolean meta_crtc_xrandr_is_assignment_changed (MetaCrtc *crtc, MetaCrtcInfo *crtc_info); MetaCrtcMode * meta_crtc_xrandr_get_current_mode (MetaCrtc *crtc); gboolean meta_crtc_xrandr_set_scale (MetaCrtc *crtc, xcb_randr_crtc_t xrandr_crtc, float scale); MetaCrtc * meta_create_xrandr_crtc (MetaGpuXrandr *gpu_xrandr, XRRCrtcInfo *xrandr_crtc, RRCrtc crtc_id, XRRScreenResources *resources, XRRCrtcTransformAttributes *transform_attributes, float scale_multiplier); #endif /* META_CRTC_XRANDR_H */ muffin-6.4.1/src/backends/x11/meta-monitor-manager-xrandr.c0000664000175000017500000014356614723361714022416 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-monitor-manager-xrandr * @title: MetaMonitorManagerXrandr * @short_description: A subclass of #MetaMonitorManager using XRadR * * #MetaMonitorManagerXrandr is a subclass of #MetaMonitorManager which * implements its functionality using the RandR X protocol. * * See also #MetaMonitorManagerKms for a native implementation using Linux DRM * and udev. */ #include "config.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include #include #include #include #include #include #include #include "backends/meta-crtc.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-output.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-crtc-xrandr.h" #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-output-xrandr.h" #include "clutter/clutter.h" #include "meta/main.h" #include "meta/meta-x11-errors.h" /* Look for DPI_FALLBACK in: * http://git.gnome.org/browse/gnome-settings-daemon/tree/plugins/xsettings/gsd-xsettings-manager.c * for the reasoning */ #define DPI_FALLBACK 96.0 #define RANDR_VERSION_FORMAT(major, minor) ((major * 100) + minor) #define RANDR_TILING_MIN_VERSION RANDR_VERSION_FORMAT (1, 5) #define RANDR_TRANSFORM_MIN_VERSION RANDR_VERSION_FORMAT (1, 3) struct _MetaMonitorManagerXrandr { MetaMonitorManager parent_instance; Display *xdisplay; int rr_event_base; int rr_error_base; int randr_version; xcb_timestamp_t last_xrandr_set_timestamp; GHashTable *tiled_monitor_atoms; }; static MetaGpu * meta_monitor_manager_xrandr_get_gpu (MetaMonitorManagerXrandr *manager_xrandr); struct _MetaMonitorManagerXrandrClass { MetaMonitorManagerClass parent_class; }; G_DEFINE_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META_TYPE_MONITOR_MANAGER); typedef struct _MetaMonitorXrandrData { Atom xrandr_name; } MetaMonitorXrandrData; GQuark quark_meta_monitor_xrandr_data; Display * meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xrandr) { return manager_xrandr->xdisplay; } uint32_t meta_monitor_manager_xrandr_get_config_timestamp (MetaMonitorManagerXrandr *manager_xrandr) { return manager_xrandr->last_xrandr_set_timestamp; } static GBytes * meta_monitor_manager_xrandr_read_edid (MetaMonitorManager *manager, MetaOutput *output) { return meta_output_xrandr_read_edid (output); } static MetaPowerSave x11_dpms_state_to_power_save (CARD16 dpms_state) { switch (dpms_state) { case DPMSModeOn: return META_POWER_SAVE_ON; case DPMSModeStandby: return META_POWER_SAVE_STANDBY; case DPMSModeSuspend: return META_POWER_SAVE_SUSPEND; case DPMSModeOff: return META_POWER_SAVE_OFF; default: return META_POWER_SAVE_UNSUPPORTED; } } static void meta_monitor_manager_xrandr_read_current_state (MetaMonitorManager *manager) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaMonitorManagerClass *parent_class = META_MONITOR_MANAGER_CLASS (meta_monitor_manager_xrandr_parent_class); Display *xdisplay = meta_monitor_manager_xrandr_get_xdisplay (manager_xrandr); BOOL dpms_capable, dpms_enabled; CARD16 dpms_state; MetaPowerSave power_save_mode; dpms_capable = DPMSCapable (xdisplay); if (dpms_capable && DPMSInfo (xdisplay, &dpms_state, &dpms_enabled) && dpms_enabled) power_save_mode = x11_dpms_state_to_power_save (dpms_state); else power_save_mode = META_POWER_SAVE_UNSUPPORTED; meta_monitor_manager_power_save_mode_changed (manager, power_save_mode); parent_class->read_current_state (manager); } static void meta_monitor_manager_xrandr_set_power_save_mode (MetaMonitorManager *manager, MetaPowerSave mode) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); CARD16 state; switch (mode) { case META_POWER_SAVE_ON: state = DPMSModeOn; break; case META_POWER_SAVE_STANDBY: state = DPMSModeStandby; break; case META_POWER_SAVE_SUSPEND: state = DPMSModeSuspend; break; case META_POWER_SAVE_OFF: state = DPMSModeOff; break; default: return; } DPMSForceLevel (manager_xrandr->xdisplay, state); DPMSSetTimeouts (manager_xrandr->xdisplay, 0, 0, 0); } static void meta_monitor_manager_xrandr_update_screen_size (MetaMonitorManagerXrandr *manager_xrandr, int width, int height, float scale) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); xcb_connection_t *xcb_conn; xcb_generic_error_t *xcb_error; xcb_void_cookie_t xcb_cookie; Screen *screen; int min_width; int min_height; int max_width; int max_height; int width_mm; int height_mm; g_assert (width > 0 && height > 0 && scale > 0); if (manager->screen_width == width && manager->screen_height == height) return; screen = ScreenOfDisplay (manager_xrandr->xdisplay, DefaultScreen (manager_xrandr->xdisplay)); meta_gpu_xrandr_get_min_screen_size (META_GPU_XRANDR (gpu), &min_width, &min_height); meta_gpu_xrandr_get_max_screen_size (META_GPU_XRANDR (gpu), &max_width, &max_height); width = MIN (MAX (min_width, width), max_width); height = MIN (MAX (min_height, height), max_height); /* The 'physical size' of an X screen is meaningless if that screen can * consist of many monitors. So just pick a size that make the dpi 96. * * Firefox and Evince apparently believe what X tells them. */ width_mm = (width / (DPI_FALLBACK * scale)) * 25.4 + 0.5; height_mm = (height / (DPI_FALLBACK * scale)) * 25.4 + 0.5; if (width == WidthOfScreen (screen) && height == HeightOfScreen (screen) && width_mm == WidthMMOfScreen (screen) && height_mm == HeightMMOfScreen (screen)) return; xcb_conn = XGetXCBConnection (manager_xrandr->xdisplay); xcb_grab_server (xcb_conn); /* Some drivers (nvidia I look at you!) might no advertise some CRTCs, so in * such case, we may ignore X errors here */ xcb_cookie = xcb_randr_set_screen_size_checked (xcb_conn, DefaultRootWindow (manager_xrandr->xdisplay), width, height, width_mm, height_mm); xcb_error = xcb_request_check (xcb_conn, xcb_cookie); if (!xcb_error) { manager->screen_width = width; manager->screen_height = height; } else { gchar buf[64]; XGetErrorText (manager_xrandr->xdisplay, xcb_error->error_code, buf, sizeof (buf) - 1); g_warning ("Impossible to resize screen at size %dx%d, error id %u: %s", width, height, xcb_error->error_code, buf); g_clear_pointer (&xcb_error, free); } xcb_ungrab_server (xcb_conn); } static xcb_randr_rotation_t meta_monitor_transform_to_xrandr (MetaMonitorTransform transform) { switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: return XCB_RANDR_ROTATION_ROTATE_0; case META_MONITOR_TRANSFORM_90: return XCB_RANDR_ROTATION_ROTATE_90; case META_MONITOR_TRANSFORM_180: return XCB_RANDR_ROTATION_ROTATE_180; case META_MONITOR_TRANSFORM_270: return XCB_RANDR_ROTATION_ROTATE_270; case META_MONITOR_TRANSFORM_FLIPPED: return XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_0; case META_MONITOR_TRANSFORM_FLIPPED_90: return XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_90; case META_MONITOR_TRANSFORM_FLIPPED_180: return XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_180; case META_MONITOR_TRANSFORM_FLIPPED_270: return XCB_RANDR_ROTATION_REFLECT_X | XCB_RANDR_ROTATION_ROTATE_270; } g_assert_not_reached (); return 0; } static gboolean xrandr_set_crtc_config (MetaMonitorManagerXrandr *manager_xrandr, MetaCrtc *crtc, gboolean save_timestamp, xcb_randr_crtc_t xrandr_crtc, xcb_timestamp_t timestamp, int x, int y, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, xcb_randr_output_t *outputs, int n_outputs) { xcb_timestamp_t new_timestamp; if (!meta_crtc_xrandr_set_config (crtc, xrandr_crtc, timestamp, x, y, mode, rotation, outputs, n_outputs, &new_timestamp)) return FALSE; if (save_timestamp) manager_xrandr->last_xrandr_set_timestamp = new_timestamp; return TRUE; } static float get_maximum_crtc_info_scale (MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos) { float max_scale = 1.0f; unsigned int i; for (i = 0; i < n_crtc_infos; i++) { MetaCrtcInfo *crtc_info = crtc_infos[i]; if (crtc_info->mode) max_scale = MAX (max_scale, crtc_info->scale); } return max_scale; } static gboolean is_crtc_assignment_changed (MetaMonitorManager *monitor_manager, MetaCrtc *crtc, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos, gboolean *weak_change) { MetaLogicalMonitorLayoutMode layout_mode; gboolean have_scaling; float max_crtc_scale = 1.0f; float max_req_scale = 1.0f; unsigned int i; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); have_scaling = meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; if (have_scaling && layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { max_crtc_scale = meta_monitor_manager_get_maximum_crtc_scale (monitor_manager); max_req_scale = get_maximum_crtc_info_scale (crtc_infos, n_crtc_infos); } for (i = 0; i < n_crtc_infos; i++) { MetaCrtcInfo *crtc_info = crtc_infos[i]; if (crtc_info->crtc != crtc) continue; if (meta_crtc_xrandr_is_assignment_changed (crtc, crtc_info)) return TRUE; if (have_scaling) { float crtc_scale = crtc->scale; float req_output_scale = crtc_info->scale; if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL) { if (fmodf (crtc_scale, 1.0) == 0.0f) { *weak_change = fabsf (crtc_scale - req_output_scale) > 0.001; return FALSE; } } else if (layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { /* In scale ui-down mode we need to check if the actual output * scale that will be applied to the crtc has actually changed * from the current value, so we need to compare the current crtc * scale with the scale that will be applied taking care of the * UI scale (max crtc scale) and of the requested maximum scale. * If we don't do this, we'd try to call randr calls which won't * ever trigger a RRScreenChangeNotify, as no actual change is * needed, and thus we won't ever emit a monitors-changed signal. */ crtc_scale /= ceilf (max_crtc_scale); req_output_scale /= ceilf (max_req_scale); } if (fabsf (crtc_scale - req_output_scale) > 0.001) return TRUE; } return FALSE; } return !!meta_crtc_xrandr_get_current_mode (crtc); } static gboolean is_output_assignment_changed (MetaOutput *output, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos, MetaOutputInfo **output_infos, unsigned int n_output_infos) { MetaCrtc *assigned_crtc; gboolean output_is_found = FALSE; unsigned int i; for (i = 0; i < n_output_infos; i++) { MetaOutputInfo *output_info = output_infos[i]; if (output_info->output != output) continue; if (output->is_primary != output_info->is_primary) return TRUE; if (output->is_presentation != output_info->is_presentation) return TRUE; if (output->is_underscanning != output_info->is_underscanning) return TRUE; output_is_found = TRUE; } assigned_crtc = meta_output_get_assigned_crtc (output); if (!output_is_found) return assigned_crtc != NULL; for (i = 0; i < n_crtc_infos; i++) { MetaCrtcInfo *crtc_info = crtc_infos[i]; unsigned int j; for (j = 0; j < crtc_info->outputs->len; j++) { MetaOutput *crtc_info_output = ((MetaOutput**) crtc_info->outputs->pdata)[j]; if (crtc_info_output == output && crtc_info->crtc == assigned_crtc) return FALSE; } } return TRUE; } static MetaGpu * meta_monitor_manager_xrandr_get_gpu (MetaMonitorManagerXrandr *manager_xrandr) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); MetaBackend *backend = meta_monitor_manager_get_backend (manager); return META_GPU (meta_backend_get_gpus (backend)->data); } static gboolean is_assignments_changed (MetaMonitorManager *manager, MetaCrtcInfo **crtc_infos, unsigned int n_crtc_infos, MetaOutputInfo **output_infos, unsigned int n_output_infos, gboolean *weak_change) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); GList *l; for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if (is_crtc_assignment_changed (manager, crtc, crtc_infos, n_crtc_infos, weak_change)) return TRUE; } for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; if (is_output_assignment_changed (output, crtc_infos, n_crtc_infos, output_infos, n_output_infos)) return TRUE; } if (meta_monitor_manager_get_default_layout_mode (manager) == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { /* If nothing has changed, ensure that the crtc logical scaling matches * with the requested one, as in case of global UI logical layout we might * assume that it is in fact equal, while it's techincally different. * Not doing this would then cause a wrong computation of the max crtc * scale and thus of the UI scaling. */ for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; unsigned int i; for (i = 0; i < n_crtc_infos; i++) { MetaCrtcInfo *crtc_info = crtc_infos[i]; if (crtc_info->crtc == crtc) { crtc->scale = crtc_info->scale; break; } } } } return FALSE; } static void apply_crtc_assignments (MetaMonitorManager *manager, gboolean save_timestamp, MetaCrtcInfo **crtcs, unsigned int n_crtcs, MetaOutputInfo **outputs, unsigned int n_outputs) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); unsigned i, valid_crtcs; GList *l; int width, height, scaled_width, scaled_height; float max_scale; float avg_screen_scale; gboolean have_scaling; XGrabServer (manager_xrandr->xdisplay); have_scaling = meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; /* Compute the new size of the screen (framebuffer) */ max_scale = get_maximum_crtc_info_scale (crtcs, n_crtcs); width = 0; height = 0; scaled_width = 0; scaled_height = 0; avg_screen_scale = 0; valid_crtcs = 0; for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; float scale = 1.0f; crtc->is_dirty = TRUE; if (crtc_info->mode == NULL) continue; if (have_scaling && scale_mode == META_X11_SCALE_MODE_UI_DOWN) scale = (ceilf (max_scale) / crtc_info->scale) * crtc_info->scale; else { scaled_width = MAX (scaled_width, crtc_info->layout.origin.x + crtc_info->layout.size.width * crtc_info->scale); scaled_height = MAX (scaled_height, crtc_info->layout.origin.y + crtc_info->layout.size.height * crtc_info->scale); } width = MAX (width, (int) roundf (crtc_info->layout.origin.x + crtc_info->layout.size.width * scale)); height = MAX (height, (int) roundf (crtc_info->layout.origin.y + crtc_info->layout.size.height * scale)); avg_screen_scale += (crtc_info->scale - avg_screen_scale) / (float) (++valid_crtcs); } /* Second disable all newly disabled CRTCs, or CRTCs that in the previous configuration would be outside the new framebuffer (otherwise X complains loudly when resizing) CRTC will be enabled again after resizing the FB */ for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; MetaCrtcConfig *crtc_config; int x2, y2; crtc_config = crtc->config; if (!crtc_config) continue; x2 = (int) roundf (crtc_config->layout.origin.x + crtc_config->layout.size.width); y2 = (int) roundf (crtc_config->layout.origin.y + crtc_config->layout.size.height); if (!crtc_info->mode || width < scaled_width || height < scaled_height || x2 > width || y2 > height) { xrandr_set_crtc_config (manager_xrandr, crtc, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); if (have_scaling) meta_crtc_xrandr_set_scale (crtc, (xcb_randr_crtc_t) crtc->crtc_id, 1.0f); meta_crtc_unset_config (crtc); crtc->scale = 1.0f; } } /* Disable CRTCs not mentioned in the list */ for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } if (!crtc->config) continue; xrandr_set_crtc_config (manager_xrandr, crtc, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, 0, 0, XCB_NONE, XCB_RANDR_ROTATION_ROTATE_0, NULL, 0); if (have_scaling) meta_crtc_xrandr_set_scale (crtc, (xcb_randr_crtc_t) crtc->crtc_id, 1.0f); meta_crtc_unset_config (crtc); crtc->scale = 1.0f; } if (!n_crtcs) goto out; if (width > 0 && height > 0) { meta_monitor_manager_xrandr_update_screen_size (manager_xrandr, width, height, avg_screen_scale); } for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; if (crtc_info->mode != NULL) { MetaCrtcMode *mode; g_autofree xcb_randr_output_t *output_ids = NULL; unsigned int j, n_output_ids; xcb_randr_rotation_t rotation; float scale = 1.0f; mode = crtc_info->mode; n_output_ids = crtc_info->outputs->len; output_ids = g_new (xcb_randr_output_t, n_output_ids); if (have_scaling && scale_mode != META_X11_SCALE_MODE_NONE) { scale = crtc_info->scale; if (scale_mode == META_X11_SCALE_MODE_UI_DOWN) scale /= ceilf (max_scale); } for (j = 0; j < n_output_ids; j++) { MetaOutput *output; output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; meta_output_assign_crtc (output, crtc); output_ids[j] = output->winsys_id; } if (have_scaling) { if (!meta_crtc_xrandr_set_scale (crtc, (xcb_randr_crtc_t) crtc->crtc_id, scale)) { meta_warning ("Scalig CRTC %d at %f failed\n", (unsigned)crtc->crtc_id, scale); } } rotation = meta_monitor_transform_to_xrandr (crtc_info->transform); if (!xrandr_set_crtc_config (manager_xrandr, crtc, save_timestamp, (xcb_randr_crtc_t) crtc->crtc_id, XCB_CURRENT_TIME, (int) roundf (crtc_info->layout.origin.x), (int) roundf (crtc_info->layout.origin.y), (xcb_randr_mode_t) mode->mode_id, rotation, output_ids, n_output_ids)) { meta_warning ("Configuring CRTC %d with mode %d (%d x %d @ %f) at position %d, %d and transform %u failed\n", (unsigned)(crtc->crtc_id), (unsigned)(mode->mode_id), mode->width, mode->height, (float)mode->refresh_rate, (int) roundf (crtc_info->layout.origin.x), (int) roundf (crtc_info->layout.origin.y), crtc_info->transform); continue; } meta_crtc_set_config (crtc, &crtc_info->layout, mode, crtc_info->transform); crtc->scale = crtc_info->scale; if (have_scaling && scale_mode == META_X11_SCALE_MODE_UI_DOWN) { scale = (ceilf (max_scale) / crtc_info->scale) * crtc_info->scale; crtc->config->layout.size.width = roundf (crtc->config->layout.size.width * scale); crtc->config->layout.size.height = roundf (crtc->config->layout.size.height * scale); } } } for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = outputs[i]; MetaOutput *output = output_info->output; output->is_primary = output_info->is_primary; output->is_presentation = output_info->is_presentation; output->is_underscanning = output_info->is_underscanning; meta_output_xrandr_apply_mode (output); } /* Disable outputs not mentioned in the list */ for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; if (output->is_dirty) { output->is_dirty = FALSE; continue; } meta_output_unassign_crtc (output); output->is_primary = FALSE; } out: XUngrabServer (manager_xrandr->xdisplay); XFlush (manager_xrandr->xdisplay); } static void meta_monitor_manager_xrandr_ensure_initial_config (MetaMonitorManager *manager) { MetaMonitorConfigManager *config_manager = meta_monitor_manager_get_config_manager (manager); MetaMonitorsConfig *config; meta_monitor_manager_ensure_configured (manager); /* * Normally we don't rebuild our data structures until we see the * RRScreenNotify event, but at least at startup we want to have the right * configuration immediately. */ meta_monitor_manager_read_current_state (manager); config = meta_monitor_config_manager_get_current (config_manager); meta_monitor_manager_update_logical_state_derived (manager, config); } static void meta_monitor_manager_xrandr_update_screen_size_derived (MetaMonitorManager *manager, MetaMonitorsConfig *config) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); int screen_width = 0; int screen_height = 0; unsigned n_crtcs = 0; float average_scale = 0; gboolean have_scaling; GList *l; have_scaling = meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; /* Compute the new size of the screen (framebuffer) */ for (l = manager->monitors; l != NULL; l = l->next) { MetaMonitor *monitor = l->data; MetaOutput *output = meta_monitor_get_main_output (monitor); MetaCrtc *crtc = meta_output_get_assigned_crtc (output); graphene_rect_t *crtc_layout; float scale = 1.0f; if (!crtc || !crtc->config) continue; if (!have_scaling) { /* When scaling up we should not reduce the screen size, or X will * fail miserably, while we must do it when scaling down, in order to * increase the available screen area we can use. */ scale = crtc->scale > 1.0f ? crtc->scale : 1.0f; } /* When computing the screen size from the crtc rects we don't have to * use inverted values when monitors are rotated, because this is already * taken in account in the crtc rectangles */ crtc_layout = &crtc->config->layout; screen_width = MAX (screen_width, crtc_layout->origin.x + roundf (crtc_layout->size.width * scale)); screen_height = MAX (screen_height, crtc_layout->origin.y + roundf (crtc_layout->size.height * scale)); ++n_crtcs; /* This value isn't completely exact, since it doesn't take care of the * actual crtc sizes, however, since w're going to use this only to set * the MM size of the screen, and given that this value is just an * estimation, we don't need to be super precise. */ average_scale += (crtc->scale - average_scale) / (float) n_crtcs; } if (screen_width > 0 && screen_height > 0) { meta_monitor_manager_xrandr_update_screen_size (manager_xrandr, screen_width, screen_height, average_scale); } } static void maybe_update_ui_scaling_factor (MetaMonitorManager *manager, MetaMonitorsConfig *config) { if (config->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL || manager->layout_mode == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); meta_settings_update_ui_scaling_factor (settings); } } static gboolean meta_monitor_manager_xrandr_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; if (!config) { if (!manager->in_init) apply_crtc_assignments (manager, TRUE, NULL, 0, NULL, 0); meta_monitor_manager_rebuild_derived (manager, NULL); return TRUE; } if (!meta_monitor_config_manager_assign (manager, config, &crtc_infos, &output_infos, error)) return FALSE; if (method != META_MONITORS_CONFIG_METHOD_VERIFY) { gboolean weak_change = FALSE; /* * If the assignment has not changed, we won't get any notification about * any new configuration from the X server; but we still need to update * our own configuration, as something not applicable in Xrandr might * have changed locally, such as the logical monitors scale. This means we * must check that our new assignment actually changes anything, otherwise * just update the logical state. * If we record a weak change it means that only UI scaling needs to be * updated and so that we don't have to reconfigure the CRTCs, but still * need to update the logical state. */ if (is_assignments_changed (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len, &weak_change)) { apply_crtc_assignments (manager, TRUE, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len); maybe_update_ui_scaling_factor (manager, config); } else { if (weak_change) maybe_update_ui_scaling_factor (manager, config); meta_monitor_manager_rebuild_derived (manager, config); } } g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); return TRUE; } static void meta_monitor_manager_xrandr_change_backlight (MetaMonitorManager *manager, MetaOutput *output, gint value) { meta_output_xrandr_change_backlight (output, value); } static void meta_monitor_manager_xrandr_get_crtc_gamma (MetaMonitorManager *manager, MetaCrtc *crtc, gsize *size, unsigned short **red, unsigned short **green, unsigned short **blue) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); XRRCrtcGamma *gamma; gamma = XRRGetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id); *size = gamma->size; *red = g_memdup2 (gamma->red, sizeof (unsigned short) * gamma->size); *green = g_memdup2 (gamma->green, sizeof (unsigned short) * gamma->size); *blue = g_memdup2 (gamma->blue, sizeof (unsigned short) * gamma->size); XRRFreeGamma (gamma); } static void meta_monitor_manager_xrandr_set_crtc_gamma (MetaMonitorManager *manager, MetaCrtc *crtc, gsize size, unsigned short *red, unsigned short *green, unsigned short *blue) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); XRRCrtcGamma *gamma; gamma = XRRAllocGamma (size); memcpy (gamma->red, red, sizeof (unsigned short) * size); memcpy (gamma->green, green, sizeof (unsigned short) * size); memcpy (gamma->blue, blue, sizeof (unsigned short) * size); XRRSetCrtcGamma (manager_xrandr->xdisplay, (XID)crtc->crtc_id, gamma); XRRFreeGamma (gamma); } static MetaMonitorXrandrData * meta_monitor_xrandr_data_from_monitor (MetaMonitor *monitor) { MetaMonitorXrandrData *monitor_xrandr_data; monitor_xrandr_data = g_object_get_qdata (G_OBJECT (monitor), quark_meta_monitor_xrandr_data); if (monitor_xrandr_data) return monitor_xrandr_data; monitor_xrandr_data = g_new0 (MetaMonitorXrandrData, 1); g_object_set_qdata_full (G_OBJECT (monitor), quark_meta_monitor_xrandr_data, monitor_xrandr_data, g_free); return monitor_xrandr_data; } static void meta_monitor_manager_xrandr_increase_monitor_count (MetaMonitorManagerXrandr *manager_xrandr, Atom name_atom) { int count; count = GPOINTER_TO_INT (g_hash_table_lookup (manager_xrandr->tiled_monitor_atoms, GSIZE_TO_POINTER (name_atom))); count++; g_hash_table_insert (manager_xrandr->tiled_monitor_atoms, GSIZE_TO_POINTER (name_atom), GINT_TO_POINTER (count)); } static int meta_monitor_manager_xrandr_decrease_monitor_count (MetaMonitorManagerXrandr *manager_xrandr, Atom name_atom) { int count; count = GPOINTER_TO_SIZE (g_hash_table_lookup (manager_xrandr->tiled_monitor_atoms, GSIZE_TO_POINTER (name_atom))); g_assert (count > 0); count--; g_hash_table_insert (manager_xrandr->tiled_monitor_atoms, GSIZE_TO_POINTER (name_atom), GINT_TO_POINTER (count)); return count; } static void meta_monitor_manager_xrandr_tiled_monitor_added (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaMonitorTiled *monitor_tiled = META_MONITOR_TILED (monitor); const char *product; char *name; uint32_t tile_group_id; MetaMonitorXrandrData *monitor_xrandr_data; Atom name_atom; XRRMonitorInfo *xrandr_monitor_info; GList *outputs; GList *l; int i; xcb_connection_t *xcb_conn; xcb_void_cookie_t cookie; if (!(meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_TILING)) return; product = meta_monitor_get_product (monitor); tile_group_id = meta_monitor_tiled_get_tile_group_id (monitor_tiled); if (product) name = g_strdup_printf ("%s-%d", product, tile_group_id); else name = g_strdup_printf ("Tiled-%d", tile_group_id); name_atom = XInternAtom (manager_xrandr->xdisplay, name, False); g_free (name); monitor_xrandr_data = meta_monitor_xrandr_data_from_monitor (monitor); monitor_xrandr_data->xrandr_name = name_atom; meta_monitor_manager_xrandr_increase_monitor_count (manager_xrandr, name_atom); outputs = meta_monitor_get_outputs (monitor); xrandr_monitor_info = XRRAllocateMonitor (manager_xrandr->xdisplay, g_list_length (outputs)); xrandr_monitor_info->name = name_atom; xrandr_monitor_info->primary = meta_monitor_is_primary (monitor); xrandr_monitor_info->automatic = True; for (l = outputs, i = 0; l; l = l->next, i++) { MetaOutput *output = l->data; xrandr_monitor_info->outputs[i] = output->winsys_id; } xcb_conn = XGetXCBConnection (manager_xrandr->xdisplay); cookie = xcb_randr_delete_monitor_checked (xcb_conn, DefaultRootWindow (manager_xrandr->xdisplay), name_atom); free (xcb_request_check (xcb_conn, cookie)); /* ignore DeleteMonitor errors */ XRRSetMonitor (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), xrandr_monitor_info); XRRFreeMonitors (xrandr_monitor_info); } static void meta_monitor_manager_xrandr_tiled_monitor_removed (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaMonitorXrandrData *monitor_xrandr_data; Atom monitor_name; int monitor_count; if (!(meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_TILING)) return; monitor_xrandr_data = meta_monitor_xrandr_data_from_monitor (monitor); monitor_name = monitor_xrandr_data->xrandr_name; monitor_count = meta_monitor_manager_xrandr_decrease_monitor_count (manager_xrandr, monitor_name); if (monitor_count == 0) XRRDeleteMonitor (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), monitor_name); } static void meta_monitor_manager_xrandr_init_monitors (MetaMonitorManagerXrandr *manager_xrandr) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); XRRMonitorInfo *m; int n, i; if (!(meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_TILING)) return; /* delete any tiled monitors setup, as mutter will want to recreate things in its image */ m = XRRGetMonitors (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), FALSE, &n); if (n == -1) return; for (i = 0; i < n; i++) { if (m[i].noutput > 1) XRRDeleteMonitor (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), m[i].name); } XRRFreeMonitors (m); } static gboolean meta_monitor_manager_xrandr_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { g_warn_if_fail ((crtc->all_transforms & transform) == transform); return TRUE; } static MetaMonitorScalesConstraint get_scale_constraints (MetaMonitorManager *manager) { MetaMonitorScalesConstraint constraints = 0; if (meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED) constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; return constraints; } static float meta_monitor_manager_xrandr_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { return meta_monitor_calculate_mode_scale (monitor, monitor_mode, get_scale_constraints (manager)); } static float * meta_monitor_manager_xrandr_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { return meta_monitor_calculate_supported_scales (monitor, monitor_mode, get_scale_constraints (manager), n_supported_scales); } static MetaMonitorManagerCapability meta_monitor_manager_xrandr_get_capabilities (MetaMonitorManager *manager) { MetaMonitorManagerCapability capabilities; MetaMonitorManagerXrandr *xrandr_manager = META_MONITOR_MANAGER_XRANDR (manager); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); capabilities = META_MONITOR_MANAGER_CAPABILITY_NONE; if (xrandr_manager->randr_version >= RANDR_TILING_MIN_VERSION) capabilities |= META_MONITOR_MANAGER_CAPABILITY_TILING; if (xrandr_manager->randr_version >= RANDR_TRANSFORM_MIN_VERSION) capabilities |= META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING; if (meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) { capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; } else { capabilities |= META_MONITOR_MANAGER_CAPABILITY_GLOBAL_SCALE_REQUIRED; } return capabilities; } static gboolean meta_monitor_manager_xrandr_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (manager); MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); meta_gpu_xrandr_get_max_screen_size (META_GPU_XRANDR (gpu), max_width, max_height); return TRUE; } static void scale_mode_changed (MetaSettings *settings, MetaMonitorManager *manager) { if (!(meta_monitor_manager_get_capabilities (manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) return; if (!meta_settings_is_experimental_feature_enabled (settings, META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING)) return; meta_monitor_manager_on_hotplug (manager); meta_settings_update_ui_scaling_factor (settings); } static MetaLogicalMonitorLayoutMode meta_monitor_manager_xrandr_get_default_layout_mode (MetaMonitorManager *manager) { MetaMonitorManagerCapability capabilities = meta_monitor_manager_get_capabilities (manager); if ((capabilities & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING) && (capabilities & META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE)) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaSettings *settings = meta_backend_get_settings (backend); MetaX11ScaleMode scale_mode = meta_settings_get_x11_scale_mode (settings); if (scale_mode == META_X11_SCALE_MODE_UI_DOWN) return META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL; else if (scale_mode == META_X11_SCALE_MODE_UP) return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; } return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static void meta_monitor_manager_xrandr_constructed (GObject *object) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object); MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); MetaSettings *settings = meta_backend_get_settings (backend); manager_xrandr->xdisplay = meta_backend_x11_get_xdisplay (backend_x11); if (!XRRQueryExtension (manager_xrandr->xdisplay, &manager_xrandr->rr_event_base, &manager_xrandr->rr_error_base)) { return; } else { int major_version, minor_version; /* We only use ScreenChangeNotify, but GDK uses the others, and we don't want to step on its toes */ XRRSelectInput (manager_xrandr->xdisplay, DefaultRootWindow (manager_xrandr->xdisplay), RRScreenChangeNotifyMask | RRCrtcChangeNotifyMask | RROutputPropertyNotifyMask); XRRQueryVersion (manager_xrandr->xdisplay, &major_version, &minor_version); manager_xrandr->randr_version = RANDR_VERSION_FORMAT (major_version, minor_version); if (manager_xrandr->randr_version >= RANDR_TILING_MIN_VERSION) manager_xrandr->tiled_monitor_atoms = g_hash_table_new (NULL, NULL); meta_monitor_manager_xrandr_init_monitors (manager_xrandr); } g_signal_connect_object (settings, "x11-scale-mode-changed", G_CALLBACK (scale_mode_changed), manager_xrandr, 0); G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->constructed (object); } static void meta_monitor_manager_xrandr_finalize (GObject *object) { MetaMonitorManagerXrandr *manager_xrandr = META_MONITOR_MANAGER_XRANDR (object); g_hash_table_destroy (manager_xrandr->tiled_monitor_atoms); G_OBJECT_CLASS (meta_monitor_manager_xrandr_parent_class)->finalize (object); } static void meta_monitor_manager_xrandr_init (MetaMonitorManagerXrandr *manager_xrandr) { } static void meta_monitor_manager_xrandr_class_init (MetaMonitorManagerXrandrClass *klass) { MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_monitor_manager_xrandr_finalize; object_class->constructed = meta_monitor_manager_xrandr_constructed; manager_class->read_edid = meta_monitor_manager_xrandr_read_edid; manager_class->read_current_state = meta_monitor_manager_xrandr_read_current_state; manager_class->ensure_initial_config = meta_monitor_manager_xrandr_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_xrandr_apply_monitors_config; manager_class->update_screen_size_derived = meta_monitor_manager_xrandr_update_screen_size_derived; manager_class->set_power_save_mode = meta_monitor_manager_xrandr_set_power_save_mode; manager_class->change_backlight = meta_monitor_manager_xrandr_change_backlight; manager_class->get_crtc_gamma = meta_monitor_manager_xrandr_get_crtc_gamma; manager_class->set_crtc_gamma = meta_monitor_manager_xrandr_set_crtc_gamma; manager_class->tiled_monitor_added = meta_monitor_manager_xrandr_tiled_monitor_added; manager_class->tiled_monitor_removed = meta_monitor_manager_xrandr_tiled_monitor_removed; manager_class->is_transform_handled = meta_monitor_manager_xrandr_is_transform_handled; manager_class->calculate_monitor_mode_scale = meta_monitor_manager_xrandr_calculate_monitor_mode_scale; manager_class->calculate_supported_scales = meta_monitor_manager_xrandr_calculate_supported_scales; manager_class->get_capabilities = meta_monitor_manager_xrandr_get_capabilities; manager_class->get_max_screen_size = meta_monitor_manager_xrandr_get_max_screen_size; manager_class->get_default_layout_mode = meta_monitor_manager_xrandr_get_default_layout_mode; quark_meta_monitor_xrandr_data = g_quark_from_static_string ("-meta-monitor-xrandr-data"); } gboolean meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager_xrandr, XEvent *event) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_xrandr); MetaGpu *gpu = meta_monitor_manager_xrandr_get_gpu (manager_xrandr); MetaGpuXrandr *gpu_xrandr; XRRScreenResources *resources; gboolean is_hotplug; gboolean is_our_configuration; if ((event->type - manager_xrandr->rr_event_base) != RRScreenChangeNotify) return FALSE; XRRUpdateConfiguration (event); meta_monitor_manager_read_current_state (manager); gpu_xrandr = META_GPU_XRANDR (gpu); resources = meta_gpu_xrandr_get_resources (gpu_xrandr); is_hotplug = resources->timestamp < resources->configTimestamp; is_our_configuration = (resources->timestamp == manager_xrandr->last_xrandr_set_timestamp); if (is_hotplug) { meta_monitor_manager_on_hotplug (manager); } else { MetaMonitorsConfig *config; if (is_our_configuration) { MetaMonitorConfigManager *config_manager = meta_monitor_manager_get_config_manager (manager); config = meta_monitor_config_manager_get_current (config_manager); } else { config = NULL; } meta_monitor_manager_rebuild_derived (manager, config); } return TRUE; } muffin-6.4.1/src/backends/x11/meta-output-xrandr.h0000664000175000017500000000310014723361714020637 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_OUTPUT_XRANDR #define META_OUTPUT_XRANDR #include #include "backends/meta-output.h" #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-monitor-manager-xrandr.h" void meta_output_xrandr_apply_mode (MetaOutput *output); void meta_output_xrandr_change_backlight (MetaOutput *output, int value); GBytes * meta_output_xrandr_read_edid (MetaOutput *output); MetaOutput * meta_create_xrandr_output (MetaGpuXrandr *gpu_xrandr, XRROutputInfo *xrandr_output, RROutput output_id, RROutput primary_output); #endif /* META_OUTPUT_XRANDR */ muffin-6.4.1/src/backends/x11/nested/0000775000175000017500000000000014723361714016176 5ustar fabiofabiomuffin-6.4.1/src/backends/x11/nested/meta-cursor-renderer-x11-nested.h0000664000175000017500000000257314723361714024312 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_CURSOR_RENDERER_X11_NESTED_NESTED_H #define META_CURSOR_RENDERER_X11_NESTED_NESTED_H #include #include "backends/meta-cursor-renderer.h" #define META_TYPE_CURSOR_RENDERER_X11_NESTED (meta_cursor_renderer_x11_nested_get_type ()) G_DECLARE_FINAL_TYPE (MetaCursorRendererX11Nested, meta_cursor_renderer_x11_nested, META, CURSOR_RENDERER_X11_NESTED, MetaCursorRenderer); #endif /* META_CURSOR_RENDERER_X11_NESTED_NESTED_H */ muffin-6.4.1/src/backends/x11/nested/meta-backend-x11-nested.c0000664000175000017500000002311614723361714022547 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/x11/nested/meta-backend-x11-nested.h" #include "backends/meta-monitor-manager-dummy.h" #include "backends/x11/nested/meta-backend-x11-nested.h" #include "backends/x11/nested/meta-cursor-renderer-x11-nested.h" #include "backends/x11/nested/meta-renderer-x11-nested.h" #include "wayland/meta-wayland.h" typedef struct _MetaBackendX11NestedPrivate { MetaGpu *gpu; } MetaBackendX11NestedPrivate; static GInitableIface *initable_parent_iface; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaBackendX11Nested, meta_backend_x11_nested, META_TYPE_BACKEND_X11, G_ADD_PRIVATE (MetaBackendX11Nested) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); static MetaRenderer * meta_backend_x11_nested_create_renderer (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_RENDERER_X11_NESTED, "backend", backend, NULL); } static MetaMonitorManager * meta_backend_x11_nested_create_monitor_manager (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_MONITOR_MANAGER_DUMMY, "backend", backend, NULL); } static MetaCursorRenderer * meta_backend_x11_nested_create_cursor_renderer (MetaBackend *backend) { return g_object_new (META_TYPE_CURSOR_RENDERER_X11_NESTED, NULL); } static MetaInputSettings * meta_backend_x11_nested_create_input_settings (MetaBackend *backend) { return NULL; } static void meta_backend_x11_nested_update_screen_size (MetaBackend *backend, int width, int height) { ClutterActor *stage = meta_backend_get_stage (backend); MetaRenderer *renderer = meta_backend_get_renderer (backend); if (meta_is_stage_views_enabled ()) { meta_renderer_rebuild_views (renderer); clutter_stage_update_resource_scales (CLUTTER_STAGE (stage)); } clutter_actor_set_size (stage, width, height); } static void meta_backend_x11_nested_select_stage_events (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); Window xwin = meta_backend_x11_get_xwindow (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); XISetMask (mask.mask, XI_Motion); /* * When we're an X11 compositor, we can't take these events or else replaying * events from our passive root window grab will cause them to come back to * us. * * When we're a nested application, we want to behave like any other * application, so select these events like normal apps do. */ XISetMask (mask.mask, XI_TouchBegin); XISetMask (mask.mask, XI_TouchEnd); XISetMask (mask.mask, XI_TouchUpdate); XISelectEvents (xdisplay, xwin, &mask, 1); /* * We have no way of tracking key changes when the stage doesn't have focus, * so we select for KeymapStateMask so that we get a complete dump of the * keyboard state in a KeymapNotify event that immediately follows each * FocusIn (and EnterNotify, but we ignore that.) */ XWindowAttributes xwa; XGetWindowAttributes(xdisplay, xwin, &xwa); XSelectInput(xdisplay, xwin, xwa.your_event_mask | FocusChangeMask | KeymapStateMask); } static void meta_backend_x11_nested_lock_layout_group (MetaBackend *backend, guint idx) { } static void meta_backend_x11_nested_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options) { } static gboolean meta_backend_x11_nested_is_lid_closed (MetaBackend *backend) { return FALSE; } static gboolean meta_backend_x11_nested_handle_host_xevent (MetaBackendX11 *x11, XEvent *event) { #ifdef HAVE_WAYLAND if (event->type == FocusIn) { Window xwin = meta_backend_x11_get_xwindow (x11); XEvent xev; if (event->xfocus.window == xwin) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); /* * Since we've selected for KeymapStateMask, every FocusIn is * followed immediately by a KeymapNotify event. */ XMaskEvent (xdisplay, KeymapStateMask, &xev); meta_wayland_compositor_update_key_state (compositor, xev.xkeymap.key_vector, 32, 8); } } #endif return FALSE; } static void meta_backend_x11_nested_translate_device_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { /* This codepath should only ever trigger as an X11 compositor, * and never under nested, as under nested all backend events * should be reported with respect to the stage window. */ g_assert (device_event->event == meta_backend_x11_get_xwindow (x11)); } static void meta_backend_x11_nested_real_init_gpus (MetaBackendX11Nested *backend_x11_nested) { MetaBackendX11NestedPrivate *priv = meta_backend_x11_nested_get_instance_private (backend_x11_nested); priv->gpu = g_object_new (META_TYPE_GPU_DUMMY, "backend", backend_x11_nested, NULL); meta_backend_add_gpu (META_BACKEND (backend_x11_nested), priv->gpu); } static void meta_backend_x11_nested_post_init (MetaBackend *backend) { MetaBackendClass *backend_class = META_BACKEND_CLASS (meta_backend_x11_nested_parent_class); backend_class->post_init (backend); #ifdef HAVE_WAYLAND meta_backend_init_wayland (backend); #endif } static gboolean meta_backend_x11_nested_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { #ifdef HAVE_WAYLAND meta_backend_init_wayland_display (META_BACKEND (initable)); #endif return initable_parent_iface->init (initable, cancellable, error); } static void initable_iface_init (GInitableIface *initable_iface) { initable_parent_iface = g_type_interface_peek_parent (initable_iface); initable_iface->init = meta_backend_x11_nested_initable_init; } static void meta_backend_x11_nested_constructed (GObject *object) { MetaBackendX11Nested *backend_x11_nested = META_BACKEND_X11_NESTED (object); MetaBackendX11NestedClass *backend_x11_nested_class = META_BACKEND_X11_NESTED_GET_CLASS (backend_x11_nested); GObjectClass *parent_class = G_OBJECT_CLASS (meta_backend_x11_nested_parent_class); parent_class->constructed (object); backend_x11_nested_class->init_gpus (backend_x11_nested); } static void meta_backend_x11_nested_init (MetaBackendX11Nested *backend_x11_nested) { } static void meta_backend_x11_nested_class_init (MetaBackendX11NestedClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_CLASS (klass); object_class->constructed = meta_backend_x11_nested_constructed; backend_class->post_init = meta_backend_x11_nested_post_init; backend_class->create_renderer = meta_backend_x11_nested_create_renderer; backend_class->create_monitor_manager = meta_backend_x11_nested_create_monitor_manager; backend_class->create_cursor_renderer = meta_backend_x11_nested_create_cursor_renderer; backend_class->create_input_settings = meta_backend_x11_nested_create_input_settings; backend_class->update_screen_size = meta_backend_x11_nested_update_screen_size; backend_class->select_stage_events = meta_backend_x11_nested_select_stage_events; backend_class->lock_layout_group = meta_backend_x11_nested_lock_layout_group; backend_class->set_keymap = meta_backend_x11_nested_set_keymap; backend_class->is_lid_closed = meta_backend_x11_nested_is_lid_closed; backend_x11_class->handle_host_xevent = meta_backend_x11_nested_handle_host_xevent; backend_x11_class->translate_device_event = meta_backend_x11_nested_translate_device_event; klass->init_gpus = meta_backend_x11_nested_real_init_gpus; } muffin-6.4.1/src/backends/x11/nested/meta-cursor-renderer-x11-nested.c0000664000175000017500000000505114723361714024277 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "backends/x11/nested/meta-cursor-renderer-x11-nested.h" #include #include "backends/x11/meta-backend-x11.h" struct _MetaCursorRendererX11Nested { MetaCursorRenderer parent; }; G_DEFINE_TYPE (MetaCursorRendererX11Nested, meta_cursor_renderer_x11_nested, META_TYPE_CURSOR_RENDERER); static gboolean meta_cursor_renderer_x11_nested_update_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { if (cursor_sprite) meta_cursor_sprite_realize_texture (cursor_sprite); return FALSE; } static Cursor create_empty_cursor (Display *xdisplay) { XcursorImage *image; XcursorPixel *pixels; Cursor xcursor; image = XcursorImageCreate (1, 1); if (image == NULL) return None; image->xhot = 0; image->yhot = 0; pixels = image->pixels; pixels[0] = 0; xcursor = XcursorImageLoadCursor (xdisplay, image); XcursorImageDestroy (image); return xcursor; } static void meta_cursor_renderer_x11_nested_init (MetaCursorRendererX11Nested *x11_nested) { MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Window xwindow = meta_backend_x11_get_xwindow (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); Cursor empty_xcursor = create_empty_cursor (xdisplay); XDefineCursor (xdisplay, xwindow, empty_xcursor); XFreeCursor (xdisplay, empty_xcursor); } static void meta_cursor_renderer_x11_nested_class_init (MetaCursorRendererX11NestedClass *klass) { MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_CLASS (klass); renderer_class->update_cursor = meta_cursor_renderer_x11_nested_update_cursor; } muffin-6.4.1/src/backends/x11/nested/meta-renderer-x11-nested.c0000664000175000017500000001745714723361714023001 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/x11/nested/meta-renderer-x11-nested.h" #include #include "clutter/x11/clutter-x11.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-output.h" #include "backends/meta-renderer.h" #include "backends/meta-renderer-view.h" #include "core/boxes-private.h" #include "meta/meta-backend.h" #include "meta/util.h" struct _MetaRendererX11Nested { MetaRendererX11 parent; }; G_DEFINE_TYPE (MetaRendererX11Nested, meta_renderer_x11_nested, META_TYPE_RENDERER_X11) static MetaMonitorTransform calculate_view_transform (MetaMonitorManager *monitor_manager, MetaLogicalMonitor *logical_monitor) { MetaMonitor *main_monitor; MetaOutput *main_output; MetaCrtc *crtc; MetaMonitorTransform crtc_transform; main_monitor = meta_logical_monitor_get_monitors (logical_monitor)->data; main_output = meta_monitor_get_main_output (main_monitor); crtc = meta_output_get_assigned_crtc (main_output); crtc_transform = meta_monitor_logical_to_crtc_transform (main_monitor, logical_monitor->transform); /* * Pick any monitor and output and check; all CRTCs of a logical monitor will * always have the same transform assigned to them. */ if (meta_monitor_manager_is_transform_handled (monitor_manager, crtc, crtc_transform)) return META_MONITOR_TRANSFORM_NORMAL; else return crtc_transform; } static MetaRendererView * get_legacy_view (MetaRenderer *renderer) { GList *views; views = meta_renderer_get_views (renderer); if (views) return META_RENDERER_VIEW (views->data); else return NULL; } static CoglOffscreen * create_offscreen (CoglContext *cogl_context, int width, int height) { CoglTexture2D *texture_2d; CoglOffscreen *offscreen; GError *error = NULL; texture_2d = cogl_texture_2d_new_with_size (cogl_context, width, height); offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture_2d)); if (!cogl_framebuffer_allocate (COGL_FRAMEBUFFER (offscreen), &error)) meta_fatal ("Couldn't allocate framebuffer: %s", error->message); return offscreen; } static void meta_renderer_x11_nested_resize_legacy_view (MetaRendererX11Nested *renderer_x11_nested, int width, int height) { MetaRenderer *renderer = META_RENDERER (renderer_x11_nested); MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); MetaRendererView *legacy_view; cairo_rectangle_int_t view_layout; CoglOffscreen *fake_onscreen; legacy_view = get_legacy_view (renderer); clutter_stage_view_get_layout (CLUTTER_STAGE_VIEW (legacy_view), &view_layout); if (view_layout.width == width && view_layout.height == height) return; view_layout = (cairo_rectangle_int_t) { .width = width, .height = height }; fake_onscreen = create_offscreen (cogl_context, width, height); g_object_set (G_OBJECT (legacy_view), "layout", &view_layout, "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), NULL); } void meta_renderer_x11_nested_ensure_legacy_view (MetaRendererX11Nested *renderer_x11_nested, int width, int height) { MetaRenderer *renderer = META_RENDERER (renderer_x11_nested); MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); cairo_rectangle_int_t view_layout; CoglOffscreen *fake_onscreen; MetaRendererView *legacy_view; if (get_legacy_view (renderer)) { meta_renderer_x11_nested_resize_legacy_view (renderer_x11_nested, width, height); return; } fake_onscreen = create_offscreen (cogl_context, width, height); view_layout = (cairo_rectangle_int_t) { .width = width, .height = height }; legacy_view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &view_layout, "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), NULL); g_assert (!meta_renderer_get_views (renderer)); meta_renderer_add_view (renderer, legacy_view); } static MetaRendererView * meta_renderer_x11_nested_create_view (MetaRenderer *renderer, MetaLogicalMonitor *logical_monitor, MetaOutput *output, MetaCrtc *crtc) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); MetaMonitorTransform view_transform; float view_scale; int width, height; CoglOffscreen *fake_onscreen; CoglOffscreen *offscreen; MetaRectangle view_layout; MetaRendererView *view; view_transform = calculate_view_transform (monitor_manager, logical_monitor); if (meta_is_stage_views_scaled ()) view_scale = logical_monitor->scale; else view_scale = 1.0; width = roundf (crtc->config->layout.size.width * view_scale); height = roundf (crtc->config->layout.size.height * view_scale); fake_onscreen = create_offscreen (cogl_context, width, height); if (view_transform != META_MONITOR_TRANSFORM_NORMAL) offscreen = create_offscreen (cogl_context, width, height); else offscreen = NULL; meta_rectangle_from_graphene_rect (&crtc->config->layout, META_ROUNDING_STRATEGY_ROUND, &view_layout); view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &view_layout, "framebuffer", COGL_FRAMEBUFFER (fake_onscreen), "offscreen", COGL_FRAMEBUFFER (offscreen), "transform", view_transform, "scale", view_scale, NULL); g_object_set_data (G_OBJECT (view), "crtc", crtc); return view; } static void meta_renderer_x11_nested_init (MetaRendererX11Nested *renderer_x11_nested) { } static void meta_renderer_x11_nested_class_init (MetaRendererX11NestedClass *klass) { MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass); renderer_class->create_view = meta_renderer_x11_nested_create_view; } muffin-6.4.1/src/backends/x11/nested/meta-renderer-x11-nested.h0000664000175000017500000000270714723361714022776 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_RENDERER_X11_NESTED_H #define META_RENDERER_X11_NESTED_H #include "backends/x11/meta-renderer-x11.h" #define META_TYPE_RENDERER_X11_NESTED (meta_renderer_x11_nested_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererX11Nested, meta_renderer_x11_nested, META, RENDERER_X11_NESTED, MetaRendererX11) void meta_renderer_x11_nested_ensure_legacy_view (MetaRendererX11Nested *renderer_x11_nested, int width, int height); #endif /* META_RENDERER_X11_NESTED_H */ muffin-6.4.1/src/backends/x11/nested/meta-backend-x11-nested.h0000664000175000017500000000247714723361714022563 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_BACKEND_X11_NESTED_H #define META_BACKEND_X11_NESTED_H #include #include "backends/x11/meta-backend-x11.h" #include "core/util-private.h" #define META_TYPE_BACKEND_X11_NESTED (meta_backend_x11_nested_get_type ()) META_EXPORT_TEST G_DECLARE_DERIVABLE_TYPE (MetaBackendX11Nested, meta_backend_x11_nested, META, BACKEND_X11_NESTED, MetaBackendX11) struct _MetaBackendX11NestedClass { MetaBackendX11Class parent_class; void (* init_gpus) (MetaBackendX11Nested *backend_x11_nested); }; #endif /* META_BACKEND_X11_NESTED_H */ muffin-6.4.1/src/backends/x11/nested/meta-stage-x11-nested.c0000664000175000017500000001671114723361714022266 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "backends/x11/nested/meta-stage-x11-nested.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-output.h" #include "backends/meta-renderer.h" #include "backends/x11/nested/meta-renderer-x11-nested.h" #include "clutter/clutter-mutter.h" static ClutterStageWindowInterface *clutter_stage_window_parent_iface = NULL; struct _MetaStageX11Nested { MetaStageX11 parent; CoglPipeline *pipeline; }; static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaStageX11Nested, meta_stage_x11_nested, META_TYPE_STAGE_X11, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_STAGE_WINDOW, clutter_stage_window_iface_init)) typedef struct _MetaStageX11View { CoglTexture *texture; ClutterStageViewCogl *view; } MetaStageX11NestedView; static void meta_stage_x11_nested_resize (ClutterStageWindow *stage_window, gint width, gint height) { if (!meta_is_stage_views_enabled ()) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); MetaRendererX11Nested *renderer_x11_nested = META_RENDERER_X11_NESTED (renderer); meta_renderer_x11_nested_ensure_legacy_view (renderer_x11_nested, width, height); } clutter_stage_window_parent_iface->resize (stage_window, width, height); } static gboolean meta_stage_x11_nested_can_clip_redraws (ClutterStageWindow *stage_window) { return FALSE; } static GList * meta_stage_x11_nested_get_views (ClutterStageWindow *stage_window) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); return meta_renderer_get_views (renderer); } typedef struct { MetaStageX11Nested *stage_nested; CoglTexture *texture; ClutterStageView *view; MetaLogicalMonitor *logical_monitor; } DrawCrtcData; static gboolean draw_view (MetaStageX11Nested *stage_nested, MetaRendererView *renderer_view, CoglTexture *texture) { MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_nested); CoglFramebuffer *onscreen = COGL_FRAMEBUFFER (stage_x11->onscreen); ClutterStageView *stage_view = CLUTTER_STAGE_VIEW (renderer_view); MetaCrtc *crtc; MetaCrtcConfig *crtc_config; CoglMatrix projection_matrix; CoglMatrix transform; float texture_width, texture_height; float sample_x, sample_y, sample_width, sample_height; float s_1, t_1, s_2, t_2; texture_width = cogl_texture_get_width (texture); texture_height = cogl_texture_get_height (texture); crtc = g_object_get_data (G_OBJECT (renderer_view), "crtc"); crtc_config = crtc->config; sample_x = 0; sample_y = 0; sample_width = texture_width; sample_height = texture_height; clutter_stage_view_get_offscreen_transformation_matrix (stage_view, &transform); cogl_framebuffer_push_matrix (onscreen); cogl_matrix_init_identity (&projection_matrix); cogl_matrix_translate (&projection_matrix, -1, 1, 0); cogl_matrix_scale (&projection_matrix, 2, -2, 0); cogl_matrix_multiply (&projection_matrix, &projection_matrix, &transform); cogl_framebuffer_set_projection_matrix (onscreen, &projection_matrix); s_1 = sample_x / texture_width; t_1 = sample_y / texture_height; s_2 = (sample_x + sample_width) / texture_width; t_2 = (sample_y + sample_height) / texture_height; cogl_framebuffer_set_viewport (onscreen, crtc_config->layout.origin.x, crtc_config->layout.origin.y, crtc_config->layout.size.width, crtc_config->layout.size.height); cogl_framebuffer_draw_textured_rectangle (onscreen, stage_nested->pipeline, 0, 0, 1, 1, s_1, t_1, s_2, t_2); cogl_framebuffer_pop_matrix (onscreen); return TRUE; } static void meta_stage_x11_nested_finish_frame (ClutterStageWindow *stage_window) { MetaStageX11Nested *stage_nested = META_STAGE_X11_NESTED (stage_window); MetaStageX11 *stage_x11 = META_STAGE_X11 (stage_window); MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglFramebuffer *onscreen = COGL_FRAMEBUFFER (stage_x11->onscreen); GList *l; if (!stage_nested->pipeline) stage_nested->pipeline = cogl_pipeline_new (clutter_backend->cogl_context); cogl_framebuffer_clear4f (onscreen, COGL_BUFFER_BIT_COLOR, 0.0f, 0.0f, 0.0f, 1.0f); for (l = meta_renderer_get_views (renderer); l; l = l->next) { ClutterStageView *view = l->data; MetaRendererView *renderer_view = META_RENDERER_VIEW (view); CoglFramebuffer *framebuffer; CoglTexture *texture; framebuffer = clutter_stage_view_get_onscreen (view); texture = cogl_offscreen_get_texture (COGL_OFFSCREEN (framebuffer)); cogl_pipeline_set_layer_texture (stage_nested->pipeline, 0, texture); cogl_pipeline_set_layer_wrap_mode (stage_nested->pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); draw_view (stage_nested, renderer_view, texture); } cogl_onscreen_swap_buffers (stage_x11->onscreen); } static void meta_stage_x11_nested_unrealize (ClutterStageWindow *stage_window) { MetaStageX11Nested *stage_nested = META_STAGE_X11_NESTED (stage_window); g_clear_pointer (&stage_nested->pipeline, cogl_object_unref); clutter_stage_window_parent_iface->unrealize (stage_window); } static void meta_stage_x11_nested_init (MetaStageX11Nested *stage_x11_nested) { } static void meta_stage_x11_nested_class_init (MetaStageX11NestedClass *klass) { } static void clutter_stage_window_iface_init (ClutterStageWindowInterface *iface) { clutter_stage_window_parent_iface = g_type_interface_peek_parent (iface); iface->resize = meta_stage_x11_nested_resize; iface->can_clip_redraws = meta_stage_x11_nested_can_clip_redraws; iface->unrealize = meta_stage_x11_nested_unrealize; iface->get_views = meta_stage_x11_nested_get_views; iface->finish_frame = meta_stage_x11_nested_finish_frame; } muffin-6.4.1/src/backends/x11/nested/meta-stage-x11-nested.h0000664000175000017500000000236114723361714022267 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_STAGE_X11_NESTED_H #define META_STAGE_X11_NESTED_H #include "clutter/clutter-mutter.h" #include "backends/x11/meta-stage-x11.h" #define META_TYPE_STAGE_X11_NESTED (meta_stage_x11_nested_get_type ()) G_DECLARE_FINAL_TYPE (MetaStageX11Nested, meta_stage_x11_nested, META, STAGE_X11_NESTED, MetaStageX11) #endif /* META_STAGE_X11_NESTED_H */ muffin-6.4.1/src/backends/x11/meta-virtual-input-device-x11.c0000664000175000017500000002131614723361714022476 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #include "config.h" #include #include #include "clutter/clutter.h" #include "clutter/x11/clutter-x11.h" #include "meta-keymap-x11.h" #include "meta-virtual-input-device-x11.h" struct _MetaVirtualInputDeviceX11 { ClutterVirtualInputDevice parent; }; G_DEFINE_TYPE (MetaVirtualInputDeviceX11, meta_virtual_input_device_x11, CLUTTER_TYPE_VIRTUAL_INPUT_DEVICE) static void meta_virtual_input_device_x11_notify_relative_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy) { XTestFakeRelativeMotionEvent (clutter_x11_get_default_display (), (int) dx, (int) dy, 0); } static void meta_virtual_input_device_x11_notify_absolute_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double x, double y) { XTestFakeMotionEvent (clutter_x11_get_default_display (), clutter_x11_get_default_screen (), (int) x, (int) y, 0); } static void meta_virtual_input_device_x11_notify_button (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t button, ClutterButtonState button_state) { XTestFakeButtonEvent (clutter_x11_get_default_display (), button, button_state == CLUTTER_BUTTON_STATE_PRESSED, 0); } static void meta_virtual_input_device_x11_notify_discrete_scroll (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, ClutterScrollDirection direction, ClutterScrollSource scroll_source) { Display *xdisplay = clutter_x11_get_default_display (); int button; switch (direction) { case CLUTTER_SCROLL_UP: button = 4; break; case CLUTTER_SCROLL_DOWN: button = 5; break; case CLUTTER_SCROLL_LEFT: button = 6; break; case CLUTTER_SCROLL_RIGHT: button = 7; break; default: g_warn_if_reached (); return; } XTestFakeButtonEvent (xdisplay, button, True, 0); XTestFakeButtonEvent (xdisplay, button, False, 0); } static void meta_virtual_input_device_x11_notify_scroll_continuous (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, double dx, double dy, ClutterScrollSource scroll_source, ClutterScrollFinishFlags finish_flags) { } static void meta_virtual_input_device_x11_notify_key (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t key, ClutterKeyState key_state) { XTestFakeKeyEvent (clutter_x11_get_default_display (), key, key_state == CLUTTER_KEY_STATE_PRESSED, 0); } static void meta_virtual_input_device_x11_notify_keyval (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, uint32_t keyval, ClutterKeyState key_state) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); MetaKeymapX11 *keymap = META_KEYMAP_X11 (clutter_seat_get_keymap (seat)); uint32_t keycode, level; if (!meta_keymap_x11_keycode_for_keyval (keymap, keyval, &keycode, &level)) { level = 0; if (!meta_keymap_x11_reserve_keycode (keymap, keyval, &keycode)) { g_warning ("No keycode found for keyval %x in current group", keyval); return; } } if (!meta_keymap_x11_get_is_modifier (keymap, keycode) && key_state == CLUTTER_KEY_STATE_PRESSED) meta_keymap_x11_latch_modifiers (keymap, level, TRUE); XTestFakeKeyEvent (clutter_x11_get_default_display (), (KeyCode) keycode, key_state == CLUTTER_KEY_STATE_PRESSED, 0); if (key_state == CLUTTER_KEY_STATE_RELEASED) { if (!meta_keymap_x11_get_is_modifier (keymap, keycode)) meta_keymap_x11_latch_modifiers (keymap, level, FALSE); meta_keymap_x11_release_keycode_if_needed (keymap, keycode); } } static void meta_virtual_input_device_x11_notify_touch_down (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot, double x, double y) { g_warning ("Virtual touch motion not implemented under X11"); } static void meta_virtual_input_device_x11_notify_touch_motion (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot, double x, double y) { g_warning ("Virtual touch motion not implemented under X11"); } static void meta_virtual_input_device_x11_notify_touch_up (ClutterVirtualInputDevice *virtual_device, uint64_t time_us, int device_slot) { g_warning ("Virtual touch motion not implemented under X11"); } static void meta_virtual_input_device_x11_init (MetaVirtualInputDeviceX11 *virtual_device_x11) { } static void meta_virtual_input_device_x11_class_init (MetaVirtualInputDeviceX11Class *klass) { ClutterVirtualInputDeviceClass *virtual_input_device_class = CLUTTER_VIRTUAL_INPUT_DEVICE_CLASS (klass); virtual_input_device_class->notify_relative_motion = meta_virtual_input_device_x11_notify_relative_motion; virtual_input_device_class->notify_absolute_motion = meta_virtual_input_device_x11_notify_absolute_motion; virtual_input_device_class->notify_button = meta_virtual_input_device_x11_notify_button; virtual_input_device_class->notify_discrete_scroll = meta_virtual_input_device_x11_notify_discrete_scroll; virtual_input_device_class->notify_scroll_continuous = meta_virtual_input_device_x11_notify_scroll_continuous; virtual_input_device_class->notify_key = meta_virtual_input_device_x11_notify_key; virtual_input_device_class->notify_keyval = meta_virtual_input_device_x11_notify_keyval; virtual_input_device_class->notify_touch_down = meta_virtual_input_device_x11_notify_touch_down; virtual_input_device_class->notify_touch_motion = meta_virtual_input_device_x11_notify_touch_motion; virtual_input_device_class->notify_touch_up = meta_virtual_input_device_x11_notify_touch_up; } muffin-6.4.1/src/backends/x11/meta-crtc-xrandr.c0000664000175000017500000003132414723361714020236 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/x11/meta-crtc-xrandr.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-output.h" #include "backends/x11/meta-crtc-xrandr.h" #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) #define DOUBLE_TO_FIXED(d) ((xcb_render_fixed_t) ((d) * 65536)) typedef struct _MetaCrtcXrandr { MetaRectangle rect; MetaMonitorTransform transform; MetaCrtcMode *current_mode; } MetaCrtcXrandr; gboolean meta_crtc_xrandr_set_config (MetaCrtc *crtc, xcb_randr_crtc_t xrandr_crtc, xcb_timestamp_t timestamp, int x, int y, xcb_randr_mode_t mode, xcb_randr_rotation_t rotation, xcb_randr_output_t *outputs, int n_outputs, xcb_timestamp_t *out_timestamp) { MetaGpu *gpu = meta_crtc_get_gpu (crtc); MetaGpuXrandr *gpu_xrandr = META_GPU_XRANDR (gpu); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay; XRRScreenResources *resources; xcb_connection_t *xcb_conn; xcb_timestamp_t config_timestamp; xcb_randr_set_crtc_config_cookie_t cookie; xcb_randr_set_crtc_config_reply_t *reply; xcb_generic_error_t *xcb_error = NULL; xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); xcb_conn = XGetXCBConnection (xdisplay); resources = meta_gpu_xrandr_get_resources (gpu_xrandr); config_timestamp = resources->configTimestamp; cookie = xcb_randr_set_crtc_config (xcb_conn, xrandr_crtc, timestamp, config_timestamp, x, y, mode, rotation, n_outputs, outputs); reply = xcb_randr_set_crtc_config_reply (xcb_conn, cookie, &xcb_error); if (xcb_error || !reply) { free (xcb_error); free (reply); return FALSE; } *out_timestamp = reply->timestamp; free (reply); return TRUE; } gboolean meta_crtc_xrandr_set_scale (MetaCrtc *crtc, xcb_randr_crtc_t xrandr_crtc, float scale) { MetaGpu *gpu = meta_crtc_get_gpu (crtc); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay; const char *scale_filter; xcb_connection_t *xcb_conn; xcb_void_cookie_t transform_cookie; xcb_generic_error_t *xcb_error = NULL; xcb_render_transform_t transformation = { DOUBLE_TO_FIXED (1), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (1), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (0), DOUBLE_TO_FIXED (1) }; float integer_scale; if (!(meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING)) return FALSE; xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); xcb_conn = XGetXCBConnection (xdisplay); if (fabsf (scale - 1.0f) > 0.001) { integer_scale = roundf (scale); if (fabsf (scale - integer_scale) > 0.001) { scale_filter = FilterGood; transformation.matrix11 = DOUBLE_TO_FIXED (1.0 / scale); transformation.matrix22 = DOUBLE_TO_FIXED (1.0 / scale); } else /* if integer multiple then use nearest neighbor filter */ { scale_filter = "nearest"; transformation.matrix11 = DOUBLE_TO_FIXED (1.0 / integer_scale); transformation.matrix22 = DOUBLE_TO_FIXED (1.0 / integer_scale); } } else scale_filter = FilterFast; transform_cookie = xcb_randr_set_crtc_transform_checked (xcb_conn, xrandr_crtc, transformation, strlen (scale_filter), scale_filter, 0, NULL); xcb_error = xcb_request_check (xcb_conn, transform_cookie); if (xcb_error) { g_warning ("Impossible to set scaling on crtc %u to %f, error id %u", xrandr_crtc, scale, xcb_error->error_code); g_clear_pointer (&xcb_error, free); return FALSE; } return TRUE; } static MetaMonitorTransform meta_monitor_transform_from_xrandr (Rotation rotation) { static const MetaMonitorTransform y_reflected_map[4] = { META_MONITOR_TRANSFORM_FLIPPED_180, META_MONITOR_TRANSFORM_FLIPPED_90, META_MONITOR_TRANSFORM_FLIPPED, META_MONITOR_TRANSFORM_FLIPPED_270 }; MetaMonitorTransform ret; switch (rotation & 0x7F) { default: case RR_Rotate_0: ret = META_MONITOR_TRANSFORM_NORMAL; break; case RR_Rotate_90: ret = META_MONITOR_TRANSFORM_90; break; case RR_Rotate_180: ret = META_MONITOR_TRANSFORM_180; break; case RR_Rotate_270: ret = META_MONITOR_TRANSFORM_270; break; } if (rotation & RR_Reflect_X) return ret + 4; else if (rotation & RR_Reflect_Y) return y_reflected_map[ret]; else return ret; } #define ALL_ROTATIONS (RR_Rotate_0 | RR_Rotate_90 | RR_Rotate_180 | RR_Rotate_270) static MetaMonitorTransform meta_monitor_transform_from_xrandr_all (Rotation rotation) { unsigned ret; /* Handle the common cases first (none or all) */ if (rotation == 0 || rotation == RR_Rotate_0) return (1 << META_MONITOR_TRANSFORM_NORMAL); /* All rotations and one reflection -> all of them by composition */ if ((rotation & ALL_ROTATIONS) && ((rotation & RR_Reflect_X) || (rotation & RR_Reflect_Y))) return ALL_TRANSFORMS; ret = 1 << META_MONITOR_TRANSFORM_NORMAL; if (rotation & RR_Rotate_90) ret |= 1 << META_MONITOR_TRANSFORM_90; if (rotation & RR_Rotate_180) ret |= 1 << META_MONITOR_TRANSFORM_180; if (rotation & RR_Rotate_270) ret |= 1 << META_MONITOR_TRANSFORM_270; if (rotation & (RR_Rotate_0 | RR_Reflect_X)) ret |= 1 << META_MONITOR_TRANSFORM_FLIPPED; if (rotation & (RR_Rotate_90 | RR_Reflect_X)) ret |= 1 << META_MONITOR_TRANSFORM_FLIPPED_90; if (rotation & (RR_Rotate_180 | RR_Reflect_X)) ret |= 1 << META_MONITOR_TRANSFORM_FLIPPED_180; if (rotation & (RR_Rotate_270 | RR_Reflect_X)) ret |= 1 << META_MONITOR_TRANSFORM_FLIPPED_270; return ret; } gboolean meta_crtc_xrandr_is_assignment_changed (MetaCrtc *crtc, MetaCrtcInfo *crtc_info) { MetaCrtcXrandr *crtc_xrandr = crtc->driver_private; unsigned int i; if (crtc_xrandr->current_mode != crtc_info->mode) return TRUE; if (crtc_xrandr->rect.x != (int) roundf (crtc_info->layout.origin.x)) return TRUE; if (crtc_xrandr->rect.y != (int) roundf (crtc_info->layout.origin.y)) return TRUE; if (crtc_xrandr->transform != crtc_info->transform) return TRUE; for (i = 0; i < crtc_info->outputs->len; i++) { MetaOutput *output = ((MetaOutput**) crtc_info->outputs->pdata)[i]; MetaCrtc *assigned_crtc; assigned_crtc = meta_output_get_assigned_crtc (output); if (assigned_crtc != crtc) return TRUE; } return FALSE; } MetaCrtcMode * meta_crtc_xrandr_get_current_mode (MetaCrtc *crtc) { MetaCrtcXrandr *crtc_xrandr = crtc->driver_private; return crtc_xrandr->current_mode; } static void meta_crtc_destroy_notify (MetaCrtc *crtc) { g_free (crtc->driver_private); } static float meta_monitor_scale_from_transformation (XRRCrtcTransformAttributes *transformation) { XTransform *xt; float scale; if (!transformation) return 1.0f; xt = &transformation->currentTransform; if (xt->matrix[0][0] == xt->matrix[1][1]) scale = XFixedToDouble (xt->matrix[0][0]); else scale = XFixedToDouble (xt->matrix[0][0] + xt->matrix[1][1]) / 2.0; return 1.0f / scale; } MetaCrtc * meta_create_xrandr_crtc (MetaGpuXrandr *gpu_xrandr, XRRCrtcInfo *xrandr_crtc, RRCrtc crtc_id, XRRScreenResources *resources, XRRCrtcTransformAttributes *transform_attributes, float scale_multiplier) { MetaGpu *gpu = META_GPU (gpu_xrandr); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); MetaCrtc *crtc; MetaCrtcXrandr *crtc_xrandr; XRRPanning *panning; unsigned int i; GList *modes; crtc = g_object_new (META_TYPE_CRTC, NULL); crtc_xrandr = g_new0 (MetaCrtcXrandr, 1); crtc_xrandr->transform = meta_monitor_transform_from_xrandr (xrandr_crtc->rotation); crtc->driver_private = crtc_xrandr; crtc->driver_notify = (GDestroyNotify) meta_crtc_destroy_notify; crtc->gpu = META_GPU (gpu_xrandr); crtc->crtc_id = crtc_id; panning = XRRGetPanning (xdisplay, resources, crtc_id); if (panning && panning->width > 0 && panning->height > 0) { crtc_xrandr->rect = (MetaRectangle) { .x = panning->left, .y = panning->top, .width = panning->width, .height = panning->height, }; } else { crtc_xrandr->rect = (MetaRectangle) { .x = xrandr_crtc->x, .y = xrandr_crtc->y, .width = xrandr_crtc->width, .height = xrandr_crtc->height, }; } crtc->is_dirty = FALSE; crtc->all_transforms = meta_monitor_transform_from_xrandr_all (xrandr_crtc->rotations); crtc->scale = meta_monitor_scale_from_transformation (transform_attributes); if (scale_multiplier > 0.0f) crtc->scale *= scale_multiplier; modes = meta_gpu_get_modes (crtc->gpu); for (i = 0; i < (unsigned int) resources->nmode; i++) { if (resources->modes[i].id == xrandr_crtc->mode) { crtc_xrandr->current_mode = g_list_nth_data (modes, i); break; } } if (crtc_xrandr->current_mode) { meta_crtc_set_config (crtc, &GRAPHENE_RECT_INIT (crtc_xrandr->rect.x, crtc_xrandr->rect.y, crtc_xrandr->rect.width, crtc_xrandr->rect.height), crtc_xrandr->current_mode, crtc_xrandr->transform); } return crtc; } muffin-6.4.1/src/backends/x11/meta-event-x11.h0000664000175000017500000000327714723361714017552 0ustar fabiofabio/* Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * * * Authored by: * Matthew Allum * Emmanuele Bassi */ #ifndef META_EVENT_X11_H #define META_EVENT_X11_H #include #include "clutter/x11/clutter-x11.h" typedef struct _MetaEventX11 MetaEventX11; struct _MetaEventX11 { /* additional fields for Key events */ gint key_group; guint key_is_modifier : 1; guint num_lock_set : 1; guint caps_lock_set : 1; }; MetaEventX11 * meta_event_x11_new (void); MetaEventX11 * meta_event_x11_copy (MetaEventX11 *event_x11); void meta_event_x11_free (MetaEventX11 *event_x11); Time meta_x11_get_current_event_time (void); gint meta_x11_event_get_key_group (const ClutterEvent *event); guint meta_x11_event_sequence_get_touch_detail (const ClutterEventSequence *sequence); ClutterX11FilterReturn meta_x11_handle_event (XEvent *xevent); #endif /* META_EVENT_X11_H */ muffin-6.4.1/src/backends/x11/meta-input-device-x11.c0000664000175000017500000003416014723361714021013 0ustar fabiofabio/* * Copyright © 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #include "config.h" #include #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" #include "backends/x11/meta-input-device-x11.h" struct _MetaInputDeviceX11 { ClutterInputDevice device; int32_t device_id; ClutterInputDeviceTool *current_tool; int inhibit_pointer_query_timer; gboolean query_status; float current_x; float current_y; #ifdef HAVE_LIBWACOM GArray *group_modes; #endif }; struct _MetaInputDeviceX11Class { ClutterInputDeviceClass device_class; }; #define N_BUTTONS 5 G_DEFINE_TYPE (MetaInputDeviceX11, meta_input_device_x11, META_TYPE_INPUT_DEVICE) static void meta_input_device_x11_constructed (GObject *object) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object); g_object_get (object, "id", &device_xi2->device_id, NULL); if (G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed) G_OBJECT_CLASS (meta_input_device_x11_parent_class)->constructed (object); #ifdef HAVE_LIBWACOM if (clutter_input_device_get_device_type (CLUTTER_INPUT_DEVICE (object)) == CLUTTER_PAD_DEVICE) { device_xi2->group_modes = g_array_new (FALSE, TRUE, sizeof (uint32_t)); g_array_set_size (device_xi2->group_modes, clutter_input_device_get_n_mode_groups (CLUTTER_INPUT_DEVICE (object))); } #endif } static gboolean meta_input_device_x11_keycode_to_evdev (ClutterInputDevice *device, uint32_t hardware_keycode, uint32_t *evdev_keycode) { /* When using evdev under X11 the hardware keycodes are the evdev keycodes plus 8. I haven't been able to find any documentation to know what the +8 is for. FIXME: This should probably verify that X server is using evdev. */ *evdev_keycode = hardware_keycode - 8; return TRUE; } static gboolean meta_input_device_x11_is_grouped (ClutterInputDevice *device, ClutterInputDevice *other_device) { #ifdef HAVE_LIBWACOM WacomDevice *wacom_device, *other_wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); other_wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (other_device)); if (wacom_device && other_wacom_device && libwacom_compare (wacom_device, other_wacom_device, WCOMPARE_NORMAL) == 0) return TRUE; #endif if (clutter_input_device_get_vendor_id (device) && clutter_input_device_get_product_id (device) && clutter_input_device_get_vendor_id (other_device) && clutter_input_device_get_product_id (other_device)) { if (strcmp (clutter_input_device_get_vendor_id (device), clutter_input_device_get_vendor_id (other_device)) == 0 && strcmp (clutter_input_device_get_product_id (device), clutter_input_device_get_product_id (other_device)) == 0) return TRUE; } return FALSE; } static void meta_input_device_x11_finalize (GObject *object) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (object); #ifdef HAVE_LIBWACOM if (device_xi2->group_modes) g_array_unref (device_xi2->group_modes); #endif g_clear_handle_id (&device_xi2->inhibit_pointer_query_timer, g_source_remove); G_OBJECT_CLASS (meta_input_device_x11_parent_class)->finalize (object); } static int meta_input_device_x11_get_group_n_modes (ClutterInputDevice *device, int group) { #ifdef HAVE_LIBWACOM WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); if (wacom_device) { if (group == 0) { if (libwacom_has_ring (wacom_device)) return libwacom_get_ring_num_modes (wacom_device); else if (libwacom_get_num_strips (wacom_device) >= 1) return libwacom_get_strips_num_modes (wacom_device); } else if (group == 1) { if (libwacom_has_ring2 (wacom_device)) return libwacom_get_ring2_num_modes (wacom_device); else if (libwacom_get_num_strips (wacom_device) >= 2) return libwacom_get_strips_num_modes (wacom_device); } } #endif return -1; } #ifdef HAVE_LIBWACOM static int meta_input_device_x11_get_button_group (ClutterInputDevice *device, uint32_t button) { WacomDevice *wacom_device; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); if (wacom_device) { WacomButtonFlags flags; if (button >= libwacom_get_num_buttons (wacom_device)) return -1; flags = libwacom_get_button_flag (wacom_device, 'A' + button); if (flags & (WACOM_BUTTON_RING_MODESWITCH | WACOM_BUTTON_TOUCHSTRIP_MODESWITCH)) return 0; if (flags & (WACOM_BUTTON_RING2_MODESWITCH | WACOM_BUTTON_TOUCHSTRIP2_MODESWITCH)) return 1; } return -1; } #endif static gboolean meta_input_device_x11_is_mode_switch_button (ClutterInputDevice *device, uint32_t group, uint32_t button) { int button_group = -1; #ifdef HAVE_LIBWACOM button_group = meta_input_device_x11_get_button_group (device, button); #endif return button_group == (int) group; } static void meta_input_device_x11_class_init (MetaInputDeviceX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterInputDeviceClass *device_class = CLUTTER_INPUT_DEVICE_CLASS (klass); gobject_class->constructed = meta_input_device_x11_constructed; gobject_class->finalize = meta_input_device_x11_finalize; device_class->keycode_to_evdev = meta_input_device_x11_keycode_to_evdev; device_class->is_grouped = meta_input_device_x11_is_grouped; device_class->get_group_n_modes = meta_input_device_x11_get_group_n_modes; device_class->is_mode_switch_button = meta_input_device_x11_is_mode_switch_button; } static void meta_input_device_x11_init (MetaInputDeviceX11 *self) { } static ClutterModifierType get_modifier_for_button (int i) { switch (i) { case 1: return CLUTTER_BUTTON1_MASK; case 2: return CLUTTER_BUTTON2_MASK; case 3: return CLUTTER_BUTTON3_MASK; case 4: return CLUTTER_BUTTON4_MASK; case 5: return CLUTTER_BUTTON5_MASK; default: return 0; } } void meta_input_device_x11_translate_state (ClutterEvent *event, XIModifierState *modifiers_state, XIButtonState *buttons_state, XIGroupState *group_state) { uint32_t button = 0; uint32_t base = 0; uint32_t latched = 0; uint32_t locked = 0; uint32_t effective; if (modifiers_state) { base = (uint32_t) modifiers_state->base; latched = (uint32_t) modifiers_state->latched; locked = (uint32_t) modifiers_state->locked; } if (buttons_state) { int len, i; len = MIN (N_BUTTONS, buttons_state->mask_len * 8); for (i = 0; i < len; i++) { if (!XIMaskIsSet (buttons_state->mask, i)) continue; button |= get_modifier_for_button (i); } } /* The XIButtonState sent in the event specifies the * state of the buttons before the event. In order to * get the current state of the buttons, we need to * filter out the current button. */ switch (event->type) { case CLUTTER_BUTTON_PRESS: button |= (get_modifier_for_button (event->button.button)); break; case CLUTTER_BUTTON_RELEASE: button &= ~(get_modifier_for_button (event->button.button)); break; default: break; } effective = button | base | latched | locked; if (group_state) effective |= (group_state->effective) << 13; _clutter_event_set_state_full (event, button, base, latched, locked, effective); } void meta_input_device_x11_update_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device); g_set_object (&device_xi2->current_tool, tool); } ClutterInputDeviceTool * meta_input_device_x11_get_current_tool (ClutterInputDevice *device) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device); return device_xi2->current_tool; } static gboolean meta_input_device_x11_query_pointer_location (MetaInputDeviceX11 *device_xi2) { Window xroot_window, xchild_window; double xroot_x, xroot_y, xwin_x, xwin_y; XIButtonState button_state; XIModifierState mod_state; XIGroupState group_state; int result; clutter_x11_trap_x_errors (); result = XIQueryPointer (clutter_x11_get_default_display (), device_xi2->device_id, clutter_x11_get_root_window (), &xroot_window, &xchild_window, &xroot_x, &xroot_y, &xwin_x, &xwin_y, &button_state, &mod_state, &group_state); clutter_x11_untrap_x_errors (); if (!result) return FALSE; device_xi2->current_x = (float) xroot_x; device_xi2->current_y = (float) xroot_y; return TRUE; } static gboolean clear_inhibit_pointer_query_cb (gpointer data) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (data); device_xi2->inhibit_pointer_query_timer = 0; return G_SOURCE_REMOVE; } gboolean meta_input_device_x11_get_pointer_location (ClutterInputDevice *device, float *x, float *y) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device); g_return_val_if_fail (META_IS_INPUT_DEVICE_X11 (device), FALSE); g_return_val_if_fail (device->device_type == CLUTTER_POINTER_DEVICE, FALSE); /* Throttle XServer queries and roundtrips using an idle timeout */ if (device_xi2->inhibit_pointer_query_timer == 0) { device_xi2->query_status = meta_input_device_x11_query_pointer_location (device_xi2); device_xi2->inhibit_pointer_query_timer = clutter_threads_add_idle (clear_inhibit_pointer_query_cb, device_xi2); } *x = device_xi2->current_x; *y = device_xi2->current_y; return device_xi2->query_status; } #ifdef HAVE_LIBWACOM uint32_t meta_input_device_x11_get_pad_group_mode (ClutterInputDevice *device, uint32_t group) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device); if (group >= device_xi2->group_modes->len) return 0; return g_array_index (device_xi2->group_modes, uint32_t, group); } static gboolean pad_switch_mode (ClutterInputDevice *device, uint32_t button, uint32_t group, uint32_t *mode) { MetaInputDeviceX11 *device_x11 = META_INPUT_DEVICE_X11 (device); uint32_t n_buttons, n_modes, button_group, next_mode, i; WacomDevice *wacom_device; GList *switch_buttons = NULL; wacom_device = meta_input_device_get_wacom_device (META_INPUT_DEVICE (device)); n_buttons = libwacom_get_num_buttons (wacom_device); for (i = 0; i < n_buttons; i++) { button_group = meta_input_device_x11_get_button_group (device, i); if (button_group == group) switch_buttons = g_list_prepend (switch_buttons, GINT_TO_POINTER (button)); } switch_buttons = g_list_reverse (switch_buttons); n_modes = clutter_input_device_get_group_n_modes (device, group); if (g_list_length (switch_buttons) > 1) { /* If there's multiple switch buttons, we don't toggle but assign a mode * to each of those buttons. */ next_mode = g_list_index (switch_buttons, GINT_TO_POINTER (button)); } else if (switch_buttons) { uint32_t cur_mode; /* If there is a single button, have it toggle across modes */ cur_mode = g_array_index (device_x11->group_modes, uint32_t, group); next_mode = (cur_mode + 1) % n_modes; } else { return FALSE; } g_list_free (switch_buttons); if (next_mode < 0 || next_mode > n_modes) return FALSE; *mode = next_mode; return TRUE; } void meta_input_device_x11_update_pad_state (ClutterInputDevice *device, uint32_t button, uint32_t state, uint32_t *group, uint32_t *mode) { MetaInputDeviceX11 *device_xi2 = META_INPUT_DEVICE_X11 (device); uint32_t button_group, *group_mode; button_group = meta_input_device_x11_get_button_group (device, button); if (button_group < 0 || button_group >= device_xi2->group_modes->len) { if (group) *group = 0; if (mode) *mode = 0; return; } group_mode = &g_array_index (device_xi2->group_modes, uint32_t, button_group); if (state) { uint32_t next_mode; if (pad_switch_mode (device, button, button_group, &next_mode)) *group_mode = next_mode; } if (group) *group = button_group; if (mode) *mode = *group_mode; } #endif muffin-6.4.1/src/backends/x11/meta-output-xrandr.c0000664000175000017500000006360514723361714020652 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/x11/meta-output-xrandr.h" #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include "meta/util.h" static Display * xdisplay_from_output (MetaOutput *output) { MetaGpu *gpu = meta_output_get_gpu (output); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); return meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); } static void output_set_presentation_xrandr (MetaOutput *output, gboolean presentation) { Display *xdisplay = xdisplay_from_output (output); Atom atom; int value = presentation; atom = XInternAtom (xdisplay, "_MUTTER_PRESENTATION_OUTPUT", False); xcb_randr_change_output_property (XGetXCBConnection (xdisplay), (XID) output->winsys_id, atom, XCB_ATOM_CARDINAL, 32, XCB_PROP_MODE_REPLACE, 1, &value); } static void output_set_underscanning_xrandr (MetaOutput *output, gboolean underscanning) { Display *xdisplay = xdisplay_from_output (output); Atom prop, valueatom; const char *value; prop = XInternAtom (xdisplay, "underscan", False); value = underscanning ? "on" : "off"; valueatom = XInternAtom (xdisplay, value, False); xcb_randr_change_output_property (XGetXCBConnection (xdisplay), (XID) output->winsys_id, prop, XCB_ATOM_ATOM, 32, XCB_PROP_MODE_REPLACE, 1, &valueatom); /* Configure the border at the same time. Currently, we use a * 5% of the width/height of the mode. In the future, we should * make the border configurable. */ if (underscanning) { MetaCrtc *crtc; MetaCrtcConfig *crtc_config; uint32_t border_value; crtc = meta_output_get_assigned_crtc (output); crtc_config = crtc->config; prop = XInternAtom (xdisplay, "underscan hborder", False); border_value = crtc_config->mode->width * 0.05; xcb_randr_change_output_property (XGetXCBConnection (xdisplay), (XID) output->winsys_id, prop, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, &border_value); prop = XInternAtom (xdisplay, "underscan vborder", False); border_value = crtc_config->mode->height * 0.05; xcb_randr_change_output_property (XGetXCBConnection (xdisplay), (XID) output->winsys_id, prop, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, &border_value); } } void meta_output_xrandr_apply_mode (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); if (output->is_primary) { XRRSetOutputPrimary (xdisplay, DefaultRootWindow (xdisplay), (XID) output->winsys_id); } output_set_presentation_xrandr (output, output->is_presentation); if (output->supports_underscanning) output_set_underscanning_xrandr (output, output->is_underscanning); } static int normalize_backlight (MetaOutput *output, int hw_value) { return round ((double)(hw_value - output->backlight_min) / (output->backlight_max - output->backlight_min) * 100.0); } void meta_output_xrandr_change_backlight (MetaOutput *output, int value) { Display *xdisplay = xdisplay_from_output (output); Atom atom; int hw_value; hw_value = round ((double) value / 100.0 * output->backlight_max + output->backlight_min); atom = XInternAtom (xdisplay, "Backlight", False); xcb_randr_change_output_property (XGetXCBConnection (xdisplay), (XID)output->winsys_id, atom, XCB_ATOM_INTEGER, 32, XCB_PROP_MODE_REPLACE, 1, &hw_value); /* We're not selecting for property notifies, so update the value immediately */ output->backlight = normalize_backlight (output, hw_value); } static gboolean output_get_integer_property (MetaOutput *output, const char *propname, gint *value) { Display *xdisplay = xdisplay_from_output (output); gboolean exists = FALSE; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *buffer; atom = XInternAtom (xdisplay, propname, False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_INTEGER, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); exists = (actual_type == XA_INTEGER && actual_format == 32 && nitems == 1); if (exists && value != NULL) *value = ((int*)buffer)[0]; XFree (buffer); return exists; } static gboolean output_get_property_exists (MetaOutput *output, const char *propname) { Display *xdisplay = xdisplay_from_output (output); gboolean exists = FALSE; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char *buffer; atom = XInternAtom (xdisplay, propname, False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); exists = (actual_type != None); XFree (buffer); return exists; } static gboolean output_get_boolean_property (MetaOutput *output, const char *propname) { Display *xdisplay = xdisplay_from_output (output); Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (xdisplay, propname, False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_CARDINAL, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_CARDINAL || actual_format != 32 || nitems < 1) return FALSE; return ((int*)buffer)[0]; } static gboolean output_get_presentation_xrandr (MetaOutput *output) { return output_get_boolean_property (output, "_MUTTER_PRESENTATION_OUTPUT"); } static gboolean output_get_underscanning_xrandr (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; g_autofree char *str = NULL; atom = XInternAtom (xdisplay, "underscan", False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1) return FALSE; str = XGetAtomName (xdisplay, *(Atom *)buffer); return (strcmp (str, "on") == 0); } static gboolean output_get_supports_underscanning_xrandr (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); Atom atom, actual_type; int actual_format, i; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; XRRPropertyInfo *property_info; Atom *values; gboolean supports_underscanning = FALSE; atom = XInternAtom (xdisplay, "underscan", False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1) return FALSE; property_info = XRRQueryOutputProperty (xdisplay, (XID) output->winsys_id, atom); values = (Atom *) property_info->values; for (i = 0; i < property_info->num_values; i++) { /* The output supports underscanning if "on" is a valid value * for the underscan property. */ char *name = XGetAtomName (xdisplay, values[i]); if (strcmp (name, "on") == 0) supports_underscanning = TRUE; XFree (name); } XFree (property_info); return supports_underscanning; } static int output_get_backlight_xrandr (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); int value = -1; Atom atom, actual_type; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (xdisplay, "Backlight", False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_INTEGER, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_INTEGER || actual_format != 32 || nitems < 1) return FALSE; value = ((int*)buffer)[0]; if (value > 0) return normalize_backlight (output, value); else return -1; } static void output_get_backlight_limits_xrandr (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); Atom atom; xcb_connection_t *xcb_conn; xcb_randr_query_output_property_cookie_t cookie; g_autofree xcb_randr_query_output_property_reply_t *reply = NULL; atom = XInternAtom (xdisplay, "Backlight", False); xcb_conn = XGetXCBConnection (xdisplay); cookie = xcb_randr_query_output_property (xcb_conn, (xcb_randr_output_t) output->winsys_id, (xcb_atom_t) atom); reply = xcb_randr_query_output_property_reply (xcb_conn, cookie, NULL); /* This can happen on systems without backlights. */ if (reply == NULL) return; if (!reply->range || reply->length != 2) { meta_verbose ("backlight %s was not range\n", output->name); return; } int32_t *values = xcb_randr_query_output_property_valid_values (reply); output->backlight_min = values[0]; output->backlight_max = values[1]; } static guint8 * get_edid_property (Display *xdisplay, RROutput output, Atom atom, gsize *len) { unsigned char *prop; int actual_format; unsigned long nitems, bytes_after; Atom actual_type; guint8 *result; XRRGetOutputProperty (xdisplay, output, atom, 0, 100, False, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if (actual_type == XA_INTEGER && actual_format == 8) { result = g_memdup2 (prop, nitems); if (len) *len = nitems; } else { result = NULL; } XFree (prop); return result; } GBytes * meta_output_xrandr_read_edid (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); Atom edid_atom; guint8 *result; gsize len; edid_atom = XInternAtom (xdisplay, "EDID", FALSE); result = get_edid_property (xdisplay, output->winsys_id, edid_atom, &len); if (!result) { edid_atom = XInternAtom (xdisplay, "EDID_DATA", FALSE); result = get_edid_property (xdisplay, output->winsys_id, edid_atom, &len); } if (result) { if (len > 0 && len % 128 == 0) return g_bytes_new_take (result, len); else g_free (result); } return NULL; } static gboolean output_get_hotplug_mode_update (MetaOutput *output) { return output_get_property_exists (output, "hotplug_mode_update"); } static gint output_get_suggested_x (MetaOutput *output) { gint val; if (output_get_integer_property (output, "suggested X", &val)) return val; return -1; } static gint output_get_suggested_y (MetaOutput *output) { gint val; if (output_get_integer_property (output, "suggested Y", &val)) return val; return -1; } static MetaConnectorType connector_type_from_atom (Display *xdisplay, Atom atom) { if (atom == XInternAtom (xdisplay, "HDMI", True)) return META_CONNECTOR_TYPE_HDMIA; if (atom == XInternAtom (xdisplay, "VGA", True)) return META_CONNECTOR_TYPE_VGA; /* Doesn't have a DRM equivalent, but means an internal panel. * We could pick either LVDS or eDP here. */ if (atom == XInternAtom (xdisplay, "Panel", True)) return META_CONNECTOR_TYPE_LVDS; if (atom == XInternAtom (xdisplay, "DVI", True) || atom == XInternAtom (xdisplay, "DVI-I", True)) return META_CONNECTOR_TYPE_DVII; if (atom == XInternAtom (xdisplay, "DVI-A", True)) return META_CONNECTOR_TYPE_DVIA; if (atom == XInternAtom (xdisplay, "DVI-D", True)) return META_CONNECTOR_TYPE_DVID; if (atom == XInternAtom (xdisplay, "DisplayPort", True)) return META_CONNECTOR_TYPE_DisplayPort; if (atom == XInternAtom (xdisplay, "TV", True)) return META_CONNECTOR_TYPE_TV; if (atom == XInternAtom (xdisplay, "TV-Composite", True)) return META_CONNECTOR_TYPE_Composite; if (atom == XInternAtom (xdisplay, "TV-SVideo", True)) return META_CONNECTOR_TYPE_SVIDEO; /* Another set of mismatches. */ if (atom == XInternAtom (xdisplay, "TV-SCART", True)) return META_CONNECTOR_TYPE_TV; if (atom == XInternAtom (xdisplay, "TV-C4", True)) return META_CONNECTOR_TYPE_TV; return META_CONNECTOR_TYPE_Unknown; } static MetaConnectorType output_get_connector_type_from_prop (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); Atom atom, actual_type, connector_type_atom; int actual_format; unsigned long nitems, bytes_after; g_autofree unsigned char *buffer = NULL; atom = XInternAtom (xdisplay, "ConnectorType", False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1) return META_CONNECTOR_TYPE_Unknown; connector_type_atom = ((Atom *) buffer)[0]; return connector_type_from_atom (xdisplay, connector_type_atom); } static MetaConnectorType output_get_connector_type_from_name (MetaOutput *output) { const char *name = output->name; /* drmmode_display.c, which was copy/pasted across all the FOSS * xf86-video-* drivers, seems to name its outputs based on the * connector type, so look for that.... * * SNA has its own naming scheme, because what else did you expect * from SNA, but it's not too different, so we can thankfully use * that with minor changes. * * http://cgit.freedesktop.org/xorg/xserver/tree/hw/xfree86/drivers/modesetting/drmmode_display.c#n953 * http://cgit.freedesktop.org/xorg/driver/xf86-video-intel/tree/src/sna/sna_display.c#n3486 */ if (g_str_has_prefix (name, "DVI")) return META_CONNECTOR_TYPE_DVII; if (g_str_has_prefix (name, "LVDS")) return META_CONNECTOR_TYPE_LVDS; if (g_str_has_prefix (name, "HDMI")) return META_CONNECTOR_TYPE_HDMIA; if (g_str_has_prefix (name, "VGA")) return META_CONNECTOR_TYPE_VGA; /* SNA uses DP, not DisplayPort. Test for both. */ if (g_str_has_prefix (name, "DP") || g_str_has_prefix (name, "DisplayPort")) return META_CONNECTOR_TYPE_DisplayPort; if (g_str_has_prefix (name, "eDP")) return META_CONNECTOR_TYPE_eDP; if (g_str_has_prefix (name, "Virtual")) return META_CONNECTOR_TYPE_VIRTUAL; if (g_str_has_prefix (name, "Composite")) return META_CONNECTOR_TYPE_Composite; if (g_str_has_prefix (name, "S-video")) return META_CONNECTOR_TYPE_SVIDEO; if (g_str_has_prefix (name, "TV")) return META_CONNECTOR_TYPE_TV; if (g_str_has_prefix (name, "CTV")) return META_CONNECTOR_TYPE_Composite; if (g_str_has_prefix (name, "DSI")) return META_CONNECTOR_TYPE_DSI; if (g_str_has_prefix (name, "DIN")) return META_CONNECTOR_TYPE_9PinDIN; return META_CONNECTOR_TYPE_Unknown; } static MetaConnectorType output_get_connector_type (MetaOutput *output) { MetaConnectorType ret; /* The "ConnectorType" property is considered mandatory since RandR 1.3, * but none of the FOSS drivers support it, because we're a bunch of * professional software developers. * * Try poking it first, without any expectations that it will work. * If it's not there, we thankfully have other bonghits to try next. */ ret = output_get_connector_type_from_prop (output); if (ret != META_CONNECTOR_TYPE_Unknown) return ret; /* Fall back to heuristics based on the output name. */ ret = output_get_connector_type_from_name (output); if (ret != META_CONNECTOR_TYPE_Unknown) return ret; return META_CONNECTOR_TYPE_Unknown; } static gint output_get_panel_orientation_transform (MetaOutput *output) { Display *xdisplay = xdisplay_from_output (output); unsigned long nitems, bytes_after; Atom atom, actual_type; int actual_format; g_autofree unsigned char *buffer = NULL; g_autofree char *str = NULL; atom = XInternAtom (xdisplay, "panel orientation", False); XRRGetOutputProperty (xdisplay, (XID)output->winsys_id, atom, 0, G_MAXLONG, False, False, XA_ATOM, &actual_type, &actual_format, &nitems, &bytes_after, &buffer); if (actual_type != XA_ATOM || actual_format != 32 || nitems < 1) return META_MONITOR_TRANSFORM_NORMAL; str = XGetAtomName (xdisplay, *(Atom *)buffer); if (strcmp (str, "Upside Down") == 0) return META_MONITOR_TRANSFORM_180; if (strcmp (str, "Left Side Up") == 0) return META_MONITOR_TRANSFORM_90; if (strcmp (str, "Right Side Up") == 0) return META_MONITOR_TRANSFORM_270; return META_MONITOR_TRANSFORM_NORMAL; } static void output_get_tile_info (MetaOutput *output) { MetaGpu *gpu = meta_output_get_gpu (output); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); Display *xdisplay = xdisplay_from_output (output); Atom tile_atom; unsigned char *prop; unsigned long nitems, bytes_after; int actual_format; Atom actual_type; if (!(meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_TILING)) return; tile_atom = XInternAtom (xdisplay, "TILE", FALSE); XRRGetOutputProperty (xdisplay, output->winsys_id, tile_atom, 0, 100, False, False, AnyPropertyType, &actual_type, &actual_format, &nitems, &bytes_after, &prop); if (actual_type == XA_INTEGER && actual_format == 32 && nitems == 8) { long *values = (long *)prop; output->tile_info.group_id = values[0]; output->tile_info.flags = values[1]; output->tile_info.max_h_tiles = values[2]; output->tile_info.max_v_tiles = values[3]; output->tile_info.loc_h_tile = values[4]; output->tile_info.loc_v_tile = values[5]; output->tile_info.tile_w = values[6]; output->tile_info.tile_h = values[7]; } XFree (prop); } static void output_get_modes (MetaOutput *output, XRROutputInfo *xrandr_output) { MetaGpu *gpu = meta_output_get_gpu (output); unsigned int i; unsigned int n_actual_modes; output->modes = g_new0 (MetaCrtcMode *, xrandr_output->nmode); n_actual_modes = 0; for (i = 0; i < (unsigned int) xrandr_output->nmode; i++) { GList *l; for (l = meta_gpu_get_modes (gpu); l; l = l->next) { MetaCrtcMode *mode = l->data; if (xrandr_output->modes[i] == (XID) mode->mode_id) { output->modes[n_actual_modes] = mode; n_actual_modes += 1; break; } } } output->n_modes = n_actual_modes; if (n_actual_modes > 0) output->preferred_mode = output->modes[0]; } static void output_get_crtcs (MetaOutput *output, XRROutputInfo *xrandr_output) { MetaGpu *gpu = meta_output_get_gpu (output); unsigned int i; unsigned int n_actual_crtcs; GList *l; output->possible_crtcs = g_new0 (MetaCrtc *, xrandr_output->ncrtc); n_actual_crtcs = 0; for (i = 0; i < (unsigned int) xrandr_output->ncrtc; i++) { for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if ((XID) crtc->crtc_id == xrandr_output->crtcs[i]) { output->possible_crtcs[n_actual_crtcs] = crtc; n_actual_crtcs += 1; break; } } } output->n_possible_crtcs = n_actual_crtcs; meta_output_unassign_crtc (output); for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if ((XID) crtc->crtc_id == xrandr_output->crtc) { meta_output_assign_crtc (output, crtc); break; } } } MetaOutput * meta_create_xrandr_output (MetaGpuXrandr *gpu_xrandr, XRROutputInfo *xrandr_output, RROutput output_id, RROutput primary_output) { MetaOutput *output; GBytes *edid; unsigned int i; output = g_object_new (META_TYPE_OUTPUT, NULL); output->gpu = META_GPU (gpu_xrandr); output->winsys_id = output_id; output->name = g_strdup (xrandr_output->name); edid = meta_output_xrandr_read_edid (output); meta_output_parse_edid (output, edid); g_bytes_unref (edid); output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; output->hotplug_mode_update = output_get_hotplug_mode_update (output); output->suggested_x = output_get_suggested_x (output); output->suggested_y = output_get_suggested_y (output); output->connector_type = output_get_connector_type (output); output->panel_orientation_transform = output_get_panel_orientation_transform (output); if (meta_monitor_transform_is_rotated ( output->panel_orientation_transform)) { output->width_mm = xrandr_output->mm_height; output->height_mm = xrandr_output->mm_width; } else { output->width_mm = xrandr_output->mm_width; output->height_mm = xrandr_output->mm_height; } output_get_tile_info (output); output_get_modes (output, xrandr_output); output_get_crtcs (output, xrandr_output); output->n_possible_clones = xrandr_output->nclone; output->possible_clones = g_new0 (MetaOutput *, output->n_possible_clones); /* * We can build the list of clones now, because we don't have the list of * outputs yet, so temporarily set the pointers to the bare XIDs, and then * we'll fix them in a second pass. */ for (i = 0; i < (unsigned int) xrandr_output->nclone; i++) { output->possible_clones[i] = GINT_TO_POINTER (xrandr_output->clones[i]); } output->is_primary = ((XID) output->winsys_id == primary_output); output->is_presentation = output_get_presentation_xrandr (output); output->is_underscanning = output_get_underscanning_xrandr (output); output->supports_underscanning = output_get_supports_underscanning_xrandr (output); output_get_backlight_limits_xrandr (output); if (!(output->backlight_min == 0 && output->backlight_max == 0)) output->backlight = output_get_backlight_xrandr (output); else output->backlight = -1; if (output->n_modes == 0 || output->n_possible_crtcs == 0) { g_object_unref (output); return NULL; } else { return output; } } muffin-6.4.1/src/backends/x11/meta-input-settings-x11.h0000664000175000017500000000400214723361714021411 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_SETTINGS_X11_H #define META_INPUT_SETTINGS_X11_H #include "backends/meta-input-settings-private.h" #define META_TYPE_INPUT_SETTINGS_X11 (meta_input_settings_x11_get_type ()) #define META_INPUT_SETTINGS_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_INPUT_SETTINGS_X11, MetaInputSettingsX11)) #define META_INPUT_SETTINGS_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_INPUT_SETTINGS_X11, MetaInputSettingsX11Class)) #define META_IS_INPUT_SETTINGS_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_INPUT_SETTINGS_X11)) #define META_IS_INPUT_SETTINGS_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_INPUT_SETTINGS_X11)) #define META_INPUT_SETTINGS_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_INPUT_SETTINGS_X11, MetaInputSettingsX11Class)) typedef struct _MetaInputSettingsX11 MetaInputSettingsX11; typedef struct _MetaInputSettingsX11Class MetaInputSettingsX11Class; struct _MetaInputSettingsX11 { MetaInputSettings parent_instance; }; struct _MetaInputSettingsX11Class { MetaInputSettingsClass parent_class; }; GType meta_input_settings_x11_get_type (void) G_GNUC_CONST; #endif /* META_INPUT_SETTINGS_X11_H */ muffin-6.4.1/src/backends/x11/meta-cursor-renderer-x11.c0000664000175000017500000000702514723361714021540 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "backends/x11/meta-cursor-renderer-x11.h" #include #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-stage-private.h" #include "backends/x11/meta-backend-x11.h" struct _MetaCursorRendererX11Private { gboolean server_cursor_visible; }; typedef struct _MetaCursorRendererX11Private MetaCursorRendererX11Private; G_DEFINE_TYPE_WITH_PRIVATE (MetaCursorRendererX11, meta_cursor_renderer_x11, META_TYPE_CURSOR_RENDERER); static gboolean meta_cursor_renderer_x11_update_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite) { MetaCursorRendererX11 *x11 = META_CURSOR_RENDERER_X11 (renderer); MetaCursorRendererX11Private *priv = meta_cursor_renderer_x11_get_instance_private (x11); MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Window xwindow = meta_backend_x11_get_xwindow (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend); if (xwindow == None) { if (cursor_sprite) meta_cursor_sprite_realize_texture (cursor_sprite); return FALSE; } gboolean has_server_cursor = FALSE; if (cursor_sprite && META_IS_CURSOR_SPRITE_XCURSOR (cursor_sprite)) { MetaCursorSpriteXcursor *sprite_xcursor = META_CURSOR_SPRITE_XCURSOR (cursor_sprite); MetaCursor cursor; cursor = meta_cursor_sprite_xcursor_get_cursor (sprite_xcursor); if (cursor != META_CURSOR_NONE) { Cursor xcursor; xcursor = meta_create_x_cursor (xdisplay, cursor); XDefineCursor (xdisplay, xwindow, xcursor); XFlush (xdisplay); XFreeCursor (xdisplay, xcursor); has_server_cursor = TRUE; } } if (has_server_cursor != priv->server_cursor_visible) { if (has_server_cursor) XFixesShowCursor (xdisplay, xwindow); else XFixesHideCursor (xdisplay, xwindow); priv->server_cursor_visible = has_server_cursor; } if (!priv->server_cursor_visible && cursor_sprite) meta_cursor_sprite_realize_texture (cursor_sprite); return priv->server_cursor_visible; } static void meta_cursor_renderer_x11_class_init (MetaCursorRendererX11Class *klass) { MetaCursorRendererClass *renderer_class = META_CURSOR_RENDERER_CLASS (klass); renderer_class->update_cursor = meta_cursor_renderer_x11_update_cursor; } static void meta_cursor_renderer_x11_init (MetaCursorRendererX11 *x11) { MetaCursorRendererX11Private *priv = meta_cursor_renderer_x11_get_instance_private (x11); /* XFixes has no way to retrieve the current cursor visibility. */ priv->server_cursor_visible = TRUE; } muffin-6.4.1/src/backends/x11/meta-barrier-x11.h0000664000175000017500000000251314723361714020047 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_BARRIER_X11_H #define META_BARRIER_X11_H #include "backends/meta-barrier-private.h" G_BEGIN_DECLS #define META_TYPE_BARRIER_IMPL_X11 (meta_barrier_impl_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaBarrierImplX11, meta_barrier_impl_x11, META, BARRIER_IMPL_X11, MetaBarrierImpl) MetaBarrierImpl *meta_barrier_impl_x11_new (MetaBarrier *barrier); G_END_DECLS #endif /* META_BARRIER_X11_H1 */ muffin-6.4.1/src/backends/x11/meta-renderer-x11.h0000664000175000017500000000244514723361714020233 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_RENDERER_X11_H #define META_RENDERER_X11_H #include #include "backends/meta-renderer.h" struct _MetaRendererX11Class { MetaRendererClass parent_class; }; #define META_TYPE_RENDERER_X11 (meta_renderer_x11_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaRendererX11, meta_renderer_x11, META, RENDERER_X11, MetaRenderer) #endif /* META_RENDERER_X11_H */ muffin-6.4.1/src/backends/x11/meta-input-device-tool-x11.h0000664000175000017500000000417414723361714021775 0ustar fabiofabio/* * Copyright © 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_DEVICE_TOOL_X11_H #define META_INPUT_DEVICE_TOOL_X11_H #include "clutter/clutter.h" #define META_TYPE_INPUT_DEVICE_TOOL_X11 (meta_input_device_tool_x11_get_type ()) #define META_INPUT_DEVICE_TOOL_X11(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_INPUT_DEVICE_TOOL_X11, MetaInputDeviceToolX11)) #define META_IS_INPUT_DEVICE_TOOL_X11(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_INPUT_DEVICE_TOOL_X11)) #define META_INPUT_DEVICE_TOOL_X11_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), META_TYPE_INPUT_DEVICE_TOOL_X11, MetaInputDeviceToolX11Class)) #define META_IS_INPUT_DEVICE_TOOL_X11_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), META_TYPE_INPUT_DEVICE_TOOL_X1)) #define META_INPUT_DEVICE_TOOL_X11_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_INPUT_DEVICE_TOOL_X11, MetaInputDeviceToolX11Class)) typedef struct _MetaInputDeviceToolX11 MetaInputDeviceToolX11; typedef struct _MetaInputDeviceToolX11Class MetaInputDeviceToolX11Class; struct _MetaInputDeviceToolX11 { ClutterInputDeviceTool parent_instance; }; struct _MetaInputDeviceToolX11Class { ClutterInputDeviceToolClass parent_class; }; GType meta_input_device_tool_x11_get_type (void) G_GNUC_CONST; ClutterInputDeviceTool * meta_input_device_tool_x11_new (guint serial, ClutterInputDeviceToolType type); #endif /* META_INPUT_DEVICE_TOOL_X11_H */ muffin-6.4.1/src/backends/x11/meta-gpu-xrandr.h0000664000175000017500000000333014723361714020077 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_GPU_XRANDR_H #define META_GPU_XRANDR_H #include #include #include "backends/meta-gpu.h" #include "backends/x11/meta-backend-x11.h" #define META_TYPE_GPU_XRANDR (meta_gpu_xrandr_get_type ()) G_DECLARE_FINAL_TYPE (MetaGpuXrandr, meta_gpu_xrandr, META, GPU_XRANDR, MetaGpu) XRRScreenResources * meta_gpu_xrandr_get_resources (MetaGpuXrandr *gpu_xrandr); void meta_gpu_xrandr_get_min_screen_size (MetaGpuXrandr *gpu_xrandr, int *min_width, int *min_height); void meta_gpu_xrandr_get_max_screen_size (MetaGpuXrandr *gpu_xrandr, int *max_width, int *max_height); MetaGpuXrandr * meta_gpu_xrandr_new (MetaBackendX11 *backend_x11); #endif /* META_GPU_XRANDR_H */ muffin-6.4.1/src/backends/x11/meta-gpu-xrandr.c0000664000175000017500000002544014723361714020100 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/x11/meta-gpu-xrandr.h" #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-output.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-crtc-xrandr.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include "backends/x11/meta-output-xrandr.h" struct _MetaGpuXrandr { MetaGpu parent; XRRScreenResources *resources; int min_screen_width; int min_screen_height; int max_screen_width; int max_screen_height; }; G_DEFINE_TYPE (MetaGpuXrandr, meta_gpu_xrandr, META_TYPE_GPU) XRRScreenResources * meta_gpu_xrandr_get_resources (MetaGpuXrandr *gpu_xrandr) { return gpu_xrandr->resources; } void meta_gpu_xrandr_get_min_screen_size (MetaGpuXrandr *gpu_xrandr, int *min_width, int *min_height) { *min_width = gpu_xrandr->min_screen_width; *min_height = gpu_xrandr->min_screen_height; } void meta_gpu_xrandr_get_max_screen_size (MetaGpuXrandr *gpu_xrandr, int *max_width, int *max_height) { *max_width = gpu_xrandr->max_screen_width; *max_height = gpu_xrandr->max_screen_height; } static int compare_outputs (const void *one, const void *two) { const MetaOutput *o_one = one, *o_two = two; return strcmp (o_one->name, o_two->name); } static char * get_xmode_name (XRRModeInfo *xmode) { int width = xmode->width; int height = xmode->height; return g_strdup_printf ("%dx%d", width, height); } static int get_current_dpi_scale (MetaMonitorManagerXrandr *manager_xrandr, MetaGpuXrandr *gpu_xrandr) { MetaMonitorManager *monitor_manager = META_MONITOR_MANAGER (manager_xrandr); MetaBackend *backend = meta_monitor_manager_get_backend (monitor_manager); MetaSettings *settings = meta_backend_get_settings (backend); Atom actual; int result, format; unsigned long n, left; g_autofree unsigned char *data = NULL; g_auto(GStrv) resources = NULL; Display *dpy; int i; if (gpu_xrandr->resources->timestamp == meta_monitor_manager_xrandr_get_config_timestamp (manager_xrandr)) { return meta_settings_get_ui_scaling_factor (settings); } dpy = meta_monitor_manager_xrandr_get_xdisplay (manager_xrandr); result = XGetWindowProperty (dpy, DefaultRootWindow (dpy), XA_RESOURCE_MANAGER, 0L, 65536, False, XA_STRING, &actual, &format, &n, &left, &data); if (result != Success || !data || actual != XA_STRING) return 1; resources = g_strsplit ((char *) data, "\n", -1); for (i = 0; resources && resources[i]; ++i) { if (g_str_has_prefix (resources[i], "Xft.dpi:")) { g_auto(GStrv) res = g_strsplit (resources[i], "\t", 2); if (res && res[0] && res[1]) { guint64 dpi; dpi = g_ascii_strtoull (res[1], NULL, 10); if (dpi > 0 && dpi < 96 * 10) { double factor = meta_settings_get_font_scaling_factor (settings); return MAX (1, roundf ((float) dpi / 96.0f / factor)); } } } } return 1; } static gboolean meta_gpu_xrandr_read_current (MetaGpu *gpu, GError **error) { MetaGpuXrandr *gpu_xrandr = META_GPU_XRANDR (gpu); MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_monitor_manager_xrandr_get_xdisplay (monitor_manager_xrandr); XRRScreenResources *resources; RROutput primary_output; unsigned int i, j; GList *l; Screen *screen; GList *outputs = NULL; GList *modes = NULL; GList *crtcs = NULL; gboolean has_transform; int dpi_scale = 1; if (gpu_xrandr->resources) XRRFreeScreenResources (gpu_xrandr->resources); gpu_xrandr->resources = NULL; XRRGetScreenSizeRange (xdisplay, DefaultRootWindow (xdisplay), &gpu_xrandr->min_screen_width, &gpu_xrandr->min_screen_height, &gpu_xrandr->max_screen_width, &gpu_xrandr->max_screen_height); screen = ScreenOfDisplay (xdisplay, DefaultScreen (xdisplay)); /* This is updated because we called XRRUpdateConfiguration. */ monitor_manager->screen_width = WidthOfScreen (screen); monitor_manager->screen_height = HeightOfScreen (screen); resources = XRRGetScreenResourcesCurrent (xdisplay, DefaultRootWindow (xdisplay)); if (!resources) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to retrieve Xrandr screen resources"); return FALSE; } gpu_xrandr->resources = resources; outputs = NULL; modes = NULL; crtcs = NULL; for (i = 0; i < (unsigned)resources->nmode; i++) { XRRModeInfo *xmode = &resources->modes[i]; MetaCrtcMode *mode; mode = g_object_new (META_TYPE_CRTC_MODE, NULL); mode->mode_id = xmode->id; mode->width = xmode->width; mode->height = xmode->height; mode->refresh_rate = (xmode->dotClock / ((float)xmode->hTotal * xmode->vTotal)); mode->flags = xmode->modeFlags; mode->name = get_xmode_name (xmode); modes = g_list_append (modes, mode); } meta_gpu_take_modes (gpu, modes); has_transform = !!(meta_monitor_manager_get_capabilities (monitor_manager) & META_MONITOR_MANAGER_CAPABILITY_NATIVE_OUTPUT_SCALING); if (has_transform && meta_monitor_manager_get_default_layout_mode (monitor_manager) == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) dpi_scale = get_current_dpi_scale (monitor_manager_xrandr, gpu_xrandr); for (i = 0; i < (unsigned)resources->ncrtc; i++) { XRRCrtcInfo *xrandr_crtc; XRRCrtcTransformAttributes *transform_attributes; RRCrtc crtc_id; MetaCrtc *crtc; crtc_id = resources->crtcs[i]; xrandr_crtc = XRRGetCrtcInfo (xdisplay, resources, crtc_id); if (!has_transform || !XRRGetCrtcTransform (xdisplay, crtc_id, &transform_attributes)) transform_attributes = NULL; crtc = meta_create_xrandr_crtc (gpu_xrandr, xrandr_crtc, crtc_id, resources, transform_attributes, dpi_scale); XFree (transform_attributes); XRRFreeCrtcInfo (xrandr_crtc); crtcs = g_list_append (crtcs, crtc); } meta_gpu_take_crtcs (gpu, crtcs); if (has_transform && dpi_scale == 1 && meta_monitor_manager_get_default_layout_mode (monitor_manager) == META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL) { dpi_scale = ceilf (meta_monitor_manager_get_maximum_crtc_scale (monitor_manager)); if (dpi_scale > 1) { for (l = crtcs; l; l = l->next) { MetaCrtc *crtc = l->data; crtc->scale *= dpi_scale; } } } primary_output = XRRGetOutputPrimary (xdisplay, DefaultRootWindow (xdisplay)); for (i = 0; i < (unsigned)resources->noutput; i++) { RROutput output_id; XRROutputInfo *xrandr_output; output_id = resources->outputs[i]; xrandr_output = XRRGetOutputInfo (xdisplay, resources, output_id); if (!xrandr_output) continue; if (xrandr_output->connection != RR_Disconnected) { MetaOutput *output; output = meta_create_xrandr_output (gpu_xrandr, xrandr_output, output_id, primary_output); if (output) outputs = g_list_prepend (outputs, output); } XRRFreeOutputInfo (xrandr_output); } /* Sort the outputs for easier handling in MetaMonitorConfig */ outputs = g_list_sort (outputs, compare_outputs); meta_gpu_take_outputs (gpu, outputs); /* Now fix the clones */ for (l = outputs; l; l = l->next) { MetaOutput *output = l->data; GList *k; for (j = 0; j < output->n_possible_clones; j++) { RROutput clone = GPOINTER_TO_INT (output->possible_clones[j]); for (k = outputs; k; k = k->next) { MetaOutput *possible_clone = k->data; if (clone == (XID) possible_clone->winsys_id) { output->possible_clones[j] = possible_clone; break; } } } } return TRUE; } MetaGpuXrandr * meta_gpu_xrandr_new (MetaBackendX11 *backend_x11) { return g_object_new (META_TYPE_GPU_XRANDR, "backend", backend_x11, NULL); } static void meta_gpu_xrandr_finalize (GObject *object) { MetaGpuXrandr *gpu_xrandr = META_GPU_XRANDR (object); g_clear_pointer (&gpu_xrandr->resources, XRRFreeScreenResources); G_OBJECT_CLASS (meta_gpu_xrandr_parent_class)->finalize (object); } static void meta_gpu_xrandr_init (MetaGpuXrandr *gpu_xrandr) { } static void meta_gpu_xrandr_class_init (MetaGpuXrandrClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaGpuClass *gpu_class = META_GPU_CLASS (klass); object_class->finalize = meta_gpu_xrandr_finalize; gpu_class->read_current = meta_gpu_xrandr_read_current; } muffin-6.4.1/src/backends/x11/meta-input-device-tool-x11.c0000664000175000017500000000262614723361714021770 0ustar fabiofabio/* * Copyright © 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "config.h" #include "meta-input-device-tool-x11.h" G_DEFINE_TYPE (MetaInputDeviceToolX11, meta_input_device_tool_x11, CLUTTER_TYPE_INPUT_DEVICE_TOOL) static void meta_input_device_tool_x11_class_init (MetaInputDeviceToolX11Class *klass) { } static void meta_input_device_tool_x11_init (MetaInputDeviceToolX11 *tool) { } ClutterInputDeviceTool * meta_input_device_tool_x11_new (guint serial, ClutterInputDeviceToolType type) { return g_object_new (META_TYPE_INPUT_DEVICE_TOOL_X11, "type", type, "serial", serial, NULL); } muffin-6.4.1/src/backends/x11/meta-clutter-backend-x11.c0000664000175000017500000001156614723361714021473 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-renderer.h" #include "backends/x11/meta-clutter-backend-x11.h" #include "backends/x11/meta-keymap-x11.h" #include "backends/x11/meta-seat-x11.h" #include "backends/x11/meta-xkb-a11y-x11.h" #include "backends/x11/nested/meta-stage-x11-nested.h" #include "clutter/clutter-mutter.h" #include "clutter/clutter.h" #include "core/bell.h" #include "meta/meta-backend.h" struct _MetaClutterBackendX11 { ClutterBackendX11 parent; MetaSeatX11 *core_seat; }; G_DEFINE_TYPE (MetaClutterBackendX11, meta_clutter_backend_x11, CLUTTER_TYPE_BACKEND_X11) static CoglRenderer * meta_clutter_backend_x11_get_renderer (ClutterBackend *clutter_backend, GError **error) { MetaBackend *backend = meta_get_backend (); MetaRenderer *renderer = meta_backend_get_renderer (backend); return meta_renderer_create_cogl_renderer (renderer); } static ClutterStageWindow * meta_clutter_backend_x11_create_stage (ClutterBackend *backend, ClutterStage *wrapper, GError **error) { ClutterStageWindow *stage; GType stage_type; if (meta_is_wayland_compositor ()) stage_type = META_TYPE_STAGE_X11_NESTED; else stage_type = META_TYPE_STAGE_X11; stage = g_object_new (stage_type, "backend", backend, "wrapper", wrapper, NULL); return stage; } static gboolean meta_clutter_backend_x11_translate_event (ClutterBackend *backend, gpointer native, ClutterEvent *event) { MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend); MetaStageX11 *stage_x11; ClutterBackendClass *clutter_backend_class; clutter_backend_class = CLUTTER_BACKEND_CLASS (meta_clutter_backend_x11_parent_class); if (clutter_backend_class->translate_event (backend, native, event)) return TRUE; stage_x11 = META_STAGE_X11 (clutter_backend_get_stage_window (backend)); if (meta_stage_x11_translate_event (stage_x11, native, event)) return TRUE; if (meta_seat_x11_translate_event (backend_x11->core_seat, native, event)) return TRUE; return FALSE; } static void meta_clutter_backend_x11_init_events (ClutterBackend *backend) { MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend); int event_base, first_event, first_error; if (XQueryExtension (clutter_x11_get_default_display (), "XInputExtension", &event_base, &first_event, &first_error)) { int major = 2; int minor = 3; if (XIQueryVersion (clutter_x11_get_default_display (), &major, &minor) != BadRequest) { backend_x11->core_seat = meta_seat_x11_new (event_base, META_VIRTUAL_CORE_POINTER_ID, META_VIRTUAL_CORE_KEYBOARD_ID); } } if (!backend_x11->core_seat) g_error ("No XInput 2.3 support"); } static ClutterSeat * meta_clutter_backend_x11_get_default_seat (ClutterBackend *backend) { MetaClutterBackendX11 *backend_x11 = META_CLUTTER_BACKEND_X11 (backend); return CLUTTER_SEAT (backend_x11->core_seat); } static void meta_clutter_backend_x11_init (MetaClutterBackendX11 *clutter_backend_x11) { } static void meta_clutter_backend_x11_class_init (MetaClutterBackendX11Class *klass) { ClutterBackendClass *clutter_backend_class = CLUTTER_BACKEND_CLASS (klass); clutter_backend_class->get_renderer = meta_clutter_backend_x11_get_renderer; clutter_backend_class->create_stage = meta_clutter_backend_x11_create_stage; clutter_backend_class->translate_event = meta_clutter_backend_x11_translate_event; clutter_backend_class->init_events = meta_clutter_backend_x11_init_events; clutter_backend_class->get_default_seat = meta_clutter_backend_x11_get_default_seat; } muffin-6.4.1/src/backends/x11/meta-renderer-x11.c0000664000175000017500000000562314723361714020227 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-renderer-view.h" #include "backends/meta-renderer.h" #include "backends/x11/meta-renderer-x11.h" #include "clutter/x11/clutter-x11.h" #include "cogl/cogl-xlib.h" #include "cogl/cogl.h" #include "cogl/winsys/cogl-winsys-egl-x11-private.h" #include "cogl/winsys/cogl-winsys-glx-private.h" #include "core/boxes-private.h" #include "meta/meta-backend.h" #include "meta/util.h" G_DEFINE_TYPE (MetaRendererX11, meta_renderer_x11, META_TYPE_RENDERER) static const CoglWinsysVtable * get_x11_cogl_winsys_vtable (CoglRenderer *renderer) { #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT if (meta_is_wayland_compositor ()) return _cogl_winsys_egl_xlib_get_vtable (); #endif switch (renderer->driver) { case COGL_DRIVER_GLES2: #ifdef COGL_HAS_EGL_PLATFORM_XLIB_SUPPORT return _cogl_winsys_egl_xlib_get_vtable (); #else break; #endif case COGL_DRIVER_GL: case COGL_DRIVER_GL3: #ifdef COGL_HAS_GLX_SUPPORT return _cogl_winsys_glx_get_vtable (); #else break; #endif case COGL_DRIVER_ANY: case COGL_DRIVER_NOP: break; } g_assert_not_reached (); return NULL; } static CoglRenderer * meta_renderer_x11_create_cogl_renderer (MetaRenderer *renderer) { CoglRenderer *cogl_renderer; Display *xdisplay = clutter_x11_get_default_display (); cogl_renderer = cogl_renderer_new (); cogl_renderer_set_custom_winsys (cogl_renderer, get_x11_cogl_winsys_vtable, NULL); cogl_xlib_renderer_set_foreign_display (cogl_renderer, xdisplay); cogl_xlib_renderer_request_reset_on_video_memory_purge (cogl_renderer, TRUE); return cogl_renderer; } static void meta_renderer_x11_init (MetaRendererX11 *renderer_x11) { } static void meta_renderer_x11_class_init (MetaRendererX11Class *klass) { MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass); renderer_class->create_cogl_renderer = meta_renderer_x11_create_cogl_renderer; } muffin-6.4.1/src/backends/x11/meta-event-x11.c0000664000175000017500000001120014723361714017526 0ustar fabiofabio/* Copyright (C) 2006, 2007, 2008 OpenedHand Ltd * Copyright (C) 2009, 2010 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * * * Authored by: * Matthew Allum * Emmanuele Bassi */ #include "config.h" #include #include #include "backends/x11/meta-event-x11.h" #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" MetaEventX11 * meta_event_x11_new (void) { return g_slice_new0 (MetaEventX11); } MetaEventX11 * meta_event_x11_copy (MetaEventX11 *event_x11) { if (event_x11 != NULL) return g_slice_dup (MetaEventX11, event_x11); return NULL; } void meta_event_x11_free (MetaEventX11 *event_x11) { if (event_x11 != NULL) g_slice_free (MetaEventX11, event_x11); } /** * meta_x11_handle_event: * @xevent: pointer to XEvent structure * * This function processes a single X event; it can be used to hook * into external X11 event processing (for example, a GDK filter * function). * * Return value: #ClutterX11FilterReturn. %CLUTTER_X11_FILTER_REMOVE * indicates that Clutter has internally handled the event and the * caller should do no further processing. %CLUTTER_X11_FILTER_CONTINUE * indicates that Clutter is either not interested in the event, * or has used the event to update internal state without taking * any exclusive action. %CLUTTER_X11_FILTER_TRANSLATE will not * occur. * * Since: 0.8 */ ClutterX11FilterReturn meta_x11_handle_event (XEvent *xevent) { ClutterX11FilterReturn result; ClutterBackend *backend; ClutterEvent *event; gint spin = 1; ClutterBackendX11 *backend_x11; Display *xdisplay; gboolean allocated_event; /* The return values here are someone approximate; we return * CLUTTER_X11_FILTER_REMOVE if a clutter event is * generated for the event. This mostly, but not entirely, * corresponds to whether other event processing should be * excluded. As long as the stage window is not shared with another * toolkit it should be safe, and never return * %CLUTTER_X11_FILTER_REMOVE when more processing is needed. */ result = CLUTTER_X11_FILTER_CONTINUE; _clutter_threads_acquire_lock (); backend = clutter_get_default_backend (); event = clutter_event_new (CLUTTER_NOTHING); backend_x11 = CLUTTER_BACKEND_X11 (backend); xdisplay = backend_x11->xdpy; allocated_event = XGetEventData (xdisplay, &xevent->xcookie); if (_clutter_backend_translate_event (backend, xevent, event)) { _clutter_event_push (event, FALSE); result = CLUTTER_X11_FILTER_REMOVE; } else { clutter_event_free (event); goto out; } /* * Motion events can generate synthetic enter and leave events, so if we * are processing a motion event, we need to spin the event loop at least * two extra times to pump the enter/leave events through (otherwise they * just get pushed down the queue and never processed). */ if (event->type == CLUTTER_MOTION) spin += 2; while (spin > 0 && (event = clutter_event_get ())) { /* forward the event into clutter for emission etc. */ _clutter_stage_queue_event (event->any.stage, event, FALSE); --spin; } out: if (allocated_event) XFreeEventData (xdisplay, &xevent->xcookie); _clutter_threads_release_lock (); return result; } Time meta_x11_get_current_event_time (void) { ClutterBackend *backend = clutter_get_default_backend (); return CLUTTER_BACKEND_X11 (backend)->last_event_time; } gint meta_x11_event_get_key_group (const ClutterEvent *event) { MetaEventX11 *event_x11; g_return_val_if_fail (event != NULL, 0); g_return_val_if_fail (event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE, 0); event_x11 = _clutter_event_get_platform_data (event); if (event_x11 == NULL) return 0; return event_x11->key_group; } guint meta_x11_event_sequence_get_touch_detail (const ClutterEventSequence *sequence) { g_return_val_if_fail (sequence != NULL, 0); return GPOINTER_TO_UINT (sequence); } muffin-6.4.1/src/backends/x11/meta-seat-x11.c0000664000175000017500000021745214723361714017362 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #include "config.h" #include #include "backends/x11/meta-event-x11.h" #include "backends/x11/meta-input-device-tool-x11.h" #include "backends/x11/meta-input-device-x11.h" #include "backends/x11/meta-keymap-x11.h" #include "backends/x11/meta-stage-x11.h" #include "backends/x11/meta-virtual-input-device-x11.h" #include "backends/x11/meta-xkb-a11y-x11.h" #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" #include "core/bell.h" #include "meta-seat-x11.h" enum { PROP_0, PROP_OPCODE, PROP_POINTER_ID, PROP_KEYBOARD_ID, N_PROPS, /* This property is overridden */ PROP_TOUCH_MODE, }; struct _MetaSeatX11 { ClutterSeat parent_instance; ClutterInputDevice *core_pointer; ClutterInputDevice *core_keyboard; GList *devices; GHashTable *devices_by_id; GHashTable *tools_by_serial; MetaKeymapX11 *keymap; int pointer_id; int keyboard_id; int opcode; guint has_touchscreens : 1; guint touch_mode : 1; }; static GParamSpec *props[N_PROPS] = { 0 }; G_DEFINE_TYPE (MetaSeatX11, meta_seat_x11, CLUTTER_TYPE_SEAT) static const char *clutter_input_axis_atom_names[] = { "Abs X", /* CLUTTER_INPUT_AXIS_X */ "Abs Y", /* CLUTTER_INPUT_AXIS_Y */ "Abs Pressure", /* CLUTTER_INPUT_AXIS_PRESSURE */ "Abs Tilt X", /* CLUTTER_INPUT_AXIS_XTILT */ "Abs Tilt Y", /* CLUTTER_INPUT_AXIS_YTILT */ "Abs Wheel", /* CLUTTER_INPUT_AXIS_WHEEL */ "Abs Distance", /* CLUTTER_INPUT_AXIS_DISTANCE */ }; static const char *wacom_type_atoms[] = { "STYLUS", "CURSOR", "ERASER", "PAD", "TOUCH" }; #define N_WACOM_TYPE_ATOMS G_N_ELEMENTS (wacom_type_atoms) enum { WACOM_TYPE_STYLUS, WACOM_TYPE_CURSOR, WACOM_TYPE_ERASER, WACOM_TYPE_PAD, WACOM_TYPE_TOUCH, }; enum { PAD_AXIS_FIRST = 3, /* First axes are always x/y/pressure, ignored in pads */ PAD_AXIS_STRIP1 = PAD_AXIS_FIRST, PAD_AXIS_STRIP2, PAD_AXIS_RING1, PAD_AXIS_RING2, }; #define N_AXIS_ATOMS G_N_ELEMENTS (clutter_input_axis_atom_names) static Atom clutter_input_axis_atoms[N_AXIS_ATOMS] = { 0, }; static void translate_valuator_class (Display *xdisplay, ClutterInputDevice *device, XIValuatorClassInfo *class) { static gboolean atoms_initialized = FALSE; ClutterInputAxis i, axis = CLUTTER_INPUT_AXIS_IGNORE; if (G_UNLIKELY (!atoms_initialized)) { XInternAtoms (xdisplay, (char **) clutter_input_axis_atom_names, N_AXIS_ATOMS, False, clutter_input_axis_atoms); atoms_initialized = TRUE; } for (i = 0; i < N_AXIS_ATOMS; i += 1) { if (clutter_input_axis_atoms[i] == class->label) { axis = i + 1; break; } } _clutter_input_device_add_axis (device, axis, class->min, class->max, class->resolution); g_debug ("Added axis '%s' (min:%.2f, max:%.2fd, res:%d) of device %d", clutter_input_axis_atom_names[axis], class->min, class->max, class->resolution, device->id); } static void translate_device_classes (Display *xdisplay, ClutterInputDevice *device, XIAnyClassInfo **classes, int n_classes) { int i; for (i = 0; i < n_classes; i++) { XIAnyClassInfo *class_info = classes[i]; switch (class_info->type) { case XIKeyClass: { XIKeyClassInfo *key_info = (XIKeyClassInfo *) class_info; int j; _clutter_input_device_set_n_keys (device, key_info->num_keycodes); for (j = 0; j < key_info->num_keycodes; j++) { clutter_input_device_set_key (device, j, key_info->keycodes[i], 0); } } break; case XIValuatorClass: translate_valuator_class (xdisplay, device, (XIValuatorClassInfo *) class_info); break; case XIScrollClass: { XIScrollClassInfo *scroll_info = (XIScrollClassInfo *) class_info; ClutterScrollDirection direction; if (scroll_info->scroll_type == XIScrollTypeVertical) direction = CLUTTER_SCROLL_DOWN; else direction = CLUTTER_SCROLL_RIGHT; g_debug ("Scroll valuator %d: %s, increment: %f", scroll_info->number, scroll_info->scroll_type == XIScrollTypeVertical ? "vertical" : "horizontal", scroll_info->increment); _clutter_input_device_add_scroll_info (device, scroll_info->number, direction, scroll_info->increment); } break; default: break; } } } static gboolean is_touch_device (XIAnyClassInfo **classes, int n_classes, ClutterInputDeviceType *device_type, uint32_t *n_touch_points) { int i; for (i = 0; i < n_classes; i++) { XITouchClassInfo *class = (XITouchClassInfo *) classes[i]; if (class->type != XITouchClass) continue; if (class->num_touches > 0) { if (class->mode == XIDirectTouch) *device_type = CLUTTER_TOUCHSCREEN_DEVICE; else if (class->mode == XIDependentTouch) *device_type = CLUTTER_TOUCHPAD_DEVICE; else continue; *n_touch_points = class->num_touches; return TRUE; } } return FALSE; } static gboolean has_8bit_property (XIDeviceInfo *info, const char *name) { gulong nitems, bytes_after; uint32_t *data = NULL; int rc, format; Atom type; Atom prop; prop = XInternAtom (clutter_x11_get_default_display (), name, True); if (prop == None) return FALSE; clutter_x11_trap_x_errors (); rc = XIGetProperty (clutter_x11_get_default_display (), info->deviceid, prop, 0, 1, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); clutter_x11_untrap_x_errors (); /* We don't care about the data */ XFree (data); if (rc != Success || type != XA_INTEGER || format != 8 || nitems != 1) return FALSE; return TRUE; } static gboolean is_touchpad_device (XIDeviceInfo *info) { return has_8bit_property (info, "libinput Tapping Enabled") || has_8bit_property (info, "Synaptics Off"); } static gboolean get_device_ids (XIDeviceInfo *info, char **vendor_id, char **product_id) { gulong nitems, bytes_after; uint32_t *data = NULL; int rc, format; Atom type; clutter_x11_trap_x_errors (); rc = XIGetProperty (clutter_x11_get_default_display (), info->deviceid, XInternAtom (clutter_x11_get_default_display (), "Device Product ID", False), 0, 2, False, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); clutter_x11_untrap_x_errors (); if (rc != Success || type != XA_INTEGER || format != 32 || nitems != 2) { XFree (data); return FALSE; } if (vendor_id) *vendor_id = g_strdup_printf ("%.4x", data[0]); if (product_id) *product_id = g_strdup_printf ("%.4x", data[1]); XFree (data); return TRUE; } static char * get_device_node_path (XIDeviceInfo *info) { gulong nitems, bytes_after; guchar *data; int rc, format; Atom prop, type; char *node_path; prop = XInternAtom (clutter_x11_get_default_display (), "Device Node", False); if (prop == None) return NULL; clutter_x11_trap_x_errors (); rc = XIGetProperty (clutter_x11_get_default_display (), info->deviceid, prop, 0, 1024, False, XA_STRING, &type, &format, &nitems, &bytes_after, (guchar **) &data); if (clutter_x11_untrap_x_errors ()) return NULL; if (rc != Success || type != XA_STRING || format != 8) { XFree (data); return FALSE; } node_path = g_strdup ((char *) data); XFree (data); return node_path; } static void get_pad_features (XIDeviceInfo *info, uint32_t *n_rings, uint32_t *n_strips) { int i, rings = 0, strips = 0; for (i = PAD_AXIS_FIRST; i < info->num_classes; i++) { XIValuatorClassInfo *valuator = (XIValuatorClassInfo*) info->classes[i]; int axis = valuator->number; if (valuator->type != XIValuatorClass) continue; if (valuator->max <= 1) continue; /* Ring/strip axes are fixed in pad devices as handled by the * wacom driver. Match those to detect pad features. */ if (axis == PAD_AXIS_STRIP1 || axis == PAD_AXIS_STRIP2) strips++; else if (axis == PAD_AXIS_RING1 || axis == PAD_AXIS_RING2) rings++; } *n_rings = rings; *n_strips = strips; } /* The Wacom driver exports the tool type as property. Use that over guessing based on the device name */ static gboolean guess_source_from_wacom_type (XIDeviceInfo *info, ClutterInputDeviceType *source_out) { gulong nitems, bytes_after; uint32_t *data = NULL; int rc, format; Atom type; Atom prop; Atom device_type; Atom types[N_WACOM_TYPE_ATOMS]; prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Tool Type", True); if (prop == None) return FALSE; clutter_x11_trap_x_errors (); rc = XIGetProperty (clutter_x11_get_default_display (), info->deviceid, prop, 0, 1, False, XA_ATOM, &type, &format, &nitems, &bytes_after, (guchar **) &data); clutter_x11_untrap_x_errors (); if (rc != Success || type != XA_ATOM || format != 32 || nitems != 1) { XFree (data); return FALSE; } device_type = *data; XFree (data); if (device_type == 0) return FALSE; rc = XInternAtoms (clutter_x11_get_default_display (), (char **)wacom_type_atoms, N_WACOM_TYPE_ATOMS, False, types); if (rc == 0) return FALSE; if (device_type == types[WACOM_TYPE_STYLUS]) { *source_out = CLUTTER_PEN_DEVICE; } else if (device_type == types[WACOM_TYPE_CURSOR]) { *source_out = CLUTTER_CURSOR_DEVICE; } else if (device_type == types[WACOM_TYPE_ERASER]) { *source_out = CLUTTER_ERASER_DEVICE; } else if (device_type == types[WACOM_TYPE_PAD]) { *source_out = CLUTTER_PAD_DEVICE; } else if (device_type == types[WACOM_TYPE_TOUCH]) { uint32_t num_touches = 0; if (!is_touch_device (info->classes, info->num_classes, source_out, &num_touches)) *source_out = CLUTTER_TOUCHSCREEN_DEVICE; } else { return FALSE; } return TRUE; } static ClutterInputDevice * create_device (MetaSeatX11 *seat_x11, ClutterBackend *backend, XIDeviceInfo *info) { ClutterInputDeviceType source, touch_source; ClutterInputDevice *retval; ClutterInputMode mode; gboolean is_enabled; uint32_t num_touches = 0, num_rings = 0, num_strips = 0; char *vendor_id = NULL, *product_id = NULL, *node_path = NULL; if (info->use == XIMasterKeyboard || info->use == XISlaveKeyboard) { source = CLUTTER_KEYBOARD_DEVICE; } else if (is_touchpad_device (info)) { source = CLUTTER_TOUCHPAD_DEVICE; } else if (info->use == XISlavePointer && is_touch_device (info->classes, info->num_classes, &touch_source, &num_touches)) { source = touch_source; } else if (!guess_source_from_wacom_type (info, &source)) { char *name; name = g_ascii_strdown (info->name, -1); if (strstr (name, "eraser") != NULL) source = CLUTTER_ERASER_DEVICE; else if (strstr (name, "cursor") != NULL) source = CLUTTER_CURSOR_DEVICE; else if (strstr (name, " pad") != NULL) source = CLUTTER_PAD_DEVICE; else if (strstr (name, "wacom") != NULL || strstr (name, "pen") != NULL) source = CLUTTER_PEN_DEVICE; else if (strstr (name, "touchpad") != NULL) source = CLUTTER_TOUCHPAD_DEVICE; else source = CLUTTER_POINTER_DEVICE; g_free (name); } switch (info->use) { case XIMasterKeyboard: case XIMasterPointer: mode = CLUTTER_INPUT_MODE_MASTER; is_enabled = TRUE; break; case XISlaveKeyboard: case XISlavePointer: mode = CLUTTER_INPUT_MODE_SLAVE; is_enabled = FALSE; break; case XIFloatingSlave: default: mode = CLUTTER_INPUT_MODE_FLOATING; is_enabled = FALSE; break; } if (info->use != XIMasterKeyboard && info->use != XIMasterPointer) { get_device_ids (info, &vendor_id, &product_id); node_path = get_device_node_path (info); } if (source == CLUTTER_PAD_DEVICE) { is_enabled = TRUE; get_pad_features (info, &num_rings, &num_strips); } retval = g_object_new (META_TYPE_INPUT_DEVICE_X11, "name", info->name, "id", info->deviceid, "has-cursor", (info->use == XIMasterPointer), "device-type", source, "device-mode", mode, "backend", backend, "enabled", is_enabled, "vendor-id", vendor_id, "product-id", product_id, "device-node", node_path, "n-rings", num_rings, "n-strips", num_strips, "n-mode-groups", MAX (num_rings, num_strips), "seat", seat_x11, NULL); translate_device_classes (clutter_x11_get_default_display (), retval, info->classes, info->num_classes); g_free (vendor_id); g_free (product_id); g_free (node_path); g_debug ("Created device '%s' (id: %d, has-cursor: %s)", info->name, info->deviceid, info->use == XIMasterPointer ? "yes" : "no"); return retval; } static void pad_passive_button_grab (ClutterInputDevice *device) { XIGrabModifiers xi_grab_mods = { XIAnyModifier, }; XIEventMask xi_event_mask; int device_id, rc; device_id = clutter_input_device_get_device_id (device); xi_event_mask.deviceid = device_id; xi_event_mask.mask_len = XIMaskLen (XI_LASTEVENT); xi_event_mask.mask = g_new0 (unsigned char, xi_event_mask.mask_len); XISetMask (xi_event_mask.mask, XI_Motion); XISetMask (xi_event_mask.mask, XI_ButtonPress); XISetMask (xi_event_mask.mask, XI_ButtonRelease); clutter_x11_trap_x_errors (); rc = XIGrabButton (clutter_x11_get_default_display (), device_id, XIAnyButton, clutter_x11_get_root_window (), None, XIGrabModeSync, XIGrabModeSync, True, &xi_event_mask, 1, &xi_grab_mods); if (rc != 0) { g_warning ("Could not passively grab pad device: %s", clutter_input_device_get_device_name (device)); } else { XIAllowEvents (clutter_x11_get_default_display (), device_id, XIAsyncDevice, CLUTTER_CURRENT_TIME); } clutter_x11_untrap_x_errors (); g_free (xi_event_mask.mask); } static void update_touch_mode (MetaSeatX11 *seat_x11) { gboolean touch_mode; touch_mode = seat_x11->has_touchscreens; if (seat_x11->touch_mode == touch_mode) return; seat_x11->touch_mode = touch_mode; g_object_notify (G_OBJECT (seat_x11), "touch-mode"); } static ClutterInputDevice * add_device (MetaSeatX11 *seat_x11, ClutterBackend *backend, XIDeviceInfo *info, gboolean in_construction) { ClutterInputDevice *device; device = create_device (seat_x11, backend, info); g_hash_table_replace (seat_x11->devices_by_id, GINT_TO_POINTER (info->deviceid), device); if (info->use == XIMasterPointer && info->deviceid == seat_x11->pointer_id) { seat_x11->core_pointer = device; } else if (info->use == XIMasterKeyboard && info->deviceid == seat_x11->keyboard_id) { seat_x11->core_keyboard = device; } else if ((info->use == XISlavePointer && info->attachment == seat_x11->pointer_id) || (info->use == XISlaveKeyboard && info->attachment == seat_x11->keyboard_id)) { seat_x11->devices = g_list_prepend (seat_x11->devices, device); } else { g_warning ("Unhandled device: %s", clutter_input_device_get_device_name (device)); } if (clutter_input_device_get_device_type (device) == CLUTTER_PAD_DEVICE) pad_passive_button_grab (device); /* relationships between devices and signal emissions are not * necessary while we're constructing the device manager instance */ if (!in_construction) { if (info->use == XISlavePointer || info->use == XISlaveKeyboard) { ClutterInputDevice *master; master = g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (info->attachment)); _clutter_input_device_set_associated_device (device, master); _clutter_input_device_add_slave (master, device); } } return device; } static gboolean has_touchscreens (MetaSeatX11 *seat_x11) { GList *l; for (l = seat_x11->devices; l; l = l->next) { if (clutter_input_device_get_device_type (l->data) == CLUTTER_TOUCHSCREEN_DEVICE) return TRUE; } return FALSE; } static void remove_device (MetaSeatX11 *seat_x11, ClutterInputDevice *device) { if (seat_x11->core_pointer == device) { seat_x11->core_pointer = NULL; } else if (seat_x11->core_keyboard == device) { seat_x11->core_keyboard = NULL; } else { seat_x11->devices = g_list_remove (seat_x11->devices, device); } } static gboolean meta_seat_x11_handle_device_event (ClutterSeat *seat, ClutterEvent *event) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat); ClutterInputDevice *device = event->device.device; gboolean is_touch; is_touch = clutter_input_device_get_device_type (device) == CLUTTER_TOUCHSCREEN_DEVICE; switch (event->type) { case CLUTTER_DEVICE_ADDED: seat_x11->has_touchscreens |= is_touch; break; case CLUTTER_DEVICE_REMOVED: if (is_touch) seat_x11->has_touchscreens = has_touchscreens (seat_x11); break; default: break; } if (is_touch) update_touch_mode (seat_x11); return TRUE; } static void relate_masters (gpointer key, gpointer value, gpointer data) { MetaSeatX11 *seat_x11 = data; ClutterInputDevice *device, *relative; device = g_hash_table_lookup (seat_x11->devices_by_id, key); relative = g_hash_table_lookup (seat_x11->devices_by_id, value); _clutter_input_device_set_associated_device (device, relative); _clutter_input_device_set_associated_device (relative, device); } static void relate_slaves (gpointer key, gpointer value, gpointer data) { MetaSeatX11 *seat_x11 = data; ClutterInputDevice *master, *slave; slave = g_hash_table_lookup (seat_x11->devices_by_id, key); master = g_hash_table_lookup (seat_x11->devices_by_id, value); _clutter_input_device_set_associated_device (slave, master); _clutter_input_device_add_slave (master, slave); } static uint device_get_tool_serial (ClutterInputDevice *device) { gulong nitems, bytes_after; uint32_t *data = NULL; int serial_id = 0; int rc, format; Atom type; Atom prop; prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True); if (prop == None) return 0; clutter_x11_trap_x_errors (); rc = XIGetProperty (clutter_x11_get_default_display (), clutter_input_device_get_device_id (device), prop, 0, 4, FALSE, XA_INTEGER, &type, &format, &nitems, &bytes_after, (guchar **) &data); clutter_x11_untrap_x_errors (); if (rc == Success && type == XA_INTEGER && format == 32 && nitems >= 4) serial_id = data[3]; XFree (data); return serial_id; } static gboolean translate_hierarchy_event (ClutterBackend *backend, MetaSeatX11 *seat_x11, XIHierarchyEvent *ev, ClutterEvent *event) { int i; gboolean retval = FALSE; for (i = 0; i < ev->num_info; i++) { if (ev->info[i].flags & XIDeviceEnabled && !g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (ev->info[i].deviceid))) { XIDeviceInfo *info; int n_devices; g_debug ("Hierarchy event: device enabled"); clutter_x11_trap_x_errors (); info = XIQueryDevice (clutter_x11_get_default_display (), ev->info[i].deviceid, &n_devices); clutter_x11_untrap_x_errors (); if (info != NULL) { ClutterInputDevice *device; device = add_device (seat_x11, backend, &info[0], FALSE); event->any.type = CLUTTER_DEVICE_ADDED; event->any.time = ev->time; clutter_event_set_device (event, device); retval = TRUE; XIFreeDeviceInfo (info); } } else if (ev->info[i].flags & XIDeviceDisabled) { g_autoptr (ClutterInputDevice) device = NULL; g_debug ("Hierarchy event: device disabled"); g_hash_table_steal_extended (seat_x11->devices_by_id, GINT_TO_POINTER (ev->info[i].deviceid), NULL, (gpointer) &device); if (device != NULL) { remove_device (seat_x11, device); event->any.type = CLUTTER_DEVICE_REMOVED; event->any.time = ev->time; clutter_event_set_device (event, device); retval = TRUE; } } else if ((ev->info[i].flags & XISlaveAttached) || (ev->info[i].flags & XISlaveDetached)) { ClutterInputDevice *master, *slave; XIDeviceInfo *info; int n_devices; g_debug ("Hierarchy event: slave %s", (ev->info[i].flags & XISlaveAttached) ? "attached" : "detached"); slave = g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (ev->info[i].deviceid)); master = clutter_input_device_get_associated_device (slave); /* detach the slave in both cases */ if (master != NULL) { _clutter_input_device_remove_slave (master, slave); _clutter_input_device_set_associated_device (slave, NULL); } /* and attach the slave to the new master if needed */ if (ev->info[i].flags & XISlaveAttached) { clutter_x11_trap_x_errors (); info = XIQueryDevice (clutter_x11_get_default_display (), ev->info[i].deviceid, &n_devices); clutter_x11_untrap_x_errors (); if (info != NULL) { master = g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (info->attachment)); if (master != NULL) { _clutter_input_device_set_associated_device (slave, master); _clutter_input_device_add_slave (master, slave); } XIFreeDeviceInfo (info); } } } } return retval; } static void translate_property_event (MetaSeatX11 *seat_x11, XIEvent *event) { XIPropertyEvent *xev = (XIPropertyEvent *) event; Atom serial_ids_prop = XInternAtom (clutter_x11_get_default_display (), "Wacom Serial IDs", True); ClutterInputDevice *device; device = g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (xev->deviceid)); if (!device) return; if (xev->property == serial_ids_prop) { ClutterInputDeviceTool *tool = NULL; ClutterInputDeviceToolType type; int serial_id; serial_id = device_get_tool_serial (device); if (serial_id != 0) { tool = g_hash_table_lookup (seat_x11->tools_by_serial, GUINT_TO_POINTER (serial_id)); if (!tool) { type = clutter_input_device_get_device_type (device) == CLUTTER_ERASER_DEVICE ? CLUTTER_INPUT_DEVICE_TOOL_ERASER : CLUTTER_INPUT_DEVICE_TOOL_PEN; tool = meta_input_device_tool_x11_new (serial_id, type); g_hash_table_insert (seat_x11->tools_by_serial, GUINT_TO_POINTER (serial_id), tool); } } meta_input_device_x11_update_tool (device, tool); g_signal_emit_by_name (seat_x11, "tool-changed", device, tool); } } static void translate_raw_event (MetaSeatX11 *seat_x11, XEvent *xevent) { ClutterInputDevice *device; XGenericEventCookie *cookie; XIEvent *xi_event; XIRawEvent *xev; float x,y; cookie = &xevent->xcookie; xi_event = (XIEvent *) cookie->data; xev = (XIRawEvent *) xi_event; device = g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (xev->deviceid)); if (device == NULL) return; if (!_clutter_is_input_pointer_a11y_enabled (device)) return; switch (cookie->evtype) { case XI_RawMotion: g_debug ("raw motion: device:%d '%s'", device->id, device->device_name); /* We don't get actual pointer location with raw events, and we cannot * rely on `clutter_input_device_get_coords()` either because of * unreparented toplevels (like all client-side decoration windows), * so we need to explicitely query the pointer here... */ if (meta_input_device_x11_get_pointer_location (device, &x, &y)) _clutter_input_pointer_a11y_on_motion_event (device, x, y); break; case XI_RawButtonPress: case XI_RawButtonRelease: g_debug ("raw button %s: device:%d '%s' button %i", cookie->evtype == XI_RawButtonPress ? "press " : "release", device->id, device->device_name, xev->detail); _clutter_input_pointer_a11y_on_button_event (device, xev->detail, (cookie->evtype == XI_RawButtonPress)); break; } } static gboolean translate_pad_axis (ClutterInputDevice *device, XIValuatorState *valuators, ClutterEventType *evtype, uint32_t *number, double *value) { double *values; int i; values = valuators->values; for (i = PAD_AXIS_FIRST; i < valuators->mask_len * 8; i++) { double val; uint32_t axis_number = 0; if (!XIMaskIsSet (valuators->mask, i)) continue; val = *values++; if (val <= 0) continue; _clutter_input_device_translate_axis (device, i, val, value); if (i == PAD_AXIS_RING1 || i == PAD_AXIS_RING2) { *evtype = CLUTTER_PAD_RING; (*value) *= 360.0; } else if (i == PAD_AXIS_STRIP1 || i == PAD_AXIS_STRIP2) { *evtype = CLUTTER_PAD_STRIP; } else continue; if (i == PAD_AXIS_STRIP2 || i == PAD_AXIS_RING2) axis_number++; *number = axis_number; return TRUE; } return FALSE; } static gboolean translate_pad_event (ClutterEvent *event, XIDeviceEvent *xev, ClutterInputDevice *device) { double value; uint32_t number, mode = 0; if (!translate_pad_axis (device, &xev->valuators, &event->any.type, &number, &value)) return FALSE; /* When touching a ring/strip a first XI_Motion event * is generated. Use it to reset the pad state, so * later events actually have a directionality. */ if (xev->evtype == XI_Motion) value = -1; #ifdef HAVE_LIBWACOM mode = meta_input_device_x11_get_pad_group_mode (device, number); #endif if (event->any.type == CLUTTER_PAD_RING) { event->pad_ring.ring_number = number; event->pad_ring.angle = value; event->pad_ring.mode = mode; } else { event->pad_strip.strip_number = number; event->pad_strip.value = value; event->pad_strip.mode = mode; } event->any.time = xev->time; clutter_event_set_device (event, device); clutter_event_set_source_device (event, device); g_debug ("%s: win:0x%x, device:%d '%s', time:%d " "(value:%f)", event->any.type == CLUTTER_PAD_RING ? "pad ring " : "pad strip", (unsigned int) xev->event, device->id, device->device_name, event->any.time, value); return TRUE; } static ClutterStage * get_event_stage (MetaSeatX11 *seat_x11, XIEvent *xi_event) { Window xwindow = None; switch (xi_event->evtype) { case XI_KeyPress: case XI_KeyRelease: case XI_ButtonPress: case XI_ButtonRelease: case XI_Motion: case XI_TouchBegin: case XI_TouchUpdate: case XI_TouchEnd: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; xwindow = xev->event; } break; case XI_Enter: case XI_Leave: case XI_FocusIn: case XI_FocusOut: { XIEnterEvent *xev = (XIEnterEvent *) xi_event; xwindow = xev->event; } break; case XI_HierarchyChanged: return CLUTTER_STAGE (meta_backend_get_stage (meta_get_backend ())); default: break; } if (xwindow == None) return NULL; return meta_x11_get_stage_from_window (xwindow); } /* * print_key_sym: Translate a symbol to its printable form if any * @symbol: the symbol to translate * @buffer: the buffer where to put the translated string * @len: size of the buffer * * Translates @symbol into a printable representation in @buffer, if possible. * * Return value: The number of bytes of the translated string, 0 if the * symbol can't be printed * * Note: The code is derived from libX11's src/KeyBind.c * Copyright 1985, 1987, 1998 The Open Group * * Note: This code works for Latin-1 symbols. clutter_keysym_to_unicode() * does the work for the other keysyms. */ static int print_keysym (uint32_t symbol, char *buffer, int len) { unsigned long high_bytes; unsigned char c; high_bytes = symbol >> 8; if (!(len && ((high_bytes == 0) || ((high_bytes == 0xFF) && (((symbol >= CLUTTER_KEY_BackSpace) && (symbol <= CLUTTER_KEY_Clear)) || (symbol == CLUTTER_KEY_Return) || (symbol == CLUTTER_KEY_Escape) || (symbol == CLUTTER_KEY_KP_Space) || (symbol == CLUTTER_KEY_KP_Tab) || (symbol == CLUTTER_KEY_KP_Enter) || ((symbol >= CLUTTER_KEY_KP_Multiply) && (symbol <= CLUTTER_KEY_KP_9)) || (symbol == CLUTTER_KEY_KP_Equal) || (symbol == CLUTTER_KEY_Delete)))))) return 0; /* if X keysym, convert to ascii by grabbing low 7 bits */ if (symbol == CLUTTER_KEY_KP_Space) c = CLUTTER_KEY_space & 0x7F; /* patch encoding botch */ else if (high_bytes == 0xFF) c = symbol & 0x7F; else c = symbol & 0xFF; buffer[0] = c; return 1; } static double * translate_axes (ClutterInputDevice *device, double x, double y, XIValuatorState *valuators) { uint32_t n_axes = clutter_input_device_get_n_axes (device); uint32_t i; double *retval; double *values; retval = g_new0 (double, n_axes); values = valuators->values; for (i = 0; i < valuators->mask_len * 8; i++) { ClutterInputAxis axis; double val; if (!XIMaskIsSet (valuators->mask, i)) continue; axis = clutter_input_device_get_axis (device, i); val = *values++; switch (axis) { case CLUTTER_INPUT_AXIS_X: retval[i] = x; break; case CLUTTER_INPUT_AXIS_Y: retval[i] = y; break; default: _clutter_input_device_translate_axis (device, i, val, &retval[i]); break; } } return retval; } static double scroll_valuators_changed (ClutterInputDevice *device, XIValuatorState *valuators, double *dx_p, double *dy_p) { gboolean retval = FALSE; uint32_t n_axes, n_val, i; double *values; n_axes = clutter_input_device_get_n_axes (device); values = valuators->values; *dx_p = *dy_p = 0.0; n_val = 0; for (i = 0; i < MIN (valuators->mask_len * 8, n_axes); i++) { ClutterScrollDirection direction; double delta; if (!XIMaskIsSet (valuators->mask, i)) continue; if (_clutter_input_device_get_scroll_delta (device, i, values[n_val], &direction, &delta)) { retval = TRUE; if (direction == CLUTTER_SCROLL_UP || direction == CLUTTER_SCROLL_DOWN) *dy_p = delta; else *dx_p = delta; } n_val += 1; } return retval; } static void translate_coords (MetaStageX11 *stage_x11, double event_x, double event_y, float *x_out, float *y_out) { ClutterStageCogl *stage_cogl = CLUTTER_STAGE_COGL (stage_x11); ClutterActor *stage = CLUTTER_ACTOR (stage_cogl->wrapper); float stage_width; float stage_height; clutter_actor_get_size (stage, &stage_width, &stage_height); *x_out = CLAMP (event_x, 0, stage_width); *y_out = CLAMP (event_y, 0, stage_height); } static void on_keymap_state_change (MetaKeymapX11 *keymap_x11, gpointer data) { ClutterSeat *seat = CLUTTER_SEAT (data); ClutterKbdA11ySettings kbd_a11y_settings; /* On keymaps state change, just reapply the current settings, it'll * take care of enabling/disabling mousekeys based on NumLock state. */ clutter_seat_get_kbd_a11y_settings (seat, &kbd_a11y_settings); meta_seat_x11_apply_kbd_a11y_settings (seat, &kbd_a11y_settings); } static void meta_seat_x11_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (object); switch (prop_id) { case PROP_OPCODE: seat_x11->opcode = g_value_get_int (value); break; case PROP_POINTER_ID: seat_x11->pointer_id = g_value_get_int (value); break; case PROP_KEYBOARD_ID: seat_x11->keyboard_id = g_value_get_int (value); break; case PROP_TOUCH_MODE: default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_seat_x11_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (object); switch (prop_id) { case PROP_OPCODE: g_value_set_int (value, seat_x11->opcode); break; case PROP_POINTER_ID: g_value_set_int (value, seat_x11->pointer_id); break; case PROP_KEYBOARD_ID: g_value_set_int (value, seat_x11->keyboard_id); break; case PROP_TOUCH_MODE: g_value_set_boolean (value, seat_x11->touch_mode); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } void meta_seat_x11_notify_devices (MetaSeatX11 *seat_x11, ClutterStage *stage) { GHashTableIter iter; ClutterInputDevice *device; g_hash_table_iter_init (&iter, seat_x11->devices_by_id); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &device)) { ClutterEvent *event; event = clutter_event_new (CLUTTER_DEVICE_ADDED); clutter_event_set_device (event, device); clutter_event_set_stage (event, stage); clutter_do_event (event); } } static void meta_seat_x11_constructed (GObject *object) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (object); ClutterBackend *backend = clutter_get_default_backend (); GHashTable *masters, *slaves; XIDeviceInfo *info; XIEventMask event_mask; unsigned char mask[XIMaskLen(XI_LASTEVENT)] = { 0, }; int n_devices, i; Display *xdisplay; xdisplay = clutter_x11_get_default_display (); masters = g_hash_table_new (NULL, NULL); slaves = g_hash_table_new (NULL, NULL); info = XIQueryDevice (clutter_x11_get_default_display (), XIAllDevices, &n_devices); for (i = 0; i < n_devices; i++) { XIDeviceInfo *xi_device = &info[i]; if (!xi_device->enabled) continue; add_device (seat_x11, backend, xi_device, TRUE); if (xi_device->use == XIMasterPointer || xi_device->use == XIMasterKeyboard) { g_hash_table_insert (masters, GINT_TO_POINTER (xi_device->deviceid), GINT_TO_POINTER (xi_device->attachment)); } else if (xi_device->use == XISlavePointer || xi_device->use == XISlaveKeyboard) { g_hash_table_insert (slaves, GINT_TO_POINTER (xi_device->deviceid), GINT_TO_POINTER (xi_device->attachment)); } } XIFreeDeviceInfo (info); g_hash_table_foreach (masters, relate_masters, seat_x11); g_hash_table_destroy (masters); g_hash_table_foreach (slaves, relate_slaves, seat_x11); g_hash_table_destroy (slaves); XISetMask (mask, XI_HierarchyChanged); XISetMask (mask, XI_DeviceChanged); XISetMask (mask, XI_PropertyEvent); event_mask.deviceid = XIAllDevices; event_mask.mask_len = sizeof (mask); event_mask.mask = mask; XISelectEvents (xdisplay, clutter_x11_get_root_window (), &event_mask, 1); memset(mask, 0, sizeof (mask)); XISetMask (mask, XI_RawMotion); XISetMask (mask, XI_RawButtonPress); XISetMask (mask, XI_RawButtonRelease); event_mask.deviceid = XIAllMasterDevices; event_mask.mask_len = sizeof (mask); event_mask.mask = mask; XISelectEvents (xdisplay, clutter_x11_get_root_window (), &event_mask, 1); XSync (xdisplay, False); seat_x11->keymap = g_object_new (META_TYPE_KEYMAP_X11, "backend", backend, NULL); g_signal_connect (seat_x11->keymap, "state-changed", G_CALLBACK (on_keymap_state_change), seat_x11); meta_seat_x11_a11y_init (CLUTTER_SEAT (seat_x11)); if (G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed) G_OBJECT_CLASS (meta_seat_x11_parent_class)->constructed (object); } static void meta_seat_x11_finalize (GObject *object) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (object); g_hash_table_unref (seat_x11->devices_by_id); g_hash_table_unref (seat_x11->tools_by_serial); g_list_free (seat_x11->devices); G_OBJECT_CLASS (meta_seat_x11_parent_class)->finalize (object); } static ClutterInputDevice * meta_seat_x11_get_pointer (ClutterSeat *seat) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat); return seat_x11->core_pointer; } static ClutterInputDevice * meta_seat_x11_get_keyboard (ClutterSeat *seat) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat); return seat_x11->core_keyboard; } static GList * meta_seat_x11_list_devices (ClutterSeat *seat) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat); GList *retval = NULL, *l; for (l = seat_x11->devices; l; l = l->next) retval = g_list_prepend (retval, l->data); return retval; } static void meta_seat_x11_bell_notify (ClutterSeat *seat) { MetaDisplay *display = meta_get_display (); meta_bell_notify (display, NULL); } static ClutterKeymap * meta_seat_x11_get_keymap (ClutterSeat *seat) { return CLUTTER_KEYMAP (META_SEAT_X11 (seat)->keymap); } static void meta_seat_x11_copy_event_data (ClutterSeat *seat, const ClutterEvent *src, ClutterEvent *dest) { gpointer event_x11; event_x11 = _clutter_event_get_platform_data (src); if (event_x11 != NULL) _clutter_event_set_platform_data (dest, meta_event_x11_copy (event_x11)); } static void meta_seat_x11_free_event_data (ClutterSeat *seat, ClutterEvent *event) { gpointer event_x11; event_x11 = _clutter_event_get_platform_data (event); if (event_x11 != NULL) meta_event_x11_free (event_x11); } static ClutterVirtualInputDevice * meta_seat_x11_create_virtual_device (ClutterSeat *seat, ClutterInputDeviceType device_type) { return g_object_new (META_TYPE_VIRTUAL_INPUT_DEVICE_X11, "seat", seat, "device-type", device_type, NULL); } static ClutterVirtualDeviceType meta_seat_x11_get_supported_virtual_device_types (ClutterSeat *seat) { return (CLUTTER_VIRTUAL_DEVICE_TYPE_KEYBOARD | CLUTTER_VIRTUAL_DEVICE_TYPE_POINTER); } static void meta_seat_x11_warp_pointer (ClutterSeat *seat, int x, int y) { MetaSeatX11 *seat_x11 = META_SEAT_X11 (seat); XIWarpPointer (clutter_x11_get_default_display (), seat_x11->pointer_id, None, clutter_x11_get_root_window (), 0, 0, 0, 0, x, y); } static void meta_seat_x11_class_init (MetaSeatX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterSeatClass *seat_class = CLUTTER_SEAT_CLASS (klass); object_class->set_property = meta_seat_x11_set_property; object_class->get_property = meta_seat_x11_get_property; object_class->constructed = meta_seat_x11_constructed; object_class->finalize = meta_seat_x11_finalize; seat_class->get_pointer = meta_seat_x11_get_pointer; seat_class->get_keyboard = meta_seat_x11_get_keyboard; seat_class->list_devices = meta_seat_x11_list_devices; seat_class->bell_notify = meta_seat_x11_bell_notify; seat_class->get_keymap = meta_seat_x11_get_keymap; seat_class->copy_event_data = meta_seat_x11_copy_event_data; seat_class->free_event_data = meta_seat_x11_free_event_data; seat_class->apply_kbd_a11y_settings = meta_seat_x11_apply_kbd_a11y_settings; seat_class->create_virtual_device = meta_seat_x11_create_virtual_device; seat_class->get_supported_virtual_device_types = meta_seat_x11_get_supported_virtual_device_types; seat_class->warp_pointer = meta_seat_x11_warp_pointer; seat_class->handle_device_event = meta_seat_x11_handle_device_event; props[PROP_OPCODE] = g_param_spec_int ("opcode", "Opcode", "Opcode", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); props[PROP_POINTER_ID] = g_param_spec_int ("pointer-id", "Pointer ID", "Pointer ID", 2, G_MAXINT, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); props[PROP_KEYBOARD_ID] = g_param_spec_int ("keyboard-id", "Keyboard ID", "Keyboard ID", 2, G_MAXINT, 2, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, props); g_object_class_override_property (object_class, PROP_TOUCH_MODE, "touch-mode"); } static void meta_seat_x11_init (MetaSeatX11 *seat) { seat->devices_by_id = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); seat->tools_by_serial = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_object_unref); } MetaSeatX11 * meta_seat_x11_new (int opcode, int master_pointer, int master_keyboard) { return g_object_new (META_TYPE_SEAT_X11, "opcode", opcode, "pointer-id", master_pointer, "keyboard-id", master_keyboard, NULL); } static ClutterInputDevice * get_source_device_checked (MetaSeatX11 *seat, XIDeviceEvent *xev) { ClutterInputDevice *source_device; source_device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->sourceid)); if (!source_device) g_warning ("Impossible to get the source device with id %d for event of " "type %d", xev->sourceid, xev->evtype); return source_device; } gboolean meta_seat_x11_translate_event (MetaSeatX11 *seat, XEvent *xevent, ClutterEvent *event) { gboolean retval = FALSE; ClutterBackend *backend = clutter_get_default_backend (); ClutterStage *stage = NULL; MetaStageX11 *stage_x11 = NULL; ClutterInputDevice *device, *source_device; XGenericEventCookie *cookie; XIEvent *xi_event; if (meta_keymap_x11_handle_event (seat->keymap, xevent)) return FALSE; cookie = &xevent->xcookie; if (cookie->type != GenericEvent || cookie->extension != seat->opcode) return FALSE; xi_event = (XIEvent *) cookie->data; if (!xi_event) return FALSE; if (cookie->evtype == XI_RawMotion || cookie->evtype == XI_RawButtonPress || cookie->evtype == XI_RawButtonRelease) { translate_raw_event (seat, xevent); return FALSE; } if (!(xi_event->evtype == XI_DeviceChanged || xi_event->evtype == XI_PropertyEvent)) { stage = get_event_stage (seat, xi_event); if (stage == NULL || CLUTTER_ACTOR_IN_DESTRUCTION (stage)) return FALSE; else stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage)); } event->any.stage = stage; switch (xi_event->evtype) { case XI_HierarchyChanged: { XIHierarchyEvent *xev = (XIHierarchyEvent *) xi_event; retval = translate_hierarchy_event (backend, seat, xev, event); } break; case XI_DeviceChanged: { XIDeviceChangedEvent *xev = (XIDeviceChangedEvent *) xi_event; device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); source_device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->sourceid)); if (device) { _clutter_input_device_reset_axes (device); translate_device_classes (clutter_x11_get_default_display (), device, xev->classes, xev->num_classes); } if (source_device) _clutter_input_device_reset_scroll_info (source_device); } retval = FALSE; break; case XI_KeyPress: case XI_KeyRelease: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; MetaKeymapX11 *keymap_x11 = seat->keymap; MetaEventX11 *event_x11; char buffer[7] = { 0, }; gunichar n; source_device = get_source_device_checked (seat, xev); if (!source_device) return FALSE; event->key.type = event->type = (xev->evtype == XI_KeyPress) ? CLUTTER_KEY_PRESS : CLUTTER_KEY_RELEASE; if (xev->evtype == XI_KeyPress && xev->flags & XIKeyRepeat) clutter_event_set_flags (event, CLUTTER_EVENT_FLAG_REPEATED); event->key.time = xev->time; event->key.stage = stage; meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); event->key.hardware_keycode = xev->detail; /* keyval is the key ignoring all modifiers ('1' vs. '!') */ event->key.keyval = meta_keymap_x11_translate_key_state (keymap_x11, event->key.hardware_keycode, &event->key.modifier_state, NULL); /* KeyEvents have platform specific data associated to them */ event_x11 = meta_event_x11_new (); _clutter_event_set_platform_data (event, event_x11); event_x11->key_group = meta_keymap_x11_get_key_group (keymap_x11, event->key.modifier_state); event_x11->key_is_modifier = meta_keymap_x11_get_is_modifier (keymap_x11, event->key.hardware_keycode); event_x11->num_lock_set = clutter_keymap_get_num_lock_state (CLUTTER_KEYMAP (keymap_x11)); event_x11->caps_lock_set = clutter_keymap_get_caps_lock_state (CLUTTER_KEYMAP (keymap_x11)); clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); clutter_event_set_device (event, device); if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER && stage != NULL) _clutter_input_device_set_stage (device, stage); /* XXX keep this in sync with the evdev device manager */ n = print_keysym (event->key.keyval, buffer, sizeof (buffer)); if (n == 0) { /* not printable */ event->key.unicode_value = (gunichar) '\0'; } else { event->key.unicode_value = g_utf8_get_char_validated (buffer, n); if (event->key.unicode_value == -1 || event->key.unicode_value == -2) event->key.unicode_value = (gunichar) '\0'; } g_debug ("%s: win:0x%x device:%d source:%d, key: %12s (%d)", event->any.type == CLUTTER_KEY_PRESS ? "key press " : "key release", (unsigned int) stage_x11->xwin, xev->deviceid, xev->sourceid, event->key.keyval ? buffer : "(none)", event->key.keyval); if (xi_event->evtype == XI_KeyPress) meta_stage_x11_set_user_time (stage_x11, event->key.time); retval = TRUE; } break; case XI_ButtonPress: case XI_ButtonRelease: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; source_device = get_source_device_checked (seat, xev); if (!source_device) return FALSE; device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); /* Set the stage for core events coming out of nowhere (see bug #684509) */ if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER && clutter_input_device_get_pointer_stage (device) == NULL && stage != NULL) _clutter_input_device_set_stage (device, stage); if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE) { /* We got these events because of the passive button grab */ XIAllowEvents (clutter_x11_get_default_display (), xev->sourceid, XIAsyncDevice, xev->time); event->any.stage = stage; if (xev->detail >= 4 && xev->detail <= 7) { retval = FALSE; if (xi_event->evtype == XI_ButtonPress && translate_pad_event (event, xev, source_device)) retval = TRUE; break; } event->any.type = (xi_event->evtype == XI_ButtonPress) ? CLUTTER_PAD_BUTTON_PRESS : CLUTTER_PAD_BUTTON_RELEASE; event->any.time = xev->time; /* The 4-7 button range is taken as non-existent on pad devices, * let the buttons above that take over this range. */ if (xev->detail > 7) xev->detail -= 4; /* Pad buttons are 0-indexed */ event->pad_button.button = xev->detail - 1; #ifdef HAVE_LIBWACOM meta_input_device_x11_update_pad_state (device, event->pad_button.button, (xi_event->evtype == XI_ButtonPress), &event->pad_button.group, &event->pad_button.mode); #endif clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); g_debug ("%s: win:0x%x, device:%d '%s', time:%d " "(button:%d)", event->any.type == CLUTTER_BUTTON_PRESS ? "pad button press " : "pad button release", (unsigned int) stage_x11->xwin, device->id, device->device_name, event->any.time, event->pad_button.button); retval = TRUE; break; } switch (xev->detail) { case 4: case 5: case 6: case 7: /* we only generate Scroll events on ButtonPress */ if (xi_event->evtype == XI_ButtonRelease) return FALSE; event->scroll.type = event->type = CLUTTER_SCROLL; if (xev->detail == 4) event->scroll.direction = CLUTTER_SCROLL_UP; else if (xev->detail == 5) event->scroll.direction = CLUTTER_SCROLL_DOWN; else if (xev->detail == 6) event->scroll.direction = CLUTTER_SCROLL_LEFT; else event->scroll.direction = CLUTTER_SCROLL_RIGHT; event->scroll.stage = stage; event->scroll.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y); meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); clutter_event_set_source_device (event, source_device); clutter_event_set_device (event, device); event->scroll.axes = translate_axes (event->scroll.device, event->scroll.x, event->scroll.y, &xev->valuators); g_debug ("scroll: win:0x%x, device:%d '%s', time:%d " "(direction:%s, " "x:%.2f, y:%.2f, " "emulated:%s)", (unsigned int) stage_x11->xwin, device->id, device->device_name, event->any.time, event->scroll.direction == CLUTTER_SCROLL_UP ? "up" : event->scroll.direction == CLUTTER_SCROLL_DOWN ? "down" : event->scroll.direction == CLUTTER_SCROLL_LEFT ? "left" : event->scroll.direction == CLUTTER_SCROLL_RIGHT ? "right" : "invalid", event->scroll.x, event->scroll.y, (xev->flags & XIPointerEmulated) ? "yes" : "no"); break; default: event->button.type = event->type = (xi_event->evtype == XI_ButtonPress) ? CLUTTER_BUTTON_PRESS : CLUTTER_BUTTON_RELEASE; event->button.stage = stage; event->button.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->button.x, &event->button.y); event->button.button = xev->detail; meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); clutter_event_set_source_device (event, source_device); clutter_event_set_device (event, device); clutter_event_set_device_tool (event, meta_input_device_x11_get_current_tool (source_device)); event->button.axes = translate_axes (event->button.device, event->button.x, event->button.y, &xev->valuators); g_debug ("%s: win:0x%x, device:%d '%s', time:%d " "(button:%d, " "x:%.2f, y:%.2f, " "axes:%s, " "emulated:%s)", event->any.type == CLUTTER_BUTTON_PRESS ? "button press " : "button release", (unsigned int) stage_x11->xwin, device->id, device->device_name, event->any.time, event->button.button, event->button.x, event->button.y, event->button.axes != NULL ? "yes" : "no", (xev->flags & XIPointerEmulated) ? "yes" : "no"); break; } if (device->stage != NULL) _clutter_input_device_set_stage (source_device, device->stage); if (xev->flags & XIPointerEmulated) _clutter_event_set_pointer_emulated (event, TRUE); if (xi_event->evtype == XI_ButtonPress) meta_stage_x11_set_user_time (stage_x11, event->button.time); retval = TRUE; } break; case XI_Motion: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; double delta_x, delta_y; source_device = get_source_device_checked (seat, xev); if (!source_device) return FALSE; device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); if (clutter_input_device_get_device_type (source_device) == CLUTTER_PAD_DEVICE) { event->any.stage = stage; if (translate_pad_event (event, xev, source_device)) retval = TRUE; break; } /* Set the stage for core events coming out of nowhere (see bug #684509) */ if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER && clutter_input_device_get_pointer_stage (device) == NULL && stage != NULL) _clutter_input_device_set_stage (device, stage); if (scroll_valuators_changed (source_device, &xev->valuators, &delta_x, &delta_y)) { event->scroll.type = event->type = CLUTTER_SCROLL; event->scroll.direction = CLUTTER_SCROLL_SMOOTH; event->scroll.stage = stage; event->scroll.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->scroll.x, &event->scroll.y); meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); clutter_event_set_scroll_delta (event, delta_x, delta_y); clutter_event_set_source_device (event, source_device); clutter_event_set_device (event, device); g_debug ("smooth scroll: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, delta:%f, %f)", (unsigned int) stage_x11->xwin, event->scroll.device->id, event->scroll.device->device_name, event->scroll.x, event->scroll.y, delta_x, delta_y); retval = TRUE; break; } event->motion.type = event->type = CLUTTER_MOTION; event->motion.stage = stage; event->motion.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->motion.x, &event->motion.y); meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); clutter_event_set_source_device (event, source_device); clutter_event_set_device (event, device); clutter_event_set_device_tool (event, meta_input_device_x11_get_current_tool (source_device)); event->motion.axes = translate_axes (event->motion.device, event->motion.x, event->motion.y, &xev->valuators); if (device->stage != NULL) _clutter_input_device_set_stage (source_device, device->stage); if (xev->flags & XIPointerEmulated) _clutter_event_set_pointer_emulated (event, TRUE); g_debug ("motion: win:0x%x device:%d '%s' (x:%.2f, y:%.2f, axes:%s)", (unsigned int) stage_x11->xwin, event->motion.device->id, event->motion.device->device_name, event->motion.x, event->motion.y, event->motion.axes != NULL ? "yes" : "no"); retval = TRUE; } break; case XI_TouchBegin: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); if (!_clutter_input_device_get_stage (device)) _clutter_input_device_set_stage (device, stage); } /* Fall through */ case XI_TouchEnd: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; source_device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->sourceid)); if (xi_event->evtype == XI_TouchBegin) event->touch.type = event->type = CLUTTER_TOUCH_BEGIN; else event->touch.type = event->type = CLUTTER_TOUCH_END; event->touch.stage = stage; event->touch.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y); meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); clutter_event_set_device (event, device); event->touch.axes = translate_axes (event->touch.device, event->motion.x, event->motion.y, &xev->valuators); if (xi_event->evtype == XI_TouchBegin) { event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; meta_stage_x11_set_user_time (stage_x11, event->touch.time); } event->touch.sequence = GUINT_TO_POINTER (xev->detail); if (xev->flags & XITouchEmulatingPointer) _clutter_event_set_pointer_emulated (event, TRUE); g_debug ("touch %s: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)", event->type == CLUTTER_TOUCH_BEGIN ? "begin" : "end", (unsigned int) stage_x11->xwin, event->touch.device->id, event->touch.device->device_name, GPOINTER_TO_UINT (event->touch.sequence), event->touch.x, event->touch.y, event->touch.axes != NULL ? "yes" : "no"); retval = TRUE; } break; case XI_TouchUpdate: { XIDeviceEvent *xev = (XIDeviceEvent *) xi_event; source_device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->sourceid)); event->touch.type = event->type = CLUTTER_TOUCH_UPDATE; event->touch.stage = stage; event->touch.time = xev->time; event->touch.sequence = GUINT_TO_POINTER (xev->detail); translate_coords (stage_x11, xev->event_x, xev->event_y, &event->touch.x, &event->touch.y); clutter_event_set_source_device (event, source_device); device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); clutter_event_set_device (event, device); event->touch.axes = translate_axes (event->touch.device, event->motion.x, event->motion.y, &xev->valuators); meta_input_device_x11_translate_state (event, &xev->mods, &xev->buttons, &xev->group); event->touch.modifier_state |= CLUTTER_BUTTON1_MASK; if (xev->flags & XITouchEmulatingPointer) _clutter_event_set_pointer_emulated (event, TRUE); g_debug ("touch update: win:0x%x device:%d '%s' (seq:%d, x:%.2f, y:%.2f, axes:%s)", (unsigned int) stage_x11->xwin, event->touch.device->id, event->touch.device->device_name, GPOINTER_TO_UINT (event->touch.sequence), event->touch.x, event->touch.y, event->touch.axes != NULL ? "yes" : "no"); retval = TRUE; } break; case XI_Enter: case XI_Leave: { XIEnterEvent *xev = (XIEnterEvent *) xi_event; device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->deviceid)); source_device = g_hash_table_lookup (seat->devices_by_id, GINT_TO_POINTER (xev->sourceid)); if (xi_event->evtype == XI_Enter) { event->crossing.type = event->type = CLUTTER_ENTER; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; event->crossing.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y); } else { if (device->stage == NULL) { g_debug ("Discarding Leave for ButtonRelease " "event off-stage"); retval = FALSE; break; } event->crossing.type = event->type = CLUTTER_LEAVE; event->crossing.stage = stage; event->crossing.source = CLUTTER_ACTOR (stage); event->crossing.related = NULL; event->crossing.time = xev->time; translate_coords (stage_x11, xev->event_x, xev->event_y, &event->crossing.x, &event->crossing.y); } _clutter_input_device_reset_scroll_info (source_device); clutter_event_set_device (event, device); clutter_event_set_source_device (event, source_device); retval = TRUE; } break; case XI_FocusIn: case XI_FocusOut: retval = FALSE; break; case XI_PropertyEvent: translate_property_event (seat, xi_event); retval = FALSE; break; } return retval; } ClutterInputDevice * meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11, int device_id) { return g_hash_table_lookup (seat_x11->devices_by_id, GINT_TO_POINTER (device_id)); } void meta_seat_x11_select_stage_events (MetaSeatX11 *seat, ClutterStage *stage) { MetaStageX11 *stage_x11; XIEventMask xi_event_mask; unsigned char *mask; int len; stage_x11 = META_STAGE_X11 (_clutter_stage_get_window (stage)); len = XIMaskLen (XI_LASTEVENT); mask = g_new0 (unsigned char, len); XISetMask (mask, XI_Motion); XISetMask (mask, XI_ButtonPress); XISetMask (mask, XI_ButtonRelease); XISetMask (mask, XI_KeyPress); XISetMask (mask, XI_KeyRelease); XISetMask (mask, XI_Enter); XISetMask (mask, XI_Leave); XISetMask (mask, XI_TouchBegin); XISetMask (mask, XI_TouchUpdate); XISetMask (mask, XI_TouchEnd); xi_event_mask.deviceid = XIAllMasterDevices; xi_event_mask.mask = mask; xi_event_mask.mask_len = len; XISelectEvents (clutter_x11_get_default_display (), stage_x11->xwin, &xi_event_mask, 1); g_free (mask); } muffin-6.4.1/src/backends/x11/meta-input-device-x11.h0000664000175000017500000000646714723361714021031 0ustar fabiofabio/* * Copyright © 2011 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef META_INPUT_DEVICE_X11_H #define META_INPUT_DEVICE_X11_H #include #ifdef HAVE_LIBWACOM #include #endif #include "backends/meta-input-device-private.h" #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_INPUT_DEVICE_X11 (meta_input_device_x11_get_type ()) #define META_INPUT_DEVICE_X11(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), META_TYPE_INPUT_DEVICE_X11, MetaInputDeviceX11)) #define META_IS_INPUT_DEVICE_X11(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), META_TYPE_INPUT_DEVICE_X11)) #define META_INPUT_DEVICE_X11_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), META_TYPE_INPUT_DEVICE_X11, MetaInputDeviceX11Class)) #define META_IS_INPUT_DEVICE_X11_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), META_TYPE_INPUT_DEVICE_X11)) #define META_INPUT_DEVICE_X11_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), META_TYPE_INPUT_DEVICE_X11, MetaInputDeviceX11Class)) typedef struct _MetaInputDeviceX11 MetaInputDeviceX11; typedef struct _MetaInputDeviceX11Class MetaInputDeviceX11Class; GType meta_input_device_x11_get_type (void) G_GNUC_CONST; void meta_input_device_x11_translate_state (ClutterEvent *event, XIModifierState *modifiers_state, XIButtonState *buttons_state, XIGroupState *group_state); void meta_input_device_x11_update_tool (ClutterInputDevice *device, ClutterInputDeviceTool *tool); ClutterInputDeviceTool * meta_input_device_x11_get_current_tool (ClutterInputDevice *device); #ifdef HAVE_LIBWACOM void meta_input_device_x11_ensure_wacom_info (ClutterInputDevice *device, WacomDeviceDatabase *wacom_db); uint32_t meta_input_device_x11_get_pad_group_mode (ClutterInputDevice *device, uint32_t group); void meta_input_device_x11_update_pad_state (ClutterInputDevice *device, uint32_t button, uint32_t state, uint32_t *group, uint32_t *mode); #endif gboolean meta_input_device_x11_get_pointer_location (ClutterInputDevice *device, float *x, float *y); G_END_DECLS #endif /* META_INPUT_DEVICE_X11_H */ muffin-6.4.1/src/backends/x11/cm/0000775000175000017500000000000014723361714015313 5ustar fabiofabiomuffin-6.4.1/src/backends/x11/cm/meta-backend-x11-cm.h0000664000175000017500000000213114723361714021000 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_BACKEND_X11_CM_H #define META_BACKEND_X11_CM_H #include #include "backends/x11/meta-backend-x11.h" #define META_TYPE_BACKEND_X11_CM (meta_backend_x11_cm_get_type ()) G_DECLARE_FINAL_TYPE (MetaBackendX11Cm, meta_backend_x11_cm, META, BACKEND_X11_CM, MetaBackendX11) #endif /* META_BACKEND_X11_CM_H */ muffin-6.4.1/src/backends/x11/cm/meta-renderer-x11-cm.h0000664000175000017500000000333714723361714021230 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_RENDERER_X11_CM_H #define META_RENDERER_X11_CM_H #include "backends/x11/meta-renderer-x11.h" #define META_TYPE_RENDERER_X11_CM (meta_renderer_x11_cm_get_type ()) G_DECLARE_FINAL_TYPE (MetaRendererX11Cm, meta_renderer_x11_cm, META, RENDERER_X11_CM, MetaRendererX11) void meta_renderer_x11_cm_ensure_screen_view (MetaRendererX11Cm *renderer_x11_cm, int width, int height); void meta_renderer_x11_cm_resize (MetaRendererX11Cm *renderer_x11_cm, int width, int height); void meta_renderer_x11_cm_set_onscreen (MetaRendererX11Cm *renderer_x11_cm, CoglOnscreen *onscreen); #endif /* META_RENDERER_X11_CM_H */ muffin-6.4.1/src/backends/x11/cm/meta-cursor-sprite-xfixes.h0000664000175000017500000000244714723361714022544 0ustar fabiofabio/* * Copyright 2013, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_CURSOR_SPRITE_XFIXES_H #define META_CURSOR_SPRITE_XFIXES_H #include #include "backends/meta-cursor.h" #include "meta/types.h" #define META_TYPE_CURSOR_SPRITE_XFIXES (meta_cursor_sprite_xfixes_get_type ()) G_DECLARE_FINAL_TYPE (MetaCursorSpriteXfixes, meta_cursor_sprite_xfixes, META, CURSOR_SPRITE_XFIXES, MetaCursorSprite) MetaCursorSpriteXfixes * meta_cursor_sprite_xfixes_new (MetaDisplay *display, GError **error); #endif /* META_CURSOR_SPRITE_XFIXES_H */ muffin-6.4.1/src/backends/x11/cm/meta-renderer-x11-cm.c0000664000175000017500000000605414723361714021222 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/x11/cm/meta-renderer-x11-cm.h" #include "backends/meta-renderer-view.h" struct _MetaRendererX11Cm { MetaRendererX11 parent; MetaRendererView *screen_view; }; G_DEFINE_TYPE (MetaRendererX11Cm, meta_renderer_x11_cm, META_TYPE_RENDERER_X11) void meta_renderer_x11_cm_ensure_screen_view (MetaRendererX11Cm *renderer_x11_cm, int width, int height) { cairo_rectangle_int_t view_layout; if (renderer_x11_cm->screen_view) return; view_layout = (cairo_rectangle_int_t) { .width = width, .height = height, }; renderer_x11_cm->screen_view = g_object_new (META_TYPE_RENDERER_VIEW, "layout", &view_layout, NULL); meta_renderer_add_view (META_RENDERER (renderer_x11_cm), renderer_x11_cm->screen_view); } void meta_renderer_x11_cm_resize (MetaRendererX11Cm *renderer_x11_cm, int width, int height) { cairo_rectangle_int_t view_layout; view_layout = (cairo_rectangle_int_t) { .width = width, .height = height, }; g_object_set (G_OBJECT (renderer_x11_cm->screen_view), "layout", &view_layout, NULL); } void meta_renderer_x11_cm_set_onscreen (MetaRendererX11Cm *renderer_x11_cm, CoglOnscreen *onscreen) { g_object_set (G_OBJECT (renderer_x11_cm->screen_view), "framebuffer", onscreen, NULL); } static void meta_renderer_x11_cm_rebuild_views (MetaRenderer *renderer) { MetaRendererX11Cm *renderer_x11_cm = META_RENDERER_X11_CM (renderer); g_return_if_fail (!meta_renderer_get_views (renderer)); meta_renderer_add_view (renderer, renderer_x11_cm->screen_view); } static void meta_renderer_x11_cm_init (MetaRendererX11Cm *renderer_x11_cm) { } static void meta_renderer_x11_cm_class_init (MetaRendererX11CmClass *klass) { MetaRendererClass *renderer_class = META_RENDERER_CLASS (klass); renderer_class->rebuild_views = meta_renderer_x11_cm_rebuild_views; } muffin-6.4.1/src/backends/x11/cm/meta-cursor-sprite-xfixes.c0000664000175000017500000001532314723361714022534 0ustar fabiofabio/* * Copyright 2013, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "backends/x11/cm/meta-cursor-sprite-xfixes.h" #include #include "core/display-private.h" #include "meta/meta-x11-display.h" enum { PROP_0, PROP_DISPLAY, N_PROPS }; static GParamSpec *obj_props[N_PROPS]; struct _MetaCursorSpriteXfixes { MetaCursorSprite parent; MetaDisplay *display; }; static void meta_screen_cast_xfixes_init_initable_iface (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaCursorSpriteXfixes, meta_cursor_sprite_xfixes, META_TYPE_CURSOR_SPRITE, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, meta_screen_cast_xfixes_init_initable_iface)) static void meta_cursor_sprite_xfixes_realize_texture (MetaCursorSprite *sprite) { } static gboolean meta_cursor_sprite_xfixes_is_animated (MetaCursorSprite *sprite) { return FALSE; } static void meta_cursor_sprite_xfixes_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaCursorSpriteXfixes *sprite_xfixes = META_CURSOR_SPRITE_XFIXES (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, sprite_xfixes->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_cursor_sprite_xfixes_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaCursorSpriteXfixes *sprite_xfixes = META_CURSOR_SPRITE_XFIXES (object); switch (prop_id) { case PROP_DISPLAY: sprite_xfixes->display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } MetaCursorSpriteXfixes * meta_cursor_sprite_xfixes_new (MetaDisplay *display, GError **error) { return g_initable_new (META_TYPE_CURSOR_SPRITE_XFIXES, NULL, error, "display", display, NULL); } static gboolean meta_cursor_sprite_xfixes_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaCursorSpriteXfixes *sprite_xfixes = META_CURSOR_SPRITE_XFIXES (initable); MetaCursorSprite *sprite = META_CURSOR_SPRITE (sprite_xfixes); MetaX11Display *x11_display; Display *xdisplay; XFixesCursorImage *cursor_image; CoglTexture2D *texture; uint8_t *cursor_data; gboolean free_cursor_data; ClutterBackend *clutter_backend; CoglContext *cogl_context; x11_display = meta_display_get_x11_display (sprite_xfixes->display); xdisplay = meta_x11_display_get_xdisplay (x11_display); cursor_image = XFixesGetCursorImage (xdisplay); if (!cursor_image) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to get cursor image"); return FALSE; } /* * Like all X APIs, XFixesGetCursorImage() returns arrays of 32-bit * quantities as arrays of long; we need to convert on 64 bit */ if (sizeof (long) == 4) { cursor_data = (uint8_t *) cursor_image->pixels; free_cursor_data = FALSE; } else { int i, j; uint32_t *cursor_words; unsigned long *p; uint32_t *q; cursor_words = g_new (uint32_t, cursor_image->width * cursor_image->height); cursor_data = (uint8_t *) cursor_words; p = cursor_image->pixels; q = cursor_words; for (j = 0; j < cursor_image->height; j++) { for (i = 0; i < cursor_image->width; i++) *(q++) = *(p++); } free_cursor_data = TRUE; } clutter_backend = clutter_get_default_backend (); cogl_context = clutter_backend_get_cogl_context (clutter_backend); texture = cogl_texture_2d_new_from_data (cogl_context, cursor_image->width, cursor_image->height, CLUTTER_CAIRO_FORMAT_ARGB32, cursor_image->width * 4, /* stride */ cursor_data, error); if (free_cursor_data) g_free (cursor_data); if (!sprite) return FALSE; meta_cursor_sprite_set_texture (sprite, COGL_TEXTURE (texture), cursor_image->xhot, cursor_image->yhot); cogl_object_unref (texture); XFree (cursor_image); return TRUE; } static void meta_screen_cast_xfixes_init_initable_iface (GInitableIface *iface) { iface->init = meta_cursor_sprite_xfixes_initable_init; } static void meta_cursor_sprite_xfixes_init (MetaCursorSpriteXfixes *sprite_xfixes) { } static void meta_cursor_sprite_xfixes_class_init (MetaCursorSpriteXfixesClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass); object_class->get_property = meta_cursor_sprite_xfixes_get_property; object_class->set_property = meta_cursor_sprite_xfixes_set_property; cursor_sprite_class->realize_texture = meta_cursor_sprite_xfixes_realize_texture; cursor_sprite_class->is_animated = meta_cursor_sprite_xfixes_is_animated; obj_props[PROP_DISPLAY] = g_param_spec_object ("display", "display", "MetaDisplay", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); } muffin-6.4.1/src/backends/x11/cm/meta-backend-x11-cm.c0000664000175000017500000003401014723361714020774 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "backends/x11/cm/meta-backend-x11-cm.h" #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-dnd-private.h" #include "backends/x11/meta-cursor-renderer-x11.h" #include "backends/x11/meta-gpu-xrandr.h" #include "backends/x11/meta-input-settings-x11.h" #include "backends/x11/meta-monitor-manager-xrandr.h" #include "backends/x11/cm/meta-renderer-x11-cm.h" #include "compositor/meta-compositor-x11.h" #include "core/display-private.h" struct _MetaBackendX11Cm { MetaBackendX11 parent; char *keymap_layouts; char *keymap_variants; char *keymap_options; int locked_group; }; G_DEFINE_TYPE (MetaBackendX11Cm, meta_backend_x11_cm, META_TYPE_BACKEND_X11) static void apply_keymap (MetaBackendX11 *x11); static void take_touch_grab (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { META_VIRTUAL_CORE_POINTER_ID, sizeof (mask_bits), mask_bits }; XIGrabModifiers mods = { XIAnyModifier, 0 }; XISetMask (mask.mask, XI_TouchBegin); XISetMask (mask.mask, XI_TouchUpdate); XISetMask (mask.mask, XI_TouchEnd); XIGrabTouchBegin (xdisplay, META_VIRTUAL_CORE_POINTER_ID, DefaultRootWindow (xdisplay), False, &mask, 1, &mods); } static void on_device_added (ClutterSeat *seat, ClutterInputDevice *device, gpointer user_data) { MetaBackendX11 *x11 = META_BACKEND_X11 (user_data); if (clutter_input_device_get_device_type (device) == CLUTTER_KEYBOARD_DEVICE) apply_keymap (x11); } static void meta_backend_x11_cm_post_init (MetaBackend *backend) { MetaBackendClass *parent_backend_class = META_BACKEND_CLASS (meta_backend_x11_cm_parent_class); ClutterSeat *seat; parent_backend_class->post_init (backend); seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_connect_object (seat, "device-added", G_CALLBACK (on_device_added), backend, 0); take_touch_grab (backend); } static MetaRenderer * meta_backend_x11_cm_create_renderer (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_RENDERER_X11_CM, "backend", backend, NULL); } static MetaMonitorManager * meta_backend_x11_cm_create_monitor_manager (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_MONITOR_MANAGER_XRANDR, "backend", backend, NULL); } static MetaCursorRenderer * meta_backend_x11_cm_create_cursor_renderer (MetaBackend *backend) { return g_object_new (META_TYPE_CURSOR_RENDERER_X11, NULL); } static MetaInputSettings * meta_backend_x11_cm_create_input_settings (MetaBackend *backend) { return g_object_new (META_TYPE_INPUT_SETTINGS_X11, NULL); } static void meta_backend_x11_cm_update_screen_size (MetaBackend *backend, int width, int height) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); Window xwin = meta_backend_x11_get_xwindow (x11); XResizeWindow (xdisplay, xwin, width, height); } static void meta_backend_x11_cm_select_stage_events (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); Window xwin = meta_backend_x11_get_xwindow (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); XISetMask (mask.mask, XI_Motion); XISelectEvents (xdisplay, xwin, &mask, 1); } static void get_xkbrf_var_defs (Display *xdisplay, const char *layouts, const char *variants, const char *options, char **rules_p, XkbRF_VarDefsRec *var_defs) { char *rules = NULL; /* Get it from the X property or fallback on defaults */ if (!XkbRF_GetNamesProp (xdisplay, &rules, var_defs) || !rules) { rules = strdup (DEFAULT_XKB_RULES_FILE); var_defs->model = strdup (DEFAULT_XKB_MODEL); var_defs->layout = NULL; var_defs->variant = NULL; var_defs->options = NULL; } /* Swap in our new options... */ free (var_defs->layout); var_defs->layout = strdup (layouts); free (var_defs->variant); var_defs->variant = strdup (variants); free (var_defs->options); var_defs->options = strdup (options); /* Sometimes, the property is a file path, and sometimes it's not. Normalize it so it's always a file path. */ if (rules[0] == '/') *rules_p = g_strdup (rules); else *rules_p = g_build_filename (XKB_BASE, "rules", rules, NULL); free (rules); } static void free_xkbrf_var_defs (XkbRF_VarDefsRec *var_defs) { free (var_defs->model); free (var_defs->layout); free (var_defs->variant); free (var_defs->options); } static void free_xkb_component_names (XkbComponentNamesRec *p) { free (p->keymap); free (p->keycodes); free (p->types); free (p->compat); free (p->symbols); free (p->geometry); } static void upload_xkb_description (Display *xdisplay, const gchar *rules_file_path, XkbRF_VarDefsRec *var_defs, XkbComponentNamesRec *comp_names) { XkbDescRec *xkb_desc; gchar *rules_file; /* Upload it to the X server using the same method as setxkbmap */ xkb_desc = XkbGetKeyboardByName (xdisplay, XkbUseCoreKbd, comp_names, XkbGBN_AllComponentsMask, XkbGBN_AllComponentsMask & (~XkbGBN_GeometryMask), True); if (!xkb_desc) { g_warning ("Couldn't upload new XKB keyboard description"); return; } XkbFreeKeyboard (xkb_desc, 0, True); rules_file = g_path_get_basename (rules_file_path); if (!XkbRF_SetNamesProp (xdisplay, rules_file, var_defs)) g_warning ("Couldn't update the XKB root window property"); g_free (rules_file); } static void apply_keymap (MetaBackendX11 *x11) { MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); XkbRF_RulesRec *xkb_rules; XkbRF_VarDefsRec xkb_var_defs = { 0 }; char *rules_file_path; if (!x11_cm->keymap_layouts || !x11_cm->keymap_variants || !x11_cm->keymap_options) return; get_xkbrf_var_defs (xdisplay, x11_cm->keymap_layouts, x11_cm->keymap_variants, x11_cm->keymap_options, &rules_file_path, &xkb_var_defs); xkb_rules = XkbRF_Load (rules_file_path, NULL, True, True); if (xkb_rules) { XkbComponentNamesRec xkb_comp_names = { 0 }; XkbRF_GetComponents (xkb_rules, &xkb_var_defs, &xkb_comp_names); upload_xkb_description (xdisplay, rules_file_path, &xkb_var_defs, &xkb_comp_names); free_xkb_component_names (&xkb_comp_names); XkbRF_Free (xkb_rules, True); } else { g_warning ("Couldn't load XKB rules"); } free_xkbrf_var_defs (&xkb_var_defs); g_free (rules_file_path); } static void meta_backend_x11_cm_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); g_free (x11_cm->keymap_layouts); x11_cm->keymap_layouts = g_strdup (layouts); g_free (x11_cm->keymap_variants); x11_cm->keymap_variants = g_strdup (variants); g_free (x11_cm->keymap_options); x11_cm->keymap_options = g_strdup (options); apply_keymap (x11); } static void meta_backend_x11_cm_lock_layout_group (MetaBackend *backend, guint idx) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); x11_cm->locked_group = idx; XkbLockGroup (xdisplay, XkbUseCoreKbd, idx); } static gboolean meta_backend_x11_cm_handle_host_xevent (MetaBackendX11 *backend_x11, XEvent *event) { MetaBackend *backend = META_BACKEND (backend_x11); MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Cm *x11_cm = META_BACKEND_X11_CM (x11); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerXrandr *monitor_manager_xrandr = META_MONITOR_MANAGER_XRANDR (monitor_manager); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); gboolean bypass_clutter = FALSE; MetaDisplay *display; display = meta_get_display (); if (display) { MetaCompositor *compositor = display->compositor; MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); Display *xdisplay = meta_backend_x11_get_xdisplay (x11); if (meta_dnd_handle_xdnd_event (backend, compositor_x11, xdisplay, event)) bypass_clutter = TRUE; } if (event->type == meta_backend_x11_get_xkb_event_base (x11)) { XkbEvent *xkb_ev = (XkbEvent *) event; if (xkb_ev->any.device == META_VIRTUAL_CORE_KEYBOARD_ID) { switch (xkb_ev->any.xkb_type) { case XkbStateNotify: if (xkb_ev->state.changed & XkbGroupLockMask) { // TODO: Restore this, ditch libgnomekbd // if (x11_cm->locked_group != xkb_ev->state.locked_group) // XkbLockGroup (xdisplay, XkbUseCoreKbd, // x11_cm->locked_group); } break; default: break; } } } bypass_clutter |= meta_monitor_manager_xrandr_handle_xevent (monitor_manager_xrandr, event); return bypass_clutter; } static void meta_backend_x11_cm_translate_device_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { Window stage_window = meta_backend_x11_get_xwindow (x11); if (device_event->event != stage_window) { device_event->event = stage_window; /* As an X11 compositor, the stage window is always at 0,0, so * using root coordinates will give us correct stage coordinates * as well... */ device_event->event_x = device_event->root_x; device_event->event_y = device_event->root_y; } } static void meta_backend_x11_cm_translate_crossing_event (MetaBackendX11 *x11, XIEnterEvent *enter_event) { Window stage_window = meta_backend_x11_get_xwindow (x11); if (enter_event->event != stage_window) { enter_event->event = stage_window; enter_event->event_x = enter_event->root_x; enter_event->event_y = enter_event->root_y; } } static void meta_backend_x11_cm_init (MetaBackendX11Cm *backend_x11_cm) { MetaGpuXrandr *gpu_xrandr; /* * The X server deals with multiple GPUs for us, so we just see what the X * server gives us as one single GPU, even though it may actually be backed * by multiple. */ gpu_xrandr = meta_gpu_xrandr_new (META_BACKEND_X11 (backend_x11_cm)); meta_backend_add_gpu (META_BACKEND (backend_x11_cm), META_GPU (gpu_xrandr)); } static void meta_backend_x11_cm_class_init (MetaBackendX11CmClass *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_CLASS (klass); backend_class->post_init = meta_backend_x11_cm_post_init; backend_class->create_renderer = meta_backend_x11_cm_create_renderer; backend_class->create_monitor_manager = meta_backend_x11_cm_create_monitor_manager; backend_class->create_cursor_renderer = meta_backend_x11_cm_create_cursor_renderer; backend_class->create_input_settings = meta_backend_x11_cm_create_input_settings; backend_class->update_screen_size = meta_backend_x11_cm_update_screen_size; backend_class->select_stage_events = meta_backend_x11_cm_select_stage_events; backend_class->lock_layout_group = meta_backend_x11_cm_lock_layout_group; backend_class->set_keymap = meta_backend_x11_cm_set_keymap; backend_x11_class->handle_host_xevent = meta_backend_x11_cm_handle_host_xevent; backend_x11_class->translate_device_event = meta_backend_x11_cm_translate_device_event; backend_x11_class->translate_crossing_event = meta_backend_x11_cm_translate_crossing_event; } muffin-6.4.1/src/backends/x11/meta-backend-x11.c0000664000175000017500000006561414723361714020016 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ /** * SECTION:meta-backend-x11 * @title: MetaBackendX11 * @short_description: A X11 MetaBackend * * MetaBackendX11 is an implementation of #MetaBackend using X and X * extensions, like XInput and XKB. */ #include "config.h" #include "backends/x11/meta-backend-x11.h" #include #include #include #include #include #include #include "backends/meta-idle-monitor-private.h" #include "backends/meta-stage-private.h" #include "backends/x11/meta-clutter-backend-x11.h" #include "backends/x11/meta-event-x11.h" #include "backends/x11/meta-seat-x11.h" #include "backends/x11/meta-stage-x11.h" #include "backends/x11/meta-renderer-x11.h" #include "clutter/clutter.h" #include "clutter/x11/clutter-x11.h" #include "compositor/compositor-private.h" #include "core/display-private.h" #include "meta/meta-cursor-tracker.h" #include "meta/util.h" struct _MetaBackendX11Private { /* The host X11 display */ Display *xdisplay; xcb_connection_t *xcb; GSource *source; int xsync_event_base; int xsync_error_base; XSyncAlarm user_active_alarm; XSyncCounter counter; int current_touch_replay_sync_serial; int pending_touch_replay_sync_serial; Atom touch_replay_sync_atom; int xinput_opcode; int xinput_event_base; int xinput_error_base; Time latest_evtime; uint8_t xkb_event_base; uint8_t xkb_error_base; struct xkb_keymap *keymap; xkb_layout_index_t keymap_layout_group; MetaLogicalMonitor *cached_current_logical_monitor; }; typedef struct _MetaBackendX11Private MetaBackendX11Private; static GInitableIface *initable_parent_iface; static void initable_iface_init (GInitableIface *initable_iface); G_DEFINE_TYPE_WITH_CODE (MetaBackendX11, meta_backend_x11, META_TYPE_BACKEND, G_ADD_PRIVATE (MetaBackendX11) G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, initable_iface_init)); static void uint64_to_xsync_value (uint64_t value, XSyncValue *xsync_value) { XSyncIntsToValue (xsync_value, value & 0xffffffff, value >> 32); } static XSyncAlarm xsync_user_active_alarm_set (MetaBackendX11Private *priv) { XSyncAlarmAttributes attr; XSyncValue delta; unsigned long flags; flags = (XSyncCACounter | XSyncCAValueType | XSyncCATestType | XSyncCAValue | XSyncCADelta | XSyncCAEvents); XSyncIntToValue (&delta, 0); attr.trigger.counter = priv->counter; attr.trigger.value_type = XSyncAbsolute; attr.delta = delta; attr.events = TRUE; uint64_to_xsync_value (1, &attr.trigger.wait_value); attr.trigger.test_type = XSyncNegativeTransition; return XSyncCreateAlarm (priv->xdisplay, flags, &attr); } static XSyncCounter find_idletime_counter (MetaBackendX11Private *priv) { int i; int n_counters; XSyncSystemCounter *counters; XSyncCounter counter = None; counters = XSyncListSystemCounters (priv->xdisplay, &n_counters); for (i = 0; i < n_counters; i++) { if (g_strcmp0 (counters[i].name, "IDLETIME") == 0) { counter = counters[i].counter; break; } } XSyncFreeSystemCounterList (counters); return counter; } static void handle_alarm_notify (MetaBackend *backend, XSyncAlarmNotifyEvent *alarm_event) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); MetaIdleMonitor *idle_monitor; XSyncAlarmAttributes attr; ClutterBackend *clutter_backend; ClutterSeat *seat; ClutterInputDevice *pointer; if (alarm_event->state != XSyncAlarmActive || alarm_event->alarm != priv->user_active_alarm) return; attr.events = TRUE; XSyncChangeAlarm (priv->xdisplay, priv->user_active_alarm, XSyncCAEvents, &attr); clutter_backend = meta_backend_get_clutter_backend (backend); seat = clutter_backend_get_default_seat (clutter_backend); pointer = clutter_seat_get_pointer (seat); idle_monitor = meta_backend_get_idle_monitor (backend, pointer); meta_idle_monitor_reset_idletime (idle_monitor); } static void meta_backend_x11_translate_device_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_GET_CLASS (x11); backend_x11_class->translate_device_event (x11, device_event); } static void maybe_translate_touch_replay_pointer_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); if (!device_event->send_event && device_event->time != META_CURRENT_TIME && priv->current_touch_replay_sync_serial != priv->pending_touch_replay_sync_serial && XSERVER_TIME_IS_BEFORE (device_event->time, priv->latest_evtime)) { /* Emulated pointer events received after XIRejectTouch is received * on a passive touch grab will contain older timestamps, update those * so we dont get InvalidTime at grabs. */ device_event->time = priv->latest_evtime; } } static void translate_device_event (MetaBackendX11 *x11, XIDeviceEvent *device_event) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); meta_backend_x11_translate_device_event (x11, device_event); if (!device_event->send_event && device_event->time != META_CURRENT_TIME) priv->latest_evtime = device_event->time; } static void meta_backend_x11_translate_crossing_event (MetaBackendX11 *x11, XIEnterEvent *enter_event) { MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_GET_CLASS (x11); if (backend_x11_class->translate_crossing_event) backend_x11_class->translate_crossing_event (x11, enter_event); } static void translate_crossing_event (MetaBackendX11 *x11, XIEnterEvent *enter_event) { /* Throw out weird events generated by grabs. */ if (enter_event->mode == XINotifyGrab || enter_event->mode == XINotifyUngrab) { enter_event->event = None; return; } meta_backend_x11_translate_crossing_event (x11, enter_event); } static void handle_device_change (MetaBackendX11 *x11, XIEvent *event) { XIDeviceChangedEvent *device_changed; ClutterInputDevice *device; ClutterBackend *backend; ClutterSeat *seat; if (event->evtype != XI_DeviceChanged) return; device_changed = (XIDeviceChangedEvent *) event; if (device_changed->reason != XISlaveSwitch) return; backend = meta_backend_get_clutter_backend (META_BACKEND (x11)); seat = clutter_backend_get_default_seat (backend); device = meta_seat_x11_lookup_device_id (META_SEAT_X11 (seat), device_changed->sourceid); meta_backend_update_last_device (META_BACKEND (x11), device); } /* Clutter makes the assumption that there is only one X window * per stage, which is a valid assumption to make for a generic * application toolkit. As such, it will ignore any events sent * to the a stage that isn't its X window. * * When running as an X window manager, we need to respond to * events from lots of windows. Trick Clutter into translating * these events by pretending we got an event on the stage window. */ static void maybe_spoof_event_as_stage_event (MetaBackendX11 *x11, XIEvent *input_event) { switch (input_event->evtype) { case XI_Motion: case XI_ButtonPress: case XI_ButtonRelease: maybe_translate_touch_replay_pointer_event (x11, (XIDeviceEvent *) input_event); G_GNUC_FALLTHROUGH; case XI_KeyPress: case XI_KeyRelease: case XI_TouchBegin: case XI_TouchUpdate: case XI_TouchEnd: translate_device_event (x11, (XIDeviceEvent *) input_event); break; case XI_Enter: case XI_Leave: translate_crossing_event (x11, (XIEnterEvent *) input_event); break; default: break; } } static void handle_input_event (MetaBackendX11 *x11, XEvent *event) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); if (event->type == GenericEvent && event->xcookie.extension == priv->xinput_opcode) { XIEvent *input_event = (XIEvent *) event->xcookie.data; if (input_event->evtype == XI_DeviceChanged) handle_device_change (x11, input_event); else maybe_spoof_event_as_stage_event (x11, input_event); } } static void keymap_changed (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); if (priv->keymap) { xkb_keymap_unref (priv->keymap); priv->keymap = NULL; } g_signal_emit_by_name (backend, "keymap-changed", 0); } static gboolean meta_backend_x11_handle_host_xevent (MetaBackendX11 *backend_x11, XEvent *event) { MetaBackendX11Class *backend_x11_class = META_BACKEND_X11_GET_CLASS (backend_x11); return backend_x11_class->handle_host_xevent (backend_x11, event); } static void handle_host_xevent (MetaBackend *backend, XEvent *event) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); gboolean bypass_clutter = FALSE; MetaDisplay *display; switch (event->type) { case ClientMessage: if (event->xclient.window == meta_backend_x11_get_xwindow (x11) && event->xclient.message_type == priv->touch_replay_sync_atom) priv->current_touch_replay_sync_serial = event->xclient.data.l[0]; break; default: break; } XGetEventData (priv->xdisplay, &event->xcookie); display = meta_get_display (); if (display) { MetaCompositor *compositor = display->compositor; MetaPluginManager *plugin_mgr = meta_compositor_get_plugin_manager (compositor); if (meta_plugin_manager_xevent_filter (plugin_mgr, event)) bypass_clutter = TRUE; } bypass_clutter = (meta_backend_x11_handle_host_xevent (x11, event) || bypass_clutter); if (event->type == (priv->xsync_event_base + XSyncAlarmNotify)) handle_alarm_notify (backend, (XSyncAlarmNotifyEvent *) event); if (event->type == priv->xkb_event_base) { XkbEvent *xkb_ev = (XkbEvent *) event; if (xkb_ev->any.device == META_VIRTUAL_CORE_KEYBOARD_ID) { switch (xkb_ev->any.xkb_type) { case XkbMapNotify: keymap_changed (backend); break; case XkbStateNotify: if (xkb_ev->state.changed & XkbGroupLockMask) { int layout_group; gboolean layout_group_changed; layout_group = xkb_ev->state.locked_group; layout_group_changed = (int) priv->keymap_layout_group != layout_group; priv->keymap_layout_group = layout_group; if (layout_group_changed) meta_backend_notify_keymap_layout_group_changed (backend, layout_group); } break; default: break; } } } if (!bypass_clutter) { handle_input_event (x11, event); meta_x11_handle_event (event); } XFreeEventData (priv->xdisplay, &event->xcookie); } typedef struct { GSource base; GPollFD event_poll_fd; MetaBackend *backend; } XEventSource; static gboolean x_event_source_prepare (GSource *source, int *timeout) { XEventSource *x_source = (XEventSource *) source; MetaBackend *backend = x_source->backend; MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); *timeout = -1; return XPending (priv->xdisplay); } static gboolean x_event_source_check (GSource *source) { XEventSource *x_source = (XEventSource *) source; MetaBackend *backend = x_source->backend; MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); return XPending (priv->xdisplay); } static gboolean x_event_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { XEventSource *x_source = (XEventSource *) source; MetaBackend *backend = x_source->backend; MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); while (XPending (priv->xdisplay)) { XEvent event; XNextEvent (priv->xdisplay, &event); handle_host_xevent (backend, &event); } return TRUE; } static GSourceFuncs x_event_funcs = { x_event_source_prepare, x_event_source_check, x_event_source_dispatch, }; static GSource * x_event_source_new (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); GSource *source; XEventSource *x_source; source = g_source_new (&x_event_funcs, sizeof (XEventSource)); x_source = (XEventSource *) source; x_source->backend = backend; x_source->event_poll_fd.fd = ConnectionNumber (priv->xdisplay); x_source->event_poll_fd.events = G_IO_IN; g_source_add_poll (source, &x_source->event_poll_fd); g_source_attach (source, NULL); return source; } static void on_monitors_changed (MetaMonitorManager *manager, MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); priv->cached_current_logical_monitor = NULL; } static void meta_backend_x11_post_init (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); MetaMonitorManager *monitor_manager; ClutterBackend *clutter_backend; ClutterSeat *seat; int major, minor; gboolean has_xi = FALSE; priv->source = x_event_source_new (backend); if (!XSyncQueryExtension (priv->xdisplay, &priv->xsync_event_base, &priv->xsync_error_base) || !XSyncInitialize (priv->xdisplay, &major, &minor)) meta_fatal ("Could not initialize XSync"); priv->counter = find_idletime_counter (priv); if (priv->counter == None) meta_fatal ("Could not initialize XSync counter"); priv->user_active_alarm = xsync_user_active_alarm_set (priv); if (XQueryExtension (priv->xdisplay, "XInputExtension", &priv->xinput_opcode, &priv->xinput_error_base, &priv->xinput_event_base)) { major = 2; minor = 3; if (XIQueryVersion (priv->xdisplay, &major, &minor) == Success) { int version = (major * 10) + minor; if (version >= 22) has_xi = TRUE; } } if (!has_xi) meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer\n"); if (!xkb_x11_setup_xkb_extension (priv->xcb, XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION, XKB_X11_SETUP_XKB_EXTENSION_NO_FLAGS, NULL, NULL, &priv->xkb_event_base, &priv->xkb_error_base)) meta_fatal ("X server doesn't have the XKB extension, version %d.%d or newer\n", XKB_X11_MIN_MAJOR_XKB_VERSION, XKB_X11_MIN_MINOR_XKB_VERSION); META_BACKEND_CLASS (meta_backend_x11_parent_class)->post_init (backend); monitor_manager = meta_backend_get_monitor_manager (backend); g_signal_connect (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), backend); priv->touch_replay_sync_atom = XInternAtom (priv->xdisplay, "_MUTTER_TOUCH_SEQUENCE_SYNC", False); clutter_backend = meta_backend_get_clutter_backend (backend); seat = clutter_backend_get_default_seat (clutter_backend); meta_seat_x11_notify_devices (META_SEAT_X11 (seat), CLUTTER_STAGE (meta_backend_get_stage (backend))); } static ClutterBackend * meta_backend_x11_create_clutter_backend (MetaBackend *backend) { return g_object_new (META_TYPE_CLUTTER_BACKEND_X11, NULL); } static gboolean meta_backend_x11_grab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; int ret; if (timestamp != META_CURRENT_TIME && XSERVER_TIME_IS_BEFORE (timestamp, priv->latest_evtime)) timestamp = priv->latest_evtime; XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_Motion); XISetMask (mask.mask, XI_KeyPress); XISetMask (mask.mask, XI_KeyRelease); ret = XIGrabDevice (priv->xdisplay, device_id, meta_backend_x11_get_xwindow (x11), timestamp, None, XIGrabModeAsync, XIGrabModeAsync, False, /* owner_events */ &mask); return (ret == Success); } static gboolean meta_backend_x11_ungrab_device (MetaBackend *backend, int device_id, uint32_t timestamp) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); int ret; ret = XIUngrabDevice (priv->xdisplay, device_id, timestamp); XFlush (priv->xdisplay); return (ret == Success); } static void meta_backend_x11_finish_touch_sequence (MetaBackend *backend, ClutterEventSequence *sequence, MetaSequenceState state) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); int event_mode; if (state == META_SEQUENCE_ACCEPTED) event_mode = XIAcceptTouch; else if (state == META_SEQUENCE_REJECTED) event_mode = XIRejectTouch; else g_return_if_reached (); XIAllowTouchEvents (priv->xdisplay, META_VIRTUAL_CORE_POINTER_ID, meta_x11_event_sequence_get_touch_detail (sequence), DefaultRootWindow (priv->xdisplay), event_mode); if (state == META_SEQUENCE_REJECTED) { XClientMessageEvent ev; ev = (XClientMessageEvent) { .type = ClientMessage, .window = meta_backend_x11_get_xwindow (x11), .message_type = priv->touch_replay_sync_atom, .format = 32, .data.l[0] = ++priv->pending_touch_replay_sync_serial, }; XSendEvent (priv->xdisplay, meta_backend_x11_get_xwindow (x11), False, 0, (XEvent *) &ev); } } static MetaLogicalMonitor * meta_backend_x11_get_current_logical_monitor (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); MetaCursorTracker *cursor_tracker; int x, y; MetaMonitorManager *monitor_manager; MetaLogicalMonitor *logical_monitor; if (priv->cached_current_logical_monitor) return priv->cached_current_logical_monitor; cursor_tracker = meta_backend_get_cursor_tracker (backend); meta_cursor_tracker_get_pointer (cursor_tracker, &x, &y, NULL); monitor_manager = meta_backend_get_monitor_manager (backend); logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); if (!logical_monitor && monitor_manager->logical_monitors) logical_monitor = monitor_manager->logical_monitors->data; priv->cached_current_logical_monitor = logical_monitor; return priv->cached_current_logical_monitor; } static struct xkb_keymap * meta_backend_x11_get_keymap (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); if (priv->keymap == NULL) { struct xkb_context *context = xkb_context_new (XKB_CONTEXT_NO_FLAGS); priv->keymap = xkb_x11_keymap_new_from_device (context, priv->xcb, xkb_x11_get_core_keyboard_device_id (priv->xcb), XKB_KEYMAP_COMPILE_NO_FLAGS); if (priv->keymap == NULL) priv->keymap = xkb_keymap_new_from_names (context, NULL, XKB_KEYMAP_COMPILE_NO_FLAGS); xkb_context_unref (context); } return priv->keymap; } static xkb_layout_index_t meta_backend_x11_get_keymap_layout_group (MetaBackend *backend) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); return priv->keymap_layout_group; } static void meta_backend_x11_set_numlock (MetaBackend *backend, gboolean numlock_state) { MetaBackendX11 *x11 = META_BACKEND_X11 (backend); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); unsigned int num_mask; num_mask = XkbKeysymToModifiers (priv->xdisplay, XK_Num_Lock); XkbLockModifiers (priv->xdisplay, XkbUseCoreKbd, num_mask, numlock_state ? num_mask : 0); } void meta_backend_x11_handle_event (MetaBackendX11 *x11, XEvent *xevent) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); priv->cached_current_logical_monitor = NULL; } uint8_t meta_backend_x11_get_xkb_event_base (MetaBackendX11 *x11) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); return priv->xkb_event_base; } static void init_xkb_state (MetaBackendX11 *x11) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); struct xkb_keymap *keymap; int32_t device_id; struct xkb_state *state; keymap = meta_backend_get_keymap (META_BACKEND (x11)); device_id = xkb_x11_get_core_keyboard_device_id (priv->xcb); state = xkb_x11_state_new_from_device (keymap, priv->xcb, device_id); priv->keymap_layout_group = xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_LOCKED); xkb_state_unref (state); } static gboolean meta_backend_x11_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaBackendX11 *x11 = META_BACKEND_X11 (initable); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); Display *xdisplay; const char *xdisplay_name; xdisplay_name = g_getenv ("DISPLAY"); if (!xdisplay_name) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to open display, DISPLAY not set"); return FALSE; } xdisplay = XOpenDisplay (xdisplay_name); if (!xdisplay) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to open display '%s'", xdisplay_name); return FALSE; } priv->xdisplay = xdisplay; priv->xcb = XGetXCBConnection (priv->xdisplay); clutter_x11_set_display (xdisplay); init_xkb_state (x11); return initable_parent_iface->init (initable, cancellable, error); } static void initable_iface_init (GInitableIface *initable_iface) { initable_parent_iface = g_type_interface_peek_parent (initable_iface); initable_iface->init = meta_backend_x11_initable_init; } static void meta_backend_x11_finalize (GObject *object) { MetaBackendX11 *x11 = META_BACKEND_X11 (object); MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); if (priv->user_active_alarm != None) { XSyncDestroyAlarm (priv->xdisplay, priv->user_active_alarm); priv->user_active_alarm = None; } G_OBJECT_CLASS (meta_backend_x11_parent_class)->finalize (object); } static void meta_backend_x11_class_init (MetaBackendX11Class *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_backend_x11_finalize; backend_class->create_clutter_backend = meta_backend_x11_create_clutter_backend; backend_class->post_init = meta_backend_x11_post_init; backend_class->grab_device = meta_backend_x11_grab_device; backend_class->ungrab_device = meta_backend_x11_ungrab_device; backend_class->finish_touch_sequence = meta_backend_x11_finish_touch_sequence; backend_class->get_current_logical_monitor = meta_backend_x11_get_current_logical_monitor; backend_class->get_keymap = meta_backend_x11_get_keymap; backend_class->get_keymap_layout_group = meta_backend_x11_get_keymap_layout_group; backend_class->set_numlock = meta_backend_x11_set_numlock; } static void meta_backend_x11_init (MetaBackendX11 *x11) { XInitThreads (); } Display * meta_backend_x11_get_xdisplay (MetaBackendX11 *x11) { MetaBackendX11Private *priv = meta_backend_x11_get_instance_private (x11); return priv->xdisplay; } Window meta_backend_x11_get_xwindow (MetaBackendX11 *x11) { ClutterActor *stage = meta_backend_get_stage (META_BACKEND (x11)); return meta_x11_get_stage_window (CLUTTER_STAGE (stage)); } void meta_backend_x11_reload_cursor (MetaBackendX11 *x11) { MetaBackend *backend = META_BACKEND (x11); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); meta_cursor_renderer_force_update (cursor_renderer); } muffin-6.4.1/src/backends/x11/meta-xkb-a11y-x11.c0000664000175000017500000002652114723361714017756 0ustar fabiofabio/* * Copyright © 2001 Ximian, Inc. * Copyright (C) 2007 William Jon McCann * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include #include #include "backends/x11/meta-xkb-a11y-x11.h" #include "clutter/x11/clutter-x11.h" #include "core/display-private.h" #include "meta/meta-x11-errors.h" #define DEFAULT_XKB_SET_CONTROLS_MASK XkbSlowKeysMask | \ XkbBounceKeysMask | \ XkbStickyKeysMask | \ XkbMouseKeysMask | \ XkbMouseKeysAccelMask | \ XkbAccessXKeysMask | \ XkbAccessXTimeoutMask | \ XkbAccessXFeedbackMask | \ XkbControlsEnabledMask static int _xkb_event_base; static XkbDescRec * get_xkb_desc_rec (Display *xdisplay) { XkbDescRec *desc; Status status = Success; clutter_x11_trap_x_errors (); desc = XkbGetMap (xdisplay, XkbAllMapComponentsMask, XkbUseCoreKbd); if (desc != NULL) { desc->ctrls = NULL; status = XkbGetControls (xdisplay, XkbAllControlsMask, desc); } clutter_x11_untrap_x_errors (); g_return_val_if_fail (desc != NULL, NULL); g_return_val_if_fail (desc->ctrls != NULL, NULL); g_return_val_if_fail (status == Success, NULL); return desc; } static void set_xkb_desc_rec (Display *xdisplay, XkbDescRec *desc) { clutter_x11_trap_x_errors (); XkbSetControls (xdisplay, DEFAULT_XKB_SET_CONTROLS_MASK, desc); XSync (xdisplay, FALSE); clutter_x11_untrap_x_errors (); } static void check_settings_changed (ClutterSeat *seat) { Display *xdisplay = clutter_x11_get_default_display (); ClutterKbdA11ySettings kbd_a11y_settings; ClutterKeyboardA11yFlags what_changed = 0; XkbDescRec *desc; desc = get_xkb_desc_rec (xdisplay); if (!desc) return; clutter_seat_get_kbd_a11y_settings (seat, &kbd_a11y_settings); if (desc->ctrls->enabled_ctrls & XkbSlowKeysMask && !(kbd_a11y_settings.controls & CLUTTER_A11Y_SLOW_KEYS_ENABLED)) { what_changed |= CLUTTER_A11Y_SLOW_KEYS_ENABLED; kbd_a11y_settings.controls |= CLUTTER_A11Y_SLOW_KEYS_ENABLED; } else if (!(desc->ctrls->enabled_ctrls & XkbSlowKeysMask) && kbd_a11y_settings.controls & CLUTTER_A11Y_SLOW_KEYS_ENABLED) { what_changed |= CLUTTER_A11Y_SLOW_KEYS_ENABLED; kbd_a11y_settings.controls &= ~CLUTTER_A11Y_SLOW_KEYS_ENABLED; } if (desc->ctrls->enabled_ctrls & XkbStickyKeysMask && !(kbd_a11y_settings.controls & CLUTTER_A11Y_STICKY_KEYS_ENABLED)) { what_changed |= CLUTTER_A11Y_STICKY_KEYS_ENABLED; kbd_a11y_settings.controls |= CLUTTER_A11Y_STICKY_KEYS_ENABLED; } else if (!(desc->ctrls->enabled_ctrls & XkbStickyKeysMask) && kbd_a11y_settings.controls & CLUTTER_A11Y_STICKY_KEYS_ENABLED) { what_changed |= CLUTTER_A11Y_STICKY_KEYS_ENABLED; kbd_a11y_settings.controls &= ~CLUTTER_A11Y_STICKY_KEYS_ENABLED; } if (what_changed) g_signal_emit_by_name (seat, "kbd-a11y-flags-changed", kbd_a11y_settings.controls, what_changed); XkbFreeKeyboard (desc, XkbAllComponentsMask, TRUE); } static ClutterX11FilterReturn xkb_a11y_event_filter (XEvent *xevent, ClutterEvent *clutter_event, gpointer data) { ClutterSeat *seat = CLUTTER_SEAT (data); XkbEvent *xkbev = (XkbEvent *) xevent; /* 'event_type' is set to zero on notifying us of updates in * response to client requests (including our own) and non-zero * to notify us of key/mouse events causing changes (like * pressing shift 5 times to enable sticky keys). * * We only want to update out settings when it's in response to an * explicit user input event, so require a non-zero event_type. */ if (xevent->xany.type == (_xkb_event_base + XkbEventCode) && xkbev->any.xkb_type == XkbControlsNotify && xkbev->ctrls.event_type != 0) check_settings_changed (seat); return CLUTTER_X11_FILTER_CONTINUE; } static gboolean is_xkb_available (Display *xdisplay) { int opcode, error_base, event_base, major, minor; if (_xkb_event_base) return TRUE; if (!XkbQueryExtension (xdisplay, &opcode, &event_base, &error_base, &major, &minor)) return FALSE; if (!XkbUseExtension (xdisplay, &major, &minor)) return FALSE; _xkb_event_base = event_base; return TRUE; } static unsigned long set_value_mask (gboolean flag, unsigned long value, unsigned long mask) { if (flag) return value | mask; return value & ~mask; } static gboolean set_xkb_ctrl (XkbDescRec *desc, ClutterKeyboardA11yFlags settings, ClutterKeyboardA11yFlags flag, unsigned long mask) { gboolean result = (settings & flag) == flag; desc->ctrls->enabled_ctrls = set_value_mask (result, desc->ctrls->enabled_ctrls, mask); return result; } void meta_seat_x11_apply_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *kbd_a11y_settings) { Display *xdisplay = clutter_x11_get_default_display (); XkbDescRec *desc; gboolean enable_accessX; desc = get_xkb_desc_rec (xdisplay); if (!desc) return; /* general */ enable_accessX = kbd_a11y_settings->controls & CLUTTER_A11Y_KEYBOARD_ENABLED; desc->ctrls->enabled_ctrls = set_value_mask (enable_accessX, desc->ctrls->enabled_ctrls, XkbAccessXKeysMask); if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, CLUTTER_A11Y_TIMEOUT_ENABLED, XkbAccessXTimeoutMask)) { desc->ctrls->ax_timeout = kbd_a11y_settings->timeout_delay; /* disable only the master flag via the server we will disable * the rest on the rebound without affecting settings state * don't change the option flags at all. */ desc->ctrls->axt_ctrls_mask = XkbAccessXKeysMask | XkbAccessXFeedbackMask; desc->ctrls->axt_ctrls_values = 0; desc->ctrls->axt_opts_mask = 0; } desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_FEATURE_STATE_CHANGE_BEEP, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_FeatureFBMask | XkbAX_SlowWarnFBMask); /* bounce keys */ if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, CLUTTER_A11Y_BOUNCE_KEYS_ENABLED, XkbBounceKeysMask)) { desc->ctrls->debounce_delay = kbd_a11y_settings->debounce_delay; desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_BOUNCE_KEYS_BEEP_REJECT, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_BKRejectFBMask); } /* mouse keys */ if (clutter_keymap_get_num_lock_state (clutter_seat_get_keymap (seat))) { /* Disable mousekeys when NumLock is ON */ desc->ctrls->enabled_ctrls &= ~(XkbMouseKeysMask | XkbMouseKeysAccelMask); } else if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, CLUTTER_A11Y_MOUSE_KEYS_ENABLED, XkbMouseKeysMask | XkbMouseKeysAccelMask)) { int mk_max_speed; int mk_accel_time; desc->ctrls->mk_interval = 100; /* msec between mousekey events */ desc->ctrls->mk_curve = 50; /* We store pixels / sec, XKB wants pixels / event */ mk_max_speed = kbd_a11y_settings->mousekeys_max_speed; desc->ctrls->mk_max_speed = mk_max_speed / (1000 / desc->ctrls->mk_interval); if (desc->ctrls->mk_max_speed <= 0) desc->ctrls->mk_max_speed = 1; mk_accel_time = kbd_a11y_settings->mousekeys_accel_time; desc->ctrls->mk_time_to_max = mk_accel_time / desc->ctrls->mk_interval; if (desc->ctrls->mk_time_to_max <= 0) desc->ctrls->mk_time_to_max = 1; desc->ctrls->mk_delay = kbd_a11y_settings->mousekeys_init_delay; } /* slow keys */ if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, CLUTTER_A11Y_SLOW_KEYS_ENABLED, XkbSlowKeysMask)) { desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_SLOW_KEYS_BEEP_PRESS, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKPressFBMask); desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_SLOW_KEYS_BEEP_ACCEPT, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKAcceptFBMask); desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_SLOW_KEYS_BEEP_REJECT, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_SKRejectFBMask); desc->ctrls->slow_keys_delay = kbd_a11y_settings->slowkeys_delay; /* anything larger than 500 seems to loose all keyboard input */ if (desc->ctrls->slow_keys_delay > 500) desc->ctrls->slow_keys_delay = 500; } /* sticky keys */ if (set_xkb_ctrl (desc, kbd_a11y_settings->controls, CLUTTER_A11Y_STICKY_KEYS_ENABLED, XkbStickyKeysMask)) { desc->ctrls->ax_options |= XkbAX_LatchToLockMask; desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_STICKY_KEYS_TWO_KEY_OFF, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_TwoKeysMask); desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_STICKY_KEYS_BEEP, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_StickyKeysFBMask); } /* toggle keys */ desc->ctrls->ax_options = set_value_mask (kbd_a11y_settings->controls & CLUTTER_A11Y_TOGGLE_KEYS_ENABLED, desc->ctrls->ax_options, XkbAccessXFeedbackMask | XkbAX_IndicatorFBMask); set_xkb_desc_rec (xdisplay, desc); XkbFreeKeyboard (desc, XkbAllComponentsMask, TRUE); } gboolean meta_seat_x11_a11y_init (ClutterSeat *seat) { Display *xdisplay = clutter_x11_get_default_display (); guint event_mask; if (!is_xkb_available (xdisplay)) return FALSE; event_mask = XkbControlsNotifyMask | XkbAccessXNotifyMask; XkbSelectEvents (xdisplay, XkbUseCoreKbd, event_mask, event_mask); clutter_x11_add_filter (xkb_a11y_event_filter, seat); return TRUE; } muffin-6.4.1/src/backends/x11/meta-stage-x11.h0000664000175000017500000000560014723361714017524 0ustar fabiofabio/* Clutter. * An OpenGL based 'interactive canvas' library. * Authored By Matthew Allum * Copyright (C) 2006-2007 OpenedHand * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * */ #ifndef META_STAGE_X11_H #define META_STAGE_X11_H #include #include #include "backends/meta-backend-private.h" #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" G_BEGIN_DECLS #define META_TYPE_STAGE_X11 (meta_stage_x11_get_type ()) #define META_STAGE_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_STAGE_X11, MetaStageX11)) #define META_IS_STAGE_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_STAGE_X11)) #define META_STAGE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_STAGE_X11, MetaStageX11Class)) #define META_IS_STAGE_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_STAGE_X11)) #define META_STAGE_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_STAGE_X11, MetaStageX11Class)) typedef struct _MetaStageX11 MetaStageX11; typedef struct _MetaStageX11Class MetaStageX11Class; G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaStageX11, g_object_unref) typedef enum { STAGE_X11_WITHDRAWN = 1 << 1 } MetaStageX11State; struct _MetaStageX11 { ClutterStageCogl parent_instance; MetaBackend *backend; CoglOnscreen *onscreen; Window xwin; gint xwin_width; gint xwin_height; /* FIXME target_width / height */ CoglFrameClosure *frame_closure; gchar *title; guint clipped_redraws_cool_off; MetaStageX11State wm_state; guint is_cursor_visible : 1; guint viewport_initialized : 1; guint accept_focus : 1; }; struct _MetaStageX11Class { ClutterStageCoglClass parent_class; }; CLUTTER_EXPORT GType meta_stage_x11_get_type (void) G_GNUC_CONST; /* Private to subclasses */ void meta_stage_x11_set_user_time (MetaStageX11 *stage_x11, guint32 user_time); gboolean meta_stage_x11_translate_event (MetaStageX11 *stage_x11, XEvent *xevent, ClutterEvent *event); ClutterStage *meta_x11_get_stage_from_window (Window win); Window meta_x11_get_stage_window (ClutterStage *stage); G_END_DECLS #endif /* META_STAGE_H */ muffin-6.4.1/src/backends/x11/meta-keymap-x11.c0000664000175000017500000006326514723361714017715 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2010 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #include "config.h" #include #include #include "backends/x11/meta-keymap-x11.h" #include "clutter/clutter.h" #include "clutter/clutter-mutter.h" #include "clutter/x11/clutter-x11.h" typedef struct _DirectionCacheEntry DirectionCacheEntry; typedef struct _ClutterKeymapKey ClutterKeymapKey; struct _ClutterKeymapKey { uint32_t keycode; uint32_t group; uint32_t level; }; struct _DirectionCacheEntry { uint32_t serial; Atom group_atom; PangoDirection direction; }; struct _MetaKeymapX11 { ClutterKeymap parent_instance; ClutterBackend *backend; int min_keycode; int max_keycode; ClutterModifierType modmap[8]; ClutterModifierType num_lock_mask; ClutterModifierType scroll_lock_mask; ClutterModifierType level3_shift_mask; PangoDirection current_direction; XkbDescPtr xkb_desc; int xkb_event_base; uint32_t xkb_map_serial; Atom current_group_atom; uint32_t current_cache_serial; DirectionCacheEntry group_direction_cache[4]; int current_group; GHashTable *reserved_keycodes; GQueue *available_keycodes; uint32_t keymap_serial; uint32_t caps_lock_state : 1; uint32_t num_lock_state : 1; uint32_t has_direction : 1; uint32_t use_xkb : 1; uint32_t have_xkb_autorepeat : 1; }; enum { PROP_0, PROP_BACKEND, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST] = { NULL, }; G_DEFINE_TYPE (MetaKeymapX11, meta_keymap_x11, CLUTTER_TYPE_KEYMAP) /* code adapted from gdk/x11/gdkkeys-x11.c - update_modmap */ static void update_modmap (Display *display, MetaKeymapX11 *keymap_x11) { static struct { const char *name; Atom atom; ClutterModifierType mask; } vmods[] = { { "Meta", 0, CLUTTER_META_MASK }, { "Super", 0, CLUTTER_SUPER_MASK }, { "Hyper", 0, CLUTTER_HYPER_MASK }, { NULL, 0, 0 } }; int i, j, k; if (vmods[0].atom == 0) for (i = 0; vmods[i].name; i++) vmods[i].atom = XInternAtom (display, vmods[i].name, FALSE); for (i = 0; i < 8; i++) keymap_x11->modmap[i] = 1 << i; for (i = 0; i < XkbNumVirtualMods; i++) { for (j = 0; vmods[j].atom; j++) { if (keymap_x11->xkb_desc->names->vmods[i] == vmods[j].atom) { for (k = 0; k < 8; k++) { if (keymap_x11->xkb_desc->server->vmods[i] & (1 << k)) keymap_x11->modmap[k] |= vmods[j].mask; } } } } } static XkbDescPtr get_xkb (MetaKeymapX11 *keymap_x11) { Display *xdisplay = clutter_x11_get_default_display (); if (keymap_x11->max_keycode == 0) XDisplayKeycodes (xdisplay, &keymap_x11->min_keycode, &keymap_x11->max_keycode); if (keymap_x11->xkb_desc == NULL) { int flags = XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask; keymap_x11->xkb_desc = XkbGetMap (xdisplay, flags, XkbUseCoreKbd); if (G_UNLIKELY (keymap_x11->xkb_desc == NULL)) { g_error ("Failed to get the keymap from XKB"); return NULL; } flags = XkbGroupNamesMask | XkbVirtualModNamesMask; XkbGetNames (xdisplay, flags, keymap_x11->xkb_desc); update_modmap (xdisplay, keymap_x11); } else if (keymap_x11->xkb_map_serial != keymap_x11->keymap_serial) { int flags = XkbKeySymsMask | XkbKeyTypesMask | XkbModifierMapMask | XkbVirtualModsMask; XkbGetUpdatedMap (xdisplay, flags, keymap_x11->xkb_desc); flags = XkbGroupNamesMask | XkbVirtualModNamesMask; XkbGetNames (xdisplay, flags, keymap_x11->xkb_desc); update_modmap (xdisplay, keymap_x11); keymap_x11->xkb_map_serial = keymap_x11->keymap_serial; } if (keymap_x11->num_lock_mask == 0) keymap_x11->num_lock_mask = XkbKeysymToModifiers (xdisplay, XK_Num_Lock); if (keymap_x11->scroll_lock_mask == 0) keymap_x11->scroll_lock_mask = XkbKeysymToModifiers (xdisplay, XK_Scroll_Lock); if (keymap_x11->level3_shift_mask == 0) keymap_x11->level3_shift_mask = XkbKeysymToModifiers (xdisplay, XK_ISO_Level3_Shift); return keymap_x11->xkb_desc; } static void update_locked_mods (MetaKeymapX11 *keymap_x11, int locked_mods) { gboolean old_caps_lock_state, old_num_lock_state; old_caps_lock_state = keymap_x11->caps_lock_state; old_num_lock_state = keymap_x11->num_lock_state; keymap_x11->caps_lock_state = (locked_mods & CLUTTER_LOCK_MASK) != 0; keymap_x11->num_lock_state = (locked_mods & keymap_x11->num_lock_mask) != 0; g_debug ("Locks state changed - Num: %s, Caps: %s", keymap_x11->num_lock_state ? "set" : "unset", keymap_x11->caps_lock_state ? "set" : "unset"); if ((keymap_x11->caps_lock_state != old_caps_lock_state) || (keymap_x11->num_lock_state != old_num_lock_state)) g_signal_emit_by_name (keymap_x11, "state-changed"); } /* the code to retrieve the keymap direction and cache it * is taken from GDK: * gdk/x11/gdkkeys-x11.c */ static PangoDirection get_direction (XkbDescPtr xkb, int group) { int rtl_minus_ltr = 0; /* total number of RTL keysyms minus LTR ones */ int code; for (code = xkb->min_key_code; code <= xkb->max_key_code; code += 1) { int level = 0; KeySym sym = XkbKeySymEntry (xkb, code, level, group); PangoDirection dir = _clutter_pango_unichar_direction (clutter_keysym_to_unicode (sym)); switch (dir) { case PANGO_DIRECTION_RTL: rtl_minus_ltr++; break; case PANGO_DIRECTION_LTR: rtl_minus_ltr--; break; default: break; } } if (rtl_minus_ltr > 0) return PANGO_DIRECTION_RTL; return PANGO_DIRECTION_LTR; } static PangoDirection get_direction_from_cache (MetaKeymapX11 *keymap_x11, XkbDescPtr xkb, int group) { Atom group_atom = xkb->names->groups[group]; gboolean cache_hit = FALSE; DirectionCacheEntry *cache = keymap_x11->group_direction_cache; PangoDirection direction = PANGO_DIRECTION_NEUTRAL; int i; if (keymap_x11->has_direction) { /* look up in the cache */ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) { if (cache[i].group_atom == group_atom) { cache_hit = TRUE; cache[i].serial = keymap_x11->current_cache_serial++; direction = cache[i].direction; group_atom = cache[i].group_atom; break; } } } else { /* initialize the cache */ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) { cache[i].group_atom = 0; cache[i].direction = PANGO_DIRECTION_NEUTRAL; cache[i].serial = keymap_x11->current_cache_serial; } keymap_x11->current_cache_serial += 1; } /* insert the new entry in the cache */ if (!cache_hit) { int oldest = 0; direction = get_direction (xkb, group); /* replace the oldest entry */ for (i = 0; i < G_N_ELEMENTS (keymap_x11->group_direction_cache); i++) { if (cache[i].serial < cache[oldest].serial) oldest = i; } cache[oldest].group_atom = group_atom; cache[oldest].direction = direction; cache[oldest].serial = keymap_x11->current_cache_serial++; } return direction; } static void update_direction (MetaKeymapX11 *keymap_x11, int group) { XkbDescPtr xkb = get_xkb (keymap_x11); Atom group_atom; group_atom = xkb->names->groups[group]; if (!keymap_x11->has_direction || keymap_x11->current_group_atom != group_atom) { keymap_x11->current_direction = get_direction_from_cache (keymap_x11, xkb, group); keymap_x11->current_group_atom = group_atom; keymap_x11->has_direction = TRUE; } } static void meta_keymap_x11_constructed (GObject *object) { MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (object); Display *xdisplay = clutter_x11_get_default_display (); int xkb_major = XkbMajorVersion; int xkb_minor = XkbMinorVersion; g_assert (keymap_x11->backend != NULL); if (XkbLibraryVersion (&xkb_major, &xkb_minor)) { xkb_major = XkbMajorVersion; xkb_minor = XkbMinorVersion; if (XkbQueryExtension (xdisplay, NULL, &keymap_x11->xkb_event_base, NULL, &xkb_major, &xkb_minor)) { Bool detectable_autorepeat_supported; keymap_x11->use_xkb = TRUE; XkbSelectEvents (xdisplay, XkbUseCoreKbd, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask, XkbNewKeyboardNotifyMask | XkbMapNotifyMask | XkbStateNotifyMask); XkbSelectEventDetails (xdisplay, XkbUseCoreKbd, XkbStateNotify, XkbAllStateComponentsMask, XkbGroupLockMask | XkbModifierLockMask); /* enable XKB autorepeat */ XkbSetDetectableAutoRepeat (xdisplay, True, &detectable_autorepeat_supported); keymap_x11->have_xkb_autorepeat = detectable_autorepeat_supported; } } } static void meta_keymap_x11_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaKeymapX11 *keymap = META_KEYMAP_X11 (object); switch (prop_id) { case PROP_BACKEND: keymap->backend = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_keymap_x11_refresh_reserved_keycodes (MetaKeymapX11 *keymap_x11) { Display *dpy = clutter_x11_get_default_display (); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes); while (g_hash_table_iter_next (&iter, &key, &value)) { uint32_t reserved_keycode = GPOINTER_TO_UINT (key); uint32_t reserved_keysym = GPOINTER_TO_UINT (value); uint32_t actual_keysym = XkbKeycodeToKeysym (dpy, reserved_keycode, 0, 0); /* If an available keycode is no longer mapped to the stored keysym, then * the keycode should not be considered available anymore and should be * removed both from the list of available and reserved keycodes. */ if (reserved_keysym != actual_keysym) { g_hash_table_iter_remove (&iter); g_queue_remove (keymap_x11->available_keycodes, key); } } } static gboolean meta_keymap_x11_replace_keycode (MetaKeymapX11 *keymap_x11, KeyCode keycode, KeySym keysym) { if (keymap_x11->use_xkb) { Display *dpy = clutter_x11_get_default_display (); XkbDescPtr xkb = get_xkb (keymap_x11); XkbMapChangesRec changes; XFlush (dpy); xkb->device_spec = XkbUseCoreKbd; memset (&changes, 0, sizeof(changes)); if (keysym != NoSymbol) { int types[XkbNumKbdGroups] = { XkbOneLevelIndex }; XkbChangeTypesOfKey (xkb, keycode, 1, XkbGroup1Mask, types, &changes); XkbKeySymEntry (xkb, keycode, 0, 0) = keysym; } else { /* Reset to NoSymbol */ XkbChangeTypesOfKey (xkb, keycode, 0, XkbGroup1Mask, NULL, &changes); } changes.changed = XkbKeySymsMask | XkbKeyTypesMask; changes.first_key_sym = keycode; changes.num_key_syms = 1; changes.first_type = 0; changes.num_types = xkb->map->num_types; XkbChangeMap (dpy, xkb, &changes); XFlush (dpy); return TRUE; } return FALSE; } static void meta_keymap_x11_finalize (GObject *object) { MetaKeymapX11 *keymap; GHashTableIter iter; gpointer key, value; keymap = META_KEYMAP_X11 (object); meta_keymap_x11_refresh_reserved_keycodes (keymap); g_hash_table_iter_init (&iter, keymap->reserved_keycodes); while (g_hash_table_iter_next (&iter, &key, &value)) { uint32_t keycode = GPOINTER_TO_UINT (key); meta_keymap_x11_replace_keycode (keymap, keycode, NoSymbol); } g_hash_table_destroy (keymap->reserved_keycodes); g_queue_free (keymap->available_keycodes); if (keymap->xkb_desc != NULL) XkbFreeKeyboard (keymap->xkb_desc, XkbAllComponentsMask, True); G_OBJECT_CLASS (meta_keymap_x11_parent_class)->finalize (object); } static gboolean meta_keymap_x11_get_num_lock_state (ClutterKeymap *keymap) { MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (keymap); return keymap_x11->num_lock_state; } static gboolean meta_keymap_x11_get_caps_lock_state (ClutterKeymap *keymap) { MetaKeymapX11 *keymap_x11 = META_KEYMAP_X11 (keymap); return keymap_x11->caps_lock_state; } static PangoDirection meta_keymap_x11_get_direction (ClutterKeymap *keymap) { MetaKeymapX11 *keymap_x11; g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), PANGO_DIRECTION_NEUTRAL); keymap_x11 = META_KEYMAP_X11 (keymap); if (keymap_x11->use_xkb) { if (!keymap_x11->has_direction) { XkbStateRec state_rec; XkbGetState (clutter_x11_get_default_display (), XkbUseCoreKbd, &state_rec); update_direction (keymap_x11, XkbStateGroup (&state_rec)); } return keymap_x11->current_direction; } else { return PANGO_DIRECTION_NEUTRAL; } } static void meta_keymap_x11_class_init (MetaKeymapX11Class *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterKeymapClass *keymap_class = CLUTTER_KEYMAP_CLASS (klass); obj_props[PROP_BACKEND] = g_param_spec_object ("backend", "Backend", "The Clutter backend", CLUTTER_TYPE_BACKEND, G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY); gobject_class->constructed = meta_keymap_x11_constructed; gobject_class->set_property = meta_keymap_x11_set_property; gobject_class->finalize = meta_keymap_x11_finalize; keymap_class->get_num_lock_state = meta_keymap_x11_get_num_lock_state; keymap_class->get_caps_lock_state = meta_keymap_x11_get_caps_lock_state; keymap_class->get_direction = meta_keymap_x11_get_direction; g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } static void meta_keymap_x11_init (MetaKeymapX11 *keymap) { keymap->current_direction = PANGO_DIRECTION_NEUTRAL; keymap->current_group = -1; keymap->reserved_keycodes = g_hash_table_new (NULL, NULL); keymap->available_keycodes = g_queue_new (); } gboolean meta_keymap_x11_handle_event (MetaKeymapX11 *keymap_x11, XEvent *xevent) { gboolean retval; if (!keymap_x11->use_xkb) return FALSE; retval = FALSE; if (xevent->type == keymap_x11->xkb_event_base) { XkbEvent *xkb_event = (XkbEvent *) xevent; switch (xkb_event->any.xkb_type) { case XkbStateNotify: g_debug ("Updating keyboard state"); keymap_x11->current_group = XkbStateGroup (&xkb_event->state); update_direction (keymap_x11, keymap_x11->current_group); update_locked_mods (keymap_x11, xkb_event->state.locked_mods); retval = TRUE; break; case XkbNewKeyboardNotify: case XkbMapNotify: g_debug ("Updating keyboard mapping"); XkbRefreshKeyboardMapping (&xkb_event->map); keymap_x11->keymap_serial += 1; retval = TRUE; break; default: break; } } else if (xevent->type == MappingNotify) { XRefreshKeyboardMapping (&xevent->xmapping); keymap_x11->keymap_serial += 1; retval = TRUE; } return retval; } int meta_keymap_x11_get_key_group (MetaKeymapX11 *keymap, ClutterModifierType state) { return XkbGroupForCoreState (state); } G_GNUC_BEGIN_IGNORE_DEPRECATIONS /* XXX - yes, I know that XKeycodeToKeysym() has been deprecated; hopefully, * this code will never get run on any decent system that is also able to * run Clutter. I just don't want to copy the implementation inside GDK for * a fallback path. */ static int translate_keysym (MetaKeymapX11 *keymap, uint32_t hardware_keycode) { int retval; retval = XKeycodeToKeysym (clutter_x11_get_default_display (), hardware_keycode, 0); return retval; } G_GNUC_END_IGNORE_DEPRECATIONS int meta_keymap_x11_translate_key_state (MetaKeymapX11 *keymap, uint32_t hardware_keycode, ClutterModifierType *modifier_state_p, ClutterModifierType *mods_p) { ClutterModifierType unconsumed_modifiers = 0; ClutterModifierType modifier_state = *modifier_state_p; int retval; g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), 0); if (keymap->use_xkb) { XkbDescRec *xkb = get_xkb (keymap); KeySym tmp_keysym; if (XkbTranslateKeyCode (xkb, hardware_keycode, modifier_state, &unconsumed_modifiers, &tmp_keysym)) { retval = tmp_keysym; } else retval = 0; } else retval = translate_keysym (keymap, hardware_keycode); if (mods_p) *mods_p = unconsumed_modifiers; *modifier_state_p = modifier_state & ~(keymap->num_lock_mask | keymap->scroll_lock_mask | LockMask); return retval; } gboolean meta_keymap_x11_get_is_modifier (MetaKeymapX11 *keymap, int keycode) { g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap), FALSE); if (keycode < keymap->min_keycode || keycode > keymap->max_keycode) return FALSE; if (keymap->use_xkb) { XkbDescRec *xkb = get_xkb (keymap); if (xkb->map->modmap && xkb->map->modmap[keycode] != 0) return TRUE; } return FALSE; } static gboolean meta_keymap_x11_get_entries_for_keyval (MetaKeymapX11 *keymap_x11, uint32_t keyval, ClutterKeymapKey **keys, int *n_keys) { if (keymap_x11->use_xkb) { XkbDescRec *xkb = get_xkb (keymap_x11); GArray *retval; int keycode; keycode = keymap_x11->min_keycode; retval = g_array_new (FALSE, FALSE, sizeof (ClutterKeymapKey)); while (keycode <= keymap_x11->max_keycode) { int max_shift_levels = XkbKeyGroupsWidth (xkb, keycode); int group = 0; int level = 0; int total_syms = XkbKeyNumSyms (xkb, keycode); int i = 0; KeySym *entry; /* entry is an array with all syms for group 0, all * syms for group 1, etc. and for each group the * shift level syms are in order */ entry = XkbKeySymsPtr (xkb, keycode); while (i < total_syms) { g_assert (i == (group * max_shift_levels + level)); if (entry[i] == keyval) { ClutterKeymapKey key; key.keycode = keycode; key.group = group; key.level = level; g_array_append_val (retval, key); g_assert (XkbKeySymEntry (xkb, keycode, level, group) == keyval); } ++level; if (level == max_shift_levels) { level = 0; ++group; } ++i; } ++keycode; } if (retval->len > 0) { *keys = (ClutterKeymapKey*) retval->data; *n_keys = retval->len; } else { *keys = NULL; *n_keys = 0; } g_array_free (retval, retval->len > 0 ? FALSE : TRUE); return *n_keys > 0; } else { return FALSE; } } static uint32_t meta_keymap_x11_get_available_keycode (MetaKeymapX11 *keymap_x11) { if (keymap_x11->use_xkb) { meta_keymap_x11_refresh_reserved_keycodes (keymap_x11); if (g_hash_table_size (keymap_x11->reserved_keycodes) < 5) { Display *dpy = clutter_x11_get_default_display (); XkbDescPtr xkb = get_xkb (keymap_x11); uint32_t i; for (i = xkb->max_key_code; i >= xkb->min_key_code; --i) { if (XkbKeycodeToKeysym (dpy, i, 0, 0) == NoSymbol) return i; } } return GPOINTER_TO_UINT (g_queue_pop_head (keymap_x11->available_keycodes)); } return 0; } gboolean meta_keymap_x11_reserve_keycode (MetaKeymapX11 *keymap_x11, uint32_t keyval, uint32_t *keycode_out) { g_return_val_if_fail (META_IS_KEYMAP_X11 (keymap_x11), FALSE); g_return_val_if_fail (keyval != 0, FALSE); g_return_val_if_fail (keycode_out != NULL, FALSE); *keycode_out = meta_keymap_x11_get_available_keycode (keymap_x11); if (*keycode_out == NoSymbol) { g_warning ("Cannot reserve a keycode for keyval %d: no available keycode", keyval); return FALSE; } if (!meta_keymap_x11_replace_keycode (keymap_x11, *keycode_out, keyval)) { g_warning ("Failed to remap keycode %d to keyval %d", *keycode_out, keyval); return FALSE; } g_hash_table_insert (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (*keycode_out), GUINT_TO_POINTER (keyval)); g_queue_remove (keymap_x11->available_keycodes, GUINT_TO_POINTER (*keycode_out)); return TRUE; } void meta_keymap_x11_release_keycode_if_needed (MetaKeymapX11 *keymap_x11, uint32_t keycode) { g_return_if_fail (META_IS_KEYMAP_X11 (keymap_x11)); if (!g_hash_table_contains (keymap_x11->reserved_keycodes, GUINT_TO_POINTER (keycode)) || g_queue_index (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode)) != -1) return; g_queue_push_tail (keymap_x11->available_keycodes, GUINT_TO_POINTER (keycode)); } void meta_keymap_x11_latch_modifiers (MetaKeymapX11 *keymap_x11, uint32_t level, gboolean enable) { uint32_t modifiers[] = { 0, ShiftMask, keymap_x11->level3_shift_mask, keymap_x11->level3_shift_mask | ShiftMask, }; uint32_t value = 0; if (!keymap_x11->use_xkb) return; level = CLAMP (level, 0, G_N_ELEMENTS (modifiers) - 1); if (enable) value = modifiers[level]; else value = 0; XkbLatchModifiers (clutter_x11_get_default_display (), XkbUseCoreKbd, modifiers[level], value); } static uint32_t meta_keymap_x11_get_current_group (MetaKeymapX11 *keymap_x11) { XkbStateRec state_rec; if (keymap_x11->current_group >= 0) return keymap_x11->current_group; XkbGetState (clutter_x11_get_default_display (), XkbUseCoreKbd, &state_rec); return XkbStateGroup (&state_rec); } gboolean meta_keymap_x11_keycode_for_keyval (MetaKeymapX11 *keymap_x11, uint32_t keyval, uint32_t *keycode_out, uint32_t *level_out) { ClutterKeymapKey *keys; int i, n_keys, group; gboolean found = FALSE; g_return_val_if_fail (keycode_out != NULL, FALSE); g_return_val_if_fail (level_out != NULL, FALSE); group = meta_keymap_x11_get_current_group (keymap_x11); if (!meta_keymap_x11_get_entries_for_keyval (keymap_x11, keyval, &keys, &n_keys)) return FALSE; for (i = 0; i < n_keys && !found; i++) { if (keys[i].group == group) { *keycode_out = keys[i].keycode; *level_out = keys[i].level; found = TRUE; } } if (!found) { GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, keymap_x11->reserved_keycodes); while (!found && g_hash_table_iter_next (&iter, &key, &value)) { uint32_t reserved_keycode = GPOINTER_TO_UINT (key); uint32_t reserved_keysym = GPOINTER_TO_UINT (value); if (keyval == reserved_keysym) { *keycode_out = reserved_keycode; *level_out = 0; found = TRUE; } } } g_free (keys); return found; } muffin-6.4.1/src/backends/x11/meta-seat-x11.h0000664000175000017500000000317614723361714017363 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_SEAT_X11_H #define META_SEAT_X11_H #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_SEAT_X11 meta_seat_x11_get_type () G_DECLARE_FINAL_TYPE (MetaSeatX11, meta_seat_x11, META, SEAT_X11, ClutterSeat) MetaSeatX11 * meta_seat_x11_new (int opcode, int master_pointer, int master_keyboard); gboolean meta_seat_x11_translate_event (MetaSeatX11 *seat, XEvent *xevent, ClutterEvent *event); ClutterInputDevice * meta_seat_x11_lookup_device_id (MetaSeatX11 *seat_x11, int device_id); void meta_seat_x11_select_stage_events (MetaSeatX11 *seat, ClutterStage *stage); void meta_seat_x11_notify_devices (MetaSeatX11 *seat_x11, ClutterStage *stage); G_END_DECLS #endif /* META_SEAT_X11_H */ muffin-6.4.1/src/backends/x11/meta-virtual-input-device-x11.h0000664000175000017500000000227014723361714022501 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Jonas Ådahl */ #ifndef META_VIRTUAL_INPUT_DEVICE_X11_H #define META_VIRTUAL_INPUT_DEVICE_X11_H #include "clutter/clutter.h" #define META_TYPE_VIRTUAL_INPUT_DEVICE_X11 (meta_virtual_input_device_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaVirtualInputDeviceX11, meta_virtual_input_device_x11, META, VIRTUAL_INPUT_DEVICE_X11, ClutterVirtualInputDevice) #endif /* META_VIRTUAL_INPUT_DEVICE_X11_H */ muffin-6.4.1/src/backends/x11/meta-cursor-renderer-x11.h0000664000175000017500000000415514723361714021546 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_X11_H #define META_CURSOR_RENDERER_X11_H #include "backends/meta-cursor-renderer.h" #define META_TYPE_CURSOR_RENDERER_X11 (meta_cursor_renderer_x11_get_type ()) #define META_CURSOR_RENDERER_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_CURSOR_RENDERER_X11, MetaCursorRendererX11)) #define META_CURSOR_RENDERER_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_CURSOR_RENDERER_X11, MetaCursorRendererX11Class)) #define META_IS_CURSOR_RENDERER_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_CURSOR_RENDERER_X11)) #define META_IS_CURSOR_RENDERER_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_CURSOR_RENDERER_X11)) #define META_CURSOR_RENDERER_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_CURSOR_RENDERER_X11, MetaCursorRendererX11Class)) typedef struct _MetaCursorRendererX11 MetaCursorRendererX11; typedef struct _MetaCursorRendererX11Class MetaCursorRendererX11Class; struct _MetaCursorRendererX11 { MetaCursorRenderer parent; }; struct _MetaCursorRendererX11Class { MetaCursorRendererClass parent_class; }; GType meta_cursor_renderer_x11_get_type (void) G_GNUC_CONST; #endif /* META_CURSOR_RENDERER_X11_H */ muffin-6.4.1/src/backends/x11/meta-monitor-manager-xrandr.h0000664000175000017500000000321614723361714022406 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_MANAGER_XRANDR_H #define META_MONITOR_MANAGER_XRANDR_H #include #include "backends/meta-monitor-manager-private.h" #define META_TYPE_MONITOR_MANAGER_XRANDR (meta_monitor_manager_xrandr_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorManagerXrandr, meta_monitor_manager_xrandr, META, MONITOR_MANAGER_XRANDR, MetaMonitorManager) Display * meta_monitor_manager_xrandr_get_xdisplay (MetaMonitorManagerXrandr *manager_xrandr); gboolean meta_monitor_manager_xrandr_handle_xevent (MetaMonitorManagerXrandr *manager, XEvent *event); uint32_t meta_monitor_manager_xrandr_get_config_timestamp (MetaMonitorManagerXrandr *manager); #endif /* META_MONITOR_MANAGER_XRANDR_H */ muffin-6.4.1/src/backends/x11/meta-barrier-x11.c0000664000175000017500000001333114723361714020042 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014-2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre * Jonas Ådahl */ /** * SECTION:barrier-x11 * @Title: MetaBarrierImplX11 * @Short_Description: Pointer barriers implementation for X11 */ #include "config.h" #include #include #include #include "backends/x11/meta-barrier-x11.h" #include "core/display-private.h" #include "meta/barrier.h" #include "x11/meta-x11-display-private.h" struct _MetaBarrierImplX11 { MetaBarrierImpl parent; MetaBarrier *barrier; PointerBarrier xbarrier; }; G_DEFINE_TYPE (MetaBarrierImplX11, meta_barrier_impl_x11, META_TYPE_BARRIER_IMPL) static gboolean _meta_barrier_impl_x11_is_active (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); return self->xbarrier != 0; } static void _meta_barrier_impl_x11_release (MetaBarrierImpl *impl, MetaBarrierEvent *event) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaDisplay *display = self->barrier->priv->display; Display *dpy = meta_x11_display_get_xdisplay (display->x11_display); if (META_X11_DISPLAY_HAS_XINPUT_23 (display->x11_display)) { XIBarrierReleasePointer (dpy, META_VIRTUAL_CORE_POINTER_ID, self->xbarrier, event->event_id); } } static void _meta_barrier_impl_x11_destroy (MetaBarrierImpl *impl) { MetaBarrierImplX11 *self = META_BARRIER_IMPL_X11 (impl); MetaDisplay *display = self->barrier->priv->display; Display *dpy; if (display == NULL) return; dpy = meta_x11_display_get_xdisplay (display->x11_display); if (!meta_barrier_is_active (self->barrier)) return; XFixesDestroyPointerBarrier (dpy, self->xbarrier); g_hash_table_remove (display->x11_display->xids, &self->xbarrier); self->xbarrier = 0; } MetaBarrierImpl * meta_barrier_impl_x11_new (MetaBarrier *barrier) { MetaBarrierImplX11 *self; MetaDisplay *display = barrier->priv->display; Display *dpy; Window root; unsigned int allowed_motion_dirs; if (display == NULL) { g_warning ("A display must be provided when constructing a barrier."); return NULL; } self = g_object_new (META_TYPE_BARRIER_IMPL_X11, NULL); self->barrier = barrier; dpy = meta_x11_display_get_xdisplay (display->x11_display); root = DefaultRootWindow (dpy); allowed_motion_dirs = meta_border_get_allows_directions (&barrier->priv->border); self->xbarrier = XFixesCreatePointerBarrier (dpy, root, barrier->priv->border.line.a.x, barrier->priv->border.line.a.y, barrier->priv->border.line.b.x, barrier->priv->border.line.b.y, allowed_motion_dirs, 0, NULL); g_hash_table_insert (display->x11_display->xids, &self->xbarrier, barrier); return META_BARRIER_IMPL (self); } static void meta_barrier_fire_xevent (MetaBarrier *barrier, XIBarrierEvent *xevent) { MetaBarrierEvent *event = g_slice_new0 (MetaBarrierEvent); event->ref_count = 1; event->event_id = xevent->eventid; event->time = xevent->time; event->dt = xevent->dtime; event->x = xevent->root_x; event->y = xevent->root_y; event->dx = xevent->dx; event->dy = xevent->dy; event->released = (xevent->flags & XIBarrierPointerReleased) != 0; event->grabbed = (xevent->flags & XIBarrierDeviceIsGrabbed) != 0; switch (xevent->evtype) { case XI_BarrierHit: _meta_barrier_emit_hit_signal (barrier, event); break; case XI_BarrierLeave: _meta_barrier_emit_left_signal (barrier, event); break; default: g_assert_not_reached (); } meta_barrier_event_unref (event); } gboolean meta_x11_display_process_barrier_xevent (MetaX11Display *x11_display, XIEvent *event) { MetaBarrier *barrier; XIBarrierEvent *xev; if (event == NULL) return FALSE; switch (event->evtype) { case XI_BarrierHit: case XI_BarrierLeave: break; default: return FALSE; } xev = (XIBarrierEvent *) event; barrier = g_hash_table_lookup (x11_display->xids, &xev->barrier); if (barrier != NULL) { meta_barrier_fire_xevent (barrier, xev); return TRUE; } return FALSE; } static void meta_barrier_impl_x11_class_init (MetaBarrierImplX11Class *klass) { MetaBarrierImplClass *impl_class = META_BARRIER_IMPL_CLASS (klass); impl_class->is_active = _meta_barrier_impl_x11_is_active; impl_class->release = _meta_barrier_impl_x11_release; impl_class->destroy = _meta_barrier_impl_x11_destroy; } static void meta_barrier_impl_x11_init (MetaBarrierImplX11 *self) { } muffin-6.4.1/src/backends/x11/meta-xkb-a11y-x11.h0000664000175000017500000000232514723361714017757 0ustar fabiofabio/* * * Copyright © 2001 Ximian, Inc. * Copyright (C) 2007 William Jon McCann * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_XKB_A11Y_X11_H #define META_XKB_A11Y_X11_H #include #include "clutter/clutter.h" void meta_seat_x11_apply_kbd_a11y_settings (ClutterSeat *seat, ClutterKbdA11ySettings *kbd_a11y_settings); gboolean meta_seat_x11_a11y_init (ClutterSeat *seat); #endif /* META_XKB_A11Y_X11_H */ muffin-6.4.1/src/backends/x11/meta-keymap-x11.h0000664000175000017500000000544314723361714017714 0ustar fabiofabio/* * Copyright (C) 2009 Intel Corp. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Emmanuele Bassi */ #ifndef META_KEYMAP_X11_H #define META_KEYMAP_X11_H #include #include #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_KEYMAP_X11 (meta_keymap_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaKeymapX11, meta_keymap_x11, META, KEYMAP_X11, ClutterKeymap) int meta_keymap_x11_get_key_group (MetaKeymapX11 *keymap, ClutterModifierType state); int meta_keymap_x11_translate_key_state (MetaKeymapX11 *keymap, guint hardware_keycode, ClutterModifierType *modifier_state_p, ClutterModifierType *mods_p); gboolean meta_keymap_x11_get_is_modifier (MetaKeymapX11 *keymap, int keycode); gboolean meta_keymap_x11_keycode_for_keyval (MetaKeymapX11 *keymap_x11, guint keyval, guint *keycode_out, guint *level_out); void meta_keymap_x11_latch_modifiers (MetaKeymapX11 *keymap_x11, uint32_t level, gboolean enable); gboolean meta_keymap_x11_reserve_keycode (MetaKeymapX11 *keymap_x11, guint keyval, guint *keycode_out); void meta_keymap_x11_release_keycode_if_needed (MetaKeymapX11 *keymap_x11, guint keycode); gboolean meta_keymap_x11_handle_event (MetaKeymapX11 *keymap_x11, XEvent *xevent); G_END_DECLS #endif /* META_KEYMAP_X11_H */ muffin-6.4.1/src/backends/meta-idle-monitor-private.h0000664000175000017500000000331414723361714021453 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Adapted from gnome-session/gnome-session/gs-idle-monitor.c and * from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c */ #ifndef META_IDLE_MONITOR_PRIVATE_H #define META_IDLE_MONITOR_PRIVATE_H #include "core/display-private.h" #include "meta/meta-idle-monitor.h" typedef struct { MetaIdleMonitor *monitor; guint id; MetaIdleMonitorWatchFunc callback; gpointer user_data; GDestroyNotify notify; guint64 timeout_msec; int idle_source_id; GSource *timeout_source; } MetaIdleMonitorWatch; struct _MetaIdleMonitor { GObject parent_instance; GDBusProxy *session_proxy; gboolean inhibited; GHashTable *watches; ClutterInputDevice *device; guint64 last_event_time; }; struct _MetaIdleMonitorClass { GObjectClass parent_class; }; void meta_idle_monitor_reset_idletime (MetaIdleMonitor *monitor); #endif /* META_IDLE_MONITOR_PRIVATE_H */ muffin-6.4.1/src/backends/meta-remote-desktop-session.c0000664000175000017500000007046514723361714022032 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-remote-desktop-session.h" #include #include #include #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-remote-access-controller-private.h" #include "backends/x11/meta-backend-x11.h" #include "cogl/cogl.h" #include "meta/meta-backend.h" #include "meta-dbus-remote-desktop.h" #define META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/org/cinnamon/Muffin/RemoteDesktop/Session" enum _MetaRemoteDesktopNotifyAxisFlags { META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_FINISH = 1 << 0, } MetaRemoteDesktopNotifyAxisFlags; struct _MetaRemoteDesktopSession { MetaDBusRemoteDesktopSessionSkeleton parent; char *peer_name; char *session_id; char *object_path; MetaScreenCastSession *screen_cast_session; gulong screen_cast_session_closed_handler_id; guint started : 1; ClutterVirtualInputDevice *virtual_pointer; ClutterVirtualInputDevice *virtual_keyboard; ClutterVirtualInputDevice *virtual_touchscreen; MetaRemoteDesktopSessionHandle *handle; }; static void meta_remote_desktop_session_init_iface (MetaDBusRemoteDesktopSessionIface *iface); static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaRemoteDesktopSession, meta_remote_desktop_session, META_DBUS_TYPE_REMOTE_DESKTOP_SESSION_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_REMOTE_DESKTOP_SESSION, meta_remote_desktop_session_init_iface) G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION, meta_dbus_session_init_iface)) struct _MetaRemoteDesktopSessionHandle { MetaRemoteAccessHandle parent; MetaRemoteDesktopSession *session; }; G_DEFINE_TYPE (MetaRemoteDesktopSessionHandle, meta_remote_desktop_session_handle, META_TYPE_REMOTE_ACCESS_HANDLE) static MetaRemoteDesktopSessionHandle * meta_remote_desktop_session_handle_new (MetaRemoteDesktopSession *session); static gboolean meta_remote_desktop_session_is_running (MetaRemoteDesktopSession *session) { return !!session->virtual_pointer; } static void init_remote_access_handle (MetaRemoteDesktopSession *session) { MetaBackend *backend = meta_get_backend (); MetaRemoteAccessController *remote_access_controller; MetaRemoteAccessHandle *remote_access_handle; session->handle = meta_remote_desktop_session_handle_new (session); remote_access_controller = meta_backend_get_remote_access_controller (backend); remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle); meta_remote_access_controller_notify_new_handle (remote_access_controller, remote_access_handle); } static gboolean meta_remote_desktop_session_start (MetaRemoteDesktopSession *session, GError **error) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); g_assert (!session->started); if (session->screen_cast_session) { if (!meta_screen_cast_session_start (session->screen_cast_session, error)) return FALSE; } session->virtual_pointer = clutter_seat_create_virtual_device (seat, CLUTTER_POINTER_DEVICE); session->virtual_keyboard = clutter_seat_create_virtual_device (seat, CLUTTER_KEYBOARD_DEVICE); session->virtual_touchscreen = clutter_seat_create_virtual_device (seat, CLUTTER_TOUCHSCREEN_DEVICE); init_remote_access_handle (session); session->started = TRUE; return TRUE; } void meta_remote_desktop_session_close (MetaRemoteDesktopSession *session) { MetaDBusRemoteDesktopSession *skeleton = META_DBUS_REMOTE_DESKTOP_SESSION (session); session->started = FALSE; if (session->screen_cast_session) { g_clear_signal_handler (&session->screen_cast_session_closed_handler_id, session->screen_cast_session); meta_screen_cast_session_close (session->screen_cast_session); session->screen_cast_session = NULL; } g_clear_object (&session->virtual_pointer); g_clear_object (&session->virtual_keyboard); g_clear_object (&session->virtual_touchscreen); meta_dbus_session_notify_closed (META_DBUS_SESSION (session)); meta_dbus_remote_desktop_session_emit_closed (skeleton); g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session)); if (session->handle) { MetaRemoteAccessHandle *remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle); meta_remote_access_handle_notify_stopped (remote_access_handle); } g_object_unref (session); } char * meta_remote_desktop_session_get_object_path (MetaRemoteDesktopSession *session) { return session->object_path; } char * meta_remote_desktop_session_get_session_id (MetaRemoteDesktopSession *session) { return session->session_id; } static void on_screen_cast_session_closed (MetaScreenCastSession *screen_cast_session, MetaRemoteDesktopSession *session) { session->screen_cast_session = NULL; meta_remote_desktop_session_close (session); } gboolean meta_remote_desktop_session_register_screen_cast (MetaRemoteDesktopSession *session, MetaScreenCastSession *screen_cast_session, GError **error) { if (session->screen_cast_session) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Remote desktop session already have an associated " "screen cast session"); return FALSE; } session->screen_cast_session = screen_cast_session; session->screen_cast_session_closed_handler_id = g_signal_connect (screen_cast_session, "session-closed", G_CALLBACK (on_screen_cast_session_closed), session); return TRUE; } MetaRemoteDesktopSession * meta_remote_desktop_session_new (MetaRemoteDesktop *remote_desktop, const char *peer_name, GError **error) { GDBusInterfaceSkeleton *interface_skeleton; MetaRemoteDesktopSession *session; GDBusConnection *connection; session = g_object_new (META_TYPE_REMOTE_DESKTOP_SESSION, NULL); session->peer_name = g_strdup (peer_name); interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); connection = meta_remote_desktop_get_connection (remote_desktop); if (!g_dbus_interface_skeleton_export (interface_skeleton, connection, session->object_path, error)) { g_object_unref (session); return NULL; } return session; } static gboolean check_permission (MetaRemoteDesktopSession *session, GDBusMethodInvocation *invocation) { return g_strcmp0 (session->peer_name, g_dbus_method_invocation_get_sender (invocation)) == 0; } static gboolean meta_remote_desktop_session_check_can_notify (MetaRemoteDesktopSession *session, GDBusMethodInvocation *invocation) { if (!session->started) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Session not started"); return FALSE; } if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return FALSE; } return TRUE; } static gboolean handle_start (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); GError *error = NULL; if (session->started) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Already started"); return TRUE; } if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } if (!meta_remote_desktop_session_start (session, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to start remote desktop: %s", error->message); g_error_free (error); meta_remote_desktop_session_close (session); return TRUE; } meta_dbus_remote_desktop_session_complete_start (skeleton, invocation); return TRUE; } static gboolean handle_stop (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); if (!session->started) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Session not started"); return TRUE; } if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } meta_remote_desktop_session_close (session); meta_dbus_remote_desktop_session_complete_stop (skeleton, invocation); return TRUE; } static gboolean handle_notify_keyboard_keycode (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, unsigned int keycode, gboolean pressed) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterKeyState state; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (pressed) state = CLUTTER_KEY_STATE_PRESSED; else state = CLUTTER_KEY_STATE_RELEASED; clutter_virtual_input_device_notify_key (session->virtual_keyboard, CLUTTER_CURRENT_TIME, keycode, state); meta_dbus_remote_desktop_session_complete_notify_keyboard_keycode (skeleton, invocation); return TRUE; } static gboolean handle_notify_keyboard_keysym (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, unsigned int keysym, gboolean pressed) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterKeyState state; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (pressed) state = CLUTTER_KEY_STATE_PRESSED; else state = CLUTTER_KEY_STATE_RELEASED; clutter_virtual_input_device_notify_keyval (session->virtual_keyboard, CLUTTER_CURRENT_TIME, keysym, state); meta_dbus_remote_desktop_session_complete_notify_keyboard_keysym (skeleton, invocation); return TRUE; } /* Translation taken from the clutter evdev backend. */ static int translate_to_clutter_button (int button) { switch (button) { case BTN_LEFT: return CLUTTER_BUTTON_PRIMARY; case BTN_RIGHT: return CLUTTER_BUTTON_SECONDARY; case BTN_MIDDLE: return CLUTTER_BUTTON_MIDDLE; default: /* * For compatibility reasons, all additional buttons go after the old * 4-7 scroll ones. */ return button - (BTN_LEFT - 1) + 4; } } static gboolean handle_notify_pointer_button (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, int button_code, gboolean pressed) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); uint32_t button; ClutterButtonState state; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; button = translate_to_clutter_button (button_code); if (pressed) state = CLUTTER_BUTTON_STATE_PRESSED; else state = CLUTTER_BUTTON_STATE_RELEASED; clutter_virtual_input_device_notify_button (session->virtual_pointer, CLUTTER_CURRENT_TIME, button, state); meta_dbus_remote_desktop_session_complete_notify_pointer_button (skeleton, invocation); return TRUE; } static gboolean handle_notify_pointer_axis (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, double dx, double dy, uint32_t flags) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterScrollFinishFlags finish_flags = CLUTTER_SCROLL_FINISHED_NONE; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (flags & META_REMOTE_DESKTOP_NOTIFY_AXIS_FLAGS_FINISH) { finish_flags |= (CLUTTER_SCROLL_FINISHED_HORIZONTAL | CLUTTER_SCROLL_FINISHED_VERTICAL); } clutter_virtual_input_device_notify_scroll_continuous (session->virtual_pointer, CLUTTER_CURRENT_TIME, dx, dy, CLUTTER_SCROLL_SOURCE_FINGER, finish_flags); meta_dbus_remote_desktop_session_complete_notify_pointer_axis (skeleton, invocation); return TRUE; } static ClutterScrollDirection discrete_steps_to_scroll_direction (unsigned int axis, int steps) { if (axis == 0 && steps < 0) return CLUTTER_SCROLL_UP; if (axis == 0 && steps > 0) return CLUTTER_SCROLL_DOWN; if (axis == 1 && steps < 0) return CLUTTER_SCROLL_LEFT; if (axis == 1 && steps > 0) return CLUTTER_SCROLL_RIGHT; g_assert_not_reached (); return 0; } static gboolean handle_notify_pointer_axis_discrete (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, unsigned int axis, int steps) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); ClutterScrollDirection direction; int step_count; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (axis > 1) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid axis value"); return TRUE; } if (steps == 0) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Invalid axis steps value"); return TRUE; } /* * We don't have the actual scroll source, but only know they should be * considered as discrete steps. The device that produces such scroll events * is the scroll wheel, so pretend that is the scroll source. */ direction = discrete_steps_to_scroll_direction (axis, steps); for (step_count = 0; step_count < abs (steps); step_count++) clutter_virtual_input_device_notify_discrete_scroll (session->virtual_pointer, CLUTTER_CURRENT_TIME, direction, CLUTTER_SCROLL_SOURCE_WHEEL); meta_dbus_remote_desktop_session_complete_notify_pointer_axis_discrete (skeleton, invocation); return TRUE; } static gboolean handle_notify_pointer_motion_relative (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, double dx, double dy) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; clutter_virtual_input_device_notify_relative_motion (session->virtual_pointer, CLUTTER_CURRENT_TIME, dx, dy); meta_dbus_remote_desktop_session_complete_notify_pointer_motion_relative (skeleton, invocation); return TRUE; } static gboolean handle_notify_pointer_motion_absolute (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, const char *stream_path, double x, double y) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); MetaScreenCastStream *stream; double abs_x, abs_y; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (!session->screen_cast_session) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No screen cast active"); return TRUE; } stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); if (!stream) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown stream"); return TRUE; } meta_screen_cast_stream_transform_position (stream, x, y, &abs_x, &abs_y); clutter_virtual_input_device_notify_absolute_motion (session->virtual_pointer, CLUTTER_CURRENT_TIME, abs_x, abs_y); meta_dbus_remote_desktop_session_complete_notify_pointer_motion_absolute (skeleton, invocation); return TRUE; } static gboolean handle_notify_touch_down (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, const char *stream_path, unsigned int slot, double x, double y) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); MetaScreenCastStream *stream; double abs_x, abs_y; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (!session->screen_cast_session) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No screen cast active"); return TRUE; } stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); if (!stream) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown stream"); return TRUE; } meta_screen_cast_stream_transform_position (stream, x, y, &abs_x, &abs_y); clutter_virtual_input_device_notify_touch_down (session->virtual_touchscreen, CLUTTER_CURRENT_TIME, slot, abs_x, abs_y); meta_dbus_remote_desktop_session_complete_notify_touch_down (skeleton, invocation); return TRUE; } static gboolean handle_notify_touch_motion (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, const char *stream_path, unsigned int slot, double x, double y) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); MetaScreenCastStream *stream; double abs_x, abs_y; if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; if (!session->screen_cast_session) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "No screen cast active"); return TRUE; } stream = meta_screen_cast_session_get_stream (session->screen_cast_session, stream_path); if (!stream) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown stream"); return TRUE; } meta_screen_cast_stream_transform_position (stream, x, y, &abs_x, &abs_y); clutter_virtual_input_device_notify_touch_motion (session->virtual_touchscreen, CLUTTER_CURRENT_TIME, slot, abs_x, abs_y); meta_dbus_remote_desktop_session_complete_notify_touch_motion (skeleton, invocation); return TRUE; } static gboolean handle_notify_touch_up (MetaDBusRemoteDesktopSession *skeleton, GDBusMethodInvocation *invocation, unsigned int slot) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (skeleton); if (!meta_remote_desktop_session_check_can_notify (session, invocation)) return TRUE; clutter_virtual_input_device_notify_touch_up (session->virtual_touchscreen, CLUTTER_CURRENT_TIME, slot); meta_dbus_remote_desktop_session_complete_notify_touch_up (skeleton, invocation); return TRUE; } static void meta_remote_desktop_session_init_iface (MetaDBusRemoteDesktopSessionIface *iface) { iface->handle_start = handle_start; iface->handle_stop = handle_stop; iface->handle_notify_keyboard_keycode = handle_notify_keyboard_keycode; iface->handle_notify_keyboard_keysym = handle_notify_keyboard_keysym; iface->handle_notify_pointer_button = handle_notify_pointer_button; iface->handle_notify_pointer_axis = handle_notify_pointer_axis; iface->handle_notify_pointer_axis_discrete = handle_notify_pointer_axis_discrete; iface->handle_notify_pointer_motion_relative = handle_notify_pointer_motion_relative; iface->handle_notify_pointer_motion_absolute = handle_notify_pointer_motion_absolute; iface->handle_notify_touch_down = handle_notify_touch_down; iface->handle_notify_touch_motion = handle_notify_touch_motion; iface->handle_notify_touch_up = handle_notify_touch_up; } static void meta_remote_desktop_session_client_vanished (MetaDbusSession *dbus_session) { meta_remote_desktop_session_close (META_REMOTE_DESKTOP_SESSION (dbus_session)); } static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { iface->client_vanished = meta_remote_desktop_session_client_vanished; } static void meta_remote_desktop_session_finalize (GObject *object) { MetaRemoteDesktopSession *session = META_REMOTE_DESKTOP_SESSION (object); g_assert (!meta_remote_desktop_session_is_running (session)); g_clear_object (&session->handle); g_free (session->peer_name); g_free (session->session_id); g_free (session->object_path); G_OBJECT_CLASS (meta_remote_desktop_session_parent_class)->finalize (object); } static void meta_remote_desktop_session_init (MetaRemoteDesktopSession *session) { MetaDBusRemoteDesktopSession *skeleton = META_DBUS_REMOTE_DESKTOP_SESSION (session); GRand *rand; static unsigned int global_session_number = 0; rand = g_rand_new (); session->session_id = meta_generate_random_id (rand, 32); g_rand_free (rand); meta_dbus_remote_desktop_session_set_session_id (skeleton, session->session_id); session->object_path = g_strdup_printf (META_REMOTE_DESKTOP_SESSION_DBUS_PATH "/u%u", ++global_session_number); } static void meta_remote_desktop_session_class_init (MetaRemoteDesktopSessionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_remote_desktop_session_finalize; } static MetaRemoteDesktopSessionHandle * meta_remote_desktop_session_handle_new (MetaRemoteDesktopSession *session) { MetaRemoteDesktopSessionHandle *handle; handle = g_object_new (META_TYPE_REMOTE_DESKTOP_SESSION_HANDLE, NULL); handle->session = session; return handle; } static void meta_remote_desktop_session_handle_stop (MetaRemoteAccessHandle *handle) { MetaRemoteDesktopSession *session; session = META_REMOTE_DESKTOP_SESSION_HANDLE (handle)->session; if (!session) return; meta_remote_desktop_session_close (session); } static void meta_remote_desktop_session_handle_init (MetaRemoteDesktopSessionHandle *handle) { } static void meta_remote_desktop_session_handle_class_init (MetaRemoteDesktopSessionHandleClass *klass) { MetaRemoteAccessHandleClass *remote_access_handle_class = META_REMOTE_ACCESS_HANDLE_CLASS (klass); remote_access_handle_class->stop = meta_remote_desktop_session_handle_stop; } muffin-6.4.1/src/backends/meta-screen-cast-monitor-stream.h0000664000175000017500000000432514723361714022571 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_MONITOR_STREAM_H #define META_SCREEN_CAST_MONITOR_STREAM_H #include #include "backends/meta-monitor-manager-private.h" #include "backends/meta-screen-cast-stream.h" #include "backends/meta-screen-cast.h" #define META_TYPE_SCREEN_CAST_MONITOR_STREAM (meta_screen_cast_monitor_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastMonitorStream, meta_screen_cast_monitor_stream, META, SCREEN_CAST_MONITOR_STREAM, MetaScreenCastStream) MetaScreenCastMonitorStream * meta_screen_cast_monitor_stream_new (MetaScreenCastSession *session, GDBusConnection *connection, MetaMonitor *monitor, ClutterStage *stage, MetaScreenCastCursorMode cursor_mode, GError **error); ClutterStage * meta_screen_cast_monitor_stream_get_stage (MetaScreenCastMonitorStream *monitor_stream); MetaMonitor * meta_screen_cast_monitor_stream_get_monitor (MetaScreenCastMonitorStream *monitor_stream); #endif /* META_SCREEN_CAST_MONITOR_STREAM_H */ muffin-6.4.1/src/backends/meta-logical-monitor.h0000664000175000017500000001033014723361714020474 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_LOGICAL_MONITOR_H #define META_LOGICAL_MONITOR_H #include #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-manager-private.h" #include "core/util-private.h" #include "meta/boxes.h" #define META_MAX_OUTPUTS_PER_MONITOR 4 struct _MetaLogicalMonitor { GObject parent; int number; MetaRectangle rect; gboolean is_primary; gboolean is_presentation; /* XXX: not yet used */ gboolean in_fullscreen; float scale; MetaMonitorTransform transform; /* The primary or first output for this monitor, 0 if we can't figure out. It can be matched to a winsys_id of a MetaOutput. This is used as an opaque token on reconfiguration when switching from clone to extened, to decide on what output the windows should go next (it's an attempt to keep windows on the same monitor, and preferably on the primary one). */ uint64_t winsys_id; GList *monitors; }; #define META_TYPE_LOGICAL_MONITOR (meta_logical_monitor_get_type ()) G_DECLARE_FINAL_TYPE (MetaLogicalMonitor, meta_logical_monitor, META, LOGICAL_MONITOR, GObject) typedef void (* MetaLogicalMonitorCrtcFunc) (MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor, MetaOutput *output, MetaCrtc *crtc, gpointer user_data); MetaLogicalMonitor * meta_logical_monitor_new (MetaMonitorManager *monitor_manager, MetaLogicalMonitorConfig *logical_monitor_config, int monitor_number); MetaLogicalMonitor * meta_logical_monitor_new_derived (MetaMonitorManager *monitor_manager, MetaMonitor *monitor, MetaRectangle *layout, float scale, int monitor_number); void meta_logical_monitor_add_monitor (MetaLogicalMonitor *logical_monitor, MetaMonitor *monitor); META_EXPORT_TEST gboolean meta_logical_monitor_is_primary (MetaLogicalMonitor *logical_monitor); void meta_logical_monitor_make_primary (MetaLogicalMonitor *logical_monitor); float meta_logical_monitor_get_scale (MetaLogicalMonitor *logical_monitor); MetaMonitorTransform meta_logical_monitor_get_transform (MetaLogicalMonitor *logical_monitor); MetaRectangle meta_logical_monitor_get_layout (MetaLogicalMonitor *logical_monitor); META_EXPORT_TEST GList * meta_logical_monitor_get_monitors (MetaLogicalMonitor *logical_monitor); gboolean meta_logical_monitor_has_neighbor (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitor *neighbor, MetaDisplayDirection neighbor_dir); void meta_logical_monitor_foreach_crtc (MetaLogicalMonitor *logical_monitor, MetaLogicalMonitorCrtcFunc func, gpointer user_data); #endif /* META_LOGICAL_MONITOR_H */ muffin-6.4.1/src/backends/meta-input-mapper-private.h0000664000175000017500000000355214723361714021476 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_INPUT_MAPPER_H #define META_INPUT_MAPPER_H #include #include "meta-monitor-manager-private.h" #define META_TYPE_INPUT_MAPPER (meta_input_mapper_get_type ()) G_DECLARE_FINAL_TYPE (MetaInputMapper, meta_input_mapper, META, INPUT_MAPPER, GObject) MetaInputMapper * meta_input_mapper_new (void); void meta_input_mapper_add_device (MetaInputMapper *mapper, ClutterInputDevice *device, gboolean builtin); void meta_input_mapper_remove_device (MetaInputMapper *mapper, ClutterInputDevice *device); ClutterInputDevice * meta_input_mapper_get_logical_monitor_device (MetaInputMapper *mapper, MetaLogicalMonitor *logical_monitor, ClutterInputDeviceType device_type); MetaLogicalMonitor * meta_input_mapper_get_device_logical_monitor (MetaInputMapper *mapper, ClutterInputDevice *device); #endif /* META_INPUT_MAPPER_H */ muffin-6.4.1/src/backends/meta-screen-cast-stream-src.h0000664000175000017500000001222714723361714021671 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_STREAM_SRC_H #define META_SCREEN_CAST_STREAM_SRC_H #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-cursor.h" #include "backends/meta-renderer.h" #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/boxes.h" typedef struct _MetaScreenCastStream MetaScreenCastStream; typedef enum _MetaScreenCastRecordFlag { META_SCREEN_CAST_RECORD_FLAG_NONE = 0, META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY = 1 << 0, } MetaScreenCastRecordFlag; #define META_TYPE_SCREEN_CAST_STREAM_SRC (meta_screen_cast_stream_src_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaScreenCastStreamSrc, meta_screen_cast_stream_src, META, SCREEN_CAST_STREAM_SRC, GObject) struct _MetaScreenCastStreamSrcClass { GObjectClass parent_class; void (* get_specs) (MetaScreenCastStreamSrc *src, int *width, int *height, float *frame_rate); void (* enable) (MetaScreenCastStreamSrc *src); void (* disable) (MetaScreenCastStreamSrc *src); gboolean (* record_to_buffer) (MetaScreenCastStreamSrc *src, uint8_t *data, GError **error); gboolean (* record_to_framebuffer) (MetaScreenCastStreamSrc *src, CoglFramebuffer *framebuffer, GError **error); void (* record_follow_up) (MetaScreenCastStreamSrc *src); gboolean (* get_videocrop) (MetaScreenCastStreamSrc *src, MetaRectangle *crop_rect); void (* set_cursor_metadata) (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor); }; void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, MetaScreenCastRecordFlag flags); gboolean meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src); MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src); gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src, CoglTexture *cursor_texture, float scale, uint8_t *data, GError **error); void meta_screen_cast_stream_src_unset_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor); void meta_screen_cast_stream_src_set_cursor_position_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, int x, int y); void meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, int x, int y); void meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, MetaCursorSprite *cursor_sprite, int x, int y, float scale); #endif /* META_SCREEN_CAST_STREAM_SRC_H */ muffin-6.4.1/src/backends/meta-cursor-renderer.h0000664000175000017500000000651514723361714020530 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_CURSOR_RENDERER_H #define META_CURSOR_RENDERER_H #include #include "backends/meta-backend-types.h" #include "backends/meta-cursor.h" #define META_TYPE_HW_CURSOR_INHIBITOR (meta_hw_cursor_inhibitor_get_type ()) G_DECLARE_INTERFACE (MetaHwCursorInhibitor, meta_hw_cursor_inhibitor, META, HW_CURSOR_INHIBITOR, GObject) struct _MetaHwCursorInhibitorInterface { GTypeInterface parent_iface; gboolean (* is_cursor_sprite_inhibited) (MetaHwCursorInhibitor *inhibitor, MetaCursorSprite *cursor_sprite); }; #define META_TYPE_CURSOR_RENDERER (meta_cursor_renderer_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaCursorRenderer, meta_cursor_renderer, META, CURSOR_RENDERER, GObject); struct _MetaCursorRendererClass { GObjectClass parent_class; gboolean (* update_cursor) (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); }; MetaCursorRenderer * meta_cursor_renderer_new (void); void meta_cursor_renderer_set_cursor (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); void meta_cursor_renderer_set_position (MetaCursorRenderer *renderer, float x, float y); graphene_point_t meta_cursor_renderer_get_position (MetaCursorRenderer *renderer); void meta_cursor_renderer_force_update (MetaCursorRenderer *renderer); MetaCursorSprite * meta_cursor_renderer_get_cursor (MetaCursorRenderer *renderer); void meta_cursor_renderer_add_hw_cursor_inhibitor (MetaCursorRenderer *renderer, MetaHwCursorInhibitor *inhibitor); void meta_cursor_renderer_remove_hw_cursor_inhibitor (MetaCursorRenderer *renderer, MetaHwCursorInhibitor *inhibitor); gboolean meta_cursor_renderer_is_hw_cursors_inhibited (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); graphene_rect_t meta_cursor_renderer_calculate_rect (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); void meta_cursor_renderer_emit_painted (MetaCursorRenderer *renderer, MetaCursorSprite *cursor_sprite); #endif /* META_CURSOR_RENDERER_H */ muffin-6.4.1/src/backends/meta-screen-cast-window-stream.h0000664000175000017500000000406514723361714022412 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_SCREEN_CAST_WINDOW_STREAM_H #define META_SCREEN_CAST_WINDOW_STREAM_H #include #include "backends/meta-screen-cast-stream.h" #include "meta/window.h" #define META_TYPE_SCREEN_CAST_WINDOW_STREAM (meta_screen_cast_window_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaScreenCastWindowStream, meta_screen_cast_window_stream, META, SCREEN_CAST_WINDOW_STREAM, MetaScreenCastStream) MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session, GDBusConnection *connection, MetaWindow *window, MetaScreenCastCursorMode cursor_mode, GError **error); MetaWindow * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream); int meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream); int meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream); #endif /* META_SCREEN_CAST_WINDOW_STREAM_H */ muffin-6.4.1/src/backends/meta-stage-private.h0000664000175000017500000000517714723361714020165 0ustar fabiofabio/* * Copyright (C) 2012 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_STAGE_PRIVATE_H #define META_STAGE_PRIVATE_H #include "backends/meta-cursor.h" #include "meta/boxes.h" #include "meta/meta-stage.h" #include "meta/types.h" G_BEGIN_DECLS typedef struct _MetaStageWatch MetaStageWatch; typedef struct _MetaOverlay MetaOverlay; typedef enum { META_STAGE_WATCH_BEFORE_PAINT, META_STAGE_WATCH_AFTER_ACTOR_PAINT, META_STAGE_WATCH_AFTER_OVERLAY_PAINT, META_STAGE_WATCH_AFTER_PAINT, } MetaStageWatchPhase; typedef void (* MetaStageWatchFunc) (MetaStage *stage, ClutterStageView *view, ClutterPaintContext *paint_context, gpointer user_data); ClutterActor *meta_stage_new (MetaBackend *backend); MetaOverlay *meta_stage_create_cursor_overlay (MetaStage *stage); void meta_stage_remove_cursor_overlay (MetaStage *stage, MetaOverlay *overlay); void meta_stage_update_cursor_overlay (MetaStage *stage, MetaOverlay *overlay, CoglTexture *texture, graphene_rect_t *rect); void meta_stage_set_active (MetaStage *stage, gboolean is_active); MetaStageWatch * meta_stage_watch_view (MetaStage *stage, ClutterStageView *view, MetaStageWatchPhase watch_mode, MetaStageWatchFunc callback, gpointer user_data); void meta_stage_remove_watch (MetaStage *stage, MetaStageWatch *watch); G_END_DECLS #endif /* META_STAGE_PRIVATE_H */ muffin-6.4.1/src/backends/meta-renderer-view.c0000664000175000017500000001375014723361714020157 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /** * SECTION:meta-renderer-view * @title: MetaRendererView * @short_description: Renders (a part of) the global stage. * * A MetaRendererView object is responsible for rendering (a part of) the * global stage, or more precisely: the part that matches what can be seen on a * #MetaLogicalMonitor. By splitting up the rendering into different parts and * attaching it to a #MetaLogicalMonitor, we can do the rendering so that each * renderer view is responsible for applying the right #MetaMonitorTransform * and the right scaling. */ #include "config.h" #include "backends/meta-renderer-view.h" #include "backends/meta-renderer.h" #include "clutter/clutter-mutter.h" enum { PROP_0, PROP_TRANSFORM, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _MetaRendererView { ClutterStageViewCogl parent; MetaMonitorTransform transform; }; G_DEFINE_TYPE (MetaRendererView, meta_renderer_view, CLUTTER_TYPE_STAGE_VIEW_COGL) MetaMonitorTransform meta_renderer_view_get_transform (MetaRendererView *view) { return view->transform; } static void meta_renderer_view_get_offscreen_transformation_matrix (ClutterStageView *view, CoglMatrix *matrix) { MetaRendererView *renderer_view = META_RENDERER_VIEW (view); cogl_matrix_init_identity (matrix); switch (renderer_view->transform) { case META_MONITOR_TRANSFORM_NORMAL: break; case META_MONITOR_TRANSFORM_90: cogl_matrix_rotate (matrix, 90, 0, 0, 1); cogl_matrix_translate (matrix, 0, -1, 0); break; case META_MONITOR_TRANSFORM_180: cogl_matrix_rotate (matrix, 180, 0, 0, 1); cogl_matrix_translate (matrix, -1, -1, 0); break; case META_MONITOR_TRANSFORM_270: cogl_matrix_rotate (matrix, 270, 0, 0, 1); cogl_matrix_translate (matrix, -1, 0, 0); break; case META_MONITOR_TRANSFORM_FLIPPED: cogl_matrix_scale (matrix, -1, 1, 1); cogl_matrix_translate (matrix, -1, 0, 0); break; case META_MONITOR_TRANSFORM_FLIPPED_90: cogl_matrix_scale (matrix, -1, 1, 1); cogl_matrix_rotate (matrix, 90, 0, 0, 1); break; case META_MONITOR_TRANSFORM_FLIPPED_180: cogl_matrix_scale (matrix, -1, 1, 1); cogl_matrix_rotate (matrix, 180, 0, 0, 1); cogl_matrix_translate (matrix, 0, -1, 0); break; case META_MONITOR_TRANSFORM_FLIPPED_270: cogl_matrix_scale (matrix, -1, 1, 1); cogl_matrix_rotate (matrix, 270, 0, 0, 1); cogl_matrix_translate (matrix, -1, -1, 0); break; } } static void meta_renderer_view_setup_offscreen_blit_pipeline (ClutterStageView *view, CoglPipeline *pipeline) { CoglMatrix matrix; meta_renderer_view_get_offscreen_transformation_matrix (view, &matrix); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); } static void meta_renderer_view_set_transform (MetaRendererView *view, MetaMonitorTransform transform) { if (view->transform == transform) return; view->transform = transform; clutter_stage_view_invalidate_offscreen_blit_pipeline (CLUTTER_STAGE_VIEW (view)); } static void meta_renderer_view_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaRendererView *view = META_RENDERER_VIEW (object); switch (prop_id) { case PROP_TRANSFORM: g_value_set_uint (value, view->transform); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_view_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaRendererView *view = META_RENDERER_VIEW (object); switch (prop_id) { case PROP_TRANSFORM: meta_renderer_view_set_transform (view, g_value_get_uint (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_renderer_view_init (MetaRendererView *view) { } static void meta_renderer_view_class_init (MetaRendererViewClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterStageViewClass *view_class = CLUTTER_STAGE_VIEW_CLASS (klass); view_class->setup_offscreen_blit_pipeline = meta_renderer_view_setup_offscreen_blit_pipeline; view_class->get_offscreen_transformation_matrix = meta_renderer_view_get_offscreen_transformation_matrix; object_class->get_property = meta_renderer_view_get_property; object_class->set_property = meta_renderer_view_set_property; obj_props[PROP_TRANSFORM] = g_param_spec_uint ("transform", "Transform", "Transform to apply to the view", META_MONITOR_TRANSFORM_NORMAL, META_MONITOR_TRANSFORM_FLIPPED_270, META_MONITOR_TRANSFORM_NORMAL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, PROP_LAST, obj_props); } muffin-6.4.1/src/backends/meta-idle-monitor-dbus.c0000664000175000017500000002203114723361714020726 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Adapted from gnome-session/gnome-session/gs-idle-monitor.c and * from gnome-desktop/libgnome-desktop/gnome-idle-monitor.c */ #include "config.h" #include "backends/meta-idle-monitor-dbus.h" #include "backends/meta-idle-monitor-private.h" #include "clutter/clutter.h" #include "meta/main.h" #include "meta/meta-idle-monitor.h" #include "meta/util.h" #include "meta-dbus-idle-monitor.h" static gboolean handle_get_idletime (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, MetaIdleMonitor *monitor) { guint64 idletime; idletime = meta_idle_monitor_get_idletime (monitor); meta_dbus_idle_monitor_complete_get_idletime (skeleton, invocation, idletime); return TRUE; } static gboolean handle_reset_idletime (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, MetaIdleMonitor *monitor) { if (!g_getenv ("MUTTER_DEBUG_RESET_IDLETIME")) { g_dbus_method_invocation_return_error_literal (invocation, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD, "This method is for testing purposes only. MUTTER_DEBUG_RESET_IDLETIME must be set to use it"); return TRUE; } meta_idle_monitor_reset_idletime (meta_idle_monitor_get_core ()); meta_dbus_idle_monitor_complete_reset_idletime (skeleton, invocation); return TRUE; } typedef struct { MetaDBusIdleMonitor *dbus_monitor; MetaIdleMonitor *monitor; char *dbus_name; guint watch_id; guint name_watcher_id; } DBusWatch; static void destroy_dbus_watch (gpointer data) { DBusWatch *watch = data; g_object_unref (watch->dbus_monitor); g_object_unref (watch->monitor); g_free (watch->dbus_name); g_bus_unwatch_name (watch->name_watcher_id); g_slice_free (DBusWatch, watch); } static void dbus_idle_callback (MetaIdleMonitor *monitor, guint watch_id, gpointer user_data) { DBusWatch *watch = user_data; GDBusInterfaceSkeleton *skeleton = G_DBUS_INTERFACE_SKELETON (watch->dbus_monitor); g_dbus_connection_emit_signal (g_dbus_interface_skeleton_get_connection (skeleton), watch->dbus_name, g_dbus_interface_skeleton_get_object_path (skeleton), "org.cinnamon.Muffin.IdleMonitor", "WatchFired", g_variant_new ("(u)", watch_id), NULL); } static void name_vanished_callback (GDBusConnection *connection, const char *name, gpointer user_data) { DBusWatch *watch = user_data; meta_idle_monitor_remove_watch (watch->monitor, watch->watch_id); } static DBusWatch * make_dbus_watch (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, MetaIdleMonitor *monitor) { DBusWatch *watch; watch = g_slice_new (DBusWatch); watch->dbus_monitor = g_object_ref (skeleton); watch->monitor = g_object_ref (monitor); watch->dbus_name = g_strdup (g_dbus_method_invocation_get_sender (invocation)); watch->name_watcher_id = g_bus_watch_name_on_connection (g_dbus_method_invocation_get_connection (invocation), watch->dbus_name, G_BUS_NAME_WATCHER_FLAGS_NONE, NULL, /* appeared */ name_vanished_callback, watch, NULL); return watch; } static gboolean handle_add_idle_watch (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, guint64 interval, MetaIdleMonitor *monitor) { DBusWatch *watch; watch = make_dbus_watch (skeleton, invocation, monitor); watch->watch_id = meta_idle_monitor_add_idle_watch (monitor, interval, dbus_idle_callback, watch, destroy_dbus_watch); meta_dbus_idle_monitor_complete_add_idle_watch (skeleton, invocation, watch->watch_id); return TRUE; } static gboolean handle_add_user_active_watch (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, MetaIdleMonitor *monitor) { DBusWatch *watch; watch = make_dbus_watch (skeleton, invocation, monitor); watch->watch_id = meta_idle_monitor_add_user_active_watch (monitor, dbus_idle_callback, watch, destroy_dbus_watch); meta_dbus_idle_monitor_complete_add_user_active_watch (skeleton, invocation, watch->watch_id); return TRUE; } static gboolean handle_remove_watch (MetaDBusIdleMonitor *skeleton, GDBusMethodInvocation *invocation, guint id, MetaIdleMonitor *monitor) { meta_idle_monitor_remove_watch (monitor, id); meta_dbus_idle_monitor_complete_remove_watch (skeleton, invocation); return TRUE; } static void create_monitor_skeleton (GDBusObjectManagerServer *manager, MetaIdleMonitor *monitor, const char *path) { MetaDBusIdleMonitor *skeleton; MetaDBusObjectSkeleton *object; skeleton = meta_dbus_idle_monitor_skeleton_new (); g_signal_connect_object (skeleton, "handle-add-idle-watch", G_CALLBACK (handle_add_idle_watch), monitor, 0); g_signal_connect_object (skeleton, "handle-add-user-active-watch", G_CALLBACK (handle_add_user_active_watch), monitor, 0); g_signal_connect_object (skeleton, "handle-remove-watch", G_CALLBACK (handle_remove_watch), monitor, 0); g_signal_connect_object (skeleton, "handle-reset-idletime", G_CALLBACK (handle_reset_idletime), monitor, 0); g_signal_connect_object (skeleton, "handle-get-idletime", G_CALLBACK (handle_get_idletime), monitor, 0); object = meta_dbus_object_skeleton_new (path); meta_dbus_object_skeleton_set_idle_monitor (object, skeleton); g_dbus_object_manager_server_export (manager, G_DBUS_OBJECT_SKELETON (object)); g_object_unref (skeleton); g_object_unref (object); } static void on_bus_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { GDBusObjectManagerServer *manager; MetaIdleMonitor *monitor; char *path; manager = g_dbus_object_manager_server_new ("/org/cinnamon/Muffin/IdleMonitor"); /* We never clear the core monitor, as that's supposed to cumulate idle times from all devices */ monitor = meta_idle_monitor_get_core (); path = g_strdup ("/org/cinnamon/Muffin/IdleMonitor/Core"); create_monitor_skeleton (manager, monitor, path); g_free (path); g_dbus_object_manager_server_set_connection (manager, connection); } static void on_name_acquired (GDBusConnection *connection, const char *name, gpointer user_data) { meta_verbose ("Acquired name %s\n", name); } static void on_name_lost (GDBusConnection *connection, const char *name, gpointer user_data) { meta_verbose ("Lost or failed to acquire name %s\n", name); } void meta_idle_monitor_init_dbus (void) { static int dbus_name_id; if (dbus_name_id > 0) return; dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION, "org.cinnamon.Muffin.IdleMonitor", G_BUS_NAME_OWNER_FLAGS_ALLOW_REPLACEMENT | (meta_get_replace_current_wm () ? G_BUS_NAME_OWNER_FLAGS_REPLACE : 0), on_bus_acquired, on_name_acquired, on_name_lost, NULL, NULL); } muffin-6.4.1/src/backends/meta-settings-private.h0000664000175000017500000000631314723361714020713 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_SETTINGS_PRIVATE_H #define META_SETTINGS_PRIVATE_H #include #include "meta/meta-settings.h" #include "meta/types.h" #include "core/util-private.h" typedef enum _MetaExperimentalFeature { META_EXPERIMENTAL_FEATURE_NONE = 0, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER = (1 << 0), META_EXPERIMENTAL_FEATURE_KMS_MODIFIERS = (1 << 1), META_EXPERIMENTAL_FEATURE_RT_SCHEDULER = (1 << 2), META_EXPERIMENTAL_FEATURE_AUTOSTART_XWAYLAND = (1 << 3), META_EXPERIMENTAL_FEATURE_X11_RANDR_FRACTIONAL_SCALING = (1 << 4), } MetaExperimentalFeature; typedef enum _MetaX11ScaleMode { META_X11_SCALE_MODE_NONE = 0, META_X11_SCALE_MODE_UP = 1, META_X11_SCALE_MODE_UI_DOWN = 2, } MetaX11ScaleMode; #define META_TYPE_SETTINGS (meta_settings_get_type ()) G_DECLARE_FINAL_TYPE (MetaSettings, meta_settings, META, SETTINGS, GObject) MetaSettings * meta_settings_new (MetaBackend *backend); void meta_settings_post_init (MetaSettings *settings); void meta_settings_update_ui_scaling_factor (MetaSettings *settings); gboolean meta_settings_get_global_scaling_factor (MetaSettings *settings, int *scaing_factor); double meta_settings_get_font_scaling_factor (MetaSettings *settings); META_EXPORT_TEST gboolean meta_settings_is_experimental_feature_enabled (MetaSettings *settings, MetaExperimentalFeature feature); MetaExperimentalFeature meta_settings_get_experimental_features (MetaSettings *settings); META_EXPORT_TEST void meta_settings_override_experimental_features (MetaSettings *settings); META_EXPORT_TEST void meta_settings_enable_experimental_feature (MetaSettings *settings, MetaExperimentalFeature feature); void meta_settings_get_xwayland_grab_patterns (MetaSettings *settings, GPtrArray **whitelist_patterns, GPtrArray **blacklist_patterns); gboolean meta_settings_are_xwayland_grabs_allowed (MetaSettings *settings); MetaX11ScaleMode meta_settings_get_x11_scale_mode (MetaSettings *settings); void meta_settings_enable_x11_fractional_scaling (MetaSettings *settings, gboolean enabled); #endif /* META_SETTINGS_PRIVATE_H */ muffin-6.4.1/src/backends/meta-screen-cast-session.c0000664000175000017500000004504714723361714021275 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-session.h" #include "backends/meta-backend-private.h" #include "backends/meta-dbus-session-watcher.h" #include "backends/meta-remote-access-controller-private.h" #include "backends/meta-screen-cast-monitor-stream.h" #include "backends/meta-screen-cast-stream.h" #include "backends/meta-screen-cast-window-stream.h" #include "core/display-private.h" #define META_SCREEN_CAST_SESSION_DBUS_PATH "/org/cinnamon/Muffin/ScreenCast/Session" struct _MetaScreenCastSession { MetaDBusScreenCastSessionSkeleton parent; MetaScreenCast *screen_cast; char *peer_name; MetaScreenCastSessionType session_type; char *object_path; GList *streams; MetaScreenCastSessionHandle *handle; gboolean disable_animations; }; static void meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface); static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastSession, meta_screen_cast_session, META_DBUS_TYPE_SCREEN_CAST_SESSION_SKELETON, G_IMPLEMENT_INTERFACE (META_DBUS_TYPE_SCREEN_CAST_SESSION, meta_screen_cast_session_init_iface) G_IMPLEMENT_INTERFACE (META_TYPE_DBUS_SESSION, meta_dbus_session_init_iface)) struct _MetaScreenCastSessionHandle { MetaRemoteAccessHandle parent; MetaScreenCastSession *session; }; G_DEFINE_TYPE (MetaScreenCastSessionHandle, meta_screen_cast_session_handle, META_TYPE_REMOTE_ACCESS_HANDLE) static MetaScreenCastSessionHandle * meta_screen_cast_session_handle_new (MetaScreenCastSession *session); static void init_remote_access_handle (MetaScreenCastSession *session) { MetaBackend *backend = meta_get_backend (); MetaRemoteAccessController *remote_access_controller; MetaRemoteAccessHandle *remote_access_handle; session->handle = meta_screen_cast_session_handle_new (session); remote_access_controller = meta_backend_get_remote_access_controller (backend); remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle); meta_remote_access_handle_set_disable_animations (remote_access_handle, session->disable_animations); meta_remote_access_controller_notify_new_handle (remote_access_controller, remote_access_handle); } gboolean meta_screen_cast_session_start (MetaScreenCastSession *session, GError **error) { GList *l; for (l = session->streams; l; l = l->next) { MetaScreenCastStream *stream = l->data; if (!meta_screen_cast_stream_start (stream, error)) return FALSE; } init_remote_access_handle (session); return TRUE; } void meta_screen_cast_session_close (MetaScreenCastSession *session) { MetaDBusScreenCastSession *skeleton = META_DBUS_SCREEN_CAST_SESSION (session); g_list_free_full (session->streams, g_object_unref); meta_dbus_session_notify_closed (META_DBUS_SESSION (session)); switch (session->session_type) { case META_SCREEN_CAST_SESSION_TYPE_NORMAL: meta_dbus_screen_cast_session_emit_closed (skeleton); break; case META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP: break; } g_dbus_interface_skeleton_unexport (G_DBUS_INTERFACE_SKELETON (session)); if (session->handle) { MetaRemoteAccessHandle *remote_access_handle = META_REMOTE_ACCESS_HANDLE (session->handle); meta_remote_access_handle_notify_stopped (remote_access_handle); } g_object_unref (session); } MetaScreenCastStream * meta_screen_cast_session_get_stream (MetaScreenCastSession *session, const char *path) { GList *l; for (l = session->streams; l; l = l->next) { MetaScreenCastStream *stream = l->data; if (g_strcmp0 (meta_screen_cast_stream_get_object_path (stream), path) == 0) return stream; } return NULL; } MetaScreenCast * meta_screen_cast_session_get_screen_cast (MetaScreenCastSession *session) { return session->screen_cast; } void meta_screen_cast_session_set_disable_animations (MetaScreenCastSession *session, gboolean disable_animations) { session->disable_animations = disable_animations; } char * meta_screen_cast_session_get_object_path (MetaScreenCastSession *session) { return session->object_path; } char * meta_screen_cast_session_get_peer_name (MetaScreenCastSession *session) { return session->peer_name; } static gboolean check_permission (MetaScreenCastSession *session, GDBusMethodInvocation *invocation) { return g_strcmp0 (session->peer_name, g_dbus_method_invocation_get_sender (invocation)) == 0; } static gboolean handle_start (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton); GError *error = NULL; if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } switch (session->session_type) { case META_SCREEN_CAST_SESSION_TYPE_NORMAL: break; case META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP: g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Must be started from remote desktop session"); return TRUE; } if (!meta_screen_cast_session_start (session, &error)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to start screen cast: %s", error->message); g_error_free (error); return TRUE; } meta_dbus_screen_cast_session_complete_start (skeleton, invocation); return TRUE; } static gboolean handle_stop (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton); if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } switch (session->session_type) { case META_SCREEN_CAST_SESSION_TYPE_NORMAL: break; case META_SCREEN_CAST_SESSION_TYPE_REMOTE_DESKTOP: g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Must be stopped from remote desktop session"); return TRUE; } meta_screen_cast_session_close (session); meta_dbus_screen_cast_session_complete_stop (skeleton, invocation); return TRUE; } static void on_stream_closed (MetaScreenCastStream *stream, MetaScreenCastSession *session) { meta_screen_cast_session_close (session); } static gboolean is_valid_cursor_mode (MetaScreenCastCursorMode cursor_mode) { switch (cursor_mode) { case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: case META_SCREEN_CAST_CURSOR_MODE_METADATA: return TRUE; } return FALSE; } static gboolean handle_record_monitor (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation, const char *connector, GVariant *properties_variant) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton); GDBusInterfaceSkeleton *interface_skeleton; GDBusConnection *connection; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitor *monitor; MetaScreenCastCursorMode cursor_mode; ClutterStage *stage; GError *error = NULL; MetaScreenCastMonitorStream *monitor_stream; MetaScreenCastStream *stream; char *stream_path; if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton); connection = g_dbus_interface_skeleton_get_connection (interface_skeleton); if (g_str_equal (connector, "")) monitor = meta_monitor_manager_get_primary_monitor (monitor_manager); else monitor = meta_monitor_manager_get_monitor_from_connector (monitor_manager, connector); if (!monitor) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown monitor"); return TRUE; } if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode)) { cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN; } else { if (!is_valid_cursor_mode (cursor_mode)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown cursor mode"); return TRUE; } } stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); monitor_stream = meta_screen_cast_monitor_stream_new (session, connection, monitor, stage, cursor_mode, &error); if (!monitor_stream) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to record monitor: %s", error->message); g_error_free (error); return TRUE; } stream = META_SCREEN_CAST_STREAM (monitor_stream); stream_path = meta_screen_cast_stream_get_object_path (stream); session->streams = g_list_append (session->streams, stream); g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session); meta_dbus_screen_cast_session_complete_record_monitor (skeleton, invocation, stream_path); return TRUE; } static gboolean handle_record_window (MetaDBusScreenCastSession *skeleton, GDBusMethodInvocation *invocation, GVariant *properties_variant) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (skeleton); GDBusInterfaceSkeleton *interface_skeleton; GDBusConnection *connection; MetaWindow *window; MetaScreenCastCursorMode cursor_mode; GError *error = NULL; MetaDisplay *display; GVariant *window_id_variant = NULL; MetaScreenCastWindowStream *window_stream; MetaScreenCastStream *stream; char *stream_path; if (!check_permission (session, invocation)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_ACCESS_DENIED, "Permission denied"); return TRUE; } if (properties_variant) window_id_variant = g_variant_lookup_value (properties_variant, "window-id", G_VARIANT_TYPE ("t")); display = meta_get_display (); if (window_id_variant) { uint64_t window_id; g_variant_get (window_id_variant, "t", &window_id); window = meta_display_get_window_from_id (display, window_id); } else { window = meta_display_get_focus_window (display); } if (!window) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Window not found"); return TRUE; } if (!g_variant_lookup (properties_variant, "cursor-mode", "u", &cursor_mode)) { cursor_mode = META_SCREEN_CAST_CURSOR_MODE_HIDDEN; } else { if (!is_valid_cursor_mode (cursor_mode)) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Unknown cursor mode"); return TRUE; } } interface_skeleton = G_DBUS_INTERFACE_SKELETON (skeleton); connection = g_dbus_interface_skeleton_get_connection (interface_skeleton); window_stream = meta_screen_cast_window_stream_new (session, connection, window, cursor_mode, &error); if (!window_stream) { g_dbus_method_invocation_return_error (invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED, "Failed to record window: %s", error->message); g_error_free (error); return TRUE; } stream = META_SCREEN_CAST_STREAM (window_stream); stream_path = meta_screen_cast_stream_get_object_path (stream); session->streams = g_list_append (session->streams, stream); g_signal_connect (stream, "closed", G_CALLBACK (on_stream_closed), session); meta_dbus_screen_cast_session_complete_record_window (skeleton, invocation, stream_path); return TRUE; } static void meta_screen_cast_session_init_iface (MetaDBusScreenCastSessionIface *iface) { iface->handle_start = handle_start; iface->handle_stop = handle_stop; iface->handle_record_monitor = handle_record_monitor; iface->handle_record_window = handle_record_window; } static void meta_screen_cast_session_client_vanished (MetaDbusSession *dbus_session) { meta_screen_cast_session_close (META_SCREEN_CAST_SESSION (dbus_session)); } static void meta_dbus_session_init_iface (MetaDbusSessionInterface *iface) { iface->client_vanished = meta_screen_cast_session_client_vanished; } MetaScreenCastSession * meta_screen_cast_session_new (MetaScreenCast *screen_cast, MetaScreenCastSessionType session_type, const char *peer_name, GError **error) { GDBusInterfaceSkeleton *interface_skeleton; MetaScreenCastSession *session; GDBusConnection *connection; static unsigned int global_session_number = 0; session = g_object_new (META_TYPE_SCREEN_CAST_SESSION, NULL); session->screen_cast = screen_cast; session->session_type = session_type; session->peer_name = g_strdup (peer_name); session->object_path = g_strdup_printf (META_SCREEN_CAST_SESSION_DBUS_PATH "/u%u", ++global_session_number); interface_skeleton = G_DBUS_INTERFACE_SKELETON (session); connection = meta_screen_cast_get_connection (screen_cast); if (!g_dbus_interface_skeleton_export (interface_skeleton, connection, session->object_path, error)) return NULL; return session; } static void meta_screen_cast_session_finalize (GObject *object) { MetaScreenCastSession *session = META_SCREEN_CAST_SESSION (object); g_clear_object (&session->handle); g_free (session->peer_name); g_free (session->object_path); G_OBJECT_CLASS (meta_screen_cast_session_parent_class)->finalize (object); } static void meta_screen_cast_session_init (MetaScreenCastSession *session) { } static void meta_screen_cast_session_class_init (MetaScreenCastSessionClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_screen_cast_session_finalize; } static MetaScreenCastSessionHandle * meta_screen_cast_session_handle_new (MetaScreenCastSession *session) { MetaScreenCastSessionHandle *handle; handle = g_object_new (META_TYPE_SCREEN_CAST_SESSION_HANDLE, NULL); handle->session = session; return handle; } static void meta_screen_cast_session_handle_stop (MetaRemoteAccessHandle *handle) { MetaScreenCastSession *session; session = META_SCREEN_CAST_SESSION_HANDLE (handle)->session; if (!session) return; meta_screen_cast_session_close (session); } static void meta_screen_cast_session_handle_init (MetaScreenCastSessionHandle *handle) { } static void meta_screen_cast_session_handle_class_init (MetaScreenCastSessionHandleClass *klass) { MetaRemoteAccessHandleClass *remote_access_handle_class = META_REMOTE_ACCESS_HANDLE_CLASS (klass); remote_access_handle_class->stop = meta_screen_cast_session_handle_stop; } muffin-6.4.1/src/backends/meta-screen-cast-stream-src.c0000664000175000017500000011471414723361714021670 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015-2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-stream-src.h" #include #include #include #include #include #include #include #include #include #include "backends/meta-screen-cast-session.h" #include "backends/meta-screen-cast-stream.h" #include "clutter/clutter-mutter.h" #include "core/meta-fraction.h" #include "meta/boxes.h" #define PRIVATE_OWNER_FROM_FIELD(TypeName, field_ptr, field_name) \ (TypeName *)((guint8 *)(field_ptr) - G_PRIVATE_OFFSET (TypeName, field_name)) #define CURSOR_META_SIZE(width, height) \ (sizeof (struct spa_meta_cursor) + \ sizeof (struct spa_meta_bitmap) + width * height * 4) enum { PROP_0, PROP_STREAM, }; enum { READY, CLOSED, N_SIGNALS }; static guint signals[N_SIGNALS]; typedef struct _MetaPipeWireSource { GSource base; MetaScreenCastStreamSrc *src; struct pw_loop *pipewire_loop; } MetaPipeWireSource; typedef struct _MetaScreenCastStreamSrcPrivate { MetaScreenCastStream *stream; struct pw_context *pipewire_context; struct pw_core *pipewire_core; MetaPipeWireSource *pipewire_source; struct spa_hook pipewire_core_listener; gboolean is_enabled; gboolean emit_closed_after_dispatch; struct pw_stream *pipewire_stream; struct spa_hook pipewire_stream_listener; uint32_t node_id; struct spa_video_info_raw video_format; int video_stride; int64_t last_frame_timestamp_us; guint follow_up_frame_source_id; GHashTable *dmabuf_handles; int stream_width; int stream_height; } MetaScreenCastStreamSrcPrivate; static void meta_screen_cast_stream_src_init_initable_iface (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastStreamSrc, meta_screen_cast_stream_src, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, meta_screen_cast_stream_src_init_initable_iface) G_ADD_PRIVATE (MetaScreenCastStreamSrc)) static inline uint32_t us2ms (uint64_t us) { return (uint32_t) (us / 1000); } static void meta_screen_cast_stream_src_get_specs (MetaScreenCastStreamSrc *src, int *width, int *height, float *frame_rate) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); klass->get_specs (src, width, height, frame_rate); } static gboolean meta_screen_cast_stream_src_get_videocrop (MetaScreenCastStreamSrc *src, MetaRectangle *crop_rect) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); if (klass->get_videocrop) return klass->get_videocrop (src, crop_rect); return FALSE; } static gboolean meta_screen_cast_stream_src_record_to_buffer (MetaScreenCastStreamSrc *src, uint8_t *data, GError **error) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); return klass->record_to_buffer (src, data, error); } static gboolean meta_screen_cast_stream_src_record_to_framebuffer (MetaScreenCastStreamSrc *src, CoglFramebuffer *framebuffer, GError **error) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); return klass->record_to_framebuffer (src, framebuffer, error); } static void meta_screen_cast_stream_src_record_follow_up (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); klass->record_follow_up (src); } static void meta_screen_cast_stream_src_set_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) { MetaScreenCastStreamSrcClass *klass = META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src); if (klass->set_cursor_metadata) klass->set_cursor_metadata (src, spa_meta_cursor); } static gboolean draw_cursor_sprite_via_offscreen (MetaScreenCastStreamSrc *src, CoglTexture *cursor_texture, int bitmap_width, int bitmap_height, uint8_t *bitmap_data, GError **error) { MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); MetaScreenCastSession *session = meta_screen_cast_stream_get_session (stream); MetaScreenCast *screen_cast = meta_screen_cast_session_get_screen_cast (session); MetaBackend *backend = meta_screen_cast_get_backend (screen_cast); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglTexture2D *bitmap_texture; CoglOffscreen *offscreen; CoglFramebuffer *fb; CoglPipeline *pipeline; CoglColor clear_color; bitmap_texture = cogl_texture_2d_new_with_size (cogl_context, bitmap_width, bitmap_height); cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (bitmap_texture), FALSE); if (!cogl_texture_allocate (COGL_TEXTURE (bitmap_texture), error)) { cogl_object_unref (bitmap_texture); return FALSE; } offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (bitmap_texture)); fb = COGL_FRAMEBUFFER (offscreen); cogl_object_unref (bitmap_texture); if (!cogl_framebuffer_allocate (fb, error)) { cogl_object_unref (fb); return FALSE; } pipeline = cogl_pipeline_new (cogl_context); cogl_pipeline_set_layer_texture (pipeline, 0, cursor_texture); cogl_pipeline_set_layer_filters (pipeline, 0, COGL_PIPELINE_FILTER_LINEAR, COGL_PIPELINE_FILTER_LINEAR); cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); cogl_framebuffer_clear (fb, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_draw_rectangle (fb, pipeline, -1, 1, 1, -1); cogl_object_unref (pipeline); cogl_framebuffer_read_pixels (fb, 0, 0, bitmap_width, bitmap_height, COGL_PIXEL_FORMAT_RGBA_8888_PRE, bitmap_data); cogl_object_unref (fb); return TRUE; } gboolean meta_screen_cast_stream_src_draw_cursor_into (MetaScreenCastStreamSrc *src, CoglTexture *cursor_texture, float scale, uint8_t *data, GError **error) { int texture_width, texture_height; int width, height; texture_width = cogl_texture_get_width (cursor_texture); texture_height = cogl_texture_get_height (cursor_texture); width = texture_width * scale; height = texture_height * scale; if (texture_width == width && texture_height == height) { cogl_texture_get_data (cursor_texture, COGL_PIXEL_FORMAT_RGBA_8888_PRE, texture_width * 4, data); } else { if (!draw_cursor_sprite_via_offscreen (src, cursor_texture, width, height, data, error)) return FALSE; } return TRUE; } void meta_screen_cast_stream_src_unset_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor) { spa_meta_cursor->id = 0; } void meta_screen_cast_stream_src_set_cursor_position_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, int x, int y) { spa_meta_cursor->id = 1; spa_meta_cursor->position.x = x; spa_meta_cursor->position.y = y; spa_meta_cursor->hotspot.x = 0; spa_meta_cursor->hotspot.y = 0; spa_meta_cursor->bitmap_offset = 0; } void meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, int x, int y) { struct spa_meta_bitmap *spa_meta_bitmap; spa_meta_cursor->id = 1; spa_meta_cursor->position.x = x; spa_meta_cursor->position.y = y; spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor); spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); spa_meta_cursor->hotspot.x = 0; spa_meta_cursor->hotspot.y = 0; *spa_meta_bitmap = (struct spa_meta_bitmap) { 0 }; } void meta_screen_cast_stream_src_set_cursor_sprite_metadata (MetaScreenCastStreamSrc *src, struct spa_meta_cursor *spa_meta_cursor, MetaCursorSprite *cursor_sprite, int x, int y, float scale) { CoglTexture *cursor_texture; struct spa_meta_bitmap *spa_meta_bitmap; int hotspot_x, hotspot_y; int texture_width, texture_height; int bitmap_width, bitmap_height; uint8_t *bitmap_data; GError *error = NULL; cursor_texture = meta_cursor_sprite_get_cogl_texture (cursor_sprite); if (!cursor_texture) { meta_screen_cast_stream_src_set_empty_cursor_sprite_metadata (src, spa_meta_cursor, x, y); return; } spa_meta_cursor->id = 1; spa_meta_cursor->position.x = x; spa_meta_cursor->position.y = y; spa_meta_cursor->bitmap_offset = sizeof (struct spa_meta_cursor); spa_meta_bitmap = SPA_MEMBER (spa_meta_cursor, spa_meta_cursor->bitmap_offset, struct spa_meta_bitmap); spa_meta_bitmap->format = SPA_VIDEO_FORMAT_RGBA; spa_meta_bitmap->offset = sizeof (struct spa_meta_bitmap); meta_cursor_sprite_get_hotspot (cursor_sprite, &hotspot_x, &hotspot_y); spa_meta_cursor->hotspot.x = (int32_t) roundf (hotspot_x * scale); spa_meta_cursor->hotspot.y = (int32_t) roundf (hotspot_y * scale); texture_width = cogl_texture_get_width (cursor_texture); texture_height = cogl_texture_get_height (cursor_texture); bitmap_width = texture_width * scale; bitmap_height = texture_height * scale; spa_meta_bitmap->size.width = bitmap_width; spa_meta_bitmap->size.height = bitmap_height; spa_meta_bitmap->stride = bitmap_width * 4; bitmap_data = SPA_MEMBER (spa_meta_bitmap, spa_meta_bitmap->offset, uint8_t); if (!meta_screen_cast_stream_src_draw_cursor_into (src, cursor_texture, scale, bitmap_data, &error)) { g_warning ("Failed to draw cursor: %s", error->message); g_error_free (error); spa_meta_cursor->id = 0; } } static void add_cursor_metadata (MetaScreenCastStreamSrc *src, struct spa_buffer *spa_buffer) { struct spa_meta_cursor *spa_meta_cursor; spa_meta_cursor = spa_buffer_find_meta_data (spa_buffer, SPA_META_Cursor, sizeof (*spa_meta_cursor)); if (spa_meta_cursor) meta_screen_cast_stream_src_set_cursor_metadata (src, spa_meta_cursor); } static void maybe_record_cursor (MetaScreenCastStreamSrc *src, struct spa_buffer *spa_buffer) { MetaScreenCastStream *stream = meta_screen_cast_stream_src_get_stream (src); switch (meta_screen_cast_stream_get_cursor_mode (stream)) { case META_SCREEN_CAST_CURSOR_MODE_HIDDEN: case META_SCREEN_CAST_CURSOR_MODE_EMBEDDED: return; case META_SCREEN_CAST_CURSOR_MODE_METADATA: add_cursor_metadata (src, spa_buffer); return; } g_assert_not_reached (); } static gboolean do_record_frame (MetaScreenCastStreamSrc *src, struct spa_buffer *spa_buffer, uint8_t *data, GError **error) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); if (spa_buffer->datas[0].data || spa_buffer->datas[0].type == SPA_DATA_MemFd) { return meta_screen_cast_stream_src_record_to_buffer (src, data, error); } else if (spa_buffer->datas[0].type == SPA_DATA_DmaBuf) { CoglDmaBufHandle *dmabuf_handle = g_hash_table_lookup (priv->dmabuf_handles, GINT_TO_POINTER (spa_buffer->datas[0].fd)); CoglFramebuffer *dmabuf_fbo = cogl_dma_buf_handle_get_framebuffer (dmabuf_handle); return meta_screen_cast_stream_src_record_to_framebuffer (src, dmabuf_fbo, error); } g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown SPA buffer type %u", spa_buffer->datas[0].type); return FALSE; } gboolean meta_screen_cast_stream_src_pending_follow_up_frame (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); return priv->follow_up_frame_source_id != 0; } static gboolean follow_up_frame_cb (gpointer user_data) { MetaScreenCastStreamSrc *src = user_data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); priv->follow_up_frame_source_id = 0; meta_screen_cast_stream_src_record_follow_up (src); return G_SOURCE_REMOVE; } static void maybe_schedule_follow_up_frame (MetaScreenCastStreamSrc *src, int64_t timeout_us) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); if (priv->follow_up_frame_source_id) return; priv->follow_up_frame_source_id = g_timeout_add (us2ms (timeout_us), follow_up_frame_cb, src); } void meta_screen_cast_stream_src_maybe_record_frame (MetaScreenCastStreamSrc *src, MetaScreenCastRecordFlag flags) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); MetaRectangle crop_rect; struct pw_buffer *buffer; struct spa_buffer *spa_buffer; uint8_t *data = NULL; uint64_t now_us; g_autoptr (GError) error = NULL; now_us = g_get_monotonic_time (); if (priv->video_format.max_framerate.num > 0 && priv->last_frame_timestamp_us != 0) { int64_t min_interval_us; int64_t time_since_last_frame_us; min_interval_us = ((G_USEC_PER_SEC * priv->video_format.max_framerate.denom) / priv->video_format.max_framerate.num); time_since_last_frame_us = now_us - priv->last_frame_timestamp_us; if (time_since_last_frame_us < min_interval_us) { int64_t timeout_us; timeout_us = min_interval_us - time_since_last_frame_us; maybe_schedule_follow_up_frame (src, timeout_us); return; } } if (!priv->pipewire_stream) return; buffer = pw_stream_dequeue_buffer (priv->pipewire_stream); if (!buffer) return; spa_buffer = buffer->buffer; data = spa_buffer->datas[0].data; if (spa_buffer->datas[0].type != SPA_DATA_DmaBuf && !data) { g_critical ("Invalid buffer data"); return; } if (!(flags & META_SCREEN_CAST_RECORD_FLAG_CURSOR_ONLY)) { g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); if (do_record_frame (src, spa_buffer, data, &error)) { struct spa_meta_region *spa_meta_video_crop; spa_buffer->datas[0].chunk->size = spa_buffer->datas[0].maxsize; spa_buffer->datas[0].chunk->stride = priv->video_stride; /* Update VideoCrop if needed */ spa_meta_video_crop = spa_buffer_find_meta_data (spa_buffer, SPA_META_VideoCrop, sizeof (*spa_meta_video_crop)); if (spa_meta_video_crop) { if (meta_screen_cast_stream_src_get_videocrop (src, &crop_rect)) { spa_meta_video_crop->region.position.x = crop_rect.x; spa_meta_video_crop->region.position.y = crop_rect.y; spa_meta_video_crop->region.size.width = crop_rect.width; spa_meta_video_crop->region.size.height = crop_rect.height; } else { spa_meta_video_crop->region.position.x = 0; spa_meta_video_crop->region.position.y = 0; spa_meta_video_crop->region.size.width = priv->stream_width; spa_meta_video_crop->region.size.height = priv->stream_height; } } } else { g_warning ("Failed to record screen cast frame: %s", error->message); spa_buffer->datas[0].chunk->size = 0; } } else { spa_buffer->datas[0].chunk->size = 0; } maybe_record_cursor (src, spa_buffer); priv->last_frame_timestamp_us = now_us; pw_stream_queue_buffer (priv->pipewire_stream, buffer); } static gboolean meta_screen_cast_stream_src_is_enabled (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); return priv->is_enabled; } static void meta_screen_cast_stream_src_enable (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->enable (src); priv->is_enabled = TRUE; } static void meta_screen_cast_stream_src_disable (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); META_SCREEN_CAST_STREAM_SRC_GET_CLASS (src)->disable (src); g_clear_handle_id (&priv->follow_up_frame_source_id, g_source_remove); priv->is_enabled = FALSE; } static void on_stream_state_changed (void *data, enum pw_stream_state old, enum pw_stream_state state, const char *error_message) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); switch (state) { case PW_STREAM_STATE_ERROR: g_warning ("pipewire stream error: %s", error_message); if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); priv->emit_closed_after_dispatch = TRUE; break; case PW_STREAM_STATE_PAUSED: if (priv->node_id == SPA_ID_INVALID && priv->pipewire_stream) { priv->node_id = pw_stream_get_node_id (priv->pipewire_stream); g_signal_emit (src, signals[READY], 0, (unsigned int) priv->node_id); } if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); break; case PW_STREAM_STATE_STREAMING: if (!meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_enable (src); break; case PW_STREAM_STATE_UNCONNECTED: case PW_STREAM_STATE_CONNECTING: break; } } static void on_stream_param_changed (void *data, uint32_t id, const struct spa_pod *format) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); uint8_t params_buffer[1024]; int32_t width, height, stride, size; struct spa_pod_builder pod_builder; const struct spa_pod *params[3]; const int bpp = 4; if (!format || id != SPA_PARAM_Format) return; spa_format_video_raw_parse (format, &priv->video_format); width = priv->video_format.size.width; height = priv->video_format.size.height; stride = SPA_ROUND_UP_N (width * bpp, 4); size = height * stride; priv->video_stride = stride; pod_builder = SPA_POD_BUILDER_INIT (params_buffer, sizeof (params_buffer)); params[0] = spa_pod_builder_add_object ( &pod_builder, SPA_TYPE_OBJECT_ParamBuffers, SPA_PARAM_Buffers, SPA_PARAM_BUFFERS_buffers, SPA_POD_CHOICE_RANGE_Int (16, 2, 16), SPA_PARAM_BUFFERS_blocks, SPA_POD_Int (1), SPA_PARAM_BUFFERS_size, SPA_POD_Int (size), SPA_PARAM_BUFFERS_stride, SPA_POD_Int (stride), SPA_PARAM_BUFFERS_align, SPA_POD_Int (16)); params[1] = spa_pod_builder_add_object ( &pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, SPA_POD_Id (SPA_META_VideoCrop), SPA_PARAM_META_size, SPA_POD_Int (sizeof (struct spa_meta_region))); params[2] = spa_pod_builder_add_object ( &pod_builder, SPA_TYPE_OBJECT_ParamMeta, SPA_PARAM_Meta, SPA_PARAM_META_type, SPA_POD_Id (SPA_META_Cursor), SPA_PARAM_META_size, SPA_POD_Int (CURSOR_META_SIZE (64, 64))); pw_stream_update_params (priv->pipewire_stream, params, G_N_ELEMENTS (params)); } static void on_stream_add_buffer (void *data, struct pw_buffer *buffer) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); CoglContext *context = clutter_backend_get_cogl_context (clutter_get_default_backend ()); CoglRenderer *renderer = cogl_context_get_renderer (context); g_autoptr (GError) error = NULL; CoglDmaBufHandle *dmabuf_handle; struct spa_buffer *spa_buffer = buffer->buffer; struct spa_data *spa_data = spa_buffer->datas; const int bpp = 4; int stride; stride = SPA_ROUND_UP_N (priv->video_format.size.width * bpp, 4); spa_data[0].mapoffset = 0; spa_data[0].maxsize = stride * priv->video_format.size.height; dmabuf_handle = cogl_renderer_create_dma_buf (renderer, priv->stream_width, priv->stream_height, &error); if (error) g_debug ("Error exporting DMA buffer handle: %s", error->message); if (dmabuf_handle) { spa_data[0].type = SPA_DATA_DmaBuf; spa_data[0].flags = SPA_DATA_FLAG_READWRITE; spa_data[0].fd = cogl_dma_buf_handle_get_fd (dmabuf_handle); spa_data[0].data = NULL; g_hash_table_insert (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd), dmabuf_handle); } else { unsigned int seals; /* Fallback to a memfd buffer */ spa_data[0].type = SPA_DATA_MemFd; spa_data[0].flags = SPA_DATA_FLAG_READWRITE; spa_data[0].fd = memfd_create ("mutter-screen-cast-memfd", MFD_CLOEXEC | MFD_ALLOW_SEALING); if (spa_data[0].fd == -1) { g_critical ("Can't create memfd: %m"); return; } spa_data[0].mapoffset = 0; spa_data[0].maxsize = stride * priv->video_format.size.height; if (ftruncate (spa_data[0].fd, spa_data[0].maxsize) < 0) { g_critical ("Can't truncate to %d: %m", spa_data[0].maxsize); return; } seals = F_SEAL_GROW | F_SEAL_SHRINK | F_SEAL_SEAL; if (fcntl (spa_data[0].fd, F_ADD_SEALS, seals) == -1) g_warning ("Failed to add seals: %m"); spa_data[0].data = mmap (NULL, spa_data[0].maxsize, PROT_READ | PROT_WRITE, MAP_SHARED, spa_data[0].fd, spa_data[0].mapoffset); if (spa_data[0].data == MAP_FAILED) { g_critical ("Failed to mmap memory: %m"); return; } } } static void on_stream_remove_buffer (void *data, struct pw_buffer *buffer) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); struct spa_buffer *spa_buffer = buffer->buffer; struct spa_data *spa_data = spa_buffer->datas; if (spa_data[0].type == SPA_DATA_DmaBuf) { if (!g_hash_table_remove (priv->dmabuf_handles, GINT_TO_POINTER (spa_data[0].fd))) g_critical ("Failed to remove non-exported DMA buffer"); } else if (spa_data[0].type == SPA_DATA_MemFd) { munmap (spa_data[0].data, spa_data[0].maxsize); close (spa_data[0].fd); } } static const struct pw_stream_events stream_events = { PW_VERSION_STREAM_EVENTS, .state_changed = on_stream_state_changed, .param_changed = on_stream_param_changed, .add_buffer = on_stream_add_buffer, .remove_buffer = on_stream_remove_buffer, }; static struct pw_stream * create_pipewire_stream (MetaScreenCastStreamSrc *src, GError **error) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); struct pw_stream *pipewire_stream; uint8_t buffer[1024]; struct spa_pod_builder pod_builder = SPA_POD_BUILDER_INIT (buffer, sizeof (buffer)); float frame_rate; MetaFraction frame_rate_fraction; struct spa_fraction max_framerate; struct spa_fraction min_framerate; const struct spa_pod *params[1]; int result; priv->node_id = SPA_ID_INVALID; pipewire_stream = pw_stream_new (priv->pipewire_core, "meta-screen-cast-src", NULL); if (!pipewire_stream) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create PipeWire stream: %s", strerror (errno)); return NULL; } meta_screen_cast_stream_src_get_specs (src, &priv->stream_width, &priv->stream_height, &frame_rate); frame_rate_fraction = meta_fraction_from_double (frame_rate); min_framerate = SPA_FRACTION (1, 1); max_framerate = SPA_FRACTION (frame_rate_fraction.num, frame_rate_fraction.denom); params[0] = spa_pod_builder_add_object ( &pod_builder, SPA_TYPE_OBJECT_Format, SPA_PARAM_EnumFormat, SPA_FORMAT_mediaType, SPA_POD_Id (SPA_MEDIA_TYPE_video), SPA_FORMAT_mediaSubtype, SPA_POD_Id (SPA_MEDIA_SUBTYPE_raw), SPA_FORMAT_VIDEO_format, SPA_POD_Id (SPA_VIDEO_FORMAT_BGRx), SPA_FORMAT_VIDEO_size, SPA_POD_Rectangle (&SPA_RECTANGLE (priv->stream_width, priv->stream_height)), SPA_FORMAT_VIDEO_framerate, SPA_POD_Fraction (&SPA_FRACTION (0, 1)), SPA_FORMAT_VIDEO_maxFramerate, SPA_POD_CHOICE_RANGE_Fraction (&max_framerate, &min_framerate, &max_framerate)); pw_stream_add_listener (pipewire_stream, &priv->pipewire_stream_listener, &stream_events, src); result = pw_stream_connect (pipewire_stream, PW_DIRECTION_OUTPUT, SPA_ID_INVALID, (PW_STREAM_FLAG_DRIVER | PW_STREAM_FLAG_ALLOC_BUFFERS), params, G_N_ELEMENTS (params)); if (result != 0) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not connect: %s", spa_strerror (result)); return NULL; } return pipewire_stream; } static void on_core_error (void *data, uint32_t id, int seq, int res, const char *message) { MetaScreenCastStreamSrc *src = data; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); g_warning ("pipewire remote error: id:%u %s", id, message); if (id == PW_ID_CORE && res == -EPIPE) { if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); priv->emit_closed_after_dispatch = TRUE; } } static gboolean pipewire_loop_source_prepare (GSource *base, int *timeout) { *timeout = -1; return FALSE; } static gboolean pipewire_loop_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { MetaPipeWireSource *pipewire_source = (MetaPipeWireSource *) source; MetaScreenCastStreamSrc *src = pipewire_source->src; MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); int result; result = pw_loop_iterate (pipewire_source->pipewire_loop, 0); if (result < 0) g_warning ("pipewire_loop_iterate failed: %s", spa_strerror (result)); if (priv->emit_closed_after_dispatch) g_signal_emit (src, signals[CLOSED], 0); return TRUE; } static void pipewire_loop_source_finalize (GSource *source) { MetaPipeWireSource *pipewire_source = (MetaPipeWireSource *) source; pw_loop_leave (pipewire_source->pipewire_loop); pw_loop_destroy (pipewire_source->pipewire_loop); } static GSourceFuncs pipewire_source_funcs = { pipewire_loop_source_prepare, NULL, pipewire_loop_source_dispatch, pipewire_loop_source_finalize }; static MetaPipeWireSource * create_pipewire_source (MetaScreenCastStreamSrc *src) { MetaPipeWireSource *pipewire_source; pipewire_source = (MetaPipeWireSource *) g_source_new (&pipewire_source_funcs, sizeof (MetaPipeWireSource)); pipewire_source->src = src; pipewire_source->pipewire_loop = pw_loop_new (NULL); if (!pipewire_source->pipewire_loop) { g_source_unref ((GSource *) pipewire_source); return NULL; } g_source_add_unix_fd (&pipewire_source->base, pw_loop_get_fd (pipewire_source->pipewire_loop), G_IO_IN | G_IO_ERR); pw_loop_enter (pipewire_source->pipewire_loop); g_source_attach (&pipewire_source->base, NULL); return pipewire_source; } static const struct pw_core_events core_events = { PW_VERSION_CORE_EVENTS, .error = on_core_error, }; static gboolean meta_screen_cast_stream_src_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (initable); MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); priv->pipewire_source = create_pipewire_source (src); if (!priv->pipewire_source) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create PipeWire source"); return FALSE; } priv->pipewire_context = pw_context_new (priv->pipewire_source->pipewire_loop, NULL, 0); if (!priv->pipewire_context) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create pipewire context"); return FALSE; } priv->pipewire_core = pw_context_connect (priv->pipewire_context, NULL, 0); if (!priv->pipewire_core) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Couldn't connect pipewire context"); return FALSE; } pw_core_add_listener (priv->pipewire_core, &priv->pipewire_core_listener, &core_events, src); priv->pipewire_stream = create_pipewire_stream (src, error); if (!priv->pipewire_stream) return FALSE; return TRUE; } static void meta_screen_cast_stream_src_init_initable_iface (GInitableIface *iface) { iface->init = meta_screen_cast_stream_src_initable_init; } MetaScreenCastStream * meta_screen_cast_stream_src_get_stream (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); return priv->stream; } static void meta_screen_cast_stream_src_finalize (GObject *object) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object); MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); if (meta_screen_cast_stream_src_is_enabled (src)) meta_screen_cast_stream_src_disable (src); g_clear_pointer (&priv->pipewire_stream, pw_stream_destroy); g_clear_pointer (&priv->dmabuf_handles, g_hash_table_destroy); g_clear_pointer (&priv->pipewire_core, pw_core_disconnect); g_clear_pointer (&priv->pipewire_context, pw_context_destroy); g_source_destroy (&priv->pipewire_source->base); g_source_unref (&priv->pipewire_source->base); G_OBJECT_CLASS (meta_screen_cast_stream_src_parent_class)->finalize (object); } static void meta_screen_cast_stream_src_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object); MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); switch (prop_id) { case PROP_STREAM: priv->stream = g_value_get_object (value); break;; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_stream_src_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaScreenCastStreamSrc *src = META_SCREEN_CAST_STREAM_SRC (object); MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); switch (prop_id) { case PROP_STREAM: g_value_set_object (value, priv->stream); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_stream_src_init (MetaScreenCastStreamSrc *src) { MetaScreenCastStreamSrcPrivate *priv = meta_screen_cast_stream_src_get_instance_private (src); priv->dmabuf_handles = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) cogl_dma_buf_handle_free); } static void meta_screen_cast_stream_src_class_init (MetaScreenCastStreamSrcClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_screen_cast_stream_src_finalize; object_class->set_property = meta_screen_cast_stream_src_set_property; object_class->get_property = meta_screen_cast_stream_src_get_property; g_object_class_install_property (object_class, PROP_STREAM, g_param_spec_object ("stream", "stream", "MetaScreenCastStream", META_TYPE_SCREEN_CAST_STREAM, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); signals[READY] = g_signal_new ("ready", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); signals[CLOSED] = g_signal_new ("closed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/backends/meta-cursor-tracker-private.h0000664000175000017500000000472514723361714022026 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Giovanni Campagna */ #ifndef META_CURSOR_TRACKER_PRIVATE_H #define META_CURSOR_TRACKER_PRIVATE_H #include "backends/meta-cursor.h" #include "backends/meta-cursor-renderer.h" #include "backends/x11/cm/meta-cursor-sprite-xfixes.h" #include "meta/meta-cursor-tracker.h" struct _MetaCursorTracker { GObject parent_instance; gboolean is_showing; MetaCursorSprite *effective_cursor; /* May be NULL when hidden */ MetaCursorSprite *displayed_cursor; /* Wayland clients can set a NULL buffer as their cursor * explicitly, which means that we shouldn't display anything. * So, we can't simply store a NULL in window_cursor to * determine an unset window cursor; we need an extra boolean. */ gboolean has_window_cursor; MetaCursorSprite *window_cursor; MetaCursorSprite *root_cursor; /* The cursor from the X11 server. */ MetaCursorSpriteXfixes *xfixes_cursor; }; gboolean meta_cursor_tracker_handle_xevent (MetaCursorTracker *tracker, XEvent *xevent); void meta_cursor_tracker_set_window_cursor (MetaCursorTracker *tracker, MetaCursorSprite *cursor_sprite); void meta_cursor_tracker_unset_window_cursor (MetaCursorTracker *tracker); void meta_cursor_tracker_set_root_cursor (MetaCursorTracker *tracker, MetaCursorSprite *cursor_sprite); void meta_cursor_tracker_update_position (MetaCursorTracker *tracker, float new_x, float new_y); MetaCursorSprite * meta_cursor_tracker_get_displayed_cursor (MetaCursorTracker *tracker); #endif muffin-6.4.1/src/backends/meta-monitor-config-migration.c0000664000175000017500000011250414723361714022317 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001, 2002 Havoc Pennington * Copyright (C) 2002, 2003 Red Hat Inc. * Some ICCCM manager selection code derived from fvwm2, * Copyright (C) 2001 Dominik Vogt, Matthias Clasen, and fvwm2 team * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2013, 2017 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* * Portions of this file are derived from gnome-desktop/libgnome-desktop/gnome-rr-config.c * * Copyright 2007, 2008, Red Hat, Inc. * Copyright 2010 Giovanni Campagna * * Author: Soren Sandmann */ #include "config.h" #include "backends/meta-monitor-config-migration.h" #include #include #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-monitor-manager-private.h" #include "meta/boxes.h" #define META_MONITORS_CONFIG_MIGRATION_ERROR (meta_monitors_config_migration_error_quark ()) static GQuark meta_monitors_config_migration_error_quark (void); G_DEFINE_QUARK (meta-monitors-config-migration-error-quark, meta_monitors_config_migration_error) enum _MetaConfigMigrationError { META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED, META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE } MetaConfigMigrationError; typedef struct { char *connector; char *vendor; char *product; char *serial; } MetaOutputKey; typedef struct { gboolean enabled; MetaRectangle rect; float refresh_rate; float scale; MetaMonitorTransform transform; gboolean is_primary; gboolean is_presentation; gboolean is_underscanning; } MetaOutputConfig; typedef struct _MetaLegacyMonitorsConfig { MetaOutputKey *keys; MetaOutputConfig *outputs; unsigned int n_outputs; } MetaLegacyMonitorsConfig; typedef enum { STATE_INITIAL, STATE_MONITORS, STATE_CONFIGURATION, STATE_OUTPUT, STATE_OUTPUT_FIELD, STATE_CLONE } ParserState; typedef struct { ParserState state; int unknown_count; GArray *key_array; GArray *output_array; MetaOutputKey key; MetaOutputConfig output; char *output_field; GHashTable *configs; } ConfigParser; static MetaLegacyMonitorsConfig * legacy_config_new (void) { return g_new0 (MetaLegacyMonitorsConfig, 1); } static void legacy_config_free (gpointer data) { MetaLegacyMonitorsConfig *config = data; g_free (config->keys); g_free (config->outputs); g_free (config); } static unsigned long output_key_hash (const MetaOutputKey *key) { return (g_str_hash (key->connector) ^ g_str_hash (key->vendor) ^ g_str_hash (key->product) ^ g_str_hash (key->serial)); } static gboolean output_key_equal (const MetaOutputKey *one, const MetaOutputKey *two) { return (strcmp (one->connector, two->connector) == 0 && strcmp (one->vendor, two->vendor) == 0 && strcmp (one->product, two->product) == 0 && strcmp (one->serial, two->serial) == 0); } static unsigned int legacy_config_hash (gconstpointer data) { const MetaLegacyMonitorsConfig *config = data; unsigned int i, hash; hash = 0; for (i = 0; i < config->n_outputs; i++) hash ^= output_key_hash (&config->keys[i]); return hash; } static gboolean legacy_config_equal (gconstpointer one, gconstpointer two) { const MetaLegacyMonitorsConfig *c_one = one; const MetaLegacyMonitorsConfig *c_two = two; unsigned int i; gboolean ok; if (c_one->n_outputs != c_two->n_outputs) return FALSE; ok = TRUE; for (i = 0; i < c_one->n_outputs && ok; i++) ok = output_key_equal (&c_one->keys[i], &c_two->keys[i]); return ok; } static void free_output_key (MetaOutputKey *key) { g_free (key->connector); g_free (key->vendor); g_free (key->product); g_free (key->serial); } static void handle_start_element (GMarkupParseContext *context, const char *element_name, const char **attribute_names, const char **attribute_values, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_INITIAL: { char *version; if (strcmp (element_name, "monitors") != 0) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid document element %s", element_name); return; } if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error, G_MARKUP_COLLECT_STRING, "version", &version, G_MARKUP_COLLECT_INVALID)) return; if (strcmp (version, "1") != 0) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid or unsupported version %s", version); return; } parser->state = STATE_MONITORS; return; } case STATE_MONITORS: { if (strcmp (element_name, "configuration") != 0) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Invalid toplevel element %s", element_name); return; } parser->key_array = g_array_new (FALSE, FALSE, sizeof (MetaOutputKey)); parser->output_array = g_array_new (FALSE, FALSE, sizeof (MetaOutputConfig)); parser->state = STATE_CONFIGURATION; return; } case STATE_CONFIGURATION: { if (strcmp (element_name, "clone") == 0 && parser->unknown_count == 0) { parser->state = STATE_CLONE; } else if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0) { char *name; if (!g_markup_collect_attributes (element_name, attribute_names, attribute_values, error, G_MARKUP_COLLECT_STRING, "name", &name, G_MARKUP_COLLECT_INVALID)) return; memset (&parser->key, 0, sizeof (MetaOutputKey)); memset (&parser->output, 0, sizeof (MetaOutputConfig)); parser->output.scale = -1.0f; parser->key.connector = g_strdup (name); parser->state = STATE_OUTPUT; } else { parser->unknown_count++; } return; } case STATE_OUTPUT: { if ((strcmp (element_name, "vendor") == 0 || strcmp (element_name, "product") == 0 || strcmp (element_name, "serial") == 0 || strcmp (element_name, "width") == 0 || strcmp (element_name, "height") == 0 || strcmp (element_name, "rate") == 0 || strcmp (element_name, "x") == 0 || strcmp (element_name, "y") == 0 || strcmp (element_name, "rotation") == 0 || strcmp (element_name, "reflect_x") == 0 || strcmp (element_name, "reflect_y") == 0 || strcmp (element_name, "primary") == 0 || strcmp (element_name, "presentation") == 0 || strcmp (element_name, "underscanning") == 0 || strcmp (element_name, "scale") == 0) && parser->unknown_count == 0) { parser->state = STATE_OUTPUT_FIELD; parser->output_field = g_strdup (element_name); } else { parser->unknown_count++; } return; } case STATE_CLONE: case STATE_OUTPUT_FIELD: { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unexpected element %s", element_name); return; } default: g_assert_not_reached (); } } static void handle_end_element (GMarkupParseContext *context, const char *element_name, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_MONITORS: { parser->state = STATE_INITIAL; return; } case STATE_CONFIGURATION: { if (strcmp (element_name, "configuration") == 0 && parser->unknown_count == 0) { MetaLegacyMonitorsConfig *config = legacy_config_new (); g_assert (parser->key_array->len == parser->output_array->len); config->n_outputs = parser->key_array->len; config->keys = (void*)g_array_free (parser->key_array, FALSE); config->outputs = (void*)g_array_free (parser->output_array, FALSE); g_hash_table_replace (parser->configs, config, config); parser->key_array = NULL; parser->output_array = NULL; parser->state = STATE_MONITORS; } else { parser->unknown_count--; g_assert (parser->unknown_count >= 0); } return; } case STATE_OUTPUT: { if (strcmp (element_name, "output") == 0 && parser->unknown_count == 0) { if (parser->key.vendor == NULL || parser->key.product == NULL || parser->key.serial == NULL) { /* Disconnected output, ignore */ free_output_key (&parser->key); } else { if (parser->output.rect.width == 0 || parser->output.rect.height == 0) parser->output.enabled = FALSE; else parser->output.enabled = TRUE; g_array_append_val (parser->key_array, parser->key); g_array_append_val (parser->output_array, parser->output); } memset (&parser->key, 0, sizeof (MetaOutputKey)); memset (&parser->output, 0, sizeof (MetaOutputConfig)); parser->state = STATE_CONFIGURATION; } else { parser->unknown_count--; g_assert (parser->unknown_count >= 0); } return; } case STATE_CLONE: { parser->state = STATE_CONFIGURATION; return; } case STATE_OUTPUT_FIELD: { g_free (parser->output_field); parser->output_field = NULL; parser->state = STATE_OUTPUT; return; } case STATE_INITIAL: default: g_assert_not_reached (); } } static void read_int (const char *text, gsize text_len, gint *field, GError **error) { char buf[64]; gint64 v; char *end; strncpy (buf, text, text_len); buf[MIN (63, text_len)] = 0; v = g_ascii_strtoll (buf, &end, 10); /* Limit reasonable values (actual limits are a lot smaller that these) */ if (*end || v < 0 || v > G_MAXINT16) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Expected a number, got %s", buf); else *field = v; } static void read_float (const char *text, gsize text_len, gfloat *field, GError **error) { char buf[64]; gfloat v; char *end; strncpy (buf, text, text_len); buf[MIN (63, text_len)] = 0; v = g_ascii_strtod (buf, &end); /* Limit reasonable values (actual limits are a lot smaller that these) */ if (*end) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Expected a number, got %s", buf); else *field = v; } static gboolean read_bool (const char *text, gsize text_len, GError **error) { if (strncmp (text, "no", text_len) == 0) return FALSE; else if (strncmp (text, "yes", text_len) == 0) return TRUE; else g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid boolean value %.*s", (int)text_len, text); return FALSE; } static gboolean is_all_whitespace (const char *text, gsize text_len) { gsize i; for (i = 0; i < text_len; i++) if (!g_ascii_isspace (text[i])) return FALSE; return TRUE; } static void handle_text (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { ConfigParser *parser = user_data; switch (parser->state) { case STATE_MONITORS: { if (!is_all_whitespace (text, text_len)) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unexpected content at this point"); return; } case STATE_CONFIGURATION: { if (parser->unknown_count == 0) { if (!is_all_whitespace (text, text_len)) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unexpected content at this point"); } else { /* Handling unknown element, ignore */ } return; } case STATE_OUTPUT: { if (parser->unknown_count == 0) { if (!is_all_whitespace (text, text_len)) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Unexpected content at this point"); } else { /* Handling unknown element, ignore */ } return; } case STATE_CLONE: { /* Ignore the clone flag */ return; } case STATE_OUTPUT_FIELD: { if (strcmp (parser->output_field, "vendor") == 0) parser->key.vendor = g_strndup (text, text_len); else if (strcmp (parser->output_field, "product") == 0) parser->key.product = g_strndup (text, text_len); else if (strcmp (parser->output_field, "serial") == 0) parser->key.serial = g_strndup (text, text_len); else if (strcmp (parser->output_field, "width") == 0) read_int (text, text_len, &parser->output.rect.width, error); else if (strcmp (parser->output_field, "height") == 0) read_int (text, text_len, &parser->output.rect.height, error); else if (strcmp (parser->output_field, "rate") == 0) read_float (text, text_len, &parser->output.refresh_rate, error); else if (strcmp (parser->output_field, "x") == 0) read_int (text, text_len, &parser->output.rect.x, error); else if (strcmp (parser->output_field, "y") == 0) read_int (text, text_len, &parser->output.rect.y, error); else if (strcmp (parser->output_field, "rotation") == 0) { if (strncmp (text, "normal", text_len) == 0) parser->output.transform = META_MONITOR_TRANSFORM_NORMAL; else if (strncmp (text, "left", text_len) == 0) parser->output.transform = META_MONITOR_TRANSFORM_90; else if (strncmp (text, "upside_down", text_len) == 0) parser->output.transform = META_MONITOR_TRANSFORM_180; else if (strncmp (text, "right", text_len) == 0) parser->output.transform = META_MONITOR_TRANSFORM_270; else g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Invalid rotation type %.*s", (int)text_len, text); } else if (strcmp (parser->output_field, "reflect_x") == 0) parser->output.transform += read_bool (text, text_len, error) ? META_MONITOR_TRANSFORM_FLIPPED : 0; else if (strcmp (parser->output_field, "reflect_y") == 0) { if (read_bool (text, text_len, error)) g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_INVALID_CONTENT, "Y reflection is not supported"); } else if (strcmp (parser->output_field, "primary") == 0) parser->output.is_primary = read_bool (text, text_len, error); else if (strcmp (parser->output_field, "presentation") == 0) parser->output.is_presentation = read_bool (text, text_len, error); else if (strcmp (parser->output_field, "underscanning") == 0) parser->output.is_underscanning = read_bool (text, text_len, error); else if (strcmp (parser->output_field, "scale") == 0) read_float (text, text_len, &parser->output.scale, error); else g_assert_not_reached (); return; } case STATE_INITIAL: default: g_assert_not_reached (); } } static const GMarkupParser config_parser = { .start_element = handle_start_element, .end_element = handle_end_element, .text = handle_text, }; static GHashTable * load_config_file (GFile *file, GError **error) { g_autofree char *contents = NULL; gsize size; g_autoptr (GMarkupParseContext) context = NULL; ConfigParser parser = { 0 }; if (!g_file_load_contents (file, NULL, &contents, &size, NULL, error)) return FALSE; parser.configs = g_hash_table_new_full (legacy_config_hash, legacy_config_equal, legacy_config_free, NULL); parser.state = STATE_INITIAL; context = g_markup_parse_context_new (&config_parser, G_MARKUP_TREAT_CDATA_AS_TEXT | G_MARKUP_PREFIX_ERROR_POSITION, &parser, NULL); if (!g_markup_parse_context_parse (context, contents, size, error)) { if (parser.key_array) g_array_free (parser.key_array, TRUE); if (parser.output_array) g_array_free (parser.output_array, TRUE); free_output_key (&parser.key); g_free (parser.output_field); g_hash_table_destroy (parser.configs); return NULL; } return parser.configs; } static MetaMonitorConfig * create_monitor_config (MetaOutputKey *output_key, MetaOutputConfig *output_config, int mode_width, int mode_height, GError **error) { MetaMonitorModeSpec *mode_spec; MetaMonitorSpec *monitor_spec; MetaMonitorConfig *monitor_config; mode_spec = g_new0 (MetaMonitorModeSpec, 1); *mode_spec = (MetaMonitorModeSpec) { .width = mode_width, .height = mode_height, .refresh_rate = output_config->refresh_rate }; if (!meta_verify_monitor_mode_spec (mode_spec, error)) { g_free (mode_spec); return NULL; } monitor_spec = g_new0 (MetaMonitorSpec, 1); *monitor_spec = (MetaMonitorSpec) { .connector = output_key->connector, .vendor = output_key->vendor, .product = output_key->product, .serial = output_key->serial }; monitor_config = g_new0 (MetaMonitorConfig, 1); *monitor_config = (MetaMonitorConfig) { .monitor_spec = monitor_spec, .mode_spec = mode_spec, .enable_underscanning = output_config->is_underscanning }; if (!meta_verify_monitor_config (monitor_config, error)) { meta_monitor_config_free (monitor_config); return NULL; } return monitor_config; } typedef struct _MonitorTile { MetaOutputKey *output_key; MetaOutputConfig *output_config; } MonitorTile; static MetaMonitorConfig * try_derive_tiled_monitor_config (MetaLegacyMonitorsConfig *config, MetaOutputKey *output_key, MetaOutputConfig *output_config, MetaMonitorConfigStore *config_store, MetaRectangle *out_layout, GError **error) { MonitorTile top_left_tile = { 0 }; MonitorTile top_right_tile = { 0 }; MonitorTile bottom_left_tile = { 0 }; MonitorTile bottom_right_tile = { 0 }; MonitorTile origin_tile = { 0 }; MetaMonitorTransform transform = output_config->transform; unsigned int i; int max_x = 0; int min_x = INT_MAX; int max_y = 0; int min_y = INT_MAX; int mode_width = 0; int mode_height = 0; MetaMonitorConfig *monitor_config; /* * In order to derive a monitor configuration for a tiled monitor, * try to find the origin tile, then combine the discovered output * tiles to given the configured transform a monitor mode. * * If the origin tile is not the main tile (tile always enabled * even for non-tiled modes), this will fail, but since infermation * about tiling is lost, there is no way to discover it. */ for (i = 0; i < config->n_outputs; i++) { MetaOutputKey *other_output_key = &config->keys[i]; MetaOutputConfig *other_output_config = &config->outputs[i]; MetaRectangle *rect; if (strcmp (output_key->vendor, other_output_key->vendor) != 0 || strcmp (output_key->product, other_output_key->product) != 0 || strcmp (output_key->serial, other_output_key->serial) != 0) continue; rect = &other_output_config->rect; min_x = MIN (min_x, rect->x); min_y = MIN (min_y, rect->y); max_x = MAX (max_x, rect->x + rect->width); max_y = MAX (max_y, rect->y + rect->height); if (min_x == rect->x && min_y == rect->y) { top_left_tile = (MonitorTile) { .output_key = other_output_key, .output_config = other_output_config }; } if (max_x == rect->x + rect->width && min_y == rect->y) { top_right_tile = (MonitorTile) { .output_key = other_output_key, .output_config = other_output_config }; } if (min_x == rect->x && max_y == rect->y + rect->height) { bottom_left_tile = (MonitorTile) { .output_key = other_output_key, .output_config = other_output_config }; } if (max_x == rect->x + rect->width && max_y == rect->y + rect->height) { bottom_right_tile = (MonitorTile) { .output_key = other_output_key, .output_config = other_output_config }; } } if (top_left_tile.output_key == bottom_right_tile.output_key) { g_set_error_literal (error, META_MONITORS_CONFIG_MIGRATION_ERROR, META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED, "Not a tiled monitor"); return NULL; } switch (transform) { case META_MONITOR_TRANSFORM_NORMAL: origin_tile = top_left_tile; mode_width = max_x - min_x; mode_height = max_y - min_y; break; case META_MONITOR_TRANSFORM_90: origin_tile = bottom_left_tile; mode_width = max_y - min_y; mode_height = max_x - min_x; break; case META_MONITOR_TRANSFORM_180: origin_tile = bottom_right_tile; mode_width = max_x - min_x; mode_height = max_y - min_y; break; case META_MONITOR_TRANSFORM_270: origin_tile = top_right_tile; mode_width = max_y - min_y; mode_height = max_x - min_x; break; case META_MONITOR_TRANSFORM_FLIPPED: origin_tile = bottom_left_tile; mode_width = max_x - min_x; mode_height = max_y - min_y; break; case META_MONITOR_TRANSFORM_FLIPPED_90: origin_tile = bottom_right_tile; mode_width = max_y - min_y; mode_height = max_x - min_x; break; case META_MONITOR_TRANSFORM_FLIPPED_180: origin_tile = top_right_tile; mode_width = max_x - min_x; mode_height = max_y - min_y; break; case META_MONITOR_TRANSFORM_FLIPPED_270: origin_tile = top_left_tile; mode_width = max_y - min_y; mode_height = max_x - min_x; break; } g_assert (origin_tile.output_key); g_assert (origin_tile.output_config); if (origin_tile.output_key != output_key) { g_set_error_literal (error, META_MONITORS_CONFIG_MIGRATION_ERROR, META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE, "Not the main tile"); return NULL; } monitor_config = create_monitor_config (origin_tile.output_key, origin_tile.output_config, mode_width, mode_height, error); if (!monitor_config) return NULL; *out_layout = (MetaRectangle) { .x = min_x, .y = min_y, .width = max_x - min_x, .height = max_y - min_y }; return monitor_config; } static MetaMonitorConfig * derive_monitor_config (MetaOutputKey *output_key, MetaOutputConfig *output_config, MetaRectangle *out_layout, GError **error) { int mode_width; int mode_height; MetaMonitorConfig *monitor_config; if (meta_monitor_transform_is_rotated (output_config->transform)) { mode_width = output_config->rect.height; mode_height = output_config->rect.width; } else { mode_width = output_config->rect.width; mode_height = output_config->rect.height; } monitor_config = create_monitor_config (output_key, output_config, mode_width, mode_height, error); if (!monitor_config) return NULL; *out_layout = output_config->rect; return monitor_config; } static MetaLogicalMonitorConfig * ensure_logical_monitor (GList **logical_monitor_configs, MetaOutputConfig *output_config, MetaRectangle *layout) { MetaLogicalMonitorConfig *new_logical_monitor_config; GList *l; for (l = *logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; if (meta_rectangle_equal (&logical_monitor_config->layout, layout)) return logical_monitor_config; } new_logical_monitor_config = g_new0 (MetaLogicalMonitorConfig, 1); *new_logical_monitor_config = (MetaLogicalMonitorConfig) { .layout = *layout, .is_primary = output_config->is_primary, .is_presentation = output_config->is_presentation, .transform = output_config->transform, .scale = output_config->scale, }; *logical_monitor_configs = g_list_append (*logical_monitor_configs, new_logical_monitor_config); return new_logical_monitor_config; } static GList * derive_logical_monitor_configs (MetaLegacyMonitorsConfig *config, MetaMonitorConfigStore *config_store, GError **error) { GList *logical_monitor_configs = NULL; unsigned int i; for (i = 0; i < config->n_outputs; i++) { MetaOutputKey *output_key = &config->keys[i]; MetaOutputConfig *output_config = &config->outputs[i]; MetaMonitorConfig *monitor_config = NULL; MetaRectangle layout; MetaLogicalMonitorConfig *logical_monitor_config; if (!output_config->enabled) continue; if (output_key->vendor && g_strcmp0 (output_key->vendor, "unknown") != 0 && output_key->product && g_strcmp0 (output_key->product, "unknown") != 0 && output_key->serial && g_strcmp0 (output_key->serial, "unknown") != 0) { monitor_config = try_derive_tiled_monitor_config (config, output_key, output_config, config_store, &layout, error); if (!monitor_config) { if ((*error)->domain == META_MONITORS_CONFIG_MIGRATION_ERROR) { int error_code = (*error)->code; g_clear_error (error); switch (error_code) { case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_TILED: break; case META_MONITORS_CONFIG_MIGRATION_ERROR_NOT_MAIN_TILE: continue; } } else { g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return NULL; } } } if (!monitor_config) monitor_config = derive_monitor_config (output_key, output_config, &layout, error); if (!monitor_config) { g_list_free_full (logical_monitor_configs, (GDestroyNotify) meta_logical_monitor_config_free); return NULL; } logical_monitor_config = ensure_logical_monitor (&logical_monitor_configs, output_config, &layout); logical_monitor_config->monitor_configs = g_list_append (logical_monitor_config->monitor_configs, monitor_config); } if (!logical_monitor_configs) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Empty configuration"); return NULL; } return logical_monitor_configs; } static char * generate_config_name (MetaLegacyMonitorsConfig *config) { char **output_strings; unsigned int i; char *key_name; output_strings = g_new0 (char *, config->n_outputs + 1); for (i = 0; i < config->n_outputs; i++) { MetaOutputKey *output_key = &config->keys[i]; output_strings[i] = g_strdup_printf ("%s:%s:%s:%s", output_key->connector, output_key->vendor, output_key->product, output_key->serial); } key_name = g_strjoinv (", ", output_strings); g_strfreev (output_strings); return key_name; } static GList * find_disabled_monitor_specs (MetaLegacyMonitorsConfig *legacy_config) { GList *disabled_monitors = NULL; unsigned int i; for (i = 0; i < legacy_config->n_outputs; i++) { MetaOutputKey *output_key = &legacy_config->keys[i]; MetaOutputConfig *output_config = &legacy_config->outputs[i]; MetaMonitorSpec *monitor_spec; if (output_config->enabled) continue; monitor_spec = g_new0 (MetaMonitorSpec, 1); *monitor_spec = (MetaMonitorSpec) { .connector = output_key->connector, .vendor = output_key->vendor, .product = output_key->product, .serial = output_key->serial }; disabled_monitors = g_list_prepend (disabled_monitors, monitor_spec); } return disabled_monitors; } static void migrate_config (gpointer key, gpointer value, gpointer user_data) { MetaLegacyMonitorsConfig *legacy_config = key; MetaMonitorConfigStore *config_store = user_data; MetaMonitorManager *monitor_manager = meta_monitor_config_store_get_monitor_manager (config_store); GList *logical_monitor_configs; MetaLogicalMonitorLayoutMode layout_mode; GError *error = NULL; GList *disabled_monitor_specs; MetaMonitorsConfig *config; logical_monitor_configs = derive_logical_monitor_configs (legacy_config, config_store, &error); if (!logical_monitor_configs) { g_autofree char *config_name = NULL; config_name = generate_config_name (legacy_config); g_warning ("Failed to migrate monitor configuration for %s: %s", config_name, error->message); return; } disabled_monitor_specs = find_disabled_monitor_specs (legacy_config); layout_mode = META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; config = meta_monitors_config_new_full (logical_monitor_configs, disabled_monitor_specs, layout_mode, META_MONITORS_CONFIG_FLAG_MIGRATED); if (!meta_verify_monitors_config (config, monitor_manager, &error)) { g_autofree char *config_name = NULL; config_name = generate_config_name (legacy_config); g_warning ("Ignoring invalid monitor configuration for %s: %s", config_name, error->message); g_object_unref (config); return; } meta_monitor_config_store_add (config_store, config); } gboolean meta_migrate_old_monitors_config (MetaMonitorConfigStore *config_store, GFile *in_file, GError **error) { g_autoptr (GHashTable) configs = NULL; configs = load_config_file (in_file, error); if (!configs) return FALSE; g_hash_table_foreach (configs, migrate_config, config_store); return TRUE; } gboolean meta_migrate_old_user_monitors_config (MetaMonitorConfigStore *config_store, const gchar *user_file_path, GError **error) { g_autofree char *backup_path = NULL; g_autoptr (GFile) backup_file = NULL; g_autoptr (GFile) user_file = NULL; user_file = g_file_new_for_path (user_file_path); backup_path = g_build_filename (g_get_user_config_dir (), "monitors-v1-backup.xml", NULL); backup_file = g_file_new_for_path (backup_path); if (!g_file_copy (user_file, backup_file, G_FILE_COPY_OVERWRITE | G_FILE_COPY_BACKUP, NULL, NULL, NULL, error)) { g_warning ("Failed to make a backup of monitors.xml: %s", (*error)->message); g_clear_error (error); } return meta_migrate_old_monitors_config (config_store, user_file, error); } gboolean meta_finish_monitors_config_migration (MetaMonitorManager *monitor_manager, MetaMonitorsConfig *config, GError **error) { MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); GList *l; MetaLogicalMonitorLayoutMode layout_mode; layout_mode = meta_monitor_manager_get_default_layout_mode (monitor_manager); for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; MetaMonitorConfig *monitor_config; MetaMonitorSpec *monitor_spec; MetaMonitor *monitor; MetaMonitorModeSpec *monitor_mode_spec; MetaMonitorMode *monitor_mode; monitor_config = logical_monitor_config->monitor_configs->data; monitor_spec = monitor_config->monitor_spec; monitor = meta_monitor_manager_get_monitor_from_spec (monitor_manager, monitor_spec); monitor_mode_spec = monitor_config->mode_spec; monitor_mode = meta_monitor_get_mode_from_spec (monitor, monitor_mode_spec); if (!monitor_mode) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Mode not available on monitor"); return FALSE; } if (logical_monitor_config->scale < 0.0f) { logical_monitor_config->scale = meta_monitor_manager_calculate_monitor_mode_scale (monitor_manager, layout_mode, monitor, monitor_mode); } } config->layout_mode = layout_mode; config->flags &= ~META_MONITORS_CONFIG_FLAG_MIGRATED; if (!meta_verify_monitors_config (config, monitor_manager, error)) return FALSE; meta_monitor_config_store_add (config_store, config); return TRUE; } muffin-6.4.1/src/backends/meta-screen-cast-window-stream.c0000664000175000017500000002334614723361714022410 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "backends/meta-screen-cast-window-stream.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-screen-cast-window.h" #include "backends/meta-screen-cast-window-stream-src.h" #include "compositor/meta-window-actor-private.h" #include "core/window-private.h" enum { PROP_0, PROP_WINDOW, }; struct _MetaScreenCastWindowStream { MetaScreenCastStream parent; MetaWindow *window; int stream_width; int stream_height; int logical_width; int logical_height; unsigned long window_unmanaged_handler_id; }; static GInitableIface *initable_parent_iface; static void meta_screen_cast_window_stream_init_initable_iface (GInitableIface *iface); G_DEFINE_TYPE_WITH_CODE (MetaScreenCastWindowStream, meta_screen_cast_window_stream, META_TYPE_SCREEN_CAST_STREAM, G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, meta_screen_cast_window_stream_init_initable_iface)) MetaWindow * meta_screen_cast_window_stream_get_window (MetaScreenCastWindowStream *window_stream) { return window_stream->window; } int meta_screen_cast_window_stream_get_width (MetaScreenCastWindowStream *window_stream) { return window_stream->stream_width; } int meta_screen_cast_window_stream_get_height (MetaScreenCastWindowStream *window_stream) { return window_stream->stream_height; } MetaScreenCastWindowStream * meta_screen_cast_window_stream_new (MetaScreenCastSession *session, GDBusConnection *connection, MetaWindow *window, MetaScreenCastCursorMode cursor_mode, GError **error) { return g_initable_new (META_TYPE_SCREEN_CAST_WINDOW_STREAM, NULL, error, "session", session, "connection", connection, "cursor-mode", cursor_mode, "window", window, NULL); } static MetaScreenCastStreamSrc * meta_screen_cast_window_stream_create_src (MetaScreenCastStream *stream, GError **error) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (stream); MetaScreenCastWindowStreamSrc *window_stream_src; window_stream_src = meta_screen_cast_window_stream_src_new (window_stream, error); if (!window_stream_src) return NULL; return META_SCREEN_CAST_STREAM_SRC (window_stream_src); } static void meta_screen_cast_window_stream_set_parameters (MetaScreenCastStream *stream, GVariantBuilder *parameters_builder) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (stream); g_variant_builder_add (parameters_builder, "{sv}", "size", g_variant_new ("(ii)", window_stream->logical_width, window_stream->logical_height)); } static void meta_screen_cast_window_stream_transform_position (MetaScreenCastStream *stream, double stream_x, double stream_y, double *x, double *y) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (stream); MetaScreenCastWindow *screen_cast_window = META_SCREEN_CAST_WINDOW (meta_window_actor_from_window (window_stream->window)); meta_screen_cast_window_transform_relative_position (screen_cast_window, stream_x, stream_y, x, y); } static void on_window_unmanaged (MetaScreenCastWindowStream *window_stream) { meta_screen_cast_stream_close (META_SCREEN_CAST_STREAM (window_stream)); } static void meta_screen_cast_window_stream_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (object); switch (prop_id) { case PROP_WINDOW: window_stream->window = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_window_stream_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (object); switch (prop_id) { case PROP_WINDOW: g_value_set_object (value, window_stream->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_screen_cast_window_stream_finalize (GObject *object) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (object); g_clear_signal_handler (&window_stream->window_unmanaged_handler_id, window_stream->window); G_OBJECT_CLASS (meta_screen_cast_window_stream_parent_class)->finalize (object); } static gboolean meta_screen_cast_window_stream_initable_init (GInitable *initable, GCancellable *cancellable, GError **error) { MetaScreenCastWindowStream *window_stream = META_SCREEN_CAST_WINDOW_STREAM (initable); MetaWindow *window = window_stream->window; MetaLogicalMonitor *logical_monitor; int scale; logical_monitor = meta_window_get_main_logical_monitor (window); if (!logical_monitor) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Main logical monitor not found"); return FALSE; } window_stream->window_unmanaged_handler_id = g_signal_connect_swapped (window, "unmanaged", G_CALLBACK (on_window_unmanaged), window_stream); if (meta_is_stage_views_scaled ()) scale = (int) ceilf (meta_logical_monitor_get_scale (logical_monitor)); else scale = 1; /* We cannot set the stream size to the exact size of the window, because * windows can be resized, whereas streams cannot. * So we set a size equals to the size of the logical monitor for the window. */ window_stream->logical_width = logical_monitor->rect.width; window_stream->logical_height = logical_monitor->rect.height; window_stream->stream_width = logical_monitor->rect.width * scale; window_stream->stream_height = logical_monitor->rect.height * scale; return initable_parent_iface->init (initable, cancellable, error); } static void meta_screen_cast_window_stream_init_initable_iface (GInitableIface *iface) { initable_parent_iface = g_type_interface_peek_parent (iface); iface->init = meta_screen_cast_window_stream_initable_init; } static void meta_screen_cast_window_stream_init (MetaScreenCastWindowStream *window_stream) { } static void meta_screen_cast_window_stream_class_init (MetaScreenCastWindowStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaScreenCastStreamClass *stream_class = META_SCREEN_CAST_STREAM_CLASS (klass); object_class->set_property = meta_screen_cast_window_stream_set_property; object_class->get_property = meta_screen_cast_window_stream_get_property; object_class->finalize = meta_screen_cast_window_stream_finalize; stream_class->create_src = meta_screen_cast_window_stream_create_src; stream_class->set_parameters = meta_screen_cast_window_stream_set_parameters; stream_class->transform_position = meta_screen_cast_window_stream_transform_position; g_object_class_install_property (object_class, PROP_WINDOW, g_param_spec_object ("window", "window", "MetaWindow", META_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } muffin-6.4.1/src/meta/0000775000175000017500000000000014723361714013457 5ustar fabiofabiomuffin-6.4.1/src/meta/common.h0000664000175000017500000005000514723361714015120 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * PLEASE KEEP IN SYNC WITH GSETTINGS SCHEMAS! */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_COMMON_H #define META_COMMON_H #include #include #include #include #include #include "clutter/clutter.h" /** * SECTION:common * @Title: Common * @Short_Description: Mutter common types */ /* This is set in stone and also hard-coded in GDK. */ #define META_VIRTUAL_CORE_POINTER_ID 2 #define META_VIRTUAL_CORE_KEYBOARD_ID 3 /* Replacement for X11 CurrentTime */ #define META_CURRENT_TIME 0L #define META_EXPORT __attribute__((visibility("default"))) extern /** * MetaFrameFlags: * @META_FRAME_ALLOWS_DELETE: frame allows delete * @META_FRAME_ALLOWS_MENU: frame allows menu * @META_FRAME_ALLOWS_MINIMIZE: frame allows minimize * @META_FRAME_ALLOWS_MAXIMIZE: frame allows maximize * @META_FRAME_ALLOWS_VERTICAL_RESIZE: frame allows vertical resize * @META_FRAME_ALLOWS_HORIZONTAL_RESIZE: frame allows horizontal resize * @META_FRAME_HAS_FOCUS: frame has focus * @META_FRAME_SHADED: frame is shaded * @META_FRAME_STUCK: frame is stuck * @META_FRAME_MAXIMIZED: frame is maximized * @META_FRAME_ALLOWS_SHADE: frame allows shade * @META_FRAME_ALLOWS_MOVE: frame allows move * @META_FRAME_FULLSCREEN: frame allows fullscreen * @META_FRAME_ABOVE: frame is above * @META_FRAME_TILED_LEFT: frame is tiled to the left * @META_FRAME_TILED_RIGHT: frame is tiled to the right */ typedef enum { META_FRAME_ALLOWS_DELETE = 1 << 0, META_FRAME_ALLOWS_MENU = 1 << 1, META_FRAME_ALLOWS_MINIMIZE = 1 << 2, META_FRAME_ALLOWS_MAXIMIZE = 1 << 3, META_FRAME_ALLOWS_VERTICAL_RESIZE = 1 << 4, META_FRAME_ALLOWS_HORIZONTAL_RESIZE = 1 << 5, META_FRAME_HAS_FOCUS = 1 << 6, META_FRAME_SHADED = 1 << 7, META_FRAME_STUCK = 1 << 8, META_FRAME_MAXIMIZED = 1 << 9, META_FRAME_ALLOWS_SHADE = 1 << 10, META_FRAME_ALLOWS_MOVE = 1 << 11, META_FRAME_FULLSCREEN = 1 << 12, META_FRAME_ABOVE = 1 << 13, META_FRAME_TILED_LEFT = 1 << 14, META_FRAME_TILED_RIGHT = 1 << 15, META_FRAME_TILED_TOP = 1 << 16, META_FRAME_TILED_BOTTOM = 1 << 17, META_FRAME_TILED_ULC = 1 << 18, META_FRAME_TILED_URC = 1 << 19, META_FRAME_TILED_LRC = 1 << 20, META_FRAME_TILED_LLC = 1 << 21, } MetaFrameFlags; #define META_FRAME_TILED_TOP_EDGES (META_FRAME_TILED_TOP | META_FRAME_TILED_ULC | META_FRAME_TILED_URC) #define META_FRAME_TILED_BOTTOM_EDGES (META_FRAME_TILED_BOTTOM | META_FRAME_TILED_LLC | META_FRAME_TILED_LRC) #define META_FRAME_TILED_LEFT_EDGES (META_FRAME_TILED_TOP | META_FRAME_TILED_BOTTOM | \ META_FRAME_TILED_LEFT | META_FRAME_TILED_ULC | META_FRAME_TILED_LLC) #define META_FRAME_TILED_RIGHT_EDGES (META_FRAME_TILED_TOP | META_FRAME_TILED_BOTTOM | \ META_FRAME_TILED_RIGHT | META_FRAME_TILED_URC | META_FRAME_TILED_LRC) #define META_FRAME_TILED_ANY (META_FRAME_TILED_LEFT | META_FRAME_TILED_RIGHT | \ META_FRAME_TILED_TOP | META_FRAME_TILED_BOTTOM | \ META_FRAME_TILED_ULC | META_FRAME_TILED_URC | \ META_FRAME_TILED_LLC | META_FRAME_TILED_LRC) /** * MetaGrabOp: * @META_GRAB_OP_NONE: None * @META_GRAB_OP_MOVING: Moving with pointer * @META_GRAB_OP_RESIZING_SE: Resizing SE with pointer * @META_GRAB_OP_RESIZING_S: Resizing S with pointer * @META_GRAB_OP_RESIZING_SW: Resizing SW with pointer * @META_GRAB_OP_RESIZING_N: Resizing N with pointer * @META_GRAB_OP_RESIZING_NE: Resizing NE with pointer * @META_GRAB_OP_RESIZING_NW: Resizing NW with pointer * @META_GRAB_OP_RESIZING_W: Resizing W with pointer * @META_GRAB_OP_RESIZING_E: Resizing E with pointer * @META_GRAB_OP_KEYBOARD_MOVING: Moving with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN: Resizing with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_S: Resizing S with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_N: Resizing N with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_W: Resizing W with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_E: Resizing E with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_SE: Resizing SE with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_NE: Resizing NE with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_SW: Resizing SW with keyboard * @META_GRAB_OP_KEYBOARD_RESIZING_NW: Resizing NS with keyboard * @META_GRAB_OP_COMPOSITOR: Compositor asked for grab */ /* The lower 16 bits of the grab operation is its type. * * Window grab operations have the following layout: * * 0000 0000 | 0000 0011 * NSEW flags | type * * Flags contains whether the operation is a keyboard operation, * and whether the keyboard operation is "unknown". * * The rest of the flags tell you which direction the resize is * going in. * * If the directions field is 0000, then the operation is a move, * not a resize. */ enum { META_GRAB_OP_WINDOW_FLAG_KEYBOARD = 0x0100, META_GRAB_OP_WINDOW_FLAG_UNKNOWN = 0x0200, META_GRAB_OP_WINDOW_DIR_WEST = 0x1000, META_GRAB_OP_WINDOW_DIR_EAST = 0x2000, META_GRAB_OP_WINDOW_DIR_SOUTH = 0x4000, META_GRAB_OP_WINDOW_DIR_NORTH = 0x8000, META_GRAB_OP_WINDOW_DIR_MASK = 0xF000, /* WGO = "window grab op". shorthand for below */ _WGO_K = META_GRAB_OP_WINDOW_FLAG_KEYBOARD, _WGO_U = META_GRAB_OP_WINDOW_FLAG_UNKNOWN, _WGO_W = META_GRAB_OP_WINDOW_DIR_WEST, _WGO_E = META_GRAB_OP_WINDOW_DIR_EAST, _WGO_S = META_GRAB_OP_WINDOW_DIR_SOUTH, _WGO_N = META_GRAB_OP_WINDOW_DIR_NORTH, }; typedef enum { META_GRAB_OP_NONE, /* Window grab ops. */ META_GRAB_OP_WINDOW_BASE, /* Special grab op when the compositor asked for a grab */ META_GRAB_OP_COMPOSITOR, /* For when a Wayland client takes a popup grab. */ META_GRAB_OP_WAYLAND_POPUP, /* For when the user clicks on a frame button. */ META_GRAB_OP_FRAME_BUTTON, META_GRAB_OP_MOVING = META_GRAB_OP_WINDOW_BASE, META_GRAB_OP_RESIZING_NW = META_GRAB_OP_WINDOW_BASE | _WGO_N | _WGO_W, META_GRAB_OP_RESIZING_N = META_GRAB_OP_WINDOW_BASE | _WGO_N, META_GRAB_OP_RESIZING_NE = META_GRAB_OP_WINDOW_BASE | _WGO_N | _WGO_E, META_GRAB_OP_RESIZING_E = META_GRAB_OP_WINDOW_BASE | _WGO_E, META_GRAB_OP_RESIZING_SW = META_GRAB_OP_WINDOW_BASE | _WGO_S | _WGO_W, META_GRAB_OP_RESIZING_S = META_GRAB_OP_WINDOW_BASE | _WGO_S, META_GRAB_OP_RESIZING_SE = META_GRAB_OP_WINDOW_BASE | _WGO_S | _WGO_E, META_GRAB_OP_RESIZING_W = META_GRAB_OP_WINDOW_BASE | _WGO_W, META_GRAB_OP_KEYBOARD_MOVING = META_GRAB_OP_WINDOW_BASE | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN = META_GRAB_OP_WINDOW_BASE | _WGO_K | _WGO_U, META_GRAB_OP_KEYBOARD_RESIZING_NW = META_GRAB_OP_WINDOW_BASE | _WGO_N | _WGO_W | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_N = META_GRAB_OP_WINDOW_BASE | _WGO_N | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_NE = META_GRAB_OP_WINDOW_BASE | _WGO_N | _WGO_E | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_E = META_GRAB_OP_WINDOW_BASE | _WGO_E | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_SW = META_GRAB_OP_WINDOW_BASE | _WGO_S | _WGO_W | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_S = META_GRAB_OP_WINDOW_BASE | _WGO_S | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_SE = META_GRAB_OP_WINDOW_BASE | _WGO_S | _WGO_E | _WGO_K, META_GRAB_OP_KEYBOARD_RESIZING_W = META_GRAB_OP_WINDOW_BASE | _WGO_W | _WGO_K, } MetaGrabOp; /** * MetaCursor: * @META_CURSOR_DEFAULT: Default cursor * @META_CURSOR_NORTH_RESIZE: Resize northern edge cursor * @META_CURSOR_SOUTH_RESIZE: Resize southern edge cursor * @META_CURSOR_WEST_RESIZE: Resize western edge cursor * @META_CURSOR_EAST_RESIZE: Resize eastern edge cursor * @META_CURSOR_SE_RESIZE: Resize south-eastern corner cursor * @META_CURSOR_SW_RESIZE: Resize south-western corner cursor * @META_CURSOR_NE_RESIZE: Resize north-eastern corner cursor * @META_CURSOR_NW_RESIZE: Resize north-western corner cursor * @META_CURSOR_MOVE_OR_RESIZE_WINDOW: Move or resize cursor * @META_CURSOR_BUSY: Busy cursor * @META_CURSOR_DND_IN_DRAG: DND in drag cursor * @META_CURSOR_DND_MOVE: DND move cursor * @META_CURSOR_DND_COPY: DND copy cursor * @META_CURSOR_DND_UNSUPPORTED_TARGET: DND unsupported target * @META_CURSOR_POINTING_HAND: pointing hand * @META_CURSOR_CROSSHAIR: crosshair (action forbidden) * @META_CURSOR_IBEAM: I-beam (text input) */ typedef enum { META_CURSOR_NONE = 0, META_CURSOR_DEFAULT, META_CURSOR_NORTH_RESIZE, META_CURSOR_SOUTH_RESIZE, META_CURSOR_WEST_RESIZE, META_CURSOR_EAST_RESIZE, META_CURSOR_SE_RESIZE, META_CURSOR_SW_RESIZE, META_CURSOR_NE_RESIZE, META_CURSOR_NW_RESIZE, META_CURSOR_MOVE_OR_RESIZE_WINDOW, META_CURSOR_BUSY, META_CURSOR_DND_IN_DRAG, META_CURSOR_DND_MOVE, META_CURSOR_DND_COPY, META_CURSOR_DND_UNSUPPORTED_TARGET, META_CURSOR_POINTING_HAND, META_CURSOR_CROSSHAIR, META_CURSOR_IBEAM, META_CURSOR_LAST } MetaCursor; /** * MetaFrameType: * @META_FRAME_TYPE_NORMAL: Normal frame * @META_FRAME_TYPE_DIALOG: Dialog frame * @META_FRAME_TYPE_MODAL_DIALOG: Modal dialog frame * @META_FRAME_TYPE_UTILITY: Utility frame * @META_FRAME_TYPE_MENU: Menu frame * @META_FRAME_TYPE_BORDER: Border frame * @META_FRAME_TYPE_ATTACHED: Attached frame * @META_FRAME_TYPE_LAST: Marks the end of the #MetaFrameType enumeration */ typedef enum { META_FRAME_TYPE_NORMAL, META_FRAME_TYPE_DIALOG, META_FRAME_TYPE_MODAL_DIALOG, META_FRAME_TYPE_UTILITY, META_FRAME_TYPE_MENU, META_FRAME_TYPE_BORDER, META_FRAME_TYPE_ATTACHED, META_FRAME_TYPE_LAST } MetaFrameType; /** * MetaVirtualModifier: * @META_VIRTUAL_SHIFT_MASK: Shift mask * @META_VIRTUAL_CONTROL_MASK: Control mask * @META_VIRTUAL_ALT_MASK: Alt mask * @META_VIRTUAL_META_MASK: Meta mask * @META_VIRTUAL_SUPER_MASK: Super mask * @META_VIRTUAL_HYPER_MASK: Hyper mask * @META_VIRTUAL_MOD2_MASK: Mod2 mask * @META_VIRTUAL_MOD3_MASK: Mod3 mask * @META_VIRTUAL_MOD4_MASK: Mod4 mask * @META_VIRTUAL_MOD5_MASK: Mod5 mask */ typedef enum { /* Create gratuitous divergence from regular * X mod bits, to be sure we find bugs */ META_VIRTUAL_SHIFT_MASK = 1 << 5, META_VIRTUAL_CONTROL_MASK = 1 << 6, META_VIRTUAL_ALT_MASK = 1 << 7, META_VIRTUAL_META_MASK = 1 << 8, META_VIRTUAL_SUPER_MASK = 1 << 9, META_VIRTUAL_HYPER_MASK = 1 << 10, META_VIRTUAL_MOD2_MASK = 1 << 11, META_VIRTUAL_MOD3_MASK = 1 << 12, META_VIRTUAL_MOD4_MASK = 1 << 13, META_VIRTUAL_MOD5_MASK = 1 << 14 } MetaVirtualModifier; /** * MetaDirection: * @META_DIRECTION_LEFT: Left * @META_DIRECTION_RIGHT: Right * @META_DIRECTION_TOP: Top * @META_DIRECTION_BOTTOM: Bottom * @META_DIRECTION_UP: Up * @META_DIRECTION_DOWN: Down * @META_DIRECTION_HORIZONTAL: Horizontal * @META_DIRECTION_VERTICAL: Vertical */ /* Relative directions or sides seem to come up all over the place... */ /* FIXME: Replace * display.[ch]:MetaDisplayDirection, * workspace.[ch]:MetaMotionDirection, * with the use of MetaDirection. */ typedef enum { META_DIRECTION_LEFT = 1 << 0, META_DIRECTION_RIGHT = 1 << 1, META_DIRECTION_TOP = 1 << 2, META_DIRECTION_BOTTOM = 1 << 3, /* Some aliases for making code more readable for various circumstances. */ META_DIRECTION_UP = META_DIRECTION_TOP, META_DIRECTION_DOWN = META_DIRECTION_BOTTOM, /* A few more definitions using aliases */ META_DIRECTION_HORIZONTAL = META_DIRECTION_LEFT | META_DIRECTION_RIGHT, META_DIRECTION_VERTICAL = META_DIRECTION_UP | META_DIRECTION_DOWN, } MetaDirection; /** * MetaMotionDirection: * @META_MOTION_UP: Upwards motion * @META_MOTION_DOWN: Downwards motion * @META_MOTION_LEFT: Motion to the left * @META_MOTION_RIGHT: Motion to the right * @META_MOTION_UP_LEFT: Motion up and to the left * @META_MOTION_UP_RIGHT: Motion up and to the right * @META_MOTION_DOWN_LEFT: Motion down and to the left * @META_MOTION_DOWN_RIGHT: Motion down and to the right */ /* Negative to avoid conflicting with real workspace * numbers */ typedef enum { META_MOTION_UP = -1, META_MOTION_DOWN = -2, META_MOTION_LEFT = -3, META_MOTION_RIGHT = -4, /* These are only used for effects */ META_MOTION_UP_LEFT = -5, META_MOTION_UP_RIGHT = -6, META_MOTION_DOWN_LEFT = -7, META_MOTION_DOWN_RIGHT = -8 } MetaMotionDirection; /** * MetaSide: * @META_SIDE_LEFT: Left side * @META_SIDE_RIGHT: Right side * @META_SIDE_TOP: Top side * @META_SIDE_BOTTOM: Bottom side */ /* Sometimes we want to talk about sides instead of directions; note * that the values must be as follows or meta_window_update_struts() * won't work. Using these values also is a safety blanket since * MetaDirection used to be used as a side. */ typedef enum { META_SIDE_LEFT = META_DIRECTION_LEFT, META_SIDE_RIGHT = META_DIRECTION_RIGHT, META_SIDE_TOP = META_DIRECTION_TOP, META_SIDE_BOTTOM = META_DIRECTION_BOTTOM } MetaSide; /** * MetaButtonFunction: * @META_BUTTON_FUNCTION_MENU: Menu * @META_BUTTON_FUNCTION_MINIMIZE: Minimize * @META_BUTTON_FUNCTION_MAXIMIZE: Maximize * @META_BUTTON_FUNCTION_CLOSE: Close * @META_BUTTON_FUNCTION_LAST: Marks the end of the #MetaButtonFunction enumeration * * Function a window button can have. Note, you can't add stuff here * without extending the theme format to draw a new function and * breaking all existing themes. */ typedef enum { META_BUTTON_FUNCTION_MENU, META_BUTTON_FUNCTION_MINIMIZE, META_BUTTON_FUNCTION_MAXIMIZE, META_BUTTON_FUNCTION_CLOSE, META_BUTTON_FUNCTION_LAST } MetaButtonFunction; #define MAX_BUTTONS_PER_CORNER META_BUTTON_FUNCTION_LAST /* Keep array size in sync with MAX_BUTTONS_PER_CORNER */ /** * MetaButtonLayout: * @left_buttons: (array fixed-size=4): * @right_buttons: (array fixed-size=4): * @left_buttons_has_spacer: (array fixed-size=4): * @right_buttons_has_spacer: (array fixed-size=4): */ typedef struct _MetaButtonLayout MetaButtonLayout; struct _MetaButtonLayout { /* buttons in the group on the left side */ MetaButtonFunction left_buttons[MAX_BUTTONS_PER_CORNER]; gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; /* buttons in the group on the right side */ MetaButtonFunction right_buttons[MAX_BUTTONS_PER_CORNER]; gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; }; /** * MetaWindowMenuType: * @META_WINDOW_MENU_WM: the window manager menu * @META_WINDOW_MENU_APP: the (fallback) app menu * * Menu the compositor should display for a given window */ typedef enum { META_WINDOW_MENU_WM, META_WINDOW_MENU_APP } MetaWindowMenuType; /** * MetaFrameBorders: * @visible: inner visible portion of frame border * @invisible: outer invisible portion of frame border * @total: sum of the two borders above */ typedef struct _MetaFrameBorders MetaFrameBorders; struct _MetaFrameBorders { /* The frame border is made up of two pieces - an inner visible portion * and an outer portion that is invisible but responds to events. */ GtkBorder visible; GtkBorder invisible; /* For convenience, we have a "total" border which is equal to the sum * of the two borders above. */ GtkBorder total; }; /* sets all dimensions to zero */ META_EXPORT void meta_frame_borders_clear (MetaFrameBorders *self); /* should investigate changing these to whatever most apps use */ #define META_ICON_WIDTH 96 #define META_ICON_HEIGHT 96 #define META_MINI_ICON_WIDTH 16 #define META_MINI_ICON_HEIGHT 16 #define META_DEFAULT_ICON_NAME "window" /* Main loop priorities determine when activity in the GLib * will take precendence over the others. Priorities are sometimes * used to enforce ordering: give A a higher priority than B if * A must occur before B. But that poses a problem since then * if A occurs frequently enough, B will never occur. * * Anything we want to occur more or less immediately should * have a priority of G_PRIORITY_DEFAULT. When we want to * coelesce multiple things together, the appropriate place to * do it is usually META_PRIORITY_BEFORE_REDRAW. * * Note that its usually better to use meta_later_add() rather * than calling g_idle_add() directly; this will make sure things * get run when added from a clutter event handler without * waiting for another repaint cycle. * * If something has a priority lower than the redraw priority * (such as a default priority idle), then it may be arbitrarily * delayed. This happens if the screen is updating rapidly: we * are spending all our time either redrawing or waiting for a * vblank-synced buffer swap. (When X is improved to allow * clutter to do the buffer-swap asychronously, this will get * better.) */ /* G_PRIORITY_DEFAULT: * events * many timeouts */ /* GTK_PRIORITY_RESIZE: (G_PRIORITY_HIGH_IDLE + 10) */ #define META_PRIORITY_RESIZE (G_PRIORITY_HIGH_IDLE + 15) /* GTK_PRIORITY_REDRAW: (G_PRIORITY_HIGH_IDLE + 20) */ #define META_PRIORITY_BEFORE_REDRAW (G_PRIORITY_HIGH_IDLE + 40) /* calc-showing idle * update-icon idle */ /* CLUTTER_PRIORITY_REDRAW: (G_PRIORITY_HIGH_IDLE + 50) */ #define META_PRIORITY_REDRAW (G_PRIORITY_HIGH_IDLE + 50) /* ==== Anything below here can be starved arbitrarily ==== */ /* G_PRIORITY_DEFAULT_IDLE: * Mutter plugin unloading */ #define META_PRIORITY_PREFS_NOTIFY (G_PRIORITY_DEFAULT_IDLE + 10) /************************************************************/ /** * MetaStackLayer: * @META_LAYER_DESKTOP: Desktop layer * @META_LAYER_BOTTOM: Bottom layer * @META_LAYER_NORMAL: Normal layer * @META_LAYER_TOP: Top layer * @META_LAYER_DOCK: Dock layer * @META_LAYER_OVERRIDE_REDIRECT: Override-redirect layer * @META_LAYER_LAST: Marks the end of the #MetaStackLayer enumeration * * Layers a window can be in. * These MUST be in the order of stacking. */ typedef enum { META_LAYER_DESKTOP = 0, META_LAYER_BOTTOM = 1, META_LAYER_NORMAL = 2, META_LAYER_TOP = 4, /* Same as DOCK; see EWMH and bug 330717 */ META_LAYER_DOCK = 4, META_LAYER_OVERRIDE_REDIRECT = 7, META_LAYER_LAST = 8 } MetaStackLayer; /** * MetaPlacementMode: * @META_PLACEMENT_MODE_AUTOMATIC: Automatic * @META_PLACEMENT_MODE_POINTER: Pointer * @META_PLACEMENT_MODE_MANUAL: Manual * @META_PLACEMENT_MODE_CENTER: Center * * How new windows should be placed. */ typedef enum { META_PLACEMENT_MODE_AUTOMATIC, META_PLACEMENT_MODE_POINTER, META_PLACEMENT_MODE_MANUAL, META_PLACEMENT_MODE_CENTER } MetaPlacementMode; /* MetaGravity: (skip) * * Identical to the corresponding gravity value macros from libX11. */ typedef enum _MetaGravity { META_GRAVITY_NONE = 0, META_GRAVITY_NORTH_WEST = 1, META_GRAVITY_NORTH = 2, META_GRAVITY_NORTH_EAST = 3, META_GRAVITY_WEST = 4, META_GRAVITY_CENTER = 5, META_GRAVITY_EAST = 6, META_GRAVITY_SOUTH_WEST = 7, META_GRAVITY_SOUTH = 8, META_GRAVITY_SOUTH_EAST = 9, META_GRAVITY_STATIC = 10, } MetaGravity; /* * Background transition */ typedef enum { META_X11_BACKGROUND_TRANSITION_NONE, META_X11_BACKGROUND_TRANSITION_FADEIN, META_X11_BACKGROUND_TRANSITION_BLEND } MetaX11BackgroundTransition; #endif muffin-6.4.1/src/meta/meson.build0000664000175000017500000000444614723361714015631 0ustar fabiofabiomutter_public_headers = [ 'barrier.h', 'boxes.h', 'common.h', 'compositor.h', 'compositor-mutter.h', 'display.h', 'group.h', 'keybindings.h', 'main.h', 'meta-backend.h', 'meta-background.h', 'meta-background-actor.h', 'meta-background-group.h', 'meta-background-image.h', 'meta-close-dialog.h', 'meta-cursor-tracker.h', 'meta-dnd.h', 'meta-idle-monitor.h', 'meta-inhibit-shortcuts-dialog.h', 'meta-launch-context.h', 'meta-monitor-manager.h', 'meta-plugin.h', 'meta-remote-access-controller.h', 'meta-selection.h', 'meta-selection-source.h', 'meta-selection-source-memory.h', 'meta-settings.h', 'meta-shadow-factory.h', 'meta-shaped-texture.h', 'meta-sound-player.h', 'meta-stage.h', 'meta-startup-notification.h', 'meta-window-actor.h', 'meta-window-group.h', 'meta-window-shape.h', 'meta-workspace-manager.h', 'prefs.h', 'theme.h', 'types.h', 'util.h', 'window.h', 'workspace.h', ] if have_x11 mutter_public_headers += [ 'meta-x11-background-actor.h', 'meta-x11-display.h', 'meta-x11-errors.h' ] endif install_headers(mutter_public_headers, subdir: mutter_includesubdir ) mutter_public_header_files = files(mutter_public_headers) mutter_enum_types = gnome.mkenums('meta-enum-types', sources: [mutter_public_headers], c_template: 'meta-enum-types.c.in', h_template: 'meta-enum-types.h.in', install_dir: mutter_includedir, install_header: true, ) mutter_version_array = meson.project_version().split('.') mutter_version_major = mutter_version_array[0] mutter_version_minor = mutter_version_array[1] mutter_version_micro = mutter_version_array[2] mutter_version_cdata = configuration_data() mutter_version_cdata.set('MUTTER_MAJOR_VERSION', '@0@'.format(mutter_version_major)) mutter_version_cdata.set('MUTTER_MINOR_VERSION', '@0@'.format(mutter_version_minor)) mutter_version_cdata.set('MUTTER_MICRO_VERSION', '@0@'.format(mutter_version_micro)) mutter_version_cdata.set('MUTTER_PLUGIN_API_VERSION', '@0@'.format(mutter_plugin_api_version)) mutter_version = configure_file( input: 'meta-version.h.in', output: 'meta-version.h', configuration: mutter_version_cdata, install_dir: mutter_includedir, install: true, ) muffin-6.4.1/src/meta/compositor.h0000664000175000017500000001415514723361714016034 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_COMPOSITOR_H #define META_COMPOSITOR_H #include #include #include #include #include #define META_TYPE_COMPOSITOR (meta_compositor_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaCompositor, meta_compositor, META, COMPOSITOR, GObject) /** * MetaCompEffect: * @META_COMP_EFFECT_CREATE: The window is newly created * (also used for a window that was previously on a different * workspace and is changed to become visible on the active * workspace.) * @META_COMP_EFFECT_UNMINIMIZE: The window should be shown * as unminimizing from its icon geometry. * @META_COMP_EFFECT_DESTROY: The window is being destroyed * @META_COMP_EFFECT_MINIMIZE: The window should be shown * as minimizing to its icon geometry. * @META_COMP_EFFECT_NONE: No effect, the window should be * shown or hidden immediately. * * Indicates the appropriate effect to show the user for * meta_compositor_show_window() and meta_compositor_hide_window() */ typedef enum { META_COMP_EFFECT_CREATE, META_COMP_EFFECT_UNMINIMIZE, META_COMP_EFFECT_DESTROY, META_COMP_EFFECT_MINIMIZE, META_COMP_EFFECT_NONE } MetaCompEffect; typedef enum { META_SIZE_CHANGE_MAXIMIZE, META_SIZE_CHANGE_UNMAXIMIZE, META_SIZE_CHANGE_FULLSCREEN, META_SIZE_CHANGE_UNFULLSCREEN, META_SIZE_CHANGE_TILE } MetaSizeChange; META_EXPORT void meta_compositor_destroy (MetaCompositor *compositor); META_EXPORT void meta_compositor_manage (MetaCompositor *compositor); META_EXPORT void meta_compositor_unmanage (MetaCompositor *compositor); META_EXPORT void meta_compositor_window_shape_changed (MetaCompositor *compositor, MetaWindow *window); META_EXPORT void meta_compositor_window_opacity_changed (MetaCompositor *compositor, MetaWindow *window); META_EXPORT gboolean meta_compositor_filter_keybinding (MetaCompositor *compositor, MetaKeyBinding *binding); META_EXPORT void meta_compositor_add_window (MetaCompositor *compositor, MetaWindow *window); META_EXPORT void meta_compositor_remove_window (MetaCompositor *compositor, MetaWindow *window); META_EXPORT void meta_compositor_show_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect); META_EXPORT void meta_compositor_hide_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect); META_EXPORT void meta_compositor_switch_workspace (MetaCompositor *compositor, MetaWorkspace *from, MetaWorkspace *to, MetaMotionDirection direction); META_EXPORT void meta_compositor_size_change_window (MetaCompositor *compositor, MetaWindow *window, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect); META_EXPORT void meta_compositor_sync_window_geometry (MetaCompositor *compositor, MetaWindow *window, gboolean did_placement); META_EXPORT void meta_compositor_sync_updates_frozen (MetaCompositor *compositor, MetaWindow *window); META_EXPORT void meta_compositor_queue_frame_drawn (MetaCompositor *compositor, MetaWindow *window, gboolean no_delay_frame); META_EXPORT void meta_compositor_sync_stack (MetaCompositor *compositor, GList *stack); META_EXPORT void meta_compositor_flash_display (MetaCompositor *compositor, MetaDisplay *display); META_EXPORT void meta_compositor_show_tile_preview (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number); META_EXPORT void meta_compositor_hide_tile_preview (MetaCompositor *compositor); META_EXPORT void meta_compositor_show_window_menu (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, int x, int y); META_EXPORT void meta_compositor_show_window_menu_for_rect (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect); #endif /* META_COMPOSITOR_H */ muffin-6.4.1/src/meta/barrier.h0000664000175000017500000000705614723361714015266 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; c-basic-offset: 2; -*- */ #ifndef __META_BARRIER_H__ #define __META_BARRIER_H__ #include #include G_BEGIN_DECLS #define META_TYPE_BARRIER (meta_barrier_get_type ()) #define META_BARRIER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_BARRIER, MetaBarrier)) #define META_BARRIER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_BARRIER, MetaBarrierClass)) #define META_IS_BARRIER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_BARRIER)) #define META_IS_BARRIER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_BARRIER)) #define META_BARRIER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_BARRIER, MetaBarrierClass)) typedef struct _MetaBarrier MetaBarrier; typedef struct _MetaBarrierClass MetaBarrierClass; typedef struct _MetaBarrierPrivate MetaBarrierPrivate; typedef struct _MetaBarrierEvent MetaBarrierEvent; /** * MetaBarrier: * * The MetaBarrier structure contains * only private data and should be accessed using the provided API * **/ struct _MetaBarrier { GObject parent; MetaBarrierPrivate *priv; }; /** * MetaBarrierClass: * * The MetaBarrierClass structure contains only * private data. */ struct _MetaBarrierClass { /*< private >*/ GObjectClass parent_class; }; META_EXPORT GType meta_barrier_get_type (void) G_GNUC_CONST; META_EXPORT gboolean meta_barrier_is_active (MetaBarrier *barrier); META_EXPORT void meta_barrier_destroy (MetaBarrier *barrier); META_EXPORT void meta_barrier_release (MetaBarrier *barrier, MetaBarrierEvent *event); /** * MetaBarrierDirection: * @META_BARRIER_DIRECTION_POSITIVE_X: Positive direction in the X axis * @META_BARRIER_DIRECTION_POSITIVE_Y: Positive direction in the Y axis * @META_BARRIER_DIRECTION_NEGATIVE_X: Negative direction in the X axis * @META_BARRIER_DIRECTION_NEGATIVE_Y: Negative direction in the Y axis */ /* Keep in sync with XFixes */ typedef enum { META_BARRIER_DIRECTION_POSITIVE_X = 1 << 0, META_BARRIER_DIRECTION_POSITIVE_Y = 1 << 1, META_BARRIER_DIRECTION_NEGATIVE_X = 1 << 2, META_BARRIER_DIRECTION_NEGATIVE_Y = 1 << 3, } MetaBarrierDirection; /** * MetaBarrierEvent: * @event_id: A unique integer ID identifying a * consecutive series of motions at or along the barrier * @time: Server time, in milliseconds * @dt: Server time, in milliseconds, since the last event * sent for this barrier * @x: The cursor X position in screen coordinates * @y: The cursor Y position in screen coordinates. * @dx: If the cursor hadn't been constrained, the delta * of X movement past the barrier, in screen coordinates * @dy: If the cursor hadn't been constrained, the delta * of X movement past the barrier, in screen coordinates * @released: A boolean flag, %TRUE if this event generated * by the pointer leaving the barrier as a result of a client * calling meta_barrier_release() (will be set only for * MetaBarrier::leave signals) * @grabbed: A boolean flag, %TRUE if the pointer was grabbed * at the time this event was sent */ struct _MetaBarrierEvent { /* < private > */ volatile guint ref_count; /* < public > */ int event_id; int dt; guint32 time; double x; double y; double dx; double dy; gboolean released; gboolean grabbed; }; #define META_TYPE_BARRIER_EVENT (meta_barrier_event_get_type ()) META_EXPORT GType meta_barrier_event_get_type (void) G_GNUC_CONST; G_END_DECLS #endif /* __META_BARRIER_H__ */ muffin-6.4.1/src/meta/keybindings.h0000664000175000017500000000302414723361714016135 0ustar fabiofabio/* * Copyright (C) 2009 Intel Corporation. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_KEYBINDINGS_H #define META_KEYBINDINGS_H #include #include #define META_TYPE_KEY_BINDING (meta_key_binding_get_type ()) META_EXPORT const char *meta_key_binding_get_name (MetaKeyBinding *binding); META_EXPORT MetaVirtualModifier meta_key_binding_get_modifiers (MetaKeyBinding *binding); META_EXPORT guint meta_key_binding_get_mask (MetaKeyBinding *binding); META_EXPORT gboolean meta_key_binding_is_builtin (MetaKeyBinding *binding); META_EXPORT gboolean meta_key_binding_is_reversed (MetaKeyBinding *binding); META_EXPORT gboolean meta_keybindings_set_custom_handler (const gchar *name, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data); #endif muffin-6.4.1/src/meta/meta-idle-monitor.h0000664000175000017500000000532414723361714017162 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_IDLE_MONITOR_H #define META_IDLE_MONITOR_H #include #include #define META_TYPE_IDLE_MONITOR (meta_idle_monitor_get_type ()) #define META_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitor)) #define META_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass)) #define META_IS_IDLE_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_IDLE_MONITOR)) #define META_IS_IDLE_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_IDLE_MONITOR)) #define META_IDLE_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_IDLE_MONITOR, MetaIdleMonitorClass)) typedef struct _MetaIdleMonitor MetaIdleMonitor; typedef struct _MetaIdleMonitorClass MetaIdleMonitorClass; META_EXPORT GType meta_idle_monitor_get_type (void); typedef void (*MetaIdleMonitorWatchFunc) (MetaIdleMonitor *monitor, guint watch_id, gpointer user_data); META_EXPORT MetaIdleMonitor *meta_idle_monitor_get_core (void); META_EXPORT guint meta_idle_monitor_add_idle_watch (MetaIdleMonitor *monitor, guint64 interval_msec, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify); META_EXPORT guint meta_idle_monitor_add_user_active_watch (MetaIdleMonitor *monitor, MetaIdleMonitorWatchFunc callback, gpointer user_data, GDestroyNotify notify); META_EXPORT void meta_idle_monitor_remove_watch (MetaIdleMonitor *monitor, guint id); META_EXPORT gint64 meta_idle_monitor_get_idletime (MetaIdleMonitor *monitor); #endif muffin-6.4.1/src/meta/prefs.h0000664000175000017500000004743014723361714014757 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter preferences */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_PREFS_H #define META_PREFS_H /* This header is a "common" one between the UI and core side */ #include #include #include #include #include /** * MetaPreference: * @META_PREF_MOUSE_BUTTON_MODS: mouse button modifiers * @META_PREF_MOUSE_BUTTON_ZOOM_MODS: mouse button zoom modifiers * @META_PREF_FOCUS_MODE: focus mode * @META_PREF_FOCUS_NEW_WINDOWS: focus new windows * @META_PREF_ATTACH_MODAL_DIALOGS: attach modal dialogs * @META_PREF_RAISE_ON_CLICK: raise on click * @META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR: action double click titlebar * @META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR: action middle click titlebar * @META_PREF_ACTION_RIGHT_CLICK_TITLEBAR: action right click titlebar * @META_PREF_ACTION_SCROLL_WHEEL_TITLEBAR: action scroll wheel titlebar * @META_PREF_AUTO_RAISE: auto-raise * @META_PREF_AUTO_RAISE_DELAY: auto-raise delay * @META_PREF_FOCUS_CHANGE_ON_POINTER_REST: focus change on pointer rest * @META_PREF_TITLEBAR_FONT: title-bar font * @META_PREF_NUM_WORKSPACES: number of workspaces * @META_PREF_DYNAMIC_WORKSPACES: dynamic workspaces * @META_PREF_KEYBINDINGS: keybindings * @META_PREF_DISABLE_WORKAROUNDS: disable workarounds * @META_PREF_BUTTON_LAYOUT: button layout * @META_PREF_WORKSPACE_NAMES: workspace names * @META_PREF_VISUAL_BELL: visual bell * @META_PREF_AUDIBLE_BELL: audible bell * @META_PREF_VISUAL_BELL_TYPE: visual bell type * @META_PREF_GNOME_ACCESSIBILITY: GNOME accessibility * @META_PREF_GNOME_ANIMATIONS: GNOME animations * @META_PREF_CURSOR_THEME: cursor theme * @META_PREF_CURSOR_SIZE: cursor size * @META_PREF_RESIZE_WITH_RIGHT_BUTTON: resize with right button * @META_PREF_EDGE_TILING: edge tiling * @META_PREF_FORCE_FULLSCREEN: force fullscreen * @META_PREF_WORKSPACES_ONLY_ON_PRIMARY: workspaces only on primary * @META_PREF_DRAGGABLE_BORDER_WIDTH: draggable border width * @META_PREF_AUTO_MAXIMIZE: auto-maximize * @META_PREF_NEW_WINDOW_PLACEMENT_MODE: new window placement mode * @META_PREF_DRAG_THRESHOLD: drag threshold * @META_PREF_LOCATE_POINTER: show pointer location * @META_PREF_GTK_THEME: gtk theme name * @META_PREF_BELL_SOUND: sound to use for audible event bell * @META_PREF_BRING_WINDOWS_TO_CURRENT_WORKSPACE: behavior when use activates an off-workspace window. * @META_PREF_INVERT_WORKSPACE_FLIP_DIRECTION: Flip arrow direction when changing workspaces during a window grab move. */ /* Keep in sync with GSettings schemas! */ typedef enum { META_PREF_MOUSE_BUTTON_MODS, META_PREF_MOUSE_BUTTON_ZOOM_MODS, META_PREF_FOCUS_MODE, META_PREF_FOCUS_NEW_WINDOWS, META_PREF_ATTACH_MODAL_DIALOGS, META_PREF_RAISE_ON_CLICK, META_PREF_ACTION_DOUBLE_CLICK_TITLEBAR, META_PREF_ACTION_MIDDLE_CLICK_TITLEBAR, META_PREF_ACTION_RIGHT_CLICK_TITLEBAR, META_PREF_ACTION_SCROLL_WHEEL_TITLEBAR, META_PREF_AUTO_RAISE, META_PREF_AUTO_RAISE_DELAY, META_PREF_FOCUS_CHANGE_ON_POINTER_REST, META_PREF_TITLEBAR_FONT, META_PREF_NUM_WORKSPACES, META_PREF_DYNAMIC_WORKSPACES, META_PREF_KEYBINDINGS, META_PREF_DISABLE_WORKAROUNDS, META_PREF_BUTTON_LAYOUT, META_PREF_WORKSPACE_NAMES, META_PREF_VISUAL_BELL, META_PREF_AUDIBLE_BELL, META_PREF_VISUAL_BELL_TYPE, META_PREF_GNOME_ACCESSIBILITY, META_PREF_GNOME_ANIMATIONS, META_PREF_CURSOR_THEME, META_PREF_CURSOR_SIZE, META_PREF_RESIZE_WITH_RIGHT_BUTTON, META_PREF_EDGE_TILING, META_PREF_FORCE_FULLSCREEN, META_PREF_WORKSPACES_ONLY_ON_PRIMARY, META_PREF_DRAGGABLE_BORDER_WIDTH, META_PREF_AUTO_MAXIMIZE, META_PREF_NEW_WINDOW_PLACEMENT_MODE, META_PREF_DRAG_THRESHOLD, META_PREF_LOCATE_POINTER, META_PREF_CHECK_ALIVE_TIMEOUT, META_PREF_BACKGROUND_TRANSITION, META_PREF_UNREDIRECT_FULLSCREEN_WINDOWS, META_PREF_WORKSPACE_CYCLE, META_PREF_MIN_WIN_OPACITY, META_PREF_MOUSE_ZOOM_ENABLED, META_PREF_TILE_MAXIMIZE, META_PREF_GTK_THEME, META_PREF_BELL_SOUND, META_PREF_BRING_WINDOWS_TO_CURRENT_WORKSPACE, META_PREF_INVERT_WORKSPACE_FLIP_DIRECTION } MetaPreference; typedef void (* MetaPrefsChangedFunc) (MetaPreference pref, gpointer user_data); META_EXPORT void meta_prefs_add_listener (MetaPrefsChangedFunc func, gpointer user_data); META_EXPORT void meta_prefs_remove_listener (MetaPrefsChangedFunc func, gpointer user_data); META_EXPORT void meta_prefs_init (void); META_EXPORT const char* meta_preference_to_string (MetaPreference pref); META_EXPORT MetaVirtualModifier meta_prefs_get_mouse_button_mods (void); META_EXPORT MetaVirtualModifier meta_prefs_get_mouse_button_zoom_mods (void); META_EXPORT gboolean meta_prefs_get_mouse_zoom_enabled (void); META_EXPORT gint meta_prefs_get_mouse_button_resize (void); META_EXPORT gint meta_prefs_get_mouse_button_menu (void); META_EXPORT CDesktopFocusMode meta_prefs_get_focus_mode (void); META_EXPORT CDesktopFocusNewWindows meta_prefs_get_focus_new_windows (void); META_EXPORT gboolean meta_prefs_get_bring_windows_to_current_workspace (void); META_EXPORT gboolean meta_prefs_get_attach_modal_dialogs (void); META_EXPORT gboolean meta_prefs_get_raise_on_click (void); /* returns NULL if GTK default should be used */ META_EXPORT const PangoFontDescription* meta_prefs_get_titlebar_font (void); META_EXPORT int meta_prefs_get_num_workspaces (void); META_EXPORT gboolean meta_prefs_get_dynamic_workspaces (void); META_EXPORT gboolean meta_prefs_get_disable_workarounds (void); META_EXPORT gboolean meta_prefs_get_auto_raise (void); META_EXPORT int meta_prefs_get_auto_raise_delay (void); META_EXPORT gboolean meta_prefs_get_focus_change_on_pointer_rest (void); META_EXPORT gboolean meta_prefs_get_gnome_accessibility (void); META_EXPORT gboolean meta_prefs_get_gnome_animations (void); META_EXPORT gboolean meta_prefs_get_edge_tiling (void); META_EXPORT gboolean meta_prefs_get_tile_maximize (void); META_EXPORT gboolean meta_prefs_get_auto_maximize (void); META_EXPORT MetaPlacementMode meta_prefs_get_new_window_placement_mode (void); META_EXPORT gboolean meta_prefs_get_show_fallback_app_menu (void); META_EXPORT void meta_prefs_set_show_fallback_app_menu (gboolean whether); META_EXPORT void meta_prefs_get_button_layout (MetaButtonLayout *button_layout); /* Double, right, middle click can be configured to any titlebar meta-action */ META_EXPORT CDesktopTitlebarAction meta_prefs_get_action_double_click_titlebar (void); META_EXPORT CDesktopTitlebarAction meta_prefs_get_action_middle_click_titlebar (void); META_EXPORT CDesktopTitlebarAction meta_prefs_get_action_right_click_titlebar (void); META_EXPORT CDesktopTitlebarScrollAction meta_prefs_get_action_scroll_wheel_titlebar (void); META_EXPORT void meta_prefs_set_num_workspaces (int n_workspaces); META_EXPORT const char* meta_prefs_get_workspace_name (int i); META_EXPORT void meta_prefs_change_workspace_name (int i, const char *name); META_EXPORT const char* meta_prefs_get_cursor_theme (void); META_EXPORT int meta_prefs_get_cursor_size (void); META_EXPORT gboolean meta_prefs_get_compositing_manager (void); META_EXPORT gboolean meta_prefs_get_force_fullscreen (void); META_EXPORT void meta_prefs_set_force_fullscreen (gboolean whether); META_EXPORT gboolean meta_prefs_get_workspaces_only_on_primary (void); META_EXPORT int meta_prefs_get_draggable_border_width (void); META_EXPORT int meta_prefs_get_drag_threshold (void); META_EXPORT gboolean meta_prefs_get_unredirect_fullscreen_windows (void); META_EXPORT gboolean meta_prefs_get_workspace_cycle (void); META_EXPORT gint meta_prefs_get_min_win_opacity (void); META_EXPORT const char* meta_prefs_get_bell_sound (void); META_EXPORT gboolean meta_prefs_get_invert_flip_direction (void); /** * MetaKeyBindingAction: * @META_KEYBINDING_ACTION_NONE: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_1: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_2: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_3: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_4: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_5: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_6: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_7: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_8: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_9: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_10: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_11: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_12: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_LEFT: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_RIGHT: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_UP: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_DOWN: FILLME * @META_KEYBINDING_ACTION_WORKSPACE_LAST: FILLME * @META_KEYBINDING_ACTION_SWITCH_APPLICATIONS: FILLME * @META_KEYBINDING_ACTION_SWITCH_APPLICATIONS_BACKWARD: FILLME * @META_KEYBINDING_ACTION_SWITCH_GROUP: FILLME * @META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD: FILLME * @META_KEYBINDING_ACTION_SWITCH_WINDOWS: FILLME * @META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD: FILLME * @META_KEYBINDING_ACTION_SWITCH_PANELS: FILLME * @META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD: FILLME * @META_KEYBINDING_ACTION_CYCLE_GROUP: FILLME * @META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD: FILLME * @META_KEYBINDING_ACTION_CYCLE_WINDOWS: FILLME * @META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD: FILLME * @META_KEYBINDING_ACTION_CYCLE_PANELS: FILLME * @META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD: FILLME * @META_KEYBINDING_ACTION_SHOW_DESKTOP: FILLME * @META_KEYBINDING_ACTION_PANEL_MAIN_MENU: FILLME * @META_KEYBINDING_ACTION_PANEL_RUN_DIALOG: FILLME * @META_KEYBINDING_ACTION_TOGGLE_RECORDING: FILLME * @META_KEYBINDING_ACTION_SET_SPEW_MARK: FILLME * @META_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU: FILLME * @META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN: FILLME * @META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_LEFT: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_RIGHT: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_UP: FILLME * @META_KEYBINDING_ACTION_PUSH_TILE_DOWN: FILLME * @META_KEYBINDING_ACTION_TOGGLE_ABOVE: FILLME * @META_KEYBINDING_ACTION_MAXIMIZE: FILLME * @META_KEYBINDING_ACTION_UNMAXIMIZE: FILLME * @META_KEYBINDING_ACTION_TOGGLE_SHADED: FILLME * @META_KEYBINDING_ACTION_MINIMIZE: FILLME * @META_KEYBINDING_ACTION_CLOSE: FILLME * @META_KEYBINDING_ACTION_BEGIN_MOVE: FILLME * @META_KEYBINDING_ACTION_BEGIN_RESIZE: FILLME * @META_KEYBINDING_ACTION_TOGGLE_ON_ALL_WORKSPACES: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_1: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_2: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_3: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_4: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_5: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_6: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_7: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_8: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_9: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_10: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_11: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LEFT: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_RIGHT: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_UP: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_DOWN: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LAST: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_MONITOR_LEFT: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_MONITOR_RIGHT: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_MONITOR_UP: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_MONITOR_DOWN: FILLME * @META_KEYBINDING_ACTION_RAISE_OR_LOWER: FILLME * @META_KEYBINDING_ACTION_RAISE: FILLME * @META_KEYBINDING_ACTION_LOWER: FILLME * @META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY: FILLME * @META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_CORNER_NW: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_CORNER_NE: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_CORNER_SW: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_CORNER_SE: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_SIDE_N: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_SIDE_S: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_SIDE_E: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_SIDE_W: FILLME * @META_KEYBINDING_ACTION_MOVE_TO_CENTER: FILLME * META_KEYBINDING_ACTION_INCREASE_OPACITY: FILLME, * META_KEYBINDING_ACTION_DECREASE_OPACITY: FILLME, * @META_KEYBINDING_ACTION_OVERLAY_KEY: FILLME * @META_KEYBINDING_ACTION_LOCATE_POINTER_KEY: FILLME * @META_KEYBINDING_ACTION_ALWAYS_ON_TOP: FILLME * @META_KEYBINDING_ACTION_LAST: FILLME */ /* XXX FIXME This should be x-macroed, but isn't yet because it would be * difficult (or perhaps impossible) to add the suffixes using the current * system. It needs some more thought, perhaps after the current system * evolves a little. */ typedef enum _MetaKeyBindingAction { META_KEYBINDING_ACTION_NONE, META_KEYBINDING_ACTION_WORKSPACE_1, META_KEYBINDING_ACTION_WORKSPACE_2, META_KEYBINDING_ACTION_WORKSPACE_3, META_KEYBINDING_ACTION_WORKSPACE_4, META_KEYBINDING_ACTION_WORKSPACE_5, META_KEYBINDING_ACTION_WORKSPACE_6, META_KEYBINDING_ACTION_WORKSPACE_7, META_KEYBINDING_ACTION_WORKSPACE_8, META_KEYBINDING_ACTION_WORKSPACE_9, META_KEYBINDING_ACTION_WORKSPACE_10, META_KEYBINDING_ACTION_WORKSPACE_11, META_KEYBINDING_ACTION_WORKSPACE_12, META_KEYBINDING_ACTION_WORKSPACE_LEFT, META_KEYBINDING_ACTION_WORKSPACE_RIGHT, META_KEYBINDING_ACTION_WORKSPACE_UP, META_KEYBINDING_ACTION_WORKSPACE_DOWN, META_KEYBINDING_ACTION_WORKSPACE_LAST, META_KEYBINDING_ACTION_SWITCH_APPLICATIONS, META_KEYBINDING_ACTION_SWITCH_APPLICATIONS_BACKWARD, META_KEYBINDING_ACTION_SWITCH_GROUP, META_KEYBINDING_ACTION_SWITCH_GROUP_BACKWARD, META_KEYBINDING_ACTION_SWITCH_WINDOWS, META_KEYBINDING_ACTION_SWITCH_WINDOWS_BACKWARD, META_KEYBINDING_ACTION_SWITCH_PANELS, META_KEYBINDING_ACTION_SWITCH_PANELS_BACKWARD, META_KEYBINDING_ACTION_CYCLE_GROUP, META_KEYBINDING_ACTION_CYCLE_GROUP_BACKWARD, META_KEYBINDING_ACTION_CYCLE_WINDOWS, META_KEYBINDING_ACTION_CYCLE_WINDOWS_BACKWARD, META_KEYBINDING_ACTION_CYCLE_PANELS, META_KEYBINDING_ACTION_CYCLE_PANELS_BACKWARD, META_KEYBINDING_ACTION_SHOW_DESKTOP, META_KEYBINDING_ACTION_PANEL_MAIN_MENU, META_KEYBINDING_ACTION_PANEL_RUN_DIALOG, META_KEYBINDING_ACTION_TOGGLE_RECORDING, META_KEYBINDING_ACTION_SET_SPEW_MARK, META_KEYBINDING_ACTION_ACTIVATE_WINDOW_MENU, META_KEYBINDING_ACTION_TOGGLE_FULLSCREEN, META_KEYBINDING_ACTION_TOGGLE_MAXIMIZED, META_KEYBINDING_ACTION_PUSH_TILE_LEFT, META_KEYBINDING_ACTION_PUSH_TILE_RIGHT, META_KEYBINDING_ACTION_PUSH_TILE_UP, META_KEYBINDING_ACTION_PUSH_TILE_DOWN, META_KEYBINDING_ACTION_TOGGLE_ABOVE, META_KEYBINDING_ACTION_MAXIMIZE, META_KEYBINDING_ACTION_UNMAXIMIZE, META_KEYBINDING_ACTION_TOGGLE_SHADED, META_KEYBINDING_ACTION_MINIMIZE, META_KEYBINDING_ACTION_CLOSE, META_KEYBINDING_ACTION_BEGIN_MOVE, META_KEYBINDING_ACTION_BEGIN_RESIZE, META_KEYBINDING_ACTION_TOGGLE_ON_ALL_WORKSPACES, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_1, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_2, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_3, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_4, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_5, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_6, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_7, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_8, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_9, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_10, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_11, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_12, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LEFT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_RIGHT, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_UP, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_DOWN, META_KEYBINDING_ACTION_MOVE_TO_WORKSPACE_LAST, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_LEFT, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_RIGHT, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_UP, META_KEYBINDING_ACTION_MOVE_TO_MONITOR_DOWN, META_KEYBINDING_ACTION_RAISE_OR_LOWER, META_KEYBINDING_ACTION_RAISE, META_KEYBINDING_ACTION_LOWER, META_KEYBINDING_ACTION_MAXIMIZE_VERTICALLY, META_KEYBINDING_ACTION_MAXIMIZE_HORIZONTALLY, META_KEYBINDING_ACTION_MOVE_TO_CORNER_NW, META_KEYBINDING_ACTION_MOVE_TO_CORNER_NE, META_KEYBINDING_ACTION_MOVE_TO_CORNER_SW, META_KEYBINDING_ACTION_MOVE_TO_CORNER_SE, META_KEYBINDING_ACTION_MOVE_TO_SIDE_N, META_KEYBINDING_ACTION_MOVE_TO_SIDE_S, META_KEYBINDING_ACTION_MOVE_TO_SIDE_E, META_KEYBINDING_ACTION_MOVE_TO_SIDE_W, META_KEYBINDING_ACTION_MOVE_TO_CENTER, META_KEYBINDING_ACTION_INCREASE_OPACITY, META_KEYBINDING_ACTION_DECREASE_OPACITY, META_KEYBINDING_ACTION_OVERLAY_KEY, META_KEYBINDING_ACTION_LOCATE_POINTER_KEY, META_KEYBINDING_ACTION_ISO_NEXT_GROUP, META_KEYBINDING_ACTION_ALWAYS_ON_TOP, META_KEYBINDING_ACTION_SWITCH_MONITOR, META_KEYBINDING_ACTION_ROTATE_MONITOR, META_KEYBINDING_ACTION_LAST } MetaKeyBindingAction; /** * MetaKeyBindingFlags: * @META_KEY_BINDING_NONE: none * @META_KEY_BINDING_PER_WINDOW: per-window * @META_KEY_BINDING_BUILTIN: built-in * @META_KEY_BINDING_IS_REVERSED: is reversed * @META_KEY_BINDING_NON_MASKABLE: always active * @META_KEY_BINDING_NO_AUTO_GRAB: not grabbed automatically */ typedef enum { META_KEY_BINDING_NONE, META_KEY_BINDING_PER_WINDOW = 1 << 0, META_KEY_BINDING_BUILTIN = 1 << 1, META_KEY_BINDING_IS_REVERSED = 1 << 2, META_KEY_BINDING_NON_MASKABLE = 1 << 3, META_KEY_BINDING_IGNORE_AUTOREPEAT = 1 << 4, META_KEY_BINDING_NO_AUTO_GRAB = 1 << 5, } MetaKeyBindingFlags; /** * MetaKeyHandlerFunc: * @display: a #MetaDisplay * @window: a #MetaWindow * @event: (type gpointer): a #ClutterKeyEvent * @binding: a #MetaKeyBinding * @user_data: data passed to the function * */ typedef void (* MetaKeyHandlerFunc) (MetaDisplay *display, MetaWindow *window, ClutterKeyEvent *event, MetaKeyBinding *binding, gpointer user_data); META_EXPORT GType meta_key_binding_get_type (void); META_EXPORT MetaKeyBindingAction meta_prefs_get_keybinding_action (const char *name); META_EXPORT gboolean meta_prefs_get_visual_bell (void); META_EXPORT gboolean meta_prefs_bell_is_audible (void); META_EXPORT CDesktopVisualBellType meta_prefs_get_visual_bell_type (void); META_EXPORT unsigned int meta_prefs_get_check_alive_timeout (void); MetaX11BackgroundTransition meta_prefs_get_background_transition (void); #endif muffin-6.4.1/src/meta/meta-selection-source.h0000664000175000017500000000613614723361714020045 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_SELECTION_SOURCE_H #define META_SELECTION_SOURCE_H #include #include typedef enum { META_SELECTION_PRIMARY, META_SELECTION_CLIPBOARD, META_SELECTION_DND, META_N_SELECTION_TYPES, } MetaSelectionType; typedef struct _MetaSelectionSourceClass MetaSelectionSourceClass; typedef struct _MetaSelectionSource MetaSelectionSource; #define META_TYPE_SELECTION_SOURCE (meta_selection_source_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaSelectionSource, meta_selection_source, META, SELECTION_SOURCE, GObject) struct _MetaSelectionSourceClass { GObjectClass parent_class; void (* activated) (MetaSelectionSource *source); void (* deactivated) (MetaSelectionSource *source); GList * (* get_mimetypes) (MetaSelectionSource *source); void (* read_async) (MetaSelectionSource *source, const gchar *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GInputStream * (* read_finish) (MetaSelectionSource *source, GAsyncResult *result, GError **error); }; META_EXPORT void meta_selection_source_read_async (MetaSelectionSource *source, const gchar *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); META_EXPORT GInputStream * meta_selection_source_read_finish (MetaSelectionSource *source, GAsyncResult *result, GError **error); META_EXPORT GList * meta_selection_source_get_mimetypes (MetaSelectionSource *source); META_EXPORT gboolean meta_selection_source_is_active (MetaSelectionSource *source); #endif /* META_SELECTION_SOURCE_H */ muffin-6.4.1/src/meta/meta-window-shape.h0000664000175000017500000000527714723361714017174 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaWindowShape * * Extracted invariant window shape * * Copyright (C) 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_WINDOW_SHAPE_H__ #define __META_WINDOW_SHAPE_H__ #include #include #include META_EXPORT GType meta_window_shape_get_type (void) G_GNUC_CONST; /** * MetaWindowShape: * #MetaWindowShape represents a 9-sliced region with borders on all sides that * are unscaled, and a constant central region that is scaled. For example, * the regions representing two windows that are rounded rectangles, * with the same corner radius but different sizes, have the * same MetaWindowShape. * * #MetaWindowShape is designed to be used as part of a hash table key, so has * efficient hash and equal functions. */ typedef struct _MetaWindowShape MetaWindowShape; META_EXPORT MetaWindowShape * meta_window_shape_new (cairo_region_t *region); META_EXPORT MetaWindowShape * meta_window_shape_ref (MetaWindowShape *shape); META_EXPORT void meta_window_shape_unref (MetaWindowShape *shape); META_EXPORT guint meta_window_shape_hash (MetaWindowShape *shape); META_EXPORT gboolean meta_window_shape_equal (MetaWindowShape *shape_a, MetaWindowShape *shape_b); META_EXPORT void meta_window_shape_get_borders (MetaWindowShape *shape, int *border_top, int *border_right, int *border_bottom, int *border_left); META_EXPORT cairo_region_t *meta_window_shape_to_region (MetaWindowShape *shape, int center_width, int center_height); #endif /* __META_WINDOW_SHAPE_H __*/ muffin-6.4.1/src/meta/types.h0000664000175000017500000000300114723361714014766 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_TYPES_H #define META_TYPES_H /** * MetaCompositor: (skip) * */ typedef struct _MetaBackend MetaBackend; typedef struct _MetaCompositor MetaCompositor; typedef struct _MetaDisplay MetaDisplay; typedef struct _MetaX11Display MetaX11Display; typedef struct _MetaFrame MetaFrame; typedef struct _MetaWindow MetaWindow; typedef struct _MetaWorkspace MetaWorkspace; /** * MetaGroup: (skip) * */ typedef struct _MetaGroup MetaGroup; typedef struct _MetaKeyBinding MetaKeyBinding; typedef struct _MetaCursorTracker MetaCursorTracker; typedef struct _MetaDnd MetaDnd; typedef struct _MetaSettings MetaSettings; typedef struct _MetaWorkspaceManager MetaWorkspaceManager; typedef struct _MetaSelection MetaSelection; #endif muffin-6.4.1/src/meta/meta-x11-errors.h0000664000175000017500000000231014723361714016473 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X error handling */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_ERRORS_H #define META_ERRORS_H #include #include #include META_EXPORT void meta_x11_error_trap_push (MetaX11Display *x11_display); META_EXPORT void meta_x11_error_trap_pop (MetaX11Display *x11_display); /* returns X error code, or 0 for no error */ META_EXPORT int meta_x11_error_trap_pop_with_return (MetaX11Display *x11_display); #endif muffin-6.4.1/src/meta/meta-backend.h0000664000175000017500000000430114723361714016141 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_BACKEND_H #define META_BACKEND_H #include #include "clutter/clutter.h" #include "meta/meta-dnd.h" #include "meta/meta-remote-access-controller.h" #define META_TYPE_BACKEND (meta_backend_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaBackend, meta_backend, META, BACKEND, GObject) META_EXPORT MetaBackend * meta_get_backend (void); META_EXPORT void meta_backend_set_keymap (MetaBackend *backend, const char *layouts, const char *variants, const char *options); META_EXPORT void meta_backend_lock_layout_group (MetaBackend *backend, guint idx); META_EXPORT void meta_backend_set_numlock (MetaBackend *backend, gboolean numlock_state); META_EXPORT ClutterActor *meta_backend_get_stage (MetaBackend *backend); META_EXPORT MetaDnd *meta_backend_get_dnd (MetaBackend *backend); META_EXPORT MetaSettings *meta_backend_get_settings (MetaBackend *backend); META_EXPORT MetaRemoteAccessController * meta_backend_get_remote_access_controller (MetaBackend *backend); META_EXPORT gboolean meta_backend_is_rendering_hardware_accelerated (MetaBackend *backend); META_EXPORT void meta_clutter_init (void); #endif /* META_BACKEND_H */ muffin-6.4.1/src/meta/meta-selection-source-memory.h0000664000175000017500000000263514723361714021353 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_MEMORY_SELECTION_SOURCE_H #define META_MEMORY_SELECTION_SOURCE_H #include "meta/meta-selection-source.h" #define META_TYPE_SELECTION_SOURCE_MEMORY (meta_selection_source_memory_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaSelectionSourceMemory, meta_selection_source_memory, META, SELECTION_SOURCE_MEMORY, MetaSelectionSource) META_EXPORT MetaSelectionSource * meta_selection_source_memory_new (const char *mimetype, GBytes *content); #endif /* META_SELECTION_SOURCE_MEMORY_H */ muffin-6.4.1/src/meta/meta-version.h.in0000664000175000017500000000200514723361714016643 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Rico Tzschichholz * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_VERSION_H #define META_VERSION_H #define META_MAJOR_VERSION @MUTTER_MAJOR_VERSION@ #define META_MINOR_VERSION @MUTTER_MINOR_VERSION@ #define META_MICRO_VERSION @MUTTER_MICRO_VERSION@ #define META_PLUGIN_API_VERSION @MUTTER_PLUGIN_API_VERSION@ #endif muffin-6.4.1/src/meta/meta-background-image.h0000664000175000017500000000443014723361714017754 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaBackgroundImageCache: * * Simple cache for background textures loaded from files * * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_BACKGROUND_IMAGE_H__ #define __META_BACKGROUND_IMAGE_H__ #include #include #include "cogl/cogl.h" #include "meta/display.h" #define META_TYPE_BACKGROUND_IMAGE (meta_background_image_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaBackgroundImage, meta_background_image, META, BACKGROUND_IMAGE, GObject) META_EXPORT gboolean meta_background_image_is_loaded (MetaBackgroundImage *image); META_EXPORT gboolean meta_background_image_get_success (MetaBackgroundImage *image); META_EXPORT CoglTexture *meta_background_image_get_texture (MetaBackgroundImage *image); #define META_TYPE_BACKGROUND_IMAGE_CACHE (meta_background_image_cache_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaBackgroundImageCache, meta_background_image_cache, META, BACKGROUND_IMAGE_CACHE, GObject) META_EXPORT MetaBackgroundImageCache *meta_background_image_cache_get_default (void); META_EXPORT MetaBackgroundImage *meta_background_image_cache_load (MetaBackgroundImageCache *cache, GFile *file); META_EXPORT void meta_background_image_cache_purge (MetaBackgroundImageCache *cache, GFile *file); #endif /* __META_BACKGROUND_IMAGE_H__ */ muffin-6.4.1/src/meta/theme.h0000664000175000017500000000211114723361714014725 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Metacity Theme Rendering */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_THEME_H #define META_THEME_H #include /** * MetaTheme: * */ typedef struct _MetaTheme MetaTheme; META_EXPORT MetaTheme* meta_theme_get_default (void); META_EXPORT MetaTheme* meta_theme_new (void); META_EXPORT void meta_theme_free (MetaTheme *theme); #endif muffin-6.4.1/src/meta/meta-close-dialog.h0000664000175000017500000000362514723361714017124 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_CLOSE_DIALOG_H #define META_CLOSE_DIALOG_H #include #include #define META_TYPE_CLOSE_DIALOG (meta_close_dialog_get_type ()) META_EXPORT G_DECLARE_INTERFACE (MetaCloseDialog, meta_close_dialog, META, CLOSE_DIALOG, GObject) typedef enum { META_CLOSE_DIALOG_RESPONSE_WAIT, META_CLOSE_DIALOG_RESPONSE_FORCE_CLOSE, } MetaCloseDialogResponse; struct _MetaCloseDialogInterface { GTypeInterface parent_iface; void (* show) (MetaCloseDialog *dialog); void (* hide) (MetaCloseDialog *dialog); void (* focus) (MetaCloseDialog *dialog); }; META_EXPORT void meta_close_dialog_show (MetaCloseDialog *dialog); META_EXPORT void meta_close_dialog_hide (MetaCloseDialog *dialog); META_EXPORT void meta_close_dialog_focus (MetaCloseDialog *dialog); META_EXPORT gboolean meta_close_dialog_is_visible (MetaCloseDialog *dialog); META_EXPORT void meta_close_dialog_response (MetaCloseDialog *dialog, MetaCloseDialogResponse response); #endif /* META_CLOSE_DIALOG_H */ muffin-6.4.1/src/meta/meta-x11-background-actor.h0000664000175000017500000000322114723361714020406 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-background-actor.h: Actor for painting the root window background * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. */ #ifndef META_X11_BACKGROUND_ACTOR_H #define META_X11_BACKGROUND_ACTOR_H #include #include /** * MetaX11BackgroundActor: * * This class handles tracking and painting the root window background. * By integrating with #MetaWindowGroup we can avoid painting parts of * the background that are obscured by other windows. */ #define META_TYPE_X11_BACKGROUND_ACTOR (meta_x11_background_actor_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaX11BackgroundActor, meta_x11_background_actor, META, X11_BACKGROUND_ACTOR, ClutterActor) META_EXPORT ClutterActor *meta_x11_background_actor_new_for_display (MetaDisplay *display); #endif /* META_X11_BACKGROUND_ACTOR_H */ muffin-6.4.1/src/meta/window.h0000664000175000017500000003345214723361714015146 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_H #define META_WINDOW_H #include #include #include #include #include /** * MetaWindowType: * @META_WINDOW_NORMAL: Normal * @META_WINDOW_DESKTOP: Desktop * @META_WINDOW_DOCK: Dock * @META_WINDOW_DIALOG: Dialog * @META_WINDOW_MODAL_DIALOG: Modal dialog * @META_WINDOW_TOOLBAR: Toolbar * @META_WINDOW_MENU: Menu * @META_WINDOW_UTILITY: Utility * @META_WINDOW_SPLASHSCREEN: Splashcreen * @META_WINDOW_DROPDOWN_MENU: Dropdown menu * @META_WINDOW_POPUP_MENU: Popup menu * @META_WINDOW_TOOLTIP: Tooltip * @META_WINDOW_NOTIFICATION: Notification * @META_WINDOW_COMBO: Combobox * @META_WINDOW_DND: Drag and drop * @META_WINDOW_OVERRIDE_OTHER: Other override-redirect window type */ typedef enum { META_WINDOW_NORMAL, META_WINDOW_DESKTOP, META_WINDOW_DOCK, META_WINDOW_DIALOG, META_WINDOW_MODAL_DIALOG, META_WINDOW_TOOLBAR, META_WINDOW_MENU, META_WINDOW_UTILITY, META_WINDOW_SPLASHSCREEN, /* override redirect window types: */ META_WINDOW_DROPDOWN_MENU, META_WINDOW_POPUP_MENU, META_WINDOW_TOOLTIP, META_WINDOW_NOTIFICATION, META_WINDOW_COMBO, META_WINDOW_DND, META_WINDOW_OVERRIDE_OTHER } MetaWindowType; /** * MetaMaximizeFlags: * @META_MAXIMIZE_HORIZONTAL: Horizontal * @META_MAXIMIZE_VERTICAL: Vertical * @META_MAXIMIZE_BOTH: Both */ typedef enum { META_MAXIMIZE_HORIZONTAL = 1 << 0, META_MAXIMIZE_VERTICAL = 1 << 1, META_MAXIMIZE_BOTH = (1 << 0 | 1 << 1), } MetaMaximizeFlags; /** * MetaWindowClientType: * @META_WINDOW_CLIENT_TYPE_WAYLAND: A Wayland based window * @META_WINDOW_CLIENT_TYPE_X11: An X11 based window */ typedef enum { META_WINDOW_CLIENT_TYPE_WAYLAND, META_WINDOW_CLIENT_TYPE_X11 } MetaWindowClientType; #define META_TYPE_WINDOW (meta_window_get_type ()) #define META_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW, MetaWindow)) #define META_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW, MetaWindowClass)) #define META_IS_WINDOW(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WINDOW)) #define META_IS_WINDOW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WINDOW)) #define META_WINDOW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WINDOW, MetaWindowClass)) G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaWindow, g_object_unref) typedef struct _MetaWindowClass MetaWindowClass; META_EXPORT GType meta_window_get_type (void); META_EXPORT MetaFrame *meta_window_get_frame (MetaWindow *window); META_EXPORT gboolean meta_window_has_focus (MetaWindow *window); META_EXPORT gboolean meta_window_appears_focused (MetaWindow *window); META_EXPORT gboolean meta_window_is_shaded (MetaWindow *window); META_EXPORT gboolean meta_window_is_override_redirect (MetaWindow *window); META_EXPORT gboolean meta_window_is_skip_taskbar (MetaWindow *window); META_EXPORT void meta_window_get_buffer_rect (const MetaWindow *window, MetaRectangle *rect); META_EXPORT void meta_window_get_frame_rect (const MetaWindow *window, MetaRectangle *rect); META_EXPORT void meta_window_client_rect_to_frame_rect (MetaWindow *window, MetaRectangle *client_rect, MetaRectangle *frame_rect); META_EXPORT void meta_window_frame_rect_to_client_rect (MetaWindow *window, MetaRectangle *frame_rect, MetaRectangle *client_rect); META_EXPORT MetaDisplay *meta_window_get_display (MetaWindow *window); META_EXPORT Window meta_window_get_xwindow (MetaWindow *window); META_EXPORT MetaWindowType meta_window_get_window_type (MetaWindow *window); META_EXPORT MetaWorkspace *meta_window_get_workspace (MetaWindow *window); META_EXPORT int meta_window_get_monitor (MetaWindow *window); META_EXPORT gboolean meta_window_is_on_all_workspaces (MetaWindow *window); META_EXPORT gboolean meta_window_located_on_workspace (MetaWindow *window, MetaWorkspace *workspace); META_EXPORT gboolean meta_window_is_hidden (MetaWindow *window); META_EXPORT void meta_window_activate (MetaWindow *window,guint32 current_time); META_EXPORT void meta_window_activate_with_workspace (MetaWindow *window, guint32 current_time, MetaWorkspace *workspace); META_EXPORT const char * meta_window_get_description (MetaWindow *window); META_EXPORT const char * meta_window_get_wm_class (MetaWindow *window); META_EXPORT const char * meta_window_get_wm_class_instance (MetaWindow *window); META_EXPORT gboolean meta_window_showing_on_its_workspace (MetaWindow *window); META_EXPORT const char * meta_window_get_sandboxed_app_id (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_theme_variant (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_application_id (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_unique_bus_name (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_application_object_path (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_window_object_path (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_app_menu_object_path (MetaWindow *window); META_EXPORT const char * meta_window_get_gtk_menubar_object_path (MetaWindow *window); META_EXPORT void meta_window_move_frame(MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw); META_EXPORT void meta_window_move_resize_frame (MetaWindow *window, gboolean user_op, int root_x_nw, int root_y_nw, int w, int h); META_EXPORT void meta_window_move_to_monitor (MetaWindow *window, int monitor); META_EXPORT void meta_window_set_demands_attention (MetaWindow *window); META_EXPORT void meta_window_unset_demands_attention (MetaWindow *window); META_EXPORT const char* meta_window_get_startup_id (MetaWindow *window); META_EXPORT void meta_window_change_workspace_by_index (MetaWindow *window, gint space_index, gboolean append); META_EXPORT void meta_window_change_workspace (MetaWindow *window, MetaWorkspace *workspace); META_EXPORT GObject *meta_window_get_compositor_private (MetaWindow *window); META_EXPORT void meta_window_set_compositor_private (MetaWindow *window, GObject *priv); META_EXPORT const char *meta_window_get_role (MetaWindow *window); META_EXPORT MetaStackLayer meta_window_get_layer (MetaWindow *window); META_EXPORT MetaWindow* meta_window_find_root_ancestor (MetaWindow *window); META_EXPORT gboolean meta_window_is_ancestor_of_transient (MetaWindow *window, MetaWindow *transient); typedef gboolean (*MetaWindowForeachFunc) (MetaWindow *window, void *user_data); META_EXPORT void meta_window_foreach_transient (MetaWindow *window, MetaWindowForeachFunc func, void *user_data); META_EXPORT void meta_window_foreach_ancestor (MetaWindow *window, MetaWindowForeachFunc func, void *user_data); META_EXPORT MetaMaximizeFlags meta_window_get_maximized (MetaWindow *window); META_EXPORT gboolean meta_window_is_fullscreen (MetaWindow *window); META_EXPORT gboolean meta_window_is_screen_sized (MetaWindow *window); META_EXPORT gboolean meta_window_is_monitor_sized (MetaWindow *window); META_EXPORT gboolean meta_window_is_on_primary_monitor (MetaWindow *window); META_EXPORT gboolean meta_window_requested_bypass_compositor (MetaWindow *window); META_EXPORT gboolean meta_window_requested_dont_bypass_compositor (MetaWindow *window); META_EXPORT gboolean meta_window_get_icon_geometry (MetaWindow *window, MetaRectangle *rect); META_EXPORT void meta_window_set_icon_geometry (MetaWindow *window, MetaRectangle *rect); META_EXPORT void meta_window_maximize (MetaWindow *window, MetaMaximizeFlags directions); META_EXPORT void meta_window_unmaximize (MetaWindow *window, MetaMaximizeFlags directions); META_EXPORT void meta_window_minimize (MetaWindow *window); META_EXPORT void meta_window_unminimize (MetaWindow *window); META_EXPORT void meta_window_raise (MetaWindow *window); META_EXPORT void meta_window_lower (MetaWindow *window); META_EXPORT const char *meta_window_get_title (MetaWindow *window); META_EXPORT MetaWindow *meta_window_get_transient_for (MetaWindow *window); META_EXPORT void meta_window_delete (MetaWindow *window, guint32 timestamp); META_EXPORT guint meta_window_get_stable_sequence (MetaWindow *window); META_EXPORT guint32 meta_window_get_user_time (MetaWindow *window); META_EXPORT uint32_t meta_window_get_client_pid (MetaWindow *window); META_EXPORT int meta_window_get_pid (MetaWindow *window); META_EXPORT const char *meta_window_get_client_machine (MetaWindow *window); META_EXPORT gboolean meta_window_is_remote (MetaWindow *window); META_EXPORT gboolean meta_window_is_attached_dialog (MetaWindow *window); META_EXPORT const char *meta_window_get_mutter_hints (MetaWindow *window); META_EXPORT MetaFrameType meta_window_get_frame_type (MetaWindow *window); META_EXPORT cairo_region_t *meta_window_get_frame_bounds (MetaWindow *window); META_EXPORT MetaWindow *meta_window_get_tile_match (MetaWindow *window, gboolean vertical); META_EXPORT void meta_window_make_fullscreen (MetaWindow *window); META_EXPORT void meta_window_unmake_fullscreen (MetaWindow *window); META_EXPORT void meta_window_make_above (MetaWindow *window); META_EXPORT void meta_window_unmake_above (MetaWindow *window); META_EXPORT void meta_window_shade (MetaWindow *window, guint32 timestamp); META_EXPORT void meta_window_unshade (MetaWindow *window, guint32 timestamp); META_EXPORT void meta_window_stick (MetaWindow *window); META_EXPORT void meta_window_unstick (MetaWindow *window); META_EXPORT void meta_window_kill (MetaWindow *window); META_EXPORT void meta_window_focus (MetaWindow *window, guint32 timestamp); META_EXPORT void meta_window_check_alive (MetaWindow *window, guint32 timestamp); META_EXPORT void meta_window_get_work_area_current_monitor (MetaWindow *window, MetaRectangle *area); META_EXPORT void meta_window_get_work_area_for_monitor (MetaWindow *window, int which_monitor, MetaRectangle *area); META_EXPORT void meta_window_get_work_area_all_monitors (MetaWindow *window, MetaRectangle *area); META_EXPORT void meta_window_begin_grab_op (MetaWindow *window, MetaGrabOp op, gboolean frame_action, guint32 timestamp); META_EXPORT gboolean meta_window_can_maximize (MetaWindow *window); META_EXPORT gboolean meta_window_can_minimize (MetaWindow *window); META_EXPORT gboolean meta_window_can_shade (MetaWindow *window); META_EXPORT gboolean meta_window_can_close (MetaWindow *window); META_EXPORT gboolean meta_window_is_always_on_all_workspaces (MetaWindow *window); META_EXPORT gboolean meta_window_is_above (MetaWindow *window); META_EXPORT gboolean meta_window_allows_move (MetaWindow *window); META_EXPORT gboolean meta_window_allows_resize (MetaWindow *window); META_EXPORT gboolean meta_window_is_client_decorated (MetaWindow *window); META_EXPORT gboolean meta_window_titlebar_is_onscreen (MetaWindow *window); META_EXPORT void meta_window_shove_titlebar_onscreen (MetaWindow *window); META_EXPORT uint64_t meta_window_get_id (MetaWindow *window); META_EXPORT MetaWindowClientType meta_window_get_client_type (MetaWindow *window); META_EXPORT const char *meta_window_get_icon_name (MetaWindow *window); META_EXPORT void meta_window_set_opacity (MetaWindow *window, guint8 opacity); META_EXPORT guint8 meta_window_get_opacity (MetaWindow *window); #endif muffin-6.4.1/src/meta/main.h0000664000175000017500000000402014723361714014550 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter main */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MAIN_H #define META_MAIN_H #include #include META_EXPORT GOptionContext *meta_get_option_context (void); META_EXPORT void meta_init (void); META_EXPORT int meta_run (void); META_EXPORT void meta_register_with_session (void); META_EXPORT gboolean meta_activate_session (void); /* Actually defined in meta-backend.c */ META_EXPORT gboolean meta_get_replace_current_wm (void); /* Actually defined in util.c */ META_EXPORT void meta_set_wm_name (const char *wm_name); META_EXPORT void meta_set_gnome_wm_keybindings (const char *wm_keybindings); META_EXPORT void meta_restart (const char *message); META_EXPORT gboolean meta_is_restart (void); /** * MetaExitCode: * @META_EXIT_SUCCESS: Success * @META_EXIT_ERROR: Error */ typedef enum { META_EXIT_SUCCESS, META_EXIT_ERROR } MetaExitCode; /* exit immediately */ META_EXPORT void meta_exit (MetaExitCode code) G_GNUC_NORETURN; /* g_main_loop_quit() then fall out of main() */ META_EXPORT void meta_quit (MetaExitCode code); META_EXPORT void meta_test_init (void); #endif muffin-6.4.1/src/meta/workspace.h0000664000175000017500000000604114723361714015627 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2004, 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WORKSPACE_H #define META_WORKSPACE_H #include #include #define META_TYPE_WORKSPACE (meta_workspace_get_type ()) #define META_WORKSPACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WORKSPACE, MetaWorkspace)) #define META_WORKSPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WORKSPACE, MetaWorkspaceClass)) #define META_IS_WORKSPACE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WORKSPACE)) #define META_IS_WORKSPACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WORKSPACE)) #define META_WORKSPACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WORKSPACE, MetaWorkspaceClass)) typedef struct _MetaWorkspaceClass MetaWorkspaceClass; META_EXPORT GType meta_workspace_get_type (void); META_EXPORT int meta_workspace_index (MetaWorkspace *workspace); META_EXPORT MetaDisplay *meta_workspace_get_display (MetaWorkspace *workspace); META_EXPORT GList* meta_workspace_list_windows (MetaWorkspace *workspace); META_EXPORT void meta_workspace_get_work_area_for_monitor (MetaWorkspace *workspace, int which_monitor, MetaRectangle *area); META_EXPORT void meta_workspace_get_work_area_all_monitors (MetaWorkspace *workspace, MetaRectangle *area); META_EXPORT void meta_workspace_activate (MetaWorkspace *workspace, guint32 timestamp); META_EXPORT void meta_workspace_activate_with_focus (MetaWorkspace *workspace, MetaWindow *focus_this, guint32 timestamp); META_EXPORT void meta_workspace_activate_with_direction_hint (MetaWorkspace *workspace, MetaMotionDirection direction, guint32 timestamp); META_EXPORT void meta_workspace_set_builtin_struts (MetaWorkspace *workspace, GSList *struts); META_EXPORT MetaWorkspace* meta_workspace_get_neighbor (MetaWorkspace *workspace, MetaMotionDirection direction); #endif muffin-6.4.1/src/meta/meta-remote-access-controller.h0000664000175000017500000000343714723361714021476 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_REMOTE_ACCESS_CONTROLLER_H #define META_REMOTE_ACCESS_CONTROLLER_H #include #include #define META_TYPE_REMOTE_ACCESS_HANDLE meta_remote_access_handle_get_type () META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaRemoteAccessHandle, meta_remote_access_handle, META, REMOTE_ACCESS_HANDLE, GObject) struct _MetaRemoteAccessHandleClass { GObjectClass parent_class; void (*stop) (MetaRemoteAccessHandle *handle); }; META_EXPORT void meta_remote_access_handle_stop (MetaRemoteAccessHandle *handle); META_EXPORT gboolean meta_remote_access_handle_get_disable_animations (MetaRemoteAccessHandle *handle); #define META_TYPE_REMOTE_ACCESS_CONTROLLER meta_remote_access_controller_get_type () META_EXPORT G_DECLARE_FINAL_TYPE (MetaRemoteAccessController, meta_remote_access_controller, META, REMOTE_ACCESS_CONTROLLER, GObject) #endif /* META_REMOTE_ACCESS_CONTROLLER_H */ muffin-6.4.1/src/meta/meta-settings.h0000664000175000017500000000211714723361714016415 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_SETTINGS_H #define META_SETTINGS_H #include #include META_EXPORT int meta_settings_get_ui_scaling_factor (MetaSettings *settings); META_EXPORT int meta_settings_get_font_dpi (MetaSettings *settings); #endif /* META_SETTINGS_H */ muffin-6.4.1/src/meta/util.h0000664000175000017500000001610514723361714014610 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter utilities */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_UTIL_H #define META_UTIL_H #include #include #include META_EXPORT gboolean meta_is_verbose (void); META_EXPORT gboolean meta_is_debugging (void); META_EXPORT gboolean meta_is_syncing (void); META_EXPORT gboolean meta_is_wayland_compositor (void); META_EXPORT void meta_debug_spew_real (const char *format, ...) G_GNUC_PRINTF (1, 2); META_EXPORT void meta_verbose_real (const char *format, ...) G_GNUC_PRINTF (1, 2); META_EXPORT void meta_bug (const char *format, ...) G_GNUC_PRINTF (1, 2); META_EXPORT void meta_warning (const char *format, ...) G_GNUC_PRINTF (1, 2); META_EXPORT void meta_fatal (const char *format, ...) G_GNUC_PRINTF (1, 2) G_GNUC_NORETURN G_ANALYZER_NORETURN; /** * MetaDebugTopic: * @META_DEBUG_VERBOSE: verbose logging * @META_DEBUG_FOCUS: focus * @META_DEBUG_WORKAREA: workarea * @META_DEBUG_STACK: stack * @META_DEBUG_THEMES: themes * @META_DEBUG_SM: session management * @META_DEBUG_EVENTS: events * @META_DEBUG_WINDOW_STATE: window state * @META_DEBUG_WINDOW_OPS: window operations * @META_DEBUG_GEOMETRY: geometry * @META_DEBUG_PLACEMENT: window placement * @META_DEBUG_PING: ping * @META_DEBUG_XINERAMA: Xinerama * @META_DEBUG_KEYBINDINGS: keybindings * @META_DEBUG_SYNC: sync * @META_DEBUG_ERRORS: errors * @META_DEBUG_STARTUP: startup * @META_DEBUG_PREFS: preferences * @META_DEBUG_GROUPS: groups * @META_DEBUG_RESIZING: resizing * @META_DEBUG_SHAPES: shapes * @META_DEBUG_COMPOSITOR: compositor * @META_DEBUG_EDGE_RESISTANCE: edge resistance */ typedef enum { META_DEBUG_VERBOSE = -1, META_DEBUG_FOCUS = 1 << 0, META_DEBUG_WORKAREA = 1 << 1, META_DEBUG_STACK = 1 << 2, META_DEBUG_THEMES = 1 << 3, META_DEBUG_SM = 1 << 4, META_DEBUG_EVENTS = 1 << 5, META_DEBUG_WINDOW_STATE = 1 << 6, META_DEBUG_WINDOW_OPS = 1 << 7, META_DEBUG_GEOMETRY = 1 << 8, META_DEBUG_PLACEMENT = 1 << 9, META_DEBUG_PING = 1 << 10, META_DEBUG_XINERAMA = 1 << 11, META_DEBUG_KEYBINDINGS = 1 << 12, META_DEBUG_SYNC = 1 << 13, META_DEBUG_ERRORS = 1 << 14, META_DEBUG_STARTUP = 1 << 15, META_DEBUG_PREFS = 1 << 16, META_DEBUG_GROUPS = 1 << 17, META_DEBUG_RESIZING = 1 << 18, META_DEBUG_SHAPES = 1 << 19, META_DEBUG_COMPOSITOR = 1 << 20, META_DEBUG_EDGE_RESISTANCE = 1 << 21, META_DEBUG_DBUS = 1 << 22, META_DEBUG_INPUT = 1 << 23 } MetaDebugTopic; META_EXPORT void meta_topic_real (MetaDebugTopic topic, const char *format, ...) G_GNUC_PRINTF (2, 3); META_EXPORT void meta_set_verbose (gboolean setting); META_EXPORT void meta_add_verbose_topic (MetaDebugTopic topic); META_EXPORT void meta_remove_verbose_topic (MetaDebugTopic topic); META_EXPORT void meta_push_no_msg_prefix (void); META_EXPORT void meta_pop_no_msg_prefix (void); META_EXPORT gint meta_unsigned_long_equal (gconstpointer v1, gconstpointer v2); META_EXPORT guint meta_unsigned_long_hash (gconstpointer v); META_EXPORT const char* meta_frame_type_to_string (MetaFrameType type); META_EXPORT const char* meta_gravity_to_string (MetaGravity gravity); META_EXPORT char* meta_external_binding_name_for_action (guint keybinding_action); META_EXPORT char* meta_g_utf8_strndup (const gchar *src, gsize n); META_EXPORT GPid meta_show_dialog (const char *type, const char *message, const char *timeout, const char *display, const char *ok_text, const char *cancel_text, const int transient_for, GSList *columns, GSList *entries); /* To disable verbose mode, we make these functions into no-ops */ #ifdef WITH_VERBOSE_MODE #define meta_debug_spew meta_debug_spew_real #define meta_verbose meta_verbose_real #define meta_topic meta_topic_real #else # ifdef G_HAVE_ISO_VARARGS # define meta_debug_spew(...) # define meta_verbose(...) # define meta_topic(...) # elif defined(G_HAVE_GNUC_VARARGS) # define meta_debug_spew(format...) # define meta_verbose(format...) # define meta_topic(format...) # else # error "This compiler does not support varargs macros and thus verbose mode can't be disabled meaningfully" # endif #endif /* !WITH_VERBOSE_MODE */ /** * MetaLaterType: * @META_LATER_RESIZE: call in a resize processing phase that is done * before GTK+ repainting (including window borders) is done. * @META_LATER_CALC_SHOWING: used by Mutter to compute which windows should be mapped * @META_LATER_CHECK_FULLSCREEN: used by Mutter to see if there's a fullscreen window * @META_LATER_SYNC_STACK: used by Mutter to send it's idea of the stacking order to the server * @META_LATER_BEFORE_REDRAW: call before the stage is redrawn * @META_LATER_IDLE: call at a very low priority (can be blocked * by running animations or redrawing applications) **/ typedef enum { META_LATER_RESIZE, META_LATER_CALC_SHOWING, META_LATER_CHECK_FULLSCREEN, META_LATER_SYNC_STACK, META_LATER_BEFORE_REDRAW, META_LATER_IDLE } MetaLaterType; META_EXPORT guint meta_later_add (MetaLaterType when, GSourceFunc func, gpointer data, GDestroyNotify notify); META_EXPORT void meta_later_remove (guint later_id); typedef enum { META_LOCALE_DIRECTION_LTR, META_LOCALE_DIRECTION_RTL, } MetaLocaleDirection; META_EXPORT MetaLocaleDirection meta_get_locale_direction (void); META_EXPORT void meta_add_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags); META_EXPORT void meta_remove_clutter_debug_flags (ClutterDebugFlag debug_flags, ClutterDrawDebugFlag draw_flags, ClutterPickDebugFlag pick_flags); #endif /* META_UTIL_H */ muffin-6.4.1/src/meta/meta-background-actor.h0000664000175000017500000000504214723361714020002 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-background-actor.h: Actor for painting the root window background * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BACKGROUND_ACTOR_H #define META_BACKGROUND_ACTOR_H #include #include "clutter/clutter.h" #include "meta/meta-background.h" /** * MetaBackgroundActor: * * This class handles tracking and painting the root window background. * By integrating with #MetaWindowGroup we can avoid painting parts of * the background that are obscured by other windows. */ #define META_TYPE_BACKGROUND_ACTOR (meta_background_actor_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaBackgroundActor, meta_background_actor, META, BACKGROUND_ACTOR, ClutterActor) META_EXPORT ClutterActor *meta_background_actor_new (MetaDisplay *display, int monitor); META_EXPORT void meta_background_actor_set_background (MetaBackgroundActor *self, MetaBackground *background); META_EXPORT void meta_background_actor_set_gradient (MetaBackgroundActor *self, gboolean enabled, int height, double tone_start); META_EXPORT void meta_background_actor_set_monitor (MetaBackgroundActor *self, int monitor); META_EXPORT void meta_background_actor_set_vignette (MetaBackgroundActor *self, gboolean enabled, double brightness, double sharpness); #endif /* META_BACKGROUND_ACTOR_H */ muffin-6.4.1/src/meta/meta-dnd.h0000664000175000017500000000202014723361714015313 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Hyungwon Hwang * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_DND_H #define META_DND_H #include #include #include #include #define META_TYPE_DND (meta_dnd_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaDnd, meta_dnd, META, DND, GObject) #endif /* META_DND_H */ muffin-6.4.1/src/meta/meta-inhibit-shortcuts-dialog.h0000664000175000017500000000342014723361714021472 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_INHIBIT_SHORTCUTS_DIALOG_H #define META_INHIBIT_SHORTCUTS_DIALOG_H #include #include #define META_TYPE_INHIBIT_SHORTCUTS_DIALOG (meta_inhibit_shortcuts_dialog_get_type ()) META_EXPORT G_DECLARE_INTERFACE (MetaInhibitShortcutsDialog, meta_inhibit_shortcuts_dialog, META, INHIBIT_SHORTCUTS_DIALOG, GObject) typedef enum { META_INHIBIT_SHORTCUTS_DIALOG_RESPONSE_ALLOW, META_INHIBIT_SHORTCUTS_DIALOG_RESPONSE_DENY, } MetaInhibitShortcutsDialogResponse; struct _MetaInhibitShortcutsDialogInterface { GTypeInterface parent_iface; void (* show) (MetaInhibitShortcutsDialog *dialog); void (* hide) (MetaInhibitShortcutsDialog *dialog); }; META_EXPORT void meta_inhibit_shortcuts_dialog_show (MetaInhibitShortcutsDialog *dialog); META_EXPORT void meta_inhibit_shortcuts_dialog_hide (MetaInhibitShortcutsDialog *dialog); META_EXPORT void meta_inhibit_shortcuts_dialog_response (MetaInhibitShortcutsDialog *dialog, MetaInhibitShortcutsDialogResponse response); #endif /* META_INHIBIT_SHORTCUTS_DIALOG_H */ muffin-6.4.1/src/meta/group.h0000664000175000017500000000350414723361714014766 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window groups */ /* * Copyright (C) 2002 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_GROUP_H #define META_GROUP_H #include #include #include #include /* note, can return NULL */ META_EXPORT MetaGroup* meta_window_get_group (MetaWindow *window); META_EXPORT void meta_window_compute_group (MetaWindow* window); META_EXPORT void meta_window_shutdown_group (MetaWindow *window); META_EXPORT void meta_window_group_leader_changed (MetaWindow *window); /* note, can return NULL */ META_EXPORT MetaGroup *meta_x11_display_lookup_group (MetaX11Display *x11_display, Window group_leader); META_EXPORT GSList* meta_group_list_windows (MetaGroup *group); META_EXPORT void meta_group_update_layers (MetaGroup *group); META_EXPORT const char* meta_group_get_startup_id (MetaGroup *group); META_EXPORT int meta_group_get_size (MetaGroup *group); META_EXPORT gboolean meta_group_property_notify (MetaGroup *group, XEvent *event); #endif muffin-6.4.1/src/meta/meta-cursor-tracker.h0000664000175000017500000000431514723361714017525 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Giovanni Campagna */ #ifndef META_CURSOR_TRACKER_H #define META_CURSOR_TRACKER_H #include #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/types.h" #include "meta/workspace.h" #define META_TYPE_CURSOR_TRACKER (meta_cursor_tracker_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaCursorTracker, meta_cursor_tracker, META, CURSOR_TRACKER, GObject) META_EXPORT MetaCursorTracker *meta_cursor_tracker_get_for_display (MetaDisplay *display); META_EXPORT void meta_cursor_tracker_get_hot (MetaCursorTracker *tracker, int *x, int *y); META_EXPORT CoglTexture *meta_cursor_tracker_get_sprite (MetaCursorTracker *tracker); META_EXPORT void meta_cursor_tracker_get_pointer (MetaCursorTracker *tracker, int *x, int *y, ClutterModifierType *mods); META_EXPORT gboolean meta_cursor_tracker_get_pointer_visible (MetaCursorTracker *tracker); META_EXPORT void meta_cursor_tracker_set_pointer_visible (MetaCursorTracker *tracker, gboolean visible); #endif muffin-6.4.1/src/meta/meta-enum-types.h.in0000664000175000017500000000105514723361714017270 0ustar fabiofabio/*** BEGIN file-header ***/ #ifndef __META_ENUM_TYPES_H__ #define __META_ENUM_TYPES_H__ #include #include G_BEGIN_DECLS /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@basename@" */ /*** END file-production ***/ /*** BEGIN file-tail ***/ G_END_DECLS #endif /* !__MUTTER_ENUM_TYPES_H__ */ /*** END file-tail ***/ /*** BEGIN value-header ***/ META_EXPORT GType @enum_name@_get_type (void) G_GNUC_CONST; #define META_TYPE_@ENUMSHORT@ (@enum_name@_get_type()) /*** END value-header ***/ muffin-6.4.1/src/meta/meta-startup-notification.h0000664000175000017500000000507514723361714020751 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2018 Red Hat, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_STARTUP_NOTIFICATION_H #define META_STARTUP_NOTIFICATION_H #include #define META_TYPE_STARTUP_SEQUENCE (meta_startup_sequence_get_type ()) #define META_TYPE_STARTUP_NOTIFICATION (meta_startup_notification_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaStartupNotification, meta_startup_notification, META, STARTUP_NOTIFICATION, GObject) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaStartupSequence, meta_startup_sequence, META, STARTUP_SEQUENCE, GObject) /** * meta_startup_notification_get_sequences: (skip) */ META_EXPORT GSList * meta_startup_notification_get_sequences (MetaStartupNotification *sn); META_EXPORT MetaLaunchContext * meta_startup_notification_create_launcher (MetaStartupNotification *sn); META_EXPORT const char * meta_startup_sequence_get_id (MetaStartupSequence *sequence); META_EXPORT gboolean meta_startup_sequence_get_completed (MetaStartupSequence *sequence); META_EXPORT const char * meta_startup_sequence_get_name (MetaStartupSequence *sequence); META_EXPORT int meta_startup_sequence_get_workspace (MetaStartupSequence *sequence); META_EXPORT uint64_t meta_startup_sequence_get_timestamp (MetaStartupSequence *sequence); META_EXPORT const char * meta_startup_sequence_get_icon_name (MetaStartupSequence *sequence); META_EXPORT const char * meta_startup_sequence_get_application_id (MetaStartupSequence *sequence); META_EXPORT const char * meta_startup_sequence_get_wmclass (MetaStartupSequence *sequence); META_EXPORT void meta_startup_sequence_complete (MetaStartupSequence *sequence); #endif /* META_STARTUP_NOTIFICATION_H */ muffin-6.4.1/src/meta/meta-shaped-texture.h0000664000175000017500000000371414723361714017523 0ustar fabiofabio/* * shaped texture * * An actor to draw a texture clipped to a list of rectangles * * Authored By Neil Roberts * * Copyright (C) 2008 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_SHAPED_TEXTURE_H__ #define __META_SHAPED_TEXTURE_H__ #include #include "clutter/clutter.h" #include G_BEGIN_DECLS #define META_TYPE_SHAPED_TEXTURE (meta_shaped_texture_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaShapedTexture, meta_shaped_texture, META, SHAPED_TEXTURE, GObject) META_EXPORT void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, gboolean create_mipmaps); META_EXPORT CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex); META_EXPORT void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, CoglTexture *mask_texture); META_EXPORT void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex, cairo_region_t *opaque_region); META_EXPORT cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture *stex, cairo_rectangle_int_t *clip); G_END_DECLS #endif /* __META_SHAPED_TEXTURE_H__ */ muffin-6.4.1/src/meta/meta-window-group.h0000664000175000017500000000064614723361714017223 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_WINDOW_GROUP_H #define META_WINDOW_GROUP_H #include "clutter/clutter.h" #define META_TYPE_WINDOW_GROUP (meta_window_group_get_type()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaWindowGroup, meta_window_group, META, WINDOW_GROUP, ClutterActor) #endif /* META_WINDOW_GROUP_H */ muffin-6.4.1/src/meta/meta-sound-player.h0000664000175000017500000000323714723361714017203 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_SOUND_PLAYER_H #define META_SOUND_PLAYER_H #include #include #define META_TYPE_SOUND_PLAYER (meta_sound_player_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaSoundPlayer, meta_sound_player, META, SOUND_PLAYER, GObject) META_EXPORT void meta_sound_player_play_from_theme (MetaSoundPlayer *player, const char *name, const char *description, GCancellable *cancellable); META_EXPORT void meta_sound_player_play_from_file (MetaSoundPlayer *player, GFile *file, const char *description, GCancellable *cancellable); #endif /* META_SOUND_PLAYER_H */ muffin-6.4.1/src/meta/meta-window-actor.h0000664000175000017500000000370714723361714017200 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Matthew Allum * Copyright (C) 2007 Iain Holmes * Based on xcompmgr - (c) 2003 Keith Packard * xfwm4 - (c) 2005-2007 Olivier Fourdan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_ACTOR_H_ #define META_WINDOW_ACTOR_H_ #include "clutter/clutter.h" #include "meta/compositor.h" #include "meta/meta-shaped-texture.h" #define META_TYPE_WINDOW_ACTOR (meta_window_actor_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaWindowActor, meta_window_actor, META, WINDOW_ACTOR, ClutterActor) META_EXPORT MetaWindow * meta_window_actor_get_meta_window (MetaWindowActor *self); META_EXPORT MetaShapedTexture *meta_window_actor_get_texture (MetaWindowActor *self); META_EXPORT void meta_window_actor_sync_visibility (MetaWindowActor *self); META_EXPORT gboolean meta_window_actor_is_destroyed (MetaWindowActor *self); META_EXPORT cairo_surface_t * meta_window_actor_get_image (MetaWindowActor *self, cairo_rectangle_int_t *clip); typedef enum { META_SHADOW_MODE_AUTO, META_SHADOW_MODE_FORCED_OFF, META_SHADOW_MODE_FORCED_ON, } MetaShadowMode; #endif /* META_WINDOW_ACTOR_H */ muffin-6.4.1/src/meta/meta-enum-types.c.in0000664000175000017500000000147314723361714017267 0ustar fabiofabio/*** BEGIN file-header ***/ #include /*** END file-header ***/ /*** BEGIN file-production ***/ /* enumerations from "@filename@" */ #include "@filename@" /*** END file-production ***/ /*** BEGIN value-header ***/ GType @enum_name@_get_type (void) { static gsize g_enum_type_id = 0; if (g_once_init_enter (&g_enum_type_id)) { static const G@Type@Value values[] = { /*** END value-header ***/ /*** BEGIN value-production ***/ { @VALUENAME@, "@VALUENAME@", "@valuenick@" }, /*** END value-production ***/ /*** BEGIN value-tail ***/ { 0, NULL, NULL } }; GType id; id = g_@type@_register_static (g_intern_static_string ("@EnumName@"), values); g_once_init_leave (&g_enum_type_id, id); } return g_enum_type_id; } /*** END value-tail ***/ muffin-6.4.1/src/meta/meta-selection.h0000664000175000017500000000550314723361714016544 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_SELECTION_H #define META_SELECTION_H #include #include #include #include #define META_TYPE_SELECTION (meta_selection_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaSelection, meta_selection, META, SELECTION, GObject) META_EXPORT MetaSelection * meta_selection_new (MetaDisplay *display); META_EXPORT void meta_selection_set_owner (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *owner); META_EXPORT void meta_selection_unset_owner (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *owner); META_EXPORT GList * meta_selection_get_mimetypes (MetaSelection *selection, MetaSelectionType selection_type); META_EXPORT void meta_selection_transfer_async (MetaSelection *selection, MetaSelectionType selection_type, const gchar *mimetype, gssize size, GOutputStream *output, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); META_EXPORT gboolean meta_selection_transfer_finish (MetaSelection *selection, GAsyncResult *result, GError **error); #endif /* META_SELECTION_H */ muffin-6.4.1/src/meta/meta-background.h0000664000175000017500000000511414723361714016674 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-background-actor.h: for painting the root window background * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BACKGROUND_H #define META_BACKGROUND_H #include #include "clutter/clutter.h" #include "meta/display.h" /** * MetaBackground: * * This class handles tracking and painting the root window background. * By integrating with #MetaWindowGroup we can avoid painting parts of * the background that are obscured by other windows. */ #define META_TYPE_BACKGROUND (meta_background_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaBackground, meta_background, META, BACKGROUND, GObject) META_EXPORT void meta_background_refresh_all (void); META_EXPORT MetaBackground *meta_background_new (MetaDisplay *display); META_EXPORT void meta_background_set_color (MetaBackground *self, ClutterColor *color); META_EXPORT void meta_background_set_gradient (MetaBackground *self, CDesktopBackgroundShading shading_direction, ClutterColor *color, ClutterColor *second_color); META_EXPORT void meta_background_set_file (MetaBackground *self, GFile *file, CDesktopBackgroundStyle style); META_EXPORT void meta_background_set_blend (MetaBackground *self, GFile *file1, GFile *file2, double blend_factor, CDesktopBackgroundStyle style); #endif /* META_BACKGROUND_H */ muffin-6.4.1/src/meta/boxes.h0000664000175000017500000001151014723361714014746 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Simple box operations */ /* * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BOXES_H #define META_BOXES_H #include #include #define META_TYPE_RECTANGLE (meta_rectangle_get_type ()) /** * MetaRectangle: * @x: X coordinate of the top-left corner * @y: Y coordinate of the top-left corner * @width: Width of the rectangle * @height: Height of the rectangle */ #ifdef __GI_SCANNER__ /* The introspection scanner is currently unable to lookup how * cairo_rectangle_int_t is actually defined. This prevents * introspection data for the GdkRectangle type to include fields * descriptions. To workaround this issue, we define it with the same * content as cairo_rectangle_int_t, but only under the introspection * define. */ struct _MetaRectangle { int x; int y; int width; int height; }; typedef struct _MetaRectangle MetaRectangle; #else typedef cairo_rectangle_int_t MetaRectangle; #endif /** * MetaStrut: * @rect: #MetaRectangle the #MetaStrut is on * @side: #MetaSide the #MetaStrut is on */ typedef struct _MetaStrut MetaStrut; struct _MetaStrut { MetaRectangle rect; MetaSide side; }; /** * MetaEdgeType: * @META_EDGE_WINDOW: Whether the edge belongs to a window * @META_EDGE_MONITOR: Whether the edge belongs to a monitor * @META_EDGE_SCREEN: Whether the edge belongs to a screen */ typedef enum { META_EDGE_WINDOW, META_EDGE_MONITOR, META_EDGE_SCREEN } MetaEdgeType; /** * MetaEdge: * @rect: #MetaRectangle with the bounds of the edge * @side_type: Side * @edge_type: To what belongs the edge */ typedef struct _MetaEdge MetaEdge; struct _MetaEdge { MetaRectangle rect; /* width or height should be 1 */ MetaSide side_type; MetaEdgeType edge_type; }; META_EXPORT GType meta_rectangle_get_type (void); META_EXPORT MetaRectangle *meta_rectangle_copy (const MetaRectangle *rect); META_EXPORT void meta_rectangle_free (MetaRectangle *rect); /* Function to make initializing a rect with a single line of code easy */ META_EXPORT MetaRectangle meta_rect (int x, int y, int width, int height); /* Basic comparison functions */ META_EXPORT int meta_rectangle_area (const MetaRectangle *rect); META_EXPORT gboolean meta_rectangle_intersect (const MetaRectangle *src1, const MetaRectangle *src2, MetaRectangle *dest); META_EXPORT gboolean meta_rectangle_equal (const MetaRectangle *src1, const MetaRectangle *src2); /* Find the bounding box of the union of two rectangles */ META_EXPORT void meta_rectangle_union (const MetaRectangle *rect1, const MetaRectangle *rect2, MetaRectangle *dest); /* overlap is similar to intersect but doesn't provide location of * intersection information. */ META_EXPORT gboolean meta_rectangle_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2); /* vert_overlap means ignore the horizontal location and ask if the * vertical parts overlap. An alternate way to think of it is "Does there * exist a way to shift either rect horizontally so that the two rects * overlap?" horiz_overlap is similar. */ META_EXPORT gboolean meta_rectangle_vert_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2); META_EXPORT gboolean meta_rectangle_horiz_overlap (const MetaRectangle *rect1, const MetaRectangle *rect2); /* could_fit_rect determines whether "outer_rect" is big enough to contain * inner_rect. contains_rect checks whether it actually contains it. */ META_EXPORT gboolean meta_rectangle_could_fit_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect); META_EXPORT gboolean meta_rectangle_contains_rect (const MetaRectangle *outer_rect, const MetaRectangle *inner_rect); #endif /* META_BOXES_H */ muffin-6.4.1/src/meta/display.h0000664000175000017500000003064314723361714015303 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_DISPLAY_H #define META_DISPLAY_H #include #include #include #include #include #include #include #include /** * MetaTabList: * @META_TAB_LIST_NORMAL: Normal windows * @META_TAB_LIST_DOCKS: Dock windows * @META_TAB_LIST_GROUP: Groups * @META_TAB_LIST_NORMAL_ALL: All windows */ typedef enum { META_TAB_LIST_NORMAL, META_TAB_LIST_DOCKS, META_TAB_LIST_GROUP, META_TAB_LIST_NORMAL_ALL } MetaTabList; /** * MetaTabShowType: * @META_TAB_SHOW_ICON: Show icon (Alt-Tab mode) * @META_TAB_SHOW_INSTANTLY: Show instantly (Alt-Esc mode) */ typedef enum { META_TAB_SHOW_ICON, /* Alt-Tab mode */ META_TAB_SHOW_INSTANTLY /* Alt-Esc mode */ } MetaTabShowType; typedef enum { META_PAD_ACTION_BUTTON, /* Action is a button */ META_PAD_ACTION_RING, /* Action is a ring */ META_PAD_ACTION_STRIP, /* Action is a strip */ } MetaPadActionType; typedef enum { META_TILE_NONE, META_TILE_LEFT, META_TILE_RIGHT, META_TILE_ULC, META_TILE_LLC, META_TILE_URC, META_TILE_LRC, META_TILE_TOP, META_TILE_BOTTOM, META_TILE_MAXIMIZED } MetaTileMode; typedef struct _MetaDisplayClass MetaDisplayClass; #define META_TYPE_DISPLAY (meta_display_get_type ()) #define META_DISPLAY(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), META_TYPE_DISPLAY, MetaDisplay)) #define META_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_DISPLAY, MetaDisplayClass)) #define META_IS_DISPLAY(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), META_TYPE_DISPLAY)) #define META_IS_DISPLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_DISPLAY)) #define META_DISPLAY_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_DISPLAY, MetaDisplayClass)) META_EXPORT GType meta_display_get_type (void) G_GNUC_CONST; #define meta_XFree(p) do { if ((p)) XFree ((p)); } while (0) META_EXPORT gboolean meta_display_supports_extended_barriers (MetaDisplay *display); META_EXPORT void meta_display_close (MetaDisplay *display, guint32 timestamp); META_EXPORT MetaCompositor *meta_display_get_compositor (MetaDisplay *display); META_EXPORT MetaX11Display *meta_display_get_x11_display (MetaDisplay *display); META_EXPORT MetaWindow *meta_display_get_focus_window (MetaDisplay *display); META_EXPORT gboolean meta_display_xserver_time_is_before (MetaDisplay *display, guint32 time1, guint32 time2); META_EXPORT guint32 meta_display_get_last_user_time (MetaDisplay *display); META_EXPORT guint32 meta_display_get_current_time (MetaDisplay *display); META_EXPORT guint32 meta_display_get_current_time_roundtrip (MetaDisplay *display); META_EXPORT GList* meta_display_get_tab_list (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace); META_EXPORT MetaWindow* meta_display_get_tab_next (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace, MetaWindow *window, gboolean backward); META_EXPORT MetaWindow* meta_display_get_tab_current (MetaDisplay *display, MetaTabList type, MetaWorkspace *workspace); META_EXPORT gboolean meta_display_begin_grab_op (MetaDisplay *display, MetaWindow *window, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, guint32 timestamp, int root_x, int root_y); META_EXPORT void meta_display_end_grab_op (MetaDisplay *display, guint32 timestamp); META_EXPORT MetaGrabOp meta_display_get_grab_op (MetaDisplay *display); META_EXPORT guint meta_display_add_keybinding (MetaDisplay *display, const char *name, GSettings *settings, MetaKeyBindingFlags flags, MetaKeyHandlerFunc handler, gpointer user_data, GDestroyNotify free_data); META_EXPORT guint meta_display_add_custom_keybinding (MetaDisplay *display, const char *name, const char **bindings, MetaKeyHandlerFunc callback, gpointer user_data, GDestroyNotify free_data); META_EXPORT gboolean meta_display_remove_keybinding (MetaDisplay *display, const char *name); META_EXPORT guint meta_display_grab_accelerator (MetaDisplay *display, const char *accelerator, MetaKeyBindingFlags flags); META_EXPORT gboolean meta_display_ungrab_accelerator (MetaDisplay *display, guint action_id); META_EXPORT guint meta_display_get_keybinding_action (MetaDisplay *display, unsigned int keycode, unsigned long mask); META_EXPORT GSList *meta_display_sort_windows_by_stacking (MetaDisplay *display, GSList *windows); META_EXPORT void meta_display_add_ignored_crossing_serial (MetaDisplay *display, unsigned long serial); META_EXPORT void meta_display_clear_mouse_mode (MetaDisplay *display); META_EXPORT void meta_display_freeze_keyboard (MetaDisplay *display, guint32 timestamp); META_EXPORT void meta_display_ungrab_keyboard (MetaDisplay *display, guint32 timestamp); META_EXPORT void meta_display_unfreeze_keyboard (MetaDisplay *display, guint32 timestamp); META_EXPORT gboolean meta_display_is_pointer_emulating_sequence (MetaDisplay *display, ClutterEventSequence *sequence); META_EXPORT void meta_display_request_pad_osd (MetaDisplay *display, ClutterInputDevice *pad, gboolean edition_mode); META_EXPORT gchar * meta_display_get_pad_action_label (MetaDisplay *display, ClutterInputDevice *pad, MetaPadActionType action_type, guint action_number); META_EXPORT void meta_display_get_size (MetaDisplay *display, int *width, int *height); META_EXPORT void meta_display_set_cursor (MetaDisplay *display, MetaCursor cursor); /** * MetaDisplayDirection: * @META_DISPLAY_UP: up * @META_DISPLAY_DOWN: down * @META_DISPLAY_LEFT: left * @META_DISPLAY_RIGHT: right */ typedef enum { META_DISPLAY_UP, META_DISPLAY_DOWN, META_DISPLAY_LEFT, META_DISPLAY_RIGHT } MetaDisplayDirection; META_EXPORT int meta_display_get_n_monitors (MetaDisplay *display); META_EXPORT int meta_display_get_primary_monitor (MetaDisplay *display); META_EXPORT int meta_display_get_current_monitor (MetaDisplay *display); META_EXPORT const gchar* meta_display_get_monitor_name (MetaDisplay *display, int monitor); META_EXPORT void meta_display_get_monitor_geometry (MetaDisplay *display, int monitor, MetaRectangle *geometry); META_EXPORT float meta_display_get_monitor_scale (MetaDisplay *display, int monitor); META_EXPORT gboolean meta_display_get_monitor_in_fullscreen (MetaDisplay *display, int monitor); META_EXPORT int meta_display_get_monitor_index_for_rect (MetaDisplay *display, MetaRectangle *rect); META_EXPORT int meta_display_get_monitor_neighbor_index (MetaDisplay *display, int which_monitor, MetaDisplayDirection dir); META_EXPORT MetaWindow *meta_display_get_pointer_window (MetaDisplay *display, MetaWindow *not_this_one); META_EXPORT void meta_display_focus_default_window (MetaDisplay *display, guint32 timestamp); META_EXPORT gint meta_display_xinerama_index_to_logical_index (MetaDisplay *display, gint x_index); META_EXPORT gint meta_display_logical_index_to_xinerama_index (MetaDisplay *display, gint log_index); /** * MetaDisplayCorner: * @META_DISPLAY_TOPLEFT: top-left corner * @META_DISPLAY_TOPRIGHT: top-right corner * @META_DISPLAY_BOTTOMLEFT: bottom-left corner * @META_DISPLAY_BOTTOMRIGHT: bottom-right corner */ typedef enum { META_DISPLAY_TOPLEFT, META_DISPLAY_TOPRIGHT, META_DISPLAY_BOTTOMLEFT, META_DISPLAY_BOTTOMRIGHT } MetaDisplayCorner; META_EXPORT MetaWorkspaceManager *meta_display_get_workspace_manager (MetaDisplay *display); /** * meta_display_get_startup_notification: (skip) */ META_EXPORT MetaStartupNotification * meta_display_get_startup_notification (MetaDisplay *display); META_EXPORT MetaSoundPlayer * meta_display_get_sound_player (MetaDisplay *display); META_EXPORT MetaSelection * meta_display_get_selection (MetaDisplay *display); META_EXPORT void meta_display_set_input_focus (MetaDisplay *display, MetaWindow *window, gboolean focus_frame, guint32 timestamp); META_EXPORT void meta_display_unset_input_focus (MetaDisplay *display, guint32 timestamp); typedef enum { META_LIST_DEFAULT = 0, /* normal windows */ META_LIST_INCLUDE_OVERRIDE_REDIRECT = 1 << 0, /* normal and O-R */ META_LIST_SORTED = 1 << 1, /* sort list by mru */ } MetaListWindowsFlags; META_EXPORT GSList* meta_display_list_windows (MetaDisplay *display, MetaListWindowsFlags flags); META_EXPORT void meta_display_set_desklets_above (MetaDisplay *display, gboolean above); META_EXPORT gboolean meta_display_get_desklets_above (MetaDisplay *display); META_EXPORT void meta_display_push_tile (MetaDisplay *display, MetaWindow *window, MetaMotionDirection direction); #endif muffin-6.4.1/src/meta/meta-workspace-manager.h0000664000175000017500000000734214723361714020170 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WORKSPACE_MANAGER_H #define META_WORKSPACE_MANAGER_H #include #include #include #include #include #define META_TYPE_WORKSPACE_MANAGER (meta_workspace_manager_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaWorkspaceManager, meta_workspace_manager, META, WORKSPACE_MANAGER, GObject) META_EXPORT GList *meta_workspace_manager_get_workspaces (MetaWorkspaceManager *workspace_manager); META_EXPORT int meta_workspace_manager_get_n_workspaces (MetaWorkspaceManager *workspace_manager); META_EXPORT MetaWorkspace* meta_workspace_manager_get_workspace_by_index (MetaWorkspaceManager *workspace_manager, int index); META_EXPORT void meta_workspace_manager_remove_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace, guint32 timestamp); META_EXPORT MetaWorkspace *meta_workspace_manager_append_new_workspace (MetaWorkspaceManager *workspace_manager, gboolean activate, guint32 timestamp); META_EXPORT void meta_workspace_manager_reorder_workspace (MetaWorkspaceManager *workspace_manager, MetaWorkspace *workspace, int new_index); META_EXPORT int meta_workspace_manager_get_active_workspace_index (MetaWorkspaceManager *workspace_manager); META_EXPORT MetaWorkspace *meta_workspace_manager_get_active_workspace (MetaWorkspaceManager *workspace_manager); META_EXPORT void meta_workspace_manager_override_workspace_layout (MetaWorkspaceManager *workspace_manager, MetaDisplayCorner starting_corner, gboolean vertical_layout, int n_rows, int n_columns); /* Show/hide the desktop (temporarily hide all windows) */ META_EXPORT void meta_workspace_manager_show_desktop (MetaWorkspaceManager *workspace_manager, guint32 timestamp); META_EXPORT void meta_workspace_manager_unshow_desktop (MetaWorkspaceManager *workspace_manager); META_EXPORT void meta_workspace_manager_toggle_desktop (MetaWorkspaceManager *workspace_manager, guint32 timestamp); #endif /* META_WORKSPACE_MANAGER_H */ muffin-6.4.1/src/meta/compositor-mutter.h0000664000175000017500000000417114723361714017347 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Matthew Allum * Copyright (C) 2007 Iain Holmes * Based on xcompmgr - (c) 2003 Keith Packard * xfwm4 - (c) 2005-2007 Olivier Fourdan * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MUTTER_H_ #define MUTTER_H_ #include "clutter/clutter.h" #include "meta/compositor.h" #include "meta/meta-window-actor.h" #include "meta/types.h" /* Public compositor API */ META_EXPORT ClutterActor *meta_get_stage_for_display (MetaDisplay *display); META_EXPORT GList *meta_get_window_actors (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_window_group_for_display (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_top_window_group_for_display (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_bottom_window_group_for_display (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_feedback_group_for_display (MetaDisplay *display); META_EXPORT void meta_disable_unredirect_for_display (MetaDisplay *display); META_EXPORT void meta_enable_unredirect_for_display (MetaDisplay *display); META_EXPORT void meta_focus_stage_window (MetaDisplay *display, guint32 timestamp); META_EXPORT gboolean meta_stage_is_focused (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_x11_background_actor_for_display (MetaDisplay *display); META_EXPORT ClutterActor *meta_get_desklet_container_for_display (MetaDisplay *display); #endif muffin-6.4.1/src/meta/meta-background-group.h0000664000175000017500000000116714723361714020032 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_GROUP_H #define META_BACKGROUND_GROUP_H #include "clutter/clutter.h" #include #define META_TYPE_BACKGROUND_GROUP (meta_background_group_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaBackgroundGroup, meta_background_group, META, BACKGROUND_GROUP, ClutterActor) struct _MetaBackgroundGroupClass { ClutterActorClass parent_class; }; META_EXPORT ClutterActor *meta_background_group_new (void); #endif /* META_BACKGROUND_GROUP_H */ muffin-6.4.1/src/meta/meta-monitor-manager.h0000664000175000017500000000524414723361714017660 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_MONITOR_MANAGER_H #define META_MONITOR_MANAGER_H #include typedef enum { META_MONITOR_SWITCH_CONFIG_ALL_MIRROR, META_MONITOR_SWITCH_CONFIG_ALL_LINEAR, META_MONITOR_SWITCH_CONFIG_EXTERNAL, META_MONITOR_SWITCH_CONFIG_BUILTIN, META_MONITOR_SWITCH_CONFIG_UNKNOWN, } MetaMonitorSwitchConfigType; typedef enum { META_XRANDR_ROTATION_NORMAL, META_XRANDR_ROTATION_LEFT, META_XRANDR_ROTATION_FLIPPED, META_XRANDR_ROTATION_RIGHT } MetaXrandrRotation; typedef struct _MetaMonitorManagerClass MetaMonitorManagerClass; typedef struct _MetaMonitorManager MetaMonitorManager; META_EXPORT GType meta_monitor_manager_get_type (void); META_EXPORT MetaMonitorManager *meta_monitor_manager_get (void); META_EXPORT gint meta_monitor_manager_get_monitor_for_connector (MetaMonitorManager *manager, const char *connector); META_EXPORT gboolean meta_monitor_manager_get_is_builtin_display_on (MetaMonitorManager *manager); META_EXPORT void meta_monitor_manager_switch_config (MetaMonitorManager *manager, MetaMonitorSwitchConfigType config_type); META_EXPORT gboolean meta_monitor_manager_can_switch_config (MetaMonitorManager *manager); META_EXPORT MetaMonitorSwitchConfigType meta_monitor_manager_get_switch_config (MetaMonitorManager *manager); META_EXPORT gint meta_monitor_manager_get_display_configuration_timeout (void); META_EXPORT gboolean meta_monitor_manager_can_apply_rotation (MetaMonitorManager *manager, MetaXrandrRotation rotation); META_EXPORT gboolean meta_monitor_manager_apply_temporary_rotation (MetaMonitorManager *manager, MetaXrandrRotation rotation); META_EXPORT gboolean meta_monitor_manager_get_current_rotation (MetaMonitorManager *manager, MetaXrandrRotation *rotation); #endif /* META_MONITOR_MANAGER_H */ muffin-6.4.1/src/meta/meta-plugin.h0000664000175000017500000003440014723361714016053 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_PLUGIN_H_ #define META_PLUGIN_H_ #include #include #include "clutter/clutter.h" #include "meta/compositor-mutter.h" #include "meta/compositor.h" #include "meta/meta-close-dialog.h" #include "meta/meta-inhibit-shortcuts-dialog.h" #include "meta/meta-version.h" #include "meta/types.h" #define META_TYPE_PLUGIN (meta_plugin_get_type ()) META_EXPORT G_DECLARE_DERIVABLE_TYPE (MetaPlugin, meta_plugin, META, PLUGIN, GObject) typedef struct _MetaPluginVersion MetaPluginVersion; typedef struct _MetaPluginInfo MetaPluginInfo; /** * MetaPluginClass: * @start: virtual function called when the compositor starts managing a screen * @minimize: virtual function called when a window is minimized * @size_change: virtual function called when a window changes size to/from constraints * @map: virtual function called when a window is mapped * @destroy: virtual function called when a window is destroyed * @switch_workspace: virtual function called when the user switches to another * workspace * @kill_window_effects: virtual function called when the effects on a window * need to be killed prematurely; the plugin must call the completed() callback * as if the effect terminated naturally * @kill_switch_workspace: virtual function called when the workspace-switching * effect needs to be killed prematurely * @xevent_filter: virtual function called when handling each event * @keybinding_filter: virtual function called when handling each keybinding * @plugin_info: virtual function that returns information about the * #MetaPlugin */ struct _MetaPluginClass { /*< private >*/ GObjectClass parent_class; /*< public >*/ /** * MetaPluginClass::start: * * Virtual function called when the compositor starts managing a screen */ void (*start) (MetaPlugin *plugin); /** * MetaPluginClass::minimize: * @actor: a #MetaWindowActor * * Virtual function called when the window represented by @actor is minimized. */ void (*minimize) (MetaPlugin *plugin, MetaWindowActor *actor); /** * MetaPluginClass::unminimize: * @actor: a #MetaWindowActor * * Virtual function called when the window represented by @actor is unminimized. */ void (*unminimize) (MetaPlugin *plugin, MetaWindowActor *actor); void (*size_changed) (MetaPlugin *plugin, MetaWindowActor *actor); void (*size_change) (MetaPlugin *plugin, MetaWindowActor *actor, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect); /** * MetaPluginClass::map: * @actor: a #MetaWindowActor * * Virtual function called when the window represented by @actor is mapped. */ void (*map) (MetaPlugin *plugin, MetaWindowActor *actor); /** * MetaPluginClass::destroy: * @actor: a #MetaWindowActor * * Virtual function called when the window represented by @actor is destroyed. */ void (*destroy) (MetaPlugin *plugin, MetaWindowActor *actor); /** * MetaPluginClass::switch_workspace: * @from: origin workspace * @to: destination workspace * @direction: a #MetaMotionDirection * * Virtual function called when the window represented by @actor is destroyed. */ void (*switch_workspace) (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction); void (*show_tile_preview) (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number); void (*hide_tile_preview) (MetaPlugin *plugin); void (*show_window_menu) (MetaPlugin *plugin, MetaWindow *window, MetaWindowMenuType menu, int x, int y); void (*show_window_menu_for_rect) (MetaPlugin *plugin, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect); /** * MetaPluginClass::kill_window_effects: * @actor: a #MetaWindowActor * * Virtual function called when the effects on @actor need to be killed * prematurely; the plugin must call the completed() callback as if the effect * terminated naturally. */ void (*kill_window_effects) (MetaPlugin *plugin, MetaWindowActor *actor); /** * MetaPluginClass::kill_switch_workspace: * * Virtual function called when the workspace-switching effect needs to be * killed prematurely. */ void (*kill_switch_workspace) (MetaPlugin *plugin); /** * MetaPluginClass::xevent_filter: * @event: (type xlib.XEvent): * * Virtual function called when handling each event. * * Returns: %TRUE if the plugin handled the event type (i.e., if the return * value is %FALSE, there will be no subsequent call to the manager * completed() callback, and the compositor must ensure that any appropriate * post-effect cleanup is carried out. */ gboolean (*xevent_filter) (MetaPlugin *plugin, XEvent *event); /** * MetaPluginClass::keybinding_filter: * @binding: a #MetaKeyBinding * * Virtual function called when handling each keybinding. * * Returns: %TRUE if the plugin handled the keybinding. */ gboolean (*keybinding_filter) (MetaPlugin *plugin, MetaKeyBinding *binding); /** * MetaPluginClass::confirm_display_config: * @plugin: a #MetaPlugin * * Virtual function called when the display configuration changes. * The common way to implement this function is to show some form * of modal dialog that should ask the user if everything was ok. * * When confirmed by the user, the plugin must call meta_plugin_complete_display_change() * to make the configuration permanent. If that function is not * called within the timeout, the previous configuration will be * reapplied. */ void (*confirm_display_change) (MetaPlugin *plugin); /** * MetaPluginClass::plugin_info: * @plugin: a #MetaPlugin * * Virtual function that returns information about the #MetaPlugin. * * Returns: a #MetaPluginInfo. */ const MetaPluginInfo * (*plugin_info) (MetaPlugin *plugin); /** * MetaPluginClass::create_close_dialog: * @plugin: a #MetaPlugin * @window: a #MetaWindow * * Virtual function called to create a "force quit" dialog * on non-responsive clients. */ MetaCloseDialog * (* create_close_dialog) (MetaPlugin *plugin, MetaWindow *window); /** * MetaPluginClass::create_inhibit_shortcuts_dialog: * @plugin: a #MetaPlugin * @window: a #MetaWindow * * Virtual function called to create a "inhibit shortcuts" dialog * when a client requests compositor shortcuts to be inhibited. */ MetaInhibitShortcutsDialog * (* create_inhibit_shortcuts_dialog) (MetaPlugin *plugin, MetaWindow *window); /** * MetaPluginClass::locate_pointer: * * Virtual function called when the user triggered the "locate-pointer" * mechanism. * The common way to implement this function is to show some animation * on screen to draw user attention on the pointer location. */ void (*locate_pointer) (MetaPlugin *plugin); }; /** * MetaPluginInfo: * @name: name of the plugin * @version: version of the plugin * @author: author of the plugin * @license: license of the plugin * @description: description of the plugin */ struct _MetaPluginInfo { const gchar *name; const gchar *version; const gchar *author; const gchar *license; const gchar *description; }; META_EXPORT const MetaPluginInfo * meta_plugin_get_info (MetaPlugin *plugin); /** * MetaPluginVersion: * @version_major: major component of the version number of Meta with which the plugin was compiled * @version_minor: minor component of the version number of Meta with which the plugin was compiled * @version_micro: micro component of the version number of Meta with which the plugin was compiled * @version_api: version of the plugin API */ struct _MetaPluginVersion { /* * Version information; the first three numbers match the Meta version * with which the plugin was compiled (see clutter-plugins/simple.c for sample * code). */ guint version_major; guint version_minor; guint version_micro; /* * Version of the plugin API; this is unrelated to the matacity version * per se. The API version is checked by the plugin manager and must match * the one used by it (see clutter-plugins/default.c for sample code). */ guint version_api; }; /* * Convenience macro to set up the plugin type. Based on GEdit. */ #define META_PLUGIN_DECLARE_WITH_CODE(ObjectName, object_name, CODE) \ G_MODULE_EXPORT MetaPluginVersion meta_plugin_version = \ { \ META_MAJOR_VERSION, \ META_MINOR_VERSION, \ META_MICRO_VERSION, \ META_PLUGIN_API_VERSION \ }; \ \ /* Prototypes */ \ G_MODULE_EXPORT GType \ object_name##_get_type (void); \ \ G_MODULE_EXPORT GType \ meta_plugin_register_type (GTypeModule *type_module); \ \ \ G_DEFINE_DYNAMIC_TYPE_EXTENDED(ObjectName, object_name, \ META_TYPE_PLUGIN, 0, CODE) \ \ /* Unused, but required by G_DEFINE_DYNAMIC_TYPE */ \ static void \ object_name##_class_finalize (ObjectName##Class *klass) {} \ \ G_MODULE_EXPORT GType \ meta_plugin_register_type (GTypeModule *type_module) \ { \ object_name##_register_type (type_module); \ return object_name##_get_type (); \ } \ #define META_PLUGIN_DECLARE(ObjectName, object_name) \ META_PLUGIN_DECLARE_WITH_CODE(ObjectName, object_name, {}) META_EXPORT void meta_plugin_switch_workspace_completed (MetaPlugin *plugin); META_EXPORT void meta_plugin_minimize_completed (MetaPlugin *plugin, MetaWindowActor *actor); META_EXPORT void meta_plugin_unminimize_completed (MetaPlugin *plugin, MetaWindowActor *actor); META_EXPORT void meta_plugin_size_change_completed (MetaPlugin *plugin, MetaWindowActor *actor); META_EXPORT void meta_plugin_map_completed (MetaPlugin *plugin, MetaWindowActor *actor); META_EXPORT void meta_plugin_destroy_completed (MetaPlugin *plugin, MetaWindowActor *actor); META_EXPORT void meta_plugin_complete_display_change (MetaPlugin *plugin, gboolean ok); /** * MetaModalOptions: * @META_MODAL_POINTER_ALREADY_GRABBED: if set the pointer is already * grabbed by the plugin and should not be grabbed again. * @META_MODAL_KEYBOARD_ALREADY_GRABBED: if set the keyboard is already * grabbed by the plugin and should not be grabbed again. * * Options that can be provided when calling meta_plugin_begin_modal(). */ typedef enum { META_MODAL_POINTER_ALREADY_GRABBED = 1 << 0, META_MODAL_KEYBOARD_ALREADY_GRABBED = 1 << 1 } MetaModalOptions; META_EXPORT gboolean meta_plugin_begin_modal (MetaPlugin *plugin, MetaModalOptions options, guint32 timestamp); META_EXPORT void meta_plugin_end_modal (MetaPlugin *plugin, guint32 timestamp); META_EXPORT MetaDisplay *meta_plugin_get_display (MetaPlugin *plugin); void _meta_plugin_set_compositor (MetaPlugin *plugin, MetaCompositor *compositor); /* XXX: Putting this in here so it's in the public header. */ META_EXPORT void meta_plugin_manager_set_plugin_type (GType gtype); #endif /* META_PLUGIN_H_ */ muffin-6.4.1/src/meta/meta-launch-context.h0000664000175000017500000000261414723361714017513 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_LAUNCH_CONTEXT_H #define META_LAUNCH_CONTEXT_H #include META_EXPORT G_DECLARE_FINAL_TYPE (MetaLaunchContext, meta_launch_context, META, LAUNCH_CONTEXT, GAppLaunchContext) #define META_TYPE_LAUNCH_CONTEXT (meta_launch_context_get_type ()) META_EXPORT void meta_launch_context_set_timestamp (MetaLaunchContext *context, uint32_t timestamp); META_EXPORT void meta_launch_context_set_workspace (MetaLaunchContext *context, MetaWorkspace *workspace); #endif /* META_LAUNCH_CONTEXT_H */ muffin-6.4.1/src/meta/meta-x11-display.h0000664000175000017500000000444414723361714016636 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2008 Iain Holmes * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_X11_DISPLAY_H #define META_X11_DISPLAY_H #include #include #include #include #include #define META_TYPE_X11_DISPLAY (meta_x11_display_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaX11Display, meta_x11_display, META, X11_DISPLAY, GObject) META_EXPORT gboolean meta_x11_init_gdk_display (GError **error); META_EXPORT int meta_x11_display_get_screen_number (MetaX11Display *x11_display); META_EXPORT Display *meta_x11_display_get_xdisplay (MetaX11Display *x11_display); META_EXPORT Window meta_x11_display_get_xroot (MetaX11Display *x11_display); META_EXPORT int meta_x11_display_get_xinput_opcode (MetaX11Display *x11_display); META_EXPORT int meta_x11_display_get_damage_event_base (MetaX11Display *x11_display); META_EXPORT int meta_x11_display_get_shape_event_base (MetaX11Display *x11_display); META_EXPORT gboolean meta_x11_display_has_shape (MetaX11Display *x11_display); META_EXPORT void meta_x11_display_set_cm_selection (MetaX11Display *x11_display); META_EXPORT gboolean meta_x11_display_xwindow_is_a_no_focus_window (MetaX11Display *x11_display, Window xwindow); META_EXPORT void meta_x11_display_set_stage_input_region (MetaX11Display *x11_display, XserverRegion region); META_EXPORT void meta_x11_display_clear_stage_input_region (MetaX11Display *x11_display); #endif /* META_X11_DISPLAY_H */ muffin-6.4.1/src/meta/meta-shadow-factory.h0000664000175000017500000001161714723361714017514 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaShadowFactory: * * Create and cache shadow textures for arbitrary window shapes * * Copyright (C) 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_SHADOW_FACTORY_H__ #define __META_SHADOW_FACTORY_H__ #include #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/meta-window-shape.h" META_EXPORT GType meta_shadow_get_type (void) G_GNUC_CONST; /** * MetaShadowParams: * @radius: the radius (gaussian standard deviation) of the shadow * @top_fade: if >= 0, the shadow doesn't extend above the top * of the shape, and fades out over the given number of pixels * @x_offset: horizontal offset of the shadow with respect to the * shape being shadowed, in pixels * @y_offset: vertical offset of the shadow with respect to the * shape being shadowed, in pixels * @opacity: opacity of the shadow, from 0 to 255 * * The #MetaShadowParams structure holds information about how to draw * a particular style of shadow. */ typedef struct _MetaShadowParams MetaShadowParams; struct _MetaShadowParams { int radius; int top_fade; int x_offset; int y_offset; guint8 opacity; }; #define META_TYPE_SHADOW_FACTORY (meta_shadow_factory_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaShadowFactory, meta_shadow_factory, META, SHADOW_FACTORY, GObject) /** * MetaShadowFactory: * * #MetaShadowFactory is used to create window shadows. It caches shadows internally * so that multiple shadows created for the same shape with the same radius will * share the same MetaShadow. */ META_EXPORT MetaShadowFactory *meta_shadow_factory_get_default (void); META_EXPORT void meta_shadow_factory_set_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, MetaShadowParams *params); META_EXPORT void meta_shadow_factory_get_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, MetaShadowParams *params); /** * MetaShadow: * #MetaShadow holds a shadow texture along with information about how to * apply that texture to draw a window texture. (E.g., it knows how big the * unscaled borders are on each side of the shadow texture.) */ typedef struct _MetaShadow MetaShadow; META_EXPORT MetaShadow *meta_shadow_ref (MetaShadow *shadow); META_EXPORT void meta_shadow_unref (MetaShadow *shadow); META_EXPORT void meta_shadow_paint (MetaShadow *shadow, CoglFramebuffer *framebuffer, int window_x, int window_y, int window_width, int window_height, guint8 opacity, cairo_region_t *clip, gboolean clip_strictly); META_EXPORT void meta_shadow_get_bounds (MetaShadow *shadow, int window_x, int window_y, int window_width, int window_height, cairo_rectangle_int_t *bounds); META_EXPORT MetaShadowFactory *meta_shadow_factory_new (void); META_EXPORT MetaShadow *meta_shadow_factory_get_shadow (MetaShadowFactory *factory, MetaWindowShape *shape, int width, int height, const char *class_name, gboolean focused); #endif /* __META_SHADOW_FACTORY_H__ */ muffin-6.4.1/src/meta/meta-stage.h0000664000175000017500000000200614723361714015655 0ustar fabiofabio/* * Copyright (C) 2012 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_STAGE_H #define META_STAGE_H #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_STAGE (meta_stage_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaStage, meta_stage, META, STAGE, ClutterStage) G_END_DECLS #endif /* META_STAGE_H */ muffin-6.4.1/src/org.cinnamon.Muffin.DisplayConfig.xml0000664000175000017500000004262114723361714021625 0ustar fabiofabio muffin-6.4.1/src/x11/0000775000175000017500000000000014723361714013142 5ustar fabiofabiomuffin-6.4.1/src/x11/meta-selection-source-x11.c0000664000175000017500000002241414723361714020127 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "x11/meta-x11-selection-input-stream-private.h" #include "x11/meta-selection-source-x11-private.h" #define MAX_MIMETYPE_SIZE 4096 struct _MetaSelectionSourceX11 { MetaSelectionSource parent_instance; MetaX11Display *x11_display; GList *mimetypes; Window owner; Atom xselection; uint32_t timestamp; }; G_DEFINE_TYPE (MetaSelectionSourceX11, meta_selection_source_x11, META_TYPE_SELECTION_SOURCE) static void stream_new_cb (GObject *source, GAsyncResult *res, GTask *task) { GInputStream *stream; GError *error = NULL; stream = meta_x11_selection_input_stream_new_finish (res, NULL, NULL, &error); if (stream) g_task_return_pointer (task, stream, g_object_unref); else g_task_return_error (task, error); g_object_unref (task); } static void meta_selection_source_x11_finalize (GObject *object) { MetaSelectionSourceX11 *source_x11 = META_SELECTION_SOURCE_X11 (object); g_list_free_full (source_x11->mimetypes, g_free); G_OBJECT_CLASS (meta_selection_source_x11_parent_class)->finalize (object); } static void meta_selection_source_x11_read_async (MetaSelectionSource *source, const gchar *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaSelectionSourceX11 *source_x11 = META_SELECTION_SOURCE_X11 (source); GTask *task; task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_source_x11_read_async); if (strcmp (mimetype, "text/plain") == 0 && g_list_find_custom (source_x11->mimetypes, "STRING", (GCompareFunc) g_strcmp0)) mimetype = "STRING"; else if (strcmp (mimetype, "text/plain;charset=utf-8") == 0 && g_list_find_custom (source_x11->mimetypes, "UTF8_STRING", (GCompareFunc) g_strcmp0)) mimetype = "UTF8_STRING"; meta_x11_selection_input_stream_new_async (source_x11->x11_display, source_x11->x11_display->selection.xwindow, gdk_x11_get_xatom_name (source_x11->xselection), mimetype, source_x11->timestamp, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback) stream_new_cb, task); } static GInputStream * meta_selection_source_x11_read_finish (MetaSelectionSource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, source), NULL); g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == meta_selection_source_x11_read_async, NULL); return g_task_propagate_pointer (G_TASK (result), error); } static GList * meta_selection_source_x11_get_mimetypes (MetaSelectionSource *source) { MetaSelectionSourceX11 *source_x11 = META_SELECTION_SOURCE_X11 (source); return g_list_copy_deep (source_x11->mimetypes, (GCopyFunc) g_strdup, NULL); } static void meta_selection_source_x11_class_init (MetaSelectionSourceX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaSelectionSourceClass *source_class = META_SELECTION_SOURCE_CLASS (klass); object_class->finalize = meta_selection_source_x11_finalize; source_class->read_async = meta_selection_source_x11_read_async; source_class->read_finish = meta_selection_source_x11_read_finish; source_class->get_mimetypes = meta_selection_source_x11_get_mimetypes; } static void meta_selection_source_x11_init (MetaSelectionSourceX11 *source) { } static GList * atoms_to_mimetypes (MetaX11Display *display, GBytes *bytes) { GList *mimetypes = NULL; const Atom *atoms; gsize size; guint i, n_atoms; gboolean utf8_string_found = FALSE, utf8_text_plain_found = FALSE; gboolean string_found = FALSE, text_plain_found = FALSE; atoms = g_bytes_get_data (bytes, &size); n_atoms = size / sizeof (Atom); for (i = 0; i < n_atoms; i++) { const gchar *mimetype; mimetype = gdk_x11_get_xatom_name (atoms[i]); mimetypes = g_list_prepend (mimetypes, g_strdup (mimetype)); utf8_text_plain_found |= strcmp (mimetype, "text/plain;charset=utf-8") == 0; text_plain_found |= strcmp (mimetype, "text/plain") == 0; utf8_string_found |= strcmp (mimetype, "UTF8_STRING") == 0; string_found |= strcmp (mimetype, "STRING") == 0; } /* Ensure non-x11 clients get well-known mimetypes */ if (string_found && !text_plain_found) mimetypes = g_list_prepend (mimetypes, g_strdup ("text/plain")); if (utf8_string_found && !utf8_text_plain_found) mimetypes = g_list_prepend (mimetypes, g_strdup ("text/plain;charset=utf-8")); return mimetypes; } static void read_mimetypes_cb (GInputStream *stream, GAsyncResult *res, GTask *task) { MetaSelectionSourceX11 *source_x11 = g_task_get_task_data (task); GError *error = NULL; GBytes *bytes; bytes = g_input_stream_read_bytes_finish (stream, res, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); g_object_unref (stream); return; } source_x11->mimetypes = atoms_to_mimetypes (source_x11->x11_display, bytes); g_bytes_unref (bytes); g_task_return_pointer (task, g_object_ref (g_task_get_task_data (task)), g_object_unref); g_object_unref (task); g_object_unref (stream); } static void get_mimetypes_cb (GObject *source, GAsyncResult *res, GTask *task) { GInputStream *stream; GError *error = NULL; stream = meta_x11_selection_input_stream_new_finish (res, NULL, NULL, &error); if (error) { g_task_return_error (task, error); g_object_unref (task); return; } g_input_stream_read_bytes_async (stream, MAX_MIMETYPE_SIZE, G_PRIORITY_DEFAULT, g_task_get_cancellable (task), (GAsyncReadyCallback) read_mimetypes_cb, task); } void meta_selection_source_x11_new_async (MetaX11Display *x11_display, Window owner, uint32_t timestamp, Atom xselection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaSelectionSourceX11 *source; GTask *task; source = g_object_new (META_TYPE_SELECTION_SOURCE_X11, NULL); source->x11_display = x11_display; source->owner = owner; source->timestamp = timestamp; source->xselection = xselection; task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_source_x11_new_async); g_task_set_task_data (task, source, g_object_unref); meta_x11_selection_input_stream_new_async (x11_display, x11_display->selection.xwindow, gdk_x11_get_xatom_name (xselection), "TARGETS", timestamp, G_PRIORITY_DEFAULT, cancellable, (GAsyncReadyCallback) get_mimetypes_cb, task); } MetaSelectionSource * meta_selection_source_x11_new_finish (GAsyncResult *result, GError **error) { GTask *task = G_TASK (result); g_return_val_if_fail (g_task_is_valid (task, NULL), NULL); g_return_val_if_fail (g_task_get_source_tag (task) == meta_selection_source_x11_new_async, NULL); return g_task_propagate_pointer (task, error); } muffin-6.4.1/src/x11/meta-x11-window-control.c0000664000175000017500000002105214723361714017626 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter interface used by GTK+ UI to talk to core */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/meta-x11-window-control.h" #include "core/frame.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "core/workspace-private.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11-private.h" #include "x11/window-x11.h" static MetaWindow * window_from_frame (MetaX11Display *x11_display, Window frame_xwindow) { MetaWindow *window; window = meta_x11_display_lookup_x_window (x11_display, frame_xwindow); if (!window || !window->frame) { meta_bug ("No such frame window 0x%lx!\n", frame_xwindow); return NULL; } return window; } void meta_x11_wm_queue_frame_resize (MetaX11Display *x11_display, Window frame_xwindow) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); meta_window_queue (window, META_QUEUE_MOVE_RESIZE); meta_window_frame_size_changed (window); } static gboolean lower_window_and_transients (MetaWindow *window, gpointer data) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; meta_window_lower (window); meta_window_foreach_transient (window, lower_window_and_transients, NULL); if (meta_prefs_get_raise_on_click ()) { /* Move window to the back of the focusing workspace's MRU list. * Do extra sanity checks to avoid possible race conditions. * (Borrowed from window.c.) */ if (workspace_manager->active_workspace && meta_window_located_on_workspace (window, workspace_manager->active_workspace)) { GList* link; link = g_list_find (workspace_manager->active_workspace->mru_list, window); g_assert (link); workspace_manager->active_workspace->mru_list = g_list_remove_link (workspace_manager->active_workspace->mru_list, link); g_list_free (link); workspace_manager->active_workspace->mru_list = g_list_append (workspace_manager->active_workspace->mru_list, window); } } return FALSE; } void meta_x11_wm_user_lower_and_unfocus (MetaX11Display *x11_display, Window frame_xwindow, uint32_t timestamp) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; lower_window_and_transients (window, NULL); /* Rather than try to figure that out whether we just lowered * the focus window, assume that's always the case. (Typically, * this will be invoked via keyboard action or by a mouse action; * in either case the window or a modal child will have been focused.) */ meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, timestamp); } void meta_x11_wm_toggle_maximize_vertically (MetaX11Display *x11_display, Window frame_xwindow) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); if (META_WINDOW_MAXIMIZED_VERTICALLY (window)) meta_window_unmaximize (window, META_MAXIMIZE_VERTICAL); else meta_window_maximize (window, META_MAXIMIZE_VERTICAL); } void meta_x11_wm_toggle_maximize_horizontally (MetaX11Display *x11_display, Window frame_xwindow) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); if (META_WINDOW_MAXIMIZED_HORIZONTALLY (window)) meta_window_unmaximize (window, META_MAXIMIZE_HORIZONTAL); else meta_window_maximize (window, META_MAXIMIZE_HORIZONTAL); } void meta_x11_wm_toggle_maximize (MetaX11Display *x11_display, Window frame_xwindow) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); if (META_WINDOW_MAXIMIZED (window)) meta_window_unmaximize (window, META_MAXIMIZE_BOTH); else meta_window_maximize (window, META_MAXIMIZE_BOTH); } void meta_x11_wm_show_window_menu (MetaX11Display *x11_display, Window frame_xwindow, MetaWindowMenuType menu, int root_x, int root_y, uint32_t timestamp) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_focus (window, timestamp); meta_window_show_menu (window, menu, root_x, root_y); } void meta_x11_wm_show_window_menu_for_rect (MetaX11Display *x11_display, Window frame_xwindow, MetaWindowMenuType menu, MetaRectangle *rect, uint32_t timestamp) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_focus (window, timestamp); meta_window_show_menu_for_rect (window, menu, rect); } gboolean meta_x11_wm_begin_grab_op (MetaX11Display *x11_display, Window frame_xwindow, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, uint32_t timestamp, int root_x, int root_y) { MetaWindow *window = window_from_frame (x11_display, frame_xwindow); MetaDisplay *display; display = meta_x11_display_get_display (x11_display); return meta_display_begin_grab_op (display, window, op, pointer_already_grabbed, frame_action, button, modmask, timestamp, root_x, root_y); } void meta_x11_wm_end_grab_op (MetaX11Display *x11_display, uint32_t timestamp) { MetaDisplay *display; display = meta_x11_display_get_display (x11_display); meta_display_end_grab_op (display, timestamp); } MetaGrabOp meta_x11_wm_get_grab_op (MetaX11Display *x11_display) { MetaDisplay *display; display = meta_x11_display_get_display (x11_display); return display->grab_op; } void meta_x11_wm_grab_buttons (MetaX11Display *x11_display, Window frame_xwindow) { MetaDisplay *display; display = meta_x11_display_get_display (x11_display); meta_verbose ("Grabbing buttons on frame 0x%lx\n", frame_xwindow); meta_display_grab_window_buttons (display, frame_xwindow); } void meta_x11_wm_set_screen_cursor (MetaX11Display *x11_display, Window frame_on_screen, MetaCursor cursor) { MetaWindow *window = window_from_frame (x11_display, frame_on_screen); meta_frame_set_screen_cursor (window->frame, cursor); } muffin-6.4.1/src/x11/meta-x11-background.h0000664000175000017500000000175414723361714016774 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_X11_BACKGROUND_H #define META_X11_BACKGROUND_H #include #include #define META_TYPE_X11_BACKGROUND (meta_x11_background_get_type ()) META_EXPORT G_DECLARE_FINAL_TYPE (MetaX11Background, meta_x11_background, META, X11_BACKGROUND, ClutterActor) ClutterActor * meta_x11_background_new (MetaDisplay *display); void meta_x11_background_set_layer (MetaX11Background *self, CoglTexture *texture); void meta_x11_background_set_layer_wrap_mode (MetaX11Background *self, CoglPipelineWrapMode wrap_mode); void meta_x11_background_set_visible_region (MetaX11Background *self, cairo_region_t *visible_region); #endif /* META_X11_BACKGROUND_H */ muffin-6.4.1/src/x11/events.h0000664000175000017500000000212414723361714014616 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "meta/display.h" #ifndef META_EVENTS_X11_H #define META_EVENTS_X11_H void meta_x11_display_init_events (MetaX11Display *x11_display); void meta_x11_display_free_events (MetaX11Display *x11_display); #endif muffin-6.4.1/src/x11/mutter-Xatomtype.h0000664000175000017500000001227214723361714016627 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file mutter-Xatomtype.h Types for communicating with X about properties * * This files defines crock C structures for calling XGetWindowProperty and * XChangeProperty. All fields must be longs as the semantics of property * routines will handle conversion to and from actual 32 bit objects. If your * compiler doesn't treat &structoflongs the same as &arrayoflongs[0], you * will have some work to do. */ /*********************************************************** Copyright 1987, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ #ifndef _XATOMTYPE_H_ #define _XATOMTYPE_H_ #define BOOL int32_t #define SIGNEDINT int32_t #define UNSIGNEDINT uint32_t #define RESOURCEID uint32_t /* this structure may be extended, but do not change the order */ typedef struct { UNSIGNEDINT flags; SIGNEDINT x, y, width, height; /* need to cvt; only for pre-ICCCM */ SIGNEDINT minWidth, minHeight; /* need to cvt */ SIGNEDINT maxWidth, maxHeight; /* need to cvt */ SIGNEDINT widthInc, heightInc; /* need to cvt */ SIGNEDINT minAspectX, minAspectY; /* need to cvt */ SIGNEDINT maxAspectX, maxAspectY; /* need to cvt */ SIGNEDINT baseWidth,baseHeight; /* need to cvt; ICCCM version 1 */ SIGNEDINT winGravity; /* need to cvt; ICCCM version 1 */ } xPropSizeHints; #define OldNumPropSizeElements 15 /* pre-ICCCM */ #define NumPropSizeElements 18 /* ICCCM version 1 */ /* this structure may be extended, but do not change the order */ /* RGB properties */ typedef struct { RESOURCEID colormap; UNSIGNEDINT red_max; UNSIGNEDINT red_mult; UNSIGNEDINT green_max; UNSIGNEDINT green_mult; UNSIGNEDINT blue_max; UNSIGNEDINT blue_mult; UNSIGNEDINT base_pixel; RESOURCEID visualid; /* ICCCM version 1 */ RESOURCEID killid; /* ICCCM version 1 */ } xPropStandardColormap; #define OldNumPropStandardColormapElements 8 /* pre-ICCCM */ #define NumPropStandardColormapElements 10 /* ICCCM version 1 */ /* this structure may be extended, but do not change the order */ typedef struct { UNSIGNEDINT flags; BOOL input; /* need to convert */ SIGNEDINT initialState; /* need to cvt */ RESOURCEID iconPixmap; RESOURCEID iconWindow; SIGNEDINT iconX; /* need to cvt */ SIGNEDINT iconY; /* need to cvt */ RESOURCEID iconMask; UNSIGNEDINT windowGroup; } xPropWMHints; #define NumPropWMHintsElements 9 /* number of elements in this structure */ /* this structure defines the icon size hints information */ typedef struct { SIGNEDINT minWidth, minHeight; /* need to cvt */ SIGNEDINT maxWidth, maxHeight; /* need to cvt */ SIGNEDINT widthInc, heightInc; /* need to cvt */ } xPropIconSize; #define NumPropIconSizeElements 6 /* number of elements in this structure */ /* this structure defines the window manager state information */ typedef struct { SIGNEDINT state; /* need to cvt */ RESOURCEID iconWindow; } xPropWMState; #define NumPropWMStateElements 2 /* number of elements in struct */ #undef BOOL #undef SIGNEDINT #undef UNSIGNEDINT #undef RESOURCEID #endif /* _XATOMTYPE_H_ */ muffin-6.4.1/src/x11/meta-x11-window-control.h0000664000175000017500000000727314723361714017644 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter interface used by GTK+ UI to talk to core */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_X11_WINDOW__CONTROL_H #define META_X11_WINDOW__CONTROL_H #include #include "meta/boxes.h" #include "meta/common.h" #include "x11/meta-x11-display-private.h" void meta_x11_wm_queue_frame_resize (MetaX11Display *x11_display, Window frame_xwindow); void meta_x11_wm_user_lower_and_unfocus (MetaX11Display *x11_display, Window frame_xwindow, uint32_t timestamp); void meta_x11_wm_toggle_maximize (MetaX11Display *x11_display, Window frame_xwindow); void meta_x11_wm_toggle_maximize_horizontally (MetaX11Display *xdisplay, Window frame_xwindow); void meta_x11_wm_toggle_maximize_vertically (MetaX11Display *x11_display, Window frame_xwindow); void meta_x11_wm_show_window_menu (MetaX11Display *x11_xdisplay, Window frame_xwindow, MetaWindowMenuType menu, int root_x, int root_y, uint32_t timestamp); void meta_x11_wm_show_window_menu_for_rect (MetaX11Display *x11_display, Window frame_xwindow, MetaWindowMenuType menu, MetaRectangle *rect, uint32_t timestamp); gboolean meta_x11_wm_begin_grab_op (MetaX11Display *x11_display, Window frame_xwindow, MetaGrabOp op, gboolean pointer_already_grabbed, gboolean frame_action, int button, gulong modmask, uint32_t timestamp, int root_x, int root_y); void meta_x11_wm_end_grab_op (MetaX11Display *x11_display, uint32_t timestamp); MetaGrabOp meta_x11_wm_get_grab_op (MetaX11Display *x11_display); void meta_x11_wm_grab_buttons (MetaX11Display *x11_display, Window frame_xwindow); void meta_x11_wm_set_screen_cursor (MetaX11Display *x11_display, Window frame_on_screen, MetaCursor cursor); #endif /* META_X11_WINDOW_CONTROL_H */ muffin-6.4.1/src/x11/meta-x11-display-private.h0000664000175000017500000002227314723361714017771 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X display handler */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_X11_DISPLAY_PRIVATE_H #define META_X11_DISPLAY_PRIVATE_H #include #include #include "backends/meta-monitor-manager-private.h" #include "core/display-private.h" #include "meta/common.h" #include "meta/meta-selection-source.h" #include "meta/types.h" #include "meta/meta-x11-display.h" #include "meta-startup-notification-x11.h" #include "meta-x11-stack-private.h" #include "ui/ui.h" typedef struct _MetaGroupPropHooks MetaGroupPropHooks; typedef struct _MetaWindowPropHooks MetaWindowPropHooks; typedef gboolean (*MetaAlarmFilter) (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data); struct _MetaX11Display { GObject parent; MetaDisplay *display; GdkDisplay *gdk_display; char *name; char *screen_name; Display *xdisplay; Window xroot; int default_depth; Visual *default_xvisual; guint32 timestamp; /* Pull in all the names of atoms as fields; we will intern them when the * class is constructed. */ #define item(x) Atom atom_##x; #include "x11/atomnames.h" #undef item Window leader_window; Window timestamp_pinging_window; /* The window and serial of the most recent FocusIn event. */ Window server_focus_window; gulong server_focus_serial; /* For windows we've focused that don't necessarily have an X window, * like the no_focus_window or the stage X window. */ Window focus_xwindow; gulong focus_serial; /* This window holds the focus when we don't want to focus * any actual clients */ Window no_focus_window; Atom atom_x_root_pixmap; /* Instead of unmapping withdrawn windows we can leave them mapped * and restack them below a guard window. When using a compositor * this allows us to provide live previews of unmapped windows */ Window guard_window; Window wm_sn_selection_window; Atom wm_sn_atom; guint32 wm_sn_timestamp; guint display_close_idle; guint32 xselectionclear_timestamp; Window wm_cm_selection_window; Window composite_overlay_window; GHashTable *xids; gboolean has_xinerama_indices; /* Managed by group.c */ GHashTable *groups_by_leader; /* Managed by window-props.c */ MetaWindowPropHooks *prop_hooks_table; GHashTable *prop_hooks; int n_prop_hooks; /* Managed by group-props.c */ MetaGroupPropHooks *group_prop_hooks; int xkb_base_event_type; guint32 last_bell_time; MetaAlarmFilter alarm_filter; gpointer alarm_filter_data; MetaUI *ui; struct { Window xwindow; guint timeout_id; MetaSelectionSource *owners[META_N_SELECTION_TYPES]; GCancellable *cancellables[META_N_SELECTION_TYPES]; GList *input_streams; GList *output_streams; } selection; /* If true, server->focus_serial refers to us changing the focus; in * this case, we can ignore focus events that have exactly focus_serial, * since we take care to make another request immediately afterwards. * But if focus is being changed by another client, we have to accept * multiple events with the same serial. */ guint focused_by_us : 1; guint keys_grabbed : 1; guint closing : 1; /* we use property updates as sentinels for certain window focus events * to avoid some race conditions on EnterNotify events */ int sentinel_counter; int composite_event_base; int composite_error_base; int composite_major_version; int composite_minor_version; int damage_event_base; int damage_error_base; int xfixes_event_base; int xfixes_error_base; int xinput_error_base; int xinput_event_base; int xinput_opcode; int xsync_event_base; int xsync_error_base; int shape_event_base; int shape_error_base; unsigned int have_xsync : 1; #define META_X11_DISPLAY_HAS_XSYNC(x11_display) ((x11_display)->have_xsync) unsigned int have_shape : 1; #define META_X11_DISPLAY_HAS_SHAPE(x11_display) ((x11_display)->have_shape) unsigned int have_composite : 1; unsigned int have_damage : 1; #define META_X11_DISPLAY_HAS_COMPOSITE(x11_display) ((x11_display)->have_composite) #define META_X11_DISPLAY_HAS_DAMAGE(x11_display) ((x11_display)->have_damage) gboolean have_xinput_23 : 1; #define META_X11_DISPLAY_HAS_XINPUT_23(x11_display) ((x11_display)->have_xinput_23) MetaX11StartupNotification *startup_notification; MetaX11Stack *x11_stack; XserverRegion empty_region; }; MetaX11Display *meta_x11_display_new (MetaDisplay *display, GError **error); void meta_x11_display_restore_active_workspace (MetaX11Display *x11_display); Window meta_x11_display_create_offscreen_window (MetaX11Display *x11_display, Window parent, long valuemask); Cursor meta_x11_display_create_x_cursor (MetaX11Display *x11_display, MetaCursor cursor); void meta_x11_display_reload_cursor (MetaX11Display *x11_display); MetaWindow *meta_x11_display_lookup_x_window (MetaX11Display *x11_display, Window xwindow); void meta_x11_display_register_x_window (MetaX11Display *x11_display, Window *xwindowp, MetaWindow *window); void meta_x11_display_unregister_x_window (MetaX11Display *x11_display, Window xwindow); MetaWindow *meta_x11_display_lookup_sync_alarm (MetaX11Display *x11_display, XSyncAlarm alarm); void meta_x11_display_register_sync_alarm (MetaX11Display *x11_display, XSyncAlarm *alarmp, MetaWindow *window); void meta_x11_display_unregister_sync_alarm (MetaX11Display *x11_display, XSyncAlarm alarm); gboolean meta_x11_display_process_barrier_xevent (MetaX11Display *x11_display, XIEvent *event); META_EXPORT void meta_x11_display_set_alarm_filter (MetaX11Display *x11_display, MetaAlarmFilter filter, gpointer data); void meta_x11_display_create_guard_window (MetaX11Display *x11_display); /* make a request to ensure the event serial has changed */ void meta_x11_display_increment_event_serial (MetaX11Display *x11_display); guint32 meta_x11_display_get_current_time_roundtrip (MetaX11Display *x11_display); void meta_x11_display_set_input_focus_xwindow (MetaX11Display *x11_display, Window window, guint32 timestamp); int meta_x11_display_logical_monitor_to_xinerama_index (MetaX11Display *x11_display, MetaLogicalMonitor *logical_monitor); MetaLogicalMonitor *meta_x11_display_xinerama_index_to_logical_monitor (MetaX11Display *x11_display, int xinerama_index); void meta_x11_display_update_workspace_layout (MetaX11Display *x11_display); void meta_x11_display_update_workspace_names (MetaX11Display *x11_display); void meta_x11_display_increment_focus_sentinel (MetaX11Display *x11_display); void meta_x11_display_decrement_focus_sentinel (MetaX11Display *x11_display); gboolean meta_x11_display_focus_sentinel_clear (MetaX11Display *x11_display); void meta_x11_display_update_focus_window (MetaX11Display *x11_display, Window xwindow, gulong serial, gboolean focused_by_us); void meta_x11_display_set_input_focus (MetaX11Display *x11_display, MetaWindow *window, gboolean focus_frame, uint32_t timestamp); MetaDisplay * meta_x11_display_get_display (MetaX11Display *x11_display); const gchar * meta_x11_get_display_name (void); #endif /* META_X11_DISPLAY_PRIVATE_H */ muffin-6.4.1/src/x11/meta-x11-stack-private.h0000664000175000017500000000216014723361714017422 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2019 Red Hat, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_X11_STACK_H #define META_X11_STACK_H #include #include "meta/types.h" #define META_TYPE_X11_STACK (meta_x11_stack_get_type ()) G_DECLARE_FINAL_TYPE (MetaX11Stack, meta_x11_stack, META, X11_STACK, GObject) typedef struct _MetaX11Stack MetaX11Stack; MetaX11Stack * meta_x11_stack_new (MetaX11Display *x11_display); #endif /* META_X11_STACK_H */ muffin-6.4.1/src/x11/meta-x11-selection-input-stream.c0000664000175000017500000004350014723361714021256 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: Benjamin Otte * Christian Kellner */ #include "config.h" #include "meta-x11-selection-input-stream-private.h" #include #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" typedef struct MetaX11SelectionInputStreamPrivate MetaX11SelectionInputStreamPrivate; struct _MetaX11SelectionInputStream { GInputStream parent_instance; }; struct MetaX11SelectionInputStreamPrivate { MetaX11Display *x11_display; Window window; GAsyncQueue *chunks; char *selection; Atom xselection; char *target; Atom xtarget; char *property; Atom xproperty; const char *type; Atom xtype; int format; GTask *pending_task; uint8_t *pending_data; size_t pending_size; guint complete : 1; guint incr : 1; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionInputStream, meta_x11_selection_input_stream, G_TYPE_INPUT_STREAM) static gboolean meta_x11_selection_input_stream_has_data (MetaX11SelectionInputStream *stream) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); return g_async_queue_length (priv->chunks) > 0 || priv->complete; } /* NB: blocks when no data is in buffer */ static size_t meta_x11_selection_input_stream_fill_buffer (MetaX11SelectionInputStream *stream, uint8_t *buffer, size_t count) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); GBytes *bytes; size_t result = 0; g_async_queue_lock (priv->chunks); for (bytes = g_async_queue_pop_unlocked (priv->chunks); bytes != NULL && count > 0; bytes = g_async_queue_try_pop_unlocked (priv->chunks)) { size_t size = g_bytes_get_size (bytes); if (size == 0) { /* EOF marker, put it back */ g_async_queue_push_front_unlocked (priv->chunks, bytes); bytes = NULL; break; } else if (size > count) { GBytes *subbytes; if (buffer) memcpy (buffer, g_bytes_get_data (bytes, NULL), count); subbytes = g_bytes_new_from_bytes (bytes, count, size - count); g_async_queue_push_front_unlocked (priv->chunks, subbytes); size = count; } else { if (buffer) memcpy (buffer, g_bytes_get_data (bytes, NULL), size); } g_bytes_unref (bytes); result += size; if (buffer) buffer += size; count -= size; } if (bytes) g_async_queue_push_front_unlocked (priv->chunks, bytes); g_async_queue_unlock (priv->chunks); return result; } static void meta_x11_selection_input_stream_flush (MetaX11SelectionInputStream *stream) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); gssize written; if (!meta_x11_selection_input_stream_has_data (stream)) return; if (priv->pending_task == NULL) return; written = meta_x11_selection_input_stream_fill_buffer (stream, priv->pending_data, priv->pending_size); g_task_return_int (priv->pending_task, written); g_clear_object (&priv->pending_task); priv->pending_data = NULL; priv->pending_size = 0; } static void meta_x11_selection_input_stream_complete (MetaX11SelectionInputStream *stream) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); if (priv->complete) return; priv->complete = TRUE; g_async_queue_push (priv->chunks, g_bytes_new (NULL, 0)); meta_x11_selection_input_stream_flush (stream); priv->x11_display->selection.input_streams = g_list_remove (priv->x11_display->selection.input_streams, stream); g_object_unref (stream); } static gssize meta_x11_selection_input_stream_read (GInputStream *input_stream, void *buffer, size_t count, GCancellable *cancellable, GError **error) { MetaX11SelectionInputStream *stream = META_X11_SELECTION_INPUT_STREAM (input_stream); return meta_x11_selection_input_stream_fill_buffer (stream, buffer, count); } static gboolean meta_x11_selection_input_stream_close (GInputStream *stream, GCancellable *cancellable, GError **error) { return TRUE; } static void meta_x11_selection_input_stream_read_async (GInputStream *input_stream, void *buffer, size_t count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaX11SelectionInputStream *stream = META_X11_SELECTION_INPUT_STREAM (input_stream); MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); GTask *task; task = g_task_new (stream, cancellable, callback, user_data); g_task_set_source_tag (task, meta_x11_selection_input_stream_read_async); g_task_set_priority (task, io_priority); if (meta_x11_selection_input_stream_has_data (stream)) { gssize size; size = meta_x11_selection_input_stream_fill_buffer (stream, buffer, count); g_task_return_int (task, size); g_object_unref (task); } else { priv->pending_data = buffer; priv->pending_size = count; priv->pending_task = task; } } static gssize meta_x11_selection_input_stream_read_finish (GInputStream *stream, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, stream), -1); g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == meta_x11_selection_input_stream_read_async, -1); return g_task_propagate_int (G_TASK (result), error); } static void meta_x11_selection_input_stream_close_async (GInputStream *stream, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (stream, cancellable, callback, user_data); g_task_set_source_tag (task, meta_x11_selection_input_stream_close_async); g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean meta_x11_selection_input_stream_close_finish (GInputStream *stream, GAsyncResult *result, GError **error) { return TRUE; } static void meta_x11_selection_input_stream_dispose (GObject *object) { MetaX11SelectionInputStream *stream = META_X11_SELECTION_INPUT_STREAM (object); MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); priv->x11_display->selection.input_streams = g_list_remove (priv->x11_display->selection.input_streams, stream); G_OBJECT_CLASS (meta_x11_selection_input_stream_parent_class)->dispose (object); } static void meta_x11_selection_input_stream_finalize (GObject *object) { MetaX11SelectionInputStream *stream = META_X11_SELECTION_INPUT_STREAM (object); MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); g_async_queue_unref (priv->chunks); g_free (priv->selection); g_free (priv->target); g_free (priv->property); G_OBJECT_CLASS (meta_x11_selection_input_stream_parent_class)->finalize (object); } static void meta_x11_selection_input_stream_class_init (MetaX11SelectionInputStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GInputStreamClass *istream_class = G_INPUT_STREAM_CLASS (klass); object_class->dispose = meta_x11_selection_input_stream_dispose; object_class->finalize = meta_x11_selection_input_stream_finalize; istream_class->read_fn = meta_x11_selection_input_stream_read; istream_class->close_fn = meta_x11_selection_input_stream_close; istream_class->read_async = meta_x11_selection_input_stream_read_async; istream_class->read_finish = meta_x11_selection_input_stream_read_finish; istream_class->close_async = meta_x11_selection_input_stream_close_async; istream_class->close_finish = meta_x11_selection_input_stream_close_finish; } static void meta_x11_selection_input_stream_init (MetaX11SelectionInputStream *stream) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); priv->chunks = g_async_queue_new_full ((GDestroyNotify) g_bytes_unref); } static void XFree_without_return_value (gpointer data) { XFree (data); } static GBytes * get_selection_property (Display *xdisplay, Window owner, Atom property, Atom *ret_type, gint *ret_format) { gulong nitems; gulong nbytes; Atom prop_type; gint prop_format; uint8_t *data = NULL; if (XGetWindowProperty (xdisplay, owner, property, 0, 0x1FFFFFFF, False, AnyPropertyType, &prop_type, &prop_format, &nitems, &nbytes, &data) != Success) goto err; if (prop_type != None) { size_t length; switch (prop_format) { case 8: length = nitems; break; case 16: length = sizeof (short) * nitems; break; case 32: length = sizeof (long) * nitems; break; default: g_warning ("Unknown XGetWindowProperty() format %u", prop_format); goto err; } *ret_type = prop_type; *ret_format = prop_format; return g_bytes_new_with_free_func (data, length, XFree_without_return_value, data); } err: if (data) XFree (data); *ret_type = None; *ret_format = 0; return NULL; } gboolean meta_x11_selection_input_stream_xevent (MetaX11SelectionInputStream *stream, const XEvent *xevent) { MetaX11SelectionInputStreamPrivate *priv = meta_x11_selection_input_stream_get_instance_private (stream); Display *xdisplay; Window xwindow; GBytes *bytes; Atom type; gint format; xdisplay = priv->x11_display->xdisplay; xwindow = priv->window; if (xevent->xany.display != xdisplay || xevent->xany.window != xwindow) return FALSE; switch (xevent->type) { case PropertyNotify: if (!priv->incr || xevent->xproperty.atom != priv->xproperty || xevent->xproperty.state != PropertyNewValue) return FALSE; bytes = get_selection_property (xdisplay, xwindow, xevent->xproperty.atom, &type, &format); if (bytes == NULL) { g_debug ("INCR request came out empty"); meta_x11_selection_input_stream_complete (stream); } else if (g_bytes_get_size (bytes) == 0 || type == None) { g_bytes_unref (bytes); meta_x11_selection_input_stream_complete (stream); } else { g_async_queue_push (priv->chunks, bytes); meta_x11_selection_input_stream_flush (stream); } XDeleteProperty (xdisplay, xwindow, xevent->xproperty.atom); return FALSE; case SelectionNotify: { GTask *task; /* selection is not for us */ if (priv->xselection != xevent->xselection.selection || priv->xtarget != xevent->xselection.target) return FALSE; /* We already received a selectionNotify before */ if (priv->pending_task == NULL || g_task_get_source_tag (priv->pending_task) != meta_x11_selection_input_stream_new_async) { g_debug ("Misbehaving client sent a reentrant SelectionNotify"); return FALSE; } task = g_steal_pointer (&priv->pending_task); if (xevent->xselection.property == None) { g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, _("Format %s not supported"), priv->target); meta_x11_selection_input_stream_complete (stream); } else { bytes = get_selection_property (xdisplay, xwindow, xevent->xselection.property, &priv->xtype, &priv->format); priv->type = gdk_x11_get_xatom_name (priv->xtype); g_task_return_pointer (task, g_object_ref (stream), g_object_unref); if (bytes == NULL) { meta_x11_selection_input_stream_complete (stream); } else { if (priv->xtype == XInternAtom (priv->x11_display->xdisplay, "INCR", False)) { /* The remainder of the selection will come through PropertyNotify events on xwindow */ priv->incr = TRUE; meta_x11_selection_input_stream_flush (stream); } else { g_async_queue_push (priv->chunks, bytes); meta_x11_selection_input_stream_complete (stream); } } XDeleteProperty (xdisplay, xwindow, xevent->xselection.property); } g_object_unref (task); } return TRUE; default: return FALSE; } } void meta_x11_selection_input_stream_new_async (MetaX11Display *x11_display, Window window, const char *selection, const char *target, guint32 timestamp, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaX11SelectionInputStream *stream; MetaX11SelectionInputStreamPrivate *priv; stream = g_object_new (META_TYPE_X11_SELECTION_INPUT_STREAM, NULL); priv = meta_x11_selection_input_stream_get_instance_private (stream); priv->x11_display = x11_display; x11_display->selection.input_streams = g_list_prepend (x11_display->selection.input_streams, stream); priv->selection = g_strdup (selection); priv->xselection = XInternAtom (x11_display->xdisplay, priv->selection, False); priv->target = g_strdup (target); priv->xtarget = XInternAtom (x11_display->xdisplay, priv->target, False); priv->property = g_strdup_printf ("META_SELECTION_%p", stream); priv->xproperty = XInternAtom (x11_display->xdisplay, priv->property, False); priv->window = window; XConvertSelection (x11_display->xdisplay, priv->xselection, priv->xtarget, priv->xproperty, window, timestamp); priv->pending_task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (priv->pending_task, meta_x11_selection_input_stream_new_async); g_task_set_priority (priv->pending_task, io_priority); } GInputStream * meta_x11_selection_input_stream_new_finish (GAsyncResult *result, const char **type, int *format, GError **error) { MetaX11SelectionInputStream *stream; MetaX11SelectionInputStreamPrivate *priv; GTask *task; g_return_val_if_fail (g_task_is_valid (result, NULL), NULL); task = G_TASK (result); g_return_val_if_fail (g_task_get_source_tag (task) == meta_x11_selection_input_stream_new_async, NULL); stream = g_task_propagate_pointer (task, error); if (!stream) return NULL; priv = meta_x11_selection_input_stream_get_instance_private (stream); if (type) *type = priv->type; if (format) *format = priv->format; return G_INPUT_STREAM (stream); } muffin-6.4.1/src/x11/iconcache.h0000664000175000017500000000500314723361714015225 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window icons */ /* * Copyright (C) 2002 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_ICON_CACHE_H #define META_ICON_CACHE_H #include "x11/meta-x11-display-private.h" typedef struct _MetaIconCache MetaIconCache; typedef enum { /* These MUST be in ascending order of preference; * i.e. if we get _NET_WM_ICON and already have * WM_HINTS, we prefer _NET_WM_ICON */ USING_NO_ICON, USING_FALLBACK_ICON, USING_KWM_WIN_ICON, USING_WM_HINTS, USING_NET_WM_ICON } IconOrigin; struct _MetaIconCache { int origin; Pixmap prev_pixmap; Pixmap prev_mask; /* TRUE if these props have changed */ guint wm_hints_dirty : 1; guint kwm_win_icon_dirty : 1; guint net_wm_icon_dirty : 1; }; void meta_icon_cache_init (MetaIconCache *icon_cache); void meta_icon_cache_property_changed (MetaIconCache *icon_cache, MetaX11Display *x11_display, Atom atom); gboolean meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache); gboolean meta_read_icons (MetaX11Display *x11_display, Window xwindow, MetaIconCache *icon_cache, Pixmap wm_hints_pixmap, Pixmap wm_hints_mask, cairo_surface_t **iconp, int ideal_width, int ideal_height, cairo_surface_t **mini_iconp, int ideal_mini_width, int ideal_mini_height); #endif muffin-6.4.1/src/x11/window-props.h0000664000175000017500000000642414723361714015771 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:window-props * @short_description: MetaWindow property handling * * A system which can inspect sets of properties of given windows * and take appropriate action given their values. * * Note that all the meta_window_reload_propert* functions require a * round trip to the server. */ /* * Copyright (C) 2001, 2002 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_PROPS_H #define META_WINDOW_PROPS_H #include "core/window-private.h" /** * meta_window_reload_property_from_xwindow: * @window: A window on the same display as the one we're * investigating (only used to find the display) * @xwindow: The X handle for the window. * @property: A single X atom. * * Requests the current values of a single property for a given * window from the server, and deals with it appropriately. * Does not return it to the caller (it's been dealt with!) */ void meta_window_reload_property_from_xwindow (MetaWindow *window, Window xwindow, Atom property, gboolean initial); /** * meta_window_load_initial_properties: * @window: The window. * * Requests the current values for standard properties for a given * window from the server, and deals with them appropriately. * Does not return them to the caller (they've been dealt with!) */ void meta_window_load_initial_properties (MetaWindow *window); /** * meta_x11_display_init_window_prop_hooks: * @x11_display: The X11 display. * * Initialises the hooks used for the reload_propert* functions * on a particular display, and stores a pointer to them in the * x11_display. */ void meta_x11_display_init_window_prop_hooks (MetaX11Display *x11_display); /** * meta_x11_display_free_window_prop_hooks: * @x11_display: The X11 display. * Frees the hooks used for the reload_propert* functions * for a particular display. */ void meta_x11_display_free_window_prop_hooks (MetaX11Display *x11_display); /** * meta_set_normal_hints: * @window: The window to set the size hints on. * @hints: Either some X size hints, or NULL for default. * * Sets the size hints for a window. This happens when a * WM_NORMAL_HINTS property is set on a window, but it is public * because the size hints are set to defaults when a window is * created. See * http://tronche.com/gui/x/icccm/sec-4.html#WM_NORMAL_HINTS * for the X details. */ void meta_set_normal_hints (MetaWindow *window, XSizeHints *hints); #endif /* META_WINDOW_PROPS_H */ muffin-6.4.1/src/x11/group-props.h0000664000175000017500000000260614723361714015614 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* MetaGroup property handling */ /* * Copyright (C) 2002 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_GROUP_PROPS_H #define META_GROUP_PROPS_H #include "core/window-private.h" #include "meta/group.h" void meta_group_reload_property (MetaGroup *group, Atom property); void meta_group_reload_properties (MetaGroup *group, const Atom *properties, int n_properties); void meta_x11_display_init_group_prop_hooks (MetaX11Display *x11_display); void meta_x11_display_free_group_prop_hooks (MetaX11Display *x11_display); #endif /* META_GROUP_PROPS_H */ muffin-6.4.1/src/x11/meta-x11-selection-input-stream-private.h0000664000175000017500000000545114723361714022736 0ustar fabiofabio/* GIO - GLib Input, Output and Streaming Library * * Copyright (C) 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: Benjamin Otte * Christian Kellner */ #ifndef META_X11_SELECTION_INPUT_STREAM_H #define META_X11_SELECTION_INPUT_STREAM_H #include #include "x11/meta-x11-display-private.h" #define META_TYPE_X11_SELECTION_INPUT_STREAM (meta_x11_selection_input_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaX11SelectionInputStream, meta_x11_selection_input_stream, META, X11_SELECTION_INPUT_STREAM, GInputStream) void meta_x11_selection_input_stream_new_async (MetaX11Display *x11_display, Window window, const char *selection, const char *target, guint32 timestamp, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); GInputStream * meta_x11_selection_input_stream_new_finish (GAsyncResult *result, const char **type, int *format, GError **error); gboolean meta_x11_selection_input_stream_xevent (MetaX11SelectionInputStream *stream, const XEvent *xevent); G_END_DECLS #endif /* META_X11_SELECTION_INPUT_STREAM_H */ muffin-6.4.1/src/x11/session.c0000664000175000017500000014415514723361714015003 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter Session Management */ /* * Copyright (C) 2001 Havoc Pennington (some code in here from * libgnomeui, (C) Tom Tromey, Carsten Schaar) * Copyright (C) 2004, 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/session.h" #include #include #include #include "core/util-private.h" #include "meta/main.h" #include "x11/meta-x11-display-private.h" #ifndef HAVE_SM void meta_session_init (const char *client_id, const char *save_file) { meta_topic (META_DEBUG_SM, "Compiled without session management support\n"); } const MetaWindowSessionInfo* meta_window_lookup_saved_state (MetaWindow *window) { return NULL; } void meta_window_release_saved_state (const MetaWindowSessionInfo *info) { ; } #else /* HAVE_SM */ #include #include #include #include #include #include #include #include #include #include #include #include "core/display-private.h" #include "meta/main.h" #include "meta/util.h" #include "meta/workspace.h" static void ice_io_error_handler (IceConn connection); static void new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data); static void save_state (void); static char* load_state (const char *previous_save_file); static void regenerate_save_file (void); static const char* full_save_file (void); static void warn_about_lame_clients_and_finish_interact (gboolean shutdown); static void disconnect (void); /* This is called when data is available on an ICE connection. */ static gboolean process_ice_messages (GIOChannel *channel, GIOCondition condition, gpointer client_data) { IceConn connection = (IceConn) client_data; IceProcessMessagesStatus status; /* This blocks infinitely sometimes. I don't know what * to do about it. Checking "condition" just breaks * session management. */ status = IceProcessMessages (connection, NULL, NULL); if (status == IceProcessMessagesIOError) { #if 0 IcePointer context = IceGetConnectionContext (connection); #endif /* We were disconnected; close our connection to the * session manager, this will result in the ICE connection * being cleaned up, since it is owned by libSM. */ disconnect (); meta_quit (META_EXIT_SUCCESS); return FALSE; } return TRUE; } /* This is called when a new ICE connection is made. It arranges for the ICE connection to be handled via the event loop. */ static void new_ice_connection (IceConn connection, IcePointer client_data, Bool opening, IcePointer *watch_data) { guint input_id; if (opening) { /* Make sure we don't pass on these file descriptors to any * exec'ed children */ GIOChannel *channel; fcntl (IceConnectionNumber (connection), F_SETFD, fcntl (IceConnectionNumber (connection), F_GETFD, 0) | FD_CLOEXEC); channel = g_io_channel_unix_new (IceConnectionNumber (connection)); input_id = g_io_add_watch (channel, G_IO_IN | G_IO_ERR, process_ice_messages, connection); g_io_channel_unref (channel); *watch_data = (IcePointer) GUINT_TO_POINTER (input_id); } else { input_id = GPOINTER_TO_UINT ((gpointer) *watch_data); g_clear_handle_id (&input_id, g_source_remove); } } static IceIOErrorHandler ice_installed_handler; /* We call any handler installed before (or after) gnome_ice_init but avoid calling the default libICE handler which does an exit() */ static void ice_io_error_handler (IceConn connection) { if (ice_installed_handler) (*ice_installed_handler) (connection); } static void ice_init (void) { static gboolean ice_initted = FALSE; if (! ice_initted) { IceIOErrorHandler default_handler; ice_installed_handler = IceSetIOErrorHandler (NULL); default_handler = IceSetIOErrorHandler (ice_io_error_handler); if (ice_installed_handler == default_handler) ice_installed_handler = NULL; IceAddConnectionWatch (new_ice_connection, NULL); ice_initted = TRUE; } } typedef enum { STATE_DISCONNECTED, STATE_IDLE, STATE_SAVING_PHASE_1, STATE_WAITING_FOR_PHASE_2, STATE_SAVING_PHASE_2, STATE_WAITING_FOR_INTERACT, STATE_DONE_WITH_INTERACT, STATE_SKIPPING_GLOBAL_SAVE, STATE_FROZEN, STATE_REGISTERING } ClientState; static void save_phase_2_callback (SmcConn smc_conn, SmPointer client_data); static void interact_callback (SmcConn smc_conn, SmPointer client_data); static void shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data); static void save_complete_callback (SmcConn smc_conn, SmPointer client_data); static void die_callback (SmcConn smc_conn, SmPointer client_data); static void save_yourself_callback (SmcConn smc_conn, SmPointer client_data, int save_style, Bool shutdown, int interact_style, Bool fast); static void set_clone_restart_commands (void); static char *client_id = NULL; static gpointer session_connection = NULL; static ClientState current_state = STATE_DISCONNECTED; static gboolean interaction_allowed = FALSE; void meta_session_init (const char *previous_client_id, const char *previous_save_file) { /* Some code here from twm */ char buf[256]; unsigned long mask; SmcCallbacks callbacks; char *saved_client_id; meta_topic (META_DEBUG_SM, "Initializing session with save file '%s'\n", previous_save_file ? previous_save_file : "(none)"); if (previous_save_file) { saved_client_id = load_state (previous_save_file); previous_client_id = saved_client_id; } else if (previous_client_id) { char *save_file = g_strconcat (previous_client_id, ".ms", NULL); saved_client_id = load_state (save_file); g_free (save_file); } else { saved_client_id = NULL; } ice_init (); mask = SmcSaveYourselfProcMask | SmcDieProcMask | SmcSaveCompleteProcMask | SmcShutdownCancelledProcMask; callbacks.save_yourself.callback = save_yourself_callback; callbacks.save_yourself.client_data = NULL; callbacks.die.callback = die_callback; callbacks.die.client_data = NULL; callbacks.save_complete.callback = save_complete_callback; callbacks.save_complete.client_data = NULL; callbacks.shutdown_cancelled.callback = shutdown_cancelled_callback; callbacks.shutdown_cancelled.client_data = NULL; session_connection = SmcOpenConnection (NULL, /* use SESSION_MANAGER env */ NULL, /* means use existing ICE connection */ SmProtoMajor, SmProtoMinor, mask, &callbacks, (char*) previous_client_id, &client_id, 255, buf); if (session_connection == NULL) { meta_topic (META_DEBUG_SM, "Failed to a open connection to a session manager, so window positions will not be saved: %s\n", buf); goto out; } else { if (client_id == NULL) meta_bug ("Session manager gave us a NULL client ID?"); meta_topic (META_DEBUG_SM, "Obtained session ID '%s'\n", client_id); } if (previous_client_id && strcmp (previous_client_id, client_id) == 0) current_state = STATE_IDLE; else current_state = STATE_REGISTERING; { SmProp prop1, prop2, prop3, prop4, prop5, prop6, *props[6]; SmPropValue prop1val, prop2val, prop3val, prop4val, prop5val, prop6val; char pid[32]; /* Historically, this was SmRestartImmediately, which made sense * for a stateless window manager, but we don't really control * what embedders do, and it's all around better if gnome-session * handles this. */ char hint = SmRestartIfRunning; char priority = 20; /* low to run before other apps */ const char *prgname; prgname = g_get_prgname (); prop1.name = (char *)SmProgram; prop1.type = (char *)SmARRAY8; prop1.num_vals = 1; prop1.vals = &prop1val; prop1val.value = (char *)prgname; prop1val.length = strlen (prgname); /* twm sets getuid() for this, but the SM spec plainly * says pw_name, twm is on crack */ prop2.name = (char *)SmUserID; prop2.type = (char *)SmARRAY8; prop2.num_vals = 1; prop2.vals = &prop2val; prop2val.value = (char*) g_get_user_name (); prop2val.length = strlen (prop2val.value); prop3.name = (char *)SmRestartStyleHint; prop3.type = (char *)SmCARD8; prop3.num_vals = 1; prop3.vals = &prop3val; prop3val.value = &hint; prop3val.length = 1; sprintf (pid, "%d", getpid ()); prop4.name = (char *)SmProcessID; prop4.type = (char *)SmARRAY8; prop4.num_vals = 1; prop4.vals = &prop4val; prop4val.value = pid; prop4val.length = strlen (prop4val.value); /* Always start in home directory */ prop5.name = (char *)SmCurrentDirectory; prop5.type = (char *)SmARRAY8; prop5.num_vals = 1; prop5.vals = &prop5val; prop5val.value = (char*) g_get_home_dir (); prop5val.length = strlen (prop5val.value); prop6.name = (char *)"_GSM_Priority"; prop6.type = (char *)SmCARD8; prop6.num_vals = 1; prop6.vals = &prop6val; prop6val.value = &priority; prop6val.length = 1; props[0] = &prop1; props[1] = &prop2; props[2] = &prop3; props[3] = &prop4; props[4] = &prop5; props[5] = &prop6; SmcSetProperties (session_connection, 6, props); } out: g_free (saved_client_id); } static void disconnect (void) { SmcCloseConnection (session_connection, 0, NULL); session_connection = NULL; current_state = STATE_DISCONNECTED; } static void save_yourself_possibly_done (gboolean shutdown, gboolean successful) { meta_topic (META_DEBUG_SM, "save possibly done shutdown = %d success = %d\n", shutdown, successful); if (current_state == STATE_SAVING_PHASE_1) { Status status; status = SmcRequestSaveYourselfPhase2 (session_connection, save_phase_2_callback, GINT_TO_POINTER (shutdown)); if (status) current_state = STATE_WAITING_FOR_PHASE_2; meta_topic (META_DEBUG_SM, "Requested phase 2, status = %d\n", status); } if (current_state == STATE_SAVING_PHASE_2 && interaction_allowed) { Status status; status = SmcInteractRequest (session_connection, /* ignore this feature of the protocol by always * claiming normal */ SmDialogNormal, interact_callback, GINT_TO_POINTER (shutdown)); if (status) current_state = STATE_WAITING_FOR_INTERACT; meta_topic (META_DEBUG_SM, "Requested interact, status = %d\n", status); } if (current_state == STATE_SAVING_PHASE_1 || current_state == STATE_SAVING_PHASE_2 || current_state == STATE_DONE_WITH_INTERACT || current_state == STATE_SKIPPING_GLOBAL_SAVE) { meta_topic (META_DEBUG_SM, "Sending SaveYourselfDone\n"); SmcSaveYourselfDone (session_connection, successful); if (shutdown) current_state = STATE_FROZEN; else current_state = STATE_IDLE; } } static void save_phase_2_callback (SmcConn smc_conn, SmPointer client_data) { gboolean shutdown; meta_topic (META_DEBUG_SM, "Phase 2 save"); shutdown = GPOINTER_TO_INT (client_data); current_state = STATE_SAVING_PHASE_2; save_state (); save_yourself_possibly_done (shutdown, TRUE); } static void save_yourself_callback (SmcConn smc_conn, SmPointer client_data, int save_style, Bool shutdown, int interact_style, Bool fast) { gboolean successful; meta_topic (META_DEBUG_SM, "SaveYourself received"); successful = TRUE; /* The first SaveYourself after registering for the first time * is a special case (SM specs 7.2). */ #if 0 /* I think the GnomeClient rationale for this doesn't apply */ if (current_state == STATE_REGISTERING) { current_state = STATE_IDLE; /* Double check that this is a section 7.2 SaveYourself: */ if (save_style == SmSaveLocal && interact_style == SmInteractStyleNone && !shutdown && !fast) { /* The protocol requires this even if xsm ignores it. */ SmcSaveYourselfDone (session_connection, successful); return; } } #endif /* ignore Global style saves * * This interpretaion of the Local/Global/Both styles * was discussed extensively on the xdg-list. See: * * https://listman.redhat.com/pipermail/xdg-list/2002-July/000615.html */ if (save_style == SmSaveGlobal) { current_state = STATE_SKIPPING_GLOBAL_SAVE; save_yourself_possibly_done (shutdown, successful); return; } interaction_allowed = interact_style != SmInteractStyleNone; current_state = STATE_SAVING_PHASE_1; regenerate_save_file (); set_clone_restart_commands (); save_yourself_possibly_done (shutdown, successful); } static void die_callback (SmcConn smc_conn, SmPointer client_data) { meta_topic (META_DEBUG_SM, "Disconnecting from session manager"); disconnect (); /* We don't actually exit here - we will simply go away with the X * server on logout, when we lose the X connection and libx11 kills * us. It looks like *crap* on logout if the user sees their * windows lose the decorations, etc. * * Anything that wants us to go away outside of session management * can use kill(). */ /* All of that is true - unless we're a wayland compositor. In which * case the X server won't go down until we do, so we must die first. */ if (meta_is_wayland_compositor ()) meta_quit (0); } static void save_complete_callback (SmcConn smc_conn, SmPointer client_data) { /* nothing */ meta_topic (META_DEBUG_SM, "SaveComplete received\n"); } static void shutdown_cancelled_callback (SmcConn smc_conn, SmPointer client_data) { meta_topic (META_DEBUG_SM, "Shutdown cancelled received\n"); if (session_connection != NULL && (current_state != STATE_IDLE && current_state != STATE_FROZEN)) { SmcSaveYourselfDone (session_connection, True); current_state = STATE_IDLE; } } static void interact_callback (SmcConn smc_conn, SmPointer client_data) { /* nothing */ gboolean shutdown; meta_topic (META_DEBUG_SM, "Interaction permission received\n"); shutdown = GPOINTER_TO_INT (client_data); current_state = STATE_DONE_WITH_INTERACT; warn_about_lame_clients_and_finish_interact (shutdown); } static void set_clone_restart_commands (void) { char *restartv[10]; char *clonev[10]; char *discardv[10]; int i; SmProp prop1, prop2, prop3, *props[3]; const char *prgname; prgname = g_get_prgname (); /* Restart (use same client ID) */ prop1.name = (char *)SmRestartCommand; prop1.type = (char *)SmLISTofARRAY8; g_return_if_fail (client_id); i = 0; restartv[i] = (char *)prgname; ++i; restartv[i] = (char *)"--sm-client-id"; ++i; restartv[i] = client_id; ++i; restartv[i] = NULL; prop1.vals = g_new (SmPropValue, i); i = 0; while (restartv[i]) { prop1.vals[i].value = restartv[i]; prop1.vals[i].length = strlen (restartv[i]); ++i; } prop1.num_vals = i; /* Clone (no client ID) */ i = 0; clonev[i] = (char *)prgname; ++i; clonev[i] = NULL; prop2.name = (char *)SmCloneCommand; prop2.type = (char *)SmLISTofARRAY8; prop2.vals = g_new (SmPropValue, i); i = 0; while (clonev[i]) { prop2.vals[i].value = clonev[i]; prop2.vals[i].length = strlen (clonev[i]); ++i; } prop2.num_vals = i; /* Discard */ i = 0; discardv[i] = (char *)"rm"; ++i; discardv[i] = (char *)"-f"; ++i; discardv[i] = (char*) full_save_file (); ++i; discardv[i] = NULL; prop3.name = (char *)SmDiscardCommand; prop3.type = (char *)SmLISTofARRAY8; prop3.vals = g_new (SmPropValue, i); i = 0; while (discardv[i]) { prop3.vals[i].value = discardv[i]; prop3.vals[i].length = strlen (discardv[i]); ++i; } prop3.num_vals = i; props[0] = &prop1; props[1] = &prop2; props[2] = &prop3; SmcSetProperties (session_connection, 3, props); g_free (prop1.vals); g_free (prop2.vals); g_free (prop3.vals); } /* The remaining code in this file actually loads/saves the session, * while the code above this comment handles chatting with the * session manager. */ static const char* window_type_to_string (MetaWindowType type) { switch (type) { case META_WINDOW_NORMAL: return "normal"; case META_WINDOW_DESKTOP: return "desktop"; case META_WINDOW_DOCK: return "dock"; case META_WINDOW_DIALOG: return "dialog"; case META_WINDOW_MODAL_DIALOG: return "modal_dialog"; case META_WINDOW_TOOLBAR: return "toolbar"; case META_WINDOW_MENU: return "menu"; case META_WINDOW_SPLASHSCREEN: return "splashscreen"; case META_WINDOW_UTILITY: return "utility"; case META_WINDOW_DROPDOWN_MENU: return "dropdown_menu"; case META_WINDOW_POPUP_MENU: return "popup_menu"; case META_WINDOW_TOOLTIP: return "tooltip"; case META_WINDOW_NOTIFICATION: return "notification"; case META_WINDOW_COMBO: return "combo"; case META_WINDOW_DND: return "dnd"; case META_WINDOW_OVERRIDE_OTHER: return "override_redirect"; } return ""; } static MetaWindowType window_type_from_string (const char *str) { if (strcmp (str, "normal") == 0) return META_WINDOW_NORMAL; else if (strcmp (str, "desktop") == 0) return META_WINDOW_DESKTOP; else if (strcmp (str, "dock") == 0) return META_WINDOW_DOCK; else if (strcmp (str, "dialog") == 0) return META_WINDOW_DIALOG; else if (strcmp (str, "modal_dialog") == 0) return META_WINDOW_MODAL_DIALOG; else if (strcmp (str, "toolbar") == 0) return META_WINDOW_TOOLBAR; else if (strcmp (str, "menu") == 0) return META_WINDOW_MENU; else if (strcmp (str, "utility") == 0) return META_WINDOW_UTILITY; else if (strcmp (str, "splashscreen") == 0) return META_WINDOW_SPLASHSCREEN; else return META_WINDOW_NORMAL; } static int window_gravity_from_string (const char *str) { if (strcmp (str, "META_GRAVITY_NORTH_WEST") == 0) return META_GRAVITY_NORTH_WEST; else if (strcmp (str, "META_GRAVITY_NORTH") == 0) return META_GRAVITY_NORTH; else if (strcmp (str, "META_GRAVITY_NORTH_EAST") == 0) return META_GRAVITY_NORTH_EAST; else if (strcmp (str, "META_GRAVITY_WEST") == 0) return META_GRAVITY_WEST; else if (strcmp (str, "META_GRAVITY_CENTER") == 0) return META_GRAVITY_CENTER; else if (strcmp (str, "META_GRAVITY_EAST") == 0) return META_GRAVITY_EAST; else if (strcmp (str, "META_GRAVITY_SOUTH_WEST") == 0) return META_GRAVITY_SOUTH_WEST; else if (strcmp (str, "META_GRAVITY_SOUTH") == 0) return META_GRAVITY_SOUTH; else if (strcmp (str, "META_GRAVITY_SOUTH_EAST") == 0) return META_GRAVITY_SOUTH_EAST; else if (strcmp (str, "META_GRAVITY_STATIC") == 0) return META_GRAVITY_STATIC; else return META_GRAVITY_NORTH_WEST; } static char* encode_text_as_utf8_markup (const char *text) { /* text can be any encoding, and is nul-terminated. * we pretend it's Latin-1 and encode as UTF-8 */ GString *str; const char *p; char *escaped; str = g_string_new (""); p = text; while (*p) { g_string_append_unichar (str, *p); ++p; } escaped = g_markup_escape_text (str->str, str->len); g_string_free (str, TRUE); return escaped; } static char* decode_text_from_utf8 (const char *text) { /* Convert back from the encoded (but not escaped) UTF-8 */ GString *str; const char *p; str = g_string_new (""); p = text; while (*p) { /* obviously this barfs if the UTF-8 contains chars > 255 */ g_string_append_c (str, g_utf8_get_char (p)); p = g_utf8_next_char (p); } return g_string_free (str, FALSE); } static void save_state (void) { char *mutter_dir; char *session_dir; FILE *outfile; GSList *windows; GSList *tmp; int stack_position; g_assert (client_id); outfile = NULL; /* * g_get_user_config_dir() is guaranteed to return an existing directory. * Eventually, if SM stays with the WM, I'd like to make this * something like /window_placement in a standard format. * Future optimisers should note also that by the time we get here * we probably already have full_save_path figured out and therefore * can just use the directory name from that. */ mutter_dir = g_strconcat (g_get_user_config_dir (), G_DIR_SEPARATOR_S "mutter", NULL); session_dir = g_strconcat (mutter_dir, G_DIR_SEPARATOR_S "sessions", NULL); if (mkdir (mutter_dir, 0700) < 0 && errno != EEXIST) { meta_warning ("Could not create directory '%s': %s\n", mutter_dir, g_strerror (errno)); } if (mkdir (session_dir, 0700) < 0 && errno != EEXIST) { meta_warning ("Could not create directory '%s': %s\n", session_dir, g_strerror (errno)); } meta_topic (META_DEBUG_SM, "Saving session to '%s'\n", full_save_file ()); outfile = fopen (full_save_file (), "w"); if (outfile == NULL) { meta_warning ("Could not open session file '%s' for writing: %s\n", full_save_file (), g_strerror (errno)); goto out; } /* The file format is: * * * * * * * * * * Note that attributes on are the match info we use to * see if the saved state applies to a restored window, and * child elements are the saved state to be applied. * */ fprintf (outfile, "\n", client_id); windows = meta_display_list_windows (meta_get_display (), META_LIST_DEFAULT); stack_position = 0; windows = g_slist_sort (windows, meta_display_stack_cmp); tmp = windows; stack_position = 0; while (tmp != NULL) { MetaWindow *window; window = tmp->data; if (window->sm_client_id) { char *sm_client_id; char *res_class; char *res_name; char *role; char *title; /* client id, class, name, role are not expected to be * in UTF-8 (I think they are in XPCS which is Latin-1? * in practice they are always ascii though.) */ sm_client_id = encode_text_as_utf8_markup (window->sm_client_id); res_class = window->res_class ? encode_text_as_utf8_markup (window->res_class) : NULL; res_name = window->res_name ? encode_text_as_utf8_markup (window->res_name) : NULL; role = window->role ? encode_text_as_utf8_markup (window->role) : NULL; if (window->title) title = g_markup_escape_text (window->title, -1); else title = NULL; meta_topic (META_DEBUG_SM, "Saving session managed window %s, client ID '%s'\n", window->desc, window->sm_client_id); fprintf (outfile, " \n", sm_client_id, res_class ? res_class : "", res_name ? res_name : "", title ? title : "", role ? role : "", window_type_to_string (window->type), stack_position); g_free (sm_client_id); g_free (res_class); g_free (res_name); g_free (role); g_free (title); /* Sticky */ if (window->on_all_workspaces_requested) { fputs (" \n", outfile); } else { int n; if (window->workspace) n = meta_workspace_index (window->workspace); else n = window->initial_workspace; fprintf (outfile, " \n", n); } /* Minimized */ if (window->minimized) fputs (" \n", outfile); /* Maximized */ if (META_WINDOW_MAXIMIZED (window)) { fprintf (outfile, " \n", window->saved_rect.x, window->saved_rect.y, window->saved_rect.width, window->saved_rect.height); } /* Gravity */ { int x, y, w, h; meta_window_get_session_geometry (window, &x, &y, &w, &h); fprintf (outfile, " \n", x, y, w, h, meta_gravity_to_string (window->size_hints.win_gravity)); } fputs (" \n", outfile); } else { meta_topic (META_DEBUG_SM, "Not saving window '%s', not session managed\n", window->desc); } tmp = tmp->next; ++stack_position; } g_slist_free (windows); fputs ("\n", outfile); out: if (outfile) { /* FIXME need a dialog for this */ if (ferror (outfile)) { meta_warning ("Error writing session file '%s': %s\n", full_save_file (), g_strerror (errno)); } if (fclose (outfile)) { meta_warning ("Error closing session file '%s': %s\n", full_save_file (), g_strerror (errno)); } } g_free (mutter_dir); g_free (session_dir); } typedef enum { WINDOW_TAG_NONE, WINDOW_TAC_DESKTOP, WINDOW_TAG_STICKY, WINDOW_TAG_MINIMIZED, WINDOW_TAG_MAXIMIZED, WINDOW_TAG_GEOMETRY } WindowTag; typedef struct { MetaWindowSessionInfo *info; char *previous_id; } ParseData; static void session_info_free (MetaWindowSessionInfo *info); static MetaWindowSessionInfo* session_info_new (void); static void start_element_handler (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error); static void end_element_handler (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error); static void text_handler (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error); static GMarkupParser mutter_session_parser = { start_element_handler, end_element_handler, text_handler, NULL, NULL }; static GSList *window_info_list = NULL; static char* load_state (const char *previous_save_file) { GMarkupParseContext *context; GError *error; ParseData parse_data; char *text; gsize length; char *session_file; session_file = g_strconcat (g_get_user_config_dir (), G_DIR_SEPARATOR_S "mutter" G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S, previous_save_file, NULL); error = NULL; if (!g_file_get_contents (session_file, &text, &length, &error)) { char *canonical_session_file = session_file; /* Maybe they were doing it the old way, with ~/.mutter */ session_file = g_strconcat (g_get_home_dir (), G_DIR_SEPARATOR_S ".mutter" G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S, previous_save_file, NULL); if (!g_file_get_contents (session_file, &text, &length, NULL)) { /* oh, just give up */ g_error_free (error); g_free (session_file); g_free (canonical_session_file); return NULL; } g_free (canonical_session_file); } meta_topic (META_DEBUG_SM, "Parsing saved session file %s\n", session_file); g_free (session_file); session_file = NULL; parse_data.info = NULL; parse_data.previous_id = NULL; context = g_markup_parse_context_new (&mutter_session_parser, 0, &parse_data, NULL); error = NULL; if (!g_markup_parse_context_parse (context, text, length, &error)) goto error; error = NULL; if (!g_markup_parse_context_end_parse (context, &error)) goto error; g_markup_parse_context_free (context); goto out; error: meta_warning ("Failed to parse saved session file: %s\n", error->message); g_error_free (error); if (parse_data.info) session_info_free (parse_data.info); g_free (parse_data.previous_id); parse_data.previous_id = NULL; out: g_free (text); return parse_data.previous_id; } /* FIXME this isn't very robust against bogus session files */ static void start_element_handler (GMarkupParseContext *context, const gchar *element_name, const gchar **attribute_names, const gchar **attribute_values, gpointer user_data, GError **error) { ParseData *pd; pd = user_data; if (strcmp (element_name, "mutter_session") == 0) { /* Get previous ID */ int i; i = 0; while (attribute_names[i]) { const char *name; const char *val; name = attribute_names[i]; val = attribute_values[i]; if (pd->previous_id) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, " attribute seen but we already have the session ID"); return; } if (strcmp (name, "id") == 0) { pd->previous_id = decode_text_from_utf8 (val); } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on <%s> element", name, "mutter_session"); return; } ++i; } } else if (strcmp (element_name, "window") == 0) { int i; if (pd->info) { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_PARSE, "nested tag"); return; } pd->info = session_info_new (); i = 0; while (attribute_names[i]) { const char *name; const char *val; name = attribute_names[i]; val = attribute_values[i]; if (strcmp (name, "id") == 0) { if (*val) pd->info->id = decode_text_from_utf8 (val); } else if (strcmp (name, "class") == 0) { if (*val) pd->info->res_class = decode_text_from_utf8 (val); } else if (strcmp (name, "name") == 0) { if (*val) pd->info->res_name = decode_text_from_utf8 (val); } else if (strcmp (name, "title") == 0) { if (*val) pd->info->title = g_strdup (val); } else if (strcmp (name, "role") == 0) { if (*val) pd->info->role = decode_text_from_utf8 (val); } else if (strcmp (name, "type") == 0) { if (*val) pd->info->type = window_type_from_string (val); } else if (strcmp (name, "stacking") == 0) { if (*val) { pd->info->stack_position = atoi (val); pd->info->stack_position_set = TRUE; } } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on <%s> element", name, "window"); session_info_free (pd->info); pd->info = NULL; return; } ++i; } } else if (strcmp (element_name, "workspace") == 0) { int i; i = 0; while (attribute_names[i]) { const char *name; name = attribute_names[i]; if (strcmp (name, "index") == 0) { pd->info->workspace_indices = g_slist_prepend (pd->info->workspace_indices, GINT_TO_POINTER (atoi (attribute_values[i]))); } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on <%s> element", name, "window"); session_info_free (pd->info); pd->info = NULL; return; } ++i; } } else if (strcmp (element_name, "sticky") == 0) { pd->info->on_all_workspaces = TRUE; pd->info->on_all_workspaces_set = TRUE; } else if (strcmp (element_name, "minimized") == 0) { pd->info->minimized = TRUE; pd->info->minimized_set = TRUE; } else if (strcmp (element_name, "maximized") == 0) { int i; i = 0; pd->info->maximized = TRUE; pd->info->maximized_set = TRUE; while (attribute_names[i]) { const char *name; const char *val; name = attribute_names[i]; val = attribute_values[i]; if (strcmp (name, "saved_x") == 0) { if (*val) { pd->info->saved_rect.x = atoi (val); pd->info->saved_rect_set = TRUE; } } else if (strcmp (name, "saved_y") == 0) { if (*val) { pd->info->saved_rect.y = atoi (val); pd->info->saved_rect_set = TRUE; } } else if (strcmp (name, "saved_width") == 0) { if (*val) { pd->info->saved_rect.width = atoi (val); pd->info->saved_rect_set = TRUE; } } else if (strcmp (name, "saved_height") == 0) { if (*val) { pd->info->saved_rect.height = atoi (val); pd->info->saved_rect_set = TRUE; } } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on <%s> element", name, "maximized"); return; } ++i; } if (pd->info->saved_rect_set) meta_topic (META_DEBUG_SM, "Saved unmaximized size %d,%d %dx%d \n", pd->info->saved_rect.x, pd->info->saved_rect.y, pd->info->saved_rect.width, pd->info->saved_rect.height); } else if (strcmp (element_name, "geometry") == 0) { int i; pd->info->geometry_set = TRUE; i = 0; while (attribute_names[i]) { const char *name; const char *val; name = attribute_names[i]; val = attribute_values[i]; if (strcmp (name, "x") == 0) { if (*val) pd->info->rect.x = atoi (val); } else if (strcmp (name, "y") == 0) { if (*val) pd->info->rect.y = atoi (val); } else if (strcmp (name, "width") == 0) { if (*val) pd->info->rect.width = atoi (val); } else if (strcmp (name, "height") == 0) { if (*val) pd->info->rect.height = atoi (val); } else if (strcmp (name, "gravity") == 0) { if (*val) pd->info->gravity = window_gravity_from_string (val); } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ATTRIBUTE, "Unknown attribute %s on <%s> element", name, "geometry"); return; } ++i; } meta_topic (META_DEBUG_SM, "Loaded geometry %d,%d %dx%d gravity %s\n", pd->info->rect.x, pd->info->rect.y, pd->info->rect.width, pd->info->rect.height, meta_gravity_to_string (pd->info->gravity)); } else { g_set_error (error, G_MARKUP_ERROR, G_MARKUP_ERROR_UNKNOWN_ELEMENT, "Unknown element %s", element_name); return; } } static void end_element_handler (GMarkupParseContext *context, const gchar *element_name, gpointer user_data, GError **error) { ParseData *pd; pd = user_data; if (strcmp (element_name, "window") == 0) { g_assert (pd->info); window_info_list = g_slist_prepend (window_info_list, pd->info); meta_topic (META_DEBUG_SM, "Loaded window info from session with class: %s name: %s role: %s\n", pd->info->res_class ? pd->info->res_class : "(none)", pd->info->res_name ? pd->info->res_name : "(none)", pd->info->role ? pd->info->role : "(none)"); pd->info = NULL; } } static void text_handler (GMarkupParseContext *context, const gchar *text, gsize text_len, gpointer user_data, GError **error) { /* Right now we don't have any elements where we care about their * content */ } static gboolean both_null_or_matching (const char *a, const char *b) { if (a == NULL && b == NULL) return TRUE; else if (a && b && strcmp (a, b) == 0) return TRUE; else return FALSE; } static GSList* get_possible_matches (MetaWindow *window) { /* Get all windows with this client ID */ GSList *retval; GSList *tmp; gboolean ignore_client_id; retval = NULL; ignore_client_id = g_getenv ("MUTTER_DEBUG_SM") != NULL; tmp = window_info_list; while (tmp != NULL) { MetaWindowSessionInfo *info; info = tmp->data; if ((ignore_client_id || both_null_or_matching (info->id, window->sm_client_id)) && both_null_or_matching (info->res_class, window->res_class) && both_null_or_matching (info->res_name, window->res_name) && both_null_or_matching (info->role, window->role)) { meta_topic (META_DEBUG_SM, "Window %s may match saved window with class: %s name: %s role: %s\n", window->desc, info->res_class ? info->res_class : "(none)", info->res_name ? info->res_name : "(none)", info->role ? info->role : "(none)"); retval = g_slist_prepend (retval, info); } else { if (meta_is_verbose ()) { if (!both_null_or_matching (info->id, window->sm_client_id)) meta_topic (META_DEBUG_SM, "Window %s has SM client ID %s, saved state has %s, no match\n", window->desc, window->sm_client_id ? window->sm_client_id : "(none)", info->id ? info->id : "(none)"); else if (!both_null_or_matching (info->res_class, window->res_class)) meta_topic (META_DEBUG_SM, "Window %s has class %s doesn't match saved class %s, no match\n", window->desc, window->res_class ? window->res_class : "(none)", info->res_class ? info->res_class : "(none)"); else if (!both_null_or_matching (info->res_name, window->res_name)) meta_topic (META_DEBUG_SM, "Window %s has name %s doesn't match saved name %s, no match\n", window->desc, window->res_name ? window->res_name : "(none)", info->res_name ? info->res_name : "(none)"); else if (!both_null_or_matching (info->role, window->role)) meta_topic (META_DEBUG_SM, "Window %s has role %s doesn't match saved role %s, no match\n", window->desc, window->role ? window->role : "(none)", info->role ? info->role : "(none)"); else meta_topic (META_DEBUG_SM, "???? should not happen - window %s doesn't match saved state %s for no good reason\n", window->desc, info->id); } } tmp = tmp->next; } return retval; } static const MetaWindowSessionInfo* find_best_match (GSList *infos, MetaWindow *window) { GSList *tmp; const MetaWindowSessionInfo *matching_title; const MetaWindowSessionInfo *matching_type; matching_title = NULL; matching_type = NULL; tmp = infos; while (tmp != NULL) { MetaWindowSessionInfo *info; info = tmp->data; if (matching_title == NULL && both_null_or_matching (info->title, window->title)) matching_title = info; if (matching_type == NULL && info->type == window->type) matching_type = info; tmp = tmp->next; } /* Prefer same title, then same type of window, then * just pick something. Eventually we could enhance this * to e.g. break ties by geometry hint similarity, * or other window features. */ if (matching_title) return matching_title; else if (matching_type) return matching_type; else return infos->data; } const MetaWindowSessionInfo* meta_window_lookup_saved_state (MetaWindow *window) { GSList *possibles; const MetaWindowSessionInfo *info; /* Window is not session managed. * I haven't yet figured out how to deal with these * in a way that doesn't cause broken side effects in * situations other than on session restore. */ if (window->sm_client_id == NULL) { meta_topic (META_DEBUG_SM, "Window %s is not session managed, not checking for saved state\n", window->desc); return NULL; } possibles = get_possible_matches (window); if (possibles == NULL) { meta_topic (META_DEBUG_SM, "Window %s has no possible matches in the list of saved window states\n", window->desc); return NULL; } info = find_best_match (possibles, window); g_slist_free (possibles); return info; } void meta_window_release_saved_state (const MetaWindowSessionInfo *info) { /* We don't want to use the same saved state again for another * window. */ window_info_list = g_slist_remove (window_info_list, info); session_info_free ((MetaWindowSessionInfo*) info); } static void session_info_free (MetaWindowSessionInfo *info) { g_free (info->id); g_free (info->res_class); g_free (info->res_name); g_free (info->title); g_free (info->role); g_slist_free (info->workspace_indices); g_free (info); } static MetaWindowSessionInfo* session_info_new (void) { MetaWindowSessionInfo *info; info = g_new0 (MetaWindowSessionInfo, 1); info->type = META_WINDOW_NORMAL; info->gravity = META_GRAVITY_NORTH_WEST; return info; } static char* full_save_path = NULL; static void regenerate_save_file (void) { g_free (full_save_path); if (client_id) full_save_path = g_strconcat (g_get_user_config_dir (), G_DIR_SEPARATOR_S "mutter" G_DIR_SEPARATOR_S "sessions" G_DIR_SEPARATOR_S, client_id, ".ms", NULL); else full_save_path = NULL; } static const char* full_save_file (void) { return full_save_path; } static int windows_cmp_by_title (MetaWindow *a, MetaWindow *b) { return g_utf8_collate (a->title, b->title); } static void finish_interact (gboolean shutdown) { if (current_state == STATE_DONE_WITH_INTERACT) /* paranoia */ { SmcInteractDone (session_connection, False /* don't cancel logout */); save_yourself_possibly_done (shutdown, TRUE); } } static void dialog_closed (GPid pid, int status, gpointer user_data) { gboolean shutdown = GPOINTER_TO_INT (user_data); if (WIFEXITED (status) && WEXITSTATUS (status) == 0) /* pressed "OK" */ { finish_interact (shutdown); } } static void warn_about_lame_clients_and_finish_interact (gboolean shutdown) { GSList *lame = NULL; GSList *windows; GSList *lame_details = NULL; GSList *tmp; GSList *columns = NULL; GPid pid; windows = meta_display_list_windows (meta_get_display (), META_LIST_DEFAULT); tmp = windows; while (tmp != NULL) { MetaWindow *window; window = tmp->data; /* only complain about normal windows, the others * are kind of dumb to worry about */ if (window->sm_client_id == NULL && window->type == META_WINDOW_NORMAL) lame = g_slist_prepend (lame, window); tmp = tmp->next; } g_slist_free (windows); if (lame == NULL) { /* No lame apps. */ finish_interact (shutdown); return; } columns = g_slist_prepend (columns, (gpointer)"Window"); columns = g_slist_prepend (columns, (gpointer)"Class"); lame = g_slist_sort (lame, (GCompareFunc) windows_cmp_by_title); tmp = lame; while (tmp != NULL) { MetaWindow *w = tmp->data; lame_details = g_slist_prepend (lame_details, w->res_class ? w->res_class : (gpointer)""); lame_details = g_slist_prepend (lame_details, w->title); tmp = tmp->next; } g_slist_free (lame); pid = meta_show_dialog("--list", _("These windows do not support “save current setup” " "and will have to be restarted manually next time " "you log in."), "240", meta_get_display()->x11_display->screen_name, NULL, NULL, None, columns, lame_details); g_slist_free (lame_details); g_child_watch_add (pid, dialog_closed, GINT_TO_POINTER (shutdown)); } #endif /* HAVE_SM */ muffin-6.4.1/src/x11/meta-startup-notification-x11.h0000664000175000017500000000353314723361714021040 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2018 Red Hat, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "meta-x11-display-private.h" #ifndef META_X11_STARTUP_NOTIFICATION_H #define META_X11_STARTUP_NOTIFICATION_H typedef struct _MetaX11StartupNotification MetaX11StartupNotification; #define META_TYPE_STARTUP_SEQUENCE_X11 (meta_startup_sequence_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaStartupSequenceX11, meta_startup_sequence_x11, META, STARTUP_SEQUENCE_X11, MetaStartupSequence) void meta_x11_startup_notification_init (MetaX11Display *x11_display); void meta_x11_startup_notification_release (MetaX11Display *x11_display); gboolean meta_x11_startup_notification_handle_xevent (MetaX11Display *x11_display, XEvent *xevent); gchar * meta_x11_startup_notification_launch (MetaX11Display *x11_display, GAppInfo *app_info, uint32_t timestamp, int workspace); #endif /* META_X11_STARTUP_NOTIFICATION_H */ muffin-6.4.1/src/x11/meta-x11-display.c0000664000175000017500000021211714723361714016312 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:x11-display * @title: MetaX11Display * @short_description: Mutter X display handler * * The X11 display is represented as a #MetaX11Display struct. */ #include "config.h" #include "core/display-private.h" #include "x11/meta-x11-display-private.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "backends/meta-dnd-private.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-settings-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-stage-x11.h" #include "core/frame.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "core/workspace-private.h" #include "meta/main.h" #include "meta/meta-x11-errors.h" #include "x11/events.h" #include "x11/group-props.h" #include "x11/meta-x11-selection-private.h" #include "x11/window-props.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-xwayland-private.h" #endif G_DEFINE_TYPE (MetaX11Display, meta_x11_display, G_TYPE_OBJECT) static GQuark quark_x11_display_logical_monitor_data = 0; typedef struct _MetaX11DisplayLogicalMonitorData { int xinerama_index; } MetaX11DisplayLogicalMonitorData; static GdkDisplay *prepared_gdk_display = NULL; static const char *gnome_wm_keybindings = "Mutter,Metacity"; static const char *net_wm_name = "Mutter (Muffin)"; static char *get_screen_name (Display *xdisplay, int number); static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager, MetaX11Display *x11_display); static void update_cursor_theme (MetaX11Display *x11_display); static void unset_wm_check_hint (MetaX11Display *x11_display); static void prefs_changed_callback (MetaPreference pref, void *data); static void meta_x11_display_unmanage_windows (MetaX11Display *x11_display) { GList *windows, *l; if (!x11_display->xids) return; windows = g_hash_table_get_values (x11_display->xids); g_list_foreach (windows, (GFunc) g_object_ref, NULL); for (l = windows; l; l = l->next) { if (META_IS_WINDOW (l->data)) { MetaWindow *window = l->data; if (!window->unmanaging) meta_window_unmanage (window, META_CURRENT_TIME); } else if (META_IS_BARRIER (l->data)) meta_barrier_destroy (META_BARRIER (l->data)); else g_assert_not_reached (); } g_list_free_full (windows, g_object_unref); } static void meta_x11_display_dispose (GObject *object) { MetaX11Display *x11_display = META_X11_DISPLAY (object); x11_display->closing = TRUE; if (x11_display->empty_region != None) { XFixesDestroyRegion (x11_display->xdisplay, x11_display->empty_region); x11_display->empty_region = None; } meta_x11_startup_notification_release (x11_display); meta_prefs_remove_listener (prefs_changed_callback, x11_display); meta_x11_display_ungrab_keys (x11_display); g_clear_object (&x11_display->x11_stack); meta_x11_selection_shutdown (x11_display); meta_x11_display_unmanage_windows (x11_display); if (x11_display->ui) { meta_ui_free (x11_display->ui); x11_display->ui = NULL; } if (x11_display->no_focus_window != None) { XUnmapWindow (x11_display->xdisplay, x11_display->no_focus_window); XDestroyWindow (x11_display->xdisplay, x11_display->no_focus_window); x11_display->no_focus_window = None; } if (x11_display->composite_overlay_window != None) { XCompositeReleaseOverlayWindow (x11_display->xdisplay, x11_display->composite_overlay_window); x11_display->composite_overlay_window = None; } if (x11_display->wm_sn_selection_window != None) { XDestroyWindow (x11_display->xdisplay, x11_display->wm_sn_selection_window); x11_display->wm_sn_selection_window = None; } if (x11_display->timestamp_pinging_window != None) { XDestroyWindow (x11_display->xdisplay, x11_display->timestamp_pinging_window); x11_display->timestamp_pinging_window = None; } if (x11_display->leader_window != None) { XDestroyWindow (x11_display->xdisplay, x11_display->leader_window); x11_display->leader_window = None; } if (x11_display->guard_window != None) { XUnmapWindow (x11_display->xdisplay, x11_display->guard_window); XDestroyWindow (x11_display->xdisplay, x11_display->guard_window); x11_display->guard_window = None; } if (x11_display->prop_hooks) { meta_x11_display_free_window_prop_hooks (x11_display); x11_display->prop_hooks = NULL; } if (x11_display->group_prop_hooks) { meta_x11_display_free_group_prop_hooks (x11_display); x11_display->group_prop_hooks = NULL; } if (x11_display->xids) { /* Must be after all calls to meta_window_unmanage() since they * unregister windows */ g_hash_table_destroy (x11_display->xids); x11_display->xids = NULL; } if (x11_display->xroot != None) { unset_wm_check_hint (x11_display); meta_x11_error_trap_push (x11_display); XSelectInput (x11_display->xdisplay, x11_display->xroot, 0); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) meta_warning ("Could not release screen %d on display \"%s\"\n", DefaultScreen (x11_display->xdisplay), x11_display->name); x11_display->xroot = None; } if (x11_display->xdisplay) { meta_x11_display_free_events (x11_display); x11_display->xdisplay = NULL; } if (x11_display->gdk_display) { gdk_display_close (x11_display->gdk_display); x11_display->gdk_display = NULL; } g_clear_handle_id (&x11_display->display_close_idle, g_source_remove); g_free (x11_display->name); x11_display->name = NULL; g_free (x11_display->screen_name); x11_display->screen_name = NULL; G_OBJECT_CLASS (meta_x11_display_parent_class)->dispose (object); } static void meta_x11_display_class_init (MetaX11DisplayClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->dispose = meta_x11_display_dispose; } static void meta_x11_display_init (MetaX11Display *x11_display) { quark_x11_display_logical_monitor_data = g_quark_from_static_string ("-meta-x11-display-logical-monitor-data"); } static void query_xsync_extension (MetaX11Display *x11_display) { int major, minor; x11_display->have_xsync = FALSE; x11_display->xsync_error_base = 0; x11_display->xsync_event_base = 0; /* I don't think we really have to fill these in */ major = SYNC_MAJOR_VERSION; minor = SYNC_MINOR_VERSION; if (!XSyncQueryExtension (x11_display->xdisplay, &x11_display->xsync_event_base, &x11_display->xsync_error_base) || !XSyncInitialize (x11_display->xdisplay, &major, &minor)) { x11_display->xsync_error_base = 0; x11_display->xsync_event_base = 0; } else { x11_display->have_xsync = TRUE; XSyncSetPriority (x11_display->xdisplay, None, 10); } meta_verbose ("Attempted to init Xsync, found version %d.%d error base %d event base %d\n", major, minor, x11_display->xsync_error_base, x11_display->xsync_event_base); } static void query_xshape_extension (MetaX11Display *x11_display) { x11_display->have_shape = FALSE; x11_display->shape_error_base = 0; x11_display->shape_event_base = 0; if (!XShapeQueryExtension (x11_display->xdisplay, &x11_display->shape_event_base, &x11_display->shape_error_base)) { x11_display->shape_error_base = 0; x11_display->shape_event_base = 0; } else x11_display->have_shape = TRUE; meta_verbose ("Attempted to init Shape, found error base %d event base %d\n", x11_display->shape_error_base, x11_display->shape_event_base); } static void query_xcomposite_extension (MetaX11Display *x11_display) { x11_display->have_composite = FALSE; x11_display->composite_error_base = 0; x11_display->composite_event_base = 0; if (!XCompositeQueryExtension (x11_display->xdisplay, &x11_display->composite_event_base, &x11_display->composite_error_base)) { x11_display->composite_error_base = 0; x11_display->composite_event_base = 0; } else { x11_display->composite_major_version = 0; x11_display->composite_minor_version = 0; if (XCompositeQueryVersion (x11_display->xdisplay, &x11_display->composite_major_version, &x11_display->composite_minor_version)) { x11_display->have_composite = TRUE; } else { x11_display->composite_major_version = 0; x11_display->composite_minor_version = 0; } } meta_verbose ("Attempted to init Composite, found error base %d event base %d " "extn ver %d %d\n", x11_display->composite_error_base, x11_display->composite_event_base, x11_display->composite_major_version, x11_display->composite_minor_version); } static void query_xdamage_extension (MetaX11Display *x11_display) { x11_display->have_damage = FALSE; x11_display->damage_error_base = 0; x11_display->damage_event_base = 0; if (!XDamageQueryExtension (x11_display->xdisplay, &x11_display->damage_event_base, &x11_display->damage_error_base)) { x11_display->damage_error_base = 0; x11_display->damage_event_base = 0; } else x11_display->have_damage = TRUE; meta_verbose ("Attempted to init Damage, found error base %d event base %d\n", x11_display->damage_error_base, x11_display->damage_event_base); } static void query_xfixes_extension (MetaX11Display *x11_display) { x11_display->xfixes_error_base = 0; x11_display->xfixes_event_base = 0; if (XFixesQueryExtension (x11_display->xdisplay, &x11_display->xfixes_event_base, &x11_display->xfixes_error_base)) { int xfixes_major, xfixes_minor; XFixesQueryVersion (x11_display->xdisplay, &xfixes_major, &xfixes_minor); if (xfixes_major * 100 + xfixes_minor < 500) meta_fatal ("Mutter requires XFixes 5.0"); } else { meta_fatal ("Mutter requires XFixes 5.0"); } meta_verbose ("Attempted to init XFixes, found error base %d event base %d\n", x11_display->xfixes_error_base, x11_display->xfixes_event_base); } static void query_xi_extension (MetaX11Display *x11_display) { int major = 2, minor = 3; gboolean has_xi = FALSE; if (XQueryExtension (x11_display->xdisplay, "XInputExtension", &x11_display->xinput_opcode, &x11_display->xinput_error_base, &x11_display->xinput_event_base)) { if (XIQueryVersion (x11_display->xdisplay, &major, &minor) == Success) { int version = (major * 10) + minor; if (version >= 22) has_xi = TRUE; if (version >= 23) x11_display->have_xinput_23 = TRUE; } } if (!has_xi) meta_fatal ("X server doesn't have the XInput extension, version 2.2 or newer\n"); } /* * Initialises the bell subsystem. This involves intialising * XKB (which, despite being a keyboard extension, is the * place to look for bell notifications), then asking it * to send us bell notifications, and then also switching * off the audible bell if we're using a visual one ourselves. * * \bug There is a line of code that's never run that tells * XKB to reset the bell status after we quit. Bill H said * () * that XFree86's implementation is broken so we shouldn't * call it, but that was in 2002. Is it working now? */ static void init_x11_bell (MetaX11Display *x11_display) { int xkb_base_error_type, xkb_opcode; if (!XkbQueryExtension (x11_display->xdisplay, &xkb_opcode, &x11_display->xkb_base_event_type, &xkb_base_error_type, NULL, NULL)) { x11_display->xkb_base_event_type = -1; meta_warning ("could not find XKB extension."); } else { unsigned int mask = XkbBellNotifyMask; gboolean visual_bell_auto_reset = FALSE; /* TRUE if and when non-broken version is available */ XkbSelectEvents (x11_display->xdisplay, XkbUseCoreKbd, XkbBellNotifyMask, XkbBellNotifyMask); if (visual_bell_auto_reset) { XkbSetAutoResetControls (x11_display->xdisplay, XkbAudibleBellMask, &mask, &mask); } } /* We are playing sounds using libcanberra support, we handle the * bell whether its an audible bell or a visible bell */ XkbChangeEnabledControls (x11_display->xdisplay, XkbUseCoreKbd, XkbAudibleBellMask, 0); } /* * \bug This is never called! If we had XkbSetAutoResetControls * enabled in meta_x11_bell_init(), this wouldn't be a problem, * but we don't. */ G_GNUC_UNUSED static void shutdown_x11_bell (MetaX11Display *x11_display) { /* TODO: persist initial bell state in display, reset here */ XkbChangeEnabledControls (x11_display->xdisplay, XkbUseCoreKbd, XkbAudibleBellMask, XkbAudibleBellMask); } static void set_desktop_geometry_hint (MetaX11Display *x11_display) { unsigned long data[2]; int monitor_width, monitor_height; if (x11_display->display->closing > 0) return; meta_display_get_size (x11_display->display, &monitor_width, &monitor_height); data[0] = monitor_width; data[1] = monitor_height; meta_verbose ("Setting _NET_DESKTOP_GEOMETRY to %lu, %lu\n", data[0], data[1]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_DESKTOP_GEOMETRY, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 2); meta_x11_error_trap_pop (x11_display); } static void set_desktop_viewport_hint (MetaX11Display *x11_display) { unsigned long data[2]; if (x11_display->display->closing > 0) return; /* * Mutter does not implement viewports, so this is a fixed 0,0 */ data[0] = 0; data[1] = 0; meta_verbose ("Setting _NET_DESKTOP_VIEWPORT to 0, 0\n"); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_DESKTOP_VIEWPORT, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 2); meta_x11_error_trap_pop (x11_display); } static int set_wm_check_hint (MetaX11Display *x11_display) { unsigned long data[1]; g_return_val_if_fail (x11_display->leader_window != None, 0); data[0] = x11_display->leader_window; XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); return Success; } static void unset_wm_check_hint (MetaX11Display *x11_display) { XDeleteProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_SUPPORTING_WM_CHECK); } static int set_supported_hint (MetaX11Display *x11_display) { Atom atoms[] = { #define EWMH_ATOMS_ONLY #define item(x) x11_display->atom_##x, #include "x11/atomnames.h" #undef item #undef EWMH_ATOMS_ONLY x11_display->atom__GTK_FRAME_EXTENTS, x11_display->atom__GTK_SHOW_WINDOW_MENU, x11_display->atom__GTK_EDGE_CONSTRAINTS, x11_display->atom__GTK_WORKAREAS, }; XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_SUPPORTED, XA_ATOM, 32, PropModeReplace, (guchar*) atoms, G_N_ELEMENTS(atoms)); return Success; } static int set_wm_icon_size_hint (MetaX11Display *x11_display) { #define N_VALS 6 gulong vals[N_VALS]; /* We've bumped the real icon size up to 96x96, but * we really should not add these sorts of constraints * on clients still using the legacy WM_HINTS interface. */ #define LEGACY_ICON_SIZE 32 /* min width, min height, max w, max h, width inc, height inc */ vals[0] = LEGACY_ICON_SIZE; vals[1] = LEGACY_ICON_SIZE; vals[2] = LEGACY_ICON_SIZE; vals[3] = LEGACY_ICON_SIZE; vals[4] = 0; vals[5] = 0; #undef LEGACY_ICON_SIZE XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom_WM_ICON_SIZE, XA_CARDINAL, 32, PropModeReplace, (guchar*) vals, N_VALS); return Success; #undef N_VALS } static Window take_manager_selection (MetaX11Display *x11_display, Window xroot, Atom manager_atom, int timestamp, gboolean should_replace) { Window current_owner, new_owner; current_owner = XGetSelectionOwner (x11_display->xdisplay, manager_atom); if (current_owner != None) { XSetWindowAttributes attrs; if (should_replace) { /* We want to find out when the current selection owner dies */ meta_x11_error_trap_push (x11_display); attrs.event_mask = StructureNotifyMask; XChangeWindowAttributes (x11_display->xdisplay, current_owner, CWEventMask, &attrs); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) current_owner = None; /* don't wait for it to die later on */ } else { meta_warning (_("Display “%s” already has a window manager; try using the --replace option to replace the current window manager."), x11_display->name); return None; } } /* We need SelectionClear and SelectionRequest events on the new owner, * but those cannot be masked, so we only need NoEventMask. */ new_owner = meta_x11_display_create_offscreen_window (x11_display, xroot, NoEventMask); XSetSelectionOwner (x11_display->xdisplay, manager_atom, new_owner, timestamp); if (XGetSelectionOwner (x11_display->xdisplay, manager_atom) != new_owner) { meta_warning ("Could not acquire selection: %s", XGetAtomName (x11_display->xdisplay, manager_atom)); return None; } { /* Send client message indicating that we are now the selection owner */ XClientMessageEvent ev; ev.type = ClientMessage; ev.window = xroot; ev.message_type = x11_display->atom_MANAGER; ev.format = 32; ev.data.l[0] = timestamp; ev.data.l[1] = manager_atom; XSendEvent (x11_display->xdisplay, xroot, False, StructureNotifyMask, (XEvent *) &ev); } /* Wait for old window manager to go away */ if (current_owner != None) { XEvent event; /* We sort of block infinitely here which is probably lame. */ meta_verbose ("Waiting for old window manager to exit\n"); do XWindowEvent (x11_display->xdisplay, current_owner, StructureNotifyMask, &event); while (event.type != DestroyNotify); } return new_owner; } /* Create the leader window here. Set its properties and * use the timestamp from one of the PropertyNotify events * that will follow. */ static void init_leader_window (MetaX11Display *x11_display, guint32 *timestamp) { gulong data[1]; XEvent event; /* We only care about the PropertyChangeMask in the next 30 or so lines of * code. Note that gdk will at some point unset the PropertyChangeMask for * this window, so we can't rely on it still being set later. See bug * 354213 for details. */ x11_display->leader_window = meta_x11_display_create_offscreen_window (x11_display, x11_display->xroot, PropertyChangeMask); meta_prop_set_utf8_string_hint (x11_display, x11_display->leader_window, x11_display->atom__NET_WM_NAME, net_wm_name); meta_prop_set_utf8_string_hint (x11_display, x11_display->leader_window, x11_display->atom__GNOME_WM_KEYBINDINGS, gnome_wm_keybindings); meta_prop_set_utf8_string_hint (x11_display, x11_display->leader_window, x11_display->atom__MUTTER_VERSION, VERSION); data[0] = x11_display->leader_window; XChangeProperty (x11_display->xdisplay, x11_display->leader_window, x11_display->atom__NET_SUPPORTING_WM_CHECK, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); XWindowEvent (x11_display->xdisplay, x11_display->leader_window, PropertyChangeMask, &event); if (timestamp) *timestamp = event.xproperty.time; /* Make it painfully clear that we can't rely on PropertyNotify events on * this window, as per bug 354213. */ XSelectInput (x11_display->xdisplay, x11_display->leader_window, NoEventMask); } static void init_event_masks (MetaX11Display *x11_display) { long event_mask; unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); if (META_X11_DISPLAY_HAS_XINPUT_23 (x11_display)) { XISetMask (mask.mask, XI_BarrierHit); XISetMask (mask.mask, XI_BarrierLeave); } XISelectEvents (x11_display->xdisplay, x11_display->xroot, &mask, 1); event_mask = (SubstructureRedirectMask | SubstructureNotifyMask | StructureNotifyMask | ColormapChangeMask | PropertyChangeMask); XSelectInput (x11_display->xdisplay, x11_display->xroot, event_mask); } static void set_active_workspace_hint (MetaWorkspaceManager *workspace_manager, MetaX11Display *x11_display) { unsigned long data[1]; /* this is because we destroy the spaces in order, * so we always end up setting a current desktop of * 0 when closing a screen, so lose the current desktop * on restart. By doing this we keep the current * desktop on restart. */ if (x11_display->display->closing > 0) return; data[0] = meta_workspace_index (workspace_manager->active_workspace); meta_verbose ("Setting _NET_CURRENT_DESKTOP to %lu\n", data[0]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_CURRENT_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } static void set_number_of_spaces_hint (MetaWorkspaceManager *workspace_manager, GParamSpec *pspec, gpointer user_data) { MetaX11Display *x11_display = user_data; unsigned long data[1]; if (x11_display->display->closing > 0) return; data[0] = meta_workspace_manager_get_n_workspaces (workspace_manager); meta_verbose ("Setting _NET_NUMBER_OF_DESKTOPS to %lu\n", data[0]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_NUMBER_OF_DESKTOPS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } static void set_showing_desktop_hint (MetaWorkspaceManager *workspace_manager, MetaX11Display *x11_display) { unsigned long data[1]; data[0] = workspace_manager->active_workspace->showing_desktop ? 1 : 0; meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_SHOWING_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } static void set_workspace_names (MetaX11Display *x11_display) { MetaWorkspaceManager *workspace_manager; GString *flattened; int i; int n_spaces; workspace_manager = x11_display->display->workspace_manager; /* flatten to nul-separated list */ n_spaces = meta_workspace_manager_get_n_workspaces (workspace_manager); flattened = g_string_new (""); i = 0; while (i < n_spaces) { const char *name; name = meta_prefs_get_workspace_name (i); if (name) g_string_append_len (flattened, name, strlen (name) + 1); else g_string_append_len (flattened, "", 1); ++i; } meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_DESKTOP_NAMES, x11_display->atom_UTF8_STRING, 8, PropModeReplace, (unsigned char *)flattened->str, flattened->len); meta_x11_error_trap_pop (x11_display); g_string_free (flattened, TRUE); } static void set_workspace_work_area_hint (MetaWorkspace *workspace, MetaX11Display *x11_display) { MetaMonitorManager *monitor_manager; GList *logical_monitors; GList *l; int num_monitors; unsigned long *data; unsigned long *tmp; g_autofree char *workarea_name; Atom workarea_atom; monitor_manager = meta_backend_get_monitor_manager (meta_get_backend ()); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); num_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); data = g_new (unsigned long, num_monitors * 4); tmp = data; for (l = logical_monitors; l; l = l->next) { MetaRectangle area; meta_workspace_get_work_area_for_logical_monitor (workspace, l->data, &area); tmp[0] = area.x; tmp[1] = area.y; tmp[2] = area.width; tmp[3] = area.height; tmp += 4; } workarea_name = g_strdup_printf ("_GTK_WORKAREAS_D%d", meta_workspace_index (workspace)); workarea_atom = XInternAtom (x11_display->xdisplay, workarea_name, False); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, workarea_atom, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, num_monitors * 4); meta_x11_error_trap_pop (x11_display); g_free (data); } static void set_work_area_hint (MetaDisplay *display, MetaX11Display *x11_display) { MetaWorkspaceManager *workspace_manager = display->workspace_manager; int num_workspaces; GList *l; unsigned long *data, *tmp; MetaRectangle area; num_workspaces = meta_workspace_manager_get_n_workspaces (workspace_manager); data = g_new (unsigned long, num_workspaces * 4); tmp = data; for (l = workspace_manager->workspaces; l; l = l->next) { MetaWorkspace *workspace = l->data; meta_workspace_get_work_area_all_monitors (workspace, &area); set_workspace_work_area_hint (workspace, x11_display); tmp[0] = area.x; tmp[1] = area.y; tmp[2] = area.width; tmp[3] = area.height; tmp += 4; } meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_WORKAREA, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, num_workspaces*4); meta_x11_error_trap_pop (x11_display); g_free (data); } /** * meta_set_wm_name: (skip) * @wm_name: value for _NET_WM_NAME * * Set the value to use for the _NET_WM_NAME property. To take effect, * it is necessary to call this function before meta_init(). */ void meta_set_wm_name (const char *wm_name) { g_return_if_fail (meta_get_display () == NULL); net_wm_name = wm_name; } /** * meta_set_gnome_wm_keybindings: (skip) * @wm_keybindings: value for _GNOME_WM_KEYBINDINGS * * Set the value to use for the _GNOME_WM_KEYBINDINGS property. To take * effect, it is necessary to call this function before meta_init(). */ void meta_set_gnome_wm_keybindings (const char *wm_keybindings) { g_return_if_fail (meta_get_display () == NULL); gnome_wm_keybindings = wm_keybindings; } const gchar * meta_x11_get_display_name (void) { #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); return meta_wayland_get_xwayland_display_name (compositor); } else #endif { return g_getenv ("DISPLAY"); } } gboolean meta_x11_init_gdk_display (GError **error) { const char *xdisplay_name; GdkDisplay *gdk_display; const char *gdk_gl_env = NULL; Display *xdisplay; xdisplay_name = meta_x11_get_display_name (); if (!xdisplay_name) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unable to open display, DISPLAY not set"); return FALSE; } gdk_set_allowed_backends ("x11"); gdk_gl_env = g_getenv ("GDK_GL"); g_setenv ("GDK_GL", "disable", TRUE); gdk_parse_args (NULL, NULL); if (!gtk_parse_args (NULL, NULL)) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to initialize gtk"); return FALSE; } gdk_display = gdk_display_open (xdisplay_name); if (!gdk_display) { meta_warning (_("Failed to initialize GDK\n")); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to initialize GDK"); return FALSE; } if (gdk_gl_env) g_setenv("GDK_GL", gdk_gl_env, TRUE); else unsetenv("GDK_GL"); /* We need to be able to fully trust that the window and monitor sizes that Gdk reports corresponds to the X ones, so we disable the automatic scale handling */ gdk_x11_display_set_window_scale (gdk_display, 1); meta_verbose ("Opening display '%s'\n", XDisplayName (NULL)); xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display); if (xdisplay == NULL) { meta_warning (_("Failed to open X Window System display “%s”\n"), XDisplayName (NULL)); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to open X11 display"); gdk_display_close (gdk_display); return FALSE; } prepared_gdk_display = gdk_display; return TRUE; } /** * meta_x11_display_new: * * Opens a new X11 display, sets it up, initialises all the X extensions * we will need. * * Returns: #MetaX11Display if the display was opened successfully, * and %NULL otherwise-- that is, if the display doesn't exist or * it already has a window manager, and sets the error appropriately. */ MetaX11Display * meta_x11_display_new (MetaDisplay *display, GError **error) { MetaX11Display *x11_display; Display *xdisplay; Screen *xscreen; Window xroot; int i, number; Window new_wm_sn_owner; gboolean replace_current_wm; Atom wm_sn_atom; char buf[128]; guint32 timestamp; Atom atom_restart_helper; Window restart_helper_window = None; GdkDisplay *gdk_display; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); /* A list of all atom names, so that we can intern them in one go. */ const char *atom_names[] = { #define item(x) #x, #include "x11/atomnames.h" #undef item }; Atom atoms[G_N_ELEMENTS(atom_names)]; if (!meta_x11_init_gdk_display (error)) return NULL; g_assert (prepared_gdk_display); gdk_display = g_steal_pointer (&prepared_gdk_display); xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display); #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_xwayland_complete_init (display, xdisplay); #endif if (meta_is_syncing ()) XSynchronize (xdisplay, True); replace_current_wm = meta_get_replace_current_wm (); /* According to _gdk_x11_display_open (), this will be returned * by gdk_display_get_default_screen () */ number = DefaultScreen (xdisplay); xroot = RootWindow (xdisplay, number); /* FVWM checks for None here, I don't know if this * ever actually happens */ if (xroot == None) { meta_warning (_("Screen %d on display “%s” is invalid\n"), number, XDisplayName (NULL)); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to open default X11 screen"); XFlush (xdisplay); XCloseDisplay (xdisplay); gdk_display_close (gdk_display); return NULL; } xscreen = ScreenOfDisplay (xdisplay, number); atom_restart_helper = XInternAtom (xdisplay, "_MUFFIN_RESTART_HELPER", False); restart_helper_window = XGetSelectionOwner (xdisplay, atom_restart_helper); if (restart_helper_window) meta_set_is_restart (TRUE); x11_display = g_object_new (META_TYPE_X11_DISPLAY, NULL); x11_display->gdk_display = gdk_display; x11_display->display = display; x11_display->atom_x_root_pixmap = XInternAtom (xdisplay, "_XROOTPMAP_ID", False); /* here we use XDisplayName which is what the user * probably put in, vs. DisplayString(display) which is * canonicalized by XOpenDisplay() */ x11_display->xdisplay = xdisplay; x11_display->xroot = xroot; x11_display->name = g_strdup (XDisplayName (NULL)); x11_display->screen_name = get_screen_name (xdisplay, number); x11_display->default_xvisual = DefaultVisualOfScreen (xscreen); x11_display->default_depth = DefaultDepthOfScreen (xscreen); meta_verbose ("Creating %d atoms\n", (int) G_N_ELEMENTS (atom_names)); XInternAtoms (xdisplay, (char **)atom_names, G_N_ELEMENTS (atom_names), False, atoms); i = 0; #define item(x) x11_display->atom_##x = atoms[i++]; #include "x11/atomnames.h" #undef item query_xsync_extension (x11_display); query_xshape_extension (x11_display); query_xcomposite_extension (x11_display); query_xdamage_extension (x11_display); query_xfixes_extension (x11_display); query_xi_extension (x11_display); g_signal_connect_object (display, "cursor-updated", G_CALLBACK (update_cursor_theme), x11_display, G_CONNECT_SWAPPED); update_cursor_theme (x11_display); x11_display->xids = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); x11_display->groups_by_leader = NULL; x11_display->ui = NULL; x11_display->composite_overlay_window = None; x11_display->guard_window = None; x11_display->leader_window = None; x11_display->timestamp_pinging_window = None; x11_display->wm_sn_selection_window = None; x11_display->display_close_idle = 0; x11_display->xselectionclear_timestamp = 0; x11_display->last_bell_time = 0; x11_display->focus_serial = 0; x11_display->server_focus_window = None; x11_display->server_focus_serial = 0; x11_display->prop_hooks = NULL; meta_x11_display_init_window_prop_hooks (x11_display); x11_display->group_prop_hooks = NULL; meta_x11_display_init_group_prop_hooks (x11_display); g_signal_connect_object (monitor_manager, "monitors-changed-internal", G_CALLBACK (on_monitors_changed_internal), x11_display, 0); init_leader_window (x11_display, ×tamp); x11_display->timestamp = timestamp; /* Make a little window used only for pinging the server for timestamps; note * that meta_create_offscreen_window already selects for PropertyChangeMask. */ x11_display->timestamp_pinging_window = meta_x11_display_create_offscreen_window (x11_display, xroot, PropertyChangeMask); sprintf (buf, "WM_S%d", number); wm_sn_atom = XInternAtom (xdisplay, buf, False); new_wm_sn_owner = take_manager_selection (x11_display, xroot, wm_sn_atom, timestamp, replace_current_wm); if (new_wm_sn_owner == None) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to acquire window manager ownership"); g_object_run_dispose (G_OBJECT (x11_display)); g_clear_object (&x11_display); return NULL; } x11_display->wm_sn_selection_window = new_wm_sn_owner; x11_display->wm_sn_atom = wm_sn_atom; x11_display->wm_sn_timestamp = timestamp; init_event_masks (x11_display); /* Select for cursor changes so the cursor tracker is up to date. */ XFixesSelectCursorInput (xdisplay, xroot, XFixesDisplayCursorNotifyMask); /* If we're a Wayland compositor, then we don't grab the COW, since it * will map it. */ if (!meta_is_wayland_compositor ()) x11_display->composite_overlay_window = XCompositeGetOverlayWindow (xdisplay, xroot); /* Now that we've gotten taken a reference count on the COW, we * can close the helper that is holding on to it */ if (meta_is_restart ()) XSetSelectionOwner (xdisplay, atom_restart_helper, None, META_CURRENT_TIME); /* Handle creating a no_focus_window for this screen */ x11_display->no_focus_window = meta_x11_display_create_offscreen_window (x11_display, xroot, FocusChangeMask|KeyPressMask|KeyReleaseMask); XMapWindow (xdisplay, x11_display->no_focus_window); /* Done with no_focus_window stuff */ meta_x11_display_init_events (x11_display); set_wm_icon_size_hint (x11_display); set_supported_hint (x11_display); set_wm_check_hint (x11_display); set_desktop_viewport_hint (x11_display); set_desktop_geometry_hint (x11_display); x11_display->ui = meta_ui_new (x11_display); x11_display->x11_stack = meta_x11_stack_new (x11_display); x11_display->keys_grabbed = FALSE; meta_x11_display_grab_keys (x11_display); meta_x11_display_update_workspace_layout (x11_display); if (meta_prefs_get_dynamic_workspaces ()) { int num = 0; int n_items = 0; uint32_t *list = NULL; if (meta_prop_get_cardinal_list (x11_display, x11_display->xroot, x11_display->atom__NET_NUMBER_OF_DESKTOPS, &list, &n_items)) { num = list[0]; g_free (list); } if (num > meta_workspace_manager_get_n_workspaces (display->workspace_manager)) meta_workspace_manager_update_num_workspaces (display->workspace_manager, timestamp, num); } g_signal_connect_object (display->workspace_manager, "active-workspace-changed", G_CALLBACK (set_active_workspace_hint), x11_display, 0); set_number_of_spaces_hint (display->workspace_manager, NULL, x11_display); g_signal_connect_object (display->workspace_manager, "notify::n-workspaces", G_CALLBACK (set_number_of_spaces_hint), x11_display, 0); set_showing_desktop_hint (display->workspace_manager, x11_display); g_signal_connect_object (display->workspace_manager, "showing-desktop-changed", G_CALLBACK (set_showing_desktop_hint), x11_display, 0); set_workspace_names (x11_display); meta_prefs_add_listener (prefs_changed_callback, x11_display); set_work_area_hint (display, x11_display); g_signal_connect_object (display, "workareas-changed", G_CALLBACK (set_work_area_hint), x11_display, 0); init_x11_bell (x11_display); meta_x11_startup_notification_init (x11_display); meta_x11_selection_init (x11_display); if (!meta_is_wayland_compositor ()) meta_dnd_init_xdnd (x11_display); return x11_display; } void meta_x11_display_restore_active_workspace (MetaX11Display *x11_display) { MetaDisplay *display; MetaWorkspace *current_workspace; uint32_t current_workspace_index = 0; guint32 timestamp; g_return_if_fail (META_IS_X11_DISPLAY (x11_display)); display = x11_display->display; timestamp = x11_display->timestamp; /* Get current workspace */ if (meta_prop_get_cardinal (x11_display, x11_display->xroot, x11_display->atom__NET_CURRENT_DESKTOP, ¤t_workspace_index)) { meta_verbose ("Read existing _NET_CURRENT_DESKTOP = %d\n", (int) current_workspace_index); /* Switch to the _NET_CURRENT_DESKTOP workspace */ current_workspace = meta_workspace_manager_get_workspace_by_index (display->workspace_manager, current_workspace_index); if (current_workspace != NULL) meta_workspace_activate (current_workspace, timestamp); } else { meta_verbose ("No _NET_CURRENT_DESKTOP present\n"); } set_active_workspace_hint (display->workspace_manager, x11_display); } int meta_x11_display_get_screen_number (MetaX11Display *x11_display) { return DefaultScreen (x11_display->xdisplay); } MetaDisplay * meta_x11_display_get_display (MetaX11Display *x11_display) { return x11_display->display; } /** * meta_x11_display_get_xdisplay: (skip) * @x11_display: a #MetaX11Display * */ Display * meta_x11_display_get_xdisplay (MetaX11Display *x11_display) { return x11_display->xdisplay; } /** * meta_x11_display_get_xroot: (skip) * @x11_display: A #MetaX11Display * */ Window meta_x11_display_get_xroot (MetaX11Display *x11_display) { return x11_display->xroot; } /** * meta_x11_display_get_xinput_opcode: (skip) * @x11_display: a #MetaX11Display * */ int meta_x11_display_get_xinput_opcode (MetaX11Display *x11_display) { return x11_display->xinput_opcode; } int meta_x11_display_get_damage_event_base (MetaX11Display *x11_display) { return x11_display->damage_event_base; } int meta_x11_display_get_shape_event_base (MetaX11Display *x11_display) { return x11_display->shape_event_base; } gboolean meta_x11_display_has_shape (MetaX11Display *x11_display) { return META_X11_DISPLAY_HAS_SHAPE (x11_display); } Window meta_x11_display_create_offscreen_window (MetaX11Display *x11_display, Window parent, long valuemask) { XSetWindowAttributes attrs; /* we want to be override redirect because sometimes we * create a window on a screen we aren't managing. * (but on a display we are managing at least one screen for) */ attrs.override_redirect = True; attrs.event_mask = valuemask; return XCreateWindow (x11_display->xdisplay, parent, -100, -100, 1, 1, 0, CopyFromParent, CopyFromParent, (Visual *)CopyFromParent, CWOverrideRedirect | CWEventMask, &attrs); } Cursor meta_x11_display_create_x_cursor (MetaX11Display *x11_display, MetaCursor cursor) { return meta_create_x_cursor (x11_display->xdisplay, cursor); } static char * get_screen_name (Display *xdisplay, int number) { char *p; char *dname; char *scr; /* DisplayString gives us a sort of canonical display, * vs. the user-entered name from XDisplayName() */ dname = g_strdup (DisplayString (xdisplay)); /* Change display name to specify this screen. */ p = strrchr (dname, ':'); if (p) { p = strchr (p, '.'); if (p) *p = '\0'; } scr = g_strdup_printf ("%s.%d", dname, number); g_free (dname); return scr; } void meta_x11_display_reload_cursor (MetaX11Display *x11_display) { Cursor xcursor; MetaCursor cursor = x11_display->display->current_cursor; /* Set a cursor for X11 applications that don't specify their own */ xcursor = meta_x11_display_create_x_cursor (x11_display, cursor); XDefineCursor (x11_display->xdisplay, x11_display->xroot, xcursor); XFlush (x11_display->xdisplay); if (xcursor) XFreeCursor (x11_display->xdisplay, xcursor); } static void set_cursor_theme (Display *xdisplay) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); int scale; scale = meta_settings_get_ui_scaling_factor (settings); XcursorSetTheme (xdisplay, meta_prefs_get_cursor_theme ()); XcursorSetDefaultSize (xdisplay, meta_prefs_get_cursor_size () * scale); } static void update_cursor_theme (MetaX11Display *x11_display) { MetaBackend *backend = meta_get_backend (); set_cursor_theme (x11_display->xdisplay); meta_x11_display_reload_cursor (x11_display); if (META_IS_BACKEND_X11 (backend)) { MetaBackendX11 *backend_x11 = META_BACKEND_X11 (backend); Display *xdisplay = meta_backend_x11_get_xdisplay (backend_x11); set_cursor_theme (xdisplay); meta_backend_x11_reload_cursor (backend_x11); } } MetaWindow * meta_x11_display_lookup_x_window (MetaX11Display *x11_display, Window xwindow) { return g_hash_table_lookup (x11_display->xids, &xwindow); } void meta_x11_display_register_x_window (MetaX11Display *x11_display, Window *xwindowp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (x11_display->xids, xwindowp) == NULL); g_hash_table_insert (x11_display->xids, xwindowp, window); } void meta_x11_display_unregister_x_window (MetaX11Display *x11_display, Window xwindow) { g_return_if_fail (g_hash_table_lookup (x11_display->xids, &xwindow) != NULL); g_hash_table_remove (x11_display->xids, &xwindow); } /* We store sync alarms in the window ID hash table, because they are * just more types of XIDs in the same global space, but we have * typesafe functions to register/unregister for readability. */ MetaWindow * meta_x11_display_lookup_sync_alarm (MetaX11Display *x11_display, XSyncAlarm alarm) { return g_hash_table_lookup (x11_display->xids, &alarm); } void meta_x11_display_register_sync_alarm (MetaX11Display *x11_display, XSyncAlarm *alarmp, MetaWindow *window) { g_return_if_fail (g_hash_table_lookup (x11_display->xids, alarmp) == NULL); g_hash_table_insert (x11_display->xids, alarmp, window); } void meta_x11_display_unregister_sync_alarm (MetaX11Display *x11_display, XSyncAlarm alarm) { g_return_if_fail (g_hash_table_lookup (x11_display->xids, &alarm) != NULL); g_hash_table_remove (x11_display->xids, &alarm); } void meta_x11_display_set_alarm_filter (MetaX11Display *x11_display, MetaAlarmFilter filter, gpointer data) { g_return_if_fail (filter == NULL || x11_display->alarm_filter == NULL); x11_display->alarm_filter = filter; x11_display->alarm_filter_data = data; } /* The guard window allows us to leave minimized windows mapped so * that compositor code may provide live previews of them. * Instead of being unmapped/withdrawn, they get pushed underneath * the guard window. We also select events on the guard window, which * should effectively be forwarded to events on the background actor, * providing that the scene graph is set up correctly. */ static Window create_guard_window (MetaX11Display *x11_display) { XSetWindowAttributes attributes; Window guard_window; gulong create_serial; int display_width, display_height; meta_display_get_size (x11_display->display, &display_width, &display_height); attributes.event_mask = NoEventMask; attributes.override_redirect = True; /* We have to call record_add() after we have the new window ID, * so save the serial for the CreateWindow request until then */ create_serial = XNextRequest (x11_display->xdisplay); guard_window = XCreateWindow (x11_display->xdisplay, x11_display->xroot, 0, /* x */ 0, /* y */ display_width, display_height, 0, /* border width */ 0, /* depth */ InputOnly, /* class */ CopyFromParent, /* visual */ CWEventMask | CWOverrideRedirect, &attributes); /* https://bugzilla.gnome.org/show_bug.cgi?id=710346 */ XStoreName (x11_display->xdisplay, guard_window, "mutter guard window"); { if (!meta_is_wayland_compositor ()) { MetaBackendX11 *backend = META_BACKEND_X11 (meta_get_backend ()); Display *backend_xdisplay = meta_backend_x11_get_xdisplay (backend); unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_ButtonPress); XISetMask (mask.mask, XI_ButtonRelease); XISetMask (mask.mask, XI_Motion); /* Sync on the connection we created the window on to * make sure it's created before we select on it on the * backend connection. */ XSync (x11_display->xdisplay, False); XISelectEvents (backend_xdisplay, guard_window, &mask, 1); } } meta_stack_tracker_record_add (x11_display->display->stack_tracker, guard_window, create_serial); meta_stack_tracker_lower (x11_display->display->stack_tracker, guard_window); XMapWindow (x11_display->xdisplay, guard_window); return guard_window; } void meta_x11_display_create_guard_window (MetaX11Display *x11_display) { if (x11_display->guard_window == None) x11_display->guard_window = create_guard_window (x11_display); } static void on_monitors_changed_internal (MetaMonitorManager *monitor_manager, MetaX11Display *x11_display) { int display_width, display_height; meta_monitor_manager_get_screen_size (monitor_manager, &display_width, &display_height); set_desktop_geometry_hint (x11_display); /* Resize the guard window to fill the screen again. */ if (x11_display->guard_window != None) { XWindowChanges changes; changes.x = 0; changes.y = 0; changes.width = display_width; changes.height = display_height; XConfigureWindow (x11_display->xdisplay, x11_display->guard_window, CWX | CWY | CWWidth | CWHeight, &changes); } x11_display->has_xinerama_indices = FALSE; } void meta_x11_display_set_cm_selection (MetaX11Display *x11_display) { char selection[32]; Atom a; guint32 timestamp; timestamp = meta_x11_display_get_current_time_roundtrip (x11_display); g_snprintf (selection, sizeof (selection), "_NET_WM_CM_S%d", DefaultScreen (x11_display->xdisplay)); a = XInternAtom (x11_display->xdisplay, selection, False); x11_display->wm_cm_selection_window = take_manager_selection (x11_display, x11_display->xroot, a, timestamp, TRUE); } static Bool find_timestamp_predicate (Display *xdisplay, XEvent *ev, XPointer arg) { MetaX11Display *x11_display = (MetaX11Display *) arg; return (ev->type == PropertyNotify && ev->xproperty.atom == x11_display->atom__MUTTER_TIMESTAMP_PING); } /* Get a timestamp, even if it means a roundtrip */ guint32 meta_x11_display_get_current_time_roundtrip (MetaX11Display *x11_display) { guint32 timestamp; timestamp = meta_display_get_current_time (x11_display->display); if (timestamp == META_CURRENT_TIME) { XEvent property_event; XChangeProperty (x11_display->xdisplay, x11_display->timestamp_pinging_window, x11_display->atom__MUTTER_TIMESTAMP_PING, XA_STRING, 8, PropModeAppend, NULL, 0); XIfEvent (x11_display->xdisplay, &property_event, find_timestamp_predicate, (XPointer) x11_display); timestamp = property_event.xproperty.time; } meta_display_sanity_check_timestamps (x11_display->display, timestamp); return timestamp; } /** * meta_x11_display_xwindow_is_a_no_focus_window: * @x11_display: A #MetaX11Display * @xwindow: An X11 window * * Returns: %TRUE iff window is one of mutter's internal "no focus" windows * which will have the focus when there is no actual client window focused. */ gboolean meta_x11_display_xwindow_is_a_no_focus_window (MetaX11Display *x11_display, Window xwindow) { return xwindow == x11_display->no_focus_window; } void meta_x11_display_increment_event_serial (MetaX11Display *x11_display) { /* We just make some random X request */ XDeleteProperty (x11_display->xdisplay, x11_display->leader_window, x11_display->atom__MOTIF_WM_HINTS); } static void meta_x11_display_update_active_window_hint (MetaX11Display *x11_display) { MetaWindow *focus_window; gulong data[1]; if (x11_display->display->closing) return; /* Leave old value for a replacement */ focus_window = meta_x11_display_lookup_x_window (x11_display, x11_display->focus_xwindow); if (focus_window) data[0] = focus_window->xwindow; else data[0] = None; meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__NET_ACTIVE_WINDOW, XA_WINDOW, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } void meta_x11_display_update_focus_window (MetaX11Display *x11_display, Window xwindow, gulong serial, gboolean focused_by_us) { x11_display->focus_serial = serial; x11_display->focused_by_us = !!focused_by_us; if (x11_display->focus_xwindow == xwindow) return; x11_display->focus_xwindow = xwindow; meta_x11_display_update_active_window_hint (x11_display); } static void meta_x11_display_set_input_focus_internal (MetaX11Display *x11_display, Window xwindow, uint32_t timestamp) { meta_x11_error_trap_push (x11_display); /* In order for mutter to know that the focus request succeeded, we track * the serial of the "focus request" we made, but if we take the serial * of the XSetInputFocus request, then there's no way to determine the * difference between focus events as a result of the SetInputFocus and * focus events that other clients send around the same time. Ensure that * we know which is which by making two requests that the server will * process at the same time. */ XGrabServer (x11_display->xdisplay); XSetInputFocus (x11_display->xdisplay, xwindow, RevertToPointerRoot, timestamp); XChangeProperty (x11_display->xdisplay, x11_display->timestamp_pinging_window, x11_display->atom__MUTTER_FOCUS_SET, XA_STRING, 8, PropModeAppend, NULL, 0); XUngrabServer (x11_display->xdisplay); XFlush (x11_display->xdisplay); meta_x11_error_trap_pop (x11_display); } void meta_x11_display_set_input_focus (MetaX11Display *x11_display, MetaWindow *window, gboolean focus_frame, uint32_t timestamp) { Window xwindow; gulong serial; if (window) xwindow = focus_frame ? window->frame->xwindow : window->xwindow; else xwindow = x11_display->no_focus_window; meta_x11_error_trap_push (x11_display); meta_x11_display_set_input_focus_internal (x11_display, xwindow, timestamp); serial = XNextRequest (x11_display->xdisplay); meta_x11_display_update_focus_window (x11_display, xwindow, serial, TRUE); meta_x11_error_trap_pop (x11_display); } void meta_x11_display_set_input_focus_xwindow (MetaX11Display *x11_display, Window window, guint32 timestamp) { gulong serial; if (meta_display_timestamp_too_old (x11_display->display, ×tamp)) return; meta_x11_display_set_input_focus_internal (x11_display, window, timestamp); serial = XNextRequest (x11_display->xdisplay); meta_x11_display_update_focus_window (x11_display, window, serial, TRUE); meta_display_update_focus_window (x11_display->display, NULL); meta_display_remove_autoraise_callback (x11_display->display); x11_display->display->last_focus_time = timestamp; } static MetaX11DisplayLogicalMonitorData * get_x11_display_logical_monitor_data (MetaLogicalMonitor *logical_monitor) { return g_object_get_qdata (G_OBJECT (logical_monitor), quark_x11_display_logical_monitor_data); } static MetaX11DisplayLogicalMonitorData * ensure_x11_display_logical_monitor_data (MetaLogicalMonitor *logical_monitor) { MetaX11DisplayLogicalMonitorData *data; data = get_x11_display_logical_monitor_data (logical_monitor); if (data) return data; data = g_new0 (MetaX11DisplayLogicalMonitorData, 1); g_object_set_qdata_full (G_OBJECT (logical_monitor), quark_x11_display_logical_monitor_data, data, g_free); return data; } static void meta_x11_display_ensure_xinerama_indices (MetaX11Display *x11_display) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; XineramaScreenInfo *infos; int n_infos, j; if (x11_display->has_xinerama_indices) return; x11_display->has_xinerama_indices = TRUE; if (!XineramaIsActive (x11_display->xdisplay)) return; infos = XineramaQueryScreens (x11_display->xdisplay, &n_infos); if (n_infos <= 0 || infos == NULL) { meta_XFree (infos); return; } logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; for (j = 0; j < n_infos; ++j) { if (logical_monitor->rect.x == infos[j].x_org && logical_monitor->rect.y == infos[j].y_org && logical_monitor->rect.width == infos[j].width && logical_monitor->rect.height == infos[j].height) { MetaX11DisplayLogicalMonitorData *logical_monitor_data; logical_monitor_data = ensure_x11_display_logical_monitor_data (logical_monitor); logical_monitor_data->xinerama_index = j; } } } meta_XFree (infos); } int meta_x11_display_logical_monitor_to_xinerama_index (MetaX11Display *x11_display, MetaLogicalMonitor *logical_monitor) { MetaX11DisplayLogicalMonitorData *logical_monitor_data; g_return_val_if_fail (logical_monitor, -1); meta_x11_display_ensure_xinerama_indices (x11_display); logical_monitor_data = get_x11_display_logical_monitor_data (logical_monitor); return logical_monitor_data->xinerama_index; } MetaLogicalMonitor * meta_x11_display_xinerama_index_to_logical_monitor (MetaX11Display *x11_display, int xinerama_index) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *logical_monitors, *l; meta_x11_display_ensure_xinerama_indices (x11_display); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaX11DisplayLogicalMonitorData *logical_monitor_data; logical_monitor_data = ensure_x11_display_logical_monitor_data (logical_monitor); if (logical_monitor_data->xinerama_index == xinerama_index) return logical_monitor; } return NULL; } void meta_x11_display_update_workspace_names (MetaX11Display *x11_display) { char **names; int n_names; int i; /* this updates names in prefs when the root window property changes, * iff the new property contents don't match what's already in prefs */ names = NULL; n_names = 0; if (!meta_prop_get_utf8_list (x11_display, x11_display->xroot, x11_display->atom__NET_DESKTOP_NAMES, &names, &n_names)) { meta_verbose ("Failed to get workspace names from root window\n"); return; } i = 0; while (i < n_names) { meta_topic (META_DEBUG_PREFS, "Setting workspace %d name to \"%s\" due to _NET_DESKTOP_NAMES change\n", i, names[i] ? names[i] : "null"); meta_prefs_change_workspace_name (i, names[i]); ++i; } g_strfreev (names); } #define _NET_WM_ORIENTATION_HORZ 0 #define _NET_WM_ORIENTATION_VERT 1 #define _NET_WM_TOPLEFT 0 #define _NET_WM_TOPRIGHT 1 #define _NET_WM_BOTTOMRIGHT 2 #define _NET_WM_BOTTOMLEFT 3 void meta_x11_display_update_workspace_layout (MetaX11Display *x11_display) { MetaWorkspaceManager *workspace_manager = x11_display->display->workspace_manager; gboolean vertical_layout = FALSE; int n_rows = -1; int n_columns = 1; MetaDisplayCorner starting_corner = META_DISPLAY_TOPLEFT; uint32_t *list; int n_items; if (workspace_manager->workspace_layout_overridden) return; list = NULL; n_items = 0; if (meta_prop_get_cardinal_list (x11_display, x11_display->xroot, x11_display->atom__NET_DESKTOP_LAYOUT, &list, &n_items)) { if (n_items == 3 || n_items == 4) { int cols, rows; switch (list[0]) { case _NET_WM_ORIENTATION_HORZ: vertical_layout = FALSE; break; case _NET_WM_ORIENTATION_VERT: vertical_layout = TRUE; break; default: meta_warning ("Someone set a weird orientation in _NET_DESKTOP_LAYOUT\n"); break; } cols = list[1]; rows = list[2]; if (rows <= 0 && cols <= 0) { meta_warning ("Columns = %d rows = %d in _NET_DESKTOP_LAYOUT makes no sense\n", rows, cols); } else { if (rows > 0) n_rows = rows; else n_rows = -1; if (cols > 0) n_columns = cols; else n_columns = -1; } if (n_items == 4) { switch (list[3]) { case _NET_WM_TOPLEFT: starting_corner = META_DISPLAY_TOPLEFT; break; case _NET_WM_TOPRIGHT: starting_corner = META_DISPLAY_TOPRIGHT; break; case _NET_WM_BOTTOMRIGHT: starting_corner = META_DISPLAY_BOTTOMRIGHT; break; case _NET_WM_BOTTOMLEFT: starting_corner = META_DISPLAY_BOTTOMLEFT; break; default: meta_warning ("Someone set a weird starting corner in _NET_DESKTOP_LAYOUT\n"); break; } } } else { meta_warning ("Someone set _NET_DESKTOP_LAYOUT to %d integers instead of 4 " "(3 is accepted for backwards compat)\n", n_items); } g_free (list); meta_workspace_manager_update_workspace_layout (workspace_manager, starting_corner, vertical_layout, n_rows, n_columns); } } static void prefs_changed_callback (MetaPreference pref, void *data) { MetaX11Display *x11_display = data; if (pref == META_PREF_WORKSPACE_NAMES) { set_workspace_names (x11_display); } } void meta_x11_display_increment_focus_sentinel (MetaX11Display *x11_display) { unsigned long data[1]; data[0] = meta_display_get_current_time (x11_display->display); XChangeProperty (x11_display->xdisplay, x11_display->xroot, x11_display->atom__MUTTER_SENTINEL, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); x11_display->sentinel_counter += 1; } void meta_x11_display_decrement_focus_sentinel (MetaX11Display *x11_display) { x11_display->sentinel_counter -= 1; if (x11_display->sentinel_counter < 0) x11_display->sentinel_counter = 0; } gboolean meta_x11_display_focus_sentinel_clear (MetaX11Display *x11_display) { return (x11_display->sentinel_counter == 0); } void meta_x11_display_set_stage_input_region (MetaX11Display *x11_display, XserverRegion region) { Display *xdisplay = x11_display->xdisplay; MetaBackend *backend = meta_get_backend (); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); Window stage_xwindow; g_return_if_fail (!meta_is_wayland_compositor ()); stage_xwindow = meta_x11_get_stage_window (stage); XFixesSetWindowShapeRegion (xdisplay, stage_xwindow, ShapeInput, 0, 0, region); /* * It's generally a good heuristic that when a crossing event is generated * because we reshape the overlay, we don't want it to affect * focus-follows-mouse focus - it's not the user doing something, it's the * environment changing under the user. */ meta_display_add_ignored_crossing_serial (x11_display->display, XNextRequest (xdisplay)); XFixesSetWindowShapeRegion (xdisplay, x11_display->composite_overlay_window, ShapeInput, 0, 0, region); } void meta_x11_display_clear_stage_input_region (MetaX11Display *x11_display) { if (x11_display->empty_region == None) { x11_display->empty_region = XFixesCreateRegion (x11_display->xdisplay, NULL, 0); } meta_x11_display_set_stage_input_region (x11_display, x11_display->empty_region); } muffin-6.4.1/src/x11/meta-x11-background.c0000664000175000017500000002044014723361714016760 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-background.c: Actor used to create a background fade effect * * Copyright 2016 Linux Mint * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Portions adapted from gnome-shell/src/shell-global.c * and meta-window-actor.c */ #include "config.h" #include #include "cogl/cogl.h" #include #include "meta/meta-x11-errors.h" #include "meta-x11-background-actor-private.h" typedef struct { MetaDisplay *display; MetaX11Display *x11_display; CoglPipeline *pipeline; float texture_width; float texture_height; cairo_region_t *visible_region; } MetaX11BackgroundPrivate; struct _MetaX11Background { ClutterActor parent_instance; MetaX11BackgroundPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaX11Background, meta_x11_background, CLUTTER_TYPE_ACTOR); static void meta_x11_background_dispose (GObject *object) { MetaX11Background *self = META_X11_BACKGROUND (object); MetaX11BackgroundPrivate *priv = self->priv; meta_x11_background_set_visible_region (self, NULL); if (priv->pipeline != NULL) { cogl_object_unref (priv->pipeline); priv->pipeline = NULL; } G_OBJECT_CLASS (meta_x11_background_parent_class)->dispose (object); } static void meta_x11_background_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MetaX11Background *self = META_X11_BACKGROUND (actor); MetaX11BackgroundPrivate *priv = self->priv; int width, height; meta_display_get_size (priv->display, &width, &height); if (min_width_p) *min_width_p = width; if (natural_width_p) *natural_width_p = width; } static void meta_x11_background_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MetaX11Background *self = META_X11_BACKGROUND (actor); MetaX11BackgroundPrivate *priv = self->priv; int width, height; meta_display_get_size (priv->display, &width, &height); if (min_height_p) *min_height_p = height; if (natural_height_p) *natural_height_p = height; } static void meta_x11_background_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { MetaX11Background *self = META_X11_BACKGROUND (actor); MetaX11BackgroundPrivate *priv = self->priv; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); guint8 opacity = clutter_actor_get_paint_opacity (actor); guint8 color_component; int width, height; meta_display_get_size (priv->display, &width, &height); color_component = (int)(0.5 + opacity); cogl_pipeline_set_color4ub (priv->pipeline, color_component, color_component, color_component, opacity); if (priv->visible_region) { int n_rectangles = cairo_region_num_rectangles (priv->visible_region); int i; for (i = 0; i < n_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (priv->visible_region, i, &rect); cogl_framebuffer_draw_textured_rectangle (framebuffer, priv->pipeline, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, rect.x / priv->texture_width, rect.y / priv->texture_height, (rect.x + rect.width) / priv->texture_width, (rect.y + rect.height) / priv->texture_height); } } else { cogl_framebuffer_draw_textured_rectangle (framebuffer, priv->pipeline, 0.0f, 0.0f, width, height, 0.0f, 0.0f, width / priv->texture_width, height / priv->texture_height); } } static gboolean meta_x11_background_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { return clutter_paint_volume_set_from_allocation (volume, actor); } static void meta_x11_background_class_init (MetaX11BackgroundClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); object_class->dispose = meta_x11_background_dispose; actor_class->get_preferred_width = meta_x11_background_get_preferred_width; actor_class->get_preferred_height = meta_x11_background_get_preferred_height; actor_class->get_paint_volume = meta_x11_background_get_paint_volume; actor_class->paint = meta_x11_background_paint; } static void meta_x11_background_init (MetaX11Background *self) { self->priv = meta_x11_background_get_instance_private (self); } ClutterActor * meta_x11_background_new (MetaDisplay *display) { MetaX11Background *self; MetaX11BackgroundPrivate *priv; self = g_object_new (META_TYPE_X11_BACKGROUND, NULL); priv = self->priv; priv->display = display; priv->x11_display = meta_display_get_x11_display (display); priv->pipeline = meta_create_texture_pipeline (NULL); return CLUTTER_ACTOR (self); } void meta_x11_background_set_layer (MetaX11Background *self, CoglTexture *texture) { MetaX11BackgroundPrivate *priv = self->priv; /* This may trigger destruction of an old texture pixmap, which, if * the underlying X pixmap is already gone has the tendency to trigger * X errors inside DRI. For safety, trap errors */ meta_x11_error_trap_push (priv->x11_display); cogl_pipeline_set_layer_texture (priv->pipeline, 0, texture); meta_x11_error_trap_pop (priv->x11_display); priv->texture_width = cogl_texture_get_width (texture); priv->texture_height = cogl_texture_get_height (texture); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } void meta_x11_background_set_layer_wrap_mode (MetaX11Background *self, CoglPipelineWrapMode wrap_mode) { MetaX11BackgroundPrivate *priv = self->priv; cogl_pipeline_set_layer_wrap_mode (priv->pipeline, 0, wrap_mode); } void meta_x11_background_set_visible_region (MetaX11Background *self, cairo_region_t *visible_region) { MetaX11BackgroundPrivate *priv; g_return_if_fail (META_IS_X11_BACKGROUND (self)); priv = self->priv; if (priv->visible_region) { cairo_region_destroy (priv->visible_region); priv->visible_region = NULL; } if (visible_region) { cairo_rectangle_int_t screen_rect = { 0 }; meta_display_get_size (priv->display, &screen_rect.width, &screen_rect.height); /* Doing the intersection here is probably unnecessary - MetaWindowGroup * should never compute a visible area that's larger than the root screen! * but it's not that expensive and adds some extra robustness. */ priv->visible_region = cairo_region_create_rectangle (&screen_rect); cairo_region_intersect (priv->visible_region, visible_region); } }muffin-6.4.1/src/x11/meta-x11-selection-private.h0000664000175000017500000000234014723361714020302 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_X11_SELECTION_H #define META_X11_SELECTION_H #include "meta/meta-selection.h" #include "x11/meta-x11-display-private.h" gboolean meta_x11_selection_handle_event (MetaX11Display *display, XEvent *event); void meta_x11_selection_init (MetaX11Display *x11_display); void meta_x11_selection_shutdown (MetaX11Display *x11_display); #endif /* META_X11_SELECTION_H */ muffin-6.4.1/src/x11/window-props.c0000664000175000017500000022126614723361714015767 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * SECTION:window-props * @short_description: #MetaWindow property handling * * A system which can inspect sets of properties of given windows * and take appropriate action given their values. * * Note that all the meta_window_reload_propert* functions require a * round trip to the server. * * The guts of this system are in meta_display_init_window_prop_hooks(). * Reading this function will give you insight into how this all fits * together. */ /* * Copyright (C) 2001, 2002, 2003 Red Hat, Inc. * Copyright (C) 2004, 2005 Elijah Newren * Copyright (C) 2009 Thomas Thurman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #define _XOPEN_SOURCE 600 /* for gethostname() */ #include "config.h" #include "x11/window-props.h" #include #include #include #include "core/frame.h" #include "core/meta-workspace-manager-private.h" #include "core/util-private.h" #include "meta/group.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11-private.h" #include "x11/window-x11.h" #include "x11/xprops.h" #ifndef HOST_NAME_MAX /* Solaris headers apparently don't define this so do so manually; #326745 */ #define HOST_NAME_MAX 255 #endif typedef void (* ReloadValueFunc) (MetaWindow *window, MetaPropValue *value, gboolean initial); typedef enum { NONE = 0, LOAD_INIT = (1 << 0), INCLUDE_OR = (1 << 1), INIT_ONLY = (1 << 2), FORCE_INIT = (1 << 3), } MetaPropHookFlags; struct _MetaWindowPropHooks { Atom property; MetaPropValueType type; ReloadValueFunc reload_func; MetaPropHookFlags flags; }; static void init_prop_value (MetaWindow *window, MetaWindowPropHooks *hooks, MetaPropValue *value); static void reload_prop_value (MetaWindow *window, MetaWindowPropHooks *hooks, MetaPropValue *value, gboolean initial); static MetaWindowPropHooks *find_hooks (MetaX11Display *x11_display, Atom property); void meta_window_reload_property_from_xwindow (MetaWindow *window, Window xwindow, Atom property, gboolean initial) { MetaPropValue value = { 0, }; MetaWindowPropHooks *hooks; hooks = find_hooks (window->display->x11_display, property); if (!hooks) return; if ((hooks->flags & INIT_ONLY) && !initial) return; init_prop_value (window, hooks, &value); meta_prop_get_values (window->display->x11_display, xwindow, &value, 1); reload_prop_value (window, hooks, &value, initial); meta_prop_free_values (&value, 1); } static void meta_window_reload_property (MetaWindow *window, Atom property, gboolean initial) { meta_window_reload_property_from_xwindow (window, window->xwindow, property, initial); } void meta_window_load_initial_properties (MetaWindow *window) { int i, j; MetaPropValue *values; int n_properties = 0; MetaX11Display *x11_display = window->display->x11_display; values = g_new0 (MetaPropValue, x11_display->n_prop_hooks); j = 0; for (i = 0; i < x11_display->n_prop_hooks; i++) { MetaWindowPropHooks *hooks = &x11_display->prop_hooks_table[i]; if (hooks->flags & LOAD_INIT) { init_prop_value (window, hooks, &values[j]); ++j; } } n_properties = j; meta_prop_get_values (window->display->x11_display, window->xwindow, values, n_properties); j = 0; for (i = 0; i < x11_display->n_prop_hooks; i++) { MetaWindowPropHooks *hooks = &x11_display->prop_hooks_table[i]; if (hooks->flags & LOAD_INIT) { /* If we didn't actually manage to load anything then we don't need * to call the reload function; this is different from a notification * where disappearance of a previously present value is significant. */ if (values[j].type != META_PROP_VALUE_INVALID || hooks->flags & FORCE_INIT) reload_prop_value (window, hooks, &values[j], TRUE); ++j; } } meta_prop_free_values (values, n_properties); g_free (values); } /* Fill in the MetaPropValue used to get the value of "property" */ static void init_prop_value (MetaWindow *window, MetaWindowPropHooks *hooks, MetaPropValue *value) { if (!hooks || hooks->type == META_PROP_VALUE_INVALID || (window->override_redirect && !(hooks->flags & INCLUDE_OR))) { value->type = META_PROP_VALUE_INVALID; value->atom = None; } else { value->type = hooks->type; value->atom = hooks->property; } } static void reload_prop_value (MetaWindow *window, MetaWindowPropHooks *hooks, MetaPropValue *value, gboolean initial) { if (!(window->override_redirect && !(hooks->flags & INCLUDE_OR))) (* hooks->reload_func) (window, value, initial); } static void reload_wm_client_machine (MetaWindow *window, MetaPropValue *value, gboolean initial) { g_free (window->wm_client_machine); window->wm_client_machine = NULL; if (value->type != META_PROP_VALUE_INVALID) window->wm_client_machine = g_strdup (value->v.str); meta_verbose ("Window has client machine \"%s\"\n", window->wm_client_machine ? window->wm_client_machine : "unset"); if (window->wm_client_machine == NULL) { window->is_remote = FALSE; } else { char hostname[HOST_NAME_MAX + 1] = ""; gethostname (hostname, HOST_NAME_MAX + 1); window->is_remote = g_strcmp0 (window->wm_client_machine, hostname) != 0; } } static void complain_about_broken_client (MetaWindow *window, MetaPropValue *value, gboolean initial) { meta_warning ("Broken client! Window %s changed client leader window or SM client ID\n", window->desc); } static void reload_net_wm_window_type (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; if (value->type != META_PROP_VALUE_INVALID) { int i; for (i = 0; i < value->v.atom_list.n_atoms; i++) { Atom atom = value->v.atom_list.atoms[i]; /* We break as soon as we find one we recognize, * supposed to prefer those near the front of the list */ if (atom == x11_display->atom__NET_WM_WINDOW_TYPE_DESKTOP || atom == x11_display->atom__NET_WM_WINDOW_TYPE_DOCK || atom == x11_display->atom__NET_WM_WINDOW_TYPE_TOOLBAR || atom == x11_display->atom__NET_WM_WINDOW_TYPE_MENU || atom == x11_display->atom__NET_WM_WINDOW_TYPE_UTILITY || atom == x11_display->atom__NET_WM_WINDOW_TYPE_SPLASH || atom == x11_display->atom__NET_WM_WINDOW_TYPE_DIALOG || atom == x11_display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU || atom == x11_display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU || atom == x11_display->atom__NET_WM_WINDOW_TYPE_TOOLTIP || atom == x11_display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION || atom == x11_display->atom__NET_WM_WINDOW_TYPE_COMBO || atom == x11_display->atom__NET_WM_WINDOW_TYPE_DND || atom == x11_display->atom__NET_WM_WINDOW_TYPE_NORMAL) { priv->type_atom = atom; break; } } } meta_window_x11_recalc_window_type (window); } static void reload_icon (MetaWindow *window, Atom atom) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; meta_icon_cache_property_changed (&priv->icon_cache, window->display->x11_display, atom); meta_window_queue(window, META_QUEUE_UPDATE_ICON); } static void reload_net_wm_icon (MetaWindow *window, MetaPropValue *value, gboolean initial) { reload_icon (window, window->display->x11_display->atom__NET_WM_ICON); } static void reload_kwm_win_icon (MetaWindow *window, MetaPropValue *value, gboolean initial) { reload_icon (window, window->display->x11_display->atom__KWM_WIN_ICON); } static void reload_icon_geometry (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { if (value->v.cardinal_list.n_cardinals != 4) { meta_verbose ("_NET_WM_ICON_GEOMETRY on %s has %d values instead of 4\n", window->desc, value->v.cardinal_list.n_cardinals); } else { MetaRectangle geometry; geometry.x = (int)value->v.cardinal_list.cardinals[0]; geometry.y = (int)value->v.cardinal_list.cardinals[1]; geometry.width = (int)value->v.cardinal_list.cardinals[2]; geometry.height = (int)value->v.cardinal_list.cardinals[3]; meta_window_set_icon_geometry (window, &geometry); } } else { meta_window_set_icon_geometry (window, NULL); } } static void meta_window_set_custom_frame_extents (MetaWindow *window, GtkBorder *extents, gboolean is_initial) { if (extents) { if (window->has_custom_frame_extents && memcmp (&window->custom_frame_extents, extents, sizeof (GtkBorder)) == 0) return; window->has_custom_frame_extents = TRUE; window->custom_frame_extents = *extents; /* If we're setting the frame extents on map, then this is telling * us to adjust our understanding of the frame rect to match what * GTK+ thinks it is. Future changes to the frame extents should * trigger a resize and send a ConfigureRequest to the application. */ if (is_initial) { meta_window_client_rect_to_frame_rect (window, &window->rect, &window->rect); meta_window_client_rect_to_frame_rect (window, &window->unconstrained_rect, &window->unconstrained_rect); } } else { if (!window->has_custom_frame_extents) return; window->has_custom_frame_extents = FALSE; memset (&window->custom_frame_extents, 0, sizeof (window->custom_frame_extents)); } meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } static void reload_gtk_frame_extents (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { if (value->v.cardinal_list.n_cardinals != 4) { meta_verbose ("_GTK_FRAME_EXTENTS on %s has %d values instead of 4\n", window->desc, value->v.cardinal_list.n_cardinals); } else { GtkBorder extents; extents.left = (int)value->v.cardinal_list.cardinals[0]; extents.right = (int)value->v.cardinal_list.cardinals[1]; extents.top = (int)value->v.cardinal_list.cardinals[2]; extents.bottom = (int)value->v.cardinal_list.cardinals[3]; meta_window_set_custom_frame_extents (window, &extents, initial); } } else { meta_window_set_custom_frame_extents (window, NULL, initial); } } static void reload_struts (MetaWindow *window, MetaPropValue *value, gboolean initial) { meta_window_update_struts (window); } static void reload_wm_window_role (MetaWindow *window, MetaPropValue *value, gboolean initial) { g_clear_pointer (&window->role, g_free); if (value->type != META_PROP_VALUE_INVALID) window->role = g_strdup (value->v.str); } static void reload_net_wm_pid (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { uint32_t cardinal = (int) value->v.cardinal; if (cardinal <= 0) meta_warning ("Application set a bogus _NET_WM_PID %u\n", cardinal); else { window->net_wm_pid = cardinal; meta_verbose ("Window has _NET_WM_PID %d\n", window->net_wm_pid); } } } static void reload_net_wm_user_time (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { uint32_t cardinal = value->v.cardinal; meta_window_set_user_time (window, cardinal); } } static void reload_net_wm_user_time_window (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { MetaWindow *prev_owner; /* Unregister old NET_WM_USER_TIME_WINDOW */ if (window->user_time_window != None) { /* See the comment to the meta_display_register_x_window call below. */ meta_x11_display_unregister_x_window (window->display->x11_display, window->user_time_window); /* Don't get events on not-managed windows */ XSelectInput (window->display->x11_display->xdisplay, window->user_time_window, NoEventMask); } /* Ensure the new user time window is not used on another MetaWindow, * and unset its user time window if that is the case. */ prev_owner = meta_x11_display_lookup_x_window (window->display->x11_display, value->v.xwindow); if (prev_owner && prev_owner->user_time_window == value->v.xwindow) { meta_x11_display_unregister_x_window (window->display->x11_display, value->v.xwindow); prev_owner->user_time_window = None; } /* Obtain the new NET_WM_USER_TIME_WINDOW and register it */ window->user_time_window = value->v.xwindow; if (window->user_time_window != None) { /* Kind of a hack; display.c:event_callback() ignores events * for unknown windows. We make window->user_time_window * known by registering it with window (despite the fact * that window->xwindow is already registered with window). * This basically means that property notifies to either the * window->user_time_window or window->xwindow will be * treated identically and will result in functions for * window being called to update it. Maybe we should ignore * any property notifies to window->user_time_window other * than atom__NET_WM_USER_TIME ones, but I just don't care * and it's not specified in the spec anyway. */ meta_x11_display_register_x_window (window->display->x11_display, &window->user_time_window, window); /* Just listen for property notify events */ XSelectInput (window->display->x11_display->xdisplay, window->user_time_window, PropertyChangeMask); /* Manually load the _NET_WM_USER_TIME field from the given window * at this time as well. If the user_time_window ever broadens in * scope, we'll probably want to load all relevant properties here. */ meta_window_reload_property_from_xwindow ( window, window->user_time_window, window->display->x11_display->atom__NET_WM_USER_TIME, initial); } } } #define MAX_TITLE_LENGTH 512 /** * set_title_text: * * Called by set_window_title() to set the value of @target to @title. * If required and @atom is set, it will update the appropriate property. * * Returns: %TRUE if a new title was set. */ static gboolean set_title_text (MetaWindow *window, gboolean previous_was_modified, const char *title, Atom atom, char **target) { gboolean modified = FALSE; if (!target) return FALSE; g_free (*target); if (!title) *target = g_strdup (""); else if (g_utf8_strlen (title, MAX_TITLE_LENGTH + 1) > MAX_TITLE_LENGTH) { *target = meta_g_utf8_strndup (title, MAX_TITLE_LENGTH); modified = TRUE; } /* if WM_CLIENT_MACHINE indicates this machine is on a remote host * lets place that hostname in the title */ else if (meta_window_is_remote (window)) { *target = g_strdup_printf (_("%s (on %s)"), title, window->wm_client_machine); modified = TRUE; } else *target = g_strdup (title); if (modified && atom != None) meta_prop_set_utf8_string_hint (window->display->x11_display, window->xwindow, atom, *target); /* Bug 330671 -- Don't forget to clear _NET_WM_VISIBLE_(ICON_)NAME */ if (!modified && previous_was_modified) { meta_x11_error_trap_push (window->display->x11_display); XDeleteProperty (window->display->x11_display->xdisplay, window->xwindow, atom); meta_x11_error_trap_pop (window->display->x11_display); } return modified; } static void set_window_title (MetaWindow *window, const char *title) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; char *new_title = NULL; gboolean modified = set_title_text (window, priv->using_net_wm_visible_name, title, window->display->x11_display->atom__NET_WM_VISIBLE_NAME, &new_title); priv->using_net_wm_visible_name = modified; meta_window_set_title (window, new_title); g_free (new_title); } static void reload_net_wm_name (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; if (value->type != META_PROP_VALUE_INVALID) { set_window_title (window, value->v.str); priv->using_net_wm_name = TRUE; meta_verbose ("Using _NET_WM_NAME for new title of %s: \"%s\"\n", window->desc, window->title); } else { set_window_title (window, NULL); priv->using_net_wm_name = FALSE; if (!initial) meta_window_reload_property (window, XA_WM_NAME, FALSE); } } static void reload_wm_name (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; if (priv->using_net_wm_name) { meta_verbose ("Ignoring WM_NAME \"%s\" as _NET_WM_NAME is set\n", value->v.str); return; } if (value->type != META_PROP_VALUE_INVALID) { set_window_title (window, value->v.str); meta_verbose ("Using WM_NAME for new title of %s: \"%s\"\n", window->desc, window->title); } else { set_window_title (window, NULL); } } static void meta_window_set_opaque_region (MetaWindow *window, cairo_region_t *region) { if (cairo_region_equal (window->opaque_region, region)) return; g_clear_pointer (&window->opaque_region, cairo_region_destroy); if (region != NULL) window->opaque_region = cairo_region_reference (region); meta_compositor_window_shape_changed (window->display->compositor, window); } static void reload_opaque_region (MetaWindow *window, MetaPropValue *value, gboolean initial) { cairo_region_t *opaque_region = NULL; if (value->type != META_PROP_VALUE_INVALID) { uint32_t *region = value->v.cardinal_list.cardinals; int nitems = value->v.cardinal_list.n_cardinals; cairo_rectangle_int_t *rects; int i, rect_index, nrects; if (nitems % 4 != 0) { meta_verbose ("_NET_WM_OPAQUE_REGION does not have a list of 4-tuples."); goto out; } /* empty region */ if (nitems == 0) goto out; nrects = nitems / 4; rects = g_new (cairo_rectangle_int_t, nrects); rect_index = 0; i = 0; while (i < nitems) { cairo_rectangle_int_t *rect = &rects[rect_index]; rect->x = region[i++]; rect->y = region[i++]; rect->width = region[i++]; rect->height = region[i++]; rect_index++; } opaque_region = cairo_region_create_rectangles (rects, nrects); g_free (rects); } out: meta_window_set_opaque_region (window, opaque_region); cairo_region_destroy (opaque_region); } static void reload_mutter_hints (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { char *new_hints = value->v.str; char *old_hints = window->mutter_hints; gboolean changed = FALSE; if (new_hints) { if (!old_hints || strcmp (new_hints, old_hints)) changed = TRUE; } else { if (old_hints) changed = TRUE; } if (changed) { g_free (old_hints); if (new_hints) window->mutter_hints = g_strdup (new_hints); else window->mutter_hints = NULL; g_object_notify (G_OBJECT (window), "mutter-hints"); } } else if (window->mutter_hints) { g_free (window->mutter_hints); window->mutter_hints = NULL; g_object_notify (G_OBJECT (window), "mutter-hints"); } } static void reload_net_wm_state (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; int i; /* We know this is only an initial window creation, * clients don't change the property. */ if (!initial) { /* no, they DON'T change the property */ meta_verbose ("Ignoring _NET_WM_STATE: we should be the one who set " "the property in the first place\n"); return; } window->shaded = FALSE; window->maximized_horizontally = FALSE; window->maximized_vertically = FALSE; window->fullscreen = FALSE; priv->wm_state_modal = FALSE; priv->wm_state_skip_taskbar = FALSE; priv->wm_state_skip_pager = FALSE; window->wm_state_above = FALSE; window->wm_state_below = FALSE; window->wm_state_demands_attention = FALSE; if (value->type == META_PROP_VALUE_INVALID) return; i = 0; while (i < value->v.atom_list.n_atoms) { if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_SHADED) window->shaded = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ) window->maximize_horizontally_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT) window->maximize_vertically_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_HIDDEN) window->minimize_after_placement = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_MODAL) priv->wm_state_modal = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_SKIP_TASKBAR) priv->wm_state_skip_taskbar = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_SKIP_PAGER) priv->wm_state_skip_pager = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_FULLSCREEN) { window->fullscreen = TRUE; g_object_notify (G_OBJECT (window), "fullscreen"); } else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_ABOVE) window->wm_state_above = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_BELOW) window->wm_state_below = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_DEMANDS_ATTENTION) window->wm_state_demands_attention = TRUE; else if (value->v.atom_list.atoms[i] == x11_display->atom__NET_WM_STATE_STICKY) window->on_all_workspaces_requested = TRUE; ++i; } meta_verbose ("Reloaded _NET_WM_STATE for %s\n", window->desc); meta_window_x11_recalc_window_type (window); meta_window_recalc_features (window); } static void reload_mwm_hints (MetaWindow *window, MetaPropValue *value, gboolean initial) { MotifWmHints *hints; gboolean old_decorated = window->decorated; window->mwm_decorated = TRUE; window->mwm_border_only = FALSE; window->mwm_has_close_func = TRUE; window->mwm_has_minimize_func = TRUE; window->mwm_has_maximize_func = TRUE; window->mwm_has_move_func = TRUE; window->mwm_has_resize_func = TRUE; if (value->type == META_PROP_VALUE_INVALID) { meta_verbose ("Window %s has no MWM hints\n", window->desc); meta_window_recalc_features (window); return; } hints = value->v.motif_hints; /* We support those MWM hints deemed non-stupid */ meta_verbose ("Window %s has MWM hints\n", window->desc); if (hints->flags & MWM_HINTS_DECORATIONS) { meta_verbose ("Window %s sets MWM_HINTS_DECORATIONS 0x%x\n", window->desc, hints->decorations); if (hints->decorations == 0) window->mwm_decorated = FALSE; /* some input methods use this */ else if (hints->decorations == MWM_DECOR_BORDER) window->mwm_border_only = TRUE; } else meta_verbose ("Decorations flag unset\n"); if (hints->flags & MWM_HINTS_FUNCTIONS) { gboolean toggle_value; meta_verbose ("Window %s sets MWM_HINTS_FUNCTIONS 0x%x\n", window->desc, hints->functions); /* If _ALL is specified, then other flags indicate what to turn off; * if ALL is not specified, flags are what to turn on. * at least, I think so */ if ((hints->functions & MWM_FUNC_ALL) == 0) { toggle_value = TRUE; meta_verbose ("Window %s disables all funcs then reenables some\n", window->desc); window->mwm_has_close_func = FALSE; window->mwm_has_minimize_func = FALSE; window->mwm_has_maximize_func = FALSE; window->mwm_has_move_func = FALSE; window->mwm_has_resize_func = FALSE; } else { meta_verbose ("Window %s enables all funcs then disables some\n", window->desc); toggle_value = FALSE; } if ((hints->functions & MWM_FUNC_CLOSE) != 0) { meta_verbose ("Window %s toggles close via MWM hints\n", window->desc); window->mwm_has_close_func = toggle_value; } if ((hints->functions & MWM_FUNC_MINIMIZE) != 0) { meta_verbose ("Window %s toggles minimize via MWM hints\n", window->desc); window->mwm_has_minimize_func = toggle_value; } if ((hints->functions & MWM_FUNC_MAXIMIZE) != 0) { meta_verbose ("Window %s toggles maximize via MWM hints\n", window->desc); window->mwm_has_maximize_func = toggle_value; } if ((hints->functions & MWM_FUNC_MOVE) != 0) { meta_verbose ("Window %s toggles move via MWM hints\n", window->desc); window->mwm_has_move_func = toggle_value; } if ((hints->functions & MWM_FUNC_RESIZE) != 0) { meta_verbose ("Window %s toggles resize via MWM hints\n", window->desc); window->mwm_has_resize_func = toggle_value; } } else meta_verbose ("Functions flag unset\n"); meta_window_recalc_features (window); /* We do all this anyhow at the end of meta_window_x11_new() */ if (!window->constructing) { if (window->decorated) meta_window_ensure_frame (window); else meta_window_destroy_frame (window); meta_window_queue (window, META_QUEUE_MOVE_RESIZE | /* because ensure/destroy frame may unmap: */ META_QUEUE_CALC_SHOWING); if (old_decorated != window->decorated) g_object_notify (G_OBJECT (window), "decorated"); } } static void reload_wm_class (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { g_autofree gchar *res_class = g_convert (value->v.class_hint.res_class, -1, "UTF-8", "LATIN1", NULL, NULL, NULL); g_autofree gchar *res_name = g_convert (value->v.class_hint.res_name, -1, "UTF-8", "LATIN1", NULL, NULL, NULL); meta_window_set_wm_class (window, res_class, res_name); } else { meta_window_set_wm_class (window, NULL, NULL); } meta_verbose ("Window %s class: '%s' name: '%s'\n", window->desc, window->res_class ? window->res_class : "none", window->res_name ? window->res_name : "none"); } static void reload_net_wm_desktop (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { window->initial_workspace_set = TRUE; window->initial_workspace = value->v.cardinal; meta_topic (META_DEBUG_PLACEMENT, "Read initial workspace prop %d for %s\n", window->initial_workspace, window->desc); } } static void reload_net_startup_id (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; guint32 timestamp = window->net_wm_user_time; MetaWorkspace *workspace = NULL; g_free (window->startup_id); if (value->type != META_PROP_VALUE_INVALID) window->startup_id = g_strdup (value->v.str); else window->startup_id = NULL; /* Update timestamp and workspace on a running window */ if (!window->constructing) { window->initial_timestamp_set = 0; window->initial_workspace_set = 0; if (meta_display_apply_startup_properties (window->display, window)) { if (window->initial_timestamp_set) timestamp = window->initial_timestamp; if (window->initial_workspace_set) workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, window->initial_workspace); meta_window_activate_with_workspace (window, timestamp, workspace); } } meta_verbose ("New _NET_STARTUP_ID \"%s\" for %s\n", window->startup_id ? window->startup_id : "unset", window->desc); } static void reload_update_counter (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { meta_window_x11_destroy_sync_request_alarm (window); window->sync_request_counter = None; if (value->v.xcounter_list.n_counters == 0) { meta_warning ("_NET_WM_SYNC_REQUEST_COUNTER is empty\n"); return; } if (value->v.xcounter_list.n_counters == 1) { window->sync_request_counter = value->v.xcounter_list.counters[0]; window->extended_sync_request_counter = FALSE; } else { window->sync_request_counter = value->v.xcounter_list.counters[1]; window->extended_sync_request_counter = TRUE; } meta_verbose ("Window has _NET_WM_SYNC_REQUEST_COUNTER 0x%lx (extended=%s)\n", window->sync_request_counter, window->extended_sync_request_counter ? "true" : "false"); if (window->extended_sync_request_counter) meta_window_x11_create_sync_request_alarm (window); } } #define FLAG_IS_ON(hints,flag) \ (((hints)->flags & (flag)) != 0) #define FLAG_IS_OFF(hints,flag) \ (((hints)->flags & (flag)) == 0) #define FLAG_TOGGLED_ON(old,new,flag) \ (FLAG_IS_OFF(old,flag) && \ FLAG_IS_ON(new,flag)) #define FLAG_TOGGLED_OFF(old,new,flag) \ (FLAG_IS_ON(old,flag) && \ FLAG_IS_OFF(new,flag)) #define FLAG_CHANGED(old,new,flag) \ (FLAG_TOGGLED_ON(old,new,flag) || FLAG_TOGGLED_OFF(old,new,flag)) static void spew_size_hints_differences (const XSizeHints *old, const XSizeHints *new) { if (FLAG_CHANGED (old, new, USPosition)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: USPosition now %s\n", FLAG_TOGGLED_ON (old, new, USPosition) ? "set" : "unset"); if (FLAG_CHANGED (old, new, USSize)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: USSize now %s\n", FLAG_TOGGLED_ON (old, new, USSize) ? "set" : "unset"); if (FLAG_CHANGED (old, new, PPosition)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PPosition now %s\n", FLAG_TOGGLED_ON (old, new, PPosition) ? "set" : "unset"); if (FLAG_CHANGED (old, new, PSize)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PSize now %s\n", FLAG_TOGGLED_ON (old, new, PSize) ? "set" : "unset"); if (FLAG_CHANGED (old, new, PMinSize)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PMinSize now %s (%d x %d -> %d x %d)\n", FLAG_TOGGLED_ON (old, new, PMinSize) ? "set" : "unset", old->min_width, old->min_height, new->min_width, new->min_height); if (FLAG_CHANGED (old, new, PMaxSize)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PMaxSize now %s (%d x %d -> %d x %d)\n", FLAG_TOGGLED_ON (old, new, PMaxSize) ? "set" : "unset", old->max_width, old->max_height, new->max_width, new->max_height); if (FLAG_CHANGED (old, new, PResizeInc)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PResizeInc now %s (width_inc %d -> %d height_inc %d -> %d)\n", FLAG_TOGGLED_ON (old, new, PResizeInc) ? "set" : "unset", old->width_inc, new->width_inc, old->height_inc, new->height_inc); if (FLAG_CHANGED (old, new, PAspect)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PAspect now %s (min %d/%d -> %d/%d max %d/%d -> %d/%d)\n", FLAG_TOGGLED_ON (old, new, PAspect) ? "set" : "unset", old->min_aspect.x, old->min_aspect.y, new->min_aspect.x, new->min_aspect.y, old->max_aspect.x, old->max_aspect.y, new->max_aspect.x, new->max_aspect.y); if (FLAG_CHANGED (old, new, PBaseSize)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PBaseSize now %s (%d x %d -> %d x %d)\n", FLAG_TOGGLED_ON (old, new, PBaseSize) ? "set" : "unset", old->base_width, old->base_height, new->base_width, new->base_height); if (FLAG_CHANGED (old, new, PWinGravity)) meta_topic (META_DEBUG_GEOMETRY, "XSizeHints: PWinGravity now %s (%d -> %d)\n", FLAG_TOGGLED_ON (old, new, PWinGravity) ? "set" : "unset", old->win_gravity, new->win_gravity); } static gboolean hints_have_changed (const XSizeHints *old, const XSizeHints *new) { /* 1. Check if the relevant values have changed if the flag is set. */ if (FLAG_TOGGLED_ON (old, new, USPosition) || (FLAG_IS_ON (new, USPosition) && (old->x != new->x || old->y != new->y))) return TRUE; if (FLAG_TOGGLED_ON (old, new, USSize) || (FLAG_IS_ON (new, USSize) && (old->width != new->width || old->height != new->height))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PPosition) || (FLAG_IS_ON (new, PPosition) && (old->x != new->x || old->y != new->y))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PSize) || (FLAG_IS_ON (new, PSize) && (old->width != new->width || old->height != new->height))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PMinSize) || (FLAG_IS_ON (new, PMinSize) && (old->min_width != new->min_width || old->min_height != new->min_height))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PMaxSize) || (FLAG_IS_ON (new, PMaxSize) && (old->max_width != new->max_width || old->max_height != new->max_height))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PResizeInc) || (FLAG_IS_ON (new, PResizeInc) && (old->width_inc != new->width_inc || old->height_inc != new->height_inc))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PAspect) || (FLAG_IS_ON (new, PAspect) && (old->min_aspect.x != new->min_aspect.x || old->min_aspect.y != new->min_aspect.y || old->max_aspect.x != new->max_aspect.x || old->max_aspect.y != new->max_aspect.y))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PBaseSize) || (FLAG_IS_ON (new, PBaseSize) && (old->base_width != new->base_width || old->base_height != new->base_height))) return TRUE; if (FLAG_TOGGLED_ON (old, new, PWinGravity) || (FLAG_IS_ON (new, PWinGravity) && (old->win_gravity != new->win_gravity))) return TRUE; /* 2. Check if the flags have been unset. */ return FLAG_TOGGLED_OFF (old, new, USPosition) || FLAG_TOGGLED_OFF (old, new, USSize) || FLAG_TOGGLED_OFF (old, new, PPosition) || FLAG_TOGGLED_OFF (old, new, PSize) || FLAG_TOGGLED_OFF (old, new, PMinSize) || FLAG_TOGGLED_OFF (old, new, PMaxSize) || FLAG_TOGGLED_OFF (old, new, PResizeInc) || FLAG_TOGGLED_OFF (old, new, PAspect) || FLAG_TOGGLED_OFF (old, new, PBaseSize) || FLAG_TOGGLED_OFF (old, new, PWinGravity); } void meta_set_normal_hints (MetaWindow *window, XSizeHints *hints) { int x, y, w, h; double minr, maxr; /* Some convenience vars */ int minw, minh, maxw, maxh; /* min/max width/height */ int basew, baseh, winc, hinc; /* base width/height, width/height increment */ /* Save the last ConfigureRequest, which we put here. * Values here set in the hints are supposed to * be ignored. */ x = window->size_hints.x; y = window->size_hints.y; w = window->size_hints.width; h = window->size_hints.height; /* as far as I can tell, value->v.size_hints.flags is just to * check whether we had old-style normal hints without gravity, * base size as returned by XGetNormalHints(), so we don't * really use it as we fixup window->size_hints to have those * fields if they're missing. */ /* * When the window is first created, NULL hints will * be passed in which will initialize all of the fields * as if flags were zero */ if (hints) window->size_hints = *hints; else window->size_hints.flags = 0; /* Put back saved ConfigureRequest. */ window->size_hints.x = x; window->size_hints.y = y; window->size_hints.width = w; window->size_hints.height = h; /* Get base size hints */ if (window->size_hints.flags & PBaseSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets base size %d x %d\n", window->desc, window->size_hints.base_width, window->size_hints.base_height); } else if (window->size_hints.flags & PMinSize) { window->size_hints.base_width = window->size_hints.min_width; window->size_hints.base_height = window->size_hints.min_height; } else { window->size_hints.base_width = 0; window->size_hints.base_height = 0; } window->size_hints.flags |= PBaseSize; /* Get min size hints */ if (window->size_hints.flags & PMinSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n", window->desc, window->size_hints.min_width, window->size_hints.min_height); } else if (window->size_hints.flags & PBaseSize) { window->size_hints.min_width = window->size_hints.base_width; window->size_hints.min_height = window->size_hints.base_height; } else { window->size_hints.min_width = 0; window->size_hints.min_height = 0; } window->size_hints.flags |= PMinSize; /* Get max size hints */ if (window->size_hints.flags & PMaxSize) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n", window->desc, window->size_hints.max_width, window->size_hints.max_height); } else { window->size_hints.max_width = G_MAXINT; window->size_hints.max_height = G_MAXINT; window->size_hints.flags |= PMaxSize; } /* Get resize increment hints */ if (window->size_hints.flags & PResizeInc) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets resize width inc: %d height inc: %d\n", window->desc, window->size_hints.width_inc, window->size_hints.height_inc); } else { window->size_hints.width_inc = 1; window->size_hints.height_inc = 1; window->size_hints.flags |= PResizeInc; } /* Get aspect ratio hints */ if (window->size_hints.flags & PAspect) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min_aspect: %d/%d max_aspect: %d/%d\n", window->desc, window->size_hints.min_aspect.x, window->size_hints.min_aspect.y, window->size_hints.max_aspect.x, window->size_hints.max_aspect.y); } else { window->size_hints.min_aspect.x = 1; window->size_hints.min_aspect.y = G_MAXINT; window->size_hints.max_aspect.x = G_MAXINT; window->size_hints.max_aspect.y = 1; window->size_hints.flags |= PAspect; } /* Get gravity hint */ if (window->size_hints.flags & PWinGravity) { meta_topic (META_DEBUG_GEOMETRY, "Window %s sets gravity %d\n", window->desc, window->size_hints.win_gravity); } else { meta_topic (META_DEBUG_GEOMETRY, "Window %s doesn't set gravity, using NW\n", window->desc); window->size_hints.win_gravity = META_GRAVITY_NORTH_WEST; window->size_hints.flags |= PWinGravity; } /*** Lots of sanity checking ***/ /* Verify all min & max hints are at least 1 pixel */ if (window->size_hints.min_width < 1) { /* someone is on crack */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min width to 0, which makes no sense\n", window->desc); window->size_hints.min_width = 1; } if (window->size_hints.max_width < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max width to 0, which makes no sense\n", window->desc); window->size_hints.max_width = 1; } if (window->size_hints.min_height < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min height to 0, which makes no sense\n", window->desc); window->size_hints.min_height = 1; } if (window->size_hints.max_height < 1) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max height to 0, which makes no sense\n", window->desc); window->size_hints.max_height = 1; } /* Verify size increment hints are at least 1 pixel */ if (window->size_hints.width_inc < 1) { /* app authors find so many ways to smoke crack */ window->size_hints.width_inc = 1; meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 width_inc to 1\n"); } if (window->size_hints.height_inc < 1) { /* another cracksmoker */ window->size_hints.height_inc = 1; meta_topic (META_DEBUG_GEOMETRY, "Corrected 0 height_inc to 1\n"); } /* divide by 0 cracksmokers; note that x & y in (min|max)_aspect are * numerator & denominator */ if (window->size_hints.min_aspect.y < 1) window->size_hints.min_aspect.y = 1; if (window->size_hints.max_aspect.y < 1) window->size_hints.max_aspect.y = 1; minw = window->size_hints.min_width; minh = window->size_hints.min_height; maxw = window->size_hints.max_width; maxh = window->size_hints.max_height; basew = window->size_hints.base_width; baseh = window->size_hints.base_height; winc = window->size_hints.width_inc; hinc = window->size_hints.height_inc; /* Make sure min and max size hints are consistent with the base + increment * size hints. If they're not, it's not a real big deal, but it means the * effective min and max size are more restrictive than the application * specified values. */ if ((minw - basew) % winc != 0) { /* Take advantage of integer division throwing away the remainder... */ window->size_hints.min_width = basew + ((minw - basew)/winc + 1)*winc; meta_topic (META_DEBUG_GEOMETRY, "Window %s has width_inc (%d) that does not evenly divide " "min_width - base_width (%d - %d); thus effective " "min_width is really %d\n", window->desc, winc, minw, basew, window->size_hints.min_width); minw = window->size_hints.min_width; } if (maxw != G_MAXINT && (maxw - basew) % winc != 0) { /* Take advantage of integer division throwing away the remainder... */ window->size_hints.max_width = basew + ((maxw - basew)/winc)*winc; meta_topic (META_DEBUG_GEOMETRY, "Window %s has width_inc (%d) that does not evenly divide " "max_width - base_width (%d - %d); thus effective " "max_width is really %d\n", window->desc, winc, maxw, basew, window->size_hints.max_width); maxw = window->size_hints.max_width; } if ((minh - baseh) % hinc != 0) { /* Take advantage of integer division throwing away the remainder... */ window->size_hints.min_height = baseh + ((minh - baseh)/hinc + 1)*hinc; meta_topic (META_DEBUG_GEOMETRY, "Window %s has height_inc (%d) that does not evenly divide " "min_height - base_height (%d - %d); thus effective " "min_height is really %d\n", window->desc, hinc, minh, baseh, window->size_hints.min_height); minh = window->size_hints.min_height; } if (maxh != G_MAXINT && (maxh - baseh) % hinc != 0) { /* Take advantage of integer division throwing away the remainder... */ window->size_hints.max_height = baseh + ((maxh - baseh)/hinc)*hinc; meta_topic (META_DEBUG_GEOMETRY, "Window %s has height_inc (%d) that does not evenly divide " "max_height - base_height (%d - %d); thus effective " "max_height is really %d\n", window->desc, hinc, maxh, baseh, window->size_hints.max_height); maxh = window->size_hints.max_height; } /* make sure maximum size hints are compatible with minimum size hints; min * size hints take precedence. */ if (window->size_hints.max_width < window->size_hints.min_width) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max width %d less than min width %d, " "disabling resize\n", window->desc, window->size_hints.max_width, window->size_hints.min_width); maxw = window->size_hints.max_width = window->size_hints.min_width; } if (window->size_hints.max_height < window->size_hints.min_height) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max height %d less than min height %d, " "disabling resize\n", window->desc, window->size_hints.max_height, window->size_hints.min_height); maxh = window->size_hints.max_height = window->size_hints.min_height; } /* Make sure the aspect ratio hints are sane. */ minr = window->size_hints.min_aspect.x / (double)window->size_hints.min_aspect.y; maxr = window->size_hints.max_aspect.x / (double)window->size_hints.max_aspect.y; if (minr > maxr) { /* another cracksmoker; not even minimally (self) consistent */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min aspect ratio larger than max aspect " "ratio; disabling aspect ratio constraints.\n", window->desc); window->size_hints.min_aspect.x = 1; window->size_hints.min_aspect.y = G_MAXINT; window->size_hints.max_aspect.x = G_MAXINT; window->size_hints.max_aspect.y = 1; } else /* check consistency of aspect ratio hints with other hints */ { if (minh > 0 && minr > (maxw / (double)minh)) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min aspect ratio larger than largest " "aspect ratio possible given min/max size constraints; " "disabling min aspect ratio constraint.\n", window->desc); window->size_hints.min_aspect.x = 1; window->size_hints.min_aspect.y = G_MAXINT; } if (maxr < (minw / (double)maxh)) { /* another cracksmoker */ meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max aspect ratio smaller than smallest " "aspect ratio possible given min/max size constraints; " "disabling max aspect ratio constraint.\n", window->desc); window->size_hints.max_aspect.x = G_MAXINT; window->size_hints.max_aspect.y = 1; } /* FIXME: Would be nice to check that aspect ratios are * consistent with base and size increment constraints. */ } } static void reload_normal_hints (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { XSizeHints old_hints; gboolean hints_have_differences; meta_topic (META_DEBUG_GEOMETRY, "Updating WM_NORMAL_HINTS for %s\n", window->desc); old_hints = window->size_hints; meta_set_normal_hints (window, value->v.size_hints.hints); hints_have_differences = hints_have_changed (&old_hints, &window->size_hints); if (hints_have_differences) { spew_size_hints_differences (&old_hints, &window->size_hints); meta_window_recalc_features (window); if (!initial) meta_window_queue (window, META_QUEUE_MOVE_RESIZE); } } } static void reload_wm_protocols (MetaWindow *window, MetaPropValue *value, gboolean initial) { int i; meta_window_x11_set_wm_take_focus (window, FALSE); meta_window_x11_set_wm_ping (window, FALSE); meta_window_x11_set_wm_delete_window (window, FALSE); if (value->type == META_PROP_VALUE_INVALID) return; i = 0; while (i < value->v.atom_list.n_atoms) { if (value->v.atom_list.atoms[i] == window->display->x11_display->atom_WM_TAKE_FOCUS) meta_window_x11_set_wm_take_focus (window, TRUE); else if (value->v.atom_list.atoms[i] == window->display->x11_display->atom_WM_DELETE_WINDOW) meta_window_x11_set_wm_delete_window (window, TRUE); else if (value->v.atom_list.atoms[i] == window->display->x11_display->atom__NET_WM_PING) meta_window_x11_set_wm_ping (window, TRUE); ++i; } meta_verbose ("New _NET_STARTUP_ID \"%s\" for %s\n", window->startup_id ? window->startup_id : "unset", window->desc); } static void reload_wm_hints (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = window_x11->priv; Window old_group_leader; gboolean urgent; old_group_leader = window->xgroup_leader; /* Fill in defaults */ window->input = TRUE; window->initially_iconic = FALSE; window->xgroup_leader = None; priv->wm_hints_pixmap = None; priv->wm_hints_mask = None; urgent = FALSE; if (value->type != META_PROP_VALUE_INVALID) { const XWMHints *hints = value->v.wm_hints; if (hints->flags & InputHint) window->input = hints->input; if (hints->flags & StateHint) window->initially_iconic = (hints->initial_state == IconicState); if (hints->flags & WindowGroupHint) window->xgroup_leader = hints->window_group; if (hints->flags & IconPixmapHint) priv->wm_hints_pixmap = hints->icon_pixmap; if (hints->flags & IconMaskHint) priv->wm_hints_mask = hints->icon_mask; if (hints->flags & XUrgencyHint) urgent = TRUE; meta_verbose ("Read WM_HINTS input: %d iconic: %d group leader: 0x%lx pixmap: 0x%lx mask: 0x%lx\n", window->input, window->initially_iconic, window->xgroup_leader, priv->wm_hints_pixmap, priv->wm_hints_mask); } if (window->xgroup_leader != old_group_leader) { meta_verbose ("Window %s changed its group leader to 0x%lx\n", window->desc, window->xgroup_leader); meta_window_group_leader_changed (window); } meta_window_set_urgent (window, urgent); meta_icon_cache_property_changed (&priv->icon_cache, window->display->x11_display, XA_WM_HINTS); meta_window_queue (window, META_QUEUE_UPDATE_ICON | META_QUEUE_MOVE_RESIZE); } static gboolean check_xtransient_for_loop (MetaWindow *window, MetaWindow *parent) { while (parent) { if (parent == window) return TRUE; parent = meta_x11_display_lookup_x_window (parent->display->x11_display, parent->xtransient_for); } return FALSE; } static void reload_transient_for (MetaWindow *window, MetaPropValue *value, gboolean initial) { MetaWindow *parent = NULL; Window transient_for; if (value->type != META_PROP_VALUE_INVALID) { transient_for = value->v.xwindow; parent = meta_x11_display_lookup_x_window (window->display->x11_display, transient_for); if (!parent) { meta_topic (META_DEBUG_WINDOW_STATE, "Invalid WM_TRANSIENT_FOR window 0x%lx specified for %s.\n", transient_for, window->desc); transient_for = None; } else if (parent->override_redirect) { const gchar *window_kind = window->override_redirect ? "override-redirect" : "top-level"; if (parent->xtransient_for != None) { /* We don't have to go through the parents, as per this code it is * not possible that a window has the WM_TRANSIENT_FOR set to an * override-redirect window anyways */ meta_warning ("WM_TRANSIENT_FOR window %s for %s window %s is an " "override-redirect window and this is not correct " "according to the standard, so we'll fallback to " "the first non-override-redirect window 0x%lx.\n", parent->desc, window->desc, window_kind, parent->xtransient_for); transient_for = parent->xtransient_for; parent = meta_x11_display_lookup_x_window (parent->display->x11_display, transient_for); } else { meta_warning ("WM_TRANSIENT_FOR window %s for %s window %s is an " "override-redirect window and this is not correct " "according to the standard, so we'll fallback to " "the root window.\n", parent->desc, window_kind, window->desc); transient_for = parent->display->x11_display->xroot; parent = NULL; } } /* Make sure there is not a loop */ if (check_xtransient_for_loop (window, parent)) { meta_warning ("WM_TRANSIENT_FOR window 0x%lx for %s would create a " "loop.\n", transient_for, window->desc); transient_for = None; } } else transient_for = None; if (transient_for == window->xtransient_for) return; window->xtransient_for = transient_for; if (window->xtransient_for != None) meta_verbose ("Window %s transient for 0x%lx\n", window->desc, window->xtransient_for); else meta_verbose ("Window %s is not transient\n", window->desc); if (window->xtransient_for == None || window->xtransient_for == window->display->x11_display->xroot) meta_window_set_transient_for (window, NULL); else { meta_window_set_transient_for (window, parent); } } static void reload_gtk_theme_variant (MetaWindow *window, MetaPropValue *value, gboolean initial) { char *requested_variant = NULL; char *current_variant = window->gtk_theme_variant; if (value->type != META_PROP_VALUE_INVALID) { requested_variant = value->v.str; meta_verbose ("Requested \"%s\" theme variant for window %s.\n", requested_variant, window->desc); } if (g_strcmp0 (requested_variant, current_variant) != 0) { g_free (current_variant); window->gtk_theme_variant = g_strdup (requested_variant); if (window->frame) meta_frame_update_style (window->frame); } } static void reload_bypass_compositor (MetaWindow *window, MetaPropValue *value, gboolean initial) { int requested_value = 0; int current_value = window->bypass_compositor; if (value->type != META_PROP_VALUE_INVALID) requested_value = (int) value->v.cardinal; if (requested_value == current_value) return; if (requested_value == _NET_WM_BYPASS_COMPOSITOR_HINT_ON) meta_verbose ("Request to bypass compositor for window %s.\n", window->desc); else if (requested_value == _NET_WM_BYPASS_COMPOSITOR_HINT_OFF) meta_verbose ("Request to don't bypass compositor for window %s.\n", window->desc); else if (requested_value != _NET_WM_BYPASS_COMPOSITOR_HINT_AUTO) return; window->bypass_compositor = requested_value; } static void reload_window_opacity (MetaWindow *window, MetaPropValue *value, gboolean initial) { guint8 opacity = 0xFF; if (value->type != META_PROP_VALUE_INVALID) opacity = (guint8)((gfloat)value->v.cardinal * 255.0 / ((gfloat)0xffffffff)); meta_window_set_opacity (window, opacity); } static void reload_theme_icon_name (MetaWindow *window, MetaPropValue *value, gboolean initial) { free (window->theme_icon_name); window->theme_icon_name = NULL; if (value->type != META_PROP_VALUE_INVALID) window->theme_icon_name = g_strdup (value->v.str); meta_verbose ("Window theme icon name or path is \"%s\"\n", window->theme_icon_name ? window->theme_icon_name : "unset"); } static void reload_progress (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { if (window->progress != value->v.cardinal) { window->progress = value->v.cardinal; g_object_notify ((GObject*) window, "progress"); meta_topic (META_DEBUG_WINDOW_STATE, "Read XAppGtkWindow progress prop %u for %s\n", window->progress, window->desc); } } else { if (window->progress != 0) { window->progress = 0; g_object_notify ((GObject*) window, "progress"); meta_topic (META_DEBUG_WINDOW_STATE, "Read XAppGtkWindow progress prop %u for %s\n", window->progress, window->desc); } } } static void reload_progress_pulse (MetaWindow *window, MetaPropValue *value, gboolean initial) { if (value->type != META_PROP_VALUE_INVALID) { gboolean new_val = value->v.cardinal == 1; if (window->progress_pulse != new_val) { window->progress_pulse = new_val; g_object_notify ((GObject*) window, "progress-pulse"); meta_topic (META_DEBUG_WINDOW_STATE, "Read XAppGtkWindow progress-pulse prop %s for %s\n", window->progress_pulse ? "TRUE" : "FALSE", window->desc); } } else { if (window->progress_pulse) { window->progress_pulse = FALSE; g_object_notify ((GObject*) window, "progress-pulse"); meta_topic (META_DEBUG_WINDOW_STATE, "Read XAppGtkWindow progress-pulse prop %s for %s\n", window->progress_pulse ? "TRUE" : "FALSE", window->desc); } } } #define RELOAD_STRING(var_name, propname) \ static void \ reload_ ## var_name (MetaWindow *window, \ MetaPropValue *value, \ gboolean initial) \ { \ g_free (window->var_name); \ \ if (value->type != META_PROP_VALUE_INVALID) \ window->var_name = g_strdup (value->v.str); \ else \ window->var_name = NULL; \ \ g_object_notify (G_OBJECT (window), propname); \ } RELOAD_STRING (gtk_unique_bus_name, "gtk-unique-bus-name") RELOAD_STRING (gtk_application_id, "gtk-application-id") RELOAD_STRING (gtk_application_object_path, "gtk-application-object-path") RELOAD_STRING (gtk_window_object_path, "gtk-window-object-path") RELOAD_STRING (gtk_app_menu_object_path, "gtk-app-menu-object-path") RELOAD_STRING (gtk_menubar_object_path, "gtk-menubar-object-path") #undef RELOAD_STRING /** * meta_x11_display_init_window_prop_hooks: * @x11_display: The #MetaDX11isplay * * Initialises the property hooks system. Each row in the table named "hooks" * represents an action to take when a property is found on a newly-created * window, or when a property changes its value. * * The first column shows which atom the row concerns. * The second gives the type of the property data. The property will be * queried for its new value, unless the type is given as * META_PROP_VALUE_INVALID, in which case nothing will be queried. * The third column gives the name of a callback which gets called with the * new value. (If the new value was not retrieved because the second column * was META_PROP_VALUE_INVALID, the callback still gets called anyway.) * This value may be NULL, in which case no callback will be called. */ void meta_x11_display_init_window_prop_hooks (MetaX11Display *x11_display) { /* The ordering here is significant for the properties we load * initially: they are roughly ordered in the order we want them to * be gotten. We want to get window name and class first so we can * use them in error messages and such. However, name is modified * depending on wm_client_machine, so push it slightly sooner. * * For override-redirect windows, we pay attention to: * * - properties that identify the window: useful for debugging * purposes. * - NET_WM_WINDOW_TYPE: can be used to do appropriate handling * for different types of override-redirect windows. */ MetaWindowPropHooks hooks[] = { { x11_display->atom_WM_CLIENT_MACHINE, META_PROP_VALUE_STRING, reload_wm_client_machine, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_NAME, META_PROP_VALUE_UTF8, reload_net_wm_name, LOAD_INIT | INCLUDE_OR }, { XA_WM_CLASS, META_PROP_VALUE_CLASS_HINT, reload_wm_class, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_PID, META_PROP_VALUE_CARDINAL, reload_net_wm_pid, LOAD_INIT | INCLUDE_OR }, { XA_WM_NAME, META_PROP_VALUE_TEXT_PROPERTY, reload_wm_name, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__MUTTER_HINTS, META_PROP_VALUE_TEXT_PROPERTY, reload_mutter_hints, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_OPAQUE_REGION, META_PROP_VALUE_CARDINAL_LIST, reload_opaque_region, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_DESKTOP, META_PROP_VALUE_CARDINAL, reload_net_wm_desktop, LOAD_INIT | INIT_ONLY }, { x11_display->atom__NET_STARTUP_ID, META_PROP_VALUE_UTF8, reload_net_startup_id, LOAD_INIT }, { x11_display->atom__NET_WM_SYNC_REQUEST_COUNTER, META_PROP_VALUE_SYNC_COUNTER_LIST, reload_update_counter, LOAD_INIT | INCLUDE_OR }, { XA_WM_NORMAL_HINTS, META_PROP_VALUE_SIZE_HINTS, reload_normal_hints, LOAD_INIT }, { x11_display->atom_WM_PROTOCOLS, META_PROP_VALUE_ATOM_LIST, reload_wm_protocols, LOAD_INIT }, { XA_WM_HINTS, META_PROP_VALUE_WM_HINTS, reload_wm_hints, LOAD_INIT }, { x11_display->atom__NET_WM_USER_TIME, META_PROP_VALUE_CARDINAL, reload_net_wm_user_time, LOAD_INIT }, { x11_display->atom__NET_WM_STATE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_state, LOAD_INIT | INIT_ONLY }, { x11_display->atom__MOTIF_WM_HINTS, META_PROP_VALUE_MOTIF_HINTS, reload_mwm_hints, LOAD_INIT }, { XA_WM_TRANSIENT_FOR, META_PROP_VALUE_WINDOW, reload_transient_for, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__GTK_THEME_VARIANT, META_PROP_VALUE_UTF8, reload_gtk_theme_variant, LOAD_INIT }, { x11_display->atom__GTK_APPLICATION_ID, META_PROP_VALUE_UTF8, reload_gtk_application_id, LOAD_INIT }, { x11_display->atom__GTK_UNIQUE_BUS_NAME, META_PROP_VALUE_UTF8, reload_gtk_unique_bus_name, LOAD_INIT }, { x11_display->atom__GTK_APPLICATION_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_application_object_path, LOAD_INIT }, { x11_display->atom__GTK_WINDOW_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_window_object_path, LOAD_INIT }, { x11_display->atom__GTK_APP_MENU_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_app_menu_object_path, LOAD_INIT }, { x11_display->atom__GTK_MENUBAR_OBJECT_PATH, META_PROP_VALUE_UTF8, reload_gtk_menubar_object_path, LOAD_INIT }, { x11_display->atom__GTK_FRAME_EXTENTS, META_PROP_VALUE_CARDINAL_LIST,reload_gtk_frame_extents, LOAD_INIT }, { x11_display->atom__NET_WM_USER_TIME_WINDOW, META_PROP_VALUE_WINDOW, reload_net_wm_user_time_window, LOAD_INIT }, { x11_display->atom__NET_WM_ICON, META_PROP_VALUE_INVALID, reload_net_wm_icon, NONE }, { x11_display->atom__KWM_WIN_ICON, META_PROP_VALUE_INVALID, reload_kwm_win_icon, NONE }, { x11_display->atom__NET_WM_ICON_GEOMETRY, META_PROP_VALUE_CARDINAL_LIST, reload_icon_geometry, LOAD_INIT }, { x11_display->atom_WM_CLIENT_LEADER, META_PROP_VALUE_INVALID, complain_about_broken_client, NONE }, { x11_display->atom_SM_CLIENT_ID, META_PROP_VALUE_INVALID, complain_about_broken_client, NONE }, { x11_display->atom_WM_WINDOW_ROLE, META_PROP_VALUE_STRING, reload_wm_window_role, LOAD_INIT | FORCE_INIT }, { x11_display->atom__NET_WM_WINDOW_TYPE, META_PROP_VALUE_ATOM_LIST, reload_net_wm_window_type, LOAD_INIT | INCLUDE_OR | FORCE_INIT }, { x11_display->atom__NET_WM_STRUT, META_PROP_VALUE_INVALID, reload_struts, NONE }, { x11_display->atom__NET_WM_STRUT_PARTIAL, META_PROP_VALUE_INVALID, reload_struts, NONE }, { x11_display->atom__NET_WM_BYPASS_COMPOSITOR, META_PROP_VALUE_CARDINAL, reload_bypass_compositor, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_WINDOW_OPACITY, META_PROP_VALUE_CARDINAL, reload_window_opacity, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_XAPP_ICON_NAME, META_PROP_VALUE_UTF8, reload_theme_icon_name, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_XAPP_PROGRESS, META_PROP_VALUE_CARDINAL, reload_progress, LOAD_INIT | INCLUDE_OR }, { x11_display->atom__NET_WM_XAPP_PROGRESS_PULSE, META_PROP_VALUE_CARDINAL, reload_progress_pulse, LOAD_INIT | INCLUDE_OR }, { 0 }, }; MetaWindowPropHooks *table; MetaWindowPropHooks *cursor; table = g_memdup2 (hooks, sizeof (hooks)), cursor = table; g_assert (x11_display->prop_hooks == NULL); x11_display->prop_hooks_table = (gpointer) table; x11_display->prop_hooks = g_hash_table_new (NULL, NULL); while (cursor->property) { /* Doing initial loading doesn't make sense if we just want notification */ g_assert (!((cursor->flags & LOAD_INIT) && cursor->type == META_PROP_VALUE_INVALID)); /* Forcing initialization doesn't make sense if not loading initially */ g_assert ((cursor->flags & LOAD_INIT) || !(cursor->flags & FORCE_INIT)); /* Atoms are safe to use with GINT_TO_POINTER because it's safe with * anything 32 bits or less, and atoms are 32 bits with the top three * bits clear. (Scheifler & Gettys, 2e, p372) */ g_hash_table_insert (x11_display->prop_hooks, GINT_TO_POINTER (cursor->property), cursor); cursor++; } x11_display->n_prop_hooks = cursor - table; } void meta_x11_display_free_window_prop_hooks (MetaX11Display *x11_display) { g_hash_table_unref (x11_display->prop_hooks); x11_display->prop_hooks = NULL; g_free (x11_display->prop_hooks_table); x11_display->prop_hooks_table = NULL; } static MetaWindowPropHooks * find_hooks (MetaX11Display *x11_display, Atom property) { return g_hash_table_lookup (x11_display->prop_hooks, GINT_TO_POINTER (property)); } muffin-6.4.1/src/x11/meta-x11-background-actor-private.h0000664000175000017500000000124114723361714021541 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_X11_BACKGROUND_ACTOR_PRIVATE_H #define META_X11_BACKGROUND_ACTOR_PRIVATE_H #include #include #include #include "meta-x11-background.h" void meta_x11_background_actor_set_visible_region (MetaX11BackgroundActor *self, cairo_region_t *visible_region); void meta_x11_background_actor_update (MetaDisplay *display); void meta_x11_background_actor_screen_size_changed (MetaDisplay *display); #endif /* META_X11_BACKGROUND_ACTOR_PRIVATE_H */ muffin-6.4.1/src/x11/xprops.h0000664000175000017500000001466414723361714014661 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X property convenience routines */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_XPROPS_H #define META_XPROPS_H #include #include #include "meta/display.h" /* Copied from Lesstif by way of GTK. Rudimentary docs can be * found in some Motif reference guides online. */ typedef struct { uint32_t flags; uint32_t functions; uint32_t decorations; uint32_t input_mode; uint32_t status; } MotifWmHints, MwmHints; #define MWM_HINTS_FUNCTIONS (1L << 0) #define MWM_HINTS_DECORATIONS (1L << 1) #define MWM_HINTS_INPUT_MODE (1L << 2) #define MWM_HINTS_STATUS (1L << 3) #define MWM_FUNC_ALL (1L << 0) #define MWM_FUNC_RESIZE (1L << 1) #define MWM_FUNC_MOVE (1L << 2) #define MWM_FUNC_MINIMIZE (1L << 3) #define MWM_FUNC_MAXIMIZE (1L << 4) #define MWM_FUNC_CLOSE (1L << 5) #define MWM_DECOR_ALL (1L << 0) #define MWM_DECOR_BORDER (1L << 1) #define MWM_DECOR_RESIZEH (1L << 2) #define MWM_DECOR_TITLE (1L << 3) #define MWM_DECOR_MENU (1L << 4) #define MWM_DECOR_MINIMIZE (1L << 5) #define MWM_DECOR_MAXIMIZE (1L << 6) #define MWM_INPUT_MODELESS 0 #define MWM_INPUT_PRIMARY_APPLICATION_MODAL 1 #define MWM_INPUT_SYSTEM_MODAL 2 #define MWM_INPUT_FULL_APPLICATION_MODAL 3 #define MWM_INPUT_APPLICATION_MODAL MWM_INPUT_PRIMARY_APPLICATION_MODAL #define MWM_TEAROFF_WINDOW (1L<<0) /* These all return the memory from Xlib, so require an XFree() * when they return TRUE. They return TRUE on success. */ gboolean meta_prop_get_motif_hints (MetaX11Display *x11_display, Window xwindow, Atom xatom, MotifWmHints **hints_p); gboolean meta_prop_get_cardinal_list (MetaX11Display *x11_display, Window xwindow, Atom xatom, uint32_t **cardinals_p, int *n_cardinals_p); gboolean meta_prop_get_latin1_string (MetaX11Display *x11_display, Window xwindow, Atom xatom, char **str_p); gboolean meta_prop_get_utf8_list (MetaX11Display *x11_display, Window xwindow, Atom xatom, char ***str_p, int *n_str_p); void meta_prop_set_utf8_string_hint (MetaX11Display *x11_display, Window xwindow, Atom atom, const char *val); gboolean meta_prop_get_window (MetaX11Display *x11_display, Window xwindow, Atom xatom, Window *window_p); gboolean meta_prop_get_cardinal (MetaX11Display *x11_display, Window xwindow, Atom xatom, uint32_t *cardinal_p); gboolean meta_prop_get_cardinal_with_atom_type (MetaX11Display *x11_display, Window xwindow, Atom xatom, Atom prop_type, uint32_t *cardinal_p); typedef enum { META_PROP_VALUE_INVALID, META_PROP_VALUE_UTF8, META_PROP_VALUE_STRING, META_PROP_VALUE_STRING_AS_UTF8, META_PROP_VALUE_MOTIF_HINTS, META_PROP_VALUE_CARDINAL, META_PROP_VALUE_WINDOW, META_PROP_VALUE_CARDINAL_LIST, META_PROP_VALUE_UTF8_LIST, META_PROP_VALUE_ATOM_LIST, META_PROP_VALUE_TEXT_PROPERTY, /* comes back as UTF-8 string */ META_PROP_VALUE_WM_HINTS, META_PROP_VALUE_CLASS_HINT, META_PROP_VALUE_SIZE_HINTS, META_PROP_VALUE_SYNC_COUNTER, /* comes back as CARDINAL */ META_PROP_VALUE_SYNC_COUNTER_LIST /* comes back as CARDINAL */ } MetaPropValueType; /* used to request/return/store property values */ typedef struct { MetaPropValueType type; Atom atom; Atom required_type; /* autofilled if None */ union { char *str; MotifWmHints *motif_hints; Window xwindow; uint32_t cardinal; XWMHints *wm_hints; XClassHint class_hint; XSyncCounter xcounter; struct { uint32_t *counters; int n_counters; } xcounter_list; struct { XSizeHints *hints; unsigned long flags; } size_hints; struct { uint32_t *cardinals; int n_cardinals; } cardinal_list; struct { char **strings; int n_strings; } string_list; struct { uint32_t *atoms; int n_atoms; } atom_list; } v; } MetaPropValue; /* Each value has type and atom initialized. If there's an error, * or property is unset, type comes back as INVALID; * else type comes back as it originated, and the data * is filled in. */ void meta_prop_get_values (MetaX11Display *x11_display, Window xwindow, MetaPropValue *values, int n_values); void meta_prop_free_values (MetaPropValue *values, int n_values); #endif muffin-6.4.1/src/x11/group.c0000664000175000017500000002053714723361714014451 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2002 Red Hat Inc. * Copyright (C) 2003 Rob Adams * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:group * @title: MetaGroup * @short_description: Mutter window groups * */ #include "config.h" #include "x11/group-private.h" #include #include "core/window-private.h" #include "meta/util.h" #include "meta/window.h" #include "x11/group-props.h" #include "x11/meta-x11-display-private.h" static MetaGroup* meta_group_new (MetaX11Display *x11_display, Window group_leader) { g_autofree MetaGroup *group = NULL; #define N_INITIAL_PROPS 3 Atom initial_props[N_INITIAL_PROPS]; int i; g_assert (N_INITIAL_PROPS == (int) G_N_ELEMENTS (initial_props)); group = g_new0 (MetaGroup, 1); group->x11_display = x11_display; group->windows = NULL; group->group_leader = group_leader; group->refcount = 1; /* owned by caller, hash table has only weak ref */ xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay); xcb_generic_error_t *e; g_autofree xcb_get_window_attributes_reply_t *attrs = xcb_get_window_attributes_reply (xcb_conn, xcb_get_window_attributes (xcb_conn, group_leader), &e); if (e) return NULL; const uint32_t events[] = { attrs->your_event_mask | XCB_EVENT_MASK_PROPERTY_CHANGE }; xcb_change_window_attributes (xcb_conn, group_leader, XCB_CW_EVENT_MASK, events); if (x11_display->groups_by_leader == NULL) x11_display->groups_by_leader = g_hash_table_new (meta_unsigned_long_hash, meta_unsigned_long_equal); g_assert (g_hash_table_lookup (x11_display->groups_by_leader, &group_leader) == NULL); g_hash_table_insert (x11_display->groups_by_leader, &group->group_leader, group); /* Fill these in the order we want them to be gotten */ i = 0; initial_props[i++] = x11_display->atom_WM_CLIENT_MACHINE; initial_props[i++] = x11_display->atom__NET_WM_PID; initial_props[i++] = x11_display->atom__NET_STARTUP_ID; g_assert (N_INITIAL_PROPS == i); meta_group_reload_properties (group, initial_props, N_INITIAL_PROPS); meta_topic (META_DEBUG_GROUPS, "Created new group with leader 0x%lx\n", group->group_leader); return g_steal_pointer (&group); } static void meta_group_unref (MetaGroup *group) { g_return_if_fail (group->refcount > 0); group->refcount -= 1; if (group->refcount == 0) { meta_topic (META_DEBUG_GROUPS, "Destroying group with leader 0x%lx\n", group->group_leader); g_assert (group->x11_display->groups_by_leader != NULL); g_hash_table_remove (group->x11_display->groups_by_leader, &group->group_leader); /* mop up hash table, this is how it gets freed on display close */ if (g_hash_table_size (group->x11_display->groups_by_leader) == 0) { g_hash_table_destroy (group->x11_display->groups_by_leader); group->x11_display->groups_by_leader = NULL; } g_free (group->wm_client_machine); g_free (group->startup_id); g_free (group); } } /** * meta_window_get_group: (skip) * @window: a #MetaWindow * */ MetaGroup* meta_window_get_group (MetaWindow *window) { if (window->unmanaging) return NULL; return window->group; } void meta_window_compute_group (MetaWindow* window) { MetaGroup *group; MetaWindow *ancestor; MetaX11Display *x11_display = window->display->x11_display; /* use window->xwindow if no window->xgroup_leader */ group = NULL; /* Determine the ancestor of the window; its group setting will override the * normal grouping rules; see bug 328211. */ ancestor = meta_window_find_root_ancestor (window); if (x11_display->groups_by_leader) { if (ancestor != window) group = ancestor->group; else if (window->xgroup_leader != None) group = g_hash_table_lookup (x11_display->groups_by_leader, &window->xgroup_leader); else group = g_hash_table_lookup (x11_display->groups_by_leader, &window->xwindow); } if (group != NULL) { window->group = group; group->refcount += 1; } else { if (ancestor != window && ancestor->xgroup_leader != None) group = meta_group_new (x11_display, ancestor->xgroup_leader); else if (window->xgroup_leader != None) group = meta_group_new (x11_display, window->xgroup_leader); else group = meta_group_new (x11_display, window->xwindow); window->group = group; } if (!window->group) return; window->group->windows = g_slist_prepend (window->group->windows, window); meta_topic (META_DEBUG_GROUPS, "Adding %s to group with leader 0x%lx\n", window->desc, group->group_leader); } static void remove_window_from_group (MetaWindow *window) { if (window->group != NULL) { meta_topic (META_DEBUG_GROUPS, "Removing %s from group with leader 0x%lx\n", window->desc, window->group->group_leader); window->group->windows = g_slist_remove (window->group->windows, window); meta_group_unref (window->group); window->group = NULL; } } void meta_window_group_leader_changed (MetaWindow *window) { remove_window_from_group (window); meta_window_compute_group (window); } void meta_window_shutdown_group (MetaWindow *window) { remove_window_from_group (window); } /** * meta_x11_display_lookup_group: (skip) * @x11_display: a #MetaX11Display * @group_leader: a X window * */ MetaGroup * meta_x11_display_lookup_group (MetaX11Display *x11_display, Window group_leader) { MetaGroup *group; group = NULL; if (x11_display->groups_by_leader) group = g_hash_table_lookup (x11_display->groups_by_leader, &group_leader); return group; } /** * meta_group_list_windows: * @group: A #MetaGroup * * Returns: (transfer container) (element-type Meta.Window): List of windows */ GSList* meta_group_list_windows (MetaGroup *group) { return g_slist_copy (group->windows); } void meta_group_update_layers (MetaGroup *group) { GSList *tmp; GSList *frozen_stacks; if (group->windows == NULL) return; frozen_stacks = NULL; tmp = group->windows; while (tmp != NULL) { MetaWindow *window = tmp->data; /* we end up freezing the same stack a lot of times, * but doesn't hurt anything. have to handle * groups that span 2 screens. */ meta_stack_freeze (window->display->stack); frozen_stacks = g_slist_prepend (frozen_stacks, window->display->stack); meta_stack_update_layer (window->display->stack, window); tmp = tmp->next; } tmp = frozen_stacks; while (tmp != NULL) { meta_stack_thaw (tmp->data); tmp = tmp->next; } g_slist_free (frozen_stacks); } const char* meta_group_get_startup_id (MetaGroup *group) { return group->startup_id; } /** * meta_group_property_notify: (skip) * @group: a #MetaGroup * @event: a X event * */ gboolean meta_group_property_notify (MetaGroup *group, XEvent *event) { meta_group_reload_property (group, event->xproperty.atom); return TRUE; } int meta_group_get_size (MetaGroup *group) { if (!group) return 0; return group->refcount; } muffin-6.4.1/src/x11/window-x11-private.h0000664000175000017500000000436214723361714016706 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington, Anders Carlsson * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_X11_PRIVATE_H #define META_WINDOW_X11_PRIVATE_H #include "core/window-private.h" #include "x11/iconcache.h" G_BEGIN_DECLS typedef struct _MetaWindowX11Private MetaWindowX11Private; struct _MetaWindowX11Class { MetaWindowClass parent_class; void (*freeze_commits) (MetaWindow *window); void (*thaw_commits) (MetaWindow *window); gboolean (*always_update_shape) (MetaWindow *window); }; struct _MetaWindowX11 { MetaWindow parent; MetaWindowX11Private *priv; }; struct _MetaWindowX11Private { /* TRUE if the client forced these on */ guint wm_state_skip_taskbar : 1; guint wm_state_skip_pager : 1; guint wm_take_focus : 1; guint wm_ping : 1; guint wm_delete_window : 1; /* Weird "_NET_WM_STATE_MODAL" flag */ guint wm_state_modal : 1; /* Info on which props we got our attributes from */ guint using_net_wm_name : 1; /* vs. plain wm_name */ guint using_net_wm_visible_name : 1; /* tracked so we can clear it */ Atom type_atom; /* Requested geometry */ int border_width; gboolean showing_resize_popup; /* These are in server coordinates. If we have a frame, it's * relative to the frame. */ MetaRectangle client_rect; MetaIconCache icon_cache; Pixmap wm_hints_pixmap; Pixmap wm_hints_mask; /* Freeze/thaw on resize (for Xwayland) */ gboolean thaw_after_paint; }; G_END_DECLS #endif muffin-6.4.1/src/x11/atomnames.h0000664000175000017500000001261014723361714015277 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * Copyright (C) 2008 Thomas Thurman * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* * \file atomnames.h A list of atom names. * * This is a list of the names of all the X atoms that Mutter uses. * Each is wrapped in a macro "item()" which is undefined here; the * idea is that when you need to make a big list of all the X atoms, * you can define item(), include this file, and then undefine it * again. * * If you also define EWMH_ATOMS_ONLY then you will only get _NET_WM_* * atoms rather than all of them. */ #ifndef item #error "item(x) must be defined when you include atomnames.h" #endif #ifndef EWMH_ATOMS_ONLY item(WM_PROTOCOLS) /* MUST BE FIRST */ item(WM_TAKE_FOCUS) item(WM_DELETE_WINDOW) item(WM_STATE) item(_MOTIF_WM_HINTS) item(WM_CHANGE_STATE) item(SM_CLIENT_ID) item(WM_CLIENT_LEADER) item(WM_WINDOW_ROLE) item(UTF8_STRING) item(WM_ICON_SIZE) item(_KWM_WIN_ICON) item(_MUTTER_HINTS) item(_GTK_THEME_VARIANT) item(_GTK_APPLICATION_ID) item(_GTK_UNIQUE_BUS_NAME) item(_GTK_APPLICATION_OBJECT_PATH) item(_GTK_WINDOW_OBJECT_PATH) item(_GTK_APP_MENU_OBJECT_PATH) item(_GTK_MENUBAR_OBJECT_PATH) item(_GTK_FRAME_EXTENTS) item(_GTK_SHOW_WINDOW_MENU) item(_GTK_EDGE_CONSTRAINTS) item(_GTK_WORKAREAS) item(_GNOME_WM_KEYBINDINGS) item(_GNOME_PANEL_ACTION) item(_GNOME_PANEL_ACTION_MAIN_MENU) item(_GNOME_PANEL_ACTION_RUN_DIALOG) item(_MUTTER_TIMESTAMP_PING) item(_MUTTER_FOCUS_SET) item(_MUTTER_SENTINEL) item(_MUTTER_VERSION) item(WM_CLIENT_MACHINE) item(MANAGER) item(TARGETS) item(MULTIPLE) item(TIMESTAMP) item(VERSION) item(ATOM_PAIR) item(_XKB_RULES_NAMES) item(WL_SURFACE_ID) item(_XWAYLAND_MAY_GRAB_KEYBOARD) item(_XWAYLAND_RANDR_EMU_MONITOR_RECTS) item(_XWAYLAND_ALLOW_COMMITS) /* Oddities: These are used, and we need atoms for them, * but when we need all _NET_WM hints (i.e. when we're making * lists of which _NET_WM hints we support in order to advertise * it) we haven't historically listed them. I don't know what * the reason for this is. It may be a bug. */ item(_NET_WM_SYNC_REQUEST) item(_NET_WM_SYNC_REQUEST_COUNTER) item(_NET_WM_VISIBLE_NAME) item(_NET_SUPPORTING_WM_CHECK) /* But I suppose it's quite reasonable not to advertise using * _NET_SUPPORTED that we support _NET_SUPPORTED :) */ item(_NET_SUPPORTED) #endif /* !EWMH_ATOMS_ONLY */ /**************************************************************************/ item(_NET_WM_NAME) item(_NET_CLOSE_WINDOW) item(_NET_WM_STATE) item(_NET_WM_STATE_SHADED) item(_NET_WM_STATE_MAXIMIZED_HORZ) item(_NET_WM_STATE_MAXIMIZED_VERT) item(_NET_WM_DESKTOP) item(_NET_NUMBER_OF_DESKTOPS) item(_NET_CURRENT_DESKTOP) item(_NET_WM_WINDOW_TYPE) item(_NET_WM_WINDOW_TYPE_DESKTOP) item(_NET_WM_WINDOW_TYPE_DOCK) item(_NET_WM_WINDOW_TYPE_TOOLBAR) item(_NET_WM_WINDOW_TYPE_MENU) item(_NET_WM_WINDOW_TYPE_UTILITY) item(_NET_WM_WINDOW_TYPE_SPLASH) item(_NET_WM_WINDOW_TYPE_DIALOG) item(_NET_WM_WINDOW_TYPE_DROPDOWN_MENU) item(_NET_WM_WINDOW_TYPE_POPUP_MENU) item(_NET_WM_WINDOW_TYPE_TOOLTIP) item(_NET_WM_WINDOW_TYPE_NOTIFICATION) item(_NET_WM_WINDOW_TYPE_COMBO) item(_NET_WM_WINDOW_TYPE_DND) item(_NET_WM_WINDOW_TYPE_NORMAL) item(_NET_WM_STATE_MODAL) item(_NET_CLIENT_LIST) item(_NET_CLIENT_LIST_STACKING) item(_NET_WM_STATE_SKIP_TASKBAR) item(_NET_WM_STATE_SKIP_PAGER) item(_NET_WM_ICON) item(_NET_WM_ICON_GEOMETRY) item(_NET_WM_MOVERESIZE) item(_NET_ACTIVE_WINDOW) item(_NET_WM_STRUT) item(_NET_WM_STATE_HIDDEN) item(_NET_WM_STATE_FULLSCREEN) item(_NET_WM_PING) item(_NET_WM_PID) item(_NET_WORKAREA) item(_NET_SHOWING_DESKTOP) item(_NET_DESKTOP_LAYOUT) item(_NET_DESKTOP_NAMES) item(_NET_WM_ALLOWED_ACTIONS) item(_NET_WM_ACTION_MOVE) item(_NET_WM_ACTION_RESIZE) item(_NET_WM_ACTION_SHADE) item(_NET_WM_ACTION_STICK) item(_NET_WM_ACTION_MAXIMIZE_HORZ) item(_NET_WM_ACTION_MAXIMIZE_VERT) item(_NET_WM_ACTION_CHANGE_DESKTOP) item(_NET_WM_ACTION_CLOSE) item(_NET_WM_STATE_ABOVE) item(_NET_WM_STATE_BELOW) item(_NET_STARTUP_ID) item(_NET_WM_STRUT_PARTIAL) item(_NET_WM_ACTION_FULLSCREEN) item(_NET_WM_ACTION_MINIMIZE) item(_NET_FRAME_EXTENTS) item(_NET_REQUEST_FRAME_EXTENTS) item(_NET_WM_USER_TIME) item(_NET_WM_STATE_DEMANDS_ATTENTION) item(_NET_MOVERESIZE_WINDOW) item(_NET_DESKTOP_GEOMETRY) item(_NET_DESKTOP_VIEWPORT) item(_NET_WM_USER_TIME_WINDOW) item(_NET_WM_ACTION_ABOVE) item(_NET_WM_ACTION_BELOW) item(_NET_WM_STATE_STICKY) item(_NET_WM_FULLSCREEN_MONITORS) item(_NET_WM_STATE_FOCUSED) item(_NET_WM_BYPASS_COMPOSITOR) item(_NET_WM_OPAQUE_REGION) item(_NET_WM_FRAME_DRAWN) item(_NET_WM_FRAME_TIMINGS) item(_NET_WM_WINDOW_OPACITY) item(_NET_RESTACK_WINDOW) item(_NET_WM_XAPP_ICON_NAME) item(_NET_WM_XAPP_PROGRESS) item(_NET_WM_XAPP_PROGRESS_PULSE) /* eof atomnames.h */ muffin-6.4.1/src/x11/xprops.c0000664000175000017500000007777514723361714014670 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter X property convenience routines */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat Inc. * * Some trivial property-unpacking code from Xlib: * Copyright 1987, 1988, 1998 The Open Group * Copyright 1988 by Wyse Technology, Inc., San Jose, Ca, * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /*********************************************************** Copyright 1988 by Wyse Technology, Inc., San Jose, Ca, Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts, All Rights Reserved Permission to use, copy, modify, and distribute this software and its documentation for any purpose and without fee is hereby granted, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name Digital not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. DIGITAL AND WYSE DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL DIGITAL OR WYSE BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. ******************************************************************/ /* Copyright 1987, 1988, 1998 The Open Group Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation. The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of The Open Group shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from The Open Group. */ #include "config.h" #include "x11/xprops.h" #include #include #include #include #include "core/util-private.h" #include "core/window-private.h" #include "meta/meta-x11-errors.h" #include "ui/ui.h" #include "x11/meta-x11-display-private.h" #include "x11/mutter-Xatomtype.h" typedef struct { MetaX11Display *x11_display; Window xwindow; Atom xatom; Atom type; int format; unsigned long n_items; unsigned long bytes_after; unsigned char *prop; } GetPropertyResults; static gboolean validate_or_free_results (GetPropertyResults *results, int expected_format, Atom expected_type, gboolean must_have_items) { MetaX11Display *x11_display = results->x11_display; char *type_name; char *expected_name; char *prop_name; const char *title; const char *res_class; const char *res_name; MetaWindow *w; if (expected_format == results->format && expected_type == results->type && (!must_have_items || results->n_items > 0)) return TRUE; meta_x11_error_trap_push (x11_display); type_name = XGetAtomName (x11_display->xdisplay, results->type); expected_name = XGetAtomName (x11_display->xdisplay, expected_type); prop_name = XGetAtomName (x11_display->xdisplay, results->xatom); meta_x11_error_trap_pop (x11_display); w = meta_x11_display_lookup_x_window (x11_display, results->xwindow); if (w != NULL) { title = w->title; res_class = w->res_class; res_name = w->res_name; } else { title = NULL; res_class = NULL; res_name = NULL; } if (title == NULL) title = "unknown"; if (res_class == NULL) res_class = "unknown"; if (res_name == NULL) res_name = "unknown"; meta_warning ("Window 0x%lx has property %s\nthat was expected to have type %s format %d\nand actually has type %s format %d n_items %d.\nThis is most likely an application bug, not a window manager bug.\nThe window has title=\"%s\" class=\"%s\" name=\"%s\"\n", results->xwindow, prop_name ? prop_name : "(bad atom)", expected_name ? expected_name : "(bad atom)", expected_format, type_name ? type_name : "(bad atom)", results->format, (int) results->n_items, title, res_class, res_name); meta_XFree (type_name); meta_XFree (expected_name); meta_XFree (prop_name); if (results->prop) { g_free (results->prop); results->prop = NULL; } return FALSE; } static xcb_get_property_cookie_t async_get_property (xcb_connection_t *xcb_conn, Window xwindow, Atom xatom, Atom required_type) { return xcb_get_property (xcb_conn, False, xwindow, xatom, required_type, 0, G_MAXUINT32); } static gboolean async_get_property_finish (xcb_connection_t *xcb_conn, xcb_get_property_cookie_t cookie, GetPropertyResults *results) { xcb_get_property_reply_t *reply; xcb_generic_error_t *error; int length; reply = xcb_get_property_reply (xcb_conn, cookie, &error); if (error) { free (error); return FALSE; } results->n_items = reply->value_len; results->type = reply->type; results->bytes_after = reply->bytes_after; results->format = reply->format; results->prop = NULL; if (results->type != None) { length = xcb_get_property_value_length (reply); /* Leave room for a trailing '\0' since xcb doesn't return null-terminated * strings */ results->prop = g_malloc (length + 1); memcpy (results->prop, xcb_get_property_value (reply), length); results->prop[length] = '\0'; } free (reply); return (results->prop != NULL); } static gboolean get_property (MetaX11Display *x11_display, Window xwindow, Atom xatom, Atom req_type, GetPropertyResults *results) { xcb_get_property_cookie_t cookie; xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay); results->x11_display = x11_display; results->xwindow = xwindow; results->xatom = xatom; results->prop = NULL; results->n_items = 0; results->type = None; results->bytes_after = 0; results->format = 0; cookie = async_get_property (xcb_conn, xwindow, xatom, req_type); return async_get_property_finish (xcb_conn, cookie, results); } static gboolean atom_list_from_results (GetPropertyResults *results, uint32_t **atoms_p, int *n_atoms_p) { if (!validate_or_free_results (results, 32, XA_ATOM, FALSE)) return FALSE; *atoms_p = (uint32_t*) results->prop; *n_atoms_p = results->n_items; results->prop = NULL; return TRUE; } static gboolean cardinal_list_from_results (GetPropertyResults *results, uint32_t **cardinals_p, int *n_cardinals_p) { if (!validate_or_free_results (results, 32, XA_CARDINAL, FALSE)) return FALSE; *cardinals_p = (uint32_t *) results->prop; *n_cardinals_p = results->n_items; results->prop = NULL; return TRUE; } gboolean meta_prop_get_cardinal_list (MetaX11Display *x11_display, Window xwindow, Atom xatom, uint32_t **cardinals_p, int *n_cardinals_p) { GetPropertyResults results; *cardinals_p = NULL; *n_cardinals_p = 0; if (!get_property (x11_display, xwindow, xatom, XA_CARDINAL, &results)) return FALSE; return cardinal_list_from_results (&results, cardinals_p, n_cardinals_p); } static gboolean motif_hints_from_results (GetPropertyResults *results, MotifWmHints **hints_p) { *hints_p = NULL; if (results->type == None || results->n_items <= 0) { g_free (results->prop); results->prop = NULL; meta_verbose ("Motif hints had unexpected type or n_items\n"); return FALSE; } /* The issue here is that some old crufty code will set a smaller * MotifWmHints than the one we expect, apparently. I'm not sure of * the history behind it. See bug #89841 for example. */ *hints_p = g_new0 (MotifWmHints, 1); memcpy(*hints_p, results->prop, MIN (sizeof (MotifWmHints), results->n_items * sizeof (uint32_t))); g_free (results->prop); results->prop = NULL; return TRUE; } gboolean meta_prop_get_motif_hints (MetaX11Display *x11_display, Window xwindow, Atom xatom, MotifWmHints **hints_p) { GetPropertyResults results; *hints_p = NULL; if (!get_property (x11_display, xwindow, xatom, AnyPropertyType, &results)) return FALSE; return motif_hints_from_results (&results, hints_p); } static gboolean latin1_string_from_results (GetPropertyResults *results, char **str_p) { *str_p = NULL; if (!validate_or_free_results (results, 8, XA_STRING, FALSE)) return FALSE; *str_p = g_strndup ((char *) results->prop, results->n_items); g_free (results->prop); results->prop = NULL; return TRUE; } gboolean meta_prop_get_latin1_string (MetaX11Display *x11_display, Window xwindow, Atom xatom, char **str_p) { GetPropertyResults results; *str_p = NULL; if (!get_property (x11_display, xwindow, xatom, XA_STRING, &results)) return FALSE; return latin1_string_from_results (&results, str_p); } static gboolean utf8_string_from_results (GetPropertyResults *results, char **str_p) { *str_p = NULL; if (!validate_or_free_results (results, 8, results->x11_display->atom_UTF8_STRING, FALSE)) return FALSE; if (results->n_items > 0 && !g_utf8_validate ((gchar *)results->prop, results->n_items, NULL)) { char *name; name = XGetAtomName (results->x11_display->xdisplay, results->xatom); meta_warning ("Property %s on window 0x%lx contained invalid UTF-8\n", name, results->xwindow); meta_XFree (name); g_free (results->prop); results->prop = NULL; return FALSE; } *str_p = g_strndup ((char *) results->prop, results->n_items); g_free (results->prop); results->prop = NULL; return TRUE; } /* this one freakishly returns g_malloc memory */ static gboolean utf8_list_from_results (GetPropertyResults *results, char ***str_p, int *n_str_p) { int i; int n_strings; char **retval; const char *p; *str_p = NULL; *n_str_p = 0; if (!validate_or_free_results (results, 8, results->x11_display->atom_UTF8_STRING, FALSE)) return FALSE; /* I'm not sure this is right, but I'm guessing the * property is nul-separated */ i = 0; n_strings = 0; while (i < (int) results->n_items) { if (results->prop[i] == '\0') ++n_strings; ++i; } if (results->prop[results->n_items - 1] != '\0') ++n_strings; /* we're guaranteed that results->prop has a nul on the end * by XGetWindowProperty */ retval = g_new0 (char*, n_strings + 1); p = (char *)results->prop; i = 0; while (i < n_strings) { if (!g_utf8_validate (p, -1, NULL)) { char *name; meta_x11_error_trap_push (results->x11_display); name = XGetAtomName (results->x11_display->xdisplay, results->xatom); meta_x11_error_trap_pop (results->x11_display); meta_warning ("Property %s on window 0x%lx contained invalid UTF-8 for item %d in the list\n", name, results->xwindow, i); meta_XFree (name); g_free (results->prop); results->prop = NULL; g_strfreev (retval); return FALSE; } retval[i] = g_strdup (p); p = p + strlen (p) + 1; ++i; } *str_p = retval; *n_str_p = i; g_free (results->prop); results->prop = NULL; return TRUE; } /* returns g_malloc not Xmalloc memory */ gboolean meta_prop_get_utf8_list (MetaX11Display *x11_display, Window xwindow, Atom xatom, char ***str_p, int *n_str_p) { GetPropertyResults results; *str_p = NULL; if (!get_property (x11_display, xwindow, xatom, x11_display->atom_UTF8_STRING, &results)) return FALSE; return utf8_list_from_results (&results, str_p, n_str_p); } void meta_prop_set_utf8_string_hint (MetaX11Display *x11_display, Window xwindow, Atom atom, const char *val) { meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, xwindow, atom, x11_display->atom_UTF8_STRING, 8, PropModeReplace, (guchar*) val, strlen (val)); meta_x11_error_trap_pop (x11_display); } static gboolean window_from_results (GetPropertyResults *results, Window *window_p) { if (!validate_or_free_results (results, 32, XA_WINDOW, TRUE)) return FALSE; *window_p = *(uint32_t *) results->prop; g_free (results->prop); results->prop = NULL; return TRUE; } static gboolean counter_from_results (GetPropertyResults *results, XSyncCounter *counter_p) { if (!validate_or_free_results (results, 32, XA_CARDINAL, TRUE)) return FALSE; *counter_p = *(uint32_t *) results->prop; g_free (results->prop); results->prop = NULL; return TRUE; } static gboolean counter_list_from_results (GetPropertyResults *results, uint32_t **counters_p, int *n_counters_p) { if (!validate_or_free_results (results, 32, XA_CARDINAL, FALSE)) return FALSE; *counters_p = (uint32_t *) results->prop; *n_counters_p = results->n_items; results->prop = NULL; return TRUE; } gboolean meta_prop_get_window (MetaX11Display *x11_display, Window xwindow, Atom xatom, Window *window_p) { GetPropertyResults results; *window_p = None; if (!get_property (x11_display, xwindow, xatom, XA_WINDOW, &results)) return FALSE; return window_from_results (&results, window_p); } gboolean meta_prop_get_cardinal (MetaX11Display *x11_display, Window xwindow, Atom xatom, uint32_t *cardinal_p) { return meta_prop_get_cardinal_with_atom_type (x11_display, xwindow, xatom, XA_CARDINAL, cardinal_p); } static gboolean cardinal_with_atom_type_from_results (GetPropertyResults *results, Atom prop_type, uint32_t *cardinal_p) { if (!validate_or_free_results (results, 32, prop_type, TRUE)) return FALSE; *cardinal_p = *((uint32_t *) results->prop); g_free (results->prop); results->prop = NULL; return TRUE; } gboolean meta_prop_get_cardinal_with_atom_type (MetaX11Display *x11_display, Window xwindow, Atom xatom, Atom prop_type, uint32_t *cardinal_p) { GetPropertyResults results; *cardinal_p = 0; if (!get_property (x11_display, xwindow, xatom, prop_type, &results)) return FALSE; return cardinal_with_atom_type_from_results (&results, prop_type, cardinal_p); } static char * text_property_to_utf8 (Display *xdisplay, const XTextProperty *prop) { char *ret = NULL; char **local_list = NULL; const char *charset = NULL; int count = 0; int res; res = XmbTextPropertyToTextList (xdisplay, prop, &local_list, &count); if (res == XNoMemory || res == XLocaleNotSupported || res == XConverterNotFound) goto out; if (count == 0) goto out; if (g_get_charset (&charset)) ret = g_strdup (local_list[0]); else ret = g_convert (local_list[0], -1, "UTF-8", charset, NULL, NULL, NULL); out: XFreeStringList (local_list); return ret; } static gboolean text_property_from_results (GetPropertyResults *results, char **utf8_str_p) { XTextProperty tp; *utf8_str_p = NULL; tp.value = results->prop; tp.encoding = results->type; tp.format = results->format; tp.nitems = results->n_items; *utf8_str_p = text_property_to_utf8 (results->x11_display->xdisplay, &tp); g_clear_pointer (&results->prop, g_free); return *utf8_str_p != NULL; } static gboolean wm_hints_from_results (GetPropertyResults *results, XWMHints **hints_p) { XWMHints *hints; xPropWMHints *raw; *hints_p = NULL; if (!validate_or_free_results (results, 32, XA_WM_HINTS, TRUE)) return FALSE; /* pre-R3 bogusly truncated window_group, don't fail on them */ if (results->n_items < (NumPropWMHintsElements - 1)) { meta_verbose ("WM_HINTS property too short: %d should be %d\n", (int) results->n_items, NumPropWMHintsElements - 1); if (results->prop) { g_free (results->prop); results->prop = NULL; } return FALSE; } hints = g_new0 (XWMHints, 1); raw = (xPropWMHints*) results->prop; hints->flags = raw->flags; hints->input = (raw->input ? True : False); hints->initial_state = raw->initialState; hints->icon_pixmap = raw->iconPixmap; hints->icon_window = raw->iconWindow; hints->icon_x = raw->iconX; hints->icon_y = raw->iconY; hints->icon_mask = raw->iconMask; if (results->n_items >= NumPropWMHintsElements) hints->window_group = raw->windowGroup; else hints->window_group = 0; if (results->prop) { g_free (results->prop); results->prop = NULL; } *hints_p = hints; return TRUE; } static gboolean class_hint_from_results (GetPropertyResults *results, XClassHint *class_hint) { int len_name; class_hint->res_class = NULL; class_hint->res_name = NULL; if (!validate_or_free_results (results, 8, XA_STRING, FALSE)) return FALSE; class_hint->res_name = g_strdup ((char *) results->prop); len_name = strlen (class_hint->res_name); if (len_name == (int) results->n_items) class_hint->res_class = g_strdup (""); else class_hint->res_class = g_strdup ((char *) results->prop + len_name + 1); g_free (results->prop); results->prop = NULL; return TRUE; } static gboolean size_hints_from_results (GetPropertyResults *results, XSizeHints **hints_p, gulong *flags_p) { xPropSizeHints *raw; XSizeHints *hints; *hints_p = NULL; *flags_p = 0; if (!validate_or_free_results (results, 32, XA_WM_SIZE_HINTS, FALSE)) return FALSE; if (results->n_items < OldNumPropSizeElements) { g_free (results->prop); results->prop = NULL; return FALSE; } raw = (xPropSizeHints*) results->prop; hints = g_new0 (XSizeHints, 1); hints->flags = raw->flags; hints->x = raw->x; hints->y = raw->y; hints->width = raw->width; hints->height = raw->height; hints->min_width = raw->minWidth; hints->min_height = raw->minHeight; hints->max_width = raw->maxWidth; hints->max_height = raw->maxHeight; hints->width_inc = raw->widthInc; hints->height_inc = raw->heightInc; hints->min_aspect.x = raw->minAspectX; hints->min_aspect.y = raw->minAspectY; hints->max_aspect.x = raw->maxAspectX; hints->max_aspect.y = raw->maxAspectY; *flags_p = (USPosition | USSize | PAllHints); if (results->n_items >= NumPropSizeElements) { hints->base_width = raw->baseWidth; hints->base_height = raw->baseHeight; hints->win_gravity = raw->winGravity; *flags_p |= (PBaseSize | PWinGravity); } hints->flags &= (*flags_p); /* get rid of unwanted bits */ g_free (results->prop); results->prop = NULL; *hints_p = hints; return TRUE; } static char* latin1_to_utf8 (const char *text) { GString *str; const char *p; str = g_string_new (""); p = text; while (*p) { g_string_append_unichar (str, *p); ++p; } return g_string_free (str, FALSE); } void meta_prop_get_values (MetaX11Display *x11_display, Window xwindow, MetaPropValue *values, int n_values) { int i; xcb_get_property_cookie_t *tasks; xcb_connection_t *xcb_conn = XGetXCBConnection (x11_display->xdisplay); meta_verbose ("Requesting %d properties of 0x%lx at once\n", n_values, xwindow); if (n_values == 0) return; tasks = g_new0 (xcb_get_property_cookie_t, n_values); /* Start up tasks. The "values" array can have values * with atom == None, which means to ignore that element. */ i = 0; while (i < n_values) { if (values[i].required_type == None) { switch (values[i].type) { case META_PROP_VALUE_INVALID: /* This means we don't really want a value, e.g. got * property notify on an atom we don't care about. */ if (values[i].atom != None) meta_bug ("META_PROP_VALUE_INVALID requested in %s\n", G_STRFUNC); break; case META_PROP_VALUE_UTF8_LIST: case META_PROP_VALUE_UTF8: values[i].required_type = x11_display->atom_UTF8_STRING; break; case META_PROP_VALUE_STRING: case META_PROP_VALUE_STRING_AS_UTF8: values[i].required_type = XA_STRING; break; case META_PROP_VALUE_MOTIF_HINTS: values[i].required_type = AnyPropertyType; break; case META_PROP_VALUE_CARDINAL_LIST: case META_PROP_VALUE_CARDINAL: values[i].required_type = XA_CARDINAL; break; case META_PROP_VALUE_WINDOW: values[i].required_type = XA_WINDOW; break; case META_PROP_VALUE_ATOM_LIST: values[i].required_type = XA_ATOM; break; case META_PROP_VALUE_TEXT_PROPERTY: values[i].required_type = AnyPropertyType; break; case META_PROP_VALUE_WM_HINTS: values[i].required_type = XA_WM_HINTS; break; case META_PROP_VALUE_CLASS_HINT: values[i].required_type = XA_STRING; break; case META_PROP_VALUE_SIZE_HINTS: values[i].required_type = XA_WM_SIZE_HINTS; break; case META_PROP_VALUE_SYNC_COUNTER: case META_PROP_VALUE_SYNC_COUNTER_LIST: values[i].required_type = XA_CARDINAL; break; } } if (values[i].atom != None) tasks[i] = async_get_property (xcb_conn, xwindow, values[i].atom, values[i].required_type); ++i; } /* Get replies for all our tasks */ meta_topic (META_DEBUG_SYNC, "Syncing to get %d GetProperty replies in %s\n", n_values, G_STRFUNC); XSync (x11_display->xdisplay, False); /* Collect results, should arrive in order requested */ i = 0; while (i < n_values) { GetPropertyResults results; /* We're relying on the fact that sequence numbers can never be zero * in Xorg. This is a bit disgusting... */ if (tasks[i].sequence == 0) { /* Probably values[i].type was None, or ag_task_create() * returned NULL. */ values[i].type = META_PROP_VALUE_INVALID; goto next; } results.x11_display = x11_display; results.xwindow = xwindow; results.xatom = values[i].atom; results.prop = NULL; results.n_items = 0; results.type = None; results.bytes_after = 0; results.format = 0; if (!async_get_property_finish (xcb_conn, tasks[i], &results)) { values[i].type = META_PROP_VALUE_INVALID; goto next; } switch (values[i].type) { case META_PROP_VALUE_INVALID: g_assert_not_reached (); break; case META_PROP_VALUE_UTF8_LIST: if (!utf8_list_from_results (&results, &values[i].v.string_list.strings, &values[i].v.string_list.n_strings)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_UTF8: if (!utf8_string_from_results (&results, &values[i].v.str)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_STRING: if (!latin1_string_from_results (&results, &values[i].v.str)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_STRING_AS_UTF8: if (!latin1_string_from_results (&results, &values[i].v.str)) values[i].type = META_PROP_VALUE_INVALID; else { char *new_str; new_str = latin1_to_utf8 (values[i].v.str); g_free (values[i].v.str); values[i].v.str = new_str; } break; case META_PROP_VALUE_MOTIF_HINTS: if (!motif_hints_from_results (&results, &values[i].v.motif_hints)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_CARDINAL_LIST: if (!cardinal_list_from_results (&results, &values[i].v.cardinal_list.cardinals, &values[i].v.cardinal_list.n_cardinals)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_CARDINAL: if (!cardinal_with_atom_type_from_results (&results, values[i].required_type, &values[i].v.cardinal)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_WINDOW: if (!window_from_results (&results, &values[i].v.xwindow)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_ATOM_LIST: if (!atom_list_from_results (&results, &values[i].v.atom_list.atoms, &values[i].v.atom_list.n_atoms)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_TEXT_PROPERTY: if (!text_property_from_results (&results, &values[i].v.str)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_WM_HINTS: if (!wm_hints_from_results (&results, &values[i].v.wm_hints)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_CLASS_HINT: if (!class_hint_from_results (&results, &values[i].v.class_hint)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_SIZE_HINTS: if (!size_hints_from_results (&results, &values[i].v.size_hints.hints, &values[i].v.size_hints.flags)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_SYNC_COUNTER: if (!counter_from_results (&results, &values[i].v.xcounter)) values[i].type = META_PROP_VALUE_INVALID; break; case META_PROP_VALUE_SYNC_COUNTER_LIST: if (!counter_list_from_results (&results, &values[i].v.xcounter_list.counters, &values[i].v.xcounter_list.n_counters)) values[i].type = META_PROP_VALUE_INVALID; break; } next: ++i; } g_free (tasks); } static void free_value (MetaPropValue *value) { switch (value->type) { case META_PROP_VALUE_INVALID: break; case META_PROP_VALUE_UTF8: case META_PROP_VALUE_STRING: g_free (value->v.str); break; case META_PROP_VALUE_STRING_AS_UTF8: g_free (value->v.str); break; case META_PROP_VALUE_MOTIF_HINTS: g_free (value->v.motif_hints); break; case META_PROP_VALUE_CARDINAL: break; case META_PROP_VALUE_WINDOW: break; case META_PROP_VALUE_ATOM_LIST: g_free (value->v.atom_list.atoms); break; case META_PROP_VALUE_TEXT_PROPERTY: g_free (value->v.str); break; case META_PROP_VALUE_WM_HINTS: g_free (value->v.wm_hints); break; case META_PROP_VALUE_CLASS_HINT: g_free (value->v.class_hint.res_class); g_free (value->v.class_hint.res_name); break; case META_PROP_VALUE_SIZE_HINTS: g_free (value->v.size_hints.hints); break; case META_PROP_VALUE_UTF8_LIST: g_strfreev (value->v.string_list.strings); break; case META_PROP_VALUE_CARDINAL_LIST: g_free (value->v.cardinal_list.cardinals); break; case META_PROP_VALUE_SYNC_COUNTER: break; case META_PROP_VALUE_SYNC_COUNTER_LIST: g_free (value->v.xcounter_list.counters); break; } } void meta_prop_free_values (MetaPropValue *values, int n_values) { int i; i = 0; while (i < n_values) { free_value (&values[i]); ++i; } /* Zero the whole thing to quickly detect breakage */ memset (values, '\0', sizeof (MetaPropValue) * n_values); } muffin-6.4.1/src/x11/meta-x11-selection-output-stream-private.h0000664000175000017500000000451514723361714023137 0ustar fabiofabio/* GIO - GLib Output, Output and Streaming Library * * Copyright (C) 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: Benjamin Otte * Christian Kellner */ #ifndef META_X11_SELECTION_OUTPUT_STREAM_H #define META_X11_SELECTION_OUTPUT_STREAM_H #include #include "x11/meta-x11-display-private.h" #define META_TYPE_X11_SELECTION_OUTPUT_STREAM (meta_x11_selection_output_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaX11SelectionOutputStream, meta_x11_selection_output_stream, META, X11_SELECTION_OUTPUT_STREAM, GOutputStream) GOutputStream * meta_x11_selection_output_stream_new (MetaX11Display *x11_display, Window window, const char *selection, const char *target, const char *property, const char *type, int format, gulong timestamp); gboolean meta_x11_selection_output_stream_xevent (MetaX11SelectionOutputStream *stream, const XEvent *xevent); #endif /* META_X11_SELECTION_OUTPUT_STREAM_H */ muffin-6.4.1/src/x11/meta-x11-stack.c0000664000175000017500000002403414723361714015751 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2019 Red Hat, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "core/frame.h" #include "core/stack.h" #include "core/window-private.h" #include "x11/meta-x11-display-private.h" #include "x11/meta-x11-stack-private.h" struct _MetaX11Stack { GObject parent; MetaX11Display *x11_display; /* * A sequence of all the Windows (X handles, not MetaWindows) of the windows * we manage, sorted in order. Suitable to be passed into _NET_CLIENT_LIST. */ GArray *xwindows; /* * MetaWindows waiting to be added to the xwindows list, after * being added to the MetaStack. * * The order of the elements in this list is not important; what is important * is the stack_position element of each window. */ GList *added; /* * Windows (X handles, not MetaWindows) waiting to be removed from the * xwindows list, after being removed from the MetaStack. * * The order of the elements in this list is not important. */ GList *removed; }; enum { PROP_DISPLAY = 1, N_PROPS }; static GParamSpec *pspecs[N_PROPS] = { 0 }; G_DEFINE_TYPE (MetaX11Stack, meta_x11_stack, G_TYPE_OBJECT) static void meta_x11_stack_init (MetaX11Stack *x11_stack) { x11_stack->xwindows = g_array_new (FALSE, FALSE, sizeof (Window)); } static void meta_x11_stack_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaX11Stack *x11_stack = META_X11_STACK (object); switch (prop_id) { case PROP_DISPLAY: x11_stack->x11_display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_x11_stack_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaX11Stack *x11_stack = META_X11_STACK (object); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, x11_stack->x11_display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void stack_window_added_cb (MetaStack *stack, MetaWindow *window, MetaX11Stack *x11_stack) { if (window->client_type != META_WINDOW_CLIENT_TYPE_X11) return; x11_stack->added = g_list_prepend (x11_stack->added, window); } static void stack_window_removed_cb (MetaStack *stack, MetaWindow *window, MetaX11Stack *x11_stack) { if (window->client_type != META_WINDOW_CLIENT_TYPE_X11) return; x11_stack->added = g_list_remove (x11_stack->added, window); x11_stack->removed = g_list_prepend (x11_stack->removed, GUINT_TO_POINTER (window->xwindow)); if (window->frame) { x11_stack->removed = g_list_prepend (x11_stack->removed, GUINT_TO_POINTER (window->frame->xwindow)); } } /** * stack_do_window_deletions: * * Go through "deleted" and take the matching windows * out of "windows". */ static void x11_stack_do_window_deletions (MetaX11Stack *x11_stack) { GList *tmp; int i; tmp = x11_stack->removed; while (tmp != NULL) { Window xwindow; xwindow = GPOINTER_TO_UINT (tmp->data); /* We go from the end figuring removals are more * likely to be recent. */ i = x11_stack->xwindows->len; while (i > 0) { --i; /* there's no guarantee we'll actually find windows to * remove, e.g. the same xwindow could have been * added/removed before we ever synced, and we put * both the window->xwindow and window->frame->xwindow * in the removal list. */ if (xwindow == g_array_index (x11_stack->xwindows, Window, i)) { g_array_remove_index (x11_stack->xwindows, i); goto next; } } next: tmp = tmp->next; } g_clear_pointer (&x11_stack->removed, g_list_free); } static void x11_stack_do_window_additions (MetaX11Stack *x11_stack) { GList *tmp; gint n_added; n_added = g_list_length (x11_stack->added); if (n_added > 0) { meta_topic (META_DEBUG_STACK, "Adding %d windows to sorted list\n", n_added); /* stack->added has the most recent additions at the * front of the list, so we need to reverse it */ x11_stack->added = g_list_reverse (x11_stack->added); tmp = x11_stack->added; while (tmp != NULL) { MetaWindow *w; w = tmp->data; g_array_append_val (x11_stack->xwindows, w->xwindow); tmp = tmp->next; } } g_clear_pointer (&x11_stack->added, g_list_free); } /** * x11_stack_sync_to_server: * * Order the windows on the X server to be the same as in our structure. * We do this using XRestackWindows if we don't know the previous order, * or XConfigureWindow on a few particular windows if we do and can figure * out the minimum set of changes. After that, we set __NET_CLIENT_LIST * and __NET_CLIENT_LIST_STACKING. * * FIXME: Now that we have a good view of the stacking order on the server * with MetaStackTracker it should be possible to do a simpler and better * job of computing the minimal set of stacking requests needed. */ static void x11_stack_sync_to_xserver (MetaX11Stack *x11_stack) { MetaX11Display *x11_display = x11_stack->x11_display; MetaStack *stack = x11_display->display->stack; GArray *x11_stacked; GList *tmp; GList *sorted; meta_topic (META_DEBUG_STACK, "Syncing window stack to server\n"); /* Create stacked xwindow arrays, in bottom-to-top order */ x11_stacked = g_array_new (FALSE, FALSE, sizeof (Window)); sorted = meta_stack_list_windows (stack, NULL); for (tmp = sorted; tmp; tmp = tmp->next) { MetaWindow *w = tmp->data; if (w->client_type == META_WINDOW_CLIENT_TYPE_X11) g_array_append_val (x11_stacked, w->xwindow); } /* Sync _NET_CLIENT_LIST and _NET_CLIENT_LIST_STACKING */ XChangeProperty (x11_stack->x11_display->xdisplay, x11_stack->x11_display->xroot, x11_stack->x11_display->atom__NET_CLIENT_LIST, XA_WINDOW, 32, PropModeReplace, (unsigned char *) x11_stack->xwindows->data, x11_stack->xwindows->len); XChangeProperty (x11_stack->x11_display->xdisplay, x11_stack->x11_display->xroot, x11_stack->x11_display->atom__NET_CLIENT_LIST_STACKING, XA_WINDOW, 32, PropModeReplace, (unsigned char *) x11_stacked->data, x11_stacked->len); g_array_free (x11_stacked, TRUE); g_list_free (sorted); } static void stack_changed_cb (MetaX11Stack *x11_stack) { /* Do removals before adds, with paranoid idea that we might re-add * the same window IDs. */ x11_stack_do_window_deletions (x11_stack); x11_stack_do_window_additions (x11_stack); x11_stack_sync_to_xserver (x11_stack); } static void meta_x11_stack_constructed (GObject *object) { MetaX11Stack *x11_stack = META_X11_STACK (object); MetaX11Display *x11_display = x11_stack->x11_display; G_OBJECT_CLASS (meta_x11_stack_parent_class)->constructed (object); g_signal_connect (x11_display->display->stack, "window-added", G_CALLBACK (stack_window_added_cb), x11_stack); g_signal_connect (x11_display->display->stack, "window-removed", G_CALLBACK (stack_window_removed_cb), x11_stack); g_signal_connect_swapped (x11_display->display->stack, "changed", G_CALLBACK (stack_changed_cb), x11_stack); } static void meta_x11_stack_finalize (GObject *object) { MetaX11Stack *x11_stack = META_X11_STACK (object); MetaX11Display *x11_display = x11_stack->x11_display; if (x11_display->display && x11_display->display->stack) { g_signal_handlers_disconnect_by_data (x11_display->display->stack, x11_stack); } g_array_free (x11_stack->xwindows, TRUE); g_list_free (x11_stack->added); g_list_free (x11_stack->removed); G_OBJECT_CLASS (meta_x11_stack_parent_class)->finalize (object); } static void meta_x11_stack_class_init (MetaX11StackClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_x11_stack_set_property; object_class->get_property = meta_x11_stack_get_property; object_class->constructed = meta_x11_stack_constructed; object_class->finalize = meta_x11_stack_finalize; pspecs[PROP_DISPLAY] = g_param_spec_object ("display", "Display", "Display", META_TYPE_X11_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_PROPS, pspecs); } MetaX11Stack * meta_x11_stack_new (MetaX11Display *x11_display) { return g_object_new (META_TYPE_X11_STACK, "display", x11_display, NULL); } muffin-6.4.1/src/x11/group-private.h0000664000175000017500000000204014723361714016113 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window group private header */ /* * Copyright (C) 2002 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_GROUP_PRIVATE_H #define META_GROUP_PRIVATE_H #include "meta/group.h" struct _MetaGroup { int refcount; MetaX11Display *x11_display; GSList *windows; Window group_leader; char *startup_id; char *wm_client_machine; }; #endif muffin-6.4.1/src/x11/iconcache.c0000664000175000017500000003444514723361714015234 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter window icons */ /* * Copyright (C) 2002 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/iconcache.h" #include #include #include #include #include #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" static gboolean find_largest_sizes (gulong *data, gulong nitems, int *width, int *height) { *width = 0; *height = 0; while (nitems > 0) { int w, h; if (nitems < 3) return FALSE; /* no space for w, h */ w = data[0]; h = data[1]; if (nitems < ((gulong)(w * h) + 2)) return FALSE; /* not enough data */ *width = MAX (w, *width); *height = MAX (h, *height); data += (w * h) + 2; nitems -= (w * h) + 2; } return TRUE; } static gboolean find_best_size (gulong *data, gulong nitems, int ideal_width, int ideal_height, int *width, int *height, gulong **start) { int best_w; int best_h; gulong *best_start; int max_width, max_height; *width = 0; *height = 0; *start = NULL; if (!find_largest_sizes (data, nitems, &max_width, &max_height)) return FALSE; if (ideal_width < 0) ideal_width = max_width; if (ideal_height < 0) ideal_height = max_height; best_w = 0; best_h = 0; best_start = NULL; while (nitems > 0) { int w, h; gboolean replace; replace = FALSE; if (nitems < 3) return FALSE; /* no space for w, h */ w = data[0]; h = data[1]; if (nitems < ((gulong)(w * h) + 2)) break; /* not enough data */ if (best_start == NULL) { replace = TRUE; } else { /* work with averages */ const int ideal_size = (ideal_width + ideal_height) / 2; int best_size = (best_w + best_h) / 2; int this_size = (w + h) / 2; /* larger than desired is always better than smaller */ if (best_size < ideal_size && this_size >= ideal_size) replace = TRUE; /* if we have too small, pick anything bigger */ else if (best_size < ideal_size && this_size > best_size) replace = TRUE; /* if we have too large, pick anything smaller * but still >= the ideal */ else if (best_size > ideal_size && this_size >= ideal_size && this_size < best_size) replace = TRUE; } if (replace) { best_start = data + 2; best_w = w; best_h = h; } data += (w * h) + 2; nitems -= (w * h) + 2; } if (best_start) { *start = best_start; *width = best_w; *height = best_h; return TRUE; } else return FALSE; } static cairo_surface_t * argbdata_to_surface (gulong *argb_data, int w, int h) { cairo_surface_t *surface; int y, x, stride; uint32_t *data; surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, w, h); stride = cairo_image_surface_get_stride (surface) / sizeof (uint32_t); data = (uint32_t *) cairo_image_surface_get_data (surface); /* One could speed this up a lot. */ for (y = 0; y < h; y++) { for (x = 0; x < w; x++) { uint32_t *p = &data[y * stride + x]; gulong *d = &argb_data[y * w + x]; *p = *d; } } cairo_surface_mark_dirty (surface); return surface; } static gboolean read_rgb_icon (MetaX11Display *x11_display, Window xwindow, int ideal_width, int ideal_height, int ideal_mini_width, int ideal_mini_height, cairo_surface_t **icon, cairo_surface_t **mini_icon) { Atom type; int format; gulong nitems; gulong bytes_after; int result, err; guchar *data; gulong *best; int w, h; gulong *best_mini; int mini_w, mini_h; gulong *data_as_long; meta_x11_error_trap_push (x11_display); type = None; data = NULL; result = XGetWindowProperty (x11_display->xdisplay, xwindow, x11_display->atom__NET_WM_ICON, 0, G_MAXLONG, False, XA_CARDINAL, &type, &format, &nitems, &bytes_after, &data); err = meta_x11_error_trap_pop_with_return (x11_display); if (err != Success || result != Success) return FALSE; if (type != XA_CARDINAL) { XFree (data); return FALSE; } data_as_long = (gulong *)data; if (!find_best_size (data_as_long, nitems, ideal_width, ideal_height, &w, &h, &best)) { XFree (data); return FALSE; } if (!find_best_size (data_as_long, nitems, ideal_mini_width, ideal_mini_height, &mini_w, &mini_h, &best_mini)) { XFree (data); return FALSE; } *icon = argbdata_to_surface (best, w, h); *mini_icon = argbdata_to_surface (best_mini, mini_w, mini_h); XFree (data); return TRUE; } static void get_pixmap_geometry (MetaX11Display *x11_display, Pixmap pixmap, int *w, int *h, int *d) { Window root_ignored; int x_ignored, y_ignored; guint width, height; guint border_width_ignored; guint depth; if (w) *w = 1; if (h) *h = 1; if (d) *d = 1; XGetGeometry (x11_display->xdisplay, pixmap, &root_ignored, &x_ignored, &y_ignored, &width, &height, &border_width_ignored, &depth); if (w) *w = width; if (h) *h = height; if (d) *d = depth; } static cairo_surface_t * surface_from_pixmap (Display *xdisplay, Pixmap xpixmap, int width, int height) { Window root_return; XVisualInfo visual_info; int x_ret, y_ret; unsigned int w_ret, h_ret, bw_ret, depth_ret; if (!XGetGeometry (xdisplay, xpixmap, &root_return, &x_ret, &y_ret, &w_ret, &h_ret, &bw_ret, &depth_ret)) return NULL; if (!XMatchVisualInfo (xdisplay, DefaultScreen (xdisplay), depth_ret, TrueColor, &visual_info)) return NULL; return cairo_xlib_surface_create (xdisplay, xpixmap, visual_info.visual, w_ret, h_ret); } static gboolean try_pixmap_and_mask (MetaX11Display *x11_display, Pixmap src_pixmap, Pixmap src_mask, cairo_surface_t **iconp) { Display *xdisplay = x11_display->xdisplay; cairo_surface_t *icon, *mask = NULL; int w, h, d; if (src_pixmap == None) return FALSE; meta_x11_error_trap_push (x11_display); get_pixmap_geometry (x11_display, src_pixmap, &w, &h, &d); icon = surface_from_pixmap (xdisplay, src_pixmap, w, h); if (icon && src_mask != None) { get_pixmap_geometry (x11_display, src_mask, &w, &h, &d); if (d == 1) mask = surface_from_pixmap (xdisplay, src_mask, w, h); } meta_x11_error_trap_pop (x11_display); if (icon && mask) { cairo_surface_t *masked; cairo_t *cr; masked = cairo_surface_create_similar_image (icon, CAIRO_FORMAT_ARGB32, cairo_xlib_surface_get_width (icon), cairo_xlib_surface_get_height (icon)); cr = cairo_create (masked); cairo_set_source_surface (cr, icon, 0, 0); cairo_mask_surface (cr, mask, 0, 0); cairo_destroy (cr); cairo_surface_destroy (icon); cairo_surface_destroy (mask); icon = masked; } if (icon) { *iconp = icon; return TRUE; } else { return FALSE; } } static void get_kwm_win_icon (MetaX11Display *x11_display, Window xwindow, Pixmap *pixmap, Pixmap *mask) { Atom type; int format; gulong nitems; gulong bytes_after; guchar *data; Pixmap *icons; int err, result; *pixmap = None; *mask = None; meta_x11_error_trap_push (x11_display); icons = NULL; result = XGetWindowProperty (x11_display->xdisplay, xwindow, x11_display->atom__KWM_WIN_ICON, 0, G_MAXLONG, False, x11_display->atom__KWM_WIN_ICON, &type, &format, &nitems, &bytes_after, &data); icons = (Pixmap *)data; err = meta_x11_error_trap_pop_with_return (x11_display); if (err != Success || result != Success) return; if (type != x11_display->atom__KWM_WIN_ICON) { XFree (icons); return; } *pixmap = icons[0]; *mask = icons[1]; XFree (icons); return; } void meta_icon_cache_init (MetaIconCache *icon_cache) { g_return_if_fail (icon_cache != NULL); icon_cache->origin = USING_NO_ICON; icon_cache->prev_pixmap = None; icon_cache->prev_mask = None; icon_cache->wm_hints_dirty = TRUE; icon_cache->kwm_win_icon_dirty = TRUE; icon_cache->net_wm_icon_dirty = TRUE; } void meta_icon_cache_property_changed (MetaIconCache *icon_cache, MetaX11Display *x11_display, Atom atom) { if (atom == x11_display->atom__NET_WM_ICON) icon_cache->net_wm_icon_dirty = TRUE; else if (atom == x11_display->atom__KWM_WIN_ICON) icon_cache->kwm_win_icon_dirty = TRUE; else if (atom == XA_WM_HINTS) icon_cache->wm_hints_dirty = TRUE; } gboolean meta_icon_cache_get_icon_invalidated (MetaIconCache *icon_cache) { if (icon_cache->origin <= USING_KWM_WIN_ICON && icon_cache->kwm_win_icon_dirty) return TRUE; else if (icon_cache->origin <= USING_WM_HINTS && icon_cache->wm_hints_dirty) return TRUE; else if (icon_cache->origin <= USING_NET_WM_ICON && icon_cache->net_wm_icon_dirty) return TRUE; else if (icon_cache->origin < USING_FALLBACK_ICON) return TRUE; else return FALSE; } gboolean meta_read_icons (MetaX11Display *x11_display, Window xwindow, MetaIconCache *icon_cache, Pixmap wm_hints_pixmap, Pixmap wm_hints_mask, cairo_surface_t **iconp, int ideal_width, int ideal_height, cairo_surface_t **mini_iconp, int ideal_mini_width, int ideal_mini_height) { /* Return value is whether the icon changed */ g_return_val_if_fail (icon_cache != NULL, FALSE); *iconp = NULL; *mini_iconp = NULL; if (!meta_icon_cache_get_icon_invalidated (icon_cache)) return FALSE; /* we have no new info to use */ /* Our algorithm here assumes that we can't have for example origin * < USING_NET_WM_ICON and icon_cache->net_wm_icon_dirty == FALSE * unless we have tried to read NET_WM_ICON. * * Put another way, if an icon origin is not dirty, then we have * tried to read it at the current size. If it is dirty, then * we haven't done that since the last change. */ if (icon_cache->origin <= USING_NET_WM_ICON && icon_cache->net_wm_icon_dirty) { icon_cache->net_wm_icon_dirty = FALSE; if (read_rgb_icon (x11_display, xwindow, ideal_width, ideal_height, ideal_mini_width, ideal_mini_height, iconp, mini_iconp)) { icon_cache->origin = USING_NET_WM_ICON; return TRUE; } } if (icon_cache->origin <= USING_WM_HINTS && icon_cache->wm_hints_dirty) { Pixmap pixmap; Pixmap mask; icon_cache->wm_hints_dirty = FALSE; pixmap = wm_hints_pixmap; mask = wm_hints_mask; /* We won't update if pixmap is unchanged; * avoids a get_from_drawable() on every geometry * hints change */ if ((pixmap != icon_cache->prev_pixmap || mask != icon_cache->prev_mask) && pixmap != None) { if (try_pixmap_and_mask (x11_display, pixmap, mask, iconp)) { *mini_iconp = cairo_surface_reference (*iconp); icon_cache->prev_pixmap = pixmap; icon_cache->prev_mask = mask; icon_cache->origin = USING_WM_HINTS; return TRUE; } } } if (icon_cache->origin <= USING_KWM_WIN_ICON && icon_cache->kwm_win_icon_dirty) { Pixmap pixmap; Pixmap mask; icon_cache->kwm_win_icon_dirty = FALSE; get_kwm_win_icon (x11_display, xwindow, &pixmap, &mask); if ((pixmap != icon_cache->prev_pixmap || mask != icon_cache->prev_mask) && pixmap != None) { if (try_pixmap_and_mask (x11_display, pixmap, mask, iconp)) { *mini_iconp = cairo_surface_reference (*iconp); icon_cache->prev_pixmap = pixmap; icon_cache->prev_mask = mask; icon_cache->origin = USING_KWM_WIN_ICON; return TRUE; } } } if (icon_cache->origin < USING_FALLBACK_ICON) { icon_cache->origin = USING_FALLBACK_ICON; *iconp = NULL; *mini_iconp = NULL; return TRUE; } /* found nothing new */ return FALSE; } muffin-6.4.1/src/x11/meta-x11-background-actor.c0000664000175000017500000005140714723361714020075 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-background-actor.c: Actor for painting the root window background * * Copyright 2009 Sander Dijkhuis * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street - Suite 500, Boston, MA * 02110-1335, USA. * * Portions adapted from gnome-shell/src/shell-global.c */ /** * SECTION:meta-x11-background-actor * @title: MetaX11BackgroundActor * @short_description: Actor for painting the root window background */ #include #include #include #include #include "cogl/cogl.h" #include "meta/compositor-mutter.h" #include "meta/meta-x11-errors.h" #include "meta/meta-x11-display.h" #include "meta-x11-background-actor-private.h" #include "meta-x11-display-private.h" #define FADE_DURATION 1500 /* We allow creating multiple MetaX11BackgroundActors for the same MetaScreen to * allow different rendering options to be set for different copies. * But we want to share the same underlying CoglTexture for efficiency and * to avoid driver bugs that might occur if we created multiple CoglTexturePixmaps * for the same pixmap. * * This structure holds common information. */ typedef struct _MetaDisplayBackground MetaDisplayBackground; struct _MetaDisplayBackground { MetaDisplay *display; MetaX11Display *x11_display; GSList *actors; float texture_width; float texture_height; CoglTexture *texture; CoglPipelineWrapMode wrap_mode; guint have_pixmap : 1; }; typedef struct { MetaDisplayBackground *background; ClutterActor *top_actor; ClutterActor *bottom_actor; cairo_region_t *visible_region; float dim_factor; gboolean transition_running; } MetaX11BackgroundActorPrivate; struct _MetaX11BackgroundActor { ClutterActor parent_instance; MetaX11BackgroundActorPrivate *priv; }; enum { PROP_0, PROP_DIM_FACTOR, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; G_DEFINE_TYPE_WITH_PRIVATE (MetaX11BackgroundActor, meta_x11_background_actor, CLUTTER_TYPE_ACTOR); static void set_texture (MetaDisplayBackground *background, CoglTexture *texture); static void set_texture_to_stage_color (MetaDisplayBackground *background); static void on_notify_stage_color (GObject *stage, GParamSpec *pspec, MetaDisplayBackground *background) { if (!background->have_pixmap) set_texture_to_stage_color (background); } static void free_display_background (MetaDisplayBackground *background) { set_texture (background, NULL); if (background->display != NULL) { ClutterActor *stage = meta_get_stage_for_display (background->display); g_signal_handlers_disconnect_by_func (stage, (gpointer) on_notify_stage_color, background); background->display = NULL; } } static MetaDisplayBackground * meta_display_background_get (MetaDisplay *display) { MetaDisplayBackground *background; background = g_object_get_data (G_OBJECT (display), "meta-display-background"); if (background == NULL) { ClutterActor *stage; background = g_new0 (MetaDisplayBackground, 1); background->display = display; background->x11_display = meta_display_get_x11_display (display); g_object_set_data_full (G_OBJECT (display), "meta-display-background", background, (GDestroyNotify) free_display_background); stage = meta_get_stage_for_display (display); g_signal_connect (stage, "notify::background-color", G_CALLBACK (on_notify_stage_color), background); meta_x11_background_actor_update (display); } return background; } static void update_wrap_mode_of_actor (MetaX11BackgroundActor *self) { MetaX11BackgroundActorPrivate *priv = self->priv; meta_x11_background_set_layer_wrap_mode (META_X11_BACKGROUND (priv->top_actor), priv->background->wrap_mode); meta_x11_background_set_layer_wrap_mode (META_X11_BACKGROUND (priv->bottom_actor), priv->background->wrap_mode); /* this ensures the actors also get resized if the stage size changed */ clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->top_actor)); clutter_actor_queue_relayout (CLUTTER_ACTOR (priv->bottom_actor)); } static void update_wrap_mode (MetaDisplayBackground *background) { GSList *l; int width, height; meta_display_get_size (background->display, &width, &height); /* We turn off repeating when we have a full-screen pixmap to keep from * getting artifacts from one side of the image sneaking into the other * side of the image via bilinear filtering. */ if (width == background->texture_width && height == background->texture_height) background->wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; else background->wrap_mode = COGL_PIPELINE_WRAP_MODE_REPEAT; for (l = background->actors; l; l = l->next) update_wrap_mode_of_actor (l->data); } static void cancel_transitions (MetaX11BackgroundActor *self) { MetaX11BackgroundActorPrivate *priv = self->priv; clutter_actor_remove_all_transitions (priv->top_actor); clutter_actor_set_opacity (priv->top_actor, 255); meta_x11_background_set_layer (META_X11_BACKGROUND (priv->bottom_actor), priv->background->texture); priv->transition_running = FALSE; } static void on_transition_complete (ClutterActor *actor, gpointer user_data) { MetaX11BackgroundActor *self = (MetaX11BackgroundActor *)user_data; MetaX11BackgroundActorPrivate *priv = self->priv; meta_x11_background_set_layer (META_X11_BACKGROUND (priv->bottom_actor), priv->background->texture); priv->transition_running = FALSE; } static void set_texture_on_actors (MetaX11BackgroundActor *self) { MetaX11BackgroundActorPrivate *priv = self->priv; meta_x11_background_set_layer (META_X11_BACKGROUND (priv->bottom_actor), priv->background->texture); meta_x11_background_set_layer (META_X11_BACKGROUND (priv->top_actor), priv->background->texture); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } static void set_texture_on_actor (MetaX11BackgroundActor *self) { MetaX11BackgroundActorPrivate *priv = self->priv; MetaX11BackgroundTransition background_transition; if (priv->transition_running) cancel_transitions (self); background_transition = meta_prefs_get_background_transition(); if (background_transition == META_X11_BACKGROUND_TRANSITION_NONE) { // NO TRANSITION clutter_actor_set_opacity (CLUTTER_ACTOR (priv->bottom_actor), 0); meta_x11_background_set_layer (META_X11_BACKGROUND (priv->top_actor), priv->background->texture); on_transition_complete (priv->top_actor, self); } else { if (background_transition == META_X11_BACKGROUND_TRANSITION_FADEIN) { // FADE_IN TRANSITION clutter_actor_set_opacity (CLUTTER_ACTOR (priv->bottom_actor), 0); } // BLEND TRANSITION clutter_actor_set_opacity (CLUTTER_ACTOR (priv->top_actor), 0); meta_x11_background_set_layer (META_X11_BACKGROUND (priv->top_actor), priv->background->texture); priv->transition_running = TRUE; clutter_actor_save_easing_state (priv->top_actor); clutter_actor_set_easing_duration (priv->top_actor, FADE_DURATION); clutter_actor_set_opacity (priv->top_actor, 255); clutter_actor_restore_easing_state (priv->top_actor); g_signal_connect (priv->top_actor, "transitions-completed", G_CALLBACK (on_transition_complete), self); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } } static void set_texture (MetaDisplayBackground *background, CoglTexture *texture) { GSList *l; /* This may trigger destruction of an old texture pixmap, which, if * the underlying X pixmap is already gone has the tendency to trigger * X errors inside DRI. For safety, trap errors */ meta_x11_error_trap_push (background->x11_display); if (background->texture != NULL) { cogl_object_unref (background->texture); background->texture = NULL; } meta_x11_error_trap_pop (background->x11_display); if (texture != NULL) background->texture = cogl_object_ref (texture); background->texture_width = cogl_texture_get_width (background->texture); background->texture_height = cogl_texture_get_height (background->texture); for (l = background->actors; l; l = l->next) set_texture_on_actor (l->data); update_wrap_mode (background); } static CoglTexture * create_color_texture_4ub (guint8 red, guint8 green, guint8 blue, guint8 alpha, CoglTextureFlags flags) { CoglColor color; guint8 pixel[4]; cogl_color_init_from_4ub (&color, red, green, blue, alpha); cogl_color_premultiply (&color); pixel[0] = cogl_color_get_red_byte (&color); pixel[1] = cogl_color_get_green_byte (&color); pixel[2] = cogl_color_get_blue_byte (&color); pixel[3] = cogl_color_get_alpha_byte (&color); return cogl_texture_new_from_data (1, 1, flags, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_ANY, 4, pixel); } /* Sets our material to paint with a 1x1 texture of the stage's background * color; doing this when we have no pixmap allows the application to turn * off painting the stage. There might be a performance benefit to * painting in this case with a solid color, but the normal solid color * case is a 1x1 root pixmap, so we'd have to reverse-engineer that to * actually pick up the (small?) performance win. This is just a fallback. */ static void set_texture_to_stage_color (MetaDisplayBackground *background) { ClutterActor *stage = meta_get_stage_for_display (background->display); ClutterColor color; CoglTexture *texture; clutter_actor_get_background_color (stage, &color); /* Slicing will prevent COGL from using hardware texturing for * the tiled 1x1 pixmap, and will cause it to draw the window * background in millions of separate 1x1 rectangles */ texture = create_color_texture_4ub (color.red, color.green, color.blue, 0xff, COGL_TEXTURE_NO_SLICING); set_texture (background, texture); cogl_object_unref (texture); } static void meta_x11_background_actor_dispose (GObject *object) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (object); MetaX11BackgroundActorPrivate *priv = self->priv; meta_x11_background_actor_set_visible_region (self, NULL); if (priv->background != NULL) { priv->background->actors = g_slist_remove (priv->background->actors, self); priv->background = NULL; } if (priv->top_actor != NULL) priv->top_actor = NULL; if (priv->bottom_actor != NULL) priv->bottom_actor = NULL; G_OBJECT_CLASS (meta_x11_background_actor_parent_class)->dispose (object); } static void meta_x11_background_actor_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (actor); MetaX11BackgroundActorPrivate *priv = self->priv; int width, height; meta_display_get_size (priv->background->display, &width, &height); if (min_width_p) *min_width_p = width; if (natural_width_p) *natural_width_p = width; } static void meta_x11_background_actor_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (actor); MetaX11BackgroundActorPrivate *priv = self->priv; int width, height; meta_display_get_size (priv->background->display, &width, &height); if (min_height_p) *min_height_p = height; if (natural_height_p) *natural_height_p = height; } static gboolean meta_x11_background_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (actor); MetaX11BackgroundActorPrivate *priv = self->priv; int width, height; meta_display_get_size (priv->background->display, &width, &height); clutter_paint_volume_set_width (volume, width); clutter_paint_volume_set_height (volume, height); return TRUE; } static void meta_x11_background_actor_set_dim_factor (MetaX11BackgroundActor *self, gfloat dim_factor) { MetaX11BackgroundActorPrivate *priv = self->priv; if (priv->dim_factor == dim_factor) return; priv->dim_factor = dim_factor; clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); g_object_notify_by_pspec (G_OBJECT (self), obj_props[PROP_DIM_FACTOR]); } static void meta_x11_background_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (object); MetaX11BackgroundActorPrivate *priv = self->priv; switch (prop_id) { case PROP_DIM_FACTOR: g_value_set_float (value, priv->dim_factor); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_x11_background_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaX11BackgroundActor *self = META_X11_BACKGROUND_ACTOR (object); switch (prop_id) { case PROP_DIM_FACTOR: meta_x11_background_actor_set_dim_factor (self, g_value_get_float (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_x11_background_actor_class_init (MetaX11BackgroundActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *pspec; object_class->dispose = meta_x11_background_actor_dispose; object_class->get_property = meta_x11_background_actor_get_property; object_class->set_property = meta_x11_background_actor_set_property; actor_class->get_preferred_width = meta_x11_background_actor_get_preferred_width; actor_class->get_preferred_height = meta_x11_background_actor_get_preferred_height; actor_class->get_paint_volume = meta_x11_background_actor_get_paint_volume; /** * MetaX11BackgroundActor:dim-factor: * * Factor to dim the background by, between 0.0 (black) and 1.0 (original * colors) */ pspec = g_param_spec_float ("dim-factor", "Dim factor", "Factor to dim the background by", 0.0, 1.0, 1.0, G_PARAM_READWRITE); obj_props[PROP_DIM_FACTOR] = pspec; g_object_class_install_property (object_class, PROP_DIM_FACTOR, pspec); } static void meta_x11_background_actor_init (MetaX11BackgroundActor *self) { self->priv = meta_x11_background_actor_get_instance_private (self); self->priv->dim_factor = 1.0; self->priv->transition_running = FALSE; } /** * meta_x11_background_actor_new_for_display: * @display: the #MetaDisplay * * Creates a new actor to draw the background for the given display. * * Return value: (transfer none): the newly created background actor */ ClutterActor * meta_x11_background_actor_new_for_display (MetaDisplay *display) { MetaX11BackgroundActor *self; MetaX11BackgroundActorPrivate *priv; g_return_val_if_fail (META_IS_DISPLAY (display), NULL); if (meta_is_wayland_compositor ()) return NULL; self = g_object_new (META_TYPE_X11_BACKGROUND_ACTOR, NULL); priv = self->priv; priv->background = meta_display_background_get (display); priv->background->actors = g_slist_prepend (priv->background->actors, self); priv->bottom_actor = meta_x11_background_new (display); clutter_actor_add_child (CLUTTER_ACTOR (self), priv->bottom_actor); priv->top_actor = meta_x11_background_new (display); clutter_actor_add_child (CLUTTER_ACTOR (self), priv->top_actor); set_texture_on_actors (self); update_wrap_mode_of_actor (self); return CLUTTER_ACTOR (self); } /** * meta_x11_background_actor_update: * @display: a #MetaDisplay * * Refetches the _XROOTPMAP_ID property for the root window and updates * the contents of the background actor based on that. There's no attempt * to optimize out pixmap values that don't change (since a root pixmap * could be replaced by with another pixmap with the same ID under some * circumstances), so this should only be called when we actually receive * a PropertyNotify event for the property. */ void meta_x11_background_actor_update (MetaDisplay *display) { MetaX11Display *x11_display; MetaDisplayBackground *background; Atom type; int format; gulong nitems; gulong bytes_after; guchar *data; Pixmap root_pixmap_id; x11_display = meta_display_get_x11_display (display); background = meta_display_background_get (display); root_pixmap_id = None; if (!XGetWindowProperty (meta_x11_display_get_xdisplay (x11_display), meta_x11_display_get_xroot (x11_display), x11_display->atom_x_root_pixmap, 0, LONG_MAX, False, AnyPropertyType, &type, &format, &nitems, &bytes_after, &data) && type != None) { /* Got a property. */ if (type == XA_PIXMAP && format == 32 && nitems == 1) { /* Was what we expected. */ root_pixmap_id = *(Pixmap *)data; } XFree(data); } if (root_pixmap_id != None) { CoglHandle texture; CoglContext *context = clutter_backend_get_cogl_context (clutter_get_default_backend ()); GError *error = NULL; meta_x11_error_trap_push (x11_display); texture = cogl_texture_pixmap_x11_new (context, root_pixmap_id, FALSE, &error); meta_x11_error_trap_pop (x11_display); if (texture != NULL) { set_texture (background, texture); cogl_object_unref (texture); background->have_pixmap = True; return; } else { g_warning ("Failed to create background texture from pixmap: %s", error->message); g_error_free (error); } } background->have_pixmap = False; set_texture_to_stage_color (background); } /** * meta_x11_background_actor_set_visible_region: * @self: a #MetaX11BackgroundActor * @visible_region: (allow-none): the area of the actor (in allocate-relative * coordinates) that is visible. * * Sets the area of the background that is unobscured by overlapping windows. * This is used to optimize and only paint the visible portions. */ void meta_x11_background_actor_set_visible_region (MetaX11BackgroundActor *self, cairo_region_t *visible_region) { MetaX11BackgroundActorPrivate *priv; g_return_if_fail (META_IS_X11_BACKGROUND_ACTOR (self)); priv = self->priv; if (priv->top_actor != NULL) meta_x11_background_set_visible_region (META_X11_BACKGROUND (priv->top_actor), visible_region); } /** * meta_x11_background_actor_screen_size_changed: * @display: a #MetaScreen * * Called by the compositor when the size of the #MetaScreen changes */ void meta_x11_background_actor_screen_size_changed (MetaDisplay *x11_display) { MetaDisplayBackground *background = meta_display_background_get (x11_display); update_wrap_mode (background); } muffin-6.4.1/src/x11/group-props.c0000664000175000017500000001425214723361714015607 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* MetaGroup property handling */ /* * Copyright (C) 2002 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/group-props.h" #include #include "x11/group-private.h" #include "x11/meta-x11-display-private.h" #include "x11/xprops.h" typedef void (* InitValueFunc) (MetaX11Display *x11_display, Atom property, MetaPropValue *value); typedef void (* ReloadValueFunc) (MetaGroup *group, MetaPropValue *value); struct _MetaGroupPropHooks { Atom property; InitValueFunc init_func; ReloadValueFunc reload_func; }; static void init_prop_value (MetaX11Display *x11_display, Atom property, MetaPropValue *value); static void reload_prop_value (MetaGroup *group, MetaPropValue *value); static MetaGroupPropHooks *find_hooks (MetaX11Display *x11_display, Atom property); void meta_group_reload_property (MetaGroup *group, Atom property) { meta_group_reload_properties (group, &property, 1); } void meta_group_reload_properties (MetaGroup *group, const Atom *properties, int n_properties) { int i; MetaPropValue *values; g_return_if_fail (properties != NULL); g_return_if_fail (n_properties > 0); values = g_new0 (MetaPropValue, n_properties); i = 0; while (i < n_properties) { init_prop_value (group->x11_display, properties[i], &values[i]); ++i; } meta_prop_get_values (group->x11_display, group->group_leader, values, n_properties); i = 0; while (i < n_properties) { reload_prop_value (group, &values[i]); ++i; } meta_prop_free_values (values, n_properties); g_free (values); } /* Fill in the MetaPropValue used to get the value of "property" */ static void init_prop_value (MetaX11Display *x11_display, Atom property, MetaPropValue *value) { MetaGroupPropHooks *hooks; value->type = META_PROP_VALUE_INVALID; value->atom = None; hooks = find_hooks (x11_display, property); if (hooks && hooks->init_func != NULL) (* hooks->init_func) (x11_display, property, value); } static void reload_prop_value (MetaGroup *group, MetaPropValue *value) { MetaGroupPropHooks *hooks; hooks = find_hooks (group->x11_display, value->atom); if (hooks && hooks->reload_func != NULL) (* hooks->reload_func) (group, value); } static void init_wm_client_machine (MetaX11Display *x11_display, Atom property, MetaPropValue *value) { value->type = META_PROP_VALUE_STRING; value->atom = x11_display->atom_WM_CLIENT_MACHINE; } static void reload_wm_client_machine (MetaGroup *group, MetaPropValue *value) { g_free (group->wm_client_machine); group->wm_client_machine = NULL; if (value->type != META_PROP_VALUE_INVALID) group->wm_client_machine = g_strdup (value->v.str); meta_verbose ("Group has client machine \"%s\"\n", group->wm_client_machine ? group->wm_client_machine : "unset"); } static void init_net_startup_id (MetaX11Display *x11_display, Atom property, MetaPropValue *value) { value->type = META_PROP_VALUE_UTF8; value->atom = x11_display->atom__NET_STARTUP_ID; } static void reload_net_startup_id (MetaGroup *group, MetaPropValue *value) { g_free (group->startup_id); group->startup_id = NULL; if (value->type != META_PROP_VALUE_INVALID) group->startup_id = g_strdup (value->v.str); meta_verbose ("Group has startup id \"%s\"\n", group->startup_id ? group->startup_id : "unset"); } #define N_HOOKS 3 void meta_x11_display_init_group_prop_hooks (MetaX11Display *x11_display) { int i; MetaGroupPropHooks *hooks; g_assert (x11_display->group_prop_hooks == NULL); x11_display->group_prop_hooks = g_new0 (MetaGroupPropHooks, N_HOOKS); hooks = x11_display->group_prop_hooks; i = 0; hooks[i].property = x11_display->atom_WM_CLIENT_MACHINE; hooks[i].init_func = init_wm_client_machine; hooks[i].reload_func = reload_wm_client_machine; ++i; hooks[i].property = x11_display->atom__NET_WM_PID; hooks[i].init_func = NULL; hooks[i].reload_func = NULL; ++i; hooks[i].property = x11_display->atom__NET_STARTUP_ID; hooks[i].init_func = init_net_startup_id; hooks[i].reload_func = reload_net_startup_id; ++i; if (i != N_HOOKS) { g_error ("Initialized %d group hooks should have been %d\n", i, N_HOOKS); } } void meta_x11_display_free_group_prop_hooks (MetaX11Display *x11_display) { g_assert (x11_display->group_prop_hooks != NULL); g_free (x11_display->group_prop_hooks); x11_display->group_prop_hooks = NULL; } static MetaGroupPropHooks* find_hooks (MetaX11Display *x11_display, Atom property) { int i; /* FIXME we could sort the array and do binary search or * something */ i = 0; while (i < N_HOOKS) { if (x11_display->group_prop_hooks[i].property == property) return &x11_display->group_prop_hooks[i]; ++i; } return NULL; } muffin-6.4.1/src/x11/meta-x11-selection-output-stream.c0000664000175000017500000005255014723361714021464 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General * Public License along with this library; if not, see . * * Author: Benjamin Otte * Christian Kellner */ #include "config.h" #include "meta-x11-selection-output-stream-private.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" typedef struct _MetaX11SelectionOutputStreamPrivate MetaX11SelectionOutputStreamPrivate; struct _MetaX11SelectionOutputStream { GOutputStream parent_instance; }; struct _MetaX11SelectionOutputStreamPrivate { MetaX11Display *x11_display; Window xwindow; Atom xselection; Atom xtarget; Atom xproperty; Atom xtype; int format; gulong timestamp; GMutex mutex; GCond cond; GByteArray *data; guint flush_requested : 1; GTask *pending_task; guint incr : 1; guint delete_pending : 1; guint pipe_error : 1; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaX11SelectionOutputStream, meta_x11_selection_output_stream, G_TYPE_OUTPUT_STREAM); static size_t get_element_size (int format); static void meta_x11_selection_output_stream_notify_selection (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); XSelectionEvent event; Display *xdisplay; event = (XSelectionEvent) { .type = SelectionNotify, .time = priv->timestamp, .requestor = priv->xwindow, .selection = priv->xselection, .target = priv->xtarget, .property = priv->xproperty, }; meta_x11_error_trap_push (priv->x11_display); xdisplay = priv->x11_display->xdisplay; XSendEvent (xdisplay, priv->xwindow, False, NoEventMask, (XEvent *) &event); XSync (xdisplay, False); meta_x11_error_trap_pop (priv->x11_display); } static gboolean meta_x11_selection_output_stream_can_flush (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (priv->delete_pending) return FALSE; if (!g_output_stream_is_closing (G_OUTPUT_STREAM (stream)) && priv->data->len < get_element_size (priv->format)) return FALSE; return TRUE; } static size_t get_max_request_size (MetaX11Display *display) { size_t size; size = XExtendedMaxRequestSize (display->xdisplay); if (size <= 0) size = XMaxRequestSize (display->xdisplay); return (size - 100) * 4; } static gboolean meta_x11_selection_output_stream_needs_flush_unlocked (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (priv->data->len == 0) { if (priv->incr) return g_output_stream_is_closing (G_OUTPUT_STREAM (stream)); else return FALSE; } if (g_output_stream_is_closing (G_OUTPUT_STREAM (stream))) return TRUE; if (priv->flush_requested) return TRUE; return priv->data->len >= get_max_request_size (priv->x11_display); } static gboolean meta_x11_selection_output_stream_needs_flush (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); gboolean result; g_mutex_lock (&priv->mutex); result = meta_x11_selection_output_stream_needs_flush_unlocked (stream); g_mutex_unlock (&priv->mutex); return result; } static size_t get_element_size (int format) { switch (format) { case 8: return 1; case 16: return sizeof (short); case 32: return sizeof (long); default: g_warning ("Unknown format %u", format); return 1; } } static gboolean meta_x11_selection_output_stream_check_pipe (MetaX11SelectionOutputStream *stream, GError **error) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (priv->pipe_error) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE, "Connection with client was broken"); return FALSE; } return TRUE; } static void meta_x11_selection_output_stream_perform_flush (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); Display *xdisplay; size_t element_size, n_elements, max_size; gboolean first_chunk = FALSE; int error_code; g_assert (!priv->delete_pending); xdisplay = priv->x11_display->xdisplay; /* We operate on a foreign window, better guard against catastrophe */ meta_x11_error_trap_push (priv->x11_display); g_mutex_lock (&priv->mutex); element_size = get_element_size (priv->format); n_elements = priv->data->len / element_size; max_size = get_max_request_size (priv->x11_display); if (!priv->incr) first_chunk = TRUE; if (!priv->incr && priv->data->len > max_size) { XWindowAttributes attrs; priv->incr = TRUE; XGetWindowAttributes (xdisplay, priv->xwindow, &attrs); if (!(attrs.your_event_mask & PropertyChangeMask)) { XSelectInput (xdisplay, priv->xwindow, attrs.your_event_mask | PropertyChangeMask); } XChangeProperty (xdisplay, priv->xwindow, priv->xproperty, XInternAtom (priv->x11_display->xdisplay, "INCR", False), 32, PropModeReplace, (guchar *) &(long) { n_elements }, 1); priv->delete_pending = TRUE; } else { size_t copy_n_elements; if (priv->incr && priv->data->len > 0) priv->delete_pending = TRUE; copy_n_elements = MIN (n_elements, max_size / element_size); XChangeProperty (xdisplay, priv->xwindow, priv->xproperty, priv->xtype, priv->format, PropModeReplace, priv->data->data, copy_n_elements); g_byte_array_remove_range (priv->data, 0, copy_n_elements * element_size); } if (first_chunk) meta_x11_selection_output_stream_notify_selection (stream); g_cond_broadcast (&priv->cond); g_mutex_unlock (&priv->mutex); error_code = meta_x11_error_trap_pop_with_return (priv->x11_display); if (error_code != Success) { char error_str[100]; priv->flush_requested = FALSE; priv->delete_pending = FALSE; priv->pipe_error = TRUE; if (priv->pending_task) { XGetErrorText (xdisplay, error_code, error_str, sizeof (error_str)); g_task_return_new_error (priv->pending_task, G_IO_ERROR, G_IO_ERROR_BROKEN_PIPE, "Failed to flush selection output stream: %s", error_str); g_clear_object (&priv->pending_task); } } else if (priv->pending_task && priv->data->len == 0 && !priv->delete_pending) { size_t result; priv->flush_requested = FALSE; result = GPOINTER_TO_SIZE (g_task_get_task_data (priv->pending_task)); g_task_return_int (priv->pending_task, result); g_clear_object (&priv->pending_task); } } static gboolean meta_x11_selection_output_stream_invoke_flush (gpointer data) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (data); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (meta_x11_selection_output_stream_needs_flush (stream) && meta_x11_selection_output_stream_can_flush (stream)) meta_x11_selection_output_stream_perform_flush (stream); if (priv->delete_pending || priv->data->len > 0) return G_SOURCE_CONTINUE; else return G_SOURCE_REMOVE; } static gssize meta_x11_selection_output_stream_write (GOutputStream *output_stream, const void *buffer, size_t count, GCancellable *cancellable, GError **error) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (output_stream); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (!meta_x11_selection_output_stream_check_pipe (stream, error)) return -1; g_mutex_lock (&priv->mutex); g_byte_array_append (priv->data, buffer, count); g_mutex_unlock (&priv->mutex); g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_flush, stream); g_mutex_lock (&priv->mutex); if (meta_x11_selection_output_stream_needs_flush_unlocked (stream)) g_cond_wait (&priv->cond, &priv->mutex); g_mutex_unlock (&priv->mutex); return count; } static void meta_x11_selection_output_stream_write_async (GOutputStream *output_stream, const void *buffer, size_t count, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (output_stream); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); GError *error = NULL; GTask *task; task = g_task_new (stream, cancellable, callback, user_data); g_task_set_source_tag (task, meta_x11_selection_output_stream_write_async); g_task_set_priority (task, io_priority); if (!meta_x11_selection_output_stream_check_pipe (stream, &error)) { g_task_return_error (task, error); return; } g_mutex_lock (&priv->mutex); g_byte_array_append (priv->data, buffer, count); g_mutex_unlock (&priv->mutex); if (!meta_x11_selection_output_stream_needs_flush (stream)) { g_task_return_int (task, count); g_object_unref (task); return; } else { if (meta_x11_selection_output_stream_can_flush (stream)) meta_x11_selection_output_stream_perform_flush (stream); g_task_return_int (task, count); g_object_unref (task); return; } } static gssize meta_x11_selection_output_stream_write_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, stream), -1); g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == meta_x11_selection_output_stream_write_async, -1); return g_task_propagate_int (G_TASK (result), error); } static gboolean meta_x11_selection_output_request_flush (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); gboolean needs_flush; g_mutex_lock (&priv->mutex); if (priv->data->len > 0) priv->flush_requested = TRUE; needs_flush = meta_x11_selection_output_stream_needs_flush_unlocked (stream); g_mutex_unlock (&priv->mutex); return needs_flush; } static gboolean meta_x11_selection_output_stream_flush (GOutputStream *output_stream, GCancellable *cancellable, GError **error) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (output_stream); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); if (!meta_x11_selection_output_stream_check_pipe (stream, error)) return FALSE; if (!meta_x11_selection_output_request_flush (stream)) return TRUE; g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_flush, stream); g_mutex_lock (&priv->mutex); if (meta_x11_selection_output_stream_needs_flush_unlocked (stream)) g_cond_wait (&priv->cond, &priv->mutex); g_mutex_unlock (&priv->mutex); return TRUE; } static void meta_x11_selection_output_stream_flush_async (GOutputStream *output_stream, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (output_stream); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); GError *error = NULL; GTask *task; task = g_task_new (stream, cancellable, callback, user_data); g_task_set_source_tag (task, meta_x11_selection_output_stream_flush_async); g_task_set_priority (task, io_priority); if (!meta_x11_selection_output_stream_check_pipe (stream, &error)) { g_task_return_error (task, error); return; } if (!meta_x11_selection_output_stream_can_flush (stream)) { if (meta_x11_selection_output_request_flush (stream)) { g_assert (priv->pending_task == NULL); priv->pending_task = task; return; } else { g_task_return_boolean (task, TRUE); g_object_unref (task); return; } } g_assert (priv->pending_task == NULL); priv->pending_task = task; meta_x11_selection_output_stream_perform_flush (stream); } static gboolean meta_x11_selection_output_stream_flush_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); g_return_val_if_fail (g_async_result_is_tagged (result, meta_x11_selection_output_stream_flush_async), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } static gboolean meta_x11_selection_output_stream_invoke_close (gpointer stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); priv->x11_display->selection.output_streams = g_list_remove (priv->x11_display->selection.output_streams, stream); return G_SOURCE_REMOVE; } static gboolean meta_x11_selection_output_stream_close (GOutputStream *stream, GCancellable *cancellable, GError **error) { g_main_context_invoke (NULL, meta_x11_selection_output_stream_invoke_close, stream); return TRUE; } static void meta_x11_selection_output_stream_close_async (GOutputStream *stream, int io_priority, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { GTask *task; task = g_task_new (stream, cancellable, callback, user_data); g_task_set_source_tag (task, meta_x11_selection_output_stream_close_async); g_task_set_priority (task, io_priority); meta_x11_selection_output_stream_invoke_close (stream); g_task_return_boolean (task, TRUE); g_object_unref (task); } static gboolean meta_x11_selection_output_stream_close_finish (GOutputStream *stream, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, stream), FALSE); g_return_val_if_fail (g_async_result_is_tagged (result, meta_x11_selection_output_stream_close_async), FALSE); return g_task_propagate_boolean (G_TASK (result), error); } static void meta_x11_selection_output_stream_dispose (GObject *object) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (object); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); priv->x11_display->selection.output_streams = g_list_remove (priv->x11_display->selection.output_streams, stream); G_OBJECT_CLASS (meta_x11_selection_output_stream_parent_class)->dispose (object); } static void meta_x11_selection_output_stream_finalize (GObject *object) { MetaX11SelectionOutputStream *stream = META_X11_SELECTION_OUTPUT_STREAM (object); MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); g_byte_array_unref (priv->data); g_cond_clear (&priv->cond); g_mutex_clear (&priv->mutex); G_OBJECT_CLASS (meta_x11_selection_output_stream_parent_class)->finalize (object); } static void meta_x11_selection_output_stream_class_init (MetaX11SelectionOutputStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (klass); object_class->dispose = meta_x11_selection_output_stream_dispose; object_class->finalize = meta_x11_selection_output_stream_finalize; output_stream_class->write_fn = meta_x11_selection_output_stream_write; output_stream_class->flush = meta_x11_selection_output_stream_flush; output_stream_class->close_fn = meta_x11_selection_output_stream_close; output_stream_class->write_async = meta_x11_selection_output_stream_write_async; output_stream_class->write_finish = meta_x11_selection_output_stream_write_finish; output_stream_class->flush_async = meta_x11_selection_output_stream_flush_async; output_stream_class->flush_finish = meta_x11_selection_output_stream_flush_finish; output_stream_class->close_async = meta_x11_selection_output_stream_close_async; output_stream_class->close_finish = meta_x11_selection_output_stream_close_finish; } static void meta_x11_selection_output_stream_init (MetaX11SelectionOutputStream *stream) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); g_mutex_init (&priv->mutex); g_cond_init (&priv->cond); priv->data = g_byte_array_new (); } gboolean meta_x11_selection_output_stream_xevent (MetaX11SelectionOutputStream *stream, const XEvent *xevent) { MetaX11SelectionOutputStreamPrivate *priv = meta_x11_selection_output_stream_get_instance_private (stream); Display *xdisplay = priv->x11_display->xdisplay; if (xevent->xany.display != xdisplay || xevent->xany.window != priv->xwindow) return FALSE; switch (xevent->type) { case PropertyNotify: if (!priv->incr || xevent->xproperty.atom != priv->xproperty || xevent->xproperty.state != PropertyDelete) return FALSE; priv->delete_pending = FALSE; if (meta_x11_selection_output_stream_needs_flush (stream) && meta_x11_selection_output_stream_can_flush (stream)) meta_x11_selection_output_stream_perform_flush (stream); return FALSE; default: return FALSE; } } GOutputStream * meta_x11_selection_output_stream_new (MetaX11Display *x11_display, Window requestor, const char *selection, const char *target, const char *property, const char *type, int format, gulong timestamp) { MetaX11SelectionOutputStream *stream; MetaX11SelectionOutputStreamPrivate *priv; stream = g_object_new (META_TYPE_X11_SELECTION_OUTPUT_STREAM, NULL); priv = meta_x11_selection_output_stream_get_instance_private (stream); x11_display->selection.output_streams = g_list_prepend (x11_display->selection.output_streams, stream); priv->x11_display = x11_display; priv->xwindow = requestor; priv->xselection = XInternAtom (x11_display->xdisplay, selection, False); priv->xtarget = XInternAtom (x11_display->xdisplay, target, False); priv->xproperty = XInternAtom (x11_display->xdisplay, property, False); priv->xtype = XInternAtom (x11_display->xdisplay, type, False); priv->format = format; priv->timestamp = timestamp; return G_OUTPUT_STREAM (stream); } muffin-6.4.1/src/x11/meta-x11-selection.c0000664000175000017500000004434514723361714016640 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "core/meta-selection-private.h" #include "meta/meta-selection-source-memory.h" #include "x11/meta-selection-source-x11-private.h" #include "x11/meta-x11-selection-output-stream-private.h" #include "x11/meta-x11-selection-private.h" #define UTF8_STRING_MIMETYPE "text/plain;charset=utf-8" #define STRING_MIMETYPE "text/plain" static gboolean atom_to_selection_type (Display *xdisplay, Atom selection, MetaSelectionType *selection_type) { if (selection == XInternAtom (xdisplay, "PRIMARY", False)) *selection_type = META_SELECTION_PRIMARY; else if (selection == XInternAtom (xdisplay, "CLIPBOARD", False)) *selection_type = META_SELECTION_CLIPBOARD; else if (selection == XInternAtom (xdisplay, "XdndSelection", False)) *selection_type = META_SELECTION_DND; else return FALSE; return TRUE; } static Atom selection_to_atom (MetaSelectionType type, Display *xdisplay) { Atom atom; switch (type) { case META_SELECTION_PRIMARY: atom = XInternAtom (xdisplay, "PRIMARY", False); break; case META_SELECTION_CLIPBOARD: atom = XInternAtom (xdisplay, "CLIPBOARD", False); break; case META_SELECTION_DND: atom = XInternAtom (xdisplay, "XdndSelection", False); break; default: g_warn_if_reached (); atom = None; break; } return atom; } static GBytes * mimetypes_to_bytes (GList *mimetypes, Display *xdisplay) { GArray *atoms = g_array_new (FALSE, FALSE, sizeof (Atom)); GList *l; char *mimetype; Atom atom; gboolean utf8_string_found = FALSE, utf8_string_mimetype_found = FALSE; gboolean string_found = FALSE, string_mimetype_found = FALSE; GBytes *bytes; for (l = mimetypes; l; l = l->next) { mimetype = l->data; atom = XInternAtom (xdisplay, mimetype, False); g_array_append_val (atoms, atom); utf8_string_mimetype_found |= strcmp (mimetype, UTF8_STRING_MIMETYPE) == 0; utf8_string_found |= strcmp (mimetype, "UTF8_STRING") == 0; string_mimetype_found |= strcmp (mimetype, STRING_MIMETYPE) == 0; string_found |= strcmp (mimetype, "STRING") == 0; } /* Some X11 clients can only handle STRING/UTF8_STRING but not the * corresponding mimetypes. */ if (utf8_string_mimetype_found && !utf8_string_found) { atom = XInternAtom (xdisplay, "UTF8_STRING", False); g_array_append_val (atoms, atom); } if (string_mimetype_found && !string_found) { atom = XInternAtom (xdisplay, "STRING", False); g_array_append_val (atoms, atom); } atom = XInternAtom (xdisplay, "TARGETS", False); g_array_append_val (atoms, atom); atom = XInternAtom (xdisplay, "TIMESTAMP", False); g_array_append_val (atoms, atom); bytes = g_bytes_new_take (atoms->data, atoms->len * sizeof (Atom)); g_array_free (atoms, FALSE); return bytes; } static void send_selection_notify (XSelectionRequestEvent *request_event, gboolean accepted) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XSelectionEvent event; memset(&event, 0, sizeof (XSelectionEvent)); event.type = SelectionNotify; event.time = request_event->time; event.requestor = request_event->requestor; event.selection = request_event->selection; event.target = request_event->target; event.property = accepted ? request_event->property : None; XSendEvent (xdisplay, request_event->requestor, False, NoEventMask, (XEvent *) &event); } static void write_mimetypes_cb (GOutputStream *stream, GAsyncResult *res, gpointer user_data) { GError *error = NULL; g_output_stream_write_bytes_finish (stream, res, &error); g_output_stream_close (stream, NULL, NULL); g_object_unref (stream); if (error) { g_warning ("Could not fetch selection mimetypes: %s\n", error->message); g_error_free (error); } } static void transfer_cb (MetaSelection *selection, GAsyncResult *res, GOutputStream *output) { GError *error = NULL; if (!meta_selection_transfer_finish (selection, res, &error)) { g_warning ("Error writing data to X11 selection: %s", error->message); g_error_free (error); } g_output_stream_close (output, NULL, NULL); g_object_unref (output); } static char * meta_x11_selection_find_target (MetaX11Display *x11_display, MetaSelection *selection, MetaSelectionType selection_type, Atom selection_atom) { GList* mimetypes = NULL; const gchar *atom_name; char *retval; mimetypes = meta_selection_get_mimetypes (selection, selection_type); atom_name = gdk_x11_get_xatom_name (selection_atom); if (g_list_find_custom (mimetypes, atom_name, (GCompareFunc) g_strcmp0)) { retval = g_strdup (atom_name); } else if (strcmp (atom_name, "UTF8_STRING") == 0 && g_list_find_custom (mimetypes, UTF8_STRING_MIMETYPE, (GCompareFunc) g_strcmp0)) { retval = g_strdup (UTF8_STRING_MIMETYPE); } else if (strcmp (atom_name, "STRING") == 0 && g_list_find_custom (mimetypes, STRING_MIMETYPE, (GCompareFunc) g_strcmp0)) { retval = g_strdup (STRING_MIMETYPE); } else { retval = NULL; } g_list_free_full (mimetypes, g_free); return retval; } static gboolean meta_x11_selection_handle_selection_request (MetaX11Display *x11_display, XEvent *xevent) { XSelectionRequestEvent *event = (XSelectionRequestEvent *) xevent; MetaSelectionType selection_type; MetaSelection *selection; GOutputStream *output; GList *mimetypes; if (!atom_to_selection_type (x11_display->xdisplay, event->selection, &selection_type)) return FALSE; if (x11_display->selection.xwindow != event->owner) return FALSE; selection = meta_display_get_selection (meta_get_display ()); if (event->target == gdk_x11_get_xatom_by_name ("TARGETS")) { GBytes *bytes; mimetypes = meta_selection_get_mimetypes (selection, selection_type); if (!mimetypes) { send_selection_notify (event, FALSE); return FALSE; } output = meta_x11_selection_output_stream_new (x11_display, event->requestor, gdk_x11_get_xatom_name (event->selection), gdk_x11_get_xatom_name (event->target), gdk_x11_get_xatom_name (event->property), "ATOM", 32, event->time); bytes = mimetypes_to_bytes (mimetypes, x11_display->xdisplay); g_list_free_full (mimetypes, g_free); g_output_stream_write_bytes_async (output, bytes, G_PRIORITY_DEFAULT, NULL, (GAsyncReadyCallback) write_mimetypes_cb, output); g_bytes_unref (bytes); return TRUE; } else if (event->target == gdk_x11_get_xatom_by_name ("DELETE")) { /* DnD only, this is just handled through other means on our non-x11 * sources, so just go with it. */ send_selection_notify (event, TRUE); } else { g_autofree char *target = NULL; target = meta_x11_selection_find_target (x11_display, selection, selection_type, event->target); if (target != NULL) { output = meta_x11_selection_output_stream_new (x11_display, event->requestor, gdk_x11_get_xatom_name (event->selection), gdk_x11_get_xatom_name (event->target), gdk_x11_get_xatom_name (event->property), gdk_x11_get_xatom_name (event->target), 8, event->time); meta_selection_transfer_async (selection, selection_type, target, -1, output, NULL, (GAsyncReadyCallback) transfer_cb, output); return TRUE; } else { send_selection_notify (event, FALSE); } } return FALSE; } typedef struct { MetaX11Display *x11_display; MetaSelection *selection; MetaSelectionType selection_type; } SourceNewData; static void source_new_cb (GObject *object, GAsyncResult *res, gpointer user_data) { MetaSelectionSource *source; SourceNewData *data = user_data; MetaSelection *selection = data->selection; MetaSelectionType selection_type = data->selection_type; MetaX11Display *x11_display = data->x11_display; g_autoptr (GError) error = NULL; source = meta_selection_source_x11_new_finish (res, &error); if (source) { g_set_object (&x11_display->selection.owners[selection_type], source); meta_selection_set_owner (selection, selection_type, source); g_object_unref (source); } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_warning ("Could not create selection source for X11: %s", error->message); } g_free (data); } static gboolean unset_clipboard_owner (gpointer data) { MetaDisplay *display = meta_get_display (); MetaSelection *selection = meta_display_get_selection (display); MetaX11Display *x11_display = meta_display_get_x11_display (display); meta_selection_unset_owner (selection, META_SELECTION_CLIPBOARD, x11_display->selection.owners[META_SELECTION_CLIPBOARD]); g_clear_object (&x11_display->selection.owners[META_SELECTION_CLIPBOARD]); x11_display->selection.timeout_id = 0; return G_SOURCE_REMOVE; } static gboolean meta_x11_selection_handle_xfixes_selection_notify (MetaX11Display *x11_display, XEvent *xevent) { XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *) xevent; Display *xdisplay = x11_display->xdisplay; MetaSelectionType selection_type; MetaSelection *selection; if (!atom_to_selection_type (xdisplay, event->selection, &selection_type)) return FALSE; selection = meta_display_get_selection (meta_get_display ()); if (selection_type == META_SELECTION_CLIPBOARD) g_clear_handle_id (&x11_display->selection.timeout_id, g_source_remove); if (x11_display->selection.cancellables[selection_type]) { g_cancellable_cancel (x11_display->selection.cancellables[selection_type]); g_clear_object (&x11_display->selection.cancellables[selection_type]); } x11_display->selection.cancellables[selection_type] = g_cancellable_new (); if (event->owner == None && x11_display->selection.owners[selection_type]) { if (event->subtype == XFixesSetSelectionOwnerNotify) { MetaSelectionSource *source; /* Replace with an empty owner */ source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); g_set_object (&x11_display->selection.owners[selection_type], source); meta_selection_set_owner (selection, selection_type, source); g_object_unref (source); } else if (event->subtype == XFixesSelectionWindowDestroyNotify && selection_type == META_SELECTION_CLIPBOARD) { /* Selection window might have gotten destroyed as part of application * shutdown. Trigger restoring clipboard, but wait a bit, because some * clients, like wine, destroy the old window immediately before a new * selection. Restoring the clipboard in this case would overwrite the * new selection, so this will be cancelled when a new selection * arrives. */ x11_display->selection.timeout_id = g_timeout_add (10, unset_clipboard_owner, NULL); } else { /* An X client went away, clear the selection */ meta_selection_unset_owner (selection, selection_type, x11_display->selection.owners[selection_type]); g_clear_object (&x11_display->selection.owners[selection_type]); } } else if (event->owner != None && event->owner != x11_display->selection.xwindow) { SourceNewData *data; data = g_new (SourceNewData, 1); data->x11_display = x11_display; data->selection = selection; data->selection_type = selection_type; meta_selection_source_x11_new_async (x11_display, event->owner, event->timestamp, event->selection, x11_display->selection.cancellables[selection_type], source_new_cb, data); } return TRUE; } gboolean meta_x11_selection_handle_event (MetaX11Display *x11_display, XEvent *xevent) { if (xevent->type == SelectionRequest) return meta_x11_selection_handle_selection_request (x11_display, xevent); else if (xevent->type - x11_display->xfixes_event_base == XFixesSelectionNotify) return meta_x11_selection_handle_xfixes_selection_notify (x11_display, xevent); return FALSE; } static void notify_selection_owner (MetaX11Display *x11_display, MetaSelectionType selection_type, MetaSelectionSource *new_owner) { Display *xdisplay = x11_display->xdisplay; if (new_owner && new_owner != x11_display->selection.owners[selection_type]) { if (x11_display->selection.cancellables[selection_type]) { g_cancellable_cancel (x11_display->selection.cancellables[selection_type]); g_clear_object (&x11_display->selection.cancellables[selection_type]); } /* If the owner is non-X11, claim the selection on our selection * window, so X11 apps can interface with it. */ XSetSelectionOwner (xdisplay, selection_to_atom (selection_type, xdisplay), x11_display->selection.xwindow, META_CURRENT_TIME); } } void meta_x11_selection_init (MetaX11Display *x11_display) { XSetWindowAttributes attributes = { 0 }; MetaDisplay *display = meta_get_display (); MetaSelection *selection; guint mask, i; attributes.event_mask = PropertyChangeMask | SubstructureNotifyMask; attributes.override_redirect = True; x11_display->selection.timeout_id = 0; x11_display->selection.xwindow = XCreateWindow (x11_display->xdisplay, x11_display->xroot, -1, -1, 1, 1, 0, /* border width */ 0, /* depth */ InputOnly, /* class */ CopyFromParent, /* visual */ CWEventMask | CWOverrideRedirect, &attributes); mask = XFixesSetSelectionOwnerNotifyMask | XFixesSelectionWindowDestroyNotifyMask | XFixesSelectionClientCloseNotifyMask; selection = meta_display_get_selection (display); for (i = 0; i < META_N_SELECTION_TYPES; i++) { MetaSelectionSource *owner; XFixesSelectSelectionInput (x11_display->xdisplay, x11_display->selection.xwindow, selection_to_atom (i, x11_display->xdisplay), mask); owner = meta_selection_get_current_owner (selection, i); notify_selection_owner (x11_display, i, owner); } g_signal_connect_swapped (selection, "owner-changed", G_CALLBACK (notify_selection_owner), x11_display); } void meta_x11_selection_shutdown (MetaX11Display *x11_display) { MetaDisplay *display = meta_get_display (); guint i; g_signal_handlers_disconnect_by_func (meta_display_get_selection (display), notify_selection_owner, x11_display); for (i = 0; i < META_N_SELECTION_TYPES; i++) { g_clear_object (&x11_display->selection.owners[i]); if (x11_display->selection.cancellables[i]) { g_cancellable_cancel (x11_display->selection.cancellables[i]); g_clear_object (&x11_display->selection.cancellables[i]); } } if (x11_display->selection.xwindow != None) { XDestroyWindow (x11_display->xdisplay, x11_display->selection.xwindow); x11_display->selection.xwindow = None; } g_clear_handle_id (&x11_display->selection.timeout_id, g_source_remove); } muffin-6.4.1/src/x11/window-x11.h0000664000175000017500000001137414723361714015237 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_WINDOW_X11_H #define META_WINDOW_X11_H #include #include "meta/compositor.h" #include "meta/window.h" G_BEGIN_DECLS #define META_TYPE_WINDOW_X11 (meta_window_x11_get_type()) #define META_WINDOW_X11(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_WINDOW_X11, MetaWindowX11)) #define META_WINDOW_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_WINDOW_X11, MetaWindowX11Class)) #define META_IS_WINDOW_X11(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_WINDOW_X11)) #define META_IS_WINDOW_X11_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_WINDOW_X11)) #define META_WINDOW_X11_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_WINDOW_X11, MetaWindowX11Class)) GType meta_window_x11_get_type (void); typedef struct _MetaWindowX11 MetaWindowX11; typedef struct _MetaWindowX11Class MetaWindowX11Class; G_DEFINE_AUTOPTR_CLEANUP_FUNC (MetaWindowX11, g_object_unref) MetaWindow * meta_window_x11_new (MetaDisplay *display, Window xwindow, gboolean must_be_viewable, MetaCompEffect effect); void meta_window_x11_set_net_wm_state (MetaWindow *window); void meta_window_x11_set_wm_state (MetaWindow *window); void meta_window_x11_set_wm_take_focus (MetaWindow *window, gboolean take_focus); void meta_window_x11_set_wm_ping (MetaWindow *window, gboolean ping); void meta_window_x11_set_wm_delete_window (MetaWindow *window, gboolean delete_window); void meta_window_x11_set_allowed_actions_hint (MetaWindow *window); void meta_window_x11_create_sync_request_alarm (MetaWindow *window); void meta_window_x11_destroy_sync_request_alarm (MetaWindow *window); void meta_window_x11_update_sync_request_counter (MetaWindow *window, gint64 new_counter_value); void meta_window_x11_update_input_region (MetaWindow *window); void meta_window_x11_update_shape_region (MetaWindow *window); void meta_window_x11_recalc_window_type (MetaWindow *window); gboolean meta_window_x11_configure_request (MetaWindow *window, XEvent *event); gboolean meta_window_x11_property_notify (MetaWindow *window, XEvent *event); gboolean meta_window_x11_client_message (MetaWindow *window, XEvent *event); void meta_window_x11_configure_notify (MetaWindow *window, XConfigureEvent *event); Window meta_window_x11_get_toplevel_xwindow (MetaWindow *window); void meta_window_x11_freeze_commits (MetaWindow *window); void meta_window_x11_thaw_commits (MetaWindow *window); void meta_window_x11_set_thaw_after_paint (MetaWindow *window, gboolean thaw_after_paint); gboolean meta_window_x11_should_thaw_after_paint (MetaWindow *window); gboolean meta_window_x11_always_update_shape (MetaWindow *window); void meta_window_x11_surface_rect_to_frame_rect (MetaWindow *window, MetaRectangle *surface_rect, MetaRectangle *frame_rect); void meta_window_x11_surface_rect_to_client_rect (MetaWindow *window, MetaRectangle *surface_rect, MetaRectangle *client_rect); #endif muffin-6.4.1/src/x11/session.h0000664000175000017500000000466714723361714015013 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * \file session.h Session management * * Maps windows to information about their placing and state on startup. * This is window matching, which we have a policy of leaving in general * to programs such as Devil's Pie, but the session manager specification * requires us to do it here. */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_SESSION_H #define META_SESSION_H #include "core/window-private.h" typedef struct _MetaWindowSessionInfo MetaWindowSessionInfo; struct _MetaWindowSessionInfo { /* Fields we use to match against */ char *id; char *res_class; char *res_name; char *title; char *role; MetaWindowType type; /* Information we restore */ GSList *workspace_indices; int stack_position; /* width/height should be multiplied by resize inc and * added to base size; position should be interpreted in * light of gravity. This preserves semantics of the * window size/pos, even if fonts/themes change, etc. */ MetaGravity gravity; MetaRectangle rect; MetaRectangle saved_rect; guint on_all_workspaces : 1; guint minimized : 1; guint maximized : 1; guint stack_position_set : 1; guint geometry_set : 1; guint on_all_workspaces_set : 1; guint minimized_set : 1; guint maximized_set : 1; guint saved_rect_set : 1; }; /* If lookup_saved_state returns something, it should be used, * and then released when you're done with it. */ const MetaWindowSessionInfo* meta_window_lookup_saved_state (MetaWindow *window); void meta_window_release_saved_state (const MetaWindowSessionInfo *info); void meta_session_init (const char *client_id, const char *save_file); void meta_session_shutdown (void); #endif muffin-6.4.1/src/x11/meta-x11-errors.c0000664000175000017500000000415714723361714016164 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington, error trapping inspired by GDK * code copyrighted by the GTK team. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:errors * @title: Errors * @short_description: Mutter X error handling */ #include "config.h" #include "meta/meta-x11-errors.h" #include #include #include #include "x11/meta-x11-display-private.h" /* In GTK+-3.0, the error trapping code was significantly rewritten. The new code * has some neat features (like knowing automatically if a sync is needed or not * and handling errors asynchronously when the error code isn't needed immediately), * but it's basically incompatible with the hacks we played with GTK+-2.0 to * use a custom error handler along with gdk_error_trap_push(). * * Since the main point of our custom error trap was to get the error logged * to the right place, with GTK+-3.0 we simply omit our own error handler and * use the GTK+ handling straight-up. * (See https://bugzilla.gnome.org/show_bug.cgi?id=630216 for restoring logging.) */ void meta_x11_error_trap_push (MetaX11Display *x11_display) { gdk_x11_display_error_trap_push (x11_display->gdk_display); } void meta_x11_error_trap_pop (MetaX11Display *x11_display) { gdk_x11_display_error_trap_pop_ignored (x11_display->gdk_display); } int meta_x11_error_trap_pop_with_return (MetaX11Display *x11_display) { return gdk_x11_display_error_trap_pop (x11_display->gdk_display); } muffin-6.4.1/src/x11/window-x11.c0000664000175000017500000041036414723361714015234 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington, Anders Carlsson * Copyright (C) 2002, 2003 Red Hat, Inc. * Copyright (C) 2003 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/window-x11.h" #include "x11/window-x11-private.h" #include #include #include #include #include #include #include #include "backends/meta-logical-monitor.h" #include "backends/x11/meta-backend-x11.h" #include "core/boxes-private.h" #include "core/frame.h" #include "core/meta-workspace-manager-private.h" #include "core/window-private.h" #include "core/workspace-private.h" #include "meta/common.h" #include "meta/meta-cursor-tracker.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "x11/meta-x11-display-private.h" #include "x11/session.h" #include "x11/window-props.h" #include "x11/xprops.h" #define TAKE_FOCUS_FALLBACK_DELAY_MS 150 enum _MetaGtkEdgeConstraints { META_GTK_EDGE_CONSTRAINT_TOP_TILED = 1 << 0, META_GTK_EDGE_CONSTRAINT_TOP_RESIZABLE = 1 << 1, META_GTK_EDGE_CONSTRAINT_RIGHT_TILED = 1 << 2, META_GTK_EDGE_CONSTRAINT_RIGHT_RESIZABLE = 1 << 3, META_GTK_EDGE_CONSTRAINT_BOTTOM_TILED = 1 << 4, META_GTK_EDGE_CONSTRAINT_BOTTOM_RESIZABLE = 1 << 5, META_GTK_EDGE_CONSTRAINT_LEFT_TILED = 1 << 6, META_GTK_EDGE_CONSTRAINT_LEFT_RESIZABLE = 1 << 7 } MetaGtkEdgeConstraints; G_DEFINE_TYPE_WITH_PRIVATE (MetaWindowX11, meta_window_x11, META_TYPE_WINDOW) static void meta_window_x11_maybe_focus_delayed (MetaWindow *window, GQueue *other_focus_candidates, guint32 timestamp); static void meta_window_x11_init (MetaWindowX11 *window_x11) { window_x11->priv = meta_window_x11_get_instance_private (window_x11); } static void send_icccm_message (MetaWindow *window, Atom atom, guint32 timestamp) { /* This comment and code are from twm, copyright * Open Group, Evans & Sutherland, etc. */ /* * ICCCM Client Messages - Section 4.2.8 of the ICCCM dictates that all * client messages will have the following form: * * event type ClientMessage * message type _XA_WM_PROTOCOLS * window tmp->w * format 32 * data[0] message atom * data[1] time stamp */ XClientMessageEvent ev; MetaX11Display *x11_display = window->display->x11_display; ev.type = ClientMessage; ev.window = window->xwindow; ev.message_type = x11_display->atom_WM_PROTOCOLS; ev.format = 32; ev.data.l[0] = atom; ev.data.l[1] = timestamp; meta_x11_error_trap_push (x11_display); XSendEvent (x11_display->xdisplay, window->xwindow, False, 0, (XEvent*) &ev); meta_x11_error_trap_pop (x11_display); } static Window read_client_leader (MetaDisplay *display, Window xwindow) { Window retval = None; meta_prop_get_window (display->x11_display, xwindow, display->x11_display->atom_WM_CLIENT_LEADER, &retval); return retval; } typedef struct { Window leader; } ClientLeaderData; static gboolean find_client_leader_func (MetaWindow *ancestor, void *data) { ClientLeaderData *d; d = data; d->leader = read_client_leader (ancestor->display, ancestor->xwindow); /* keep going if no client leader found */ return d->leader == None; } static void update_sm_hints (MetaWindow *window) { Window leader; window->xclient_leader = None; window->sm_client_id = NULL; /* If not on the current window, we can get the client * leader from transient parents. If we find a client * leader, we read the SM_CLIENT_ID from it. */ leader = read_client_leader (window->display, window->xwindow); if (leader == None) { ClientLeaderData d; d.leader = None; meta_window_foreach_ancestor (window, find_client_leader_func, &d); leader = d.leader; } if (leader != None) { window->xclient_leader = leader; meta_prop_get_latin1_string (window->display->x11_display, leader, window->display->x11_display->atom_SM_CLIENT_ID, &window->sm_client_id); } else { meta_verbose ("Didn't find a client leader for %s\n", window->desc); if (!meta_prefs_get_disable_workarounds ()) { /* Some broken apps (kdelibs fault?) set SM_CLIENT_ID on the app * instead of the client leader */ meta_prop_get_latin1_string (window->display->x11_display, window->xwindow, window->display->x11_display->atom_SM_CLIENT_ID, &window->sm_client_id); if (window->sm_client_id) meta_warning ("Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER window as specified in the ICCCM.\n", window->desc); } } meta_verbose ("Window %s client leader: 0x%lx SM_CLIENT_ID: '%s'\n", window->desc, window->xclient_leader, window->sm_client_id ? window->sm_client_id : "none"); } static void send_configure_notify (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); XEvent event; g_assert (!window->override_redirect); /* from twm */ event.type = ConfigureNotify; event.xconfigure.display = x11_display->xdisplay; event.xconfigure.event = window->xwindow; event.xconfigure.window = window->xwindow; event.xconfigure.x = priv->client_rect.x - priv->border_width; event.xconfigure.y = priv->client_rect.y - priv->border_width; if (window->frame) { if (window->withdrawn) { MetaFrameBorders borders; /* We reparent the client window and put it to the position * where the visible top-left of the frame window currently is. */ meta_frame_calc_borders (window->frame, &borders); event.xconfigure.x = window->frame->rect.x + borders.invisible.left; event.xconfigure.y = window->frame->rect.y + borders.invisible.top; } else { /* Need to be in root window coordinates */ event.xconfigure.x += window->frame->rect.x; event.xconfigure.y += window->frame->rect.y; } } event.xconfigure.width = priv->client_rect.width; event.xconfigure.height = priv->client_rect.height; event.xconfigure.border_width = priv->border_width; /* requested not actual */ event.xconfigure.above = None; /* FIXME */ event.xconfigure.override_redirect = False; meta_topic (META_DEBUG_GEOMETRY, "Sending synthetic configure notify to %s with x: %d y: %d w: %d h: %d\n", window->desc, event.xconfigure.x, event.xconfigure.y, event.xconfigure.width, event.xconfigure.height); meta_x11_error_trap_push (x11_display); XSendEvent (x11_display->xdisplay, window->xwindow, False, StructureNotifyMask, &event); meta_x11_error_trap_pop (x11_display); } static void adjust_for_gravity (MetaWindow *window, gboolean coords_assume_border, MetaGravity gravity, MetaRectangle *rect) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); int ref_x, ref_y; int bw; int child_x, child_y; int frame_width, frame_height; MetaFrameBorders borders; /* We're computing position to pass to window_move, which is * the position of the client window (META_GRAVITY_STATIC basically) * * (see WM spec description of gravity computation, but note that * their formulas assume we're honoring the border width, rather * than compensating for having turned it off) */ if (gravity == META_GRAVITY_STATIC) return; if (coords_assume_border) bw = priv->border_width; else bw = 0; meta_frame_calc_borders (window->frame, &borders); child_x = borders.visible.left; child_y = borders.visible.top; frame_width = child_x + rect->width + borders.visible.right; frame_height = child_y + rect->height + borders.visible.bottom; /* Calculate the the reference point, which is the corner of the * outer window specified by the gravity. So, META_GRAVITY_NORTH_EAST * would have the reference point as the top-right corner of the * outer window. */ ref_x = rect->x; ref_y = rect->y; switch (gravity) { case META_GRAVITY_NORTH: case META_GRAVITY_CENTER: case META_GRAVITY_SOUTH: ref_x += rect->width / 2 + bw; break; case META_GRAVITY_NORTH_EAST: case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: ref_x += rect->width + bw * 2; break; default: break; } switch (gravity) { case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_EAST: ref_y += rect->height / 2 + bw; break; case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_EAST: ref_y += rect->height + bw * 2; break; default: break; } /* Find the top-left corner of the outer window from * the reference point. */ rect->x = ref_x; rect->y = ref_y; switch (gravity) { case META_GRAVITY_NORTH: case META_GRAVITY_CENTER: case META_GRAVITY_SOUTH: rect->x -= frame_width / 2; break; case META_GRAVITY_NORTH_EAST: case META_GRAVITY_EAST: case META_GRAVITY_SOUTH_EAST: rect->x -= frame_width; break; default: break; } switch (gravity) { case META_GRAVITY_WEST: case META_GRAVITY_CENTER: case META_GRAVITY_EAST: rect->y -= frame_height / 2; break; case META_GRAVITY_SOUTH_WEST: case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_EAST: rect->y -= frame_height; break; default: break; } /* Adjust to get the top-left corner of the inner window. */ rect->x += child_x; rect->y += child_y; } static void meta_window_apply_session_info (MetaWindow *window, const MetaWindowSessionInfo *info) { if (info->stack_position_set) { meta_topic (META_DEBUG_SM, "Restoring stack position %d for window %s\n", info->stack_position, window->desc); /* FIXME well, I'm not sure how to do this. */ } if (info->minimized_set) { meta_topic (META_DEBUG_SM, "Restoring minimized state %d for window %s\n", info->minimized, window->desc); if (info->minimized) meta_window_minimize (window); } if (info->maximized_set) { meta_topic (META_DEBUG_SM, "Restoring maximized state %d for window %s\n", info->maximized, window->desc); if (window->has_maximize_func && info->maximized) { meta_window_maximize (window, META_MAXIMIZE_BOTH); if (info->saved_rect_set) { meta_topic (META_DEBUG_SM, "Restoring saved rect %d,%d %dx%d for window %s\n", info->saved_rect.x, info->saved_rect.y, info->saved_rect.width, info->saved_rect.height, window->desc); window->saved_rect.x = info->saved_rect.x; window->saved_rect.y = info->saved_rect.y; window->saved_rect.width = info->saved_rect.width; window->saved_rect.height = info->saved_rect.height; } } } if (info->on_all_workspaces_set) { window->on_all_workspaces_requested = info->on_all_workspaces; meta_window_on_all_workspaces_changed (window); meta_topic (META_DEBUG_SM, "Restoring sticky state %d for window %s\n", window->on_all_workspaces_requested, window->desc); } if (info->workspace_indices) { GSList *tmp; GSList *spaces; spaces = NULL; tmp = info->workspace_indices; while (tmp != NULL) { MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaWorkspace *space; space = meta_workspace_manager_get_workspace_by_index (workspace_manager, GPOINTER_TO_INT (tmp->data)); if (space) spaces = g_slist_prepend (spaces, space); tmp = tmp->next; } if (spaces) { /* XXX: What should we do if there's more than one workspace * listed? We only support one workspace for each window. * * For now, just choose the first one. */ MetaWorkspace *workspace = spaces->data; meta_window_change_workspace (window, workspace); window->initial_workspace_set = TRUE; meta_topic (META_DEBUG_SM, "Restoring saved window %s to workspace %d\n", window->desc, meta_workspace_index (workspace)); g_slist_free (spaces); } } if (info->geometry_set) { MetaRectangle rect; MetaMoveResizeFlags flags; MetaGravity gravity; window->placed = TRUE; /* don't do placement algorithms later */ rect.x = info->rect.x; rect.y = info->rect.y; rect.width = window->size_hints.base_width + info->rect.width * window->size_hints.width_inc; rect.height = window->size_hints.base_height + info->rect.height * window->size_hints.height_inc; /* Force old gravity, ignoring anything now set */ window->size_hints.win_gravity = info->gravity; gravity = window->size_hints.win_gravity; flags = META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; adjust_for_gravity (window, FALSE, gravity, &rect); meta_window_client_rect_to_frame_rect (window, &rect, &rect); meta_window_move_resize_internal (window, flags, gravity, rect); } } static void meta_window_x11_manage (MetaWindow *window) { MetaDisplay *display = window->display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); meta_icon_cache_init (&priv->icon_cache); meta_x11_display_register_x_window (display->x11_display, &window->xwindow, window); /* assign the window to its group, or create a new group if needed */ window->group = NULL; window->xgroup_leader = None; meta_window_compute_group (window); meta_window_load_initial_properties (window); if (!window->override_redirect) update_sm_hints (window); /* must come after transient_for */ if (window->decorated) meta_window_ensure_frame (window); /* Now try applying saved stuff from the session */ { const MetaWindowSessionInfo *info; info = meta_window_lookup_saved_state (window); if (info) { meta_window_apply_session_info (window, info); meta_window_release_saved_state (info); } } /* For override-redirect windows, save the client rect * directly. window->rect was assigned from the XWindowAttributes * in the main meta_window_shared_new. * * For normal windows, do a full ConfigureRequest based on the * window hints, as that's what the ICCCM says to do. */ priv->client_rect = window->rect; window->buffer_rect = window->rect; if (!window->override_redirect) { MetaRectangle rect; MetaMoveResizeFlags flags; MetaGravity gravity = window->size_hints.win_gravity; rect.x = window->size_hints.x; rect.y = window->size_hints.y; rect.width = window->size_hints.width; rect.height = window->size_hints.height; flags = META_MOVE_RESIZE_CONFIGURE_REQUEST | META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION; adjust_for_gravity (window, TRUE, gravity, &rect); meta_window_client_rect_to_frame_rect (window, &rect, &rect); meta_window_move_resize_internal (window, flags, gravity, rect); } meta_window_x11_update_shape_region (window); meta_window_x11_update_input_region (window); } static void meta_window_x11_unmanage (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); meta_x11_error_trap_push (x11_display); meta_window_x11_destroy_sync_request_alarm (window); if (window->withdrawn) { /* We need to clean off the window's state so it * won't be restored if the app maps it again. */ meta_verbose ("Cleaning state from window %s\n", window->desc); XDeleteProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_DESKTOP); XDeleteProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_STATE); XDeleteProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_FULLSCREEN_MONITORS); meta_window_x11_set_wm_state (window); } else { /* We need to put WM_STATE so that others will understand it on * restart. */ if (!window->minimized) meta_window_x11_set_wm_state (window); /* If we're unmanaging a window that is not withdrawn, then * either (a) mutter is exiting, in which case we need to map * the window so the next WM will know that it's not Withdrawn, * or (b) we want to create a new MetaWindow to replace the * current one, which will happen automatically if we re-map * the X Window. */ XMapWindow (x11_display->xdisplay, window->xwindow); } meta_x11_display_unregister_x_window (x11_display, window->xwindow); /* Put back anything we messed up */ if (priv->border_width != 0) XSetWindowBorderWidth (x11_display->xdisplay, window->xwindow, priv->border_width); /* No save set */ XRemoveFromSaveSet (x11_display->xdisplay, window->xwindow); /* Even though the window is now unmanaged, we can't unselect events. This * window might be a window from this process, like a GdkMenu, in * which case it will have pointer events and so forth selected * for it by GDK. There's no way to disentangle those events from the events * we've selected. Even for a window from a different X client, * GDK could also have selected events for it for IPC purposes, so we * can't unselect in that case either. * * Similarly, we can't unselected for events on window->user_time_window. * It might be our own GDK focus window, or it might be a window that a * different client is using for multiple different things: * _NET_WM_USER_TIME_WINDOW and IPC, perhaps. */ if (window->user_time_window != None) { meta_x11_display_unregister_x_window (x11_display, window->user_time_window); window->user_time_window = None; } if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) XShapeSelectInput (x11_display->xdisplay, window->xwindow, NoEventMask); meta_window_ungrab_keys (window); meta_display_ungrab_window_buttons (window->display, window->xwindow); meta_display_ungrab_focus_window_button (window->display, window); meta_x11_error_trap_pop (x11_display); if (window->frame) { /* The XReparentWindow call in meta_window_destroy_frame() moves the * window so we need to send a configure notify; see bug 399552. (We * also do this just in case a window got unmaximized.) */ send_configure_notify (window); meta_window_destroy_frame (window); } } void meta_window_x11_set_wm_ping (MetaWindow *window, gboolean ping) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); priv->wm_ping = ping; } static gboolean meta_window_x11_can_ping (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); return priv->wm_ping; } static void meta_window_x11_ping (MetaWindow *window, guint32 serial) { MetaDisplay *display = window->display; send_icccm_message (window, display->x11_display->atom__NET_WM_PING, serial); } void meta_window_x11_set_wm_delete_window (MetaWindow *window, gboolean delete_window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); priv->wm_delete_window = delete_window; } static void meta_window_x11_delete (MetaWindow *window, guint32 timestamp) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); MetaX11Display *x11_display = window->display->x11_display; meta_x11_error_trap_push (x11_display); if (priv->wm_delete_window) { meta_topic (META_DEBUG_WINDOW_OPS, "Deleting %s with delete_window request\n", window->desc); send_icccm_message (window, x11_display->atom_WM_DELETE_WINDOW, timestamp); } else { meta_topic (META_DEBUG_WINDOW_OPS, "Deleting %s with explicit kill\n", window->desc); XKillClient (x11_display->xdisplay, window->xwindow); } meta_x11_error_trap_pop (x11_display); } static void meta_window_x11_kill (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; meta_topic (META_DEBUG_WINDOW_OPS, "Disconnecting %s with XKillClient()\n", window->desc); meta_x11_error_trap_push (x11_display); XKillClient (x11_display->xdisplay, window->xwindow); meta_x11_error_trap_pop (x11_display); } static void request_take_focus (MetaWindow *window, guint32 timestamp) { MetaDisplay *display = window->display; meta_topic (META_DEBUG_FOCUS, "WM_TAKE_FOCUS(%s, %u)\n", window->desc, timestamp); send_icccm_message (window, display->x11_display->atom_WM_TAKE_FOCUS, timestamp); } typedef struct { MetaWindow *window; GQueue *pending_focus_candidates; guint32 timestamp; guint timeout_id; gulong unmanaged_id; gulong focused_changed_id; } MetaWindowX11DelayedFocusData; static void disconnect_pending_focus_window_signals (MetaWindow *window, GQueue *focus_candidates) { g_signal_handlers_disconnect_by_func (window, g_queue_remove, focus_candidates); } static void meta_window_x11_delayed_focus_data_free (MetaWindowX11DelayedFocusData *data) { g_clear_signal_handler (&data->unmanaged_id, data->window); g_clear_signal_handler (&data->focused_changed_id, data->window->display); if (data->pending_focus_candidates) { g_queue_foreach (data->pending_focus_candidates, (GFunc) disconnect_pending_focus_window_signals, data->pending_focus_candidates); g_queue_free (data->pending_focus_candidates); } g_clear_handle_id (&data->timeout_id, g_source_remove); g_free (data); } static void focus_candidates_maybe_take_and_focus_next (GQueue **focus_candidates_ptr, guint32 timestamp) { MetaWindow *focus_window; GQueue *focus_candidates; g_assert (*focus_candidates_ptr); if (g_queue_is_empty (*focus_candidates_ptr)) return; focus_candidates = g_steal_pointer (focus_candidates_ptr); focus_window = g_queue_pop_head (focus_candidates); disconnect_pending_focus_window_signals (focus_window, focus_candidates); meta_window_x11_maybe_focus_delayed (focus_window, focus_candidates, timestamp); } static gboolean focus_window_delayed_timeout (gpointer user_data) { MetaWindowX11DelayedFocusData *data = user_data; MetaWindow *window = data->window; guint32 timestamp = data->timestamp; focus_candidates_maybe_take_and_focus_next (&data->pending_focus_candidates, timestamp); data->timeout_id = 0; meta_window_x11_delayed_focus_data_free (data); meta_window_focus (window, timestamp); return G_SOURCE_REMOVE; } static void meta_window_x11_maybe_focus_delayed (MetaWindow *window, GQueue *other_focus_candidates, guint32 timestamp) { MetaWindowX11DelayedFocusData *data; data = g_new0 (MetaWindowX11DelayedFocusData, 1); data->window = window; data->timestamp = timestamp; data->pending_focus_candidates = other_focus_candidates; meta_topic (META_DEBUG_FOCUS, "Requesting delayed focus to %s\n", window->desc); data->unmanaged_id = g_signal_connect_swapped (window, "unmanaged", G_CALLBACK (meta_window_x11_delayed_focus_data_free), data); data->focused_changed_id = g_signal_connect_swapped (window->display, "notify::focus-window", G_CALLBACK (meta_window_x11_delayed_focus_data_free), data); data->timeout_id = g_timeout_add (TAKE_FOCUS_FALLBACK_DELAY_MS, focus_window_delayed_timeout, data); } static void maybe_focus_default_window (MetaDisplay *display, MetaWindow *not_this_one, guint32 timestamp) { MetaWorkspace *workspace; MetaStack *stack = display->stack; g_autoptr (GList) focusable_windows = NULL; g_autoptr (GQueue) focus_candidates = NULL; GList *l; if (not_this_one && not_this_one->workspace) workspace = not_this_one->workspace; else workspace = display->workspace_manager->active_workspace; /* Go through all the focusable windows and try to focus them * in order, waiting for a delay. The first one that replies to * the request (in case of take focus windows) changing the display * focused window, will stop the chained requests. */ focusable_windows = meta_stack_get_default_focus_candidates (stack, workspace); focus_candidates = g_queue_new (); for (l = g_list_last (focusable_windows); l; l = l->prev) { MetaWindow *focus_window = l->data; if (focus_window == not_this_one) continue; g_queue_push_tail (focus_candidates, focus_window); g_signal_connect_swapped (focus_window, "unmanaged", G_CALLBACK (g_queue_remove), focus_candidates); if (!META_IS_WINDOW_X11 (focus_window)) break; if (focus_window->input) break; if (focus_window->shaded && focus_window->frame) break; } focus_candidates_maybe_take_and_focus_next (&focus_candidates, timestamp); } static void meta_window_x11_focus (MetaWindow *window, guint32 timestamp) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); /* For output-only or shaded windows, focus the frame. * This seems to result in the client window getting key events * though, so I don't know if it's icccm-compliant. * * Still, we have to do this or keynav breaks for these windows. */ if (window->frame && (window->shaded || !meta_window_is_focusable (window))) { meta_topic (META_DEBUG_FOCUS, "Focusing frame of %s\n", window->desc); meta_display_set_input_focus (window->display, window, TRUE, timestamp); } else { if (window->input) { meta_topic (META_DEBUG_FOCUS, "Setting input focus on %s since input = true\n", window->desc); meta_display_set_input_focus (window->display, window, FALSE, timestamp); } if (priv->wm_take_focus) { meta_topic (META_DEBUG_FOCUS, "Sending WM_TAKE_FOCUS to %s since take_focus = true\n", window->desc); if (!window->input) { /* The "Globally Active Input" window case, where the window * doesn't want us to call XSetInputFocus on it, but does * want us to send a WM_TAKE_FOCUS. * * Normally, we want to just leave the focus undisturbed until * the window responds to WM_TAKE_FOCUS, but if we're unmanaging * the current focus window we *need* to move the focus away, so * we focus the no focus window before sending WM_TAKE_FOCUS, * and eventually the default focus window excluding this one, * if meanwhile we don't get any focus request. */ if (window->display->focus_window != NULL && window->display->focus_window->unmanaging) { meta_display_unset_input_focus (window->display, timestamp); maybe_focus_default_window (window->display, window, timestamp); } } request_take_focus (window, timestamp); } } } static void meta_window_get_client_root_coords (MetaWindow *window, MetaRectangle *rect) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); *rect = priv->client_rect; if (window->frame) { rect->x += window->frame->rect.x; rect->y += window->frame->rect.y; } } static void meta_window_refresh_resize_popup (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); if (priv->showing_resize_popup) { MetaRectangle rect; int display_w, display_h; meta_window_get_client_root_coords (window, &rect); display_w = (rect.width - window->size_hints.base_width); if (window->size_hints.width_inc > 0) display_w /= window->size_hints.width_inc; display_h = (rect.height - window->size_hints.base_height); if (window->size_hints.height_inc > 0) display_h /= window->size_hints.height_inc; meta_display_show_resize_popup (window->display, TRUE, &rect, display_w, display_h); } else { meta_display_show_resize_popup (window->display, FALSE, NULL, 0, 0); } } static void meta_window_x11_grab_op_began (MetaWindow *window, MetaGrabOp op) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); if (meta_grab_op_is_resizing (op)) { if (window->sync_request_counter != None) meta_window_x11_create_sync_request_alarm (window); if (window->size_hints.width_inc > 2 || window->size_hints.height_inc > 2) { priv->showing_resize_popup = TRUE; meta_window_refresh_resize_popup (window); } } META_WINDOW_CLASS (meta_window_x11_parent_class)->grab_op_began (window, op); } static void meta_window_x11_grab_op_ended (MetaWindow *window, MetaGrabOp op) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); if (priv->showing_resize_popup) { priv->showing_resize_popup = FALSE; meta_window_refresh_resize_popup (window); } META_WINDOW_CLASS (meta_window_x11_parent_class)->grab_op_ended (window, op); } static void update_net_frame_extents (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; unsigned long data[4]; MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); /* Left */ data[0] = borders.visible.left; /* Right */ data[1] = borders.visible.right; /* Top */ data[2] = borders.visible.top; /* Bottom */ data[3] = borders.visible.bottom; meta_topic (META_DEBUG_GEOMETRY, "Setting _NET_FRAME_EXTENTS on managed window 0x%lx " "to left = %lu, right = %lu, top = %lu, bottom = %lu\n", window->xwindow, data[0], data[1], data[2], data[3]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_FRAME_EXTENTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_x11_error_trap_pop (x11_display); } static gboolean is_edge_constraint_resizable (MetaEdgeConstraint constraint) { switch (constraint) { case META_EDGE_CONSTRAINT_NONE: case META_EDGE_CONSTRAINT_WINDOW: return TRUE; case META_EDGE_CONSTRAINT_MONITOR: return FALSE; } g_assert_not_reached (); return FALSE; } static gboolean is_edge_constraint_tiled (MetaEdgeConstraint constraint) { switch (constraint) { case META_EDGE_CONSTRAINT_NONE: return FALSE; case META_EDGE_CONSTRAINT_WINDOW: case META_EDGE_CONSTRAINT_MONITOR: return TRUE; } g_assert_not_reached (); return FALSE; } static unsigned long edge_constraints_to_gtk_edge_constraints (MetaWindow *window) { unsigned long gtk_edge_constraints = 0; if (is_edge_constraint_tiled (window->edge_constraints.top)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_TOP_TILED; if (is_edge_constraint_resizable (window->edge_constraints.top)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_TOP_RESIZABLE; if (is_edge_constraint_tiled (window->edge_constraints.right)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_RIGHT_TILED; if (is_edge_constraint_resizable (window->edge_constraints.right)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_RIGHT_RESIZABLE; if (is_edge_constraint_tiled (window->edge_constraints.bottom)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_BOTTOM_TILED; if (is_edge_constraint_resizable (window->edge_constraints.bottom)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_BOTTOM_RESIZABLE; if (is_edge_constraint_tiled (window->edge_constraints.left)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_LEFT_TILED; if (is_edge_constraint_resizable (window->edge_constraints.left)) gtk_edge_constraints |= META_GTK_EDGE_CONSTRAINT_LEFT_RESIZABLE; return gtk_edge_constraints; } static void update_gtk_edge_constraints (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; unsigned long data[1]; data[0] = edge_constraints_to_gtk_edge_constraints (window); meta_verbose ("Setting _GTK_EDGE_CONSTRAINTS to %lu\n", data[0]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__GTK_EDGE_CONSTRAINTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } static gboolean sync_request_timeout (gpointer data) { MetaWindow *window = data; window->sync_request_timeout_id = 0; /* We have now waited for more than a second for the * application to respond to the sync request */ window->disable_sync = TRUE; /* Reset the wait serial, so we don't continue freezing * window updates */ window->sync_request_wait_serial = 0; meta_compositor_sync_updates_frozen (window->display->compositor, window); if (window == window->display->grab_window && meta_grab_op_is_resizing (window->display->grab_op)) { meta_window_update_resize (window, window->display->grab_last_user_action_was_snap, window->display->grab_latest_motion_x, window->display->grab_latest_motion_y, TRUE); } return FALSE; } static void send_sync_request (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; XClientMessageEvent ev; gint64 wait_serial; /* For the old style of _NET_WM_SYNC_REQUEST_COUNTER, we just have to * increase the value, but for the new "extended" style we need to * pick an even (unfrozen) value sufficiently ahead of the last serial * that we received from the client; the same code still works * for the old style. The increment of 240 is specified by the EWMH * and is (1 second) * (60fps) * (an increment of 4 per frame). */ wait_serial = window->sync_request_serial + 240; window->sync_request_wait_serial = wait_serial; ev.type = ClientMessage; ev.window = window->xwindow; ev.message_type = x11_display->atom_WM_PROTOCOLS; ev.format = 32; ev.data.l[0] = x11_display->atom__NET_WM_SYNC_REQUEST; /* FIXME: meta_display_get_current_time() is bad, but since calls * come from meta_window_move_resize_internal (which in turn come * from all over), I'm not sure what we can do to fix it. Do we * want to use _roundtrip, though? */ ev.data.l[1] = meta_display_get_current_time (window->display); ev.data.l[2] = wait_serial & G_GUINT64_CONSTANT(0xffffffff); ev.data.l[3] = wait_serial >> 32; ev.data.l[4] = window->extended_sync_request_counter ? 1 : 0; /* We don't need to trap errors here as we are already * inside an error_trap_push()/pop() pair. */ XSendEvent (x11_display->xdisplay, window->xwindow, False, 0, (XEvent*) &ev); /* We give the window 1 sec to respond to _NET_WM_SYNC_REQUEST; * if this time expires, we consider the window unresponsive * and resize it unsynchonized. */ window->sync_request_timeout_id = g_timeout_add (1000, sync_request_timeout, window); g_source_set_name_by_id (window->sync_request_timeout_id, "[mutter] sync_request_timeout"); meta_compositor_sync_updates_frozen (window->display->compositor, window); } static unsigned long meta_window_get_net_wm_desktop (MetaWindow *window) { if (window->on_all_workspaces) return 0xFFFFFFFF; else return meta_workspace_index (window->workspace); } static void meta_window_x11_current_workspace_changed (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; /* FIXME if on more than one workspace, we claim to be "sticky", * the WM spec doesn't say what to do here. */ unsigned long data[1]; if (window->workspace == NULL) { /* this happens when unmanaging windows */ return; } data[0] = meta_window_get_net_wm_desktop (window); meta_verbose ("Setting _NET_WM_DESKTOP of %s to %lu\n", window->desc, data[0]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_DESKTOP, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 1); meta_x11_error_trap_pop (x11_display); } static void meta_window_x11_move_resize_internal (MetaWindow *window, MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, MetaRectangle intermediate_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags, MetaMoveResizeResultFlags *result) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); MetaFrameBorders borders; MetaRectangle client_rect; int size_dx, size_dy; XWindowChanges values; unsigned int mask; gboolean need_configure_notify; gboolean need_move_client = FALSE; gboolean need_move_frame = FALSE; gboolean need_resize_client = FALSE; gboolean need_resize_frame = FALSE; gboolean frame_shape_changed = FALSE; gboolean configure_frame_first; gboolean is_configure_request; is_configure_request = (flags & META_MOVE_RESIZE_CONFIGURE_REQUEST) != 0; meta_frame_calc_borders (window->frame, &borders); size_dx = constrained_rect.x - window->rect.width; size_dy = constrained_rect.y - window->rect.height; window->rect = constrained_rect; if (window->frame) { int new_w, new_h; int new_x, new_y; /* Compute new frame size */ new_w = window->rect.width + borders.invisible.left + borders.invisible.right; if (window->shaded) new_h = borders.total.top + borders.total.bottom; else new_h = window->rect.height + borders.invisible.top + borders.invisible.bottom; if (new_w != window->frame->rect.width || new_h != window->frame->rect.height) { need_resize_frame = TRUE; window->frame->rect.width = new_w; window->frame->rect.height = new_h; } /* Compute new frame coords */ new_x = window->rect.x - borders.invisible.left; new_y = window->rect.y - borders.invisible.top; if (new_x != window->frame->rect.x || new_y != window->frame->rect.y) { need_move_frame = TRUE; window->frame->rect.x = new_x; window->frame->rect.y = new_y; } } /* Calculate the new client rect */ meta_window_frame_rect_to_client_rect (window, &constrained_rect, &client_rect); /* The above client_rect is in root window coordinates. The * values we need to pass to XConfigureWindow are in parent * coordinates, so if the window is in a frame, we need to * correct the x/y positions here. */ if (window->frame) { client_rect.x = borders.total.left; client_rect.y = borders.total.top; } if (client_rect.x != priv->client_rect.x || client_rect.y != priv->client_rect.y) { need_move_client = TRUE; priv->client_rect.x = client_rect.x; priv->client_rect.y = client_rect.y; } if (client_rect.width != priv->client_rect.width || client_rect.height != priv->client_rect.height) { need_resize_client = TRUE; priv->client_rect.width = client_rect.width; priv->client_rect.height = client_rect.height; } /* If frame extents have changed, fill in other frame fields and change frame's extents property. */ if (window->frame && (window->frame->child_x != borders.total.left || window->frame->child_y != borders.total.top || window->frame->right_width != borders.total.right || window->frame->bottom_height != borders.total.bottom)) { window->frame->child_x = borders.total.left; window->frame->child_y = borders.total.top; window->frame->right_width = borders.total.right; window->frame->bottom_height = borders.total.bottom; update_net_frame_extents (window); } /* See ICCCM 4.1.5 for when to send ConfigureNotify */ need_configure_notify = FALSE; /* If this is a configure request and we change nothing, then we * must send configure notify. */ if (is_configure_request && !(need_move_client || need_move_frame || need_resize_client || need_resize_frame || priv->border_width != 0)) need_configure_notify = TRUE; /* When tiling a window, the frame sizes changes - which edges depend on * the tile direction. For every direction but META_TILE_RIGHT and * META_TILE_LRC (lower-right-corner), the client position needs to be * shifted in at least one direction relative to the frame window, and * will need to tell the client window about it. */ if ((need_move_client || need_move_frame) && !(need_resize_client || need_resize_frame) && window->type != META_WINDOW_TOOLTIP) need_configure_notify = TRUE; /* For META_TILE_RIGHT and META_TILE_LRC, the right border and * bottom border (for lrc) are removed, but the client size may not * change, if it was already the tiled size. The client position * won't need to change either, from the frame's upper-left-corner, * so the window may not be notified. */ if (need_resize_frame && !need_resize_client && window->type != META_WINDOW_TOOLTIP) need_configure_notify = TRUE; /* MapRequest events with a PPosition or UPosition hint with a frame * are moved by mutter without resizing; send a configure notify * in such cases. See #322840. (Note that window->constructing is * only true iff this call is due to a MapRequest, and when * PPosition/UPosition hints aren't set, mutter seems to send a * ConfigureNotify anyway due to the above code.) */ if (window->constructing && window->frame && ((window->size_hints.flags & PPosition) || (window->size_hints.flags & USPosition))) need_configure_notify = TRUE; /* If resizing, freeze commits - This is for Xwayland, and a no-op on Xorg */ if (need_resize_client || need_resize_frame) { if (!meta_window_x11_should_thaw_after_paint (window)) { meta_window_x11_set_thaw_after_paint (window, TRUE); meta_window_x11_freeze_commits (window); } } /* The rest of this function syncs our new size/pos with X as * efficiently as possible */ /* For nice effect, when growing the window we want to move/resize * the frame first, when shrinking the window we want to move/resize * the client first. If we grow one way and shrink the other, * see which way we're moving "more" * * Mail from Owen subject "Suggestion: Gravity and resizing from the left" * http://mail.gnome.org/archives/wm-spec-list/1999-November/msg00088.html * * An annoying fact you need to know in this code is that META_GRAVITY_STATIC * does nothing if you _only_ resize or _only_ move the frame; * it must move _and_ resize, otherwise you get META_GRAVITY_NORTH_WEST * behavior. The move and resize must actually occur, it is not * enough to set CWX | CWWidth but pass in the current size/pos. */ /* Normally, we configure the frame first depending on whether * we grow the frame more than we shrink. The idea is to avoid * messing up the window contents by having a temporary situation * where the frame is smaller than the window. However, if we're * cooperating with the client to create an atomic frame upate, * and the window is redirected, then we should always update * the frame first, since updating the frame will force a new * backing pixmap to be allocated, and the old backing pixmap * will be left undisturbed for us to paint to the screen until * the client finishes redrawing. */ if (window->extended_sync_request_counter) configure_frame_first = TRUE; else configure_frame_first = size_dx + size_dy >= 0; if (configure_frame_first && window->frame) frame_shape_changed = meta_frame_sync_to_window (window->frame, need_resize_frame); values.border_width = 0; values.x = client_rect.x; values.y = client_rect.y; values.width = client_rect.width; values.height = client_rect.height; mask = 0; if (is_configure_request && priv->border_width != 0) mask |= CWBorderWidth; /* must force to 0 */ if (need_move_client) mask |= (CWX | CWY); if (need_resize_client) mask |= (CWWidth | CWHeight); if (mask != 0) { meta_x11_error_trap_push (window->display->x11_display); if (window == window->display->grab_window && meta_grab_op_is_resizing (window->display->grab_op) && !window->disable_sync && window->sync_request_counter != None && window->sync_request_alarm != None && window->sync_request_timeout_id == 0) { send_sync_request (window); } XConfigureWindow (window->display->x11_display->xdisplay, window->xwindow, mask, &values); meta_x11_error_trap_pop (window->display->x11_display); } if (!configure_frame_first && window->frame) frame_shape_changed = meta_frame_sync_to_window (window->frame, need_resize_frame); if (window->frame) window->buffer_rect = window->frame->rect; else window->buffer_rect = client_rect; if (need_configure_notify) send_configure_notify (window); if (priv->showing_resize_popup) meta_window_refresh_resize_popup (window); if (frame_shape_changed) *result |= META_MOVE_RESIZE_RESULT_FRAME_SHAPE_CHANGED; if (need_move_client || need_move_frame) *result |= META_MOVE_RESIZE_RESULT_MOVED; if (need_resize_client || need_resize_frame) *result |= META_MOVE_RESIZE_RESULT_RESIZED; if (flags & META_MOVE_RESIZE_STATE_CHANGED) *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; update_gtk_edge_constraints (window); } static gboolean meta_window_x11_update_struts (MetaWindow *window) { GSList *old_struts; GSList *new_struts; GSList *old_iter, *new_iter; uint32_t *struts = NULL; int nitems; gboolean changed; g_return_val_if_fail (!window->override_redirect, FALSE); meta_verbose ("Updating struts for %s\n", window->desc); old_struts = window->struts; new_struts = NULL; if (meta_prop_get_cardinal_list (window->display->x11_display, window->xwindow, window->display->x11_display->atom__NET_WM_STRUT_PARTIAL, &struts, &nitems)) { if (nitems != 12) meta_verbose ("_NET_WM_STRUT_PARTIAL on %s has %d values instead " "of 12\n", window->desc, nitems); else { /* Pull out the strut info for each side in the hint */ int i; for (i=0; i<4; i++) { MetaStrut *temp; int thickness, strut_begin, strut_end; thickness = struts[i]; if (thickness == 0) continue; strut_begin = struts[4+(i*2)]; strut_end = struts[4+(i*2)+1]; temp = g_new0 (MetaStrut, 1); temp->side = 1 << i; /* See MetaSide def. Matches nicely, eh? */ meta_display_get_size (window->display, &temp->rect.width, &temp->rect.height); switch (temp->side) { case META_SIDE_RIGHT: temp->rect.x = BOX_RIGHT(temp->rect) - thickness; G_GNUC_FALLTHROUGH; case META_SIDE_LEFT: temp->rect.width = thickness; temp->rect.y = strut_begin; temp->rect.height = strut_end - strut_begin + 1; break; case META_SIDE_BOTTOM: temp->rect.y = BOX_BOTTOM(temp->rect) - thickness; G_GNUC_FALLTHROUGH; case META_SIDE_TOP: temp->rect.height = thickness; temp->rect.x = strut_begin; temp->rect.width = strut_end - strut_begin + 1; break; default: g_assert_not_reached (); } new_struts = g_slist_prepend (new_struts, temp); } meta_verbose ("_NET_WM_STRUT_PARTIAL struts %u %u %u %u for " "window %s\n", struts[0], struts[1], struts[2], struts[3], window->desc); } g_free (struts); } else { meta_verbose ("No _NET_WM_STRUT property for %s\n", window->desc); } if (!new_struts && meta_prop_get_cardinal_list (window->display->x11_display, window->xwindow, window->display->x11_display->atom__NET_WM_STRUT, &struts, &nitems)) { if (nitems != 4) meta_verbose ("_NET_WM_STRUT on %s has %d values instead of 4\n", window->desc, nitems); else { /* Pull out the strut info for each side in the hint */ int i; for (i=0; i<4; i++) { MetaStrut *temp; int thickness; thickness = struts[i]; if (thickness == 0) continue; temp = g_new0 (MetaStrut, 1); temp->side = 1 << i; meta_display_get_size (window->display, &temp->rect.width, &temp->rect.height); switch (temp->side) { case META_SIDE_RIGHT: temp->rect.x = BOX_RIGHT(temp->rect) - thickness; G_GNUC_FALLTHROUGH; case META_SIDE_LEFT: temp->rect.width = thickness; break; case META_SIDE_BOTTOM: temp->rect.y = BOX_BOTTOM(temp->rect) - thickness; G_GNUC_FALLTHROUGH; case META_SIDE_TOP: temp->rect.height = thickness; break; default: g_assert_not_reached (); } new_struts = g_slist_prepend (new_struts, temp); } meta_verbose ("_NET_WM_STRUT struts %u %u %u %u for window %s\n", struts[0], struts[1], struts[2], struts[3], window->desc); } g_free (struts); } else if (!new_struts) { meta_verbose ("No _NET_WM_STRUT property for %s\n", window->desc); } /* Determine whether old_struts and new_struts are the same */ old_iter = old_struts; new_iter = new_struts; while (old_iter && new_iter) { MetaStrut *old_strut = (MetaStrut*) old_iter->data; MetaStrut *new_strut = (MetaStrut*) new_iter->data; if (old_strut->side != new_strut->side || !meta_rectangle_equal (&old_strut->rect, &new_strut->rect)) break; old_iter = old_iter->next; new_iter = new_iter->next; } changed = (old_iter != NULL || new_iter != NULL); /* Update appropriately */ g_slist_free_full (old_struts, g_free); window->struts = new_struts; return changed; } static void meta_window_x11_get_default_skip_hints (MetaWindow *window, gboolean *skip_taskbar_out, gboolean *skip_pager_out) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); *skip_taskbar_out = priv->wm_state_skip_taskbar; *skip_pager_out = priv->wm_state_skip_pager; } static gboolean meta_window_x11_update_icon (MetaWindow *window, cairo_surface_t **icon, cairo_surface_t **mini_icon) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); return meta_read_icons (window->display->x11_display, window->xwindow, &priv->icon_cache, priv->wm_hints_pixmap, priv->wm_hints_mask, icon, META_ICON_WIDTH, META_ICON_HEIGHT, mini_icon, META_MINI_ICON_WIDTH, META_MINI_ICON_HEIGHT); } static void meta_window_x11_update_main_monitor (MetaWindow *window, MetaWindowUpdateMonitorFlags flags) { window->monitor = meta_window_calculate_main_logical_monitor (window); } static void meta_window_x11_main_monitor_changed (MetaWindow *window, const MetaLogicalMonitor *old) { } static uint32_t meta_window_x11_get_client_pid (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; xcb_connection_t *xcb = XGetXCBConnection (x11_display->xdisplay); xcb_res_client_id_spec_t spec = { 0 }; xcb_res_query_client_ids_cookie_t cookie; xcb_res_query_client_ids_reply_t *reply = NULL; spec.client = window->xwindow; spec.mask = XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID; cookie = xcb_res_query_client_ids (xcb, 1, &spec); reply = xcb_res_query_client_ids_reply (xcb, cookie, NULL); if (reply == NULL) return 0; uint32_t pid = 0, *value; xcb_res_client_id_value_iterator_t it; for (it = xcb_res_query_client_ids_ids_iterator (reply); it.rem; xcb_res_client_id_value_next (&it)) { spec = it.data->spec; if (spec.mask & XCB_RES_CLIENT_ID_MASK_LOCAL_CLIENT_PID) { value = xcb_res_client_id_value_value (it.data); pid = *value; break; } } free (reply); return pid; } static void meta_window_x11_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source) { /* * Not needed on X11 because clients can use a keyboard grab * to bypass the compositor shortcuts. */ } static gboolean meta_window_x11_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source) { /* * On X11, we don't use a shortcuts inhibitor, clients just grab * the keyboard. */ return FALSE; } void meta_window_x11_set_wm_take_focus (MetaWindow *window, gboolean take_focus) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); priv->wm_take_focus = take_focus; } static gboolean meta_window_x11_is_focusable (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); return window->input || priv->wm_take_focus; } static gboolean meta_window_x11_is_stackable (MetaWindow *window) { return !window->override_redirect; } static gboolean meta_window_x11_are_updates_frozen (MetaWindow *window) { if (window->extended_sync_request_counter && window->sync_request_serial % 2 == 1) return TRUE; if (window->sync_request_serial < window->sync_request_wait_serial) return TRUE; return FALSE; } /* Get layer ignoring any transient or group relationships */ static MetaStackLayer get_standalone_layer (MetaWindow *window) { MetaStackLayer layer; switch (window->type) { case META_WINDOW_DESKTOP: layer = META_LAYER_DESKTOP; break; case META_WINDOW_DOCK: if (window->wm_state_below || (window->monitor && window->monitor->in_fullscreen)) layer = META_LAYER_BOTTOM; else layer = META_LAYER_DOCK; break; case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_OVERRIDE_OTHER: layer = META_LAYER_OVERRIDE_REDIRECT; break; default: layer = meta_window_get_default_layer (window); break; } return layer; } /* Note that this function can never use window->layer only * get_standalone_layer, or we'd have issues. */ static MetaStackLayer get_maximum_layer_in_group (MetaWindow *window) { GSList *members; MetaGroup *group; GSList *tmp; MetaStackLayer max; MetaStackLayer layer; max = META_LAYER_DESKTOP; group = meta_window_get_group (window); if (group != NULL) members = meta_group_list_windows (group); else members = NULL; tmp = members; while (tmp != NULL) { MetaWindow *w = tmp->data; if (!w->override_redirect) { layer = get_standalone_layer (w); if (layer > max) max = layer; } tmp = tmp->next; } g_slist_free (members); return max; } static MetaStackLayer meta_window_x11_calculate_layer (MetaWindow *window) { MetaStackLayer layer = get_standalone_layer (window); /* We can only do promotion-due-to-group for dialogs and other * transients, or weird stuff happens like the desktop window and * nautilus windows getting in the same layer, or all gnome-terminal * windows getting in fullscreen layer if any terminal is * fullscreen. */ if (layer != META_LAYER_DESKTOP && meta_window_has_transient_type (window) && window->transient_for == NULL) { /* We only do the group thing if the dialog is NOT transient for * a particular window. Imagine a group with a normal window, a dock, * and a dialog transient for the normal window; you don't want the dialog * above the dock if it wouldn't normally be. */ MetaStackLayer group_max; group_max = get_maximum_layer_in_group (window); if (group_max > layer) { meta_topic (META_DEBUG_STACK, "Promoting window %s from layer %u to %u due to group membership\n", window->desc, layer, group_max); layer = group_max; } } meta_topic (META_DEBUG_STACK, "Window %s on layer %u type = %u has_focus = %d\n", window->desc, layer, window->type, window->has_focus); return layer; } static void meta_window_x11_impl_freeze_commits (MetaWindow *window) { } static void meta_window_x11_impl_thaw_commits (MetaWindow *window) { } static void meta_window_x11_map (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; meta_x11_error_trap_push (x11_display); XMapWindow (x11_display->xdisplay, window->xwindow); meta_x11_error_trap_pop (x11_display); } static void meta_window_x11_unmap (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; meta_x11_error_trap_push (x11_display); XUnmapWindow (x11_display->xdisplay, window->xwindow); meta_x11_error_trap_pop (x11_display); window->unmaps_pending ++; } static gboolean meta_window_x11_impl_always_update_shape (MetaWindow *window) { return FALSE; } static gboolean meta_window_x11_is_focus_async (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); return !window->input && priv->wm_take_focus; } static void meta_window_x11_class_init (MetaWindowX11Class *klass) { MetaWindowClass *window_class = META_WINDOW_CLASS (klass); window_class->manage = meta_window_x11_manage; window_class->unmanage = meta_window_x11_unmanage; window_class->ping = meta_window_x11_ping; window_class->delete = meta_window_x11_delete; window_class->kill = meta_window_x11_kill; window_class->focus = meta_window_x11_focus; window_class->grab_op_began = meta_window_x11_grab_op_began; window_class->grab_op_ended = meta_window_x11_grab_op_ended; window_class->current_workspace_changed = meta_window_x11_current_workspace_changed; window_class->move_resize_internal = meta_window_x11_move_resize_internal; window_class->update_struts = meta_window_x11_update_struts; window_class->get_default_skip_hints = meta_window_x11_get_default_skip_hints; window_class->update_icon = meta_window_x11_update_icon; window_class->update_main_monitor = meta_window_x11_update_main_monitor; window_class->main_monitor_changed = meta_window_x11_main_monitor_changed; window_class->get_client_pid = meta_window_x11_get_client_pid; window_class->force_restore_shortcuts = meta_window_x11_force_restore_shortcuts; window_class->shortcuts_inhibited = meta_window_x11_shortcuts_inhibited; window_class->is_focusable = meta_window_x11_is_focusable; window_class->is_stackable = meta_window_x11_is_stackable; window_class->can_ping = meta_window_x11_can_ping; window_class->are_updates_frozen = meta_window_x11_are_updates_frozen; window_class->calculate_layer = meta_window_x11_calculate_layer; window_class->map = meta_window_x11_map; window_class->unmap = meta_window_x11_unmap; window_class->is_focus_async = meta_window_x11_is_focus_async; klass->freeze_commits = meta_window_x11_impl_freeze_commits; klass->thaw_commits = meta_window_x11_impl_thaw_commits; klass->always_update_shape = meta_window_x11_impl_always_update_shape; } void meta_window_x11_set_net_wm_state (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); int i; unsigned long data[13]; i = 0; if (window->shaded) { data[i] = x11_display->atom__NET_WM_STATE_SHADED; ++i; } if (priv->wm_state_modal) { data[i] = x11_display->atom__NET_WM_STATE_MODAL; ++i; } if (window->skip_pager) { data[i] = x11_display->atom__NET_WM_STATE_SKIP_PAGER; ++i; } if (window->skip_taskbar) { data[i] = x11_display->atom__NET_WM_STATE_SKIP_TASKBAR; ++i; } if (window->maximized_horizontally) { data[i] = x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ; ++i; } if (window->maximized_vertically) { data[i] = x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT; ++i; } if (window->fullscreen) { data[i] = x11_display->atom__NET_WM_STATE_FULLSCREEN; ++i; } if (!meta_window_showing_on_its_workspace (window) || window->shaded) { data[i] = x11_display->atom__NET_WM_STATE_HIDDEN; ++i; } if (window->wm_state_above) { data[i] = x11_display->atom__NET_WM_STATE_ABOVE; ++i; } if (window->wm_state_below) { data[i] = x11_display->atom__NET_WM_STATE_BELOW; ++i; } if (window->wm_state_demands_attention) { data[i] = x11_display->atom__NET_WM_STATE_DEMANDS_ATTENTION; ++i; } if (window->on_all_workspaces_requested) { data[i] = x11_display->atom__NET_WM_STATE_STICKY; ++i; } if (meta_window_appears_focused (window)) { data[i] = x11_display->atom__NET_WM_STATE_FOCUSED; ++i; } meta_verbose ("Setting _NET_WM_STATE with %d atoms\n", i); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_STATE, XA_ATOM, 32, PropModeReplace, (guchar*) data, i); meta_x11_error_trap_pop (x11_display); if (window->fullscreen) { if (meta_window_has_fullscreen_monitors (window)) { data[0] = meta_x11_display_logical_monitor_to_xinerama_index (window->display->x11_display, window->fullscreen_monitors.top); data[1] = meta_x11_display_logical_monitor_to_xinerama_index (window->display->x11_display, window->fullscreen_monitors.bottom); data[2] = meta_x11_display_logical_monitor_to_xinerama_index (window->display->x11_display, window->fullscreen_monitors.left); data[3] = meta_x11_display_logical_monitor_to_xinerama_index (window->display->x11_display, window->fullscreen_monitors.right); meta_verbose ("Setting _NET_WM_FULLSCREEN_MONITORS\n"); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_FULLSCREEN_MONITORS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_x11_error_trap_pop (x11_display); } else { meta_verbose ("Clearing _NET_WM_FULLSCREEN_MONITORS\n"); meta_x11_error_trap_push (x11_display); XDeleteProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_FULLSCREEN_MONITORS); meta_x11_error_trap_pop (x11_display); } } /* Edge constraints */ update_gtk_edge_constraints (window); } static cairo_region_t * region_create_from_x_rectangles (const XRectangle *rects, int n_rects) { int i; cairo_rectangle_int_t *cairo_rects = g_newa (cairo_rectangle_int_t, n_rects); for (i = 0; i < n_rects; i ++) { cairo_rects[i].x = rects[i].x; cairo_rects[i].y = rects[i].y; cairo_rects[i].width = rects[i].width; cairo_rects[i].height = rects[i].height; } return cairo_region_create_rectangles (cairo_rects, n_rects); } static void meta_window_set_input_region (MetaWindow *window, cairo_region_t *region) { if (cairo_region_equal (window->input_region, region)) return; g_clear_pointer (&window->input_region, cairo_region_destroy); if (region != NULL) window->input_region = cairo_region_reference (region); meta_compositor_window_shape_changed (window->display->compositor, window); } #if 0 /* Print out a region; useful for debugging */ static void print_region (cairo_region_t *region) { int n_rects; int i; n_rects = cairo_region_num_rectangles (region); g_print ("["); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); g_print ("+%d+%dx%dx%d ", rect.x, rect.y, rect.width, rect.height); } g_print ("]\n"); } #endif void meta_window_x11_update_input_region (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; cairo_region_t *region = NULL; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); /* Decorated windows don't have an input region, because we don't shape the frame to match the client windows (so the events are blocked by the frame anyway) */ if (window->decorated) { if (window->input_region) meta_window_set_input_region (window, NULL); return; } if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) { /* Translate the set of XShape rectangles that we * get from the X server to a cairo_region. */ XRectangle *rects = NULL; int n_rects = -1, ordering; meta_x11_error_trap_push (x11_display); rects = XShapeGetRectangles (x11_display->xdisplay, window->xwindow, ShapeInput, &n_rects, &ordering); meta_x11_error_trap_pop (x11_display); /* XXX: The X Shape specification is quite unfortunately specified. * * By default, the window has a shape the same as its bounding region, * which we consider "NULL". * * If the window sets an empty region, then we'll get n_rects as 0 * and rects as NULL, which we need to transform back into an empty * region. * * It would be great to have a less-broken extension for this, but * hey, it's X11! */ if (n_rects == -1) { /* We had an error. */ region = NULL; } else if (n_rects == 0) { /* Client set an empty region. */ region = cairo_region_create (); } else if (n_rects == 1 && (rects[0].x == 0 && rects[0].y == 0 && rects[0].width == priv->client_rect.width && rects[0].height == priv->client_rect.height)) { /* This is the bounding region case. Keep the * region as NULL. */ region = NULL; } else { /* Window has a custom shape. */ region = region_create_from_x_rectangles (rects, n_rects); } meta_XFree (rects); } if (region != NULL) { cairo_rectangle_int_t client_area; client_area.x = 0; client_area.y = 0; client_area.width = priv->client_rect.width; client_area.height = priv->client_rect.height; /* The shape we get back from the client may have coordinates * outside of the frame. The X SHAPE Extension requires that * the overall shape the client provides never exceeds the * "bounding rectangle" of the window -- the shape that the * window would have gotten if it was unshaped. In our case, * this is simply the client area. */ cairo_region_intersect_rectangle (region, &client_area); } meta_window_set_input_region (window, region); cairo_region_destroy (region); } static void meta_window_set_shape_region (MetaWindow *window, cairo_region_t *region) { if (cairo_region_equal (window->shape_region, region)) return; g_clear_pointer (&window->shape_region, cairo_region_destroy); if (region != NULL) window->shape_region = cairo_region_reference (region); meta_compositor_window_shape_changed (window->display->compositor, window); } void meta_window_x11_update_shape_region (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); cairo_region_t *region = NULL; if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) { /* Translate the set of XShape rectangles that we * get from the X server to a cairo_region. */ XRectangle *rects = NULL; int n_rects, ordering; int x_bounding, y_bounding, x_clip, y_clip; unsigned w_bounding, h_bounding, w_clip, h_clip; int bounding_shaped, clip_shaped; meta_x11_error_trap_push (x11_display); XShapeQueryExtents (x11_display->xdisplay, window->xwindow, &bounding_shaped, &x_bounding, &y_bounding, &w_bounding, &h_bounding, &clip_shaped, &x_clip, &y_clip, &w_clip, &h_clip); if (bounding_shaped) { rects = XShapeGetRectangles (x11_display->xdisplay, window->xwindow, ShapeBounding, &n_rects, &ordering); } meta_x11_error_trap_pop (x11_display); if (rects) { region = region_create_from_x_rectangles (rects, n_rects); XFree (rects); } } if (region != NULL) { cairo_rectangle_int_t client_area; client_area.x = 0; client_area.y = 0; client_area.width = priv->client_rect.width; client_area.height = priv->client_rect.height; /* The shape we get back from the client may have coordinates * outside of the frame. The X SHAPE Extension requires that * the overall shape the client provides never exceeds the * "bounding rectangle" of the window -- the shape that the * window would have gotten if it was unshaped. In our case, * this is simply the client area. */ cairo_region_intersect_rectangle (region, &client_area); /* Some applications might explicitly set their bounding region * to the client area. Detect these cases, and throw out the * bounding region in this case for decorated windows. */ if (window->decorated && cairo_region_contains_rectangle (region, &client_area) == CAIRO_REGION_OVERLAP_IN) g_clear_pointer (®ion, cairo_region_destroy); } meta_window_set_shape_region (window, region); cairo_region_destroy (region); } /* Generally meta_window_same_application() is a better idea * of "sameness", since it handles the case where multiple apps * want to look like the same app or the same app wants to look * like multiple apps, but in the case of workarounds for legacy * applications (which likely aren't setting the group properly * anyways), it may be desirable to check this as well. */ static gboolean meta_window_same_client (MetaWindow *window, MetaWindow *other_window) { int resource_mask = window->display->x11_display->xdisplay->resource_mask; return ((window->xwindow & ~resource_mask) == (other_window->xwindow & ~resource_mask)); } static void meta_window_move_resize_request (MetaWindow *window, guint value_mask, MetaGravity gravity, int new_x, int new_y, int new_width, int new_height) { int x, y, width, height; gboolean allow_position_change; gboolean in_grab_op; MetaMoveResizeFlags flags; /* We ignore configure requests while the user is moving/resizing * the window, since these represent the app sucking and fighting * the user, most likely due to a bug in the app (e.g. pfaedit * seemed to do this) * * Still have to do the ConfigureNotify and all, but pretend the * app asked for the current size/position instead of the new one. */ in_grab_op = (window->display->grab_window == window && meta_grab_op_is_mouse (window->display->grab_op)); /* it's essential to use only the explicitly-set fields, * and otherwise use our current up-to-date position. * * Otherwise you get spurious position changes when the app changes * size, for example, if window->rect is not in sync with the * server-side position in effect when the configure request was * generated. */ meta_window_get_gravity_position (window, gravity, &x, &y); allow_position_change = FALSE; if (meta_prefs_get_disable_workarounds ()) { if (window->type == META_WINDOW_DIALOG || window->type == META_WINDOW_MODAL_DIALOG || window->type == META_WINDOW_SPLASHSCREEN) ; /* No position change for these */ else if ((window->size_hints.flags & PPosition) || /* USPosition is just stale if window is placed; * no --geometry involved here. */ ((window->size_hints.flags & USPosition) && !window->placed)) allow_position_change = TRUE; } else { allow_position_change = TRUE; } if (in_grab_op) allow_position_change = FALSE; if (allow_position_change) { if (value_mask & CWX) x = new_x; if (value_mask & CWY) y = new_y; if (value_mask & (CWX | CWY)) { /* Once manually positioned, windows shouldn't be placed * by the window manager. */ window->placed = TRUE; } } else { meta_topic (META_DEBUG_GEOMETRY, "Not allowing position change for window %s PPosition 0x%lx USPosition 0x%lx type %u\n", window->desc, window->size_hints.flags & PPosition, window->size_hints.flags & USPosition, window->type); } width = window->rect.width; height = window->rect.height; if (!in_grab_op) { if (value_mask & CWWidth) width = new_width; if (value_mask & CWHeight) height = new_height; } /* ICCCM 4.1.5 */ /* We're ignoring the value_mask here, since sizes * not in the mask will be the current window geometry. */ window->size_hints.x = x; window->size_hints.y = y; window->size_hints.width = width; window->size_hints.height = height; /* NOTE: We consider ConfigureRequests to be "user" actions in one * way, but not in another. Explanation of the two cases are in the * next two big comments. */ /* The constraints code allows user actions to move windows * offscreen, etc., and configure request actions would often send * windows offscreen when users don't want it if not constrained * (e.g. hitting a dropdown triangle in a fileselector to show more * options, which makes the window bigger). Thus we do not set * META_MOVE_RESIZE_USER_ACTION in flags to the * meta_window_move_resize_internal() call. */ flags = META_MOVE_RESIZE_CONFIGURE_REQUEST; if (value_mask & (CWX | CWY)) flags |= META_MOVE_RESIZE_MOVE_ACTION; if (value_mask & (CWWidth | CWHeight)) flags |= META_MOVE_RESIZE_RESIZE_ACTION; if (flags & (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION)) { MetaRectangle rect; rect.x = x; rect.y = y; rect.width = width; rect.height = height; if (window->monitor) { MetaRectangle monitor_rect; meta_display_get_monitor_geometry (window->display, window->monitor->number, &monitor_rect); /* Workaround braindead legacy apps that don't know how to * fullscreen themselves properly - don't get fooled by * windows which hide their titlebar when maximized or which are * client decorated; that's not the same as fullscreen, even * if there are no struts making the workarea smaller than * the monitor. */ if (meta_prefs_get_force_fullscreen() && (window->decorated || !meta_window_is_client_decorated (window)) && meta_rectangle_equal (&rect, &monitor_rect) && window->has_fullscreen_func && !window->fullscreen) { /* meta_topic (META_DEBUG_GEOMETRY, */ meta_warning ( "Treating resize request of legacy application %s as a " "fullscreen request\n", window->desc); meta_window_make_fullscreen_internal (window); } } adjust_for_gravity (window, TRUE, gravity, &rect); meta_window_client_rect_to_frame_rect (window, &rect, &rect); meta_window_move_resize_internal (window, flags, gravity, rect); } } static void restack_window (MetaWindow *window, MetaWindow *sibling, int direction) { switch (direction) { case Above: if (sibling) meta_window_stack_just_above (window, sibling); else meta_window_raise (window); break; case Below: if (sibling) meta_window_stack_just_below (window, sibling); else meta_window_lower (window); break; case TopIf: case BottomIf: case Opposite: break; } } gboolean meta_window_x11_configure_request (MetaWindow *window, XEvent *event) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); /* Note that x, y is the corner of the window border, * and width, height is the size of the window inside * its border, but that we always deny border requests * and give windows a border of 0. But we save the * requested border here. */ if (event->xconfigurerequest.value_mask & CWBorderWidth) priv->border_width = event->xconfigurerequest.border_width; meta_window_move_resize_request(window, event->xconfigurerequest.value_mask, window->size_hints.win_gravity, event->xconfigurerequest.x, event->xconfigurerequest.y, event->xconfigurerequest.width, event->xconfigurerequest.height); /* Handle stacking. We only handle raises/lowers, mostly because * stack.c really can't deal with anything else. I guess we'll fix * that if a client turns up that really requires it. Only a very * few clients even require the raise/lower (and in fact all client * attempts to deal with stacking order are essentially broken, * since they have no idea what other clients are involved or how * the stack looks). * * I'm pretty sure no interesting client uses TopIf, BottomIf, or * Opposite anyway. */ if (event->xconfigurerequest.value_mask & CWStackMode) { MetaWindow *active_window; active_window = window->display->focus_window; if (meta_prefs_get_disable_workarounds ()) { meta_topic (META_DEBUG_STACK, "%s sent an xconfigure stacking request; this is " "broken behavior and the request is being ignored.\n", window->desc); } else if (active_window && !meta_window_same_application (window, active_window) && !meta_window_same_client (window, active_window) && XSERVER_TIME_IS_BEFORE (window->net_wm_user_time, active_window->net_wm_user_time)) { meta_topic (META_DEBUG_STACK, "Ignoring xconfigure stacking request from %s (with " "user_time %u); currently active application is %s (with " "user_time %u).\n", window->desc, window->net_wm_user_time, active_window->desc, active_window->net_wm_user_time); if (event->xconfigurerequest.detail == Above) meta_window_set_demands_attention(window); } else { MetaWindow *sibling = NULL; /* Handle Above/Below with a sibling set */ if (event->xconfigurerequest.above != None) { MetaDisplay *display; display = meta_window_get_display (window); sibling = meta_x11_display_lookup_x_window (display->x11_display, event->xconfigurerequest.above); if (sibling == NULL) return TRUE; meta_topic (META_DEBUG_STACK, "xconfigure stacking request from window %s sibling %s stackmode %d\n", window->desc, sibling->desc, event->xconfigurerequest.detail); } restack_window (window, sibling, event->xconfigurerequest.detail); } } return TRUE; } static gboolean process_property_notify (MetaWindow *window, XPropertyEvent *event) { Window xid = window->xwindow; if (meta_is_verbose ()) /* avoid looking up the name if we don't have to */ { char *property_name = XGetAtomName (window->display->x11_display->xdisplay, event->atom); meta_verbose ("Property notify on %s for %s\n", window->desc, property_name); XFree (property_name); } if (event->atom == window->display->x11_display->atom__NET_WM_USER_TIME && window->user_time_window) { xid = window->user_time_window; } meta_window_reload_property_from_xwindow (window, xid, event->atom, FALSE); return TRUE; } gboolean meta_window_x11_property_notify (MetaWindow *window, XEvent *event) { return process_property_notify (window, &event->xproperty); } #define _NET_WM_MOVERESIZE_SIZE_TOPLEFT 0 #define _NET_WM_MOVERESIZE_SIZE_TOP 1 #define _NET_WM_MOVERESIZE_SIZE_TOPRIGHT 2 #define _NET_WM_MOVERESIZE_SIZE_RIGHT 3 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT 4 #define _NET_WM_MOVERESIZE_SIZE_BOTTOM 5 #define _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT 6 #define _NET_WM_MOVERESIZE_SIZE_LEFT 7 #define _NET_WM_MOVERESIZE_MOVE 8 #define _NET_WM_MOVERESIZE_SIZE_KEYBOARD 9 #define _NET_WM_MOVERESIZE_MOVE_KEYBOARD 10 #define _NET_WM_MOVERESIZE_CANCEL 11 static int query_pressed_buttons (MetaWindow *window) { MetaCursorTracker *tracker = meta_cursor_tracker_get_for_display (window->display); ClutterModifierType mods; int button = 0; meta_cursor_tracker_get_pointer (tracker, NULL, NULL, &mods); if (mods & CLUTTER_BUTTON1_MASK) button |= 1 << 1; if (mods & CLUTTER_BUTTON2_MASK) button |= 1 << 2; if (mods & CLUTTER_BUTTON3_MASK) button |= 1 << 3; return button; } static void handle_net_restack_window (MetaDisplay *display, XEvent *event) { MetaWindow *window, *sibling = NULL; /* Ignore if this does not come from a pager, see the WM spec */ if (event->xclient.data.l[0] != 2) return; window = meta_x11_display_lookup_x_window (display->x11_display, event->xclient.window); if (window) { if (event->xclient.data.l[1]) sibling = meta_x11_display_lookup_x_window (display->x11_display, event->xclient.data.l[1]); restack_window (window, sibling, event->xclient.data.l[2]); } } gboolean meta_window_x11_client_message (MetaWindow *window, XEvent *event) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); MetaDisplay *display; display = window->display; if (window->override_redirect) { /* Don't warn here: we could warn on any of the messages below, * but we might also receive other client messages that are * part of protocols we don't know anything about. So, silently * ignoring is simplest. */ return FALSE; } if (event->xclient.message_type == x11_display->atom__NET_CLOSE_WINDOW) { guint32 timestamp; if (event->xclient.data.l[0] != 0) timestamp = event->xclient.data.l[0]; else { meta_warning ("Receiving a NET_CLOSE_WINDOW message for %s without " "a timestamp! This means some buggy (outdated) " "application is on the loose!\n", window->desc); timestamp = meta_display_get_current_time (window->display); } meta_window_delete (window, timestamp); return TRUE; } else if (event->xclient.message_type == x11_display->atom__NET_WM_DESKTOP) { int space; MetaWorkspaceManager *workspace_manager = window->display->workspace_manager; MetaWorkspace *workspace; space = event->xclient.data.l[0]; meta_verbose ("Request to move %s to workspace %d\n", window->desc, space); workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, space); if (workspace) meta_window_change_workspace (window, workspace); else if (space == (int) 0xFFFFFFFF) meta_window_stick (window); else meta_verbose ("No such workspace %d for screen\n", space); meta_verbose ("Window %s now on_all_workspaces = %d\n", window->desc, window->on_all_workspaces); return TRUE; } else if (event->xclient.message_type == x11_display->atom__NET_WM_STATE) { gulong action; Atom first; Atom second; action = event->xclient.data.l[0]; first = event->xclient.data.l[1]; second = event->xclient.data.l[2]; if (meta_is_verbose ()) { char *str1; char *str2; meta_x11_error_trap_push (x11_display); str1 = XGetAtomName (x11_display->xdisplay, first); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) str1 = NULL; meta_x11_error_trap_push (x11_display); str2 = XGetAtomName (x11_display->xdisplay, second); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) str2 = NULL; meta_verbose ("Request to change _NET_WM_STATE action %lu atom1: %s atom2: %s\n", action, str1 ? str1 : "(unknown)", str2 ? str2 : "(unknown)"); meta_XFree (str1); meta_XFree (str2); } if (first == x11_display->atom__NET_WM_STATE_SHADED || second == x11_display->atom__NET_WM_STATE_SHADED) { gboolean shade; guint32 timestamp; /* Stupid protocol has no timestamp; of course, shading * sucks anyway so who really cares that we're forced to do * a roundtrip here? */ timestamp = meta_display_get_current_time_roundtrip (window->display); shade = (action == _NET_WM_STATE_ADD || (action == _NET_WM_STATE_TOGGLE && !window->shaded)); if (shade && window->has_shade_func) meta_window_shade (window, timestamp); else meta_window_unshade (window, timestamp); } if (first == x11_display->atom__NET_WM_STATE_FULLSCREEN || second == x11_display->atom__NET_WM_STATE_FULLSCREEN) { gboolean make_fullscreen; make_fullscreen = (action == _NET_WM_STATE_ADD || (action == _NET_WM_STATE_TOGGLE && !window->fullscreen)); if (make_fullscreen && window->has_fullscreen_func) meta_window_make_fullscreen (window); else meta_window_unmake_fullscreen (window); } if (first == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ || second == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ || first == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT || second == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT) { gboolean max; MetaMaximizeFlags directions = 0; max = (action == _NET_WM_STATE_ADD || (action == _NET_WM_STATE_TOGGLE && !window->maximized_horizontally)); if (first == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ || second == x11_display->atom__NET_WM_STATE_MAXIMIZED_HORZ) directions |= META_MAXIMIZE_HORIZONTAL; if (first == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT || second == x11_display->atom__NET_WM_STATE_MAXIMIZED_VERT) directions |= META_MAXIMIZE_VERTICAL; if (max && window->has_maximize_func) { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_maximize (window, directions); } else { if (meta_prefs_get_raise_on_click ()) meta_window_raise (window); meta_window_unmaximize (window, directions); } } if (first == x11_display->atom__NET_WM_STATE_MODAL || second == x11_display->atom__NET_WM_STATE_MODAL) { priv->wm_state_modal = (action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !priv->wm_state_modal); meta_window_x11_recalc_window_type (window); meta_window_queue(window, META_QUEUE_MOVE_RESIZE); } if (first == x11_display->atom__NET_WM_STATE_SKIP_PAGER || second == x11_display->atom__NET_WM_STATE_SKIP_PAGER) { priv->wm_state_skip_pager = (action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->skip_pager); meta_window_recalc_features (window); meta_window_x11_set_net_wm_state (window); } if (first == x11_display->atom__NET_WM_STATE_SKIP_TASKBAR || second == x11_display->atom__NET_WM_STATE_SKIP_TASKBAR) { priv->wm_state_skip_taskbar = (action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->skip_taskbar); meta_window_recalc_features (window); meta_window_x11_set_net_wm_state (window); } if (first == x11_display->atom__NET_WM_STATE_ABOVE || second == x11_display->atom__NET_WM_STATE_ABOVE) { if ((action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention)) meta_window_make_above (window); else meta_window_unmake_above (window); } if (first == x11_display->atom__NET_WM_STATE_BELOW || second == x11_display->atom__NET_WM_STATE_BELOW) { window->wm_state_below = (action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->wm_state_below); meta_window_update_layer (window); meta_window_x11_set_net_wm_state (window); } if (first == x11_display->atom__NET_WM_STATE_DEMANDS_ATTENTION || second == x11_display->atom__NET_WM_STATE_DEMANDS_ATTENTION) { if ((action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->wm_state_demands_attention)) meta_window_set_demands_attention (window); else meta_window_unset_demands_attention (window); } if (first == x11_display->atom__NET_WM_STATE_STICKY || second == x11_display->atom__NET_WM_STATE_STICKY) { if ((action == _NET_WM_STATE_ADD) || (action == _NET_WM_STATE_TOGGLE && !window->on_all_workspaces_requested)) meta_window_stick (window); else meta_window_unstick (window); } return TRUE; } else if (event->xclient.message_type == x11_display->atom_WM_CHANGE_STATE) { meta_verbose ("WM_CHANGE_STATE client message, state: %ld\n", event->xclient.data.l[0]); if (event->xclient.data.l[0] == IconicState) meta_window_minimize (window); return TRUE; } else if (event->xclient.message_type == x11_display->atom__NET_WM_MOVERESIZE) { int x_root; int y_root; int action; MetaGrabOp op; int button; guint32 timestamp; /* _NET_WM_MOVERESIZE messages are almost certainly going to come from * clients when users click on the fake "frame" that the client has, * thus we should also treat such messages as though it were a * "frame action". */ gboolean const frame_action = TRUE; x_root = event->xclient.data.l[0]; y_root = event->xclient.data.l[1]; action = event->xclient.data.l[2]; button = event->xclient.data.l[3]; /* FIXME: What a braindead protocol; no timestamp?!? */ timestamp = meta_display_get_current_time_roundtrip (display); meta_topic (META_DEBUG_WINDOW_OPS, "Received _NET_WM_MOVERESIZE message on %s, %d,%d action = %d, button %d\n", window->desc, x_root, y_root, action, button); op = META_GRAB_OP_NONE; switch (action) { case _NET_WM_MOVERESIZE_SIZE_TOPLEFT: op = META_GRAB_OP_RESIZING_NW; break; case _NET_WM_MOVERESIZE_SIZE_TOP: op = META_GRAB_OP_RESIZING_N; break; case _NET_WM_MOVERESIZE_SIZE_TOPRIGHT: op = META_GRAB_OP_RESIZING_NE; break; case _NET_WM_MOVERESIZE_SIZE_RIGHT: op = META_GRAB_OP_RESIZING_E; break; case _NET_WM_MOVERESIZE_SIZE_BOTTOMRIGHT: op = META_GRAB_OP_RESIZING_SE; break; case _NET_WM_MOVERESIZE_SIZE_BOTTOM: op = META_GRAB_OP_RESIZING_S; break; case _NET_WM_MOVERESIZE_SIZE_BOTTOMLEFT: op = META_GRAB_OP_RESIZING_SW; break; case _NET_WM_MOVERESIZE_SIZE_LEFT: op = META_GRAB_OP_RESIZING_W; break; case _NET_WM_MOVERESIZE_MOVE: op = META_GRAB_OP_MOVING; break; case _NET_WM_MOVERESIZE_SIZE_KEYBOARD: op = META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN; break; case _NET_WM_MOVERESIZE_MOVE_KEYBOARD: op = META_GRAB_OP_KEYBOARD_MOVING; break; case _NET_WM_MOVERESIZE_CANCEL: /* handled below */ break; default: break; } if (action == _NET_WM_MOVERESIZE_CANCEL) { meta_display_end_grab_op (window->display, timestamp); } else if (op != META_GRAB_OP_NONE && ((window->has_move_func && op == META_GRAB_OP_KEYBOARD_MOVING) || (window->has_resize_func && op == META_GRAB_OP_KEYBOARD_RESIZING_UNKNOWN))) { meta_window_begin_grab_op (window, op, frame_action, timestamp); } else if (op != META_GRAB_OP_NONE && ((window->has_move_func && op == META_GRAB_OP_MOVING) || (window->has_resize_func && (op != META_GRAB_OP_MOVING && op != META_GRAB_OP_KEYBOARD_MOVING)))) { int button_mask; meta_topic (META_DEBUG_WINDOW_OPS, "Beginning move/resize with button = %d\n", button); meta_display_begin_grab_op (window->display, window, op, FALSE, frame_action, button, 0, timestamp, x_root, y_root); button_mask = query_pressed_buttons (window); if (button == 0) { /* * the button SHOULD already be included in the message */ if ((button_mask & (1 << 1)) != 0) button = 1; else if ((button_mask & (1 << 2)) != 0) button = 2; else if ((button_mask & (1 << 3)) != 0) button = 3; if (button != 0) window->display->grab_button = button; else meta_display_end_grab_op (window->display, timestamp); } else { /* There is a potential race here. If the user presses and * releases their mouse button very fast, it's possible for * both the ButtonPress and ButtonRelease to be sent to the * client before it can get a chance to send _NET_WM_MOVERESIZE * to us. When that happens, we'll become stuck in a grab * state, as we haven't received a ButtonRelease to cancel the * grab. * * We can solve this by querying after we take the explicit * pointer grab -- if the button isn't pressed, we cancel the * drag immediately. */ if ((button_mask & (1 << button)) == 0) meta_display_end_grab_op (window->display, timestamp); } } return TRUE; } else if (event->xclient.message_type == x11_display->atom__NET_MOVERESIZE_WINDOW) { MetaGravity gravity; guint value_mask; gravity = (MetaGravity) (event->xclient.data.l[0] & 0xff); value_mask = (event->xclient.data.l[0] & 0xf00) >> 8; /* source = (event->xclient.data.l[0] & 0xf000) >> 12; */ if (gravity == 0) gravity = window->size_hints.win_gravity; meta_window_move_resize_request(window, value_mask, gravity, event->xclient.data.l[1], /* x */ event->xclient.data.l[2], /* y */ event->xclient.data.l[3], /* width */ event->xclient.data.l[4]); /* height */ } else if (event->xclient.message_type == x11_display->atom__NET_ACTIVE_WINDOW) { MetaClientType source_indication; guint32 timestamp; meta_verbose ("_NET_ACTIVE_WINDOW request for window '%s', activating\n", window->desc); source_indication = event->xclient.data.l[0]; timestamp = event->xclient.data.l[1]; if (source_indication > META_CLIENT_TYPE_MAX_RECOGNIZED) source_indication = META_CLIENT_TYPE_UNKNOWN; if (timestamp == 0) { /* Client using older EWMH _NET_ACTIVE_WINDOW without a timestamp */ meta_warning ("Buggy client sent a _NET_ACTIVE_WINDOW message with a " "timestamp of 0 for %s\n", window->desc); timestamp = meta_display_get_current_time (display); } meta_window_activate_full (window, timestamp, source_indication, NULL); return TRUE; } else if (event->xclient.message_type == x11_display->atom__NET_WM_FULLSCREEN_MONITORS) { MetaLogicalMonitor *top, *bottom, *left, *right; meta_verbose ("_NET_WM_FULLSCREEN_MONITORS request for window '%s'\n", window->desc); top = meta_x11_display_xinerama_index_to_logical_monitor (window->display->x11_display, event->xclient.data.l[0]); bottom = meta_x11_display_xinerama_index_to_logical_monitor (window->display->x11_display, event->xclient.data.l[1]); left = meta_x11_display_xinerama_index_to_logical_monitor (window->display->x11_display, event->xclient.data.l[2]); right = meta_x11_display_xinerama_index_to_logical_monitor (window->display->x11_display, event->xclient.data.l[3]); /* source_indication = event->xclient.data.l[4]; */ meta_window_update_fullscreen_monitors (window, top, bottom, left, right); } else if (event->xclient.message_type == x11_display->atom__GTK_SHOW_WINDOW_MENU) { gulong x, y; /* l[0] is device_id, which we don't use */ x = event->xclient.data.l[1]; y = event->xclient.data.l[2]; meta_window_show_menu (window, META_WINDOW_MENU_WM, x, y); } else if (event->xclient.message_type == x11_display->atom__NET_RESTACK_WINDOW) { handle_net_restack_window (display, event); } return FALSE; } static void set_wm_state_on_xwindow (MetaDisplay *display, Window xwindow, int state) { unsigned long data[2]; /* Mutter doesn't use icon windows, so data[1] should be None * according to the ICCCM 2.0 Section 4.1.3.1. */ data[0] = state; data[1] = None; meta_x11_error_trap_push (display->x11_display); XChangeProperty (display->x11_display->xdisplay, xwindow, display->x11_display->atom_WM_STATE, display->x11_display->atom_WM_STATE, 32, PropModeReplace, (guchar*) data, 2); meta_x11_error_trap_pop (display->x11_display); } void meta_window_x11_set_wm_state (MetaWindow *window) { int state; if (window->withdrawn) state = WithdrawnState; else if (window->iconic) state = IconicState; else state = NormalState; set_wm_state_on_xwindow (window->display, window->xwindow, state); } /* The MUTTER_WM_CLASS_FILTER environment variable is designed for * performance and regression testing environments where we want to do * tests with only a limited set of windows and ignore all other windows * * When it is set to a comma separated list of WM_CLASS class names, all * windows not matching the list will be ignored. * * Returns TRUE if window has been filtered out and should be ignored. */ static gboolean maybe_filter_xwindow (MetaDisplay *display, Window xwindow, gboolean must_be_viewable, XWindowAttributes *attrs) { static char **filter_wm_classes = NULL; static gboolean initialized = FALSE; XClassHint class_hint; gboolean filtered; Status success; int i; if (!initialized) { const char *filter_string = g_getenv ("MUTTER_WM_CLASS_FILTER"); if (filter_string) filter_wm_classes = g_strsplit (filter_string, ",", -1); initialized = TRUE; } if (!filter_wm_classes || !filter_wm_classes[0]) return FALSE; filtered = TRUE; meta_x11_error_trap_push (display->x11_display); success = XGetClassHint (display->x11_display->xdisplay, xwindow, &class_hint); if (success) { for (i = 0; filter_wm_classes[i]; i++) { if (strcmp (class_hint.res_class, filter_wm_classes[i]) == 0) { filtered = FALSE; break; } } XFree (class_hint.res_name); XFree (class_hint.res_class); } if (filtered) { /* We want to try and get the window managed by the next WM that come along, * so we need to make sure that windows that are requested to be mapped while * Mutter is running (!must_be_viewable), or windows already viewable at startup * get a non-withdrawn WM_STATE property. Previously unmapped windows are left * with whatever WM_STATE property they had. */ if (!must_be_viewable || attrs->map_state == IsViewable) { uint32_t old_state; if (!meta_prop_get_cardinal_with_atom_type (display->x11_display, xwindow, display->x11_display->atom_WM_STATE, display->x11_display->atom_WM_STATE, &old_state)) old_state = WithdrawnState; if (old_state == WithdrawnState) set_wm_state_on_xwindow (display, xwindow, NormalState); } /* Make sure filtered windows are hidden from view */ XUnmapWindow (display->x11_display->xdisplay, xwindow); } meta_x11_error_trap_pop (display->x11_display); return filtered; } static gboolean is_our_xwindow (MetaX11Display *x11_display, Window xwindow, XWindowAttributes *attrs) { if (xwindow == x11_display->no_focus_window) return TRUE; if (xwindow == x11_display->wm_sn_selection_window) return TRUE; if (xwindow == x11_display->wm_cm_selection_window) return TRUE; if (xwindow == x11_display->guard_window) return TRUE; if (xwindow == x11_display->composite_overlay_window) return TRUE; { MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_X11 (backend)) { if (xwindow == meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend))) return TRUE; } } /* Any windows created via meta_create_offscreen_window */ if (attrs->override_redirect && attrs->x == -100 && attrs->y == -100 && attrs->width == 1 && attrs->height == 1) return TRUE; return FALSE; } #ifdef WITH_VERBOSE_MODE static const char* wm_state_to_string (int state) { switch (state) { case NormalState: return "NormalState"; case IconicState: return "IconicState"; case WithdrawnState: return "WithdrawnState"; } return "Unknown"; } #endif MetaWindow * meta_window_x11_new (MetaDisplay *display, Window xwindow, gboolean must_be_viewable, MetaCompEffect effect) { MetaX11Display *x11_display = display->x11_display; XWindowAttributes attrs; gulong existing_wm_state; MetaWindow *window = NULL; gulong event_mask; meta_verbose ("Attempting to manage 0x%lx\n", xwindow); if (meta_x11_display_xwindow_is_a_no_focus_window (x11_display, xwindow)) { meta_verbose ("Not managing no_focus_window 0x%lx\n", xwindow); return NULL; } meta_x11_error_trap_push (x11_display); /* Push a trap over all of window * creation, to reduce XSync() calls */ /* * This function executes without any server grabs held. This means that * the window could have already gone away, or could go away at any point, * so we must be careful with X error handling. */ if (!XGetWindowAttributes (x11_display->xdisplay, xwindow, &attrs)) { meta_verbose ("Failed to get attributes for window 0x%lx\n", xwindow); goto error; } if (attrs.root != x11_display->xroot) { meta_verbose ("Not on our screen\n"); goto error; } if (attrs.class == InputOnly) { meta_verbose ("Not managing InputOnly windows\n"); goto error; } if (is_our_xwindow (x11_display, xwindow, &attrs)) { meta_verbose ("Not managing our own windows\n"); goto error; } if (maybe_filter_xwindow (display, xwindow, must_be_viewable, &attrs)) { meta_verbose ("Not managing filtered window\n"); goto error; } existing_wm_state = WithdrawnState; if (must_be_viewable && attrs.map_state != IsViewable) { /* Only manage if WM_STATE is IconicState or NormalState */ uint32_t state; /* WM_STATE isn't a cardinal, it's type WM_STATE, but is an int */ if (!(meta_prop_get_cardinal_with_atom_type (x11_display, xwindow, x11_display->atom_WM_STATE, x11_display->atom_WM_STATE, &state) && (state == IconicState || state == NormalState))) { meta_verbose ("Deciding not to manage unmapped or unviewable window 0x%lx\n", xwindow); goto error; } existing_wm_state = state; meta_verbose ("WM_STATE of %lx = %s\n", xwindow, wm_state_to_string (existing_wm_state)); } /* * XAddToSaveSet can only be called on windows created by a different * client. with Mutter we want to be able to create manageable windows * from within the process (such as a dummy desktop window). As we do not * want this call failing to prevent the window from being managed, we * call this before creating the return-checked error trap. */ XAddToSaveSet (x11_display->xdisplay, xwindow); meta_x11_error_trap_push (x11_display); event_mask = PropertyChangeMask; if (attrs.override_redirect) event_mask |= StructureNotifyMask; /* If the window is from this client (a menu, say) we need to augment * the event mask, not replace it. For windows from other clients, * attrs.your_event_mask will be empty at this point. */ XSelectInput (x11_display->xdisplay, xwindow, attrs.your_event_mask | event_mask); { unsigned char mask_bits[XIMaskLen (XI_LASTEVENT)] = { 0 }; XIEventMask mask = { XIAllMasterDevices, sizeof (mask_bits), mask_bits }; XISetMask (mask.mask, XI_Enter); XISetMask (mask.mask, XI_Leave); XISetMask (mask.mask, XI_FocusIn); XISetMask (mask.mask, XI_FocusOut); XISelectEvents (x11_display->xdisplay, xwindow, &mask, 1); } if (META_X11_DISPLAY_HAS_SHAPE (x11_display)) XShapeSelectInput (x11_display->xdisplay, xwindow, ShapeNotifyMask); /* Get rid of any borders */ if (attrs.border_width != 0) XSetWindowBorderWidth (x11_display->xdisplay, xwindow, 0); /* Get rid of weird gravities */ if (attrs.win_gravity != NorthWestGravity) { XSetWindowAttributes set_attrs; set_attrs.win_gravity = NorthWestGravity; XChangeWindowAttributes (x11_display->xdisplay, xwindow, CWWinGravity, &set_attrs); } if (meta_x11_error_trap_pop_with_return (x11_display) != Success) { meta_verbose ("Window 0x%lx disappeared just as we tried to manage it\n", xwindow); goto error; } window = _meta_window_shared_new (display, META_WINDOW_CLIENT_TYPE_X11, NULL, xwindow, existing_wm_state, effect, &attrs); MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); priv->border_width = attrs.border_width; meta_window_grab_keys (window); if (window->type != META_WINDOW_DOCK && !window->override_redirect) { meta_display_grab_window_buttons (window->display, window->xwindow); meta_display_grab_focus_window_button (window->display, window); } meta_x11_error_trap_pop (x11_display); /* pop the XSync()-reducing trap */ return window; error: meta_x11_error_trap_pop (x11_display); return NULL; } void meta_window_x11_recalc_window_type (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); MetaWindowType type; if (priv->type_atom != None) { if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_DESKTOP) type = META_WINDOW_DESKTOP; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_DOCK) type = META_WINDOW_DOCK; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_TOOLBAR) type = META_WINDOW_TOOLBAR; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_MENU) type = META_WINDOW_MENU; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_UTILITY) type = META_WINDOW_UTILITY; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_SPLASH) type = META_WINDOW_SPLASHSCREEN; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_DIALOG) type = META_WINDOW_DIALOG; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_NORMAL) type = META_WINDOW_NORMAL; /* The below are *typically* override-redirect windows, but the spec does * not disallow using them for managed windows. */ else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_DROPDOWN_MENU) type = META_WINDOW_DROPDOWN_MENU; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_POPUP_MENU) type = META_WINDOW_POPUP_MENU; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_TOOLTIP) type = META_WINDOW_TOOLTIP; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_NOTIFICATION) type = META_WINDOW_NOTIFICATION; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_COMBO) type = META_WINDOW_COMBO; else if (priv->type_atom == x11_display->atom__NET_WM_WINDOW_TYPE_DND) type = META_WINDOW_DND; else { char *atom_name; /* * Fallback on a normal type, and print warning. Don't abort. */ type = META_WINDOW_NORMAL; meta_x11_error_trap_push (x11_display); atom_name = XGetAtomName (x11_display->xdisplay, priv->type_atom); meta_x11_error_trap_pop (x11_display); meta_warning ("Unrecognized type atom [%s] set for %s \n", atom_name ? atom_name : "unknown", window->desc); if (atom_name) XFree (atom_name); } } else if (window->transient_for != NULL) { type = META_WINDOW_DIALOG; } else { type = META_WINDOW_NORMAL; } if (type == META_WINDOW_DIALOG && priv->wm_state_modal) type = META_WINDOW_MODAL_DIALOG; /* We don't want to allow override-redirect windows to have decorated-window * types since that's just confusing. */ if (window->override_redirect) { switch (type) { /* Decorated types */ case META_WINDOW_NORMAL: case META_WINDOW_DIALOG: case META_WINDOW_MODAL_DIALOG: case META_WINDOW_MENU: case META_WINDOW_UTILITY: type = META_WINDOW_OVERRIDE_OTHER; break; /* Undecorated types, normally not override-redirect */ case META_WINDOW_DESKTOP: case META_WINDOW_DOCK: case META_WINDOW_TOOLBAR: case META_WINDOW_SPLASHSCREEN: /* Undecorated types, normally override-redirect types */ case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_POPUP_MENU: case META_WINDOW_TOOLTIP: case META_WINDOW_NOTIFICATION: case META_WINDOW_COMBO: case META_WINDOW_DND: /* To complete enum */ case META_WINDOW_OVERRIDE_OTHER: break; } } meta_verbose ("Calculated type %u for %s, old type %u\n", type, window->desc, type); meta_window_set_type (window, type); } /** * meta_window_x11_configure_notify: (skip) * @window: a #MetaWindow * @event: a #XConfigureEvent * * This is used to notify us of an unrequested configuration * (only applicable to override redirect windows) */ void meta_window_x11_configure_notify (MetaWindow *window, XConfigureEvent *event) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); g_assert (window->override_redirect); g_assert (window->frame == NULL); window->rect.x = event->x; window->rect.y = event->y; window->rect.width = event->width; window->rect.height = event->height; priv->client_rect = window->rect; window->buffer_rect = window->rect; meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_NONE); /* Whether an override-redirect window is considered fullscreen depends * on its geometry. */ if (window->override_redirect) meta_display_queue_check_fullscreen (window->display); if (!event->override_redirect && !event->send_event) meta_warning ("Unhandled change of windows override redirect status\n"); meta_compositor_sync_window_geometry (window->display->compositor, window, FALSE); } void meta_window_x11_set_allowed_actions_hint (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; #define MAX_N_ACTIONS 12 unsigned long data[MAX_N_ACTIONS]; int i; i = 0; if (window->has_move_func) { data[i] = x11_display->atom__NET_WM_ACTION_MOVE; ++i; } if (window->has_resize_func) { data[i] = x11_display->atom__NET_WM_ACTION_RESIZE; ++i; } if (window->has_fullscreen_func) { data[i] = x11_display->atom__NET_WM_ACTION_FULLSCREEN; ++i; } if (window->has_minimize_func) { data[i] = x11_display->atom__NET_WM_ACTION_MINIMIZE; ++i; } if (window->has_shade_func) { data[i] = x11_display->atom__NET_WM_ACTION_SHADE; ++i; } /* sticky according to EWMH is different from mutter's sticky; * mutter doesn't support EWMH sticky */ if (window->has_maximize_func) { data[i] = x11_display->atom__NET_WM_ACTION_MAXIMIZE_HORZ; ++i; data[i] = x11_display->atom__NET_WM_ACTION_MAXIMIZE_VERT; ++i; } /* We always allow this */ data[i] = x11_display->atom__NET_WM_ACTION_CHANGE_DESKTOP; ++i; if (window->has_close_func) { data[i] = x11_display->atom__NET_WM_ACTION_CLOSE; ++i; } /* I guess we always allow above/below operations */ data[i] = x11_display->atom__NET_WM_ACTION_ABOVE; ++i; data[i] = x11_display->atom__NET_WM_ACTION_BELOW; ++i; g_assert (i <= MAX_N_ACTIONS); meta_verbose ("Setting _NET_WM_ALLOWED_ACTIONS with %d atoms\n", i); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, window->xwindow, x11_display->atom__NET_WM_ALLOWED_ACTIONS, XA_ATOM, 32, PropModeReplace, (guchar*) data, i); meta_x11_error_trap_pop (x11_display); #undef MAX_N_ACTIONS } void meta_window_x11_create_sync_request_alarm (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; XSyncAlarmAttributes values; XSyncValue init; if (window->sync_request_counter == None || window->sync_request_alarm != None) return; meta_x11_error_trap_push (x11_display); /* In the new (extended style), the counter value is initialized by * the client before mapping the window. In the old style, we're * responsible for setting the initial value of the counter. */ if (window->extended_sync_request_counter) { if (!XSyncQueryCounter(x11_display->xdisplay, window->sync_request_counter, &init)) { meta_x11_error_trap_pop_with_return (x11_display); window->sync_request_counter = None; return; } window->sync_request_serial = XSyncValueLow32 (init) + ((gint64)XSyncValueHigh32 (init) << 32); } else { XSyncIntToValue (&init, 0); XSyncSetCounter (x11_display->xdisplay, window->sync_request_counter, init); window->sync_request_serial = 0; } values.trigger.counter = window->sync_request_counter; values.trigger.test_type = XSyncPositiveComparison; /* Initialize to one greater than the current value */ values.trigger.value_type = XSyncRelative; XSyncIntToValue (&values.trigger.wait_value, 1); /* After triggering, increment test_value by this until * until the test condition is false */ XSyncIntToValue (&values.delta, 1); /* we want events (on by default anyway) */ values.events = True; window->sync_request_alarm = XSyncCreateAlarm (x11_display->xdisplay, XSyncCACounter | XSyncCAValueType | XSyncCAValue | XSyncCATestType | XSyncCADelta | XSyncCAEvents, &values); if (meta_x11_error_trap_pop_with_return (x11_display) == Success) meta_x11_display_register_sync_alarm (x11_display, &window->sync_request_alarm, window); else { window->sync_request_alarm = None; window->sync_request_counter = None; } } void meta_window_x11_destroy_sync_request_alarm (MetaWindow *window) { MetaX11Display *x11_display = window->display->x11_display; if (window->sync_request_alarm != None) { /* Has to be unregistered _before_ clearing the structure field */ meta_x11_display_unregister_sync_alarm (x11_display, window->sync_request_alarm); XSyncDestroyAlarm (x11_display->xdisplay, window->sync_request_alarm); window->sync_request_alarm = None; } } void meta_window_x11_update_sync_request_counter (MetaWindow *window, gint64 new_counter_value) { gboolean needs_frame_drawn = FALSE; gboolean no_delay_frame = FALSE; if (window->extended_sync_request_counter && new_counter_value % 2 == 0) { needs_frame_drawn = TRUE; no_delay_frame = new_counter_value == window->sync_request_serial + 1; } window->sync_request_serial = new_counter_value; meta_compositor_sync_updates_frozen (window->display->compositor, window); if (new_counter_value >= window->sync_request_wait_serial && window->sync_request_timeout_id) { if (!window->extended_sync_request_counter || new_counter_value % 2 == 0) g_clear_handle_id (&window->sync_request_timeout_id, g_source_remove); if (window == window->display->grab_window && meta_grab_op_is_resizing (window->display->grab_op) && (!window->extended_sync_request_counter || new_counter_value % 2 == 0)) { meta_topic (META_DEBUG_RESIZING, "Alarm event received last motion x = %d y = %d\n", window->display->grab_latest_motion_x, window->display->grab_latest_motion_y); /* This means we are ready for another configure; * no pointer round trip here, to keep in sync */ meta_window_update_resize (window, window->display->grab_last_user_action_was_snap, window->display->grab_latest_motion_x, window->display->grab_latest_motion_y, TRUE); } } /* If sync was previously disabled, turn it back on and hope * the application has come to its senses (maybe it was just * busy with a pagefault or a long computation). */ window->disable_sync = FALSE; if (needs_frame_drawn) meta_compositor_queue_frame_drawn (window->display->compositor, window, no_delay_frame); } Window meta_window_x11_get_toplevel_xwindow (MetaWindow *window) { return window->frame ? window->frame->xwindow : window->xwindow; } void meta_window_x11_freeze_commits (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); META_WINDOW_X11_GET_CLASS (window_x11)->freeze_commits (window); } void meta_window_x11_thaw_commits (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); META_WINDOW_X11_GET_CLASS (window_x11)->thaw_commits (window); } void meta_window_x11_set_thaw_after_paint (MetaWindow *window, gboolean thaw_after_paint) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); priv->thaw_after_paint = thaw_after_paint; } gboolean meta_window_x11_should_thaw_after_paint (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); MetaWindowX11Private *priv = meta_window_x11_get_instance_private (window_x11); return priv->thaw_after_paint; } gboolean meta_window_x11_always_update_shape (MetaWindow *window) { MetaWindowX11 *window_x11 = META_WINDOW_X11 (window); return META_WINDOW_X11_GET_CLASS (window_x11)->always_update_shape (window); } void meta_window_x11_surface_rect_to_frame_rect (MetaWindow *window, MetaRectangle *surface_rect, MetaRectangle *frame_rect) { MetaFrameBorders borders; g_return_if_fail (window->frame); meta_frame_calc_borders (window->frame, &borders); *frame_rect = *surface_rect; frame_rect->x += borders.invisible.left; frame_rect->y += borders.invisible.top; frame_rect->width -= borders.invisible.left + borders.invisible.right; frame_rect->height -= borders.invisible.top + borders.invisible.bottom; } void meta_window_x11_surface_rect_to_client_rect (MetaWindow *window, MetaRectangle *surface_rect, MetaRectangle *client_rect) { MetaFrameBorders borders; meta_frame_calc_borders (window->frame, &borders); *client_rect = *surface_rect; client_rect->x += borders.total.left; client_rect->y += borders.total.top; client_rect->width -= borders.total.left + borders.total.right; client_rect->height -= borders.total.top + borders.total.bottom; } muffin-6.4.1/src/x11/meta-startup-notification-x11.c0000664000175000017500000002620514723361714021034 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2018 Red Hat, Inc * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "meta-startup-notification-x11.h" #include #include "core/display-private.h" #include "core/startup-notification-private.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" #ifdef HAVE_STARTUP_NOTIFICATION enum { PROP_SEQ_X11_0, PROP_SEQ_X11_SEQ, N_SEQ_X11_PROPS }; struct _MetaStartupSequenceX11 { MetaStartupSequence parent_instance; SnStartupSequence *seq; }; struct _MetaX11StartupNotification { SnDisplay *sn_display; SnMonitorContext *sn_context; }; static GParamSpec *seq_x11_props[N_SEQ_X11_PROPS]; G_DEFINE_TYPE (MetaStartupSequenceX11, meta_startup_sequence_x11, META_TYPE_STARTUP_SEQUENCE) static void meta_startup_sequence_x11_complete (MetaStartupSequence *seq) { MetaStartupSequenceX11 *seq_x11; seq_x11 = META_STARTUP_SEQUENCE_X11 (seq); sn_startup_sequence_complete (seq_x11->seq); } static void meta_startup_sequence_x11_finalize (GObject *object) { MetaStartupSequenceX11 *seq_x11; seq_x11 = META_STARTUP_SEQUENCE_X11 (object); sn_startup_sequence_unref (seq_x11->seq); G_OBJECT_CLASS (meta_startup_sequence_x11_parent_class)->finalize (object); } static void meta_startup_sequence_x11_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaStartupSequenceX11 *seq_x11; seq_x11 = META_STARTUP_SEQUENCE_X11 (object); switch (prop_id) { case PROP_SEQ_X11_SEQ: seq_x11->seq = g_value_get_pointer (value); sn_startup_sequence_ref (seq_x11->seq); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_sequence_x11_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaStartupSequenceX11 *seq_x11; seq_x11 = META_STARTUP_SEQUENCE_X11 (object); switch (prop_id) { case PROP_SEQ_X11_SEQ: g_value_set_pointer (value, seq_x11->seq); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_startup_sequence_x11_init (MetaStartupSequenceX11 *seq) { } static void meta_startup_sequence_x11_class_init (MetaStartupSequenceX11Class *klass) { MetaStartupSequenceClass *seq_class; GObjectClass *object_class; seq_class = META_STARTUP_SEQUENCE_CLASS (klass); seq_class->complete = meta_startup_sequence_x11_complete; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_startup_sequence_x11_finalize; object_class->set_property = meta_startup_sequence_x11_set_property; object_class->get_property = meta_startup_sequence_x11_get_property; seq_x11_props[PROP_SEQ_X11_SEQ] = g_param_spec_pointer ("seq", "Sequence", "Sequence", G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_properties (object_class, N_SEQ_X11_PROPS, seq_x11_props); } static MetaStartupSequence * meta_startup_sequence_x11_new (SnStartupSequence *seq) { gint64 timestamp; timestamp = sn_startup_sequence_get_timestamp (seq); return g_object_new (META_TYPE_STARTUP_SEQUENCE_X11, "id", sn_startup_sequence_get_id (seq), "icon-name", sn_startup_sequence_get_icon_name (seq), "application-id", sn_startup_sequence_get_application_id (seq), "wmclass", sn_startup_sequence_get_wmclass (seq), "name", sn_startup_sequence_get_name (seq), "workspace", sn_startup_sequence_get_workspace (seq), "timestamp", timestamp, "seq", seq, NULL); } static void sn_error_trap_push (SnDisplay *sn_display, Display *xdisplay) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); if (display != NULL) meta_x11_error_trap_push (display->x11_display); } static void sn_error_trap_pop (SnDisplay *sn_display, Display *xdisplay) { MetaDisplay *display; display = meta_display_for_x_display (xdisplay); if (display != NULL) meta_x11_error_trap_pop (display->x11_display); } static void meta_startup_notification_sn_event (SnMonitorEvent *event, void *user_data) { MetaX11Display *x11_display = user_data; MetaStartupNotification *sn = x11_display->display->startup_notification; MetaStartupSequence *seq; SnStartupSequence *sequence; sequence = sn_monitor_event_get_startup_sequence (event); sn_startup_sequence_ref (sequence); switch (sn_monitor_event_get_type (event)) { case SN_MONITOR_EVENT_INITIATED: { const char *wmclass; wmclass = sn_startup_sequence_get_wmclass (sequence); meta_topic (META_DEBUG_STARTUP, "Received startup initiated for %s wmclass %s\n", sn_startup_sequence_get_id (sequence), wmclass ? wmclass : "(unset)"); seq = meta_startup_sequence_x11_new (sequence); meta_startup_notification_add_sequence (sn, seq); g_object_unref (seq); } break; case SN_MONITOR_EVENT_COMPLETED: { meta_topic (META_DEBUG_STARTUP, "Received startup completed for %s\n", sn_startup_sequence_get_id (sequence)); seq = meta_startup_notification_lookup_sequence (sn, sn_startup_sequence_get_id (sequence)); if (seq) { meta_startup_sequence_complete (seq); meta_startup_notification_remove_sequence (sn, seq); } } break; case SN_MONITOR_EVENT_CHANGED: meta_topic (META_DEBUG_STARTUP, "Received startup changed for %s\n", sn_startup_sequence_get_id (sequence)); break; case SN_MONITOR_EVENT_CANCELED: meta_topic (META_DEBUG_STARTUP, "Received startup canceled for %s\n", sn_startup_sequence_get_id (sequence)); break; } sn_startup_sequence_unref (sequence); } #endif void meta_x11_startup_notification_init (MetaX11Display *x11_display) { #ifdef HAVE_STARTUP_NOTIFICATION MetaX11StartupNotification *x11_sn; x11_sn = g_new0 (MetaX11StartupNotification, 1); x11_sn->sn_display = sn_display_new (x11_display->xdisplay, sn_error_trap_push, sn_error_trap_pop); x11_sn->sn_context = sn_monitor_context_new (x11_sn->sn_display, meta_x11_display_get_screen_number (x11_display), meta_startup_notification_sn_event, x11_display, NULL); x11_display->startup_notification = x11_sn; #endif } void meta_x11_startup_notification_release (MetaX11Display *x11_display) { #ifdef HAVE_STARTUP_NOTIFICATION MetaX11StartupNotification *x11_sn = x11_display->startup_notification; x11_display->startup_notification = NULL; if (x11_sn) { sn_monitor_context_unref (x11_sn->sn_context); sn_display_unref (x11_sn->sn_display); g_free (x11_sn); } #endif } gboolean meta_x11_startup_notification_handle_xevent (MetaX11Display *x11_display, XEvent *xevent) { #ifdef HAVE_STARTUP_NOTIFICATION MetaX11StartupNotification *x11_sn = x11_display->startup_notification; if (!x11_sn) return FALSE; return sn_display_process_event (x11_sn->sn_display, xevent); #else return FALSE; #endif } #ifdef HAVE_STARTUP_NOTIFICATION typedef void (* SetAppIdFunc) (SnLauncherContext *context, const char *app_id); #endif gchar * meta_x11_startup_notification_launch (MetaX11Display *x11_display, GAppInfo *app_info, uint32_t timestamp, int workspace) { gchar *startup_id = NULL; #ifdef HAVE_STARTUP_NOTIFICATION MetaX11StartupNotification *x11_sn = x11_display->startup_notification; SnLauncherContext *sn_launcher; int screen; screen = meta_x11_display_get_screen_number (x11_display); sn_launcher = sn_launcher_context_new (x11_sn->sn_display, screen); sn_launcher_context_set_name (sn_launcher, g_app_info_get_name (app_info)); sn_launcher_context_set_workspace (sn_launcher, workspace); sn_launcher_context_set_binary_name (sn_launcher, g_app_info_get_executable (app_info)); if (G_IS_DESKTOP_APP_INFO (app_info)) { const char *application_id; SetAppIdFunc func = NULL; GModule *self; application_id = g_desktop_app_info_get_filename (G_DESKTOP_APP_INFO (app_info)); self = g_module_open (NULL, G_MODULE_BIND_MASK); /* This here is a terrible workaround to bypass a libsn bug that is not * likely to get fixed at this point. * sn_launcher_context_set_application_id is correctly defined in the * sn-launcher.h file, but it's mistakenly called * sn_launcher_set_application_id in the C file. * * We look up the symbol instead, but still prefer the correctly named * function, if one were ever to be added. */ if (!g_module_symbol (self, "sn_launcher_context_set_application_id", (gpointer *) &func)) { g_module_symbol (self, "sn_launcher_set_application_id", (gpointer *) &func); } if (func && application_id) func (sn_launcher, application_id); g_module_close (self); } sn_launcher_context_initiate (sn_launcher, g_get_prgname (), g_app_info_get_name (app_info), timestamp); startup_id = g_strdup (sn_launcher_context_get_startup_id (sn_launcher)); /* Fire and forget, we have a SnMonitor in addition */ sn_launcher_context_unref (sn_launcher); #endif /* HAVE_STARTUP_NOTIFICATION */ return startup_id; } muffin-6.4.1/src/x11/meta-selection-source-x11-private.h0000664000175000017500000000404114723361714021600 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_SELECTION_SOURCE_X11_H #define META_SELECTION_SOURCE_X11_H #include "meta/meta-selection-source.h" #include "x11/meta-x11-display-private.h" #define META_TYPE_SELECTION_SOURCE_X11 (meta_selection_source_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaSelectionSourceX11, meta_selection_source_x11, META, SELECTION_SOURCE_X11, MetaSelectionSource) void meta_selection_source_x11_new_async (MetaX11Display *display, Window owner, uint32_t timestamp, Atom xselection, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); MetaSelectionSource * meta_selection_source_x11_new_finish (GAsyncResult *result, GError **error); #endif /* META_SELECTION_SOURCE_X11_H */ muffin-6.4.1/src/x11/events.c0000664000175000017500000017240414723361714014622 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2002, 2003, 2004 Red Hat, Inc. * Copyright (C) 2003, 2004 Rob Adams * Copyright (C) 2004-2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "x11/events.h" #include #include #include #include #include "backends/meta-cursor-tracker-private.h" #include "backends/x11/meta-backend-x11.h" #include "compositor/meta-compositor-x11.h" #include "cogl/cogl.h" #include "core/bell.h" #include "core/display-private.h" #include "core/meta-workspace-manager-private.h" #include "core/window-private.h" #include "core/workspace-private.h" #include "meta/meta-backend.h" #include "meta/meta-x11-errors.h" #include "x11/meta-startup-notification-x11.h" #include "x11/meta-x11-display-private.h" #include "x11/meta-x11-selection-private.h" #include "x11/meta-x11-selection-input-stream-private.h" #include "x11/meta-x11-selection-output-stream-private.h" #include "x11/window-x11.h" #include "x11/xprops.h" #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #include "wayland/meta-xwayland-private.h" #include "wayland/meta-xwayland.h" #endif static XIEvent * get_input_event (MetaX11Display *x11_display, XEvent *event) { if (event->type == GenericEvent && event->xcookie.extension == x11_display->xinput_opcode) { XIEvent *input_event; /* NB: GDK event filters already have generic events * allocated, so no need to do XGetEventData() on our own */ input_event = (XIEvent *) event->xcookie.data; switch (input_event->evtype) { case XI_Motion: case XI_ButtonPress: case XI_ButtonRelease: if (((XIDeviceEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID) return input_event; break; case XI_KeyPress: case XI_KeyRelease: if (((XIDeviceEvent *) input_event)->deviceid == META_VIRTUAL_CORE_KEYBOARD_ID) return input_event; break; case XI_FocusIn: case XI_FocusOut: if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_KEYBOARD_ID) return input_event; break; case XI_Enter: case XI_Leave: if (((XIEnterEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID) return input_event; break; case XI_BarrierHit: case XI_BarrierLeave: if (((XIBarrierEvent *) input_event)->deviceid == META_VIRTUAL_CORE_POINTER_ID) return input_event; break; default: break; } } return NULL; } static Window xievent_get_modified_window (MetaX11Display *x11_display, XIEvent *input_event) { switch (input_event->evtype) { case XI_Motion: case XI_ButtonPress: case XI_ButtonRelease: case XI_KeyPress: case XI_KeyRelease: return ((XIDeviceEvent *) input_event)->event; case XI_FocusIn: case XI_FocusOut: case XI_Enter: case XI_Leave: return ((XIEnterEvent *) input_event)->event; case XI_BarrierHit: case XI_BarrierLeave: return ((XIBarrierEvent *) input_event)->event; } return None; } /* Return the window this has to do with, if any, rather * than the frame or root window that was selecting * for substructure */ static Window event_get_modified_window (MetaX11Display *x11_display, XEvent *event) { XIEvent *input_event = get_input_event (x11_display, event); if (input_event) return xievent_get_modified_window (x11_display, input_event); switch (event->type) { case KeymapNotify: case Expose: case GraphicsExpose: case NoExpose: case VisibilityNotify: case ResizeRequest: case PropertyNotify: case SelectionClear: case SelectionRequest: case SelectionNotify: case ColormapNotify: case ClientMessage: return event->xany.window; case CreateNotify: return event->xcreatewindow.window; case DestroyNotify: return event->xdestroywindow.window; case UnmapNotify: return event->xunmap.window; case MapNotify: return event->xmap.window; case MapRequest: return event->xmaprequest.window; case ReparentNotify: return event->xreparent.window; case ConfigureNotify: return event->xconfigure.window; case ConfigureRequest: return event->xconfigurerequest.window; case GravityNotify: return event->xgravity.window; case CirculateNotify: return event->xcirculate.window; case CirculateRequest: return event->xcirculaterequest.window; case MappingNotify: return None; default: if (META_X11_DISPLAY_HAS_SHAPE (x11_display) && event->type == (x11_display->shape_event_base + ShapeNotify)) { XShapeEvent *sev = (XShapeEvent*) event; return sev->window; } return None; } } static guint32 event_get_time (MetaX11Display *x11_display, XEvent *event) { XIEvent *input_event = get_input_event (x11_display, event); if (input_event) return input_event->time; switch (event->type) { case PropertyNotify: return event->xproperty.time; case SelectionClear: case SelectionRequest: case SelectionNotify: return event->xselection.time; case KeymapNotify: case Expose: case GraphicsExpose: case NoExpose: case MapNotify: case UnmapNotify: case VisibilityNotify: case ResizeRequest: case ColormapNotify: case ClientMessage: case CreateNotify: case DestroyNotify: case MapRequest: case ReparentNotify: case ConfigureNotify: case ConfigureRequest: case GravityNotify: case CirculateNotify: case CirculateRequest: case MappingNotify: default: return META_CURRENT_TIME; } } const char* meta_event_detail_to_string (int d) { const char *detail = "???"; switch (d) { /* We are an ancestor in the A<->B focus change relationship */ case XINotifyAncestor: detail = "NotifyAncestor"; break; case XINotifyDetailNone: detail = "NotifyDetailNone"; break; /* We are a descendant in the A<->B focus change relationship */ case XINotifyInferior: detail = "NotifyInferior"; break; case XINotifyNonlinear: detail = "NotifyNonlinear"; break; case XINotifyNonlinearVirtual: detail = "NotifyNonlinearVirtual"; break; case XINotifyPointer: detail = "NotifyPointer"; break; case XINotifyPointerRoot: detail = "NotifyPointerRoot"; break; case XINotifyVirtual: detail = "NotifyVirtual"; break; } return detail; } const char* meta_event_mode_to_string (int m) { const char *mode = "???"; switch (m) { case XINotifyNormal: mode = "NotifyNormal"; break; case XINotifyGrab: mode = "NotifyGrab"; break; case XINotifyUngrab: mode = "NotifyUngrab"; break; case XINotifyWhileGrabbed: mode = "NotifyWhileGrabbed"; break; } return mode; } G_GNUC_UNUSED static const char* stack_mode_to_string (int mode) { switch (mode) { case Above: return "Above"; case Below: return "Below"; case TopIf: return "TopIf"; case BottomIf: return "BottomIf"; case Opposite: return "Opposite"; } return "Unknown"; } static gint64 sync_value_to_64 (const XSyncValue *value) { gint64 v; v = XSyncValueLow32 (*value); v |= (((gint64)XSyncValueHigh32 (*value)) << 32); return v; } static const char* alarm_state_to_string (XSyncAlarmState state) { switch (state) { case XSyncAlarmActive: return "Active"; case XSyncAlarmInactive: return "Inactive"; case XSyncAlarmDestroyed: return "Destroyed"; default: return "(unknown)"; } } static void meta_spew_xi2_event (MetaX11Display *x11_display, XIEvent *input_event, const char **name_p, char **extra_p) { const char *name = NULL; char *extra = NULL; XIEnterEvent *enter_event = (XIEnterEvent *) input_event; switch (input_event->evtype) { case XI_FocusIn: name = "XI_FocusIn"; break; case XI_FocusOut: name = "XI_FocusOut"; break; case XI_Enter: name = "XI_Enter"; break; case XI_Leave: name = "XI_Leave"; break; case XI_BarrierHit: name = "XI_BarrierHit"; break; case XI_BarrierLeave: name = "XI_BarrierLeave"; break; } switch (input_event->evtype) { case XI_FocusIn: case XI_FocusOut: extra = g_strdup_printf ("detail: %s mode: %s\n", meta_event_detail_to_string (enter_event->detail), meta_event_mode_to_string (enter_event->mode)); break; case XI_Enter: case XI_Leave: extra = g_strdup_printf ("win: 0x%lx root: 0x%lx mode: %s detail: %s focus: %d x: %g y: %g", enter_event->event, enter_event->root, meta_event_mode_to_string (enter_event->mode), meta_event_detail_to_string (enter_event->detail), enter_event->focus, enter_event->root_x, enter_event->root_y); break; } *name_p = name; *extra_p = extra; } static void meta_spew_core_event (MetaX11Display *x11_display, XEvent *event, const char **name_p, char **extra_p) { const char *name = NULL; char *extra = NULL; switch (event->type) { case KeymapNotify: name = "KeymapNotify"; break; case Expose: name = "Expose"; break; case GraphicsExpose: name = "GraphicsExpose"; break; case NoExpose: name = "NoExpose"; break; case VisibilityNotify: name = "VisibilityNotify"; break; case CreateNotify: name = "CreateNotify"; extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx", event->xcreatewindow.parent, event->xcreatewindow.window); break; case DestroyNotify: name = "DestroyNotify"; extra = g_strdup_printf ("event: 0x%lx window: 0x%lx", event->xdestroywindow.event, event->xdestroywindow.window); break; case UnmapNotify: name = "UnmapNotify"; extra = g_strdup_printf ("event: 0x%lx window: 0x%lx from_configure: %d", event->xunmap.event, event->xunmap.window, event->xunmap.from_configure); break; case MapNotify: name = "MapNotify"; extra = g_strdup_printf ("event: 0x%lx window: 0x%lx override_redirect: %d", event->xmap.event, event->xmap.window, event->xmap.override_redirect); break; case MapRequest: name = "MapRequest"; extra = g_strdup_printf ("window: 0x%lx parent: 0x%lx\n", event->xmaprequest.window, event->xmaprequest.parent); break; case ReparentNotify: name = "ReparentNotify"; extra = g_strdup_printf ("window: 0x%lx parent: 0x%lx event: 0x%lx\n", event->xreparent.window, event->xreparent.parent, event->xreparent.event); break; case ConfigureNotify: name = "ConfigureNotify"; extra = g_strdup_printf ("x: %d y: %d w: %d h: %d above: 0x%lx override_redirect: %d", event->xconfigure.x, event->xconfigure.y, event->xconfigure.width, event->xconfigure.height, event->xconfigure.above, event->xconfigure.override_redirect); break; case ConfigureRequest: name = "ConfigureRequest"; extra = g_strdup_printf ("parent: 0x%lx window: 0x%lx x: %d %sy: %d %sw: %d %sh: %d %sborder: %d %sabove: %lx %sstackmode: %s %s", event->xconfigurerequest.parent, event->xconfigurerequest.window, event->xconfigurerequest.x, event->xconfigurerequest.value_mask & CWX ? "" : "(unset) ", event->xconfigurerequest.y, event->xconfigurerequest.value_mask & CWY ? "" : "(unset) ", event->xconfigurerequest.width, event->xconfigurerequest.value_mask & CWWidth ? "" : "(unset) ", event->xconfigurerequest.height, event->xconfigurerequest.value_mask & CWHeight ? "" : "(unset) ", event->xconfigurerequest.border_width, event->xconfigurerequest.value_mask & CWBorderWidth ? "" : "(unset)", event->xconfigurerequest.above, event->xconfigurerequest.value_mask & CWSibling ? "" : "(unset)", stack_mode_to_string (event->xconfigurerequest.detail), event->xconfigurerequest.value_mask & CWStackMode ? "" : "(unset)"); break; case GravityNotify: name = "GravityNotify"; break; case ResizeRequest: name = "ResizeRequest"; extra = g_strdup_printf ("width = %d height = %d", event->xresizerequest.width, event->xresizerequest.height); break; case CirculateNotify: name = "CirculateNotify"; break; case CirculateRequest: name = "CirculateRequest"; break; case PropertyNotify: { char *str; const char *state; name = "PropertyNotify"; meta_x11_error_trap_push (x11_display); str = XGetAtomName (x11_display->xdisplay, event->xproperty.atom); meta_x11_error_trap_pop (x11_display); if (event->xproperty.state == PropertyNewValue) state = "PropertyNewValue"; else if (event->xproperty.state == PropertyDelete) state = "PropertyDelete"; else state = "???"; extra = g_strdup_printf ("atom: %s state: %s", str ? str : "(unknown atom)", state); meta_XFree (str); } break; case SelectionClear: name = "SelectionClear"; break; case SelectionRequest: name = "SelectionRequest"; break; case SelectionNotify: name = "SelectionNotify"; break; case ColormapNotify: name = "ColormapNotify"; break; case ClientMessage: { char *str; name = "ClientMessage"; meta_x11_error_trap_push (x11_display); str = XGetAtomName (x11_display->xdisplay, event->xclient.message_type); meta_x11_error_trap_pop (x11_display); extra = g_strdup_printf ("type: %s format: %d\n", str ? str : "(unknown atom)", event->xclient.format); meta_XFree (str); } break; case MappingNotify: name = "MappingNotify"; break; default: if (META_X11_DISPLAY_HAS_XSYNC (x11_display) && event->type == (x11_display->xsync_event_base + XSyncAlarmNotify)) { XSyncAlarmNotifyEvent *aevent = (XSyncAlarmNotifyEvent*) event; name = "XSyncAlarmNotify"; extra = g_strdup_printf ("alarm: 0x%lx" " counter_value: %" G_GINT64_FORMAT " alarm_value: %" G_GINT64_FORMAT " time: %u alarm state: %s", aevent->alarm, (gint64) sync_value_to_64 (&aevent->counter_value), (gint64) sync_value_to_64 (&aevent->alarm_value), (unsigned int)aevent->time, alarm_state_to_string (aevent->state)); } else if (META_X11_DISPLAY_HAS_SHAPE (x11_display) && event->type == (x11_display->shape_event_base + ShapeNotify)) { XShapeEvent *sev = (XShapeEvent*) event; name = "ShapeNotify"; extra = g_strdup_printf ("kind: %s " "x: %d y: %d w: %u h: %u " "shaped: %d", sev->kind == ShapeBounding ? "ShapeBounding" : (sev->kind == ShapeClip ? "ShapeClip" : "(unknown)"), sev->x, sev->y, sev->width, sev->height, sev->shaped); } else { name = "(Unknown event)"; extra = g_strdup_printf ("type: %d", event->xany.type); } break; } *name_p = name; *extra_p = extra; } static char * meta_spew_event (MetaX11Display *x11_display, XEvent *event) { const char *name = NULL; char *extra = NULL; char *winname; char *ret; XIEvent *input_event; input_event = get_input_event (x11_display, event); if (input_event) meta_spew_xi2_event (x11_display, input_event, &name, &extra); else meta_spew_core_event (x11_display, event, &name, &extra); if (event->xany.window == x11_display->xroot) winname = g_strdup_printf ("root"); else winname = g_strdup_printf ("0x%lx", event->xany.window); ret = g_strdup_printf ("%s on %s%s %s %sserial %lu", name, winname, extra ? ":" : "", extra ? extra : "", event->xany.send_event ? "SEND " : "", event->xany.serial); g_free (winname); g_free (extra); return ret; } G_GNUC_UNUSED static void meta_spew_event_print (MetaX11Display *x11_display, XEvent *event) { char *event_str; /* filter overnumerous events */ if (event->type == Expose || event->type == MotionNotify || event->type == NoExpose) return; if (event->type == (x11_display->damage_event_base + XDamageNotify)) return; if (event->type == (x11_display->xsync_event_base + XSyncAlarmNotify)) return; if (event->type == PropertyNotify && event->xproperty.atom == x11_display->atom__NET_WM_USER_TIME) return; event_str = meta_spew_event (x11_display, event); g_print ("%s\n", event_str); g_free (event_str); } static gboolean handle_window_focus_event (MetaX11Display *x11_display, MetaWindow *window, XIEnterEvent *event, unsigned long serial) { MetaDisplay *display = x11_display->display; MetaWindow *focus_window; #ifdef WITH_VERBOSE_MODE const char *window_type; /* Note the event can be on either the window or the frame, * we focus the frame for shaded windows */ if (window) { if (event->event == window->xwindow) window_type = "client window"; else if (window->frame && event->event == window->frame->xwindow) window_type = "frame window"; else window_type = "unknown client window"; } else if (meta_x11_display_xwindow_is_a_no_focus_window (x11_display, event->event)) window_type = "no_focus_window"; else if (event->event == x11_display->xroot) window_type = "root window"; else window_type = "unknown window"; meta_topic (META_DEBUG_FOCUS, "Focus %s event received on %s 0x%lx (%s) " "mode %s detail %s serial %lu\n", event->evtype == XI_FocusIn ? "in" : event->evtype == XI_FocusOut ? "out" : "???", window ? window->desc : "", event->event, window_type, meta_event_mode_to_string (event->mode), meta_event_detail_to_string (event->mode), serial); #endif /* FIXME our pointer tracking is broken; see how * gtk+/gdk/x11/gdkevents-x11.c or XFree86/xc/programs/xterm/misc.c * for how to handle it the correct way. In brief you need to track * pointer focus and regular focus, and handle EnterNotify in * PointerRoot mode with no window manager. However as noted above, * accurate focus tracking will break things because we want to keep * windows "focused" when using keybindings on them, and also we * sometimes "focus" a window by focusing its frame or * no_focus_window; so this all needs rethinking massively. * * My suggestion is to change it so that we clearly separate * actual keyboard focus tracking using the xterm algorithm, * and mutter's "pretend" focus window, and go through all * the code and decide which one should be used in each place; * a hard bit is deciding on a policy for that. * * http://bugzilla.gnome.org/show_bug.cgi?id=90382 */ /* We ignore grabs, though this is questionable. It may be better to * increase the intelligence of the focus window tracking. * * The problem is that keybindings for windows are done with * XGrabKey, which means focus_window disappears and the front of * the MRU list gets confused from what the user expects once a * keybinding is used. */ if (event->mode == XINotifyGrab || event->mode == XINotifyUngrab || /* From WindowMaker, ignore all funky pointer root events */ event->detail > XINotifyNonlinearVirtual) { meta_topic (META_DEBUG_FOCUS, "Ignoring focus event generated by a grab or other weirdness\n"); return FALSE; } if (event->evtype == XI_FocusIn) { x11_display->server_focus_window = event->event; x11_display->server_focus_serial = serial; focus_window = window; } else if (event->evtype == XI_FocusOut) { if (event->detail == XINotifyInferior) { /* This event means the client moved focus to a subwindow */ meta_topic (META_DEBUG_FOCUS, "Ignoring focus out with NotifyInferior\n"); return FALSE; } x11_display->server_focus_window = None; x11_display->server_focus_serial = serial; focus_window = NULL; } else g_assert_not_reached (); /* If display->focused_by_us, then the focus_serial will be used only * for a focus change we made and have already accounted for. * (See request_xserver_input_focus_change().) Otherwise, we can get * multiple focus events with the same serial. */ if (x11_display->server_focus_serial > x11_display->focus_serial || (!x11_display->focused_by_us && x11_display->server_focus_serial == x11_display->focus_serial)) { meta_x11_display_update_focus_window (x11_display, focus_window ? focus_window->xwindow : None, x11_display->server_focus_serial, FALSE); meta_display_update_focus_window (display, focus_window); return TRUE; } else { return FALSE; } } static gboolean crossing_serial_is_ignored (MetaX11Display *x11_display, unsigned long serial) { int i; i = 0; while (i < N_IGNORED_CROSSING_SERIALS) { if (x11_display->display->ignored_crossing_serials[i] == serial) return TRUE; ++i; } return FALSE; } static gboolean handle_input_xevent (MetaX11Display *x11_display, XIEvent *input_event, unsigned long serial) { XIEnterEvent *enter_event = (XIEnterEvent *) input_event; Window modified; MetaWindow *window; MetaDisplay *display = x11_display->display; MetaWorkspaceManager *workspace_manager = display->workspace_manager; if (input_event == NULL) return FALSE; switch (input_event->evtype) { case XI_Enter: case XI_Leave: case XI_FocusIn: case XI_FocusOut: break; default: return FALSE; } modified = xievent_get_modified_window (x11_display, input_event); window = modified != None ? meta_x11_display_lookup_x_window (x11_display, modified) : NULL; /* If this is an event for a GTK+ widget, let GTK+ handle it. */ if (meta_ui_window_is_widget (x11_display->ui, modified)) return FALSE; switch (input_event->evtype) { case XI_Enter: if (display->event_route != META_EVENT_ROUTE_NORMAL) break; /* Check if we've entered a window; do this even if window->has_focus to * avoid races. */ if (window && !crossing_serial_is_ignored (x11_display, serial) && enter_event->mode != XINotifyGrab && enter_event->mode != XINotifyUngrab && enter_event->detail != XINotifyInferior && meta_x11_display_focus_sentinel_clear (x11_display)) { meta_window_handle_enter (window, enter_event->time, enter_event->root_x, enter_event->root_y); } break; case XI_Leave: if (display->event_route != META_EVENT_ROUTE_NORMAL) break; if (window != NULL && enter_event->mode != XINotifyGrab && enter_event->mode != XINotifyUngrab) { meta_window_handle_leave (window); } break; case XI_FocusIn: case XI_FocusOut: if (handle_window_focus_event (x11_display, window, enter_event, serial) && enter_event->event == enter_event->root) { if (enter_event->evtype == XI_FocusIn && enter_event->detail == XINotifyDetailNone) { meta_topic (META_DEBUG_FOCUS, "Focus got set to None, probably due to " "brain-damage in the X protocol (see bug " "125492). Setting the default focus window.\n"); meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, meta_x11_display_get_current_time_roundtrip (x11_display)); } else if (enter_event->evtype == XI_FocusIn && enter_event->mode == XINotifyNormal && enter_event->detail == XINotifyInferior) { meta_topic (META_DEBUG_FOCUS, "Focus got set to root window, probably due to " "gnome-session logout dialog usage (see bug " "153220). Setting the default focus window.\n"); meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, meta_x11_display_get_current_time_roundtrip (x11_display)); } } break; } /* Don't eat events for GTK frames (we need to update the :hover state on buttons) */ if (window && window->frame && modified == window->frame->xwindow) return FALSE; /* Don't pass these events through to Clutter / GTK+ */ return TRUE; } static void process_request_frame_extents (MetaX11Display *x11_display, XEvent *event) { /* The X window whose frame extents will be set. */ Window xwindow = event->xclient.window; unsigned long data[4] = { 0, 0, 0, 0 }; MotifWmHints *hints = NULL; gboolean hints_set = FALSE; meta_verbose ("Setting frame extents for 0x%lx\n", xwindow); /* See if the window is decorated. */ hints_set = meta_prop_get_motif_hints (x11_display, xwindow, x11_display->atom__MOTIF_WM_HINTS, &hints); if ((hints_set && hints->decorations) || !hints_set) { MetaFrameBorders borders; /* Return estimated frame extents for a normal window. */ meta_ui_theme_get_frame_borders (x11_display->ui, META_FRAME_TYPE_NORMAL, 0, &borders); data[0] = borders.visible.left; data[1] = borders.visible.right; data[2] = borders.visible.top; data[3] = borders.visible.bottom; } meta_topic (META_DEBUG_GEOMETRY, "Setting _NET_FRAME_EXTENTS on unmanaged window 0x%lx " "to top = %lu, left = %lu, bottom = %lu, right = %lu\n", xwindow, data[0], data[1], data[2], data[3]); meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, xwindow, x11_display->atom__NET_FRAME_EXTENTS, XA_CARDINAL, 32, PropModeReplace, (guchar*) data, 4); meta_x11_error_trap_pop (x11_display); g_free (hints); } /* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ static gboolean convert_property (MetaX11Display *x11_display, Window w, Atom target, Atom property) { #define N_TARGETS 4 Atom conversion_targets[N_TARGETS]; long icccm_version[] = { 2, 0 }; conversion_targets[0] = x11_display->atom_TARGETS; conversion_targets[1] = x11_display->atom_MULTIPLE; conversion_targets[2] = x11_display->atom_TIMESTAMP; conversion_targets[3] = x11_display->atom_VERSION; meta_x11_error_trap_push (x11_display); if (target == x11_display->atom_TARGETS) XChangeProperty (x11_display->xdisplay, w, property, XA_ATOM, 32, PropModeReplace, (unsigned char *)conversion_targets, N_TARGETS); else if (target == x11_display->atom_TIMESTAMP) XChangeProperty (x11_display->xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *)&x11_display->wm_sn_timestamp, 1); else if (target == x11_display->atom_VERSION) XChangeProperty (x11_display->xdisplay, w, property, XA_INTEGER, 32, PropModeReplace, (unsigned char *)icccm_version, 2); else { meta_x11_error_trap_pop_with_return (x11_display); return FALSE; } if (meta_x11_error_trap_pop_with_return (x11_display) != Success) return FALSE; /* Be sure the PropertyNotify has arrived so we * can send SelectionNotify */ /* FIXME the error trap pop synced anyway, right? */ meta_topic (META_DEBUG_SYNC, "Syncing on %s\n", G_STRFUNC); XSync (x11_display->xdisplay, False); return TRUE; } /* from fvwm2, Copyright Matthias Clasen, Dominik Vogt */ static void process_selection_request (MetaX11Display *x11_display, XEvent *event) { XSelectionEvent reply; if (x11_display->wm_sn_selection_window != event->xselectionrequest.owner || x11_display->wm_sn_atom != event->xselectionrequest.selection) { char *str; meta_x11_error_trap_push (x11_display); str = XGetAtomName (x11_display->xdisplay, event->xselectionrequest.selection); meta_x11_error_trap_pop (x11_display); meta_verbose ("Selection request with selection %s window 0x%lx not a WM_Sn selection we recognize\n", str ? str : "(bad atom)", event->xselectionrequest.owner); meta_XFree (str); return; } reply.type = SelectionNotify; reply.display = x11_display->xdisplay; reply.requestor = event->xselectionrequest.requestor; reply.selection = event->xselectionrequest.selection; reply.target = event->xselectionrequest.target; reply.property = None; reply.time = event->xselectionrequest.time; if (event->xselectionrequest.target == x11_display->atom_MULTIPLE) { if (event->xselectionrequest.property != None) { Atom type, *adata; int i, format; unsigned long num, rest; unsigned char *data; meta_x11_error_trap_push (x11_display); if (XGetWindowProperty (x11_display->xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, 0, 256, False, x11_display->atom_ATOM_PAIR, &type, &format, &num, &rest, &data) != Success) { meta_x11_error_trap_pop_with_return (x11_display); return; } if (meta_x11_error_trap_pop_with_return (x11_display) == Success) { /* FIXME: to be 100% correct, should deal with rest > 0, * but since we have 4 possible targets, we will hardly ever * meet multiple requests with a length > 8 */ adata = (Atom*)data; i = 0; while (i < (int) num) { if (!convert_property (x11_display, event->xselectionrequest.requestor, adata[i], adata[i+1])) adata[i+1] = None; i += 2; } meta_x11_error_trap_push (x11_display); XChangeProperty (x11_display->xdisplay, event->xselectionrequest.requestor, event->xselectionrequest.property, x11_display->atom_ATOM_PAIR, 32, PropModeReplace, data, num); meta_x11_error_trap_pop (x11_display); meta_XFree (data); } } } else { if (event->xselectionrequest.property == None) event->xselectionrequest.property = event->xselectionrequest.target; if (convert_property (x11_display, event->xselectionrequest.requestor, event->xselectionrequest.target, event->xselectionrequest.property)) reply.property = event->xselectionrequest.property; } XSendEvent (x11_display->xdisplay, event->xselectionrequest.requestor, False, 0L, (XEvent*)&reply); meta_verbose ("Handled selection request\n"); } static gboolean close_display_idle_cb (gpointer user_data) { MetaX11Display *x11_display = META_X11_DISPLAY (user_data); meta_display_close (x11_display->display, x11_display->xselectionclear_timestamp); x11_display->display_close_idle = 0; return G_SOURCE_REMOVE; } static gboolean process_selection_clear (MetaX11Display *x11_display, XEvent *event) { if (x11_display->wm_sn_selection_window != event->xselectionclear.window || x11_display->wm_sn_atom != event->xselectionclear.selection) { char *str; meta_x11_error_trap_push (x11_display); str = XGetAtomName (x11_display->xdisplay, event->xselectionclear.selection); meta_x11_error_trap_pop (x11_display); meta_verbose ("Selection clear with selection %s window 0x%lx not a WM_Sn selection we recognize\n", str ? str : "(bad atom)", event->xselectionclear.window); meta_XFree (str); return FALSE; } meta_verbose ("Got selection clear for on display %s\n", x11_display->name); /* We can't close a GdkDisplay in an even handler. */ if (!x11_display->display_close_idle) { x11_display->xselectionclear_timestamp = event->xselectionclear.time; x11_display->display_close_idle = g_idle_add (close_display_idle_cb, x11_display); } return TRUE; } static void notify_bell (MetaX11Display *x11_display, XkbAnyEvent *xkb_ev) { MetaDisplay *display = x11_display->display; XkbBellNotifyEvent *xkb_bell_event = (XkbBellNotifyEvent*) xkb_ev; MetaWindow *window; window = meta_x11_display_lookup_x_window (x11_display, xkb_bell_event->window); if (!window && display->focus_window && display->focus_window->frame) window = display->focus_window; x11_display->last_bell_time = xkb_ev->time; if (!meta_bell_notify (display, window) && meta_prefs_bell_is_audible ()) { /* Force a classic bell if the libcanberra bell failed. */ XkbForceDeviceBell (x11_display->xdisplay, xkb_bell_event->device, xkb_bell_event->bell_class, xkb_bell_event->bell_id, xkb_bell_event->percent); } } static gboolean handle_other_xevent (MetaX11Display *x11_display, XEvent *event) { MetaDisplay *display = x11_display->display; MetaWorkspaceManager *workspace_manager = display->workspace_manager; Window modified; MetaWindow *window; MetaWindow *property_for_window; gboolean frame_was_receiver; gboolean bypass_gtk = FALSE; modified = event_get_modified_window (x11_display, event); window = modified != None ? meta_x11_display_lookup_x_window (x11_display, modified) : NULL; frame_was_receiver = (window && window->frame && modified == window->frame->xwindow); /* We only want to respond to _NET_WM_USER_TIME property notify * events on _NET_WM_USER_TIME_WINDOW windows; in particular, * responding to UnmapNotify events is kind of bad. */ property_for_window = NULL; if (window && modified == window->user_time_window) { property_for_window = window; window = NULL; } if (META_X11_DISPLAY_HAS_XSYNC (x11_display) && event->type == (x11_display->xsync_event_base + XSyncAlarmNotify)) { MetaWindow *alarm_window = meta_x11_display_lookup_sync_alarm (x11_display, ((XSyncAlarmNotifyEvent*)event)->alarm); if (alarm_window != NULL) { XSyncValue value = ((XSyncAlarmNotifyEvent*)event)->counter_value; gint64 new_counter_value; new_counter_value = XSyncValueLow32 (value) + ((gint64)XSyncValueHigh32 (value) << 32); meta_window_x11_update_sync_request_counter (alarm_window, new_counter_value); bypass_gtk = TRUE; /* GTK doesn't want to see this really */ } else { if (x11_display->alarm_filter && x11_display->alarm_filter (x11_display, (XSyncAlarmNotifyEvent*)event, x11_display->alarm_filter_data)) bypass_gtk = TRUE; } goto out; } if (META_X11_DISPLAY_HAS_SHAPE (x11_display) && event->type == (x11_display->shape_event_base + ShapeNotify)) { bypass_gtk = TRUE; /* GTK doesn't want to see this really */ if (window && !frame_was_receiver) { XShapeEvent *sev = (XShapeEvent*) event; if (sev->kind == ShapeBounding) meta_window_x11_update_shape_region (window); else if (sev->kind == ShapeInput) meta_window_x11_update_input_region (window); } else { meta_topic (META_DEBUG_SHAPES, "ShapeNotify not on a client window (window %s frame_was_receiver = %d)\n", window ? window->desc : "(none)", frame_was_receiver); } goto out; } switch (event->type) { case KeymapNotify: break; case Expose: break; case GraphicsExpose: break; case NoExpose: break; case VisibilityNotify: break; case CreateNotify: { if (event->xcreatewindow.parent == x11_display->xroot) meta_stack_tracker_create_event (display->stack_tracker, &event->xcreatewindow); } break; case DestroyNotify: { if (event->xdestroywindow.event == x11_display->xroot) meta_stack_tracker_destroy_event (display->stack_tracker, &event->xdestroywindow); } if (window) { /* FIXME: It sucks that DestroyNotify events don't come with * a timestamp; could we do something better here? Maybe X * will change one day? */ guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (display); if (display->grab_op != META_GRAB_OP_NONE && display->grab_window == window) meta_display_end_grab_op (display, timestamp); if (frame_was_receiver) { meta_warning ("Unexpected destruction of frame 0x%lx, not sure if this should silently fail or be considered a bug\n", window->frame->xwindow); meta_x11_error_trap_push (x11_display); meta_window_destroy_frame (window->frame->window); meta_x11_error_trap_pop (x11_display); } else { /* Unmanage destroyed window */ meta_window_unmanage (window, timestamp); window = NULL; } } break; case UnmapNotify: if (window) { /* FIXME: It sucks that UnmapNotify events don't come with * a timestamp; could we do something better here? Maybe X * will change one day? */ guint32 timestamp; timestamp = meta_display_get_current_time_roundtrip (display); if (display->grab_op != META_GRAB_OP_NONE && display->grab_window == window && window->frame == NULL) meta_display_end_grab_op (display, timestamp); if (!frame_was_receiver) { if (window->unmaps_pending == 0) { meta_topic (META_DEBUG_WINDOW_STATE, "Window %s withdrawn\n", window->desc); /* Unmanage withdrawn window */ window->withdrawn = TRUE; meta_window_unmanage (window, timestamp); window = NULL; } else { window->unmaps_pending -= 1; meta_topic (META_DEBUG_WINDOW_STATE, "Received pending unmap, %d now pending\n", window->unmaps_pending); } } } break; case MapNotify: /* NB: override redirect windows wont cause a map request so we * watch out for map notifies against any root windows too if a * compositor is enabled: */ if (window == NULL && event->xmap.event == x11_display->xroot) { window = meta_window_x11_new (display, event->xmap.window, FALSE, META_COMP_EFFECT_CREATE); } else if (window && window->restore_focus_on_map && window->reparents_pending == 0) { meta_window_focus (window, meta_display_get_current_time_roundtrip (display)); } break; case MapRequest: if (window == NULL) { window = meta_window_x11_new (display, event->xmaprequest.window, FALSE, META_COMP_EFFECT_CREATE); /* The window might have initial iconic state, but this is a * MapRequest, fall through to ensure it is unminimized in * that case. */ } else if (frame_was_receiver) { meta_warning ("Map requests on the frame window are unexpected\n"); break; } /* Double check that creating the MetaWindow succeeded */ if (window == NULL) break; meta_verbose ("MapRequest on %s mapped = %d minimized = %d\n", window->desc, window->mapped, window->minimized); if (window->minimized) { meta_window_unminimize (window); if (window->workspace != workspace_manager->active_workspace) { meta_verbose ("Changing workspace due to MapRequest mapped = %d minimized = %d\n", window->mapped, window->minimized); meta_window_change_workspace (window, workspace_manager->active_workspace); } } break; case ReparentNotify: { if (window && window->reparents_pending > 0) window->reparents_pending -= 1; if (event->xreparent.event == x11_display->xroot) meta_stack_tracker_reparent_event (display->stack_tracker, &event->xreparent); } break; case ConfigureNotify: if (event->xconfigure.event != event->xconfigure.window) { if (event->xconfigure.event == x11_display->xroot && event->xconfigure.window != x11_display->composite_overlay_window) meta_stack_tracker_configure_event (display->stack_tracker, &event->xconfigure); } if (window && window->override_redirect) meta_window_x11_configure_notify (window, &event->xconfigure); break; case ConfigureRequest: /* This comment and code is found in both twm and fvwm */ /* * According to the July 27, 1988 ICCCM draft, we should ignore size and * position fields in the WM_NORMAL_HINTS property when we map a window. * Instead, we'll read the current geometry. Therefore, we should respond * to configuration requests for windows which have never been mapped. */ if (window == NULL) { unsigned int xwcm; XWindowChanges xwc; xwcm = event->xconfigurerequest.value_mask & (CWX | CWY | CWWidth | CWHeight | CWBorderWidth); xwc.x = event->xconfigurerequest.x; xwc.y = event->xconfigurerequest.y; xwc.width = event->xconfigurerequest.width; xwc.height = event->xconfigurerequest.height; xwc.border_width = event->xconfigurerequest.border_width; meta_verbose ("Configuring withdrawn window to %d,%d %dx%d border %d (some values may not be in mask)\n", xwc.x, xwc.y, xwc.width, xwc.height, xwc.border_width); meta_x11_error_trap_push (x11_display); XConfigureWindow (x11_display->xdisplay, event->xconfigurerequest.window, xwcm, &xwc); meta_x11_error_trap_pop (x11_display); } else { if (!frame_was_receiver) meta_window_x11_configure_request (window, event); } break; case GravityNotify: break; case ResizeRequest: break; case CirculateNotify: break; case CirculateRequest: break; case PropertyNotify: { MetaGroup *group; if (window && !frame_was_receiver) meta_window_x11_property_notify (window, event); else if (property_for_window && !frame_was_receiver) meta_window_x11_property_notify (property_for_window, event); group = meta_x11_display_lookup_group (x11_display, event->xproperty.window); if (group != NULL) meta_group_property_notify (group, event); if (event->xproperty.window == x11_display->xroot) { if (event->xproperty.atom == x11_display->atom__NET_DESKTOP_LAYOUT) meta_x11_display_update_workspace_layout (x11_display); else if (event->xproperty.atom == x11_display->atom__NET_DESKTOP_NAMES) meta_x11_display_update_workspace_names (x11_display); /* we just use this property as a sentinel to avoid * certain race conditions. See the comment for the * sentinel_counter variable declaration in display.h */ if (event->xproperty.atom == x11_display->atom__MUTTER_SENTINEL) { meta_x11_display_decrement_focus_sentinel (x11_display); } } } break; case SelectionRequest: process_selection_request (x11_display, event); break; case SelectionNotify: break; case ColormapNotify: break; case ClientMessage: if (window) { #ifdef HAVE_WAYLAND if (event->xclient.message_type == x11_display->atom_WL_SURFACE_ID) { guint32 surface_id = event->xclient.data.l[0]; meta_xwayland_handle_wl_surface_id (window, surface_id); } else if (event->xclient.message_type == x11_display->atom__XWAYLAND_MAY_GRAB_KEYBOARD) { if (meta_is_wayland_compositor ()) g_object_set (G_OBJECT (window), "xwayland-may-grab-keyboard", (event->xclient.data.l[0] != 0), NULL); } else #endif if (!frame_was_receiver) meta_window_x11_client_message (window, event); } else { if (event->xclient.window == x11_display->xroot) { if (event->xclient.message_type == x11_display->atom__NET_CURRENT_DESKTOP) { int space; MetaWorkspace *workspace; guint32 time; space = event->xclient.data.l[0]; time = event->xclient.data.l[1]; meta_verbose ("Request to change current workspace to %d with " "specified timestamp of %u\n", space, time); workspace = meta_workspace_manager_get_workspace_by_index (workspace_manager, space); if (workspace) { /* Handle clients using the older version of the spec... */ if (time == 0) time = meta_x11_display_get_current_time_roundtrip (x11_display); meta_workspace_activate (workspace, time); } else { meta_verbose ("Don't know about workspace %d\n", space); } } else if (event->xclient.message_type == x11_display->atom__NET_NUMBER_OF_DESKTOPS) { int num_spaces; num_spaces = event->xclient.data.l[0]; meta_verbose ("Request to set number of workspaces to %d\n", num_spaces); meta_prefs_set_num_workspaces (num_spaces); } else if (event->xclient.message_type == x11_display->atom__NET_SHOWING_DESKTOP) { gboolean showing_desktop; guint32 timestamp; showing_desktop = event->xclient.data.l[0] != 0; /* FIXME: Braindead protocol doesn't have a timestamp */ timestamp = meta_x11_display_get_current_time_roundtrip (x11_display); meta_verbose ("Request to %s desktop\n", showing_desktop ? "show" : "hide"); if (showing_desktop) meta_workspace_manager_show_desktop (workspace_manager, timestamp); else { meta_workspace_manager_unshow_desktop (workspace_manager); meta_workspace_focus_default_window (workspace_manager->active_workspace, NULL, timestamp); } } else if (event->xclient.message_type == x11_display->atom_WM_PROTOCOLS) { meta_verbose ("Received WM_PROTOCOLS message\n"); if ((Atom)event->xclient.data.l[0] == x11_display->atom__NET_WM_PING) { guint32 timestamp = event->xclient.data.l[1]; meta_display_pong_for_serial (display, timestamp); /* We don't want ping reply events going into * the GTK+ event loop because gtk+ will treat * them as ping requests and send more replies. */ bypass_gtk = TRUE; } } } if (event->xclient.message_type == x11_display->atom__NET_REQUEST_FRAME_EXTENTS) { meta_verbose ("Received _NET_REQUEST_FRAME_EXTENTS message\n"); process_request_frame_extents (x11_display, event); } } break; case MappingNotify: { gboolean ignore_current; ignore_current = FALSE; /* Check whether the next event is an identical MappingNotify * event. If it is, ignore the current event, we'll update * when we get the next one. */ if (XPending (x11_display->xdisplay)) { XEvent next_event; XPeekEvent (x11_display->xdisplay, &next_event); if (next_event.type == MappingNotify && next_event.xmapping.request == event->xmapping.request) ignore_current = TRUE; } if (!ignore_current) { /* Let XLib know that there is a new keyboard mapping. */ XRefreshKeyboardMapping (&event->xmapping); } } break; default: if (event->type == x11_display->xkb_base_event_type) { XkbAnyEvent *xkb_ev = (XkbAnyEvent *) event; switch (xkb_ev->xkb_type) { case XkbBellNotify: if (XSERVER_TIME_IS_BEFORE(x11_display->last_bell_time, xkb_ev->time - 100)) { notify_bell (x11_display, xkb_ev); } break; default: break; } } break; } out: return bypass_gtk; } static gboolean window_has_xwindow (MetaWindow *window, Window xwindow) { if (window->xwindow == xwindow) return TRUE; if (window->frame && window->frame->xwindow == xwindow) return TRUE; return FALSE; } static gboolean process_selection_event (MetaX11Display *x11_display, XEvent *event) { gboolean handled = FALSE; GList *l; handled |= meta_x11_selection_handle_event (x11_display, event); for (l = x11_display->selection.input_streams; l && !handled;) { GList *next = l->next; handled |= meta_x11_selection_input_stream_xevent (l->data, event); l = next; } for (l = x11_display->selection.output_streams; l && !handled;) { GList *next = l->next; handled |= meta_x11_selection_output_stream_xevent (l->data, event); l = next; } return handled; } /** * meta_display_handle_xevent: * @display: The MetaDisplay that events are coming from * @event: The event that just happened * * This is the most important function in the whole program. It is the heart, * it is the nexus, it is the Grand Central Station of Mutter's world. * When we create a #MetaDisplay, we ask GDK to pass *all* events for *all* * windows to this function. So every time anything happens that we might * want to know about, this function gets called. You see why it gets a bit * busy around here. Most of this function is a ginormous switch statement * dealing with all the kinds of events that might turn up. */ static gboolean meta_x11_display_handle_xevent (MetaX11Display *x11_display, XEvent *event) { MetaDisplay *display = x11_display->display; MetaBackend *backend = meta_get_backend (); Window modified; gboolean bypass_compositor = FALSE, bypass_gtk = FALSE; XIEvent *input_event; MetaCursorTracker *cursor_tracker; COGL_TRACE_BEGIN_SCOPED (MetaX11DisplayHandleXevent, "X11Display (handle X11 event)"); #if 0 meta_spew_event_print (x11_display, event); #endif if (meta_x11_startup_notification_handle_xevent (x11_display, event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor () && meta_xwayland_dnd_handle_event (event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } #endif if (process_selection_event (x11_display, event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } display->current_time = event_get_time (x11_display, event); if (META_IS_BACKEND_X11 (backend)) meta_backend_x11_handle_event (META_BACKEND_X11 (backend), event); if (x11_display->focused_by_us && event->xany.serial > x11_display->focus_serial && display->focus_window && !window_has_xwindow (display->focus_window, x11_display->server_focus_window)) { meta_topic (META_DEBUG_FOCUS, "Earlier attempt to focus %s failed\n", display->focus_window->desc); meta_x11_display_update_focus_window (x11_display, x11_display->server_focus_window, x11_display->server_focus_serial, FALSE); meta_display_update_focus_window (display, meta_x11_display_lookup_x_window (x11_display, x11_display->server_focus_window)); } if (event->xany.window == x11_display->xroot) { cursor_tracker = meta_backend_get_cursor_tracker (backend); if (meta_cursor_tracker_handle_xevent (cursor_tracker, event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } } modified = event_get_modified_window (x11_display, event); input_event = get_input_event (x11_display, event); if (event->type == UnmapNotify) { if (meta_ui_window_should_not_cause_focus (x11_display->xdisplay, modified)) { meta_display_add_ignored_crossing_serial (display, event->xany.serial); meta_topic (META_DEBUG_FOCUS, "Adding EnterNotify serial %lu to ignored focus serials\n", event->xany.serial); } } if (meta_x11_display_process_barrier_xevent (x11_display, input_event)) { bypass_gtk = bypass_compositor = TRUE; goto out; } if (handle_input_xevent (x11_display, input_event, event->xany.serial)) { bypass_gtk = bypass_compositor = TRUE; goto out; } if (handle_other_xevent (x11_display, event)) { bypass_gtk = TRUE; goto out; } if (event->type == SelectionClear) { if (process_selection_clear (x11_display, event)) { bypass_gtk = TRUE; goto out; } } out: if (!bypass_compositor && META_IS_COMPOSITOR_X11 (display->compositor)) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (display->compositor); MetaWindow *window; if (modified != None) window = meta_x11_display_lookup_x_window (x11_display, modified); else window = NULL; meta_compositor_x11_process_xevent (compositor_x11, event, window); } display->current_time = META_CURRENT_TIME; return bypass_gtk; } static GdkFilterReturn xevent_filter (GdkXEvent *xevent, GdkEvent *event, gpointer data) { MetaX11Display *x11_display = data; if (meta_x11_display_handle_xevent (x11_display, xevent)) return GDK_FILTER_REMOVE; else return GDK_FILTER_CONTINUE; } void meta_x11_display_init_events (MetaX11Display *x11_display) { gdk_window_add_filter (NULL, xevent_filter, x11_display); } void meta_x11_display_free_events (MetaX11Display *x11_display) { gdk_window_remove_filter (NULL, xevent_filter, x11_display); } muffin-6.4.1/src/tests/0000775000175000017500000000000014723361714013673 5ustar fabiofabiomuffin-6.4.1/src/tests/wayland-test-clients/0000775000175000017500000000000014723361714017746 5ustar fabiofabiomuffin-6.4.1/src/tests/wayland-test-clients/meson.build0000664000175000017500000000252114723361714022110 0ustar fabiofabiowayland_test_client_installed_tests_libexecdir = join_paths( mutter_installed_tests_libexecdir, 'wayland-test-clients', ) test_driver_server_header = custom_target( 'test-driver server header', input: 'test-driver.xml', output: 'test-driver-server-protocol.h', command: [ wayland_scanner, 'server-header', '@INPUT@', '@OUTPUT@', ] ) test_driver_client_header = custom_target( 'test-driver client header', input: 'test-driver.xml', output: 'test-driver-client-protocol.h', command: [ wayland_scanner, 'client-header', '@INPUT@', '@OUTPUT@', ] ) test_driver_protocol_code = custom_target( 'test-driver source', input: 'test-driver.xml', output: 'test-driver-protocol.c', command: [ wayland_scanner, 'private-code', '@INPUT@', '@OUTPUT@', ] ) common_sources = [ 'wayland-test-client-utils.c', 'wayland-test-client-utils.h', wayland_protocol_client_headers, wayland_protocol_sources, test_driver_client_header, test_driver_protocol_code, ] executable('subsurface-remap-toplevel', sources: [ 'subsurface-remap-toplevel.c', common_sources, ], include_directories: tests_includepath, c_args: tests_c_args, dependencies: [ glib_dep, wayland_client_dep, ], install: have_installed_tests, install_dir: wayland_test_client_installed_tests_libexecdir, ) muffin-6.4.1/src/tests/wayland-test-clients/subsurface-remap-toplevel.c0000664000175000017500000002427414723361714025217 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include #include "wayland-test-client-utils.h" #include "test-driver-client-protocol.h" #include "xdg-shell-client-protocol.h" typedef enum _State { STATE_INIT = 0, STATE_WAIT_FOR_CONFIGURE_1, STATE_WAIT_FOR_FRAME_1, STATE_WAIT_FOR_ACTOR_DESTROYED, STATE_WAIT_FOR_CONFIGURE_2, STATE_WAIT_FOR_FRAME_2 } State; static struct wl_display *display; static struct wl_registry *registry; static struct wl_compositor *compositor; static struct wl_subcompositor *subcompositor; static struct xdg_wm_base *xdg_wm_base; static struct wl_shm *shm; static struct test_driver *test_driver; static struct wl_surface *surface; static struct xdg_surface *xdg_surface; static struct xdg_toplevel *xdg_toplevel; static struct wl_surface *subsurface_surface; static struct wl_subsurface *subsurface; static struct wl_callback *frame_callback; static gboolean running; static State state; static void init_surface (void) { xdg_toplevel_set_title (xdg_toplevel, "gradient-test"); wl_surface_commit (surface); } static void actor_destroyed (void *data, struct wl_callback *callback, uint32_t serial) { g_assert_cmpint (state, ==, STATE_WAIT_FOR_ACTOR_DESTROYED); init_surface (); state = STATE_WAIT_FOR_CONFIGURE_2; wl_callback_destroy (callback); } static const struct wl_callback_listener actor_destroy_listener = { actor_destroyed, }; static void reset_surface (void) { struct wl_callback *callback; callback = test_driver_sync_actor_destroyed (test_driver, surface); wl_callback_add_listener (callback, &actor_destroy_listener, NULL); wl_surface_attach (surface, NULL, 0, 0); wl_surface_commit (surface); state = STATE_WAIT_FOR_ACTOR_DESTROYED; } static void handle_buffer_release (void *data, struct wl_buffer *buffer) { wl_buffer_destroy (buffer); } static const struct wl_buffer_listener buffer_listener = { handle_buffer_release }; static gboolean create_shm_buffer (int width, int height, struct wl_buffer **out_buffer, void **out_data, int *out_size) { struct wl_shm_pool *pool; static struct wl_buffer *buffer; int fd, size, stride; int bytes_per_pixel; void *data; bytes_per_pixel = 4; stride = width * bytes_per_pixel; size = stride * height; fd = create_anonymous_file (size); if (fd < 0) { fprintf (stderr, "Creating a buffer file for %d B failed: %m\n", size); return FALSE; } data = mmap (NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (data == MAP_FAILED) { fprintf (stderr, "mmap failed: %m\n"); close (fd); return FALSE; } pool = wl_shm_create_pool (shm, fd, size); buffer = wl_shm_pool_create_buffer (pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); wl_buffer_add_listener (buffer, &buffer_listener, buffer); wl_shm_pool_destroy (pool); close (fd); *out_buffer = buffer; *out_data = data; *out_size = size; return TRUE; } static void fill (void *buffer_data, int width, int height, uint32_t color) { uint32_t *pixels = buffer_data; int x, y; for (y = 0; y < height; y++) { for (x = 0; x < width; x++) pixels[y * width + x] = color; } } static void draw (struct wl_surface *surface, int width, int height, uint32_t color) { struct wl_buffer *buffer; void *buffer_data; int size; if (!create_shm_buffer (width, height, &buffer, &buffer_data, &size)) g_error ("Failed to create shm buffer"); fill (buffer_data, width, height, color); wl_surface_attach (surface, buffer, 0, 0); } static void draw_main (void) { draw (surface, 700, 500, 0xff00ff00); } static void draw_subsurface (void) { draw (subsurface_surface, 500, 300, 0xff007f00); } static void handle_xdg_toplevel_configure (void *data, struct xdg_toplevel *xdg_toplevel, int32_t width, int32_t height, struct wl_array *state) { } static void handle_xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel) { g_assert_not_reached (); } static const struct xdg_toplevel_listener xdg_toplevel_listener = { handle_xdg_toplevel_configure, handle_xdg_toplevel_close, }; static void handle_frame_callback (void *data, struct wl_callback *callback, uint32_t time) { switch (state) { case STATE_WAIT_FOR_FRAME_1: reset_surface (); break; case STATE_WAIT_FOR_FRAME_2: exit (EXIT_SUCCESS); case STATE_INIT: g_assert_not_reached (); case STATE_WAIT_FOR_CONFIGURE_1: g_assert_not_reached (); case STATE_WAIT_FOR_ACTOR_DESTROYED: g_assert_not_reached (); case STATE_WAIT_FOR_CONFIGURE_2: g_assert_not_reached (); } } static const struct wl_callback_listener frame_listener = { handle_frame_callback, }; static void handle_xdg_surface_configure (void *data, struct xdg_surface *xdg_surface, uint32_t serial) { switch (state) { case STATE_INIT: g_assert_not_reached (); case STATE_WAIT_FOR_CONFIGURE_1: draw_main (); state = STATE_WAIT_FOR_FRAME_1; break; case STATE_WAIT_FOR_CONFIGURE_2: draw_main (); state = STATE_WAIT_FOR_FRAME_2; break; case STATE_WAIT_FOR_ACTOR_DESTROYED: g_assert_not_reached (); case STATE_WAIT_FOR_FRAME_1: case STATE_WAIT_FOR_FRAME_2: /* ignore */ return; } xdg_surface_ack_configure (xdg_surface, serial); frame_callback = wl_surface_frame (surface); wl_callback_add_listener (frame_callback, &frame_listener, NULL); wl_surface_commit (surface); wl_display_flush (display); } static const struct xdg_surface_listener xdg_surface_listener = { handle_xdg_surface_configure, }; static void handle_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 = { handle_xdg_wm_base_ping, }; static void handle_registry_global (void *data, struct wl_registry *registry, uint32_t id, const char *interface, uint32_t version) { if (strcmp (interface, "wl_compositor") == 0) { compositor = wl_registry_bind (registry, id, &wl_compositor_interface, 1); } else if (strcmp (interface, "wl_subcompositor") == 0) { subcompositor = wl_registry_bind (registry, id, &wl_subcompositor_interface, 1); } else if (strcmp (interface, "xdg_wm_base") == 0) { xdg_wm_base = wl_registry_bind (registry, id, &xdg_wm_base_interface, 1); xdg_wm_base_add_listener (xdg_wm_base, &xdg_wm_base_listener, NULL); } else if (strcmp (interface, "wl_shm") == 0) { shm = wl_registry_bind (registry, id, &wl_shm_interface, 1); } else if (strcmp (interface, "test_driver") == 0) { test_driver = wl_registry_bind (registry, id, &test_driver_interface, 1); } } static void handle_registry_global_remove (void *data, struct wl_registry *registry, uint32_t name) { } static const struct wl_registry_listener registry_listener = { handle_registry_global, handle_registry_global_remove }; int main (int argc, char **argv) { display = wl_display_connect (NULL); registry = wl_display_get_registry (display); wl_registry_add_listener (registry, ®istry_listener, NULL); wl_display_roundtrip (display); if (!shm) { fprintf (stderr, "No wl_shm global\n"); return EXIT_FAILURE; } if (!xdg_wm_base) { fprintf (stderr, "No xdg_wm_base global\n"); return EXIT_FAILURE; } wl_display_roundtrip (display); g_assert_nonnull (test_driver); surface = wl_compositor_create_surface (compositor); xdg_surface = xdg_wm_base_get_xdg_surface (xdg_wm_base, surface); xdg_surface_add_listener (xdg_surface, &xdg_surface_listener, NULL); xdg_toplevel = xdg_surface_get_toplevel (xdg_surface); xdg_toplevel_add_listener (xdg_toplevel, &xdg_toplevel_listener, NULL); subsurface_surface = wl_compositor_create_surface (compositor); subsurface = wl_subcompositor_get_subsurface (subcompositor, subsurface_surface, surface); wl_subsurface_set_position (subsurface, 100, 100); draw_subsurface (); wl_surface_commit (subsurface_surface); init_surface (); state = STATE_WAIT_FOR_CONFIGURE_1; running = TRUE; while (running) { if (wl_display_dispatch (display) == -1) return EXIT_FAILURE; } return EXIT_SUCCESS; } muffin-6.4.1/src/tests/wayland-test-clients/test-driver.xml0000664000175000017500000000051214723361714022736 0ustar fabiofabio muffin-6.4.1/src/tests/wayland-test-clients/wayland-test-client-utils.c0000664000175000017500000000410014723361714025133 0ustar fabiofabio/* * Copyright © 2012 Collabora, Ltd. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice (including the * next paragraph) shall be included in all copies or substantial * portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ #include "wayland-test-client-utils.h" #include #include #include #include #include static int create_tmpfile_cloexec (char *tmpname) { int fd; fd = mkostemp (tmpname, O_CLOEXEC); if (fd >= 0) unlink (tmpname); return fd; } int create_anonymous_file (off_t size) { static const char template[] = "/wayland-test-client-shared-XXXXXX"; const char *path; char *name; int fd; int ret; path = getenv ("XDG_RUNTIME_DIR"); if (!path) { errno = ENOENT; return -1; } name = malloc (strlen (path) + sizeof (template)); if (!name) return -1; strcpy (name, path); strcat (name, template); fd = create_tmpfile_cloexec (name); free (name); if (fd < 0) return -1; do ret = posix_fallocate (fd, 0, size); while (ret == EINTR); if (ret != 0) { close (fd); errno = ret; return -1; } return fd; } muffin-6.4.1/src/tests/wayland-test-clients/wayland-test-client-utils.h0000664000175000017500000000025714723361714025151 0ustar fabiofabio#ifndef WAYLAND_TEST_CLIENT_UTILS_H #define WAYLAND_TEST_CLIENT_UTILS_H #include int create_anonymous_file (off_t size); #endif /* WAYLAND_TEST_CLIENT_UTILS_H */ muffin-6.4.1/src/tests/boxes-tests.c0000664000175000017500000014217414723361714016330 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter box operation testing program */ /* * Copyright (C) 2005 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/boxes-tests.h" #include #include #include #include #include #include #include "core/boxes-private.h" #define NUM_RANDOM_RUNS 10000 static void init_random_ness (void) { srand (time (NULL)); } static void get_random_rect (MetaRectangle *rect) { rect->x = rand () % 1600; rect->y = rand () % 1200; rect->width = rand () % 1600 + 1; rect->height = rand () % 1200 + 1; } static MetaRectangle* new_meta_rect (int x, int y, int width, int height) { MetaRectangle* temporary; temporary = g_new (MetaRectangle, 1); temporary->x = x; temporary->y = y; temporary->width = width; temporary->height = height; return temporary; } static MetaStrut* new_meta_strut (int x, int y, int width, int height, int side) { MetaStrut* temporary; temporary = g_new (MetaStrut, 1); temporary->rect = meta_rect(x, y, width, height); temporary->side = side; return temporary; } static MetaEdge* new_screen_edge (int x, int y, int width, int height, int side_type) { MetaEdge* temporary; temporary = g_new (MetaEdge, 1); temporary->rect.x = x; temporary->rect.y = y; temporary->rect.width = width; temporary->rect.height = height; temporary->side_type = side_type; temporary->edge_type = META_EDGE_SCREEN; return temporary; } static MetaEdge* new_monitor_edge (int x, int y, int width, int height, int side_type) { MetaEdge* temporary; temporary = g_new (MetaEdge, 1); temporary->rect.x = x; temporary->rect.y = y; temporary->rect.width = width; temporary->rect.height = height; temporary->side_type = side_type; temporary->edge_type = META_EDGE_MONITOR; return temporary; } static void test_area (void) { MetaRectangle temp; int i; for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&temp); g_assert (meta_rectangle_area (&temp) == temp.width * temp.height); } temp = meta_rect (0, 0, 5, 7); g_assert (meta_rectangle_area (&temp) == 35); } static void test_intersect (void) { MetaRectangle a = {100, 200, 50, 40}; MetaRectangle b = { 0, 50, 110, 152}; MetaRectangle c = { 0, 0, 10, 10}; MetaRectangle d = {100, 100, 50, 50}; MetaRectangle b_intersect_d = {100, 100, 10, 50}; MetaRectangle temp; MetaRectangle temp2; meta_rectangle_intersect (&a, &b, &temp); temp2 = meta_rect (100, 200, 10, 2); g_assert (meta_rectangle_equal (&temp, &temp2)); g_assert (meta_rectangle_area (&temp) == 20); meta_rectangle_intersect (&a, &c, &temp); g_assert (meta_rectangle_area (&temp) == 0); meta_rectangle_intersect (&a, &d, &temp); g_assert (meta_rectangle_area (&temp) == 0); meta_rectangle_intersect (&b, &d, &b); g_assert (meta_rectangle_equal (&b, &b_intersect_d)); } static void test_equal (void) { MetaRectangle a = {10, 12, 4, 18}; MetaRectangle b = a; MetaRectangle c = {10, 12, 4, 19}; MetaRectangle d = {10, 12, 7, 18}; MetaRectangle e = {10, 62, 4, 18}; MetaRectangle f = {27, 12, 4, 18}; g_assert ( meta_rectangle_equal (&a, &b)); g_assert (!meta_rectangle_equal (&a, &c)); g_assert (!meta_rectangle_equal (&a, &d)); g_assert (!meta_rectangle_equal (&a, &e)); g_assert (!meta_rectangle_equal (&a, &f)); } static void test_overlap_funcs (void) { MetaRectangle temp1, temp2; int i; for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&temp1); get_random_rect (&temp2); g_assert (meta_rectangle_overlap (&temp1, &temp2) == (meta_rectangle_horiz_overlap (&temp1, &temp2) && meta_rectangle_vert_overlap (&temp1, &temp2))); } temp1 = meta_rect ( 0, 0, 10, 10); temp2 = meta_rect (20, 0, 10, 5); g_assert (!meta_rectangle_overlap (&temp1, &temp2)); g_assert (!meta_rectangle_horiz_overlap (&temp1, &temp2)); g_assert ( meta_rectangle_vert_overlap (&temp1, &temp2)); } static void test_basic_fitting (void) { MetaRectangle temp1, temp2, temp3; int i; /* Four cases: * case temp1 fits temp2 temp1 could fit temp2 * 1 Y Y * 2 N Y * 3 Y N * 4 N N * Of the four cases, case 3 is impossible. An alternate way of looking * at this table is that either the middle column must be no, or the last * column must be yes. So we test that. Also, we can repeat the test * reversing temp1 and temp2. */ for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&temp1); get_random_rect (&temp2); g_assert (meta_rectangle_contains_rect (&temp1, &temp2) == FALSE || meta_rectangle_could_fit_rect (&temp1, &temp2) == TRUE); g_assert (meta_rectangle_contains_rect (&temp2, &temp1) == FALSE || meta_rectangle_could_fit_rect (&temp2, &temp1) == TRUE); } temp1 = meta_rect ( 0, 0, 10, 10); temp2 = meta_rect ( 5, 5, 5, 5); temp3 = meta_rect ( 8, 2, 3, 7); g_assert ( meta_rectangle_contains_rect (&temp1, &temp2)); g_assert (!meta_rectangle_contains_rect (&temp2, &temp1)); g_assert (!meta_rectangle_contains_rect (&temp1, &temp3)); g_assert ( meta_rectangle_could_fit_rect (&temp1, &temp3)); g_assert (!meta_rectangle_could_fit_rect (&temp3, &temp2)); } static void free_strut_list (GSList *struts) { g_slist_free_full (struts, g_free); } static GSList* get_strut_list (int which) { GSList *ans; MetaSide wc = 0; /* wc == who cares? ;-) */ ans = NULL; g_assert (which >=0 && which <= 6); switch (which) { case 0: break; case 1: ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ans = g_slist_prepend (ans, new_meta_strut ( 400, 1160, 1600, 40, META_SIDE_BOTTOM)); break; case 2: ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, META_SIDE_BOTTOM)); ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 150, 50, META_SIDE_BOTTOM)); break; case 3: ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); ans = g_slist_prepend (ans, new_meta_strut ( 800, 1100, 400, 100, META_SIDE_LEFT)); ans = g_slist_prepend (ans, new_meta_strut ( 300, 1150, 80, 50, META_SIDE_BOTTOM)); ans = g_slist_prepend (ans, new_meta_strut ( 700, 525, 200, 150, wc)); break; case 4: ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, META_SIDE_LEFT)); ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, META_SIDE_TOP)); break; case 5: ans = g_slist_prepend (ans, new_meta_strut ( 800, 0, 1600, 20, META_SIDE_TOP)); ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 800, 1200, META_SIDE_LEFT)); ans = g_slist_prepend (ans, new_meta_strut ( 800, 10, 800, 1200, META_SIDE_RIGHT)); break; case 6: ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 40, META_SIDE_TOP)); ans = g_slist_prepend (ans, new_meta_strut ( 0, 0, 1600, 20, META_SIDE_TOP)); break; } return ans; } static GList* get_screen_region (int which) { GList *ret; GSList *struts; MetaRectangle basic_rect; basic_rect = meta_rect (0, 0, 1600, 1200); ret = NULL; struts = get_strut_list (which); ret = meta_rectangle_get_minimal_spanning_set_for_region (&basic_rect, struts); free_strut_list (struts); return ret; } static GList* get_screen_edges (int which) { GList *ret; GSList *struts; MetaRectangle basic_rect; basic_rect = meta_rect (0, 0, 1600, 1200); ret = NULL; struts = get_strut_list (which); ret = meta_rectangle_find_onscreen_edges (&basic_rect, struts); free_strut_list (struts); return ret; } static GList* get_monitor_edges (int which_monitor_set, int which_strut_set) { GList *ret; GSList *struts; GList *xins; xins = NULL; g_assert (which_monitor_set >=0 && which_monitor_set <= 3); switch (which_monitor_set) { case 0: xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 1200)); break; case 1: xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 800, 1200)); xins = g_list_prepend (xins, new_meta_rect (800, 0, 800, 1200)); break; case 2: xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 600)); xins = g_list_prepend (xins, new_meta_rect ( 0, 600, 1600, 600)); break; case 3: xins = g_list_prepend (xins, new_meta_rect ( 0, 0, 1600, 600)); xins = g_list_prepend (xins, new_meta_rect ( 0, 600, 800, 600)); xins = g_list_prepend (xins, new_meta_rect (800, 600, 800, 600)); break; } ret = NULL; struts = get_strut_list (which_strut_set); ret = meta_rectangle_find_nonintersected_monitor_edges (xins, struts); free_strut_list (struts); meta_rectangle_free_list_and_elements (xins); return ret; } #if 0 static void test_merge_regions (void) { /* logarithmically distributed random number of struts (range?) * logarithmically distributed random size of struts (up to screen size???) * uniformly distributed location of center of struts (within screen) * merge all regions that are possible * print stats on problem setup * number of (non-completely-occluded?) struts * percentage of screen covered * length of resulting non-minimal spanning set * length of resulting minimal spanning set * print stats on merged regions: * number boxes merged * number of those merges that were of the form A contains B * number of those merges that were of the form A partially contains B * number of those merges that were of the form A is adjacent to B */ GList* region; GList* compare; int num_contains, num_merged, num_part_contains, num_adjacent; num_contains = num_merged = num_part_contains = num_adjacent = 0; compare = region = get_screen_region (2); g_assert (region); printf ("Merging stats:\n"); printf (" Length of initial list: %d\n", g_list_length (region)); #ifdef PRINT_DEBUG char rect1[RECT_LENGTH], rect2[RECT_LENGTH]; char region_list[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list); printf (" Initial rectangles: %s\n", region_list); #endif while (compare && compare->next) { MetaRectangle *a = compare->data; GList *other = compare->next; g_assert (a->width > 0 && a->height > 0); while (other) { MetaRectangle *b = other->data; GList *delete_me = NULL; g_assert (b->width > 0 && b->height > 0); #ifdef PRINT_DEBUG printf (" -- Comparing %s to %s --\n", meta_rectangle_to_string (a, rect1), meta_rectangle_to_string (b, rect2)); #endif /* If a contains b, just remove b */ if (meta_rectangle_contains_rect (a, b)) { delete_me = other; num_contains++; num_merged++; } /* If b contains a, just remove a */ else if (meta_rectangle_contains_rect (a, b)) { delete_me = compare; num_contains++; num_merged++; } /* If a and b might be mergeable horizontally */ else if (a->y == b->y && a->height == b->height) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; num_part_contains++; num_merged++; } /* If a and b are adjacent */ else if (a->x + a->width == b->x || a->x == b->x + b->width) { int new_x = MIN (a->x, b->x); a->width = MAX (a->x + a->width, b->x + b->width) - new_x; a->x = new_x; delete_me = other; num_adjacent++; num_merged++; } } /* If a and b might be mergeable vertically */ else if (a->x == b->x && a->width == b->width) { /* If a and b overlap */ if (meta_rectangle_overlap (a, b)) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; num_part_contains++; num_merged++; } /* If a and b are adjacent */ else if (a->y + a->height == b->y || a->y == b->y + b->height) { int new_y = MIN (a->y, b->y); a->height = MAX (a->y + a->height, b->y + b->height) - new_y; a->y = new_y; delete_me = other; num_adjacent++; num_merged++; } } other = other->next; /* Delete any rectangle in the list that is no longer wanted */ if (delete_me != NULL) { #ifdef PRINT_DEBUG MetaRectangle *bla = delete_me->data; printf (" Deleting rect %s\n", meta_rectangle_to_string (bla, rect1)); #endif /* Deleting the rect we're compare others to is a little tricker */ if (compare == delete_me) { compare = compare->next; other = compare->next; a = compare->data; } /* Okay, we can free it now */ g_free (delete_me->data); region = g_list_delete_link (region, delete_me); } #ifdef PRINT_DEBUG char region_list[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list); printf (" After comparison, new list is: %s\n", region_list); #endif } compare = compare->next; } printf (" Num rectangles contained in others : %d\n", num_contains); printf (" Num rectangles partially contained in others: %d\n", num_part_contains); printf (" Num rectangles adjacent to others : %d\n", num_adjacent); printf (" Num rectangles merged with others : %d\n", num_merged); #ifdef PRINT_DEBUG char region_list2[(RECT_LENGTH + 2) * g_list_length (region)]; meta_rectangle_region_to_string (region, ", ", region_list2); printf (" Final rectangles: %s\n", region_list2); #endif meta_rectangle_free_spanning_set (region); region = NULL; printf ("%s passed.\n", G_STRFUNC); } #endif static void verify_lists_are_equal (GList *code, GList *answer) { int which = 0; while (code && answer) { MetaRectangle *a = code->data; MetaRectangle *b = answer->data; if (a->x != b->x || a->y != b->y || a->width != b->width || a->height != b->height) { g_error ("%dth item in code answer answer lists do not match; " "code rect: %d,%d + %d,%d; answer rect: %d,%d + %d,%d\n", which, a->x, a->y, a->width, a->height, b->x, b->y, b->width, b->height); } code = code->next; answer = answer->next; which++; } /* Ought to be at the end of both lists; check if we aren't */ if (code) { MetaRectangle *tmp = code->data; g_error ("code list longer than answer list by %d items; " "first extra item: %d,%d +%d,%d\n", g_list_length (code), tmp->x, tmp->y, tmp->width, tmp->height); } if (answer) { MetaRectangle *tmp = answer->data; g_error ("answer list longer than code list by %d items; " "first extra item: %d,%d +%d,%d\n", g_list_length (answer), tmp->x, tmp->y, tmp->width, tmp->height); } } static void test_regions_okay (void) { GList* region; GList* tmp; /*************************************************************/ /* Make sure test region 0 has the right spanning rectangles */ /*************************************************************/ region = get_screen_region (0); tmp = NULL; tmp = g_list_prepend (tmp, new_meta_rect (0, 0, 1600, 1200)); verify_lists_are_equal (region, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (region); /*************************************************************/ /* Make sure test region 1 has the right spanning rectangles */ /*************************************************************/ region = get_screen_region (1); tmp = NULL; tmp = g_list_prepend (tmp, new_meta_rect (0, 20, 400, 1180)); tmp = g_list_prepend (tmp, new_meta_rect (0, 20, 1600, 1140)); verify_lists_are_equal (region, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (region); /*************************************************************/ /* Make sure test region 2 has the right spanning rectangles */ /*************************************************************/ region = get_screen_region (2); tmp = NULL; tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 300, 1180)); tmp = g_list_prepend (tmp, new_meta_rect ( 450, 20, 350, 1180)); tmp = g_list_prepend (tmp, new_meta_rect (1200, 20, 400, 1180)); tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 800, 1130)); tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 1080)); verify_lists_are_equal (region, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (region); /*************************************************************/ /* Make sure test region 3 has the right spanning rectangles */ /*************************************************************/ region = get_screen_region (3); tmp = NULL; tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 300, 1180)); /* 354000 */ tmp = g_list_prepend (tmp, new_meta_rect ( 380, 20, 1220, 1180)); /* 377600 */ tmp = g_list_prepend (tmp, new_meta_rect ( 0, 20, 1600, 1130)); /* 791000 */ #if 0 printf ("Got to here...\n"); char region_list[(RECT_LENGTH+2) * g_list_length (region)]; char tmp_list[ (RECT_LENGTH+2) * g_list_length (tmp)]; meta_rectangle_region_to_string (region, ", ", region_list); meta_rectangle_region_to_string (region, ", ", tmp_list); printf ("%s vs. %s\n", region_list, tmp_list); #endif verify_lists_are_equal (region, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (region); /*************************************************************/ /* Make sure test region 4 has the right spanning rectangles */ /*************************************************************/ region = get_screen_region (4); tmp = NULL; tmp = g_list_prepend (tmp, new_meta_rect ( 800, 20, 800, 1180)); verify_lists_are_equal (region, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (region); /*************************************************************/ /* Make sure test region 5 has the right spanning rectangles */ /*************************************************************/ g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Region to merge was empty!*"); region = get_screen_region (5); g_test_assert_expected_messages (); verify_lists_are_equal (region, NULL); /* FIXME: Still to do: * - Create random struts and check the regions somehow */ } static void test_region_fitting (void) { GList* region; MetaRectangle rect; /* See test_basic_fitting() for how/why these automated random tests work */ int i; region = get_screen_region (3); for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&rect); g_assert (meta_rectangle_contained_in_region (region, &rect) == FALSE || meta_rectangle_could_fit_in_region (region, &rect) == TRUE); } meta_rectangle_free_list_and_elements (region); /* Do some manual tests too */ region = get_screen_region (1); rect = meta_rect (50, 50, 400, 400); g_assert (meta_rectangle_could_fit_in_region (region, &rect)); g_assert (meta_rectangle_contained_in_region (region, &rect)); rect = meta_rect (250, 0, 500, 1150); g_assert (!meta_rectangle_could_fit_in_region (region, &rect)); g_assert (!meta_rectangle_contained_in_region (region, &rect)); rect = meta_rect (250, 0, 400, 400); g_assert (meta_rectangle_could_fit_in_region (region, &rect)); g_assert (!meta_rectangle_contained_in_region (region, &rect)); meta_rectangle_free_list_and_elements (region); region = get_screen_region (2); rect = meta_rect (1000, 50, 600, 1100); g_assert (meta_rectangle_could_fit_in_region (region, &rect)); g_assert (!meta_rectangle_contained_in_region (region, &rect)); meta_rectangle_free_list_and_elements (region); } static void test_clamping_to_region (void) { GList* region; MetaRectangle rect; MetaRectangle min_size; FixedDirections fixed_directions; int i; min_size.height = min_size.width = 1; fixed_directions = 0; region = get_screen_region (3); for (i = 0; i < NUM_RANDOM_RUNS; i++) { MetaRectangle temp; get_random_rect (&rect); temp = rect; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (meta_rectangle_could_fit_in_region (region, &rect) == TRUE); g_assert (rect.x == temp.x && rect.y == temp.y); } meta_rectangle_free_list_and_elements (region); /* Do some manual tests too */ region = get_screen_region (1); rect = meta_rect (50, 50, 10000, 10000); meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 1600 && rect.height == 1140); rect = meta_rect (275, -50, 410, 10000); meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 400 && rect.height == 1180); rect = meta_rect (50, 50, 10000, 10000); min_size.height = 1170; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 400 && rect.height == 1180); rect = meta_rect (50, 50, 10000, 10000); min_size.width = 600; min_size.height = 1170; g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No rect whose size to clamp to found*"); meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_test_assert_expected_messages (); g_assert (rect.width == 600 && rect.height == 1170); rect = meta_rect (350, 50, 100, 1100); min_size.width = 1; min_size.height = 1; fixed_directions = FIXED_DIRECTION_X; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 100 && rect.height == 1100); rect = meta_rect (300, 70, 500, 1100); min_size.width = 1; min_size.height = 1; fixed_directions = FIXED_DIRECTION_Y; meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_assert (rect.width == 400 && rect.height == 1100); rect = meta_rect (300, 70, 999999, 999999); min_size.width = 100; min_size.height = 200; fixed_directions = FIXED_DIRECTION_Y; g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "No rect whose size to clamp to found*"); meta_rectangle_clamp_to_fit_into_region (region, fixed_directions, &rect, &min_size); g_test_assert_expected_messages (); g_assert (rect.width == 100 && rect.height == 999999); meta_rectangle_free_list_and_elements (region); } static gboolean rect_overlaps_region (const GList *spanning_rects, const MetaRectangle *rect) { /* FIXME: Should I move this to boxes.[ch]? */ const GList *temp; gboolean overlaps; temp = spanning_rects; overlaps = FALSE; while (!overlaps && temp != NULL) { overlaps = overlaps || meta_rectangle_overlap (temp->data, rect); temp = temp->next; } return overlaps; } gboolean time_to_print = FALSE; static void test_clipping_to_region (void) { GList* region; MetaRectangle rect, temp; FixedDirections fixed_directions = 0; int i; region = get_screen_region (3); for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&rect); if (rect_overlaps_region (region, &rect)) { meta_rectangle_clip_to_region (region, 0, &rect); g_assert (meta_rectangle_contained_in_region (region, &rect) == TRUE); } } meta_rectangle_free_list_and_elements (region); /* Do some manual tests too */ region = get_screen_region (2); rect = meta_rect (-50, -10, 10000, 10000); meta_rectangle_clip_to_region (region, fixed_directions, &rect); g_assert (meta_rectangle_equal (region->data, &rect)); rect = meta_rect (300, 1000, 400, 200); temp = meta_rect (300, 1000, 400, 150); meta_rectangle_clip_to_region (region, fixed_directions, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (400, 1000, 300, 200); temp = meta_rect (450, 1000, 250, 200); meta_rectangle_clip_to_region (region, fixed_directions, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (400, 1000, 300, 200); temp = meta_rect (400, 1000, 300, 150); meta_rectangle_clip_to_region (region, FIXED_DIRECTION_X, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (400, 1000, 300, 200); temp = meta_rect (400, 1000, 300, 150); meta_rectangle_clip_to_region (region, FIXED_DIRECTION_X, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); meta_rectangle_free_list_and_elements (region); } static void test_shoving_into_region (void) { GList* region; MetaRectangle rect, temp; FixedDirections fixed_directions = 0; int i; region = get_screen_region (3); for (i = 0; i < NUM_RANDOM_RUNS; i++) { get_random_rect (&rect); if (meta_rectangle_could_fit_in_region (region, &rect)) { meta_rectangle_shove_into_region (region, 0, &rect); g_assert (meta_rectangle_contained_in_region (region, &rect)); } } meta_rectangle_free_list_and_elements (region); /* Do some manual tests too */ region = get_screen_region (2); rect = meta_rect (300, 1000, 400, 200); temp = meta_rect (300, 950, 400, 200); meta_rectangle_shove_into_region (region, fixed_directions, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (425, 1000, 300, 200); temp = meta_rect (450, 1000, 300, 200); meta_rectangle_shove_into_region (region, fixed_directions, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (425, 1000, 300, 200); temp = meta_rect (425, 950, 300, 200); meta_rectangle_shove_into_region (region, FIXED_DIRECTION_X, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 300, 1000, 400, 200); temp = meta_rect (1200, 1000, 400, 200); meta_rectangle_shove_into_region (region, FIXED_DIRECTION_Y, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 800, 1150, 400, 50); /* Completely "offscreen" :) */ temp = meta_rect ( 800, 1050, 400, 50); meta_rectangle_shove_into_region (region, 0, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (-1000, 0, 400, 150); /* Offscreen in 2 directions */ temp = meta_rect ( 0, 20, 400, 150); meta_rectangle_shove_into_region (region, 0, &rect); g_assert (meta_rectangle_equal (&rect, &temp)); meta_rectangle_free_list_and_elements (region); } static void verify_edge_lists_are_equal (GList *code, GList *answer) { int which = 0; while (code && answer) { MetaEdge *a = code->data; MetaEdge *b = answer->data; if (!meta_rectangle_equal (&a->rect, &b->rect) || a->side_type != b->side_type || a->edge_type != b->edge_type) { g_error ("%dth item in code answer answer lists do not match; " "code rect: %d,%d + %d,%d; answer rect: %d,%d + %d,%d\n", which, a->rect.x, a->rect.y, a->rect.width, a->rect.height, b->rect.x, b->rect.y, b->rect.width, b->rect.height); } code = code->next; answer = answer->next; which++; } /* Ought to be at the end of both lists; check if we aren't */ if (code) { MetaEdge *tmp = code->data; g_error ("code list longer than answer list by %d items; " "first extra item rect: %d,%d +%d,%d\n", g_list_length (code), tmp->rect.x, tmp->rect.y, tmp->rect.width, tmp->rect.height); } if (answer) { MetaEdge *tmp = answer->data; g_error ("answer list longer than code list by %d items; " "first extra item rect: %d,%d +%d,%d\n", g_list_length (answer), tmp->rect.x, tmp->rect.y, tmp->rect.width, tmp->rect.height); } } static void test_find_onscreen_edges (void) { GList* edges; GList* tmp; int left = META_DIRECTION_LEFT; int right = META_DIRECTION_RIGHT; int top = META_DIRECTION_TOP; int bottom = META_DIRECTION_BOTTOM; /*************************************************/ /* Make sure test region 0 has the correct edges */ /*************************************************/ edges = get_screen_edges (0); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 1600, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 0, 1600, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 0, 0, 1200, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 0, 0, 1200, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 1 has the correct edges */ /*************************************************/ edges = get_screen_edges (1); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 400, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 400, 1160, 1200, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1140, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 400, 1160, 0, 40, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 2 has the correct edges */ /*************************************************/ edges = get_screen_edges (2); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge (1200, 1200, 400, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 450, 1200, 350, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 300, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 150, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 400, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 0, 100, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 0, 50, right)); tmp = g_list_prepend (tmp, new_screen_edge (1200, 1100, 0, 100, left)); tmp = g_list_prepend (tmp, new_screen_edge ( 450, 1150, 0, 50, left)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 3 has the correct edges */ /*************************************************/ edges = get_screen_edges (3); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge (1200, 1200, 400, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 380, 1200, 420, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 300, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 80, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 400, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 700, 525, 200, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 700, 675, 200, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 1600, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1100, 0, 100, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 700, 525, 0, 150, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 300, 1150, 0, 50, right)); tmp = g_list_prepend (tmp, new_screen_edge (1200, 1100, 0, 100, left)); tmp = g_list_prepend (tmp, new_screen_edge ( 900, 525, 0, 150, left)); tmp = g_list_prepend (tmp, new_screen_edge ( 380, 1150, 0, 50, left)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 20, 0, 1180, left)); #if 0 #define FUDGE 50 /* number of edges */ char big_buffer1[(EDGE_LENGTH+2)*FUDGE], big_buffer2[(EDGE_LENGTH+2)*FUDGE]; meta_rectangle_edge_list_to_string (edges, "\n ", big_buffer1); meta_rectangle_edge_list_to_string (tmp, "\n ", big_buffer2); printf("Generated edge list:\n %s\nComparison edges list:\n %s\n", big_buffer1, big_buffer2); #endif verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 4 has the correct edges */ /*************************************************/ edges = get_screen_edges (4); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge ( 800, 1200, 800, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 20, 800, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 20, 0, 1180, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 800, 20, 0, 1180, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 5 has the correct edges */ /*************************************************/ edges = get_screen_edges (5); tmp = NULL; verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************/ /* Make sure test region 6 has the correct edges */ /*************************************************/ edges = get_screen_edges (6); tmp = NULL; tmp = g_list_prepend (tmp, new_screen_edge ( 0, 1200, 1600, 0, bottom)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 40, 1600, 0, top)); tmp = g_list_prepend (tmp, new_screen_edge (1600, 40, 0, 1160, right)); tmp = g_list_prepend (tmp, new_screen_edge ( 0, 40, 0, 1160, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); } static void test_find_nonintersected_monitor_edges (void) { GList* edges; GList* tmp; int left = META_DIRECTION_LEFT; int right = META_DIRECTION_RIGHT; int top = META_DIRECTION_TOP; int bottom = META_DIRECTION_BOTTOM; /*************************************************************************/ /* Make sure test monitor set 0 for with region 0 has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (0, 0); tmp = NULL; verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************************************/ /* Make sure test monitor set 2 for with region 1 has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (2, 1); tmp = NULL; tmp = g_list_prepend (tmp, new_monitor_edge ( 0, 600, 1600, 0, bottom)); tmp = g_list_prepend (tmp, new_monitor_edge ( 0, 600, 1600, 0, top)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************************************/ /* Make sure test monitor set 1 for with region 2 has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (1, 2); tmp = NULL; tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 20, 0, 1080, right)); tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 20, 0, 1180, left)); #if 0 #define FUDGE 50 char big_buffer1[(EDGE_LENGTH+2)*FUDGE], big_buffer2[(EDGE_LENGTH+2)*FUDGE]; meta_rectangle_edge_list_to_string (edges, "\n ", big_buffer1); meta_rectangle_edge_list_to_string (tmp, "\n ", big_buffer2); printf("Generated edge list:\n %s\nComparison edges list:\n %s\n", big_buffer1, big_buffer2); #endif verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************************************/ /* Make sure test monitor set 3 for with region 3 has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (3, 3); tmp = NULL; tmp = g_list_prepend (tmp, new_monitor_edge ( 900, 600, 700, 0, bottom)); tmp = g_list_prepend (tmp, new_monitor_edge ( 0, 600, 700, 0, bottom)); tmp = g_list_prepend (tmp, new_monitor_edge ( 900, 600, 700, 0, top)); tmp = g_list_prepend (tmp, new_monitor_edge ( 0, 600, 700, 0, top)); tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 675, 0, 425, right)); tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 675, 0, 525, left)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************************************/ /* Make sure test monitor set 3 for with region 4 has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (3, 4); tmp = NULL; tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 600, 800, 0, bottom)); tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 600, 800, 0, top)); tmp = g_list_prepend (tmp, new_monitor_edge ( 800, 600, 0, 600, right)); verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); /*************************************************************************/ /* Make sure test monitor set 3 for with region 5has the correct edges */ /*************************************************************************/ edges = get_monitor_edges (3, 5); tmp = NULL; verify_edge_lists_are_equal (edges, tmp); meta_rectangle_free_list_and_elements (tmp); meta_rectangle_free_list_and_elements (edges); } static void test_gravity_resize (void) { MetaRectangle oldrect, rect, temp; rect.x = -500; /* Some random amount not equal to oldrect.x to ensure that * the resize is done with respect to oldrect instead of rect */ oldrect = meta_rect ( 50, 300, 250, 400); temp = meta_rect ( 50, 300, 20, 5); meta_rectangle_resize_with_gravity (&oldrect, &rect, META_GRAVITY_NORTH_WEST, 20, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 50, 300, 250, 400); temp = meta_rect (165, 300, 20, 5); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_NORTH, 20, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 50, 300, 250, 400); temp = meta_rect (280, 300, 20, 5); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_NORTH_EAST, 20, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 50, 300, 250, 400); temp = meta_rect ( 50, 695, 50, 5); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_SOUTH_WEST, 50, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 50, 300, 250, 400); temp = meta_rect (150, 695, 50, 5); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_SOUTH, 50, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 50, 300, 250, 400); temp = meta_rect (250, 695, 50, 5); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_SOUTH_EAST, 50, 5); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (167, 738, 237, 843); temp = meta_rect (167, 1113, 832, 93); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_WEST, 832, 93); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect ( 167, 738, 237, 843); temp = meta_rect (-131, 1113, 833, 93); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_CENTER, 832, 93); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (300, 1000, 400, 200); temp = meta_rect (270, 994, 430, 212); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_EAST, 430, 211); g_assert (meta_rectangle_equal (&rect, &temp)); rect = meta_rect (300, 1000, 400, 200); temp = meta_rect (300, 1000, 430, 211); meta_rectangle_resize_with_gravity (&rect, &rect, META_GRAVITY_STATIC, 430, 211); g_assert (meta_rectangle_equal (&rect, &temp)); } #define EPSILON 0.000000001 static void test_find_closest_point_to_line (void) { double x1, y1, x2, y2, px, py, rx, ry; double answer_x, answer_y; x1 = 3.0; y1 = 49.0; x2 = 2.0; y2 = - 1.0; px = -2.6; py = 19.1; answer_x = 2.4; answer_y = 19; meta_rectangle_find_linepoint_closest_to_point (x1, y1, x2, y2, px, py, &rx, &ry); g_assert (fabs (rx - answer_x) < EPSILON && fabs (ry - answer_y) < EPSILON); /* Special test for x1 == x2, so that slop of line is infinite */ x1 = 3.0; y1 = 49.0; x2 = 3.0; y2 = - 1.0; px = -2.6; py = 19.1; answer_x = 3.0; answer_y = 19.1; meta_rectangle_find_linepoint_closest_to_point (x1, y1, x2, y2, px, py, &rx, &ry); g_assert (fabs (rx - answer_x) < EPSILON && fabs (ry - answer_y) < EPSILON); /* Special test for y1 == y2, so perp line has slope of infinity */ x1 = 3.14; y1 = 7.0; x2 = 2.718; y2 = 7.0; px = -2.6; py = 19.1; answer_x = -2.6; answer_y = 7; meta_rectangle_find_linepoint_closest_to_point (x1, y1, x2, y2, px, py, &rx, &ry); g_assert (fabs (rx - answer_x) < EPSILON && fabs (ry - answer_y) < EPSILON); /* Test when we the point we want to be closest to is actually on the line */ x1 = 3.0; y1 = 49.0; x2 = 2.0; y2 = - 1.0; px = 2.4; py = 19.0; answer_x = 2.4; answer_y = 19; meta_rectangle_find_linepoint_closest_to_point (x1, y1, x2, y2, px, py, &rx, &ry); g_assert (fabs (rx - answer_x) < EPSILON && fabs (ry - answer_y) < EPSILON); } void init_boxes_tests (void) { init_random_ness (); g_test_add_func ("/util/boxes/area", test_area); g_test_add_func ("/util/boxes/intersect", test_intersect); g_test_add_func ("/util/boxes/equal", test_equal); g_test_add_func ("/util/boxes/overlap", test_overlap_funcs); g_test_add_func ("/util/boxes/basic-fitting", test_basic_fitting); g_test_add_func ("/util/boxes/regions-ok", test_regions_okay); g_test_add_func ("/util/boxes/regions-fitting", test_region_fitting); g_test_add_func ("/util/boxes/clamp-to-region", test_clamping_to_region); g_test_add_func ("/util/boxes/clip-to-region", test_clipping_to_region); g_test_add_func ("/util/boxes/shove-into-region", test_shoving_into_region); /* And now the functions dealing with edges more than boxes */ g_test_add_func ("/util/boxes/onscreen-edges", test_find_onscreen_edges); g_test_add_func ("/util/boxes/nonintersected-monitor-edges", test_find_nonintersected_monitor_edges); /* And now the misfit functions that don't quite fit in anywhere else... */ g_test_add_func ("/util/boxes/gravity-resize", test_gravity_resize); g_test_add_func ("/util/boxes/closest-point-to-line", test_find_closest_point_to_line); } muffin-6.4.1/src/tests/meta-backend-test.c0000664000175000017500000000532414723361714017333 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/meta-backend-test.h" #include "tests/meta-gpu-test.h" #include "tests/meta-monitor-manager-test.h" struct _MetaBackendTest { MetaBackendX11Nested parent; MetaGpu *gpu; gboolean is_lid_closed; }; G_DEFINE_TYPE (MetaBackendTest, meta_backend_test, META_TYPE_BACKEND_X11_NESTED) void meta_backend_test_set_is_lid_closed (MetaBackendTest *backend_test, gboolean is_lid_closed) { backend_test->is_lid_closed = is_lid_closed; } MetaGpu * meta_backend_test_get_gpu (MetaBackendTest *backend_test) { return backend_test->gpu; } static gboolean meta_backend_test_is_lid_closed (MetaBackend *backend) { MetaBackendTest *backend_test = META_BACKEND_TEST (backend); return backend_test->is_lid_closed; } static void meta_backend_test_init_gpus (MetaBackendX11Nested *backend_x11_nested) { MetaBackendTest *backend_test = META_BACKEND_TEST (backend_x11_nested); backend_test->gpu = g_object_new (META_TYPE_GPU_TEST, "backend", backend_test, NULL); meta_backend_add_gpu (META_BACKEND (backend_test), backend_test->gpu); } static void meta_backend_test_init (MetaBackendTest *backend_test) { } static MetaMonitorManager * meta_backend_test_create_monitor_manager (MetaBackend *backend, GError **error) { return g_object_new (META_TYPE_MONITOR_MANAGER_TEST, "backend", backend, NULL); } static void meta_backend_test_class_init (MetaBackendTestClass *klass) { MetaBackendClass *backend_class = META_BACKEND_CLASS (klass); MetaBackendX11NestedClass *backend_x11_nested_class = META_BACKEND_X11_NESTED_CLASS (klass); backend_class->create_monitor_manager = meta_backend_test_create_monitor_manager; backend_class->is_lid_closed = meta_backend_test_is_lid_closed; backend_x11_nested_class->init_gpus = meta_backend_test_init_gpus; } muffin-6.4.1/src/tests/test-utils.c0000664000175000017500000003435114723361714016162 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/test-utils.h" #include #include #include "core/display-private.h" #include "core/window-private.h" #include "wayland/meta-wayland.h" #include "wayland/meta-xwayland.h" #include "x11/meta-x11-display-private.h" struct _TestClient { char *id; MetaWindowClientType type; GSubprocess *subprocess; GCancellable *cancellable; GMainLoop *loop; GDataOutputStream *in; GDataInputStream *out; char *line; GError **error; AsyncWaiter *waiter; }; struct _AsyncWaiter { XSyncCounter counter; int counter_value; XSyncAlarm alarm; GMainLoop *loop; int counter_wait_value; }; G_DEFINE_QUARK (test-runner-error-quark, test_runner_error) static char *test_client_path; static void ensure_test_client_path (int argc, char **argv) { test_client_path = g_test_build_filename (G_TEST_BUILT, "src", "tests", "mutter-test-client", NULL); if (!g_file_test (test_client_path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE)) { g_autofree char *basename = NULL; g_autofree char *dirname = NULL; basename = g_path_get_basename (argv[0]); dirname = g_path_get_dirname (argv[0]); test_client_path = g_build_filename (dirname, "mutter-test-client", NULL); } if (!g_file_test (test_client_path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_EXECUTABLE)) g_error ("mutter-test-client executable not found"); } void test_init (int *argc, char ***argv) { g_test_init (argc, argv, NULL); g_test_bug_base ("http://bugzilla.gnome.org/show_bug.cgi?id="); ensure_test_client_path (*argc, *argv); meta_wayland_override_display_name ("mutter-test-display"); meta_xwayland_override_display_number (512); } AsyncWaiter * async_waiter_new (void) { AsyncWaiter *waiter = g_new0 (AsyncWaiter, 1); MetaDisplay *display = meta_get_display (); Display *xdisplay = display->x11_display->xdisplay; XSyncValue value; XSyncAlarmAttributes attr; waiter->counter_value = 0; XSyncIntToValue (&value, waiter->counter_value); waiter->counter = XSyncCreateCounter (xdisplay, value); attr.trigger.counter = waiter->counter; attr.trigger.test_type = XSyncPositiveComparison; /* Initialize to one greater than the current value */ attr.trigger.value_type = XSyncRelative; XSyncIntToValue (&attr.trigger.wait_value, 1); /* After triggering, increment test_value by this until * until the test condition is false */ XSyncIntToValue (&attr.delta, 1); /* we want events (on by default anyway) */ attr.events = True; waiter->alarm = XSyncCreateAlarm (xdisplay, XSyncCACounter | XSyncCAValueType | XSyncCAValue | XSyncCATestType | XSyncCADelta | XSyncCAEvents, &attr); waiter->loop = g_main_loop_new (NULL, FALSE); return waiter; } void async_waiter_destroy (AsyncWaiter *waiter) { MetaDisplay *display = meta_get_display (); Display *xdisplay = display->x11_display->xdisplay; XSyncDestroyAlarm (xdisplay, waiter->alarm); XSyncDestroyCounter (xdisplay, waiter->counter); g_main_loop_unref (waiter->loop); } static int async_waiter_next_value (AsyncWaiter *waiter) { return waiter->counter_value + 1; } static void async_waiter_wait (AsyncWaiter *waiter, int wait_value) { if (waiter->counter_value < wait_value) { waiter->counter_wait_value = wait_value; g_main_loop_run (waiter->loop); waiter->counter_wait_value = 0; } } void async_waiter_set_and_wait (AsyncWaiter *waiter) { MetaDisplay *display = meta_get_display (); Display *xdisplay = display->x11_display->xdisplay; int wait_value = async_waiter_next_value (waiter); XSyncValue sync_value; XSyncIntToValue (&sync_value, wait_value); XSyncSetCounter (xdisplay, waiter->counter, sync_value); async_waiter_wait (waiter, wait_value); } gboolean async_waiter_alarm_filter (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data) { AsyncWaiter *waiter = data; if (event->alarm != waiter->alarm) return FALSE; waiter->counter_value = XSyncValueLow32 (event->counter_value); if (waiter->counter_wait_value != 0 && waiter->counter_value >= waiter->counter_wait_value) g_main_loop_quit (waiter->loop); return TRUE; } char * test_client_get_id (TestClient *client) { return client->id; } static void test_client_line_read (GObject *source, GAsyncResult *result, gpointer data) { TestClient *client = data; client->line = g_data_input_stream_read_line_finish_utf8 (client->out, result, NULL, client->error); g_main_loop_quit (client->loop); } gboolean test_client_do (TestClient *client, GError **error, ...) { GString *command = g_string_new (NULL); char *line = NULL; va_list vap; va_start (vap, error); while (TRUE) { char *word = va_arg (vap, char *); char *quoted; if (word == NULL) break; if (command->len > 0) g_string_append_c (command, ' '); quoted = g_shell_quote (word); g_string_append (command, quoted); g_free (quoted); } va_end (vap); g_string_append_c (command, '\n'); if (!g_data_output_stream_put_string (client->in, command->str, client->cancellable, error)) goto out; g_data_input_stream_read_line_async (client->out, G_PRIORITY_DEFAULT, client->cancellable, test_client_line_read, client); client->error = error; g_main_loop_run (client->loop); line = client->line; client->line = NULL; client->error = NULL; if (!line) { if (*error == NULL) g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_RUNTIME_ERROR, "test client exited"); goto out; } if (strcmp (line, "OK") != 0) { g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_RUNTIME_ERROR, "%s", line); goto out; } out: g_string_free (command, TRUE); g_free (line); return *error == NULL; } gboolean test_client_wait (TestClient *client, GError **error) { if (client->type == META_WINDOW_CLIENT_TYPE_WAYLAND) { return test_client_do (client, error, "sync", NULL); } else { int wait_value = async_waiter_next_value (client->waiter); char *counter_str = g_strdup_printf ("%lu", client->waiter->counter); char *wait_value_str = g_strdup_printf ("%d", wait_value); gboolean success; success = test_client_do (client, error, "set_counter", counter_str, wait_value_str, NULL); g_free (counter_str); g_free (wait_value_str); if (!success) return FALSE; async_waiter_wait (client->waiter, wait_value); return TRUE; } } MetaWindow * test_client_find_window (TestClient *client, const char *window_id, GError **error) { MetaDisplay *display = meta_get_display (); GSList *windows; GSList *l; MetaWindow *result; char *expected_title; windows = meta_display_list_windows (display, META_LIST_INCLUDE_OVERRIDE_REDIRECT); expected_title = g_strdup_printf ("test/%s/%s", client->id, window_id); result = NULL; for (l = windows; l; l = l->next) { MetaWindow *window = l->data; if (g_strcmp0 (window->title, expected_title) == 0) { result = window; break; } } g_slist_free (windows); g_free (expected_title); if (result == NULL) g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_RUNTIME_ERROR, "window %s/%s isn't known to Mutter", client->id, window_id); return result; } typedef struct _WaitForShownData { GMainLoop *loop; MetaWindow *window; gulong shown_handler_id; } WaitForShownData; static void on_window_shown (MetaWindow *window, WaitForShownData *data) { g_main_loop_quit (data->loop); } static gboolean wait_for_showing_before_redraw (gpointer user_data) { WaitForShownData *data = user_data; if (meta_window_is_hidden (data->window)) { data->shown_handler_id = g_signal_connect (data->window, "shown", G_CALLBACK (on_window_shown), data); } else { g_main_loop_quit (data->loop); } return FALSE; } void test_client_wait_for_window_shown (TestClient *client, MetaWindow *window) { WaitForShownData data = { .loop = g_main_loop_new (NULL, FALSE), .window = window, }; meta_later_add (META_LATER_BEFORE_REDRAW, wait_for_showing_before_redraw, &data, NULL); g_main_loop_run (data.loop); g_clear_signal_handler (&data.shown_handler_id, window); g_main_loop_unref (data.loop); } gboolean test_client_alarm_filter (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data) { TestClient *client = data; if (client->waiter) return async_waiter_alarm_filter (x11_display, event, client->waiter); else return FALSE; } TestClient * test_client_new (const char *id, MetaWindowClientType type, GError **error) { TestClient *client; GSubprocessLauncher *launcher; GSubprocess *subprocess; MetaWaylandCompositor *compositor; const char *wayland_display_name; const char *x11_display_name; launcher = g_subprocess_launcher_new ((G_SUBPROCESS_FLAGS_STDIN_PIPE | G_SUBPROCESS_FLAGS_STDOUT_PIPE)); g_assert (meta_is_wayland_compositor ()); compositor = meta_wayland_compositor_get_default (); wayland_display_name = meta_wayland_get_wayland_display_name (compositor); x11_display_name = meta_wayland_get_xwayland_display_name (compositor); g_subprocess_launcher_setenv (launcher, "WAYLAND_DISPLAY", wayland_display_name, TRUE); g_subprocess_launcher_setenv (launcher, "DISPLAY", x11_display_name, TRUE); subprocess = g_subprocess_launcher_spawn (launcher, error, test_client_path, "--client-id", id, (type == META_WINDOW_CLIENT_TYPE_WAYLAND ? "--wayland" : NULL), NULL); g_object_unref (launcher); if (!subprocess) return NULL; client = g_new0 (TestClient, 1); client->type = type; client->id = g_strdup (id); client->cancellable = g_cancellable_new (); client->subprocess = subprocess; client->in = g_data_output_stream_new (g_subprocess_get_stdin_pipe (subprocess)); client->out = g_data_input_stream_new (g_subprocess_get_stdout_pipe (subprocess)); client->loop = g_main_loop_new (NULL, FALSE); if (client->type == META_WINDOW_CLIENT_TYPE_X11) client->waiter = async_waiter_new (); return client; } gboolean test_client_quit (TestClient *client, GError **error) { if (!test_client_do (client, error, "destroy_all", NULL)) return FALSE; if (!test_client_wait (client, error)) return FALSE; return TRUE; } void test_client_destroy (TestClient *client) { GError *error = NULL; if (client->waiter) async_waiter_destroy (client->waiter); g_output_stream_close (G_OUTPUT_STREAM (client->in), NULL, &error); if (error) { g_warning ("Error closing client stdin: %s", error->message); g_clear_error (&error); } g_object_unref (client->in); g_input_stream_close (G_INPUT_STREAM (client->out), NULL, &error); if (error) { g_warning ("Error closing client stdout: %s", error->message); g_clear_error (&error); } g_object_unref (client->out); g_object_unref (client->cancellable); g_object_unref (client->subprocess); g_main_loop_unref (client->loop); g_free (client->id); g_free (client); } const char * test_get_plugin_name (void) { const char *name; name = g_getenv ("MUTTER_TEST_PLUGIN_PATH"); if (name) return name; else return "libdefault"; } void test_wait_for_x11_display (void) { MetaDisplay *display; display = meta_get_display (); g_assert_nonnull (display); while (!display->x11_display) g_main_context_iteration (NULL, TRUE); g_assert_nonnull (display->x11_display); } muffin-6.4.1/src/tests/meson.build0000664000175000017500000001052014723361714016033 0ustar fabiofabioclutter_test_utils = files ( 'clutter-test-utils.c', 'clutter-test-utils.h', ) if have_clutter_tests subdir('clutter') endif tests_includepath = mutter_includes tests_c_args = mutter_c_args tests_deps = [ mutter_deps, libmutter_dep, libmutter_cogl_dep, libmutter_clutter_dep, ] subdir('wayland-test-clients') if have_installed_tests stacking_files_datadir = join_paths(pkgdatadir, 'tests') installed_tests_cdata = configuration_data() installed_tests_cdata.set('libexecdir', libexecdir) installed_tests_cdata.set('apiversion', libmutter_api_version) configure_file( input: 'mutter-all.test.in', output: 'mutter-all.test', configuration: installed_tests_cdata, install: true, install_dir: mutter_installed_tests_datadir, ) install_subdir('stacking', install_dir: stacking_files_datadir) endif test_env = environment() test_env.set('G_TEST_SRCDIR', join_paths(meson.project_source_root(), 'src')) test_env.set('G_TEST_BUILDDIR', meson.project_build_root()) test_env.set('MUTTER_TEST_PLUGIN_PATH', '@0@'.format(default_plugin.full_path())) test_client = executable('mutter-test-client', sources: ['test-client.c'], include_directories: tests_includepath, c_args: tests_c_args, dependencies: [ gtk3_dep, gio_unix_dep, x11_dep, xext_dep, ], install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, ) test_runner = executable('mutter-test-runner', sources: [ 'test-utils.c', 'test-utils.h', 'test-runner.c', ], include_directories: tests_includepath, c_args: tests_c_args, dependencies: [tests_deps], install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, ) unit_tests = executable('mutter-test-unit-tests', sources: [ 'test-utils.c', 'test-utils.h', 'unit-tests.c', 'boxes-tests.c', 'boxes-tests.h', 'meta-backend-test.c', 'meta-backend-test.h', 'meta-gpu-test.c', 'meta-gpu-test.h', 'meta-monitor-manager-test.c', 'meta-monitor-manager-test.h', 'monitor-config-migration-unit-tests.c', 'monitor-config-migration-unit-tests.h', 'monitor-store-unit-tests.c', 'monitor-store-unit-tests.h', 'monitor-test-utils.c', 'monitor-test-utils.h', 'monitor-transform-tests.c', 'monitor-transform-tests.h', 'monitor-unit-tests.c', 'monitor-unit-tests.h', 'wayland-unit-tests.c', 'wayland-unit-tests.h', test_driver_server_header, test_driver_protocol_code, ], include_directories: tests_includepath, c_args: tests_c_args, dependencies: [tests_deps], install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, ) headless_start_test = executable('mutter-headless-start-test', sources: [ 'headless-start-test.c', 'meta-backend-test.c', 'meta-backend-test.h', 'meta-gpu-test.c', 'meta-gpu-test.h', 'meta-monitor-manager-test.c', 'meta-monitor-manager-test.h', 'test-utils.c', 'test-utils.h', ], include_directories: tests_includepath, c_args: tests_c_args, dependencies: [tests_deps], install: have_installed_tests, install_dir: mutter_installed_tests_libexecdir, ) stacking_tests = [ 'basic-x11', 'basic-wayland', 'client-side-decorated', 'closed-transient', 'closed-transient-no-input-no-take-focus-parent', 'closed-transient-no-input-no-take-focus-parents', 'closed-transient-no-input-parent', 'closed-transient-no-input-parent-delayed-focus-default-cancelled', 'closed-transient-no-input-parents', 'closed-transient-no-input-parents-queued-default-focus-destroyed', 'closed-transient-only-take-focus-parents', 'minimized', 'mixed-windows', 'set-parent', 'override-redirect', 'set-override-redirect-parent', 'set-parent-exported', ] foreach stacking_test: stacking_tests suites = ['core', 'mutter/stacking'] if stacking_test.startswith('closed-transient-no-input') suites += ['flaky'] endif test(stacking_test, test_runner, suite: suites, env: test_env, args: [ files(join_paths('stacking', stacking_test + '.metatest')), ], is_parallel: false, timeout: 60, ) endforeach test('normal', unit_tests, suite: ['core', 'mutter/unit'], env: test_env, is_parallel: false, timeout: 60, ) test('headless-start', headless_start_test, suite: ['core', 'mutter/unit'], env: test_env, is_parallel: false, timeout: 60, ) muffin-6.4.1/src/tests/monitor-test-utils.c0000664000175000017500000000546214723361714017650 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/monitor-test-utils.h" #include "backends/meta-backend-private.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-config-store.h" void set_custom_monitor_config (const char *filename) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store; GError *error = NULL; const char *path; g_assert_nonnull (config_manager); config_store = meta_monitor_config_manager_get_store (config_manager); path = g_test_get_filename (G_TEST_DIST, "tests", "monitor-configs", filename, NULL); if (!meta_monitor_config_store_set_custom (config_store, path, NULL, &error)) g_error ("Failed to set custom config: %s", error->message); } char * read_file (const char *file_path) { g_autoptr (GFile) file = NULL; g_autoptr (GFileInputStream) input_stream = NULL; g_autoptr (GFileInfo) file_info = NULL; goffset file_size; gsize bytes_read; g_autofree char *buffer = NULL; GError *error = NULL; file = g_file_new_for_path (file_path); input_stream = g_file_read (file, NULL, &error); if (!input_stream) g_error ("Failed to read migrated config file: %s", error->message); file_info = g_file_input_stream_query_info (input_stream, G_FILE_ATTRIBUTE_STANDARD_SIZE, NULL, &error); if (!file_info) g_error ("Failed to read file info: %s", error->message); file_size = g_file_info_get_size (file_info); buffer = g_malloc0 (file_size + 1); if (!g_input_stream_read_all (G_INPUT_STREAM (input_stream), buffer, file_size, &bytes_read, NULL, &error)) g_error ("Failed to read file content: %s", error->message); g_assert_cmpint ((goffset) bytes_read, ==, file_size); return g_steal_pointer (&buffer); } muffin-6.4.1/src/tests/monitor-config-migration-unit-tests.h0000664000175000017500000000167214723361714023110 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MONITOR_CONFIG_MIGRATION_UNIT_TESTS_H #define MONITOR_CONFIG_MIGRATION_UNIT_TESTS_H void init_monitor_config_migration_tests (void); #endif /* MONITOR_CONFIG_MIGRATION_UNIT_TESTS_H */ muffin-6.4.1/src/tests/test-runner.c0000664000175000017500000006122314723361714016331 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include #include "compositor/meta-plugin-manager.h" #include "core/window-private.h" #include "meta/main.h" #include "meta/util.h" #include "meta/window.h" #include "tests/test-utils.h" #include "ui/ui.h" #include "wayland/meta-wayland.h" #include "x11/meta-x11-display-private.h" typedef struct { GHashTable *clients; AsyncWaiter *waiter; GString *warning_messages; GMainLoop *loop; } TestCase; static gboolean test_case_alarm_filter (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data) { TestCase *test = data; GHashTableIter iter; gpointer key, value; if (async_waiter_alarm_filter (x11_display, event, test->waiter)) return TRUE; g_hash_table_iter_init (&iter, test->clients); while (g_hash_table_iter_next (&iter, &key, &value)) if (test_client_alarm_filter (x11_display, event, value)) return TRUE; return FALSE; } static TestCase * test_case_new (void) { TestCase *test = g_new0 (TestCase, 1); test_wait_for_x11_display (); meta_x11_display_set_alarm_filter (meta_get_display ()->x11_display, test_case_alarm_filter, test); test->clients = g_hash_table_new (g_str_hash, g_str_equal); test->waiter = async_waiter_new (); test->loop = g_main_loop_new (NULL, FALSE); return test; } static gboolean test_case_loop_quit (gpointer data) { TestCase *test = data; g_main_loop_quit (test->loop); return FALSE; } static gboolean test_case_dispatch (TestCase *test, GError **error) { /* Wait until we've done any outstanding queued up work. * Though we add this as BEFORE_REDRAW, the iteration that runs the * BEFORE_REDRAW idles will proceed on and do the redraw, so we're * waiting until after *all* frame processing. */ meta_later_add (META_LATER_BEFORE_REDRAW, test_case_loop_quit, test, NULL); g_main_loop_run (test->loop); return TRUE; } static gboolean test_case_wait (TestCase *test, GError **error) { GHashTableIter iter; gpointer key, value; /* First have each client set a XSync counter, and wait until * we receive the resulting event - so we know we've received * everything that the client have sent us. */ g_hash_table_iter_init (&iter, test->clients); while (g_hash_table_iter_next (&iter, &key, &value)) if (!test_client_wait (value, error)) return FALSE; /* Then wait until we've done any outstanding queued up work. */ test_case_dispatch (test, error); /* Then set an XSync counter ourselves and and wait until * we receive the resulting event - this makes sure that we've * received back any X events we generated. */ async_waiter_set_and_wait (test->waiter); return TRUE; } static gboolean test_case_sleep (TestCase *test, guint32 interval, GError **error) { g_timeout_add_full (G_PRIORITY_LOW, interval, test_case_loop_quit, test, NULL); g_main_loop_run (test->loop); return TRUE; } #define BAD_COMMAND(...) \ G_STMT_START { \ g_set_error (error, \ TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_BAD_COMMAND, \ __VA_ARGS__); \ return FALSE; \ } G_STMT_END static TestClient * test_case_lookup_client (TestCase *test, char *client_id, GError **error) { TestClient *client = g_hash_table_lookup (test->clients, client_id); if (!client) g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_BAD_COMMAND, "No such client %s", client_id); return client; } static gboolean test_case_parse_window_id (TestCase *test, const char *client_and_window_id, TestClient **client, const char **window_id, GError **error) { const char *slash = strchr (client_and_window_id, '/'); char *tmp; if (slash == NULL) BAD_COMMAND ("client/window ID %s doesnt' contain a /", client_and_window_id); *window_id = slash + 1; tmp = g_strndup (client_and_window_id, slash - client_and_window_id); *client = test_case_lookup_client (test, tmp, error); g_free (tmp); return client != NULL; } static gboolean test_case_assert_stacking (TestCase *test, char **expected_windows, int n_expected_windows, GError **error) { MetaDisplay *display = meta_get_display (); guint64 *windows; int n_windows; GString *stack_string = g_string_new (NULL); GString *expected_string = g_string_new (NULL); int i; meta_stack_tracker_get_stack (display->stack_tracker, &windows, &n_windows); for (i = 0; i < n_windows; i++) { MetaWindow *window = meta_display_lookup_stack_id (display, windows[i]); if (window != NULL && window->title) { /* See comment in meta_ui_new() about why the dummy window for GTK+ theming * is managed as a MetaWindow. */ if (META_STACK_ID_IS_X11 (windows[i]) && meta_ui_window_is_dummy (display->x11_display->ui, windows[i])) continue; if (stack_string->len > 0) g_string_append_c (stack_string, ' '); if (g_str_has_prefix (window->title, "test/")) g_string_append (stack_string, window->title + 5); else g_string_append_printf (stack_string, "(%s)", window->title); } else if (windows[i] == display->x11_display->guard_window) { if (stack_string->len > 0) g_string_append_c (stack_string, ' '); g_string_append_c (stack_string, '|'); } } for (i = 0; i < n_expected_windows; i++) { if (expected_string->len > 0) g_string_append_c (expected_string, ' '); g_string_append (expected_string, expected_windows[i]); } /* Don't require '| ' as a prefix if there are no hidden windows - we * remove the prefix from the actual string instead of adding it to the * expected string for clarity of the error message */ if (index (expected_string->str, '|') == NULL && stack_string->str[0] == '|') { g_string_erase (stack_string, 0, stack_string->str[1] == ' ' ? 2 : 1); } if (strcmp (expected_string->str, stack_string->str) != 0) { g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, "stacking: expected='%s', actual='%s'", expected_string->str, stack_string->str); } g_string_free (stack_string, TRUE); g_string_free (expected_string, TRUE); return *error == NULL; } static gboolean test_case_assert_focused (TestCase *test, const char *expected_window, GError **error) { MetaDisplay *display = meta_get_display (); if (!display->focus_window) { if (g_strcmp0 (expected_window, "none") != 0) { g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, "focus: expected='%s', actual='none'", expected_window); } } else { const char *focused = display->focus_window->title; if (g_str_has_prefix (focused, "test/")) focused += 5; if (g_strcmp0 (focused, expected_window) != 0) g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, "focus: expected='%s', actual='%s'", expected_window, focused); } return *error == NULL; } static gboolean test_case_check_xserver_stacking (TestCase *test, GError **error) { MetaDisplay *display = meta_get_display (); GString *local_string = g_string_new (NULL); GString *x11_string = g_string_new (NULL); int i; guint64 *windows; int n_windows; meta_stack_tracker_get_stack (display->stack_tracker, &windows, &n_windows); for (i = 0; i < n_windows; i++) { if (META_STACK_ID_IS_X11 (windows[i])) { if (local_string->len > 0) g_string_append_c (local_string, ' '); g_string_append_printf (local_string, "%#lx", (Window)windows[i]); } } Window root; Window parent; Window *children; unsigned int n_children; XQueryTree (display->x11_display->xdisplay, display->x11_display->xroot, &root, &parent, &children, &n_children); for (i = 0; i < (int)n_children; i++) { if (x11_string->len > 0) g_string_append_c (x11_string, ' '); g_string_append_printf (x11_string, "%#lx", (Window)children[i]); } if (strcmp (x11_string->str, local_string->str) != 0) g_set_error (error, TEST_RUNNER_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED, "xserver stacking: x11='%s', local='%s'", x11_string->str, local_string->str); XFree (children); g_string_free (local_string, TRUE); g_string_free (x11_string, TRUE); return *error == NULL; } static gboolean test_case_do (TestCase *test, int argc, char **argv, GError **error) { if (strcmp (argv[0], "new_client") == 0) { MetaWindowClientType type; TestClient *client; if (argc != 3) BAD_COMMAND("usage: new_client [wayland|x11]"); if (strcmp (argv[2], "x11") == 0) type = META_WINDOW_CLIENT_TYPE_X11; else if (strcmp (argv[2], "wayland") == 0) type = META_WINDOW_CLIENT_TYPE_WAYLAND; else BAD_COMMAND("usage: new_client [wayland|x11]"); if (g_hash_table_lookup (test->clients, argv[1])) BAD_COMMAND("client %s already exists", argv[1]); client = test_client_new (argv[1], type, error); if (!client) return FALSE; g_hash_table_insert (test->clients, test_client_get_id (client), client); } else if (strcmp (argv[0], "quit_client") == 0) { if (argc != 2) BAD_COMMAND("usage: quit_client "); TestClient *client = test_case_lookup_client (test, argv[1], error); if (!client) return FALSE; if (!test_client_quit (client, error)) return FALSE; g_hash_table_remove (test->clients, test_client_get_id (client)); test_client_destroy (client); } else if (strcmp (argv[0], "create") == 0) { if (!(argc == 2 || (argc == 3 && strcmp (argv[2], "override") == 0) || (argc == 3 && strcmp (argv[2], "csd") == 0))) BAD_COMMAND("usage: %s / [override|csd]", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, "create", window_id, argc == 3 ? argv[2] : NULL, NULL)) return FALSE; if (!test_client_wait (client, error)) return FALSE; } else if (strcmp (argv[0], "set_parent") == 0 || strcmp (argv[0], "set_parent_exported") == 0) { if (argc != 3) BAD_COMMAND("usage: %s / ", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, argv[2], NULL)) return FALSE; } else if (strcmp (argv[0], "accept_focus") == 0) { if (argc != 3 || (g_ascii_strcasecmp (argv[2], "true") != 0 && g_ascii_strcasecmp (argv[2], "false") != 0)) BAD_COMMAND("usage: %s / [true|false]", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, argv[2], NULL)) return FALSE; } else if (strcmp (argv[0], "can_take_focus") == 0) { if (argc != 3 || (g_ascii_strcasecmp (argv[2], "true") != 0 && g_ascii_strcasecmp (argv[2], "false") != 0)) BAD_COMMAND("usage: %s / [true|false]", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, argv[2], NULL)) return FALSE; } else if (strcmp (argv[0], "accept_take_focus") == 0) { if (argc != 3 || (g_ascii_strcasecmp (argv[2], "true") != 0 && g_ascii_strcasecmp (argv[2], "false") != 0)) BAD_COMMAND("usage: %s / [true|false]", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, argv[2], NULL)) return FALSE; } else if (strcmp (argv[0], "show") == 0) { if (argc != 2) BAD_COMMAND("usage: %s /", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, NULL)) return FALSE; MetaWindow *window = test_client_find_window (client, window_id, error); if (!window) return FALSE; test_client_wait_for_window_shown (client, window); } else if (strcmp (argv[0], "hide") == 0 || strcmp (argv[0], "activate") == 0 || strcmp (argv[0], "raise") == 0 || strcmp (argv[0], "lower") == 0 || strcmp (argv[0], "minimize") == 0 || strcmp (argv[0], "unminimize") == 0 || strcmp (argv[0], "destroy") == 0) { if (argc != 2) BAD_COMMAND("usage: %s /", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; if (!test_client_do (client, error, argv[0], window_id, NULL)) return FALSE; } else if (strcmp (argv[0], "local_activate") == 0) { if (argc != 2) BAD_COMMAND("usage: %s /", argv[0]); TestClient *client; const char *window_id; if (!test_case_parse_window_id (test, argv[1], &client, &window_id, error)) return FALSE; MetaWindow *window = test_client_find_window (client, window_id, error); if (!window) return FALSE; meta_window_activate (window, 0); } else if (strcmp (argv[0], "wait") == 0) { if (argc != 1) BAD_COMMAND("usage: %s", argv[0]); if (!test_case_wait (test, error)) return FALSE; } else if (strcmp (argv[0], "dispatch") == 0) { if (argc != 1) BAD_COMMAND("usage: %s", argv[0]); if (!test_case_dispatch (test, error)) return FALSE; } else if (strcmp (argv[0], "sleep") == 0) { guint64 interval; if (argc != 2) BAD_COMMAND("usage: %s ", argv[0]); if (!g_ascii_string_to_unsigned (argv[1], 10, 0, G_MAXUINT32, &interval, error)) return FALSE; if (!test_case_sleep (test, (guint32) interval, error)) return FALSE; } else if (strcmp (argv[0], "assert_stacking") == 0) { if (!test_case_assert_stacking (test, argv + 1, argc - 1, error)) return FALSE; if (!test_case_check_xserver_stacking (test, error)) return FALSE; } else if (strcmp (argv[0], "assert_focused") == 0) { if (!test_case_assert_focused (test, argv[1], error)) return FALSE; } else { BAD_COMMAND("Unknown command %s", argv[0]); } return TRUE; } static gboolean test_case_destroy (TestCase *test, GError **error) { /* Failures when cleaning up the test case aren't recoverable, since we'll * pollute the subsequent test cases, so we just return the error, and * skip the rest of the cleanup. */ GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, test->clients); while (g_hash_table_iter_next (&iter, &key, &value)) { if (!test_client_do (value, error, "destroy_all", NULL)) return FALSE; } if (!test_case_wait (test, error)) return FALSE; if (!test_case_assert_stacking (test, NULL, 0, error)) return FALSE; g_hash_table_iter_init (&iter, test->clients); while (g_hash_table_iter_next (&iter, &key, &value)) test_client_destroy (value); async_waiter_destroy (test->waiter); meta_x11_display_set_alarm_filter (meta_get_display ()->x11_display, NULL, NULL); g_hash_table_destroy (test->clients); g_free (test); return TRUE; } /**********************************************************************/ static gboolean run_test (const char *filename, int index) { TestCase *test = test_case_new (); GError *error = NULL; GFile *file = g_file_new_for_path (filename); GDataInputStream *in = NULL; GFileInputStream *in_raw = g_file_read (file, NULL, &error); g_object_unref (file); if (in_raw == NULL) goto out; in = g_data_input_stream_new (G_INPUT_STREAM (in_raw)); g_object_unref (in_raw); int line_no = 0; while (error == NULL) { char *line = g_data_input_stream_read_line_utf8 (in, NULL, NULL, &error); if (line == NULL) break; line_no++; int argc; char **argv = NULL; if (!g_shell_parse_argv (line, &argc, &argv, &error)) { if (g_error_matches (error, G_SHELL_ERROR, G_SHELL_ERROR_EMPTY_STRING)) { g_clear_error (&error); goto next; } goto next; } test_case_do (test, argc, argv, &error); next: if (error) g_prefix_error (&error, "%d: ", line_no); g_free (line); g_strfreev (argv); } { GError *tmp_error = NULL; if (!g_input_stream_close (G_INPUT_STREAM (in), NULL, &tmp_error)) { if (error != NULL) g_clear_error (&tmp_error); else g_propagate_error (&error, tmp_error); } } out: if (in != NULL) g_object_unref (in); GError *cleanup_error = NULL; test_case_destroy (test, &cleanup_error); const char *testspos = strstr (filename, "tests/"); char *pretty_name; if (testspos) pretty_name = g_strdup (testspos + strlen("tests/")); else pretty_name = g_strdup (filename); if (error || cleanup_error) { g_print ("not ok %d %s\n", index, pretty_name); if (error) g_print (" %s\n", error->message); if (cleanup_error) { g_print (" Fatal Error During Cleanup\n"); g_print (" %s\n", cleanup_error->message); exit (1); } } else { g_print ("ok %d %s\n", index, pretty_name); } g_free (pretty_name); gboolean success = error == NULL; g_clear_error (&error); g_clear_error (&cleanup_error); return success; } typedef struct { int n_tests; char **tests; } RunTestsInfo; static gboolean run_tests (gpointer data) { RunTestsInfo *info = data; int i; gboolean success = TRUE; g_print ("1..%d\n", info->n_tests); for (i = 0; i < info->n_tests; i++) if (!run_test (info->tests[i], i + 1)) success = FALSE; meta_quit (success ? 0 : 1); return FALSE; } /**********************************************************************/ static gboolean find_metatests_in_directory (GFile *directory, GPtrArray *results, GError **error) { GFileEnumerator *enumerator = g_file_enumerate_children (directory, "standard::name,standard::type", G_FILE_QUERY_INFO_NONE, NULL, error); if (!enumerator) return FALSE; while (*error == NULL) { GFileInfo *info = g_file_enumerator_next_file (enumerator, NULL, error); if (info == NULL) break; GFile *child = g_file_enumerator_get_child (enumerator, info); switch (g_file_info_get_file_type (info)) { case G_FILE_TYPE_REGULAR: { const char *name = g_file_info_get_name (info); if (g_str_has_suffix (name, ".metatest")) g_ptr_array_add (results, g_file_get_path (child)); break; } case G_FILE_TYPE_DIRECTORY: find_metatests_in_directory (child, results, error); break; default: break; } g_object_unref (child); g_object_unref (info); } { GError *tmp_error = NULL; if (!g_file_enumerator_close (enumerator, NULL, &tmp_error)) { if (*error != NULL) g_clear_error (&tmp_error); else g_propagate_error (error, tmp_error); } } g_object_unref (enumerator); return *error == NULL; } static gboolean all_tests = FALSE; const GOptionEntry options[] = { { "all", 0, 0, G_OPTION_ARG_NONE, &all_tests, "Run all installed tests", NULL }, { NULL } }; int main (int argc, char **argv) { GOptionContext *ctx; GError *error = NULL; /* First parse the arguments that are passed to us */ ctx = g_option_context_new (NULL); g_option_context_add_main_entries (ctx, options, NULL); if (!g_option_context_parse (ctx, &argc, &argv, &error)) { g_printerr ("%s", error->message); return 1; } g_option_context_free (ctx); test_init (&argc, &argv); GPtrArray *tests = g_ptr_array_new (); if (all_tests) { GFile *test_dir = g_file_new_for_path (MUTTER_PKGDATADIR "/tests"); if (!find_metatests_in_directory (test_dir, tests, &error)) { g_printerr ("Error enumerating tests: %s\n", error->message); return 1; } } else { int i; char *curdir = g_get_current_dir (); for (i = 1; i < argc; i++) { if (g_path_is_absolute (argv[i])) g_ptr_array_add (tests, g_strdup (argv[i])); else g_ptr_array_add (tests, g_build_filename (curdir, argv[i], NULL)); } g_free (curdir); } /* Then initalize mutter with a different set of arguments */ char *fake_args[] = { NULL, (char *)"--wayland", (char *)"--nested" }; fake_args[0] = argv[0]; char **fake_argv = fake_args; int fake_argc = G_N_ELEMENTS (fake_args); ctx = meta_get_option_context (); if (!g_option_context_parse (ctx, &fake_argc, &fake_argv, &error)) { g_printerr ("mutter: %s\n", error->message); exit (1); } g_option_context_free (ctx); meta_plugin_manager_load (test_get_plugin_name ()); meta_init (); meta_register_with_session (); RunTestsInfo info; info.tests = (char **)tests->pdata; info.n_tests = tests->len; g_idle_add (run_tests, &info); return meta_run (); } muffin-6.4.1/src/tests/test-client.c0000664000175000017500000005045014723361714016276 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include #include #include #include #include const char *client_id = "0"; static gboolean wayland; GHashTable *windows; GQuark event_source_quark; GQuark event_handlers_quark; GQuark can_take_focus_quark; typedef void (*XEventHandler) (GtkWidget *window, XEvent *event); static void read_next_line (GDataInputStream *in); static void window_export_handle_cb (GdkWindow *window, const char *handle_str, gpointer user_data) { GdkWindow *gdk_window = gtk_widget_get_window (GTK_WIDGET (user_data)); if (!gdk_wayland_window_set_transient_for_exported (gdk_window, (gchar *) handle_str)) g_print ("Fail to set transient_for exported window handle %s", handle_str); gdk_window_set_modal_hint (gdk_window, TRUE); } static GtkWidget * lookup_window (const char *window_id) { GtkWidget *window = g_hash_table_lookup (windows, window_id); if (!window) g_print ("Window %s doesn't exist", window_id); return window; } typedef struct { GSource base; GSource **self_ref; GPollFD event_poll_fd; Display *xdisplay; } XClientEventSource; static gboolean x_event_source_prepare (GSource *source, int *timeout) { XClientEventSource *x_source = (XClientEventSource *) source; *timeout = -1; return XPending (x_source->xdisplay); } static gboolean x_event_source_check (GSource *source) { XClientEventSource *x_source = (XClientEventSource *) source; return XPending (x_source->xdisplay); } static gboolean x_event_source_dispatch (GSource *source, GSourceFunc callback, gpointer user_data) { XClientEventSource *x_source = (XClientEventSource *) source; while (XPending (x_source->xdisplay)) { GHashTableIter iter; XEvent event; gpointer value; XNextEvent (x_source->xdisplay, &event); g_hash_table_iter_init (&iter, windows); while (g_hash_table_iter_next (&iter, NULL, &value)) { GList *l; GtkWidget *window = value; GList *handlers = g_object_get_qdata (G_OBJECT (window), event_handlers_quark); for (l = handlers; l; l = l->next) { XEventHandler handler = l->data; handler (window, &event); } } } return TRUE; } static void x_event_source_finalize (GSource *source) { XClientEventSource *x_source = (XClientEventSource *) source; *x_source->self_ref = NULL; } static GSourceFuncs x_event_funcs = { x_event_source_prepare, x_event_source_check, x_event_source_dispatch, x_event_source_finalize, }; static GSource* ensure_xsource_handler (GdkDisplay *gdkdisplay) { static GSource *source = NULL; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdkdisplay); XClientEventSource *x_source; if (source) return g_source_ref (source); source = g_source_new (&x_event_funcs, sizeof (XClientEventSource)); x_source = (XClientEventSource *) source; x_source->self_ref = &source; x_source->xdisplay = xdisplay; x_source->event_poll_fd.fd = ConnectionNumber (xdisplay); x_source->event_poll_fd.events = G_IO_IN; g_source_add_poll (source, &x_source->event_poll_fd); g_source_set_priority (source, GDK_PRIORITY_EVENTS - 1); g_source_set_can_recurse (source, TRUE); g_source_attach (source, NULL); return source; } static gboolean window_has_x11_event_handler (GtkWidget *window, XEventHandler handler) { GList *handlers = g_object_get_qdata (G_OBJECT (window), event_handlers_quark); g_return_val_if_fail (handler, FALSE); g_return_val_if_fail (!wayland, FALSE); return g_list_find (handlers, handler) != NULL; } static void unref_and_maybe_destroy_gsource (GSource *source) { g_source_unref (source); if (source->ref_count == 1) g_source_destroy (source); } static void window_add_x11_event_handler (GtkWidget *window, XEventHandler handler) { GSource *source; GList *handlers = g_object_get_qdata (G_OBJECT (window), event_handlers_quark); g_return_if_fail (!window_has_x11_event_handler (window, handler)); source = ensure_xsource_handler (gtk_widget_get_display (window)); g_object_set_qdata_full (G_OBJECT (window), event_source_quark, source, (GDestroyNotify) unref_and_maybe_destroy_gsource); handlers = g_list_append (handlers, handler); g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers); } static void window_remove_x11_event_handler (GtkWidget *window, XEventHandler handler) { GList *handlers = g_object_get_qdata (G_OBJECT (window), event_handlers_quark); g_return_if_fail (window_has_x11_event_handler (window, handler)); g_object_set_qdata (G_OBJECT (window), event_source_quark, NULL); handlers = g_list_remove (handlers, handler); g_object_set_qdata (G_OBJECT (window), event_handlers_quark, handlers); } static void handle_take_focus (GtkWidget *window, XEvent *xevent) { GdkWindow *gdkwindow = gtk_widget_get_window (window); GdkDisplay *display = gtk_widget_get_display (window); Atom wm_protocols = gdk_x11_get_xatom_by_name_for_display (display, "WM_PROTOCOLS"); Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); if (xevent->xany.type != ClientMessage || xevent->xany.window != GDK_WINDOW_XID (gdkwindow)) return; if (xevent->xclient.message_type == wm_protocols && xevent->xclient.data.l[0] == wm_take_focus) { XSetInputFocus (xevent->xany.display, GDK_WINDOW_XID (gdkwindow), RevertToParent, xevent->xclient.data.l[1]); } } static void process_line (const char *line) { GError *error = NULL; int argc; char **argv; if (!g_shell_parse_argv (line, &argc, &argv, &error)) { g_print ("error parsing command: %s", error->message); g_error_free (error); return; } if (argc < 1) { g_print ("Empty command"); goto out; } if (strcmp (argv[0], "create") == 0) { int i; if (argc < 2) { g_print ("usage: create [override|csd]"); goto out; } if (g_hash_table_lookup (windows, argv[1])) { g_print ("window %s already exists", argv[1]); goto out; } gboolean override = FALSE; gboolean csd = FALSE; for (i = 2; i < argc; i++) { if (strcmp (argv[i], "override") == 0) override = TRUE; if (strcmp (argv[i], "csd") == 0) csd = TRUE; } if (override && csd) { g_print ("override and csd keywords are exclusie"); goto out; } GtkWidget *window = gtk_window_new (override ? GTK_WINDOW_POPUP : GTK_WINDOW_TOPLEVEL); g_hash_table_insert (windows, g_strdup (argv[1]), window); if (csd) { GtkWidget *headerbar = gtk_header_bar_new (); gtk_window_set_titlebar (GTK_WINDOW (window), headerbar); gtk_widget_show (headerbar); } gtk_window_set_default_size (GTK_WINDOW (window), 100, 100); gchar *title = g_strdup_printf ("test/%s/%s", client_id, argv[1]); gtk_window_set_title (GTK_WINDOW (window), title); g_free (title); g_object_set_qdata (G_OBJECT (window), can_take_focus_quark, GUINT_TO_POINTER (TRUE)); gtk_widget_realize (window); if (!wayland) { /* The cairo xlib backend creates a window when initialized, which * confuses our testing if it happens asynchronously the first * time a window is painted. By creating an Xlib surface and * destroying it, we force initialization at a more predictable time. */ GdkWindow *window_gdk = gtk_widget_get_window (window); cairo_surface_t *surface = gdk_window_create_similar_surface (window_gdk, CAIRO_CONTENT_COLOR, 1, 1); cairo_surface_destroy (surface); } } else if (strcmp (argv[0], "set_parent") == 0) { if (argc != 3) { g_print ("usage: set_parent "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) { g_print ("unknown window %s", argv[1]); goto out; } GtkWidget *parent_window = lookup_window (argv[2]); if (!parent_window) { g_print ("unknown parent window %s", argv[2]); goto out; } gtk_window_set_transient_for (GTK_WINDOW (window), GTK_WINDOW (parent_window)); } else if (strcmp (argv[0], "set_parent_exported") == 0) { if (argc != 3) { g_print ("usage: set_parent_exported "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) { g_print ("unknown window %s", argv[1]); goto out; } GtkWidget *parent_window = lookup_window (argv[2]); if (!parent_window) { g_print ("unknown parent window %s", argv[2]); goto out; } GdkWindow *parent_gdk_window = gtk_widget_get_window (parent_window); if (!gdk_wayland_window_export_handle (parent_gdk_window, window_export_handle_cb, window, NULL)) g_print ("Fail to export handle for window id %s", argv[2]); } else if (strcmp (argv[0], "accept_focus") == 0) { if (argc != 3) { g_print ("usage: %s [true|false]", argv[0]); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) { g_print ("unknown window %s", argv[1]); goto out; } if (!wayland && window_has_x11_event_handler (window, handle_take_focus)) { g_print ("Impossible to use %s for windows accepting take focus", argv[1]); goto out; } gboolean enabled = g_ascii_strcasecmp (argv[2], "true") == 0; gtk_window_set_accept_focus (GTK_WINDOW (window), enabled); } else if (strcmp (argv[0], "can_take_focus") == 0) { if (argc != 3) { g_print ("usage: %s [true|false]", argv[0]); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) { g_print ("unknown window %s", argv[1]); goto out; } if (wayland) { g_print ("%s not supported under wayland", argv[0]); goto out; } if (window_has_x11_event_handler (window, handle_take_focus)) { g_print ("Impossible to change %s for windows accepting take focus", argv[1]); goto out; } GdkDisplay *display = gdk_display_get_default (); GdkWindow *gdkwindow = gtk_widget_get_window (window); Display *xdisplay = gdk_x11_display_get_xdisplay (display); Window xwindow = GDK_WINDOW_XID (gdkwindow); Atom wm_take_focus = gdk_x11_get_xatom_by_name_for_display (display, "WM_TAKE_FOCUS"); gboolean add = g_ascii_strcasecmp(argv[2], "true") == 0; Atom *protocols = NULL; Atom *new_protocols; int n_protocols = 0; int i, n = 0; gdk_display_sync (display); XGetWMProtocols (xdisplay, xwindow, &protocols, &n_protocols); new_protocols = g_new0 (Atom, n_protocols + (add ? 1 : 0)); for (i = 0; i < n_protocols; ++i) { if (protocols[i] != wm_take_focus) new_protocols[n++] = protocols[i]; } if (add) new_protocols[n++] = wm_take_focus; XSetWMProtocols (xdisplay, xwindow, new_protocols, n); g_object_set_qdata (G_OBJECT (window), can_take_focus_quark, GUINT_TO_POINTER (add)); XFree (new_protocols); XFree (protocols); } else if (strcmp (argv[0], "accept_take_focus") == 0) { if (argc != 3) { g_print ("usage: %s [true|false]", argv[0]); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) { g_print ("unknown window %s", argv[1]); goto out; } if (wayland) { g_print ("%s not supported under wayland", argv[0]); goto out; } if (gtk_window_get_accept_focus (GTK_WINDOW (window))) { g_print ("%s not supported for input windows", argv[0]); goto out; } if (!g_object_get_qdata (G_OBJECT (window), can_take_focus_quark)) { g_print ("%s not supported for windows with no WM_TAKE_FOCUS set", argv[0]); goto out; } if (g_ascii_strcasecmp (argv[2], "true") == 0) window_add_x11_event_handler (window, handle_take_focus); else window_remove_x11_event_handler (window, handle_take_focus); } else if (strcmp (argv[0], "show") == 0) { if (argc != 2) { g_print ("usage: show "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gtk_widget_show (window); gdk_display_sync (gdk_display_get_default ()); } else if (strcmp (argv[0], "hide") == 0) { if (argc != 2) { g_print ("usage: hide "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gtk_widget_hide (window); } else if (strcmp (argv[0], "activate") == 0) { if (argc != 2) { g_print ("usage: activate "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gtk_window_present (GTK_WINDOW (window)); } else if (strcmp (argv[0], "resize") == 0) { if (argc != 4) { g_print ("usage: resize "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; int width = atoi (argv[2]); int height = atoi (argv[3]); gtk_window_resize (GTK_WINDOW (window), width, height); } else if (strcmp (argv[0], "raise") == 0) { if (argc != 2) { g_print ("usage: raise "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gdk_window_raise (gtk_widget_get_window (window)); } else if (strcmp (argv[0], "lower") == 0) { if (argc != 2) { g_print ("usage: lower "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gdk_window_lower (gtk_widget_get_window (window)); } else if (strcmp (argv[0], "destroy") == 0) { if (argc != 2) { g_print ("usage: destroy "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; g_hash_table_remove (windows, argv[1]); gtk_widget_destroy (window); } else if (strcmp (argv[0], "destroy_all") == 0) { if (argc != 1) { g_print ("usage: destroy_all"); goto out; } GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, windows); while (g_hash_table_iter_next (&iter, &key, &value)) gtk_widget_destroy (value); g_hash_table_remove_all (windows); } else if (strcmp (argv[0], "sync") == 0) { if (argc != 1) { g_print ("usage: sync"); goto out; } gdk_display_sync (gdk_display_get_default ()); } else if (strcmp (argv[0], "set_counter") == 0) { XSyncCounter counter; int value; if (argc != 3) { g_print ("usage: set_counter "); goto out; } if (wayland) { g_print ("usage: set_counter can only be used for X11"); goto out; } counter = strtoul(argv[1], NULL, 10); value = atoi(argv[2]); XSyncValue sync_value; XSyncIntToValue (&sync_value, value); XSyncSetCounter (gdk_x11_display_get_xdisplay (gdk_display_get_default ()), counter, sync_value); } else if (strcmp (argv[0], "minimize") == 0) { if (argc != 2) { g_print ("usage: minimize "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gtk_window_iconify (GTK_WINDOW (window)); } else if (strcmp (argv[0], "unminimize") == 0) { if (argc != 2) { g_print ("usage: unminimize "); goto out; } GtkWidget *window = lookup_window (argv[1]); if (!window) goto out; gtk_window_deiconify (GTK_WINDOW (window)); } else { g_print ("Unknown command %s", argv[0]); goto out; } g_print ("OK\n"); out: g_strfreev (argv); } static void on_line_received (GObject *source, GAsyncResult *result, gpointer user_data) { GDataInputStream *in = G_DATA_INPUT_STREAM (source); GError *error = NULL; gsize length; char *line = g_data_input_stream_read_line_finish_utf8 (in, result, &length, &error); if (line == NULL) { if (error != NULL) g_printerr ("Error reading from stdin: %s\n", error->message); gtk_main_quit (); return; } process_line (line); g_free (line); read_next_line (in); } static void read_next_line (GDataInputStream *in) { g_data_input_stream_read_line_async (in, G_PRIORITY_DEFAULT, NULL, on_line_received, NULL); } const GOptionEntry options[] = { { "wayland", 0, 0, G_OPTION_ARG_NONE, &wayland, "Create a wayland client, not an X11 one", NULL }, { "client-id", 0, 0, G_OPTION_ARG_STRING, &client_id, "Identifier used in Window titles for this client", "CLIENT_ID", }, { NULL } }; int main(int argc, char **argv) { GOptionContext *context = g_option_context_new (NULL); GError *error = NULL; g_option_context_add_main_entries (context, options, NULL); if (!g_option_context_parse (context, &argc, &argv, &error)) { g_printerr ("%s", error->message); return 1; } if (wayland) gdk_set_allowed_backends ("wayland"); else gdk_set_allowed_backends ("x11"); gtk_init (NULL, NULL); windows = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL); event_source_quark = g_quark_from_static_string ("event-source"); event_handlers_quark = g_quark_from_static_string ("event-handlers"); can_take_focus_quark = g_quark_from_static_string ("can-take-focus"); GInputStream *raw_in = g_unix_input_stream_new (0, FALSE); GDataInputStream *in = g_data_input_stream_new (raw_in); read_next_line (in); gtk_main (); return 0; } muffin-6.4.1/src/tests/README0000664000175000017500000000626714723361714014566 0ustar fabiofabioThis directory implements a framework for automated tests of Mutter. The basic idea is that mutter-test-runner acts as the window manager and compositor, and forks off instances of mutter-test-client to act as clients. There's a simple scripting language for tests. A very small test would look like: --- # Start up a new X11 client with the client id 1 (doesn't have to be an integer) # Windows for this client will be referred to as 1/ new_client 1 x11 # Create and show two windows - again the IDs don't have to be integers create 1/1 show 1/1 create 1/2 show 1/2 # Wait for the commands we've executed in the clients to reach Mutter wait # Check that the windows are in the order we expect assert_stacking 1/1 1/2 --- Running ======= The tests are installed according to: https://wiki.gnome.org/Initiatives/GnomeGoals/InstalledTests if -Dtests=true is passed to `meson configure`. You can run them uninstalled with: ninja test Command reference ================= The following commands are supported. Quoting and comments follow shell rules. new_client [wayland|x11] Starts a client, connecting by either Wayland or X11. The client will subsequently be known with the given client-id (an arbitrary string) quit_client Destroys all windows for the client, waits for that to be processed, then instructs the client to exit. create / [override|csd] Creates a new window. For the X11 backend, the keyword 'override' can be given to create an override-redirect and the keyword 'csd' can be given to create a client-side decorated window. show / hide / Ask the client to show (map) or hide (unmap) the given window activate / Ask the client to raise and focus the given window. This is currently a no-op for Wayland, where this capability is not supported in the protocol. local_activate - The same as 'activate', but the operation is done directly inside Mutter and works for both backends raise / lower / Ask the client to raise or lower the given window ID. This is a no-op for Wayland clients. (It's also considered discouraged, but supported, for non-override-redirect X11 clients.) minimize / unminimize / Ask the client to minimize or unminimize the given window ID. This older term for this operation is "iconify". destroy / Destroy the given window wait Wait until all requests sent by Mutter to clients have been received by Mutter, and then wait until all requests by Mutter have been processed by the X server. assert_stacking / / ... Assert that the list of client windows known to Mutter is as given and in the given order, bottom to top. The character '|' can be present in the list of windows to indicate the guard window that separates hidden and visible windows. If '|' isn't present, the guard window is asserted to be below all client windows. This function also queries the X server stack and verifies that Mutter's expectation of the X server stack matches reality. muffin-6.4.1/src/tests/monitor-config-migration-unit-tests.c0000664000175000017500000001131314723361714023074 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/monitor-config-migration-unit-tests.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-monitor-config-migration.h" #include "tests/monitor-test-utils.h" static void test_migration (const char *old_config, const char *new_config) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); GError *error = NULL; const char *old_config_path; g_autoptr (GFile) old_config_file = NULL; g_autofree char *migrated_path = NULL; const char *expected_path; g_autofree char *migrated_data = NULL; g_autofree char *expected_data = NULL; g_autoptr (GFile) migrated_file = NULL; migrated_path = g_build_filename (g_get_tmp_dir (), "test-migrated-monitors.xml", NULL); if (!meta_monitor_config_store_set_custom (config_store, "/dev/null", migrated_path, &error)) g_error ("Failed to set custom config store: %s", error->message); old_config_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", old_config, NULL); old_config_file = g_file_new_for_path (old_config_path); if (!meta_migrate_old_monitors_config (config_store, old_config_file, &error)) g_error ("Failed to migrate config: %s", error->message); expected_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", new_config, NULL); expected_data = read_file (expected_path); migrated_data = read_file (migrated_path); g_assert_nonnull (expected_data); g_assert_nonnull (migrated_data); g_assert_cmpstr (expected_data, ==, migrated_data); migrated_file = g_file_new_for_path (migrated_path); if (!g_file_delete (migrated_file, NULL, &error)) g_error ("Failed to remove test data output file: %s", error->message); } static void meta_test_monitor_config_migration_basic (void) { test_migration ("basic-old.xml", "basic-new.xml"); } static void meta_test_monitor_config_migration_rotated (void) { test_migration ("rotated-old.xml", "rotated-new.xml"); } static void meta_test_monitor_config_migration_tiled (void) { test_migration ("tiled-old.xml", "tiled-new.xml"); } static void meta_test_monitor_config_migration_first_rotated (void) { test_migration ("first-rotated-old.xml", "first-rotated-new.xml"); } static void meta_test_monitor_config_migration_oneoff (void) { test_migration ("oneoff-old.xml", "oneoff-new.xml"); } static void meta_test_monitor_config_migration_wiggle (void) { test_migration ("wiggle-old.xml", "wiggle-new.xml"); } void init_monitor_config_migration_tests (void) { g_test_add_func ("/backends/monitor-config-migration/basic", meta_test_monitor_config_migration_basic); g_test_add_func ("/backends/monitor-config-migration/rotated", meta_test_monitor_config_migration_rotated); g_test_add_func ("/backends/monitor-config-migration/tiled", meta_test_monitor_config_migration_tiled); g_test_add_func ("/backends/monitor-config-migration/first-rotated", meta_test_monitor_config_migration_first_rotated); g_test_add_func ("/backends/monitor-config-migration/oneoff", meta_test_monitor_config_migration_oneoff); g_test_add_func ("/backends/monitor-config-migration/wiggle", meta_test_monitor_config_migration_wiggle); } muffin-6.4.1/src/tests/boxes-tests.h0000664000175000017500000000143314723361714016325 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef BOXES_TESTS_H #define BOXES_TESTS_H void init_boxes_tests (void); #endif /* BOXES_TESTS_H */ muffin-6.4.1/src/tests/monitor-transform-tests.h0000664000175000017500000000152514723361714020707 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MONITOR_TRANSFORM_UNIT_TESTS_H #define MONITOR_TRANSFORM_UNIT_TESTS_H void init_monitor_transform_tests (void); #endif /* MONITOR_TRANSFORM_TESTS_H */ muffin-6.4.1/src/tests/unit-tests.c0000664000175000017500000001763514723361714016172 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include #include "compositor/meta-plugin-manager.h" #include "core/boxes-private.h" #include "core/main-private.h" #include "tests/boxes-tests.h" #include "tests/meta-backend-test.h" #include "tests/monitor-config-migration-unit-tests.h" #include "tests/monitor-unit-tests.h" #include "tests/monitor-store-unit-tests.h" #include "tests/monitor-transform-tests.h" #include "tests/test-utils.h" #include "tests/wayland-unit-tests.h" #include "wayland/meta-wayland.h" typedef struct _MetaTestLaterOrderCallbackData { GMainLoop *loop; /* Loop to terminate when done. */ int callback_num; /* Callback number integer. */ int *expected_callback_num; /* Pointer to the expected callback number. */ } MetaTestLaterOrderCallbackData; static gboolean test_later_order_callback (gpointer user_data) { MetaTestLaterOrderCallbackData *data = user_data; g_assert_cmpint (data->callback_num, ==, *data->expected_callback_num); if (*data->expected_callback_num == 0) g_main_loop_quit (data->loop); else (*data->expected_callback_num)--; return FALSE; } static void meta_test_util_later_order (void) { GMainLoop *loop; int expected_callback_num; int i; const int num_callbacks = 3; MetaTestLaterOrderCallbackData callback_data[num_callbacks]; loop = g_main_loop_new (NULL, FALSE); /* Schedule three BEFORE_DRAW callbacks each with its own number associated * with it. */ for (i = 0; i < num_callbacks; i++) { callback_data[i] = (MetaTestLaterOrderCallbackData) { .loop = loop, .callback_num = i, .expected_callback_num = &expected_callback_num, }; meta_later_add (META_LATER_BEFORE_REDRAW, test_later_order_callback, &callback_data[i], NULL); } /* Check that the callbacks are invoked in the opposite order that they were * scheduled. Each callback will decrease the number by 1 after it checks the * validity. */ expected_callback_num = num_callbacks - 1; g_main_loop_run (loop); g_assert_cmpint (expected_callback_num, ==, 0); g_main_loop_unref (loop); } typedef enum _MetaTestLaterScheduleFromLaterState { META_TEST_LATER_EXPECT_CALC_SHOWING, META_TEST_LATER_EXPECT_SYNC_STACK, META_TEST_LATER_EXPECT_BEFORE_REDRAW, META_TEST_LATER_FINISHED, } MetaTestLaterScheduleFromLaterState; typedef struct _MetaTestLaterScheduleFromLaterData { GMainLoop *loop; MetaTestLaterScheduleFromLaterState state; } MetaTestLaterScheduleFromLaterData; static gboolean test_later_schedule_from_later_sync_stack_callback (gpointer user_data); static gboolean test_later_schedule_from_later_calc_showing_callback (gpointer user_data) { MetaTestLaterScheduleFromLaterData *data = user_data; g_assert_cmpint (data->state, ==, META_TEST_LATER_EXPECT_CALC_SHOWING); meta_later_add (META_LATER_SYNC_STACK, test_later_schedule_from_later_sync_stack_callback, data, NULL); data->state = META_TEST_LATER_EXPECT_SYNC_STACK; return FALSE; } static gboolean test_later_schedule_from_later_sync_stack_callback (gpointer user_data) { MetaTestLaterScheduleFromLaterData *data = user_data; g_assert_cmpint (data->state, ==, META_TEST_LATER_EXPECT_SYNC_STACK); data->state = META_TEST_LATER_EXPECT_BEFORE_REDRAW; return FALSE; } static gboolean test_later_schedule_from_later_before_redraw_callback (gpointer user_data) { MetaTestLaterScheduleFromLaterData *data = user_data; g_assert_cmpint (data->state, ==, META_TEST_LATER_EXPECT_BEFORE_REDRAW); data->state = META_TEST_LATER_FINISHED; g_main_loop_quit (data->loop); return FALSE; } static void meta_test_util_later_schedule_from_later (void) { MetaTestLaterScheduleFromLaterData data; data.loop = g_main_loop_new (NULL, FALSE); /* Test that scheduling a MetaLater with 'when' being later than the one being * invoked causes it to be invoked before any callback with a later 'when' * value being invoked. * * The first and last callback is queued here. The one to be invoked in * between is invoked in test_later_schedule_from_later_calc_showing_callback. */ meta_later_add (META_LATER_CALC_SHOWING, test_later_schedule_from_later_calc_showing_callback, &data, NULL); meta_later_add (META_LATER_BEFORE_REDRAW, test_later_schedule_from_later_before_redraw_callback, &data, NULL); data.state = META_TEST_LATER_EXPECT_CALC_SHOWING; g_main_loop_run (data.loop); g_main_loop_unref (data.loop); g_assert_cmpint (data.state, ==, META_TEST_LATER_FINISHED); } static void meta_test_adjacent_to (void) { MetaRectangle base = { .x = 10, .y = 10, .width = 10, .height = 10 }; MetaRectangle adjacent[] = { { .x = 20, .y = 10, .width = 10, .height = 10 }, { .x = 0, .y = 10, .width = 10, .height = 10 }, { .x = 0, .y = 1, .width = 10, .height = 10 }, { .x = 20, .y = 19, .width = 10, .height = 10 }, { .x = 10, .y = 20, .width = 10, .height = 10 }, { .x = 10, .y = 0, .width = 10, .height = 10 }, }; MetaRectangle not_adjacent[] = { { .x = 0, .y = 0, .width = 10, .height = 10 }, { .x = 20, .y = 20, .width = 10, .height = 10 }, { .x = 21, .y = 10, .width = 10, .height = 10 }, { .x = 10, .y = 21, .width = 10, .height = 10 }, { .x = 10, .y = 5, .width = 10, .height = 10 }, { .x = 11, .y = 10, .width = 10, .height = 10 }, { .x = 19, .y = 10, .width = 10, .height = 10 }, }; unsigned int i; for (i = 0; i < G_N_ELEMENTS (adjacent); i++) g_assert (meta_rectangle_is_adjacent_to (&base, &adjacent[i])); for (i = 0; i < G_N_ELEMENTS (not_adjacent); i++) g_assert (!meta_rectangle_is_adjacent_to (&base, ¬_adjacent[i])); } static gboolean run_tests (gpointer data) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); gboolean ret; meta_settings_override_experimental_features (settings); meta_settings_enable_experimental_feature ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); pre_run_monitor_tests (); pre_run_wayland_tests (); ret = g_test_run (); finish_monitor_tests (); meta_quit (ret != 0); return FALSE; } static void init_tests (int argc, char **argv) { g_test_add_func ("/util/meta-later/order", meta_test_util_later_order); g_test_add_func ("/util/meta-later/schedule-from-later", meta_test_util_later_schedule_from_later); g_test_add_func ("/core/boxes/adjacent-to", meta_test_adjacent_to); init_monitor_store_tests (); init_monitor_config_migration_tests (); init_monitor_tests (); init_boxes_tests (); init_wayland_tests (); init_monitor_transform_tests (); } int main (int argc, char *argv[]) { test_init (&argc, &argv); init_tests (argc, argv); meta_plugin_manager_load (test_get_plugin_name ()); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_TEST); meta_init (); meta_register_with_session (); g_idle_add (run_tests, NULL); return meta_run (); } muffin-6.4.1/src/tests/migration/0000775000175000017500000000000014723361714015664 5ustar fabiofabiomuffin-6.4.1/src/tests/migration/wiggle-new-finished.xml0000664000175000017500000000124314723361714022242 0ustar fabiofabio 0 0 1 yes left no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000801086425781 muffin-6.4.1/src/tests/migration/oneoff-old.xml0000664000175000017500000000131114723361714020432 0ustar fabiofabio no MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 0 0 normal no no yes no no MetaProduct's Inc. MetaMonitor 0x654321 muffin-6.4.1/src/tests/migration/tiled-old.xml0000664000175000017500000000174514723361714020272 0ustar fabiofabio no DEL DELL P2415Q GTTPW67P0WFB 1920 2160 60 0 0 normal no no yes no no DEL DELL P2415Q GTTPW67P0WFB 1920 2160 60 1920 0 normal no no yes no no muffin-6.4.1/src/tests/migration/oneoff-new-finished.xml0000664000175000017500000000142414723361714022241 0ustar fabiofabio 0 0 1 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 DP-2 MetaProduct's Inc. MetaMonitor 0x654321 muffin-6.4.1/src/tests/migration/rotated-new.xml0000664000175000017500000000121514723361714020636 0ustar fabiofabio 0 0 yes right no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 muffin-6.4.1/src/tests/migration/first-rotated-old.xml0000664000175000017500000000177614723361714021764 0ustar fabiofabio no DEL DELL P2415Q GTTPW67P0WFB 2160 3840 29.981103897094727 0 0 left no no no no no AUO 0x123d 0x00000000 1920 1080 60.049972534179688 2160 0 normal no no yes no no muffin-6.4.1/src/tests/migration/first-rotated-new.xml0000664000175000017500000000210114723361714021756 0ustar fabiofabio 0 0 left no HDMI-1 DEL DELL P2415Q GTTPW67P0WFB 3840 2160 29.981103897094727 2160 0 yes eDP-1 AUO 0x123d 0x00000000 1920 1080 60.049972534179688 muffin-6.4.1/src/tests/migration/basic-old.xml0000664000175000017500000000370514723361714020250 0ustar fabiofabio no DEL DELL P2415Q GTTPW67P0WFB 3840 2160 29.981103897094727 0 0 normal no no no no no AUO 0x123d 0x00000000 1920 1080 60.049972534179688 3840 0 normal no no yes no no no ACI VX239 ECLMRS004144 1920 1080 60 1920 0 normal no no no no no AUO 0x123d 0x00000000 1920 1080 60.049468994140625 0 0 normal no no yes no no muffin-6.4.1/src/tests/migration/rotated-new-finished.xml0000664000175000017500000000122414723361714022425 0ustar fabiofabio 0 0 1 yes right no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 muffin-6.4.1/src/tests/migration/wiggle-new.xml0000664000175000017500000000123414723361714020453 0ustar fabiofabio 0 0 yes left no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000801086425781 muffin-6.4.1/src/tests/migration/wiggle-new-discarded.xml0000664000175000017500000000004314723361714022370 0ustar fabiofabio muffin-6.4.1/src/tests/migration/wiggle-old.xml0000664000175000017500000000107414723361714020442 0ustar fabiofabio no MetaProduct's Inc. MetaMonitor 0x123456 600 800 60.000801086425781 0 0 left no no yes no no muffin-6.4.1/src/tests/migration/oneoff-new.xml0000664000175000017500000000141514723361714020452 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 DP-2 MetaProduct's Inc. MetaMonitor 0x654321 muffin-6.4.1/src/tests/migration/basic-new.xml0000664000175000017500000000357514723361714020270 0ustar fabiofabio 0 0 HDMI-1 DEL DELL P2415Q GTTPW67P0WFB 3840 2160 29.981103897094727 3840 0 yes eDP-1 AUO 0x123d 0x00000000 1920 1080 60.049972534179688 1920 0 DP-2 ACI VX239 ECLMRS004144 1920 1080 60 0 0 yes eDP-1 AUO 0x123d 0x00000000 1920 1080 60.049468994140625 muffin-6.4.1/src/tests/migration/tiled-new.xml0000664000175000017500000000103114723361714020271 0ustar fabiofabio 0 0 yes DP-1 DEL DELL P2415Q GTTPW67P0WFB 3840 2160 60 muffin-6.4.1/src/tests/migration/rotated-old.xml0000664000175000017500000000105514723361714020625 0ustar fabiofabio no MetaProduct's Inc. MetaMonitor 0x123456 600 800 60 0 0 right no no yes no no muffin-6.4.1/src/tests/test-utils.h0000664000175000017500000000542714723361714016171 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef TEST_UTILS_H #define TEST_UTILS_H #include #include #include #include "meta/window.h" #define TEST_RUNNER_ERROR test_runner_error_quark () typedef enum { TEST_RUNNER_ERROR_BAD_COMMAND, TEST_RUNNER_ERROR_RUNTIME_ERROR, TEST_RUNNER_ERROR_ASSERTION_FAILED } TestRunnerError; GQuark test_runner_error_quark (void); typedef struct _AsyncWaiter AsyncWaiter; typedef struct _TestClient TestClient; void test_init (int *argc, char ***argv); gboolean async_waiter_alarm_filter (MetaX11Display *display, XSyncAlarmNotifyEvent *event, gpointer data); void async_waiter_set_and_wait (AsyncWaiter *waiter); AsyncWaiter * async_waiter_new (void); void async_waiter_destroy (AsyncWaiter *waiter); char * test_client_get_id (TestClient *client); gboolean test_client_alarm_filter (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data); gboolean test_client_wait (TestClient *client, GError **error); gboolean test_client_do (TestClient *client, GError **error, ...) G_GNUC_NULL_TERMINATED; MetaWindow * test_client_find_window (TestClient *client, const char *window_id, GError **error); void test_client_wait_for_window_shown (TestClient *client, MetaWindow *window); gboolean test_client_quit (TestClient *client, GError **error); TestClient * test_client_new (const char *id, MetaWindowClientType type, GError **error); void test_client_destroy (TestClient *client); const char * test_get_plugin_name (void); void test_wait_for_x11_display (void); #endif /* TEST_UTILS_H */ muffin-6.4.1/src/tests/monitor-store-unit-tests.h0000664000175000017500000000161614723361714021006 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MONITOR_STORE_UNIT_TESTS_H #define MONITOR_STORE_UNIT_TESTS_H void init_monitor_store_tests (void); #endif /* MONITOR_STORE_UNIT_TESTS_H */ muffin-6.4.1/src/tests/meta-gpu-test.c0000664000175000017500000000271714723361714016542 0ustar fabiofabio/* * Copyright (C) 2016-2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/meta-gpu-test.h" #include "backends/meta-backend-private.h" #include "tests/meta-monitor-manager-test.h" struct _MetaGpuTest { MetaGpu parent; }; G_DEFINE_TYPE (MetaGpuTest, meta_gpu_test, META_TYPE_GPU) static gboolean meta_gpu_test_read_current (MetaGpu *gpu, GError **error) { MetaBackend *backend = meta_gpu_get_backend (gpu); MetaMonitorManager *manager = meta_backend_get_monitor_manager (backend); meta_monitor_manager_test_read_current (manager); return TRUE; } static void meta_gpu_test_init (MetaGpuTest *gpu_test) { } static void meta_gpu_test_class_init (MetaGpuTestClass *klass) { MetaGpuClass *gpu_class = META_GPU_CLASS (klass); gpu_class->read_current = meta_gpu_test_read_current; } muffin-6.4.1/src/tests/clutter/0000775000175000017500000000000014723361714015355 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/meson.build0000664000175000017500000000024214723361714017515 0ustar fabiofabioclutter_tests_includepath = include_directories('.') subdir('accessibility') subdir('conform') subdir('interactive') subdir('micro-bench') subdir('performance') muffin-6.4.1/src/tests/clutter/README0000664000175000017500000000344714723361714016245 0ustar fabiofabioOutline of test categories: The conform/ tests should be non-interactive unit-tests that verify a single feature is behaving as documented. Use the GLib and Clutter test API and macros to write the test units. The conformance test suites are meant to be used with continuous integration builds. The performance/ tests are performance tests, both focused tests testing single metrics and larger tests. These tests are used to report one or more performance markers for the build of Clutter. Each performance marker is picked up from the standard output of running the tests from strings having the form "\n@ marker-name: 42.23" where 'marker-name' and '42.23' are the key/value pairs of a single metric. Each test can provide multiple key/value pairs. Note that if framerate is the feedback metric the test should forcibly enable FPS debugging itself. The file test-common.h contains utility function helping to do fps reporting. The interactive/ tests are any tests whose status can not be determined without a user looking at some visual output, or providing some manual input etc. This covers most of the original Clutter tests. Ideally some of these tests will be migrated into the conform/ directory. The accessibility/ tests are tests created to test the accessibility support of clutter, testing some of the atk interfaces. Other notes: • All tests should ideally include a detailed description in the source explaining exactly what the test is for, how the test was designed to work, and possibly a rationale for the approach taken for testing. Tests for specific bugs should reference the bug report URL or number. • When running tests under Valgrind, you should follow the instructions available here: https://wiki.gnome.org/Valgrind and also use the suppression file available in the Git repository. muffin-6.4.1/src/tests/clutter/micro-bench/0000775000175000017500000000000014723361714017543 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/micro-bench/meson.build0000664000175000017500000000126314723361714021707 0ustar fabiofabioclutter_tests_micro_bench_c_args = [ '-DG_DISABLE_SINGLE_INCLUDES', '-DGLIB_DISABLE_DEPRECATION_WARNINGS', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', ] clutter_tests_micro_bench_c_args += clutter_debug_c_args clutter_tests_micro_bench_tests = [ 'test-text', 'test-picking', 'test-text-perf', 'test-random-text', 'test-cogl-perf', ] foreach test : clutter_tests_micro_bench_tests executable(test, sources: '@0@.c'.format(test), include_directories: clutter_includes, c_args: clutter_tests_micro_bench_c_args, dependencies: [ clutter_deps, libmutter_clutter_dep, ], install: false, ) endforeach muffin-6.4.1/src/tests/clutter/micro-bench/test-random-text.c0000664000175000017500000000511514723361714023130 0ustar fabiofabio#include #include #include #define MAX_TEXT_LEN 10 #define MIN_FONT_SIZE 10 #define MAX_FONT_SIZE 30 static const char * const font_names[] = { "Sans", "Sans Italic", "Serif", "Serif Bold", "Times", "Monospace" }; #define FONT_NAME_COUNT 6 static gboolean on_idle (gpointer data) { ClutterActor *stage = CLUTTER_ACTOR (data); int line_height = 0, xpos = 0, ypos = 0; int stage_width = clutter_actor_get_width (stage); int stage_height = clutter_actor_get_height (stage); char text[MAX_TEXT_LEN + 1]; char font_name[64]; int i; GList *children, *node; static GTimer *timer = NULL; static int frame_count = 0; /* Remove all of the children of the stage */ children = clutter_container_get_children (CLUTTER_CONTAINER (stage)); for (node = children; node; node = node->next) clutter_container_remove_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (node->data)); g_list_free (children); /* Fill the stage with new random labels */ while (ypos < stage_height) { int text_len = rand () % MAX_TEXT_LEN + 1; ClutterActor *label; for (i = 0; i < text_len; i++) text[i] = rand () % (128 - 32) + 32; text[text_len] = '\0'; sprintf (font_name, "%s %i", font_names[rand () % FONT_NAME_COUNT], rand () % (MAX_FONT_SIZE - MIN_FONT_SIZE) + MIN_FONT_SIZE); label = clutter_text_new_with_text (font_name, text); if (clutter_actor_get_height (label) > line_height) line_height = clutter_actor_get_height (label); if (xpos + clutter_actor_get_width (label) > stage_width) { xpos = 0; ypos += line_height; line_height = 0; } clutter_actor_set_position (label, xpos, ypos); clutter_container_add (CLUTTER_CONTAINER (stage), label, NULL); xpos += clutter_actor_get_width (label); } if (timer == NULL) timer = g_timer_new (); else { if (++frame_count >= 10) { printf ("10 frames in %f seconds\n", g_timer_elapsed (timer, NULL)); g_timer_start (timer); frame_count = 0; } } return TRUE; } int main (int argc, char *argv[]) { ClutterActor *stage; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Random Text"); clutter_actor_show (stage); clutter_threads_add_idle (on_idle, stage); clutter_main (); clutter_actor_destroy (stage); return 0; } muffin-6.4.1/src/tests/clutter/micro-bench/test-text.c0000664000175000017500000000612514723361714021654 0ustar fabiofabio#include #include #include #define STAGE_WIDTH 640 #define STAGE_HEIGHT 480 #define COLS 18 #define ROWS 20 static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, gconstpointer *data) { static GTimer *timer = NULL; static int fps = 0; if (!timer) { timer = g_timer_new (); g_timer_start (timer); } if (g_timer_elapsed (timer, NULL) >= 1) { printf ("fps: %d\n", fps); g_timer_start (timer); fps = 0; } ++fps; } static gboolean queue_redraw (gpointer stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return G_SOURCE_CONTINUE; } int main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *group; g_setenv ("CLUTTER_VBLANK", "none", FALSE); g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "Text"); group = clutter_group_new (); clutter_actor_set_size (group, STAGE_WIDTH, STAGE_WIDTH); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group); clutter_threads_add_idle (queue_redraw, stage); g_signal_connect (group, "paint", G_CALLBACK (on_paint), NULL); { gint row, col; for (row=0; row #include #include #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 static int font_size; static int n_chars; static int rows, cols; static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, gconstpointer *data) { static GTimer *timer = NULL; static int fps = 0; if (!timer) { timer = g_timer_new (); g_timer_start (timer); } if (g_timer_elapsed (timer, NULL) >= 1) { printf ("fps=%d, strings/sec=%d, chars/sec=%d\n", fps, fps * rows * cols, fps * rows * cols * n_chars); g_timer_start (timer); fps = 0; } ++fps; } static gboolean queue_redraw (gpointer stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return G_SOURCE_CONTINUE; } static gunichar get_character (int ch) { int total_letters = 0; int i; static const struct { gunichar first_letter; int n_letters; } ranges[] = { { 'a', 26 }, /* lower case letters */ { 'A', 26 }, /* upper case letters */ { '0', 10 }, /* digits */ { 0x410, 0x40 }, /* cyrillic alphabet */ { 0x3b1, 18 } /* greek alphabet */ }; for (i = 0; i < G_N_ELEMENTS (ranges); i++) total_letters += ranges[i].n_letters; ch %= total_letters; for (i = 0; i < G_N_ELEMENTS (ranges) - 1; i++) if (ch < ranges[i].n_letters) return ch + ranges[i].first_letter; else ch -= ranges[i].n_letters; return ch + ranges[i].first_letter; } static ClutterActor * create_label (void) { ClutterColor label_color = { 0xff, 0xff, 0xff, 0xff }; ClutterActor *label; char *font_name; GString *str; int i; font_name = g_strdup_printf ("Monospace %dpx", font_size); str = g_string_new (NULL); for (i = 0; i < n_chars; i++) g_string_append_unichar (str, get_character (i)); label = clutter_text_new_with_text (font_name, str->str); clutter_text_set_color (CLUTTER_TEXT (label), &label_color); g_free (font_name); g_string_free (str, TRUE); return label; } int main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *label; int w, h; int row, col; float scale = 1.0f; g_setenv ("CLUTTER_VBLANK", "none", FALSE); g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; if (argc != 3) { g_printerr ("Usage test-text-perf FONT_SIZE N_CHARS\n"); exit (1); } font_size = atoi (argv[1]); n_chars = atoi (argv[2]); g_print ("Monospace %dpx, string length = %d\n", font_size, n_chars); stage = clutter_stage_new (); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "Text Performance"); g_signal_connect (stage, "paint", G_CALLBACK (on_paint), NULL); label = create_label (); w = clutter_actor_get_width (label); h = clutter_actor_get_height (label); /* If the label is too big to fit on the stage then scale it so that it will fit */ if (w > STAGE_WIDTH || h > STAGE_HEIGHT) { float x_scale = STAGE_WIDTH / (float) w; float y_scale = STAGE_HEIGHT / (float) h; if (x_scale < y_scale) { scale = x_scale; cols = 1; rows = STAGE_HEIGHT / (h * scale); } else { scale = y_scale; cols = STAGE_WIDTH / (w * scale); rows = 1; } g_print ("Text scaled by %f to fit on the stage\n", scale); } else { cols = STAGE_WIDTH / w; rows = STAGE_HEIGHT / h; } clutter_actor_destroy (label); for (row=0; row #include #include #include #include #include #include #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 gboolean run_all = FALSE; static GOptionEntry entries[] = { { "run-all", 'a', 0, G_OPTION_ARG_NONE, &run_all, "Run all tests", "" }, { NULL } }; typedef struct _TestState { ClutterActor *stage; int current_test; } TestState; typedef void (*TestCallback) (TestState *state, ClutterPaintContext *paint_context); static void test_rectangles (TestState *state, ClutterPaintContext *paint_context) { #define RECT_WIDTH 5 #define RECT_HEIGHT 5 CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); int x; int y; CoglPipeline *pipeline; /* Should the rectangles be randomly positioned/colored/rotated? * * It could be good to develop equivalent GL and Cairo tests so we can * have a sanity check for our Cogl performance. * * The color should vary to check that we correctly batch color changes * The use of alpha should vary so we have a variation of which rectangles * require blending. * Should this be a random variation? * It could be good to experiment with focibly enabling blending for * rectangles that don't technically need it for the sake of extending * batching. E.g. if you a long run of interleved rectangles with every * other rectangle needing blending then it may be worth enabling blending * for all the rectangles to avoid the state changes. * The modelview should change between rectangles to check the software * transform codepath. * Should we group some rectangles under the same modelview? Potentially * we could avoid software transform for long runs of rectangles with the * same modelview. * */ pipeline = cogl_pipeline_new (ctx); for (y = 0; y < STAGE_HEIGHT; y += RECT_HEIGHT) { for (x = 0; x < STAGE_WIDTH; x += RECT_WIDTH) { cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, x, y, 0); cogl_framebuffer_rotate (framebuffer, 45, 0, 0, 1); cogl_pipeline_set_color4f (pipeline, 1, (1.0f / STAGE_WIDTH) * y, (1.0f / STAGE_HEIGHT) * x, 1); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, RECT_WIDTH, RECT_HEIGHT); cogl_framebuffer_pop_matrix (framebuffer); } } for (y = 0; y < STAGE_HEIGHT; y += RECT_HEIGHT) { for (x = 0; x < STAGE_WIDTH; x += RECT_WIDTH) { cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, x, y, 0); cogl_framebuffer_rotate (framebuffer, 0, 0, 0, 1); cogl_pipeline_set_color4f (pipeline, 1, (1.0f / STAGE_WIDTH) * x, (1.0f / STAGE_HEIGHT) * y, (1.0f / STAGE_WIDTH) * x); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, RECT_WIDTH, RECT_HEIGHT); cogl_framebuffer_pop_matrix (framebuffer); } } } TestCallback tests[] = { test_rectangles }; static void on_paint (ClutterActor *actor, ClutterPaintContext *paint_context, TestState *state) { tests[state->current_test] (state, paint_context); } static gboolean queue_redraw (gpointer stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } int main (int argc, char *argv[]) { TestState state; ClutterActor *stage; GError *error = NULL; g_setenv ("CLUTTER_VBLANK", "none", FALSE); g_setenv ("CLUTTER_SHOW_FPS", "1", FALSE); if (clutter_init_with_args (&argc, &argv, NULL, entries, NULL, &error) != CLUTTER_INIT_SUCCESS) { g_warning ("Unable to initialise Clutter:\n%s", error->message); g_error_free (error); return EXIT_FAILURE; } state.current_test = 0; state.stage = stage = clutter_stage_new (); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_White); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Performance Test"); /* We want continuous redrawing of the stage... */ clutter_threads_add_idle (queue_redraw, stage); g_signal_connect_after (stage, "paint", G_CALLBACK (on_paint), &state); clutter_actor_show (stage); clutter_main (); clutter_actor_destroy (stage); return 0; } muffin-6.4.1/src/tests/clutter/micro-bench/test-picking.c0000664000175000017500000000722314723361714022314 0ustar fabiofabio #include #include #include #define N_ACTORS 100 #define N_EVENTS 5 static gint n_actors = N_ACTORS; static gint n_events = N_EVENTS; static GOptionEntry entries[] = { { "num-actors", 'a', 0, G_OPTION_ARG_INT, &n_actors, "Number of actors", "ACTORS" }, { "num-events", 'e', 0, G_OPTION_ARG_INT, &n_events, "Number of events", "EVENTS" }, { NULL } }; static gboolean motion_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { return FALSE; } static void do_events (ClutterActor *stage) { glong i; static gdouble angle = 0; for (i = 0; i < n_events; i++) { angle += (2.0 * G_PI) / (gdouble)n_actors; while (angle > G_PI * 2.0) angle -= G_PI * 2.0; /* If we synthesized events, they would be motion compressed; * calling get_actor_at_position() doesn't have that problem */ clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage), CLUTTER_PICK_REACTIVE, 256.0 + 206.0 * cos (angle), 256.0 + 206.0 * sin (angle)); } } static void on_paint (ClutterActor *stage, ClutterPaintContext *paint_context, gconstpointer *data) { do_events (stage); } static gboolean queue_redraw (gpointer stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return TRUE; } int main (int argc, char **argv) { glong i; gdouble angle; ClutterColor color = { 0x00, 0x00, 0x00, 0xff }; ClutterActor *stage, *rect; GError *error = NULL; g_setenv ("CLUTTER_VBLANK", "none", FALSE); g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE); g_setenv ("CLUTTER_SHOW_FPS", "1", FALSE); if (clutter_init_with_args (&argc, &argv, NULL, entries, NULL, &error) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_size (stage, 512, 512); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "Picking"); printf ("Picking performance test with " "%d actors and %d events per frame\n", n_actors, n_events); for (i = n_actors - 1; i >= 0; i--) { angle = ((2.0 * G_PI) / (gdouble) n_actors) * i; color.red = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, i))) / (gdouble)(n_actors/4.0) - 1.0)) * 255.0; color.green = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, fmod (i + (n_actors/3.0)*2, n_actors)))) / (gdouble)(n_actors/4) - 1.0)) * 255.0; color.blue = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, fmod ((i + (n_actors/3.0)), n_actors)))) / (gdouble)(n_actors/4.0) - 1.0)) * 255.0; rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_size (rect, 100, 100); clutter_actor_set_anchor_point_from_gravity (rect, CLUTTER_GRAVITY_CENTER); clutter_actor_set_position (rect, 256 + 206 * cos (angle), 256 + 206 * sin (angle)); clutter_actor_set_reactive (rect, TRUE); g_signal_connect (rect, "motion-event", G_CALLBACK (motion_event_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); } clutter_actor_show (stage); clutter_threads_add_idle (queue_redraw, stage); g_signal_connect (stage, "paint", G_CALLBACK (on_paint), NULL); clutter_main (); clutter_actor_destroy (stage); return 0; } muffin-6.4.1/src/tests/clutter/performance/0000775000175000017500000000000014723361714017656 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/performance/test-state-hidden.c0000664000175000017500000001206014723361714023347 0ustar fabiofabio#include #include #include #include #include "test-common.h" #define STAGE_WIDTH 160 #define STAGE_HEIGHT 120 #define ACTOR_WIDTH 8 #define ACTOR_HEIGHT 8 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) static void completed (ClutterState *state, gpointer data) { if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } else if (g_str_equal (clutter_state_get_state (state), "active")) clutter_state_set_state (state, "right"); else { clutter_state_set_state (state, "active"); } } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_group_new (); ClutterActor *rectangle = clutter_rectangle_new_with_color (color); gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL); g_free (file); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_container_add (CLUTTER_CONTAINER (group), rectangle, NULL); return group; } gint main (gint argc, gchar **argv) { ClutterActor *stage; ClutterActor *group; ClutterState *layout_state; gint i; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); stage = clutter_stage_new (); group = clutter_group_new (); layout_state = clutter_state_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Performance [hidden]"); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); for (i=0; i #include #include static GTimer *testtimer = NULL; static gint testframes = 0; static float testmaxtime = 1.0; /* initialize environment to be suitable for fps testing */ static inline void clutter_perf_fps_init (void) { /* Force not syncing to vblank, we want free-running maximum FPS */ g_setenv ("vblank_mode", "0", FALSE); g_setenv ("CLUTTER_VBLANK", "none", FALSE); /* also overrride internal default FPS */ g_setenv ("CLUTTER_DEFAULT_FPS", "1000", FALSE); if (g_getenv ("CLUTTER_PERFORMANCE_TEST_DURATION")) testmaxtime = atof(g_getenv("CLUTTER_PERFORMANCE_TEST_DURATION")); else testmaxtime = 10.0; g_random_set_seed (12345678); } static void perf_stage_paint_cb (ClutterStage *stage, ClutterPaintContext *paint_context, gpointer *data); static gboolean perf_fake_mouse_cb (gpointer stage); static inline void clutter_perf_fps_start (ClutterStage *stage) { g_signal_connect (stage, "paint", G_CALLBACK (perf_stage_paint_cb), NULL); } static inline void clutter_perf_fake_mouse (ClutterStage *stage) { clutter_threads_add_timeout (1000/60, perf_fake_mouse_cb, stage); } static inline void clutter_perf_fps_report (const gchar *id) { g_print ("\n@ %s: %.2f fps \n", id, testframes / g_timer_elapsed (testtimer, NULL)); } static void perf_stage_paint_cb (ClutterStage *stage, ClutterPaintContext *paint_context, gpointer *data) { if (!testtimer) testtimer = g_timer_new (); testframes ++; if (g_timer_elapsed (testtimer, NULL) > testmaxtime) { clutter_main_quit (); } } static void wrap (gfloat *value, gfloat min, gfloat max) { if (*value > max) *value = min; else if (*value < min) *value = max; } static gboolean perf_fake_mouse_cb (gpointer stage) { ClutterEvent *event = clutter_event_new (CLUTTER_MOTION); static ClutterInputDevice *device = NULL; int i; static float x = 0.0; static float y = 0.0; static float xd = 0.0; static float yd = 0.0; static gboolean inited = FALSE; gfloat w, h; if (!inited) /* XXX: force clutter to do handle our motion events, by forcibly updating the input device's state this shoudl be possible to do in a better manner in the future, a versioning check will have to be added when this is possible without a hack... and the means to do the hack is deprecated */ { ClutterEvent *event2 = clutter_event_new (CLUTTER_ENTER); ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); device = clutter_seat_get_pointer (seat); event2->crossing.stage = stage; event2->crossing.source = stage; event2->crossing.x = 10; event2->crossing.y = 10; event2->crossing.related = NULL; clutter_event_set_device (event2, device); clutter_input_device_update_from_event (device, event2, TRUE); clutter_event_put (event2); clutter_event_free (event2); inited = TRUE; } clutter_actor_get_size (stage, &w, &h); event->motion.stage = stage; clutter_event_set_device (event, device); /* called about every 60fps, and do 10 picks per stage */ for (i = 0; i < 10; i++) { event->motion.x = x; event->motion.y = y; clutter_event_put (event); x += xd; y += yd; xd += g_random_double_range (-0.1, 0.1); yd += g_random_double_range (-0.1, 0.1); wrap (&x, 0, w); wrap (&y, 0, h); xd = CLAMP(xd, -1.3, 1.3); yd = CLAMP(yd, -1.3, 1.3); } clutter_event_free (event); return G_SOURCE_CONTINUE; } muffin-6.4.1/src/tests/clutter/performance/meson.build0000664000175000017500000000160314723361714022020 0ustar fabiofabioclutter_tests_performance_c_args = [ '-DTESTS_DATA_DIR="@0@"'.format(clutter_tests_interactive_srcdir), '-DG_DISABLE_SINGLE_INCLUDES', '-DGLIB_DISABLE_DEPRECATION_WARNINGS', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', ] clutter_tests_performance_c_args += clutter_debug_c_args clutter_tests_performance_tests = [ 'test-picking', 'test-text-perf', 'test-state', 'test-state-interactive', 'test-state-hidden', 'test-state-mini', 'test-state-pick', ] foreach test : clutter_tests_performance_tests executable(test, sources: [ '@0@.c'.format(test), 'test-common.h', ], include_directories: [ clutter_includes, clutter_tests_includepath, ], c_args: clutter_tests_performance_c_args, dependencies: [ clutter_deps, libmutter_clutter_dep, ], install: false, ) endforeach muffin-6.4.1/src/tests/clutter/performance/create-report.rb0000775000175000017500000001063414723361714022766 0ustar fabiofabio#!/usr/bin/env ruby # # ruby program to generate a performance report in PDF/png over git revisions, based on work # originally done for gegl by pippin@gimp.org, the original program is in the public domain. require 'cairo' def cairo_surface(w,h) surface = Cairo::PDFSurface.new("report.pdf", w,h) cr = Cairo::Context.new(surface) yield(cr) end class Database def initialize() @vals = Hash.new @runs = Array.new @colors = [ [0,1,0, 0.8], [0,1,1, 0.8], [1,0,0, 0.8], [1,0,1, 0.8], [1,1,0, 0.8], #[0.5,0.5,0.5,0.8], # gray doesnt have sufficient contrast against background [0.5,0.5,1, 0.8], [0.5,1,0.5, 0.8], [0.5,1,1, 0.8], [1,0.5,0.5, 0.8], [1,0.5,1, 0.8], [1,1,0.5, 0.8], [1,1,1, 0.8], ] @width = 1800 @height = 500 @marginlx = 10 @marginrx = 180 @rgap = 40 @marginy = 10 end def val_max(key) max=0 @runs.each { |run| val = @vals[key][run] if val and val > max max = val end } max end def val_min(key) min=9999990 @runs.each { |run| val = @vals[key][run] min = val if val and val < min } #min 0 # this shows the relative noise in measurements better end def add_run(run) @runs = @runs + [run] end def add_entry(run, name, val) if !@vals[name] @vals[name]=Hash.new end # check if there is an existing value, # and perhaps have different behaviors # associated with @vals[name][run] = val.to_f end def drawbg cr cr.set_source_rgba(0.2, 0.2, 0.2, 1) cr.paint i=0 @runs.each { |run| if i % 2 == 1 cr.move_to 1.0 * i / @runs.length * (@width - @marginlx-@marginrx) + @marginlx, 0 * (@height - @marginy*2) + @marginy cr.line_to 1.0 * i / @runs.length * (@width - @marginlx-@marginrx) + @marginlx, 1.0 * (@height - @marginy*2) + @marginy cr.rel_line_to(1.0 / @runs.length * (@width - @marginlx-@marginrx), 0) cr.rel_line_to(0, -(@height - @marginy*2)) cr.set_source_rgba([0.25,0.25,0.25,1]) cr.fill end i+=1 } end def drawtext cr i = 0 @runs.each { |run| y = i * 10 + 20 while y > @height - @marginy y = y - @height + @marginy + 10 end cr.move_to 1.0 * i / @runs.length * (@width - @marginlx-@marginrx) + @marginlx, y cr.set_source_rgba(0.6,0.6,0.6,1) cr.show_text(run[0..6]) i+=1 } end def draw_limits cr, key cr.move_to @width - @marginrx + @rgap, 20 cr.set_source_rgba(1.0, 1.0, 1.0, 1.0) cr.show_text(" #{val_max(key)} ") cr.move_to @width - @marginrx + @rgap, @height - @marginy cr.show_text(" #{val_min(key)} ") end def draw_val cr, key, valno min = val_min(key) max = val_max(key) cr.set_source_rgba(@colors[valno]) cr.move_to(@width - @marginrx + @rgap, valno * 14 + @marginy + 20) cr.show_text(key) cr.line_width = 2 cr.new_path i = 0 @runs.each { |run| val = @vals[key][run] if val cr.line_to 1.0 * (i+0.5) / @runs.length * (@width - @marginlx-@marginrx) + @marginlx, (1.0 - ((val-min) * 1.0 / (max - min))) * (@height - @marginy*2) + @marginy end i = i + 1 } cr.stroke end def create_report cairo_surface(@width, @height) { |cr| drawbg cr valno = 0 @vals.each { |key, value| draw_val cr, key, valno valno += 1 } drawtext cr cr.target.write_to_png("report.png") valno = 0 @vals.each { |key, value| cr.show_page drawbg cr draw_val cr, key, valno drawtext cr draw_limits cr, key valno += 1 } } end end generator = Database.new items = File.open('jobs').each { |rev| rev.strip! generator.add_run(rev) filename = "reports/" + rev; if File.exist?(filename) File.open(filename).each { |line| if line =~ /^@ (.*):(.*)/ generator.add_entry(rev, $1, $2) end } end } generator.create_report muffin-6.4.1/src/tests/clutter/performance/test-state.c0000664000175000017500000001236514723361714022126 0ustar fabiofabio#include #include #include #include #include "test-common.h" #include "test-utils.h" static gint times = 16; #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 #define ACTOR_WIDTH 64 #define ACTOR_HEIGHT 64 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) static void completed (ClutterState *state, gpointer data) { if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } else if (g_str_equal (clutter_state_get_state (state), "active")) clutter_state_set_state (state, "right"); else { clutter_state_set_state (state, "active"); } times --; if (times <=0) clutter_main_quit (); } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { GError *error = NULL; ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_group_new (); ClutterActor *rectangle = clutter_rectangle_new_with_color (color); ClutterActor *hand = NULL; gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, &error); if (rectangle == NULL) g_error ("image load failed: %s", error->message); g_free (file); clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL); return group; } gint main (gint argc, gchar **argv) { ClutterActor *stage; ClutterState *layout_state; gint i; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); stage = clutter_stage_new (); layout_state = clutter_state_new (); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Performance"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); for (i=0; i #include #include #include #include "test-common.h" #include "test-utils.h" #define STAGE_WIDTH 160 #define STAGE_HEIGHT 120 #define ACTOR_WIDTH 8 #define ACTOR_HEIGHT 8 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) static void completed (ClutterState *state, gpointer data) { if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } else if (g_str_equal (clutter_state_get_state (state), "active")) clutter_state_set_state (state, "right"); else { clutter_state_set_state (state, "active"); } } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { GError *error = NULL; ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_group_new (); ClutterActor *rectangle = clutter_rectangle_new_with_color (color); ClutterActor *hand = NULL; gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, &error); if (rectangle == NULL) g_error ("image load failed: %s", error->message); g_free (file); clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL); return group; } gint main (gint argc, gchar **argv) { ClutterActor *stage; ClutterState *layout_state; gint i; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); stage = clutter_stage_new (); layout_state = clutter_state_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Performance [mini]"); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); for (i=0; i #include #include #include #include "test-common.h" #include "test-utils.h" static gint times = 16; #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 #define ACTOR_WIDTH 64 #define ACTOR_HEIGHT 64 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) static void completed (ClutterState *state, gpointer data) { if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } else if (g_str_equal (clutter_state_get_state (state), "active")) clutter_state_set_state (state, "right"); else { clutter_state_set_state (state, "active"); } times --; if (times <=0) clutter_main_quit (); } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { GError *error = NULL; ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_group_new (); ClutterActor *rectangle = clutter_rectangle_new_with_color (color); ClutterActor *hand = NULL; gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, &error); if (rectangle == NULL) g_error ("image load failed: %s", error->message); g_free (file); clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL); return group; } gint main (gint argc, gchar **argv) { ClutterActor *stage; ClutterState *layout_state; gint i; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); stage = clutter_stage_new (); layout_state = clutter_state_new (); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Performance [pick]"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); for (i=0; i #include #include #include "test-common.h" #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 static int font_size; static int n_chars; static int rows, cols; static gboolean queue_redraw (gpointer stage) { clutter_actor_queue_redraw (CLUTTER_ACTOR (stage)); return G_SOURCE_CONTINUE; } static gunichar get_character (int ch) { int total_letters = 0; int i; static const struct { gunichar first_letter; int n_letters; } ranges[] = { { 'a', 26 }, /* lower case letters */ { 'A', 26 }, /* upper case letters */ { '0', 10 }, /* digits */ { 0x410, 0x40 }, /* cyrillic alphabet */ { 0x3b1, 18 } /* greek alphabet */ }; for (i = 0; i < G_N_ELEMENTS (ranges); i++) total_letters += ranges[i].n_letters; ch %= total_letters; for (i = 0; i < G_N_ELEMENTS (ranges) - 1; i++) if (ch < ranges[i].n_letters) return ch + ranges[i].first_letter; else ch -= ranges[i].n_letters; return ch + ranges[i].first_letter; } static ClutterActor * create_label (void) { ClutterColor label_color = { 0xff, 0xff, 0xff, 0xff }; ClutterActor *label; char *font_name; GString *str; int i; font_name = g_strdup_printf ("Monospace %dpx", font_size); str = g_string_new (NULL); for (i = 0; i < n_chars; i++) g_string_append_unichar (str, get_character (i)); label = clutter_text_new_with_text (font_name, str->str); clutter_text_set_color (CLUTTER_TEXT (label), &label_color); g_free (font_name); g_string_free (str, TRUE); return label; } int main (int argc, char *argv[]) { ClutterActor *stage; ClutterColor stage_color = { 0x00, 0x00, 0x00, 0xff }; ClutterActor *label; int w, h; int row, col; float scale = 1.0f; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); if (argc != 3) { //g_printerr ("Usage test-text-perf FONT_SIZE N_CHARS\n"); //exit (1); font_size = 30; n_chars = 400; } else { font_size = atoi (argv[1]); n_chars = atoi (argv[2]); } g_print ("Monospace %dpx, string length = %d\n", font_size, n_chars); stage = clutter_stage_new (); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); clutter_stage_set_title (CLUTTER_STAGE (stage), "Text Performance"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); label = create_label (); w = clutter_actor_get_width (label); h = clutter_actor_get_height (label); /* If the label is too big to fit on the stage then scale it so that it will fit */ if (w > STAGE_WIDTH || h > STAGE_HEIGHT) { float x_scale = STAGE_WIDTH / (float) w; float y_scale = STAGE_HEIGHT / (float) h; if (x_scale < y_scale) { scale = x_scale; cols = 1; rows = STAGE_HEIGHT / (h * scale); } else { scale = y_scale; cols = STAGE_WIDTH / (w * scale); rows = 1; } g_print ("Text scaled by %f to fit on the stage\n", scale); } else { cols = STAGE_WIDTH / w; rows = STAGE_HEIGHT / h; } clutter_actor_destroy (label); for (row=0; row #include #include #include #include "test-common.h" #include "test-utils.h" #define STAGE_WIDTH 800 #define STAGE_HEIGHT 600 #define ACTOR_WIDTH 64 #define ACTOR_HEIGHT 64 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) static gboolean press_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "right"); return TRUE; } static gboolean release_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "active"); return TRUE; } static gboolean enter_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "hover"); return TRUE; } static gboolean leave_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "normal"); return TRUE; } static void completed (ClutterState *state, gpointer data) { g_print ("Completed transitioning to state: %s\n", clutter_state_get_state (state)); if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { GError *error = NULL; ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_group_new (); ClutterActor *rectangle = clutter_rectangle_new_with_color (color); ClutterActor *hand = NULL; gchar *file = g_build_filename (TESTS_DATA_DIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, &error); if (rectangle == NULL) g_error ("image load failed: %s", error->message); g_free (file); clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_container_add (CLUTTER_CONTAINER (group), rectangle, hand, NULL); return group; } gint main (gint argc, gchar **argv) { ClutterActor *stage; ClutterState *layout_state; gint i; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init (&argc, &argv)) g_error ("Failed to initialize Clutter"); stage = clutter_stage_new (); layout_state = clutter_state_new (); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Performance [interactive]"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "button-press-event", G_CALLBACK (press_event), layout_state); g_signal_connect (stage, "button-release-event", G_CALLBACK (release_event), layout_state); for (i=0; i #include #include #include "test-common.h" #define N_ACTORS 100 #define N_EVENTS 5 static gint n_actors = N_ACTORS; static gint n_events = N_EVENTS; static GOptionEntry entries[] = { { "num-actors", 'a', 0, G_OPTION_ARG_INT, &n_actors, "Number of actors", "ACTORS" }, { "num-events", 'e', 0, G_OPTION_ARG_INT, &n_events, "Number of events", "EVENTS" }, { NULL } }; static gboolean motion_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { return FALSE; } static void do_events (ClutterActor *stage) { glong i; static gdouble angle = 0; for (i = 0; i < n_events; i++) { angle += (2.0 * G_PI) / (gdouble)n_actors; while (angle > G_PI * 2.0) angle -= G_PI * 2.0; /* If we synthesized events, they would be motion compressed; * calling get_actor_at_position() doesn't have that problem */ clutter_stage_get_actor_at_pos (CLUTTER_STAGE (stage), CLUTTER_PICK_REACTIVE, 256.0 + 206.0 * cos (angle), 256.0 + 206.0 * sin (angle)); } } static gboolean queue_redraw (gpointer data) { ClutterActor *stage = CLUTTER_ACTOR (data); clutter_actor_queue_redraw (stage); do_events (stage); return TRUE; } int main (int argc, char **argv) { glong i; gdouble angle; ClutterColor color = { 0x00, 0x00, 0x00, 0xff }; ClutterActor *stage, *rect; clutter_perf_fps_init (); if (CLUTTER_INIT_SUCCESS != clutter_init_with_args (&argc, &argv, NULL, entries, NULL, NULL)) { g_warning ("Failed to initialize clutter"); return -1; } stage = clutter_stage_new (); clutter_actor_set_size (stage, 512, 512); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "Picking Performance"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); printf ("Picking performance test with " "%d actors and %d events per frame\n", n_actors, n_events); for (i = n_actors - 1; i >= 0; i--) { angle = ((2.0 * G_PI) / (gdouble) n_actors) * i; color.red = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, i))) / (gdouble)(n_actors/4.0) - 1.0)) * 255.0; color.green = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, fmod (i + (n_actors/3.0)*2, n_actors)))) / (gdouble)(n_actors/4) - 1.0)) * 255.0; color.blue = (1.0 - ABS ((MAX (0, MIN (n_actors/2.0 + 0, fmod ((i + (n_actors/3.0)), n_actors)))) / (gdouble)(n_actors/4.0) - 1.0)) * 255.0; rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_size (rect, 100, 100); clutter_actor_set_anchor_point_from_gravity (rect, CLUTTER_GRAVITY_CENTER); clutter_actor_set_position (rect, 256 + 206 * cos (angle), 256 + 206 * sin (angle)); clutter_actor_set_reactive (rect, TRUE); g_signal_connect (rect, "motion-event", G_CALLBACK (motion_event_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); } clutter_actor_show (stage); clutter_perf_fps_start (CLUTTER_STAGE (stage)); clutter_threads_add_idle (queue_redraw, stage); clutter_main (); clutter_perf_fps_report ("test-picking"); return 0; } muffin-6.4.1/src/tests/clutter/interactive/0000775000175000017500000000000014723361714017672 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/interactive/test-script.c0000664000175000017500000000746514723361714022333 0ustar fabiofabio#include #include #include #include #include #include static ClutterScript *script = NULL; static guint merge_id = 0; int test_script_main (int argc, char *argv[]); static const gchar *test_unmerge = "[" " {" " \"id\" : \"main-stage\"," " \"type\" : \"ClutterStage\"," " \"children\" : [ \"blue-button\" ]" " }," " {" " \"id\" : \"blue-button\"," " \"type\" : \"ClutterRectangle\"," " \"color\" : \"#0000ffff\"," " \"x\" : 350," " \"y\" : 50," " \"width\" : 100," " \"height\" : 100," " \"visible\" : true," " \"reactive\" : true" " }" "]"; static const gchar *test_behaviour = "[" " {" " \"id\" : \"main-timeline\"," " \"type\" : \"ClutterTimeline\"," " \"duration\" : 5000," " \"loop\" : true" " }," " {" " \"id\" : \"sine-alpha\"," " \"type\" : \"ClutterAlpha\"," " \"function\" : \"sine_alpha\"," " \"timeline\" : \"main-timeline\"" " }," "]"; static gboolean blue_button_press (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { g_print ("[*] Pressed '%s'\n", clutter_get_script_id (G_OBJECT (actor))); g_print ("[*] Unmerging objects with merge id: %d\n", merge_id); clutter_script_unmerge_objects (script, merge_id); return TRUE; } static gboolean red_button_press (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { GObject *timeline; g_print ("[*] Pressed '%s'\n", clutter_get_script_id (G_OBJECT (actor))); timeline = clutter_script_get_object (script, "main-timeline"); g_assert (CLUTTER_IS_TIMELINE (timeline)); if (!clutter_timeline_is_playing (CLUTTER_TIMELINE (timeline))) clutter_timeline_start (CLUTTER_TIMELINE (timeline)); else clutter_timeline_pause (CLUTTER_TIMELINE (timeline)); return TRUE; } G_MODULE_EXPORT int test_script_main (int argc, char *argv[]) { GObject *stage, *blue_button, *red_button; GError *error = NULL; gchar *file; gint res; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; script = clutter_script_new (); g_assert (CLUTTER_IS_SCRIPT (script)); clutter_script_load_from_data (script, test_behaviour, -1, &error); if (error) { g_print ("*** Error:\n" "*** %s\n", error->message); g_error_free (error); g_object_unref (script); return EXIT_FAILURE; } file = g_build_filename (TESTS_DATADIR, "test-script.json", NULL); clutter_script_load_from_file (script, file, &error); if (error) { g_print ("*** Error:\n" "*** %s\n", error->message); g_error_free (error); g_object_unref (script); g_free (file); return EXIT_FAILURE; } g_free (file); merge_id = clutter_script_load_from_data (script, test_unmerge, -1, &error); if (error) { g_print ("*** Error:\n" "*** %s\n", error->message); g_error_free (error); g_object_unref (script); return EXIT_FAILURE; } clutter_script_connect_signals (script, NULL); res = clutter_script_get_objects (script, "main-stage", &stage, "red-button", &red_button, "blue-button", &blue_button, NULL); g_assert (res == 3); clutter_actor_show (CLUTTER_ACTOR (stage)); g_signal_connect (red_button, "button-press-event", G_CALLBACK (red_button_press), NULL); g_signal_connect (blue_button, "button-press-event", G_CALLBACK (blue_button_press), NULL); clutter_main (); g_object_unref (script); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-path-constraint.c0000664000175000017500000000667114723361714024143 0ustar fabiofabio#include #include #include #define PATH_DESCRIPTION \ "M 0, 0 " \ "L 0, 300 " \ "L 300, 300 " \ "L 300, 0 " \ "L 0, 0" static gboolean toggled = FALSE; int test_path_constraint_main (int argc, char *argv[]); static gboolean on_button_press (ClutterActor *actor, const ClutterEvent *event, gpointer dummy G_GNUC_UNUSED) { if (!toggled) clutter_actor_animate (actor, CLUTTER_EASE_OUT_CUBIC, 500, "@constraints.path.offset", 1.0, NULL); else clutter_actor_animate (actor, CLUTTER_EASE_OUT_CUBIC, 500, "@constraints.path.offset", 0.0, NULL); toggled = !toggled; return TRUE; } static gchar * node_to_string (const ClutterPathNode *node) { GString *buffer = g_string_sized_new (256); gsize len = 0, i; switch (node->type) { case CLUTTER_PATH_MOVE_TO: g_string_append (buffer, "move-to "); len = 1; break; case CLUTTER_PATH_LINE_TO: g_string_append (buffer, "line-to "); len = 1; break; case CLUTTER_PATH_CURVE_TO: g_string_append (buffer, "curve-to "); len = 3; break; case CLUTTER_PATH_CLOSE: g_string_append (buffer, "close"); len = 0; break; default: break; } for (i = 0; i < len; i++) { if (i == 0) g_string_append (buffer, "[ "); g_string_append_printf (buffer, "[ %d, %d ]", node->points[i].x, node->points[i].y); if (i == len - 1) g_string_append (buffer, " ]"); } return g_string_free (buffer, FALSE); } static void on_node_reached (ClutterPathConstraint *constraint, ClutterActor *actor, guint index_) { ClutterPath *path = clutter_path_constraint_get_path (constraint); ClutterPathNode node; gchar *str; clutter_path_get_node (path, index_, &node); str = node_to_string (&node); g_print ("Node %d reached: %s\n", index_, str); g_free (str); } G_MODULE_EXPORT int test_path_constraint_main (int argc, char *argv[]) { ClutterActor *stage, *rect; ClutterPath *path; ClutterColor rect_color = { 0xcc, 0x00, 0x00, 0xff }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Path Constraint"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); path = clutter_path_new (); clutter_path_set_description (path, PATH_DESCRIPTION); rect = clutter_rectangle_new (); clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &rect_color); clutter_actor_set_size (rect, 128, 128); clutter_actor_set_reactive (rect, TRUE); clutter_actor_add_constraint_with_name (rect, "path", clutter_path_constraint_new (path, 0.0)); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); g_signal_connect (rect, "button-press-event", G_CALLBACK (on_button_press), NULL); g_signal_connect (clutter_actor_get_constraint (rect, "path"), "node-reached", G_CALLBACK (on_node_reached), NULL); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-binding-pool.c0000664000175000017500000002344714723361714023406 0ustar fabiofabio#include #include #include #include #include #include #define TYPE_KEY_GROUP (key_group_get_type ()) #define KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_KEY_GROUP, KeyGroup)) #define IS_KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_KEY_GROUP)) #define KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_KEY_GROUP, KeyGroupClass)) #define IS_KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_KEY_GROUP)) typedef struct _KeyGroup KeyGroup; typedef struct _KeyGroupClass KeyGroupClass; struct _KeyGroup { ClutterActor parent_instance; gint selected_index; }; struct _KeyGroupClass { ClutterActorClass parent_class; void (* activate) (KeyGroup *group, ClutterActor *child); }; GType key_group_get_type (void); int test_binding_pool_main (int argc, char *argv[]); const char * test_binding_pool_describe (void); G_DEFINE_TYPE (KeyGroup, key_group, CLUTTER_TYPE_ACTOR) enum { ACTIVATE, LAST_SIGNAL }; static guint group_signals[LAST_SIGNAL] = { 0, }; static gboolean key_group_action_move_left (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { gint n_children; g_debug ("%s: activated '%s' (k:%d, m:%d)", G_STRLOC, action_name, key_val, modifiers); n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self)); self->selected_index -= 1; if (self->selected_index < 0) self->selected_index = n_children - 1; return TRUE; } static gboolean key_group_action_move_right (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { gint n_children; g_debug ("%s: activated '%s' (k:%d, m:%d)", G_STRLOC, action_name, key_val, modifiers); n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self)); self->selected_index += 1; if (self->selected_index >= n_children) self->selected_index = 0; return TRUE; } static gboolean key_group_action_activate (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { ClutterActor *child = NULL; g_debug ("%s: activated '%s' (k:%d, m:%d)", G_STRLOC, action_name, key_val, modifiers); if (self->selected_index == -1) return FALSE; child = clutter_actor_get_child_at_index (CLUTTER_ACTOR (self), self->selected_index); if (child != NULL) { g_signal_emit (self, group_signals[ACTIVATE], 0, child); return TRUE; } else return FALSE; } static gboolean key_group_key_press (ClutterActor *actor, ClutterKeyEvent *event) { ClutterBindingPool *pool; gboolean res; pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); g_assert (pool != NULL); res = clutter_binding_pool_activate (pool, event->keyval, event->modifier_state, G_OBJECT (actor)); /* if we activate a key binding, redraw the actor */ if (res) clutter_actor_queue_redraw (actor); return res ? CLUTTER_EVENT_STOP : CLUTTER_EVENT_PROPAGATE; } static void key_group_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { KeyGroup *self = KEY_GROUP (actor); ClutterActorIter iter; ClutterActor *child; gint i = 0; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglPipeline *pipeline; pipeline = cogl_pipeline_new (ctx); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { /* paint the selection rectangle */ if (i == self->selected_index) { ClutterActorBox box = { 0, }; clutter_actor_get_allocation_box (child, &box); box.x1 -= 2; box.y1 -= 2; box.x2 += 2; box.y2 += 2; cogl_pipeline_set_color4ub (pipeline, 255, 255, 0, 224); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, box.x1, box.y1, box.x2, box.y2); } clutter_actor_paint (child, paint_context); i += 1; } } static void key_group_class_init (KeyGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterBindingPool *binding_pool; actor_class->paint = key_group_paint; actor_class->key_press_event = key_group_key_press; group_signals[ACTIVATE] = g_signal_new (g_intern_static_string ("activate"), G_OBJECT_CLASS_TYPE (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (KeyGroupClass, activate), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); binding_pool = clutter_binding_pool_get_for_class (klass); clutter_binding_pool_install_action (binding_pool, "move-right", CLUTTER_KEY_Right, 0, G_CALLBACK (key_group_action_move_right), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "move-left", CLUTTER_KEY_Left, 0, G_CALLBACK (key_group_action_move_left), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_Return, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_KP_Enter, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_ISO_Enter, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); } static void key_group_init (KeyGroup *self) { self->selected_index = -1; } static void on_key_group_activate (KeyGroup *group, ClutterActor *child) { g_print ("Child '%s' activated!\n", clutter_actor_get_name (child)); } G_MODULE_EXPORT int test_binding_pool_main (int argc, char *argv[]) { ClutterActor *stage, *key_group; gint group_x, group_y; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Key Binding Pool"); g_signal_connect (stage, "button-press-event", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); key_group = g_object_new (TYPE_KEY_GROUP, NULL); clutter_actor_add_child (stage, key_group); /* add three rectangles to the key group */ clutter_container_add (CLUTTER_CONTAINER (key_group), g_object_new (CLUTTER_TYPE_ACTOR, "background-color", CLUTTER_COLOR_Red, "name", "Red Rectangle", "width", 100.0, "height", 100.0, "x", 0.0, "y", 0.0, NULL), g_object_new (CLUTTER_TYPE_ACTOR, "background-color", CLUTTER_COLOR_Green, "name", "Green Rectangle", "width", 100.0, "height", 100.0, "x", 125.0, "y", 0.0, NULL), g_object_new (CLUTTER_TYPE_ACTOR, "background-color", CLUTTER_COLOR_Blue, "name", "Blue Rectangle", "width", 100.0, "height", 100.0, "x", 250.0, "y", 0.0, NULL), NULL); g_signal_connect (key_group, "activate", G_CALLBACK (on_key_group_activate), NULL); group_x = (clutter_actor_get_width (stage) - clutter_actor_get_width (key_group)) / 2; group_y = (clutter_actor_get_height (stage) - clutter_actor_get_height (key_group)) / 2; clutter_actor_set_position (key_group, group_x, group_y); clutter_actor_set_reactive (key_group, TRUE); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), key_group); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_binding_pool_describe (void) { return "Binding pools example"; } muffin-6.4.1/src/tests/clutter/interactive/meson.build0000664000175000017500000000430714723361714022040 0ustar fabiofabioclutter_tests_interactive_srcdir = meson.current_source_dir() clutter_tests_interactive_includepath = include_directories('.') clutter_tests_interactive_c_args = [ '-DTESTS_DATADIR="@0@"'.format(clutter_tests_interactive_srcdir), '-DG_DISABLE_SINGLE_INCLUDES', '-DGLIB_DISABLE_DEPRECATION_WARNINGS', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', ] clutter_tests_interactive_c_args += clutter_debug_c_args clutter_tests_interactive_link_args = [ '-Wl,--export-dynamic', ] clutter_tests_interactive_test_sources = [ 'test-events.c', 'test-actors.c', 'test-shader-effects.c', 'test-script.c', 'test-grab.c', 'test-cogl-shader-glsl.c', 'test-state.c', 'test-cogl-tex-tile.c', 'test-cogl-tex-convert.c', 'test-cogl-offscreen.c', 'test-cogl-tex-polygon.c', 'test-cogl-multitexture.c', 'test-paint-wrapper.c', 'test-layout.c', 'test-animation.c', 'test-easing.c', 'test-binding-pool.c', 'test-text.c', 'test-text-field.c', 'test-cairo-clock.c', 'test-cairo-flowers.c', 'test-stage-sizing.c', 'test-scrolling.c', 'test-swipe-action.c', 'test-cogl-point-sprites.c', 'test-path-constraint.c', 'test-state-script.c', 'test-devices.c', 'test-content.c', 'test-keyframe-transition.c', 'test-bind-constraint.c', 'test-touch-events.c', 'test-rotate-zoom.c', 'test-image.c', ] gen_test_unit_names = find_program('meson/gen-test-unit-names.sh') clutter_interactive_test_unit_names_h = custom_target('gen-test-unit-names', output: 'test-unit-names.h', input: clutter_tests_interactive_test_sources, command: [gen_test_unit_names, '@OUTPUT@', '@INPUT@'], install: false, ) clutter_tests_interactive_sources = [ 'test-main.c', clutter_interactive_test_unit_names_h, clutter_tests_interactive_test_sources ] executable('test-interactive', sources: clutter_tests_interactive_sources, include_directories: [ clutter_includes, clutter_tests_includepath, clutter_tests_interactive_includepath, ], c_args: clutter_tests_interactive_c_args, link_args: clutter_tests_interactive_link_args, dependencies: [ clutter_deps, libmutter_clutter_dep, libmutter_dep, gdk_pixbuf_dep, ], install: false, ) muffin-6.4.1/src/tests/clutter/interactive/test-content.c0000664000175000017500000001525214723361714022472 0ustar fabiofabio#include #include #include typedef struct _ColorContent { GObject parent_instance; double red; double green; double blue; double alpha; float padding; } ColorContent; typedef struct _ColorContentClass { GObjectClass parent_class; } ColorContentClass; static void clutter_content_iface_init (ClutterContentInterface *iface); GType color_content_get_type (void); int test_content_main (int argc, char *argv[]); const char * test_content_describe (void); G_DEFINE_TYPE_WITH_CODE (ColorContent, color_content, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) static void color_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { ColorContent *self = (ColorContent *) content; ClutterActorBox box, content_box; ClutterColor color; PangoLayout *layout; PangoRectangle logical; ClutterPaintNode *node; #if 0 g_debug ("Painting content [%p] " "{ r:%.2f, g:%.2f, b:%.2f, a:%.2f } " "for actor [%p] (context: [%p])", content, self->red, self->green, self->blue, self->alpha, actor, context); #endif clutter_actor_get_content_box (actor, &content_box); box = content_box; box.x1 += self->padding; box.y1 += self->padding; box.x2 -= self->padding; box.y2 -= self->padding; color.alpha = self->alpha * 255; color.red = self->red * 255; color.green = self->green * 255; color.blue = self->blue * 255; node = clutter_color_node_new (&color); clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); color.red = (1.0 - self->red) * 255; color.green = (1.0 - self->green) * 255; color.blue = (1.0 - self->blue) * 255; layout = clutter_actor_create_pango_layout (actor, "A"); pango_layout_get_pixel_extents (layout, NULL, &logical); node = clutter_text_node_new (layout, &color); /* top-left */ box.x1 = clutter_actor_box_get_x (&content_box); box.y1 = clutter_actor_box_get_y (&content_box); box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* top-right */ box.x1 = clutter_actor_box_get_x (&content_box) + clutter_actor_box_get_width (&content_box) - logical.width; box.y1 = clutter_actor_box_get_y (&content_box); box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* bottom-right */ box.x1 = clutter_actor_box_get_x (&content_box) + clutter_actor_box_get_width (&content_box) - logical.width; box.y1 = clutter_actor_box_get_y (&content_box) + clutter_actor_box_get_height (&content_box) - logical.height; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* bottom-left */ box.x1 = clutter_actor_box_get_x (&content_box); box.y1 = clutter_actor_box_get_y (&content_box) + clutter_actor_box_get_height (&content_box) - logical.height; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* center */ box.x1 = clutter_actor_box_get_x (&content_box) + (clutter_actor_box_get_width (&content_box) - logical.width) / 2.0; box.y1 = clutter_actor_box_get_y (&content_box) + (clutter_actor_box_get_height (&content_box) - logical.height) / 2.0; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); g_object_unref (layout); } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->paint_content = color_content_paint_content; } static void color_content_class_init (ColorContentClass *klass) { } static void color_content_init (ColorContent *self) { } static ClutterContent * color_content_new (double red, double green, double blue, double alpha, float padding) { ColorContent *self = g_object_new (color_content_get_type (), NULL); self->red = red; self->green = green; self->blue = blue; self->alpha = alpha; self->padding = padding; return (ClutterContent *) self; } G_MODULE_EXPORT int test_content_main (int argc, char *argv[]) { ClutterActor *stage, *grid; ClutterContent *content; int i, n_rects; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; stage = clutter_stage_new (); clutter_actor_set_name (stage, "Stage"); clutter_stage_set_title (CLUTTER_STAGE (stage), "Content"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_actor_show (stage); grid = clutter_actor_new (); clutter_actor_set_name (grid, "Grid"); clutter_actor_set_margin_top (grid, 12); clutter_actor_set_margin_right (grid, 12); clutter_actor_set_margin_bottom (grid, 12); clutter_actor_set_margin_left (grid, 12); clutter_actor_set_layout_manager (grid, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL)); clutter_actor_add_constraint (grid, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.0)); clutter_actor_add_child (stage, grid); content = color_content_new (g_random_double_range (0.0, 1.0), g_random_double_range (0.0, 1.0), g_random_double_range (0.0, 1.0), 1.0, 2.0); n_rects = g_random_int_range (12, 24); for (i = 0; i < n_rects; i++) { ClutterActor *box = clutter_actor_new (); ClutterColor bg_color = { g_random_int_range (0, 255), g_random_int_range (0, 255), g_random_int_range (0, 255), 255 }; char *name, *color; color = clutter_color_to_string (&bg_color); name = g_strconcat ("Box <", color, ">", NULL); clutter_actor_set_name (box, name); g_free (name); g_free (color); clutter_actor_set_background_color (box, &bg_color); clutter_actor_set_content (box, content); clutter_actor_set_size (box, 64, 64); clutter_actor_add_child (grid, box); } clutter_main (); g_object_unref (content); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_content_describe (void) { return "A simple test for ClutterContent"; } muffin-6.4.1/src/tests/clutter/interactive/test-script.json0000664000175000017500000000301314723361714023043 0ustar fabiofabio{ "My Scene" : { "id" : "main-stage", "type" : "ClutterStage", "title" : { "translatable" : true, "string" : "ClutterScript test" }, "color" : "white", "signals" : [ { "name" : "key-press-event", "handler" : "clutter_main_quit" }, { "name" : "destroy", "handler" : "clutter_main_quit" } ], "children" : [ { "id" : "red-button", "type" : "ClutterRectangle", "color" : "#ff0000ff", "x" : 50, "y" : 50, "width" : 100, "height" : 100, "reactive" : true, "rotation" : [ { "z-axis" : [ 45.0, [ 75, 75 ] ] } ] }, { "id" : "green-button", "type" : "ClutterRectangle", "color" : "#00ff00ff", "border-width" : 5, "border-color" : "#00cc00ff", "position" : [ 200.0, 50.0 ], "size" : { "width" : 100.0, "height" : 100.0 }, "depth" : -200.0, "reactive" : true, "signals" : [ { "name" : "button-press-event", "handler" : "clutter_main_quit" } ] }, { "id" : "label", "type" : "ClutterText", "x" : 50, "y" : 200, "text" : { "translatable" : true, "string" : "Clutter Script" }, "font-name" : "Sans 24px", "color" : "black", "line-alignment" : "center", "line-wrap" : false, "ellipsize" : "none", "rotation" : [ { "y-axis" : [ 60.0, [ 275, 100 ] ] }, { "z-axis" : [ 45.0, [ 75, 75 ] ] } ] } ] } } muffin-6.4.1/src/tests/clutter/interactive/test-state.c0000664000175000017500000001575014723361714022143 0ustar fabiofabio#include #include #include #include #include "test-utils.h" #define STAGE_WIDTH 1024 #define STAGE_HEIGHT 768 #define ACTOR_WIDTH 128 #define ACTOR_HEIGHT 128 #define COLS (STAGE_WIDTH/ACTOR_WIDTH) #define ROWS (STAGE_HEIGHT/ACTOR_HEIGHT) #define TOTAL (ROWS*COLS) gint test_state_main (gint argc, gchar **argv); const char * test_state_describe (void); static gboolean press_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "right"); return TRUE; } static gboolean release_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "active"); return TRUE; } static gboolean enter_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "hover"); return TRUE; } static gboolean leave_event (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { ClutterState *state = CLUTTER_STATE (user_data); clutter_state_set_state (state, "normal"); return TRUE; } static void completed (ClutterState *state, gpointer data) { g_print ("Completed transitioning to state: %s\n", clutter_state_get_state (state)); if (g_str_equal (clutter_state_get_state (state), "right")) { /* skip straight to left state when reaching right */ clutter_state_warp_to_state (state, "left"); } } static ClutterActor *new_rect (gint r, gint g, gint b, gint a) { GError *error = NULL; ClutterColor *color = clutter_color_new (r, g, b, a); ClutterActor *group = clutter_actor_new (); ClutterActor *rectangle = clutter_actor_new (); ClutterActor *hand = NULL; gchar *file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, &error); if (rectangle == NULL) g_error ("image load failed: %s", error->message); g_free (file); clutter_actor_set_size (hand, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_actor_set_background_color (rectangle, color); clutter_actor_set_size (rectangle, ACTOR_WIDTH,ACTOR_HEIGHT); clutter_color_free (color); clutter_actor_add_child (group, rectangle); clutter_actor_add_child (group, hand); return group; } G_MODULE_EXPORT gint test_state_main (gint argc, gchar **argv) { ClutterActor *stage; ClutterState *layout_state; gint i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; layout_state = clutter_state_new (); stage = clutter_stage_new (); clutter_actor_set_background_color (stage, CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Machine"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "button-press-event", G_CALLBACK (press_event), layout_state); g_signal_connect (stage, "button-release-event", G_CALLBACK (release_event), layout_state); for (i = 0; i < TOTAL; i++) { ClutterActor *actor; ClutterState *a_state; int row = i/COLS; int col = i%COLS; actor = new_rect (255 * (1.0 * col / COLS), 50, 255 * (1.0 * row / ROWS), 255); clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); clutter_actor_set_position (actor, 320.0, 240.0); clutter_actor_set_reactive (actor, TRUE); clutter_actor_add_effect_with_name (actor, "fade", clutter_desaturate_effect_new (0.0)); clutter_state_set (layout_state, NULL, "active", actor, "delayed::x", CLUTTER_LINEAR, ACTOR_WIDTH * 1.0 * ((TOTAL-1-i) % COLS), ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2, actor, "delayed::y", CLUTTER_LINEAR, ACTOR_HEIGHT * 1.0 * ((TOTAL-1-i) / COLS), ((row*1.0/ROWS))/2, 0.0, actor, "rotation-angle-x", CLUTTER_LINEAR, 0.0, actor, "rotation-angle-y", CLUTTER_LINEAR, 0.0, NULL); clutter_state_set (layout_state, NULL, "right", actor, "delayed::x", CLUTTER_LINEAR, STAGE_WIDTH * 1.0, ((row*1.0/ROWS))/2, (1.0-(row*1.0/ROWS))/2, actor, "delayed::y", CLUTTER_LINEAR, STAGE_HEIGHT * 1.0, ((row*1.0/ROWS))/2, 0.0, NULL); clutter_state_set (layout_state, NULL, "left", actor, "rotation-angle-x", CLUTTER_LINEAR, 45.0, actor, "rotation-angle-y", CLUTTER_LINEAR, 5.0, actor, "x", CLUTTER_LINEAR, 0-64.0, actor, "y", CLUTTER_LINEAR, 0-64.0, NULL); a_state = clutter_state_new (); g_object_set_data_full (G_OBJECT (actor), "hover-state-machine", a_state, g_object_unref); g_signal_connect (actor, "enter-event", G_CALLBACK (enter_event), a_state); g_signal_connect (actor, "leave-event", G_CALLBACK (leave_event), a_state); clutter_state_set (a_state, NULL, "normal", actor, "opacity", CLUTTER_LINEAR, 0x77, actor, "rotation-angle-z", CLUTTER_LINEAR, 0.0, actor, "@effects.fade.factor", CLUTTER_LINEAR, 0.0, NULL); clutter_state_set (a_state, NULL, "hover", actor, "opacity", CLUTTER_LINEAR, 0xff, actor, "rotation-angle-z", CLUTTER_LINEAR, 10.0, actor, "@effects.fade.factor", CLUTTER_LINEAR, 1.0, NULL); clutter_actor_set_opacity (actor, 0x77); clutter_state_set_duration (a_state, NULL, NULL, 500); } clutter_state_set_duration (layout_state, NULL, NULL, 1000); clutter_state_set_duration (layout_state, "active", "left", 1400); g_signal_connect (layout_state, "completed", G_CALLBACK (completed), NULL); clutter_actor_show (stage); clutter_state_warp_to_state (layout_state, "left"); clutter_state_set_state (layout_state, "active"); clutter_main (); g_object_unref (layout_state); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_state_describe (void) { return "Animating using the State class."; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-tex-polygon.c0000664000175000017500000003240114723361714024222 0ustar fabiofabio#include #include #include #include #include /* Coglbox declaration *--------------------------------------------------*/ G_BEGIN_DECLS #define TEST_TYPE_COGLBOX test_coglbox_get_type() #define TEST_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ TEST_TYPE_COGLBOX, TestCoglbox)) #define TEST_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) #define TEST_IS_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ TEST_TYPE_COGLBOX)) #define TEST_IS_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ TEST_TYPE_COGLBOX)) #define TEST_COGLBOX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) typedef struct _TestCoglbox TestCoglbox; typedef struct _TestCoglboxClass TestCoglboxClass; typedef struct _TestCoglboxPrivate TestCoglboxPrivate; struct _TestCoglbox { ClutterActor parent; /*< private >*/ TestCoglboxPrivate *priv; }; struct _TestCoglboxClass { ClutterActorClass parent_class; /* padding for future expansion */ void (*_test_coglbox1) (void); void (*_test_coglbox2) (void); void (*_test_coglbox3) (void); void (*_test_coglbox4) (void); }; static GType test_coglbox_get_type (void) G_GNUC_CONST; int test_cogl_tex_polygon_main (int argc, char *argv[]); const char * test_cogl_tex_polygon_describe (void); G_END_DECLS /* Coglbox private declaration *--------------------------------------------------*/ struct _TestCoglboxPrivate { CoglHandle sliced_tex, not_sliced_tex; gint frame; gboolean use_sliced; gboolean use_linear_filtering; }; G_DEFINE_TYPE_WITH_PRIVATE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); #define TEST_COGLBOX_GET_PRIVATE(obj) \ ((TestCoglboxPrivate *)test_coglbox_get_instance_private (TEST_COGLBOX ((obj)))) /* Coglbox implementation *--------------------------------------------------*/ static void test_coglbox_fade_texture (CoglFramebuffer *framebuffer, CoglPipeline *pipeline, float x1, float y1, float x2, float y2, float tx1, float ty1, float tx2, float ty2) { CoglVertexP3T2C4 vertices[4]; CoglPrimitive *primitive; int i; vertices[0].x = x1; vertices[0].y = y1; vertices[0].z = 0; vertices[0].s = tx1; vertices[0].t = ty1; vertices[1].x = x1; vertices[1].y = y2; vertices[1].z = 0; vertices[1].s = tx1; vertices[1].t = ty2; vertices[2].x = x2; vertices[2].y = y2; vertices[2].z = 0; vertices[2].s = tx2; vertices[2].t = ty2; vertices[3].x = x2; vertices[3].y = y1; vertices[3].z = 0; vertices[3].s = tx2; vertices[3].t = ty1; for (i = 0; i < 4; i++) { CoglColor cogl_color; cogl_color_init_from_4ub (&cogl_color, 255, 255, 255, ((i ^ (i >> 1)) & 1) ? 0 : 128); cogl_color_premultiply (&cogl_color); vertices[i].r = cogl_color_get_red_byte (&cogl_color); vertices[i].g = cogl_color_get_green_byte (&cogl_color); vertices[i].b = cogl_color_get_blue_byte (&cogl_color); vertices[i].a = cogl_color_get_alpha_byte (&cogl_color); } primitive = cogl_primitive_new_p3t2c4 (cogl_framebuffer_get_context (framebuffer), COGL_VERTICES_MODE_TRIANGLE_FAN, 4, vertices); cogl_primitive_draw (primitive, framebuffer, pipeline); cogl_object_unref (primitive); } static void test_coglbox_triangle_texture (CoglFramebuffer *framebuffer, CoglHandle material, int tex_width, int tex_height, float x, float y, float tx1, float ty1, float tx2, float ty2, float tx3, float ty3) { CoglVertexP3T2 vertices[3]; CoglPrimitive *primitive; vertices[0].x = x + tx1 * tex_width; vertices[0].y = y + ty1 * tex_height; vertices[0].z = 0; vertices[0].s = tx1; vertices[0].t = ty1; vertices[1].x = x + tx2 * tex_width; vertices[1].y = y + ty2 * tex_height; vertices[1].z = 0; vertices[1].s = tx2; vertices[1].t = ty2; vertices[2].x = x + tx3 * tex_width; vertices[2].y = y + ty3 * tex_height; vertices[2].z = 0; vertices[2].s = tx3; vertices[2].t = ty3; primitive = cogl_primitive_new_p3t2 (cogl_framebuffer_get_context (framebuffer), COGL_VERTICES_MODE_TRIANGLE_FAN, 3, vertices); cogl_primitive_draw (primitive, framebuffer, material); cogl_object_unref (primitive); } static void test_coglbox_paint (ClutterActor *self, ClutterPaintContext *paint_context) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); CoglHandle tex_handle = priv->use_sliced ? priv->sliced_tex : priv->not_sliced_tex; int tex_width = cogl_texture_get_width (tex_handle); int tex_height = cogl_texture_get_height (tex_handle); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglHandle material = cogl_material_new (); cogl_material_set_layer (material, 0, tex_handle); cogl_material_set_layer_filters (material, 0, priv->use_linear_filtering ? COGL_MATERIAL_FILTER_LINEAR : COGL_MATERIAL_FILTER_NEAREST, priv->use_linear_filtering ? COGL_MATERIAL_FILTER_LINEAR : COGL_MATERIAL_FILTER_NEAREST); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, tex_width / 2, 0, 0); cogl_framebuffer_rotate (framebuffer, priv->frame, 0, 1, 0); cogl_framebuffer_translate (framebuffer, -tex_width / 2, 0, 0); /* Draw a hand and refect it */ cogl_framebuffer_draw_textured_rectangle (framebuffer, material, 0, 0, tex_width, tex_height, 0, 0, 1, 1); test_coglbox_fade_texture (framebuffer, material, 0, tex_height, tex_width, (tex_height * 3 / 2), 0.0, 1.0, 1.0, 0.5); cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, tex_width * 3 / 2 + 60, 0, 0); cogl_framebuffer_rotate (framebuffer, priv->frame, 0, 1, 0); cogl_framebuffer_translate (framebuffer, -tex_width / 2 - 10, 0, 0); /* Draw the texture split into two triangles */ test_coglbox_triangle_texture (framebuffer, material, tex_width, tex_height, 0, 0, 0, 0, 0, 1, 1, 1); test_coglbox_triangle_texture (framebuffer, material, tex_width, tex_height, 20, 0, 0, 0, 1, 0, 1, 1); cogl_framebuffer_pop_matrix (framebuffer); cogl_object_unref (material); } static void test_coglbox_finalize (GObject *object) { G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); } static void test_coglbox_dispose (GObject *object) { TestCoglboxPrivate *priv; priv = TEST_COGLBOX_GET_PRIVATE (object); cogl_object_unref (priv->not_sliced_tex); cogl_object_unref (priv->sliced_tex); G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); } static void test_coglbox_init (TestCoglbox *self) { TestCoglboxPrivate *priv; GError *error = NULL; gchar *file; self->priv = priv = TEST_COGLBOX_GET_PRIVATE (self); priv->use_linear_filtering = FALSE; priv->use_sliced = FALSE; file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); priv->sliced_tex = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, &error); if (priv->sliced_tex == NULL) { if (error) { g_warning ("Texture loading failed: %s", error->message); g_error_free (error); error = NULL; } else g_warning ("Texture loading failed: "); } priv->not_sliced_tex = cogl_texture_new_from_file (file, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY, &error); if (priv->not_sliced_tex == NULL) { if (error) { g_warning ("Texture loading failed: %s", error->message); g_error_free (error); } else g_warning ("Texture loading failed: "); } g_free (file); } static void test_coglbox_class_init (TestCoglboxClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->finalize = test_coglbox_finalize; gobject_class->dispose = test_coglbox_dispose; actor_class->paint = test_coglbox_paint; } static ClutterActor* test_coglbox_new (void) { return g_object_new (TEST_TYPE_COGLBOX, NULL); } static void frame_cb (ClutterTimeline *timeline, gint elapsed_msecs, gpointer data) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (data); gdouble progress = clutter_timeline_get_progress (timeline); priv->frame = 360.0 * progress; clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); } static void update_toggle_text (ClutterText *button, gboolean val) { clutter_text_set_text (button, val ? "Enabled" : "Disabled"); } static gboolean on_toggle_click (ClutterActor *button, ClutterEvent *event, gboolean *toggle_val) { update_toggle_text (CLUTTER_TEXT (button), *toggle_val = !*toggle_val); return TRUE; } static ClutterActor * make_toggle (const char *label_text, gboolean *toggle_val) { ClutterActor *group = clutter_group_new (); ClutterActor *label = clutter_text_new_with_text ("Sans 14", label_text); ClutterActor *button = clutter_text_new_with_text ("Sans 14", ""); clutter_actor_set_reactive (button, TRUE); update_toggle_text (CLUTTER_TEXT (button), *toggle_val); clutter_actor_set_position (button, clutter_actor_get_width (label) + 10, 0); clutter_container_add (CLUTTER_CONTAINER (group), label, button, NULL); g_signal_connect (button, "button-press-event", G_CALLBACK (on_toggle_click), toggle_val); return group; } G_MODULE_EXPORT int test_cogl_tex_polygon_main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *coglbox; ClutterActor *filtering_toggle; ClutterActor *slicing_toggle; ClutterActor *note; ClutterTimeline *timeline; ClutterColor blue = { 0x30, 0x30, 0xff, 0xff }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Stage */ stage = clutter_stage_new (); clutter_stage_set_color (CLUTTER_STAGE (stage), &blue); clutter_actor_set_size (stage, 640, 480); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Texture Polygon"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* Cogl Box */ coglbox = test_coglbox_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); /* Timeline for animation */ timeline = clutter_timeline_new (6000); clutter_timeline_set_loop (timeline, TRUE); g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox); clutter_timeline_start (timeline); /* Labels for toggling settings */ slicing_toggle = make_toggle ("Texture slicing: ", &(TEST_COGLBOX_GET_PRIVATE (coglbox) ->use_sliced)); clutter_actor_set_position (slicing_toggle, 0, clutter_actor_get_height (stage) - clutter_actor_get_height (slicing_toggle)); filtering_toggle = make_toggle ("Linear filtering: ", &(TEST_COGLBOX_GET_PRIVATE (coglbox) ->use_linear_filtering)); clutter_actor_set_position (filtering_toggle, 0, clutter_actor_get_y (slicing_toggle) - clutter_actor_get_height (filtering_toggle)); note = clutter_text_new_with_text ("Sans 10", "<- Click to change"); clutter_actor_set_position (note, clutter_actor_get_width (filtering_toggle) + 10, (clutter_actor_get_height (stage) + clutter_actor_get_y (filtering_toggle)) / 2 - clutter_actor_get_height (note) / 2); clutter_container_add (CLUTTER_CONTAINER (stage), slicing_toggle, filtering_toggle, note, NULL); clutter_actor_show (stage); clutter_main (); return 0; } G_MODULE_EXPORT const char * test_cogl_tex_polygon_describe (void) { return "Texture polygon primitive."; } muffin-6.4.1/src/tests/clutter/interactive/test-shader-effects.c0000664000175000017500000000621714723361714023704 0ustar fabiofabio#include #include #include #include #include #include #include "test-utils.h" int test_shader_effects_main (int argc, char *argv[]); G_MODULE_EXPORT int test_shader_effects_main (int argc, char *argv[]) { ClutterTimeline *timeline; ClutterActor *stage, *hand, *label, *rect; gchar *file; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Make a timeline */ timeline = clutter_timeline_new (7692); clutter_timeline_set_repeat_count (timeline, -1); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Rotations"); clutter_actor_set_background_color (stage, CLUTTER_COLOR_Aluminium3); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* Make a hand */ file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); hand = clutter_test_utils_create_texture_from_file (file, NULL); if (!hand) g_error("Unable to load '%s'", file); g_free (file); clutter_actor_set_position (hand, 326, 265); clutter_actor_add_effect_with_name (hand, "desaturate", clutter_desaturate_effect_new (0.75)); clutter_actor_add_effect_with_name (hand, "blur", clutter_blur_effect_new ()); clutter_actor_animate_with_timeline (hand, CLUTTER_LINEAR, timeline, "@effects.desaturate.factor", 1.0, "rotation-angle-z", 360.0, "fixed::anchor-x", 86.0, "fixed::anchor-y", 125.0, "opacity", 128, NULL); rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_DarkOrange); clutter_actor_add_effect_with_name (rect, "blur", clutter_blur_effect_new ()); clutter_actor_set_position (rect, 415, 215); clutter_actor_set_size (rect, 150, 150); clutter_actor_animate_with_timeline (rect, CLUTTER_LINEAR, timeline, "rotation-angle-z", 360.0, "fixed::anchor-x", 75.0, "fixed::anchor-y", 75.0, NULL); label = clutter_text_new_with_text ("Mono 16", "The Wonder\n" "of the\n" "Spinning Hand"); clutter_text_set_line_alignment (CLUTTER_TEXT (label), PANGO_ALIGN_CENTER); clutter_actor_set_position (label, 336, 275); clutter_actor_set_size (label, 500, 100); clutter_actor_animate_with_timeline (label, CLUTTER_LINEAR, timeline, "rotation-angle-z", 360.0, "fixed::anchor-x", 86.0, "fixed::anchor-y", 125.0, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), rect, hand, label, NULL); /* start the timeline and thus the animations */ clutter_timeline_start (timeline); clutter_actor_show_all (stage); clutter_main(); g_object_unref (timeline); return 0; } muffin-6.4.1/src/tests/clutter/interactive/test-animation.c0000664000175000017500000000724214723361714022777 0ustar fabiofabio#include #include #include static gboolean is_expanded = FALSE; int test_animation_main (int argc, char *argv[]); const char * test_animation_describe (void); static void on_rect_transitions_completed (ClutterActor *actor) { is_expanded = !is_expanded; g_print ("Animation complete\n"); clutter_actor_set_reactive (actor, TRUE); } static void on_clicked (ClutterClickAction *action, ClutterActor *actor, gpointer dummy G_GNUC_UNUSED) { gfloat old_x, old_y, new_x, new_y; gfloat old_width, old_height, new_width, new_height; gdouble new_angle; const ClutterColor *new_color; guint8 new_opacity; clutter_actor_get_position (actor, &old_x, &old_y); clutter_actor_get_size (actor, &old_width, &old_height); /* determine the final state of the animation depending on * the state of the actor */ if (!is_expanded) { new_x = old_x - 100; new_y = old_y - 100; new_width = old_width + 200; new_height = old_height + 200; new_angle = 360.0; new_color = CLUTTER_COLOR_DarkScarletRed; new_opacity = 255; } else { new_x = old_x + 100; new_y = old_y + 100; new_width = old_width - 200; new_height = old_height - 200; new_angle = 0.0; new_color = CLUTTER_COLOR_LightOrange; new_opacity = 128; } clutter_actor_save_easing_state (actor); clutter_actor_set_easing_mode (actor, CLUTTER_EASE_IN_EXPO); clutter_actor_set_easing_duration (actor, 2000); clutter_actor_set_position (actor, new_x, new_y); clutter_actor_set_size (actor, new_width, new_height); clutter_actor_set_background_color (actor, new_color); clutter_actor_set_rotation_angle (actor, CLUTTER_Z_AXIS, new_angle); clutter_actor_set_reactive (actor, FALSE); /* animate the opacity halfway through, with a different pacing */ clutter_actor_save_easing_state (actor); clutter_actor_set_easing_mode (actor, CLUTTER_LINEAR); clutter_actor_set_easing_delay (actor, 1000); clutter_actor_set_easing_duration (actor, 1000); clutter_actor_set_opacity (actor, new_opacity); clutter_actor_restore_easing_state (actor); clutter_actor_restore_easing_state (actor); } G_MODULE_EXPORT int test_animation_main (int argc, char *argv[]) { ClutterActor *stage, *rect; ClutterAction *action; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue); clutter_stage_set_title (CLUTTER_STAGE (stage), "Animation"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); rect = clutter_actor_new (); clutter_actor_set_background_color (rect, CLUTTER_COLOR_LightOrange); clutter_actor_add_child (stage, rect); clutter_actor_set_size (rect, 50, 50); clutter_actor_set_pivot_point (rect, .5f, .5f); clutter_actor_set_translation (rect, -25, -25, 0); clutter_actor_set_position (rect, clutter_actor_get_width (stage) / 2, clutter_actor_get_height (stage) / 2); clutter_actor_set_opacity (rect, 128); clutter_actor_set_reactive (rect, TRUE); g_signal_connect (rect, "transitions-completed", G_CALLBACK (on_rect_transitions_completed), NULL); action = clutter_click_action_new (); g_signal_connect (action, "clicked", G_CALLBACK (on_clicked), NULL); clutter_actor_add_action_with_name (rect, "click", action); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_animation_describe (void) { return "Simple animation demo"; } muffin-6.4.1/src/tests/clutter/interactive/test-keyframe-transition.c0000664000175000017500000000710314723361714025007 0ustar fabiofabio#include #include #include static const ClutterColor colors[] = { { 255, 0, 0, 255 }, { 0, 255, 0, 255 }, { 0, 0, 255, 255 }, }; #define PADDING (64.0f) #define SIZE (64.0f) const char * test_keyframe_transition_describe (void); int test_keyframe_transition_main (int argc, char *argv[]); static void on_transition_stopped (ClutterActor *actor, const gchar *transition_name, gboolean is_finished) { g_print ("%s: transition stopped: %s (finished: %s)\n", clutter_actor_get_name (actor), transition_name, is_finished ? "yes" : "no"); } G_MODULE_EXPORT const char * test_keyframe_transition_describe (void) { return "Demonstrate the keyframe transition."; } G_MODULE_EXPORT int test_keyframe_transition_main (int argc, char *argv[]) { ClutterActor *stage; int i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Keyframe Transitions"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); for (i = 0; i < 3; i++) { ClutterTransition *transition, *group; ClutterActor *rect; float cur_x, cur_y; float new_x, new_y; gchar *name; cur_x = PADDING; cur_y = PADDING + ((SIZE + PADDING) * i); new_x = clutter_actor_get_width (stage) - PADDING - SIZE; new_y = g_random_double_range (PADDING, clutter_actor_get_height (stage) - PADDING - SIZE); name = g_strdup_printf ("rect%02d", i); rect = clutter_actor_new (); clutter_actor_set_name (rect, name); clutter_actor_set_background_color (rect, &colors[i]); clutter_actor_set_size (rect, SIZE, SIZE); clutter_actor_set_position (rect, PADDING, cur_y); clutter_actor_add_child (stage, rect); group = clutter_transition_group_new (); clutter_timeline_set_duration (CLUTTER_TIMELINE (group), 2000); clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (group), 1); clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (group), TRUE); transition = clutter_keyframe_transition_new ("x"); clutter_transition_set_from (transition, G_TYPE_FLOAT, cur_x); clutter_transition_set_to (transition, G_TYPE_FLOAT, new_x); clutter_keyframe_transition_set (CLUTTER_KEYFRAME_TRANSITION (transition), G_TYPE_FLOAT, 1, 0.5, new_x / 2.0f, CLUTTER_EASE_OUT_EXPO); clutter_transition_group_add_transition (CLUTTER_TRANSITION_GROUP (group), transition); g_object_unref (transition); transition = clutter_keyframe_transition_new ("y"); clutter_transition_set_from (transition, G_TYPE_FLOAT, cur_y); clutter_transition_set_to (transition, G_TYPE_FLOAT, cur_y); clutter_keyframe_transition_set (CLUTTER_KEYFRAME_TRANSITION (transition), G_TYPE_FLOAT, 1, 0.5, new_y, CLUTTER_EASE_OUT_EXPO); clutter_transition_group_add_transition (CLUTTER_TRANSITION_GROUP (group), transition); g_object_unref (transition); clutter_actor_add_transition (rect, "rectAnimation", group); g_signal_connect (rect, "transition-stopped", G_CALLBACK (on_transition_stopped), NULL); g_object_unref (group); g_free (name); } clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-layout.c0000664000175000017500000004605514723361714022342 0ustar fabiofabio#include #include #include #include #include #include "test-utils.h" /* layout actor, by Lucas Rocha */ #define MY_TYPE_THING (my_thing_get_type ()) #define MY_THING(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), MY_TYPE_THING, MyThing)) #define MY_IS_THING(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), MY_TYPE_THING)) #define MY_THING_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), MY_TYPE_THING, MyThingClass)) #define MY_IS_THING_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), MY_TYPE_THING)) #define MY_THING_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), MY_TYPE_THING, MyThingClass)) typedef struct _MyThing MyThing; typedef struct _MyThingPrivate MyThingPrivate; typedef struct _MyThingClass MyThingClass; struct _MyThing { ClutterActor parent_instance; MyThingPrivate *priv; }; struct _MyThingClass { ClutterActorClass parent_class; }; enum { PROP_0, PROP_SPACING, PROP_PADDING, PROP_USE_TRANSFORMED_BOX }; struct _MyThingPrivate { gfloat spacing; gfloat padding; guint use_transformed_box : 1; }; GType my_thing_get_type (void); int test_layout_main (int argc, char *argv[]); const char * test_layout_describe (void); G_DEFINE_TYPE_WITH_PRIVATE (MyThing, my_thing, CLUTTER_TYPE_ACTOR) #define MY_THING_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE ((obj), MY_TYPE_THING, MyThingPrivate)) static void my_thing_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { MyThingPrivate *priv = MY_THING (gobject)->priv; gboolean needs_relayout = TRUE; switch (prop_id) { case PROP_SPACING: priv->spacing = g_value_get_float (value); break; case PROP_PADDING: priv->padding = g_value_get_float (value); break; case PROP_USE_TRANSFORMED_BOX: priv->use_transformed_box = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); needs_relayout = FALSE; break; } /* setting spacing or padding queues a relayout because they are supposed to change the internal allocation of children */ if (needs_relayout) clutter_actor_queue_relayout (CLUTTER_ACTOR (gobject)); } static void my_thing_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { MyThingPrivate *priv = MY_THING (gobject)->priv; switch (prop_id) { case PROP_SPACING: g_value_set_float (value, priv->spacing); break; case PROP_PADDING: g_value_set_float (value, priv->padding); break; case PROP_USE_TRANSFORMED_BOX: g_value_set_boolean (value, priv->use_transformed_box); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void my_thing_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { ClutterActorIter iter; ClutterActor *child; gfloat min_left, min_right; gfloat natural_left, natural_right; min_left = 0; min_right = 0; natural_left = 0; natural_right = 0; clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_x, child_min, child_natural; child_x = clutter_actor_get_x (child); clutter_actor_get_preferred_size (child, &child_min, NULL, &child_natural, NULL); if (child == clutter_actor_get_first_child (self)) { /* First child */ min_left = child_x; natural_left = child_x; min_right = min_left + child_min; natural_right = natural_left + child_natural; } else { /* Union of extents with previous children */ if (child_x < min_left) min_left = child_x; if (child_x < natural_left) natural_left = child_x; if (child_x + child_min > min_right) min_right = child_x + child_min; if (child_x + child_natural > natural_right) natural_right = child_x + child_natural; } } if (min_left < 0) min_left = 0; if (natural_left < 0) natural_left = 0; if (min_right < 0) min_right = 0; if (natural_right < 0) natural_right = 0; g_assert (min_right >= min_left); g_assert (natural_right >= natural_left); if (min_width_p) *min_width_p = min_right - min_left; if (natural_width_p) *natural_width_p = natural_right - min_left; } static void my_thing_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { ClutterActorIter iter; ClutterActor *child; gfloat min_top, min_bottom; gfloat natural_top, natural_bottom; min_top = 0; min_bottom = 0; natural_top = 0; natural_bottom = 0; clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { gfloat child_y, child_min, child_natural; child_y = clutter_actor_get_y (child); clutter_actor_get_preferred_size (child, NULL, &child_min, NULL, &child_natural); if (child == clutter_actor_get_first_child (self)) { /* First child */ min_top = child_y; natural_top = child_y; min_bottom = min_top + child_min; natural_bottom = natural_top + child_natural; } else { /* Union of extents with previous children */ if (child_y < min_top) min_top = child_y; if (child_y < natural_top) natural_top = child_y; if (child_y + child_min > min_bottom) min_bottom = child_y + child_min; if (child_y + child_natural > natural_bottom) natural_bottom = child_y + child_natural; } } if (min_top < 0) min_top = 0; if (natural_top < 0) natural_top = 0; if (min_bottom < 0) min_bottom = 0; if (natural_bottom < 0) natural_bottom = 0; g_assert (min_bottom >= min_top); g_assert (natural_bottom >= natural_top); if (min_height_p) *min_height_p = min_bottom - min_top; if (natural_height_p) *natural_height_p = natural_bottom - min_top; } static void my_thing_allocate (ClutterActor *self, const ClutterActorBox *box, ClutterAllocationFlags flags) { MyThingPrivate *priv; gfloat current_x, current_y, max_row_height; ClutterActorIter iter; ClutterActor *child; clutter_actor_set_allocation (self, box, flags); priv = MY_THING (self)->priv; current_x = priv->padding; current_y = priv->padding; max_row_height = 0; /* The allocation logic here is to horizontally place children * side-by-side and reflow into a new row when we run out of * space */ clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { gfloat natural_width, natural_height; ClutterActorBox child_box; clutter_actor_get_preferred_size (child, NULL, NULL, &natural_width, &natural_height); /* if it fits in the current row, keep it there; otherwise * reflow into another row */ if (current_x + natural_width > box->x2 - box->x1 - priv->padding) { current_x = priv->padding; current_y += max_row_height + priv->spacing; max_row_height = 0; } child_box.x1 = current_x; child_box.y1 = current_y; child_box.x2 = child_box.x1 + natural_width; child_box.y2 = child_box.y1 + natural_height; clutter_actor_allocate (child, &child_box, flags); /* if we take into account the transformation of the children * then we first check if it's transformed; then we get the * onscreen coordinates of the two points of the bounding box * of the actor (origin(x, y) and (origin + size)(x,y)) and * we update the coordinates and area given to the next child */ if (priv->use_transformed_box) { if (clutter_actor_is_scaled (child) || clutter_actor_is_rotated (child)) { graphene_point3d_t v1 = { 0, }, v2 = { 0, }; ClutterActorBox transformed_box = { 0, }; /* origin */ if (!(flags & CLUTTER_ABSOLUTE_ORIGIN_CHANGED)) { v1.x = 0; v1.y = 0; } else { v1.x = box->x1; v1.y = box->y1; } clutter_actor_apply_transform_to_point (child, &v1, &v2); transformed_box.x1 = v2.x; transformed_box.y1 = v2.y; /* size */ v1.x = natural_width; v1.y = natural_height; clutter_actor_apply_transform_to_point (child, &v1, &v2); transformed_box.x2 = v2.x; transformed_box.y2 = v2.y; natural_width = transformed_box.x2 - transformed_box.x1; natural_height = transformed_box.y2 - transformed_box.y1; } } /* Record the maximum child height on current row to know * what's the increment that should be used for the next * row */ if (natural_height > max_row_height) max_row_height = natural_height; current_x += natural_width + priv->spacing; } } #define MIN_SIZE 24 #define MAX_SIZE 64 static void my_thing_class_init (MyThingClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->set_property = my_thing_set_property; gobject_class->get_property = my_thing_get_property; actor_class->get_preferred_width = my_thing_get_preferred_width; actor_class->get_preferred_height = my_thing_get_preferred_height; actor_class->allocate = my_thing_allocate; g_object_class_install_property (gobject_class, PROP_SPACING, g_param_spec_float ("spacing", "Spacing", "Spacing of the thing", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_PADDING, g_param_spec_float ("padding", "Padding", "Padding around the thing", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE)); g_object_class_install_property (gobject_class, PROP_USE_TRANSFORMED_BOX, g_param_spec_boolean ("use-transformed-box", "Use Transformed Box", "Use transformed box when allocating", FALSE, G_PARAM_READWRITE)); } static void my_thing_init (MyThing *thing) { thing->priv = MY_THING_GET_PRIVATE (thing); } static ClutterActor * my_thing_new (gfloat padding, gfloat spacing) { return g_object_new (MY_TYPE_THING, "padding", padding, "spacing", spacing, NULL); } /* test code */ static ClutterActor *box = NULL; static ClutterActor *icon = NULL; static ClutterTimeline *main_timeline = NULL; static void toggle_property_value (ClutterActor *actor, const gchar *property_name) { gboolean value; g_object_get (actor, property_name, &value, NULL); value = !value; g_object_set (box, property_name, value, NULL); } static void increase_property_value (ClutterActor *actor, const char *property_name) { gfloat value; g_object_get (actor, property_name, &value, NULL); value = value + 10.0; g_object_set (box, property_name, value, NULL); } static void decrease_property_value (ClutterActor *actor, const char *property_name) { gfloat value; g_object_get (actor, property_name, &value, NULL); value = MAX (0, value - 10.0); g_object_set (box, property_name, value, NULL); } static ClutterActor * create_item (void) { ClutterActor *clone = clutter_clone_new (icon); gint32 size = g_random_int_range (MIN_SIZE, MAX_SIZE); clutter_actor_set_size (clone, size, size); clutter_actor_animate_with_timeline (clone, CLUTTER_EASE_OUT_CUBIC, main_timeline, "scale-x", 2.0, "scale-y", 2.0, "fixed::scale-gravity", CLUTTER_GRAVITY_CENTER, NULL); return clone; } static gboolean keypress_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { switch (clutter_event_get_key_symbol (event)) { case CLUTTER_KEY_q: clutter_main_quit (); break; case CLUTTER_KEY_a: { if (icon != NULL) { ClutterActor *clone = create_item (); /* Add one item to container */ clutter_actor_add_child (box, clone); } break; } case CLUTTER_KEY_d: { ClutterActor *last_child; last_child = clutter_actor_get_last_child (box); if (last_child != NULL) { /* Remove last item on container */ clutter_actor_remove_child (box, last_child); } break; } case CLUTTER_KEY_w: { decrease_property_value (box, "padding"); break; } case CLUTTER_KEY_e: { increase_property_value (box, "padding"); break; } case CLUTTER_KEY_r: { decrease_property_value (box, "spacing"); break; } case CLUTTER_KEY_s: { toggle_property_value (box, "use-transformed-box"); break; } case CLUTTER_KEY_t: { increase_property_value (box, "spacing"); break; } case CLUTTER_KEY_z: { if (clutter_timeline_is_playing (main_timeline)) clutter_timeline_pause (main_timeline); else clutter_timeline_start (main_timeline); break; } default: break; } return FALSE; } static void relayout_on_frame (ClutterTimeline *timeline) { gboolean use_transformed_box; /* if we care about transformations updating the layout, we need to inform * the layout that a transformation is happening; this is either done by * attaching a notification on the transformation properties or by simply * queuing a relayout on each frame of the timeline used to drive the * behaviour. for simplicity's sake, we used the latter */ g_object_get (G_OBJECT (box), "use-transformed-box", &use_transformed_box, NULL); if (use_transformed_box) clutter_actor_queue_relayout (box); } G_MODULE_EXPORT int test_layout_main (int argc, char *argv[]) { ClutterActor *stage, *instructions; gint i, size; GError *error = NULL; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_set_size (stage, 800, 600); clutter_stage_set_title (CLUTTER_STAGE (stage), "Layout"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); main_timeline = clutter_timeline_new (2000); clutter_timeline_set_repeat_count (main_timeline, -1); clutter_timeline_set_auto_reverse (main_timeline, TRUE); g_signal_connect (main_timeline, "new-frame", G_CALLBACK (relayout_on_frame), NULL); box = my_thing_new (10, 10); clutter_actor_set_position (box, 20, 20); clutter_actor_set_size (box, 350, -1); icon = clutter_test_utils_create_texture_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", &error); if (error) g_error ("Unable to load 'redhand.png': %s", error->message); size = g_random_int_range (MIN_SIZE, MAX_SIZE); clutter_actor_set_size (icon, size, size); clutter_actor_add_child (box, icon); clutter_actor_animate_with_timeline (icon, CLUTTER_EASE_OUT_CUBIC, main_timeline, "scale-x", 2.0, "scale-y", 2.0, "fixed::scale-gravity", CLUTTER_GRAVITY_CENTER, NULL); for (i = 1; i < 33; i++) { ClutterActor *clone = create_item (); clutter_actor_add_child (box, clone); } clutter_actor_add_child (stage, box); instructions = clutter_text_new_with_text (NULL, "Instructions:\n" "a - add a new item\n" "d - remove last item\n" "z - start/pause behaviour\n" "w - decrease padding\n" "e - increase padding\n" "r - decrease spacing\n" "t - increase spacing\n" "s - use transformed box\n" "q - quit"); clutter_text_set_use_markup (CLUTTER_TEXT (instructions), TRUE); clutter_actor_set_position (instructions, 450, 10); clutter_actor_add_child (stage, instructions); g_signal_connect (stage, "key-release-event", G_CALLBACK (keypress_cb), NULL); clutter_timeline_stop (main_timeline); clutter_actor_show (stage); clutter_main (); g_object_unref (main_timeline); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_layout_describe (void) { return "Container implementing a layout policy."; } muffin-6.4.1/src/tests/clutter/interactive/test-swipe-action.c0000664000175000017500000001436114723361714023422 0ustar fabiofabio#include #include enum { VERTICAL = 0, HORIZONTAL = 1, BOTH = 2 }; int test_swipe_action_main (int argc, char *argv[]); const char * test_swipe_action_describe (void); static void swept_cb (ClutterSwipeAction *action, ClutterActor *actor, ClutterSwipeDirection direction, gpointer data_) { guint axis = GPOINTER_TO_UINT (data_); gchar *direction_str = g_strdup (""); if (axis == HORIZONTAL && ((direction & CLUTTER_SWIPE_DIRECTION_UP) != 0 || (direction & CLUTTER_SWIPE_DIRECTION_DOWN) != 0)) { g_print ("discarding non-horizontal swipe on '%s'\n", clutter_actor_get_name (actor)); return; } if (axis == VERTICAL && ((direction & CLUTTER_SWIPE_DIRECTION_LEFT) != 0 || (direction & CLUTTER_SWIPE_DIRECTION_RIGHT) != 0)) { g_print ("discarding non-vertical swipe on '%s'\n", clutter_actor_get_name (actor)); return; } if (direction & CLUTTER_SWIPE_DIRECTION_UP) { char *old_str = direction_str; direction_str = g_strconcat (direction_str, " up", NULL); g_free (old_str); } if (direction & CLUTTER_SWIPE_DIRECTION_DOWN) { char *old_str = direction_str; direction_str = g_strconcat (direction_str, " down", NULL); g_free (old_str); } if (direction & CLUTTER_SWIPE_DIRECTION_LEFT) { char *old_str = direction_str; direction_str = g_strconcat (direction_str, " left", NULL); g_free (old_str); } if (direction & CLUTTER_SWIPE_DIRECTION_RIGHT) { char *old_str = direction_str; direction_str = g_strconcat (direction_str, " right", NULL); g_free (old_str); } g_print ("swept: '%s': %s\n", clutter_actor_get_name (actor), direction_str); g_free (direction_str); } static void gesture_cancel_cb (ClutterSwipeAction *action, ClutterActor *actor, gpointer user_data) { g_debug ("gesture cancelled: '%s'", clutter_actor_get_name (actor)); } static void attach_action (ClutterActor *actor, guint axis) { ClutterAction *action; action = g_object_new (CLUTTER_TYPE_SWIPE_ACTION, NULL); clutter_actor_add_action (actor, action); g_signal_connect (action, "swept", G_CALLBACK (swept_cb), GUINT_TO_POINTER (axis)); g_signal_connect (action, "gesture-cancel", G_CALLBACK (gesture_cancel_cb), NULL); } G_MODULE_EXPORT int test_swipe_action_main (int argc, char *argv[]) { ClutterActor *stage, *rect; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Swipe action"); clutter_actor_set_size (stage, 640, 480); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Red); clutter_actor_set_name (rect, "Vertical swipes"); clutter_actor_set_size (rect, 150, 150); clutter_actor_set_position (rect, 10, 100); clutter_actor_set_reactive (rect, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); attach_action (rect, VERTICAL); rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Blue); clutter_actor_set_name (rect, "Horizontal swipes"); clutter_actor_set_size (rect, 150, 150); clutter_actor_set_position (rect, 170, 100); clutter_actor_set_reactive (rect, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); attach_action (rect, HORIZONTAL); rect = clutter_rectangle_new_with_color (CLUTTER_COLOR_Green); clutter_actor_set_name (rect, "All swipes"); clutter_actor_set_size (rect, 150, 150); clutter_actor_set_position (rect, 330, 100); clutter_actor_set_reactive (rect, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); attach_action (rect, BOTH); { ClutterLayoutManager *layout = clutter_box_layout_new (); ClutterActor *box, *label; float offset; clutter_box_layout_set_vertical (CLUTTER_BOX_LAYOUT (layout), TRUE); clutter_box_layout_set_spacing (CLUTTER_BOX_LAYOUT (layout), 6); box = clutter_box_new (layout); label = clutter_text_new (); clutter_text_set_markup (CLUTTER_TEXT (label), "Red: vertical swipes only"); clutter_box_layout_pack (CLUTTER_BOX_LAYOUT (layout), label, TRUE, TRUE, TRUE, CLUTTER_BOX_ALIGNMENT_START, CLUTTER_BOX_ALIGNMENT_CENTER); label = clutter_text_new (); clutter_text_set_markup (CLUTTER_TEXT (label), "Blue: horizontal swipes only"); clutter_box_layout_pack (CLUTTER_BOX_LAYOUT (layout), label, TRUE, TRUE, TRUE, CLUTTER_BOX_ALIGNMENT_START, CLUTTER_BOX_ALIGNMENT_CENTER); label = clutter_text_new (); clutter_text_set_markup (CLUTTER_TEXT (label), "Green: both"); clutter_box_layout_pack (CLUTTER_BOX_LAYOUT (layout), label, TRUE, TRUE, TRUE, CLUTTER_BOX_ALIGNMENT_START, CLUTTER_BOX_ALIGNMENT_CENTER); offset = clutter_actor_get_height (stage) - clutter_actor_get_height (box) - 12.0; clutter_container_add_actor (CLUTTER_CONTAINER (stage), box); clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_X, 12.0)); clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_Y, offset)); } clutter_actor_show_all (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_swipe_action_describe (void) { return "Swipe gesture recognizer."; } muffin-6.4.1/src/tests/clutter/interactive/test-state-script.c0000664000175000017500000000232614723361714023440 0ustar fabiofabio#include #include #include #define TEST_STATE_SCRIPT_FILE "test-script-signals.json" int test_state_script_main (int argc, char *argv[]); G_MODULE_EXPORT int test_state_script_main (int argc, char *argv[]) { ClutterActor *stage, *button; ClutterScript *script; GError *error = NULL; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; script = clutter_script_new (); clutter_script_load_from_file (script, TEST_STATE_SCRIPT_FILE, &error); if (error != NULL) g_error ("Unable to load '%s': %s\n", TEST_STATE_SCRIPT_FILE, error->message); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "State Script"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_actor_show (stage); button = CLUTTER_ACTOR (clutter_script_get_object (script, "button")); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); clutter_actor_add_constraint (button, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5)); clutter_script_connect_signals (script, NULL); clutter_main (); g_object_unref (script); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-tex-tile.c0000664000175000017500000001403114723361714023467 0ustar fabiofabio#include #include #include #include #include #include /* Coglbox declaration *--------------------------------------------------*/ G_BEGIN_DECLS #define TEST_TYPE_COGLBOX test_coglbox_get_type() #define TEST_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ TEST_TYPE_COGLBOX, TestCoglbox)) #define TEST_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) #define TEST_IS_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ TEST_TYPE_COGLBOX)) #define TEST_IS_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ TEST_TYPE_COGLBOX)) #define TEST_COGLBOX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) typedef struct _TestCoglbox TestCoglbox; typedef struct _TestCoglboxClass TestCoglboxClass; typedef struct _TestCoglboxPrivate TestCoglboxPrivate; struct _TestCoglbox { ClutterActor parent; /*< private >*/ TestCoglboxPrivate *priv; }; struct _TestCoglboxClass { ClutterActorClass parent_class; /* padding for future expansion */ void (*_test_coglbox1) (void); void (*_test_coglbox2) (void); void (*_test_coglbox3) (void); void (*_test_coglbox4) (void); }; static GType test_coglbox_get_type (void) G_GNUC_CONST; int test_cogl_tex_tile_main (int argc, char *argv[]); const char * test_cogl_tex_tile_describe (void); G_END_DECLS /* Coglbox private declaration *--------------------------------------------------*/ struct _TestCoglboxPrivate { CoglHandle cogl_tex_id; gdouble animation_progress; }; G_DEFINE_TYPE_WITH_PRIVATE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); #define TEST_COGLBOX_GET_PRIVATE(obj) \ (test_coglbox_get_instance_private (TEST_COGLBOX ((obj)))) /* Coglbox implementation *--------------------------------------------------*/ static void test_coglbox_paint (ClutterActor *self, ClutterPaintContext *paint_context) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglPipeline *pipeline; gfloat texcoords[4] = { 0.0f, 0.0f, 1.0f, 1.0f }; gfloat angle; gfloat frac; gint t; angle = priv->animation_progress * 2 * G_PI; frac = ((priv->animation_progress <= 0.5f ? priv->animation_progress : 1.0f - priv->animation_progress) + 0.5f) * 2.0f; for (t=0; t<4; t+=2) { texcoords[t] += cos (angle); texcoords[t+1] += sin (angle); texcoords[t] *= frac; texcoords[t+1] *= frac; } priv = TEST_COGLBOX_GET_PRIVATE (self); cogl_framebuffer_push_matrix (framebuffer); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0x66, 0x66, 0xdd, 0xff); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, 400, 400); cogl_object_unref (pipeline); cogl_framebuffer_translate (framebuffer, 100, 100, 0); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_texture (pipeline, 0, priv->cogl_tex_id); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 200, 213, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_object_unref (pipeline); cogl_framebuffer_pop_matrix (framebuffer); } static void test_coglbox_finalize (GObject *object) { G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); } static void test_coglbox_dispose (GObject *object) { TestCoglboxPrivate *priv; priv = TEST_COGLBOX_GET_PRIVATE (object); cogl_object_unref (priv->cogl_tex_id); G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); } static void test_coglbox_init (TestCoglbox *self) { TestCoglboxPrivate *priv; gchar *file; self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); priv->cogl_tex_id = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, NULL); g_free (file); } static void test_coglbox_class_init (TestCoglboxClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->finalize = test_coglbox_finalize; gobject_class->dispose = test_coglbox_dispose; actor_class->paint = test_coglbox_paint; } static ClutterActor* test_coglbox_new (void) { return g_object_new (TEST_TYPE_COGLBOX, NULL); } static void frame_cb (ClutterTimeline *timeline, gint msecs, gpointer data) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (data); priv->animation_progress = clutter_timeline_get_progress (timeline); clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); } G_MODULE_EXPORT int test_cogl_tex_tile_main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *coglbox; ClutterTimeline *timeline; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Stage */ stage = clutter_stage_new (); clutter_actor_set_size (stage, 400, 400); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Texture Tiling"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* Cogl Box */ coglbox = test_coglbox_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); /* Timeline for animation */ timeline = clutter_timeline_new (6000); /* 6 second duration */ clutter_timeline_set_loop (timeline, TRUE); g_signal_connect (timeline, "new-frame", G_CALLBACK (frame_cb), coglbox); clutter_timeline_start (timeline); clutter_actor_show_all (stage); clutter_main (); return 0; } G_MODULE_EXPORT const char * test_cogl_tex_tile_describe (void) { return "Texture tiling."; } muffin-6.4.1/src/tests/clutter/interactive/test-paint-wrapper.c0000664000175000017500000002174514723361714023615 0ustar fabiofabio#include #include #if defined (_MSC_VER) && !defined (_USE_MATH_DEFINES) #define _USE_MATH_DEFINES #endif #include #include #include #include #ifdef CLUTTER_WINDOWING_X11 #include "clutter/x11/clutter-x11.h" #endif #include "test-utils.h" #define NHANDS 6 typedef struct SuperOH { ClutterActor **hand, *bgtex; ClutterActor *real_hand; ClutterActor *group; ClutterActor *stage; gint stage_width; gint stage_height; gfloat radius; ClutterTimeline *timeline; gulong frame_id; gboolean *paint_guards; } SuperOH; static gint n_hands = NHANDS; static gint use_alpha = 255; static GOptionEntry super_oh_entries[] = { { "num-hands", 'n', 0, G_OPTION_ARG_INT, &n_hands, "Number of hands", "HANDS" }, { "use-alpha", 'a', 0, G_OPTION_ARG_INT, &use_alpha, "Stage opacity", "VALUE" }, { NULL } }; int test_paint_wrapper_main (int argc, char *argv[]); const char * test_paint_wrapper_describe (void); static gboolean on_button_press_event (ClutterActor *actor, ClutterEvent *event, SuperOH *oh) { gfloat x, y; clutter_event_get_coords (event, &x, &y); g_print ("*** button press event (button:%d) at %.2f, %.2f ***\n", clutter_event_get_button (event), x, y); clutter_actor_hide (actor); return TRUE; } static gboolean input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data) { SuperOH *oh = data; if (event->type == CLUTTER_KEY_RELEASE) { g_print ("*** key press event (key:%c) ***\n", clutter_event_get_key_symbol (event)); if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_q) { clutter_main_quit (); return TRUE; } else if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_r) { gint i; for (i = 0; i < n_hands; i++) clutter_actor_show (oh->hand[i]); return TRUE; } } return FALSE; } /* Timeline handler */ static void frame_cb (ClutterTimeline *timeline, gint msecs, gpointer data) { SuperOH *oh = data; gint i; float rotation = clutter_timeline_get_progress (timeline) * 360.0f; /* Rotate everything clockwise about stage center*/ clutter_actor_set_rotation (oh->group, CLUTTER_Z_AXIS, rotation, oh->stage_width / 2, oh->stage_height / 2, 0); for (i = 0; i < n_hands; i++) { /* Rotate each hand around there centers - to get this we need * to take into account any scaling. */ clutter_actor_set_rotation (oh->hand[i], CLUTTER_Z_AXIS, -6.0 * rotation, 0, 0, 0); } } static void hand_pre_paint (ClutterActor *actor, ClutterPaintContext *paint_context, gpointer user_data) { CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglPipeline *pipeline; SuperOH *oh = user_data; gfloat w, h; int actor_num; for (actor_num = 0; oh->hand[actor_num] != actor; actor_num++) ; g_assert (oh->paint_guards[actor_num] == FALSE); clutter_actor_get_size (actor, &w, &h); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, 128); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, w / 2, h / 2); cogl_object_unref (pipeline); oh->paint_guards[actor_num] = TRUE; } static void hand_post_paint (ClutterActor *actor, ClutterPaintContext *paint_context, gpointer user_data) { CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); CoglPipeline *pipeline; SuperOH *oh = user_data; gfloat w, h; int actor_num; for (actor_num = 0; oh->hand[actor_num] != actor; actor_num++) ; g_assert (oh->paint_guards[actor_num] == TRUE); clutter_actor_get_size (actor, &w, &h); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0, 255, 0, 128); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, w / 2, h / 2, w, h); cogl_object_unref (pipeline); oh->paint_guards[actor_num] = FALSE; } static void stop_and_quit (ClutterActor *actor, SuperOH *oh) { g_clear_signal_handler (&oh->frame_id, oh->timeline); clutter_timeline_stop (oh->timeline); clutter_main_quit (); } G_MODULE_EXPORT int test_paint_wrapper_main (int argc, char *argv[]) { ClutterActor *stage; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; SuperOH *oh; gint i; GError *error; ClutterActor *real_hand; error = NULL; #ifdef CLUTTER_WINDOWING_X11 clutter_x11_set_use_argb_visual (TRUE); #endif if (clutter_init_with_args (&argc, &argv, NULL, super_oh_entries, NULL, &error) != CLUTTER_INIT_SUCCESS) { g_warning ("Unable to initialise Clutter:\n%s", error->message); g_error_free (error); return EXIT_FAILURE; } oh = g_new(SuperOH, 1); stage = clutter_stage_new (); clutter_actor_set_size (stage, 800, 600); if (use_alpha != 255) { clutter_stage_set_use_alpha (CLUTTER_STAGE (stage), TRUE); clutter_actor_set_opacity (stage, use_alpha); } clutter_stage_set_title (CLUTTER_STAGE (stage), "Paint Test"); clutter_actor_set_background_color (stage, &stage_color); g_signal_connect (stage, "destroy", G_CALLBACK (stop_and_quit), oh); oh->stage = stage; /* Create a timeline to manage animation */ oh->timeline = clutter_timeline_new (6000); clutter_timeline_set_repeat_count (oh->timeline, -1); /* fire a callback for frame change */ oh->frame_id = g_signal_connect (oh->timeline, "new-frame", G_CALLBACK (frame_cb), oh); real_hand = clutter_test_utils_create_texture_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", &error); if (real_hand == NULL) { g_error ("image load failed: %s", error->message); return EXIT_FAILURE; } /* create a new group to hold multiple actors in a group */ oh->group = clutter_group_new(); oh->hand = g_new (ClutterActor*, n_hands); oh->stage_width = clutter_actor_get_width (stage); oh->stage_height = clutter_actor_get_height (stage); oh->radius = (oh->stage_width + oh->stage_height) / n_hands; for (i = 0; i < n_hands; i++) { gint x, y, w, h; if (i == 0) oh->hand[i] = real_hand; else oh->hand[i] = clutter_clone_new (real_hand); clutter_actor_set_reactive (oh->hand[i], TRUE); clutter_actor_set_size (oh->hand[i], 200, 213); /* Place around a circle */ w = clutter_actor_get_width (oh->hand[i]); h = clutter_actor_get_height (oh->hand[i]); x = oh->stage_width / 2 + oh->radius * cos (i * G_PI / (n_hands / 2)) - w / 2; y = oh->stage_height / 2 + oh->radius * sin (i * G_PI / (n_hands / 2)) - h / 2; clutter_actor_set_position (oh->hand[i], x, y); clutter_actor_move_anchor_point_from_gravity (oh->hand[i], CLUTTER_GRAVITY_CENTER); g_signal_connect (oh->hand[i], "button-press-event", G_CALLBACK (on_button_press_event), oh); /* paint something before each hand */ g_signal_connect (oh->hand[i], "paint", G_CALLBACK (hand_pre_paint), oh); /* paint something after each hand */ g_signal_connect_after (oh->hand[i], "paint", G_CALLBACK (hand_post_paint), oh); /* Add to our group group */ clutter_container_add_actor (CLUTTER_CONTAINER (oh->group), oh->hand[i]); } oh->paint_guards = g_malloc0 (sizeof (gboolean) * n_hands); /* Add the group to the stage */ clutter_container_add_actor (CLUTTER_CONTAINER (stage), CLUTTER_ACTOR (oh->group)); /* Show everying ( and map window ) */ clutter_actor_show (stage); g_signal_connect (stage, "key-release-event", G_CALLBACK (input_cb), oh); /* and start it */ clutter_timeline_start (oh->timeline); clutter_main (); g_object_unref (oh->timeline); g_free (oh->paint_guards); g_free (oh->hand); g_free (oh); return 0; } G_MODULE_EXPORT const char * test_paint_wrapper_describe (void) { return "Wrap an actor's paint cycle for pre and post processing."; } muffin-6.4.1/src/tests/clutter/interactive/test-scrolling.c0000664000175000017500000001031514723361714023007 0ustar fabiofabio#include #include #include #include #define RECT_WIDTH 400 #define RECT_HEIGHT 300 #define N_RECTS 7 static const gchar *rect_color[N_RECTS] = { "#edd400", "#f57900", "#c17d11", "#73d216", "#3465a4", "#75507b", "#cc0000" }; static ClutterActor *rectangle[N_RECTS]; static ClutterActor *viewport = NULL; int test_scrolling_main (int argc, char *argv[]); static void on_drag_end (ClutterDragAction *action, ClutterActor *actor, gfloat event_x, gfloat event_y, ClutterModifierType modifiers) { gfloat viewport_x = clutter_actor_get_x (viewport); gfloat offset_x; gint child_visible; /* check if we're at the viewport edges */ if (viewport_x > 0) { clutter_actor_save_easing_state (viewport); clutter_actor_set_easing_mode (viewport, CLUTTER_EASE_OUT_BOUNCE); clutter_actor_set_x (viewport, 0); clutter_actor_restore_easing_state (viewport); return; } if (viewport_x < (-1.0f * (RECT_WIDTH * (N_RECTS - 1)))) { clutter_actor_save_easing_state (viewport); clutter_actor_set_easing_mode (viewport, CLUTTER_EASE_OUT_BOUNCE); clutter_actor_set_x (viewport, -1.0f * (RECT_WIDTH * (N_RECTS - 1))); clutter_actor_restore_easing_state (viewport); return; } /* animate the viewport to fully show the child once we pass * a certain threshold with the dragging action */ offset_x = fabsf (viewport_x) / RECT_WIDTH + 0.5f; if (offset_x > (RECT_WIDTH * 0.33)) child_visible = (int) offset_x + 1; else child_visible = (int) offset_x; /* sanity check on the children number */ child_visible = CLAMP (child_visible, 0, N_RECTS); clutter_actor_save_easing_state (viewport); clutter_actor_set_x (viewport, -1.0f * RECT_WIDTH * child_visible); clutter_actor_restore_easing_state (viewport); } G_MODULE_EXPORT int test_scrolling_main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *scroll; ClutterAction *action; gint i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Scrolling"); clutter_actor_set_size (stage, 800, 600); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* scroll: the group that contains the scrolling viewport; we set its * size to be the same as one rectangle, position it in the middle of * the stage and set it to clip its contents to the allocated size */ scroll = clutter_actor_new (); clutter_actor_add_child (stage, scroll); clutter_actor_set_size (scroll, RECT_WIDTH, RECT_HEIGHT); clutter_actor_add_constraint (scroll, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5)); clutter_actor_set_clip_to_allocation (scroll, TRUE); /* viewport: the actual container for the children; we scroll it using * the Drag action constrained to the horizontal axis, and every time * the dragging ends we check whether we're dragging past the end of * the viewport */ viewport = clutter_actor_new (); clutter_actor_set_layout_manager (viewport, clutter_box_layout_new ()); clutter_actor_add_child (scroll, viewport); /* add dragging capabilities to the viewport; the heavy lifting is * all done by the DragAction itself, plus the ::drag-end signal * handler in our code */ action = clutter_drag_action_new (); clutter_actor_add_action (viewport, action); clutter_drag_action_set_drag_axis (CLUTTER_DRAG_ACTION (action), CLUTTER_DRAG_X_AXIS); g_signal_connect (action, "drag-end", G_CALLBACK (on_drag_end), NULL); clutter_actor_set_reactive (viewport, TRUE); /* children of the viewport */ for (i = 0; i < N_RECTS; i++) { ClutterColor color; clutter_color_from_string (&color, rect_color[i]); rectangle[i] = clutter_actor_new (); clutter_actor_set_background_color (rectangle[i], &color); clutter_actor_add_child (viewport, rectangle[i]); clutter_actor_set_size (rectangle[i], RECT_WIDTH, RECT_HEIGHT); } clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-touch-events.c0000664000175000017500000001263414723361714023445 0ustar fabiofabio/* * Copyright (C) 2012 Collabora Ltd. * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #define STAGE_WIDTH 800 #define STAGE_HEIGHT 550 #define NUM_COLORS 10 #define NUM_ACTORS 10 static GQueue events = G_QUEUE_INIT; static GQueue all_events = G_QUEUE_INIT; static gboolean new_surface = TRUE; static const ClutterColor static_colors[] = { { 0xff, 0x00, 0x00, 0xff }, /* red */ { 0x80, 0x00, 0x00, 0xff }, /* dark red */ { 0x00, 0xff, 0x00, 0xff }, /* green */ { 0x00, 0x80, 0x00, 0xff }, /* dark green */ { 0x00, 0x00, 0xff, 0xff }, /* blue */ { 0x00, 0x00, 0x80, 0xff }, /* dark blue */ { 0x00, 0xff, 0xff, 0xff }, /* cyan */ { 0x00, 0x80, 0x80, 0xff }, /* dark cyan */ { 0xff, 0x00, 0xff, 0xff }, /* magenta */ { 0xff, 0xff, 0x00, 0xff }, /* yellow */ }; static GHashTable *sequence_to_color = NULL; int test_touch_events_main (int argc, char *argv[]); const char * test_touch_events_describe (void); static void draw_touch (ClutterEvent *event, cairo_t *cr) { ClutterEventSequence *sequence = clutter_event_get_event_sequence (event); const ClutterColor *color; color = g_hash_table_lookup (sequence_to_color, sequence); if (color == NULL) { color = &static_colors[g_random_int_range (0, NUM_COLORS)]; g_hash_table_insert (sequence_to_color, (gpointer) sequence, (gpointer) color); } cairo_set_source_rgba (cr, color->red / 255, color->green / 255, color->blue / 255, color->alpha / 255); cairo_arc (cr, event->touch.x, event->touch.y, 5, 0, 2 * G_PI); cairo_fill (cr); } static gboolean draw_touches (ClutterCanvas *canvas, cairo_t *cr, int width, int height) { g_queue_foreach (new_surface ? &all_events : &events, (GFunc) draw_touch, cr); g_queue_clear (&events); new_surface = FALSE; return TRUE; } static gboolean event_cb (ClutterActor *actor, ClutterEvent *event, ClutterActor *canvas) { ClutterEvent *copy; if (event->type != CLUTTER_TOUCH_UPDATE) return FALSE; copy = clutter_event_copy (event); g_queue_push_tail (&events, copy); g_queue_push_tail (&all_events, copy); clutter_actor_queue_redraw (canvas); return TRUE; } static gboolean rect_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterColor color; if (event->type != CLUTTER_TOUCH_BEGIN) return FALSE; color = static_colors[g_random_int_range (0, NUM_COLORS)]; clutter_rectangle_set_color (CLUTTER_RECTANGLE (actor), &color); return TRUE; } G_MODULE_EXPORT int test_touch_events_main (int argc, char *argv[]) { ClutterActor *stage, *canvas_actor; ClutterContent *canvas; int i; /* initialize Clutter */ if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; /* create a resizable stage */ stage = clutter_stage_new (); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_stage_set_title (CLUTTER_STAGE (stage), "Touch events"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_actor_set_reactive (stage, TRUE); clutter_actor_show (stage); /* our 2D canvas, courtesy of Cairo */ canvas = clutter_canvas_new (); clutter_canvas_set_size (CLUTTER_CANVAS (canvas), STAGE_WIDTH, STAGE_HEIGHT); g_signal_connect (canvas, "draw", G_CALLBACK (draw_touches), NULL); canvas_actor = g_object_new (CLUTTER_TYPE_ACTOR, "content", canvas, NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), canvas_actor); g_signal_connect (stage, "event", G_CALLBACK (event_cb), canvas_actor); for (i = 0; i < NUM_ACTORS; i++) { gfloat size = STAGE_HEIGHT / NUM_ACTORS; ClutterColor color = static_colors[i % NUM_COLORS]; ClutterActor *rectangle = clutter_rectangle_new_with_color (&color); /* Test that event delivery to actors work */ g_signal_connect (rectangle, "event", G_CALLBACK (rect_event_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rectangle); clutter_actor_set_size (rectangle, size, size); clutter_actor_set_position (rectangle, 0, i * size); clutter_actor_set_reactive (rectangle, TRUE); } sequence_to_color = g_hash_table_new (NULL, NULL); clutter_main (); g_queue_foreach (&all_events, (GFunc) clutter_event_free, NULL); g_queue_clear (&events); g_queue_clear (&all_events); g_hash_table_destroy (sequence_to_color); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_touch_events_describe (void) { return "Draw shapes based on touch events"; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-shader-glsl.c0000664000175000017500000002505014723361714024144 0ustar fabiofabio#include #include #include #include #include typedef struct { const char *name; const char *source; } ShaderSource; int test_cogl_shader_glsl_main (int argc, char *argv[]); /* a couple of boilerplate defines that are common amongst all the * sample shaders */ /* FRAGMENT_SHADER_BEGIN: generate boilerplate with a local vec4 color already * initialized, from a sampler2D in a variable tex. */ #define FRAGMENT_SHADER_VARS \ "uniform sampler2D tex;" \ "uniform float x_step, y_step;" #define FRAGMENT_SHADER_BEGIN \ "void main (){" \ " vec4 color = texture2D (tex, vec2(cogl_tex_coord_in[0]));" /* FRAGMENT_SHADER_END: apply the changed color to the output buffer correctly * blended with the gl specified color (makes the opacity of actors work * correctly). */ #define FRAGMENT_SHADER_END \ " cogl_color_out = color;" \ " cogl_color_out = cogl_color_out * cogl_color_in;" \ "}" static ShaderSource shaders[]= { {"brightness-contrast", FRAGMENT_SHADER_VARS "uniform float brightness, contrast;" FRAGMENT_SHADER_BEGIN " color.rgb /= color.a;" " color.rgb = (color.rgb - vec3(0.5, 0.5, 0.5)) * contrast + " "vec3 (brightness + 0.5, brightness + 0.5, brightness + 0.5);" " color.rgb *= color.a;" FRAGMENT_SHADER_END }, {"box-blur", FRAGMENT_SHADER_VARS "vec4 get_rgba_rel(sampler2D tex, float dx, float dy)" "{" " return texture2D (tex, cogl_tex_coord_in[0].st " " + vec2(dx, dy) * 2.0);" "}" FRAGMENT_SHADER_BEGIN " float count = 1.0;" " color += get_rgba_rel (tex, -x_step, -y_step); count++;" " color += get_rgba_rel (tex, -x_step, 0.0); count++;" " color += get_rgba_rel (tex, -x_step, y_step); count++;" " color += get_rgba_rel (tex, 0.0, -y_step); count++;" " color += get_rgba_rel (tex, 0.0, 0.0); count++;" " color += get_rgba_rel (tex, 0.0, y_step); count++;" " color += get_rgba_rel (tex, x_step, -y_step); count++;" " color += get_rgba_rel (tex, x_step, 0.0); count++;" " color += get_rgba_rel (tex, x_step, y_step); count++;" " color = color / count;" FRAGMENT_SHADER_END }, {"invert", FRAGMENT_SHADER_VARS FRAGMENT_SHADER_BEGIN " color.rgb /= color.a;" " color.rgb = vec3(1.0, 1.0, 1.0) - color.rgb;\n" " color.rgb *= color.a;" FRAGMENT_SHADER_END }, {"brightness-contrast", FRAGMENT_SHADER_VARS "uniform float brightness;" "uniform float contrast;" FRAGMENT_SHADER_BEGIN " color.rgb /= color.a;" " color.r = (color.r - 0.5) * contrast + brightness + 0.5;" " color.g = (color.g - 0.5) * contrast + brightness + 0.5;" " color.b = (color.b - 0.5) * contrast + brightness + 0.5;" " color.rgb *= color.a;" FRAGMENT_SHADER_END }, {"gray", FRAGMENT_SHADER_VARS FRAGMENT_SHADER_BEGIN " float avg = (color.r + color.g + color.b) / 3.0;" " color.r = avg;" " color.g = avg;" " color.b = avg;" FRAGMENT_SHADER_END }, {"combined-mirror", FRAGMENT_SHADER_VARS FRAGMENT_SHADER_BEGIN " vec4 colorB = texture2D (tex, vec2(cogl_tex_coord_in[0].ts));" " float avg = (color.r + color.g + color.b) / 3.0;" " color.r = avg;" " color.g = avg;" " color.b = avg;" " color = (color + colorB)/2.0;" FRAGMENT_SHADER_END }, {"edge-detect", FRAGMENT_SHADER_VARS "float get_avg_rel(sampler2D texB, float dx, float dy)" "{" " vec4 colorB = texture2D (texB, cogl_tex_coord_in[0].st + vec2(dx, dy));" " return (colorB.r + colorB.g + colorB.b) / 3.0;" "}" FRAGMENT_SHADER_BEGIN " mat3 sobel_h = mat3( 1.0, 2.0, 1.0," " 0.0, 0.0, 0.0," " -1.0, -2.0, -1.0);" " mat3 sobel_v = mat3( 1.0, 0.0, -1.0," " 2.0, 0.0, -2.0," " 1.0, 0.0, -1.0);" " mat3 map = mat3( get_avg_rel(tex, -x_step, -y_step)," " get_avg_rel(tex, -x_step, 0.0)," " get_avg_rel(tex, -x_step, y_step)," " get_avg_rel(tex, 0.0, -y_step)," " get_avg_rel(tex, 0.0, 0.0)," " get_avg_rel(tex, 0.0, y_step)," " get_avg_rel(tex, x_step, -y_step)," " get_avg_rel(tex, x_step, 0.0)," " get_avg_rel(tex, x_step, y_step) );" " mat3 gh = sobel_h * map;" " mat3 gv = map * sobel_v;" " float avgh = (gh[0][0] + gh[0][1] + gh[0][2] +" " gh[1][0] + gh[1][1] + gh[1][2] +" " gh[2][0] + gh[2][1] + gh[2][2]) / 18.0 + 0.5;" " float avgv = (gv[0][0] + gv[0][1] + gv[0][2] +" " gv[1][0] + gv[1][1] + gv[1][2] +" " gv[2][0] + gv[2][1] + gv[2][2]) / 18.0 + 0.5;" " float avg = (avgh + avgv) / 2.0;" " color.r = avg * color.r;" " color.g = avg * color.g;" " color.b = avg * color.b;" FRAGMENT_SHADER_END } }; static CoglHandle redhand; static CoglMaterial *material; static unsigned int timeout_id = 0; static int shader_no = 0; static void paint_cb (ClutterActor *actor, ClutterPaintContext *paint_context) { CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); float stage_width = clutter_actor_get_width (actor); float stage_height = clutter_actor_get_height (actor); float image_width = cogl_texture_get_width (redhand); float image_height = cogl_texture_get_height (redhand); cogl_framebuffer_draw_rectangle (framebuffer, COGL_PIPELINE (material), stage_width / 2.0f - image_width / 2.0f, stage_height / 2.0f - image_height / 2.0f, stage_width / 2.0f + image_width / 2.0f, stage_height / 2.0f + image_height / 2.0f); } static void set_shader_num (int new_no) { CoglHandle shader; CoglHandle program; int image_width = cogl_texture_get_width (redhand); int image_height = cogl_texture_get_height (redhand); int uniform_no; g_print ("setting shaders[%i] named '%s'\n", new_no, shaders[new_no].name); shader = cogl_create_shader (COGL_SHADER_TYPE_FRAGMENT); cogl_shader_source (shader, shaders[new_no].source); program = cogl_create_program (); cogl_program_attach_shader (program, shader); cogl_object_unref (shader); cogl_program_link (program); uniform_no = cogl_program_get_uniform_location (program, "tex"); cogl_program_set_uniform_1i (program, uniform_no, 0); uniform_no = cogl_program_get_uniform_location (program, "radius"); cogl_program_set_uniform_1f (program, uniform_no, 3.0); uniform_no = cogl_program_get_uniform_location (program, "brightness"); cogl_program_set_uniform_1f (program, uniform_no, 0.4); uniform_no = cogl_program_get_uniform_location (program, "contrast"); cogl_program_set_uniform_1f (program, uniform_no, -1.9); uniform_no = cogl_program_get_uniform_location (program, "x_step"); cogl_program_set_uniform_1f (program, uniform_no, 1.0f / image_width); uniform_no = cogl_program_get_uniform_location (program, "y_step"); cogl_program_set_uniform_1f (program, uniform_no, 1.0f / image_height); cogl_material_set_user_program (material, program); cogl_object_unref (program); shader_no = new_no; } static gboolean button_release_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { int new_no; /* Stop the automatic cycling if the user want to manually control * which shader to display */ g_clear_handle_id (&timeout_id, g_source_remove); if (event->button.button == 1) { new_no = shader_no - 1; if (new_no < 0) new_no = G_N_ELEMENTS (shaders) - 1; } else { new_no = shader_no + 1; if (new_no >= G_N_ELEMENTS (shaders)) new_no = 0; } set_shader_num (new_no); return CLUTTER_EVENT_STOP; } static gboolean key_release_cb (ClutterActor *actor, ClutterEvent *event, gpointer user_data) { guint keysym = clutter_event_get_key_symbol (event); ClutterModifierType mods = clutter_event_get_state (event); if (keysym == CLUTTER_KEY_q || ((mods & CLUTTER_SHIFT_MASK) && keysym == CLUTTER_KEY_q)) clutter_main_quit (); return CLUTTER_EVENT_STOP; } static gboolean timeout_cb (gpointer user_data) { shader_no++; if (shader_no > (G_N_ELEMENTS (shaders) - 1)) shader_no = 0; set_shader_num (shader_no); return G_SOURCE_CONTINUE; } static gboolean idle_cb (gpointer data) { clutter_actor_queue_redraw (data); return G_SOURCE_CONTINUE; } static gboolean destroy_window_cb (ClutterStage *stage, ClutterEvent *event, gpointer user_data) { clutter_main_quit (); return CLUTTER_EVENT_STOP; } G_MODULE_EXPORT int test_cogl_shader_glsl_main (int argc, char *argv[]) { ClutterActor *stage; char *file; GError *error; ClutterColor stage_color = { 0x61, 0x64, 0x8c, 0xff }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Assembly Shader Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); error = NULL; redhand = cogl_texture_new_from_file (file, COGL_TEXTURE_NO_ATLAS, COGL_PIXEL_FORMAT_ANY, &error); if (redhand == NULL) g_error ("image load failed: %s", error->message); material = cogl_material_new (); cogl_material_set_layer (material, 0, redhand); set_shader_num (0); g_signal_connect_after (stage, "paint", G_CALLBACK (paint_cb), NULL); clutter_actor_set_reactive (stage, TRUE); g_signal_connect (stage, "button-release-event", G_CALLBACK (button_release_cb), NULL); g_signal_connect (stage, "key-release-event", G_CALLBACK (key_release_cb), NULL); g_signal_connect (stage, "delete-event", G_CALLBACK (destroy_window_cb), NULL); timeout_id = clutter_threads_add_timeout (1000, timeout_cb, NULL); clutter_threads_add_idle (idle_cb, stage); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-text.c0000664000175000017500000000637114723361714022006 0ustar fabiofabio#include #include #include #define FONT "Mono Bold 24px" static const gchar *runes = "ᚠᛇᚻ᛫ᛒᛦᚦ᛫ᚠᚱᚩᚠᚢᚱ᛫ᚠᛁᚱᚪ᛫ᚷᛖᚻᚹᛦᛚᚳᚢᛗ\n" "ᛋᚳᛖᚪᛚ᛫ᚦᛖᚪᚻ᛫ᛗᚪᚾᚾᚪ᛫ᚷᛖᚻᚹᛦᛚᚳ᛫ᛗᛁᚳᛚᚢᚾ᛫ᚻᛦᛏ᛫ᛞᚫᛚᚪᚾ\n" "ᚷᛁᚠ᛫ᚻᛖ᛫ᚹᛁᛚᛖ᛫ᚠᚩᚱ᛫ᛞᚱᛁᚻᛏᚾᛖ᛫ᛞᚩᛗᛖᛋ᛫ᚻᛚᛇᛏᚪᚾ᛬\n"; gint test_text_main (gint argc, gchar **argv); const char * test_text_describe (void); G_MODULE_EXPORT gint test_text_main (gint argc, gchar **argv) { ClutterActor *stage; ClutterActor *text, *text2; ClutterColor text_color = { 0x33, 0xff, 0x33, 0xff }; ClutterColor cursor_color = { 0xff, 0x33, 0x33, 0xff }; ClutterTextBuffer *buffer; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Text Editing"); clutter_actor_set_background_color (stage, CLUTTER_COLOR_Black); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); buffer = clutter_text_buffer_new_with_text ("·", -1); text = clutter_text_new_with_buffer (buffer); clutter_text_set_font_name (CLUTTER_TEXT (text), FONT); clutter_text_set_color (CLUTTER_TEXT (text), &text_color); clutter_container_add (CLUTTER_CONTAINER (stage), text, NULL); clutter_actor_set_position (text, 40, 30); clutter_actor_set_width (text, 1024); clutter_text_set_line_wrap (CLUTTER_TEXT (text), TRUE); clutter_actor_set_reactive (text, TRUE); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), text); clutter_text_set_editable (CLUTTER_TEXT (text), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (text), TRUE); clutter_text_set_cursor_color (CLUTTER_TEXT (text), &cursor_color); clutter_text_set_selected_text_color (CLUTTER_TEXT (text), CLUTTER_COLOR_Blue); text2 = clutter_text_new_with_buffer (buffer); clutter_text_set_color (CLUTTER_TEXT (text2), &text_color); clutter_container_add (CLUTTER_CONTAINER (stage), text2, NULL); clutter_actor_set_position (text2, 40, 300); clutter_actor_set_width (text2, 1024); clutter_text_set_line_wrap (CLUTTER_TEXT (text2), TRUE); clutter_actor_set_reactive (text2, TRUE); clutter_text_set_editable (CLUTTER_TEXT (text2), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (text2), TRUE); clutter_text_set_cursor_color (CLUTTER_TEXT (text2), &cursor_color); clutter_text_set_selected_text_color (CLUTTER_TEXT (text2), CLUTTER_COLOR_Green); if (argv[1]) { GError *error = NULL; gchar *utf8; g_file_get_contents (argv[1], &utf8, NULL, &error); if (error) { utf8 = g_strconcat ("Unable to open '", argv[1], "':\n", error->message, NULL); g_error_free (error); } clutter_text_set_text (CLUTTER_TEXT (text), utf8); } else clutter_text_set_text (CLUTTER_TEXT (text), runes); clutter_actor_set_size (stage, 1024, 768); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_text_describe (void) { return "Multi-line text editing."; } muffin-6.4.1/src/tests/clutter/interactive/test-grab.c0000664000175000017500000002213014723361714021724 0ustar fabiofabio#include #include int test_grab_main (int argc, char *argv[]); const char * test_grab_describe (void); static void stage_state_cb (ClutterStage *stage, gpointer data) { gchar *detail = (gchar*)data; printf("[stage signal] %s\n", detail); } static gboolean debug_event_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { gchar keybuf[9], *source = (gchar*)data; int len = 0; switch (event->type) { case CLUTTER_KEY_PRESS: len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->key.keyval), keybuf); keybuf[len] = '\0'; printf ("[%s] KEY PRESS '%s'", source, keybuf); break; case CLUTTER_KEY_RELEASE: len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->key.keyval), keybuf); keybuf[len] = '\0'; printf ("[%s] KEY RELEASE '%s'", source, keybuf); break; case CLUTTER_MOTION: printf("[%s] MOTION", source); break; case CLUTTER_ENTER: printf("[%s] ENTER", source); break; case CLUTTER_LEAVE: printf("[%s] LEAVE", source); break; case CLUTTER_BUTTON_PRESS: printf("[%s] BUTTON PRESS (click count:%i)", source, event->button.click_count); break; case CLUTTER_BUTTON_RELEASE: printf("[%s] BUTTON RELEASE", source); break; case CLUTTER_SCROLL: printf("[%s] BUTTON SCROLL", source); break; case CLUTTER_STAGE_STATE: printf("[%s] STAGE STATE", source); break; case CLUTTER_DESTROY_NOTIFY: printf("[%s] DESTROY NOTIFY", source); break; case CLUTTER_CLIENT_MESSAGE: printf("[%s] CLIENT MESSAGE\n", source); break; case CLUTTER_DELETE: printf("[%s] DELETE", source); break; case CLUTTER_TOUCH_BEGIN: g_print ("[%s] TOUCH BEGIN", source); break; case CLUTTER_TOUCH_UPDATE: g_print ("[%s] TOUCH UPDATE", source); break; case CLUTTER_TOUCH_END: g_print ("[%s] TOUCH END", source); break; case CLUTTER_TOUCH_CANCEL: g_print ("[%s] TOUCH CANCEL", source); break; case CLUTTER_TOUCHPAD_PINCH: g_print ("[%s] TOUCHPAD PINCH", source); break; case CLUTTER_TOUCHPAD_SWIPE: g_print ("[%s] TOUCHPAD SWIPE", source); break; case CLUTTER_PROXIMITY_IN: g_print ("[%s] PROXIMITY IN", source); break; case CLUTTER_PROXIMITY_OUT: g_print ("[%s] PROXIMITY OUT", source); break; case CLUTTER_PAD_BUTTON_PRESS: g_print ("[%s] PAD BUTTON PRESS", source); break; case CLUTTER_PAD_BUTTON_RELEASE: g_print ("[%s] PAD BUTTON RELEASE", source); break; case CLUTTER_PAD_STRIP: g_print ("[%s] PAD STRIP", source); break; case CLUTTER_PAD_RING: g_print ("[%s] PAD RING", source); break; case CLUTTER_NOTHING: default: return FALSE; } if (clutter_event_get_source (event) == actor) printf(" *source*"); printf("\n"); return FALSE; } static gboolean grab_pointer_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterInputDevice *device = clutter_event_get_device (event); clutter_input_device_grab (device, actor); return FALSE; } static gboolean red_release_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterInputDevice *device = clutter_event_get_device (event); clutter_input_device_ungrab (device); return FALSE; } static gboolean blue_release_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { clutter_actor_destroy (actor); return FALSE; } static gboolean green_press_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterActor *stage; gboolean enabled; stage = clutter_actor_get_stage (actor); enabled = !clutter_stage_get_motion_events_enabled (CLUTTER_STAGE (stage)); clutter_stage_set_motion_events_enabled (CLUTTER_STAGE (stage), enabled); g_print ("per actor motion events are now %s\n", enabled ? "enabled" : "disabled"); return FALSE; } static gboolean toggle_grab_pointer_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterInputDevice *device = clutter_event_get_device (event); /* we only deal with the event if the source is ourself */ if (event->button.source == actor) { if (clutter_input_device_get_grabbed_actor (device) != NULL) clutter_input_device_ungrab (device); else clutter_input_device_grab (device, actor); } return FALSE; } static gboolean cyan_press_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); ClutterInputDevice *device = clutter_seat_get_pointer (seat); if (clutter_input_device_get_grabbed_actor (device) != NULL) clutter_input_device_ungrab (device); else clutter_input_device_grab (device, actor); return FALSE; } G_MODULE_EXPORT int test_grab_main (int argc, char *argv[]) { ClutterActor *stage, *actor; ClutterColor rcol = { 0xff, 0, 0, 0xff}, bcol = { 0, 0, 0xff, 0xff }, gcol = { 0, 0xff, 0, 0xff }, ccol = { 0, 0xff, 0xff, 0xff }, ycol = { 0xff, 0xff, 0, 0xff }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; g_print ("Red box: aquire grab on press, releases it on next button release\n"); g_print ("Blue box: aquire grab on press, destroys the blue box actor on release\n"); g_print ("Yellow box: aquire grab on press, releases grab on next press on yellow box\n"); g_print ("Green box: toggle per actor motion events.\n\n"); g_print ("Cyan box: toggle grab (from cyan box) for keyboard events.\n\n"); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Grabs"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "event", G_CALLBACK (debug_event_cb), (char *) "stage"); g_signal_connect (stage, "activate", G_CALLBACK (stage_state_cb), (char *) "activate"); g_signal_connect (stage, "deactivate", G_CALLBACK (stage_state_cb), (char *) "deactivate"); actor = clutter_rectangle_new_with_color (&rcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), (char *) "red box"); g_signal_connect (actor, "button-press-event", G_CALLBACK (grab_pointer_cb), NULL); g_signal_connect (actor, "button-release-event", G_CALLBACK (red_release_cb), NULL); actor = clutter_rectangle_new_with_color (&ycol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 100, 300); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), (char *) "yellow box"); g_signal_connect (actor, "button-press-event", G_CALLBACK (toggle_grab_pointer_cb), NULL); actor = clutter_rectangle_new_with_color (&bcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 300, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), (char *) "blue box"); g_signal_connect (actor, "button-press-event", G_CALLBACK (grab_pointer_cb), NULL); g_signal_connect (actor, "button-release-event", G_CALLBACK (blue_release_cb), NULL); actor = clutter_rectangle_new_with_color (&gcol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 300, 300); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), (char *) "green box"); g_signal_connect (actor, "button-press-event", G_CALLBACK (green_press_cb), NULL); actor = clutter_rectangle_new_with_color (&ccol); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 500, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (debug_event_cb), (char *) "cyan box"); g_signal_connect (actor, "button-press-event", G_CALLBACK (cyan_press_cb), NULL); clutter_actor_show_all (CLUTTER_ACTOR (stage)); clutter_main(); return 0; } G_MODULE_EXPORT const char * test_grab_describe (void) { return "Examples of using actor grabs"; } muffin-6.4.1/src/tests/clutter/interactive/test-devices.c0000664000175000017500000001777314723361714022454 0ustar fabiofabio#include #include #include #ifdef CLUTTER_WINDOWING_X11 #include #endif #include "test-utils.h" typedef struct { ClutterActor *stage; GHashTable *devices; } TestDevicesApp; int test_devices_main (int argc, char **argv); static const gchar * device_type_name (ClutterInputDevice *device) { ClutterInputDeviceType d_type; d_type = clutter_input_device_get_device_type (device); switch (d_type) { case CLUTTER_POINTER_DEVICE: return "Pointer"; case CLUTTER_KEYBOARD_DEVICE: return "Keyboard"; case CLUTTER_EXTENSION_DEVICE: return "Extension"; case CLUTTER_PEN_DEVICE: return "Pen"; case CLUTTER_ERASER_DEVICE: return "Eraser"; case CLUTTER_CURSOR_DEVICE: return "Cursor"; default: return "Unknown"; } } static const gchar * axis_type_name (ClutterInputAxis axis) { switch (axis) { case CLUTTER_INPUT_AXIS_X: return "Absolute X"; case CLUTTER_INPUT_AXIS_Y: return "Absolute Y"; case CLUTTER_INPUT_AXIS_PRESSURE: return "Pressure"; case CLUTTER_INPUT_AXIS_XTILT: return "X Tilt"; case CLUTTER_INPUT_AXIS_YTILT: return "Y Tilt"; case CLUTTER_INPUT_AXIS_WHEEL: return "Wheel"; default: return "Unknown"; } } static gboolean stage_button_event_cb (ClutterActor *actor, ClutterEvent *event, TestDevicesApp *app) { ClutterInputDevice *device; ClutterInputDevice *source_device; ClutterActor *hand = NULL; gdouble *axes; guint n_axes, i; device = clutter_event_get_device (event); source_device = clutter_event_get_source_device (event); hand = g_hash_table_lookup (app->devices, device); g_print ("Device: '%s' (id:%d, type: %s, source: '%s', axes: %d)\n", clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device), device_type_name (device), source_device != device ? clutter_input_device_get_device_name (source_device) : "", clutter_input_device_get_n_axes (device)); if (hand != NULL) { gfloat event_x, event_y; clutter_event_get_coords (event, &event_x, &event_y); clutter_actor_set_position (hand, event_x, event_y); } axes = clutter_event_get_axes (event, &n_axes); for (i = 0; i < n_axes; i++) { ClutterInputAxis axis; axis = clutter_input_device_get_axis (device, i); if (axis == CLUTTER_INPUT_AXIS_IGNORE) continue; g_print ("\tAxis[%2d][%s].value: %.2f\n", i, axis_type_name (axis), axes[i]); } return FALSE; } static gboolean stage_motion_event_cb (ClutterActor *actor, ClutterEvent *event, TestDevicesApp *app) { ClutterInputDevice *device; ClutterActor *hand = NULL; device = clutter_event_get_device (event); hand = g_hash_table_lookup (app->devices, device); if (hand != NULL) { gfloat event_x, event_y; clutter_event_get_coords (event, &event_x, &event_y); clutter_actor_set_position (hand, event_x, event_y); return TRUE; } return FALSE; } static void seat_device_added_cb (ClutterSeat *seat, ClutterInputDevice *device, TestDevicesApp *app) { ClutterInputDeviceType device_type; ClutterActor *hand = NULL; g_print ("got a %s device '%s' with id %d\n", device_type_name (device), clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device)); device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_POINTER_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_POINTER_DEVICE) { g_print ("*** enabling device '%s' ***\n", clutter_input_device_get_device_name (device)); clutter_input_device_set_enabled (device, TRUE); hand = clutter_test_utils_create_texture_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", NULL); g_hash_table_insert (app->devices, device, hand); clutter_container_add_actor (CLUTTER_CONTAINER (app->stage), hand); } } static void seat_device_removed_cb (ClutterSeat *seat, ClutterInputDevice *device, TestDevicesApp *app) { ClutterInputDeviceType device_type; ClutterActor *hand = NULL; g_print ("removed a %s device '%s' with id %d\n", device_type_name (device), clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device)); device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_POINTER_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_POINTER_DEVICE) { hand = g_hash_table_lookup (app->devices, device); if (hand != NULL) clutter_container_add_actor (CLUTTER_CONTAINER (app->stage), hand); g_hash_table_remove (app->devices, device); } } G_MODULE_EXPORT int test_devices_main (int argc, char **argv) { ClutterActor *stage; TestDevicesApp *app; ClutterSeat *seat; GList *stage_devices, *l; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; app = g_new0 (TestDevicesApp, 1); app->devices = g_hash_table_new (g_direct_hash, g_direct_equal) ; stage = clutter_stage_new (); clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue); clutter_stage_set_title (CLUTTER_STAGE (stage), "Devices"); clutter_stage_hide_cursor (CLUTTER_STAGE (stage)); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "motion-event", G_CALLBACK (stage_motion_event_cb), app); g_signal_connect (stage, "button-press-event", G_CALLBACK (stage_button_event_cb), app); app->stage = stage; clutter_actor_show_all (stage); seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_connect (seat, "device-added", G_CALLBACK (seat_device_added_cb), app); g_signal_connect (seat, "device-removed", G_CALLBACK (seat_device_removed_cb), app); stage_devices = clutter_seat_list_devices (seat); if (stage_devices == NULL) g_error ("No input devices found."); for (l = stage_devices; l != NULL; l = l->next) { ClutterInputDevice *device = l->data; ClutterInputDeviceType device_type; ClutterActor *hand = NULL; g_print ("got a %s device '%s' with id %d\n", device_type_name (device), clutter_input_device_get_device_name (device), clutter_input_device_get_device_id (device)); device_type = clutter_input_device_get_device_type (device); if (device_type == CLUTTER_POINTER_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_POINTER_DEVICE) { g_print ("*** enabling device '%s' ***\n", clutter_input_device_get_device_name (device)); clutter_input_device_set_enabled (device, TRUE); hand = clutter_test_utils_create_texture_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", NULL); g_hash_table_insert (app->devices, device, hand); clutter_container_add_actor (CLUTTER_CONTAINER (stage), hand); } } g_list_free (stage_devices); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-main.c0000664000175000017500000001232614723361714021743 0ustar fabiofabio#include "config.h" #include #include #include #include #include "backends/x11/nested/meta-backend-x11-nested.h" #include "core/main-private.h" #include "meta/main.h" #include "wayland/meta-wayland.h" #include "wayland/meta-xwayland.h" #include "test-unit-names.h" #define MAX_DESC_SIZE 72 static GModule *module = NULL; static gpointer get_symbol_with_suffix (const char *unit_name, const char *suffix) { char *main_symbol_name; gpointer func; main_symbol_name = g_strconcat (unit_name, "_", suffix, NULL); main_symbol_name = g_strdelimit (main_symbol_name, "-", '_'); g_module_symbol (module, main_symbol_name, &func); g_free (main_symbol_name); return func; } static gpointer get_unit_name_main (const char *unit_name) { return get_symbol_with_suffix (unit_name, "main"); } static char * get_unit_name_description (const char *unit_name, gssize max_len) { const char *description; gpointer func; char *retval; func = get_symbol_with_suffix (unit_name, "describe"); if (func == NULL) description = "No description found"; else { const char *(* unit_test_describe) (void); unit_test_describe = func; description = unit_test_describe (); } if (max_len > 0 && strlen (description) >= max_len) { GString *buf = g_string_sized_new (max_len); char *newline; newline = strchr (description, '\n'); if (newline != NULL) { g_string_append_len (buf, description, MIN (newline - description - 1, max_len - 3)); } else g_string_append_len (buf, description, max_len - 3); g_string_append (buf, "..."); retval = g_string_free (buf, FALSE); } else retval = g_strdup (description); return retval; } static gboolean list_all = FALSE; static gboolean describe = FALSE; static char **unit_names = NULL; static GOptionEntry entries[] = { { "describe", 'd', 0, G_OPTION_ARG_NONE, &describe, "Describe the interactive unit test", NULL, }, { "list-all", 'l', 0, G_OPTION_ARG_NONE, &list_all, "List all available units", NULL, }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &unit_names, "The interactive unit test", "UNIT_NAME" }, { NULL } }; int main (int argc, char **argv) { int ret, i, n_unit_names; GOptionContext *context; context = g_option_context_new (" - Interactive test suite"); g_option_context_add_main_entries (context, entries, NULL); g_option_context_set_help_enabled (context, TRUE); g_option_context_set_ignore_unknown_options (context, TRUE); if (!g_option_context_parse (context, &argc, &argv, NULL)) { g_print ("Usage: test-interactive \n"); return EXIT_FAILURE; } g_option_context_free (context); meta_wayland_override_display_name ("mutter-test-display"); meta_xwayland_override_display_number (512); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_X11_NESTED); meta_init (); module = g_module_open (NULL, 0); if (!module) g_error ("*** Failed to open self for symbol lookup"); ret = EXIT_SUCCESS; if (list_all) { g_print ("* Available unit tests:\n"); for (i = 0; i < G_N_ELEMENTS (test_unit_names); i++) { char *str; gsize len; len = MAX_DESC_SIZE - strlen (test_unit_names[i]); str = get_unit_name_description (test_unit_names[i], len - 2); g_print (" - %s:%*s%s\n", test_unit_names[i], (int) (len - strlen (str)), " ", str); g_free (str); } ret = EXIT_SUCCESS; goto out; } if (unit_names != NULL) n_unit_names = g_strv_length (unit_names); else { g_print ("Usage: test-interactive \n"); ret = EXIT_FAILURE; goto out; } for (i = 0; i < n_unit_names; i++) { const char *unit_name = unit_names[i]; char *unit_test = NULL; gboolean found; int j; unit_test = g_path_get_basename (unit_name); found = FALSE; for (j = 0; j < G_N_ELEMENTS (test_unit_names); j++) { if (strcmp (test_unit_names[j], unit_test) == 0) { found = TRUE; break; } } if (!found) g_error ("*** Unit '%s' does not exist", unit_test); if (describe) { char *str; str = get_unit_name_description (unit_test, -1); g_print ("* %s:\n%s\n\n", unit_test, str); g_free (str); ret = EXIT_SUCCESS; } else { int (* unit_test_main) (int argc, char **argv); gpointer func; func = get_unit_name_main (unit_test); if (func == NULL) g_error ("*** Unable to find the main entry point for '%s'", unit_test); unit_test_main = func; ret = unit_test_main (n_unit_names, unit_names); g_free (unit_test); break; } g_free (unit_test); } out: g_module_close (module); return ret; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-tex-convert.c0000664000175000017500000001571614723361714024225 0ustar fabiofabio#include #include #include #include #include /* Coglbox declaration *--------------------------------------------------*/ G_BEGIN_DECLS #define TEST_TYPE_COGLBOX test_coglbox_get_type() #define TEST_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ TEST_TYPE_COGLBOX, TestCoglbox)) #define TEST_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) #define TEST_IS_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ TEST_TYPE_COGLBOX)) #define TEST_IS_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ TEST_TYPE_COGLBOX)) #define TEST_COGLBOX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) typedef struct _TestCoglbox TestCoglbox; typedef struct _TestCoglboxClass TestCoglboxClass; typedef struct _TestCoglboxPrivate TestCoglboxPrivate; struct _TestCoglbox { ClutterActor parent; /*< private >*/ TestCoglboxPrivate *priv; }; struct _TestCoglboxClass { ClutterActorClass parent_class; /* padding for future expansion */ void (*_test_coglbox1) (void); void (*_test_coglbox2) (void); void (*_test_coglbox3) (void); void (*_test_coglbox4) (void); }; static GType test_coglbox_get_type (void) G_GNUC_CONST; int test_cogl_tex_convert_main (int argc, char *argv[]); const char * test_cogl_tex_convert_describe (void); G_END_DECLS /* Coglbox private declaration *--------------------------------------------------*/ struct _TestCoglboxPrivate { CoglHandle cogl_tex_id[4]; gint frame; }; G_DEFINE_TYPE_WITH_PRIVATE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); #define TEST_COGLBOX_GET_PRIVATE(obj) \ (test_coglbox_get_instance_private (TEST_COGLBOX ((obj)))) /* Coglbox implementation *--------------------------------------------------*/ static void test_coglbox_paint (ClutterActor *self, ClutterPaintContext *paint_context) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); CoglPipeline *pipeline; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); gfloat texcoords[4] = { 0.0, 0.0, 1.0, 1.0 }; priv = TEST_COGLBOX_GET_PRIVATE (self); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0x66, 0x66, 0xdd, 0xff); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, 400, 400); cogl_object_unref (pipeline); pipeline = cogl_pipeline_new (ctx); cogl_framebuffer_push_matrix (framebuffer); cogl_pipeline_set_layer_texture (pipeline, 0, priv->cogl_tex_id[0]); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 200, 213, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, 200, 0, 0); cogl_pipeline_set_layer_texture (pipeline, 0, priv->cogl_tex_id[1]); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 200, 213, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, 0, 200, 0); cogl_pipeline_set_layer_texture (pipeline, 0, priv->cogl_tex_id[2]); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 200, 213, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, 200, 200, 0); cogl_pipeline_set_layer_texture (pipeline, 0, priv->cogl_tex_id[3]); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 200, 213, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_framebuffer_pop_matrix (framebuffer); cogl_object_unref (pipeline); } static void test_coglbox_finalize (GObject *object) { G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); } static void test_coglbox_dispose (GObject *object) { TestCoglboxPrivate *priv; priv = TEST_COGLBOX_GET_PRIVATE (object); cogl_object_unref (priv->cogl_tex_id); G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); } static void test_coglbox_init (TestCoglbox *self) { TestCoglboxPrivate *priv; gchar *file; self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); priv->cogl_tex_id[0] = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, NULL); priv->cogl_tex_id[1] = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_BGRA_8888, NULL); priv->cogl_tex_id[2] = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ARGB_8888, NULL); priv->cogl_tex_id[3] = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_G_8, NULL); g_free (file); } static void test_coglbox_class_init (TestCoglboxClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->finalize = test_coglbox_finalize; gobject_class->dispose = test_coglbox_dispose; actor_class->paint = test_coglbox_paint; } static ClutterActor* test_coglbox_new (void) { return g_object_new (TEST_TYPE_COGLBOX, NULL); } G_MODULE_EXPORT int test_cogl_tex_convert_main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *coglbox; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Stage */ stage = clutter_stage_new (); clutter_actor_set_size (stage, 400, 400); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Texture Conversion"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* Cogl Box */ coglbox = test_coglbox_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); clutter_actor_show_all (stage); clutter_main (); return 0; } G_MODULE_EXPORT const char * test_cogl_tex_convert_describe (void) { return "Pixel format conversion of Cogl textures."; } muffin-6.4.1/src/tests/clutter/interactive/test-image.c0000664000175000017500000001657514723361714022113 0ustar fabiofabio#include #include #include #include typedef struct _SolidContent { GObject parent_instance; double red; double green; double blue; double alpha; float padding; } SolidContent; typedef struct _SolidContentClass { GObjectClass parent_class; } SolidContentClass; static void clutter_content_iface_init (ClutterContentInterface *iface); GType solid_content_get_type (void); const char * test_image_describe (void); int test_image_main (int argc, char *argv[]); G_DEFINE_TYPE_WITH_CODE (SolidContent, solid_content, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)) static void solid_content_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root, ClutterPaintContext *paint_context) { SolidContent *self = (SolidContent *) content; ClutterActorBox box, content_box; ClutterColor color; PangoLayout *layout; PangoRectangle logical; ClutterPaintNode *node; #if 0 g_debug ("Painting content [%p] " "{ r:%.2f, g:%.2f, b:%.2f, a:%.2f } " "for actor [%p] (context: [%p])", content, self->red, self->green, self->blue, self->alpha, actor, context); #endif clutter_actor_get_content_box (actor, &content_box); box = content_box; box.x1 += self->padding; box.y1 += self->padding; box.x2 -= self->padding; box.y2 -= self->padding; color.alpha = self->alpha * 255; color.red = self->red * 255; color.green = self->green * 255; color.blue = self->blue * 255; node = clutter_color_node_new (&color); clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); color.red = (1.0 - self->red) * 255; color.green = (1.0 - self->green) * 255; color.blue = (1.0 - self->blue) * 255; layout = clutter_actor_create_pango_layout (actor, "A"); pango_layout_get_pixel_extents (layout, NULL, &logical); node = clutter_text_node_new (layout, &color); /* top-left */ box.x1 = clutter_actor_box_get_x (&content_box); box.y1 = clutter_actor_box_get_y (&content_box); box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* top-right */ box.x1 = clutter_actor_box_get_x (&content_box) + clutter_actor_box_get_width (&content_box) - logical.width; box.y1 = clutter_actor_box_get_y (&content_box); box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* bottom-right */ box.x1 = clutter_actor_box_get_x (&content_box) + clutter_actor_box_get_width (&content_box) - logical.width; box.y1 = clutter_actor_box_get_y (&content_box) + clutter_actor_box_get_height (&content_box) - logical.height; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* bottom-left */ box.x1 = clutter_actor_box_get_x (&content_box); box.y1 = clutter_actor_box_get_y (&content_box) + clutter_actor_box_get_height (&content_box) - logical.height; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); /* center */ box.x1 = clutter_actor_box_get_x (&content_box) + (clutter_actor_box_get_width (&content_box) - logical.width) / 2.0; box.y1 = clutter_actor_box_get_y (&content_box) + (clutter_actor_box_get_height (&content_box) - logical.height) / 2.0; box.x2 = box.x1 + logical.width; box.y2 = box.y1 + logical.height; clutter_paint_node_add_rectangle (node, &box); clutter_paint_node_add_child (root, node); clutter_paint_node_unref (node); g_object_unref (layout); } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->paint_content = solid_content_paint_content; } static void solid_content_class_init (SolidContentClass *klass) { } static void solid_content_init (SolidContent *self) { } static ClutterContent * solid_content_new (double red, double green, double blue, double alpha, float padding) { SolidContent *self = g_object_new (solid_content_get_type (), NULL); self->red = red; self->green = green; self->blue = blue; self->alpha = alpha; self->padding = padding; return (ClutterContent *) self; } G_MODULE_EXPORT const char * test_image_describe (void) { return "A test with image content."; } G_MODULE_EXPORT int test_image_main (int argc, char *argv[]) { ClutterActor *stage, *grid; ClutterContent *color, *image; GdkPixbuf *pixbuf; int i, n_rects; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; stage = clutter_stage_new (); clutter_actor_set_name (stage, "Stage"); clutter_stage_set_title (CLUTTER_STAGE (stage), "Content"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_actor_show (stage); grid = clutter_actor_new (); clutter_actor_set_name (grid, "Grid"); clutter_actor_set_margin_top (grid, 12); clutter_actor_set_margin_right (grid, 12); clutter_actor_set_margin_bottom (grid, 12); clutter_actor_set_margin_left (grid, 12); clutter_actor_set_layout_manager (grid, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL)); clutter_actor_add_constraint (grid, clutter_bind_constraint_new (stage, CLUTTER_BIND_SIZE, 0.0)); clutter_actor_add_child (stage, grid); color = solid_content_new (g_random_double_range (0.0, 1.0), g_random_double_range (0.0, 1.0), g_random_double_range (0.0, 1.0), 1.0, 2.0); pixbuf = gdk_pixbuf_new_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", NULL); image = clutter_image_new (); clutter_image_set_data (CLUTTER_IMAGE (image), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), gdk_pixbuf_get_rowstride (pixbuf), NULL); g_object_unref (pixbuf); n_rects = g_random_int_range (12, 24); for (i = 0; i < n_rects; i++) { ClutterActor *box = clutter_actor_new (); ClutterColor bg_color = { g_random_int_range (0, 255), g_random_int_range (0, 255), g_random_int_range (0, 255), 255 }; char *name, *str; str = clutter_color_to_string (&bg_color); name = g_strconcat ("Box <", color, ">", NULL); clutter_actor_set_name (box, name); g_free (name); g_free (str); if ((i % 2) == 0) clutter_actor_set_content (box, color); else clutter_actor_set_content (box, image); clutter_actor_set_size (box, 64, 64); clutter_actor_add_child (grid, box); } clutter_main (); g_object_unref (color); g_object_unref (image); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-easing.c0000664000175000017500000002113314723361714022261 0ustar fabiofabio#include #include #include /* all the easing modes provided by Clutter */ static const struct { const gchar *name; ClutterAnimationMode mode; } easing_modes[] = { { "linear", CLUTTER_LINEAR }, { "easeInQuad", CLUTTER_EASE_IN_QUAD }, { "easeOutQuad", CLUTTER_EASE_OUT_QUAD }, { "easeInOutQuad", CLUTTER_EASE_IN_OUT_QUAD }, { "easeInCubic", CLUTTER_EASE_IN_CUBIC }, { "easeOutCubic", CLUTTER_EASE_OUT_CUBIC }, { "easeInOutCubic", CLUTTER_EASE_IN_OUT_CUBIC }, { "easeInQuart", CLUTTER_EASE_IN_QUART }, { "easeOutQuart", CLUTTER_EASE_OUT_QUART }, { "easeInOutQuart", CLUTTER_EASE_IN_OUT_QUART }, { "easeInQuint", CLUTTER_EASE_IN_QUINT }, { "easeOutQuint", CLUTTER_EASE_OUT_QUINT }, { "easeInOutQuint", CLUTTER_EASE_IN_OUT_QUINT }, { "easeInSine", CLUTTER_EASE_IN_SINE }, { "easeOutSine", CLUTTER_EASE_OUT_SINE }, { "easeInOutSine", CLUTTER_EASE_IN_OUT_SINE }, { "easeInExpo", CLUTTER_EASE_IN_EXPO }, { "easeOutExpo", CLUTTER_EASE_OUT_EXPO }, { "easeInOutExpo", CLUTTER_EASE_IN_OUT_EXPO }, { "easeInCirc", CLUTTER_EASE_IN_CIRC }, { "easeOutCirc", CLUTTER_EASE_OUT_CIRC }, { "easeInOutCirc", CLUTTER_EASE_IN_OUT_CIRC }, { "easeInElastic", CLUTTER_EASE_IN_ELASTIC }, { "easeOutElastic", CLUTTER_EASE_OUT_ELASTIC }, { "easeInOutElastic", CLUTTER_EASE_IN_OUT_ELASTIC }, { "easeInBack", CLUTTER_EASE_IN_BACK }, { "easeOutBack", CLUTTER_EASE_OUT_BACK }, { "easeInOutBack", CLUTTER_EASE_IN_OUT_BACK }, { "easeInBounce", CLUTTER_EASE_IN_BOUNCE }, { "easeOutBounce", CLUTTER_EASE_OUT_BOUNCE }, { "easeInOutBounce", CLUTTER_EASE_IN_OUT_BOUNCE }, }; #define HELP_TEXT "Easing mode: %s (%d of %d)\n" \ "Left click to tween\n" \ "Right click to change the easing mode" static const gint n_easing_modes = G_N_ELEMENTS (easing_modes); static gint current_mode = 0; static gint duration = 1; static gboolean recenter = FALSE; static ClutterActor *main_stage = NULL; static ClutterActor *easing_mode_label = NULL; static ClutterAnimation *last_animation = NULL; int test_easing_main (int argc, char *argv[]); const char * test_easing_describe (void); /* recenter_bouncer: * * repositions (through an animation) the bouncer at the center of the stage */ static void recenter_bouncer (ClutterAnimation *animation, ClutterActor *rectangle) { gfloat base_x, base_y; gint cur_mode; base_x = clutter_actor_get_width (main_stage) / 2; base_y = clutter_actor_get_height (main_stage) / 2; cur_mode = easing_modes[current_mode].mode; clutter_actor_animate (rectangle, cur_mode, 250, "x", base_x, "y", base_y, NULL); } static gboolean on_button_press (ClutterActor *actor, ClutterButtonEvent *event, ClutterActor *rectangle) { if (event->button == CLUTTER_BUTTON_SECONDARY) { gchar *text; /* cycle through the various easing modes */ current_mode = (current_mode + 1 < n_easing_modes) ? current_mode + 1 : 0; /* update the text of the label */ text = g_strdup_printf (HELP_TEXT, easing_modes[current_mode].name, current_mode + 1, n_easing_modes); clutter_text_set_text (CLUTTER_TEXT (easing_mode_label), text); g_free (text); } else if (event->button == CLUTTER_BUTTON_PRIMARY) { ClutterAnimation *animation; ClutterAnimationMode cur_mode; cur_mode = easing_modes[current_mode].mode; /* tween the actor using the current easing mode */ animation = clutter_actor_animate (rectangle, cur_mode, duration * 1000, "x", event->x, "y", event->y, NULL); /* if we were asked to, recenter the bouncer at the end of the * animation. we keep track of the animation to avoid connecting * the signal handler to the same Animation twice. */ if (recenter && last_animation != animation) g_signal_connect_after (animation, "completed", G_CALLBACK (recenter_bouncer), rectangle); last_animation = animation; } return TRUE; } static gboolean draw_bouncer (ClutterCanvas *canvas, cairo_t *cr, int width, int height) { const ClutterColor *bouncer_color; cairo_pattern_t *pattern; float radius; radius = MAX (width, height); cairo_save (cr); cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint (cr); cairo_restore (cr); cairo_arc (cr, radius / 2, radius / 2, radius / 2, 0.0, 2.0 * G_PI); bouncer_color = CLUTTER_COLOR_DarkScarletRed; pattern = cairo_pattern_create_radial (radius / 2, radius / 2, 0, radius, radius, radius); cairo_pattern_add_color_stop_rgba (pattern, 0, bouncer_color->red / 255.0, bouncer_color->green / 255.0, bouncer_color->blue / 255.0, bouncer_color->alpha / 255.0); cairo_pattern_add_color_stop_rgba (pattern, 0.85, bouncer_color->red / 255.0, bouncer_color->green / 255.0, bouncer_color->blue / 255.0, 0.25); cairo_set_source (cr, pattern); cairo_fill_preserve (cr); cairo_pattern_destroy (pattern); return TRUE; } static ClutterActor * make_bouncer (gfloat width, gfloat height) { ClutterContent *canvas; ClutterActor *retval; canvas = clutter_canvas_new (); clutter_canvas_set_size (CLUTTER_CANVAS (canvas), width, height); g_signal_connect (canvas, "draw", G_CALLBACK (draw_bouncer), NULL); retval = g_object_new (CLUTTER_TYPE_ACTOR, "content", canvas, NULL); clutter_actor_set_name (retval, "bouncer"); clutter_actor_set_size (retval, width, height); clutter_actor_set_anchor_point (retval, width / 2, height / 2); clutter_actor_set_reactive (retval, TRUE); clutter_content_invalidate (canvas); return retval; } static GOptionEntry test_easing_entries[] = { { "re-center", 'r', 0, G_OPTION_ARG_NONE, &recenter, "Re-center the actor when the animation ends", NULL }, { "duration", 'd', 0, G_OPTION_ARG_INT, &duration, "Duration of the animation", "SECONDS" }, { NULL } }; G_MODULE_EXPORT int test_easing_main (int argc, char *argv[]) { ClutterActor *stage, *rect, *label; gchar *text; gfloat stage_width, stage_height; GError *error = NULL; if (clutter_init_with_args (&argc, &argv, NULL, test_easing_entries, NULL, &error) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Easing Modes"); clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); main_stage = stage; clutter_actor_get_size (stage, &stage_width, &stage_height); /* create the actor that we want to tween */ rect = make_bouncer (50, 50); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); clutter_actor_set_position (rect, stage_width / 2, stage_height / 2); text = g_strdup_printf (HELP_TEXT, easing_modes[current_mode].name, current_mode + 1, n_easing_modes); label = clutter_text_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); clutter_text_set_text (CLUTTER_TEXT (label), text); clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_X_AXIS, 0.95)); clutter_actor_add_constraint (label, clutter_align_constraint_new (stage, CLUTTER_ALIGN_Y_AXIS, 0.95)); easing_mode_label = label; g_free (text); g_signal_connect (stage, "button-press-event", G_CALLBACK (on_button_press), rect); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_easing_describe (void) { return "Visualize all easing modes provided by Clutter"; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-offscreen.c0000664000175000017500000002447114723361714023717 0ustar fabiofabio#include #include #include #include #include /* Coglbox declaration *--------------------------------------------------*/ G_BEGIN_DECLS #define TEST_TYPE_COGLBOX test_coglbox_get_type() #define TEST_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ TEST_TYPE_COGLBOX, TestCoglbox)) #define TEST_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) #define TEST_IS_COGLBOX(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ TEST_TYPE_COGLBOX)) #define TEST_IS_COGLBOX_CLASS(klass) \ (G_TYPE_CHECK_CLASS_TYPE ((klass), \ TEST_TYPE_COGLBOX)) #define TEST_COGLBOX_GET_CLASS(obj) \ (G_TYPE_INSTANCE_GET_CLASS ((obj), \ TEST_TYPE_COGLBOX, TestCoglboxClass)) typedef struct _TestCoglbox TestCoglbox; typedef struct _TestCoglboxClass TestCoglboxClass; typedef struct _TestCoglboxPrivate TestCoglboxPrivate; struct _TestCoglbox { ClutterActor parent; /*< private >*/ TestCoglboxPrivate *priv; }; struct _TestCoglboxClass { ClutterActorClass parent_class; /* padding for future expansion */ void (*_test_coglbox1) (void); void (*_test_coglbox2) (void); void (*_test_coglbox3) (void); void (*_test_coglbox4) (void); }; static GType test_coglbox_get_type (void) G_GNUC_CONST; int test_cogl_offscreen_main (int argc, char *argv[]); const char * test_cogl_offscreen_describe (void); G_END_DECLS /* Coglbox private declaration *--------------------------------------------------*/ struct _TestCoglboxPrivate { CoglHandle texhand_id; CoglHandle texture_id; CoglHandle offscreen_id; }; G_DEFINE_TYPE_WITH_PRIVATE (TestCoglbox, test_coglbox, CLUTTER_TYPE_ACTOR); #define TEST_COGLBOX_GET_PRIVATE(obj) \ (test_coglbox_get_instance_private (TEST_COGLBOX ((obj)))) /* Coglbox implementation *--------------------------------------------------*/ static void test_coglbox_paint (ClutterActor *self, ClutterPaintContext *paint_context) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (self); CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglContext *ctx = cogl_framebuffer_get_context (framebuffer); gfloat texcoords[4] = { 0, 0, 1, 1 }; CoglPipeline *pipeline; pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0x66, 0x66, 0xdd, 0xff); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, 0, 0, 400, 400); cogl_object_unref (pipeline); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_texture (pipeline, 0, priv->texhand_id); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, 400, 400, 0, 0, 6, 6); cogl_object_unref (pipeline); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0xff, 0, 0, 0xff); cogl_framebuffer_draw_rectangle (priv->offscreen_id, pipeline, 20, 20, 20 + 100, 20 + 100); cogl_pipeline_set_color4ub (pipeline, 0, 0xff, 0, 0xff); cogl_framebuffer_draw_rectangle (priv->offscreen_id, pipeline, 80, 80, 80 + 100, 80 + 100); cogl_object_unref (pipeline); pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 0x88, 0x88, 0x88, 0x88); cogl_pipeline_set_layer_texture (pipeline, 0, priv->texture_id); cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 100, 100, 300, 300, texcoords[0], texcoords[1], texcoords[2], texcoords[3]); cogl_object_unref (pipeline); } static void test_coglbox_finalize (GObject *object) { G_OBJECT_CLASS (test_coglbox_parent_class)->finalize (object); } static void test_coglbox_dispose (GObject *object) { TestCoglboxPrivate *priv; priv = TEST_COGLBOX_GET_PRIVATE (object); cogl_object_unref (priv->texture_id); cogl_object_unref (priv->offscreen_id); G_OBJECT_CLASS (test_coglbox_parent_class)->dispose (object); } /* A newly created Cogl framebuffer will be initialized with a * viewport covering the size of the viewport i.e. equavalent to: * * calling cogl_framebuffer_set_viewport ( * fb, * 0, 0, * cogl_framebuffer_get_viewport_width (fb), * cogl_framebuffer_get_viewport_width (fb)); * * The projection matrix will be an identity matrix. * * The modelview matrix will be an identity matrix, and this will * create a coordinate system - like OpenGL - with the viewport * being mapped to a unit cube with the origin (0, 0, 0) in the * center, x, y and z ranging from -1 to 1 with (-1, -1) being top * left and (1, 1) bottom right. * * This sets up a Clutter like coordinate system for a Cogl * framebuffer */ static void setup_viewport (CoglFramebuffer *framebuffer, unsigned int width, unsigned int height, float fovy, float aspect, float z_near, float z_far) { float z_camera; CoglMatrix projection_matrix; CoglMatrix mv_matrix; cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); /* For Ortho projection. * _cogl_matrix_stack_ortho (projection_stack, 0, width, 0, height, -1, 1); */ cogl_framebuffer_perspective (framebuffer, fovy, aspect, z_near, z_far); /* * In theory, we can compute the camera distance from screen as: * * 0.5 * tan (FOV) * * However, it's better to compute the z_camera from our projection * matrix so that we get a 1:1 mapping at the screen distance. Consider * the upper-left corner of the screen. It has object coordinates * (0,0,0), so by the transform below, ends up with eye coordinate * * x_eye = x_object / width - 0.5 = - 0.5 * y_eye = (height - y_object) / width - 0.5 = 0.5 * z_eye = z_object / width - z_camera = - z_camera * * From cogl_perspective(), we know that the projection matrix has * the form: * * (x, 0, 0, 0) * (0, y, 0, 0) * (0, 0, c, d) * (0, 0, -1, 0) * * Applied to the above, we get clip coordinates of * * x_clip = x * (- 0.5) * y_clip = y * 0.5 * w_clip = - 1 * (- z_camera) = z_camera * * Dividing through by w to get normalized device coordinates, we * have, x_nd = x * 0.5 / z_camera, y_nd = - y * 0.5 / z_camera. * The upper left corner of the screen has normalized device coordinates, * (-1, 1), so to have the correct 1:1 mapping, we have to have: * * z_camera = 0.5 * x = 0.5 * y * * If x != y, then we have a non-uniform aspect ration, and a 1:1 mapping * doesn't make sense. */ cogl_framebuffer_get_projection_matrix (framebuffer, &projection_matrix); z_camera = 0.5 * projection_matrix.xx; cogl_matrix_init_identity (&mv_matrix); cogl_matrix_translate (&mv_matrix, -0.5f, -0.5f, -z_camera); cogl_matrix_scale (&mv_matrix, 1.0f / width, -1.0f / height, 1.0f / width); cogl_matrix_translate (&mv_matrix, 0.0f, -1.0 * height, 0.0f); cogl_framebuffer_set_modelview_matrix (framebuffer, &mv_matrix); } static void test_coglbox_map (ClutterActor *actor) { TestCoglboxPrivate *priv = TEST_COGLBOX_GET_PRIVATE (actor); ClutterActor *stage; ClutterPerspective perspective; float stage_width; float stage_height; CLUTTER_ACTOR_CLASS (test_coglbox_parent_class)->map (actor); printf ("Creating offscreen\n"); priv->offscreen_id = cogl_offscreen_new_to_texture (priv->texture_id); stage = clutter_actor_get_stage (actor); clutter_stage_get_perspective (CLUTTER_STAGE (stage), &perspective); clutter_actor_get_size (stage, &stage_width, &stage_height); setup_viewport (priv->offscreen_id, stage_width, stage_height, perspective.fovy, perspective.aspect, perspective.z_near, perspective.z_far); if (priv->offscreen_id == NULL) printf ("Failed creating offscreen to texture!\n"); } static void test_coglbox_init (TestCoglbox *self) { TestCoglboxPrivate *priv; gchar *file; self->priv = priv = TEST_COGLBOX_GET_PRIVATE(self); printf ("Loading redhand.png\n"); file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); priv->texhand_id = cogl_texture_new_from_file (file, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_ANY, NULL); g_free (file); printf ("Creating texture with size\n"); priv->texture_id = cogl_texture_new_with_size (200, 200, COGL_TEXTURE_NONE, COGL_PIXEL_FORMAT_RGB_888); if (priv->texture_id == NULL) printf ("Failed creating texture with size!\n"); } static void test_coglbox_class_init (TestCoglboxClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); gobject_class->finalize = test_coglbox_finalize; gobject_class->dispose = test_coglbox_dispose; actor_class->map = test_coglbox_map; actor_class->paint = test_coglbox_paint; } static ClutterActor* test_coglbox_new (void) { return g_object_new (TEST_TYPE_COGLBOX, NULL); } G_MODULE_EXPORT int test_cogl_offscreen_main (int argc, char *argv[]) { ClutterActor *stage; ClutterActor *coglbox; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Stage */ stage = clutter_stage_new (); clutter_actor_set_size (stage, 400, 400); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Offscreen Buffers"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* Cogl Box */ coglbox = test_coglbox_new (); clutter_container_add_actor (CLUTTER_CONTAINER (stage), coglbox); clutter_actor_show_all (stage); clutter_main (); return 0; } G_MODULE_EXPORT const char * test_cogl_offscreen_describe (void) { return "Offscreen buffer support in Cogl."; } muffin-6.4.1/src/tests/clutter/interactive/test-script-signals.json0000664000175000017500000000323214723361714024504 0ustar fabiofabio[ { "id" : "button", "type" : "ClutterRectangle", "width" : "16 em", "height" : "6 em", "color" : "rgb(255, 0, 0)", "opacity" : 128, "scale-gravity" : "center", "reactive" : true, "signals" : [ { "name" : "button-press-event", "handler" : "on_button_press" }, { "name" : "enter-event", "states" : "button-states", "target-state" : "hover" }, { "name" : "leave-event", "states" : "button-states", "target-state" : "base" }, { "name" : "button-press-event", "states" : "button-states", "target-state" : "active" }, { "name" : "button-release-event", "states" : "button-states", "target-state" : "hover" } ] }, { "id" : "button-states", "type" : "ClutterState", "duration" : 250, "transitions" : [ { "source" : null, "target" : "base", "keys" : [ [ "button", "opacity", "linear", 128 ], [ "button", "scale-x", "ease-in-cubic", 1.0 ], [ "button", "scale-y", "ease-in-cubic", 1.0 ], [ "button", "color", "linear", "rgb(255, 0, 0)" ] ] }, { "source" : null, "target" : "hover", "keys" : [ [ "button", "opacity", "linear", 255 ], [ "button", "scale-x", "ease-out-bounce", 1.4 ], [ "button", "scale-y", "ease-out-bounce", 1.4 ], [ "button", "color", "linear", "rgb(0, 255, 0)" ] ] }, { "source" : null, "target" : "active", "keys" : [ [ "button", "opacity", "linear", 255 ], [ "button", "color", "linear", "rgb(0, 0, 255)" ] ] } ] } ] muffin-6.4.1/src/tests/clutter/interactive/wrapper.sh.in0000775000175000017500000000060514723361714022317 0ustar fabiofabio#!/bin/sh UNIT_TEST=$1 shift echo "Running ./test-interactive $UNIT_TEST $@" echo "" echo "NOTE: For debugging purposes, you can run this single test as follows:" echo "$ libtool --mode=execute \\" echo " gdb --eval-command=\"b `echo $UNIT_TEST|tr '-' '_'`_main\" \\" echo " --args ./test-interactive $UNIT_TEST" @abs_builddir@/test-interactive $UNIT_TEST "$@" muffin-6.4.1/src/tests/clutter/interactive/test-bind-constraint.c0000664000175000017500000002173214723361714024116 0ustar fabiofabio#include #include #define RECT_SIZE 128 #define H_PADDING 32 #define V_PADDING 32 enum { NorthWest, North, NorthEast, West, Center, East, SouthWest, South, SouthEast, N_RECTS }; static ClutterActor *rects[N_RECTS] = { NULL, }; static const gchar *colors[N_RECTS] = { "#8ae234", "#73d216", "#4e9a06", "#729fcf", "#3465a4", "#204a87", "#ef2929", "#cc0000", "#a40000" }; static const gchar *names[N_RECTS] = { "North West", "North", "North East", "West", "Center", "East", "South West", "South", "South East" }; static const gchar *desaturare_glsl_shader = "uniform sampler2D tex;\n" "uniform float factor;\n" "\n" "vec3 desaturate (const vec3 color, const float desaturation)\n" "{\n" " const vec3 gray_conv = vec3 (0.299, 0.587, 0.114);\n" " vec3 gray = vec3 (dot (gray_conv, color));\n" " return vec3 (mix (color.rgb, gray, desaturation));\n" "}\n" "\n" "void main ()\n" "{\n" " vec4 color = cogl_color_in * texture2D (tex, vec2 (cogl_tex_coord_in[0].xy));\n" " color.rgb = desaturate (color.rgb, factor);\n" " cogl_color_out = color;\n" "}\n"; static gboolean is_expanded = FALSE; const char * test_bind_constraint_describe (void); int test_bind_constraint_main (int argc, char *argv[]); static gboolean on_button_release (ClutterActor *actor, ClutterEvent *event, gpointer data G_GNUC_UNUSED) { if (!is_expanded) { gfloat north_offset, south_offset; gfloat west_offset, east_offset; /* expand the 8 rectangles by animating the offset of the * bind constraints */ north_offset = (clutter_actor_get_height (rects[Center]) + V_PADDING) * -1.0f; south_offset = (clutter_actor_get_height (rects[Center]) + V_PADDING); west_offset = (clutter_actor_get_width (rects[Center]) + H_PADDING) * -1.0f; east_offset = (clutter_actor_get_width (rects[Center]) + H_PADDING); clutter_actor_animate (rects[NorthWest], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", west_offset, "@constraints.y-bind.offset", north_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[North], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.y-bind.offset", north_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[NorthEast], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", east_offset, "@constraints.y-bind.offset", north_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[West], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", west_offset, "reactive", TRUE, NULL); /* turn on the desaturation effect and set the center * rectangle not reactive */ clutter_actor_animate (rects[Center], CLUTTER_LINEAR, 500, "@effects.desaturate.enabled", TRUE, "reactive", FALSE, NULL); clutter_actor_animate (rects[East], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", east_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[SouthWest], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", west_offset, "@constraints.y-bind.offset", south_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[South], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.y-bind.offset", south_offset, "reactive", TRUE, NULL); clutter_actor_animate (rects[SouthEast], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 255, "@constraints.x-bind.offset", east_offset, "@constraints.y-bind.offset", south_offset, "reactive", TRUE, NULL); } else { gint i; clutter_actor_animate (rects[Center], CLUTTER_LINEAR, 500, "@effects.desaturate.enabled", FALSE, "reactive", TRUE, NULL); for (i = NorthWest; i < N_RECTS; i++) { if (i == Center) continue; /* put the 8 rectangles back into their initial state */ clutter_actor_animate (rects[i], CLUTTER_EASE_OUT_EXPO, 500, "opacity", 0, "@constraints.x-bind.offset", 0.0f, "@constraints.y-bind.offset", 0.0f, "reactive", FALSE, NULL); } } is_expanded = !is_expanded; g_print ("Selected: [%s]\n", clutter_actor_get_name (actor)); return TRUE; } G_MODULE_EXPORT const char * test_bind_constraint_describe (void) { return "Demonstrate the usage of ClutterBindConstraint"; } G_MODULE_EXPORT int test_bind_constraint_main (int argc, char *argv[]) { ClutterActor *stage, *rect; ClutterConstraint *constraint; ClutterEffect *effect; ClutterColor rect_color; gint i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_stage_set_title (CLUTTER_STAGE (stage), "Constraints"); clutter_actor_set_size (stage, 800, 600); /* main rectangle */ clutter_color_from_string (&rect_color, "#3465a4"); rect = clutter_actor_new (); g_signal_connect (rect, "button-release-event", G_CALLBACK (on_button_release), NULL); clutter_actor_set_background_color (rect, &rect_color); clutter_actor_set_size (rect, RECT_SIZE, RECT_SIZE); clutter_actor_set_reactive (rect, TRUE); clutter_actor_set_name (rect, names[Center]); clutter_actor_add_child (stage, rect); /* align the center rectangle to the center of the stage */ constraint = clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5); clutter_actor_add_constraint_with_name (rect, "align", constraint); /* this is the equivalent of the DesaturateEffect; we cannot animate * the factor because the animation API only understands GObject * properties; so we use the ActorMeta:enabled property to toggle * the shader */ effect = clutter_shader_effect_new (CLUTTER_FRAGMENT_SHADER); clutter_shader_effect_set_shader_source (CLUTTER_SHADER_EFFECT (effect), desaturare_glsl_shader); clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect), "tex", G_TYPE_INT, 1, 0); clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect), "factor", G_TYPE_FLOAT, 1, 0.66); clutter_actor_meta_set_enabled (CLUTTER_ACTOR_META (effect), FALSE); clutter_actor_add_effect_with_name (rect, "desaturate", effect); rects[Center] = rect; /* build the other rectangles, and bind their position and size * to the center rectangle. we are going to animate the offset * of the BindConstraints */ for (i = 0; i < N_RECTS; i++) { if (i == Center) continue; clutter_color_from_string (&rect_color, colors[i]); rect = clutter_actor_new (); clutter_actor_set_background_color (rect, &rect_color); clutter_actor_set_opacity (rect, 0); clutter_actor_set_name (rect, names[i]); clutter_actor_add_child (stage, rect); constraint = clutter_bind_constraint_new (rects[Center], CLUTTER_BIND_X, 0.0); clutter_actor_add_constraint_with_name (rect, "x-bind", constraint); constraint = clutter_bind_constraint_new (rects[Center], CLUTTER_BIND_Y, 0.0); clutter_actor_add_constraint_with_name (rect, "y-bind", constraint); constraint = clutter_bind_constraint_new (rects[Center], CLUTTER_BIND_SIZE, 0.0); clutter_actor_add_constraint_with_name (rect, "size-bind", constraint); g_signal_connect (rect, "button-release-event", G_CALLBACK (on_button_release), NULL); rects[i] = rect; } clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/redhand.png0000664000175000017500000002007214723361714022006 0ustar fabiofabioPNG  IHDRbKGD pHYs  tIME 3x<,IDATx]y|M~>( "HQV> Q)UU:hPzRJu]\PBiBEDD99Z9kwg=Z888888888888888888888888D),rU0H#ۜ#Tىm!8Ad' ` (@9cɂ 4$ p.w Y) $m"!Eچ.RT 3 49m0L)c&f=(cl+TRJRJ/e-JcPJ#VboL)}RYp'l0t0;]X Lfse(4VuC]e J)SDؐmC)N)-۸6?! [R:ֆ^l*xE%#wo&&+b}i)JFuZ ` aa+<\0N<5uWT@m:t@P>M芋Qv~ 0Cpu٪EeB:8:A.BK-۸W`v2ϖ-vDN M^KKݠz }=s&&u156v- l0Dھ4^^6O x1J~ζqwGOHر$$t !dBJSRpaLzγuktZÆ:3CMw?J:l:/^ m|^f=H3y6z…h9tlu1{Fm i"KXn*b)ANJwe:\0AZ|ot7n|c.Ńpv_ԀE;wZɢEۆR\ۺ6דE-ÑҔW2QmӺPq٦637lZCEΉ 0AWµz%"%lnb9/GDRKca+.bi9R4ټ.TuiY^@-s +W \ApuvApipU],L],s +Ws +W \Ap \Ap +W . TNJ^Z v]ލX[ S}=P @􄛏. .AX}yC+A8x cp +W vBJhPrWSXUgQaKvѣ;wX ׯa(;reRW֮qMouuq3V(ؼ`= @WRf<3gx RG>\s^O0Jqu2Ǐ󞼛ۥ!sB{ _c O2EA4h||  D3F6264^GqӮ5d ҿ!t Ovh֩uxm Z-A[v)5@u:XX}֬m3A68!!d ̞ ]QdT?d1h4>C؜w#gX"ƍCsϡ>?׶l-[k(6rUwfwDz _}E?eP061Bj:bOfqqDj6jBG=^8䷻K qHX:ur=VawNkxic 4.Wx8]Ĥ$:>:|88)S xxpK8E˸1iՙH??͝>w,gXz}=̓Sw_.u+:&;w!߿]j!fL$&%eK>]ٳiɁ v'VD7ް+7C-͛ǹ pGϤ$9/D<qehw40憸3kVNcQp&-ZmyNt>OO\7O3)<Q~~HܴBD2O 1{wދ3- Ɛ vI'ӵZ$Xx l ܯ|rⷄիp#$Vpr?>13XDA_3,l1nZ],Gw<,B|h'A@ŋ pTKۼ9by9s t,:k>z4lq ~I`hSviT>J{o%5/t%GV._v0 u99RJDj rĀb }yWjxAkkjԔD< R)8NEco 6*)E4NՁJJ# TA:Aj)j1$_1iR[gh2y<+lljF4XM6?||NΜ.K$HCa" VԢԫbWCHgEgQgUEa[#X vv_)KRPX[ =GZ HQTCcQ'(u[tŋ5O8|wX>OV?XFwTCvZ/*eF~qDRJ*RSq7we/n&)VRE/ A0Jk|ˡC[ 5xy)VdiJU]aQ/OwMQtY#0ŗK~ c|Ʈ]3, ݖ-y"Ed%Q/j%o鸾cw< :#-[YG]vR]s7DԔ)h+vG$~*+n a lwg^ $UmD9zmx6VWO?aH}oL ۷\bfB>jƢϏ??!A oWD b@jU'h}m3֒O]i^3W.:.:i5rZScj LLD={9q7bBeˬZ\߱Ckޜ d/o.]$ULw 6oV=@hݹ-~&hat?xP_L ":H1c$W.grȦ߭w.?dq|cb{Yz[j)Q:<.Q1 hva,/[CUJCiJ ʏEcQ݉L4Cp<ݻJf4S92(1#!DA6j7mTÆ)zm 5 R_G}~>srTܚ5gVn׮FY_@ƍ>cJ! Qjpxß~/)t8t}0TVި_D܂bjA?(ꋘy+v^۲ nwEd̟/;9BH-dzE7Ғ%uT**pbh4+~N~ [˟+9I,Nhd;SݔҖo}fƐ1o2,*SUׯctD3IVb5A*2Gʳ//$\)֜q~tlvFȑ]T$c$qalYܤC a*V *OƩ瞃X:40i'gh:B2%^Ba Bڴie#G1~<í}wъĬ"kL򬩡#GYYA &0VW㷷FmN>"'L@W_l c Rڌr _ǯh((NO=N qV[Y2uJݳf[="}t(!Drݚt CUlulqs"tpTcu5.})~K-R qfJM1\2E b.x3g>_~(N-CFG;MQ8Î\Ih}w$,Q fW$WrN=쇤4h;q"^z -Z8(3*Ϝ%K\z}w풲30!d1/!jQ̤I6I 6>^gǎ!{R8w܄n˗OJvA0)N3I^$Xò4%ƏYXZ?$"'NOt](TGiJ rWDّ# pG T f~+YSSqz8+*lVO"?8iUWnߎk?<΂ q&)7pIL5=Xڬ,3W//48-~!<VQףEߏw*-Q"1nO|72O5I҆r Ut%%8=a*S迧 zkWxGFB4h<}Ǐ)4.5XG}>62WUIYE ͮooxGF«MxiPh' ``24>/ h((3Ȗ 13!$@ﺶe .̞ cM M C e%K6 \`M`9O>~{"gO>28Ɨ qQ|MScAv'93f~e An"9H"h1E`^|09D" 4}w Uh>pqڍb  D^p'縺aO0*A$;~IW}_!_ 1ßŀ#G1nbpr&H!kQڅQ}^| / ;!qj*o:M1ďMm}E򓒐Դeopxݿ<"O!3 7d IrɄ_~Aߢx^氹9s.gnsE2"/ cM Cю(=|bAVs"rD8EY]p"t8~%QԤDtvht^Y},!$ɡ r𪛱UgϢ*- 騹pOs;_Fѣل(#ljףh((iny9Luuz}jh<= ot4~]u."p(ܤ&X+κs4B,8b )] !mP!_v,j ' AӽٗxsX`r8,A?n#$x.x1TSD99k !;n&\yXG$KqfPJ#<+r]RRBsUq dx|8AĹ_]0@;8BT3 !ܟ3)420&hJsXsWե?nf$ cs 6M Ncx3a"54+ rR SpքpRDsVIENDB`muffin-6.4.1/src/tests/clutter/interactive/test-rotate-zoom.c0000664000175000017500000000626014723361714023277 0ustar fabiofabio/* * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or modify it * under the terms and conditions of the GNU Lesser General Public License, * version 2.1, as published by the Free Software Foundation. * * This program is distributed in the hope it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for * more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. * Boston, MA 02111-1307, USA. * */ #include #include #include #include #include #include #define STAGE_WIDTH 800 #define STAGE_HEIGHT 550 int test_rotate_zoom_main (int argc, char *argv[]); const char * test_rotate_zoom_describe (void); static ClutterActor * create_hand (void) { GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file (TESTS_DATADIR G_DIR_SEPARATOR_S "redhand.png", NULL); ClutterContent *image = clutter_image_new (); ClutterActor *actor = clutter_actor_new (); clutter_image_set_data (CLUTTER_IMAGE (image), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), gdk_pixbuf_get_rowstride (pixbuf), NULL); clutter_actor_set_content (actor, image); clutter_actor_set_size (actor, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf)); clutter_actor_set_reactive (actor, TRUE); g_object_unref (pixbuf); return actor; } G_MODULE_EXPORT int test_rotate_zoom_main (int argc, char *argv[]) { ClutterActor *stage, *actor; gfloat width, height; /* initialize Clutter */ if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; /* create a resizable stage */ stage = clutter_stage_new (); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); clutter_stage_set_title (CLUTTER_STAGE (stage), "Rotate and Zoom actions"); clutter_actor_set_size (stage, STAGE_WIDTH, STAGE_HEIGHT); clutter_actor_set_reactive (stage, FALSE); clutter_actor_show (stage); actor = create_hand (); clutter_actor_add_action (actor, clutter_rotate_action_new ()); clutter_actor_add_action (actor, clutter_zoom_action_new ()); clutter_actor_add_child (stage, actor); clutter_actor_get_size (actor, &width, &height); clutter_actor_set_position (actor, STAGE_WIDTH / 2 - width / 2, STAGE_HEIGHT / 2 - height / 2); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_rotate_zoom_describe (void) { return "Rotates and zooms an actor using touch events"; } muffin-6.4.1/src/tests/clutter/interactive/test-text-field.c0000664000175000017500000002557014723361714023071 0ustar fabiofabio#include #include #include gint test_text_field_main (gint argc, gchar **argv); const char * test_text_field_describe (void); static void on_entry_activate (ClutterText *text, gpointer data) { g_print ("Text activated: %s (cursor: %d, selection at: %d)\n", clutter_text_get_text (text), clutter_text_get_cursor_position (text), clutter_text_get_selection_bound (text)); } #define is_hex_digit(c) (((c) >= '0' && (c) <= '9') || \ ((c) >= 'a' && (c) <= 'f') || \ ((c) >= 'A' && (c) <= 'F')) #define to_hex_digit(c) (((c) <= '9') ? (c) - '0' : ((c) & 7) + 9) static gboolean on_captured_event (ClutterText *text, ClutterEvent *event, gpointer dummy G_GNUC_UNUSED) { gboolean is_unicode_mode = FALSE; gunichar c; guint keyval; if (event->type != CLUTTER_KEY_PRESS) return FALSE; is_unicode_mode = GPOINTER_TO_INT (g_object_get_data (G_OBJECT (text), "unicode-mode")); c = clutter_event_get_key_unicode (event); keyval = clutter_event_get_key_symbol (event); if (keyval == CLUTTER_KEY_U) { if (is_unicode_mode) { GString *str = g_object_get_data (G_OBJECT (text), "unicode-str"); clutter_text_set_preedit_string (text, NULL, NULL, 0); g_object_set_data (G_OBJECT (text), "unicode-mode", GINT_TO_POINTER (FALSE)); g_object_set_data (G_OBJECT (text), "unicode-str", NULL); g_string_free (str, TRUE); return FALSE; } if (clutter_event_has_control_modifier (event)) { PangoAttrList *attrs; PangoAttribute *a; GString *str = g_string_sized_new (5); g_string_append (str, "u"); g_object_set_data (G_OBJECT (text), "unicode-mode", GINT_TO_POINTER (TRUE)); g_object_set_data (G_OBJECT (text), "unicode-str", str); attrs = pango_attr_list_new (); a = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); a->start_index = 0; a->end_index = str->len; pango_attr_list_insert (attrs, a); clutter_text_set_preedit_string (text, str->str, attrs, str->len); pango_attr_list_unref (attrs); return TRUE; } return FALSE; } else if (is_unicode_mode && is_hex_digit (c)) { GString *str = g_object_get_data (G_OBJECT (text), "unicode-str"); PangoAttrList *attrs; PangoAttribute *a; gchar buf[8]; gsize len; len = g_unichar_to_utf8 (c, buf); buf[len] = '\0'; g_string_append (str, buf); g_print ("added '%s' to '%s' (len:%d)\n", buf, str->str, (int) str->len); attrs = pango_attr_list_new (); a = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); a->start_index = 0; a->end_index = str->len; pango_attr_list_insert (attrs, a); clutter_text_set_preedit_string (text, str->str, attrs, str->len); pango_attr_list_unref (attrs); return TRUE; } else if (is_unicode_mode && (keyval == CLUTTER_KEY_BackSpace)) { GString *str = g_object_get_data (G_OBJECT (text), "unicode-str"); PangoAttrList *attrs; PangoAttribute *a; g_string_truncate (str, str->len - 1); attrs = pango_attr_list_new (); a = pango_attr_underline_new (PANGO_UNDERLINE_SINGLE); a->start_index = 0; a->end_index = str->len; pango_attr_list_insert (attrs, a); clutter_text_set_preedit_string (text, str->str, attrs, str->len); pango_attr_list_unref (attrs); return TRUE; } else if (is_unicode_mode && (keyval == CLUTTER_KEY_Return || keyval == CLUTTER_KEY_KP_Enter || keyval == CLUTTER_KEY_ISO_Enter || keyval == CLUTTER_KEY_KP_Space)) { GString *str = g_object_get_data (G_OBJECT (text), "unicode-str"); const gchar *contents = clutter_text_get_text (text); gunichar uchar = 0; gchar ch; gint i; clutter_text_set_preedit_string (text, NULL, NULL, 0); g_object_set_data (G_OBJECT (text), "unicode-mode", GINT_TO_POINTER (FALSE)); g_object_set_data (G_OBJECT (text), "unicode-str", NULL); for (i = 0; i < str->len; i++) { ch = str->str[i]; if (is_hex_digit (ch)) uchar += ((gunichar) to_hex_digit (ch) << ((4 - i) * 4)); } g_assert (g_unichar_validate (uchar)); g_string_overwrite (str, 0, contents); g_string_insert_unichar (str, clutter_text_get_cursor_position (text), uchar); i = clutter_text_get_cursor_position (text); clutter_text_set_text (text, str->str); if (i >= 0) i += 1; else i = -1; clutter_text_set_cursor_position (text, i); clutter_text_set_selection_bound (text, i); g_string_free (str, TRUE); return TRUE; } else return FALSE; } static ClutterActor * create_label (const ClutterColor *color, const gchar *text) { ClutterActor *retval = clutter_text_new (); clutter_text_set_color (CLUTTER_TEXT (retval), color); clutter_text_set_markup (CLUTTER_TEXT (retval), text); clutter_text_set_editable (CLUTTER_TEXT (retval), FALSE); clutter_text_set_selectable (CLUTTER_TEXT (retval), FALSE); clutter_text_set_single_line_mode (CLUTTER_TEXT (retval), TRUE); clutter_text_set_ellipsize (CLUTTER_TEXT (retval), PANGO_ELLIPSIZE_END); return retval; } static ClutterActor * create_entry (const ClutterColor *color, const gchar *text, PangoAttrList *attrs, gunichar password_char, gint max_length) { ClutterActor *retval = clutter_text_new_full (NULL, text, color); ClutterColor selection = { 0, }; ClutterColor selected_text = { 0x00, 0x00, 0xff, 0xff }; clutter_actor_set_reactive (retval, TRUE); clutter_color_darken (color, &selection); clutter_text_set_editable (CLUTTER_TEXT (retval), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (retval), TRUE); clutter_text_set_activatable (CLUTTER_TEXT (retval), TRUE); clutter_text_set_single_line_mode (CLUTTER_TEXT (retval), TRUE); clutter_text_set_password_char (CLUTTER_TEXT (retval), password_char); clutter_text_set_cursor_color (CLUTTER_TEXT (retval), &selection); clutter_text_set_max_length (CLUTTER_TEXT (retval), max_length); clutter_text_set_selected_text_color (CLUTTER_TEXT (retval), &selected_text); clutter_actor_set_background_color (retval, CLUTTER_COLOR_LightGray); if (attrs) clutter_text_set_attributes (CLUTTER_TEXT (retval), attrs); g_signal_connect (retval, "activate", G_CALLBACK (on_entry_activate), NULL); g_signal_connect (retval, "captured-event", G_CALLBACK (on_captured_event), NULL); return retval; } G_MODULE_EXPORT gint test_text_field_main (gint argc, gchar **argv) { ClutterActor *stage; ClutterActor *box, *label, *entry; ClutterLayoutManager *grid; PangoAttrList *entry_attrs; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Text Fields"); clutter_actor_set_background_color (stage, CLUTTER_COLOR_Black); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); grid = clutter_grid_layout_new (); clutter_grid_layout_set_column_spacing (CLUTTER_GRID_LAYOUT (grid), 6); clutter_grid_layout_set_row_spacing (CLUTTER_GRID_LAYOUT (grid), 6); box = clutter_actor_new (); clutter_actor_set_layout_manager (box, grid); clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_WIDTH, -24.0)); clutter_actor_add_constraint (box, clutter_bind_constraint_new (stage, CLUTTER_BIND_HEIGHT, -24.0)); clutter_actor_set_position (box, 12, 12); clutter_actor_add_child (stage, box); label = create_label (CLUTTER_COLOR_White, "Input field:"); g_object_set (label, "min-width", 150.0, NULL); clutter_actor_add_child (box, label); clutter_layout_manager_child_set (grid, CLUTTER_CONTAINER (box), label, "row", 0, "column", 0, "x-expand", FALSE, "y-expand", FALSE, NULL); entry_attrs = pango_attr_list_new (); pango_attr_list_insert (entry_attrs, pango_attr_underline_new (PANGO_UNDERLINE_ERROR)); pango_attr_list_insert (entry_attrs, pango_attr_underline_color_new (65535, 0, 0)); entry = create_entry (CLUTTER_COLOR_Black, "somme misspeeled textt", entry_attrs, 0, 0); clutter_actor_add_child (box, entry); clutter_layout_manager_child_set (grid, CLUTTER_CONTAINER (box), entry, "row", 0, "column", 1, "x-expand", TRUE, "x-fill", TRUE, "y-expand", FALSE, NULL); clutter_actor_grab_key_focus (entry); label = create_label (CLUTTER_COLOR_White, "A very long password field:"); clutter_actor_add_child (box, label); clutter_layout_manager_child_set (grid, CLUTTER_CONTAINER (box), label, "row", 1, "column", 0, "x-expand", FALSE, "y-expand", FALSE, NULL); entry = create_entry (CLUTTER_COLOR_Black, "password", NULL, '*', 8); clutter_actor_add_child (box, entry); clutter_layout_manager_child_set (grid, CLUTTER_CONTAINER (box), entry, "row", 1, "column", 1, "x-expand", TRUE, "x-fill", TRUE, "y-expand", FALSE, NULL); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_text_field_describe (void) { return "Text actor single-line and password mode support\n" "\n" "This test checks the :single-line-mode and :password-char properties of\n" "the ClutterText actor, plus the password hint feature and the :max-length\n" "property."; } muffin-6.4.1/src/tests/clutter/interactive/test-cairo-clock.c0000664000175000017500000000654014723361714023206 0ustar fabiofabio#include #include #include #include int test_cairo_clock_main (int argc, char *argv[]); const char * test_cairo_clock_describe (void); static gboolean draw_clock (ClutterCanvas *canvas, cairo_t *cr, int width, int height) { GDateTime *now; float hours, minutes, seconds; /* get the current time and compute the angles */ now = g_date_time_new_now_local (); seconds = g_date_time_get_second (now) * G_PI / 30; minutes = g_date_time_get_minute (now) * G_PI / 30; hours = g_date_time_get_hour (now) * G_PI / 6; /* clear the contents of the canvas, to avoid painting * over the previous frame */ cairo_save (cr); cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 1.0); cairo_set_operator (cr, CAIRO_OPERATOR_SOURCE); cairo_paint (cr); cairo_restore (cr); /* scale the modelview to the size of the surface */ cairo_scale (cr, width, height); cairo_set_line_cap (cr, CAIRO_LINE_CAP_ROUND); cairo_set_line_width (cr, 0.1); /* the black rail that holds the seconds indicator */ clutter_cairo_set_source_color (cr, CLUTTER_COLOR_Black); cairo_translate (cr, 0.5, 0.5); cairo_arc (cr, 0, 0, 0.4, 0, G_PI * 2); cairo_stroke (cr); /* the seconds indicator */ clutter_cairo_set_source_color (cr, CLUTTER_COLOR_White); cairo_move_to (cr, 0, 0); cairo_arc (cr, sinf (seconds) * 0.4, - cosf (seconds) * 0.4, 0.05, 0, G_PI * 2); cairo_fill (cr); /* the minutes hand */ clutter_cairo_set_source_color (cr, CLUTTER_COLOR_DarkChameleon); cairo_move_to (cr, 0, 0); cairo_line_to (cr, sinf (minutes) * 0.4, -cosf (minutes) * 0.4); cairo_stroke (cr); /* the hours hand */ cairo_move_to (cr, 0, 0); cairo_line_to (cr, sinf (hours) * 0.2, -cosf (hours) * 0.2); cairo_stroke (cr); g_date_time_unref (now); /* we're done drawing */ return TRUE; } static gboolean invalidate_clock (gpointer data_) { /* invalidate the contents of the canvas */ clutter_content_invalidate (data_); /* keep the timeout source */ return TRUE; } G_MODULE_EXPORT int test_cairo_clock_main (int argc, char *argv[]) { ClutterActor *stage; ClutterContent *canvas; /* initialize Clutter */ if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; /* create a resizable stage */ stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "2D Clock"); clutter_actor_set_background_color (stage, CLUTTER_COLOR_LightSkyBlue); clutter_actor_set_size (stage, 300, 300); clutter_actor_show (stage); /* our 2D canvas, courtesy of Cairo */ canvas = clutter_canvas_new (); clutter_canvas_set_size (CLUTTER_CANVAS (canvas), 300, 300); clutter_actor_set_content (stage, canvas); /* quit on destroy */ g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* connect our drawing code */ g_signal_connect (canvas, "draw", G_CALLBACK (draw_clock), NULL); /* invalidate the canvas, so that we can draw before the main loop starts */ clutter_content_invalidate (canvas); /* set up a timer that invalidates the canvas every second */ clutter_threads_add_timeout (1000, invalidate_clock, canvas); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_cairo_clock_describe (void) { return "Simple 2D canvas using a Cairo texture actor"; } muffin-6.4.1/src/tests/clutter/interactive/test-cairo-flowers.c0000664000175000017500000001432214723361714023571 0ustar fabiofabio/* * Pretty cairo flower hack. */ #include #ifndef _MSC_VER #include /* for sleep(), used for screenshots */ #endif #include #ifdef _MSC_VER #define _USE_MATH_DEFINES #endif #include #define PETAL_MIN 20 #define PETAL_VAR 40 #define N_FLOWERS 40 /* reduce if you have a small card */ typedef struct Flower { ClutterActor *ctex; gint x,y,rot,v,rv; } Flower; static ClutterActor *stage = NULL; int test_cairo_flowers_main (int argc, char **argv); const char * test_cairo_flowers_describe (void); static gboolean draw_flower (ClutterCanvas *canvas, cairo_t *cr, gint width, gint height, gpointer user_data) { /* No science here, just a hack from toying */ gint i, j; double colors[] = { 0.71, 0.81, 0.83, 1.0, 0.78, 0.57, 0.64, 0.30, 0.35, 0.73, 0.40, 0.39, 0.91, 0.56, 0.64, 0.70, 0.47, 0.45, 0.92, 0.75, 0.60, 0.82, 0.86, 0.85, 0.51, 0.56, 0.67, 1.0, 0.79, 0.58, }; gint size; gint petal_size; gint n_groups; /* Num groups of petals 1-3 */ gint n_petals; /* num of petals 4 - 8 */ gint pm1, pm2; gint idx, last_idx = -1; petal_size = GPOINTER_TO_INT (user_data); size = petal_size * 8; n_groups = rand() % 3 + 1; cairo_set_tolerance (cr, 0.1); /* Clear */ cairo_set_operator (cr, CAIRO_OPERATOR_CLEAR); cairo_paint(cr); cairo_set_operator (cr, CAIRO_OPERATOR_OVER); cairo_translate(cr, size/2, size/2); for (i=0; iy += flowers[i]->v; flowers[i]->rot += flowers[i]->rv; if (flowers[i]->y > (gint) clutter_actor_get_height (stage)) flowers[i]->y = -clutter_actor_get_height (flowers[i]->ctex); clutter_actor_set_position (flowers[i]->ctex, flowers[i]->x, flowers[i]->y); clutter_actor_set_rotation (flowers[i]->ctex, CLUTTER_Z_AXIS, flowers[i]->rot, clutter_actor_get_width (flowers[i]->ctex)/2, clutter_actor_get_height (flowers[i]->ctex)/2, 0); } } static void stop_and_quit (ClutterActor *actor, ClutterTimeline *timeline) { clutter_timeline_stop (timeline); clutter_main_quit (); } G_MODULE_EXPORT int test_cairo_flowers_main (int argc, char **argv) { Flower *flowers[N_FLOWERS]; ClutterTimeline *timeline; int i; srand (time (NULL)); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; /* Create a timeline to manage animation */ timeline = clutter_timeline_new (6000); clutter_timeline_set_repeat_count (timeline, -1); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cairo Flowers"); g_signal_connect (stage, "destroy", G_CALLBACK (stop_and_quit), timeline); clutter_actor_set_background_color (stage, CLUTTER_COLOR_Black); for (i=0; i< N_FLOWERS; i++) { flowers[i] = g_new0(Flower, 1); flowers[i]->ctex = make_flower_actor(); flowers[i]->x = rand() % (int) clutter_actor_get_width (stage) - (PETAL_MIN + PETAL_VAR) * 2; flowers[i]->y = rand() % (int) clutter_actor_get_height (stage); flowers[i]->rv = rand() % 5 + 1; flowers[i]->v = rand() % 10 + 2; clutter_container_add_actor (CLUTTER_CONTAINER (stage), flowers[i]->ctex); clutter_actor_set_position (flowers[i]->ctex, flowers[i]->x, flowers[i]->y); } /* fire a callback for frame change */ g_signal_connect (timeline, "new-frame", G_CALLBACK (tick), flowers); clutter_actor_show (stage); clutter_timeline_start (timeline); g_signal_connect (stage, "key-press-event", G_CALLBACK (clutter_main_quit), NULL); clutter_main(); g_object_unref (timeline); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_cairo_flowers_describe (void) { return "Drawing pretty flowers with Cairo"; } muffin-6.4.1/src/tests/clutter/interactive/test-cogl-multitexture.c0000664000175000017500000001771314723361714024521 0ustar fabiofabio#include #include #include #include #include #include #include #include typedef struct _TestMultiLayerMaterialState { ClutterActor *group; CoglHandle alpha_tex; CoglHandle redhand_tex; gfloat *tex_coords; ClutterTimeline *timeline; CoglHandle material0; CoglMatrix tex_matrix0; CoglMatrix rot_matrix0; CoglHandle light_tex0; CoglHandle material1; CoglMatrix tex_matrix1; CoglMatrix rot_matrix1; CoglHandle light_tex1; } TestMultiLayerMaterialState; int test_cogl_multitexture_main (int argc, char *argv[]); const char * test_cogl_multitexture_describe (void); static void frame_cb (ClutterTimeline *timeline, gint frame_no, gpointer data) { TestMultiLayerMaterialState *state = data; cogl_matrix_multiply (&state->tex_matrix0, &state->tex_matrix0, &state->rot_matrix0); cogl_material_set_layer_matrix (state->material0, 2, &state->tex_matrix0); cogl_matrix_multiply (&state->tex_matrix1, &state->tex_matrix1, &state->rot_matrix1); cogl_material_set_layer_matrix (state->material1, 2, &state->tex_matrix1); } static void material_rectangle_paint (ClutterActor *actor, ClutterPaintContext *paint_context, gpointer data) { TestMultiLayerMaterialState *state = data; CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_translate (framebuffer, 150, 15, 0); cogl_framebuffer_draw_multitextured_rectangle (framebuffer, COGL_FRAMEBUFFER (state->material0), 0, 0, 200, 213, state->tex_coords, 12); cogl_framebuffer_translate (framebuffer, -300, -30, 0); cogl_framebuffer_draw_multitextured_rectangle (framebuffer, COGL_FRAMEBUFFER (state->material1), 0, 0, 200, 213, state->tex_coords, 12); cogl_framebuffer_pop_matrix (framebuffer); } static void animation_completed_cb (ClutterAnimation *animation, TestMultiLayerMaterialState *state) { static gboolean go_back = FALSE; gdouble new_rotation_y; if (go_back) new_rotation_y = 30; else new_rotation_y = -30; go_back = !go_back; clutter_actor_animate_with_timeline (state->group, CLUTTER_LINEAR, state->timeline, "rotation-angle-y", new_rotation_y, "signal-after::completed", animation_completed_cb, state, NULL); } G_MODULE_EXPORT int test_cogl_multitexture_main (int argc, char *argv[]) { GError *error = NULL; ClutterActor *stage; ClutterColor stage_color = { 0x61, 0x56, 0x56, 0xff }; g_autofree TestMultiLayerMaterialState *state = g_new0 (TestMultiLayerMaterialState, 1); gfloat stage_w, stage_h; gchar **files; gfloat tex_coords[] = { /* tx1 ty1 tx2 ty2 */ 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1 }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_actor_get_size (stage, &stage_w, &stage_h); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl: Multi-texturing"); clutter_stage_set_color (CLUTTER_STAGE (stage), &stage_color); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); /* We create a non-descript actor that we know doesn't have a * default paint handler, so that we can easily control * painting in a paint signal handler, without having to * sub-class anything etc. */ state->group = clutter_group_new (); clutter_actor_set_position (state->group, stage_w / 2, stage_h / 2); g_signal_connect (state->group, "paint", G_CALLBACK(material_rectangle_paint), state); files = g_new (gchar*, 4); files[0] = g_build_filename (TESTS_DATADIR, "redhand_alpha.png", NULL); files[1] = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); files[2] = g_build_filename (TESTS_DATADIR, "light0.png", NULL); files[3] = NULL; state->alpha_tex = cogl_texture_new_from_file (files[0], COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY, &error); if (!state->alpha_tex) g_critical ("Failed to load redhand_alpha.png: %s", error->message); state->redhand_tex = cogl_texture_new_from_file (files[1], COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY, &error); if (!state->redhand_tex) g_critical ("Failed to load redhand.png: %s", error->message); state->light_tex0 = cogl_texture_new_from_file (files[2], COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY, &error); if (!state->light_tex0) g_critical ("Failed to load light0.png: %s", error->message); state->light_tex1 = cogl_texture_new_from_file (files[2], COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_ANY, &error); if (!state->light_tex1) g_critical ("Failed to load light0.png: %s", error->message); g_strfreev (files); state->material0 = cogl_material_new (); cogl_material_set_layer (state->material0, 0, state->alpha_tex); cogl_material_set_layer (state->material0, 1, state->redhand_tex); cogl_material_set_layer (state->material0, 2, state->light_tex0); state->material1 = cogl_material_new (); cogl_material_set_layer (state->material1, 0, state->alpha_tex); cogl_material_set_layer (state->material1, 1, state->redhand_tex); cogl_material_set_layer (state->material1, 2, state->light_tex1); state->tex_coords = tex_coords; cogl_matrix_init_identity (&state->tex_matrix0); cogl_matrix_init_identity (&state->tex_matrix1); cogl_matrix_init_identity (&state->rot_matrix0); cogl_matrix_init_identity (&state->rot_matrix1); cogl_matrix_translate (&state->rot_matrix0, 0.5, 0.5, 0); cogl_matrix_rotate (&state->rot_matrix0, 10.0, 0, 0, 1.0); cogl_matrix_translate (&state->rot_matrix0, -0.5, -0.5, 0); cogl_matrix_translate (&state->rot_matrix1, 0.5, 0.5, 0); cogl_matrix_rotate (&state->rot_matrix1, -10.0, 0, 0, 1.0); cogl_matrix_translate (&state->rot_matrix1, -0.5, -0.5, 0); clutter_actor_set_anchor_point (state->group, 86, 125); clutter_container_add_actor (CLUTTER_CONTAINER(stage), state->group); state->timeline = clutter_timeline_new (2812); g_signal_connect (state->timeline, "new-frame", G_CALLBACK (frame_cb), state); clutter_actor_animate_with_timeline (state->group, CLUTTER_LINEAR, state->timeline, "rotation-angle-y", 30.0, "signal-after::completed", animation_completed_cb, state, NULL); /* start the timeline and thus the animations */ clutter_timeline_start (state->timeline); clutter_actor_show_all (stage); clutter_main(); cogl_object_unref (state->material1); cogl_object_unref (state->material0); cogl_object_unref (state->alpha_tex); cogl_object_unref (state->redhand_tex); cogl_object_unref (state->light_tex0); cogl_object_unref (state->light_tex1); g_free (state); return 0; } G_MODULE_EXPORT const char * test_cogl_multitexture_describe (void) { return "Multi-texturing support in Cogl."; } muffin-6.4.1/src/tests/clutter/interactive/test-actors.c0000664000175000017500000001621414723361714022312 0ustar fabiofabio#include #include #include #include #include #include #include "test-utils.h" #define NHANDS 6 typedef struct SuperOH { ClutterActor **hand; ClutterActor *bgtex; ClutterActor *real_hand; ClutterActor *group; ClutterActor *stage; gint stage_width; gint stage_height; gfloat radius; ClutterTimeline *timeline; } SuperOH; static gint n_hands = NHANDS; int test_actors_main (int argc, char *argv[]); static GOptionEntry super_oh_entries[] = { { "num-hands", 'n', 0, G_OPTION_ARG_INT, &n_hands, "Number of hands", "HANDS" }, { NULL } }; static void on_group_destroy (ClutterActor *actor, SuperOH *oh) { oh->group = NULL; } static void on_hand_destroy (ClutterActor *actor, SuperOH *oh) { int i; for (i = 0; i < n_hands; i++) { if (oh->hand[i] == actor) oh->hand[i] = NULL; } } static gboolean on_button_press_event (ClutterActor *actor, ClutterEvent *event, SuperOH *oh) { gfloat x, y; clutter_event_get_coords (event, &x, &y); g_print ("*** button press event (button:%d) at %.2f, %.2f on %s ***\n", clutter_event_get_button (event), x, y, clutter_actor_get_name (actor)); clutter_actor_hide (actor); return TRUE; } static gboolean input_cb (ClutterActor *stage, ClutterEvent *event, gpointer data) { SuperOH *oh = data; if (event->type == CLUTTER_KEY_RELEASE) { g_print ("*** key press event (key:%c) ***\n", clutter_event_get_key_symbol (event)); if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_q) { clutter_main_quit (); return TRUE; } else if (clutter_event_get_key_symbol (event) == CLUTTER_KEY_r) { gint i; for (i = 0; i < n_hands; i++) { if (oh->hand[i] != NULL) clutter_actor_show (oh->hand[i]); } return TRUE; } } return FALSE; } /* Timeline handler */ static void frame_cb (ClutterTimeline *timeline, gint msecs, gpointer data) { SuperOH *oh = data; gint i; float rotation = clutter_timeline_get_progress (timeline) * 360.0f; /* Rotate everything clockwise about stage center*/ if (oh->group != NULL) clutter_actor_set_rotation (oh->group, CLUTTER_Z_AXIS, rotation, oh->stage_width / 2, oh->stage_height / 2, 0); for (i = 0; i < n_hands; i++) { /* Rotate each hand around there centers - to get this we need * to take into account any scaling. */ if (oh->hand[i] != NULL) clutter_actor_set_rotation (oh->hand[i], CLUTTER_Z_AXIS, -6.0 * rotation, 0, 0, 0); } } static void stop_and_quit (ClutterActor *stage, SuperOH *data) { clutter_timeline_stop (data->timeline); clutter_main_quit (); } G_MODULE_EXPORT int test_actors_main (int argc, char *argv[]) { SuperOH *oh; gint i; GError *error; ClutterActor *real_hand; gchar *file; error = NULL; if (clutter_init_with_args (&argc, &argv, NULL, super_oh_entries, NULL, &error) != CLUTTER_INIT_SUCCESS) { g_warning ("Unable to initialise Clutter:\n%s", error->message); g_error_free (error); return EXIT_FAILURE; } oh = g_new (SuperOH, 1); oh->stage = clutter_stage_new (); clutter_actor_set_size (oh->stage, 800, 600); clutter_actor_set_name (oh->stage, "Default Stage"); clutter_actor_set_background_color (oh->stage, CLUTTER_COLOR_LightSkyBlue); g_signal_connect (oh->stage, "destroy", G_CALLBACK (stop_and_quit), oh); clutter_stage_set_title (CLUTTER_STAGE (oh->stage), "Actors"); /* Create a timeline to manage animation */ oh->timeline = clutter_timeline_new (6000); clutter_timeline_set_repeat_count (oh->timeline, -1); /* fire a callback for frame change */ g_signal_connect (oh->timeline, "new-frame", G_CALLBACK (frame_cb), oh); file = g_build_filename (TESTS_DATADIR, "redhand.png", NULL); real_hand = clutter_test_utils_create_texture_from_file (file, &error); if (real_hand == NULL) g_error ("image load failed: %s", error->message); g_free (file); /* create a new actor to hold other actors */ oh->group = clutter_actor_new (); clutter_actor_set_layout_manager (oh->group, clutter_fixed_layout_new ()); clutter_actor_set_name (oh->group, "Group"); g_signal_connect (oh->group, "destroy", G_CALLBACK (on_group_destroy), oh); clutter_actor_add_constraint (oh->group, clutter_align_constraint_new (oh->stage, CLUTTER_ALIGN_BOTH, 0.5)); clutter_actor_add_constraint (oh->group, clutter_bind_constraint_new (oh->stage, CLUTTER_BIND_SIZE, 0.0f)); oh->hand = g_new (ClutterActor*, n_hands); oh->stage_width = clutter_actor_get_width (oh->stage); oh->stage_height = clutter_actor_get_height (oh->stage); oh->radius = (oh->stage_width + oh->stage_height) / n_hands; for (i = 0; i < n_hands; i++) { gint x, y, w, h; if (i == 0) { oh->hand[i] = real_hand; clutter_actor_set_name (oh->hand[i], "Real Hand"); } else { oh->hand[i] = clutter_clone_new (real_hand); clutter_actor_set_name (oh->hand[i], "Clone Hand"); } clutter_actor_set_reactive (oh->hand[i], TRUE); clutter_actor_set_size (oh->hand[i], 200, 213); /* Place around a circle */ w = clutter_actor_get_width (oh->hand[i]); h = clutter_actor_get_height (oh->hand[i]); x = oh->stage_width / 2 + oh->radius * cos (i * G_PI / (n_hands / 2)) - w / 2; y = oh->stage_height / 2 + oh->radius * sin (i * G_PI / (n_hands / 2)) - h / 2; clutter_actor_set_position (oh->hand[i], x, y); clutter_actor_move_anchor_point_from_gravity (oh->hand[i], CLUTTER_GRAVITY_CENTER); /* Add to our group group */ clutter_container_add_actor (CLUTTER_CONTAINER (oh->group), oh->hand[i]); g_signal_connect (oh->hand[i], "button-press-event", G_CALLBACK (on_button_press_event), oh); g_signal_connect (oh->hand[i], "destroy", G_CALLBACK (on_hand_destroy), oh); } /* Add the group to the stage */ clutter_container_add_actor (CLUTTER_CONTAINER (oh->stage), oh->group); /* Show everying */ clutter_actor_show (oh->stage); g_signal_connect (oh->stage, "key-release-event", G_CALLBACK (input_cb), oh); /* and start it */ clutter_timeline_start (oh->timeline); clutter_main (); clutter_timeline_stop (oh->timeline); /* clean up */ g_object_unref (oh->timeline); g_free (oh->hand); g_free (oh); return EXIT_SUCCESS; } muffin-6.4.1/src/tests/clutter/interactive/test-stage-sizing.c0000664000175000017500000000573014723361714023424 0ustar fabiofabio#include #include #include int test_stage_sizing_main (int argc, char *argv[]); const char * test_stage_sizing_describe (void); static gboolean shrink_clicked_cb (ClutterActor *stage) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); clutter_actor_set_size (stage, MAX (0, width - 10.f), MAX (0, height - 10.f)); return CLUTTER_EVENT_STOP; } static gboolean expand_clicked_cb (ClutterActor *stage) { gfloat width, height; clutter_actor_get_size (stage, &width, &height); clutter_actor_set_size (stage, width + 10.f, height + 10.f); return CLUTTER_EVENT_STOP; } G_MODULE_EXPORT int test_stage_sizing_main (int argc, char *argv[]) { ClutterActor *stage, *rect, *label, *box; ClutterMargin margin = { 12.f, 12.f, 6.f, 6.f }; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Stage Sizing"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); box = clutter_actor_new (); clutter_actor_set_layout_manager (box, clutter_box_layout_new ()); clutter_actor_add_constraint (box, clutter_align_constraint_new (stage, CLUTTER_ALIGN_BOTH, 0.5)); clutter_actor_add_child (stage, box); rect = clutter_actor_new (); clutter_actor_set_layout_manager (rect, clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER)); clutter_actor_set_background_color (rect, CLUTTER_COLOR_SkyBlue); clutter_actor_set_reactive (rect, TRUE); g_signal_connect_swapped (rect, "button-press-event", G_CALLBACK (shrink_clicked_cb), stage); label = clutter_text_new_with_text ("Sans 16", "Shrink"); clutter_actor_set_margin (label, &margin); clutter_actor_add_child (rect, label); clutter_actor_add_child (box, rect); rect = clutter_actor_new (); clutter_actor_set_layout_manager (rect, clutter_bin_layout_new (CLUTTER_BIN_ALIGNMENT_CENTER, CLUTTER_BIN_ALIGNMENT_CENTER)); clutter_actor_set_background_color (rect, CLUTTER_COLOR_Butter); clutter_actor_set_reactive (rect, TRUE); g_signal_connect_swapped (rect, "button-press-event", G_CALLBACK (expand_clicked_cb), stage); label = clutter_text_new_with_text ("Sans 16", "Expand"); clutter_actor_set_margin (label, &margin); clutter_actor_add_child (rect, label); clutter_actor_add_child (box, rect); clutter_stage_set_minimum_size (CLUTTER_STAGE (stage), clutter_actor_get_width (box), clutter_actor_get_height (box)); clutter_actor_show (stage); clutter_main (); return EXIT_SUCCESS; } G_MODULE_EXPORT const char * test_stage_sizing_describe (void) { return "Check stage sizing policies."; } muffin-6.4.1/src/tests/clutter/interactive/meson/0000775000175000017500000000000014723361714021013 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/interactive/meson/gen-test-unit-names.sh0000775000175000017500000000055014723361714025156 0ustar fabiofabio#!/bin/sh outputfile=$1 shift echo '/* ** This file is autogenerated. Do not edit. ** */' > "$outputfile" echo '' >> "$outputfile" echo 'const char *test_unit_names[] = {' >> "$outputfile" for test_source_file in "$@"; do echo " \"$(echo "$test_source_file" | sed 's/.*\(test-[a-z0-9\-]\+\)\.c/\1/')\"," >> "$outputfile" done echo '};' >> "$outputfile" muffin-6.4.1/src/tests/clutter/interactive/test-cogl-point-sprites.c0000664000175000017500000001753114723361714024564 0ustar fabiofabio#include #include #include #include #include #define N_FIREWORKS 32 /* Units per second per second */ #define GRAVITY -1.5f #define N_SPARKS (N_FIREWORKS * 32) /* Must be a power of two */ #define TIME_PER_SPARK 0.01f /* in seconds */ #define TEXTURE_SIZE 32 typedef struct _Firework Firework; struct _Firework { float size; float x, y; float start_x, start_y; ClutterColor color; /* Velocities are in units per second */ float initial_x_velocity; float initial_y_velocity; GTimer *timer; }; typedef struct _Spark Spark; struct _Spark { float x, y; ClutterColor color; ClutterColor base_color; }; typedef struct _Data Data; struct _Data { Firework fireworks[N_FIREWORKS]; int next_spark_num; Spark sparks[N_SPARKS]; GTimer *last_spark_time; CoglMaterial *material; }; int test_cogl_point_sprites_main (int argc, char *argv[]); const char * test_cogl_point_sprites_describe (void); static CoglHandle generate_round_texture (void) { guint8 *p, *data; int x, y; CoglHandle tex; p = data = g_malloc (TEXTURE_SIZE * TEXTURE_SIZE * 4); /* Generate a yellow circle which gets transparent towards the edges */ for (y = 0; y < TEXTURE_SIZE; y++) for (x = 0; x < TEXTURE_SIZE; x++) { int dx = x - TEXTURE_SIZE / 2; int dy = y - TEXTURE_SIZE / 2; float value = sqrtf (dx * dx + dy * dy) * 255.0 / (TEXTURE_SIZE / 2); if (value > 255.0f) value = 255.0f; value = 255.0f - value; *(p++) = value; *(p++) = value; *(p++) = value; *(p++) = value; } tex = cogl_texture_new_from_data (TEXTURE_SIZE, TEXTURE_SIZE, COGL_TEXTURE_NO_SLICING, COGL_PIXEL_FORMAT_RGBA_8888_PRE, COGL_PIXEL_FORMAT_ANY, TEXTURE_SIZE * 4, data); g_free (data); return tex; } static void paint_cb (ClutterActor *stage, ClutterPaintContext *paint_context, Data *data) { CoglFramebuffer *framebuffer = clutter_paint_context_get_framebuffer (paint_context); CoglMatrix old_matrix, new_matrix; int i; float diff_time; cogl_framebuffer_get_projection_matrix (framebuffer, &old_matrix); /* Use an orthogonal projection from -1 -> 1 in both axes */ cogl_matrix_init_identity (&new_matrix); cogl_framebuffer_set_projection_matrix (framebuffer, &new_matrix); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_set_modelview_matrix (framebuffer, &new_matrix); /* Update all of the firework's positions */ for (i = 0; i < N_FIREWORKS; i++) { Firework *firework = data->fireworks + i; if ((fabsf (firework->x - firework->start_x) > 2.0f) || firework->y < -1.0f) { firework->size = g_random_double_range (0.001f, 0.1f); firework->start_x = 1.0f + firework->size; firework->start_y = -1.0f; firework->initial_x_velocity = g_random_double_range (-0.1f, -2.0f); firework->initial_y_velocity = g_random_double_range (0.1f, 4.0f); g_timer_reset (firework->timer); /* Pick a random color out of six */ if (g_random_boolean ()) { memset (&firework->color, 0, sizeof (ClutterColor)); ((guint8 *) &firework->color)[g_random_int_range (0, 3)] = 255; } else { memset (&firework->color, 255, sizeof (ClutterColor)); ((guint8 *) &firework->color)[g_random_int_range (0, 3)] = 0; } firework->color.alpha = 255; /* Fire some of the fireworks from the other side */ if (g_random_boolean ()) { firework->start_x = -firework->start_x; firework->initial_x_velocity = -firework->initial_x_velocity; } } diff_time = g_timer_elapsed (firework->timer, NULL); firework->x = (firework->start_x + firework->initial_x_velocity * diff_time); firework->y = ((firework->initial_y_velocity * diff_time + 0.5f * GRAVITY * diff_time * diff_time) + firework->start_y); } diff_time = g_timer_elapsed (data->last_spark_time, NULL); if (diff_time < 0.0f || diff_time >= TIME_PER_SPARK) { /* Add a new spark for each firework, overwriting the oldest ones */ for (i = 0; i < N_FIREWORKS; i++) { Spark *spark = data->sparks + data->next_spark_num; Firework *firework = data->fireworks + i; spark->x = (firework->x + g_random_double_range (-firework->size / 2.0f, firework->size / 2.0f)); spark->y = (firework->y + g_random_double_range (-firework->size / 2.0f, firework->size / 2.0f)); spark->base_color = firework->color; data->next_spark_num = (data->next_spark_num + 1) & (N_SPARKS - 1); } /* Update the colour of each spark */ for (i = 0; i < N_SPARKS; i++) { float color_value; /* First spark is the oldest */ Spark *spark = data->sparks + ((data->next_spark_num + i) & (N_SPARKS - 1)); color_value = i / (N_SPARKS - 1.0f); spark->color.red = spark->base_color.red * color_value; spark->color.green = spark->base_color.green * color_value; spark->color.blue = spark->base_color.blue * color_value; spark->color.alpha = 255.0f * color_value; } g_timer_reset (data->last_spark_time); } cogl_framebuffer_set_projection_matrix (framebuffer, &old_matrix); cogl_framebuffer_pop_matrix (framebuffer); } static gboolean idle_cb (gpointer data) { clutter_actor_queue_redraw (data); return G_SOURCE_CONTINUE; } G_MODULE_EXPORT int test_cogl_point_sprites_main (int argc, char *argv[]) { ClutterActor *stage; CoglHandle tex; Data data; GError *error = NULL; int i; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return EXIT_FAILURE; data.material = cogl_material_new (); data.last_spark_time = g_timer_new (); data.next_spark_num = 0; cogl_material_set_point_size (data.material, TEXTURE_SIZE); tex = generate_round_texture (); cogl_material_set_layer (data.material, 0, tex); cogl_object_unref (tex); if (!cogl_material_set_layer_point_sprite_coords_enabled (data.material, 0, TRUE, &error)) { g_warning ("Failed to enable point sprite coords: %s", error->message); g_clear_error (&error); } for (i = 0; i < N_FIREWORKS; i++) { data.fireworks[i].x = -FLT_MAX; data.fireworks[i].y = FLT_MAX; data.fireworks[i].size = 0.0f; data.fireworks[i].timer = g_timer_new (); } for (i = 0; i < N_SPARKS; i++) { data.sparks[i].x = 2.0f; data.sparks[i].y = 2.0f; } stage = clutter_stage_new (); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_Black); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cogl Point Sprites"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect_after (stage, "paint", G_CALLBACK (paint_cb), &data); clutter_actor_show (stage); clutter_threads_add_idle (idle_cb, stage); clutter_main (); cogl_object_unref (data.material); g_timer_destroy (data.last_spark_time); for (i = 0; i < N_FIREWORKS; i++) g_timer_destroy (data.fireworks[i].timer); return 0; } G_MODULE_EXPORT const char * test_cogl_point_sprites_describe (void) { return "Point sprites support in Cogl."; } muffin-6.4.1/src/tests/clutter/interactive/test-events.c0000664000175000017500000003726514723361714022334 0ustar fabiofabio#include #include #include gboolean IsMotion = TRUE; int test_events_main (int argc, char *argv[]); const char * test_events_describe (void); static const gchar * get_event_type_name (const ClutterEvent *event) { switch (event->type) { case CLUTTER_BUTTON_PRESS: return "BUTTON PRESS"; case CLUTTER_BUTTON_RELEASE: return "BUTTON_RELEASE"; case CLUTTER_KEY_PRESS: return "KEY PRESS"; case CLUTTER_KEY_RELEASE: return "KEY RELEASE"; case CLUTTER_ENTER: return "ENTER"; case CLUTTER_LEAVE: return "LEAVE"; case CLUTTER_MOTION: return "MOTION"; case CLUTTER_DELETE: return "DELETE"; case CLUTTER_TOUCH_BEGIN: return "TOUCH BEGIN"; case CLUTTER_TOUCH_UPDATE: return "TOUCH UPDATE"; case CLUTTER_TOUCH_END: return "TOUCH END"; case CLUTTER_TOUCH_CANCEL: return "TOUCH CANCEL"; default: return "EVENT"; } } static gchar * get_event_state_string (const ClutterEvent *event) { const char *mods[18]; int i = 0; ClutterModifierType state = clutter_event_get_state (event); if (state & CLUTTER_SHIFT_MASK) mods[i++] = "shift"; if (state & CLUTTER_LOCK_MASK) mods[i++] = "lock"; if (state & CLUTTER_CONTROL_MASK) mods[i++] = "ctrl"; if (state & CLUTTER_MOD1_MASK) mods[i++] = "mod1"; if (state & CLUTTER_MOD2_MASK) mods[i++] = "mod2"; if (state & CLUTTER_MOD3_MASK) mods[i++] = "mod3"; if (state & CLUTTER_MOD4_MASK) mods[i++] = "mod4"; if (state & CLUTTER_MOD5_MASK) mods[i++] = "mod5"; if (state & CLUTTER_BUTTON1_MASK) mods[i++] = "btn1"; if (state & CLUTTER_BUTTON2_MASK) mods[i++] = "btn2"; if (state & CLUTTER_BUTTON3_MASK) mods[i++] = "btn3"; if (state & CLUTTER_BUTTON4_MASK) mods[i++] = "btn4"; if (state & CLUTTER_BUTTON5_MASK) mods[i++] = "btn5"; if (state & CLUTTER_SUPER_MASK) mods[i++] = "super"; if (state & CLUTTER_HYPER_MASK) mods[i++] = "hyper"; if (state & CLUTTER_META_MASK) mods[i++] = "meta"; if (state & CLUTTER_RELEASE_MASK) mods[i++] = "release"; if (i == 0) mods[i++] = "-"; mods[i] = NULL; return g_strjoinv (",", (char **) mods); } static void stage_state_cb (ClutterStage *stage, gpointer data) { gchar *detail = (gchar*)data; printf("[stage signal] %s\n", detail); } static gboolean red_button_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterActor *stage; if (IsMotion) IsMotion = FALSE; else IsMotion = TRUE; stage = clutter_actor_get_stage (actor); clutter_stage_set_motion_events_enabled (CLUTTER_STAGE (stage), IsMotion); g_print ("*** Per actor motion events %s ***\n", IsMotion ? "enabled" : "disabled"); return FALSE; } static gboolean capture_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { g_print ("* captured event '%s' for type '%s' *\n", get_event_type_name (event), G_OBJECT_TYPE_NAME (actor)); return FALSE; } static void key_focus_in_cb (ClutterActor *actor, gpointer data) { ClutterActor *focus_box = CLUTTER_ACTOR (data); if (CLUTTER_IS_STAGE (actor)) clutter_actor_hide (focus_box); else { clutter_actor_set_position (focus_box, clutter_actor_get_x (actor) - 5, clutter_actor_get_y (actor) - 5); clutter_actor_set_size (focus_box, clutter_actor_get_width (actor) + 10, clutter_actor_get_height (actor) + 10); clutter_actor_show (focus_box); } } static void fill_keybuf (char *keybuf, ClutterKeyEvent *event) { char utf8[6]; int len; /* printable character, if any (ß, ∑) */ len = g_unichar_to_utf8 (event->unicode_value, utf8); utf8[len] = '\0'; sprintf (keybuf, "'%s' ", utf8); /* key combination (s, S, Delete) */ len = g_unichar_to_utf8 (clutter_keysym_to_unicode (event->keyval), utf8); utf8[len] = '\0'; if (event->modifier_state & CLUTTER_SHIFT_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_LOCK_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_CONTROL_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD1_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD2_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD3_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD4_MASK) strcat (keybuf, ""); if (event->modifier_state & CLUTTER_MOD5_MASK) strcat (keybuf, ""); strcat (keybuf, utf8); } static gboolean input_cb (ClutterActor *actor, ClutterEvent *event, gpointer data) { ClutterActor *stage = clutter_actor_get_stage (actor); ClutterActor *source_actor = clutter_event_get_source (event); graphene_point_t position; gchar *state; gchar keybuf[128]; gint device_id; gint source_device_id = 0; device_id = clutter_event_get_device_id (event); if (clutter_event_get_source_device (event) != NULL) source_device_id = clutter_input_device_get_device_id (clutter_event_get_source_device (event)); state = get_event_state_string (event); switch (event->type) { case CLUTTER_KEY_PRESS: fill_keybuf (keybuf, &event->key); printf ("[%s] KEY PRESS %s", clutter_actor_get_name (source_actor), keybuf); break; case CLUTTER_KEY_RELEASE: fill_keybuf (keybuf, &event->key); printf ("[%s] KEY RELEASE %s", clutter_actor_get_name (source_actor), keybuf); break; case CLUTTER_MOTION: clutter_event_get_position (event, &position); g_print ("[%s] MOTION (coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_ENTER: g_print ("[%s] ENTER (from:%s device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_related (event) != NULL ? clutter_actor_get_name (clutter_event_get_related (event)) : "", device_id, source_device_id, state); break; case CLUTTER_LEAVE: g_print ("[%s] LEAVE (to:%s device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_related (event) != NULL ? clutter_actor_get_name (clutter_event_get_related (event)) : "", device_id, source_device_id, state); break; case CLUTTER_BUTTON_PRESS: clutter_event_get_position (event, &position); g_print ("[%s] BUTTON PRESS (button:%i, click count:%i coords:%.02f,%.02f device:%d/%d, state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_button (event), clutter_event_get_click_count (event), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_BUTTON_RELEASE: clutter_event_get_position (event, &position); g_print ("[%s] BUTTON RELEASE (button:%i, click count:%i coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_button (event), clutter_event_get_click_count (event), position.x, position.y, device_id, source_device_id, state); if (source_actor == stage) clutter_stage_set_key_focus (CLUTTER_STAGE (stage), NULL); else if (source_actor == actor && clutter_actor_get_parent (actor) == stage) clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); break; case CLUTTER_TOUCH_BEGIN: clutter_event_get_position (event, &position); g_print ("[%s] TOUCH BEGIN (seq:%p coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_event_sequence (event), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_TOUCH_UPDATE: clutter_event_get_position (event, &position); g_print ("[%s] TOUCH UPDATE (seq:%p coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_event_sequence (event), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_TOUCH_END: clutter_event_get_position (event, &position); g_print ("[%s] TOUCH END (seq:%p coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_event_sequence (event), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_TOUCH_CANCEL: clutter_event_get_position (event, &position); g_print ("[%s] TOUCH CANCEL (seq:%p coords:%.02f,%.02f device:%d/%d state:%s)", clutter_actor_get_name (source_actor), clutter_event_get_event_sequence (event), position.x, position.y, device_id, source_device_id, state); break; case CLUTTER_SCROLL: { ClutterScrollDirection dir = clutter_event_get_scroll_direction (event); if (dir == CLUTTER_SCROLL_SMOOTH) { gdouble dx, dy; clutter_event_get_scroll_delta (event, &dx, &dy); g_print ("[%s] BUTTON SCROLL (direction:smooth %.02f,%.02f state:%s)", clutter_actor_get_name (source_actor), dx, dy, state); } else g_print ("[%s] BUTTON SCROLL (direction:%s state:%s)", clutter_actor_get_name (source_actor), dir == CLUTTER_SCROLL_UP ? "up" : dir == CLUTTER_SCROLL_DOWN ? "down" : dir == CLUTTER_SCROLL_LEFT ? "left" : dir == CLUTTER_SCROLL_RIGHT ? "right" : "?", state); } break; case CLUTTER_STAGE_STATE: g_print ("[%s] STAGE STATE", clutter_actor_get_name (source_actor)); break; case CLUTTER_DESTROY_NOTIFY: g_print ("[%s] DESTROY NOTIFY", clutter_actor_get_name (source_actor)); break; case CLUTTER_CLIENT_MESSAGE: g_print ("[%s] CLIENT MESSAGE", clutter_actor_get_name (source_actor)); break; case CLUTTER_DELETE: g_print ("[%s] DELETE", clutter_actor_get_name (source_actor)); break; case CLUTTER_TOUCHPAD_PINCH: g_print ("[%s] TOUCHPAD PINCH", clutter_actor_get_name (source_actor)); break; case CLUTTER_TOUCHPAD_SWIPE: g_print ("[%s] TOUCHPAD SWIPE", clutter_actor_get_name (source_actor)); break; case CLUTTER_PROXIMITY_IN: g_print ("[%s] PROXIMITY IN", clutter_actor_get_name (source_actor)); break; case CLUTTER_PROXIMITY_OUT: g_print ("[%s] PROXIMITY OUT", clutter_actor_get_name (source_actor)); break; case CLUTTER_PAD_BUTTON_PRESS: g_print ("[%s] PAD BUTTON PRESS", clutter_actor_get_name (source_actor)); break; case CLUTTER_PAD_BUTTON_RELEASE: g_print ("[%s] PAD BUTTON RELEASE", clutter_actor_get_name (source_actor)); break; case CLUTTER_PAD_STRIP: g_print ("[%s] PAD STRIP", clutter_actor_get_name (source_actor)); break; case CLUTTER_PAD_RING: g_print ("[%s] PAD RING", clutter_actor_get_name (source_actor)); break; case CLUTTER_NOTHING: default: return FALSE; } g_free (state); if (source_actor == actor) g_print (" *source*"); g_print ("\n"); return FALSE; } G_MODULE_EXPORT int test_events_main (int argc, char *argv[]) { ClutterActor *stage, *actor, *focus_box, *group; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Events"); clutter_actor_set_name (stage, "Stage"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); g_signal_connect (stage, "event", G_CALLBACK (input_cb), (char *) "stage"); g_signal_connect (stage, "activate", G_CALLBACK (stage_state_cb), (char *) "activate"); g_signal_connect (stage, "deactivate", G_CALLBACK (stage_state_cb), (char *) "deactivate"); focus_box = clutter_rectangle_new_with_color (CLUTTER_COLOR_Black); clutter_actor_set_name (focus_box, "Focus Box"); clutter_container_add (CLUTTER_CONTAINER(stage), focus_box, NULL); actor = clutter_rectangle_new_with_color (CLUTTER_COLOR_Red); clutter_actor_set_name (actor, "Red Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), (char *) "red box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* Toggle motion - enter/leave capture */ g_signal_connect (actor, "button-press-event", G_CALLBACK (red_button_cb), NULL); clutter_stage_set_key_focus (CLUTTER_STAGE (stage), actor); actor = clutter_rectangle_new_with_color (CLUTTER_COLOR_Green); clutter_actor_set_name (actor, "Green Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, 250, 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add (CLUTTER_CONTAINER (stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), (char *) "green box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); g_signal_connect (actor, "captured-event", G_CALLBACK (capture_cb), NULL); /* non reactive */ actor = clutter_rectangle_new_with_color (CLUTTER_COLOR_Black); clutter_actor_set_name (actor, "Black Box"); clutter_actor_set_size (actor, 400, 50); clutter_actor_set_position (actor, 100, 250); clutter_container_add (CLUTTER_CONTAINER(stage), actor, NULL); g_signal_connect (actor, "event", G_CALLBACK (input_cb), (char *) "blue box"); g_signal_connect (actor, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); g_signal_connect (stage, "key-focus-in", G_CALLBACK (key_focus_in_cb), focus_box); /* non reactive group, with reactive child */ actor = clutter_rectangle_new_with_color (CLUTTER_COLOR_Yellow); clutter_actor_set_name (actor, "Yellow Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_reactive (actor, TRUE); g_signal_connect (actor, "event", G_CALLBACK (input_cb), (char *) "yellow box"); /* note group not reactive */ group = clutter_group_new (); clutter_container_add (CLUTTER_CONTAINER (group), actor, NULL); clutter_container_add (CLUTTER_CONTAINER (stage), group, NULL); clutter_actor_set_position (group, 100, 350); clutter_actor_show_all (group); /* border actor */ actor = clutter_rectangle_new_with_color (CLUTTER_COLOR_Magenta); clutter_actor_set_name (actor, "Border Box"); clutter_actor_set_size (actor, 100, 100); clutter_actor_set_position (actor, (clutter_actor_get_width (stage) - 100) / 2, clutter_actor_get_height (stage) - 100); clutter_actor_set_reactive (actor, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), actor); g_signal_connect (actor, "event", G_CALLBACK (input_cb), NULL); clutter_actor_show_all (CLUTTER_ACTOR (stage)); clutter_main(); return 0; } G_MODULE_EXPORT const char * test_events_describe (void) { return "Event handling and propagation."; } muffin-6.4.1/src/tests/clutter/test-utils.h0000664000175000017500000000211314723361714017640 0ustar fabiofabio#include #include static inline ClutterActor * clutter_test_utils_create_texture_from_file (const char *filename, GError **error) { g_autoptr (ClutterContent) image = NULL; g_autoptr (GdkPixbuf) pixbuf = NULL; pixbuf = gdk_pixbuf_new_from_file (filename, error); if (!pixbuf) return NULL; image = clutter_image_new (); if (!clutter_image_set_data (CLUTTER_IMAGE (image), gdk_pixbuf_get_pixels (pixbuf), gdk_pixbuf_get_has_alpha (pixbuf) ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, gdk_pixbuf_get_width (pixbuf), gdk_pixbuf_get_height (pixbuf), gdk_pixbuf_get_rowstride (pixbuf), error)) return NULL; return g_object_new (CLUTTER_TYPE_ACTOR, "content", image, NULL); } muffin-6.4.1/src/tests/clutter/conform/0000775000175000017500000000000014723361714017020 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/conform/text-cache.c0000664000175000017500000002242014723361714021211 0ustar fabiofabio#include #include #include #include "test-conform-common.h" #define TEST_FONT "Sans 10" static const char long_text[] = "This is some REALLY " "long text that contains markup for testing the use_markup " "property and to test word-wrapping, justification and alignment."; typedef struct _CallbackData CallbackData; struct _CallbackData { ClutterActor *stage; ClutterActor *label; PangoLayout *old_layout; gboolean layout_changed; PangoRectangle label_extents; PangoLayout *test_layout; gboolean test_failed; }; static void on_paint (ClutterActor *stage, ClutterPaintContext *paint_context, CallbackData *data) { PangoLayout *new_layout; /* Check whether the layout used for this paint is different from the layout used for the last paint */ new_layout = clutter_text_get_layout (CLUTTER_TEXT (data->label)); data->layout_changed = data->old_layout != new_layout; if (data->old_layout) g_object_unref (data->old_layout); /* Keep a reference to the old layout so we can be sure it won't just reallocate a new layout with the same address */ data->old_layout = g_object_ref (new_layout); pango_layout_get_extents (new_layout, NULL, &data->label_extents); } static void force_redraw (CallbackData *data) { /* XXX - this is fugly; we force a paint on the stage, which * will then paint the Text actor. inside the Text actor we * check for a Layout with the allocation size. if the allocation * has changed it will cause a relayout in the middle of the * paint, which is expensive and broken. this will ensure that * the test passes, though */ clutter_actor_paint (clutter_actor_get_stage (data->label)); } static gboolean check_result (CallbackData *data, const char *note, gboolean layout_should_change) { PangoRectangle test_extents; gboolean fail = FALSE; if (g_test_verbose ()) g_print ("%s: ", note); /* Force a redraw to get the on_paint handler to run */ force_redraw (data); /* Compare the extents from the label with the extents from our test layout */ pango_layout_get_extents (data->test_layout, NULL, &test_extents); if (memcmp (&test_extents, &data->label_extents, sizeof (PangoRectangle))) { if (g_test_verbose ()) g_print ("extents are different: expected: %d, %d, %d, %d " "-> text: %d, %d, %d, %d\n", test_extents.x / 1024, test_extents.y / 1024, test_extents.width / 1024, test_extents.height / 1024, data->label_extents.x / 1024, data->label_extents.y / 1024, data->label_extents.width / 1024, data->label_extents.height / 1024); fail = TRUE; } else { if (g_test_verbose ()) g_print ("extents are the same, "); } if (data->layout_changed) { if (g_test_verbose ()) g_print ("layout changed, "); } else { if (g_test_verbose ()) g_print ("layout did not change, "); } if (data->layout_changed != layout_should_change) fail = TRUE; if (fail) { if (g_test_verbose ()) g_print ("FAIL\n"); data->test_failed = TRUE; } else { if (g_test_verbose ()) g_print ("pass\n"); } return fail; } static gboolean do_tests (CallbackData *data) { PangoFontDescription *fd; static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff }; PangoAttrList *attr_list, *attr_list_copy; PangoAttribute *attr; /* TEST 1: change the text */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 0"); pango_layout_set_text (data->test_layout, "Counter 0", -1); g_assert (check_result (data, "Change text", TRUE) == FALSE); /* TEST 2: change a single character */ clutter_text_set_text (CLUTTER_TEXT (data->label), "Counter 1"); pango_layout_set_text (data->test_layout, "Counter 1", -1); g_assert (check_result (data, "Change a single character", TRUE) == FALSE); /* TEST 3: move the label */ clutter_actor_set_position (data->label, 10, 0); g_assert (check_result (data, "Move the label", FALSE) == FALSE); /* TEST 4: change the font */ clutter_text_set_font_name (CLUTTER_TEXT (data->label), "Serif 15"); fd = pango_font_description_from_string ("Serif 15"); pango_layout_set_font_description (data->test_layout, fd); pango_font_description_free (fd); g_assert (check_result (data, "Change the font", TRUE) == FALSE); /* TEST 5: change the color */ clutter_text_set_color (CLUTTER_TEXT (data->label), &red); g_assert (check_result (data, "Change the color", FALSE) == FALSE); /* TEST 6: change the attributes */ attr = pango_attr_weight_new (PANGO_WEIGHT_BOLD); attr->start_index = 0; attr->end_index = 2; attr_list = pango_attr_list_new (); pango_attr_list_insert (attr_list, attr); attr_list_copy = pango_attr_list_copy (attr_list); clutter_text_set_attributes (CLUTTER_TEXT (data->label), attr_list); pango_layout_set_attributes (data->test_layout, attr_list_copy); pango_attr_list_unref (attr_list_copy); pango_attr_list_unref (attr_list); g_assert (check_result (data, "Change the attributes", TRUE) == FALSE); /* TEST 7: change the text again */ clutter_text_set_attributes (CLUTTER_TEXT (data->label), NULL); clutter_text_set_text (CLUTTER_TEXT (data->label), long_text); pango_layout_set_attributes (data->test_layout, NULL); pango_layout_set_text (data->test_layout, long_text, -1); g_assert (check_result (data, "Change the text again", TRUE) == FALSE); /* TEST 8: enable markup */ clutter_text_set_use_markup (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_markup (data->test_layout, long_text, -1); g_assert (check_result (data, "Enable markup", TRUE) == FALSE); /* This part can't be a test because Clutter won't restrict the width if wrapping and ellipsizing is disabled so the extents will be different, but we still want to do it for the later tests */ clutter_actor_set_width (data->label, 200); pango_layout_set_width (data->test_layout, 200 * PANGO_SCALE); /* Force a redraw so that changing the width won't affect the results */ force_redraw (data); /* TEST 9: enable ellipsize */ clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_END); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_END); g_assert (check_result (data, "Enable ellipsize", TRUE) == FALSE); clutter_text_set_ellipsize (CLUTTER_TEXT (data->label), PANGO_ELLIPSIZE_NONE); pango_layout_set_ellipsize (data->test_layout, PANGO_ELLIPSIZE_NONE); force_redraw (data); /* TEST 10: enable line wrap */ clutter_text_set_line_wrap (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_WORD); g_assert (check_result (data, "Enable line wrap", TRUE) == FALSE); /* TEST 11: change wrap mode * FIXME - broken */ clutter_text_set_line_wrap_mode (CLUTTER_TEXT (data->label), PANGO_WRAP_CHAR); pango_layout_set_wrap (data->test_layout, PANGO_WRAP_CHAR); g_assert (check_result (data, "Change wrap mode", TRUE) == FALSE); /* TEST 12: enable justify */ clutter_text_set_justify (CLUTTER_TEXT (data->label), TRUE); pango_layout_set_justify (data->test_layout, TRUE); /* Pango appears to have a bug which means that you can't change the justification after setting the text but this fixes it. See http://bugzilla.gnome.org/show_bug.cgi?id=551865 */ pango_layout_context_changed (data->test_layout); g_assert (check_result (data, "Enable justify", TRUE) == FALSE); /* TEST 13: change alignment */ clutter_text_set_line_alignment (CLUTTER_TEXT (data->label), PANGO_ALIGN_RIGHT); pango_layout_set_alignment (data->test_layout, PANGO_ALIGN_RIGHT); g_assert (check_result (data, "Change alignment", TRUE) == FALSE); clutter_main_quit (); return FALSE; } static PangoLayout * make_layout_like_label (ClutterText *label) { PangoLayout *label_layout, *new_layout; PangoContext *context; PangoFontDescription *fd; /* Make another layout using the same context as the layout from the label */ label_layout = clutter_text_get_layout (label); context = pango_layout_get_context (label_layout); new_layout = pango_layout_new (context); fd = pango_font_description_from_string (TEST_FONT); pango_layout_set_font_description (new_layout, fd); pango_font_description_free (fd); return new_layout; } void text_cache (void) { CallbackData data; memset (&data, 0, sizeof (data)); data.stage = clutter_stage_new (); data.label = clutter_text_new_with_text (TEST_FONT, ""); data.test_layout = make_layout_like_label (CLUTTER_TEXT (data.label)); g_signal_connect (data.stage, "paint", G_CALLBACK (on_paint), &data); clutter_container_add (CLUTTER_CONTAINER (data.stage), data.label, NULL); clutter_actor_show (data.stage); clutter_threads_add_idle ((GSourceFunc) do_tests, &data); clutter_main (); clutter_actor_destroy (data.stage); if (g_test_verbose ()) g_print ("\nOverall result: "); if (g_test_verbose ()) { if (data.test_failed) g_print ("FAIL\n"); else g_print ("pass\n"); } else g_assert (data.test_failed != TRUE); } muffin-6.4.1/src/tests/clutter/conform/timeline-interpolate.c0000664000175000017500000000774214723361714023330 0ustar fabiofabio#include #include #include #include "test-conform-common.h" /* We ask for 1 frame per millisecond. * Whenever this rate can't be achieved then the timeline * will interpolate the number frames that should have * passed between timeouts. */ #define TEST_TIMELINE_FPS 1000 #define TEST_TIMELINE_DURATION 5000 /* We are at the mercy of the system scheduler so this * may not be a very reliable tolerance. */ #define TEST_ERROR_TOLERANCE 20 typedef struct _TestState { ClutterTimeline *timeline; int64_t start_time; guint new_frame_counter; gint expected_frame; gint completion_count; gboolean passed; } TestState; static void new_frame_cb (ClutterTimeline *timeline, gint frame_num, TestState *state) { int64_t current_time; gint current_frame; glong msec_diff; gint loop_overflow = 0; static gint step = 1; current_time = g_get_real_time (); current_frame = clutter_timeline_get_elapsed_time (state->timeline); msec_diff = (current_time - state->start_time) / G_TIME_SPAN_MILLISECOND; /* If we expect to have interpolated past the end of the timeline * we keep track of the overflow so we can determine when * the next timeout will happen. We then clip expected_frames * to TEST_TIMELINE_DURATION since clutter-timeline * semantics guaranty this frame is always signaled before * looping */ if (state->expected_frame > TEST_TIMELINE_DURATION) { loop_overflow = state->expected_frame - TEST_TIMELINE_DURATION; state->expected_frame = TEST_TIMELINE_DURATION; } if (current_frame >= (state->expected_frame-TEST_ERROR_TOLERANCE) && current_frame <= (state->expected_frame+TEST_ERROR_TOLERANCE)) { g_test_message ("elapsed milliseconds=%-5li " "expected frame=%-4i actual frame=%-4i (OK)", msec_diff, state->expected_frame, current_frame); } else { g_test_message ("elapsed milliseconds=%-5li " "expected frame=%-4i actual frame=%-4i (FAILED)", msec_diff, state->expected_frame, current_frame); state->passed = FALSE; } if (step>0) { state->expected_frame = current_frame + (TEST_TIMELINE_FPS / 4); g_test_message ("Sleeping for 250ms " "so next frame should be (%i + %i) = %i", current_frame, (TEST_TIMELINE_FPS / 4), state->expected_frame); g_usleep (250000); } else { state->expected_frame = current_frame + TEST_TIMELINE_FPS; g_test_message ("Sleeping for 1sec " "so next frame should be (%i + %i) = %i", current_frame, TEST_TIMELINE_FPS, state->expected_frame); g_usleep (1000000); } if (current_frame >= TEST_TIMELINE_DURATION) { state->expected_frame += loop_overflow; state->expected_frame -= TEST_TIMELINE_DURATION; g_test_message ("End of timeline reached: " "Wrapping expected frame too %i", state->expected_frame); } state->new_frame_counter++; step = -step; } static void completed_cb (ClutterTimeline *timeline, TestState *state) { state->completion_count++; if (state->completion_count == 2) { if (state->passed) { g_test_message ("Passed\n"); clutter_main_quit (); } else { g_test_message ("Failed\n"); exit (EXIT_FAILURE); } } } void timeline_interpolation (void) { TestState state; state.timeline = clutter_timeline_new (TEST_TIMELINE_DURATION); clutter_timeline_set_loop (state.timeline, TRUE); g_signal_connect (G_OBJECT(state.timeline), "new-frame", G_CALLBACK(new_frame_cb), &state); g_signal_connect (G_OBJECT(state.timeline), "completed", G_CALLBACK(completed_cb), &state); state.completion_count = 0; state.new_frame_counter = 0; state.passed = TRUE; state.expected_frame = 0; state.start_time = g_get_real_time (); clutter_timeline_start (state.timeline); clutter_main(); g_object_unref (state.timeline); } muffin-6.4.1/src/tests/clutter/conform/timeline-progress.c0000664000175000017500000000752614723361714022646 0ustar fabiofabio#include #include #include "test-conform-common.h" void timeline_progress_step (TestConformSimpleFixture *fixture G_GNUC_UNUSED, gconstpointer dummy G_GNUC_UNUSED) { ClutterTimeline *timeline; timeline = clutter_timeline_new (1000); if (g_test_verbose ()) g_print ("mode: step(3, end)\n"); clutter_timeline_rewind (timeline); clutter_timeline_set_step_progress (timeline, 3, CLUTTER_STEP_MODE_END); g_assert_cmpint (clutter_timeline_get_progress (timeline), ==, 0); clutter_timeline_advance (timeline, 1000 / 3 - 1); g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 0); clutter_timeline_advance (timeline, 1000 / 3 + 1); g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333); clutter_timeline_advance (timeline, 1000 / 3 * 2 - 1); g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 333); clutter_timeline_advance (timeline, 1000 / 3 * 2 + 1); g_assert_cmpint (clutter_timeline_get_progress (timeline) * 1000, ==, 666); clutter_timeline_rewind (timeline); clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 1); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 500); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 999); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 1000); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); if (g_test_verbose ()) g_print ("mode: step-start\n"); clutter_timeline_rewind (timeline); clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_START); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 1); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 500); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 999); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_advance (timeline, 1000); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); if (g_test_verbose ()) g_print ("mode: step-end\n"); clutter_timeline_rewind (timeline); clutter_timeline_set_progress_mode (timeline, CLUTTER_STEP_END); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 1); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 500); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 999); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 1000); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); g_object_unref (timeline); } void timeline_progress_mode (TestConformSimpleFixture *fixture G_GNUC_UNUSED, gconstpointer dummy G_GNUC_UNUSED) { ClutterTimeline *timeline; timeline = clutter_timeline_new (1000); g_assert (clutter_timeline_get_progress_mode (timeline) == CLUTTER_LINEAR); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); clutter_timeline_advance (timeline, 500); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.5); clutter_timeline_advance (timeline, 1000); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 1.0); clutter_timeline_rewind (timeline); g_assert_cmpfloat (clutter_timeline_get_progress (timeline), ==, 0.0); g_object_unref (timeline); } muffin-6.4.1/src/tests/clutter/conform/meson.build0000664000175000017500000000351214723361714021163 0ustar fabiofabioclutter_tests_conform_c_args = [ '-DG_LOG_DOMAIN="Clutter-Conform"', '-DCOGL_DISABLE_DEPRECATION_WARNINGS', ] clutter_tests_conform_c_args += clutter_debug_c_args clutter_tests_conform_link_args = [ '-Wl,--export-dynamic', ] clutter_conform_tests_actor_tests = [ 'actor-anchors', 'actor-clone', 'actor-destroy', 'actor-graph', 'actor-invariants', 'actor-iter', 'actor-layout', 'actor-meta', 'actor-offscreen-redirect', 'actor-paint-opacity', 'actor-pick', 'actor-shader-effect', 'actor-size', ] clutter_conform_tests_classes_tests = [ 'text', ] clutter_conform_tests_general_tests = [ 'binding-pool', 'color', 'interval', 'script-parser', 'units', ] clutter_conform_tests_deprecated_tests = [ 'group', 'rectangle', ] clutter_conform_tests = [] clutter_conform_tests += clutter_conform_tests_actor_tests clutter_conform_tests += clutter_conform_tests_classes_tests clutter_conform_tests += clutter_conform_tests_general_tests clutter_conform_tests += clutter_conform_tests_deprecated_tests test_env = environment() test_env.set('G_TEST_SRCDIR', meson.current_source_dir()) test_env.set('G_TEST_BUILDDIR', meson.current_build_dir()) test_env.set('G_ENABLE_DIAGNOSTIC', '0') test_env.set('CLUTTER_ENABLE_DIAGNOSTIC', '0') test_env.set('CLUTTER_SCALE', '1') foreach test : clutter_conform_tests test_executable = executable('@0@'.format(test), sources: [ '@0@.c'.format(test), clutter_test_utils, ], include_directories: clutter_includes, c_args: clutter_tests_conform_c_args, link_args: clutter_tests_conform_link_args, dependencies: [ clutter_deps, libmutter_clutter_dep, libmutter_cogl_path_dep, libmutter_dep ], install: false, ) test(test, test_executable, suite: ['clutter', 'clutter/conform'], env: test_env ) endforeach muffin-6.4.1/src/tests/clutter/conform/actor-size.c0000664000175000017500000001375614723361714021260 0ustar fabiofabio#include #include #include #include "tests/clutter-test-utils.h" #define TEST_TYPE_ACTOR (test_actor_get_type ()) typedef struct _TestActor TestActor; typedef struct _ClutterActorClass TestActorClass; struct _TestActor { ClutterActor parent_instance; guint preferred_width_called : 1; guint preferred_height_called : 1; }; GType test_actor_get_type (void); G_DEFINE_TYPE (TestActor, test_actor, CLUTTER_TYPE_ACTOR); static void test_actor_get_preferred_width (ClutterActor *self, gfloat for_height, gfloat *min_width_p, gfloat *nat_width_p) { TestActor *test = (TestActor *) self; test->preferred_width_called = TRUE; if (for_height == 10) { *min_width_p = 10; *nat_width_p = 100; } else { *min_width_p = 100; *nat_width_p = 100; } } static void test_actor_get_preferred_height (ClutterActor *self, gfloat for_width, gfloat *min_height_p, gfloat *nat_height_p) { TestActor *test = (TestActor *) self; test->preferred_height_called = TRUE; if (for_width == 10) { *min_height_p = 50; *nat_height_p = 100; } else { *min_height_p = 100; *nat_height_p = 100; } } static void test_actor_class_init (TestActorClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->get_preferred_width = test_actor_get_preferred_width; actor_class->get_preferred_height = test_actor_get_preferred_height; } static void test_actor_init (TestActor *self) { } static void actor_preferred_size (void) { ClutterActor *test; TestActor *self; gfloat min_width, min_height; gfloat nat_width, nat_height; test = g_object_new (TEST_TYPE_ACTOR, NULL); self = (TestActor *) test; if (g_test_verbose ()) g_print ("Preferred size\n"); clutter_actor_get_preferred_size (test, &min_width, &min_height, &nat_width, &nat_height); g_assert (self->preferred_width_called); g_assert (self->preferred_height_called); g_assert_cmpfloat (min_width, ==, 100); g_assert_cmpfloat (min_height, ==, 100); g_assert_cmpfloat (nat_width, ==, min_width); g_assert_cmpfloat (nat_height, ==, min_height); if (g_test_verbose ()) g_print ("Preferred width\n"); self->preferred_width_called = FALSE; clutter_actor_get_preferred_width (test, 10, &min_width, &nat_width); g_assert (self->preferred_width_called); g_assert_cmpfloat (min_width, ==, 10); g_assert_cmpfloat (nat_width, ==, 100); if (g_test_verbose ()) g_print ("Preferred height\n"); self->preferred_height_called = FALSE; clutter_actor_get_preferred_height (test, 200, &min_height, &nat_height); g_assert (self->preferred_height_called); g_assert_cmpfloat (min_height, !=, 10); g_assert_cmpfloat (nat_height, ==, 100); if (g_test_verbose ()) g_print ("Preferred width (cached)\n"); self->preferred_width_called = FALSE; clutter_actor_get_preferred_width (test, 10, &min_width, &nat_width); g_assert (!self->preferred_width_called); g_assert_cmpfloat (min_width, ==, 10); g_assert_cmpfloat (nat_width, ==, 100); if (g_test_verbose ()) g_print ("Preferred height (cache eviction)\n"); self->preferred_height_called = FALSE; clutter_actor_get_preferred_height (test, 10, &min_height, &nat_height); g_assert (self->preferred_height_called); g_assert_cmpfloat (min_height, ==, 50); g_assert_cmpfloat (nat_height, ==, 100); clutter_actor_destroy (test); } static void actor_fixed_size (void) { ClutterActor *rect; gboolean min_width_set, nat_width_set; gboolean min_height_set, nat_height_set; gfloat min_width, min_height; gfloat nat_width, nat_height; rect = clutter_actor_new (); g_object_ref_sink (rect); if (g_test_verbose ()) g_print ("Initial size is 0\n"); g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 0); g_assert_cmpfloat (clutter_actor_get_height (rect), ==, 0); clutter_actor_set_size (rect, 100, 100); if (g_test_verbose ()) g_print ("Explicit size set\n"); g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 100); g_assert_cmpfloat (clutter_actor_get_height (rect), ==, 100); g_object_get (G_OBJECT (rect), "min-width-set", &min_width_set, "min-height-set", &min_height_set, "natural-width-set", &nat_width_set, "natural-height-set", &nat_height_set, NULL); if (g_test_verbose ()) g_print ("Notification properties\n"); g_assert (min_width_set && nat_width_set); g_assert (min_height_set && nat_height_set); clutter_actor_get_preferred_size (rect, &min_width, &min_height, &nat_width, &nat_height); if (g_test_verbose ()) g_print ("Preferred size\n"); g_assert_cmpfloat (min_width, ==, 100); g_assert_cmpfloat (min_height, ==, 100); g_assert_cmpfloat (min_width, ==, nat_width); g_assert_cmpfloat (min_height, ==, nat_height); clutter_actor_set_size (rect, -1, -1); if (g_test_verbose ()) g_print ("Explicit size unset\n"); g_object_get (G_OBJECT (rect), "min-width-set", &min_width_set, "min-height-set", &min_height_set, "natural-width-set", &nat_width_set, "natural-height-set", &nat_height_set, NULL); g_assert (!min_width_set && !nat_width_set); g_assert (!min_height_set && !nat_height_set); g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 0); g_assert_cmpfloat (clutter_actor_get_height (rect), ==, 0); clutter_actor_destroy (rect); g_object_unref (rect); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/size/preferred", actor_preferred_size) CLUTTER_TEST_UNIT ("/actor/size/fixed", actor_fixed_size) ) muffin-6.4.1/src/tests/clutter/conform/actor-pick.c0000664000175000017500000001527014723361714021225 0ustar fabiofabio#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" #define STAGE_WIDTH 640 #define STAGE_HEIGHT 480 #define ACTORS_X 12 #define ACTORS_Y 16 typedef struct _State State; struct _State { ClutterActor *stage; int y, x; ClutterActor *actors[ACTORS_X * ACTORS_Y]; guint actor_width, actor_height; guint failed_pass; guint failed_idx; gboolean pass; }; static const char *test_passes[] = { "No covering actor", "Invisible covering actor", "Clipped covering actor", "Blur effect", }; static gboolean on_timeout (gpointer data) { State *state = data; int test_num = 0; int y, x; ClutterActor *over_actor = NULL; /* This will cause an unclipped pick redraw that will get buffered. We'll check below that this buffer is discarded because we also need to pick non-reactive actors */ clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_REACTIVE, 10, 10); clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_REACTIVE, 10, 10); for (test_num = 0; test_num < G_N_ELEMENTS (test_passes); test_num++) { if (test_num == 0) { if (g_test_verbose ()) g_print ("No covering actor:\n"); } if (test_num == 1) { static const ClutterColor red = { 0xff, 0x00, 0x00, 0xff }; /* Create an actor that covers the whole stage but that isn't visible so it shouldn't affect the picking */ over_actor = clutter_rectangle_new_with_color (&red); clutter_actor_set_size (over_actor, STAGE_WIDTH, STAGE_HEIGHT); clutter_actor_add_child (state->stage, over_actor); clutter_actor_hide (over_actor); if (g_test_verbose ()) g_print ("Invisible covering actor:\n"); } else if (test_num == 2) { ClutterActorBox over_actor_box = CLUTTER_ACTOR_BOX_INIT (0, 0, STAGE_WIDTH, STAGE_HEIGHT); /* Make the actor visible but set a clip so that only some of the actors are accessible */ clutter_actor_show (over_actor); clutter_actor_set_clip (over_actor, state->actor_width * 2, state->actor_height * 2, state->actor_width * (ACTORS_X - 4), state->actor_height * (ACTORS_Y - 4)); /* Only allocated actors can be picked, so force an allocation * of the overlay actor here. */ clutter_actor_allocate (over_actor, &over_actor_box, 0); if (g_test_verbose ()) g_print ("Clipped covering actor:\n"); } else if (test_num == 3) { if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) continue; clutter_actor_hide (over_actor); clutter_actor_add_effect_with_name (CLUTTER_ACTOR (state->stage), "blur", clutter_blur_effect_new ()); if (g_test_verbose ()) g_print ("With blur effect:\n"); } for (y = 0; y < ACTORS_Y; y++) { x = 0; for (; x < ACTORS_X; x++) { gboolean pass = FALSE; gfloat pick_x; ClutterActor *actor; pick_x = x * state->actor_width + state->actor_width / 2; actor = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (state->stage), CLUTTER_PICK_ALL, pick_x, y * state->actor_height + state->actor_height / 2); if (g_test_verbose ()) g_print ("% 3i,% 3i / %p -> ", x, y, state->actors[y * ACTORS_X + x]); if (actor == NULL) { if (g_test_verbose ()) g_print ("NULL: FAIL\n"); } else if (actor == over_actor) { if (test_num == 2 && x >= 2 && x < ACTORS_X - 2 && y >= 2 && y < ACTORS_Y - 2) pass = TRUE; if (g_test_verbose ()) g_print ("over_actor: %s\n", pass ? "pass" : "FAIL"); } else { if (actor == state->actors[y * ACTORS_X + x] && (test_num != 2 || x < 2 || x >= ACTORS_X - 2 || y < 2 || y >= ACTORS_Y - 2)) pass = TRUE; if (g_test_verbose ()) g_print ("%p: %s\n", actor, pass ? "pass" : "FAIL"); } if (!pass) { state->failed_pass = test_num; state->failed_idx = y * ACTORS_X + x; state->pass = FALSE; } } } } clutter_main_quit (); return G_SOURCE_REMOVE; } static void actor_pick (void) { int y, x; State state; state.pass = TRUE; state.stage = clutter_test_get_stage (); state.actor_width = STAGE_WIDTH / ACTORS_X; state.actor_height = STAGE_HEIGHT / ACTORS_Y; for (y = 0; y < ACTORS_Y; y++) for (x = 0; x < ACTORS_X; x++) { ClutterColor color = { x * 255 / (ACTORS_X - 1), y * 255 / (ACTORS_Y - 1), 128, 255 }; ClutterActor *rect = clutter_rectangle_new_with_color (&color); clutter_actor_set_position (rect, x * state.actor_width, y * state.actor_height); clutter_actor_set_size (rect, state.actor_width, state.actor_height); clutter_actor_add_child (state.stage, rect); state.actors[y * ACTORS_X + x] = rect; } clutter_actor_show (state.stage); clutter_threads_add_idle (on_timeout, &state); clutter_main (); if (g_test_verbose ()) { if (!state.pass) g_test_message ("Failed pass: %s[%d], actor index: %d [%p]\n", test_passes[state.failed_pass], state.failed_pass, state.failed_idx, state.actors[state.failed_idx]); } g_assert (state.pass); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/pick", actor_pick) ) muffin-6.4.1/src/tests/clutter/conform/path.c0000664000175000017500000004362114723361714020126 0ustar fabiofabio#include #include #include #include #include "test-conform-common.h" #define MAX_NODES 128 #define FLOAT_FUZZ_AMOUNT 5.0f typedef struct _CallbackData CallbackData; typedef gboolean (* PathTestFunc) (CallbackData *data); static void compare_node (const ClutterPathNode *node, gpointer data_p); struct _CallbackData { ClutterPath *path; guint n_nodes; ClutterPathNode nodes[MAX_NODES]; gboolean nodes_different; guint nodes_found; }; static const char path_desc[] = "M 21 22 " "L 25 26 " "C 29 30 31 32 33 34 " "m 23 24 " "l 27 28 " "c 35 36 37 38 39 40 " "z"; static const ClutterPathNode path_nodes[] = { { CLUTTER_PATH_MOVE_TO, { { 21, 22 }, { 0, 0 }, { 0, 0 } } }, { CLUTTER_PATH_LINE_TO, { { 25, 26 }, { 0, 0 }, { 0, 0 } } }, { CLUTTER_PATH_CURVE_TO, { { 29, 30 }, { 31, 32 }, { 33, 34 } } }, { CLUTTER_PATH_REL_MOVE_TO, { { 23, 24 }, { 0, 0 }, { 0, 0 } } }, { CLUTTER_PATH_REL_LINE_TO, { { 27, 28 }, { 0, 0 }, { 0, 0 } } }, { CLUTTER_PATH_REL_CURVE_TO, { { 35, 36 }, { 37, 38 }, { 39, 40 } } }, { CLUTTER_PATH_CLOSE, { { 0, 0 }, { 0, 0 }, { 0, 0 } } } }; static gboolean path_test_add_move_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_MOVE_TO; node.points[0].x = 1; node.points[0].y = 2; clutter_path_add_move_to (data->path, node.points[0].x, node.points[0].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_line_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_LINE_TO; node.points[0].x = 3; node.points[0].y = 4; clutter_path_add_line_to (data->path, node.points[0].x, node.points[0].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_curve_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_CURVE_TO; node.points[0].x = 5; node.points[0].y = 6; node.points[1].x = 7; node.points[1].y = 8; node.points[2].x = 9; node.points[2].y = 10; clutter_path_add_curve_to (data->path, node.points[0].x, node.points[0].y, node.points[1].x, node.points[1].y, node.points[2].x, node.points[2].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_close (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_CLOSE; clutter_path_add_close (data->path); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_rel_move_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_REL_MOVE_TO; node.points[0].x = 11; node.points[0].y = 12; clutter_path_add_rel_move_to (data->path, node.points[0].x, node.points[0].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_rel_line_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_REL_LINE_TO; node.points[0].x = 13; node.points[0].y = 14; clutter_path_add_rel_line_to (data->path, node.points[0].x, node.points[0].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_rel_curve_to (CallbackData *data) { ClutterPathNode node = { 0, }; node.type = CLUTTER_PATH_REL_CURVE_TO; node.points[0].x = 15; node.points[0].y = 16; node.points[1].x = 17; node.points[1].y = 18; node.points[2].x = 19; node.points[2].y = 20; clutter_path_add_rel_curve_to (data->path, node.points[0].x, node.points[0].y, node.points[1].x, node.points[1].y, node.points[2].x, node.points[2].y); data->nodes[data->n_nodes++] = node; return TRUE; } static gboolean path_test_add_string (CallbackData *data) { int i; for (i = 0; i < G_N_ELEMENTS (path_nodes); i++) data->nodes[data->n_nodes++] = path_nodes[i]; clutter_path_add_string (data->path, path_desc); return TRUE; } static gboolean path_test_add_node_by_struct (CallbackData *data) { int i; for (i = 0; i < G_N_ELEMENTS (path_nodes); i++) { data->nodes[data->n_nodes++] = path_nodes[i]; clutter_path_add_node (data->path, path_nodes + i); } return TRUE; } static gboolean path_test_get_n_nodes (CallbackData *data) { return clutter_path_get_n_nodes (data->path) == data->n_nodes; } static gboolean path_test_get_node (CallbackData *data) { int i; data->nodes_found = 0; data->nodes_different = FALSE; for (i = 0; i < data->n_nodes; i++) { ClutterPathNode node; clutter_path_get_node (data->path, i, &node); compare_node (&node, data); } return !data->nodes_different; } static gboolean path_test_get_nodes (CallbackData *data) { GSList *list, *node; data->nodes_found = 0; data->nodes_different = FALSE; list = clutter_path_get_nodes (data->path); for (node = list; node; node = node->next) compare_node (node->data, data); g_slist_free (list); return !data->nodes_different && data->nodes_found == data->n_nodes; } static gboolean path_test_insert_beginning (CallbackData *data) { ClutterPathNode node; node.type = CLUTTER_PATH_LINE_TO; node.points[0].x = 41; node.points[0].y = 42; memmove (data->nodes + 1, data->nodes, data->n_nodes++ * sizeof (ClutterPathNode)); data->nodes[0] = node; clutter_path_insert_node (data->path, 0, &node); return TRUE; } static gboolean path_test_insert_end (CallbackData *data) { ClutterPathNode node; node.type = CLUTTER_PATH_LINE_TO; node.points[0].x = 43; node.points[0].y = 44; data->nodes[data->n_nodes++] = node; clutter_path_insert_node (data->path, -1, &node); return TRUE; } static gboolean path_test_insert_middle (CallbackData *data) { ClutterPathNode node; int pos = data->n_nodes / 2; node.type = CLUTTER_PATH_LINE_TO; node.points[0].x = 45; node.points[0].y = 46; memmove (data->nodes + pos + 1, data->nodes + pos, (data->n_nodes - pos) * sizeof (ClutterPathNode)); data->nodes[pos] = node; data->n_nodes++; clutter_path_insert_node (data->path, pos, &node); return TRUE; } static gboolean path_test_clear (CallbackData *data) { clutter_path_clear (data->path); data->n_nodes = 0; return TRUE; } static gboolean path_test_clear_insert (CallbackData *data) { return path_test_clear (data) && path_test_insert_middle (data); } static gboolean path_test_remove_beginning (CallbackData *data) { memmove (data->nodes, data->nodes + 1, --data->n_nodes * sizeof (ClutterPathNode)); clutter_path_remove_node (data->path, 0); return TRUE; } static gboolean path_test_remove_end (CallbackData *data) { clutter_path_remove_node (data->path, --data->n_nodes); return TRUE; } static gboolean path_test_remove_middle (CallbackData *data) { int pos = data->n_nodes / 2; memmove (data->nodes + pos, data->nodes + pos + 1, (--data->n_nodes - pos) * sizeof (ClutterPathNode)); clutter_path_remove_node (data->path, pos); return TRUE; } static gboolean path_test_remove_only (CallbackData *data) { return path_test_clear (data) && path_test_add_line_to (data) && path_test_remove_beginning (data); } static gboolean path_test_replace (CallbackData *data) { ClutterPathNode node; int pos = data->n_nodes / 2; node.type = CLUTTER_PATH_LINE_TO; node.points[0].x = 47; node.points[0].y = 48; data->nodes[pos] = node; clutter_path_replace_node (data->path, pos, &node); return TRUE; } static gboolean path_test_set_description (CallbackData *data) { data->n_nodes = G_N_ELEMENTS (path_nodes); memcpy (data->nodes, path_nodes, sizeof (path_nodes)); return clutter_path_set_description (data->path, path_desc); } static gboolean path_test_get_description (CallbackData *data) { char *desc1, *desc2; gboolean ret = TRUE; desc1 = clutter_path_get_description (data->path); clutter_path_clear (data->path); if (!clutter_path_set_description (data->path, desc1)) ret = FALSE; desc2 = clutter_path_get_description (data->path); if (strcmp (desc1, desc2)) ret = FALSE; g_free (desc1); g_free (desc2); return ret; } static gboolean path_test_convert_to_cairo_path (CallbackData *data) { cairo_surface_t *surface; cairo_t *cr; cairo_path_t *cpath; guint i, j; ClutterKnot path_start = { 0, 0 }, last_point = { 0, 0 }; /* Create a temporary image surface and context to hold the cairo path */ surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, 10, 10); cr = cairo_create (surface); /* Convert to a cairo path */ clutter_path_to_cairo_path (data->path, cr); /* Get a copy of the cairo path data */ cpath = cairo_copy_path (cr); /* Convert back to a clutter path */ clutter_path_clear (data->path); clutter_path_add_cairo_path (data->path, cpath); /* The relative nodes will have been converted to absolute so we need to reflect this in the node array for comparison */ for (i = 0; i < data->n_nodes; i++) { switch (data->nodes[i].type) { case CLUTTER_PATH_MOVE_TO: path_start = last_point = data->nodes[i].points[0]; break; case CLUTTER_PATH_LINE_TO: last_point = data->nodes[i].points[0]; break; case CLUTTER_PATH_CURVE_TO: last_point = data->nodes[i].points[2]; break; case CLUTTER_PATH_REL_MOVE_TO: last_point.x += data->nodes[i].points[0].x; last_point.y += data->nodes[i].points[0].y; data->nodes[i].points[0] = last_point; data->nodes[i].type = CLUTTER_PATH_MOVE_TO; path_start = last_point; break; case CLUTTER_PATH_REL_LINE_TO: last_point.x += data->nodes[i].points[0].x; last_point.y += data->nodes[i].points[0].y; data->nodes[i].points[0] = last_point; data->nodes[i].type = CLUTTER_PATH_LINE_TO; break; case CLUTTER_PATH_REL_CURVE_TO: for (j = 0; j < 3; j++) { data->nodes[i].points[j].x += last_point.x; data->nodes[i].points[j].y += last_point.y; } last_point = data->nodes[i].points[2]; data->nodes[i].type = CLUTTER_PATH_CURVE_TO; break; case CLUTTER_PATH_CLOSE: last_point = path_start; /* Cairo always adds a move to after every close so we need to insert one here. Since Cairo commit 166453c1abf2 it doesn't seem to do this anymore so will assume that if Cairo's minor version is >= 11 then it includes that commit */ if (cairo_version () < CAIRO_VERSION_ENCODE (1, 11, 0)) { memmove (data->nodes + i + 2, data->nodes + i + 1, (data->n_nodes - i - 1) * sizeof (ClutterPathNode)); data->nodes[i + 1].type = CLUTTER_PATH_MOVE_TO; data->nodes[i + 1].points[0] = last_point; data->n_nodes++; } break; } } /* Free the cairo resources */ cairo_path_destroy (cpath); cairo_destroy (cr); cairo_surface_destroy (surface); return TRUE; } static gboolean float_fuzzy_equals (float fa, float fb) { return fabs (fa - fb) <= FLOAT_FUZZ_AMOUNT; } static void set_triangle_path (CallbackData *data) { /* Triangular shaped path hitting (0,0), (64,64) and (128,0) in four parts. The two curves are actually straight lines */ static const ClutterPathNode nodes[] = { { CLUTTER_PATH_MOVE_TO, { { 0, 0 } } }, { CLUTTER_PATH_LINE_TO, { { 32, 32 } } }, { CLUTTER_PATH_CURVE_TO, { { 40, 40 }, { 56, 56 }, { 64, 64 } } }, { CLUTTER_PATH_REL_CURVE_TO, { { 8, -8 }, { 24, -24 }, { 32, -32 } } }, { CLUTTER_PATH_REL_LINE_TO, { { 32, -32 } } } }; gint i; clutter_path_clear (data->path); for (i = 0; i < G_N_ELEMENTS (nodes); i++) clutter_path_add_node (data->path, nodes + i); memcpy (data->nodes, nodes, sizeof (nodes)); data->n_nodes = G_N_ELEMENTS (nodes); } static gboolean path_test_get_position (CallbackData *data) { static const float values[] = { 0.125f, 16.0f, 16.0f, 0.375f, 48.0f, 48.0f, 0.625f, 80.0f, 48.0f, 0.875f, 112.0f, 16.0f }; gint i; set_triangle_path (data); for (i = 0; i < G_N_ELEMENTS (values); i += 3) { ClutterKnot pos; clutter_path_get_position (data->path, values[i], &pos); if (!float_fuzzy_equals (values[i + 1], pos.x) || !float_fuzzy_equals (values[i + 2], pos.y)) return FALSE; } return TRUE; } static gboolean path_test_get_length (CallbackData *data) { const float actual_length /* sqrt(64**2 + 64**2) * 2 */ = 181.019336f; guint approx_length; clutter_path_set_description (data->path, "M 0 0 L 46340 0"); g_object_get (data->path, "length", &approx_length, NULL); if (!(fabs (approx_length - 46340.f) / 46340.f <= 0.15f)) { if (g_test_verbose ()) g_print ("M 0 0 L 46340 0 - Expected 46340, got %d instead.", approx_length); return FALSE; } clutter_path_set_description (data->path, "M 0 0 L 46341 0"); g_object_get (data->path, "length", &approx_length, NULL); if (!(fabs (approx_length - 46341.f) / 46341.f <= 0.15f)) { if (g_test_verbose ()) g_print ("M 0 0 L 46341 0 - Expected 46341, got %d instead.", approx_length); return FALSE; } set_triangle_path (data); g_object_get (data->path, "length", &approx_length, NULL); /* Allow 15% margin of error */ if (!(fabs (approx_length - actual_length) / (float) actual_length <= 0.15f)) { if (g_test_verbose ()) g_print ("Expected %g, got %d instead.\n", actual_length, approx_length); return FALSE; } return TRUE; } static gboolean path_test_boxed_type (CallbackData *data) { gboolean ret = TRUE; GSList *nodes, *l; GValue value; nodes = clutter_path_get_nodes (data->path); memset (&value, 0, sizeof (value)); for (l = nodes; l; l = l->next) { g_value_init (&value, CLUTTER_TYPE_PATH_NODE); g_value_set_boxed (&value, l->data); if (!clutter_path_node_equal (l->data, g_value_get_boxed (&value))) ret = FALSE; g_value_unset (&value); } g_slist_free (nodes); return ret; } static const struct { const char *desc; PathTestFunc func; } path_tests[] = { { "Add line to", path_test_add_line_to }, { "Add move to", path_test_add_move_to }, { "Add curve to", path_test_add_curve_to }, { "Add close", path_test_add_close }, { "Add relative line to", path_test_add_rel_line_to }, { "Add relative move to", path_test_add_rel_move_to }, { "Add relative curve to", path_test_add_rel_curve_to }, { "Add string", path_test_add_string }, { "Add node by struct", path_test_add_node_by_struct }, { "Get number of nodes", path_test_get_n_nodes }, { "Get a node", path_test_get_node }, { "Get all nodes", path_test_get_nodes }, { "Insert at beginning", path_test_insert_beginning }, { "Insert at end", path_test_insert_end }, { "Insert at middle", path_test_insert_middle }, { "Add after insert", path_test_add_line_to }, { "Clear then insert", path_test_clear_insert }, { "Add string again", path_test_add_string }, { "Remove from beginning", path_test_remove_beginning }, { "Remove from end", path_test_remove_end }, { "Remove from middle", path_test_remove_middle }, { "Add after remove", path_test_add_line_to }, { "Remove only node", path_test_remove_only }, { "Add after remove again", path_test_add_line_to }, { "Replace a node", path_test_replace }, { "Set description", path_test_set_description }, { "Get description", path_test_get_description }, { "Convert to cairo path and back", path_test_convert_to_cairo_path }, { "Clear", path_test_clear }, { "Get position", path_test_get_position }, { "Check node boxed type", path_test_boxed_type }, { "Get length", path_test_get_length } }; static void compare_node (const ClutterPathNode *node, gpointer data_p) { CallbackData *data = data_p; if (data->nodes_found >= data->n_nodes) data->nodes_different = TRUE; else { guint n_points = 0, i; const ClutterPathNode *onode = data->nodes + data->nodes_found; if (node->type != onode->type) data->nodes_different = TRUE; switch (node->type & ~CLUTTER_PATH_RELATIVE) { case CLUTTER_PATH_MOVE_TO: n_points = 1; break; case CLUTTER_PATH_LINE_TO: n_points = 1; break; case CLUTTER_PATH_CURVE_TO: n_points = 3; break; case CLUTTER_PATH_CLOSE: n_points = 0; break; default: data->nodes_different = TRUE; break; } for (i = 0; i < n_points; i++) if (node->points[i].x != onode->points[i].x || node->points[i].y != onode->points[i].y) { data->nodes_different = TRUE; break; } } data->nodes_found++; } static gboolean compare_nodes (CallbackData *data) { data->nodes_different = FALSE; data->nodes_found = 0; clutter_path_foreach (data->path, compare_node, data); return !data->nodes_different && data->nodes_found == data->n_nodes; } void path_base (TestConformSimpleFixture *fixture, gconstpointer _data) { CallbackData data; gint i; memset (&data, 0, sizeof (data)); data.path = clutter_path_new (); for (i = 0; i < G_N_ELEMENTS (path_tests); i++) { gboolean succeeded; if (g_test_verbose ()) g_print ("%s... ", path_tests[i].desc); succeeded = path_tests[i].func (&data) && compare_nodes (&data); if (g_test_verbose ()) g_print ("%s\n", succeeded ? "ok" : "FAIL"); g_assert (succeeded); } g_object_unref (data.path); } muffin-6.4.1/src/tests/clutter/conform/script-parser.c0000664000175000017500000003003314723361714021761 0ustar fabiofabio#include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" #define TEST_TYPE_GROUP (test_group_get_type ()) #define TEST_TYPE_GROUP_META (test_group_meta_get_type ()) #define TEST_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_GROUP, TestGroup)) #define TEST_IS_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_GROUP)) #define TEST_GROUP_META(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_GROUP_META, TestGroupMeta)) #define TEST_IS_GROUP_META(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_GROUP_META)) typedef struct _ClutterActor TestGroup; typedef struct _ClutterActorClass TestGroupClass; typedef struct _TestGroupMeta { ClutterChildMeta parent_instance; guint is_focus : 1; } TestGroupMeta; typedef struct _ClutterChildMetaClass TestGroupMetaClass; GType test_group_meta_get_type (void); G_DEFINE_TYPE (TestGroupMeta, test_group_meta, CLUTTER_TYPE_CHILD_META) enum { PROP_META_0, PROP_META_FOCUS }; static void test_group_meta_set_property (GObject *gobject, guint prop_id, const GValue *value, GParamSpec *pspec) { TestGroupMeta *self = TEST_GROUP_META (gobject); switch (prop_id) { case PROP_META_FOCUS: self->is_focus = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void test_group_meta_get_property (GObject *gobject, guint prop_id, GValue *value, GParamSpec *pspec) { TestGroupMeta *self = TEST_GROUP_META (gobject); switch (prop_id) { case PROP_META_FOCUS: g_value_set_boolean (value, self->is_focus); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (gobject, prop_id, pspec); break; } } static void test_group_meta_class_init (TestGroupMetaClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; gobject_class->set_property = test_group_meta_set_property; gobject_class->get_property = test_group_meta_get_property; pspec = g_param_spec_boolean ("focus", "Focus", "Focus", FALSE, G_PARAM_READWRITE); g_object_class_install_property (gobject_class, PROP_META_FOCUS, pspec); } static void test_group_meta_init (TestGroupMeta *meta) { meta->is_focus = FALSE; } static void clutter_container_iface_init (ClutterContainerIface *iface) { iface->child_meta_type = TEST_TYPE_GROUP_META; } GType test_group_get_type (void); G_DEFINE_TYPE_WITH_CODE (TestGroup, test_group, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_iface_init)) static void test_group_class_init (TestGroupClass *klass) { } static void test_group_init (TestGroup *self) { } static void script_child (void) { ClutterScript *script = clutter_script_new (); GObject *container, *actor; GError *error = NULL; gboolean focus_ret; gchar *test_file; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-child.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); container = actor = NULL; clutter_script_get_objects (script, "test-group", &container, "test-rect-1", &actor, NULL); g_assert (TEST_IS_GROUP (container)); g_assert (CLUTTER_IS_RECTANGLE (actor)); focus_ret = FALSE; clutter_container_child_get (CLUTTER_CONTAINER (container), CLUTTER_ACTOR (actor), "focus", &focus_ret, NULL); g_assert (focus_ret); actor = clutter_script_get_object (script, "test-rect-2"); g_assert (CLUTTER_IS_RECTANGLE (actor)); focus_ret = FALSE; clutter_container_child_get (CLUTTER_CONTAINER (container), CLUTTER_ACTOR (actor), "focus", &focus_ret, NULL); g_assert (!focus_ret); g_object_unref (script); g_free (test_file); } static void script_single (void) { ClutterScript *script = clutter_script_new (); ClutterColor color = { 0, }; GObject *actor = NULL; GError *error = NULL; ClutterActor *rect; gchar *test_file; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-single.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); actor = clutter_script_get_object (script, "test"); g_assert (CLUTTER_IS_RECTANGLE (actor)); rect = CLUTTER_ACTOR (actor); g_assert_cmpfloat (clutter_actor_get_width (rect), ==, 50.0); g_assert_cmpfloat (clutter_actor_get_y (rect), ==, 100.0); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color); g_assert_cmpint (color.red, ==, 255); g_assert_cmpint (color.green, ==, 0xcc); g_assert_cmpint (color.alpha, ==, 0xff); g_object_unref (script); g_free (test_file); } static void script_object_property (void) { ClutterScript *script = clutter_script_new (); ClutterLayoutManager *manager; GObject *actor = NULL; GError *error = NULL; gchar *test_file; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-object-property.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); actor = clutter_script_get_object (script, "test"); g_assert (CLUTTER_IS_BOX (actor)); manager = clutter_box_get_layout_manager (CLUTTER_BOX (actor)); g_assert (CLUTTER_IS_BIN_LAYOUT (manager)); g_object_unref (script); g_free (test_file); } static void script_named_object (void) { ClutterScript *script = clutter_script_new (); ClutterLayoutManager *manager; GObject *actor = NULL; GError *error = NULL; gchar *test_file; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-named-object.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); actor = clutter_script_get_object (script, "test"); g_assert (CLUTTER_IS_BOX (actor)); manager = clutter_box_get_layout_manager (CLUTTER_BOX (actor)); g_assert (CLUTTER_IS_BOX_LAYOUT (manager)); g_assert (clutter_box_layout_get_vertical (CLUTTER_BOX_LAYOUT (manager))); g_object_unref (script); g_free (test_file); } static void script_animation (void) { ClutterScript *script = clutter_script_new (); GObject *animation = NULL; GError *error = NULL; gchar *test_file; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-animation.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); animation = clutter_script_get_object (script, "test"); g_assert (CLUTTER_IS_ANIMATION (animation)); g_object_unref (script); g_free (test_file); } static void script_layout_property (void) { ClutterScript *script = clutter_script_new (); GObject *manager, *container, *actor1, *actor2; GError *error = NULL; gchar *test_file; gboolean x_fill, expand; ClutterBoxAlignment y_align; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-layout-property.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); manager = container = actor1 = actor2 = NULL; clutter_script_get_objects (script, "manager", &manager, "container", &container, "actor-1", &actor1, "actor-2", &actor2, NULL); g_assert (CLUTTER_IS_LAYOUT_MANAGER (manager)); g_assert (CLUTTER_IS_CONTAINER (container)); g_assert (CLUTTER_IS_ACTOR (actor1)); g_assert (CLUTTER_IS_ACTOR (actor2)); x_fill = FALSE; y_align = CLUTTER_BOX_ALIGNMENT_START; expand = FALSE; clutter_layout_manager_child_get (CLUTTER_LAYOUT_MANAGER (manager), CLUTTER_CONTAINER (container), CLUTTER_ACTOR (actor1), "x-fill", &x_fill, "y-align", &y_align, "expand", &expand, NULL); g_assert (x_fill); g_assert (y_align == CLUTTER_BOX_ALIGNMENT_CENTER); g_assert (expand); x_fill = TRUE; y_align = CLUTTER_BOX_ALIGNMENT_START; expand = TRUE; clutter_layout_manager_child_get (CLUTTER_LAYOUT_MANAGER (manager), CLUTTER_CONTAINER (container), CLUTTER_ACTOR (actor2), "x-fill", &x_fill, "y-align", &y_align, "expand", &expand, NULL); g_assert (x_fill == FALSE); g_assert (y_align == CLUTTER_BOX_ALIGNMENT_END); g_assert (expand == FALSE); g_object_unref (script); } static void script_margin (void) { ClutterScript *script = clutter_script_new (); ClutterActor *actor; gchar *test_file; GError *error = NULL; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-margin.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s", error->message); g_assert_no_error (error); actor = CLUTTER_ACTOR (clutter_script_get_object (script, "actor-1")); g_assert_cmpfloat (clutter_actor_get_margin_top (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_right (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_bottom (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_left (actor), ==, 10.0f); actor = CLUTTER_ACTOR (clutter_script_get_object (script, "actor-2")); g_assert_cmpfloat (clutter_actor_get_margin_top (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_right (actor), ==, 20.0f); g_assert_cmpfloat (clutter_actor_get_margin_bottom (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_left (actor), ==, 20.0f); actor = CLUTTER_ACTOR (clutter_script_get_object (script, "actor-3")); g_assert_cmpfloat (clutter_actor_get_margin_top (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_right (actor), ==, 20.0f); g_assert_cmpfloat (clutter_actor_get_margin_bottom (actor), ==, 30.0f); g_assert_cmpfloat (clutter_actor_get_margin_left (actor), ==, 20.0f); actor = CLUTTER_ACTOR (clutter_script_get_object (script, "actor-4")); g_assert_cmpfloat (clutter_actor_get_margin_top (actor), ==, 10.0f); g_assert_cmpfloat (clutter_actor_get_margin_right (actor), ==, 20.0f); g_assert_cmpfloat (clutter_actor_get_margin_bottom (actor), ==, 30.0f); g_assert_cmpfloat (clutter_actor_get_margin_left (actor), ==, 40.0f); g_object_unref (script); g_free (test_file); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/script/single-object", script_single) CLUTTER_TEST_UNIT ("/script/container-child", script_child) CLUTTER_TEST_UNIT ("/script/named-object", script_named_object) CLUTTER_TEST_UNIT ("/script/animation", script_animation) CLUTTER_TEST_UNIT ("/script/object-property", script_object_property) CLUTTER_TEST_UNIT ("/script/layout-property", script_layout_property) CLUTTER_TEST_UNIT ("/script/actor-margin", script_margin) ) muffin-6.4.1/src/tests/clutter/conform/actor-destroy.c0000664000175000017500000001237414723361714021772 0ustar fabiofabio#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" #define TEST_TYPE_DESTROY (test_destroy_get_type ()) #define TEST_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TEST_TYPE_DESTROY, TestDestroy)) #define TEST_IS_DESTROY(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TEST_TYPE_DESTROY)) typedef struct _TestDestroy TestDestroy; typedef struct _TestDestroyClass TestDestroyClass; struct _TestDestroy { ClutterActor parent_instance; ClutterActor *bg; ClutterActor *label; GList *children; }; struct _TestDestroyClass { ClutterActorClass parent_class; }; static void clutter_container_init (ClutterContainerIface *iface); GType test_destroy_get_type (void); G_DEFINE_TYPE_WITH_CODE (TestDestroy, test_destroy, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTAINER, clutter_container_init)); static void test_destroy_add (ClutterContainer *container, ClutterActor *actor) { TestDestroy *self = TEST_DESTROY (container); if (g_test_verbose ()) g_print ("Adding '%s' (type:%s)\n", clutter_actor_get_name (actor), G_OBJECT_TYPE_NAME (actor)); self->children = g_list_prepend (self->children, actor); clutter_actor_set_parent (actor, CLUTTER_ACTOR (container)); } static void test_destroy_remove (ClutterContainer *container, ClutterActor *actor) { TestDestroy *self = TEST_DESTROY (container); if (g_test_verbose ()) g_print ("Removing '%s' (type:%s)\n", clutter_actor_get_name (actor), G_OBJECT_TYPE_NAME (actor)); g_assert_true (g_list_find (self->children, actor)); self->children = g_list_remove (self->children, actor); clutter_actor_unparent (actor); } static void clutter_container_init (ClutterContainerIface *iface) { iface->add = test_destroy_add; iface->remove = test_destroy_remove; } static void test_destroy_destroy (ClutterActor *self) { TestDestroy *test = TEST_DESTROY (self); g_assert_cmpuint (g_list_length (test->children), ==, 3); if (test->bg != NULL) { if (g_test_verbose ()) g_print ("Destroying '%s' (type:%s)\n", clutter_actor_get_name (test->bg), G_OBJECT_TYPE_NAME (test->bg)); clutter_actor_destroy (test->bg); test->bg = NULL; } if (test->label != NULL) { if (g_test_verbose ()) g_print ("Destroying '%s' (type:%s)\n", clutter_actor_get_name (test->label), G_OBJECT_TYPE_NAME (test->label)); clutter_actor_destroy (test->label); test->label = NULL; } g_assert_cmpuint (g_list_length (test->children), ==, 1); if (CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy) CLUTTER_ACTOR_CLASS (test_destroy_parent_class)->destroy (self); g_assert_null (test->children); } static void test_destroy_class_init (TestDestroyClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->destroy = test_destroy_destroy; } static void test_destroy_init (TestDestroy *self) { self->bg = clutter_rectangle_new (); clutter_container_add_actor (CLUTTER_CONTAINER (self), self->bg); clutter_actor_set_name (self->bg, "Background"); self->label = clutter_text_new (); clutter_container_add_actor (CLUTTER_CONTAINER (self), self->label); clutter_actor_set_name (self->label, "Label"); } static void on_destroy (ClutterActor *actor, gpointer data) { gboolean *destroy_called = data; g_assert_true (CLUTTER_IS_ACTOR (clutter_actor_get_parent (actor))); *destroy_called = TRUE; } static void on_parent_set (ClutterActor *actor, ClutterActor *old_parent, gpointer data) { gboolean *parent_set_called = data; *parent_set_called = TRUE; } static void on_notify (ClutterActor *actor, ClutterActor *old_parent, gpointer data) { gboolean *property_changed = data; *property_changed = TRUE; } static void actor_destruction (void) { ClutterActor *test = g_object_new (TEST_TYPE_DESTROY, NULL); ClutterActor *child = clutter_rectangle_new (); gboolean destroy_called = FALSE; gboolean parent_set_called = FALSE; gboolean property_changed = FALSE; g_object_ref_sink (test); g_object_add_weak_pointer (G_OBJECT (test), (gpointer *) &test); g_object_add_weak_pointer (G_OBJECT (child), (gpointer *) &child); if (g_test_verbose ()) g_print ("Adding external child...\n"); clutter_actor_set_name (child, "Child"); clutter_container_add_actor (CLUTTER_CONTAINER (test), child); g_signal_connect (child, "parent-set", G_CALLBACK (on_parent_set), &parent_set_called); g_signal_connect (child, "notify", G_CALLBACK (on_notify), &property_changed); g_signal_connect (child, "destroy", G_CALLBACK (on_destroy), &destroy_called); if (g_test_verbose ()) g_print ("Calling destroy()...\n"); clutter_actor_destroy (test); g_assert (destroy_called); g_assert_false (parent_set_called); g_assert_false (property_changed); g_assert_null (child); g_assert_null (test); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/destruction", actor_destruction) ) muffin-6.4.1/src/tests/clutter/conform/text.c0000664000175000017500000003626214723361714020161 0ustar fabiofabio#include #include #include #include "tests/clutter-test-utils.h" typedef struct { gunichar unichar; const char bytes[6]; gint nbytes; } TestData; static const TestData test_text_data[] = { { 0xe4, "\xc3\xa4", 2 }, /* LATIN SMALL LETTER A WITH DIAERESIS */ { 0x2665, "\xe2\x99\xa5", 3 } /* BLACK HEART SUIT */ }; static void text_utf8_validation (void) { int i; for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; gunichar unichar; char bytes[6]; int nbytes; g_assert (g_unichar_validate (t->unichar)); nbytes = g_unichar_to_utf8 (t->unichar, bytes); bytes[nbytes] = '\0'; g_assert_cmpint (nbytes, ==, t->nbytes); g_assert (memcmp (t->bytes, bytes, nbytes) == 0); unichar = g_utf8_get_char_validated (bytes, nbytes); g_assert_cmpint (unichar, ==, t->unichar); } } static int get_nbytes (ClutterText *text) { const char *s = clutter_text_get_text (text); return strlen (s); } static int get_nchars (ClutterText *text) { const char *s = clutter_text_get_text (text); g_assert (g_utf8_validate (s, -1, NULL)); return g_utf8_strlen (s, -1); } #define DONT_MOVE_CURSOR (-2) static void insert_unichar (ClutterText *text, gunichar unichar, int position) { if (position > DONT_MOVE_CURSOR) { clutter_text_set_cursor_position (text, position); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, position); } clutter_text_insert_unichar (text, unichar); } static void text_set_empty (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); g_object_ref_sink (text); g_assert_cmpstr (clutter_text_get_text (text), ==, ""); g_assert_cmpint (*clutter_text_get_text (text), ==, '\0'); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_text_set_text (text, ""); g_assert_cmpint (get_nchars (text), ==, 0); g_assert_cmpint (get_nbytes (text), ==, 0); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_set_text (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); g_object_ref_sink (text); clutter_text_set_text (text, "abcdef"); g_assert_cmpint (get_nchars (text), ==, 6); g_assert_cmpint (get_nbytes (text), ==, 6); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_text_set_cursor_position (text, 5); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 5); /* FIXME: cursor position should be -1? clutter_text_set_text (text, ""); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); */ clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_append_some (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; int j; for (j = 1; j <= 4; j++) { insert_unichar (text, t->unichar, DONT_MOVE_CURSOR); g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); } clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_prepend_some (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; int j; clutter_text_insert_unichar (text, t->unichar); g_assert_cmpint (get_nchars (text), ==, 1); g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); for (j = 2; j <= 4; j++) { insert_unichar (text, t->unichar, 0); g_assert_cmpint (get_nchars (text), ==, j); g_assert_cmpint (get_nbytes (text), ==, j * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); } clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_insert (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; clutter_text_insert_unichar (text, t->unichar); clutter_text_insert_unichar (text, t->unichar); insert_unichar (text, t->unichar, 1); g_assert_cmpint (get_nchars (text), ==, 3); g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 2); clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_delete_chars (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) clutter_text_insert_unichar (text, t->unichar); if (g_test_verbose ()) g_print ("text: %s\n", clutter_text_get_text (text)); clutter_text_set_cursor_position (text, 2); clutter_text_delete_chars (text, 1); if (g_test_verbose ()) g_print ("text: %s (cursor at: %d)\n", clutter_text_get_text (text), clutter_text_get_cursor_position (text)); g_assert_cmpint (get_nchars (text), ==, 3); g_assert_cmpint (get_nbytes (text), ==, 3 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); clutter_text_set_cursor_position (text, 2); clutter_text_delete_chars (text, 1); if (g_test_verbose ()) g_print ("text: %s (cursor at: %d)\n", clutter_text_get_text (text), clutter_text_get_cursor_position (text)); g_assert_cmpint (get_nchars (text), ==, 2); g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_get_chars (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); gchar *chars; g_object_ref_sink (text); clutter_text_set_text (text, "00abcdef11"); g_assert_cmpint (get_nchars (text), ==, 10); g_assert_cmpint (get_nbytes (text), ==, 10); g_assert_cmpstr (clutter_text_get_text (text), ==, "00abcdef11"); chars = clutter_text_get_chars (text, 2, -1); g_assert_cmpstr (chars, ==, "abcdef11"); g_free (chars); chars = clutter_text_get_chars (text, 0, 8); g_assert_cmpstr (chars, ==, "00abcdef"); g_free (chars); chars = clutter_text_get_chars (text, 2, 8); g_assert_cmpstr (chars, ==, "abcdef"); g_free (chars); chars = clutter_text_get_chars (text, 8, 12); g_assert_cmpstr (chars, ==, "11"); g_free (chars); clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_delete_text (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; j++) clutter_text_insert_unichar (text, t->unichar); clutter_text_set_cursor_position (text, 3); clutter_text_delete_text (text, 2, 4); g_assert_cmpint (get_nchars (text), ==, 2); g_assert_cmpint (get_nbytes (text), ==, 2 * t->nbytes); /* FIXME: cursor position should be -1? g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); */ clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_password_char (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); g_object_ref_sink (text); g_assert_cmpint (clutter_text_get_password_char (text), ==, 0); clutter_text_set_text (text, "hello"); g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); clutter_text_set_password_char (text, '*'); g_assert_cmpint (clutter_text_get_password_char (text), ==, '*'); g_assert_cmpstr (clutter_text_get_text (text), ==, "hello"); clutter_actor_destroy (CLUTTER_ACTOR (text)); } static ClutterEvent * init_event (void) { ClutterEvent *retval = clutter_event_new (CLUTTER_KEY_PRESS); clutter_event_set_time (retval, CLUTTER_CURRENT_TIME); clutter_event_set_flags (retval, CLUTTER_EVENT_FLAG_SYNTHETIC); return retval; } static void send_keyval (ClutterText *text, int keyval) { ClutterEvent *event = init_event (); /* Unicode should be ignored for cursor keys etc. */ clutter_event_set_key_unicode (event, 0); clutter_event_set_key_symbol (event, keyval); clutter_actor_event (CLUTTER_ACTOR (text), event, FALSE); clutter_event_free (event); } static void send_unichar (ClutterText *text, gunichar unichar) { ClutterEvent *event = init_event (); /* Key symbol should be ignored for printable characters */ clutter_event_set_key_symbol (event, 0); clutter_event_set_key_unicode (event, unichar); clutter_actor_event (CLUTTER_ACTOR (text), event, FALSE); clutter_event_free (event); } static void text_cursor (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; int j; for (j = 0; j < 4; ++j) clutter_text_insert_unichar (text, t->unichar); clutter_text_set_cursor_position (text, 2); /* test cursor moves and is clamped */ send_keyval (text, CLUTTER_KEY_Left); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 1); send_keyval (text, CLUTTER_KEY_Left); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); send_keyval (text, CLUTTER_KEY_Left); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 0); /* delete text containing the cursor */ clutter_text_set_cursor_position (text, 3); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, 3); clutter_text_delete_text (text, 2, 4); send_keyval (text, CLUTTER_KEY_Left); /* FIXME: cursor position should be -1? g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); */ clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static void text_event (void) { ClutterText *text = CLUTTER_TEXT (clutter_text_new ()); int i; g_object_ref_sink (text); /* only editable entries listen to events */ clutter_text_set_editable (text, TRUE); for (i = 0; i < G_N_ELEMENTS (test_text_data); i++) { const TestData *t = &test_text_data[i]; send_unichar (text, t->unichar); g_assert_cmpint (get_nchars (text), ==, 1); g_assert_cmpint (get_nbytes (text), ==, 1 * t->nbytes); g_assert_cmpint (clutter_text_get_cursor_position (text), ==, -1); clutter_text_set_text (text, ""); } clutter_actor_destroy (CLUTTER_ACTOR (text)); } static inline void validate_markup_attributes (ClutterText *text, PangoAttrType attr_type, int start_index, int end_index) { PangoLayout *layout; PangoAttrList *attrs; PangoAttrIterator *iter; layout = clutter_text_get_layout (text); g_assert (layout != NULL); attrs = pango_layout_get_attributes (layout); g_assert (attrs != NULL); iter = pango_attr_list_get_iterator (attrs); while (pango_attr_iterator_next (iter)) { GSList *attributes = pango_attr_iterator_get_attrs (iter); PangoAttribute *a; if (attributes == NULL) break; g_assert (attributes->data != NULL); a = attributes->data; if (a->klass->type == PANGO_ATTR_SCALE) { PangoAttrFloat *scale = (PangoAttrFloat*) a; float resource_scale; if (!clutter_actor_get_resource_scale (CLUTTER_ACTOR (text), &resource_scale)) resource_scale = 1.0; g_assert_cmpfloat (scale->value, ==, resource_scale); g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy); continue; } g_assert (a->klass->type == attr_type); g_assert_cmpint (a->start_index, ==, start_index); g_assert_cmpint (a->end_index, ==, end_index); g_slist_free_full (attributes, (GDestroyNotify) pango_attribute_destroy); } pango_attr_iterator_destroy (iter); } static void text_idempotent_use_markup (void) { ClutterText *text; const char *contents = "foo bar"; const char *display = "foo bar"; int bar_start_index = strstr (display, "bar") - display; int bar_end_index = bar_start_index + strlen ("bar"); /* case 1: text -> use_markup */ if (g_test_verbose ()) g_print ("text: '%s' -> use-markup: TRUE\n", contents); text = g_object_new (CLUTTER_TYPE_TEXT, "text", contents, "use-markup", TRUE, NULL); g_object_ref_sink (text); if (g_test_verbose ()) g_print ("Contents: '%s' (expected: '%s')\n", clutter_text_get_text (text), display); g_assert_cmpstr (clutter_text_get_text (text), ==, display); validate_markup_attributes (text, PANGO_ATTR_WEIGHT, bar_start_index, bar_end_index); clutter_actor_destroy (CLUTTER_ACTOR (text)); /* case 2: use_markup -> text */ if (g_test_verbose ()) g_print ("use-markup: TRUE -> text: '%s'\n", contents); text = g_object_new (CLUTTER_TYPE_TEXT, "use-markup", TRUE, "text", contents, NULL); if (g_test_verbose ()) g_print ("Contents: '%s' (expected: '%s')\n", clutter_text_get_text (text), display); g_assert_cmpstr (clutter_text_get_text (text), ==, display); validate_markup_attributes (text, PANGO_ATTR_WEIGHT, bar_start_index, bar_end_index); clutter_actor_destroy (CLUTTER_ACTOR (text)); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/text/utf8-validation", text_utf8_validation) CLUTTER_TEST_UNIT ("/text/set-empty", text_set_empty) CLUTTER_TEST_UNIT ("/text/set-text", text_set_text) CLUTTER_TEST_UNIT ("/text/append-some", text_append_some) CLUTTER_TEST_UNIT ("/text/prepend-some", text_prepend_some) CLUTTER_TEST_UNIT ("/text/insert", text_insert) CLUTTER_TEST_UNIT ("/text/delete-chars", text_delete_chars) CLUTTER_TEST_UNIT ("/text/get-chars", text_get_chars) CLUTTER_TEST_UNIT ("/text/delete-text", text_delete_text) CLUTTER_TEST_UNIT ("/text/password-char", text_password_char) CLUTTER_TEST_UNIT ("/text/cursor", text_cursor) CLUTTER_TEST_UNIT ("/text/event", text_event) CLUTTER_TEST_UNIT ("/text/idempotent-use-markup", text_idempotent_use_markup) ) muffin-6.4.1/src/tests/clutter/conform/actor-offscreen-redirect.c0000664000175000017500000003117514723361714024052 0ustar fabiofabio#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" typedef struct _FooActor FooActor; typedef struct _FooActorClass FooActorClass; struct _FooActorClass { ClutterActorClass parent_class; }; struct _FooActor { ClutterActor parent; guint8 last_paint_opacity; int paint_count; }; typedef struct { ClutterActor *stage; FooActor *foo_actor; ClutterActor *parent_container; ClutterActor *container; ClutterActor *child; ClutterActor *unrelated_actor; gboolean was_painted; } Data; GType foo_actor_get_type (void) G_GNUC_CONST; G_DEFINE_TYPE (FooActor, foo_actor, CLUTTER_TYPE_ACTOR); static gboolean group_has_overlaps; static void foo_actor_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); FooActor *foo_actor = (FooActor *) actor; ClutterActorBox allocation; CoglPipeline *pipeline; CoglFramebuffer *framebuffer; foo_actor->last_paint_opacity = clutter_actor_get_paint_opacity (actor); foo_actor->paint_count++; clutter_actor_get_allocation_box (actor, &allocation); /* Paint a red rectangle with the right opacity */ pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 255, 0, 0, foo_actor->last_paint_opacity); framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_draw_rectangle (framebuffer, pipeline, allocation.x1, allocation.y1, allocation.x2, allocation.y2); cogl_object_unref (pipeline); } static gboolean foo_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { return clutter_paint_volume_set_from_allocation (volume, actor); } static gboolean foo_actor_has_overlaps (ClutterActor *actor) { return FALSE; } static void foo_actor_class_init (FooActorClass *klass) { ClutterActorClass *actor_class = (ClutterActorClass *) klass; actor_class->paint = foo_actor_paint; actor_class->get_paint_volume = foo_actor_get_paint_volume; actor_class->has_overlaps = foo_actor_has_overlaps; } static void foo_actor_init (FooActor *self) { } typedef struct _FooGroup FooGroup; typedef struct _FooGroupClass FooGroupClass; struct _FooGroupClass { ClutterActorClass parent_class; }; struct _FooGroup { ClutterActor parent; }; GType foo_group_get_type (void); G_DEFINE_TYPE (FooGroup, foo_group, CLUTTER_TYPE_ACTOR) static gboolean foo_group_has_overlaps (ClutterActor *actor) { return group_has_overlaps; } static void foo_group_class_init (FooGroupClass *klass) { ClutterActorClass *actor_class = (ClutterActorClass *) klass; actor_class->has_overlaps = foo_group_has_overlaps; } static void foo_group_init (FooGroup *self) { } static void verify_results (Data *data, guint8 expected_color_red, guint8 expected_color_green, guint8 expected_color_blue, int expected_paint_count, int expected_paint_opacity) { guchar *pixel; data->foo_actor->paint_count = 0; /* Read a pixel at the center of the to determine what color it painted. This should cause a redraw */ pixel = clutter_stage_read_pixels (CLUTTER_STAGE (data->stage), 50, 50, /* x/y */ 1, 1 /* width/height */); g_assert_cmpint (expected_paint_count, ==, data->foo_actor->paint_count); g_assert_cmpint (expected_paint_opacity, ==, data->foo_actor->last_paint_opacity); g_assert_cmpint (ABS ((int) expected_color_red - (int) pixel[0]), <=, 2); g_assert_cmpint (ABS ((int) expected_color_green - (int) pixel[1]), <=, 2); g_assert_cmpint (ABS ((int) expected_color_blue - (int) pixel[2]), <=, 2); g_free (pixel); } static void verify_redraw (Data *data, int expected_paint_count) { GMainLoop *main_loop = g_main_loop_new (NULL, TRUE); gulong paint_handler; paint_handler = g_signal_connect_data (data->stage, "paint", G_CALLBACK (g_main_loop_quit), main_loop, NULL, G_CONNECT_SWAPPED | G_CONNECT_AFTER); /* Queue a redraw on the stage */ clutter_actor_queue_redraw (data->stage); data->foo_actor->paint_count = 0; /* Wait for it to paint */ g_main_loop_run (main_loop); g_clear_signal_handler (&paint_handler, data->stage); g_assert_cmpint (data->foo_actor->paint_count, ==, expected_paint_count); } static gboolean verify_redraws (gpointer user_data) { Data *data = user_data; clutter_actor_set_offscreen_redirect (data->container, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS); /* Queueing a redraw on the actor should cause a redraw */ clutter_actor_queue_redraw (data->container); verify_redraw (data, 1); /* Queueing a redraw on a child should cause a redraw */ clutter_actor_queue_redraw (data->child); verify_redraw (data, 1); /* Modifying the transformation on the parent should not cause a redraw, since the FBO stores pre-transformed rendering that can be reused with any transformation. */ clutter_actor_set_anchor_point (data->parent_container, 0, 1); verify_redraw (data, 0); /* Redrawing an unrelated actor shouldn't cause a redraw */ clutter_actor_set_position (data->unrelated_actor, 0, 1); verify_redraw (data, 0); data->was_painted = TRUE; return G_SOURCE_REMOVE; } static gboolean run_verify (gpointer user_data) { Data *data = user_data; int i; group_has_overlaps = FALSE; /* By default the actor shouldn't be redirected so the redraw should cause the actor to be painted */ verify_results (data, 255, 0, 0, 1, 255); /* Make the actor semi-transparent and verify the paint opacity */ clutter_actor_set_opacity (data->container, 127); verify_results (data, 255, 127, 127, 1, 127); /* With automatic redirect for opacity it shouldn't redirect if * has_overlaps returns FALSE; */ clutter_actor_set_offscreen_redirect (data->container, CLUTTER_OFFSCREEN_REDIRECT_AUTOMATIC_FOR_OPACITY); verify_results (data, 255, 127, 127, 1, 127); /* We do a double check here to verify that the actor wasn't cached * during the last check. If it was cached then this check wouldn't * result in any foo-actor re-paint. */ verify_results (data, 255, 127, 127, 1, 127); /* With automatic redirect for opacity it should redirect if * has_overlaps returns TRUE. * The first paint will still cause the actor to draw because * it needs to fill the cache first. It should be painted with full * opacity */ group_has_overlaps = TRUE; verify_results (data, 255, 127, 127, 1, 255); /* The second time the actor is painted it should be cached */ verify_results (data, 255, 127, 127, 0, 255); /* We should be able to change the opacity without causing the actor to redraw */ clutter_actor_set_opacity (data->container, 64); verify_results (data, 255, 191, 191, 0, 255); /* Changing it back to fully opaque should cause it not to go through the FBO so it will draw */ clutter_actor_set_opacity (data->container, 255); verify_results (data, 255, 0, 0, 1, 255); /* Tell it to always redirect through the FBO. This should cause a paint of the actor because the last draw didn't go through the FBO */ clutter_actor_set_offscreen_redirect (data->container, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS); verify_results (data, 255, 0, 0, 1, 255); /* We should be able to change the opacity without causing the actor to redraw */ clutter_actor_set_opacity (data->container, 64); verify_results (data, 255, 191, 191, 0, 255); /* Even changing it back to fully opaque shouldn't cause a redraw */ clutter_actor_set_opacity (data->container, 255); verify_results (data, 255, 0, 0, 0, 255); /* ON_IDLE: Defer redirection through the FBO until it is deemed to be the * best performing option, which means when the actor's contents have * stopped changing. */ clutter_actor_set_offscreen_redirect (data->container, CLUTTER_OFFSCREEN_REDIRECT_ON_IDLE); /* Changing modes should not incur a redraw */ verify_results (data, 255, 0, 0, 0, 255); /* These will incur a redraw because the actor is dirty: */ for (i = 0; i < 10; i++) { clutter_actor_queue_redraw (data->container); verify_results (data, 255, 0, 0, 1, 255); } /* The actor is not dirty, but also not yet cached so a redraw is expected */ verify_results (data, 255, 0, 0, 1, 255); /* These will NOT incur a redraw because the actor is unchanged: */ for (i = 0; i < 10; i++) { verify_results (data, 255, 0, 0, 0, 255); } /* The first opacity change should require no redaw */ clutter_actor_set_opacity (data->container, 64); verify_results (data, 255, 191, 191, 0, 255); /* The second opacity change should require no redaw */ clutter_actor_set_opacity (data->container, 127); verify_results (data, 255, 127, 127, 0, 255); /* The third opacity change should require no redaw */ clutter_actor_set_opacity (data->container, 255); verify_results (data, 255, 0, 0, 0, 255); /* Now several frames without the actor changing AND the FBO is populated. * Expect no internal repaints. */ for (i = 0; i < 10; i++) { verify_results (data, 255, 0, 0, 0, 255); } /* Another opacity change, no redraw expected */ clutter_actor_set_opacity (data->container, 127); verify_results (data, 255, 127, 127, 0, 255); /* Finally the actor's content changes so a redraw is expected */ clutter_actor_queue_redraw (data->container); verify_results (data, 255, 127, 127, 1, 127); /* Check redraws */ g_idle_add (verify_redraws, data); return G_SOURCE_REMOVE; } static void actor_offscreen_redirect (void) { Data data = { 0 }; data.stage = clutter_test_get_stage (); data.parent_container = clutter_actor_new (); clutter_actor_set_background_color (data.parent_container, &(ClutterColor) { 255, 255, 255, 255 }); data.container = g_object_new (foo_group_get_type (), NULL); data.foo_actor = g_object_new (foo_actor_get_type (), NULL); clutter_actor_set_size (CLUTTER_ACTOR (data.foo_actor), 100, 100); clutter_actor_add_child (data.container, CLUTTER_ACTOR (data.foo_actor)); clutter_actor_add_child (data.parent_container, data.container); clutter_actor_add_child (data.stage, data.parent_container); data.child = clutter_actor_new (); clutter_actor_set_size (data.child, 1, 1); clutter_actor_add_child (data.container, data.child); data.unrelated_actor = clutter_actor_new (); clutter_actor_set_size (data.child, 1, 1); clutter_actor_add_child (data.stage, data.unrelated_actor); clutter_actor_show (data.stage); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, run_verify, &data, NULL); while (!data.was_painted) g_main_context_iteration (NULL, FALSE); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/offscreen/redirect", actor_offscreen_redirect) ) muffin-6.4.1/src/tests/clutter/conform/interval.c0000664000175000017500000000752214723361714021016 0ustar fabiofabio#include #include "tests/clutter-test-utils.h" static void interval_initial_state (void) { ClutterInterval *interval; int initial, final; const GValue *value; interval = clutter_interval_new (G_TYPE_INT, 0, 100); g_assert (CLUTTER_IS_INTERVAL (interval)); g_assert (clutter_interval_get_value_type (interval) == G_TYPE_INT); clutter_interval_get_interval (interval, &initial, &final); g_assert_cmpint (initial, ==, 0); g_assert_cmpint (final, ==, 100); value = clutter_interval_compute (interval, 0); g_assert (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 0); value = clutter_interval_compute (interval, 1); g_assert (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 100); value = clutter_interval_compute (interval, 0.5); g_assert (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 50); clutter_interval_set_final (interval, 200); value = clutter_interval_peek_final_value (interval); g_assert (G_VALUE_HOLDS_INT (value)); g_assert_cmpint (g_value_get_int (value), ==, 200); g_object_unref (interval); } static void interval_transform (void) { ClutterInterval *interval; GValue value = G_VALUE_INIT; const GValue *value_p = NULL; interval = clutter_interval_new_with_values (G_TYPE_FLOAT, NULL, NULL); g_value_init (&value, G_TYPE_DOUBLE); g_value_set_double (&value, 0.0); clutter_interval_set_initial_value (interval, &value); g_value_set_double (&value, 100.0); clutter_interval_set_final_value (interval, &value); g_value_unset (&value); value_p = clutter_interval_peek_initial_value (interval); g_assert (G_VALUE_HOLDS_FLOAT (value_p)); g_assert_cmpfloat (g_value_get_float (value_p), ==, 0.f); value_p = clutter_interval_peek_final_value (interval); g_assert (G_VALUE_HOLDS_FLOAT (value_p)); g_assert_cmpfloat (g_value_get_float (value_p), ==, 100.f); g_object_unref (interval); } static void interval_from_script (void) { ClutterScript *script = clutter_script_new (); ClutterInterval *interval; gchar *test_file; GError *error = NULL; GValue *initial, *final; test_file = g_test_build_filename (G_TEST_DIST, "scripts", "test-script-interval.json", NULL); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_printerr ("\tError: %s", error->message); g_assert_no_error (error); interval = CLUTTER_INTERVAL (clutter_script_get_object (script, "int-1")); initial = clutter_interval_peek_initial_value (interval); if (g_test_verbose ()) g_test_message ("\tinitial ['%s'] = '%.2f'", g_type_name (G_VALUE_TYPE (initial)), g_value_get_float (initial)); g_assert (G_VALUE_HOLDS (initial, G_TYPE_FLOAT)); g_assert_cmpfloat (g_value_get_float (initial), ==, 23.3f); final = clutter_interval_peek_final_value (interval); if (g_test_verbose ()) g_test_message ("\tfinal ['%s'] = '%.2f'", g_type_name (G_VALUE_TYPE (final)), g_value_get_float (final)); g_assert (G_VALUE_HOLDS (final, G_TYPE_FLOAT)); g_assert_cmpfloat (g_value_get_float (final), ==, 42.2f); interval = CLUTTER_INTERVAL (clutter_script_get_object (script, "int-2")); initial = clutter_interval_peek_initial_value (interval); g_assert (G_VALUE_HOLDS (initial, CLUTTER_TYPE_COLOR)); final = clutter_interval_peek_final_value (interval); g_assert (G_VALUE_HOLDS (final, CLUTTER_TYPE_COLOR)); g_object_unref (script); g_free (test_file); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/interval/initial-state", interval_initial_state) CLUTTER_TEST_UNIT ("/interval/transform", interval_transform) CLUTTER_TEST_UNIT ("/interval/from-script", interval_from_script) ) muffin-6.4.1/src/tests/clutter/conform/actor-invariants.c0000664000175000017500000002502514723361714022454 0ustar fabiofabio#include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" static void actor_initial_state (void) { ClutterActor *actor; actor = clutter_actor_new (); g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); if (g_test_verbose ()) g_print ("initial state - visible: %s, realized: %s, mapped: %s\n", CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_MAPPED (actor) ? "yes" : "no"); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor))); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_shown_not_parented (void) { ClutterActor *actor; actor = clutter_actor_new (); g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_show (actor); if (g_test_verbose ()) g_print ("show without a parent - visible: %s, realized: %s, mapped: %s\n", CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_MAPPED (actor) ? "yes" : "no"); g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_realized (void) { ClutterActor *actor; ClutterActor *stage; stage = clutter_test_get_stage (); actor = clutter_actor_new (); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); clutter_actor_hide (actor); /* don't show, so won't map */ clutter_actor_add_child (stage, actor); clutter_actor_realize (actor); g_assert (CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor))); } static void actor_mapped (void) { ClutterActor *actor; ClutterActor *stage; stage = clutter_test_get_stage (); clutter_actor_show (stage); actor = clutter_actor_new (); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); clutter_actor_add_child (stage, actor); if (g_test_verbose ()) g_print ("adding to a container should map - " "visible: %s, realized: %s, mapped: %s\n", CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_MAPPED (actor) ? "yes" : "no"); g_assert (CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); clutter_actor_hide (actor); if (g_test_verbose ()) g_print ("hiding should unmap - " "visible: %s, realized: %s, mapped: %s\n", CLUTTER_ACTOR_IS_VISIBLE (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_REALIZED (actor) ? "yes" : "no", CLUTTER_ACTOR_IS_MAPPED (actor) ? "yes" : "no"); g_assert (CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (actor)); } static void actor_visibility_not_recursive (void) { ClutterActor *actor, *group; ClutterActor *stage; stage = clutter_test_get_stage (); group = clutter_actor_new (); actor = clutter_actor_new (); clutter_actor_hide (group); /* don't show, so won't map */ clutter_actor_hide (actor); /* don't show, so won't map */ g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (stage))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor))); clutter_actor_add_child (stage, group); clutter_actor_add_child (group, actor); clutter_actor_show (actor); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (group)); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (stage)); clutter_actor_show (stage); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (group)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (stage)); clutter_actor_hide (actor); clutter_actor_hide (group); clutter_actor_hide (stage); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (actor)); clutter_actor_show (stage); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (actor)); } static void actor_realize_not_recursive (void) { ClutterActor *actor, *group; ClutterActor *stage; stage = clutter_test_get_stage (); clutter_actor_show (stage); group = clutter_actor_new (); actor = clutter_actor_new (); clutter_actor_hide (group); /* don't show, so won't map */ clutter_actor_hide (actor); /* don't show, so won't map */ g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group))); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); clutter_actor_add_child (stage, group); clutter_actor_add_child (group, actor); clutter_actor_realize (group); g_assert (CLUTTER_ACTOR_IS_REALIZED (group)); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group))); /* realizing group did not realize the child */ g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor))); } static void actor_map_recursive (void) { ClutterActor *actor, *group; ClutterActor *stage; stage = clutter_test_get_stage (); clutter_actor_show (stage); group = clutter_actor_new (); actor = clutter_actor_new (); clutter_actor_hide (group); /* hide at first */ clutter_actor_show (actor); /* show at first */ g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group))); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group))); g_assert ((CLUTTER_ACTOR_IS_VISIBLE (actor))); clutter_actor_add_child (stage, group); clutter_actor_add_child (group, actor); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (group))); g_assert (!(CLUTTER_ACTOR_IS_REALIZED (actor))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group))); g_assert ((CLUTTER_ACTOR_IS_VISIBLE (actor))); /* show group, which should map and realize both * group and child. */ clutter_actor_show (group); g_assert (CLUTTER_ACTOR_IS_REALIZED (group)); g_assert (CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (CLUTTER_ACTOR_IS_MAPPED (group)); g_assert (CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (group)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); } static void actor_show_on_set_parent (void) { ClutterActor *actor, *group; gboolean show_on_set_parent; ClutterActor *stage; stage = clutter_test_get_stage (); group = clutter_actor_new (); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (group))); clutter_actor_add_child (stage, group); actor = clutter_actor_new (); g_object_get (actor, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!(CLUTTER_ACTOR_IS_VISIBLE (actor))); g_assert (show_on_set_parent); clutter_actor_add_child (group, actor); g_object_get (actor, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (show_on_set_parent); g_object_ref (actor); clutter_actor_remove_child (group, actor); g_object_get (actor, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!CLUTTER_ACTOR_IS_REALIZED (actor)); g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (show_on_set_parent); clutter_actor_destroy (actor); clutter_actor_destroy (group); actor = clutter_actor_new (); clutter_actor_add_child (stage, actor); clutter_actor_hide (actor); g_object_get (actor, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (show_on_set_parent); clutter_actor_destroy (actor); actor = clutter_actor_new (); clutter_actor_hide (actor); clutter_actor_add_child (stage, actor); g_object_get (actor, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!CLUTTER_ACTOR_IS_VISIBLE (actor)); g_assert (!CLUTTER_ACTOR_IS_MAPPED (actor)); g_assert (!show_on_set_parent); clutter_actor_destroy (actor); } static void clone_no_map (void) { ClutterActor *stage; ClutterActor *group; ClutterActor *actor; ClutterActor *clone; stage = clutter_test_get_stage (); clutter_actor_show (stage); group = clutter_actor_new (); actor = clutter_actor_new (); clutter_actor_hide (group); clutter_actor_add_child (group, actor); clutter_actor_add_child (stage, group); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); clone = clutter_clone_new (group); clutter_actor_add_child (stage, clone); g_assert (CLUTTER_ACTOR_IS_MAPPED (clone)); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (group))); g_assert (!(CLUTTER_ACTOR_IS_MAPPED (actor))); clutter_actor_destroy (CLUTTER_ACTOR (clone)); clutter_actor_destroy (CLUTTER_ACTOR (group)); } G_GNUC_BEGIN_IGNORE_DEPRECATIONS static void default_stage (void) { ClutterActor *stage, *def_stage; stage = clutter_test_get_stage (); def_stage = clutter_stage_get_default (); if (clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE)) g_assert (stage != def_stage); else g_assert (stage == def_stage); g_assert (CLUTTER_ACTOR_IS_REALIZED (def_stage)); } G_GNUC_END_IGNORE_DEPRECATIONS CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/invariants/initial-state", actor_initial_state) CLUTTER_TEST_UNIT ("/actor/invariants/show-not-parented", actor_shown_not_parented) CLUTTER_TEST_UNIT ("/actor/invariants/realized", actor_realized) CLUTTER_TEST_UNIT ("/actor/invariants/mapped", actor_mapped) CLUTTER_TEST_UNIT ("/actor/invariants/visibility-not-recursive", actor_visibility_not_recursive) CLUTTER_TEST_UNIT ("/actor/invariants/realize-not-recursive", actor_realize_not_recursive) CLUTTER_TEST_UNIT ("/actor/invariants/map-recursive", actor_map_recursive) CLUTTER_TEST_UNIT ("/actor/invariants/show-on-set-parent", actor_show_on_set_parent) CLUTTER_TEST_UNIT ("/actor/invariants/clone-no-map", clone_no_map) CLUTTER_TEST_UNIT ("/actor/invariants/default-stage", default_stage) ) muffin-6.4.1/src/tests/clutter/conform/rectangle.c0000664000175000017500000000306714723361714021136 0ustar fabiofabio#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" static void rectangle_set_size (void) { ClutterActor *rect = clutter_rectangle_new (); /* initial positioning */ g_assert_cmpint (clutter_actor_get_x (rect), ==, 0); g_assert_cmpint (clutter_actor_get_y (rect), ==, 0); clutter_actor_set_size (rect, 100, 100); /* make sure that changing the size does not affect the * rest of the bounding box */ g_assert_cmpint (clutter_actor_get_x (rect), ==, 0); g_assert_cmpint (clutter_actor_get_y (rect), ==, 0); g_assert_cmpint (clutter_actor_get_width (rect), ==, 100); g_assert_cmpint (clutter_actor_get_height (rect), ==, 100); clutter_actor_destroy (rect); } static void rectangle_set_color (void) { ClutterActor *rect = clutter_rectangle_new (); ClutterColor white = { 255, 255, 255, 255 }; ClutterColor black = { 0, 0, 0, 255 }; ClutterColor check = { 0, }; clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &black); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &check); g_assert_cmpint (check.blue, ==, black.blue); clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &white); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &check); g_assert_cmpint (check.green, ==, white.green); g_assert_cmpint (clutter_actor_get_opacity (rect), ==, white.alpha); clutter_actor_destroy (rect); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/rectangle/set-size", rectangle_set_size) CLUTTER_TEST_UNIT ("/rectangle/set-color", rectangle_set_color) ) muffin-6.4.1/src/tests/clutter/conform/group.c0000664000175000017500000000357414723361714020331 0ustar fabiofabio#define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" static void group_depth_sorting (void) { ClutterActor *group; ClutterActor *child, *test; ClutterGroup *g; GList *children; group = clutter_group_new (); g = CLUTTER_GROUP (group); child = clutter_rectangle_new (); clutter_actor_set_size (child, 20, 20); clutter_actor_set_depth (child, 0); clutter_actor_set_name (child, "zero"); clutter_container_add_actor (CLUTTER_CONTAINER (group), child); children = clutter_container_get_children (CLUTTER_CONTAINER (group)); g_assert (children->data == child); g_assert (children->next == NULL); g_list_free (children); child = clutter_rectangle_new (); clutter_actor_set_size (child, 20, 20); clutter_actor_set_depth (child, 10); clutter_actor_set_name (child, "plus-ten"); clutter_container_add_actor (CLUTTER_CONTAINER (group), child); test = clutter_group_get_nth_child (g, 0); g_assert_cmpstr (clutter_actor_get_name (test), ==, "zero"); test = clutter_group_get_nth_child (g, 1); g_assert_cmpstr (clutter_actor_get_name (test), ==, "plus-ten"); child = clutter_rectangle_new (); clutter_actor_set_size (child, 20, 20); clutter_actor_set_depth (child, -10); clutter_actor_set_name (child, "minus-ten"); clutter_container_add_actor (CLUTTER_CONTAINER (group), child); g_assert_cmpint (clutter_group_get_n_children (g), ==, 3); test = clutter_group_get_nth_child (g, 0); g_assert_cmpstr (clutter_actor_get_name (test), ==, "minus-ten"); test = clutter_group_get_nth_child (g, 1); g_assert_cmpstr (clutter_actor_get_name (test), ==, "zero"); test = clutter_group_get_nth_child (g, 2); g_assert_cmpstr (clutter_actor_get_name (test), ==, "plus-ten"); clutter_actor_destroy (group); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/group/depth-sorting", group_depth_sorting) ) muffin-6.4.1/src/tests/clutter/conform/scripts/0000775000175000017500000000000014723361714020507 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/conform/scripts/test-script-named-object.json0000664000175000017500000000171414723361714026214 0ustar fabiofabio[ { "id" : "layout", "type" : "ClutterBoxLayout", "orientation" : "vertical", "spacing" : 12, "pack-start" : false }, { "type" : "ClutterStage", "id" : "main-stage", "children" : [ { "id" : "test", "type" : "ClutterBox", "layout-manager" : "layout", "children" : [ { "id" : "child-1", "type" : "ClutterRectangle", "width" : "3 em", "height" : "3 em" } ], "constraints" : [ { "type" : "ClutterAlignConstraint", "name" : "x-align", "factor" : 0.5, "align-axis" : "x-axis", "source" : "main-stage" }, { "type" : "ClutterAlignConstraint", "name" : "y-align", "factor" : 0.5, "align-axis" : "y-axis", "source" : "main-stage" } ] } ] } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-animator-1.json0000664000175000017500000000011314723361714024322 0ustar fabiofabio{ "type" : "ClutterAnimator", "id" : "animator", "duration" : 1000 } muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-single.json0000664000175000017500000000025314723361714025142 0ustar fabiofabio{ "type" : "ClutterRectangle", "id" : "test", "width" : 50.0, "height" : 100.0, "x" : 100.0, "y" : 100.0, "color" : "#ffccdd", "name" : "Test Rectangle" } muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-object-property.json0000664000175000017500000000040114723361714027004 0ustar fabiofabio{ "id" : "test", "type" : "ClutterBox", "layout-manager" : { "id" : "layout", "type" : "ClutterBinLayout" }, "children" : [ { "id" : "child-1", "type" : "ClutterRectangle", "width" : "3 em", "height" : "3 em" } ] } muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-margin.json0000664000175000017500000000054214723361714025137 0ustar fabiofabio[ { "id" : "actor-1", "type" : "ClutterActor", "margin" : [ 10 ] }, { "id" : "actor-2", "type" : "ClutterActor", "margin" : [ 10, 20 ] }, { "id" : "actor-3", "type" : "ClutterActor", "margin" : [ 10, 20, 30 ] }, { "id" : "actor-4", "type" : "ClutterActor", "margin" : [ 10, 20, 30, 40] } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-animator-2.json0000664000175000017500000000101214723361714024322 0ustar fabiofabio[ { "type" : "ClutterRectangle", "id" : "foo", "x" : 0, "y" : 0, "width" : 100, "height" : 100 }, { "type" : "ClutterAnimator", "id" : "animator", "duration" : 1000, "properties" : [ { "object" : "foo", "name" : "x", "ease-in" : true, "interpolation" : "linear", "keys" : [ [ 0.0, "easeInCubic", 100.0 ], [ 0.2, "easeOutCubic", 150.0 ], [ 0.8, "linear", 200.0 ] ] } ] } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-animation.json0000664000175000017500000000037214723361714025642 0ustar fabiofabio{ "type" : "ClutterAnimation", "id" : "test", "mode" : "easeInCubic", "duration" : 500, "object" : { "type" : "ClutterRectangle", "id" : "rect", "opacity" : 128, "width" : 100, "height" : 16, "color" : "white" } } muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-interval.json0000664000175000017500000000042214723361714025503 0ustar fabiofabio[ { "id" : "int-1", "type" : "ClutterInterval", "value-type" : "gfloat", "initial" : 23.3, "final" : 42.2 }, { "id" : "int-2", "type" : "ClutterInterval", "value-type" : "ClutterColor", "initial" : "red", "final" : "blue" } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-timeline-markers.json0000664000175000017500000000041514723361714027131 0ustar fabiofabio{ "id" : "timeline0", "type" : "ClutterTimeline", "duration" : 1000, "markers" : [ { "name" : "marker0", "time" : 250 }, { "name" : "marker1", "time" : 500 }, { "name" : "marker2", "time" : 750 }, { "name" : "marker3", "progress" : 0.5 } ] } muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-child.json0000664000175000017500000000064014723361714024744 0ustar fabiofabio{ "type" : "TestGroup", "id" : "test-group", "children" : [ { "type" : "ClutterRectangle", "id" : "test-rect-1", "width" : 100.0, "height" : 100.0, "color" : [ 255, 0, 0, 255 ], "child::focus" : true }, { "type" : "ClutterRectangle", "id" : "test-rect-2", "width" : 100.0, "height" : 100.0, "color" : [ 0, 255, 0, 255 ] } ] } muffin-6.4.1/src/tests/clutter/conform/scripts/test-animator-3.json0000664000175000017500000000144314723361714024333 0ustar fabiofabio[ { "type" : "ClutterRectangle", "id" : "foo", "x" : 0, "y" : 0, "width" : 100, "height" : 100 }, { "type" : "ClutterAnimator", "id" : "animator", "duration" : 1000, "properties" : [ { "object" : "foo", "name" : "x", "ease-in" : true, "interpolation" : "linear", "keys" : [ [ 0.0, "easeInCubic", 100.0 ], [ 0.2, "easeOutCubic", 150.0 ], [ 0.8, "linear", 200.0 ] ] }, { "object" : "foo", "name" : "y", "ease-in" : true, "interpolation" : "linear", "keys" : [ [ 0.0, "easeInCubic", 100.0 ], [ 0.2, "easeOutCubic", 150.0 ], [ 0.8, "linear", 200.0 ] ] } ] } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-model.json0000664000175000017500000000065414723361714024766 0ustar fabiofabio{ "id" : "test-model", "type" : "ClutterListModel", "columns" : [ [ "text-column", "gchararray" ], [ "int-column", "gint" ], [ "actor-column", "ClutterRectangle" ] ], "rows" : [ [ "text-row-1", 1, null ], [ "text-row-2", 2, { "type" : "ClutterRectangle", "color" : "blue" } ], { "int-column" : 3, "actor-column" : { "type" : "ClutterRectangle", "name" : "actor-row-3" } } ] } muffin-6.4.1/src/tests/clutter/conform/scripts/test-state-1.json0000664000175000017500000000103614723361714023635 0ustar fabiofabio[ { "type" : "ClutterRectangle", "id" : "rect", "width" : 100, "height" : 100 }, { "type" : "ClutterState", "id" : "state", "transitions" : [ { "source" : "base", "target" : "clicked", "duration" : 250, "keys" : [ [ "rect", "opacity", "linear", 128 ] ] }, { "source" : "clicked", "target" : "base", "duration" : 150, "keys" : [ [ "rect", "opacity", "linear", 255 ] ] } ] } ] muffin-6.4.1/src/tests/clutter/conform/scripts/test-script-layout-property.json0000664000175000017500000000100214723361714027051 0ustar fabiofabio[ { "id" : "manager", "type" : "ClutterBoxLayout" }, { "id" : "container", "type" : "ClutterBox", "layout-manager" : "manager", "children" : [ { "id" : "actor-1", "type" : "ClutterRectangle", "layout::x-fill" : true, "layout::y-align" : "center", "layout::expand" : true }, { "id" : "actor-2", "type" : "ClutterRectangle", "layout::x-fill" : false, "layout::y-align" : "end", "layout::expand" : false } ] } ] muffin-6.4.1/src/tests/clutter/conform/timeline.c0000664000175000017500000002606314723361714021001 0ustar fabiofabio#include #include #include #include #include "test-conform-common.h" /* This test runs three timelines at 30 fps with 10 frames. Some of the timelines have markers. Once the timelines are run it then checks that all of the frames were hit, all of the markers were hit and that the completed signal was fired. The timelines are then run again but this time with a timeout source that introduces a delay. This should cause some frames to be skipped. The test is run again but only the markers and the completed signal is checked for. */ #define FRAME_COUNT 10 #define FPS 30 typedef struct _TimelineData TimelineData; struct _TimelineData { int timeline_num; guint frame_hit_count[FRAME_COUNT + 1]; GSList *markers_hit; guint completed_count; }; static void timeline_data_init (TimelineData *data, int timeline_num) { memset (data, 0, sizeof (TimelineData)); data->timeline_num = timeline_num; } static void timeline_data_destroy (TimelineData *data) { g_slist_free_full (data->markers_hit, g_free); } static void timeline_complete_cb (ClutterTimeline *timeline, TimelineData *data) { if (g_test_verbose ()) g_print ("%i: Completed\n", data->timeline_num); data->completed_count++; } static void timeline_new_frame_cb (ClutterTimeline *timeline, gint msec, TimelineData *data) { /* Calculate an approximate frame number from the duration with rounding */ int frame_no = ((msec * FRAME_COUNT + (FRAME_COUNT * 1000 / FPS) / 2) / (FRAME_COUNT * 1000 / FPS)); if (g_test_verbose ()) g_print ("%i: Doing frame %d, delta = %i\n", data->timeline_num, frame_no, clutter_timeline_get_delta (timeline)); g_assert (frame_no >= 0 && frame_no <= FRAME_COUNT); data->frame_hit_count[frame_no]++; } static void timeline_marker_reached_cb (ClutterTimeline *timeline, const gchar *marker_name, guint frame_num, TimelineData *data) { if (g_test_verbose ()) g_print ("%i: Marker '%s' (%d) reached, delta = %i\n", data->timeline_num, marker_name, frame_num, clutter_timeline_get_delta (timeline)); data->markers_hit = g_slist_prepend (data->markers_hit, g_strdup (marker_name)); } static gboolean check_timeline (ClutterTimeline *timeline, TimelineData *data, gboolean check_missed_frames) { gchar **markers; gsize n_markers; guint *marker_reached_count; gboolean succeeded = TRUE; GSList *node; int i; int missed_frame_count = 0; int frame_offset; if (clutter_timeline_get_direction (timeline) == CLUTTER_TIMELINE_BACKWARD) frame_offset = 0; else frame_offset = 1; markers = clutter_timeline_list_markers (timeline, -1, &n_markers); marker_reached_count = g_new0 (guint, n_markers); for (node = data->markers_hit; node; node = node->next) { for (i = 0; i < n_markers; i++) if (!strcmp (node->data, markers[i])) break; if (i < n_markers) marker_reached_count[i]++; else { if (g_test_verbose ()) g_print ("FAIL: unknown marker '%s' hit for timeline %i\n", (char *) node->data, data->timeline_num); succeeded = FALSE; } } for (i = 0; i < n_markers; i++) if (marker_reached_count[i] != 1) { if (g_test_verbose ()) g_print ("FAIL: marker '%s' hit %i times for timeline %i\n", markers[i], marker_reached_count[i], data->timeline_num); succeeded = FALSE; } if (check_missed_frames) { for (i = 0; i < FRAME_COUNT; i++) if (data->frame_hit_count[i + frame_offset] < 1) missed_frame_count++; if (missed_frame_count) { if (g_test_verbose ()) g_print ("FAIL: missed %i frame%s for timeline %i\n", missed_frame_count, missed_frame_count == 1 ? "" : "s", data->timeline_num); succeeded = FALSE; } } if (data->completed_count != 1) { if (g_test_verbose ()) g_print ("FAIL: timeline %i completed %i times\n", data->timeline_num, data->completed_count); succeeded = FALSE; } g_strfreev (markers); g_free (marker_reached_count); return succeeded; } static gboolean timeout_cb (gpointer data G_GNUC_UNUSED) { clutter_main_quit (); return FALSE; } static gboolean delay_cb (gpointer data) { /* Waste a bit of time so that it will skip frames */ g_usleep (G_USEC_PER_SEC * 66 / 1000); return TRUE; } void timeline_base (TestConformSimpleFixture *fixture, gconstpointer data) { ClutterTimeline *timeline_1; TimelineData data_1; ClutterTimeline *timeline_2; TimelineData data_2; ClutterTimeline *timeline_3; TimelineData data_3; gchar **markers; gsize n_markers; guint delay_tag; /* NB: We have to ensure a stage is instantiated else the master * clock wont run... */ ClutterActor *stage = clutter_stage_new (); timeline_data_init (&data_1, 1); timeline_1 = clutter_timeline_new (FRAME_COUNT * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "start-marker", 0 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "foo", 5 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "bar", 5 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "baz", 5 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "near-end-marker", 9 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_1, "end-marker", 10 * 1000 / FPS); markers = clutter_timeline_list_markers (timeline_1, 5 * 1000 / FPS, &n_markers); g_assert (markers != NULL); g_assert (n_markers == 3); g_strfreev (markers); timeline_data_init (&data_2, 2); timeline_2 = clutter_timeline_clone (timeline_1); clutter_timeline_add_marker_at_time (timeline_2, "bar", 2 * 1000 / FPS); markers = clutter_timeline_list_markers (timeline_2, -1, &n_markers); g_assert (markers != NULL); g_assert (n_markers == 1); g_assert (strcmp (markers[0], "bar") == 0); g_strfreev (markers); timeline_data_init (&data_3, 3); timeline_3 = clutter_timeline_clone (timeline_1); clutter_timeline_set_direction (timeline_3, CLUTTER_TIMELINE_BACKWARD); clutter_timeline_add_marker_at_time (timeline_3, "start-marker", FRAME_COUNT * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_3, "foo", 5 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_3, "baz", 8 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_3, "near-end-marker", 1 * 1000 / FPS); clutter_timeline_add_marker_at_time (timeline_3, "end-marker", 0 * 1000 / FPS); g_signal_connect (timeline_1, "marker-reached", G_CALLBACK (timeline_marker_reached_cb), &data_1); g_signal_connect (timeline_1, "new-frame", G_CALLBACK (timeline_new_frame_cb), &data_1); g_signal_connect (timeline_1, "completed", G_CALLBACK (timeline_complete_cb), &data_1); g_signal_connect (timeline_2, "marker-reached::bar", G_CALLBACK (timeline_marker_reached_cb), &data_2); g_signal_connect (timeline_2, "new-frame", G_CALLBACK (timeline_new_frame_cb), &data_2); g_signal_connect (timeline_2, "completed", G_CALLBACK (timeline_complete_cb), &data_2); g_signal_connect (timeline_3, "marker-reached", G_CALLBACK (timeline_marker_reached_cb), &data_3); g_signal_connect (timeline_3, "new-frame", G_CALLBACK (timeline_new_frame_cb), &data_3); g_signal_connect (timeline_3, "completed", G_CALLBACK (timeline_complete_cb), &data_3); if (g_test_verbose ()) g_print ("Without delay...\n"); clutter_timeline_start (timeline_1); clutter_timeline_start (timeline_2); clutter_timeline_start (timeline_3); clutter_threads_add_timeout (2000, timeout_cb, NULL); clutter_main (); g_assert (check_timeline (timeline_1, &data_1, TRUE)); g_assert (check_timeline (timeline_2, &data_2, TRUE)); g_assert (check_timeline (timeline_3, &data_3, TRUE)); if (g_test_verbose ()) g_print ("With delay...\n"); timeline_data_destroy (&data_1); timeline_data_init (&data_1, 1); timeline_data_destroy (&data_2); timeline_data_init (&data_2, 2); timeline_data_destroy (&data_3); timeline_data_init (&data_3, 3); clutter_timeline_start (timeline_1); clutter_timeline_start (timeline_2); clutter_timeline_start (timeline_3); clutter_threads_add_timeout (2000, timeout_cb, NULL); delay_tag = clutter_threads_add_timeout (99, delay_cb, NULL); clutter_main (); g_assert (check_timeline (timeline_1, &data_1, FALSE)); g_assert (check_timeline (timeline_2, &data_2, FALSE)); g_assert (check_timeline (timeline_3, &data_3, FALSE)); g_object_unref (timeline_1); g_object_unref (timeline_2); g_object_unref (timeline_3); timeline_data_destroy (&data_1); timeline_data_destroy (&data_2); timeline_data_destroy (&data_3); g_clear_handle_id (&delay_tag, g_source_remove); clutter_actor_destroy (stage); } void timeline_markers_from_script (TestConformSimpleFixture *fixture, gconstpointer data) { ClutterScript *script = clutter_script_new (); ClutterTimeline *timeline; GError *error = NULL; gchar *test_file; gchar **markers; gsize n_markers; test_file = clutter_test_get_data_file ("test-script-timeline-markers.json"); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error != NULL) g_print ("Error: %s", error->message); g_assert_no_error (error); timeline = CLUTTER_TIMELINE (clutter_script_get_object (script, "timeline0")); g_assert (clutter_timeline_has_marker (timeline, "marker0")); g_assert (clutter_timeline_has_marker (timeline, "marker1")); g_assert (!clutter_timeline_has_marker (timeline, "foo")); g_assert (clutter_timeline_has_marker (timeline, "marker2")); g_assert (clutter_timeline_has_marker (timeline, "marker3")); markers = clutter_timeline_list_markers (timeline, -1, &n_markers); g_assert_cmpint (n_markers, ==, 4); g_strfreev (markers); markers = clutter_timeline_list_markers (timeline, 500, &n_markers); g_assert_cmpint (n_markers, ==, 2); g_assert (markers != NULL); g_assert_cmpstr (markers[0], ==, "marker3"); g_assert_cmpstr (markers[1], ==, "marker1"); g_strfreev (markers); g_object_unref (script); g_free (test_file); } muffin-6.4.1/src/tests/clutter/conform/actor-paint-opacity.c0000664000175000017500000001110314723361714023047 0ustar fabiofabio#include #include #include "tests/clutter-test-utils.h" static void opacity_label (void) { ClutterActor *stage; ClutterActor *label; ClutterColor label_color = { 255, 0, 0, 128 }; ClutterColor color_check = { 0, }; stage = clutter_test_get_stage (); label = clutter_text_new_with_text ("Sans 18px", "Label, 50% opacity"); clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%%.get_color()/1\n"); clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_actor_add_child (stage, label); clutter_actor_set_position (label, 10, 10); if (g_test_verbose ()) g_print ("label 50%%.get_color()/2\n"); clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) g_print ("label 50%%.get_paint_opacity()/1\n"); g_assert (clutter_actor_get_paint_opacity (label) == 255); if (g_test_verbose ()) g_print ("label 50%%.get_paint_opacity()/2\n"); clutter_actor_set_opacity (label, 128); g_assert (clutter_actor_get_paint_opacity (label) == 128); } G_GNUC_BEGIN_IGNORE_DEPRECATIONS static void opacity_rectangle (void) { ClutterActor *stage; ClutterActor *rect; ClutterColor rect_color = { 0, 0, 255, 255 }; ClutterColor color_check = { 0, }; stage = clutter_test_get_stage (); rect = clutter_rectangle_new_with_color (&rect_color); clutter_actor_set_size (rect, 128, 128); clutter_actor_set_position (rect, 150, 90); if (g_test_verbose ()) g_print ("rect 100%%.get_color()/1\n"); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); g_assert (color_check.alpha == rect_color.alpha); clutter_actor_add_child (stage, rect); if (g_test_verbose ()) g_print ("rect 100%%.get_color()/2\n"); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 255); } G_GNUC_END_IGNORE_DEPRECATIONS G_GNUC_BEGIN_IGNORE_DEPRECATIONS static void opacity_paint (void) { ClutterActor *stage, *group1, *group2; ClutterActor *label, *rect; ClutterColor label_color = { 255, 0, 0, 128 }; ClutterColor rect_color = { 0, 0, 255, 255 }; ClutterColor color_check = { 0, }; stage = clutter_test_get_stage (); group1 = clutter_group_new (); clutter_actor_set_opacity (group1, 128); clutter_container_add (CLUTTER_CONTAINER (stage), group1, NULL); clutter_actor_set_position (group1, 10, 30); clutter_actor_show (group1); label = clutter_text_new_with_text ("Sans 18px", "Label+Group, 25% opacity"); clutter_text_set_color (CLUTTER_TEXT (label), &label_color); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/1\n"); clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); clutter_container_add (CLUTTER_CONTAINER (group1), label, NULL); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_color()/2\n"); clutter_text_get_color (CLUTTER_TEXT (label), &color_check); g_assert (color_check.alpha == label_color.alpha); if (g_test_verbose ()) g_print ("label 50%% + group 50%%.get_paint_opacity() = 128\n"); g_assert (clutter_actor_get_paint_opacity (label) == 128); clutter_actor_destroy (label); group2 = clutter_group_new (); clutter_container_add (CLUTTER_CONTAINER (group1), group2, NULL); clutter_actor_set_position (group2, 10, 60); rect = clutter_rectangle_new_with_color (&rect_color); clutter_actor_set_size (rect, 128, 128); if (g_test_verbose ()) g_print ("rect 100%% + group 100%% + group 50%%.get_color()/1\n"); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); g_assert (color_check.alpha == rect_color.alpha); clutter_container_add (CLUTTER_CONTAINER (group2), rect, NULL); if (g_test_verbose ()) g_print ("rect 100%% + group 100%% + group 50%%.get_color()/2\n"); clutter_rectangle_get_color (CLUTTER_RECTANGLE (rect), &color_check); g_assert (color_check.alpha == rect_color.alpha); if (g_test_verbose ()) g_print ("rect 100%%.get_paint_opacity()\n"); g_assert (clutter_actor_get_paint_opacity (rect) == 128); } G_GNUC_END_IGNORE_DEPRECATIONS CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/opacity/text", opacity_label) CLUTTER_TEST_UNIT ("/actor/opacity/rectangle", opacity_rectangle) CLUTTER_TEST_UNIT ("/actor/opacity/paint", opacity_paint) ) muffin-6.4.1/src/tests/clutter/conform/timeline-rewind.c0000664000175000017500000000413014723361714022256 0ustar fabiofabio#include #include #include #include "test-conform-common.h" #define TEST_TIMELINE_DURATION 500 #define TEST_WATCHDOG_KICK_IN_SECONDS 10 typedef struct _TestState { ClutterTimeline *timeline; gint rewind_count; } TestState; static gboolean watchdog_timeout (gpointer data) { TestState *state = data; g_test_message ("Watchdog timer kicking in"); g_test_message ("rewind_count=%i", state->rewind_count); if (state->rewind_count <= 3) { /* The test has hung */ g_test_message ("Failed (This test shouldn't have hung!)"); exit (EXIT_FAILURE); } else { g_test_message ("Passed"); clutter_main_quit (); } return G_SOURCE_REMOVE; } static void new_frame_cb (ClutterTimeline *timeline, gint elapsed_time, TestState *state) { if (elapsed_time == TEST_TIMELINE_DURATION) { g_test_message ("new-frame signal received (end of timeline)"); g_test_message ("Rewinding timeline"); clutter_timeline_rewind (timeline); state->rewind_count++; } else { if (elapsed_time == 0) { g_test_message ("new-frame signal received (start of timeline)"); } else { g_test_message ("new-frame signal received (mid frame)"); } if (state->rewind_count >= 2) { g_test_message ("Sleeping for 1 second"); g_usleep (1000000); } } } void timeline_rewind (void) { TestState state; state.timeline = clutter_timeline_new (TEST_TIMELINE_DURATION); g_signal_connect (G_OBJECT(state.timeline), "new-frame", G_CALLBACK(new_frame_cb), &state); g_test_message ("Installing a watchdog timeout " "to determine if this test hangs"); clutter_threads_add_timeout (TEST_WATCHDOG_KICK_IN_SECONDS * 1000, watchdog_timeout, &state); state.rewind_count = 0; clutter_timeline_start (state.timeline); clutter_main(); g_object_unref (state.timeline); } muffin-6.4.1/src/tests/clutter/conform/texture-fbo.c0000664000175000017500000001576114723361714021442 0ustar fabiofabio#include #include #include "test-conform-common.h" #define SOURCE_SIZE 32 #define SOURCE_DIVISIONS_X 2 #define SOURCE_DIVISIONS_Y 2 #define DIVISION_WIDTH (SOURCE_SIZE / SOURCE_DIVISIONS_X) #define DIVISION_HEIGHT (SOURCE_SIZE / SOURCE_DIVISIONS_Y) static const ClutterColor corner_colors[SOURCE_DIVISIONS_X * SOURCE_DIVISIONS_Y] = { { 0xff, 0x00, 0x00, 0xff }, /* red top left */ { 0x00, 0xff, 0x00, 0xff }, /* green top right */ { 0x00, 0x00, 0xff, 0xff }, /* blue bottom left */ { 0xff, 0x00, 0xff, 0xff } /* purple bottom right */ }; static const ClutterColor stage_color = { 0x0, 0x0, 0x0, 0xff }; typedef struct _TestState { ClutterActor *stage; guint frame; gboolean was_painted; } TestState; static ClutterActor * create_source (void) { int x, y; ClutterActor *group = clutter_group_new (); /* Create a group with a different coloured rectangle at each corner */ for (y = 0; y < SOURCE_DIVISIONS_Y; y++) for (x = 0; x < SOURCE_DIVISIONS_X; x++) { ClutterActor *rect = clutter_rectangle_new (); clutter_actor_set_size (rect, DIVISION_WIDTH, DIVISION_HEIGHT); clutter_actor_set_position (rect, DIVISION_WIDTH * x, DIVISION_HEIGHT * y); clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), corner_colors + (y * SOURCE_DIVISIONS_X + x)); clutter_container_add (CLUTTER_CONTAINER (group), rect, NULL); } return group; } static void pre_paint_clip_cb (void) { /* Generate a clip path that clips out the top left division */ cogl_path_move_to (DIVISION_WIDTH, 0); cogl_path_line_to (SOURCE_SIZE, 0); cogl_path_line_to (SOURCE_SIZE, SOURCE_SIZE); cogl_path_line_to (0, SOURCE_SIZE); cogl_path_line_to (0, DIVISION_HEIGHT); cogl_path_line_to (DIVISION_WIDTH, DIVISION_HEIGHT); cogl_path_close (); cogl_clip_push_from_path (); } static void post_paint_clip_cb (void) { cogl_clip_pop (); } static void validate_part (TestState *state, int xpos, int ypos, int clip_flags) { int x, y; /* Check whether the center of each division is the right color */ for (y = 0; y < SOURCE_DIVISIONS_Y; y++) for (x = 0; x < SOURCE_DIVISIONS_X; x++) { guchar *pixels; const ClutterColor *correct_color; /* Read the center pixels of this division */ pixels = clutter_stage_read_pixels (CLUTTER_STAGE (state->stage), x * DIVISION_WIDTH + DIVISION_WIDTH / 2 + xpos, y * DIVISION_HEIGHT + DIVISION_HEIGHT / 2 + ypos, 1, 1); /* If this division is clipped then it should be the stage color */ if ((clip_flags & (1 << ((y * SOURCE_DIVISIONS_X) + x)))) correct_color = &stage_color; else /* Otherwise it should be the color for this division */ correct_color = corner_colors + (y * SOURCE_DIVISIONS_X) + x; g_assert (pixels != NULL); g_assert_cmpint (pixels[0], ==, correct_color->red); g_assert_cmpint (pixels[1], ==, correct_color->green); g_assert_cmpint (pixels[2], ==, correct_color->blue); g_free (pixels); } } static void validate_result (TestState *state) { int ypos = 0; if (g_test_verbose ()) g_print ("Testing onscreen clone...\n"); validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0); ypos++; #if 0 /* this doesn't work */ if (g_test_verbose ()) g_print ("Testing offscreen clone...\n"); validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 0); #endif ypos++; if (g_test_verbose ()) g_print ("Testing onscreen clone with rectangular clip...\n"); validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, ~1); ypos++; if (g_test_verbose ()) g_print ("Testing onscreen clone with path clip...\n"); validate_part (state, SOURCE_SIZE, ypos * SOURCE_SIZE, 1); ypos++; } static gboolean on_paint (gpointer data) { TestState *state = data; int frame_num; /* XXX: validate_result calls clutter_stage_read_pixels which will result in * another paint run so to avoid infinite recursion we only aim to validate * the first frame. */ frame_num = state->frame++; if (frame_num == 1) validate_result (state); state->was_painted = TRUE; return G_SOURCE_REMOVE; } void texture_fbo (TestConformSimpleFixture *fixture, gconstpointer data) { TestState state; ClutterActor *actor; int ypos = 0; state.frame = 0; state.stage = clutter_stage_new (); clutter_stage_set_color (CLUTTER_STAGE (state.stage), &stage_color); /* Onscreen source with clone next to it */ actor = create_source (); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); actor = clutter_texture_new_from_actor (actor); clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); ypos++; /* Offscreen source with clone */ #if 0 /* this doesn't work */ actor = create_source (); actor = clutter_texture_new_from_actor (actor); clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); #endif ypos++; /* Source clipped to the top left division */ actor = create_source (); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); clutter_actor_set_clip (actor, 0, 0, DIVISION_WIDTH, DIVISION_HEIGHT); actor = clutter_texture_new_from_actor (actor); clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); ypos++; /* Source clipped to everything but top left division using a path */ actor = create_source (); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); clutter_actor_set_position (actor, 0, ypos * SOURCE_SIZE); g_signal_connect (actor, "paint", G_CALLBACK (pre_paint_clip_cb), NULL); g_signal_connect_after (actor, "paint", G_CALLBACK (post_paint_clip_cb), NULL); actor = clutter_texture_new_from_actor (actor); clutter_actor_set_position (actor, SOURCE_SIZE, ypos * SOURCE_SIZE); clutter_container_add (CLUTTER_CONTAINER (state.stage), actor, NULL); ypos++; clutter_actor_show (state.stage); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, on_paint, &state, NULL); while (!state.was_painted) g_main_context_iteration (NULL, FALSE); clutter_actor_destroy (state.stage); } muffin-6.4.1/src/tests/clutter/conform/actor-shader-effect.c0000664000175000017500000001772314723361714023004 0ustar fabiofabio#define CLUTTER_ENABLE_EXPERIMENTAL_API #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" /**************************************************************** Old style shader effect This uses clutter_shader_effect_set_source ****************************************************************/ static const gchar old_shader_effect_source[] = "uniform vec3 override_color;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = vec4 (override_color, 1.0);\n" "}"; typedef struct _FooOldShaderEffectClass { ClutterShaderEffectClass parent_class; } FooOldShaderEffectClass; typedef struct _FooOldShaderEffect { ClutterShaderEffect parent; } FooOldShaderEffect; GType foo_old_shader_effect_get_type (void); G_DEFINE_TYPE (FooOldShaderEffect, foo_old_shader_effect, CLUTTER_TYPE_SHADER_EFFECT); static void foo_old_shader_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { clutter_shader_effect_set_shader_source (CLUTTER_SHADER_EFFECT (effect), old_shader_effect_source); clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect), "override_color", G_TYPE_FLOAT, 3, 1.0f, 0.0f, 0.0f); CLUTTER_OFFSCREEN_EFFECT_CLASS (foo_old_shader_effect_parent_class)-> paint_target (effect, paint_context); } static void foo_old_shader_effect_class_init (FooOldShaderEffectClass *klass) { ClutterOffscreenEffectClass *offscreen_effect_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); offscreen_effect_class->paint_target = foo_old_shader_effect_paint_target; } static void foo_old_shader_effect_init (FooOldShaderEffect *self) { } /**************************************************************** New style shader effect This overrides get_static_shader_source() ****************************************************************/ static const gchar new_shader_effect_source[] = "uniform vec3 override_color;\n" "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = (vec4 (override_color, 1.0) +\n" " vec4 (0.0, 0.0, 1.0, 0.0));\n" "}"; typedef struct _FooNewShaderEffectClass { ClutterShaderEffectClass parent_class; } FooNewShaderEffectClass; typedef struct _FooNewShaderEffect { ClutterShaderEffect parent; } FooNewShaderEffect; GType foo_new_shader_effect_get_type (void); G_DEFINE_TYPE (FooNewShaderEffect, foo_new_shader_effect, CLUTTER_TYPE_SHADER_EFFECT); static gchar * foo_new_shader_effect_get_static_source (ClutterShaderEffect *effect) { static gboolean already_called = FALSE; /* This should only be called once even though we have two actors using this effect */ g_assert (!already_called); already_called = TRUE; return g_strdup (new_shader_effect_source); } static void foo_new_shader_effect_paint_target (ClutterOffscreenEffect *effect, ClutterPaintContext *paint_context) { clutter_shader_effect_set_uniform (CLUTTER_SHADER_EFFECT (effect), "override_color", G_TYPE_FLOAT, 3, 0.0f, 1.0f, 0.0f); CLUTTER_OFFSCREEN_EFFECT_CLASS (foo_new_shader_effect_parent_class)-> paint_target (effect, paint_context); } static void foo_new_shader_effect_class_init (FooNewShaderEffectClass *klass) { ClutterOffscreenEffectClass *offscreen_effect_class = CLUTTER_OFFSCREEN_EFFECT_CLASS (klass); ClutterShaderEffectClass *shader_effect_class = CLUTTER_SHADER_EFFECT_CLASS (klass); offscreen_effect_class->paint_target = foo_new_shader_effect_paint_target; shader_effect_class->get_static_shader_source = foo_new_shader_effect_get_static_source; } static void foo_new_shader_effect_init (FooNewShaderEffect *self) { } /**************************************************************** Another new style shader effect This is the same but with a different shader. This is just sanity check that each class gets its own copy of the private data ****************************************************************/ static const gchar another_new_shader_effect_source[] = "\n" "void\n" "main ()\n" "{\n" " cogl_color_out = vec4 (1.0, 0.0, 1.0, 1.0);\n" "}"; typedef struct _FooAnotherNewShaderEffectClass { ClutterShaderEffectClass parent_class; } FooAnotherNewShaderEffectClass; typedef struct _FooAnotherNewShaderEffect { ClutterShaderEffect parent; } FooAnotherNewShaderEffect; GType foo_another_new_shader_effect_get_type (void); G_DEFINE_TYPE (FooAnotherNewShaderEffect, foo_another_new_shader_effect, CLUTTER_TYPE_SHADER_EFFECT); static gchar * foo_another_new_shader_effect_get_static_source (ClutterShaderEffect *effect) { return g_strdup (another_new_shader_effect_source); } static void foo_another_new_shader_effect_class_init (FooAnotherNewShaderEffectClass *klass) { ClutterShaderEffectClass *shader_effect_class = CLUTTER_SHADER_EFFECT_CLASS (klass); shader_effect_class->get_static_shader_source = foo_another_new_shader_effect_get_static_source; } static void foo_another_new_shader_effect_init (FooAnotherNewShaderEffect *self) { } /****************************************************************/ static ClutterActor * make_actor (GType shader_type) { ClutterActor *rect; const ClutterColor white = { 0xff, 0xff, 0xff, 0xff }; rect = clutter_rectangle_new (); clutter_rectangle_set_color (CLUTTER_RECTANGLE (rect), &white); clutter_actor_set_size (rect, 50, 50); clutter_actor_add_effect (rect, g_object_new (shader_type, NULL)); return rect; } static guint32 get_pixel (CoglFramebuffer *fb, int x, int y) { guint8 data[4]; cogl_framebuffer_read_pixels (fb, x, y, 1, 1, COGL_PIXEL_FORMAT_RGBA_8888_PRE, data); return (((guint32) data[0] << 16) | ((guint32) data[1] << 8) | data[2]); } static void view_painted_cb (ClutterStage *stage, ClutterStageView *view, cairo_region_t *redraw_clip, gpointer data) { CoglFramebuffer *fb = clutter_stage_view_get_framebuffer (view); gboolean *was_painted = data; /* old shader effect */ g_assert_cmpint (get_pixel (fb, 0, 25), ==, 0xff0000); /* new shader effect */ g_assert_cmpint (get_pixel (fb, 100, 25), ==, 0x00ffff); /* another new shader effect */ g_assert_cmpint (get_pixel (fb, 200, 25), ==, 0xff00ff); /* new shader effect */ g_assert_cmpint (get_pixel (fb, 300, 25), ==, 0x00ffff); *was_painted = TRUE; } static void actor_shader_effect (void) { ClutterActor *stage; ClutterActor *rect; gboolean was_painted; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) return; stage = clutter_stage_new (); rect = make_actor (foo_old_shader_effect_get_type ()); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); rect = make_actor (foo_new_shader_effect_get_type ()); clutter_actor_set_x (rect, 100); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); rect = make_actor (foo_another_new_shader_effect_get_type ()); clutter_actor_set_x (rect, 200); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); rect = make_actor (foo_new_shader_effect_get_type ()); clutter_actor_set_x (rect, 300); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rect); clutter_actor_show (stage); was_painted = FALSE; g_signal_connect_after (stage, "paint-view", G_CALLBACK (view_painted_cb), &was_painted); while (!was_painted) g_main_context_iteration (NULL, FALSE); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/shader-effect", actor_shader_effect) ) muffin-6.4.1/src/tests/clutter/conform/actor-meta.c0000664000175000017500000000217414723361714021224 0ustar fabiofabio#include #include #include #include "tests/clutter-test-utils.h" static void actor_meta_clear (void) { ClutterActor *actor, *stage; stage = clutter_test_get_stage (); actor = clutter_actor_new (); g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_action (actor, clutter_click_action_new ()); clutter_actor_add_constraint (actor, clutter_bind_constraint_new (stage, CLUTTER_BIND_ALL, 0)); clutter_actor_add_effect (actor, clutter_blur_effect_new ()); g_assert (clutter_actor_has_actions (actor)); g_assert (clutter_actor_has_constraints (actor)); g_assert (clutter_actor_has_effects (actor)); clutter_actor_clear_actions (actor); g_assert (!clutter_actor_has_actions (actor)); clutter_actor_clear_constraints (actor); g_assert (!clutter_actor_has_constraints (actor)); clutter_actor_clear_effects (actor); g_assert (!clutter_actor_has_effects (actor)); clutter_actor_destroy (actor); g_assert (actor == NULL); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/meta/clear", actor_meta_clear) ) muffin-6.4.1/src/tests/clutter/conform/state.c0000664000175000017500000000542114723361714020306 0ustar fabiofabio#include #include "test-conform-common.h" void state_base (TestConformSimpleFixture *fixture G_GNUC_UNUSED, gconstpointer dummy G_GNUC_UNUSED) { ClutterScript *script = clutter_script_new (); GObject *state = NULL; GError *error = NULL; gchar *test_file; GList *states, *keys; ClutterStateKey *state_key; guint duration; test_file = clutter_test_get_data_file ("test-state-1.json"); clutter_script_load_from_file (script, test_file, &error); if (g_test_verbose () && error) g_print ("Error: %s\n", error->message); g_free (test_file); #if GLIB_CHECK_VERSION (2, 20, 0) g_assert_no_error (error); #else g_assert (error == NULL); #endif state = clutter_script_get_object (script, "state"); g_assert (CLUTTER_IS_STATE (state)); states = clutter_state_get_states (CLUTTER_STATE (state)); g_assert (states != NULL); g_assert (g_list_find (states, g_intern_static_string ("clicked"))); g_list_free (states); duration = clutter_state_get_duration (CLUTTER_STATE (state), "base", "clicked"); g_assert_cmpint (duration, ==, 250); duration = clutter_state_get_duration (CLUTTER_STATE (state), "clicked", "base"); g_assert_cmpint (duration, ==, 150); keys = clutter_state_get_keys (CLUTTER_STATE (state), "base", "clicked", clutter_script_get_object (script, "rect"), "opacity"); g_assert (keys != NULL); g_assert_cmpint (g_list_length (keys), ==, 1); state_key = keys->data; g_assert (clutter_state_key_get_object (state_key) == clutter_script_get_object (script, "rect")); g_assert (clutter_state_key_get_mode (state_key) == CLUTTER_LINEAR); g_assert_cmpstr (clutter_state_key_get_property_name (state_key), ==, "opacity"); g_list_free (keys); keys = clutter_state_get_keys (CLUTTER_STATE (state), NULL, NULL, NULL, NULL); g_assert_cmpint (g_list_length (keys), ==, 2); g_list_free (keys); clutter_state_set (CLUTTER_STATE (state), "base", "clicked", state, "state", CLUTTER_LINEAR, "foo", NULL); keys = clutter_state_get_keys (CLUTTER_STATE (state), "base", "clicked", NULL, NULL); g_assert (keys != NULL); g_assert_cmpint (g_list_length (keys), ==, 2); g_list_free (keys); states = clutter_state_get_states (CLUTTER_STATE (state)); g_assert_cmpint (g_list_length (states), ==, 2); g_list_free (states); clutter_state_remove_key (CLUTTER_STATE (state), NULL, "clicked", NULL, NULL); states = clutter_state_get_states (CLUTTER_STATE (state)); /* removing the "clicked" state, will also cause the "base" state to be removed * since in the .json there is no default source state */ g_assert_cmpint (g_list_length (states), ==, 0); g_list_free (states); g_object_unref (script); } muffin-6.4.1/src/tests/clutter/conform/color.c0000664000175000017500000002355414723361714020313 0ustar fabiofabio#include #include "tests/clutter-test-utils.h" static void color_hls_roundtrip (void) { ClutterColor color; gfloat hue, luminance, saturation; /* test luminance only */ clutter_color_from_string (&color, "#7f7f7f"); g_assert_cmpuint (color.red, ==, 0x7f); g_assert_cmpuint (color.green, ==, 0x7f); g_assert_cmpuint (color.blue, ==, 0x7f); clutter_color_to_hls (&color, &hue, &luminance, &saturation); g_assert_cmpfloat (hue, ==, 0.0); g_assert (luminance >= 0.0 && luminance <= 1.0); g_assert_cmpfloat (saturation, ==, 0.0); if (g_test_verbose ()) { g_print ("RGB = { %x, %x, %x }, HLS = { %.2f, %.2f, %.2f }\n", color.red, color.green, color.blue, hue, luminance, saturation); } color.red = color.green = color.blue = 0; clutter_color_from_hls (&color, hue, luminance, saturation); g_assert_cmpuint (color.red, ==, 0x7f); g_assert_cmpuint (color.green, ==, 0x7f); g_assert_cmpuint (color.blue, ==, 0x7f); /* full conversion */ clutter_color_from_string (&color, "#7f8f7f"); color.alpha = 255; g_assert_cmpuint (color.red, ==, 0x7f); g_assert_cmpuint (color.green, ==, 0x8f); g_assert_cmpuint (color.blue, ==, 0x7f); clutter_color_to_hls (&color, &hue, &luminance, &saturation); g_assert (hue >= 0.0 && hue < 360.0); g_assert (luminance >= 0.0 && luminance <= 1.0); g_assert (saturation >= 0.0 && saturation <= 1.0); if (g_test_verbose ()) { g_print ("RGB = { %x, %x, %x }, HLS = { %.2f, %.2f, %.2f }\n", color.red, color.green, color.blue, hue, luminance, saturation); } color.red = color.green = color.blue = 0; clutter_color_from_hls (&color, hue, luminance, saturation); g_assert_cmpuint (color.red, ==, 0x7f); g_assert_cmpuint (color.green, ==, 0x8f); g_assert_cmpuint (color.blue, ==, 0x7f); /* the alpha channel should be untouched */ g_assert_cmpuint (color.alpha, ==, 255); } static void color_from_string_invalid (void) { ClutterColor color; g_assert (!clutter_color_from_string (&color, "ff0000ff")); g_assert (!clutter_color_from_string (&color, "#decaffbad")); g_assert (!clutter_color_from_string (&color, "ponies")); g_assert (!clutter_color_from_string (&color, "rgb(255, 0, 0, 0)")); g_assert (!clutter_color_from_string (&color, "rgba(1.0, 0, 0)")); g_assert (!clutter_color_from_string (&color, "hsl(100, 0, 0)")); g_assert (!clutter_color_from_string (&color, "hsla(10%, 0%, 50%)")); g_assert (!clutter_color_from_string (&color, "hsla(100%, 0%, 50%, 20%)")); g_assert (!clutter_color_from_string (&color, "hsla(0.5, 0.9, 0.2, 0.4)")); } static void color_from_string_valid (void) { ClutterColor color; g_assert (clutter_color_from_string (&color, "#ff0000ff")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0xff, 0, 0, 0xff }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 0xff); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, 0); g_assert_cmpuint (color.alpha, ==, 0xff); g_assert (clutter_color_from_string (&color, "#0f0f")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0xff, 0, 0xff }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 0); g_assert_cmpuint (color.green, ==, 0xff); g_assert_cmpuint (color.blue, ==, 0); g_assert_cmpuint (color.alpha, ==, 0xff); g_assert (clutter_color_from_string (&color, "#0000ff")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0, 0, 0xff, 0xff }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 0); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, 0xff); g_assert_cmpuint (color.alpha, ==, 0xff); g_assert (clutter_color_from_string (&color, "#abc")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0xaa, 0xbb, 0xcc, 0xff }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 0xaa); g_assert_cmpuint (color.green, ==, 0xbb); g_assert_cmpuint (color.blue, ==, 0xcc); g_assert_cmpuint (color.alpha, ==, 0xff); g_assert (clutter_color_from_string (&color, "#123abc")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 0x12, 0x3a, 0xbc, 0xff }\n", color.red, color.green, color.blue, color.alpha); } g_assert (color.red == 0x12); g_assert (color.green == 0x3a); g_assert (color.blue == 0xbc); g_assert (color.alpha == 0xff); g_assert (clutter_color_from_string (&color, "rgb(255, 128, 64)")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 255, 128, 64, 255 }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 255); g_assert_cmpuint (color.green, ==, 128); g_assert_cmpuint (color.blue, ==, 64); g_assert_cmpuint (color.alpha, ==, 255); g_assert (clutter_color_from_string (&color, "rgba ( 30%, 0, 25%, 0.5 ) ")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { %.1f, 0, %.1f, 128 }\n", color.red, color.green, color.blue, color.alpha, CLAMP (255.0 / 100.0 * 30.0, 0, 255), CLAMP (255.0 / 100.0 * 25.0, 0, 255)); } g_assert_cmpuint (color.red, ==, (255.0 / 100.0 * 30.0)); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, (255.0 / 100.0 * 25.0)); g_assert_cmpuint (color.alpha, ==, 127); g_assert (clutter_color_from_string (&color, "rgb( 50%, -50%, 150% )")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 127, 0, 255, 255 }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 127); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, 255); g_assert_cmpuint (color.alpha, ==, 255); g_assert (clutter_color_from_string (&color, "hsl( 0, 100%, 50% )")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 255, 0, 0, 255 }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 255); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, 0); g_assert_cmpuint (color.alpha, ==, 255); g_assert (clutter_color_from_string (&color, "hsl( 0, 100%, 50% )")); g_assert (clutter_color_from_string (&color, "hsla( 0, 100%, 50%, 0.5 )")); if (g_test_verbose ()) { g_print ("color = { %x, %x, %x, %x }, expected = { 255, 0, 0, 127 }\n", color.red, color.green, color.blue, color.alpha); } g_assert_cmpuint (color.red, ==, 255); g_assert_cmpuint (color.green, ==, 0); g_assert_cmpuint (color.blue, ==, 0); g_assert_cmpuint (color.alpha, ==, 127); g_test_bug ("662818"); g_assert (clutter_color_from_string (&color, "hsla(0,100%,50% , 0.5)")); } static void color_to_string (void) { ClutterColor color; gchar *str; color.red = 0xcc; color.green = 0xcc; color.blue = 0xcc; color.alpha = 0x22; str = clutter_color_to_string (&color); g_assert_cmpstr (str, ==, "#cccccc22"); g_free (str); } static void color_operators (void) { ClutterColor op1, op2; ClutterColor res; clutter_color_from_pixel (&op1, 0xff0000ff); g_assert_cmpuint (op1.red, ==, 0xff); g_assert_cmpuint (op1.green, ==, 0); g_assert_cmpuint (op1.blue, ==, 0); g_assert_cmpuint (op1.alpha, ==, 0xff); clutter_color_from_pixel (&op2, 0x00ff00ff); g_assert_cmpuint (op2.red, ==, 0); g_assert_cmpuint (op2.green, ==, 0xff); g_assert_cmpuint (op2.blue, ==, 0); g_assert_cmpuint (op2.alpha, ==, 0xff); if (g_test_verbose ()) g_print ("Adding %x, %x; expected result: %x\n", clutter_color_to_pixel (&op1), clutter_color_to_pixel (&op2), 0xffff00ff); clutter_color_add (&op1, &op2, &res); g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0xffff00ff); if (g_test_verbose ()) g_print ("Checking alpha channel on color add\n"); op1.alpha = 0xdd; op2.alpha = 0xcc; clutter_color_add (&op1, &op2, &res); g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0xffff00dd); clutter_color_from_pixel (&op1, 0xffffffff); clutter_color_from_pixel (&op2, 0xff00ffff); if (g_test_verbose ()) g_print ("Subtracting %x, %x; expected result: %x\n", clutter_color_to_pixel (&op1), clutter_color_to_pixel (&op2), 0x00ff00ff); clutter_color_subtract (&op1, &op2, &res); g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0x00ff00ff); if (g_test_verbose ()) g_print ("Checking alpha channel on color subtract\n"); op1.alpha = 0xdd; op2.alpha = 0xcc; clutter_color_subtract (&op1, &op2, &res); g_assert_cmpuint (clutter_color_to_pixel (&res), ==, 0x00ff00cc); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/color/hls-roundtrip", color_hls_roundtrip) CLUTTER_TEST_UNIT ("/color/from-string/invalid", color_from_string_invalid) CLUTTER_TEST_UNIT ("/color/from-string/valid", color_from_string_valid) CLUTTER_TEST_UNIT ("/color/to-string", color_to_string) CLUTTER_TEST_UNIT ("/color/operators", color_operators) ) muffin-6.4.1/src/tests/clutter/conform/units.c0000664000175000017500000001154714723361714020336 0ustar fabiofabio#include #include "tests/clutter-test-utils.h" static void units_cache (void) { ClutterUnits units; ClutterSettings *settings; gfloat pixels; gint old_dpi; settings = clutter_settings_get_default (); g_object_get (settings, "font-dpi", &old_dpi, NULL); g_object_set (settings, "font-dpi", 96 * 1024, NULL); clutter_units_from_em (&units, 1.0); pixels = clutter_units_to_pixels (&units); g_object_set (settings, "font-dpi", ((96 * 2) * 1024), NULL); g_assert_cmpfloat (clutter_units_to_pixels (&units), !=, pixels); g_object_set (settings, "font-dpi", (96 * 1024), NULL); g_assert_cmpfloat (clutter_units_to_pixels (&units), ==, pixels); g_object_set (settings, "font-dpi", old_dpi, NULL); } static void units_constructors (void) { ClutterUnits units, units_cm; clutter_units_from_pixels (&units, 100); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 100.0); g_assert_cmpfloat (clutter_units_to_pixels (&units), ==, 100.0); clutter_units_from_em (&units, 5.0); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5.0); g_assert_cmpfloat (clutter_units_to_pixels (&units), !=, 5.0); clutter_units_from_cm (&units_cm, 5.0); g_assert (clutter_units_get_unit_type (&units_cm) == CLUTTER_UNIT_CM); g_assert_cmpfloat (clutter_units_get_unit_value (&units_cm), ==, 5.0); g_assert_cmpfloat (clutter_units_to_pixels (&units_cm), !=, 5.0); clutter_units_from_mm (&units, 50.0); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM); g_assert_cmpfloat (clutter_units_to_pixels (&units), ==, clutter_units_to_pixels (&units_cm)); } static void units_string (void) { ClutterUnits units; gchar *string; g_assert (clutter_units_from_string (&units, "") == FALSE); g_assert (clutter_units_from_string (&units, "10") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10); g_assert (clutter_units_from_string (&units, "10 px") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert (clutter_units_from_string (&units, "10 mm") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM); g_assert (clutter_units_from_string (&units, "10 cm") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_CM); g_assert (clutter_units_from_string (&units, "10 ") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_PIXEL); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 10); g_assert (clutter_units_from_string (&units, "5 em") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5); g_assert (clutter_units_from_string (&units, "5 emeralds") == FALSE); g_assert (clutter_units_from_string (&units, " 16 mm") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_MM); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 16); g_assert (clutter_units_from_string (&units, " 24 pt ") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_POINT); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 24); g_assert (clutter_units_from_string (&units, " 32 em garbage") == FALSE); g_assert (clutter_units_from_string (&units, "5.1cm") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_CM); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 5.1f); g_assert (clutter_units_from_string (&units, "5,mm") == FALSE); g_assert (clutter_units_from_string (&units, ".5pt") == TRUE); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_POINT); g_assert_cmpfloat (clutter_units_get_unit_value (&units), ==, 0.5f); g_assert (clutter_units_from_string (&units, "1 omg!!pony") == FALSE); clutter_units_from_pt (&units, 24.0); string = clutter_units_to_string (&units); g_assert_cmpstr (string, ==, "24.0 pt"); g_free (string); clutter_units_from_em (&units, 3.0); string = clutter_units_to_string (&units); g_assert_cmpstr (string, ==, "3.00 em"); units.unit_type = CLUTTER_UNIT_PIXEL; units.value = 0; g_assert (clutter_units_from_string (&units, string) == TRUE); g_assert (clutter_units_get_unit_type (&units) != CLUTTER_UNIT_PIXEL); g_assert (clutter_units_get_unit_type (&units) == CLUTTER_UNIT_EM); g_assert_cmpint ((int) clutter_units_get_unit_value (&units), ==, 3); g_free (string); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/units/string", units_string) CLUTTER_TEST_UNIT ("/units/cache", units_cache) CLUTTER_TEST_UNIT ("/units/constructors", units_constructors) ) muffin-6.4.1/src/tests/clutter/conform/cally-text.c0000664000175000017500000002244714723361714021263 0ustar fabiofabio#include #include #include #include "test-conform-common.h" #define TEST_FONT "Sans 10" typedef struct _CallbackData CallbackData; struct _CallbackData { ClutterActor *stage; ClutterActor *label; gint offset; gboolean test_failed; gint extents_x; gint extents_y; gint extents_width; gint extents_height; GSList *run_attributes; GSList *default_attributes; CallbackData *next; }; static gint attribute_lookup_func (gconstpointer data, gconstpointer user_data) { AtkAttribute *lookup_attr = (AtkAttribute*) user_data; AtkAttribute *at = (AtkAttribute *) data; if (!data) return -1; if (!g_strcmp0 (at->name, lookup_attr->name)) return g_strcmp0 (at->value, lookup_attr->value); return -1; } /* check l1 is a sub-set of l2 */ static gboolean compare_lists (GSList* l1, GSList* l2) { gboolean fail = FALSE; if (l2 && !l1) return TRUE; while (l1) { AtkAttribute *at = (AtkAttribute *) l1->data; GSList* result = g_slist_find_custom ((GSList*) l2, (gconstpointer) at, attribute_lookup_func); if (!result) { fail = TRUE; break; } l1 = g_slist_next (l1); } return fail; } static void dump_attribute_set (AtkAttributeSet *at_set) { GSList *attrs = (GSList*) at_set; while (attrs) { AtkAttribute *at = (AtkAttribute *) attrs->data; g_print ("text attribute %s = %s\n", at->name, at->value); attrs = g_slist_next (attrs); } } static gboolean check_result (CallbackData *data) { gboolean fail = FALSE; gchar *text = NULL; const gchar *expected_text = NULL; AtkObject *object = NULL; AtkText *cally_text = NULL; gunichar unichar; gunichar expected_char; gint x, y, width, height; gint pos; AtkAttributeSet *at_set = NULL; GSList *attrs; gint start = -1; gint end = -1; object = atk_gobject_accessible_for_object (G_OBJECT (data->label)); cally_text = ATK_TEXT (object); if (!cally_text) { g_print("no text\n"); return TRUE; } text = atk_text_get_text (cally_text, 0, -1); expected_text = clutter_text_get_text (CLUTTER_TEXT (data->label)); if (g_strcmp0 (expected_text, text) != 0) { if (g_test_verbose ()) g_print ("text value differs %s vs %s\n", expected_text, text); fail = TRUE; } unichar = atk_text_get_character_at_offset (cally_text, data->offset); expected_char = g_utf8_get_char (g_utf8_offset_to_pointer (text, data->offset)); if (expected_char != unichar) { if (g_test_verbose ()) g_print ("text af offset differs\n"); fail = TRUE; } atk_text_get_character_extents (cally_text, data->offset, &x, &y, &width, &height, ATK_XY_WINDOW); if (x != data->extents_x) { if (g_test_verbose ()) g_print ("extents x position at index 0 differs (current value=%d)\n", x); fail = TRUE; } if (y != data->extents_y) { if (g_test_verbose ()) g_print ("extents y position at index 0 differs (current value=%d)\n", y); fail = TRUE; } if (width != data->extents_width) { if (g_test_verbose ()) g_print ("extents width at index 0 differs (current value=%d)\n", width); fail = TRUE; } if (height != data->extents_height) { if (g_test_verbose ()) g_print ("extents height at index 0 differs (current value=%d)\n", height); fail = TRUE; } pos = atk_text_get_offset_at_point (cally_text, x, y, ATK_XY_WINDOW); if (pos != data->offset) { if (g_test_verbose ()) g_print ("offset at position (%d, %d) differs (current value=%d)\n", x, y, pos); fail = TRUE; } at_set = atk_text_get_run_attributes (cally_text, 0, &start, &end); if (start != 0) { if (g_test_verbose ()) g_print ("run attributes start offset is not 0: %d\n", start); fail = TRUE; } if (end != g_utf8_strlen (text, -1)) { if (g_test_verbose ()) g_print ("run attributes end offset is not text length: %d\n", end); fail = TRUE; } attrs = (GSList*) at_set; fail = compare_lists (attrs, data->run_attributes); if (fail && g_test_verbose ()) { g_print ("run attributes mismatch\n"); dump_attribute_set (attrs); } at_set = atk_text_get_default_attributes (cally_text); attrs = (GSList*) at_set; fail = compare_lists (attrs, data->default_attributes); if (fail && g_test_verbose ()) { g_print ("default attributes mismatch\n"); dump_attribute_set (attrs); } g_free (text); text = NULL; if (fail) { if (g_test_verbose ()) g_print ("FAIL\n"); data->test_failed = TRUE; } else if (g_test_verbose ()) g_print ("pass\n"); return fail; } static gboolean do_tests (CallbackData *data) { while (data) { gboolean result = check_result (data); g_assert (result == FALSE); data = data->next; } clutter_main_quit (); return FALSE; } static GSList* build_attribute_set (const gchar* first_attribute, ...) { AtkAttributeSet *return_set = g_slist_alloc (); va_list args; const gchar *name; const gchar *value; gint i = 0; value = first_attribute; va_start (args, first_attribute); while (value) { if ((i> 0) && (i % 2 != 0)) { AtkAttribute *at = g_malloc (sizeof (AtkAttribute)); at->name = g_strdup (name); at->value = g_strdup (value); return_set = g_slist_prepend (return_set, at); } i++; name = g_strdup (value); value = va_arg (args, gchar*); } va_end (args); return return_set; } void cally_text (void) { CallbackData data; CallbackData data1; GSList* default_attributes = build_attribute_set ("left-margin", "0", "right-margin", "0", "indent", "0", "invisible", "false", "editable", "false", "pixels-above-lines", "0", "pixels-below-lines", "0", "pixels-inside-wrap", "0", "bg-full-height", "0", "bg-stipple", "false", "fg-stipple", "false", "fg-color", "0,0,0", "wrap-mode", "word", "justification", "left", "size", "10", "weight", "400", "family-name", "Sans", "stretch", "normal", "variant", "normal", "style", "normal", "language", "en-us", "direction", "ltr", NULL); memset (&data, 0, sizeof (data)); data.stage = clutter_stage_new (); data.default_attributes = default_attributes; data.run_attributes = build_attribute_set ("fg-color", "0,0,0", NULL); data.label = clutter_text_new_with_text (TEST_FONT, "Lorem ipsum dolor sit amet"); clutter_container_add (CLUTTER_CONTAINER (data.stage), data.label, NULL); data.offset = 6; data.extents_x = 64; data.extents_y = 99; data.extents_width = 3; data.extents_height = 17; clutter_actor_set_position (data.label, 20, 100); memset (&data1, 0, sizeof (data1)); data1.stage = data.stage; data1.default_attributes = default_attributes; data1.run_attributes = build_attribute_set ("bg-color", "0,65535,0", "fg-color", "65535,65535,0", "strikethrough", "true", NULL); data1.label = clutter_text_new_with_text (TEST_FONT, ""); clutter_text_set_markup (CLUTTER_TEXT(data1.label), "Lorem ipsum dolor sit amet"); clutter_container_add (CLUTTER_CONTAINER (data1.stage), data1.label, NULL); data1.offset = 10; data1.extents_x = 90; data1.extents_y = 199; data1.extents_width = 13; data1.extents_height = 17; clutter_actor_set_position (data1.label, 20, 200); data.next = &data1; clutter_actor_show (data.stage); clutter_threads_add_idle ((GSourceFunc) do_tests, &data); clutter_main (); clutter_actor_destroy (data.stage); if (g_test_verbose ()) g_print ("\nOverall result: "); if (g_test_verbose ()) { if (data.test_failed) g_print ("FAIL\n"); else g_print ("pass\n"); } else { g_assert (data.test_failed != TRUE); g_assert (data1.test_failed != TRUE); } } muffin-6.4.1/src/tests/clutter/conform/actor-graph.c0000664000175000017500000005116214723361714021400 0ustar fabiofabio#include #include "tests/clutter-test-utils.h" static void actor_add_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", NULL)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 3); iter = clutter_actor_get_first_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); iter = clutter_actor_get_next_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); iter = clutter_actor_get_next_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); g_assert (iter == clutter_actor_get_last_child (actor)); g_assert (clutter_actor_get_next_sibling (iter) == NULL); iter = clutter_actor_get_last_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); g_assert (iter == clutter_actor_get_first_child (actor)); g_assert (clutter_actor_get_previous_sibling (iter) == NULL); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_insert_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_insert_child_at_index (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL), 0); iter = clutter_actor_get_first_child (actor); g_assert (iter != NULL); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); g_assert (iter == clutter_actor_get_child_at_index (actor, 0)); clutter_actor_insert_child_below (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL), iter); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 2); iter = clutter_actor_get_first_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); iter = clutter_actor_get_next_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); g_assert (iter == clutter_actor_get_child_at_index (actor, 1)); iter = clutter_actor_get_first_child (actor); clutter_actor_insert_child_above (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", NULL), iter); iter = clutter_actor_get_last_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); clutter_actor_remove_all_children (actor); clutter_actor_insert_child_at_index (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "1", NULL), 0); iter = clutter_actor_get_child_at_index (actor, 0); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "1"); g_assert (clutter_actor_get_first_child (actor) == iter); g_assert (clutter_actor_get_last_child (actor) == iter); clutter_actor_insert_child_at_index (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "2", NULL), 0); iter = clutter_actor_get_child_at_index (actor, 0); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "2"); g_assert (clutter_actor_get_first_child (actor) == iter); iter = clutter_actor_get_child_at_index (actor, 1); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "1"); g_assert (clutter_actor_get_last_child (actor) == iter); clutter_actor_insert_child_at_index (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "3", NULL), -1); iter = clutter_actor_get_child_at_index (actor, 2); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "3"); g_assert (clutter_actor_get_last_child (actor) == iter); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_remove_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 2); g_assert (clutter_actor_get_first_child (actor) != clutter_actor_get_last_child (actor)); iter = clutter_actor_get_first_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); iter = clutter_actor_get_last_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); clutter_actor_remove_child (actor, clutter_actor_get_first_child (actor)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 1); iter = clutter_actor_get_first_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); g_assert (clutter_actor_get_first_child (actor) == clutter_actor_get_last_child (actor)); clutter_actor_remove_child (actor, clutter_actor_get_first_child (actor)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 0); g_assert (clutter_actor_get_first_child (actor) == NULL); g_assert (clutter_actor_get_last_child (actor) == NULL); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_raise_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; gboolean show_on_set_parent; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", "visible", FALSE, NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", "visible", FALSE, NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", "visible", FALSE, NULL)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 3); iter = clutter_actor_get_child_at_index (actor, 1); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); clutter_actor_set_child_above_sibling (actor, iter, clutter_actor_get_child_at_index (actor, 2)); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 0)), ==, "foo"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 1)), ==, "baz"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 2)), ==, "bar"); g_assert (!clutter_actor_is_visible (iter)); g_object_get (iter, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!show_on_set_parent); iter = clutter_actor_get_child_at_index (actor, 0); clutter_actor_set_child_above_sibling (actor, iter, NULL); g_object_add_weak_pointer (G_OBJECT (iter), (gpointer *) &iter); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 0)), ==, "baz"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 1)), ==, "bar"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 2)), ==, "foo"); g_assert (!clutter_actor_is_visible (iter)); g_object_get (iter, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!show_on_set_parent); clutter_actor_destroy (actor); g_assert (actor == NULL); g_assert (iter == NULL); } static void actor_lower_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; gboolean show_on_set_parent; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", "visible", FALSE, NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", "visible", FALSE, NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", "visible", FALSE, NULL)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 3); iter = clutter_actor_get_child_at_index (actor, 1); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); clutter_actor_set_child_below_sibling (actor, iter, clutter_actor_get_child_at_index (actor, 0)); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 0)), ==, "bar"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 1)), ==, "foo"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 2)), ==, "baz"); g_assert (!clutter_actor_is_visible (iter)); g_object_get (iter, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!show_on_set_parent); iter = clutter_actor_get_child_at_index (actor, 2); clutter_actor_set_child_below_sibling (actor, iter, NULL); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 0)), ==, "baz"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 1)), ==, "bar"); g_assert_cmpstr (clutter_actor_get_name (clutter_actor_get_child_at_index (actor, 2)), ==, "foo"); g_assert (!clutter_actor_is_visible (iter)); g_object_get (iter, "show-on-set-parent", &show_on_set_parent, NULL); g_assert (!show_on_set_parent); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_replace_child (void) { ClutterActor *actor = clutter_actor_new (); ClutterActor *iter; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); iter = clutter_actor_get_child_at_index (actor, 0); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); clutter_actor_replace_child (actor, iter, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", NULL)); iter = clutter_actor_get_child_at_index (actor, 0); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); iter = clutter_actor_get_child_at_index (actor, 1); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); clutter_actor_replace_child (actor, iter, g_object_new (CLUTTER_TYPE_ACTOR, "name", "qux", NULL)); iter = clutter_actor_get_child_at_index (actor, 0); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); iter = clutter_actor_get_child_at_index (actor, 1); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "qux"); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); clutter_actor_replace_child (actor, iter, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); iter = clutter_actor_get_last_child (actor); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "foo"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "bar"); iter = clutter_actor_get_previous_sibling (iter); g_assert_cmpstr (clutter_actor_get_name (iter), ==, "baz"); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_remove_all (void) { ClutterActor *actor = clutter_actor_new (); g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "baz", NULL)); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 3); clutter_actor_remove_all_children (actor); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 0); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_added (ClutterContainer *container, ClutterActor *child, gpointer data) { ClutterActor *actor = CLUTTER_ACTOR (container); int *counter = data; ClutterActor *old_child; if (g_test_verbose ()) g_print ("Adding actor '%s'\n", clutter_actor_get_name (child)); old_child = clutter_actor_get_child_at_index (actor, 0); if (old_child != child) clutter_actor_remove_child (actor, old_child); *counter += 1; } static void actor_removed (ClutterContainer *container, ClutterActor *child, gpointer data) { int *counter = data; if (g_test_verbose ()) g_print ("Removing actor '%s'\n", clutter_actor_get_name (child)); *counter += 1; } static void actor_container_signals (void) { ClutterActor *actor = clutter_actor_new (); int add_count, remove_count; g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); add_count = remove_count = 0; g_signal_connect (actor, "actor-added", G_CALLBACK (actor_added), &add_count); g_signal_connect (actor, "actor-removed", G_CALLBACK (actor_removed), &remove_count); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "foo", NULL)); g_assert_cmpint (add_count, ==, 1); g_assert_cmpint (remove_count, ==, 0); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 1); clutter_actor_add_child (actor, g_object_new (CLUTTER_TYPE_ACTOR, "name", "bar", NULL)); g_assert_cmpint (add_count, ==, 2); g_assert_cmpint (remove_count, ==, 1); g_assert_cmpint (clutter_actor_get_n_children (actor), ==, 1); g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (actor_added), &add_count); g_signal_handlers_disconnect_by_func (actor, G_CALLBACK (actor_removed), &remove_count); clutter_actor_destroy (actor); g_assert (actor == NULL); } static void actor_contains (void) { /* This build up the following tree: * * a * ╱ │ ╲ * ╱ │ ╲ * b c d * ╱ ╲ ╱ ╲ ╱ ╲ * e f g h i j */ struct { ClutterActor *actor_a, *actor_b, *actor_c, *actor_d, *actor_e; ClutterActor *actor_f, *actor_g, *actor_h, *actor_i, *actor_j; } d; int x, y; ClutterActor **actor_array = &d.actor_a; /* Matrix of expected results */ static const gboolean expected_results[] = { /* a, b, c, d, e, f, g, h, i, j */ /* a */ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, /* b */ 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, /* c */ 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, /* d */ 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, /* e */ 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, /* f */ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, /* g */ 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, /* h */ 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, /* i */ 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, /* j */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 }; d.actor_a = clutter_actor_new (); d.actor_b = clutter_actor_new (); d.actor_c = clutter_actor_new (); d.actor_d = clutter_actor_new (); d.actor_e = clutter_actor_new (); d.actor_f = clutter_actor_new (); d.actor_g = clutter_actor_new (); d.actor_h = clutter_actor_new (); d.actor_i = clutter_actor_new (); d.actor_j = clutter_actor_new (); clutter_actor_add_child (d.actor_a, d.actor_b); clutter_actor_add_child (d.actor_a, d.actor_c); clutter_actor_add_child (d.actor_a, d.actor_d); clutter_actor_add_child (d.actor_b, d.actor_e); clutter_actor_add_child (d.actor_b, d.actor_f); clutter_actor_add_child (d.actor_c, d.actor_g); clutter_actor_add_child (d.actor_c, d.actor_h); clutter_actor_add_child (d.actor_d, d.actor_i); clutter_actor_add_child (d.actor_d, d.actor_j); for (y = 0; y < 10; y++) for (x = 0; x < 10; x++) g_assert_cmpint (clutter_actor_contains (actor_array[x], actor_array[y]), ==, expected_results[x * 10 + y]); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/graph/add-child", actor_add_child) CLUTTER_TEST_UNIT ("/actor/graph/insert-child", actor_insert_child) CLUTTER_TEST_UNIT ("/actor/graph/remove-child", actor_remove_child) CLUTTER_TEST_UNIT ("/actor/graph/raise-child", actor_raise_child) CLUTTER_TEST_UNIT ("/actor/graph/lower-child", actor_lower_child) CLUTTER_TEST_UNIT ("/actor/graph/replace-child", actor_replace_child) CLUTTER_TEST_UNIT ("/actor/graph/remove-all", actor_remove_all) CLUTTER_TEST_UNIT ("/actor/graph/container-signals", actor_container_signals) CLUTTER_TEST_UNIT ("/actor/graph/contains", actor_contains) ) muffin-6.4.1/src/tests/clutter/conform/actor-anchors.c0000664000175000017500000006542114723361714021737 0ustar fabiofabio#include #include #define CLUTTER_DISABLE_DEPRECATION_WARNINGS #include #include "tests/clutter-test-utils.h" #define NOTIFY_ANCHOR_X (1 << 0) #define NOTIFY_ANCHOR_Y (1 << 1) #define NOTIFY_ANCHOR_GRAVITY (1 << 2) #define NOTIFY_SCALE_X (1 << 3) #define NOTIFY_SCALE_Y (1 << 4) #define NOTIFY_SCALE_CENTER_X (1 << 5) #define NOTIFY_SCALE_CENTER_Y (1 << 6) #define NOTIFY_SCALE_GRAVITY (1 << 7) #define NOTIFY_ROTATION_ANGLE_X (1 << 8) #define NOTIFY_ROTATION_ANGLE_Y (1 << 9) #define NOTIFY_ROTATION_ANGLE_Z (1 << 10) #define NOTIFY_ROTATION_CENTER_X (1 << 11) #define NOTIFY_ROTATION_CENTER_Y (1 << 12) #define NOTIFY_ROTATION_CENTER_Z (1 << 13) #define NOTIFY_ROTATION_CENTER_Z_GRAVITY (1 << 14) #define RECT_WIDTH 100.0 #define RECT_HEIGHT 80.0 /* Allow the transformed position by off by a certain number of pixels */ #define POSITION_TOLERANCE 2 typedef struct _TestState { gulong notifications; ClutterActor *rect; } TestState; static const struct { ClutterGravity gravity; gfloat x_pos; gfloat y_pos; } gravities[] = { { CLUTTER_GRAVITY_NORTH, RECT_WIDTH / 2, 0 }, { CLUTTER_GRAVITY_NORTH_EAST, RECT_WIDTH, 0 }, { CLUTTER_GRAVITY_EAST, RECT_WIDTH, RECT_HEIGHT / 2 }, { CLUTTER_GRAVITY_SOUTH_EAST, RECT_WIDTH, RECT_HEIGHT }, { CLUTTER_GRAVITY_SOUTH, RECT_WIDTH / 2, RECT_HEIGHT }, { CLUTTER_GRAVITY_SOUTH_WEST, 0, RECT_HEIGHT }, { CLUTTER_GRAVITY_WEST, 0, RECT_HEIGHT / 2 }, { CLUTTER_GRAVITY_NORTH_WEST, 0, 0 }, { CLUTTER_GRAVITY_CENTER, RECT_WIDTH / 2, RECT_HEIGHT / 2 } }; static const char * const properties[] = { "anchor-x", "anchor-y", "anchor-gravity", "scale-x", "scale-y", "scale-center-x", "scale-center-y", "scale-gravity", "rotation-angle-x", "rotation-angle-y", "rotation-angle-z", "rotation-center-x", "rotation-center-y", "rotation-center-z", "rotation-center-z-gravity" }; static const int n_properties = G_N_ELEMENTS (properties); static void notify_cb (GObject *object, GParamSpec *pspec, TestState *state) { int i; int new_flags = 0; int flag = 1; for (i = 0; i < n_properties; i++) { if (!strcmp (properties[i], pspec->name)) new_flags |= flag; flag <<= 1; } g_assert ((new_flags & state->notifications) == 0); state->notifications |= new_flags; } #define assert_notifications(flags) G_STMT_START { \ g_assert (state->notifications == (flags)); \ state->notifications = 0; } G_STMT_END /* Helper macro to assert the transformed position. This needs to be a macro so that the assertion failure will report the right line number */ #define assert_coords(state, x_1, y_1, x_2, y_2) G_STMT_START { \ graphene_point3d_t verts[4]; \ clutter_actor_get_abs_allocation_vertices ((state)->rect, verts); \ check_coords ((state), (x_1), (y_1), (x_2), (y_2), verts); \ g_assert (approx_equal ((x_1), verts[0].x)); \ g_assert (approx_equal ((y_1), verts[0].y)); \ g_assert (approx_equal ((x_2), verts[3].x)); \ g_assert (approx_equal ((y_2), verts[3].y)); } G_STMT_END #define assert_position(state, x, y) \ assert_coords((state), (x), (y), (x) + RECT_WIDTH, (y) + RECT_HEIGHT) #define assert_vertex_and_free(v, xc, yc, zc) G_STMT_START { \ g_assert (approx_equal (v->x, xc) && \ approx_equal (v->y, yc) && \ approx_equal (v->z, zc)); \ g_boxed_free (GRAPHENE_TYPE_POINT3D, v); } G_STMT_END static inline gboolean approx_equal (int a, int b) { return abs (a - b) <= POSITION_TOLERANCE; } static void check_coords (TestState *state, gint x_1, gint y_1, gint x_2, gint y_2, const graphene_point3d_t *verts) { if (g_test_verbose ()) g_print ("checking that (%i,%i,%i,%i) \xe2\x89\x88 (%i,%i,%i,%i): %s\n", x_1, y_1, x_2, y_2, (int) (verts[0].x), (int) (verts[0].y), (int) (verts[3].x), (int) (verts[3].y), approx_equal (x_1, verts[0].x) && approx_equal (y_1, verts[0].y) && approx_equal (x_2, verts[3].x) && approx_equal (y_2, verts[3].y) ? "yes" : "NO"); } static void test_anchor_point (TestState *state) { ClutterActor *rect = state->rect; gfloat anchor_x, anchor_y; ClutterGravity anchor_gravity; int i; /* Assert the default settings */ g_assert (clutter_actor_get_x (rect) == 100); g_assert (clutter_actor_get_y (rect) == 200); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == 0); g_assert (anchor_y == 0); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); /* Change the anchor point */ clutter_actor_set_anchor_point (rect, 20, 30); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == 20); g_assert (anchor_y == 30); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); assert_position (state, 80, 170); assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); /* Move the anchor point */ clutter_actor_move_anchor_point (rect, 40, 50); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == 40); g_assert (anchor_y == 50); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); assert_position (state, 80, 170); assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); /* Put the actor back to its default position */ clutter_actor_set_position (rect, 100, 200); /* Change the anchor point with each of the gravities */ for (i = 0; i < G_N_ELEMENTS (gravities); i++) { if (g_test_verbose ()) { GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); GEnumValue *value = g_enum_get_value (gravity_class, gravities[i].gravity); g_print ("Setting gravity to %s\n", value ? value->value_name : "?"); g_type_class_unref (gravity_class); } g_object_set (rect, "anchor-gravity", gravities[i].gravity, NULL); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == gravities[i].x_pos); g_assert (anchor_y == gravities[i].y_pos); g_assert (anchor_gravity == gravities[i].gravity); assert_position (state, 100 - gravities[i].x_pos, 200 - gravities[i].y_pos); assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y | NOTIFY_ANCHOR_GRAVITY); } /* Verify that the anchor point moves if the actor changes size when it is set from the gravity */ clutter_actor_set_size (rect, RECT_WIDTH * 2, RECT_HEIGHT * 2); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == RECT_WIDTH); g_assert (anchor_y == RECT_HEIGHT); g_assert (anchor_gravity == CLUTTER_GRAVITY_CENTER); assert_coords (state, 100 - RECT_WIDTH, 200 - RECT_HEIGHT, 100 + RECT_WIDTH, 200 + RECT_HEIGHT); assert_notifications (0); clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); /* Change the anchor point using units again to assert that the gravity property changes */ clutter_actor_set_anchor_point (rect, 20, 30); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == 20); g_assert (anchor_y == 30); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); assert_position (state, 80, 170); assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y | NOTIFY_ANCHOR_GRAVITY); /* Verify that the anchor point doesn't move if the actor changes size when it is set from units */ clutter_actor_set_size (rect, RECT_WIDTH * 2, RECT_HEIGHT * 2); g_object_get (rect, "anchor-x", &anchor_x, "anchor-y", &anchor_y, "anchor-gravity", &anchor_gravity, NULL); g_assert (anchor_x == 20); g_assert (anchor_y == 30); g_assert (anchor_gravity == CLUTTER_GRAVITY_NONE); assert_coords (state, 80, 170, 80 + RECT_WIDTH * 2, 170 + RECT_HEIGHT * 2); assert_notifications (0); clutter_actor_set_size (rect, RECT_WIDTH, RECT_HEIGHT); /* Put the anchor back */ clutter_actor_set_anchor_point_from_gravity (rect, CLUTTER_GRAVITY_NONE); assert_notifications (NOTIFY_ANCHOR_X | NOTIFY_ANCHOR_Y); } static void test_scale_center (TestState *state) { ClutterActor *rect = state->rect; gdouble scale_x, scale_y; gfloat center_x, center_y; ClutterGravity gravity; int i; /* Assert the default settings */ g_assert (clutter_actor_get_x (rect) == 100); g_assert (clutter_actor_get_y (rect) == 200); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, "scale-x", &scale_x, "scale-y", &scale_y, "scale-gravity", &gravity, NULL); g_assert (center_x == 0); g_assert (center_y == 0); g_assert (scale_x == 1.0); g_assert (scale_y == 1.0); g_assert (gravity == CLUTTER_GRAVITY_NONE); /* Try changing the scale without affecting the center */ g_object_set (rect, "scale-x", 2.0, "scale-y", 3.0, NULL); g_assert (clutter_actor_get_x (rect) == 100); g_assert (clutter_actor_get_y (rect) == 200); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, "scale-x", &scale_x, "scale-y", &scale_y, "scale-gravity", &gravity, NULL); g_assert (center_x == 0); g_assert (center_y == 0); g_assert (scale_x == 2.0); g_assert (scale_y == 3.0); g_assert (gravity == CLUTTER_GRAVITY_NONE); assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y); assert_coords (state, 100, 200, 100 + RECT_WIDTH * 2, 200 + RECT_HEIGHT * 3); /* Change the scale and center */ g_object_set (rect, "scale-x", 4.0, "scale-y", 2.0, "scale-center-x", 10.0, "scale-center-y", 20.0, NULL); g_assert (clutter_actor_get_x (rect) == 100); g_assert (clutter_actor_get_y (rect) == 200); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, "scale-x", &scale_x, "scale-y", &scale_y, "scale-gravity", &gravity, NULL); g_assert (center_x == 10); g_assert (center_y == 20); g_assert (scale_x == 4.0); g_assert (scale_y == 2.0); g_assert (gravity == CLUTTER_GRAVITY_NONE); assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y); assert_coords (state, 100 + 10 - 10 * 4, 200 + 20 - 20 * 2, 100 + 10 + (RECT_WIDTH - 10) * 4, 200 + 20 + (RECT_HEIGHT - 20) * 2); /* Change the anchor point with each of the gravities */ for (i = 0; i < G_N_ELEMENTS (gravities); i++) { if (g_test_verbose ()) { GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); GEnumValue *value = g_enum_get_value (gravity_class, gravities[i].gravity); g_print ("Setting scale center to %s\n", value ? value->value_name : "?"); g_type_class_unref (gravity_class); } g_object_set (rect, "scale-gravity", gravities[i].gravity, NULL); g_assert (clutter_actor_get_x (rect) == 100); g_assert (clutter_actor_get_y (rect) == 200); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, "scale-x", &scale_x, "scale-y", &scale_y, "scale-gravity", &gravity, NULL); g_assert (center_x == gravities[i].x_pos); g_assert (center_y == gravities[i].y_pos); g_assert (scale_x == 4.0); g_assert (scale_y == 2.0); g_assert (gravity == gravities[i].gravity); assert_notifications (NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y | NOTIFY_SCALE_GRAVITY); assert_coords (state, 100 - gravities[i].x_pos * 3, 200 - gravities[i].y_pos, 100 + (gravities[i].x_pos + (RECT_WIDTH - gravities[i].x_pos) * 4), 200 + (gravities[i].y_pos + (RECT_HEIGHT - gravities[i].y_pos) * 2)); } /* Change the scale center using units again to assert that the gravity property changes */ clutter_actor_set_scale_full (rect, 4, 2, 10, 20); g_object_get (rect, "scale-center-x", ¢er_x, "scale-center-y", ¢er_y, "scale-x", &scale_x, "scale-y", &scale_y, "scale-gravity", &gravity, NULL); g_assert (center_x == 10); g_assert (center_y == 20); g_assert (scale_x == 4.0); g_assert (scale_y == 2.0); g_assert (gravity == CLUTTER_GRAVITY_NONE); assert_notifications (NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y | NOTIFY_SCALE_GRAVITY); assert_coords (state, 100 + 10 - 10 * 4, 200 + 20 - 20 * 2, 100 + 10 + (RECT_WIDTH - 10) * 4, 200 + 20 + (RECT_HEIGHT - 20) * 2); /* Put the scale back to normal */ clutter_actor_set_scale_full (rect, 1, 1, 0, 0); assert_notifications (NOTIFY_SCALE_X | NOTIFY_SCALE_Y | NOTIFY_SCALE_CENTER_X | NOTIFY_SCALE_CENTER_Y); } static void test_rotate_center (TestState *state) { ClutterActor *rect = state->rect; gdouble angle_x, angle_y, angle_z; graphene_point3d_t *center_x, *center_y, *center_z; ClutterGravity z_center_gravity; gfloat stage_width, stage_height; gfloat rect_x, rect_y; int i; /* Position the rectangle at the center of the stage so that rotations by 90° along the X or Y axis will cause the actor to be appear as a flat line. This makes verifying the transformations easier */ clutter_actor_get_size (clutter_actor_get_stage (rect), &stage_width, &stage_height); rect_x = stage_width / 2; rect_y = stage_height / 2; clutter_actor_set_position (rect, rect_x, rect_y); /* Assert the default settings */ g_assert_cmpfloat (clutter_actor_get_x (rect), ==, rect_x); g_assert_cmpfloat (clutter_actor_get_y (rect), ==, rect_y); g_assert_cmpfloat (clutter_actor_get_width (rect), ==, RECT_WIDTH); g_assert_cmpfloat (clutter_actor_get_height (rect), ==, RECT_HEIGHT); g_object_get (rect, "rotation-angle-x", &angle_x, "rotation-angle-y", &angle_y, "rotation-angle-z", &angle_z, "rotation-center-x", ¢er_x, "rotation-center-y", ¢er_y, "rotation-center-z", ¢er_z, "rotation-center-z-gravity", &z_center_gravity, NULL); g_assert (angle_x == 0.0); g_assert (angle_y == 0.0); g_assert (angle_z == 0.0); assert_vertex_and_free (center_x, 0, 0, 0); assert_vertex_and_free (center_y, 0, 0, 0); assert_vertex_and_free (center_z, 0, 0, 0); g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); /* Change each of the rotation angles without affecting the center point */ for (i = CLUTTER_X_AXIS; i <= CLUTTER_Z_AXIS; i++) { char prop_name[] = "rotation-angle- "; prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x'; if (g_test_verbose ()) g_print ("Setting %s to 90 degrees\n", prop_name); g_object_set (rect, prop_name, 90.0, NULL); assert_notifications (NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)); g_assert (clutter_actor_get_x (rect) == rect_x); g_assert (clutter_actor_get_y (rect) == rect_y); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "rotation-angle-x", &angle_x, "rotation-angle-y", &angle_y, "rotation-angle-z", &angle_z, "rotation-center-x", ¢er_x, "rotation-center-y", ¢er_y, "rotation-center-z", ¢er_z, "rotation-center-z-gravity", &z_center_gravity, NULL); if (i == CLUTTER_X_AXIS) { g_assert (angle_x == 90.0); assert_coords (state, rect_x, rect_y, verts[3].x, rect_y); } else g_assert (angle_x == 0.0); if (i == CLUTTER_Y_AXIS) { g_assert (angle_y == 90.0); assert_coords (state, rect_x, rect_y, rect_x, verts[3].y); } else g_assert (angle_y == 0.0); if (i == CLUTTER_Z_AXIS) { g_assert (angle_z == 90.0); assert_coords (state, rect_x, rect_y, rect_x - RECT_HEIGHT, rect_y + RECT_WIDTH); } else g_assert (angle_z == 0.0); assert_vertex_and_free (center_x, 0, 0, 0); assert_vertex_and_free (center_y, 0, 0, 0); assert_vertex_and_free (center_z, 0, 0, 0); g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); g_object_set (rect, prop_name, 0.0, NULL); assert_notifications (NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)); } clutter_actor_set_position (rect, rect_x -= 10, rect_y -= 20); /* Same test but also change the center position */ for (i = CLUTTER_X_AXIS; i <= CLUTTER_Z_AXIS; i++) { char prop_name[] = "rotation-angle- "; prop_name[sizeof (prop_name) - 2] = i - CLUTTER_X_AXIS + 'x'; if (g_test_verbose ()) g_print ("Setting %s to 90 degrees with center 10,20,0\n", prop_name); clutter_actor_set_rotation (rect, i, 90.0, 10, 20, 0); assert_notifications ((NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)) | (NOTIFY_ROTATION_CENTER_X << (i - CLUTTER_X_AXIS))); g_assert (clutter_actor_get_x (rect) == rect_x); g_assert (clutter_actor_get_y (rect) == rect_y); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "rotation-angle-x", &angle_x, "rotation-angle-y", &angle_y, "rotation-angle-z", &angle_z, "rotation-center-x", ¢er_x, "rotation-center-y", ¢er_y, "rotation-center-z", ¢er_z, "rotation-center-z-gravity", &z_center_gravity, NULL); if (i == CLUTTER_X_AXIS) { g_assert (angle_x == 90.0); assert_coords (state, verts[0].x, rect_y + 20, verts[3].x, rect_y + 20); assert_vertex_and_free (center_x, 10, 20, 0); } else { g_assert (angle_x == 0.0); assert_vertex_and_free (center_x, 0, 0, 0); } if (i == CLUTTER_Y_AXIS) { g_assert (angle_y == 90.0); assert_coords (state, rect_x + 10, verts[0].y, rect_x + 10, verts[3].y); assert_vertex_and_free (center_y, 10, 20, 0); } else { g_assert (angle_y == 0.0); assert_vertex_and_free (center_y, 0, 0, 0); } if (i == CLUTTER_Z_AXIS) { g_assert (angle_z == 90.0); assert_coords (state, rect_x + 10 + 20, rect_y + 20 - 10, rect_x + 10 + 20 - RECT_HEIGHT, rect_y + 20 + RECT_WIDTH - 10); assert_vertex_and_free (center_z, 10, 20, 0); } else { g_assert (angle_z == 0.0); assert_vertex_and_free (center_z, 0, 0, 0); } g_assert (z_center_gravity == CLUTTER_GRAVITY_NONE); clutter_actor_set_rotation (rect, i, 0, 0, 0, 0); assert_notifications ((NOTIFY_ROTATION_ANGLE_X << (i - CLUTTER_X_AXIS)) | (NOTIFY_ROTATION_CENTER_X << (i - CLUTTER_X_AXIS))); } /* Try rotating the z with all of the gravities */ for (i = 0; i < G_N_ELEMENTS (gravities); i++) { if (g_test_verbose ()) { GEnumClass *gravity_class = g_type_class_ref (CLUTTER_TYPE_GRAVITY); GEnumValue *value = g_enum_get_value (gravity_class, gravities[i].gravity); g_print ("Setting z rotation to 90 degrees with center at %s\n", value ? value->value_name : "?"); g_type_class_unref (gravity_class); } clutter_actor_set_z_rotation_from_gravity (rect, 90, gravities[i].gravity); assert_notifications (NOTIFY_ROTATION_ANGLE_Z | NOTIFY_ROTATION_CENTER_Z | NOTIFY_ROTATION_CENTER_Z_GRAVITY); g_assert (clutter_actor_get_x (rect) == rect_x); g_assert (clutter_actor_get_y (rect) == rect_y); g_assert (clutter_actor_get_width (rect) == RECT_WIDTH); g_assert (clutter_actor_get_height (rect) == RECT_HEIGHT); g_object_get (rect, "rotation-angle-x", &angle_x, "rotation-angle-y", &angle_y, "rotation-angle-z", &angle_z, "rotation-center-x", ¢er_x, "rotation-center-y", ¢er_y, "rotation-center-z", ¢er_z, "rotation-center-z-gravity", &z_center_gravity, NULL); g_assert (angle_x == 0.0); g_assert (angle_y == 0.0); g_assert (angle_z == 90.0); assert_vertex_and_free (center_x, 0, 0, 0); assert_vertex_and_free (center_y, 0, 0, 0); assert_vertex_and_free (center_z, gravities[i].x_pos, gravities[i].y_pos, 0); assert_coords (state, rect_x + gravities[i].x_pos + gravities[i].y_pos, rect_y + gravities[i].y_pos - gravities[i].x_pos, rect_x + gravities[i].x_pos + gravities[i].y_pos - RECT_HEIGHT, rect_y + gravities[i].y_pos + RECT_WIDTH - gravities[i].x_pos); g_assert (z_center_gravity == gravities[i].gravity); g_assert (clutter_actor_get_z_rotation_gravity (rect) == gravities[i].gravity); /* Put the rotation back */ clutter_actor_set_z_rotation_from_gravity (rect, 0, CLUTTER_GRAVITY_NONE); assert_notifications (NOTIFY_ROTATION_ANGLE_Z | NOTIFY_ROTATION_CENTER_Z | NOTIFY_ROTATION_CENTER_Z_GRAVITY); } } static gboolean idle_cb (gpointer data) { test_anchor_point (data); test_scale_center (data); test_rotate_center (data); clutter_main_quit (); return G_SOURCE_REMOVE; } static void actor_anchors (void) { TestState state; ClutterActor *stage; stage = clutter_test_get_stage (); state.rect = clutter_actor_new (); clutter_actor_add_child (stage, state.rect); clutter_actor_set_position (state.rect, 100, 200); clutter_actor_set_size (state.rect, RECT_WIDTH, RECT_HEIGHT); /* Record notifications on the actor properties */ state.notifications = 0; g_signal_connect (state.rect, "notify", G_CALLBACK (notify_cb), &state); /* Run the tests in a low priority idle function so that we can be sure the stage is correctly setup */ clutter_threads_add_idle_full (G_PRIORITY_LOW, idle_cb, &state, NULL); clutter_actor_show (stage); clutter_main (); } static void actor_pivot (void) { ClutterActor *stage, *actor_implicit, *actor_explicit; ClutterMatrix transform, result_implicit, result_explicit; ClutterActorBox allocation = CLUTTER_ACTOR_BOX_INIT (0, 0, 90, 30); gfloat angle = 30; stage = clutter_test_get_stage (); actor_implicit = clutter_actor_new (); actor_explicit = clutter_actor_new (); clutter_actor_add_child (stage, actor_implicit); clutter_actor_add_child (stage, actor_explicit); /* Fake allocation or pivot-point will not have any effect */ clutter_actor_allocate (actor_implicit, &allocation, CLUTTER_ALLOCATION_NONE); clutter_actor_allocate (actor_explicit, &allocation, CLUTTER_ALLOCATION_NONE); clutter_actor_set_pivot_point (actor_implicit, 0.5, 0.5); clutter_actor_set_pivot_point (actor_explicit, 0.5, 0.5); /* Implict transformation */ clutter_actor_set_rotation_angle (actor_implicit, CLUTTER_Z_AXIS, angle); /* Explict transformation */ clutter_matrix_init_identity(&transform); cogl_matrix_rotate (&transform, angle, 0, 0, 1.0); clutter_actor_set_transform (actor_explicit, &transform); clutter_actor_get_transform (actor_implicit, &result_implicit); clutter_actor_get_transform (actor_explicit, &result_explicit); g_assert (cogl_matrix_equal (&result_implicit, &result_explicit)); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/transforms/anchor-point", actor_anchors) CLUTTER_TEST_UNIT ("/actor/transforms/pivot-point", actor_pivot) ) muffin-6.4.1/src/tests/clutter/conform/binding-pool.c0000664000175000017500000002256414723361714021556 0ustar fabiofabio#include #include #include "tests/clutter-test-utils.h" #define TYPE_KEY_GROUP (key_group_get_type ()) #define KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_KEY_GROUP, KeyGroup)) #define IS_KEY_GROUP(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_KEY_GROUP)) #define KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_KEY_GROUP, KeyGroupClass)) #define IS_KEY_GROUP_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_KEY_GROUP)) typedef struct _KeyGroup KeyGroup; typedef struct _KeyGroupClass KeyGroupClass; struct _KeyGroup { ClutterActor parent_instance; gint selected_index; }; struct _KeyGroupClass { ClutterActorClass parent_class; void (* activate) (KeyGroup *group, ClutterActor *child); }; GType key_group_get_type (void); G_DEFINE_TYPE (KeyGroup, key_group, CLUTTER_TYPE_ACTOR) enum { ACTIVATE, LAST_SIGNAL }; static guint group_signals[LAST_SIGNAL] = { 0, }; static gboolean key_group_action_move_left (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { gint n_children; g_assert_cmpstr (action_name, ==, "move-left"); g_assert_cmpint (key_val, ==, CLUTTER_KEY_Left); n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self)); self->selected_index -= 1; if (self->selected_index < 0) self->selected_index = n_children - 1; return TRUE; } static gboolean key_group_action_move_right (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { gint n_children; g_assert_cmpstr (action_name, ==, "move-right"); g_assert_cmpint (key_val, ==, CLUTTER_KEY_Right); n_children = clutter_actor_get_n_children (CLUTTER_ACTOR (self)); self->selected_index += 1; if (self->selected_index >= n_children) self->selected_index = 0; return TRUE; } static gboolean key_group_action_activate (KeyGroup *self, const gchar *action_name, guint key_val, ClutterModifierType modifiers) { ClutterActor *child = NULL; g_assert_cmpstr (action_name, ==, "activate"); g_assert (key_val == CLUTTER_KEY_Return || key_val == CLUTTER_KEY_KP_Enter || key_val == CLUTTER_KEY_ISO_Enter); if (self->selected_index == -1) return FALSE; child = clutter_actor_get_child_at_index (CLUTTER_ACTOR (self), self->selected_index); if (child != NULL) { g_signal_emit (self, group_signals[ACTIVATE], 0, child); return TRUE; } else return FALSE; } static gboolean key_group_key_press (ClutterActor *actor, ClutterKeyEvent *event) { ClutterBindingPool *pool; gboolean res; pool = clutter_binding_pool_find (G_OBJECT_TYPE_NAME (actor)); g_assert (pool != NULL); res = clutter_binding_pool_activate (pool, event->keyval, event->modifier_state, G_OBJECT (actor)); /* if we activate a key binding, redraw the actor */ if (res) clutter_actor_queue_redraw (actor); return res; } static void key_group_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { KeyGroup *self = KEY_GROUP (actor); CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); ClutterActorIter iter; ClutterActor *child; CoglPipeline *pipeline; CoglFramebuffer *framebuffer; gint i = 0; pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_color4ub (pipeline, 255, 255, 0, 224); framebuffer = clutter_paint_context_get_framebuffer (paint_context); clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { /* paint the selection rectangle */ if (i == self->selected_index) { ClutterActorBox box = { 0, }; clutter_actor_get_allocation_box (child, &box); box.x1 -= 2; box.y1 -= 2; box.x2 += 2; box.y2 += 2; cogl_framebuffer_draw_rectangle (framebuffer, pipeline, box.x1, box.y1, box.x2, box.y2); } clutter_actor_paint (child, paint_context); } cogl_object_unref (pipeline); } static void key_group_class_init (KeyGroupClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); ClutterBindingPool *binding_pool; actor_class->paint = key_group_paint; actor_class->key_press_event = key_group_key_press; group_signals[ACTIVATE] = g_signal_new (g_intern_static_string ("activate"), G_OBJECT_CLASS_TYPE (gobject_class), G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (KeyGroupClass, activate), NULL, NULL, g_cclosure_marshal_VOID__OBJECT, G_TYPE_NONE, 1, CLUTTER_TYPE_ACTOR); binding_pool = clutter_binding_pool_get_for_class (klass); clutter_binding_pool_install_action (binding_pool, "move-right", CLUTTER_KEY_Right, 0, G_CALLBACK (key_group_action_move_right), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "move-left", CLUTTER_KEY_Left, 0, G_CALLBACK (key_group_action_move_left), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_Return, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_KP_Enter, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); clutter_binding_pool_install_action (binding_pool, "activate", CLUTTER_KEY_ISO_Enter, 0, G_CALLBACK (key_group_action_activate), NULL, NULL); } static void key_group_init (KeyGroup *self) { self->selected_index = -1; } static void init_event (ClutterKeyEvent *event) { event->type = CLUTTER_KEY_PRESS; event->time = 0; /* not needed */ event->flags = CLUTTER_EVENT_FLAG_SYNTHETIC; event->stage = NULL; /* not needed */ event->source = NULL; /* not needed */ event->modifier_state = 0; event->hardware_keycode = 0; /* not needed */ } static void send_keyval (KeyGroup *group, int keyval) { ClutterKeyEvent event; init_event (&event); event.keyval = keyval; event.unicode_value = 0; /* should be ignored for cursor keys etc. */ clutter_actor_event (CLUTTER_ACTOR (group), (ClutterEvent *) &event, FALSE); } static void on_activate (KeyGroup *key_group, ClutterActor *child, gpointer data) { gint _index = GPOINTER_TO_INT (data); g_assert_cmpint (key_group->selected_index, ==, _index); } static void binding_pool (void) { KeyGroup *key_group = g_object_new (TYPE_KEY_GROUP, NULL); g_object_ref_sink (key_group); clutter_actor_add_child (CLUTTER_ACTOR (key_group), g_object_new (CLUTTER_TYPE_ACTOR, "width", 50.0, "height", 50.0, "x", 0.0, "y", 0.0, NULL)); clutter_actor_add_child (CLUTTER_ACTOR (key_group), g_object_new (CLUTTER_TYPE_ACTOR, "width", 50.0, "height", 50.0, "x", 75.0, "y", 0.0, NULL)); clutter_actor_add_child (CLUTTER_ACTOR (key_group), g_object_new (CLUTTER_TYPE_ACTOR, "width", 50.0, "height", 50.0, "x", 150.0, "y", 0.0, NULL)); g_assert_cmpint (key_group->selected_index, ==, -1); send_keyval (key_group, CLUTTER_KEY_Left); g_assert_cmpint (key_group->selected_index, ==, 2); send_keyval (key_group, CLUTTER_KEY_Left); g_assert_cmpint (key_group->selected_index, ==, 1); send_keyval (key_group, CLUTTER_KEY_Right); g_assert_cmpint (key_group->selected_index, ==, 2); send_keyval (key_group, CLUTTER_KEY_Right); g_assert_cmpint (key_group->selected_index, ==, 0); g_signal_connect (key_group, "activate", G_CALLBACK (on_activate), GINT_TO_POINTER (0)); send_keyval (key_group, CLUTTER_KEY_Return); clutter_actor_destroy (CLUTTER_ACTOR (key_group)); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/binding-pool", binding_pool) ) muffin-6.4.1/src/tests/clutter/conform/actor-iter.c0000664000175000017500000001241214723361714021235 0ustar fabiofabio#include #include #include "tests/clutter-test-utils.h" static void actor_iter_traverse_children (void) { ClutterActorIter iter; ClutterActor *actor; ClutterActor *child; int i, n_actors; actor = clutter_actor_new (); clutter_actor_set_name (actor, "root"); g_object_ref_sink (actor); n_actors = g_random_int_range (10, 50); for (i = 0; i < n_actors; i++) { char *name; name = g_strdup_printf ("actor%d", i); child = clutter_actor_new (); clutter_actor_set_name (child, name); clutter_actor_add_child (actor, child); g_free (name); } g_assert_cmpint (clutter_actor_get_n_children (actor), ==, n_actors); i = 0; clutter_actor_iter_init (&iter, actor); g_assert (clutter_actor_iter_is_valid (&iter)); while (clutter_actor_iter_next (&iter, &child)) { g_assert (CLUTTER_IS_ACTOR (child)); g_assert (clutter_actor_get_parent (child) == actor); if (g_test_verbose ()) g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child)); if (i == 0) g_assert (child == clutter_actor_get_first_child (actor)); if (i == (n_actors - 1)) g_assert (child == clutter_actor_get_last_child (actor)); i += 1; } g_assert_cmpint (i, ==, n_actors); i = 0; clutter_actor_iter_init (&iter, actor); g_assert (clutter_actor_iter_is_valid (&iter)); while (clutter_actor_iter_prev (&iter, &child)) { g_assert (CLUTTER_IS_ACTOR (child)); g_assert (clutter_actor_get_parent (child) == actor); if (g_test_verbose ()) g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child)); if (i == 0) g_assert (child == clutter_actor_get_last_child (actor)); if (i == (n_actors - 1)) g_assert (child == clutter_actor_get_first_child (actor)); i += 1; } g_object_unref (actor); } static void actor_iter_traverse_remove (void) { ClutterActorIter iter; ClutterActor *actor; ClutterActor *child; int i, n_actors; actor = clutter_actor_new (); clutter_actor_set_name (actor, "root"); g_object_ref_sink (actor); n_actors = g_random_int_range (10, 50); for (i = 0; i < n_actors; i++) { char *name; name = g_strdup_printf ("actor%d", i); child = clutter_actor_new (); clutter_actor_set_name (child, name); clutter_actor_add_child (actor, child); g_free (name); } g_assert_cmpint (clutter_actor_get_n_children (actor), ==, n_actors); i = 0; clutter_actor_iter_init (&iter, actor); g_assert (clutter_actor_iter_is_valid (&iter)); while (clutter_actor_iter_next (&iter, &child)) { g_assert (CLUTTER_IS_ACTOR (child)); g_assert (clutter_actor_get_parent (child) == actor); if (g_test_verbose ()) g_print ("actor %d = '%s'\n", i, clutter_actor_get_name (child)); if (i == 0) g_assert (child == clutter_actor_get_first_child (actor)); if (i == (n_actors - 1)) g_assert (child == clutter_actor_get_last_child (actor)); clutter_actor_iter_remove (&iter); g_assert (clutter_actor_iter_is_valid (&iter)); i += 1; } g_assert_cmpint (i, ==, n_actors); g_assert_cmpint (0, ==, clutter_actor_get_n_children (actor)); } static void actor_iter_assignment (void) { ClutterActorIter iter_a, iter_b; ClutterActor *actor; ClutterActor *child; int i, n_actors; actor = clutter_actor_new (); clutter_actor_set_name (actor, "root"); g_object_ref_sink (actor); n_actors = g_random_int_range (10, 50); for (i = 0; i < n_actors; i++) { char *name; name = g_strdup_printf ("actor[%02d]", i); child = clutter_actor_new (); clutter_actor_set_name (child, name); clutter_actor_add_child (actor, child); g_free (name); } g_assert_cmpint (clutter_actor_get_n_children (actor), ==, n_actors); i = 0; clutter_actor_iter_init (&iter_a, actor); iter_b = iter_a; g_assert (clutter_actor_iter_is_valid (&iter_a)); g_assert (clutter_actor_iter_is_valid (&iter_b)); while (clutter_actor_iter_next (&iter_a, &child)) { g_assert (CLUTTER_IS_ACTOR (child)); g_assert (clutter_actor_get_parent (child) == actor); if (g_test_verbose ()) g_print ("actor %2d = '%s'\n", i, clutter_actor_get_name (child)); if (i == 0) g_assert (child == clutter_actor_get_first_child (actor)); if (i == (n_actors - 1)) g_assert (child == clutter_actor_get_last_child (actor)); i += 1; } g_assert_cmpint (i, ==, n_actors); i = n_actors - 1; while (clutter_actor_iter_prev (&iter_b, &child)) { g_assert (clutter_actor_get_parent (child) == actor); if (g_test_verbose ()) g_print ("actor %2d = '%s'\n", i, clutter_actor_get_name (child)); if (i == n_actors - 1) g_assert (child == clutter_actor_get_last_child (actor)); if (i == 0) g_assert (child == clutter_actor_get_first_child (actor)); i -= 1; } g_assert_cmpint (i, ==, -1); g_object_unref (actor); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/iter/traverse-children", actor_iter_traverse_children) CLUTTER_TEST_UNIT ("/actor/iter/traverse-remove", actor_iter_traverse_remove) CLUTTER_TEST_UNIT ("/actor/iter/assignment", actor_iter_assignment) ) muffin-6.4.1/src/tests/clutter/conform/actor-layout.c0000664000175000017500000000626614723361714021621 0ustar fabiofabio#include #include "tests/clutter-test-utils.h" static void actor_basic_layout (void) { ClutterActor *stage = clutter_test_get_stage (); ClutterActor *vase; ClutterActor *flower[3]; graphene_point_t p; vase = clutter_actor_new (); clutter_actor_set_name (vase, "Vase"); clutter_actor_set_layout_manager (vase, clutter_flow_layout_new (CLUTTER_FLOW_HORIZONTAL)); clutter_actor_add_child (stage, vase); flower[0] = clutter_actor_new (); clutter_actor_set_background_color (flower[0], CLUTTER_COLOR_Red); clutter_actor_set_size (flower[0], 100, 100); clutter_actor_set_name (flower[0], "Red Flower"); clutter_actor_add_child (vase, flower[0]); flower[1] = clutter_actor_new (); clutter_actor_set_background_color (flower[1], CLUTTER_COLOR_Yellow); clutter_actor_set_size (flower[1], 100, 100); clutter_actor_set_name (flower[1], "Yellow Flower"); clutter_actor_add_child (vase, flower[1]); flower[2] = clutter_actor_new (); clutter_actor_set_background_color (flower[2], CLUTTER_COLOR_Green); clutter_actor_set_size (flower[2], 100, 100); clutter_actor_set_name (flower[2], "Green Flower"); clutter_actor_add_child (vase, flower[2]); graphene_point_init (&p, 50, 50); clutter_test_assert_actor_at_point (stage, &p, flower[0]); graphene_point_init (&p, 150, 50); clutter_test_assert_actor_at_point (stage, &p, flower[1]); graphene_point_init (&p, 250, 50); clutter_test_assert_actor_at_point (stage, &p, flower[2]); } static void actor_margin_layout (void) { ClutterActor *stage = clutter_test_get_stage (); ClutterActor *vase; ClutterActor *flower[3]; graphene_point_t p; vase = clutter_actor_new (); clutter_actor_set_name (vase, "Vase"); clutter_actor_set_layout_manager (vase, clutter_box_layout_new ()); clutter_actor_add_child (stage, vase); flower[0] = clutter_actor_new (); clutter_actor_set_background_color (flower[0], CLUTTER_COLOR_Red); clutter_actor_set_size (flower[0], 100, 100); clutter_actor_set_name (flower[0], "Red Flower"); clutter_actor_add_child (vase, flower[0]); flower[1] = clutter_actor_new (); clutter_actor_set_background_color (flower[1], CLUTTER_COLOR_Yellow); clutter_actor_set_size (flower[1], 100, 100); clutter_actor_set_name (flower[1], "Yellow Flower"); clutter_actor_set_margin_right (flower[1], 6); clutter_actor_set_margin_left (flower[1], 6); clutter_actor_add_child (vase, flower[1]); flower[2] = clutter_actor_new (); clutter_actor_set_background_color (flower[2], CLUTTER_COLOR_Green); clutter_actor_set_size (flower[2], 100, 100); clutter_actor_set_name (flower[2], "Green Flower"); clutter_actor_set_margin_top (flower[2], 6); clutter_actor_set_margin_bottom (flower[2], 6); clutter_actor_add_child (vase, flower[2]); graphene_point_init (&p, 0, 7); clutter_test_assert_actor_at_point (stage, &p, flower[0]); graphene_point_init (&p, 106, 50); clutter_test_assert_actor_at_point (stage, &p, flower[1]); graphene_point_init (&p, 212, 7); clutter_test_assert_actor_at_point (stage, &p, flower[2]); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/layout/basic", actor_basic_layout) CLUTTER_TEST_UNIT ("/actor/layout/margin", actor_margin_layout) ) muffin-6.4.1/src/tests/clutter/conform/actor-clone.c0000664000175000017500000000330714723361714021375 0ustar fabiofabio#include #include #include #include "tests/clutter-test-utils.h" static void on_presented (ClutterStage *stage, CoglFrameEvent *frame_event, ClutterFrameInfo *frame_info, gboolean *was_presented) { *was_presented = TRUE; } static void actor_clone_unmapped (void) { ClutterActor *container; ClutterActor *actor; ClutterActor *clone; ClutterActor *stage; gboolean was_presented; stage = clutter_test_get_stage (); container = clutter_actor_new (); g_object_ref_sink (container); g_object_add_weak_pointer (G_OBJECT (container), (gpointer *) &container); actor = clutter_actor_new (); g_object_ref_sink (actor); g_object_add_weak_pointer (G_OBJECT (actor), (gpointer *) &actor); clone = clutter_clone_new (actor); g_object_ref_sink (clone); g_object_add_weak_pointer (G_OBJECT (clone), (gpointer *) &clone); clutter_actor_hide (container); clutter_actor_hide (actor); clutter_actor_add_child (stage, container); clutter_actor_add_child (container, actor); clutter_actor_add_child (stage, clone); clutter_actor_set_offscreen_redirect (actor, CLUTTER_OFFSCREEN_REDIRECT_ALWAYS); g_signal_connect (stage, "presented", G_CALLBACK (on_presented), &was_presented); clutter_actor_show (stage); was_presented = FALSE; while (!was_presented) g_main_context_iteration (NULL, FALSE); clutter_actor_destroy (clone); clutter_actor_destroy (actor); clutter_actor_destroy (container); g_assert_null (clone); g_assert_null (actor); g_assert_null (container); } CLUTTER_TEST_SUITE ( CLUTTER_TEST_UNIT ("/actor/clone/unmapped", actor_clone_unmapped) ) muffin-6.4.1/src/tests/clutter/clutter-1.0.suppressions0000664000175000017500000000366514723361714022044 0ustar fabiofabio{ ioctl_1 Memcheck:Param ioctl(generic) fun:ioctl fun:driDrawableInitVBlank fun:intelMakeCurrent fun:glXMakeContextCurrent } { ioctl_2 Memcheck:Param ioctl(generic) fun:ioctl fun:driDrawableGetMSC32 fun:clutter_backend_glx_redraw } { ioctl_3 Memcheck:Param ioctl(generic) fun:ioctl fun:driWaitForMSC32 fun:clutter_backend_glx_redraw } { mesa_init_context Memcheck:Leak fun:*alloc ... fun:glXCreateNewContext } { type_register Memcheck:Leak fun:*alloc ... fun:g_type_register_* } { type_ref Memcheck:Leak fun:*alloc ... fun:g_type_class_ref } { type_interface_prereq Memcheck:Leak fun:*alloc ... fun:g_type_interface_add_prerequisite } { get_charset Memcheck:Leak fun:*alloc ... fun:g_get_charset } { glx_query_version Memcheck:Leak fun:*alloc ... fun:glXQueryVersion } { glx_create_context Memcheck:Leak fun:*alloc ... fun:glXCreateNewContext } { glx_make_current Memcheck:Leak fun:*alloc ... fun:glXMakeContextCurrent } { gl_draw_arrays Memcheck:Leak fun:*malloc ... fun:glDrawArrays } { cogl_clear Memcheck:Leak fun:*alloc ... fun:cogl_clear } { default_font Memcheck:Leak fun:*alloc ... fun:clutter_backend_get_font_name } { id_pool Memcheck:Leak fun:*alloc ... fun:clutter_id_pool_new } { x_open_display Memcheck:Leak fun:*alloc ... fun:XOpenDisplay } # ... and font descriptions from every "sans 12" type string { pango_font_description_from_string Memcheck:Leak fun:*alloc ... fun:pango_font_description_from_string } # other lib init { fontconfig_init Memcheck:Leak fun:*alloc ... fun:FcConfigParseAndLoad } { freetype_init Memcheck:Leak fun:*alloc ... fun:FT_Open_Face } { x_init_ext Memcheck:Leak fun:*alloc ... fun:XInitExtension } muffin-6.4.1/src/tests/clutter/accessibility/0000775000175000017500000000000014723361714020204 5ustar fabiofabiomuffin-6.4.1/src/tests/clutter/accessibility/meson.build0000664000175000017500000000165114723361714022351 0ustar fabiofabioclutter_test_accessibility_common_sources = [ 'cally-examples-util.c', 'cally-examples-util.h', ] clutter_test_accessibility_c_args = [ '-DPREFIXDIR="@0@"'.format(libdir), '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS', '-DGLIB_DISABLE_DEPRECATION_WARNINGS', ] clutter_test_accessibility_c_args += clutter_debug_c_args clutter_accessibility_tests_dependencies = [ clutter_deps, libmutter_clutter_dep, ] clutter_accessibility_tests = [ 'cally-atkcomponent-example', 'cally-atktext-example', 'cally-atkevents-example', 'cally-atkeditabletext-example', 'cally-clone-example', ] foreach test : clutter_accessibility_tests executable(test, sources: [ clutter_test_accessibility_common_sources, test + '.c', ], include_directories: clutter_includes, c_args: clutter_test_accessibility_c_args, dependencies: [clutter_accessibility_tests_dependencies], install: false, ) endforeach muffin-6.4.1/src/tests/clutter/accessibility/cally-atktext-example.c0000664000175000017500000002111514723361714024567 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "cally-examples-util.h" #define WIDTH 800 #define HEIGHT 600 static ClutterActor *text_actor = NULL; static ClutterActor *text_editable_actor = NULL; /* * Test AtkText interface */ static void test_atk_text (ClutterActor *actor) { gchar *text = NULL; AtkObject *object = NULL; AtkText *cally_text = NULL; gboolean boolean = FALSE; gunichar unichar; gint count = -1; gint start = -1; gint end = -1; gint pos = -1; AtkAttributeSet *at_set = NULL; GSList *attrs; gchar *buf; gint x, y, width, height; object = atk_gobject_accessible_for_object (G_OBJECT (actor)); cally_text = ATK_TEXT (object); if (!cally_text) return; text = atk_text_get_text (cally_text, 0, -1); g_print ("atk_text_get_text output: %s\n", text); unichar = atk_text_get_character_at_offset (cally_text, 5); buf = g_ucs4_to_utf8 (&unichar, 1, NULL, NULL, NULL); g_print ("atk_text_get_character_at_offset(5): '%s' vs '%c'\n", buf, text[5]); g_free (text); text = NULL; g_free (buf); buf = NULL; text = atk_text_get_string_at_offset (cally_text, 5, ATK_TEXT_GRANULARITY_WORD, &start, &end); g_print ("atk_text_get_string_at_offset: %s, %i, %i\n", text, start, end); g_free (text); text = NULL; pos = atk_text_get_caret_offset (cally_text); g_print ("atk_text_get_caret_offset: %i\n", pos); atk_text_set_caret_offset (cally_text, 5); count = atk_text_get_character_count (cally_text); g_print ("atk_text_get_character_count: %i\n", count); count = atk_text_get_n_selections (cally_text); g_print ("atk_text_get_n_selections: %i\n", count); text = atk_text_get_selection (cally_text, 0, &start, &end); g_print ("atk_text_get_selection: %s, %i, %i\n", text, start, end); g_free(text); text = NULL; boolean = atk_text_remove_selection (cally_text, 0); g_print ("atk_text_remove_selection (0): %i\n", boolean); boolean = atk_text_remove_selection (cally_text, 1); g_print ("atk_text_remove_selection (1): %i\n", boolean); boolean = atk_text_add_selection (cally_text, 5, 10); g_print ("atk_text_add_selection: %i\n", boolean); boolean = atk_text_set_selection (cally_text, 0, 6, 10); g_print ("atk_text_set_selection: %i\n", boolean); at_set = atk_text_get_run_attributes (cally_text, 0, &start, &end); g_print ("atk_text_get_run_attributes: %i, %i\n", start, end); attrs = (GSList*) at_set; while (attrs) { AtkAttribute *at = (AtkAttribute *) attrs->data; g_print ("text run %s = %s\n", at->name, at->value); attrs = g_slist_next (attrs); } atk_text_get_character_extents (cally_text, 0, &x, &y, &width, &height, ATK_XY_WINDOW); g_print ("atk_text_get_character_extents (0, window): x=%i y=%i width=%i height=%i\n", x, y, width, height); atk_text_get_character_extents (cally_text, 0, &x, &y, &width, &height, ATK_XY_SCREEN); g_print ("atk_text_get_character_extents (0, screen): x=%i y=%i width=%i height=%i\n", x, y, width, height); pos = atk_text_get_offset_at_point (cally_text, 200, 10, ATK_XY_WINDOW); g_print ("atk_text_get_offset_at_point (200, 10, window): %i\n", pos); pos = atk_text_get_offset_at_point (cally_text, 200, 100, ATK_XY_SCREEN); g_print ("atk_text_get_offset_at_point (200, 100, screen): %i\n", pos); } static void dump_actor_default_atk_attributes (ClutterActor *actor) { AtkObject *object = NULL; AtkText *cally_text = NULL; AtkAttributeSet *at_set = NULL; GSList *attrs; const gchar *text_value = NULL; object = atk_gobject_accessible_for_object (G_OBJECT (actor)); cally_text = ATK_TEXT (object); if (!cally_text) return; text_value = clutter_text_get_text (CLUTTER_TEXT (actor)); g_print ("text value = %s\n", text_value); at_set = atk_text_get_default_attributes (cally_text); attrs = (GSList*) at_set; while (attrs) { AtkAttribute *at = (AtkAttribute *) attrs->data; g_print ("text default %s = %s\n", at->name, at->value); attrs = g_slist_next (attrs); } } static gboolean button_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { test_atk_text (text_actor); test_atk_text (text_editable_actor); return TRUE; } static void make_ui (ClutterActor *stage) { ClutterColor color_stage = { 0x00, 0x00, 0x00, 0xff }; ClutterColor color_text = { 0xff, 0x00, 0x00, 0xff }; ClutterColor color_sel = { 0x00, 0xff, 0x00, 0x55 }; ClutterColor color_rect = { 0x00, 0xff, 0xff, 0xff }; ClutterColor color_label = { 0x00, 0x00, 0x00, 0xff }; ClutterActor *button = NULL; ClutterActor *rectangle = NULL; ClutterActor *label = NULL; clutter_stage_set_color (CLUTTER_STAGE (stage), &color_stage); clutter_actor_set_size (stage, WIDTH, HEIGHT); /* text */ text_actor = clutter_text_new_full ("Sans Bold 32px", "", &color_text); clutter_text_set_markup (CLUTTER_TEXT(text_actor), "Lorem ipsum dolor sit amet"); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text_actor); dump_actor_default_atk_attributes (text_actor); /* text_editable */ text_editable_actor = clutter_text_new_full ("Sans Bold 32px", "consectetur adipisicing elit", &color_text); clutter_actor_set_position (text_editable_actor, 20, 100); clutter_text_set_editable (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_text_set_selection_color (CLUTTER_TEXT (text_editable_actor), &color_sel); clutter_text_set_line_wrap (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_actor_grab_key_focus (text_editable_actor); clutter_actor_set_reactive (text_editable_actor, TRUE); dump_actor_default_atk_attributes (text_editable_actor); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text_editable_actor); /* test button */ button = clutter_group_new (); rectangle = clutter_rectangle_new_with_color (&color_rect); clutter_actor_set_size (rectangle, 75, 35); label = clutter_text_new_full ("Sans Bold 32px", "Test", &color_label); clutter_actor_set_position (button, 100, 200); clutter_container_add_actor (CLUTTER_CONTAINER (button), rectangle); clutter_container_add_actor (CLUTTER_CONTAINER (button), label); clutter_actor_set_reactive (button, TRUE); g_signal_connect_after (button, "button-press-event", G_CALLBACK (button_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); } int main (int argc, char *argv[]) { ClutterActor *stage; g_set_application_name ("AtkText"); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; cally_util_a11y_init (&argc, &argv); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cally - AtkText Test"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); make_ui (stage); clutter_actor_show_all (stage); test_atk_text (text_actor); test_atk_text (text_editable_actor); clutter_main (); return 0; } muffin-6.4.1/src/tests/clutter/accessibility/cally-atkevents-example.c0000664000175000017500000001435414723361714025116 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ /* * The purpose of this example is test key event and global event * implementation, specifically: * * atk_add_global_event_listener * atk_remove_global_event_listener * atk_add_key_event_listener * atk_remove_key_event_listener */ #include #include #include #include "cally-examples-util.h" #define WIDTH 800 #define HEIGHT 600 #define HEIGHT_STEP 100 #define NUM_ENTRIES 3 struct _Data{ gint value; }; typedef struct _Data Data; static gboolean atk_key_listener (AtkKeyEventStruct *event, gpointer data) { Data *my_data = (Data*) data; g_print ("atk_listener: 0x%x ", event->keyval); if (my_data != NULL) { g_print ("\t Data value: %i\n", my_data->value); } else { g_print ("\tNo data!!\n"); } return FALSE; } static gboolean window_event_listener (GSignalInvocationHint * signal_hint, guint n_param_values, const GValue * param_values, gpointer data) { AtkObject *accessible; GSignalQuery signal_query; const gchar *name, *s; g_signal_query (signal_hint->signal_id, &signal_query); name = signal_query.signal_name; accessible = ATK_OBJECT (g_value_get_object (¶m_values[0])); s = atk_object_get_name (accessible); g_print ("Detected window event \"%s\" from object \"%p\" named \"%s\"\n", name, accessible, s); return TRUE; } static void make_ui (ClutterActor *stage) { gint i = 0; ClutterActor *editable = NULL; ClutterActor *rectangle = NULL; ClutterActor *label = NULL; ClutterColor color_sel = { 0x00, 0xff, 0x00, 0x55 }; ClutterColor color_label = { 0x00, 0xff, 0x55, 0xff }; ClutterColor color_rect = { 0x00, 0xff, 0xff, 0x55 }; float label_geom_y, editable_geom_y; clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_White); clutter_actor_set_size (stage, WIDTH, HEIGHT); label_geom_y = 50; editable_geom_y = 50; for (i = 0; i < NUM_ENTRIES; i++) { /* label */ label = clutter_text_new_full ("Sans Bold 32px", "Entry", &color_label); clutter_actor_set_position (label, 0, label_geom_y); /* editable */ editable = clutter_text_new_full ("Sans Bold 32px", "ddd", CLUTTER_COLOR_Red); clutter_actor_set_position (editable, 150, editable_geom_y); clutter_actor_set_size (editable, 500, 75); clutter_text_set_editable (CLUTTER_TEXT (editable), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (editable), TRUE); clutter_text_set_selection_color (CLUTTER_TEXT (editable), &color_sel); clutter_actor_grab_key_focus (editable); clutter_actor_set_reactive (editable, TRUE); /* rectangle: to create a entry "feeling" */ rectangle = clutter_rectangle_new_with_color (&color_rect); clutter_actor_set_position (rectangle, 150, editable_geom_y); clutter_actor_set_size (rectangle, 500, 75); clutter_container_add_actor (CLUTTER_CONTAINER (stage), label); clutter_container_add_actor (CLUTTER_CONTAINER (stage), editable); clutter_container_add_actor (CLUTTER_CONTAINER (stage), rectangle); label_geom_y += HEIGHT_STEP; editable_geom_y += HEIGHT_STEP; } } int main (int argc, char *argv[]) { ClutterActor *stage, *stage_main; Data data1, data2, data3; guint id_1 = 0, id_2 = 0, id_3 = 0; g_set_application_name ("AtkText"); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; if (cally_util_a11y_init (&argc, &argv) == FALSE) { g_error ("This example requires the accessibility support, " "especifically AtkUtil implementation loaded, " "as it tries to register and remove event listeners"); } data1.value = 10; data2.value = 20; data3.value = 30; /* key event listeners */ id_1 = atk_add_key_event_listener ((AtkKeySnoopFunc)atk_key_listener, &data1); atk_remove_key_event_listener (id_1); id_2 = atk_add_key_event_listener ((AtkKeySnoopFunc)atk_key_listener, &data2); id_3 = atk_add_key_event_listener ((AtkKeySnoopFunc)atk_key_listener, &data3); atk_remove_key_event_listener (id_2); g_print ("key event listener ids registered: (%i, %i, %i)\n", id_1, id_2, id_3); /* event listeners */ atk_add_global_event_listener (window_event_listener, "Atk:AtkWindow:create"); atk_add_global_event_listener (window_event_listener, "Atk:AtkWindow:destroy"); atk_add_global_event_listener (window_event_listener, "Atk:AtkWindow:activate"); atk_add_global_event_listener (window_event_listener, "Atk:AtkWindow:deactivate"); stage_main = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage_main), "Cally - AtkEvents/1"); g_signal_connect (stage_main, "destroy", G_CALLBACK (clutter_main_quit), NULL); make_ui (stage_main); clutter_actor_show_all (stage_main); if (clutter_feature_available (CLUTTER_FEATURE_STAGE_MULTIPLE)) { stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cally - AtkEvents/2"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); make_ui (stage); clutter_actor_show_all (stage); } clutter_main (); return 0; } muffin-6.4.1/src/tests/clutter/accessibility/cally-atkcomponent-example.c0000664000175000017500000000614214723361714025610 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include "cally-examples-util.h" #define WIDTH 300 #define HEIGHT 300 #define SIZE 50 #define DEPTH -100 int main (int argc, char *argv[]) { ClutterActor *stage = NULL; ClutterActor *button1 = NULL; ClutterActor *button2 = NULL; ClutterActor *button3 = NULL; ClutterActor *button4 = NULL; ClutterActor *group[4]; int i = 0; if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; cally_util_a11y_init (&argc, &argv); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cally - AtkComponent Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_White); clutter_actor_set_size (stage, WIDTH, HEIGHT); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); button1 = clutter_rectangle_new_with_color (CLUTTER_COLOR_Yellow); clutter_actor_set_size (button1, SIZE, SIZE); button2 = clutter_rectangle_new_with_color (CLUTTER_COLOR_Green); clutter_actor_set_position (button2, 2 * SIZE, 0); clutter_actor_set_size (button2, SIZE, SIZE); button3 = clutter_rectangle_new_with_color (CLUTTER_COLOR_Blue); clutter_actor_set_position (button3, 0, 2 * SIZE); clutter_actor_set_size (button3, SIZE, SIZE); clutter_actor_set_depth( button3, DEPTH); /* a nested hierarchy, to check that the relative positions are computed properly */ button4 = clutter_rectangle_new_with_color (CLUTTER_COLOR_Magenta); clutter_actor_set_position (button4, SIZE / 2, SIZE / 2); clutter_actor_set_size (button4, SIZE, SIZE); for (i = 0; i < 4; i++) { group[i] = clutter_group_new (); clutter_actor_set_position (group[i], SIZE / 2, SIZE / 2); clutter_actor_set_size (group[i], SIZE, SIZE); if (i > 0) clutter_container_add_actor (CLUTTER_CONTAINER (group[i]), group [i - 1]); } clutter_container_add_actor (CLUTTER_CONTAINER (stage), button1); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button2); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button3); clutter_container_add_actor (CLUTTER_CONTAINER (stage), group[3]); clutter_container_add_actor (CLUTTER_CONTAINER (group[0]), button4); clutter_actor_show (stage); clutter_main (); return 0; } muffin-6.4.1/src/tests/clutter/accessibility/cally-examples-util.h0000664000175000017500000000170614723361714024254 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ gboolean cally_util_a11y_init (int *argc, char ***argv); muffin-6.4.1/src/tests/clutter/accessibility/cally-atkeditabletext-example.c0000664000175000017500000002045514723361714026267 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "cally-examples-util.h" #define WIDTH 800 #define HEIGHT 600 static ClutterActor *text_actor = NULL; static ClutterActor *text_editable_actor = NULL; /* * Test AtkText interface */ static void test_atk_text (ClutterActor *actor) { AtkObject *object = NULL; AtkEditableText *cally_editable_text = NULL; gint pos = 0; object = atk_gobject_accessible_for_object (G_OBJECT (actor)); cally_editable_text = ATK_EDITABLE_TEXT (object); if (cally_editable_text != NULL) { atk_editable_text_set_text_contents (cally_editable_text, "New text"); atk_editable_text_delete_text (cally_editable_text, 0, 3); pos = 3; atk_editable_text_insert_text (cally_editable_text, "New", 0, &pos); /* Not implemented in cally, just checking that we can call this functions */ atk_editable_text_copy_text (cally_editable_text, 0, -1); atk_editable_text_paste_text (cally_editable_text, 5); atk_editable_text_cut_text (cally_editable_text, 0, -1); } } static gboolean insert_text_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { AtkObject *object = NULL; AtkEditableText *cally_editable_text = NULL; gint pos = 0; object = atk_gobject_accessible_for_object (G_OBJECT (text_editable_actor)); cally_editable_text = ATK_EDITABLE_TEXT (object); pos = 3; atk_editable_text_insert_text (cally_editable_text, "New", 0, &pos); return TRUE; } static gboolean delete_text_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { AtkObject *object = NULL; AtkEditableText *cally_editable_text = NULL; object = atk_gobject_accessible_for_object (G_OBJECT (text_editable_actor)); cally_editable_text = ATK_EDITABLE_TEXT (object); atk_editable_text_delete_text (cally_editable_text, 0, 3); return TRUE; } static gboolean set_text_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { AtkObject *object = NULL; AtkEditableText *cally_editable_text = NULL; object = atk_gobject_accessible_for_object (G_OBJECT (text_editable_actor)); cally_editable_text = ATK_EDITABLE_TEXT (object); atk_editable_text_set_text_contents (cally_editable_text, "New text"); return TRUE; } static gboolean activate_deactivate_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { gboolean active = FALSE; active = clutter_text_get_activatable (CLUTTER_TEXT (text_editable_actor)); clutter_text_set_activatable (CLUTTER_TEXT (text_editable_actor), !active); return TRUE; } static gboolean print_cursor_position_press_cb (ClutterActor *actor, ClutterButtonEvent *event, gpointer data) { gint pos = 0; pos = clutter_text_get_cursor_position (CLUTTER_TEXT (text_editable_actor)); g_print ("current cursor position %i\n", pos); return TRUE; } static void activate_cb (ClutterActor *actor, gpointer data) { g_print ("Actor activated\n"); } static ClutterActor* _create_button (const gchar *text) { ClutterActor *button = NULL; ClutterActor *rectangle = NULL; ClutterActor *label = NULL; button = clutter_group_new (); rectangle = clutter_rectangle_new_with_color (CLUTTER_COLOR_Magenta); clutter_actor_set_size (rectangle, 375, 35); label = clutter_text_new_full ("Sans Bold 32px", text, CLUTTER_COLOR_Black); clutter_container_add_actor (CLUTTER_CONTAINER (button), rectangle); clutter_container_add_actor (CLUTTER_CONTAINER (button), label); clutter_actor_set_reactive (button, TRUE); return button; } static void make_ui (ClutterActor *stage) { ClutterActor *button = NULL; clutter_stage_set_title (CLUTTER_STAGE (stage), "Cally - AtkEditable Test"); clutter_stage_set_color (CLUTTER_STAGE (stage), CLUTTER_COLOR_White); clutter_actor_set_size (stage, WIDTH, HEIGHT); /* text */ text_actor = clutter_text_new_full ("Sans Bold 32px", "Lorem ipsum dolor sit amet", CLUTTER_COLOR_Red); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text_actor); /* text_editable */ text_editable_actor = clutter_text_new_full ("Sans Bold 32px", "consectetur adipisicing elit", CLUTTER_COLOR_Red); clutter_actor_set_position (text_editable_actor, 0, 100); clutter_text_set_editable (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_text_set_selection_color (CLUTTER_TEXT (text_editable_actor), CLUTTER_COLOR_Green); clutter_text_set_activatable (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_text_set_line_wrap (CLUTTER_TEXT (text_editable_actor), TRUE); clutter_actor_grab_key_focus (text_editable_actor); clutter_actor_set_reactive (text_editable_actor, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), text_editable_actor); g_signal_connect (text_editable_actor, "activate", G_CALLBACK (activate_cb), NULL); /* test buttons */ button = _create_button ("Set"); clutter_actor_set_position (button, 100, 200); g_signal_connect_after (button, "button-press-event", G_CALLBACK (set_text_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); button = _create_button ("Delete"); clutter_actor_set_position (button, 100, 250); g_signal_connect_after (button, "button-press-event", G_CALLBACK (delete_text_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); button = _create_button ("Insert"); clutter_actor_set_position (button, 100, 300); g_signal_connect_after (button, "button-press-event", G_CALLBACK (insert_text_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); button = _create_button ("Activate/Deactivate"); clutter_actor_set_position (button, 100, 350); g_signal_connect_after (button, "button-press-event", G_CALLBACK (activate_deactivate_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); button = _create_button ("Cursor position"); clutter_actor_set_position (button, 100, 450); g_signal_connect_after (button, "button-press-event", G_CALLBACK (print_cursor_position_press_cb), NULL); clutter_container_add_actor (CLUTTER_CONTAINER (stage), button); } int main (int argc, char *argv[]) { ClutterActor *stage = NULL; g_set_application_name ("AtkEditableText"); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; cally_util_a11y_init (&argc, &argv); stage = clutter_stage_new (); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); make_ui (stage); clutter_actor_show_all (stage); test_atk_text (text_actor); test_atk_text (text_editable_actor); clutter_main (); return 0; } muffin-6.4.1/src/tests/clutter/accessibility/cally-clone-example.c0000664000175000017500000000777414723361714024222 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include "cally-examples-util.h" #define WIDTH 800 #define HEIGHT 600 #define HEIGHT_STEP 100 #define NUM_ENTRIES 3 static void make_ui (ClutterActor *stage) { ClutterActor *editable = NULL; ClutterActor *rectangle = NULL; ClutterActor *label = NULL; ClutterColor color_stage = { 0x00, 0x00, 0x00, 0xff }; ClutterColor color_text = { 0xff, 0x00, 0x00, 0xff }; ClutterColor color_sel = { 0x00, 0xff, 0x00, 0x55 }; ClutterColor color_label = { 0x00, 0xff, 0x55, 0xff }; ClutterColor color_rect = { 0x00, 0xff, 0xff, 0x55 }; ClutterActor *full_entry = NULL; ClutterActor *cloned_entry = NULL; clutter_stage_set_color (CLUTTER_STAGE (stage), &color_stage); clutter_actor_set_size (stage, WIDTH, HEIGHT); label = clutter_text_new_full ("Sans Bold 32px", "Entry", &color_label); clutter_actor_set_position (label, 0, 50); /* editable */ editable = clutter_text_new_full ("Sans Bold 32px", "ddd", &color_text); clutter_actor_set_position (editable, 150, 50); clutter_text_set_editable (CLUTTER_TEXT (editable), TRUE); clutter_text_set_selectable (CLUTTER_TEXT (editable), TRUE); clutter_text_set_selection_color (CLUTTER_TEXT (editable), &color_sel); clutter_actor_grab_key_focus (editable); clutter_actor_set_reactive (editable, TRUE); /* rectangle: to create a entry "feeling" */ rectangle = clutter_rectangle_new_with_color (&color_rect); clutter_actor_set_position (rectangle, 150, 50); clutter_actor_add_constraint (rectangle, clutter_bind_constraint_new (editable, CLUTTER_BIND_SIZE, 0)); full_entry = clutter_group_new (); clutter_actor_set_position (full_entry, 0, 50); clutter_actor_set_size (full_entry, 100, 75); clutter_container_add_actor (CLUTTER_CONTAINER (full_entry), label); clutter_container_add_actor (CLUTTER_CONTAINER (full_entry), editable); clutter_container_add_actor (CLUTTER_CONTAINER (full_entry), rectangle); clutter_actor_show_all (full_entry); clutter_actor_set_scale (full_entry, 2, 1); clutter_container_add_actor (CLUTTER_CONTAINER (stage), full_entry); /* Cloning! */ cloned_entry = clutter_clone_new (full_entry); clutter_actor_set_position (cloned_entry, 50, 200); clutter_actor_set_scale (cloned_entry, 1, 2); clutter_actor_show_all (cloned_entry); clutter_actor_set_reactive (cloned_entry, TRUE); clutter_container_add_actor (CLUTTER_CONTAINER (stage), cloned_entry); } int main (int argc, char *argv[]) { ClutterActor *stage; g_set_application_name ("Clone Example"); if (clutter_init (&argc, &argv) != CLUTTER_INIT_SUCCESS) return 1; cally_util_a11y_init (&argc, &argv); stage = clutter_stage_new (); clutter_stage_set_title (CLUTTER_STAGE (stage), "Cally - Clone Test"); g_signal_connect (stage, "destroy", G_CALLBACK (clutter_main_quit), NULL); make_ui (stage); clutter_actor_show_all (stage); clutter_main (); return 0; } muffin-6.4.1/src/tests/clutter/accessibility/cally-examples-util.c0000664000175000017500000000776114723361714024256 0ustar fabiofabio/* CALLY - The Clutter Accessibility Implementation Library * * Copyright (C) 2009 Igalia, S.L. * * Author: Alejandro Piñeiro Iglesias * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ #include #include #include #include "cally-examples-util.h" /* Checking the at-spi sources, the module directory is * $(libdir)/gtk-2.0/modules * * It is supposed cally would be installed on the same libdir. * * You could use the option atk-bridge-dir to use other directory. */ #define ATK_BRIDGE_DEFAULT_MODULE_DIRECTORY PREFIXDIR"/gtk-2.0/modules" static gchar * _search_for_bridge_module (const gchar *module_name) { /* We simplify the search for the atk bridge, see see the definition * of the macro for more information*/ return g_strdup (ATK_BRIDGE_DEFAULT_MODULE_DIRECTORY); } static gchar* _a11y_check_custom_bridge (int *argc, char ***argv) { GError *error = NULL; GOptionContext *context; static gchar *bridge_dir = NULL; static GOptionEntry entries [] = { {"atk-bridge-dir", 'd', 0, G_OPTION_ARG_STRING, &bridge_dir, "atk-bridge module directory", NULL} }; context = g_option_context_new ("- cally examples"); g_option_context_add_main_entries (context, entries, NULL); if (!g_option_context_parse (context, argc, argv, &error)) { g_print ("%s\n", error->message); g_print ("Use --help for more information.\n"); exit (0); } return bridge_dir; } static gboolean _a11y_invoke_module (const gchar *module_path, gboolean init) { GModule *handle; void (*invoke_fn) (void); const char *method; if (init) method = "gnome_accessibility_module_init"; else method = "gnome_accessibility_module_shutdown"; if (!module_path) return FALSE; if (!(handle = g_module_open (module_path, G_MODULE_BIND_LAZY))) { g_warning ("Accessibility: failed to load module '%s': '%s'", module_path, g_module_error ()); return FALSE; } if (!g_module_symbol (handle, method, (gpointer *)&invoke_fn)) { g_warning ("Accessibility: error library '%s' does not include " "method '%s' required for accessibility support", module_path, method); g_module_close (handle); return FALSE; } g_debug ("Module %s loaded successfully", module_path); invoke_fn (); return TRUE; } /** * This method will initialize the accessibility support provided by cally. * * Basically it will load the cally module using gmodule functions. * * Returns if it was able to init the a11y support or not. */ gboolean cally_util_a11y_init (int *argc, char ***argv) { gchar *bridge_dir = NULL; gchar *bridge_path = NULL; gboolean result = FALSE; if (clutter_get_accessibility_enabled () == FALSE) { g_warning ("Accessibility: clutter has no accessibility enabled" " skipping the atk-bridge load"); return FALSE; } bridge_dir = _a11y_check_custom_bridge (argc, argv); if (bridge_dir == NULL) bridge_dir = _search_for_bridge_module ("atk-bridge"); bridge_path = g_module_build_path (bridge_dir, "libatk-bridge"); result = _a11y_invoke_module (bridge_path, TRUE); g_free (bridge_dir); g_free (bridge_path); return result; } muffin-6.4.1/src/tests/meta-gpu-test.h0000664000175000017500000000165214723361714016544 0ustar fabiofabio/* * Copyright (C) 2016-2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_GPU_TEST_H #define META_GPU_TEST_H #include "backends/meta-gpu.h" #define META_TYPE_GPU_TEST (meta_gpu_test_get_type ()) G_DECLARE_FINAL_TYPE (MetaGpuTest, meta_gpu_test, META, GPU_TEST, MetaGpu) #endif /* META_GPU_TEST_H */ muffin-6.4.1/src/tests/meta-monitor-manager-test.c0000664000175000017500000003340614723361714021045 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/meta-monitor-manager-test.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-gpu.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-output.h" #include "tests/meta-backend-test.h" struct _MetaMonitorManagerTest { MetaMonitorManager parent; gboolean handles_transforms; int tiled_monitor_count; MetaMonitorTestSetup *test_setup; }; G_DEFINE_TYPE (MetaMonitorManagerTest, meta_monitor_manager_test, META_TYPE_MONITOR_MANAGER) static MetaMonitorTestSetup *_initial_test_setup = NULL; void meta_monitor_manager_test_init_test_setup (MetaMonitorTestSetup *test_setup) { _initial_test_setup = test_setup; } void meta_monitor_manager_test_emulate_hotplug (MetaMonitorManagerTest *manager_test, MetaMonitorTestSetup *test_setup) { MetaMonitorManager *manager = META_MONITOR_MANAGER (manager_test); MetaMonitorTestSetup *old_test_setup; old_test_setup = manager_test->test_setup; manager_test->test_setup = test_setup; meta_monitor_manager_read_current_state (manager); meta_monitor_manager_on_hotplug (manager); g_free (old_test_setup); } void meta_monitor_manager_test_set_handles_transforms (MetaMonitorManagerTest *manager_test, gboolean handles_transforms) { g_assert (handles_transforms || meta_is_stage_views_enabled()); manager_test->handles_transforms = handles_transforms; } int meta_monitor_manager_test_get_tiled_monitor_count (MetaMonitorManagerTest *manager_test) { return manager_test->tiled_monitor_count; } void meta_monitor_manager_test_read_current (MetaMonitorManager *manager) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendTest *backend_test = META_BACKEND_TEST (backend); MetaGpu *gpu = meta_backend_test_get_gpu (backend_test); GList *l; g_assert (manager_test->test_setup); for (l = manager_test->test_setup->outputs; l; l = l->next) META_OUTPUT (l->data)->gpu = gpu; for (l = manager_test->test_setup->crtcs; l; l = l->next) META_CRTC (l->data)->gpu = gpu; meta_gpu_take_modes (gpu, manager_test->test_setup->modes); meta_gpu_take_crtcs (gpu, manager_test->test_setup->crtcs); meta_gpu_take_outputs (gpu, manager_test->test_setup->outputs); } static void meta_monitor_manager_test_ensure_initial_config (MetaMonitorManager *manager) { MetaMonitorsConfig *config; config = meta_monitor_manager_ensure_configured (manager); if (meta_is_stage_views_enabled ()) { meta_monitor_manager_update_logical_state (manager, config); } else { meta_monitor_manager_update_logical_state_derived (manager, NULL); } } static void apply_crtc_assignments (MetaMonitorManager *manager, MetaCrtcInfo **crtcs, unsigned int n_crtcs, MetaOutputInfo **outputs, unsigned int n_outputs) { MetaBackend *backend = meta_monitor_manager_get_backend (manager); MetaBackendTest *backend_test = META_BACKEND_TEST (backend); MetaGpu *gpu = meta_backend_test_get_gpu (backend_test); GList *l; unsigned int i; for (i = 0; i < n_crtcs; i++) { MetaCrtcInfo *crtc_info = crtcs[i]; MetaCrtc *crtc = crtc_info->crtc; crtc->is_dirty = TRUE; if (crtc_info->mode == NULL) { meta_crtc_unset_config (crtc); } else { MetaOutput *output; unsigned int j; meta_crtc_set_config (crtc, &crtc_info->layout, crtc_info->mode, crtc_info->transform); for (j = 0; j < crtc_info->outputs->len; j++) { output = ((MetaOutput**)crtc_info->outputs->pdata)[j]; output->is_dirty = TRUE; meta_output_assign_crtc (output, crtc); } } } for (i = 0; i < n_outputs; i++) { MetaOutputInfo *output_info = outputs[i]; MetaOutput *output = output_info->output; output->is_primary = output_info->is_primary; output->is_presentation = output_info->is_presentation; output->is_underscanning = output_info->is_underscanning; } /* Disable CRTCs not mentioned in the list */ for (l = meta_gpu_get_crtcs (gpu); l; l = l->next) { MetaCrtc *crtc = l->data; if (crtc->is_dirty) { crtc->is_dirty = FALSE; continue; } meta_crtc_unset_config (crtc); } /* Disable outputs not mentioned in the list */ for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; if (output->is_dirty) { output->is_dirty = FALSE; continue; } meta_output_unassign_crtc (output); output->is_primary = FALSE; } } static void update_screen_size (MetaMonitorManager *manager, MetaMonitorsConfig *config) { GList *l; int screen_width = 0; int screen_height = 0; for (l = config->logical_monitor_configs; l; l = l->next) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; int right_edge; int bottom_edge; right_edge = (logical_monitor_config->layout.width + logical_monitor_config->layout.x); if (right_edge > screen_width) screen_width = right_edge; bottom_edge = (logical_monitor_config->layout.height + logical_monitor_config->layout.y); if (bottom_edge > screen_height) screen_height = bottom_edge; } manager->screen_width = screen_width; manager->screen_height = screen_height; } static gboolean meta_monitor_manager_test_apply_monitors_config (MetaMonitorManager *manager, MetaMonitorsConfig *config, MetaMonitorsConfigMethod method, GError **error) { GPtrArray *crtc_infos; GPtrArray *output_infos; if (!config) { manager->screen_width = META_MONITOR_MANAGER_MIN_SCREEN_WIDTH; manager->screen_height = META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT; if (meta_is_stage_views_enabled ()) meta_monitor_manager_rebuild (manager, NULL); else meta_monitor_manager_rebuild_derived (manager, config); return TRUE; } if (!meta_monitor_config_manager_assign (manager, config, &crtc_infos, &output_infos, error)) return FALSE; if (method == META_MONITORS_CONFIG_METHOD_VERIFY) { g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); return TRUE; } apply_crtc_assignments (manager, (MetaCrtcInfo **) crtc_infos->pdata, crtc_infos->len, (MetaOutputInfo **) output_infos->pdata, output_infos->len); g_ptr_array_free (crtc_infos, TRUE); g_ptr_array_free (output_infos, TRUE); update_screen_size (manager, config); if (meta_is_stage_views_enabled ()) meta_monitor_manager_rebuild (manager, config); else meta_monitor_manager_rebuild_derived (manager, config); return TRUE; } static void meta_monitor_manager_test_tiled_monitor_added (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); manager_test->tiled_monitor_count++; } static void meta_monitor_manager_test_tiled_monitor_removed (MetaMonitorManager *manager, MetaMonitor *monitor) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); manager_test->tiled_monitor_count--; } static gboolean meta_monitor_manager_test_is_transform_handled (MetaMonitorManager *manager, MetaCrtc *crtc, MetaMonitorTransform transform) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (manager); return manager_test->handles_transforms; } static float meta_monitor_manager_test_calculate_monitor_mode_scale (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode) { MetaOutput *output; MetaOutputTest *output_test; output = meta_monitor_get_main_output (monitor); output_test = output->driver_private; if (output_test) return output_test->scale; else return 1; } static float * meta_monitor_manager_test_calculate_supported_scales (MetaMonitorManager *manager, MetaLogicalMonitorLayoutMode layout_mode, MetaMonitor *monitor, MetaMonitorMode *monitor_mode, int *n_supported_scales) { MetaMonitorScalesConstraint constraints = META_MONITOR_SCALES_CONSTRAINT_NONE; switch (layout_mode) { case META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL: case META_LOGICAL_MONITOR_LAYOUT_MODE_GLOBAL_UI_LOGICAL: break; case META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL: constraints |= META_MONITOR_SCALES_CONSTRAINT_NO_FRAC; break; } return meta_monitor_calculate_supported_scales (monitor, monitor_mode, constraints, n_supported_scales); } static gboolean is_monitor_framebuffer_scaled (void) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); return meta_settings_is_experimental_feature_enabled ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); } static MetaMonitorManagerCapability meta_monitor_manager_test_get_capabilities (MetaMonitorManager *manager) { MetaMonitorManagerCapability capabilities; capabilities = META_MONITOR_MANAGER_CAPABILITY_TILING; if (is_monitor_framebuffer_scaled ()) capabilities |= META_MONITOR_MANAGER_CAPABILITY_LAYOUT_MODE; return capabilities; } static gboolean meta_monitor_manager_test_get_max_screen_size (MetaMonitorManager *manager, int *max_width, int *max_height) { if (meta_is_stage_views_enabled ()) return FALSE; *max_width = 65535; *max_height = 65535; return TRUE; } static MetaLogicalMonitorLayoutMode meta_monitor_manager_test_get_default_layout_mode (MetaMonitorManager *manager) { if (!meta_is_stage_views_enabled ()) return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; if (is_monitor_framebuffer_scaled ()) return META_LOGICAL_MONITOR_LAYOUT_MODE_LOGICAL; else return META_LOGICAL_MONITOR_LAYOUT_MODE_PHYSICAL; } static void meta_monitor_manager_test_dispose (GObject *object) { MetaMonitorManagerTest *manager_test = META_MONITOR_MANAGER_TEST (object); g_clear_pointer (&manager_test->test_setup, g_free); } static void meta_monitor_manager_test_init (MetaMonitorManagerTest *manager_test) { g_assert (_initial_test_setup); manager_test->handles_transforms = TRUE; manager_test->test_setup = _initial_test_setup; } static void meta_monitor_manager_test_class_init (MetaMonitorManagerTestClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaMonitorManagerClass *manager_class = META_MONITOR_MANAGER_CLASS (klass); object_class->dispose = meta_monitor_manager_test_dispose; manager_class->ensure_initial_config = meta_monitor_manager_test_ensure_initial_config; manager_class->apply_monitors_config = meta_monitor_manager_test_apply_monitors_config; manager_class->tiled_monitor_added = meta_monitor_manager_test_tiled_monitor_added; manager_class->tiled_monitor_removed = meta_monitor_manager_test_tiled_monitor_removed; manager_class->is_transform_handled = meta_monitor_manager_test_is_transform_handled; manager_class->calculate_monitor_mode_scale = meta_monitor_manager_test_calculate_monitor_mode_scale; manager_class->calculate_supported_scales = meta_monitor_manager_test_calculate_supported_scales; manager_class->get_capabilities = meta_monitor_manager_test_get_capabilities; manager_class->get_max_screen_size = meta_monitor_manager_test_get_max_screen_size; manager_class->get_default_layout_mode = meta_monitor_manager_test_get_default_layout_mode; } muffin-6.4.1/src/tests/monitor-test-utils.h0000664000175000017500000000177514723361714017660 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MONITOR_TEST_UTILS_H #define MONITOR_TEST_UTILS_H #include gboolean is_using_monitor_config_manager (void); void set_custom_monitor_config (const char *filename); char * read_file (const char *file_path); #endif /* MONITOR_TEST_UTILS_H */ muffin-6.4.1/src/tests/headless-start-test.c0000664000175000017500000001466614723361714017754 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-monitor-manager-private.h" #include "backends/meta-crtc.h" #include "backends/meta-output.h" #include "compositor/meta-plugin-manager.h" #include "core/display-private.h" #include "core/main-private.h" #include "meta/main.h" #include "tests/meta-backend-test.h" #include "tests/meta-monitor-manager-test.h" #include "tests/test-utils.h" #include "wayland/meta-wayland.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) #define FRAME_WARNING "Frame has assigned frame counter but no frame drawn time" static gboolean run_tests (gpointer data) { MetaBackend *backend = meta_get_backend (); MetaSettings *settings = meta_backend_get_settings (backend); gboolean ret; g_test_log_set_fatal_handler (NULL, NULL); meta_settings_override_experimental_features (settings); meta_settings_enable_experimental_feature ( settings, META_EXPERIMENTAL_FEATURE_SCALE_MONITOR_FRAMEBUFFER); ret = g_test_run (); meta_quit (ret != 0); return FALSE; } static gboolean ignore_frame_counter_warning (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if ((log_level & G_LOG_LEVEL_WARNING) && g_strcmp0 (log_domain, "mutter") == 0 && g_str_has_suffix (message, FRAME_WARNING)) return FALSE; return TRUE; } static void meta_test_headless_start (void) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); GList *gpus; MetaGpu *gpu; gpus = meta_backend_get_gpus (backend); g_assert_cmpint ((int) g_list_length (gpus), ==, 1); gpu = gpus->data; g_assert_null (meta_gpu_get_modes (gpu)); g_assert_null (meta_gpu_get_outputs (gpu)); g_assert_null (meta_gpu_get_crtcs (gpu)); g_assert_null (monitor_manager->monitors); g_assert_null (monitor_manager->logical_monitors); g_assert_cmpint (monitor_manager->screen_width, ==, META_MONITOR_MANAGER_MIN_SCREEN_WIDTH); g_assert_cmpint (monitor_manager->screen_height, ==, META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT); } static void meta_test_headless_monitor_getters (void) { MetaDisplay *display; int index; display = meta_get_display (); index = meta_display_get_monitor_index_for_rect (display, &(MetaRectangle) { 0 }); g_assert_cmpint (index, ==, -1); } static void meta_test_headless_monitor_connect (void) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); MetaMonitorTestSetup *test_setup; MetaCrtcMode **modes; MetaCrtcMode *crtc_mode; MetaCrtc *crtc; MetaCrtc **possible_crtcs; MetaOutput *output; GList *logical_monitors; ClutterActor *stage; test_setup = g_new0 (MetaMonitorTestSetup, 1); crtc_mode = g_object_new (META_TYPE_CRTC_MODE, NULL); crtc_mode->mode_id = 1; crtc_mode->width = 1024; crtc_mode->height = 768; crtc_mode->refresh_rate = 60.0; test_setup->modes = g_list_append (NULL, crtc_mode); crtc = g_object_new (META_TYPE_CRTC, NULL); crtc->crtc_id = 1; crtc->all_transforms = ALL_TRANSFORMS; test_setup->crtcs = g_list_append (NULL, crtc); modes = g_new0 (MetaCrtcMode *, 1); modes[0] = crtc_mode; possible_crtcs = g_new0 (MetaCrtc *, 1); possible_crtcs[0] = g_list_first (test_setup->crtcs)->data; output = g_object_new (META_TYPE_OUTPUT, NULL); output->winsys_id = 1; output->name = g_strdup ("DP-1"); output->vendor = g_strdup ("MetaProduct's Inc."); output->product = g_strdup ("MetaMonitor"); output->serial = g_strdup ("0x987654"); output->preferred_mode = modes[0]; output->n_modes = 1; output->modes = modes; output->n_possible_crtcs = 1; output->possible_crtcs = possible_crtcs; output->connector_type = META_CONNECTOR_TYPE_DisplayPort; test_setup->outputs = g_list_append (NULL, output); meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup); logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); g_assert_cmpint (g_list_length (logical_monitors), ==, 1); g_assert_cmpint (monitor_manager->screen_width, ==, 1024); g_assert_cmpint (monitor_manager->screen_height, ==, 768); stage = meta_backend_get_stage (backend); g_assert_cmpint (clutter_actor_get_width (stage), ==, 1024); g_assert_cmpint (clutter_actor_get_height (stage), ==, 768); } static MetaMonitorTestSetup * create_headless_test_setup (void) { return g_new0 (MetaMonitorTestSetup, 1); } static void init_tests (int argc, char **argv) { MetaMonitorTestSetup *initial_test_setup; initial_test_setup = create_headless_test_setup (); meta_monitor_manager_test_init_test_setup (initial_test_setup); g_test_add_func ("/headless-start/start", meta_test_headless_start); g_test_add_func ("/headless-start/monitor-getters", meta_test_headless_monitor_getters); g_test_add_func ("/headless-start/connect", meta_test_headless_monitor_connect); } int main (int argc, char *argv[]) { test_init (&argc, &argv); init_tests (argc, argv); meta_plugin_manager_load (test_get_plugin_name ()); meta_override_compositor_configuration (META_COMPOSITOR_TYPE_WAYLAND, META_TYPE_BACKEND_TEST); meta_init (); meta_register_with_session (); g_test_log_set_fatal_handler (ignore_frame_counter_warning, NULL); g_idle_add (run_tests, NULL); return meta_run (); } muffin-6.4.1/src/tests/meta-backend-test.h0000664000175000017500000000244114723361714017335 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_BACKEND_TEST_H #define META_BACKEND_TEST_H #include "backends/x11/nested/meta-backend-x11-nested.h" #define META_TYPE_BACKEND_TEST (meta_backend_test_get_type ()) G_DECLARE_FINAL_TYPE (MetaBackendTest, meta_backend_test, META, BACKEND_TEST, MetaBackendX11Nested) void meta_backend_test_set_is_lid_closed (MetaBackendTest *backend_test, gboolean is_lid_closed); MetaGpu * meta_backend_test_get_gpu (MetaBackendTest *backend_test); #endif /* META_BACKEND_TEST_H */ muffin-6.4.1/src/tests/clutter-test-utils.h0000664000175000017500000001335714723361714017652 0ustar fabiofabio/* * Clutter. * * An OpenGL based 'interactive canvas' library. * * Copyright (C) 2013 Emmanuele Bassi * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #include "config.h" #ifndef __CLUTTER_TEST_UTILS_H__ #define __CLUTTER_TEST_UTILS_H__ #define __CLUTTER_H_INSIDE__ #include "clutter/clutter-types.h" #include "clutter/clutter-actor.h" #include "clutter/clutter-color.h" #include "clutter/clutter-private.h" #include "core/main-private.h" #include "meta/common.h" #include "meta/main.h" #include "backends/x11/nested/meta-backend-x11-nested.h" #include "wayland/meta-wayland.h" #include "wayland/meta-xwayland.h" G_BEGIN_DECLS /** * CLUTTER_TEST_UNIT: * @path: the GTest path for the test function * @func: the GTestFunc function * * Adds @func at the given @path in the test suite. * * Since: 1.18 */ #define CLUTTER_TEST_UNIT(path,func) \ clutter_test_add (path, func); /** * CLUTTER_TEST_SUITE: * @units: a list of %CLUTTER_TEST_UNIT definitions * * Defines the entry point and initializes a Clutter test unit, e.g.: * * |[ * CLUTTER_TEST_SUITE ( * CLUTTER_TEST_UNIT ("/foobarize", foobarize) * CLUTTER_TEST_UNIT ("/bar-enabled", bar_enabled) * ) * ]| * * Expands to: * * |[ * int * main (int argc, * char *argv[]) * { * clutter_test_init (&argc, &argv); * * clutter_test_add ("/foobarize", foobarize); * clutter_test_add ("/bar-enabled", bar_enabled); * * return clutter_test_run (); * } * ]| * * Since: 1.18 */ #define CLUTTER_TEST_SUITE(units) \ int \ main (int argc, char *argv[]) \ { \ meta_test_init ();\ \ clutter_test_init (&argc, &argv); \ \ { \ units \ } \ \ return clutter_test_run (); \ } CLUTTER_EXPORT void clutter_test_init (int *argc, char ***argv); CLUTTER_EXPORT int clutter_test_run (void); CLUTTER_EXPORT void clutter_test_add (const char *test_path, GTestFunc test_func); CLUTTER_EXPORT void clutter_test_add_data (const char *test_path, GTestDataFunc test_func, gpointer test_data); CLUTTER_EXPORT void clutter_test_add_data_full (const char *test_path, GTestDataFunc test_func, gpointer test_data, GDestroyNotify test_notify); CLUTTER_EXPORT ClutterActor * clutter_test_get_stage (void); #define clutter_test_assert_actor_at_point(stage,point,actor) \ G_STMT_START { \ const graphene_point_t *__p = (point); \ ClutterActor *__actor = (actor); \ ClutterActor *__stage = (stage); \ ClutterActor *__res; \ if (clutter_test_check_actor_at_point (__stage, __p, actor, &__res)) ; else { \ const char *__str1 = clutter_actor_get_name (__actor) != NULL \ ? clutter_actor_get_name (__actor) \ : G_OBJECT_TYPE_NAME (__actor); \ const char *__str2 = clutter_actor_get_name (__res) != NULL \ ? clutter_actor_get_name (__res) \ : G_OBJECT_TYPE_NAME (__res); \ char *__msg = g_strdup_printf ("assertion failed (actor %s at %.2f,%.2f): found actor %s", \ __str1, __p->x, __p->y, __str2); \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ g_free (__msg); \ } \ } G_STMT_END #define clutter_test_assert_color_at_point(stage,point,color) \ G_STMT_START { \ const graphene_point_t *__p = (point); \ const ClutterColor *__c = (color); \ ClutterActor *__stage = (stage); \ ClutterColor __res; \ if (clutter_test_check_color_at_point (__stage, __p, __c, &__res)) ; else { \ char *__str1 = clutter_color_to_string (__c); \ char *__str2 = clutter_color_to_string (&__res); \ char *__msg = g_strdup_printf ("assertion failed (color %s at %.2f,%.2f): found color %s", \ __str1, __p->x, __p->y, __str2); \ g_assertion_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, __msg); \ g_free (__msg); \ g_free (__str1); \ g_free (__str2); \ } \ } G_STMT_END CLUTTER_EXPORT gboolean clutter_test_check_actor_at_point (ClutterActor *stage, const graphene_point_t *point, ClutterActor *actor, ClutterActor **result); CLUTTER_EXPORT gboolean clutter_test_check_color_at_point (ClutterActor *stage, const graphene_point_t *point, const ClutterColor *color, ClutterColor *result); G_END_DECLS #endif /* __CLUTTER_TEST_UTILS_H__ */ muffin-6.4.1/src/tests/clutter-test-utils.c0000664000175000017500000002732414723361714017644 0ustar fabiofabio#include "clutter-test-utils.h" #include #include #include typedef struct { ClutterActor *stage; guint no_display : 1; } ClutterTestEnvironment; static ClutterTestEnvironment *test_environ = NULL; #define DBUS_NAME_WARNING "Lost or failed to acquire name" static gboolean log_func (const gchar *log_domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data) { if ((log_level & G_LOG_LEVEL_WARNING) && g_strcmp0 (log_domain, "mutter") == 0 && g_str_has_prefix (message, DBUS_NAME_WARNING)) return FALSE; return TRUE; } /* * clutter_test_init: * @argc: (inout): number of arguments in @argv * @argv: (inout) (array length=argc) (nullable): array of arguments * * Initializes the Clutter test environment. * * Since: 1.18 */ void clutter_test_init (int *argc, char ***argv) { gboolean no_display = FALSE; if (G_UNLIKELY (test_environ != NULL)) g_error ("Attempting to initialize the test suite more than once, " "aborting...\n"); #ifdef CLUTTER_WINDOWING_X11 /* on X11 backends we need the DISPLAY environment set. * * check_windowing_backend() will pre-initialize the Clutter * backend object. */ if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11)) { const char *display = g_getenv ("DISPLAY"); if (display == NULL || *display == '\0') { g_test_message ("No DISPLAY environment variable found, but we require a " "DISPLAY set in order to run the conformance test suite.\n" "Skipping all tests.\n"); no_display = TRUE; goto out; } } #endif /* we explicitly disable the synchronisation to the vertical refresh * rate, and run the master clock using a 60 fps timer instead. */ _clutter_set_sync_to_vblank (FALSE); /* perform the actual initialization */ g_assert (clutter_init (NULL, NULL) == CLUTTER_INIT_SUCCESS); out: g_test_init (argc, argv, NULL); g_test_bug_base ("https://bugzilla.gnome.org/show_bug.cgi?id=%s"); /* our global state, accessible from each test unit */ test_environ = g_new0 (ClutterTestEnvironment, 1); test_environ->no_display = no_display; } /** * clutter_test_get_stage: * * Retrieves the #ClutterStage used for testing. * * Return value: (transfer none): the stage used for testing * * Since: 1.18 */ ClutterActor * clutter_test_get_stage (void) { g_assert (test_environ != NULL); if (test_environ->stage == NULL) { /* create a stage, and ensure that it goes away at the end */ test_environ->stage = clutter_stage_new (); clutter_actor_set_name (test_environ->stage, "Test Stage"); g_object_add_weak_pointer (G_OBJECT (test_environ->stage), (gpointer *) &test_environ->stage); } return test_environ->stage; } typedef struct { gpointer test_func; gpointer test_data; GDestroyNotify test_notify; } ClutterTestData; static void clutter_test_func_wrapper (gconstpointer data_) { const ClutterTestData *data = data_; g_test_log_set_fatal_handler (log_func, NULL); /* ensure that the previous test state has been cleaned up */ g_assert_null (test_environ->stage); if (test_environ->no_display) { g_test_skip ("No DISPLAY set"); goto out; } if (data->test_data != NULL) { GTestDataFunc test_func = data->test_func; test_func (data->test_data); } else { GTestFunc test_func = data->test_func; test_func (); } out: if (data->test_notify != NULL) data->test_notify (data->test_data); if (test_environ->stage != NULL) { clutter_actor_destroy (test_environ->stage); g_assert_null (test_environ->stage); } } /** * clutter_test_add: (skip) * @test_path: unique path for identifying the test * @test_func: function containing the test * * Adds a test unit to the Clutter test environment. * * See also: g_test_add() * * Since: 1.18 */ void clutter_test_add (const char *test_path, GTestFunc test_func) { clutter_test_add_data_full (test_path, (GTestDataFunc) test_func, NULL, NULL); } /** * clutter_test_add_data: (skip) * @test_path: unique path for identifying the test * @test_func: function containing the test * @test_data: data to pass to the test function * * Adds a test unit to the Clutter test environment. * * See also: g_test_add_data_func() * * Since: 1.18 */ void clutter_test_add_data (const char *test_path, GTestDataFunc test_func, gpointer test_data) { clutter_test_add_data_full (test_path, test_func, test_data, NULL); } /** * clutter_test_add_data_full: * @test_path: unique path for identifying the test * @test_func: (scope notified) (closure test_data): function containing the test * @test_data: data to pass to the test function * @test_notify: function called when the test function ends * * Adds a test unit to the Clutter test environment. * * See also: g_test_add_data_func_full() * * Since: 1.18 */ void clutter_test_add_data_full (const char *test_path, GTestDataFunc test_func, gpointer test_data, GDestroyNotify test_notify) { ClutterTestData *data; g_return_if_fail (test_path != NULL); g_return_if_fail (test_func != NULL); g_assert (test_environ != NULL); data = g_new (ClutterTestData, 1); data->test_func = test_func; data->test_data = test_data; data->test_notify = test_notify; g_test_add_data_func_full (test_path, data, clutter_test_func_wrapper, g_free); } /** * clutter_test_run: * * Runs the test suite using the units added by calling * clutter_test_add(). * * The typical test suite is composed of a list of functions * called by clutter_test_run(), for instance: * * |[ * static void unit_foo (void) { ... } * * static void unit_bar (void) { ... } * * static void unit_baz (void) { ... } * * int * main (int argc, char *argv[]) * { * clutter_test_init (&argc, &argv); * * clutter_test_add ("/unit/foo", unit_foo); * clutter_test_add ("/unit/bar", unit_bar); * clutter_test_add ("/unit/baz", unit_baz); * * return clutter_test_run (); * } * ]| * * Return value: the exit code for the test suite * * Since: 1.18 */ int clutter_test_run (void) { int res; g_assert (test_environ != NULL); res = g_test_run (); g_free (test_environ); return res; } typedef struct { ClutterActor *stage; graphene_point_t point; gpointer result; guint check_actor : 1; guint check_color : 1; guint was_painted : 1; } ValidateData; static gboolean validate_stage (gpointer data_) { ValidateData *data = data_; if (data->check_actor) { data->result = clutter_stage_get_actor_at_pos (CLUTTER_STAGE (data->stage), CLUTTER_PICK_ALL, data->point.x, data->point.y); } if (data->check_color) { data->result = clutter_stage_read_pixels (CLUTTER_STAGE (data->stage), data->point.x, data->point.y, 1, 1); } if (!g_test_verbose ()) { clutter_actor_hide (data->stage); data->was_painted = TRUE; } return G_SOURCE_REMOVE; } static gboolean on_key_press_event (ClutterActor *stage, ClutterEvent *event, gpointer data_) { ValidateData *data = data_; if (data->stage == stage && clutter_event_get_key_symbol (event) == CLUTTER_KEY_Escape) { clutter_actor_hide (stage); data->was_painted = TRUE; } return CLUTTER_EVENT_PROPAGATE; } /** * clutter_test_check_actor_at_point: * @stage: a #ClutterStage * @point: coordinates to check * @actor: the expected actor at the given coordinates * @result: (out) (nullable): actor at the coordinates * * Checks the given coordinates of the @stage and compares the * actor found there with the given @actor. * * Returns: %TRUE if the actor at the given coordinates matches * * Since: 1.18 */ gboolean clutter_test_check_actor_at_point (ClutterActor *stage, const graphene_point_t *point, ClutterActor *actor, ClutterActor **result) { ValidateData *data; gulong press_id = 0; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (point != NULL, FALSE); g_return_val_if_fail (CLUTTER_IS_ACTOR (stage), FALSE); g_return_val_if_fail (result != NULL, FALSE); data = g_new0 (ValidateData, 1); data->stage = stage; data->point = *point; data->check_actor = TRUE; if (g_test_verbose ()) { g_printerr ("Press ESC to close the stage and resume the test\n"); press_id = g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press_event), data); } clutter_actor_show (stage); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, validate_stage, data, NULL); while (!data->was_painted) g_main_context_iteration (NULL, TRUE); *result = data->result; g_clear_signal_handler (&press_id, stage); g_free (data); return *result == actor; } /** * clutter_test_check_color_at_point: * @stage: a #ClutterStage * @point: coordinates to check * @color: expected color * @result: (out caller-allocates): color at the given coordinates * * Checks the color at the given coordinates on @stage, and matches * it with the red, green, and blue channels of @color. The alpha * component of @color and @result is ignored. * * Returns: %TRUE if the colors match * * Since: 1.18 */ gboolean clutter_test_check_color_at_point (ClutterActor *stage, const graphene_point_t *point, const ClutterColor *color, ClutterColor *result) { ValidateData *data; gboolean retval; guint8 *buffer; gulong press_id = 0; g_return_val_if_fail (CLUTTER_IS_STAGE (stage), FALSE); g_return_val_if_fail (point != NULL, FALSE); g_return_val_if_fail (color != NULL, FALSE); g_return_val_if_fail (result != NULL, FALSE); data = g_new0 (ValidateData, 1); data->stage = stage; data->point = *point; data->check_color = TRUE; if (g_test_verbose ()) { g_printerr ("Press ESC to close the stage and resume the test\n"); press_id = g_signal_connect (stage, "key-press-event", G_CALLBACK (on_key_press_event), data); } clutter_actor_show (stage); clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, validate_stage, data, NULL); while (!data->was_painted) g_main_context_iteration (NULL, TRUE); g_clear_signal_handler (&press_id, stage); buffer = data->result; clutter_color_init (result, buffer[0], buffer[1], buffer[2], 255); /* we only check the color channels, so we can't use clutter_color_equal() */ retval = buffer[0] == color->red && buffer[1] == color->green && buffer[2] == color->blue; g_free (data->result); g_free (data); return retval; } muffin-6.4.1/src/tests/mutter-all.test.in0000664000175000017500000000021314723361714017263 0ustar fabiofabio[Test] Description=All Mutter tests Exec=@libexecdir@/installed-tests/mutter-@apiversion@/mutter-test-runner --all Type=session Output=TAP muffin-6.4.1/src/tests/monitor-unit-tests.h0000664000175000017500000000167514723361714017661 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef MONITOR_UNIT_TESTS_H #define MONITOR_UNIT_TESTS_H void init_monitor_tests (void); void pre_run_monitor_tests (void); void finish_monitor_tests (void); #endif /* MONITOR_UNIT_TESTS_H */ muffin-6.4.1/src/tests/wayland-unit-tests.h0000664000175000017500000000152614723361714017624 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef WAYLAND_UNIT_TESTS_H #define WAYLAND_UNIT_TESTS_H void init_wayland_tests (void); void pre_run_wayland_tests (void); #endif /* WAYLAND_UNIT_TESTS_H */ muffin-6.4.1/src/tests/meta-monitor-manager-test.h0000664000175000017500000000363714723361714021055 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MONITOR_MANAGER_TEST_H #define META_MONITOR_MANAGER_TEST_H #include "backends/meta-monitor-manager-private.h" typedef struct _MetaMonitorTestSetup { GList *modes; GList *outputs; GList *crtcs; } MetaMonitorTestSetup; typedef struct _MetaOutputTest { float scale; } MetaOutputTest; #define META_TYPE_MONITOR_MANAGER_TEST (meta_monitor_manager_test_get_type ()) G_DECLARE_FINAL_TYPE (MetaMonitorManagerTest, meta_monitor_manager_test, META, MONITOR_MANAGER_TEST, MetaMonitorManager) void meta_monitor_manager_test_init_test_setup (MetaMonitorTestSetup *test_setup); void meta_monitor_manager_test_read_current (MetaMonitorManager *manager); void meta_monitor_manager_test_emulate_hotplug (MetaMonitorManagerTest *manager_test, MetaMonitorTestSetup *test_setup); void meta_monitor_manager_test_set_handles_transforms (MetaMonitorManagerTest *manager_test, gboolean handles_transforms); int meta_monitor_manager_test_get_tiled_monitor_count (MetaMonitorManagerTest *manager_test); #endif /* META_MONITOR_MANAGER_TEST_H */ muffin-6.4.1/src/tests/monitor-store-unit-tests.c0000664000175000017500000005664314723361714021013 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/monitor-store-unit-tests.h" #include "backends/meta-backend-private.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-monitor-config-manager.h" #include "backends/meta-monitor-manager-private.h" #include "tests/monitor-test-utils.h" #define MAX_N_MONITORS 10 #define MAX_N_LOGICAL_MONITORS 10 #define MAX_N_CONFIGURATIONS 10 typedef struct _MonitorTestCaseMonitorMode { int width; int height; float refresh_rate; MetaCrtcModeFlag flags; } MonitorTestCaseMonitorMode; typedef struct _MonitorTestCaseMonitor { const char *connector; const char *vendor; const char *product; const char *serial; MonitorTestCaseMonitorMode mode; gboolean is_underscanning; } MonitorTestCaseMonitor; typedef struct _MonitorTestCaseLogicalMonitor { MetaRectangle layout; float scale; MetaMonitorTransform transform; gboolean is_primary; gboolean is_presentation; MonitorTestCaseMonitor monitors[MAX_N_MONITORS]; int n_monitors; } MonitorTestCaseLogicalMonitor; typedef struct _MonitorStoreTestConfiguration { MonitorTestCaseLogicalMonitor logical_monitors[MAX_N_LOGICAL_MONITORS]; int n_logical_monitors; } MonitorStoreTestConfiguration; typedef struct _MonitorStoreTestExpect { MonitorStoreTestConfiguration configurations[MAX_N_CONFIGURATIONS]; int n_configurations; } MonitorStoreTestExpect; static MetaMonitorsConfigKey * create_config_key_from_expect (MonitorStoreTestConfiguration *expect_config) { MetaMonitorsConfigKey *config_key; GList *monitor_specs; int i; monitor_specs = NULL; for (i = 0; i < expect_config->n_logical_monitors; i++) { int j; for (j = 0; j < expect_config->logical_monitors[i].n_monitors; j++) { MetaMonitorSpec *monitor_spec; MonitorTestCaseMonitor *test_monitor = &expect_config->logical_monitors[i].monitors[j]; monitor_spec = g_new0 (MetaMonitorSpec, 1); monitor_spec->connector = g_strdup (test_monitor->connector); monitor_spec->vendor = g_strdup (test_monitor->vendor); monitor_spec->product = g_strdup (test_monitor->product); monitor_spec->serial = g_strdup (test_monitor->serial); monitor_specs = g_list_prepend (monitor_specs, monitor_spec); } } g_assert_nonnull (monitor_specs); monitor_specs = g_list_sort (monitor_specs, (GCompareFunc) meta_monitor_spec_compare); config_key = g_new0 (MetaMonitorsConfigKey, 1); *config_key = (MetaMonitorsConfigKey) { .monitor_specs = monitor_specs }; return config_key; } static void check_monitor_configuration (MetaMonitorConfigStore *config_store, MonitorStoreTestConfiguration *config_expect) { MetaMonitorsConfigKey *config_key; MetaMonitorsConfig *config; GList *l; int i; config_key = create_config_key_from_expect (config_expect); config = meta_monitor_config_store_lookup (config_store, config_key); g_assert_nonnull (config); g_assert (meta_monitors_config_key_equal (config->key, config_key)); meta_monitors_config_key_free (config_key); g_assert_cmpuint (g_list_length (config->logical_monitor_configs), ==, config_expect->n_logical_monitors); for (l = config->logical_monitor_configs, i = 0; l; l = l->next, i++) { MetaLogicalMonitorConfig *logical_monitor_config = l->data; GList *k; int j; g_assert (meta_rectangle_equal (&logical_monitor_config->layout, &config_expect->logical_monitors[i].layout)); g_assert_cmpfloat (logical_monitor_config->scale, ==, config_expect->logical_monitors[i].scale); g_assert_cmpint (logical_monitor_config->transform, ==, config_expect->logical_monitors[i].transform); g_assert_cmpint (logical_monitor_config->is_primary, ==, config_expect->logical_monitors[i].is_primary); g_assert_cmpint (logical_monitor_config->is_presentation, ==, config_expect->logical_monitors[i].is_presentation); g_assert_cmpint ((int) g_list_length (logical_monitor_config->monitor_configs), ==, config_expect->logical_monitors[i].n_monitors); for (k = logical_monitor_config->monitor_configs, j = 0; k; k = k->next, j++) { MetaMonitorConfig *monitor_config = k->data; MonitorTestCaseMonitor *test_monitor = &config_expect->logical_monitors[i].monitors[j]; g_assert_cmpstr (monitor_config->monitor_spec->connector, ==, test_monitor->connector); g_assert_cmpstr (monitor_config->monitor_spec->vendor, ==, test_monitor->vendor); g_assert_cmpstr (monitor_config->monitor_spec->product, ==, test_monitor->product); g_assert_cmpstr (monitor_config->monitor_spec->serial, ==, test_monitor->serial); g_assert_cmpint (monitor_config->mode_spec->width, ==, test_monitor->mode.width); g_assert_cmpint (monitor_config->mode_spec->height, ==, test_monitor->mode.height); g_assert_cmpfloat (monitor_config->mode_spec->refresh_rate, ==, test_monitor->mode.refresh_rate); g_assert_cmpint (monitor_config->mode_spec->flags, ==, test_monitor->mode.flags); g_assert_cmpint (monitor_config->enable_underscanning, ==, test_monitor->is_underscanning); } } } static void check_monitor_configurations (MonitorStoreTestExpect *expect) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); int i; g_assert_cmpint (meta_monitor_config_store_get_config_count (config_store), ==, expect->n_configurations); for (i = 0; i < expect->n_configurations; i++) check_monitor_configuration (config_store, &expect->configurations[i]); } static void meta_test_monitor_store_single (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 1920, .height = 1080 }, .scale = 1, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; set_custom_monitor_config ("single.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_vertical (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, }, { .layout = { .x = 0, .y = 768, .width = 800, .height = 600 }, .scale = 1, .is_primary = FALSE, .is_presentation = FALSE, .monitors = { { .connector = "DP-2", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 2 } }, .n_configurations = 1 }; set_custom_monitor_config ("vertical.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_primary (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1, .is_primary = FALSE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, }, { .layout = { .x = 1024, .y = 0, .width = 800, .height = 600 }, .scale = 1, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-2", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 2 } }, .n_configurations = 1 }; set_custom_monitor_config ("primary.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_underscanning (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .is_underscanning = TRUE, .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, }, }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; set_custom_monitor_config ("underscanning.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_scale (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 960, .height = 540 }, .scale = 2, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } set_custom_monitor_config ("scale.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_fractional_scale (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1.5, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1200, .height = 900, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } set_custom_monitor_config ("fractional-scale.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_high_precision_fractional_scale (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 744, .height = 558 }, .scale = 1.3763440847396851, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } set_custom_monitor_config ("high-precision-fractional-scale.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_mirrored (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1, .is_primary = TRUE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } }, { .connector = "DP-2", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 2, } }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; set_custom_monitor_config ("mirrored.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_first_rotated (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_270, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, }, { .layout = { .x = 768, .y = 0, .width = 1024, .height = 768 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_NORMAL, .is_primary = FALSE, .is_presentation = FALSE, .monitors = { { .connector = "DP-2", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 2 } }, .n_configurations = 1 }; set_custom_monitor_config ("first-rotated.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_second_rotated (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 256, .width = 1024, .height = 768 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_NORMAL, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, }, { .layout = { .x = 1024, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90, .is_primary = FALSE, .is_presentation = FALSE, .monitors = { { .connector = "DP-2", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } } }, .n_monitors = 1, } }, .n_logical_monitors = 2 } }, .n_configurations = 1 }; set_custom_monitor_config ("second-rotated.xml"); check_monitor_configurations (&expect); } static void meta_test_monitor_store_interlaced (void) { MonitorStoreTestExpect expect = { .configurations = { { .logical_monitors = { { .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1, .is_primary = TRUE, .is_presentation = FALSE, .monitors = { { .connector = "DP-1", .vendor = "MetaProduct's Inc.", .product = "MetaMonitor", .serial = "0x123456", .mode = { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .flags = META_CRTC_MODE_FLAG_INTERLACE, } } }, .n_monitors = 1, }, }, .n_logical_monitors = 1 } }, .n_configurations = 1 }; set_custom_monitor_config ("interlaced.xml"); check_monitor_configurations (&expect); } void init_monitor_store_tests (void) { g_test_add_func ("/backends/monitor-store/single", meta_test_monitor_store_single); g_test_add_func ("/backends/monitor-store/vertical", meta_test_monitor_store_vertical); g_test_add_func ("/backends/monitor-store/primary", meta_test_monitor_store_primary); g_test_add_func ("/backends/monitor-store/underscanning", meta_test_monitor_store_underscanning); g_test_add_func ("/backends/monitor-store/scale", meta_test_monitor_store_scale); g_test_add_func ("/backends/monitor-store/fractional-scale", meta_test_monitor_store_fractional_scale); g_test_add_func ("/backends/monitor-store/high-precision-fractional-scale", meta_test_monitor_store_high_precision_fractional_scale); g_test_add_func ("/backends/monitor-store/mirrored", meta_test_monitor_store_mirrored); g_test_add_func ("/backends/monitor-store/first-rotated", meta_test_monitor_store_first_rotated); g_test_add_func ("/backends/monitor-store/second-rotated", meta_test_monitor_store_second_rotated); g_test_add_func ("/backends/monitor-store/interlaced", meta_test_monitor_store_interlaced); } muffin-6.4.1/src/tests/wayland-unit-tests.c0000664000175000017500000001426114723361714017617 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/wayland-unit-tests.h" #include #include #include "wayland/meta-wayland.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-private.h" #include "test-driver-server-protocol.h" typedef struct _WaylandTestClient { GSubprocess *subprocess; char *path; GMainLoop *main_loop; } WaylandTestClient; static char * get_test_client_path (const char *test_client_name) { return g_test_build_filename (G_TEST_BUILT, "src", "tests", "wayland-test-clients", test_client_name, NULL); } static WaylandTestClient * wayland_test_client_new (const char *test_client_name) { MetaWaylandCompositor *compositor; const char *wayland_display_name; g_autofree char *test_client_path = NULL; g_autoptr (GSubprocessLauncher) launcher = NULL; GSubprocess *subprocess; GError *error = NULL; WaylandTestClient *wayland_test_client; compositor = meta_wayland_compositor_get_default (); wayland_display_name = meta_wayland_get_wayland_display_name (compositor); test_client_path = get_test_client_path (test_client_name); launcher = g_subprocess_launcher_new (G_SUBPROCESS_FLAGS_NONE); g_subprocess_launcher_setenv (launcher, "WAYLAND_DISPLAY", wayland_display_name, TRUE); subprocess = g_subprocess_launcher_spawn (launcher, &error, test_client_path, NULL); if (!subprocess) { g_error ("Failed to launch Wayland test client '%s': %s", test_client_path, error->message); } wayland_test_client = g_new0 (WaylandTestClient, 1); wayland_test_client->subprocess = subprocess; wayland_test_client->path = g_strdup (test_client_name); wayland_test_client->main_loop = g_main_loop_new (NULL, FALSE); return wayland_test_client; } static void wayland_test_client_finished (GObject *source_object, GAsyncResult *res, gpointer user_data) { WaylandTestClient *wayland_test_client = user_data; GError *error = NULL; if (!g_subprocess_wait_finish (wayland_test_client->subprocess, res, &error)) { g_error ("Failed to wait for Wayland test client '%s': %s", wayland_test_client->path, error->message); } g_main_loop_quit (wayland_test_client->main_loop); } static void wayland_test_client_finish (WaylandTestClient *wayland_test_client) { g_subprocess_wait_async (wayland_test_client->subprocess, NULL, wayland_test_client_finished, wayland_test_client); g_main_loop_run (wayland_test_client->main_loop); g_assert_true (g_subprocess_get_successful (wayland_test_client->subprocess)); g_main_loop_unref (wayland_test_client->main_loop); g_free (wayland_test_client->path); g_object_unref (wayland_test_client->subprocess); g_free (wayland_test_client); } static void subsurface_remap_toplevel (void) { WaylandTestClient *wayland_test_client; wayland_test_client = wayland_test_client_new ("subsurface-remap-toplevel"); wayland_test_client_finish (wayland_test_client); } static void on_actor_destroyed (ClutterActor *actor, struct wl_resource *callback) { wl_callback_send_done (callback, 0); wl_resource_destroy (callback); } static void sync_actor_destroy (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandActorSurface *actor_surface; MetaSurfaceActor *actor; struct wl_resource *callback; g_assert_nonnull (surface); actor_surface = (MetaWaylandActorSurface *) surface->role; g_assert_nonnull (actor_surface); actor = meta_wayland_actor_surface_get_actor (actor_surface); g_assert_nonnull (actor); callback = wl_resource_create (client, &wl_callback_interface, 1, id); g_signal_connect (actor, "destroy", G_CALLBACK (on_actor_destroyed), callback); } static const struct test_driver_interface meta_test_driver_interface = { sync_actor_destroy, }; static void bind_test_driver (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &test_driver_interface, version, id); wl_resource_set_implementation (resource, &meta_test_driver_interface, NULL, NULL); } void pre_run_wayland_tests (void) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); g_assert_nonnull (compositor); if (wl_global_create (compositor->wayland_display, &test_driver_interface, 1, NULL, bind_test_driver) == NULL) g_error ("Failed to register a global wl-subcompositor object"); } void init_wayland_tests (void) { g_test_add_func ("/wayland/subsurface/remap-toplevel", subsurface_remap_toplevel); } muffin-6.4.1/src/tests/monitor-configs/0000775000175000017500000000000014723361714017010 5ustar fabiofabiomuffin-6.4.1/src/tests/monitor-configs/second-rotated.xml0000664000175000017500000000166114723361714022451 0ustar fabiofabio 0 256 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 1024 0 left no DP-2 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/single.xml0000664000175000017500000000073614723361714021021 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1920 1080 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/fractional-scale.xml0000664000175000017500000000076614723361714022752 0ustar fabiofabio 0 0 yes 1.5 DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1200 900 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/first-rotated.xml0000664000175000017500000000201014723361714022312 0ustar fabiofabio 0 0 yes right no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 768 0 normal no DP-2 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/scale.xml0000664000175000017500000000076514723361714020631 0ustar fabiofabio 0 0 yes 2 DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1920 1080 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/tiled-custom-resolution.xml0000664000175000017500000000076314723361714024352 0ustar fabiofabio 0 0 yes 2 DP-1 MetaProduct's Inc. MetaMonitor 0x123456 640 480 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/oneoff.xml0000664000175000017500000000137214723361714021011 0ustar fabiofabio 0 0 1 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 DP-2 MetaProduct's Inc. MetaMonitor 0x654321 muffin-6.4.1/src/tests/monitor-configs/interlaced.xml0000664000175000017500000000076714723361714021656 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 interlace muffin-6.4.1/src/tests/monitor-configs/mirrored.xml0000664000175000017500000000141214723361714021353 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 DP-2 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/vertical.xml0000664000175000017500000000152614723361714021347 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 0 768 DP-2 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/non-preferred-tiled-custom-resolution.xml0000664000175000017500000000101414723361714027104 0ustar fabiofabio 0 0 yes DP-2 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60 muffin-6.4.1/src/tests/monitor-configs/tiled.xml0000664000175000017500000000076314723361714020641 0ustar fabiofabio 0 0 yes 2 DP-1 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/lid-scale.xml0000664000175000017500000000076614723361714021400 0ustar fabiofabio 0 0 yes 2 eDP-1 MetaProduct's Inc. MetaMonitor 0x123456 1920 1080 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/second-rotated-tiled.xml0000664000175000017500000000166014723361714023547 0ustar fabiofabio 0 256 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 1024 0 left no DP-2 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/lid-switch.xml0000664000175000017500000000372514723361714021610 0ustar fabiofabio 0 0 yes eDP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 1024 0 right no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 0 0 yes left no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 0 0 yes right no eDP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/underscanning.xml0000664000175000017500000000100114723361714022360 0ustar fabiofabio 0 0 yes DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 yes muffin-6.4.1/src/tests/monitor-configs/high-precision-fractional-scale.xml0000664000175000017500000000100514723361714025643 0ustar fabiofabio 0 0 yes 1.3763440847396851 DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 muffin-6.4.1/src/tests/monitor-configs/primary.xml0000664000175000017500000000156314723361714021222 0ustar fabiofabio 0 0 no DP-1 MetaProduct's Inc. MetaMonitor 0x123456 1024 768 60.000495910644531 1024 0 yes DP-2 MetaProduct's Inc. MetaMonitor 0x123456 800 600 60.000495910644531 muffin-6.4.1/src/tests/monitor-transform-tests.c0000664000175000017500000000564114723361714020705 0ustar fabiofabio/* * Copyright (C) 2020 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/monitor-transform-tests.h" #include "backends/meta-monitor-transform.h" static void test_transform (void) { const struct { MetaMonitorTransform transform; MetaMonitorTransform other; MetaMonitorTransform expect; } tests[] = { { .transform = META_MONITOR_TRANSFORM_NORMAL, .other = META_MONITOR_TRANSFORM_90, .expect = META_MONITOR_TRANSFORM_90, }, { .transform = META_MONITOR_TRANSFORM_NORMAL, .other = META_MONITOR_TRANSFORM_FLIPPED_90, .expect = META_MONITOR_TRANSFORM_FLIPPED_90, }, { .transform = META_MONITOR_TRANSFORM_90, .other = META_MONITOR_TRANSFORM_90, .expect = META_MONITOR_TRANSFORM_180, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED_90, .other = META_MONITOR_TRANSFORM_90, .expect = META_MONITOR_TRANSFORM_FLIPPED_180, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED_90, .other = META_MONITOR_TRANSFORM_180, .expect = META_MONITOR_TRANSFORM_FLIPPED_270, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED_180, .other = META_MONITOR_TRANSFORM_FLIPPED_180, .expect = META_MONITOR_TRANSFORM_NORMAL, }, { .transform = META_MONITOR_TRANSFORM_NORMAL, .other = -META_MONITOR_TRANSFORM_90, .expect = META_MONITOR_TRANSFORM_270, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED, .other = -META_MONITOR_TRANSFORM_90, .expect = META_MONITOR_TRANSFORM_FLIPPED_270, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED_180, .other = -META_MONITOR_TRANSFORM_270, .expect = META_MONITOR_TRANSFORM_FLIPPED_270, }, { .transform = META_MONITOR_TRANSFORM_FLIPPED_180, .other = -META_MONITOR_TRANSFORM_FLIPPED_180, .expect = META_MONITOR_TRANSFORM_NORMAL, }, }; int i; for (i = 0; i < G_N_ELEMENTS (tests); i++) { MetaMonitorTransform result; result = meta_monitor_transform_transform (tests[i].transform, tests[i].other); g_assert_cmpint (result, ==, tests[i].expect); } } void init_monitor_transform_tests (void) { g_test_add_func ("/util/monitor-transform/transform", test_transform); } muffin-6.4.1/src/tests/stacking/0000775000175000017500000000000014723361714015476 5ustar fabiofabiomuffin-6.4.1/src/tests/stacking/closed-transient-no-input-no-take-focus-parent.metatest0000664000175000017500000000043414723361714030234 0ustar fabiofabionew_client 1 x11 create 1/1 show 1/1 create 1/2 csd set_parent 1/2 1 can_take_focus 1/2 false accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 show 1/3 wait assert_focused 1/3 assert_stacking 1/1 1/2 1/3 destroy 1/3 wait assert_focused 1/1 assert_stacking 1/1 1/2 muffin-6.4.1/src/tests/stacking/client-side-decorated.metatest0000664000175000017500000000037414723361714023402 0ustar fabiofabionew_client 1 x11 create 1/1 show 1/1 create 1/2 csd show 1/2 wait assert_stacking 1/1 1/2 destroy 1/2 wait assert_stacking 1/1 create 1/2 csd show 1/2 create 1/3 csd show 1/3 wait assert_stacking 1/1 1/2 1/3 destroy 1/2 wait assert_stacking 1/1 1/3 muffin-6.4.1/src/tests/stacking/closed-transient-no-input-parents.metatest0000664000175000017500000000112014723361714025737 0ustar fabiofabionew_client 0 x11 create 0/1 show 0/1 new_client 1 x11 create 1/1 show 1/1 create 1/2 csd set_parent 1/2 1 accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 accept_focus 1/3 false show 1/3 create 1/4 csd set_parent 1/4 3 accept_focus 1/4 false show 1/4 create 1/5 csd set_parent 1/5 3 show 1/5 wait assert_focused 1/5 assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5 destroy 1/5 dispatch assert_focused none assert_stacking 0/1 1/1 1/2 1/3 1/4 sleep 600 assert_focused 1/1 assert_stacking 0/1 1/1 1/2 1/3 1/4 destroy 1/3 wait assert_focused 1/1 assert_stacking 0/1 1/1 1/2 1/4 muffin-6.4.1/src/tests/stacking/closed-transient-no-input-no-take-focus-parents.metatest0000664000175000017500000000057714723361714030427 0ustar fabiofabionew_client 2 x11 create 2/1 show 2/1 wait new_client 1 x11 create 1/1 accept_focus 1/1 false can_take_focus 1/1 false show 1/1 create 1/2 csd set_parent 1/2 1 can_take_focus 1/2 false accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 show 1/3 wait assert_focused 1/3 assert_stacking 2/1 1/1 1/2 1/3 destroy 1/3 wait assert_stacking 1/1 1/2 2/1 assert_focused 2/1 ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootmuffin-6.4.1/src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.metatestmuffin-6.4.1/src/tests/stacking/closed-transient-no-input-parent-delayed-focus-default-cancelled.met0000664000175000017500000000066314723361714032602 0ustar fabiofabionew_client 2 x11 create 2/1 show 2/1 new_client 1 x11 create 1/1 show 1/1 create 1/2 csd set_parent 1/2 1 accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 show 1/3 wait assert_focused 1/3 assert_stacking 2/1 1/1 1/2 1/3 destroy 1/3 dispatch assert_focused none assert_stacking 2/1 1/1 1/2 activate 2/1 wait assert_focused 2/1 assert_stacking 1/1 1/2 2/1 sleep 250 assert_focused 2/1 assert_stacking 1/1 1/2 2/1 muffin-6.4.1/src/tests/stacking/minimized.metatest0000664000175000017500000000050014723361714021226 0ustar fabiofabionew_client 1 x11 create 1/1 show 1/1 create 1/2 show 1/2 wait assert_stacking 1/1 1/2 minimize 1/2 wait assert_stacking 1/2 | 1/1 # unminimize doesn't work for GTK+ currently, because GTK+ expects # to be able to de-iconify with MapWindow, but the window is already # mapped. activate 1/2 wait assert_stacking 1/1 1/2 muffin-6.4.1/src/tests/stacking/basic-x11.metatest0000664000175000017500000000040214723361714020732 0ustar fabiofabionew_client 1 x11 create 1/1 show 1/1 create 1/2 show 1/2 wait assert_stacking 1/1 1/2 activate 1/1 wait assert_stacking 1/2 1/1 activate 1/2 wait assert_stacking 1/1 1/2 local_activate 1/1 assert_stacking 1/2 1/1 local_activate 1/2 assert_stacking 1/1 1/2 muffin-6.4.1/src/tests/stacking/set-parent.metatest0000664000175000017500000000026514723361714021333 0ustar fabiofabionew_client 1 wayland create 1/1 show 1/1 create 1/2 show 1/2 wait assert_stacking 1/1 1/2 set_parent 1/1 2 wait assert_stacking 1/2 1/1 local_activate 1/2 assert_stacking 1/2 1/1 muffin-6.4.1/src/tests/stacking/basic-wayland.metatest0000664000175000017500000000065014723361714021765 0ustar fabiofabionew_client 1 wayland create 1/1 show 1/1 create 1/2 show 1/2 wait assert_stacking 1/1 1/2 # Currently Wayland clients have no wait to bring themselves to the user's # attention; gtk_window_present() is a no-op with the X11 backend of GTK+ # activate 1/1 # wait # assert_stacking 1/2 1/1 # activate 1/2 # wait # assert_stacking 1/1 1/2 local_activate 1/1 assert_stacking 1/2 1/1 local_activate 1/2 assert_stacking 1/1 1/2 muffin-6.4.1/src/tests/stacking/closed-transient-only-take-focus-parents.metatest0000664000175000017500000000073714723361714027223 0ustar fabiofabionew_client 0 x11 create 0/1 show 0/1 new_client 1 x11 create 1/1 accept_focus 1/1 false can_take_focus 1/1 true accept_take_focus 1/1 true show 1/1 create 1/2 csd set_parent 1/2 1 accept_focus 1/2 false can_take_focus 1/2 true accept_take_focus 1/2 true show 1/2 create 1/3 set_parent 1/3 2 show 1/3 assert_focused 1/3 assert_stacking 0/1 1/1 1/2 1/3 destroy 1/3 wait assert_focused 1/2 assert_stacking 0/1 1/1 1/2 sleep 150 assert_focused 1/2 assert_stacking 0/1 1/1 1/2 muffin-6.4.1/src/tests/stacking/closed-transient.metatest0000664000175000017500000000030614723361714022523 0ustar fabiofabionew_client 1 wayland create 1/1 show 1/1 new_client 2 wayland create 2/1 show 2/1 create 1/2 show 1/2 set_parent 1/2 1 wait assert_stacking 1/1 2/1 1/2 destroy 1/2 wait assert_stacking 2/1 1/1 muffin-6.4.1/src/tests/stacking/closed-transient-no-input-parent.metatest0000664000175000017500000000056014723361714025563 0ustar fabiofabionew_client 2 x11 create 2/1 show 2/1 new_client 1 x11 create 1/1 show 1/1 create 1/2 csd set_parent 1/2 1 accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 show 1/3 wait assert_focused 1/3 assert_stacking 2/1 1/1 1/2 1/3 destroy 1/3 dispatch assert_focused none assert_stacking 2/1 1/1 1/2 sleep 150 assert_focused 1/1 assert_stacking 2/1 1/1 1/2 ././@LongLink0000644000000000000000000000015200000000000011601 Lustar rootrootmuffin-6.4.1/src/tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.metatestmuffin-6.4.1/src/tests/stacking/closed-transient-no-input-parents-queued-default-focus-destroyed.met0000664000175000017500000000103414723361714032727 0ustar fabiofabionew_client 0 x11 create 0/1 show 0/1 new_client 1 x11 create 1/1 show 1/1 create 1/2 csd set_parent 1/2 1 accept_focus 1/2 false show 1/2 create 1/3 csd set_parent 1/3 2 accept_focus 1/3 false show 1/3 create 1/4 csd set_parent 1/4 3 accept_focus 1/4 false show 1/4 create 1/5 csd set_parent 1/5 3 show 1/5 wait assert_focused 1/5 assert_stacking 0/1 1/1 1/2 1/3 1/4 1/5 destroy 1/5 dispatch assert_focused none assert_stacking 0/1 1/1 1/2 1/3 1/4 destroy 1/2 dispatch sleep 450 assert_focused 1/1 assert_stacking 0/1 1/1 1/3 1/4 muffin-6.4.1/src/tests/stacking/set-override-redirect-parent.metatest0000664000175000017500000000060414723361714024744 0ustar fabiofabionew_client 1 x11 create 1/1 override show 1/1 create 1/2 set_parent 1/2 1 show 1/2 create 1/3 set_parent 1/3 2 show 1/3 new_client 2 x11 create 2/1 show 2/1 create 2/2 override set_parent 2/2 1 show 2/2 create 2/3 set_parent 2/3 2 show 2/3 new_client 3 x11 create 3/1 override show 3/1 create 3/2 override set_parent 3/2 1 show 3/2 create 3/3 override set_parent 3/3 2 show 3/3 muffin-6.4.1/src/tests/stacking/mixed-windows.metatest0000664000175000017500000000047314723361714022050 0ustar fabiofabionew_client w wayland new_client x x11 create w/1 show w/1 create w/2 show w/2 wait create x/1 show x/1 create x/2 show x/2 wait assert_stacking w/1 w/2 x/1 x/2 local_activate w/1 assert_stacking w/2 x/1 x/2 w/1 local_activate x/1 assert_stacking w/2 x/2 w/1 x/1 lower x/1 wait assert_stacking x/1 w/2 x/2 w/1 muffin-6.4.1/src/tests/stacking/set-parent-exported.metatest0000664000175000017500000000026314723361714023161 0ustar fabiofabionew_client 1 wayland create 1/1 show 1/1 create 1/2 show 1/2 wait set_parent_exported 1/1 2 wait assert_stacking 1/2 1/1 local_activate 1/2 assert_stacking 1/2 1/1 destroy 1/2 muffin-6.4.1/src/tests/stacking/override-redirect.metatest0000664000175000017500000000033414723361714022664 0ustar fabiofabionew_client 1 x11 create 1/1 show 1/1 create 1/2 override show 1/2 wait assert_stacking 1/1 1/2 activate 1/1 wait assert_stacking 1/1 1/2 lower 1/2 wait assert_stacking 1/2 | 1/1 raise 1/2 wait assert_stacking 1/1 1/2 muffin-6.4.1/src/tests/monitor-unit-tests.c0000664000175000017500000050476014723361714017657 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "tests/monitor-unit-tests.h" #include "backends/meta-backend-private.h" #include "backends/meta-crtc.h" #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-config-migration.h" #include "backends/meta-monitor-config-store.h" #include "backends/meta-output.h" #include "core/window-private.h" #include "meta-backend-test.h" #include "tests/meta-monitor-manager-test.h" #include "tests/monitor-test-utils.h" #include "tests/test-utils.h" #include "x11/meta-x11-display-private.h" #define ALL_TRANSFORMS ((1 << (META_MONITOR_TRANSFORM_FLIPPED_270 + 1)) - 1) #define MAX_N_MODES 10 #define MAX_N_OUTPUTS 10 #define MAX_N_CRTCS 10 #define MAX_N_MONITORS 10 #define MAX_N_LOGICAL_MONITORS 10 /* * The following structures are used to define test cases. * * Each test case consists of a test case setup and a test case expectaction. * and a expected result, consisting * of an array of monitors, logical monitors and a screen size. * * TEST CASE SETUP: * * A test case setup consists of an array of modes, an array of outputs and an * array of CRTCs. * * A mode has a width and height in pixels, and a refresh rate in updates per * second. * * An output has an array of available modes, and a preferred mode. Modes are * defined as indices into the modes array of the test case setup. * * It also has CRTc and an array of possible CRTCs. Crtcs are defined as indices * into the CRTC array. The CRTC value -1 means no CRTC. * * It also has various meta data, such as physical dimension, tile info and * scale. * * A CRTC only has a current mode. A mode is defined as an index into the modes * array. * * * TEST CASE EXPECTS: * * A test case expects consists of an array of monitors, an array of logical * monitors, a output and crtc count, and a screen width. * * A monitor represents a physical monitor (such as an external monitor, or a * laptop panel etc). A monitor consists of an array of outputs, defined by * indices into the setup output array, an array of monitor modes, and the * current mode, defined by an index into the monitor modes array, and the * physical dimensions. * * A logical monitor represents a region of the total screen area. It contains * the expected layout and a scale. */ typedef enum _MonitorTestFlag { MONITOR_TEST_FLAG_NONE, MONITOR_TEST_FLAG_NO_STORED } MonitorTestFlag; typedef struct _MonitorTestCaseMode { int width; int height; float refresh_rate; MetaCrtcModeFlag flags; } MonitorTestCaseMode; typedef struct _MonitorTestCaseOutput { int crtc; int modes[MAX_N_MODES]; int n_modes; int preferred_mode; int possible_crtcs[MAX_N_CRTCS]; int n_possible_crtcs; int width_mm; int height_mm; MetaTileInfo tile_info; float scale; gboolean is_laptop_panel; gboolean is_underscanning; const char *serial; MetaMonitorTransform panel_orientation_transform; } MonitorTestCaseOutput; typedef struct _MonitorTestCaseCrtc { int current_mode; } MonitorTestCaseCrtc; typedef struct _MonitorTestCaseSetup { MonitorTestCaseMode modes[MAX_N_MODES]; int n_modes; MonitorTestCaseOutput outputs[MAX_N_OUTPUTS]; int n_outputs; MonitorTestCaseCrtc crtcs[MAX_N_CRTCS]; int n_crtcs; } MonitorTestCaseSetup; typedef struct _MonitorTestCaseMonitorCrtcMode { uint64_t output; int crtc_mode; } MetaTestCaseMonitorCrtcMode; typedef struct _MonitorTestCaseMonitorMode { int width; int height; float refresh_rate; MetaCrtcModeFlag flags; MetaTestCaseMonitorCrtcMode crtc_modes[MAX_N_CRTCS]; } MetaMonitorTestCaseMonitorMode; typedef struct _MonitorTestCaseMonitor { uint64_t outputs[MAX_N_OUTPUTS]; int n_outputs; MetaMonitorTestCaseMonitorMode modes[MAX_N_MODES]; int n_modes; int current_mode; int width_mm; int height_mm; gboolean is_underscanning; } MonitorTestCaseMonitor; typedef struct _MonitorTestCaseLogicalMonitor { MetaRectangle layout; float scale; int monitors[MAX_N_MONITORS]; int n_monitors; MetaMonitorTransform transform; } MonitorTestCaseLogicalMonitor; typedef struct _MonitorTestCaseCrtcExpect { MetaMonitorTransform transform; int current_mode; float x; float y; } MonitorTestCaseCrtcExpect; typedef struct _MonitorTestCaseExpect { MonitorTestCaseMonitor monitors[MAX_N_MONITORS]; int n_monitors; MonitorTestCaseLogicalMonitor logical_monitors[MAX_N_LOGICAL_MONITORS]; int n_logical_monitors; int primary_logical_monitor; int n_outputs; MonitorTestCaseCrtcExpect crtcs[MAX_N_CRTCS]; int n_crtcs; int n_tiled_monitors; int screen_width; int screen_height; } MonitorTestCaseExpect; typedef struct _MonitorTestCase { MonitorTestCaseSetup setup; MonitorTestCaseExpect expect; } MonitorTestCase; static MonitorTestCase initial_test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, .x = 1024, } }, .n_crtcs = 2, .screen_width = 1024 * 2, .screen_height = 768 } }; static TestClient *wayland_monitor_test_client = NULL; static TestClient *x11_monitor_test_client = NULL; #define WAYLAND_TEST_CLIENT_NAME "wayland_monitor_test_client" #define WAYLAND_TEST_CLIENT_WINDOW "window1" #define X11_TEST_CLIENT_NAME "x11_monitor_test_client" #define X11_TEST_CLIENT_WINDOW "window1" static gboolean monitor_tests_alarm_filter (MetaX11Display *x11_display, XSyncAlarmNotifyEvent *event, gpointer data) { return test_client_alarm_filter (x11_display, event, x11_monitor_test_client); } static void create_monitor_test_clients (void) { GError *error = NULL; test_wait_for_x11_display (); meta_x11_display_set_alarm_filter (meta_get_display ()->x11_display, monitor_tests_alarm_filter, NULL); wayland_monitor_test_client = test_client_new (WAYLAND_TEST_CLIENT_NAME, META_WINDOW_CLIENT_TYPE_WAYLAND, &error); if (!wayland_monitor_test_client) g_error ("Failed to launch Wayland test client: %s", error->message); x11_monitor_test_client = test_client_new (X11_TEST_CLIENT_NAME, META_WINDOW_CLIENT_TYPE_X11, &error); if (!x11_monitor_test_client) g_error ("Failed to launch X11 test client: %s", error->message); if (!test_client_do (wayland_monitor_test_client, &error, "create", WAYLAND_TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to create Wayland window: %s", error->message); if (!test_client_do (x11_monitor_test_client, &error, "create", X11_TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to create X11 window: %s", error->message); if (!test_client_do (wayland_monitor_test_client, &error, "show", WAYLAND_TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to show the window: %s", error->message); if (!test_client_do (x11_monitor_test_client, &error, "show", X11_TEST_CLIENT_WINDOW, NULL)) g_error ("Failed to show the window: %s", error->message); } static void check_test_client_state (TestClient *test_client) { GError *error = NULL; if (!test_client_wait (test_client, &error)) { g_error ("Failed to sync test client '%s': %s", test_client_get_id (test_client), error->message); } } static void check_monitor_test_clients_state (void) { check_test_client_state (wayland_monitor_test_client); check_test_client_state (x11_monitor_test_client); } static void destroy_monitor_test_clients (void) { GError *error = NULL; if (!test_client_quit (wayland_monitor_test_client, &error)) g_error ("Failed to quit Wayland test client: %s", error->message); if (!test_client_quit (x11_monitor_test_client, &error)) g_error ("Failed to quit X11 test client: %s", error->message); test_client_destroy (wayland_monitor_test_client); test_client_destroy (x11_monitor_test_client); meta_x11_display_set_alarm_filter (meta_get_display ()->x11_display, NULL, NULL); } static MetaOutput * output_from_winsys_id (MetaBackend *backend, uint64_t winsys_id) { MetaGpu *gpu = meta_backend_test_get_gpu (META_BACKEND_TEST (backend)); GList *l; for (l = meta_gpu_get_outputs (gpu); l; l = l->next) { MetaOutput *output = l->data; if (output->winsys_id == winsys_id) return output; } return NULL; } typedef struct _CheckMonitorModeData { MetaBackend *backend; MetaTestCaseMonitorCrtcMode *expect_crtc_mode_iter; } CheckMonitorModeData; static gboolean check_monitor_mode (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { CheckMonitorModeData *data = user_data; MetaBackend *backend = data->backend; MetaOutput *output; MetaCrtcMode *crtc_mode; int expect_crtc_mode_index; output = output_from_winsys_id (backend, data->expect_crtc_mode_iter->output); g_assert (monitor_crtc_mode->output == output); expect_crtc_mode_index = data->expect_crtc_mode_iter->crtc_mode; if (expect_crtc_mode_index == -1) { crtc_mode = NULL; } else { MetaGpu *gpu = meta_output_get_gpu (output); crtc_mode = g_list_nth_data (meta_gpu_get_modes (gpu), expect_crtc_mode_index); } g_assert (monitor_crtc_mode->crtc_mode == crtc_mode); if (crtc_mode) { float refresh_rate; MetaCrtcModeFlag flags; refresh_rate = meta_monitor_mode_get_refresh_rate (mode); flags = meta_monitor_mode_get_flags (mode); g_assert_cmpfloat (refresh_rate, ==, crtc_mode->refresh_rate); g_assert_cmpint (flags, ==, (crtc_mode->flags & HANDLED_CRTC_MODE_FLAGS)); } data->expect_crtc_mode_iter++; return TRUE; } static gboolean check_current_monitor_mode (MetaMonitor *monitor, MetaMonitorMode *mode, MetaMonitorCrtcMode *monitor_crtc_mode, gpointer user_data, GError **error) { CheckMonitorModeData *data = user_data; MetaBackend *backend = data->backend; MetaOutput *output; MetaCrtc *crtc; output = output_from_winsys_id (backend, data->expect_crtc_mode_iter->output); crtc = meta_output_get_assigned_crtc (output); if (data->expect_crtc_mode_iter->crtc_mode == -1) { g_assert_null (crtc); } else { MetaCrtcConfig *crtc_config; MetaLogicalMonitor *logical_monitor; g_assert_nonnull (crtc); crtc_config = crtc->config; g_assert_nonnull (crtc_config); g_assert (monitor_crtc_mode->crtc_mode == crtc_config->mode); logical_monitor = meta_monitor_get_logical_monitor (monitor); g_assert_nonnull (logical_monitor); } data->expect_crtc_mode_iter++; return TRUE; } static MetaLogicalMonitor * logical_monitor_from_layout (MetaMonitorManager *monitor_manager, MetaRectangle *layout) { GList *l; for (l = monitor_manager->logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; if (meta_rectangle_equal (layout, &logical_monitor->rect)) return logical_monitor; } return NULL; } static void check_logical_monitor (MonitorTestCase *test_case, MetaMonitorManager *monitor_manager, MonitorTestCaseLogicalMonitor *test_logical_monitor) { MetaLogicalMonitor *logical_monitor; MetaOutput *primary_output; GList *monitors; GList *l; int i; logical_monitor = logical_monitor_from_layout (monitor_manager, &test_logical_monitor->layout); g_assert_nonnull (logical_monitor); g_assert_cmpint (logical_monitor->rect.x, ==, test_logical_monitor->layout.x); g_assert_cmpint (logical_monitor->rect.y, ==, test_logical_monitor->layout.y); g_assert_cmpint (logical_monitor->rect.width, ==, test_logical_monitor->layout.width); g_assert_cmpint (logical_monitor->rect.height, ==, test_logical_monitor->layout.height); g_assert_cmpfloat (logical_monitor->scale, ==, test_logical_monitor->scale); g_assert_cmpuint (logical_monitor->transform, ==, test_logical_monitor->transform); if (logical_monitor == monitor_manager->primary_logical_monitor) g_assert (meta_logical_monitor_is_primary (logical_monitor)); primary_output = NULL; monitors = meta_logical_monitor_get_monitors (logical_monitor); g_assert_cmpint ((int) g_list_length (monitors), ==, test_logical_monitor->n_monitors); for (i = 0; i < test_logical_monitor->n_monitors; i++) { MetaMonitor *monitor = g_list_nth (monitor_manager->monitors, test_logical_monitor->monitors[i])->data; g_assert_nonnull (g_list_find (monitors, monitor)); } for (l = monitors; l; l = l->next) { MetaMonitor *monitor = l->data; GList *outputs; GList *l_output; outputs = meta_monitor_get_outputs (monitor); for (l_output = outputs; l_output; l_output = l_output->next) { MetaOutput *output = l_output->data; MetaCrtc *crtc; if (output->is_primary) { g_assert_null (primary_output); primary_output = output; } crtc = meta_output_get_assigned_crtc (output); g_assert (!crtc || meta_monitor_get_logical_monitor (monitor) == logical_monitor); g_assert_cmpint (logical_monitor->is_presentation, ==, output->is_presentation); } } if (logical_monitor == monitor_manager->primary_logical_monitor) g_assert_nonnull (primary_output); } static void check_monitor_configuration (MonitorTestCase *test_case) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); MetaGpu *gpu = meta_backend_test_get_gpu (META_BACKEND_TEST (backend)); int tiled_monitor_count; GList *monitors; GList *crtcs; int n_logical_monitors; GList *l; int i; g_assert_cmpint (monitor_manager->screen_width, ==, test_case->expect.screen_width); g_assert_cmpint (monitor_manager->screen_height, ==, test_case->expect.screen_height); g_assert_cmpint ((int) g_list_length (meta_gpu_get_outputs (gpu)), ==, test_case->expect.n_outputs); g_assert_cmpint ((int) g_list_length (meta_gpu_get_crtcs (gpu)), ==, test_case->expect.n_crtcs); tiled_monitor_count = meta_monitor_manager_test_get_tiled_monitor_count (monitor_manager_test); g_assert_cmpint (tiled_monitor_count, ==, test_case->expect.n_tiled_monitors); monitors = meta_monitor_manager_get_monitors (monitor_manager); g_assert_cmpint ((int) g_list_length (monitors), ==, test_case->expect.n_monitors); for (l = monitors, i = 0; l; l = l->next, i++) { MetaMonitor *monitor = l->data; GList *outputs; GList *l_output; int j; int width_mm, height_mm; GList *modes; GList *l_mode; MetaMonitorMode *current_mode; int expected_current_mode_index; MetaMonitorMode *expected_current_mode; outputs = meta_monitor_get_outputs (monitor); g_assert_cmpint ((int) g_list_length (outputs), ==, test_case->expect.monitors[i].n_outputs); for (l_output = outputs, j = 0; l_output; l_output = l_output->next, j++) { MetaOutput *output = l_output->data; uint64_t winsys_id = test_case->expect.monitors[i].outputs[j]; g_assert (output == output_from_winsys_id (backend, winsys_id)); g_assert_cmpint (test_case->expect.monitors[i].is_underscanning, ==, output->is_underscanning); } meta_monitor_get_physical_dimensions (monitor, &width_mm, &height_mm); g_assert_cmpint (width_mm, ==, test_case->expect.monitors[i].width_mm); g_assert_cmpint (height_mm, ==, test_case->expect.monitors[i].height_mm); modes = meta_monitor_get_modes (monitor); g_assert_cmpint (g_list_length (modes), ==, test_case->expect.monitors[i].n_modes); for (l_mode = modes, j = 0; l_mode; l_mode = l_mode->next, j++) { MetaMonitorMode *mode = l_mode->data; int width; int height; float refresh_rate; MetaCrtcModeFlag flags; CheckMonitorModeData data; meta_monitor_mode_get_resolution (mode, &width, &height); refresh_rate = meta_monitor_mode_get_refresh_rate (mode); flags = meta_monitor_mode_get_flags (mode); g_assert_cmpint (width, ==, test_case->expect.monitors[i].modes[j].width); g_assert_cmpint (height, ==, test_case->expect.monitors[i].modes[j].height); g_assert_cmpfloat (refresh_rate, ==, test_case->expect.monitors[i].modes[j].refresh_rate); g_assert_cmpint (flags, ==, test_case->expect.monitors[i].modes[j].flags); data = (CheckMonitorModeData) { .backend = backend, .expect_crtc_mode_iter = test_case->expect.monitors[i].modes[j].crtc_modes }; meta_monitor_mode_foreach_output (monitor, mode, check_monitor_mode, &data, NULL); } current_mode = meta_monitor_get_current_mode (monitor); expected_current_mode_index = test_case->expect.monitors[i].current_mode; if (expected_current_mode_index == -1) expected_current_mode = NULL; else expected_current_mode = g_list_nth (modes, expected_current_mode_index)->data; g_assert (current_mode == expected_current_mode); if (current_mode) g_assert (meta_monitor_is_active (monitor)); else g_assert (!meta_monitor_is_active (monitor)); if (current_mode) { CheckMonitorModeData data; data = (CheckMonitorModeData) { .backend = backend, .expect_crtc_mode_iter = test_case->expect.monitors[i].modes[expected_current_mode_index].crtc_modes }; meta_monitor_mode_foreach_output (monitor, expected_current_mode, check_current_monitor_mode, &data, NULL); } meta_monitor_derive_current_mode (monitor); g_assert (current_mode == meta_monitor_get_current_mode (monitor)); } n_logical_monitors = meta_monitor_manager_get_num_logical_monitors (monitor_manager); g_assert_cmpint (n_logical_monitors, ==, test_case->expect.n_logical_monitors); /* * Check that we have a primary logical monitor (except for headless), * and that the main output of the first monitor is the only output * that is marked as primary (further below). Note: outputs being primary or * not only matters on X11. */ if (test_case->expect.primary_logical_monitor == -1) { g_assert_null (monitor_manager->primary_logical_monitor); g_assert_null (monitor_manager->logical_monitors); } else { MonitorTestCaseLogicalMonitor *test_logical_monitor = &test_case->expect.logical_monitors[test_case->expect.primary_logical_monitor]; MetaLogicalMonitor *logical_monitor; logical_monitor = logical_monitor_from_layout (monitor_manager, &test_logical_monitor->layout); g_assert (logical_monitor == monitor_manager->primary_logical_monitor); } for (i = 0; i < test_case->expect.n_logical_monitors; i++) { MonitorTestCaseLogicalMonitor *test_logical_monitor = &test_case->expect.logical_monitors[i]; check_logical_monitor (test_case, monitor_manager, test_logical_monitor); } g_assert_cmpint (n_logical_monitors, ==, i); crtcs = meta_gpu_get_crtcs (gpu); for (l = crtcs, i = 0; l; l = l->next, i++) { MetaCrtc *crtc = l->data; MetaCrtcConfig *crtc_config = crtc->config; if (test_case->expect.crtcs[i].current_mode == -1) { g_assert_null (crtc_config); } else { MetaCrtcMode *expected_current_mode; g_assert_nonnull (crtc_config); expected_current_mode = g_list_nth_data (meta_gpu_get_modes (gpu), test_case->expect.crtcs[i].current_mode); g_assert (crtc_config->mode == expected_current_mode); g_assert_cmpuint (crtc_config->transform, ==, test_case->expect.crtcs[i].transform); g_assert_cmpfloat_with_epsilon (crtc_config->layout.origin.x, test_case->expect.crtcs[i].x, FLT_EPSILON); g_assert_cmpfloat_with_epsilon (crtc_config->layout.origin.y, test_case->expect.crtcs[i].y, FLT_EPSILON); } } check_monitor_test_clients_state (); } static void meta_output_test_destroy_notify (MetaOutput *output) { g_clear_pointer (&output->driver_private, g_free); } static MetaMonitorTestSetup * create_monitor_test_setup (MonitorTestCase *test_case, MonitorTestFlag flags) { MetaMonitorTestSetup *test_setup; int i; int n_laptop_panels = 0; int n_normal_panels = 0; gboolean hotplug_mode_update; if (flags & MONITOR_TEST_FLAG_NO_STORED) hotplug_mode_update = TRUE; else hotplug_mode_update = FALSE; test_setup = g_new0 (MetaMonitorTestSetup, 1); test_setup->modes = NULL; for (i = 0; i < test_case->setup.n_modes; i++) { MetaCrtcMode *mode; mode = g_object_new (META_TYPE_CRTC_MODE, NULL); mode->mode_id = i; mode->width = test_case->setup.modes[i].width; mode->height = test_case->setup.modes[i].height; mode->refresh_rate = test_case->setup.modes[i].refresh_rate; mode->flags = test_case->setup.modes[i].flags; test_setup->modes = g_list_append (test_setup->modes, mode); } test_setup->crtcs = NULL; for (i = 0; i < test_case->setup.n_crtcs; i++) { MetaCrtc *crtc; crtc = g_object_new (META_TYPE_CRTC, NULL); crtc->crtc_id = i + 1; crtc->all_transforms = ALL_TRANSFORMS; test_setup->crtcs = g_list_append (test_setup->crtcs, crtc); } test_setup->outputs = NULL; for (i = 0; i < test_case->setup.n_outputs; i++) { MetaOutput *output; MetaOutputTest *output_test; int crtc_index; MetaCrtc *crtc; int preferred_mode_index; MetaCrtcMode *preferred_mode; MetaCrtcMode **modes; int n_modes; int j; MetaCrtc **possible_crtcs; int n_possible_crtcs; int scale; gboolean is_laptop_panel; const char *serial; crtc_index = test_case->setup.outputs[i].crtc; if (crtc_index == -1) crtc = NULL; else crtc = g_list_nth_data (test_setup->crtcs, crtc_index); preferred_mode_index = test_case->setup.outputs[i].preferred_mode; if (preferred_mode_index == -1) preferred_mode = NULL; else preferred_mode = g_list_nth_data (test_setup->modes, preferred_mode_index); n_modes = test_case->setup.outputs[i].n_modes; modes = g_new0 (MetaCrtcMode *, n_modes); for (j = 0; j < n_modes; j++) { int mode_index; mode_index = test_case->setup.outputs[i].modes[j]; modes[j] = g_list_nth_data (test_setup->modes, mode_index); } n_possible_crtcs = test_case->setup.outputs[i].n_possible_crtcs; possible_crtcs = g_new0 (MetaCrtc *, n_possible_crtcs); for (j = 0; j < n_possible_crtcs; j++) { int possible_crtc_index; possible_crtc_index = test_case->setup.outputs[i].possible_crtcs[j]; possible_crtcs[j] = g_list_nth_data (test_setup->crtcs, possible_crtc_index); } output_test = g_new0 (MetaOutputTest, 1); scale = test_case->setup.outputs[i].scale; if (scale < 1) scale = 1; *output_test = (MetaOutputTest) { .scale = scale }; is_laptop_panel = test_case->setup.outputs[i].is_laptop_panel; serial = test_case->setup.outputs[i].serial; if (!serial) serial = "0x123456"; output = g_object_new (META_TYPE_OUTPUT, NULL); if (crtc) meta_output_assign_crtc (output, crtc); output->winsys_id = i; output->name = (is_laptop_panel ? g_strdup_printf ("eDP-%d", ++n_laptop_panels) : g_strdup_printf ("DP-%d", ++n_normal_panels)); output->vendor = g_strdup ("MetaProduct's Inc."); output->product = g_strdup ("MetaMonitor"); output->serial = g_strdup (serial); output->suggested_x = -1; output->suggested_y = -1; output->hotplug_mode_update = hotplug_mode_update; output->width_mm = test_case->setup.outputs[i].width_mm; output->height_mm = test_case->setup.outputs[i].height_mm; output->subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; output->preferred_mode = preferred_mode; output->n_modes = n_modes; output->modes = modes; output->n_possible_crtcs = n_possible_crtcs; output->possible_crtcs = possible_crtcs; output->n_possible_clones = 0; output->possible_clones = NULL; output->backlight = -1; output->connector_type = (is_laptop_panel ? META_CONNECTOR_TYPE_eDP : META_CONNECTOR_TYPE_DisplayPort); output->tile_info = test_case->setup.outputs[i].tile_info; output->is_underscanning = test_case->setup.outputs[i].is_underscanning; output->panel_orientation_transform = test_case->setup.outputs[i].panel_orientation_transform; output->driver_private = output_test; output->driver_notify = (GDestroyNotify) meta_output_test_destroy_notify; test_setup->outputs = g_list_append (test_setup->outputs, output); } return test_setup; } static void meta_test_monitor_initial_linear_config (void) { check_monitor_configuration (&initial_test_case); } static void emulate_hotplug (MetaMonitorTestSetup *test_setup) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); meta_monitor_manager_test_emulate_hotplug (monitor_manager_test, test_setup); g_usleep (G_USEC_PER_SEC / 100); } static void meta_test_monitor_one_disconnected_linear_config (void) { MonitorTestCase test_case = initial_test_case; MetaMonitorTestSetup *test_setup; test_case.setup.n_outputs = 1; test_case.expect = (MonitorTestCaseExpect) { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, }, { .current_mode = -1, } }, .n_crtcs = 2, .screen_width = 1024, .screen_height = 768 }; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_one_off_linear_config (void) { MonitorTestCase test_case; MetaMonitorTestSetup *test_setup; MonitorTestCaseOutput outputs[] = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 224, .height_mm = 126 } }; test_case = initial_test_case; memcpy (&test_case.setup.outputs, &outputs, sizeof (outputs)); test_case.setup.n_outputs = G_N_ELEMENTS (outputs); test_case.setup.crtcs[1].current_mode = -1; test_case.expect = (MonitorTestCaseExpect) { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 224, .height_mm = 126 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, .x = 1024, } }, .n_crtcs = 2, .screen_width = 1024 * 2, .screen_height = 768 }; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_preferred_linear_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 }, { .width = 1280, .height = 720, .refresh_rate = 60.0 } }, .n_modes = 3, .outputs = { { .crtc = -1, .modes = { 0, 1, 2 }, .n_modes = 3, .preferred_mode = 1, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 } }, .n_outputs = 1, .crtcs = { { .current_mode = -1 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } }, { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 1 } } }, { .width = 1280, .height = 720, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 2 } } } }, .n_modes = 3, .current_mode = 1, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 1, } }, .n_crtcs = 1, .screen_width = 1024, .screen_height = 768, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_tiled_linear_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 400, .height = 600, .refresh_rate = 60.0 }, }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 }, { .output = 1, .crtc_mode = 0, } } }, }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, .x = 400, .y = 0 } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_tiled_non_preferred_linear_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 640, .height = 480, .refresh_rate = 60.0 }, { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 512, .height = 768, .refresh_rate = 120.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 }, }, .n_modes = 4, .outputs = { { .crtc = -1, .modes = { 0, 2 }, .n_modes = 2, .preferred_mode = 1, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 512, .tile_h = 768 } }, { .crtc = -1, .modes = { 1, 2, 3 }, .n_modes = 3, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 512, .tile_h = 768 } } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 1024, .height = 768, .refresh_rate = 120.0, .crtc_modes = { { .output = 0, .crtc_mode = 2 }, { .output = 1, .crtc_mode = 2, } } }, { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = -1 }, { .output = 1, .crtc_mode = 1, } } }, { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = -1 }, { .output = 1, .crtc_mode = 3, } } }, }, .n_modes = 3, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 2, }, { .current_mode = 2, .x = 512 } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 1024, .screen_height = 768, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_tiled_non_main_origin_linear_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 400, .height = 600, .refresh_rate = 60.0 }, { .width = 800, .height = 600, .refresh_rate = 30.0 }, }, .n_modes = 2, .outputs = { { .crtc = -1, .modes = { 0, 1 }, .n_modes = 2, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0, }, { .output = 1, .crtc_mode = 0, } } }, { .width = 800, .height = 600, .refresh_rate = 30.0, .crtc_modes = { { .output = 0, .crtc_mode = 1 }, { .output = 1, .crtc_mode = -1, } } }, }, .n_modes = 2, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, .x = 400, .y = 0 }, { .current_mode = 0, } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_hidpi_linear_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1280, .height = 720, .refresh_rate = 60.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, /* These will result in DPI of about 216" */ .width_mm = 150, .height_mm = 85, .scale = 2, }, { .crtc = 1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .scale = 1, } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1280, .height = 720, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } }, }, .n_modes = 1, .current_mode = 0, .width_mm = 150, .height_mm = 85 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 1 } } }, }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 640, .height = 360 }, .scale = 2 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 640, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 1, .x = 640, } }, .n_crtcs = 2, .screen_width = 640 + 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void set_suggested_output_position (MetaOutput *output, int x, int y) { output->suggested_x = x; output->suggested_y = y; } static void meta_test_monitor_suggested_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = 1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 1 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, /* * Logical monitors expectations altered to correspond to the * "suggested_x/y" changed further below. */ .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 1024, .y = 758, .width = 800, .height = 600 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 1, .n_outputs = 2, .crtcs = { { .current_mode = 0, .x = 1024, .y = 758, }, { .current_mode = 1, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024 + 800, .screen_height = 1358 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); set_suggested_output_position (g_list_nth_data (test_setup->outputs, 0), 1024, 758); set_suggested_output_position (g_list_nth_data (test_setup->outputs, 1), 0, 0); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_limited_crtcs (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = -1, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failed to use linear *"); emulate_hotplug (test_setup); g_test_assert_expected_messages (); check_monitor_configuration (&test_case); } static void meta_test_monitor_lid_switch_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, .x = 1024, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024 * 2, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); meta_monitor_manager_lid_is_closed_changed (monitor_manager); test_case.expect.logical_monitors[0] = (MonitorTestCaseLogicalMonitor) { .monitors = { 1 }, .n_monitors = 1, .layout = {.x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }; test_case.expect.n_logical_monitors = 1; test_case.expect.screen_width = 1024; test_case.expect.monitors[0].current_mode = -1; test_case.expect.crtcs[0].current_mode = -1; test_case.expect.crtcs[1].x = 0; check_monitor_configuration (&test_case); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); meta_monitor_manager_lid_is_closed_changed (monitor_manager); test_case.expect.logical_monitors[0] = (MonitorTestCaseLogicalMonitor) { .monitors = { 0 }, .n_monitors = 1, .layout = {.x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }; test_case.expect.n_logical_monitors = 2; test_case.expect.screen_width = 1024 * 2; test_case.expect.monitors[0].current_mode = 0; test_case.expect.primary_logical_monitor = 0; test_case.expect.crtcs[0].current_mode = 0; test_case.expect.crtcs[1].current_mode = 0; test_case.expect.crtcs[1].x = 1024; check_monitor_configuration (&test_case); } static void meta_test_monitor_lid_opened_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = -1, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, /* Second one checked after lid opened. */ .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = -1, }, { .current_mode = 0, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); meta_monitor_manager_lid_is_closed_changed (monitor_manager); test_case.expect.n_logical_monitors = 2; test_case.expect.screen_width = 1024 * 2; test_case.expect.monitors[0].current_mode = 0; test_case.expect.crtcs[0].current_mode = 0; test_case.expect.crtcs[0].x = 1024; test_case.expect.crtcs[1].current_mode = 0; test_case.expect.crtcs[1].x = 0; check_monitor_configuration (&test_case); } static void meta_test_monitor_lid_closed_no_external (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE } }, .n_outputs = 1, .crtcs = { { .current_mode = 0 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, }, }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_lid_closed_with_hotplugged_external (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 1, /* Second is hotplugged later */ .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 1, /* Second is hotplugged later */ .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, /* Second is hotplugged later */ .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, }, { .current_mode = -1, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); /* * The first part of this test emulate the following: * 1) Start with the lid open * 2) Connect external monitor * 3) Close lid */ test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* External monitor connected */ test_case.setup.n_outputs = 2; test_case.expect.n_outputs = 2; test_case.expect.n_monitors = 2; test_case.expect.n_logical_monitors = 2; test_case.expect.crtcs[1].current_mode = 0; test_case.expect.crtcs[1].x = 1024; test_case.expect.screen_width = 1024 * 2; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* Lid closed */ test_case.expect.monitors[0].current_mode = -1; test_case.expect.logical_monitors[0].monitors[0] = 1, test_case.expect.n_logical_monitors = 1; test_case.expect.crtcs[0].current_mode = -1; test_case.expect.crtcs[1].x = 0; test_case.expect.screen_width = 1024; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* * The second part of this test emulate the following: * 1) Open lid * 2) Disconnect external monitor * 3) Close lid * 4) Open lid */ /* Lid opened */ test_case.expect.monitors[0].current_mode = 0; test_case.expect.logical_monitors[0].monitors[0] = 0, test_case.expect.logical_monitors[1].monitors[0] = 1, test_case.expect.n_logical_monitors = 2; test_case.expect.crtcs[0].current_mode = 0; test_case.expect.crtcs[1].x = 1024; test_case.expect.screen_width = 1024 * 2; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* External monitor disconnected */ test_case.setup.n_outputs = 1; test_case.expect.n_outputs = 1; test_case.expect.n_monitors = 1; test_case.expect.n_logical_monitors = 1; test_case.expect.crtcs[1].current_mode = -1; test_case.expect.screen_width = 1024; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* Lid closed */ test_case.expect.logical_monitors[0].monitors[0] = 0, test_case.expect.n_logical_monitors = 1; test_case.expect.screen_width = 1024; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* Lid opened */ test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_lid_scaled_closed_opened (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 960, .height = 540 }, .scale = 2 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 960, .screen_height = 540 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("lid-scale.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); meta_monitor_manager_lid_is_closed_changed (monitor_manager); check_monitor_configuration (&test_case); meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); meta_monitor_manager_lid_is_closed_changed (monitor_manager); check_monitor_configuration (&test_case); } static void meta_test_monitor_no_outputs (void) { MonitorTestCase test_case = { .setup = { .n_modes = 0, .n_outputs = 0, .n_crtcs = 0 }, .expect = { .n_monitors = 0, .n_logical_monitors = 0, .primary_logical_monitor = -1, .n_outputs = 0, .n_crtcs = 0, .n_tiled_monitors = 0, .screen_width = META_MONITOR_MANAGER_MIN_SCREEN_WIDTH, .screen_height = META_MONITOR_MANAGER_MIN_SCREEN_HEIGHT } }; MetaMonitorTestSetup *test_setup; GError *error = NULL; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); if (!test_client_do (x11_monitor_test_client, &error, "resize", X11_TEST_CLIENT_WINDOW, "123", "210", NULL)) g_error ("Failed to resize X11 window: %s", error->message); if (!test_client_do (wayland_monitor_test_client, &error, "resize", WAYLAND_TEST_CLIENT_WINDOW, "123", "210", NULL)) g_error ("Failed to resize Wayland window: %s", error->message); check_monitor_test_clients_state (); /* Also check that we handle going headless -> headless */ test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_underscanning_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_underscanning = TRUE, } }, .n_outputs = 1, .crtcs = { { .current_mode = 0 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, .is_underscanning = TRUE, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_preferred_non_first_mode (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .flags = META_CRTC_MODE_FLAG_NHSYNC, }, { .width = 800, .height = 600, .refresh_rate = 60.0, .flags = META_CRTC_MODE_FLAG_PHSYNC, }, }, .n_modes = 2, .outputs = { { .crtc = -1, .modes = { 0, 1 }, .n_modes = 2, .preferred_mode = 1, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 } }, .n_outputs = 1, .crtcs = { { .current_mode = -1 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 1 } } }, }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 1, } }, .n_crtcs = 1, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_non_upright_panel (void) { MonitorTestCase test_case = initial_test_case; MetaMonitorTestSetup *test_setup; test_case.setup.modes[1] = (MonitorTestCaseMode) { .width = 768, .height = 1024, .refresh_rate = 60.0, }; test_case.setup.n_modes = 2; test_case.setup.outputs[0].modes[0] = 1; test_case.setup.outputs[0].preferred_mode = 1; test_case.setup.outputs[0].panel_orientation_transform = META_MONITOR_TRANSFORM_90; /* * Note we do not swap outputs[0].width_mm and height_mm, because these get * swapped for rotated panels inside the xrandr / kms code and we directly * create a dummy output here, skipping this code. */ test_case.setup.crtcs[0].current_mode = 1; test_case.expect.monitors[0].modes[0].crtc_modes[0].crtc_mode = 1; test_case.expect.crtcs[0].current_mode = 1; test_case.expect.crtcs[0].transform = META_MONITOR_TRANSFORM_90; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_vertical_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 }, { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = 1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 1 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 0, .y = 768, .width = 800, .height = 600 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 1, .y = 768, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 + 600 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("vertical.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_primary_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 }, { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = 1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 1 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 800, .height = 600 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 1, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 1, .x = 1024, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 1024 + 800, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("primary.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_underscanning_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, .is_underscanning = TRUE, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("underscanning.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_scale_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1920, .height = 1080, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 960, .height = 540 }, .scale = 2 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 960, .screen_height = 540 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("scale.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_fractional_scale_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1200, .height = 900, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1200, .height = 900, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1.5 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 800, .screen_height = 600 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("fractional-scale.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_high_precision_fractional_scale_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 744, .height = 558 }, .scale = 1024.0/744.0 /* 1.3763440847396851 */ } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 744, .screen_height = 558 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("high-precision-fractional-scale.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_tiled_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 400, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0, }, { .output = 1, .crtc_mode = 0, } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 400, .height = 300 }, .scale = 2 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, .x = 200, .y = 0 } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 400, .screen_height = 300 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("tiled.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_tiled_custom_resolution_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 400, .height = 600, .refresh_rate = 60.000495910644531 }, { .width = 640, .height = 480, .refresh_rate = 60.000495910644531 } }, .n_modes = 2, .outputs = { { .crtc = -1, .modes = { 0, 1 }, .n_modes = 2, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 0, 1 }, .n_modes = 2, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0, }, { .output = 1, .crtc_mode = 0, } } }, { .width = 640, .height = 480, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 1, }, { .output = 1, .crtc_mode = -1, } } } }, .n_modes = 2, .current_mode = 1, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 320, .height = 240 }, .scale = 2 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 1, }, { .current_mode = -1, .x = 400, .y = 0, } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 320, .screen_height = 240 } }; MetaMonitorTestSetup *test_setup; if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("tiled-custom-resolution.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_tiled_non_preferred_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 640, .height = 480, .refresh_rate = 60.0 }, { .width = 800, .height = 600, .refresh_rate = 60.0 }, { .width = 512, .height = 768, .refresh_rate = 120.0 }, { .width = 1024, .height = 768, .refresh_rate = 60.0 }, }, .n_modes = 4, .outputs = { { .crtc = -1, .modes = { 0, 2 }, .n_modes = 2, .preferred_mode = 1, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 512, .tile_h = 768 } }, { .crtc = -1, .modes = { 1, 2, 3 }, .n_modes = 3, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 512, .tile_h = 768 } } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0, 1 }, .n_outputs = 2, .modes = { { .width = 1024, .height = 768, .refresh_rate = 120.0, .crtc_modes = { { .output = 0, .crtc_mode = 2 }, { .output = 1, .crtc_mode = 2, } } }, { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = -1 }, { .output = 1, .crtc_mode = 1, } } }, { .width = 1024, .height = 768, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = -1 }, { .output = 1, .crtc_mode = 3, } } }, }, .n_modes = 3, .current_mode = 1, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = -1, }, { .current_mode = 1, } }, .n_crtcs = 2, .n_tiled_monitors = 1, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("non-preferred-tiled-custom-resolution.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_mirrored_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 220, .height_mm = 124 } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 220, .height_mm = 124 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0, 1 }, .n_monitors = 2, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0, } }, .n_crtcs = 2, .n_tiled_monitors = 0, .screen_width = 800, .screen_height = 600 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("mirrored.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_first_rotated_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, } }, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_270 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 768, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_270 }, { .current_mode = 0, .x = 768, } }, .n_crtcs = 2, .screen_width = 768 + 1024, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("first-rotated.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_second_rotated_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 256, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, .y = 256, }, { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_90, .x = 1024, } }, .n_crtcs = 2, .screen_width = 768 + 1024, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("second-rotated.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_second_rotated_tiled_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 }, { .width = 400, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, }, { .crtc = -1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1, 2 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1, 2 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 3, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 3 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1, 2 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 1, }, { .output = 2, .crtc_mode = 1, } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 256, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 600, .height = 800 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 3, .crtcs = { { .current_mode = 0, .y = 256, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_90, .x = 1024, .y = 0, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_90, .x = 1024, .y = 400, } }, .n_crtcs = 3, .n_tiled_monitors = 1, .screen_width = 1024 + 600, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); meta_monitor_manager_test_set_handles_transforms (monitor_manager_test, TRUE); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("second-rotated-tiled.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_second_rotated_nonnative_tiled_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 }, { .width = 400, .height = 600, .refresh_rate = 60.000495910644531 } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, }, { .crtc = -1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1, 2 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 0, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } }, { .crtc = -1, .modes = { 1 }, .n_modes = 1, .preferred_mode = 1, .possible_crtcs = { 1, 2 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .tile_info = { .group_id = 1, .max_h_tiles = 2, .max_v_tiles = 1, .loc_h_tile = 1, .loc_v_tile = 0, .tile_w = 400, .tile_h = 600 } } }, .n_outputs = 3, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 3 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1, 2 }, .n_outputs = 2, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 1, }, { .output = 2, .crtc_mode = 1, } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 256, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 600, .height = 800 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 3, .crtcs = { { .current_mode = 0, .y = 256, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_NORMAL, .x = 1024, .y = 0, }, { .current_mode = 1, .transform = META_MONITOR_TRANSFORM_NORMAL, .x = 1024, .y = 400, } }, .n_crtcs = 3, .n_tiled_monitors = 1, .screen_width = 1024 + 600, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); meta_monitor_manager_test_set_handles_transforms (monitor_manager_test, FALSE); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("second-rotated-tiled.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_second_rotated_nonnative_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = 0, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, }, { .crtc = 1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, } }, .n_outputs = 2, .crtcs = { { .current_mode = 0 }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 256, .width = 1024, .height = 768 }, .scale = 1 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90 } }, .n_logical_monitors = 2, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, .y = 256, }, { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_NORMAL, .x = 1024, } }, .n_crtcs = 2, .screen_width = 768 + 1024, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); if (!meta_is_stage_views_enabled ()) { g_test_skip ("Not using stage views"); return; } meta_monitor_manager_test_set_handles_transforms (monitor_manager_test, FALSE); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("second-rotated.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_interlaced_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 }, { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .flags = META_CRTC_MODE_FLAG_INTERLACE, } }, .n_modes = 2, .outputs = { { .crtc = 0, .modes = { 0, 1 }, .n_modes = 2, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 }, }, .n_outputs = 1, .crtcs = { { .current_mode = 0 }, }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .flags = META_CRTC_MODE_FLAG_NONE, .crtc_modes = { { .output = 0, .crtc_mode = 0, }, } }, { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .flags = META_CRTC_MODE_FLAG_INTERLACE, .crtc_modes = { { .output = 0, .crtc_mode = 1, } } } }, .n_modes = 2, .current_mode = 1, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 1024, .height = 768 }, .scale = 1 } }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 1, } }, .n_crtcs = 1, .n_tiled_monitors = 0, .screen_width = 1024, .screen_height = 768 } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("interlaced.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_oneoff (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125 }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0, 1 }, .n_possible_crtcs = 2, .width_mm = 222, .height_mm = 125, .serial = "0x654321" } }, .n_outputs = 2, .crtcs = { { .current_mode = -1 }, { .current_mode = -1 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = -1, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 2, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_NORMAL }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 2, .crtcs = { { .current_mode = 0, }, { .current_mode = -1, } }, .n_crtcs = 2, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("oneoff.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_custom_lid_switch_config (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, .is_laptop_panel = TRUE }, { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 1 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125, } }, .n_outputs = 1, /* Second one hot plugged later */ .crtcs = { { .current_mode = 0, }, { .current_mode = 0 } }, .n_crtcs = 2 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, }, { .outputs = { 1 }, .n_outputs = 1, .modes = { { .width = 1024, .height = 768, .refresh_rate = 60.000495910644531, .crtc_modes = { { .output = 1, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125, } }, .n_monitors = 1, /* Second one hot plugged later */ .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 768, .height = 1024 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_270 }, { .monitors = { 1 }, .n_monitors = 1, .layout = { .x = 1024, .y = 0, .width = 768, .height = 1024 }, .scale = 1 } }, .n_logical_monitors = 1, /* Second one hot plugged later */ .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_270 }, { .current_mode = -1, } }, .n_crtcs = 2, .screen_width = 768, .screen_height = 1024 } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); set_custom_monitor_config ("lid-switch.xml"); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* External monitor connected */ test_case.setup.n_outputs = 2; test_case.expect.n_monitors = 2; test_case.expect.n_outputs = 2; test_case.expect.crtcs[0].transform = META_MONITOR_TRANSFORM_NORMAL; test_case.expect.crtcs[1].current_mode = 0; test_case.expect.crtcs[1].x = 1024; test_case.expect.crtcs[1].transform = META_MONITOR_TRANSFORM_270; test_case.expect.logical_monitors[0].layout = (MetaRectangle) { .width = 1024, .height = 768 }; test_case.expect.logical_monitors[0].transform = META_MONITOR_TRANSFORM_NORMAL; test_case.expect.logical_monitors[1].transform = META_MONITOR_TRANSFORM_270; test_case.expect.n_logical_monitors = 2; test_case.expect.screen_width = 1024 + 768; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* Lid was closed */ test_case.expect.crtcs[0].current_mode = -1; test_case.expect.crtcs[1].transform = META_MONITOR_TRANSFORM_90; test_case.expect.crtcs[1].x = 0; test_case.expect.monitors[0].current_mode = -1; test_case.expect.logical_monitors[0].layout = (MetaRectangle) { .width = 768, .height = 1024 }; test_case.expect.logical_monitors[0].monitors[0] = 1; test_case.expect.logical_monitors[0].transform = META_MONITOR_TRANSFORM_90; test_case.expect.n_logical_monitors = 1; test_case.expect.screen_width = 768; meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), TRUE); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); /* Lid was opened */ test_case.expect.crtcs[0].current_mode = 0; test_case.expect.crtcs[0].transform = META_MONITOR_TRANSFORM_NORMAL; test_case.expect.crtcs[1].current_mode = 0; test_case.expect.crtcs[1].transform = META_MONITOR_TRANSFORM_270; test_case.expect.crtcs[1].x = 1024; test_case.expect.monitors[0].current_mode = 0; test_case.expect.logical_monitors[0].layout = (MetaRectangle) { .width = 1024, .height = 768 }; test_case.expect.logical_monitors[0].monitors[0] = 0; test_case.expect.logical_monitors[0].transform = META_MONITOR_TRANSFORM_NORMAL; test_case.expect.logical_monitors[1].transform = META_MONITOR_TRANSFORM_270; test_case.expect.n_logical_monitors = 2; test_case.expect.screen_width = 1024 + 768; meta_backend_test_set_is_lid_closed (META_BACKEND_TEST (backend), FALSE); test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); } static void meta_test_monitor_migrated_rotated (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 } }, .n_outputs = 1, .crtcs = { { .current_mode = -1 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 600, .height = 800 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_270 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_270 } }, .n_crtcs = 1, .screen_width = 600, .screen_height = 800, } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); g_autofree char *migrated_path = NULL; const char *old_config_path; g_autoptr (GFile) old_config_file = NULL; GError *error = NULL; const char *expected_path; g_autofree char *migrated_data = NULL; g_autofree char *expected_data = NULL; g_autoptr (GFile) migrated_file = NULL; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); migrated_path = g_build_filename (g_get_tmp_dir (), "test-finished-migrated-monitors.xml", NULL); if (!meta_monitor_config_store_set_custom (config_store, "/dev/null", migrated_path, &error)) g_error ("Failed to set custom config store files: %s", error->message); old_config_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "rotated-old.xml", NULL); old_config_file = g_file_new_for_path (old_config_path); if (!meta_migrate_old_monitors_config (config_store, old_config_file, &error)) g_error ("Failed to migrate config: %s", error->message); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); expected_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "rotated-new-finished.xml", NULL); expected_data = read_file (expected_path); migrated_data = read_file (migrated_path); g_assert_nonnull (expected_data); g_assert_nonnull (migrated_data); g_assert (strcmp (expected_data, migrated_data) == 0); migrated_file = g_file_new_for_path (migrated_path); if (!g_file_delete (migrated_file, NULL, &error)) g_error ("Failed to remove test data output file: %s", error->message); } static void meta_test_monitor_migrated_wiggle_discard (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 59.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 } }, .n_outputs = 1, .crtcs = { { .current_mode = -1 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 59.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 800, .height = 600 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_NORMAL }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, } }, .n_crtcs = 1, .screen_width = 800, .screen_height = 600, } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); g_autofree char *migrated_path = NULL; const char *old_config_path; g_autoptr (GFile) old_config_file = NULL; GError *error = NULL; const char *expected_path; g_autofree char *migrated_data = NULL; g_autofree char *expected_data = NULL; g_autoptr (GFile) migrated_file = NULL; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); migrated_path = g_build_filename (g_get_tmp_dir (), "test-finished-migrated-monitors.xml", NULL); if (!meta_monitor_config_store_set_custom (config_store, "/dev/null", migrated_path, &error)) g_error ("Failed to set custom config store files: %s", error->message); old_config_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "wiggle-old.xml", NULL); old_config_file = g_file_new_for_path (old_config_path); if (!meta_migrate_old_monitors_config (config_store, old_config_file, &error)) g_error ("Failed to migrate config: %s", error->message); g_test_expect_message (G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "Failed to finish monitors config migration: " "Mode not available on monitor"); emulate_hotplug (test_setup); g_test_assert_expected_messages (); check_monitor_configuration (&test_case); expected_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "wiggle-new-discarded.xml", NULL); expected_data = read_file (expected_path); migrated_data = read_file (migrated_path); g_assert_nonnull (expected_data); g_assert_nonnull (migrated_data); g_assert (strcmp (expected_data, migrated_data) == 0); migrated_file = g_file_new_for_path (migrated_path); if (!g_file_delete (migrated_file, NULL, &error)) g_error ("Failed to remove test data output file: %s", error->message); } static gboolean quit_main_loop (gpointer data) { GMainLoop *loop = data; g_main_loop_quit (loop); return G_SOURCE_REMOVE; } static void dispatch (void) { GMainLoop *loop; loop = g_main_loop_new (NULL, FALSE); meta_later_add (META_LATER_BEFORE_REDRAW, quit_main_loop, loop, NULL); g_main_loop_run (loop); } static TestClient * create_test_window (const char *window_name) { TestClient *test_client; static int client_count = 0; g_autofree char *client_name = NULL; g_autoptr (GError) error = NULL; client_name = g_strdup_printf ("test_client_%d", client_count++); test_client = test_client_new (client_name, META_WINDOW_CLIENT_TYPE_WAYLAND, &error); if (!test_client) g_error ("Failed to launch test client: %s", error->message); if (!test_client_do (test_client, &error, "create", window_name, NULL)) g_error ("Failed to create window: %s", error->message); return test_client; } static void meta_test_monitor_wm_tiling (void) { MonitorTestCase test_case = initial_test_case; MetaMonitorTestSetup *test_setup; g_autoptr (GError) error = NULL; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); /* * 1) Start with two monitors connected. * 2) Tile it on the second monitor. * 3) Unplug both monitors. * 4) Replug in first monitor. */ const char *test_window_name= "window1"; TestClient *test_client = create_test_window (test_window_name); if (!test_client_do (test_client, &error, "show", test_window_name, NULL)) g_error ("Failed to show the window: %s", error->message); MetaWindow *test_window = test_client_find_window (test_client, test_window_name, &error); if (!test_window) g_error ("Failed to find the window: %s", error->message); test_client_wait_for_window_shown (test_client, test_window); meta_window_tile (test_window, META_TILE_MAXIMIZED); meta_window_move_to_monitor (test_window, 1); check_test_client_state (test_client); fprintf(stderr, ":::: %s:%d %s() - UNPLUGGING\n", __FILE__, __LINE__, __func__); test_case.setup.n_outputs = 0; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); test_case.setup.n_outputs = 1; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); dispatch (); /* * 1) Start with two monitors connected. * 2) Tile a window on the second monitor. * 3) Untile window. * 4) Unplug monitor. * 5) Tile window again. */ test_case.setup.n_outputs = 2; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); meta_window_move_to_monitor (test_window, 1); meta_window_tile (test_window, META_TILE_NONE); test_case.setup.n_outputs = 1; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NO_STORED); emulate_hotplug (test_setup); meta_window_tile (test_window, META_TILE_MAXIMIZED); test_client_destroy (test_client); } static void meta_test_monitor_migrated_wiggle (void) { MonitorTestCase test_case = { .setup = { .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0 } }, .n_modes = 1, .outputs = { { .crtc = -1, .modes = { 0 }, .n_modes = 1, .preferred_mode = 0, .possible_crtcs = { 0 }, .n_possible_crtcs = 1, .width_mm = 222, .height_mm = 125 } }, .n_outputs = 1, .crtcs = { { .current_mode = -1 } }, .n_crtcs = 1 }, .expect = { .monitors = { { .outputs = { 0 }, .n_outputs = 1, .modes = { { .width = 800, .height = 600, .refresh_rate = 60.0, .crtc_modes = { { .output = 0, .crtc_mode = 0 } } } }, .n_modes = 1, .current_mode = 0, .width_mm = 222, .height_mm = 125 } }, .n_monitors = 1, .logical_monitors = { { .monitors = { 0 }, .n_monitors = 1, .layout = { .x = 0, .y = 0, .width = 600, .height = 800 }, .scale = 1, .transform = META_MONITOR_TRANSFORM_90 }, }, .n_logical_monitors = 1, .primary_logical_monitor = 0, .n_outputs = 1, .crtcs = { { .current_mode = 0, .transform = META_MONITOR_TRANSFORM_90 } }, .n_crtcs = 1, .screen_width = 600, .screen_height = 800, } }; MetaMonitorTestSetup *test_setup; MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; MetaMonitorConfigStore *config_store = meta_monitor_config_manager_get_store (config_manager); g_autofree char *migrated_path = NULL; const char *old_config_path; g_autoptr (GFile) old_config_file = NULL; GError *error = NULL; const char *expected_path; g_autofree char *migrated_data = NULL; g_autofree char *expected_data = NULL; g_autoptr (GFile) migrated_file = NULL; test_setup = create_monitor_test_setup (&test_case, MONITOR_TEST_FLAG_NONE); migrated_path = g_build_filename (g_get_tmp_dir (), "test-finished-migrated-monitors.xml", NULL); if (!meta_monitor_config_store_set_custom (config_store, "/dev/null", migrated_path, &error)) g_error ("Failed to set custom config store files: %s", error->message); old_config_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "wiggle-old.xml", NULL); old_config_file = g_file_new_for_path (old_config_path); if (!meta_migrate_old_monitors_config (config_store, old_config_file, &error)) g_error ("Failed to migrate config: %s", error->message); emulate_hotplug (test_setup); check_monitor_configuration (&test_case); expected_path = g_test_get_filename (G_TEST_DIST, "tests", "migration", "wiggle-new-finished.xml", NULL); expected_data = read_file (expected_path); migrated_data = read_file (migrated_path); g_assert_nonnull (expected_data); g_assert_nonnull (migrated_data); g_assert (strcmp (expected_data, migrated_data) == 0); migrated_file = g_file_new_for_path (migrated_path); if (!g_file_delete (migrated_file, NULL, &error)) g_error ("Failed to remove test data output file: %s", error->message); } static void test_case_setup (void **fixture, const void *data) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaMonitorManagerTest *monitor_manager_test = META_MONITOR_MANAGER_TEST (monitor_manager); MetaMonitorConfigManager *config_manager = monitor_manager->config_manager; meta_monitor_manager_test_set_handles_transforms (monitor_manager_test, TRUE); meta_monitor_config_manager_set_current (config_manager, NULL); meta_monitor_config_manager_clear_history (config_manager); } static void add_monitor_test (const char *test_path, GTestFunc test_func) { g_test_add (test_path, gpointer, NULL, test_case_setup, (void (* ) (void **, const void *)) test_func, NULL); } void init_monitor_tests (void) { MetaMonitorTestSetup *initial_test_setup; initial_test_setup = create_monitor_test_setup (&initial_test_case, MONITOR_TEST_FLAG_NO_STORED); meta_monitor_manager_test_init_test_setup (initial_test_setup); add_monitor_test ("/backends/monitor/initial-linear-config", meta_test_monitor_initial_linear_config); add_monitor_test ("/backends/monitor/one-disconnected-linear-config", meta_test_monitor_one_disconnected_linear_config); add_monitor_test ("/backends/monitor/one-off-linear-config", meta_test_monitor_one_off_linear_config); add_monitor_test ("/backends/monitor/preferred-linear-config", meta_test_monitor_preferred_linear_config); add_monitor_test ("/backends/monitor/tiled-linear-config", meta_test_monitor_tiled_linear_config); add_monitor_test ("/backends/monitor/tiled-non-preferred-linear-config", meta_test_monitor_tiled_non_preferred_linear_config); add_monitor_test ("/backends/monitor/tiled-non-main-origin-linear-config", meta_test_monitor_tiled_non_main_origin_linear_config); add_monitor_test ("/backends/monitor/hidpi-linear-config", meta_test_monitor_hidpi_linear_config); add_monitor_test ("/backends/monitor/suggested-config", meta_test_monitor_suggested_config); add_monitor_test ("/backends/monitor/limited-crtcs", meta_test_monitor_limited_crtcs); add_monitor_test ("/backends/monitor/lid-switch-config", meta_test_monitor_lid_switch_config); add_monitor_test ("/backends/monitor/lid-opened-config", meta_test_monitor_lid_opened_config); add_monitor_test ("/backends/monitor/lid-closed-no-external", meta_test_monitor_lid_closed_no_external); add_monitor_test ("/backends/monitor/lid-closed-with-hotplugged-external", meta_test_monitor_lid_closed_with_hotplugged_external); add_monitor_test ("/backends/monitor/lid-scaled-closed-opened", meta_test_monitor_lid_scaled_closed_opened); add_monitor_test ("/backends/monitor/no-outputs", meta_test_monitor_no_outputs); add_monitor_test ("/backends/monitor/underscanning-config", meta_test_monitor_underscanning_config); add_monitor_test ("/backends/monitor/preferred-non-first-mode", meta_test_monitor_preferred_non_first_mode); add_monitor_test ("/backends/monitor/non-upright-panel", meta_test_monitor_non_upright_panel); add_monitor_test ("/backends/monitor/custom/vertical-config", meta_test_monitor_custom_vertical_config); add_monitor_test ("/backends/monitor/custom/primary-config", meta_test_monitor_custom_primary_config); add_monitor_test ("/backends/monitor/custom/underscanning-config", meta_test_monitor_custom_underscanning_config); add_monitor_test ("/backends/monitor/custom/scale-config", meta_test_monitor_custom_scale_config); add_monitor_test ("/backends/monitor/custom/fractional-scale-config", meta_test_monitor_custom_fractional_scale_config); add_monitor_test ("/backends/monitor/custom/high-precision-fractional-scale-config", meta_test_monitor_custom_high_precision_fractional_scale_config); add_monitor_test ("/backends/monitor/custom/tiled-config", meta_test_monitor_custom_tiled_config); add_monitor_test ("/backends/monitor/custom/tiled-custom-resolution-config", meta_test_monitor_custom_tiled_custom_resolution_config); add_monitor_test ("/backends/monitor/custom/tiled-non-preferred-config", meta_test_monitor_custom_tiled_non_preferred_config); add_monitor_test ("/backends/monitor/custom/mirrored-config", meta_test_monitor_custom_mirrored_config); add_monitor_test ("/backends/monitor/custom/first-rotated-config", meta_test_monitor_custom_first_rotated_config); add_monitor_test ("/backends/monitor/custom/second-rotated-config", meta_test_monitor_custom_second_rotated_config); add_monitor_test ("/backends/monitor/custom/second-rotated-tiled-config", meta_test_monitor_custom_second_rotated_tiled_config); add_monitor_test ("/backends/monitor/custom/second-rotated-nonnative-tiled-config", meta_test_monitor_custom_second_rotated_nonnative_tiled_config); add_monitor_test ("/backends/monitor/custom/second-rotated-nonnative-config", meta_test_monitor_custom_second_rotated_nonnative_config); add_monitor_test ("/backends/monitor/custom/interlaced-config", meta_test_monitor_custom_interlaced_config); add_monitor_test ("/backends/monitor/custom/oneoff-config", meta_test_monitor_custom_oneoff); add_monitor_test ("/backends/monitor/custom/lid-switch-config", meta_test_monitor_custom_lid_switch_config); add_monitor_test ("/backends/monitor/migrated/rotated", meta_test_monitor_migrated_rotated); add_monitor_test ("/backends/monitor/migrated/wiggle", meta_test_monitor_migrated_wiggle); add_monitor_test ("/backends/monitor/migrated/wiggle-discard", meta_test_monitor_migrated_wiggle_discard); add_monitor_test ("/backends/monitor/wm/tiling", meta_test_monitor_wm_tiling); } void pre_run_monitor_tests (void) { create_monitor_test_clients (); } void finish_monitor_tests (void) { destroy_monitor_test_clients (); } muffin-6.4.1/src/ui/0000775000175000017500000000000014723361714013146 5ustar fabiofabiomuffin-6.4.1/src/ui/theme-private.h0000664000175000017500000002074514723361714016101 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Metacity Theme Rendering */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_THEME_PRIVATE_H #define META_THEME_PRIVATE_H #include #include "meta/boxes.h" #include "meta/common.h" #include "meta/theme.h" /** * MetaStyleInfo: (skip) * */ typedef struct _MetaStyleInfo MetaStyleInfo; /** * MetaFrameLayout: (skip) * */ typedef struct _MetaFrameLayout MetaFrameLayout; /** * MetaButtonSpace: (skip) * */ typedef struct _MetaButtonSpace MetaButtonSpace; /** * MetaFrameGeometry: (skip) * */ typedef struct _MetaFrameGeometry MetaFrameGeometry; /** * Various parameters used to calculate the geometry of a frame. **/ struct _MetaFrameLayout { /** Invisible border required by the theme */ GtkBorder invisible_border; /** Border/padding of the entire frame */ GtkBorder frame_border; /** Border/padding of the titlebar region */ GtkBorder titlebar_border; /** Border/padding of titlebar buttons */ GtkBorder button_border; /** Margin of title */ GtkBorder title_margin; /** Margin of titlebar buttons */ GtkBorder button_margin; /** Min size of titlebar region */ GtkRequisition titlebar_min_size; /** Min size of titlebar buttons */ GtkRequisition button_min_size; /** Size of images in buttons */ guint icon_size; /** Space between titlebar elements */ guint titlebar_spacing; /** scale factor for title text */ double title_scale; /** Whether title text will be displayed */ guint has_title : 1; /** Whether we should hide the buttons */ guint hide_buttons : 1; /** Radius of the top left-hand corner; 0 if not rounded */ guint top_left_corner_rounded_radius; /** Radius of the top right-hand corner; 0 if not rounded */ guint top_right_corner_rounded_radius; /** Radius of the bottom left-hand corner; 0 if not rounded */ guint bottom_left_corner_rounded_radius; /** Radius of the bottom right-hand corner; 0 if not rounded */ guint bottom_right_corner_rounded_radius; }; /** * The computed size of a button (really just a way of tying its * visible and clickable areas together). * The reason for two different rectangles here is Fitts' law & maximized * windows; see bug #97703 for more details. */ struct _MetaButtonSpace { /** The screen area where the button's image is drawn */ GdkRectangle visible; /** The screen area where the button can be activated by clicking */ GdkRectangle clickable; }; /** * Calculated actual geometry of the frame */ struct _MetaFrameGeometry { MetaFrameBorders borders; int width; int height; GdkRectangle title_rect; GtkBorder content_border; /* used for a memset hack */ #define ADDRESS_OF_BUTTON_RECTS(fgeom) (((char*)(fgeom)) + G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) #define LENGTH_OF_BUTTON_RECTS (G_STRUCT_OFFSET (MetaFrameGeometry, menu_rect) + sizeof (MetaButtonSpace) - G_STRUCT_OFFSET (MetaFrameGeometry, close_rect)) /* The button rects (if changed adjust memset hack) */ MetaButtonSpace close_rect; MetaButtonSpace max_rect; MetaButtonSpace min_rect; MetaButtonSpace menu_rect; /* End of button rects (if changed adjust memset hack) */ /* Saved button layout */ MetaButtonLayout button_layout; int n_left_buttons; int n_right_buttons; /* Round corners */ guint top_left_corner_rounded_radius; guint top_right_corner_rounded_radius; guint bottom_left_corner_rounded_radius; guint bottom_right_corner_rounded_radius; }; typedef enum { META_BUTTON_STATE_NORMAL, META_BUTTON_STATE_PRESSED, META_BUTTON_STATE_PRELIGHT, META_BUTTON_STATE_LAST } MetaButtonState; typedef enum { META_BUTTON_TYPE_CLOSE, META_BUTTON_TYPE_MAXIMIZE, META_BUTTON_TYPE_MINIMIZE, META_BUTTON_TYPE_MENU, META_BUTTON_TYPE_LAST } MetaButtonType; typedef enum { META_STYLE_ELEMENT_WINDOW, META_STYLE_ELEMENT_FRAME, META_STYLE_ELEMENT_TITLEBAR, META_STYLE_ELEMENT_TITLE, META_STYLE_ELEMENT_BUTTON, META_STYLE_ELEMENT_IMAGE, META_STYLE_ELEMENT_LAST } MetaStyleElement; struct _MetaStyleInfo { int refcount; GtkStyleContext *styles[META_STYLE_ELEMENT_LAST]; }; /* Kinds of frame... * * normal -> focused / unfocused * max -> focused / unfocused * shaded -> focused / unfocused * max/shaded -> focused / unfocused * * so 4 states with 2 sub-states each, meaning 8 total * * 8 window states times 7 or 8 window types. Except some * window types never get a frame so that narrows it down a bit. * */ typedef enum { META_FRAME_STATE_NORMAL, META_FRAME_STATE_MAXIMIZED, META_FRAME_STATE_TILED_LEFT, META_FRAME_STATE_TILED_RIGHT, META_FRAME_STATE_SHADED, META_FRAME_STATE_MAXIMIZED_AND_SHADED, META_FRAME_STATE_TILED_LEFT_AND_SHADED, META_FRAME_STATE_TILED_RIGHT_AND_SHADED, META_FRAME_STATE_LAST } MetaFrameState; typedef enum { META_FRAME_FOCUS_NO, META_FRAME_FOCUS_YES, META_FRAME_FOCUS_LAST } MetaFrameFocus; /** * A theme. This is a singleton class which groups all settings from a theme * together. */ struct _MetaTheme { MetaFrameLayout *layouts[META_FRAME_TYPE_LAST]; }; void meta_frame_layout_apply_scale (const MetaFrameLayout *layout, PangoFontDescription *font_desc); MetaFrameLayout* meta_theme_get_frame_layout (MetaTheme *theme, MetaFrameType type); MetaStyleInfo * meta_theme_create_style_info (GdkScreen *screen, const gchar *variant); MetaStyleInfo * meta_style_info_ref (MetaStyleInfo *style); void meta_style_info_unref (MetaStyleInfo *style_info); void meta_style_info_set_flags (MetaStyleInfo *style_info, MetaFrameFlags flags); PangoFontDescription * meta_style_info_create_font_desc (MetaStyleInfo *style_info); void meta_theme_draw_frame (MetaTheme *theme, MetaStyleInfo *style_info, cairo_t *cr, MetaFrameType type, MetaFrameFlags flags, int client_width, int client_height, PangoLayout *title_layout, int text_height, const MetaButtonLayout *button_layout, MetaButtonState button_states[META_BUTTON_TYPE_LAST], cairo_surface_t *mini_icon); void meta_theme_get_frame_borders (MetaTheme *theme, MetaStyleInfo *style_info, MetaFrameType type, int text_height, MetaFrameFlags flags, MetaFrameBorders *borders); void meta_theme_calc_geometry (MetaTheme *theme, MetaStyleInfo *style_info, MetaFrameType type, int text_height, MetaFrameFlags flags, int client_width, int client_height, const MetaButtonLayout *button_layout, MetaFrameGeometry *fgeom); /* random stuff */ int meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc, PangoContext *context); int meta_theme_get_window_scaling_factor (void); #endif /* META_THEME_PRIVATE_H */ muffin-6.4.1/src/ui/frames.c0000664000175000017500000016350114723361714014575 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Metacity window frame manager widget */ /* * Copyright (C) 2001 Havoc Pennington * Copyright (C) 2003 Red Hat, Inc. * Copyright (C) 2005, 2006 Elijah Newren * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include "core/frame.h" #include "core/window-private.h" #include "meta/boxes.h" #include "meta/prefs.h" #include "meta/theme.h" #include "meta/util.h" #include "ui/ui.h" #include "ui/frames.h" #include "x11/meta-x11-window-control.h" #include "x11/window-x11-private.h" #include "x11/window-x11.h" #define DEFAULT_INNER_BUTTON_BORDER 3 static void meta_frames_destroy (GtkWidget *object); static void meta_frames_finalize (GObject *object); static void meta_frames_style_updated (GtkWidget *widget); static gboolean meta_frames_draw (GtkWidget *widget, cairo_t *cr); static void meta_ui_frame_attach_style (MetaUIFrame *frame); static void meta_ui_frame_paint (MetaUIFrame *frame, cairo_t *cr); static void meta_ui_frame_calc_geometry (MetaUIFrame *frame, MetaFrameGeometry *fgeom); static void meta_ui_frame_update_prelit_control (MetaUIFrame *frame, MetaFrameControl control); static void meta_frames_font_changed (MetaFrames *frames); static void meta_frames_button_layout_changed (MetaFrames *frames); static GdkRectangle* control_rect (MetaFrameControl control, MetaFrameGeometry *fgeom); static MetaFrameControl get_control (MetaUIFrame *frame, int x, int y); G_DEFINE_TYPE (MetaFrames, meta_frames, GTK_TYPE_WINDOW); enum { META_ACTION_CLICK, META_ACTION_RIGHT_CLICK, META_ACTION_MIDDLE_CLICK, META_ACTION_DOUBLE_CLICK, META_ACTION_SCROLL, META_ACTION_IGNORE }; static GObject * meta_frames_constructor (GType gtype, guint n_properties, GObjectConstructParam *properties) { GObject *object; GObjectClass *gobject_class; gobject_class = G_OBJECT_CLASS (meta_frames_parent_class); object = gobject_class->constructor (gtype, n_properties, properties); g_object_set (object, "type", GTK_WINDOW_POPUP, NULL); return object; } static void meta_frames_class_init (MetaFramesClass *class) { GObjectClass *gobject_class; GtkWidgetClass *widget_class; gobject_class = G_OBJECT_CLASS (class); widget_class = (GtkWidgetClass*) class; gobject_class->constructor = meta_frames_constructor; gobject_class->finalize = meta_frames_finalize; widget_class->destroy = meta_frames_destroy; widget_class->style_updated = meta_frames_style_updated; widget_class->draw = meta_frames_draw; } static gint unsigned_long_equal (gconstpointer v1, gconstpointer v2) { return *((const gulong*) v1) == *((const gulong*) v2); } static guint unsigned_long_hash (gconstpointer v) { gulong val = * (const gulong *) v; /* I'm not sure this works so well. */ #if GLIB_SIZEOF_LONG > 4 return (guint) (val ^ (val >> 32)); #else return val; #endif } static void prefs_changed_callback (MetaPreference pref, void *data) { switch (pref) { case META_PREF_TITLEBAR_FONT: meta_frames_font_changed (META_FRAMES (data)); break; case META_PREF_BUTTON_LAYOUT: meta_frames_button_layout_changed (META_FRAMES (data)); break; case META_PREF_GTK_THEME: meta_frames_style_updated (GTK_WIDGET (data)); break; default: break; } } static void invalidate_whole_window (MetaUIFrame *frame) { if (!frame->is_frozen) { meta_window_x11_freeze_commits (frame->meta_window); frame->is_frozen = TRUE; } gdk_window_invalidate_rect (frame->window, NULL, FALSE); } static MetaStyleInfo * meta_frames_get_theme_variant (MetaFrames *frames, const gchar *variant) { MetaStyleInfo *style_info; style_info = g_hash_table_lookup (frames->style_variants, variant); if (style_info == NULL) { style_info = meta_theme_create_style_info (gtk_widget_get_screen (GTK_WIDGET (frames)), variant); g_hash_table_insert (frames->style_variants, g_strdup (variant), style_info); } return style_info; } static void update_style_contexts (MetaFrames *frames) { MetaStyleInfo *style_info; GList *variants, *variant; GdkScreen *screen; screen = gtk_widget_get_screen (GTK_WIDGET (frames)); if (frames->normal_style) meta_style_info_unref (frames->normal_style); frames->normal_style = meta_theme_create_style_info (screen, NULL); variants = g_hash_table_get_keys (frames->style_variants); for (variant = variants; variant; variant = variant->next) { style_info = meta_theme_create_style_info (screen, (char *)variant->data); g_hash_table_insert (frames->style_variants, g_strdup (variant->data), style_info); } g_list_free (variants); } static void meta_frames_init (MetaFrames *frames) { frames->text_heights = g_hash_table_new (NULL, NULL); frames->frames = g_hash_table_new (unsigned_long_hash, unsigned_long_equal); frames->style_variants = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)meta_style_info_unref); update_style_contexts (frames); meta_prefs_add_listener (prefs_changed_callback, frames); } static void listify_func (gpointer key, gpointer value, gpointer data) { GSList **listp; listp = data; *listp = g_slist_prepend (*listp, value); } static void meta_frames_destroy (GtkWidget *object) { GSList *winlist; GSList *tmp; MetaFrames *frames; frames = META_FRAMES (object); winlist = NULL; g_hash_table_foreach (frames->frames, listify_func, &winlist); /* Unmanage all frames */ for (tmp = winlist; tmp != NULL; tmp = tmp->next) { MetaUIFrame *frame = tmp->data; meta_ui_frame_unmanage (frame); } g_slist_free (winlist); if (frames->normal_style) { meta_style_info_unref (frames->normal_style); frames->normal_style = NULL; } if (frames->style_variants) { g_hash_table_destroy (frames->style_variants); frames->style_variants = NULL; } GTK_WIDGET_CLASS (meta_frames_parent_class)->destroy (object); } static void meta_frames_finalize (GObject *object) { MetaFrames *frames; frames = META_FRAMES (object); meta_prefs_remove_listener (prefs_changed_callback, frames); g_hash_table_destroy (frames->text_heights); g_assert (g_hash_table_size (frames->frames) == 0); g_hash_table_destroy (frames->frames); G_OBJECT_CLASS (meta_frames_parent_class)->finalize (object); } static void queue_recalc_func (gpointer key, gpointer value, gpointer user_data) { MetaUIFrame *frame = value; MetaFrames *frames = user_data; invalidate_whole_window (frame); meta_x11_wm_queue_frame_resize (frames->x11_display, frame->xwindow); g_clear_object (&frame->text_layout); } static void meta_frames_font_changed (MetaFrames *frames) { if (g_hash_table_size (frames->text_heights) > 0) { g_hash_table_destroy (frames->text_heights); frames->text_heights = g_hash_table_new (NULL, NULL); } /* Queue a draw/resize on all frames */ g_hash_table_foreach (frames->frames, queue_recalc_func, frames); } static void queue_draw_func (gpointer key, gpointer value, gpointer data) { MetaUIFrame *frame = value; invalidate_whole_window (frame); } static void meta_frames_button_layout_changed (MetaFrames *frames) { g_hash_table_foreach (frames->frames, queue_draw_func, frames); } static void reattach_style_func (gpointer key, gpointer value, gpointer data) { MetaUIFrame *frame = value; meta_ui_frame_attach_style (frame); } static void meta_frames_style_updated (GtkWidget *widget) { MetaFrames *frames; frames = META_FRAMES (widget); meta_frames_font_changed (frames); update_style_contexts (frames); g_hash_table_foreach (frames->frames, reattach_style_func, NULL); meta_display_queue_retheme_all_windows (meta_get_display ()); GTK_WIDGET_CLASS (meta_frames_parent_class)->style_updated (widget); } static void meta_ui_frame_ensure_layout (MetaUIFrame *frame, MetaFrameType type) { MetaFrames *frames = frame->frames; GtkWidget *widget; MetaFrameLayout *layout; widget = GTK_WIDGET (frames); g_return_if_fail (gtk_widget_get_realized (widget)); layout = meta_theme_get_frame_layout (meta_theme_get_default (), type); if (layout != frame->cache_layout) g_clear_object (&frame->text_layout); frame->cache_layout = layout; if (frame->text_layout == NULL) { gpointer key, value; PangoFontDescription *font_desc; int size; frame->text_layout = gtk_widget_create_pango_layout (widget, frame->title); pango_layout_set_ellipsize (frame->text_layout, PANGO_ELLIPSIZE_END); pango_layout_set_auto_dir (frame->text_layout, FALSE); pango_layout_set_single_paragraph_mode (frame->text_layout, TRUE); font_desc = meta_style_info_create_font_desc (frame->style_info); meta_frame_layout_apply_scale (layout, font_desc); size = pango_font_description_get_size (font_desc); if (g_hash_table_lookup_extended (frames->text_heights, GINT_TO_POINTER (size), &key, &value)) { frame->text_height = GPOINTER_TO_INT (value); } else { frame->text_height = meta_pango_font_desc_get_text_height (font_desc, gtk_widget_get_pango_context (widget)); g_hash_table_replace (frames->text_heights, GINT_TO_POINTER (size), GINT_TO_POINTER (frame->text_height)); } pango_layout_set_font_description (frame->text_layout, font_desc); pango_font_description_free (font_desc); } } static void meta_ui_frame_calc_geometry (MetaUIFrame *frame, MetaFrameGeometry *fgeom) { MetaFrameFlags flags; MetaFrameType type; MetaButtonLayout button_layout; MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->meta_window); MetaWindowX11Private *priv = window_x11->priv; flags = meta_frame_get_flags (frame->meta_window->frame); type = meta_window_get_frame_type (frame->meta_window); meta_ui_frame_ensure_layout (frame, type); meta_prefs_get_button_layout (&button_layout); meta_theme_calc_geometry (meta_theme_get_default (), frame->style_info, type, frame->text_height, flags, priv->client_rect.width, priv->client_rect.height, &button_layout, fgeom); } MetaFrames* meta_frames_new (MetaX11Display *x11_display) { MetaFrames *frames; frames = g_object_new (META_TYPE_FRAMES, "type", GTK_WINDOW_POPUP, NULL); frames->x11_display = x11_display; /* Put the window at an arbitrary offscreen location; the one place * it can't be is at -100x-100, since the meta_window_new() will * mistake it for a window created via meta_create_offscreen_window() * and ignore it, and we need this window to get frame-synchronization * messages so that GTK+'s style change handling works. */ gtk_window_move (GTK_WINDOW (frames), -200, -200); gtk_window_resize (GTK_WINDOW (frames), 1, 1); return frames; } static const char * get_global_theme_variant (MetaFrames *frames) { GdkScreen *screen = gtk_widget_get_screen (GTK_WIDGET (frames)); GtkSettings *settings = gtk_settings_get_for_screen (screen); gboolean dark_theme_requested; g_object_get (settings, "gtk-application-prefer-dark-theme", &dark_theme_requested, NULL); if (dark_theme_requested) return "dark"; return NULL; } /* In order to use a style with a window it has to be attached to that * window. Actually, the colormaps just have to match, but since GTK+ * already takes care of making sure that its cheap to attach a style * to multiple windows with the same colormap, we can just go ahead * and attach separately for each window. */ static void meta_ui_frame_attach_style (MetaUIFrame *frame) { MetaFrames *frames = frame->frames; const char *variant; if (frame->style_info != NULL) meta_style_info_unref (frame->style_info); variant = frame->meta_window->gtk_theme_variant; if (variant == NULL) variant = get_global_theme_variant (frame->frames);; if (variant == NULL || *variant == '\0') frame->style_info = meta_style_info_ref (frames->normal_style); else frame->style_info = meta_style_info_ref (meta_frames_get_theme_variant (frames, variant)); } MetaUIFrame * meta_frames_manage_window (MetaFrames *frames, MetaWindow *meta_window, Window xwindow, GdkWindow *window) { MetaUIFrame *frame; g_assert (window); frame = g_new (MetaUIFrame, 1); frame->frames = frames; frame->window = window; gdk_window_set_user_data (frame->window, frames); frame->style_info = NULL; /* Don't set event mask here, it's in frame.c */ frame->xwindow = xwindow; frame->meta_window = meta_window; frame->cache_layout = NULL; frame->text_layout = NULL; frame->text_height = -1; frame->title = NULL; frame->prelit_control = META_FRAME_CONTROL_NONE; frame->button_state = META_BUTTON_STATE_NORMAL; frame->is_frozen = FALSE; meta_x11_wm_grab_buttons (frames->x11_display, frame->xwindow); g_hash_table_replace (frames->frames, &frame->xwindow, frame); return frame; } void meta_ui_frame_unmanage (MetaUIFrame *frame) { MetaFrames *frames = frame->frames; /* restore the cursor */ meta_x11_wm_set_screen_cursor (frames->x11_display, frame->xwindow, META_CURSOR_DEFAULT); gdk_window_set_user_data (frame->window, NULL); g_hash_table_remove (frames->frames, &frame->xwindow); meta_style_info_unref (frame->style_info); gdk_window_destroy (frame->window); if (frame->text_layout) g_object_unref (G_OBJECT (frame->text_layout)); if (frame->is_frozen) meta_window_x11_thaw_commits (frame->meta_window); g_free (frame->title); g_free (frame); } void meta_ui_frame_get_borders (MetaUIFrame *frame, MetaFrameBorders *borders) { MetaFrameFlags flags; MetaFrameType type; flags = meta_frame_get_flags (frame->meta_window->frame); type = meta_window_get_frame_type (frame->meta_window); g_return_if_fail (type < META_FRAME_TYPE_LAST); meta_ui_frame_ensure_layout (frame, type); /* We can't get the full geometry, because that depends on * the client window size and probably we're being called * by the core move/resize code to decide on the client * window size */ meta_theme_get_frame_borders (meta_theme_get_default (), frame->style_info, type, frame->text_height, flags, borders); } /* The visible frame rectangle surrounds the visible portion of the * frame window; it subtracts only the invisible borders from the frame * window's size. */ static void get_visible_frame_rect (MetaFrameGeometry *fgeom, cairo_rectangle_int_t *rect) { rect->x = fgeom->borders.invisible.left; rect->y = fgeom->borders.invisible.top; rect->width = fgeom->width - fgeom->borders.invisible.right - rect->x; rect->height = fgeom->height - fgeom->borders.invisible.bottom - rect->y; } static cairo_region_t * get_visible_region (MetaUIFrame *frame, MetaFrameGeometry *fgeom) { cairo_region_t *corners_region; cairo_region_t *visible_region; cairo_rectangle_int_t rect; cairo_rectangle_int_t frame_rect; corners_region = cairo_region_create (); get_visible_frame_rect (fgeom, &frame_rect); if (fgeom->top_left_corner_rounded_radius != 0) { const int corner = fgeom->top_left_corner_rounded_radius; const float radius = corner; int i; for (i=0; itop_right_corner_rounded_radius != 0) { const int corner = fgeom->top_right_corner_rounded_radius; const float radius = corner; int i; for (i=0; ibottom_left_corner_rounded_radius != 0) { const int corner = fgeom->bottom_left_corner_rounded_radius; const float radius = corner; int i; for (i=0; ibottom_right_corner_rounded_radius != 0) { const int corner = fgeom->bottom_right_corner_rounded_radius; const float radius = corner; int i; for (i=0; iwindow); old_height = gdk_window_get_height (frame->window); gdk_window_move_resize (frame->window, x, y, width, height); if (old_width != width || old_height != height) invalidate_whole_window (frame); } void meta_ui_frame_queue_draw (MetaUIFrame *frame) { invalidate_whole_window (frame); } void meta_ui_frame_set_title (MetaUIFrame *frame, const char *title) { g_free (frame->title); frame->title = g_strdup (title); g_clear_object (&frame->text_layout); invalidate_whole_window (frame); } void meta_ui_frame_update_style (MetaUIFrame *frame) { meta_ui_frame_attach_style (frame); invalidate_whole_window (frame); } static void redraw_control (MetaUIFrame *frame, MetaFrameControl control) { MetaFrameGeometry fgeom; GdkRectangle *rect; meta_ui_frame_calc_geometry (frame, &fgeom); rect = control_rect (control, &fgeom); gdk_window_invalidate_rect (frame->window, rect, FALSE); } static gboolean meta_frame_titlebar_event (MetaUIFrame *frame, const ClutterEvent *event, int action) { MetaFrameFlags flags; MetaX11Display *x11_display; uint32_t evtime; float x, y; g_assert (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_SCROLL); x11_display = frame->frames->x11_display; flags = meta_frame_get_flags (frame->meta_window->frame); evtime = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); switch (action) { case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_SHADE: { if (flags & META_FRAME_ALLOWS_SHADE) { if (flags & META_FRAME_SHADED) meta_window_unshade (frame->meta_window, evtime); else meta_window_shade (frame->meta_window, evtime); } } break; case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_x11_wm_toggle_maximize (x11_display, frame->xwindow); } } break; case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_HORIZONTALLY: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_x11_wm_toggle_maximize_horizontally (x11_display, frame->xwindow); } } break; case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_VERTICALLY: { if (flags & META_FRAME_ALLOWS_MAXIMIZE) { meta_x11_wm_toggle_maximize_vertically (x11_display, frame->xwindow); } } break; case C_DESKTOP_TITLEBAR_ACTION_MINIMIZE: { if (flags & META_FRAME_ALLOWS_MINIMIZE) meta_window_minimize (frame->meta_window); } break; case C_DESKTOP_TITLEBAR_ACTION_NONE: /* Yaay, a sane user that doesn't use that other weird crap! */ break; case C_DESKTOP_TITLEBAR_ACTION_LOWER: meta_x11_wm_user_lower_and_unfocus (x11_display, frame->xwindow, evtime); break; case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_STUCK: { if (flags & META_FRAME_STUCK) { meta_window_unstick (frame->meta_window); } else { meta_window_stick (frame->meta_window); } } break; case C_DESKTOP_TITLEBAR_ACTION_TOGGLE_ABOVE: { if (flags & META_FRAME_ABOVE) { meta_window_unmake_above (frame->meta_window); } else { meta_window_make_above (frame->meta_window); } } break; case C_DESKTOP_TITLEBAR_ACTION_MENU: meta_x11_wm_show_window_menu (x11_display, frame->xwindow, META_WINDOW_MENU_WM, x, y, evtime); break; case C_DESKTOP_TITLEBAR_SCROLL_ACTION_SHADE: { if (flags & META_FRAME_ALLOWS_SHADE) { if (event->scroll.direction == CLUTTER_SCROLL_UP && !(flags & META_FRAME_SHADED)) { meta_window_shade (frame->meta_window, evtime); } else if (event->scroll.direction == CLUTTER_SCROLL_DOWN && (flags & META_FRAME_SHADED)) { meta_window_unshade (frame->meta_window, evtime); } } } break; case C_DESKTOP_TITLEBAR_SCROLL_ACTION_OPACITY: { if (event->scroll.direction == CLUTTER_SCROLL_UP) { meta_window_increase_opacity (frame->meta_window); } else if (event->scroll.direction == CLUTTER_SCROLL_DOWN) { meta_window_decrease_opacity (frame->meta_window); } } break; } return TRUE; } static gboolean meta_frame_double_click_event (MetaUIFrame *frame, const ClutterEvent *event) { int action = meta_prefs_get_action_double_click_titlebar (); return meta_frame_titlebar_event (frame, event, action); } static gboolean meta_frame_middle_click_event (MetaUIFrame *frame, ClutterButtonEvent *event) { int action = meta_prefs_get_action_middle_click_titlebar(); return meta_frame_titlebar_event (frame, (const ClutterEvent *) event, action); } static gboolean meta_frame_right_click_event (MetaUIFrame *frame, ClutterButtonEvent *event) { int action = meta_prefs_get_action_right_click_titlebar(); return meta_frame_titlebar_event (frame, (const ClutterEvent *) event, action); } static gboolean meta_frame_scroll_event (MetaUIFrame *frame, ClutterScrollEvent *event) { int action = meta_prefs_get_action_scroll_wheel_titlebar(); return meta_frame_titlebar_event (frame, (const ClutterEvent *) event, action); } static gboolean meta_frames_try_grab_op (MetaUIFrame *frame, MetaGrabOp op, gdouble grab_x, gdouble grab_y, guint32 time) { MetaFrames *frames = frame->frames; gboolean ret; ret = meta_x11_wm_begin_grab_op (frames->x11_display, frame->xwindow, op, FALSE, TRUE, frame->grab_button, 0, time, grab_x, grab_y); if (!ret) { frames->current_grab_op = op; frames->grab_frame = frame; frames->grab_x = grab_x; frames->grab_y = grab_y; } else frames->grab_touch = NULL; return ret; } static gboolean meta_frames_retry_grab_op (MetaFrames *frames, guint time) { MetaGrabOp op; gboolean ret; if (frames->current_grab_op == META_GRAB_OP_NONE) return TRUE; op = frames->current_grab_op; frames->current_grab_op = META_GRAB_OP_NONE; ret = meta_x11_wm_begin_grab_op (frames->x11_display, frames->grab_frame->xwindow, op, FALSE, TRUE, frames->grab_frame->grab_button, 0, time, frames->grab_x, frames->grab_y); if (ret) frames->grab_touch = NULL; return ret; } static MetaGrabOp grab_op_from_resize_control (MetaFrameControl control) { switch (control) { case META_FRAME_CONTROL_RESIZE_SE: return META_GRAB_OP_RESIZING_SE; case META_FRAME_CONTROL_RESIZE_S: return META_GRAB_OP_RESIZING_S; case META_FRAME_CONTROL_RESIZE_SW: return META_GRAB_OP_RESIZING_SW; case META_FRAME_CONTROL_RESIZE_NE: return META_GRAB_OP_RESIZING_NE; case META_FRAME_CONTROL_RESIZE_N: return META_GRAB_OP_RESIZING_N; case META_FRAME_CONTROL_RESIZE_NW: return META_GRAB_OP_RESIZING_NW; case META_FRAME_CONTROL_RESIZE_E: return META_GRAB_OP_RESIZING_E; case META_FRAME_CONTROL_RESIZE_W: return META_GRAB_OP_RESIZING_W; default: g_assert_not_reached (); return META_GRAB_OP_NONE; } } static guint get_action (const ClutterEvent *event) { if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) { switch (event->button.button) { case CLUTTER_BUTTON_PRIMARY: if (clutter_event_get_click_count (event) == 2) return META_ACTION_DOUBLE_CLICK; else return META_ACTION_CLICK; case CLUTTER_BUTTON_SECONDARY: return META_ACTION_RIGHT_CLICK; case CLUTTER_BUTTON_MIDDLE: return META_ACTION_MIDDLE_CLICK; default: meta_verbose ("No action triggered for button %u %s\n", event->button.button, (event->type == CLUTTER_BUTTON_PRESS) ? "press" : "release"); } } else if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_UPDATE || event->type == CLUTTER_TOUCH_END) { return META_ACTION_CLICK; } else if (event->type == CLUTTER_SCROLL) { return META_ACTION_SCROLL; } return META_ACTION_IGNORE; } static uint32_t get_button_number (const ClutterEvent *event) { if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_UPDATE || event->type == CLUTTER_TOUCH_END) return -1; else if (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) return clutter_event_get_button (event); g_assert_not_reached (); return -1; } static gboolean meta_frame_double_click_edge_event (MetaUIFrame *frame, const ClutterEvent *event, MetaFrameControl control) { switch (control) { case META_FRAME_CONTROL_RESIZE_N: case META_FRAME_CONTROL_RESIZE_S: return meta_frame_titlebar_event (frame, event, C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_VERTICALLY); case META_FRAME_CONTROL_RESIZE_E: case META_FRAME_CONTROL_RESIZE_W: return meta_frame_titlebar_event (frame, event, C_DESKTOP_TITLEBAR_ACTION_TOGGLE_MAXIMIZE_HORIZONTALLY); default: return FALSE; } } static gboolean meta_frame_left_click_event (MetaUIFrame *frame, const ClutterEvent *event) { MetaX11Display *x11_display = frame->frames->x11_display; MetaFrameControl control; guint32 evtime; gfloat x, y; evtime = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); control = get_control (frame, x, y); switch (control) { case META_FRAME_CONTROL_MAXIMIZE: case META_FRAME_CONTROL_UNMAXIMIZE: case META_FRAME_CONTROL_MINIMIZE: case META_FRAME_CONTROL_DELETE: case META_FRAME_CONTROL_MENU: frame->grab_button = get_button_number (event); frame->button_state = META_BUTTON_STATE_PRESSED; frame->prelit_control = control; redraw_control (frame, control); if (control == META_FRAME_CONTROL_MENU) { MetaFrameGeometry fgeom; GdkRectangle *rect; MetaRectangle root_rect; int win_x, win_y; meta_ui_frame_calc_geometry (frame, &fgeom); rect = control_rect (control, &fgeom); gdk_window_get_position (frame->window, &win_x, &win_y); root_rect.x = win_x + rect->x; root_rect.y = win_y + rect->y; root_rect.width = rect->width; root_rect.height = rect->height; /* if the compositor takes a grab for showing the menu, we will * get a LeaveNotify event we want to ignore, to keep the pressed * button state while the menu is open */ frame->maybe_ignore_leave_notify = TRUE; meta_x11_wm_show_window_menu_for_rect (x11_display, frame->xwindow, META_WINDOW_MENU_WM, &root_rect, evtime); } else { meta_frames_try_grab_op (frame, META_GRAB_OP_FRAME_BUTTON, x, y, evtime); } return TRUE; case META_FRAME_CONTROL_RESIZE_SE: case META_FRAME_CONTROL_RESIZE_S: case META_FRAME_CONTROL_RESIZE_SW: case META_FRAME_CONTROL_RESIZE_NE: case META_FRAME_CONTROL_RESIZE_N: case META_FRAME_CONTROL_RESIZE_NW: case META_FRAME_CONTROL_RESIZE_E: case META_FRAME_CONTROL_RESIZE_W: meta_frames_try_grab_op (frame, grab_op_from_resize_control (control), x, y, evtime); return TRUE; case META_FRAME_CONTROL_TITLE: { MetaFrameFlags flags = meta_frame_get_flags (frame->meta_window->frame); if (flags & META_FRAME_ALLOWS_MOVE) { meta_frames_try_grab_op (frame, META_GRAB_OP_MOVING, x, y, evtime); } } return TRUE; case META_FRAME_CONTROL_NONE: /* We can get this for example when trying to resize window * that cannot be resized (e. g. it is maximized and the theme * currently used has borders for maximized windows), see #751884 */ case META_FRAME_CONTROL_CLIENT_AREA: /* See https://gitlab.gnome.org/GNOME/mutter/-/issues/1668#note_1046828. * This can happen when clicking on the frame of a tiles, unfocused window. */ return FALSE; default: g_assert_not_reached (); return FALSE; } } static gboolean handle_press_event (MetaUIFrame *frame, const ClutterEvent *event) { MetaFrameControl control; uint32_t evtime, action; float x, y; g_assert (event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_TOUCH_BEGIN); action = get_action (event); if (action == META_ACTION_IGNORE) return FALSE; evtime = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); control = get_control (frame, x, y); /* don't do the rest of this if on client area */ if (control == META_FRAME_CONTROL_CLIENT_AREA) return FALSE; /* not on the frame, just passed through from client */ if (action == META_ACTION_CLICK && !(control == META_FRAME_CONTROL_MINIMIZE || control == META_FRAME_CONTROL_DELETE || control == META_FRAME_CONTROL_MAXIMIZE)) { meta_topic (META_DEBUG_FOCUS, "Focusing window with frame 0x%lx due to button 1 press\n", frame->xwindow); meta_window_focus (frame->meta_window, evtime); } /* We want to shade even if we have a GrabOp, since we'll have a move grab * if we double click the titlebar. */ if (control == META_FRAME_CONTROL_TITLE && action == META_ACTION_DOUBLE_CLICK) { meta_x11_wm_end_grab_op (frame->frames->x11_display, evtime); return meta_frame_double_click_event (frame, event); } if ((control == META_FRAME_CONTROL_RESIZE_N || control == META_FRAME_CONTROL_RESIZE_S || control == META_FRAME_CONTROL_RESIZE_E || control == META_FRAME_CONTROL_RESIZE_W) && action == META_ACTION_DOUBLE_CLICK) { meta_x11_wm_end_grab_op (frame->frames->x11_display, evtime); return meta_frame_double_click_edge_event (frame, event, control); } if (meta_x11_wm_get_grab_op (frame->frames->x11_display) != META_GRAB_OP_NONE) return FALSE; /* already up to something */ frame->grab_button = get_button_number (event); switch (action) { case META_ACTION_CLICK: return meta_frame_left_click_event (frame, event); case META_ACTION_MIDDLE_CLICK: return meta_frame_middle_click_event (frame, (ClutterButtonEvent *) event); case META_ACTION_RIGHT_CLICK: return meta_frame_right_click_event (frame, (ClutterButtonEvent *) event); default: return FALSE; } } static gboolean handle_release_event (MetaUIFrame *frame, const ClutterEvent *event) { guint32 evtime, button; gfloat x, y; g_assert (event->type == CLUTTER_BUTTON_RELEASE || event->type == CLUTTER_TOUCH_END); evtime = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); button = get_button_number (event); frame->frames->current_grab_op = META_GRAB_OP_NONE; meta_x11_wm_end_grab_op (frame->frames->x11_display, evtime); /* We only handle the releases we handled the presses for (things * involving frame controls). Window ops that don't require a * frame are handled in the Xlib part of the code, display.c/window.c */ if (((int) button) == frame->grab_button && frame->button_state == META_BUTTON_STATE_PRESSED) { switch (frame->prelit_control) { case META_FRAME_CONTROL_MINIMIZE: meta_window_minimize (frame->meta_window); break; case META_FRAME_CONTROL_MAXIMIZE: /* Focus the window on the maximize */ meta_window_focus (frame->meta_window, evtime); if (meta_prefs_get_raise_on_click ()) meta_window_raise (frame->meta_window); meta_window_maximize (frame->meta_window, META_MAXIMIZE_BOTH); break; case META_FRAME_CONTROL_UNMAXIMIZE: if (meta_prefs_get_raise_on_click ()) meta_window_raise (frame->meta_window); meta_window_unmaximize (frame->meta_window, META_MAXIMIZE_BOTH); break; case META_FRAME_CONTROL_DELETE: meta_window_delete (frame->meta_window, evtime); break; default: break; } /* Update the prelit control regardless of what button the mouse * was released over; needed so that the new button can become * prelit so to let the user know that it can now be pressed. * :) */ MetaFrameControl control = get_control (frame, x, y); meta_ui_frame_update_prelit_control (frame, control); } return TRUE; } static gboolean handle_scroll_event (MetaUIFrame *frame, const ClutterEvent *event) { MetaFrameControl control; uint32_t action; float x, y; g_assert (event->type == CLUTTER_SCROLL); action = get_action (event); if (action == META_ACTION_IGNORE) return FALSE; clutter_event_get_coords (event, &x, &y); control = get_control (frame, x, y); if (control != META_FRAME_CONTROL_TITLE) { return FALSE; } if (meta_x11_wm_get_grab_op (frame->frames->x11_display) != META_GRAB_OP_NONE) return FALSE; /* already up to something */ return meta_frame_scroll_event (frame, (ClutterScrollEvent *) event); } static void meta_ui_frame_update_prelit_control (MetaUIFrame *frame, MetaFrameControl control) { MetaFrameControl old_control; MetaCursor cursor; meta_verbose ("Updating prelit control from %u to %u\n", frame->prelit_control, control); cursor = META_CURSOR_DEFAULT; switch (control) { case META_FRAME_CONTROL_CLIENT_AREA: break; case META_FRAME_CONTROL_NONE: break; case META_FRAME_CONTROL_TITLE: break; case META_FRAME_CONTROL_DELETE: break; case META_FRAME_CONTROL_MENU: break; case META_FRAME_CONTROL_MINIMIZE: break; case META_FRAME_CONTROL_MAXIMIZE: break; case META_FRAME_CONTROL_UNMAXIMIZE: break; case META_FRAME_CONTROL_RESIZE_SE: cursor = META_CURSOR_SE_RESIZE; break; case META_FRAME_CONTROL_RESIZE_S: cursor = META_CURSOR_SOUTH_RESIZE; break; case META_FRAME_CONTROL_RESIZE_SW: cursor = META_CURSOR_SW_RESIZE; break; case META_FRAME_CONTROL_RESIZE_N: cursor = META_CURSOR_NORTH_RESIZE; break; case META_FRAME_CONTROL_RESIZE_NE: cursor = META_CURSOR_NE_RESIZE; break; case META_FRAME_CONTROL_RESIZE_NW: cursor = META_CURSOR_NW_RESIZE; break; case META_FRAME_CONTROL_RESIZE_W: cursor = META_CURSOR_WEST_RESIZE; break; case META_FRAME_CONTROL_RESIZE_E: cursor = META_CURSOR_EAST_RESIZE; break; } /* set/unset the prelight cursor */ meta_x11_wm_set_screen_cursor (frame->frames->x11_display, frame->xwindow, cursor); switch (control) { case META_FRAME_CONTROL_MENU: case META_FRAME_CONTROL_MINIMIZE: case META_FRAME_CONTROL_MAXIMIZE: case META_FRAME_CONTROL_DELETE: case META_FRAME_CONTROL_UNMAXIMIZE: /* leave control set */ break; default: /* Only prelight buttons */ control = META_FRAME_CONTROL_NONE; break; } if (control == frame->prelit_control && frame->button_state == META_BUTTON_STATE_PRELIGHT) return; /* Save the old control so we can unprelight it */ old_control = frame->prelit_control; frame->button_state = META_BUTTON_STATE_PRELIGHT; frame->prelit_control = control; redraw_control (frame, old_control); redraw_control (frame, control); } static gboolean handle_motion_event (MetaUIFrame *frame, const ClutterEvent *event) { MetaFrames *frames = frame->frames; MetaFrameControl control; ClutterModifierType modifiers; guint32 evtime; gfloat x, y; g_assert (event->type == CLUTTER_MOTION || event->type == CLUTTER_TOUCH_UPDATE); modifiers = clutter_event_get_state (event); evtime = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); control = get_control (frame, x, y); if (frame->button_state == META_BUTTON_STATE_PRESSED) { /* If the user leaves the frame button, set the state * back to normal and redraw. */ if (frame->prelit_control != control) { frame->button_state = META_BUTTON_STATE_NORMAL; redraw_control (frame, frame->prelit_control); } } else { /* Update prelit control and cursor */ meta_ui_frame_update_prelit_control (frame, control); } if (frames->current_grab_op != META_GRAB_OP_NONE && (event->type == CLUTTER_TOUCH_UPDATE || (event->type == CLUTTER_MOTION && (modifiers & CLUTTER_BUTTON1_MASK)))) meta_frames_retry_grab_op (frames, evtime); return TRUE; } static cairo_region_t * get_visible_frame_border_region (MetaUIFrame *frame) { cairo_rectangle_int_t area; cairo_region_t *frame_border; MetaFrameFlags flags; MetaFrameType type; MetaFrameBorders borders; MetaRectangle buffer_rect = frame->meta_window->buffer_rect; flags = meta_frame_get_flags (frame->meta_window->frame); type = meta_window_get_frame_type (frame->meta_window); meta_theme_get_frame_borders (meta_theme_get_default (), frame->style_info, type, frame->text_height, flags, &borders); /* Frame rect */ area.x = 0; area.y = 0; area.width = buffer_rect.width; area.height = buffer_rect.height; frame_border = cairo_region_create_rectangle (&area); /* Client rect */ area.x += borders.total.left; area.y += borders.total.top; area.width -= borders.total.left + borders.total.right; area.height -= borders.total.top + borders.total.bottom; /* Visible frame border */ cairo_region_subtract_rectangle (frame_border, &area); return frame_border; } /* * Draw the opaque and semi-opaque pixels of this frame into a mask. * * (0,0) in Cairo coordinates is assumed to be the top left corner of the * invisible border. * * The parts of @cr's surface in the clip region are assumed to be * initialized to fully-transparent, and the clip region is assumed to * contain the invisible border and the visible parts of the frame, but * not the client area. * * This function uses @cr to draw pixels of arbitrary color (it will * typically be drawing in a %CAIRO_FORMAT_A8 surface, so the color is * discarded anyway) with appropriate alpha values to reproduce this * frame's alpha channel, as a mask to be applied to an opaque pixmap. * * @frame: This frame * @frame_rect: The frame rect * @cr: Used to draw the resulting mask */ void meta_ui_frame_get_mask (MetaUIFrame *frame, cairo_rectangle_int_t *frame_rect, cairo_t *cr) { MetaFrameBorders borders; MetaFrameFlags flags; cairo_surface_t *surface; double xscale, yscale; int scale; flags = meta_frame_get_flags (frame->meta_window->frame); meta_style_info_set_flags (frame->style_info, flags); meta_ui_frame_get_borders (frame, &borders); /* See comment in meta_frame_layout_draw_with_style() for details on HiDPI handling */ scale = meta_theme_get_window_scaling_factor (); surface = cairo_get_target (cr); cairo_surface_get_device_scale (surface, &xscale, &yscale); cairo_surface_set_device_scale (surface, scale, scale); gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_FRAME], cr, borders.invisible.left / scale, borders.invisible.top / scale, frame_rect->width / scale, frame_rect->height / scale); gtk_render_background (frame->style_info->styles[META_STYLE_ELEMENT_TITLEBAR], cr, borders.invisible.left / scale, borders.invisible.top / scale, frame_rect->width / scale, borders.total.top / scale); cairo_surface_set_device_scale (surface, xscale, yscale); } /* XXX -- this is disgusting. Find a better approach here. * Use multiple widgets? */ static MetaUIFrame * find_frame_to_draw (MetaFrames *frames, cairo_t *cr) { GHashTableIter iter; MetaUIFrame *frame; g_hash_table_iter_init (&iter, frames->frames); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &frame)) if (gtk_cairo_should_draw_window (cr, frame->window)) return frame; return NULL; } static gboolean meta_frames_draw (GtkWidget *widget, cairo_t *cr) { MetaUIFrame *frame; MetaFrames *frames; cairo_region_t *region; frames = META_FRAMES (widget); frame = find_frame_to_draw (frames, cr); if (frame == NULL) return FALSE; region = get_visible_frame_border_region (frame); gdk_cairo_region (cr, region); cairo_clip (cr); /* The target may be cleared to black or transparent, depending * on the frame's visual; we don't want decorations to appear * differently when the theme's decorations aren't fully opaque, * so clear to black first */ cairo_paint (cr); meta_ui_frame_paint (frame, cr); cairo_region_destroy (region); return TRUE; } static void meta_ui_frame_paint (MetaUIFrame *frame, cairo_t *cr) { MetaFrameFlags flags; MetaFrameType type; cairo_surface_t *mini_icon; MetaButtonState button_states[META_BUTTON_TYPE_LAST]; int i; int button_type = -1; MetaButtonLayout button_layout; MetaWindowX11 *window_x11 = META_WINDOW_X11 (frame->meta_window); MetaWindowX11Private *priv = window_x11->priv; for (i = 0; i < META_BUTTON_TYPE_LAST; i++) button_states[i] = META_BUTTON_STATE_NORMAL; /* Set prelight state */ switch (frame->prelit_control) { case META_FRAME_CONTROL_MENU: button_type = META_BUTTON_TYPE_MENU; break; case META_FRAME_CONTROL_MINIMIZE: button_type = META_BUTTON_TYPE_MINIMIZE; break; case META_FRAME_CONTROL_MAXIMIZE: button_type = META_BUTTON_TYPE_MAXIMIZE; break; case META_FRAME_CONTROL_UNMAXIMIZE: button_type = META_BUTTON_TYPE_MAXIMIZE; break; case META_FRAME_CONTROL_DELETE: button_type = META_BUTTON_TYPE_CLOSE; break; default: break; } if (button_type > -1) button_states[button_type] = frame->button_state; mini_icon = frame->meta_window->mini_icon; flags = meta_frame_get_flags (frame->meta_window->frame); type = meta_window_get_frame_type (frame->meta_window); meta_ui_frame_ensure_layout (frame, type); meta_prefs_get_button_layout (&button_layout); meta_theme_draw_frame (meta_theme_get_default (), frame->style_info, cr, type, flags, priv->client_rect.width, priv->client_rect.height, frame->text_layout, frame->text_height, &button_layout, button_states, mini_icon); if (frame->is_frozen) { meta_window_x11_thaw_commits (frame->meta_window); frame->is_frozen = FALSE; } } static gboolean handle_enter_notify_event (MetaUIFrame *frame, ClutterCrossingEvent *event) { MetaFrameControl control; frame->maybe_ignore_leave_notify = FALSE; control = get_control (frame, event->x, event->y); meta_ui_frame_update_prelit_control (frame, control); return TRUE; } static gboolean handle_leave_notify_event (MetaUIFrame *frame, ClutterCrossingEvent *event) { MetaGrabOp grab_op; grab_op = meta_x11_wm_get_grab_op (frame->frames->x11_display); /* ignore the first LeaveNotify event after opening a window menu * if it is the result of a compositor grab */ frame->maybe_ignore_leave_notify = frame->maybe_ignore_leave_notify && grab_op == META_GRAB_OP_COMPOSITOR; if (frame->maybe_ignore_leave_notify) return FALSE; meta_ui_frame_update_prelit_control (frame, META_FRAME_CONTROL_NONE); return TRUE; } gboolean meta_ui_frame_handle_event (MetaUIFrame *frame, const ClutterEvent *event) { if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_UPDATE || event->type == CLUTTER_TOUCH_END) { ClutterEventSequence *sequence; MetaFrames *frames = frame->frames; /* In X11, mutter sets up passive touch grabs which basically * means we handle those events twice (once through the passive * grab, and then through XISelectEvents). * * Receiving touch events here means we are going through the * former, but passive grabs are exclusively for gesture * recognition purposes. * * We do actually want this to happen though the regular event * selection paths to avoid breaking internal state, which means * we will get pointer events, because we don't select for XI_Touch*. */ if (!meta_is_wayland_compositor ()) return FALSE; sequence = clutter_event_get_event_sequence (event); /* Lock onto a single touch */ if (frames->grab_touch && frames->grab_touch != sequence) return FALSE; if (event->type == CLUTTER_TOUCH_BEGIN) frames->grab_touch = sequence; else if (event->type == CLUTTER_TOUCH_END) frames->grab_touch = NULL; } switch (event->any.type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_TOUCH_BEGIN: return handle_press_event (frame, event); case CLUTTER_BUTTON_RELEASE: case CLUTTER_TOUCH_END: return handle_release_event (frame, event); case CLUTTER_SCROLL: return handle_scroll_event (frame, event); case CLUTTER_MOTION: case CLUTTER_TOUCH_UPDATE: return handle_motion_event (frame, event); case CLUTTER_ENTER: return handle_enter_notify_event (frame, (ClutterCrossingEvent *) event); case CLUTTER_LEAVE: return handle_leave_notify_event (frame, (ClutterCrossingEvent *) event); default: return FALSE; } } static GdkRectangle* control_rect (MetaFrameControl control, MetaFrameGeometry *fgeom) { GdkRectangle *rect; rect = NULL; switch (control) { case META_FRAME_CONTROL_TITLE: rect = &fgeom->title_rect; break; case META_FRAME_CONTROL_DELETE: rect = &fgeom->close_rect.visible; break; case META_FRAME_CONTROL_MENU: rect = &fgeom->menu_rect.visible; break; case META_FRAME_CONTROL_MINIMIZE: rect = &fgeom->min_rect.visible; break; case META_FRAME_CONTROL_MAXIMIZE: case META_FRAME_CONTROL_UNMAXIMIZE: rect = &fgeom->max_rect.visible; break; case META_FRAME_CONTROL_RESIZE_SE: break; case META_FRAME_CONTROL_RESIZE_S: break; case META_FRAME_CONTROL_RESIZE_SW: break; case META_FRAME_CONTROL_RESIZE_N: break; case META_FRAME_CONTROL_RESIZE_NE: break; case META_FRAME_CONTROL_RESIZE_NW: break; case META_FRAME_CONTROL_RESIZE_W: break; case META_FRAME_CONTROL_RESIZE_E: break; case META_FRAME_CONTROL_NONE: break; case META_FRAME_CONTROL_CLIENT_AREA: break; } return rect; } #define TOP_RESIZE_HEIGHT 4 #define CORNER_SIZE_MULT 2 static MetaFrameControl get_control (MetaUIFrame *frame, int root_x, int root_y) { MetaFrameGeometry fgeom; MetaFrameFlags flags; MetaFrameType type; gboolean has_vert, has_horiz; gboolean has_north_resize; cairo_rectangle_int_t client; int x, y; int win_x, win_y; gdk_window_get_position (frame->window, &win_x, &win_y); x = root_x - win_x; y = root_y - win_y; meta_window_get_client_area_rect (frame->meta_window, &client); if (META_POINT_IN_RECT (x, y, client)) return META_FRAME_CONTROL_CLIENT_AREA; meta_ui_frame_calc_geometry (frame, &fgeom); if (META_POINT_IN_RECT (x, y, fgeom.close_rect.clickable)) return META_FRAME_CONTROL_DELETE; if (META_POINT_IN_RECT (x, y, fgeom.min_rect.clickable)) return META_FRAME_CONTROL_MINIMIZE; if (META_POINT_IN_RECT (x, y, fgeom.menu_rect.clickable)) return META_FRAME_CONTROL_MENU; flags = meta_frame_get_flags (frame->meta_window->frame); type = meta_window_get_frame_type (frame->meta_window); has_north_resize = (type != META_FRAME_TYPE_ATTACHED); has_vert = (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) != 0; has_horiz = (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) != 0; if (flags & META_FRAME_TILED_ANY) has_vert = has_horiz = FALSE; if (META_POINT_IN_RECT (x, y, fgeom.title_rect)) { if (has_vert && y <= TOP_RESIZE_HEIGHT && !(flags & META_FRAME_TILED_TOP_EDGES) && has_north_resize) return META_FRAME_CONTROL_RESIZE_N; else return META_FRAME_CONTROL_TITLE; } if (META_POINT_IN_RECT (x, y, fgeom.max_rect.clickable)) { if (flags & META_FRAME_MAXIMIZED) return META_FRAME_CONTROL_UNMAXIMIZE; else return META_FRAME_CONTROL_MAXIMIZE; } /* South resize always has priority over north resize, * in case of overlap. */ if (y >= (fgeom.height - fgeom.borders.total.bottom * CORNER_SIZE_MULT) && x >= (fgeom.width - fgeom.borders.total.right * CORNER_SIZE_MULT)) { if ((has_vert && has_horiz) || (flags & META_FRAME_TILED_ULC)) return META_FRAME_CONTROL_RESIZE_SE; else if (has_vert || (flags & META_FRAME_TILED_TOP_EDGES)) return META_FRAME_CONTROL_RESIZE_S; else if (has_horiz || (flags & META_FRAME_TILED_LEFT_EDGES)) return META_FRAME_CONTROL_RESIZE_E; } else if (y >= (fgeom.height - fgeom.borders.total.bottom * CORNER_SIZE_MULT) && x <= fgeom.borders.total.left * CORNER_SIZE_MULT) { if ((has_vert && has_horiz) || (flags & META_FRAME_TILED_URC)) return META_FRAME_CONTROL_RESIZE_SW; else if (has_vert || (flags & META_FRAME_TILED_TOP_EDGES)) return META_FRAME_CONTROL_RESIZE_S; else if (has_horiz || (flags & META_FRAME_TILED_RIGHT_EDGES)) return META_FRAME_CONTROL_RESIZE_W; } else if (y < (fgeom.borders.invisible.top * CORNER_SIZE_MULT) && x <= (fgeom.borders.total.left * CORNER_SIZE_MULT) && has_north_resize) { if ((has_vert && has_horiz) || (flags & META_FRAME_TILED_LRC)) return META_FRAME_CONTROL_RESIZE_NW; else if (has_vert || (flags & META_FRAME_TILED_BOTTOM_EDGES)) return META_FRAME_CONTROL_RESIZE_N; else if (has_horiz || (flags & META_FRAME_TILED_RIGHT_EDGES)) return META_FRAME_CONTROL_RESIZE_W; } else if (y < (fgeom.borders.invisible.top * CORNER_SIZE_MULT) && x >= (fgeom.width - fgeom.borders.total.right * CORNER_SIZE_MULT) && has_north_resize) { if ((has_vert && has_horiz) || (flags & META_FRAME_TILED_LLC)) return META_FRAME_CONTROL_RESIZE_NE; else if (has_vert || (flags & META_FRAME_TILED_BOTTOM_EDGES)) return META_FRAME_CONTROL_RESIZE_N; else if (has_horiz || (flags & META_FRAME_TILED_LEFT_EDGES)) return META_FRAME_CONTROL_RESIZE_E; } else if (y < (fgeom.borders.invisible.top + TOP_RESIZE_HEIGHT)) { if ((has_vert || (flags & META_FRAME_TILED_BOTTOM_EDGES)) && has_north_resize) return META_FRAME_CONTROL_RESIZE_N; } else if (y >= (fgeom.height - fgeom.borders.total.bottom)) { if (has_vert || (flags & META_FRAME_TILED_TOP_EDGES)) return META_FRAME_CONTROL_RESIZE_S; } else if (x <= fgeom.borders.total.left) { if (has_horiz || (flags & META_FRAME_TILED_RIGHT_EDGES)) return META_FRAME_CONTROL_RESIZE_W; } else if (x >= (fgeom.width - fgeom.borders.total.right)) { if (has_horiz || (flags & META_FRAME_TILED_LEFT_EDGES)) return META_FRAME_CONTROL_RESIZE_E; } if (y >= fgeom.borders.total.top) return META_FRAME_CONTROL_NONE; else return META_FRAME_CONTROL_TITLE; } muffin-6.4.1/src/ui/ui.c0000664000175000017500000001705314723361714013735 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter interface for talking to GTK+ UI module */ /* * Copyright (C) 2002 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include #include "meta/prefs.h" #include "meta/util.h" #include "ui/frames.h" #include "ui/theme-private.h" #include "ui/ui.h" #include "x11/meta-x11-display-private.h" #include "x11/meta-x11-window-control.h" struct _MetaUI { Display *xdisplay; MetaFrames *frames; /* For double-click tracking */ gint button_click_number; Window button_click_window; int button_click_x; int button_click_y; guint32 button_click_time; }; MetaUI * meta_ui_new (MetaX11Display *x11_display) { MetaUI *ui; if (!gtk_init_check (NULL, NULL)) meta_fatal ("Unable to initialize GTK"); g_assert (x11_display->gdk_display == gdk_display_get_default ()); ui = g_new0 (MetaUI, 1); ui->xdisplay = x11_display->xdisplay; ui->frames = meta_frames_new (x11_display); /* GTK+ needs the frame-sync protocol to work in order to properly * handle style changes. This means that the dummy widget we create * to get the style for title bars actually needs to be mapped * and fully tracked as a MetaWindow. Horrible, but mostly harmless - * the window is a 1x1 overide redirect window positioned offscreen. */ gtk_widget_show (GTK_WIDGET (ui->frames)); g_object_set_data (G_OBJECT (x11_display->gdk_display), "meta-ui", ui); return ui; } void meta_ui_free (MetaUI *ui) { GdkDisplay *gdk_display; gtk_widget_destroy (GTK_WIDGET (ui->frames)); gdk_display = gdk_x11_lookup_xdisplay (ui->xdisplay); g_object_set_data (G_OBJECT (gdk_display), "meta-ui", NULL); g_free (ui); } static void set_background_none (Display *xdisplay, Window xwindow) { XSetWindowAttributes attrs; attrs.background_pixmap = None; XChangeWindowAttributes (xdisplay, xwindow, CWBackPixmap, &attrs); } MetaUIFrame * meta_ui_create_frame (MetaUI *ui, Display *xdisplay, MetaWindow *meta_window, Visual *xvisual, gint x, gint y, gint width, gint height, gulong *create_serial) { GdkDisplay *display = gdk_x11_lookup_xdisplay (xdisplay); GdkScreen *screen; GdkWindowAttr attrs; gint attributes_mask; GdkWindow *window; GdkVisual *visual; screen = gdk_display_get_default_screen (display); /* Default depth/visual handles clients with weird visuals; they can * always be children of the root depth/visual obviously, but * e.g. DRI games can't be children of a parent that has the same * visual as the client. */ if (!xvisual) visual = gdk_screen_get_system_visual (screen); else { visual = gdk_x11_screen_lookup_visual (screen, XVisualIDFromVisual (xvisual)); } attrs.title = NULL; attrs.event_mask = GDK_EXPOSURE_MASK; attrs.x = x; attrs.y = y; attrs.wclass = GDK_INPUT_OUTPUT; attrs.visual = visual; attrs.window_type = GDK_WINDOW_CHILD; attrs.cursor = NULL; attrs.wmclass_name = NULL; attrs.wmclass_class = NULL; attrs.override_redirect = FALSE; attrs.width = width; attrs.height = height; attributes_mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL; /* We make an assumption that gdk_window_new() is going to call * XCreateWindow as it's first operation; this seems to be true currently * as long as you pass in a colormap. */ if (create_serial) *create_serial = XNextRequest (xdisplay); window = gdk_window_new (gdk_screen_get_root_window(screen), &attrs, attributes_mask); gdk_window_resize (window, width, height); set_background_none (xdisplay, GDK_WINDOW_XID (window)); return meta_frames_manage_window (ui->frames, meta_window, GDK_WINDOW_XID (window), window); } void meta_ui_map_frame (MetaUI *ui, Window xwindow) { GdkWindow *window; GdkDisplay *display; display = gdk_x11_lookup_xdisplay (ui->xdisplay); window = gdk_x11_window_lookup_for_display (display, xwindow); if (window) gdk_window_show_unraised (window); } void meta_ui_unmap_frame (MetaUI *ui, Window xwindow) { GdkWindow *window; GdkDisplay *display; display = gdk_x11_lookup_xdisplay (ui->xdisplay); window = gdk_x11_window_lookup_for_display (display, xwindow); if (window) gdk_window_hide (window); } gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay, Window xwindow) { GdkWindow *window; GdkDisplay *display; display = gdk_x11_lookup_xdisplay (xdisplay); window = gdk_x11_window_lookup_for_display (display, xwindow); /* we shouldn't cause focus if we're an override redirect * toplevel which is not foreign */ if (window && gdk_window_get_window_type (window) == GDK_WINDOW_TEMP) return TRUE; else return FALSE; } void meta_ui_theme_get_frame_borders (MetaUI *ui, MetaFrameType type, MetaFrameFlags flags, MetaFrameBorders *borders) { GdkDisplay *display; GdkScreen *screen; int text_height; MetaStyleInfo *style_info = NULL; PangoContext *context; const PangoFontDescription *font_desc; PangoFontDescription *free_font_desc = NULL; display = gdk_x11_lookup_xdisplay (ui->xdisplay); screen = gdk_display_get_default_screen (display); style_info = meta_theme_create_style_info (screen, NULL); context = gtk_widget_get_pango_context (GTK_WIDGET (ui->frames)); font_desc = meta_prefs_get_titlebar_font (); if (!font_desc) { free_font_desc = meta_style_info_create_font_desc (style_info); font_desc = (const PangoFontDescription *) free_font_desc; } text_height = meta_pango_font_desc_get_text_height (font_desc, context); meta_theme_get_frame_borders (meta_theme_get_default (), style_info, type, text_height, flags, borders); if (free_font_desc) pango_font_description_free (free_font_desc); if (style_info != NULL) meta_style_info_unref (style_info); } gboolean meta_ui_window_is_widget (MetaUI *ui, Window xwindow) { GdkDisplay *display; GdkWindow *window; display = gdk_x11_lookup_xdisplay (ui->xdisplay); window = gdk_x11_window_lookup_for_display (display, xwindow); if (window) { void *user_data = NULL; gdk_window_get_user_data (window, &user_data); return user_data != NULL && user_data != ui->frames; } else return FALSE; } gboolean meta_ui_window_is_dummy (MetaUI *ui, Window xwindow) { GdkWindow *frames_window = gtk_widget_get_window (GTK_WIDGET (ui->frames)); return xwindow == gdk_x11_window_get_xid (frames_window); } muffin-6.4.1/src/ui/frames.h0000664000175000017500000001050414723361714014574 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Metacity window frame manager widget */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_FRAMES_H #define META_FRAMES_H #include #include #include "core/main-private.h" #include "meta/common.h" #include "meta/types.h" #include "ui/theme-private.h" #include "ui/ui.h" typedef enum { META_FRAME_CONTROL_NONE, META_FRAME_CONTROL_TITLE, META_FRAME_CONTROL_DELETE, META_FRAME_CONTROL_MENU, META_FRAME_CONTROL_MINIMIZE, META_FRAME_CONTROL_MAXIMIZE, META_FRAME_CONTROL_UNMAXIMIZE, META_FRAME_CONTROL_RESIZE_SE, META_FRAME_CONTROL_RESIZE_S, META_FRAME_CONTROL_RESIZE_SW, META_FRAME_CONTROL_RESIZE_N, META_FRAME_CONTROL_RESIZE_NE, META_FRAME_CONTROL_RESIZE_NW, META_FRAME_CONTROL_RESIZE_W, META_FRAME_CONTROL_RESIZE_E, META_FRAME_CONTROL_CLIENT_AREA } MetaFrameControl; /* This is one widget that manages all the window frames * as subwindows. */ #define META_TYPE_FRAMES (meta_frames_get_type ()) #define META_FRAMES(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_FRAMES, MetaFrames)) #define META_FRAMES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_FRAMES, MetaFramesClass)) #define META_IS_FRAMES(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_TYPE_FRAMES)) #define META_IS_FRAMES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_FRAMES)) #define META_FRAMES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_FRAMES, MetaFramesClass)) typedef struct _MetaFrames MetaFrames; typedef struct _MetaFramesClass MetaFramesClass; struct _MetaUIFrame { MetaFrames *frames; MetaWindow *meta_window; Window xwindow; GdkWindow *window; MetaStyleInfo *style_info; MetaFrameLayout *cache_layout; PangoLayout *text_layout; int text_height; char *title; /* NULL once we have a layout */ guint maybe_ignore_leave_notify : 1; /* FIXME get rid of this, it can just be in the MetaFrames struct */ MetaFrameControl prelit_control; MetaButtonState button_state; int grab_button; gboolean is_frozen; }; struct _MetaFrames { GtkWindow parent_instance; MetaX11Display *x11_display; GHashTable *text_heights; GHashTable *frames; MetaStyleInfo *normal_style; GHashTable *style_variants; MetaGrabOp current_grab_op; MetaUIFrame *grab_frame; guint grab_button; gdouble grab_x; gdouble grab_y; ClutterEventSequence *grab_touch; }; struct _MetaFramesClass { GtkWindowClass parent_class; }; GType meta_frames_get_type (void) G_GNUC_CONST; MetaFrames * meta_frames_new (MetaX11Display *x11_display); MetaUIFrame * meta_frames_manage_window (MetaFrames *frames, MetaWindow *meta_window, Window xwindow, GdkWindow *window); void meta_ui_frame_unmanage (MetaUIFrame *frame); void meta_ui_frame_set_title (MetaUIFrame *frame, const char *title); void meta_ui_frame_update_style (MetaUIFrame *frame); void meta_ui_frame_get_borders (MetaUIFrame *frame, MetaFrameBorders *borders); cairo_region_t * meta_ui_frame_get_bounds (MetaUIFrame *frame); void meta_ui_frame_get_mask (MetaUIFrame *frame, cairo_rectangle_int_t *frame_rect, cairo_t *cr); void meta_ui_frame_move_resize (MetaUIFrame *frame, int x, int y, int width, int height); void meta_ui_frame_queue_draw (MetaUIFrame *frame); gboolean meta_ui_frame_handle_event (MetaUIFrame *frame, const ClutterEvent *event); #endif muffin-6.4.1/src/ui/theme.c0000664000175000017500000013402214723361714014416 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "ui/theme-private.h" #include #include #include #include #include #include "core/util-private.h" #include "meta/prefs.h" #include "ui/frames.h" static void scale_border (GtkBorder *border, double factor); static MetaFrameLayout * meta_frame_layout_new (void) { MetaFrameLayout *layout; layout = g_new0 (MetaFrameLayout, 1); /* Spacing as hardcoded in GTK+: * https://git.gnome.org/browse/gtk+/tree/gtk/gtkheaderbar.c?h=gtk-3-14#n53 */ layout->titlebar_spacing = 6; layout->has_title = TRUE; layout->title_scale = PANGO_SCALE_MEDIUM; layout->icon_size = META_MINI_ICON_WIDTH; return layout; } static void meta_frame_layout_free (MetaFrameLayout *layout) { g_return_if_fail (layout != NULL); g_free (layout); } static void meta_frame_layout_get_borders (const MetaFrameLayout *layout, int text_height, MetaFrameFlags flags, MetaFrameType type, MetaFrameBorders *borders) { int buttons_height, content_height, draggable_borders; int scale = meta_theme_get_window_scaling_factor (); meta_frame_borders_clear (borders); /* For a full-screen window, we don't have any borders, visible or not. */ if (flags & META_FRAME_FULLSCREEN) return; g_return_if_fail (layout != NULL); if (!layout->has_title) text_height = 0; else text_height = layout->title_margin.top + text_height + layout->title_margin.bottom; buttons_height = MAX ((int)layout->icon_size, layout->button_min_size.height) + layout->button_margin.top + layout->button_border.top + layout->button_margin.bottom + layout->button_border.bottom; content_height = MAX (buttons_height, text_height); content_height = MAX (content_height, layout->titlebar_min_size.height) + layout->titlebar_border.top + layout->titlebar_border.bottom; borders->visible.top = layout->frame_border.top + content_height; borders->visible.left = layout->frame_border.left; borders->visible.right = layout->frame_border.right; borders->visible.bottom = layout->frame_border.bottom; borders->invisible = layout->invisible_border; draggable_borders = meta_prefs_get_draggable_border_width (); if (flags & META_FRAME_ALLOWS_HORIZONTAL_RESIZE) { if (!(flags & META_FRAME_TILED_LEFT_EDGES)) borders->invisible.left = MAX (layout->invisible_border.left, draggable_borders - borders->visible.left); else borders->invisible.left = 0; if (!(flags & META_FRAME_TILED_RIGHT_EDGES)) borders->invisible.right = MAX (layout->invisible_border.right, draggable_borders - borders->visible.right); else borders->invisible.right = 0; } if (flags & META_FRAME_ALLOWS_VERTICAL_RESIZE) { if (!(flags & META_FRAME_TILED_BOTTOM_EDGES)) borders->invisible.bottom = MAX (layout->invisible_border.bottom, draggable_borders - borders->visible.bottom); else borders->invisible.bottom = 0; /* borders.visible.top is the height of the *title bar*. We can't do the same * algorithm here, titlebars are expectedly much bigger. Just subtract a couple * pixels to get a proper feel. */ if (type != META_FRAME_TYPE_ATTACHED && !(flags & META_FRAME_TILED_TOP_EDGES)) borders->invisible.top = MAX (layout->invisible_border.top, draggable_borders - 2); else borders->invisible.top = 0; } borders->total.left = borders->invisible.left + borders->visible.left; borders->total.right = borders->invisible.right + borders->visible.right; borders->total.bottom = borders->invisible.bottom + borders->visible.bottom; borders->total.top = borders->invisible.top + borders->visible.top; /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */ scale_border (&borders->visible, scale); scale_border (&borders->invisible, scale); scale_border (&borders->total, scale); } int meta_theme_get_window_scaling_factor (void) { GdkScreen *screen; GValue value = G_VALUE_INIT; g_value_init (&value, G_TYPE_INT); screen = gdk_screen_get_default (); if (gdk_screen_get_setting (screen, "gdk-window-scaling-factor", &value)) return g_value_get_int (&value); else return 1; } void meta_frame_layout_apply_scale (const MetaFrameLayout *layout, PangoFontDescription *font_desc) { int size = pango_font_description_get_size (font_desc); double scale = layout->title_scale / meta_theme_get_window_scaling_factor (); pango_font_description_set_size (font_desc, MAX (size * scale, 1)); } static MetaButtonSpace* rect_for_function (MetaFrameGeometry *fgeom, MetaFrameFlags flags, MetaButtonFunction function, MetaTheme *theme) { switch (function) { case META_BUTTON_FUNCTION_MENU: if (flags & META_FRAME_ALLOWS_MENU) return &fgeom->menu_rect; else return NULL; case META_BUTTON_FUNCTION_MINIMIZE: if (flags & META_FRAME_ALLOWS_MINIMIZE) return &fgeom->min_rect; else return NULL; case META_BUTTON_FUNCTION_MAXIMIZE: if (flags & META_FRAME_ALLOWS_MAXIMIZE) return &fgeom->max_rect; else return NULL; case META_BUTTON_FUNCTION_CLOSE: if (flags & META_FRAME_ALLOWS_DELETE) return &fgeom->close_rect; else return NULL; case META_BUTTON_FUNCTION_LAST: return NULL; } return NULL; } static gboolean strip_button (MetaButtonSpace *func_rects[MAX_BUTTONS_PER_CORNER], int *n_rects, MetaButtonSpace *to_strip) { int i; i = 0; while (i < *n_rects) { if (func_rects[i] == to_strip) { *n_rects -= 1; /* shift the other rects back in the array */ while (i < *n_rects) { func_rects[i] = func_rects[i+1]; ++i; } func_rects[i] = NULL; return TRUE; } ++i; } return FALSE; /* did not strip anything */ } static void get_padding_and_border (GtkStyleContext *style, GtkBorder *border) { GtkBorder tmp; GtkStateFlags state = gtk_style_context_get_state (style); gtk_style_context_get_border (style, state, border); gtk_style_context_get_padding (style, state, &tmp); border->left += tmp.left; border->top += tmp.top; border->right += tmp.right; border->bottom += tmp.bottom; } static void get_min_size (GtkStyleContext *style, GtkRequisition *requisition) { gtk_style_context_get (style, gtk_style_context_get_state (style), "min-width", &requisition->width, "min-height", &requisition->height, NULL); } static void scale_border (GtkBorder *border, double factor) { border->left *= factor; border->right *= factor; border->top *= factor; border->bottom *= factor; } static void meta_frame_layout_sync_with_style (MetaFrameLayout *layout, MetaStyleInfo *style_info, MetaFrameFlags flags) { GtkStyleContext *style; GtkBorder border; GtkRequisition requisition; GdkRectangle clip_rect; int border_radius, max_radius; meta_style_info_set_flags (style_info, flags); style = style_info->styles[META_STYLE_ELEMENT_FRAME]; get_padding_and_border (style, &layout->frame_border); scale_border (&layout->frame_border, layout->title_scale); gtk_render_background_get_clip (style, 0, 0, 0, 0, &clip_rect); layout->invisible_border.left = -clip_rect.x; layout->invisible_border.right = clip_rect.width + clip_rect.x; layout->invisible_border.top = -clip_rect.y; layout->invisible_border.bottom = clip_rect.height + clip_rect.y; if (layout->hide_buttons) layout->icon_size = 0; if (!layout->has_title && layout->hide_buttons) return; /* border-only - be done */ style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR]; gtk_style_context_get (style, gtk_style_context_get_state (style), "border-radius", &border_radius, NULL); /* GTK+ currently does not allow us to look up radii of individual * corners; however we don't clip the client area, so with the * current trend of using small/no visible frame borders, most * themes should work fine with this. */ layout->top_left_corner_rounded_radius = border_radius; layout->top_right_corner_rounded_radius = border_radius; max_radius = MIN (layout->frame_border.bottom, layout->frame_border.left); layout->bottom_left_corner_rounded_radius = MAX (border_radius, max_radius); max_radius = MIN (layout->frame_border.bottom, layout->frame_border.right); layout->bottom_right_corner_rounded_radius = MAX (border_radius, max_radius); get_min_size (style, &layout->titlebar_min_size); get_padding_and_border (style, &layout->titlebar_border); scale_border (&layout->titlebar_border, layout->title_scale); style = style_info->styles[META_STYLE_ELEMENT_TITLE]; gtk_style_context_get_margin (style, gtk_style_context_get_state (style), &layout->title_margin); scale_border (&layout->title_margin, layout->title_scale); style = style_info->styles[META_STYLE_ELEMENT_BUTTON]; get_min_size (style, &layout->button_min_size); get_padding_and_border (style, &layout->button_border); scale_border (&layout->button_border, layout->title_scale); gtk_style_context_get_margin (style, gtk_style_context_get_state (style), &layout->button_margin); scale_border (&layout->button_margin, layout->title_scale); style = style_info->styles[META_STYLE_ELEMENT_IMAGE]; get_min_size (style, &requisition); get_padding_and_border (style, &border); scale_border (&border, layout->title_scale); layout->button_border.left += border.left; layout->button_border.right += border.right; layout->button_border.top += border.top; layout->button_border.bottom += border.bottom; gtk_style_context_get_margin (style, gtk_style_context_get_state (style), &border); layout->button_border.left += border.left; layout->button_border.right += border.right; layout->button_border.top += border.top; layout->button_border.bottom += border.bottom; layout->button_min_size.width = MAX(layout->button_min_size.width, requisition.width); layout->button_min_size.height = MAX(layout->button_min_size.height, requisition.height); } static void meta_frame_layout_calc_geometry (MetaFrameLayout *layout, MetaStyleInfo *style_info, int text_height, MetaFrameFlags flags, int client_width, int client_height, const MetaButtonLayout *button_layout, MetaFrameType type, MetaFrameGeometry *fgeom, MetaTheme *theme) { int i, n_left, n_right, n_left_spacers, n_right_spacers; int x; int button_y; int title_right_edge; int width, height; int content_width, content_height; int button_width, button_height; int min_size_for_rounding; int scale = meta_theme_get_window_scaling_factor (); /* the left/right rects in order; the max # of rects * is the number of button functions */ MetaButtonSpace *left_func_rects[MAX_BUTTONS_PER_CORNER]; MetaButtonSpace *right_func_rects[MAX_BUTTONS_PER_CORNER]; gboolean left_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; gboolean right_buttons_has_spacer[MAX_BUTTONS_PER_CORNER]; MetaFrameBorders borders; meta_frame_layout_sync_with_style (layout, style_info, flags); meta_frame_layout_get_borders (layout, text_height, flags, type, &borders); fgeom->borders = borders; /* Scale geometry for HiDPI, see comment in meta_frame_layout_draw_with_style() */ fgeom->content_border = layout->frame_border; fgeom->content_border.left += layout->titlebar_border.left * scale; fgeom->content_border.right += layout->titlebar_border.right * scale; fgeom->content_border.top += layout->titlebar_border.top * scale; fgeom->content_border.bottom += layout->titlebar_border.bottom * scale; width = client_width + borders.total.left + borders.total.right; height = borders.total.top + borders.total.bottom; if (!(flags & META_FRAME_SHADED)) height += client_height; fgeom->width = width; fgeom->height = height; content_width = width - (fgeom->content_border.left + borders.invisible.left) - (fgeom->content_border.right + borders.invisible.right); content_height = borders.visible.top - fgeom->content_border.top - fgeom->content_border.bottom; button_width = MAX ((int)layout->icon_size, layout->button_min_size.width) + layout->button_border.left + layout->button_border.right; button_height = MAX ((int)layout->icon_size, layout->button_min_size.height) + layout->button_border.top + layout->button_border.bottom; button_width *= scale; button_height *= scale; /* FIXME all this code sort of pretends that duplicate buttons * with the same function are allowed, but that breaks the * code in frames.c, so isn't really allowed right now. * Would need left_close_rect, right_close_rect, etc. */ /* Init all button rects to 0, lame hack */ memset (ADDRESS_OF_BUTTON_RECTS (fgeom), '\0', LENGTH_OF_BUTTON_RECTS); n_left = 0; n_right = 0; n_left_spacers = 0; n_right_spacers = 0; if (!layout->hide_buttons) { /* Try to fill in rects */ for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->left_buttons[i] != META_BUTTON_FUNCTION_LAST; i++) { left_func_rects[n_left] = rect_for_function (fgeom, flags, button_layout->left_buttons[i], theme); if (left_func_rects[n_left] != NULL) { left_buttons_has_spacer[n_left] = button_layout->left_buttons_has_spacer[i]; if (button_layout->left_buttons_has_spacer[i]) ++n_left_spacers; ++n_left; } } for (i = 0; i < MAX_BUTTONS_PER_CORNER && button_layout->right_buttons[i] != META_BUTTON_FUNCTION_LAST; i++) { right_func_rects[n_right] = rect_for_function (fgeom, flags, button_layout->right_buttons[i], theme); if (right_func_rects[n_right] != NULL) { right_buttons_has_spacer[n_right] = button_layout->right_buttons_has_spacer[i]; if (button_layout->right_buttons_has_spacer[i]) ++n_right_spacers; ++n_right; } } } /* Be sure buttons fit */ while (n_left > 0 || n_right > 0) { int space_used_by_buttons; space_used_by_buttons = 0; space_used_by_buttons += layout->button_margin.left * scale * n_left; space_used_by_buttons += button_width * n_left; space_used_by_buttons += layout->button_margin.right * scale * n_left; space_used_by_buttons += (button_width * 0.75) * n_left_spacers; space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_left - 1, 0); space_used_by_buttons += layout->button_margin.left * scale * n_right; space_used_by_buttons += button_width * n_right; space_used_by_buttons += layout->button_margin.right * scale * n_right; space_used_by_buttons += (button_width * 0.75) * n_right_spacers; space_used_by_buttons += layout->titlebar_spacing * scale * MAX (n_right - 1, 0); if (space_used_by_buttons <= content_width) break; /* Everything fits, bail out */ /* First try to remove separators */ if (n_left_spacers > 0) { left_buttons_has_spacer[--n_left_spacers] = FALSE; continue; } else if (n_right_spacers > 0) { right_buttons_has_spacer[--n_right_spacers] = FALSE; continue; } /* Otherwise we need to shave out a button. Shave * min, max, close, then menu (menu is most useful); * prefer the default button locations. */ if (strip_button (left_func_rects, &n_left, &fgeom->min_rect)) continue; else if (strip_button (right_func_rects, &n_right, &fgeom->min_rect)) continue; else if (strip_button (left_func_rects, &n_left, &fgeom->max_rect)) continue; else if (strip_button (right_func_rects, &n_right, &fgeom->max_rect)) continue; else if (strip_button (left_func_rects, &n_left, &fgeom->close_rect)) continue; else if (strip_button (right_func_rects, &n_right, &fgeom->close_rect)) continue; else if (strip_button (right_func_rects, &n_right, &fgeom->menu_rect)) continue; else if (strip_button (left_func_rects, &n_left, &fgeom->menu_rect)) continue; else { meta_bug ("Could not find a button to strip. n_left = %d n_right = %d\n", n_left, n_right); } } /* Save the button layout */ fgeom->button_layout = *button_layout; fgeom->n_left_buttons = n_left; fgeom->n_right_buttons = n_right; /* center buttons vertically */ button_y = fgeom->content_border.top + borders.invisible.top + (content_height - button_height) / 2; /* right edge of farthest-right button */ x = width - fgeom->content_border.right - borders.invisible.right; i = n_right - 1; while (i >= 0) { MetaButtonSpace *rect; if (x < 0) /* if we go negative, leave the buttons we don't get to as 0-width */ break; x -= layout->button_margin.right * scale; rect = right_func_rects[i]; rect->visible.x = x - button_width; if (right_buttons_has_spacer[i]) rect->visible.x -= (button_width * 0.75); rect->visible.y = button_y; rect->visible.width = button_width; rect->visible.height = button_height; if (flags & META_FRAME_MAXIMIZED || flags & META_FRAME_TILED_ANY) { rect->clickable.x = rect->visible.x; rect->clickable.y = 0; rect->clickable.width = rect->visible.width; rect->clickable.height = button_height + button_y; if (i == n_right - 1) rect->clickable.width += fgeom->content_border.right; } else memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable)); x = rect->visible.x - layout->button_margin.left * scale; if (i > 0) x -= layout->titlebar_spacing * scale; --i; } /* save right edge of titlebar for later use */ title_right_edge = x; /* Now x changes to be position from the left and we go through * the left-side buttons */ x = fgeom->content_border.left + borders.invisible.left; for (i = 0; i < n_left; i++) { MetaButtonSpace *rect; x += layout->button_margin.left * scale; rect = left_func_rects[i]; rect->visible.x = x; rect->visible.y = button_y; rect->visible.width = button_width; rect->visible.height = button_height; if (flags & META_FRAME_MAXIMIZED) { if (i==0) { rect->clickable.x = 0; rect->clickable.width = button_width + x; } else { rect->clickable.x = rect->visible.x; rect->clickable.width = button_width; } rect->clickable.y = 0; rect->clickable.height = button_height + button_y; } else memmove (&(rect->clickable), &(rect->visible), sizeof (rect->clickable)); x = rect->visible.x + rect->visible.width + layout->button_margin.right * scale; if (i < n_left - 1) x += layout->titlebar_spacing * scale; if (left_buttons_has_spacer[i]) x += (button_width * 0.75); } /* Center vertically in the available content area */ fgeom->title_rect.x = x; fgeom->title_rect.y = fgeom->content_border.top + borders.invisible.top + (content_height - text_height) / 2; fgeom->title_rect.width = title_right_edge - fgeom->title_rect.x; fgeom->title_rect.height = text_height; /* Nuke title if it won't fit */ if (fgeom->title_rect.width < 0 || fgeom->title_rect.height < 0) { fgeom->title_rect.width = 0; fgeom->title_rect.height = 0; } if (flags & META_FRAME_SHADED) min_size_for_rounding = 0; else min_size_for_rounding = 5 * scale; fgeom->top_left_corner_rounded_radius = 0; fgeom->top_right_corner_rounded_radius = 0; fgeom->bottom_left_corner_rounded_radius = 0; fgeom->bottom_right_corner_rounded_radius = 0; if (borders.visible.top + borders.visible.left >= min_size_for_rounding) fgeom->top_left_corner_rounded_radius = layout->top_left_corner_rounded_radius * scale; if (borders.visible.top + borders.visible.right >= min_size_for_rounding) fgeom->top_right_corner_rounded_radius = layout->top_right_corner_rounded_radius * scale; if (borders.visible.bottom + borders.visible.left >= min_size_for_rounding) fgeom->bottom_left_corner_rounded_radius = layout->bottom_left_corner_rounded_radius * scale; if (borders.visible.bottom + borders.visible.right >= min_size_for_rounding) fgeom->bottom_right_corner_rounded_radius = layout->bottom_right_corner_rounded_radius * scale; } static void get_button_rect (MetaButtonType type, const MetaFrameGeometry *fgeom, GdkRectangle *rect) { switch (type) { case META_BUTTON_TYPE_CLOSE: *rect = fgeom->close_rect.visible; break; case META_BUTTON_TYPE_MAXIMIZE: *rect = fgeom->max_rect.visible; break; case META_BUTTON_TYPE_MINIMIZE: *rect = fgeom->min_rect.visible; break; case META_BUTTON_TYPE_MENU: *rect = fgeom->menu_rect.visible; break; default: case META_BUTTON_TYPE_LAST: g_assert_not_reached (); break; } } static const char * get_class_from_button_type (MetaButtonType type) { switch (type) { case META_BUTTON_TYPE_CLOSE: return "close"; case META_BUTTON_TYPE_MAXIMIZE: return "maximize"; case META_BUTTON_TYPE_MINIMIZE: return "minimize"; default: return NULL; } } static void meta_frame_layout_draw_with_style (MetaFrameLayout *layout, MetaStyleInfo *style_info, cairo_t *cr, const MetaFrameGeometry *fgeom, PangoLayout *title_layout, MetaFrameFlags flags, MetaButtonState button_states[META_BUTTON_TYPE_LAST], cairo_surface_t *mini_icon) { GtkStyleContext *style; GtkStateFlags state; MetaButtonType button_type; GdkRectangle visible_rect; GdkRectangle titlebar_rect; GdkRectangle button_rect; const MetaFrameBorders *borders; cairo_surface_t *frame_surface; double xscale, yscale; int scale; /* We opt out of GTK+/Clutter's HiDPI handling, so we have to do the scaling * ourselves; the nitty-gritty is a bit confusing, so here is an overview: * - the values in MetaFrameLayout are always as they appear in the theme, * i.e. unscaled * - calculated values (borders, MetaFrameGeometry) include the scale - as * the geometry is comprised of scaled decorations and the client size * which we must not scale, we don't have another option * - for drawing, we scale the canvas to have GTK+ render elements (borders, * radii, ...) at the correct scale - as a result, we have to "unscale" * the geometry again to not apply the scaling twice * - As per commit e36b629c GTK expects the device scale to be set and match * the final scaling or the surface caching won't take this in account * breaking -gtk-scaled items. */ scale = meta_theme_get_window_scaling_factor (); frame_surface = cairo_get_target (cr); cairo_surface_get_device_scale (frame_surface, &xscale, &yscale); cairo_surface_set_device_scale (frame_surface, scale, scale); borders = &fgeom->borders; visible_rect.x = borders->invisible.left / scale; visible_rect.y = borders->invisible.top / scale; visible_rect.width = (fgeom->width - borders->invisible.left - borders->invisible.right) / scale; visible_rect.height = (fgeom->height - borders->invisible.top - borders->invisible.bottom) / scale; meta_style_info_set_flags (style_info, flags); style = style_info->styles[META_STYLE_ELEMENT_FRAME]; gtk_render_background (style, cr, visible_rect.x, visible_rect.y, visible_rect.width, visible_rect.height); gtk_render_frame (style, cr, visible_rect.x, visible_rect.y, visible_rect.width, visible_rect.height); titlebar_rect.x = visible_rect.x; titlebar_rect.y = visible_rect.y; titlebar_rect.width = visible_rect.width; titlebar_rect.height = borders->visible.top / scale; style = style_info->styles[META_STYLE_ELEMENT_TITLEBAR]; gtk_render_background (style, cr, titlebar_rect.x, titlebar_rect.y, titlebar_rect.width, titlebar_rect.height); gtk_render_frame (style, cr, titlebar_rect.x, titlebar_rect.y, titlebar_rect.width, titlebar_rect.height); if (layout->has_title && title_layout) { PangoRectangle logical; int text_width, x, y; pango_layout_set_width (title_layout, -1); pango_layout_get_pixel_extents (title_layout, NULL, &logical); text_width = MIN(fgeom->title_rect.width / scale, logical.width); if (text_width < logical.width) pango_layout_set_width (title_layout, PANGO_SCALE * text_width); /* Center within the frame if possible */ x = titlebar_rect.x + (titlebar_rect.width - text_width) / 2; y = titlebar_rect.y + (titlebar_rect.height - logical.height) / 2; if (x < fgeom->title_rect.x / scale) x = fgeom->title_rect.x / scale; else if (x + text_width > (fgeom->title_rect.x + fgeom->title_rect.width) / scale) x = (fgeom->title_rect.x + fgeom->title_rect.width) / scale - text_width; style = style_info->styles[META_STYLE_ELEMENT_TITLE]; gtk_render_layout (style, cr, x, y, title_layout); } style = style_info->styles[META_STYLE_ELEMENT_BUTTON]; state = gtk_style_context_get_state (style); for (button_type = META_BUTTON_TYPE_CLOSE; button_type < META_BUTTON_TYPE_LAST; button_type++) { const char *button_class = get_class_from_button_type (button_type); if (button_class) gtk_style_context_add_class (style, button_class); get_button_rect (button_type, fgeom, &button_rect); button_rect.x /= scale; button_rect.y /= scale; button_rect.width /= scale; button_rect.height /= scale; if (button_states[button_type] == META_BUTTON_STATE_PRELIGHT) gtk_style_context_set_state (style, state | GTK_STATE_PRELIGHT); else if (button_states[button_type] == META_BUTTON_STATE_PRESSED) gtk_style_context_set_state (style, state | GTK_STATE_ACTIVE); else gtk_style_context_set_state (style, state); cairo_save (cr); if (button_rect.width > 0 && button_rect.height > 0) { cairo_surface_t *surface = NULL; const char *icon_name = NULL; gtk_render_background (style, cr, button_rect.x, button_rect.y, button_rect.width, button_rect.height); gtk_render_frame (style, cr, button_rect.x, button_rect.y, button_rect.width, button_rect.height); switch (button_type) { case META_BUTTON_TYPE_CLOSE: icon_name = "window-close-symbolic"; break; case META_BUTTON_TYPE_MAXIMIZE: if (flags & META_FRAME_MAXIMIZED) icon_name = "window-restore-symbolic"; else icon_name = "window-maximize-symbolic"; break; case META_BUTTON_TYPE_MINIMIZE: icon_name = "window-minimize-symbolic"; break; case META_BUTTON_TYPE_MENU: icon_name = "open-menu-symbolic"; break; default: icon_name = NULL; break; } if (icon_name) { g_autoptr (GIcon) icon = NULL; g_autoptr (GtkIconInfo) info = NULL; g_autoptr (GdkPixbuf) pixbuf = NULL; GtkIconTheme *theme; int flags; theme = gtk_icon_theme_get_default (); /* This can't be exactly like Gtk does as some -gtk-* css * properties that are used for setting the loading flags * are not accessible from here */ flags = GTK_ICON_LOOKUP_USE_BUILTIN; flags |= (meta_get_locale_direction () == META_LOCALE_DIRECTION_LTR) ? GTK_ICON_LOOKUP_DIR_LTR : GTK_ICON_LOOKUP_DIR_RTL; icon = g_themed_icon_new_with_default_fallbacks (icon_name); info = gtk_icon_theme_lookup_by_gicon_for_scale (theme, icon, layout->icon_size, scale, flags); if (gtk_icon_info_is_symbolic (info)) pixbuf = gtk_icon_info_load_symbolic_for_context (info, style, NULL, NULL); else pixbuf = gtk_icon_info_load_icon (info, NULL); if (pixbuf) surface = gdk_cairo_surface_create_from_pixbuf (pixbuf, scale, NULL); } if (surface) { double x, y; x = button_rect.x + (button_rect.width - layout->icon_size) / 2.0; y = button_rect.y + (button_rect.height - layout->icon_size) / 2.0; gtk_render_icon_surface (style, cr, surface, x, y); cairo_surface_destroy (surface); } } cairo_restore (cr); if (button_class) gtk_style_context_remove_class (style, button_class); gtk_style_context_set_state (style, state); } cairo_surface_set_device_scale (frame_surface, xscale, yscale); } /** * meta_theme_get_default: (skip) * */ MetaTheme* meta_theme_get_default (void) { static MetaTheme *theme = NULL; int frame_type; if (theme) return theme; theme = meta_theme_new (); for (frame_type = 0; frame_type < META_FRAME_TYPE_LAST; frame_type++) { MetaFrameLayout *layout = meta_frame_layout_new (); switch (frame_type) { case META_FRAME_TYPE_NORMAL: case META_FRAME_TYPE_DIALOG: case META_FRAME_TYPE_MODAL_DIALOG: case META_FRAME_TYPE_ATTACHED: break; case META_FRAME_TYPE_MENU: case META_FRAME_TYPE_UTILITY: layout->title_scale = PANGO_SCALE_SMALL; break; case META_FRAME_TYPE_BORDER: layout->has_title = FALSE; layout->hide_buttons = TRUE; break; default: g_assert_not_reached (); } theme->layouts[frame_type] = layout; } return theme; } /** * meta_theme_new: (skip) * */ MetaTheme* meta_theme_new (void) { return g_new0 (MetaTheme, 1); } void meta_theme_free (MetaTheme *theme) { int i; g_return_if_fail (theme != NULL); for (i = 0; i < META_FRAME_TYPE_LAST; i++) if (theme->layouts[i]) meta_frame_layout_free (theme->layouts[i]); g_free (theme); } MetaFrameLayout* meta_theme_get_frame_layout (MetaTheme *theme, MetaFrameType type) { g_return_val_if_fail (type < META_FRAME_TYPE_LAST, NULL); return theme->layouts[type]; } static GtkStyleContext * create_style_context (GType widget_type, GtkStyleContext *parent_style, GtkCssProvider *provider, const char *object_name, const char *first_class, ...) { GtkStyleContext *style; GtkStateFlags state; GtkWidgetPath *path; const char *name; va_list ap; style = gtk_style_context_new (); gtk_style_context_set_scale (style, meta_theme_get_window_scaling_factor ()); gtk_style_context_set_parent (style, parent_style); if (parent_style) path = gtk_widget_path_copy (gtk_style_context_get_path (parent_style)); else path = gtk_widget_path_new (); gtk_widget_path_append_type (path, widget_type); if (object_name) gtk_widget_path_iter_set_object_name (path, -1, object_name); state = gtk_style_context_get_state (style); if (meta_get_locale_direction() == META_LOCALE_DIRECTION_RTL) { state |= GTK_STATE_FLAG_DIR_RTL; state &= ~GTK_STATE_FLAG_DIR_LTR; } else { state |= GTK_STATE_FLAG_DIR_LTR; state &= ~GTK_STATE_FLAG_DIR_RTL; } gtk_style_context_set_state (style, state); va_start (ap, first_class); for (name = first_class; name; name = va_arg (ap, const char *)) gtk_widget_path_iter_add_class (path, -1, name); va_end (ap); gtk_style_context_set_path (style, path); gtk_widget_path_unref (path); gtk_style_context_add_provider (style, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_SETTINGS); return style; } static inline GtkCssProvider * get_css_provider_for_theme_name (const gchar *theme_name, const gchar *variant) { static GtkCssProvider *default_provider = NULL; if (!theme_name || *theme_name == '\0') { if (G_UNLIKELY (default_provider == NULL)) default_provider = gtk_css_provider_new (); return default_provider; } return gtk_css_provider_get_named (theme_name, variant); } MetaStyleInfo * meta_theme_create_style_info (GdkScreen *screen, const gchar *variant) { MetaStyleInfo *style_info; GtkCssProvider *provider; char *theme_name; g_object_get (gtk_settings_get_for_screen (screen), "gtk-theme-name", &theme_name, NULL); provider = get_css_provider_for_theme_name (theme_name, variant); g_free (theme_name); style_info = g_new0 (MetaStyleInfo, 1); style_info->refcount = 1; style_info->styles[META_STYLE_ELEMENT_WINDOW] = create_style_context (META_TYPE_FRAMES, NULL, provider, "window", GTK_STYLE_CLASS_BACKGROUND, "ssd", NULL); style_info->styles[META_STYLE_ELEMENT_FRAME] = create_style_context (META_TYPE_FRAMES, style_info->styles[META_STYLE_ELEMENT_WINDOW], provider, "decoration", NULL); style_info->styles[META_STYLE_ELEMENT_TITLEBAR] = create_style_context (GTK_TYPE_HEADER_BAR, style_info->styles[META_STYLE_ELEMENT_FRAME], provider, "headerbar", GTK_STYLE_CLASS_TITLEBAR, GTK_STYLE_CLASS_HORIZONTAL, "default-decoration", NULL); style_info->styles[META_STYLE_ELEMENT_TITLE] = create_style_context (GTK_TYPE_LABEL, style_info->styles[META_STYLE_ELEMENT_TITLEBAR], provider, "label", GTK_STYLE_CLASS_TITLE, NULL); style_info->styles[META_STYLE_ELEMENT_BUTTON] = create_style_context (GTK_TYPE_BUTTON, style_info->styles[META_STYLE_ELEMENT_TITLEBAR], provider, "button", "titlebutton", NULL); style_info->styles[META_STYLE_ELEMENT_IMAGE] = create_style_context (GTK_TYPE_IMAGE, style_info->styles[META_STYLE_ELEMENT_BUTTON], provider, "image", NULL); return style_info; } MetaStyleInfo * meta_style_info_ref (MetaStyleInfo *style_info) { g_return_val_if_fail (style_info != NULL, NULL); g_return_val_if_fail (style_info->refcount > 0, NULL); g_atomic_int_inc ((volatile int *)&style_info->refcount); return style_info; } void meta_style_info_unref (MetaStyleInfo *style_info) { g_return_if_fail (style_info != NULL); g_return_if_fail (style_info->refcount > 0); if (g_atomic_int_dec_and_test ((volatile int *)&style_info->refcount)) { int i; for (i = 0; i < META_STYLE_ELEMENT_LAST; i++) g_object_unref (style_info->styles[i]); g_free (style_info); } } static void add_toplevel_class (GtkStyleContext *style, const char *class_name) { if (gtk_style_context_get_parent (style)) { GtkWidgetPath *path; path = gtk_widget_path_copy (gtk_style_context_get_path (style)); gtk_widget_path_iter_add_class (path, 0, class_name); gtk_style_context_set_path (style, path); gtk_widget_path_unref (path); } else gtk_style_context_add_class (style, class_name); } static void remove_toplevel_class (GtkStyleContext *style, const char *class_name) { if (gtk_style_context_get_parent (style)) { GtkWidgetPath *path; path = gtk_widget_path_copy (gtk_style_context_get_path (style)); gtk_widget_path_iter_remove_class (path, 0, class_name); gtk_style_context_set_path (style, path); gtk_widget_path_unref (path); } else gtk_style_context_remove_class (style, class_name); } void meta_style_info_set_flags (MetaStyleInfo *style_info, MetaFrameFlags flags) { GtkStyleContext *style; const char *class_name = NULL; gboolean backdrop; GtkStateFlags state; int i; backdrop = !(flags & META_FRAME_HAS_FOCUS); if (flags & META_FRAME_MAXIMIZED) class_name = "maximized"; else if (flags & META_FRAME_TILED_ANY) class_name = "tiled"; for (i = 0; i < META_STYLE_ELEMENT_LAST; i++) { style = style_info->styles[i]; state = gtk_style_context_get_state (style); if (backdrop) gtk_style_context_set_state (style, state | GTK_STATE_FLAG_BACKDROP); else gtk_style_context_set_state (style, state & ~GTK_STATE_FLAG_BACKDROP); remove_toplevel_class (style, "maximized"); remove_toplevel_class (style, "tiled"); if (class_name) add_toplevel_class (style, class_name); } } PangoFontDescription* meta_style_info_create_font_desc (MetaStyleInfo *style_info) { PangoFontDescription *font_desc; const PangoFontDescription *override = meta_prefs_get_titlebar_font (); GtkStyleContext *context = style_info->styles[META_STYLE_ELEMENT_TITLE]; gtk_style_context_get (context, gtk_style_context_get_state (context), "font", &font_desc, NULL); if (override) pango_font_description_merge (font_desc, override, TRUE); return font_desc; } void meta_theme_draw_frame (MetaTheme *theme, MetaStyleInfo *style_info, cairo_t *cr, MetaFrameType type, MetaFrameFlags flags, int client_width, int client_height, PangoLayout *title_layout, int text_height, const MetaButtonLayout *button_layout, MetaButtonState button_states[META_BUTTON_TYPE_LAST], cairo_surface_t *mini_icon) { MetaFrameGeometry fgeom; MetaFrameLayout *layout; g_return_if_fail (type < META_FRAME_TYPE_LAST); layout = theme->layouts[type]; /* Parser is not supposed to allow this currently */ if (layout == NULL) return; meta_frame_layout_calc_geometry (layout, style_info, text_height, flags, client_width, client_height, button_layout, type, &fgeom, theme); meta_frame_layout_draw_with_style (layout, style_info, cr, &fgeom, title_layout, flags, button_states, mini_icon); } void meta_theme_get_frame_borders (MetaTheme *theme, MetaStyleInfo *style_info, MetaFrameType type, int text_height, MetaFrameFlags flags, MetaFrameBorders *borders) { MetaFrameLayout *layout; g_return_if_fail (type < META_FRAME_TYPE_LAST); layout = theme->layouts[type]; meta_frame_borders_clear (borders); /* Parser is not supposed to allow this currently */ if (layout == NULL) return; meta_frame_layout_sync_with_style (layout, style_info, flags); meta_frame_layout_get_borders (layout, text_height, flags, type, borders); } void meta_theme_calc_geometry (MetaTheme *theme, MetaStyleInfo *style_info, MetaFrameType type, int text_height, MetaFrameFlags flags, int client_width, int client_height, const MetaButtonLayout *button_layout, MetaFrameGeometry *fgeom) { MetaFrameLayout *layout; g_return_if_fail (type < META_FRAME_TYPE_LAST); layout = theme->layouts[type]; /* Parser is not supposed to allow this currently */ if (layout == NULL) return; meta_frame_layout_calc_geometry (layout, style_info, text_height, flags, client_width, client_height, button_layout, type, fgeom, theme); } /** * meta_pango_font_desc_get_text_height: * @font_desc: the font * @context: the context of the font * * Returns the height of the letters in a particular font. * * Returns: the height of the letters */ int meta_pango_font_desc_get_text_height (const PangoFontDescription *font_desc, PangoContext *context) { PangoFontMetrics *metrics; PangoLanguage *lang; int retval; lang = pango_context_get_language (context); metrics = pango_context_get_metrics (context, font_desc, lang); retval = PANGO_PIXELS (pango_font_metrics_get_ascent (metrics) + pango_font_metrics_get_descent (metrics)); pango_font_metrics_unref (metrics); return retval; } /** * meta_frame_type_to_string: * @type: a #MetaFrameType * * Converts a frame type enum value to the name string that would * appear in the theme definition file. * * Return value: the string value */ const char* meta_frame_type_to_string (MetaFrameType type) { switch (type) { case META_FRAME_TYPE_NORMAL: return "normal"; case META_FRAME_TYPE_DIALOG: return "dialog"; case META_FRAME_TYPE_MODAL_DIALOG: return "modal_dialog"; case META_FRAME_TYPE_UTILITY: return "utility"; case META_FRAME_TYPE_MENU: return "menu"; case META_FRAME_TYPE_BORDER: return "border"; case META_FRAME_TYPE_ATTACHED: return "attached"; #if 0 case META_FRAME_TYPE_TOOLBAR: return "toolbar"; #endif case META_FRAME_TYPE_LAST: break; } return ""; } muffin-6.4.1/src/ui/ui.h0000664000175000017500000000513114723361714013734 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* Mutter interface for talking to GTK+ UI module */ /* * Copyright (C) 2001 Havoc Pennington * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_UI_H #define META_UI_H #include #include #include #include #include #include "core/util-private.h" #include "meta/types.h" typedef struct _MetaUI MetaUI; typedef struct _MetaUIFrame MetaUIFrame; typedef gboolean (* MetaEventFunc) (XEvent *xevent, gpointer data); MetaUI *meta_ui_new (MetaX11Display *x11_display); void meta_ui_free (MetaUI *ui); void meta_ui_theme_get_frame_borders (MetaUI *ui, MetaFrameType type, MetaFrameFlags flags, MetaFrameBorders *borders); MetaUIFrame * meta_ui_create_frame (MetaUI *ui, Display *xdisplay, MetaWindow *meta_window, Visual *xvisual, gint x, gint y, gint width, gint height, gulong *create_serial); void meta_ui_move_resize_frame (MetaUI *ui, Window frame, int x, int y, int width, int height); /* GDK insists on tracking map/unmap */ void meta_ui_map_frame (MetaUI *ui, Window xwindow); void meta_ui_unmap_frame (MetaUI *ui, Window xwindow); gboolean meta_ui_window_should_not_cause_focus (Display *xdisplay, Window xwindow); gboolean meta_ui_window_is_widget (MetaUI *ui, Window xwindow); META_EXPORT_TEST gboolean meta_ui_window_is_dummy (MetaUI *ui, Window xwindow); #endif muffin-6.4.1/src/wayland/0000775000175000017500000000000014723361714014170 5ustar fabiofabiomuffin-6.4.1/src/wayland/meta-wayland-input-device.c0000664000175000017500000000733114723361714021315 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "wayland/meta-wayland-input-device.h" #include #include "wayland/meta-wayland-seat.h" enum { PROP_0, PROP_SEAT }; typedef struct _MetaWaylandInputDevicePrivate { MetaWaylandSeat *seat; } MetaWaylandInputDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandInputDevice, meta_wayland_input_device, G_TYPE_OBJECT) MetaWaylandSeat * meta_wayland_input_device_get_seat (MetaWaylandInputDevice *input_device) { MetaWaylandInputDevicePrivate *priv = meta_wayland_input_device_get_instance_private (input_device); return priv->seat; } uint32_t meta_wayland_input_device_next_serial (MetaWaylandInputDevice *input_device) { MetaWaylandSeat *seat = meta_wayland_input_device_get_seat (input_device); return wl_display_next_serial (seat->wl_display); } static void meta_wayland_input_device_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (object); MetaWaylandInputDevicePrivate *priv = meta_wayland_input_device_get_instance_private (input_device); switch (prop_id) { case PROP_SEAT: priv->seat = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_input_device_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (object); MetaWaylandInputDevicePrivate *priv = meta_wayland_input_device_get_instance_private (input_device); switch (prop_id) { case PROP_SEAT: g_value_set_pointer (value, priv->seat); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_input_device_init (MetaWaylandInputDevice *input_device) { } static void meta_wayland_input_device_class_init (MetaWaylandInputDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->set_property = meta_wayland_input_device_set_property; object_class->get_property = meta_wayland_input_device_get_property; pspec = g_param_spec_pointer ("seat", "MetaWaylandSeat", "The seat", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_SEAT, pspec); } muffin-6.4.1/src/wayland/meta-wayland-pointer-gestures.h0000664000175000017500000000205714723361714022245 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_POINTER_GESTURES_H #define META_WAYLAND_POINTER_GESTURES_H #include #include #include "wayland/meta-wayland-types.h" void meta_wayland_pointer_gestures_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_POINTER_GESTURES_H */ muffin-6.4.1/src/wayland/meta-wayland.c0000664000175000017500000004662314723361714016732 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland.h" #include #include #include #include #include "clutter/clutter.h" #include "clutter/wayland/clutter-wayland-compositor.h" #include "core/main-private.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-dma-buf.h" #include "wayland/meta-wayland-egl-stream.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "wayland/meta-wayland-inhibit-shortcuts.h" #include "wayland/meta-wayland-outputs.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-region.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-subsurface.h" #include "wayland/meta-wayland-tablet-manager.h" #include "wayland/meta-wayland-xdg-foreign.h" #include "wayland/meta-xwayland-grab-keyboard.h" #include "wayland/meta-xwayland-private.h" #include "wayland/meta-xwayland.h" static char *_display_name_override; G_DEFINE_TYPE (MetaWaylandCompositor, meta_wayland_compositor, G_TYPE_OBJECT) MetaWaylandCompositor * meta_wayland_compositor_get_default (void) { MetaBackend *backend; MetaWaylandCompositor *wayland_compositor; backend = meta_get_backend (); wayland_compositor = meta_backend_get_wayland_compositor (backend); g_assert (wayland_compositor); return wayland_compositor; } typedef struct { GSource source; struct wl_display *display; } WaylandEventSource; static gboolean wayland_event_source_prepare (GSource *base, int *timeout) { WaylandEventSource *source = (WaylandEventSource *)base; *timeout = -1; wl_display_flush_clients (source->display); return FALSE; } static gboolean wayland_event_source_dispatch (GSource *base, GSourceFunc callback, void *data) { WaylandEventSource *source = (WaylandEventSource *)base; struct wl_event_loop *loop = wl_display_get_event_loop (source->display); wl_event_loop_dispatch (loop, 0); return TRUE; } static GSourceFuncs wayland_event_source_funcs = { wayland_event_source_prepare, NULL, wayland_event_source_dispatch, NULL }; static GSource * wayland_event_source_new (struct wl_display *display) { WaylandEventSource *source; struct wl_event_loop *loop = wl_display_get_event_loop (display); source = (WaylandEventSource *) g_source_new (&wayland_event_source_funcs, sizeof (WaylandEventSource)); source->display = display; g_source_add_unix_fd (&source->source, wl_event_loop_get_fd (loop), G_IO_IN | G_IO_ERR); return &source->source; } void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, MetaWindow *window) { MetaWaylandSurface *surface = window ? window->surface : NULL; meta_wayland_seat_set_input_focus (compositor->seat, surface); } void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor) { meta_wayland_seat_repick (compositor->seat); } static void wl_compositor_create_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandCompositor *compositor = wl_resource_get_user_data (resource); meta_wayland_surface_create (compositor, client, resource, id); } static void wl_compositor_create_region (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandCompositor *compositor = wl_resource_get_user_data (resource); meta_wayland_region_create (compositor, client, resource, id); } static const struct wl_compositor_interface meta_wayland_wl_compositor_interface = { wl_compositor_create_surface, wl_compositor_create_region }; static void compositor_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &wl_compositor_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_wl_compositor_interface, compositor, NULL); } /** * meta_wayland_compositor_update: * @compositor: the #MetaWaylandCompositor instance * @event: the #ClutterEvent used to update @seat's state * * This is used to update display server state like updating cursor * position and keeping track of buttons and keys pressed. It must be * called for all input events coming from the underlying devices. */ void meta_wayland_compositor_update (MetaWaylandCompositor *compositor, const ClutterEvent *event) { if (meta_wayland_tablet_manager_consumes_event (compositor->tablet_manager, event)) meta_wayland_tablet_manager_update (compositor->tablet_manager, event); else meta_wayland_seat_update (compositor->seat, event); } void meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor) { GList *l; int64_t now_us; now_us = g_get_monotonic_time (); l = compositor->frame_callback_surfaces; while (l) { GList *l_cur = l; MetaWaylandSurface *surface = l->data; MetaSurfaceActor *actor; MetaWaylandActorSurface *actor_surface; l = l->next; actor = meta_wayland_surface_get_actor (surface); if (!actor) continue; if (!clutter_actor_has_mapped_clones (CLUTTER_ACTOR (actor)) && meta_surface_actor_is_obscured (actor)) continue; actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role); meta_wayland_actor_surface_emit_frame_callbacks (actor_surface, now_us / 1000); compositor->frame_callback_surfaces = g_list_delete_link (compositor->frame_callback_surfaces, l_cur); } } /** * meta_wayland_compositor_handle_event: * @compositor: the #MetaWaylandCompositor instance * @event: the #ClutterEvent to be sent * * This method sends events to the focused wayland client, if any. * * Return value: whether @event was sent to a wayland client. */ gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, const ClutterEvent *event) { if (meta_wayland_tablet_manager_handle_event (compositor->tablet_manager, event)) return TRUE; return meta_wayland_seat_handle_event (compositor->seat, event); } /* meta_wayland_compositor_update_key_state: * @compositor: the #MetaWaylandCompositor * @key_vector: bit vector of key states * @key_vector_len: length of @key_vector * @offset: the key for the first evdev keycode is found at this offset in @key_vector * * This function is used to resynchronize the key state that Mutter * is tracking with the actual keyboard state. This is useful, for example, * to handle changes in key state when a nested compositor doesn't * have focus. We need to fix up the XKB modifier tracking and deliver * any modifier changes to clients. */ void meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor, char *key_vector, int key_vector_len, int offset) { meta_wayland_keyboard_update_key_state (compositor->seat->keyboard, key_vector, key_vector_len, offset); } void meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface) { if (g_list_find (compositor->frame_callback_surfaces, surface)) return; compositor->frame_callback_surfaces = g_list_prepend (compositor->frame_callback_surfaces, surface); } void meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface) { compositor->frame_callback_surfaces = g_list_remove (compositor->frame_callback_surfaces, surface); } static void set_gnome_env (const char *name, const char *value) { GDBusConnection *session_bus; GError *error = NULL; g_autoptr (GVariant) result = NULL; setenv (name, value, TRUE); session_bus = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, NULL); g_assert (session_bus); result = g_dbus_connection_call_sync (session_bus, "org.gnome.SessionManager", "/org/gnome/SessionManager", "org.gnome.SessionManager", "Setenv", g_variant_new ("(ss)", name, value), NULL, G_DBUS_CALL_FLAGS_NO_AUTO_START, -1, NULL, &error); if (error) { char *remote_error; remote_error = g_dbus_error_get_remote_error (error); if (g_strcmp0 (remote_error, "org.gnome.SessionManager.NotInInitialization") != 0) meta_warning ("Failed to set environment variable %s for gnome-session: %s\n", name, error->message); g_free (remote_error); g_error_free (error); } } static void meta_wayland_log_func (const char *, va_list) G_GNUC_PRINTF (1, 0); static void meta_wayland_log_func (const char *fmt, va_list arg) { char *str = g_strdup_vprintf (fmt, arg); g_warning ("WL: %s", str); g_free (str); } static void meta_wayland_compositor_init (MetaWaylandCompositor *compositor) { compositor->scheduled_surface_associations = g_hash_table_new (NULL, NULL); wl_log_set_handler_server (meta_wayland_log_func); compositor->wayland_display = wl_display_create (); if (compositor->wayland_display == NULL) g_error ("Failed to create the global wl_display"); clutter_wayland_set_compositor_display (compositor->wayland_display); } static void meta_wayland_compositor_class_init (MetaWaylandCompositorClass *klass) { } static bool meta_xwayland_global_filter (const struct wl_client *client, const struct wl_global *global, void *data) { MetaWaylandCompositor *compositor = (MetaWaylandCompositor *) data; MetaXWaylandManager *xwayland_manager = &compositor->xwayland_manager; /* Keyboard grabbing protocol is for Xwayland only */ if (client != xwayland_manager->client) return (wl_global_get_interface (global) != &zwp_xwayland_keyboard_grab_manager_v1_interface); /* All others are visible to all clients */ return true; } void meta_wayland_override_display_name (const char *display_name) { g_clear_pointer (&_display_name_override, g_free); _display_name_override = g_strdup (display_name); } static const char * meta_wayland_get_xwayland_auth_file (MetaWaylandCompositor *compositor) { return compositor->xwayland_manager.auth_file; } MetaWaylandCompositor * meta_wayland_compositor_new (MetaBackend *backend) { MetaWaylandCompositor *compositor; compositor = g_object_new (META_TYPE_WAYLAND_COMPOSITOR, NULL); compositor->backend = backend; return compositor; } void meta_wayland_compositor_setup (MetaWaylandCompositor *wayland_compositor) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); GSource *wayland_event_source; wayland_event_source = wayland_event_source_new (compositor->wayland_display); /* XXX: Here we are setting the wayland event source to have a * slightly lower priority than the X event source, because we are * much more likely to get confused being told about surface changes * relating to X clients when we don't know what's happened to them * according to the X protocol. */ g_source_set_priority (wayland_event_source, GDK_PRIORITY_EVENTS + 1); g_source_attach (wayland_event_source, NULL); if (!wl_global_create (compositor->wayland_display, &wl_compositor_interface, META_WL_COMPOSITOR_VERSION, compositor, compositor_bind)) g_error ("Failed to register the global wl_compositor"); wl_display_init_shm (compositor->wayland_display); meta_wayland_outputs_init (compositor); meta_wayland_data_device_manager_init (compositor); meta_wayland_data_device_primary_manager_init (compositor); meta_wayland_data_device_primary_legacy_manager_init (compositor); meta_wayland_subsurfaces_init (compositor); meta_wayland_shell_init (compositor); meta_wayland_pointer_gestures_init (compositor); meta_wayland_tablet_manager_init (compositor); meta_wayland_seat_init (compositor); meta_wayland_relative_pointer_init (compositor); meta_wayland_pointer_constraints_init (compositor); meta_wayland_xdg_foreign_init (compositor); meta_wayland_dma_buf_init (compositor); meta_wayland_keyboard_shortcuts_inhibit_init (compositor); meta_wayland_surface_inhibit_shortcuts_dialog_init (); meta_wayland_text_input_init (compositor); meta_wayland_gtk_text_input_init (compositor); /* Xwayland specific protocol, needs to be filtered out for all other clients */ if (meta_xwayland_grab_keyboard_init (compositor)) wl_display_set_global_filter (compositor->wayland_display, meta_xwayland_global_filter, compositor); #ifdef HAVE_WAYLAND_EGLSTREAM meta_wayland_eglstream_controller_init (compositor); #endif if (meta_get_x11_display_policy () != META_DISPLAY_POLICY_DISABLED) { if (!meta_xwayland_init (&compositor->xwayland_manager, compositor->wayland_display)) g_error ("Failed to start X Wayland"); } if (_display_name_override) { compositor->display_name = g_steal_pointer (&_display_name_override); if (wl_display_add_socket (compositor->wayland_display, compositor->display_name) != 0) g_error ("Failed to create_socket"); } else { const char *display_name; display_name = wl_display_add_socket_auto (compositor->wayland_display); if (!display_name) g_error ("Failed to create socket"); compositor->display_name = g_strdup (display_name); } if (meta_get_x11_display_policy () != META_DISPLAY_POLICY_DISABLED) { set_gnome_env ("GNOME_SETUP_DISPLAY", compositor->xwayland_manager.private_connection.name); set_gnome_env ("DISPLAY", compositor->xwayland_manager.public_connection.name); set_gnome_env ("XAUTHORITY", meta_wayland_get_xwayland_auth_file (compositor)); } set_gnome_env ("WAYLAND_DISPLAY", meta_wayland_get_wayland_display_name (compositor)); } const char * meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor) { return compositor->display_name; } const char * meta_wayland_get_xwayland_display_name (MetaWaylandCompositor *compositor) { return compositor->xwayland_manager.private_connection.name; } void meta_wayland_finalize (void) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); meta_xwayland_shutdown (&compositor->xwayland_manager); g_clear_pointer (&compositor->display_name, g_free); } void meta_wayland_compositor_restore_shortcuts (MetaWaylandCompositor *compositor, ClutterInputDevice *source) { MetaWaylandKeyboard *keyboard; /* Clutter is not multi-seat aware yet, use the default seat instead */ keyboard = compositor->seat->keyboard; if (!keyboard || !keyboard->focus_surface) return; if (!meta_wayland_surface_is_shortcuts_inhibited (keyboard->focus_surface, compositor->seat)) return; meta_wayland_surface_restore_shortcuts (keyboard->focus_surface, compositor->seat); } gboolean meta_wayland_compositor_is_shortcuts_inhibited (MetaWaylandCompositor *compositor, ClutterInputDevice *source) { MetaWaylandKeyboard *keyboard; /* Clutter is not multi-seat aware yet, use the default seat instead */ keyboard = compositor->seat->keyboard; if (keyboard && keyboard->focus_surface != NULL) return meta_wayland_surface_is_shortcuts_inhibited (keyboard->focus_surface, compositor->seat); return FALSE; } void meta_wayland_compositor_flush_clients (MetaWaylandCompositor *compositor) { wl_display_flush_clients (compositor->wayland_display); } static void on_scheduled_association_unmanaged (MetaWindow *window, gpointer user_data); static void meta_wayland_compositor_remove_surface_association (MetaWaylandCompositor *compositor, int id) { MetaWindow *window; window = g_hash_table_lookup (compositor->scheduled_surface_associations, GINT_TO_POINTER (id)); if (window) { g_signal_handlers_disconnect_by_func (window, on_scheduled_association_unmanaged, GINT_TO_POINTER (id)); g_hash_table_remove (compositor->scheduled_surface_associations, GINT_TO_POINTER (id)); } } static void on_scheduled_association_unmanaged (MetaWindow *window, gpointer user_data) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_remove_surface_association (compositor, GPOINTER_TO_INT (user_data)); } void meta_wayland_compositor_schedule_surface_association (MetaWaylandCompositor *compositor, int id, MetaWindow *window) { g_signal_connect (window, "unmanaged", G_CALLBACK (on_scheduled_association_unmanaged), GINT_TO_POINTER (id)); g_hash_table_insert (compositor->scheduled_surface_associations, GINT_TO_POINTER (id), window); } void meta_wayland_compositor_notify_surface_id (MetaWaylandCompositor *compositor, int id, MetaWaylandSurface *surface) { MetaWindow *window; window = g_hash_table_lookup (compositor->scheduled_surface_associations, GINT_TO_POINTER (id)); if (window) { meta_xwayland_associate_window_with_surface (window, surface); meta_wayland_compositor_remove_surface_association (compositor, id); } } muffin-6.4.1/src/wayland/meta-wayland-tablet-tool.c0000664000175000017500000007664014723361714021160 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-tablet-tool.h" #include #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-cursor-surface.h" #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-tablet.h" #include "wayland/meta-wayland-tablet-seat.h" #include "backends/meta-input-settings-private.h" #include "backends/meta-logical-monitor.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #include "backends/native/meta-event-native.h" #endif #include "tablet-unstable-v2-server-protocol.h" #define TABLET_AXIS_MAX 65535 static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void meta_wayland_tablet_tool_update_cursor_surface (MetaWaylandTabletTool *tool) { MetaCursorSprite *cursor = NULL; if (tool->cursor_renderer == NULL) return; if (tool->current && tool->current_tablet) { if (tool->cursor_surface && meta_wayland_surface_get_buffer (tool->cursor_surface)) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (tool->cursor_surface->role); cursor = meta_wayland_cursor_surface_get_sprite (cursor_surface); } else cursor = NULL; } else if (tool->current_tablet) cursor = META_CURSOR_SPRITE (tool->default_sprite); else cursor = NULL; meta_cursor_renderer_set_cursor (tool->cursor_renderer, cursor); } static void meta_wayland_tablet_tool_set_cursor_surface (MetaWaylandTabletTool *tool, MetaWaylandSurface *surface) { if (tool->cursor_surface == surface) return; if (tool->cursor_surface) { MetaWaylandCursorSurface *cursor_surface; cursor_surface = META_WAYLAND_CURSOR_SURFACE (tool->cursor_surface->role); meta_wayland_cursor_surface_set_renderer (cursor_surface, NULL); meta_wayland_surface_update_outputs (tool->cursor_surface); wl_list_remove (&tool->cursor_surface_destroy_listener.link); } tool->cursor_surface = surface; if (tool->cursor_surface) { meta_wayland_surface_update_outputs (tool->cursor_surface); wl_resource_add_destroy_listener (tool->cursor_surface->resource, &tool->cursor_surface_destroy_listener); } meta_wayland_tablet_tool_update_cursor_surface (tool); } static uint32_t input_device_get_capabilities (ClutterInputDevice *device) { ClutterInputAxis axis; guint32 capabilities = 0, i; for (i = 0; i < clutter_input_device_get_n_axes (device); i++) { axis = clutter_input_device_get_axis (device, i); switch (axis) { case CLUTTER_INPUT_AXIS_PRESSURE: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE; break; case CLUTTER_INPUT_AXIS_DISTANCE: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE; break; case CLUTTER_INPUT_AXIS_XTILT: case CLUTTER_INPUT_AXIS_YTILT: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_TILT; break; case CLUTTER_INPUT_AXIS_ROTATION: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION; break; case CLUTTER_INPUT_AXIS_WHEEL: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL; break; case CLUTTER_INPUT_AXIS_SLIDER: capabilities |= 1 << ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER; break; default: break; } } return capabilities; } static enum zwp_tablet_tool_v2_type input_device_tool_get_type (ClutterInputDeviceTool *device_tool) { ClutterInputDeviceToolType tool_type; tool_type = clutter_input_device_tool_get_tool_type (device_tool); switch (tool_type) { case CLUTTER_INPUT_DEVICE_TOOL_NONE: case CLUTTER_INPUT_DEVICE_TOOL_PEN: return ZWP_TABLET_TOOL_V2_TYPE_PEN; case CLUTTER_INPUT_DEVICE_TOOL_ERASER: return ZWP_TABLET_TOOL_V2_TYPE_ERASER; case CLUTTER_INPUT_DEVICE_TOOL_BRUSH: return ZWP_TABLET_TOOL_V2_TYPE_BRUSH; case CLUTTER_INPUT_DEVICE_TOOL_PENCIL: return ZWP_TABLET_TOOL_V2_TYPE_PENCIL; case CLUTTER_INPUT_DEVICE_TOOL_AIRBRUSH: return ZWP_TABLET_TOOL_V2_TYPE_AIRBRUSH; case CLUTTER_INPUT_DEVICE_TOOL_MOUSE: return ZWP_TABLET_TOOL_V2_TYPE_MOUSE; case CLUTTER_INPUT_DEVICE_TOOL_LENS: return ZWP_TABLET_TOOL_V2_TYPE_LENS; } g_assert_not_reached (); return 0; } static void meta_wayland_tablet_tool_notify_capabilities (MetaWaylandTabletTool *tool, struct wl_resource *resource) { uint32_t capabilities; capabilities = input_device_get_capabilities (tool->device); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_TILT)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_TILT); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL)) zwp_tablet_tool_v2_send_capability (resource, ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL); } static void meta_wayland_tablet_tool_notify_details (MetaWaylandTabletTool *tool, struct wl_resource *resource) { guint64 serial, id; zwp_tablet_tool_v2_send_type (resource, input_device_tool_get_type (tool->device_tool)); serial = clutter_input_device_tool_get_serial (tool->device_tool); zwp_tablet_tool_v2_send_hardware_serial (resource, (uint32_t) (serial >> 32), (uint32_t) (serial & G_MAXUINT32)); id = clutter_input_device_tool_get_id (tool->device_tool); zwp_tablet_tool_v2_send_hardware_id_wacom (resource, (uint32_t) (id >> 32), (uint32_t) (id & G_MAXUINT32)); meta_wayland_tablet_tool_notify_capabilities (tool, resource); zwp_tablet_tool_v2_send_done (resource); } static void meta_wayland_tablet_tool_ensure_resource (MetaWaylandTabletTool *tool, struct wl_client *client) { struct wl_resource *seat_resource, *tool_resource; seat_resource = meta_wayland_tablet_seat_lookup_resource (tool->seat, client); if (seat_resource && !meta_wayland_tablet_tool_lookup_resource (tool, client)) { tool_resource = meta_wayland_tablet_tool_create_new_resource (tool, client, seat_resource, 0); meta_wayland_tablet_seat_notify_tool (tool->seat, tool, client); meta_wayland_tablet_tool_notify_details (tool, tool_resource); } } static void broadcast_proximity_in (MetaWaylandTabletTool *tool) { struct wl_resource *resource, *tablet_resource; struct wl_client *client; client = wl_resource_get_client (tool->focus_surface->resource); tablet_resource = meta_wayland_tablet_lookup_resource (tool->current_tablet, client); wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_proximity_in (resource, tool->proximity_serial, tablet_resource, tool->focus_surface->resource); } } static void broadcast_proximity_out (MetaWaylandTabletTool *tool) { struct wl_resource *resource; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_proximity_out (resource); } } static void broadcast_frame (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; guint32 _time = event ? clutter_event_get_time (event) : CLUTTER_CURRENT_TIME; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_frame (resource, _time); } } static void meta_wayland_tablet_tool_set_focus (MetaWaylandTabletTool *tool, MetaWaylandSurface *surface, const ClutterEvent *event) { if (tool->focus_surface == surface) return; if (tool->focus_surface != NULL) { struct wl_list *l; l = &tool->focus_resource_list; if (!wl_list_empty (l)) { broadcast_proximity_out (tool); broadcast_frame (tool, event); move_resources (&tool->resource_list, &tool->focus_resource_list); } wl_list_remove (&tool->focus_surface_destroy_listener.link); tool->focus_surface = NULL; } if (surface != NULL && tool->current_tablet) { struct wl_client *client; struct wl_list *l; tool->focus_surface = surface; client = wl_resource_get_client (tool->focus_surface->resource); wl_resource_add_destroy_listener (tool->focus_surface->resource, &tool->focus_surface_destroy_listener); move_resources_for_client (&tool->focus_resource_list, &tool->resource_list, client); meta_wayland_tablet_tool_ensure_resource (tool, client); l = &tool->focus_resource_list; if (!wl_list_empty (l)) { struct wl_client *client = wl_resource_get_client (tool->focus_surface->resource); struct wl_display *display = wl_client_get_display (client); tool->proximity_serial = wl_display_next_serial (display); broadcast_proximity_in (tool); broadcast_frame (tool, event); } } meta_wayland_tablet_tool_update_cursor_surface (tool); } static void tablet_tool_handle_focus_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandTabletTool *tool; tool = wl_container_of (listener, tool, focus_surface_destroy_listener); meta_wayland_tablet_tool_set_focus (tool, NULL, NULL); } static void tablet_tool_handle_cursor_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandTabletTool *tool; tool = wl_container_of (listener, tool, cursor_surface_destroy_listener); meta_wayland_tablet_tool_set_cursor_surface (tool, NULL); } static void tool_cursor_prepare_at (MetaCursorSpriteXcursor *sprite_xcursor, int x, int y, MetaWaylandTabletTool *tool) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); /* Reload the cursor texture if the scale has changed. */ if (logical_monitor) { MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (sprite_xcursor); float ceiled_scale; ceiled_scale = ceilf (logical_monitor->scale); meta_cursor_sprite_xcursor_set_theme_scale (sprite_xcursor, (int) ceiled_scale); if (meta_is_stage_views_scaled ()) meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0 / ceiled_scale); else meta_cursor_sprite_set_texture_scale (cursor_sprite, 1.0); } } MetaWaylandTabletTool * meta_wayland_tablet_tool_new (MetaWaylandTabletSeat *seat, ClutterInputDevice *device, ClutterInputDeviceTool *device_tool) { MetaWaylandTabletTool *tool; tool = g_slice_new0 (MetaWaylandTabletTool); tool->seat = seat; tool->device = device; tool->device_tool = device_tool; wl_list_init (&tool->resource_list); wl_list_init (&tool->focus_resource_list); tool->focus_surface_destroy_listener.notify = tablet_tool_handle_focus_surface_destroy; tool->cursor_surface_destroy_listener.notify = tablet_tool_handle_cursor_surface_destroy; tool->default_sprite = meta_cursor_sprite_xcursor_new (META_CURSOR_CROSSHAIR); tool->prepare_at_signal_id = g_signal_connect (tool->default_sprite, "prepare-at", G_CALLBACK (tool_cursor_prepare_at), tool); return tool; } void meta_wayland_tablet_tool_free (MetaWaylandTabletTool *tool) { struct wl_resource *resource, *next; meta_wayland_tablet_tool_set_focus (tool, NULL, NULL); meta_wayland_tablet_tool_set_cursor_surface (tool, NULL); g_clear_object (&tool->cursor_renderer); wl_resource_for_each_safe (resource, next, &tool->resource_list) { zwp_tablet_tool_v2_send_removed (resource); wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_clear_signal_handler (&tool->prepare_at_signal_id, tool->default_sprite); g_object_unref (tool->default_sprite); g_slice_free (MetaWaylandTabletTool, tool); } static void tool_set_cursor (struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t hotspot_x, int32_t hotspot_y) { MetaWaylandTabletTool *tool = wl_resource_get_user_data (resource); MetaWaylandSurface *surface; surface = (surface_resource ? wl_resource_get_user_data (surface_resource) : NULL); if (tool->focus_surface == NULL) return; if (tool->cursor_renderer == NULL) return; if (wl_resource_get_client (tool->focus_surface->resource) != client) return; if (tool->proximity_serial - serial > G_MAXUINT32 / 2) return; if (surface && !meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_TABLET_CURSOR_SURFACE, NULL)) { wl_resource_post_error (resource, WL_POINTER_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface_resource)); return; } if (surface) { MetaWaylandCursorSurface *cursor_surface; cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); meta_wayland_cursor_surface_set_renderer (cursor_surface, tool->cursor_renderer); meta_wayland_cursor_surface_set_hotspot (cursor_surface, hotspot_x, hotspot_y); } meta_wayland_tablet_tool_set_cursor_surface (tool, surface); } static void tool_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_tool_v2_interface tool_interface = { tool_set_cursor, tool_destroy }; static void emit_proximity_in (MetaWaylandTabletTool *tool, struct wl_resource *resource) { struct wl_resource *tablet_resource; struct wl_client *client; if (!tool->focus_surface) return; client = wl_resource_get_client (resource); tablet_resource = meta_wayland_tablet_lookup_resource (tool->current_tablet, client); zwp_tablet_tool_v2_send_proximity_in (resource, tool->proximity_serial, tablet_resource, tool->focus_surface->resource); } struct wl_resource * meta_wayland_tablet_tool_create_new_resource (MetaWaylandTabletTool *tool, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_tool_v2_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &tool_interface, tool, unbind_resource); wl_resource_set_user_data (resource, tool); if (tool->focus_surface && wl_resource_get_client (tool->focus_surface->resource) == client) { wl_list_insert (&tool->focus_resource_list, wl_resource_get_link (resource)); emit_proximity_in (tool, resource); } else { wl_list_insert (&tool->resource_list, wl_resource_get_link (resource)); } return resource; } struct wl_resource * meta_wayland_tablet_tool_lookup_resource (MetaWaylandTabletTool *tool, struct wl_client *client) { struct wl_resource *resource = NULL; if (!wl_list_empty (&tool->resource_list)) resource = wl_resource_find_for_client (&tool->resource_list, client); if (!wl_list_empty (&tool->focus_resource_list)) resource = wl_resource_find_for_client (&tool->focus_resource_list, client); return resource; } static void meta_wayland_tablet_tool_account_button (MetaWaylandTabletTool *tool, const ClutterEvent *event) { if (event->type == CLUTTER_BUTTON_PRESS) { tool->pressed_buttons |= 1 << (event->button.button - 1); tool->button_count++; } else if (event->type == CLUTTER_BUTTON_RELEASE) { tool->pressed_buttons &= ~(1 << (event->button.button - 1)); tool->button_count--; } } static void sync_focus_surface (MetaWaylandTabletTool *tool, const ClutterEvent *event) { MetaDisplay *display = meta_get_display (); switch (display->event_route) { case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_FRAME_BUTTON: /* The compositor has a grab, so remove our focus */ meta_wayland_tablet_tool_set_focus (tool, NULL, event); break; case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: meta_wayland_tablet_tool_set_focus (tool, tool->current, event); break; default: g_assert_not_reached (); } } static void repick_for_event (MetaWaylandTabletTool *tool, const ClutterEvent *for_event) { ClutterActor *actor = NULL; actor = clutter_event_get_source (for_event); if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) tool->current = meta_surface_actor_wayland_get_surface (META_SURFACE_ACTOR_WAYLAND (actor)); else tool->current = NULL; sync_focus_surface (tool, for_event); meta_wayland_tablet_tool_update_cursor_surface (tool); } static void meta_wayland_tablet_tool_get_relative_coordinates (MetaWaylandTabletTool *tool, const ClutterEvent *event, MetaWaylandSurface *surface, wl_fixed_t *sx, wl_fixed_t *sy) { float xf, yf; clutter_event_get_coords (event, &xf, &yf); meta_wayland_surface_get_relative_coordinates (surface, xf, yf, &xf, &yf); *sx = wl_fixed_from_double (xf); *sy = wl_fixed_from_double (yf); } static void broadcast_motion (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; wl_fixed_t sx, sy; meta_wayland_tablet_tool_get_relative_coordinates (tool, event, tool->focus_surface, &sx, &sy); wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_motion (resource, sx, sy); } } static void broadcast_down (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; tool->down_serial = wl_display_next_serial (tool->seat->manager->wl_display); wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_down (resource, tool->down_serial); } } static void broadcast_up (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_up (resource); } } static void broadcast_button (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; guint32 button; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { button = meta_event_native_get_event_code (event); } else #endif { /* We can't do much better here, there's several * different BTN_ ranges to cover. */ button = event->button.button; } tool->button_serial = wl_display_next_serial (tool->seat->manager->wl_display); wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_button (resource, tool->button_serial, button, event->type == CLUTTER_BUTTON_PRESS ? ZWP_TABLET_TOOL_V2_BUTTON_STATE_PRESSED : ZWP_TABLET_TOOL_V2_BUTTON_STATE_RELEASED); } } static void broadcast_axis (MetaWaylandTabletTool *tool, const ClutterEvent *event, ClutterInputAxis axis) { struct wl_resource *resource; ClutterInputDevice *source; uint32_t value; gdouble val; source = clutter_event_get_source_device (event); if (!clutter_input_device_get_axis_value (source, event->motion.axes, axis, &val)) return; value = val * TABLET_AXIS_MAX; wl_resource_for_each (resource, &tool->focus_resource_list) { switch (axis) { case CLUTTER_INPUT_AXIS_PRESSURE: zwp_tablet_tool_v2_send_pressure (resource, value); break; case CLUTTER_INPUT_AXIS_DISTANCE: zwp_tablet_tool_v2_send_distance (resource, value); break; case CLUTTER_INPUT_AXIS_SLIDER: zwp_tablet_tool_v2_send_slider (resource, value); break; default: break; } } } static void broadcast_tilt (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; ClutterInputDevice *source; gdouble xtilt, ytilt; source = clutter_event_get_source_device (event); if (!clutter_input_device_get_axis_value (source, event->motion.axes, CLUTTER_INPUT_AXIS_XTILT, &xtilt) || !clutter_input_device_get_axis_value (source, event->motion.axes, CLUTTER_INPUT_AXIS_YTILT, &ytilt)) return; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_tilt (resource, wl_fixed_from_double (xtilt), wl_fixed_from_double (ytilt)); } } static void broadcast_rotation (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; ClutterInputDevice *source; gdouble rotation; source = clutter_event_get_source_device (event); if (!clutter_input_device_get_axis_value (source, event->motion.axes, CLUTTER_INPUT_AXIS_ROTATION, &rotation)) return; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_rotation (resource, wl_fixed_from_double (rotation)); } } static void broadcast_wheel (MetaWaylandTabletTool *tool, const ClutterEvent *event) { struct wl_resource *resource; ClutterInputDevice *source; gdouble angle; gint32 clicks = 0; source = clutter_event_get_source_device (event); if (!clutter_input_device_get_axis_value (source, event->motion.axes, CLUTTER_INPUT_AXIS_WHEEL, &angle)) return; /* FIXME: Perform proper angle-to-clicks accumulation elsewhere */ if (angle > 0.01) clicks = 1; else if (angle < -0.01) clicks = -1; else return; wl_resource_for_each (resource, &tool->focus_resource_list) { zwp_tablet_tool_v2_send_wheel (resource, wl_fixed_from_double (angle), clicks); } } static void broadcast_axes (MetaWaylandTabletTool *tool, const ClutterEvent *event) { ClutterInputDevice *device; guint32 capabilities; if (!event->motion.axes) return; device = clutter_event_get_source_device (event); capabilities = input_device_get_capabilities (device); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_PRESSURE)) broadcast_axis (tool, event, CLUTTER_INPUT_AXIS_PRESSURE); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_DISTANCE)) broadcast_axis (tool, event, CLUTTER_INPUT_AXIS_DISTANCE); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_TILT)) broadcast_tilt (tool, event); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_ROTATION)) broadcast_rotation (tool, event); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_SLIDER)) broadcast_axis (tool, event, CLUTTER_INPUT_AXIS_SLIDER); if (capabilities & (1 << ZWP_TABLET_TOOL_V2_CAPABILITY_WHEEL)) broadcast_wheel (tool, event); } static void handle_motion_event (MetaWaylandTabletTool *tool, const ClutterEvent *event) { if (!tool->focus_surface) return; broadcast_motion (tool, event); broadcast_axes (tool, event); broadcast_frame (tool, event); } static void handle_button_event (MetaWaylandTabletTool *tool, const ClutterEvent *event) { if (!tool->focus_surface) return; if (event->type == CLUTTER_BUTTON_PRESS && tool->button_count == 1) clutter_event_get_coords (event, &tool->grab_x, &tool->grab_y); if (event->type == CLUTTER_BUTTON_PRESS && event->button.button == 1) broadcast_down (tool, event); else if (event->type == CLUTTER_BUTTON_RELEASE && event->button.button == 1) broadcast_up (tool, event); else broadcast_button (tool, event); broadcast_frame (tool, event); } void meta_wayland_tablet_tool_update (MetaWaylandTabletTool *tool, const ClutterEvent *event) { switch (event->type) { case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: meta_wayland_tablet_tool_account_button (tool, event); break; case CLUTTER_MOTION: if (!tool->pressed_buttons) repick_for_event (tool, event); break; case CLUTTER_PROXIMITY_IN: if (!tool->cursor_renderer) tool->cursor_renderer = meta_cursor_renderer_new (); tool->current_tablet = meta_wayland_tablet_seat_lookup_tablet (tool->seat, clutter_event_get_source_device (event)); break; case CLUTTER_PROXIMITY_OUT: tool->current_tablet = NULL; meta_wayland_tablet_tool_set_cursor_surface (tool, NULL); meta_wayland_tablet_tool_update_cursor_surface (tool); g_clear_object (&tool->cursor_renderer); break; default: break; } } gboolean meta_wayland_tablet_tool_handle_event (MetaWaylandTabletTool *tool, const ClutterEvent *event) { switch (event->type) { case CLUTTER_PROXIMITY_IN: /* We don't have much info here to make anything useful out of it, * wait until the first motion event so we have both coordinates * and tool. */ break; case CLUTTER_PROXIMITY_OUT: meta_wayland_tablet_tool_set_focus (tool, NULL, event); break; case CLUTTER_MOTION: handle_motion_event (tool, event); break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: handle_button_event (tool, event); break; default: return CLUTTER_EVENT_PROPAGATE; } return CLUTTER_EVENT_STOP; } void meta_wayland_tablet_tool_set_cursor_position (MetaWaylandTabletTool *tool, float new_x, float new_y) { if (tool->cursor_renderer) meta_cursor_renderer_set_position (tool->cursor_renderer, new_x, new_y); } static gboolean tablet_tool_can_grab_surface (MetaWaylandTabletTool *tool, MetaWaylandSurface *surface) { MetaWaylandSurface *subsurface; if (tool->focus_surface == surface) return TRUE; META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface) { if (tablet_tool_can_grab_surface (tool, subsurface)) return TRUE; } return FALSE; } gboolean meta_wayland_tablet_tool_can_grab_surface (MetaWaylandTabletTool *tool, MetaWaylandSurface *surface, uint32_t serial) { return ((tool->down_serial == serial || tool->button_serial == serial) && tablet_tool_can_grab_surface (tool, surface)); } gboolean meta_wayland_tablet_tool_can_popup (MetaWaylandTabletTool *tool, uint32_t serial) { return tool->down_serial == serial || tool->button_serial == serial; } muffin-6.4.1/src/wayland/meta-wayland-pointer-gesture-pinch.h0000664000175000017500000000305114723361714023154 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_POINTER_GESTURE_PINCH_H #define META_WAYLAND_POINTER_GESTURE_PINCH_H #include #include #include "clutter/clutter.h" #include "wayland/meta-wayland-types.h" gboolean meta_wayland_pointer_gesture_pinch_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_gesture_pinch_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *gestures_resource, uint32_t id); #endif /* META_WAYLAND_POINTER_GESTURE_PINCH_H */ muffin-6.4.1/src/wayland/meta-wayland-legacy-xdg-shell.h0000664000175000017500000000406514723361714022060 0ustar fabiofabio/* * Copyright (C) 2013-2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_LEGACY_XDG_SHELL_H #define META_WAYLAND_LEGACY_XDG_SHELL_H #include "wayland/meta-wayland-shell-surface.h" #include "wayland/meta-wayland-surface.h" #define META_TYPE_WAYLAND_ZXDG_SURFACE_V6 (meta_wayland_zxdg_surface_v6_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandZxdgSurfaceV6, meta_wayland_zxdg_surface_v6, META, WAYLAND_ZXDG_SURFACE_V6, MetaWaylandShellSurface) struct _MetaWaylandZxdgSurfaceV6Class { MetaWaylandShellSurfaceClass parent_class; void (*shell_client_destroyed) (MetaWaylandZxdgSurfaceV6 *xdg_surface); }; #define META_TYPE_WAYLAND_ZXDG_TOPLEVEL_V6 (meta_wayland_zxdg_toplevel_v6_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandZxdgToplevelV6, meta_wayland_zxdg_toplevel_v6, META, WAYLAND_ZXDG_TOPLEVEL_V6, MetaWaylandZxdgSurfaceV6); #define META_TYPE_WAYLAND_ZXDG_POPUP_V6 (meta_wayland_zxdg_popup_v6_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandZxdgPopupV6, meta_wayland_zxdg_popup_v6, META, WAYLAND_ZXDG_POPUP_V6, MetaWaylandZxdgSurfaceV6); void meta_wayland_legacy_xdg_shell_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_LEGACY_XDG_SHELL_H */ muffin-6.4.1/src/wayland/meta-xwayland-dnd-private.h0000664000175000017500000000245514723361714021335 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_XWAYLAND_SELECTION_PRIVATE_H #define META_XWAYLAND_SELECTION_PRIVATE_H #define META_TYPE_WAYLAND_DATA_SOURCE_XWAYLAND (meta_wayland_data_source_xwayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandDataSourceXWayland, meta_wayland_data_source_xwayland, META, WAYLAND_DATA_SOURCE_XWAYLAND, MetaWaylandDataSource); #endif /* META_XWAYLAND_SELECTION_PRIVATE_H */ muffin-6.4.1/src/wayland/meta-wayland-subsurface.h0000664000175000017500000000422014723361714021062 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_SUBSURFACE_H #define META_WAYLAND_SUBSURFACE_H #include "wayland/meta-wayland-actor-surface.h" #define META_TYPE_WAYLAND_SUBSURFACE (meta_wayland_subsurface_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSubsurface, meta_wayland_subsurface, META, WAYLAND_SUBSURFACE, MetaWaylandActorSurface) typedef enum { META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE, META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW } MetaWaylandSubsurfacePlacement; typedef struct { MetaWaylandSubsurfacePlacement placement; MetaWaylandSurface *surface; MetaWaylandSurface *sibling; struct wl_listener surface_destroy_listener; struct wl_listener sibling_destroy_listener; } MetaWaylandSubsurfacePlacementOp; void meta_wayland_subsurface_parent_state_applied (MetaWaylandSubsurface *subsurface); void meta_wayland_subsurface_union_geometry (MetaWaylandSubsurface *subsurface, int parent_x, int parent_y, MetaRectangle *out_geometry); void meta_wayland_subsurface_placement_op_free (MetaWaylandSubsurfacePlacementOp *op); void meta_wayland_subsurfaces_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_SUBSURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-manager.h0000664000175000017500000000427414723361714021614 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_MANAGER_H #define META_WAYLAND_TABLET_MANAGER_H #include #include #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletManager { MetaWaylandCompositor *compositor; struct wl_display *wl_display; struct wl_list resource_list; GHashTable *seats; }; void meta_wayland_tablet_manager_init (MetaWaylandCompositor *compositor); void meta_wayland_tablet_manager_free (MetaWaylandTabletManager *tablet_manager); gboolean meta_wayland_tablet_manager_consumes_event (MetaWaylandTabletManager *manager, const ClutterEvent *event); void meta_wayland_tablet_manager_update (MetaWaylandTabletManager *manager, const ClutterEvent *event); gboolean meta_wayland_tablet_manager_handle_event (MetaWaylandTabletManager *manager, const ClutterEvent *event); MetaWaylandTabletSeat * meta_wayland_tablet_manager_ensure_seat (MetaWaylandTabletManager *manager, MetaWaylandSeat *seat); void meta_wayland_tablet_manager_update_cursor_position (MetaWaylandTabletManager *manager, const ClutterEvent *event); #endif /* META_WAYLAND_TABLET_MANAGER_H */ muffin-6.4.1/src/wayland/meta-wayland-cursor-surface.c0000664000175000017500000003146714723361714021673 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-cursor-surface.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "cogl/cogl-wayland-server.h" #include "cogl/cogl.h" #include "core/boxes-private.h" #include "wayland/meta-cursor-sprite-wayland.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-xwayland.h" typedef struct _MetaWaylandCursorSurfacePrivate MetaWaylandCursorSurfacePrivate; struct _MetaWaylandCursorSurfacePrivate { int hot_x; int hot_y; MetaCursorSpriteWayland *cursor_sprite; MetaCursorRenderer *cursor_renderer; MetaWaylandBuffer *buffer; struct wl_list frame_callbacks; gulong cursor_painted_handler_id; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandCursorSurface, meta_wayland_cursor_surface, META_TYPE_WAYLAND_SURFACE_ROLE) static void update_cursor_sprite_texture (MetaWaylandCursorSurface *cursor_surface) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (cursor_surface)); MetaCursorSprite *cursor_sprite = META_CURSOR_SPRITE (priv->cursor_sprite); CoglTexture *texture; if (!priv->cursor_renderer) return; texture = meta_wayland_surface_get_texture (surface); if (texture) { meta_cursor_sprite_set_texture (cursor_sprite, texture, priv->hot_x * surface->scale, priv->hot_y * surface->scale); } else { meta_cursor_sprite_set_texture (cursor_sprite, NULL, 0, 0); } meta_cursor_renderer_force_update (priv->cursor_renderer); } static void cursor_sprite_prepare_at (MetaCursorSprite *cursor_sprite, int x, int y, MetaWaylandCursorSurface *cursor_surface) { MetaWaylandSurfaceRole *role = META_WAYLAND_SURFACE_ROLE (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role); if (!meta_xwayland_is_xwayland_surface (surface)) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaLogicalMonitor *logical_monitor; logical_monitor = meta_monitor_manager_get_logical_monitor_at (monitor_manager, x, y); if (logical_monitor) { float texture_scale; if (meta_is_stage_views_scaled ()) texture_scale = 1.0 / surface->scale; else texture_scale = (meta_logical_monitor_get_scale (logical_monitor) / surface->scale); meta_cursor_sprite_set_texture_scale (cursor_sprite, texture_scale); } } meta_wayland_surface_update_outputs (surface); } static void meta_wayland_cursor_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface_role); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); wl_list_insert_list (&priv->frame_callbacks, &surface->unassigned.pending_frame_callback_list); wl_list_init (&surface->unassigned.pending_frame_callback_list); } static void meta_wayland_cursor_surface_pre_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface_role); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (pending->newly_attached && priv->buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&priv->buffer); } } static void meta_wayland_cursor_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface_role); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); if (pending->newly_attached) { g_set_object (&priv->buffer, buffer); if (priv->buffer) meta_wayland_surface_ref_buffer_use_count (surface); } wl_list_insert_list (&priv->frame_callbacks, &pending->frame_callback_list); wl_list_init (&pending->frame_callback_list); if (pending->newly_attached && ((!cairo_region_is_empty (pending->surface_damage) || !cairo_region_is_empty (pending->buffer_damage)) || !priv->buffer)) update_cursor_sprite_texture (META_WAYLAND_CURSOR_SURFACE (surface_role)); } static gboolean meta_wayland_cursor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *role, MetaLogicalMonitor *logical_monitor) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (role); MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); graphene_point_t point; graphene_rect_t logical_monitor_rect; if (!priv->cursor_renderer) return FALSE; logical_monitor_rect = meta_rectangle_to_graphene_rect (&logical_monitor->rect); point = meta_cursor_renderer_get_position (priv->cursor_renderer); return graphene_rect_contains_point (&logical_monitor_rect, &point); } static void meta_wayland_cursor_surface_dispose (GObject *object) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (object); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (object)); MetaWaylandFrameCallback *cb, *next; wl_list_for_each_safe (cb, next, &priv->frame_callbacks, link) wl_resource_destroy (cb->resource); g_signal_handlers_disconnect_by_func (priv->cursor_sprite, cursor_sprite_prepare_at, cursor_surface); g_clear_object (&priv->cursor_renderer); g_clear_object (&priv->cursor_sprite); if (priv->buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&priv->buffer); } G_OBJECT_CLASS (meta_wayland_cursor_surface_parent_class)->dispose (object); } static void meta_wayland_cursor_surface_constructed (GObject *object) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (object); MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (cursor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandBuffer *buffer; buffer = meta_wayland_surface_get_buffer (surface); g_warn_if_fail (!buffer || buffer->resource); if (buffer && buffer->resource) { g_set_object (&priv->buffer, buffer); meta_wayland_surface_ref_buffer_use_count (surface); } priv->cursor_sprite = meta_cursor_sprite_wayland_new (surface); g_signal_connect_object (priv->cursor_sprite, "prepare-at", G_CALLBACK (cursor_sprite_prepare_at), cursor_surface, 0); } static void meta_wayland_cursor_surface_init (MetaWaylandCursorSurface *role) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (role); wl_list_init (&priv->frame_callbacks); } static void meta_wayland_cursor_surface_class_init (MetaWaylandCursorSurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); surface_role_class->assigned = meta_wayland_cursor_surface_assigned; surface_role_class->pre_apply_state = meta_wayland_cursor_surface_pre_apply_state; surface_role_class->apply_state = meta_wayland_cursor_surface_apply_state; surface_role_class->is_on_logical_monitor = meta_wayland_cursor_surface_is_on_logical_monitor; object_class->constructed = meta_wayland_cursor_surface_constructed; object_class->dispose = meta_wayland_cursor_surface_dispose; } MetaCursorSprite * meta_wayland_cursor_surface_get_sprite (MetaWaylandCursorSurface *cursor_surface) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); return META_CURSOR_SPRITE (priv->cursor_sprite); } void meta_wayland_cursor_surface_set_hotspot (MetaWaylandCursorSurface *cursor_surface, int hotspot_x, int hotspot_y) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); if (priv->hot_x == hotspot_x && priv->hot_y == hotspot_y) return; priv->hot_x = hotspot_x; priv->hot_y = hotspot_y; update_cursor_sprite_texture (cursor_surface); } void meta_wayland_cursor_surface_get_hotspot (MetaWaylandCursorSurface *cursor_surface, int *hotspot_x, int *hotspot_y) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); if (hotspot_x) *hotspot_x = priv->hot_x; if (hotspot_y) *hotspot_y = priv->hot_y; } static void on_cursor_painted (MetaCursorRenderer *renderer, MetaCursorSprite *displayed_sprite, MetaWaylandCursorSurface *cursor_surface) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); guint32 time = (guint32) (g_get_monotonic_time () / 1000); if (displayed_sprite != META_CURSOR_SPRITE (priv->cursor_sprite)) return; while (!wl_list_empty (&priv->frame_callbacks)) { MetaWaylandFrameCallback *callback = wl_container_of (priv->frame_callbacks.next, callback, link); wl_callback_send_done (callback->resource, time); wl_resource_destroy (callback->resource); } } void meta_wayland_cursor_surface_set_renderer (MetaWaylandCursorSurface *cursor_surface, MetaCursorRenderer *renderer) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); if (priv->cursor_renderer == renderer) return; if (priv->cursor_renderer) { g_clear_signal_handler (&priv->cursor_painted_handler_id, priv->cursor_renderer); g_object_unref (priv->cursor_renderer); } if (renderer) { priv->cursor_painted_handler_id = g_signal_connect_object (renderer, "cursor-painted", G_CALLBACK (on_cursor_painted), cursor_surface, 0); g_object_ref (renderer); } priv->cursor_renderer = renderer; update_cursor_sprite_texture (cursor_surface); } MetaCursorRenderer * meta_wayland_cursor_surface_get_renderer (MetaWaylandCursorSurface *cursor_surface) { MetaWaylandCursorSurfacePrivate *priv = meta_wayland_cursor_surface_get_instance_private (cursor_surface); return priv->cursor_renderer; } muffin-6.4.1/src/wayland/meta-wayland-data-device-primary-legacy.h0000664000175000017500000000426714723361714024024 0ustar fabiofabio/* * Copyright © 2008 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_DEVICE_PRIMARY_LEGACY_H #define META_WAYLAND_DATA_DEVICE_PRIMARY_LEGACY_H #include #include #include "clutter/clutter.h" #include "meta/meta-selection-source.h" #include "wayland/meta-wayland-data-offer.h" #include "wayland/meta-wayland-data-source.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandDataDevicePrimaryLegacy { uint32_t serial; MetaWaylandDataSource *data_source; struct wl_list resource_list; struct wl_list focus_resource_list; struct wl_client *focus_client; guint selection_owner_signal_id; MetaSelectionSource *owner; }; void meta_wayland_data_device_primary_legacy_manager_init (MetaWaylandCompositor *compositor); void meta_wayland_data_device_primary_legacy_init (MetaWaylandDataDevicePrimaryLegacy *data_device); void meta_wayland_data_device_primary_legacy_set_keyboard_focus (MetaWaylandDataDevicePrimaryLegacy *data_device); #endif /* META_WAYLAND_DATA_DEVICE_PRIMARY_LEGACY_H */ muffin-6.4.1/src/wayland/meta-window-xwayland.h0000664000175000017500000000205614723361714020424 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_WINDOW_XWAYLAND_H #define META_WINDOW_XWAYLAND_H #include "meta/window.h" #include "x11/window-x11.h" #include "x11/window-x11-private.h" G_BEGIN_DECLS #define META_TYPE_WINDOW_XWAYLAND (meta_window_xwayland_get_type()) G_DECLARE_FINAL_TYPE (MetaWindowXwayland, meta_window_xwayland, META, WINDOW_XWAYLAND, MetaWindowX11) G_END_DECLS #endif muffin-6.4.1/src/wayland/meta-wayland-buffer.c0000664000175000017500000004575514723361714020206 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Endless Mobile * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ /** * SECTION:meta-wayland-buffer * @title: MetaWaylandBuffer * @short_description: A wrapper for wayland buffers * * #MetaWaylandBuffer is a general wrapper around wl_buffer, the basic way of * passing rendered data from Wayland clients to the compositor. Note that a * buffer can be backed by several types of memory, as specified by * #MetaWaylandBufferType. */ /** * MetaWaylandBufferType: * @META_WAYLAND_BUFFER_TYPE_UNKNOWN: Unknown type. * @META_WAYLAND_BUFFER_TYPE_SHM: wl_buffer backed by shared memory * @META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: wl_buffer backed by an EGLImage * @META_WAYLAND_BUFFER_TYPE_EGL_STREAM: wl_buffer backed by an EGLStream (NVIDIA-specific) * @META_WAYLAND_BUFFER_TYPE_DMA_BUF: wl_buffer backed by a Linux DMA-BUF * * Specifies the backing memory for a #MetaWaylandBuffer. Depending on the type * of buffer, this will lead to different handling for the compositor. For * example, a shared-memory buffer will still need to be uploaded to the GPU. */ #include "config.h" #include "wayland/meta-wayland-buffer.h" #include #include "backends/meta-backend-private.h" #include "clutter/clutter.h" #include "cogl/cogl-egl.h" #include "meta/util.h" #include "wayland/meta-wayland-dma-buf.h" #ifndef DRM_FORMAT_MOD_INVALID #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) #endif enum { RESOURCE_DESTROYED, LAST_SIGNAL }; guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandBuffer, meta_wayland_buffer, G_TYPE_OBJECT); static void meta_wayland_buffer_destroy_handler (struct wl_listener *listener, void *data) { MetaWaylandBuffer *buffer = wl_container_of (listener, buffer, destroy_listener); buffer->resource = NULL; g_signal_emit (buffer, signals[RESOURCE_DESTROYED], 0); g_object_unref (buffer); } MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource) { MetaWaylandBuffer *buffer; struct wl_listener *listener; listener = wl_resource_get_destroy_listener (resource, meta_wayland_buffer_destroy_handler); if (listener) { buffer = wl_container_of (listener, buffer, destroy_listener); } else { buffer = g_object_new (META_TYPE_WAYLAND_BUFFER, NULL); buffer->resource = resource; buffer->destroy_listener.notify = meta_wayland_buffer_destroy_handler; wl_resource_add_destroy_listener (resource, &buffer->destroy_listener); } return buffer; } struct wl_resource * meta_wayland_buffer_get_resource (MetaWaylandBuffer *buffer) { return buffer->resource; } gboolean meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer) { return buffer->type != META_WAYLAND_BUFFER_TYPE_UNKNOWN; } gboolean meta_wayland_buffer_realize (MetaWaylandBuffer *buffer) { EGLint format; MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); #ifdef HAVE_WAYLAND_EGLSTREAM MetaWaylandEglStream *stream; #endif MetaWaylandDmaBufBuffer *dma_buf; if (wl_shm_buffer_get (buffer->resource) != NULL) { buffer->type = META_WAYLAND_BUFFER_TYPE_SHM; return TRUE; } #ifdef HAVE_WAYLAND_EGLSTREAM stream = meta_wayland_egl_stream_new (buffer, NULL); if (stream) { CoglTexture2D *texture; texture = meta_wayland_egl_stream_create_texture (stream, NULL); if (!texture) return FALSE; buffer->egl_stream.stream = stream; buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_STREAM; buffer->egl_stream.texture = COGL_TEXTURE (texture); buffer->is_y_inverted = meta_wayland_egl_stream_is_y_inverted (stream); return TRUE; } #endif /* HAVE_WAYLAND_EGLSTREAM */ if (meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_TEXTURE_FORMAT, &format, NULL)) { buffer->type = META_WAYLAND_BUFFER_TYPE_EGL_IMAGE; return TRUE; } dma_buf = meta_wayland_dma_buf_from_buffer (buffer); if (dma_buf) { buffer->dma_buf.dma_buf = dma_buf; buffer->type = META_WAYLAND_BUFFER_TYPE_DMA_BUF; return TRUE; } return FALSE; } static void shm_buffer_get_cogl_pixel_format (struct wl_shm_buffer *shm_buffer, CoglPixelFormat *format_out, CoglTextureComponents *components_out) { CoglPixelFormat format; CoglTextureComponents components = COGL_TEXTURE_COMPONENTS_RGBA; switch (wl_shm_buffer_get_format (shm_buffer)) { #if G_BYTE_ORDER == G_BIG_ENDIAN case WL_SHM_FORMAT_ARGB8888: format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; break; case WL_SHM_FORMAT_XRGB8888: format = COGL_PIXEL_FORMAT_ARGB_8888; components = COGL_TEXTURE_COMPONENTS_RGB; break; #elif G_BYTE_ORDER == G_LITTLE_ENDIAN case WL_SHM_FORMAT_ARGB8888: format = COGL_PIXEL_FORMAT_BGRA_8888_PRE; break; case WL_SHM_FORMAT_XRGB8888: format = COGL_PIXEL_FORMAT_BGRA_8888; components = COGL_TEXTURE_COMPONENTS_RGB; break; #endif default: g_warn_if_reached (); format = COGL_PIXEL_FORMAT_ARGB_8888; } if (format_out) *format_out = format; if (components_out) *components_out = components; } static gboolean shm_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); struct wl_shm_buffer *shm_buffer; int stride, width, height; CoglPixelFormat format; CoglTextureComponents components; CoglBitmap *bitmap; CoglTexture *new_texture; shm_buffer = wl_shm_buffer_get (buffer->resource); stride = wl_shm_buffer_get_stride (shm_buffer); width = wl_shm_buffer_get_width (shm_buffer); height = wl_shm_buffer_get_height (shm_buffer); shm_buffer_get_cogl_pixel_format (shm_buffer, &format, &components); if (*texture && cogl_texture_get_width (*texture) == width && cogl_texture_get_height (*texture) == height && cogl_texture_get_components (*texture) == components && _cogl_texture_get_format (*texture) == format) { buffer->is_y_inverted = TRUE; return TRUE; } cogl_clear_object (texture); wl_shm_buffer_begin_access (shm_buffer); bitmap = cogl_bitmap_new_for_data (cogl_context, width, height, format, stride, wl_shm_buffer_get_data (shm_buffer)); new_texture = COGL_TEXTURE (cogl_texture_2d_new_from_bitmap (bitmap)); cogl_texture_set_components (new_texture, components); if (!cogl_texture_allocate (new_texture, error)) { g_clear_pointer (&new_texture, cogl_object_unref); if (g_error_matches (*error, COGL_TEXTURE_ERROR, COGL_TEXTURE_ERROR_SIZE)) { CoglTexture2DSliced *texture_sliced; g_clear_error (error); texture_sliced = cogl_texture_2d_sliced_new_from_bitmap (bitmap, COGL_TEXTURE_MAX_WASTE); new_texture = COGL_TEXTURE (texture_sliced); cogl_texture_set_components (new_texture, components); if (!cogl_texture_allocate (new_texture, error)) g_clear_pointer (&new_texture, cogl_object_unref); } } cogl_object_unref (bitmap); wl_shm_buffer_end_access (shm_buffer); if (!new_texture) return FALSE; *texture = new_texture; buffer->is_y_inverted = TRUE; return TRUE; } static gboolean egl_image_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); int format, width, height, y_inverted; CoglPixelFormat cogl_format; EGLImageKHR egl_image; CoglEglImageFlags flags; CoglTexture2D *texture_2d; if (buffer->egl_image.texture) { cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_image.texture); return TRUE; } if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_TEXTURE_FORMAT, &format, error)) return FALSE; if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_WIDTH, &width, error)) return FALSE; if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_HEIGHT, &height, error)) return FALSE; if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_WAYLAND_Y_INVERTED_WL, &y_inverted, NULL)) y_inverted = EGL_TRUE; switch (format) { case EGL_TEXTURE_RGB: cogl_format = COGL_PIXEL_FORMAT_RGB_888; break; case EGL_TEXTURE_RGBA: cogl_format = COGL_PIXEL_FORMAT_RGBA_8888_PRE; break; default: g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported buffer format %d", format); return FALSE; } /* The WL_bind_wayland_display spec states that EGL_NO_CONTEXT is to be used * in conjunction with the EGL_WAYLAND_BUFFER_WL target. */ egl_image = meta_egl_create_image (egl, egl_display, EGL_NO_CONTEXT, EGL_WAYLAND_BUFFER_WL, buffer->resource, NULL, error); if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; flags = COGL_EGL_IMAGE_FLAG_NONE; texture_2d = cogl_egl_texture_2d_new_from_image (cogl_context, width, height, cogl_format, egl_image, flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!texture_2d) return FALSE; buffer->egl_image.texture = COGL_TEXTURE (texture_2d); buffer->is_y_inverted = !!y_inverted; cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_image.texture); return TRUE; } #ifdef HAVE_WAYLAND_EGLSTREAM static gboolean egl_stream_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { MetaWaylandEglStream *stream = buffer->egl_stream.stream; g_assert (stream); if (!meta_wayland_egl_stream_attach (stream, error)) return FALSE; cogl_clear_object (texture); *texture = cogl_object_ref (buffer->egl_stream.texture); return TRUE; } #endif /* HAVE_WAYLAND_EGLSTREAM */ /** * meta_wayland_buffer_attach: * @buffer: a pointer to a #MetaWaylandBuffer * @texture: (inout) (transfer full): a #CoglTexture representing the surface * content * @error: return location for error or %NULL * * This function should be passed a pointer to the texture used to draw the * surface content. The texture will either be replaced by a new texture, or * stay the same, in which case, it may later be updated with new content when * processing damage. The new texture might be newly created, or it may be a * reference to an already existing one. * * If replaced, the old texture will have its reference count decreased by one, * potentially freeing it. When a new texture is set, the caller (i.e. the * surface) will be the owner of one reference count. It must free it, either * using g_object_unref() or have it updated again using * meta_wayland_buffer_attach(), which also might free it, as described above. */ gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { g_return_val_if_fail (buffer->resource, FALSE); if (!meta_wayland_buffer_is_realized (buffer)) { /* The buffer should have been realized at surface commit time */ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown buffer type"); return FALSE; } switch (buffer->type) { case META_WAYLAND_BUFFER_TYPE_SHM: return shm_buffer_attach (buffer, texture, error); case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: return egl_image_buffer_attach (buffer, texture, error); #ifdef HAVE_WAYLAND_EGLSTREAM case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: return egl_stream_buffer_attach (buffer, texture, error); #endif case META_WAYLAND_BUFFER_TYPE_DMA_BUF: return meta_wayland_dma_buf_buffer_attach (buffer, texture, error); case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_assert_not_reached (); return FALSE; } g_assert_not_reached (); return FALSE; } /** * meta_wayland_buffer_create_snippet: * @buffer: A #MetaWaylandBuffer object * * If needed, this method creates a #CoglSnippet to make sure the buffer can be * dealt with appropriately in a #CoglPipeline that renders it. * * Returns: (transfer full) (nullable): A new #CoglSnippet, or %NULL. */ CoglSnippet * meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer) { #ifdef HAVE_WAYLAND_EGLSTREAM if (!buffer->egl_stream.stream) return NULL; return meta_wayland_egl_stream_create_snippet (buffer->egl_stream.stream); #else return NULL; #endif /* HAVE_WAYLAND_EGLSTREAM */ } gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer) { return buffer->is_y_inverted; } static gboolean process_shm_buffer_damage (MetaWaylandBuffer *buffer, CoglTexture *texture, cairo_region_t *region, GError **error) { struct wl_shm_buffer *shm_buffer; int i, n_rectangles; gboolean set_texture_failed = FALSE; CoglPixelFormat format; n_rectangles = cairo_region_num_rectangles (region); shm_buffer = wl_shm_buffer_get (buffer->resource); shm_buffer_get_cogl_pixel_format (shm_buffer, &format, NULL); g_return_val_if_fail (cogl_pixel_format_get_n_planes (format) == 1, FALSE); wl_shm_buffer_begin_access (shm_buffer); for (i = 0; i < n_rectangles; i++) { const uint8_t *data = wl_shm_buffer_get_data (shm_buffer); int32_t stride = wl_shm_buffer_get_stride (shm_buffer); cairo_rectangle_int_t rect; int bpp; bpp = cogl_pixel_format_get_bytes_per_pixel (format, 0); cairo_region_get_rectangle (region, i, &rect); if (!_cogl_texture_set_region (texture, rect.width, rect.height, format, stride, data + rect.x * bpp + rect.y * stride, rect.x, rect.y, 0, error)) { set_texture_failed = TRUE; break; } } wl_shm_buffer_end_access (shm_buffer); return !set_texture_failed; } void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, CoglTexture *texture, cairo_region_t *region) { gboolean res = FALSE; GError *error = NULL; g_return_if_fail (buffer->resource); switch (buffer->type) { case META_WAYLAND_BUFFER_TYPE_SHM: res = process_shm_buffer_damage (buffer, texture, region, &error); break; case META_WAYLAND_BUFFER_TYPE_EGL_IMAGE: #ifdef HAVE_WAYLAND_EGLSTREAM case META_WAYLAND_BUFFER_TYPE_EGL_STREAM: #endif case META_WAYLAND_BUFFER_TYPE_DMA_BUF: res = TRUE; break; case META_WAYLAND_BUFFER_TYPE_UNKNOWN: g_set_error (&error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown buffer type"); res = FALSE; break; } if (!res) { g_warning ("Failed to process Wayland buffer damage: %s", error->message); g_error_free (error); } } static void meta_wayland_buffer_finalize (GObject *object) { MetaWaylandBuffer *buffer = META_WAYLAND_BUFFER (object); g_clear_pointer (&buffer->egl_image.texture, cogl_object_unref); #ifdef HAVE_WAYLAND_EGLSTREAM g_clear_pointer (&buffer->egl_stream.texture, cogl_object_unref); g_clear_object (&buffer->egl_stream.stream); #endif g_clear_pointer (&buffer->dma_buf.texture, cogl_object_unref); g_clear_object (&buffer->dma_buf.dma_buf); G_OBJECT_CLASS (meta_wayland_buffer_parent_class)->finalize (object); } static void meta_wayland_buffer_init (MetaWaylandBuffer *buffer) { } static void meta_wayland_buffer_class_init (MetaWaylandBufferClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_buffer_finalize; /** * MetaWaylandBuffer::resource-destroyed: * * Called when the underlying wl_resource was destroyed. */ signals[RESOURCE_DESTROYED] = g_signal_new ("resource-destroyed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/wayland/meta-wayland-private.h0000664000175000017500000000476214723361714020405 0ustar fabiofabio/* * Copyright (C) 2012 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_PRIVATE_H #define META_WAYLAND_PRIVATE_H #include #include #include "clutter/clutter.h" #include "core/window-private.h" #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-pointer-gestures.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-tablet-manager.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland.h" typedef struct _MetaXWaylandDnd MetaXWaylandDnd; typedef struct { struct wl_list link; struct wl_resource *resource; MetaWaylandSurface *surface; } MetaWaylandFrameCallback; typedef struct { int display_index; char *lock_file; int abstract_fd; int unix_fd; char *name; } MetaXWaylandConnection; typedef struct { MetaXWaylandConnection private_connection; MetaXWaylandConnection public_connection; guint xserver_grace_period_id; struct wl_display *wayland_display; struct wl_client *client; struct wl_resource *xserver_resource; char *auth_file; GCancellable *xserver_died_cancellable; GSubprocess *proc; GList *x11_windows; MetaXWaylandDnd *dnd; } MetaXWaylandManager; struct _MetaWaylandCompositor { GObject parent; MetaBackend *backend; struct wl_display *wayland_display; char *display_name; GHashTable *outputs; GList *frame_callback_surfaces; MetaXWaylandManager xwayland_manager; MetaWaylandSeat *seat; MetaWaylandTabletManager *tablet_manager; GHashTable *scheduled_surface_associations; }; #define META_TYPE_WAYLAND_COMPOSITOR (meta_wayland_compositor_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandCompositor, meta_wayland_compositor, META, WAYLAND_COMPOSITOR, GObject) #endif /* META_WAYLAND_PRIVATE_H */ muffin-6.4.1/src/wayland/meta-wayland-types.h0000664000175000017500000000555114723361714020074 0ustar fabiofabio/* * Copyright (C) 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_TYPES_H #define META_WAYLAND_TYPES_H typedef struct _MetaWaylandCompositor MetaWaylandCompositor; typedef struct _MetaWaylandSeat MetaWaylandSeat; typedef struct _MetaWaylandInputDevice MetaWaylandInputDevice; typedef struct _MetaWaylandPointer MetaWaylandPointer; typedef struct _MetaWaylandPointerGrab MetaWaylandPointerGrab; typedef struct _MetaWaylandPointerGrabInterface MetaWaylandPointerGrabInterface; typedef struct _MetaWaylandPopupGrab MetaWaylandPopupGrab; typedef struct _MetaWaylandPopup MetaWaylandPopup; typedef struct _MetaWaylandPopupSurface MetaWaylandPopupSurface; typedef struct _MetaWaylandKeyboard MetaWaylandKeyboard; typedef struct _MetaWaylandKeyboardGrab MetaWaylandKeyboardGrab; typedef struct _MetaWaylandKeyboardGrabInterface MetaWaylandKeyboardGrabInterface; typedef struct _MetaWaylandTouch MetaWaylandTouch; typedef struct _MetaWaylandDragDestFuncs MetaWaylandDragDestFuncs; typedef struct _MetaWaylandDataOffer MetaWaylandDataOffer; typedef struct _MetaWaylandDataDevice MetaWaylandDataDevice; typedef struct _MetaWaylandDataDevicePrimary MetaWaylandDataDevicePrimary; typedef struct _MetaWaylandDataDevicePrimaryLegacy MetaWaylandDataDevicePrimaryLegacy; typedef struct _MetaWaylandTabletManager MetaWaylandTabletManager; typedef struct _MetaWaylandTabletSeat MetaWaylandTabletSeat; typedef struct _MetaWaylandTabletTool MetaWaylandTabletTool; typedef struct _MetaWaylandTablet MetaWaylandTablet; typedef struct _MetaWaylandTabletPad MetaWaylandTabletPad; typedef struct _MetaWaylandTabletPadGroup MetaWaylandTabletPadGroup; typedef struct _MetaWaylandTabletPadStrip MetaWaylandTabletPadStrip; typedef struct _MetaWaylandTabletPadRing MetaWaylandTabletPadRing; typedef struct _MetaWaylandBuffer MetaWaylandBuffer; typedef struct _MetaWaylandRegion MetaWaylandRegion; typedef struct _MetaWaylandSurface MetaWaylandSurface; typedef struct _MetaWaylandSurfaceState MetaWaylandSurfaceState; typedef struct _MetaWaylandOutput MetaWaylandOutput; typedef struct _MetaWaylandWindowConfiguration MetaWaylandWindowConfiguration; typedef struct _MetaWaylandPointerClient MetaWaylandPointerClient; #endif muffin-6.4.1/src/wayland/meta-wayland-pointer-gesture-pinch.c0000664000175000017500000001315214723361714023152 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-pointer-gesture-pinch.h" #include #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "pointer-gestures-unstable-v1-server-protocol.h" static void handle_pinch_begin (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; MetaWaylandSeat *seat; struct wl_resource *resource; uint32_t serial, fingers; pointer_client = pointer->focus_client; seat = meta_wayland_pointer_get_seat (pointer); serial = wl_display_next_serial (seat->wl_display); fingers = clutter_event_get_touchpad_gesture_finger_count (event); wl_resource_for_each (resource, &pointer_client->pinch_gesture_resources) { zwp_pointer_gesture_pinch_v1_send_begin (resource, serial, clutter_event_get_time (event), pointer->focus_surface->resource, fingers); } } static void handle_pinch_update (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; struct wl_resource *resource; gdouble dx, dy, scale, rotation; pointer_client = pointer->focus_client; clutter_event_get_gesture_motion_delta (event, &dx, &dy); rotation = clutter_event_get_gesture_pinch_angle_delta (event); scale = clutter_event_get_gesture_pinch_scale (event); wl_resource_for_each (resource, &pointer_client->pinch_gesture_resources) { zwp_pointer_gesture_pinch_v1_send_update (resource, clutter_event_get_time (event), wl_fixed_from_double (dx), wl_fixed_from_double (dy), wl_fixed_from_double (scale), wl_fixed_from_double (rotation)); } } static void handle_pinch_end (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; MetaWaylandSeat *seat; struct wl_resource *resource; gboolean cancelled = FALSE; uint32_t serial; pointer_client = pointer->focus_client; seat = meta_wayland_pointer_get_seat (pointer); serial = wl_display_next_serial (seat->wl_display); if (event->touchpad_pinch.phase == CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL) cancelled = TRUE; wl_resource_for_each (resource, &pointer_client->pinch_gesture_resources) { zwp_pointer_gesture_pinch_v1_send_end (resource, serial, clutter_event_get_time (event), cancelled); } } gboolean meta_wayland_pointer_gesture_pinch_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { if (event->type != CLUTTER_TOUCHPAD_PINCH) return FALSE; if (!pointer->focus_client) return FALSE; switch (event->touchpad_pinch.phase) { case CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN: handle_pinch_begin (pointer, event); break; case CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE: handle_pinch_update (pointer, event); break; case CLUTTER_TOUCHPAD_GESTURE_PHASE_END: case CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL: handle_pinch_end (pointer, event); break; default: return FALSE; } return TRUE; } static void pointer_gesture_pinch_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_pointer_gesture_pinch_v1_interface pointer_gesture_pinch_interface = { pointer_gesture_pinch_destroy }; void meta_wayland_pointer_gesture_pinch_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *gestures_resource, uint32_t id) { MetaWaylandPointerClient *pointer_client; struct wl_resource *res; pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); g_return_if_fail (pointer_client != NULL); res = wl_resource_create (client, &zwp_pointer_gesture_pinch_v1_interface, wl_resource_get_version (gestures_resource), id); wl_resource_set_implementation (res, &pointer_gesture_pinch_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); wl_list_insert (&pointer_client->pinch_gesture_resources, wl_resource_get_link (res)); } muffin-6.4.1/src/wayland/meta-wayland-touch.h0000664000175000017500000000571614723361714020055 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2014 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TOUCH_H #define META_WAYLAND_TOUCH_H #include #include #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_TOUCH (meta_wayland_touch_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandTouch, meta_wayland_touch, META, WAYLAND_TOUCH, MetaWaylandInputDevice) typedef struct _MetaWaylandTouchSurface MetaWaylandTouchSurface; typedef struct _MetaWaylandTouchInfo MetaWaylandTouchInfo; struct _MetaWaylandTouch { MetaWaylandInputDevice parent; struct wl_list resource_list; GHashTable *touch_surfaces; /* HT of MetaWaylandSurface->MetaWaylandTouchSurface */ GHashTable *touches; /* HT of sequence->MetaWaylandTouchInfo */ guint64 frame_slots; }; void meta_wayland_touch_enable (MetaWaylandTouch *touch); void meta_wayland_touch_disable (MetaWaylandTouch *touch); void meta_wayland_touch_update (MetaWaylandTouch *touch, const ClutterEvent *event); gboolean meta_wayland_touch_handle_event (MetaWaylandTouch *touch, const ClutterEvent *event); void meta_wayland_touch_create_new_resource (MetaWaylandTouch *touch, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); void meta_wayland_touch_cancel (MetaWaylandTouch *touch); ClutterEventSequence * meta_wayland_touch_find_grab_sequence (MetaWaylandTouch *touch, MetaWaylandSurface *surface, uint32_t serial); gboolean meta_wayland_touch_get_press_coords (MetaWaylandTouch *touch, ClutterEventSequence *sequence, gfloat *x, gfloat *y); gboolean meta_wayland_touch_can_popup (MetaWaylandTouch *touch, uint32_t serial); #endif /* META_WAYLAND_TOUCH_H */ muffin-6.4.1/src/wayland/meta-wayland-data-device-primary.c0000664000175000017500000002746014723361714022555 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* The file is based on src/data-device.c from Weston */ #include "config.h" #include "wayland/meta-wayland-data-device-primary.h" #include "compositor/meta-dnd-actor-private.h" #include "meta/meta-selection-source-memory.h" #include "wayland/meta-selection-source-wayland-private.h" #include "wayland/meta-wayland-data-offer-primary.h" #include "wayland/meta-wayland-data-source-primary.h" #include "wayland/meta-wayland-dnd-surface.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "primary-selection-unstable-v1-server-protocol.h" static struct wl_resource * create_and_send_primary_offer (MetaWaylandDataDevicePrimary *data_device, struct wl_resource *target); static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void default_destructor (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void set_selection_source (MetaWaylandDataDevicePrimary *data_device, MetaSelectionSource *selection_source) { MetaDisplay *display = meta_get_display (); meta_selection_set_owner (meta_display_get_selection (display), META_SELECTION_PRIMARY, selection_source); g_set_object (&data_device->owner, selection_source); } static void unset_selection_source (MetaWaylandDataDevicePrimary *data_device) { MetaDisplay *display = meta_get_display (); if (!data_device->owner) return; meta_selection_unset_owner (meta_display_get_selection (display), META_SELECTION_PRIMARY, data_device->owner); g_clear_object (&data_device->owner); } static void primary_source_destroyed (gpointer data, GObject *object_was_here) { MetaWaylandDataDevicePrimary *data_device = data; data_device->data_source = NULL; unset_selection_source (data_device); } static void meta_wayland_data_device_primary_set_selection (MetaWaylandDataDevicePrimary *data_device, MetaWaylandDataSource *source, uint32_t serial) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_data_device); MetaSelectionSource *selection_source; g_assert (!source || META_IS_WAYLAND_DATA_SOURCE_PRIMARY (source)); if (data_device->data_source && data_device->serial - serial < UINT32_MAX / 2) return; if (data_device->data_source) { g_object_weak_unref (G_OBJECT (data_device->data_source), primary_source_destroyed, data_device); } data_device->data_source = source; data_device->serial = serial; if (source) { meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), primary_source_destroyed, data_device); selection_source = meta_selection_source_wayland_new (source); } else { selection_source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); } set_selection_source (data_device, selection_source); g_object_unref (selection_source); } static void primary_device_set_selection (struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial) { MetaWaylandDataDevicePrimary *data_device = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_data_device); MetaWaylandDataSource *source = NULL; if (source_resource) source = wl_resource_get_user_data (source_resource); if (wl_resource_get_client (resource) != meta_wayland_keyboard_get_focus_client (seat->keyboard)) return; meta_wayland_data_device_primary_set_selection (data_device, source, serial); } static const struct zwp_primary_selection_device_v1_interface primary_device_interface = { primary_device_set_selection, default_destructor, }; static void owner_changed_cb (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *new_owner, MetaWaylandDataDevicePrimary *data_device) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandSeat *seat = compositor->seat; struct wl_resource *data_device_resource; struct wl_client *focus_client; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (!focus_client) return; if (selection_type == META_SELECTION_PRIMARY) { wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer = NULL; if (new_owner) { offer = create_and_send_primary_offer (data_device, data_device_resource); } zwp_primary_selection_device_v1_send_selection (data_device_resource, offer); } } } static void ensure_owners_changed_handler_connected (MetaWaylandDataDevicePrimary *data_device) { if (data_device->selection_owner_signal_id != 0) return; data_device->selection_owner_signal_id = g_signal_connect (meta_display_get_selection (meta_get_display ()), "owner-changed", G_CALLBACK (owner_changed_cb), data_device); } static void primary_device_manager_create_source (struct wl_client *client, struct wl_resource *manager_resource, guint32 id) { struct wl_resource *source_resource; source_resource = wl_resource_create (client, &zwp_primary_selection_source_v1_interface, wl_resource_get_version (manager_resource), id); meta_wayland_data_source_primary_new (source_resource); } static void primary_device_manager_get_device (struct wl_client *client, struct wl_resource *manager_resource, guint32 id, struct wl_resource *seat_resource) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); struct wl_resource *cr; cr = wl_resource_create (client, &zwp_primary_selection_device_v1_interface, wl_resource_get_version (manager_resource), id); wl_resource_set_implementation (cr, &primary_device_interface, &seat->primary_data_device, unbind_resource); wl_list_insert (&seat->primary_data_device.resource_list, wl_resource_get_link (cr)); ensure_owners_changed_handler_connected (&seat->primary_data_device); } static const struct zwp_primary_selection_device_manager_v1_interface primary_manager_interface = { primary_device_manager_create_source, primary_device_manager_get_device, default_destructor, }; static void bind_primary_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_primary_selection_device_manager_v1_interface, version, id); wl_resource_set_implementation (resource, &primary_manager_interface, NULL, NULL); } void meta_wayland_data_device_primary_manager_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &zwp_primary_selection_device_manager_v1_interface, 1, NULL, bind_primary_manager) == NULL) g_error ("Could not create data_device"); } void meta_wayland_data_device_primary_init (MetaWaylandDataDevicePrimary *data_device) { wl_list_init (&data_device->resource_list); wl_list_init (&data_device->focus_resource_list); } static struct wl_resource * create_and_send_primary_offer (MetaWaylandDataDevicePrimary *data_device, struct wl_resource *target) { MetaWaylandDataOffer *offer; MetaDisplay *display = meta_get_display (); struct wl_resource *resource; GList *mimetypes, *l; mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (display), META_SELECTION_PRIMARY); if (!mimetypes) return NULL; offer = meta_wayland_data_offer_primary_new (target); resource = meta_wayland_data_offer_get_resource (offer); zwp_primary_selection_device_v1_send_data_offer (target, resource); for (l = mimetypes; l; l = l->next) zwp_primary_selection_offer_v1_send_offer (resource, l->data); g_list_free_full (mimetypes, g_free); return resource; } void meta_wayland_data_device_primary_set_keyboard_focus (MetaWaylandDataDevicePrimary *data_device) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_data_device); struct wl_client *focus_client; struct wl_resource *data_device_resource; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client == data_device->focus_client) return; data_device->focus_client = focus_client; move_resources (&data_device->resource_list, &data_device->focus_resource_list); if (!focus_client) return; move_resources_for_client (&data_device->focus_resource_list, &data_device->resource_list, focus_client); wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer; offer = create_and_send_primary_offer (data_device, data_device_resource); zwp_primary_selection_device_v1_send_selection (data_device_resource, offer); } } muffin-6.4.1/src/wayland/meta-wayland-buffer.h0000664000175000017500000000631014723361714020173 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Endless Mobile * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_BUFFER_H #define META_WAYLAND_BUFFER_H #include #include #include "cogl/cogl.h" #include "wayland/meta-wayland-types.h" #include "wayland/meta-wayland-egl-stream.h" #include "wayland/meta-wayland-dma-buf.h" typedef enum _MetaWaylandBufferType { META_WAYLAND_BUFFER_TYPE_UNKNOWN, META_WAYLAND_BUFFER_TYPE_SHM, META_WAYLAND_BUFFER_TYPE_EGL_IMAGE, #ifdef HAVE_WAYLAND_EGLSTREAM META_WAYLAND_BUFFER_TYPE_EGL_STREAM, #endif META_WAYLAND_BUFFER_TYPE_DMA_BUF, } MetaWaylandBufferType; struct _MetaWaylandBuffer { GObject parent; struct wl_resource *resource; struct wl_listener destroy_listener; gboolean is_y_inverted; MetaWaylandBufferType type; struct { CoglTexture *texture; } egl_image; #ifdef HAVE_WAYLAND_EGLSTREAM struct { MetaWaylandEglStream *stream; CoglTexture *texture; } egl_stream; #endif struct { MetaWaylandDmaBufBuffer *dma_buf; CoglTexture *texture; } dma_buf; }; #define META_TYPE_WAYLAND_BUFFER (meta_wayland_buffer_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandBuffer, meta_wayland_buffer, META, WAYLAND_BUFFER, GObject); MetaWaylandBuffer * meta_wayland_buffer_from_resource (struct wl_resource *resource); struct wl_resource * meta_wayland_buffer_get_resource (MetaWaylandBuffer *buffer); gboolean meta_wayland_buffer_is_realized (MetaWaylandBuffer *buffer); gboolean meta_wayland_buffer_realize (MetaWaylandBuffer *buffer); gboolean meta_wayland_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error); CoglSnippet * meta_wayland_buffer_create_snippet (MetaWaylandBuffer *buffer); gboolean meta_wayland_buffer_is_y_inverted (MetaWaylandBuffer *buffer); void meta_wayland_buffer_process_damage (MetaWaylandBuffer *buffer, CoglTexture *texture, cairo_region_t *region); #endif /* META_WAYLAND_BUFFER_H */ muffin-6.4.1/src/wayland/meta-wayland-pointer.h0000664000175000017500000001356214723361714020411 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef META_WAYLAND_POINTER_H #define META_WAYLAND_POINTER_H #include #include #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-pointer-constraints.h" #include "wayland/meta-wayland-pointer-gesture-pinch.h" #include "wayland/meta-wayland-pointer-gesture-swipe.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_POINTER (meta_wayland_pointer_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandPointer, meta_wayland_pointer, META, WAYLAND_POINTER, MetaWaylandInputDevice) struct _MetaWaylandPointerGrabInterface { void (*focus) (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface); void (*motion) (MetaWaylandPointerGrab *grab, const ClutterEvent *event); void (*button) (MetaWaylandPointerGrab *grab, const ClutterEvent *event); void (*cancel) (MetaWaylandPointerGrab *grab); }; struct _MetaWaylandPointerGrab { const MetaWaylandPointerGrabInterface *interface; MetaWaylandPointer *pointer; }; struct _MetaWaylandPointerClient { struct wl_list pointer_resources; struct wl_list swipe_gesture_resources; struct wl_list pinch_gesture_resources; struct wl_list relative_pointer_resources; }; struct _MetaWaylandPointer { MetaWaylandInputDevice parent; MetaWaylandPointerClient *focus_client; GHashTable *pointer_clients; MetaWaylandSurface *focus_surface; gulong focus_surface_destroyed_handler_id; guint32 focus_serial; guint32 click_serial; MetaWaylandSurface *cursor_surface; gulong cursor_surface_destroy_id; MetaWaylandPointerGrab *grab; MetaWaylandPointerGrab default_grab; guint32 grab_button; guint32 grab_serial; guint32 grab_time; float grab_x, grab_y; ClutterInputDevice *device; MetaWaylandSurface *current; gulong current_surface_destroyed_handler_id; guint32 button_count; }; void meta_wayland_pointer_enable (MetaWaylandPointer *pointer); void meta_wayland_pointer_disable (MetaWaylandPointer *pointer); void meta_wayland_pointer_update (MetaWaylandPointer *pointer, const ClutterEvent *event); gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer); void meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer, MetaWaylandSurface *surface); void meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, MetaWaylandPointerGrab *grab); void meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer); MetaWaylandPopup *meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, MetaWaylandPopupSurface *popup_surface); void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer); void meta_wayland_pointer_repick (MetaWaylandPointer *pointer); void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *x, wl_fixed_t *y); void meta_wayland_pointer_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, uint32_t serial); gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial); MetaWaylandSurface *meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer); MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client); void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource); void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor); MetaWaylandSeat *meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer); void meta_wayland_surface_cursor_update (MetaWaylandSurface *cursor_surface); void meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer); #endif /* META_WAYLAND_POINTER_H */ muffin-6.4.1/src/wayland/meta-wayland-legacy-xdg-shell.c0000664000175000017500000021577414723361714022066 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2012-2013 Intel Corporation * Copyright (C) 2013-2015 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-legacy-xdg-shell.h" #include "backends/meta-logical-monitor.h" #include "core/window-private.h" #include "wayland/meta-wayland-outputs.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-shell-surface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-window-configuration.h" #include "wayland/meta-wayland.h" #include "wayland/meta-window-wayland.h" #include "xdg-shell-unstable-v6-server-protocol.h" enum { ZXDG_SURFACE_V6_PROP_0, ZXDG_SURFACE_V6_PROP_SHELL_CLIENT, ZXDG_SURFACE_V6_PROP_RESOURCE, }; typedef struct _MetaWaylandZxdgShellV6Client { struct wl_resource *resource; GList *surfaces; GList *surface_constructors; } MetaWaylandZxdgShellV6Client; typedef struct _MetaWaylandZxdgPositionerV6 { MetaRectangle anchor_rect; int32_t width; int32_t height; uint32_t gravity; uint32_t anchor; uint32_t constraint_adjustment; int32_t offset_x; int32_t offset_y; } MetaWaylandZxdgPositionerV6; typedef struct _MetaWaylandZxdgSurfaceV6Constructor { MetaWaylandSurface *surface; struct wl_resource *resource; MetaWaylandZxdgShellV6Client *shell_client; } MetaWaylandZxdgSurfaceV6Constructor; typedef struct _MetaWaylandZxdgSurfaceV6Private { struct wl_resource *resource; MetaWaylandZxdgShellV6Client *shell_client; MetaRectangle geometry; guint configure_sent : 1; guint first_buffer_attached : 1; guint has_set_geometry : 1; } MetaWaylandZxdgSurfaceV6Private; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandZxdgSurfaceV6, meta_wayland_zxdg_surface_v6, META_TYPE_WAYLAND_SHELL_SURFACE); struct _MetaWaylandZxdgToplevelV6 { MetaWaylandZxdgSurfaceV6 parent; struct wl_resource *resource; }; G_DEFINE_TYPE (MetaWaylandZxdgToplevelV6, meta_wayland_zxdg_toplevel_v6, META_TYPE_WAYLAND_ZXDG_SURFACE_V6); struct _MetaWaylandZxdgPopupV6 { MetaWaylandZxdgSurfaceV6 parent; struct wl_resource *resource; MetaWaylandSurface *parent_surface; struct wl_listener parent_destroy_listener; MetaWaylandPopup *popup; struct { MetaWaylandSurface *parent_surface; /* * The coordinates/dimensions in the placement rule are in logical pixel * coordinate space, i.e. not scaled given what monitor the popup is on. */ MetaPlacementRule placement_rule; MetaWaylandSeat *grab_seat; uint32_t grab_serial; } setup; }; static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWaylandZxdgPopupV6, meta_wayland_zxdg_popup_v6, META_TYPE_WAYLAND_ZXDG_SURFACE_V6, G_IMPLEMENT_INTERFACE (META_TYPE_WAYLAND_POPUP_SURFACE, popup_surface_iface_init)); static MetaPlacementRule meta_wayland_zxdg_positioner_v6_to_placement (MetaWaylandZxdgPositionerV6 *xdg_positioner); static struct wl_resource * meta_wayland_zxdg_surface_v6_get_shell_resource (MetaWaylandZxdgSurfaceV6 *xdg_surface); static MetaRectangle meta_wayland_zxdg_surface_v6_get_window_geometry (MetaWaylandZxdgSurfaceV6 *xdg_surface); static void meta_wayland_zxdg_surface_v6_send_configure (MetaWaylandZxdgSurfaceV6 *xdg_surface, MetaWaylandWindowConfiguration *configuration); static MetaWaylandSurface * surface_from_xdg_surface_resource (struct wl_resource *resource) { MetaWaylandSurfaceRole *surface_role = wl_resource_get_user_data (resource); return meta_wayland_surface_role_get_surface (surface_role); } static MetaWaylandSurface * surface_from_xdg_toplevel_resource (struct wl_resource *resource) { return surface_from_xdg_surface_resource (resource); } static void zxdg_toplevel_v6_destructor (struct wl_resource *resource) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = wl_resource_get_user_data (resource); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_toplevel); meta_wayland_shell_surface_destroy_window (shell_surface); xdg_toplevel->resource = NULL; } static void zxdg_toplevel_v6_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void zxdg_toplevel_v6_set_parent (struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *transient_for = NULL; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (parent_resource) { MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); transient_for = meta_wayland_surface_get_window (parent_surface); } meta_window_set_transient_for (window, transient_for); } static void zxdg_toplevel_v6_set_title (struct wl_client *client, struct wl_resource *resource, const char *title) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!g_utf8_validate (title, -1, NULL)) title = ""; meta_window_set_title (window, title); } static void zxdg_toplevel_v6_set_app_id (struct wl_client *client, struct wl_resource *resource, const char *app_id) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!g_utf8_validate (app_id, -1, NULL)) app_id = ""; meta_window_set_wm_class (window, app_id, app_id); } static void zxdg_toplevel_v6_show_window_menu (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, int32_t x, int32_t y) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; int monitor_scale; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL)) return; monitor_scale = meta_window_wayland_get_geometry_scale (window); meta_window_show_menu (window, META_WINDOW_MENU_WM, window->buffer_rect.x + (x * monitor_scale), window->buffer_rect.y + (y * monitor_scale)); } static void zxdg_toplevel_v6_move (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; gfloat x, y; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; meta_wayland_surface_begin_grab_op (surface, seat, META_GRAB_OP_MOVING, x, y); } static MetaGrabOp grab_op_for_xdg_toplevel_resize_edge (int edge) { MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_TOP) op |= META_GRAB_OP_WINDOW_DIR_NORTH; if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_BOTTOM) op |= META_GRAB_OP_WINDOW_DIR_SOUTH; if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_LEFT) op |= META_GRAB_OP_WINDOW_DIR_WEST; if (edge & ZXDG_TOPLEVEL_V6_RESIZE_EDGE_RIGHT) op |= META_GRAB_OP_WINDOW_DIR_EAST; if (op == META_GRAB_OP_WINDOW_BASE) { g_warning ("invalid edge: %d", edge); return META_GRAB_OP_NONE; } return op; } static void zxdg_toplevel_v6_resize (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; gfloat x, y; MetaGrabOp grab_op; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; grab_op = grab_op_for_xdg_toplevel_resize_edge (edges); meta_wayland_surface_begin_grab_op (surface, seat, grab_op, x, y); } static void zxdg_toplevel_v6_set_max_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWaylandSurfaceState *pending; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (width < 0 || height < 0) { wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "invalid negative max size requested %i x %i", width, height); return; } pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_max_size = TRUE; pending->new_max_width = width; pending->new_max_height = height; } static void zxdg_toplevel_v6_set_min_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWaylandSurfaceState *pending; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (width < 0 || height < 0) { wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "invalid negative min size requested %i x %i", width, height); return; } pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_min_size = TRUE; pending->new_min_width = width; pending->new_min_height = height; } static void zxdg_toplevel_v6_set_maximized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!window->has_maximize_func) return; meta_window_force_placement (window, TRUE); meta_window_maximize (window, META_MAXIMIZE_BOTH); } static void zxdg_toplevel_v6_unset_maximized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } static void zxdg_toplevel_v6_set_fullscreen (struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (output_resource) { MetaWaylandOutput *output = wl_resource_get_user_data (output_resource); if (output && output->logical_monitor) meta_window_move_to_monitor (window, output->logical_monitor->number); } meta_window_make_fullscreen (window); } static void zxdg_toplevel_v6_unset_fullscreen (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_unmake_fullscreen (window); } static void zxdg_toplevel_v6_set_minimized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_minimize (window); } static const struct zxdg_toplevel_v6_interface meta_wayland_zxdg_toplevel_v6_interface = { zxdg_toplevel_v6_destroy, zxdg_toplevel_v6_set_parent, zxdg_toplevel_v6_set_title, zxdg_toplevel_v6_set_app_id, zxdg_toplevel_v6_show_window_menu, zxdg_toplevel_v6_move, zxdg_toplevel_v6_resize, zxdg_toplevel_v6_set_max_size, zxdg_toplevel_v6_set_min_size, zxdg_toplevel_v6_set_maximized, zxdg_toplevel_v6_unset_maximized, zxdg_toplevel_v6_set_fullscreen, zxdg_toplevel_v6_unset_fullscreen, zxdg_toplevel_v6_set_minimized, }; static void zxdg_popup_v6_destructor (struct wl_resource *resource) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (wl_resource_get_user_data (resource)); if (xdg_popup->parent_surface) { wl_list_remove (&xdg_popup->parent_destroy_listener.link); xdg_popup->parent_surface = NULL; } if (xdg_popup->popup) meta_wayland_popup_dismiss (xdg_popup->popup); xdg_popup->resource = NULL; } static void zxdg_popup_v6_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void zxdg_popup_v6_grab (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (wl_resource_get_user_data (resource)); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *parent_surface; parent_surface = xdg_popup->setup.parent_surface; if (!parent_surface) { wl_resource_post_error (resource, ZXDG_POPUP_V6_ERROR_INVALID_GRAB, "tried to grab after popup was mapped"); return; } xdg_popup->setup.grab_seat = seat; xdg_popup->setup.grab_serial = serial; } static const struct zxdg_popup_v6_interface meta_wayland_zxdg_popup_v6_interface = { zxdg_popup_v6_destroy, zxdg_popup_v6_grab, }; static void handle_popup_parent_destroyed (struct wl_listener *listener, void *data) { MetaWaylandZxdgPopupV6 *xdg_popup = wl_container_of (listener, xdg_popup, parent_destroy_listener); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_popup); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_popup); wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "destroyed popup not top most popup"); xdg_popup->parent_surface = NULL; meta_wayland_shell_surface_destroy_window (shell_surface); } static void add_state_value (struct wl_array *states, enum zxdg_toplevel_v6_state state) { uint32_t *s; s = wl_array_add (states, sizeof *s); *s = state; } static void fill_states (struct wl_array *states, MetaWindow *window) { if (META_WINDOW_MAXIMIZED (window)) add_state_value (states, ZXDG_TOPLEVEL_V6_STATE_MAXIMIZED); if (meta_window_is_fullscreen (window)) add_state_value (states, ZXDG_TOPLEVEL_V6_STATE_FULLSCREEN); if (meta_grab_op_is_resizing (window->display->grab_op)) add_state_value (states, ZXDG_TOPLEVEL_V6_STATE_RESIZING); if (meta_window_appears_focused (window)) add_state_value (states, ZXDG_TOPLEVEL_V6_STATE_ACTIVATED); } static void meta_wayland_zxdg_toplevel_v6_send_configure (MetaWaylandZxdgToplevelV6 *xdg_toplevel, MetaWaylandWindowConfiguration *configuration) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; struct wl_array states; window = meta_wayland_surface_get_window (surface); wl_array_init (&states); fill_states (&states, window); zxdg_toplevel_v6_send_configure (xdg_toplevel->resource, configuration->width / configuration->scale, configuration->height / configuration->scale, &states); wl_array_release (&states); meta_wayland_zxdg_surface_v6_send_configure (xdg_surface, configuration); } static gboolean is_new_size_hints_valid (MetaWindow *window, MetaWaylandSurfaceState *pending) { int new_min_width, new_min_height; int new_max_width, new_max_height; if (pending->has_new_min_size) { new_min_width = pending->new_min_width; new_min_height = pending->new_min_height; } else { meta_window_wayland_get_min_size (window, &new_min_width, &new_min_height); } if (pending->has_new_max_size) { new_max_width = pending->new_max_width; new_max_height = pending->new_max_height; } else { meta_window_wayland_get_max_size (window, &new_max_width, &new_max_height); } /* Zero means unlimited */ return ((new_max_width == 0 || new_min_width <= new_max_width) && (new_max_height == 0 || new_min_height <= new_max_height)); } static void meta_wayland_zxdg_toplevel_v6_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (surface_role); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (xdg_surface); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) { meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); return; } surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_toplevel_v6_parent_class); surface_role_class->apply_state (surface_role, pending); if (!xdg_surface_priv->configure_sent) { MetaWaylandWindowConfiguration *configuration; configuration = meta_wayland_window_configuration_new_empty (); meta_wayland_zxdg_toplevel_v6_send_configure (xdg_toplevel, configuration); meta_wayland_window_configuration_free (configuration); return; } } static void meta_wayland_zxdg_toplevel_v6_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (surface_role); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; MetaRectangle old_geometry; gboolean geometry_changed; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!pending->newly_attached) return; old_geometry = xdg_surface_priv->geometry; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_toplevel_v6_parent_class); surface_role_class->post_apply_state (surface_role, pending); geometry_changed = !meta_rectangle_equal (&old_geometry, &xdg_surface_priv->geometry); if (geometry_changed || pending->has_acked_configure_serial) { MetaRectangle window_geometry; window_geometry = meta_wayland_zxdg_surface_v6_get_window_geometry (xdg_surface); meta_window_wayland_finish_move_resize (window, window_geometry, pending); } else if (pending->dx != 0 || pending->dy != 0) { g_warning ("XXX: Attach-initiated move without a new geometry. " "This is unimplemented right now."); } /* When we get to this point, we ought to have valid size hints */ if (pending->has_new_min_size || pending->has_new_max_size) { if (is_new_size_hints_valid (window, pending)) { if (pending->has_new_min_size) meta_window_wayland_set_min_size (window, pending->new_min_width, pending->new_min_height); if (pending->has_new_max_size) meta_window_wayland_set_max_size (window, pending->new_max_width, pending->new_max_height); meta_window_recalc_features (window); } else { wl_resource_post_error (surface->resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "Invalid min/max size"); } } } static MetaWaylandSurface * meta_wayland_zxdg_toplevel_v6_get_toplevel (MetaWaylandSurfaceRole *surface_role) { return meta_wayland_surface_role_get_surface (surface_role); } static void meta_wayland_zxdg_toplevel_v6_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (shell_surface); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); MetaWaylandZxdgSurfaceV6Private *xdg_surface_priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); if (!xdg_surface_priv->resource) return; if (!xdg_toplevel->resource) return; meta_wayland_zxdg_toplevel_v6_send_configure (xdg_toplevel, configuration); } static void meta_wayland_zxdg_toplevel_v6_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { } static void meta_wayland_zxdg_toplevel_v6_close (MetaWaylandShellSurface *shell_surface) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (shell_surface); zxdg_toplevel_v6_send_close (xdg_toplevel->resource); } static void meta_wayland_zxdg_toplevel_v6_shell_client_destroyed (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (xdg_surface); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandZxdgSurfaceV6Class *xdg_surface_class = META_WAYLAND_ZXDG_SURFACE_V6_CLASS (meta_wayland_zxdg_toplevel_v6_parent_class); xdg_surface_class->shell_client_destroyed (xdg_surface); if (xdg_toplevel->resource) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "xdg_shell of xdg_toplevel@%d was destroyed", wl_resource_get_id (xdg_toplevel->resource)); wl_resource_destroy (xdg_toplevel->resource); } } static void meta_wayland_zxdg_toplevel_v6_finalize (GObject *object) { MetaWaylandZxdgToplevelV6 *xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (object); g_clear_pointer (&xdg_toplevel->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_zxdg_toplevel_v6_parent_class)->finalize (object); } static void meta_wayland_zxdg_toplevel_v6_init (MetaWaylandZxdgToplevelV6 *role) { } static void meta_wayland_zxdg_toplevel_v6_class_init (MetaWaylandZxdgToplevelV6Class *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; MetaWaylandZxdgSurfaceV6Class *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_zxdg_toplevel_v6_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_zxdg_toplevel_v6_apply_state; surface_role_class->post_apply_state = meta_wayland_zxdg_toplevel_v6_post_apply_state; surface_role_class->get_toplevel = meta_wayland_zxdg_toplevel_v6_get_toplevel; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->configure = meta_wayland_zxdg_toplevel_v6_configure; shell_surface_class->managed = meta_wayland_zxdg_toplevel_v6_managed; shell_surface_class->close = meta_wayland_zxdg_toplevel_v6_close; xdg_surface_class = META_WAYLAND_ZXDG_SURFACE_V6_CLASS (klass); xdg_surface_class->shell_client_destroyed = meta_wayland_zxdg_toplevel_v6_shell_client_destroyed; } static void scale_placement_rule (MetaPlacementRule *placement_rule, MetaWaylandSurface *surface) { MetaWindow *window; int geometry_scale; window = meta_wayland_surface_get_window (surface); geometry_scale = meta_window_wayland_get_geometry_scale (window); placement_rule->anchor_rect.x *= geometry_scale; placement_rule->anchor_rect.y *= geometry_scale; placement_rule->anchor_rect.width *= geometry_scale; placement_rule->anchor_rect.height *= geometry_scale; placement_rule->offset_x *= geometry_scale; placement_rule->offset_y *= geometry_scale; placement_rule->width *= geometry_scale; placement_rule->height *= geometry_scale; } static void finish_popup_setup (MetaWaylandZxdgPopupV6 *xdg_popup) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_popup); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_popup); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent_surface; MetaPlacementRule scaled_placement_rule; MetaWaylandSeat *seat; uint32_t serial; MetaDisplay *display = meta_get_display (); MetaWindow *window; parent_surface = xdg_popup->setup.parent_surface; seat = xdg_popup->setup.grab_seat; serial = xdg_popup->setup.grab_serial; xdg_popup->setup.parent_surface = NULL; xdg_popup->setup.grab_seat = NULL; if (!meta_wayland_surface_get_window (parent_surface)) { zxdg_popup_v6_send_popup_done (xdg_popup->resource); return; } if (seat) { MetaWaylandSurface *top_popup; if (!meta_wayland_seat_can_popup (seat, serial)) { zxdg_popup_v6_send_popup_done (xdg_popup->resource); return; } top_popup = meta_wayland_pointer_get_top_popup (seat->pointer); if (top_popup && parent_surface != top_popup) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "parent not top most surface"); return; } } xdg_popup->parent_surface = parent_surface; xdg_popup->parent_destroy_listener.notify = handle_popup_parent_destroyed; wl_resource_add_destroy_listener (parent_surface->resource, &xdg_popup->parent_destroy_listener); window = meta_window_wayland_new (display, surface); meta_wayland_shell_surface_set_window (shell_surface, window); scaled_placement_rule = xdg_popup->setup.placement_rule; scale_placement_rule (&scaled_placement_rule, surface); meta_window_place_with_placement_rule (window, &scaled_placement_rule); if (seat) { MetaWaylandPopupSurface *popup_surface; MetaWaylandPopup *popup; meta_window_focus (window, meta_display_get_current_time (display)); popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role); popup = meta_wayland_pointer_start_popup_grab (seat->pointer, popup_surface); if (popup == NULL) { zxdg_popup_v6_send_popup_done (xdg_popup->resource); meta_wayland_shell_surface_destroy_window (shell_surface); return; } xdg_popup->popup = popup; } else { /* The keyboard focus semantics for non-grabbing zxdg_shell_v6 popups * is pretty undefined. Same applies for subsurfaces, but in practice, * subsurfaces never receive keyboard focus, so it makes sense to * do the same for non-grabbing popups. * * See https://bugzilla.gnome.org/show_bug.cgi?id=771694#c24 */ window->input = FALSE; } } static void meta_wayland_zxdg_popup_v6_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; if (xdg_popup->setup.parent_surface) finish_popup_setup (xdg_popup); surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_popup_v6_parent_class); surface_role_class->apply_state (surface_role, pending); } static void meta_wayland_zxdg_popup_v6_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_popup_v6_parent_class); surface_role_class->post_apply_state (surface_role, pending); if (!pending->newly_attached) return; if (!surface->buffer_ref.buffer) return; if (pending->has_acked_configure_serial) { MetaRectangle window_geometry; window_geometry = meta_wayland_zxdg_surface_v6_get_window_geometry (xdg_surface); meta_window_wayland_finish_move_resize (window, window_geometry, pending); } } static MetaWaylandSurface * meta_wayland_zxdg_popup_v6_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (surface_role); if (xdg_popup->parent_surface) return meta_wayland_surface_get_toplevel (xdg_popup->parent_surface); else return NULL; } static void meta_wayland_zxdg_popup_v6_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (shell_surface); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_popup); MetaWindow *parent_window = meta_wayland_surface_get_window (xdg_popup->parent_surface); int geometry_scale; int x, y; /* If the parent surface was destroyed, its window will be destroyed * before the popup receives the parent-destroy signal. This means that * the popup may potentially get temporary focus until itself is destroyed. * If this happen, don't try to configure the xdg_popup surface. * * FIXME: Could maybe add a signal that is emitted before the window is * created so that we can avoid incorrect intermediate foci. */ if (!parent_window) return; geometry_scale = meta_window_wayland_get_geometry_scale (parent_window); x = configuration->rel_x / geometry_scale; y = configuration->rel_y / geometry_scale; zxdg_popup_v6_send_configure (xdg_popup->resource, x, y, configuration->width / configuration->scale, configuration->height / configuration->scale); meta_wayland_zxdg_surface_v6_send_configure (xdg_surface, configuration); } static void meta_wayland_zxdg_popup_v6_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (shell_surface); MetaWaylandSurface *parent = xdg_popup->parent_surface; g_assert (parent); meta_window_set_transient_for (window, meta_wayland_surface_get_window (parent)); meta_window_set_type (window, META_WINDOW_DROPDOWN_MENU); } static void meta_wayland_zxdg_popup_v6_shell_client_destroyed (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (xdg_surface); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandZxdgSurfaceV6Class *xdg_surface_class = META_WAYLAND_ZXDG_SURFACE_V6_CLASS (meta_wayland_zxdg_popup_v6_parent_class); xdg_surface_class->shell_client_destroyed (xdg_surface); if (xdg_popup->resource) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "xdg_shell of xdg_popup@%d was destroyed", wl_resource_get_id (xdg_popup->resource)); wl_resource_destroy (xdg_popup->resource); } } static void meta_wayland_zxdg_popup_v6_done (MetaWaylandPopupSurface *popup_surface) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (popup_surface); zxdg_popup_v6_send_popup_done (xdg_popup->resource); } static void meta_wayland_zxdg_popup_v6_dismiss (MetaWaylandPopupSurface *popup_surface) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (popup_surface); MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_popup); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_popup); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *top_popup; top_popup = meta_wayland_popup_get_top_popup (xdg_popup->popup); if (surface != top_popup) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_NOT_THE_TOPMOST_POPUP, "destroyed popup not top most popup"); } xdg_popup->popup = NULL; meta_wayland_shell_surface_destroy_window (shell_surface); } static MetaWaylandSurface * meta_wayland_zxdg_popup_v6_get_surface (MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (popup_surface); return meta_wayland_surface_role_get_surface (surface_role); } static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface) { iface->done = meta_wayland_zxdg_popup_v6_done; iface->dismiss = meta_wayland_zxdg_popup_v6_dismiss; iface->get_surface = meta_wayland_zxdg_popup_v6_get_surface; } static void meta_wayland_zxdg_popup_v6_role_finalize (GObject *object) { MetaWaylandZxdgPopupV6 *xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (object); g_clear_pointer (&xdg_popup->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_zxdg_popup_v6_parent_class)->finalize (object); } static void meta_wayland_zxdg_popup_v6_init (MetaWaylandZxdgPopupV6 *role) { } static void meta_wayland_zxdg_popup_v6_class_init (MetaWaylandZxdgPopupV6Class *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; MetaWaylandZxdgSurfaceV6Class *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_zxdg_popup_v6_role_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_zxdg_popup_v6_apply_state; surface_role_class->post_apply_state = meta_wayland_zxdg_popup_v6_post_apply_state; surface_role_class->get_toplevel = meta_wayland_zxdg_popup_v6_get_toplevel; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->configure = meta_wayland_zxdg_popup_v6_configure; shell_surface_class->managed = meta_wayland_zxdg_popup_v6_managed; xdg_surface_class = META_WAYLAND_ZXDG_SURFACE_V6_CLASS (klass); xdg_surface_class->shell_client_destroyed = meta_wayland_zxdg_popup_v6_shell_client_destroyed; } static struct wl_resource * meta_wayland_zxdg_surface_v6_get_shell_resource (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); return priv->shell_client->resource; } static MetaRectangle meta_wayland_zxdg_surface_v6_get_window_geometry (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); return priv->geometry; } static gboolean meta_wayland_zxdg_surface_v6_is_assigned (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); return priv->resource != NULL; } static void meta_wayland_zxdg_surface_v6_send_configure (MetaWaylandZxdgSurfaceV6 *xdg_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); zxdg_surface_v6_send_configure (priv->resource, configuration->serial); priv->configure_sent = TRUE; } static void zxdg_surface_v6_destructor (struct wl_resource *resource) { MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces, xdg_surface); priv->resource = NULL; priv->first_buffer_attached = FALSE; } static void zxdg_surface_v6_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void zxdg_surface_v6_get_toplevel (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_ROLE, "wl_surface@%d already has a role assigned", wl_resource_get_id (surface->resource)); } static void zxdg_surface_v6_get_popup (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { MetaWaylandZxdgSurfaceV6 *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); wl_resource_post_error (priv->shell_client->resource, ZXDG_SHELL_V6_ERROR_ROLE, "wl_surface@%d already has a role assigned", wl_resource_get_id (surface->resource)); } static void zxdg_surface_v6_set_window_geometry (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_geometry = TRUE; pending->new_geometry.x = x; pending->new_geometry.y = y; pending->new_geometry.width = width; pending->new_geometry.height = height; } static void zxdg_surface_v6_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->has_acked_configure_serial = TRUE; pending->acked_configure_serial = serial; } static const struct zxdg_surface_v6_interface meta_wayland_zxdg_surface_v6_interface = { zxdg_surface_v6_destroy, zxdg_surface_v6_get_toplevel, zxdg_surface_v6_get_popup, zxdg_surface_v6_set_window_geometry, zxdg_surface_v6_ack_configure, }; static void meta_wayland_zxdg_surface_v6_finalize (GObject *object) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (object); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); g_clear_pointer (&priv->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_zxdg_surface_v6_parent_class)->finalize (object); } static void meta_wayland_zxdg_surface_v6_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (surface_role); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = meta_wayland_surface_get_window (surface); MetaWaylandSurfaceRoleClass *surface_role_class; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_surface_v6_parent_class); surface_role_class->apply_state (surface_role, pending); /* Ignore commits when unassigned. */ if (!priv->resource) return; if (surface->buffer_ref.buffer == NULL && priv->first_buffer_attached) { /* XDG surfaces can't commit NULL buffers */ wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "Cannot commit a NULL buffer to an xdg_surface"); return; } if (surface->buffer_ref.buffer && !priv->configure_sent) { wl_resource_post_error (surface->resource, ZXDG_SURFACE_V6_ERROR_UNCONFIGURED_BUFFER, "buffer committed to unconfigured xdg_surface"); return; } if (!window) return; if (surface->buffer_ref.buffer) priv->first_buffer_attached = TRUE; } static void meta_wayland_zxdg_surface_v6_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (surface_role); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); if (pending->has_new_geometry) { meta_wayland_shell_surface_determine_geometry (shell_surface, &pending->new_geometry, &priv->geometry); priv->has_set_geometry = TRUE; } else if (!priv->has_set_geometry) { MetaRectangle new_geometry = { 0 }; /* If the surface has never set any geometry, calculate * a default one unioning the surface and all subsurfaces together. */ meta_wayland_shell_surface_calculate_geometry (shell_surface, &new_geometry); if (!meta_rectangle_equal (&new_geometry, &priv->geometry)) { pending->has_new_geometry = TRUE; priv->geometry = new_geometry; } } } static void meta_wayland_zxdg_surface_v6_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (surface_role); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); struct wl_resource *xdg_shell_resource = meta_wayland_zxdg_surface_v6_get_shell_resource (xdg_surface); MetaWaylandSurfaceRoleClass *surface_role_class; priv->configure_sent = FALSE; priv->first_buffer_attached = FALSE; if (surface->buffer_ref.buffer) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "wl_surface@%d already has a buffer committed", wl_resource_get_id (surface->resource)); return; } surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_zxdg_surface_v6_parent_class); surface_role_class->assigned (surface_role); } static void meta_wayland_zxdg_surface_v6_ping (MetaWaylandShellSurface *shell_surface, uint32_t serial) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (shell_surface); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); zxdg_shell_v6_send_ping (priv->shell_client->resource, serial); } static void meta_wayland_zxdg_surface_v6_real_shell_client_destroyed (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); if (priv->resource) { wl_resource_post_error (priv->shell_client->resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "xdg_shell of xdg_surface@%d was destroyed", wl_resource_get_id (priv->resource)); wl_resource_destroy (priv->resource); } } static void meta_wayland_zxdg_surface_v6_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (object); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); switch (prop_id) { case ZXDG_SURFACE_V6_PROP_SHELL_CLIENT: priv->shell_client = g_value_get_pointer (value); break; case ZXDG_SURFACE_V6_PROP_RESOURCE: priv->resource = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_zxdg_surface_v6_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWaylandZxdgSurfaceV6 *xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (object); MetaWaylandZxdgSurfaceV6Private *priv = meta_wayland_zxdg_surface_v6_get_instance_private (xdg_surface); switch (prop_id) { case ZXDG_SURFACE_V6_PROP_SHELL_CLIENT: g_value_set_pointer (value, priv->shell_client); break; case ZXDG_SURFACE_V6_PROP_RESOURCE: g_value_set_pointer (value, priv->resource); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_zxdg_surface_v6_init (MetaWaylandZxdgSurfaceV6 *xdg_surface) { } static void meta_wayland_zxdg_surface_v6_class_init (MetaWaylandZxdgSurfaceV6Class *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; GParamSpec *pspec; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_zxdg_surface_v6_finalize; object_class->set_property = meta_wayland_zxdg_surface_v6_set_property; object_class->get_property = meta_wayland_zxdg_surface_v6_get_property; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_zxdg_surface_v6_apply_state; surface_role_class->post_apply_state = meta_wayland_zxdg_surface_v6_post_apply_state; surface_role_class->assigned = meta_wayland_zxdg_surface_v6_assigned; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->ping = meta_wayland_zxdg_surface_v6_ping; klass->shell_client_destroyed = meta_wayland_zxdg_surface_v6_real_shell_client_destroyed; pspec = g_param_spec_pointer ("shell-client", "MetaWaylandZxdgShellV6Client", "The shell client instance", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, ZXDG_SURFACE_V6_PROP_SHELL_CLIENT, pspec); pspec = g_param_spec_pointer ("xdg-surface-resource", "xdg_surface wl_resource", "The xdg_surface wl_resource instance", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, ZXDG_SURFACE_V6_PROP_RESOURCE, pspec); } static void meta_wayland_zxdg_surface_v6_shell_client_destroyed (MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgSurfaceV6Class *xdg_surface_class = META_WAYLAND_ZXDG_SURFACE_V6_GET_CLASS (xdg_surface); xdg_surface_class->shell_client_destroyed (xdg_surface); } static void meta_wayland_zxdg_surface_v6_constructor_finalize (MetaWaylandZxdgSurfaceV6Constructor *constructor, MetaWaylandZxdgSurfaceV6 *xdg_surface) { MetaWaylandZxdgShellV6Client *shell_client = constructor->shell_client; shell_client->surface_constructors = g_list_remove (shell_client->surface_constructors, constructor); shell_client->surfaces = g_list_append (shell_client->surfaces, xdg_surface); wl_resource_set_implementation (constructor->resource, &meta_wayland_zxdg_surface_v6_interface, xdg_surface, zxdg_surface_v6_destructor); g_free (constructor); } static void zxdg_surface_v6_constructor_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_post_error (resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, "xdg_surface destroyed before constructed"); wl_resource_destroy (resource); } static void zxdg_surface_v6_constructor_get_toplevel (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandZxdgSurfaceV6Constructor *constructor = wl_resource_get_user_data (resource); MetaWaylandZxdgShellV6Client *shell_client = constructor->shell_client; struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *surface = constructor->surface; MetaWaylandZxdgToplevelV6 *xdg_toplevel; MetaWaylandZxdgSurfaceV6 *xdg_surface; MetaWaylandShellSurface *shell_surface; MetaWindow *window; if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_ZXDG_TOPLEVEL_V6, "shell-client", shell_client, "xdg-surface-resource", xdg_surface_resource, NULL)) { wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } xdg_toplevel = META_WAYLAND_ZXDG_TOPLEVEL_V6 (surface->role); xdg_toplevel->resource = wl_resource_create (client, &zxdg_toplevel_v6_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (xdg_toplevel->resource, &meta_wayland_zxdg_toplevel_v6_interface, xdg_toplevel, zxdg_toplevel_v6_destructor); xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_toplevel); meta_wayland_zxdg_surface_v6_constructor_finalize (constructor, xdg_surface); window = meta_window_wayland_new (meta_get_display (), surface); shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); meta_wayland_shell_surface_set_window (shell_surface, window); } static void zxdg_surface_v6_constructor_get_popup (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { MetaWaylandZxdgSurfaceV6Constructor *constructor = wl_resource_get_user_data (resource); MetaWaylandZxdgShellV6Client *shell_client = constructor->shell_client; MetaWaylandSurface *surface = constructor->surface; struct wl_resource *xdg_shell_resource = constructor->shell_client->resource; struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); MetaWaylandZxdgPositionerV6 *xdg_positioner; MetaWaylandZxdgPopupV6 *xdg_popup; MetaWaylandZxdgSurfaceV6 *xdg_surface; if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_ZXDG_POPUP_V6, "shell-client", shell_client, "xdg-surface-resource", xdg_surface_resource, NULL)) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } if (!META_IS_WAYLAND_ZXDG_SURFACE_V6 (parent_surface->role)) { wl_resource_post_error (xdg_shell_resource, ZXDG_SHELL_V6_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent role"); return; } xdg_popup = META_WAYLAND_ZXDG_POPUP_V6 (surface->role); xdg_popup->resource = wl_resource_create (client, &zxdg_popup_v6_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (xdg_popup->resource, &meta_wayland_zxdg_popup_v6_interface, xdg_popup, zxdg_popup_v6_destructor); xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (xdg_popup); meta_wayland_zxdg_surface_v6_constructor_finalize (constructor, xdg_surface); xdg_positioner = wl_resource_get_user_data (positioner_resource); xdg_popup->setup.placement_rule = meta_wayland_zxdg_positioner_v6_to_placement (xdg_positioner); xdg_popup->setup.parent_surface = parent_surface; } static void zxdg_surface_v6_constructor_set_window_geometry (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { wl_resource_post_error (resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, "xdg_surface::set_window_geometry called before constructed"); } static void zxdg_surface_v6_constructor_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { wl_resource_post_error (resource, ZXDG_SURFACE_V6_ERROR_NOT_CONSTRUCTED, "xdg_surface::ack_configure called before constructed"); } static const struct zxdg_surface_v6_interface meta_wayland_zxdg_surface_v6_constructor_interface = { zxdg_surface_v6_constructor_destroy, zxdg_surface_v6_constructor_get_toplevel, zxdg_surface_v6_constructor_get_popup, zxdg_surface_v6_constructor_set_window_geometry, zxdg_surface_v6_constructor_ack_configure, }; static void zxdg_surface_v6_constructor_destructor (struct wl_resource *resource) { MetaWaylandZxdgSurfaceV6Constructor *constructor = wl_resource_get_user_data (resource); constructor->shell_client->surface_constructors = g_list_remove (constructor->shell_client->surface_constructors, constructor); g_free (constructor); } static MetaPlacementRule meta_wayland_zxdg_positioner_v6_to_placement (MetaWaylandZxdgPositionerV6 *xdg_positioner) { return (MetaPlacementRule) { .anchor_rect = xdg_positioner->anchor_rect, .gravity = xdg_positioner->gravity, .anchor = xdg_positioner->anchor, .constraint_adjustment = xdg_positioner->constraint_adjustment, .offset_x = xdg_positioner->offset_x, .offset_y = xdg_positioner->offset_y, .width = xdg_positioner->width, .height = xdg_positioner->height, }; } static void zxdg_positioner_v6_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void zxdg_positioner_v6_set_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); if (width <= 0 || height <= 0) { wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid size"); return; } positioner->width = width; positioner->height = height; } static void zxdg_positioner_v6_set_anchor_rect (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); if (width <= 0 || height <= 0) { wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid anchor rectangle size"); return; } positioner->anchor_rect = (MetaRectangle) { .x = x, .y = y, .width = width, .height = height, }; } static void zxdg_positioner_v6_set_anchor (struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); if ((anchor & ZXDG_POSITIONER_V6_ANCHOR_LEFT && anchor & ZXDG_POSITIONER_V6_ANCHOR_RIGHT) || (anchor & ZXDG_POSITIONER_V6_ANCHOR_TOP && anchor & ZXDG_POSITIONER_V6_ANCHOR_BOTTOM)) { wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid anchor"); return; } positioner->anchor = anchor; } static void zxdg_positioner_v6_set_gravity (struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); if ((gravity & ZXDG_POSITIONER_V6_GRAVITY_LEFT && gravity & ZXDG_POSITIONER_V6_GRAVITY_RIGHT) || (gravity & ZXDG_POSITIONER_V6_GRAVITY_TOP && gravity & ZXDG_POSITIONER_V6_GRAVITY_BOTTOM)) { wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid gravity"); return; } positioner->gravity = gravity; } static void zxdg_positioner_v6_set_constraint_adjustment (struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); uint32_t all_adjustments = (ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_X | ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_X | ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_SLIDE_Y | ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_FLIP_Y | ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_X | ZXDG_POSITIONER_V6_CONSTRAINT_ADJUSTMENT_RESIZE_Y); if ((constraint_adjustment & ~all_adjustments) != 0) { wl_resource_post_error (resource, ZXDG_POSITIONER_V6_ERROR_INVALID_INPUT, "Invalid constraint action"); return; } positioner->constraint_adjustment = constraint_adjustment; } static void zxdg_positioner_v6_set_offset (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); positioner->offset_x = x; positioner->offset_y = y; } static const struct zxdg_positioner_v6_interface meta_wayland_zxdg_positioner_v6_interface = { zxdg_positioner_v6_destroy, zxdg_positioner_v6_set_size, zxdg_positioner_v6_set_anchor_rect, zxdg_positioner_v6_set_anchor, zxdg_positioner_v6_set_gravity, zxdg_positioner_v6_set_constraint_adjustment, zxdg_positioner_v6_set_offset, }; static void zxdg_positioner_v6_destructor (struct wl_resource *resource) { MetaWaylandZxdgPositionerV6 *positioner = wl_resource_get_user_data (resource); g_free (positioner); } static void zxdg_shell_v6_destroy (struct wl_client *client, struct wl_resource *resource) { MetaWaylandZxdgShellV6Client *shell_client = wl_resource_get_user_data (resource); if (shell_client->surfaces || shell_client->surface_constructors) wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_DEFUNCT_SURFACES, "xdg_shell destroyed before its surfaces"); wl_resource_destroy (resource); } static void zxdg_shell_v6_create_positioner (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandZxdgPositionerV6 *positioner; struct wl_resource *positioner_resource; positioner = g_new0 (MetaWaylandZxdgPositionerV6, 1); positioner_resource = wl_resource_create (client, &zxdg_positioner_v6_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (positioner_resource, &meta_wayland_zxdg_positioner_v6_interface, positioner, zxdg_positioner_v6_destructor); } static void zxdg_shell_v6_get_xdg_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandZxdgShellV6Client *shell_client = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandZxdgSurfaceV6 *xdg_surface = NULL; MetaWaylandZxdgSurfaceV6Constructor *constructor; if (surface->role && !META_IS_WAYLAND_ZXDG_SURFACE_V6 (surface->role)) { wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } if (surface->role) xdg_surface = META_WAYLAND_ZXDG_SURFACE_V6 (surface->role); if (xdg_surface && meta_wayland_zxdg_surface_v6_is_assigned (xdg_surface)) { wl_resource_post_error (surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "zxdg_shell_v6::get_xdg_surface already requested"); return; } if (surface->buffer_ref.buffer) { wl_resource_post_error (resource, ZXDG_SHELL_V6_ERROR_INVALID_SURFACE_STATE, "wl_surface@%d already has a buffer committed", wl_resource_get_id (surface->resource)); return; } constructor = g_new0 (MetaWaylandZxdgSurfaceV6Constructor, 1); constructor->surface = surface; constructor->shell_client = shell_client; constructor->resource = wl_resource_create (client, &zxdg_surface_v6_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (constructor->resource, &meta_wayland_zxdg_surface_v6_constructor_interface, constructor, zxdg_surface_v6_constructor_destructor); shell_client->surface_constructors = g_list_append (shell_client->surface_constructors, constructor); } static void zxdg_shell_v6_pong (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaDisplay *display = meta_get_display (); meta_display_pong_for_serial (display, serial); } static const struct zxdg_shell_v6_interface meta_wayland_zxdg_shell_v6_interface = { zxdg_shell_v6_destroy, zxdg_shell_v6_create_positioner, zxdg_shell_v6_get_xdg_surface, zxdg_shell_v6_pong, }; static void meta_wayland_zxdg_shell_v6_client_destroy (MetaWaylandZxdgShellV6Client *shell_client) { while (shell_client->surface_constructors) { MetaWaylandZxdgSurfaceV6Constructor *constructor = g_list_first (shell_client->surface_constructors)->data; wl_resource_destroy (constructor->resource); } g_list_free (shell_client->surface_constructors); while (shell_client->surfaces) { MetaWaylandZxdgSurfaceV6 *xdg_surface = g_list_first (shell_client->surfaces)->data; meta_wayland_zxdg_surface_v6_shell_client_destroyed (xdg_surface); } g_list_free (shell_client->surfaces); g_free (shell_client); } static void zxdg_shell_v6_destructor (struct wl_resource *resource) { MetaWaylandZxdgShellV6Client *shell_client = wl_resource_get_user_data (resource); meta_wayland_zxdg_shell_v6_client_destroy (shell_client); } static void bind_zxdg_shell_v6 (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandZxdgShellV6Client *shell_client; shell_client = g_new0 (MetaWaylandZxdgShellV6Client, 1); shell_client->resource = wl_resource_create (client, &zxdg_shell_v6_interface, version, id); wl_resource_set_implementation (shell_client->resource, &meta_wayland_zxdg_shell_v6_interface, shell_client, zxdg_shell_v6_destructor); } void meta_wayland_legacy_xdg_shell_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &zxdg_shell_v6_interface, META_ZXDG_SHELL_V6_VERSION, compositor, bind_zxdg_shell_v6) == NULL) g_error ("Failed to register a global xdg-shell object"); } muffin-6.4.1/src/wayland/meta-wayland-input-device.h0000664000175000017500000000311114723361714021312 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_WAYLAND_INPUT_DEVICE_H #define META_WAYLAND_INPUT_DEVICE_H #include #include #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_INPUT_DEVICE (meta_wayland_input_device_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandInputDevice, meta_wayland_input_device, META, WAYLAND_INPUT_DEVICE, GObject) struct _MetaWaylandInputDeviceClass { GObjectClass parent_class; }; MetaWaylandSeat * meta_wayland_input_device_get_seat (MetaWaylandInputDevice *input_device); uint32_t meta_wayland_input_device_next_serial (MetaWaylandInputDevice *input_device); #endif /* META_WAYLAND_INPUT_DEVICE_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-pad.h0000664000175000017500000000607514723361714020747 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_PAD_H #define META_WAYLAND_TABLET_PAD_H #include #include #include "backends/meta-cursor-renderer.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletPad { MetaWaylandTabletSeat *tablet_seat; ClutterInputDevice *device; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandSurface *focus_surface; struct wl_listener focus_surface_listener; uint32_t focus_serial; uint32_t n_buttons; GList *groups; GList *rings; GList *strips; GHashTable *feedback; MetaWaylandSurface *focus; }; MetaWaylandTabletPad * meta_wayland_tablet_pad_new (ClutterInputDevice *device, MetaWaylandTabletSeat *tablet_seat); void meta_wayland_tablet_pad_free (MetaWaylandTabletPad *pad); struct wl_resource * meta_wayland_tablet_pad_create_new_resource (MetaWaylandTabletPad *pad, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); struct wl_resource * meta_wayland_tablet_pad_lookup_resource (MetaWaylandTabletPad *pad, struct wl_client *client); void meta_wayland_tablet_pad_notify (MetaWaylandTabletPad *pad, struct wl_resource *resource); void meta_wayland_tablet_pad_update (MetaWaylandTabletPad *pad, const ClutterEvent *event); gboolean meta_wayland_tablet_pad_handle_event (MetaWaylandTabletPad *pad, const ClutterEvent *event); void meta_wayland_tablet_pad_set_focus (MetaWaylandTabletPad *pad, MetaWaylandSurface *surface); gchar * meta_wayland_tablet_pad_get_label (MetaWaylandTabletPad *pad, MetaPadActionType type, guint action); #endif /* META_WAYLAND_TABLET_PAD_H */ muffin-6.4.1/src/wayland/meta-xwayland-dnd.c0000664000175000017500000007764414723361714017674 0ustar fabiofabio/* * Copyright © 2012 Intel Corporation * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The copyright holders make * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* The file is loosely based on xwayland/selection.c from Weston */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "meta/meta-x11-errors.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-xwayland-private.h" #include "wayland/meta-xwayland-dnd-private.h" #include "wayland/meta-xwayland.h" #include "x11/meta-x11-display-private.h" #define INCR_CHUNK_SIZE (128 * 1024) #define XDND_VERSION 5 struct _MetaWaylandDataSourceXWayland { MetaWaylandDataSource parent; MetaXWaylandDnd *dnd; gboolean has_utf8_string_atom; }; struct _MetaXWaylandDnd { Window owner; Time client_message_timestamp; MetaWaylandDataSource *source; /* owned by MetaWaylandDataDevice */ MetaWaylandSurface *focus_surface; Window dnd_window; /* Mutter-internal window, acts as peer on wayland drop sites */ Window dnd_dest; /* X11 drag dest window */ guint32 last_motion_time; }; enum { ATOM_DND_SELECTION, ATOM_DND_AWARE, ATOM_DND_STATUS, ATOM_DND_POSITION, ATOM_DND_ENTER, ATOM_DND_LEAVE, ATOM_DND_DROP, ATOM_DND_FINISHED, ATOM_DND_PROXY, ATOM_DND_TYPE_LIST, ATOM_DND_ACTION_MOVE, ATOM_DND_ACTION_COPY, ATOM_DND_ACTION_ASK, ATOM_DND_ACTION_PRIVATE, N_DND_ATOMS }; /* Matches order in enum above */ const gchar *atom_names[] = { "XdndSelection", "XdndAware", "XdndStatus", "XdndPosition", "XdndEnter", "XdndLeave", "XdndDrop", "XdndFinished", "XdndProxy", "XdndTypeList", "XdndActionMove", "XdndActionCopy", "XdndActionAsk", "XdndActionPrivate", NULL }; Atom xdnd_atoms[N_DND_ATOMS]; G_DEFINE_TYPE (MetaWaylandDataSourceXWayland, meta_wayland_data_source_xwayland, META_TYPE_WAYLAND_DATA_SOURCE); /* XDND helpers */ static Atom action_to_atom (uint32_t action) { if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY) return xdnd_atoms[ATOM_DND_ACTION_COPY]; else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) return xdnd_atoms[ATOM_DND_ACTION_MOVE]; else if (action & WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) return xdnd_atoms[ATOM_DND_ACTION_ASK]; else return None; } static enum wl_data_device_manager_dnd_action atom_to_action (Atom atom) { if (atom == xdnd_atoms[ATOM_DND_ACTION_COPY] || atom == xdnd_atoms[ATOM_DND_ACTION_PRIVATE]) return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; else if (atom == xdnd_atoms[ATOM_DND_ACTION_MOVE]) return WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; else if (atom == xdnd_atoms[ATOM_DND_ACTION_ASK]) return WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; else return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; } static void xdnd_send_enter (MetaXWaylandDnd *dnd, Window dest) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = x11_display->xdisplay; MetaWaylandDataSource *data_source; XEvent xev = { 0 }; gchar **p; struct wl_array *source_mime_types; meta_x11_error_trap_push (x11_display); data_source = compositor->seat->data_device.dnd_data_source; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_ENTER]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = x11_display->selection.xwindow; xev.xclient.data.l[1] = XDND_VERSION << 24; /* version */ xev.xclient.data.l[2] = xev.xclient.data.l[3] = xev.xclient.data.l[4] = 0; source_mime_types = meta_wayland_data_source_get_mime_types (data_source); if (source_mime_types->size <= 3) { /* The mimetype atoms fit in this same message */ gint i = 2; wl_array_for_each (p, source_mime_types) { xev.xclient.data.l[i++] = gdk_x11_get_xatom_by_name (*p); } } else { /* We have more than 3 mimetypes, we must set up * the mimetype list as a XdndTypeList property. */ g_autofree Atom *atomlist = NULL; gint i = 0; xev.xclient.data.l[1] |= 1; atomlist = g_new0 (Atom, source_mime_types->size); wl_array_for_each (p, source_mime_types) { atomlist[i++] = gdk_x11_get_xatom_by_name (*p); } XChangeProperty (xdisplay, x11_display->selection.xwindow, xdnd_atoms[ATOM_DND_TYPE_LIST], XA_ATOM, 32, PropModeReplace, (guchar *) atomlist, i); } XSendEvent (xdisplay, dest, False, NoEventMask, &xev); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) g_critical ("Error sending XdndEnter"); } static void xdnd_send_leave (MetaXWaylandDnd *dnd, Window dest) { MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = x11_display->xdisplay; XEvent xev = { 0 }; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_LEAVE]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = x11_display->selection.xwindow; meta_x11_error_trap_push (x11_display); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); meta_x11_error_trap_pop (x11_display); } static void xdnd_send_position (MetaXWaylandDnd *dnd, Window dest, uint32_t time, int x, int y) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataSource *source = compositor->seat->data_device.dnd_data_source; MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = x11_display->xdisplay; uint32_t action = 0, user_action, actions; XEvent xev = { 0 }; user_action = meta_wayland_data_source_get_user_action (source); meta_wayland_data_source_get_actions (source, &actions); if (user_action & actions) action = user_action; if (!action) action = actions; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_POSITION]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = x11_display->selection.xwindow; xev.xclient.data.l[1] = 0; xev.xclient.data.l[2] = (x << 16) | y; xev.xclient.data.l[3] = time; xev.xclient.data.l[4] = action_to_atom (action); meta_x11_error_trap_push (x11_display); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) g_critical ("Error sending XdndPosition"); } static void xdnd_send_drop (MetaXWaylandDnd *dnd, Window dest, uint32_t time) { MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = x11_display->xdisplay; XEvent xev = { 0 }; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_DROP]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = x11_display->selection.xwindow; xev.xclient.data.l[2] = time; meta_x11_error_trap_push (x11_display); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) g_critical ("Error sending XdndDrop"); } static void xdnd_send_finished (MetaXWaylandDnd *dnd, Window dest, gboolean accepted) { MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = x11_display->xdisplay; MetaWaylandDataSource *source = dnd->source; uint32_t action = 0; XEvent xev = { 0 }; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_FINISHED]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = dnd->dnd_window; if (accepted) { action = meta_wayland_data_source_get_current_action (source); xev.xclient.data.l[1] = 1; /* Drop successful */ xev.xclient.data.l[2] = action_to_atom (action); } meta_x11_error_trap_push (x11_display); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) g_critical ("Error sending XdndFinished"); } static void xdnd_send_status (MetaXWaylandDnd *dnd, Window dest, uint32_t action) { MetaX11Display *x11_display = meta_get_display ()->x11_display; Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); XEvent xev = { 0 }; xev.xclient.type = ClientMessage; xev.xclient.message_type = xdnd_atoms[ATOM_DND_STATUS]; xev.xclient.format = 32; xev.xclient.window = dest; xev.xclient.data.l[0] = dnd->dnd_window; xev.xclient.data.l[1] = 1 << 1; /* Bit 2: dest wants XdndPosition messages */ xev.xclient.data.l[4] = action_to_atom (action); if (xev.xclient.data.l[4]) xev.xclient.data.l[1] |= 1 << 0; /* Bit 1: dest accepts the drop */ meta_x11_error_trap_push (x11_display); XSendEvent (xdisplay, dest, False, NoEventMask, &xev); if (meta_x11_error_trap_pop_with_return (x11_display) != Success) g_critical ("Error sending Xdndstatus"); } static void meta_xwayland_end_dnd_grab (MetaWaylandDataDevice *data_device, gboolean success) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; MetaWaylandDragGrab *drag_grab = compositor->seat->data_device.current_grab; MetaXWaylandDnd *dnd = manager->dnd; if (drag_grab) { if (!success && dnd->source) meta_wayland_data_source_set_current_offer (dnd->source, NULL); meta_wayland_data_device_end_drag (data_device); } XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1); XUnmapWindow (xdisplay, dnd->dnd_window); } static void transfer_cb (MetaSelection *selection, GAsyncResult *res, GOutputStream *stream) { GError *error = NULL; if (!meta_selection_transfer_finish (selection, res, &error)) { g_warning ("Could not transfer DnD selection: %s\n", error->message); g_error_free (error); } g_output_stream_close (stream, NULL, NULL); g_object_unref (stream); } static void meta_x11_source_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { MetaDisplay *display = meta_get_display (); GOutputStream *stream; stream = g_unix_output_stream_new (fd, TRUE); meta_selection_transfer_async (meta_display_get_selection (display), META_SELECTION_DND, mime_type, -1, stream, NULL, (GAsyncReadyCallback) transfer_cb, stream); } static void meta_x11_source_target (MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaXWaylandDnd *dnd = source_xwayland->dnd; uint32_t action = 0; if (mime_type) action = meta_wayland_data_source_get_current_action (source); xdnd_send_status (dnd, dnd->owner, action); } static void meta_x11_source_cancel (MetaWaylandDataSource *source) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaXWaylandDnd *dnd = source_xwayland->dnd; xdnd_send_finished (dnd, dnd->owner, FALSE); } static void meta_x11_source_action (MetaWaylandDataSource *source, uint32_t action) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaXWaylandDnd *dnd = source_xwayland->dnd; if (!meta_wayland_data_source_has_target (source)) action = 0; xdnd_send_status (dnd, dnd->owner, action); } static void meta_x11_source_drop_performed (MetaWaylandDataSource *source) { } static void meta_x11_source_drag_finished (MetaWaylandDataSource *source) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); MetaXWaylandDnd *dnd = source_xwayland->dnd; MetaX11Display *x11_display = meta_get_display ()->x11_display; uint32_t action = meta_wayland_data_source_get_current_action (source); if (action == WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); /* Request data deletion on the drag source */ XConvertSelection (xdisplay, xdnd_atoms[ATOM_DND_SELECTION], gdk_x11_get_xatom_by_name ("DELETE"), gdk_x11_get_xatom_by_name ("_META_SELECTION"), x11_display->selection.xwindow, META_CURRENT_TIME); } xdnd_send_finished (dnd, dnd->owner, TRUE); } static void meta_wayland_data_source_xwayland_init (MetaWaylandDataSourceXWayland *source_xwayland) { } static void meta_wayland_data_source_xwayland_class_init (MetaWaylandDataSourceXWaylandClass *klass) { MetaWaylandDataSourceClass *data_source_class = META_WAYLAND_DATA_SOURCE_CLASS (klass); data_source_class->send = meta_x11_source_send; data_source_class->target = meta_x11_source_target; data_source_class->cancel = meta_x11_source_cancel; data_source_class->action = meta_x11_source_action; data_source_class->drop_performed = meta_x11_source_drop_performed; data_source_class->drag_finished = meta_x11_source_drag_finished; } static MetaWaylandDataSource * meta_wayland_data_source_xwayland_new (MetaXWaylandDnd *dnd) { MetaWaylandDataSourceXWayland *source_xwayland; source_xwayland = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_XWAYLAND, NULL); source_xwayland->dnd = dnd; return META_WAYLAND_DATA_SOURCE (source_xwayland); } static void meta_x11_drag_dest_focus_in (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, MetaWaylandDataOffer *offer) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; dnd->dnd_dest = meta_wayland_surface_get_window (surface)->xwindow; xdnd_send_enter (dnd, dnd->dnd_dest); } static void meta_x11_drag_dest_focus_out (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; xdnd_send_leave (dnd, dnd->dnd_dest); dnd->dnd_dest = None; } static void meta_x11_drag_dest_motion (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, const ClutterEvent *event) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; guint32 time; gfloat x, y; time = clutter_event_get_time (event); clutter_event_get_coords (event, &x, &y); xdnd_send_position (dnd, dnd->dnd_dest, time, x, y); } static void meta_x11_drag_dest_drop (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; xdnd_send_drop (dnd, dnd->dnd_dest, meta_display_get_current_time_roundtrip (meta_get_display ())); } static void meta_x11_drag_dest_update (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; MetaWaylandSeat *seat = compositor->seat; graphene_point_t pos; clutter_input_device_get_coords (seat->pointer->device, NULL, &pos); xdnd_send_position (dnd, dnd->dnd_dest, clutter_get_current_event_time (), pos.x, pos.y); } static const MetaWaylandDragDestFuncs meta_x11_drag_dest_funcs = { meta_x11_drag_dest_focus_in, meta_x11_drag_dest_focus_out, meta_x11_drag_dest_motion, meta_x11_drag_dest_drop, meta_x11_drag_dest_update }; const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void) { return &meta_x11_drag_dest_funcs; } static gboolean meta_xwayland_data_source_fetch_mimetype_list (MetaWaylandDataSource *source, Window window, Atom prop) { MetaWaylandDataSourceXWayland *source_xwayland = META_WAYLAND_DATA_SOURCE_XWAYLAND (source); Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); gulong nitems_ret, bytes_after_ret, i; Atom *atoms, type_ret, utf8_string; int format_ret; struct wl_array *source_mime_types; source_mime_types = meta_wayland_data_source_get_mime_types (source); if (source_mime_types->size != 0) return TRUE; utf8_string = gdk_x11_get_xatom_by_name ("UTF8_STRING"); XGetWindowProperty (xdisplay, window, prop, 0, /* offset */ 0x1fffffff, /* length */ False, /* delete */ AnyPropertyType, &type_ret, &format_ret, &nitems_ret, &bytes_after_ret, (guchar **) &atoms); if (nitems_ret == 0 || type_ret != XA_ATOM) { XFree (atoms); return FALSE; } for (i = 0; i < nitems_ret; i++) { const gchar *mime_type; if (atoms[i] == utf8_string) { meta_wayland_data_source_add_mime_type (source, "text/plain;charset=utf-8"); source_xwayland->has_utf8_string_atom = TRUE; } mime_type = gdk_x11_get_xatom_name (atoms[i]); meta_wayland_data_source_add_mime_type (source, mime_type); } XFree (atoms); return TRUE; } static MetaWaylandSurface * pick_drop_surface (MetaWaylandCompositor *compositor, const ClutterEvent *event) { MetaDisplay *display = meta_get_display (); MetaWindow *focus_window = NULL; graphene_point_t pos; clutter_event_get_coords (event, &pos.x, &pos.y); focus_window = meta_stack_get_default_focus_window_at_point (display->stack, NULL, NULL, pos.x, pos.y); return focus_window ? focus_window->surface : NULL; } static void repick_drop_surface (MetaWaylandCompositor *compositor, MetaWaylandDragGrab *drag_grab, const ClutterEvent *event) { Display *xdisplay = GDK_DISPLAY_XDISPLAY (gdk_display_get_default ()); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; MetaWaylandSurface *focus = NULL; MetaWindow *focus_window; focus = pick_drop_surface (compositor, event); if (dnd->focus_surface == focus) return; dnd->focus_surface = focus; if (focus) focus_window = meta_wayland_surface_get_window (focus); else focus_window = NULL; if (focus_window && focus_window->client_type == META_WINDOW_CLIENT_TYPE_WAYLAND) { XMapRaised (xdisplay, dnd->dnd_window); XMoveResizeWindow (xdisplay, dnd->dnd_window, focus_window->rect.x, focus_window->rect.y, focus_window->rect.width, focus_window->rect.height); } else { XMoveResizeWindow (xdisplay, dnd->dnd_window, -1, -1, 1, 1); XUnmapWindow (xdisplay, dnd->dnd_window); } } static void drag_xgrab_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { /* Do not update the focus here. First, the surface may perfectly * be the X11 source DnD icon window's, so we can only be fooled * here. Second, delaying focus handling to XdndEnter/Leave * makes us do the negotiation orderly on the X11 side. */ } static void drag_xgrab_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; MetaWaylandSeat *seat = compositor->seat; repick_drop_surface (compositor, (MetaWaylandDragGrab *) grab, event); dnd->last_motion_time = clutter_event_get_time (event); meta_wayland_pointer_send_motion (seat->pointer, event); } static void drag_xgrab_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandSeat *seat = compositor->seat; MetaWaylandDataSource *data_source; meta_wayland_pointer_send_button (seat->pointer, event); data_source = compositor->seat->data_device.dnd_data_source; if (seat->pointer->button_count == 0 && (!meta_wayland_drag_grab_get_focus ((MetaWaylandDragGrab *) grab) || meta_wayland_data_source_get_current_action (data_source) == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE)) meta_xwayland_end_dnd_grab (&seat->data_device, FALSE); } static const MetaWaylandPointerGrabInterface drag_xgrab_interface = { drag_xgrab_focus, drag_xgrab_motion, drag_xgrab_button, }; static gboolean meta_xwayland_dnd_handle_client_message (MetaWaylandCompositor *compositor, XEvent *xevent) { XClientMessageEvent *event = (XClientMessageEvent *) xevent; MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; MetaWaylandSeat *seat = compositor->seat; MetaX11Display *x11_display = meta_get_display ()->x11_display; /* Source side messages */ if (event->window == x11_display->selection.xwindow) { MetaWaylandDataSource *data_source; uint32_t action = 0; data_source = compositor->seat->data_device.dnd_data_source; if (!data_source) return FALSE; if (event->message_type == xdnd_atoms[ATOM_DND_STATUS]) { /* The first bit in data.l[1] is set if the drag was accepted */ meta_wayland_data_source_set_has_target (data_source, (event->data.l[1] & 1) != 0); /* data.l[4] contains the action atom */ if (event->data.l[4]) action = atom_to_action ((Atom) event->data.l[4]); meta_wayland_data_source_set_current_action (data_source, action); return TRUE; } else if (event->message_type == xdnd_atoms[ATOM_DND_FINISHED]) { /* Reject messages mid-grab */ if (compositor->seat->data_device.current_grab) return FALSE; meta_wayland_data_source_notify_finish (data_source); return TRUE; } } /* Dest side messages */ else if (dnd->source && compositor->seat->data_device.current_grab && (Window) event->data.l[0] == dnd->owner) { MetaWaylandDragGrab *drag_grab = compositor->seat->data_device.current_grab; MetaWaylandSurface *drag_focus = meta_wayland_drag_grab_get_focus (drag_grab); if (!drag_focus && event->message_type != xdnd_atoms[ATOM_DND_ENTER]) return FALSE; if (event->message_type == xdnd_atoms[ATOM_DND_ENTER]) { /* Bit 1 in data.l[1] determines whether there's 3 or less mimetype * atoms (and are thus contained in this same message), or whether * there's more than 3 and we need to check the XdndTypeList property * for the full list. */ if (!(event->data.l[1] & 1)) { /* Mimetypes are contained in this message */ const gchar *mimetype; gint i; struct wl_array *source_mime_types; /* We only need to fetch once */ source_mime_types = meta_wayland_data_source_get_mime_types (dnd->source); if (source_mime_types->size == 0) { for (i = 2; i <= 4; i++) { if (event->data.l[i] == None) break; mimetype = gdk_x11_get_xatom_name (event->data.l[i]); meta_wayland_data_source_add_mime_type (dnd->source, mimetype); } } } else { /* Fetch mimetypes from type list */ meta_xwayland_data_source_fetch_mimetype_list (dnd->source, event->data.l[0], xdnd_atoms[ATOM_DND_TYPE_LIST]); } meta_wayland_data_source_set_actions (dnd->source, WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK); meta_wayland_drag_grab_set_focus (drag_grab, dnd->focus_surface); return TRUE; } else if (event->message_type == xdnd_atoms[ATOM_DND_POSITION]) { ClutterEvent *motion; graphene_point_t pos; uint32_t action = 0; dnd->client_message_timestamp = event->data.l[3]; motion = clutter_event_new (CLUTTER_MOTION); clutter_input_device_get_coords (seat->pointer->device, NULL, &pos); clutter_event_set_coords (motion, pos.x, pos.y); clutter_event_set_device (motion, seat->pointer->device); clutter_event_set_source_device (motion, seat->pointer->device); clutter_event_set_time (motion, dnd->last_motion_time); action = atom_to_action ((Atom) event->data.l[4]); meta_wayland_data_source_set_user_action (dnd->source, action); meta_wayland_surface_drag_dest_motion (drag_focus, motion); xdnd_send_status (dnd, (Window) event->data.l[0], meta_wayland_data_source_get_current_action (dnd->source)); clutter_event_free (motion); return TRUE; } else if (event->message_type == xdnd_atoms[ATOM_DND_LEAVE]) { meta_wayland_drag_grab_set_focus (drag_grab, NULL); return TRUE; } else if (event->message_type == xdnd_atoms[ATOM_DND_DROP]) { dnd->client_message_timestamp = event->data.l[2]; meta_wayland_surface_drag_dest_drop (drag_focus); meta_xwayland_end_dnd_grab (&seat->data_device, TRUE); return TRUE; } } return FALSE; } static gboolean meta_xwayland_dnd_handle_xfixes_selection_notify (MetaWaylandCompositor *compositor, XEvent *xevent) { XFixesSelectionNotifyEvent *event = (XFixesSelectionNotifyEvent *) xevent; MetaXWaylandDnd *dnd = compositor->xwayland_manager.dnd; MetaWaylandDataDevice *data_device = &compositor->seat->data_device; MetaX11Display *x11_display = meta_get_display ()->x11_display; MetaWaylandSurface *focus; if (event->selection != xdnd_atoms[ATOM_DND_SELECTION]) return FALSE; dnd->owner = event->owner; focus = compositor->seat->pointer->focus_surface; if (event->owner != None && event->owner != x11_display->selection.xwindow && focus && meta_xwayland_is_xwayland_surface (focus)) { dnd->source = meta_wayland_data_source_xwayland_new (dnd); meta_wayland_data_device_set_dnd_source (&compositor->seat->data_device, dnd->source); meta_wayland_data_device_start_drag (data_device, wl_resource_get_client (focus->resource), &drag_xgrab_interface, focus, dnd->source, NULL); } else if (event->owner == None) { meta_xwayland_end_dnd_grab (data_device, FALSE); g_clear_object (&dnd->source); } return FALSE; } gboolean meta_xwayland_dnd_handle_event (XEvent *xevent) { MetaWaylandCompositor *compositor; compositor = meta_wayland_compositor_get_default (); if (!compositor->xwayland_manager.dnd) return FALSE; switch (xevent->type) { case ClientMessage: return meta_xwayland_dnd_handle_client_message (compositor, xevent); default: { MetaX11Display *x11_display = meta_get_display ()->x11_display; if (xevent->type - x11_display->xfixes_event_base == XFixesSelectionNotify) return meta_xwayland_dnd_handle_xfixes_selection_notify (compositor, xevent); return FALSE; } } } void meta_xwayland_init_dnd (Display *xdisplay) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; MetaXWaylandDnd *dnd = manager->dnd; XSetWindowAttributes attributes; guint32 i, version = XDND_VERSION; g_assert (manager->dnd == NULL); manager->dnd = dnd = g_slice_new0 (MetaXWaylandDnd); for (i = 0; i < N_DND_ATOMS; i++) xdnd_atoms[i] = gdk_x11_get_xatom_by_name (atom_names[i]); attributes.event_mask = PropertyChangeMask | SubstructureNotifyMask; attributes.override_redirect = True; dnd->dnd_window = XCreateWindow (xdisplay, gdk_x11_window_get_xid (gdk_get_default_root_window ()), -1, -1, 1, 1, 0, /* border width */ 0, /* depth */ InputOnly, /* class */ CopyFromParent, /* visual */ CWEventMask | CWOverrideRedirect, &attributes); XChangeProperty (xdisplay, dnd->dnd_window, xdnd_atoms[ATOM_DND_AWARE], XA_ATOM, 32, PropModeReplace, (guchar*) &version, 1); } void meta_xwayland_shutdown_dnd (Display *xdisplay) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; MetaXWaylandDnd *dnd = manager->dnd; g_assert (dnd != NULL); XDestroyWindow (xdisplay, dnd->dnd_window); dnd->dnd_window = None; g_slice_free (MetaXWaylandDnd, dnd); manager->dnd = NULL; } muffin-6.4.1/src/wayland/meta-wayland-tablet-cursor-surface.h0000664000175000017500000000241614723361714023141 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_TABLET_CURSOR_SURFACE_H #define META_WAYLAND_TABLET_CURSOR_SURFACE_H #include "wayland/meta-wayland-cursor-surface.h" #define META_TYPE_WAYLAND_TABLET_CURSOR_SURFACE (meta_wayland_tablet_cursor_surface_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandTabletCursorSurface, meta_wayland_tablet_cursor_surface, META, WAYLAND_TABLET_CURSOR_SURFACE, MetaWaylandCursorSurface) #endif /* META_WAYLAND_TABLET_CURSOR_SURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-strip.h0000664000175000017500000000446514723361714022107 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_PAD_STRIP_H #define META_WAYLAND_TABLET_PAD_STRIP_H #include #include #include "backends/meta-cursor-renderer.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletPadStrip { MetaWaylandTabletPad *pad; MetaWaylandTabletPadGroup *group; struct wl_list resource_list; struct wl_list focus_resource_list; gchar *feedback; }; MetaWaylandTabletPadStrip * meta_wayland_tablet_pad_strip_new (MetaWaylandTabletPad *pad); void meta_wayland_tablet_pad_strip_free (MetaWaylandTabletPadStrip *strip); void meta_wayland_tablet_pad_strip_set_group (MetaWaylandTabletPadStrip *strip, MetaWaylandTabletPadGroup *group); struct wl_resource * meta_wayland_tablet_pad_strip_create_new_resource (MetaWaylandTabletPadStrip *strip, struct wl_client *client, struct wl_resource *group_resource, uint32_t id); gboolean meta_wayland_tablet_pad_strip_handle_event (MetaWaylandTabletPadStrip *strip, const ClutterEvent *event); void meta_wayland_tablet_pad_strip_sync_focus (MetaWaylandTabletPadStrip *strip); #endif /* META_WAYLAND_TABLET_PAD_STRIP_H */ muffin-6.4.1/src/wayland/meta-window-wayland.c0000664000175000017500000011335314723361714020232 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "wayland/meta-window-wayland.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-private.h" #include "core/boxes-private.h" #include "core/stack-tracker.h" #include "core/window-private.h" #include "meta/meta-x11-errors.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-window-configuration.h" #include "wayland/meta-wayland-xdg-shell.h" struct _MetaWindowWayland { MetaWindow parent; int geometry_scale; GList *pending_configurations; gboolean has_pending_state_change; int last_sent_x; int last_sent_y; int last_sent_width; int last_sent_height; int last_sent_rel_x; int last_sent_rel_y; int last_sent_geometry_scale; MetaGravity last_sent_gravity; gboolean has_been_shown; }; struct _MetaWindowWaylandClass { MetaWindowClass parent_class; }; G_DEFINE_TYPE (MetaWindowWayland, meta_window_wayland, META_TYPE_WINDOW) static void set_geometry_scale_for_window (MetaWindowWayland *wl_window, int geometry_scale) { MetaWindowActor *window_actor; wl_window->geometry_scale = geometry_scale; window_actor = meta_window_actor_from_window (META_WINDOW (wl_window)); if (window_actor) meta_window_actor_set_geometry_scale (window_actor, geometry_scale); } static int get_window_geometry_scale_for_logical_monitor (MetaLogicalMonitor *logical_monitor) { g_assert (logical_monitor); if (meta_is_stage_views_scaled ()) return 1; else return meta_logical_monitor_get_scale (logical_monitor); } static void meta_window_wayland_manage (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); MetaDisplay *display = window->display; wl_window->geometry_scale = meta_window_wayland_get_geometry_scale (window); meta_display_register_wayland_window (display, window); { meta_stack_tracker_record_add (window->display->stack_tracker, window->stamp, 0); } meta_wayland_surface_window_managed (window->surface, window); } static void meta_window_wayland_unmanage (MetaWindow *window) { { meta_stack_tracker_record_remove (window->display->stack_tracker, window->stamp, 0); } meta_display_unregister_wayland_window (window->display, window); } static void meta_window_wayland_ping (MetaWindow *window, guint32 serial) { meta_wayland_surface_ping (window->surface, serial); } static void meta_window_wayland_delete (MetaWindow *window, guint32 timestamp) { meta_wayland_surface_delete (window->surface); } static void meta_window_wayland_kill (MetaWindow *window) { MetaWaylandSurface *surface = window->surface; struct wl_resource *resource = surface->resource; /* Send the client an unrecoverable error to kill the client. */ wl_resource_post_error (resource, WL_DISPLAY_ERROR_NO_MEMORY, "User requested that we kill you. Sorry. Don't take it too personally."); } static void meta_window_wayland_focus (MetaWindow *window, guint32 timestamp) { if (meta_window_is_focusable (window)) { meta_display_set_input_focus (window->display, window, FALSE, timestamp); } } static void meta_window_wayland_configure (MetaWindowWayland *wl_window, MetaWaylandWindowConfiguration *configuration) { MetaWindow *window = META_WINDOW (wl_window); meta_wayland_surface_configure_notify (window->surface, configuration); wl_window->pending_configurations = g_list_prepend (wl_window->pending_configurations, configuration); } static void surface_state_changed (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); MetaWaylandWindowConfiguration *configuration; /* don't send notify when the window is being unmanaged */ if (window->unmanaging) return; configuration = meta_wayland_window_configuration_new (wl_window->last_sent_x, wl_window->last_sent_y, wl_window->last_sent_width, wl_window->last_sent_height, wl_window->last_sent_geometry_scale, META_MOVE_RESIZE_STATE_CHANGED, wl_window->last_sent_gravity); meta_window_wayland_configure (wl_window, configuration); } static void meta_window_wayland_grab_op_began (MetaWindow *window, MetaGrabOp op) { if (meta_grab_op_is_resizing (op)) surface_state_changed (window); META_WINDOW_CLASS (meta_window_wayland_parent_class)->grab_op_began (window, op); } static void meta_window_wayland_grab_op_ended (MetaWindow *window, MetaGrabOp op) { if (meta_grab_op_is_resizing (op)) surface_state_changed (window); META_WINDOW_CLASS (meta_window_wayland_parent_class)->grab_op_ended (window, op); } static void meta_window_wayland_move_resize_internal (MetaWindow *window, MetaGravity gravity, MetaRectangle unconstrained_rect, MetaRectangle constrained_rect, MetaRectangle temporary_rect, int rel_x, int rel_y, MetaMoveResizeFlags flags, MetaMoveResizeResultFlags *result) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); gboolean can_move_now = FALSE; int configured_x; int configured_y; int configured_width; int configured_height; int geometry_scale; int new_x; int new_y; int new_buffer_x; int new_buffer_y; g_assert (window->frame == NULL); /* don't do anything if we're dropping the window, see #751847 */ if (window->unmanaging) return; configured_x = constrained_rect.x; configured_y = constrained_rect.y; /* The scale the window is drawn in might change depending on what monitor it * is mainly on. Scale the configured rectangle to be in logical pixel * coordinate space so that we can have a scale independent size to pass * to the Wayland surface. */ geometry_scale = meta_window_wayland_get_geometry_scale (window); if (flags & META_MOVE_RESIZE_UNMAXIMIZE && !meta_window_is_fullscreen (window)) { configured_width = 0; configured_height = 0; } else if (flags & META_MOVE_RESIZE_UNFULLSCREEN && !meta_window_get_maximized (window) && meta_window_get_tile_mode (window) == META_TILE_NONE) { configured_width = 0; configured_height = 0; } else { configured_width = constrained_rect.width; configured_height = constrained_rect.height; } /* For wayland clients, the size is completely determined by the client, * and while this allows to avoid some trickery with frames and the resulting * lagging, we also need to insist a bit when the constraints would apply * a different size than the client decides. * * Note that this is not generally a problem for normal toplevel windows (the * constraints don't see the size hints, or just change the position), but * it can be for maximized or fullscreen. */ if (flags & META_MOVE_RESIZE_FORCE_MOVE) { can_move_now = TRUE; } else if (flags & META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE) { /* This is a call to wl_surface_commit(), ignore the constrained_rect and * update the real client size to match the buffer size. */ if (window->rect.width != unconstrained_rect.width || window->rect.height != unconstrained_rect.height) { *result |= META_MOVE_RESIZE_RESULT_RESIZED; window->rect.width = unconstrained_rect.width; window->rect.height = unconstrained_rect.height; } /* This is a commit of an attach. We should move the window to match the * new position the client wants. */ can_move_now = TRUE; if (window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED) window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_FINISHED; } else { if (window->placement.rule) { switch (window->placement.state) { case META_PLACEMENT_STATE_UNCONSTRAINED: case META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED: case META_PLACEMENT_STATE_INVALIDATED: can_move_now = FALSE; break; case META_PLACEMENT_STATE_CONSTRAINED_PENDING: { if (flags & META_MOVE_RESIZE_PLACEMENT_CHANGED || rel_x != wl_window->last_sent_rel_x || rel_y != wl_window->last_sent_rel_y || constrained_rect.width != window->rect.width || constrained_rect.height != window->rect.height) { MetaWaylandWindowConfiguration *configuration; configuration = meta_wayland_window_configuration_new_relative (rel_x, rel_y, configured_width, configured_height, geometry_scale); meta_window_wayland_configure (wl_window, configuration); wl_window->last_sent_rel_x = rel_x; wl_window->last_sent_rel_y = rel_y; window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_CONFIGURED; can_move_now = FALSE; } else { window->placement.state = META_PLACEMENT_STATE_CONSTRAINED_FINISHED; can_move_now = TRUE; } break; } case META_PLACEMENT_STATE_CONSTRAINED_FINISHED: can_move_now = TRUE; break; } } else if (constrained_rect.width != window->rect.width || constrained_rect.height != window->rect.height || flags & META_MOVE_RESIZE_STATE_CHANGED) { MetaWaylandWindowConfiguration *configuration; /* If the constrained size is 1x1 and the unconstrained size is 0x0 * it means that we are trying to resize a window where the client has * not yet committed a buffer. The 1x1 constrained size is a result of * how the constraints code works. Lets avoid trying to have the * client configure itself to draw on a 1x1 surface. * * We cannot guard against only an empty unconstrained_rect here, * because the client may have created a xdg surface without a buffer * attached and asked it to be maximized. In such case we should let * it know about the expected window geometry of a maximized window, * even though there is currently no buffer attached. */ if (unconstrained_rect.width == 0 && unconstrained_rect.height == 0 && constrained_rect.width == 1 && constrained_rect.height == 1) return; configuration = meta_wayland_window_configuration_new (configured_x, configured_y, configured_width, configured_height, geometry_scale, flags, gravity); meta_window_wayland_configure (wl_window, configuration); can_move_now = FALSE; } else { can_move_now = TRUE; } } wl_window->last_sent_x = configured_x; wl_window->last_sent_y = configured_y; wl_window->last_sent_width = configured_width; wl_window->last_sent_height = configured_height; wl_window->last_sent_geometry_scale = geometry_scale; wl_window->last_sent_gravity = gravity; if (can_move_now) { new_x = constrained_rect.x; new_y = constrained_rect.y; } else { new_x = temporary_rect.x; new_y = temporary_rect.y; wl_window->has_pending_state_change |= !!(flags & META_MOVE_RESIZE_STATE_CHANGED); } if (new_x != window->rect.x || new_y != window->rect.y) { *result |= META_MOVE_RESIZE_RESULT_MOVED; window->rect.x = new_x; window->rect.y = new_y; } if (window->placement.rule && window->placement.state == META_PLACEMENT_STATE_CONSTRAINED_FINISHED) { window->placement.current.rel_x = rel_x; window->placement.current.rel_y = rel_y; } new_buffer_x = new_x - window->custom_frame_extents.left; new_buffer_y = new_y - window->custom_frame_extents.top; if (new_buffer_x != window->buffer_rect.x || new_buffer_y != window->buffer_rect.y) { *result |= META_MOVE_RESIZE_RESULT_MOVED; window->buffer_rect.x = new_buffer_x; window->buffer_rect.y = new_buffer_y; } if (can_move_now && flags & META_MOVE_RESIZE_WAYLAND_STATE_CHANGED) *result |= META_MOVE_RESIZE_RESULT_STATE_CHANGED; } static void scale_size (int *width, int *height, float scale) { if (*width < G_MAXINT) { float new_width = (*width * scale); *width = (int) MIN (new_width, G_MAXINT); } if (*height < G_MAXINT) { float new_height = (*height * scale); *height = (int) MIN (new_height, G_MAXINT); } } static void scale_rect_size (MetaRectangle *rect, float scale) { scale_size (&rect->width, &rect->height, scale); } static void meta_window_wayland_update_main_monitor (MetaWindow *window, MetaWindowUpdateMonitorFlags flags) { MetaBackend *backend = meta_get_backend (); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); MetaWindow *toplevel_window; MetaLogicalMonitor *from; MetaLogicalMonitor *to; MetaLogicalMonitor *scaled_new; float from_scale, to_scale; float scale; MetaRectangle rect; from = window->monitor; /* If the window is not a toplevel window (i.e. it's a popup window) just use * the monitor of the toplevel. */ toplevel_window = meta_wayland_surface_get_toplevel_window (window->surface); if (toplevel_window != window) { meta_window_update_monitor (toplevel_window, flags); window->monitor = toplevel_window->monitor; return; } /* Require both the current and the new monitor would be the new main monitor, * even given the resulting scale the window would end up having. This is * needed to avoid jumping back and forth between the new and the old, since * changing main monitor may cause the window to be resized so that it no * longer have that same new main monitor. */ to = meta_window_calculate_main_logical_monitor (window); if (from == to) return; if (from == NULL || to == NULL) { window->monitor = to; return; } if (flags & META_WINDOW_UPDATE_MONITOR_FLAGS_FORCE) { window->monitor = to; return; } from_scale = meta_logical_monitor_get_scale (from); to_scale = meta_logical_monitor_get_scale (to); if (from_scale == to_scale) { window->monitor = to; return; } if (meta_is_stage_views_scaled ()) { window->monitor = to; return; } /* To avoid a window alternating between two main monitors because scaling * changes the main monitor, wait until both the current and the new scale * will result in the same main monitor. */ scale = to_scale / from_scale; rect = window->rect; scale_rect_size (&rect, scale); scaled_new = meta_monitor_manager_get_logical_monitor_from_rect (monitor_manager, &rect); if (to != scaled_new) return; window->monitor = to; } static void meta_window_wayland_main_monitor_changed (MetaWindow *window, const MetaLogicalMonitor *old) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); int old_geometry_scale = wl_window->geometry_scale; int geometry_scale; float scale_factor; MetaWaylandSurface *surface; if (!window->monitor) return; geometry_scale = meta_window_wayland_get_geometry_scale (window); /* This function makes sure that window geometry, window actor geometry and * surface actor geometry gets set according the old and current main monitor * scale. If there either is no past or current main monitor, or if the scale * didn't change, there is nothing to do. */ if (old == NULL || window->monitor == NULL || old_geometry_scale == geometry_scale) return; /* MetaWindow keeps its rectangles in the physical pixel coordinate space. * When the main monitor of a window changes, it can cause the corresponding * window surfaces to be scaled given the monitor scale, so we need to scale * the rectangles in MetaWindow accordingly. */ scale_factor = (float) geometry_scale / old_geometry_scale; /* Window size. */ scale_rect_size (&window->rect, scale_factor); scale_rect_size (&window->unconstrained_rect, scale_factor); scale_rect_size (&window->saved_rect, scale_factor); scale_size (&window->size_hints.min_width, &window->size_hints.min_height, scale_factor); scale_size (&window->size_hints.max_width, &window->size_hints.max_height, scale_factor); /* Window geometry offset (XXX: Need a better place, see * meta_window_wayland_finish_move_resize). */ window->custom_frame_extents.left = (int)(scale_factor * window->custom_frame_extents.left); window->custom_frame_extents.top = (int)(scale_factor * window->custom_frame_extents.top); /* Buffer rect. */ scale_rect_size (&window->buffer_rect, scale_factor); window->buffer_rect.x = window->rect.x - window->custom_frame_extents.left; window->buffer_rect.y = window->rect.y - window->custom_frame_extents.top; meta_compositor_sync_window_geometry (window->display->compositor, window, TRUE); surface = window->surface; if (surface) { MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface->role); meta_wayland_actor_surface_sync_actor_state (actor_surface); } set_geometry_scale_for_window (wl_window, geometry_scale); meta_window_emit_size_changed (window); } static uint32_t meta_window_wayland_get_client_pid (MetaWindow *window) { MetaWaylandSurface *surface = window->surface; struct wl_resource *resource = surface->resource; pid_t pid; wl_client_get_credentials (wl_resource_get_client (resource), &pid, NULL, NULL); return (uint32_t)pid; } static void appears_focused_changed (GObject *object, GParamSpec *pspec, gpointer user_data) { MetaWindow *window = META_WINDOW (object); if (window->placement.rule) return; surface_state_changed (window); } static void on_window_shown (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); gboolean has_been_shown; has_been_shown = wl_window->has_been_shown; wl_window->has_been_shown = TRUE; if (!has_been_shown) meta_compositor_sync_updates_frozen (window->display->compositor, window); } static void meta_window_wayland_init (MetaWindowWayland *wl_window) { MetaWindow *window = META_WINDOW (wl_window); wl_window->geometry_scale = 1; g_signal_connect (window, "notify::appears-focused", G_CALLBACK (appears_focused_changed), NULL); g_signal_connect (window, "shown", G_CALLBACK (on_window_shown), NULL); } static void meta_window_wayland_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_restore_shortcuts (compositor, source); } static gboolean meta_window_wayland_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); return meta_wayland_compositor_is_shortcuts_inhibited (compositor, source); } static gboolean meta_window_wayland_is_focusable (MetaWindow *window) { return window->input; } static gboolean meta_window_wayland_can_ping (MetaWindow *window) { return TRUE; } static gboolean meta_window_wayland_is_stackable (MetaWindow *window) { return meta_wayland_surface_get_buffer (window->surface) != NULL; } static gboolean meta_window_wayland_are_updates_frozen (MetaWindow *window) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); return !wl_window->has_been_shown; } static gboolean meta_window_wayland_is_focus_async (MetaWindow *window) { return FALSE; } static MetaStackLayer meta_window_wayland_calculate_layer (MetaWindow *window) { return meta_window_get_default_layer (window); } static void meta_window_wayland_map (MetaWindow *window) { } static void meta_window_wayland_unmap (MetaWindow *window) { } static void meta_window_wayland_finalize (GObject *object) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (object); g_list_free_full (wl_window->pending_configurations, (GDestroyNotify) meta_wayland_window_configuration_free); G_OBJECT_CLASS (meta_window_wayland_parent_class)->finalize (object); } static void meta_window_wayland_class_init (MetaWindowWaylandClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaWindowClass *window_class = META_WINDOW_CLASS (klass); object_class->finalize = meta_window_wayland_finalize; window_class->manage = meta_window_wayland_manage; window_class->unmanage = meta_window_wayland_unmanage; window_class->ping = meta_window_wayland_ping; window_class->delete = meta_window_wayland_delete; window_class->kill = meta_window_wayland_kill; window_class->focus = meta_window_wayland_focus; window_class->grab_op_began = meta_window_wayland_grab_op_began; window_class->grab_op_ended = meta_window_wayland_grab_op_ended; window_class->move_resize_internal = meta_window_wayland_move_resize_internal; window_class->update_main_monitor = meta_window_wayland_update_main_monitor; window_class->main_monitor_changed = meta_window_wayland_main_monitor_changed; window_class->get_client_pid = meta_window_wayland_get_client_pid; window_class->force_restore_shortcuts = meta_window_wayland_force_restore_shortcuts; window_class->shortcuts_inhibited = meta_window_wayland_shortcuts_inhibited; window_class->is_focusable = meta_window_wayland_is_focusable; window_class->is_stackable = meta_window_wayland_is_stackable; window_class->can_ping = meta_window_wayland_can_ping; window_class->are_updates_frozen = meta_window_wayland_are_updates_frozen; window_class->calculate_layer = meta_window_wayland_calculate_layer; window_class->map = meta_window_wayland_map; window_class->unmap = meta_window_wayland_unmap; window_class->is_focus_async = meta_window_wayland_is_focus_async; } MetaWindow * meta_window_wayland_new (MetaDisplay *display, MetaWaylandSurface *surface) { XWindowAttributes attrs = { 0 }; MetaWindowWayland *wl_window; MetaWindow *window; /* * Set attributes used by _meta_window_shared_new, don't bother trying to fake * X11 window attributes with the rest, since they'll be ignored anyway. */ attrs.x = 0; attrs.y = 0; attrs.width = 0; attrs.height = 0; attrs.depth = 24; attrs.visual = NULL; attrs.map_state = IsUnmapped; attrs.override_redirect = False; window = _meta_window_shared_new (display, META_WINDOW_CLIENT_TYPE_WAYLAND, surface, None, WithdrawnState, META_COMP_EFFECT_CREATE, &attrs); wl_window = META_WINDOW_WAYLAND (window); set_geometry_scale_for_window (wl_window, wl_window->geometry_scale); return window; } MetaWaylandWindowConfiguration * meta_window_wayland_peek_configuration (MetaWindowWayland *wl_window, uint32_t serial) { GList *l; for (l = wl_window->pending_configurations; l; l = l->next) { MetaWaylandWindowConfiguration *configuration = l->data; if (configuration->serial == serial) return configuration; } return NULL; } static MetaWaylandWindowConfiguration * acquire_acked_configuration (MetaWindowWayland *wl_window, MetaWaylandSurfaceState *pending) { GList *l; if (!pending->has_acked_configure_serial) return NULL; for (l = wl_window->pending_configurations; l; l = l->next) { MetaWaylandWindowConfiguration *configuration = l->data; GList *tail; gboolean is_matching_configuration; if (configuration->serial > pending->acked_configure_serial) continue; tail = l; if (tail->prev) { tail->prev->next = NULL; tail->prev = NULL; } else { wl_window->pending_configurations = NULL; } is_matching_configuration = configuration->serial == pending->acked_configure_serial; if (is_matching_configuration) tail = g_list_delete_link (tail, l); g_list_free_full (tail, (GDestroyNotify) meta_wayland_window_configuration_free); if (is_matching_configuration) return configuration; else return NULL; } return NULL; } int meta_window_wayland_get_geometry_scale (MetaWindow *window) { if (!window->monitor) return 1; return get_window_geometry_scale_for_logical_monitor (window->monitor); } static void calculate_offset (MetaWaylandWindowConfiguration *configuration, MetaRectangle *geometry, MetaRectangle *rect) { int offset_x; int offset_y; rect->x = configuration->x; rect->y = configuration->y; offset_x = configuration->width - geometry->width; offset_y = configuration->height - geometry->height; switch (configuration->gravity) { case META_GRAVITY_SOUTH: case META_GRAVITY_SOUTH_WEST: rect->y += offset_y; break; case META_GRAVITY_EAST: case META_GRAVITY_NORTH_EAST: rect->x += offset_x; break; case META_GRAVITY_SOUTH_EAST: rect->x += offset_x; rect->y += offset_y; break; default: break; } } /** * meta_window_move_resize_wayland: * * Complete a resize operation from a wayland client. */ void meta_window_wayland_finish_move_resize (MetaWindow *window, MetaRectangle new_geom, MetaWaylandSurfaceState *pending) { MetaWindowWayland *wl_window = META_WINDOW_WAYLAND (window); MetaDisplay *display = window->display; int dx, dy; int geometry_scale; MetaGravity gravity; MetaRectangle rect; MetaMoveResizeFlags flags; MetaWaylandWindowConfiguration *acked_configuration; gboolean is_window_being_resized; /* new_geom is in the logical pixel coordinate space, but MetaWindow wants its * rects to represent what in turn will end up on the stage, i.e. we need to * scale new_geom to physical pixels given what buffer scale and texture scale * is in use. */ geometry_scale = meta_window_wayland_get_geometry_scale (window); new_geom.x *= geometry_scale; new_geom.y *= geometry_scale; new_geom.width *= geometry_scale; new_geom.height *= geometry_scale; /* The (dx, dy) offset is also in logical pixel coordinate space and needs * to be scaled in the same way as new_geom. */ dx = pending->dx * geometry_scale; dy = pending->dy * geometry_scale; /* XXX: Find a better place to store the window geometry offsets. */ window->custom_frame_extents.left = new_geom.x; window->custom_frame_extents.top = new_geom.y; flags = META_MOVE_RESIZE_WAYLAND_FINISH_MOVE_RESIZE; acked_configuration = acquire_acked_configuration (wl_window, pending); /* x/y are ignored when we're doing interactive resizing */ is_window_being_resized = (meta_grab_op_is_resizing (display->grab_op) && display->grab_window == window); if (!is_window_being_resized) { if (acked_configuration) { if (window->placement.rule) { MetaWindow *parent; parent = meta_window_get_transient_for (window); rect.x = parent->rect.x + acked_configuration->rel_x; rect.y = parent->rect.y + acked_configuration->rel_y; } else { calculate_offset (acked_configuration, &new_geom, &rect); } } else { rect.x = window->rect.x; rect.y = window->rect.y; } rect.x += dx; rect.y += dy; } else { if (acked_configuration) calculate_offset (acked_configuration, &new_geom, &rect); } if (rect.x != window->rect.x || rect.y != window->rect.y) flags |= META_MOVE_RESIZE_MOVE_ACTION; if (wl_window->has_pending_state_change && acked_configuration) { flags |= META_MOVE_RESIZE_WAYLAND_STATE_CHANGED; wl_window->has_pending_state_change = FALSE; } rect.width = new_geom.width; rect.height = new_geom.height; if (rect.width != window->rect.width || rect.height != window->rect.height) flags |= META_MOVE_RESIZE_RESIZE_ACTION; if (window->display->grab_window == window) gravity = meta_resize_gravity_from_grab_op (window->display->grab_op); else gravity = META_GRAVITY_STATIC; meta_window_move_resize_internal (window, flags, gravity, rect); g_clear_pointer (&acked_configuration, meta_wayland_window_configuration_free); } void meta_window_wayland_place_relative_to (MetaWindow *window, MetaWindow *other, int x, int y) { int geometry_scale; /* If there is no monitor, we can't position the window reliably. */ if (!other->monitor) return; geometry_scale = meta_window_wayland_get_geometry_scale (other); meta_window_move_frame (window, FALSE, other->buffer_rect.x + (x * geometry_scale), other->buffer_rect.y + (y * geometry_scale)); window->placed = TRUE; } void meta_window_place_with_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule) { gboolean first_placement; first_placement = !window->placement.rule; g_clear_pointer (&window->placement.rule, g_free); window->placement.rule = g_new0 (MetaPlacementRule, 1); *window->placement.rule = *placement_rule; window->unconstrained_rect.x = window->rect.x; window->unconstrained_rect.y = window->rect.y; window->unconstrained_rect.width = placement_rule->width; window->unconstrained_rect.height = placement_rule->height; window->calc_placement = first_placement; meta_window_move_resize_internal (window, (META_MOVE_RESIZE_MOVE_ACTION | META_MOVE_RESIZE_RESIZE_ACTION | META_MOVE_RESIZE_PLACEMENT_CHANGED), META_GRAVITY_NORTH_WEST, window->unconstrained_rect); window->calc_placement = FALSE; } void meta_window_update_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule) { window->placement.state = META_PLACEMENT_STATE_INVALIDATED; meta_window_place_with_placement_rule (window, placement_rule); } void meta_window_wayland_set_min_size (MetaWindow *window, int width, int height) { gint64 new_width, new_height; float scale; meta_topic (META_DEBUG_GEOMETRY, "Window %s sets min size %d x %d\n", window->desc, width, height); if (width == 0 && height == 0) { window->size_hints.min_width = 0; window->size_hints.min_height = 0; window->size_hints.flags &= ~PMinSize; return; } scale = (float) meta_window_wayland_get_geometry_scale (window); scale_size (&width, &height, scale); new_width = width + (window->custom_frame_extents.left + window->custom_frame_extents.right); new_height = height + (window->custom_frame_extents.top + window->custom_frame_extents.bottom); window->size_hints.min_width = (int) MIN (new_width, G_MAXINT); window->size_hints.min_height = (int) MIN (new_height, G_MAXINT); window->size_hints.flags |= PMinSize; } void meta_window_wayland_set_max_size (MetaWindow *window, int width, int height) { gint64 new_width, new_height; float scale; meta_topic (META_DEBUG_GEOMETRY, "Window %s sets max size %d x %d\n", window->desc, width, height); if (width == 0 && height == 0) { window->size_hints.max_width = G_MAXINT; window->size_hints.max_height = G_MAXINT; window->size_hints.flags &= ~PMaxSize; return; } scale = (float) meta_window_wayland_get_geometry_scale (window); scale_size (&width, &height, scale); new_width = width + (window->custom_frame_extents.left + window->custom_frame_extents.right); new_height = height + (window->custom_frame_extents.top + window->custom_frame_extents.bottom); window->size_hints.max_width = (int) ((new_width > 0 && new_width < G_MAXINT) ? new_width : G_MAXINT); window->size_hints.max_height = (int) ((new_height > 0 && new_height < G_MAXINT) ? new_height : G_MAXINT); window->size_hints.flags |= PMaxSize; } void meta_window_wayland_get_min_size (MetaWindow *window, int *width, int *height) { gint64 current_width, current_height; float scale; if (!(window->size_hints.flags & PMinSize)) { /* Zero means unlimited */ *width = 0; *height = 0; return; } current_width = window->size_hints.min_width - (window->custom_frame_extents.left + window->custom_frame_extents.right); current_height = window->size_hints.min_height - (window->custom_frame_extents.top + window->custom_frame_extents.bottom); *width = MAX (current_width, 0); *height = MAX (current_height, 0); scale = 1.0 / (float) meta_window_wayland_get_geometry_scale (window); scale_size (width, height, scale); } void meta_window_wayland_get_max_size (MetaWindow *window, int *width, int *height) { gint64 current_width = 0; gint64 current_height = 0; float scale; if (!(window->size_hints.flags & PMaxSize)) { /* Zero means unlimited */ *width = 0; *height = 0; return; } if (window->size_hints.max_width < G_MAXINT) current_width = window->size_hints.max_width - (window->custom_frame_extents.left + window->custom_frame_extents.right); if (window->size_hints.max_height < G_MAXINT) current_height = window->size_hints.max_height - (window->custom_frame_extents.top + window->custom_frame_extents.bottom); *width = CLAMP (current_width, 0, G_MAXINT); *height = CLAMP (current_height, 0, G_MAXINT); scale = 1.0 / (float) meta_window_wayland_get_geometry_scale (window); scale_size (width, height, scale); } muffin-6.4.1/src/wayland/meta-wayland-outputs.h0000664000175000017500000000343014723361714020445 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_OUTPUTS_H #define META_WAYLAND_OUTPUTS_H #include "backends/meta-monitor-manager-private.h" #include "wayland/meta-wayland-private.h" #define META_TYPE_WAYLAND_OUTPUT (meta_wayland_output_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandOutput, meta_wayland_output, META, WAYLAND_OUTPUT, GObject) struct _MetaWaylandOutput { GObject parent; struct wl_global *global; MetaLogicalMonitor *logical_monitor; guint mode_flags; float refresh_rate; gint scale; int mode_width; int mode_height; GList *resources; GList *xdg_output_resources; uint64_t winsys_id; }; void meta_wayland_outputs_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_OUTPUTS_H */ muffin-6.4.1/src/wayland/meta-xwayland-grab-keyboard.c0000664000175000017500000002625114723361714021624 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Olivier Fourdan */ #include "config.h" #include #include "meta/meta-backend.h" #include "backends/meta-settings-private.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-window-wayland.h" #include "wayland/meta-xwayland-grab-keyboard.h" struct _MetaXwaylandKeyboardActiveGrab { MetaWaylandSurface *surface; MetaWaylandSeat *seat; MetaWaylandKeyboardGrab keyboard_grab; gulong surface_destroyed_handler; gulong shortcuts_restored_handler; gulong window_associate_handler; struct wl_resource *resource; }; static gboolean meta_xwayland_keyboard_grab_key (MetaWaylandKeyboardGrab *grab, const ClutterEvent *event) { MetaXwaylandKeyboardActiveGrab *active_grab; MetaWaylandKeyboard *keyboard; active_grab = wl_container_of (grab, active_grab, keyboard_grab); keyboard = active_grab->keyboard_grab.keyboard; /* Force focus onto the surface who has the active grab on the keyboard */ if (active_grab->surface != NULL && keyboard->focus_surface != active_grab->surface) meta_wayland_keyboard_set_focus (keyboard, active_grab->surface); /* Chain-up with default keyboard handler */ return keyboard->default_grab.interface->key (&keyboard->default_grab, event); } static void meta_xwayland_keyboard_grab_modifiers (MetaWaylandKeyboardGrab *grab, ClutterModifierType modifiers) { MetaXwaylandKeyboardActiveGrab *active_grab; MetaWaylandKeyboard *keyboard; active_grab = wl_container_of (grab, active_grab, keyboard_grab); keyboard = active_grab->keyboard_grab.keyboard; /* Force focus onto the surface who has the active grab on the keyboard */ if (active_grab->surface != NULL && keyboard->focus_surface != active_grab->surface) meta_wayland_keyboard_set_focus (keyboard, active_grab->surface); /* Chain-up with default keyboard handler */ return keyboard->default_grab.interface->modifiers (&keyboard->default_grab, modifiers); } static void meta_xwayland_keyboard_grab_end (MetaXwaylandKeyboardActiveGrab *active_grab) { MetaWaylandSeat *seat = active_grab->seat; if (seat->keyboard->grab->interface->key == meta_xwayland_keyboard_grab_key) { meta_wayland_keyboard_end_grab (active_grab->keyboard_grab.keyboard); meta_wayland_keyboard_set_focus (active_grab->keyboard_grab.keyboard, NULL); meta_display_sync_wayland_input_focus (meta_get_display ()); } if (!active_grab->surface) return; g_clear_signal_handler (&active_grab->surface_destroyed_handler, active_grab->surface); g_clear_signal_handler (&active_grab->shortcuts_restored_handler, active_grab->surface); meta_wayland_surface_restore_shortcuts (active_grab->surface, active_grab->seat); g_clear_signal_handler (&active_grab->window_associate_handler, active_grab->surface->role); active_grab->surface = NULL; } static const MetaWaylandKeyboardGrabInterface keyboard_grab_interface = { meta_xwayland_keyboard_grab_key, meta_xwayland_keyboard_grab_modifiers }; static void zwp_xwayland_keyboard_grab_destructor (struct wl_resource *resource) { MetaXwaylandKeyboardActiveGrab *active_grab; active_grab = wl_resource_get_user_data (resource); meta_xwayland_keyboard_grab_end (active_grab); g_free (active_grab); } static void zwp_xwayland_keyboard_grab_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_xwayland_keyboard_grab_v1_interface xwayland_keyboard_grab_interface = { zwp_xwayland_keyboard_grab_destroy, }; static void surface_destroyed_cb (MetaWaylandSurface *surface, MetaXwaylandKeyboardActiveGrab *active_grab) { active_grab->surface = NULL; } static void shortcuts_restored_cb (MetaWaylandSurface *surface, MetaXwaylandKeyboardActiveGrab *active_grab) { meta_xwayland_keyboard_grab_end (active_grab); } static void zwp_xwayland_keyboard_grab_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static gboolean application_is_in_pattern_array (MetaWindow *window, GPtrArray *pattern_array) { guint i; for (i = 0; pattern_array && i < pattern_array->len; i++) { GPatternSpec *pattern = (GPatternSpec *) g_ptr_array_index (pattern_array, i); if ((window->res_class && g_pattern_match_string (pattern, window->res_class)) || (window->res_name && g_pattern_match_string (pattern, window->res_name))) return TRUE; } return FALSE; } static gboolean meta_xwayland_grab_is_granted (MetaWindow *window) { MetaBackend *backend; MetaSettings *settings; GPtrArray *whitelist; GPtrArray *blacklist; gboolean may_grab; backend = meta_get_backend (); settings = meta_backend_get_settings (backend); /* Check whether the window is blacklisted */ meta_settings_get_xwayland_grab_patterns (settings, &whitelist, &blacklist); if (blacklist && application_is_in_pattern_array (window, blacklist)) return FALSE; /* Check if we are dealing with good citizen Xwayland client whitelisting itself. */ g_object_get (G_OBJECT (window), "xwayland-may-grab-keyboard", &may_grab, NULL); if (may_grab) return TRUE; /* Last resort, is it white listed. */ if (whitelist && application_is_in_pattern_array (window, whitelist)) return TRUE; return FALSE; } static gboolean meta_xwayland_grab_should_lock_focus (MetaWindow *window) { MetaBackend *backend; MetaSettings *settings; /* Lock focus applies to O-R windows which never receive keyboard focus otherwise */ if (!window->override_redirect) return FALSE; backend = meta_get_backend (); settings = meta_backend_get_settings (backend); return meta_settings_are_xwayland_grabs_allowed (settings); } static void meta_xwayland_keyboard_grab_activate (MetaXwaylandKeyboardActiveGrab *active_grab) { MetaWaylandSurface *surface = active_grab->surface; MetaWindow *window = meta_wayland_surface_get_window (surface); MetaWaylandSeat *seat = active_grab->seat; if (meta_xwayland_grab_is_granted (window)) { meta_verbose ("XWayland window %s has a grab granted", window->desc); meta_wayland_surface_inhibit_shortcuts (surface, seat); if (meta_xwayland_grab_should_lock_focus (window)) meta_wayland_keyboard_start_grab (seat->keyboard, &active_grab->keyboard_grab); } g_clear_signal_handler (&active_grab->window_associate_handler, active_grab->surface->role); } static void meta_xwayland_keyboard_window_associated (MetaWaylandSurfaceRole *surface_role, MetaXwaylandKeyboardActiveGrab *active_grab) { meta_xwayland_keyboard_grab_activate (active_grab); } static void zwp_xwayland_keyboard_grab_manager_grab (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *seat_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWindow *window = meta_wayland_surface_get_window (surface); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaXwaylandKeyboardActiveGrab *active_grab; struct wl_resource *grab_resource; grab_resource = wl_resource_create (client, &zwp_xwayland_keyboard_grab_manager_v1_interface, wl_resource_get_version (resource), id); active_grab = g_new0 (MetaXwaylandKeyboardActiveGrab, 1); active_grab->surface = surface; active_grab->resource = grab_resource; active_grab->seat = seat; active_grab->keyboard_grab.interface = &keyboard_grab_interface; active_grab->surface_destroyed_handler = g_signal_connect (surface, "destroy", G_CALLBACK (surface_destroyed_cb), active_grab); active_grab->shortcuts_restored_handler = g_signal_connect (surface, "shortcuts-restored", G_CALLBACK (shortcuts_restored_cb), active_grab); if (window) meta_xwayland_keyboard_grab_activate (active_grab); else if (surface->role) active_grab->window_associate_handler = g_signal_connect (surface->role, "window-associated", G_CALLBACK (meta_xwayland_keyboard_window_associated), active_grab); else g_warning ("Cannot grant Xwayland grab to surface %p", surface); wl_resource_set_implementation (grab_resource, &xwayland_keyboard_grab_interface, active_grab, zwp_xwayland_keyboard_grab_destructor); } static const struct zwp_xwayland_keyboard_grab_manager_v1_interface meta_keyboard_grab_manager_interface = { zwp_xwayland_keyboard_grab_manager_destroy, zwp_xwayland_keyboard_grab_manager_grab, }; static void bind_keyboard_grab (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_xwayland_keyboard_grab_manager_v1_interface, MIN (META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION, version), id); wl_resource_set_implementation (resource, &meta_keyboard_grab_manager_interface, NULL, NULL); } gboolean meta_xwayland_grab_keyboard_init (MetaWaylandCompositor *compositor) { return (wl_global_create (compositor->wayland_display, &zwp_xwayland_keyboard_grab_manager_v1_interface, META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION, NULL, bind_keyboard_grab) != NULL); } muffin-6.4.1/src/wayland/meta-wayland-data-device.h0000664000175000017500000001041714723361714021073 0ustar fabiofabio/* * Copyright © 2008 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_DEVICE_H #define META_WAYLAND_DATA_DEVICE_H #include #include #include "clutter/clutter.h" #include "meta/meta-selection-source.h" #include "wayland/meta-wayland-data-offer.h" #include "wayland/meta-wayland-data-source.h" #include "wayland/meta-wayland-types.h" typedef struct _MetaWaylandDragGrab MetaWaylandDragGrab; typedef struct _MetaWaylandDataSourceFuncs MetaWaylandDataSourceFuncs; struct _MetaWaylandDataDevice { uint32_t selection_serial; MetaWaylandDataSource *selection_data_source; MetaWaylandDataSource *dnd_data_source; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandDragGrab *current_grab; struct wl_client *focus_client; guint selection_owner_signal_id; MetaSelectionSource *owners[META_N_SELECTION_TYPES]; }; void meta_wayland_data_device_manager_init (MetaWaylandCompositor *compositor); void meta_wayland_data_device_init (MetaWaylandDataDevice *data_device); void meta_wayland_data_device_set_keyboard_focus (MetaWaylandDataDevice *data_device); gboolean meta_wayland_data_device_is_dnd_surface (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface); MetaWaylandDragGrab * meta_wayland_data_device_get_current_grab (MetaWaylandDataDevice *data_device); void meta_wayland_data_device_set_dnd_source (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source); void meta_wayland_data_device_set_selection (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial); void meta_wayland_data_device_unset_dnd_selection (MetaWaylandDataDevice *data_device); const MetaWaylandDragDestFuncs * meta_wayland_data_device_get_drag_dest_funcs (void); void meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data_device, struct wl_client *client, const MetaWaylandPointerGrabInterface *funcs, MetaWaylandSurface *surface, MetaWaylandDataSource *source, MetaWaylandSurface *icon_surface); void meta_wayland_data_device_end_drag (MetaWaylandDataDevice *data_device); void meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab, MetaWaylandSurface *surface); MetaWaylandSurface * meta_wayland_drag_grab_get_focus (MetaWaylandDragGrab *drag_grab); void meta_wayland_drag_grab_update_feedback_actor (MetaWaylandDragGrab *drag_grab, ClutterEvent *event); #endif /* META_WAYLAND_DATA_DEVICE_H */ muffin-6.4.1/src/wayland/meta-wayland-egl-stream.c0000664000175000017500000002705314723361714020764 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "wayland/meta-wayland-egl-stream.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-egl-ext.h" #include "backends/meta-egl.h" #include "cogl/cogl-egl.h" #include "meta/meta-backend.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland-eglstream-controller-server-protocol.h" static struct wl_interface *wl_eglstream_controller_interface_ptr = NULL; static void attach_eglstream_consumer (struct wl_client *client, struct wl_resource *resource, struct wl_resource *wl_surface, struct wl_resource *wl_eglstream) { MetaWaylandBuffer *buffer = meta_wayland_buffer_from_resource (wl_eglstream); if (!meta_wayland_buffer_is_realized (buffer)) meta_wayland_buffer_realize (buffer); } static const struct wl_eglstream_controller_interface meta_eglstream_controller_interface = { attach_eglstream_consumer }; static void bind_eglstream_controller (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; g_assert (wl_eglstream_controller_interface_ptr != NULL); resource = wl_resource_create (client, wl_eglstream_controller_interface_ptr, version, id); if (resource == NULL) { wl_client_post_no_memory(client); return; } wl_resource_set_implementation (resource, &meta_eglstream_controller_interface, data, NULL); } gboolean meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor) { /* * wl_eglstream_controller_interface is provided by * libnvidia-egl-wayland.so.1 * * Since it might not be available on the * system, dynamically load it at runtime and resolve the needed * symbols. If available, it should be found under any of the search * directories of dlopen() * * Failure to initialize wl_eglstream_controller is non-fatal */ void *lib = dlopen ("libnvidia-egl-wayland.so.1", RTLD_NOW | RTLD_LAZY); if (!lib) goto fail; wl_eglstream_controller_interface_ptr = dlsym (lib, "wl_eglstream_controller_interface"); if (!wl_eglstream_controller_interface_ptr) goto fail; if (wl_global_create (compositor->wayland_display, wl_eglstream_controller_interface_ptr, 1, NULL, bind_eglstream_controller) == NULL) goto fail; g_debug ("WL: loaded libnvidia-egl-wayland.so.1:wl_eglstream_controller."); return TRUE; fail: if (lib) dlclose(lib); g_debug ("WL: Unable to initialize wl_eglstream_controller."); return FALSE; } struct _MetaWaylandEglStream { GObject parent; EGLStreamKHR egl_stream; MetaWaylandBuffer *buffer; CoglTexture2D *texture; gboolean is_y_inverted; CoglSnippet *snippet; }; G_DEFINE_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream, G_TYPE_OBJECT) MetaWaylandEglStream * meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); EGLAttrib stream_attribs[] = { EGL_WAYLAND_EGLSTREAM_WL, (EGLAttrib) buffer->resource, EGL_NONE }; EGLStreamKHR egl_stream; MetaWaylandEglStream *stream; egl_stream = meta_egl_create_stream_attrib (egl, egl_display, stream_attribs, error); if (egl_stream == EGL_NO_STREAM_KHR) { g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Failed to create stream from wl_buffer resource"); return NULL; } stream = g_object_new (META_TYPE_WAYLAND_EGL_STREAM, NULL); stream->egl_stream = egl_stream; stream->buffer = buffer; return stream; } static void stream_texture_destroyed (gpointer data) { MetaWaylandEglStream *stream = data; stream->texture = NULL; g_object_unref (stream); } static gboolean alloc_egl_stream_texture (CoglTexture2D *texture, gpointer user_data, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); MetaWaylandEglStream *stream = user_data; return meta_egl_stream_consumer_gl_texture_external (egl, egl_display, stream->egl_stream, error); } CoglTexture2D * meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); CoglTexture2D *texture; int width, height; int y_inverted; if (!meta_egl_query_wayland_buffer (egl, egl_display, stream->buffer->resource, EGL_WIDTH, &width, error)) return NULL; if (!meta_egl_query_wayland_buffer (egl, egl_display, stream->buffer->resource, EGL_HEIGHT, &height, error)) return NULL; if (!meta_egl_query_wayland_buffer (egl, egl_display, stream->buffer->resource, EGL_WAYLAND_Y_INVERTED_WL, &y_inverted, NULL)) y_inverted = EGL_TRUE; texture = cogl_texture_2d_new_from_egl_image_external (cogl_context, width, height, alloc_egl_stream_texture, g_object_ref (stream), stream_texture_destroyed, error); if (!texture) { g_object_unref (stream); return NULL; } if (!cogl_texture_allocate (COGL_TEXTURE (texture), error)) { cogl_object_unref (texture); return NULL; } stream->texture = texture; stream->is_y_inverted = !!y_inverted; return texture; } gboolean meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); EGLint stream_state; if (!meta_egl_query_stream (egl, egl_display, stream->egl_stream, EGL_STREAM_STATE_KHR, &stream_state, error)) return FALSE; if (stream_state == EGL_STREAM_STATE_NEW_FRAME_AVAILABLE_KHR) { if (!meta_egl_stream_consumer_acquire (egl, egl_display, stream->egl_stream, error)) return FALSE; } return TRUE; } gboolean meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream) { return stream->is_y_inverted; } CoglSnippet * meta_wayland_egl_stream_create_snippet (MetaWaylandEglStream *stream) { if (!stream->snippet) { CoglSnippet *snippet; snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_TEXTURE_LOOKUP, "uniform samplerExternalOES tex_external;", NULL); cogl_snippet_set_replace (snippet, "cogl_texel = texture2D (tex_external,\n" " cogl_tex_coord.xy);"); stream->snippet = snippet; } return cogl_object_ref (stream->snippet); } gboolean meta_wayland_is_egl_stream_buffer (MetaWaylandBuffer *buffer) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); int stream_fd; if (!meta_egl_has_extensions (egl, egl_display, NULL, "EGL_KHR_stream_consumer_gltexture", "EGL_KHR_stream_cross_process_fd", NULL)) return FALSE; if (!meta_egl_query_wayland_buffer (egl, egl_display, buffer->resource, EGL_WAYLAND_BUFFER_WL, &stream_fd, NULL)) return FALSE; return TRUE; } static void meta_wayland_egl_stream_finalize (GObject *object) { MetaWaylandEglStream *stream = META_WAYLAND_EGL_STREAM (object); MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); g_assert (!stream->texture); meta_egl_destroy_stream (egl, egl_display, stream->egl_stream, NULL); cogl_clear_object (&stream->snippet); G_OBJECT_CLASS (meta_wayland_egl_stream_parent_class)->finalize (object); } static void meta_wayland_egl_stream_init (MetaWaylandEglStream *stream) { } static void meta_wayland_egl_stream_class_init (MetaWaylandEglStreamClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_egl_stream_finalize; } muffin-6.4.1/src/wayland/meta-wayland-inhibit-shortcuts.h0000664000175000017500000000271114723361714022405 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Olivier Fourdan */ #ifndef META_WAYLAND_INHIBIT_SHORTCUTS_H #define META_WAYLAND_INHIBIT_SHORTCUTS_H #include #include "meta/window.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_KEYBOARD_SHORTCUTS_INHIBIT (meta_wayland_keyboard_shortcuts_inhibit_resource_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandKeyboardShotscutsInhibit, meta_wayland_keyboard_shortcuts_inhibit_resource, META, WAYLAND_KEYBOARD_SHORTCUTS_INHIBIT, GObject); gboolean meta_wayland_keyboard_shortcuts_inhibit_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_INHIBIT_SHORTCUTS_H */ muffin-6.4.1/src/wayland/meta-wayland-inhibit-shortcuts-dialog.h0000664000175000017500000000231014723361714023635 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_WAYLAND_INHIBIT_SHORTCUTS_DIALOG_H #define META_WAYLAND_INHIBIT_SHORTCUTS_DIALOG_H #include "wayland/meta-wayland-private.h" void meta_wayland_surface_show_inhibit_shortcuts_dialog (MetaWaylandSurface *surface, MetaWaylandSeat *seat); void meta_wayland_surface_cancel_inhibit_shortcuts_dialog (MetaWaylandSurface *surface); void meta_wayland_surface_inhibit_shortcuts_dialog_init (void); #endif /* META_WAYLAND_INHIBIT_SHORTCUTS_DIALOG_H */ muffin-6.4.1/src/wayland/meta-xwayland.c0000664000175000017500000005770314723361714017123 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * X Wayland Support * * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-xwayland.h" #include "wayland/meta-xwayland-private.h" #include #include #include #include #include #if defined(HAVE_SYS_RANDOM) #include #elif defined(HAVE_LINUX_RANDOM) #include #endif #include #include #include "core/main-private.h" #include "meta/main.h" #include "wayland/meta-xwayland-surface.h" #include "x11/meta-x11-display-private.h" static int display_number_override = -1; static void meta_xwayland_stop_xserver (MetaXWaylandManager *manager); void meta_xwayland_associate_window_with_surface (MetaWindow *window, MetaWaylandSurface *surface) { MetaDisplay *display = window->display; MetaXwaylandSurface *xwayland_surface; if (!meta_wayland_surface_assign_role (surface, META_TYPE_XWAYLAND_SURFACE, NULL)) { wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } xwayland_surface = META_XWAYLAND_SURFACE (surface->role); meta_xwayland_surface_associate_with_window (xwayland_surface, window); /* Now that we have a surface check if it should have focus. */ meta_display_sync_wayland_input_focus (display); } static gboolean associate_window_with_surface_id (MetaXWaylandManager *manager, MetaWindow *window, guint32 surface_id) { struct wl_resource *resource; resource = wl_client_get_object (manager->client, surface_id); if (resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); meta_xwayland_associate_window_with_surface (window, surface); return TRUE; } else return FALSE; } void meta_xwayland_handle_wl_surface_id (MetaWindow *window, guint32 surface_id) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; if (!associate_window_with_surface_id (manager, window, surface_id)) { /* No surface ID yet, schedule this association for whenever the * surface is made known. */ meta_wayland_compositor_schedule_surface_association (compositor, surface_id, window); } } gboolean meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; return wl_resource_get_client (surface->resource) == manager->client; } static gboolean try_display (int display, char **filename_out, int *fd_out) { gboolean ret = FALSE; char *filename; int fd; filename = g_strdup_printf ("/tmp/.X%d-lock", display); again: fd = open (filename, O_WRONLY | O_CLOEXEC | O_CREAT | O_EXCL, 0444); if (fd < 0 && errno == EEXIST) { char pid[11]; char *end; pid_t other; fd = open (filename, O_CLOEXEC, O_RDONLY); if (fd < 0 || read (fd, pid, 11) != 11) { g_warning ("can't read lock file %s: %m", filename); goto out; } close (fd); fd = -1; pid[10] = '\0'; other = strtol (pid, &end, 0); if (end != pid + 10) { g_warning ("can't parse lock file %s", filename); goto out; } if (kill (other, 0) < 0 && errno == ESRCH) { /* Process is dead. Try unlinking the lock file and trying again. */ if (unlink (filename) < 0) { g_warning ("failed to unlink stale lock file %s: %m", filename); goto out; } goto again; } goto out; } else if (fd < 0) { g_warning ("failed to create lock file %s: %m", filename); goto out; } ret = TRUE; out: if (!ret) { g_free (filename); filename = NULL; if (fd >= 0) { close (fd); fd = -1; } } *filename_out = filename; *fd_out = fd; return ret; } static char * create_lock_file (int display, int *display_out) { char *filename; int fd; char pid[12]; int size; int number_of_tries = 0; while (!try_display (display, &filename, &fd)) { display++; number_of_tries++; /* If we can't get a display after 50 times, then something's wrong. Just * abort in this case. */ if (number_of_tries >= 50) return NULL; } /* Subtle detail: we use the pid of the wayland compositor, not the xserver * in the lock file. Another subtlety: snprintf returns the number of bytes * it _would've_ written without either the NUL or the size clamping, hence * the disparity in size. */ size = snprintf (pid, 12, "%10d\n", getpid ()); if (size != 11 || write (fd, pid, 11) != 11) { unlink (filename); close (fd); g_warning ("failed to write pid to lock file %s", filename); g_free (filename); return NULL; } close (fd); *display_out = display; return filename; } static int bind_to_abstract_socket (int display, gboolean *fatal) { struct sockaddr_un addr; socklen_t size, name_size; int fd; fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) { *fatal = TRUE; g_warning ("Failed to create socket: %m"); return -1; } addr.sun_family = AF_LOCAL; name_size = snprintf (addr.sun_path, sizeof addr.sun_path, "%c/tmp/.X11-unix/X%d", 0, display); size = offsetof (struct sockaddr_un, sun_path) + name_size; if (bind (fd, (struct sockaddr *) &addr, size) < 0) { *fatal = errno != EADDRINUSE; g_warning ("failed to bind to @%s: %m", addr.sun_path + 1); close (fd); return -1; } if (listen (fd, 1) < 0) { *fatal = errno != EADDRINUSE; g_warning ("Failed to listen on abstract socket @%s: %m", addr.sun_path + 1); close (fd); return -1; } return fd; } static int bind_to_unix_socket (int display) { struct sockaddr_un addr; socklen_t size, name_size; int fd; fd = socket (PF_LOCAL, SOCK_STREAM | SOCK_CLOEXEC, 0); if (fd < 0) return -1; addr.sun_family = AF_LOCAL; name_size = snprintf (addr.sun_path, sizeof addr.sun_path, "/tmp/.X11-unix/X%d", display) + 1; size = offsetof (struct sockaddr_un, sun_path) + name_size; unlink (addr.sun_path); if (bind (fd, (struct sockaddr *) &addr, size) < 0) { g_warning ("failed to bind to %s: %m\n", addr.sun_path); close (fd); return -1; } if (listen (fd, 1) < 0) { unlink (addr.sun_path); close (fd); return -1; } return fd; } static void xserver_died (GObject *source, GAsyncResult *result, gpointer user_data) { GSubprocess *proc = G_SUBPROCESS (source); MetaDisplay *display = meta_get_display (); g_autoptr (GError) error = NULL; if (!g_subprocess_wait_finish (proc, result, &error)) { if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) return; g_warning ("Failed to finish waiting for Xwayland: %s", error->message); } else if (!g_subprocess_get_successful (proc)) { if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY) g_warning ("X Wayland crashed; exiting"); else g_warning ("X Wayland crashed; attempting to recover"); } if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY) { meta_exit (META_EXIT_ERROR); } else if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_ON_DEMAND) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); if (display->x11_display) meta_display_shutdown_x11 (display); if (!meta_xwayland_init (&compositor->xwayland_manager, compositor->wayland_display)) g_warning ("Failed to init X sockets"); } } static gboolean shutdown_xwayland_cb (gpointer data) { MetaXWaylandManager *manager = data; meta_verbose ("Shutting down Xwayland"); manager->xserver_grace_period_id = 0; meta_display_shutdown_x11 (meta_get_display ()); meta_xwayland_stop_xserver (manager); return G_SOURCE_REMOVE; } static int x_io_error (Display *display) { g_warning ("Connection to xwayland lost"); if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_MANDATORY) meta_exit (META_EXIT_ERROR); return 0; } void meta_xwayland_override_display_number (int number) { display_number_override = number; } static gboolean open_display_sockets (MetaXWaylandManager *manager, int display_index, int *abstract_fd_out, int *unix_fd_out, gboolean *fatal) { int abstract_fd, unix_fd; abstract_fd = bind_to_abstract_socket (display_index, fatal); if (abstract_fd < 0) return FALSE; unix_fd = bind_to_unix_socket (display_index); if (unix_fd < 0) { *fatal = FALSE; close (abstract_fd); return FALSE; } *abstract_fd_out = abstract_fd; *unix_fd_out = unix_fd; return TRUE; } static gboolean choose_xdisplay (MetaXWaylandManager *manager, MetaXWaylandConnection *connection) { int display = 0; char *lock_file = NULL; gboolean fatal = FALSE; if (display_number_override != -1) display = display_number_override; else if (g_getenv ("RUNNING_UNDER_GDM")) display = 1024; do { lock_file = create_lock_file (display, &display); if (!lock_file) { g_warning ("Failed to create an X lock file"); return FALSE; } if (!open_display_sockets (manager, display, &connection->abstract_fd, &connection->unix_fd, &fatal)) { unlink (lock_file); if (!fatal) { display++; continue; } else { g_warning ("Failed to bind X11 socket"); return FALSE; } } break; } while (1); connection->display_index = display; connection->name = g_strdup_printf (":%d", connection->display_index); connection->lock_file = lock_file; return TRUE; } G_DEFINE_AUTOPTR_CLEANUP_FUNC (FILE, fclose) static gboolean prepare_auth_file (MetaXWaylandManager *manager) { Xauth auth_entry = { 0 }; g_autoptr (FILE) fp = NULL; char auth_data[16]; int fd; manager->auth_file = g_build_filename (g_get_user_runtime_dir (), ".mutter-Xwaylandauth.XXXXXX", NULL); if (getrandom (auth_data, sizeof (auth_data), 0) != sizeof (auth_data)) { g_warning ("Failed to get random data: %s", g_strerror (errno)); return FALSE; } auth_entry.family = FamilyLocal; auth_entry.address = (char *) g_get_host_name (); auth_entry.address_length = strlen (auth_entry.address); auth_entry.name = (char *) "MIT-MAGIC-COOKIE-1"; auth_entry.name_length = strlen (auth_entry.name); auth_entry.data = auth_data; auth_entry.data_length = sizeof (auth_data); fd = g_mkstemp (manager->auth_file); if (fd < 0) { g_warning ("Failed to open Xauthority file: %s", g_strerror (errno)); return FALSE; } fp = fdopen (fd, "w+"); if (!fp) { g_warning ("Failed to open Xauthority stream: %s", g_strerror (errno)); close (fd); return FALSE; } if (!XauWriteAuth (fp, &auth_entry)) { g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); return FALSE; } auth_entry.family = FamilyWild; if (!XauWriteAuth (fp, &auth_entry)) { g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); return FALSE; } if (fflush (fp) == EOF) { g_warning ("Error writing to Xauthority file: %s", g_strerror (errno)); return FALSE; } return TRUE; } static void add_local_user_to_xhost (Display *xdisplay) { XHostAddress host_entry; XServerInterpretedAddress siaddr; siaddr.type = (char *) "localuser"; siaddr.typelength = strlen (siaddr.type); siaddr.value = (char *) g_get_user_name(); siaddr.valuelength = strlen (siaddr.value); host_entry.family = FamilyServerInterpreted; host_entry.address = (char *) &siaddr; XAddHost (xdisplay, &host_entry); } static void on_init_x11_cb (MetaDisplay *display, GAsyncResult *result, gpointer user_data) { g_autoptr (GError) error = NULL; if (!meta_display_init_x11_finish (display, result, &error)) g_warning ("Failed to initialize X11 display: %s\n", error->message); } static gboolean on_displayfd_ready (int fd, GIOCondition condition, gpointer user_data) { GTask *task = user_data; /* The server writes its display name to the displayfd * socket when it's ready. We don't care about the data * in the socket, just that it wrote something, since * that means it's ready. */ g_task_return_boolean (task, TRUE); g_object_unref (task); return G_SOURCE_REMOVE; } void meta_xwayland_start_xserver (MetaXWaylandManager *manager, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { int xwayland_client_fd[2]; int displayfd[2]; g_autoptr(GSubprocessLauncher) launcher = NULL; GSubprocessFlags flags; GError *error = NULL; g_autoptr (GTask) task = NULL; task = g_task_new (NULL, cancellable, callback, user_data); g_task_set_source_tag (task, meta_xwayland_start_xserver); g_task_set_task_data (task, manager, NULL); /* We want xwayland to be a wayland client so we make a socketpair to setup a * wayland protocol connection. */ if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, xwayland_client_fd) < 0) { g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (errno), "xwayland_client_fd socketpair failed"); return; } if (socketpair (AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0, displayfd) < 0) { g_task_return_new_error (task, G_IO_ERROR, g_io_error_from_errno (errno), "displayfd socketpair failed"); return; } /* xwayland, please. */ flags = G_SUBPROCESS_FLAGS_NONE; if (getenv ("XWAYLAND_STFU")) { flags |= G_SUBPROCESS_FLAGS_STDOUT_SILENCE; flags |= G_SUBPROCESS_FLAGS_STDERR_SILENCE; } launcher = g_subprocess_launcher_new (flags); g_subprocess_launcher_take_fd (launcher, xwayland_client_fd[1], 3); g_subprocess_launcher_take_fd (launcher, manager->public_connection.abstract_fd, 4); g_subprocess_launcher_take_fd (launcher, manager->public_connection.unix_fd, 5); g_subprocess_launcher_take_fd (launcher, displayfd[1], 6); g_subprocess_launcher_take_fd (launcher, manager->private_connection.abstract_fd, 7); g_subprocess_launcher_setenv (launcher, "WAYLAND_SOCKET", "3", TRUE); manager->proc = g_subprocess_launcher_spawn (launcher, &error, XWAYLAND_PATH, manager->public_connection.name, "-rootless", "-noreset", "-accessx", "-core", "-auth", manager->auth_file, "-listen", "4", "-listen", "5", "-displayfd", "6", #ifdef HAVE_XWAYLAND_INITFD "-initfd", "7", #else "-listen", "7", #endif NULL); if (!manager->proc) { g_task_return_error (task, error); return; } manager->xserver_died_cancellable = g_cancellable_new (); g_subprocess_wait_async (manager->proc, manager->xserver_died_cancellable, xserver_died, NULL); g_unix_fd_add (displayfd[0], G_IO_IN, on_displayfd_ready, g_steal_pointer (&task)); manager->client = wl_client_create (manager->wayland_display, xwayland_client_fd[0]); } gboolean meta_xwayland_start_xserver_finish (MetaXWaylandManager *manager, GAsyncResult *result, GError **error) { g_assert (g_task_get_source_tag (G_TASK (result)) == meta_xwayland_start_xserver); return g_task_propagate_boolean (G_TASK (result), error); } static gboolean xdisplay_connection_activity_cb (gint fd, GIOCondition cond, gpointer user_data) { MetaDisplay *display = meta_get_display (); meta_display_init_x11 (display, NULL, (GAsyncReadyCallback) on_init_x11_cb, NULL); return G_SOURCE_REMOVE; } static void meta_xwayland_stop_xserver_timeout (MetaXWaylandManager *manager) { if (manager->xserver_grace_period_id) return; manager->xserver_grace_period_id = g_timeout_add_seconds (10, shutdown_xwayland_cb, manager); } static void window_unmanaged_cb (MetaWindow *window, MetaXWaylandManager *manager) { manager->x11_windows = g_list_remove (manager->x11_windows, window); g_signal_handlers_disconnect_by_func (window, window_unmanaged_cb, manager); if (!manager->x11_windows) { meta_verbose ("All X11 windows gone, setting shutdown timeout"); meta_xwayland_stop_xserver_timeout (manager); } } static void window_created_cb (MetaDisplay *display, MetaWindow *window, MetaXWaylandManager *manager) { /* Ignore all internal windows */ if (!window->xwindow || meta_window_get_client_pid (window) == getpid ()) return; manager->x11_windows = g_list_prepend (manager->x11_windows, window); g_signal_connect (window, "unmanaged", G_CALLBACK (window_unmanaged_cb), manager); g_clear_handle_id (&manager->xserver_grace_period_id, g_source_remove); } static void meta_xwayland_stop_xserver (MetaXWaylandManager *manager) { if (manager->proc) g_subprocess_send_signal (manager->proc, SIGTERM); g_signal_handlers_disconnect_by_func (meta_get_display (), window_created_cb, manager); g_clear_object (&manager->xserver_died_cancellable); g_clear_object (&manager->proc); } gboolean meta_xwayland_init (MetaXWaylandManager *manager, struct wl_display *wl_display) { MetaDisplayPolicy policy; gboolean fatal; if (!manager->public_connection.name) { if (!choose_xdisplay (manager, &manager->public_connection)) return FALSE; if (!choose_xdisplay (manager, &manager->private_connection)) return FALSE; if (!prepare_auth_file (manager)) return FALSE; } else { if (!open_display_sockets (manager, manager->public_connection.display_index, &manager->public_connection.abstract_fd, &manager->public_connection.unix_fd, &fatal)) return FALSE; if (!open_display_sockets (manager, manager->private_connection.display_index, &manager->private_connection.abstract_fd, &manager->private_connection.unix_fd, &fatal)) return FALSE; } manager->wayland_display = wl_display; policy = meta_get_x11_display_policy (); if (policy == META_DISPLAY_POLICY_ON_DEMAND) { g_unix_fd_add (manager->public_connection.abstract_fd, G_IO_IN, xdisplay_connection_activity_cb, manager); } return TRUE; } static void on_x11_display_closing (MetaDisplay *display) { Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); meta_xwayland_shutdown_dnd (xdisplay); g_signal_handlers_disconnect_by_func (display, on_x11_display_closing, NULL); } /* To be called right after connecting */ void meta_xwayland_complete_init (MetaDisplay *display, Display *xdisplay) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaXWaylandManager *manager = &compositor->xwayland_manager; /* We install an X IO error handler in addition to the child watch, because after Xlib connects our child watch may not be called soon enough, and therefore we won't crash when X exits (and most important we won't reset the tty). */ XSetIOErrorHandler (x_io_error); g_signal_connect (display, "x11-display-closing", G_CALLBACK (on_x11_display_closing), NULL); meta_xwayland_init_dnd (xdisplay); add_local_user_to_xhost (xdisplay); if (meta_get_x11_display_policy () == META_DISPLAY_POLICY_ON_DEMAND) { meta_xwayland_stop_xserver_timeout (manager); g_signal_connect (meta_get_display (), "window-created", G_CALLBACK (window_created_cb), manager); } } static void meta_xwayland_connection_release (MetaXWaylandConnection *connection) { unlink (connection->lock_file); g_clear_pointer (&connection->lock_file, g_free); } void meta_xwayland_shutdown (MetaXWaylandManager *manager) { char path[256]; g_cancellable_cancel (manager->xserver_died_cancellable); snprintf (path, sizeof path, "/tmp/.X11-unix/X%d", manager->public_connection.display_index); unlink (path); snprintf (path, sizeof path, "/tmp/.X11-unix/X%d", manager->private_connection.display_index); unlink (path); g_clear_pointer (&manager->public_connection.name, g_free); g_clear_pointer (&manager->private_connection.name, g_free); meta_xwayland_connection_release (&manager->public_connection); meta_xwayland_connection_release (&manager->private_connection); if (manager->auth_file) { unlink (manager->auth_file); g_clear_pointer (&manager->auth_file, g_free); } } muffin-6.4.1/src/wayland/meta-wayland-data-source.c0000664000175000017500000003611414723361714021131 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include #include "wayland/meta-wayland-data-source.h" #include "wayland/meta-wayland-private.h" #define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) typedef struct _MetaWaylandDataSourcePrivate { struct wl_resource *resource; MetaWaylandDataOffer *offer; struct wl_array mime_types; gboolean has_target; uint32_t dnd_actions; enum wl_data_device_manager_dnd_action user_dnd_action; enum wl_data_device_manager_dnd_action current_dnd_action; MetaWaylandSeat *seat; guint actions_set : 1; guint in_ask : 1; guint drop_performed : 1; } MetaWaylandDataSourcePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandDataSource, meta_wayland_data_source, G_TYPE_OBJECT); static void meta_wayland_data_source_real_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); wl_data_source_send_send (priv->resource, mime_type, fd); close (fd); } static void meta_wayland_data_source_real_target (MetaWaylandDataSource *source, const gchar *mime_type) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); wl_data_source_send_target (priv->resource, mime_type); } static void meta_wayland_data_source_real_cancel (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (!priv->resource) return; wl_data_source_send_cancelled (priv->resource); } static void meta_wayland_data_source_real_action (MetaWaylandDataSource *source, enum wl_data_device_manager_dnd_action action) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (wl_resource_get_version (priv->resource) >= WL_DATA_SOURCE_ACTION_SINCE_VERSION) wl_data_source_send_action (priv->resource, action); } static void meta_wayland_data_source_real_drop_performed (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (wl_resource_get_version (priv->resource) >= WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) { priv->drop_performed = TRUE; wl_data_source_send_dnd_drop_performed (priv->resource); } } static void meta_wayland_data_source_real_drag_finished (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); enum wl_data_device_manager_dnd_action action; if (meta_wayland_data_source_get_in_ask (source)) { action = meta_wayland_data_source_get_current_action (source); meta_wayland_data_source_real_action (source, action); } if (wl_resource_get_version (priv->resource) >= WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) wl_data_source_send_dnd_finished (priv->resource); } static void meta_wayland_data_source_finalize (GObject *object) { MetaWaylandDataSource *source = META_WAYLAND_DATA_SOURCE (object); MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); char **pos; wl_array_for_each (pos, &priv->mime_types) g_free (*pos); wl_array_release (&priv->mime_types); G_OBJECT_CLASS (meta_wayland_data_source_parent_class)->finalize (object); } static void meta_wayland_data_source_init (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); wl_array_init (&priv->mime_types); priv->current_dnd_action = -1; priv->drop_performed = FALSE; } static void meta_wayland_data_source_class_init (MetaWaylandDataSourceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_data_source_finalize; klass->send = meta_wayland_data_source_real_send; klass->target = meta_wayland_data_source_real_target; klass->cancel = meta_wayland_data_source_real_cancel; klass->action = meta_wayland_data_source_real_action; klass->drop_performed = meta_wayland_data_source_real_drop_performed; klass->drag_finished = meta_wayland_data_source_real_drag_finished; } static void data_source_offer (struct wl_client *client, struct wl_resource *resource, const char *type) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); if (!meta_wayland_data_source_add_mime_type (source, type)) wl_resource_post_no_memory (resource); } static void data_source_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void data_source_set_actions (struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (priv->actions_set) { wl_resource_post_error (priv->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "cannot set actions more than once"); return; } if (dnd_actions & ~(ALL_ACTIONS)) { wl_resource_post_error (priv->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "invalid actions mask %x", dnd_actions); return; } if (meta_wayland_data_source_get_seat (source)) { wl_resource_post_error (priv->resource, WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, "invalid action change after " "wl_data_device.start_drag"); return; } meta_wayland_data_source_set_actions (source, dnd_actions); } static struct wl_data_source_interface data_source_interface = { data_source_offer, data_source_destroy, data_source_set_actions }; static void destroy_data_source (struct wl_resource *resource) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); meta_wayland_data_source_set_resource (source, NULL); g_object_unref (source); } MetaWaylandDataSource * meta_wayland_data_source_new (struct wl_resource *resource) { MetaWaylandDataSource *source = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE, NULL); MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); meta_wayland_data_source_set_resource (source, resource); wl_resource_set_implementation (resource, &data_source_interface, source, destroy_data_source); if (wl_resource_get_version (resource) < WL_DATA_SOURCE_ACTION_SINCE_VERSION) { priv->dnd_actions = priv->user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; } return source; } struct wl_resource * meta_wayland_data_source_get_resource (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->resource; } void meta_wayland_data_source_set_resource (MetaWaylandDataSource *source, struct wl_resource *resource) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->resource = resource; } gboolean meta_wayland_data_source_get_in_ask (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->in_ask; } void meta_wayland_data_source_update_in_ask (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->in_ask = priv->current_dnd_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; } void meta_wayland_data_source_target (MetaWaylandDataSource *source, const char *mime_type) { if (META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->target) META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->target (source, mime_type); } void meta_wayland_data_source_send (MetaWaylandDataSource *source, const char *mime_type, int fd) { META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->send (source, mime_type, fd); } gboolean meta_wayland_data_source_has_target (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->has_target; } void meta_wayland_data_source_set_seat (MetaWaylandDataSource *source, MetaWaylandSeat *seat) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->seat = seat; } MetaWaylandSeat * meta_wayland_data_source_get_seat (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->seat; } void meta_wayland_data_source_set_has_target (MetaWaylandDataSource *source, gboolean has_target) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->has_target = has_target; } struct wl_array * meta_wayland_data_source_get_mime_types (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private ((MetaWaylandDataSource *)source); return &priv->mime_types; } void meta_wayland_data_source_cancel (MetaWaylandDataSource *source) { META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->cancel (source); } gboolean meta_wayland_data_source_get_actions (MetaWaylandDataSource *source, uint32_t *dnd_actions) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (dnd_actions) *dnd_actions = priv->dnd_actions; return priv->actions_set; } enum wl_data_device_manager_dnd_action meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (!priv->seat) return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; return priv->user_dnd_action; } enum wl_data_device_manager_dnd_action meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->current_dnd_action; } void meta_wayland_data_source_set_current_offer (MetaWaylandDataSource *source, MetaWaylandDataOffer *offer) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->offer = offer; } MetaWaylandDataOffer * meta_wayland_data_source_get_current_offer (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->offer; } void meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source, enum wl_data_device_manager_dnd_action action) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); if (priv->current_dnd_action == action) return; priv->current_dnd_action = action; if (!meta_wayland_data_source_get_in_ask (source)) META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->action (source, action); } void meta_wayland_data_source_set_actions (MetaWaylandDataSource *source, uint32_t dnd_actions) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); priv->dnd_actions = dnd_actions; priv->actions_set = TRUE; } void meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source, uint32_t action) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); MetaWaylandDataOffer *offer; if (priv->user_dnd_action == action) return; priv->user_dnd_action = action; offer = meta_wayland_data_source_get_current_offer (source); if (offer) meta_wayland_data_offer_update_action (offer); } gboolean meta_wayland_data_source_get_drop_performed (MetaWaylandDataSource *source) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); return priv->drop_performed; } void meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source) { META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drop_performed (source); } void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source) { META_WAYLAND_DATA_SOURCE_GET_CLASS (source)->drag_finished (source); } gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const char *mime_type) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); char **pos; pos = wl_array_add (&priv->mime_types, sizeof (*pos)); if (pos) { *pos = g_strdup (mime_type); return *pos != NULL; } return FALSE; } gboolean meta_wayland_data_source_has_mime_type (MetaWaylandDataSource *source, const char *mime_type) { MetaWaylandDataSourcePrivate *priv = meta_wayland_data_source_get_instance_private (source); char **p; wl_array_for_each (p, &priv->mime_types) { if (g_strcmp0 (mime_type, *p) == 0) return TRUE; } return FALSE; } muffin-6.4.1/src/wayland/meta-wayland-data-device-primary.h0000664000175000017500000000417314723361714022556 0ustar fabiofabio/* * Copyright © 2008 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_DEVICE_PRIMARY_H #define META_WAYLAND_DATA_DEVICE_PRIMARY_H #include #include #include "clutter/clutter.h" #include "meta/meta-selection-source.h" #include "wayland/meta-wayland-data-offer.h" #include "wayland/meta-wayland-data-source.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandDataDevicePrimary { uint32_t serial; MetaWaylandDataSource *data_source; struct wl_list resource_list; struct wl_list focus_resource_list; struct wl_client *focus_client; guint selection_owner_signal_id; MetaSelectionSource *owner; }; void meta_wayland_data_device_primary_manager_init (MetaWaylandCompositor *compositor); void meta_wayland_data_device_primary_init (MetaWaylandDataDevicePrimary *data_device); void meta_wayland_data_device_primary_set_keyboard_focus (MetaWaylandDataDevicePrimary *data_device); #endif /* META_WAYLAND_DATA_DEVICE_PRIMARY_H */ muffin-6.4.1/src/wayland/meta-wayland-cursor-surface.h0000664000175000017500000000455414723361714021675 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_CURSOR_SURFACE_H #define META_WAYLAND_CURSOR_SURFACE_H #include "backends/meta-cursor-renderer.h" #include "wayland/meta-wayland-surface.h" struct _MetaWaylandCursorSurfaceClass { MetaWaylandSurfaceRoleClass parent_class; }; #define META_TYPE_WAYLAND_CURSOR_SURFACE (meta_wayland_cursor_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandCursorSurface, meta_wayland_cursor_surface, META, WAYLAND_CURSOR_SURFACE, MetaWaylandSurfaceRole); MetaCursorSprite * meta_wayland_cursor_surface_get_sprite (MetaWaylandCursorSurface *cursor_surface); void meta_wayland_cursor_surface_set_hotspot (MetaWaylandCursorSurface *cursor_surface, int hotspot_x, int hotspot_y); void meta_wayland_cursor_surface_get_hotspot (MetaWaylandCursorSurface *cursor_surface, int *hotspot_x, int *hotspot_y); void meta_wayland_cursor_surface_set_renderer (MetaWaylandCursorSurface *cursor_surface, MetaCursorRenderer *renderer); MetaCursorRenderer * meta_wayland_cursor_surface_get_renderer (MetaWaylandCursorSurface *cursor_surface); #endif /* META_WAYLAND_CURSOR_SURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-pointer-constraints.h0000664000175000017500000000343014723361714022747 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_WAYLAND_POINTER_CONSTRAINTS_H #define META_WAYLAND_POINTER_CONSTRAINTS_H #include #include "meta/window.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_POINTER_CONSTRAINT (meta_wayland_pointer_constraint_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint, META, WAYLAND_POINTER_CONSTRAINT, GObject); void meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor); MetaWaylandSeat * meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint); cairo_region_t * meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint); MetaWaylandSurface * meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint); #endif /* META_WAYLAND_POINTER_CONSTRAINTS_H */ muffin-6.4.1/src/wayland/meta-xwayland.h0000664000175000017500000000314114723361714017113 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_XWAYLAND_H #define META_XWAYLAND_H #include #include "core/util-private.h" #include "meta/types.h" #include "wayland/meta-wayland-types.h" META_EXPORT_TEST void meta_xwayland_override_display_number (int number); void meta_xwayland_handle_wl_surface_id (MetaWindow *window, guint32 surface_id); gboolean meta_xwayland_is_xwayland_surface (MetaWaylandSurface *surface); void meta_xwayland_handle_xwayland_grab (MetaWindow *window, gboolean allow); void meta_xwayland_associate_window_with_surface (MetaWindow *window, MetaWaylandSurface *surface); #endif /* META_XWAYLAND_H */ muffin-6.4.1/src/wayland/meta-wayland-versions.h0000664000175000017500000000442214723361714020574 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_VERSIONS_H #define META_WAYLAND_VERSIONS_H /* Protocol objects, will never change version */ /* #define META_WL_DISPLAY_VERSION 1 */ /* #define META_WL_REGISTRY_VERSION 1 */ #define META_WL_CALLBACK_VERSION 1 /* Not handled by mutter-wayland directly */ /* #define META_WL_SHM_VERSION 1 */ /* #define META_WL_SHM_POOL_VERSION 1 */ /* #define META_WL_DRM_VERSION 1 */ /* #define META_WL_BUFFER_VERSION 1 */ /* Global/master objects (version exported by wl_registry and negotiated through bind) */ #define META_WL_COMPOSITOR_VERSION 4 #define META_WL_DATA_DEVICE_MANAGER_VERSION 3 #define META_XDG_WM_BASE_VERSION 3 #define META_ZXDG_SHELL_V6_VERSION 1 #define META_WL_SHELL_VERSION 1 #define META_WL_SEAT_VERSION 5 #define META_WL_OUTPUT_VERSION 2 #define META_XSERVER_VERSION 1 #define META_GTK_SHELL1_VERSION 3 #define META_WL_SUBCOMPOSITOR_VERSION 1 #define META_ZWP_POINTER_GESTURES_V1_VERSION 1 #define META_ZXDG_EXPORTER_V1_VERSION 1 #define META_ZXDG_IMPORTER_V1_VERSION 1 #define META_ZWP_LINUX_DMABUF_V1_VERSION 3 #define META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION 1 #define META_ZXDG_OUTPUT_V1_VERSION 3 #define META_ZWP_XWAYLAND_KEYBOARD_GRAB_V1_VERSION 1 #define META_GTK_TEXT_INPUT_VERSION 1 #define META_ZWP_TEXT_INPUT_V3_VERSION 1 #define META_WP_VIEWPORTER_VERSION 1 #endif muffin-6.4.1/src/wayland/meta-wayland-dnd-surface.h0000664000175000017500000000230414723361714021114 0ustar fabiofabio/* * Copyright (C) 2015-2019 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_DND_SURFACE_H #define META_WAYLAND_DND_SURFACE_H #include "wayland/meta-wayland-actor-surface.h" #define META_TYPE_WAYLAND_SURFACE_ROLE_DND (meta_wayland_surface_role_dnd_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceRoleDND, meta_wayland_surface_role_dnd, META, WAYLAND_SURFACE_ROLE_DND, MetaWaylandActorSurface) #endif /* META_WAYLAND_DND_SURFACE_H */ muffin-6.4.1/src/wayland/meta-window-xwayland.c0000664000175000017500000002317414723361714020423 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "core/frame.h" #include "meta/meta-x11-errors.h" #include "x11/window-x11.h" #include "x11/window-x11-private.h" #include "x11/xprops.h" #include "wayland/meta-window-xwayland.h" #include "wayland/meta-wayland.h" enum { PROP_0, PROP_XWAYLAND_MAY_GRAB_KEYBOARD, PROP_LAST }; static GParamSpec *obj_props[PROP_LAST]; struct _MetaWindowXwayland { MetaWindowX11 parent; gboolean xwayland_may_grab_keyboard; int freeze_count; }; struct _MetaWindowXwaylandClass { MetaWindowX11Class parent_class; }; G_DEFINE_TYPE (MetaWindowXwayland, meta_window_xwayland, META_TYPE_WINDOW_X11) static void meta_window_xwayland_init (MetaWindowXwayland *window_xwayland) { } /** * meta_window_xwayland_adjust_fullscreen_monitor_rect: * * This function implements a workaround for X11 apps which use randr to change the * the monitor resolution, followed by setting _NET_WM_FULLSCREEN to make the * window-manager fullscreen them. * * Newer versions of Xwayland support the randr part of this by supporting randr * resolution change emulation in combination with using WPviewport to scale the * app's window (at the emulated resolution) to fill the entire monitor. * * Apps using randr in combination with NET_WM_STATE_FULLSCREEN expect the * fullscreen window to have the size of the emulated randr resolution since * when running on regular Xorg the resolution will actually be changed and * after that going fullscreen through NET_WM_STATE_FULLSCREEN will size * the window to be equal to the new resolution. * * We need to emulate this behavior for these apps to work correctly. * * Xwayland's emulated resolution is a per X11 client setting and Xwayland * will set a special _XWAYLAND_RANDR_EMU_MONITOR_RECTS property on the * toplevel windows of a client (and only those of that client), which has * changed the (emulated) resolution through a randr call. * * Here we check for that property and if it is set we adjust the fullscreen * monitor rect for this window to match the emulated resolution. * * Here is a step-by-step of such an app going fullscreen: * 1. App changes monitor resolution with randr. * 2. Xwayland sets the _XWAYLAND_RANDR_EMU_MONITOR_RECTS property on all the * apps current and future windows. This property contains the origin of the * monitor for which the emulated resolution is set and the emulated * resolution. * 3. App sets _NET_WM_FULLSCREEN. * 4. We check the property and adjust the app's fullscreen size to match * the emulated resolution. * 5. Xwayland sees a Window at monitor origin fully covering the emulated * monitor resolution. Xwayland sets a viewport making the emulated * resolution sized window cover the full actual monitor resolution. */ static void meta_window_xwayland_adjust_fullscreen_monitor_rect (MetaWindow *window, MetaRectangle *fs_monitor_rect) { MetaX11Display *x11_display = window->display->x11_display; MetaRectangle win_monitor_rect; cairo_rectangle_int_t *rects; uint32_t *list = NULL; int i, n_items = 0; if (!window->monitor) { g_warning ("MetaWindow does not have a monitor"); return; } win_monitor_rect = meta_logical_monitor_get_layout (window->monitor); if (!meta_prop_get_cardinal_list (x11_display, window->xwindow, x11_display->atom__XWAYLAND_RANDR_EMU_MONITOR_RECTS, &list, &n_items)) return; if (n_items % 4) { meta_verbose ("_XWAYLAND_RANDR_EMU_MONITOR_RECTS on %s has %d values which is not a multiple of 4", window->desc, n_items); g_free (list); return; } rects = (cairo_rectangle_int_t *) list; n_items = n_items / 4; for (i = 0; i < n_items; i++) { if (rects[i].x == win_monitor_rect.x && rects[i].y == win_monitor_rect.y) { fs_monitor_rect->width = rects[i].width; fs_monitor_rect->height = rects[i].height; break; } } g_free (list); } static void meta_window_xwayland_force_restore_shortcuts (MetaWindow *window, ClutterInputDevice *source) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); meta_wayland_compositor_restore_shortcuts (compositor, source); } static gboolean meta_window_xwayland_shortcuts_inhibited (MetaWindow *window, ClutterInputDevice *source) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); return meta_wayland_compositor_is_shortcuts_inhibited (compositor, source); } static void apply_allow_commits_x11_property (MetaWindowXwayland *xwayland_window, gboolean allow_commits) { MetaWindow *window = META_WINDOW (xwayland_window); MetaDisplay *display = window->display; MetaX11Display *x11_display = display->x11_display; Display *xdisplay = x11_display->xdisplay; MetaFrame *frame; Window xwin; guint32 property[1]; frame = meta_window_get_frame (window); if (!frame) xwin = window->xwindow; else xwin = meta_frame_get_xwindow (frame); if (!xwin) return; property[0] = !!allow_commits; meta_x11_error_trap_push (x11_display); XChangeProperty (xdisplay, xwin, x11_display->atom__XWAYLAND_ALLOW_COMMITS, XA_CARDINAL, 32, PropModeReplace, (guchar*) &property, 1); meta_x11_error_trap_pop (x11_display); XFlush (xdisplay); } static void meta_window_xwayland_freeze_commits (MetaWindow *window) { MetaWindowXwayland *xwayland_window = META_WINDOW_XWAYLAND (window); if (xwayland_window->freeze_count == 0) apply_allow_commits_x11_property (xwayland_window, FALSE); xwayland_window->freeze_count++; } static void meta_window_xwayland_thaw_commits (MetaWindow *window) { MetaWindowXwayland *xwayland_window = META_WINDOW_XWAYLAND (window); g_return_if_fail (xwayland_window->freeze_count > 0); xwayland_window->freeze_count--; if (xwayland_window->freeze_count > 0) return; apply_allow_commits_x11_property (xwayland_window, TRUE); } static gboolean meta_window_xwayland_always_update_shape (MetaWindow *window) { /* * On Xwayland, resizing a window will clear the corresponding Wayland * buffer to plain solid black. * * Therefore, to address the black shadows which sometimes show during * resize with Xwayland, we need to always update the window shape * regardless of the actual frozen state of the window actor. */ return TRUE; } static void meta_window_xwayland_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWindowXwayland *window = META_WINDOW_XWAYLAND (object); switch (prop_id) { case PROP_XWAYLAND_MAY_GRAB_KEYBOARD: g_value_set_boolean (value, window->xwayland_may_grab_keyboard); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_xwayland_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWindowXwayland *window = META_WINDOW_XWAYLAND (object); switch (prop_id) { case PROP_XWAYLAND_MAY_GRAB_KEYBOARD: window->xwayland_may_grab_keyboard = g_value_get_boolean (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_xwayland_class_init (MetaWindowXwaylandClass *klass) { MetaWindowClass *window_class = META_WINDOW_CLASS (klass); MetaWindowX11Class *window_x11_class = META_WINDOW_X11_CLASS (klass); GObjectClass *gobject_class = G_OBJECT_CLASS (klass); window_class->adjust_fullscreen_monitor_rect = meta_window_xwayland_adjust_fullscreen_monitor_rect; window_class->force_restore_shortcuts = meta_window_xwayland_force_restore_shortcuts; window_class->shortcuts_inhibited = meta_window_xwayland_shortcuts_inhibited; window_x11_class->freeze_commits = meta_window_xwayland_freeze_commits; window_x11_class->thaw_commits = meta_window_xwayland_thaw_commits; window_x11_class->always_update_shape = meta_window_xwayland_always_update_shape; gobject_class->get_property = meta_window_xwayland_get_property; gobject_class->set_property = meta_window_xwayland_set_property; obj_props[PROP_XWAYLAND_MAY_GRAB_KEYBOARD] = g_param_spec_boolean ("xwayland-may-grab-keyboard", "Xwayland may use keyboard grabs", "Whether the client may use Xwayland keyboard grabs on this window", FALSE, G_PARAM_READWRITE); g_object_class_install_properties (gobject_class, PROP_LAST, obj_props); } muffin-6.4.1/src/wayland/meta-wayland-data-source-primary-legacy.c0000664000175000017500000001002214723361714024042 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include #include "gtk-primary-selection-server-protocol.h" #include "wayland/meta-wayland-data-source-primary-legacy.h" typedef struct _MetaWaylandDataSourcePrimaryLegacy { MetaWaylandDataSource parent; } MetaWaylandDataSourcePrimaryLegacy; G_DEFINE_TYPE (MetaWaylandDataSourcePrimaryLegacy, meta_wayland_data_source_primary_legacy, META_TYPE_WAYLAND_DATA_SOURCE); static void primary_source_offer (struct wl_client *client, struct wl_resource *resource, const char *type) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); if (!meta_wayland_data_source_add_mime_type (source, type)) wl_resource_post_no_memory (resource); } static void primary_source_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static struct gtk_primary_selection_source_interface primary_source_interface = { primary_source_offer, primary_source_destroy, }; static void destroy_primary_source (struct wl_resource *resource) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); meta_wayland_data_source_set_resource (source, NULL); g_object_unref (source); } static void meta_wayland_data_source_primary_legacy_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { struct wl_resource *resource = meta_wayland_data_source_get_resource (source); gtk_primary_selection_source_send_send (resource, mime_type, fd); close (fd); } static void meta_wayland_data_source_primary_legacy_cancel (MetaWaylandDataSource *source) { struct wl_resource *resource = meta_wayland_data_source_get_resource (source); if (resource) gtk_primary_selection_source_send_cancelled (resource); } static void meta_wayland_data_source_primary_legacy_init (MetaWaylandDataSourcePrimaryLegacy *source_primary) { } static void meta_wayland_data_source_primary_legacy_class_init (MetaWaylandDataSourcePrimaryLegacyClass *klass) { MetaWaylandDataSourceClass *data_source_class = META_WAYLAND_DATA_SOURCE_CLASS (klass); data_source_class->send = meta_wayland_data_source_primary_legacy_send; data_source_class->cancel = meta_wayland_data_source_primary_legacy_cancel; } MetaWaylandDataSource * meta_wayland_data_source_primary_legacy_new (struct wl_resource *resource) { MetaWaylandDataSource *source_primary = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY, NULL); meta_wayland_data_source_set_resource (source_primary, resource); wl_resource_set_implementation (resource, &primary_source_interface, source_primary, destroy_primary_source); return source_primary; } muffin-6.4.1/src/wayland/meta-wayland-xdg-shell.h0000664000175000017500000000374014723361714020615 0ustar fabiofabio/* * Copyright (C) 2013-2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_XDG_SHELL_H #define META_WAYLAND_XDG_SHELL_H #include "wayland/meta-wayland-shell-surface.h" #define META_TYPE_WAYLAND_XDG_SURFACE (meta_wayland_xdg_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandXdgSurface, meta_wayland_xdg_surface, META, WAYLAND_XDG_SURFACE, MetaWaylandShellSurface) struct _MetaWaylandXdgSurfaceClass { MetaWaylandShellSurfaceClass parent_class; void (*shell_client_destroyed) (MetaWaylandXdgSurface *xdg_surface); void (*reset) (MetaWaylandXdgSurface *xdg_surface); }; #define META_TYPE_WAYLAND_XDG_TOPLEVEL (meta_wayland_xdg_toplevel_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandXdgToplevel, meta_wayland_xdg_toplevel, META, WAYLAND_XDG_TOPLEVEL, MetaWaylandXdgSurface); #define META_TYPE_WAYLAND_XDG_POPUP (meta_wayland_xdg_popup_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandXdgPopup, meta_wayland_xdg_popup, META, WAYLAND_XDG_POPUP, MetaWaylandXdgSurface); void meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_XDG_SHELL_H */ muffin-6.4.1/src/wayland/meta-wayland-dma-buf.h0000664000175000017500000000345014723361714020237 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl * Daniel Stone */ #ifndef META_WAYLAND_DMA_BUF_H #define META_WAYLAND_DMA_BUF_H #include #include #include "cogl/cogl.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_DMA_BUF_BUFFER (meta_wayland_dma_buf_buffer_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, META, WAYLAND_DMA_BUF_BUFFER, GObject); typedef struct _MetaWaylandDmaBufBuffer MetaWaylandDmaBufBuffer; gboolean meta_wayland_dma_buf_init (MetaWaylandCompositor *compositor); gboolean meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error); MetaWaylandDmaBufBuffer * meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer); #endif /* META_WAYLAND_DMA_BUF_H */ muffin-6.4.1/src/wayland/meta-xwayland-surface.h0000664000175000017500000000257414723361714020552 0ustar fabiofabio/* * Copyright (C) 2013 Intel Corporation * Copyright (C) 2013-2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_XWAYLAND_SURFACE_H #define META_XWAYLAND_SURFACE_H #include "meta/types.h" #include "wayland/meta-wayland-actor-surface.h" #define META_TYPE_XWAYLAND_SURFACE (meta_xwayland_surface_get_type ()) G_DECLARE_FINAL_TYPE (MetaXwaylandSurface, meta_xwayland_surface, META, XWAYLAND_SURFACE, MetaWaylandActorSurface) void meta_xwayland_surface_associate_with_window (MetaXwaylandSurface *xwayland_surface, MetaWindow *window); #endif /* META_XWAYLAND_SURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-shell-surface.h0000664000175000017500000000576414723361714021473 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_SHELL_SURFACE_H #define META_WAYLAND_SHELL_SURFACE_H #include "wayland/meta-wayland-actor-surface.h" #define META_TYPE_WAYLAND_SHELL_SURFACE (meta_wayland_shell_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandShellSurface, meta_wayland_shell_surface, META, WAYLAND_SHELL_SURFACE, MetaWaylandActorSurface) struct _MetaWaylandShellSurfaceClass { MetaWaylandActorSurfaceClass parent_class; void (*configure) (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration); void (*managed) (MetaWaylandShellSurface *shell_surface, MetaWindow *window); void (*ping) (MetaWaylandShellSurface *shell_surface, uint32_t serial); void (*close) (MetaWaylandShellSurface *shell_surface); }; void meta_wayland_shell_surface_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration); void meta_wayland_shell_surface_ping (MetaWaylandShellSurface *shell_surface, uint32_t serial); void meta_wayland_shell_surface_close (MetaWaylandShellSurface *shell_surface); void meta_wayland_shell_surface_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window); void meta_wayland_shell_surface_calculate_geometry (MetaWaylandShellSurface *shell_surface, MetaRectangle *out_geometry); void meta_wayland_shell_surface_determine_geometry (MetaWaylandShellSurface *shell_surface, MetaRectangle *set_geometry, MetaRectangle *out_geometry); void meta_wayland_shell_surface_set_window (MetaWaylandShellSurface *shell_surface, MetaWindow *window); void meta_wayland_shell_surface_destroy_window (MetaWaylandShellSurface *shell_surface); #endif /* META_WAYLAND_SHELL_SURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-window-configuration.c0000664000175000017500000000565214723361714023101 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-window-configuration.h" static uint32_t global_serial_counter = 0; MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new (int x, int y, int width, int height, int scale, MetaMoveResizeFlags flags, MetaGravity gravity) { MetaWaylandWindowConfiguration *configuration; configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { .serial = ++global_serial_counter, .has_position = TRUE, .x = x, .y = y, .has_size = TRUE, .width = width, .height = height, .scale = scale, .gravity = gravity, .flags = flags, }; return configuration; } MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_relative (int rel_x, int rel_y, int width, int height, int scale) { MetaWaylandWindowConfiguration *configuration; configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { .serial = ++global_serial_counter, .has_relative_position = TRUE, .rel_x = rel_x, .rel_y = rel_y, .has_size = TRUE, .width = width, .height = height, .scale = scale, }; return configuration; } MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (void) { MetaWaylandWindowConfiguration *configuration; configuration = g_new0 (MetaWaylandWindowConfiguration, 1); *configuration = (MetaWaylandWindowConfiguration) { .serial = ++global_serial_counter, .scale = 1, }; return configuration; } void meta_wayland_window_configuration_free (MetaWaylandWindowConfiguration *configuration) { g_free (configuration); } muffin-6.4.1/src/wayland/meta-cursor-sprite-wayland.c0000664000175000017500000000400614723361714021536 0ustar fabiofabio/* * Copyright 2015, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include "wayland/meta-cursor-sprite-wayland.h" struct _MetaCursorSpriteWayland { MetaCursorSprite parent; MetaWaylandSurface *surface; }; G_DEFINE_TYPE (MetaCursorSpriteWayland, meta_cursor_sprite_wayland, META_TYPE_CURSOR_SPRITE) static void meta_cursor_sprite_wayland_realize_texture (MetaCursorSprite *sprite) { } static gboolean meta_cursor_sprite_wayland_is_animated (MetaCursorSprite *sprite) { return FALSE; } MetaCursorSpriteWayland * meta_cursor_sprite_wayland_new (MetaWaylandSurface *surface) { MetaCursorSpriteWayland *sprite_wayland; sprite_wayland = g_object_new (META_TYPE_CURSOR_SPRITE_WAYLAND, NULL); sprite_wayland->surface = surface; return sprite_wayland; } MetaWaylandBuffer * meta_cursor_sprite_wayland_get_buffer (MetaCursorSpriteWayland *sprite_wayland) { return meta_wayland_surface_get_buffer (sprite_wayland->surface); } static void meta_cursor_sprite_wayland_init (MetaCursorSpriteWayland *sprite_wayland) { } static void meta_cursor_sprite_wayland_class_init (MetaCursorSpriteWaylandClass *klass) { MetaCursorSpriteClass *cursor_sprite_class = META_CURSOR_SPRITE_CLASS (klass); cursor_sprite_class->realize_texture = meta_cursor_sprite_wayland_realize_texture; cursor_sprite_class->is_animated = meta_cursor_sprite_wayland_is_animated; } muffin-6.4.1/src/wayland/meta-wayland-inhibit-shortcuts.c0000664000175000017500000001615614723361714022410 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Olivier Fourdan */ #include "config.h" #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-inhibit-shortcuts.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "keyboard-shortcuts-inhibit-unstable-v1-server-protocol.h" struct _MetaWaylandKeyboardShotscutsInhibit { MetaWaylandSurface *surface; MetaWaylandSeat *seat; gulong inhibit_shortcut_handler; gulong restore_shortcut_handler; gulong surface_destroyed_handler; struct wl_resource *resource; }; static void zwp_keyboard_shortcuts_inhibit_destructor (struct wl_resource *resource) { MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit; shortcut_inhibit = wl_resource_get_user_data (resource); if (shortcut_inhibit->surface) { meta_wayland_surface_cancel_inhibit_shortcuts_dialog (shortcut_inhibit->surface); g_clear_signal_handler (&shortcut_inhibit->surface_destroyed_handler, shortcut_inhibit->surface); g_clear_signal_handler (&shortcut_inhibit->inhibit_shortcut_handler, shortcut_inhibit->surface); g_clear_signal_handler (&shortcut_inhibit->restore_shortcut_handler, shortcut_inhibit->surface); meta_wayland_surface_restore_shortcuts (shortcut_inhibit->surface, shortcut_inhibit->seat); } g_free (shortcut_inhibit); } static void zwp_keyboard_shortcuts_inhibit_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface meta_keyboard_shortcuts_inhibit_interface = { zwp_keyboard_shortcuts_inhibit_destroy, }; static void surface_destroyed_cb (MetaWaylandSurface *surface, MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit) { shortcut_inhibit->surface = NULL; shortcut_inhibit->seat = NULL; } static void shortcuts_inhibited_cb (MetaWaylandSurface *surface, MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit) { zwp_keyboard_shortcuts_inhibitor_v1_send_active (shortcut_inhibit->resource); } static void shortcuts_restored_cb (MetaWaylandSurface *surface, MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit) { zwp_keyboard_shortcuts_inhibitor_v1_send_inactive (shortcut_inhibit->resource); } static void zwp_keyboard_shortcuts_inhibit_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void zwp_keyboard_shortcuts_inhibit_manager_inhibit_shortcuts (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *seat_resource) { MetaWaylandKeyboardShotscutsInhibit *shortcut_inhibit; MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); struct wl_resource *keyboard_shortcuts_inhibit_resource; keyboard_shortcuts_inhibit_resource = wl_resource_create (client, &zwp_keyboard_shortcuts_inhibitor_v1_interface, META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION, id); shortcut_inhibit = g_new0 (MetaWaylandKeyboardShotscutsInhibit, 1); shortcut_inhibit->surface = surface; shortcut_inhibit->seat = seat; shortcut_inhibit->resource = keyboard_shortcuts_inhibit_resource; shortcut_inhibit->inhibit_shortcut_handler = g_signal_connect (surface, "shortcuts-inhibited", G_CALLBACK (shortcuts_inhibited_cb), shortcut_inhibit); shortcut_inhibit->restore_shortcut_handler = g_signal_connect (surface, "shortcuts-restored", G_CALLBACK (shortcuts_restored_cb), shortcut_inhibit); shortcut_inhibit->surface_destroyed_handler = g_signal_connect (surface, "destroy", G_CALLBACK (surface_destroyed_cb), shortcut_inhibit); /* Cannot grant shortcuts to a surface without any window */ if (meta_wayland_surface_get_toplevel_window (surface)) meta_wayland_surface_show_inhibit_shortcuts_dialog (surface, seat); wl_resource_set_implementation (keyboard_shortcuts_inhibit_resource, &meta_keyboard_shortcuts_inhibit_interface, shortcut_inhibit, zwp_keyboard_shortcuts_inhibit_destructor); } static const struct zwp_keyboard_shortcuts_inhibit_manager_v1_interface meta_keyboard_shortcuts_inhibit_manager_interface = { zwp_keyboard_shortcuts_inhibit_manager_destroy, zwp_keyboard_shortcuts_inhibit_manager_inhibit_shortcuts, }; static void bind_keyboard_shortcuts_inhibit (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION, id); wl_resource_set_implementation (resource, &meta_keyboard_shortcuts_inhibit_manager_interface, NULL, NULL); } gboolean meta_wayland_keyboard_shortcuts_inhibit_init (MetaWaylandCompositor *compositor) { return (wl_global_create (compositor->wayland_display, &zwp_keyboard_shortcuts_inhibit_manager_v1_interface, META_ZWP_KEYBOARD_SHORTCUTS_INHIBIT_V1_VERSION, NULL, bind_keyboard_shortcuts_inhibit) != NULL); } muffin-6.4.1/src/wayland/meta-wayland-data-source-primary.h0000664000175000017500000000341214723361714022612 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_SOURCE_PRIMARY_H #define META_WAYLAND_DATA_SOURCE_PRIMARY_H #include "meta-wayland-data-source.h" #define META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY (meta_wayland_data_source_primary_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandDataSourcePrimary, meta_wayland_data_source_primary, META, WAYLAND_DATA_SOURCE_PRIMARY, MetaWaylandDataSource); MetaWaylandDataSource * meta_wayland_data_source_primary_new (struct wl_resource *resource); #endif /* META_WAYLAND_DATA_SOURCE_PRIMARY_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-seat.c0000664000175000017500000004170014723361714021124 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet-tool.h" #include "wayland/meta-wayland-tablet.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void notify_tool_added (MetaWaylandTabletSeat *tablet_seat, struct wl_resource *client_resource, MetaWaylandTabletTool *tool) { struct wl_resource *tool_resource; struct wl_client *client; client = wl_resource_get_client (client_resource); tool_resource = meta_wayland_tablet_tool_lookup_resource (tool, client); if (!tool_resource) return; zwp_tablet_seat_v2_send_tool_added (client_resource, tool_resource); } static void notify_tablet_added (MetaWaylandTabletSeat *tablet_seat, struct wl_resource *client_resource, ClutterInputDevice *device) { struct wl_resource *resource; MetaWaylandTablet *tablet; struct wl_client *client; tablet = g_hash_table_lookup (tablet_seat->tablets, device); if (!tablet) return; client = wl_resource_get_client (client_resource); if (meta_wayland_tablet_lookup_resource (tablet, client)) return; resource = meta_wayland_tablet_create_new_resource (tablet, client, client_resource, 0); if (!resource) return; zwp_tablet_seat_v2_send_tablet_added (client_resource, resource); meta_wayland_tablet_notify (tablet, resource); } static void broadcast_tablet_added (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { struct wl_resource *resource; wl_resource_for_each (resource, &tablet_seat->resource_list) { notify_tablet_added (tablet_seat, resource, device); } } static void notify_tablets (MetaWaylandTabletSeat *tablet_seat, struct wl_resource *client_resource) { ClutterInputDevice *device; GHashTableIter iter; g_hash_table_iter_init (&iter, tablet_seat->tablets); while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) notify_tablet_added (tablet_seat, client_resource, device); } static void notify_pad_added (MetaWaylandTabletSeat *tablet_seat, struct wl_resource *tablet_seat_resource, ClutterInputDevice *device) { struct wl_resource *resource; MetaWaylandTabletPad *pad; struct wl_client *client; pad = g_hash_table_lookup (tablet_seat->pads, device); if (!pad) return; client = wl_resource_get_client (tablet_seat_resource); if (meta_wayland_tablet_pad_lookup_resource (pad, client)) return; resource = meta_wayland_tablet_pad_create_new_resource (pad, client, tablet_seat_resource, 0); if (!resource) return; zwp_tablet_seat_v2_send_pad_added (tablet_seat_resource, resource); meta_wayland_tablet_pad_notify (pad, resource); } static void broadcast_pad_added (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { struct wl_resource *resource; wl_resource_for_each (resource, &tablet_seat->resource_list) { notify_pad_added (tablet_seat, resource, device); } } static void notify_pads (MetaWaylandTabletSeat *tablet_seat, struct wl_resource *tablet_seat_resource) { ClutterInputDevice *device; GHashTableIter iter; g_hash_table_iter_init (&iter, tablet_seat->pads); while (g_hash_table_iter_next (&iter, (gpointer *) &device, NULL)) notify_pad_added (tablet_seat, tablet_seat_resource, device); } static gboolean is_tablet_device (ClutterInputDevice *device) { ClutterInputDeviceType device_type; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return FALSE; device_type = clutter_input_device_get_device_type (device); return (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE || device_type == CLUTTER_CURSOR_DEVICE); } static gboolean is_pad_device (ClutterInputDevice *device) { ClutterInputDeviceType device_type; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return FALSE; device_type = clutter_input_device_get_device_type (device); return device_type == CLUTTER_PAD_DEVICE; } static void meta_wayland_tablet_seat_device_added (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { MetaWaylandSurface *pad_focus = tablet_seat->seat->keyboard->focus_surface; if (is_tablet_device (device)) { MetaWaylandTablet *tablet; GList *pads, *l; tablet = meta_wayland_tablet_new (device, tablet_seat); g_hash_table_insert (tablet_seat->tablets, device, tablet); broadcast_tablet_added (tablet_seat, device); /* Because the insertion order is undefined, there might be already * pads that are logically paired to this tablet. Look those up and * refocus them. */ pads = meta_wayland_tablet_seat_lookup_paired_pads (tablet_seat, tablet); for (l = pads; l; l = l->next) meta_wayland_tablet_pad_set_focus (l->data, pad_focus); g_list_free (pads); } else if (is_pad_device (device)) { MetaWaylandTabletPad *pad; pad = meta_wayland_tablet_pad_new (device, tablet_seat); g_hash_table_insert (tablet_seat->pads, device, pad); broadcast_pad_added (tablet_seat, device); meta_wayland_tablet_pad_set_focus (pad, pad_focus); } } static void meta_wayland_tablet_seat_device_removed (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { g_hash_table_remove (tablet_seat->tablets, device); g_hash_table_remove (tablet_seat->pads, device); } static void tablet_seat_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_seat_v2_interface tablet_seat_interface = { tablet_seat_destroy }; MetaWaylandTabletSeat * meta_wayland_tablet_seat_new (MetaWaylandTabletManager *manager, MetaWaylandSeat *seat) { MetaWaylandTabletSeat *tablet_seat; GList *devices, *l; tablet_seat = g_slice_new0 (MetaWaylandTabletSeat); tablet_seat->manager = manager; tablet_seat->seat = seat; tablet_seat->clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); tablet_seat->tablets = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_tablet_free); tablet_seat->tools = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_tablet_tool_free); tablet_seat->pads = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_tablet_pad_free); wl_list_init (&tablet_seat->resource_list); g_signal_connect_swapped (tablet_seat->clutter_seat, "device-added", G_CALLBACK (meta_wayland_tablet_seat_device_added), tablet_seat); g_signal_connect_swapped (tablet_seat->clutter_seat, "device-removed", G_CALLBACK (meta_wayland_tablet_seat_device_removed), tablet_seat); devices = clutter_seat_list_devices (tablet_seat->clutter_seat); for (l = devices; l; l = l->next) meta_wayland_tablet_seat_device_added (tablet_seat, l->data); g_list_free (devices); return tablet_seat; } void meta_wayland_tablet_seat_free (MetaWaylandTabletSeat *tablet_seat) { struct wl_resource *resource, *next; wl_resource_for_each_safe (resource, next, &tablet_seat->resource_list) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_signal_handlers_disconnect_by_data (tablet_seat->clutter_seat, tablet_seat); g_hash_table_destroy (tablet_seat->tablets); g_hash_table_destroy (tablet_seat->tools); g_hash_table_destroy (tablet_seat->pads); g_slice_free (MetaWaylandTabletSeat, tablet_seat); } struct wl_resource * meta_wayland_tablet_seat_create_new_resource (MetaWaylandTabletSeat *tablet_seat, struct wl_client *client, struct wl_resource *manager_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_seat_v2_interface, wl_resource_get_version (manager_resource), id); wl_resource_set_implementation (resource, &tablet_seat_interface, tablet_seat, unbind_resource); wl_resource_set_user_data (resource, tablet_seat); wl_list_insert (&tablet_seat->resource_list, wl_resource_get_link (resource)); /* Notify client of all available tablets/pads */ notify_tablets (tablet_seat, resource); notify_pads (tablet_seat, resource); return resource; } struct wl_resource * meta_wayland_tablet_seat_lookup_resource (MetaWaylandTabletSeat *tablet_seat, struct wl_client *client) { return wl_resource_find_for_client (&tablet_seat->resource_list, client); } MetaWaylandTablet * meta_wayland_tablet_seat_lookup_tablet (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { return g_hash_table_lookup (tablet_seat->tablets, device); } MetaWaylandTabletTool * meta_wayland_tablet_seat_lookup_tool (MetaWaylandTabletSeat *tablet_seat, ClutterInputDeviceTool *tool) { return g_hash_table_lookup (tablet_seat->tools, tool); } MetaWaylandTabletPad * meta_wayland_tablet_seat_lookup_pad (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device) { return g_hash_table_lookup (tablet_seat->pads, device); } static MetaWaylandTabletTool * meta_wayland_tablet_seat_ensure_tool (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device, ClutterInputDeviceTool *device_tool) { MetaWaylandTabletTool *tool; tool = g_hash_table_lookup (tablet_seat->tools, device_tool); if (!tool) { tool = meta_wayland_tablet_tool_new (tablet_seat, device, device_tool); g_hash_table_insert (tablet_seat->tools, device_tool, tool); } return tool; } void meta_wayland_tablet_seat_update (MetaWaylandTabletSeat *tablet_seat, const ClutterEvent *event) { ClutterInputDevice *device; ClutterInputDeviceTool *device_tool; MetaWaylandTabletTool *tool = NULL; MetaWaylandTabletPad *pad = NULL; device = clutter_event_get_source_device (event); switch (event->type) { case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_MOTION: device_tool = clutter_event_get_device_tool (event); if (device && device_tool) tool = meta_wayland_tablet_seat_ensure_tool (tablet_seat, device, device_tool); if (!tool) return; meta_wayland_tablet_tool_update (tool, event); break; case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_RING: case CLUTTER_PAD_STRIP: pad = g_hash_table_lookup (tablet_seat->pads, device); if (!pad) return; return meta_wayland_tablet_pad_update (pad, event); default: break; } } gboolean meta_wayland_tablet_seat_handle_event (MetaWaylandTabletSeat *tablet_seat, const ClutterEvent *event) { ClutterInputDeviceTool *device_tool; MetaWaylandTabletTool *tool = NULL; MetaWaylandTabletPad *pad = NULL; switch (event->type) { case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_MOTION: device_tool = clutter_event_get_device_tool (event); if (device_tool) tool = g_hash_table_lookup (tablet_seat->tools, device_tool); if (!tool) return CLUTTER_EVENT_PROPAGATE; meta_wayland_tablet_tool_handle_event (tool, event); return CLUTTER_EVENT_PROPAGATE; case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_RING: case CLUTTER_PAD_STRIP: pad = g_hash_table_lookup (tablet_seat->pads, clutter_event_get_source_device (event)); if (!pad) return CLUTTER_EVENT_PROPAGATE; return meta_wayland_tablet_pad_handle_event (pad, event); default: return CLUTTER_EVENT_STOP; } } void meta_wayland_tablet_seat_notify_tool (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTabletTool *tool, struct wl_client *client) { struct wl_resource *resource; resource = wl_resource_find_for_client (&tablet_seat->resource_list, client); if (resource) notify_tool_added (tablet_seat, resource, tool); } static GList * lookup_grouped_devices (ClutterInputDevice *device, ClutterInputDeviceType type) { ClutterSeat *clutter_seat; GList *devices, *l; GList *group = NULL; clutter_seat = clutter_input_device_get_seat (device); devices = clutter_seat_list_devices (clutter_seat); for (l = devices; l; l = l->next) { if (l->data == device) continue; if (clutter_input_device_get_device_type (l->data) != type) continue; if (!clutter_input_device_is_grouped (device, l->data)) continue; group = g_list_prepend (group, l->data); } g_list_free (devices); return group; } MetaWaylandTablet * meta_wayland_tablet_seat_lookup_paired_tablet (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTabletPad *pad) { MetaWaylandTablet *tablet; GList *devices; devices = lookup_grouped_devices (pad->device, CLUTTER_TABLET_DEVICE); if (!devices) return NULL; /* We only accept one device here */ g_warn_if_fail (!devices->next); tablet = meta_wayland_tablet_seat_lookup_tablet (pad->tablet_seat, devices->data); g_list_free (devices); return tablet; } GList * meta_wayland_tablet_seat_lookup_paired_pads (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTablet *tablet) { GList *l, *devices, *pads = NULL; MetaWaylandTabletPad *pad; devices = lookup_grouped_devices (tablet->device, CLUTTER_PAD_DEVICE); for (l = devices; l; l = l->next) { pad = meta_wayland_tablet_seat_lookup_pad (tablet_seat, l->data); if (pad) pads = g_list_prepend (pads, pad); } return pads; } void meta_wayland_tablet_seat_set_pad_focus (MetaWaylandTabletSeat *tablet_seat, MetaWaylandSurface *surface) { MetaWaylandTabletPad *pad; GHashTableIter iter; g_hash_table_iter_init (&iter, tablet_seat->pads); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &pad)) meta_wayland_tablet_pad_set_focus (pad, surface); } gboolean meta_wayland_tablet_seat_can_popup (MetaWaylandTabletSeat *tablet_seat, uint32_t serial) { MetaWaylandTabletTool *tool; GHashTableIter iter; g_hash_table_iter_init (&iter, tablet_seat->tools); while (g_hash_table_iter_next (&iter, NULL, (gpointer *) &tool)) { if (meta_wayland_tablet_tool_can_popup (tool, serial)) return TRUE; } return FALSE; } muffin-6.4.1/src/wayland/meta-wayland-data-source.h0000664000175000017500000001306514723361714021136 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_SOURCE_H #define META_WAYLAND_DATA_SOURCE_H #include #include #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_DATA_SOURCE (meta_wayland_data_source_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandDataSource, meta_wayland_data_source, META, WAYLAND_DATA_SOURCE, GObject) typedef struct _MetaWaylandDataSourceClass MetaWaylandDataSourceClass; struct _MetaWaylandDataSourceClass { GObjectClass parent_class; void (* send) (MetaWaylandDataSource *source, const gchar *mime_type, gint fd); void (* target) (MetaWaylandDataSource *source, const gchar *mime_type); void (* cancel) (MetaWaylandDataSource *source); void (* action) (MetaWaylandDataSource *source, uint32_t action); void (* drop_performed) (MetaWaylandDataSource *source); void (* drag_finished) (MetaWaylandDataSource *source); }; MetaWaylandDataSource * meta_wayland_data_source_new (struct wl_resource *resource); struct wl_resource * meta_wayland_data_source_get_resource (MetaWaylandDataSource *source); void meta_wayland_data_source_set_resource (MetaWaylandDataSource *source, struct wl_resource *resource); gboolean meta_wayland_data_source_get_in_ask (MetaWaylandDataSource *source); void meta_wayland_data_source_update_in_ask (MetaWaylandDataSource *source); void meta_wayland_data_source_target (MetaWaylandDataSource *source, const char *mime_type); void meta_wayland_data_source_send (MetaWaylandDataSource *source, const char *mime_type, int fd); void meta_wayland_data_source_cancel (MetaWaylandDataSource *source); gboolean meta_wayland_data_source_has_target (MetaWaylandDataSource *source); void meta_wayland_data_source_set_seat (MetaWaylandDataSource *source, MetaWaylandSeat *seat); MetaWaylandSeat * meta_wayland_data_source_get_seat (MetaWaylandDataSource *source); void meta_wayland_data_source_set_has_target (MetaWaylandDataSource *source, gboolean has_target); struct wl_array * meta_wayland_data_source_get_mime_types (MetaWaylandDataSource *source); gboolean meta_wayland_data_source_add_mime_type (MetaWaylandDataSource *source, const gchar *mime_type); gboolean meta_wayland_data_source_has_mime_type (MetaWaylandDataSource *source, const char *mime_type); gboolean meta_wayland_data_source_get_actions (MetaWaylandDataSource *source, uint32_t *dnd_actions); void meta_wayland_data_source_set_actions (MetaWaylandDataSource *source, uint32_t dnd_actions); enum wl_data_device_manager_dnd_action meta_wayland_data_source_get_current_action (MetaWaylandDataSource *source); void meta_wayland_data_source_set_current_action (MetaWaylandDataSource *source, enum wl_data_device_manager_dnd_action action); enum wl_data_device_manager_dnd_action meta_wayland_data_source_get_user_action (MetaWaylandDataSource *source); void meta_wayland_data_source_set_user_action (MetaWaylandDataSource *source, uint32_t action); MetaWaylandDataOffer * meta_wayland_data_source_get_current_offer (MetaWaylandDataSource *source); void meta_wayland_data_source_set_current_offer (MetaWaylandDataSource *source, MetaWaylandDataOffer *offer); gboolean meta_wayland_data_source_get_drop_performed (MetaWaylandDataSource *source); void meta_wayland_data_source_notify_drop_performed (MetaWaylandDataSource *source); void meta_wayland_data_source_notify_finish (MetaWaylandDataSource *source); #endif /* META_WAYLAND_DATA_SOURCE_H */ muffin-6.4.1/src/wayland/meta-selection-source-wayland-private.h0000664000175000017500000000262514723361714023662 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_SELECTION_SOURCE_WAYLAND_H #define META_SELECTION_SOURCE_WAYLAND_H #include #include "meta/meta-selection-source.h" #include "wayland/meta-wayland-data-device.h" #define META_TYPE_SELECTION_SOURCE_WAYLAND (meta_selection_source_wayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaSelectionSourceWayland, meta_selection_source_wayland, META, SELECTION_SOURCE_WAYLAND, MetaSelectionSource) MetaSelectionSource * meta_selection_source_wayland_new (MetaWaylandDataSource *source); #endif /* META_SELECTION_SOURCE_WAYLAND_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-ring.c0000664000175000017500000001371614723361714021677 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-tablet-pad-ring.h" #include #include #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-pad-group.h" #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } MetaWaylandTabletPadRing * meta_wayland_tablet_pad_ring_new (MetaWaylandTabletPad *pad) { MetaWaylandTabletPadRing *ring; ring = g_slice_new0 (MetaWaylandTabletPadRing); wl_list_init (&ring->resource_list); wl_list_init (&ring->focus_resource_list); ring->pad = pad; return ring; } void meta_wayland_tablet_pad_ring_free (MetaWaylandTabletPadRing *ring) { struct wl_resource *resource, *next; wl_resource_for_each_safe (resource, next, &ring->resource_list) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_free (ring->feedback); g_slice_free (MetaWaylandTabletPadRing, ring); } static void tablet_pad_ring_set_feedback (struct wl_client *client, struct wl_resource *resource, const char *str, uint32_t serial) { MetaWaylandTabletPadRing *ring = wl_resource_get_user_data (resource); if (ring->group->mode_switch_serial != serial) return; ring->feedback = g_strdup (str); } static void tablet_pad_ring_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_pad_ring_v2_interface ring_interface = { tablet_pad_ring_set_feedback, tablet_pad_ring_destroy, }; struct wl_resource * meta_wayland_tablet_pad_ring_create_new_resource (MetaWaylandTabletPadRing *ring, struct wl_client *client, struct wl_resource *group_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_pad_ring_v2_interface, wl_resource_get_version (group_resource), id); wl_resource_set_implementation (resource, &ring_interface, ring, unbind_resource); wl_resource_set_user_data (resource, ring); wl_list_insert (&ring->resource_list, wl_resource_get_link (resource)); return resource; } gboolean meta_wayland_tablet_pad_ring_handle_event (MetaWaylandTabletPadRing *ring, const ClutterEvent *event) { struct wl_list *focus_resources = &ring->focus_resource_list; enum zwp_tablet_pad_ring_v2_source source; gboolean source_known = FALSE; struct wl_resource *resource; if (wl_list_empty (focus_resources)) return FALSE; if (event->type != CLUTTER_PAD_RING) return FALSE; if (event->pad_ring.ring_source == CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER) { source = ZWP_TABLET_PAD_RING_V2_SOURCE_FINGER; source_known = TRUE; } wl_resource_for_each (resource, focus_resources) { gdouble angle = event->pad_ring.angle; if (source_known) zwp_tablet_pad_ring_v2_send_source (resource, source); if (angle >= 0) zwp_tablet_pad_ring_v2_send_angle (resource, wl_fixed_from_double (angle)); else zwp_tablet_pad_ring_v2_send_stop (resource); zwp_tablet_pad_ring_v2_send_frame (resource, clutter_event_get_time (event)); } return TRUE; } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } void meta_wayland_tablet_pad_ring_sync_focus (MetaWaylandTabletPadRing *ring) { g_clear_pointer (&ring->feedback, g_free); if (!wl_list_empty (&ring->focus_resource_list)) { move_resources (&ring->resource_list, &ring->focus_resource_list); } if (ring->pad->focus_surface != NULL) { move_resources_for_client (&ring->focus_resource_list, &ring->resource_list, wl_resource_get_client (ring->pad->focus_surface->resource)); } } void meta_wayland_tablet_pad_ring_set_group (MetaWaylandTabletPadRing *ring, MetaWaylandTabletPadGroup *group) { /* Group is static, can only be set once */ g_assert (ring->group == NULL); ring->group = group; group->rings = g_list_append (group->rings, ring); } muffin-6.4.1/src/wayland/meta-wayland-gtk-shell.c0000664000175000017500000004060014723361714020607 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * 2013-2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-gtk-shell.h" #include "core/bell.h" #include "core/window-private.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-window-wayland.h" #include "gtk-shell-server-protocol.h" static GQuark quark_gtk_surface_data = 0; typedef struct _MetaWaylandGtkSurface { struct wl_resource *resource; MetaWaylandSurface *surface; gboolean is_modal; gulong configure_handler_id; } MetaWaylandGtkSurface; struct _MetaWaylandGtkShell { GObject parent; GList *shell_resources; uint32_t capabilities; }; G_DEFINE_TYPE (MetaWaylandGtkShell, meta_wayland_gtk_shell, G_TYPE_OBJECT) static void gtk_surface_destructor (struct wl_resource *resource) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); if (gtk_surface->surface) { g_object_steal_qdata (G_OBJECT (gtk_surface->surface), quark_gtk_surface_data); g_clear_signal_handler (>k_surface->configure_handler_id, gtk_surface->surface); } g_free (gtk_surface); } static void gtk_surface_set_dbus_properties (struct wl_client *client, struct wl_resource *resource, const char *application_id, const char *app_menu_path, const char *menubar_path, const char *window_object_path, const char *application_object_path, const char *unique_bus_name) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_set_gtk_dbus_properties (window, application_id, unique_bus_name, app_menu_path, menubar_path, application_object_path, window_object_path); } static void gtk_surface_set_modal (struct wl_client *client, struct wl_resource *resource) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (gtk_surface->is_modal) return; gtk_surface->is_modal = TRUE; meta_window_set_type (window, META_WINDOW_MODAL_DIALOG); } static void gtk_surface_unset_modal (struct wl_client *client, struct wl_resource *resource) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!gtk_surface->is_modal) return; gtk_surface->is_modal = FALSE; meta_window_set_type (window, META_WINDOW_NORMAL); } static void gtk_surface_present (struct wl_client *client, struct wl_resource *resource, uint32_t timestamp) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_APPLICATION, NULL); } static void gtk_surface_request_focus (struct wl_client *client, struct wl_resource *resource, const char *startup_id) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaDisplay *display = meta_get_display (); MetaStartupSequence *sequence = NULL; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (startup_id) sequence = meta_startup_notification_lookup_sequence (display->startup_notification, startup_id); if (sequence) { uint32_t timestamp; int32_t workspace_idx; workspace_idx = meta_startup_sequence_get_workspace (sequence); timestamp = meta_startup_sequence_get_timestamp (sequence); meta_startup_sequence_complete (sequence); meta_startup_notification_remove_sequence (display->startup_notification, sequence); if (workspace_idx >= 0) meta_window_change_workspace_by_index (window, workspace_idx, TRUE); meta_window_activate_full (window, timestamp, META_CLIENT_TYPE_APPLICATION, NULL); } else { meta_window_set_demands_attention (window); } } static const struct gtk_surface1_interface meta_wayland_gtk_surface_interface = { gtk_surface_set_dbus_properties, gtk_surface_set_modal, gtk_surface_unset_modal, gtk_surface_present, gtk_surface_request_focus, }; static void gtk_surface_surface_destroyed (MetaWaylandGtkSurface *gtk_surface) { wl_resource_set_implementation (gtk_surface->resource, NULL, NULL, NULL); gtk_surface->surface = NULL; } static void fill_edge_states (struct wl_array *states, MetaWindow *window) { uint32_t *s; if (window->edge_constraints.top != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_TOP; } if (window->edge_constraints.right != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_RIGHT; } if (window->edge_constraints.bottom != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_BOTTOM; } if (window->edge_constraints.left != META_EDGE_CONSTRAINT_MONITOR) { s = wl_array_add (states, sizeof *s); *s = GTK_SURFACE1_EDGE_CONSTRAINT_RESIZABLE_LEFT; } } static void send_configure_edges (MetaWaylandGtkSurface *gtk_surface, MetaWindow *window) { struct wl_array edge_states; wl_array_init (&edge_states); fill_edge_states (&edge_states, window); gtk_surface1_send_configure_edges (gtk_surface->resource, &edge_states); wl_array_release (&edge_states); } static void add_state_value (struct wl_array *states, enum gtk_surface1_state state) { uint32_t *s; s = wl_array_add (states, sizeof *s); *s = state; } static void fill_states (struct wl_array *states, MetaWindow *window, struct wl_resource *resource) { int version; version = wl_resource_get_version (resource); if (version < GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION && (window->tile_mode == META_TILE_LEFT || window->tile_mode == META_TILE_RIGHT)) add_state_value (states, GTK_SURFACE1_STATE_TILED); if (version >= GTK_SURFACE1_STATE_TILED_TOP_SINCE_VERSION && window->edge_constraints.top != META_EDGE_CONSTRAINT_NONE) add_state_value (states, GTK_SURFACE1_STATE_TILED_TOP); if (version >= GTK_SURFACE1_STATE_TILED_RIGHT_SINCE_VERSION && window->edge_constraints.right != META_EDGE_CONSTRAINT_NONE) add_state_value (states, GTK_SURFACE1_STATE_TILED_RIGHT); if (version >= GTK_SURFACE1_STATE_TILED_BOTTOM_SINCE_VERSION && window->edge_constraints.bottom != META_EDGE_CONSTRAINT_NONE) add_state_value (states, GTK_SURFACE1_STATE_TILED_BOTTOM); if (version >= GTK_SURFACE1_STATE_TILED_LEFT_SINCE_VERSION && window->edge_constraints.left != META_EDGE_CONSTRAINT_NONE) add_state_value (states, GTK_SURFACE1_STATE_TILED_LEFT); } static void send_configure (MetaWaylandGtkSurface *gtk_surface, MetaWindow *window) { struct wl_array states; wl_array_init (&states); fill_states (&states, window, gtk_surface->resource); gtk_surface1_send_configure (gtk_surface->resource, &states); wl_array_release (&states); } static void on_configure (MetaWaylandSurface *surface, MetaWaylandGtkSurface *gtk_surface) { MetaWindow *window; window = meta_wayland_surface_get_window (surface); send_configure (gtk_surface, window); if (wl_resource_get_version (gtk_surface->resource) >= GTK_SURFACE1_CONFIGURE_EDGES_SINCE_VERSION) send_configure_edges (gtk_surface, window); } static void gtk_shell_get_gtk_surface (struct wl_client *client, struct wl_resource *resource, guint32 id, struct wl_resource *surface_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandGtkSurface *gtk_surface; gtk_surface = g_object_get_qdata (G_OBJECT (surface), quark_gtk_surface_data); if (gtk_surface) { wl_resource_post_error (surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "gtk_shell::get_gtk_surface already requested"); return; } gtk_surface = g_new0 (MetaWaylandGtkSurface, 1); gtk_surface->surface = surface; gtk_surface->resource = wl_resource_create (client, >k_surface1_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (gtk_surface->resource, &meta_wayland_gtk_surface_interface, gtk_surface, gtk_surface_destructor); gtk_surface->configure_handler_id = g_signal_connect (surface, "configure", G_CALLBACK (on_configure), gtk_surface); g_object_set_qdata_full (G_OBJECT (surface), quark_gtk_surface_data, gtk_surface, (GDestroyNotify) gtk_surface_surface_destroyed); } static void gtk_shell_set_startup_id (struct wl_client *client, struct wl_resource *resource, const char *startup_id) { MetaStartupSequence *sequence; MetaDisplay *display; display = meta_get_display (); sequence = meta_startup_notification_lookup_sequence (display->startup_notification, startup_id); if (sequence) meta_startup_sequence_complete (sequence); } static void gtk_shell_system_bell (struct wl_client *client, struct wl_resource *resource, struct wl_resource *gtk_surface_resource) { MetaDisplay *display = meta_get_display (); if (gtk_surface_resource) { MetaWaylandGtkSurface *gtk_surface = wl_resource_get_user_data (gtk_surface_resource); MetaWaylandSurface *surface = gtk_surface->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_bell_notify (display, window); } else { meta_bell_notify (display, NULL); } } static void gtk_shell_notify_launch (struct wl_client *client, struct wl_resource *resource, const char *startup_id) { MetaDisplay *display = meta_get_display (); MetaStartupSequence *sequence; uint32_t timestamp; sequence = meta_startup_notification_lookup_sequence (display->startup_notification, startup_id); if (sequence) { g_warning ("Naughty client notified launch with duplicate startup_id '%s'", startup_id); return; } timestamp = meta_display_get_current_time_roundtrip (display); sequence = g_object_new (META_TYPE_STARTUP_SEQUENCE, "id", startup_id, "timestamp", timestamp, NULL); meta_startup_notification_add_sequence (display->startup_notification, sequence); g_object_unref (sequence); } static const struct gtk_shell1_interface meta_wayland_gtk_shell_interface = { gtk_shell_get_gtk_surface, gtk_shell_set_startup_id, gtk_shell_system_bell, gtk_shell_notify_launch, }; static void gtk_shell_destructor (struct wl_resource *resource) { MetaWaylandGtkShell *gtk_shell = wl_resource_get_user_data (resource); gtk_shell->shell_resources = g_list_remove (gtk_shell->shell_resources, resource); } static void bind_gtk_shell (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandGtkShell *gtk_shell = data; struct wl_resource *resource; resource = wl_resource_create (client, >k_shell1_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_gtk_shell_interface, data, gtk_shell_destructor); gtk_shell->shell_resources = g_list_prepend (gtk_shell->shell_resources, resource); gtk_shell1_send_capabilities (resource, gtk_shell->capabilities); } static void meta_wayland_gtk_shell_init (MetaWaylandGtkShell *gtk_shell) { } static void meta_wayland_gtk_shell_class_init (MetaWaylandGtkShellClass *klass) { quark_gtk_surface_data = g_quark_from_static_string ("-meta-wayland-gtk-shell-surface-data"); } static uint32_t calculate_capabilities (void) { uint32_t capabilities = 0; if (!meta_prefs_get_show_fallback_app_menu ()) capabilities = GTK_SHELL1_CAPABILITY_GLOBAL_APP_MENU; return capabilities; } static void prefs_changed (MetaPreference pref, gpointer user_data) { MetaWaylandGtkShell *gtk_shell = user_data; uint32_t new_capabilities; GList *l; if (pref != META_PREF_BUTTON_LAYOUT) return; new_capabilities = calculate_capabilities (); if (gtk_shell->capabilities == new_capabilities) return; gtk_shell->capabilities = new_capabilities; for (l = gtk_shell->shell_resources; l; l = l->next) { struct wl_resource *resource = l->data; gtk_shell1_send_capabilities (resource, gtk_shell->capabilities); } } static MetaWaylandGtkShell * meta_wayland_gtk_shell_new (MetaWaylandCompositor *compositor) { MetaWaylandGtkShell *gtk_shell; gtk_shell = g_object_new (META_TYPE_WAYLAND_GTK_SHELL, NULL); if (wl_global_create (compositor->wayland_display, >k_shell1_interface, META_GTK_SHELL1_VERSION, gtk_shell, bind_gtk_shell) == NULL) g_error ("Failed to register a global gtk-shell object"); gtk_shell->capabilities = calculate_capabilities (); meta_prefs_add_listener (prefs_changed, gtk_shell); return gtk_shell; } void meta_wayland_init_gtk_shell (MetaWaylandCompositor *compositor) { g_object_set_data_full (G_OBJECT (compositor), "-meta-wayland-gtk-shell", meta_wayland_gtk_shell_new (compositor), g_object_unref); } muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-strip.c0000664000175000017500000001403114723361714022070 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-tablet-pad-strip.h" #include #include #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-pad-group.h" #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } MetaWaylandTabletPadStrip * meta_wayland_tablet_pad_strip_new (MetaWaylandTabletPad *pad) { MetaWaylandTabletPadStrip *strip; strip = g_slice_new0 (MetaWaylandTabletPadStrip); wl_list_init (&strip->resource_list); wl_list_init (&strip->focus_resource_list); strip->pad = pad; return strip; } void meta_wayland_tablet_pad_strip_free (MetaWaylandTabletPadStrip *strip) { struct wl_resource *resource, *next; wl_resource_for_each_safe (resource, next, &strip->resource_list) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_free (strip->feedback); g_slice_free (MetaWaylandTabletPadStrip, strip); } static void tablet_pad_strip_set_feedback (struct wl_client *client, struct wl_resource *resource, const char *str, uint32_t serial) { MetaWaylandTabletPadStrip *strip = wl_resource_get_user_data (resource); if (strip->group->mode_switch_serial != serial) return; strip->feedback = g_strdup (str); } static void tablet_pad_strip_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_pad_strip_v2_interface strip_interface = { tablet_pad_strip_set_feedback, tablet_pad_strip_destroy, }; struct wl_resource * meta_wayland_tablet_pad_strip_create_new_resource (MetaWaylandTabletPadStrip *strip, struct wl_client *client, struct wl_resource *group_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_pad_strip_v2_interface, wl_resource_get_version (group_resource), id); wl_resource_set_implementation (resource, &strip_interface, strip, unbind_resource); wl_resource_set_user_data (resource, strip); wl_list_insert (&strip->resource_list, wl_resource_get_link (resource)); return resource; } gboolean meta_wayland_tablet_pad_strip_handle_event (MetaWaylandTabletPadStrip *strip, const ClutterEvent *event) { struct wl_list *focus_resources = &strip->focus_resource_list; enum zwp_tablet_pad_strip_v2_source source; gboolean source_known = FALSE; struct wl_resource *resource; if (wl_list_empty (focus_resources)) return FALSE; if (event->type != CLUTTER_PAD_STRIP) return FALSE; if (event->pad_strip.strip_source == CLUTTER_INPUT_DEVICE_PAD_SOURCE_FINGER) { source = ZWP_TABLET_PAD_STRIP_V2_SOURCE_FINGER; source_known = TRUE; } wl_resource_for_each (resource, focus_resources) { gdouble value = event->pad_strip.value; if (source_known) zwp_tablet_pad_strip_v2_send_source (resource, source); if (value >= 0) zwp_tablet_pad_strip_v2_send_position (resource, (uint32_t) (value * 65535)); else zwp_tablet_pad_strip_v2_send_stop (resource); zwp_tablet_pad_strip_v2_send_frame (resource, clutter_event_get_time (event)); } return TRUE; } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } void meta_wayland_tablet_pad_strip_sync_focus (MetaWaylandTabletPadStrip *strip) { g_clear_pointer (&strip->feedback, g_free); if (!wl_list_empty (&strip->focus_resource_list)) { move_resources (&strip->resource_list, &strip->focus_resource_list); } if (strip->pad->focus_surface != NULL) { move_resources_for_client (&strip->focus_resource_list, &strip->resource_list, wl_resource_get_client (strip->pad->focus_surface->resource)); } } void meta_wayland_tablet_pad_strip_set_group (MetaWaylandTabletPadStrip *strip, MetaWaylandTabletPadGroup *group) { /* Group is static, can only be set once */ g_assert (strip->group == NULL); strip->group = group; group->strips = g_list_append (group->strips, strip); } muffin-6.4.1/src/wayland/meta-wayland-text-input-legacy.c0000664000175000017500000005244214723361714022307 0ustar fabiofabio/* * Copyright (C) 2017, 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-text-input-legacy.h" #include "wayland/meta-wayland-versions.h" #include "gtk-text-input-server-protocol.h" #define META_TYPE_WAYLAND_GTK_TEXT_INPUT_FOCUS (meta_wayland_gtk_text_input_focus_get_type ()) typedef enum { META_WAYLAND_PENDING_STATE_NONE = 0, META_WAYLAND_PENDING_STATE_INPUT_RECT = 1 << 0, META_WAYLAND_PENDING_STATE_CONTENT_TYPE = 1 << 1, META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT = 1 << 2, } MetaWaylandTextInputPendingState; typedef struct _MetaWaylandGtkTextInput MetaWaylandGtkTextInput; struct _MetaWaylandGtkTextInput { MetaWaylandSeat *seat; ClutterInputFocus *input_focus; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandSurface *surface; struct wl_listener surface_listener; uint32_t focus_serial; MetaWaylandTextInputPendingState pending_state; struct { char *text; uint32_t cursor; uint32_t anchor; } surrounding; cairo_rectangle_int_t cursor_rect; uint32_t content_type_hint; uint32_t content_type_purpose; }; struct _MetaWaylandGtkTextInputFocus { ClutterInputFocus parent_instance; MetaWaylandGtkTextInput *text_input; }; G_DECLARE_FINAL_TYPE (MetaWaylandGtkTextInputFocus, meta_wayland_gtk_text_input_focus, META, WAYLAND_GTK_TEXT_INPUT_FOCUS, ClutterInputFocus) G_DEFINE_TYPE (MetaWaylandGtkTextInputFocus, meta_wayland_gtk_text_input_focus, CLUTTER_TYPE_INPUT_FOCUS) static void meta_wayland_text_input_focus_request_surrounding (ClutterInputFocus *focus) { MetaWaylandGtkTextInput *text_input; text_input = META_WAYLAND_GTK_TEXT_INPUT_FOCUS (focus)->text_input; clutter_input_focus_set_surrounding (focus, text_input->surrounding.text, text_input->surrounding.cursor, text_input->surrounding.anchor); } static void meta_wayland_text_input_focus_delete_surrounding (ClutterInputFocus *focus, int offset, guint len) { MetaWaylandGtkTextInput *text_input; uint32_t before_length; uint32_t after_length; struct wl_resource *resource; text_input = META_WAYLAND_GTK_TEXT_INPUT_FOCUS (focus)->text_input; before_length = ABS (MIN (offset, 0)); after_length = MAX (0, offset + len); g_warn_if_fail (ABS (offset) <= len); wl_resource_for_each (resource, &text_input->focus_resource_list) { gtk_text_input_send_delete_surrounding_text (resource, before_length, after_length); } } static void meta_wayland_text_input_focus_commit_text (ClutterInputFocus *focus, const gchar *text) { MetaWaylandGtkTextInput *text_input; struct wl_resource *resource; text_input = META_WAYLAND_GTK_TEXT_INPUT_FOCUS (focus)->text_input; wl_resource_for_each (resource, &text_input->focus_resource_list) { gtk_text_input_send_preedit_string (resource, NULL, 0); gtk_text_input_send_commit_string (resource, text); } } static void meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus, const gchar *text, guint cursor) { MetaWaylandGtkTextInput *text_input; struct wl_resource *resource; text_input = META_WAYLAND_GTK_TEXT_INPUT_FOCUS (focus)->text_input; wl_resource_for_each (resource, &text_input->focus_resource_list) { gtk_text_input_send_preedit_string (resource, text, cursor); } } static void meta_wayland_gtk_text_input_focus_class_init (MetaWaylandGtkTextInputFocusClass *klass) { ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass); focus_class->request_surrounding = meta_wayland_text_input_focus_request_surrounding; focus_class->delete_surrounding = meta_wayland_text_input_focus_delete_surrounding; focus_class->commit_text = meta_wayland_text_input_focus_commit_text; focus_class->set_preedit_text = meta_wayland_text_input_focus_set_preedit_text; } static void meta_wayland_gtk_text_input_focus_init (MetaWaylandGtkTextInputFocus *focus) { } static ClutterInputFocus * meta_wayland_text_input_focus_new (MetaWaylandGtkTextInput *text_input) { MetaWaylandGtkTextInputFocus *focus; focus = g_object_new (META_TYPE_WAYLAND_GTK_TEXT_INPUT_FOCUS, NULL); focus->text_input = text_input; return CLUTTER_INPUT_FOCUS (focus); } static void text_input_handle_focus_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandGtkTextInput *text_input = wl_container_of (listener, text_input, surface_listener); meta_wayland_gtk_text_input_set_focus (text_input, NULL); } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } void meta_wayland_gtk_text_input_set_focus (MetaWaylandGtkTextInput *text_input, MetaWaylandSurface *surface) { if (text_input->surface == surface) return; text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; if (text_input->surface) { if (!wl_list_empty (&text_input->focus_resource_list)) { ClutterInputFocus *focus = text_input->input_focus; ClutterInputMethod *input_method; struct wl_resource *resource; uint32_t serial; if (clutter_input_focus_is_focused (focus)) { input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); clutter_input_method_focus_out (input_method); } serial = wl_display_next_serial (text_input->seat->wl_display); wl_resource_for_each (resource, &text_input->focus_resource_list) { gtk_text_input_send_leave (resource, serial, text_input->surface->resource); } move_resources (&text_input->resource_list, &text_input->focus_resource_list); } wl_list_remove (&text_input->surface_listener.link); text_input->surface = NULL; } if (surface) { struct wl_resource *focus_surface_resource; text_input->surface = surface; focus_surface_resource = text_input->surface->resource; wl_resource_add_destroy_listener (focus_surface_resource, &text_input->surface_listener); move_resources_for_client (&text_input->focus_resource_list, &text_input->resource_list, wl_resource_get_client (focus_surface_resource)); if (!wl_list_empty (&text_input->focus_resource_list)) { struct wl_resource *resource; text_input->focus_serial = wl_display_next_serial (text_input->seat->wl_display); wl_resource_for_each (resource, &text_input->focus_resource_list) { gtk_text_input_send_enter (resource, text_input->focus_serial, surface->resource); } } } } static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void text_input_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void text_input_enable (struct wl_client *client, struct wl_resource *resource, uint32_t serial, uint32_t flags) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); ClutterInputFocus *focus = text_input->input_focus; ClutterInputMethod *input_method; gboolean show_preedit; if (serial != text_input->focus_serial) return; if (!clutter_input_focus_is_focused (focus)) { input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); if (input_method) clutter_input_method_focus_in (input_method, focus); else return; } show_preedit = (flags & GTK_TEXT_INPUT_ENABLE_FLAGS_CAN_SHOW_PREEDIT) != 0; clutter_input_focus_set_can_show_preedit (focus, show_preedit); if (flags & GTK_TEXT_INPUT_ENABLE_FLAGS_TOGGLE_INPUT_PANEL) clutter_input_focus_set_input_panel_state (focus, CLUTTER_INPUT_PANEL_STATE_TOGGLE); } static void text_input_disable (struct wl_client *client, struct wl_resource *resource) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); ClutterInputFocus *focus = text_input->input_focus; ClutterInputMethod *input_method; if (!clutter_input_focus_is_focused (focus)) return; clutter_input_focus_reset (text_input->input_focus); text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); clutter_input_method_focus_out (input_method); } static void text_input_set_surrounding_text (struct wl_client *client, struct wl_resource *resource, const char *text, int32_t cursor, int32_t anchor) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); g_free (text_input->surrounding.text); text_input->surrounding.text = g_strdup (text); text_input->surrounding.cursor = cursor; text_input->surrounding.anchor = anchor; text_input->pending_state |= META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT; } static ClutterInputContentHintFlags translate_hints (uint32_t hints) { ClutterInputContentHintFlags clutter_hints = 0; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_COMPLETION) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_COMPLETION; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_SPELLCHECK) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SPELLCHECK; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_LOWERCASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LOWERCASE; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_UPPERCASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_UPPERCASE; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_TITLECASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_TITLECASE; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_HIDDEN_TEXT) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_HIDDEN_TEXT; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_SENSITIVE_DATA) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_LATIN) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LATIN; if (hints & GTK_TEXT_INPUT_CONTENT_HINT_MULTILINE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_MULTILINE; return clutter_hints; } static ClutterInputContentPurpose translate_purpose (uint32_t purpose) { switch (purpose) { case GTK_TEXT_INPUT_CONTENT_PURPOSE_NORMAL: return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; case GTK_TEXT_INPUT_CONTENT_PURPOSE_ALPHA: return CLUTTER_INPUT_CONTENT_PURPOSE_ALPHA; case GTK_TEXT_INPUT_CONTENT_PURPOSE_DIGITS: return CLUTTER_INPUT_CONTENT_PURPOSE_DIGITS; case GTK_TEXT_INPUT_CONTENT_PURPOSE_NUMBER: return CLUTTER_INPUT_CONTENT_PURPOSE_NUMBER; case GTK_TEXT_INPUT_CONTENT_PURPOSE_PHONE: return CLUTTER_INPUT_CONTENT_PURPOSE_PHONE; case GTK_TEXT_INPUT_CONTENT_PURPOSE_URL: return CLUTTER_INPUT_CONTENT_PURPOSE_URL; case GTK_TEXT_INPUT_CONTENT_PURPOSE_EMAIL: return CLUTTER_INPUT_CONTENT_PURPOSE_EMAIL; case GTK_TEXT_INPUT_CONTENT_PURPOSE_NAME: return CLUTTER_INPUT_CONTENT_PURPOSE_NAME; case GTK_TEXT_INPUT_CONTENT_PURPOSE_PASSWORD: return CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD; case GTK_TEXT_INPUT_CONTENT_PURPOSE_DATE: return CLUTTER_INPUT_CONTENT_PURPOSE_DATE; case GTK_TEXT_INPUT_CONTENT_PURPOSE_TIME: return CLUTTER_INPUT_CONTENT_PURPOSE_TIME; case GTK_TEXT_INPUT_CONTENT_PURPOSE_DATETIME: return CLUTTER_INPUT_CONTENT_PURPOSE_DATETIME; case GTK_TEXT_INPUT_CONTENT_PURPOSE_TERMINAL: return CLUTTER_INPUT_CONTENT_PURPOSE_TERMINAL; } g_warn_if_reached (); return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; } static void text_input_set_content_type (struct wl_client *client, struct wl_resource *resource, uint32_t hint, uint32_t purpose) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); if (!text_input->surface) return; text_input->content_type_hint = hint; text_input->content_type_purpose = purpose; text_input->pending_state |= META_WAYLAND_PENDING_STATE_CONTENT_TYPE; } static void text_input_set_cursor_rectangle (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); if (!text_input->surface) return; text_input->cursor_rect = (cairo_rectangle_int_t) { x, y, width, height }; text_input->pending_state |= META_WAYLAND_PENDING_STATE_INPUT_RECT; } static void text_input_commit_state (struct wl_client *client, struct wl_resource *resource) { MetaWaylandGtkTextInput *text_input = wl_resource_get_user_data (resource); ClutterInputFocus *focus = text_input->input_focus; if (!clutter_input_focus_is_focused (focus)) return; if (text_input->surface == NULL) return; if (text_input->pending_state & META_WAYLAND_PENDING_STATE_CONTENT_TYPE) { clutter_input_focus_set_content_hints (text_input->input_focus, translate_hints (text_input->content_type_hint)); clutter_input_focus_set_content_purpose (text_input->input_focus, translate_purpose (text_input->content_type_purpose)); } if (text_input->pending_state & META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT) { clutter_input_focus_set_surrounding (text_input->input_focus, text_input->surrounding.text, text_input->surrounding.cursor, text_input->surrounding.anchor); } if (text_input->pending_state & META_WAYLAND_PENDING_STATE_INPUT_RECT) { graphene_rect_t cursor_rect; float x1, y1, x2, y2; cairo_rectangle_int_t rect; rect = text_input->cursor_rect; meta_wayland_surface_get_absolute_coordinates (text_input->surface, rect.x, rect.y, &x1, &y1); meta_wayland_surface_get_absolute_coordinates (text_input->surface, rect.x + rect.width, rect.y + rect.height, &x2, &y2); graphene_rect_init (&cursor_rect, x1, y1, x2 - x1, y2 - y1); clutter_input_focus_set_cursor_location (text_input->input_focus, &cursor_rect); } text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; } static struct gtk_text_input_interface meta_text_input_interface = { text_input_destroy, text_input_enable, text_input_disable, text_input_set_surrounding_text, text_input_set_content_type, text_input_set_cursor_rectangle, text_input_commit_state, }; MetaWaylandGtkTextInput * meta_wayland_gtk_text_input_new (MetaWaylandSeat *seat) { MetaWaylandGtkTextInput *text_input; text_input = g_new0 (MetaWaylandGtkTextInput, 1); text_input->input_focus = meta_wayland_text_input_focus_new (text_input); text_input->seat = seat; wl_list_init (&text_input->resource_list); wl_list_init (&text_input->focus_resource_list); text_input->surface_listener.notify = text_input_handle_focus_surface_destroy; return text_input; } void meta_wayland_gtk_text_input_destroy (MetaWaylandGtkTextInput *text_input) { meta_wayland_gtk_text_input_set_focus (text_input, NULL); g_object_unref (text_input->input_focus); g_free (text_input); } static void meta_wayland_text_input_create_new_resource (MetaWaylandGtkTextInput *text_input, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *text_input_resource; text_input_resource = wl_resource_create (client, >k_text_input_interface, META_GTK_TEXT_INPUT_VERSION, id); wl_resource_set_implementation (text_input_resource, &meta_text_input_interface, text_input, unbind_resource); if (text_input->surface && wl_resource_get_client (text_input->surface->resource) == client) { wl_list_insert (&text_input->focus_resource_list, wl_resource_get_link (text_input_resource)); gtk_text_input_send_enter (text_input_resource, text_input->focus_serial, text_input->surface->resource); } else { wl_list_insert (&text_input->resource_list, wl_resource_get_link (text_input_resource)); } } static void text_input_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void text_input_manager_get_text_input (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *seat_resource) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); meta_wayland_text_input_create_new_resource (seat->gtk_text_input, client, seat_resource, id); } static struct gtk_text_input_manager_interface meta_text_input_manager_interface = { text_input_manager_destroy, text_input_manager_get_text_input, }; static void bind_text_input (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, >k_text_input_manager_interface, META_GTK_TEXT_INPUT_VERSION, id); wl_resource_set_implementation (resource, &meta_text_input_manager_interface, NULL, NULL); } gboolean meta_wayland_gtk_text_input_init (MetaWaylandCompositor *compositor) { return (wl_global_create (compositor->wayland_display, >k_text_input_manager_interface, META_GTK_TEXT_INPUT_VERSION, compositor->seat->gtk_text_input, bind_text_input) != NULL); } gboolean meta_wayland_gtk_text_input_handle_event (MetaWaylandGtkTextInput *text_input, const ClutterEvent *event) { if (!text_input->surface || !clutter_input_focus_is_focused (text_input->input_focus)) return FALSE; return clutter_input_focus_filter_event (text_input->input_focus, event); } muffin-6.4.1/src/wayland/meta-wayland-data-source-primary.c0000664000175000017500000000766114723361714022617 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include #include "primary-selection-unstable-v1-server-protocol.h" #include "wayland/meta-wayland-data-source-primary.h" typedef struct _MetaWaylandDataSourcePrimary { MetaWaylandDataSource parent; } MetaWaylandDataSourcePrimary; G_DEFINE_TYPE (MetaWaylandDataSourcePrimary, meta_wayland_data_source_primary, META_TYPE_WAYLAND_DATA_SOURCE); static void primary_source_offer (struct wl_client *client, struct wl_resource *resource, const char *type) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); if (!meta_wayland_data_source_add_mime_type (source, type)) wl_resource_post_no_memory (resource); } static void primary_source_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static struct zwp_primary_selection_source_v1_interface primary_source_interface = { primary_source_offer, primary_source_destroy, }; static void destroy_primary_source (struct wl_resource *resource) { MetaWaylandDataSource *source = wl_resource_get_user_data (resource); meta_wayland_data_source_set_resource (source, NULL); g_object_unref (source); } static void meta_wayland_data_source_primary_send (MetaWaylandDataSource *source, const gchar *mime_type, gint fd) { struct wl_resource *resource = meta_wayland_data_source_get_resource (source); zwp_primary_selection_source_v1_send_send (resource, mime_type, fd); close (fd); } static void meta_wayland_data_source_primary_cancel (MetaWaylandDataSource *source) { struct wl_resource *resource = meta_wayland_data_source_get_resource (source); if (resource) zwp_primary_selection_source_v1_send_cancelled (resource); } static void meta_wayland_data_source_primary_init (MetaWaylandDataSourcePrimary *source_primary) { } static void meta_wayland_data_source_primary_class_init (MetaWaylandDataSourcePrimaryClass *klass) { MetaWaylandDataSourceClass *data_source_class = META_WAYLAND_DATA_SOURCE_CLASS (klass); data_source_class->send = meta_wayland_data_source_primary_send; data_source_class->cancel = meta_wayland_data_source_primary_cancel; } MetaWaylandDataSource * meta_wayland_data_source_primary_new (struct wl_resource *resource) { MetaWaylandDataSource *source_primary = g_object_new (META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY, NULL); meta_wayland_data_source_set_resource (source_primary, resource); wl_resource_set_implementation (resource, &primary_source_interface, source_primary, destroy_primary_source); return source_primary; } muffin-6.4.1/src/wayland/meta-wayland-dnd-surface.c0000664000175000017500000001221514723361714021111 0ustar fabiofabio/* * Copyright (C) 2015-2019 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-dnd-surface.h" #include "backends/meta-logical-monitor.h" #include "compositor/meta-feedback-actor-private.h" #include "wayland/meta-wayland.h" struct _MetaWaylandSurfaceRoleDND { MetaWaylandActorSurface parent; int32_t pending_offset_x; int32_t pending_offset_y; }; G_DEFINE_TYPE (MetaWaylandSurfaceRoleDND, meta_wayland_surface_role_dnd, META_TYPE_WAYLAND_ACTOR_SURFACE) static void dnd_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (wl_list_empty (&surface->unassigned.pending_frame_callback_list)) return; meta_wayland_compositor_add_frame_callback_surface (surface->compositor, surface); } static void dnd_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleDND *surface_role_dnd = META_WAYLAND_SURFACE_ROLE_DND (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_surface_role_dnd_parent_class); meta_wayland_compositor_add_frame_callback_surface (surface->compositor, surface); surface_role_dnd->pending_offset_x = pending->dx; surface_role_dnd->pending_offset_y = pending->dy; surface_role_class->apply_state (surface_role, pending); } static MetaLogicalMonitor * dnd_surface_find_logical_monitor (MetaWaylandActorSurface *actor_surface) { MetaBackend *backend = meta_get_backend (); MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (backend); MetaMonitorManager *monitor_manager = meta_backend_get_monitor_manager (backend); graphene_point_t pointer_pos; pointer_pos = meta_cursor_renderer_get_position (cursor_renderer); return meta_monitor_manager_get_logical_monitor_at (monitor_manager, pointer_pos.x, pointer_pos.y); } static double dnd_subsurface_get_geometry_scale (MetaWaylandActorSurface *actor_surface) { if (meta_is_stage_views_scaled ()) { return 1; } else { MetaLogicalMonitor *logical_monitor; logical_monitor = dnd_surface_find_logical_monitor (actor_surface); return meta_logical_monitor_get_scale (logical_monitor); } } static void dnd_subsurface_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaSurfaceActor *surface_actor = meta_wayland_actor_surface_get_actor (actor_surface); MetaFeedbackActor *feedback_actor = META_FEEDBACK_ACTOR (clutter_actor_get_parent (CLUTTER_ACTOR (surface_actor))); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurfaceRoleDND *surface_role_dnd = META_WAYLAND_SURFACE_ROLE_DND (surface_role); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_surface_role_dnd_parent_class); int geometry_scale; float anchor_x; float anchor_y; g_return_if_fail (META_IS_FEEDBACK_ACTOR (feedback_actor)); geometry_scale = meta_wayland_actor_surface_get_geometry_scale (actor_surface); meta_feedback_actor_set_geometry_scale (feedback_actor, geometry_scale); meta_feedback_actor_get_anchor (feedback_actor, &anchor_x, &anchor_y); anchor_x -= surface_role_dnd->pending_offset_x; anchor_y -= surface_role_dnd->pending_offset_y; meta_feedback_actor_set_anchor (feedback_actor, anchor_x, anchor_y); actor_surface_class->sync_actor_state (actor_surface); } static void meta_wayland_surface_role_dnd_init (MetaWaylandSurfaceRoleDND *role) { } static void meta_wayland_surface_role_dnd_class_init (MetaWaylandSurfaceRoleDNDClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (klass); surface_role_class->assigned = dnd_surface_assigned; surface_role_class->apply_state = dnd_surface_apply_state; actor_surface_class->get_geometry_scale = dnd_subsurface_get_geometry_scale; actor_surface_class->sync_actor_state = dnd_subsurface_sync_actor_state; } muffin-6.4.1/src/wayland/meta-wayland-tablet-manager.c0000664000175000017500000002044114723361714021601 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-manager.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet-tool.h" #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static gboolean is_tablet_device (ClutterInputDevice *device) { ClutterInputDeviceType device_type; if (clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER) return FALSE; device_type = clutter_input_device_get_device_type (device); return (device_type == CLUTTER_TABLET_DEVICE || device_type == CLUTTER_PEN_DEVICE || device_type == CLUTTER_ERASER_DEVICE || device_type == CLUTTER_CURSOR_DEVICE || device_type == CLUTTER_PAD_DEVICE); } static void tablet_manager_get_tablet_seat (struct wl_client *client, struct wl_resource *resource, guint32 id, struct wl_resource *seat_resource) { MetaWaylandTabletManager *tablet_manager = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandTabletSeat *tablet_seat; tablet_seat = meta_wayland_tablet_manager_ensure_seat (tablet_manager, seat); meta_wayland_tablet_seat_create_new_resource (tablet_seat, client, resource, id); } static void tablet_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_manager_v2_interface tablet_manager_interface = { tablet_manager_get_tablet_seat, tablet_manager_destroy }; static void bind_tablet_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; MetaWaylandTabletManager *tablet_manager = compositor->tablet_manager; struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_manager_v2_interface, MIN (version, 1), id); wl_resource_set_implementation (resource, &tablet_manager_interface, tablet_manager, unbind_resource); wl_resource_set_user_data (resource, tablet_manager); wl_list_insert (&tablet_manager->resource_list, wl_resource_get_link (resource)); } static MetaWaylandTabletManager * meta_wayland_tablet_manager_new (MetaWaylandCompositor *compositor) { MetaWaylandTabletManager *tablet_manager; tablet_manager = g_slice_new0 (MetaWaylandTabletManager); tablet_manager->compositor = compositor; tablet_manager->wl_display = compositor->wayland_display; tablet_manager->seats = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_tablet_seat_free); wl_list_init (&tablet_manager->resource_list); wl_global_create (tablet_manager->wl_display, &zwp_tablet_manager_v2_interface, 1, compositor, bind_tablet_manager); return tablet_manager; } void meta_wayland_tablet_manager_init (MetaWaylandCompositor *compositor) { compositor->tablet_manager = meta_wayland_tablet_manager_new (compositor); } void meta_wayland_tablet_manager_free (MetaWaylandTabletManager *tablet_manager) { g_hash_table_destroy (tablet_manager->seats); g_slice_free (MetaWaylandTabletManager, tablet_manager); } static MetaWaylandTabletSeat * meta_wayland_tablet_manager_lookup_seat (MetaWaylandTabletManager *manager, ClutterInputDevice *device) { MetaWaylandTabletSeat *tablet_seat; MetaWaylandSeat *seat; GHashTableIter iter; if (!device || !is_tablet_device (device)) return NULL; g_hash_table_iter_init (&iter, manager->seats); while (g_hash_table_iter_next (&iter, (gpointer*) &seat, (gpointer*) &tablet_seat)) { if (meta_wayland_tablet_seat_lookup_tablet (tablet_seat, device) || meta_wayland_tablet_seat_lookup_pad (tablet_seat, device)) return tablet_seat; } return NULL; } gboolean meta_wayland_tablet_manager_consumes_event (MetaWaylandTabletManager *manager, const ClutterEvent *event) { ClutterInputDevice *device = clutter_event_get_source_device (event); return meta_wayland_tablet_manager_lookup_seat (manager, device) != NULL; } void meta_wayland_tablet_manager_update (MetaWaylandTabletManager *manager, const ClutterEvent *event) { ClutterInputDevice *device = clutter_event_get_source_device (event); MetaWaylandTabletSeat *tablet_seat; tablet_seat = meta_wayland_tablet_manager_lookup_seat (manager, device); if (!tablet_seat) return; switch (event->type) { case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_MOTION: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_RING: case CLUTTER_PAD_STRIP: meta_wayland_tablet_seat_update (tablet_seat, event); break; default: break; } } gboolean meta_wayland_tablet_manager_handle_event (MetaWaylandTabletManager *manager, const ClutterEvent *event) { ClutterInputDevice *device = clutter_event_get_source_device (event); MetaWaylandTabletSeat *tablet_seat; tablet_seat = meta_wayland_tablet_manager_lookup_seat (manager, device); if (!tablet_seat) return CLUTTER_EVENT_PROPAGATE; switch (event->type) { case CLUTTER_PROXIMITY_IN: case CLUTTER_PROXIMITY_OUT: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_MOTION: case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: case CLUTTER_PAD_RING: case CLUTTER_PAD_STRIP: return meta_wayland_tablet_seat_handle_event (tablet_seat, event); default: return CLUTTER_EVENT_PROPAGATE; } } MetaWaylandTabletSeat * meta_wayland_tablet_manager_ensure_seat (MetaWaylandTabletManager *manager, MetaWaylandSeat *seat) { MetaWaylandTabletSeat *tablet_seat; tablet_seat = g_hash_table_lookup (manager->seats, seat); if (!tablet_seat) { tablet_seat = meta_wayland_tablet_seat_new (manager, seat); g_hash_table_insert (manager->seats, seat, tablet_seat); } return tablet_seat; } void meta_wayland_tablet_manager_update_cursor_position (MetaWaylandTabletManager *manager, const ClutterEvent *event) { MetaWaylandTabletSeat *tablet_seat = NULL; MetaWaylandTabletTool *tool = NULL; ClutterInputDeviceTool *device_tool; ClutterInputDevice *device; device = clutter_event_get_source_device (event); device_tool = clutter_event_get_device_tool (event); if (device) tablet_seat = meta_wayland_tablet_manager_lookup_seat (manager, device); if (tablet_seat && device_tool) tool = meta_wayland_tablet_seat_lookup_tool (tablet_seat, device_tool); if (tool) { gfloat new_x, new_y; clutter_event_get_coords (event, &new_x, &new_y); meta_wayland_tablet_tool_set_cursor_position (tool, new_x, new_y); } } muffin-6.4.1/src/wayland/meta-wayland-xdg-shell.c0000664000175000017500000024127314723361714020615 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2012-2013 Intel Corporation * Copyright (C) 2013-2015 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-xdg-shell.h" #include "backends/meta-logical-monitor.h" #include "core/boxes-private.h" #include "core/window-private.h" #include "wayland/meta-wayland-outputs.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-shell-surface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-window-configuration.h" #include "wayland/meta-wayland.h" #include "wayland/meta-window-wayland.h" #include "xdg-shell-server-protocol.h" enum { XDG_SURFACE_PROP_0, XDG_SURFACE_PROP_SHELL_CLIENT, XDG_SURFACE_PROP_RESOURCE, }; typedef struct _MetaWaylandXdgShellClient { struct wl_resource *resource; GList *surfaces; GList *surface_constructors; } MetaWaylandXdgShellClient; typedef struct _MetaWaylandXdgPositioner { MetaRectangle anchor_rect; int32_t width; int32_t height; uint32_t gravity; uint32_t anchor; uint32_t constraint_adjustment; int32_t offset_x; int32_t offset_y; gboolean is_reactive; gboolean has_parent_size; int32_t parent_width; int32_t parent_height; gboolean acked_parent_configure; uint32_t parent_configure_serial; } MetaWaylandXdgPositioner; typedef struct _MetaWaylandXdgSurfaceConstructor { MetaWaylandSurface *surface; struct wl_resource *resource; MetaWaylandXdgShellClient *shell_client; } MetaWaylandXdgSurfaceConstructor; typedef struct _MetaWaylandXdgSurfacePrivate { struct wl_resource *resource; MetaWaylandXdgShellClient *shell_client; MetaRectangle geometry; guint configure_sent : 1; guint first_buffer_attached : 1; guint has_set_geometry : 1; } MetaWaylandXdgSurfacePrivate; G_DEFINE_TYPE_WITH_PRIVATE (MetaWaylandXdgSurface, meta_wayland_xdg_surface, META_TYPE_WAYLAND_SHELL_SURFACE); struct _MetaWaylandXdgToplevel { MetaWaylandXdgSurface parent; struct wl_resource *resource; }; G_DEFINE_TYPE (MetaWaylandXdgToplevel, meta_wayland_xdg_toplevel, META_TYPE_WAYLAND_XDG_SURFACE); struct _MetaWaylandXdgPopup { MetaWaylandXdgSurface parent; struct wl_resource *resource; MetaWaylandSurface *parent_surface; gulong parent_surface_unmapped_handler_id; uint32_t pending_reposition_token; gboolean pending_repositioned; MetaWaylandPopup *popup; gboolean dismissed_by_client; struct { MetaWaylandSurface *parent_surface; /* * The coordinates/dimensions in the placement rule are in logical pixel * coordinate space, i.e. not scaled given what monitor the popup is on. */ MetaPlacementRule placement_rule; MetaWaylandSeat *grab_seat; uint32_t grab_serial; } setup; }; static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWaylandXdgPopup, meta_wayland_xdg_popup, META_TYPE_WAYLAND_XDG_SURFACE, G_IMPLEMENT_INTERFACE (META_TYPE_WAYLAND_POPUP_SURFACE, popup_surface_iface_init)); static MetaPlacementRule meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner, MetaWindow *parent_window); static struct wl_resource * meta_wayland_xdg_surface_get_wm_base_resource (MetaWaylandXdgSurface *xdg_surface); static MetaRectangle meta_wayland_xdg_surface_get_window_geometry (MetaWaylandXdgSurface *xdg_surface); static void meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface, MetaWaylandWindowConfiguration *configuration); static void scale_placement_rule (MetaPlacementRule *placement_rule, MetaWaylandSurface *surface); static MetaWaylandSurface * surface_from_xdg_surface_resource (struct wl_resource *resource) { MetaWaylandSurfaceRole *surface_role = wl_resource_get_user_data (resource); if (!META_IS_WAYLAND_SURFACE_ROLE (surface_role)) return NULL; return meta_wayland_surface_role_get_surface (surface_role); } static MetaWaylandSurface * surface_from_xdg_toplevel_resource (struct wl_resource *resource) { return surface_from_xdg_surface_resource (resource); } static void meta_wayland_xdg_surface_reset (MetaWaylandXdgSurface *xdg_surface) { META_WAYLAND_XDG_SURFACE_GET_CLASS (xdg_surface)->reset (xdg_surface); } static void xdg_toplevel_destructor (struct wl_resource *resource) { MetaWaylandXdgToplevel *xdg_toplevel = wl_resource_get_user_data (resource); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_toplevel); meta_wayland_shell_surface_destroy_window (shell_surface); xdg_toplevel->resource = NULL; } static void xdg_toplevel_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void xdg_toplevel_set_parent (struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *transient_for = NULL; MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (parent_resource) { MetaWaylandSurface *parent_surface = surface_from_xdg_surface_resource (parent_resource); transient_for = meta_wayland_surface_get_window (parent_surface); } meta_window_set_transient_for (window, transient_for); } static void xdg_toplevel_set_title (struct wl_client *client, struct wl_resource *resource, const char *title) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!g_utf8_validate (title, -1, NULL)) title = ""; meta_window_set_title (window, title); } static void xdg_toplevel_set_app_id (struct wl_client *client, struct wl_resource *resource, const char *app_id) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!g_utf8_validate (app_id, -1, NULL)) app_id = ""; meta_window_set_wm_class (window, app_id, app_id); } static void xdg_toplevel_show_window_menu (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, int32_t x, int32_t y) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; int monitor_scale; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, FALSE, NULL, NULL)) return; monitor_scale = meta_window_wayland_get_geometry_scale (window); meta_window_show_menu (window, META_WINDOW_MENU_WM, window->buffer_rect.x + (x * monitor_scale), window->buffer_rect.y + (y * monitor_scale)); } static void xdg_toplevel_move (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; float x, y; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; meta_wayland_surface_begin_grab_op (surface, seat, META_GRAB_OP_MOVING, x, y); } static MetaGrabOp grab_op_for_xdg_toplevel_resize_edge (int edge) { MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; if (edge & XDG_TOPLEVEL_RESIZE_EDGE_TOP) op |= META_GRAB_OP_WINDOW_DIR_NORTH; if (edge & XDG_TOPLEVEL_RESIZE_EDGE_BOTTOM) op |= META_GRAB_OP_WINDOW_DIR_SOUTH; if (edge & XDG_TOPLEVEL_RESIZE_EDGE_LEFT) op |= META_GRAB_OP_WINDOW_DIR_WEST; if (edge & XDG_TOPLEVEL_RESIZE_EDGE_RIGHT) op |= META_GRAB_OP_WINDOW_DIR_EAST; if (op == META_GRAB_OP_WINDOW_BASE) { g_warning ("invalid edge: %d", edge); return META_GRAB_OP_NONE; } return op; } static void xdg_toplevel_resize (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; gfloat x, y; MetaGrabOp grab_op; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; grab_op = grab_op_for_xdg_toplevel_resize_edge (edges); meta_wayland_surface_begin_grab_op (surface, seat, grab_op, x, y); } static void xdg_toplevel_set_max_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWaylandSurfaceState *pending; if (width < 0 || height < 0) { wl_resource_post_error (resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "invalid negative max size requested %i x %i", width, height); return; } pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_max_size = TRUE; pending->new_max_width = width; pending->new_max_height = height; } static void xdg_toplevel_set_min_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWaylandSurfaceState *pending; if (width < 0 || height < 0) { wl_resource_post_error (resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "invalid negative min size requested %i x %i", width, height); return; } pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_min_size = TRUE; pending->new_min_width = width; pending->new_min_height = height; } static void xdg_toplevel_set_maximized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!window->has_maximize_func) return; meta_window_force_placement (window, TRUE); meta_window_maximize (window, META_MAXIMIZE_BOTH); } static void xdg_toplevel_unset_maximized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } static void xdg_toplevel_set_fullscreen (struct wl_client *client, struct wl_resource *resource, struct wl_resource *output_resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; if (output_resource) { MetaWaylandOutput *output = wl_resource_get_user_data (output_resource); if (output && output->logical_monitor) { meta_window_move_to_monitor (window, output->logical_monitor->number); } } meta_window_make_fullscreen (window); } static void xdg_toplevel_unset_fullscreen (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_unmake_fullscreen (window); } static void xdg_toplevel_set_minimized (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_xdg_toplevel_resource (resource); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) return; meta_window_minimize (window); } static const struct xdg_toplevel_interface meta_wayland_xdg_toplevel_interface = { xdg_toplevel_destroy, xdg_toplevel_set_parent, xdg_toplevel_set_title, xdg_toplevel_set_app_id, xdg_toplevel_show_window_menu, xdg_toplevel_move, xdg_toplevel_resize, xdg_toplevel_set_max_size, xdg_toplevel_set_min_size, xdg_toplevel_set_maximized, xdg_toplevel_unset_maximized, xdg_toplevel_set_fullscreen, xdg_toplevel_unset_fullscreen, xdg_toplevel_set_minimized, }; static void meta_wayland_xdg_popup_unmap (MetaWaylandXdgPopup *xdg_popup) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_popup); g_assert (!xdg_popup->popup); if (xdg_popup->parent_surface) { g_clear_signal_handler (&xdg_popup->parent_surface_unmapped_handler_id, xdg_popup->parent_surface); xdg_popup->parent_surface = NULL; } meta_wayland_shell_surface_destroy_window (shell_surface); } static void dismiss_popup (MetaWaylandXdgPopup *xdg_popup) { if (xdg_popup->popup) meta_wayland_popup_dismiss (xdg_popup->popup); else meta_wayland_xdg_popup_unmap (xdg_popup); } static void xdg_popup_destructor (struct wl_resource *resource) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (wl_resource_get_user_data (resource)); dismiss_popup (xdg_popup); xdg_popup->resource = NULL; } static void xdg_popup_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void xdg_popup_grab (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (wl_resource_get_user_data (resource)); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *parent_surface; parent_surface = xdg_popup->setup.parent_surface; if (!parent_surface) { wl_resource_post_error (resource, XDG_POPUP_ERROR_INVALID_GRAB, "tried to grab after popup was mapped"); return; } xdg_popup->setup.grab_seat = seat; xdg_popup->setup.grab_serial = serial; } static void xdg_popup_reposition (struct wl_client *client, struct wl_resource *resource, struct wl_resource *positioner_resource, uint32_t token) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (wl_resource_get_user_data (resource)); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; MetaWindow *parent_window; MetaWaylandXdgPositioner *xdg_positioner; MetaPlacementRule placement_rule; window = meta_wayland_surface_get_window (surface); if (!window) return; parent_window = meta_wayland_surface_get_window (xdg_popup->parent_surface); xdg_positioner = wl_resource_get_user_data (positioner_resource); placement_rule = meta_wayland_xdg_positioner_to_placement (xdg_positioner, parent_window); xdg_popup->pending_reposition_token = token; xdg_popup->pending_repositioned = TRUE; scale_placement_rule (&placement_rule, surface); meta_window_update_placement_rule (window, &placement_rule); } static const struct xdg_popup_interface meta_wayland_xdg_popup_interface = { xdg_popup_destroy, xdg_popup_grab, xdg_popup_reposition, }; static void on_parent_surface_unmapped (MetaWaylandSurface *parent_surface, MetaWaylandXdgPopup *xdg_popup) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_popup); wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "destroyed popup not top most popup"); xdg_popup->parent_surface = NULL; meta_wayland_shell_surface_destroy_window (shell_surface); } static void add_state_value (struct wl_array *states, enum xdg_toplevel_state state) { uint32_t *s; s = wl_array_add (states, sizeof *s); *s = state; } static void fill_states (MetaWaylandXdgToplevel *xdg_toplevel, struct wl_array *states) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_toplevel); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = meta_wayland_surface_get_window (surface); if (META_WINDOW_MAXIMIZED (window)) add_state_value (states, XDG_TOPLEVEL_STATE_MAXIMIZED); if (meta_window_is_fullscreen (window)) add_state_value (states, XDG_TOPLEVEL_STATE_FULLSCREEN); if (meta_grab_op_is_resizing (window->display->grab_op)) add_state_value (states, XDG_TOPLEVEL_STATE_RESIZING); if (meta_window_appears_focused (window)) add_state_value (states, XDG_TOPLEVEL_STATE_ACTIVATED); if (wl_resource_get_version (xdg_toplevel->resource) >= XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION) { if (window->edge_constraints.top != META_EDGE_CONSTRAINT_NONE) add_state_value (states, XDG_TOPLEVEL_STATE_TILED_TOP); if (window->edge_constraints.right != META_EDGE_CONSTRAINT_NONE) add_state_value (states, XDG_TOPLEVEL_STATE_TILED_RIGHT); if (window->edge_constraints.bottom != META_EDGE_CONSTRAINT_NONE) add_state_value (states, XDG_TOPLEVEL_STATE_TILED_BOTTOM); if (window->edge_constraints.left != META_EDGE_CONSTRAINT_NONE) add_state_value (states, XDG_TOPLEVEL_STATE_TILED_LEFT); } } static void meta_wayland_xdg_toplevel_send_configure (MetaWaylandXdgToplevel *xdg_toplevel, MetaWaylandWindowConfiguration *configuration) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); struct wl_array states; wl_array_init (&states); fill_states (xdg_toplevel, &states); xdg_toplevel_send_configure (xdg_toplevel->resource, configuration->width / configuration->scale, configuration->height / configuration->scale, &states); wl_array_release (&states); meta_wayland_xdg_surface_send_configure (xdg_surface, configuration); } static gboolean is_new_size_hints_valid (MetaWindow *window, MetaWaylandSurfaceState *pending) { int new_min_width, new_min_height; int new_max_width, new_max_height; if (pending->has_new_min_size) { new_min_width = pending->new_min_width; new_min_height = pending->new_min_height; } else { meta_window_wayland_get_min_size (window, &new_min_width, &new_min_height); } if (pending->has_new_max_size) { new_max_width = pending->new_max_width; new_max_height = pending->new_max_height; } else { meta_window_wayland_get_max_size (window, &new_max_width, &new_max_height); } /* Zero means unlimited */ return ((new_max_width == 0 || new_min_width <= new_max_width) && (new_max_height == 0 || new_min_height <= new_max_height)); } static void meta_wayland_xdg_toplevel_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (surface_role); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); MetaWaylandXdgSurfacePrivate *xdg_surface_priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (xdg_toplevel); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window; window = meta_wayland_surface_get_window (surface); if (!window) { meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); return; } if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached) { meta_wayland_xdg_surface_reset (xdg_surface); meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); return; } surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_toplevel_parent_class); surface_role_class->apply_state (surface_role, pending); if (!xdg_surface_priv->configure_sent) { MetaWaylandWindowConfiguration *configuration; configuration = meta_wayland_window_configuration_new_empty (); meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, configuration); meta_wayland_window_configuration_free (configuration); return; } } static void meta_wayland_xdg_toplevel_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandXdgSurfacePrivate *xdg_surface_priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWindow *window; MetaRectangle old_geometry; MetaRectangle window_geometry; gboolean geometry_changed; window = meta_wayland_surface_get_window (surface); if (!window) return; old_geometry = xdg_surface_priv->geometry; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_toplevel_parent_class); surface_role_class->post_apply_state (surface_role, pending); if (!pending->newly_attached) return; window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface); geometry_changed = !meta_rectangle_equal (&old_geometry, &window_geometry); if (geometry_changed || pending->has_acked_configure_serial) { meta_window_wayland_finish_move_resize (window, window_geometry, pending); } else if (pending->dx != 0 || pending->dy != 0) { g_warning ("XXX: Attach-initiated move without a new geometry. " "This is unimplemented right now."); } /* When we get to this point, we ought to have valid size hints */ if (pending->has_new_min_size || pending->has_new_max_size) { if (is_new_size_hints_valid (window, pending)) { if (pending->has_new_min_size) meta_window_wayland_set_min_size (window, pending->new_min_width, pending->new_min_height); if (pending->has_new_max_size) meta_window_wayland_set_max_size (window, pending->new_max_width, pending->new_max_height); meta_window_recalc_features (window); } else { wl_resource_post_error (surface->resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Invalid min/max size"); } } } static MetaWaylandSurface * meta_wayland_xdg_toplevel_get_toplevel (MetaWaylandSurfaceRole *surface_role) { return meta_wayland_surface_role_get_surface (surface_role); } static void meta_wayland_xdg_toplevel_reset (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_surface); MetaWaylandXdgSurfaceClass *xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_toplevel_parent_class); MetaWaylandSurface *surface; MetaWindow *window; surface = meta_wayland_surface_role_get_surface (surface_role); meta_wayland_shell_surface_destroy_window (shell_surface); meta_wayland_actor_surface_reset_actor (META_WAYLAND_ACTOR_SURFACE (surface_role)); window = meta_window_wayland_new (meta_get_display (), surface); meta_wayland_shell_surface_set_window (shell_surface, window); xdg_surface_class->reset (xdg_surface); } static void meta_wayland_xdg_toplevel_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (shell_surface); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); MetaWaylandXdgSurfacePrivate *xdg_surface_priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); if (!xdg_surface_priv->resource) return; if (!xdg_toplevel->resource) return; meta_wayland_xdg_toplevel_send_configure (xdg_toplevel, configuration); } static void meta_wayland_xdg_toplevel_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { } static void meta_wayland_xdg_toplevel_close (MetaWaylandShellSurface *shell_surface) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (shell_surface); xdg_toplevel_send_close (xdg_toplevel->resource); } static void meta_wayland_xdg_toplevel_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (xdg_surface); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandXdgSurfaceClass *xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_toplevel_parent_class); xdg_surface_class->shell_client_destroyed (xdg_surface); if (xdg_toplevel->resource) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base of xdg_toplevel@%d was destroyed", wl_resource_get_id (xdg_toplevel->resource)); wl_resource_destroy (xdg_toplevel->resource); } } static void meta_wayland_xdg_toplevel_finalize (GObject *object) { MetaWaylandXdgToplevel *xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (object); g_clear_pointer (&xdg_toplevel->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_xdg_toplevel_parent_class)->finalize (object); } static void meta_wayland_xdg_toplevel_init (MetaWaylandXdgToplevel *role) { } static void meta_wayland_xdg_toplevel_class_init (MetaWaylandXdgToplevelClass *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; MetaWaylandXdgSurfaceClass *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_xdg_toplevel_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_xdg_toplevel_apply_state; surface_role_class->post_apply_state = meta_wayland_xdg_toplevel_post_apply_state; surface_role_class->get_toplevel = meta_wayland_xdg_toplevel_get_toplevel; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->configure = meta_wayland_xdg_toplevel_configure; shell_surface_class->managed = meta_wayland_xdg_toplevel_managed; shell_surface_class->close = meta_wayland_xdg_toplevel_close; xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass); xdg_surface_class->shell_client_destroyed = meta_wayland_xdg_toplevel_shell_client_destroyed; xdg_surface_class->reset = meta_wayland_xdg_toplevel_reset; } static void scale_placement_rule (MetaPlacementRule *placement_rule, MetaWaylandSurface *surface) { MetaWindow *window = meta_wayland_surface_get_window (surface); int geometry_scale; geometry_scale = meta_window_wayland_get_geometry_scale (window); placement_rule->anchor_rect.x *= geometry_scale; placement_rule->anchor_rect.y *= geometry_scale; placement_rule->anchor_rect.width *= geometry_scale; placement_rule->anchor_rect.height *= geometry_scale; placement_rule->offset_x *= geometry_scale; placement_rule->offset_y *= geometry_scale; placement_rule->width *= geometry_scale; placement_rule->height *= geometry_scale; } static void meta_wayland_xdg_popup_place (MetaWaylandXdgPopup *xdg_popup, MetaPlacementRule *placement_rule) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaPlacementRule scaled_placement_rule; MetaWindow *window; scaled_placement_rule = *placement_rule; scale_placement_rule (&scaled_placement_rule, surface); window = meta_wayland_surface_get_window (surface); meta_window_place_with_placement_rule (window, &scaled_placement_rule); } static void finish_popup_setup (MetaWaylandXdgPopup *xdg_popup) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent_surface; MetaWaylandSeat *seat; uint32_t serial; MetaDisplay *display = meta_get_display (); MetaWindow *window; parent_surface = xdg_popup->setup.parent_surface; seat = xdg_popup->setup.grab_seat; serial = xdg_popup->setup.grab_serial; xdg_popup->setup.parent_surface = NULL; xdg_popup->setup.grab_seat = NULL; if (!meta_wayland_surface_get_window (parent_surface)) { xdg_popup_send_popup_done (xdg_popup->resource); return; } if (seat) { MetaWaylandSurface *top_popup; if (!meta_wayland_seat_can_popup (seat, serial)) { xdg_popup_send_popup_done (xdg_popup->resource); return; } top_popup = meta_wayland_pointer_get_top_popup (seat->pointer); if (top_popup && parent_surface != top_popup) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "parent not top most surface"); return; } } xdg_popup->parent_surface = parent_surface; xdg_popup->parent_surface_unmapped_handler_id = g_signal_connect (parent_surface, "unmapped", G_CALLBACK (on_parent_surface_unmapped), xdg_popup); window = meta_window_wayland_new (display, surface); meta_wayland_shell_surface_set_window (shell_surface, window); meta_wayland_xdg_popup_place (xdg_popup, &xdg_popup->setup.placement_rule); if (seat) { MetaWaylandPopupSurface *popup_surface; MetaWaylandPopup *popup; meta_window_focus (window, meta_display_get_current_time (display)); popup_surface = META_WAYLAND_POPUP_SURFACE (surface->role); popup = meta_wayland_pointer_start_popup_grab (seat->pointer, popup_surface); if (popup == NULL) { xdg_popup_send_popup_done (xdg_popup->resource); meta_wayland_shell_surface_destroy_window (shell_surface); return; } xdg_popup->popup = popup; } else { /* The keyboard focus semantics for non-grabbing xdg_wm_base popups * is pretty undefined. Same applies for subsurfaces, but in practice, * subsurfaces never receive keyboard focus, so it makes sense to * do the same for non-grabbing popups. * * See https://bugzilla.gnome.org/show_bug.cgi?id=771694#c24 */ window->input = FALSE; } } static void meta_wayland_xdg_popup_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (surface_role); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandXdgSurfacePrivate *xdg_surface_priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (xdg_popup); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (xdg_popup->setup.parent_surface) finish_popup_setup (xdg_popup); if (!surface->buffer_ref.buffer && xdg_surface_priv->first_buffer_attached) { meta_wayland_xdg_surface_reset (xdg_surface); meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); return; } surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_popup_parent_class); surface_role_class->apply_state (surface_role, pending); if (xdg_popup->dismissed_by_client && surface->buffer_ref.buffer) { wl_resource_post_error (xdg_popup->resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "Can't commit buffer to dismissed popup"); return; } } static void meta_wayland_xdg_popup_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (surface_role); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_popup_parent_class); MetaWindow *window; MetaWindow *parent_window; MetaRectangle buffer_rect; MetaRectangle parent_buffer_rect; window = meta_wayland_surface_get_window (surface); if (!window) return; if (!pending->newly_attached) return; if (!surface->buffer_ref.buffer) return; surface_role_class->post_apply_state (surface_role, pending); if (pending->has_acked_configure_serial) { MetaRectangle window_geometry; window_geometry = meta_wayland_xdg_surface_get_window_geometry (xdg_surface); meta_window_wayland_finish_move_resize (window, window_geometry, pending); } parent_window = meta_wayland_surface_get_window (xdg_popup->parent_surface); meta_window_get_buffer_rect (window, &buffer_rect); meta_window_get_buffer_rect (parent_window, &parent_buffer_rect); if (!meta_rectangle_overlap (&buffer_rect, &parent_buffer_rect) && !meta_rectangle_is_adjacent_to (&buffer_rect, &parent_buffer_rect)) { g_warning ("Buggy client caused popup to be placed outside of " "parent window"); dismiss_popup (xdg_popup); } } static MetaWaylandSurface * meta_wayland_xdg_popup_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (surface_role); if (xdg_popup->parent_surface) return meta_wayland_surface_get_toplevel (xdg_popup->parent_surface); else return NULL; } static void meta_wayland_xdg_popup_reset (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (xdg_surface); MetaWaylandXdgSurfaceClass *xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_popup_parent_class); dismiss_popup (xdg_popup); xdg_popup->dismissed_by_client = TRUE; xdg_surface_class->reset (xdg_surface); } static void meta_wayland_xdg_popup_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); MetaWindow *parent_window = meta_wayland_surface_get_window (xdg_popup->parent_surface); int geometry_scale; int x, y; /* If the parent surface was destroyed, its window will be destroyed * before the popup receives the parent-destroy signal. This means that * the popup may potentially get temporary focus until itself is destroyed. * If this happen, don't try to configure the xdg_popup surface. * * FIXME: Could maybe add a signal that is emitted before the window is * created so that we can avoid incorrect intermediate foci. */ if (!parent_window) return; geometry_scale = meta_window_wayland_get_geometry_scale (parent_window); x = configuration->rel_x / geometry_scale; y = configuration->rel_y / geometry_scale; if (xdg_popup->pending_repositioned) { xdg_popup_send_repositioned (xdg_popup->resource, xdg_popup->pending_reposition_token); xdg_popup->pending_repositioned = FALSE; } xdg_popup_send_configure (xdg_popup->resource, x, y, configuration->width / configuration->scale, configuration->height / configuration->scale); meta_wayland_xdg_surface_send_configure (xdg_surface, configuration); } static void meta_wayland_xdg_popup_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (shell_surface); MetaWaylandSurface *parent = xdg_popup->parent_surface; g_assert (parent); meta_window_set_transient_for (window, meta_wayland_surface_get_window (parent)); meta_window_set_type (window, META_WINDOW_DROPDOWN_MENU); } static void meta_wayland_xdg_popup_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (xdg_surface); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandXdgSurfaceClass *xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (meta_wayland_xdg_popup_parent_class); xdg_surface_class->shell_client_destroyed (xdg_surface); if (xdg_popup->resource) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base of xdg_popup@%d was destroyed", wl_resource_get_id (xdg_popup->resource)); wl_resource_destroy (xdg_popup->resource); } } static void meta_wayland_xdg_popup_done (MetaWaylandPopupSurface *popup_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface); xdg_popup_send_popup_done (xdg_popup->resource); } static void meta_wayland_xdg_popup_dismiss (MetaWaylandPopupSurface *popup_surface) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (popup_surface); MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xdg_popup); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *top_popup; top_popup = meta_wayland_popup_get_top_popup (xdg_popup->popup); if (surface != top_popup) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_NOT_THE_TOPMOST_POPUP, "destroyed popup not top most popup"); } xdg_popup->popup = NULL; meta_wayland_xdg_popup_unmap (xdg_popup); } static MetaWaylandSurface * meta_wayland_xdg_popup_get_surface (MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (popup_surface); return meta_wayland_surface_role_get_surface (surface_role); } static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface) { iface->done = meta_wayland_xdg_popup_done; iface->dismiss = meta_wayland_xdg_popup_dismiss; iface->get_surface = meta_wayland_xdg_popup_get_surface; } static void meta_wayland_xdg_popup_finalize (GObject *object) { MetaWaylandXdgPopup *xdg_popup = META_WAYLAND_XDG_POPUP (object); g_clear_pointer (&xdg_popup->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_xdg_popup_parent_class)->finalize (object); } static void meta_wayland_xdg_popup_init (MetaWaylandXdgPopup *role) { } static void meta_wayland_xdg_popup_class_init (MetaWaylandXdgPopupClass *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; MetaWaylandXdgSurfaceClass *xdg_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_xdg_popup_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_xdg_popup_apply_state; surface_role_class->post_apply_state = meta_wayland_xdg_popup_post_apply_state; surface_role_class->get_toplevel = meta_wayland_xdg_popup_get_toplevel; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->configure = meta_wayland_xdg_popup_configure; shell_surface_class->managed = meta_wayland_xdg_popup_managed; xdg_surface_class = META_WAYLAND_XDG_SURFACE_CLASS (klass); xdg_surface_class->shell_client_destroyed = meta_wayland_xdg_popup_shell_client_destroyed; xdg_surface_class->reset = meta_wayland_xdg_popup_reset; } static struct wl_resource * meta_wayland_xdg_surface_get_wm_base_resource (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); return priv->shell_client->resource; } static MetaRectangle meta_wayland_xdg_surface_get_window_geometry (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); return priv->geometry; } static gboolean meta_wayland_xdg_surface_is_assigned (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); return priv->resource != NULL; } static void meta_wayland_xdg_surface_send_configure (MetaWaylandXdgSurface *xdg_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); xdg_surface_send_configure (priv->resource, configuration->serial); priv->configure_sent = TRUE; } static void xdg_surface_destructor (struct wl_resource *resource) { MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); priv->shell_client->surfaces = g_list_remove (priv->shell_client->surfaces, xdg_surface); priv->resource = NULL; priv->first_buffer_attached = FALSE; } static void xdg_surface_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void xdg_surface_get_toplevel (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_ROLE, "wl_surface@%d already has a role assigned", wl_resource_get_id (surface->resource)); } static void xdg_surface_get_popup (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { MetaWaylandXdgSurface *xdg_surface = wl_resource_get_user_data (resource); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); wl_resource_post_error (priv->shell_client->resource, XDG_WM_BASE_ERROR_ROLE, "wl_surface@%d already has a role assigned", wl_resource_get_id (surface->resource)); } static void xdg_surface_set_window_geometry (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->has_new_geometry = TRUE; pending->new_geometry.x = x; pending->new_geometry.y = y; pending->new_geometry.width = width; pending->new_geometry.height = height; } static void xdg_surface_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaWaylandSurface *surface = surface_from_xdg_surface_resource (resource); MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->has_acked_configure_serial = TRUE; pending->acked_configure_serial = serial; } static const struct xdg_surface_interface meta_wayland_xdg_surface_interface = { xdg_surface_destroy, xdg_surface_get_toplevel, xdg_surface_get_popup, xdg_surface_set_window_geometry, xdg_surface_ack_configure, }; static void meta_wayland_xdg_surface_finalize (GObject *object) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); g_clear_pointer (&priv->resource, wl_resource_destroy); G_OBJECT_CLASS (meta_wayland_xdg_surface_parent_class)->finalize (object); } static void meta_wayland_xdg_surface_real_reset (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); priv->first_buffer_attached = FALSE; priv->configure_sent = FALSE; priv->geometry = (MetaRectangle) { 0 }; priv->has_set_geometry = FALSE; } static void meta_wayland_xdg_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = meta_wayland_surface_get_window (surface); MetaWaylandSurfaceRoleClass *surface_role_class; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class); surface_role_class->apply_state (surface_role, pending); /* Ignore commits when unassigned. */ if (!priv->resource) return; if (!window) return; if (surface->buffer_ref.buffer) priv->first_buffer_attached = TRUE; } static void meta_wayland_xdg_surface_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface_role); if (pending->has_new_geometry) { meta_wayland_shell_surface_determine_geometry (shell_surface, &pending->new_geometry, &priv->geometry); priv->has_set_geometry = TRUE; } else if (!priv->has_set_geometry) { MetaRectangle new_geometry = { 0 }; /* If the surface has never set any geometry, calculate * a default one unioning the surface and all subsurfaces together. */ meta_wayland_shell_surface_calculate_geometry (shell_surface, &new_geometry); if (!meta_rectangle_equal (&new_geometry, &priv->geometry)) { pending->has_new_geometry = TRUE; priv->geometry = new_geometry; } } } static void meta_wayland_xdg_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (surface_role); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); struct wl_resource *xdg_wm_base_resource = meta_wayland_xdg_surface_get_wm_base_resource (xdg_surface); MetaWaylandSurfaceRoleClass *surface_role_class; priv->configure_sent = FALSE; priv->first_buffer_attached = FALSE; if (surface->buffer_ref.buffer) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "wl_surface@%d already has a buffer committed", wl_resource_get_id (surface->resource)); return; } surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_xdg_surface_parent_class); surface_role_class->assigned (surface_role); } static void meta_wayland_xdg_surface_ping (MetaWaylandShellSurface *shell_surface, uint32_t serial) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (shell_surface); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); xdg_wm_base_send_ping (priv->shell_client->resource, serial); } static void meta_wayland_xdg_surface_real_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); if (priv->resource) { wl_resource_post_error (priv->shell_client->resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base of xdg_surface@%d was destroyed", wl_resource_get_id (priv->resource)); wl_resource_destroy (priv->resource); } } static void meta_wayland_xdg_surface_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); switch (prop_id) { case XDG_SURFACE_PROP_SHELL_CLIENT: priv->shell_client = g_value_get_pointer (value); break; case XDG_SURFACE_PROP_RESOURCE: priv->resource = g_value_get_pointer (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_xdg_surface_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWaylandXdgSurface *xdg_surface = META_WAYLAND_XDG_SURFACE (object); MetaWaylandXdgSurfacePrivate *priv = meta_wayland_xdg_surface_get_instance_private (xdg_surface); switch (prop_id) { case XDG_SURFACE_PROP_SHELL_CLIENT: g_value_set_pointer (value, priv->shell_client); break; case XDG_SURFACE_PROP_RESOURCE: g_value_set_pointer (value, priv->resource); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_xdg_surface_init (MetaWaylandXdgSurface *xdg_surface) { } static void meta_wayland_xdg_surface_class_init (MetaWaylandXdgSurfaceClass *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; GParamSpec *pspec; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_xdg_surface_finalize; object_class->set_property = meta_wayland_xdg_surface_set_property; object_class->get_property = meta_wayland_xdg_surface_get_property; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = meta_wayland_xdg_surface_apply_state; surface_role_class->post_apply_state = meta_wayland_xdg_surface_post_apply_state; surface_role_class->assigned = meta_wayland_xdg_surface_assigned; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->ping = meta_wayland_xdg_surface_ping; klass->shell_client_destroyed = meta_wayland_xdg_surface_real_shell_client_destroyed; klass->reset = meta_wayland_xdg_surface_real_reset; pspec = g_param_spec_pointer ("shell-client", "MetaWaylandXdgShellClient", "The shell client instance", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, XDG_SURFACE_PROP_SHELL_CLIENT, pspec); pspec = g_param_spec_pointer ("xdg-surface-resource", "xdg_surface wl_resource", "The xdg_surface wl_resource instance", G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS); g_object_class_install_property (object_class, XDG_SURFACE_PROP_RESOURCE, pspec); } static void meta_wayland_xdg_surface_shell_client_destroyed (MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgSurfaceClass *xdg_surface_class = META_WAYLAND_XDG_SURFACE_GET_CLASS (xdg_surface); xdg_surface_class->shell_client_destroyed (xdg_surface); } static void meta_wayland_xdg_surface_constructor_finalize (MetaWaylandXdgSurfaceConstructor *constructor, MetaWaylandXdgSurface *xdg_surface) { MetaWaylandXdgShellClient *shell_client = constructor->shell_client; shell_client->surface_constructors = g_list_remove (shell_client->surface_constructors, constructor); shell_client->surfaces = g_list_append (shell_client->surfaces, xdg_surface); wl_resource_set_implementation (constructor->resource, &meta_wayland_xdg_surface_interface, xdg_surface, xdg_surface_destructor); g_free (constructor); } static void xdg_surface_constructor_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_post_error (resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface destroyed before constructed"); wl_resource_destroy (resource); } static void xdg_surface_constructor_get_toplevel (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandXdgSurfaceConstructor *constructor = wl_resource_get_user_data (resource); MetaWaylandXdgShellClient *shell_client = constructor->shell_client; struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *surface = constructor->surface; MetaWaylandXdgToplevel *xdg_toplevel; MetaWaylandXdgSurface *xdg_surface; MetaWaylandShellSurface *shell_surface; MetaWindow *window; if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_XDG_TOPLEVEL, "shell-client", shell_client, "xdg-surface-resource", xdg_surface_resource, NULL)) { wl_resource_post_error (resource, XDG_WM_BASE_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } xdg_toplevel = META_WAYLAND_XDG_TOPLEVEL (surface->role); xdg_toplevel->resource = wl_resource_create (client, &xdg_toplevel_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (xdg_toplevel->resource, &meta_wayland_xdg_toplevel_interface, xdg_toplevel, xdg_toplevel_destructor); xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_toplevel); meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface); window = meta_window_wayland_new (meta_get_display (), surface); shell_surface = META_WAYLAND_SHELL_SURFACE (xdg_surface); meta_wayland_shell_surface_set_window (shell_surface, window); } static void xdg_surface_constructor_get_popup (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *parent_resource, struct wl_resource *positioner_resource) { MetaWaylandXdgSurfaceConstructor *constructor = wl_resource_get_user_data (resource); MetaWaylandXdgShellClient *shell_client = constructor->shell_client; MetaWaylandSurface *surface = constructor->surface; struct wl_resource *xdg_wm_base_resource = constructor->shell_client->resource; struct wl_resource *xdg_surface_resource = constructor->resource; MetaWaylandSurface *parent_surface; MetaWindow *parent_window; MetaWaylandXdgPositioner *xdg_positioner; MetaWaylandXdgPopup *xdg_popup; MetaWaylandXdgSurface *xdg_surface; if (!parent_resource) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Parent surface is null but Mutter does not yet " "support specifying parent surfaces via other " "protocols"); return; } parent_surface = surface_from_xdg_surface_resource (parent_resource); if (!parent_surface || !META_IS_WAYLAND_XDG_SURFACE (parent_surface->role)) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent role"); return; } parent_window = meta_wayland_surface_get_window (parent_surface); if (!parent_window) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_INVALID_POPUP_PARENT, "Invalid popup parent window"); return; } if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_XDG_POPUP, "shell-client", shell_client, "xdg-surface-resource", xdg_surface_resource, NULL)) { wl_resource_post_error (xdg_wm_base_resource, XDG_WM_BASE_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } xdg_popup = META_WAYLAND_XDG_POPUP (surface->role); xdg_popup->resource = wl_resource_create (client, &xdg_popup_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (xdg_popup->resource, &meta_wayland_xdg_popup_interface, xdg_popup, xdg_popup_destructor); xdg_surface = META_WAYLAND_XDG_SURFACE (xdg_popup); meta_wayland_xdg_surface_constructor_finalize (constructor, xdg_surface); xdg_positioner = wl_resource_get_user_data (positioner_resource); xdg_popup->setup.placement_rule = meta_wayland_xdg_positioner_to_placement (xdg_positioner, parent_window); xdg_popup->setup.parent_surface = parent_surface; } static void xdg_surface_constructor_set_window_geometry (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { wl_resource_post_error (resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface::set_window_geometry called before constructed"); } static void xdg_surface_constructor_ack_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { wl_resource_post_error (resource, XDG_SURFACE_ERROR_NOT_CONSTRUCTED, "xdg_surface::ack_configure called before constructed"); } static const struct xdg_surface_interface meta_wayland_xdg_surface_constructor_interface = { xdg_surface_constructor_destroy, xdg_surface_constructor_get_toplevel, xdg_surface_constructor_get_popup, xdg_surface_constructor_set_window_geometry, xdg_surface_constructor_ack_configure, }; static void xdg_surface_constructor_destructor (struct wl_resource *resource) { MetaWaylandXdgSurfaceConstructor *constructor = wl_resource_get_user_data (resource); constructor->shell_client->surface_constructors = g_list_remove (constructor->shell_client->surface_constructors, constructor); g_free (constructor); } static MetaPlacementAnchor positioner_anchor_to_placement_anchor (uint32_t anchor) { switch (anchor) { case XDG_POSITIONER_ANCHOR_NONE: return META_PLACEMENT_ANCHOR_NONE; case XDG_POSITIONER_ANCHOR_TOP: return META_PLACEMENT_ANCHOR_TOP; case XDG_POSITIONER_ANCHOR_BOTTOM: return META_PLACEMENT_ANCHOR_BOTTOM; case XDG_POSITIONER_ANCHOR_LEFT: return META_PLACEMENT_ANCHOR_LEFT; case XDG_POSITIONER_ANCHOR_RIGHT: return META_PLACEMENT_ANCHOR_RIGHT; case XDG_POSITIONER_ANCHOR_TOP_LEFT: return (META_PLACEMENT_ANCHOR_TOP | META_PLACEMENT_ANCHOR_LEFT); case XDG_POSITIONER_ANCHOR_BOTTOM_LEFT: return (META_PLACEMENT_ANCHOR_BOTTOM | META_PLACEMENT_ANCHOR_LEFT); case XDG_POSITIONER_ANCHOR_TOP_RIGHT: return (META_PLACEMENT_ANCHOR_TOP | META_PLACEMENT_ANCHOR_RIGHT); case XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT: return (META_PLACEMENT_ANCHOR_BOTTOM | META_PLACEMENT_ANCHOR_RIGHT); default: g_assert_not_reached (); return META_PLACEMENT_ANCHOR_NONE; } } static MetaPlacementGravity positioner_gravity_to_placement_gravity (uint32_t gravity) { switch (gravity) { case XDG_POSITIONER_GRAVITY_NONE: return META_PLACEMENT_GRAVITY_NONE; case XDG_POSITIONER_GRAVITY_TOP: return META_PLACEMENT_GRAVITY_TOP; case XDG_POSITIONER_GRAVITY_BOTTOM: return META_PLACEMENT_GRAVITY_BOTTOM; case XDG_POSITIONER_GRAVITY_LEFT: return META_PLACEMENT_GRAVITY_LEFT; case XDG_POSITIONER_GRAVITY_RIGHT: return META_PLACEMENT_GRAVITY_RIGHT; case XDG_POSITIONER_GRAVITY_TOP_LEFT: return (META_PLACEMENT_GRAVITY_TOP | META_PLACEMENT_GRAVITY_LEFT); case XDG_POSITIONER_GRAVITY_BOTTOM_LEFT: return (META_PLACEMENT_GRAVITY_BOTTOM | META_PLACEMENT_GRAVITY_LEFT); case XDG_POSITIONER_GRAVITY_TOP_RIGHT: return (META_PLACEMENT_GRAVITY_TOP | META_PLACEMENT_GRAVITY_RIGHT); case XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT: return (META_PLACEMENT_GRAVITY_BOTTOM | META_PLACEMENT_GRAVITY_RIGHT); default: g_assert_not_reached (); return META_PLACEMENT_GRAVITY_NONE; } } static MetaPlacementRule meta_wayland_xdg_positioner_to_placement (MetaWaylandXdgPositioner *xdg_positioner, MetaWindow *parent_window) { MetaRectangle parent_rect; meta_window_get_frame_rect (parent_window, &parent_rect); if (xdg_positioner->acked_parent_configure) { MetaWindowWayland *parent_wl_window = META_WINDOW_WAYLAND (parent_window); uint32_t serial; MetaWaylandWindowConfiguration *configuration; serial = xdg_positioner->parent_configure_serial; configuration = meta_window_wayland_peek_configuration (parent_wl_window, serial); if (configuration) { if (configuration->flags & META_MOVE_RESIZE_STATE_CHANGED) { if (configuration->has_position) { parent_rect.x = configuration->x; parent_rect.y = configuration->y; } if (configuration->has_size) { parent_rect.width = configuration->width / configuration->scale; parent_rect.height = configuration->height / configuration->scale; } } else if (xdg_positioner->has_parent_size) { meta_rectangle_resize_with_gravity (&parent_rect, &parent_rect, configuration->gravity, xdg_positioner->parent_width, xdg_positioner->parent_height); } } } else if (xdg_positioner->has_parent_size) { meta_rectangle_resize_with_gravity (&parent_rect, &parent_rect, META_GRAVITY_SOUTH_EAST, xdg_positioner->parent_width, xdg_positioner->parent_height); } return (MetaPlacementRule) { .anchor_rect = xdg_positioner->anchor_rect, .gravity = positioner_gravity_to_placement_gravity (xdg_positioner->gravity), .anchor = positioner_anchor_to_placement_anchor (xdg_positioner->anchor), .constraint_adjustment = xdg_positioner->constraint_adjustment, .offset_x = xdg_positioner->offset_x, .offset_y = xdg_positioner->offset_y, .width = xdg_positioner->width, .height = xdg_positioner->height, .is_reactive = xdg_positioner->is_reactive, .parent_rect = parent_rect, }; } static void xdg_positioner_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void xdg_positioner_set_size (struct wl_client *client, struct wl_resource *resource, int32_t width, int32_t height) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); if (width <= 0 || height <= 0) { wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid size"); return; } positioner->width = width; positioner->height = height; } static void xdg_positioner_set_anchor_rect (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); if (width <= 0 || height <= 0) { wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid anchor rectangle size"); return; } positioner->anchor_rect = (MetaRectangle) { .x = x, .y = y, .width = width, .height = height, }; } static void xdg_positioner_set_anchor (struct wl_client *client, struct wl_resource *resource, uint32_t anchor) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); if (anchor > XDG_POSITIONER_ANCHOR_BOTTOM_RIGHT) { wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid anchor"); return; } positioner->anchor = anchor; } static void xdg_positioner_set_gravity (struct wl_client *client, struct wl_resource *resource, uint32_t gravity) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); if (gravity > XDG_POSITIONER_GRAVITY_BOTTOM_RIGHT) { wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid gravity"); return; } positioner->gravity = gravity; } static void xdg_positioner_set_constraint_adjustment (struct wl_client *client, struct wl_resource *resource, uint32_t constraint_adjustment) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); uint32_t all_adjustments = (XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_SLIDE_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_FLIP_Y | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_X | XDG_POSITIONER_CONSTRAINT_ADJUSTMENT_RESIZE_Y); if ((constraint_adjustment & ~all_adjustments) != 0) { wl_resource_post_error (resource, XDG_POSITIONER_ERROR_INVALID_INPUT, "Invalid constraint action"); return; } positioner->constraint_adjustment = constraint_adjustment; } static void xdg_positioner_set_offset (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); positioner->offset_x = x; positioner->offset_y = y; } static void xdg_positioner_set_reactive (struct wl_client *client, struct wl_resource *resource) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); positioner->is_reactive = TRUE; } static void xdg_positioner_set_parent_size (struct wl_client *client, struct wl_resource *resource, int32_t parent_width, int32_t parent_height) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); positioner->has_parent_size = TRUE; positioner->parent_width = parent_width; positioner->parent_height = parent_height; } static void xdg_positioner_set_parent_configure (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); positioner->acked_parent_configure = TRUE; positioner->parent_configure_serial = serial; } static const struct xdg_positioner_interface meta_wayland_xdg_positioner_interface = { xdg_positioner_destroy, xdg_positioner_set_size, xdg_positioner_set_anchor_rect, xdg_positioner_set_anchor, xdg_positioner_set_gravity, xdg_positioner_set_constraint_adjustment, xdg_positioner_set_offset, xdg_positioner_set_reactive, xdg_positioner_set_parent_size, xdg_positioner_set_parent_configure, }; static void xdg_positioner_destructor (struct wl_resource *resource) { MetaWaylandXdgPositioner *positioner = wl_resource_get_user_data (resource); g_free (positioner); } static void xdg_wm_base_destroy (struct wl_client *client, struct wl_resource *resource) { MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource); if (shell_client->surfaces || shell_client->surface_constructors) wl_resource_post_error (resource, XDG_WM_BASE_ERROR_DEFUNCT_SURFACES, "xdg_wm_base destroyed before its surfaces"); wl_resource_destroy (resource); } static void xdg_wm_base_create_positioner (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandXdgPositioner *positioner; struct wl_resource *positioner_resource; positioner = g_new0 (MetaWaylandXdgPositioner, 1); positioner_resource = wl_resource_create (client, &xdg_positioner_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (positioner_resource, &meta_wayland_xdg_positioner_interface, positioner, xdg_positioner_destructor); } static void xdg_wm_base_get_xdg_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandXdgSurfaceConstructor *constructor; if (surface->role && !META_IS_WAYLAND_XDG_SURFACE (surface->role)) { wl_resource_post_error (resource, XDG_WM_BASE_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } if (surface->role && META_IS_WAYLAND_XDG_SURFACE (surface->role) && meta_wayland_xdg_surface_is_assigned (META_WAYLAND_XDG_SURFACE (surface->role))) { wl_resource_post_error (surface_resource, XDG_WM_BASE_ERROR_ROLE, "xdg_wm_base::get_xdg_surface already requested"); return; } if (surface->buffer_ref.buffer) { wl_resource_post_error (resource, XDG_WM_BASE_ERROR_INVALID_SURFACE_STATE, "wl_surface@%d already has a buffer committed", wl_resource_get_id (surface->resource)); return; } constructor = g_new0 (MetaWaylandXdgSurfaceConstructor, 1); constructor->surface = surface; constructor->shell_client = shell_client; constructor->resource = wl_resource_create (client, &xdg_surface_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (constructor->resource, &meta_wayland_xdg_surface_constructor_interface, constructor, xdg_surface_constructor_destructor); shell_client->surface_constructors = g_list_append (shell_client->surface_constructors, constructor); } static void xdg_wm_base_pong (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaDisplay *display = meta_get_display (); meta_display_pong_for_serial (display, serial); } static const struct xdg_wm_base_interface meta_wayland_xdg_wm_base_interface = { xdg_wm_base_destroy, xdg_wm_base_create_positioner, xdg_wm_base_get_xdg_surface, xdg_wm_base_pong, }; static void meta_wayland_xdg_shell_client_destroy (MetaWaylandXdgShellClient *shell_client) { while (shell_client->surface_constructors) { MetaWaylandXdgSurfaceConstructor *constructor = g_list_first (shell_client->surface_constructors)->data; wl_resource_destroy (constructor->resource); } g_list_free (shell_client->surface_constructors); while (shell_client->surfaces) { MetaWaylandXdgSurface *xdg_surface = g_list_first (shell_client->surfaces)->data; meta_wayland_xdg_surface_shell_client_destroyed (xdg_surface); } g_list_free (shell_client->surfaces); g_free (shell_client); } static void xdg_wm_base_destructor (struct wl_resource *resource) { MetaWaylandXdgShellClient *shell_client = wl_resource_get_user_data (resource); meta_wayland_xdg_shell_client_destroy (shell_client); } static void bind_xdg_wm_base (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandXdgShellClient *shell_client; shell_client = g_new0 (MetaWaylandXdgShellClient, 1); shell_client->resource = wl_resource_create (client, &xdg_wm_base_interface, version, id); wl_resource_set_implementation (shell_client->resource, &meta_wayland_xdg_wm_base_interface, shell_client, xdg_wm_base_destructor); } void meta_wayland_xdg_shell_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &xdg_wm_base_interface, META_XDG_WM_BASE_VERSION, compositor, bind_xdg_wm_base) == NULL) g_error ("Failed to register a global xdg-shell object"); } muffin-6.4.1/src/wayland/protocol/0000775000175000017500000000000014723361714016031 5ustar fabiofabiomuffin-6.4.1/src/wayland/protocol/gtk-text-input.xml0000664000175000017500000003314614723361714021466 0ustar fabiofabio Copyright © 2012, 2013 Intel Corporation Copyright © 2015, 2016 Jan Arne Petersen Permission to use, copy, modify, distribute, and sell this software and its documentation for any purpose is hereby granted without fee, provided that the above copyright notice appear in all copies and that both that copyright notice and this permission notice appear in supporting documentation, and that the name of the copyright holders not be used in advertising or publicity pertaining to distribution of the software without specific, written prior permission. The copyright holders make no representations about the suitability of this software for any purpose. It is provided "as is" without express or implied warranty. THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. The gtk_text_input interface represents text input and input methods associated with a seat. It provides enter/leave events to follow the text input focus for a seat. Requests are used to enable/disable the text-input object and set state information like surrounding and selected text or the content type. The information about the entered text is sent to the text-input object via the pre-edit and commit_string events. Using this interface removes the need for applications to directly process hardware key events and compose text out of them. Text is valid UTF-8 encoded, indices and lengths are in bytes. Indices have to always point to the first byte of an UTF-8 encoded code point. Lengths are not allowed to contain just a part of an UTF-8 encoded code point. Focus moving throughout surfaces will result in the emission of gtk_text_input.enter and gtk_text_input.leave events. The focused surface must perform gtk_text_input.enable and gtk_text_input.disable requests as the keyboard focus moves across editable and non-editable elements of the UI. Those two requests are not expected to be paired with each other, the compositor must be able to handle consecutive series of the same request. State is sent by the state requests (set_surrounding_text, set_content_type and set_cursor_rectangle) and a commit request. After an enter event or disable request all state information is invalidated and needs to be resent by the client. This protocol defines requests and events necessary for regular clients to communicate with an input method. The gtk_input_method protocol defines the interfaces necessary to implement standalone input methods. If a compositor implements both interfaces, it will be the arbiter of the communication between both. Warning! The protocol described in this file is experimental and backward incompatible changes may be made. Backward compatible changes may be added together with the corresponding interface version bump. Backward incompatible changes are done by bumping the version number in the protocol and interface names and resetting the interface version. Once the protocol is to be declared stable, the 'z' prefix and the version number in the protocol and interface names are removed and the interface version number is reset. Destroy the wp_text_input object. Also disables all surfaces enabled through this wp_text_input object Content hint is a bitmask to allow to modify the behavior of the text input. Requests text input on a surface. The serial provided must be the one received on gtk_text_input.enter. Explicitly disable text input in a surface (typically when there is no focus on any text entry inside the surface). Sets the plain surrounding text around the input position. Text is UTF-8 encoded. Cursor is the byte offset within the surrounding text. Anchor is the byte offset of the selection anchor within the surrounding text. If there is no selected text, anchor is the same as cursor. Make sure to always send some text before and after the cursor except when the cursor is at the beginning or end of text. When there was a configure_surrounding_text event take the before_cursor and after_cursor arguments into account for picking how much surrounding text to send. There is a maximum length of wayland messages so text can not be longer than 4000 bytes. Content hint is a bitmask to allow to modify the behavior of the text input. The content purpose allows to specify the primary purpose of a text input. This allows an input method to show special purpose input panels with extra characters or to disallow some characters. Sets the content purpose and content hint. While the purpose is the basic purpose of an input field, the hint flags allow to modify some of the behavior. When no content type is explicitly set, a normal content purpose with none hint should be assumed. Sets the cursor outline as a x, y, width, height rectangle in surface local coordinates. Allows the compositor to put a window with word suggestions near the cursor. Allows to atomically send state updates from client. The previous set_surrounding_text, set_content_type and set_cursor_rectangle become effective after this call. Serial should be set to the serial from the last wp_text_input.enter event. To make sure to not receive outdated input method events after a state update, wl_display_sync() should be called after making this request. Notification that this seat's text-input focus is on a certain surface. When the seat has the keyboard capability the text-input focus follows the keyboard focus. Notification that this seat's text-input focus is no longer on a certain surface. The client should reset any preedit string previously set. The leave notification is sent before the enter notification for the new focus. When the seat has the keyboard capability the text-input focus follows the keyboard focus. Notify when a new composing text (pre-edit) should be set around the current cursor position. Any previously set composing text should be removed. Notify when text should be inserted into the editor widget. The text to commit could be either just a single character after a key press or the result of some composing (pre-edit). The text argument could be also null if some text is removed (see gtk_text_input.delete_surrounding_text). Any previously set composing text should be removed. Notify when the text around the current cursor position should be deleted. Before_length and after_length is the length (in bytes) of text before and after the current cursor position (excluding the selection) to delete. This event should be handled as part of a following commit_string or preedit_string event. A factory for text-input objects. This object is a global singleton. Destroy the wp_text_input_manager object. Creates a new text-input object for a given seat. muffin-6.4.1/src/wayland/protocol/gtk-primary-selection.xml0000664000175000017500000002371114723361714023010 0ustar fabiofabio Copyright © 2015, 2016 Red Hat Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice (including the next paragraph) shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. This protocol provides the ability to have a primary selection device to match that of the X server. This primary selection is a shortcut to the common clipboard selection, where text just needs to be selected in order to allow copying it elsewhere. The de facto way to perform this action is the middle mouse button, although it is not limited to this one. Clients wishing to honor primary selection should create a primary selection source and set it as the selection through wp_primary_selection_device.set_selection whenever the text selection changes. In order to minimize calls in pointer-driven text selection, it should happen only once after the operation finished. Similarly, a NULL source should be set when text is unselected. wp_primary_selection_offer objects are first announced through the wp_primary_selection_device.data_offer event. Immediately after this event, the primary data offer will emit wp_primary_selection_offer.offer events to let know of the mime types being offered. When the primary selection changes, the client with the keyboard focus will receive wp_primary_selection_device.selection events. Only the client with the keyboard focus will receive such events with a non-NULL wp_primary_selection_offer. Across keyboard focus changes, previously focused clients will receive wp_primary_selection_device.events with a NULL wp_primary_selection_offer. In order to request the primary selection data, the client must pass a recent serial pertaining to the press event that is triggering the operation, if the compositor deems the serial valid and recent, the wp_primary_selection_source.send event will happen in the other end to let the transfer begin. The client owning the primary selection should write the requested data, and close the file descriptor immediately. If the primary selection owner client disappeared during the transfer, the client reading the data will receive a wp_primary_selection_device.selection event with a NULL wp_primary_selection_offer, the client should take this as a hint to finish the reads related to the no longer existing offer. The primary selection owner should be checking for errors during writes, merely cancelling the ongoing transfer if any happened. The primary selection device manager is a singleton global object that provides access to the primary selection. It allows to create wp_primary_selection_source objects, as well as retrieving the per-seat wp_primary_selection_device objects. Create a new primary selection source. Create a new data device for a given seat. Destroy the primary selection device manager. Replaces the current selection. The previous owner of the primary selection will receive a wp_primary_selection_source.cancelled event. To unset the selection, set the source to NULL. Introduces a new wp_primary_selection_offer object that may be used to receive the current primary selection. Immediately following this event, the new wp_primary_selection_offer object will send wp_primary_selection_offer.offer events to describe the offered mime types. The wp_primary_selection_device.selection event is sent to notify the client of a new primary selection. This event is sent after the wp_primary_selection.data_offer event introducing this object, and after the offer has announced its mimetypes through wp_primary_selection_offer.offer. The data_offer is valid until a new offer or NULL is received or until the client loses keyboard focus. The client must destroy the previous selection data_offer, if any, upon receiving this event. Destroy the primary selection device. A wp_primary_selection_offer represents an offer to transfer the contents of the primary selection clipboard to the client. Similar to wl_data_offer, the offer also describes the mime types that the source will transferthat the data can be converted to and provides the mechanisms for transferring the data directly to the client. To transfer the contents of the primary selection clipboard, the client issues this request and indicates the mime type that it wants to receive. The transfer happens through the passed file descriptor (typically created with the pipe system call). The source client writes the data in the mime type representation requested and then closes the file descriptor. The receiving client reads from the read end of the pipe until EOF and closes its end, at which point the transfer is complete. Destroy the primary selection offer. Sent immediately after creating announcing the wp_primary_selection_offer through wp_primary_selection_device.data_offer. One event is sent per offered mime type. The source side of a wp_primary_selection_offer, it provides a way to describe the offered data and respond to requests to transfer the requested contents of the primary selection clipboard. This request adds a mime type to the set of mime types advertised to targets. Can be called several times to offer multiple types. Destroy the primary selection source. Request for the current primary selection contents from the client. Send the specified mime type over the passed file descriptor, then close it. This primary selection source is no longer valid. The client should clean up and destroy this primary selection source. muffin-6.4.1/src/wayland/protocol/gtk-shell.xml0000664000175000017500000000524314723361714020451 0ustar fabiofabio gtk_shell is a protocol extension providing additional features for clients implementing it. muffin-6.4.1/src/wayland/meta-wayland-data-device.c0000664000175000017500000011035614723361714021071 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* The file is based on src/data-device.c from Weston */ #include "config.h" #include "wayland/meta-wayland-data-device.h" #include #include #include #include #include #include #include #include "compositor/meta-dnd-actor-private.h" #include "meta/meta-selection-source-memory.h" #include "wayland/meta-selection-source-wayland-private.h" #include "wayland/meta-wayland-dnd-surface.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #define ROOTWINDOW_DROP_MIME "application/x-rootwindow-drop" static void unset_selection_source (MetaWaylandDataDevice *data_device, MetaSelectionType selection_type); static void drag_grab_data_source_destroyed (gpointer data, GObject *where_the_object_was); static struct wl_resource * create_and_send_clipboard_offer (MetaWaylandDataDevice *data_device, struct wl_resource *target); static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void default_destructor (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static struct wl_resource * create_and_send_dnd_offer (MetaWaylandDataSource *source, struct wl_resource *target) { MetaWaylandDataOffer *offer; struct wl_array *mime_types; struct wl_resource *resource; char **p; offer = meta_wayland_data_offer_new (META_SELECTION_DND, source, target); resource = meta_wayland_data_offer_get_resource (offer); wl_data_device_send_data_offer (target, resource); mime_types = meta_wayland_data_source_get_mime_types (source); wl_array_for_each (p, mime_types) wl_data_offer_send_offer (resource, *p); meta_wayland_data_offer_update_action (offer); meta_wayland_data_source_set_current_offer (source, offer); return resource; } struct _MetaWaylandDragGrab { MetaWaylandPointerGrab generic; MetaWaylandKeyboardGrab keyboard_grab; MetaWaylandSeat *seat; struct wl_client *drag_client; MetaWaylandSurface *drag_focus; gulong drag_focus_destroy_handler_id; struct wl_resource *drag_focus_data_device; struct wl_listener drag_focus_listener; MetaWaylandSurface *drag_surface; struct wl_listener drag_icon_listener; MetaWaylandDataSource *drag_data_source; ClutterActor *feedback_actor; MetaWaylandSurface *drag_origin; struct wl_listener drag_origin_listener; int drag_start_x, drag_start_y; ClutterModifierType buttons; guint need_initial_focus : 1; }; static void set_selection_source (MetaWaylandDataDevice *data_device, MetaSelectionType selection_type, MetaSelectionSource *selection_source) { MetaDisplay *display = meta_get_display (); meta_selection_set_owner (meta_display_get_selection (display), selection_type, selection_source); g_set_object (&data_device->owners[selection_type], selection_source); } static void unset_selection_source (MetaWaylandDataDevice *data_device, MetaSelectionType selection_type) { MetaDisplay *display = meta_get_display (); if (!data_device->owners[selection_type]) return; meta_selection_unset_owner (meta_display_get_selection (display), selection_type, data_device->owners[selection_type]); g_clear_object (&data_device->owners[selection_type]); } static void destroy_drag_focus (struct wl_listener *listener, void *data) { MetaWaylandDragGrab *grab = wl_container_of (listener, grab, drag_focus_listener); grab->drag_focus_data_device = NULL; g_clear_signal_handler (&grab->drag_focus_destroy_handler_id, grab->drag_focus); grab->drag_focus = NULL; } static void on_drag_focus_destroyed (MetaWaylandSurface *surface, MetaWaylandDragGrab *grab) { meta_wayland_surface_drag_dest_focus_out (grab->drag_focus); grab->drag_focus = NULL; } static void meta_wayland_drag_grab_set_source (MetaWaylandDragGrab *drag_grab, MetaWaylandDataSource *source) { if (drag_grab->drag_data_source) g_object_weak_unref (G_OBJECT (drag_grab->drag_data_source), drag_grab_data_source_destroyed, drag_grab); drag_grab->drag_data_source = source; if (source) g_object_weak_ref (G_OBJECT (source), drag_grab_data_source_destroyed, drag_grab); } static void meta_wayland_drag_source_fake_acceptance (MetaWaylandDataSource *source, const gchar *mimetype) { uint32_t actions, user_action, action = 0; meta_wayland_data_source_get_actions (source, &actions); user_action = meta_wayland_data_source_get_user_action (source); /* Pick a suitable action */ if ((user_action & actions) != 0) action = user_action; else if (actions != 0) action = 1 << (ffs (actions) - 1); /* Bail out if there is none, source didn't cooperate */ if (action == 0) return; meta_wayland_data_source_target (source, mimetype); meta_wayland_data_source_set_current_action (source, action); meta_wayland_data_source_set_has_target (source, TRUE); } void meta_wayland_drag_grab_set_focus (MetaWaylandDragGrab *drag_grab, MetaWaylandSurface *surface) { MetaWaylandSeat *seat = drag_grab->seat; MetaWaylandDataSource *source = drag_grab->drag_data_source; struct wl_client *client; struct wl_resource *data_device_resource, *offer = NULL; if (!drag_grab->need_initial_focus && drag_grab->drag_focus == surface) return; drag_grab->need_initial_focus = FALSE; if (drag_grab->drag_focus) { meta_wayland_surface_drag_dest_focus_out (drag_grab->drag_focus); g_clear_signal_handler (&drag_grab->drag_focus_destroy_handler_id, drag_grab->drag_focus); drag_grab->drag_focus = NULL; } if (source) meta_wayland_data_source_set_current_offer (source, NULL); if (!surface && source && meta_wayland_data_source_has_mime_type (source, ROOTWINDOW_DROP_MIME)) meta_wayland_drag_source_fake_acceptance (source, ROOTWINDOW_DROP_MIME); else if (source) meta_wayland_data_source_target (source, NULL); if (!surface) return; if (!source && wl_resource_get_client (surface->resource) != drag_grab->drag_client) return; client = wl_resource_get_client (surface->resource); data_device_resource = wl_resource_find_for_client (&seat->data_device.resource_list, client); if (!data_device_resource) { data_device_resource = wl_resource_find_for_client (&seat->data_device.focus_resource_list, client); } if (source && data_device_resource) offer = create_and_send_dnd_offer (source, data_device_resource); drag_grab->drag_focus = surface; drag_grab->drag_focus_destroy_handler_id = g_signal_connect (surface, "destroy", G_CALLBACK (on_drag_focus_destroyed), drag_grab); drag_grab->drag_focus_data_device = data_device_resource; meta_wayland_surface_drag_dest_focus_in (drag_grab->drag_focus, offer ? wl_resource_get_user_data (offer) : NULL); } MetaWaylandSurface * meta_wayland_drag_grab_get_focus (MetaWaylandDragGrab *drag_grab) { return drag_grab->drag_focus; } void meta_wayland_drag_grab_update_feedback_actor (MetaWaylandDragGrab *drag_grab, ClutterEvent *event) { meta_feedback_actor_update (META_FEEDBACK_ACTOR (drag_grab->feedback_actor), event); } static void drag_grab_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { MetaWaylandDragGrab *drag_grab = (MetaWaylandDragGrab*) grab; meta_wayland_drag_grab_set_focus (drag_grab, surface); } static void data_source_update_user_dnd_action (MetaWaylandDataSource *source, ClutterModifierType modifiers) { enum wl_data_device_manager_dnd_action user_dnd_action = 0; if (modifiers & CLUTTER_SHIFT_MASK) user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; else if (modifiers & CLUTTER_CONTROL_MASK) user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; else if (modifiers & (CLUTTER_MOD1_MASK | CLUTTER_BUTTON2_MASK)) user_dnd_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; meta_wayland_data_source_set_user_action (source, user_dnd_action); } static void drag_grab_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandDragGrab *drag_grab = (MetaWaylandDragGrab*) grab; if (drag_grab->drag_focus) meta_wayland_surface_drag_dest_motion (drag_grab->drag_focus, event); if (drag_grab->drag_surface) meta_feedback_actor_update (META_FEEDBACK_ACTOR (drag_grab->feedback_actor), event); } static void data_device_end_drag_grab (MetaWaylandDragGrab *drag_grab) { meta_wayland_drag_grab_set_source (drag_grab, NULL); meta_wayland_drag_grab_set_focus (drag_grab, NULL); if (drag_grab->drag_origin) { drag_grab->drag_origin = NULL; wl_list_remove (&drag_grab->drag_origin_listener.link); } if (drag_grab->drag_surface) { drag_grab->drag_surface = NULL; wl_list_remove (&drag_grab->drag_icon_listener.link); } if (drag_grab->feedback_actor) { clutter_actor_remove_all_children (drag_grab->feedback_actor); clutter_actor_destroy (drag_grab->feedback_actor); } drag_grab->seat->data_device.current_grab = NULL; /* There might be other grabs created in result to DnD actions like popups * on "ask" actions, we must not reset those, only our own. */ if (drag_grab->generic.pointer->grab == (MetaWaylandPointerGrab *) drag_grab) { meta_wayland_pointer_end_grab (drag_grab->generic.pointer); meta_wayland_keyboard_end_grab (drag_grab->keyboard_grab.keyboard); meta_display_sync_wayland_input_focus (meta_get_display ()); } g_slice_free (MetaWaylandDragGrab, drag_grab); } static gboolean on_fake_read_hup (GIOChannel *channel, GIOCondition condition, gpointer data) { MetaWaylandDataSource *source = data; meta_wayland_data_source_notify_finish (source); g_io_channel_shutdown (channel, FALSE, NULL); g_io_channel_unref (channel); return G_SOURCE_REMOVE; } static void meta_wayland_data_source_fake_read (MetaWaylandDataSource *source, const gchar *mimetype) { GIOChannel *channel; int p[2]; if (!g_unix_open_pipe (p, FD_CLOEXEC, NULL)) { meta_wayland_data_source_notify_finish (source); return; } if (!g_unix_set_fd_nonblocking (p[0], TRUE, NULL) || !g_unix_set_fd_nonblocking (p[1], TRUE, NULL)) { meta_wayland_data_source_notify_finish (source); close (p[0]); close (p[1]); return; } meta_wayland_data_source_send (source, mimetype, p[1]); channel = g_io_channel_unix_new (p[0]); g_io_add_watch (channel, G_IO_HUP, on_fake_read_hup, source); } static void drag_grab_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandDragGrab *drag_grab = (MetaWaylandDragGrab*) grab; MetaWaylandSeat *seat = drag_grab->seat; ClutterEventType event_type = clutter_event_type (event); if (drag_grab->generic.pointer->grab_button == clutter_event_get_button (event) && event_type == CLUTTER_BUTTON_RELEASE) { MetaWaylandDataSource *source = drag_grab->drag_data_source; gboolean success; if (drag_grab->drag_focus && source && meta_wayland_data_source_has_target (source) && meta_wayland_data_source_get_current_action (source)) { meta_wayland_surface_drag_dest_drop (drag_grab->drag_focus); meta_wayland_data_source_notify_drop_performed (source); meta_wayland_data_source_update_in_ask (source); success = TRUE; } else if (!drag_grab->drag_focus && source && meta_wayland_data_source_has_target (source) && meta_wayland_data_source_get_current_action (source) && meta_wayland_data_source_has_mime_type (source, ROOTWINDOW_DROP_MIME)) { /* Perform a fake read, that will lead to notify_finish() being called */ meta_wayland_data_source_fake_read (source, ROOTWINDOW_DROP_MIME); success = TRUE; } else { if (source) meta_wayland_data_source_set_current_offer (source, NULL); meta_wayland_data_device_set_dnd_source (&seat->data_device, NULL); unset_selection_source (&seat->data_device, META_SELECTION_DND); success = FALSE; } /* Finish drag and let actor self-destruct */ meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor), success); drag_grab->feedback_actor = NULL; } if (seat->pointer->button_count == 0 && event_type == CLUTTER_BUTTON_RELEASE) data_device_end_drag_grab (drag_grab); } static const MetaWaylandPointerGrabInterface drag_grab_interface = { drag_grab_focus, drag_grab_motion, drag_grab_button, }; static gboolean keyboard_drag_grab_key (MetaWaylandKeyboardGrab *grab, const ClutterEvent *event) { if (event->key.keyval == CLUTTER_KEY_Escape) { MetaWaylandDragGrab *drag_grab; drag_grab = wl_container_of (grab, drag_grab, keyboard_grab); meta_wayland_data_source_cancel (drag_grab->drag_data_source); meta_wayland_data_source_set_current_offer (drag_grab->drag_data_source, NULL); meta_dnd_actor_drag_finish (META_DND_ACTOR (drag_grab->feedback_actor), FALSE); drag_grab->feedback_actor = NULL; data_device_end_drag_grab (drag_grab); return TRUE; } return FALSE; } static void keyboard_drag_grab_modifiers (MetaWaylandKeyboardGrab *grab, ClutterModifierType modifiers) { MetaWaylandDragGrab *drag_grab; drag_grab = wl_container_of (grab, drag_grab, keyboard_grab); /* The modifiers here just contain keyboard modifiers, mix it with the * mouse button modifiers we got when starting the drag operation. */ modifiers |= drag_grab->buttons; if (drag_grab->drag_data_source) { data_source_update_user_dnd_action (drag_grab->drag_data_source, modifiers); if (drag_grab->drag_focus) meta_wayland_surface_drag_dest_update (drag_grab->drag_focus); } } static const MetaWaylandKeyboardGrabInterface keyboard_drag_grab_interface = { keyboard_drag_grab_key, keyboard_drag_grab_modifiers }; static void destroy_data_device_origin (struct wl_listener *listener, void *data) { MetaWaylandDragGrab *drag_grab = wl_container_of (listener, drag_grab, drag_origin_listener); drag_grab->drag_origin = NULL; meta_wayland_data_device_set_dnd_source (&drag_grab->seat->data_device, NULL); unset_selection_source (&drag_grab->seat->data_device, META_SELECTION_DND); meta_wayland_data_source_set_current_offer (drag_grab->drag_data_source, NULL); data_device_end_drag_grab (drag_grab); } static void drag_grab_data_source_destroyed (gpointer data, GObject *where_the_object_was) { MetaWaylandDragGrab *drag_grab = data; drag_grab->drag_data_source = NULL; data_device_end_drag_grab (drag_grab); } static void destroy_data_device_icon (struct wl_listener *listener, void *data) { MetaWaylandDragGrab *drag_grab = wl_container_of (listener, drag_grab, drag_icon_listener); drag_grab->drag_surface = NULL; if (drag_grab->feedback_actor) clutter_actor_remove_all_children (drag_grab->feedback_actor); } void meta_wayland_data_device_start_drag (MetaWaylandDataDevice *data_device, struct wl_client *client, const MetaWaylandPointerGrabInterface *funcs, MetaWaylandSurface *surface, MetaWaylandDataSource *source, MetaWaylandSurface *icon_surface) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); MetaWaylandDragGrab *drag_grab; graphene_point_t pos, surface_pos; ClutterModifierType modifiers; MetaSurfaceActor *surface_actor; data_device->current_grab = drag_grab = g_slice_new0 (MetaWaylandDragGrab); drag_grab->generic.interface = funcs; drag_grab->generic.pointer = seat->pointer; drag_grab->keyboard_grab.interface = &keyboard_drag_grab_interface; drag_grab->keyboard_grab.keyboard = seat->keyboard; drag_grab->drag_client = client; drag_grab->seat = seat; drag_grab->drag_origin = surface; drag_grab->drag_origin_listener.notify = destroy_data_device_origin; wl_resource_add_destroy_listener (surface->resource, &drag_grab->drag_origin_listener); surface_actor = meta_wayland_surface_get_actor (surface); clutter_actor_transform_stage_point (CLUTTER_ACTOR (surface_actor), seat->pointer->grab_x, seat->pointer->grab_y, &surface_pos.x, &surface_pos.y); drag_grab->drag_start_x = surface_pos.x; drag_grab->drag_start_y = surface_pos.y; drag_grab->need_initial_focus = TRUE; modifiers = clutter_input_device_get_modifier_state (seat->pointer->device); drag_grab->buttons = modifiers & (CLUTTER_BUTTON1_MASK | CLUTTER_BUTTON2_MASK | CLUTTER_BUTTON3_MASK | CLUTTER_BUTTON4_MASK | CLUTTER_BUTTON5_MASK); meta_wayland_drag_grab_set_source (drag_grab, source); meta_wayland_data_device_set_dnd_source (data_device, drag_grab->drag_data_source); data_source_update_user_dnd_action (source, modifiers); if (icon_surface) { ClutterActor *drag_surface_actor; drag_grab->drag_surface = icon_surface; drag_grab->drag_icon_listener.notify = destroy_data_device_icon; wl_resource_add_destroy_listener (icon_surface->resource, &drag_grab->drag_icon_listener); drag_surface_actor = CLUTTER_ACTOR (meta_wayland_surface_get_actor (drag_grab->drag_surface)); drag_grab->feedback_actor = meta_dnd_actor_new (CLUTTER_ACTOR (surface_actor), drag_grab->drag_start_x, drag_grab->drag_start_y); meta_feedback_actor_set_anchor (META_FEEDBACK_ACTOR (drag_grab->feedback_actor), 0, 0); clutter_actor_add_child (drag_grab->feedback_actor, drag_surface_actor); clutter_input_device_get_coords (seat->pointer->device, NULL, &pos); meta_feedback_actor_set_position (META_FEEDBACK_ACTOR (drag_grab->feedback_actor), pos.x, pos.y); } meta_wayland_pointer_start_grab (seat->pointer, (MetaWaylandPointerGrab*) drag_grab); meta_wayland_data_source_set_seat (source, seat); } void meta_wayland_data_device_end_drag (MetaWaylandDataDevice *data_device) { if (data_device->current_grab) data_device_end_drag_grab (data_device->current_grab); } static void data_device_start_drag (struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, guint32 serial) { MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); MetaWaylandSurface *surface = NULL, *icon_surface = NULL; MetaWaylandDataSource *drag_source = NULL; MetaSelectionSource *selection_source; if (origin_resource) surface = wl_resource_get_user_data (origin_resource); if (!surface) return; if (seat->pointer->button_count == 0 || seat->pointer->grab_serial != serial || !seat->pointer->focus_surface || seat->pointer->focus_surface != surface) return; /* FIXME: Check that the data source type array isn't empty. */ if (data_device->current_grab || seat->pointer->grab != &seat->pointer->default_grab) return; if (icon_resource) icon_surface = wl_resource_get_user_data (icon_resource); if (source_resource) drag_source = wl_resource_get_user_data (source_resource); if (icon_resource && !meta_wayland_surface_assign_role (icon_surface, META_TYPE_WAYLAND_SURFACE_ROLE_DND, NULL)) { wl_resource_post_error (resource, WL_DATA_DEVICE_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (icon_resource)); return; } selection_source = meta_selection_source_wayland_new (drag_source); set_selection_source (data_device, META_SELECTION_DND, selection_source); g_object_unref (selection_source); meta_wayland_pointer_set_focus (seat->pointer, NULL); meta_wayland_data_device_start_drag (data_device, client, &drag_grab_interface, surface, drag_source, icon_surface); if (meta_wayland_seat_has_keyboard (seat)) meta_wayland_keyboard_start_grab (seat->keyboard, &seat->data_device.current_grab->keyboard_grab); } static void selection_data_source_destroyed (gpointer data, GObject *object_was_here) { MetaWaylandDataDevice *data_device = data; data_device->selection_data_source = NULL; unset_selection_source (data_device, META_SELECTION_CLIPBOARD); } static void meta_wayland_drag_dest_focus_in (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, MetaWaylandDataOffer *offer) { MetaWaylandDragGrab *grab = data_device->current_grab; MetaWaylandDataSource *source; struct wl_display *display; struct wl_client *client; struct wl_resource *resource; uint32_t source_actions; wl_fixed_t sx, sy; if (!grab->drag_focus_data_device) return; client = wl_resource_get_client (surface->resource); display = wl_client_get_display (client); grab->drag_focus_listener.notify = destroy_drag_focus; wl_resource_add_destroy_listener (grab->drag_focus_data_device, &grab->drag_focus_listener); resource = meta_wayland_data_offer_get_resource (offer); if (wl_resource_get_version (resource) >= WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) { source = meta_wayland_data_offer_get_source (offer); meta_wayland_data_source_get_actions (source, &source_actions); wl_data_offer_send_source_actions (resource, source_actions); } meta_wayland_pointer_get_relative_coordinates (grab->generic.pointer, surface, &sx, &sy); wl_data_device_send_enter (grab->drag_focus_data_device, wl_display_next_serial (display), surface->resource, sx, sy, resource); } static void meta_wayland_drag_dest_focus_out (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandDragGrab *grab = data_device->current_grab; if (!grab->drag_focus_data_device) return; wl_data_device_send_leave (grab->drag_focus_data_device); wl_list_remove (&grab->drag_focus_listener.link); grab->drag_focus_data_device = NULL; } static void meta_wayland_drag_dest_motion (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, const ClutterEvent *event) { MetaWaylandDragGrab *grab = data_device->current_grab; wl_fixed_t sx, sy; if (!grab->drag_focus_data_device) return; meta_wayland_pointer_get_relative_coordinates (grab->generic.pointer, grab->drag_focus, &sx, &sy); wl_data_device_send_motion (grab->drag_focus_data_device, clutter_event_get_time (event), sx, sy); } static void meta_wayland_drag_dest_drop (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { MetaWaylandDragGrab *grab = data_device->current_grab; if (!grab->drag_focus_data_device) return; wl_data_device_send_drop (grab->drag_focus_data_device); } static void meta_wayland_drag_dest_update (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { } static const MetaWaylandDragDestFuncs meta_wayland_drag_dest_funcs = { meta_wayland_drag_dest_focus_in, meta_wayland_drag_dest_focus_out, meta_wayland_drag_dest_motion, meta_wayland_drag_dest_drop, meta_wayland_drag_dest_update }; const MetaWaylandDragDestFuncs * meta_wayland_data_device_get_drag_dest_funcs (void) { return &meta_wayland_drag_dest_funcs; } static void dnd_data_source_destroyed (gpointer data, GObject *object_was_here) { MetaWaylandDataDevice *data_device = data; data_device->dnd_data_source = NULL; unset_selection_source (data_device, META_SELECTION_DND); } void meta_wayland_data_device_set_dnd_source (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source) { if (data_device->dnd_data_source == source) return; if (data_device->dnd_data_source) { g_object_weak_unref (G_OBJECT (data_device->dnd_data_source), dnd_data_source_destroyed, data_device); } data_device->dnd_data_source = source; if (source) { g_object_weak_ref (G_OBJECT (source), dnd_data_source_destroyed, data_device); } } void meta_wayland_data_device_set_selection (MetaWaylandDataDevice *data_device, MetaWaylandDataSource *source, guint32 serial) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); MetaSelectionSource *selection_source; if (data_device->selection_data_source && data_device->selection_serial - serial < UINT32_MAX / 2) return; if (data_device->selection_data_source) { g_object_weak_unref (G_OBJECT (data_device->selection_data_source), selection_data_source_destroyed, data_device); data_device->selection_data_source = NULL; } data_device->selection_data_source = source; data_device->selection_serial = serial; if (source) { meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), selection_data_source_destroyed, data_device); selection_source = meta_selection_source_wayland_new (source); } else { selection_source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); } set_selection_source (data_device, META_SELECTION_CLIPBOARD, selection_source); g_object_unref (selection_source); } static void data_device_set_selection (struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, guint32 serial) { MetaWaylandDataDevice *data_device = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); MetaWaylandDataSource *source; if (source_resource) source = wl_resource_get_user_data (source_resource); else source = NULL; if (source) { if (meta_wayland_data_source_get_actions (source, NULL)) { wl_resource_post_error(source_resource, WL_DATA_SOURCE_ERROR_INVALID_SOURCE, "cannot set drag-and-drop source as selection"); return; } } if (wl_resource_get_client (resource) != meta_wayland_keyboard_get_focus_client (seat->keyboard)) return; /* FIXME: Store serial and check against incoming serial here. */ meta_wayland_data_device_set_selection (data_device, source, serial); } static const struct wl_data_device_interface data_device_interface = { data_device_start_drag, data_device_set_selection, default_destructor, }; static void create_data_source (struct wl_client *client, struct wl_resource *resource, guint32 id) { struct wl_resource *source_resource; source_resource = wl_resource_create (client, &wl_data_source_interface, wl_resource_get_version (resource), id); meta_wayland_data_source_new (source_resource); } static void owner_changed_cb (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *new_owner, MetaWaylandDataDevice *data_device) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandSeat *seat = compositor->seat; struct wl_resource *data_device_resource; struct wl_client *focus_client; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (!focus_client) return; if (selection_type == META_SELECTION_CLIPBOARD) { wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer = NULL; if (new_owner) { offer = create_and_send_clipboard_offer (data_device, data_device_resource); } wl_data_device_send_selection (data_device_resource, offer); } } } static void ensure_owners_changed_handler_connected (MetaWaylandDataDevice *data_device) { if (data_device->selection_owner_signal_id != 0) return; data_device->selection_owner_signal_id = g_signal_connect (meta_display_get_selection (meta_get_display ()), "owner-changed", G_CALLBACK (owner_changed_cb), data_device); } static void get_data_device (struct wl_client *client, struct wl_resource *manager_resource, guint32 id, struct wl_resource *seat_resource) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); struct wl_resource *cr; cr = wl_resource_create (client, &wl_data_device_interface, wl_resource_get_version (manager_resource), id); wl_resource_set_implementation (cr, &data_device_interface, &seat->data_device, unbind_resource); wl_list_insert (&seat->data_device.resource_list, wl_resource_get_link (cr)); ensure_owners_changed_handler_connected (&seat->data_device); } static const struct wl_data_device_manager_interface manager_interface = { create_data_source, get_data_device }; static void bind_manager (struct wl_client *client, void *data, guint32 version, guint32 id) { struct wl_resource *resource; resource = wl_resource_create (client, &wl_data_device_manager_interface, version, id); wl_resource_set_implementation (resource, &manager_interface, NULL, NULL); } void meta_wayland_data_device_manager_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &wl_data_device_manager_interface, META_WL_DATA_DEVICE_MANAGER_VERSION, NULL, bind_manager) == NULL) g_error ("Could not create data_device"); } void meta_wayland_data_device_init (MetaWaylandDataDevice *data_device) { wl_list_init (&data_device->resource_list); wl_list_init (&data_device->focus_resource_list); } static struct wl_resource * create_and_send_clipboard_offer (MetaWaylandDataDevice *data_device, struct wl_resource *target) { MetaWaylandDataOffer *offer; MetaDisplay *display = meta_get_display (); struct wl_resource *resource; GList *mimetypes, *l; mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (display), META_SELECTION_CLIPBOARD); if (!mimetypes) return NULL; offer = meta_wayland_data_offer_new (META_SELECTION_CLIPBOARD, NULL, target); resource = meta_wayland_data_offer_get_resource (offer); wl_data_device_send_data_offer (target, resource); for (l = mimetypes; l; l = l->next) wl_data_offer_send_offer (resource, l->data); g_list_free_full (mimetypes, g_free); return resource; } void meta_wayland_data_device_set_keyboard_focus (MetaWaylandDataDevice *data_device) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, data_device); struct wl_client *focus_client; struct wl_resource *data_device_resource; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client == data_device->focus_client) return; data_device->focus_client = focus_client; move_resources (&data_device->resource_list, &data_device->focus_resource_list); if (!focus_client) return; move_resources_for_client (&data_device->focus_resource_list, &data_device->resource_list, focus_client); wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer; offer = create_and_send_clipboard_offer (data_device, data_device_resource); wl_data_device_send_selection (data_device_resource, offer); } } gboolean meta_wayland_data_device_is_dnd_surface (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface) { return data_device->current_grab && data_device->current_grab->drag_surface == surface; } MetaWaylandDragGrab * meta_wayland_data_device_get_current_grab (MetaWaylandDataDevice *data_device) { return data_device->current_grab; } void meta_wayland_data_device_unset_dnd_selection (MetaWaylandDataDevice *data_device) { unset_selection_source (data_device, META_SELECTION_DND); } muffin-6.4.1/src/wayland/meta-xwayland-private.h0000664000175000017500000000373014723361714020567 0ustar fabiofabio/* * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_XWAYLAND_PRIVATE_H #define META_XWAYLAND_PRIVATE_H #include #include "wayland/meta-wayland-private.h" gboolean meta_xwayland_init (MetaXWaylandManager *manager, struct wl_display *display); void meta_xwayland_complete_init (MetaDisplay *display, Display *xdisplay); void meta_xwayland_shutdown (MetaXWaylandManager *manager); /* wl_data_device/X11 selection interoperation */ void meta_xwayland_init_dnd (Display *xdisplay); void meta_xwayland_shutdown_dnd (Display *xdisplay); gboolean meta_xwayland_dnd_handle_event (XEvent *xevent); const MetaWaylandDragDestFuncs * meta_xwayland_selection_get_drag_dest_funcs (void); void meta_xwayland_start_xserver (MetaXWaylandManager *manager, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data); gboolean meta_xwayland_start_xserver_finish (MetaXWaylandManager *manager, GAsyncResult *result, GError **error); #endif /* META_XWAYLAND_PRIVATE_H */ muffin-6.4.1/src/wayland/meta-wayland-dma-buf.c0000664000175000017500000004733414723361714020243 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * Copyright (C) 2017 Intel Corporation * Copyright (C) 2018,2019 DisplayLink (UK) Ltd. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl * Daniel Stone */ /** * SECTION:meta-wayland-dma-buf * @title: MetaWaylandDmaBuf * @short_description: Handles passing DMA-BUFs in Wayland * * The MetaWaylandDmaBuf namespace contains several objects and functions to * handle DMA-BUF buffers that are passed through from clients in Wayland (e.g. * using the linux_dmabuf_unstable_v1 protocol). */ #include "config.h" #include "wayland/meta-wayland-dma-buf.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-egl-ext.h" #include "backends/meta-egl.h" #include "cogl/cogl-egl.h" #include "cogl/cogl.h" #include "meta/meta-backend.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" #include "linux-dmabuf-unstable-v1-server-protocol.h" #ifndef DRM_FORMAT_MOD_INVALID #define DRM_FORMAT_MOD_INVALID ((1ULL << 56) - 1) #endif #define META_WAYLAND_DMA_BUF_MAX_FDS 4 struct _MetaWaylandDmaBufBuffer { GObject parent; int width; int height; uint32_t drm_format; uint64_t drm_modifier; bool is_y_inverted; int fds[META_WAYLAND_DMA_BUF_MAX_FDS]; uint32_t offsets[META_WAYLAND_DMA_BUF_MAX_FDS]; uint32_t strides[META_WAYLAND_DMA_BUF_MAX_FDS]; }; G_DEFINE_TYPE (MetaWaylandDmaBufBuffer, meta_wayland_dma_buf_buffer, G_TYPE_OBJECT); static gboolean meta_wayland_dma_buf_realize_texture (MetaWaylandBuffer *buffer, GError **error) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); MetaWaylandDmaBufBuffer *dma_buf = buffer->dma_buf.dma_buf; uint32_t n_planes; uint64_t modifiers[META_WAYLAND_DMA_BUF_MAX_FDS]; CoglPixelFormat cogl_format; EGLImageKHR egl_image; CoglEglImageFlags flags; CoglTexture2D *texture; if (buffer->dma_buf.texture) return TRUE; switch (dma_buf->drm_format) { /* * NOTE: The cogl_format here is only used for texture color channel * swizzling as compared to COGL_PIXEL_FORMAT_ARGB. It is *not* used * for accessing the buffer memory. EGL will access the buffer * memory according to the DRM fourcc code. Cogl will not mmap * and access the buffer memory at all. */ case DRM_FORMAT_XRGB8888: cogl_format = COGL_PIXEL_FORMAT_RGB_888; break; case DRM_FORMAT_ARGB8888: cogl_format = COGL_PIXEL_FORMAT_ARGB_8888_PRE; break; case DRM_FORMAT_ARGB2101010: cogl_format = COGL_PIXEL_FORMAT_ARGB_2101010_PRE; break; case DRM_FORMAT_RGB565: cogl_format = COGL_PIXEL_FORMAT_RGB_565; break; default: g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unsupported buffer format %d", dma_buf->drm_format); return FALSE; } for (n_planes = 0; n_planes < META_WAYLAND_DMA_BUF_MAX_FDS; n_planes++) { if (dma_buf->fds[n_planes] < 0) break; modifiers[n_planes] = dma_buf->drm_modifier; } egl_image = meta_egl_create_dmabuf_image (egl, egl_display, dma_buf->width, dma_buf->height, dma_buf->drm_format, n_planes, dma_buf->fds, dma_buf->strides, dma_buf->offsets, modifiers, error); if (egl_image == EGL_NO_IMAGE_KHR) return FALSE; flags = COGL_EGL_IMAGE_FLAG_NO_GET_DATA; texture = cogl_egl_texture_2d_new_from_image (cogl_context, dma_buf->width, dma_buf->height, cogl_format, egl_image, flags, error); meta_egl_destroy_image (egl, egl_display, egl_image, NULL); if (!texture) return FALSE; buffer->dma_buf.texture = COGL_TEXTURE (texture); buffer->is_y_inverted = dma_buf->is_y_inverted; return TRUE; } gboolean meta_wayland_dma_buf_buffer_attach (MetaWaylandBuffer *buffer, CoglTexture **texture, GError **error) { if (!meta_wayland_dma_buf_realize_texture (buffer, error)) return FALSE; cogl_clear_object (texture); *texture = cogl_object_ref (buffer->dma_buf.texture); return TRUE; } static void buffer_params_add (struct wl_client *client, struct wl_resource *resource, int32_t fd, uint32_t plane_idx, uint32_t offset, uint32_t stride, uint32_t drm_modifier_hi, uint32_t drm_modifier_lo) { MetaWaylandDmaBufBuffer *dma_buf; uint64_t drm_modifier; drm_modifier = ((uint64_t) drm_modifier_hi) << 32; drm_modifier |= ((uint64_t) drm_modifier_lo) & 0xffffffff; dma_buf = wl_resource_get_user_data (resource); if (!dma_buf) { wl_resource_post_error (resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params already used"); return; } if (plane_idx >= META_WAYLAND_DMA_BUF_MAX_FDS) { wl_resource_post_error (resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_IDX, "out-of-bounds plane index %d", plane_idx); return; } if (dma_buf->fds[plane_idx] != -1) { wl_resource_post_error (resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_PLANE_SET, "plane index %d already set", plane_idx); return; } if (dma_buf->drm_modifier != DRM_FORMAT_MOD_INVALID && dma_buf->drm_modifier != drm_modifier) { wl_resource_post_error (resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "mismatching modifier between planes"); return; } dma_buf->drm_modifier = drm_modifier; dma_buf->fds[plane_idx] = fd; dma_buf->offsets[plane_idx] = offset; dma_buf->strides[plane_idx] = stride; } static void buffer_params_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void buffer_params_destructor (struct wl_resource *resource) { MetaWaylandDmaBufBuffer *dma_buf; /* The user-data for our MetaWaylandBuffer is only valid in between adding * FDs and creating the buffer; once it is created, we free it out into * the wild, where the ref is considered transferred to the wl_buffer. */ dma_buf = wl_resource_get_user_data (resource); if (dma_buf) g_object_unref (dma_buf); } static void buffer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_buffer_interface dma_buf_buffer_impl = { buffer_destroy, }; /** * meta_wayland_dma_buf_from_buffer: * @buffer: A #MetaWaylandBuffer object * * Fetches the associated #MetaWaylandDmaBufBuffer from the wayland buffer. * This does not *create* a new object, as this happens in the create_params * request of linux_dmabuf_unstable_v1. * * Returns: (transfer none): The corresponding #MetaWaylandDmaBufBuffer (or * %NULL if it wasn't a dma_buf-based wayland buffer) */ MetaWaylandDmaBufBuffer * meta_wayland_dma_buf_from_buffer (MetaWaylandBuffer *buffer) { if (wl_resource_instance_of (buffer->resource, &wl_buffer_interface, &dma_buf_buffer_impl)) return wl_resource_get_user_data (buffer->resource); return NULL; } static void buffer_params_create_common (struct wl_client *client, struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t drm_format, uint32_t flags) { MetaWaylandDmaBufBuffer *dma_buf; MetaWaylandBuffer *buffer; struct wl_resource *buffer_resource; GError *error = NULL; dma_buf = wl_resource_get_user_data (params_resource); if (!dma_buf) { wl_resource_post_error (params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_ALREADY_USED, "params already used"); return; } /* Calling the 'create' method is the point of no return: after that point, * the params object cannot be used. This method must either transfer the * ownership of the MetaWaylandDmaBufBuffer to a MetaWaylandBuffer, or * destroy it. */ wl_resource_set_user_data (params_resource, NULL); if (dma_buf->fds[0] == -1) { wl_resource_post_error (params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "no planes added to params"); g_object_unref (dma_buf); return; } if ((dma_buf->fds[3] >= 0 || dma_buf->fds[2] >= 0) && (dma_buf->fds[2] == -1 || dma_buf->fds[1] == -1)) { wl_resource_post_error (params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INCOMPLETE, "gap in planes added to params"); g_object_unref (dma_buf); return; } dma_buf->width = width; dma_buf->height = height; dma_buf->drm_format = drm_format; dma_buf->is_y_inverted = !(flags & ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT); if (flags & ~ZWP_LINUX_BUFFER_PARAMS_V1_FLAGS_Y_INVERT) { wl_resource_post_error (params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "unknown flags 0x%x supplied", flags); g_object_unref (dma_buf); return; } /* Create a new MetaWaylandBuffer wrapping our dmabuf, and immediately try * to realize it, so we can give the client success/fail feedback for the * import. */ buffer_resource = wl_resource_create (client, &wl_buffer_interface, 1, buffer_id); wl_resource_set_implementation (buffer_resource, &dma_buf_buffer_impl, dma_buf, NULL); buffer = meta_wayland_buffer_from_resource (buffer_resource); meta_wayland_buffer_realize (buffer); if (!meta_wayland_dma_buf_realize_texture (buffer, &error)) { if (buffer_id == 0) { zwp_linux_buffer_params_v1_send_failed (params_resource); } else { wl_resource_post_error (params_resource, ZWP_LINUX_BUFFER_PARAMS_V1_ERROR_INVALID_WL_BUFFER, "failed to import supplied dmabufs: %s", error ? error->message : "unknown error"); } /* will unref the MetaWaylandBuffer */ wl_resource_destroy (buffer->resource); return; } /* If buffer_id is 0, we are using the non-immediate interface, so * need to send a success event with our buffer. */ if (buffer_id == 0) zwp_linux_buffer_params_v1_send_created (params_resource, buffer->resource); } static void buffer_params_create (struct wl_client *client, struct wl_resource *params_resource, int32_t width, int32_t height, uint32_t format, uint32_t flags) { buffer_params_create_common (client, params_resource, 0, width, height, format, flags); } static void buffer_params_create_immed (struct wl_client *client, struct wl_resource *params_resource, uint32_t buffer_id, int32_t width, int32_t height, uint32_t format, uint32_t flags) { buffer_params_create_common (client, params_resource, buffer_id, width, height, format, flags); } static const struct zwp_linux_buffer_params_v1_interface buffer_params_implementation = { buffer_params_destroy, buffer_params_add, buffer_params_create, buffer_params_create_immed, }; static void dma_buf_handle_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void dma_buf_handle_create_buffer_params (struct wl_client *client, struct wl_resource *dma_buf_resource, uint32_t params_id) { struct wl_resource *params_resource; MetaWaylandDmaBufBuffer *dma_buf; dma_buf = g_object_new (META_TYPE_WAYLAND_DMA_BUF_BUFFER, NULL); params_resource = wl_resource_create (client, &zwp_linux_buffer_params_v1_interface, wl_resource_get_version (dma_buf_resource), params_id); wl_resource_set_implementation (params_resource, &buffer_params_implementation, dma_buf, buffer_params_destructor); } static const struct zwp_linux_dmabuf_v1_interface dma_buf_implementation = { dma_buf_handle_destroy, dma_buf_handle_create_buffer_params, }; static void send_modifiers (struct wl_resource *resource, uint32_t format) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); EGLint num_modifiers; EGLuint64KHR *modifiers; GError *error = NULL; gboolean ret; int i; zwp_linux_dmabuf_v1_send_format (resource, format); /* The modifier event was only added in v3; v1 and v2 only have the format * event. */ if (wl_resource_get_version (resource) < ZWP_LINUX_DMABUF_V1_MODIFIER_SINCE_VERSION) return; /* First query the number of available modifiers, then allocate an array, * then fill the array. */ ret = meta_egl_query_dma_buf_modifiers (egl, egl_display, format, 0, NULL, NULL, &num_modifiers, NULL); if (!ret) return; if (num_modifiers == 0) { zwp_linux_dmabuf_v1_send_modifier (resource, format, DRM_FORMAT_MOD_INVALID >> 32, DRM_FORMAT_MOD_INVALID & 0xffffffff); return; } modifiers = g_new0 (uint64_t, num_modifiers); ret = meta_egl_query_dma_buf_modifiers (egl, egl_display, format, num_modifiers, modifiers, NULL, &num_modifiers, &error); if (!ret) { g_warning ("Failed to query modifiers for format 0x%" PRIu32 ": %s", format, error ? error->message : "unknown error"); g_free (modifiers); return; } for (i = 0; i < num_modifiers; i++) { zwp_linux_dmabuf_v1_send_modifier (resource, format, modifiers[i] >> 32, modifiers[i] & 0xffffffff); } g_free (modifiers); } static void dma_buf_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &zwp_linux_dmabuf_v1_interface, version, id); wl_resource_set_implementation (resource, &dma_buf_implementation, compositor, NULL); send_modifiers (resource, DRM_FORMAT_ARGB8888); send_modifiers (resource, DRM_FORMAT_XRGB8888); send_modifiers (resource, DRM_FORMAT_ARGB2101010); send_modifiers (resource, DRM_FORMAT_RGB565); } /** * meta_wayland_dma_buf_init: * @compositor: The #MetaWaylandCompositor * * Creates the global Wayland object that exposes the linux-dmabuf protocol. * * Returns: Whether the initialization was succesfull. If this is %FALSE, * clients won't be able to use the linux-dmabuf protocol to pass buffers. */ gboolean meta_wayland_dma_buf_init (MetaWaylandCompositor *compositor) { MetaBackend *backend = meta_get_backend (); MetaEgl *egl = meta_backend_get_egl (backend); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); EGLDisplay egl_display = cogl_egl_context_get_egl_display (cogl_context); g_assert (backend && egl && clutter_backend && cogl_context && egl_display); if (!meta_egl_has_extensions (egl, egl_display, NULL, "EGL_EXT_image_dma_buf_import_modifiers", NULL)) return FALSE; if (!wl_global_create (compositor->wayland_display, &zwp_linux_dmabuf_v1_interface, META_ZWP_LINUX_DMABUF_V1_VERSION, compositor, dma_buf_bind)) return FALSE; return TRUE; } static void meta_wayland_dma_buf_buffer_finalize (GObject *object) { MetaWaylandDmaBufBuffer *dma_buf = META_WAYLAND_DMA_BUF_BUFFER (object); int i; for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++) { if (dma_buf->fds[i] != -1) close (dma_buf->fds[i]); } G_OBJECT_CLASS (meta_wayland_dma_buf_buffer_parent_class)->finalize (object); } static void meta_wayland_dma_buf_buffer_init (MetaWaylandDmaBufBuffer *dma_buf) { int i; dma_buf->drm_modifier = DRM_FORMAT_MOD_INVALID; for (i = 0; i < META_WAYLAND_DMA_BUF_MAX_FDS; i++) dma_buf->fds[i] = -1; } static void meta_wayland_dma_buf_buffer_class_init (MetaWaylandDmaBufBufferClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_dma_buf_buffer_finalize; } muffin-6.4.1/src/wayland/meta-wayland-touch.c0000664000175000017500000004575514723361714020057 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #include "backends/native/meta-event-native.h" #include "backends/native/meta-seat-native.h" #endif G_DEFINE_TYPE (MetaWaylandTouch, meta_wayland_touch, META_TYPE_WAYLAND_INPUT_DEVICE) struct _MetaWaylandTouchSurface { MetaWaylandSurface *surface; MetaWaylandTouch *touch; struct wl_listener surface_destroy_listener; struct wl_list resource_list; gint touch_count; }; struct _MetaWaylandTouchInfo { MetaWaylandTouchSurface *touch_surface; guint32 slot_serial; gint32 slot; gfloat start_x; gfloat start_y; gfloat x; gfloat y; guint updated : 1; guint begin_delivered : 1; }; #ifdef HAVE_NATIVE_BACKEND static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void touch_surface_free (gpointer data) { MetaWaylandTouchSurface *touch_surface = data; MetaWaylandTouch *touch = touch_surface->touch; move_resources (&touch->resource_list, &touch_surface->resource_list); wl_list_remove (&touch_surface->surface_destroy_listener.link); g_free (touch_surface); } static MetaWaylandTouchSurface * touch_surface_increment_touch (MetaWaylandTouchSurface *surface) { surface->touch_count++; return surface; } static void touch_surface_decrement_touch (MetaWaylandTouchSurface *touch_surface) { touch_surface->touch_count--; if (touch_surface->touch_count == 0) { /* Now that there are no touches on the surface, free the * MetaWaylandTouchSurface, the memory is actually owned by * the touch_surface->touch_surfaces hashtable, so remove the * item from there. */ MetaWaylandTouch *touch = touch_surface->touch; g_hash_table_remove (touch->touch_surfaces, touch_surface->surface); } } static void touch_handle_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandTouchSurface *touch_surface = wl_container_of (listener, touch_surface, surface_destroy_listener); MetaWaylandSurface *surface = touch_surface->surface; MetaWaylandTouch *touch = touch_surface->touch; MetaWaylandTouchInfo *touch_info; GHashTableIter iter; g_hash_table_iter_init (&iter, touch->touches); /* Destroy all touches on the surface, this indirectly drops touch_count * on the touch_surface to 0, also freeing touch_surface and removing * from the touch_surfaces hashtable. */ while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &touch_info)) { if (touch_info->touch_surface == touch_surface) g_hash_table_iter_remove (&iter); } /* Ensure the surface no longer exists */ g_assert (g_hash_table_remove (touch->touch_surfaces, surface) == FALSE); } static MetaWaylandTouchSurface * touch_surface_get (MetaWaylandTouch *touch, MetaWaylandSurface *surface) { MetaWaylandTouchSurface *touch_surface; touch_surface = g_hash_table_lookup (touch->touch_surfaces, surface); if (touch_surface) return touch_surface_increment_touch (touch_surface); /* Create a new one for this surface */ touch_surface = g_new0 (MetaWaylandTouchSurface, 1); touch_surface->touch = touch; touch_surface->surface = surface; touch_surface->touch_count = 1; touch_surface->surface_destroy_listener.notify = touch_handle_surface_destroy; wl_resource_add_destroy_listener (touch_surface->surface->resource, &touch_surface->surface_destroy_listener); wl_list_init (&touch_surface->resource_list); move_resources_for_client (&touch_surface->resource_list, &touch->resource_list, wl_resource_get_client (touch_surface->surface->resource)); g_hash_table_insert (touch->touch_surfaces, surface, touch_surface); return touch_surface; } static MetaWaylandTouchInfo * touch_get_info (MetaWaylandTouch *touch, ClutterEventSequence *sequence, gboolean create) { MetaWaylandTouchInfo *touch_info; touch_info = g_hash_table_lookup (touch->touches, sequence); if (!touch_info && create) { touch_info = g_new0 (MetaWaylandTouchInfo, 1); touch_info->slot = meta_event_native_sequence_get_slot (sequence); g_hash_table_insert (touch->touches, sequence, touch_info); } return touch_info; } static void touch_get_relative_coordinates (MetaWaylandTouch *touch, MetaWaylandSurface *surface, const ClutterEvent *event, gfloat *x, gfloat *y) { gfloat event_x, event_y; clutter_event_get_coords (event, &event_x, &event_y); return meta_wayland_surface_get_relative_coordinates (surface, event_x, event_y, x, y); } #endif /* HAVE_NATIVE_BACKEND */ void meta_wayland_touch_update (MetaWaylandTouch *touch, const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND MetaWaylandTouchInfo *touch_info; ClutterEventSequence *sequence; sequence = clutter_event_get_event_sequence (event); if (event->type == CLUTTER_TOUCH_BEGIN) { MetaWaylandSurface *surface = NULL; ClutterActor *actor; actor = clutter_event_get_source (event); if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) surface = meta_surface_actor_wayland_get_surface (META_SURFACE_ACTOR_WAYLAND (actor)); if (!surface) return; touch_info = touch_get_info (touch, sequence, TRUE); touch_info->touch_surface = touch_surface_get (touch, surface); clutter_event_get_coords (event, &touch_info->start_x, &touch_info->start_y); } else touch_info = touch_get_info (touch, sequence, FALSE); if (!touch_info) return; if (event->type != CLUTTER_TOUCH_BEGIN && !touch_info->begin_delivered) { g_hash_table_remove (touch->touches, sequence); return; } if (event->type == CLUTTER_TOUCH_BEGIN || event->type == CLUTTER_TOUCH_END) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (touch); touch_info->slot_serial = meta_wayland_input_device_next_serial (input_device); } touch_get_relative_coordinates (touch, touch_info->touch_surface->surface, event, &touch_info->x, &touch_info->y); touch_info->updated = TRUE; #endif /* HAVE_NATIVE_BACKEND */ } static void handle_touch_begin (MetaWaylandTouch *touch, const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND MetaWaylandTouchInfo *touch_info; ClutterEventSequence *sequence; struct wl_resource *resource; struct wl_list *l; sequence = clutter_event_get_event_sequence (event); touch_info = touch_get_info (touch, sequence, FALSE); if (!touch_info) return; l = &touch_info->touch_surface->resource_list; wl_resource_for_each(resource, l) { wl_touch_send_down (resource, touch_info->slot_serial, clutter_event_get_time (event), touch_info->touch_surface->surface->resource, touch_info->slot, wl_fixed_from_double (touch_info->x), wl_fixed_from_double (touch_info->y)); } touch_info->begin_delivered = TRUE; #endif /* HAVE_NATIVE_BACKEND */ } static void handle_touch_update (MetaWaylandTouch *touch, const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND MetaWaylandTouchInfo *touch_info; ClutterEventSequence *sequence; struct wl_resource *resource; struct wl_list *l; sequence = clutter_event_get_event_sequence (event); touch_info = touch_get_info (touch, sequence, FALSE); if (!touch_info) return; l = &touch_info->touch_surface->resource_list; wl_resource_for_each(resource, l) { wl_touch_send_motion (resource, clutter_event_get_time (event), touch_info->slot, wl_fixed_from_double (touch_info->x), wl_fixed_from_double (touch_info->y)); } #endif /* HAVE_NATIVE_BACKEND */ } static void handle_touch_end (MetaWaylandTouch *touch, const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND MetaWaylandTouchInfo *touch_info; ClutterEventSequence *sequence; struct wl_resource *resource; struct wl_list *l; sequence = clutter_event_get_event_sequence (event); touch_info = touch_get_info (touch, sequence, FALSE); if (!touch_info) return; l = &touch_info->touch_surface->resource_list; wl_resource_for_each (resource, l) { wl_touch_send_up (resource, touch_info->slot_serial, clutter_event_get_time (event), touch_info->slot); } g_hash_table_remove (touch->touches, sequence); #endif /* HAVE_NATIVE_BACKEND */ } static GList * touch_get_surfaces (MetaWaylandTouch *touch, gboolean only_updated) { MetaWaylandTouchInfo *touch_info; GList *surfaces = NULL; GHashTableIter iter; g_hash_table_iter_init (&iter, touch->touches); while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &touch_info)) { if (only_updated && !touch_info->updated) continue; if (g_list_find (surfaces, touch_info->touch_surface)) continue; surfaces = g_list_prepend (surfaces, touch_info->touch_surface); touch_info->updated = FALSE; } return g_list_reverse (surfaces); } static void touch_send_frame_event (MetaWaylandTouch *touch) { GList *surfaces, *s; surfaces = touch_get_surfaces (touch, TRUE); for (s = surfaces; s; s = s->next) { MetaWaylandTouchSurface *touch_surface = s->data; struct wl_resource *resource; struct wl_list *l; l = &touch_surface->resource_list; wl_resource_for_each(resource, l) { wl_touch_send_frame (resource); } } g_list_free (surfaces); } static void check_send_frame_event (MetaWaylandTouch *touch, const ClutterEvent *event) { gboolean send_frame_event; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); ClutterEventSequence *sequence; gint32 slot; if (META_IS_BACKEND_NATIVE (backend)) { sequence = clutter_event_get_event_sequence (event); slot = meta_event_native_sequence_get_slot (sequence); touch->frame_slots &= ~(1 << slot); if (touch->frame_slots == 0) send_frame_event = TRUE; else send_frame_event = FALSE; } else #endif /* HAVE_NATIVE_BACKEND */ { send_frame_event = TRUE; } if (send_frame_event) touch_send_frame_event (touch); } gboolean meta_wayland_touch_handle_event (MetaWaylandTouch *touch, const ClutterEvent *event) { switch (event->type) { case CLUTTER_TOUCH_BEGIN: handle_touch_begin (touch, event); break; case CLUTTER_TOUCH_UPDATE: handle_touch_update (touch, event); break; case CLUTTER_TOUCH_END: handle_touch_end (touch, event); break; default: return FALSE; } check_send_frame_event (touch, event); return FALSE; } static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void touch_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_touch_interface touch_interface = { touch_release, }; #ifdef HAVE_NATIVE_BACKEND static void touch_info_free (MetaWaylandTouchInfo *touch_info) { touch_surface_decrement_touch (touch_info->touch_surface); g_free (touch_info); } #endif /* HAVE_NATIVE_BACKEND */ void meta_wayland_touch_cancel (MetaWaylandTouch *touch) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (touch); MetaWaylandSeat *seat = meta_wayland_input_device_get_seat (input_device); GList *surfaces, *s; if (!meta_wayland_seat_has_touch (seat)) return; surfaces = s = touch_get_surfaces (touch, FALSE); for (s = surfaces; s; s = s->next) { MetaWaylandTouchSurface *touch_surface = s->data; struct wl_resource *resource; struct wl_list *l; l = &touch_surface->resource_list; wl_resource_for_each(resource, l) wl_touch_send_cancel (resource); } g_hash_table_remove_all (touch->touches); g_list_free (surfaces); } #ifdef HAVE_NATIVE_BACKEND static gboolean evdev_filter_func (struct libinput_event *event, gpointer data) { MetaWaylandTouch *touch = data; switch (libinput_event_get_type (event)) { case LIBINPUT_EVENT_TOUCH_DOWN: case LIBINPUT_EVENT_TOUCH_UP: case LIBINPUT_EVENT_TOUCH_MOTION: { struct libinput_event_touch *touch_event; int32_t slot; touch_event = libinput_event_get_touch_event (event); slot = libinput_event_touch_get_slot (touch_event); /* XXX: Could theoretically overflow, 64 slots should be * enough for most hw/usecases though. */ touch->frame_slots |= (1 << slot); break; } case LIBINPUT_EVENT_TOUCH_CANCEL: /* Clutter translates this into individual CLUTTER_TOUCH_CANCEL events, * which are not so useful when sending a global signal as the protocol * requires. */ meta_wayland_touch_cancel (touch); break; default: break; } return CLUTTER_EVENT_PROPAGATE; } #endif void meta_wayland_touch_enable (MetaWaylandTouch *touch) { #ifdef HAVE_NATIVE_BACKEND touch->touch_surfaces = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) touch_surface_free); touch->touches = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) touch_info_free); #endif /* HAVE_NATIVE_BACKEND */ wl_list_init (&touch->resource_list); #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); meta_seat_native_add_filter (META_SEAT_NATIVE (seat), evdev_filter_func, touch, NULL); } #endif } void meta_wayland_touch_disable (MetaWaylandTouch *touch) { #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { ClutterBackend *backend = clutter_get_default_backend (); ClutterSeat *seat = clutter_backend_get_default_seat (backend); meta_seat_native_remove_filter (META_SEAT_NATIVE (seat), evdev_filter_func, touch); } #endif meta_wayland_touch_cancel (touch); g_clear_pointer (&touch->touch_surfaces, g_hash_table_unref); g_clear_pointer (&touch->touches, g_hash_table_unref); } void meta_wayland_touch_create_new_resource (MetaWaylandTouch *touch, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); struct wl_resource *cr; if (!meta_wayland_seat_has_touch (seat)) { wl_resource_post_error (seat_resource, WL_DISPLAY_ERROR_INVALID_METHOD, "Cannot retrieve touch interface without touch capability"); return; } cr = wl_resource_create (client, &wl_touch_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (cr, &touch_interface, touch, unbind_resource); wl_list_insert (&touch->resource_list, wl_resource_get_link (cr)); } gboolean meta_wayland_touch_can_popup (MetaWaylandTouch *touch, uint32_t serial) { MetaWaylandTouchInfo *touch_info; GHashTableIter iter; if (!touch->touches) return FALSE; g_hash_table_iter_init (&iter, touch->touches); while (g_hash_table_iter_next (&iter, NULL, (gpointer*) &touch_info)) { if (touch_info->slot_serial == serial) return TRUE; } return FALSE; } ClutterEventSequence * meta_wayland_touch_find_grab_sequence (MetaWaylandTouch *touch, MetaWaylandSurface *surface, uint32_t serial) { MetaWaylandTouchInfo *touch_info; ClutterEventSequence *sequence; GHashTableIter iter; if (!touch->touches) return NULL; g_hash_table_iter_init (&iter, touch->touches); while (g_hash_table_iter_next (&iter, (gpointer*) &sequence, (gpointer*) &touch_info)) { if (touch_info->slot_serial == serial && touch_info->touch_surface->surface == surface) return sequence; } return NULL; } gboolean meta_wayland_touch_get_press_coords (MetaWaylandTouch *touch, ClutterEventSequence *sequence, gfloat *x, gfloat *y) { MetaWaylandTouchInfo *touch_info; if (!touch->touches) return FALSE; touch_info = g_hash_table_lookup (touch->touches, sequence); if (!touch_info) return FALSE; if (x) *x = touch_info->start_x; if (y) *y = touch_info->start_y; return TRUE; } static void meta_wayland_touch_init (MetaWaylandTouch *touch) { } static void meta_wayland_touch_class_init (MetaWaylandTouchClass *klass) { } muffin-6.4.1/src/wayland/meta-wayland-seat.h0000664000175000017500000000622014723361714017656 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2012 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_SEAT_H #define META_WAYLAND_SEAT_H #include #include "clutter/clutter.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-data-device-primary.h" #include "wayland/meta-wayland-data-device-primary-legacy.h" #include "wayland/meta-wayland-input-device.h" #include "wayland/meta-wayland-keyboard.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-tablet-tool.h" #include "wayland/meta-wayland-text-input.h" #include "wayland/meta-wayland-text-input-legacy.h" #include "wayland/meta-wayland-touch.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandSeat { struct wl_list base_resource_list; struct wl_display *wl_display; MetaWaylandPointer *pointer; MetaWaylandKeyboard *keyboard; MetaWaylandTouch *touch; MetaWaylandDataDevice data_device; MetaWaylandDataDevicePrimary primary_data_device; MetaWaylandDataDevicePrimaryLegacy primary_legacy_data_device; MetaWaylandGtkTextInput *gtk_text_input; MetaWaylandTextInput *text_input; guint capabilities; }; void meta_wayland_seat_init (MetaWaylandCompositor *compositor); void meta_wayland_seat_free (MetaWaylandSeat *seat); void meta_wayland_seat_update (MetaWaylandSeat *seat, const ClutterEvent *event); gboolean meta_wayland_seat_handle_event (MetaWaylandSeat *seat, const ClutterEvent *event); void meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat, MetaWaylandSurface *surface); void meta_wayland_seat_repick (MetaWaylandSeat *seat); gboolean meta_wayland_seat_get_grab_info (MetaWaylandSeat *seat, MetaWaylandSurface *surface, uint32_t serial, gboolean require_pressed, gfloat *x, gfloat *y); gboolean meta_wayland_seat_can_popup (MetaWaylandSeat *seat, uint32_t serial); gboolean meta_wayland_seat_has_keyboard (MetaWaylandSeat *seat); gboolean meta_wayland_seat_has_pointer (MetaWaylandSeat *seat); gboolean meta_wayland_seat_has_touch (MetaWaylandSeat *seat); #endif /* META_WAYLAND_SEAT_H */ muffin-6.4.1/src/wayland/meta-wayland-egl-stream.h0000664000175000017500000000407214723361714020765 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_WAYLAND_EGL_STREAM_H #define META_WAYLAND_EGL_STREAM_H #include #include #include "cogl/cogl.h" #include "wayland/meta-wayland-types.h" gboolean meta_wayland_eglstream_controller_init (MetaWaylandCompositor *compositor); #define META_TYPE_WAYLAND_EGL_STREAM (meta_wayland_egl_stream_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandEglStream, meta_wayland_egl_stream, META, WAYLAND_EGL_STREAM, GObject); gboolean meta_wayland_is_egl_stream_buffer (MetaWaylandBuffer *buffer); MetaWaylandEglStream * meta_wayland_egl_stream_new (MetaWaylandBuffer *buffer, GError **error); gboolean meta_wayland_egl_stream_attach (MetaWaylandEglStream *stream, GError **error); CoglTexture2D * meta_wayland_egl_stream_create_texture (MetaWaylandEglStream *stream, GError **error); CoglSnippet * meta_wayland_egl_stream_create_snippet (MetaWaylandEglStream *stream); gboolean meta_wayland_egl_stream_is_y_inverted (MetaWaylandEglStream *stream); #endif /* META_WAYLAND_EGL_STREAM_H */ muffin-6.4.1/src/wayland/meta-wayland-data-offer-primary.c0000664000175000017500000001120214723361714022402 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include "meta-wayland-data-offer-primary.h" #include #include #include #include #include #include #include #include "core/display-private.h" #include "primary-selection-unstable-v1-server-protocol.h" #include "wayland/meta-wayland-data-offer.h" static void transfer_cb (MetaSelection *selection, GAsyncResult *res, GOutputStream *stream) { GError *error = NULL; if (!meta_selection_transfer_finish (selection, res, &error)) { g_warning ("Could not fetch selection data: %s", error->message); g_error_free (error); } g_output_stream_close (stream, NULL, NULL); g_object_unref (stream); } static void primary_offer_receive (struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { MetaDisplay *display = meta_get_display (); GOutputStream *stream; GList *mime_types; gboolean found; mime_types = meta_selection_get_mimetypes (meta_display_get_selection (display), META_SELECTION_PRIMARY); found = g_list_find_custom (mime_types, mime_type, (GCompareFunc) g_strcmp0) != NULL; g_list_free_full (mime_types, g_free); if (!found) { close (fd); return; } stream = g_unix_output_stream_new (fd, TRUE); meta_selection_transfer_async (meta_display_get_selection (display), META_SELECTION_PRIMARY, mime_type, -1, stream, NULL, (GAsyncReadyCallback) transfer_cb, stream); } static void primary_offer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_primary_selection_offer_v1_interface primary_offer_interface = { primary_offer_receive, primary_offer_destroy, }; static void destroy_primary_offer (struct wl_resource *resource) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); if (offer->source) { if (offer == meta_wayland_data_source_get_current_offer (offer->source)) { meta_wayland_data_source_cancel (offer->source); meta_wayland_data_source_set_current_offer (offer->source, NULL); } g_object_remove_weak_pointer (G_OBJECT (offer->source), (gpointer *)&offer->source); offer->source = NULL; } meta_display_sync_wayland_input_focus (meta_get_display ()); g_slice_free (MetaWaylandDataOffer, offer); } MetaWaylandDataOffer * meta_wayland_data_offer_primary_new (struct wl_resource *target) { MetaWaylandDataOffer *offer; offer = g_slice_new0 (MetaWaylandDataOffer); offer->selection_type = META_SELECTION_PRIMARY; offer->resource = wl_resource_create (wl_resource_get_client (target), &zwp_primary_selection_offer_v1_interface, wl_resource_get_version (target), 0); wl_resource_set_implementation (offer->resource, &primary_offer_interface, offer, destroy_primary_offer); return offer; } muffin-6.4.1/src/wayland/meta-wayland-tablet-pad.c0000664000175000017500000004364314723361714020744 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include #include "backends/meta-input-settings-private.h" #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-pad-group.h" #include "wayland/meta-wayland-tablet-pad-ring.h" #include "wayland/meta-wayland-tablet-pad-strip.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-tablet.h" #ifdef HAVE_NATIVE_BACKEND #include #include "backends/native/meta-backend-native.h" #include "backends/native/meta-input-device-native.h" #endif #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void pad_handle_focus_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandTabletPad *pad = wl_container_of (listener, pad, focus_surface_listener); meta_wayland_tablet_pad_set_focus (pad, NULL); } static void group_rings_strips (MetaWaylandTabletPad *pad) { gint n_group, n_elem; GList *g, *l; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); struct libinput_device *libinput_device = NULL; if (META_IS_BACKEND_NATIVE (backend)) libinput_device = meta_input_device_native_get_libinput_device (pad->device); #endif for (n_group = 0, g = pad->groups; g; g = g->next) { MetaWaylandTabletPadGroup *group = g->data; #ifdef HAVE_NATIVE_BACKEND struct libinput_tablet_pad_mode_group *mode_group = NULL; if (libinput_device) mode_group = libinput_device_tablet_pad_get_mode_group (libinput_device, n_group); #endif for (n_elem = 0, l = pad->rings; l; l = l->next) { MetaWaylandTabletPadRing *ring = l->data; #ifdef HAVE_NATIVE_BACKEND if (mode_group) { if (libinput_tablet_pad_mode_group_has_ring (mode_group, n_elem)) meta_wayland_tablet_pad_ring_set_group (ring, group); } else #endif { /* Assign everything to the first group */ if (n_group == 0) meta_wayland_tablet_pad_ring_set_group (ring, group); } n_elem++; } for (n_elem = 0, l = pad->strips; l; l = l->next) { MetaWaylandTabletPadStrip *strip = l->data; #ifdef HAVE_NATIVE_BACKEND if (mode_group) { if (libinput_tablet_pad_mode_group_has_strip (mode_group, n_elem)) meta_wayland_tablet_pad_strip_set_group (strip, group); } else #endif { /* Assign everything to the first group */ if (n_group == 0) meta_wayland_tablet_pad_strip_set_group (strip, group); } n_elem++; } n_group++; } } MetaWaylandTabletPad * meta_wayland_tablet_pad_new (ClutterInputDevice *device, MetaWaylandTabletSeat *tablet_seat) { #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); #endif MetaWaylandTabletPad *pad; guint n_elems, i; pad = g_slice_new0 (MetaWaylandTabletPad); wl_list_init (&pad->resource_list); wl_list_init (&pad->focus_resource_list); pad->focus_surface_listener.notify = pad_handle_focus_surface_destroy; pad->device = device; pad->tablet_seat = tablet_seat; pad->feedback = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_free); #ifdef HAVE_NATIVE_BACKEND /* Buttons, only can be honored this with the native backend */ if (META_IS_BACKEND_NATIVE (backend)) { struct libinput_device *libinput_device; libinput_device = meta_input_device_native_get_libinput_device (device); pad->n_buttons = libinput_device_tablet_pad_get_num_buttons (libinput_device); } #endif n_elems = clutter_input_device_get_n_mode_groups (pad->device); for (i = 0; i < n_elems; i++) { pad->groups = g_list_prepend (pad->groups, meta_wayland_tablet_pad_group_new (pad)); } n_elems = clutter_input_device_get_n_rings (pad->device); for (i = 0; i < n_elems; i++) { MetaWaylandTabletPadRing *ring; ring = meta_wayland_tablet_pad_ring_new (pad); pad->rings = g_list_prepend (pad->rings, ring); } n_elems = clutter_input_device_get_n_strips (pad->device); for (i = 0; i < n_elems; i++) { MetaWaylandTabletPadStrip *strip; strip = meta_wayland_tablet_pad_strip_new (pad); pad->strips = g_list_prepend (pad->strips, strip); } group_rings_strips (pad); return pad; } void meta_wayland_tablet_pad_free (MetaWaylandTabletPad *pad) { struct wl_resource *resource, *next; meta_wayland_tablet_pad_set_focus (pad, NULL); wl_resource_for_each_safe (resource, next, &pad->resource_list) { zwp_tablet_pad_v2_send_removed (resource); wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_list_free_full (pad->groups, (GDestroyNotify) meta_wayland_tablet_pad_group_free); g_list_free_full (pad->rings, (GDestroyNotify) meta_wayland_tablet_pad_ring_free); g_list_free_full (pad->strips, (GDestroyNotify) meta_wayland_tablet_pad_strip_free); g_hash_table_destroy (pad->feedback); g_slice_free (MetaWaylandTabletPad, pad); } static MetaWaylandTabletPadGroup * tablet_pad_lookup_button_group (MetaWaylandTabletPad *pad, guint button) { GList *l; for (l = pad->groups; l; l = l->next) { MetaWaylandTabletPadGroup *group = l->data; if (meta_wayland_tablet_pad_group_has_button (group, button)) return group; } return NULL; } static void tablet_pad_set_feedback (struct wl_client *client, struct wl_resource *resource, uint32_t button, const char *str, uint32_t serial) { MetaWaylandTabletPad *pad = wl_resource_get_user_data (resource); MetaWaylandTabletPadGroup *group = tablet_pad_lookup_button_group (pad, button); MetaInputSettings *input_settings; if (!group || group->mode_switch_serial != serial) return; input_settings = meta_backend_get_input_settings (meta_get_backend ()); if (input_settings && meta_input_settings_is_pad_button_grabbed (input_settings, pad->device, button)) return; if (meta_wayland_tablet_pad_group_is_mode_switch_button (group, button)) return; g_hash_table_insert (pad->feedback, GUINT_TO_POINTER (button), g_strdup (str)); } static void tablet_pad_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_pad_v2_interface pad_interface = { tablet_pad_set_feedback, tablet_pad_destroy, }; void meta_wayland_tablet_pad_notify (MetaWaylandTabletPad *pad, struct wl_resource *resource) { struct wl_client *client = wl_resource_get_client (resource); const gchar *node_path; GList *l; node_path = clutter_input_device_get_device_node (pad->device); if (node_path) zwp_tablet_pad_v2_send_path (resource, node_path); zwp_tablet_pad_v2_send_buttons (resource, pad->n_buttons); for (l = pad->groups; l; l = l->next) { MetaWaylandTabletPadGroup *group = l->data; struct wl_resource *group_resource; group_resource = meta_wayland_tablet_pad_group_create_new_resource (group, client, resource, 0); zwp_tablet_pad_v2_send_group (resource, group_resource); meta_wayland_tablet_pad_group_notify (group, group_resource); } zwp_tablet_pad_v2_send_done (resource); } struct wl_resource * meta_wayland_tablet_pad_create_new_resource (MetaWaylandTabletPad *pad, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_pad_v2_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &pad_interface, pad, unbind_resource); wl_resource_set_user_data (resource, pad); wl_list_insert (&pad->resource_list, wl_resource_get_link (resource)); return resource; } struct wl_resource * meta_wayland_tablet_pad_lookup_resource (MetaWaylandTabletPad *pad, struct wl_client *client) { struct wl_resource *resource; resource = wl_resource_find_for_client (&pad->resource_list, client); if (!resource) resource = wl_resource_find_for_client (&pad->focus_resource_list, client); return resource; } static gboolean handle_pad_button_event (MetaWaylandTabletPad *pad, const ClutterEvent *event) { enum zwp_tablet_pad_v2_button_state button_state; struct wl_list *focus_resources = &pad->focus_resource_list; struct wl_resource *resource; if (wl_list_empty (focus_resources)) return FALSE; if (event->type == CLUTTER_PAD_BUTTON_PRESS) button_state = ZWP_TABLET_PAD_V2_BUTTON_STATE_PRESSED; else if (event->type == CLUTTER_PAD_BUTTON_RELEASE) button_state = ZWP_TABLET_PAD_V2_BUTTON_STATE_RELEASED; else return FALSE; wl_resource_for_each (resource, focus_resources) { zwp_tablet_pad_v2_send_button (resource, clutter_event_get_time (event), event->pad_button.button, button_state); } return TRUE; } static gboolean meta_wayland_tablet_pad_handle_event_action (MetaWaylandTabletPad *pad, const ClutterEvent *event) { MetaInputSettings *input_settings; ClutterInputDevice *device; device = clutter_event_get_source_device (event); input_settings = meta_backend_get_input_settings (meta_get_backend ()); if (input_settings && meta_input_settings_is_pad_button_grabbed (input_settings, device, event->pad_button.button)) return TRUE; return FALSE; } gboolean meta_wayland_tablet_pad_handle_event (MetaWaylandTabletPad *pad, const ClutterEvent *event) { MetaWaylandTabletPadGroup *group; gboolean handled = FALSE; guint n_group; n_group = clutter_event_get_mode_group (event); group = g_list_nth_data (pad->groups, n_group); switch (clutter_event_type (event)) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: if (group) handled |= meta_wayland_tablet_pad_group_handle_event (group, event); handled |= meta_wayland_tablet_pad_handle_event_action (pad, event); if (handled) return TRUE; return handle_pad_button_event (pad, event); case CLUTTER_PAD_RING: case CLUTTER_PAD_STRIP: if (group) return meta_wayland_tablet_pad_group_handle_event (group, event); default: return FALSE; } } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void meta_wayland_tablet_pad_update_groups_focus (MetaWaylandTabletPad *pad) { GList *l; for (l = pad->groups; l; l = l->next) meta_wayland_tablet_pad_group_sync_focus (l->data); } static void meta_wayland_tablet_pad_broadcast_enter (MetaWaylandTabletPad *pad, uint32_t serial, MetaWaylandTablet *tablet, MetaWaylandSurface *surface) { struct wl_resource *resource, *tablet_resource; struct wl_client *client; client = wl_resource_get_client (pad->focus_surface->resource); tablet_resource = meta_wayland_tablet_lookup_resource (tablet, client); wl_resource_for_each (resource, &pad->focus_resource_list) { zwp_tablet_pad_v2_send_enter (resource, serial, tablet_resource, surface->resource); } } static void meta_wayland_tablet_pad_broadcast_leave (MetaWaylandTabletPad *pad, uint32_t serial, MetaWaylandSurface *surface) { struct wl_resource *resource; wl_resource_for_each (resource, &pad->focus_resource_list) { zwp_tablet_pad_v2_send_leave (resource, serial, surface->resource); } } void meta_wayland_tablet_pad_set_focus (MetaWaylandTabletPad *pad, MetaWaylandSurface *surface) { MetaWaylandTablet *tablet; if (pad->focus_surface == surface) return; g_hash_table_remove_all (pad->feedback); if (pad->focus_surface != NULL) { struct wl_client *client = wl_resource_get_client (pad->focus_surface->resource); struct wl_list *focus_resources = &pad->focus_resource_list; if (!wl_list_empty (focus_resources)) { struct wl_display *display = wl_client_get_display (client); uint32_t serial = wl_display_next_serial (display); meta_wayland_tablet_pad_broadcast_leave (pad, serial, pad->focus_surface); move_resources (&pad->resource_list, &pad->focus_resource_list); } wl_list_remove (&pad->focus_surface_listener.link); pad->focus_surface = NULL; } tablet = meta_wayland_tablet_seat_lookup_paired_tablet (pad->tablet_seat, pad); if (tablet != NULL && surface != NULL) { struct wl_client *client; pad->focus_surface = surface; wl_resource_add_destroy_listener (pad->focus_surface->resource, &pad->focus_surface_listener); client = wl_resource_get_client (pad->focus_surface->resource); move_resources_for_client (&pad->focus_resource_list, &pad->resource_list, client); if (!wl_list_empty (&pad->focus_resource_list)) { struct wl_display *display = wl_client_get_display (client); pad->focus_serial = wl_display_next_serial (display); meta_wayland_tablet_pad_broadcast_enter (pad, pad->focus_serial, tablet, pad->focus_surface); } } meta_wayland_tablet_pad_update_groups_focus (pad); } void meta_wayland_tablet_pad_update (MetaWaylandTabletPad *pad, const ClutterEvent *event) { MetaWaylandTabletPadGroup *group; guint n_group; n_group = clutter_event_get_mode_group (event); group = g_list_nth_data (pad->groups, n_group); if (group) meta_wayland_tablet_pad_group_update (group, event); } static gchar * meta_wayland_tablet_pad_label_mode_switch_button (MetaWaylandTabletPad *pad, guint button) { MetaWaylandTabletPadGroup *group; GList *l; for (l = pad->groups; l; l = l->next) { group = l->data; if (meta_wayland_tablet_pad_group_is_mode_switch_button (group, button)) return g_strdup_printf (_("Mode Switch: Mode %d"), group->current_mode + 1); } return NULL; } gchar * meta_wayland_tablet_pad_get_label (MetaWaylandTabletPad *pad, MetaPadActionType type, guint action) { const gchar *label = NULL; gchar *mode_label; switch (type) { case META_PAD_ACTION_BUTTON: mode_label = meta_wayland_tablet_pad_label_mode_switch_button (pad, action); if (mode_label) return mode_label; label = g_hash_table_lookup (pad->feedback, GUINT_TO_POINTER (action)); break; case META_PAD_ACTION_RING: { MetaWaylandTabletPadRing *ring; ring = g_list_nth_data (pad->rings, action); if (ring) label = ring->feedback; break; } case META_PAD_ACTION_STRIP: { MetaWaylandTabletPadStrip *strip; strip = g_list_nth_data (pad->strips, action); if (strip) label = strip->feedback; break; } } return g_strdup (label); } muffin-6.4.1/src/wayland/meta-wayland-tablet-tool.h0000664000175000017500000000722514723361714021156 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_TOOL_H #define META_WAYLAND_TABLET_TOOL_H #include #include #include "backends/meta-cursor-renderer.h" #include "backends/meta-cursor-sprite-xcursor.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletTool { MetaWaylandTabletSeat *seat; ClutterInputDevice *device; ClutterInputDeviceTool *device_tool; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandSurface *focus_surface; struct wl_listener focus_surface_destroy_listener; MetaWaylandSurface *cursor_surface; struct wl_listener cursor_surface_destroy_listener; MetaCursorRenderer *cursor_renderer; MetaCursorSpriteXcursor *default_sprite; gulong prepare_at_signal_id; MetaWaylandSurface *current; guint32 pressed_buttons; guint32 button_count; guint32 proximity_serial; guint32 down_serial; guint32 button_serial; float grab_x, grab_y; MetaWaylandTablet *current_tablet; }; MetaWaylandTabletTool * meta_wayland_tablet_tool_new (MetaWaylandTabletSeat *seat, ClutterInputDevice *device, ClutterInputDeviceTool *device_tool); void meta_wayland_tablet_tool_free (MetaWaylandTabletTool *tool); struct wl_resource * meta_wayland_tablet_tool_create_new_resource (MetaWaylandTabletTool *tool, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); struct wl_resource * meta_wayland_tablet_tool_lookup_resource (MetaWaylandTabletTool *tool, struct wl_client *client); void meta_wayland_tablet_tool_update (MetaWaylandTabletTool *tool, const ClutterEvent *event); gboolean meta_wayland_tablet_tool_handle_event (MetaWaylandTabletTool *tool, const ClutterEvent *event); void meta_wayland_tablet_tool_set_cursor_position (MetaWaylandTabletTool *tool, float new_x, float new_y); gboolean meta_wayland_tablet_tool_can_grab_surface (MetaWaylandTabletTool *tool, MetaWaylandSurface *surface, uint32_t serial); gboolean meta_wayland_tablet_tool_can_popup (MetaWaylandTabletTool *tool, uint32_t serial); #endif /* META_WAYLAND_TABLET_TOOL_H */ muffin-6.4.1/src/wayland/meta-wayland-pointer-constraints.c0000664000175000017500000011441014723361714022743 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "wayland/meta-wayland-pointer-constraints.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-pointer-constraint.h" #include "core/frame.h" #include "core/window-private.h" #include "meta/meta-backend.h" #include "wayland/meta-pointer-confinement-wayland.h" #include "wayland/meta-pointer-lock-wayland.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-region.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-xwayland.h" #include "pointer-constraints-unstable-v1-server-protocol.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #endif static GQuark quark_pending_constraint_state = 0; static GQuark quark_surface_pointer_constraints_data = 0; struct _MetaWaylandPointerConstraint { GObject parent; MetaWaylandSurface *surface; gboolean is_enabled; cairo_region_t *region; struct wl_resource *resource; MetaWaylandPointerGrab grab; MetaWaylandSeat *seat; enum zwp_pointer_constraints_v1_lifetime lifetime; gulong pointer_focus_surface_handler_id; gboolean hint_set; wl_fixed_t x_hint; wl_fixed_t y_hint; MetaPointerConstraint *constraint; }; typedef struct _MetaWaylandSurfacePointerConstraintsData { MetaWaylandSurface *surface; GList *pointer_constraints; MetaWindow *window; gulong window_associated_handler_id; gulong appears_changed_handler_id; gulong raised_handler_id; } MetaWaylandSurfacePointerConstraintsData; typedef struct { MetaWaylandPointerConstraint *constraint; cairo_region_t *region; gulong applied_handler_id; } MetaWaylandPendingConstraintState; typedef struct { GList *pending_constraint_states; } MetaWaylandPendingConstraintStateContainer; G_DEFINE_TYPE (MetaWaylandPointerConstraint, meta_wayland_pointer_constraint, G_TYPE_OBJECT); static const struct zwp_locked_pointer_v1_interface locked_pointer_interface; static const struct zwp_confined_pointer_v1_interface confined_pointer_interface; static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface; static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface; static void meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint); static void meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint); static void meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window); static void meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat, MetaWindow *window); static MetaWaylandSurfacePointerConstraintsData * get_surface_constraints_data (MetaWaylandSurface *surface) { return g_object_get_qdata (G_OBJECT (surface), quark_surface_pointer_constraints_data); } static void appears_focused_changed (MetaWindow *window, GParamSpec *pspec, gpointer user_data) { MetaWaylandCompositor *wayland_compositor; wayland_compositor = meta_wayland_compositor_get_default (); meta_wayland_pointer_constraint_maybe_remove_for_seat (wayland_compositor->seat, window); meta_wayland_pointer_constraint_maybe_enable_for_window (window); } static void window_raised (MetaWindow *window) { meta_wayland_pointer_constraint_maybe_enable_for_window (window); } static void connect_window (MetaWaylandSurfacePointerConstraintsData *data, MetaWindow *window) { data->window = window; g_object_add_weak_pointer (G_OBJECT (data->window), (gpointer *) &data->window); data->appears_changed_handler_id = g_signal_connect (data->window, "notify::appears-focused", G_CALLBACK (appears_focused_changed), NULL); data->raised_handler_id = g_signal_connect (data->window, "raised", G_CALLBACK (window_raised), NULL); } static void window_associated (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfacePointerConstraintsData *data) { MetaWaylandSurface *surface = data->surface; MetaWindow *window; window = meta_wayland_surface_get_window (surface); connect_window (data, window); g_clear_signal_handler (&data->window_associated_handler_id, surface); meta_wayland_pointer_constraint_maybe_enable_for_window (window); } static MetaWaylandSurfacePointerConstraintsData * surface_constraint_data_new (MetaWaylandSurface *surface) { MetaWaylandSurfacePointerConstraintsData *data; MetaWindow *window; data = g_new0 (MetaWaylandSurfacePointerConstraintsData, 1); data->surface = surface; window = meta_wayland_surface_get_window (surface); if (window) { connect_window (data, window); } else if (meta_xwayland_is_xwayland_surface (surface)) { data->window_associated_handler_id = g_signal_connect (surface->role, "window-associated", G_CALLBACK (window_associated), data); } else { /* TODO: Support constraints on non-toplevel windows, such as subsurfaces. */ g_warn_if_reached (); } return data; } static void surface_constraint_data_free (MetaWaylandSurfacePointerConstraintsData *data) { if (data->window) { g_clear_signal_handler (&data->appears_changed_handler_id, data->window); g_clear_signal_handler (&data->raised_handler_id, data->window); g_object_remove_weak_pointer (G_OBJECT (data->window), (gpointer *) &data->window); } else { g_clear_signal_handler (&data->window_associated_handler_id, data->surface->role); } g_list_free_full (data->pointer_constraints, (GDestroyNotify) meta_wayland_pointer_constraint_destroy); g_free (data); } static void constrained_surface_destroyed (MetaWaylandSurface *surface, MetaWaylandSurfacePointerConstraintsData *data) { surface_constraint_data_free (data); } static MetaWaylandSurfacePointerConstraintsData * ensure_surface_constraints_data (MetaWaylandSurface *surface) { MetaWaylandSurfacePointerConstraintsData *data; data = get_surface_constraints_data (surface); if (!data) { data = surface_constraint_data_new (surface); g_object_set_qdata (G_OBJECT (surface), quark_surface_pointer_constraints_data, data); g_signal_connect (surface, "destroy", G_CALLBACK (constrained_surface_destroyed), data); } return data; } static void surface_add_pointer_constraint (MetaWaylandSurface *surface, MetaWaylandPointerConstraint *constraint) { MetaWaylandSurfacePointerConstraintsData *data; data = ensure_surface_constraints_data (surface); data->pointer_constraints = g_list_append (data->pointer_constraints, constraint); } static void surface_remove_pointer_constraints (MetaWaylandSurface *surface, MetaWaylandPointerConstraint *constraint) { MetaWaylandSurfacePointerConstraintsData *data; data = get_surface_constraints_data (surface); data->pointer_constraints = g_list_remove (data->pointer_constraints, constraint); if (!data->pointer_constraints) { g_object_set_qdata (G_OBJECT (surface), quark_surface_pointer_constraints_data, NULL); } } static void pointer_focus_surface_changed (MetaWaylandPointer *pointer, MetaWaylandPointerConstraint *constraint) { MetaWindow *window; window = meta_wayland_surface_get_window (constraint->surface); if (window) { MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); meta_wayland_pointer_constraint_maybe_remove_for_seat (seat, window); } meta_wayland_pointer_constraint_maybe_enable (constraint); } static MetaWaylandPointerConstraint * meta_wayland_pointer_constraint_new (MetaWaylandSurface *surface, MetaWaylandSeat *seat, MetaWaylandRegion *region, enum zwp_pointer_constraints_v1_lifetime lifetime, struct wl_resource *resource, const MetaWaylandPointerGrabInterface *grab_interface) { MetaWaylandPointerConstraint *constraint; constraint = g_object_new (META_TYPE_WAYLAND_POINTER_CONSTRAINT, NULL); if (!constraint) return NULL; constraint->surface = surface; constraint->seat = seat; constraint->lifetime = lifetime; constraint->resource = resource; constraint->grab.interface = grab_interface; if (region) { constraint->region = cairo_region_copy (meta_wayland_region_peek_cairo_region (region)); } else { constraint->region = NULL; } constraint->pointer_focus_surface_handler_id = g_signal_connect (seat->pointer, "focus-surface-changed", G_CALLBACK (pointer_focus_surface_changed), constraint); return constraint; } static gboolean meta_wayland_pointer_constraint_is_enabled (MetaWaylandPointerConstraint *constraint) { return constraint->is_enabled; } static void meta_wayland_pointer_constraint_notify_activated (MetaWaylandPointerConstraint *constraint) { struct wl_resource *resource = constraint->resource; if (wl_resource_instance_of (resource, &zwp_locked_pointer_v1_interface, &locked_pointer_interface)) { zwp_locked_pointer_v1_send_locked (resource); } else if (wl_resource_instance_of (resource, &zwp_confined_pointer_v1_interface, &confined_pointer_interface)) { zwp_confined_pointer_v1_send_confined (resource); } } static void meta_wayland_pointer_constraint_notify_deactivated (MetaWaylandPointerConstraint *constraint) { struct wl_resource *resource = constraint->resource; if (wl_resource_instance_of (resource, &zwp_locked_pointer_v1_interface, &locked_pointer_interface)) zwp_locked_pointer_v1_send_unlocked (resource); else if (wl_resource_instance_of (resource, &zwp_confined_pointer_v1_interface, &confined_pointer_interface)) zwp_confined_pointer_v1_send_unconfined (resource); } static MetaPointerConstraint * meta_wayland_pointer_constraint_create_pointer_constraint (MetaWaylandPointerConstraint *constraint) { struct wl_resource *resource = constraint->resource; if (wl_resource_instance_of (resource, &zwp_locked_pointer_v1_interface, &locked_pointer_interface)) { return meta_pointer_lock_wayland_new (); } else if (wl_resource_instance_of (resource, &zwp_confined_pointer_v1_interface, &confined_pointer_interface)) { return meta_pointer_confinement_wayland_new (constraint); } g_assert_not_reached (); return NULL; } static void meta_wayland_pointer_constraint_enable (MetaWaylandPointerConstraint *constraint) { MetaBackend *backend = meta_get_backend (); g_assert (!constraint->is_enabled); constraint->is_enabled = TRUE; meta_wayland_pointer_constraint_notify_activated (constraint); meta_wayland_pointer_start_grab (constraint->seat->pointer, &constraint->grab); constraint->constraint = meta_wayland_pointer_constraint_create_pointer_constraint (constraint); meta_backend_set_client_pointer_constraint (backend, constraint->constraint); g_object_add_weak_pointer (G_OBJECT (constraint->constraint), (gpointer *) &constraint->constraint); g_object_unref (constraint->constraint); } static void meta_wayland_pointer_constraint_disable (MetaWaylandPointerConstraint *constraint) { constraint->is_enabled = FALSE; meta_wayland_pointer_constraint_notify_deactivated (constraint); meta_backend_set_client_pointer_constraint (meta_get_backend (), NULL); meta_wayland_pointer_end_grab (constraint->grab.pointer); } void meta_wayland_pointer_constraint_destroy (MetaWaylandPointerConstraint *constraint) { if (meta_wayland_pointer_constraint_is_enabled (constraint)) meta_wayland_pointer_constraint_disable (constraint); wl_resource_set_user_data (constraint->resource, NULL); g_clear_pointer (&constraint->region, cairo_region_destroy); g_object_unref (constraint); } static gboolean is_within_constraint_region (MetaWaylandPointerConstraint *constraint, wl_fixed_t sx, wl_fixed_t sy) { cairo_region_t *region; gboolean is_within; region = meta_wayland_pointer_constraint_calculate_effective_region (constraint); is_within = cairo_region_contains_point (region, wl_fixed_to_int (sx), wl_fixed_to_int (sy)); cairo_region_destroy (region); return is_within; } static gboolean should_constraint_be_enabled (MetaWaylandPointerConstraint *constraint) { MetaWindow *window; window = meta_wayland_surface_get_window (constraint->surface); if (!window) { /* * Locks from Xwayland may come before we have had the opportunity to * associate the X11 Window with the wl_surface. */ g_warn_if_fail (meta_xwayland_is_xwayland_surface (constraint->surface)); return FALSE; } if (window->unmanaging) return FALSE; if (constraint->seat->pointer->focus_surface != constraint->surface) return FALSE; if (meta_xwayland_is_xwayland_surface (constraint->surface)) { MetaDisplay *display = meta_get_display (); /* * We need to handle Xwayland surfaces differently in order to allow * Xwayland to be able to lock the pointer. For example, we cannot require * the locked window to "appear focused" because the surface Xwayland * locks might not be able to appear focused (for example it may be a * override redirect window). * * Since we don't have any way to know what focused window an override * redirect is associated with, nor have a way to know if the override * redirect window even shares the same connection as a focused window, * we simply can only really restrict it to enable the lock if any * Xwayland window appears focused. */ if (display->focus_window && display->focus_window->client_type != META_WINDOW_CLIENT_TYPE_X11) return FALSE; } else { if (!meta_window_appears_focused (window)) return FALSE; } return TRUE; } static void meta_wayland_pointer_constraint_maybe_enable (MetaWaylandPointerConstraint *constraint) { wl_fixed_t sx, sy; if (constraint->is_enabled) return; if (!should_constraint_be_enabled (constraint)) return; meta_wayland_pointer_get_relative_coordinates (constraint->seat->pointer, constraint->surface, &sx, &sy); if (!is_within_constraint_region (constraint, sx, sy)) return; meta_wayland_pointer_constraint_enable (constraint); } static void meta_wayland_pointer_constraint_remove (MetaWaylandPointerConstraint *constraint) { MetaWaylandSurface *surface = constraint->surface; surface_remove_pointer_constraints (surface, constraint); meta_wayland_pointer_constraint_destroy (constraint); } static void meta_wayland_pointer_constraint_deactivate (MetaWaylandPointerConstraint *constraint) { switch (constraint->lifetime) { case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: meta_wayland_pointer_constraint_remove (constraint); break; case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT: meta_wayland_pointer_constraint_disable (constraint); break; default: g_assert_not_reached (); } } void meta_wayland_pointer_constraint_maybe_remove_for_seat (MetaWaylandSeat *seat, MetaWindow *window) { MetaWaylandPointer *pointer = seat->pointer; MetaWaylandPointerConstraint *constraint; if ((pointer->grab->interface != &confined_pointer_grab_interface && pointer->grab->interface != &locked_pointer_grab_interface)) return; constraint = wl_container_of (pointer->grab, constraint, grab); if (should_constraint_be_enabled (constraint)) return; meta_wayland_pointer_constraint_deactivate (constraint); } static void meta_wayland_pointer_constraint_maybe_enable_for_window (MetaWindow *window) { MetaWaylandSurface *surface = window->surface; MetaWaylandSurfacePointerConstraintsData *surface_data; GList *l; if (!surface) { g_warn_if_fail (window->client_type == META_WINDOW_CLIENT_TYPE_X11); return; } surface_data = get_surface_constraints_data (surface); if (!surface_data) return; for (l = surface_data->pointer_constraints; l; l = l->next) { MetaWaylandPointerConstraint *constraint = l->data; meta_wayland_pointer_constraint_maybe_enable (constraint); } } MetaWaylandSeat * meta_wayland_pointer_constraint_get_seat (MetaWaylandPointerConstraint *constraint) { return constraint->seat; } cairo_region_t * meta_wayland_pointer_constraint_calculate_effective_region (MetaWaylandPointerConstraint *constraint) { cairo_region_t *region; MetaWindow *window; region = meta_wayland_surface_calculate_input_region (constraint->surface); if (constraint->region) cairo_region_intersect (region, constraint->region); window = meta_wayland_surface_get_window (constraint->surface); if (window && window->frame) { MetaFrame *frame = window->frame; int actual_width, actual_height; g_assert (meta_xwayland_is_xwayland_surface (constraint->surface)); actual_width = window->buffer_rect.width - (frame->child_x + frame->right_width); actual_height = window->buffer_rect.height - (frame->child_y + frame->bottom_height); if (actual_width > 0 && actual_height > 0) { cairo_region_intersect_rectangle (region, &(cairo_rectangle_int_t) { .x = frame->child_x, .y = frame->child_y, .width = actual_width, .height = actual_height }); } } return region; } MetaWaylandSurface * meta_wayland_pointer_constraint_get_surface (MetaWaylandPointerConstraint *constraint) { return constraint->surface; } static void pointer_constraint_resource_destroyed (struct wl_resource *resource) { MetaWaylandPointerConstraint *constraint = wl_resource_get_user_data (resource); if (!constraint) return; meta_wayland_pointer_constraint_remove (constraint); } static void pending_constraint_state_free (MetaWaylandPendingConstraintState *constraint_pending) { g_clear_pointer (&constraint_pending->region, cairo_region_destroy); if (constraint_pending->constraint) g_object_remove_weak_pointer (G_OBJECT (constraint_pending->constraint), (gpointer *) &constraint_pending->constraint); } static MetaWaylandPendingConstraintStateContainer * get_pending_constraint_state_container (MetaWaylandSurfaceState *pending) { return g_object_get_qdata (G_OBJECT (pending), quark_pending_constraint_state); } static MetaWaylandPendingConstraintState * get_pending_constraint_state (MetaWaylandPointerConstraint *constraint) { MetaWaylandSurfaceState *pending; MetaWaylandPendingConstraintStateContainer *container; GList *l; pending = meta_wayland_surface_get_pending_state (constraint->surface); container = get_pending_constraint_state_container (pending); for (l = container->pending_constraint_states; l; l = l->next) { MetaWaylandPendingConstraintState *constraint_pending = l->data; if (constraint_pending->constraint == constraint) return constraint_pending; } return NULL; } static void pending_constraint_state_container_free (MetaWaylandPendingConstraintStateContainer *container) { g_list_free_full (container->pending_constraint_states, (GDestroyNotify) pending_constraint_state_free); g_free (container); } static MetaWaylandPendingConstraintStateContainer * ensure_pending_constraint_state_container (MetaWaylandSurfaceState *pending) { MetaWaylandPendingConstraintStateContainer *container; container = get_pending_constraint_state_container (pending); if (!container) { container = g_new0 (MetaWaylandPendingConstraintStateContainer, 1); g_object_set_qdata_full (G_OBJECT (pending), quark_pending_constraint_state, container, (GDestroyNotify) pending_constraint_state_container_free); } return container; } static void remove_pending_constraint_state (MetaWaylandPointerConstraint *constraint, MetaWaylandSurfaceState *pending) { MetaWaylandPendingConstraintStateContainer *container; GList *l; container = get_pending_constraint_state_container (pending); for (l = container->pending_constraint_states; l; l = l->next) { MetaWaylandPendingConstraintState *constraint_pending = l->data; if (constraint_pending->constraint != constraint) continue; pending_constraint_state_free (l->data); container->pending_constraint_states = g_list_remove_link (container->pending_constraint_states, l); break; } } static void pending_constraint_state_applied (MetaWaylandSurfaceState *pending, MetaWaylandPendingConstraintState *constraint_pending) { MetaWaylandPointerConstraint *constraint = constraint_pending->constraint; if (!constraint) return; g_clear_pointer (&constraint->region, cairo_region_destroy); if (constraint_pending->region) { constraint->region = constraint_pending->region; constraint_pending->region = NULL; } else { constraint->region = NULL; } g_clear_signal_handler (&constraint_pending->applied_handler_id, pending); remove_pending_constraint_state (constraint, pending); /* The pointer is potentially warped by the actor paint signal callback if * the new region proved it necessary. */ } static MetaWaylandPendingConstraintState * ensure_pending_constraint_state (MetaWaylandPointerConstraint *constraint) { MetaWaylandSurfaceState *pending; MetaWaylandPendingConstraintStateContainer *container; MetaWaylandPendingConstraintState *constraint_pending; pending = meta_wayland_surface_get_pending_state (constraint->surface); container = ensure_pending_constraint_state_container (pending); constraint_pending = get_pending_constraint_state (constraint); if (!constraint_pending) { constraint_pending = g_new0 (MetaWaylandPendingConstraintState, 1); constraint_pending->constraint = constraint; constraint_pending->applied_handler_id = g_signal_connect (pending, "applied", G_CALLBACK (pending_constraint_state_applied), constraint_pending); g_object_add_weak_pointer (G_OBJECT (constraint), (gpointer *) &constraint_pending->constraint); container->pending_constraint_states = g_list_append (container->pending_constraint_states, constraint_pending); } return constraint_pending; } static void meta_wayland_pointer_constraint_set_pending_region (MetaWaylandPointerConstraint *constraint, MetaWaylandRegion *region) { MetaWaylandPendingConstraintState *constraint_pending; constraint_pending = ensure_pending_constraint_state (constraint); g_clear_pointer (&constraint_pending->region, cairo_region_destroy); if (region) { constraint_pending->region = cairo_region_copy (meta_wayland_region_peek_cairo_region (region)); } } static MetaWaylandPointerConstraint * get_pointer_constraint_for_seat (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { MetaWaylandSurfacePointerConstraintsData *surface_data; GList *l; surface_data = get_surface_constraints_data (surface); if (!surface_data) return NULL; for (l = surface_data->pointer_constraints; l; l = l->next) { MetaWaylandPointerConstraint *constraint = l->data; if (seat == constraint->seat) return constraint; } return NULL; } static void init_pointer_constraint (struct wl_resource *resource, uint32_t id, MetaWaylandSurface *surface, MetaWaylandSeat *seat, MetaWaylandRegion *region, enum zwp_pointer_constraints_v1_lifetime lifetime, const struct wl_interface *interface, const void *implementation, const MetaWaylandPointerGrabInterface *grab_interface) { struct wl_client *client = wl_resource_get_client (resource); struct wl_resource *cr; MetaWaylandPointerConstraint *constraint; if (get_pointer_constraint_for_seat (surface, seat)) { wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "the pointer was already requested to be " "locked or confined on that surface"); return; } cr = wl_resource_create (client, interface, wl_resource_get_version (resource), id); if (cr == NULL) { wl_client_post_no_memory (client); return; } switch (lifetime) { case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT: case ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT: break; default: wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "Invalid constraint lifetime"); return; } constraint = meta_wayland_pointer_constraint_new (surface, seat, region, lifetime, cr, grab_interface); if (constraint == NULL) { wl_client_post_no_memory (client); return; } surface_add_pointer_constraint (surface, constraint); wl_resource_set_implementation (cr, implementation, constraint, pointer_constraint_resource_destroyed); meta_wayland_pointer_constraint_maybe_enable (constraint); } static void locked_pointer_destroy (struct wl_client *client, struct wl_resource *resource) { MetaWaylandPointerConstraint *constraint = wl_resource_get_user_data (resource); gboolean warp_pointer = FALSE; int warp_x, warp_y; if (constraint && constraint->is_enabled && constraint->hint_set && is_within_constraint_region (constraint, constraint->x_hint, constraint->y_hint)) { float sx, sy; float x, y; sx = (float)wl_fixed_to_double (constraint->x_hint); sy = (float)wl_fixed_to_double (constraint->y_hint); meta_wayland_surface_get_absolute_coordinates (constraint->surface, sx, sy, &x, &y); warp_pointer = TRUE; warp_x = (int) x; warp_y = (int) y; } wl_resource_destroy (resource); if (warp_pointer) { ClutterSeat *seat; seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); clutter_seat_warp_pointer (seat, warp_x, warp_y); } } static void locked_pointer_set_cursor_position_hint (struct wl_client *client, struct wl_resource *resource, wl_fixed_t surface_x, wl_fixed_t surface_y) { MetaWaylandPointerConstraint *constraint = wl_resource_get_user_data (resource); /* Ignore a set cursor hint that was already sent after the constraint * was cancelled. */ if (!constraint || !constraint->resource || constraint->resource != resource) return; constraint->hint_set = TRUE; constraint->x_hint = surface_x; constraint->y_hint = surface_y; } static void locked_pointer_set_region (struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { MetaWaylandPointerConstraint *constraint = wl_resource_get_user_data (resource); MetaWaylandRegion *region = region_resource ? wl_resource_get_user_data (region_resource) : NULL; if (!constraint) return; meta_wayland_pointer_constraint_set_pending_region (constraint, region); } static const struct zwp_locked_pointer_v1_interface locked_pointer_interface = { locked_pointer_destroy, locked_pointer_set_cursor_position_hint, locked_pointer_set_region, }; static void locked_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { } static void locked_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { meta_wayland_pointer_send_relative_motion (grab->pointer, event); meta_wayland_pointer_broadcast_frame (grab->pointer); } static void locked_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { meta_wayland_pointer_send_button (grab->pointer, event); } static void locked_pointer_grab_pointer_cancel (MetaWaylandPointerGrab *grab) { MetaWaylandPointerConstraint *constraint = wl_container_of (grab, constraint, grab); meta_wayland_pointer_constraint_deactivate (constraint); } static const MetaWaylandPointerGrabInterface locked_pointer_grab_interface = { locked_pointer_grab_pointer_focus, locked_pointer_grab_pointer_motion, locked_pointer_grab_pointer_button, locked_pointer_grab_pointer_cancel, }; static void pointer_constraints_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void pointer_constraints_lock_pointer (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *pointer_resource, struct wl_resource *region_resource, uint32_t lifetime) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); MetaWaylandRegion *region = region_resource ? wl_resource_get_user_data (region_resource) : NULL; init_pointer_constraint (resource, id, surface, seat, region, lifetime, &zwp_locked_pointer_v1_interface, &locked_pointer_interface, &locked_pointer_grab_interface); } static void confined_pointer_grab_pointer_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { } static void confined_pointer_grab_pointer_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointerConstraint *constraint = wl_container_of (grab, constraint, grab); MetaWaylandPointer *pointer = grab->pointer; g_assert (pointer->focus_surface); g_assert (pointer->focus_surface == constraint->surface); meta_wayland_pointer_send_motion (pointer, event); } static void confined_pointer_grab_pointer_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { meta_wayland_pointer_send_button (grab->pointer, event); } static void confined_pointer_grab_pointer_cancel (MetaWaylandPointerGrab *grab) { MetaWaylandPointerConstraint *constraint = wl_container_of (grab, constraint, grab); meta_wayland_pointer_constraint_deactivate (constraint); } static const MetaWaylandPointerGrabInterface confined_pointer_grab_interface = { confined_pointer_grab_pointer_focus, confined_pointer_grab_pointer_motion, confined_pointer_grab_pointer_button, confined_pointer_grab_pointer_cancel, }; static void confined_pointer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void confined_pointer_set_region (struct wl_client *client, struct wl_resource *resource, struct wl_resource *region_resource) { MetaWaylandPointerConstraint *constraint = wl_resource_get_user_data (resource); MetaWaylandRegion *region = region_resource ? wl_resource_get_user_data (region_resource) : NULL; if (!constraint) return; meta_wayland_pointer_constraint_set_pending_region (constraint, region); } static const struct zwp_confined_pointer_v1_interface confined_pointer_interface = { confined_pointer_destroy, confined_pointer_set_region, }; static void pointer_constraints_confine_pointer (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *pointer_resource, struct wl_resource *region_resource, uint32_t lifetime) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); MetaWaylandRegion *region = region_resource ? wl_resource_get_user_data (region_resource) : NULL; init_pointer_constraint (resource, id, surface, seat, region, lifetime, &zwp_confined_pointer_v1_interface, &confined_pointer_interface, &confined_pointer_grab_interface); } static const struct zwp_pointer_constraints_v1_interface pointer_constraints = { pointer_constraints_destroy, pointer_constraints_lock_pointer, pointer_constraints_confine_pointer, }; static void bind_pointer_constraints (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &zwp_pointer_constraints_v1_interface, 1, id); wl_resource_set_implementation (resource, &pointer_constraints, compositor, NULL); } static void meta_wayland_pointer_constraint_finalize (GObject *object) { MetaWaylandPointerConstraint *constraint = META_WAYLAND_POINTER_CONSTRAINT (object); g_clear_signal_handler (&constraint->pointer_focus_surface_handler_id, constraint->seat->pointer); G_OBJECT_CLASS (meta_wayland_pointer_constraint_parent_class)->finalize (object); } void meta_wayland_pointer_constraints_init (MetaWaylandCompositor *compositor) { if (!wl_global_create (compositor->wayland_display, &zwp_pointer_constraints_v1_interface, 1, compositor, bind_pointer_constraints)) g_error ("Could not create wp_pointer_constraints global"); } static void meta_wayland_pointer_constraint_init (MetaWaylandPointerConstraint *constraint) { } static void meta_wayland_pointer_constraint_class_init (MetaWaylandPointerConstraintClass *klass) { GObjectClass *object_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_pointer_constraint_finalize; quark_pending_constraint_state = g_quark_from_static_string ("-meta-wayland-pointer-constraint-pending_state"); quark_surface_pointer_constraints_data = g_quark_from_static_string ("-meta-wayland-surface-constraints-data"); } muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-group.c0000664000175000017500000002746114723361714022076 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-tablet-pad-group.h" #include "wayland/meta-wayland-tablet-pad-ring.h" #include "wayland/meta-wayland-tablet-pad-strip.h" #include "wayland/meta-wayland-tablet-pad.h" #include "wayland/meta-wayland-tablet-seat.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include "backends/native/meta-event-native.h" #include "backends/native/meta-input-device-native.h" #endif #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } MetaWaylandTabletPadGroup * meta_wayland_tablet_pad_group_new (MetaWaylandTabletPad *pad) { MetaWaylandTabletPadGroup *group; group = g_slice_new0 (MetaWaylandTabletPadGroup); wl_list_init (&group->resource_list); wl_list_init (&group->focus_resource_list); group->pad = pad; return group; } void meta_wayland_tablet_pad_group_free (MetaWaylandTabletPadGroup *group) { struct wl_resource *resource, *next; wl_resource_for_each_safe (resource, next, &group->resource_list) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_list_free (group->rings); g_list_free (group->strips); g_slice_free (MetaWaylandTabletPadGroup, group); } static void tablet_pad_group_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_pad_group_v2_interface group_interface = { tablet_pad_group_destroy }; struct wl_resource * meta_wayland_tablet_pad_group_create_new_resource (MetaWaylandTabletPadGroup *group, struct wl_client *client, struct wl_resource *pad_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_pad_group_v2_interface, wl_resource_get_version (pad_resource), id); wl_resource_set_implementation (resource, &group_interface, group, unbind_resource); wl_resource_set_user_data (resource, group); wl_list_insert (&group->resource_list, wl_resource_get_link (resource)); return resource; } struct wl_resource * meta_wayland_tablet_pad_group_lookup_resource (MetaWaylandTabletPadGroup *group, struct wl_client *client) { struct wl_resource *resource; resource = wl_resource_find_for_client (&group->resource_list, client); if (!resource) resource = wl_resource_find_for_client (&group->focus_resource_list, client); return resource; } gboolean meta_wayland_tablet_pad_group_has_button (MetaWaylandTabletPadGroup *group, guint button) { #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) { struct libinput_device *libinput_device; struct libinput_tablet_pad_mode_group *mode_group; guint n_group; libinput_device = meta_input_device_native_get_libinput_device (group->pad->device); n_group = g_list_index (group->pad->groups, group); mode_group = libinput_device_tablet_pad_get_mode_group (libinput_device, n_group); return libinput_tablet_pad_mode_group_has_button (mode_group, button); } else #endif { return g_list_length (group->pad->groups) == 1; } } static void meta_wayland_tablet_pad_group_send_buttons (MetaWaylandTabletPadGroup *group, struct wl_resource *resource) { struct wl_array buttons; guint i; wl_array_init (&buttons); for (i = 0; i < group->pad->n_buttons; i++) { uint32_t *pos; if (!meta_wayland_tablet_pad_group_has_button (group, i)) continue; pos = wl_array_add (&buttons, sizeof (*pos)); *pos = i; } zwp_tablet_pad_group_v2_send_buttons (resource, &buttons); wl_array_release (&buttons); } void meta_wayland_tablet_pad_group_notify (MetaWaylandTabletPadGroup *group, struct wl_resource *resource) { struct wl_client *client = wl_resource_get_client (resource); struct wl_array buttons; guint n_group, n_modes; GList *l; wl_array_init (&buttons); /* Buttons */ meta_wayland_tablet_pad_group_send_buttons (group, resource); /* Rings */ for (l = group->rings; l; l = l->next) { MetaWaylandTabletPadRing *ring = l->data; struct wl_resource *ring_resource; ring_resource = meta_wayland_tablet_pad_ring_create_new_resource (ring, client, resource, 0); zwp_tablet_pad_group_v2_send_ring (resource, ring_resource); } /* Strips */ for (l = group->strips; l; l = l->next) { MetaWaylandTabletPadStrip *strip = l->data; struct wl_resource *strip_resource; strip_resource = meta_wayland_tablet_pad_strip_create_new_resource (strip, client, resource, 0); zwp_tablet_pad_group_v2_send_strip (resource, strip_resource); } n_group = g_list_index (group->pad->groups, group); n_modes = clutter_input_device_get_group_n_modes (group->pad->device, n_group); zwp_tablet_pad_group_v2_send_modes (resource, n_modes); zwp_tablet_pad_group_v2_send_done (resource); } void meta_wayland_tablet_pad_group_update (MetaWaylandTabletPadGroup *group, const ClutterEvent *event) { switch (event->type) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: if (meta_wayland_tablet_pad_group_is_mode_switch_button (group, event->pad_button.button)) group->current_mode = event->pad_button.mode; break; default: break; } } static gboolean handle_pad_ring_event (MetaWaylandTabletPadGroup *group, const ClutterEvent *event) { MetaWaylandTabletPadRing *ring; if (event->type != CLUTTER_PAD_RING) return FALSE; ring = g_list_nth_data (group->rings, event->pad_ring.ring_number); if (!ring) return FALSE; return meta_wayland_tablet_pad_ring_handle_event (ring, event); } static gboolean handle_pad_strip_event (MetaWaylandTabletPadGroup *group, const ClutterEvent *event) { MetaWaylandTabletPadStrip *strip; if (event->type != CLUTTER_PAD_STRIP) return FALSE; strip = g_list_nth_data (group->strips, event->pad_strip.strip_number); if (!strip) return FALSE; return meta_wayland_tablet_pad_strip_handle_event (strip, event); } static void broadcast_group_mode (MetaWaylandTabletPadGroup *group, uint32_t time) { struct wl_display *display = group->pad->tablet_seat->seat->wl_display; struct wl_resource *resource; group->mode_switch_serial = wl_display_next_serial (display); wl_resource_for_each (resource, &group->focus_resource_list) { zwp_tablet_pad_group_v2_send_mode_switch (resource, time, group->mode_switch_serial, group->current_mode); } } static void broadcast_group_buttons (MetaWaylandTabletPadGroup *group) { struct wl_resource *resource; wl_resource_for_each (resource, &group->focus_resource_list) { meta_wayland_tablet_pad_group_send_buttons (group, resource); } } gboolean meta_wayland_tablet_pad_group_handle_event (MetaWaylandTabletPadGroup *group, const ClutterEvent *event) { switch (clutter_event_type (event)) { case CLUTTER_PAD_BUTTON_PRESS: case CLUTTER_PAD_BUTTON_RELEASE: if (meta_wayland_tablet_pad_group_is_mode_switch_button (group, event->pad_button.button)) { if (event->type == CLUTTER_PAD_BUTTON_PRESS) broadcast_group_mode (group, clutter_event_get_time (event)); return TRUE; } else { return FALSE; } break; case CLUTTER_PAD_RING: return handle_pad_ring_event (group, event); case CLUTTER_PAD_STRIP: return handle_pad_strip_event (group, event); default: return FALSE; } } static void meta_wayland_tablet_pad_group_update_rings_focus (MetaWaylandTabletPadGroup *group) { GList *l; for (l = group->rings; l; l = l->next) meta_wayland_tablet_pad_ring_sync_focus (l->data); } static void meta_wayland_tablet_pad_group_update_strips_focus (MetaWaylandTabletPadGroup *group) { GList *l; for (l = group->strips; l; l = l->next) meta_wayland_tablet_pad_strip_sync_focus (l->data); } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } void meta_wayland_tablet_pad_group_sync_focus (MetaWaylandTabletPadGroup *group) { if (!wl_list_empty (&group->focus_resource_list)) { move_resources (&group->resource_list, &group->focus_resource_list); } if (group->pad->focus_surface != NULL) { move_resources_for_client (&group->focus_resource_list, &group->resource_list, wl_resource_get_client (group->pad->focus_surface->resource)); } meta_wayland_tablet_pad_group_update_rings_focus (group); meta_wayland_tablet_pad_group_update_strips_focus (group); if (!wl_list_empty (&group->focus_resource_list)) { broadcast_group_mode (group, clutter_get_current_event_time ()); broadcast_group_buttons (group); } } gboolean meta_wayland_tablet_pad_group_is_mode_switch_button (MetaWaylandTabletPadGroup *group, guint button) { gint n_group = g_list_index (group->pad->groups, group); g_assert (n_group >= 0); return clutter_input_device_is_mode_switch_button (group->pad->device, n_group, button); } muffin-6.4.1/src/wayland/meta-wayland-text-input.h0000664000175000017500000000333314723361714021045 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TEXT_INPUT_H #define META_WAYLAND_TEXT_INPUT_H #include #include "meta/window.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_TEXT_INPUT (meta_wayland_text_input_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandTextInput, meta_wayland_text_input, META, WAYLAND_TEXT_INPUT, GObject); MetaWaylandTextInput * meta_wayland_text_input_new (MetaWaylandSeat *seat); void meta_wayland_text_input_destroy (MetaWaylandTextInput *text_input); gboolean meta_wayland_text_input_init (MetaWaylandCompositor *compositor); void meta_wayland_text_input_set_focus (MetaWaylandTextInput *text_input, MetaWaylandSurface *surface); gboolean meta_wayland_text_input_handle_event (MetaWaylandTextInput *text_input, const ClutterEvent *event); #endif /* META_WAYLAND_TEXT_INPUT_H */ muffin-6.4.1/src/wayland/meta-xwayland-surface.c0000664000175000017500000002233114723361714020536 0ustar fabiofabio/* * Copyright (C) 2013 Intel Corporation * Copyright (C) 2013-2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-xwayland-surface.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-private.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-xwayland-private.h" enum { WINDOW_ASSOCIATED, N_SIGNALS }; static guint signals[N_SIGNALS]; struct _MetaXwaylandSurface { MetaWaylandActorSurface parent; MetaWindow *window; gulong unmanaging_handler_id; gulong position_changed_handler_id; gulong effects_completed_handler_id; }; G_DEFINE_TYPE (MetaXwaylandSurface, meta_xwayland_surface, META_TYPE_WAYLAND_ACTOR_SURFACE) static void clear_window (MetaXwaylandSurface *xwayland_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xwayland_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindowActor *window_actor; MetaSurfaceActor *surface_actor; if (!xwayland_surface->window) return; g_clear_signal_handler (&xwayland_surface->unmanaging_handler_id, xwayland_surface->window); g_clear_signal_handler (&xwayland_surface->position_changed_handler_id, xwayland_surface->window); window_actor = meta_window_actor_from_window (xwayland_surface->window); g_clear_signal_handler (&xwayland_surface->effects_completed_handler_id, window_actor); xwayland_surface->window->surface = NULL; xwayland_surface->window = NULL; surface_actor = meta_wayland_surface_get_actor (surface); if (surface_actor) clutter_actor_set_reactive (CLUTTER_ACTOR (surface_actor), FALSE); meta_wayland_surface_notify_unmapped (surface); } static void window_unmanaging (MetaWindow *window, MetaXwaylandSurface *xwayland_surface) { clear_window (xwayland_surface); } static void window_position_changed (MetaWindow *window, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } static void window_actor_effects_completed (MetaWindowActor *window_actor, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); meta_wayland_compositor_repick (surface->compositor); } void meta_xwayland_surface_associate_with_window (MetaXwaylandSurface *xwayland_surface, MetaWindow *window) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (xwayland_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActor *surface_actor; MetaWindowActor *window_actor; /* * If the window has an existing surface, like if we're undecorating or * decorating the window, then we need to detach the window from its old * surface. */ if (window->surface) { MetaXwaylandSurface *other_xwayland_surface; other_xwayland_surface = META_XWAYLAND_SURFACE (window->surface->role); clear_window (other_xwayland_surface); } window->surface = surface; xwayland_surface->window = window; surface_actor = meta_wayland_surface_get_actor (surface); if (surface_actor) clutter_actor_set_reactive (CLUTTER_ACTOR (surface_actor), TRUE); xwayland_surface->unmanaging_handler_id = g_signal_connect (window, "unmanaging", G_CALLBACK (window_unmanaging), xwayland_surface); xwayland_surface->position_changed_handler_id = g_signal_connect (window, "position-changed", G_CALLBACK (window_position_changed), surface); xwayland_surface->effects_completed_handler_id = g_signal_connect (meta_window_actor_from_window (window), "effects-completed", G_CALLBACK (window_actor_effects_completed), surface); g_signal_emit (xwayland_surface, signals[WINDOW_ASSOCIATED], 0); window_actor = meta_window_actor_from_window (window); if (window_actor) meta_window_actor_assign_surface_actor (window_actor, surface_actor); } static void meta_xwayland_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_xwayland_surface_parent_class); surface->dnd.funcs = meta_xwayland_selection_get_drag_dest_funcs (); surface_role_class->assigned (surface_role); } static void meta_xwayland_surface_pre_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (surface_role); if (pending->newly_attached && surface->buffer_ref.buffer && xwayland_surface->window) meta_window_queue (xwayland_surface->window, META_QUEUE_CALC_SHOWING); } static void meta_xwayland_surface_get_relative_coordinates (MetaWaylandSurfaceRole *surface_role, float abs_x, float abs_y, float *out_sx, float *out_sy) { MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (surface_role); MetaRectangle window_rect = { 0 }; if (xwayland_surface->window) meta_window_get_buffer_rect (xwayland_surface->window, &window_rect); *out_sx = abs_x - window_rect.x; *out_sy = abs_y - window_rect.y; } static MetaWaylandSurface * meta_xwayland_surface_get_toplevel (MetaWaylandSurfaceRole *surface_role) { return meta_wayland_surface_role_get_surface (surface_role); } static MetaWindow * meta_xwayland_surface_get_window (MetaWaylandSurfaceRole *surface_role) { MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (surface_role); return xwayland_surface->window; } static double meta_xwayland_surface_get_geometry_scale (MetaWaylandActorSurface *actor_surface) { return 1; } static void meta_xwayland_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (actor_surface); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (meta_xwayland_surface_parent_class); if (xwayland_surface->window) { MetaWindowActor *window_actor = meta_window_actor_from_window (xwayland_surface->window); actor_surface_class->sync_actor_state (actor_surface); meta_window_actor_update_regions (window_actor); } } static void meta_xwayland_surface_finalize (GObject *object) { MetaXwaylandSurface *xwayland_surface = META_XWAYLAND_SURFACE (object); GObjectClass *parent_object_class = G_OBJECT_CLASS (meta_xwayland_surface_parent_class); clear_window (xwayland_surface); parent_object_class->finalize (object); } static void meta_xwayland_surface_init (MetaXwaylandSurface *xwayland_surface) { } static void meta_xwayland_surface_class_init (MetaXwaylandSurfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (klass); object_class->finalize = meta_xwayland_surface_finalize; surface_role_class->assigned = meta_xwayland_surface_assigned; surface_role_class->pre_apply_state = meta_xwayland_surface_pre_apply_state; surface_role_class->get_relative_coordinates = meta_xwayland_surface_get_relative_coordinates; surface_role_class->get_toplevel = meta_xwayland_surface_get_toplevel; surface_role_class->get_window = meta_xwayland_surface_get_window; actor_surface_class->get_geometry_scale = meta_xwayland_surface_get_geometry_scale; actor_surface_class->sync_actor_state = meta_xwayland_surface_sync_actor_state; signals[WINDOW_ASSOCIATED] = g_signal_new ("window-associated", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } muffin-6.4.1/src/wayland/meta-wayland-popup.h0000664000175000017500000000436314723361714020073 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * Copyright (C) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ #ifndef META_WAYLAND_POPUP_H #define META_WAYLAND_POPUP_H #include #include #include "wayland/meta-wayland-types.h" #include "wayland/meta-wayland-pointer.h" #define META_TYPE_WAYLAND_POPUP_SURFACE (meta_wayland_popup_surface_get_type ()) G_DECLARE_INTERFACE (MetaWaylandPopupSurface, meta_wayland_popup_surface, META, WAYLAND_POPUP_SURFACE, GObject); struct _MetaWaylandPopupSurfaceInterface { GTypeInterface parent_iface; void (*done) (MetaWaylandPopupSurface *popup_surface); void (*dismiss) (MetaWaylandPopupSurface *popup_surface); MetaWaylandSurface *(*get_surface) (MetaWaylandPopupSurface *popup_surface); }; MetaWaylandPopupGrab *meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, MetaWaylandPopupSurface *popup_surface); void meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab); MetaWaylandSurface *meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab); gboolean meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab); MetaWaylandPopup *meta_wayland_popup_create (MetaWaylandPopupSurface *surface, MetaWaylandPopupGrab *grab); void meta_wayland_popup_destroy (MetaWaylandPopup *popup); void meta_wayland_popup_dismiss (MetaWaylandPopup *popup); MetaWaylandSurface *meta_wayland_popup_get_top_popup (MetaWaylandPopup *popup); #endif /* META_WAYLAND_POPUP_H */ muffin-6.4.1/src/wayland/meta-wayland-viewporter.h0000664000175000017500000000201714723361714021130 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2018-2019 Robert Mader * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_WAYLAND_VIEWPORTER_H #define META_WAYLAND_VIEWPORTER_H #include "wayland/meta-wayland-types.h" void meta_wayland_init_viewporter (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_VIEWPORTER_H */ muffin-6.4.1/src/wayland/meta-pointer-confinement-wayland.h0000664000175000017500000000305014723361714022703 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_POINTER_CONFINEMENT_WAYLAND_H #define META_POINTER_CONFINEMENT_WAYLAND_H #include #include "backends/meta-pointer-constraint.h" #include "wayland/meta-wayland-pointer-constraints.h" G_BEGIN_DECLS #define META_TYPE_POINTER_CONFINEMENT_WAYLAND (meta_pointer_confinement_wayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland, META, POINTER_CONFINEMENT_WAYLAND, MetaPointerConstraint); MetaPointerConstraint *meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint); G_END_DECLS #endif /* META_CONFINEMENT_WAYLAND_H */ muffin-6.4.1/src/wayland/meta-wayland-pointer-gesture-swipe.c0000664000175000017500000001241514723361714023201 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-pointer-gesture-swipe.h" #include #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "pointer-gestures-unstable-v1-server-protocol.h" static void handle_swipe_begin (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; MetaWaylandSeat *seat; struct wl_resource *resource; uint32_t serial, fingers; pointer_client = pointer->focus_client; seat = meta_wayland_pointer_get_seat (pointer); serial = wl_display_next_serial (seat->wl_display); fingers = clutter_event_get_touchpad_gesture_finger_count (event); wl_resource_for_each (resource, &pointer_client->swipe_gesture_resources) { zwp_pointer_gesture_swipe_v1_send_begin (resource, serial, clutter_event_get_time (event), pointer->focus_surface->resource, fingers); } } static void handle_swipe_update (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; struct wl_resource *resource; gdouble dx, dy; pointer_client = pointer->focus_client; clutter_event_get_gesture_motion_delta (event, &dx, &dy); wl_resource_for_each (resource, &pointer_client->swipe_gesture_resources) { zwp_pointer_gesture_swipe_v1_send_update (resource, clutter_event_get_time (event), wl_fixed_from_double (dx), wl_fixed_from_double (dy)); } } static void handle_swipe_end (MetaWaylandPointer *pointer, const ClutterEvent *event) { MetaWaylandPointerClient *pointer_client; MetaWaylandSeat *seat; struct wl_resource *resource; gboolean cancelled = FALSE; uint32_t serial; pointer_client = pointer->focus_client; seat = meta_wayland_pointer_get_seat (pointer); serial = wl_display_next_serial (seat->wl_display); if (event->touchpad_swipe.phase == CLUTTER_TOUCHPAD_GESTURE_PHASE_CANCEL) cancelled = TRUE; wl_resource_for_each (resource, &pointer_client->swipe_gesture_resources) { zwp_pointer_gesture_swipe_v1_send_end (resource, serial, clutter_event_get_time (event), cancelled); } } gboolean meta_wayland_pointer_gesture_swipe_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { if (event->type != CLUTTER_TOUCHPAD_SWIPE) return FALSE; if (!pointer->focus_client) return FALSE; switch (event->touchpad_swipe.phase) { case CLUTTER_TOUCHPAD_GESTURE_PHASE_BEGIN: handle_swipe_begin (pointer, event); break; case CLUTTER_TOUCHPAD_GESTURE_PHASE_UPDATE: handle_swipe_update (pointer, event); break; case CLUTTER_TOUCHPAD_GESTURE_PHASE_END: handle_swipe_end (pointer, event); break; default: return FALSE; } return TRUE; } static void pointer_gesture_swipe_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_pointer_gesture_swipe_v1_interface pointer_gesture_swipe_interface = { pointer_gesture_swipe_release }; void meta_wayland_pointer_gesture_swipe_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *pointer_resource, uint32_t id) { MetaWaylandPointerClient *pointer_client; struct wl_resource *res; pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); g_return_if_fail (pointer_client != NULL); res = wl_resource_create (client, &zwp_pointer_gesture_swipe_v1_interface, wl_resource_get_version (pointer_resource), id); wl_resource_set_implementation (res, &pointer_gesture_swipe_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); wl_list_insert (&pointer_client->swipe_gesture_resources, wl_resource_get_link (res)); } muffin-6.4.1/src/wayland/meta-wayland-data-offer-primary-legacy.c0000664000175000017500000001120214723361714023644 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include "meta-wayland-data-offer-primary-legacy.h" #include #include #include #include #include #include #include #include "core/display-private.h" #include "gtk-primary-selection-server-protocol.h" #include "wayland/meta-wayland-data-offer.h" static void transfer_cb (MetaSelection *selection, GAsyncResult *res, GOutputStream *stream) { GError *error = NULL; if (!meta_selection_transfer_finish (selection, res, &error)) { g_warning ("Could not fetch selection data: %s", error->message); g_error_free (error); } g_output_stream_close (stream, NULL, NULL); g_object_unref (stream); } static void primary_offer_receive (struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { MetaDisplay *display = meta_get_display (); GOutputStream *stream; GList *mime_types; gboolean found; mime_types = meta_selection_get_mimetypes (meta_display_get_selection (display), META_SELECTION_PRIMARY); found = g_list_find_custom (mime_types, mime_type, (GCompareFunc) g_strcmp0) != NULL; g_list_free_full (mime_types, g_free); if (!found) { close (fd); return; } stream = g_unix_output_stream_new (fd, TRUE); meta_selection_transfer_async (meta_display_get_selection (display), META_SELECTION_PRIMARY, mime_type, -1, stream, NULL, (GAsyncReadyCallback) transfer_cb, stream); } static void primary_offer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct gtk_primary_selection_offer_interface primary_offer_interface = { primary_offer_receive, primary_offer_destroy, }; static void destroy_primary_offer (struct wl_resource *resource) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); if (offer->source) { if (offer == meta_wayland_data_source_get_current_offer (offer->source)) { meta_wayland_data_source_cancel (offer->source); meta_wayland_data_source_set_current_offer (offer->source, NULL); } g_object_remove_weak_pointer (G_OBJECT (offer->source), (gpointer *)&offer->source); offer->source = NULL; } meta_display_sync_wayland_input_focus (meta_get_display ()); g_slice_free (MetaWaylandDataOffer, offer); } MetaWaylandDataOffer * meta_wayland_data_offer_primary_legacy_new (struct wl_resource *target) { MetaWaylandDataOffer *offer; offer = g_slice_new0 (MetaWaylandDataOffer); offer->selection_type = META_SELECTION_PRIMARY; offer->resource = wl_resource_create (wl_resource_get_client (target), >k_primary_selection_offer_interface, wl_resource_get_version (target), 0); wl_resource_set_implementation (offer->resource, &primary_offer_interface, offer, destroy_primary_offer); return offer; } muffin-6.4.1/src/wayland/meta-wayland-pointer.c0000664000175000017500000012670314723361714020406 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * Copyright © 2008 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* The file is based on src/input.c from Weston */ #include "config.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-cursor-renderer.h" #include "backends/meta-cursor-tracker-private.h" #include "backends/meta-cursor.h" #include "clutter/clutter.h" #include "cogl/cogl-wayland-server.h" #include "cogl/cogl.h" #include "compositor/meta-surface-actor-wayland.h" #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-cursor-surface.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-xwayland.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include "backends/native/meta-event-native.h" #endif #include "relative-pointer-unstable-v1-server-protocol.h" #define DEFAULT_AXIS_STEP_DISTANCE wl_fixed_from_int (10) enum { FOCUS_SURFACE_CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandPointer, meta_wayland_pointer, META_TYPE_WAYLAND_INPUT_DEVICE) static void meta_wayland_pointer_set_current (MetaWaylandPointer *pointer, MetaWaylandSurface *surface); static void meta_wayland_pointer_reset_grab (MetaWaylandPointer *pointer); static void meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer); static MetaWaylandPointerClient * meta_wayland_pointer_client_new (void) { MetaWaylandPointerClient *pointer_client; pointer_client = g_slice_new0 (MetaWaylandPointerClient); wl_list_init (&pointer_client->pointer_resources); wl_list_init (&pointer_client->swipe_gesture_resources); wl_list_init (&pointer_client->pinch_gesture_resources); wl_list_init (&pointer_client->relative_pointer_resources); return pointer_client; } static void meta_wayland_pointer_client_free (MetaWaylandPointerClient *pointer_client) { struct wl_resource *resource, *next; /* Since we make every wl_pointer resource defunct when we stop advertising * the pointer capability on the wl_seat, we need to make sure all the * resources in the pointer client instance gets removed. */ wl_resource_for_each_safe (resource, next, &pointer_client->pointer_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } wl_resource_for_each_safe (resource, next, &pointer_client->swipe_gesture_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } wl_resource_for_each_safe (resource, next, &pointer_client->pinch_gesture_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } wl_resource_for_each_safe (resource, next, &pointer_client->relative_pointer_resources) { wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_slice_free (MetaWaylandPointerClient, pointer_client); } static gboolean meta_wayland_pointer_client_is_empty (MetaWaylandPointerClient *pointer_client) { return (wl_list_empty (&pointer_client->pointer_resources) && wl_list_empty (&pointer_client->swipe_gesture_resources) && wl_list_empty (&pointer_client->pinch_gesture_resources) && wl_list_empty (&pointer_client->relative_pointer_resources)); } MetaWaylandPointerClient * meta_wayland_pointer_get_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client) { if (!pointer->pointer_clients) return NULL; return g_hash_table_lookup (pointer->pointer_clients, client); } static MetaWaylandPointerClient * meta_wayland_pointer_ensure_pointer_client (MetaWaylandPointer *pointer, struct wl_client *client) { MetaWaylandPointerClient *pointer_client; pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (pointer_client) return pointer_client; pointer_client = meta_wayland_pointer_client_new (); g_hash_table_insert (pointer->pointer_clients, client, pointer_client); if (!pointer->focus_client && pointer->focus_surface && wl_resource_get_client (pointer->focus_surface->resource) == client) pointer->focus_client = pointer_client; return pointer_client; } static void meta_wayland_pointer_cleanup_pointer_client (MetaWaylandPointer *pointer, MetaWaylandPointerClient *pointer_client, struct wl_client *client) { if (meta_wayland_pointer_client_is_empty (pointer_client)) { if (pointer->focus_client == pointer_client) pointer->focus_client = NULL; g_hash_table_remove (pointer->pointer_clients, client); } } void meta_wayland_pointer_unbind_pointer_client_resource (struct wl_resource *resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (resource); MetaWaylandPointerClient *pointer_client; struct wl_client *client = wl_resource_get_client (resource); wl_list_remove (wl_resource_get_link (resource)); pointer_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (!pointer_client) { /* This happens if all pointer devices were unplugged and no new resources * were created by the client. * * If this is a resource that was previously made defunct, pointer_client * be non-NULL but it is harmless since the below cleanup call will be * prevented from removing the pointer client because of valid resources. */ return; } meta_wayland_pointer_cleanup_pointer_client (pointer, pointer_client, client); } static void sync_focus_surface (MetaWaylandPointer *pointer) { MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); if (!meta_cursor_tracker_get_pointer_visible (cursor_tracker) && !clutter_seat_is_unfocus_inhibited (clutter_seat)) { meta_wayland_pointer_set_focus (pointer, NULL); return; } switch (display->event_route) { case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_FRAME_BUTTON: /* The compositor has a grab, so remove our focus... */ meta_wayland_pointer_set_focus (pointer, NULL); break; case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: { const MetaWaylandPointerGrabInterface *interface = pointer->grab->interface; interface->focus (pointer->grab, pointer->current); } break; default: g_assert_not_reached (); } } static void meta_wayland_pointer_send_frame (MetaWaylandPointer *pointer, struct wl_resource *resource) { if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) wl_pointer_send_frame (resource); } void meta_wayland_pointer_broadcast_frame (MetaWaylandPointer *pointer) { struct wl_resource *resource; if (!pointer->focus_client) return; wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { meta_wayland_pointer_send_frame (pointer, resource); } } void meta_wayland_pointer_send_relative_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { #ifdef HAVE_NATIVE_BACKEND struct wl_resource *resource; double dx, dy; double dx_unaccel, dy_unaccel; uint64_t time_us; uint32_t time_us_hi; uint32_t time_us_lo; wl_fixed_t dxf, dyf; wl_fixed_t dx_unaccelf, dy_unaccelf; MetaBackend *backend = meta_get_backend (); if (!pointer->focus_client) return; if (!META_IS_BACKEND_NATIVE (backend) || !meta_event_native_get_relative_motion (event, &dx, &dy, &dx_unaccel, &dy_unaccel)) return; time_us = meta_event_native_get_time_usec (event); if (time_us == 0) time_us = clutter_event_get_time (event) * 1000ULL; time_us_hi = (uint32_t) (time_us >> 32); time_us_lo = (uint32_t) time_us; dxf = wl_fixed_from_double (dx); dyf = wl_fixed_from_double (dy); dx_unaccelf = wl_fixed_from_double (dx_unaccel); dy_unaccelf = wl_fixed_from_double (dy_unaccel); wl_resource_for_each (resource, &pointer->focus_client->relative_pointer_resources) { zwp_relative_pointer_v1_send_relative_motion (resource, time_us_hi, time_us_lo, dxf, dyf, dx_unaccelf, dy_unaccelf); } #endif } void meta_wayland_pointer_send_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; uint32_t time; float sx, sy; if (!pointer->focus_client) return; time = clutter_event_get_time (event); meta_wayland_surface_get_relative_coordinates (pointer->focus_surface, event->motion.x, event->motion.y, &sx, &sy); wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { wl_pointer_send_motion (resource, time, wl_fixed_from_double (sx), wl_fixed_from_double (sy)); } meta_wayland_pointer_send_relative_motion (pointer, event); meta_wayland_pointer_broadcast_frame (pointer); } void meta_wayland_pointer_send_button (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; ClutterEventType event_type; event_type = clutter_event_type (event); if (pointer->focus_client && !wl_list_empty (&pointer->focus_client->pointer_resources)) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); uint32_t time; uint32_t button; uint32_t serial; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); if (META_IS_BACKEND_NATIVE (backend)) button = meta_event_native_get_event_code (event); else #endif { button = clutter_event_get_button (event); switch (button) { case 1: button = BTN_LEFT; break; /* The evdev input right and middle button numbers are swapped relative to how Clutter numbers them */ case 2: button = BTN_MIDDLE; break; case 3: button = BTN_RIGHT; break; default: button = button + (BTN_LEFT - 1) + 4; break; } } time = clutter_event_get_time (event); serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { wl_pointer_send_button (resource, serial, time, button, event_type == CLUTTER_BUTTON_PRESS ? 1 : 0); } meta_wayland_pointer_broadcast_frame (pointer); } if (pointer->button_count == 0 && event_type == CLUTTER_BUTTON_RELEASE) sync_focus_surface (pointer); } static void default_grab_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { MetaWaylandPointer *pointer = grab->pointer; MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); MetaDisplay *display = meta_get_display (); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); if (!meta_wayland_seat_has_pointer (seat)) return; if (!meta_cursor_tracker_get_pointer_visible (cursor_tracker) && !clutter_seat_is_unfocus_inhibited (clutter_seat)) return; if (pointer->button_count > 0) return; switch (display->event_route) { case META_EVENT_ROUTE_WINDOW_OP: case META_EVENT_ROUTE_COMPOSITOR_GRAB: case META_EVENT_ROUTE_FRAME_BUTTON: return; break; case META_EVENT_ROUTE_NORMAL: case META_EVENT_ROUTE_WAYLAND_POPUP: break; } meta_wayland_pointer_set_focus (pointer, surface); } static void default_grab_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointer *pointer = grab->pointer; meta_wayland_pointer_send_motion (pointer, event); } static void default_grab_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointer *pointer = grab->pointer; meta_wayland_pointer_send_button (pointer, event); } static const MetaWaylandPointerGrabInterface default_pointer_grab_interface = { default_grab_focus, default_grab_motion, default_grab_button }; static void meta_wayland_pointer_on_cursor_changed (MetaCursorTracker *cursor_tracker, MetaWaylandPointer *pointer) { if (pointer->cursor_surface) meta_wayland_surface_update_outputs (pointer->cursor_surface); } void meta_wayland_pointer_enable (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterSeat *clutter_seat; pointer->pointer_clients = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) meta_wayland_pointer_client_free); pointer->cursor_surface = NULL; clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); pointer->device = clutter_seat_get_pointer (clutter_seat); g_signal_connect (cursor_tracker, "cursor-changed", G_CALLBACK (meta_wayland_pointer_on_cursor_changed), pointer); g_signal_connect_swapped (cursor_tracker, "visibility-changed", G_CALLBACK (sync_focus_surface), pointer); g_signal_connect_swapped (clutter_seat, "is-unfocus-inhibited-changed", G_CALLBACK (sync_focus_surface), pointer); } void meta_wayland_pointer_disable (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); g_signal_handlers_disconnect_by_func (cursor_tracker, (gpointer) meta_wayland_pointer_on_cursor_changed, pointer); g_signal_handlers_disconnect_by_func (cursor_tracker, sync_focus_surface, pointer); g_signal_handlers_disconnect_by_func (clutter_seat, sync_focus_surface, pointer); if (pointer->cursor_surface) { g_clear_signal_handler (&pointer->cursor_surface_destroy_id, pointer->cursor_surface); } meta_wayland_pointer_cancel_grab (pointer); meta_wayland_pointer_reset_grab (pointer); meta_wayland_pointer_set_focus (pointer, NULL); meta_wayland_pointer_set_current (pointer, NULL); g_clear_pointer (&pointer->pointer_clients, g_hash_table_unref); pointer->cursor_surface = NULL; } static int count_buttons (const ClutterEvent *event) { static gint maskmap[5] = { CLUTTER_BUTTON1_MASK, CLUTTER_BUTTON2_MASK, CLUTTER_BUTTON3_MASK, CLUTTER_BUTTON4_MASK, CLUTTER_BUTTON5_MASK }; ClutterModifierType mod_mask; int i, count; mod_mask = clutter_event_get_state (event); count = 0; for (i = 0; i < 5; i++) { if (mod_mask & maskmap[i]) count++; } return count; } static void current_surface_destroyed (MetaWaylandSurface *surface, MetaWaylandPointer *pointer) { meta_wayland_pointer_set_current (pointer, NULL); } static void meta_wayland_pointer_set_current (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { if (pointer->current) { g_clear_signal_handler (&pointer->current_surface_destroyed_handler_id, pointer->current); pointer->current = NULL; } if (surface) { pointer->current = surface; pointer->current_surface_destroyed_handler_id = g_signal_connect (surface, "destroy", G_CALLBACK (current_surface_destroyed), pointer); } } static void repick_for_event (MetaWaylandPointer *pointer, const ClutterEvent *for_event) { ClutterActor *actor; MetaWaylandSurface *surface; if (!for_event) return; if (clutter_event_type (for_event) == CLUTTER_LEAVE) actor = clutter_event_get_related (for_event); else actor = clutter_event_get_source (for_event); if (META_IS_SURFACE_ACTOR_WAYLAND (actor)) { MetaSurfaceActorWayland *actor_wayland = META_SURFACE_ACTOR_WAYLAND (actor); surface = meta_surface_actor_wayland_get_surface (actor_wayland); } else { surface = NULL; } meta_wayland_pointer_set_current (pointer, surface); sync_focus_surface (pointer); meta_wayland_pointer_update_cursor_surface (pointer); } void meta_wayland_pointer_update (MetaWaylandPointer *pointer, const ClutterEvent *event) { repick_for_event (pointer, event); if (event->type == CLUTTER_MOTION || event->type == CLUTTER_BUTTON_PRESS || event->type == CLUTTER_BUTTON_RELEASE) { pointer->button_count = count_buttons (event); } } static void notify_motion (MetaWaylandPointer *pointer, const ClutterEvent *event) { pointer->grab->interface->motion (pointer->grab, event); } static void handle_motion_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { notify_motion (pointer, event); } static void handle_button_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { gboolean implicit_grab; implicit_grab = (event->type == CLUTTER_BUTTON_PRESS) && (pointer->button_count == 1); if (implicit_grab) { pointer->grab_button = clutter_event_get_button (event); pointer->grab_time = clutter_event_get_time (event); clutter_event_get_coords (event, &pointer->grab_x, &pointer->grab_y); } pointer->grab->interface->button (pointer->grab, event); if (implicit_grab) { MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (pointer); pointer->grab_serial = wl_display_get_serial (seat->wl_display); } } static void handle_scroll_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { struct wl_resource *resource; wl_fixed_t x_value = 0, y_value = 0; int x_discrete = 0, y_discrete = 0; enum wl_pointer_axis_source source = -1; if (clutter_event_is_pointer_emulated (event)) return; switch (event->scroll.scroll_source) { case CLUTTER_SCROLL_SOURCE_WHEEL: source = WL_POINTER_AXIS_SOURCE_WHEEL; break; case CLUTTER_SCROLL_SOURCE_FINGER: source = WL_POINTER_AXIS_SOURCE_FINGER; break; case CLUTTER_SCROLL_SOURCE_CONTINUOUS: source = WL_POINTER_AXIS_SOURCE_CONTINUOUS; break; default: source = WL_POINTER_AXIS_SOURCE_WHEEL; break; } switch (clutter_event_get_scroll_direction (event)) { case CLUTTER_SCROLL_UP: y_value = -DEFAULT_AXIS_STEP_DISTANCE; y_discrete = -1; break; case CLUTTER_SCROLL_DOWN: y_value = DEFAULT_AXIS_STEP_DISTANCE; y_discrete = 1; break; case CLUTTER_SCROLL_LEFT: x_value = -DEFAULT_AXIS_STEP_DISTANCE; x_discrete = -1; break; case CLUTTER_SCROLL_RIGHT: x_value = DEFAULT_AXIS_STEP_DISTANCE; x_discrete = 1; break; case CLUTTER_SCROLL_SMOOTH: { double dx, dy; /* Clutter smooth scroll events are in discrete steps (1 step = 1.0 long * vector along one axis). To convert to smooth scroll events that are * in pointer motion event space, multiply the vector with the 10. */ const double factor = 10.0; clutter_event_get_scroll_delta (event, &dx, &dy); x_value = wl_fixed_from_double (dx) * factor; y_value = wl_fixed_from_double (dy) * factor; } break; default: return; } if (pointer->focus_client) { wl_resource_for_each (resource, &pointer->focus_client->pointer_resources) { if (wl_resource_get_version (resource) >= WL_POINTER_AXIS_SOURCE_SINCE_VERSION) wl_pointer_send_axis_source (resource, source); /* X axis */ if (x_discrete != 0 && wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) wl_pointer_send_axis_discrete (resource, WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_discrete); if (x_value) wl_pointer_send_axis (resource, clutter_event_get_time (event), WL_POINTER_AXIS_HORIZONTAL_SCROLL, x_value); if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_HORIZONTAL) && wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION) wl_pointer_send_axis_stop (resource, clutter_event_get_time (event), WL_POINTER_AXIS_HORIZONTAL_SCROLL); /* Y axis */ if (y_discrete != 0 && wl_resource_get_version (resource) >= WL_POINTER_AXIS_DISCRETE_SINCE_VERSION) wl_pointer_send_axis_discrete (resource, WL_POINTER_AXIS_VERTICAL_SCROLL, y_discrete); if (y_value) wl_pointer_send_axis (resource, clutter_event_get_time (event), WL_POINTER_AXIS_VERTICAL_SCROLL, y_value); if ((event->scroll.finish_flags & CLUTTER_SCROLL_FINISHED_VERTICAL) && wl_resource_get_version (resource) >= WL_POINTER_AXIS_STOP_SINCE_VERSION) wl_pointer_send_axis_stop (resource, clutter_event_get_time (event), WL_POINTER_AXIS_VERTICAL_SCROLL); } meta_wayland_pointer_broadcast_frame (pointer); } } gboolean meta_wayland_pointer_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event) { switch (event->type) { case CLUTTER_MOTION: handle_motion_event (pointer, event); break; case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: handle_button_event (pointer, event); break; case CLUTTER_SCROLL: handle_scroll_event (pointer, event); break; case CLUTTER_TOUCHPAD_SWIPE: meta_wayland_pointer_gesture_swipe_handle_event (pointer, event); break; case CLUTTER_TOUCHPAD_PINCH: meta_wayland_pointer_gesture_pinch_handle_event (pointer, event); break; default: break; } return FALSE; } static void meta_wayland_pointer_send_enter (MetaWaylandPointer *pointer, struct wl_resource *pointer_resource, uint32_t serial, MetaWaylandSurface *surface) { wl_fixed_t sx, sy; meta_wayland_pointer_get_relative_coordinates (pointer, surface, &sx, &sy); wl_pointer_send_enter (pointer_resource, serial, surface->resource, sx, sy); } static void meta_wayland_pointer_send_leave (MetaWaylandPointer *pointer, struct wl_resource *pointer_resource, uint32_t serial, MetaWaylandSurface *surface) { wl_pointer_send_leave (pointer_resource, serial, surface->resource); } static void meta_wayland_pointer_broadcast_enter (MetaWaylandPointer *pointer, uint32_t serial, MetaWaylandSurface *surface) { struct wl_resource *pointer_resource; wl_resource_for_each (pointer_resource, &pointer->focus_client->pointer_resources) meta_wayland_pointer_send_enter (pointer, pointer_resource, serial, surface); meta_wayland_pointer_broadcast_frame (pointer); } static void meta_wayland_pointer_broadcast_leave (MetaWaylandPointer *pointer, uint32_t serial, MetaWaylandSurface *surface) { struct wl_resource *pointer_resource; wl_resource_for_each (pointer_resource, &pointer->focus_client->pointer_resources) meta_wayland_pointer_send_leave (pointer, pointer_resource, serial, surface); meta_wayland_pointer_broadcast_frame (pointer); } static void focus_surface_destroyed (MetaWaylandSurface *surface, MetaWaylandPointer *pointer) { meta_wayland_pointer_set_focus (pointer, NULL); } void meta_wayland_pointer_set_focus (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); ClutterBackend *clutter_backend = clutter_get_default_backend (); ClutterSeat *clutter_seat = clutter_backend_get_default_seat (clutter_backend); g_return_if_fail (meta_cursor_tracker_get_pointer_visible (cursor_tracker) || clutter_seat_is_unfocus_inhibited (clutter_seat) || surface == NULL); if (pointer->focus_surface == surface) return; if (pointer->focus_surface != NULL) { uint32_t serial; serial = meta_wayland_input_device_next_serial (input_device); if (pointer->focus_client) { meta_wayland_pointer_broadcast_leave (pointer, serial, pointer->focus_surface); pointer->focus_client = NULL; } g_clear_signal_handler (&pointer->focus_surface_destroyed_handler_id, pointer->focus_surface); pointer->focus_surface = NULL; } if (surface != NULL) { ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); struct wl_client *client = wl_resource_get_client (surface->resource); graphene_point_t pos; MetaWindow *focus_window; pointer->focus_surface = surface; pointer->focus_surface_destroyed_handler_id = g_signal_connect_after (pointer->focus_surface, "destroy", G_CALLBACK (focus_surface_destroyed), pointer); clutter_stage_get_device_coords (stage, pointer->device, NULL, &pos); focus_window = meta_wayland_surface_get_window (pointer->focus_surface); if (focus_window) meta_window_handle_enter (focus_window, /* XXX -- can we reliably get a timestamp for setting focus? */ clutter_get_current_event_time (), pos.x, pos.y); pointer->focus_client = meta_wayland_pointer_get_pointer_client (pointer, client); if (pointer->focus_client && pointer->focus_surface) { pointer->focus_serial = meta_wayland_input_device_next_serial (input_device); meta_wayland_pointer_broadcast_enter (pointer, pointer->focus_serial, pointer->focus_surface); } } meta_wayland_pointer_update_cursor_surface (pointer); g_signal_emit (pointer, signals[FOCUS_SURFACE_CHANGED], 0); } void meta_wayland_pointer_start_grab (MetaWaylandPointer *pointer, MetaWaylandPointerGrab *grab) { const MetaWaylandPointerGrabInterface *interface; meta_wayland_pointer_cancel_grab (pointer); pointer->grab = grab; interface = pointer->grab->interface; grab->pointer = pointer; interface->focus (pointer->grab, pointer->current); } static void meta_wayland_pointer_reset_grab (MetaWaylandPointer *pointer) { pointer->grab = &pointer->default_grab; } void meta_wayland_pointer_end_grab (MetaWaylandPointer *pointer) { const MetaWaylandPointerGrabInterface *interface; pointer->grab = &pointer->default_grab; interface = pointer->grab->interface; interface->focus (pointer->grab, pointer->current); meta_wayland_pointer_update_cursor_surface (pointer); } static void meta_wayland_pointer_cancel_grab (MetaWaylandPointer *pointer) { if (pointer->grab->interface->cancel) pointer->grab->interface->cancel (pointer->grab); } void meta_wayland_pointer_end_popup_grab (MetaWaylandPointer *pointer) { MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)pointer->grab; meta_wayland_popup_grab_destroy (popup_grab); } MetaWaylandPopup * meta_wayland_pointer_start_popup_grab (MetaWaylandPointer *pointer, MetaWaylandPopupSurface *popup_surface) { MetaWaylandPopupGrab *grab; if (pointer->grab != &pointer->default_grab && !meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) return NULL; if (pointer->grab == &pointer->default_grab) grab = meta_wayland_popup_grab_create (pointer, popup_surface); else grab = (MetaWaylandPopupGrab*)pointer->grab; return meta_wayland_popup_create (popup_surface, grab); } void meta_wayland_pointer_repick (MetaWaylandPointer *pointer) { clutter_input_device_update (pointer->device, NULL, FALSE); repick_for_event (pointer, NULL); } void meta_wayland_pointer_get_relative_coordinates (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, wl_fixed_t *sx, wl_fixed_t *sy) { MetaBackend *backend = meta_get_backend (); ClutterStage *stage = CLUTTER_STAGE (meta_backend_get_stage (backend)); float xf = 0.0f, yf = 0.0f; graphene_point_t pos; clutter_stage_get_device_coords (stage, pointer->device, NULL, &pos); meta_wayland_surface_get_relative_coordinates (surface, pos.x, pos.y, &xf, &yf); *sx = wl_fixed_from_double (xf); *sy = wl_fixed_from_double (yf); } void meta_wayland_pointer_update_cursor_surface (MetaWaylandPointer *pointer) { MetaBackend *backend = meta_get_backend (); MetaCursorTracker *cursor_tracker = meta_backend_get_cursor_tracker (backend); if (pointer->current) { MetaCursorSprite *cursor_sprite = NULL; if (pointer->cursor_surface) { MetaWaylandCursorSurface *cursor_surface = META_WAYLAND_CURSOR_SURFACE (pointer->cursor_surface->role); cursor_sprite = meta_wayland_cursor_surface_get_sprite (cursor_surface); } meta_cursor_tracker_set_window_cursor (cursor_tracker, cursor_sprite); } else { meta_cursor_tracker_unset_window_cursor (cursor_tracker); } } static void ensure_update_cursor_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { if (pointer->cursor_surface != surface) return; pointer->cursor_surface = NULL; meta_wayland_pointer_update_cursor_surface (pointer); } static void meta_wayland_pointer_set_cursor_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *cursor_surface) { MetaWaylandSurface *prev_cursor_surface; prev_cursor_surface = pointer->cursor_surface; if (prev_cursor_surface == cursor_surface) return; pointer->cursor_surface = cursor_surface; if (prev_cursor_surface) { meta_wayland_surface_update_outputs (prev_cursor_surface); g_clear_signal_handler (&pointer->cursor_surface_destroy_id, prev_cursor_surface); } if (cursor_surface) { pointer->cursor_surface_destroy_id = g_signal_connect_swapped (cursor_surface, "destroy", G_CALLBACK (ensure_update_cursor_surface), pointer); } meta_wayland_pointer_update_cursor_surface (pointer); } static void pointer_set_cursor (struct wl_client *client, struct wl_resource *resource, uint32_t serial, struct wl_resource *surface_resource, int32_t hot_x, int32_t hot_y) { MetaWaylandPointer *pointer = wl_resource_get_user_data (resource); MetaWaylandSurface *surface; surface = (surface_resource ? wl_resource_get_user_data (surface_resource) : NULL); if (pointer->focus_surface == NULL) return; if (wl_resource_get_client (pointer->focus_surface->resource) != client) return; if (pointer->focus_serial - serial > G_MAXUINT32 / 2) return; if (surface && !meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_CURSOR_SURFACE, NULL)) { wl_resource_post_error (resource, WL_POINTER_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface_resource)); return; } if (surface) { MetaCursorRenderer *cursor_renderer = meta_backend_get_cursor_renderer (meta_get_backend ()); MetaWaylandCursorSurface *cursor_surface; cursor_surface = META_WAYLAND_CURSOR_SURFACE (surface->role); meta_wayland_cursor_surface_set_renderer (cursor_surface, cursor_renderer); meta_wayland_cursor_surface_set_hotspot (cursor_surface, hot_x, hot_y); } meta_wayland_pointer_set_cursor_surface (pointer, surface); } static void pointer_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_pointer_interface pointer_interface = { pointer_set_cursor, pointer_release, }; void meta_wayland_pointer_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; MetaWaylandPointerClient *pointer_client; resource = wl_resource_create (client, &wl_pointer_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &pointer_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); pointer_client = meta_wayland_pointer_ensure_pointer_client (pointer, client); wl_list_insert (&pointer_client->pointer_resources, wl_resource_get_link (resource)); if (pointer->focus_client == pointer_client) { meta_wayland_pointer_send_enter (pointer, resource, pointer->focus_serial, pointer->focus_surface); meta_wayland_pointer_send_frame (pointer, resource); } } static gboolean pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface) { MetaWaylandSurface *subsurface; if (pointer->focus_surface == surface) return TRUE; META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface) { if (pointer_can_grab_surface (pointer, subsurface)) return TRUE; } return FALSE; } gboolean meta_wayland_pointer_can_grab_surface (MetaWaylandPointer *pointer, MetaWaylandSurface *surface, uint32_t serial) { return (pointer->grab_serial == serial && pointer_can_grab_surface (pointer, surface)); } gboolean meta_wayland_pointer_can_popup (MetaWaylandPointer *pointer, uint32_t serial) { return pointer->grab_serial == serial; } MetaWaylandSurface * meta_wayland_pointer_get_top_popup (MetaWaylandPointer *pointer) { MetaWaylandPopupGrab *grab; if (!meta_wayland_pointer_grab_is_popup_grab (pointer->grab)) return NULL; grab = (MetaWaylandPopupGrab*)pointer->grab; return meta_wayland_popup_grab_get_top_popup(grab); } static void relative_pointer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_relative_pointer_v1_interface relative_pointer_interface = { relative_pointer_destroy }; static void relative_pointer_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void relative_pointer_manager_get_relative_pointer (struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *pointer_resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); struct wl_resource *resource; MetaWaylandPointerClient *pointer_client; resource = wl_resource_create (client, &zwp_relative_pointer_v1_interface, wl_resource_get_version (manager_resource), id); if (!resource) { wl_client_post_no_memory (client); return; } wl_resource_set_implementation (resource, &relative_pointer_interface, pointer, meta_wayland_pointer_unbind_pointer_client_resource); pointer_client = meta_wayland_pointer_ensure_pointer_client (pointer, client); wl_list_insert (&pointer_client->relative_pointer_resources, wl_resource_get_link (resource)); } static const struct zwp_relative_pointer_manager_v1_interface relative_pointer_manager = { relative_pointer_manager_destroy, relative_pointer_manager_get_relative_pointer, }; static void bind_relative_pointer_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandCompositor *compositor = data; struct wl_resource *resource; resource = wl_resource_create (client, &zwp_relative_pointer_manager_v1_interface, 1, id); if (version != 1) wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "bound invalid version %u of " "wp_relative_pointer_manager", version); wl_resource_set_implementation (resource, &relative_pointer_manager, compositor, NULL); } void meta_wayland_relative_pointer_init (MetaWaylandCompositor *compositor) { /* Relative pointer events are currently only supported by the native backend * so lets just advertise the extension when the native backend is used. */ #ifdef HAVE_NATIVE_BACKEND if (!META_IS_BACKEND_NATIVE (meta_get_backend ())) return; #else return; #endif if (!wl_global_create (compositor->wayland_display, &zwp_relative_pointer_manager_v1_interface, 1, compositor, bind_relative_pointer_manager)) g_error ("Could not create relative pointer manager global"); } MetaWaylandSeat * meta_wayland_pointer_get_seat (MetaWaylandPointer *pointer) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (pointer); return meta_wayland_input_device_get_seat (input_device); } static void meta_wayland_pointer_init (MetaWaylandPointer *pointer) { pointer->default_grab.interface = &default_pointer_grab_interface; pointer->default_grab.pointer = pointer; pointer->grab = &pointer->default_grab; } static void meta_wayland_pointer_class_init (MetaWaylandPointerClass *klass) { signals[FOCUS_SURFACE_CHANGED] = g_signal_new ("focus-surface-changed", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } muffin-6.4.1/src/wayland/meta-pointer-confinement-wayland.c0000664000175000017500000005566314723361714022717 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:meta-pointer-confinement-wayland * @title: MetaPointerConfinementWayland * @short_description: A #MetaPointerConstraint implementing pointer confinement * * A MetaPointerConfinementConstraint implements the client pointer constraint * "pointer confinement": the cursor should not be able to "break out" of a * certain area defined by the client requesting it. */ #include "config.h" #include "wayland/meta-pointer-confinement-wayland.h" #include #include #include "backends/meta-backend-private.h" #include "backends/meta-pointer-constraint.h" #include "compositor/meta-surface-actor-wayland.h" #include "core/meta-border.h" #include "wayland/meta-wayland-pointer-constraints.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-surface.h" struct _MetaPointerConfinementWayland { MetaPointerConstraint parent; MetaWaylandPointerConstraint *constraint; }; typedef struct _MetaBox { int x1; int y1; int x2; int y2; } MetaBox; G_DEFINE_TYPE (MetaPointerConfinementWayland, meta_pointer_confinement_wayland, META_TYPE_POINTER_CONSTRAINT); static MetaBorder * add_border (GArray *borders, float x1, float y1, float x2, float y2, MetaBorderMotionDirection blocking_directions) { MetaBorder border; border = (MetaBorder) { .line = (MetaLine2) { .a = (MetaVector2) { .x = x1, .y = y1, }, .b = (MetaVector2) { .x = x2, .y = y2, }, }, .blocking_directions = blocking_directions, }; g_array_append_val (borders, border); return &g_array_index (borders, MetaBorder, borders->len - 1); } static gint compare_lines_x (gconstpointer a, gconstpointer b) { const MetaBorder *border_a = a; const MetaBorder *border_b = b; if (border_a->line.a.x == border_b->line.a.x) return border_a->line.b.x < border_b->line.b.x; else return border_a->line.a.x > border_b->line.a.x; } static void add_non_overlapping_edges (MetaBox *boxes, unsigned int band_above_start, unsigned int band_below_start, unsigned int band_below_end, GArray *borders) { unsigned int i; GArray *band_merge; MetaBorder *border; MetaBorder *prev_border; MetaBorder *new_border; band_merge = g_array_new (FALSE, FALSE, sizeof *border); /* Add bottom band of previous row, and top band of current row, and * sort them so lower left x coordinate comes first. If there are two * borders with the same left x coordinate, the wider one comes first. */ for (i = band_above_start; i < band_below_start; i++) { MetaBox *box = &boxes[i]; add_border (band_merge, box->x1, box->y2, box->x2, box->y2, META_BORDER_MOTION_DIRECTION_POSITIVE_Y); } for (i = band_below_start; i < band_below_end; i++) { MetaBox *box= &boxes[i]; add_border (band_merge, box->x1, box->y1, box->x2, box->y1, META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); } g_array_sort (band_merge, compare_lines_x); /* Combine the two combined bands so that any overlapping border is * eliminated. */ prev_border = NULL; for (i = 0; i < band_merge->len; i++) { border = &g_array_index (band_merge, MetaBorder, i); g_assert (border->line.a.y == border->line.b.y); g_assert (!prev_border || prev_border->line.a.y == border->line.a.y); g_assert (!prev_border || (prev_border->line.a.x != border->line.a.x || prev_border->line.b.x != border->line.b.x)); g_assert (!prev_border || prev_border->line.a.x <= border->line.a.x); if (prev_border && prev_border->line.a.x == border->line.a.x) { /* * ------------ + * ------- = * [ ]----- */ prev_border->line.a.x = border->line.b.x; } else if (prev_border && prev_border->line.b.x == border->line.b.x) { /* * ------------ + * ------ = * ------[ ] */ prev_border->line.b.x = border->line.a.x; } else if (prev_border && prev_border->line.b.x == border->line.a.x) { /* * -------- + * ------ = * -------------- */ prev_border->line.b.x = border->line.b.x; } else if (prev_border && prev_border->line.b.x >= border->line.a.x) { /* * --------------- + * ------ = * -----[ ]---- */ new_border = add_border (borders, border->line.b.x, border->line.b.y, prev_border->line.b.x, prev_border->line.b.y, prev_border->blocking_directions); prev_border->line.b.x = border->line.a.x; prev_border = new_border; } else { g_assert (!prev_border || prev_border->line.b.x < border->line.a.x); /* * First border or non-overlapping. * * ----- + * ----- = * ----- ----- */ g_array_append_val (borders, *border); prev_border = &g_array_index (borders, MetaBorder, borders->len - 1); } } g_array_free (band_merge, FALSE); } static void add_band_bottom_edges (MetaBox *boxes, int band_start, int band_end, GArray *borders) { int i; for (i = band_start; i < band_end; i++) { add_border (borders, boxes[i].x1, boxes[i].y2, boxes[i].x2, boxes[i].y2, META_BORDER_MOTION_DIRECTION_POSITIVE_Y); } } static void region_to_outline (cairo_region_t *region, GArray *borders) { MetaBox *boxes; int num_boxes; int i; int top_most, bottom_most; int current_roof; int prev_top; int band_start, prev_band_start; /* * Remove any overlapping lines from the set of rectangles. Note that * pixman regions are grouped as rows of rectangles, where rectangles * in one row never touch or overlap and are all of the same height. * * -------- --- -------- --- * | | | | | | | | * ----------====---- --- ----------- ----- --- * | | => | | * ----==========--------- ----- ---------- * | | | | * ------------------- ------------------- * */ num_boxes = cairo_region_num_rectangles (region); boxes = g_new (MetaBox, num_boxes); for (i = 0; i < num_boxes; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); boxes[i] = (MetaBox) { .x1 = rect.x, .y1 = rect.y, .x2 = rect.x + rect.width, .y2 = rect.y + rect.height, }; } prev_top = 0; top_most = boxes[0].y1; current_roof = top_most; bottom_most = boxes[num_boxes - 1].y2; band_start = 0; prev_band_start = 0; for (i = 0; i < num_boxes; i++) { /* Detect if there is a vertical empty space, and add the lower * level of the previous band if so was the case. */ if (i > 0 && boxes[i].y1 != prev_top && boxes[i].y1 != boxes[i - 1].y2) { current_roof = boxes[i].y1; add_band_bottom_edges (boxes, band_start, i, borders); } /* Special case adding the last band, since it won't be handled * by the band change detection below. */ if (boxes[i].y1 != current_roof && i == num_boxes - 1) { if (boxes[i].y1 != prev_top) { /* The last band is a single box, so we don't * have a prev_band_start to tell us when the * previous band started. */ add_non_overlapping_edges (boxes, band_start, i, i + 1, borders); } else { add_non_overlapping_edges (boxes, prev_band_start, band_start, i + 1, borders); } } /* Detect when passing a band and combine the top border of the * just passed band with the bottom band of the previous band. */ if (boxes[i].y1 != top_most && boxes[i].y1 != prev_top) { /* Combine the two passed bands. */ if (prev_top != current_roof) { add_non_overlapping_edges (boxes, prev_band_start, band_start, i, borders); } prev_band_start = band_start; band_start = i; } /* Add the top border if the box is part of the current roof. */ if (boxes[i].y1 == current_roof) { add_border (borders, boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y1, META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); } /* Add the bottom border of the last band. */ if (boxes[i].y2 == bottom_most) { add_border (borders, boxes[i].x1, boxes[i].y2, boxes[i].x2, boxes[i].y2, META_BORDER_MOTION_DIRECTION_POSITIVE_Y); } /* Always add the left border. */ add_border (borders, boxes[i].x1, boxes[i].y1, boxes[i].x1, boxes[i].y2, META_BORDER_MOTION_DIRECTION_NEGATIVE_X); /* Always add the right border. */ add_border (borders, boxes[i].x2, boxes[i].y1, boxes[i].x2, boxes[i].y2, META_BORDER_MOTION_DIRECTION_POSITIVE_X); prev_top = boxes[i].y1; } g_free (boxes); } static MetaBorder * get_closest_border (GArray *borders, MetaLine2 *motion, uint32_t directions) { MetaBorder *border; MetaVector2 intersection; MetaVector2 delta; float distance_2; MetaBorder *closest_border = NULL; float closest_distance_2 = DBL_MAX; unsigned int i; for (i = 0; i < borders->len; i++) { border = &g_array_index (borders, MetaBorder, i); if (!meta_border_is_blocking_directions (border, directions)) continue; if (!meta_line2_intersects_with (&border->line, motion, &intersection)) continue; delta = meta_vector2_subtract (intersection, motion->a); distance_2 = delta.x*delta.x + delta.y*delta.y; if (distance_2 < closest_distance_2) { closest_border = border; closest_distance_2 = distance_2; } } return closest_border; } static void clamp_to_border (MetaBorder *border, MetaLine2 *motion, uint32_t *motion_dir) { /* * When clamping either rightward or downward motions, the motion needs to be * clamped so that the destination coordinate does not end up on the border * (see weston_pointer_clamp_event_to_region). Do this by clamping such * motions to the border minus the smallest possible wl_fixed_t value. * * When clamping in either leftward or upward motion, the resulting coordinate * needs to be clamped so that it is enough on the inside to avoid the * inaccuracies of clutter's stage to actor transformation algorithm (the one * used in clutter_actor_transform_stage_point) to make it end up outside the * next motion. It also needs to be clamped so that to the wl_fixed_t * coordinate may still be right on the border (i.e. at .0). Testing shows * that the smallest wl_fixed_t value divided by 10 is small enough to make * the wl_fixed_t coordinate .0 and large enough to avoid the inaccuracies of * clutters transform algorithm. */ if (meta_border_is_horizontal (border)) { if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_Y) motion->b.y = border->line.a.y - wl_fixed_to_double (1); else motion->b.y = border->line.a.y + wl_fixed_to_double (1) / 10; *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_Y | META_BORDER_MOTION_DIRECTION_NEGATIVE_Y); } else { if (*motion_dir & META_BORDER_MOTION_DIRECTION_POSITIVE_X) motion->b.x = border->line.a.x - wl_fixed_to_double (1); else motion->b.x = border->line.a.x + wl_fixed_to_double (1) / 10; *motion_dir &= ~(META_BORDER_MOTION_DIRECTION_POSITIVE_X | META_BORDER_MOTION_DIRECTION_NEGATIVE_X); } } static uint32_t get_motion_directions (MetaLine2 *motion) { uint32_t directions = 0; if (motion->a.x < motion->b.x) directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_X; else if (motion->a.x > motion->b.x) directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_X; if (motion->a.y < motion->b.y) directions |= META_BORDER_MOTION_DIRECTION_POSITIVE_Y; else if (motion->a.y > motion->b.y) directions |= META_BORDER_MOTION_DIRECTION_NEGATIVE_Y; return directions; } static void meta_pointer_confinement_wayland_constrain (MetaPointerConstraint *constraint, ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y) { MetaPointerConfinementWayland *self = META_POINTER_CONFINEMENT_WAYLAND (constraint); MetaWaylandSurface *surface; cairo_region_t *region; float sx, sy; float prev_sx, prev_sy; GArray *borders; MetaLine2 motion; MetaBorder *closest_border; uint32_t directions; surface = meta_wayland_pointer_constraint_get_surface (self->constraint); meta_wayland_surface_get_relative_coordinates (surface, *x, *y, &sx, &sy); meta_wayland_surface_get_relative_coordinates (surface, prev_x, prev_y, &prev_sx, &prev_sy); /* For motions in a positive direction on any axis, append the smallest * possible value representable in a Wayland absolute coordinate. This is * in order to avoid not clamping motion that as a floating point number * won't be clamped, but will be rounded up to be outside of the range * of wl_fixed_t. */ if (sx > prev_sx) sx += (float)wl_fixed_to_double(1); if (sy > prev_sy) sy += (float)wl_fixed_to_double(1); borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder)); /* * Generate borders given the confine region we are to use. The borders * are defined to be the outer region of the allowed area. This means * top/left borders are "within" the allowed area, while bottom/right * borders are outside. This needs to be considered when clamping * confined motion vectors. */ region = meta_wayland_pointer_constraint_calculate_effective_region (self->constraint); region_to_outline (region, borders); cairo_region_destroy (region); motion = (MetaLine2) { .a = (MetaVector2) { .x = prev_sx, .y = prev_sy, }, .b = (MetaVector2) { .x = sx, .y = sy, }, }; directions = get_motion_directions (&motion); while (directions) { closest_border = get_closest_border (borders, &motion, directions); if (closest_border) clamp_to_border (closest_border, &motion, &directions); else break; } meta_wayland_surface_get_absolute_coordinates (surface, motion.b.x, motion.b.y, x, y); g_array_free (borders, FALSE); } static float point_to_border_distance_2 (MetaBorder *border, float x, float y) { float orig_x, orig_y; float dx, dy; if (meta_border_is_horizontal (border)) { if (x < border->line.a.x) orig_x = border->line.a.x; else if (x > border->line.b.x) orig_x = border->line.b.x; else orig_x = x; orig_y = border->line.a.y; } else { if (y < border->line.a.y) orig_y = border->line.a.y; else if (y > border->line.b.y) orig_y = border->line.b.y; else orig_y = y; orig_x = border->line.a.x; } dx = fabsf (orig_x - x); dy = fabsf (orig_y - y); return dx*dx + dy*dy; } static void warp_to_behind_border (MetaBorder *border, float *sx, float *sy) { switch (border->blocking_directions) { case META_BORDER_MOTION_DIRECTION_POSITIVE_X: case META_BORDER_MOTION_DIRECTION_NEGATIVE_X: if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_X) *sx = border->line.a.x - wl_fixed_to_double (1); else *sx = border->line.a.x + wl_fixed_to_double (1); if (*sy < border->line.a.y) *sy = border->line.a.y + wl_fixed_to_double (1); else if (*sy > border->line.b.y) *sy = border->line.b.y - wl_fixed_to_double (1); break; case META_BORDER_MOTION_DIRECTION_POSITIVE_Y: case META_BORDER_MOTION_DIRECTION_NEGATIVE_Y: if (border->blocking_directions == META_BORDER_MOTION_DIRECTION_POSITIVE_Y) *sy = border->line.a.y - wl_fixed_to_double (1); else *sy = border->line.a.y + wl_fixed_to_double (1); if (*sx < border->line.a.x) *sx = border->line.a.x + wl_fixed_to_double (1); else if (*sx > (border->line.b.x)) *sx = border->line.b.x - wl_fixed_to_double (1); break; } } static void meta_pointer_confinement_wayland_maybe_warp (MetaPointerConfinementWayland *self) { MetaWaylandSeat *seat; MetaWaylandSurface *surface; graphene_point_t point; float sx; float sy; cairo_region_t *region; seat = meta_wayland_pointer_constraint_get_seat (self->constraint); surface = meta_wayland_pointer_constraint_get_surface (self->constraint); clutter_input_device_get_coords (seat->pointer->device, NULL, &point); meta_wayland_surface_get_relative_coordinates (surface, point.x, point.y, &sx, &sy); region = meta_wayland_pointer_constraint_calculate_effective_region (self->constraint); if (!cairo_region_contains_point (region, (int)sx, (int)sy)) { GArray *borders; float closest_distance_2 = FLT_MAX; MetaBorder *closest_border = NULL; ClutterSeat *seat; unsigned int i; float x; float y; borders = g_array_new (FALSE, FALSE, sizeof (MetaBorder)); region_to_outline (region, borders); for (i = 0; i < borders->len; i++) { MetaBorder *border = &g_array_index (borders, MetaBorder, i); float distance_2; distance_2 = point_to_border_distance_2 (border, sx, sy); if (distance_2 < closest_distance_2) { closest_border = border; closest_distance_2 = distance_2; } } warp_to_behind_border (closest_border, &sx, &sy); meta_wayland_surface_get_absolute_coordinates (surface, sx, sy, &x, &y); seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); clutter_seat_warp_pointer (seat, (int)x, (int)y); } cairo_region_destroy (region); } static void surface_geometry_changed (MetaWaylandSurface *surface, MetaPointerConfinementWayland *self) { meta_pointer_confinement_wayland_maybe_warp (self); } static void window_position_changed (MetaWindow *window, MetaPointerConfinementWayland *self) { meta_pointer_confinement_wayland_maybe_warp (self); } MetaPointerConstraint * meta_pointer_confinement_wayland_new (MetaWaylandPointerConstraint *constraint) { GObject *object; MetaPointerConfinementWayland *confinement; MetaWaylandSurface *surface; MetaWindow *window; object = g_object_new (META_TYPE_POINTER_CONFINEMENT_WAYLAND, NULL); confinement = META_POINTER_CONFINEMENT_WAYLAND (object); confinement->constraint = constraint; surface = meta_wayland_pointer_constraint_get_surface (constraint); g_signal_connect_object (surface, "geometry-changed", G_CALLBACK (surface_geometry_changed), confinement, 0); window = meta_wayland_surface_get_window (surface); if (window) { g_signal_connect_object (window, "position-changed", G_CALLBACK (window_position_changed), confinement, 0); } return META_POINTER_CONSTRAINT (confinement); } static void meta_pointer_confinement_wayland_init (MetaPointerConfinementWayland *confinement_wayland) { } static void meta_pointer_confinement_wayland_class_init (MetaPointerConfinementWaylandClass *klass) { MetaPointerConstraintClass *pointer_constraint_class = META_POINTER_CONSTRAINT_CLASS (klass); pointer_constraint_class->constrain = meta_pointer_confinement_wayland_constrain; } muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-ring.h0000664000175000017500000000433014723361714021674 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_PAD_RING_H #define META_WAYLAND_TABLET_PAD_RING_H #include #include #include "backends/meta-cursor-renderer.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletPadRing { MetaWaylandTabletPad *pad; MetaWaylandTabletPadGroup *group; struct wl_list resource_list; struct wl_list focus_resource_list; gchar *feedback; }; MetaWaylandTabletPadRing * meta_wayland_tablet_pad_ring_new (MetaWaylandTabletPad *pad); void meta_wayland_tablet_pad_ring_free (MetaWaylandTabletPadRing *ring); void meta_wayland_tablet_pad_ring_set_group (MetaWaylandTabletPadRing *ring, MetaWaylandTabletPadGroup *group); struct wl_resource * meta_wayland_tablet_pad_ring_create_new_resource (MetaWaylandTabletPadRing *ring, struct wl_client *client, struct wl_resource *group_resource, uint32_t id); gboolean meta_wayland_tablet_pad_ring_handle_event (MetaWaylandTabletPadRing *ring, const ClutterEvent *event); void meta_wayland_tablet_pad_ring_sync_focus (MetaWaylandTabletPadRing *ring); #endif /* META_WAYLAND_TABLET_PAD_RING_H */ muffin-6.4.1/src/wayland/meta-wayland-xdg-foreign.h0000664000175000017500000000213714723361714021136 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_WAYLAND_FOREIGN_H #define META_WAYLAND_FOREIGN_H #include #include "wayland/meta-wayland-types.h" gboolean meta_wayland_xdg_foreign_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_FOREIGN_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet.h0000664000175000017500000000415014723361714020175 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_H #define META_WAYLAND_TABLET_H #include #include #include "backends/meta-cursor-renderer.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTablet { MetaWaylandTabletSeat *tablet_seat; ClutterInputDevice *device; struct wl_list resource_list; MetaWaylandSurface *current; }; MetaWaylandTablet * meta_wayland_tablet_new (ClutterInputDevice *device, MetaWaylandTabletSeat *tablet_seat); void meta_wayland_tablet_free (MetaWaylandTablet *tablet); struct wl_resource * meta_wayland_tablet_create_new_resource (MetaWaylandTablet *tablet, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); struct wl_resource * meta_wayland_tablet_lookup_resource (MetaWaylandTablet *tablet, struct wl_client *client); void meta_wayland_tablet_notify (MetaWaylandTablet *tablet, struct wl_resource *resource); #endif /* META_WAYLAND_TABLET_H */ muffin-6.4.1/src/wayland/meta-wayland-window-configuration.h0000664000175000017500000000510514723361714023077 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_WAYLAND_WINDOW_CONFIGURATION_H #define META_WAYLAND_WINDOW_CONFIGURATION_H #include #include #include "core/window-private.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandWindowConfiguration { uint32_t serial; gboolean has_position; int x; int y; gboolean has_relative_position; int rel_x; int rel_y; gboolean has_size; int width; int height; int scale; MetaGravity gravity; MetaMoveResizeFlags flags; }; MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new (int x, int y, int width, int height, int scale, MetaMoveResizeFlags flags, MetaGravity gravity); MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_relative (int rel_x, int rel_y, int width, int height, int scale); MetaWaylandWindowConfiguration * meta_wayland_window_configuration_new_empty (void); void meta_wayland_window_configuration_free (MetaWaylandWindowConfiguration *configuration); #endif /* META_WAYLAND_WINDOW_CONFIGURATION_H */ muffin-6.4.1/src/wayland/meta-selection-source-wayland.c0000664000175000017500000001323214723361714022201 0ustar fabiofabio/* * Copyright (C) 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include #include #include "wayland/meta-selection-source-wayland-private.h" struct _MetaSelectionSourceWayland { MetaSelectionSource parent_instance; MetaWaylandDataSource *data_source; GList *mimetypes; }; G_DEFINE_TYPE (MetaSelectionSourceWayland, meta_selection_source_wayland, META_TYPE_SELECTION_SOURCE) static void meta_selection_source_wayland_finalize (GObject *object) { MetaSelectionSourceWayland *source_wayland = META_SELECTION_SOURCE_WAYLAND (object); g_list_free_full (source_wayland->mimetypes, g_free); G_OBJECT_CLASS (meta_selection_source_wayland_parent_class)->finalize (object); } static void meta_selection_source_wayland_read_async (MetaSelectionSource *source, const gchar *mimetype, GCancellable *cancellable, GAsyncReadyCallback callback, gpointer user_data) { MetaSelectionSourceWayland *source_wayland = META_SELECTION_SOURCE_WAYLAND (source); GInputStream *stream; GTask *task; int pipe_fds[2]; if (!g_unix_open_pipe (pipe_fds, FD_CLOEXEC, NULL)) { g_task_report_new_error (source, callback, user_data, meta_selection_source_wayland_read_async, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not open pipe to read wayland selection"); return; } if (!g_unix_set_fd_nonblocking (pipe_fds[0], TRUE, NULL) || !g_unix_set_fd_nonblocking (pipe_fds[1], TRUE, NULL)) { g_task_report_new_error (source, callback, user_data, meta_selection_source_wayland_read_async, G_IO_ERROR, G_IO_ERROR_FAILED, "Could not make pipe nonblocking"); close (pipe_fds[0]); close (pipe_fds[1]); return; } task = g_task_new (source, cancellable, callback, user_data); g_task_set_source_tag (task, meta_selection_source_wayland_read_async); stream = g_unix_input_stream_new (pipe_fds[0], TRUE); meta_wayland_data_source_send (source_wayland->data_source, mimetype, pipe_fds[1]); close (pipe_fds[1]); g_task_return_pointer (task, stream, g_object_unref); g_object_unref (task); } static GInputStream * meta_selection_source_wayland_read_finish (MetaSelectionSource *source, GAsyncResult *result, GError **error) { g_return_val_if_fail (g_task_is_valid (result, source), FALSE); g_return_val_if_fail (g_task_get_source_tag (G_TASK (result)) == meta_selection_source_wayland_read_async, FALSE); return g_task_propagate_pointer (G_TASK (result), error); } static GList * meta_selection_source_wayland_get_mimetypes (MetaSelectionSource *source) { MetaSelectionSourceWayland *source_wayland = META_SELECTION_SOURCE_WAYLAND (source); return g_list_copy_deep (source_wayland->mimetypes, (GCopyFunc) g_strdup, NULL); } static void meta_selection_source_wayland_deactivated (MetaSelectionSource *source) { MetaSelectionSourceWayland *source_wayland = META_SELECTION_SOURCE_WAYLAND (source); meta_wayland_data_source_cancel (source_wayland->data_source); META_SELECTION_SOURCE_CLASS (meta_selection_source_wayland_parent_class)->deactivated (source); } static void meta_selection_source_wayland_class_init (MetaSelectionSourceWaylandClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaSelectionSourceClass *source_class = META_SELECTION_SOURCE_CLASS (klass); object_class->finalize = meta_selection_source_wayland_finalize; source_class->deactivated = meta_selection_source_wayland_deactivated; source_class->read_async = meta_selection_source_wayland_read_async; source_class->read_finish = meta_selection_source_wayland_read_finish; source_class->get_mimetypes = meta_selection_source_wayland_get_mimetypes; } static void meta_selection_source_wayland_init (MetaSelectionSourceWayland *source) { } static GList * copy_string_array_to_list (struct wl_array *array) { GList *l = NULL; char **p; wl_array_for_each (p, array) l = g_list_prepend (l, g_strdup (*p)); return l; } MetaSelectionSource * meta_selection_source_wayland_new (MetaWaylandDataSource *data_source) { MetaSelectionSourceWayland *source_wayland; struct wl_array *mimetypes; source_wayland = g_object_new (META_TYPE_SELECTION_SOURCE_WAYLAND, NULL); source_wayland->data_source = data_source; mimetypes = meta_wayland_data_source_get_mime_types (data_source); source_wayland->mimetypes = copy_string_array_to_list (mimetypes); return META_SELECTION_SOURCE (source_wayland); } muffin-6.4.1/src/wayland/meta-wayland-surface.c0000664000175000017500000017146514723361714020363 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-surface.h" #include #include #include "backends/meta-cursor-tracker-private.h" #include "clutter/clutter.h" #include "clutter/wayland/clutter-wayland-compositor.h" #include "cogl/cogl-wayland-server.h" #include "cogl/cogl.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-surface-actor.h" #include "compositor/meta-window-actor-private.h" #include "compositor/region-utils.h" #include "core/display-private.h" #include "core/window-private.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-gtk-shell.h" #include "wayland/meta-wayland-keyboard.h" #include "wayland/meta-wayland-legacy-xdg-shell.h" #include "wayland/meta-wayland-outputs.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-region.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-subsurface.h" #include "wayland/meta-wayland-viewporter.h" #include "wayland/meta-wayland-wl-shell.h" #include "wayland/meta-wayland-xdg-shell.h" #include "wayland/meta-window-wayland.h" #include "wayland/meta-xwayland-private.h" #include "wayland/meta-xwayland-private.h" enum { SURFACE_STATE_SIGNAL_APPLIED, SURFACE_STATE_SIGNAL_N_SIGNALS }; enum { SURFACE_ROLE_PROP_0, SURFACE_ROLE_PROP_SURFACE, }; static guint surface_state_signals[SURFACE_STATE_SIGNAL_N_SIGNALS]; typedef struct _MetaWaylandSurfaceRolePrivate { MetaWaylandSurface *surface; } MetaWaylandSurfaceRolePrivate; G_DEFINE_TYPE (MetaWaylandSurface, meta_wayland_surface, G_TYPE_OBJECT); G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandSurfaceRole, meta_wayland_surface_role, G_TYPE_OBJECT) G_DEFINE_TYPE (MetaWaylandSurfaceState, meta_wayland_surface_state, G_TYPE_OBJECT) enum { SURFACE_DESTROY, SURFACE_UNMAPPED, SURFACE_CONFIGURE, SURFACE_SHORTCUTS_INHIBITED, SURFACE_SHORTCUTS_RESTORED, SURFACE_GEOMETRY_CHANGED, SURFACE_PRE_STATE_APPLIED, N_SURFACE_SIGNALS }; guint surface_signals[N_SURFACE_SIGNALS] = { 0 }; static void meta_wayland_surface_role_assigned (MetaWaylandSurfaceRole *surface_role); static void meta_wayland_surface_role_pre_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); static void meta_wayland_surface_role_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); static void meta_wayland_surface_role_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); static gboolean meta_wayland_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor); static MetaWaylandSurface * meta_wayland_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role); static void role_assignment_valist_to_properties (GType role_type, const char *first_property_name, va_list var_args, GArray *names, GArray *values) { GObjectClass *object_class; const char *property_name = first_property_name; object_class = g_type_class_ref (role_type); while (property_name) { GValue value = G_VALUE_INIT; GParamSpec *pspec; GType ptype; gchar *error = NULL; pspec = g_object_class_find_property (object_class, property_name); g_assert (pspec); ptype = G_PARAM_SPEC_VALUE_TYPE (pspec); G_VALUE_COLLECT_INIT (&value, ptype, var_args, 0, &error); g_assert (!error); g_array_append_val (names, property_name); g_array_append_val (values, value); property_name = va_arg (var_args, const char *); } g_type_class_unref (object_class); } gboolean meta_wayland_surface_assign_role (MetaWaylandSurface *surface, GType role_type, const char *first_property_name, ...) { va_list var_args; if (!surface->role) { if (first_property_name) { GArray *names; GArray *values; const char *surface_prop_name; GValue surface_value = G_VALUE_INIT; GObject *role_object; names = g_array_new (FALSE, FALSE, sizeof (const char *)); values = g_array_new (FALSE, FALSE, sizeof (GValue)); g_array_set_clear_func (values, (GDestroyNotify) g_value_unset); va_start (var_args, first_property_name); role_assignment_valist_to_properties (role_type, first_property_name, var_args, names, values); va_end (var_args); surface_prop_name = "surface"; g_value_init (&surface_value, META_TYPE_WAYLAND_SURFACE); g_value_set_object (&surface_value, surface); g_array_append_val (names, surface_prop_name); g_array_append_val (values, surface_value); role_object = g_object_new_with_properties (role_type, values->len, (const char **) names->data, (const GValue *) values->data); surface->role = META_WAYLAND_SURFACE_ROLE (role_object); g_array_free (names, TRUE); g_array_free (values, TRUE); } else { surface->role = g_object_new (role_type, "surface", surface, NULL); } meta_wayland_surface_role_assigned (surface->role); /* Release the use count held on behalf of the just assigned role. */ if (surface->unassigned.buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&surface->unassigned.buffer); } return TRUE; } else if (G_OBJECT_TYPE (surface->role) != role_type) { return FALSE; } else { va_start (var_args, first_property_name); g_object_set_valist (G_OBJECT (surface->role), first_property_name, var_args); va_end (var_args); meta_wayland_surface_role_assigned (surface->role); return TRUE; } } static int get_buffer_width (MetaWaylandSurface *surface) { MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); if (buffer) return cogl_texture_get_width (surface->texture); else return 0; } static int get_buffer_height (MetaWaylandSurface *surface) { MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); if (buffer) return cogl_texture_get_height (surface->texture); else return 0; } static void surface_process_damage (MetaWaylandSurface *surface, cairo_region_t *surface_region, cairo_region_t *buffer_region) { MetaWaylandBuffer *buffer = meta_wayland_surface_get_buffer (surface); cairo_rectangle_int_t surface_rect; cairo_rectangle_int_t buffer_rect; cairo_region_t *scaled_region; cairo_region_t *transformed_region; cairo_region_t *viewport_region; graphene_rect_t src_rect; MetaSurfaceActor *actor; /* If the client destroyed the buffer it attached before committing, but * still posted damage, or posted damage without any buffer, don't try to * process it on the non-existing buffer. */ if (!buffer) return; buffer_rect = (cairo_rectangle_int_t) { .width = get_buffer_width (surface), .height = get_buffer_height (surface), }; /* Intersect the damage region with the surface region before scaling in * order to avoid integer overflow when scaling a damage region is too large * (for example INT32_MAX which mesa passes). */ surface_rect = (cairo_rectangle_int_t) { .width = meta_wayland_surface_get_width (surface), .height = meta_wayland_surface_get_height (surface), }; cairo_region_intersect_rectangle (surface_region, &surface_rect); /* The damage region must be in the same coordinate space as the buffer, * i.e. scaled with surface->scale. */ scaled_region = meta_region_scale (surface_region, surface->scale); if (surface->viewport.has_src_rect) { src_rect = (graphene_rect_t) { .origin.x = surface->viewport.src_rect.origin.x * surface->scale, .origin.y = surface->viewport.src_rect.origin.y * surface->scale, .size.width = surface->viewport.src_rect.size.width * surface->scale, .size.height = surface->viewport.src_rect.size.height * surface->scale }; } else { src_rect = (graphene_rect_t) { .size.width = surface_rect.width * surface->scale, .size.height = surface_rect.height * surface->scale, }; } viewport_region = meta_region_crop_and_scale (scaled_region, &src_rect, surface_rect.width * surface->scale, surface_rect.height * surface->scale); transformed_region = meta_region_transform (viewport_region, surface->buffer_transform, buffer_rect.width, buffer_rect.height); /* Now add the scaled, cropped and transformed damage region to the * buffer damage. Buffer damage is already in the correct coordinate space. */ cairo_region_union (buffer_region, transformed_region); cairo_region_intersect_rectangle (buffer_region, &buffer_rect); meta_wayland_buffer_process_damage (buffer, surface->texture, buffer_region); actor = meta_wayland_surface_get_actor (surface); if (actor) { int i, n_rectangles; n_rectangles = cairo_region_num_rectangles (buffer_region); for (i = 0; i < n_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (buffer_region, i, &rect); meta_surface_actor_process_damage (actor, rect.x, rect.y, rect.width, rect.height); } } cairo_region_destroy (viewport_region); cairo_region_destroy (scaled_region); cairo_region_destroy (transformed_region); } MetaWaylandBuffer * meta_wayland_surface_get_buffer (MetaWaylandSurface *surface) { return surface->buffer_ref.buffer; } void meta_wayland_surface_ref_buffer_use_count (MetaWaylandSurface *surface) { g_return_if_fail (surface->buffer_ref.buffer); g_warn_if_fail (surface->buffer_ref.buffer->resource); surface->buffer_ref.use_count++; } void meta_wayland_surface_unref_buffer_use_count (MetaWaylandSurface *surface) { MetaWaylandBuffer *buffer = surface->buffer_ref.buffer; g_return_if_fail (surface->buffer_ref.use_count != 0); surface->buffer_ref.use_count--; g_return_if_fail (buffer); if (surface->buffer_ref.use_count == 0 && buffer->resource) wl_buffer_send_release (buffer->resource); } static void pending_buffer_resource_destroyed (MetaWaylandBuffer *buffer, MetaWaylandSurfaceState *pending) { g_clear_signal_handler (&pending->buffer_destroy_handler_id, buffer); pending->buffer = NULL; } static void meta_wayland_surface_state_set_default (MetaWaylandSurfaceState *state) { state->newly_attached = FALSE; state->buffer = NULL; state->buffer_destroy_handler_id = 0; state->dx = 0; state->dy = 0; state->scale = 0; state->input_region = NULL; state->input_region_set = FALSE; state->opaque_region = NULL; state->opaque_region_set = FALSE; state->surface_damage = cairo_region_create (); state->buffer_damage = cairo_region_create (); wl_list_init (&state->frame_callback_list); state->has_new_geometry = FALSE; state->has_acked_configure_serial = FALSE; state->has_new_min_size = FALSE; state->has_new_max_size = FALSE; state->has_new_buffer_transform = FALSE; state->has_new_viewport_src_rect = FALSE; state->has_new_viewport_dst_size = FALSE; state->subsurface_placement_ops = NULL; } static void meta_wayland_surface_state_clear (MetaWaylandSurfaceState *state) { MetaWaylandFrameCallback *cb, *next; g_clear_pointer (&state->surface_damage, cairo_region_destroy); g_clear_pointer (&state->buffer_damage, cairo_region_destroy); g_clear_pointer (&state->input_region, cairo_region_destroy); g_clear_pointer (&state->opaque_region, cairo_region_destroy); if (state->buffer) g_clear_signal_handler (&state->buffer_destroy_handler_id, state->buffer); wl_list_for_each_safe (cb, next, &state->frame_callback_list, link) wl_resource_destroy (cb->resource); if (state->subsurface_placement_ops) { g_slist_free_full ( state->subsurface_placement_ops, (GDestroyNotify) meta_wayland_subsurface_placement_op_free); } } static void meta_wayland_surface_state_reset (MetaWaylandSurfaceState *state) { meta_wayland_surface_state_clear (state); meta_wayland_surface_state_set_default (state); } static void meta_wayland_surface_state_merge_into (MetaWaylandSurfaceState *from, MetaWaylandSurfaceState *to) { if (from->newly_attached) { if (to->buffer) g_clear_signal_handler (&to->buffer_destroy_handler_id, to->buffer); if (from->buffer) g_clear_signal_handler (&from->buffer_destroy_handler_id, from->buffer); to->newly_attached = TRUE; to->buffer = from->buffer; to->dx = from->dx; to->dy = from->dy; } wl_list_insert_list (&to->frame_callback_list, &from->frame_callback_list); cairo_region_union (to->surface_damage, from->surface_damage); cairo_region_union (to->buffer_damage, from->buffer_damage); cairo_region_destroy (from->surface_damage); cairo_region_destroy (from->buffer_damage); if (from->input_region_set) { if (to->input_region) cairo_region_union (to->input_region, from->input_region); else to->input_region = cairo_region_reference (from->input_region); to->input_region_set = TRUE; cairo_region_destroy (from->input_region); } if (from->opaque_region_set) { if (to->opaque_region) cairo_region_union (to->opaque_region, from->opaque_region); else to->opaque_region = cairo_region_reference (from->opaque_region); to->opaque_region_set = TRUE; cairo_region_destroy (from->opaque_region); } if (from->has_new_geometry) { to->new_geometry = from->new_geometry; to->has_new_geometry = TRUE; } if (from->has_acked_configure_serial) { to->acked_configure_serial = from->acked_configure_serial; to->has_acked_configure_serial = TRUE; } if (from->has_new_min_size) { to->new_min_width = from->new_min_width; to->new_min_height = from->new_min_height; to->has_new_min_size = TRUE; } if (from->has_new_max_size) { to->new_max_width = from->new_max_width; to->new_max_height = from->new_max_height; to->has_new_max_size = TRUE; } if (from->scale > 0) to->scale = from->scale; if (from->has_new_buffer_transform) { to->buffer_transform = from->buffer_transform; to->has_new_buffer_transform = TRUE; } if (from->has_new_viewport_src_rect) { to->viewport_src_rect.origin.x = from->viewport_src_rect.origin.x; to->viewport_src_rect.origin.y = from->viewport_src_rect.origin.y; to->viewport_src_rect.size.width = from->viewport_src_rect.size.width; to->viewport_src_rect.size.height = from->viewport_src_rect.size.height; to->has_new_viewport_src_rect = TRUE; } if (from->has_new_viewport_dst_size) { to->viewport_dst_width = from->viewport_dst_width; to->viewport_dst_height = from->viewport_dst_height; to->has_new_viewport_dst_size = TRUE; } if (to->buffer && to->buffer_destroy_handler_id == 0) { to->buffer_destroy_handler_id = g_signal_connect (to->buffer, "resource-destroyed", G_CALLBACK (pending_buffer_resource_destroyed), to); } if (from->subsurface_placement_ops != NULL) { if (to->subsurface_placement_ops != NULL) { to->subsurface_placement_ops = g_slist_concat (to->subsurface_placement_ops, from->subsurface_placement_ops); } else { to->subsurface_placement_ops = from->subsurface_placement_ops; } from->subsurface_placement_ops = NULL; } meta_wayland_surface_state_set_default (from); } static void meta_wayland_surface_state_finalize (GObject *object) { MetaWaylandSurfaceState *state = META_WAYLAND_SURFACE_STATE (object); meta_wayland_surface_state_clear (state); G_OBJECT_CLASS (meta_wayland_surface_state_parent_class)->finalize (object); } static void meta_wayland_surface_state_init (MetaWaylandSurfaceState *state) { meta_wayland_surface_state_set_default (state); } static void meta_wayland_surface_state_class_init (MetaWaylandSurfaceStateClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_surface_state_finalize; surface_state_signals[SURFACE_STATE_SIGNAL_APPLIED] = g_signal_new ("applied", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_wayland_surface_apply_state (MetaWaylandSurface *surface, MetaWaylandSurfaceState *state) { MetaWaylandSurface *subsurface_surface; gboolean had_damage = FALSE; g_signal_emit (surface, surface_signals[SURFACE_PRE_STATE_APPLIED], 0); if (surface->role) { meta_wayland_surface_role_pre_apply_state (surface->role, state); } else { if (state->newly_attached && surface->unassigned.buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&surface->unassigned.buffer); } } if (state->newly_attached) { /* Always release any previously held buffer. If the buffer held is same * as the newly attached buffer, we still need to release it here, because * wl_surface.attach+commit and wl_buffer.release on the attached buffer * is symmetric. */ if (surface->buffer_held) meta_wayland_surface_unref_buffer_use_count (surface); g_set_object (&surface->buffer_ref.buffer, state->buffer); if (state->buffer) meta_wayland_surface_ref_buffer_use_count (surface); if (state->buffer) { GError *error = NULL; if (!meta_wayland_buffer_attach (state->buffer, &surface->texture, &error)) { g_warning ("Could not import pending buffer: %s", error->message); wl_resource_post_error (surface->resource, WL_DISPLAY_ERROR_NO_MEMORY, "Failed to attach buffer to surface %i: %s", wl_resource_get_id (surface->resource), error->message); g_error_free (error); goto cleanup; } } else { cogl_clear_object (&surface->texture); } /* If the newly attached buffer is going to be accessed directly without * making a copy, such as an EGL buffer, mark it as in-use don't release * it until is replaced by a subsequent wl_surface.commit or when the * wl_surface is destroyed. */ surface->buffer_held = (state->buffer && !wl_shm_buffer_get (state->buffer->resource)); } if (state->scale > 0) surface->scale = state->scale; if (state->has_new_buffer_transform) surface->buffer_transform = state->buffer_transform; if (state->has_new_viewport_src_rect) { surface->viewport.src_rect.origin.x = state->viewport_src_rect.origin.x; surface->viewport.src_rect.origin.y = state->viewport_src_rect.origin.y; surface->viewport.src_rect.size.width = state->viewport_src_rect.size.width; surface->viewport.src_rect.size.height = state->viewport_src_rect.size.height; surface->viewport.has_src_rect = surface->viewport.src_rect.size.width > 0; } if (state->has_new_viewport_dst_size) { surface->viewport.dst_width = state->viewport_dst_width; surface->viewport.dst_height = state->viewport_dst_height; surface->viewport.has_dst_size = surface->viewport.dst_width > 0; } if (!cairo_region_is_empty (state->surface_damage) || !cairo_region_is_empty (state->buffer_damage)) { surface_process_damage (surface, state->surface_damage, state->buffer_damage); had_damage = TRUE; } surface->offset_x += state->dx; surface->offset_y += state->dy; if (state->opaque_region_set) { if (surface->opaque_region) cairo_region_destroy (surface->opaque_region); if (state->opaque_region) surface->opaque_region = cairo_region_reference (state->opaque_region); else surface->opaque_region = NULL; } if (state->input_region_set) { if (surface->input_region) cairo_region_destroy (surface->input_region); if (state->input_region) surface->input_region = cairo_region_reference (state->input_region); else surface->input_region = NULL; } if (surface->role) { meta_wayland_surface_role_apply_state (surface->role, state); g_assert (wl_list_empty (&state->frame_callback_list)); } else { wl_list_insert_list (surface->unassigned.pending_frame_callback_list.prev, &state->frame_callback_list); wl_list_init (&state->frame_callback_list); if (state->newly_attached) { /* The need to keep the wl_buffer from being released depends on what * role the surface is given. That means we need to also keep a use * count for wl_buffer's that are used by unassigned wl_surface's. */ g_set_object (&surface->unassigned.buffer, surface->buffer_ref.buffer); if (surface->unassigned.buffer) meta_wayland_surface_ref_buffer_use_count (surface); } } if (state->subsurface_placement_ops) { GSList *l; for (l = state->subsurface_placement_ops; l; l = l->next) { MetaWaylandSubsurfacePlacementOp *op = l->data; GNode *sibling_node; if (!op->surface || !op->sibling) continue; if (op->sibling == surface) sibling_node = surface->subsurface_leaf_node; else sibling_node = op->sibling->subsurface_branch_node; g_node_unlink (op->surface->subsurface_branch_node); switch (op->placement) { case META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE: g_node_insert_after (surface->subsurface_branch_node, sibling_node, op->surface->subsurface_branch_node); break; case META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW: g_node_insert_before (surface->subsurface_branch_node, sibling_node, op->surface->subsurface_branch_node); break; } } meta_wayland_surface_notify_subsurface_state_changed (surface); } cleanup: /* If we have a buffer that we are not using, decrease the use count so it may * be released if no-one else has a use-reference to it. */ if (state->newly_attached && !surface->buffer_held && surface->buffer_ref.buffer) meta_wayland_surface_unref_buffer_use_count (surface); g_signal_emit (state, surface_state_signals[SURFACE_STATE_SIGNAL_APPLIED], 0); META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) { MetaWaylandSubsurface *subsurface; subsurface = META_WAYLAND_SUBSURFACE (subsurface_surface->role); meta_wayland_subsurface_parent_state_applied (subsurface); } if (had_damage) { MetaWindow *toplevel_window; toplevel_window = meta_wayland_surface_get_toplevel_window (surface); if (toplevel_window) { MetaWindowActor *toplevel_window_actor; toplevel_window_actor = meta_window_actor_from_window (toplevel_window); if (toplevel_window_actor) meta_window_actor_notify_damaged (toplevel_window_actor); } } if (surface->role) meta_wayland_surface_role_post_apply_state (surface->role, state); meta_wayland_surface_state_reset (state); } void meta_wayland_surface_apply_cached_state (MetaWaylandSurface *surface) { if (!surface->cached_state) return; meta_wayland_surface_apply_state (surface, surface->cached_state); } MetaWaylandSurfaceState * meta_wayland_surface_get_pending_state (MetaWaylandSurface *surface) { return surface->pending_state; } MetaWaylandSurfaceState * meta_wayland_surface_ensure_cached_state (MetaWaylandSurface *surface) { if (!surface->cached_state) surface->cached_state = g_object_new (META_TYPE_WAYLAND_SURFACE_STATE, NULL); return surface->cached_state; } static void meta_wayland_surface_commit (MetaWaylandSurface *surface) { MetaWaylandSurfaceState *pending = surface->pending_state; COGL_TRACE_BEGIN_SCOPED (MetaWaylandSurfaceCommit, "WaylandSurface (commit)"); if (pending->buffer && !meta_wayland_buffer_is_realized (pending->buffer)) meta_wayland_buffer_realize (pending->buffer); /* * If this is a sub-surface and it is in effective synchronous mode, only * cache the pending surface state until either one of the following two * scenarios happens: * 1) Its parent surface gets its state applied. * 2) Its mode changes from synchronized to desynchronized and its parent * surface is in effective desynchronized mode. */ if (meta_wayland_surface_should_cache_state (surface)) { MetaWaylandSurfaceState *cached_state; cached_state = meta_wayland_surface_ensure_cached_state (surface); meta_wayland_surface_state_merge_into (pending, cached_state); } else { meta_wayland_surface_apply_state (surface, surface->pending_state); } } static void wl_surface_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void wl_surface_attach (struct wl_client *client, struct wl_resource *surface_resource, struct wl_resource *buffer_resource, gint32 dx, gint32 dy) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; MetaWaylandBuffer *buffer; /* X11 unmanaged window */ if (!surface) return; if (buffer_resource) buffer = meta_wayland_buffer_from_resource (buffer_resource); else buffer = NULL; if (surface->pending_state->buffer) { g_clear_signal_handler (&pending->buffer_destroy_handler_id, pending->buffer); } pending->newly_attached = TRUE; pending->buffer = buffer; pending->dx = dx; pending->dy = dy; if (buffer) { pending->buffer_destroy_handler_id = g_signal_connect (buffer, "resource-destroyed", G_CALLBACK (pending_buffer_resource_destroyed), pending); } } static void wl_surface_damage (struct wl_client *client, struct wl_resource *surface_resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; cairo_rectangle_int_t rectangle; /* X11 unmanaged window */ if (!surface) return; rectangle = (cairo_rectangle_int_t) { .x = x, .y = y, .width = width, .height = height }; cairo_region_union_rectangle (pending->surface_damage, &rectangle); } static void destroy_frame_callback (struct wl_resource *callback_resource) { MetaWaylandFrameCallback *callback = wl_resource_get_user_data (callback_resource); wl_list_remove (&callback->link); g_slice_free (MetaWaylandFrameCallback, callback); } static void wl_surface_frame (struct wl_client *client, struct wl_resource *surface_resource, guint32 callback_id) { MetaWaylandFrameCallback *callback; MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; /* X11 unmanaged window */ if (!surface) return; callback = g_slice_new0 (MetaWaylandFrameCallback); callback->surface = surface; callback->resource = wl_resource_create (client, &wl_callback_interface, META_WL_CALLBACK_VERSION, callback_id); wl_resource_set_implementation (callback->resource, NULL, callback, destroy_frame_callback); wl_list_insert (pending->frame_callback_list.prev, &callback->link); } static void wl_surface_set_opaque_region (struct wl_client *client, struct wl_resource *surface_resource, struct wl_resource *region_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; /* X11 unmanaged window */ if (!surface) return; g_clear_pointer (&pending->opaque_region, cairo_region_destroy); if (region_resource) { MetaWaylandRegion *region = wl_resource_get_user_data (region_resource); cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region); pending->opaque_region = cairo_region_copy (cr_region); } pending->opaque_region_set = TRUE; } static void wl_surface_set_input_region (struct wl_client *client, struct wl_resource *surface_resource, struct wl_resource *region_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; /* X11 unmanaged window */ if (!surface) return; g_clear_pointer (&pending->input_region, cairo_region_destroy); if (region_resource) { MetaWaylandRegion *region = wl_resource_get_user_data (region_resource); cairo_region_t *cr_region = meta_wayland_region_peek_cairo_region (region); pending->input_region = cairo_region_copy (cr_region); } pending->input_region_set = TRUE; } static void wl_surface_commit (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); /* X11 unmanaged window */ if (!surface) return; meta_wayland_surface_commit (surface); } static MetaMonitorTransform transform_from_wl_output_transform (int32_t transform_value) { enum wl_output_transform transform = transform_value; switch (transform) { case WL_OUTPUT_TRANSFORM_NORMAL: return META_MONITOR_TRANSFORM_NORMAL; case WL_OUTPUT_TRANSFORM_90: return META_MONITOR_TRANSFORM_90; case WL_OUTPUT_TRANSFORM_180: return META_MONITOR_TRANSFORM_180; case WL_OUTPUT_TRANSFORM_270: return META_MONITOR_TRANSFORM_270; case WL_OUTPUT_TRANSFORM_FLIPPED: return META_MONITOR_TRANSFORM_FLIPPED; case WL_OUTPUT_TRANSFORM_FLIPPED_90: return META_MONITOR_TRANSFORM_FLIPPED_90; case WL_OUTPUT_TRANSFORM_FLIPPED_180: return META_MONITOR_TRANSFORM_FLIPPED_180; case WL_OUTPUT_TRANSFORM_FLIPPED_270: return META_MONITOR_TRANSFORM_FLIPPED_270; default: return -1; } } static void wl_surface_set_buffer_transform (struct wl_client *client, struct wl_resource *resource, int32_t transform) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurfaceState *pending = surface->pending_state; MetaMonitorTransform buffer_transform; buffer_transform = transform_from_wl_output_transform (transform); if (buffer_transform == -1) { wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_TRANSFORM, "Trying to set invalid buffer_transform of %d\n", transform); return; } pending->buffer_transform = buffer_transform; pending->has_new_buffer_transform = TRUE; } static void wl_surface_set_buffer_scale (struct wl_client *client, struct wl_resource *resource, int scale) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurfaceState *pending = surface->pending_state; if (scale <= 0) { wl_resource_post_error (resource, WL_SURFACE_ERROR_INVALID_SCALE, "Trying to set invalid buffer_scale of %d\n", scale); return; } pending->scale = scale; } static void wl_surface_damage_buffer (struct wl_client *client, struct wl_resource *surface_resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurfaceState *pending = surface->pending_state; cairo_rectangle_int_t rectangle; /* X11 unmanaged window */ if (!surface) return; rectangle = (cairo_rectangle_int_t) { .x = x, .y = y, .width = width, .height = height }; cairo_region_union_rectangle (pending->buffer_damage, &rectangle); } static const struct wl_surface_interface meta_wayland_wl_surface_interface = { wl_surface_destroy, wl_surface_attach, wl_surface_damage, wl_surface_frame, wl_surface_set_opaque_region, wl_surface_set_input_region, wl_surface_commit, wl_surface_set_buffer_transform, wl_surface_set_buffer_scale, wl_surface_damage_buffer, }; static void surface_entered_output (MetaWaylandSurface *surface, MetaWaylandOutput *wayland_output) { GList *iter; struct wl_resource *resource; for (iter = wayland_output->resources; iter != NULL; iter = iter->next) { resource = iter->data; if (wl_resource_get_client (resource) != wl_resource_get_client (surface->resource)) continue; wl_surface_send_enter (surface->resource, resource); } } static void surface_left_output (MetaWaylandSurface *surface, MetaWaylandOutput *wayland_output) { GList *iter; struct wl_resource *resource; for (iter = wayland_output->resources; iter != NULL; iter = iter->next) { resource = iter->data; if (wl_resource_get_client (resource) != wl_resource_get_client (surface->resource)) continue; wl_surface_send_leave (surface->resource, resource); } } static void set_surface_is_on_output (MetaWaylandSurface *surface, MetaWaylandOutput *wayland_output, gboolean is_on_output); static void surface_handle_output_destroy (MetaWaylandOutput *wayland_output, MetaWaylandSurface *surface) { set_surface_is_on_output (surface, wayland_output, FALSE); } static void set_surface_is_on_output (MetaWaylandSurface *surface, MetaWaylandOutput *wayland_output, gboolean is_on_output) { gpointer orig_id; gboolean was_on_output = g_hash_table_lookup_extended (surface->outputs_to_destroy_notify_id, wayland_output, NULL, &orig_id); if (!was_on_output && is_on_output) { gulong id; id = g_signal_connect (wayland_output, "output-destroyed", G_CALLBACK (surface_handle_output_destroy), surface); g_hash_table_insert (surface->outputs_to_destroy_notify_id, wayland_output, GSIZE_TO_POINTER ((gsize)id)); surface_entered_output (surface, wayland_output); } else if (was_on_output && !is_on_output) { g_hash_table_remove (surface->outputs_to_destroy_notify_id, wayland_output); g_signal_handler_disconnect (wayland_output, (gulong) GPOINTER_TO_SIZE (orig_id)); surface_left_output (surface, wayland_output); } } static void update_surface_output_state (gpointer key, gpointer value, gpointer user_data) { MetaWaylandOutput *wayland_output = value; MetaWaylandSurface *surface = user_data; MetaLogicalMonitor *logical_monitor; gboolean is_on_logical_monitor; g_assert (surface->role); logical_monitor = wayland_output->logical_monitor; if (!logical_monitor) { set_surface_is_on_output (surface, wayland_output, FALSE); return; } is_on_logical_monitor = meta_wayland_surface_role_is_on_logical_monitor (surface->role, logical_monitor); set_surface_is_on_output (surface, wayland_output, is_on_logical_monitor); } static void surface_output_disconnect_signal (gpointer key, gpointer value, gpointer user_data) { g_signal_handler_disconnect (key, (gulong) GPOINTER_TO_SIZE (value)); } void meta_wayland_surface_update_outputs (MetaWaylandSurface *surface) { if (!surface->compositor) return; g_hash_table_foreach (surface->compositor->outputs, update_surface_output_state, surface); } void meta_wayland_surface_update_outputs_recursively (MetaWaylandSurface *surface) { MetaWaylandSurface *subsurface_surface; meta_wayland_surface_update_outputs (surface); META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) meta_wayland_surface_update_outputs_recursively (subsurface_surface); } void meta_wayland_surface_notify_unmapped (MetaWaylandSurface *surface) { g_signal_emit (surface, surface_signals[SURFACE_UNMAPPED], 0); } static void unlink_note (GNode *node, gpointer data) { g_node_unlink (node); } static void wl_surface_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandCompositor *compositor = surface->compositor; MetaWaylandFrameCallback *cb, *next; g_signal_emit (surface, surface_signals[SURFACE_DESTROY], 0); g_clear_object (&surface->role); if (surface->unassigned.buffer) { meta_wayland_surface_unref_buffer_use_count (surface); g_clear_object (&surface->unassigned.buffer); } if (surface->buffer_held) meta_wayland_surface_unref_buffer_use_count (surface); g_clear_pointer (&surface->texture, cogl_object_unref); g_clear_object (&surface->buffer_ref.buffer); g_clear_object (&surface->cached_state); g_clear_object (&surface->pending_state); if (surface->opaque_region) cairo_region_destroy (surface->opaque_region); if (surface->input_region) cairo_region_destroy (surface->input_region); meta_wayland_compositor_remove_frame_callback_surface (compositor, surface); g_hash_table_foreach (surface->outputs_to_destroy_notify_id, surface_output_disconnect_signal, surface); g_hash_table_unref (surface->outputs_to_destroy_notify_id); wl_list_for_each_safe (cb, next, &surface->unassigned.pending_frame_callback_list, link) wl_resource_destroy (cb->resource); if (surface->resource) wl_resource_set_user_data (surface->resource, NULL); if (surface->wl_subsurface) wl_resource_destroy (surface->wl_subsurface); if (surface->subsurface_branch_node) { g_node_children_foreach (surface->subsurface_branch_node, G_TRAVERSE_NON_LEAVES, unlink_note, NULL); g_clear_pointer (&surface->subsurface_branch_node, g_node_destroy); } g_hash_table_destroy (surface->shortcut_inhibited_seats); g_object_unref (surface); meta_wayland_compositor_repick (compositor); } MetaWaylandSurface * meta_wayland_surface_create (MetaWaylandCompositor *compositor, struct wl_client *client, struct wl_resource *compositor_resource, guint32 id) { MetaWaylandSurface *surface = g_object_new (META_TYPE_WAYLAND_SURFACE, NULL); int surface_version; surface->compositor = compositor; surface->scale = 1; surface_version = wl_resource_get_version (compositor_resource); surface->resource = wl_resource_create (client, &wl_surface_interface, surface_version, id); wl_resource_set_implementation (surface->resource, &meta_wayland_wl_surface_interface, surface, wl_surface_destructor); wl_list_init (&surface->unassigned.pending_frame_callback_list); surface->outputs_to_destroy_notify_id = g_hash_table_new (NULL, NULL); surface->shortcut_inhibited_seats = g_hash_table_new (NULL, NULL); meta_wayland_compositor_notify_surface_id (compositor, id, surface); return surface; } gboolean meta_wayland_surface_begin_grab_op (MetaWaylandSurface *surface, MetaWaylandSeat *seat, MetaGrabOp grab_op, gfloat x, gfloat y) { MetaWindow *window = meta_wayland_surface_get_window (surface); if (grab_op == META_GRAB_OP_NONE) return FALSE; /* This is an input driven operation so we set frame_action to constrain it in the same way as it would be if the window was being moved/resized via a SSD event. */ return meta_display_begin_grab_op (window->display, window, grab_op, TRUE, /* pointer_already_grabbed */ TRUE, /* frame_action */ 1, /* button. XXX? */ 0, /* modmask */ meta_display_get_current_time_roundtrip (window->display), x, y); } /** * meta_wayland_shell_init: * @compositor: The #MetaWaylandCompositor object * * Initializes the Wayland interfaces providing features that deal with * desktop-specific conundrums, like XDG shell, wl_shell (deprecated), etc. */ void meta_wayland_shell_init (MetaWaylandCompositor *compositor) { meta_wayland_xdg_shell_init (compositor); meta_wayland_legacy_xdg_shell_init (compositor); meta_wayland_wl_shell_init (compositor); meta_wayland_init_gtk_shell (compositor); meta_wayland_init_viewporter (compositor); } void meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface->role); g_signal_emit (surface, surface_signals[SURFACE_CONFIGURE], 0); meta_wayland_shell_surface_configure (shell_surface, configuration); } void meta_wayland_surface_ping (MetaWaylandSurface *surface, guint32 serial) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface->role); meta_wayland_shell_surface_ping (shell_surface, serial); } void meta_wayland_surface_delete (MetaWaylandSurface *surface) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface->role); meta_wayland_shell_surface_close (shell_surface); } void meta_wayland_surface_window_managed (MetaWaylandSurface *surface, MetaWindow *window) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface->role); meta_wayland_shell_surface_managed (shell_surface, window); } void meta_wayland_surface_drag_dest_focus_in (MetaWaylandSurface *surface, MetaWaylandDataOffer *offer) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataDevice *data_device = &compositor->seat->data_device; surface->dnd.funcs->focus_in (data_device, surface, offer); } void meta_wayland_surface_drag_dest_motion (MetaWaylandSurface *surface, const ClutterEvent *event) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataDevice *data_device = &compositor->seat->data_device; surface->dnd.funcs->motion (data_device, surface, event); } void meta_wayland_surface_drag_dest_focus_out (MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataDevice *data_device = &compositor->seat->data_device; surface->dnd.funcs->focus_out (data_device, surface); } void meta_wayland_surface_drag_dest_drop (MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataDevice *data_device = &compositor->seat->data_device; surface->dnd.funcs->drop (data_device, surface); } void meta_wayland_surface_drag_dest_update (MetaWaylandSurface *surface) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandDataDevice *data_device = &compositor->seat->data_device; surface->dnd.funcs->update (data_device, surface); } MetaWaylandSurface * meta_wayland_surface_get_toplevel (MetaWaylandSurface *surface) { if (surface->role) return meta_wayland_surface_role_get_toplevel (surface->role); else return NULL; } MetaWindow * meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface) { MetaWaylandSurface *toplevel; toplevel = meta_wayland_surface_get_toplevel (surface); if (toplevel) return meta_wayland_surface_get_window (toplevel); else return NULL; } void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, float abs_x, float abs_y, float *sx, float *sy) { if (surface != NULL && surface->role) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface->role); surface_role_class->get_relative_coordinates (surface->role, abs_x, abs_y, sx, sy); } } void meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface, float sx, float sy, float *x, float *y) { ClutterActor *actor = CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface)); graphene_point3d_t sv = { .x = sx, .y = sy, }; graphene_point3d_t v = { 0 }; clutter_actor_apply_relative_transform_to_point (actor, NULL, &sv, &v); *x = v.x; *y = v.y; } static void meta_wayland_surface_init (MetaWaylandSurface *surface) { surface->pending_state = g_object_new (META_TYPE_WAYLAND_SURFACE_STATE, NULL); surface->subsurface_branch_node = g_node_new (surface); surface->subsurface_leaf_node = g_node_prepend_data (surface->subsurface_branch_node, surface); g_signal_connect (surface, "geometry-changed", G_CALLBACK (meta_wayland_surface_update_outputs_recursively), NULL); } static void meta_wayland_surface_class_init (MetaWaylandSurfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); surface_signals[SURFACE_DESTROY] = g_signal_new ("destroy", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_UNMAPPED] = g_signal_new ("unmapped", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_CONFIGURE] = g_signal_new ("configure", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_SHORTCUTS_INHIBITED] = g_signal_new ("shortcuts-inhibited", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_SHORTCUTS_RESTORED] = g_signal_new ("shortcuts-restored", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_GEOMETRY_CHANGED] = g_signal_new ("geometry-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); surface_signals[SURFACE_PRE_STATE_APPLIED] = g_signal_new ("pre-state-applied", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); } static void meta_wayland_surface_role_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (object); MetaWaylandSurfaceRolePrivate *priv = meta_wayland_surface_role_get_instance_private (surface_role); switch (prop_id) { case SURFACE_ROLE_PROP_SURFACE: priv->surface = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_surface_role_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (object); MetaWaylandSurfaceRolePrivate *priv = meta_wayland_surface_role_get_instance_private (surface_role); switch (prop_id) { case SURFACE_ROLE_PROP_SURFACE: g_value_set_object (value, priv->surface); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_wayland_surface_role_init (MetaWaylandSurfaceRole *role) { } static void meta_wayland_surface_role_class_init (MetaWaylandSurfaceRoleClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_wayland_surface_role_set_property; object_class->get_property = meta_wayland_surface_role_get_property; g_object_class_install_property (object_class, SURFACE_ROLE_PROP_SURFACE, g_param_spec_object ("surface", "MetaWaylandSurface", "The MetaWaylandSurface instance", META_TYPE_WAYLAND_SURFACE, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); } static void meta_wayland_surface_role_assigned (MetaWaylandSurfaceRole *surface_role) { META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role)->assigned (surface_role); } static void meta_wayland_surface_role_pre_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->pre_apply_state) klass->pre_apply_state (surface_role, pending); } static void meta_wayland_surface_role_post_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->post_apply_state) klass->post_apply_state (surface_role, pending); } static void meta_wayland_surface_role_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role)->apply_state (surface_role, pending); } static gboolean meta_wayland_surface_role_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->is_on_logical_monitor) return klass->is_on_logical_monitor (surface_role, logical_monitor); else return FALSE; } static MetaWaylandSurface * meta_wayland_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->get_toplevel) return klass->get_toplevel (surface_role); else return NULL; } static MetaWindow * meta_wayland_surface_role_get_window (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->get_window) return klass->get_window (surface_role); else return NULL; } MetaWindow * meta_wayland_surface_get_window (MetaWaylandSurface *surface) { if (!surface->role) return NULL; return meta_wayland_surface_role_get_window (surface->role); } static gboolean meta_wayland_surface_role_should_cache_state (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); if (klass->should_cache_state) return klass->should_cache_state (surface_role); else return FALSE; } gboolean meta_wayland_surface_should_cache_state (MetaWaylandSurface *surface) { if (!surface->role) return FALSE; return meta_wayland_surface_role_should_cache_state (surface->role); } static void meta_wayland_surface_role_notify_subsurface_state_changed (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurfaceRoleClass *klass; klass = META_WAYLAND_SURFACE_ROLE_GET_CLASS (surface_role); g_return_if_fail (klass->notify_subsurface_state_changed); klass->notify_subsurface_state_changed (surface_role); } void meta_wayland_surface_notify_subsurface_state_changed (MetaWaylandSurface *surface) { if (surface->role) meta_wayland_surface_role_notify_subsurface_state_changed (surface->role); } MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role) { MetaWaylandSurfaceRolePrivate *priv = meta_wayland_surface_role_get_instance_private (role); return priv->surface; } cairo_region_t * meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface) { cairo_region_t *region; cairo_rectangle_int_t buffer_rect; if (!surface->buffer_ref.buffer) return NULL; buffer_rect = (cairo_rectangle_int_t) { .width = meta_wayland_surface_get_width (surface), .height = meta_wayland_surface_get_height (surface), }; region = cairo_region_create_rectangle (&buffer_rect); if (surface->input_region) cairo_region_intersect (region, surface->input_region); return region; } void meta_wayland_surface_inhibit_shortcuts (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { g_hash_table_add (surface->shortcut_inhibited_seats, seat); g_signal_emit (surface, surface_signals[SURFACE_SHORTCUTS_INHIBITED], 0); } void meta_wayland_surface_restore_shortcuts (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { g_signal_emit (surface, surface_signals[SURFACE_SHORTCUTS_RESTORED], 0); g_hash_table_remove (surface->shortcut_inhibited_seats, seat); } gboolean meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { if (surface->shortcut_inhibited_seats == NULL) return FALSE; return g_hash_table_contains (surface->shortcut_inhibited_seats, seat); } CoglTexture * meta_wayland_surface_get_texture (MetaWaylandSurface *surface) { return surface->texture; } MetaSurfaceActor * meta_wayland_surface_get_actor (MetaWaylandSurface *surface) { if (!surface->role || !META_IS_WAYLAND_ACTOR_SURFACE (surface->role)) return NULL; return meta_wayland_actor_surface_get_actor (META_WAYLAND_ACTOR_SURFACE (surface->role)); } void meta_wayland_surface_notify_geometry_changed (MetaWaylandSurface *surface) { g_signal_emit (surface, surface_signals[SURFACE_GEOMETRY_CHANGED], 0); } int meta_wayland_surface_get_width (MetaWaylandSurface *surface) { if (surface->viewport.has_dst_size) { return surface->viewport.dst_width; } else if (surface->viewport.has_src_rect) { return ceilf (surface->viewport.src_rect.size.width); } else { int width; if (meta_monitor_transform_is_rotated (surface->buffer_transform)) width = get_buffer_height (surface); else width = get_buffer_width (surface); return width / surface->scale; } } int meta_wayland_surface_get_height (MetaWaylandSurface *surface) { if (surface->viewport.has_dst_size) { return surface->viewport.dst_height; } else if (surface->viewport.has_src_rect) { return ceilf (surface->viewport.src_rect.size.height); } else { int height; if (meta_monitor_transform_is_rotated (surface->buffer_transform)) height = get_buffer_width (surface); else height = get_buffer_height (surface); return height / surface->scale; } } muffin-6.4.1/src/wayland/meta-wayland-surface.h0000664000175000017500000003433114723361714020356 0ustar fabiofabio/* * Copyright (C) 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_SURFACE_H #define META_WAYLAND_SURFACE_H #include #include #include #include #include "backends/meta-monitor-manager-private.h" #include "clutter/clutter.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-surface-actor.h" #include "meta/meta-cursor-tracker.h" #include "wayland/meta-wayland-pointer-constraints.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_SURFACE (meta_wayland_surface_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurface, meta_wayland_surface, META, WAYLAND_SURFACE, GObject); #define META_TYPE_WAYLAND_SURFACE_ROLE (meta_wayland_surface_role_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandSurfaceRole, meta_wayland_surface_role, META, WAYLAND_SURFACE_ROLE, GObject); #define META_TYPE_WAYLAND_SURFACE_STATE (meta_wayland_surface_state_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandSurfaceState, meta_wayland_surface_state, META, WAYLAND_SURFACE_STATE, GObject) struct _MetaWaylandSurfaceRoleClass { GObjectClass parent_class; void (*assigned) (MetaWaylandSurfaceRole *surface_role); void (*pre_apply_state) (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); void (*apply_state) (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); void (*post_apply_state) (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending); gboolean (*is_on_logical_monitor) (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor); MetaWaylandSurface * (*get_toplevel) (MetaWaylandSurfaceRole *surface_role); gboolean (*should_cache_state) (MetaWaylandSurfaceRole *surface_role); void (*notify_subsurface_state_changed) (MetaWaylandSurfaceRole *surface_role); void (*get_relative_coordinates) (MetaWaylandSurfaceRole *surface_role, float abs_x, float abs_y, float *out_sx, float *out_sy); MetaWindow * (*get_window) (MetaWaylandSurfaceRole *surface_role); }; struct _MetaWaylandSurfaceState { GObject parent; /* wl_surface.attach */ gboolean newly_attached; MetaWaylandBuffer *buffer; gulong buffer_destroy_handler_id; int32_t dx; int32_t dy; int scale; /* wl_surface.damage */ cairo_region_t *surface_damage; /* wl_surface.damage_buffer */ cairo_region_t *buffer_damage; cairo_region_t *input_region; gboolean input_region_set; cairo_region_t *opaque_region; gboolean opaque_region_set; /* wl_surface.frame */ struct wl_list frame_callback_list; MetaRectangle new_geometry; gboolean has_new_geometry; gboolean has_acked_configure_serial; uint32_t acked_configure_serial; /* pending min/max size in window geometry coordinates */ gboolean has_new_min_size; int new_min_width; int new_min_height; gboolean has_new_max_size; int new_max_width; int new_max_height; gboolean has_new_buffer_transform; MetaMonitorTransform buffer_transform; gboolean has_new_viewport_src_rect; graphene_rect_t viewport_src_rect; gboolean has_new_viewport_dst_size; int viewport_dst_width; int viewport_dst_height; GSList *subsurface_placement_ops; }; struct _MetaWaylandDragDestFuncs { void (* focus_in) (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, MetaWaylandDataOffer *offer); void (* focus_out) (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface); void (* motion) (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface, const ClutterEvent *event); void (* drop) (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface); void (* update) (MetaWaylandDataDevice *data_device, MetaWaylandSurface *surface); }; struct _MetaWaylandSurface { GObject parent; /* Generic stuff */ struct wl_resource *resource; MetaWaylandCompositor *compositor; MetaWaylandSurfaceRole *role; cairo_region_t *input_region; cairo_region_t *opaque_region; int scale; int32_t offset_x, offset_y; GNode *subsurface_branch_node; GNode *subsurface_leaf_node; GHashTable *outputs_to_destroy_notify_id; MetaMonitorTransform buffer_transform; CoglTexture *texture; /* Buffer reference state. */ struct { MetaWaylandBuffer *buffer; unsigned int use_count; } buffer_ref; /* Buffer renderer state. */ gboolean buffer_held; /* Intermediate state for when no role has been assigned. */ struct { struct wl_list pending_frame_callback_list; MetaWaylandBuffer *buffer; } unassigned; struct { const MetaWaylandDragDestFuncs *funcs; } dnd; /* All the pending state that wl_surface.commit will apply. */ MetaWaylandSurfaceState *pending_state; /* State cached due to inter-surface synchronization such. */ MetaWaylandSurfaceState *cached_state; /* Extension resources. */ struct wl_resource *wl_subsurface; /* wl_subsurface stuff. */ struct { MetaWaylandSurface *parent; struct wl_listener parent_destroy_listener; int x; int y; /* When the surface is synchronous, its state will be applied * when the parent is committed. This is done by moving the * "real" pending state below to here when this surface is * committed and in synchronous mode. * * When the parent surface is committed, we apply the pending * state here. */ gboolean synchronous; int32_t pending_x; int32_t pending_y; gboolean pending_pos; } sub; /* wp_viewport */ struct { struct wl_resource *resource; gulong destroy_handler_id; gboolean has_src_rect; graphene_rect_t src_rect; gboolean has_dst_size; int dst_width; int dst_height; } viewport; /* table of seats for which shortcuts are inhibited */ GHashTable *shortcut_inhibited_seats; }; void meta_wayland_shell_init (MetaWaylandCompositor *compositor); MetaWaylandSurface *meta_wayland_surface_create (MetaWaylandCompositor *compositor, struct wl_client *client, struct wl_resource *compositor_resource, guint32 id); MetaWaylandSurfaceState * meta_wayland_surface_get_pending_state (MetaWaylandSurface *surface); MetaWaylandSurfaceState * meta_wayland_surface_ensure_cached_state (MetaWaylandSurface *surface); void meta_wayland_surface_apply_cached_state (MetaWaylandSurface *surface); gboolean meta_wayland_surface_is_effectively_synchronized (MetaWaylandSurface *surface); gboolean meta_wayland_surface_assign_role (MetaWaylandSurface *surface, GType role_type, const char *first_property_name, ...); MetaWaylandBuffer *meta_wayland_surface_get_buffer (MetaWaylandSurface *surface); void meta_wayland_surface_ref_buffer_use_count (MetaWaylandSurface *surface); void meta_wayland_surface_unref_buffer_use_count (MetaWaylandSurface *surface); void meta_wayland_surface_set_window (MetaWaylandSurface *surface, MetaWindow *window); void meta_wayland_surface_configure_notify (MetaWaylandSurface *surface, MetaWaylandWindowConfiguration *configuration); void meta_wayland_surface_ping (MetaWaylandSurface *surface, guint32 serial); void meta_wayland_surface_delete (MetaWaylandSurface *surface); /* Drag dest functions */ void meta_wayland_surface_drag_dest_focus_in (MetaWaylandSurface *surface, MetaWaylandDataOffer *offer); void meta_wayland_surface_drag_dest_motion (MetaWaylandSurface *surface, const ClutterEvent *event); void meta_wayland_surface_drag_dest_focus_out (MetaWaylandSurface *surface); void meta_wayland_surface_drag_dest_drop (MetaWaylandSurface *surface); void meta_wayland_surface_drag_dest_update (MetaWaylandSurface *surface); void meta_wayland_surface_update_outputs (MetaWaylandSurface *surface); MetaWaylandSurface *meta_wayland_surface_get_toplevel (MetaWaylandSurface *surface); MetaWindow * meta_wayland_surface_get_window (MetaWaylandSurface *surface); gboolean meta_wayland_surface_should_cache_state (MetaWaylandSurface *surface); MetaWindow * meta_wayland_surface_get_toplevel_window (MetaWaylandSurface *surface); void meta_wayland_surface_queue_pending_frame_callbacks (MetaWaylandSurface *surface); void meta_wayland_surface_queue_pending_state_frame_callbacks (MetaWaylandSurface *surface, MetaWaylandSurfaceState *pending); void meta_wayland_surface_get_relative_coordinates (MetaWaylandSurface *surface, float abs_x, float abs_y, float *sx, float *sy); void meta_wayland_surface_get_absolute_coordinates (MetaWaylandSurface *surface, float sx, float sy, float *x, float *y); MetaWaylandSurface * meta_wayland_surface_role_get_surface (MetaWaylandSurfaceRole *role); cairo_region_t * meta_wayland_surface_calculate_input_region (MetaWaylandSurface *surface); gboolean meta_wayland_surface_begin_grab_op (MetaWaylandSurface *surface, MetaWaylandSeat *seat, MetaGrabOp grab_op, gfloat x, gfloat y); void meta_wayland_surface_window_managed (MetaWaylandSurface *surface, MetaWindow *window); void meta_wayland_surface_inhibit_shortcuts (MetaWaylandSurface *surface, MetaWaylandSeat *seat); void meta_wayland_surface_restore_shortcuts (MetaWaylandSurface *surface, MetaWaylandSeat *seat); gboolean meta_wayland_surface_is_shortcuts_inhibited (MetaWaylandSurface *surface, MetaWaylandSeat *seat); CoglTexture * meta_wayland_surface_get_texture (MetaWaylandSurface *surface); MetaSurfaceActor * meta_wayland_surface_get_actor (MetaWaylandSurface *surface); void meta_wayland_surface_notify_geometry_changed (MetaWaylandSurface *surface); void meta_wayland_surface_notify_subsurface_state_changed (MetaWaylandSurface *surface); void meta_wayland_surface_notify_unmapped (MetaWaylandSurface *surface); void meta_wayland_surface_update_outputs_recursively (MetaWaylandSurface *surface); int meta_wayland_surface_get_width (MetaWaylandSurface *surface); int meta_wayland_surface_get_height (MetaWaylandSurface *surface); static inline GNode * meta_get_next_subsurface_sibling (GNode *n) { GNode *next; if (!n) return NULL; next = g_node_next_sibling (n); if (!next) return NULL; if (!G_NODE_IS_LEAF (next)) return next; else return meta_get_next_subsurface_sibling (next); } static inline GNode * meta_get_first_subsurface_node (MetaWaylandSurface *surface) { GNode *n; n = g_node_first_child (surface->subsurface_branch_node); if (!G_NODE_IS_LEAF (n)) return n; else return meta_get_next_subsurface_sibling (n); } #define META_WAYLAND_SURFACE_FOREACH_SUBSURFACE(surface, subsurface) \ for (GNode *G_PASTE(__n, __LINE__) = meta_get_first_subsurface_node ((surface)); \ (subsurface = (G_PASTE (__n, __LINE__) ? G_PASTE (__n, __LINE__)->data : NULL)); \ G_PASTE (__n, __LINE__) = meta_get_next_subsurface_sibling (G_PASTE (__n, __LINE__))) #endif muffin-6.4.1/src/wayland/meta-wayland-tablet-pad-group.h0000664000175000017500000000623414723361714022076 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_PAD_GROUP_H #define META_WAYLAND_TABLET_PAD_GROUP_H #include #include #include "clutter/clutter.h" #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletPadGroup { MetaWaylandTabletPad *pad; GArray *buttons; uint32_t n_modes; uint32_t current_mode; struct wl_list resource_list; struct wl_list focus_resource_list; uint32_t mode_switch_serial; GList *strips; GList *rings; }; MetaWaylandTabletPadGroup * meta_wayland_tablet_pad_group_new (MetaWaylandTabletPad *pad); void meta_wayland_tablet_pad_group_free (MetaWaylandTabletPadGroup *group); struct wl_resource * meta_wayland_tablet_pad_group_create_new_resource (MetaWaylandTabletPadGroup *group, struct wl_client *client, struct wl_resource *pad_resource, uint32_t id); struct wl_resource * meta_wayland_tablet_pad_group_lookup_resource (MetaWaylandTabletPadGroup *group, struct wl_client *client); void meta_wayland_tablet_pad_group_notify (MetaWaylandTabletPadGroup *group, struct wl_resource *resource); void meta_wayland_tablet_pad_group_update (MetaWaylandTabletPadGroup *group, const ClutterEvent *event); gboolean meta_wayland_tablet_pad_group_handle_event (MetaWaylandTabletPadGroup *group, const ClutterEvent *event); void meta_wayland_tablet_pad_group_sync_focus (MetaWaylandTabletPadGroup *group); gboolean meta_wayland_tablet_pad_group_has_button (MetaWaylandTabletPadGroup *group, guint button); gboolean meta_wayland_tablet_pad_group_is_mode_switch_button (MetaWaylandTabletPadGroup *group, guint button); #endif /* META_WAYLAND_TABLET_PAD_GROUP_H */ muffin-6.4.1/src/wayland/meta-wayland-pointer-gesture-swipe.h0000664000175000017500000000305014723361714023201 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_POINTER_GESTURE_SWIPE_H #define META_WAYLAND_POINTER_GESTURE_SWIPE_H #include #include #include "clutter/clutter.h" #include "wayland/meta-wayland-types.h" gboolean meta_wayland_pointer_gesture_swipe_handle_event (MetaWaylandPointer *pointer, const ClutterEvent *event); void meta_wayland_pointer_gesture_swipe_create_new_resource (MetaWaylandPointer *pointer, struct wl_client *client, struct wl_resource *pointer_resource, uint32_t id); #endif /* META_WAYLAND_POINTER_GESTURE_SWIPE_H */ muffin-6.4.1/src/wayland/meta-wayland.h0000664000175000017500000001104714723361714016727 0ustar fabiofabio/* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_H #define META_WAYLAND_H #include "clutter/clutter.h" #include "core/util-private.h" #include "meta/types.h" #include "wayland/meta-wayland-types.h" META_EXPORT_TEST void meta_wayland_override_display_name (const char *display_name); void meta_wayland_pre_clutter_init (void); void meta_wayland_init (void); void meta_wayland_finalize (void); MetaWaylandCompositor * meta_wayland_compositor_new (MetaBackend *backend); void meta_wayland_compositor_setup (MetaWaylandCompositor *compositor); META_EXPORT_TEST MetaWaylandCompositor *meta_wayland_compositor_get_default (void); void meta_wayland_compositor_update (MetaWaylandCompositor *compositor, const ClutterEvent *event); gboolean meta_wayland_compositor_handle_event (MetaWaylandCompositor *compositor, const ClutterEvent *event); void meta_wayland_compositor_update_key_state (MetaWaylandCompositor *compositor, char *key_vector, int key_vector_len, int offset); void meta_wayland_compositor_repick (MetaWaylandCompositor *compositor); void meta_wayland_compositor_set_input_focus (MetaWaylandCompositor *compositor, MetaWindow *window); void meta_wayland_compositor_paint_finished (MetaWaylandCompositor *compositor); void meta_wayland_compositor_add_frame_callback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface); void meta_wayland_compositor_remove_frame_callback_surface (MetaWaylandCompositor *compositor, MetaWaylandSurface *surface); META_EXPORT_TEST const char *meta_wayland_get_wayland_display_name (MetaWaylandCompositor *compositor); META_EXPORT_TEST const char *meta_wayland_get_xwayland_display_name (MetaWaylandCompositor *compositor); void meta_wayland_compositor_restore_shortcuts (MetaWaylandCompositor *compositor, ClutterInputDevice *source); gboolean meta_wayland_compositor_is_shortcuts_inhibited (MetaWaylandCompositor *compositor, ClutterInputDevice *source); void meta_wayland_compositor_flush_clients (MetaWaylandCompositor *compositor); void meta_wayland_compositor_schedule_surface_association (MetaWaylandCompositor *compositor, int id, MetaWindow *window); void meta_wayland_compositor_notify_surface_id (MetaWaylandCompositor *compositor, int id, MetaWaylandSurface *surface); #endif muffin-6.4.1/src/wayland/meta-window-wayland.h0000664000175000017500000000613714723361714020240 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WINDOW_WAYLAND_H #define META_WINDOW_WAYLAND_H #include "core/window-private.h" #include "meta/window.h" #include "wayland/meta-wayland-types.h" G_BEGIN_DECLS #define META_TYPE_WINDOW_WAYLAND (meta_window_wayland_get_type()) G_DECLARE_FINAL_TYPE (MetaWindowWayland, meta_window_wayland, META, WINDOW_WAYLAND, MetaWindow) MetaWindow * meta_window_wayland_new (MetaDisplay *display, MetaWaylandSurface *surface); void meta_window_wayland_finish_move_resize (MetaWindow *window, MetaRectangle new_geom, MetaWaylandSurfaceState *pending); int meta_window_wayland_get_geometry_scale (MetaWindow *window); void meta_window_wayland_place_relative_to (MetaWindow *window, MetaWindow *other, int x, int y); void meta_window_place_with_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule); void meta_window_update_placement_rule (MetaWindow *window, MetaPlacementRule *placement_rule); MetaWaylandWindowConfiguration * meta_window_wayland_peek_configuration (MetaWindowWayland *wl_window, uint32_t serial); void meta_window_wayland_set_min_size (MetaWindow *window, int width, int height); void meta_window_wayland_set_max_size (MetaWindow *window, int width, int height); void meta_window_wayland_get_min_size (MetaWindow *window, int *width, int *height); void meta_window_wayland_get_max_size (MetaWindow *window, int *width, int *height); #endif muffin-6.4.1/src/wayland/meta-wayland-data-source-primary-legacy.h0000664000175000017500000000351014723361714024053 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY_H #define META_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY_H #include "meta-wayland-data-source.h" #define META_TYPE_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY (meta_wayland_data_source_primary_legacy_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandDataSourcePrimaryLegacy, meta_wayland_data_source_primary_legacy, META, WAYLAND_DATA_SOURCE_PRIMARY_LEGACY, MetaWaylandDataSource); MetaWaylandDataSource * meta_wayland_data_source_primary_legacy_new (struct wl_resource *resource); #endif /* META_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY_H */ muffin-6.4.1/src/wayland/meta-wayland-data-device-primary-legacy.c0000664000175000017500000003001414723361714024004 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ /* The file is based on src/data-device.c from Weston */ #include "config.h" #include "wayland/meta-wayland-data-device-primary-legacy.h" #include "compositor/meta-dnd-actor-private.h" #include "meta/meta-selection-source-memory.h" #include "wayland/meta-selection-source-wayland-private.h" #include "wayland/meta-wayland-data-offer-primary-legacy.h" #include "wayland/meta-wayland-data-source-primary-legacy.h" #include "wayland/meta-wayland-dnd-surface.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "gtk-primary-selection-server-protocol.h" static struct wl_resource * create_and_send_primary_offer (MetaWaylandDataDevicePrimaryLegacy *data_device, struct wl_resource *target); static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void default_destructor (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void set_selection_source (MetaWaylandDataDevicePrimaryLegacy *data_device, MetaSelectionSource *selection_source) { MetaDisplay *display = meta_get_display (); meta_selection_set_owner (meta_display_get_selection (display), META_SELECTION_PRIMARY, selection_source); g_set_object (&data_device->owner, selection_source); } static void unset_selection_source (MetaWaylandDataDevicePrimaryLegacy *data_device) { MetaDisplay *display = meta_get_display (); if (!data_device->owner) return; meta_selection_unset_owner (meta_display_get_selection (display), META_SELECTION_PRIMARY, data_device->owner); g_clear_object (&data_device->owner); } static void primary_source_destroyed (gpointer data, GObject *object_was_here) { MetaWaylandDataDevicePrimaryLegacy *data_device = data; data_device->data_source = NULL; unset_selection_source (data_device); } static void meta_wayland_data_device_primary_legacy_set_selection (MetaWaylandDataDevicePrimaryLegacy *data_device, MetaWaylandDataSource *source, uint32_t serial) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_legacy_data_device); MetaSelectionSource *selection_source; g_assert (!source || META_IS_WAYLAND_DATA_SOURCE_PRIMARY_LEGACY (source)); if (data_device->data_source && data_device->serial - serial < UINT32_MAX / 2) return; if (data_device->data_source) { g_object_weak_unref (G_OBJECT (data_device->data_source), primary_source_destroyed, data_device); } data_device->data_source = source; data_device->serial = serial; if (source) { meta_wayland_data_source_set_seat (source, seat); g_object_weak_ref (G_OBJECT (source), primary_source_destroyed, data_device); selection_source = meta_selection_source_wayland_new (source); } else { selection_source = g_object_new (META_TYPE_SELECTION_SOURCE_MEMORY, NULL); } set_selection_source (data_device, selection_source); g_object_unref (selection_source); } static void primary_device_set_selection (struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial) { MetaWaylandDataDevicePrimaryLegacy *data_device = wl_resource_get_user_data (resource); MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_legacy_data_device); MetaWaylandDataSource *source = NULL; if (source_resource) source = wl_resource_get_user_data (source_resource); if (wl_resource_get_client (resource) != meta_wayland_keyboard_get_focus_client (seat->keyboard)) return; meta_wayland_data_device_primary_legacy_set_selection (data_device, source, serial); } static const struct gtk_primary_selection_device_interface primary_device_interface = { primary_device_set_selection, default_destructor, }; static void owner_changed_cb (MetaSelection *selection, MetaSelectionType selection_type, MetaSelectionSource *new_owner, MetaWaylandDataDevicePrimaryLegacy *data_device) { MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); MetaWaylandSeat *seat = compositor->seat; struct wl_resource *data_device_resource; struct wl_client *focus_client; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (!focus_client) return; if (selection_type == META_SELECTION_PRIMARY) { wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer = NULL; if (new_owner) { offer = create_and_send_primary_offer (data_device, data_device_resource); } gtk_primary_selection_device_send_selection (data_device_resource, offer); } } } static void ensure_owners_changed_handler_connected (MetaWaylandDataDevicePrimaryLegacy *data_device) { if (data_device->selection_owner_signal_id != 0) return; data_device->selection_owner_signal_id = g_signal_connect (meta_display_get_selection (meta_get_display ()), "owner-changed", G_CALLBACK (owner_changed_cb), data_device); } static void primary_device_manager_create_source (struct wl_client *client, struct wl_resource *manager_resource, guint32 id) { struct wl_resource *source_resource; source_resource = wl_resource_create (client, >k_primary_selection_source_interface, wl_resource_get_version (manager_resource), id); meta_wayland_data_source_primary_legacy_new (source_resource); } static void primary_device_manager_get_device (struct wl_client *client, struct wl_resource *manager_resource, guint32 id, struct wl_resource *seat_resource) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); struct wl_resource *cr; cr = wl_resource_create (client, >k_primary_selection_device_interface, wl_resource_get_version (manager_resource), id); wl_resource_set_implementation (cr, &primary_device_interface, &seat->primary_legacy_data_device, unbind_resource); wl_list_insert (&seat->primary_legacy_data_device.resource_list, wl_resource_get_link (cr)); ensure_owners_changed_handler_connected (&seat->primary_legacy_data_device); } static const struct gtk_primary_selection_device_manager_interface primary_manager_interface = { primary_device_manager_create_source, primary_device_manager_get_device, default_destructor, }; static void bind_primary_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, >k_primary_selection_device_manager_interface, version, id); wl_resource_set_implementation (resource, &primary_manager_interface, NULL, NULL); } void meta_wayland_data_device_primary_legacy_manager_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, >k_primary_selection_device_manager_interface, 1, NULL, bind_primary_manager) == NULL) g_error ("Could not create data_device"); } void meta_wayland_data_device_primary_legacy_init (MetaWaylandDataDevicePrimaryLegacy *data_device) { wl_list_init (&data_device->resource_list); wl_list_init (&data_device->focus_resource_list); } static struct wl_resource * create_and_send_primary_offer (MetaWaylandDataDevicePrimaryLegacy *data_device, struct wl_resource *target) { MetaWaylandDataOffer *offer; MetaDisplay *display = meta_get_display (); struct wl_resource *resource; GList *mimetypes, *l; mimetypes = meta_selection_get_mimetypes (meta_display_get_selection (display), META_SELECTION_PRIMARY); if (!mimetypes) return NULL; offer = meta_wayland_data_offer_primary_legacy_new (target); resource = meta_wayland_data_offer_get_resource (offer); gtk_primary_selection_device_send_data_offer (target, resource); for (l = mimetypes; l; l = l->next) gtk_primary_selection_offer_send_offer (resource, l->data); g_list_free_full (mimetypes, g_free); return resource; } void meta_wayland_data_device_primary_legacy_set_keyboard_focus (MetaWaylandDataDevicePrimaryLegacy *data_device) { MetaWaylandSeat *seat = wl_container_of (data_device, seat, primary_legacy_data_device); struct wl_client *focus_client; struct wl_resource *data_device_resource; focus_client = meta_wayland_keyboard_get_focus_client (seat->keyboard); if (focus_client == data_device->focus_client) return; data_device->focus_client = focus_client; move_resources (&data_device->resource_list, &data_device->focus_resource_list); if (!focus_client) return; move_resources_for_client (&data_device->focus_resource_list, &data_device->resource_list, focus_client); wl_resource_for_each (data_device_resource, &data_device->focus_resource_list) { struct wl_resource *offer; offer = create_and_send_primary_offer (data_device, data_device_resource); gtk_primary_selection_device_send_selection (data_device_resource, offer); } } muffin-6.4.1/src/wayland/meta-wayland-shell-surface.c0000664000175000017500000003275114723361714021462 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-shell-surface.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-actor-wayland.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-subsurface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-window-wayland.h" typedef struct _MetaWaylandShellSurfacePrivate { MetaWindow *window; gulong unmanaging_handler_id; gulong position_changed_handler_id; gulong effects_completed_handler_id; } MetaWaylandShellSurfacePrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandShellSurface, meta_wayland_shell_surface, META_TYPE_WAYLAND_ACTOR_SURFACE) void meta_wayland_shell_surface_calculate_geometry (MetaWaylandShellSurface *shell_surface, MetaRectangle *out_geometry) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (shell_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaRectangle geometry; MetaWaylandSurface *subsurface_surface; geometry = (MetaRectangle) { .width = meta_wayland_surface_get_width (surface), .height = meta_wayland_surface_get_height (surface), }; META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) { MetaWaylandSubsurface *subsurface; subsurface = META_WAYLAND_SUBSURFACE (subsurface_surface->role); meta_wayland_subsurface_union_geometry (subsurface, 0, 0, &geometry); } *out_geometry = geometry; } void meta_wayland_shell_surface_determine_geometry (MetaWaylandShellSurface *shell_surface, MetaRectangle *set_geometry, MetaRectangle *out_geometry) { MetaRectangle bounding_geometry = { 0 }; MetaRectangle intersected_geometry = { 0 }; meta_wayland_shell_surface_calculate_geometry (shell_surface, &bounding_geometry); meta_rectangle_intersect (set_geometry, &bounding_geometry, &intersected_geometry); *out_geometry = intersected_geometry; } static void clear_window (MetaWaylandShellSurface *shell_surface) { MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (shell_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActor *surface_actor; if (!priv->window) return; g_clear_signal_handler (&priv->unmanaging_handler_id, priv->window); g_clear_signal_handler (&priv->position_changed_handler_id, priv->window); g_clear_signal_handler (&priv->effects_completed_handler_id, meta_window_actor_from_window (priv->window)); priv->window = NULL; surface_actor = meta_wayland_surface_get_actor (surface); if (surface_actor) clutter_actor_set_reactive (CLUTTER_ACTOR (surface_actor), FALSE); meta_wayland_surface_notify_unmapped (surface); } static void window_unmanaging (MetaWindow *window, MetaWaylandShellSurface *shell_surface) { clear_window (shell_surface); } static void window_position_changed (MetaWindow *window, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); } static void window_actor_effects_completed (MetaWindowActor *window_actor, MetaWaylandSurface *surface) { meta_wayland_surface_update_outputs_recursively (surface); meta_wayland_compositor_repick (surface->compositor); } void meta_wayland_shell_surface_set_window (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (shell_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActor *surface_actor; g_assert (!priv->window); priv->window = window; surface_actor = meta_wayland_surface_get_actor (surface); if (surface_actor) clutter_actor_set_reactive (CLUTTER_ACTOR (surface_actor), TRUE); priv->unmanaging_handler_id = g_signal_connect (window, "unmanaging", G_CALLBACK (window_unmanaging), shell_surface); priv->position_changed_handler_id = g_signal_connect (window, "position-changed", G_CALLBACK (window_position_changed), surface); priv->effects_completed_handler_id = g_signal_connect (meta_window_actor_from_window (window), "effects-completed", G_CALLBACK (window_actor_effects_completed), surface); meta_window_update_monitor (window, META_WINDOW_UPDATE_MONITOR_FLAGS_NONE); } void meta_wayland_shell_surface_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandShellSurfaceClass *shell_surface_class = META_WAYLAND_SHELL_SURFACE_GET_CLASS (shell_surface); shell_surface_class->configure (shell_surface, configuration); } void meta_wayland_shell_surface_ping (MetaWaylandShellSurface *shell_surface, uint32_t serial) { MetaWaylandShellSurfaceClass *shell_surface_class = META_WAYLAND_SHELL_SURFACE_GET_CLASS (shell_surface); shell_surface_class->ping (shell_surface, serial); } void meta_wayland_shell_surface_close (MetaWaylandShellSurface *shell_surface) { MetaWaylandShellSurfaceClass *shell_surface_class = META_WAYLAND_SHELL_SURFACE_GET_CLASS (shell_surface); shell_surface_class->close (shell_surface); } void meta_wayland_shell_surface_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { MetaWaylandShellSurfaceClass *shell_surface_class = META_WAYLAND_SHELL_SURFACE_GET_CLASS (shell_surface); shell_surface_class->managed (shell_surface, window); } static void meta_wayland_shell_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_shell_surface_parent_class); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs (); surface_role_class->assigned (surface_role); } static void meta_wayland_shell_surface_surface_pre_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface_role); MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (pending->newly_attached && !surface->buffer_ref.buffer && priv->window) meta_window_queue (priv->window, META_QUEUE_CALC_SHOWING); } static void meta_wayland_shell_surface_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface_role); MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface_role); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWindow *window; MetaWaylandBuffer *buffer; double geometry_scale; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_shell_surface_parent_class); surface_role_class->apply_state (surface_role, pending); buffer = surface->buffer_ref.buffer; if (!buffer) return; window = priv->window; if (!window) return; geometry_scale = meta_wayland_actor_surface_get_geometry_scale (actor_surface); window->buffer_rect.width = meta_wayland_surface_get_width (surface) * geometry_scale; window->buffer_rect.height = meta_wayland_surface_get_height (surface) * geometry_scale; } static MetaWindow * meta_wayland_shell_surface_get_window (MetaWaylandSurfaceRole *surface_role) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface_role); MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); return priv->window; } static void meta_wayland_shell_surface_notify_subsurface_state_changed (MetaWaylandSurfaceRole *surface_role) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (surface_role); MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWindow *window; MetaWindowActor *window_actor; window = priv->window; if (!window) return; window_actor = meta_window_actor_from_window (window); meta_window_actor_wayland_rebuild_surface_tree (window_actor); } static double meta_wayland_shell_surface_get_geometry_scale (MetaWaylandActorSurface *actor_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *toplevel_window; toplevel_window = meta_wayland_surface_get_toplevel_window (surface); if (meta_is_stage_views_scaled () || !toplevel_window) return 1; else return meta_window_wayland_get_geometry_scale (toplevel_window); } static void meta_wayland_shell_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_shell_surface_parent_class); MetaWindow *toplevel_window; toplevel_window = meta_wayland_surface_get_toplevel_window (surface); if (!toplevel_window) return; actor_surface_class->sync_actor_state (actor_surface); } void meta_wayland_shell_surface_destroy_window (MetaWaylandShellSurface *shell_surface) { MetaWaylandShellSurfacePrivate *priv = meta_wayland_shell_surface_get_instance_private (shell_surface); MetaWindow *window; MetaDisplay *display; uint32_t timestamp; window = priv->window; if (!window) return; display = meta_window_get_display (window); timestamp = meta_display_get_current_time_roundtrip (display); meta_window_unmanage (window, timestamp); g_assert (!priv->window); } static void meta_wayland_shell_surface_finalize (GObject *object) { MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (object); meta_wayland_shell_surface_destroy_window (shell_surface); G_OBJECT_CLASS (meta_wayland_shell_surface_parent_class)->finalize (object); } static void meta_wayland_shell_surface_init (MetaWaylandShellSurface *role) { } static void meta_wayland_shell_surface_class_init (MetaWaylandShellSurfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (klass); object_class->finalize = meta_wayland_shell_surface_finalize; surface_role_class->assigned = meta_wayland_shell_surface_assigned; surface_role_class->pre_apply_state = meta_wayland_shell_surface_surface_pre_apply_state; surface_role_class->apply_state = meta_wayland_shell_surface_surface_apply_state; surface_role_class->notify_subsurface_state_changed = meta_wayland_shell_surface_notify_subsurface_state_changed; surface_role_class->get_window = meta_wayland_shell_surface_get_window; actor_surface_class->get_geometry_scale = meta_wayland_shell_surface_get_geometry_scale; actor_surface_class->sync_actor_state = meta_wayland_shell_surface_sync_actor_state; } muffin-6.4.1/src/wayland/meta-wayland-popup.c0000664000175000017500000002263614723361714020071 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * Copyright (C) 2015 Red Hat, Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . */ /* * Copyright © 2008 Kristian Høgsberg * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-pointer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-surface.h" G_DEFINE_INTERFACE (MetaWaylandPopupSurface, meta_wayland_popup_surface, G_TYPE_OBJECT); struct _MetaWaylandPopupGrab { MetaWaylandPointerGrab generic; struct wl_client *grab_client; struct wl_list all_popups; }; struct _MetaWaylandPopup { MetaWaylandPopupGrab *grab; MetaWaylandPopupSurface *popup_surface; struct wl_list link; }; static void meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, MetaWaylandSurface *surface); static void meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab); static void meta_wayland_popup_surface_default_init (MetaWaylandPopupSurfaceInterface *iface) { } static void meta_wayland_popup_surface_done (MetaWaylandPopupSurface *popup_surface) { META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->done (popup_surface); } static void meta_wayland_popup_surface_dismiss (MetaWaylandPopupSurface *popup_surface) { META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->dismiss (popup_surface); } static MetaWaylandSurface * meta_wayland_popup_surface_get_surface (MetaWaylandPopupSurface *popup_surface) { return META_WAYLAND_POPUP_SURFACE_GET_IFACE (popup_surface)->get_surface (popup_surface); } static void popup_grab_focus (MetaWaylandPointerGrab *grab, MetaWaylandSurface *surface) { MetaWaylandPopupGrab *popup_grab = (MetaWaylandPopupGrab*)grab; MetaWaylandSeat *seat = meta_wayland_pointer_get_seat (grab->pointer); /* * We rely on having a pointer grab even when the seat doesn't have * the pointer capability. In this case, we shouldn't update any pointer focus * since there is no such thing when the seat doesn't have the pointer * capability. */ if (!meta_wayland_seat_has_pointer (seat)) return; /* Popup grabs are in owner-events mode (ie, events for the same client are reported as normal) */ if (surface && wl_resource_get_client (surface->resource) == popup_grab->grab_client) meta_wayland_pointer_set_focus (grab->pointer, surface); else meta_wayland_pointer_set_focus (grab->pointer, NULL); } static void popup_grab_motion (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { meta_wayland_pointer_send_motion (grab->pointer, event); } static void popup_grab_button (MetaWaylandPointerGrab *grab, const ClutterEvent *event) { MetaWaylandPointer *pointer = grab->pointer; if (pointer->focus_surface) meta_wayland_pointer_send_button (grab->pointer, event); else if (clutter_event_type (event) == CLUTTER_BUTTON_RELEASE && pointer->button_count == 0) meta_wayland_pointer_end_popup_grab (grab->pointer); } static void popup_grab_cancel (MetaWaylandPointerGrab *grab) { meta_wayland_pointer_end_popup_grab (grab->pointer); } static MetaWaylandPointerGrabInterface popup_grab_interface = { popup_grab_focus, popup_grab_motion, popup_grab_button, popup_grab_cancel }; MetaWaylandPopupGrab * meta_wayland_popup_grab_create (MetaWaylandPointer *pointer, MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); struct wl_client *client = wl_resource_get_client (surface->resource); MetaWaylandPopupGrab *grab; grab = g_slice_new0 (MetaWaylandPopupGrab); grab->generic.interface = &popup_grab_interface; grab->generic.pointer = pointer; grab->grab_client = client; wl_list_init (&grab->all_popups); meta_wayland_popup_grab_begin (grab, surface); return grab; } void meta_wayland_popup_grab_destroy (MetaWaylandPopupGrab *grab) { meta_wayland_popup_grab_end (grab); g_slice_free (MetaWaylandPopupGrab, grab); } static void meta_wayland_popup_grab_begin (MetaWaylandPopupGrab *grab, MetaWaylandSurface *surface) { MetaWaylandPointer *pointer = grab->generic.pointer; MetaWindow *window = meta_wayland_surface_get_window (surface); meta_wayland_pointer_start_grab (pointer, (MetaWaylandPointerGrab*)grab); meta_display_begin_grab_op (window->display, window, META_GRAB_OP_WAYLAND_POPUP, FALSE, /* pointer_already_grabbed */ FALSE, /* frame_action */ 1, /* button. XXX? */ 0, /* modmask */ meta_display_get_current_time_roundtrip ( window->display), pointer->grab_x, pointer->grab_y); } void meta_wayland_popup_grab_end (MetaWaylandPopupGrab *grab) { MetaWaylandPopup *popup, *tmp; g_assert (grab->generic.interface == &popup_grab_interface); wl_list_for_each_safe (popup, tmp, &grab->all_popups, link) { meta_wayland_popup_surface_done (popup->popup_surface); meta_wayland_popup_destroy (popup); } { MetaDisplay *display = meta_get_display (); meta_display_end_grab_op (display, meta_display_get_current_time_roundtrip (display)); } meta_wayland_pointer_end_grab (grab->generic.pointer); } MetaWaylandSurface * meta_wayland_popup_grab_get_top_popup (MetaWaylandPopupGrab *grab) { MetaWaylandPopup *popup; g_assert (!wl_list_empty (&grab->all_popups)); popup = wl_container_of (grab->all_popups.next, popup, link); return meta_wayland_popup_surface_get_surface (popup->popup_surface); } gboolean meta_wayland_pointer_grab_is_popup_grab (MetaWaylandPointerGrab *grab) { return grab->interface == &popup_grab_interface; } void meta_wayland_popup_destroy (MetaWaylandPopup *popup) { meta_wayland_popup_surface_dismiss (popup->popup_surface); wl_list_remove (&popup->link); g_slice_free (MetaWaylandPopup, popup); } void meta_wayland_popup_dismiss (MetaWaylandPopup *popup) { MetaWaylandPopupGrab *popup_grab = popup->grab; meta_wayland_popup_destroy (popup); if (wl_list_empty (&popup_grab->all_popups)) { meta_wayland_pointer_end_popup_grab (popup_grab->generic.pointer); } else { MetaWaylandSurface *top_popup_surface; MetaWaylandSeat *seat; top_popup_surface = meta_wayland_popup_grab_get_top_popup (popup_grab); seat = meta_wayland_pointer_get_seat (popup_grab->generic.pointer); if (meta_wayland_seat_has_keyboard (seat)) meta_wayland_keyboard_set_focus (seat->keyboard, top_popup_surface); } } MetaWaylandSurface * meta_wayland_popup_get_top_popup (MetaWaylandPopup *popup) { return meta_wayland_popup_grab_get_top_popup (popup->grab); } MetaWaylandPopup * meta_wayland_popup_create (MetaWaylandPopupSurface *popup_surface, MetaWaylandPopupGrab *grab) { MetaWaylandSurface *surface = meta_wayland_popup_surface_get_surface (popup_surface); MetaWaylandPopup *popup; MetaWaylandSeat *seat; /* Don't allow creating popups if the grab has a different client. */ if (grab->grab_client != wl_resource_get_client (surface->resource)) return NULL; popup = g_slice_new0 (MetaWaylandPopup); popup->grab = grab; popup->popup_surface = popup_surface; wl_list_insert (&grab->all_popups, &popup->link); seat = meta_wayland_pointer_get_seat (grab->generic.pointer); if (meta_wayland_seat_has_keyboard (seat)) meta_wayland_keyboard_set_focus (seat->keyboard, surface); return popup; } muffin-6.4.1/src/wayland/meta-pointer-lock-wayland.h0000664000175000017500000000254314723361714021334 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #ifndef META_POINTER_LOCK_WAYLAND_H #define META_POINTER_LOCK_WAYLAND_H #include #include "backends/meta-pointer-constraint.h" G_BEGIN_DECLS #define META_TYPE_POINTER_LOCK_WAYLAND (meta_pointer_lock_wayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland, META, POINTER_LOCK_WAYLAND, MetaPointerConstraint); MetaPointerConstraint *meta_pointer_lock_wayland_new (void); G_END_DECLS #endif /* META_LOCK_WAYLAND_H */ muffin-6.4.1/src/wayland/meta-wayland-keyboard.c0000664000175000017500000007102314723361714020520 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ /* * Copyright © 2010-2011 Intel Corporation * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The copyright holders make * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ /* The file is based on src/input.c from Weston */ #include "config.h" #include #include #include #include #include #include #include #include "backends/meta-backend-private.h" #include "core/display-private.h" #include "wayland/meta-wayland-private.h" #ifdef HAVE_NATIVE_BACKEND #include "backends/native/meta-backend-native.h" #include "backends/native/meta-event-native.h" #endif #define GSD_KEYBOARD_SCHEMA "org.cinnamon.settings-daemon.peripherals.keyboard" G_DEFINE_TYPE (MetaWaylandKeyboard, meta_wayland_keyboard, META_TYPE_WAYLAND_INPUT_DEVICE) static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard); static void notify_modifiers (MetaWaylandKeyboard *keyboard); static guint evdev_code (const ClutterKeyEvent *event); static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static int create_anonymous_file (off_t size, GError **error) { static const char template[] = "mutter-shared-XXXXXX"; char *path; int fd, flags; fd = g_file_open_tmp (template, &path, error); if (fd == -1) return -1; unlink (path); g_free (path); flags = fcntl (fd, F_GETFD); if (flags == -1) goto err; if (fcntl (fd, F_SETFD, flags | FD_CLOEXEC) == -1) goto err; if (ftruncate (fd, size) < 0) goto err; return fd; err: g_set_error_literal (error, G_FILE_ERROR, g_file_error_from_errno (errno), strerror (errno)); close (fd); return -1; } static void send_keymap (MetaWaylandKeyboard *keyboard, struct wl_resource *resource) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; GError *error = NULL; int fd; char *keymap_area; if (!xkb_info->keymap_string) return; fd = create_anonymous_file (xkb_info->keymap_size, &error); if (fd < 0) { g_warning ("Creating a keymap file for %lu bytes failed: %s", (unsigned long) xkb_info->keymap_size, error->message); g_clear_error (&error); return; } keymap_area = mmap (NULL, xkb_info->keymap_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); if (keymap_area == MAP_FAILED) { g_warning ("Failed to mmap() %lu bytes\n", (unsigned long) xkb_info->keymap_size); close (fd); return; } strcpy (keymap_area, xkb_info->keymap_string); munmap (keymap_area, xkb_info->keymap_size); wl_keyboard_send_keymap (resource, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, fd, keyboard->xkb_info.keymap_size); close (fd); } static void inform_clients_of_new_keymap (MetaWaylandKeyboard *keyboard) { struct wl_resource *keyboard_resource; wl_resource_for_each (keyboard_resource, &keyboard->resource_list) send_keymap (keyboard, keyboard_resource); wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list) send_keymap (keyboard, keyboard_resource); } static void meta_wayland_keyboard_take_keymap (MetaWaylandKeyboard *keyboard, struct xkb_keymap *keymap) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; if (keymap == NULL) { g_warning ("Attempting to set null keymap (compilation probably failed)"); return; } g_clear_pointer (&xkb_info->keymap_string, g_free); xkb_keymap_unref (xkb_info->keymap); xkb_info->keymap = xkb_keymap_ref (keymap); meta_wayland_keyboard_update_xkb_state (keyboard); xkb_info->keymap_string = xkb_keymap_get_as_string (xkb_info->keymap, XKB_KEYMAP_FORMAT_TEXT_V1); if (!xkb_info->keymap_string) { g_warning ("Failed to get string version of keymap"); return; } xkb_info->keymap_size = strlen (xkb_info->keymap_string) + 1; inform_clients_of_new_keymap (keyboard); notify_modifiers (keyboard); } static xkb_mod_mask_t kbd_a11y_apply_mask (MetaWaylandKeyboard *keyboard) { xkb_mod_mask_t latched, locked, depressed, group; xkb_mod_mask_t update_mask = 0; depressed = xkb_state_serialize_mods(keyboard->xkb_info.state, XKB_STATE_DEPRESSED); latched = xkb_state_serialize_mods (keyboard->xkb_info.state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods (keyboard->xkb_info.state, XKB_STATE_MODS_LOCKED); group = xkb_state_serialize_layout (keyboard->xkb_info.state, XKB_STATE_LAYOUT_EFFECTIVE); if ((latched & keyboard->kbd_a11y_latched_mods) != keyboard->kbd_a11y_latched_mods) update_mask |= XKB_STATE_MODS_LATCHED; if ((locked & keyboard->kbd_a11y_locked_mods) != keyboard->kbd_a11y_locked_mods) update_mask |= XKB_STATE_MODS_LOCKED; if (update_mask) { latched |= keyboard->kbd_a11y_latched_mods; locked |= keyboard->kbd_a11y_locked_mods; xkb_state_update_mask (keyboard->xkb_info.state, depressed, latched, locked, 0, 0, group); } return update_mask; } static void on_keymap_changed (MetaBackend *backend, gpointer data) { MetaWaylandKeyboard *keyboard = data; meta_wayland_keyboard_take_keymap (keyboard, meta_backend_get_keymap (backend)); } static void on_keymap_layout_group_changed (MetaBackend *backend, guint idx, gpointer data) { MetaWaylandKeyboard *keyboard = data; xkb_mod_mask_t depressed_mods; xkb_mod_mask_t latched_mods; xkb_mod_mask_t locked_mods; struct xkb_state *state; state = keyboard->xkb_info.state; depressed_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED); latched_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED); locked_mods = xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED); xkb_state_update_mask (state, depressed_mods, latched_mods, locked_mods, 0, 0, idx); kbd_a11y_apply_mask (keyboard); notify_modifiers (keyboard); } static void keyboard_handle_focus_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandKeyboard *keyboard = wl_container_of (listener, keyboard, focus_surface_listener); meta_wayland_keyboard_set_focus (keyboard, NULL); } static gboolean meta_wayland_keyboard_broadcast_key (MetaWaylandKeyboard *keyboard, uint32_t time, uint32_t key, uint32_t state) { struct wl_resource *resource; if (!wl_list_empty (&keyboard->focus_resource_list)) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (keyboard); uint32_t serial; serial = meta_wayland_input_device_next_serial (input_device); if (state) { keyboard->key_down_serial = serial; keyboard->key_down_keycode = key; } else { keyboard->key_up_serial = serial; keyboard->key_up_keycode = key; } wl_resource_for_each (resource, &keyboard->focus_resource_list) wl_keyboard_send_key (resource, serial, time, key, state); } /* Eat the key events if we have a focused surface. */ return (keyboard->focus_surface != NULL); } static gboolean notify_key (MetaWaylandKeyboard *keyboard, const ClutterEvent *event) { return keyboard->grab->interface->key (keyboard->grab, event); } static xkb_mod_mask_t add_vmod (xkb_mod_mask_t mask, xkb_mod_mask_t mod, xkb_mod_mask_t vmod, xkb_mod_mask_t *added) { if ((mask & mod) && !(mod & *added)) { mask |= vmod; *added |= mod; } return mask; } static xkb_mod_mask_t add_virtual_mods (xkb_mod_mask_t mask) { MetaKeyBindingManager *keys = &(meta_get_display ()->key_binding_manager); xkb_mod_mask_t added; guint i; /* Order is important here: if multiple vmods share the same real modifier we only want to add the first. */ struct { xkb_mod_mask_t mod; xkb_mod_mask_t vmod; } mods[] = { { keys->super_mask, keys->virtual_super_mask }, { keys->hyper_mask, keys->virtual_hyper_mask }, { keys->meta_mask, keys->virtual_meta_mask }, }; added = 0; for (i = 0; i < G_N_ELEMENTS (mods); ++i) mask = add_vmod (mask, mods[i].mod, mods[i].vmod, &added); return mask; } static void keyboard_send_modifiers (MetaWaylandKeyboard *keyboard, struct wl_resource *resource, uint32_t serial) { struct xkb_state *state = keyboard->xkb_info.state; xkb_mod_mask_t depressed, latched, locked; depressed = add_virtual_mods (xkb_state_serialize_mods (state, XKB_STATE_MODS_DEPRESSED)); latched = add_virtual_mods (xkb_state_serialize_mods (state, XKB_STATE_MODS_LATCHED)); locked = add_virtual_mods (xkb_state_serialize_mods (state, XKB_STATE_MODS_LOCKED)); wl_keyboard_send_modifiers (resource, serial, depressed, latched, locked, xkb_state_serialize_layout (state, XKB_STATE_LAYOUT_EFFECTIVE)); } static void meta_wayland_keyboard_broadcast_modifiers (MetaWaylandKeyboard *keyboard) { struct wl_resource *resource; if (!wl_list_empty (&keyboard->focus_resource_list)) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (keyboard); uint32_t serial; serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &keyboard->focus_resource_list) keyboard_send_modifiers (keyboard, resource, serial); } } static void notify_modifiers (MetaWaylandKeyboard *keyboard) { struct xkb_state *state; state = keyboard->xkb_info.state; keyboard->grab->interface->modifiers (keyboard->grab, xkb_state_serialize_mods (state, XKB_STATE_MODS_EFFECTIVE)); } static void meta_wayland_keyboard_update_xkb_state (MetaWaylandKeyboard *keyboard) { MetaWaylandXkbInfo *xkb_info = &keyboard->xkb_info; xkb_mod_mask_t latched, locked, numlock; MetaBackend *backend = meta_get_backend (); xkb_layout_index_t layout_idx; ClutterKeymap *keymap; ClutterSeat *seat; /* Preserve latched/locked modifiers state */ if (xkb_info->state) { latched = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods (xkb_info->state, XKB_STATE_MODS_LOCKED); xkb_state_unref (xkb_info->state); } else { latched = locked = 0; } seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); keymap = clutter_seat_get_keymap (seat); numlock = (1 << xkb_keymap_mod_get_index (xkb_info->keymap, "Mod2")); if (clutter_keymap_get_num_lock_state (keymap)) locked |= numlock; else locked &= ~numlock; xkb_info->state = xkb_state_new (xkb_info->keymap); layout_idx = meta_backend_get_keymap_layout_group (backend); xkb_state_update_mask (xkb_info->state, 0, latched, locked, 0, 0, layout_idx); kbd_a11y_apply_mask (keyboard); } static void on_kbd_a11y_mask_changed (ClutterSeat *seat, xkb_mod_mask_t new_latched_mods, xkb_mod_mask_t new_locked_mods, MetaWaylandKeyboard *keyboard) { xkb_mod_mask_t latched, locked, depressed, group; if (keyboard->xkb_info.state == NULL) return; depressed = xkb_state_serialize_mods(keyboard->xkb_info.state, XKB_STATE_DEPRESSED); latched = xkb_state_serialize_mods (keyboard->xkb_info.state, XKB_STATE_MODS_LATCHED); locked = xkb_state_serialize_mods (keyboard->xkb_info.state, XKB_STATE_MODS_LOCKED); group = xkb_state_serialize_layout (keyboard->xkb_info.state, XKB_STATE_LAYOUT_EFFECTIVE); /* Clear previous masks */ latched &= ~keyboard->kbd_a11y_latched_mods; locked &= ~keyboard->kbd_a11y_locked_mods; xkb_state_update_mask (keyboard->xkb_info.state, depressed, latched, locked, 0, 0, group); /* Apply new masks */ keyboard->kbd_a11y_latched_mods = new_latched_mods; keyboard->kbd_a11y_locked_mods = new_locked_mods; kbd_a11y_apply_mask (keyboard); notify_modifiers (keyboard); } static void notify_key_repeat_for_resource (MetaWaylandKeyboard *keyboard, struct wl_resource *keyboard_resource) { if (wl_resource_get_version (keyboard_resource) >= WL_KEYBOARD_REPEAT_INFO_SINCE_VERSION) { gboolean repeat; unsigned int delay, rate; repeat = g_settings_get_boolean (keyboard->settings, "repeat"); if (repeat) { unsigned int interval; interval = g_settings_get_uint (keyboard->settings, "repeat-interval"); /* Our setting is in the milliseconds between keys. "rate" is the number * of keys per second. */ if (interval > 0) rate = (1000 / interval); else rate = 0; delay = g_settings_get_uint (keyboard->settings, "delay"); } else { rate = 0; delay = 0; } wl_keyboard_send_repeat_info (keyboard_resource, rate, delay); } } static void notify_key_repeat (MetaWaylandKeyboard *keyboard) { struct wl_resource *keyboard_resource; wl_resource_for_each (keyboard_resource, &keyboard->resource_list) { notify_key_repeat_for_resource (keyboard, keyboard_resource); } wl_resource_for_each (keyboard_resource, &keyboard->focus_resource_list) { notify_key_repeat_for_resource (keyboard, keyboard_resource); } } static void settings_changed (GSettings *settings, const char *key, gpointer data) { MetaWaylandKeyboard *keyboard = data; notify_key_repeat (keyboard); } static gboolean default_grab_key (MetaWaylandKeyboardGrab *grab, const ClutterEvent *event) { MetaWaylandKeyboard *keyboard = grab->keyboard; gboolean is_press = event->type == CLUTTER_KEY_PRESS; guint32 code = 0; #ifdef HAVE_NATIVE_BACKEND MetaBackend *backend = meta_get_backend (); #endif /* Ignore autorepeat events, as autorepeat in Wayland is done on the client * side. */ if (event->key.flags & CLUTTER_EVENT_FLAG_REPEATED) return FALSE; #ifdef HAVE_NATIVE_BACKEND if (META_IS_BACKEND_NATIVE (backend)) code = meta_event_native_get_event_code (event); if (code == 0) #endif code = evdev_code (&event->key); return meta_wayland_keyboard_broadcast_key (keyboard, event->key.time, code, is_press); } static void default_grab_modifiers (MetaWaylandKeyboardGrab *grab, ClutterModifierType modifiers) { meta_wayland_keyboard_broadcast_modifiers (grab->keyboard); } static const MetaWaylandKeyboardGrabInterface default_keyboard_grab_interface = { default_grab_key, default_grab_modifiers }; void meta_wayland_keyboard_enable (MetaWaylandKeyboard *keyboard) { MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = clutter_get_default_backend (); keyboard->settings = g_settings_new ("org.gnome.desktop.peripherals.keyboard"); g_signal_connect (keyboard->settings, "changed", G_CALLBACK (settings_changed), keyboard); g_signal_connect (backend, "keymap-changed", G_CALLBACK (on_keymap_changed), keyboard); g_signal_connect (backend, "keymap-layout-group-changed", G_CALLBACK (on_keymap_layout_group_changed), keyboard); g_signal_connect (clutter_backend_get_default_seat (clutter_backend), "kbd-a11y-mods-state-changed", G_CALLBACK (on_kbd_a11y_mask_changed), keyboard); meta_wayland_keyboard_take_keymap (keyboard, meta_backend_get_keymap (backend)); } static void meta_wayland_xkb_info_destroy (MetaWaylandXkbInfo *xkb_info) { g_clear_pointer (&xkb_info->keymap, xkb_keymap_unref); g_clear_pointer (&xkb_info->state, xkb_state_unref); g_clear_pointer (&xkb_info->keymap_string, g_free); } void meta_wayland_keyboard_disable (MetaWaylandKeyboard *keyboard) { MetaBackend *backend = meta_get_backend (); g_signal_handlers_disconnect_by_func (backend, on_keymap_changed, keyboard); g_signal_handlers_disconnect_by_func (backend, on_keymap_layout_group_changed, keyboard); meta_wayland_keyboard_end_grab (keyboard); meta_wayland_keyboard_set_focus (keyboard, NULL); wl_list_remove (&keyboard->resource_list); wl_list_init (&keyboard->resource_list); wl_list_remove (&keyboard->focus_resource_list); wl_list_init (&keyboard->focus_resource_list); g_clear_object (&keyboard->settings); } static guint evdev_code (const ClutterKeyEvent *event) { /* clutter-xkb-utils.c adds a fixed offset of 8 to go into XKB's * range, so we do the reverse here. */ return event->hardware_keycode - 8; } void meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event) { gboolean is_press = event->type == CLUTTER_KEY_PRESS; /* Only handle real, non-synthetic, events here. The IM is free to reemit * key events (incl. modifiers), handling those additionally will result * in doubly-pressed keys. */ if ((event->flags & (CLUTTER_EVENT_FLAG_SYNTHETIC | CLUTTER_EVENT_FLAG_INPUT_METHOD)) != 0) return; /* If we get a key event but still have pending modifier state * changes from a previous event that didn't get cleared, we need to * send that state right away so that the new key event can be * interpreted by clients correctly modified. */ if (keyboard->mods_changed) notify_modifiers (keyboard); keyboard->mods_changed = xkb_state_update_key (keyboard->xkb_info.state, event->hardware_keycode, is_press ? XKB_KEY_DOWN : XKB_KEY_UP); keyboard->mods_changed |= kbd_a11y_apply_mask (keyboard); } gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event) { #ifdef WITH_VERBOSE_MODE gboolean is_press = event->type == CLUTTER_KEY_PRESS; #endif gboolean handled; /* Synthetic key events are for autorepeat. Ignore those, as * autorepeat in Wayland is done on the client side. */ if ((event->flags & CLUTTER_EVENT_FLAG_SYNTHETIC) && !(event->flags & CLUTTER_EVENT_FLAG_INPUT_METHOD)) return FALSE; meta_verbose ("Handling key %s event code %d\n", is_press ? "press" : "release", event->hardware_keycode); handled = notify_key (keyboard, (const ClutterEvent *) event); if (handled) meta_verbose ("Sent event to wayland client\n"); else meta_verbose ("No wayland surface is focused, continuing normal operation\n"); if (keyboard->mods_changed != 0) { notify_modifiers (keyboard); keyboard->mods_changed = 0; } return handled; } void meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *keyboard, char *key_vector, int key_vector_len, int offset) { gboolean mods_changed = FALSE; int i; for (i = offset; i < key_vector_len * 8; i++) { gboolean set = (key_vector[i/8] & (1 << (i % 8))) != 0; /* The 'offset' parameter allows the caller to have the indices * into key_vector to either be X-style (base 8) or evdev (base 0), or * something else (unlikely). We subtract 'offset' to convert to evdev * style, then add 8 to convert the "evdev" style keycode back to * the X-style that xkbcommon expects. */ mods_changed |= xkb_state_update_key (keyboard->xkb_info.state, i - offset + 8, set ? XKB_KEY_DOWN : XKB_KEY_UP); } mods_changed |= kbd_a11y_apply_mask (keyboard); if (mods_changed) notify_modifiers (keyboard); } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } static void broadcast_focus (MetaWaylandKeyboard *keyboard, struct wl_resource *resource) { struct wl_array fake_keys; /* We never want to send pressed keys to wayland clients on * enter. The protocol says that we should send them, presumably so * that clients can trigger their own key repeat routine in case * they are given focus and a key is physically pressed. * * Unfortunately this causes some clients, in particular Xwayland, * to register key events that they really shouldn't handle, * e.g. on an Alt+Tab keybinding, where Alt is released before Tab, * clients would see Tab being pressed on enter followed by a key * release event for Tab, meaning that Tab would be processed by * the client when it really shouldn't. * * Since the use case for the pressed keys array on enter seems weak * to us, we'll just fake that there are no pressed keys instead * which should be spec compliant even if it might not be true. */ wl_array_init (&fake_keys); keyboard_send_modifiers (keyboard, resource, keyboard->focus_serial); wl_keyboard_send_enter (resource, keyboard->focus_serial, keyboard->focus_surface->resource, &fake_keys); } void meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard, MetaWaylandSurface *surface) { MetaWaylandInputDevice *input_device = META_WAYLAND_INPUT_DEVICE (keyboard); if (keyboard->focus_surface == surface) return; if (keyboard->focus_surface != NULL) { if (!wl_list_empty (&keyboard->focus_resource_list)) { struct wl_resource *resource; uint32_t serial; serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &keyboard->focus_resource_list) { wl_keyboard_send_leave (resource, serial, keyboard->focus_surface->resource); } move_resources (&keyboard->resource_list, &keyboard->focus_resource_list); } wl_list_remove (&keyboard->focus_surface_listener.link); keyboard->focus_surface = NULL; } if (surface != NULL) { struct wl_resource *focus_surface_resource; keyboard->focus_surface = surface; focus_surface_resource = keyboard->focus_surface->resource; wl_resource_add_destroy_listener (focus_surface_resource, &keyboard->focus_surface_listener); move_resources_for_client (&keyboard->focus_resource_list, &keyboard->resource_list, wl_resource_get_client (focus_surface_resource)); /* Make sure a11y masks are applied before braodcasting modifiers */ kbd_a11y_apply_mask (keyboard); if (!wl_list_empty (&keyboard->focus_resource_list)) { struct wl_resource *resource; keyboard->focus_serial = meta_wayland_input_device_next_serial (input_device); wl_resource_for_each (resource, &keyboard->focus_resource_list) { broadcast_focus (keyboard, resource); } } } } struct wl_client * meta_wayland_keyboard_get_focus_client (MetaWaylandKeyboard *keyboard) { if (keyboard->focus_surface) return wl_resource_get_client (keyboard->focus_surface->resource); else return NULL; } static void keyboard_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_keyboard_interface keyboard_interface = { keyboard_release, }; void meta_wayland_keyboard_create_new_resource (MetaWaylandKeyboard *keyboard, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &wl_keyboard_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &keyboard_interface, keyboard, unbind_resource); send_keymap (keyboard, resource); notify_key_repeat_for_resource (keyboard, resource); if (keyboard->focus_surface && wl_resource_get_client (keyboard->focus_surface->resource) == client) { wl_list_insert (&keyboard->focus_resource_list, wl_resource_get_link (resource)); broadcast_focus (keyboard, resource); } else { wl_list_insert (&keyboard->resource_list, wl_resource_get_link (resource)); } } gboolean meta_wayland_keyboard_can_popup (MetaWaylandKeyboard *keyboard, uint32_t serial) { return (keyboard->key_down_serial == serial || ((keyboard->key_down_keycode == keyboard->key_up_keycode) && keyboard->key_up_serial == serial)); } void meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard, MetaWaylandKeyboardGrab *grab) { meta_wayland_keyboard_set_focus (keyboard, NULL); keyboard->grab = grab; grab->keyboard = keyboard; } void meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard) { keyboard->grab = &keyboard->default_grab; } static void meta_wayland_keyboard_init (MetaWaylandKeyboard *keyboard) { wl_list_init (&keyboard->resource_list); wl_list_init (&keyboard->focus_resource_list); keyboard->default_grab.interface = &default_keyboard_grab_interface; keyboard->default_grab.keyboard = keyboard; keyboard->grab = &keyboard->default_grab; keyboard->focus_surface_listener.notify = keyboard_handle_focus_surface_destroy; } static void meta_wayland_keyboard_finalize (GObject *object) { MetaWaylandKeyboard *keyboard = META_WAYLAND_KEYBOARD (object); meta_wayland_xkb_info_destroy (&keyboard->xkb_info); } static void meta_wayland_keyboard_class_init (MetaWaylandKeyboardClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_keyboard_finalize; } muffin-6.4.1/src/wayland/meta-wayland-data-offer-primary.h0000664000175000017500000000272114723361714022415 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_OFFER_PRIMARY_H #define META_WAYLAND_DATA_OFFER_PRIMARY_H #include "meta-wayland-data-offer.h" MetaWaylandDataOffer * meta_wayland_data_offer_primary_new (struct wl_resource *target); #endif /* META_WAYLAND_DATA_OFFER_PRIMARY_H */ muffin-6.4.1/src/wayland/meta-wayland-outputs.c0000664000175000017500000005703514723361714020452 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "wayland/meta-wayland-outputs.h" #include #include "backends/meta-logical-monitor.h" #include "backends/meta-monitor.h" #include "backends/meta-monitor-manager-private.h" #include "wayland/meta-wayland-private.h" #include "xdg-output-unstable-v1-server-protocol.h" /* Wayland protocol headers list new additions, not deprecations */ #define NO_XDG_OUTPUT_DONE_SINCE_VERSION 3 enum { OUTPUT_DESTROYED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL]; G_DEFINE_TYPE (MetaWaylandOutput, meta_wayland_output, G_TYPE_OBJECT) static void send_xdg_output_events (struct wl_resource *resource, MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor, gboolean need_all_events, gboolean *pending_done_event); static void output_resource_destroy (struct wl_resource *res) { MetaWaylandOutput *wayland_output; wayland_output = wl_resource_get_user_data (res); if (!wayland_output) return; wayland_output->resources = g_list_remove (wayland_output->resources, res); } static MetaMonitor * pick_main_monitor (MetaLogicalMonitor *logical_monitor) { GList *monitors; monitors = meta_logical_monitor_get_monitors (logical_monitor); return g_list_first (monitors)->data; } static enum wl_output_subpixel cogl_subpixel_order_to_wl_output_subpixel (CoglSubpixelOrder subpixel_order) { switch (subpixel_order) { case COGL_SUBPIXEL_ORDER_UNKNOWN: return WL_OUTPUT_SUBPIXEL_UNKNOWN; case COGL_SUBPIXEL_ORDER_NONE: return WL_OUTPUT_SUBPIXEL_NONE; case COGL_SUBPIXEL_ORDER_HORIZONTAL_RGB: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_RGB; case COGL_SUBPIXEL_ORDER_HORIZONTAL_BGR: return WL_OUTPUT_SUBPIXEL_HORIZONTAL_BGR; case COGL_SUBPIXEL_ORDER_VERTICAL_RGB: return WL_OUTPUT_SUBPIXEL_VERTICAL_RGB; case COGL_SUBPIXEL_ORDER_VERTICAL_BGR: return WL_OUTPUT_SUBPIXEL_VERTICAL_BGR; } g_assert_not_reached (); return WL_OUTPUT_SUBPIXEL_UNKNOWN; } static enum wl_output_subpixel calculate_suitable_subpixel_order (MetaLogicalMonitor *logical_monitor) { GList *monitors; GList *l; MetaMonitor *first_monitor; CoglSubpixelOrder subpixel_order; monitors = meta_logical_monitor_get_monitors (logical_monitor); first_monitor = monitors->data; subpixel_order = meta_monitor_get_subpixel_order (first_monitor); for (l = monitors->next; l; l = l->next) { MetaMonitor *monitor = l->data; if (meta_monitor_get_subpixel_order (monitor) != subpixel_order) { subpixel_order = COGL_SUBPIXEL_ORDER_UNKNOWN; break; } } return cogl_subpixel_order_to_wl_output_subpixel (subpixel_order); } static int calculate_wayland_output_scale (MetaLogicalMonitor *logical_monitor) { float scale; scale = meta_logical_monitor_get_scale (logical_monitor); return ceilf (scale); } static void get_rotated_physical_dimensions (MetaMonitor *monitor, int *width_mm, int *height_mm) { int monitor_width_mm, monitor_height_mm; MetaLogicalMonitor *logical_monitor; meta_monitor_get_physical_dimensions (monitor, &monitor_width_mm, &monitor_height_mm); logical_monitor = meta_monitor_get_logical_monitor (monitor); if (meta_monitor_transform_is_rotated (logical_monitor->transform)) { *width_mm = monitor_height_mm; *height_mm = monitor_width_mm; } else { *width_mm = monitor_width_mm; *height_mm = monitor_height_mm; } } static gboolean is_different_rotation (MetaLogicalMonitor *a, MetaLogicalMonitor *b) { return (meta_monitor_transform_is_rotated (a->transform) != meta_monitor_transform_is_rotated (b->transform)); } static void get_native_output_mode_resolution (MetaLogicalMonitor *logical_monitor, MetaMonitorMode *mode, int *mode_width, int *mode_height) { MetaMonitorTransform transform; transform = meta_logical_monitor_get_transform (logical_monitor); if (meta_monitor_transform_is_rotated (transform)) meta_monitor_mode_get_resolution (mode, mode_height, mode_width); else meta_monitor_mode_get_resolution (mode, mode_width, mode_height); } static void send_output_events (struct wl_resource *resource, MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor, gboolean need_all_events, gboolean *pending_done_event) { int version = wl_resource_get_version (resource); MetaMonitor *monitor; MetaMonitorMode *current_mode; MetaMonitorMode *preferred_mode; guint mode_flags = WL_OUTPUT_MODE_CURRENT; MetaLogicalMonitor *old_logical_monitor; guint old_mode_flags; gint old_scale; float old_refresh_rate; float refresh_rate; int new_width, new_height; old_logical_monitor = wayland_output->logical_monitor; old_mode_flags = wayland_output->mode_flags; old_scale = wayland_output->scale; old_refresh_rate = wayland_output->refresh_rate; monitor = pick_main_monitor (logical_monitor); current_mode = meta_monitor_get_current_mode (monitor); refresh_rate = meta_monitor_mode_get_refresh_rate (current_mode); gboolean need_done = FALSE; if (need_all_events || old_logical_monitor->rect.x != logical_monitor->rect.x || old_logical_monitor->rect.y != logical_monitor->rect.y || is_different_rotation (old_logical_monitor, logical_monitor)) { int width_mm, height_mm; const char *vendor; const char *product; uint32_t transform; enum wl_output_subpixel subpixel_order; /* * While the wl_output carries information specific to a single monitor, * it is actually referring to a region of the compositor's screen region * (logical monitor), which may consist of multiple monitors (clones). * Arbitrarily use whatever monitor is the first in the logical monitor * and use that for these details. */ get_rotated_physical_dimensions (monitor, &width_mm, &height_mm); vendor = meta_monitor_get_vendor (monitor); product = meta_monitor_get_product (monitor); subpixel_order = calculate_suitable_subpixel_order (logical_monitor); /* * TODO: When we support wl_surface.set_buffer_transform, pass along * the correct transform here instead of always pretending its 'normal'. * The reason for this is to try stopping clients from setting any buffer * transform other than 'normal'. */ transform = WL_OUTPUT_TRANSFORM_NORMAL; wl_output_send_geometry (resource, logical_monitor->rect.x, logical_monitor->rect.y, width_mm, height_mm, subpixel_order, vendor, product, transform); need_done = TRUE; } preferred_mode = meta_monitor_get_preferred_mode (monitor); if (current_mode == preferred_mode) mode_flags |= WL_OUTPUT_MODE_PREFERRED; get_native_output_mode_resolution (logical_monitor, current_mode, &new_width, &new_height); if (need_all_events || wayland_output->mode_width != new_width || wayland_output->mode_height != new_height || old_refresh_rate != refresh_rate || old_mode_flags != mode_flags) { wl_output_send_mode (resource, mode_flags, new_width, new_height, (int32_t) (refresh_rate * 1000)); need_done = TRUE; } if (version >= WL_OUTPUT_SCALE_SINCE_VERSION) { int scale; scale = calculate_wayland_output_scale (logical_monitor); if (need_all_events || old_scale != scale) { wl_output_send_scale (resource, scale); need_done = TRUE; } } if (need_all_events && version >= WL_OUTPUT_DONE_SINCE_VERSION) { wl_output_send_done (resource); need_done = FALSE; } if (pending_done_event && need_done) *pending_done_event = TRUE; } static void bind_output (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandOutput *wayland_output = data; MetaLogicalMonitor *logical_monitor = wayland_output->logical_monitor; struct wl_resource *resource; #ifdef WITH_VERBOSE_MODE MetaMonitor *monitor; #endif resource = wl_resource_create (client, &wl_output_interface, version, id); wayland_output->resources = g_list_prepend (wayland_output->resources, resource); wl_resource_set_user_data (resource, wayland_output); wl_resource_set_destructor (resource, output_resource_destroy); if (!logical_monitor) return; #ifdef WITH_VERBOSE_MODE monitor = pick_main_monitor (logical_monitor); meta_verbose ("Binding monitor %p/%s (%u, %u, %u, %u) x %f\n", logical_monitor, meta_monitor_get_product (monitor), logical_monitor->rect.x, logical_monitor->rect.y, wayland_output->mode_width, wayland_output->mode_height, wayland_output->refresh_rate); #endif send_output_events (resource, wayland_output, logical_monitor, TRUE, NULL); } static void wayland_output_destroy_notify (gpointer data) { MetaWaylandOutput *wayland_output = data; g_signal_emit (wayland_output, signals[OUTPUT_DESTROYED], 0); g_object_unref (wayland_output); } static void meta_wayland_output_set_logical_monitor (MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor) { MetaMonitor *monitor; MetaMonitorMode *current_mode; MetaMonitorMode *preferred_mode; wayland_output->logical_monitor = logical_monitor; wayland_output->mode_flags = WL_OUTPUT_MODE_CURRENT; monitor = pick_main_monitor (logical_monitor); current_mode = meta_monitor_get_current_mode (monitor); preferred_mode = meta_monitor_get_preferred_mode (monitor); if (current_mode == preferred_mode) wayland_output->mode_flags |= WL_OUTPUT_MODE_PREFERRED; wayland_output->scale = calculate_wayland_output_scale (logical_monitor); wayland_output->refresh_rate = meta_monitor_mode_get_refresh_rate (current_mode); wayland_output->winsys_id = logical_monitor->winsys_id; get_native_output_mode_resolution (logical_monitor, current_mode, &wayland_output->mode_width, &wayland_output->mode_height); } static void wayland_output_update_for_output (MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor) { GList *iter; gboolean pending_done_event; pending_done_event = FALSE; for (iter = wayland_output->resources; iter; iter = iter->next) { struct wl_resource *resource = iter->data; send_output_events (resource, wayland_output, logical_monitor, FALSE, &pending_done_event); } for (iter = wayland_output->xdg_output_resources; iter; iter = iter->next) { struct wl_resource *xdg_output = iter->data; send_xdg_output_events (xdg_output, wayland_output, logical_monitor, FALSE, &pending_done_event); } /* Send the "done" events if needed */ if (pending_done_event) { for (iter = wayland_output->resources; iter; iter = iter->next) { struct wl_resource *resource = iter->data; if (wl_resource_get_version (resource) >= WL_OUTPUT_DONE_SINCE_VERSION) wl_output_send_done (resource); } for (iter = wayland_output->xdg_output_resources; iter; iter = iter->next) { struct wl_resource *xdg_output = iter->data; if (wl_resource_get_version (xdg_output) < NO_XDG_OUTPUT_DONE_SINCE_VERSION) zxdg_output_v1_send_done (xdg_output); } } /* It's very important that we change the output pointer here, as the old structure is about to be freed by MetaMonitorManager */ meta_wayland_output_set_logical_monitor (wayland_output, logical_monitor); } static MetaWaylandOutput * meta_wayland_output_new (MetaWaylandCompositor *compositor, MetaLogicalMonitor *logical_monitor) { MetaWaylandCompositor *wayland_compositor = meta_wayland_compositor_get_default (); MetaWaylandOutput *wayland_output; wayland_output = g_object_new (META_TYPE_WAYLAND_OUTPUT, NULL); wayland_output->global = wl_global_create (compositor->wayland_display, &wl_output_interface, META_WL_OUTPUT_VERSION, wayland_output, bind_output); meta_wayland_compositor_flush_clients (wayland_compositor); meta_wayland_output_set_logical_monitor (wayland_output, logical_monitor); return wayland_output; } static void make_output_resources_inert (MetaWaylandOutput *wayland_output) { GList *l; for (l = wayland_output->resources; l; l = l->next) { struct wl_resource *output_resource = l->data; wl_resource_set_user_data (output_resource, NULL); } g_list_free (wayland_output->resources); wayland_output->resources = NULL; for (l = wayland_output->xdg_output_resources; l; l = l->next) { struct wl_resource *xdg_output_resource = l->data; wl_resource_set_user_data (xdg_output_resource, NULL); } g_list_free (wayland_output->xdg_output_resources); wayland_output->xdg_output_resources = NULL; } static void make_output_inert (gpointer key, gpointer value, gpointer data) { MetaWaylandOutput *wayland_output = value; wayland_output->logical_monitor = NULL; make_output_resources_inert (wayland_output); } static gboolean delayed_destroy_outputs (gpointer data) { g_hash_table_destroy (data); return G_SOURCE_REMOVE; } static GHashTable * meta_wayland_compositor_update_outputs (MetaWaylandCompositor *compositor, MetaMonitorManager *monitor_manager) { GHashTable *new_table; GList *logical_monitors, *l; logical_monitors = meta_monitor_manager_get_logical_monitors (monitor_manager); new_table = g_hash_table_new_full (g_int64_hash, g_int64_equal, NULL, wayland_output_destroy_notify); for (l = logical_monitors; l; l = l->next) { MetaLogicalMonitor *logical_monitor = l->data; MetaWaylandOutput *wayland_output = NULL; if (logical_monitor->winsys_id == 0) continue; wayland_output = g_hash_table_lookup (compositor->outputs, &logical_monitor->winsys_id); if (wayland_output) { g_hash_table_steal (compositor->outputs, &logical_monitor->winsys_id); } else { wayland_output = meta_wayland_output_new (compositor, logical_monitor); } wayland_output_update_for_output (wayland_output, logical_monitor); g_hash_table_insert (new_table, &wayland_output->winsys_id, wayland_output); } g_hash_table_foreach (compositor->outputs, make_output_inert, NULL); g_timeout_add_seconds (10, delayed_destroy_outputs, compositor->outputs); return new_table; } static void on_monitors_changed (MetaMonitorManager *monitors, MetaWaylandCompositor *compositor) { compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); } static void meta_wayland_output_init (MetaWaylandOutput *wayland_output) { } static void meta_wayland_output_finalize (GObject *object) { MetaWaylandOutput *wayland_output = META_WAYLAND_OUTPUT (object); wl_global_destroy (wayland_output->global); /* Make sure the wl_output destructor doesn't try to access MetaWaylandOutput * after we have freed it. */ make_output_resources_inert (wayland_output); G_OBJECT_CLASS (meta_wayland_output_parent_class)->finalize (object); } static void meta_wayland_output_class_init (MetaWaylandOutputClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_wayland_output_finalize; signals[OUTPUT_DESTROYED] = g_signal_new ("output-destroyed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_xdg_output_destructor (struct wl_resource *resource) { MetaWaylandOutput *wayland_output; wayland_output = wl_resource_get_user_data (resource); if (!wayland_output) return; wayland_output->xdg_output_resources = g_list_remove (wayland_output->xdg_output_resources, resource); } static void meta_xdg_output_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zxdg_output_v1_interface meta_xdg_output_interface = { meta_xdg_output_destroy, }; static void send_xdg_output_events (struct wl_resource *resource, MetaWaylandOutput *wayland_output, MetaLogicalMonitor *logical_monitor, gboolean need_all_events, gboolean *pending_done_event) { MetaRectangle new_layout; MetaRectangle old_layout; MetaLogicalMonitor *old_logical_monitor; MetaMonitor *monitor; gboolean need_done; int version; need_done = FALSE; old_logical_monitor = wayland_output->logical_monitor; old_layout = meta_logical_monitor_get_layout (old_logical_monitor); new_layout = meta_logical_monitor_get_layout (logical_monitor); if (need_all_events || old_layout.x != new_layout.x || old_layout.y != new_layout.y) { zxdg_output_v1_send_logical_position (resource, new_layout.x, new_layout.y); need_done = TRUE; } if (need_all_events || old_layout.width != new_layout.width || old_layout.height != new_layout.height) { zxdg_output_v1_send_logical_size (resource, new_layout.width, new_layout.height); need_done = TRUE; } version = wl_resource_get_version (resource); monitor = pick_main_monitor (logical_monitor); if (need_all_events && version >= ZXDG_OUTPUT_V1_NAME_SINCE_VERSION) { const char *name; name = meta_monitor_get_connector (monitor); zxdg_output_v1_send_name (resource, name); } if (need_all_events && version >= ZXDG_OUTPUT_V1_DESCRIPTION_SINCE_VERSION) { const char *description; description = meta_monitor_get_display_name (monitor); zxdg_output_v1_send_description (resource, description); } if (pending_done_event && need_done) *pending_done_event = TRUE; } static void meta_xdg_output_manager_get_xdg_output (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *output) { struct wl_resource *xdg_output_resource; MetaWaylandOutput *wayland_output; int xdg_output_version; int wl_output_version; xdg_output_resource = wl_resource_create (client, &zxdg_output_v1_interface, wl_resource_get_version (resource), id); wayland_output = wl_resource_get_user_data (output); if (!wayland_output) return; wl_resource_set_implementation (xdg_output_resource, &meta_xdg_output_interface, wayland_output, meta_xdg_output_destructor); wayland_output->xdg_output_resources = g_list_prepend (wayland_output->xdg_output_resources, xdg_output_resource); if (!wayland_output->logical_monitor) return; send_xdg_output_events (xdg_output_resource, wayland_output, wayland_output->logical_monitor, TRUE, NULL); xdg_output_version = wl_resource_get_version (xdg_output_resource); wl_output_version = wl_resource_get_version (output); if (xdg_output_version < NO_XDG_OUTPUT_DONE_SINCE_VERSION) zxdg_output_v1_send_done (xdg_output_resource); else if (wl_output_version >= WL_OUTPUT_DONE_SINCE_VERSION) wl_output_send_done (output); } static void meta_xdg_output_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zxdg_output_manager_v1_interface meta_xdg_output_manager_interface = { meta_xdg_output_manager_destroy, meta_xdg_output_manager_get_xdg_output, }; static void bind_xdg_output_manager (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zxdg_output_manager_v1_interface, version, id); wl_resource_set_implementation (resource, &meta_xdg_output_manager_interface, NULL, NULL); } void meta_wayland_outputs_init (MetaWaylandCompositor *compositor) { MetaMonitorManager *monitors; monitors = meta_monitor_manager_get (); g_signal_connect (monitors, "monitors-changed-internal", G_CALLBACK (on_monitors_changed), compositor); compositor->outputs = g_hash_table_new_full (g_int64_hash, g_int64_equal, NULL, wayland_output_destroy_notify); compositor->outputs = meta_wayland_compositor_update_outputs (compositor, monitors); wl_global_create (compositor->wayland_display, &zxdg_output_manager_v1_interface, META_ZXDG_OUTPUT_V1_VERSION, NULL, bind_xdg_output_manager); } muffin-6.4.1/src/wayland/meta-wayland-region.h0000664000175000017500000000275714723361714020220 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Endless Mobile * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef META_WAYLAND_REGION_H #define META_WAYLAND_REGION_H #include #include #include #include "wayland/meta-wayland-types.h" MetaWaylandRegion * meta_wayland_region_create (MetaWaylandCompositor *compositor, struct wl_client *client, struct wl_resource *compositor_resource, guint32 id); cairo_region_t * meta_wayland_region_peek_cairo_region (MetaWaylandRegion *region); #endif /* META_WAYLAND_REGION_H */ muffin-6.4.1/src/wayland/meta-wayland-actor-surface.h0000664000175000017500000000432714723361714021466 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_ACTOR_SURFACE_H #define META_WAYLAND_ACTOR_SURFACE_H #include "wayland/meta-wayland-surface.h" #define META_TYPE_WAYLAND_ACTOR_SURFACE (meta_wayland_actor_surface_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaWaylandActorSurface, meta_wayland_actor_surface, META, WAYLAND_ACTOR_SURFACE, MetaWaylandSurfaceRole) struct _MetaWaylandActorSurfaceClass { MetaWaylandSurfaceRoleClass parent_class; double (* get_geometry_scale) (MetaWaylandActorSurface *actor_surface); void (* sync_actor_state) (MetaWaylandActorSurface *actor_surface); }; void meta_wayland_actor_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface); double meta_wayland_actor_surface_get_geometry_scale (MetaWaylandActorSurface *actor_surface); META_EXPORT_TEST MetaSurfaceActor * meta_wayland_actor_surface_get_actor (MetaWaylandActorSurface *actor_surface); void meta_wayland_actor_surface_reset_actor (MetaWaylandActorSurface *actor_surface); void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface, MetaWaylandSurfaceState *pending); void meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface, uint32_t timestamp_ms); #endif /* META_WAYLAND_ACTOR_SURFACE_H */ muffin-6.4.1/src/wayland/meta-wayland-text-input-legacy.h0000664000175000017500000000320314723361714022303 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_GTK_TEXT_INPUT_H #define META_WAYLAND_GTK_TEXT_INPUT_H #include #include "meta/window.h" #include "wayland/meta-wayland-types.h" typedef struct _MetaWaylandGtkTextInput MetaWaylandGtkTextInput; MetaWaylandGtkTextInput * meta_wayland_gtk_text_input_new (MetaWaylandSeat *seat); void meta_wayland_gtk_text_input_destroy (MetaWaylandGtkTextInput *text_input); gboolean meta_wayland_gtk_text_input_init (MetaWaylandCompositor *compositor); void meta_wayland_gtk_text_input_set_focus (MetaWaylandGtkTextInput *text_input, MetaWaylandSurface *surface); gboolean meta_wayland_gtk_text_input_handle_event (MetaWaylandGtkTextInput *text_input, const ClutterEvent *event); #endif /* META_WAYLAND_GTK_TEXT_INPUT_H */ muffin-6.4.1/src/wayland/meta-wayland-inhibit-shortcuts-dialog.c0000664000175000017500000001373214723361714023642 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "compositor/compositor-private.h" #include "core/window-private.h" #include "wayland/meta-wayland-inhibit-shortcuts-dialog.h" #include "wayland/meta-wayland.h" #include "wayland/meta-window-wayland.h" static GQuark quark_surface_inhibit_shortcuts_data = 0; typedef struct _InhibitShortcutsData { MetaWaylandSurface *surface; MetaWaylandSeat *seat; MetaInhibitShortcutsDialog *dialog; gulong response_handler_id; gboolean has_last_response; gboolean request_canceled; MetaInhibitShortcutsDialogResponse last_response; } InhibitShortcutsData; static InhibitShortcutsData * surface_inhibit_shortcuts_data_get (MetaWaylandSurface *surface) { return g_object_get_qdata (G_OBJECT (surface), quark_surface_inhibit_shortcuts_data); } static void surface_inhibit_shortcuts_data_set (MetaWaylandSurface *surface, InhibitShortcutsData *data) { g_object_set_qdata (G_OBJECT (surface), quark_surface_inhibit_shortcuts_data, data); } static void surface_inhibit_shortcuts_data_destroy_dialog (InhibitShortcutsData *data) { g_clear_signal_handler (&data->response_handler_id, data->dialog); meta_inhibit_shortcuts_dialog_hide (data->dialog); g_clear_object (&data->dialog); } static void surface_inhibit_shortcuts_data_free (InhibitShortcutsData *data) { if (data->dialog) surface_inhibit_shortcuts_data_destroy_dialog (data); g_free (data); } static void on_surface_destroyed (MetaWaylandSurface *surface, InhibitShortcutsData *data) { surface_inhibit_shortcuts_data_free (data); g_object_set_qdata (G_OBJECT (surface), quark_surface_inhibit_shortcuts_data, NULL); } static void inhibit_shortcuts_dialog_response_apply (InhibitShortcutsData *data) { if (data->last_response == META_INHIBIT_SHORTCUTS_DIALOG_RESPONSE_ALLOW) meta_wayland_surface_inhibit_shortcuts (data->surface, data->seat); else if (meta_wayland_surface_is_shortcuts_inhibited (data->surface, data->seat)) meta_wayland_surface_restore_shortcuts (data->surface, data->seat); } static void inhibit_shortcuts_dialog_response_cb (MetaInhibitShortcutsDialog *dialog, MetaInhibitShortcutsDialogResponse response, InhibitShortcutsData *data) { data->last_response = response; data->has_last_response = TRUE; /* If the request was canceled, we don't need to apply the choice made */ if (!data->request_canceled) inhibit_shortcuts_dialog_response_apply (data); meta_inhibit_shortcuts_dialog_hide (data->dialog); surface_inhibit_shortcuts_data_destroy_dialog (data); } static InhibitShortcutsData * meta_wayland_surface_ensure_inhibit_shortcuts_dialog (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { InhibitShortcutsData *data; MetaWindow *window; MetaDisplay *display; MetaInhibitShortcutsDialog *dialog; data = surface_inhibit_shortcuts_data_get (surface); if (data) return data; data = g_new0 (InhibitShortcutsData, 1); surface_inhibit_shortcuts_data_set (surface, data); g_signal_connect (surface, "destroy", G_CALLBACK (on_surface_destroyed), data); window = meta_wayland_surface_get_toplevel_window (surface); display = window->display; dialog = meta_compositor_create_inhibit_shortcuts_dialog (display->compositor, window); data->surface = surface; data->seat = seat; data->dialog = dialog; data->response_handler_id = g_signal_connect (dialog, "response", G_CALLBACK (inhibit_shortcuts_dialog_response_cb), data); return data; } void meta_wayland_surface_show_inhibit_shortcuts_dialog (MetaWaylandSurface *surface, MetaWaylandSeat *seat) { InhibitShortcutsData *data; g_return_if_fail (META_IS_WAYLAND_SURFACE (surface)); data = surface_inhibit_shortcuts_data_get (surface); if (data && data->has_last_response) { /* The dialog was shown before for this surface but is not showing * anymore, reuse the last user response. */ inhibit_shortcuts_dialog_response_apply (data); return; } data = meta_wayland_surface_ensure_inhibit_shortcuts_dialog (surface, seat); /* This is a new request */ data->request_canceled = FALSE; meta_inhibit_shortcuts_dialog_show (data->dialog); } void meta_wayland_surface_cancel_inhibit_shortcuts_dialog (MetaWaylandSurface *surface) { InhibitShortcutsData *data; g_return_if_fail (META_IS_WAYLAND_SURFACE (surface)); /* The closure notify will take care of actually hiding the dialog */ data = surface_inhibit_shortcuts_data_get (surface); g_return_if_fail (data); /* Keep the dialog on screen, but mark the request as canceled */ data->request_canceled = TRUE; } void meta_wayland_surface_inhibit_shortcuts_dialog_init (void) { quark_surface_inhibit_shortcuts_data = g_quark_from_static_string ("-meta-wayland-surface-inhibit-shortcuts-data"); } muffin-6.4.1/src/wayland/meta-wayland-subsurface.c0000664000175000017500000004535214723361714021070 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-subsurface.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-wayland.h" #include "wayland/meta-wayland.h" #include "wayland/meta-wayland-actor-surface.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-window-wayland.h" struct _MetaWaylandSubsurface { MetaWaylandActorSurface parent; }; G_DEFINE_TYPE (MetaWaylandSubsurface, meta_wayland_subsurface, META_TYPE_WAYLAND_ACTOR_SURFACE) static void transform_subsurface_position (MetaWaylandSurface *surface, int *x, int *y) { do { *x += surface->sub.x; *y += surface->sub.y; surface = surface->sub.parent; } while (surface); } static void sync_actor_subsurface_state (MetaWaylandSurface *surface) { ClutterActor *actor = CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface)); MetaWindow *toplevel_window; int x, y; toplevel_window = meta_wayland_surface_get_toplevel_window (surface); if (!toplevel_window) return; if (toplevel_window->client_type == META_WINDOW_CLIENT_TYPE_X11) return; x = y = 0; transform_subsurface_position (surface, &x, &y); clutter_actor_set_position (actor, x, y); clutter_actor_set_reactive (actor, TRUE); if (surface->buffer_ref.buffer) clutter_actor_show (actor); else clutter_actor_hide (actor); } static gboolean is_child (MetaWaylandSurface *surface, MetaWaylandSurface *sibling) { if (surface->sub.parent == sibling) return TRUE; else return FALSE; } static gboolean is_sibling (MetaWaylandSurface *surface, MetaWaylandSurface *sibling) { if (surface->sub.parent == sibling->sub.parent) return TRUE; else return FALSE; } static gboolean is_surface_effectively_synchronized (MetaWaylandSurface *surface) { return meta_wayland_surface_should_cache_state (surface); } void meta_wayland_subsurface_parent_state_applied (MetaWaylandSubsurface *subsurface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (subsurface); MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (subsurface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (surface->sub.pending_pos) { surface->sub.x = surface->sub.pending_x; surface->sub.y = surface->sub.pending_y; surface->sub.pending_pos = FALSE; } if (is_surface_effectively_synchronized (surface)) meta_wayland_surface_apply_cached_state (surface); meta_wayland_actor_surface_sync_actor_state (actor_surface); } void meta_wayland_subsurface_union_geometry (MetaWaylandSubsurface *subsurface, int parent_x, int parent_y, MetaRectangle *out_geometry) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (subsurface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaRectangle geometry; MetaWaylandSurface *subsurface_surface; geometry = (MetaRectangle) { .x = surface->offset_x + surface->sub.x, .y = surface->offset_y + surface->sub.y, .width = meta_wayland_surface_get_width (surface), .height = meta_wayland_surface_get_height (surface), }; meta_rectangle_union (out_geometry, &geometry, out_geometry); META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) { MetaWaylandSubsurface *subsurface; subsurface = META_WAYLAND_SUBSURFACE (subsurface_surface->role); meta_wayland_subsurface_union_geometry (subsurface, parent_x + geometry.x, parent_y + geometry.y, out_geometry); } } static void meta_wayland_subsurface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_subsurface_parent_class); surface->dnd.funcs = meta_wayland_data_device_get_drag_dest_funcs (); surface_role_class->assigned (surface_role); } static MetaWaylandSurface * meta_wayland_subsurface_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent = surface->sub.parent; if (parent) return meta_wayland_surface_get_toplevel (parent); else return NULL; } static gboolean meta_wayland_subsurface_should_cache_state (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent; if (surface->sub.synchronous) return TRUE; parent = surface->sub.parent; if (parent) return meta_wayland_surface_should_cache_state (parent); return TRUE; } static void meta_wayland_subsurface_notify_subsurface_state_changed (MetaWaylandSurfaceRole *surface_role) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent = surface->sub.parent; if (parent) return meta_wayland_surface_notify_subsurface_state_changed (parent); } static double meta_wayland_subsurface_get_geometry_scale (MetaWaylandActorSurface *actor_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandSurface *parent = surface->sub.parent; if (parent) { MetaWaylandActorSurface *parent_actor; parent_actor = META_WAYLAND_ACTOR_SURFACE (surface->sub.parent->role); return meta_wayland_actor_surface_get_geometry_scale (parent_actor); } else { return 1; } } static void meta_wayland_subsurface_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (meta_wayland_subsurface_parent_class); MetaWaylandSurface *toplevel_surface; toplevel_surface = meta_wayland_surface_get_toplevel (surface); if (toplevel_surface && meta_wayland_surface_get_window (toplevel_surface)) actor_surface_class->sync_actor_state (actor_surface); sync_actor_subsurface_state (surface); } static void meta_wayland_subsurface_init (MetaWaylandSubsurface *role) { } static void meta_wayland_subsurface_class_init (MetaWaylandSubsurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_CLASS (klass); surface_role_class->assigned = meta_wayland_subsurface_assigned; surface_role_class->get_toplevel = meta_wayland_subsurface_get_toplevel; surface_role_class->should_cache_state = meta_wayland_subsurface_should_cache_state; surface_role_class->notify_subsurface_state_changed = meta_wayland_subsurface_notify_subsurface_state_changed; actor_surface_class->get_geometry_scale = meta_wayland_subsurface_get_geometry_scale; actor_surface_class->sync_actor_state = meta_wayland_subsurface_sync_actor_state; } static void unparent_actor (MetaWaylandSurface *surface) { ClutterActor *actor; ClutterActor *parent_actor; actor = CLUTTER_ACTOR (meta_wayland_surface_get_actor (surface)); if (!actor) return; parent_actor = clutter_actor_get_parent (actor); clutter_actor_remove_child (parent_actor, actor); } static void wl_subsurface_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); g_node_unlink (surface->subsurface_branch_node); unparent_actor (surface); if (surface->sub.parent) { wl_list_remove (&surface->sub.parent_destroy_listener.link); surface->sub.parent = NULL; } surface->wl_subsurface = NULL; } static void wl_subsurface_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void wl_subsurface_set_position (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); surface->sub.pending_x = x; surface->sub.pending_y = y; surface->sub.pending_pos = TRUE; } static gboolean is_valid_sibling (MetaWaylandSurface *surface, MetaWaylandSurface *sibling) { if (is_child (surface, sibling)) return TRUE; if (is_sibling (surface, sibling)) return TRUE; return FALSE; } static void subsurface_handle_pending_surface_destroyed (struct wl_listener *listener, void *data) { MetaWaylandSubsurfacePlacementOp *op = wl_container_of (listener, op, surface_destroy_listener); op->surface = NULL; } static void subsurface_handle_pending_sibling_destroyed (struct wl_listener *listener, void *data) { MetaWaylandSubsurfacePlacementOp *op = wl_container_of (listener, op, sibling_destroy_listener); op->sibling = NULL; } void meta_wayland_subsurface_placement_op_free (MetaWaylandSubsurfacePlacementOp *op) { if (op->surface) wl_list_remove (&op->surface_destroy_listener.link); if (op->sibling) wl_list_remove (&op->sibling_destroy_listener.link); g_free (op); } static void queue_subsurface_placement (MetaWaylandSurface *surface, MetaWaylandSurface *sibling, MetaWaylandSubsurfacePlacement placement) { MetaWaylandSurface *parent = surface->sub.parent; MetaWaylandSubsurfacePlacementOp *op = g_new0 (MetaWaylandSubsurfacePlacementOp, 1); op->placement = placement; op->surface = surface; op->sibling = sibling; op->surface_destroy_listener.notify = subsurface_handle_pending_surface_destroyed; op->sibling_destroy_listener.notify = subsurface_handle_pending_sibling_destroyed; wl_resource_add_destroy_listener (surface->resource, &op->surface_destroy_listener); wl_resource_add_destroy_listener (sibling->resource, &op->sibling_destroy_listener); parent->pending_state->subsurface_placement_ops = g_slist_append (parent->pending_state->subsurface_placement_ops, op); } static void wl_subsurface_place_above (struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *sibling = wl_resource_get_user_data (sibling_resource); if (!is_valid_sibling (surface, sibling)) { wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "wl_subsurface::place_above: wl_surface@%d is " "not a valid parent or sibling", wl_resource_get_id (sibling->resource)); return; } queue_subsurface_placement (surface, sibling, META_WAYLAND_SUBSURFACE_PLACEMENT_ABOVE); } static void wl_subsurface_place_below (struct wl_client *client, struct wl_resource *resource, struct wl_resource *sibling_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); MetaWaylandSurface *sibling = wl_resource_get_user_data (sibling_resource); if (!is_valid_sibling (surface, sibling)) { wl_resource_post_error (resource, WL_SUBSURFACE_ERROR_BAD_SURFACE, "wl_subsurface::place_below: wl_surface@%d is " "not a valid parent or sibling", wl_resource_get_id (sibling->resource)); return; } queue_subsurface_placement (surface, sibling, META_WAYLAND_SUBSURFACE_PLACEMENT_BELOW); } static void wl_subsurface_set_sync (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); surface->sub.synchronous = TRUE; } static void wl_subsurface_set_desync (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (resource); gboolean was_effectively_synchronized; was_effectively_synchronized = is_surface_effectively_synchronized (surface); surface->sub.synchronous = FALSE; if (was_effectively_synchronized && !is_surface_effectively_synchronized (surface)) meta_wayland_surface_apply_cached_state (surface); } static const struct wl_subsurface_interface meta_wayland_wl_subsurface_interface = { wl_subsurface_destroy, wl_subsurface_set_position, wl_subsurface_place_above, wl_subsurface_place_below, wl_subsurface_set_sync, wl_subsurface_set_desync, }; static void wl_subcompositor_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void surface_handle_parent_surface_destroyed (struct wl_listener *listener, void *data) { MetaWaylandSurface *surface = wl_container_of (listener, surface, sub.parent_destroy_listener); surface->sub.parent = NULL; } static gboolean is_same_or_ancestor (MetaWaylandSurface *surface, MetaWaylandSurface *other_surface) { if (surface == other_surface) return TRUE; if (other_surface->sub.parent) return is_same_or_ancestor (surface, other_surface->sub.parent); return FALSE; } static void wl_subcompositor_get_subsurface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource, struct wl_resource *parent_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandSurface *parent = wl_resource_get_user_data (parent_resource); MetaWindow *toplevel_window; if (surface->wl_subsurface) { wl_resource_post_error (surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "wl_subcompositor::get_subsurface already requested"); return; } if (is_same_or_ancestor (surface, parent)) { wl_resource_post_error (resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "Circular relationship between wl_surface@%d " "and parent surface wl_surface@%d", wl_resource_get_id (surface->resource), wl_resource_get_id (parent->resource)); return; } if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_SUBSURFACE, NULL)) { wl_resource_post_error (resource, WL_SUBCOMPOSITOR_ERROR_BAD_SURFACE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } toplevel_window = meta_wayland_surface_get_toplevel_window (parent); if (toplevel_window && toplevel_window->client_type == META_WINDOW_CLIENT_TYPE_X11) g_warning ("XWayland subsurfaces not currently supported"); surface->wl_subsurface = wl_resource_create (client, &wl_subsurface_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (surface->wl_subsurface, &meta_wayland_wl_subsurface_interface, surface, wl_subsurface_destructor); surface->sub.synchronous = TRUE; surface->sub.parent = parent; surface->sub.parent_destroy_listener.notify = surface_handle_parent_surface_destroyed; wl_resource_add_destroy_listener (parent->resource, &surface->sub.parent_destroy_listener); g_node_append (parent->subsurface_branch_node, surface->subsurface_branch_node); meta_wayland_surface_notify_subsurface_state_changed (parent); } static const struct wl_subcompositor_interface meta_wayland_subcompositor_interface = { wl_subcompositor_destroy, wl_subcompositor_get_subsurface, }; static void bind_subcompositor (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &wl_subcompositor_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_subcompositor_interface, data, NULL); } void meta_wayland_subsurfaces_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &wl_subcompositor_interface, META_WL_SUBCOMPOSITOR_VERSION, compositor, bind_subcompositor) == NULL) g_error ("Failed to register a global wl-subcompositor object"); } muffin-6.4.1/src/wayland/meta-cursor-sprite-wayland.h0000664000175000017500000000246414723361714021551 0ustar fabiofabio/* * Copyright 2013, 2018 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #ifndef META_CURSOR_SPRITE_WAYLAND_H #define META_CURSOR_SPRITE_WAYLAND_H #include #include "backends/meta-cursor.h" #include "wayland/meta-wayland-surface.h" #define META_TYPE_CURSOR_SPRITE_WAYLAND meta_cursor_sprite_wayland_get_type () G_DECLARE_FINAL_TYPE (MetaCursorSpriteWayland, meta_cursor_sprite_wayland, META, CURSOR_SPRITE_WAYLAND, MetaCursorSprite) MetaCursorSpriteWayland * meta_cursor_sprite_wayland_new (MetaWaylandSurface *surface); MetaWaylandBuffer * meta_cursor_sprite_wayland_get_buffer (MetaCursorSpriteWayland *sprite_wayland); #endif /* META_CURSOR_SPRITE_WAYLAND_H */ muffin-6.4.1/src/wayland/meta-wayland-tablet-cursor-surface.c0000664000175000017500000000241714723361714023135 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-tablet-cursor-surface.h" struct _MetaWaylandTabletCursorSurface { MetaWaylandCursorSurface parent; }; G_DEFINE_TYPE (MetaWaylandTabletCursorSurface, meta_wayland_tablet_cursor_surface, META_TYPE_WAYLAND_CURSOR_SURFACE) static void meta_wayland_tablet_cursor_surface_init (MetaWaylandTabletCursorSurface *role) { } static void meta_wayland_tablet_cursor_surface_class_init (MetaWaylandTabletCursorSurfaceClass *klass) { } muffin-6.4.1/src/wayland/meta-pointer-lock-wayland.c0000664000175000017500000000467714723361714021341 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ /** * SECTION:meta-pointer-lock-wayland * @title: MetaPointerLockWayland * @short_description: A #MetaPointerConstraint implementing pointer lock. * * A MetaPointerLockConstraint implements the client pointer constraint "pointer * lock": the cursor should not make any movement. */ #include "config.h" #include "wayland/meta-pointer-lock-wayland.h" #include #include "backends/meta-pointer-constraint.h" struct _MetaPointerLockWayland { MetaPointerConstraint parent; }; G_DEFINE_TYPE (MetaPointerLockWayland, meta_pointer_lock_wayland, META_TYPE_POINTER_CONSTRAINT); static void meta_pointer_lock_wayland_constrain (MetaPointerConstraint *constraint, ClutterInputDevice *device, guint32 time, float prev_x, float prev_y, float *x, float *y) { *x = prev_x; *y = prev_y; } MetaPointerConstraint * meta_pointer_lock_wayland_new (void) { return g_object_new (META_TYPE_POINTER_LOCK_WAYLAND, NULL); } static void meta_pointer_lock_wayland_init (MetaPointerLockWayland *lock_wayland) { } static void meta_pointer_lock_wayland_class_init (MetaPointerLockWaylandClass *klass) { MetaPointerConstraintClass *pointer_constraint_class = META_POINTER_CONSTRAINT_CLASS (klass); pointer_constraint_class->constrain = meta_pointer_lock_wayland_constrain; } muffin-6.4.1/src/wayland/meta-wayland-keyboard.h0000664000175000017500000001224014723361714020521 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ /* * Copyright © 2008-2011 Kristian Høgsberg * Copyright © 2012 Collabora, Ltd. * * Permission to use, copy, modify, distribute, and sell this software and * its documentation for any purpose is hereby granted without fee, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of the copyright holders not be used in * advertising or publicity pertaining to distribution of the software * without specific, written prior permission. The copyright holders make * no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #ifndef META_WAYLAND_KEYBOARD_H #define META_WAYLAND_KEYBOARD_H #include #include #include "clutter/clutter.h" #include "wayland/meta-wayland-types.h" #define META_TYPE_WAYLAND_KEYBOARD (meta_wayland_keyboard_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandKeyboard, meta_wayland_keyboard, META, WAYLAND_KEYBOARD, MetaWaylandInputDevice) struct _MetaWaylandKeyboardGrabInterface { gboolean (*key) (MetaWaylandKeyboardGrab *grab, const ClutterEvent *event); void (*modifiers) (MetaWaylandKeyboardGrab *grab, ClutterModifierType modifiers); }; struct _MetaWaylandKeyboardGrab { const MetaWaylandKeyboardGrabInterface *interface; MetaWaylandKeyboard *keyboard; }; typedef struct { struct xkb_keymap *keymap; struct xkb_state *state; size_t keymap_size; char *keymap_string; } MetaWaylandXkbInfo; struct _MetaWaylandKeyboard { MetaWaylandInputDevice parent; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandSurface *focus_surface; struct wl_listener focus_surface_listener; uint32_t focus_serial; uint32_t key_down_keycode; uint32_t key_down_serial; uint32_t key_up_keycode; uint32_t key_up_serial; MetaWaylandXkbInfo xkb_info; enum xkb_state_component mods_changed; xkb_mod_mask_t kbd_a11y_latched_mods; xkb_mod_mask_t kbd_a11y_locked_mods; MetaWaylandKeyboardGrab *grab; MetaWaylandKeyboardGrab default_grab; GSettings *settings; }; void meta_wayland_keyboard_enable (MetaWaylandKeyboard *keyboard); void meta_wayland_keyboard_disable (MetaWaylandKeyboard *keyboard); void meta_wayland_keyboard_update (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event); gboolean meta_wayland_keyboard_handle_event (MetaWaylandKeyboard *keyboard, const ClutterKeyEvent *event); void meta_wayland_keyboard_update_key_state (MetaWaylandKeyboard *compositor, char *key_vector, int key_vector_len, int offset); void meta_wayland_keyboard_set_focus (MetaWaylandKeyboard *keyboard, MetaWaylandSurface *surface); struct wl_client * meta_wayland_keyboard_get_focus_client (MetaWaylandKeyboard *keyboard); void meta_wayland_keyboard_create_new_resource (MetaWaylandKeyboard *keyboard, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); gboolean meta_wayland_keyboard_can_popup (MetaWaylandKeyboard *keyboard, uint32_t serial); void meta_wayland_keyboard_start_grab (MetaWaylandKeyboard *keyboard, MetaWaylandKeyboardGrab *grab); void meta_wayland_keyboard_end_grab (MetaWaylandKeyboard *keyboard); #endif /* META_WAYLAND_KEYBOARD_H */ muffin-6.4.1/src/wayland/meta-wayland-data-offer-primary-legacy.h0000664000175000017500000000275514723361714023666 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_OFFER_PRIMARY_LEGACY_H #define META_WAYLAND_DATA_OFFER_PRIMARY_LEGACY_H #include "meta-wayland-data-offer.h" MetaWaylandDataOffer * meta_wayland_data_offer_primary_legacy_new (struct wl_resource *target); #endif /* META_WAYLAND_DATA_OFFER_PRIMARY_LEGACY_H */ muffin-6.4.1/src/wayland/meta-wayland-region.c0000664000175000017500000000612214723361714020201 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2014 Endless Mobile * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "wayland/meta-wayland-region.h" struct _MetaWaylandRegion { struct wl_resource *resource; cairo_region_t *region; }; static void wl_region_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void wl_region_add (struct wl_client *client, struct wl_resource *resource, gint32 x, gint32 y, gint32 width, gint32 height) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_rectangle_int_t rectangle = { x, y, width, height }; cairo_region_union_rectangle (region->region, &rectangle); } static void wl_region_subtract (struct wl_client *client, struct wl_resource *resource, gint32 x, gint32 y, gint32 width, gint32 height) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_rectangle_int_t rectangle = { x, y, width, height }; cairo_region_subtract_rectangle (region->region, &rectangle); } static const struct wl_region_interface meta_wayland_wl_region_interface = { wl_region_destroy, wl_region_add, wl_region_subtract }; static void wl_region_destructor (struct wl_resource *resource) { MetaWaylandRegion *region = wl_resource_get_user_data (resource); cairo_region_destroy (region->region); g_slice_free (MetaWaylandRegion, region); } MetaWaylandRegion * meta_wayland_region_create (MetaWaylandCompositor *compositor, struct wl_client *client, struct wl_resource *compositor_resource, guint32 id) { MetaWaylandRegion *region = g_slice_new0 (MetaWaylandRegion); region->resource = wl_resource_create (client, &wl_region_interface, wl_resource_get_version (compositor_resource), id); wl_resource_set_implementation (region->resource, &meta_wayland_wl_region_interface, region, wl_region_destructor); region->region = cairo_region_create (); return region; } cairo_region_t * meta_wayland_region_peek_cairo_region (MetaWaylandRegion *region) { return region->region; } muffin-6.4.1/src/wayland/meta-wayland-tablet.c0000664000175000017500000000745214723361714020200 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-tablet.h" #include #include #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-private.h" #include "tablet-unstable-v2-server-protocol.h" static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } MetaWaylandTablet * meta_wayland_tablet_new (ClutterInputDevice *device, MetaWaylandTabletSeat *tablet_seat) { MetaWaylandTablet *tablet; tablet = g_slice_new0 (MetaWaylandTablet); wl_list_init (&tablet->resource_list); tablet->device = device; tablet->tablet_seat = tablet_seat; return tablet; } void meta_wayland_tablet_free (MetaWaylandTablet *tablet) { struct wl_resource *resource, *next; wl_resource_for_each_safe (resource, next, &tablet->resource_list) { zwp_tablet_v2_send_removed (resource); wl_list_remove (wl_resource_get_link (resource)); wl_list_init (wl_resource_get_link (resource)); } g_slice_free (MetaWaylandTablet, tablet); } static void tablet_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zwp_tablet_v2_interface tablet_interface = { tablet_destroy }; void meta_wayland_tablet_notify (MetaWaylandTablet *tablet, struct wl_resource *resource) { ClutterInputDevice *device = tablet->device; const gchar *node_path, *vendor, *product; guint vid, pid; zwp_tablet_v2_send_name (resource, clutter_input_device_get_device_name (device)); node_path = clutter_input_device_get_device_node (device); if (node_path) zwp_tablet_v2_send_path (resource, node_path); vendor = clutter_input_device_get_vendor_id (device); product = clutter_input_device_get_product_id (device); if (vendor && sscanf (vendor, "%x", &vid) == 1 && product && sscanf (product, "%x", &pid) == 1) zwp_tablet_v2_send_id (resource, vid, pid); zwp_tablet_v2_send_done (resource); } struct wl_resource * meta_wayland_tablet_create_new_resource (MetaWaylandTablet *tablet, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_tablet_v2_interface, wl_resource_get_version (seat_resource), id); wl_resource_set_implementation (resource, &tablet_interface, tablet, unbind_resource); wl_resource_set_user_data (resource, tablet); wl_list_insert (&tablet->resource_list, wl_resource_get_link (resource)); return resource; } struct wl_resource * meta_wayland_tablet_lookup_resource (MetaWaylandTablet *tablet, struct wl_client *client) { return wl_resource_find_for_client (&tablet->resource_list, client); } muffin-6.4.1/src/wayland/meta-wayland-viewporter.c0000664000175000017500000001671414723361714021134 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2018-2019 Robert Mader * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "meta-wayland-viewporter.h" #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-subsurface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-versions.h" #include "viewporter-server-protocol.h" static void wp_viewport_destructor (struct wl_resource *resource) { MetaWaylandSurface *surface; MetaWaylandSurfaceState *pending; surface = wl_resource_get_user_data (resource); if (!surface) return; g_clear_signal_handler (&surface->viewport.destroy_handler_id, surface); pending = meta_wayland_surface_get_pending_state (surface); pending->viewport_src_rect.size.width = -1; pending->viewport_dst_width = -1; pending->has_new_viewport_src_rect = TRUE; pending->has_new_viewport_dst_size = TRUE; surface->viewport.resource = NULL; } static void on_surface_destroyed (MetaWaylandSurface *surface) { wl_resource_set_user_data (surface->viewport.resource, NULL); } static void wp_viewport_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void wp_viewport_set_source (struct wl_client *client, struct wl_resource *resource, wl_fixed_t src_x, wl_fixed_t src_y, wl_fixed_t src_width, wl_fixed_t src_height) { MetaWaylandSurface *surface; float new_x; float new_y; float new_width; float new_height; surface = wl_resource_get_user_data (resource); if (!surface) { wl_resource_post_error (resource, WP_VIEWPORT_ERROR_NO_SURFACE, "wl_surface for this viewport no longer exists"); return; } new_x = wl_fixed_to_double (src_x); new_y = wl_fixed_to_double (src_y); new_width = wl_fixed_to_double (src_width); new_height = wl_fixed_to_double (src_height); if ((new_x >= 0 && new_y >= 0 && new_width > 0 && new_height > 0) || (new_x == -1 && new_y == -1 && new_width == -1 && new_height == -1)) { MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->viewport_src_rect.origin.x = new_x; pending->viewport_src_rect.origin.y = new_y; pending->viewport_src_rect.size.width = new_width; pending->viewport_src_rect.size.height = new_height; pending->has_new_viewport_src_rect = TRUE; } else { wl_resource_post_error (resource, WP_VIEWPORT_ERROR_BAD_VALUE, "x and y values must be zero or positive and " "width and height valuest must be positive or " "all values must be -1 to unset the viewport"); } } static void wp_viewport_set_destination (struct wl_client *client, struct wl_resource *resource, int dst_width, int dst_height) { MetaWaylandSurface *surface; surface = wl_resource_get_user_data (resource); if (!surface) { wl_resource_post_error (resource, WP_VIEWPORT_ERROR_NO_SURFACE, "wl_surface for this viewport no longer exists"); return; } if ((dst_width > 0 && dst_height > 0) || (dst_width == -1 && dst_height == -1)) { MetaWaylandSurfaceState *pending; pending = meta_wayland_surface_get_pending_state (surface); pending->viewport_dst_width = dst_width; pending->viewport_dst_height = dst_height; pending->has_new_viewport_dst_size = TRUE; } else { wl_resource_post_error (resource, WP_VIEWPORT_ERROR_BAD_VALUE, "all values must be either positive or -1"); } } static const struct wp_viewport_interface meta_wayland_viewport_interface = { wp_viewport_destroy, wp_viewport_set_source, wp_viewport_set_destination, }; static void wp_viewporter_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void wp_viewporter_get_viewport (struct wl_client *client, struct wl_resource *resource, uint32_t viewport_id, struct wl_resource *surface_resource) { MetaWaylandSurface *surface; struct wl_resource *viewport_resource; surface = wl_resource_get_user_data (surface_resource); if (surface->viewport.resource) { wl_resource_post_error (resource, WP_VIEWPORTER_ERROR_VIEWPORT_EXISTS, "viewport already exists on surface"); return; } viewport_resource = wl_resource_create (client, &wp_viewport_interface, wl_resource_get_version (resource), viewport_id); wl_resource_set_implementation (viewport_resource, &meta_wayland_viewport_interface, surface, wp_viewport_destructor); surface->viewport.resource = viewport_resource; surface->viewport.destroy_handler_id = g_signal_connect (surface, "destroy", G_CALLBACK (on_surface_destroyed), NULL); } static const struct wp_viewporter_interface meta_wayland_viewporter_interface = { wp_viewporter_destroy, wp_viewporter_get_viewport, }; static void wp_viewporter_bind (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &wp_viewporter_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_viewporter_interface, data, NULL); } void meta_wayland_init_viewporter (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &wp_viewporter_interface, META_WP_VIEWPORTER_VERSION, compositor, wp_viewporter_bind) == NULL) g_error ("Failed to register a global wl-viewporter object"); } muffin-6.4.1/src/wayland/meta-wayland-gtk-shell.h0000664000175000017500000000222414723361714020614 0ustar fabiofabio/* * Copyright (C) 2016 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_GTK_SHELL_H #define META_WAYLAND_GTK_SHELL_H #include "wayland/meta-wayland.h" #define META_TYPE_WAYLAND_GTK_SHELL (meta_wayland_gtk_shell_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandGtkShell, meta_wayland_gtk_shell, META, WAYLAND_GTK_SHELL, GObject) void meta_wayland_init_gtk_shell (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_GTK_SHELL_H */ muffin-6.4.1/src/wayland/meta-wayland-data-offer.h0000664000175000017500000000431414723361714020734 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #ifndef META_WAYLAND_DATA_OFFER_H #define META_WAYLAND_DATA_OFFER_H #include "meta/meta-selection.h" #include "wayland/meta-wayland-data-source.h" struct _MetaWaylandDataOffer { struct wl_resource *resource; MetaWaylandDataSource *source; struct wl_listener source_destroy_listener; gboolean accepted; gboolean action_sent; uint32_t dnd_actions; enum wl_data_device_manager_dnd_action preferred_dnd_action; MetaSelectionType selection_type; }; MetaWaylandDataOffer * meta_wayland_data_offer_new (MetaSelectionType selection_type, MetaWaylandDataSource *source, struct wl_resource *resource); void meta_wayland_data_offer_update_action (MetaWaylandDataOffer *offer); struct wl_resource * meta_wayland_data_offer_get_resource (MetaWaylandDataOffer *offer); MetaWaylandDataSource * meta_wayland_data_offer_get_source (MetaWaylandDataOffer *offer); #endif /* META_WAYLAND_DATA_OFFER_H */ muffin-6.4.1/src/wayland/meta-wayland-seat.c0000664000175000017500000004145414723361714017661 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2013 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #include "config.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-data-device-primary-legacy.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-tablet-seat.h" #include "wayland/meta-wayland-versions.h" #define CAPABILITY_ENABLED(prev, cur, capability) ((cur & (capability)) && !(prev & (capability))) #define CAPABILITY_DISABLED(prev, cur, capability) ((prev & (capability)) && !(cur & (capability))) static void unbind_resource (struct wl_resource *resource) { wl_list_remove (wl_resource_get_link (resource)); } static void seat_get_pointer (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandPointer *pointer = seat->pointer; if (meta_wayland_seat_has_pointer (seat)) meta_wayland_pointer_create_new_resource (pointer, client, resource, id); } static void seat_get_keyboard (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandKeyboard *keyboard = seat->keyboard; if (meta_wayland_seat_has_keyboard (seat)) meta_wayland_keyboard_create_new_resource (keyboard, client, resource, id); } static void seat_get_touch (struct wl_client *client, struct wl_resource *resource, uint32_t id) { MetaWaylandSeat *seat = wl_resource_get_user_data (resource); MetaWaylandTouch *touch = seat->touch; if (meta_wayland_seat_has_touch (seat)) meta_wayland_touch_create_new_resource (touch, client, resource, id); } static void seat_release (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct wl_seat_interface seat_interface = { seat_get_pointer, seat_get_keyboard, seat_get_touch, seat_release }; static void bind_seat (struct wl_client *client, void *data, guint32 version, guint32 id) { MetaWaylandSeat *seat = data; struct wl_resource *resource; resource = wl_resource_create (client, &wl_seat_interface, version, id); wl_resource_set_implementation (resource, &seat_interface, seat, unbind_resource); wl_list_insert (&seat->base_resource_list, wl_resource_get_link (resource)); wl_seat_send_capabilities (resource, seat->capabilities); if (version >= WL_SEAT_NAME_SINCE_VERSION) wl_seat_send_name (resource, "seat0"); } static uint32_t lookup_device_capabilities (ClutterSeat *seat) { GList *devices, *l; uint32_t capabilities = 0; devices = clutter_seat_list_devices (seat); for (l = devices; l; l = l->next) { ClutterInputDeviceType device_type; /* Only look for physical devices, master devices have rather generic * keyboard/pointer device types, which is not truly representative of * the slave devices connected to them. */ if (clutter_input_device_get_device_mode (l->data) == CLUTTER_INPUT_MODE_MASTER) continue; device_type = clutter_input_device_get_device_type (l->data); switch (device_type) { case CLUTTER_TOUCHPAD_DEVICE: case CLUTTER_POINTER_DEVICE: capabilities |= WL_SEAT_CAPABILITY_POINTER; break; case CLUTTER_KEYBOARD_DEVICE: capabilities |= WL_SEAT_CAPABILITY_KEYBOARD; break; case CLUTTER_TOUCHSCREEN_DEVICE: capabilities |= WL_SEAT_CAPABILITY_TOUCH; break; default: g_debug ("Ignoring device '%s' with unhandled type %d", clutter_input_device_get_device_name (l->data), device_type); break; } } g_list_free (devices); return capabilities; } static void meta_wayland_seat_set_capabilities (MetaWaylandSeat *seat, uint32_t flags) { struct wl_resource *resource; uint32_t prev_flags; prev_flags = seat->capabilities; if (prev_flags == flags) return; seat->capabilities = flags; if (CAPABILITY_ENABLED (prev_flags, flags, WL_SEAT_CAPABILITY_POINTER)) meta_wayland_pointer_enable (seat->pointer); else if (CAPABILITY_DISABLED (prev_flags, flags, WL_SEAT_CAPABILITY_POINTER)) meta_wayland_pointer_disable (seat->pointer); if (CAPABILITY_ENABLED (prev_flags, flags, WL_SEAT_CAPABILITY_KEYBOARD)) { MetaDisplay *display; meta_wayland_keyboard_enable (seat->keyboard); display = meta_get_display (); /* Post-initialization, ensure the input focus is in sync */ if (display) meta_display_sync_wayland_input_focus (display); } else if (CAPABILITY_DISABLED (prev_flags, flags, WL_SEAT_CAPABILITY_KEYBOARD)) meta_wayland_keyboard_disable (seat->keyboard); if (CAPABILITY_ENABLED (prev_flags, flags, WL_SEAT_CAPABILITY_TOUCH)) meta_wayland_touch_enable (seat->touch); else if (CAPABILITY_DISABLED (prev_flags, flags, WL_SEAT_CAPABILITY_TOUCH)) meta_wayland_touch_disable (seat->touch); /* Broadcast capability changes */ wl_resource_for_each (resource, &seat->base_resource_list) { wl_seat_send_capabilities (resource, flags); } } static void meta_wayland_seat_update_capabilities (MetaWaylandSeat *seat, ClutterSeat *clutter_seat) { uint32_t capabilities; capabilities = lookup_device_capabilities (clutter_seat); meta_wayland_seat_set_capabilities (seat, capabilities); } static void meta_wayland_seat_devices_updated (ClutterSeat *clutter_seat, ClutterInputDevice *input_device, MetaWaylandSeat *seat) { meta_wayland_seat_update_capabilities (seat, clutter_seat); } static MetaWaylandSeat * meta_wayland_seat_new (MetaWaylandCompositor *compositor, struct wl_display *display) { MetaWaylandSeat *seat = g_new0 (MetaWaylandSeat, 1); ClutterSeat *clutter_seat; wl_list_init (&seat->base_resource_list); seat->wl_display = display; seat->pointer = g_object_new (META_TYPE_WAYLAND_POINTER, "seat", seat, NULL); seat->keyboard = g_object_new (META_TYPE_WAYLAND_KEYBOARD, "seat", seat, NULL); seat->touch = g_object_new (META_TYPE_WAYLAND_TOUCH, "seat", seat, NULL); seat->text_input = meta_wayland_text_input_new (seat); seat->gtk_text_input = meta_wayland_gtk_text_input_new (seat); meta_wayland_data_device_init (&seat->data_device); meta_wayland_data_device_primary_init (&seat->primary_data_device); meta_wayland_data_device_primary_legacy_init (&seat->primary_legacy_data_device); clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); meta_wayland_seat_update_capabilities (seat, clutter_seat); g_signal_connect (clutter_seat, "device-added", G_CALLBACK (meta_wayland_seat_devices_updated), seat); g_signal_connect (clutter_seat, "device-removed", G_CALLBACK (meta_wayland_seat_devices_updated), seat); wl_global_create (display, &wl_seat_interface, META_WL_SEAT_VERSION, seat, bind_seat); meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); return seat; } void meta_wayland_seat_init (MetaWaylandCompositor *compositor) { compositor->seat = meta_wayland_seat_new (compositor, compositor->wayland_display); } void meta_wayland_seat_free (MetaWaylandSeat *seat) { ClutterSeat *clutter_seat; clutter_seat = clutter_backend_get_default_seat (clutter_get_default_backend ()); g_signal_handlers_disconnect_by_data (clutter_seat, seat); meta_wayland_seat_set_capabilities (seat, 0); g_object_unref (seat->pointer); g_object_unref (seat->keyboard); g_object_unref (seat->touch); meta_wayland_gtk_text_input_destroy (seat->gtk_text_input); meta_wayland_text_input_destroy (seat->text_input); g_free (seat); } static gboolean event_is_synthesized_crossing (const ClutterEvent *event) { ClutterInputDevice *device; if (event->type != CLUTTER_ENTER && event->type != CLUTTER_LEAVE) return FALSE; device = clutter_event_get_source_device (event); return clutter_input_device_get_device_mode (device) == CLUTTER_INPUT_MODE_MASTER; } static gboolean event_from_supported_hardware_device (MetaWaylandSeat *seat, const ClutterEvent *event) { ClutterInputDevice *input_device; ClutterInputMode input_mode; ClutterInputDeviceType device_type; gboolean hardware_device = FALSE; gboolean supported_device = FALSE; input_device = clutter_event_get_source_device (event); if (input_device == NULL) goto out; input_mode = clutter_input_device_get_device_mode (input_device); if (input_mode != CLUTTER_INPUT_MODE_SLAVE) goto out; hardware_device = TRUE; device_type = clutter_input_device_get_device_type (input_device); switch (device_type) { case CLUTTER_TOUCHPAD_DEVICE: case CLUTTER_POINTER_DEVICE: case CLUTTER_KEYBOARD_DEVICE: case CLUTTER_TOUCHSCREEN_DEVICE: supported_device = TRUE; break; default: supported_device = FALSE; break; } out: return hardware_device && supported_device; } void meta_wayland_seat_update (MetaWaylandSeat *seat, const ClutterEvent *event) { if (!(clutter_event_get_flags (event) & CLUTTER_EVENT_FLAG_INPUT_METHOD) && !event_from_supported_hardware_device (seat, event) && !event_is_synthesized_crossing (event)) return; switch (event->type) { case CLUTTER_MOTION: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: case CLUTTER_ENTER: case CLUTTER_LEAVE: if (meta_wayland_seat_has_pointer (seat)) meta_wayland_pointer_update (seat->pointer, event); break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: if (meta_wayland_seat_has_keyboard (seat)) meta_wayland_keyboard_update (seat->keyboard, (const ClutterKeyEvent *) event); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: if (meta_wayland_seat_has_touch (seat)) meta_wayland_touch_update (seat->touch, event); break; default: break; } } gboolean meta_wayland_seat_handle_event (MetaWaylandSeat *seat, const ClutterEvent *event) { if (!(clutter_event_get_flags (event) & CLUTTER_EVENT_FLAG_INPUT_METHOD) && !event_from_supported_hardware_device (seat, event)) return FALSE; switch (event->type) { case CLUTTER_MOTION: case CLUTTER_BUTTON_PRESS: case CLUTTER_BUTTON_RELEASE: case CLUTTER_SCROLL: case CLUTTER_TOUCHPAD_SWIPE: case CLUTTER_TOUCHPAD_PINCH: if (meta_wayland_seat_has_pointer (seat)) return meta_wayland_pointer_handle_event (seat->pointer, event); break; case CLUTTER_KEY_PRESS: case CLUTTER_KEY_RELEASE: if (meta_wayland_text_input_handle_event (seat->text_input, event)) return TRUE; if (meta_wayland_gtk_text_input_handle_event (seat->gtk_text_input, event)) return TRUE; if (meta_wayland_seat_has_keyboard (seat)) return meta_wayland_keyboard_handle_event (seat->keyboard, (const ClutterKeyEvent *) event); break; case CLUTTER_TOUCH_BEGIN: case CLUTTER_TOUCH_UPDATE: case CLUTTER_TOUCH_END: if (meta_wayland_seat_has_touch (seat)) return meta_wayland_touch_handle_event (seat->touch, event); break; case CLUTTER_IM_COMMIT: case CLUTTER_IM_DELETE: case CLUTTER_IM_PREEDIT: if (meta_wayland_text_input_handle_event (seat->text_input, event)) return TRUE; if (meta_wayland_gtk_text_input_handle_event (seat->gtk_text_input, event)) return TRUE; break; default: break; } return FALSE; } void meta_wayland_seat_repick (MetaWaylandSeat *seat) { if (!meta_wayland_seat_has_pointer (seat)) return; meta_wayland_pointer_repick (seat->pointer); } void meta_wayland_seat_set_input_focus (MetaWaylandSeat *seat, MetaWaylandSurface *surface) { MetaWaylandTabletSeat *tablet_seat; MetaWaylandCompositor *compositor = meta_wayland_compositor_get_default (); if (meta_wayland_seat_has_keyboard (seat)) { meta_wayland_keyboard_set_focus (seat->keyboard, surface); meta_wayland_data_device_set_keyboard_focus (&seat->data_device); meta_wayland_data_device_primary_set_keyboard_focus (&seat->primary_data_device); meta_wayland_data_device_primary_legacy_set_keyboard_focus (&seat->primary_legacy_data_device); } tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); meta_wayland_tablet_seat_set_pad_focus (tablet_seat, surface); meta_wayland_text_input_set_focus (seat->text_input, surface); meta_wayland_gtk_text_input_set_focus (seat->gtk_text_input, surface); } gboolean meta_wayland_seat_get_grab_info (MetaWaylandSeat *seat, MetaWaylandSurface *surface, uint32_t serial, gboolean require_pressed, gfloat *x, gfloat *y) { MetaWaylandCompositor *compositor; MetaWaylandTabletSeat *tablet_seat; GList *tools, *l; compositor = meta_wayland_compositor_get_default (); tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); tools = g_hash_table_get_values (tablet_seat->tools); if (meta_wayland_seat_has_touch (seat)) { ClutterEventSequence *sequence; sequence = meta_wayland_touch_find_grab_sequence (seat->touch, surface, serial); if (sequence) { meta_wayland_touch_get_press_coords (seat->touch, sequence, x, y); return TRUE; } } if (meta_wayland_seat_has_pointer (seat)) { if ((!require_pressed || seat->pointer->button_count > 0) && meta_wayland_pointer_can_grab_surface (seat->pointer, surface, serial)) { if (x) *x = seat->pointer->grab_x; if (y) *y = seat->pointer->grab_y; return TRUE; } } for (l = tools; l; l = l->next) { MetaWaylandTabletTool *tool = l->data; if ((!require_pressed || tool->button_count > 0) && meta_wayland_tablet_tool_can_grab_surface (tool, surface, serial)) { if (x) *x = tool->grab_x; if (y) *y = tool->grab_y; return TRUE; } } return FALSE; } gboolean meta_wayland_seat_can_popup (MetaWaylandSeat *seat, uint32_t serial) { MetaWaylandCompositor *compositor; MetaWaylandTabletSeat *tablet_seat; compositor = meta_wayland_compositor_get_default (); tablet_seat = meta_wayland_tablet_manager_ensure_seat (compositor->tablet_manager, seat); return (meta_wayland_pointer_can_popup (seat->pointer, serial) || meta_wayland_keyboard_can_popup (seat->keyboard, serial) || meta_wayland_touch_can_popup (seat->touch, serial) || meta_wayland_tablet_seat_can_popup (tablet_seat, serial)); } gboolean meta_wayland_seat_has_keyboard (MetaWaylandSeat *seat) { return (seat->capabilities & WL_SEAT_CAPABILITY_KEYBOARD) != 0; } gboolean meta_wayland_seat_has_pointer (MetaWaylandSeat *seat) { return (seat->capabilities & WL_SEAT_CAPABILITY_POINTER) != 0; } gboolean meta_wayland_seat_has_touch (MetaWaylandSeat *seat) { return (seat->capabilities & WL_SEAT_CAPABILITY_TOUCH) != 0; } muffin-6.4.1/src/wayland/meta-wayland-text-input.c0000664000175000017500000006200014723361714021034 0ustar fabiofabio/* * Copyright (C) 2017, 2018 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-text-input.h" #include #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-versions.h" #include "text-input-unstable-v3-server-protocol.h" #define META_TYPE_WAYLAND_TEXT_INPUT_FOCUS (meta_wayland_text_input_focus_get_type ()) typedef enum { META_WAYLAND_PENDING_STATE_NONE = 0, META_WAYLAND_PENDING_STATE_INPUT_RECT = 1 << 0, META_WAYLAND_PENDING_STATE_CONTENT_TYPE = 1 << 1, META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT = 1 << 2, META_WAYLAND_PENDING_STATE_CHANGE_CAUSE = 1 << 3, META_WAYLAND_PENDING_STATE_ENABLED = 1 << 4, } MetaWaylandTextInputPendingState; typedef struct _MetaWaylandTextInput MetaWaylandTextInput; struct _MetaWaylandTextInput { MetaWaylandSeat *seat; ClutterInputFocus *input_focus; struct wl_list resource_list; struct wl_list focus_resource_list; MetaWaylandSurface *surface; struct wl_listener surface_listener; MetaWaylandTextInputPendingState pending_state; GHashTable *resource_serials; struct { char *text; uint32_t cursor; uint32_t anchor; } surrounding; cairo_rectangle_int_t cursor_rect; uint32_t content_type_hint; uint32_t content_type_purpose; uint32_t text_change_cause; gboolean enabled; guint done_idle_id; }; struct _MetaWaylandTextInputFocus { ClutterInputFocus parent_instance; MetaWaylandTextInput *text_input; }; G_DECLARE_FINAL_TYPE (MetaWaylandTextInputFocus, meta_wayland_text_input_focus, META, WAYLAND_TEXT_INPUT_FOCUS, ClutterInputFocus) G_DEFINE_TYPE (MetaWaylandTextInputFocus, meta_wayland_text_input_focus, CLUTTER_TYPE_INPUT_FOCUS) static void meta_wayland_text_input_focus_request_surrounding (ClutterInputFocus *focus) { MetaWaylandTextInput *text_input; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; clutter_input_focus_set_surrounding (focus, text_input->surrounding.text, text_input->surrounding.cursor, text_input->surrounding.anchor); } static uint32_t lookup_serial (MetaWaylandTextInput *text_input, struct wl_resource *resource) { return GPOINTER_TO_UINT (g_hash_table_lookup (text_input->resource_serials, resource)); } static void increment_serial (MetaWaylandTextInput *text_input, struct wl_resource *resource) { uint32_t serial; serial = lookup_serial (text_input, resource); g_hash_table_insert (text_input->resource_serials, resource, GUINT_TO_POINTER (serial + 1)); } static void clutter_input_focus_send_done (ClutterInputFocus *focus) { MetaWaylandTextInput *text_input; struct wl_resource *resource; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_done (resource, lookup_serial (text_input, resource)); } } static gboolean done_idle_cb (gpointer user_data) { ClutterInputFocus *focus = user_data; MetaWaylandTextInput *text_input; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; clutter_input_focus_send_done (focus); text_input->done_idle_id = 0; return G_SOURCE_REMOVE; } static void meta_wayland_text_input_focus_defer_done (ClutterInputFocus *focus) { MetaWaylandTextInput *text_input; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; if (text_input->done_idle_id != 0) return; /* This operates on 2 principles: * - IM operations come as individual ClutterEvents * - We want to run .done after them all. The slightly lower * G_PRIORITY_DEFAULT + 1 priority should ensure we at least group * all events seen so far. * * FIXME: .done may be delayed indefinitely if there's a high enough * priority idle source in the main loop. It's unlikely that * recurring idles run at this high priority though. */ text_input->done_idle_id = g_idle_add_full (CLUTTER_PRIORITY_EVENTS + 1, done_idle_cb, focus, NULL); } static void meta_wayland_text_input_focus_flush_done (ClutterInputFocus *focus) { MetaWaylandTextInput *text_input; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; if (text_input->done_idle_id == 0) return; g_clear_handle_id (&text_input->done_idle_id, g_source_remove); clutter_input_focus_send_done (focus); } static void meta_wayland_text_input_focus_delete_surrounding (ClutterInputFocus *focus, int offset, guint len) { MetaWaylandTextInput *text_input; uint32_t before_length; uint32_t after_length; struct wl_resource *resource; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; before_length = ABS (MIN (offset, 0)); after_length = MAX (0, offset + len); g_warn_if_fail (ABS (offset) <= len); wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_delete_surrounding_text (resource, before_length, after_length); } meta_wayland_text_input_focus_defer_done (focus); } static void meta_wayland_text_input_focus_commit_text (ClutterInputFocus *focus, const gchar *text) { MetaWaylandTextInput *text_input; struct wl_resource *resource; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_preedit_string (resource, NULL, 0, 0); zwp_text_input_v3_send_commit_string (resource, text); } meta_wayland_text_input_focus_defer_done (focus); } static void meta_wayland_text_input_focus_set_preedit_text (ClutterInputFocus *focus, const gchar *text, guint cursor) { MetaWaylandTextInput *text_input; struct wl_resource *resource; gsize pos = 0; text_input = META_WAYLAND_TEXT_INPUT_FOCUS (focus)->text_input; if (text) pos = g_utf8_offset_to_pointer (text, cursor) - text; wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_preedit_string (resource, text, pos, pos); } meta_wayland_text_input_focus_defer_done (focus); } static void meta_wayland_text_input_focus_class_init (MetaWaylandTextInputFocusClass *klass) { ClutterInputFocusClass *focus_class = CLUTTER_INPUT_FOCUS_CLASS (klass); focus_class->request_surrounding = meta_wayland_text_input_focus_request_surrounding; focus_class->delete_surrounding = meta_wayland_text_input_focus_delete_surrounding; focus_class->commit_text = meta_wayland_text_input_focus_commit_text; focus_class->set_preedit_text = meta_wayland_text_input_focus_set_preedit_text; } static void meta_wayland_text_input_focus_init (MetaWaylandTextInputFocus *focus) { } static ClutterInputFocus * meta_wayland_text_input_focus_new (MetaWaylandTextInput *text_input) { MetaWaylandTextInputFocus *focus; focus = g_object_new (META_TYPE_WAYLAND_TEXT_INPUT_FOCUS, NULL); focus->text_input = text_input; return CLUTTER_INPUT_FOCUS (focus); } static void text_input_handle_focus_surface_destroy (struct wl_listener *listener, void *data) { MetaWaylandTextInput *text_input = wl_container_of (listener, text_input, surface_listener); meta_wayland_text_input_set_focus (text_input, NULL); } static void move_resources (struct wl_list *destination, struct wl_list *source) { wl_list_insert_list (destination, source); wl_list_init (source); } static void move_resources_for_client (struct wl_list *destination, struct wl_list *source, struct wl_client *client) { struct wl_resource *resource, *tmp; wl_resource_for_each_safe (resource, tmp, source) { if (wl_resource_get_client (resource) == client) { wl_list_remove (wl_resource_get_link (resource)); wl_list_insert (destination, wl_resource_get_link (resource)); } } } void meta_wayland_text_input_set_focus (MetaWaylandTextInput *text_input, MetaWaylandSurface *surface) { if (text_input->surface == surface) return; text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; if (text_input->surface) { if (!wl_list_empty (&text_input->focus_resource_list)) { ClutterInputFocus *focus = text_input->input_focus; ClutterInputMethod *input_method; struct wl_resource *resource; if (clutter_input_focus_is_focused (focus)) { input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); clutter_input_method_focus_out (input_method); } wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_leave (resource, text_input->surface->resource); } move_resources (&text_input->resource_list, &text_input->focus_resource_list); } wl_list_remove (&text_input->surface_listener.link); text_input->surface = NULL; } if (surface) { struct wl_resource *focus_surface_resource; text_input->surface = surface; focus_surface_resource = text_input->surface->resource; wl_resource_add_destroy_listener (focus_surface_resource, &text_input->surface_listener); move_resources_for_client (&text_input->focus_resource_list, &text_input->resource_list, wl_resource_get_client (focus_surface_resource)); if (!wl_list_empty (&text_input->focus_resource_list)) { struct wl_resource *resource; wl_resource_for_each (resource, &text_input->focus_resource_list) { zwp_text_input_v3_send_enter (resource, surface->resource); } } } } static void text_input_destructor (struct wl_resource *resource) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); g_hash_table_remove (text_input->resource_serials, resource); wl_list_remove (wl_resource_get_link (resource)); } static void text_input_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void text_input_enable (struct wl_client *client, struct wl_resource *resource) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); text_input->enabled = TRUE; text_input->pending_state |= META_WAYLAND_PENDING_STATE_ENABLED; } static void text_input_disable (struct wl_client *client, struct wl_resource *resource) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); text_input->enabled = FALSE; text_input->pending_state |= META_WAYLAND_PENDING_STATE_ENABLED; } static void text_input_set_surrounding_text (struct wl_client *client, struct wl_resource *resource, const char *text, int32_t cursor, int32_t anchor) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); g_free (text_input->surrounding.text); text_input->surrounding.text = g_strdup (text); text_input->surrounding.cursor = cursor; text_input->surrounding.anchor = anchor; text_input->pending_state |= META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT; } static void text_input_set_text_change_cause (struct wl_client *client, struct wl_resource *resource, uint32_t cause) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); text_input->text_change_cause = cause; text_input->pending_state |= META_WAYLAND_PENDING_STATE_CHANGE_CAUSE; } static ClutterInputContentHintFlags translate_hints (uint32_t hints) { ClutterInputContentHintFlags clutter_hints = 0; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_COMPLETION) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_COMPLETION; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_SPELLCHECK) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SPELLCHECK; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_AUTO_CAPITALIZATION) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_AUTO_CAPITALIZATION; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_LOWERCASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LOWERCASE; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_UPPERCASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_UPPERCASE; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_TITLECASE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_TITLECASE; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_HIDDEN_TEXT) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_HIDDEN_TEXT; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_SENSITIVE_DATA) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_SENSITIVE_DATA; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_LATIN) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_LATIN; if (hints & ZWP_TEXT_INPUT_V3_CONTENT_HINT_MULTILINE) clutter_hints |= CLUTTER_INPUT_CONTENT_HINT_MULTILINE; return clutter_hints; } static ClutterInputContentPurpose translate_purpose (uint32_t purpose) { switch (purpose) { case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL: return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_ALPHA: return CLUTTER_INPUT_CONTENT_PURPOSE_ALPHA; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DIGITS: return CLUTTER_INPUT_CONTENT_PURPOSE_DIGITS; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NUMBER: return CLUTTER_INPUT_CONTENT_PURPOSE_NUMBER; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PHONE: return CLUTTER_INPUT_CONTENT_PURPOSE_PHONE; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_URL: return CLUTTER_INPUT_CONTENT_PURPOSE_URL; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_EMAIL: return CLUTTER_INPUT_CONTENT_PURPOSE_EMAIL; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NAME: return CLUTTER_INPUT_CONTENT_PURPOSE_NAME; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_PASSWORD: return CLUTTER_INPUT_CONTENT_PURPOSE_PASSWORD; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATE: return CLUTTER_INPUT_CONTENT_PURPOSE_DATE; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TIME: return CLUTTER_INPUT_CONTENT_PURPOSE_TIME; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_DATETIME: return CLUTTER_INPUT_CONTENT_PURPOSE_DATETIME; case ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_TERMINAL: return CLUTTER_INPUT_CONTENT_PURPOSE_TERMINAL; } g_warn_if_reached (); return CLUTTER_INPUT_CONTENT_PURPOSE_NORMAL; } static void text_input_set_content_type (struct wl_client *client, struct wl_resource *resource, uint32_t hint, uint32_t purpose) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); if (!text_input->surface) return; text_input->content_type_hint = hint; text_input->content_type_purpose = purpose; text_input->pending_state |= META_WAYLAND_PENDING_STATE_CONTENT_TYPE; } static void text_input_set_cursor_rectangle (struct wl_client *client, struct wl_resource *resource, int32_t x, int32_t y, int32_t width, int32_t height) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); if (!text_input->surface) return; text_input->cursor_rect = (cairo_rectangle_int_t) { x, y, width, height }; text_input->pending_state |= META_WAYLAND_PENDING_STATE_INPUT_RECT; } static void meta_wayland_text_input_reset (MetaWaylandTextInput *text_input) { g_clear_pointer (&text_input->surrounding.text, g_free); text_input->content_type_hint = ZWP_TEXT_INPUT_V3_CONTENT_HINT_NONE; text_input->content_type_purpose = ZWP_TEXT_INPUT_V3_CONTENT_PURPOSE_NORMAL; text_input->text_change_cause = ZWP_TEXT_INPUT_V3_CHANGE_CAUSE_INPUT_METHOD; text_input->cursor_rect = (cairo_rectangle_int_t) { 0, 0, 0, 0 }; text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; } static void text_input_commit_state (struct wl_client *client, struct wl_resource *resource) { MetaWaylandTextInput *text_input = wl_resource_get_user_data (resource); ClutterInputFocus *focus = text_input->input_focus; gboolean enable_panel = FALSE; ClutterInputMethod *input_method; increment_serial (text_input, resource); if (text_input->surface == NULL) return; input_method = clutter_backend_get_input_method (clutter_get_default_backend ()); if (input_method && text_input->pending_state & META_WAYLAND_PENDING_STATE_ENABLED) { if (text_input->enabled) { if (!clutter_input_focus_is_focused (focus)) clutter_input_method_focus_in (input_method, focus); else enable_panel = TRUE; clutter_input_focus_set_can_show_preedit (focus, TRUE); } else if (clutter_input_focus_is_focused (focus)) { text_input->pending_state = META_WAYLAND_PENDING_STATE_NONE; clutter_input_focus_reset (text_input->input_focus); clutter_input_method_focus_out (input_method); } } if (!clutter_input_focus_is_focused (focus)) { meta_wayland_text_input_reset (text_input); return; } if (text_input->pending_state & META_WAYLAND_PENDING_STATE_CONTENT_TYPE) { clutter_input_focus_set_content_hints (text_input->input_focus, translate_hints (text_input->content_type_hint)); clutter_input_focus_set_content_purpose (text_input->input_focus, translate_purpose (text_input->content_type_purpose)); } if (text_input->pending_state & META_WAYLAND_PENDING_STATE_SURROUNDING_TEXT) { clutter_input_focus_set_surrounding (text_input->input_focus, text_input->surrounding.text, text_input->surrounding.cursor, text_input->surrounding.anchor); } if (text_input->pending_state & META_WAYLAND_PENDING_STATE_INPUT_RECT) { graphene_rect_t cursor_rect; float x1, y1, x2, y2; cairo_rectangle_int_t rect; rect = text_input->cursor_rect; meta_wayland_surface_get_absolute_coordinates (text_input->surface, rect.x, rect.y, &x1, &y1); meta_wayland_surface_get_absolute_coordinates (text_input->surface, rect.x + rect.width, rect.y + rect.height, &x2, &y2); graphene_rect_init (&cursor_rect, x1, y1, x2 - x1, y2 - y1); clutter_input_focus_set_cursor_location (text_input->input_focus, &cursor_rect); } meta_wayland_text_input_reset (text_input); if (enable_panel) clutter_input_focus_set_input_panel_state (focus, CLUTTER_INPUT_PANEL_STATE_ON); } static struct zwp_text_input_v3_interface meta_text_input_interface = { text_input_destroy, text_input_enable, text_input_disable, text_input_set_surrounding_text, text_input_set_text_change_cause, text_input_set_content_type, text_input_set_cursor_rectangle, text_input_commit_state, }; MetaWaylandTextInput * meta_wayland_text_input_new (MetaWaylandSeat *seat) { MetaWaylandTextInput *text_input; text_input = g_new0 (MetaWaylandTextInput, 1); text_input->input_focus = meta_wayland_text_input_focus_new (text_input); text_input->seat = seat; wl_list_init (&text_input->resource_list); wl_list_init (&text_input->focus_resource_list); text_input->surface_listener.notify = text_input_handle_focus_surface_destroy; text_input->resource_serials = g_hash_table_new (NULL, NULL); return text_input; } void meta_wayland_text_input_destroy (MetaWaylandTextInput *text_input) { meta_wayland_text_input_set_focus (text_input, NULL); g_object_unref (text_input->input_focus); g_hash_table_destroy (text_input->resource_serials); g_free (text_input); } static void meta_wayland_text_input_create_new_resource (MetaWaylandTextInput *text_input, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id) { struct wl_resource *text_input_resource; text_input_resource = wl_resource_create (client, &zwp_text_input_v3_interface, META_ZWP_TEXT_INPUT_V3_VERSION, id); wl_resource_set_implementation (text_input_resource, &meta_text_input_interface, text_input, text_input_destructor); if (text_input->surface && wl_resource_get_client (text_input->surface->resource) == client) { wl_list_insert (&text_input->focus_resource_list, wl_resource_get_link (text_input_resource)); zwp_text_input_v3_send_enter (text_input_resource, text_input->surface->resource); } else { wl_list_insert (&text_input->resource_list, wl_resource_get_link (text_input_resource)); } } static void text_input_manager_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void text_input_manager_get_text_input (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *seat_resource) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); meta_wayland_text_input_create_new_resource (seat->text_input, client, seat_resource, id); } static struct zwp_text_input_manager_v3_interface meta_text_input_manager_interface = { text_input_manager_destroy, text_input_manager_get_text_input, }; static void bind_text_input (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_text_input_manager_v3_interface, META_ZWP_TEXT_INPUT_V3_VERSION, id); wl_resource_set_implementation (resource, &meta_text_input_manager_interface, NULL, NULL); } gboolean meta_wayland_text_input_init (MetaWaylandCompositor *compositor) { return (wl_global_create (compositor->wayland_display, &zwp_text_input_manager_v3_interface, META_ZWP_TEXT_INPUT_V3_VERSION, compositor->seat->text_input, bind_text_input) != NULL); } gboolean meta_wayland_text_input_handle_event (MetaWaylandTextInput *text_input, const ClutterEvent *event) { if (!text_input->surface || !clutter_input_focus_is_focused (text_input->input_focus)) return FALSE; if ((event->type == CLUTTER_KEY_PRESS || event->type == CLUTTER_KEY_RELEASE) && clutter_event_get_flags (event) & CLUTTER_EVENT_FLAG_INPUT_METHOD) meta_wayland_text_input_focus_flush_done (text_input->input_focus); return clutter_input_focus_filter_event (text_input->input_focus, event); } muffin-6.4.1/src/wayland/meta-wayland-pointer-gestures.c0000664000175000017500000000527514723361714022245 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Author: Carlos Garnacho */ #include "config.h" #include "wayland/meta-wayland-pointer-gestures.h" #include #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-private.h" #include "pointer-gestures-unstable-v1-server-protocol.h" static void gestures_get_swipe (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *pointer_resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); meta_wayland_pointer_gesture_swipe_create_new_resource (pointer, client, resource, id); } static void gestures_get_pinch (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *pointer_resource) { MetaWaylandPointer *pointer = wl_resource_get_user_data (pointer_resource); meta_wayland_pointer_gesture_pinch_create_new_resource (pointer, client, resource, id); } static const struct zwp_pointer_gestures_v1_interface pointer_gestures_interface = { gestures_get_swipe, gestures_get_pinch }; static void bind_pointer_gestures (struct wl_client *client, void *data, guint32 version, guint32 id) { struct wl_resource *resource; resource = wl_resource_create (client, &zwp_pointer_gestures_v1_interface, version, id); wl_resource_set_implementation (resource, &pointer_gestures_interface, NULL, NULL); } void meta_wayland_pointer_gestures_init (MetaWaylandCompositor *compositor) { wl_global_create (compositor->wayland_display, &zwp_pointer_gestures_v1_interface, META_ZWP_POINTER_GESTURES_V1_VERSION, NULL, bind_pointer_gestures); } muffin-6.4.1/src/wayland/meta-wayland-tablet-seat.h0000664000175000017500000001024714723361714021133 0ustar fabiofabio/* * Wayland Support * * Copyright (C) 2015 Red Hat * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library. If not, see . * * Author: Carlos Garnacho */ #ifndef META_WAYLAND_TABLET_SEAT_H #define META_WAYLAND_TABLET_SEAT_H #include #include #include "wayland/meta-wayland-types.h" struct _MetaWaylandTabletSeat { MetaWaylandTabletManager *manager; MetaWaylandSeat *seat; ClutterSeat *clutter_seat; struct wl_list resource_list; GHashTable *tablets; GHashTable *tools; GHashTable *pads; }; MetaWaylandTabletSeat *meta_wayland_tablet_seat_new (MetaWaylandTabletManager *tablet_manager, MetaWaylandSeat *seat); void meta_wayland_tablet_seat_free (MetaWaylandTabletSeat *tablet_seat); struct wl_resource *meta_wayland_tablet_seat_create_new_resource (MetaWaylandTabletSeat *tablet_seat, struct wl_client *client, struct wl_resource *seat_resource, uint32_t id); struct wl_resource *meta_wayland_tablet_seat_lookup_resource (MetaWaylandTabletSeat *tablet_seat, struct wl_client *client); MetaWaylandTablet *meta_wayland_tablet_seat_lookup_tablet (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device); MetaWaylandTabletTool *meta_wayland_tablet_seat_lookup_tool (MetaWaylandTabletSeat *tablet_seat, ClutterInputDeviceTool *tool); MetaWaylandTabletPad *meta_wayland_tablet_seat_lookup_pad (MetaWaylandTabletSeat *tablet_seat, ClutterInputDevice *device); void meta_wayland_tablet_seat_update (MetaWaylandTabletSeat *tablet_seat, const ClutterEvent *event); gboolean meta_wayland_tablet_seat_handle_event (MetaWaylandTabletSeat *tablet_seat, const ClutterEvent *event); void meta_wayland_tablet_seat_notify_tool (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTabletTool *tool, struct wl_client *client); void meta_wayland_tablet_seat_set_pad_focus (MetaWaylandTabletSeat *tablet_seat, MetaWaylandSurface *surface); MetaWaylandTablet *meta_wayland_tablet_seat_lookup_paired_tablet (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTabletPad *pad); GList *meta_wayland_tablet_seat_lookup_paired_pads (MetaWaylandTabletSeat *tablet_seat, MetaWaylandTablet *tablet); gboolean meta_wayland_tablet_seat_can_popup (MetaWaylandTabletSeat *tablet_seat, uint32_t serial); #endif /* META_WAYLAND_TABLET_SEAT_H */ muffin-6.4.1/src/wayland/meta-wayland-wl-shell.c0000664000175000017500000006204314723361714020451 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2012-2013 Intel Corporation * Copyright (C) 2013-2015 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-wl-shell.h" #include "core/window-private.h" #include "wayland/meta-wayland-popup.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-seat.h" #include "wayland/meta-wayland-shell-surface.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-window-configuration.h" #include "wayland/meta-wayland.h" #include "wayland/meta-window-wayland.h" typedef enum { META_WL_SHELL_SURFACE_STATE_NONE, META_WL_SHELL_SURFACE_STATE_TOPLEVEL, META_WL_SHELL_SURFACE_STATE_POPUP, META_WL_SHELL_SURFACE_STATE_TRANSIENT, META_WL_SHELL_SURFACE_STATE_FULLSCREEN, META_WL_SHELL_SURFACE_STATE_MAXIMIZED, } MetaWlShellSurfaceState; struct _MetaWaylandWlShellSurface { MetaWaylandShellSurface parent; struct wl_resource *resource; MetaWlShellSurfaceState state; char *title; char *wm_class; MetaWaylandSurface *parent_surface; GList *children; MetaWaylandSeat *popup_seat; MetaWaylandPopup *popup; gboolean pending_popup; int x; int y; uint32_t emulated_ack_configure_serial; }; static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWaylandWlShellSurface, meta_wayland_wl_shell_surface, META_TYPE_WAYLAND_SHELL_SURFACE, G_IMPLEMENT_INTERFACE (META_TYPE_WAYLAND_POPUP_SURFACE, popup_surface_iface_init)); static MetaWaylandSurface * surface_from_wl_shell_surface_resource (struct wl_resource *resource) { MetaWaylandWlShellSurface *wl_shell_surface = wl_resource_get_user_data (resource); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (wl_shell_surface); return meta_wayland_surface_role_get_surface (surface_role); } static void sync_wl_shell_parent_relationship (MetaWaylandSurface *surface, MetaWaylandSurface *parent); static void wl_shell_surface_destructor (struct wl_resource *resource) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (wl_resource_get_user_data (resource)); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); GList *l; if (wl_shell_surface->popup) meta_wayland_popup_dismiss (wl_shell_surface->popup); for (l = wl_shell_surface->children; l; l = l->next) { MetaWaylandSurface *child_surface = l->data; MetaWaylandWlShellSurface *child_wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (child_surface->role); child_wl_shell_surface->parent_surface = NULL; if (child_wl_shell_surface->parent_surface == surface) { meta_wayland_popup_dismiss (child_wl_shell_surface->popup); child_wl_shell_surface->parent_surface = NULL; } } if (wl_shell_surface->parent_surface) { MetaWaylandSurface *parent_surface = wl_shell_surface->parent_surface; MetaWaylandWlShellSurface *parent_wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (parent_surface->role); parent_wl_shell_surface->children = g_list_remove (parent_wl_shell_surface->children, surface); } g_free (wl_shell_surface->title); g_free (wl_shell_surface->wm_class); if (wl_shell_surface->popup) { wl_shell_surface->parent_surface = NULL; meta_wayland_popup_dismiss (wl_shell_surface->popup); } wl_shell_surface->resource = NULL; } static void wl_shell_surface_pong (struct wl_client *client, struct wl_resource *resource, uint32_t serial) { MetaDisplay *display = meta_get_display (); meta_display_pong_for_serial (display, serial); } static void wl_shell_surface_move (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); gfloat x, y; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; meta_wayland_surface_begin_grab_op (surface, seat, META_GRAB_OP_MOVING, x, y); } static MetaGrabOp grab_op_for_wl_shell_surface_resize_edge (int edge) { MetaGrabOp op = META_GRAB_OP_WINDOW_BASE; if (edge & WL_SHELL_SURFACE_RESIZE_TOP) op |= META_GRAB_OP_WINDOW_DIR_NORTH; if (edge & WL_SHELL_SURFACE_RESIZE_BOTTOM) op |= META_GRAB_OP_WINDOW_DIR_SOUTH; if (edge & WL_SHELL_SURFACE_RESIZE_LEFT) op |= META_GRAB_OP_WINDOW_DIR_WEST; if (edge & WL_SHELL_SURFACE_RESIZE_RIGHT) op |= META_GRAB_OP_WINDOW_DIR_EAST; if (op == META_GRAB_OP_WINDOW_BASE) { g_warning ("invalid edge: %d", edge); return META_GRAB_OP_NONE; } return op; } static void wl_shell_surface_resize (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, uint32_t edges) { MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); gfloat x, y; MetaGrabOp grab_op; if (!meta_wayland_seat_get_grab_info (seat, surface, serial, TRUE, &x, &y)) return; grab_op = grab_op_for_wl_shell_surface_resize_edge (edges); meta_wayland_surface_begin_grab_op (surface, seat, grab_op, x, y); } static void wl_shell_surface_set_state (MetaWaylandSurface *surface, MetaWlShellSurfaceState state) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface->role); MetaWlShellSurfaceState old_state = wl_shell_surface->state; MetaWindow *window; wl_shell_surface->state = state; window = meta_wayland_surface_get_window (surface); if (window && old_state != state) { if (old_state == META_WL_SHELL_SURFACE_STATE_POPUP && wl_shell_surface->popup) { meta_wayland_popup_dismiss (wl_shell_surface->popup); wl_shell_surface->popup = NULL; } if (state == META_WL_SHELL_SURFACE_STATE_FULLSCREEN) meta_window_make_fullscreen (window); else meta_window_unmake_fullscreen (window); if (state == META_WL_SHELL_SURFACE_STATE_MAXIMIZED) meta_window_maximize (window, META_MAXIMIZE_BOTH); else meta_window_unmaximize (window, META_MAXIMIZE_BOTH); } } static void wl_shell_surface_set_toplevel (struct wl_client *client, struct wl_resource *resource) { MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); wl_shell_surface_set_state (surface, META_WL_SHELL_SURFACE_STATE_TOPLEVEL); } static void set_wl_shell_surface_parent (MetaWaylandSurface *surface, MetaWaylandSurface *parent) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface->role); MetaWaylandWlShellSurface *parent_wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (parent->role); if (wl_shell_surface->parent_surface) { MetaWaylandWlShellSurface *old_parent = META_WAYLAND_WL_SHELL_SURFACE (wl_shell_surface->parent_surface->role); old_parent->children = g_list_remove (old_parent->children, surface); } parent_wl_shell_surface->children = g_list_append (parent_wl_shell_surface->children, surface); wl_shell_surface->parent_surface = parent; } static void wl_shell_surface_set_transient (struct wl_client *client, struct wl_resource *resource, struct wl_resource *parent_resource, int32_t x, int32_t y, uint32_t flags) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (wl_resource_get_user_data (resource)); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource); wl_shell_surface_set_state (surface, META_WL_SHELL_SURFACE_STATE_TRANSIENT); set_wl_shell_surface_parent (surface, parent_surf); wl_shell_surface->x = x; wl_shell_surface->y = y; if (meta_wayland_surface_get_window (surface) && meta_wayland_surface_get_window (parent_surf)) sync_wl_shell_parent_relationship (surface, parent_surf); } static void wl_shell_surface_set_fullscreen (struct wl_client *client, struct wl_resource *resource, uint32_t method, uint32_t framerate, struct wl_resource *output) { MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); wl_shell_surface_set_state (surface, META_WL_SHELL_SURFACE_STATE_FULLSCREEN); } static void meta_wayland_wl_shell_surface_create_popup (MetaWaylandWlShellSurface *wl_shell_surface) { MetaWaylandPopupSurface *popup_surface = META_WAYLAND_POPUP_SURFACE (wl_shell_surface); MetaWaylandSeat *seat = wl_shell_surface->popup_seat; MetaWaylandPopup *popup; popup = meta_wayland_pointer_start_popup_grab (seat->pointer, popup_surface); if (!popup) { wl_shell_surface_send_popup_done (wl_shell_surface->resource); return; } wl_shell_surface->popup = popup; } static void wl_shell_surface_set_popup (struct wl_client *client, struct wl_resource *resource, struct wl_resource *seat_resource, uint32_t serial, struct wl_resource *parent_resource, int32_t x, int32_t y, uint32_t flags) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (wl_resource_get_user_data (resource)); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); MetaWaylandSurface *parent_surf = wl_resource_get_user_data (parent_resource); MetaWaylandSeat *seat = wl_resource_get_user_data (seat_resource); if (wl_shell_surface->popup) { wl_shell_surface->parent_surface = NULL; meta_wayland_popup_dismiss (wl_shell_surface->popup); } wl_shell_surface_set_state (surface, META_WL_SHELL_SURFACE_STATE_POPUP); if (!meta_wayland_seat_can_popup (seat, serial)) { wl_shell_surface_send_popup_done (resource); return; } set_wl_shell_surface_parent (surface, parent_surf); wl_shell_surface->popup_seat = seat; wl_shell_surface->x = x; wl_shell_surface->y = y; wl_shell_surface->pending_popup = TRUE; if (meta_wayland_surface_get_window (surface) && meta_wayland_surface_get_window (parent_surf)) sync_wl_shell_parent_relationship (surface, parent_surf); } static void wl_shell_surface_set_maximized (struct wl_client *client, struct wl_resource *resource, struct wl_resource *output) { MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); wl_shell_surface_set_state (surface, META_WL_SHELL_SURFACE_STATE_MAXIMIZED); } static void wl_shell_surface_set_title (struct wl_client *client, struct wl_resource *resource, const char *title) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (wl_resource_get_user_data (resource)); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); MetaWindow *window; g_clear_pointer (&wl_shell_surface->title, g_free); if (!g_utf8_validate (title, -1, NULL)) title = ""; wl_shell_surface->title = g_strdup (title); window = meta_wayland_surface_get_window (surface); if (window) meta_window_set_title (window, title); } static void wl_shell_surface_set_class (struct wl_client *client, struct wl_resource *resource, const char *class_) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (wl_resource_get_user_data (resource)); MetaWaylandSurface *surface = surface_from_wl_shell_surface_resource (resource); MetaWindow *window; g_clear_pointer (&wl_shell_surface->wm_class, g_free); if (!g_utf8_validate (class_, -1, NULL)) class_ = ""; wl_shell_surface->wm_class = g_strdup (class_); window = meta_wayland_surface_get_window (surface); if (window) meta_window_set_wm_class (window, class_, class_); } static const struct wl_shell_surface_interface meta_wayland_wl_shell_surface_interface = { wl_shell_surface_pong, wl_shell_surface_move, wl_shell_surface_resize, wl_shell_surface_set_toplevel, wl_shell_surface_set_transient, wl_shell_surface_set_fullscreen, wl_shell_surface_set_popup, wl_shell_surface_set_maximized, wl_shell_surface_set_title, wl_shell_surface_set_class, }; static void sync_wl_shell_parent_relationship (MetaWaylandSurface *surface, MetaWaylandSurface *parent) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface->role); MetaWindow *window; MetaWindow *parent_window; window = meta_wayland_surface_get_window (surface); parent_window = meta_wayland_surface_get_window (parent); meta_window_set_transient_for (window, parent_window); if (wl_shell_surface->state == META_WL_SHELL_SURFACE_STATE_POPUP || wl_shell_surface->state == META_WL_SHELL_SURFACE_STATE_TRANSIENT) meta_window_wayland_place_relative_to (window, parent_window, wl_shell_surface->x, wl_shell_surface->y); if (wl_shell_surface->state == META_WL_SHELL_SURFACE_STATE_POPUP && wl_shell_surface->pending_popup) { meta_wayland_wl_shell_surface_create_popup (wl_shell_surface); wl_shell_surface->pending_popup = FALSE; } } static void create_wl_shell_surface_window (MetaWaylandSurface *surface) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface->role); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (wl_shell_surface); MetaWaylandSurface *parent; MetaWindow *window; GList *l; window = meta_window_wayland_new (meta_get_display (), surface); meta_wayland_shell_surface_set_window (shell_surface, window); if (wl_shell_surface->title) meta_window_set_title (window, wl_shell_surface->title); if (wl_shell_surface->wm_class) meta_window_set_wm_class (window, wl_shell_surface->wm_class, wl_shell_surface->wm_class); parent = wl_shell_surface->parent_surface; if (parent && meta_wayland_surface_get_window (parent)) sync_wl_shell_parent_relationship (surface, parent); for (l = wl_shell_surface->children; l; l = l->next) { MetaWaylandSurface *child = l->data; if (meta_wayland_surface_get_window (child)) sync_wl_shell_parent_relationship (child, surface); } } static void wl_shell_get_shell_surface (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); MetaWaylandWlShellSurface *wl_shell_surface; if (META_IS_WAYLAND_WL_SHELL_SURFACE (surface->role) && META_WAYLAND_WL_SHELL_SURFACE (surface->role)->resource) { wl_resource_post_error (surface_resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "wl_shell::get_shell_surface already requested"); return; } if (!meta_wayland_surface_assign_role (surface, META_TYPE_WAYLAND_WL_SHELL_SURFACE, NULL)) { wl_resource_post_error (resource, WL_SHELL_ERROR_ROLE, "wl_surface@%d already has a different role", wl_resource_get_id (surface->resource)); return; } wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface->role); wl_shell_surface->resource = wl_resource_create (client, &wl_shell_surface_interface, wl_resource_get_version (resource), id); wl_resource_set_implementation (wl_shell_surface->resource, &meta_wayland_wl_shell_surface_interface, wl_shell_surface, wl_shell_surface_destructor); create_wl_shell_surface_window (surface); } static const struct wl_shell_interface meta_wayland_wl_shell_interface = { wl_shell_get_shell_surface, }; static void bind_wl_shell (struct wl_client *client, void *data, uint32_t version, uint32_t id) { struct wl_resource *resource; resource = wl_resource_create (client, &wl_shell_interface, version, id); wl_resource_set_implementation (resource, &meta_wayland_wl_shell_interface, data, NULL); } static void wl_shell_surface_role_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface_role); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (wl_shell_surface); MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaWindow *window = meta_wayland_surface_get_window (surface); cairo_region_t *input_region; MetaRectangle geom = { 0 }; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (meta_wayland_wl_shell_surface_parent_class); surface_role_class->apply_state (surface_role, pending); /* For wl_shell, it's equivalent to an unmap. Semantics * are poorly defined, so we can choose some that are * convenient for us. */ if (surface->buffer_ref.buffer && !window) { create_wl_shell_surface_window (surface); } else if (!surface->buffer_ref.buffer && window) { if (wl_shell_surface->popup) meta_wayland_popup_dismiss (wl_shell_surface->popup); else meta_wayland_shell_surface_destroy_window (shell_surface); return; } if (!window) return; if (!pending->newly_attached) return; input_region = meta_wayland_surface_calculate_input_region (surface); if (!cairo_region_is_empty (input_region)) { cairo_region_get_extents (input_region, &geom); cairo_region_destroy (input_region); } else { meta_wayland_shell_surface_calculate_geometry (shell_surface, &geom); } pending->has_acked_configure_serial = TRUE; pending->acked_configure_serial = wl_shell_surface->emulated_ack_configure_serial; meta_window_wayland_finish_move_resize (window, geom, pending); } static MetaWaylandSurface * wl_shell_surface_role_get_toplevel (MetaWaylandSurfaceRole *surface_role) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (surface_role); if (wl_shell_surface->state == META_WL_SHELL_SURFACE_STATE_POPUP && wl_shell_surface->parent_surface) return meta_wayland_surface_get_toplevel (wl_shell_surface->parent_surface); else return meta_wayland_surface_role_get_surface (surface_role); } static void wl_shell_surface_role_configure (MetaWaylandShellSurface *shell_surface, MetaWaylandWindowConfiguration *configuration) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface); if (!wl_shell_surface->resource) return; wl_shell_surface_send_configure (wl_shell_surface->resource, 0, configuration->width / configuration->scale, configuration->height / configuration->scale); wl_shell_surface->emulated_ack_configure_serial = configuration->serial; } static void wl_shell_surface_role_managed (MetaWaylandShellSurface *shell_surface, MetaWindow *window) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface); if (wl_shell_surface->state == META_WL_SHELL_SURFACE_STATE_POPUP) meta_window_set_type (window, META_WINDOW_DROPDOWN_MENU); } static void wl_shell_surface_role_ping (MetaWaylandShellSurface *shell_surface, guint32 serial) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (shell_surface); wl_shell_surface_send_ping (wl_shell_surface->resource, serial); } static void wl_shell_surface_role_close (MetaWaylandShellSurface *shell_surface) { /* Not supported by wl_shell_surface. */ } static void meta_wayland_wl_shell_surface_popup_done (MetaWaylandPopupSurface *popup_surface) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (popup_surface); wl_shell_surface_send_popup_done (wl_shell_surface->resource); } static void meta_wayland_wl_shell_surface_popup_dismiss (MetaWaylandPopupSurface *popup_surface) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (popup_surface); MetaWaylandShellSurface *shell_surface = META_WAYLAND_SHELL_SURFACE (wl_shell_surface); wl_shell_surface->popup = NULL; meta_wayland_shell_surface_destroy_window (shell_surface); } static MetaWaylandSurface * meta_wayland_wl_shell_surface_popup_get_surface (MetaWaylandPopupSurface *popup_surface) { MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (popup_surface); return meta_wayland_surface_role_get_surface (surface_role); } static void popup_surface_iface_init (MetaWaylandPopupSurfaceInterface *iface) { iface->done = meta_wayland_wl_shell_surface_popup_done; iface->dismiss = meta_wayland_wl_shell_surface_popup_dismiss; iface->get_surface = meta_wayland_wl_shell_surface_popup_get_surface; } static void wl_shell_surface_role_finalize (GObject *object) { MetaWaylandWlShellSurface *wl_shell_surface = META_WAYLAND_WL_SHELL_SURFACE (object); GObjectClass *object_class; g_clear_pointer (&wl_shell_surface->resource, wl_resource_destroy); object_class = G_OBJECT_CLASS (meta_wayland_wl_shell_surface_parent_class); object_class->finalize (object); } static void meta_wayland_wl_shell_surface_init (MetaWaylandWlShellSurface *wl_shell_surface) { } static void meta_wayland_wl_shell_surface_class_init (MetaWaylandWlShellSurfaceClass *klass) { GObjectClass *object_class; MetaWaylandSurfaceRoleClass *surface_role_class; MetaWaylandShellSurfaceClass *shell_surface_class; object_class = G_OBJECT_CLASS (klass); object_class->finalize = wl_shell_surface_role_finalize; surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); surface_role_class->apply_state = wl_shell_surface_role_apply_state; surface_role_class->get_toplevel = wl_shell_surface_role_get_toplevel; shell_surface_class = META_WAYLAND_SHELL_SURFACE_CLASS (klass); shell_surface_class->configure = wl_shell_surface_role_configure; shell_surface_class->managed = wl_shell_surface_role_managed; shell_surface_class->ping = wl_shell_surface_role_ping; shell_surface_class->close = wl_shell_surface_role_close; } void meta_wayland_wl_shell_init (MetaWaylandCompositor *compositor) { if (wl_global_create (compositor->wayland_display, &wl_shell_interface, META_WL_SHELL_VERSION, compositor, bind_wl_shell) == NULL) g_error ("Failed to register a global wl-shell object"); } muffin-6.4.1/src/wayland/meta-wayland-data-offer.c0000664000175000017500000002457714723361714020744 0ustar fabiofabio/* * Copyright © 2011 Kristian Høgsberg * 2020 Red Hat Inc. * * Permission to use, copy, modify, distribute, and sell this software and its * documentation for any purpose is hereby granted without fee, provided that * the above copyright notice appear in all copies and that both that copyright * notice and this permission notice appear in supporting documentation, and * that the name of the copyright holders not be used in advertising or * publicity pertaining to distribution of the software without specific, * written prior permission. The copyright holders make no representations * about the suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * THE COPYRIGHT HOLDERS DISCLAIM ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO * EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY SPECIAL, INDIRECT OR * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE * OF THIS SOFTWARE. */ #include "config.h" #include #include #include #include #include #include #include #include "meta/meta-selection.h" #include "wayland/meta-wayland-data-device.h" #include "wayland/meta-wayland-private.h" #include "meta-wayland-data-offer.h" #define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) static void data_offer_accept (struct wl_client *client, struct wl_resource *resource, guint32 serial, const char *mime_type) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); /* FIXME: Check that client is currently focused by the input * device that is currently dragging this data source. Should * this be a wl_data_device request? */ if (offer->source) { meta_wayland_data_source_target (offer->source, mime_type); meta_wayland_data_source_set_has_target (offer->source, mime_type != NULL); } offer->accepted = mime_type != NULL; } static void transfer_cb (MetaSelection *selection, GAsyncResult *res, GOutputStream *stream) { GError *error = NULL; if (!meta_selection_transfer_finish (selection, res, &error)) { g_warning ("Could not fetch selection data: %s", error->message); g_error_free (error); } g_output_stream_close (stream, NULL, NULL); g_object_unref (stream); } static void data_offer_receive (struct wl_client *client, struct wl_resource *resource, const char *mime_type, int32_t fd) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); MetaDisplay *display = meta_get_display (); MetaSelectionType selection_type; GList *mime_types; gboolean found; selection_type = offer->selection_type; mime_types = meta_selection_get_mimetypes (meta_display_get_selection (display), selection_type); found = g_list_find_custom (mime_types, mime_type, (GCompareFunc) g_strcmp0) != NULL; g_list_free_full (mime_types, g_free); if (found) { GOutputStream *stream; stream = g_unix_output_stream_new (fd, TRUE); meta_selection_transfer_async (meta_display_get_selection (display), selection_type, mime_type, -1, stream, NULL, (GAsyncReadyCallback) transfer_cb, stream); } else { close (fd); } } static void data_offer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void data_offer_finish (struct wl_client *client, struct wl_resource *resource) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); enum wl_data_device_manager_dnd_action current_action; if (!offer->source || offer != meta_wayland_data_source_get_current_offer (offer->source)) return; if (!offer->accepted || !offer->action_sent) { wl_resource_post_error (offer->resource, WL_DATA_OFFER_ERROR_INVALID_FINISH, "premature finish request"); return; } current_action = meta_wayland_data_source_get_current_action (offer->source); if (current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE || current_action == WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) { wl_resource_post_error (offer->resource, WL_DATA_OFFER_ERROR_INVALID_OFFER, "offer finished with an invalid action"); return; } meta_wayland_data_source_notify_finish (offer->source); } static void data_offer_set_actions (struct wl_client *client, struct wl_resource *resource, uint32_t dnd_actions, uint32_t preferred_action) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); if (dnd_actions & ~(ALL_ACTIONS)) { wl_resource_post_error (offer->resource, WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, "invalid actions mask %x", dnd_actions); return; } if (preferred_action && (!(preferred_action & dnd_actions) || __builtin_popcount (preferred_action) > 1)) { wl_resource_post_error (offer->resource, WL_DATA_OFFER_ERROR_INVALID_ACTION, "invalid action %x", preferred_action); return; } offer->dnd_actions = dnd_actions; offer->preferred_dnd_action = preferred_action; meta_wayland_data_offer_update_action (offer); } static const struct wl_data_offer_interface data_offer_interface = { data_offer_accept, data_offer_receive, data_offer_destroy, data_offer_finish, data_offer_set_actions, }; static void destroy_data_offer (struct wl_resource *resource) { MetaWaylandDataOffer *offer = wl_resource_get_user_data (resource); MetaWaylandSeat *seat; if (offer->source) { seat = meta_wayland_data_source_get_seat (offer->source); if (offer == meta_wayland_data_source_get_current_offer (offer->source)) { if (seat->data_device.dnd_data_source == offer->source) { if (wl_resource_get_version (offer->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) meta_wayland_data_source_notify_finish (offer->source); else if (meta_wayland_data_source_get_drop_performed (offer->source)) meta_wayland_data_source_cancel (offer->source); } else { meta_wayland_data_source_set_current_offer (offer->source, NULL); meta_wayland_data_source_set_has_target (offer->source, FALSE); } } g_object_remove_weak_pointer (G_OBJECT (offer->source), (gpointer *)&offer->source); offer->source = NULL; } meta_display_sync_wayland_input_focus (meta_get_display ()); g_slice_free (MetaWaylandDataOffer, offer); } MetaWaylandDataOffer * meta_wayland_data_offer_new (MetaSelectionType selection_type, MetaWaylandDataSource *source, struct wl_resource *target) { MetaWaylandDataOffer *offer; offer = g_slice_new0 (MetaWaylandDataOffer); offer->selection_type = selection_type; offer->resource = wl_resource_create (wl_resource_get_client (target), &wl_data_offer_interface, wl_resource_get_version (target), 0); wl_resource_set_implementation (offer->resource, &data_offer_interface, offer, destroy_data_offer); if (source) { offer->source = source; g_object_add_weak_pointer (G_OBJECT (source), (gpointer *)&offer->source); } return offer; } static enum wl_data_device_manager_dnd_action data_offer_choose_action (MetaWaylandDataOffer *offer) { MetaWaylandDataSource *source = offer->source; uint32_t actions, user_action, available_actions; if (wl_resource_get_version (offer->resource) < WL_DATA_OFFER_ACTION_SINCE_VERSION) return WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; meta_wayland_data_source_get_actions (source, &actions); user_action = meta_wayland_data_source_get_user_action (source); available_actions = actions & offer->dnd_actions; if (!available_actions) return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; /* If the user is forcing an action, go for it */ if ((user_action & available_actions) != 0) return user_action; /* If the dest side has a preferred DnD action, use it */ if ((offer->preferred_dnd_action & available_actions) != 0) return offer->preferred_dnd_action; /* Use the first found action, in bit order */ return 1 << (ffs (available_actions) - 1); } void meta_wayland_data_offer_update_action (MetaWaylandDataOffer *offer) { enum wl_data_device_manager_dnd_action current_action, action; MetaWaylandDataSource *source; if (!offer->source) return; source = offer->source; current_action = meta_wayland_data_source_get_current_action (source); action = data_offer_choose_action (offer); if (current_action == action) return; meta_wayland_data_source_set_current_action (source, action); if (!meta_wayland_data_source_get_in_ask (source) && wl_resource_get_version (offer->resource) >= WL_DATA_OFFER_ACTION_SINCE_VERSION) { wl_data_offer_send_action (offer->resource, action); offer->action_sent = TRUE; } } struct wl_resource * meta_wayland_data_offer_get_resource (MetaWaylandDataOffer *offer) { return offer->resource; } MetaWaylandDataSource * meta_wayland_data_offer_get_source (MetaWaylandDataOffer *offer) { return offer->source; } muffin-6.4.1/src/wayland/meta-wayland-xdg-foreign.c0000664000175000017500000003134314723361714021132 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2015 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jonas Ådahl */ #include "config.h" #include "wayland/meta-wayland-xdg-foreign.h" #include #include "core/util-private.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-versions.h" #include "wayland/meta-wayland-xdg-shell.h" #include "wayland/meta-wayland-legacy-xdg-shell.h" #include "xdg-foreign-unstable-v1-server-protocol.h" #define META_XDG_FOREIGN_HANDLE_LENGTH 32 typedef struct _MetaWaylandXdgExported MetaWaylandXdgExported; typedef struct _MetaWaylandXdgImported MetaWaylandXdgImported; typedef struct _MetaWaylandXdgForeign { MetaWaylandCompositor *compositor; GRand *rand; GHashTable *exported_surfaces; } MetaWaylandXdgForeign; struct _MetaWaylandXdgExported { MetaWaylandXdgForeign *foreign; struct wl_resource *resource; MetaWaylandSurface *surface; gulong surface_unmapped_handler_id; char *handle; GList *imported; }; struct _MetaWaylandXdgImported { MetaWaylandXdgForeign *foreign; struct wl_resource *resource; MetaWaylandSurface *parent_of; gulong parent_of_unmapped_handler_id; MetaWaylandXdgExported *exported; }; static void meta_wayland_xdg_imported_destroy (MetaWaylandXdgImported *imported); static void xdg_exporter_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void xdg_exported_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static const struct zxdg_exported_v1_interface meta_xdg_exported_interface = { xdg_exported_destroy, }; static void meta_wayland_xdg_exported_destroy (MetaWaylandXdgExported *exported) { MetaWaylandXdgForeign *foreign = exported->foreign; while (exported->imported) { MetaWaylandXdgImported *imported = exported->imported->data; zxdg_imported_v1_send_destroyed (imported->resource); meta_wayland_xdg_imported_destroy (imported); } g_clear_signal_handler (&exported->surface_unmapped_handler_id, exported->surface); wl_resource_set_user_data (exported->resource, NULL); g_hash_table_remove (foreign->exported_surfaces, exported->handle); g_free (exported->handle); g_free (exported); } static void xdg_exported_destructor (struct wl_resource *resource) { MetaWaylandXdgExported *exported = wl_resource_get_user_data (resource); if (exported) meta_wayland_xdg_exported_destroy (exported); } static void exported_surface_unmapped (MetaWaylandSurface *surface, MetaWaylandXdgExported *exported) { meta_wayland_xdg_exported_destroy (exported); } static void xdg_exporter_export (struct wl_client *client, struct wl_resource *resource, uint32_t id, struct wl_resource *surface_resource) { MetaWaylandXdgForeign *foreign = wl_resource_get_user_data (resource); MetaWaylandSurface *surface = wl_resource_get_user_data (surface_resource); struct wl_resource *xdg_exported_resource; MetaWaylandXdgExported *exported; char *handle; if (!surface->role || !meta_wayland_surface_get_window (surface) || !(META_IS_WAYLAND_XDG_SURFACE (surface->role) || META_IS_WAYLAND_ZXDG_SURFACE_V6 (surface->role))) { wl_resource_post_error (resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "exported surface had an invalid role"); return; } xdg_exported_resource = wl_resource_create (client, &zxdg_exported_v1_interface, wl_resource_get_version (resource), id); if (!xdg_exported_resource) { wl_client_post_no_memory (client); return; } exported = g_new0 (MetaWaylandXdgExported, 1); exported->foreign = foreign; exported->surface = surface; exported->resource = xdg_exported_resource; exported->surface_unmapped_handler_id = g_signal_connect (surface, "unmapped", G_CALLBACK (exported_surface_unmapped), exported); wl_resource_set_implementation (xdg_exported_resource, &meta_xdg_exported_interface, exported, xdg_exported_destructor); while (TRUE) { handle = meta_generate_random_id (foreign->rand, META_XDG_FOREIGN_HANDLE_LENGTH); if (!g_hash_table_contains (foreign->exported_surfaces, handle)) { g_hash_table_insert (foreign->exported_surfaces, handle, exported); break; } g_free (handle); } exported->handle = handle; zxdg_exported_v1_send_handle (xdg_exported_resource, handle); } static const struct zxdg_exporter_v1_interface meta_xdg_exporter_interface = { xdg_exporter_destroy, xdg_exporter_export, }; static void bind_xdg_exporter (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandXdgForeign *foreign = data; struct wl_resource *resource; resource = wl_resource_create (client, &zxdg_exporter_v1_interface, META_ZXDG_EXPORTER_V1_VERSION, id); if (resource == NULL) { wl_client_post_no_memory (client); return; } wl_resource_set_implementation (resource, &meta_xdg_exporter_interface, foreign, NULL); } static void xdg_imported_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void imported_parent_of_unmapped (MetaWaylandSurface *surface, MetaWaylandXdgImported *imported) { imported->parent_of = NULL; } static gboolean is_valid_child (MetaWaylandSurface *surface) { if (!surface) return TRUE; if (!surface->role) return FALSE; if (!META_IS_WAYLAND_XDG_TOPLEVEL (surface->role) && !META_IS_WAYLAND_ZXDG_TOPLEVEL_V6 (surface->role)) return FALSE; if (!meta_wayland_surface_get_window (surface)) return FALSE; return TRUE; } static void xdg_imported_set_parent_of (struct wl_client *client, struct wl_resource *resource, struct wl_resource *surface_resource) { MetaWaylandXdgImported *imported = wl_resource_get_user_data (resource); MetaWaylandSurface *surface; if (!imported) return; if (surface_resource) surface = wl_resource_get_user_data (surface_resource); else surface = NULL; if (!is_valid_child (surface)) { wl_resource_post_error (imported->resource, WL_DISPLAY_ERROR_INVALID_OBJECT, "set_parent_of was called with an invalid child"); return; } if (imported->parent_of) g_clear_signal_handler (&imported->parent_of_unmapped_handler_id, imported->parent_of); imported->parent_of = surface; if (surface) { MetaWindow *window; MetaWindow *exported_window; imported->parent_of_unmapped_handler_id = g_signal_connect (surface, "unmapped", G_CALLBACK (imported_parent_of_unmapped), imported); window = meta_wayland_surface_get_window (surface); exported_window = meta_wayland_surface_get_window (imported->exported->surface); meta_window_set_transient_for (window, exported_window); } } static const struct zxdg_imported_v1_interface meta_xdg_imported_interface = { xdg_imported_destroy, xdg_imported_set_parent_of, }; static void xdg_importer_destroy (struct wl_client *client, struct wl_resource *resource) { wl_resource_destroy (resource); } static void meta_wayland_xdg_imported_destroy (MetaWaylandXdgImported *imported) { MetaWaylandXdgExported *exported = imported->exported; exported->imported = g_list_remove (exported->imported, imported); if (imported->parent_of) { MetaWindow *window; g_clear_signal_handler (&imported->parent_of_unmapped_handler_id, imported->parent_of); window = meta_wayland_surface_get_window (imported->parent_of); if (window) meta_window_set_transient_for (window, NULL); } wl_resource_set_user_data (imported->resource, NULL); g_free (imported); } static void xdg_imported_destructor (struct wl_resource *resource) { MetaWaylandXdgImported *imported = wl_resource_get_user_data (resource); if (!imported) return; meta_wayland_xdg_imported_destroy (imported); } static void xdg_importer_import (struct wl_client *client, struct wl_resource *resource, uint32_t id, const char *handle) { MetaWaylandXdgForeign *foreign = wl_resource_get_user_data (resource); struct wl_resource *xdg_imported_resource; MetaWaylandXdgImported *imported; MetaWaylandXdgExported *exported; xdg_imported_resource = wl_resource_create (client, &zxdg_imported_v1_interface, wl_resource_get_version (resource), id); if (!xdg_imported_resource) { wl_client_post_no_memory (client); return; } wl_resource_set_implementation (xdg_imported_resource, &meta_xdg_imported_interface, NULL, xdg_imported_destructor); exported = g_hash_table_lookup (foreign->exported_surfaces, handle); if (!exported || (!META_IS_WAYLAND_XDG_SURFACE (exported->surface->role) && !META_IS_WAYLAND_ZXDG_SURFACE_V6 (exported->surface->role))) { zxdg_imported_v1_send_destroyed (xdg_imported_resource); return; } imported = g_new0 (MetaWaylandXdgImported, 1); imported->foreign = foreign; imported->exported = exported; imported->resource = xdg_imported_resource; wl_resource_set_user_data (xdg_imported_resource, imported); exported->imported = g_list_prepend (exported->imported, imported); } static const struct zxdg_importer_v1_interface meta_xdg_importer_interface = { xdg_importer_destroy, xdg_importer_import, }; static void bind_xdg_importer (struct wl_client *client, void *data, uint32_t version, uint32_t id) { MetaWaylandXdgForeign *foreign = data; struct wl_resource *resource; resource = wl_resource_create (client, &zxdg_importer_v1_interface, META_ZXDG_IMPORTER_V1_VERSION, id); if (resource == NULL) { wl_client_post_no_memory (client); return; } wl_resource_set_implementation (resource, &meta_xdg_importer_interface, foreign, NULL); } gboolean meta_wayland_xdg_foreign_init (MetaWaylandCompositor *compositor) { MetaWaylandXdgForeign *foreign; foreign = g_new0 (MetaWaylandXdgForeign, 1); foreign->compositor = compositor; foreign->rand = g_rand_new (); foreign->exported_surfaces = g_hash_table_new ((GHashFunc) g_str_hash, (GEqualFunc) g_str_equal); if (wl_global_create (compositor->wayland_display, &zxdg_exporter_v1_interface, 1, foreign, bind_xdg_exporter) == NULL) return FALSE; if (wl_global_create (compositor->wayland_display, &zxdg_importer_v1_interface, 1, foreign, bind_xdg_importer) == NULL) return FALSE; return TRUE; } muffin-6.4.1/src/wayland/meta-wayland-wl-shell.h0000664000175000017500000000240214723361714020447 0ustar fabiofabio/* * Copyright (C) 2013-2015 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef META_WAYLAND_WL_SHELL_H #define META_WAYLAND_WL_SHELL_H #include "wayland/meta-wayland-shell-surface.h" #define META_TYPE_WAYLAND_WL_SHELL_SURFACE (meta_wayland_wl_shell_surface_get_type ()) G_DECLARE_FINAL_TYPE (MetaWaylandWlShellSurface, meta_wayland_wl_shell_surface, META, WAYLAND_WL_SHELL_SURFACE, MetaWaylandShellSurface); void meta_wayland_wl_shell_init (MetaWaylandCompositor *compositor); #endif /* META_WAYLAND_WL_SHELL_H */ muffin-6.4.1/src/wayland/meta-wayland-actor-surface.c0000664000175000017500000003707714723361714021471 0ustar fabiofabio/* * Copyright (C) 2012,2013 Intel Corporation * Copyright (C) 2013-2017 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "wayland/meta-wayland-actor-surface.h" #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-wayland.h" #include "compositor/region-utils.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-surface.h" #include "wayland/meta-window-wayland.h" #include "wayland/meta-xwayland-surface.h" typedef struct _MetaWaylandActorSurfacePrivate MetaWaylandActorSurfacePrivate; struct _MetaWaylandActorSurfacePrivate { MetaSurfaceActor *actor; gulong actor_destroyed_handler_id; struct wl_list frame_callback_list; }; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaWaylandActorSurface, meta_wayland_actor_surface, META_TYPE_WAYLAND_SURFACE_ROLE) static void meta_wayland_actor_surface_constructed (GObject *object) { G_OBJECT_CLASS (meta_wayland_actor_surface_parent_class)->constructed (object); meta_wayland_actor_surface_reset_actor (META_WAYLAND_ACTOR_SURFACE (object)); } static void clear_surface_actor (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (!priv->actor) return; g_clear_signal_handler (&priv->actor_destroyed_handler_id, priv->actor); g_signal_handlers_disconnect_by_func (priv->actor, meta_wayland_surface_notify_geometry_changed, surface); g_clear_object (&priv->actor); } static void meta_wayland_actor_surface_dispose (GObject *object) { MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (object); MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); MetaWaylandFrameCallback *cb, *next; if (priv->actor) { clutter_actor_set_reactive (CLUTTER_ACTOR (priv->actor), FALSE); clear_surface_actor (actor_surface); } wl_list_for_each_safe (cb, next, &priv->frame_callback_list, link) wl_resource_destroy (cb->resource); G_OBJECT_CLASS (meta_wayland_actor_surface_parent_class)->dispose (object); } static void meta_wayland_actor_surface_assigned (MetaWaylandSurfaceRole *surface_role) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (META_WAYLAND_ACTOR_SURFACE (surface_role)); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (wl_list_empty (&surface->unassigned.pending_frame_callback_list)) return; wl_list_insert_list (priv->frame_callback_list.prev, &surface->unassigned.pending_frame_callback_list); wl_list_init (&surface->unassigned.pending_frame_callback_list); meta_wayland_compositor_add_frame_callback_surface (surface->compositor, surface); } void meta_wayland_actor_surface_queue_frame_callbacks (MetaWaylandActorSurface *actor_surface, MetaWaylandSurfaceState *pending) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); if (wl_list_empty (&pending->frame_callback_list)) return; wl_list_insert_list (priv->frame_callback_list.prev, &pending->frame_callback_list); wl_list_init (&pending->frame_callback_list); meta_wayland_compositor_add_frame_callback_surface (surface->compositor, surface); } void meta_wayland_actor_surface_emit_frame_callbacks (MetaWaylandActorSurface *actor_surface, uint32_t timestamp_ms) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); while (!wl_list_empty (&priv->frame_callback_list)) { MetaWaylandFrameCallback *callback = wl_container_of (priv->frame_callback_list.next, callback, link); wl_callback_send_done (callback->resource, timestamp_ms); wl_resource_destroy (callback->resource); } } double meta_wayland_actor_surface_get_geometry_scale (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_GET_CLASS (actor_surface); return actor_surface_class->get_geometry_scale (actor_surface); } static void meta_wayland_actor_surface_real_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); MetaWaylandSurfaceRole *surface_role = META_WAYLAND_SURFACE_ROLE (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaSurfaceActor *surface_actor; MetaShapedTexture *stex; MetaWaylandBuffer *buffer; cairo_rectangle_int_t surface_rect; MetaWaylandSurface *subsurface_surface; surface_actor = priv->actor; stex = meta_surface_actor_get_texture (surface_actor); buffer = surface->buffer_ref.buffer; if (buffer) { CoglSnippet *snippet; gboolean is_y_inverted; snippet = meta_wayland_buffer_create_snippet (buffer); is_y_inverted = meta_wayland_buffer_is_y_inverted (buffer); meta_shaped_texture_set_texture (stex, surface->texture); meta_shaped_texture_set_snippet (stex, snippet); meta_shaped_texture_set_is_y_inverted (stex, is_y_inverted); meta_shaped_texture_set_buffer_scale (stex, surface->scale); cogl_clear_object (&snippet); } else { meta_shaped_texture_set_texture (stex, NULL); } surface_rect = (cairo_rectangle_int_t) { .width = meta_wayland_surface_get_width (surface), .height = meta_wayland_surface_get_height (surface), }; if (surface->input_region) { cairo_region_t *input_region; input_region = cairo_region_copy (surface->input_region); cairo_region_intersect_rectangle (input_region, &surface_rect); meta_surface_actor_set_input_region (surface_actor, input_region); cairo_region_destroy (input_region); } else { meta_surface_actor_set_input_region (surface_actor, NULL); } if (!META_IS_XWAYLAND_SURFACE (surface_role)) { if (!meta_shaped_texture_has_alpha (stex)) { cairo_region_t *opaque_region; opaque_region = cairo_region_create_rectangle (&surface_rect); meta_surface_actor_set_opaque_region (surface_actor, opaque_region); cairo_region_destroy (opaque_region); } else if (surface->opaque_region) { cairo_region_t *opaque_region; opaque_region = cairo_region_copy (surface->opaque_region); cairo_region_intersect_rectangle (opaque_region, &surface_rect); meta_surface_actor_set_opaque_region (surface_actor, opaque_region); cairo_region_destroy (opaque_region); } else { meta_surface_actor_set_opaque_region (surface_actor, NULL); } } meta_surface_actor_set_transform (surface_actor, surface->buffer_transform); if (surface->viewport.has_src_rect) { meta_surface_actor_set_viewport_src_rect (surface_actor, &surface->viewport.src_rect); } else { meta_surface_actor_reset_viewport_src_rect (surface_actor); } if (surface->viewport.has_dst_size) { meta_surface_actor_set_viewport_dst_size (surface_actor, surface->viewport.dst_width, surface->viewport.dst_height); } else { meta_surface_actor_reset_viewport_dst_size (surface_actor); } META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) { MetaWaylandActorSurface *actor_surface; actor_surface = META_WAYLAND_ACTOR_SURFACE (subsurface_surface->role); meta_wayland_actor_surface_sync_actor_state (actor_surface); } } void meta_wayland_actor_surface_sync_actor_state (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfaceClass *actor_surface_class = META_WAYLAND_ACTOR_SURFACE_GET_CLASS (actor_surface); actor_surface_class->sync_actor_state (actor_surface); } static void meta_wayland_actor_surface_apply_state (MetaWaylandSurfaceRole *surface_role, MetaWaylandSurfaceState *pending) { MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface_role); MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); if (!wl_list_empty (&pending->frame_callback_list) && priv->actor && !meta_surface_actor_is_obscured (priv->actor)) { MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (surface_role); MetaBackend *backend = surface->compositor->backend; ClutterActor *stage = meta_backend_get_stage (backend); clutter_stage_schedule_update (CLUTTER_STAGE (stage)); } meta_wayland_actor_surface_queue_frame_callbacks (actor_surface, pending); meta_wayland_actor_surface_sync_actor_state (actor_surface); } static gboolean meta_wayland_actor_surface_is_on_logical_monitor (MetaWaylandSurfaceRole *surface_role, MetaLogicalMonitor *logical_monitor) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (META_WAYLAND_ACTOR_SURFACE (surface_role)); ClutterActor *actor = CLUTTER_ACTOR (priv->actor); float x, y, width, height; cairo_rectangle_int_t actor_rect; cairo_region_t *region; MetaRectangle logical_monitor_layout; gboolean is_on_monitor; if (!clutter_actor_is_mapped (actor)) return FALSE; clutter_actor_get_transformed_position (actor, &x, &y); clutter_actor_get_transformed_size (actor, &width, &height); actor_rect.x = (int) roundf (x); actor_rect.y = (int) roundf (y); actor_rect.width = (int) roundf (x + width) - actor_rect.x; actor_rect.height = (int) roundf (y + height) - actor_rect.y; /* Calculate the scaled surface actor region. */ region = cairo_region_create_rectangle (&actor_rect); logical_monitor_layout = meta_logical_monitor_get_layout (logical_monitor); cairo_region_intersect_rectangle (region, &((cairo_rectangle_int_t) { .x = logical_monitor_layout.x, .y = logical_monitor_layout.y, .width = logical_monitor_layout.width, .height = logical_monitor_layout.height, })); is_on_monitor = !cairo_region_is_empty (region); cairo_region_destroy (region); return is_on_monitor; } static void meta_wayland_actor_surface_get_relative_coordinates (MetaWaylandSurfaceRole *surface_role, float abs_x, float abs_y, float *out_sx, float *out_sy) { MetaWaylandActorSurface *actor_surface = META_WAYLAND_ACTOR_SURFACE (surface_role); MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); clutter_actor_transform_stage_point (CLUTTER_ACTOR (priv->actor), abs_x, abs_y, out_sx, out_sy); } static void meta_wayland_actor_surface_init (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); wl_list_init (&priv->frame_callback_list); } static void meta_wayland_actor_surface_class_init (MetaWaylandActorSurfaceClass *klass) { MetaWaylandSurfaceRoleClass *surface_role_class = META_WAYLAND_SURFACE_ROLE_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->constructed = meta_wayland_actor_surface_constructed; object_class->dispose = meta_wayland_actor_surface_dispose; surface_role_class->assigned = meta_wayland_actor_surface_assigned; surface_role_class->apply_state = meta_wayland_actor_surface_apply_state; surface_role_class->is_on_logical_monitor = meta_wayland_actor_surface_is_on_logical_monitor; surface_role_class->get_relative_coordinates = meta_wayland_actor_surface_get_relative_coordinates; klass->sync_actor_state = meta_wayland_actor_surface_real_sync_actor_state; } MetaSurfaceActor * meta_wayland_actor_surface_get_actor (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); return priv->actor; } static void on_actor_destroyed (ClutterActor *actor, MetaWaylandActorSurface *actor_surface) { clear_surface_actor (actor_surface); } void meta_wayland_actor_surface_reset_actor (MetaWaylandActorSurface *actor_surface) { MetaWaylandActorSurfacePrivate *priv = meta_wayland_actor_surface_get_instance_private (actor_surface); MetaWaylandSurface *surface = meta_wayland_surface_role_get_surface (META_WAYLAND_SURFACE_ROLE (actor_surface)); MetaWaylandSurface *subsurface_surface; META_WAYLAND_SURFACE_FOREACH_SUBSURFACE (surface, subsurface_surface) { MetaWaylandActorSurface *actor_surface; actor_surface = META_WAYLAND_ACTOR_SURFACE (subsurface_surface->role); meta_wayland_actor_surface_reset_actor (actor_surface); meta_wayland_actor_surface_sync_actor_state (actor_surface); } clear_surface_actor (actor_surface); priv->actor = g_object_ref_sink (meta_surface_actor_wayland_new (surface)); priv->actor_destroyed_handler_id = g_signal_connect (priv->actor, "destroy", G_CALLBACK (on_actor_destroyed), actor_surface); g_signal_connect_swapped (priv->actor, "notify::allocation", G_CALLBACK (meta_wayland_surface_notify_geometry_changed), surface); g_signal_connect_swapped (priv->actor, "notify::position", G_CALLBACK (meta_wayland_surface_notify_geometry_changed), surface); g_signal_connect_swapped (priv->actor, "notify::mapped", G_CALLBACK (meta_wayland_surface_notify_geometry_changed), surface); } muffin-6.4.1/src/wayland/meta-xwayland-grab-keyboard.h0000664000175000017500000000271214723361714021625 0ustar fabiofabio/* * Copyright (C) 2017 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Olivier Fourdan */ #ifndef META_XWAYLAND_GRAB_KEYBOARD_H #define META_XWAYLAND_GRAB_KEYBOARD_H #include #include "wayland/meta-wayland-types.h" #include "meta/window.h" #include "xwayland-keyboard-grab-unstable-v1-server-protocol.h" #define META_TYPE_XWAYLAND_KEYBOARD_ACTIVE_GRAB (meta_xwayland_keyboard_active_grab_get_type ()) G_DECLARE_FINAL_TYPE (MetaXwaylandKeyboardActiveGrab, meta_xwayland_keyboard_active_grab, META, XWAYLAND_KEYBOARD_ACTIVE_GRAB, GObject); gboolean meta_xwayland_grab_keyboard_init (MetaWaylandCompositor *compositor); #endif /* META_XWAYLAND_GRAB_KEYBOARD_H */ muffin-6.4.1/src/org.cinnamon.Muffin.ScreenCast.xml0000664000175000017500000000767414723361714021135 0ustar fabiofabio muffin-6.4.1/src/compositor/0000775000175000017500000000000014723361714014727 5ustar fabiofabiomuffin-6.4.1/src/compositor/plugins/0000775000175000017500000000000014723361714016410 5ustar fabiofabiomuffin-6.4.1/src/compositor/plugins/meson.build0000664000175000017500000000071314723361714020553 0ustar fabiofabiodefault_plugin_c_args = [ '-fPIC', '-DG_LOG_DOMAIN="mutter"', '-DGETTEXT_PACKAGE="@0@"'.format(meson.project_name()), ] default_plugin = shared_module('default', sources: ['default.c'], include_directories: mutter_includes, c_args: default_plugin_c_args, dependencies: [ glib_dep, gtk3_dep, json_glib_dep, cinnamon_desktop_dep, libmutter_clutter_dep, ], install_dir: join_paths(pkglibdir, 'plugins'), install: true, ) muffin-6.4.1/src/compositor/plugins/default.c0000664000175000017500000006726614723361714020221 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "meta/display.h" #include #include #include #include "clutter/clutter.h" #include "meta/meta-backend.h" #include "meta/meta-background-actor.h" #include "meta/meta-background-group.h" #include "meta/meta-monitor-manager.h" #include "meta/meta-plugin.h" #include "meta/util.h" #include "meta/window.h" #define DESTROY_TIMEOUT 100 #define MINIMIZE_TIMEOUT 250 #define MAP_TIMEOUT 250 #define SWITCH_TIMEOUT 500 #define ACTOR_DATA_KEY "MCCP-Default-actor-data" #define DISPLAY_TILE_PREVIEW_DATA_KEY "MCCP-Default-display-tile-preview-data" #define META_TYPE_DEFAULT_PLUGIN (meta_default_plugin_get_type ()) #define META_DEFAULT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPlugin)) #define META_DEFAULT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginClass)) #define META_IS_DEFAULT_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_DEFAULT_PLUGIN_TYPE)) #define META_IS_DEFAULT_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_DEFAULT_PLUGIN)) #define META_DEFAULT_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_DEFAULT_PLUGIN, MetaDefaultPluginClass)) typedef struct _MetaDefaultPlugin MetaDefaultPlugin; typedef struct _MetaDefaultPluginClass MetaDefaultPluginClass; typedef struct _MetaDefaultPluginPrivate MetaDefaultPluginPrivate; struct _MetaDefaultPlugin { MetaPlugin parent; MetaDefaultPluginPrivate *priv; }; struct _MetaDefaultPluginClass { MetaPluginClass parent_class; }; static GQuark actor_data_quark = 0; static GQuark display_tile_preview_data_quark = 0; static void start (MetaPlugin *plugin); static void minimize (MetaPlugin *plugin, MetaWindowActor *actor); static void map (MetaPlugin *plugin, MetaWindowActor *actor); static void destroy (MetaPlugin *plugin, MetaWindowActor *actor); static void switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction); static void kill_window_effects (MetaPlugin *plugin, MetaWindowActor *actor); static void kill_switch_workspace (MetaPlugin *plugin); static void show_tile_preview (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number); static void hide_tile_preview (MetaPlugin *plugin); static void confirm_display_change (MetaPlugin *plugin); static const MetaPluginInfo * plugin_info (MetaPlugin *plugin); /* * Plugin private data that we store in the .plugin_private member. */ struct _MetaDefaultPluginPrivate { /* Valid only when switch_workspace effect is in progress */ ClutterTimeline *tml_switch_workspace1; ClutterTimeline *tml_switch_workspace2; ClutterActor *desktop1; ClutterActor *desktop2; ClutterActor *background_group; MetaPluginInfo info; }; META_PLUGIN_DECLARE_WITH_CODE (MetaDefaultPlugin, meta_default_plugin, G_ADD_PRIVATE_DYNAMIC (MetaDefaultPlugin)); /* * Per actor private data we attach to each actor. */ typedef struct _ActorPrivate { ClutterActor *orig_parent; ClutterTimeline *tml_minimize; ClutterTimeline *tml_destroy; ClutterTimeline *tml_map; } ActorPrivate; /* callback data for when animations complete */ typedef struct { ClutterActor *actor; MetaPlugin *plugin; } EffectCompleteData; typedef struct _DisplayTilePreview { ClutterActor *actor; GdkRGBA *preview_color; MetaRectangle tile_rect; } DisplayTilePreview; static void meta_default_plugin_dispose (GObject *object) { /* MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (object)->priv; */ G_OBJECT_CLASS (meta_default_plugin_parent_class)->dispose (object); } static void meta_default_plugin_finalize (GObject *object) { G_OBJECT_CLASS (meta_default_plugin_parent_class)->finalize (object); } static void meta_default_plugin_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_default_plugin_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { switch (prop_id) { default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_default_plugin_class_init (MetaDefaultPluginClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); MetaPluginClass *plugin_class = META_PLUGIN_CLASS (klass); gobject_class->finalize = meta_default_plugin_finalize; gobject_class->dispose = meta_default_plugin_dispose; gobject_class->set_property = meta_default_plugin_set_property; gobject_class->get_property = meta_default_plugin_get_property; plugin_class->start = start; plugin_class->map = map; plugin_class->minimize = minimize; plugin_class->destroy = destroy; plugin_class->switch_workspace = switch_workspace; plugin_class->show_tile_preview = show_tile_preview; plugin_class->hide_tile_preview = hide_tile_preview; plugin_class->plugin_info = plugin_info; plugin_class->kill_window_effects = kill_window_effects; plugin_class->kill_switch_workspace = kill_switch_workspace; plugin_class->confirm_display_change = confirm_display_change; } static void meta_default_plugin_init (MetaDefaultPlugin *self) { MetaDefaultPluginPrivate *priv; self->priv = priv = meta_default_plugin_get_instance_private (self); priv->info.name = "Default Effects"; priv->info.version = "0.1"; priv->info.author = "Intel Corp."; priv->info.license = "GPL"; priv->info.description = "This is an example of a plugin implementation."; } /* * Actor private data accessor */ static void free_actor_private (gpointer data) { if (G_LIKELY (data != NULL)) g_slice_free (ActorPrivate, data); } static ActorPrivate * get_actor_private (MetaWindowActor *actor) { ActorPrivate *priv = g_object_get_qdata (G_OBJECT (actor), actor_data_quark); if (G_UNLIKELY (actor_data_quark == 0)) actor_data_quark = g_quark_from_static_string (ACTOR_DATA_KEY); if (G_UNLIKELY (!priv)) { priv = g_slice_new0 (ActorPrivate); g_object_set_qdata_full (G_OBJECT (actor), actor_data_quark, priv, free_actor_private); } return priv; } static ClutterTimeline * actor_animate (ClutterActor *actor, ClutterAnimationMode mode, guint duration, const gchar *first_property, ...) { va_list args; ClutterTransition *transition; clutter_actor_save_easing_state (actor); clutter_actor_set_easing_mode (actor, mode); clutter_actor_set_easing_duration (actor, duration); va_start (args, first_property); g_object_set_valist (G_OBJECT (actor), first_property, args); va_end (args); transition = clutter_actor_get_transition (actor, first_property); clutter_actor_restore_easing_state (actor); return CLUTTER_TIMELINE (transition); } static void on_switch_workspace_effect_complete (ClutterTimeline *timeline, gpointer data) { MetaPlugin *plugin = META_PLUGIN (data); MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; MetaDisplay *display = meta_plugin_get_display (plugin); GList *l = meta_get_window_actors (display); while (l) { ClutterActor *a = l->data; MetaWindowActor *window_actor = META_WINDOW_ACTOR (a); ActorPrivate *apriv = get_actor_private (window_actor); if (apriv->orig_parent) { g_object_ref (a); clutter_actor_remove_child (clutter_actor_get_parent (a), a); clutter_actor_add_child (apriv->orig_parent, a); g_object_unref (a); apriv->orig_parent = NULL; } l = l->next; } clutter_actor_destroy (priv->desktop1); clutter_actor_destroy (priv->desktop2); priv->tml_switch_workspace1 = NULL; priv->tml_switch_workspace2 = NULL; priv->desktop1 = NULL; priv->desktop2 = NULL; meta_plugin_switch_workspace_completed (plugin); } static void on_monitors_changed (MetaMonitorManager *monitor_manager, MetaPlugin *plugin) { MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin); MetaDisplay *display = meta_plugin_get_display (plugin); int i, n; GRand *rand = g_rand_new_with_seed (123456); clutter_actor_destroy_all_children (self->priv->background_group); n = meta_display_get_n_monitors (display); for (i = 0; i < n; i++) { MetaRectangle rect; ClutterActor *background_actor; MetaBackground *background; ClutterColor color; meta_display_get_monitor_geometry (display, i, &rect); background_actor = meta_background_actor_new (display, i); clutter_actor_set_position (background_actor, rect.x, rect.y); clutter_actor_set_size (background_actor, rect.width, rect.height); /* Don't use rand() here, mesa calls srand() internally when parsing the driconf XML, but it's nice if the colors are reproducible. */ clutter_color_init (&color, g_rand_int_range (rand, 0, 255), g_rand_int_range (rand, 0, 255), g_rand_int_range (rand, 0, 255), 255); background = meta_background_new (display); meta_background_set_color (background, &color); meta_background_actor_set_background (META_BACKGROUND_ACTOR (background_actor), background); g_object_unref (background); meta_background_actor_set_vignette (META_BACKGROUND_ACTOR (background_actor), TRUE, 0.5, 0.5); clutter_actor_add_child (self->priv->background_group, background_actor); } g_rand_free (rand); } static void init_keymap (MetaDefaultPlugin *self) { g_autoptr (GError) error = NULL; g_autoptr (GDBusProxy) proxy = NULL; g_autoptr (GVariant) result = NULL; g_autoptr (GVariant) props = NULL; g_autofree char *x11_layout = NULL; g_autofree char *x11_options = NULL; g_autofree char *x11_variant = NULL; proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE, NULL, "org.freedesktop.locale1", "/org/freedesktop/locale1", "org.freedesktop.DBus.Properties", NULL, &error); if (!proxy) { g_message ("Failed to acquire org.freedesktop.locale1 proxy: %s, " "probably running in CI", error->message); return; } result = g_dbus_proxy_call_sync (proxy, "GetAll", g_variant_new ("(s)", "org.freedesktop.locale1"), G_DBUS_CALL_FLAGS_NONE, 100, NULL, &error); if (!result) { g_warning ("Failed to retrieve locale properties: %s", error->message); return; } props = g_variant_get_child_value (result, 0); if (!props) { g_warning ("No locale properties found"); return; } if (!g_variant_lookup (props, "X11Layout", "s", &x11_layout)) x11_layout = g_strdup ("us"); if (!g_variant_lookup (props, "X11Options", "s", &x11_options)) x11_options = g_strdup (""); if (!g_variant_lookup (props, "X11Variant", "s", &x11_variant)) x11_variant = g_strdup (""); meta_backend_set_keymap (meta_get_backend (), x11_layout, x11_variant, x11_options); } static void start (MetaPlugin *plugin) { MetaDefaultPlugin *self = META_DEFAULT_PLUGIN (plugin); MetaDisplay *display = meta_plugin_get_display (plugin); MetaMonitorManager *monitor_manager = meta_monitor_manager_get (); self->priv->background_group = meta_background_group_new (); clutter_actor_insert_child_below (meta_get_window_group_for_display (display), self->priv->background_group, NULL); g_signal_connect (monitor_manager, "monitors-changed", G_CALLBACK (on_monitors_changed), plugin); on_monitors_changed (monitor_manager, plugin); if (meta_is_wayland_compositor ()) init_keymap (self); clutter_actor_show (meta_get_stage_for_display (display)); } static void switch_workspace (MetaPlugin *plugin, gint from, gint to, MetaMotionDirection direction) { MetaDisplay *display; MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; GList *l; ClutterActor *workspace0 = clutter_actor_new (); ClutterActor *workspace1 = clutter_actor_new (); ClutterActor *stage; int screen_width, screen_height; display = meta_plugin_get_display (plugin); stage = meta_get_stage_for_display (display); meta_display_get_size (display, &screen_width, &screen_height); clutter_actor_set_pivot_point (workspace1, 1.0, 1.0); clutter_actor_set_position (workspace1, screen_width, screen_height); clutter_actor_set_scale (workspace1, 0.0, 0.0); clutter_actor_add_child (stage, workspace1); clutter_actor_add_child (stage, workspace0); if (from == to) { meta_plugin_switch_workspace_completed (plugin); return; } l = g_list_last (meta_get_window_actors (display)); while (l) { MetaWindowActor *window_actor = l->data; ActorPrivate *apriv = get_actor_private (window_actor); ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWorkspace *workspace; gint win_workspace; workspace = meta_window_get_workspace (meta_window_actor_get_meta_window (window_actor)); win_workspace = meta_workspace_index (workspace); if (win_workspace == to || win_workspace == from) { ClutterActor *parent = win_workspace == to ? workspace1 : workspace0; apriv->orig_parent = clutter_actor_get_parent (actor); g_object_ref (actor); clutter_actor_remove_child (clutter_actor_get_parent (actor), actor); clutter_actor_add_child (parent, actor); clutter_actor_show (actor); clutter_actor_set_child_below_sibling (parent, actor, NULL); g_object_unref (actor); } else if (win_workspace < 0) { /* Sticky window */ apriv->orig_parent = NULL; } else { /* Window on some other desktop */ clutter_actor_hide (actor); apriv->orig_parent = NULL; } l = l->prev; } priv->desktop1 = workspace0; priv->desktop2 = workspace1; priv->tml_switch_workspace1 = actor_animate (workspace0, CLUTTER_EASE_IN_SINE, SWITCH_TIMEOUT, "scale-x", 1.0, "scale-y", 1.0, NULL); g_signal_connect (priv->tml_switch_workspace1, "completed", G_CALLBACK (on_switch_workspace_effect_complete), plugin); priv->tml_switch_workspace2 = actor_animate (workspace1, CLUTTER_EASE_IN_SINE, SWITCH_TIMEOUT, "scale-x", 0.0, "scale-y", 0.0, NULL); } /* * Minimize effect completion callback; this function restores actor state, and * calls the manager callback function. */ static void on_minimize_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data) { /* * Must reverse the effect of the effect; must hide it first to ensure * that the restoration will not be visible. */ MetaPlugin *plugin = data->plugin; ActorPrivate *apriv; MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor); apriv = get_actor_private (META_WINDOW_ACTOR (data->actor)); apriv->tml_minimize = NULL; clutter_actor_hide (data->actor); /* FIXME - we shouldn't assume the original scale, it should be saved * at the start of the effect */ clutter_actor_set_scale (data->actor, 1.0, 1.0); /* Now notify the manager that we are done with this effect */ meta_plugin_minimize_completed (plugin, window_actor); g_free (data); } /* * Simple minimize handler: it applies a scale effect (which must be reversed on * completion). */ static void minimize (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; MetaRectangle icon_geometry; MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); ClutterTimeline *timeline = NULL; ClutterActor *actor = CLUTTER_ACTOR (window_actor); type = meta_window_get_window_type (meta_window); if (!meta_window_get_icon_geometry(meta_window, &icon_geometry)) { icon_geometry.x = 0; icon_geometry.y = 0; } if (type == META_WINDOW_NORMAL) { timeline = actor_animate (actor, CLUTTER_EASE_IN_SINE, MINIMIZE_TIMEOUT, "scale-x", 0.0, "scale-y", 0.0, "x", (double)icon_geometry.x, "y", (double)icon_geometry.y, NULL); } if (timeline) { EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); apriv->tml_minimize = timeline; data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_minimize, "completed", G_CALLBACK (on_minimize_effect_complete), data); } else meta_plugin_minimize_completed (plugin, window_actor); } static void on_map_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data) { /* * Must reverse the effect of the effect. */ MetaPlugin *plugin = data->plugin; MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor); ActorPrivate *apriv = get_actor_private (window_actor); apriv->tml_map = NULL; /* Now notify the manager that we are done with this effect */ meta_plugin_map_completed (plugin, window_actor); g_free (data); } /* * Simple map handler: it applies a scale effect which must be reversed on * completion). */ static void map (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); clutter_actor_set_pivot_point (actor, 0.5, 0.5); clutter_actor_set_opacity (actor, 0); clutter_actor_set_scale (actor, 0.5, 0.5); clutter_actor_show (actor); apriv->tml_map = actor_animate (actor, CLUTTER_EASE_OUT_QUAD, MAP_TIMEOUT, "opacity", 255, "scale-x", 1.0, "scale-y", 1.0, NULL); data->actor = actor; data->plugin = plugin; g_signal_connect (apriv->tml_map, "completed", G_CALLBACK (on_map_effect_complete), data); } else meta_plugin_map_completed (plugin, window_actor); } /* * Destroy effect completion callback; this is a simple effect that requires no * further action than notifying the manager that the effect is completed. */ static void on_destroy_effect_complete (ClutterTimeline *timeline, EffectCompleteData *data) { MetaPlugin *plugin = data->plugin; MetaWindowActor *window_actor = META_WINDOW_ACTOR (data->actor); ActorPrivate *apriv = get_actor_private (window_actor); apriv->tml_destroy = NULL; meta_plugin_destroy_completed (plugin, window_actor); } /* * Simple TV-out like effect. */ static void destroy (MetaPlugin *plugin, MetaWindowActor *window_actor) { MetaWindowType type; ClutterActor *actor = CLUTTER_ACTOR (window_actor); MetaWindow *meta_window = meta_window_actor_get_meta_window (window_actor); ClutterTimeline *timeline = NULL; type = meta_window_get_window_type (meta_window); if (type == META_WINDOW_NORMAL) { timeline = actor_animate (actor, CLUTTER_EASE_OUT_QUAD, DESTROY_TIMEOUT, "opacity", 0, "scale-x", 0.8, "scale-y", 0.8, NULL); } if (timeline) { EffectCompleteData *data = g_new0 (EffectCompleteData, 1); ActorPrivate *apriv = get_actor_private (window_actor); apriv->tml_destroy = timeline; data->plugin = plugin; data->actor = actor; g_signal_connect (apriv->tml_destroy, "completed", G_CALLBACK (on_destroy_effect_complete), data); } else meta_plugin_destroy_completed (plugin, window_actor); } /* * Tile preview private data accessor */ static void free_display_tile_preview (DisplayTilePreview *preview) { if (G_LIKELY (preview != NULL)) { clutter_actor_destroy (preview->actor); g_slice_free (DisplayTilePreview, preview); } } static void on_display_closing (MetaDisplay *display, DisplayTilePreview *preview) { free_display_tile_preview (preview); } static DisplayTilePreview * get_display_tile_preview (MetaDisplay *display) { DisplayTilePreview *preview; if (!display_tile_preview_data_quark) { display_tile_preview_data_quark = g_quark_from_static_string (DISPLAY_TILE_PREVIEW_DATA_KEY); } preview = g_object_get_qdata (G_OBJECT (display), display_tile_preview_data_quark); if (!preview) { preview = g_slice_new0 (DisplayTilePreview); preview->actor = clutter_actor_new (); clutter_actor_set_background_color (preview->actor, CLUTTER_COLOR_Blue); clutter_actor_set_opacity (preview->actor, 100); clutter_actor_add_child (meta_get_window_group_for_display (display), preview->actor); g_signal_connect (display, "closing", G_CALLBACK (on_display_closing), preview); g_object_set_qdata (G_OBJECT (display), display_tile_preview_data_quark, preview); } return preview; } static void show_tile_preview (MetaPlugin *plugin, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number) { MetaDisplay *display = meta_plugin_get_display (plugin); DisplayTilePreview *preview = get_display_tile_preview (display); ClutterActor *window_actor; if (clutter_actor_is_visible (preview->actor) && preview->tile_rect.x == tile_rect->x && preview->tile_rect.y == tile_rect->y && preview->tile_rect.width == tile_rect->width && preview->tile_rect.height == tile_rect->height) return; /* nothing to do */ clutter_actor_set_position (preview->actor, tile_rect->x, tile_rect->y); clutter_actor_set_size (preview->actor, tile_rect->width, tile_rect->height); clutter_actor_show (preview->actor); window_actor = CLUTTER_ACTOR (meta_window_get_compositor_private (window)); clutter_actor_set_child_below_sibling (clutter_actor_get_parent (preview->actor), preview->actor, window_actor); preview->tile_rect = *tile_rect; } static void hide_tile_preview (MetaPlugin *plugin) { MetaDisplay *display = meta_plugin_get_display (plugin); DisplayTilePreview *preview = get_display_tile_preview (display); clutter_actor_hide (preview->actor); } static void finish_timeline (ClutterTimeline *timeline) { g_object_ref (timeline); clutter_timeline_stop (timeline); g_signal_emit_by_name (timeline, "completed", NULL); g_object_unref (timeline); } static void kill_switch_workspace (MetaPlugin *plugin) { MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; if (priv->tml_switch_workspace1) { g_object_ref (priv->tml_switch_workspace1); clutter_timeline_stop (priv->tml_switch_workspace1); clutter_timeline_stop (priv->tml_switch_workspace2); g_signal_emit_by_name (priv->tml_switch_workspace1, "completed", NULL); g_object_unref (priv->tml_switch_workspace1); } } static void kill_window_effects (MetaPlugin *plugin, MetaWindowActor *window_actor) { ActorPrivate *apriv; apriv = get_actor_private (window_actor); if (apriv->tml_minimize) finish_timeline (apriv->tml_minimize); if (apriv->tml_map) finish_timeline (apriv->tml_map); if (apriv->tml_destroy) finish_timeline (apriv->tml_destroy); } static const MetaPluginInfo * plugin_info (MetaPlugin *plugin) { MetaDefaultPluginPrivate *priv = META_DEFAULT_PLUGIN (plugin)->priv; return &priv->info; } static void on_dialog_closed (GPid pid, gint status, gpointer user_data) { MetaPlugin *plugin = user_data; gboolean ok; ok = g_spawn_check_exit_status (status, NULL); meta_plugin_complete_display_change (plugin, ok); } static void confirm_display_change (MetaPlugin *plugin) { GPid pid; pid = meta_show_dialog ("--question", "Does the display look OK?", "20", NULL, "_Keep This Configuration", "_Restore Previous Configuration", 0, NULL, NULL); g_child_watch_add (pid, on_dialog_closed, plugin); } muffin-6.4.1/src/compositor/meta-texture-tower.c0000664000175000017500000003571114723361714020664 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaTextureTower * * Mipmap emulation by creation of scaled down images * * Copyright (C) 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include #include #include "compositor/meta-texture-tower.h" #ifndef M_LOG2E #define M_LOG2E 1.4426950408889634074 #endif #define MAX_TEXTURE_LEVELS 12 /* If the texture format in memory doesn't match this, then Mesa * will do the conversion, so things will still work, but it might * be slow depending on how efficient Mesa is. These should be the * native formats unless the display is 16bpp. If conversions * here are a bottleneck, investigate whether we are converting when * storing window data *into* the texture before adding extra code * to handle multiple texture formats. */ #if G_BYTE_ORDER == G_LITTLE_ENDIAN #define TEXTURE_FORMAT COGL_PIXEL_FORMAT_BGRA_8888_PRE #else #define TEXTURE_FORMAT COGL_PIXEL_FORMAT_ARGB_8888_PRE #endif typedef struct { guint16 x1; guint16 y1; guint16 x2; guint16 y2; } Box; struct _MetaTextureTower { int n_levels; CoglTexture *textures[MAX_TEXTURE_LEVELS]; CoglOffscreen *fbos[MAX_TEXTURE_LEVELS]; Box invalid[MAX_TEXTURE_LEVELS]; CoglPipeline *pipeline_template; }; /** * meta_texture_tower_new: * * Creates a new texture tower. The base texture has to be set with * meta_texture_tower_set_base_texture() before use. * * Return value: the new texture tower. Free with meta_texture_tower_free() */ MetaTextureTower * meta_texture_tower_new (void) { MetaTextureTower *tower; tower = g_slice_new0 (MetaTextureTower); return tower; } /** * meta_texture_tower_free: * @tower: a #MetaTextureTower * * Frees a texture tower created with meta_texture_tower_new(). */ void meta_texture_tower_free (MetaTextureTower *tower) { g_return_if_fail (tower != NULL); if (tower->pipeline_template != NULL) cogl_object_unref (tower->pipeline_template); meta_texture_tower_set_base_texture (tower, NULL); g_slice_free (MetaTextureTower, tower); } /** * meta_texture_tower_set_base_texture: * @tower: a #MetaTextureTower * @texture: the new texture used as a base for scaled down versions * * Sets the base texture that is the scaled texture that the * scaled textures of the tower are derived from. The texture itself * will be used as level 0 of the tower and will be referenced until * unset or until the tower is freed. */ void meta_texture_tower_set_base_texture (MetaTextureTower *tower, CoglTexture *texture) { int i; g_return_if_fail (tower != NULL); if (texture == tower->textures[0]) return; if (tower->textures[0] != NULL) { for (i = 1; i < tower->n_levels; i++) { if (tower->textures[i] != NULL) { cogl_object_unref (tower->textures[i]); tower->textures[i] = NULL; } if (tower->fbos[i] != NULL) { cogl_object_unref (tower->fbos[i]); tower->fbos[i] = NULL; } } cogl_object_unref (tower->textures[0]); } tower->textures[0] = texture; if (tower->textures[0] != NULL) { int width, height; cogl_object_ref (tower->textures[0]); width = cogl_texture_get_width (tower->textures[0]); height = cogl_texture_get_height (tower->textures[0]); tower->n_levels = 1 + MAX ((int)(M_LOG2E * log (width)), (int)(M_LOG2E * log (height))); tower->n_levels = MIN(tower->n_levels, MAX_TEXTURE_LEVELS); meta_texture_tower_update_area (tower, 0, 0, width, height); } else { tower->n_levels = 0; } } /** * meta_texture_tower_update_area: * @tower: a #MetaTextureTower * @x: X coordinate of upper left of rectangle that changed * @y: Y coordinate of upper left of rectangle that changed * @width: width of rectangle that changed * @height: height rectangle that changed * * Mark a region of the base texture as having changed; the next * time a scaled down version of the base texture is retrieved, * the appropriate area of the scaled down texture will be updated. */ void meta_texture_tower_update_area (MetaTextureTower *tower, int x, int y, int width, int height) { int texture_width, texture_height; Box invalid; int i; g_return_if_fail (tower != NULL); if (tower->textures[0] == NULL) return; texture_width = cogl_texture_get_width (tower->textures[0]); texture_height = cogl_texture_get_height (tower->textures[0]); invalid.x1 = x; invalid.y1 = y; invalid.x2 = x + width; invalid.y2 = y + height; for (i = 1; i < tower->n_levels; i++) { texture_width = MAX (1, texture_width / 2); texture_height = MAX (1, texture_height / 2); invalid.x1 = invalid.x1 / 2; invalid.y1 = invalid.y1 / 2; invalid.x2 = MIN (texture_width, (invalid.x2 + 1) / 2); invalid.y2 = MIN (texture_height, (invalid.y2 + 1) / 2); if (tower->invalid[i].x1 == tower->invalid[i].x2 || tower->invalid[i].y1 == tower->invalid[i].y2) { tower->invalid[i] = invalid; } else { tower->invalid[i].x1 = MIN (tower->invalid[i].x1, invalid.x1); tower->invalid[i].y1 = MIN (tower->invalid[i].y1, invalid.y1); tower->invalid[i].x2 = MAX (tower->invalid[i].x2, invalid.x2); tower->invalid[i].y2 = MAX (tower->invalid[i].y2, invalid.y2); } } } /* It generally looks worse if we scale up a window texture by even a * small amount than if we scale it down using bilinear filtering, so * we always pick the *larger* adjacent level. */ #define LOD_BIAS (-0.49) /* This determines the appropriate level of detail to use when drawing the * texture, in a way that corresponds to what the GL specification does * when mip-mapping. This is probably fancier and slower than what we need, * but we do the computation only once each time we paint a window, and * its easier to just use the equations from the specification than to * come up with something simpler. * * If window is being painted at an angle from the viewer, then we have to * pick a point in the texture; we use the middle of the texture (which is * why the width/height are passed in.) This is not the normal case for * Meta. */ static int get_paint_level (ClutterPaintContext *paint_context, int width, int height) { CoglFramebuffer *framebuffer; CoglMatrix projection, modelview, pm; float v[4]; double viewport_width, viewport_height; double u0, v0; double xc, yc, wc; double dxdu_, dxdv_, dydu_, dydv_; double det_, det_sq; double rho_sq; double lambda; /* See * http://www.opengl.org/registry/doc/glspec32.core.20090803.pdf * Section 3.8.9, p. 1.6.2. Here we have * * u(x,y) = x_o; * v(x,y) = y_o; * * Since we are mapping 1:1 from object coordinates into pixel * texture coordinates, the clip coordinates are: * * (x_c) (x_o) (u) * (y_c) = (M_projection)(M_modelview) (y_o) = (PM) (v) * (z_c) (z_o) (0) * (w_c) (w_o) (1) */ framebuffer = clutter_paint_context_get_framebuffer (paint_context); cogl_framebuffer_get_projection_matrix (framebuffer, &projection); cogl_framebuffer_get_modelview_matrix (framebuffer, &modelview); cogl_matrix_multiply (&pm, &projection, &modelview); cogl_framebuffer_get_viewport4fv (framebuffer, v); viewport_width = v[2]; viewport_height = v[3]; u0 = width / 2.; v0 = height / 2.; xc = pm.xx * u0 + pm.xy * v0 + pm.xw; yc = pm.yx * u0 + pm.yy * v0 + pm.yw; wc = pm.wx * u0 + pm.wy * v0 + pm.ww; /* We'll simplify the equations below for a bit of micro-optimization. * The commented out code is the unsimplified version. // Partial derivates of window coordinates: // // x_w = 0.5 * viewport_width * x_c / w_c + viewport_center_x // y_w = 0.5 * viewport_height * y_c / w_c + viewport_center_y // // with respect to u, v, using // d(a/b)/dx = da/dx * (1/b) - a * db/dx / (b^2) dxdu = 0.5 * viewport_width * (pm.xx - pm.wx * (xc/wc)) / wc; dxdv = 0.5 * viewport_width * (pm.xy - pm.wy * (xc/wc)) / wc; dydu = 0.5 * viewport_height * (pm.yx - pm.wx * (yc/wc)) / wc; dydv = 0.5 * viewport_height * (pm.yy - pm.wy * (yc/wc)) / wc; // Compute the inverse partials as the matrix inverse det = dxdu * dydv - dxdv * dydu; dudx = dydv / det; dudy = - dxdv / det; dvdx = - dydu / det; dvdy = dvdu / det; // Scale factor; maximum of the distance in texels for a change of 1 pixel // in the X direction or 1 pixel in the Y direction rho = MAX (sqrt (dudx * dudx + dvdx * dvdx), sqrt(dudy * dudy + dvdy * dvdy)); // Level of detail lambda = log2 (rho) + LOD_BIAS; */ /* dxdu * wc, etc */ dxdu_ = 0.5 * viewport_width * (pm.xx - pm.wx * (xc/wc)); dxdv_ = 0.5 * viewport_width * (pm.xy - pm.wy * (xc/wc)); dydu_ = 0.5 * viewport_height * (pm.yx - pm.wx * (yc/wc)); dydv_ = 0.5 * viewport_height * (pm.yy - pm.wy * (yc/wc)); /* det * wc^2 */ det_ = dxdu_ * dydv_ - dxdv_ * dydu_; det_sq = det_ * det_; if (det_sq == 0.0) return -1; /* (rho * det * wc)^2 */ rho_sq = MAX (dydv_ * dydv_ + dydu_ * dydu_, dxdv_ * dxdv_ + dxdu_ * dxdu_); lambda = 0.5 * M_LOG2E * log (rho_sq * wc * wc / det_sq) + LOD_BIAS; #if 0 g_print ("%g %g %g\n", 0.5 * viewport_width * pm.xx / pm.ww, 0.5 * viewport_height * pm.yy / pm.ww, lambda); #endif if (lambda <= 0.) return 0; else return (int)(0.5 + lambda); } static void texture_tower_create_texture (MetaTextureTower *tower, int level, int width, int height) { tower->textures[level] = cogl_texture_new_with_size (width, height, COGL_TEXTURE_NO_AUTO_MIPMAP, TEXTURE_FORMAT); tower->invalid[level].x1 = 0; tower->invalid[level].y1 = 0; tower->invalid[level].x2 = width; tower->invalid[level].y2 = height; } static void texture_tower_revalidate (MetaTextureTower *tower, int level) { CoglTexture *source_texture = tower->textures[level - 1]; int source_texture_width = cogl_texture_get_width (source_texture); int source_texture_height = cogl_texture_get_height (source_texture); CoglTexture *dest_texture = tower->textures[level]; int dest_texture_width = cogl_texture_get_width (dest_texture); int dest_texture_height = cogl_texture_get_height (dest_texture); Box *invalid = &tower->invalid[level]; CoglFramebuffer *fb; GError *catch_error = NULL; CoglPipeline *pipeline; if (tower->fbos[level] == NULL) tower->fbos[level] = cogl_offscreen_new_with_texture (dest_texture); fb = COGL_FRAMEBUFFER (tower->fbos[level]); if (!cogl_framebuffer_allocate (fb, &catch_error)) { g_error_free (catch_error); return; } cogl_framebuffer_orthographic (fb, 0, 0, dest_texture_width, dest_texture_height, -1., 1.); if (!tower->pipeline_template) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); tower->pipeline_template = cogl_pipeline_new (ctx); cogl_pipeline_set_blend (tower->pipeline_template, "RGBA = ADD (SRC_COLOR, 0)", NULL); } pipeline = cogl_pipeline_copy (tower->pipeline_template); cogl_pipeline_set_layer_texture (pipeline, 0, tower->textures[level - 1]); cogl_framebuffer_draw_textured_rectangle (fb, pipeline, invalid->x1, invalid->y1, invalid->x2, invalid->y2, (2. * invalid->x1) / source_texture_width, (2. * invalid->y1) / source_texture_height, (2. * invalid->x2) / source_texture_width, (2. * invalid->y2) / source_texture_height); cogl_object_unref (pipeline); tower->invalid[level].x1 = tower->invalid[level].x2 = 0; tower->invalid[level].y1 = tower->invalid[level].y2 = 0; } /** * meta_texture_tower_get_paint_texture: * @tower: a #MetaTextureTower * @paint_context: a #ClutterPaintContext * * Gets the texture from the tower that best matches the current * rendering scale. (On the assumption here the texture is going to * be rendered with vertex coordinates that correspond to its * size in pixels, so a 200x200 texture will be rendered on the * rectangle (0, 0, 200, 200). * * Return value: the COGL texture handle to use for painting, or * %NULL if no base texture has yet been set. */ CoglTexture * meta_texture_tower_get_paint_texture (MetaTextureTower *tower, ClutterPaintContext *paint_context) { int texture_width, texture_height; int level; g_return_val_if_fail (tower != NULL, NULL); if (tower->textures[0] == NULL) return NULL; texture_width = cogl_texture_get_width (tower->textures[0]); texture_height = cogl_texture_get_height (tower->textures[0]); level = get_paint_level (paint_context, texture_width, texture_height); if (level < 0) /* singular paint matrix, scaled to nothing */ return NULL; level = MIN (level, tower->n_levels - 1); if (tower->textures[level] == NULL || (tower->invalid[level].x2 != tower->invalid[level].x1 && tower->invalid[level].y2 != tower->invalid[level].y1)) { int i; for (i = 1; i <= level; i++) { /* Use "floor" convention here to be consistent with the NPOT texture extension */ texture_width = MAX (1, texture_width / 2); texture_height = MAX (1, texture_height / 2); if (tower->textures[i] == NULL) texture_tower_create_texture (tower, i, texture_width, texture_height); } for (i = 1; i <= level; i++) { if (tower->invalid[level].x2 != tower->invalid[level].x1 && tower->invalid[level].y2 != tower->invalid[level].y1) texture_tower_revalidate (tower, i); } } return tower->textures[level]; } muffin-6.4.1/src/compositor/meta-dnd-actor-private.h0000664000175000017500000000314114723361714021346 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-dnd-actor-private.h: Actor for painting the DnD surface * * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_DND_ACTOR_PRIVATE_H #define META_DND_ACTOR_PRIVATE_H #include "compositor/meta-feedback-actor-private.h" /** * MetaDnDActor: * * This class handles the rendering of the DnD surface */ #define META_TYPE_DND_ACTOR (meta_dnd_actor_get_type ()) G_DECLARE_FINAL_TYPE (MetaDnDActor, meta_dnd_actor, META, DND_ACTOR, MetaFeedbackActor) ClutterActor *meta_dnd_actor_new (ClutterActor *drag_origin, int start_x, int start_y); void meta_dnd_actor_drag_finish (MetaDnDActor *self, gboolean success); #endif /* META_DND_ACTOR_PRIVATE_H */ muffin-6.4.1/src/compositor/clutter-utils.h0000664000175000017500000000344014723361714017721 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for use with Clutter * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_CLUTTER_UTILS_H__ #define __META_CLUTTER_UTILS_H__ #include "clutter/clutter.h" gboolean meta_actor_vertices_are_untransformed (graphene_point3d_t *verts, float widthf, float heightf, int *x_origin, int *y_origin); gboolean meta_actor_painting_untransformed (CoglFramebuffer *fb, int paint_width, int paint_height, int sample_widthf, int sample_heightf, int *x_origin, int *y_origin); #endif /* __META_CLUTTER_UTILS_H__ */ muffin-6.4.1/src/compositor/meta-window-actor-private.h0000664000175000017500000000736214723361714022121 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_WINDOW_ACTOR_PRIVATE_H #define META_WINDOW_ACTOR_PRIVATE_H #include #include "compositor/meta-plugin-manager.h" #include "compositor/meta-surface-actor.h" #include "meta/compositor-mutter.h" struct _MetaWindowActorClass { ClutterActorClass parent; void (*frame_complete) (MetaWindowActor *actor, ClutterFrameInfo *frame_info, int64_t presentation_time); void (*assign_surface_actor) (MetaWindowActor *actor, MetaSurfaceActor *surface_actor); void (*queue_frame_drawn) (MetaWindowActor *actor, gboolean skip_sync_delay); void (*pre_paint) (MetaWindowActor *actor); void (*post_paint) (MetaWindowActor *actor); void (*queue_destroy) (MetaWindowActor *actor); void (*set_frozen) (MetaWindowActor *actor, gboolean frozen); void (*update_regions) (MetaWindowActor *actor); }; typedef enum { META_WINDOW_ACTOR_CHANGE_SIZE = 1 << 0, META_WINDOW_ACTOR_CHANGE_POSITION = 1 << 1 } MetaWindowActorChanges; void meta_window_actor_queue_destroy (MetaWindowActor *self); void meta_window_actor_show (MetaWindowActor *self, MetaCompEffect effect); void meta_window_actor_hide (MetaWindowActor *self, MetaCompEffect effect); void meta_window_actor_size_change (MetaWindowActor *self, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect); void meta_window_actor_pre_paint (MetaWindowActor *self); void meta_window_actor_post_paint (MetaWindowActor *self); void meta_window_actor_frame_complete (MetaWindowActor *self, ClutterFrameInfo *frame_info, gint64 presentation_time); gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self); MetaWindowActorChanges meta_window_actor_sync_actor_geometry (MetaWindowActor *self, gboolean did_placement); void meta_window_actor_update_opacity (MetaWindowActor *self); void meta_window_actor_mapped (MetaWindowActor *self); void meta_window_actor_unmapped (MetaWindowActor *self); void meta_window_actor_sync_updates_frozen (MetaWindowActor *self); void meta_window_actor_queue_frame_drawn (MetaWindowActor *self, gboolean no_delay_frame); void meta_window_actor_effect_completed (MetaWindowActor *actor, MetaPluginEffect event); MetaSurfaceActor *meta_window_actor_get_surface (MetaWindowActor *self); void meta_window_actor_assign_surface_actor (MetaWindowActor *self, MetaSurfaceActor *surface_actor); MetaWindowActor *meta_window_actor_from_window (MetaWindow *window); MetaWindowActor *meta_window_actor_from_actor (ClutterActor *actor); void meta_window_actor_set_geometry_scale (MetaWindowActor *window_actor, int geometry_scale); int meta_window_actor_get_geometry_scale (MetaWindowActor *window_actor); void meta_window_actor_notify_damaged (MetaWindowActor *window_actor); gboolean meta_window_actor_is_frozen (MetaWindowActor *self); gboolean meta_window_actor_is_opaque (MetaWindowActor *self); void meta_window_actor_update_regions (MetaWindowActor *self); #endif /* META_WINDOW_ACTOR_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-background-actor-private.h0000664000175000017500000000047714723361714022731 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_ACTOR_PRIVATE_H #define META_BACKGROUND_ACTOR_PRIVATE_H #include "meta/meta-background-actor.h" cairo_region_t *meta_background_actor_get_clip_region (MetaBackgroundActor *self); #endif /* META_BACKGROUND_ACTOR_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-window-shape.c0000664000175000017500000001613614723361714020433 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaWindowShape * * Extracted invariant window shape * * Copyright (C) 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "meta/meta-window-shape.h" #include #include "compositor/region-utils.h" struct _MetaWindowShape { guint ref_count; int top, right, bottom, left; int n_rectangles; cairo_rectangle_int_t *rectangles; guint hash; }; MetaWindowShape * meta_window_shape_new (cairo_region_t *region) { MetaWindowShape *shape; MetaRegionIterator iter; cairo_rectangle_int_t extents; int max_yspan_y1 = 0; int max_yspan_y2 = 0; int max_xspan_x1 = -1; int max_xspan_x2 = -1; guint hash; shape = g_slice_new0 (MetaWindowShape); shape->ref_count = 1; cairo_region_get_extents (region, &extents); shape->n_rectangles = cairo_region_num_rectangles (region); if (shape->n_rectangles == 0) { shape->rectangles = NULL; shape->top = shape->right = shape->bottom = shape->left = 0; shape->hash = 0; return shape; } for (meta_region_iterator_init (&iter, region); !meta_region_iterator_at_end (&iter); meta_region_iterator_next (&iter)) { int max_line_xspan_x1 = -1; int max_line_xspan_x2 = -1; if (iter.rectangle.width > max_line_xspan_x2 - max_line_xspan_x1) { max_line_xspan_x1 = iter.rectangle.x; max_line_xspan_x2 = iter.rectangle.x + iter.rectangle.width; } if (iter.line_end) { if (iter.rectangle.height > max_yspan_y2 - max_yspan_y1) { max_yspan_y1 = iter.rectangle.y; max_yspan_y2 = iter.rectangle.y + iter.rectangle.height; } if (max_xspan_x1 < 0) /* First line */ { max_xspan_x1 = max_line_xspan_x1; max_xspan_x2 = max_line_xspan_x2; } else { max_xspan_x1 = MAX (max_xspan_x1, max_line_xspan_x1); max_xspan_x2 = MIN (max_xspan_x2, max_line_xspan_x2); if (max_xspan_x2 < max_xspan_x1) max_xspan_x2 = max_xspan_x1; } } } #if 0 g_print ("xspan: %d -> %d, yspan: %d -> %d\n", max_xspan_x1, max_xspan_x2, max_yspan_y1, max_yspan_y2); #endif shape->top = max_yspan_y1 - extents.y; shape->right = extents.x + extents.width - max_xspan_x2; shape->bottom = extents.y + extents.height - max_yspan_y2; shape->left = max_xspan_x1 - extents.x; shape->rectangles = g_new (cairo_rectangle_int_t, shape->n_rectangles); hash = 0; for (meta_region_iterator_init (&iter, region); !meta_region_iterator_at_end (&iter); meta_region_iterator_next (&iter)) { int x1, x2, y1, y2; x1 = iter.rectangle.x; x2 = iter.rectangle.x + iter.rectangle.width; y1 = iter.rectangle.y; y2 = iter.rectangle.y + iter.rectangle.height; if (x1 > max_xspan_x1) x1 -= MIN (x1, max_xspan_x2 - 1) - max_xspan_x1; if (x2 > max_xspan_x1) x2 -= MIN (x2, max_xspan_x2 - 1) - max_xspan_x1; if (y1 > max_yspan_y1) y1 -= MIN (y1, max_yspan_y2 - 1) - max_yspan_y1; if (y2 > max_yspan_y1) y2 -= MIN (y2, max_yspan_y2 - 1) - max_yspan_y1; shape->rectangles[iter.i].x = x1 - extents.x; shape->rectangles[iter.i].y = y1 - extents.y; shape->rectangles[iter.i].width = x2 - x1; shape->rectangles[iter.i].height = y2 - y1; #if 0 g_print ("%d: +%d+%dx%dx%d => +%d+%dx%dx%d\n", iter.i, iter.rectangle.x, iter.rectangle.y, iter.rectangle.width, iter.rectangle.height, shape->rectangles[iter.i].x, shape->rectangles[iter.i].y, hape->rectangles[iter.i].width, shape->rectangles[iter.i].height); #endif hash = hash * 31 + x1 * 17 + x2 * 27 + y1 * 37 + y2 * 43; } shape->hash = hash; #if 0 g_print ("%d %d %d %d: %#x\n\n", shape->top, shape->right, shape->bottom, shape->left, shape->hash); #endif return shape; } MetaWindowShape * meta_window_shape_ref (MetaWindowShape *shape) { shape->ref_count++; return shape; } void meta_window_shape_unref (MetaWindowShape *shape) { shape->ref_count--; if (shape->ref_count == 0) { g_free (shape->rectangles); g_slice_free (MetaWindowShape, shape); } } guint meta_window_shape_hash (MetaWindowShape *shape) { return shape->hash; } gboolean meta_window_shape_equal (MetaWindowShape *shape_a, MetaWindowShape *shape_b) { if (shape_a->n_rectangles != shape_b->n_rectangles) return FALSE; return memcmp (shape_a->rectangles, shape_b->rectangles, sizeof (cairo_rectangle_int_t) * shape_a->n_rectangles) == 0; } void meta_window_shape_get_borders (MetaWindowShape *shape, int *border_top, int *border_right, int *border_bottom, int *border_left) { if (border_top) *border_top = shape->top; if (border_right) *border_right = shape->right; if (border_bottom) *border_bottom = shape->bottom; if (border_left) *border_left = shape->left; } /** * meta_window_shape_to_region: * @shape: a #MetaWindowShape * @center_width: size of the central region horizontally * @center_height: size of the central region vertically * * Converts the shape to to a cairo_region_t using the given width * and height for the central scaled region. * * Return value: a newly created region */ cairo_region_t * meta_window_shape_to_region (MetaWindowShape *shape, int center_width, int center_height) { cairo_region_t *region; int i; region = cairo_region_create (); for (i = 0; i < shape->n_rectangles; i++) { cairo_rectangle_int_t rect = shape->rectangles[i]; if (rect.x <= shape->left && rect.x + rect.width >= shape->left + 1) rect.width += center_width; else if (rect.x >= shape->left + 1) rect.x += center_width; if (rect.y <= shape->top && rect.y + rect.height >= shape->top + 1) rect.height += center_height; else if (rect.y >= shape->top + 1) rect.y += center_height; cairo_region_union_rectangle (region, &rect); } return region; } G_DEFINE_BOXED_TYPE (MetaWindowShape, meta_window_shape, meta_window_shape_ref, meta_window_shape_unref) muffin-6.4.1/src/compositor/meta-window-actor-wayland.c0000664000175000017500000001205414723361714022073 0ustar fabiofabio/* * Copyright (C) 2018 Endless, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Georges Basile Stavracas Neto */ #include "compositor/meta-surface-actor-wayland.h" #include "compositor/meta-window-actor-wayland.h" #include "meta/meta-window-actor.h" #include "wayland/meta-wayland-surface.h" struct _MetaWindowActorWayland { MetaWindowActor parent; }; G_DEFINE_TYPE (MetaWindowActorWayland, meta_window_actor_wayland, META_TYPE_WINDOW_ACTOR) static gboolean remove_surface_actor_from_children (GNode *node, gpointer data) { MetaWaylandSurface *surface = node->data; MetaSurfaceActor *surface_actor = meta_wayland_surface_get_actor (surface); MetaWindowActor *window_actor = data; ClutterActor *parent; parent = clutter_actor_get_parent (CLUTTER_ACTOR (surface_actor)); if (!parent) return FALSE; g_return_val_if_fail (parent == CLUTTER_ACTOR (window_actor), FALSE); clutter_actor_remove_child (CLUTTER_ACTOR (window_actor), CLUTTER_ACTOR (surface_actor)); return FALSE; } static gboolean add_surface_actor_to_children (GNode *node, gpointer data) { MetaWaylandSurface *surface = node->data; MetaSurfaceActor *surface_actor = meta_wayland_surface_get_actor (surface); MetaWindowActor *window_actor = data; clutter_actor_add_child (CLUTTER_ACTOR (window_actor), CLUTTER_ACTOR (surface_actor)); return FALSE; } void meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor) { MetaSurfaceActor *surface_actor = meta_window_actor_get_surface (actor); MetaWaylandSurface *surface = meta_surface_actor_wayland_get_surface ( META_SURFACE_ACTOR_WAYLAND (surface_actor)); GNode *root_node = surface->subsurface_branch_node; g_node_traverse (root_node, G_IN_ORDER, G_TRAVERSE_LEAVES, -1, remove_surface_actor_from_children, actor); g_node_traverse (root_node, G_IN_ORDER, G_TRAVERSE_LEAVES, -1, add_surface_actor_to_children, actor); } static void meta_window_actor_wayland_assign_surface_actor (MetaWindowActor *actor, MetaSurfaceActor *surface_actor) { MetaWindowActorClass *parent_class = META_WINDOW_ACTOR_CLASS (meta_window_actor_wayland_parent_class); g_warn_if_fail (!meta_window_actor_get_surface (actor)); parent_class->assign_surface_actor (actor, surface_actor); meta_window_actor_wayland_rebuild_surface_tree (actor); } static void meta_window_actor_wayland_frame_complete (MetaWindowActor *actor, ClutterFrameInfo *frame_info, int64_t presentation_time) { } static void meta_window_actor_wayland_queue_frame_drawn (MetaWindowActor *actor, gboolean skip_sync_delay) { } static void meta_window_actor_wayland_pre_paint (MetaWindowActor *actor) { } static void meta_window_actor_wayland_post_paint (MetaWindowActor *actor) { } static void meta_window_actor_wayland_queue_destroy (MetaWindowActor *actor) { } static void meta_window_actor_wayland_set_frozen (MetaWindowActor *actor, gboolean frozen) { } static void meta_window_actor_wayland_update_regions (MetaWindowActor *actor) { } static void meta_window_actor_wayland_class_init (MetaWindowActorWaylandClass *klass) { MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass); window_actor_class->assign_surface_actor = meta_window_actor_wayland_assign_surface_actor; window_actor_class->frame_complete = meta_window_actor_wayland_frame_complete; window_actor_class->queue_frame_drawn = meta_window_actor_wayland_queue_frame_drawn; window_actor_class->pre_paint = meta_window_actor_wayland_pre_paint; window_actor_class->post_paint = meta_window_actor_wayland_post_paint; window_actor_class->queue_destroy = meta_window_actor_wayland_queue_destroy; window_actor_class->set_frozen = meta_window_actor_wayland_set_frozen; window_actor_class->update_regions = meta_window_actor_wayland_update_regions; } static void meta_window_actor_wayland_init (MetaWindowActorWayland *self) { } muffin-6.4.1/src/compositor/meta-plugin.c0000664000175000017500000001450214723361714017317 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-plugin * @title: MetaPlugin * @short_description: Entry point for plugins * */ #include "config.h" #include "meta/meta-plugin.h" #include #include #include #include #include "backends/meta-monitor-manager-private.h" #include "clutter/x11/clutter-x11.h" #include "compositor/compositor-private.h" #include "compositor/meta-window-actor-private.h" #include "compositor/meta-plugin-manager.h" #include "meta/display.h" #include "meta/util.h" typedef struct _MetaPluginPrivate { MetaCompositor *compositor; } MetaPluginPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaPlugin, meta_plugin, G_TYPE_OBJECT); static void meta_plugin_class_init (MetaPluginClass *klass) { } static void meta_plugin_init (MetaPlugin *self) { } const MetaPluginInfo * meta_plugin_get_info (MetaPlugin *plugin) { MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass && klass->plugin_info) return klass->plugin_info (plugin); return NULL; } gboolean _meta_plugin_xevent_filter (MetaPlugin *plugin, XEvent *xev) { MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->xevent_filter) return klass->xevent_filter (plugin, xev); else return FALSE; } void meta_plugin_switch_workspace_completed (MetaPlugin *plugin) { MetaPluginPrivate *priv = meta_plugin_get_instance_private (plugin); meta_switch_workspace_completed (priv->compositor); } static void meta_plugin_window_effect_completed (MetaPlugin *plugin, MetaWindowActor *actor, unsigned long event) { meta_window_actor_effect_completed (actor, event); } void meta_plugin_minimize_completed (MetaPlugin *plugin, MetaWindowActor *actor) { meta_plugin_window_effect_completed (plugin, actor, META_PLUGIN_MINIMIZE); } void meta_plugin_unminimize_completed (MetaPlugin *plugin, MetaWindowActor *actor) { meta_plugin_window_effect_completed (plugin, actor, META_PLUGIN_UNMINIMIZE); } void meta_plugin_size_change_completed (MetaPlugin *plugin, MetaWindowActor *actor) { meta_plugin_window_effect_completed (plugin, actor, META_PLUGIN_SIZE_CHANGE); } void meta_plugin_map_completed (MetaPlugin *plugin, MetaWindowActor *actor) { meta_plugin_window_effect_completed (plugin, actor, META_PLUGIN_MAP); } void meta_plugin_destroy_completed (MetaPlugin *plugin, MetaWindowActor *actor) { meta_plugin_window_effect_completed (plugin, actor, META_PLUGIN_DESTROY); } /** * meta_plugin_begin_modal: * @plugin: a #MetaPlugin * @options: flags that modify the behavior of the modal grab * @timestamp: the timestamp used for establishing grabs * * This function is used to grab the keyboard and mouse for the exclusive * use of the plugin. Correct operation requires that both the keyboard * and mouse are grabbed, or thing will break. (In particular, other * passive X grabs in Meta can trigger but not be handled by the normal * keybinding handling code.) However, the plugin can establish the keyboard * and/or mouse grabs ahead of time and pass in the * %META_MODAL_POINTER_ALREADY_GRABBED and/or %META_MODAL_KEYBOARD_ALREADY_GRABBED * options. This facility is provided for two reasons: first to allow using * this function to establish modality after a passive grab, and second to * allow using obscure features of XGrabPointer() and XGrabKeyboard() without * having to add them to this API. * * Return value: whether we successfully grabbed the keyboard and * mouse and made the plugin modal. */ gboolean meta_plugin_begin_modal (MetaPlugin *plugin, MetaModalOptions options, guint32 timestamp) { MetaPluginPrivate *priv = meta_plugin_get_instance_private (plugin); return meta_begin_modal_for_plugin (priv->compositor, plugin, options, timestamp); } /** * meta_plugin_end_modal: * @plugin: a #MetaPlugin * @timestamp: the time used for releasing grabs * * Ends the modal operation begun with meta_plugin_begin_modal(). This * ungrabs both the mouse and keyboard even when * %META_MODAL_POINTER_ALREADY_GRABBED or * %META_MODAL_KEYBOARD_ALREADY_GRABBED were provided as options * when beginnning the modal operation. */ void meta_plugin_end_modal (MetaPlugin *plugin, guint32 timestamp) { MetaPluginPrivate *priv = meta_plugin_get_instance_private (plugin); meta_end_modal_for_plugin (priv->compositor, plugin, timestamp); } /** * meta_plugin_get_display: * @plugin: a #MetaPlugin * * Gets the #MetaDisplay corresponding to a plugin. * * Return value: (transfer none): the #MetaDisplay for the plugin */ MetaDisplay * meta_plugin_get_display (MetaPlugin *plugin) { MetaPluginPrivate *priv = meta_plugin_get_instance_private (plugin); MetaDisplay *display = meta_compositor_get_display (priv->compositor); return display; } void _meta_plugin_set_compositor (MetaPlugin *plugin, MetaCompositor *compositor) { MetaPluginPrivate *priv = meta_plugin_get_instance_private (plugin); priv->compositor = compositor; } void meta_plugin_complete_display_change (MetaPlugin *plugin, gboolean ok) { MetaMonitorManager *manager; manager = meta_monitor_manager_get (); meta_monitor_manager_confirm_configuration (manager, ok); } muffin-6.4.1/src/compositor/meta-window-actor-wayland.h0000664000175000017500000000255314723361714022103 0ustar fabiofabio/* * Copyright (C) 2018 Endless, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Georges Basile Stavracas Neto */ #ifndef META_WINDOW_ACTOR_WAYLAND_H #define META_WINDOW_ACTOR_WAYLAND_H #include "compositor/meta-window-actor-private.h" #include "wayland/meta-wayland-surface.h" #define META_TYPE_WINDOW_ACTOR_WAYLAND (meta_window_actor_wayland_get_type()) G_DECLARE_FINAL_TYPE (MetaWindowActorWayland, meta_window_actor_wayland, META, WINDOW_ACTOR_WAYLAND, MetaWindowActor) void meta_window_actor_wayland_rebuild_surface_tree (MetaWindowActor *actor); #endif /*META_WINDOW_ACTOR_WAYLAND_H */ muffin-6.4.1/src/compositor/meta-feedback-actor.c0000664000175000017500000001762214723361714020661 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ /** * SECTION:meta-feedback-actor * @title: MetaFeedbackActor * @short_description: Actor for painting user interaction feedback */ #include "config.h" #include "compositor/compositor-private.h" #include "compositor/meta-feedback-actor-private.h" #include "core/display-private.h" enum { PROP_ANCHOR_X = 1, PROP_ANCHOR_Y }; typedef struct _MetaFeedbackActorPrivate MetaFeedbackActorPrivate; struct _MetaFeedbackActorPrivate { float anchor_x; float anchor_y; float pos_x; float pos_y; int geometry_scale; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaFeedbackActor, meta_feedback_actor, CLUTTER_TYPE_ACTOR) static void meta_feedback_actor_constructed (GObject *object) { MetaDisplay *display; ClutterActor *feedback_group; display = meta_get_display (); feedback_group = meta_get_feedback_group_for_display (display); clutter_actor_add_child (feedback_group, CLUTTER_ACTOR (object)); } static void meta_feedback_actor_update_position (MetaFeedbackActor *self) { MetaFeedbackActorPrivate *priv = meta_feedback_actor_get_instance_private (self); clutter_actor_set_position (CLUTTER_ACTOR (self), priv->pos_x - (priv->anchor_x * priv->geometry_scale), priv->pos_y - (priv->anchor_y * priv->geometry_scale)); } static void meta_feedback_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaFeedbackActor *self = META_FEEDBACK_ACTOR (object); MetaFeedbackActorPrivate *priv = meta_feedback_actor_get_instance_private (self); switch (prop_id) { case PROP_ANCHOR_X: priv->anchor_x = g_value_get_int (value); meta_feedback_actor_update_position (self); break; case PROP_ANCHOR_Y: priv->anchor_y = g_value_get_int (value); meta_feedback_actor_update_position (self); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_feedback_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaFeedbackActor *self = META_FEEDBACK_ACTOR (object); MetaFeedbackActorPrivate *priv = meta_feedback_actor_get_instance_private (self); switch (prop_id) { case PROP_ANCHOR_X: g_value_set_float (value, priv->anchor_x); break; case PROP_ANCHOR_Y: g_value_set_float (value, priv->anchor_y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_feedback_actor_class_init (MetaFeedbackActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->constructed = meta_feedback_actor_constructed; object_class->set_property = meta_feedback_actor_set_property; object_class->get_property = meta_feedback_actor_get_property; pspec = g_param_spec_float ("anchor-x", "Anchor X", "The X axis of the anchor point", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANCHOR_X, pspec); pspec = g_param_spec_float ("anchor-y", "Anchor Y", "The Y axis of the anchor point", 0, G_MAXFLOAT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_ANCHOR_Y, pspec); } static void meta_feedback_actor_init (MetaFeedbackActor *self) { clutter_actor_set_reactive (CLUTTER_ACTOR (self), FALSE); } /** * meta_feedback_actor_new: * * Creates a new actor to draw the current drag and drop surface. * * Return value: the newly created background actor */ ClutterActor * meta_feedback_actor_new (float anchor_x, float anchor_y) { MetaFeedbackActor *self; self = g_object_new (META_TYPE_FEEDBACK_ACTOR, "anchor-x", anchor_x, "anchor-y", anchor_y, NULL); return CLUTTER_ACTOR (self); } void meta_feedback_actor_set_anchor (MetaFeedbackActor *self, float anchor_x, float anchor_y) { MetaFeedbackActorPrivate *priv; g_return_if_fail (META_IS_FEEDBACK_ACTOR (self)); priv = meta_feedback_actor_get_instance_private (self); if (priv->anchor_x == anchor_x && priv->anchor_y == anchor_y) return; if (priv->anchor_x != anchor_x) { priv->anchor_x = anchor_x; g_object_notify (G_OBJECT (self), "anchor-x"); } if (priv->anchor_y != anchor_y) { priv->anchor_y = anchor_y; g_object_notify (G_OBJECT (self), "anchor-y"); } meta_feedback_actor_update_position (self); } void meta_feedback_actor_get_anchor (MetaFeedbackActor *self, float *anchor_x, float *anchor_y) { MetaFeedbackActorPrivate *priv; g_return_if_fail (META_IS_FEEDBACK_ACTOR (self)); priv = meta_feedback_actor_get_instance_private (self); if (anchor_x) *anchor_x = priv->anchor_x; if (anchor_y) *anchor_y = priv->anchor_y; } void meta_feedback_actor_set_position (MetaFeedbackActor *self, float x, float y) { MetaFeedbackActorPrivate *priv; g_return_if_fail (META_IS_FEEDBACK_ACTOR (self)); priv = meta_feedback_actor_get_instance_private (self); priv->pos_x = x; priv->pos_y = y; meta_feedback_actor_update_position (self); } void meta_feedback_actor_update (MetaFeedbackActor *self, const ClutterEvent *event) { graphene_point_t point; g_return_if_fail (META_IS_FEEDBACK_ACTOR (self)); g_return_if_fail (event != NULL); clutter_event_get_position (event, &point); meta_feedback_actor_set_position (self, point.x, point.y); } void meta_feedback_actor_set_geometry_scale (MetaFeedbackActor *self, int geometry_scale) { MetaFeedbackActorPrivate *priv = meta_feedback_actor_get_instance_private (self); CoglMatrix child_transform; if (priv->geometry_scale == geometry_scale) return; priv->geometry_scale = geometry_scale; cogl_matrix_init_identity (&child_transform); cogl_matrix_scale (&child_transform, geometry_scale, geometry_scale, 1); clutter_actor_set_child_transform (CLUTTER_ACTOR (self), &child_transform); } int meta_feedback_actor_get_geometry_scale (MetaFeedbackActor *self) { MetaFeedbackActorPrivate *priv = meta_feedback_actor_get_instance_private (self); return priv->geometry_scale; } muffin-6.4.1/src/compositor/meta-plugin-manager.h0000664000175000017500000001043114723361714020731 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_PLUGIN_MANAGER_H_ #define META_PLUGIN_MANAGER_H_ #include "core/util-private.h" #include "meta/meta-plugin.h" #include "meta/types.h" typedef enum { META_PLUGIN_NONE, META_PLUGIN_MINIMIZE, META_PLUGIN_MAP, META_PLUGIN_DESTROY, META_PLUGIN_SWITCH_WORKSPACE, META_PLUGIN_UNMINIMIZE, META_PLUGIN_SIZE_CHANGE, } MetaPluginEffect; /** * MetaPluginManager: (skip) * */ typedef struct MetaPluginManager MetaPluginManager; MetaPluginManager * meta_plugin_manager_new (MetaCompositor *compositor); META_EXPORT_TEST void meta_plugin_manager_load (const gchar *plugin_name); gboolean meta_plugin_manager_event_simple (MetaPluginManager *mgr, MetaWindowActor *actor, MetaPluginEffect event); void meta_plugin_manager_event_size_changed (MetaPluginManager *mgr, MetaWindowActor *actor); gboolean meta_plugin_manager_event_size_change (MetaPluginManager *mgr, MetaWindowActor *actor, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect); gboolean meta_plugin_manager_switch_workspace (MetaPluginManager *mgr, gint from, gint to, MetaMotionDirection direction); gboolean meta_plugin_manager_filter_keybinding (MetaPluginManager *mgr, MetaKeyBinding *binding); gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *mgr, XEvent *xev); gboolean _meta_plugin_xevent_filter (MetaPlugin *plugin, XEvent *xev); void meta_plugin_manager_confirm_display_change (MetaPluginManager *mgr); gboolean meta_plugin_manager_show_tile_preview (MetaPluginManager *mgr, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number); gboolean meta_plugin_manager_hide_tile_preview (MetaPluginManager *mgr); void meta_plugin_manager_show_window_menu (MetaPluginManager *mgr, MetaWindow *window, MetaWindowMenuType menu, int x, int y); void meta_plugin_manager_show_window_menu_for_rect (MetaPluginManager *mgr, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect); MetaCloseDialog * meta_plugin_manager_create_close_dialog (MetaPluginManager *plugin_mgr, MetaWindow *window); MetaInhibitShortcutsDialog * meta_plugin_manager_create_inhibit_shortcuts_dialog (MetaPluginManager *plugin_mgr, MetaWindow *window); void meta_plugin_manager_locate_pointer (MetaPluginManager *mgr); #endif muffin-6.4.1/src/compositor/clutter-utils.c0000664000175000017500000001550714723361714017723 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for use with Cogl * * Copyright 2010 Red Hat, Inc. * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "compositor/clutter-utils.h" #include /* This file uses pixel-aligned region computation to determine what * can be clipped out. This only really works if everything is aligned * to the pixel grid - not scaled or rotated and at integer offsets. * * (This could be relaxed - if we turned off filtering for unscaled * windows then windows would be, by definition aligned to the pixel * grid. And for rectangular windows without a shape, the outline that * we draw for an unrotated window is always a rectangle because we * don't use antialasing for the window boundary - with or without * filtering, with or without a scale. But figuring out exactly * what pixels will be drawn by the graphics system in these cases * gets tricky, so we just go for the easiest part - no scale, * and at integer offsets.) * * The way we check for pixel-aligned is by looking at the * transformation into screen space of the allocation box of an actor * and and checking if the corners are "close enough" to integral * pixel values. */ /* The definition of "close enough" to integral pixel values is * equality when we convert to 24.8 fixed-point. */ static inline int round_to_fixed (float x) { return roundf (x * 256); } /* Help macros to scale from OpenGL <-1,1> coordinates system to * window coordinates ranging [0,window-size]. Borrowed from clutter-utils.c */ #define MTX_GL_SCALE_X(x,w,v1,v2) ((((((x) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) #define MTX_GL_SCALE_Y(y,w,v1,v2) ((v1) - (((((y) / (w)) + 1.0f) / 2.0f) * (v1)) + (v2)) /* This helper function checks if (according to our fixed point precision) * the vertices @verts form a box of width @widthf and height @heightf * located at integral coordinates. These coordinates are returned * in @x_origin and @y_origin. */ gboolean meta_actor_vertices_are_untransformed (graphene_point3d_t *verts, float widthf, float heightf, int *x_origin, int *y_origin) { int width, height; int v0x, v0y, v1x, v1y, v2x, v2y, v3x, v3y; int x, y; width = round_to_fixed (widthf); height = round_to_fixed (heightf); v0x = round_to_fixed (verts[0].x); v0y = round_to_fixed (verts[0].y); v1x = round_to_fixed (verts[1].x); v1y = round_to_fixed (verts[1].y); v2x = round_to_fixed (verts[2].x); v2y = round_to_fixed (verts[2].y); v3x = round_to_fixed (verts[3].x); v3y = round_to_fixed (verts[3].y); /* Using shifting for converting fixed => int, gets things right for * negative values. / 256. wouldn't do the same */ x = v0x >> 8; y = v0y >> 8; /* At integral coordinates? */ if (x * 256 != v0x || y * 256 != v0y) return FALSE; /* Not scaled? */ if (v1x - v0x != width || v2y - v0y != height) return FALSE; /* Not rotated/skewed? */ if (v0x != v2x || v0y != v1y || v3x != v1x || v3y != v2y) return FALSE; if (x_origin) *x_origin = x; if (y_origin) *y_origin = y; return TRUE; } /** * meta_actor_painting_untransformed: * @paint_width: the width of the painted area * @paint_height: the height of the painted area * @sample_width: the width of the sampled area of the texture * @sample_height: the height of the sampled area of the texture * @x_origin: if the transform is only an integer translation * then the X coordinate of the location of the origin under the transformation * from drawing space to screen pixel space is returned here. * @y_origin: if the transform is only an integer translation * then the X coordinate of the location of the origin under the transformation * from drawing space to screen pixel space is returned here. * * Determines if the current painting transform is an integer translation. * This can differ from the result of meta_actor_is_untransformed() when * painting an actor if we're inside a inside a clone paint. @paint_width * and @paint_height are used to determine the vertices of the rectangle * we check to see if the painted area is "close enough" to the integer * transform. */ gboolean meta_actor_painting_untransformed (CoglFramebuffer *fb, int paint_width, int paint_height, int sample_width, int sample_height, int *x_origin, int *y_origin) { CoglMatrix modelview, projection, modelview_projection; graphene_point3d_t vertices[4]; float viewport[4]; int i; cogl_framebuffer_get_modelview_matrix (fb, &modelview); cogl_framebuffer_get_projection_matrix (fb, &projection); cogl_matrix_multiply (&modelview_projection, &projection, &modelview); vertices[0].x = 0; vertices[0].y = 0; vertices[0].z = 0; vertices[1].x = paint_width; vertices[1].y = 0; vertices[1].z = 0; vertices[2].x = 0; vertices[2].y = paint_height; vertices[2].z = 0; vertices[3].x = paint_width; vertices[3].y = paint_height; vertices[3].z = 0; cogl_framebuffer_get_viewport4fv (fb, viewport); for (i = 0; i < 4; i++) { float w = 1; cogl_matrix_transform_point (&modelview_projection, &vertices[i].x, &vertices[i].y, &vertices[i].z, &w); vertices[i].x = MTX_GL_SCALE_X (vertices[i].x, w, viewport[2], viewport[0]); vertices[i].y = MTX_GL_SCALE_Y (vertices[i].y, w, viewport[3], viewport[1]); } return meta_actor_vertices_are_untransformed (vertices, sample_width, sample_height, x_origin, y_origin); } muffin-6.4.1/src/compositor/region-utils.c0000664000175000017500000003434614723361714017526 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for region manipulation * * Copyright (C) 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "backends/meta-monitor-transform.h" #include "compositor/region-utils.h" #include "core/boxes-private.h" #include #define META_REGION_MAX_STACK_RECTS 256 #define META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED(n_rects, rects) \ g_autofree cairo_rectangle_int_t *G_PASTE(__n, __LINE__) = NULL; \ if (n_rects < META_REGION_MAX_STACK_RECTS) \ rects = g_newa (cairo_rectangle_int_t, n_rects); \ else \ rects = G_PASTE(__n, __LINE__) = g_new (cairo_rectangle_int_t, n_rects); /* MetaRegionBuilder */ /* Various algorithms in this file require unioning together a set of rectangles * that are unsorted or overlap; unioning such a set of rectangles 1-by-1 * using cairo_region_union_rectangle() produces O(N^2) behavior (if the union * adds or removes rectangles in the middle of the region, then it has to * move all the rectangles after that.) To avoid this behavior, MetaRegionBuilder * creates regions for small groups of rectangles and merges them together in * a binary tree. * * Possible improvement: From a glance at the code, accumulating all the rectangles * into a flat array and then calling the (not usefully documented) * cairo_region_create_rectangles() would have the same behavior and would be * simpler and a bit more efficient. */ /* Optimium performance seems to be with MAX_CHUNK_RECTANGLES=4; 8 is about 10% slower. * But using 8 may be more robust to systems with slow malloc(). */ #define MAX_CHUNK_RECTANGLES 8 void meta_region_builder_init (MetaRegionBuilder *builder) { int i; for (i = 0; i < META_REGION_BUILDER_MAX_LEVELS; i++) builder->levels[i] = NULL; builder->n_levels = 1; } void meta_region_builder_add_rectangle (MetaRegionBuilder *builder, int x, int y, int width, int height) { cairo_rectangle_int_t rect; int i; if (builder->levels[0] == NULL) builder->levels[0] = cairo_region_create (); rect.x = x; rect.y = y; rect.width = width; rect.height = height; cairo_region_union_rectangle (builder->levels[0], &rect); if (cairo_region_num_rectangles (builder->levels[0]) >= MAX_CHUNK_RECTANGLES) { for (i = 1; i < builder->n_levels + 1; i++) { if (builder->levels[i] == NULL) { if (i < META_REGION_BUILDER_MAX_LEVELS) { builder->levels[i] = builder->levels[i - 1]; builder->levels[i - 1] = NULL; if (i == builder->n_levels) builder->n_levels++; } break; } else { cairo_region_union (builder->levels[i], builder->levels[i - 1]); cairo_region_destroy (builder->levels[i - 1]); builder->levels[i - 1] = NULL; } } } } cairo_region_t * meta_region_builder_finish (MetaRegionBuilder *builder) { cairo_region_t *result = NULL; int i; for (i = 0; i < builder->n_levels; i++) { if (builder->levels[i]) { if (result == NULL) result = builder->levels[i]; else { cairo_region_union(result, builder->levels[i]); cairo_region_destroy (builder->levels[i]); } } } if (result == NULL) result = cairo_region_create (); return result; } /* MetaRegionIterator */ void meta_region_iterator_init (MetaRegionIterator *iter, cairo_region_t *region) { iter->region = region; iter->i = 0; iter->n_rectangles = cairo_region_num_rectangles (region); iter->line_start = TRUE; if (iter->n_rectangles > 1) { cairo_region_get_rectangle (region, 0, &iter->rectangle); cairo_region_get_rectangle (region, 1, &iter->next_rectangle); iter->line_end = iter->next_rectangle.y != iter->rectangle.y; } else if (iter->n_rectangles > 0) { cairo_region_get_rectangle (region, 0, &iter->rectangle); iter->line_end = TRUE; } } gboolean meta_region_iterator_at_end (MetaRegionIterator *iter) { return iter->i >= iter->n_rectangles; } void meta_region_iterator_next (MetaRegionIterator *iter) { iter->i++; iter->rectangle = iter->next_rectangle; iter->line_start = iter->line_end; if (iter->i + 1 < iter->n_rectangles) { cairo_region_get_rectangle (iter->region, iter->i + 1, &iter->next_rectangle); iter->line_end = iter->next_rectangle.y != iter->rectangle.y; } else { iter->line_end = TRUE; } } cairo_region_t * meta_region_scale_double (cairo_region_t *region, double scale, MetaRoundingStrategy rounding_strategy) { int n_rects, i; cairo_rectangle_int_t *rects; cairo_region_t *scaled_region; g_return_val_if_fail (scale > 0.0, NULL); if (G_APPROX_VALUE (scale, 1.f, FLT_EPSILON)) return cairo_region_copy (region); n_rects = cairo_region_num_rectangles (region); META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED (n_rects, rects); for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (region, i, &rects[i]); meta_rectangle_scale_double (&rects[i], scale, rounding_strategy, &rects[i]); } scaled_region = cairo_region_create_rectangles (rects, n_rects); return scaled_region; } cairo_region_t * meta_region_scale (cairo_region_t *region, int scale) { int n_rects, i; cairo_rectangle_int_t *rects; cairo_region_t *scaled_region; if (scale == 1) return cairo_region_copy (region); n_rects = cairo_region_num_rectangles (region); META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED (n_rects, rects); for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (region, i, &rects[i]); rects[i].x *= scale; rects[i].y *= scale; rects[i].width *= scale; rects[i].height *= scale; } scaled_region = cairo_region_create_rectangles (rects, n_rects); return scaled_region; } static void add_expanded_rect (MetaRegionBuilder *builder, int x, int y, int width, int height, int x_amount, int y_amount, gboolean flip) { if (flip) meta_region_builder_add_rectangle (builder, y - y_amount, x - x_amount, height + 2 * y_amount, width + 2 * x_amount); else meta_region_builder_add_rectangle (builder, x - x_amount, y - y_amount, width + 2 * x_amount, height + 2 * y_amount); } static cairo_region_t * expand_region (cairo_region_t *region, int x_amount, int y_amount, gboolean flip) { MetaRegionBuilder builder; int n; int i; meta_region_builder_init (&builder); n = cairo_region_num_rectangles (region); for (i = 0; i < n; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); add_expanded_rect (&builder, rect.x, rect.y, rect.width, rect.height, x_amount, y_amount, flip); } return meta_region_builder_finish (&builder); } /* This computes a (clipped version) of the inverse of the region * and expands it by the given amount */ static cairo_region_t * expand_region_inverse (cairo_region_t *region, int x_amount, int y_amount, gboolean flip) { MetaRegionBuilder builder; MetaRegionIterator iter; cairo_rectangle_int_t extents; int last_x; meta_region_builder_init (&builder); cairo_region_get_extents (region, &extents); add_expanded_rect (&builder, extents.x, extents.y - 1, extents.width, 1, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x - 1, extents.y, 1, extents.height, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x + extents.width, extents.y, 1, extents.height, x_amount, y_amount, flip); add_expanded_rect (&builder, extents.x, extents.y + extents.height, extents.width, 1, x_amount, y_amount, flip); last_x = extents.x; for (meta_region_iterator_init (&iter, region); !meta_region_iterator_at_end (&iter); meta_region_iterator_next (&iter)) { if (iter.rectangle.x > last_x) add_expanded_rect (&builder, last_x, iter.rectangle.y, iter.rectangle.x - last_x, iter.rectangle.height, x_amount, y_amount, flip); if (iter.line_end) { if (extents.x + extents.width > iter.rectangle.x + iter.rectangle.width) add_expanded_rect (&builder, iter.rectangle.x + iter.rectangle.width, iter.rectangle.y, (extents.x + extents.width) - (iter.rectangle.x + iter.rectangle.width), iter.rectangle.height, x_amount, y_amount, flip); last_x = extents.x; } else last_x = iter.rectangle.x + iter.rectangle.width; } return meta_region_builder_finish (&builder); } /** * meta_make_border_region: * @region: a #cairo_region_t * @x_amount: distance from the border to extend horizontally * @y_amount: distance from the border to extend vertically * @flip: if true, the result is computed with x and y interchanged * * Computes the "border region" of a given region, which is roughly * speaking the set of points near the boundary of the region. If we * define the operation of growing a region as computing the set of * points within a given manhattan distance of the region, then the * border is 'grow(region) intersect grow(inverse(region))'. * * If we create an image by filling the region with a solid color, * the border is the region affected by blurring the region. * * Return value: a new region which is the border of the given region */ cairo_region_t * meta_make_border_region (cairo_region_t *region, int x_amount, int y_amount, gboolean flip) { cairo_region_t *border_region; cairo_region_t *inverse_region; border_region = expand_region (region, x_amount, y_amount, flip); inverse_region = expand_region_inverse (region, x_amount, y_amount, flip); cairo_region_intersect (border_region, inverse_region); cairo_region_destroy (inverse_region); return border_region; } cairo_region_t * meta_region_transform (cairo_region_t *region, MetaMonitorTransform transform, int width, int height) { int n_rects, i; cairo_rectangle_int_t *rects; cairo_region_t *transformed_region; if (transform == META_MONITOR_TRANSFORM_NORMAL) return cairo_region_copy (region); n_rects = cairo_region_num_rectangles (region); META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED (n_rects, rects); for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (region, i, &rects[i]); meta_rectangle_transform (&rects[i], transform, width, height, &rects[i]); } transformed_region = cairo_region_create_rectangles (rects, n_rects); return transformed_region; } cairo_region_t * meta_region_crop_and_scale (cairo_region_t *region, graphene_rect_t *src_rect, int dst_width, int dst_height) { int n_rects, i; cairo_rectangle_int_t *rects; cairo_region_t *viewport_region; if (G_APPROX_VALUE (src_rect->size.width, dst_width, FLT_EPSILON) && G_APPROX_VALUE (src_rect->size.height, dst_height, FLT_EPSILON) && G_APPROX_VALUE (roundf (src_rect->origin.x), src_rect->origin.x, FLT_EPSILON) && G_APPROX_VALUE (roundf (src_rect->origin.y), src_rect->origin.y, FLT_EPSILON)) { viewport_region = cairo_region_copy (region); if (G_APPROX_VALUE (src_rect->origin.x, 0, FLT_EPSILON) || G_APPROX_VALUE (src_rect->origin.y, 0, FLT_EPSILON)) { cairo_region_translate (viewport_region, (int) src_rect->origin.x, (int) src_rect->origin.y); } return viewport_region; } n_rects = cairo_region_num_rectangles (region); META_REGION_CREATE_RECTANGLE_ARRAY_SCOPED (n_rects, rects); for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (region, i, &rects[i]); meta_rectangle_crop_and_scale (&rects[i], src_rect, dst_width, dst_height, &rects[i]); } viewport_region = cairo_region_create_rectangles (rects, n_rects); return viewport_region; } muffin-6.4.1/src/compositor/meta-background-private.h0000664000175000017500000000104214723361714021610 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_BACKGROUND_PRIVATE_H #define META_BACKGROUND_PRIVATE_H #include "cogl/cogl.h" #include "meta/meta-background.h" CoglTexture *meta_background_get_texture (MetaBackground *self, int monitor_index, cairo_rectangle_int_t *texture_area, CoglPipelineWrapMode *wrap_mode); #endif /* META_BACKGROUND_PRIVATE_H */ muffin-6.4.1/src/compositor/cogl-utils.c0000664000175000017500000001016114723361714017154 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for use with Cogl * * Copyright 2010 Red Hat, Inc. * Copyright 2010 Intel Corporation * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "clutter/clutter.h" #include "compositor/cogl-utils.h" /* Based on gnome-shell/src/st/st-private.c:_st_create_texture_material.c */ /** * meta_create_texture_pipeline: * @src_texture: (nullable): texture to use initially for the layer * * Creates a pipeline with a single layer. Using a common template * makes it easier for Cogl to share a shader for different uses in * Mutter. * * Return value: (transfer full): a newly created #CoglPipeline */ CoglPipeline * meta_create_texture_pipeline (CoglTexture *src_texture) { static CoglPipeline *texture_pipeline_template = NULL; CoglPipeline *pipeline; /* The only state used in the pipeline that would affect the shader generation is the texture type on the layer. Therefore we create a template pipeline which sets this state and all texture pipelines are created as a copy of this. That way Cogl can find the shader state for the pipeline more quickly by looking at the pipeline ancestry instead of resorting to the shader cache. */ if (G_UNLIKELY (texture_pipeline_template == NULL)) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); texture_pipeline_template = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_null_texture (texture_pipeline_template, 0); } pipeline = cogl_pipeline_copy (texture_pipeline_template); if (src_texture != NULL) cogl_pipeline_set_layer_texture (pipeline, 0, src_texture); return pipeline; } /** * meta_create_texture: * @width: width of the texture to create * @height: height of the texture to create * @components; components to store in the texture (color or alpha) * @flags: flags that affect the allocation behavior * * Creates a texture of the given size with the specified components * for use as a frame buffer object. * * If %META_TEXTURE_ALLOW_SLICING is present in @flags, and the texture * is larger than the texture size limits of the system, then the texture * will be created as a sliced texture. This also will cause problems * with using the texture with GLSL, and is more likely to be an issue * since all GL implementations have texture size limits, and they can * be as small as 2048x2048 on reasonably current systems. */ CoglTexture * meta_create_texture (int width, int height, CoglTextureComponents components, MetaTextureFlags flags) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); CoglTexture *texture; texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (ctx, width, height)); cogl_texture_set_components (texture, components); if ((flags & META_TEXTURE_ALLOW_SLICING) != 0) { /* To find out if we need to slice the texture, we have to go ahead and force storage * to be allocated */ GError *catch_error = NULL; if (!cogl_texture_allocate (texture, &catch_error)) { g_error_free (catch_error); cogl_object_unref (texture); texture = COGL_TEXTURE (cogl_texture_2d_sliced_new_with_size (ctx, width, height, COGL_TEXTURE_MAX_WASTE)); cogl_texture_set_components (texture, components); } } return texture; } muffin-6.4.1/src/compositor/meta-background-image.c0000664000175000017500000002353314723361714021224 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-background-image * @title: MetaBackgroundImage * @short_description: objects holding images loaded from files, used for backgrounds */ #include "config.h" #include "meta/meta-background-image.h" #include #include #include "clutter/clutter.h" #include "compositor/cogl-utils.h" enum { LOADED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /** * MetaBackgroundImageCache: * * #MetaBackgroundImageCache caches loading of textures for backgrounds; there's actually * nothing background specific about it, other than it is tuned to work well for * large images as typically are used for backgrounds. */ struct _MetaBackgroundImageCache { GObject parent_instance; GHashTable *images; }; /** * MetaBackgroundImage: * * #MetaBackgroundImage is an object that represents a loaded or loading background image. */ struct _MetaBackgroundImage { GObject parent_instance; GFile *file; MetaBackgroundImageCache *cache; gboolean in_cache; gboolean loaded; CoglTexture *texture; }; G_DEFINE_TYPE (MetaBackgroundImageCache, meta_background_image_cache, G_TYPE_OBJECT); static void meta_background_image_cache_init (MetaBackgroundImageCache *cache) { cache->images = g_hash_table_new (g_file_hash, (GEqualFunc) g_file_equal); } static void meta_background_image_cache_finalize (GObject *object) { MetaBackgroundImageCache *cache = META_BACKGROUND_IMAGE_CACHE (object); GHashTableIter iter; gpointer key, value; g_hash_table_iter_init (&iter, cache->images); while (g_hash_table_iter_next (&iter, &key, &value)) { MetaBackgroundImage *image = value; image->in_cache = FALSE; } g_hash_table_destroy (cache->images); G_OBJECT_CLASS (meta_background_image_cache_parent_class)->finalize (object); } static void meta_background_image_cache_class_init (MetaBackgroundImageCacheClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_background_image_cache_finalize; } /** * meta_background_image_cache_get_default: * * Return value: (transfer none): the global singleton background cache */ MetaBackgroundImageCache * meta_background_image_cache_get_default (void) { static MetaBackgroundImageCache *cache; if (cache == NULL) cache = g_object_new (META_TYPE_BACKGROUND_IMAGE_CACHE, NULL); return cache; } static void load_file (GTask *task, MetaBackgroundImage *image, gpointer task_data, GCancellable *cancellable) { GError *error = NULL; GdkPixbuf *pixbuf; GFileInputStream *stream; stream = g_file_read (image->file, NULL, &error); if (stream == NULL) { g_task_return_error (task, error); return; } pixbuf = gdk_pixbuf_new_from_stream (G_INPUT_STREAM (stream), NULL, &error); g_object_unref (stream); if (pixbuf == NULL) { g_task_return_error (task, error); return; } g_task_return_pointer (task, pixbuf, (GDestroyNotify) g_object_unref); } static void file_loaded (GObject *source_object, GAsyncResult *result, gpointer user_data) { MetaBackgroundImage *image = META_BACKGROUND_IMAGE (source_object); GError *error = NULL; GError *catch_error = NULL; GTask *task; CoglTexture *texture; GdkPixbuf *pixbuf, *rotated; int width, height, row_stride; guchar *pixels; gboolean has_alpha; task = G_TASK (result); pixbuf = g_task_propagate_pointer (task, &error); if (pixbuf == NULL) { char *uri = g_file_get_uri (image->file); g_warning ("Failed to load background '%s': %s", uri, error->message); g_clear_error (&error); g_free (uri); goto out; } rotated = gdk_pixbuf_apply_embedded_orientation (pixbuf); if (rotated != NULL) { g_object_unref (pixbuf); pixbuf = rotated; } width = gdk_pixbuf_get_width (pixbuf); height = gdk_pixbuf_get_height (pixbuf); row_stride = gdk_pixbuf_get_rowstride (pixbuf); pixels = gdk_pixbuf_get_pixels (pixbuf); has_alpha = gdk_pixbuf_get_has_alpha (pixbuf); texture = meta_create_texture (width, height, has_alpha ? COGL_TEXTURE_COMPONENTS_RGBA : COGL_TEXTURE_COMPONENTS_RGB, META_TEXTURE_ALLOW_SLICING); if (!cogl_texture_set_data (texture, has_alpha ? COGL_PIXEL_FORMAT_RGBA_8888 : COGL_PIXEL_FORMAT_RGB_888, row_stride, pixels, 0, &catch_error)) { g_warning ("Failed to create texture for background"); g_error_free (catch_error); cogl_object_unref (texture); } image->texture = texture; out: if (pixbuf != NULL) g_object_unref (pixbuf); image->loaded = TRUE; g_signal_emit (image, signals[LOADED], 0); } /** * meta_background_image_cache_load: * @cache: a #MetaBackgroundImageCache * @file: #GFile to load * * Loads an image to use as a background, or returns a reference to an * image that is already in the process of loading or loaded. In either * case, what is returned is a #MetaBackgroundImage which can be derefenced * to get a #CoglTexture. If meta_background_image_is_loaded() returns %TRUE, * the background is loaded, otherwise the MetaBackgroundImage::loaded * signal will be emitted exactly once. The 'loaded' state means that the * loading process finished, whether it succeeded or failed. * * Return value: (transfer full): a #MetaBackgroundImage to dereference to get the loaded texture */ MetaBackgroundImage * meta_background_image_cache_load (MetaBackgroundImageCache *cache, GFile *file) { MetaBackgroundImage *image; GTask *task; g_return_val_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache), NULL); g_return_val_if_fail (file != NULL, NULL); image = g_hash_table_lookup (cache->images, file); if (image != NULL) return g_object_ref (image); image = g_object_new (META_TYPE_BACKGROUND_IMAGE, NULL); image->cache = cache; image->in_cache = TRUE; image->file = g_object_ref (file); g_hash_table_insert (cache->images, image->file, image); task = g_task_new (image, NULL, file_loaded, NULL); g_task_run_in_thread (task, (GTaskThreadFunc) load_file); g_object_unref (task); return image; } /** * meta_background_image_cache_purge: * @cache: a #MetaBackgroundImageCache * @file: file to remove from the cache * * Remove an entry from the cache; this would be used if monitoring * showed that the file changed. */ void meta_background_image_cache_purge (MetaBackgroundImageCache *cache, GFile *file) { MetaBackgroundImage *image; g_return_if_fail (META_IS_BACKGROUND_IMAGE_CACHE (cache)); g_return_if_fail (file != NULL); image = g_hash_table_lookup (cache->images, file); if (image == NULL) return; g_hash_table_remove (cache->images, image->file); image->in_cache = FALSE; } G_DEFINE_TYPE (MetaBackgroundImage, meta_background_image, G_TYPE_OBJECT); static void meta_background_image_init (MetaBackgroundImage *image) { } static void meta_background_image_finalize (GObject *object) { MetaBackgroundImage *image = META_BACKGROUND_IMAGE (object); if (image->in_cache) g_hash_table_remove (image->cache->images, image->file); if (image->texture) cogl_object_unref (image->texture); if (image->file) g_object_unref (image->file); G_OBJECT_CLASS (meta_background_image_parent_class)->finalize (object); } static void meta_background_image_class_init (MetaBackgroundImageClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_background_image_finalize; signals[LOADED] = g_signal_new ("loaded", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } /** * meta_background_image_is_loaded: * @image: a #MetaBackgroundImage * * Return value: %TRUE if loading has already completed, %FALSE otherwise */ gboolean meta_background_image_is_loaded (MetaBackgroundImage *image) { g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE); return image->loaded; } /** * meta_background_image_get_success: * @image: a #MetaBackgroundImage * * This function is a convenience function for checking for success, * without having to call meta_background_image_get_texture() and * handle the return of a Cogl type. * * Return value: %TRUE if loading completed successfully, otherwise %FALSE */ gboolean meta_background_image_get_success (MetaBackgroundImage *image) { g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), FALSE); return image->texture != NULL; } /** * meta_background_image_get_texture: * @image: a #MetaBackgroundImage * * Return value: (transfer none): a #CoglTexture if loading succeeded; if * loading failed or has not yet finished, %NULL. */ CoglTexture * meta_background_image_get_texture (MetaBackgroundImage *image) { g_return_val_if_fail (META_IS_BACKGROUND_IMAGE (image), NULL); return image->texture; } muffin-6.4.1/src/compositor/meta-texture-tower.h0000664000175000017500000000560714723361714020672 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * MetaTextureTower * * Mipmap emulation by creation of scaled down images * * Copyright (C) 2009 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_TEXTURE_TOWER_H__ #define __META_TEXTURE_TOWER_H__ #include "clutter/clutter.h" G_BEGIN_DECLS /** * SECTION:MetaTextureTower * @short_description: mipmap emulation by creation of scaled down images * * A #MetaTextureTower is used to get good looking scaled down images when * we can't use the GL drivers mipmap support. There are two separate reasons * * - Some cards (including radeon cards <= r5xx) only support * TEXTURE_RECTANGLE_ARB and not NPOT textures. Rectangular textures * are defined not to support mipmapping. * - Even when NPOT textures are available, the combination of NPOT * textures, texture_from_pixmap, and mipmapping doesn't typically * work, since the X server doesn't allocate pixmaps in the right * layout for mipmapping. * * So, what we do is create the "mipmap" levels ourselves by successive * power-of-two scaledowns, and when rendering pick the single texture * that best matches the scale we are rendering at. (Since we aren't * typically using perspective transforms, we'll frequently have a single * scale for the entire texture.) */ typedef struct _MetaTextureTower MetaTextureTower; MetaTextureTower *meta_texture_tower_new (void); void meta_texture_tower_free (MetaTextureTower *tower); void meta_texture_tower_set_base_texture (MetaTextureTower *tower, CoglTexture *texture); void meta_texture_tower_update_area (MetaTextureTower *tower, int x, int y, int width, int height); CoglTexture *meta_texture_tower_get_paint_texture (MetaTextureTower *tower, ClutterPaintContext *paint_context); G_END_DECLS #endif /* __META_TEXTURE_TOWER_H__ */ muffin-6.4.1/src/compositor/README0000664000175000017500000000476414723361714015622 0ustar fabiofabioIntro ===== In general, the compositor splits the window from the contents of the window from the shape of the window. In other words, a window has contents, and the contents of the window have a shape. This is represented by the actor hierarchy: +--------------------------------------+ | MetaWindowActor | | +----------------------------------+ | | | MetaSurfaceActor | | | | +------------------------------+ | | | | | MetaShapedTexture | | | | | | | | | | | | | | | | | | | | | | | | | | | | | +------------------------------+ | | | +----------------------------------+ | +--------------------------------------+ Surfaces may also contain subsurfaces. The MetaWindowActor and MetaSurfaceActor subclasses that will be created depend on the client type, and the display server type. ## Subsurfaces Additionally, there is also the case of subsurfaces: surfaces that are child of other surfaces. That is also represented in the actor hierarchy by having one or many MetaSurfaceActors (the subsurfaces) added as children of a parent MetaSurfaceActor. There are no limits to how many subsurfaces a surface may have. With subsurfaces, the actor hierarchy looks like this: MetaWindowActor ↳ MetaSurfaceActor (surface) ↳ MetaShapedTexture ↳ MetaSurfaceActor (subsurface) ↳ MetaShapedTexture ↳ MetaSurfaceActor (sub-subsurface) ↳ MetaShapedTexture ↳ MetaSurfaceActor (subsurface) ↳ MetaShapedTexture In this example, the main surface has 2 subsurfaces. One of these subsurfaces contains a subsurface as well. All MetaWindowActors contain at least one MetaSurfaceActor, and all MetaSurfaceActors contain a MetaShapedTexture. ## Client and compositor MetaWindowActor and its subclasses represent the client window's type. A X11 client will have a MetaWindowActorX11 representing it, and a Wayland client will have a MetaWindowActorWayland. On the compositor side, the surface where the contents of the window are drawn into are represented by MetaSurfaceActor subclasses. On a Wayland session, windows are backed by a MetaSurfaceActorWayland surface, whereas on X11 sessions, by MetaSurfaceActorX11. XWayland windows are X11 client windows (MetaWindowActorX11) backed by Wayland surfaces (MetaWindowActorWayland). Env Vars ======== MUTTER_DISABLE_MIPMAPS - set to disable use of mipmaped windows.muffin-6.4.1/src/compositor/meta-shadow-factory.c0000664000175000017500000010642414723361714020760 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-shadow-factory * @title: MetaShadowFactory * @short_description: Create and cache shadow textures for abritrary window shapes */ #include "config.h" #include #include #include "compositor/cogl-utils.h" #include "compositor/region-utils.h" #include "meta/meta-shadow-factory.h" #include "meta/util.h" /* This file implements blurring the shape of a window to produce a * shadow texture. The details are discussed below; a quick summary * of the optimizations we use: * * - If the window shape is along the lines of a rounded rectangle - * a rectangular center portion with stuff at the corners - then * the blur of this - the shadow - can also be represented as a * 9-sliced texture and the same texture can be used for different * size. * * - We use the fact that a Gaussian blur is separable to do a * 2D blur as 1D blur of the rows followed by a 1D blur of the * columns. * * - For better cache efficiency, we blur rows, transpose the image * in blocks, blur rows again, and then transpose back. * * - We approximate the 1D gaussian blur as 3 successive box filters. */ typedef struct _MetaShadowCacheKey MetaShadowCacheKey; typedef struct _MetaShadowClassInfo MetaShadowClassInfo; struct _MetaShadowCacheKey { MetaWindowShape *shape; int radius; int top_fade; }; struct _MetaShadow { int ref_count; MetaShadowFactory *factory; MetaShadowCacheKey key; CoglTexture *texture; CoglPipeline *pipeline; /* The outer order is the distance the shadow extends outside the window * shape; the inner border is the unscaled portion inside the window * shape */ int outer_border_top; int inner_border_top; int outer_border_right; int inner_border_right; int outer_border_bottom; int inner_border_bottom; int outer_border_left; int inner_border_left; guint scale_width : 1; guint scale_height : 1; }; struct _MetaShadowClassInfo { const char *name; /* const so we can reuse for static definitions */ MetaShadowParams focused; MetaShadowParams unfocused; }; struct _MetaShadowFactory { GObject parent_instance; /* MetaShadowCacheKey => MetaShadow; the shadows are not referenced * by the factory, they are simply removed from the table when freed */ GHashTable *shadows; /* class name => MetaShadowClassInfo */ GHashTable *shadow_classes; }; enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; /* The first element in this array also defines the default parameters * for newly created classes */ MetaShadowClassInfo default_shadow_classes[] = { { "normal", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "dialog", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "modal_dialog", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "utility", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "border", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "menu", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } }, { "popup-menu", { 1, -1, 0, 0, 128 }, { 1, -1, 0, 0, 128 } }, { "dropdown-menu", { 1, -1, 0, 0, 128 }, { 1, -1, 0, 0, 128 } }, { "attached", { 10, -1, 0, 3, 128 }, { 8, -1, 0, 2, 64 } } }; G_DEFINE_TYPE (MetaShadowFactory, meta_shadow_factory, G_TYPE_OBJECT); static guint meta_shadow_cache_key_hash (gconstpointer val) { const MetaShadowCacheKey *key = val; return 59 * key->radius + 67 * key->top_fade + 73 * meta_window_shape_hash (key->shape); } static gboolean meta_shadow_cache_key_equal (gconstpointer a, gconstpointer b) { const MetaShadowCacheKey *key_a = a; const MetaShadowCacheKey *key_b = b; return (key_a->radius == key_b->radius && key_a->top_fade == key_b->top_fade && meta_window_shape_equal (key_a->shape, key_b->shape)); } MetaShadow * meta_shadow_ref (MetaShadow *shadow) { shadow->ref_count++; return shadow; } void meta_shadow_unref (MetaShadow *shadow) { shadow->ref_count--; if (shadow->ref_count == 0) { if (shadow->factory) { g_hash_table_remove (shadow->factory->shadows, &shadow->key); } meta_window_shape_unref (shadow->key.shape); cogl_object_unref (shadow->texture); cogl_object_unref (shadow->pipeline); g_slice_free (MetaShadow, shadow); } } /** * meta_shadow_paint: * @window_x: x position of the region to paint a shadow for * @window_y: y position of the region to paint a shadow for * @window_width: actual width of the region to paint a shadow for * @window_height: actual height of the region to paint a shadow for * @clip: (nullable): if non-%NULL specifies the visible portion * of the shadow. * @clip_strictly: if %TRUE, drawing will be clipped strictly * to @clip, otherwise, it will be only used to optimize * drawing. * * Paints the shadow at the given position, for the specified actual * size of the region. (Since a #MetaShadow can be shared between * different sizes with the same extracted #MetaWindowShape the * size needs to be passed in here.) */ void meta_shadow_paint (MetaShadow *shadow, CoglFramebuffer *framebuffer, int window_x, int window_y, int window_width, int window_height, guint8 opacity, cairo_region_t *clip, gboolean clip_strictly) { float texture_width = cogl_texture_get_width (shadow->texture); float texture_height = cogl_texture_get_height (shadow->texture); int i, j; float src_x[4]; float src_y[4]; int dest_x[4]; int dest_y[4]; int n_x, n_y; if (clip && cairo_region_is_empty (clip)) return; cogl_pipeline_set_color4ub (shadow->pipeline, opacity, opacity, opacity, opacity); if (shadow->scale_width) { n_x = 3; src_x[0] = 0.0; src_x[1] = (shadow->inner_border_left + shadow->outer_border_left) / texture_width; src_x[2] = (texture_width - (shadow->inner_border_right + shadow->outer_border_right)) / texture_width; src_x[3] = 1.0; dest_x[0] = window_x - shadow->outer_border_left; dest_x[1] = window_x + shadow->inner_border_left; dest_x[2] = window_x + window_width - shadow->inner_border_right; dest_x[3] = window_x + window_width + shadow->outer_border_right; } else { n_x = 1; src_x[0] = 0.0; src_x[1] = 1.0; dest_x[0] = window_x - shadow->outer_border_left; dest_x[1] = window_x + window_width + shadow->outer_border_right; } if (shadow->scale_height) { n_y = 3; src_y[0] = 0.0; src_y[1] = (shadow->inner_border_top + shadow->outer_border_top) / texture_height; src_y[2] = (texture_height - (shadow->inner_border_bottom + shadow->outer_border_bottom)) / texture_height; src_y[3] = 1.0; dest_y[0] = window_y - shadow->outer_border_top; dest_y[1] = window_y + shadow->inner_border_top; dest_y[2] = window_y + window_height - shadow->inner_border_bottom; dest_y[3] = window_y + window_height + shadow->outer_border_bottom; } else { n_y = 1; src_y[0] = 0.0; src_y[1] = 1.0; dest_y[0] = window_y - shadow->outer_border_top; dest_y[1] = window_y + window_height + shadow->outer_border_bottom; } for (j = 0; j < n_y; j++) { cairo_rectangle_int_t dest_rect; dest_rect.y = dest_y[j]; dest_rect.height = dest_y[j + 1] - dest_y[j]; if (dest_rect.height == 0) continue; for (i = 0; i < n_x; i++) { cairo_region_overlap_t overlap; dest_rect.x = dest_x[i]; dest_rect.width = dest_x[i + 1] - dest_x[i]; if (dest_rect.width == 0) continue; if (clip) overlap = cairo_region_contains_rectangle (clip, &dest_rect); else overlap = CAIRO_REGION_OVERLAP_IN; if (overlap == CAIRO_REGION_OVERLAP_OUT) continue; /* There's quite a bit of overhead from allocating a new * region in order to find an exact intersection and * generating more geometry - we make the assumption that * unless we have to clip strictly it will be cheaper to * just draw the entire rectangle. */ if (overlap == CAIRO_REGION_OVERLAP_IN || (overlap == CAIRO_REGION_OVERLAP_PART && !clip_strictly)) { cogl_framebuffer_draw_textured_rectangle (framebuffer, shadow->pipeline, dest_x[i], dest_y[j], dest_x[i + 1], dest_y[j + 1], src_x[i], src_y[j], src_x[i + 1], src_y[j + 1]); } else if (overlap == CAIRO_REGION_OVERLAP_PART) { cairo_region_t *intersection; int n_rectangles, k; intersection = cairo_region_create_rectangle (&dest_rect); cairo_region_intersect (intersection, clip); n_rectangles = cairo_region_num_rectangles (intersection); for (k = 0; k < n_rectangles; k++) { cairo_rectangle_int_t rect; float src_x1, src_x2, src_y1, src_y2; cairo_region_get_rectangle (intersection, k, &rect); /* Separately linear interpolate X and Y coordinates in the source * based on the destination X and Y coordinates */ src_x1 = (src_x[i] * (dest_rect.x + dest_rect.width - rect.x) + src_x[i + 1] * (rect.x - dest_rect.x)) / dest_rect.width; src_x2 = (src_x[i] * (dest_rect.x + dest_rect.width - (rect.x + rect.width)) + src_x[i + 1] * (rect.x + rect.width - dest_rect.x)) / dest_rect.width; src_y1 = (src_y[j] * (dest_rect.y + dest_rect.height - rect.y) + src_y[j + 1] * (rect.y - dest_rect.y)) / dest_rect.height; src_y2 = (src_y[j] * (dest_rect.y + dest_rect.height - (rect.y + rect.height)) + src_y[j + 1] * (rect.y + rect.height - dest_rect.y)) / dest_rect.height; cogl_framebuffer_draw_textured_rectangle (framebuffer, shadow->pipeline, rect.x, rect.y, rect.x + rect.width, rect.y + rect.height, src_x1, src_y1, src_x2, src_y2); } cairo_region_destroy (intersection); } } } } /** * meta_shadow_get_bounds: * @shadow: a #MetaShadow * @window_x: x position of the region to paint a shadow for * @window_y: y position of the region to paint a shadow for * @window_width: actual width of the region to paint a shadow for * @window_height: actual height of the region to paint a shadow for * * Computes the bounds of the pixels that will be affected by * meta_shadow_paint() */ void meta_shadow_get_bounds (MetaShadow *shadow, int window_x, int window_y, int window_width, int window_height, cairo_rectangle_int_t *bounds) { bounds->x = window_x - shadow->outer_border_left; bounds->y = window_y - shadow->outer_border_top; bounds->width = window_width + shadow->outer_border_left + shadow->outer_border_right; bounds->height = window_height + shadow->outer_border_top + shadow->outer_border_bottom; } static void meta_shadow_class_info_free (MetaShadowClassInfo *class_info) { g_free ((char *)class_info->name); g_slice_free (MetaShadowClassInfo, class_info); } static void meta_shadow_factory_init (MetaShadowFactory *factory) { guint i; factory->shadows = g_hash_table_new (meta_shadow_cache_key_hash, meta_shadow_cache_key_equal); factory->shadow_classes = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, (GDestroyNotify)meta_shadow_class_info_free); for (i = 0; i < G_N_ELEMENTS (default_shadow_classes); i++) { MetaShadowClassInfo *class_info = g_slice_new (MetaShadowClassInfo); *class_info = default_shadow_classes[i]; class_info->name = g_strdup (class_info->name); g_hash_table_insert (factory->shadow_classes, (char *)class_info->name, class_info); } } static void meta_shadow_factory_finalize (GObject *object) { MetaShadowFactory *factory = META_SHADOW_FACTORY (object); GHashTableIter iter; gpointer key, value; /* Detach from the shadows in the table so we won't try to * remove them when they're freed. */ g_hash_table_iter_init (&iter, factory->shadows); while (g_hash_table_iter_next (&iter, &key, &value)) { MetaShadow *shadow = key; shadow->factory = NULL; } g_hash_table_destroy (factory->shadows); g_hash_table_destroy (factory->shadow_classes); G_OBJECT_CLASS (meta_shadow_factory_parent_class)->finalize (object); } static void meta_shadow_factory_class_init (MetaShadowFactoryClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->finalize = meta_shadow_factory_finalize; signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } MetaShadowFactory * meta_shadow_factory_new (void) { return g_object_new (META_TYPE_SHADOW_FACTORY, NULL); } /** * meta_shadow_factory_get_default: * * Return value: (transfer none): the global singleton shadow factory */ MetaShadowFactory * meta_shadow_factory_get_default (void) { static MetaShadowFactory *factory; if (factory == NULL) factory = meta_shadow_factory_new (); return factory; } /* We emulate a 1D Gaussian blur by using 3 consecutive box blurs; * this produces a result that's within 3% of the original and can be * implemented much faster for large filter sizes because of the * efficiency of implementation of a box blur. Idea and formula * for choosing the box blur size come from: * * http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement * * The 2D blur is then done by blurring the rows, flipping the * image and blurring the columns. (This is possible because the * Gaussian kernel is separable - it's the product of a horizontal * blur and a vertical blur.) */ static int get_box_filter_size (int radius) { return (int)(0.5 + radius * (0.75 * sqrt(2*M_PI))); } /* The "spread" of the filter is the number of pixels from an original * pixel that it's blurred image extends. (A no-op blur that doesn't * blur would have a spread of 0.) See comment in blur_rows() for why the * odd and even cases are different */ static int get_shadow_spread (int radius) { int d; if (radius == 0) return 0; d = get_box_filter_size (radius); if (d % 2 == 1) return 3 * (d / 2); else return 3 * (d / 2) - 1; } /* This applies a single box blur pass to a horizontal range of pixels; * since the box blur has the same weight for all pixels, we can * implement an efficient sliding window algorithm where we add * in pixels coming into the window from the right and remove * them when they leave the windw to the left. * * d is the filter width; for even d shift indicates how the blurred * result is aligned with the original - does ' x ' go to ' yy' (shift=1) * or 'yy ' (shift=-1) */ static void blur_xspan (guchar *row, guchar *tmp_buffer, int row_width, int x0, int x1, int d, int shift) { int offset; int sum = 0; int i; if (d % 2 == 1) offset = d / 2; else offset = (d - shift) / 2; /* All the conditionals in here look slow, but the branches will * be well predicted and there are enough different possibilities * that trying to write this as a series of unconditional loops * is hard and not an obvious win. The main slow down here seems * to be the integer division per pixel; one possible optimization * would be to accumulate into two 16-bit integer buffers and * only divide down after all three passes. (SSE parallel implementation * of the divide step is possible.) */ for (i = x0 - d + offset; i < x1 + offset; i++) { if (i >= 0 && i < row_width) sum += row[i]; if (i >= x0 + offset) { if (i >= d) sum -= row[i - d]; tmp_buffer[i - offset] = (sum + d / 2) / d; } } memcpy (row + x0, tmp_buffer + x0, x1 - x0); } static void blur_rows (cairo_region_t *convolve_region, int x_offset, int y_offset, guchar *buffer, int buffer_width, int buffer_height, int d) { int i, j; int n_rectangles; guchar *tmp_buffer; tmp_buffer = g_malloc (buffer_width); n_rectangles = cairo_region_num_rectangles (convolve_region); for (i = 0; i < n_rectangles; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (convolve_region, i, &rect); for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++) { guchar *row = buffer + j * buffer_width; int x0 = x_offset + rect.x; int x1 = x0 + rect.width; /* We want to produce a symmetric blur that spreads a pixel * equally far to the left and right. If d is odd that happens * naturally, but for d even, we approximate by using a blur * on either side and then a centered blur of size d + 1. * (technique also from the SVG specification) */ if (d % 2 == 1) { blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0); blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0); blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 0); } else { blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, 1); blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d, -1); blur_xspan (row, tmp_buffer, buffer_width, x0, x1, d + 1, 0); } } } g_free (tmp_buffer); } static void fade_bytes (guchar *bytes, int width, int distance, int total) { guint32 multiplier = (distance * 0x10000 + 0x8000) / total; int i; for (i = 0; i < width; i++) bytes[i] = (bytes[i] * multiplier) >> 16; } /* Swaps width and height. Either swaps in-place and returns the original * buffer or allocates a new buffer, frees the original buffer and returns * the new buffer. */ static guchar * flip_buffer (guchar *buffer, int width, int height) { /* Working in blocks increases cache efficiency, compared to reading * or writing an entire column at once */ #define BLOCK_SIZE 16 if (width == height) { int i0, j0; for (j0 = 0; j0 < height; j0 += BLOCK_SIZE) for (i0 = 0; i0 <= j0; i0 += BLOCK_SIZE) { int max_j = MIN(j0 + BLOCK_SIZE, height); int max_i = MIN(i0 + BLOCK_SIZE, width); int i, j; if (i0 == j0) { for (j = j0; j < max_j; j++) for (i = i0; i < j; i++) { guchar tmp = buffer[j * width + i]; buffer[j * width + i] = buffer[i * width + j]; buffer[i * width + j] = tmp; } } else { for (j = j0; j < max_j; j++) for (i = i0; i < max_i; i++) { guchar tmp = buffer[j * width + i]; buffer[j * width + i] = buffer[i * width + j]; buffer[i * width + j] = tmp; } } } return buffer; } else { guchar *new_buffer = g_malloc (height * width); int i0, j0; for (i0 = 0; i0 < width; i0 += BLOCK_SIZE) for (j0 = 0; j0 < height; j0 += BLOCK_SIZE) { int max_j = MIN(j0 + BLOCK_SIZE, height); int max_i = MIN(i0 + BLOCK_SIZE, width); int i, j; for (i = i0; i < max_i; i++) for (j = j0; j < max_j; j++) new_buffer[i * height + j] = buffer[j * width + i]; } g_free (buffer); return new_buffer; } #undef BLOCK_SIZE } static void make_shadow (MetaShadow *shadow, cairo_region_t *region) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); GError *error = NULL; int d = get_box_filter_size (shadow->key.radius); int spread = get_shadow_spread (shadow->key.radius); cairo_rectangle_int_t extents; cairo_region_t *row_convolve_region; cairo_region_t *column_convolve_region; guchar *buffer; int buffer_width; int buffer_height; int x_offset; int y_offset; int n_rectangles, j, k; cairo_region_get_extents (region, &extents); /* In the case where top_fade >= 0 and the portion above the top * edge of the shape will be cropped, it seems like we could create * a smaller buffer and omit the top portion, but actually, in our * multi-pass blur algorithm, the blur into the area above the window * in the first pass will contribute back to the final pixel values * for the top pixels, so we create a buffer as if we weren't cropping * and only crop when creating the CoglTexture. */ buffer_width = extents.width + 2 * spread; buffer_height = extents.height + 2 * spread; /* Round up so we have aligned rows/columns */ buffer_width = (buffer_width + 3) & ~3; buffer_height = (buffer_height + 3) & ~3; /* Square buffer allows in-place swaps, which are roughly 70% faster, but we * don't want to over-allocate too much memory. */ if (buffer_height < buffer_width && buffer_height > (3 * buffer_width) / 4) buffer_height = buffer_width; if (buffer_width < buffer_height && buffer_width > (3 * buffer_height) / 4) buffer_width = buffer_height; buffer = g_malloc0 (buffer_width * buffer_height); /* Blurring with multiple box-blur passes is fast, but (especially for * large shadow sizes) we can improve efficiency by restricting the blur * to the region that actually needs to be blurred. */ row_convolve_region = meta_make_border_region (region, spread, spread, FALSE); column_convolve_region = meta_make_border_region (region, 0, spread, TRUE); /* Offsets between coordinates of the regions and coordinates in the buffer */ x_offset = spread; y_offset = spread; /* Step 1: unblurred image */ n_rectangles = cairo_region_num_rectangles (region); for (k = 0; k < n_rectangles; k++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, k, &rect); for (j = y_offset + rect.y; j < y_offset + rect.y + rect.height; j++) memset (buffer + buffer_width * j + x_offset + rect.x, 255, rect.width); } /* Step 2: swap rows and columns */ buffer = flip_buffer (buffer, buffer_width, buffer_height); /* Step 3: blur rows (really columns) */ blur_rows (column_convolve_region, y_offset, x_offset, buffer, buffer_height, buffer_width, d); /* Step 4: swap rows and columns */ buffer = flip_buffer (buffer, buffer_height, buffer_width); /* Step 5: blur rows */ blur_rows (row_convolve_region, x_offset, y_offset, buffer, buffer_width, buffer_height, d); /* Step 6: fade out the top, if applicable */ if (shadow->key.top_fade >= 0) { for (j = y_offset; j < y_offset + MIN (shadow->key.top_fade, extents.height + shadow->outer_border_bottom); j++) fade_bytes(buffer + j * buffer_width, buffer_width, j - y_offset, shadow->key.top_fade); } /* We offset the passed in pixels to crop off the extra area we allocated at the top * in the case of top_fade >= 0. We also account for padding at the left for symmetry * though that doesn't currently occur. */ shadow->texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, shadow->outer_border_left + extents.width + shadow->outer_border_right, shadow->outer_border_top + extents.height + shadow->outer_border_bottom, COGL_PIXEL_FORMAT_A_8, buffer_width, (buffer + (y_offset - shadow->outer_border_top) * buffer_width + (x_offset - shadow->outer_border_left)), &error)); if (error) { meta_warning ("Failed to allocate shadow texture: %s\n", error->message); g_error_free (error); } cairo_region_destroy (row_convolve_region); cairo_region_destroy (column_convolve_region); g_free (buffer); shadow->pipeline = meta_create_texture_pipeline (shadow->texture); } static MetaShadowParams * get_shadow_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, gboolean create) { MetaShadowClassInfo *class_info = g_hash_table_lookup (factory->shadow_classes, class_name); if (class_info == NULL) { if (create) { class_info = g_slice_new0 (MetaShadowClassInfo); *class_info = default_shadow_classes[0]; class_info->name = g_strdup (class_info->name); g_hash_table_insert (factory->shadow_classes, (char *)class_info->name, class_info); } else { class_info = &default_shadow_classes[0]; } } if (focused) return &class_info->focused; else return &class_info->unfocused; } /** * meta_shadow_factory_get_shadow: * @factory: a #MetaShadowFactory * @shape: the size-invariant shape of the window's region * @width: the actual width of the window's region * @height: the actual height of the window's region * @class_name: name of the class of window shadows * @focused: whether the shadow is for a focused window * * Gets the appropriate shadow object for drawing shadows for the * specified window shape. The region that we are shadowing is specified * as a combination of a size-invariant extracted shape and the size. * In some cases, the same shadow object can be shared between sizes; * in other cases a different shadow object is used for each size. * * Return value: (transfer full): a newly referenced #MetaShadow; unref with * meta_shadow_unref() */ MetaShadow * meta_shadow_factory_get_shadow (MetaShadowFactory *factory, MetaWindowShape *shape, int width, int height, const char *class_name, gboolean focused) { MetaShadowParams *params; MetaShadowCacheKey key; MetaShadow *shadow; cairo_region_t *region; int spread; int shape_border_top, shape_border_right, shape_border_bottom, shape_border_left; int inner_border_top, inner_border_right, inner_border_bottom, inner_border_left; int outer_border_top, outer_border_right, outer_border_bottom, outer_border_left; gboolean scale_width, scale_height; gboolean cacheable; int center_width, center_height; g_return_val_if_fail (META_IS_SHADOW_FACTORY (factory), NULL); g_return_val_if_fail (shape != NULL, NULL); /* Using a single shadow texture for different window sizes only works * when there is a central scaled area that is greater than twice * the spread of the gaussian blur we are applying to get to the * shadow image. * ********* *********** * /----------\ *###########* *#############* * | | => **#*********#** => **#***********#** * | | **#** **#** **#** **#** * | | **#*********#** **#***********#** * \----------/ *###########* *#############* * ********** ************ * Original Blur Stretched Blur * * For smaller sizes, we create a separate shadow image for each size; * since we assume that there will be little reuse, we don't try to * cache such images but just recreate them. (Since the current cache * policy is to only keep around referenced shadows, there wouldn't * be any harm in caching them, it would just make the book-keeping * a bit tricker.) * * In the case where we are fading a the top, that also has to fit * within the top unscaled border. */ params = get_shadow_params (factory, class_name, focused, FALSE); spread = get_shadow_spread (params->radius); meta_window_shape_get_borders (shape, &shape_border_top, &shape_border_right, &shape_border_bottom, &shape_border_left); inner_border_top = MAX (shape_border_top + spread, params->top_fade); outer_border_top = params->top_fade >= 0 ? 0 : spread; inner_border_right = shape_border_right + spread; outer_border_right = spread; inner_border_bottom = shape_border_bottom + spread; outer_border_bottom = spread; inner_border_left = shape_border_left + spread; outer_border_left = spread; scale_width = inner_border_left + inner_border_right <= width; scale_height = inner_border_top + inner_border_bottom <= height; cacheable = scale_width && scale_height; if (cacheable) { key.shape = shape; key.radius = params->radius; key.top_fade = params->top_fade; shadow = g_hash_table_lookup (factory->shadows, &key); if (shadow) return meta_shadow_ref (shadow); } shadow = g_slice_new0 (MetaShadow); shadow->ref_count = 1; shadow->factory = factory; shadow->key.shape = meta_window_shape_ref (shape); shadow->key.radius = params->radius; shadow->key.top_fade = params->top_fade; shadow->outer_border_top = outer_border_top; shadow->inner_border_top = inner_border_top; shadow->outer_border_right = outer_border_right; shadow->inner_border_right = inner_border_right; shadow->outer_border_bottom = outer_border_bottom; shadow->inner_border_bottom = inner_border_bottom; shadow->outer_border_left = outer_border_left; shadow->inner_border_left = inner_border_left; shadow->scale_width = scale_width; if (scale_width) center_width = inner_border_left + inner_border_right - (shape_border_left + shape_border_right); else center_width = width - (shape_border_left + shape_border_right); shadow->scale_height = scale_height; if (scale_height) center_height = inner_border_top + inner_border_bottom - (shape_border_top + shape_border_bottom); else center_height = height - (shape_border_top + shape_border_bottom); g_assert (center_width >= 0 && center_height >= 0); region = meta_window_shape_to_region (shape, center_width, center_height); make_shadow (shadow, region); cairo_region_destroy (region); if (cacheable) g_hash_table_insert (factory->shadows, &shadow->key, shadow); return shadow; } /** * meta_shadow_factory_set_params: * @factory: a #MetaShadowFactory * @class_name: name of the class of shadow to set the params for. * the default shadow classes are the names of the different * theme frame types (normal, dialog, modal_dialog, utility, * border, menu, attached) and in addition, popup-menu * and dropdown-menu. * @focused: whether the shadow is for a focused window * @params: new parameter values * * Updates the shadow parameters for a particular class of shadows * for either the focused or unfocused state. If the class name * does not name an existing class, a new class will be created * (the other focus state for that class will have default values * assigned to it.) */ void meta_shadow_factory_set_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, MetaShadowParams *params) { MetaShadowParams *stored_params; g_return_if_fail (META_IS_SHADOW_FACTORY (factory)); g_return_if_fail (class_name != NULL); g_return_if_fail (params != NULL); g_return_if_fail (params->radius >= 0); stored_params = get_shadow_params (factory, class_name, focused, TRUE); *stored_params = *params; g_signal_emit (factory, signals[CHANGED], 0); } /** * meta_shadow_factory_get_params: * @factory: a #MetaShadowFactory * @class_name: name of the class of shadow to get the params for * @focused: whether the shadow is for a focused window * @params: (out caller-allocates): location to store the current parameter values * * Gets the shadow parameters for a particular class of shadows * for either the focused or unfocused state. If the class name * does not name an existing class, default values will be returned * without printing an error. */ void meta_shadow_factory_get_params (MetaShadowFactory *factory, const char *class_name, gboolean focused, MetaShadowParams *params) { MetaShadowParams *stored_params; g_return_if_fail (META_IS_SHADOW_FACTORY (factory)); g_return_if_fail (class_name != NULL); stored_params = get_shadow_params (factory, class_name, focused, FALSE); if (params) *params = *stored_params; } G_DEFINE_BOXED_TYPE (MetaShadow, meta_shadow, meta_shadow_ref, meta_shadow_unref) muffin-6.4.1/src/compositor/meta-surface-actor-x11.c0000664000175000017500000003047614723361714021176 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Owen Taylor * Jasper St. Pierre */ #include "config.h" #include "compositor/meta-surface-actor-x11.h" #include #include "cogl/winsys/cogl-texture-pixmap-x11.h" #include "compositor/meta-cullable.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-window-actor-private.h" #include "core/window-private.h" #include "meta/meta-x11-errors.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11.h" struct _MetaSurfaceActorX11 { MetaSurfaceActor parent; MetaWindow *window; MetaDisplay *display; CoglTexture *texture; Pixmap pixmap; Damage damage; int last_width; int last_height; /* This is used to detect fullscreen windows that need to be unredirected */ guint full_damage_frames_count; guint does_full_damage : 1; /* Other state... */ guint received_damage : 1; guint size_changed : 1; guint unredirected : 1; }; G_DEFINE_TYPE (MetaSurfaceActorX11, meta_surface_actor_x11, META_TYPE_SURFACE_ACTOR) static void free_damage (MetaSurfaceActorX11 *self) { MetaDisplay *display = self->display; Display *xdisplay; if (self->damage == None) return; xdisplay = meta_x11_display_get_xdisplay (display->x11_display); meta_x11_error_trap_push (display->x11_display); XDamageDestroy (xdisplay, self->damage); self->damage = None; meta_x11_error_trap_pop (display->x11_display); } static void detach_pixmap (MetaSurfaceActorX11 *self) { MetaDisplay *display = self->display; MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); Display *xdisplay; if (self->pixmap == None) return; xdisplay = meta_x11_display_get_xdisplay (display->x11_display); /* Get rid of all references to the pixmap before freeing it; it's unclear whether * you are supposed to be able to free a GLXPixmap after freeing the underlying * pixmap, but it certainly doesn't work with current DRI/Mesa */ meta_shaped_texture_set_texture (stex, NULL); cogl_flush (); meta_x11_error_trap_push (display->x11_display); XFreePixmap (xdisplay, self->pixmap); self->pixmap = None; meta_x11_error_trap_pop (display->x11_display); g_clear_pointer (&self->texture, cogl_object_unref); } static void set_pixmap (MetaSurfaceActorX11 *self, Pixmap pixmap) { CoglContext *ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); GError *error = NULL; CoglTexture *texture; g_assert (self->pixmap == None); self->pixmap = pixmap; texture = COGL_TEXTURE (cogl_texture_pixmap_x11_new (ctx, self->pixmap, FALSE, &error)); if (error != NULL) { g_warning ("Failed to allocate stex texture: %s", error->message); g_error_free (error); } else if (G_UNLIKELY (!cogl_texture_pixmap_x11_is_using_tfp_extension (COGL_TEXTURE_PIXMAP_X11 (texture)))) g_warning ("NOTE: Not using GLX TFP!\n"); self->texture = texture; meta_shaped_texture_set_texture (stex, texture); } static void update_pixmap (MetaSurfaceActorX11 *self) { MetaDisplay *display = self->display; Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); if (self->size_changed) { detach_pixmap (self); self->size_changed = FALSE; } if (self->pixmap == None) { Pixmap new_pixmap; Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window); meta_x11_error_trap_push (display->x11_display); new_pixmap = XCompositeNameWindowPixmap (xdisplay, xwindow); if (meta_x11_error_trap_pop_with_return (display->x11_display) != Success) { /* Probably a BadMatch if the window isn't viewable; we could * GrabServer/GetWindowAttributes/NameWindowPixmap/UngrabServer/Sync * to avoid this, but there's no reason to take two round trips * when one will do. (We need that Sync if we want to handle failures * for any reason other than !viewable. That's unlikely, but maybe * we'll BadAlloc or something.) */ new_pixmap = None; } if (new_pixmap == None) { meta_verbose ("Unable to get named pixmap for %s\n", meta_window_get_description (self->window)); return; } set_pixmap (self, new_pixmap); } } gboolean meta_surface_actor_x11_is_visible (MetaSurfaceActorX11 *self) { return (self->pixmap != None) && !self->unredirected; } static void meta_surface_actor_x11_process_damage (MetaSurfaceActor *actor, int x, int y, int width, int height) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); self->received_damage = TRUE; if (meta_window_is_fullscreen (self->window) && !self->unredirected && !self->does_full_damage) { MetaRectangle window_rect; meta_window_get_frame_rect (self->window, &window_rect); if (x == 0 && y == 0 && window_rect.width == width && window_rect.height == height) self->full_damage_frames_count++; else self->full_damage_frames_count = 0; if (self->full_damage_frames_count >= 100) self->does_full_damage = TRUE; } if (!meta_surface_actor_x11_is_visible (self)) return; cogl_texture_pixmap_x11_update_area (COGL_TEXTURE_PIXMAP_X11 (self->texture), x, y, width, height); meta_surface_actor_update_area (actor, x, y, width, height); } static void meta_surface_actor_x11_pre_paint (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaDisplay *display = self->display; Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); if (self->received_damage) { meta_x11_error_trap_push (display->x11_display); XDamageSubtract (xdisplay, self->damage, None, None); meta_x11_error_trap_pop (display->x11_display); self->received_damage = FALSE; } update_pixmap (self); } static gboolean meta_surface_actor_x11_is_opaque (MetaSurfaceActor *actor) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (actor); MetaShapedTexture *stex = meta_surface_actor_get_texture (actor); if (meta_surface_actor_x11_is_unredirected (self)) return TRUE; return meta_shaped_texture_is_opaque (stex); } gboolean meta_surface_actor_x11_should_unredirect (MetaSurfaceActorX11 *self) { MetaWindow *window = self->window; if (meta_window_requested_dont_bypass_compositor (window)) return FALSE; if (window->opacity != 0xFF) return FALSE; if (window->shape_region != NULL) return FALSE; if (!meta_window_is_monitor_sized (window)) return FALSE; if (meta_window_requested_bypass_compositor (window)) return TRUE; if (!meta_surface_actor_x11_is_opaque (META_SURFACE_ACTOR (self))) return FALSE; if (meta_window_is_override_redirect (window)) return TRUE; if (self->does_full_damage && meta_prefs_get_unredirect_fullscreen_windows ()) return TRUE; return FALSE; } static void sync_unredirected (MetaSurfaceActorX11 *self) { MetaDisplay *display = self->display; Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window); meta_x11_error_trap_push (display->x11_display); if (self->unredirected) { XCompositeUnredirectWindow (xdisplay, xwindow, CompositeRedirectManual); XSync (xdisplay, False); detach_pixmap (self); } else { XCompositeRedirectWindow (xdisplay, xwindow, CompositeRedirectManual); XSync (xdisplay, False); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } meta_x11_error_trap_pop (display->x11_display); } void meta_surface_actor_x11_set_unredirected (MetaSurfaceActorX11 *self, gboolean unredirected) { if (self->unredirected == unredirected) return; self->unredirected = unredirected; sync_unredirected (self); } gboolean meta_surface_actor_x11_is_unredirected (MetaSurfaceActorX11 *self) { return self->unredirected; } static void release_x11_resources (MetaSurfaceActorX11 *self) { detach_pixmap (self); free_damage (self); } static void meta_surface_actor_x11_dispose (GObject *object) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (object); release_x11_resources (self); G_OBJECT_CLASS (meta_surface_actor_x11_parent_class)->dispose (object); } static void meta_surface_actor_x11_class_init (MetaSurfaceActorX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaSurfaceActorClass *surface_actor_class = META_SURFACE_ACTOR_CLASS (klass); object_class->dispose = meta_surface_actor_x11_dispose; surface_actor_class->process_damage = meta_surface_actor_x11_process_damage; surface_actor_class->pre_paint = meta_surface_actor_x11_pre_paint; surface_actor_class->is_opaque = meta_surface_actor_x11_is_opaque; } static void meta_surface_actor_x11_init (MetaSurfaceActorX11 *self) { self->last_width = -1; self->last_height = -1; } static void create_damage (MetaSurfaceActorX11 *self) { Display *xdisplay = meta_x11_display_get_xdisplay (self->display->x11_display); Window xwindow = meta_window_x11_get_toplevel_xwindow (self->window); self->damage = XDamageCreate (xdisplay, xwindow, XDamageReportBoundingBox); } static void window_decorated_notify (MetaWindow *window, GParamSpec *pspec, gpointer user_data) { MetaSurfaceActorX11 *self = META_SURFACE_ACTOR_X11 (user_data); release_x11_resources (self); create_damage (self); } static void reset_texture (MetaSurfaceActorX11 *self) { MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (!self->texture) return; /* Setting the texture to NULL will cause all the FBO's cached by the * shaped texture's MetaTextureTower to be discarded and recreated. */ meta_shaped_texture_set_texture (stex, NULL); meta_shaped_texture_set_texture (stex, self->texture); } MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window) { MetaSurfaceActorX11 *self = g_object_new (META_TYPE_SURFACE_ACTOR_X11, NULL); MetaDisplay *display = meta_window_get_display (window); g_assert (!meta_is_wayland_compositor ()); self->window = window; self->display = display; g_signal_connect_object (self->display, "gl-video-memory-purged", G_CALLBACK (reset_texture), self, G_CONNECT_SWAPPED); create_damage (self); g_signal_connect_object (self->window, "notify::decorated", G_CALLBACK (window_decorated_notify), self, 0); g_signal_connect_object (meta_window_actor_from_window (window), "destroy", G_CALLBACK (release_x11_resources), self, G_CONNECT_SWAPPED); self->unredirected = FALSE; sync_unredirected (self); clutter_actor_set_reactive (CLUTTER_ACTOR (self), TRUE); return META_SURFACE_ACTOR (self); } void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self, int width, int height) { MetaShapedTexture *stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (self->last_width == width && self->last_height == height) return; self->size_changed = TRUE; self->last_width = width; self->last_height = height; meta_shaped_texture_set_fallback_size (stex, width, height); } muffin-6.4.1/src/compositor/meta-compositor-server.c0000664000175000017500000000406014723361714021521 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "compositor/meta-compositor-server.h" struct _MetaCompositorServer { MetaCompositor parent; }; G_DEFINE_TYPE (MetaCompositorServer, meta_compositor_server, META_TYPE_COMPOSITOR) static void meta_compositor_server_manage (MetaCompositor *compositor) { } static void meta_compositor_server_unmanage (MetaCompositor *compositor) { } static int64_t meta_compositor_server_monotonic_to_high_res_xserver_time (MetaCompositor *compositor, int64_t monotonic_time_us) { return meta_translate_to_high_res_xserver_time (monotonic_time_us); } MetaCompositorServer * meta_compositor_server_new (MetaDisplay *display) { return g_object_new (META_TYPE_COMPOSITOR_SERVER, "display", display, NULL); } static void meta_compositor_server_init (MetaCompositorServer *compositor_server) { } static void meta_compositor_server_class_init (MetaCompositorServerClass *klass) { MetaCompositorClass *compositor_class = META_COMPOSITOR_CLASS (klass); compositor_class->manage = meta_compositor_server_manage; compositor_class->unmanage = meta_compositor_server_unmanage; compositor_class->monotonic_to_high_res_xserver_time = meta_compositor_server_monotonic_to_high_res_xserver_time; } muffin-6.4.1/src/compositor/meta-module.c0000664000175000017500000001160314723361714017305 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "compositor/meta-module.h" #include #include "meta/meta-plugin.h" #include "meta/meta-version.h" enum { PROP_0, PROP_PATH, }; struct _MetaModulePrivate { GModule *lib; gchar *path; GType plugin_type; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaModule, meta_module, G_TYPE_TYPE_MODULE); static gboolean meta_module_load (GTypeModule *gmodule) { MetaModulePrivate *priv = META_MODULE (gmodule)->priv; MetaPluginVersion *info = NULL; GType (*register_type) (GTypeModule *) = NULL; if (priv->lib && priv->plugin_type) return TRUE; g_assert (priv->path); if (!priv->lib && !(priv->lib = g_module_open (priv->path, 0))) { g_warning ("Could not load library [%s (%s)]", priv->path, g_module_error ()); return FALSE; } if (g_module_symbol (priv->lib, "meta_plugin_version", (gpointer *)(void *)&info) && g_module_symbol (priv->lib, "meta_plugin_register_type", (gpointer *)(void *)®ister_type) && info && register_type) { if (info->version_api != META_PLUGIN_API_VERSION) g_warning ("Plugin API mismatch for [%s]", priv->path); else { GType plugin_type; if (!(plugin_type = register_type (gmodule))) { g_warning ("Could not register type for plugin %s", priv->path); return FALSE; } else { priv->plugin_type = plugin_type; } return TRUE; } } else g_warning ("Broken plugin module [%s]", priv->path); return FALSE; } static void meta_module_unload (GTypeModule *gmodule) { MetaModulePrivate *priv = META_MODULE (gmodule)->priv; g_module_close (priv->lib); priv->lib = NULL; priv->plugin_type = 0; } static void meta_module_dispose (GObject *object) { G_OBJECT_CLASS (meta_module_parent_class)->dispose (object); } static void meta_module_finalize (GObject *object) { MetaModulePrivate *priv = META_MODULE (object)->priv; g_free (priv->path); priv->path = NULL; G_OBJECT_CLASS (meta_module_parent_class)->finalize (object); } static void meta_module_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaModulePrivate *priv = META_MODULE (object)->priv; switch (prop_id) { case PROP_PATH: g_free (priv->path); priv->path = g_value_dup_string (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_module_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaModulePrivate *priv = META_MODULE (object)->priv; switch (prop_id) { case PROP_PATH: g_value_set_string (value, priv->path); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_module_class_init (MetaModuleClass *klass) { GObjectClass *gobject_class = G_OBJECT_CLASS (klass); GTypeModuleClass *gmodule_class = G_TYPE_MODULE_CLASS (klass); gobject_class->finalize = meta_module_finalize; gobject_class->dispose = meta_module_dispose; gobject_class->set_property = meta_module_set_property; gobject_class->get_property = meta_module_get_property; gmodule_class->load = meta_module_load; gmodule_class->unload = meta_module_unload; g_object_class_install_property (gobject_class, PROP_PATH, g_param_spec_string ("path", "Path", "Load path", NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } static void meta_module_init (MetaModule *self) { self->priv = meta_module_get_instance_private (self); } GType meta_module_get_plugin_type (MetaModule *module) { MetaModulePrivate *priv = META_MODULE (module)->priv; return priv->plugin_type; } muffin-6.4.1/src/compositor/meta-sync-ring.c0000664000175000017500000003730414723361714017737 0ustar fabiofabio/* * This is based on an original C++ implementation for compiz that * carries the following copyright notice: * * * Copyright © 2011 NVIDIA Corporation * * Permission to use, copy, modify, distribute, and sell this software * and its documentation for any purpose is hereby granted without * fee, provided that the above copyright notice appear in all copies * and that both that copyright notice and this permission notice * appear in supporting documentation, and that the name of NVIDIA * Corporation not be used in advertising or publicity pertaining to * distribution of the software without specific, written prior * permission. NVIDIA Corporation makes no representations about the * suitability of this software for any purpose. It is provided "as * is" without express or implied warranty. * * NVIDIA CORPORATION DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS, IN NO EVENT SHALL NVIDIA CORPORATION BE LIABLE FOR ANY * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS * SOFTWARE. * * Authors: James Jones */ #include "config.h" #include "compositor/meta-sync-ring.h" #include #include #include #include #include "clutter/clutter.h" #include "cogl/cogl.h" #include "meta/util.h" /* Theory of operation: * * We use a ring of NUM_SYNCS fence objects. On each frame we advance * to the next fence in the ring. For each fence we do: * * 1. fence is XSyncTriggerFence()'d and glWaitSync()'d * 2. NUM_SYNCS / 2 frames later, fence should be triggered * 3. fence is XSyncResetFence()'d * 4. NUM_SYNCS / 2 frames later, fence should be reset * 5. go back to 1 and re-use fence * * glClientWaitSync() and XAlarms are used in steps 2 and 4, * respectively, to double-check the expectections. */ #define NUM_SYNCS 10 #define MAX_SYNC_WAIT_TIME (1 * 1000 * 1000 * 1000) /* one sec */ #define MAX_REBOOT_ATTEMPTS 2 typedef enum { META_SYNC_STATE_READY, META_SYNC_STATE_WAITING, META_SYNC_STATE_DONE, META_SYNC_STATE_RESET_PENDING, } MetaSyncState; typedef struct { Display *xdisplay; XSyncFence xfence; GLsync gl_x11_sync; GLsync gpu_fence; XSyncCounter xcounter; XSyncAlarm xalarm; XSyncValue next_counter_value; MetaSyncState state; } MetaSync; typedef struct { Display *xdisplay; int xsync_event_base; int xsync_error_base; GHashTable *alarm_to_sync; MetaSync *syncs_array[NUM_SYNCS]; guint current_sync_idx; MetaSync *current_sync; guint warmup_syncs; guint reboots; } MetaSyncRing; static MetaSyncRing meta_sync_ring = { 0 }; static XSyncValue SYNC_VALUE_ZERO; static XSyncValue SYNC_VALUE_ONE; static const char* (*meta_gl_get_string) (GLenum name); static void (*meta_gl_get_integerv) (GLenum pname, GLint *params); static const char* (*meta_gl_get_stringi) (GLenum name, GLuint index); static void (*meta_gl_delete_sync) (GLsync sync); static GLenum (*meta_gl_client_wait_sync) (GLsync sync, GLbitfield flags, GLuint64 timeout); static void (*meta_gl_wait_sync) (GLsync sync, GLbitfield flags, GLuint64 timeout); static GLsync (*meta_gl_import_sync) (GLenum external_sync_type, GLintptr external_sync, GLbitfield flags); static GLsync (*meta_gl_fence_sync) (GLenum condition, GLbitfield flags); static MetaSyncRing * meta_sync_ring_get (void) { if (meta_sync_ring.reboots > MAX_REBOOT_ATTEMPTS) return NULL; return &meta_sync_ring; } static gboolean load_gl_symbol (const char *name, void **func) { *func = cogl_get_proc_address (name); if (!*func) { meta_verbose ("MetaSyncRing: failed to resolve required GL symbol \"%s\"\n", name); return FALSE; } return TRUE; } static gboolean check_gl_extensions (void) { ClutterBackend *backend; CoglContext *cogl_context; CoglDisplay *cogl_display; CoglRenderer *cogl_renderer; backend = clutter_get_default_backend (); cogl_context = clutter_backend_get_cogl_context (backend); cogl_display = cogl_context_get_display (cogl_context); cogl_renderer = cogl_display_get_renderer (cogl_display); switch (cogl_renderer_get_driver (cogl_renderer)) { case COGL_DRIVER_GL3: { int num_extensions, i; gboolean arb_sync = FALSE; gboolean x11_sync_object = FALSE; meta_gl_get_integerv (GL_NUM_EXTENSIONS, &num_extensions); for (i = 0; i < num_extensions; ++i) { const char *ext = meta_gl_get_stringi (GL_EXTENSIONS, i); if (g_strcmp0 ("GL_ARB_sync", ext) == 0) arb_sync = TRUE; else if (g_strcmp0 ("GL_EXT_x11_sync_object", ext) == 0) x11_sync_object = TRUE; } return arb_sync && x11_sync_object; } case COGL_DRIVER_GL: { const char *extensions = meta_gl_get_string (GL_EXTENSIONS); return (extensions != NULL && strstr (extensions, "GL_ARB_sync") != NULL && strstr (extensions, "GL_EXT_x11_sync_object") != NULL); } default: break; } return FALSE; } static gboolean load_required_symbols (void) { static gboolean success = FALSE; if (success) return TRUE; /* We don't link against libGL directly because cogl may want to * use something else. This assumes that cogl has been initialized * and dynamically loaded libGL at this point. */ if (!load_gl_symbol ("glGetString", (void **) &meta_gl_get_string)) goto out; if (!load_gl_symbol ("glGetIntegerv", (void **) &meta_gl_get_integerv)) goto out; if (!load_gl_symbol ("glGetStringi", (void **) &meta_gl_get_stringi)) goto out; if (!check_gl_extensions ()) { meta_verbose ("MetaSyncRing: couldn't find required GL extensions\n"); goto out; } if (!load_gl_symbol ("glDeleteSync", (void **) &meta_gl_delete_sync)) goto out; if (!load_gl_symbol ("glClientWaitSync", (void **) &meta_gl_client_wait_sync)) goto out; if (!load_gl_symbol ("glWaitSync", (void **) &meta_gl_wait_sync)) goto out; if (!load_gl_symbol ("glImportSyncEXT", (void **) &meta_gl_import_sync)) goto out; if (!load_gl_symbol ("glFenceSync", (void **) &meta_gl_fence_sync)) goto out; success = TRUE; out: return success; } static void meta_sync_insert (MetaSync *self) { g_return_if_fail (self->state == META_SYNC_STATE_READY); XSyncTriggerFence (self->xdisplay, self->xfence); XFlush (self->xdisplay); meta_gl_wait_sync (self->gl_x11_sync, 0, GL_TIMEOUT_IGNORED); self->gpu_fence = meta_gl_fence_sync (GL_SYNC_GPU_COMMANDS_COMPLETE, 0); self->state = META_SYNC_STATE_WAITING; } static GLenum meta_sync_check_update_finished (MetaSync *self, GLuint64 timeout) { GLenum status = GL_WAIT_FAILED; switch (self->state) { case META_SYNC_STATE_DONE: status = GL_ALREADY_SIGNALED; break; case META_SYNC_STATE_WAITING: status = meta_gl_client_wait_sync (self->gpu_fence, 0, timeout); if (status == GL_ALREADY_SIGNALED || status == GL_CONDITION_SATISFIED) { self->state = META_SYNC_STATE_DONE; meta_gl_delete_sync (self->gpu_fence); self->gpu_fence = 0; } break; default: break; } g_warn_if_fail (status != GL_WAIT_FAILED); return status; } static void meta_sync_reset (MetaSync *self) { XSyncAlarmAttributes attrs; int overflow; g_return_if_fail (self->state == META_SYNC_STATE_DONE); XSyncResetFence (self->xdisplay, self->xfence); attrs.trigger.wait_value = self->next_counter_value; XSyncChangeAlarm (self->xdisplay, self->xalarm, XSyncCAValue, &attrs); XSyncSetCounter (self->xdisplay, self->xcounter, self->next_counter_value); XSyncValueAdd (&self->next_counter_value, self->next_counter_value, SYNC_VALUE_ONE, &overflow); self->state = META_SYNC_STATE_RESET_PENDING; } static void meta_sync_handle_event (MetaSync *self, XSyncAlarmNotifyEvent *event) { g_return_if_fail (event->alarm == self->xalarm); g_return_if_fail (self->state == META_SYNC_STATE_RESET_PENDING); self->state = META_SYNC_STATE_READY; } static MetaSync * meta_sync_new (Display *xdisplay) { MetaSync *self; XSyncAlarmAttributes attrs; self = g_malloc0 (sizeof (MetaSync)); self->xdisplay = xdisplay; self->xfence = XSyncCreateFence (xdisplay, DefaultRootWindow (xdisplay), FALSE); self->gl_x11_sync = 0; self->gpu_fence = 0; self->xcounter = XSyncCreateCounter (xdisplay, SYNC_VALUE_ZERO); attrs.trigger.counter = self->xcounter; attrs.trigger.value_type = XSyncAbsolute; attrs.trigger.wait_value = SYNC_VALUE_ONE; attrs.trigger.test_type = XSyncPositiveTransition; attrs.events = TRUE; self->xalarm = XSyncCreateAlarm (xdisplay, XSyncCACounter | XSyncCAValueType | XSyncCAValue | XSyncCATestType | XSyncCAEvents, &attrs); XSyncIntToValue (&self->next_counter_value, 1); self->state = META_SYNC_STATE_READY; return self; } static void meta_sync_import (MetaSync *self) { g_return_if_fail (self->gl_x11_sync == 0); self->gl_x11_sync = meta_gl_import_sync (GL_SYNC_X11_FENCE_EXT, self->xfence, 0); } static Bool alarm_event_predicate (Display *dpy, XEvent *event, XPointer data) { MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return False; if (event->type == ring->xsync_event_base + XSyncAlarmNotify) { if (((MetaSync *) data)->xalarm == ((XSyncAlarmNotifyEvent *) event)->alarm) return True; } return False; } static void meta_sync_free (MetaSync *self) { /* When our assumptions don't hold, something has gone wrong but we * don't know what, so we reboot the ring. While doing that, we * trigger fences before deleting them to try to get ourselves out * of a potentially stuck GPU state. */ switch (self->state) { case META_SYNC_STATE_WAITING: meta_gl_delete_sync (self->gpu_fence); break; case META_SYNC_STATE_DONE: /* nothing to do */ break; case META_SYNC_STATE_RESET_PENDING: { XEvent event; XIfEvent (self->xdisplay, &event, alarm_event_predicate, (XPointer) self); meta_sync_handle_event (self, (XSyncAlarmNotifyEvent *) &event); } G_GNUC_FALLTHROUGH; case META_SYNC_STATE_READY: XSyncTriggerFence (self->xdisplay, self->xfence); XFlush (self->xdisplay); break; default: break; } meta_gl_delete_sync (self->gl_x11_sync); XSyncDestroyFence (self->xdisplay, self->xfence); XSyncDestroyCounter (self->xdisplay, self->xcounter); XSyncDestroyAlarm (self->xdisplay, self->xalarm); g_free (self); } gboolean meta_sync_ring_init (Display *xdisplay) { gint major, minor; guint i; MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return FALSE; g_return_val_if_fail (xdisplay != NULL, FALSE); g_return_val_if_fail (ring->xdisplay == NULL, FALSE); if (!load_required_symbols ()) return FALSE; if (!XSyncQueryExtension (xdisplay, &ring->xsync_event_base, &ring->xsync_error_base) || !XSyncInitialize (xdisplay, &major, &minor)) return FALSE; XSyncIntToValue (&SYNC_VALUE_ZERO, 0); XSyncIntToValue (&SYNC_VALUE_ONE, 1); ring->xdisplay = xdisplay; ring->alarm_to_sync = g_hash_table_new (NULL, NULL); for (i = 0; i < NUM_SYNCS; ++i) { MetaSync *sync = meta_sync_new (ring->xdisplay); ring->syncs_array[i] = sync; g_hash_table_replace (ring->alarm_to_sync, (gpointer) sync->xalarm, sync); } /* Since the connection we create the X fences on isn't the same as * the one used for the GLX context, we need to XSync() here to * ensure glImportSync() succeeds. */ XSync (xdisplay, False); for (i = 0; i < NUM_SYNCS; ++i) meta_sync_import (ring->syncs_array[i]); ring->current_sync_idx = 0; ring->current_sync = ring->syncs_array[0]; ring->warmup_syncs = 0; return TRUE; } void meta_sync_ring_destroy (void) { guint i; MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return; g_return_if_fail (ring->xdisplay != NULL); ring->current_sync_idx = 0; ring->current_sync = NULL; ring->warmup_syncs = 0; for (i = 0; i < NUM_SYNCS; ++i) meta_sync_free (ring->syncs_array[i]); g_hash_table_destroy (ring->alarm_to_sync); ring->xsync_event_base = 0; ring->xsync_error_base = 0; ring->xdisplay = NULL; } static gboolean meta_sync_ring_reboot (Display *xdisplay) { MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return FALSE; meta_sync_ring_destroy (); ring->reboots += 1; if (!meta_sync_ring_get ()) { meta_warning ("MetaSyncRing: Too many reboots -- disabling\n"); return FALSE; } return meta_sync_ring_init (xdisplay); } gboolean meta_sync_ring_after_frame (void) { MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return FALSE; g_return_val_if_fail (ring->xdisplay != NULL, FALSE); if (ring->warmup_syncs >= NUM_SYNCS / 2) { guint reset_sync_idx = (ring->current_sync_idx + NUM_SYNCS - (NUM_SYNCS / 2)) % NUM_SYNCS; MetaSync *sync_to_reset = ring->syncs_array[reset_sync_idx]; GLenum status = meta_sync_check_update_finished (sync_to_reset, 0); if (status == GL_TIMEOUT_EXPIRED) { meta_warning ("MetaSyncRing: We should never wait for a sync -- add more syncs?\n"); status = meta_sync_check_update_finished (sync_to_reset, MAX_SYNC_WAIT_TIME); } if (status != GL_ALREADY_SIGNALED && status != GL_CONDITION_SATISFIED) { meta_warning ("MetaSyncRing: Timed out waiting for sync object.\n"); return meta_sync_ring_reboot (ring->xdisplay); } meta_sync_reset (sync_to_reset); } else { ring->warmup_syncs += 1; } ring->current_sync_idx += 1; ring->current_sync_idx %= NUM_SYNCS; ring->current_sync = ring->syncs_array[ring->current_sync_idx]; return TRUE; } gboolean meta_sync_ring_insert_wait (void) { MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return FALSE; g_return_val_if_fail (ring->xdisplay != NULL, FALSE); if (ring->current_sync->state != META_SYNC_STATE_READY) { meta_warning ("MetaSyncRing: Sync object is not ready -- were events handled properly?\n"); if (!meta_sync_ring_reboot (ring->xdisplay)) return FALSE; } meta_sync_insert (ring->current_sync); return TRUE; } void meta_sync_ring_handle_event (XEvent *xevent) { XSyncAlarmNotifyEvent *event; MetaSync *sync; MetaSyncRing *ring = meta_sync_ring_get (); if (!ring) return; g_return_if_fail (ring->xdisplay != NULL); if (xevent->type != (ring->xsync_event_base + XSyncAlarmNotify)) return; event = (XSyncAlarmNotifyEvent *) xevent; sync = g_hash_table_lookup (ring->alarm_to_sync, (gpointer) event->alarm); if (sync) meta_sync_handle_event (sync, event); } muffin-6.4.1/src/compositor/meta-window-group-private.h0000664000175000017500000000122114723361714022131 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_WINDOW_GROUP_PRIVATE_H #define META_WINDOW_GROUP_PRIVATE_H #include "meta/display.h" #include "meta/meta-window-group.h" /** * MetaWindowGroup: * * This class is a subclass of ClutterActor with special handling for * #MetaCullable when painting children. It uses code similar to * meta_cullable_cull_out_children(), but also has additional special * cases for the undirected window, and similar. */ typedef struct _MetaWindowGroupPrivate MetaWindowGroupPrivate; ClutterActor *meta_window_group_new (MetaDisplay *display); #endif /* META_WINDOW_GROUP_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-window-actor-x11.c0000664000175000017500000014765014723361714021060 0ustar fabiofabio/* * Copyright (C) 2018 Endless, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Georges Basile Stavracas Neto */ #include "config.h" #include "compositor/meta-window-actor-x11.h" #include "backends/meta-logical-monitor.h" #include "compositor/compositor-private.h" #include "compositor/meta-cullable.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-surface-actor.h" #include "compositor/meta-surface-actor-x11.h" #include "compositor/region-utils.h" #include "core/frame.h" #include "core/window-private.h" #include "meta/compositor.h" #include "meta/meta-enum-types.h" #include "meta/meta-shadow-factory.h" #include "meta/meta-window-actor.h" #include "meta/meta-x11-errors.h" #include "meta/window.h" #include "x11/meta-x11-display-private.h" #include "x11/window-x11.h" enum { PROP_SHADOW_MODE = 1, PROP_SHADOW_CLASS }; struct _MetaWindowActorX11 { MetaWindowActor parent; /* List of FrameData for recent frames */ GList *frames; guint send_frame_messages_timer; int64_t frame_drawn_time; gulong repaint_scheduled_id; gulong size_changed_id; /* If set, the client needs to be sent a _NET_WM_FRAME_DRAWN * client message for one or more messages in ->frames */ gboolean needs_frame_drawn; gboolean repaint_scheduled; /* * MetaShadowFactory only caches shadows that are actually in use; * to avoid unnecessary recomputation we do two things: 1) we store * both a focused and unfocused shadow for the window. If the window * doesn't have different focused and unfocused shadow parameters, * these will be the same. 2) when the shadow potentially changes we * don't immediately unreference the old shadow, we just flag it as * dirty and recompute it when we next need it (recompute_focused_shadow, * recompute_unfocused_shadow.) Because of our extraction of * size-invariant window shape, we'll often find that the new shadow * is the same as the old shadow. */ MetaShadow *focused_shadow; MetaShadow *unfocused_shadow; /* A region that matches the shape of the window, including frame bounds */ cairo_region_t *shape_region; /* The region we should clip to when painting the shadow */ cairo_region_t *shadow_clip; /* The frame region */ cairo_region_t *frame_bounds; /* Extracted size-invariant shape used for shadows */ MetaWindowShape *shadow_shape; char *shadow_class; MetaShadowFactory *shadow_factory; gulong shadow_factory_changed_handler_id; MetaShadowMode shadow_mode; gboolean needs_reshape; gboolean recompute_focused_shadow; gboolean recompute_unfocused_shadow; gboolean is_frozen; }; static MetaShadowMode user_shadow_mode = META_SHADOW_MODE_AUTO; static MetaCullableInterface *cullable_parent_iface; static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWindowActorX11, meta_window_actor_x11, META_TYPE_WINDOW_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)) /* Each time the application updates the sync request counter to a new even value * value, we queue a frame into the windows list of frames. Once we're painting * an update "in response" to the window, we fill in frame_counter with the * Cogl counter for that frame, and send _NET_WM_FRAME_DRAWN at the end of the * frame. _NET_WM_FRAME_TIMINGS is sent when we get a frame_complete callback. * * As an exception, if a window is completely obscured, we try to throttle drawning * to a slower frame rate. In this case, frame_counter stays -1 until * send_frame_message_timeout() runs, at which point we send both the * _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages. */ typedef struct { uint64_t sync_request_serial; int64_t frame_counter; int64_t frame_drawn_time; } FrameData; static void frame_data_free (FrameData *frame) { g_slice_free (FrameData, frame); } static void surface_repaint_scheduled (MetaSurfaceActor *actor, gpointer user_data) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data); actor_x11->repaint_scheduled = TRUE; } static void remove_frame_messages_timer (MetaWindowActorX11 *actor_x11) { g_assert (actor_x11->send_frame_messages_timer != 0); g_clear_handle_id (&actor_x11->send_frame_messages_timer, g_source_remove); } static void do_send_frame_drawn (MetaWindowActorX11 *actor_x11, FrameData *frame) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaDisplay *display = meta_window_get_display (window); Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); int64_t now_us; XClientMessageEvent ev = { 0, }; now_us = g_get_monotonic_time (); frame->frame_drawn_time = meta_compositor_monotonic_to_high_res_xserver_time (display->compositor, now_us); actor_x11->frame_drawn_time = frame->frame_drawn_time; ev.type = ClientMessage; ev.window = meta_window_get_xwindow (window); ev.message_type = display->x11_display->atom__NET_WM_FRAME_DRAWN; ev.format = 32; ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff); ev.data.l[1] = frame->sync_request_serial >> 32; ev.data.l[2] = frame->frame_drawn_time & G_GUINT64_CONSTANT (0xffffffff); ev.data.l[3] = frame->frame_drawn_time >> 32; meta_x11_error_trap_push (display->x11_display); XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev); XFlush (xdisplay); meta_x11_error_trap_pop (display->x11_display); } static void do_send_frame_timings (MetaWindowActorX11 *actor_x11, FrameData *frame, int refresh_interval, int64_t presentation_time) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaDisplay *display = meta_window_get_display (window); Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); XClientMessageEvent ev = { 0, }; ev.type = ClientMessage; ev.window = meta_window_get_xwindow (window); ev.message_type = display->x11_display->atom__NET_WM_FRAME_TIMINGS; ev.format = 32; ev.data.l[0] = frame->sync_request_serial & G_GUINT64_CONSTANT (0xffffffff); ev.data.l[1] = frame->sync_request_serial >> 32; if (presentation_time != 0) { MetaCompositor *compositor = display->compositor; int64_t presentation_time_server; presentation_time_server = meta_compositor_monotonic_to_high_res_xserver_time (compositor, presentation_time); int64_t presentation_time_offset = presentation_time_server - frame->frame_drawn_time; if (presentation_time_offset == 0) presentation_time_offset = 1; if ((int32_t)presentation_time_offset == presentation_time_offset) ev.data.l[2] = presentation_time_offset; } ev.data.l[3] = refresh_interval; ev.data.l[4] = 1000 * META_SYNC_DELAY; meta_x11_error_trap_push (display->x11_display); XSendEvent (xdisplay, ev.window, False, 0, (XEvent *) &ev); XFlush (xdisplay); meta_x11_error_trap_pop (display->x11_display); } static void send_frame_timings (MetaWindowActorX11 *actor_x11, FrameData *frame, ClutterFrameInfo *frame_info, int64_t presentation_time) { float refresh_rate; int refresh_interval; refresh_rate = frame_info->refresh_rate; /* 0.0 is a flag for not known, but sanity-check against other odd numbers */ if (refresh_rate >= 1.0) refresh_interval = (int) (0.5 + 1000000 / refresh_rate); else refresh_interval = 0; do_send_frame_timings (actor_x11, frame, refresh_interval, presentation_time); } static gboolean send_frame_messages_timeout (gpointer data) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (data); GList *l; for (l = actor_x11->frames; l;) { GList *l_next = l->next; FrameData *frame = l->data; if (frame->frame_counter == -1) { do_send_frame_drawn (actor_x11, frame); do_send_frame_timings (actor_x11, frame, 0, 0); actor_x11->frames = g_list_delete_link (actor_x11->frames, l); frame_data_free (frame); } l = l_next; } actor_x11->needs_frame_drawn = FALSE; actor_x11->send_frame_messages_timer = 0; return G_SOURCE_REMOVE; } static void queue_send_frame_messages_timeout (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaDisplay *display = meta_window_get_display (window); MetaLogicalMonitor *logical_monitor; int64_t now_us; int64_t current_time; float refresh_rate; int interval, offset; if (actor_x11->send_frame_messages_timer != 0) return; logical_monitor = meta_window_get_main_logical_monitor (window); if (logical_monitor) { GList *monitors = meta_logical_monitor_get_monitors (logical_monitor); MetaMonitor *monitor; MetaMonitorMode *mode; monitor = g_list_first (monitors)->data; mode = meta_monitor_get_current_mode (monitor); refresh_rate = meta_monitor_mode_get_refresh_rate (mode); } else { refresh_rate = 60.0f; } now_us = g_get_monotonic_time (); current_time = meta_compositor_monotonic_to_high_res_xserver_time (display->compositor, now_us); interval = (int) (1000000 / refresh_rate) * 6; offset = MAX (0, actor_x11->frame_drawn_time + interval - current_time) / 1000; /* The clutter master clock source has already been added with META_PRIORITY_REDRAW, * so the timer will run *after* the clutter frame handling, if a frame is ready * to be drawn when the timer expires. */ actor_x11->send_frame_messages_timer = g_timeout_add_full (META_PRIORITY_REDRAW, offset, send_frame_messages_timeout, actor_x11, NULL); g_source_set_name_by_id (actor_x11->send_frame_messages_timer, "[mutter] send_frame_messages_timeout"); } static void assign_frame_counter_to_frames (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaCompositor *compositor = window->display->compositor; ClutterStage *stage = meta_compositor_get_stage (compositor); GList *l; /* If the window is obscured, then we're expecting to deal with sending * frame messages in a timeout, rather than in this paint cycle. */ if (actor_x11->send_frame_messages_timer != 0) return; for (l = actor_x11->frames; l; l = l->next) { FrameData *frame = l->data; if (frame->frame_counter == -1) frame->frame_counter = clutter_stage_get_frame_counter (stage); } } static void meta_window_actor_x11_frame_complete (MetaWindowActor *actor, ClutterFrameInfo *frame_info, int64_t presentation_time) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); GList *l; if (meta_window_actor_is_destroyed (actor)) return; for (l = actor_x11->frames; l;) { GList *l_next = l->next; FrameData *frame = l->data; int64_t frame_counter = frame_info->frame_counter; if (frame->frame_counter != -1 && frame->frame_counter <= frame_counter) { MetaWindow *window = meta_window_actor_get_meta_window (actor); if (G_UNLIKELY (frame->frame_drawn_time == 0)) g_warning ("%s: Frame has assigned frame counter but no frame drawn time", window->desc); if (G_UNLIKELY (frame->frame_counter < frame_counter)) g_warning ("%s: frame_complete callback never occurred for frame %" G_GINT64_FORMAT, window->desc, frame->frame_counter); actor_x11->frames = g_list_delete_link (actor_x11->frames, l); send_frame_timings (actor_x11, frame, frame_info, presentation_time); frame_data_free (frame); } l = l_next; } } static void surface_size_changed (MetaSurfaceActor *actor, gpointer user_data) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (user_data); meta_window_actor_x11_update_shape (actor_x11); } static void meta_window_actor_x11_assign_surface_actor (MetaWindowActor *actor, MetaSurfaceActor *surface_actor) { MetaWindowActorClass *parent_class = META_WINDOW_ACTOR_CLASS (meta_window_actor_x11_parent_class); MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaSurfaceActor *prev_surface_actor; prev_surface_actor = meta_window_actor_get_surface (actor); if (prev_surface_actor) { g_warn_if_fail (meta_is_wayland_compositor ()); g_clear_signal_handler (&actor_x11->size_changed_id, prev_surface_actor); clutter_actor_remove_child (CLUTTER_ACTOR (actor), CLUTTER_ACTOR (prev_surface_actor)); } parent_class->assign_surface_actor (actor, surface_actor); clutter_actor_add_child (CLUTTER_ACTOR (actor), CLUTTER_ACTOR (surface_actor)); meta_window_actor_x11_update_shape (actor_x11); actor_x11->size_changed_id = g_signal_connect (surface_actor, "size-changed", G_CALLBACK (surface_size_changed), actor_x11); actor_x11->repaint_scheduled_id = g_signal_connect (surface_actor, "repaint-scheduled", G_CALLBACK (surface_repaint_scheduled), actor_x11); } static void meta_window_actor_x11_queue_frame_drawn (MetaWindowActor *actor, gboolean skip_sync_delay) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaWindow *window = meta_window_actor_get_meta_window (actor); FrameData *frame; if (meta_window_actor_is_destroyed (actor)) return; frame = g_slice_new0 (FrameData); frame->frame_counter = -1; frame->sync_request_serial = window->sync_request_serial; actor_x11->frames = g_list_prepend (actor_x11->frames, frame); actor_x11->needs_frame_drawn = TRUE; if (skip_sync_delay) { ClutterActor *stage = clutter_actor_get_stage (CLUTTER_ACTOR (actor_x11)); clutter_stage_skip_sync_delay (CLUTTER_STAGE (stage)); } if (!actor_x11->repaint_scheduled) { MetaSurfaceActor *surface; gboolean is_obscured; surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (surface) is_obscured = meta_surface_actor_is_obscured (surface); else is_obscured = FALSE; /* A frame was marked by the client without actually doing any * damage or any unobscured, or while we had the window frozen * (e.g. during an interactive resize.) We need to make sure that the * pre_paint/post_paint functions get called, enabling us to * send a _NET_WM_FRAME_DRAWN. We do a 1-pixel redraw to get * consistent timing with non-empty frames. If the window * is completely obscured we fire off the send_frame_messages timeout. */ if (is_obscured) { queue_send_frame_messages_timeout (actor_x11); } else if (surface) { clutter_actor_queue_redraw (CLUTTER_ACTOR (surface)); actor_x11->repaint_scheduled = TRUE; } } } static gboolean has_shadow (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); if (actor_x11->shadow_mode == META_SHADOW_MODE_FORCED_OFF) return FALSE; if (actor_x11->shadow_mode == META_SHADOW_MODE_FORCED_ON) return TRUE; /* Leaving out shadows for tiled, maximized and fullscreen windows is an effeciency * win and also prevents the unsightly effect of the shadow of maximized * window appearing on an adjacent window */ if ((meta_window_get_maximized (window) == META_MAXIMIZE_BOTH) || meta_window_is_fullscreen (window) || META_WINDOW_TILED (window)) return FALSE; /* * Always put a shadow around windows with a frame - This should override * the restriction about not putting a shadow around ARGB windows. */ if (meta_window_get_frame (window)) return TRUE; /* * Do not add shadows to non-opaque (ARGB32) windows, as we can't easily * generate shadows for them. */ if (!meta_window_actor_is_opaque (META_WINDOW_ACTOR (actor_x11))) return FALSE; /* * If a window specifies that it has custom frame extents, that likely * means that it is drawing a shadow itself. Don't draw our own. */ if (window->has_custom_frame_extents) return FALSE; /* * Generate shadows for all other windows. */ return TRUE; } gboolean meta_window_actor_x11_should_unredirect (MetaWindowActorX11 *actor_x11) { MetaSurfaceActor *surface; MetaSurfaceActorX11 *surface_x11; if (meta_window_actor_is_destroyed (META_WINDOW_ACTOR (actor_x11))) return FALSE; surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (!surface) return FALSE; if (!META_IS_SURFACE_ACTOR_X11 (surface)) return FALSE; surface_x11 = META_SURFACE_ACTOR_X11 (surface); return meta_surface_actor_x11_should_unredirect (surface_x11); } void meta_window_actor_x11_set_unredirected (MetaWindowActorX11 *actor_x11, gboolean unredirected) { MetaSurfaceActor *surface; MetaSurfaceActorX11 *surface_x11; surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); g_assert (surface); g_return_if_fail (META_IS_SURFACE_ACTOR_X11 (surface)); surface_x11 = META_SURFACE_ACTOR_X11 (surface); meta_surface_actor_x11_set_unredirected (surface_x11, unredirected); } static const char * get_shadow_class (MetaWindowActorX11 *actor_x11) { if (actor_x11->shadow_class) { return actor_x11->shadow_class; } else { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaWindowType window_type; window_type = meta_window_get_window_type (window); switch (window_type) { case META_WINDOW_DROPDOWN_MENU: case META_WINDOW_COMBO: return "dropdown-menu"; case META_WINDOW_POPUP_MENU: return "popup-menu"; default: { MetaFrameType frame_type; frame_type = meta_window_get_frame_type (window); return meta_frame_type_to_string (frame_type); } } } } static void get_shadow_params (MetaWindowActorX11 *actor_x11, gboolean appears_focused, MetaShadowParams *params) { const char *shadow_class = get_shadow_class (actor_x11); meta_shadow_factory_get_params (actor_x11->shadow_factory, shadow_class, appears_focused, params); } static void get_shape_bounds (MetaWindowActorX11 *actor_x11, cairo_rectangle_int_t *bounds) { cairo_region_get_extents (actor_x11->shape_region, bounds); } static void get_shadow_bounds (MetaWindowActorX11 *actor_x11, gboolean appears_focused, cairo_rectangle_int_t *bounds) { MetaShadow *shadow; cairo_rectangle_int_t shape_bounds; MetaShadowParams params; shadow = appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow; get_shape_bounds (actor_x11, &shape_bounds); get_shadow_params (actor_x11, appears_focused, ¶ms); meta_shadow_get_bounds (shadow, params.x_offset + shape_bounds.x, params.y_offset + shape_bounds.y, shape_bounds.width, shape_bounds.height, bounds); } /* If we have an ARGB32 window that we decorate with a frame, it's * probably something like a translucent terminal - something where * the alpha channel represents transparency rather than a shape. We * don't want to show the shadow through the translucent areas since * the shadow is wrong for translucent windows (it should be * translucent itself and colored), and not only that, will /look/ * horribly wrong - a misplaced big black blob. As a hack, what we * want to do is just draw the shadow as normal outside the frame, and * inside the frame draw no shadow. This is also not even close to * the right result, but looks OK. We also apply this approach to * windows set to be partially translucent with _NET_WM_WINDOW_OPACITY. */ static gboolean clip_shadow_under_window (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); if (window->frame) return TRUE; return meta_window_actor_is_opaque (META_WINDOW_ACTOR (actor_x11)); } /** * set_clip_region_beneath: * @actor_x11: a #MetaWindowActorX11 * @clip_region: the region of the screen that isn't completely * obscured beneath the main window texture. * * Provides a hint as to what areas need to be drawn *beneath* * the main window texture. This is the relevant clip region * when drawing the shadow, properly accounting for areas of the * shadow hid by the window itself. This will be set before painting * then unset afterwards. */ static void set_clip_region_beneath (MetaWindowActorX11 *actor_x11, cairo_region_t *beneath_region) { MetaWindow *window; gboolean appears_focused; window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); appears_focused = meta_window_appears_focused (window); if (appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow) { g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy); if (beneath_region) { actor_x11->shadow_clip = cairo_region_copy (beneath_region); if (clip_shadow_under_window (actor_x11)) { if (actor_x11->frame_bounds) cairo_region_subtract (actor_x11->shadow_clip, actor_x11->frame_bounds); } } else { actor_x11->shadow_clip = NULL; } } } static void check_needs_shadow (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaShadow *old_shadow = NULL; MetaShadow **shadow_location; gboolean recompute_shadow; gboolean should_have_shadow; gboolean appears_focused; /* Calling has_shadow() here at every pre-paint is cheap * and avoids the need to explicitly handle window type changes, which * we would do if tried to keep track of when we might be adding or removing * a shadow more explicitly. We only keep track of changes to the *shape* of * the shadow with actor_x11->recompute_shadow. */ should_have_shadow = has_shadow (actor_x11); appears_focused = meta_window_appears_focused (window); if (appears_focused) { recompute_shadow = actor_x11->recompute_focused_shadow; actor_x11->recompute_focused_shadow = FALSE; shadow_location = &actor_x11->focused_shadow; } else { recompute_shadow = actor_x11->recompute_unfocused_shadow; actor_x11->recompute_unfocused_shadow = FALSE; shadow_location = &actor_x11->unfocused_shadow; } if (!should_have_shadow || recompute_shadow) { if (*shadow_location != NULL) { old_shadow = *shadow_location; *shadow_location = NULL; } } if (!*shadow_location && should_have_shadow) { MetaShadowFactory *factory = actor_x11->shadow_factory; const char *shadow_class = get_shadow_class (actor_x11); cairo_rectangle_int_t shape_bounds; if (!actor_x11->shadow_shape) { actor_x11->shadow_shape = meta_window_shape_new (actor_x11->shape_region); } get_shape_bounds (actor_x11, &shape_bounds); *shadow_location = meta_shadow_factory_get_shadow (factory, actor_x11->shadow_shape, shape_bounds.width, shape_bounds.height, shadow_class, appears_focused); } if (old_shadow) meta_shadow_unref (old_shadow); } void meta_window_actor_x11_process_damage (MetaWindowActorX11 *actor_x11, XDamageNotifyEvent *event) { MetaSurfaceActor *surface; surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (surface) meta_surface_actor_process_damage (surface, event->area.x, event->area.y, event->area.width, event->area.height); meta_window_actor_notify_damaged (META_WINDOW_ACTOR (actor_x11)); } static cairo_region_t * scan_visible_region (guchar *mask_data, int stride, cairo_region_t *scan_area) { int i, n_rects = cairo_region_num_rectangles (scan_area); MetaRegionBuilder builder; meta_region_builder_init (&builder); for (i = 0; i < n_rects; i++) { int x, y; cairo_rectangle_int_t rect; cairo_region_get_rectangle (scan_area, i, &rect); for (y = rect.y; y < (rect.y + rect.height); y++) { for (x = rect.x; x < (rect.x + rect.width); x++) { int x2 = x; while (mask_data[y * stride + x2] == 255 && x2 < (rect.x + rect.width)) x2++; if (x2 > x) { meta_region_builder_add_rectangle (&builder, x, y, x2 - x, 1); x = x2; } } } } return meta_region_builder_finish (&builder); } static void get_client_area_rect_from_texture (MetaWindowActorX11 *actor_x11, MetaShapedTexture *shaped_texture, cairo_rectangle_int_t *client_area) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); cairo_rectangle_int_t surface_rect = { 0 }; surface_rect.width = meta_shaped_texture_get_width (shaped_texture); surface_rect.height = meta_shaped_texture_get_height (shaped_texture); meta_window_x11_surface_rect_to_client_rect (window, &surface_rect, client_area); } static void get_client_area_rect (MetaWindowActorX11 *actor_x11, cairo_rectangle_int_t *client_area) { MetaSurfaceActor *surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaShapedTexture *stex = meta_surface_actor_get_texture (surface); if (!meta_window_x11_always_update_shape (window) || !stex) { meta_window_get_client_area_rect (window, client_area); return; } get_client_area_rect_from_texture (actor_x11, stex, client_area); } static void build_and_scan_frame_mask (MetaWindowActorX11 *actor_x11, cairo_region_t *shape_region) { ClutterBackend *backend = clutter_get_default_backend (); MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); CoglContext *ctx = clutter_backend_get_cogl_context (backend); MetaSurfaceActor *surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); uint8_t *mask_data; unsigned int tex_width, tex_height; MetaShapedTexture *stex; CoglTexture2D *mask_texture; int stride; cairo_t *cr; cairo_surface_t *image; GError *error = NULL; stex = meta_surface_actor_get_texture (surface); g_return_if_fail (stex); meta_shaped_texture_set_mask_texture (stex, NULL); tex_width = meta_shaped_texture_get_width (stex); tex_height = meta_shaped_texture_get_height (stex); stride = cairo_format_stride_for_width (CAIRO_FORMAT_A8, tex_width); /* Create data for an empty image */ mask_data = g_malloc0 (stride * tex_height); image = cairo_image_surface_create_for_data (mask_data, CAIRO_FORMAT_A8, tex_width, tex_height, stride); cr = cairo_create (image); gdk_cairo_region (cr, shape_region); cairo_fill (cr); if (window->frame) { cairo_region_t *frame_paint_region, *scanned_region; cairo_rectangle_int_t rect = { 0, 0, tex_width, tex_height }; cairo_rectangle_int_t client_area; cairo_rectangle_int_t frame_rect; /* If we update the shape regardless of the frozen state of the actor, * as with Xwayland to avoid the black shadow effect, we ought to base * the frame size on the buffer size rather than the reported window's * frame size, as the buffer may not have been committed yet at this * point. */ if (meta_window_x11_always_update_shape (window)) { meta_window_x11_surface_rect_to_frame_rect (window, &rect, &frame_rect); get_client_area_rect_from_texture (actor_x11, stex, &client_area); } else { meta_window_get_frame_rect (window, &frame_rect); meta_window_get_client_area_rect (window, &client_area); } /* Make sure we don't paint the frame over the client window. */ frame_paint_region = cairo_region_create_rectangle (&rect); cairo_region_subtract_rectangle (frame_paint_region, &client_area); gdk_cairo_region (cr, frame_paint_region); cairo_clip (cr); meta_frame_get_mask (window->frame, &frame_rect, cr); cairo_surface_flush (image); scanned_region = scan_visible_region (mask_data, stride, frame_paint_region); cairo_region_union (shape_region, scanned_region); cairo_region_destroy (scanned_region); cairo_region_destroy (frame_paint_region); } cairo_destroy (cr); cairo_surface_destroy (image); mask_texture = cogl_texture_2d_new_from_data (ctx, tex_width, tex_height, COGL_PIXEL_FORMAT_A_8, stride, mask_data, &error); if (error) { g_warning ("Failed to allocate mask texture: %s", error->message); g_error_free (error); } if (mask_texture) { meta_shaped_texture_set_mask_texture (stex, COGL_TEXTURE (mask_texture)); cogl_object_unref (mask_texture); } else { meta_shaped_texture_set_mask_texture (stex, NULL); } g_free (mask_data); } static void invalidate_shadow (MetaWindowActorX11 *actor_x11) { actor_x11->recompute_focused_shadow = TRUE; actor_x11->recompute_unfocused_shadow = TRUE; if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11))) return; clutter_actor_queue_redraw (CLUTTER_ACTOR (actor_x11)); } static void update_shape_region (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); cairo_region_t *region = NULL; cairo_rectangle_int_t client_area; get_client_area_rect (actor_x11, &client_area); if (window->frame && window->shape_region) { region = cairo_region_copy (window->shape_region); cairo_region_translate (region, client_area.x, client_area.y); } else if (window->shape_region != NULL) { region = cairo_region_reference (window->shape_region); } else { /* If we don't have a shape on the server, that means that * we have an implicit shape of one rectangle covering the * entire window. */ region = cairo_region_create_rectangle (&client_area); } if (window->shape_region || window->frame) build_and_scan_frame_mask (actor_x11, region); g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy); actor_x11->shape_region = region; g_clear_pointer (&actor_x11->shadow_shape, meta_window_shape_unref); invalidate_shadow (actor_x11); } static void update_input_region (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); MetaSurfaceActor *surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); cairo_region_t *region; if (window->shape_region && window->input_region) { region = cairo_region_copy (window->shape_region); cairo_region_intersect (region, window->input_region); } else if (window->shape_region) { region = cairo_region_reference (window->shape_region); } else if (window->input_region) { region = cairo_region_reference (window->input_region); } else { region = NULL; } meta_surface_actor_set_input_region (surface, region); cairo_region_destroy (region); } static gboolean is_actor_maybe_transparent (MetaWindowActorX11 *actor_x11) { MetaSurfaceActor *surface; MetaShapedTexture *stex; surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (!surface) return TRUE; if (META_IS_SURFACE_ACTOR_X11 (surface) && meta_surface_actor_x11_is_unredirected (META_SURFACE_ACTOR_X11 (surface))) return FALSE; stex = meta_surface_actor_get_texture (surface); if (!meta_shaped_texture_has_alpha (stex)) return FALSE; return TRUE; } static void update_opaque_region (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); gboolean is_maybe_transparent; cairo_region_t *opaque_region; MetaSurfaceActor *surface; is_maybe_transparent = is_actor_maybe_transparent (actor_x11); if (is_maybe_transparent && window->opaque_region) { cairo_rectangle_int_t client_area; get_client_area_rect (actor_x11, &client_area); /* The opaque region is defined to be a part of the * window which ARGB32 will always paint with opaque * pixels. For these regions, we want to avoid painting * windows and shadows beneath them. * * If the client gives bad coordinates where it does not * fully paint, the behavior is defined by the specification * to be undefined, and considered a client bug. In mutter's * case, graphical glitches will occur. */ opaque_region = cairo_region_copy (window->opaque_region); cairo_region_translate (opaque_region, client_area.x, client_area.y); cairo_region_intersect (opaque_region, actor_x11->shape_region); } else if (is_maybe_transparent) { opaque_region = NULL; } else { opaque_region = cairo_region_reference (actor_x11->shape_region); } surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); meta_surface_actor_set_opaque_region (surface, opaque_region); cairo_region_destroy (opaque_region); } static void update_frame_bounds (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy); actor_x11->frame_bounds = cairo_region_copy (meta_window_get_frame_bounds (window)); } static void update_regions (MetaWindowActorX11 *actor_x11) { if (!actor_x11->needs_reshape) return; update_shape_region (actor_x11); update_input_region (actor_x11); update_opaque_region (actor_x11); actor_x11->needs_reshape = FALSE; } static void check_needs_reshape (MetaWindowActorX11 *actor_x11) { MetaWindow *window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); if (meta_window_x11_always_update_shape (window)) return; update_regions (actor_x11); } void meta_window_actor_x11_update_shape (MetaWindowActorX11 *actor_x11) { MetaSurfaceActor *surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); actor_x11->needs_reshape = TRUE; if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11))) return; clutter_actor_queue_redraw (CLUTTER_ACTOR (surface)); } static void handle_updates (MetaWindowActorX11 *actor_x11) { MetaSurfaceActor *surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); MetaWindow *window; if (META_IS_SURFACE_ACTOR_X11 (surface) && meta_surface_actor_x11_is_unredirected (META_SURFACE_ACTOR_X11 (surface))) return; window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); if (meta_window_actor_is_frozen (META_WINDOW_ACTOR (actor_x11))) { /* The window is frozen due to a pending animation: we'll wait until * the animation finishes to repair the window. * * However, with Xwayland, we still might need to update the shape * region as the wl_buffer will be set to plain black on resize, * which causes the shadows to look bad. */ if (surface && meta_window_x11_always_update_shape (window)) check_needs_reshape (actor_x11); return; } meta_surface_actor_pre_paint (surface); if (!META_IS_SURFACE_ACTOR_X11 (surface) || !meta_surface_actor_x11_is_visible (META_SURFACE_ACTOR_X11 (surface))) return; update_frame_bounds (actor_x11); check_needs_reshape (actor_x11); check_needs_shadow (actor_x11); } static void meta_window_actor_x11_pre_paint (MetaWindowActor *actor) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); handle_updates (actor_x11); assign_frame_counter_to_frames (actor_x11); } static void meta_window_actor_x11_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaWindow *window; gboolean appears_focused; MetaShadow *shadow; /* This window got damage when obscured; we set up a timer * to send frame completion events, but since we're drawing * the window now (for some other reason) cancel the timer * and send the completion events normally */ if (actor_x11->send_frame_messages_timer != 0) { remove_frame_messages_timer (actor_x11); assign_frame_counter_to_frames (actor_x11); } window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); appears_focused = meta_window_appears_focused (window); shadow = appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow; if (shadow) { MetaShadowParams params; cairo_rectangle_int_t shape_bounds; cairo_region_t *clip = actor_x11->shadow_clip; CoglFramebuffer *framebuffer; get_shape_bounds (actor_x11, &shape_bounds); get_shadow_params (actor_x11, appears_focused, ¶ms); /* The frame bounds are already subtracted from actor_x11->shadow_clip * if that exists. */ if (!clip && clip_shadow_under_window (actor_x11)) { cairo_rectangle_int_t bounds; get_shadow_bounds (actor_x11, appears_focused, &bounds); clip = cairo_region_create_rectangle (&bounds); if (actor_x11->frame_bounds) cairo_region_subtract (clip, actor_x11->frame_bounds); } framebuffer = clutter_paint_context_get_framebuffer (paint_context); meta_shadow_paint (shadow, framebuffer, params.x_offset + shape_bounds.x, params.y_offset + shape_bounds.y, shape_bounds.width, shape_bounds.height, (clutter_actor_get_paint_opacity (actor) * params.opacity * window->opacity) / (255 * 255), clip, clip_shadow_under_window (actor_x11)); if (clip && clip != actor_x11->shadow_clip) cairo_region_destroy (clip); } CLUTTER_ACTOR_CLASS (meta_window_actor_x11_parent_class)->paint (actor, paint_context); } static void meta_window_actor_x11_post_paint (MetaWindowActor *actor) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaWindow *window; actor_x11->repaint_scheduled = FALSE; if (meta_window_actor_is_destroyed (actor)) return; /* If the window had damage, but wasn't actually redrawn because * it is obscured, we should wait until timer expiration before * sending _NET_WM_FRAME_* messages. */ if (actor_x11->send_frame_messages_timer == 0 && actor_x11->needs_frame_drawn) { GList *l; for (l = actor_x11->frames; l; l = l->next) { FrameData *frame = l->data; if (frame->frame_drawn_time == 0) do_send_frame_drawn (actor_x11, frame); } actor_x11->needs_frame_drawn = FALSE; } /* This is for Xwayland, and a no-op on plain Xorg */ window = meta_window_actor_get_meta_window (actor); if (meta_window_x11_should_thaw_after_paint (window)) { meta_window_x11_thaw_commits (window); meta_window_x11_set_thaw_after_paint (window, FALSE); } } static gboolean meta_window_actor_x11_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaWindow *window; gboolean appears_focused; MetaSurfaceActor *surface; /* The paint volume is computed before paint functions are called * so our bounds might not be updated yet. Force an update. */ handle_updates (actor_x11); window = meta_window_actor_get_meta_window (META_WINDOW_ACTOR (actor_x11)); appears_focused = meta_window_appears_focused (window); if (appears_focused ? actor_x11->focused_shadow : actor_x11->unfocused_shadow) { cairo_rectangle_int_t shadow_bounds; ClutterActorBox shadow_box; /* We could compute an full clip region as we do for the window * texture, but the shadow is relatively cheap to draw, and * a little more complex to clip, so we just catch the case where * the shadow is completely obscured and doesn't need to be drawn * at all. */ get_shadow_bounds (actor_x11, appears_focused, &shadow_bounds); shadow_box.x1 = shadow_bounds.x; shadow_box.x2 = shadow_bounds.x + shadow_bounds.width; shadow_box.y1 = shadow_bounds.y; shadow_box.y2 = shadow_bounds.y + shadow_bounds.height; clutter_paint_volume_union_box (volume, &shadow_box); } surface = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (surface) { ClutterActor *surface_actor = CLUTTER_ACTOR (surface); const ClutterPaintVolume *child_volume; child_volume = clutter_actor_get_transformed_paint_volume (surface_actor, actor); if (!child_volume) return FALSE; clutter_paint_volume_union (volume, child_volume); } return TRUE; } static void meta_window_actor_x11_queue_destroy (MetaWindowActor *actor) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); if (actor_x11->send_frame_messages_timer != 0) remove_frame_messages_timer (actor_x11); } static void meta_window_actor_x11_set_frozen (MetaWindowActor *actor, gboolean frozen) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (actor); MetaWindow *window = meta_window_actor_get_meta_window (actor); if (actor_x11->is_frozen == frozen) return; actor_x11->is_frozen = frozen; if (frozen) meta_window_x11_freeze_commits (window); else meta_window_x11_thaw_commits (window); } static void meta_window_actor_x11_update_regions (MetaWindowActor *actor) { update_regions (META_WINDOW_ACTOR_X11 (actor)); } static void meta_window_actor_x11_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object); switch (prop_id) { case PROP_SHADOW_MODE: { MetaShadowMode newv = g_value_get_enum (value); if (newv == actor_x11->shadow_mode) return; actor_x11->shadow_mode = newv; invalidate_shadow (actor_x11); } break; case PROP_SHADOW_CLASS: { const char *newv = g_value_get_string (value); if (g_strcmp0 (newv, actor_x11->shadow_class) == 0) return; g_free (actor_x11->shadow_class); actor_x11->shadow_class = g_strdup (newv); invalidate_shadow (actor_x11); } break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_actor_x11_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object); switch (prop_id) { case PROP_SHADOW_MODE: g_value_set_enum (value, actor_x11->shadow_mode); break; case PROP_SHADOW_CLASS: g_value_set_string (value, actor_x11->shadow_class); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_actor_x11_constructed (GObject *object) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object); MetaWindowActor *actor = META_WINDOW_ACTOR (actor_x11); MetaWindow *window = meta_window_actor_get_meta_window (actor); /* * Start off with an empty shape region to maintain the invariant that it's * always set. */ actor_x11->shape_region = cairo_region_create (); G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->constructed (object); /* If a window doesn't start off with updates frozen, we should * we should send a _NET_WM_FRAME_DRAWN immediately after the first drawn. */ if (window->extended_sync_request_counter && !meta_window_updates_are_frozen (window)) meta_window_actor_queue_frame_drawn (actor, FALSE); } static void meta_window_actor_x11_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { MetaWindowActorX11 *self = META_WINDOW_ACTOR_X11 (cullable); cullable_parent_iface->cull_out (cullable, unobscured_region, clip_region); set_clip_region_beneath (self, clip_region); } static void meta_window_actor_x11_reset_culling (MetaCullable *cullable) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (cullable); g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy); cullable_parent_iface->reset_culling (cullable); } static void cullable_iface_init (MetaCullableInterface *iface) { cullable_parent_iface = g_type_interface_peek_parent (iface); iface->cull_out = meta_window_actor_x11_cull_out; iface->reset_culling = meta_window_actor_x11_reset_culling; } static void meta_window_actor_x11_dispose (GObject *object) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object); MetaSurfaceActor *surface_actor; g_clear_signal_handler (&actor_x11->shadow_factory_changed_handler_id, actor_x11->shadow_factory); if (actor_x11->send_frame_messages_timer != 0) remove_frame_messages_timer (actor_x11); surface_actor = meta_window_actor_get_surface (META_WINDOW_ACTOR (actor_x11)); if (surface_actor) { g_clear_signal_handler (&actor_x11->repaint_scheduled_id, surface_actor); g_clear_signal_handler (&actor_x11->size_changed_id, surface_actor); } g_clear_pointer (&actor_x11->shape_region, cairo_region_destroy); g_clear_pointer (&actor_x11->shadow_clip, cairo_region_destroy); g_clear_pointer (&actor_x11->frame_bounds, cairo_region_destroy); g_clear_pointer (&actor_x11->shadow_class, g_free); g_clear_pointer (&actor_x11->focused_shadow, meta_shadow_unref); g_clear_pointer (&actor_x11->unfocused_shadow, meta_shadow_unref); g_clear_pointer (&actor_x11->shadow_shape, meta_window_shape_unref); G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->dispose (object); } static void meta_window_actor_x11_finalize (GObject *object) { MetaWindowActorX11 *actor_x11 = META_WINDOW_ACTOR_X11 (object); g_list_free_full (actor_x11->frames, (GDestroyNotify) frame_data_free); G_OBJECT_CLASS (meta_window_actor_x11_parent_class)->finalize (object); } static void meta_window_actor_x11_class_init (MetaWindowActorX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); MetaWindowActorClass *window_actor_class = META_WINDOW_ACTOR_CLASS (klass); GParamSpec *pspec; window_actor_class->frame_complete = meta_window_actor_x11_frame_complete; window_actor_class->assign_surface_actor = meta_window_actor_x11_assign_surface_actor; window_actor_class->queue_frame_drawn = meta_window_actor_x11_queue_frame_drawn; window_actor_class->pre_paint = meta_window_actor_x11_pre_paint; window_actor_class->post_paint = meta_window_actor_x11_post_paint; window_actor_class->queue_destroy = meta_window_actor_x11_queue_destroy; window_actor_class->set_frozen = meta_window_actor_x11_set_frozen; window_actor_class->update_regions = meta_window_actor_x11_update_regions; actor_class->paint = meta_window_actor_x11_paint; actor_class->get_paint_volume = meta_window_actor_x11_get_paint_volume; object_class->constructed = meta_window_actor_x11_constructed; object_class->set_property = meta_window_actor_x11_set_property; object_class->get_property = meta_window_actor_x11_get_property; object_class->dispose = meta_window_actor_x11_dispose; object_class->finalize = meta_window_actor_x11_finalize; pspec = g_param_spec_enum ("shadow-mode", "Shadow mode", "Decides when to paint shadows", META_TYPE_SHADOW_MODE, META_SHADOW_MODE_AUTO, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SHADOW_MODE, pspec); pspec = g_param_spec_string ("shadow-class", "Name of the shadow class for this window.", "NULL means to use the default shadow class for this window type", NULL, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_SHADOW_CLASS, pspec); user_shadow_mode = (g_strcmp0 ("1", g_getenv ("MUFFIN_NO_SHADOWS")) == 0) ? META_SHADOW_MODE_FORCED_OFF : META_SHADOW_MODE_AUTO; } static void meta_window_actor_x11_init (MetaWindowActorX11 *self) { /* We do this now since we might be going right back into the frozen state. */ g_signal_connect (self, "thawed", G_CALLBACK (handle_updates), NULL); self->shadow_factory = meta_shadow_factory_get_default (); self->shadow_factory_changed_handler_id = g_signal_connect_swapped (self->shadow_factory, "changed", G_CALLBACK (invalidate_shadow), self); self->shadow_mode = user_shadow_mode; } muffin-6.4.1/src/compositor/meta-surface-actor-x11.h0000664000175000017500000000402514723361714021172 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Owen Taylor * Jasper St. Pierre */ #ifndef __META_SURFACE_ACTOR_X11_H__ #define __META_SURFACE_ACTOR_X11_H__ #include #include #include "compositor/meta-surface-actor.h" #include "meta/display.h" #include "meta/window.h" G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR_X11 (meta_surface_actor_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaSurfaceActorX11, meta_surface_actor_x11, META, SURFACE_ACTOR_X11, MetaSurfaceActor) MetaSurfaceActor * meta_surface_actor_x11_new (MetaWindow *window); void meta_surface_actor_x11_set_size (MetaSurfaceActorX11 *self, int width, int height); gboolean meta_surface_actor_x11_should_unredirect (MetaSurfaceActorX11 *self); void meta_surface_actor_x11_set_unredirected (MetaSurfaceActorX11 *self, gboolean unredirected); gboolean meta_surface_actor_x11_is_unredirected (MetaSurfaceActorX11 *self); gboolean meta_surface_actor_x11_is_visible (MetaSurfaceActorX11 *self); G_END_DECLS #endif /* __META_SURFACE_ACTOR_X11_H__ */ muffin-6.4.1/src/compositor/meta-shaped-texture.c0000664000175000017500000013107714723361714020772 0ustar fabiofabio/* * Authored By Neil Roberts * and Jasper St. Pierre * * Copyright (C) 2008 Intel Corporation * Copyright (C) 2012 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /** * SECTION:meta-shaped-texture * @title: MetaShapedTexture * @short_description: An actor to draw a masked texture. */ #include "config.h" #include "backends/meta-monitor-transform.h" #include "compositor/meta-shaped-texture-private.h" #include "core/boxes-private.h" #include #include #include "cogl/cogl.h" #include "compositor/clutter-utils.h" #include "compositor/meta-texture-tower.h" #include "compositor/region-utils.h" #include "core/boxes-private.h" #include "meta/meta-shaped-texture.h" /* MAX_MIPMAPPING_FPS needs to be as small as possible for the best GPU * performance, but higher than the refresh rate of commonly slow updating * windows like top or a blinking cursor, so that such windows do get * mipmapped. */ #define MAX_MIPMAPPING_FPS 5 #define MIN_MIPMAP_AGE_USEC (G_USEC_PER_SEC / MAX_MIPMAPPING_FPS) /* MIN_FAST_UPDATES_BEFORE_UNMIPMAP allows windows to update themselves * occasionally without causing mipmapping to be disabled, so long as such * an update takes fewer update_area calls than: */ #define MIN_FAST_UPDATES_BEFORE_UNMIPMAP 20 static void meta_shaped_texture_dispose (GObject *object); static void clutter_content_iface_init (ClutterContentInterface *iface); enum { SIZE_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL]; struct _MetaShapedTexture { GObject parent; MetaTextureTower *paint_tower; CoglTexture *texture; CoglTexture *mask_texture; CoglSnippet *snippet; CoglPipeline *base_pipeline; CoglPipeline *masked_pipeline; CoglPipeline *unblended_pipeline; gboolean is_y_inverted; /* The region containing only fully opaque pixels */ cairo_region_t *opaque_region; /* MetaCullable regions, see that documentation for more details */ cairo_region_t *clip_region; gboolean size_invalid; MetaMonitorTransform transform; gboolean has_viewport_src_rect; graphene_rect_t viewport_src_rect; gboolean has_viewport_dst_size; int viewport_dst_width; int viewport_dst_height; int tex_width, tex_height; int fallback_width, fallback_height; int dst_width, dst_height; gint64 prev_invalidation, last_invalidation; guint fast_updates; guint remipmap_timeout_id; gint64 earliest_remipmap; int buffer_scale; guint create_mipmaps : 1; }; G_DEFINE_TYPE_WITH_CODE (MetaShapedTexture, meta_shaped_texture, G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (CLUTTER_TYPE_CONTENT, clutter_content_iface_init)); static void meta_shaped_texture_class_init (MetaShapedTextureClass *klass) { GObjectClass *gobject_class = (GObjectClass *) klass; gobject_class->dispose = meta_shaped_texture_dispose; signals[SIZE_CHANGED] = g_signal_new ("size-changed", G_TYPE_FROM_CLASS (gobject_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void invalidate_size (MetaShapedTexture *stex) { stex->size_invalid = TRUE; } static void meta_shaped_texture_init (MetaShapedTexture *stex) { stex->paint_tower = meta_texture_tower_new (); stex->buffer_scale = 1; stex->texture = NULL; stex->mask_texture = NULL; stex->create_mipmaps = TRUE; stex->is_y_inverted = TRUE; stex->transform = META_MONITOR_TRANSFORM_NORMAL; } static void update_size (MetaShapedTexture *stex) { int buffer_scale = stex->buffer_scale; int dst_width; int dst_height; if (stex->has_viewport_dst_size) { dst_width = stex->viewport_dst_width; dst_height = stex->viewport_dst_height; } else if (stex->has_viewport_src_rect) { dst_width = stex->viewport_src_rect.size.width; dst_height = stex->viewport_src_rect.size.height; } else { if (meta_monitor_transform_is_rotated (stex->transform)) { if (stex->texture) { dst_width = stex->tex_height / buffer_scale; dst_height = stex->tex_width / buffer_scale; } else { dst_width = stex->fallback_height / buffer_scale; dst_height = stex->fallback_width / buffer_scale; } } else { if (stex->texture) { dst_width = stex->tex_width / buffer_scale; dst_height = stex->tex_height / buffer_scale; } else { dst_width = stex->fallback_width / buffer_scale; dst_height = stex->fallback_height / buffer_scale; } } } stex->size_invalid = FALSE; if (stex->dst_width != dst_width || stex->dst_height != dst_height) { stex->dst_width = dst_width; stex->dst_height = dst_height; meta_shaped_texture_set_mask_texture (stex, NULL); clutter_content_invalidate_size (CLUTTER_CONTENT (stex)); g_signal_emit (stex, signals[SIZE_CHANGED], 0); } } static void ensure_size_valid (MetaShapedTexture *stex) { if (stex->size_invalid) update_size (stex); } void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex, cairo_region_t *clip_region) { g_clear_pointer (&stex->clip_region, cairo_region_destroy); if (clip_region) stex->clip_region = cairo_region_reference (clip_region); } static void meta_shaped_texture_reset_pipelines (MetaShapedTexture *stex) { g_clear_pointer (&stex->base_pipeline, cogl_object_unref); g_clear_pointer (&stex->masked_pipeline, cogl_object_unref); g_clear_pointer (&stex->unblended_pipeline, cogl_object_unref); } static void meta_shaped_texture_dispose (GObject *object) { MetaShapedTexture *stex = (MetaShapedTexture *) object; g_clear_handle_id (&stex->remipmap_timeout_id, g_source_remove); if (stex->paint_tower) meta_texture_tower_free (stex->paint_tower); stex->paint_tower = NULL; g_clear_pointer (&stex->texture, cogl_object_unref); meta_shaped_texture_set_mask_texture (stex, NULL); meta_shaped_texture_reset_pipelines (stex); g_clear_pointer (&stex->opaque_region, cairo_region_destroy); g_clear_pointer (&stex->clip_region, cairo_region_destroy); g_clear_pointer (&stex->snippet, cogl_object_unref); G_OBJECT_CLASS (meta_shaped_texture_parent_class)->dispose (object); } static CoglPipeline * get_base_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { CoglPipeline *pipeline; CoglMatrix matrix; if (stex->base_pipeline) return stex->base_pipeline; pipeline = cogl_pipeline_new (ctx); cogl_pipeline_set_layer_wrap_mode_s (pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode_t (pipeline, 0, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode_s (pipeline, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); cogl_pipeline_set_layer_wrap_mode_t (pipeline, 1, COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE); cogl_matrix_init_identity (&matrix); if (!stex->is_y_inverted) { cogl_matrix_scale (&matrix, 1, -1, 1); cogl_matrix_translate (&matrix, 0, -1, 0); cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); } if (stex->transform != META_MONITOR_TRANSFORM_NORMAL) { graphene_euler_t euler; cogl_matrix_translate (&matrix, 0.5, 0.5, 0.0); switch (stex->transform) { case META_MONITOR_TRANSFORM_90: graphene_euler_init_with_order (&euler, 0.0, 0.0, 90.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_180: graphene_euler_init_with_order (&euler, 0.0, 0.0, 180.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_270: graphene_euler_init_with_order (&euler, 0.0, 0.0, 270.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_FLIPPED: graphene_euler_init_with_order (&euler, 0.0, 180.0, 0.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_FLIPPED_90: graphene_euler_init_with_order (&euler, 180.0, 0.0, 90.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_FLIPPED_180: graphene_euler_init_with_order (&euler, 0.0, 180.0, 180.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_FLIPPED_270: graphene_euler_init_with_order (&euler, 180.0, 0.0, 270.0, GRAPHENE_EULER_ORDER_SYXZ); break; case META_MONITOR_TRANSFORM_NORMAL: g_assert_not_reached (); } cogl_matrix_rotate_euler (&matrix, &euler); cogl_matrix_translate (&matrix, -0.5, -0.5, 0.0); } if (stex->has_viewport_src_rect) { float scaled_tex_width = stex->tex_width / (float) stex->buffer_scale; float scaled_tex_height = stex->tex_height / (float) stex->buffer_scale; if (meta_monitor_transform_is_rotated (stex->transform)) { cogl_matrix_scale (&matrix, stex->viewport_src_rect.size.width / scaled_tex_height, stex->viewport_src_rect.size.height / scaled_tex_width, 1); } else { cogl_matrix_scale (&matrix, stex->viewport_src_rect.size.width / scaled_tex_width, stex->viewport_src_rect.size.height / scaled_tex_height, 1); } cogl_matrix_translate (&matrix, stex->viewport_src_rect.origin.x / stex->viewport_src_rect.size.width, stex->viewport_src_rect.origin.y / stex->viewport_src_rect.size.height, 0); } cogl_pipeline_set_layer_matrix (pipeline, 0, &matrix); cogl_pipeline_set_layer_matrix (pipeline, 1, &matrix); if (stex->snippet) cogl_pipeline_add_layer_snippet (pipeline, 0, stex->snippet); stex->base_pipeline = pipeline; return stex->base_pipeline; } static CoglPipeline * get_unmasked_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { return get_base_pipeline (stex, ctx); } static CoglPipeline * get_masked_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { CoglPipeline *pipeline; if (stex->masked_pipeline) return stex->masked_pipeline; pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); cogl_pipeline_set_layer_combine (pipeline, 1, "RGBA = MODULATE (PREVIOUS, TEXTURE[A])", NULL); stex->masked_pipeline = pipeline; return pipeline; } static CoglPipeline * get_unblended_pipeline (MetaShapedTexture *stex, CoglContext *ctx) { CoglPipeline *pipeline; if (stex->unblended_pipeline) return stex->unblended_pipeline; pipeline = cogl_pipeline_copy (get_base_pipeline (stex, ctx)); cogl_pipeline_set_layer_combine (pipeline, 0, "RGBA = REPLACE (TEXTURE)", NULL); stex->unblended_pipeline = pipeline; return pipeline; } static void paint_clipped_rectangle_node (MetaShapedTexture *stex, ClutterPaintNode *root_node, CoglPipeline *pipeline, cairo_rectangle_int_t *rect, ClutterActorBox *alloc) { g_autoptr (ClutterPaintNode) node = NULL; float ratio_h, ratio_v; float x1, y1, x2, y2; float coords[8]; float alloc_width; float alloc_height; ratio_h = clutter_actor_box_get_width (alloc) / (float) stex->dst_width; ratio_v = clutter_actor_box_get_height (alloc) / (float) stex->dst_height; x1 = alloc->x1 + rect->x * ratio_h; y1 = alloc->y1 + rect->y * ratio_v; x2 = alloc->x1 + (rect->x + rect->width) * ratio_h; y2 = alloc->y1 + (rect->y + rect->height) * ratio_v; alloc_width = alloc->x2 - alloc->x1; alloc_height = alloc->y2 - alloc->y1; coords[0] = rect->x / alloc_width * ratio_h; coords[1] = rect->y / alloc_height * ratio_v; coords[2] = (rect->x + rect->width) / alloc_width * ratio_h; coords[3] = (rect->y + rect->height) / alloc_height * ratio_v; coords[4] = coords[0]; coords[5] = coords[1]; coords[6] = coords[2]; coords[7] = coords[3]; node = clutter_pipeline_node_new (pipeline); clutter_paint_node_set_static_name (node, "MetaShapedTexture (clipped)"); clutter_paint_node_add_child (root_node, node); clutter_paint_node_add_multitexture_rectangle (node, &(ClutterActorBox) { .x1 = x1, .y1 = y1, .x2 = x2, .y2 = y2, }, coords, 8); } static void set_cogl_texture (MetaShapedTexture *stex, CoglTexture *cogl_tex) { int width, height; cogl_clear_object (&stex->texture); if (cogl_tex != NULL) { stex->texture = cogl_object_ref (cogl_tex); width = cogl_texture_get_width (COGL_TEXTURE (cogl_tex)); height = cogl_texture_get_height (COGL_TEXTURE (cogl_tex)); } else { width = 0; height = 0; } if (stex->tex_width != width || stex->tex_height != height) { stex->tex_width = width; stex->tex_height = height; update_size (stex); } /* NB: We don't queue a redraw of the actor here because we don't * know how much of the buffer has changed with respect to the * previous buffer. We only queue a redraw in response to surface * damage. */ if (stex->create_mipmaps) meta_texture_tower_set_base_texture (stex->paint_tower, cogl_tex); } static gboolean texture_is_idle_and_not_mipmapped (gpointer user_data) { MetaShapedTexture *stex = META_SHAPED_TEXTURE (user_data); if ((g_get_monotonic_time () - stex->earliest_remipmap) < 0) return G_SOURCE_CONTINUE; clutter_content_invalidate (CLUTTER_CONTENT (stex)); stex->remipmap_timeout_id = 0; return G_SOURCE_REMOVE; } static inline void flip_ints (int *x, int *y) { int tmp; tmp = *x; *x = *y; *y = tmp; } static void do_paint_content (MetaShapedTexture *stex, ClutterPaintNode *root_node, ClutterPaintContext *paint_context, CoglTexture *paint_tex, ClutterActorBox *alloc, uint8_t opacity) { int dst_width, dst_height; cairo_rectangle_int_t content_rect; gboolean use_opaque_region; cairo_region_t *blended_tex_region; CoglContext *ctx; CoglPipelineFilter filter; CoglFramebuffer *framebuffer; int sample_width, sample_height; ensure_size_valid (stex); dst_width = stex->dst_width; dst_height = stex->dst_height; if (dst_width == 0 || dst_height == 0) /* no contents yet */ return; content_rect = (cairo_rectangle_int_t) { .x = 0, .y = 0, .width = dst_width, .height = dst_height, }; /* Use nearest-pixel interpolation if the texture is unscaled. This * improves performance, especially with software rendering. */ framebuffer = clutter_paint_node_get_framebuffer (root_node); if (!framebuffer) framebuffer = clutter_paint_context_get_framebuffer (paint_context); if (stex->has_viewport_src_rect) { sample_width = stex->viewport_src_rect.size.width * stex->buffer_scale; sample_height = stex->viewport_src_rect.size.height * stex->buffer_scale; } else { sample_width = cogl_texture_get_width (stex->texture); sample_height = cogl_texture_get_height (stex->texture); } if (meta_monitor_transform_is_rotated (stex->transform)) flip_ints (&sample_width, &sample_height); if (meta_actor_painting_untransformed (framebuffer, dst_width, dst_height, sample_width, sample_height, NULL, NULL)) filter = COGL_PIPELINE_FILTER_NEAREST; else filter = COGL_PIPELINE_FILTER_LINEAR; ctx = clutter_backend_get_cogl_context (clutter_get_default_backend ()); use_opaque_region = stex->opaque_region && opacity == 255; if (use_opaque_region) { if (stex->clip_region) blended_tex_region = cairo_region_copy (stex->clip_region); else blended_tex_region = cairo_region_create_rectangle (&content_rect); cairo_region_subtract (blended_tex_region, stex->opaque_region); } else { if (stex->clip_region) blended_tex_region = cairo_region_reference (stex->clip_region); else blended_tex_region = NULL; } /* Limit to how many separate rectangles we'll draw; beyond this just * fall back and draw the whole thing */ #define MAX_RECTS 16 if (blended_tex_region) { int n_rects = cairo_region_num_rectangles (blended_tex_region); if (n_rects > MAX_RECTS) { /* Fall back to taking the fully blended path. */ use_opaque_region = FALSE; g_clear_pointer (&blended_tex_region, cairo_region_destroy); } } /* First, paint the unblended parts, which are part of the opaque region. */ if (use_opaque_region) { cairo_region_t *region; int n_rects; int i; if (stex->clip_region) { region = cairo_region_copy (stex->clip_region); cairo_region_intersect (region, stex->opaque_region); } else { region = cairo_region_reference (stex->opaque_region); } if (!cairo_region_is_empty (region)) { CoglPipeline *opaque_pipeline; opaque_pipeline = get_unblended_pipeline (stex, ctx); cogl_pipeline_set_layer_texture (opaque_pipeline, 0, paint_tex); cogl_pipeline_set_layer_filters (opaque_pipeline, 0, filter, filter); n_rects = cairo_region_num_rectangles (region); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); paint_clipped_rectangle_node (stex, root_node, opaque_pipeline, &rect, alloc); } } cairo_region_destroy (region); } /* Now, go ahead and paint the blended parts. */ /* We have three cases: * 1) blended_tex_region has rectangles - paint the rectangles. * 2) blended_tex_region is empty - don't paint anything * 3) blended_tex_region is NULL - paint fully-blended. * * 1) and 3) are the times where we have to paint stuff. This tests * for 1) and 3). */ if (!blended_tex_region || !cairo_region_is_empty (blended_tex_region)) { CoglPipeline *blended_pipeline; if (stex->mask_texture == NULL) { blended_pipeline = get_unmasked_pipeline (stex, ctx); } else { blended_pipeline = get_masked_pipeline (stex, ctx); cogl_pipeline_set_layer_texture (blended_pipeline, 1, stex->mask_texture); cogl_pipeline_set_layer_filters (blended_pipeline, 1, filter, filter); } cogl_pipeline_set_layer_texture (blended_pipeline, 0, paint_tex); cogl_pipeline_set_layer_filters (blended_pipeline, 0, filter, filter); CoglColor color; cogl_color_init_from_4ub (&color, opacity, opacity, opacity, opacity); cogl_pipeline_set_color (blended_pipeline, &color); if (blended_tex_region) { /* 1) blended_tex_region is not empty. Paint the rectangles. */ int i; int n_rects = cairo_region_num_rectangles (blended_tex_region); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (blended_tex_region, i, &rect); if (!gdk_rectangle_intersect (&content_rect, &rect, &rect)) continue; paint_clipped_rectangle_node (stex, root_node, blended_pipeline, &rect, alloc); } } else { g_autoptr (ClutterPaintNode) node = NULL; node = clutter_pipeline_node_new (blended_pipeline); clutter_paint_node_set_static_name (node, "MetaShapedTexture (unclipped)"); clutter_paint_node_add_child (root_node, node); /* 3) blended_tex_region is NULL. Do a full paint. */ clutter_paint_node_add_rectangle (node, alloc); } } g_clear_pointer (&blended_tex_region, cairo_region_destroy); } static CoglTexture * select_texture_for_paint (MetaShapedTexture *stex, ClutterPaintContext *paint_context) { CoglTexture *texture = NULL; int64_t now; if (!stex->texture) return NULL; now = g_get_monotonic_time (); if (stex->create_mipmaps && stex->last_invalidation) { int64_t age = now - stex->last_invalidation; if (age >= MIN_MIPMAP_AGE_USEC || stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP) { texture = meta_texture_tower_get_paint_texture (stex->paint_tower, paint_context); } } if (!texture) { texture = stex->texture; if (stex->create_mipmaps) { /* Minus 1000 to ensure we don't fail the age test in timeout */ stex->earliest_remipmap = now + MIN_MIPMAP_AGE_USEC - 1000; if (!stex->remipmap_timeout_id) stex->remipmap_timeout_id = g_timeout_add (MIN_MIPMAP_AGE_USEC / 1000, texture_is_idle_and_not_mipmapped, stex); } } return texture; } static void meta_shaped_texture_paint_content (ClutterContent *content, ClutterActor *actor, ClutterPaintNode *root_node, ClutterPaintContext *paint_context) { MetaShapedTexture *stex = META_SHAPED_TEXTURE (content); ClutterActorBox alloc; CoglTexture *paint_tex = NULL; uint8_t opacity; if (stex->clip_region && cairo_region_is_empty (stex->clip_region)) return; /* The GL EXT_texture_from_pixmap extension does allow for it to be * used together with SGIS_generate_mipmap, however this is very * rarely supported. Also, even when it is supported there * are distinct performance implications from: * * - Updating mipmaps that we don't need * - Having to reallocate pixmaps on the server into larger buffers * * So, we just unconditionally use our mipmap emulation code. If we * wanted to use SGIS_generate_mipmap, we'd have to query COGL to * see if it was supported (no API currently), and then if and only * if that was the case, set the clutter texture quality to HIGH. * Setting the texture quality to high without SGIS_generate_mipmap * support for TFP textures will result in fallbacks to XGetImage. */ paint_tex = select_texture_for_paint (stex, paint_context); if (!paint_tex) return; opacity = clutter_actor_get_paint_opacity (actor); clutter_actor_get_content_box (actor, &alloc); do_paint_content (stex, root_node, paint_context, paint_tex, &alloc, opacity); } static gboolean meta_shaped_texture_get_preferred_size (ClutterContent *content, float *width, float *height) { MetaShapedTexture *stex = META_SHAPED_TEXTURE (content); ensure_size_valid (stex); if (width) *width = stex->dst_width; if (height) *height = stex->dst_height; return TRUE; } static void clutter_content_iface_init (ClutterContentInterface *iface) { iface->paint_content = meta_shaped_texture_paint_content; iface->get_preferred_size = meta_shaped_texture_get_preferred_size; } void meta_shaped_texture_set_create_mipmaps (MetaShapedTexture *stex, gboolean create_mipmaps) { g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); create_mipmaps = create_mipmaps != FALSE; if (create_mipmaps != stex->create_mipmaps) { CoglTexture *base_texture; stex->create_mipmaps = create_mipmaps; base_texture = create_mipmaps ? stex->texture : NULL; meta_texture_tower_set_base_texture (stex->paint_tower, base_texture); } } void meta_shaped_texture_set_mask_texture (MetaShapedTexture *stex, CoglTexture *mask_texture) { g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); g_clear_pointer (&stex->mask_texture, cogl_object_unref); if (mask_texture != NULL) { stex->mask_texture = mask_texture; cogl_object_ref (stex->mask_texture); } clutter_content_invalidate (CLUTTER_CONTENT (stex)); } /** * meta_shaped_texture_update_area: * @stex: #MetaShapedTexture * @x: the x coordinate of the damaged area * @y: the y coordinate of the damaged area * @width: the width of the damaged area * @height: the height of the damaged area * @clip: (out): the resulting clip region * * Repairs the damaged area indicated by @x, @y, @width and @height * and potentially queues a redraw. * * Return value: Whether a redraw have been queued or not */ gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex, int x, int y, int width, int height, cairo_rectangle_int_t *clip) { MetaMonitorTransform inverted_transform; if (stex->texture == NULL) return FALSE; *clip = (cairo_rectangle_int_t) { .x = x, .y = y, .width = width, .height = height }; meta_rectangle_scale_double (clip, 1.0 / stex->buffer_scale, META_ROUNDING_STRATEGY_SHRINK, clip); inverted_transform = meta_monitor_transform_invert (stex->transform); ensure_size_valid (stex); meta_rectangle_transform (clip, inverted_transform, stex->dst_width, stex->dst_height, clip); if (stex->has_viewport_src_rect || stex->has_viewport_dst_size) { graphene_rect_t viewport; graphene_rect_t inverted_viewport; float dst_width; float dst_height; int inverted_dst_width; int inverted_dst_height; if (stex->has_viewport_src_rect) { viewport = stex->viewport_src_rect; } else { viewport = (graphene_rect_t) { .origin.x = 0, .origin.y = 0, .size.width = stex->tex_width, .size.height = stex->tex_height, }; } if (stex->has_viewport_dst_size) { dst_width = (float) stex->viewport_dst_width; dst_height = (float) stex->viewport_dst_height; } else { dst_width = (float) stex->tex_width; dst_height = (float) stex->tex_height; } inverted_viewport = (graphene_rect_t) { .origin.x = -(viewport.origin.x * (dst_width / viewport.size.width)), .origin.y = -(viewport.origin.y * (dst_height / viewport.size.height)), .size.width = dst_width, .size.height = dst_height }; inverted_dst_width = ceilf (viewport.size.width); inverted_dst_height = ceilf (viewport.size.height); meta_rectangle_crop_and_scale (clip, &inverted_viewport, inverted_dst_width, inverted_dst_height, clip); } meta_texture_tower_update_area (stex->paint_tower, x, y, width, height); stex->prev_invalidation = stex->last_invalidation; stex->last_invalidation = g_get_monotonic_time (); if (stex->prev_invalidation) { gint64 interval = stex->last_invalidation - stex->prev_invalidation; gboolean fast_update = interval < MIN_MIPMAP_AGE_USEC; if (!fast_update) stex->fast_updates = 0; else if (stex->fast_updates < MIN_FAST_UPDATES_BEFORE_UNMIPMAP) stex->fast_updates++; } return TRUE; } /** * meta_shaped_texture_set_texture: * @stex: The #MetaShapedTexture * @pixmap: The #CoglTexture to display */ void meta_shaped_texture_set_texture (MetaShapedTexture *stex, CoglTexture *texture) { g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); if (stex->texture == texture) return; set_cogl_texture (stex, texture); } /** * meta_shaped_texture_set_is_y_inverted: (skip) */ void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex, gboolean is_y_inverted) { if (stex->is_y_inverted == is_y_inverted) return; meta_shaped_texture_reset_pipelines (stex); stex->is_y_inverted = is_y_inverted; } /** * meta_shaped_texture_set_snippet: (skip) */ void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, CoglSnippet *snippet) { if (stex->snippet == snippet) return; meta_shaped_texture_reset_pipelines (stex); g_clear_pointer (&stex->snippet, cogl_object_unref); if (snippet) stex->snippet = cogl_object_ref (snippet); } /** * meta_shaped_texture_get_texture: * @stex: The #MetaShapedTexture * * Returns: (transfer none): the unshaped texture */ CoglTexture * meta_shaped_texture_get_texture (MetaShapedTexture *stex) { g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL); return COGL_TEXTURE (stex->texture); } /** * meta_shaped_texture_set_opaque_region: * @stex: a #MetaShapedTexture * @opaque_region: (transfer full): the region of the texture that * can have blending turned off. * * As most windows have a large portion that does not require blending, * we can easily turn off blending if we know the areas that do not * require blending. This sets the region where we will not blend for * optimization purposes. */ void meta_shaped_texture_set_opaque_region (MetaShapedTexture *stex, cairo_region_t *opaque_region) { g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); if (stex->opaque_region) cairo_region_destroy (stex->opaque_region); if (opaque_region) stex->opaque_region = cairo_region_reference (opaque_region); else stex->opaque_region = NULL; } cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex) { return stex->opaque_region; } gboolean meta_shaped_texture_has_alpha (MetaShapedTexture *stex) { CoglTexture *texture; texture = stex->texture; if (!texture) return TRUE; switch (cogl_texture_get_components (texture)) { case COGL_TEXTURE_COMPONENTS_A: case COGL_TEXTURE_COMPONENTS_RGBA: return TRUE; case COGL_TEXTURE_COMPONENTS_RG: case COGL_TEXTURE_COMPONENTS_RGB: case COGL_TEXTURE_COMPONENTS_DEPTH: return FALSE; } g_warn_if_reached (); return FALSE; } gboolean meta_shaped_texture_is_opaque (MetaShapedTexture *stex) { CoglTexture *texture; cairo_rectangle_int_t opaque_rect; texture = stex->texture; if (!texture) return FALSE; if (!meta_shaped_texture_has_alpha (stex)) return TRUE; if (!stex->opaque_region) return FALSE; if (cairo_region_num_rectangles (stex->opaque_region) != 1) return FALSE; cairo_region_get_extents (stex->opaque_region, &opaque_rect); ensure_size_valid (stex); return meta_rectangle_equal (&opaque_rect, &(MetaRectangle) { .width = stex->dst_width, .height = stex->dst_height }); } void meta_shaped_texture_set_transform (MetaShapedTexture *stex, MetaMonitorTransform transform) { if (stex->transform == transform) return; stex->transform = transform; meta_shaped_texture_reset_pipelines (stex); invalidate_size (stex); } void meta_shaped_texture_set_viewport_src_rect (MetaShapedTexture *stex, graphene_rect_t *src_rect) { if (!stex->has_viewport_src_rect || stex->viewport_src_rect.origin.x != src_rect->origin.x || stex->viewport_src_rect.origin.y != src_rect->origin.y || stex->viewport_src_rect.size.width != src_rect->size.width || stex->viewport_src_rect.size.height != src_rect->size.height) { stex->has_viewport_src_rect = TRUE; stex->viewport_src_rect = *src_rect; meta_shaped_texture_reset_pipelines (stex); invalidate_size (stex); } } void meta_shaped_texture_reset_viewport_src_rect (MetaShapedTexture *stex) { if (!stex->has_viewport_src_rect) return; stex->has_viewport_src_rect = FALSE; meta_shaped_texture_reset_pipelines (stex); invalidate_size (stex); } void meta_shaped_texture_set_viewport_dst_size (MetaShapedTexture *stex, int dst_width, int dst_height) { if (!stex->has_viewport_dst_size || stex->viewport_dst_width != dst_width || stex->viewport_dst_height != dst_height) { stex->has_viewport_dst_size = TRUE; stex->viewport_dst_width = dst_width; stex->viewport_dst_height = dst_height; invalidate_size (stex); } } void meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex) { if (!stex->has_viewport_dst_size) return; stex->has_viewport_dst_size = FALSE; invalidate_size (stex); } static gboolean should_get_via_offscreen (MetaShapedTexture *stex) { if (!cogl_texture_is_get_data_supported (stex->texture)) return TRUE; if (stex->has_viewport_src_rect || stex->has_viewport_dst_size) return TRUE; switch (stex->transform) { case META_MONITOR_TRANSFORM_90: case META_MONITOR_TRANSFORM_180: case META_MONITOR_TRANSFORM_270: case META_MONITOR_TRANSFORM_FLIPPED: case META_MONITOR_TRANSFORM_FLIPPED_90: case META_MONITOR_TRANSFORM_FLIPPED_180: case META_MONITOR_TRANSFORM_FLIPPED_270: return TRUE; case META_MONITOR_TRANSFORM_NORMAL: break; } return FALSE; } static cairo_surface_t * get_image_via_offscreen (MetaShapedTexture *stex, cairo_rectangle_int_t *clip, int image_width, int image_height) { g_autoptr (ClutterPaintNode) root_node = NULL; ClutterBackend *clutter_backend = clutter_get_default_backend (); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); CoglTexture *image_texture; GError *error = NULL; CoglOffscreen *offscreen; CoglFramebuffer *fb; CoglMatrix projection_matrix; cairo_rectangle_int_t fallback_clip; ClutterColor clear_color; ClutterPaintContext *paint_context; cairo_surface_t *surface; if (!clip) { fallback_clip = (cairo_rectangle_int_t) { .width = image_width, .height = image_height, }; clip = &fallback_clip; } image_texture = COGL_TEXTURE (cogl_texture_2d_new_with_size (cogl_context, image_width, image_height)); cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (image_texture), FALSE); if (!cogl_texture_allocate (COGL_TEXTURE (image_texture), &error)) { g_error_free (error); cogl_object_unref (image_texture); return FALSE; } offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (image_texture)); fb = COGL_FRAMEBUFFER (offscreen); cogl_object_unref (image_texture); if (!cogl_framebuffer_allocate (fb, &error)) { g_error_free (error); cogl_object_unref (fb); return FALSE; } cogl_framebuffer_push_matrix (fb); cogl_matrix_init_identity (&projection_matrix); cogl_matrix_scale (&projection_matrix, 1.0 / (image_width / 2.0), -1.0 / (image_height / 2.0), 0); cogl_matrix_translate (&projection_matrix, -(image_width / 2.0), -(image_height / 2.0), 0); cogl_framebuffer_set_projection_matrix (fb, &projection_matrix); clear_color = (ClutterColor) { 0, 0, 0, 0 }; root_node = clutter_root_node_new (fb, &clear_color, COGL_BUFFER_BIT_COLOR); clutter_paint_node_set_static_name (root_node, "MetaShapedTexture.offscreen"); paint_context = clutter_paint_context_new_for_framebuffer (fb); do_paint_content (stex, root_node, paint_context, stex->texture, &(ClutterActorBox) { 0, 0, image_width, image_height, }, 255); clutter_paint_node_paint (root_node, paint_context); clutter_paint_context_destroy (paint_context); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, clip->width, clip->height); cogl_framebuffer_read_pixels (fb, clip->x, clip->y, clip->width, clip->height, CLUTTER_CAIRO_FORMAT_ARGB32, cairo_image_surface_get_data (surface)); cogl_object_unref (fb); cairo_surface_mark_dirty (surface); return surface; } /** * meta_shaped_texture_get_image: * @stex: A #MetaShapedTexture * @clip: (nullable): A clipping rectangle, to help prevent extra processing. * In the case that the clipping rectangle is partially or fully * outside the bounds of the texture, the rectangle will be clipped. * * Flattens the two layers of the shaped texture into one ARGB32 * image by alpha blending the two images, and returns the flattened * image. * * Returns: (nullable) (transfer full): a new cairo surface to be freed with * cairo_surface_destroy(). */ cairo_surface_t * meta_shaped_texture_get_image (MetaShapedTexture *stex, cairo_rectangle_int_t *clip) { cairo_rectangle_int_t *image_clip = NULL; CoglTexture *texture, *mask_texture; cairo_surface_t *surface; g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), NULL); texture = COGL_TEXTURE (stex->texture); if (texture == NULL) return NULL; ensure_size_valid (stex); if (stex->dst_width == 0 || stex->dst_height == 0) return NULL; if (clip != NULL) { cairo_rectangle_int_t dst_rect; image_clip = alloca (sizeof (cairo_rectangle_int_t)); dst_rect = (cairo_rectangle_int_t) { .width = stex->dst_width, .height = stex->dst_height, }; if (!meta_rectangle_intersect (&dst_rect, clip, image_clip)) return NULL; *image_clip = (MetaRectangle) { .x = image_clip->x * stex->buffer_scale, .y = image_clip->y * stex->buffer_scale, .width = image_clip->width * stex->buffer_scale, .height = image_clip->height * stex->buffer_scale, }; } if (should_get_via_offscreen (stex)) { int image_width; int image_height; image_width = stex->dst_width * stex->buffer_scale; image_height = stex->dst_height * stex->buffer_scale; return get_image_via_offscreen (stex, image_clip, image_width, image_height); } if (image_clip) texture = cogl_texture_new_from_sub_texture (texture, image_clip->x, image_clip->y, image_clip->width, image_clip->height); surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, cogl_texture_get_width (texture), cogl_texture_get_height (texture)); cogl_texture_get_data (texture, CLUTTER_CAIRO_FORMAT_ARGB32, cairo_image_surface_get_stride (surface), cairo_image_surface_get_data (surface)); cairo_surface_mark_dirty (surface); if (image_clip) cogl_object_unref (texture); mask_texture = stex->mask_texture; if (mask_texture != NULL) { cairo_t *cr; cairo_surface_t *mask_surface; if (image_clip) mask_texture = cogl_texture_new_from_sub_texture (mask_texture, image_clip->x, image_clip->y, image_clip->width, image_clip->height); mask_surface = cairo_image_surface_create (CAIRO_FORMAT_A8, cogl_texture_get_width (mask_texture), cogl_texture_get_height (mask_texture)); cogl_texture_get_data (mask_texture, COGL_PIXEL_FORMAT_A_8, cairo_image_surface_get_stride (mask_surface), cairo_image_surface_get_data (mask_surface)); cairo_surface_mark_dirty (mask_surface); cr = cairo_create (surface); cairo_set_source_surface (cr, mask_surface, 0, 0); cairo_set_operator (cr, CAIRO_OPERATOR_DEST_IN); cairo_paint (cr); cairo_destroy (cr); cairo_surface_destroy (mask_surface); if (image_clip) cogl_object_unref (mask_texture); } return surface; } void meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex, int fallback_width, int fallback_height) { stex->fallback_width = fallback_width; stex->fallback_height = fallback_height; invalidate_size (stex); } MetaShapedTexture * meta_shaped_texture_new (void) { return g_object_new (META_TYPE_SHAPED_TEXTURE, NULL); } void meta_shaped_texture_set_buffer_scale (MetaShapedTexture *stex, int buffer_scale) { g_return_if_fail (META_IS_SHAPED_TEXTURE (stex)); if (buffer_scale == stex->buffer_scale) return; stex->buffer_scale = buffer_scale; invalidate_size (stex); } int meta_shaped_texture_get_buffer_scale (MetaShapedTexture *stex) { g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 1.0); return stex->buffer_scale; } int meta_shaped_texture_get_width (MetaShapedTexture *stex) { g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); ensure_size_valid (stex); return stex->dst_width; } int meta_shaped_texture_get_height (MetaShapedTexture *stex) { g_return_val_if_fail (META_IS_SHAPED_TEXTURE (stex), 0); ensure_size_valid (stex); return stex->dst_height; } muffin-6.4.1/src/compositor/meta-window-actor-x11.h0000664000175000017500000000347114723361714021055 0ustar fabiofabio/* * Copyright (C) 2018 Endless, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Georges Basile Stavracas Neto */ #ifndef META_WINDOW_ACTOR_X11_H #define META_WINDOW_ACTOR_X11_H #include "compositor/meta-window-actor-private.h" #define META_TYPE_WINDOW_ACTOR_X11 (meta_window_actor_x11_get_type()) G_DECLARE_FINAL_TYPE (MetaWindowActorX11, meta_window_actor_x11, META, WINDOW_ACTOR_X11, MetaWindowActor) void meta_window_actor_x11_process_x11_damage (MetaWindowActorX11 *actor_x11, XDamageNotifyEvent *event); gboolean meta_window_actor_x11_should_unredirect (MetaWindowActorX11 *actor_x11); void meta_window_actor_x11_set_unredirected (MetaWindowActorX11 *actor_x11, gboolean unredirected); void meta_window_actor_x11_update_shape (MetaWindowActorX11 *actor_x11); void meta_window_actor_x11_process_damage (MetaWindowActorX11 *actor_x11, XDamageNotifyEvent *event); #endif /* META_WINDOW_ACTOR_X11_H */ muffin-6.4.1/src/compositor/meta-background-actor.c0000664000175000017500000010647214723361714021256 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2009 Sander Dijkhuis * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Portions adapted from gnome-shell/src/shell-global.c */ /** * SECTION:meta-background-actor * @title: MetaBackgroundActor * @short_description: Actor for painting the root window background * */ /* * The overall model drawing model of this widget is that we have one * texture, or two interpolated textures, possibly with alpha or * margins that let the underlying background show through, blended * over a solid color or a gradient. The result of that combination * can then be affected by a "vignette" that darkens the background * away from a central point (or as a no-GLSL fallback, simply darkens * the background) and by overall opacity. * * As of GNOME 3.14, GNOME is only using a fraction of this when the * user sets the background through the control center - what can be * set is: * * A single image without a border * An animation of images without a border that blend together, * with the blend changing every 4-5 minutes * A solid color with a repeated noise texture blended over it * * This all is pretty easy to do in a fragment shader, except when: * * A) We don't have GLSL - in this case, the operation of * interpolating the two textures and blending the result over the * background can't be expressed with Cogl's fixed-function layer * combining (which is confined to what GL's texture environment * combining can do) So we can only handle the above directly if * there are no margins or alpha. * * B) The image textures are sliced. Texture size limits on older * hardware (pre-965 intel hardware, r300, etc.) is often 2048, * and it would be common to use a texture larger than this for a * background and expect it to be scaled down. Cogl can compensate * for this by breaking the texture up into multiple textures, but * can't multitexture with sliced textures. So we can only handle * the above if there's a single texture. * * However, even when we *can* represent everything in a single pass, * it's not necessarily efficient. If we want to draw a 1024x768 * background, it's pretty inefficient to bilinearly texture from * two 2560x1440 images and mix that. So the drawing model we take * here is that MetaBackground generates a single texture (which * might be a 1x1 texture for a solid color, or a 1x2 texture for a * gradient, or a repeated texture for wallpaper, or a pre-rendered * texture the size of the screen), and we draw with that, possibly * adding the vignette and opacity. */ #include "config.h" #include "compositor/meta-background-actor-private.h" #include "clutter/clutter.h" #include "compositor/clutter-utils.h" #include "compositor/cogl-utils.h" #include "compositor/meta-background-private.h" #include "compositor/meta-cullable.h" #include "meta/display.h" #include "meta/meta-x11-errors.h" enum { PROP_META_DISPLAY = 1, PROP_MONITOR, PROP_BACKGROUND, PROP_GRADIENT, PROP_GRADIENT_HEIGHT, PROP_GRADIENT_MAX_DARKNESS, PROP_VIGNETTE, PROP_VIGNETTE_SHARPNESS, PROP_VIGNETTE_BRIGHTNESS }; typedef enum { CHANGED_BACKGROUND = 1 << 0, CHANGED_EFFECTS = 1 << 2, CHANGED_VIGNETTE_PARAMETERS = 1 << 3, CHANGED_GRADIENT_PARAMETERS = 1 << 4, CHANGED_ALL = 0xFFFF } ChangedFlags; #define GRADIENT_VERTEX_SHADER_DECLARATIONS \ "uniform vec2 scale;\n" \ "varying vec2 position;\n" \ #define GRADIENT_VERTEX_SHADER_CODE \ "position = cogl_tex_coord0_in.xy * scale;\n" \ #define GRADIENT_FRAGMENT_SHADER_DECLARATIONS \ "uniform float gradient_height_perc;\n" \ "uniform float gradient_max_darkness;\n" \ "varying vec2 position;\n" \ #define GRADIENT_FRAGMENT_SHADER_CODE \ "float min_brightness = 1.0 - gradient_max_darkness;\n" \ "float gradient_y_pos = min(position.y, gradient_height_perc) / gradient_height_perc;\n" \ "float pixel_brightness = (1.0 - min_brightness) * gradient_y_pos + min_brightness;\n" \ "cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \ #define VIGNETTE_VERTEX_SHADER_DECLARATIONS \ "uniform vec2 scale;\n" \ "uniform vec2 offset;\n" \ "varying vec2 position;\n" \ #define VIGNETTE_VERTEX_SHADER_CODE \ "position = cogl_tex_coord0_in.xy * scale + offset;\n" \ #define VIGNETTE_SQRT_2 "1.4142" #define VIGNETTE_FRAGMENT_SHADER_DECLARATIONS \ "uniform float vignette_sharpness;\n" \ "varying vec2 position;\n" \ "float rand(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453123); }\n" \ #define VIGNETTE_FRAGMENT_SHADER_CODE \ "float t = " VIGNETTE_SQRT_2 " * length(position);\n" \ "t = min(t, 1.0);\n" \ "float pixel_brightness = 1.0 - t * vignette_sharpness;\n" \ "cogl_color_out.rgb = cogl_color_out.rgb * pixel_brightness;\n" \ "cogl_color_out.rgb += (rand(position) - 0.5) / 255.0;\n" \ typedef struct _MetaBackgroundLayer MetaBackgroundLayer; typedef enum { PIPELINE_VIGNETTE = (1 << 0), PIPELINE_BLEND = (1 << 1), PIPELINE_GRADIENT = (1 << 2), } PipelineFlags; struct _MetaBackgroundActor { ClutterActor parent; MetaDisplay *display; int monitor; MetaBackground *background; gboolean gradient; double gradient_max_darkness; int gradient_height; gboolean vignette; double vignette_brightness; double vignette_sharpness; ChangedFlags changed; CoglPipeline *pipeline; PipelineFlags pipeline_flags; cairo_rectangle_int_t texture_area; gboolean force_bilinear; cairo_region_t *clip_region; cairo_region_t *unobscured_region; }; static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaBackgroundActor, meta_background_actor, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void set_clip_region (MetaBackgroundActor *self, cairo_region_t *clip_region) { g_clear_pointer (&self->clip_region, cairo_region_destroy); if (clip_region) { if (cairo_region_is_empty (clip_region)) self->clip_region = cairo_region_reference (clip_region); else self->clip_region = cairo_region_copy (clip_region); } } static void set_unobscured_region (MetaBackgroundActor *self, cairo_region_t *unobscured_region) { g_clear_pointer (&self->unobscured_region, cairo_region_destroy); if (unobscured_region) { if (cairo_region_is_empty (unobscured_region)) self->unobscured_region = cairo_region_reference (unobscured_region); else self->unobscured_region = cairo_region_copy (unobscured_region); } } static void meta_background_actor_dispose (GObject *object) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); set_clip_region (self, NULL); set_unobscured_region (self, NULL); meta_background_actor_set_background (self, NULL); if (self->pipeline) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } G_OBJECT_CLASS (meta_background_actor_parent_class)->dispose (object); } static void get_preferred_size (MetaBackgroundActor *self, gfloat *width, gfloat *height) { MetaRectangle monitor_geometry; meta_display_get_monitor_geometry (self->display, self->monitor, &monitor_geometry); if (width != NULL) *width = monitor_geometry.width; if (height != NULL) *height = monitor_geometry.height; } static void meta_background_actor_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width_p, gfloat *natural_width_p) { gfloat width; get_preferred_size (META_BACKGROUND_ACTOR (actor), &width, NULL); if (min_width_p) *min_width_p = width; if (natural_width_p) *natural_width_p = width; } static void meta_background_actor_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height_p, gfloat *natural_height_p) { gfloat height; get_preferred_size (META_BACKGROUND_ACTOR (actor), NULL, &height); if (min_height_p) *min_height_p = height; if (natural_height_p) *natural_height_p = height; } static CoglPipeline * make_pipeline (PipelineFlags pipeline_flags) { static CoglPipeline *templates[8]; CoglPipeline **templatep; templatep = &templates[pipeline_flags]; if (*templatep == NULL) { /* Cogl automatically caches pipelines with no eviction policy, * so we need to prevent identical pipelines from getting cached * separately, by reusing the same shader snippets. */ *templatep = COGL_PIPELINE (meta_create_texture_pipeline (NULL)); if ((pipeline_flags & PIPELINE_VIGNETTE) != 0) { static CoglSnippet *vignette_vertex_snippet; static CoglSnippet *vignette_fragment_snippet; if (!vignette_vertex_snippet) vignette_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, VIGNETTE_VERTEX_SHADER_DECLARATIONS, VIGNETTE_VERTEX_SHADER_CODE); cogl_pipeline_add_snippet (*templatep, vignette_vertex_snippet); if (!vignette_fragment_snippet) vignette_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, VIGNETTE_FRAGMENT_SHADER_DECLARATIONS, VIGNETTE_FRAGMENT_SHADER_CODE); cogl_pipeline_add_snippet (*templatep, vignette_fragment_snippet); } if ((pipeline_flags & PIPELINE_GRADIENT) != 0) { static CoglSnippet *gradient_vertex_snippet; static CoglSnippet *gradient_fragment_snippet; if (!gradient_vertex_snippet) gradient_vertex_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_VERTEX, GRADIENT_VERTEX_SHADER_DECLARATIONS, GRADIENT_VERTEX_SHADER_CODE); cogl_pipeline_add_snippet (*templatep, gradient_vertex_snippet); if (!gradient_fragment_snippet) gradient_fragment_snippet = cogl_snippet_new (COGL_SNIPPET_HOOK_FRAGMENT, GRADIENT_FRAGMENT_SHADER_DECLARATIONS, GRADIENT_FRAGMENT_SHADER_CODE); cogl_pipeline_add_snippet (*templatep, gradient_fragment_snippet); } if ((pipeline_flags & PIPELINE_BLEND) == 0) cogl_pipeline_set_blend (*templatep, "RGBA = ADD (SRC_COLOR, 0)", NULL); } return cogl_pipeline_copy (*templatep); } static void setup_pipeline (MetaBackgroundActor *self, ClutterPaintContext *paint_context, cairo_rectangle_int_t *actor_pixel_rect) { PipelineFlags pipeline_flags = 0; guint8 opacity; float color_component; CoglFramebuffer *fb; CoglPipelineFilter min_filter, mag_filter; opacity = clutter_actor_get_paint_opacity (CLUTTER_ACTOR (self)); if (opacity < 255) pipeline_flags |= PIPELINE_BLEND; if (self->vignette && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) pipeline_flags |= PIPELINE_VIGNETTE; if (self->gradient && clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) pipeline_flags |= PIPELINE_GRADIENT; if (self->pipeline && pipeline_flags != self->pipeline_flags) { cogl_object_unref (self->pipeline); self->pipeline = NULL; } if (self->pipeline == NULL) { self->pipeline_flags = pipeline_flags; self->pipeline = make_pipeline (pipeline_flags); self->changed = CHANGED_ALL; } if ((self->changed & CHANGED_BACKGROUND) != 0) { CoglPipelineWrapMode wrap_mode; CoglTexture *texture = meta_background_get_texture (self->background, self->monitor, &self->texture_area, &wrap_mode); self->force_bilinear = texture && (self->texture_area.width != (int)cogl_texture_get_width (texture) || self->texture_area.height != (int)cogl_texture_get_height (texture)); cogl_pipeline_set_layer_texture (self->pipeline, 0, texture); cogl_pipeline_set_layer_wrap_mode (self->pipeline, 0, wrap_mode); self->changed &= ~CHANGED_BACKGROUND; } if ((self->changed & CHANGED_VIGNETTE_PARAMETERS) != 0) { cogl_pipeline_set_uniform_1f (self->pipeline, cogl_pipeline_get_uniform_location (self->pipeline, "vignette_sharpness"), self->vignette_sharpness); self->changed &= ~CHANGED_VIGNETTE_PARAMETERS; } if ((self->changed & CHANGED_GRADIENT_PARAMETERS) != 0) { MetaRectangle monitor_geometry; float gradient_height_perc; meta_display_get_monitor_geometry (self->display, self->monitor, &monitor_geometry); gradient_height_perc = MAX (0.0001, self->gradient_height / (float)monitor_geometry.height); cogl_pipeline_set_uniform_1f (self->pipeline, cogl_pipeline_get_uniform_location (self->pipeline, "gradient_height_perc"), gradient_height_perc); cogl_pipeline_set_uniform_1f (self->pipeline, cogl_pipeline_get_uniform_location (self->pipeline, "gradient_max_darkness"), self->gradient_max_darkness); self->changed &= ~CHANGED_GRADIENT_PARAMETERS; } if (self->vignette) { color_component = self->vignette_brightness * opacity / 255.; if (!clutter_feature_available (CLUTTER_FEATURE_SHADERS_GLSL)) { /* Darken everything to match the average brightness that would * be there if we were drawing the vignette, which is * (1 - (pi/12.) * vignette_sharpness) [exercise for the reader :] */ color_component *= (1 - 0.74 * self->vignette_sharpness); } } else color_component = opacity / 255.; cogl_pipeline_set_color4f (self->pipeline, color_component, color_component, color_component, opacity / 255.); fb = clutter_paint_context_get_framebuffer (paint_context); if (!self->force_bilinear && meta_actor_painting_untransformed (fb, actor_pixel_rect->width, actor_pixel_rect->height, actor_pixel_rect->width, actor_pixel_rect->height, NULL, NULL)) { min_filter = COGL_PIPELINE_FILTER_NEAREST; mag_filter = COGL_PIPELINE_FILTER_NEAREST; } else { min_filter = COGL_PIPELINE_FILTER_LINEAR_MIPMAP_NEAREST; mag_filter = COGL_PIPELINE_FILTER_LINEAR; } cogl_pipeline_set_layer_filters (self->pipeline, 0, min_filter, mag_filter); } static void set_glsl_parameters (MetaBackgroundActor *self, cairo_rectangle_int_t *actor_pixel_rect) { float scale[2]; float offset[2]; /* Compute a scale and offset for transforming texture coordinates to the * coordinate system from [-0.5 to 0.5] across the area of the actor */ scale[0] = self->texture_area.width / (float)actor_pixel_rect->width; scale[1] = self->texture_area.height / (float)actor_pixel_rect->height; offset[0] = self->texture_area.x / (float)actor_pixel_rect->width - 0.5; offset[1] = self->texture_area.y / (float)actor_pixel_rect->height - 0.5; cogl_pipeline_set_uniform_float (self->pipeline, cogl_pipeline_get_uniform_location (self->pipeline, "scale"), 2, 1, scale); cogl_pipeline_set_uniform_float (self->pipeline, cogl_pipeline_get_uniform_location (self->pipeline, "offset"), 2, 1, offset); } static void paint_clipped_rectangle (CoglFramebuffer *fb, CoglPipeline *pipeline, cairo_rectangle_int_t *rect, cairo_rectangle_int_t *texture_area) { float x1, y1, x2, y2; float tx1, ty1, tx2, ty2; x1 = rect->x; y1 = rect->y; x2 = rect->x + rect->width; y2 = rect->y + rect->height; tx1 = (x1 - texture_area->x) / texture_area->width; ty1 = (y1 - texture_area->y) / texture_area->height; tx2 = (x2 - texture_area->x) / texture_area->width; ty2 = (y2 - texture_area->y) / texture_area->height; cogl_framebuffer_draw_textured_rectangle (fb, pipeline, x1, y1, x2, y2, tx1, ty1, tx2, ty2); } static gboolean meta_background_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { return clutter_paint_volume_set_from_allocation (volume, actor); } static void meta_background_actor_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (actor); ClutterActorBox actor_box; cairo_rectangle_int_t actor_pixel_rect; CoglFramebuffer *fb; cairo_region_t *region; int i, n_rects; if ((self->clip_region && cairo_region_is_empty (self->clip_region))) return; clutter_actor_get_content_box (actor, &actor_box); actor_pixel_rect.x = actor_box.x1; actor_pixel_rect.y = actor_box.y1; actor_pixel_rect.width = actor_box.x2 - actor_box.x1; actor_pixel_rect.height = actor_box.y2 - actor_box.y1; setup_pipeline (self, paint_context, &actor_pixel_rect); set_glsl_parameters (self, &actor_pixel_rect); /* Limit to how many separate rectangles we'll draw; beyond this just * fall back and draw the whole thing */ #define MAX_RECTS 64 fb = clutter_paint_context_get_framebuffer (paint_context); /* Now figure out what to actually paint. */ if (self->clip_region) { region = cairo_region_copy (self->clip_region); cairo_region_intersect_rectangle (region, &actor_pixel_rect); } else { region = cairo_region_create_rectangle (&actor_pixel_rect); } if (self->unobscured_region) cairo_region_intersect (region, self->unobscured_region); if (cairo_region_is_empty (region)) { cairo_region_destroy (region); return; } n_rects = cairo_region_num_rectangles (region); if (n_rects <= MAX_RECTS) { for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); paint_clipped_rectangle (fb, self->pipeline, &rect, &self->texture_area); } } else { cairo_rectangle_int_t rect; cairo_region_get_extents (region, &rect); paint_clipped_rectangle (fb, self->pipeline, &rect, &self->texture_area); } cairo_region_destroy (region); } static void meta_background_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); switch (prop_id) { case PROP_META_DISPLAY: self->display = g_value_get_object (value); break; case PROP_MONITOR: meta_background_actor_set_monitor (self, g_value_get_int (value)); break; case PROP_BACKGROUND: meta_background_actor_set_background (self, g_value_get_object (value)); break; case PROP_GRADIENT: meta_background_actor_set_gradient (self, g_value_get_boolean (value), self->gradient_height, self->gradient_max_darkness); break; case PROP_GRADIENT_HEIGHT: meta_background_actor_set_gradient (self, self->gradient, g_value_get_int (value), self->gradient_max_darkness); break; case PROP_GRADIENT_MAX_DARKNESS: meta_background_actor_set_gradient (self, self->gradient, self->gradient_height, g_value_get_double (value)); break; case PROP_VIGNETTE: meta_background_actor_set_vignette (self, g_value_get_boolean (value), self->vignette_brightness, self->vignette_sharpness); break; case PROP_VIGNETTE_SHARPNESS: meta_background_actor_set_vignette (self, self->vignette, self->vignette_brightness, g_value_get_double (value)); break; case PROP_VIGNETTE_BRIGHTNESS: meta_background_actor_set_vignette (self, self->vignette, g_value_get_double (value), self->vignette_sharpness); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_background_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (object); switch (prop_id) { case PROP_META_DISPLAY: g_value_set_object (value, self->display); break; case PROP_MONITOR: g_value_set_int (value, self->monitor); break; case PROP_BACKGROUND: g_value_set_object (value, self->background); break; case PROP_GRADIENT: g_value_set_boolean (value, self->gradient); break; case PROP_GRADIENT_HEIGHT: g_value_set_int (value, self->gradient_height); break; case PROP_GRADIENT_MAX_DARKNESS: g_value_set_double (value, self->gradient_max_darkness); break; case PROP_VIGNETTE: g_value_set_boolean (value, self->vignette); break; case PROP_VIGNETTE_BRIGHTNESS: g_value_set_double (value, self->vignette_brightness); break; case PROP_VIGNETTE_SHARPNESS: g_value_set_double (value, self->vignette_sharpness); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_background_actor_class_init (MetaBackgroundActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); GParamSpec *param_spec; object_class->dispose = meta_background_actor_dispose; object_class->set_property = meta_background_actor_set_property; object_class->get_property = meta_background_actor_get_property; actor_class->get_preferred_width = meta_background_actor_get_preferred_width; actor_class->get_preferred_height = meta_background_actor_get_preferred_height; actor_class->get_paint_volume = meta_background_actor_get_paint_volume; actor_class->paint = meta_background_actor_paint; param_spec = g_param_spec_object ("meta-display", "MetaDisplay", "MetaDisplay", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_META_DISPLAY, param_spec); param_spec = g_param_spec_int ("monitor", "monitor", "monitor", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_MONITOR, param_spec); param_spec = g_param_spec_object ("background", "Background", "MetaBackground object holding background parameters", META_TYPE_BACKGROUND, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_BACKGROUND, param_spec); param_spec = g_param_spec_boolean ("gradient", "Gradient", "Whether gradient effect is enabled", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_GRADIENT, param_spec); param_spec = g_param_spec_int ("gradient-height", "Gradient Height", "Height of gradient effect", 0, G_MAXINT, 0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_GRADIENT_HEIGHT, param_spec); param_spec = g_param_spec_double ("gradient-max-darkness", "Gradient Max Darkness", "How dark is the gradient initially", 0.0, 1.0, 0.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_GRADIENT_MAX_DARKNESS, param_spec); param_spec = g_param_spec_boolean ("vignette", "Vignette", "Whether vignette effect is enabled", FALSE, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VIGNETTE, param_spec); param_spec = g_param_spec_double ("brightness", "Vignette Brightness", "Brightness of vignette effect", 0.0, 1.0, 1.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VIGNETTE_BRIGHTNESS, param_spec); param_spec = g_param_spec_double ("vignette-sharpness", "Vignette Sharpness", "Sharpness of vignette effect", 0.0, G_MAXDOUBLE, 0.0, G_PARAM_READWRITE); g_object_class_install_property (object_class, PROP_VIGNETTE_SHARPNESS, param_spec); } static void meta_background_actor_init (MetaBackgroundActor *self) { self->gradient = FALSE; self->gradient_height = 0; self->gradient_max_darkness = 0.0; self->vignette = FALSE; self->vignette_brightness = 1.0; self->vignette_sharpness = 0.0; } /** * meta_background_actor_new: * @monitor: Index of the monitor for which to draw the background * * Creates a new actor to draw the background for the given monitor. * * Return value: the newly created background actor */ ClutterActor * meta_background_actor_new (MetaDisplay *display, int monitor) { MetaBackgroundActor *self; self = g_object_new (META_TYPE_BACKGROUND_ACTOR, "meta-display", display, "monitor", monitor, NULL); return CLUTTER_ACTOR (self); } static void meta_background_actor_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable); set_unobscured_region (self, unobscured_region); set_clip_region (self, clip_region); } static void meta_background_actor_reset_culling (MetaCullable *cullable) { MetaBackgroundActor *self = META_BACKGROUND_ACTOR (cullable); set_unobscured_region (self, NULL); set_clip_region (self, NULL); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_background_actor_cull_out; iface->reset_culling = meta_background_actor_reset_culling; } /** * meta_background_actor_get_clip_region: * @self: a #MetaBackgroundActor * * Return value (transfer none): a #cairo_region_t that represents the part of * the background not obscured by other #MetaBackgroundActor or * #MetaWindowActor objects. */ cairo_region_t * meta_background_actor_get_clip_region (MetaBackgroundActor *self) { return self->clip_region; } static void invalidate_pipeline (MetaBackgroundActor *self, ChangedFlags changed) { self->changed |= changed; } static void on_background_changed (MetaBackground *background, MetaBackgroundActor *self) { invalidate_pipeline (self, CHANGED_BACKGROUND); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } void meta_background_actor_set_background (MetaBackgroundActor *self, MetaBackground *background) { g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); g_return_if_fail (background == NULL || META_IS_BACKGROUND (background)); if (background == self->background) return; if (self->background) { g_signal_handlers_disconnect_by_func (self->background, (gpointer)on_background_changed, self); g_object_unref (self->background); self->background = NULL; } if (background) { self->background = g_object_ref (background); g_signal_connect (self->background, "changed", G_CALLBACK (on_background_changed), self); } invalidate_pipeline (self, CHANGED_BACKGROUND); clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } void meta_background_actor_set_gradient (MetaBackgroundActor *self, gboolean enabled, int height, double max_darkness) { gboolean changed = FALSE; g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); g_return_if_fail (height >= 0); g_return_if_fail (max_darkness >= 0. && max_darkness <= 1.); enabled = enabled != FALSE && height != 0; if (enabled != self->gradient) { self->gradient = enabled; invalidate_pipeline (self, CHANGED_EFFECTS); changed = TRUE; } if (height != self->gradient_height || max_darkness != self->gradient_max_darkness) { self->gradient_height = height; self->gradient_max_darkness = max_darkness; invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS); changed = TRUE; } if (changed) clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } void meta_background_actor_set_monitor (MetaBackgroundActor *self, int monitor) { MetaRectangle old_monitor_geometry; MetaRectangle new_monitor_geometry; MetaDisplay *display = self->display; if(self->monitor == monitor) return; meta_display_get_monitor_geometry (display, self->monitor, &old_monitor_geometry); meta_display_get_monitor_geometry (display, monitor, &new_monitor_geometry); if(old_monitor_geometry.height != new_monitor_geometry.height) invalidate_pipeline (self, CHANGED_GRADIENT_PARAMETERS); self->monitor = monitor; } void meta_background_actor_set_vignette (MetaBackgroundActor *self, gboolean enabled, double brightness, double sharpness) { gboolean changed = FALSE; g_return_if_fail (META_IS_BACKGROUND_ACTOR (self)); g_return_if_fail (brightness >= 0. && brightness <= 1.); g_return_if_fail (sharpness >= 0.); enabled = enabled != FALSE; if (enabled != self->vignette) { self->vignette = enabled; invalidate_pipeline (self, CHANGED_EFFECTS); changed = TRUE; } if (brightness != self->vignette_brightness || sharpness != self->vignette_sharpness) { self->vignette_brightness = brightness; self->vignette_sharpness = sharpness; invalidate_pipeline (self, CHANGED_VIGNETTE_PARAMETERS); changed = TRUE; } if (changed) clutter_actor_queue_redraw (CLUTTER_ACTOR (self)); } muffin-6.4.1/src/compositor/compositor.c0000664000175000017500000015347214723361714017305 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:compositor * @Title: MetaCompositor * @Short_Description: Compositor API * * At a high-level, a window is not-visible or visible. When a * window is added (with meta_compositor_add_window()) it is not visible. * meta_compositor_show_window() indicates a transition from not-visible to * visible. Some of the reasons for this: * * - Window newly created * - Window is unminimized * - Window is moved to the current desktop * - Window was made sticky * * meta_compositor_hide_window() indicates that the window has transitioned from * visible to not-visible. Some reasons include: * * - Window was destroyed * - Window is minimized * - Window is moved to a different desktop * - Window no longer sticky. * * Note that combinations are possible - a window might have first * been minimized and then moved to a different desktop. The 'effect' parameter * to meta_compositor_show_window() and meta_compositor_hide_window() is a hint * as to the appropriate effect to show the user and should not * be considered to be indicative of a state change. * * When the active workspace is changed, meta_compositor_switch_workspace() is * called first, then meta_compositor_show_window() and * meta_compositor_hide_window() are called individually for each window * affected, with an effect of META_COMP_EFFECT_NONE. * If hiding windows will affect the switch workspace animation, the * compositor needs to delay hiding the windows until the switch * workspace animation completes. * * # Containers # * * There's two containers in the stage that are used to place window actors, here * are listed in the order in which they are painted: * * - window group, accessible with meta_get_window_group_for_display() * - top window group, accessible with meta_get_top_window_group_for_display() * * Mutter will place actors representing windows in the window group, except for * override-redirect windows (ie. popups and menus) which will be placed in the * top window group. */ #include "config.h" #include "compositor/compositor-private.h" #include #include "backends/meta-dnd-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-event-x11.h" #include "backends/x11/meta-stage-x11.h" #include "clutter/clutter-mutter.h" #include "cogl/cogl.h" #include "compositor/meta-window-actor-x11.h" #include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-group-private.h" #include "core/display-private.h" #include "core/frame.h" #include "core/util-private.h" #include "core/window-private.h" #include "core/meta-workspace-manager-private.h" #include "meta/compositor-mutter.h" #include "meta/main.h" #include "meta/meta-backend.h" #include "meta/meta-background-actor.h" #include "meta/meta-background-group.h" #include "meta/meta-shadow-factory.h" #include "meta/meta-x11-errors.h" #include "meta/meta-x11-background-actor.h" #include "meta/prefs.h" #include "meta/window.h" #include "x11/meta-x11-display-private.h" #ifdef HAVE_WAYLAND #include "compositor/meta-window-actor-wayland.h" #include "wayland/meta-wayland-private.h" #endif enum { PROP_0, PROP_DISPLAY, N_PROPS }; static GParamSpec *obj_props[N_PROPS] = { NULL, }; typedef struct _MetaCompositorPrivate { GObject parent; MetaDisplay *display; guint pre_paint_func_id; guint post_paint_func_id; gulong stage_presented_id; gulong stage_after_paint_id; ClutterActor *stage; ClutterActor *window_group; ClutterActor *top_window_group; ClutterActor *feedback_group; ClutterActor *bottom_window_group; ClutterActor *background_actor; ClutterActor *desklet_container; GList *windows; CoglContext *context; MetaWindowActor *top_window_actor; gulong top_window_actor_destroy_id; int disable_unredirect_count; int switch_workspace_in_progress; MetaPluginManager *plugin_mgr; } MetaCompositorPrivate; G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (MetaCompositor, meta_compositor, G_TYPE_OBJECT) static void on_presented (ClutterStage *stage, CoglFrameEvent event, ClutterFrameInfo *frame_info, MetaCompositor *compositor); static void on_top_window_actor_destroyed (MetaWindowActor *window_actor, MetaCompositor *compositor); static gboolean is_modal (MetaDisplay *display) { return display->event_route == META_EVENT_ROUTE_COMPOSITOR_GRAB; } static void sync_actor_stacking (MetaCompositor *compositor); static void meta_finish_workspace_switch (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *l; /* Finish hiding and showing actors for the new workspace */ for (l = priv->windows; l; l = l->next) meta_window_actor_sync_visibility (l->data); /* Fix up stacking order. */ sync_actor_stacking (compositor); } void meta_switch_workspace_completed (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); /* FIXME -- must redo stacking order */ priv->switch_workspace_in_progress--; if (priv->switch_workspace_in_progress < 0) { g_warning ("Error in workspace_switch accounting!"); priv->switch_workspace_in_progress = 0; } if (!priv->switch_workspace_in_progress) meta_finish_workspace_switch (compositor); } void meta_compositor_destroy (MetaCompositor *compositor) { g_object_run_dispose (G_OBJECT (compositor)); g_object_unref (compositor); } /* compat helper */ static MetaCompositor * get_compositor_for_display (MetaDisplay *display) { return display->compositor; } /** * meta_get_stage_for_display: * @display: a #MetaDisplay * * Returns: (transfer none): The #ClutterStage for the display */ ClutterActor * meta_get_stage_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->stage; } /** * meta_get_window_group_for_display: * @display: a #MetaDisplay * * Returns: (transfer none): The window group corresponding to @display */ ClutterActor * meta_get_window_group_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->window_group; } /** * meta_get_top_window_group_for_display: * @display: a #MetaDisplay * * Returns: (transfer none): The top window group corresponding to @display */ ClutterActor * meta_get_top_window_group_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->top_window_group; } /** * meta_get_feedback_group_for_display: * @display: a #MetaDisplay * * Returns: (transfer none): The feedback group corresponding to @display */ ClutterActor * meta_get_feedback_group_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->feedback_group; } /** * meta_get_bottom_window_group_for_display: * @display: a #MetaDisplay * * Returns: (transfer none): The bottom window group corresponding to @display */ ClutterActor * meta_get_bottom_window_group_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->bottom_window_group; } /** * meta_get_window_actors: * @display: a #MetaDisplay * * Returns: (transfer none) (element-type Clutter.Actor): The set of #MetaWindowActor on @display */ GList * meta_get_window_actors (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; g_return_val_if_fail (display, NULL); compositor = get_compositor_for_display (display); g_return_val_if_fail (compositor, NULL); priv = meta_compositor_get_instance_private (compositor); return priv->windows; } void meta_focus_stage_window (MetaDisplay *display, guint32 timestamp) { ClutterStage *stage; Window window; stage = CLUTTER_STAGE (meta_get_stage_for_display (display)); if (!stage) return; window = meta_x11_get_stage_window (stage); if (window == None) return; meta_x11_display_set_input_focus_xwindow (display->x11_display, window, timestamp); } gboolean meta_stage_is_focused (MetaDisplay *display) { ClutterStage *stage; Window window; if (meta_is_wayland_compositor ()) return TRUE; stage = CLUTTER_STAGE (meta_get_stage_for_display (display)); if (!stage) return FALSE; window = meta_x11_get_stage_window (stage); if (window == None) return FALSE; return (display->x11_display->focus_xwindow == window); } static gboolean grab_devices (MetaModalOptions options, guint32 timestamp) { MetaBackend *backend = META_BACKEND (meta_get_backend ()); gboolean pointer_grabbed = FALSE; gboolean keyboard_grabbed = FALSE; if ((options & META_MODAL_POINTER_ALREADY_GRABBED) == 0) { if (!meta_backend_grab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp)) goto fail; pointer_grabbed = TRUE; } if ((options & META_MODAL_KEYBOARD_ALREADY_GRABBED) == 0) { if (!meta_backend_grab_device (backend, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp)) goto fail; keyboard_grabbed = TRUE; } return TRUE; fail: if (pointer_grabbed) meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp); if (keyboard_grabbed) meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp); return FALSE; } gboolean meta_begin_modal_for_plugin (MetaCompositor *compositor, MetaPlugin *plugin, MetaModalOptions options, guint32 timestamp) { /* To some extent this duplicates code in meta_display_begin_grab_op(), but there * are significant differences in how we handle grabs that make it difficult to * merge the two. */ MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaDisplay *display = priv->display; #ifdef HAVE_WAYLAND if (display->grab_op == META_GRAB_OP_WAYLAND_POPUP) { MetaWaylandSeat *seat = meta_wayland_compositor_get_default ()->seat; meta_wayland_pointer_end_popup_grab (seat->pointer); } #endif if (is_modal (display) || display->grab_op != META_GRAB_OP_NONE) return FALSE; if (display->x11_display) { /* XXX: why is this needed? */ XIUngrabDevice (display->x11_display->xdisplay, META_VIRTUAL_CORE_POINTER_ID, timestamp); XSync (display->x11_display->xdisplay, False); } if (!grab_devices (options, timestamp)) return FALSE; display->grab_op = META_GRAB_OP_COMPOSITOR; display->event_route = META_EVENT_ROUTE_COMPOSITOR_GRAB; display->grab_window = NULL; display->grab_have_pointer = TRUE; display->grab_have_keyboard = TRUE; g_signal_emit_by_name (display, "grab-op-begin", meta_plugin_get_display (plugin), display->grab_window, display->grab_op); if (meta_is_wayland_compositor ()) { meta_display_sync_wayland_input_focus (display); meta_display_cancel_touch (display); #ifdef HAVE_WAYLAND meta_dnd_wayland_handle_begin_modal (compositor); #endif } return TRUE; } void meta_end_modal_for_plugin (MetaCompositor *compositor, MetaPlugin *plugin, guint32 timestamp) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaDisplay *display = priv->display; MetaBackend *backend = meta_get_backend (); MetaWindow *grab_window = display->grab_window; MetaGrabOp grab_op = display->grab_op; g_return_if_fail (is_modal (display)); display->grab_op = META_GRAB_OP_NONE; display->event_route = META_EVENT_ROUTE_NORMAL; display->grab_window = NULL; display->grab_have_pointer = FALSE; display->grab_have_keyboard = FALSE; meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_POINTER_ID, timestamp); meta_backend_ungrab_device (backend, META_VIRTUAL_CORE_KEYBOARD_ID, timestamp); #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) { meta_dnd_wayland_handle_end_modal (compositor); meta_display_sync_wayland_input_focus (display); } #endif g_signal_emit_by_name (display, "grab-op-end", meta_plugin_get_display (plugin), grab_window, grab_op); } static void after_stage_paint (ClutterStage *stage, gpointer data) { MetaCompositor *compositor = data; MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *l; for (l = priv->windows; l; l = l->next) meta_window_actor_post_paint (l->data); } static void redirect_windows (MetaX11Display *x11_display) { Display *xdisplay = meta_x11_display_get_xdisplay (x11_display); Window xroot = meta_x11_display_get_xroot (x11_display); int screen_number = meta_x11_display_get_screen_number (x11_display); guint n_retries; guint max_retries; if (meta_get_replace_current_wm ()) max_retries = 5; else max_retries = 1; n_retries = 0; /* Some compositors (like old versions of Mutter) might not properly unredirect * subwindows before destroying the WM selection window; so we wait a while * for such a compositor to exit before giving up. */ while (TRUE) { meta_x11_error_trap_push (x11_display); XCompositeRedirectSubwindows (xdisplay, xroot, CompositeRedirectManual); XSync (xdisplay, FALSE); if (!meta_x11_error_trap_pop_with_return (x11_display)) break; if (n_retries == max_retries) { /* This probably means that a non-WM compositor like xcompmgr is running; * we have no way to get it to exit */ meta_fatal (_("Another compositing manager is already running on screen %i on display “%s”."), screen_number, x11_display->name); } n_retries++; g_usleep (G_USEC_PER_SEC); } } void meta_compositor_redirect_x11_windows (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaDisplay *display = priv->display; if (display->x11_display) redirect_windows (display->x11_display); } void meta_compositor_manage (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaDisplay *display = priv->display; MetaBackend *backend = meta_get_backend (); priv->stage = meta_backend_get_stage (backend); priv->stage_presented_id = g_signal_connect (priv->stage, "presented", G_CALLBACK (on_presented), compositor); /* We use connect_after() here to accomodate code in GNOME Shell that, * when benchmarking drawing performance, connects to ::after-paint * and calls glFinish(). The timing information from that will be * more accurate if we hold off until that completes before we signal * apps to begin drawing the next frame. If there are no other * connections to ::after-paint, connect() vs. connect_after() doesn't * matter. */ priv->stage_after_paint_id = g_signal_connect_after (priv->stage, "after-paint", G_CALLBACK (after_stage_paint), compositor); clutter_stage_set_sync_delay (CLUTTER_STAGE (priv->stage), META_SYNC_DELAY); priv->window_group = meta_window_group_new (display); priv->top_window_group = meta_window_group_new (display); priv->bottom_window_group = meta_window_group_new (display); priv->feedback_group = meta_window_group_new (display); if (!meta_is_wayland_compositor ()) { priv->background_actor = meta_x11_background_actor_new_for_display (display); clutter_actor_add_child (priv->window_group, priv->background_actor); } clutter_actor_add_child (priv->window_group, priv->bottom_window_group); // This needs to remain stacked just above the background actor in the window group. // So sync_actor_stacking() has to be able to reference it. The deskletManager // will take this and finish setting it up. priv->desklet_container = clutter_actor_new (); clutter_actor_add_child (priv->window_group, priv->desklet_container); clutter_actor_add_child (priv->stage, priv->window_group); clutter_actor_add_child (priv->stage, priv->top_window_group); clutter_actor_add_child (priv->stage, priv->feedback_group); META_COMPOSITOR_GET_CLASS (compositor)->manage (compositor); priv->plugin_mgr = meta_plugin_manager_new (compositor); clutter_actor_show (priv->stage); } void meta_compositor_unmanage (MetaCompositor *compositor) { META_COMPOSITOR_GET_CLASS (compositor)->unmanage (compositor); } static void ensure_tooltip_visible (MetaWindow *window) { const MetaLogicalMonitor *monitor; MetaRectangle work_area, win_rect; gint new_x, new_y; /* Why this is here: * As of gtk 3.24, tooltips for GtkStatusIcons began displaying their tool tip * off the screen in certain situations. * * See: https://github.com/GNOME/gtk/commit/14d22cb3233e * * If the status icon is too small relative to its panel (which has been assigned * as a strut to muffin), tooltip positioning fails both tests in gdkwindowimpl.c * (maybe_flip_position()) skipping repositioning of the tooltip inside the workarea. * This only occurs on bottom panels, and only begins happening when the status icon * becomes 10px or more smaller than the panel it's *centered* on. * * Since the calculations are based upon the monitor's workarea and the status icon * plug window's size, there's no way to compensate for or fool gtk into displaying it * correctly. So here, we do our own check and adjustment if a part of the tooltip * window falls outside the current monitor's work area. This is also useful since * muffin knows *exactly* the work area for each monitor, whereas gtk only has * _NET_WORKAREA to go by, which only keeps track of (primary monitor * n_workspaces), * so an odd monitor layout would trip this up anyhow. * * This may cause regressions - see: https://github.com/linuxmint/muffin/commit/050038690, * but without being able to reproduce the issue mentioned there, we'll just have to address * it if it appears again as a result of this change. */ monitor = window->monitor; if (!monitor) return; meta_workspace_get_work_area_for_monitor (meta_workspace_manager_get_active_workspace (window->display->workspace_manager), monitor->number, &work_area); meta_window_get_buffer_rect (window, &win_rect); new_x = win_rect.x; new_y = win_rect.y; if (win_rect.y < work_area.y) { new_y = work_area.y; } else if (win_rect.y + win_rect.height > work_area.y + work_area.height) { new_y = (work_area.y + work_area.height - win_rect.height); } if (win_rect.x < work_area.x) { new_x = work_area.x; } else if (win_rect.x + win_rect.width > work_area.x + work_area.width) { new_x = (work_area.x + work_area.width - win_rect.width); } if (new_x != win_rect.x || new_y != win_rect.y) { meta_window_move_frame (window, FALSE, new_x, new_y); } } void meta_compositor_add_window (MetaCompositor *compositor, MetaWindow *window) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaWindowActor *window_actor; ClutterActor *window_group; GType window_actor_type = G_TYPE_INVALID; switch (window->client_type) { case META_WINDOW_CLIENT_TYPE_X11: window_actor_type = META_TYPE_WINDOW_ACTOR_X11; break; #ifdef HAVE_WAYLAND case META_WINDOW_CLIENT_TYPE_WAYLAND: window_actor_type = META_TYPE_WINDOW_ACTOR_WAYLAND; break; #endif default: g_return_if_reached (); } window_actor = g_object_new (window_actor_type, "meta-window", window, "show-on-set-parent", FALSE, NULL); if (window->layer == META_LAYER_OVERRIDE_REDIRECT) { if (window->type == META_WINDOW_TOOLTIP) { ensure_tooltip_visible (window); } window_group = priv->top_window_group; } else if (window->type == META_WINDOW_DESKTOP) window_group = priv->bottom_window_group; else window_group = priv->window_group; clutter_actor_add_child (window_group, CLUTTER_ACTOR (window_actor)); /* Initial position in the stack is arbitrary; stacking will be synced * before we first paint. */ priv->windows = g_list_append (priv->windows, window_actor); sync_actor_stacking (compositor); } static void meta_compositor_real_remove_window (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_queue_destroy (window_actor); } void meta_compositor_remove_window (MetaCompositor *compositor, MetaWindow *window) { META_COMPOSITOR_GET_CLASS (compositor)->remove_window (compositor, window); } void meta_compositor_remove_window_actor (MetaCompositor *compositor, MetaWindowActor *window_actor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); priv->windows = g_list_remove (priv->windows, window_actor); } void meta_compositor_sync_updates_frozen (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_sync_updates_frozen (window_actor); } void meta_compositor_queue_frame_drawn (MetaCompositor *compositor, MetaWindow *window, gboolean no_delay_frame) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_queue_frame_drawn (window_actor, no_delay_frame); } void meta_compositor_window_shape_changed (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor; window_actor = meta_window_actor_from_window (window); if (!window_actor) return; meta_window_actor_x11_update_shape (META_WINDOW_ACTOR_X11 (window_actor)); } void meta_compositor_window_opacity_changed (MetaCompositor *compositor, MetaWindow *window) { MetaWindowActor *window_actor; window_actor = meta_window_actor_from_window (window); if (!window_actor) return; meta_window_actor_update_opacity (window_actor); } gboolean meta_compositor_filter_keybinding (MetaCompositor *compositor, MetaKeyBinding *binding) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return meta_plugin_manager_filter_keybinding (priv->plugin_mgr, binding); } void meta_compositor_show_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_show (window_actor, effect); } void meta_compositor_hide_window (MetaCompositor *compositor, MetaWindow *window, MetaCompEffect effect) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_hide (window_actor, effect); meta_stack_tracker_queue_sync_stack (priv->display->stack_tracker); } void meta_compositor_size_change_window (MetaCompositor *compositor, MetaWindow *window, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); meta_window_actor_size_change (window_actor, which_change, old_frame_rect, old_buffer_rect); } void meta_compositor_switch_workspace (MetaCompositor *compositor, MetaWorkspace *from, MetaWorkspace *to, MetaMotionDirection direction) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); gint to_indx, from_indx; to_indx = meta_workspace_index (to); from_indx = meta_workspace_index (from); priv->switch_workspace_in_progress++; if (!meta_plugin_manager_switch_workspace (priv->plugin_mgr, from_indx, to_indx, direction)) { priv->switch_workspace_in_progress--; /* We have to explicitely call this to fix up stacking order of the * actors; this is because the abs stacking position of actors does not * necessarily change during the window hiding/unhiding, only their * relative position toward the destkop window. */ meta_finish_workspace_switch (compositor); } } static void sync_actor_stacking (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *children; GList *expected_window_node; GList *tmp; GList *old; GList *backgrounds; gboolean has_windows; gboolean reordered; /* NB: The first entries in the lists are stacked the lowest */ /* Restacking will trigger full screen redraws, so it's worth a * little effort to make sure we actually need to restack before * we go ahead and do it */ children = clutter_actor_get_children (priv->window_group); has_windows = FALSE; reordered = FALSE; /* We allow for actors in the window group other than the actors we * know about, but it's up to a plugin to try and keep them stacked correctly * (we really need extra API to make that reliable.) */ /* First we collect a list of all backgrounds, and check if they're at the * bottom. Then we check if the window actors are in the correct sequence */ backgrounds = NULL; expected_window_node = priv->windows; for (old = children; old != NULL; old = old->next) { ClutterActor *actor = old->data; if (META_IS_BACKGROUND_GROUP (actor) || META_IS_BACKGROUND_ACTOR (actor) || META_IS_X11_BACKGROUND_ACTOR (actor)) { backgrounds = g_list_prepend (backgrounds, actor); if (has_windows) reordered = TRUE; } else if (META_IS_WINDOW_ACTOR (actor) && !reordered) { has_windows = TRUE; if (expected_window_node != NULL && actor == expected_window_node->data) expected_window_node = expected_window_node->next; else reordered = TRUE; } } g_clear_pointer (&children, g_list_free); if (!reordered) { g_list_free (backgrounds); return; } /* reorder the actors by lowering them in turn to the bottom of the stack. * windows first, then background. * * We reorder the actors even if they're not parented to the window group, * to allow stacking to work with intermediate actors (eg during effects) */ for (tmp = g_list_last (priv->windows); tmp != NULL; tmp = tmp->prev) { ClutterActor *actor = tmp->data, *parent; parent = clutter_actor_get_parent (actor); clutter_actor_set_child_below_sibling (parent, actor, NULL); } // Place the desklet container above or below windows. if (priv->display->desklets_above) { clutter_actor_set_child_above_sibling (priv->window_group, priv->desklet_container, NULL); } else { clutter_actor_set_child_below_sibling (priv->window_group, priv->desklet_container, NULL); } // Then the bottom window group (which META_WINDOW_DESKTOP windows like nemo-desktop's get placed in). clutter_actor_set_child_below_sibling (priv->window_group, priv->bottom_window_group, NULL); if (meta_is_wayland_compositor ()) { children = clutter_actor_get_children (priv->bottom_window_group); for (tmp = children; tmp != NULL; tmp = tmp->next) { MetaWindowActor *child = tmp->data; MetaWindow *mw = meta_window_actor_get_meta_window (child); if (mw != NULL) { // CsdBackground manager sets _NET_WM_STATE_BELOW (gtk_window_set_keep_below) // This sets its stack layer to META_LAYER_BOTTOM, so we can keep these below // the nemo-desktop, etc.. MetaStackLayer layer = meta_window_get_default_layer (mw); if (layer == META_LAYER_BOTTOM) { clutter_actor_set_child_below_sibling (priv->bottom_window_group, CLUTTER_ACTOR (child), NULL); } } } g_list_free (children); } // and finally backgrounds.. /* we prepended the backgrounds above so the last actor in the list * should get lowered to the bottom last. */ for (tmp = backgrounds; tmp != NULL; tmp = tmp->next) { ClutterActor *actor = tmp->data, *parent; parent = clutter_actor_get_parent (actor); clutter_actor_set_child_below_sibling (parent, actor, NULL); } g_list_free (backgrounds); } /* * Find the top most window that is visible on the screen. The intention of * this is to avoid offscreen windows that isn't actually part of the visible * desktop (such as the UI frames override redirect window). */ static MetaWindowActor * get_top_visible_window_actor (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *l; for (l = g_list_last (priv->windows); l; l = l->prev) { MetaWindowActor *window_actor = l->data; MetaWindow *window = meta_window_actor_get_meta_window (window_actor); MetaRectangle buffer_rect; MetaRectangle display_rect = { 0 }; if (!window->visible_to_compositor) continue; meta_window_get_buffer_rect (window, &buffer_rect); meta_display_get_size (priv->display, &display_rect.width, &display_rect.height); if (meta_rectangle_overlap (&display_rect, &buffer_rect)) return window_actor; } return NULL; } static void on_top_window_actor_destroyed (MetaWindowActor *window_actor, MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); priv->top_window_actor = NULL; priv->top_window_actor_destroy_id = 0; priv->windows = g_list_remove (priv->windows, window_actor); meta_stack_tracker_queue_sync_stack (priv->display->stack_tracker); } void meta_compositor_sync_stack (MetaCompositor *compositor, GList *stack) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaWindowActor *top_window_actor; GList *old_stack; /* This is painful because hidden windows that we are in the process * of animating out of existence. They'll be at the bottom of the * stack of X windows, but we want to leave them in their old position * until the animation effect finishes. */ /* Sources: first window is the highest */ stack = g_list_copy (stack); /* The new stack of MetaWindow */ old_stack = g_list_reverse (priv->windows); /* The old stack of MetaWindowActor */ priv->windows = NULL; while (TRUE) { MetaWindowActor *old_actor = NULL, *stack_actor = NULL, *actor; MetaWindow *old_window = NULL, *stack_window = NULL, *window; /* Find the remaining top actor in our existing stack (ignoring * windows that have been hidden and are no longer animating) */ while (old_stack) { old_actor = old_stack->data; old_window = meta_window_actor_get_meta_window (old_actor); if ((old_window->hidden || old_window->unmanaging) && !meta_window_actor_effect_in_progress (old_actor)) { old_stack = g_list_delete_link (old_stack, old_stack); old_actor = NULL; } else break; } /* And the remaining top actor in the new stack */ while (stack) { stack_window = stack->data; stack_actor = meta_window_actor_from_window (stack_window); if (!stack_actor) { meta_verbose ("Failed to find corresponding MetaWindowActor " "for window %s\n", meta_window_get_description (stack_window)); stack = g_list_delete_link (stack, stack); } else break; } if (!old_actor && !stack_actor) /* Nothing more to stack */ break; /* We usually prefer the window in the new stack, but if if we * found a hidden window in the process of being animated out * of existence in the old stack we use that instead. We've * filtered out non-animating hidden windows above. */ if (old_actor && (!stack_actor || old_window->hidden || old_window->unmanaging)) { actor = old_actor; window = old_window; } else { actor = stack_actor; window = stack_window; } /* OK, we know what actor we want next. Add it to our window * list, and remove it from both source lists. (It will * be at the front of at least one, hopefully it will be * near the front of the other.) */ priv->windows = g_list_prepend (priv->windows, actor); stack = g_list_remove (stack, window); old_stack = g_list_remove (old_stack, actor); } sync_actor_stacking (compositor); top_window_actor = get_top_visible_window_actor (compositor); if (priv->top_window_actor == top_window_actor) return; g_clear_signal_handler (&priv->top_window_actor_destroy_id, priv->top_window_actor); priv->top_window_actor = top_window_actor; if (priv->top_window_actor) priv->top_window_actor_destroy_id = g_signal_connect (priv->top_window_actor, "destroy", G_CALLBACK (on_top_window_actor_destroyed), compositor); } void meta_compositor_sync_window_geometry (MetaCompositor *compositor, MetaWindow *window, gboolean did_placement) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaWindowActor *window_actor = meta_window_actor_from_window (window); MetaWindowActorChanges changes; changes = meta_window_actor_sync_actor_geometry (window_actor, did_placement); if (changes & META_WINDOW_ACTOR_CHANGE_SIZE) meta_plugin_manager_event_size_changed (priv->plugin_mgr, window_actor); } static void on_presented (ClutterStage *stage, CoglFrameEvent event, ClutterFrameInfo *frame_info, MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *l; if (event == COGL_FRAME_EVENT_COMPLETE) { gint64 presentation_time_cogl = frame_info->presentation_time; gint64 presentation_time; if (presentation_time_cogl != 0) { /* Cogl reports presentation in terms of its own clock, which is * guaranteed to be in nanoseconds but with no specified base. The * normal case with the open source GPU drivers on Linux 3.8 and * newer is that the base of cogl_get_clock_time() is that of * clock_gettime(CLOCK_MONOTONIC), so the same as g_get_monotonic_time), * but there's no exposure of that through the API. clock_gettime() * is fairly fast, so calling it twice and subtracting to get a * nearly-zero number is acceptable, if a litle ugly. */ gint64 current_cogl_time = cogl_get_clock_time (priv->context); gint64 current_monotonic_time = g_get_monotonic_time (); presentation_time = current_monotonic_time + (presentation_time_cogl - current_cogl_time) / 1000; } else { presentation_time = 0; } for (l = priv->windows; l; l = l->next) meta_window_actor_frame_complete (l->data, frame_info, presentation_time); } } static void meta_compositor_real_pre_paint (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); GList *l; for (l = priv->windows; l; l = l->next) meta_window_actor_pre_paint (l->data); } static void meta_compositor_pre_paint (MetaCompositor *compositor) { COGL_TRACE_BEGIN_SCOPED (MetaCompositorPrePaint, "Compositor (pre-paint)"); META_COMPOSITOR_GET_CLASS (compositor)->pre_paint (compositor); } static gboolean meta_pre_paint_func (gpointer data) { MetaCompositor *compositor = data; meta_compositor_pre_paint (compositor); return TRUE; } static void meta_compositor_real_post_paint (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); CoglGraphicsResetStatus status; #ifdef HAVE_WAYLAND if (meta_is_wayland_compositor ()) meta_wayland_compositor_paint_finished (meta_wayland_compositor_get_default ()); #endif status = cogl_get_graphics_reset_status (priv->context); switch (status) { case COGL_GRAPHICS_RESET_STATUS_NO_ERROR: break; case COGL_GRAPHICS_RESET_STATUS_PURGED_CONTEXT_RESET: g_signal_emit_by_name (priv->display, "gl-video-memory-purged"); clutter_actor_queue_redraw (CLUTTER_ACTOR (priv->stage)); break; default: /* The ARB_robustness spec says that, on error, the application should destroy the old context and create a new one. Since we don't have the necessary plumbing to do this we'll simply restart the process. Obviously we can't do this when we are a wayland compositor but in that case we shouldn't get here since we don't enable robustness in that case. */ g_assert (!meta_is_wayland_compositor ()); meta_restart (NULL); break; } } static void meta_compositor_post_paint (MetaCompositor *compositor) { COGL_TRACE_BEGIN_SCOPED (MetaCompositorPostPaint, "Compositor (post-paint)"); META_COMPOSITOR_GET_CLASS (compositor)->post_paint (compositor); } static gboolean meta_post_paint_func (gpointer data) { MetaCompositor *compositor = data; meta_compositor_post_paint (compositor); return TRUE; } static void meta_compositor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaCompositor *compositor = META_COMPOSITOR (object); MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); switch (prop_id) { case PROP_DISPLAY: priv->display = g_value_get_object (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_compositor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaCompositor *compositor = META_COMPOSITOR (object); MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); switch (prop_id) { case PROP_DISPLAY: g_value_set_object (value, priv->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); } } static void meta_compositor_init (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); priv->context = clutter_backend->cogl_context; priv->pre_paint_func_id = clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_PRE_PAINT, meta_pre_paint_func, compositor, NULL); priv->post_paint_func_id = clutter_threads_add_repaint_func_full (CLUTTER_REPAINT_FLAGS_POST_PAINT, meta_post_paint_func, compositor, NULL); } static void meta_compositor_dispose (GObject *object) { MetaCompositor *compositor = META_COMPOSITOR (object); MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); g_clear_signal_handler (&priv->stage_after_paint_id, priv->stage); g_clear_signal_handler (&priv->stage_presented_id, priv->stage); g_clear_handle_id (&priv->pre_paint_func_id, clutter_threads_remove_repaint_func); g_clear_handle_id (&priv->post_paint_func_id, clutter_threads_remove_repaint_func); g_clear_signal_handler (&priv->top_window_actor_destroy_id, priv->top_window_actor); g_clear_pointer (&priv->background_actor, clutter_actor_destroy); g_clear_pointer (&priv->bottom_window_group, clutter_actor_destroy); g_clear_pointer (&priv->desklet_container, clutter_actor_destroy); g_clear_pointer (&priv->window_group, clutter_actor_destroy); g_clear_pointer (&priv->top_window_group, clutter_actor_destroy); g_clear_pointer (&priv->feedback_group, clutter_actor_destroy); g_clear_pointer (&priv->windows, g_list_free); G_OBJECT_CLASS (meta_compositor_parent_class)->dispose (object); } static void meta_compositor_class_init (MetaCompositorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); object_class->set_property = meta_compositor_set_property; object_class->get_property = meta_compositor_get_property; object_class->dispose = meta_compositor_dispose; klass->remove_window = meta_compositor_real_remove_window; klass->pre_paint = meta_compositor_real_pre_paint; klass->post_paint = meta_compositor_real_post_paint; obj_props[PROP_DISPLAY] = g_param_spec_object ("display", "display", "MetaDisplay", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); g_object_class_install_properties (object_class, N_PROPS, obj_props); } /** * meta_disable_unredirect_for_display: * @display: a #MetaDisplay * * Disables unredirection, can be usefull in situations where having * unredirected windows is undesireable like when recording a video. * */ void meta_disable_unredirect_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; if (display->closing) return; compositor = get_compositor_for_display (display); priv = meta_compositor_get_instance_private (compositor); priv->disable_unredirect_count++; } /** * meta_enable_unredirect_for_display: * @display: a #MetaDisplay * * Enables unredirection which reduces the overhead for apps like games. * */ void meta_enable_unredirect_for_display (MetaDisplay *display) { MetaCompositor *compositor; MetaCompositorPrivate *priv; if (display->closing) return; compositor = get_compositor_for_display (display); priv = meta_compositor_get_instance_private (compositor); if (priv->disable_unredirect_count == 0) g_warning ("Called enable_unredirect_for_display while unredirection is enabled."); if (priv->disable_unredirect_count > 0) priv->disable_unredirect_count--; } gboolean meta_compositor_is_unredirect_inhibited (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return priv->disable_unredirect_count > 0; } #define FLASH_TIME_MS 50 static void flash_out_completed (ClutterTimeline *timeline, gboolean is_finished, gpointer user_data) { ClutterActor *flash = CLUTTER_ACTOR (user_data); clutter_actor_destroy (flash); } void meta_compositor_flash_display (MetaCompositor *compositor, MetaDisplay *display) { ClutterActor *stage; ClutterActor *flash; ClutterTransition *transition; gfloat width, height; stage = meta_get_stage_for_display (display); clutter_actor_get_size (stage, &width, &height); flash = clutter_actor_new (); clutter_actor_set_background_color (flash, CLUTTER_COLOR_Black); clutter_actor_set_size (flash, width, height); clutter_actor_set_opacity (flash, 0); clutter_actor_add_child (stage, flash); clutter_actor_save_easing_state (flash); clutter_actor_set_easing_mode (flash, CLUTTER_EASE_IN_QUAD); clutter_actor_set_easing_duration (flash, FLASH_TIME_MS); clutter_actor_set_opacity (flash, 192); transition = clutter_actor_get_transition (flash, "opacity"); clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE); clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2); g_signal_connect (transition, "stopped", G_CALLBACK (flash_out_completed), flash); clutter_actor_restore_easing_state (flash); } static void window_flash_out_completed (ClutterTimeline *timeline, gboolean is_finished, gpointer user_data) { ClutterActor *flash = CLUTTER_ACTOR (user_data); clutter_actor_destroy (flash); } void meta_compositor_flash_window (MetaCompositor *compositor, MetaWindow *window) { ClutterActor *window_actor = CLUTTER_ACTOR (meta_window_actor_from_window (window)); ClutterActor *flash; ClutterTransition *transition; flash = clutter_actor_new (); clutter_actor_set_background_color (flash, CLUTTER_COLOR_Black); clutter_actor_set_size (flash, window->rect.width, window->rect.height); clutter_actor_set_position (flash, window->custom_frame_extents.left, window->custom_frame_extents.top); clutter_actor_set_opacity (flash, 0); clutter_actor_add_child (window_actor, flash); clutter_actor_save_easing_state (flash); clutter_actor_set_easing_mode (flash, CLUTTER_EASE_IN_QUAD); clutter_actor_set_easing_duration (flash, FLASH_TIME_MS); clutter_actor_set_opacity (flash, 192); transition = clutter_actor_get_transition (flash, "opacity"); clutter_timeline_set_auto_reverse (CLUTTER_TIMELINE (transition), TRUE); clutter_timeline_set_repeat_count (CLUTTER_TIMELINE (transition), 2); g_signal_connect (transition, "stopped", G_CALLBACK (window_flash_out_completed), flash); clutter_actor_restore_easing_state (flash); } /** * meta_compositor_monotonic_to_high_res_xserver_time: * @display: a #MetaDisplay * @monotonic_time: time in the units of g_get_monotonic_time() * * _NET_WM_FRAME_DRAWN and _NET_WM_FRAME_TIMINGS messages represent time * as a "high resolution server time" - this is the server time interpolated * to microsecond resolution. The advantage of this time representation * is that if X server is running on the same computer as a client, and * the Xserver uses 'clock_gettime(CLOCK_MONOTONIC, ...)' for the server * time, the client can detect this, and all such clients will share a * a time representation with high accuracy. If there is not a common * time source, then the time synchronization will be less accurate. */ int64_t meta_compositor_monotonic_to_high_res_xserver_time (MetaCompositor *compositor, int64_t monotonic_time_us) { MetaCompositorClass *klass = META_COMPOSITOR_GET_CLASS (compositor); return klass->monotonic_to_high_res_xserver_time (compositor, monotonic_time_us); } void meta_compositor_show_tile_preview (MetaCompositor *compositor, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_plugin_manager_show_tile_preview (priv->plugin_mgr, window, tile_rect, tile_monitor_number); } void meta_compositor_hide_tile_preview (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_plugin_manager_hide_tile_preview (priv->plugin_mgr); } void meta_compositor_show_window_menu (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, int x, int y) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_plugin_manager_show_window_menu (priv->plugin_mgr, window, menu, x, y); } void meta_compositor_show_window_menu_for_rect (MetaCompositor *compositor, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_plugin_manager_show_window_menu_for_rect (priv->plugin_mgr, window, menu, rect); } MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *compositor, MetaWindow *window) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return meta_plugin_manager_create_close_dialog (priv->plugin_mgr, window); } MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (MetaCompositor *compositor, MetaWindow *window) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return meta_plugin_manager_create_inhibit_shortcuts_dialog (priv->plugin_mgr, window); } void meta_compositor_locate_pointer (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_plugin_manager_locate_pointer (priv->plugin_mgr); } MetaPluginManager * meta_compositor_get_plugin_manager (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return priv->plugin_mgr; } MetaDisplay * meta_compositor_get_display (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return priv->display; } ClutterStage * meta_compositor_get_stage (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return CLUTTER_STAGE (priv->stage); } MetaWindowActor * meta_compositor_get_top_window_actor (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return priv->top_window_actor; } gboolean meta_compositor_is_switching_workspace (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); return priv->switch_workspace_in_progress > 0; } /** * meta_get_x11_background_actor_for_display: * @display: a #MetaDisplay * * Gets the actor that draws the root window background under the windows. * The root window background automatically tracks the image or color set * by the environment. * * Returns: (transfer none): The background actor corresponding to @display */ ClutterActor * meta_get_x11_background_actor_for_display (MetaDisplay *display) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (display->compositor); if (meta_is_wayland_compositor ()) { return NULL; } return priv->background_actor; } /** * meta_get_desklet_container_for_display: * @display: a #MetaDisplay * * Returns the desklet container actor. * * Return value: (transfer none): The desklet container actor. */ ClutterActor * meta_get_desklet_container_for_display (MetaDisplay *display) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (display->compositor); return priv->desklet_container; } void meta_update_desklet_stacking (MetaCompositor *compositor) { MetaCompositorPrivate *priv = meta_compositor_get_instance_private (compositor); meta_stack_tracker_queue_sync_stack (priv->display->stack_tracker); } muffin-6.4.1/src/compositor/meta-surface-actor.c0000664000175000017500000004642114723361714020564 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:meta-surface-actor * @title: MetaSurfaceActor * @short_description: An actor representing a surface in the scene graph * * MetaSurfaceActor is an abstract class which represents a surface in the * Clutter scene graph. A subclass can implement the specifics of a surface * depending on the way it is handled by a display protocol. * * An important feature of #MetaSurfaceActor is that it allows you to set an * "input region": all events that occur in the surface, but outside of the * input region are to be explicitly ignored. By default, this region is to * %NULL, which means events on the whole surface is allowed. */ #include "config.h" #include "compositor/meta-surface-actor.h" #include "clutter/clutter.h" #include "compositor/clutter-utils.h" #include "compositor/meta-cullable.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-window-actor-private.h" #include "compositor/region-utils.h" #include "meta/meta-shaped-texture.h" typedef struct _MetaSurfaceActorPrivate { MetaShapedTexture *texture; cairo_region_t *input_region; /* MetaCullable regions, see that documentation for more details */ cairo_region_t *unobscured_region; /* Freeze/thaw accounting */ cairo_region_t *pending_damage; guint frozen : 1; } MetaSurfaceActorPrivate; static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaSurfaceActor, meta_surface_actor, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (MetaSurfaceActor) G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); enum { REPAINT_SCHEDULED, SIZE_CHANGED, LAST_SIGNAL, }; static guint signals[LAST_SIGNAL]; typedef enum { IN_STAGE_PERSPECTIVE, IN_ACTOR_PERSPECTIVE } ScalePerspectiveType; static cairo_region_t * effective_unobscured_region (MetaSurfaceActor *surface_actor) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (surface_actor); ClutterActor *actor; /* Fail if we have any mapped clones. */ actor = CLUTTER_ACTOR (surface_actor); do { if (clutter_actor_has_mapped_clones (actor)) return NULL; actor = clutter_actor_get_parent (actor); } while (actor != NULL); return priv->unobscured_region; } static cairo_region_t* get_scaled_region (MetaSurfaceActor *surface_actor, cairo_region_t *region, ScalePerspectiveType scale_perspective) { MetaWindowActor *window_actor; cairo_region_t *scaled_region; int geometry_scale; float x, y; window_actor = meta_window_actor_from_actor (CLUTTER_ACTOR (surface_actor)); geometry_scale = meta_window_actor_get_geometry_scale (window_actor); clutter_actor_get_position (CLUTTER_ACTOR (surface_actor), &x, &y); cairo_region_translate (region, x, y); switch (scale_perspective) { case IN_STAGE_PERSPECTIVE: scaled_region = meta_region_scale_double (region, geometry_scale, META_ROUNDING_STRATEGY_GROW); break; case IN_ACTOR_PERSPECTIVE: scaled_region = meta_region_scale_double (region, 1.0 / geometry_scale, META_ROUNDING_STRATEGY_GROW); break; } cairo_region_translate (region, -x, -y); cairo_region_translate (scaled_region, -x, -y); return scaled_region; } static void set_unobscured_region (MetaSurfaceActor *surface_actor, cairo_region_t *unobscured_region) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (surface_actor); g_clear_pointer (&priv->unobscured_region, cairo_region_destroy); if (unobscured_region) { if (cairo_region_is_empty (unobscured_region)) { priv->unobscured_region = cairo_region_reference (unobscured_region); } else { cairo_rectangle_int_t bounds = { 0, }; float width, height; clutter_content_get_preferred_size (CLUTTER_CONTENT (priv->texture), &width, &height); bounds = (cairo_rectangle_int_t) { .width = width, .height = height, }; priv->unobscured_region = get_scaled_region (surface_actor, unobscured_region, IN_ACTOR_PERSPECTIVE); cairo_region_intersect_rectangle (priv->unobscured_region, &bounds); } } } static void set_clip_region (MetaSurfaceActor *surface_actor, cairo_region_t *clip_region) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (surface_actor); MetaShapedTexture *stex = priv->texture; if (clip_region && !cairo_region_is_empty (clip_region)) { cairo_region_t *region; region = get_scaled_region (surface_actor, clip_region, IN_ACTOR_PERSPECTIVE); meta_shaped_texture_set_clip_region (stex, region); cairo_region_destroy (region); } else { meta_shaped_texture_set_clip_region (stex, clip_region); } } static void meta_surface_actor_pick (ClutterActor *actor, ClutterPickContext *pick_context) { MetaSurfaceActor *self = META_SURFACE_ACTOR (actor); MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); ClutterActorIter iter; ClutterActor *child; if (!clutter_actor_should_pick_paint (actor)) return; /* If there is no region then use the regular pick */ if (priv->input_region == NULL) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (meta_surface_actor_parent_class); actor_class->pick (actor, pick_context); } else { int n_rects; int i; n_rects = cairo_region_num_rectangles (priv->input_region); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; ClutterActorBox box; cairo_region_get_rectangle (priv->input_region, i, &rect); box.x1 = rect.x; box.y1 = rect.y; box.x2 = rect.x + rect.width; box.y2 = rect.y + rect.height; clutter_actor_pick_box (actor, pick_context, &box); } } clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) clutter_actor_pick (child, pick_context); } static gboolean meta_surface_actor_get_paint_volume (ClutterActor *actor, ClutterPaintVolume *volume) { return clutter_paint_volume_set_from_allocation (volume, actor); } static void meta_surface_actor_dispose (GObject *object) { MetaSurfaceActor *self = META_SURFACE_ACTOR (object); MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); g_clear_pointer (&priv->input_region, cairo_region_destroy); g_clear_object (&priv->texture); set_unobscured_region (self, NULL); G_OBJECT_CLASS (meta_surface_actor_parent_class)->dispose (object); } static void meta_surface_actor_class_init (MetaSurfaceActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); object_class->dispose = meta_surface_actor_dispose; actor_class->pick = meta_surface_actor_pick; actor_class->get_paint_volume = meta_surface_actor_get_paint_volume; signals[REPAINT_SCHEDULED] = g_signal_new ("repaint-scheduled", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[SIZE_CHANGED] = g_signal_new ("size-changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } gboolean meta_surface_actor_is_opaque (MetaSurfaceActor *self) { return META_SURFACE_ACTOR_GET_CLASS (self)->is_opaque (self); } static void meta_surface_actor_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { MetaSurfaceActor *surface_actor = META_SURFACE_ACTOR (cullable); MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (surface_actor); uint8_t opacity = clutter_actor_get_opacity (CLUTTER_ACTOR (cullable)); set_unobscured_region (surface_actor, unobscured_region); set_clip_region (surface_actor, clip_region); if (opacity == 0xff) { cairo_region_t *opaque_region; cairo_region_t *scaled_opaque_region; opaque_region = meta_shaped_texture_get_opaque_region (priv->texture); if (!opaque_region) return; scaled_opaque_region = get_scaled_region (surface_actor, opaque_region, IN_STAGE_PERSPECTIVE); if (unobscured_region) cairo_region_subtract (unobscured_region, scaled_opaque_region); if (clip_region) cairo_region_subtract (clip_region, scaled_opaque_region); cairo_region_destroy (scaled_opaque_region); } } static gboolean meta_surface_actor_is_untransformed (MetaCullable *cullable) { ClutterActor *actor = CLUTTER_ACTOR (cullable); MetaWindowActor *window_actor; float width, height; graphene_point3d_t verts[4]; int geometry_scale; clutter_actor_get_size (actor, &width, &height); clutter_actor_get_abs_allocation_vertices (actor, verts); window_actor = meta_window_actor_from_actor (actor); geometry_scale = meta_window_actor_get_geometry_scale (window_actor); return meta_actor_vertices_are_untransformed (verts, width * geometry_scale, height * geometry_scale, NULL, NULL); } static void meta_surface_actor_reset_culling (MetaCullable *cullable) { MetaSurfaceActor *surface_actor = META_SURFACE_ACTOR (cullable); set_clip_region (surface_actor, NULL); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_surface_actor_cull_out; iface->is_untransformed = meta_surface_actor_is_untransformed; iface->reset_culling = meta_surface_actor_reset_culling; } static void texture_size_changed (MetaShapedTexture *texture, gpointer user_data) { MetaSurfaceActor *actor = META_SURFACE_ACTOR (user_data); g_signal_emit (actor, signals[SIZE_CHANGED], 0); } static void meta_surface_actor_init (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); priv->texture = meta_shaped_texture_new (); g_signal_connect_object (priv->texture, "size-changed", G_CALLBACK (texture_size_changed), self, 0); clutter_actor_set_content (CLUTTER_ACTOR (self), CLUTTER_CONTENT (priv->texture)); clutter_actor_set_request_mode (CLUTTER_ACTOR (self), CLUTTER_REQUEST_CONTENT_SIZE); } /** * meta_surface_actor_get_image: * @self: A #MetaSurfaceActor * @clip: (nullable): A clipping rectangle. The clip region is in * the same coordinate space as the contents preferred size. * For a shaped texture of a wl_surface, this means surface * coordinate space. If NULL, the whole content will be used. * * Get the image from the texture content. The resulting size of * the returned image may be different from the preferred size of * the shaped texture content. * * Returns: (nullable) (transfer full): a new cairo surface to be freed * with cairo_surface_destroy(). */ cairo_surface_t * meta_surface_actor_get_image (MetaSurfaceActor *self, cairo_rectangle_int_t *clip) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); return meta_shaped_texture_get_image (priv->texture, clip); } MetaShapedTexture * meta_surface_actor_get_texture (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); return priv->texture; } void meta_surface_actor_update_area (MetaSurfaceActor *self, int x, int y, int width, int height) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); gboolean repaint_scheduled = FALSE; cairo_rectangle_int_t clip; if (meta_shaped_texture_update_area (priv->texture, x, y, width, height, &clip)) { cairo_region_t *unobscured_region; unobscured_region = effective_unobscured_region (self); if (unobscured_region) { cairo_region_t *intersection; if (cairo_region_is_empty (unobscured_region)) return; intersection = cairo_region_copy (unobscured_region); cairo_region_intersect_rectangle (intersection, &clip); if (!cairo_region_is_empty (intersection)) { cairo_rectangle_int_t damage_rect; cairo_region_get_extents (intersection, &damage_rect); clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self), &damage_rect); repaint_scheduled = TRUE; } cairo_region_destroy (intersection); } else { clutter_actor_queue_redraw_with_clip (CLUTTER_ACTOR (self), &clip); repaint_scheduled = TRUE; } } if (repaint_scheduled) g_signal_emit (self, signals[REPAINT_SCHEDULED], 0); } gboolean meta_surface_actor_is_obscured (MetaSurfaceActor *self) { cairo_region_t *unobscured_region; unobscured_region = effective_unobscured_region (self); if (unobscured_region) return cairo_region_is_empty (unobscured_region); else return FALSE; } void meta_surface_actor_set_input_region (MetaSurfaceActor *self, cairo_region_t *region) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); if (priv->input_region) cairo_region_destroy (priv->input_region); if (region) priv->input_region = cairo_region_reference (region); else priv->input_region = NULL; } void meta_surface_actor_set_opaque_region (MetaSurfaceActor *self, cairo_region_t *region) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_set_opaque_region (priv->texture, region); } cairo_region_t * meta_surface_actor_get_opaque_region (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); return meta_shaped_texture_get_opaque_region (priv->texture); } void meta_surface_actor_process_damage (MetaSurfaceActor *self, int x, int y, int width, int height) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); if (meta_surface_actor_is_frozen (self)) { /* The window is frozen due to an effect in progress: we ignore damage * here on the off chance that this will stop the corresponding * texture_from_pixmap from being update. * * pending_damage tracks any damage that happened while the window was * frozen so that when can apply it when the window becomes unfrozen. * * It should be noted that this is an unreliable mechanism since it's * quite likely that drivers will aim to provide a zero-copy * implementation of the texture_from_pixmap extension and in those cases * any drawing done to the window is always immediately reflected in the * texture regardless of damage event handling. */ cairo_rectangle_int_t rect = { .x = x, .y = y, .width = width, .height = height }; if (!priv->pending_damage) priv->pending_damage = cairo_region_create_rectangle (&rect); else cairo_region_union_rectangle (priv->pending_damage, &rect); return; } META_SURFACE_ACTOR_GET_CLASS (self)->process_damage (self, x, y, width, height); } void meta_surface_actor_pre_paint (MetaSurfaceActor *self) { META_SURFACE_ACTOR_GET_CLASS (self)->pre_paint (self); } void meta_surface_actor_set_frozen (MetaSurfaceActor *self, gboolean frozen) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); priv->frozen = frozen; if (!frozen && priv->pending_damage) { int i, n_rects = cairo_region_num_rectangles (priv->pending_damage); cairo_rectangle_int_t rect; /* Since we ignore damage events while a window is frozen for certain effects * we need to apply the tracked damage now. */ for (i = 0; i < n_rects; i++) { cairo_region_get_rectangle (priv->pending_damage, i, &rect); meta_surface_actor_process_damage (self, rect.x, rect.y, rect.width, rect.height); } g_clear_pointer (&priv->pending_damage, cairo_region_destroy); } } gboolean meta_surface_actor_is_frozen (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); return priv->frozen; } void meta_surface_actor_set_transform (MetaSurfaceActor *self, MetaMonitorTransform transform) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_set_transform (priv->texture, transform); } void meta_surface_actor_set_viewport_src_rect (MetaSurfaceActor *self, graphene_rect_t *src_rect) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_set_viewport_src_rect (priv->texture, src_rect); } void meta_surface_actor_reset_viewport_src_rect (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_reset_viewport_src_rect (priv->texture); } void meta_surface_actor_set_viewport_dst_size (MetaSurfaceActor *self, int dst_width, int dst_height) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_set_viewport_dst_size (priv->texture, dst_width, dst_height); } void meta_surface_actor_reset_viewport_dst_size (MetaSurfaceActor *self) { MetaSurfaceActorPrivate *priv = meta_surface_actor_get_instance_private (self); meta_shaped_texture_reset_viewport_dst_size (priv->texture); } muffin-6.4.1/src/compositor/region-utils.h0000664000175000017500000001054714723361714017530 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for region manipulation * * Copyright (C) 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_REGION_UTILS_H__ #define __META_REGION_UTILS_H__ #include #include #include "backends/meta-backend-types.h" #include "clutter/clutter.h" #include "core/boxes-private.h" /** * MetaRegionIterator: * @region: region being iterated * @rectangle: current rectangle * @line_start: whether the current rectangle starts a horizontal band * @line_end: whether the current rectangle ends a horizontal band * * cairo_region_t is a yx banded region; sometimes its useful to iterate through * such a region treating the start and end of each horizontal band in a distinct * fashion. * * Usage: * * MetaRegionIterator iter; * for (meta_region_iterator_init (&iter, region); * !meta_region_iterator_at_end (&iter); * meta_region_iterator_next (&iter)) * { * [ Use iter.rectangle, iter.line_start, iter.line_end ] * } */ typedef struct _MetaRegionIterator MetaRegionIterator; struct _MetaRegionIterator { cairo_region_t *region; cairo_rectangle_int_t rectangle; gboolean line_start; gboolean line_end; int i; /*< private >*/ int n_rectangles; cairo_rectangle_int_t next_rectangle; }; typedef struct _MetaRegionBuilder MetaRegionBuilder; #define META_REGION_BUILDER_MAX_LEVELS 16 struct _MetaRegionBuilder { /* To merge regions in binary tree order, we need to keep track of * the regions that we've already merged together at different * levels of the tree. We fill in an array in the pattern: * * |a | * |b |a | * |c | |ab | * |d |c |ab | * |e | | |abcd| */ cairo_region_t *levels[META_REGION_BUILDER_MAX_LEVELS]; int n_levels; }; void meta_region_builder_init (MetaRegionBuilder *builder); void meta_region_builder_add_rectangle (MetaRegionBuilder *builder, int x, int y, int width, int height); cairo_region_t * meta_region_builder_finish (MetaRegionBuilder *builder); void meta_region_iterator_init (MetaRegionIterator *iter, cairo_region_t *region); gboolean meta_region_iterator_at_end (MetaRegionIterator *iter); void meta_region_iterator_next (MetaRegionIterator *iter); cairo_region_t * meta_region_scale (cairo_region_t *region, int scale); cairo_region_t * meta_region_scale_double (cairo_region_t *region, double scale, MetaRoundingStrategy rounding_strategy); cairo_region_t * meta_make_border_region (cairo_region_t *region, int x_amount, int y_amount, gboolean flip); cairo_region_t * meta_region_transform (cairo_region_t *region, MetaMonitorTransform transform, int width, int height); cairo_region_t * meta_region_crop_and_scale (cairo_region_t *region, graphene_rect_t *src_rect, int dst_width, int dst_height); #endif /* __META_REGION_UTILS_H__ */ muffin-6.4.1/src/compositor/meta-cullable.h0000664000175000017500000000414314723361714017611 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Written by: * Owen Taylor * Ray Strode * Jasper St. Pierre */ #ifndef __META_CULLABLE_H__ #define __META_CULLABLE_H__ #include "clutter/clutter.h" G_BEGIN_DECLS #define META_TYPE_CULLABLE (meta_cullable_get_type ()) G_DECLARE_INTERFACE (MetaCullable, meta_cullable, META, CULLABLE, ClutterActor) struct _MetaCullableInterface { GTypeInterface g_iface; void (* cull_out) (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); gboolean (* is_untransformed) (MetaCullable *cullable); void (* reset_culling) (MetaCullable *cullable); }; void meta_cullable_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); gboolean meta_cullable_is_untransformed (MetaCullable *cullable); void meta_cullable_reset_culling (MetaCullable *cullable); /* Utility methods for implementations */ void meta_cullable_cull_out_children (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region); void meta_cullable_reset_culling_children (MetaCullable *cullable); G_END_DECLS #endif /* __META_CULLABLE_H__ */ muffin-6.4.1/src/compositor/meta-background.c0000664000175000017500000010025614723361714020142 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "compositor/meta-background-private.h" #include #include "backends/meta-backend-private.h" #include "compositor/cogl-utils.h" #include "meta/display.h" #include "meta/meta-background-image.h" #include "meta/meta-background.h" #include "meta/meta-monitor-manager.h" #include "meta/util.h" enum { CHANGED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; typedef struct _MetaBackgroundMonitor MetaBackgroundMonitor; struct _MetaBackgroundMonitor { gboolean dirty; CoglTexture *texture; CoglFramebuffer *fbo; }; struct _MetaBackground { GObject parent; MetaDisplay *display; MetaBackgroundMonitor *monitors; int n_monitors; CDesktopBackgroundStyle style; CDesktopBackgroundShading shading_direction; ClutterColor color; ClutterColor second_color; GFile *file1; MetaBackgroundImage *background_image1; GFile *file2; MetaBackgroundImage *background_image2; CoglTexture *color_texture; CoglTexture *wallpaper_texture; float blend_factor; guint wallpaper_allocation_failed : 1; }; enum { PROP_META_DISPLAY = 1, PROP_MONITOR, }; G_DEFINE_TYPE (MetaBackground, meta_background, G_TYPE_OBJECT) static gboolean texture_has_alpha (CoglTexture *texture); static GSList *all_backgrounds = NULL; static void free_fbos (MetaBackground *self) { int i; for (i = 0; i < self->n_monitors; i++) { MetaBackgroundMonitor *monitor = &self->monitors[i]; if (monitor->fbo) { cogl_object_unref (monitor->fbo); monitor->fbo = NULL; } if (monitor->texture) { cogl_object_unref (monitor->texture); monitor->texture = NULL; } } } static void free_color_texture (MetaBackground *self) { if (self->color_texture != NULL) { cogl_object_unref (self->color_texture); self->color_texture = NULL; } } static void free_wallpaper_texture (MetaBackground *self) { if (self->wallpaper_texture != NULL) { cogl_object_unref (self->wallpaper_texture); self->wallpaper_texture = NULL; } self->wallpaper_allocation_failed = FALSE; } static void invalidate_monitor_backgrounds (MetaBackground *self) { free_fbos (self); g_free (self->monitors); self->monitors = NULL; self->n_monitors = 0; if (self->display) { int i; self->n_monitors = meta_display_get_n_monitors (self->display); self->monitors = g_new0 (MetaBackgroundMonitor, self->n_monitors); for (i = 0; i < self->n_monitors; i++) self->monitors[i].dirty = TRUE; } } static void on_monitors_changed (MetaBackground *self) { invalidate_monitor_backgrounds (self); } static void set_display (MetaBackground *self, MetaDisplay *display) { g_set_object (&self->display, display); invalidate_monitor_backgrounds (self); } static void meta_background_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { switch (prop_id) { case PROP_META_DISPLAY: set_display (META_BACKGROUND (object), g_value_get_object (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_background_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaBackground *self = META_BACKGROUND (object); switch (prop_id) { case PROP_META_DISPLAY: g_value_set_object (value, self->display); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static gboolean need_prerender (MetaBackground *self) { CoglTexture *texture1 = self->background_image1 ? meta_background_image_get_texture (self->background_image1) : NULL; CoglTexture *texture2 = self->background_image2 ? meta_background_image_get_texture (self->background_image2) : NULL; if (texture1 == NULL && texture2 == NULL) return FALSE; if (texture2 == NULL && self->style == C_DESKTOP_BACKGROUND_STYLE_WALLPAPER) return FALSE; return TRUE; } static void mark_changed (MetaBackground *self) { int i; if (!need_prerender (self)) free_fbos (self); for (i = 0; i < self->n_monitors; i++) self->monitors[i].dirty = TRUE; g_signal_emit (self, signals[CHANGED], 0); } static void on_background_loaded (MetaBackgroundImage *image, MetaBackground *self) { mark_changed (self); } static gboolean file_equal0 (GFile *file1, GFile *file2) { if (file1 == file2) return TRUE; if ((file1 == NULL) || (file2 == NULL)) return FALSE; return g_file_equal (file1, file2); } static void set_file (MetaBackground *self, GFile **filep, MetaBackgroundImage **imagep, GFile *file, gboolean force_reload) { if (force_reload || !file_equal0 (*filep, file)) { if (*imagep) { g_signal_handlers_disconnect_by_func (*imagep, (gpointer)on_background_loaded, self); g_object_unref (*imagep); *imagep = NULL; } g_set_object (filep, file); if (file) { MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); *imagep = meta_background_image_cache_load (cache, file); g_signal_connect (*imagep, "loaded", G_CALLBACK (on_background_loaded), self); } } } static void on_gl_video_memory_purged (MetaBackground *self) { MetaBackgroundImageCache *cache = meta_background_image_cache_get_default (); /* The GPU memory that just got invalidated is the texture inside * self->background_image1,2 and/or its mipmaps. However, to save memory the * original pixbuf isn't kept in RAM so we can't do a simple re-upload. The * only copy of the image was the one in texture memory that got invalidated. * So we need to do a full reload from disk. */ if (self->file1) { meta_background_image_cache_purge (cache, self->file1); set_file (self, &self->file1, &self->background_image1, self->file1, TRUE); } if (self->file2) { meta_background_image_cache_purge (cache, self->file2); set_file (self, &self->file2, &self->background_image2, self->file2, TRUE); } mark_changed (self); } static void meta_background_dispose (GObject *object) { MetaBackground *self = META_BACKGROUND (object); free_color_texture (self); free_wallpaper_texture (self); set_file (self, &self->file1, &self->background_image1, NULL, FALSE); set_file (self, &self->file2, &self->background_image2, NULL, FALSE); set_display (self, NULL); G_OBJECT_CLASS (meta_background_parent_class)->dispose (object); } static void meta_background_finalize (GObject *object) { all_backgrounds = g_slist_remove (all_backgrounds, object); G_OBJECT_CLASS (meta_background_parent_class)->finalize (object); } static void meta_background_constructed (GObject *object) { MetaBackground *self = META_BACKGROUND (object); MetaMonitorManager *monitor_manager = meta_monitor_manager_get (); G_OBJECT_CLASS (meta_background_parent_class)->constructed (object); g_signal_connect_object (self->display, "gl-video-memory-purged", G_CALLBACK (on_gl_video_memory_purged), object, G_CONNECT_SWAPPED); g_signal_connect_object (monitor_manager, "monitors-changed", G_CALLBACK (on_monitors_changed), self, G_CONNECT_SWAPPED); } static void meta_background_class_init (MetaBackgroundClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *param_spec; object_class->dispose = meta_background_dispose; object_class->finalize = meta_background_finalize; object_class->constructed = meta_background_constructed; object_class->set_property = meta_background_set_property; object_class->get_property = meta_background_get_property; signals[CHANGED] = g_signal_new ("changed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); param_spec = g_param_spec_object ("meta-display", "MetaDisplay", "MetaDisplay", META_TYPE_DISPLAY, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_META_DISPLAY, param_spec); } static void meta_background_init (MetaBackground *self) { all_backgrounds = g_slist_prepend (all_backgrounds, self); } static void set_texture_area_from_monitor_area (cairo_rectangle_int_t *monitor_area, cairo_rectangle_int_t *texture_area) { texture_area->x = 0; texture_area->y = 0; texture_area->width = monitor_area->width; texture_area->height = monitor_area->height; } static void get_texture_area (MetaBackground *self, cairo_rectangle_int_t *monitor_rect, float monitor_scale, CoglTexture *texture, cairo_rectangle_int_t *texture_area) { cairo_rectangle_int_t image_area; int screen_width, screen_height; float texture_width, texture_height; float monitor_x_scale, monitor_y_scale; texture_width = cogl_texture_get_width (texture); texture_height = cogl_texture_get_height (texture); switch (self->style) { case C_DESKTOP_BACKGROUND_STYLE_STRETCHED: default: /* paint region is whole actor, and the texture * is scaled disproportionately to fit the actor */ set_texture_area_from_monitor_area (monitor_rect, texture_area); break; case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER: meta_display_get_size (self->display, &screen_width, &screen_height); /* Start off by centering a tile in the middle of the * total screen area taking care of the monitor scaling. */ image_area.x = (screen_width - texture_width) / 2.0; image_area.y = (screen_height - texture_height) / 2.0; image_area.width = texture_width; image_area.height = texture_height; /* Translate into the coordinate system of the particular monitor */ image_area.x -= monitor_rect->x; image_area.y -= monitor_rect->y; *texture_area = image_area; break; case C_DESKTOP_BACKGROUND_STYLE_CENTERED: /* paint region is the original image size centered in the actor, * and the texture is scaled to the original image size */ image_area.width = texture_width; image_area.height = texture_height; image_area.x = monitor_rect->width / 2 - image_area.width / 2; image_area.y = monitor_rect->height / 2 - image_area.height / 2; *texture_area = image_area; break; case C_DESKTOP_BACKGROUND_STYLE_SCALED: case C_DESKTOP_BACKGROUND_STYLE_ZOOM: /* paint region is the actor size in one dimension, and centered and * scaled by proportional amount in the other dimension. * * SCALED forces the centered dimension to fit on screen. * ZOOM forces the centered dimension to grow off screen */ monitor_x_scale = monitor_rect->width / texture_width; monitor_y_scale = monitor_rect->height / texture_height; if ((self->style == C_DESKTOP_BACKGROUND_STYLE_SCALED && (monitor_x_scale < monitor_y_scale)) || (self->style == C_DESKTOP_BACKGROUND_STYLE_ZOOM && (monitor_x_scale > monitor_y_scale))) { /* Fill image to exactly fit actor horizontally */ image_area.width = monitor_rect->width; image_area.height = texture_height * monitor_x_scale; /* Position image centered vertically in actor */ image_area.x = 0; image_area.y = monitor_rect->height / 2 - image_area.height / 2; } else { /* Scale image to exactly fit actor vertically */ image_area.width = texture_width * monitor_y_scale; image_area.height = monitor_rect->height; /* Position image centered horizontally in actor */ image_area.x = monitor_rect->width / 2 - image_area.width / 2; image_area.y = 0; } *texture_area = image_area; break; case C_DESKTOP_BACKGROUND_STYLE_SPANNED: { /* paint region is the union of all monitors, with the origin * of the region set to align with monitor associated with the background. */ meta_display_get_size (self->display, &screen_width, &screen_height); /* unclipped texture area is whole screen, scaled depending on monitor */ image_area.width = screen_width * monitor_scale; image_area.height = screen_height * monitor_scale; /* But make (0,0) line up with the appropriate monitor */ image_area.x = -monitor_rect->x; image_area.y = -monitor_rect->y; *texture_area = image_area; break; } } } static gboolean draw_texture (MetaBackground *self, CoglFramebuffer *framebuffer, CoglPipeline *pipeline, CoglTexture *texture, cairo_rectangle_int_t *monitor_area, float monitor_scale) { cairo_rectangle_int_t texture_area; gboolean bare_region_visible; get_texture_area (self, monitor_area, monitor_scale, texture, &texture_area); switch (self->style) { case C_DESKTOP_BACKGROUND_STYLE_STRETCHED: case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER: case C_DESKTOP_BACKGROUND_STYLE_ZOOM: case C_DESKTOP_BACKGROUND_STYLE_SPANNED: /* Draw the entire monitor */ cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, 0, 0, monitor_area->width, monitor_area->height, - texture_area.x / (float)texture_area.width, - texture_area.y / (float)texture_area.height, (monitor_area->width - texture_area.x) / (float)texture_area.width, (monitor_area->height - texture_area.y) / (float)texture_area.height); bare_region_visible = texture_has_alpha (texture); /* Draw just the texture */ break; case C_DESKTOP_BACKGROUND_STYLE_CENTERED: case C_DESKTOP_BACKGROUND_STYLE_SCALED: cogl_framebuffer_draw_textured_rectangle (framebuffer, pipeline, texture_area.x, texture_area.y, texture_area.x + texture_area.width, texture_area.y + texture_area.height, 0, 0, 1.0, 1.0); bare_region_visible = texture_has_alpha (texture) || memcmp (&texture_area, monitor_area, sizeof (cairo_rectangle_int_t)) != 0; break; case C_DESKTOP_BACKGROUND_STYLE_NONE: bare_region_visible = TRUE; break; default: g_return_val_if_reached(FALSE); } return bare_region_visible; } static void ensure_color_texture (MetaBackground *self) { if (self->color_texture == NULL) { ClutterBackend *backend = clutter_get_default_backend (); CoglContext *ctx = clutter_backend_get_cogl_context (backend); GError *error = NULL; uint8_t pixels[6]; int width, height; if (self->shading_direction == C_DESKTOP_BACKGROUND_SHADING_SOLID) { width = 1; height = 1; pixels[0] = self->color.red; pixels[1] = self->color.green; pixels[2] = self->color.blue; } else { switch (self->shading_direction) { case C_DESKTOP_BACKGROUND_SHADING_VERTICAL: width = 1; height = 2; break; case C_DESKTOP_BACKGROUND_SHADING_HORIZONTAL: width = 2; height = 1; break; default: g_return_if_reached (); } pixels[0] = self->color.red; pixels[1] = self->color.green; pixels[2] = self->color.blue; pixels[3] = self->second_color.red; pixels[4] = self->second_color.green; pixels[5] = self->second_color.blue; } self->color_texture = COGL_TEXTURE (cogl_texture_2d_new_from_data (ctx, width, height, COGL_PIXEL_FORMAT_RGB_888, width * 3, pixels, &error)); if (error != NULL) { meta_warning ("Failed to allocate color texture: %s\n", error->message); g_error_free (error); } } } typedef enum { PIPELINE_REPLACE, PIPELINE_ADD, PIPELINE_OVER_REVERSE, } PipelineType; static CoglPipeline * create_pipeline (PipelineType type) { const char * const blend_strings[3] = { [PIPELINE_REPLACE] = "RGBA = ADD (SRC_COLOR, 0)", [PIPELINE_ADD] = "RGBA = ADD (SRC_COLOR, DST_COLOR)", [PIPELINE_OVER_REVERSE] = "RGBA = ADD (SRC_COLOR * (1 - DST_COLOR[A]), DST_COLOR)", }; static CoglPipeline *templates[3]; if (templates[type] == NULL) { templates[type] = meta_create_texture_pipeline (NULL); cogl_pipeline_set_blend (templates[type], blend_strings[type], NULL); } cogl_pipeline_set_layer_filters (templates[type], 0, COGL_PIPELINE_FILTER_LINEAR_MIPMAP_LINEAR, COGL_PIPELINE_FILTER_LINEAR); return cogl_pipeline_copy (templates[type]); } static gboolean texture_has_alpha (CoglTexture *texture) { if (!texture) return FALSE; switch (cogl_texture_get_components (texture)) { case COGL_TEXTURE_COMPONENTS_A: case COGL_TEXTURE_COMPONENTS_RGBA: return TRUE; case COGL_TEXTURE_COMPONENTS_RG: case COGL_TEXTURE_COMPONENTS_RGB: case COGL_TEXTURE_COMPONENTS_DEPTH: return FALSE; default: g_assert_not_reached (); return FALSE; } } static gboolean ensure_wallpaper_texture (MetaBackground *self, CoglTexture *texture) { if (self->wallpaper_texture == NULL && !self->wallpaper_allocation_failed) { int width = cogl_texture_get_width (texture); int height = cogl_texture_get_height (texture); CoglOffscreen *offscreen; CoglFramebuffer *fbo; GError *catch_error = NULL; CoglPipeline *pipeline; self->wallpaper_texture = meta_create_texture (width, height, COGL_TEXTURE_COMPONENTS_RGBA, META_TEXTURE_FLAGS_NONE); offscreen = cogl_offscreen_new_with_texture (self->wallpaper_texture); fbo = COGL_FRAMEBUFFER (offscreen); if (!cogl_framebuffer_allocate (fbo, &catch_error)) { /* This probably means that the size of the wallpapered texture is larger * than the maximum texture size; we treat it as permanent until the * background is changed again. */ g_error_free (catch_error); cogl_object_unref (self->wallpaper_texture); self->wallpaper_texture = NULL; cogl_object_unref (fbo); self->wallpaper_allocation_failed = TRUE; return FALSE; } cogl_framebuffer_orthographic (fbo, 0, 0, width, height, -1., 1.); pipeline = create_pipeline (PIPELINE_REPLACE); cogl_pipeline_set_layer_texture (pipeline, 0, texture); cogl_framebuffer_draw_textured_rectangle (fbo, pipeline, 0, 0, width, height, 0., 0., 1., 1.); cogl_object_unref (pipeline); if (texture_has_alpha (texture)) { ensure_color_texture (self); pipeline = create_pipeline (PIPELINE_OVER_REVERSE); cogl_pipeline_set_layer_texture (pipeline, 0, self->color_texture); cogl_framebuffer_draw_rectangle (fbo, pipeline, 0, 0, width, height); cogl_object_unref (pipeline); } cogl_object_unref (fbo); } return self->wallpaper_texture != NULL; } static CoglPipelineWrapMode get_wrap_mode (CDesktopBackgroundStyle style) { switch (style) { case C_DESKTOP_BACKGROUND_STYLE_WALLPAPER: return COGL_PIPELINE_WRAP_MODE_REPEAT; case C_DESKTOP_BACKGROUND_STYLE_NONE: case C_DESKTOP_BACKGROUND_STYLE_STRETCHED: case C_DESKTOP_BACKGROUND_STYLE_CENTERED: case C_DESKTOP_BACKGROUND_STYLE_SCALED: case C_DESKTOP_BACKGROUND_STYLE_ZOOM: case C_DESKTOP_BACKGROUND_STYLE_SPANNED: default: return COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; } } static int get_best_mipmap_level (CoglTexture *texture, int visible_width, int visible_height) { int mipmap_width = cogl_texture_get_width (texture); int mipmap_height = cogl_texture_get_height (texture); int halves = 0; while (mipmap_width >= visible_width && mipmap_height >= visible_height) { halves++; mipmap_width /= 2; mipmap_height /= 2; } return MAX (0, halves - 1); } CoglTexture * meta_background_get_texture (MetaBackground *self, int monitor_index, cairo_rectangle_int_t *texture_area, CoglPipelineWrapMode *wrap_mode) { MetaBackgroundMonitor *monitor; MetaRectangle geometry; cairo_rectangle_int_t monitor_area; CoglTexture *texture1, *texture2; float monitor_scale; g_return_val_if_fail (META_IS_BACKGROUND (self), NULL); g_return_val_if_fail (monitor_index >= 0 && monitor_index < self->n_monitors, NULL); monitor = &self->monitors[monitor_index]; meta_display_get_monitor_geometry (self->display, monitor_index, &geometry); monitor_scale = meta_display_get_monitor_scale (self->display, monitor_index); monitor_area.x = geometry.x; monitor_area.y = geometry.y; monitor_area.width = geometry.width; monitor_area.height = geometry.height; texture1 = self->background_image1 ? meta_background_image_get_texture (self->background_image1) : NULL; texture2 = self->background_image2 ? meta_background_image_get_texture (self->background_image2) : NULL; if (texture1 == NULL && texture2 == NULL) { ensure_color_texture (self); if (texture_area) set_texture_area_from_monitor_area (&monitor_area, texture_area); if (wrap_mode) *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; return self->color_texture; } if (texture2 == NULL && self->style == C_DESKTOP_BACKGROUND_STYLE_WALLPAPER && self->shading_direction == C_DESKTOP_BACKGROUND_SHADING_SOLID && ensure_wallpaper_texture (self, texture1)) { if (texture_area) get_texture_area (self, &monitor_area, monitor_scale, self->wallpaper_texture, texture_area); if (wrap_mode) *wrap_mode = COGL_PIPELINE_WRAP_MODE_REPEAT; return self->wallpaper_texture; } if (monitor->dirty) { GError *catch_error = NULL; gboolean bare_region_visible = FALSE; int texture_width, texture_height; if (meta_is_stage_views_scaled ()) { texture_width = monitor_area.width * monitor_scale; texture_height = monitor_area.height * monitor_scale; } else { texture_width = monitor_area.width; texture_height = monitor_area.height; } if (monitor->texture == NULL) { CoglOffscreen *offscreen; monitor->texture = meta_create_texture (texture_width, texture_height, COGL_TEXTURE_COMPONENTS_RGBA, META_TEXTURE_FLAGS_NONE); offscreen = cogl_offscreen_new_with_texture (monitor->texture); monitor->fbo = COGL_FRAMEBUFFER (offscreen); } if (self->style != C_DESKTOP_BACKGROUND_STYLE_WALLPAPER) { monitor_area.x *= monitor_scale; monitor_area.y *= monitor_scale; monitor_area.width *= monitor_scale; monitor_area.height *= monitor_scale; } if (!cogl_framebuffer_allocate (monitor->fbo, &catch_error)) { /* Texture or framebuffer allocation failed; it's unclear why this happened; * we'll try again the next time this is called. (MetaBackgroundActor * caches the result, so user might be left without a background.) */ cogl_object_unref (monitor->texture); monitor->texture = NULL; cogl_object_unref (monitor->fbo); monitor->fbo = NULL; g_error_free (catch_error); return NULL; } cogl_framebuffer_orthographic (monitor->fbo, 0, 0, monitor_area.width, monitor_area.height, -1., 1.); if (texture2 != NULL && self->blend_factor != 0.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_REPLACE); int mipmap_level; mipmap_level = get_best_mipmap_level (texture2, texture_width, texture_height); cogl_pipeline_set_color4f (pipeline, self->blend_factor, self->blend_factor, self->blend_factor, self->blend_factor); cogl_pipeline_set_layer_texture (pipeline, 0, texture2); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style)); cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level); bare_region_visible = draw_texture (self, monitor->fbo, pipeline, texture2, &monitor_area, monitor_scale); cogl_object_unref (pipeline); } else { cogl_framebuffer_clear4f (monitor->fbo, COGL_BUFFER_BIT_COLOR, 0.0, 0.0, 0.0, 0.0); } if (texture1 != NULL && self->blend_factor != 1.0) { CoglPipeline *pipeline = create_pipeline (PIPELINE_ADD); int mipmap_level; mipmap_level = get_best_mipmap_level (texture1, texture_width, texture_height); cogl_pipeline_set_color4f (pipeline, (1 - self->blend_factor), (1 - self->blend_factor), (1 - self->blend_factor), (1 - self->blend_factor));; cogl_pipeline_set_layer_texture (pipeline, 0, texture1); cogl_pipeline_set_layer_wrap_mode (pipeline, 0, get_wrap_mode (self->style)); cogl_pipeline_set_layer_max_mipmap_level (pipeline, 0, mipmap_level); bare_region_visible = bare_region_visible || draw_texture (self, monitor->fbo, pipeline, texture1, &monitor_area, monitor_scale); cogl_object_unref (pipeline); } if (bare_region_visible) { CoglPipeline *pipeline = create_pipeline (PIPELINE_OVER_REVERSE); ensure_color_texture (self); cogl_pipeline_set_layer_texture (pipeline, 0, self->color_texture); cogl_framebuffer_draw_rectangle (monitor->fbo, pipeline, 0, 0, monitor_area.width, monitor_area.height); cogl_object_unref (pipeline); } monitor->dirty = FALSE; } if (texture_area) set_texture_area_from_monitor_area (&geometry, texture_area); if (wrap_mode) *wrap_mode = COGL_PIPELINE_WRAP_MODE_CLAMP_TO_EDGE; return monitor->texture; } MetaBackground * meta_background_new (MetaDisplay *display) { return g_object_new (META_TYPE_BACKGROUND, "meta-display", display, NULL); } void meta_background_set_color (MetaBackground *self, ClutterColor *color) { ClutterColor dummy = { 0 }; g_return_if_fail (META_IS_BACKGROUND (self)); g_return_if_fail (color != NULL); meta_background_set_gradient (self, C_DESKTOP_BACKGROUND_SHADING_SOLID, color, &dummy); } void meta_background_set_gradient (MetaBackground *self, CDesktopBackgroundShading shading_direction, ClutterColor *color, ClutterColor *second_color) { g_return_if_fail (META_IS_BACKGROUND (self)); g_return_if_fail (color != NULL); g_return_if_fail (second_color != NULL); self->shading_direction = shading_direction; self->color = *color; self->second_color = *second_color; free_color_texture (self); free_wallpaper_texture (self); mark_changed (self); } /** * meta_background_set_file: * @self: a #MetaBackground * @file: (nullable): a #GFile representing the background file * @style: the background style to apply * * Set the background to @file */ void meta_background_set_file (MetaBackground *self, GFile *file, CDesktopBackgroundStyle style) { g_return_if_fail (META_IS_BACKGROUND (self)); meta_background_set_blend (self, file, NULL, 0.0, style); } void meta_background_set_blend (MetaBackground *self, GFile *file1, GFile *file2, double blend_factor, CDesktopBackgroundStyle style) { g_return_if_fail (META_IS_BACKGROUND (self)); g_return_if_fail (blend_factor >= 0.0 && blend_factor <= 1.0); set_file (self, &self->file1, &self->background_image1, file1, FALSE); set_file (self, &self->file2, &self->background_image2, file2, FALSE); self->blend_factor = blend_factor; self->style = style; free_wallpaper_texture (self); mark_changed (self); } void meta_background_refresh_all (void) { GSList *l; for (l = all_backgrounds; l; l = l->next) mark_changed (l->data); } muffin-6.4.1/src/compositor/meta-feedback-actor-private.h0000664000175000017500000000472314723361714022334 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * meta-feedback-actor-private.h: Actor for painting user interaction feedback * * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ #ifndef META_FEEDBACK_ACTOR_PRIVATE_H #define META_FEEDBACK_ACTOR_PRIVATE_H #include "clutter/clutter.h" /** * MetaFeedbackActor: * * This class handles the rendering of user interaction feedback */ #define META_TYPE_FEEDBACK_ACTOR (meta_feedback_actor_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaFeedbackActor, meta_feedback_actor, META, FEEDBACK_ACTOR, ClutterActor) struct _MetaFeedbackActorClass { /*< private >*/ ClutterActorClass parent_class; }; ClutterActor *meta_feedback_actor_new (float anchor_x, float anchor_y); void meta_feedback_actor_set_anchor (MetaFeedbackActor *actor, float anchor_x, float anchor_y); void meta_feedback_actor_get_anchor (MetaFeedbackActor *actor, float *anchor_x, float *anchor_y); void meta_feedback_actor_set_position (MetaFeedbackActor *self, float x, float y); void meta_feedback_actor_update (MetaFeedbackActor *self, const ClutterEvent *event); void meta_feedback_actor_set_geometry_scale (MetaFeedbackActor *self, int geometry_scale); int meta_feedback_actor_get_geometry_scale (MetaFeedbackActor *self); #endif /* META_FEEDBACK_ACTOR_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-surface-actor-wayland.c0000664000175000017500000000752614723361714022224 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #include "config.h" #include "compositor/meta-surface-actor-wayland.h" #include #include "backends/meta-backend-private.h" #include "backends/meta-logical-monitor.h" #include "cogl/cogl-wayland-server.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/region-utils.h" #include "wayland/meta-wayland-buffer.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-window-wayland.h" struct _MetaSurfaceActorWayland { MetaSurfaceActor parent; MetaWaylandSurface *surface; }; G_DEFINE_TYPE (MetaSurfaceActorWayland, meta_surface_actor_wayland, META_TYPE_SURFACE_ACTOR) static void meta_surface_actor_wayland_process_damage (MetaSurfaceActor *actor, int x, int y, int width, int height) { meta_surface_actor_update_area (actor, x, y, width, height); } static void meta_surface_actor_wayland_pre_paint (MetaSurfaceActor *actor) { } static gboolean meta_surface_actor_wayland_is_opaque (MetaSurfaceActor *actor) { MetaShapedTexture *stex = meta_surface_actor_get_texture (actor); return meta_shaped_texture_is_opaque (stex); } static void meta_surface_actor_wayland_dispose (GObject *object) { MetaSurfaceActorWayland *self = META_SURFACE_ACTOR_WAYLAND (object); MetaShapedTexture *stex; stex = meta_surface_actor_get_texture (META_SURFACE_ACTOR (self)); if (stex) meta_shaped_texture_set_texture (stex, NULL); if (self->surface) { g_object_remove_weak_pointer (G_OBJECT (self->surface), (gpointer *) &self->surface); self->surface = NULL; } G_OBJECT_CLASS (meta_surface_actor_wayland_parent_class)->dispose (object); } static void meta_surface_actor_wayland_class_init (MetaSurfaceActorWaylandClass *klass) { MetaSurfaceActorClass *surface_actor_class = META_SURFACE_ACTOR_CLASS (klass); GObjectClass *object_class = G_OBJECT_CLASS (klass); surface_actor_class->process_damage = meta_surface_actor_wayland_process_damage; surface_actor_class->pre_paint = meta_surface_actor_wayland_pre_paint; surface_actor_class->is_opaque = meta_surface_actor_wayland_is_opaque; object_class->dispose = meta_surface_actor_wayland_dispose; } static void meta_surface_actor_wayland_init (MetaSurfaceActorWayland *self) { } MetaSurfaceActor * meta_surface_actor_wayland_new (MetaWaylandSurface *surface) { MetaSurfaceActorWayland *self = g_object_new (META_TYPE_SURFACE_ACTOR_WAYLAND, NULL); g_assert (meta_is_wayland_compositor ()); self->surface = surface; g_object_add_weak_pointer (G_OBJECT (self->surface), (gpointer *) &self->surface); return META_SURFACE_ACTOR (self); } MetaWaylandSurface * meta_surface_actor_wayland_get_surface (MetaSurfaceActorWayland *self) { return self->surface; } muffin-6.4.1/src/compositor/meta-sync-ring.h0000664000175000017500000000053514723361714017740 0ustar fabiofabio#ifndef _META_SYNC_RING_H_ #define _META_SYNC_RING_H_ #include #include gboolean meta_sync_ring_init (Display *dpy); void meta_sync_ring_destroy (void); gboolean meta_sync_ring_after_frame (void); gboolean meta_sync_ring_insert_wait (void); void meta_sync_ring_handle_event (XEvent *event); #endif /* _META_SYNC_RING_H_ */ muffin-6.4.1/src/compositor/meta-surface-actor-wayland.h0000664000175000017500000000422414723361714022221 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * * Written by: * Jasper St. Pierre */ #ifndef __META_SURFACE_ACTOR_WAYLAND_H__ #define __META_SURFACE_ACTOR_WAYLAND_H__ #include #include "backends/meta-monitor-manager-private.h" #include "compositor/meta-surface-actor.h" #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland.h" G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR_WAYLAND (meta_surface_actor_wayland_get_type ()) G_DECLARE_FINAL_TYPE (MetaSurfaceActorWayland, meta_surface_actor_wayland, META, SURFACE_ACTOR_WAYLAND, MetaSurfaceActor) MetaSurfaceActor * meta_surface_actor_wayland_new (MetaWaylandSurface *surface); MetaWaylandSurface * meta_surface_actor_wayland_get_surface (MetaSurfaceActorWayland *self); void meta_surface_actor_wayland_surface_destroyed (MetaSurfaceActorWayland *self); double meta_surface_actor_wayland_get_scale (MetaSurfaceActorWayland *actor); void meta_surface_actor_wayland_get_subsurface_rect (MetaSurfaceActorWayland *self, MetaRectangle *rect); void meta_surface_actor_wayland_add_frame_callbacks (MetaSurfaceActorWayland *self, struct wl_list *frame_callbacks); G_END_DECLS #endif /* __META_SURFACE_ACTOR_WAYLAND_H__ */ muffin-6.4.1/src/compositor/cogl-utils.h0000664000175000017500000000247714723361714017174 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Utilities for use with Cogl * * Copyright 2010 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef __META_COGL_UTILS_H__ #define __META_COGL_UTILS_H__ #include "cogl/cogl.h" CoglPipeline * meta_create_texture_pipeline (CoglTexture *texture); typedef enum { META_TEXTURE_FLAGS_NONE = 0, META_TEXTURE_ALLOW_SLICING = 1 << 1 } MetaTextureFlags; CoglTexture *meta_create_texture (int width, int height, CoglTextureComponents components, MetaTextureFlags flags); #endif /* __META_COGL_UTILS_H__ */ muffin-6.4.1/src/compositor/meta-compositor-x11.c0000664000175000017500000004014014723361714020623 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #include "config.h" #include "compositor/meta-compositor-x11.h" #include #include #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-event-x11.h" #include "clutter/x11/clutter-x11.h" #include "compositor/meta-sync-ring.h" #include "compositor/meta-window-actor-x11.h" #include "core/display-private.h" #include "core/window-private.h" #include "x11/meta-x11-display-private.h" #include "x11/meta-x11-background-actor-private.h" struct _MetaCompositorX11 { MetaCompositor parent; Window output; gulong before_update_handler_id; gulong after_update_handler_id; gboolean frame_has_updated_xsurfaces; gboolean have_x11_sync_object; MetaWindow *unredirected_window; gboolean xserver_uses_monotonic_clock; int64_t xserver_time_query_time_us; int64_t xserver_time_offset_us; gboolean randr_scale_disabled; }; G_DEFINE_TYPE (MetaCompositorX11, meta_compositor_x11, META_TYPE_COMPOSITOR) static void on_before_update (ClutterStage *stage, MetaCompositor *compositor); static void on_after_update (ClutterStage *stage, MetaCompositor *compositor); static void process_damage (MetaCompositorX11 *compositor_x11, XDamageNotifyEvent *damage_xevent, MetaWindow *window) { MetaWindowActor *window_actor = meta_window_actor_from_window (window); MetaWindowActorX11 *window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); meta_window_actor_x11_process_damage (window_actor_x11, damage_xevent); compositor_x11->frame_has_updated_xsurfaces = TRUE; } void meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11, XEvent *xevent, MetaWindow *window) { MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); MetaDisplay *display = meta_compositor_get_display (compositor); MetaX11Display *x11_display = display->x11_display; int damage_event_base; damage_event_base = meta_x11_display_get_damage_event_base (x11_display); if (xevent->type == damage_event_base + XDamageNotify) { /* * Core code doesn't handle damage events, so we need to extract the * MetaWindow ourselves. */ if (!window) { Window xwindow; xwindow = ((XDamageNotifyEvent *) xevent)->drawable; window = meta_x11_display_lookup_x_window (x11_display, xwindow); } if (window) process_damage (compositor_x11, (XDamageNotifyEvent *) xevent, window); } else if (xevent->type == PropertyNotify) { if (((XPropertyEvent *) xevent)->atom == x11_display->atom_x_root_pixmap) { if (((XPropertyEvent *) xevent)->window == meta_x11_display_get_xroot (x11_display)) { meta_x11_background_actor_update (display); return; } } } if (compositor_x11->have_x11_sync_object) meta_sync_ring_handle_event (xevent); /* * Clutter needs to know about MapNotify events otherwise it will think the * stage is invisible */ if (xevent->type == MapNotify) meta_x11_handle_event (xevent); } static void determine_server_clock_source (MetaCompositorX11 *compositor_x11) { MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); MetaDisplay *display = meta_compositor_get_display (compositor); MetaX11Display *x11_display = display->x11_display; uint32_t server_time_ms; int64_t server_time_us; int64_t translated_monotonic_now_us; server_time_ms = meta_x11_display_get_current_time_roundtrip (x11_display); server_time_us = ms2us (server_time_ms); translated_monotonic_now_us = meta_translate_to_high_res_xserver_time (g_get_monotonic_time ()); /* If the server time offset is within a second of the monotonic time, we * assume that they are identical. This seems like a big margin, but we want * to be as robust as possible even if the system is under load and our * processing of the server response is delayed. */ if (ABS (server_time_us - translated_monotonic_now_us) < s2us (1)) compositor_x11->xserver_uses_monotonic_clock = TRUE; else compositor_x11->xserver_uses_monotonic_clock = FALSE; } static void meta_compositor_x11_manage (MetaCompositor *compositor) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); MetaDisplay *display = meta_compositor_get_display (compositor); Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); MetaBackend *backend = meta_get_backend (); Window xwindow; determine_server_clock_source (compositor_x11); meta_x11_display_set_cm_selection (display->x11_display); compositor_x11->output = display->x11_display->composite_overlay_window; xwindow = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)); XReparentWindow (xdisplay, xwindow, compositor_x11->output, 0, 0); meta_x11_display_clear_stage_input_region (display->x11_display); /* * Make sure there isn't any left-over output shape on the overlay window by * setting the whole screen to be an output region. * * Note: there doesn't seem to be any real chance of that because the X * server will destroy the overlay window when the last client using it * exits. */ XFixesSetWindowShapeRegion (xdisplay, compositor_x11->output, ShapeBounding, 0, 0, None); /* * Map overlay window before redirecting windows offscreen so we catch their * contents until we show the stage. */ XMapWindow (xdisplay, compositor_x11->output); ClutterStage *stage = meta_compositor_get_stage (compositor); compositor_x11->before_update_handler_id = g_signal_connect (stage, "before-update", G_CALLBACK (on_before_update), compositor); compositor_x11->after_update_handler_id = g_signal_connect (stage, "after-update", G_CALLBACK (on_after_update), compositor); compositor_x11->have_x11_sync_object = meta_sync_ring_init (xdisplay); meta_compositor_redirect_x11_windows (META_COMPOSITOR (compositor)); } static void meta_compositor_x11_unmanage (MetaCompositor *compositor) { MetaDisplay *display = meta_compositor_get_display (compositor); MetaX11Display *x11_display = display->x11_display; Display *xdisplay = x11_display->xdisplay; Window xroot = x11_display->xroot; /* * This is the most important part of cleanup - we have to do this before * giving up the window manager selection or the next window manager won't be * able to redirect subwindows */ XCompositeUnredirectSubwindows (xdisplay, xroot, CompositeRedirectManual); } /* * Sets an bounding shape on the COW so that the given window * is exposed. If window is %NULL it clears the shape again. * * Used so we can unredirect windows, by shaping away the part * of the COW, letting the raw window be seen through below. */ static void shape_cow_for_window (MetaCompositorX11 *compositor_x11, MetaWindow *window) { MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); MetaDisplay *display = meta_compositor_get_display (compositor); Display *xdisplay = meta_x11_display_get_xdisplay (display->x11_display); if (!window) { XFixesSetWindowShapeRegion (xdisplay, compositor_x11->output, ShapeBounding, 0, 0, None); } else { XserverRegion output_region; XRectangle screen_rect, window_bounds; int width, height; MetaRectangle rect; meta_window_get_frame_rect (window, &rect); window_bounds.x = rect.x; window_bounds.y = rect.y; window_bounds.width = rect.width; window_bounds.height = rect.height; meta_display_get_size (display, &width, &height); screen_rect.x = 0; screen_rect.y = 0; screen_rect.width = width; screen_rect.height = height; output_region = XFixesCreateRegion (xdisplay, &window_bounds, 1); XFixesInvertRegion (xdisplay, output_region, &screen_rect, output_region); XFixesSetWindowShapeRegion (xdisplay, compositor_x11->output, ShapeBounding, 0, 0, output_region); XFixesDestroyRegion (xdisplay, output_region); } } static void set_unredirected_window (MetaCompositorX11 *compositor_x11, MetaWindow *window) { MetaWindow *prev_unredirected_window = compositor_x11->unredirected_window; if (prev_unredirected_window == window) return; if (prev_unredirected_window) { MetaWindowActor *window_actor; MetaWindowActorX11 *window_actor_x11; window_actor = meta_window_actor_from_window (prev_unredirected_window); window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); meta_window_actor_x11_set_unredirected (window_actor_x11, FALSE); } shape_cow_for_window (compositor_x11, window); compositor_x11->unredirected_window = window; if (window) { MetaWindowActor *window_actor; MetaWindowActorX11 *window_actor_x11; window_actor = meta_window_actor_from_window (window); window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); meta_window_actor_x11_set_unredirected (window_actor_x11, TRUE); } } static void maybe_unredirect_top_window (MetaCompositorX11 *compositor_x11) { MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); MetaWindow *window_to_unredirect = NULL; MetaWindowActor *window_actor; MetaWindowActorX11 *window_actor_x11; if (meta_compositor_is_unredirect_inhibited (compositor)) goto out; window_actor = meta_compositor_get_top_window_actor (compositor); if (!window_actor) goto out; window_actor_x11 = META_WINDOW_ACTOR_X11 (window_actor); if (!meta_window_actor_x11_should_unredirect (window_actor_x11)) goto out; window_to_unredirect = meta_window_actor_get_meta_window (window_actor); out: set_unredirected_window (compositor_x11, window_to_unredirect); } static void on_before_update (ClutterStage *stage, MetaCompositor *compositor) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); if (compositor_x11->frame_has_updated_xsurfaces) { MetaDisplay *display = meta_compositor_get_display (compositor); /* * We need to make sure that any X drawing that happens before the * XDamageSubtract() for each window above is visible to subsequent GL * rendering; the standardized way to do this is GL_EXT_X11_sync_object. * Since this isn't implemented yet in mesa, we also have a path that * relies on the implementation of the open source drivers. * * Anything else, we just hope for the best. * * Xorg and open source driver specifics: * * The X server makes sure to flush drawing to the kernel before sending * out damage events, but since we use DamageReportBoundingBox there may * be drawing between the last damage event and the XDamageSubtract() * that needs to be flushed as well. * * Xorg always makes sure that drawing is flushed to the kernel before * writing events or responses to the client, so any round trip request * at this point is sufficient to flush the GLX buffers. */ if (compositor_x11->have_x11_sync_object) compositor_x11->have_x11_sync_object = meta_sync_ring_insert_wait (); else XSync (display->x11_display->xdisplay, False); } } static void on_after_update (ClutterStage *stage, MetaCompositor *compositor) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); if (compositor_x11->frame_has_updated_xsurfaces) { if (compositor_x11->have_x11_sync_object) compositor_x11->have_x11_sync_object = meta_sync_ring_after_frame (); compositor_x11->frame_has_updated_xsurfaces = FALSE; } } static void meta_compositor_x11_pre_paint (MetaCompositor *compositor) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); MetaCompositorClass *parent_class; maybe_unredirect_top_window (compositor_x11); parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class); parent_class->pre_paint (compositor); } static void meta_compositor_x11_remove_window (MetaCompositor *compositor, MetaWindow *window) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); MetaCompositorClass *parent_class; if (compositor_x11->unredirected_window == window) set_unredirected_window (compositor_x11, NULL); parent_class = META_COMPOSITOR_CLASS (meta_compositor_x11_parent_class); parent_class->remove_window (compositor, window); } static int64_t meta_compositor_x11_monotonic_to_high_res_xserver_time (MetaCompositor *compositor, int64_t monotonic_time_us) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (compositor); int64_t now_us; if (compositor_x11->xserver_uses_monotonic_clock) return meta_translate_to_high_res_xserver_time (monotonic_time_us); now_us = g_get_monotonic_time (); if (compositor_x11->xserver_time_query_time_us == 0 || now_us > (compositor_x11->xserver_time_query_time_us + s2us (10))) { MetaDisplay *display = meta_compositor_get_display (compositor); MetaX11Display *x11_display = display->x11_display; uint32_t xserver_time_ms; int64_t xserver_time_us; compositor_x11->xserver_time_query_time_us = now_us; xserver_time_ms = meta_x11_display_get_current_time_roundtrip (x11_display); xserver_time_us = ms2us (xserver_time_ms); compositor_x11->xserver_time_offset_us = xserver_time_us - now_us; } return monotonic_time_us + compositor_x11->xserver_time_offset_us; } Window meta_compositor_x11_get_output_xwindow (MetaCompositorX11 *compositor_x11) { return compositor_x11->output; } MetaCompositorX11 * meta_compositor_x11_new (MetaDisplay *display) { return g_object_new (META_TYPE_COMPOSITOR_X11, "display", display, NULL); } static void meta_compositor_x11_dispose (GObject *object) { MetaCompositorX11 *compositor_x11 = META_COMPOSITOR_X11 (object); MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); ClutterStage *stage = meta_compositor_get_stage (compositor); if (compositor_x11->have_x11_sync_object) { meta_sync_ring_destroy (); compositor_x11->have_x11_sync_object = FALSE; } g_clear_signal_handler (&compositor_x11->before_update_handler_id, stage); g_clear_signal_handler (&compositor_x11->after_update_handler_id, stage); G_OBJECT_CLASS (meta_compositor_x11_parent_class)->dispose (object); } static void meta_compositor_x11_init (MetaCompositorX11 *compositor_x11) { } static void meta_compositor_x11_class_init (MetaCompositorX11Class *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); MetaCompositorClass *compositor_class = META_COMPOSITOR_CLASS (klass); object_class->dispose = meta_compositor_x11_dispose; compositor_class->manage = meta_compositor_x11_manage; compositor_class->unmanage = meta_compositor_x11_unmanage; compositor_class->pre_paint = meta_compositor_x11_pre_paint; compositor_class->remove_window = meta_compositor_x11_remove_window; compositor_class->monotonic_to_high_res_xserver_time = meta_compositor_x11_monotonic_to_high_res_xserver_time; } muffin-6.4.1/src/compositor/meta-surface-actor.h0000664000175000017500000000567014723361714020572 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_SURFACE_ACTOR_PRIVATE_H #define META_SURFACE_ACTOR_PRIVATE_H #include "config.h" #include "backends/meta-backend-types.h" #include "meta/meta-shaped-texture.h" #include "meta/window.h" G_BEGIN_DECLS #define META_TYPE_SURFACE_ACTOR (meta_surface_actor_get_type ()) G_DECLARE_DERIVABLE_TYPE (MetaSurfaceActor, meta_surface_actor, META, SURFACE_ACTOR, ClutterActor) struct _MetaSurfaceActorClass { /*< private >*/ ClutterActorClass parent_class; void (* process_damage) (MetaSurfaceActor *actor, int x, int y, int width, int height); void (* pre_paint) (MetaSurfaceActor *actor); gboolean (* is_opaque) (MetaSurfaceActor *actor); }; cairo_surface_t *meta_surface_actor_get_image (MetaSurfaceActor *self, cairo_rectangle_int_t *clip); MetaShapedTexture *meta_surface_actor_get_texture (MetaSurfaceActor *self); void meta_surface_actor_update_area (MetaSurfaceActor *self, int x, int y, int width, int height); gboolean meta_surface_actor_is_obscured (MetaSurfaceActor *self); void meta_surface_actor_set_input_region (MetaSurfaceActor *self, cairo_region_t *region); void meta_surface_actor_set_opaque_region (MetaSurfaceActor *self, cairo_region_t *region); cairo_region_t * meta_surface_actor_get_opaque_region (MetaSurfaceActor *self); void meta_surface_actor_process_damage (MetaSurfaceActor *actor, int x, int y, int width, int height); void meta_surface_actor_pre_paint (MetaSurfaceActor *actor); gboolean meta_surface_actor_is_opaque (MetaSurfaceActor *actor); gboolean meta_surface_actor_is_frozen (MetaSurfaceActor *actor); void meta_surface_actor_set_frozen (MetaSurfaceActor *actor, gboolean frozen); void meta_surface_actor_set_transform (MetaSurfaceActor *self, MetaMonitorTransform transform); void meta_surface_actor_set_viewport_src_rect (MetaSurfaceActor *self, graphene_rect_t *src_rect); void meta_surface_actor_reset_viewport_src_rect (MetaSurfaceActor *self); void meta_surface_actor_set_viewport_dst_size (MetaSurfaceActor *self, int dst_width, int dst_height); void meta_surface_actor_reset_viewport_dst_size (MetaSurfaceActor *self); G_END_DECLS #endif /* META_SURFACE_ACTOR_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-window-actor.c0000664000175000017500000012733614723361714020450 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:meta-window-actor * @title: MetaWindowActor * @short_description: An actor representing a top-level window in the scene * graph * * #MetaWindowActor is a #ClutterActor that adds a notion of a window to the * Clutter scene graph. It contains a #MetaWindow which provides the windowing * API, and the #MetaCompositor that handles it. For the actual content of the * window, it contains a #MetaSurfaceActor. * * #MetaWindowActor takes care of the rendering features you need for your * window. For example, it will take the windows' requested opacity and use * that for clutter_actor_set_opacity(). Furthermore, it will also draw a * shadow around the window (using #MetaShadow) and deal with synchronization * between events of the window and the actual render loop. See * MetaWindowActor::first-frame for an example of the latter. */ #include "config.h" #include #include #include #include "backends/meta-screen-cast-window.h" #include "compositor/compositor-private.h" #include "compositor/meta-cullable.h" #include "compositor/meta-shaped-texture-private.h" #include "compositor/meta-surface-actor-x11.h" #include "compositor/meta-surface-actor.h" #include "compositor/meta-window-actor-private.h" #include "core/boxes-private.h" #include "core/window-private.h" #include "meta/window.h" #ifdef HAVE_WAYLAND #include "compositor/meta-surface-actor-wayland.h" #include "wayland/meta-wayland-surface.h" #endif typedef enum { INITIALLY_FROZEN, DRAWING_FIRST_FRAME, EMITTED_FIRST_FRAME } FirstFrameState; typedef struct _MetaWindowActorPrivate { MetaWindow *window; MetaCompositor *compositor; MetaSurfaceActor *surface; int geometry_scale; /* * These need to be counters rather than flags, since more plugins * can implement same effect; the practicality of stacking effects * might be dubious, but we have to at least handle it correctly. */ gint minimize_in_progress; gint unminimize_in_progress; gint size_change_in_progress; gint map_in_progress; gint destroy_in_progress; guint freeze_count; guint visible : 1; guint disposed : 1; guint needs_destroy : 1; guint updates_frozen : 1; guint first_frame_state : 2; /* FirstFrameState */ } MetaWindowActorPrivate; enum { FIRST_FRAME, EFFECTS_COMPLETED, DAMAGED, THAWED, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; enum { PROP_META_WINDOW = 1, }; static void meta_window_actor_dispose (GObject *object); static void meta_window_actor_constructed (GObject *object); static void meta_window_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); static void meta_window_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); static void meta_window_actor_real_assign_surface_actor (MetaWindowActor *self, MetaSurfaceActor *surface_actor); static void cullable_iface_init (MetaCullableInterface *iface); static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface); G_DEFINE_ABSTRACT_TYPE_WITH_CODE (MetaWindowActor, meta_window_actor, CLUTTER_TYPE_ACTOR, G_ADD_PRIVATE (MetaWindowActor) G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init) G_IMPLEMENT_INTERFACE (META_TYPE_SCREEN_CAST_WINDOW, screen_cast_window_iface_init)); static void meta_window_actor_class_init (MetaWindowActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->dispose = meta_window_actor_dispose; object_class->set_property = meta_window_actor_set_property; object_class->get_property = meta_window_actor_get_property; object_class->constructed = meta_window_actor_constructed; klass->assign_surface_actor = meta_window_actor_real_assign_surface_actor; /** * MetaWindowActor::first-frame: * @actor: the #MetaWindowActor instance * * The ::first-frame signal will be emitted the first time a frame * of window contents has been drawn by the application and Mutter * has had the chance to drawn that frame to the screen. If the * window starts off initially hidden, obscured, or on on a * different workspace, the ::first-frame signal will be emitted * even though the user doesn't see the contents. * * MetaDisplay::window-created is a good place to connect to this * signal - at that point, the MetaWindowActor for the window * exists, but the window has reliably not yet been drawn. * Connecting to an existing window that has already been drawn to * the screen is not useful. */ signals[FIRST_FRAME] = g_signal_new ("first-frame", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindowActor::effects-completed: * @actor: the #MetaWindowActor instance * * The ::effects-completed signal will be emitted once all pending compositor * effects are completed. */ signals[EFFECTS_COMPLETED] = g_signal_new ("effects-completed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindowActor::damaged: * @actor: the #MetaWindowActor instance * * Notify that one or more of the surfaces of the window have been damaged. */ signals[DAMAGED] = g_signal_new ("damaged", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); /** * MetaWindowActor::thawed: * @actor: the #MetaWindowActor instance */ signals[THAWED] = g_signal_new ("thawed", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); pspec = g_param_spec_object ("meta-window", "MetaWindow", "The displayed MetaWindow", META_TYPE_WINDOW, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_META_WINDOW, pspec); } static void meta_window_actor_init (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); priv->geometry_scale = 1; } static void window_appears_focused_notify (MetaWindow *mw, GParamSpec *arg1, gpointer data) { clutter_actor_queue_redraw (CLUTTER_ACTOR (data)); } gboolean meta_window_actor_is_opaque (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; if (window->opacity != 0xff) return FALSE; if (!priv->surface) return FALSE; return meta_surface_actor_is_opaque (priv->surface); } gboolean meta_window_actor_is_frozen (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); return priv->surface == NULL || priv->freeze_count > 0; } void meta_window_actor_update_regions (MetaWindowActor *self) { META_WINDOW_ACTOR_GET_CLASS (self)->update_regions (self); } static void meta_window_actor_set_frozen (MetaWindowActor *self, gboolean frozen) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (meta_surface_actor_is_frozen (priv->surface) == frozen) return; meta_surface_actor_set_frozen (priv->surface, frozen); META_WINDOW_ACTOR_GET_CLASS (self)->set_frozen (self, frozen); } static void meta_window_actor_freeze (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (priv->freeze_count == 0 && priv->surface) meta_window_actor_set_frozen (self, TRUE); priv->freeze_count ++; } static void meta_window_actor_sync_thawed_state (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (priv->first_frame_state == INITIALLY_FROZEN) priv->first_frame_state = DRAWING_FIRST_FRAME; if (priv->surface) meta_window_actor_set_frozen (self, FALSE); /* We sometimes ignore moves and resizes on frozen windows */ meta_window_actor_sync_actor_geometry (self, FALSE); } static void meta_window_actor_thaw (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (priv->freeze_count <= 0) g_error ("Error in freeze/thaw accounting"); priv->freeze_count--; if (priv->freeze_count > 0) return; /* We still might be frozen due to lack of a MetaSurfaceActor */ if (meta_window_actor_is_frozen (self)) return; meta_window_actor_sync_thawed_state (self); g_signal_emit (self, signals[THAWED], 0); } static void meta_window_actor_real_assign_surface_actor (MetaWindowActor *self, MetaSurfaceActor *surface_actor) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); g_clear_object (&priv->surface); priv->surface = g_object_ref_sink (surface_actor); if (meta_window_actor_is_frozen (self)) meta_window_actor_set_frozen (self, TRUE); else meta_window_actor_sync_thawed_state (self); } void meta_window_actor_assign_surface_actor (MetaWindowActor *self, MetaSurfaceActor *surface_actor) { META_WINDOW_ACTOR_GET_CLASS (self)->assign_surface_actor (self, surface_actor); } static void init_surface_actor (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; MetaSurfaceActor *surface_actor; if (!meta_is_wayland_compositor ()) surface_actor = meta_surface_actor_x11_new (window); #ifdef HAVE_WAYLAND else if (window->surface) surface_actor = meta_wayland_surface_get_actor (window->surface); #endif else surface_actor = NULL; if (surface_actor) meta_window_actor_assign_surface_actor (self, surface_actor); } static void meta_window_actor_constructed (GObject *object) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; priv->compositor = window->display->compositor; /* Hang our compositor window state off the MetaWindow for fast retrieval */ meta_window_set_compositor_private (window, object); init_surface_actor (self); meta_window_actor_update_opacity (self); meta_window_actor_sync_updates_frozen (self); if (meta_window_actor_is_frozen (self)) priv->first_frame_state = INITIALLY_FROZEN; else priv->first_frame_state = DRAWING_FIRST_FRAME; meta_window_actor_sync_actor_geometry (self, priv->window->placed); } static void meta_window_actor_dispose (GObject *object) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaCompositor *compositor = priv->compositor; if (priv->disposed) { G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object); return; } priv->disposed = TRUE; meta_compositor_remove_window_actor (compositor, self); g_clear_object (&priv->window); if (priv->surface) { clutter_actor_remove_child (CLUTTER_ACTOR (self), CLUTTER_ACTOR (priv->surface)); g_clear_object (&priv->surface); } G_OBJECT_CLASS (meta_window_actor_parent_class)->dispose (object); } static void meta_window_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); switch (prop_id) { case PROP_META_WINDOW: priv->window = g_value_dup_object (value); g_signal_connect_object (priv->window, "notify::appears-focused", G_CALLBACK (window_appears_focused_notify), self, 0); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_window_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaWindowActor *self = META_WINDOW_ACTOR (object); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); switch (prop_id) { case PROP_META_WINDOW: g_value_set_object (value, priv->window); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } /** * meta_window_actor_get_meta_window: * @self: a #MetaWindowActor * * Gets the #MetaWindow object that the the #MetaWindowActor is displaying * * Return value: (transfer none): the displayed #MetaWindow */ MetaWindow * meta_window_actor_get_meta_window (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); return priv->window; } /** * meta_window_actor_get_texture: * @self: a #MetaWindowActor * * Gets the ClutterActor that is used to display the contents of the window, * or NULL if no texture is shown yet, because the window is not mapped. * * Return value: (transfer none): the #ClutterActor for the contents */ MetaShapedTexture * meta_window_actor_get_texture (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (priv->surface) return meta_surface_actor_get_texture (priv->surface); else return NULL; } /** * meta_window_actor_get_surface: * @self: a #MetaWindowActor * * Gets the MetaSurfaceActor that draws the content of this window, * or NULL if there is no surface yet associated with this window. * * Return value: (transfer none): the #MetaSurfaceActor for the contents */ MetaSurfaceActor * meta_window_actor_get_surface (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); return priv->surface; } /** * meta_window_actor_is_destroyed: * @self: a #MetaWindowActor * * Gets whether the X window that the actor was displaying has been destroyed * * Return value: %TRUE when the window is destroyed, otherwise %FALSE */ gboolean meta_window_actor_is_destroyed (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); return priv->disposed || priv->needs_destroy; } void meta_window_actor_queue_frame_drawn (MetaWindowActor *self, gboolean no_delay_frame) { META_WINDOW_ACTOR_GET_CLASS (self)->queue_frame_drawn (self, no_delay_frame); } gboolean meta_window_actor_effect_in_progress (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); return (priv->minimize_in_progress || priv->size_change_in_progress || priv->map_in_progress || priv->destroy_in_progress); } static gboolean is_freeze_thaw_effect (MetaPluginEffect event) { switch (event) { case META_PLUGIN_DESTROY: case META_PLUGIN_SIZE_CHANGE: case META_PLUGIN_UNMINIMIZE: return TRUE; break; default: return FALSE; } } static gboolean start_simple_effect (MetaWindowActor *self, MetaPluginEffect event) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaCompositor *compositor = priv->compositor; MetaPluginManager *plugin_mgr = meta_compositor_get_plugin_manager (compositor); gint *counter = NULL; gboolean use_freeze_thaw = FALSE; g_assert (plugin_mgr != NULL); switch (event) { case META_PLUGIN_NONE: return FALSE; case META_PLUGIN_MINIMIZE: counter = &priv->minimize_in_progress; break; case META_PLUGIN_UNMINIMIZE: counter = &priv->unminimize_in_progress; break; case META_PLUGIN_MAP: counter = &priv->map_in_progress; break; case META_PLUGIN_DESTROY: counter = &priv->destroy_in_progress; break; case META_PLUGIN_SIZE_CHANGE: case META_PLUGIN_SWITCH_WORKSPACE: g_assert_not_reached (); break; } g_assert (counter); use_freeze_thaw = is_freeze_thaw_effect (event); if (use_freeze_thaw) meta_window_actor_freeze (self); (*counter)++; if (!meta_plugin_manager_event_simple (plugin_mgr, self, event)) { (*counter)--; if (use_freeze_thaw) meta_window_actor_thaw (self); return FALSE; } return TRUE; } static void meta_window_actor_after_effects (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (priv->needs_destroy) { clutter_actor_destroy (CLUTTER_ACTOR (self)); return; } g_signal_emit (self, signals[EFFECTS_COMPLETED], 0); meta_window_actor_sync_visibility (self); meta_window_actor_sync_actor_geometry (self, FALSE); } void meta_window_actor_effect_completed (MetaWindowActor *self, MetaPluginEffect event) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); gboolean inconsistent = FALSE; /* NB: Keep in mind that when effects get completed it possible * that the corresponding MetaWindow may have be been destroyed. * In this case priv->window will == NULL */ switch (event) { case META_PLUGIN_NONE: break; case META_PLUGIN_MINIMIZE: { priv->minimize_in_progress--; if (priv->minimize_in_progress < 0) { g_warning ("Error in minimize accounting."); priv->minimize_in_progress = 0; inconsistent = TRUE; } } break; case META_PLUGIN_UNMINIMIZE: { priv->unminimize_in_progress--; if (priv->unminimize_in_progress < 0) { g_warning ("Error in unminimize accounting."); priv->unminimize_in_progress = 0; inconsistent = TRUE; } } break; case META_PLUGIN_MAP: /* * Make sure that the actor is at the correct place in case * the plugin fscked. */ priv->map_in_progress--; if (priv->map_in_progress < 0) { g_warning ("Error in map accounting."); priv->map_in_progress = 0; inconsistent = TRUE; } break; case META_PLUGIN_DESTROY: priv->destroy_in_progress--; if (priv->destroy_in_progress < 0) { g_warning ("Error in destroy accounting."); priv->destroy_in_progress = 0; inconsistent = TRUE; } break; case META_PLUGIN_SIZE_CHANGE: priv->size_change_in_progress--; if (priv->size_change_in_progress < 0) { g_warning ("Error in size change accounting."); priv->size_change_in_progress = 0; inconsistent = TRUE; } break; case META_PLUGIN_SWITCH_WORKSPACE: g_assert_not_reached (); break; } if (is_freeze_thaw_effect (event) && !inconsistent) meta_window_actor_thaw (self); if (!meta_window_actor_effect_in_progress (self)) meta_window_actor_after_effects (self); } void meta_window_actor_queue_destroy (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; MetaWindowType window_type = meta_window_get_window_type (window); meta_window_set_compositor_private (window, NULL); META_WINDOW_ACTOR_GET_CLASS (self)->queue_destroy (self); if (window_type == META_WINDOW_DROPDOWN_MENU || window_type == META_WINDOW_POPUP_MENU || window_type == META_WINDOW_TOOLTIP || window_type == META_WINDOW_NOTIFICATION || window_type == META_WINDOW_COMBO || window_type == META_WINDOW_DND || window_type == META_WINDOW_OVERRIDE_OTHER) { /* * No effects, just kill it. */ clutter_actor_destroy (CLUTTER_ACTOR (self)); return; } priv->needs_destroy = TRUE; if (!meta_window_actor_effect_in_progress (self)) clutter_actor_destroy (CLUTTER_ACTOR (self)); } MetaWindowActorChanges meta_window_actor_sync_actor_geometry (MetaWindowActor *self, gboolean did_placement) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaRectangle window_rect; ClutterActor *actor = CLUTTER_ACTOR (self); MetaWindowActorChanges changes = 0; meta_window_get_buffer_rect (priv->window, &window_rect); /* When running as a Wayland compositor we catch size changes when new * buffers are attached */ if (META_IS_SURFACE_ACTOR_X11 (priv->surface)) meta_surface_actor_x11_set_size (META_SURFACE_ACTOR_X11 (priv->surface), window_rect.width, window_rect.height); /* Normally we want freezing a window to also freeze its position; this allows * windows to atomically move and resize together, either under app control, * or because the user is resizing from the left/top. But on initial placement * we need to assign a position, since immediately after the window * is shown, the map effect will go into effect and prevent further geometry * updates. */ if (meta_window_actor_is_frozen (self) && !did_placement) return META_WINDOW_ACTOR_CHANGE_POSITION | META_WINDOW_ACTOR_CHANGE_SIZE; if (meta_window_actor_effect_in_progress (self)) return META_WINDOW_ACTOR_CHANGE_POSITION | META_WINDOW_ACTOR_CHANGE_SIZE; if (clutter_actor_has_allocation (actor)) { ClutterActorBox box; float old_x, old_y; float old_width, old_height; clutter_actor_get_allocation_box (actor, &box); old_x = box.x1; old_y = box.y1; old_width = box.x2 - box.x1; old_height = box.y2 - box.y1; if (old_x != window_rect.x || old_y != window_rect.y) changes |= META_WINDOW_ACTOR_CHANGE_POSITION; if (old_width != window_rect.width || old_height != window_rect.height) changes |= META_WINDOW_ACTOR_CHANGE_SIZE; } else { changes = META_WINDOW_ACTOR_CHANGE_POSITION | META_WINDOW_ACTOR_CHANGE_SIZE; } if (changes & META_WINDOW_ACTOR_CHANGE_POSITION) clutter_actor_set_position (actor, window_rect.x, window_rect.y); if (changes & META_WINDOW_ACTOR_CHANGE_SIZE) clutter_actor_set_size (actor, window_rect.width, window_rect.height); return changes; } void meta_window_actor_show (MetaWindowActor *self, MetaCompEffect effect) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaCompositor *compositor = priv->compositor; MetaPluginEffect event; g_return_if_fail (!priv->visible); priv->visible = TRUE; switch (effect) { case META_COMP_EFFECT_CREATE: event = META_PLUGIN_MAP; break; case META_COMP_EFFECT_UNMINIMIZE: event = META_PLUGIN_UNMINIMIZE; break; case META_COMP_EFFECT_NONE: event = META_PLUGIN_NONE; break; default: g_assert_not_reached(); } if (event == META_PLUGIN_MAP) meta_window_actor_sync_actor_geometry (self, TRUE); if (meta_compositor_is_switching_workspace (compositor) || !start_simple_effect (self, event)) { clutter_actor_show (CLUTTER_ACTOR (self)); } } void meta_window_actor_hide (MetaWindowActor *self, MetaCompEffect effect) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaCompositor *compositor = priv->compositor; MetaPluginEffect event; g_return_if_fail (priv->visible); priv->visible = FALSE; /* If a plugin is animating a workspace transition, we have to * hold off on hiding the window, and do it after the workspace * switch completes */ if (meta_compositor_is_switching_workspace (compositor)) return; switch (effect) { case META_COMP_EFFECT_DESTROY: event = META_PLUGIN_DESTROY; break; case META_COMP_EFFECT_MINIMIZE: event = META_PLUGIN_MINIMIZE; break; case META_COMP_EFFECT_NONE: event = META_PLUGIN_NONE; break; default: g_assert_not_reached(); } if (!start_simple_effect (self, event)) clutter_actor_hide (CLUTTER_ACTOR (self)); } void meta_window_actor_size_change (MetaWindowActor *self, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaCompositor *compositor = priv->compositor; MetaPluginManager *plugin_mgr = meta_compositor_get_plugin_manager (compositor); priv->size_change_in_progress++; meta_window_actor_freeze (self); if (!meta_plugin_manager_event_size_change (plugin_mgr, self, which_change, old_frame_rect, old_buffer_rect)) { priv->size_change_in_progress--; meta_window_actor_thaw (self); } } #if 0 /* Print out a region; useful for debugging */ static void print_region (cairo_region_t *region) { int n_rects; int i; n_rects = cairo_region_num_rectangles (region); g_print ("["); for (i = 0; i < n_rects; i++) { cairo_rectangle_int_t rect; cairo_region_get_rectangle (region, i, &rect); g_print ("+%d+%dx%dx%d ", rect.x, rect.y, rect.width, rect.height); } g_print ("]\n"); } #endif #if 0 /* Dump a region to a PNG file; useful for debugging */ static void see_region (cairo_region_t *region, int width, int height, char *filename) { cairo_surface_t *surface = cairo_image_surface_create (CAIRO_FORMAT_A8, width, height); cairo_t *cr = cairo_create (surface); gdk_cairo_region (cr, region); cairo_fill (cr); cairo_surface_write_to_png (surface, filename); cairo_destroy (cr); cairo_surface_destroy (surface); } #endif static void meta_window_actor_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } static void meta_window_actor_reset_culling (MetaCullable *cullable) { meta_cullable_reset_culling_children (cullable); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_window_actor_cull_out; iface->reset_culling = meta_window_actor_reset_culling; } void meta_window_actor_sync_visibility (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); if (CLUTTER_ACTOR_IS_VISIBLE (self) != priv->visible) { if (priv->visible) clutter_actor_show (CLUTTER_ACTOR (self)); else clutter_actor_hide (CLUTTER_ACTOR (self)); } } void meta_window_actor_pre_paint (MetaWindowActor *self) { if (meta_window_actor_is_destroyed (self)) return; META_WINDOW_ACTOR_GET_CLASS (self)->pre_paint (self); } void meta_window_actor_post_paint (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); META_WINDOW_ACTOR_GET_CLASS (self)->post_paint (self); if (meta_window_actor_is_destroyed (self)) return; if (priv->first_frame_state == DRAWING_FIRST_FRAME) { priv->first_frame_state = EMITTED_FIRST_FRAME; g_signal_emit (self, signals[FIRST_FRAME], 0); } } void meta_window_actor_frame_complete (MetaWindowActor *self, ClutterFrameInfo *frame_info, gint64 presentation_time) { META_WINDOW_ACTOR_GET_CLASS (self)->frame_complete (self, frame_info, presentation_time); } void meta_window_actor_update_opacity (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; if (priv->surface) clutter_actor_set_opacity (CLUTTER_ACTOR (priv->surface), window->opacity); } static void meta_window_actor_set_updates_frozen (MetaWindowActor *self, gboolean updates_frozen) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); updates_frozen = updates_frozen != FALSE; if (priv->updates_frozen != updates_frozen) { priv->updates_frozen = updates_frozen; if (updates_frozen) meta_window_actor_freeze (self); else meta_window_actor_thaw (self); } } void meta_window_actor_sync_updates_frozen (MetaWindowActor *self) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); MetaWindow *window = priv->window; meta_window_actor_set_updates_frozen (self, meta_window_updates_are_frozen (window)); } MetaWindowActor * meta_window_actor_from_window (MetaWindow *window) { return META_WINDOW_ACTOR (meta_window_get_compositor_private (window)); } void meta_window_actor_set_geometry_scale (MetaWindowActor *window_actor, int geometry_scale) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); CoglMatrix child_transform; if (priv->geometry_scale == geometry_scale) return; priv->geometry_scale = geometry_scale; cogl_matrix_init_identity (&child_transform); cogl_matrix_scale (&child_transform, geometry_scale, geometry_scale, 1); clutter_actor_set_child_transform (CLUTTER_ACTOR (window_actor), &child_transform); } int meta_window_actor_get_geometry_scale (MetaWindowActor *window_actor) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); return priv->geometry_scale; } static void meta_window_actor_get_buffer_bounds (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); MetaShapedTexture *stex; int buffer_scale; stex = meta_surface_actor_get_texture (priv->surface); buffer_scale = meta_shaped_texture_get_buffer_scale (stex); *bounds = (MetaRectangle) { .width = meta_shaped_texture_get_width (stex) * buffer_scale, .height = meta_shaped_texture_get_height (stex) * buffer_scale, }; } static void meta_window_actor_transform_relative_position (MetaScreenCastWindow *screen_cast_window, double x, double y, double *x_out, double *y_out) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); MetaRectangle bounds; graphene_point3d_t v1 = { 0.f, }, v2 = { 0.f, }; meta_window_actor_get_buffer_bounds (screen_cast_window, &bounds); v1.x = CLAMP ((float) x, bounds.x, bounds.x + bounds.width); v1.y = CLAMP ((float) y, bounds.y, bounds.y + bounds.height); clutter_actor_apply_transform_to_point (CLUTTER_ACTOR (priv->surface), &v1, &v2); *x_out = (double) v2.x; *y_out = (double) v2.y; } static gboolean meta_window_actor_transform_cursor_position (MetaScreenCastWindow *screen_cast_window, MetaCursorSprite *cursor_sprite, graphene_point_t *cursor_position, float *out_cursor_scale, graphene_point_t *out_relative_cursor_position) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (window_actor); MetaWindow *window; window = priv->window; if (!meta_window_has_pointer (window)) return FALSE; if (cursor_sprite && meta_cursor_sprite_get_cogl_texture (cursor_sprite) && out_cursor_scale) { MetaShapedTexture *stex; double texture_scale; float cursor_texture_scale; stex = meta_surface_actor_get_texture (priv->surface); texture_scale = meta_shaped_texture_get_buffer_scale (stex); cursor_texture_scale = meta_cursor_sprite_get_texture_scale (cursor_sprite); *out_cursor_scale = texture_scale / cursor_texture_scale; } if (out_relative_cursor_position) { clutter_actor_transform_stage_point (CLUTTER_ACTOR (priv->surface), cursor_position->x, cursor_position->y, &out_relative_cursor_position->x, &out_relative_cursor_position->y); } return TRUE; } static void meta_window_actor_capture_into (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, uint8_t *data) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); cairo_surface_t *image; uint8_t *cr_data; int cr_stride; int cr_width; int cr_height; int bpp = 4; if (meta_window_actor_is_destroyed (window_actor)) return; image = meta_window_actor_get_image (window_actor, bounds); cr_data = cairo_image_surface_get_data (image); cr_width = cairo_image_surface_get_width (image); cr_height = cairo_image_surface_get_height (image); cr_stride = cairo_image_surface_get_stride (image); if (cr_width == bounds->width && cr_height == bounds->height) { memcpy (data, cr_data, cr_height * cr_stride); } else { int width = MIN (bounds->width, cr_width); int height = MIN (bounds->height, cr_height); int stride = width * bpp; uint8_t *src, *dst; src = cr_data; dst = data; for (int i = 0; i < height; i++) { memcpy (dst, src, stride); if (width < bounds->width) memset (dst + stride, 0, (bounds->width * bpp) - stride); src += cr_stride; dst += bounds->width * bpp; } for (int i = height; i < bounds->height; i++) { memset (dst, 0, bounds->width * bpp); dst += bounds->width * bpp; } } cairo_surface_destroy (image); } static gboolean meta_window_actor_blit_to_framebuffer (MetaScreenCastWindow *screen_cast_window, MetaRectangle *bounds, CoglFramebuffer *framebuffer) { MetaWindowActor *window_actor = META_WINDOW_ACTOR (screen_cast_window); ClutterActor *actor = CLUTTER_ACTOR (window_actor); ClutterPaintContext *paint_context; MetaRectangle scaled_clip; CoglColor clear_color; float resource_scale; float width, height; float x, y; if (meta_window_actor_is_destroyed (window_actor)) return FALSE; clutter_actor_get_size (actor, &width, &height); if (width == 0 || height == 0) return FALSE; if (!clutter_actor_get_resource_scale (actor, &resource_scale)) return FALSE; clutter_actor_inhibit_culling (actor); width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); clutter_actor_get_position (actor, &x, &y); cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); cogl_framebuffer_set_viewport (framebuffer, 0, 0, width, height); meta_rectangle_scale_double (bounds, resource_scale, META_ROUNDING_STRATEGY_GROW, &scaled_clip); meta_rectangle_intersect (&scaled_clip, &(MetaRectangle) { .width = width, .height = height, }, &scaled_clip); cogl_framebuffer_push_rectangle_clip (framebuffer, scaled_clip.x, scaled_clip.y, scaled_clip.x + scaled_clip.width, scaled_clip.y + scaled_clip.height); cogl_framebuffer_push_matrix (framebuffer); cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); cogl_framebuffer_translate (framebuffer, -x, -y, 0); paint_context = clutter_paint_context_new_for_framebuffer (framebuffer); clutter_actor_paint (actor, paint_context); clutter_paint_context_destroy (paint_context); cogl_framebuffer_pop_matrix (framebuffer); cogl_framebuffer_pop_clip (framebuffer); clutter_actor_uninhibit_culling (actor); return TRUE; } static gboolean meta_window_actor_has_damage (MetaScreenCastWindow *screen_cast_window) { return clutter_actor_has_damage (CLUTTER_ACTOR (screen_cast_window)); } static void screen_cast_window_iface_init (MetaScreenCastWindowInterface *iface) { iface->get_buffer_bounds = meta_window_actor_get_buffer_bounds; iface->transform_relative_position = meta_window_actor_transform_relative_position; iface->transform_cursor_position = meta_window_actor_transform_cursor_position; iface->capture_into = meta_window_actor_capture_into; iface->blit_to_framebuffer = meta_window_actor_blit_to_framebuffer; iface->has_damage = meta_window_actor_has_damage; } MetaWindowActor * meta_window_actor_from_actor (ClutterActor *actor) { if (!META_IS_SURFACE_ACTOR (actor)) return NULL; do { actor = clutter_actor_get_parent (actor); if (META_IS_WINDOW_ACTOR (actor)) return META_WINDOW_ACTOR (actor); } while (actor != NULL); return NULL; } void meta_window_actor_notify_damaged (MetaWindowActor *window_actor) { g_signal_emit (window_actor, signals[DAMAGED], 0); } /** * meta_window_actor_get_image: * @self: A #MetaWindowActor * @clip: (nullable): A clipping rectangle, to help prevent extra processing. * In the case that the clipping rectangle is partially or fully * outside the bounds of the actor, the rectangle will be clipped. * * Flattens the layers of @self into one ARGB32 image by alpha blending * the images, and returns the flattened image. * * Returns: (nullable) (transfer full): a new cairo surface to be freed with * cairo_surface_destroy(). */ cairo_surface_t * meta_window_actor_get_image (MetaWindowActor *self, MetaRectangle *clip) { MetaWindowActorPrivate *priv = meta_window_actor_get_instance_private (self); ClutterActor *actor = CLUTTER_ACTOR (self); MetaBackend *backend = meta_get_backend (); ClutterBackend *clutter_backend = meta_backend_get_clutter_backend (backend); CoglContext *cogl_context = clutter_backend_get_cogl_context (clutter_backend); float resource_scale; float width, height; CoglTexture2D *texture; g_autoptr (GError) error = NULL; CoglOffscreen *offscreen; CoglFramebuffer *framebuffer; CoglColor clear_color; float x, y; MetaRectangle scaled_clip; ClutterPaintContext *paint_context; cairo_surface_t *surface = NULL; if (!priv->surface) return NULL; clutter_actor_inhibit_culling (actor); if (clutter_actor_get_n_children (actor) == 1) { MetaShapedTexture *stex; MetaRectangle *surface_clip = NULL; if (clip) { int geometry_scale; geometry_scale = meta_window_actor_get_geometry_scale (self); surface_clip = g_alloca (sizeof (MetaRectangle)); surface_clip->x = clip->x / geometry_scale, surface_clip->y = clip->y / geometry_scale; surface_clip->width = clip->width / geometry_scale; surface_clip->height = clip->height / geometry_scale; } stex = meta_surface_actor_get_texture (priv->surface); surface = meta_shaped_texture_get_image (stex, surface_clip); goto out; } clutter_actor_get_size (actor, &width, &height); if (width == 0 || height == 0) goto out; if (!clutter_actor_get_resource_scale (actor, &resource_scale)) goto out; width = ceilf (width * resource_scale); height = ceilf (height * resource_scale); texture = cogl_texture_2d_new_with_size (cogl_context, width, height); if (!texture) goto out; cogl_primitive_texture_set_auto_mipmap (COGL_PRIMITIVE_TEXTURE (texture), FALSE); offscreen = cogl_offscreen_new_with_texture (COGL_TEXTURE (texture)); framebuffer = COGL_FRAMEBUFFER (offscreen); cogl_object_unref (texture); if (!cogl_framebuffer_allocate (framebuffer, &error)) { g_warning ("Failed to allocate framebuffer for screenshot: %s", error->message); cogl_object_unref (framebuffer); cogl_object_unref (texture); goto out; } cogl_color_init_from_4ub (&clear_color, 0, 0, 0, 0); clutter_actor_get_position (actor, &x, &y); cogl_framebuffer_clear (framebuffer, COGL_BUFFER_BIT_COLOR, &clear_color); cogl_framebuffer_orthographic (framebuffer, 0, 0, width, height, 0, 1.0); cogl_framebuffer_scale (framebuffer, resource_scale, resource_scale, 1); cogl_framebuffer_translate (framebuffer, -x, -y, 0); paint_context = clutter_paint_context_new_for_framebuffer (framebuffer); clutter_actor_paint (actor, paint_context); clutter_paint_context_destroy (paint_context); if (clip) { meta_rectangle_scale_double (clip, resource_scale, META_ROUNDING_STRATEGY_GROW, &scaled_clip); meta_rectangle_intersect (&scaled_clip, &(MetaRectangle) { .width = width, .height = height, }, &scaled_clip); } else { scaled_clip = (MetaRectangle) { .width = width, .height = height, }; } surface = cairo_image_surface_create (CAIRO_FORMAT_ARGB32, scaled_clip.width, scaled_clip.height); cogl_framebuffer_read_pixels (framebuffer, scaled_clip.x, scaled_clip.y, scaled_clip.width, scaled_clip.height, CLUTTER_CAIRO_FORMAT_ARGB32, cairo_image_surface_get_data (surface)); cogl_object_unref (framebuffer); cairo_surface_mark_dirty (surface); out: clutter_actor_uninhibit_culling (actor); return surface; } muffin-6.4.1/src/compositor/meta-dnd.c0000664000175000017500000002343714723361714016575 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2016 Hyungwon Hwang * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * */ #include "config.h" #include #include "clutter/x11/clutter-x11.h" #include "meta/meta-backend.h" #include "compositor/compositor-private.h" #include "core/display-private.h" #include "backends/meta-dnd-private.h" #include "backends/x11/meta-backend-x11.h" #include "backends/x11/meta-stage-x11.h" #include "meta/meta-dnd.h" #include "x11/meta-x11-display-private.h" struct _MetaDndClass { GObjectClass parent_class; }; #ifdef HAVE_WAYLAND #include "wayland/meta-wayland-private.h" #include "wayland/meta-wayland-data-device.h" #endif typedef struct _MetaDndPrivate MetaDndPrivate; struct _MetaDndPrivate { #ifdef HAVE_WAYLAND gulong handler_id[3]; MetaCompositor *compositor; MetaWaylandCompositor *wl_compositor; #else /* to avoid warnings (g_type_class_add_private: assertion `private_size > 0' failed) */ gchar dummy; #endif }; struct _MetaDnd { GObject parent; MetaDndPrivate *priv; }; G_DEFINE_TYPE_WITH_PRIVATE (MetaDnd, meta_dnd, G_TYPE_OBJECT); enum { ENTER, POSITION_CHANGE, LEAVE, LAST_SIGNAL }; static guint signals[LAST_SIGNAL] = { 0 }; static void meta_dnd_class_init (MetaDndClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); signals[ENTER] = g_signal_new ("dnd-enter", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); signals[POSITION_CHANGE] = g_signal_new ("dnd-position-change", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT); signals[LEAVE] = g_signal_new ("dnd-leave", G_TYPE_FROM_CLASS (object_class), G_SIGNAL_RUN_LAST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); } static void meta_dnd_init (MetaDnd *dnd) { } void meta_dnd_init_xdnd (MetaX11Display *x11_display) { MetaBackend *backend = meta_get_backend (); Display *xdisplay = x11_display->xdisplay; Window xwindow, overlay_xwindow; long xdnd_version = 5; overlay_xwindow = x11_display->composite_overlay_window; xwindow = meta_backend_x11_get_xwindow (META_BACKEND_X11 (backend)); XChangeProperty (xdisplay, xwindow, XInternAtom (xdisplay, "XdndAware", TRUE), XA_ATOM, 32, PropModeReplace, (const unsigned char *) &xdnd_version, 1); XChangeProperty (xdisplay, overlay_xwindow, XInternAtom (xdisplay, "XdndProxy", TRUE), XA_WINDOW, 32, PropModeReplace, (const unsigned char *) &xwindow, 1); /* * XdndProxy is additionally set on the proxy window as verification that the * XdndProxy property on the target window isn't a left-over */ XChangeProperty (xdisplay, xwindow, XInternAtom (xdisplay, "XdndProxy", TRUE), XA_WINDOW, 32, PropModeReplace, (const unsigned char *) &xwindow, 1); } static void meta_dnd_notify_dnd_enter (MetaDnd *dnd) { g_signal_emit (dnd, signals[ENTER], 0); } static void meta_dnd_notify_dnd_position_change (MetaDnd *dnd, int x, int y) { g_signal_emit (dnd, signals[POSITION_CHANGE], 0, x, y); } static void meta_dnd_notify_dnd_leave (MetaDnd *dnd) { g_signal_emit (dnd, signals[LEAVE], 0); } /* * Process Xdnd events * * We pass the position and leave events to the plugin via a signal * where the actual drag & drop handling happens. * * http://www.freedesktop.org/wiki/Specifications/XDND */ gboolean meta_dnd_handle_xdnd_event (MetaBackend *backend, MetaCompositorX11 *compositor_x11, Display *xdisplay, XEvent *xev) { MetaDnd *dnd = meta_backend_get_dnd (backend); MetaCompositor *compositor = META_COMPOSITOR (compositor_x11); Window output_window; ClutterStage *stage; if (xev->xany.type != ClientMessage) return FALSE; output_window = meta_compositor_x11_get_output_xwindow (compositor_x11); stage = meta_compositor_get_stage (compositor); if (xev->xany.window != output_window && xev->xany.window != meta_x11_get_stage_window (stage)) return FALSE; if (xev->xclient.message_type == XInternAtom (xdisplay, "XdndPosition", TRUE)) { XEvent xevent; Window src = xev->xclient.data.l[0]; memset (&xevent, 0, sizeof(xevent)); xevent.xany.type = ClientMessage; xevent.xany.display = xdisplay; xevent.xclient.window = src; xevent.xclient.message_type = XInternAtom (xdisplay, "XdndStatus", TRUE); xevent.xclient.format = 32; xevent.xclient.data.l[0] = output_window; /* flags: bit 0: will we accept the drop? bit 1: do we want more position messages */ xevent.xclient.data.l[1] = 2; xevent.xclient.data.l[4] = None; XSendEvent (xdisplay, src, False, 0, &xevent); meta_dnd_notify_dnd_position_change (dnd, (int)(xev->xclient.data.l[2] >> 16), (int)(xev->xclient.data.l[2] & 0xFFFF)); return TRUE; } else if (xev->xclient.message_type == XInternAtom (xdisplay, "XdndLeave", TRUE)) { meta_dnd_notify_dnd_leave (dnd); return TRUE; } else if (xev->xclient.message_type == XInternAtom (xdisplay, "XdndEnter", TRUE)) { meta_dnd_notify_dnd_enter (dnd); return TRUE; } return FALSE; } #ifdef HAVE_WAYLAND static void meta_dnd_wayland_on_motion_event (ClutterActor *actor, ClutterEvent *event, MetaDnd *dnd) { MetaDndPrivate *priv = meta_dnd_get_instance_private (dnd); MetaWaylandDragGrab *current_grab; gfloat event_x, event_y; g_return_if_fail (event != NULL); clutter_event_get_coords (event, &event_x, &event_y); meta_dnd_notify_dnd_position_change (dnd, (int)event_x, (int)event_y); current_grab = meta_wayland_data_device_get_current_grab (&priv->wl_compositor->seat->data_device); if (current_grab) meta_wayland_drag_grab_update_feedback_actor (current_grab, event); } static void meta_dnd_wayland_end_notify (ClutterActor *actor, ClutterEvent *event, MetaDnd *dnd) { MetaDndPrivate *priv = meta_dnd_get_instance_private (dnd); meta_wayland_data_device_end_drag (&priv->wl_compositor->seat->data_device); meta_dnd_wayland_handle_end_modal (priv->compositor); } static void meta_dnd_wayland_on_button_released (ClutterActor *actor, ClutterEvent *event, MetaDnd *dnd) { meta_dnd_wayland_end_notify (actor, event, dnd); } static void meta_dnd_wayland_on_key_pressed (ClutterActor *actor, ClutterEvent *event, MetaDnd *dnd) { guint key = clutter_event_get_key_symbol (event); if (key != CLUTTER_KEY_Escape) return; meta_dnd_wayland_end_notify (actor, event, dnd); } void meta_dnd_wayland_handle_begin_modal (MetaCompositor *compositor) { MetaWaylandCompositor *wl_compositor = meta_wayland_compositor_get_default (); MetaDnd *dnd = meta_backend_get_dnd (meta_get_backend ()); MetaDndPrivate *priv = meta_dnd_get_instance_private (dnd); if (priv->handler_id[0] == 0 && meta_wayland_data_device_get_current_grab (&wl_compositor->seat->data_device) != NULL) { ClutterStage *stage = meta_compositor_get_stage (compositor); priv->compositor = compositor; priv->wl_compositor = wl_compositor; priv->handler_id[0] = g_signal_connect (stage, "motion-event", G_CALLBACK (meta_dnd_wayland_on_motion_event), dnd); priv->handler_id[1] = g_signal_connect (stage, "button-release-event", G_CALLBACK (meta_dnd_wayland_on_button_released), dnd); priv->handler_id[2] = g_signal_connect (stage, "key-press-event", G_CALLBACK (meta_dnd_wayland_on_key_pressed), dnd); meta_dnd_notify_dnd_enter (dnd); } } void meta_dnd_wayland_handle_end_modal (MetaCompositor *compositor) { MetaDnd *dnd = meta_backend_get_dnd (meta_get_backend ()); MetaDndPrivate *priv = meta_dnd_get_instance_private (dnd); ClutterStage *stage = meta_compositor_get_stage (compositor); unsigned int i; if (!priv->compositor) return; for (i = 0; i < G_N_ELEMENTS (priv->handler_id); i++) g_clear_signal_handler (&priv->handler_id[i], stage); priv->compositor = NULL; priv->wl_compositor = NULL; meta_dnd_notify_dnd_leave (dnd); } #endif muffin-6.4.1/src/compositor/meta-dnd-actor.c0000664000175000017500000001615714723361714017704 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright 2014 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Author: Carlos Garnacho */ /** * SECTION:meta-dnd-actor * @title: MetaDnDActor * @short_description: Actor for painting the drag and drop surface * */ #include "config.h" #include "compositor/meta-dnd-actor-private.h" #include "compositor/meta-window-actor-private.h" #include "clutter/clutter.h" #define DRAG_FAILED_DURATION 500 enum { PROP_DRAG_ORIGIN = 1, PROP_DRAG_START_X, PROP_DRAG_START_Y }; struct _MetaDnDActor { MetaFeedbackActor parent; ClutterActor *drag_origin; int drag_start_x; int drag_start_y; }; G_DEFINE_TYPE (MetaDnDActor, meta_dnd_actor, META_TYPE_FEEDBACK_ACTOR) static void meta_dnd_actor_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { MetaDnDActor *self = META_DND_ACTOR (object); switch (prop_id) { case PROP_DRAG_ORIGIN: self->drag_origin = g_value_get_object (value); break; case PROP_DRAG_START_X: self->drag_start_x = g_value_get_int (value); break; case PROP_DRAG_START_Y: self->drag_start_y = g_value_get_int (value); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_dnd_actor_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { MetaDnDActor *self = META_DND_ACTOR (object); switch (prop_id) { case PROP_DRAG_ORIGIN: g_value_set_object (value, self->drag_origin); break; case PROP_DRAG_START_X: g_value_set_int (value, self->drag_start_x); break; case PROP_DRAG_START_Y: g_value_set_int (value, self->drag_start_y); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } } static void meta_dnd_actor_class_init (MetaDnDActorClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GParamSpec *pspec; object_class->set_property = meta_dnd_actor_set_property; object_class->get_property = meta_dnd_actor_get_property; pspec = g_param_spec_object ("drag-origin", "Drag origin", "The origin of the DnD operation", CLUTTER_TYPE_ACTOR, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DRAG_ORIGIN, pspec); pspec = g_param_spec_int ("drag-start-x", "Drag start X", "The X axis of the drag start point", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DRAG_START_X, pspec); pspec = g_param_spec_int ("drag-start-y", "Drag start Y", "The Y axis of the drag start point", 0, G_MAXINT, 0, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); g_object_class_install_property (object_class, PROP_DRAG_START_Y, pspec); } static void meta_dnd_actor_init (MetaDnDActor *self) { } /** * meta_dnd_actor_new: * * Creates a new actor to draw the current drag and drop surface. * * Return value: the newly created background actor */ ClutterActor * meta_dnd_actor_new (ClutterActor *drag_origin, int drag_start_x, int drag_start_y) { MetaDnDActor *self; self = g_object_new (META_TYPE_DND_ACTOR, "drag-origin", drag_origin, "drag-start-x", drag_start_x, "drag-start-y", drag_start_y, NULL); return CLUTTER_ACTOR (self); } static void drag_failed_complete (ClutterTimeline *timeline, gboolean is_finished, gpointer user_data) { ClutterActor *self = user_data; clutter_actor_remove_all_children (self); clutter_actor_destroy (self); } void meta_dnd_actor_drag_finish (MetaDnDActor *self, gboolean success) { ClutterActor *actor; g_return_if_fail (META_IS_DND_ACTOR (self)); actor = CLUTTER_ACTOR (self); if (success) { clutter_actor_remove_all_children (CLUTTER_ACTOR (self)); clutter_actor_destroy (CLUTTER_ACTOR (self)); } else { ClutterTransition *transition; clutter_actor_save_easing_state (actor); clutter_actor_set_easing_mode (actor, CLUTTER_EASE_OUT_CUBIC); clutter_actor_set_easing_duration (actor, DRAG_FAILED_DURATION); clutter_actor_set_opacity (actor, 0); if (CLUTTER_ACTOR_IS_VISIBLE (self->drag_origin)) { MetaWindowActor *origin_actor; float anchor_x, anchor_y; graphene_point_t dest; int origin_geometry_scale; int feedback_geometry_scale; clutter_actor_get_transformed_position (self->drag_origin, &dest.x, &dest.y); origin_actor = meta_window_actor_from_actor (self->drag_origin); g_return_if_fail (origin_actor); origin_geometry_scale = meta_window_actor_get_geometry_scale (origin_actor); meta_feedback_actor_get_anchor (META_FEEDBACK_ACTOR (self), &anchor_x, &anchor_y); feedback_geometry_scale = meta_feedback_actor_get_geometry_scale (META_FEEDBACK_ACTOR (self)); dest.x += ((self->drag_start_x * origin_geometry_scale) - (anchor_x * feedback_geometry_scale)); dest.y += ((self->drag_start_y * origin_geometry_scale) - (anchor_y * feedback_geometry_scale)); clutter_actor_set_position (actor, dest.x, dest.y); } transition = clutter_actor_get_transition (actor, "opacity"); g_signal_connect (transition, "stopped", G_CALLBACK (drag_failed_complete), self); clutter_actor_restore_easing_state (actor); } } muffin-6.4.1/src/compositor/compositor-private.h0000664000175000017500000000702714723361714020754 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #ifndef META_COMPOSITOR_PRIVATE_H #define META_COMPOSITOR_PRIVATE_H #include #include "clutter/clutter.h" #include "compositor/meta-plugin-manager.h" #include "compositor/meta-window-actor-private.h" #include "meta/compositor.h" #include "meta/display.h" /* Wait 2ms after vblank before starting to draw next frame */ #define META_SYNC_DELAY 2 struct _MetaCompositorClass { GObjectClass parent_class; void (* manage) (MetaCompositor *compositor); void (* unmanage) (MetaCompositor *compositor); void (* pre_paint) (MetaCompositor *compositor); void (* post_paint) (MetaCompositor *compositor); void (* remove_window) (MetaCompositor *compositor, MetaWindow *window); int64_t (* monotonic_to_high_res_xserver_time) (MetaCompositor *compositor, int64_t time_us); }; void meta_compositor_remove_window_actor (MetaCompositor *compositor, MetaWindowActor *window_actor); void meta_switch_workspace_completed (MetaCompositor *compositor); gboolean meta_begin_modal_for_plugin (MetaCompositor *compositor, MetaPlugin *plugin, MetaModalOptions options, guint32 timestamp); void meta_end_modal_for_plugin (MetaCompositor *compositor, MetaPlugin *plugin, guint32 timestamp); MetaPluginManager * meta_compositor_get_plugin_manager (MetaCompositor *compositor); int64_t meta_compositor_monotonic_to_high_res_xserver_time (MetaCompositor *compositor, int64_t monotonic_time_us); void meta_compositor_flash_window (MetaCompositor *compositor, MetaWindow *window); MetaCloseDialog * meta_compositor_create_close_dialog (MetaCompositor *compositor, MetaWindow *window); MetaInhibitShortcutsDialog * meta_compositor_create_inhibit_shortcuts_dialog (MetaCompositor *compositor, MetaWindow *window); void meta_compositor_locate_pointer (MetaCompositor *compositor); void meta_compositor_redirect_x11_windows (MetaCompositor *compositor); gboolean meta_compositor_is_unredirect_inhibited (MetaCompositor *compositor); MetaDisplay * meta_compositor_get_display (MetaCompositor *compositor); MetaWindowActor * meta_compositor_get_top_window_actor (MetaCompositor *compositor); ClutterStage * meta_compositor_get_stage (MetaCompositor *compositor); gboolean meta_compositor_is_switching_workspace (MetaCompositor *compositor); void meta_update_desklet_stacking (MetaCompositor *compositor); static inline int64_t us (int64_t us) { return us; } static inline int64_t ms2us (int64_t ms) { return us (ms * 1000); } static inline int64_t s2us (int64_t s) { return ms2us(s * 1000); } /* * This function takes a 64 bit time stamp from the monotonic clock, and clamps * it to the scope of the X server clock, without losing the granularity. */ static inline int64_t meta_translate_to_high_res_xserver_time (int64_t time_us) { int64_t us; int64_t ms; us = time_us % 1000; ms = time_us / 1000; return ms2us (ms & 0xffffffff) + us; } #endif /* META_COMPOSITOR_PRIVATE_H */ muffin-6.4.1/src/compositor/meta-compositor-x11.h0000664000175000017500000000266714723361714020644 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_COMPOSITOR_X11_H #define META_COMPOSITOR_X11_H #include "compositor/compositor-private.h" #define META_TYPE_COMPOSITOR_X11 (meta_compositor_x11_get_type ()) G_DECLARE_FINAL_TYPE (MetaCompositorX11, meta_compositor_x11, META, COMPOSITOR_X11, MetaCompositor) MetaCompositorX11 * meta_compositor_x11_new (MetaDisplay *display); void meta_compositor_x11_process_xevent (MetaCompositorX11 *compositor_x11, XEvent *xevent, MetaWindow *window); Window meta_compositor_x11_get_output_xwindow (MetaCompositorX11 *compositor_x11); #endif /* META_COMPOSITOR_X11_H */ muffin-6.4.1/src/compositor/meta-compositor-server.h0000664000175000017500000000225314723361714021530 0ustar fabiofabio/* * Copyright (C) 2019 Red Hat Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. * */ #ifndef META_COMPOSITOR_SERVER_H #define META_COMPOSITOR_SERVER_H #include "compositor/compositor-private.h" #define META_TYPE_COMPOSITOR_SERVER (meta_compositor_server_get_type ()) G_DECLARE_FINAL_TYPE (MetaCompositorServer, meta_compositor_server, META, COMPOSITOR_SERVER, MetaCompositor) MetaCompositorServer * meta_compositor_server_new (MetaDisplay *display); #endif /* META_COMPOSITOR_SERVER_H */ muffin-6.4.1/src/compositor/meta-background-group.c0000664000175000017500000000345514723361714021277 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /** * SECTION:meta-background-group * @title: MetaBackgroundGroup * @short_description: Container for background actors * * This class is a subclass of ClutterActor with special handling for * MetaBackgroundActor/MetaBackgroundGroup when painting children. * It makes sure to only draw the parts of the backgrounds not * occluded by opaque windows. * * See #MetaWindowGroup for more information behind the motivation, * and details on implementation. */ #include "config.h" #include "compositor/meta-cullable.h" #include "meta/meta-background-group.h" static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaBackgroundGroup, meta_background_group, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void meta_background_group_class_init (MetaBackgroundGroupClass *klass) { } static void meta_background_group_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } static void meta_background_group_reset_culling (MetaCullable *cullable) { meta_cullable_reset_culling_children (cullable); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_background_group_cull_out; iface->reset_culling = meta_background_group_reset_culling; } static void meta_background_group_init (MetaBackgroundGroup *self) { } ClutterActor * meta_background_group_new (void) { MetaBackgroundGroup *background_group; background_group = g_object_new (META_TYPE_BACKGROUND_GROUP, NULL); return CLUTTER_ACTOR (background_group); } muffin-6.4.1/src/compositor/meta-plugin-manager.c0000664000175000017500000003064714723361714020737 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "config.h" #include "compositor/meta-plugin-manager.h" #include #include #include "clutter/x11/clutter-x11.h" #include "compositor/compositor-private.h" #include "compositor/meta-module.h" #include "core/meta-close-dialog-default-private.h" #include "core/meta-inhibit-shortcuts-dialog-default-private.h" #include "core/window-private.h" #include "meta/meta-x11-errors.h" #include "meta/prefs.h" #include "meta/workspace.h" static GType plugin_type = G_TYPE_NONE; struct MetaPluginManager { MetaCompositor *compositor; MetaPlugin *plugin; }; void meta_plugin_manager_set_plugin_type (GType gtype) { if (plugin_type != G_TYPE_NONE) meta_fatal ("Mutter plugin already set: %s", g_type_name (plugin_type)); plugin_type = gtype; } /* * Loads the given plugin. */ void meta_plugin_manager_load (const gchar *plugin_name) { const gchar *dpath = MUTTER_PLUGIN_DIR "/"; gchar *path; MetaModule *module; if (g_path_is_absolute (plugin_name)) path = g_strdup (plugin_name); else path = g_strconcat (dpath, plugin_name, ".so", NULL); module = g_object_new (META_TYPE_MODULE, "path", path, NULL); if (!module || !g_type_module_use (G_TYPE_MODULE (module))) { /* This is fatal under the assumption that a monitoring * process like gnome-session will take over and handle * our untimely exit. */ g_printerr ("Unable to load plugin module [%s]: %s", path, g_module_error()); exit (1); } meta_plugin_manager_set_plugin_type (meta_module_get_plugin_type (module)); g_type_module_unuse (G_TYPE_MODULE (module)); g_free (path); } static void on_confirm_display_change (MetaMonitorManager *monitors, MetaPluginManager *plugin_mgr) { meta_plugin_manager_confirm_display_change (plugin_mgr); } MetaPluginManager * meta_plugin_manager_new (MetaCompositor *compositor) { MetaPluginManager *plugin_mgr; MetaPluginClass *klass; MetaPlugin *plugin; MetaMonitorManager *monitors; plugin_mgr = g_new0 (MetaPluginManager, 1); plugin_mgr->compositor = compositor; plugin_mgr->plugin = plugin = g_object_new (plugin_type, NULL); _meta_plugin_set_compositor (plugin, compositor); klass = META_PLUGIN_GET_CLASS (plugin); if (klass->start) klass->start (plugin); monitors = meta_monitor_manager_get (); g_signal_connect (monitors, "confirm-display-change", G_CALLBACK (on_confirm_display_change), plugin_mgr); return plugin_mgr; } static void meta_plugin_manager_kill_window_effects (MetaPluginManager *plugin_mgr, MetaWindowActor *actor) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->kill_window_effects) klass->kill_window_effects (plugin, actor); } static void meta_plugin_manager_kill_switch_workspace (MetaPluginManager *plugin_mgr) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->kill_switch_workspace) klass->kill_switch_workspace (plugin); } /* * Public method that the compositor hooks into for events that require * no additional parameters. * * Returns TRUE if the plugin handled the event type (i.e., * if the return value is FALSE, there will be no subsequent call to the * manager completed() callback, and the compositor must ensure that any * appropriate post-effect cleanup is carried out. */ gboolean meta_plugin_manager_event_simple (MetaPluginManager *plugin_mgr, MetaWindowActor *actor, MetaPluginEffect event) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); gboolean retval = FALSE; if (display->display_opening) return FALSE; switch (event) { case META_PLUGIN_MINIMIZE: if (klass->minimize) { retval = TRUE; meta_plugin_manager_kill_window_effects (plugin_mgr, actor); klass->minimize (plugin, actor); } break; case META_PLUGIN_UNMINIMIZE: if (klass->unminimize) { retval = TRUE; meta_plugin_manager_kill_window_effects (plugin_mgr, actor); klass->unminimize (plugin, actor); } break; case META_PLUGIN_MAP: if (klass->map) { retval = TRUE; meta_plugin_manager_kill_window_effects (plugin_mgr, actor); klass->map (plugin, actor); } break; case META_PLUGIN_DESTROY: if (klass->destroy) { retval = TRUE; meta_plugin_manager_kill_window_effects (plugin_mgr, actor); klass->destroy (plugin, actor); } break; default: g_warning ("Incorrect handler called for event %d", event); } return retval; } void meta_plugin_manager_event_size_changed (MetaPluginManager *plugin_mgr, MetaWindowActor *actor) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->size_changed) klass->size_changed (plugin, actor); } gboolean meta_plugin_manager_event_size_change (MetaPluginManager *plugin_mgr, MetaWindowActor *actor, MetaSizeChange which_change, MetaRectangle *old_frame_rect, MetaRectangle *old_buffer_rect) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); if (display->display_opening) return FALSE; if (!klass->size_change) return FALSE; meta_plugin_manager_kill_window_effects (plugin_mgr, actor); klass->size_change (plugin, actor, which_change, old_frame_rect, old_buffer_rect); return TRUE; } /* * The public method that the compositor hooks into for desktop switching. * * Returns TRUE if the plugin handled the event type (i.e., * if the return value is FALSE, there will be no subsequent call to the * manager completed() callback, and the compositor must ensure that any * appropriate post-effect cleanup is carried out. */ gboolean meta_plugin_manager_switch_workspace (MetaPluginManager *plugin_mgr, gint from, gint to, MetaMotionDirection direction) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); gboolean retval = FALSE; if (display->display_opening) return FALSE; if (klass->switch_workspace) { retval = TRUE; meta_plugin_manager_kill_switch_workspace (plugin_mgr); klass->switch_workspace (plugin, from, to, direction); } return retval; } gboolean meta_plugin_manager_filter_keybinding (MetaPluginManager *plugin_mgr, MetaKeyBinding *binding) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->keybinding_filter) return klass->keybinding_filter (plugin, binding); return FALSE; } gboolean meta_plugin_manager_xevent_filter (MetaPluginManager *plugin_mgr, XEvent *xev) { MetaPlugin *plugin = plugin_mgr->plugin; return _meta_plugin_xevent_filter (plugin, xev); } void meta_plugin_manager_confirm_display_change (MetaPluginManager *plugin_mgr) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->confirm_display_change) klass->confirm_display_change (plugin); else meta_plugin_complete_display_change (plugin, TRUE); } gboolean meta_plugin_manager_show_tile_preview (MetaPluginManager *plugin_mgr, MetaWindow *window, MetaRectangle *tile_rect, int tile_monitor_number) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); if (display->display_opening) return FALSE; if (klass->show_tile_preview) { klass->show_tile_preview (plugin, window, tile_rect, tile_monitor_number); return TRUE; } return FALSE; } gboolean meta_plugin_manager_hide_tile_preview (MetaPluginManager *plugin_mgr) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); if (display->display_opening) return FALSE; if (klass->hide_tile_preview) { klass->hide_tile_preview (plugin); return TRUE; } return FALSE; } void meta_plugin_manager_show_window_menu (MetaPluginManager *plugin_mgr, MetaWindow *window, MetaWindowMenuType menu, int x, int y) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); if (display->display_opening) return; if (klass->show_window_menu) klass->show_window_menu (plugin, window, menu, x, y); } void meta_plugin_manager_show_window_menu_for_rect (MetaPluginManager *plugin_mgr, MetaWindow *window, MetaWindowMenuType menu, MetaRectangle *rect) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); MetaDisplay *display = meta_compositor_get_display (plugin_mgr->compositor); if (display->display_opening) return; if (klass->show_window_menu_for_rect) klass->show_window_menu_for_rect (plugin, window, menu, rect); } MetaCloseDialog * meta_plugin_manager_create_close_dialog (MetaPluginManager *plugin_mgr, MetaWindow *window) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->create_close_dialog) return klass->create_close_dialog (plugin, window); return meta_close_dialog_default_new (window); } MetaInhibitShortcutsDialog * meta_plugin_manager_create_inhibit_shortcuts_dialog (MetaPluginManager *plugin_mgr, MetaWindow *window) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->create_inhibit_shortcuts_dialog) return klass->create_inhibit_shortcuts_dialog (plugin, window); return meta_inhibit_shortcuts_dialog_default_new (window); } void meta_plugin_manager_locate_pointer (MetaPluginManager *plugin_mgr) { MetaPlugin *plugin = plugin_mgr->plugin; MetaPluginClass *klass = META_PLUGIN_GET_CLASS (plugin); if (klass->locate_pointer) klass->locate_pointer (plugin); } muffin-6.4.1/src/compositor/meta-cullable.c0000664000175000017500000002052714723361714017610 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (C) 2013 Red Hat * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Written by: * Owen Taylor * Ray Strode * Jasper St. Pierre */ #include "config.h" #include "compositor/clutter-utils.h" #include "compositor/meta-cullable.h" G_DEFINE_INTERFACE (MetaCullable, meta_cullable, CLUTTER_TYPE_ACTOR); static gboolean has_active_effects (ClutterActor *actor) { g_autoptr (GList) effects = NULL; GList *l; effects = clutter_actor_get_effects (actor); for (l = effects; l != NULL; l = l->next) { if (clutter_actor_meta_get_enabled (CLUTTER_ACTOR_META (l->data))) return TRUE; } return FALSE; } /** * SECTION:meta-cullable * @title: MetaCullable * @short_description: CPU culling operations for efficient drawing * * When we are painting a stack of 5-10 large actors, the standard * bottom-to-top method of drawing every actor results in a tremendous * amount of overdraw. If these actors are painting textures like * windows, it can easily max out the available memory bandwidth on a * low-end graphics chipset. It's even worse if window textures are * being accessed over the AGP bus. * * #MetaCullable is our solution. The basic technique applied here is to * do a pre-pass before painting where we walk each actor from top to bottom * and ask each actor to "cull itself out". We pass in a region it can copy * to clip its drawing to, and the actor can subtract its fully opaque pixels * so that actors underneath know not to draw there as well. */ /** * meta_cullable_cull_out_children: * @cullable: The #MetaCullable * @unobscured_region: The unobscured region, as passed into cull_out() * @clip_region: The clip region, as passed into cull_out() * * This is a helper method for actors that want to recurse over their * child actors, and cull them out. * * See #MetaCullable and meta_cullable_cull_out() for more details. */ void meta_cullable_cull_out_children (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { ClutterActor *actor = CLUTTER_ACTOR (cullable); ClutterActor *child; ClutterActorIter iter; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_prev (&iter, &child)) { float x, y; gboolean needs_culling; if (!META_IS_CULLABLE (child)) continue; needs_culling = (unobscured_region != NULL && clip_region != NULL); if (needs_culling && !CLUTTER_ACTOR_IS_VISIBLE (child)) needs_culling = FALSE; /* If an actor has effects applied, then that can change the area * it paints and the opacity, so we no longer can figure out what * portion of the actor is obscured and what portion of the screen * it obscures, so we skip the actor. * * This has a secondary beneficial effect: if a ClutterOffscreenEffect * is applied to an actor, then our clipped redraws interfere with the * caching of the FBO - even if we only need to draw a small portion * of the window right now, ClutterOffscreenEffect may use other portions * of the FBO later. So, skipping actors with effects applied also * prevents these bugs. * * Theoretically, we should check clutter_actor_get_offscreen_redirect() * as well for the same reason, but omitted for simplicity in the * hopes that no-one will do that. */ if (needs_culling && has_active_effects (child)) needs_culling = FALSE; if (needs_culling && !meta_cullable_is_untransformed (META_CULLABLE (child))) needs_culling = FALSE; if (needs_culling) { clutter_actor_get_position (child, &x, &y); /* Temporarily move to the coordinate system of the actor */ cairo_region_translate (unobscured_region, - x, - y); cairo_region_translate (clip_region, - x, - y); meta_cullable_cull_out (META_CULLABLE (child), unobscured_region, clip_region); cairo_region_translate (unobscured_region, x, y); cairo_region_translate (clip_region, x, y); } else { meta_cullable_cull_out (META_CULLABLE (child), NULL, NULL); } } } /** * meta_cullable_reset_culling_children: * @cullable: The #MetaCullable * * This is a helper method for actors that want to recurse over their * child actors, and cull them out. * * See #MetaCullable and meta_cullable_reset_culling() for more details. */ void meta_cullable_reset_culling_children (MetaCullable *cullable) { ClutterActor *actor = CLUTTER_ACTOR (cullable); ClutterActor *child; ClutterActorIter iter; clutter_actor_iter_init (&iter, actor); while (clutter_actor_iter_next (&iter, &child)) { if (!META_IS_CULLABLE (child)) continue; meta_cullable_reset_culling (META_CULLABLE (child)); } } static gboolean meta_cullable_default_is_untransformed (MetaCullable *cullable) { float width, height; graphene_point3d_t verts[4]; clutter_actor_get_size (CLUTTER_ACTOR (cullable), &width, &height); clutter_actor_get_abs_allocation_vertices (CLUTTER_ACTOR (cullable), verts); return meta_actor_vertices_are_untransformed (verts, width, height, NULL, NULL); } static void meta_cullable_default_init (MetaCullableInterface *iface) { iface->is_untransformed = meta_cullable_default_is_untransformed; } /** * meta_cullable_cull_out: * @cullable: The #MetaCullable * @unobscured_region: The unobscured region, in @cullable's space. * @clip_region: The clip region, in @cullable's space. * * When #MetaWindowGroup is painted, we walk over its direct cullable * children from top to bottom and ask themselves to "cull out". Cullables * can use @unobscured_region and @clip_region to clip their drawing. Actors * interested in eliminating overdraw should copy the @clip_region and only * paint those parts, as everything else has been obscured by actors above it. * * Actors that may have fully opaque parts should also subtract out a region * that is fully opaque from @unobscured_region and @clip_region. * * @unobscured_region and @clip_region are extremely similar. The difference * is that @clip_region starts off with the stage's clip, if Clutter detects * that we're doing a clipped redraw. @unobscured_region, however, starts off * with the full stage size, so actors that may want to record what parts of * their window are unobscured for e.g. scheduling repaints can do so. * * Actors that have children can also use the meta_cullable_cull_out_children() * helper method to do a simple cull across all their children. */ void meta_cullable_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { META_CULLABLE_GET_IFACE (cullable)->cull_out (cullable, unobscured_region, clip_region); } /** * meta_cullable_is_untransformed: * @cullable: The #MetaCullable * * Check if a cullable is "untransformed" - which actually means transformed by * at most a integer-translation. */ gboolean meta_cullable_is_untransformed (MetaCullable *cullable) { return META_CULLABLE_GET_IFACE (cullable)->is_untransformed (cullable); } /** * meta_cullable_reset_culling: * @cullable: The #MetaCullable * * Actors that copied data in their cull_out() implementation can now * reset their data, as the paint is now over. Additional paints may be * done by #ClutterClone or similar, and they should not be affected by * the culling operation. */ void meta_cullable_reset_culling (MetaCullable *cullable) { META_CULLABLE_GET_IFACE (cullable)->reset_culling (cullable); } muffin-6.4.1/src/compositor/meta-shaped-texture-private.h0000664000175000017500000000703314723361714022441 0ustar fabiofabio/* * shaped texture * * An actor to draw a texture clipped to a list of rectangles * * Authored By Neil Roberts * * Copyright (C) 2008 Intel Corporation * 2013 Red Hat, Inc. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA * 02111-1307, USA. */ #ifndef __META_SHAPED_TEXTURE_PRIVATE_H__ #define __META_SHAPED_TEXTURE_PRIVATE_H__ #include "backends/meta-monitor-manager-private.h" #include "meta/meta-shaped-texture.h" MetaShapedTexture *meta_shaped_texture_new (void); void meta_shaped_texture_set_texture (MetaShapedTexture *stex, CoglTexture *texture); void meta_shaped_texture_set_is_y_inverted (MetaShapedTexture *stex, gboolean is_y_inverted); void meta_shaped_texture_set_snippet (MetaShapedTexture *stex, CoglSnippet *snippet); void meta_shaped_texture_set_fallback_size (MetaShapedTexture *stex, int fallback_width, int fallback_height); cairo_region_t * meta_shaped_texture_get_opaque_region (MetaShapedTexture *stex); gboolean meta_shaped_texture_is_opaque (MetaShapedTexture *stex); gboolean meta_shaped_texture_has_alpha (MetaShapedTexture *stex); void meta_shaped_texture_set_transform (MetaShapedTexture *stex, MetaMonitorTransform transform); void meta_shaped_texture_set_viewport_src_rect (MetaShapedTexture *stex, graphene_rect_t *src_rect); void meta_shaped_texture_reset_viewport_src_rect (MetaShapedTexture *stex); void meta_shaped_texture_set_viewport_dst_size (MetaShapedTexture *stex, int dst_width, int dst_height); void meta_shaped_texture_reset_viewport_dst_size (MetaShapedTexture *stex); void meta_shaped_texture_set_buffer_scale (MetaShapedTexture *stex, int buffer_scale); int meta_shaped_texture_get_buffer_scale (MetaShapedTexture *stex); gboolean meta_shaped_texture_update_area (MetaShapedTexture *stex, int x, int y, int width, int height, cairo_rectangle_int_t *clip); int meta_shaped_texture_get_width (MetaShapedTexture *stex); int meta_shaped_texture_get_height (MetaShapedTexture *stex); void meta_shaped_texture_set_clip_region (MetaShapedTexture *stex, cairo_region_t *clip_region); #endif muffin-6.4.1/src/compositor/meta-module.h0000664000175000017500000000345314723361714017316 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ /* * Copyright (c) 2008 Intel Corp. * * Author: Tomas Frydrych * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef META_MODULE_H_ #define META_MODULE_H_ #include #define META_TYPE_MODULE (meta_module_get_type ()) #define META_MODULE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), META_TYPE_MODULE, MetaModule)) #define META_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), META_TYPE_MODULE, MetaModuleClass)) #define META_IS_MODULE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), META_MODULE_TYPE)) #define META_IS_MODULE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), META_TYPE_MODULE)) #define META_MODULE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), META_TYPE_MODULE, MetaModuleClass)) typedef struct _MetaModule MetaModule; typedef struct _MetaModuleClass MetaModuleClass; typedef struct _MetaModulePrivate MetaModulePrivate; struct _MetaModule { GTypeModule parent; MetaModulePrivate *priv; }; struct _MetaModuleClass { GTypeModuleClass parent_class; }; GType meta_module_get_type (void); GType meta_module_get_plugin_type (MetaModule *module); #endif muffin-6.4.1/src/compositor/meta-window-group.c0000664000175000017500000001623314723361714020465 0ustar fabiofabio/* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*- */ #include "config.h" #include #include #include "compositor/clutter-utils.h" #include "compositor/compositor-private.h" #include "compositor/meta-cullable.h" #include "compositor/meta-window-actor-private.h" #include "compositor/meta-window-group-private.h" #include "core/display-private.h" #include "core/window-private.h" struct _MetaWindowGroupClass { ClutterActorClass parent_class; }; struct _MetaWindowGroup { ClutterActor parent; MetaDisplay *display; }; static void cullable_iface_init (MetaCullableInterface *iface); G_DEFINE_TYPE_WITH_CODE (MetaWindowGroup, meta_window_group, CLUTTER_TYPE_ACTOR, G_IMPLEMENT_INTERFACE (META_TYPE_CULLABLE, cullable_iface_init)); static void meta_window_group_cull_out (MetaCullable *cullable, cairo_region_t *unobscured_region, cairo_region_t *clip_region) { meta_cullable_cull_out_children (cullable, unobscured_region, clip_region); } static void meta_window_group_reset_culling (MetaCullable *cullable) { meta_cullable_reset_culling_children (cullable); } static void cullable_iface_init (MetaCullableInterface *iface) { iface->cull_out = meta_window_group_cull_out; iface->reset_culling = meta_window_group_reset_culling; } static void meta_window_group_paint (ClutterActor *actor, ClutterPaintContext *paint_context) { MetaWindowGroup *window_group = META_WINDOW_GROUP (actor); ClutterActorClass *parent_actor_class = CLUTTER_ACTOR_CLASS (meta_window_group_parent_class); ClutterActor *stage = clutter_actor_get_stage (actor); const cairo_region_t *redraw_clip; cairo_region_t *clip_region; cairo_region_t *unobscured_region; cairo_rectangle_int_t visible_rect; int paint_x_origin, paint_y_origin; int screen_width, screen_height; redraw_clip = clutter_paint_context_get_redraw_clip (paint_context); if (!redraw_clip) { parent_actor_class->paint (actor, paint_context); return; } meta_display_get_size (window_group->display, &screen_width, &screen_height); /* Normally we expect an actor to be drawn at it's position on the screen. * However, if we're inside the paint of a ClutterClone, that won't be the * case and we need to compensate. We look at the position of the window * group under the current model-view matrix and the position of the actor. * If they are both simply integer translations, then we can compensate * easily, otherwise we give up. * * Possible cleanup: work entirely in paint space - we can compute the * combination of the model-view matrix with the local matrix for each child * actor and get a total transformation for that actor for how we are * painting currently, and never worry about how actors are positioned * on the stage. */ if (clutter_actor_is_in_clone_paint (actor)) { CoglFramebuffer *fb; fb = clutter_paint_context_get_framebuffer (paint_context); if (!meta_actor_painting_untransformed (fb, screen_width, screen_height, screen_width, screen_height, &paint_x_origin, &paint_y_origin) || !meta_cullable_is_untransformed (META_CULLABLE (actor))) { parent_actor_class->paint (actor, paint_context); return; } } else { paint_x_origin = 0; paint_y_origin = 0; } visible_rect.x = visible_rect.y = 0; visible_rect.width = clutter_actor_get_width (CLUTTER_ACTOR (stage)); visible_rect.height = clutter_actor_get_height (CLUTTER_ACTOR (stage)); unobscured_region = cairo_region_create_rectangle (&visible_rect); /* Get the clipped redraw bounds so that we can avoid painting shadows on * windows that don't need to be painted in this frame. In the case of a * multihead setup with mismatched monitor sizes, we could intersect this * with an accurate union of the monitors to avoid painting shadows that are * visible only in the holes. */ clip_region = cairo_region_copy (redraw_clip); cairo_region_translate (clip_region, -paint_x_origin, -paint_y_origin); meta_cullable_cull_out (META_CULLABLE (window_group), unobscured_region, clip_region); cairo_region_destroy (unobscured_region); cairo_region_destroy (clip_region); parent_actor_class->paint (actor, paint_context); meta_cullable_reset_culling (META_CULLABLE (window_group)); } /* Adapted from clutter_actor_update_default_paint_volume() */ static gboolean meta_window_group_get_paint_volume (ClutterActor *self, ClutterPaintVolume *volume) { ClutterActorIter iter; ClutterActor *child; clutter_actor_iter_init (&iter, self); while (clutter_actor_iter_next (&iter, &child)) { const ClutterPaintVolume *child_volume; if (!CLUTTER_ACTOR_IS_MAPPED (child)) continue; child_volume = clutter_actor_get_transformed_paint_volume (child, self); if (child_volume == NULL) return FALSE; clutter_paint_volume_union (volume, child_volume); } return TRUE; } /* This is a workaround for Clutter's awful allocation tracking. * Without this, any time the window group changed size, which is * any time windows are dragged around, we'll do a full repaint * of the window group, which includes the background actor, meaning * a full-stage repaint. * * Since actors are allowed to paint outside their allocation, and * since child actors are allowed to be outside their parents, this * doesn't affect anything, but it means that we'll get much more * sane and consistent clipped repaints from Clutter. */ static void meta_window_group_get_preferred_width (ClutterActor *actor, gfloat for_height, gfloat *min_width, gfloat *nat_width) { *min_width = 0; *nat_width = 0; } static void meta_window_group_get_preferred_height (ClutterActor *actor, gfloat for_width, gfloat *min_height, gfloat *nat_height) { *min_height = 0; *nat_height = 0; } static void meta_window_group_class_init (MetaWindowGroupClass *klass) { ClutterActorClass *actor_class = CLUTTER_ACTOR_CLASS (klass); actor_class->paint = meta_window_group_paint; actor_class->get_paint_volume = meta_window_group_get_paint_volume; actor_class->get_preferred_width = meta_window_group_get_preferred_width; actor_class->get_preferred_height = meta_window_group_get_preferred_height; } static void meta_window_group_init (MetaWindowGroup *window_group) { } ClutterActor * meta_window_group_new (MetaDisplay *display) { MetaWindowGroup *window_group; window_group = g_object_new (META_TYPE_WINDOW_GROUP, NULL); window_group->display = display; return CLUTTER_ACTOR (window_group); } muffin-6.4.1/po/0000775000175000017500000000000014723361714012360 5ustar fabiofabiomuffin-6.4.1/po/pt.po0000664000175000017500000052575114723361714013362 0ustar fabiofabio# mutter's Portuguese Translation # Copyright © 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014 metacity/mutter # Distributed under the same licence as the metacity package # Duarte Loreto , 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014. # Pedro Albuquerque , 2015. # Tiago Santos , 2016. # Juliano de Souza Camargo , 2020. # msgid "" msgstr "" "Project-Id-Version: 3.10\n" "Report-Msgid-Bugs-To: https://gitlab.gnome.org/GNOME/mutter/issues\n" "POT-Creation-Date: 2020-05-29 14:52+0000\n" "PO-Revision-Date: 2020-09-14 21:15-0300\n" "Last-Translator: Juliano de Souza Camargo \n" "Language-Team: Portuguese <>\n" "Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Gtranslator 3.36.0\n" "Plural-Forms: nplurals=2; plural=(n != 1)\n" #: data/50-mutter-navigation.xml:6 msgid "Navigation" msgstr "Navegação" #: data/50-mutter-navigation.xml:9 msgid "Move window to workspace 1" msgstr "Mover janela para a área de trabalho 1" #: data/50-mutter-navigation.xml:12 msgid "Move window to workspace 2" msgstr "Mover janela para a área de trabalho 2" #: data/50-mutter-navigation.xml:15 msgid "Move window to workspace 3" msgstr "Mover janela para a área de trabalho 3" #: data/50-mutter-navigation.xml:18 msgid "Move window to workspace 4" msgstr "Mover janela para a área de trabalho 4" #: data/50-mutter-navigation.xml:21 msgid "Move window to last workspace" msgstr "Mover janela para a última área de trabalho" #: data/50-mutter-navigation.xml:24 msgid "Move window one workspace up" msgstr "Mover a janela uma área de trabalho acima" #: data/50-mutter-navigation.xml:27 msgid "Move window one workspace down" msgstr "Mover a janela uma área de trabalho abaixo" #: data/50-mutter-navigation.xml:30 msgid "Move window one monitor to the left" msgstr "Mover a janela um monitor para a esquerda" #: data/50-mutter-navigation.xml:33 msgid "Move window one monitor to the right" msgstr "Mover a janela um monitor para a direita" #: data/50-mutter-navigation.xml:36 msgid "Move window one monitor up" msgstr "Mover a janela um monitor acima" #: data/50-mutter-navigation.xml:39 msgid "Move window one monitor down" msgstr "Mover a janela um monitor abaixo" #: data/50-mutter-navigation.xml:43 msgid "Switch applications" msgstr "Mudar de aplicações" #: data/50-mutter-navigation.xml:48 msgid "Switch to previous application" msgstr "Mudar para a aplicação anterior" #: data/50-mutter-navigation.xml:52 msgid "Switch windows" msgstr "Mudar de janelas" #: data/50-mutter-navigation.xml:57 msgid "Switch to previous window" msgstr "Mudar para a janela anterior" #: data/50-mutter-navigation.xml:61 msgid "Switch windows of an application" msgstr "Alternar entre janelas de uma aplicação" #: data/50-mutter-navigation.xml:66 msgid "Switch to previous window of an application" msgstr "Mudar para a janela anterior de uma aplicação" #: data/50-mutter-navigation.xml:70 msgid "Switch system controls" msgstr "Alternar controlos de sistema" #: data/50-mutter-navigation.xml:75 msgid "Switch to previous system control" msgstr "Mudar para o controlo de sistema anterior" #: data/50-mutter-navigation.xml:79 msgid "Switch windows directly" msgstr "Alternar diretamente entre janelas" #: data/50-mutter-navigation.xml:84 msgid "Switch directly to previous window" msgstr "Mudar diretamente para a janela anterior" #: data/50-mutter-navigation.xml:88 msgid "Switch windows of an app directly" msgstr "Alternar diretamente entre janelas de uma aplicação" #: data/50-mutter-navigation.xml:93 msgid "Switch directly to previous window of an app" msgstr "Mudar diretamente para a janela anterior de uma aplicação" #: data/50-mutter-navigation.xml:97 msgid "Switch system controls directly" msgstr "Alternar diretamente entre controlos de sistema" #: data/50-mutter-navigation.xml:102 msgid "Switch directly to previous system control" msgstr "Mudar diretamente para o controlo de sistema anterior" #: data/50-mutter-navigation.xml:105 msgid "Hide all normal windows" msgstr "Ocultar todas as janelas normais" #: data/50-mutter-navigation.xml:108 msgid "Switch to workspace 1" msgstr "Mudar para a área de trabalho 1" #: data/50-mutter-navigation.xml:111 msgid "Switch to workspace 2" msgstr "Mudar para a área de trabalho 2" #: data/50-mutter-navigation.xml:114 msgid "Switch to workspace 3" msgstr "Mudar para a área de trabalho 3" #: data/50-mutter-navigation.xml:117 msgid "Switch to workspace 4" msgstr "Mudar para a área de trabalho 4" #: data/50-mutter-navigation.xml:120 msgid "Switch to last workspace" msgstr "Mudar para a última área de trabalho 1" #: data/50-mutter-navigation.xml:123 msgid "Move to workspace above" msgstr "Mover para a área de trabalho acima" #: data/50-mutter-navigation.xml:126 msgid "Move to workspace below" msgstr "Mover para a área de trabalho abaixo" #: data/50-mutter-system.xml:6 data/50-mutter-wayland.xml:6 msgid "System" msgstr "Sistema" #: data/50-mutter-system.xml:8 msgid "Show the run command prompt" msgstr "Mostrar a linha de comando de execução" #: data/50-mutter-system.xml:10 msgid "Show the activities overview" msgstr "Mostrar o resumo de atividades" #: data/50-mutter-wayland.xml:8 msgid "Restore the keyboard shortcuts" msgstr "Redefinir os atalhos de teclado" #: data/50-mutter-windows.xml:6 msgid "Windows" msgstr "Janelas" #: data/50-mutter-windows.xml:8 msgid "Activate the window menu" msgstr "Ativar o menu de janela" #: data/50-mutter-windows.xml:10 msgid "Toggle fullscreen mode" msgstr "Alternar modo de ecrã completo" #: data/50-mutter-windows.xml:12 msgid "Toggle maximization state" msgstr "Alternar estado de maximização" #: data/50-mutter-windows.xml:14 msgid "Maximize window" msgstr "Maximizar a janela" #: data/50-mutter-windows.xml:16 msgid "Restore window" msgstr "Restaurar a janela" #: data/50-mutter-windows.xml:18 msgid "Close window" msgstr "Fechar a janela" #: data/50-mutter-windows.xml:20 msgid "Hide window" msgstr "Esconder a janela" #: data/50-mutter-windows.xml:22 msgid "Move window" msgstr "Mover a janela" #: data/50-mutter-windows.xml:24 msgid "Resize window" msgstr "Redimensionar a janela" #: data/50-mutter-windows.xml:27 msgid "Toggle window on all workspaces or one" msgstr "Alternar janela em todas as áreas de trabalho ou só numa" #: data/50-mutter-windows.xml:29 msgid "Raise window if covered, otherwise lower it" msgstr "Elevar a janela se estiver tapada, caso contrário baixá-la" #: data/50-mutter-windows.xml:31 msgid "Raise window above other windows" msgstr "Elevar janela acima de outras janelas" #: data/50-mutter-windows.xml:33 msgid "Lower window below other windows" msgstr "Baixar janela abaixo de outras janelas" #: data/50-mutter-windows.xml:35 msgid "Maximize window vertically" msgstr "Maximizar janela verticalmente" #: data/50-mutter-windows.xml:37 msgid "Maximize window horizontally" msgstr "Maximizar janela horizontalmente" #: data/50-mutter-windows.xml:41 msgid "View split on left" msgstr "Ver a divisão à esquerda" #: data/50-mutter-windows.xml:45 msgid "View split on right" msgstr "Ver a divisão à direita" #: data/mutter.desktop.in:4 msgid "Mutter" msgstr "Mutter" #: data/org.gnome.mutter.gschema.xml.in:7 msgid "Modifier to use for extended window management operations" msgstr "Modificador a utilizar para operações estendidas de gestão de janelas" #: data/org.gnome.mutter.gschema.xml.in:8 #| msgid "" #| "This key will initiate the \"overlay\", which is a combination window " #| "overview and application launching system. The default is intended to be " #| "the \"Windows key\" on PC hardware. It's expected that this binding " #| "either the default or set to the empty string." msgid "" "This key will initiate the “overlay”, which is a combination window overview " "and application launching system. The default is intended to be the “Windows " "key” on PC hardware. It’s expected that this binding either the default or " "set to the empty string." msgstr "" "Esta chave irá iniciar a \"sobreposição\", que é um sistema de visão " "combinada de janelas e arranque de aplicações. A predefinição é a \"tecla " "Windows\" em computadores PC. Espera-se que esta associação seja a " "predefinição ou uma cadeia vazia." #: data/org.gnome.mutter.gschema.xml.in:20 msgid "Attach modal dialogs" msgstr "Anexar diálogos modais" #: data/org.gnome.mutter.gschema.xml.in:21 msgid "" "When true, instead of having independent titlebars, modal dialogs appear " "attached to the titlebar of the parent window and are moved together with " "the parent window." msgstr "" "Quando verdadeiro, em vez de terem barras de título independentes, os " "diálogos modais surgem anexados à barra de título da janela-mãe e são " "movidos juntamente com a janela-mãe." #: data/org.gnome.mutter.gschema.xml.in:30 msgid "Enable edge tiling when dropping windows on screen edges" msgstr "Ativar ajuste em grelha ao largar janelas nos limites do ecrã" #: data/org.gnome.mutter.gschema.xml.in:31 msgid "" "If enabled, dropping windows on vertical screen edges maximizes them " "vertically and resizes them horizontally to cover half of the available " "area. Dropping windows on the top screen edge maximizes them completely." msgstr "" "Se ativo, ao largar janelas nos limites verticais do ecrã maximiza-as " "verticalmente e redimensiona-as horizontalmente para cobrirem metade da área " "disponível. Largar janelas no limite superior do ecrã maximiza-as " "completamente." #: data/org.gnome.mutter.gschema.xml.in:40 msgid "Workspaces are managed dynamically" msgstr "Áreas de trabalho geridas dinamicamente" #: data/org.gnome.mutter.gschema.xml.in:41 #| msgid "" #| "Determines whether workspaces are managed dynamically or whether there's " #| "a static number of workspaces (determined by the num-workspaces key in " #| "org.gnome.desktop.wm.preferences)." msgid "" "Determines whether workspaces are managed dynamically or whether there’s a " "static number of workspaces (determined by the num-workspaces key in org." "gnome.desktop.wm.preferences)." msgstr "" "Determina se as áreas de trabalho são geridas dinamicamente ou se existe um " "número estático de áreas de trabalho (determinado pela chave num-workspaces " "em org.gnome.desktop.wm.preferences)." #: data/org.gnome.mutter.gschema.xml.in:50 msgid "Workspaces only on primary" msgstr "Áreas de trabalho só no principal" #: data/org.gnome.mutter.gschema.xml.in:51 msgid "" "Determines whether workspace switching should happen for windows on all " "monitors or only for windows on the primary monitor." msgstr "" "Determina se a troca de área de trabalho deverá ocorrer para janelas em " "todos os monitores ou apenas para janelas no monitor principal." #: data/org.gnome.mutter.gschema.xml.in:59 msgid "No tab popup" msgstr "Sem popup ao utilizar tabulador" #: data/org.gnome.mutter.gschema.xml.in:60 msgid "" "Determines whether the use of popup and highlight frame should be disabled " "for window cycling." msgstr "" "Determina se deverá ou não ser desativado o popup e o realce na janela ao " "circular entre janelas." #: data/org.gnome.mutter.gschema.xml.in:68 msgid "Delay focus changes until the pointer stops moving" msgstr "Adiar as alterações de foco até que o ponteiro pare de se mover" #: data/org.gnome.mutter.gschema.xml.in:69 #| msgid "" #| "If set to true, and the focus mode is either \"sloppy\" or \"mouse\" then " #| "the focus will not be changed immediately when entering a window, but " #| "only after the pointer stops moving." msgid "" "If set to true, and the focus mode is either “sloppy” or “mouse” then the " "focus will not be changed immediately when entering a window, but only after " "the pointer stops moving." msgstr "" "Se verdadeiro, e o modo de foco for \"sloppy\" ou \"mouse\" então o foco não " "será alterado imediatamente ao entrar numa janela mas apenas quando o " "ponteiro parar de se mover." #: data/org.gnome.mutter.gschema.xml.in:79 msgid "Draggable border width" msgstr "Largura da margem de arrasto" #: data/org.gnome.mutter.gschema.xml.in:80 #| msgid "" #| "The amount of total draggable borders. If the theme's visible borders are " #| "not enough, invisible borders will be added to meet this value." msgid "" "The amount of total draggable borders. If the theme’s visible borders are " "not enough, invisible borders will be added to meet this value." msgstr "" "A quantidade de margem total arrastável. Se as margens visíveis do tema não " "forem suficientes, serão adicionadas margens invisíveis para atingir este " "valor." #: data/org.gnome.mutter.gschema.xml.in:89 msgid "Auto maximize nearly monitor sized windows" msgstr "Maximizar automaticamente janelas de dimensão aproximada à do monitor" #: data/org.gnome.mutter.gschema.xml.in:90 msgid "" "If enabled, new windows that are initially the size of the monitor " "automatically get maximized." msgstr "" "Se ativo, novas janelas que sejam inicialmente do tamanho do monitor são " "maximizadas automaticamente." #: data/org.gnome.mutter.gschema.xml.in:98 msgid "Place new windows in the center" msgstr "Posicionar novas janelas no centro" #: data/org.gnome.mutter.gschema.xml.in:99 msgid "" "When true, the new windows will always be put in the center of the active " "screen of the monitor." msgstr "" "Se ativo, as novas janelas são sempre posicionadas no centro do ecrã do " "monitor ativo." #: data/org.gnome.mutter.gschema.xml.in:107 msgid "Enable experimental features" msgstr "Ativar as funcionalidades experimentais" #: data/org.gnome.mutter.gschema.xml.in:108 #, fuzzy msgid "" "To enable experimental features, add the feature keyword to the list. " "Whether the feature requires restarting the compositor depends on the given " "feature. Any experimental feature is not required to still be available, or " "configurable. Don’t expect adding anything in this setting to be future " "proof. Currently possible keywords: • “scale-monitor-framebuffer” — makes " "mutter default to layout logical monitors in a logical pixel coordinate " "space, while scaling monitor framebuffers instead of window content, to " "manage HiDPI monitors. Does not require a restart. • “rt-scheduler” — makes " "mutter request a low priority real-time scheduling. The executable or user " "must have CAP_SYS_NICE. Requires a restart. • “autostart-xwayland” — " "initializes Xwayland lazily if there are X11 clients. Requires restart." msgstr "" "Para ativar as funcionalidades experimentais, adicione a palavra-chave da " "funcionalidade na lista. Se deve reiniciar o compositor após ativá-la, " "depende de cada funcionalidade. Qualquer funcionalidade experimental não " "necessita de estar disponível ou configurável. Não espere que adicionar algo " "nas definições seja mantido pode tempo indeterminado. Atualmente as palavras-" "chave são: • “scale-monitor-framebuffer” — makes mutter default to layout " "logical monitors in a logical pixel coordinate space, while scaling monitor " "framebuffers instead of window content, to manage HiDPI monitors. Does not " "require a restart. • “rt-scheduler” — makes mutter request a low priority " "real-time scheduling. The executable or user must have CAP_SYS_NICE. " "Requires a restart. • “autostart-xwayland” — initializes Xwayland lazily if " "there are X11 clients. Requires restart." #: data/org.gnome.mutter.gschema.xml.in:134 msgid "Modifier to use to locate the pointer" msgstr "Modificador para localizar o ponteiro" #: data/org.gnome.mutter.gschema.xml.in:135 msgid "This key will initiate the “locate pointer” action." msgstr "Esta chave inicializa a ação “localizar ponteiro”" #: data/org.gnome.mutter.gschema.xml.in:142 msgid "Timeout for check-alive ping" msgstr "Expirou o teste de atividade " #: data/org.gnome.mutter.gschema.xml.in:143 msgid "" "Number of milliseconds a client has to respond to a ping request in order to " "not be detected as frozen. Using 0 will disable the alive check completely." msgstr "" "Número em milissegundos que um cliente precisa responder a um teste de " "atividade de maneira que não seja detetado como inativo. Usar 0 desativará o " "teste de atividade completamente." #: data/org.gnome.mutter.gschema.xml.in:165 msgid "Select window from tab popup" msgstr "Selecionar a janela no popup de tabulador" #: data/org.gnome.mutter.gschema.xml.in:170 msgid "Cancel tab popup" msgstr "Cancelar o popup de tabulador" #: data/org.gnome.mutter.gschema.xml.in:175 #| msgid "Switch monitor" msgid "Switch monitor configurations" msgstr "Alternar configurações de ecrã" #: data/org.gnome.mutter.gschema.xml.in:180 msgid "Rotates the built-in monitor configuration" msgstr "Alternar as configurações nativas do ecrã" #: data/org.gnome.mutter.wayland.gschema.xml.in:6 msgid "Switch to VT 1" msgstr "Alternar para a área de trabalho 1" #: data/org.gnome.mutter.wayland.gschema.xml.in:10 msgid "Switch to VT 2" msgstr "Alternar para a área de trabalho 2" #: data/org.gnome.mutter.wayland.gschema.xml.in:14 msgid "Switch to VT 3" msgstr "Alternar para a área de trabalho 3" #: data/org.gnome.mutter.wayland.gschema.xml.in:18 msgid "Switch to VT 4" msgstr "Alternar para a área de trabalho 4" #: data/org.gnome.mutter.wayland.gschema.xml.in:22 msgid "Switch to VT 5" msgstr "Mover para a área de trabalho 5" #: data/org.gnome.mutter.wayland.gschema.xml.in:26 msgid "Switch to VT 6" msgstr "Mover para a área de trabalho 6" #: data/org.gnome.mutter.wayland.gschema.xml.in:30 msgid "Switch to VT 7" msgstr "Mover para a área de trabalho 7" #: data/org.gnome.mutter.wayland.gschema.xml.in:34 msgid "Switch to VT 8" msgstr "Mover para a área de trabalho 8" #: data/org.gnome.mutter.wayland.gschema.xml.in:38 msgid "Switch to VT 9" msgstr "Mover para a área de trabalho 9" #: data/org.gnome.mutter.wayland.gschema.xml.in:42 msgid "Switch to VT 10" msgstr "Mover para a área de trabalho 10" #: data/org.gnome.mutter.wayland.gschema.xml.in:46 msgid "Switch to VT 11" msgstr "Mover para a área de trabalho 11" #: data/org.gnome.mutter.wayland.gschema.xml.in:50 msgid "Switch to VT 12" msgstr "Mover para a área de trabalho 12" #: data/org.gnome.mutter.wayland.gschema.xml.in:54 msgid "Re-enable shortcuts" msgstr "Reativar os atalhos" #: data/org.gnome.mutter.wayland.gschema.xml.in:64 msgid "Allow X11 grabs to lock keyboard focus with Xwayland" msgstr "" #: data/org.gnome.mutter.wayland.gschema.xml.in:65 msgid "" "Allow all keyboard events to be routed to X11 “override redirect” windows " "with a grab when running in Xwayland. This option is to support X11 clients " "which map an “override redirect” window (which do not receive keyboard " "focus) and issue a keyboard grab to force all keyboard events to that " "window. This option is seldom used and has no effect on regular X11 windows " "which can receive keyboard focus under normal circumstances. For a X11 grab " "to be taken into account under Wayland, the client must also either send a " "specific X11 ClientMessage to the root window or be among the applications " "white-listed in key “xwayland-grab-access-rules”." msgstr "" #: data/org.gnome.mutter.wayland.gschema.xml.in:84 msgid "Xwayland applications allowed to issue keyboard grabs" msgstr "" #: data/org.gnome.mutter.wayland.gschema.xml.in:85 msgid "" "List the resource names or resource class of X11 windows either allowed or " "not allowed to issue X11 keyboard grabs under Xwayland. The resource name or " "resource class of a given X11 window can be obtained using the command " "“xprop WM_CLASS”. Wildcards “*” and jokers “?” in the values are supported. " "Values starting with “!” are blacklisted, which has precedence over the " "whitelist, to revoke applications from the default system list. The default " "system list includes the following applications: " "“@XWAYLAND_GRAB_DEFAULT_ACCESS_RULES@” Users can break an existing grab by " "using the specific keyboard shortcut defined by the keybinding key “restore-" "shortcuts”." msgstr "" #. TRANSLATORS: This string refers to a button that switches between #. * different modes. #. #: src/backends/meta-input-settings.c:2631 #, c-format #| msgid "Mode Switch: Mode %d" msgid "Mode Switch (Group %d)" msgstr "Alteração de modo (Grupo %d)" #. TRANSLATORS: This string refers to an action, cycles drawing tablets' #. * mapping through the available outputs. #. #: src/backends/meta-input-settings.c:2654 msgid "Switch monitor" msgstr "Alternar monitor" #: src/backends/meta-input-settings.c:2656 msgid "Show on-screen help" msgstr "Mostrar ajuda no ecrã" #: src/backends/meta-monitor.c:226 msgid "Built-in display" msgstr "Ecrã embutido" #: src/backends/meta-monitor.c:255 msgid "Unknown" msgstr "Desconhecido" #: src/backends/meta-monitor.c:257 msgid "Unknown Display" msgstr "Ecrã desconhecido" #: src/backends/meta-monitor.c:265 #, c-format #| msgid "%s %s" msgctxt "" "This is a monitor vendor name, followed by a size in inches, like 'Dell 15\"'" msgid "%s %s" msgstr "%s %s" #: src/backends/meta-monitor.c:273 #, c-format #| msgid "%s %s" msgctxt "" "This is a monitor vendor name followed by product/model name where size in " "inches could not be calculated, e.g. Dell U2414H" msgid "%s %s" msgstr "%s %s" #. Translators: this string will appear in Sysprof #: src/backends/meta-profiler.c:79 #| msgid "Compositing Manager" msgid "Compositor" msgstr "Compositor" #. This probably means that a non-WM compositor like xcompmgr is running; #. * we have no way to get it to exit #: src/compositor/compositor.c:533 #, c-format #| msgid "" #| "Another compositing manager is already running on screen %i on display " #| "\"%s\"." msgid "" "Another compositing manager is already running on screen %i on display “%s”." msgstr "" "Já se encontra em execução outro gestor de janelas no ecrã %i do ecrã “%s”." #: src/core/bell.c:192 msgid "Bell event" msgstr "Evento de campainha" #: src/core/main.c:190 msgid "Disable connection to session manager" msgstr "Desativar a ligação ao gestor de sessão" #: src/core/main.c:196 msgid "Replace the running window manager" msgstr "Substituir o gestor de janelas em execução" #: src/core/main.c:202 msgid "Specify session management ID" msgstr "Especificar a ID de gestão de sessão" #: src/core/main.c:207 msgid "X Display to use" msgstr "Ecrã X a utilizar" #: src/core/main.c:213 msgid "Initialize session from savefile" msgstr "Inicializar a sessão a partir de um ficheiro de gravação de sessão" #: src/core/main.c:219 msgid "Make X calls synchronous" msgstr "Fazer as chamadas X sincronamente" #: src/core/main.c:226 msgid "Run as a wayland compositor" msgstr "Executar como compositor wayland" #: src/core/main.c:232 msgid "Run as a nested compositor" msgstr "Executar como compositor aninhado" #: src/core/main.c:238 msgid "Run wayland compositor without starting Xwayland" msgstr "Executar o compositor wayland sem a utilização do Xwayland" #: src/core/main.c:246 msgid "Run as a full display server, rather than nested" msgstr "Executar como servidor de ecrã completo, em vez de aninhado" #: src/core/main.c:252 msgid "Run with X11 backend" msgstr "Executar com a infira-estrutura X11" #. Translators: %s is a window title #: src/core/meta-close-dialog-default.c:151 #, c-format msgid "“%s” is not responding." msgstr "“%s” não está a responder." #: src/core/meta-close-dialog-default.c:153 msgid "Application is not responding." msgstr "A aplicação não está a responder." #: src/core/meta-close-dialog-default.c:158 msgid "" "You may choose to wait a short while for it to continue or force the " "application to quit entirely." msgstr "" "Poderá aguardar uns instantes para que continue ou forçar a aplicação a " "terminar definitivamente." #: src/core/meta-close-dialog-default.c:165 msgid "_Force Quit" msgstr "_Forçar terminar" #: src/core/meta-close-dialog-default.c:165 msgid "_Wait" msgstr "_Aguardar" #: src/core/mutter.c:38 #, c-format #| msgid "" #| "mutter %s\n" #| "Copyright (C) 2001-%d Havoc Pennington, Red Hat, Inc., and others\n" #| "This is free software; see the source for copying conditions.\n" #| "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A " #| "PARTICULAR PURPOSE.\n" msgid "" "mutter %s\n" "Copyright © 2001-%d Havoc Pennington, Red Hat, Inc., and others\n" "This is free software; see the source for copying conditions.\n" "There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A " "PARTICULAR PURPOSE.\n" msgstr "" "mutter %s\n" "Copyright © 2001-%d Havoc Pennington, Red Hat, Inc., e outros\n" "Esta é uma aplicação livre; consulte o código fonte para condições de " "cópia.\n" "NÃO existe qualquer garantia; nem sequer de COMERCIALIZAÇÃO ou ADEQUAÇÃO A " "UM PROPÓSITO ESPECÍFICO.\n" #: src/core/mutter.c:52 msgid "Print version" msgstr "Imprimir a versão" #: src/core/mutter.c:58 msgid "Mutter plugin to use" msgstr "Extensão Mutter a utilizar" #: src/core/prefs.c:1911 #, c-format msgid "Workspace %d" msgstr "Área de trabalho %d" #: src/core/util.c:122 msgid "Mutter was compiled without support for verbose mode\n" msgstr "O Mutter foi compilado sem suporte para modo verboso\n" #: src/wayland/meta-wayland-tablet-pad.c:568 #, c-format msgid "Mode Switch: Mode %d" msgstr "Alteração de Modo: Modo %d" #: src/x11/meta-x11-display.c:676 #, c-format #| msgid "" #| "Display \"%s\" already has a window manager; try using the --replace " #| "option to replace the current window manager." msgid "" "Display “%s” already has a window manager; try using the --replace option to " "replace the current window manager." msgstr "" "O ecrã “%s” já tem um gestor de janelas; tente utilizar a opção --replace " "para substituir o gestor de janelas atual." #: src/x11/meta-x11-display.c:1089 msgid "Failed to initialize GDK\n" msgstr "Falhou ao iniciar o GDK\n" #: src/x11/meta-x11-display.c:1113 #, c-format #| msgid "Failed to open X Window System display '%s'\n" msgid "Failed to open X Window System display “%s”\n" msgstr "Falha ao abrir ecrã “%s” do sistema Janelas X\n" #: src/x11/meta-x11-display.c:1196 #, c-format #| msgid "Screen %d on display '%s' is invalid\n" msgid "Screen %d on display “%s” is invalid\n" msgstr "Ecrã %d no monitor “%s” é inválido\n" #: src/x11/meta-x11-selection-input-stream.c:460 #, c-format msgid "Format %s not supported" msgstr "O formato “%s” não é suportado" #: src/x11/session.c:1821 #| msgid "" #| "These windows do not support "save current setup" and will have " #| "to be restarted manually next time you log in." msgid "" "These windows do not support “save current setup” and will have to be " "restarted manually next time you log in." msgstr "" "Estas janelas não suportam “gravar configuração atual” e terão de ser " "reiniciadas manualmente da próxima vez que iniciar sessão." #: src/x11/window-props.c:569 #, c-format msgid "%s (on %s)" msgstr "%s (em %s)" #~ msgid "Move window one workspace to the left" #~ msgstr "Mover a janela uma área de trabalho para a esquerda" #~ msgid "Move window one workspace to the right" #~ msgstr "Mover a janela uma área de trabalho para a direita" #~ msgid "Move to workspace left" #~ msgstr "Mover para a área de trabalho à esquerda" #~ msgid "Move to workspace right" #~ msgstr "Mover para a área de trabalho à direita" #~ msgid "Toggle shaded state" #~ msgstr "Alternar estado sombreado" #~ msgid "background texture could not be created from file" #~ msgstr "incapaz de criar de ficheiro a textura de fundo" #~ msgid "Unknown window information request: %d" #~ msgstr "Pedido de informação de janela desconhecido: %d" #~ msgid "Missing %s extension required for compositing" #~ msgstr "Incapaz de encontrar a extensão %s, necessária para a composição" #~ msgid "" #~ "Some other program is already using the key %s with modifiers %x as a " #~ "binding\n" #~ msgstr "" #~ "A tecla %s com os modificadores %x já está a ser utilizada como atalho " #~ "por outra aplicação\n" #~ msgid "\"%s\" is not a valid accelerator\n" #~ msgstr "\"%s\" não é um atalho válido\n" #~ msgid "Failed to scan themes directory: %s\n" #~ msgstr "Falha ao analizar diretório de temas: %s\n" #~ msgid "" #~ "Could not find a theme! Be sure %s exists and contains the usual themes.\n" #~ msgstr "" #~ "Incapaz de encontrar um tema! Certifique-se que %s existe e contém os " #~ "temas normais.\n" #~ msgid "" #~ "Workarounds for broken applications disabled. Some applications may not " #~ "behave properly.\n" #~ msgstr "" #~ "Recursos para aplicações problemáticas inativos. Algumas aplicações " #~ "poderão não funcionar corretamente.\n" #~ msgid "Could not parse font description \"%s\" from GSettings key %s\n" #~ msgstr "" #~ "Incapaz de processar a descrição de fonte \"%s\" da chave GSettings %s\n" #~ msgid "" #~ "\"%s\" found in configuration database is not a valid value for mouse " #~ "button modifier\n" #~ msgstr "" #~ "\"%s\" encontrado na base de dados de configuração não é um valor válido " #~ "para o modificador de botão de rato\n" #~ msgid "" #~ "\"%s\" found in configuration database is not a valid value for " #~ "keybinding \"%s\"\n" #~ msgstr "" #~ "\"%s\" encontrado na base de dados de configuração não é um valor válido " #~ "para o atalho de teclado \"%s\"\n" #~ msgid "" #~ "Could not acquire window manager selection on screen %d display \"%s\"\n" #~ msgstr "" #~ "Incapaz de obter seleção do gestor de janelas no ecrã %d monitor \"%s\"\n" #~ msgid "Screen %d on display \"%s\" already has a window manager\n" #~ msgstr "Ecrã %d no monitor \"%s\" já tem um gestor de janelas\n" #~ msgid "Could not release screen %d on display \"%s\"\n" #~ msgstr "Incapaz libertar ecrã %d no monitor \"%s\"\n" #~ msgid "Could not create directory '%s': %s\n" #~ msgstr "Incapaz de criar diretório '%s': %s\n" #~ msgid "Could not open session file '%s' for writing: %s\n" #~ msgstr "Incapaz de abrir ficheiro de sessão '%s' para escrita: %s\n" #~ msgid "Error writing session file '%s': %s\n" #~ msgstr "Erro ao escrever ficheiro de sessão '%s': %s\n" #~ msgid "Error closing session file '%s': %s\n" #~ msgstr "Erro ao fechar ficheiro de sessão '%s': %s\n" #~ msgid "Failed to parse saved session file: %s\n" #~ msgstr "Falha ao processar ficheiro de sessão gravado: %s\n" #~ msgid " attribute seen but we already have the session ID" #~ msgstr "" #~ "Atributo observado mas o ID de sessão já é conhecido" #~ msgid "Unknown attribute %s on <%s> element" #~ msgstr "Atributo %s desconhecido no elemento <%s>" #~ msgid "nested tag" #~ msgstr "etiqueta de encadeada" #~ msgid "Unknown element %s" #~ msgstr "Elemento %s desconhecido" #~ msgid "Failed to open debug log: %s\n" #~ msgstr "Falha ao abrir registo de depuração: %s\n" #~ msgid "Failed to fdopen() log file %s: %s\n" #~ msgstr "Falha ao efetuar fdopen() do ficheiro de registo %s: %s\n" #~ msgid "Opened log file %s\n" #~ msgstr "Ficheiro de registo %s aberto\n" #~ msgid "Window manager: " #~ msgstr "Gestor de janelas: " #~ msgid "Bug in window manager: " #~ msgstr "Erro no gestor de janelas: " #~ msgid "Window manager warning: " #~ msgstr "Aviso do gestor de janelas: " #~ msgid "Window manager error: " #~ msgstr "Erro do gestor de janelas: " #~ msgid "" #~ "Window %s sets SM_CLIENT_ID on itself, instead of on the WM_CLIENT_LEADER " #~ "window as specified in the ICCCM.\n" #~ msgstr "" #~ "Janela %s define SM_CLIENT_ID em si própria, em vez de na janela " #~ "WM_CLIENT_LEADER tal como especificado no ICCCM.\n" #~ msgid "" #~ "Window %s sets an MWM hint indicating it isn't resizable, but sets min " #~ "size %d x %d and max size %d x %d; this doesn't make much sense.\n" #~ msgstr "" #~ "Janela %s define uma dica MWM indicando que não é redimensionável, mas " #~ "define tamanho mínimo %d x %d e tamanho máximo %d x %d; isto não faz " #~ "muito sentido.\n" #~ msgid "Application set a bogus _NET_WM_PID %lu\n" #~ msgstr "Aplicação definiu um _NET_WM_PID %lu fictício\n" #~ msgid "Invalid WM_TRANSIENT_FOR window 0x%lx specified for %s.\n" #~ msgstr "Janela 0x%lx de WM_TRANSIENT_FOR inválido especificada para %s.\n" #~ msgid "WM_TRANSIENT_FOR window 0x%lx for %s would create loop.\n" #~ msgstr "" #~ "Janela 0x%lx de WM_TRANSIENT_FOR para %s iria criar um ciclo infinito.\n" #~ msgid "" #~ "Window 0x%lx has property %s\n" #~ "that was expected to have type %s format %d\n" #~ "and actually has type %s format %d n_items %d.\n" #~ "This is most likely an application bug, not a window manager bug.\n" #~ "The window has title=\"%s\" class=\"%s\" name=\"%s\"\n" #~ msgstr "" #~ "A janela 0x%lx possui a propriedade %s\n" #~ "que se esperava ter o tipo %s formato %d\n" #~ "mas na verdade tem o tipo %s formato %d nº itens %d.\n" #~ "Isto é provavelmente um erro da aplicação, não um erro do gestor de " #~ "janelas.\n" #~ "A janela tem o título=\"%s\" classe=\"%s\" nome=\"%s\"\n" #~ msgid "Property %s on window 0x%lx contained invalid UTF-8\n" #~ msgstr "Propriedade %s na janela 0x%lx continha UTF-8 inválido\n" #~ msgid "" #~ "Property %s on window 0x%lx contained invalid UTF-8 for item %d in the " #~ "list\n" #~ msgstr "" #~ "Propriedade %s na janela 0x%lx continha UTF-8 inválido para item %d na " #~ "lista\n" #~ msgid "Mi_nimize" #~ msgstr "Mi_nimizar" #~ msgid "Ma_ximize" #~ msgstr "Ma_ximizar" #~ msgid "Unma_ximize" #~ msgstr "Resta_urar" #~ msgid "Roll _Up" #~ msgstr "_Enrolar" #~ msgid "_Unroll" #~ msgstr "D_esenrolar" #~ msgid "_Move" #~ msgstr "_Mover" #~ msgid "_Resize" #~ msgstr "_Redimensionar" #~ msgid "Move Titlebar On_screen" #~ msgstr "Mover a _Barra de Títulos no Ecrã" #~ msgid "Always on _Top" #~ msgstr "Sempre no _Topo" #~ msgid "_Always on Visible Workspace" #~ msgstr "_Sempre na Área de Trabalho Visível" #~ msgid "_Only on This Workspace" #~ msgstr "_Apenas nesta Área de Trabalho" #~ msgid "Move to Workspace _Left" #~ msgstr "Mover para a Área de Trabalho à _Esquerda" #~ msgid "Move to Workspace R_ight" #~ msgstr "Mover para a Área de Trabalho à _Direita" #~ msgid "Move to Workspace _Up" #~ msgstr "Mover para a Área de Trabalho _Acima" #~ msgid "Move to Workspace _Down" #~ msgstr "Mover para a Área de Trabalho A_baixo" #~ msgid "_Close" #~ msgstr "_Fechar" #~ msgid "Workspace %d%n" #~ msgstr "Área de Trabalho %d%n" #~ msgid "Workspace 1_0" #~ msgstr "Área de Trabalho 1_0" #~ msgid "Workspace %s%d" #~ msgstr "Área de Trabalho %s%d" #~ msgid "Move to Another _Workspace" #~ msgstr "Mover para Outra Área de _Trabalho" #~ msgid "Shift" #~ msgstr "Shift" #~ msgid "Ctrl" #~ msgstr "Ctrl" #~ msgid "Alt" #~ msgstr "Alt" #~ msgid "Meta" #~ msgstr "Meta" #~ msgid "Super" #~ msgstr "Super" #~ msgid "Hyper" #~ msgstr "Hyper" #~ msgid "Mod2" #~ msgstr "Mod2" #~ msgid "Mod3" #~ msgstr "Mod3" #~ msgid "Mod4" #~ msgstr "Mod4" #~ msgid "Mod5" #~ msgstr "Mod5" #~ msgid "%d x %d" #~ msgstr "%d x %d" #~ msgid "top" #~ msgstr "superior" #~ msgid "bottom" #~ msgstr "inferior" #~ msgid "left" #~ msgstr "esquerda" #~ msgid "right" #~ msgstr "direita" #~ msgid "frame geometry does not specify \"%s\" dimension" #~ msgstr "geometria de moldura não especifica dimensão \"%s\"" #~ msgid "frame geometry does not specify dimension \"%s\" for border \"%s\"" #~ msgstr "" #~ "geometria de moldura não especifica dimensão \"%s\" para margem \"%s\"" #~ msgid "Button aspect ratio %g is not reasonable" #~ msgstr "Rácio de aparência de botão %g não é um valor razoável" #~ msgid "Frame geometry does not specify size of buttons" #~ msgstr "Geometria de moldura não especifica dimensão dos botões" #~ msgid "Gradients should have at least two colors" #~ msgstr "Gradientes deverão ter pelo menos duas cores" #~ msgid "" #~ "GTK custom color specification must have color name and fallback in " #~ "parentheses, e.g. gtk:custom(foo,bar); could not parse \"%s\"" #~ msgstr "" #~ "Especificação de cor GTK tem de ter o nome da cor e o recurso entre " #~ "parentesis, por ex. gtk:custom(foo,bar); incapaz de processar \"%s\"" #~ msgid "" #~ "Invalid character '%c' in color_name parameter of gtk:custom, only A-Za-" #~ "z0-9-_ are valid" #~ msgstr "" #~ "Caracter '%c' inválido no parâmetro color_name de gtk:custom, apenas são " #~ "válidos os carateres A-Za-z0-9-_" #~ msgid "" #~ "Gtk:custom format is \"gtk:custom(color_name,fallback)\", \"%s\" does not " #~ "fit the format" #~ msgstr "" #~ "Formato Gtk:custom é \"gtk:custom(color_name,fallback)\", \"%s\" não " #~ "cumpre o formato" #~ msgid "" #~ "GTK color specification must have the state in brackets, e.g. gtk:" #~ "fg[NORMAL] where NORMAL is the state; could not parse \"%s\"" #~ msgstr "" #~ "Especificação de cor GTK tem de ter o estado entre parentesis retos, por " #~ "ex. gtk:fg[NORMAL] em que NORMAL é o estado; incapaz de processar \"%s\"" #~ msgid "" #~ "GTK color specification must have a close bracket after the state, e.g. " #~ "gtk:fg[NORMAL] where NORMAL is the state; could not parse \"%s\"" #~ msgstr "" #~ "Especificação de cor GTK tem de ter um parentesis reto de fecho após o " #~ "estado, por ex. gtk:fg[NORMAL] em que NORMAL é o estado; incapaz de " #~ "processar \"%s\"" #~ msgid "Did not understand state \"%s\" in color specification" #~ msgstr "Incapaz de compreender o estado \"%s\" na especificação de cor" #~ msgid "Did not understand color component \"%s\" in color specification" #~ msgstr "" #~ "Incapaz de compreender componente de cor \"%s\" na especificação de cor" #~ msgid "" #~ "Blend format is \"blend/bg_color/fg_color/alpha\", \"%s\" does not fit " #~ "the format" #~ msgstr "" #~ "Formato de mistura é \"blend/bg_color/fg_color/alpha\", \"%s\" não cumpre " #~ "o formato" #~ msgid "Could not parse alpha value \"%s\" in blended color" #~ msgstr "Incapaz de processar o valor alfa \"%s\" na mistura de cor" #~ msgid "Alpha value \"%s\" in blended color is not between 0.0 and 1.0" #~ msgstr "Valor alfa \"%s\" na mistura de cor não está entre 0.0 e 1.0" #~ msgid "" #~ "Shade format is \"shade/base_color/factor\", \"%s\" does not fit the " #~ "format" #~ msgstr "" #~ "Formato de sombra é \"shade/base_color/fator\", \"%s\" não cumpre o " #~ "formato" #~ msgid "Could not parse shade factor \"%s\" in shaded color" #~ msgstr "" #~ "Incapaz de processar o valor de fator de sombra \"%s\" na cor sombreada" #~ msgid "Shade factor \"%s\" in shaded color is negative" #~ msgstr "Fator de sombreado \"%s\" na cor sombreada é negativo" #~ msgid "Could not parse color \"%s\"" #~ msgstr "Incapaz de processar cor \"%s\"" #~ msgid "Coordinate expression contains character '%s' which is not allowed" #~ msgstr "Expressão de coordenada contém caracter '%s' que não é permitido" #~ msgid "" #~ "Coordinate expression contains floating point number '%s' which could not " #~ "be parsed" #~ msgstr "" #~ "Expressão de coordenada contém número decimal '%s' que não pode ser " #~ "processado" #~ msgid "" #~ "Coordinate expression contains integer '%s' which could not be parsed" #~ msgstr "" #~ "Expressão de coordenada contém número inteiro '%s' que não pode ser " #~ "processado" #~ msgid "" #~ "Coordinate expression contained unknown operator at the start of this " #~ "text: \"%s\"" #~ msgstr "" #~ "Expressão de coordenada contém operador desconhecido no início deste " #~ "texto: \"%s\"" #~ msgid "Coordinate expression was empty or not understood" #~ msgstr "Expressão de coordenada está vazia ou não foi compreendida" #~ msgid "Coordinate expression results in division by zero" #~ msgstr "Expressão de coordenada resulta em divisão por zero" #~ msgid "" #~ "Coordinate expression tries to use mod operator on a floating-point number" #~ msgstr "" #~ "Expressão de coordenada tenta utilizar operador mod num número decimal" #~ msgid "" #~ "Coordinate expression has an operator \"%s\" where an operand was expected" #~ msgstr "" #~ "Expressão de coordenada tem um operador \"%s\" onde era esperado um " #~ "operando" #~ msgid "Coordinate expression had an operand where an operator was expected" #~ msgstr "" #~ "Expressão de coordenada tem um operando onde era esperado um operador" #~ msgid "Coordinate expression ended with an operator instead of an operand" #~ msgstr "" #~ "Expressão de coordenada terminou com um operador em vez de um operando" #~ msgid "" #~ "Coordinate expression has operator \"%c\" following operator \"%c\" with " #~ "no operand in between" #~ msgstr "" #~ "Expressão de coordenada tem o operador \"%c\" seguido do operador \"%c\" " #~ "sem um operando entre os dois" #~ msgid "Coordinate expression had unknown variable or constant \"%s\"" #~ msgstr "" #~ "Expressão de coordenada tem uma variável ou constante \"%s\" desconhecida" #~ msgid "Coordinate expression parser overflowed its buffer." #~ msgstr "O processador de expressão de coordenada transbordou o seu buffer." #~ msgid "" #~ "Coordinate expression had a close parenthesis with no open parenthesis" #~ msgstr "" #~ "Expressão de coordenada tem um fecho de parentesis sem a respetiva " #~ "abertura" #~ msgid "" #~ "Coordinate expression had an open parenthesis with no close parenthesis" #~ msgstr "" #~ "Expressão de coordenada tem uma abertura de parentesis sem o respetivo " #~ "fecho" #~ msgid "Coordinate expression doesn't seem to have any operators or operands" #~ msgstr "Expressão de coordenada não aparenta ter operadores nem operandos" #~ msgid "Theme contained an expression that resulted in an error: %s\n" #~ msgstr "O tema continha uma expressão que resultou num erro: %s\n" #~ msgid "" #~ "